Index: /tags/sj_tags/sj_root_20080929/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+ippdb.src
Index: /tags/sj_tags/sj_root_20080929/Branches.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Branches.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Branches.txt	(revision 22322)
@@ -0,0 +1,18 @@
+sj_ippTests_branch_20080929
+ Branch rooted at tag sj_root_20080929
+ * ippTests: add draft testing scripts. Expect these
+             to be re-coded later, hence isolating on 
+             a branch.
+
+eam_branch_20080806
+ * ippTasks : adding cleanup tasks
+
+eam_branch_20080719 : ippTools psphot
+ * ippTools : adding cleanup sequences to every major stage, 
+	      examining the autocode options
+ * psphot : mask the cosmic rays by footprint.
+ * psModules : needed to support the psphot mods
+ * pswarp : adding multi-threading
+
+pap_branch_080908
+ * psModules/src/imcombine: make stamps tolerant of masked pixels
Index: /tags/sj_tags/sj_root_20080929/DataChallenge/copy_essence.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataChallenge/copy_essence.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataChallenge/copy_essence.pl	(revision 22322)
@@ -0,0 +1,62 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use File::Spec;
+use File::Find::Rule;
+
+die "Usage: $0 SOURCE_DIR TARGET_DIR\n" if scalar @ARGV != 2;
+
+my $source = File::Spec->canonpath( $ARGV[0] );
+my $target = File::Spec->canonpath( $ARGV[1] );
+
+my @dirs = File::Find::Rule->directory->in( $source );
+
+# Create the directory structure
+foreach my $dir ( @dirs ) {
+    my $rel = File::Spec->abs2rel( $dir, $source );
+    my @subs = File::Spec->splitpath( $rel );
+    
+    # Create all the underlying directories
+    my $source_path = $source;	# Path we've traversed for source
+    my $path = $target;	# Path we've traversed for target
+    foreach my $subdir ( @subs ) {
+	my $newdir = File::Spec->catdir( $path, $subdir );
+	if (defined $subdir and length $subdir > 0) {
+	    if (not -d $newdir) {
+		print "mkdir $newdir\n";
+#		mkdir $newdir;
+	    }
+	    $path = $newdir;
+	}
+    }
+}
+
+# Find the files
+my @files = File::Find::Rule->file->name( '*.fits*' )->in( $source );
+
+foreach my $file ( @files ) {
+    my $rel_name = File::Spec->abs2rel( $file, $source );
+
+    my $unzipper;
+    if ($rel_name =~ /\.fits\.gz$/) {
+	$unzipper = 'gunzip -c';
+	$rel_name =~ s/\.gz$//;
+    } elsif ($rel_name =~ /\.fits\.bz2$/) {
+	$unzipper = 'bunzip2 -c';
+	$rel_name =~ s/\.bz2$//;
+    } elsif ($rel_name =~ /\.fits$/) {
+	$unzipper = 'cat';
+    } else {
+	die "Unrecognised file type for $rel_name\n";
+    }
+
+    my $out_file = File::Spec->rel2abs( $rel_name, $target );
+
+    print "$unzipper $file > $out_file\n";
+#    system "$unzipper $file > $out_file";
+}
+
+__END__;
+
Index: /tags/sj_tags/sj_root_20080929/DataChallenge/essence_injected_02.mysql
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataChallenge/essence_injected_02.mysql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataChallenge/essence_injected_02.mysql	(revision 22322)
@@ -0,0 +1,862 @@
+-- MySQL dump 10.9
+--
+-- Host: localhost    Database: essence
+-- ------------------------------------------------------
+-- Server version	4.1.21-log
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `detInputExp`
+--
+
+DROP TABLE IF EXISTS `detInputExp`;
+CREATE TABLE `detInputExp` (
+  `det_id` int(11) NOT NULL default '0',
+  `iteration` int(11) NOT NULL default '0',
+  `exp_tag` varchar(64) NOT NULL default '',
+  `include` tinyint(4) default NULL,
+  PRIMARY KEY  (`det_id`,`iteration`,`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detInputExp`
+--
+
+LOCK TABLES `detInputExp` WRITE;
+/*!40000 ALTER TABLE `detInputExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detInputExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `detNormalizedExp`
+--
+
+DROP TABLE IF EXISTS `detNormalizedExp`;
+CREATE TABLE `detNormalizedExp` (
+  `det_id` int(11) NOT NULL default '0',
+  `iteration` int(11) NOT NULL default '0',
+  `recipe` varchar(64) default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `b1_uri` varchar(255) default NULL,
+  `b2_uri` varchar(255) default NULL,
+  PRIMARY KEY  (`det_id`,`iteration`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detNormalizedExp`
+--
+
+LOCK TABLES `detNormalizedExp` WRITE;
+/*!40000 ALTER TABLE `detNormalizedExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detNormalizedExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `detNormalizedImfile`
+--
+
+DROP TABLE IF EXISTS `detNormalizedImfile`;
+CREATE TABLE `detNormalizedImfile` (
+  `det_id` int(11) NOT NULL default '0',
+  `iteration` int(11) NOT NULL default '0',
+  `class_id` varchar(64) NOT NULL default '',
+  `uri` varchar(255) default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `b1_uri` varchar(255) default NULL,
+  `b2_uri` varchar(255) default NULL,
+  PRIMARY KEY  (`det_id`,`iteration`,`class_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detNormalizedImfile`
+--
+
+LOCK TABLES `detNormalizedImfile` WRITE;
+/*!40000 ALTER TABLE `detNormalizedImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detNormalizedImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `detNormalizedStatImfile`
+--
+
+DROP TABLE IF EXISTS `detNormalizedStatImfile`;
+CREATE TABLE `detNormalizedStatImfile` (
+  `det_id` int(11) NOT NULL default '0',
+  `iteration` int(11) NOT NULL default '0',
+  `class_id` varchar(64) NOT NULL default '',
+  `norm` float default NULL,
+  PRIMARY KEY  (`det_id`,`iteration`,`class_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detNormalizedStatImfile`
+--
+
+LOCK TABLES `detNormalizedStatImfile` WRITE;
+/*!40000 ALTER TABLE `detNormalizedStatImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detNormalizedStatImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `detProcessedExp`
+--
+
+DROP TABLE IF EXISTS `detProcessedExp`;
+CREATE TABLE `detProcessedExp` (
+  `det_id` int(11) NOT NULL default '0',
+  `exp_tag` varchar(64) NOT NULL default '',
+  `recipe` varchar(64) default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `b1_uri` varchar(255) default NULL,
+  `b2_uri` varchar(255) default NULL,
+  PRIMARY KEY  (`det_id`,`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detProcessedExp`
+--
+
+LOCK TABLES `detProcessedExp` WRITE;
+/*!40000 ALTER TABLE `detProcessedExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detProcessedExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `detProcessedImfile`
+--
+
+DROP TABLE IF EXISTS `detProcessedImfile`;
+CREATE TABLE `detProcessedImfile` (
+  `det_id` int(11) NOT NULL default '0',
+  `exp_tag` varchar(64) NOT NULL default '',
+  `class_id` varchar(64) NOT NULL default '',
+  `uri` varchar(255) default NULL,
+  `recipe` varchar(64) default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `b1_uri` varchar(255) default NULL,
+  `b2_uri` varchar(255) default NULL,
+  PRIMARY KEY  (`det_id`,`exp_tag`,`class_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detProcessedImfile`
+--
+
+LOCK TABLES `detProcessedImfile` WRITE;
+/*!40000 ALTER TABLE `detProcessedImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detProcessedImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `detResidExp`
+--
+
+DROP TABLE IF EXISTS `detResidExp`;
+CREATE TABLE `detResidExp` (
+  `det_id` int(11) NOT NULL default '0',
+  `iteration` int(11) NOT NULL default '0',
+  `exp_tag` varchar(64) NOT NULL default '',
+  `recipe` varchar(64) default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `b1_uri` varchar(255) default NULL,
+  `b2_uri` varchar(255) default NULL,
+  `accept` tinyint(4) default NULL,
+  PRIMARY KEY  (`det_id`,`iteration`,`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detResidExp`
+--
+
+LOCK TABLES `detResidExp` WRITE;
+/*!40000 ALTER TABLE `detResidExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detResidExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `detResidImfile`
+--
+
+DROP TABLE IF EXISTS `detResidImfile`;
+CREATE TABLE `detResidImfile` (
+  `det_id` int(11) NOT NULL default '0',
+  `iteration` int(11) NOT NULL default '0',
+  `exp_tag` varchar(64) NOT NULL default '',
+  `class_id` varchar(64) NOT NULL default '',
+  `uri` varchar(255) default NULL,
+  `recipe` varchar(64) default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `b1_uri` varchar(255) default NULL,
+  `b2_uri` varchar(255) default NULL,
+  PRIMARY KEY  (`det_id`,`iteration`,`exp_tag`,`class_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detResidImfile`
+--
+
+LOCK TABLES `detResidImfile` WRITE;
+/*!40000 ALTER TABLE `detResidImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detResidImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `detRun`
+--
+
+DROP TABLE IF EXISTS `detRun`;
+CREATE TABLE `detRun` (
+  `det_id` int(11) NOT NULL auto_increment,
+  `iteration` int(11) default NULL,
+  `det_type` varchar(64) default NULL,
+  `mode` varchar(64) default NULL,
+  `state` varchar(64) default NULL,
+  `exp_type` varchar(64) default NULL,
+  `filter` varchar(64) default NULL,
+  `airmass` float default NULL,
+  `exp_time` float default NULL,
+  `ccd_temp` float default NULL,
+  `posang` double default NULL,
+  `object` varchar(64) default NULL,
+  `registered` datetime default NULL,
+  `use_begin` datetime default NULL,
+  `use_end` datetime default NULL,
+  PRIMARY KEY  (`det_id`),
+  KEY `det_id` (`det_id`),
+  KEY `iteration` (`iteration`),
+  KEY `det_type` (`det_type`),
+  KEY `mode` (`mode`),
+  KEY `state` (`state`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detRun`
+--
+
+LOCK TABLES `detRun` WRITE;
+/*!40000 ALTER TABLE `detRun` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detRun` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `detRunSummary`
+--
+
+DROP TABLE IF EXISTS `detRunSummary`;
+CREATE TABLE `detRunSummary` (
+  `det_id` int(11) NOT NULL default '0',
+  `iteration` int(11) NOT NULL default '0',
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `accept` tinyint(4) default NULL,
+  PRIMARY KEY  (`det_id`,`iteration`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detRunSummary`
+--
+
+LOCK TABLES `detRunSummary` WRITE;
+/*!40000 ALTER TABLE `detRunSummary` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detRunSummary` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `detStackedImfile`
+--
+
+DROP TABLE IF EXISTS `detStackedImfile`;
+CREATE TABLE `detStackedImfile` (
+  `det_id` int(11) NOT NULL default '0',
+  `iteration` int(11) NOT NULL default '0',
+  `class_id` varchar(64) NOT NULL default '',
+  `uri` varchar(255) default NULL,
+  `recipe` varchar(64) default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  PRIMARY KEY  (`det_id`,`iteration`,`class_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `detStackedImfile`
+--
+
+LOCK TABLES `detStackedImfile` WRITE;
+/*!40000 ALTER TABLE `detStackedImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `detStackedImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `expTagCounter`
+--
+
+DROP TABLE IF EXISTS `expTagCounter`;
+CREATE TABLE `expTagCounter` (
+  `counter` bigint(20) unsigned default NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `expTagCounter`
+--
+
+LOCK TABLES `expTagCounter` WRITE;
+/*!40000 ALTER TABLE `expTagCounter` DISABLE KEYS */;
+INSERT INTO `expTagCounter` VALUES (4070);
+/*!40000 ALTER TABLE `expTagCounter` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `newExp`
+--
+
+DROP TABLE IF EXISTS `newExp`;
+CREATE TABLE `newExp` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `exp_id` varchar(64) default NULL,
+  `camera` varchar(64) default NULL,
+  `telescope` varchar(64) default NULL,
+  `dateobs` datetime default NULL,
+  `exp_type` varchar(64) default NULL,
+  `imfiles` int(11) default NULL,
+  PRIMARY KEY  (`exp_tag`),
+  KEY `exp_id` (`exp_id`),
+  KEY `camera` (`camera`),
+  KEY `telescope` (`telescope`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `newExp`
+--
+
+LOCK TABLES `newExp` WRITE;
+/*!40000 ALTER TABLE `newExp` DISABLE KEYS */;
+INSERT INTO `newExp` VALUES ('dflat001.1058','dflat001','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat001.1113','dflat001','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat001.1317','dflat001','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat001.1440','dflat001','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat001.2717','dflat001','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat001.2980','dflat001','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat001.3171','dflat001','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat001.41','dflat001','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat001.48','dflat001','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat001.584','dflat001','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat002.1062','dflat002','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat002.1117','dflat002','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat002.1322','dflat002','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat002.1444','dflat002','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat002.2721','dflat002','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat002.2984','dflat002','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat002.3175','dflat002','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat002.45','dflat002','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat002.52','dflat002','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat002.588','dflat002','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat003.1066','dflat003','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat003.1121','dflat003','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat003.1326','dflat003','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat003.1448','dflat003','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat003.2725','dflat003','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat003.2988','dflat003','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat003.3179','dflat003','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat003.49','dflat003','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat003.56','dflat003','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat003.592','dflat003','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat004.1070','dflat004','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat004.1125','dflat004','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat004.1330','dflat004','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat004.1452','dflat004','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat004.2729','dflat004','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat004.2992','dflat004','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat004.3183','dflat004','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat004.53','dflat004','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat004.596','dflat004','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat004.60','dflat004','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat005.1129','dflat005','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat005.1334','dflat005','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat005.1456','dflat005','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat005.2733','dflat005','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat005.2996','dflat005','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat005.3186','dflat005','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat005.57','dflat005','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat005.600','dflat005','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat005.64','dflat005','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.1074','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.1133','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.1338','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.1460','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.1936','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.2244','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.2383','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.2735','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.3000','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.3190','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.46','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.604','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.61','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.68','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat006.853','dflat006','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.1078','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.1137','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.1342','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.1466','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.1940','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.2248','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.2387','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.2740','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.3004','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.3194','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.50','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.608','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.65','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.72','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat007.857','dflat007','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.1082','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.1141','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.1346','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.1470','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.1944','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.2252','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.2391','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.2744','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.3008','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.3198','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.54','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.612','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.69','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.76','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat008.861','dflat008','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.1086','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.1145','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.1350','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.1475','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.1948','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.2256','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.2395','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.2749','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.3012','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.3201','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.58','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.616','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.73','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.80','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat009.865','dflat009','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.1090','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.1149','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.1354','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.1479','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.1952','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.2262','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.2399','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.2532','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.2753','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.3015','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.3207','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.619','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.62','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.77','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.84','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat010.869','dflat010','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.1094','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.1153','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.1358','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.1482','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.1656','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.1955','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.2141','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.2266','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.2404','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.2536','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.2757','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.3020','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.3210','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.3900','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.623','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.66','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.81','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.873','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat011.88','dflat011','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.1098','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.1157','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.1362','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.1435','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.1486','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.1660','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.1804','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.1959','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.196','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.2145','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.2269','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.2408','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.2540','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.2761','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.2961','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.2971','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.3023','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.3215','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.3543','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.3901','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.586','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.627','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.70','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.85','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.877','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat012.91','dflat012','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.1102','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.1161','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.1366','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.1439','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.1490','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.1664','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.1808','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.1963','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.200','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.2149','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.2274','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.2411','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.2544','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.2764','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.2965','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.2975','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.3027','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.3219','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.3546','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.3902','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.590','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.631','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.74','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.881','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.89','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat013.95','dflat013','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.1106','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.1165','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.1370','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.1443','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.1494','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.1668','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.1812','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.1967','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.204','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.2153','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.2278','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.2416','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.2548','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.2768','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.2969','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.2979','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.3031','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.3222','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.3549','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.3903','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.594','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.635','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.78','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.885','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.94','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat014.99','dflat014','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.104','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.1110','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.1169','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.1374','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.1447','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.1498','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.1672','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.1816','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.1971','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.208','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.2159','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.2281','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.2419','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.2552','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.2772','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.2974','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.2983','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.3036','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.3226','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.3552','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.3904','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.598','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.639','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.82','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.889','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat015.98','dflat015','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.102','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.108','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.1114','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.1173','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.1378','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.1450','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.1501','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.1676','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.1820','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.1975','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.212','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.2162','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.2285','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.2424','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.2556','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.2775','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.2978','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.2987','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.3040','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.3229','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.3555','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.3905','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.601','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.643','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.86','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat016.893','dflat016','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.105','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.111','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.1118','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.1177','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.1382','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.1454','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.1505','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.1680','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.1824','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.1980','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.2166','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.217','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.2289','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.2429','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.2561','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.2779','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.2982','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.2991','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.3044','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.3233','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.3558','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.3906','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.606','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.647','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.897','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat017.90','dflat017','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.110','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.1122','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.116','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.1180','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.1386','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.1458','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.1509','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.1684','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.1828','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.1984','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.2170','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.221','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.2293','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.2433','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.2565','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.2783','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.2986','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.2995','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.3048','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.3239','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.3561','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.3907','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.610','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.651','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.901','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat018.93','dflat018','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.1126','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.114','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.1185','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.120','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.1390','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.1463','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.1513','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.1687','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.1832','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.1988','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.2173','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.225','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.2296','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.2436','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.2569','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.2788','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.2990','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.2999','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.3052','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.3242','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.3564','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.3908','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.613','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.655','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.905','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat019.97','dflat019','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.101','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.1130','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.118','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.1189','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.124','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.1394','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.1467','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.1517','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.1692','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.1836','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.1991','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.2177','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.228','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.2301','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.2440','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.2573','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.2791','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.2994','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.3003','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.3057','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.3246','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.3567','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.3909','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.617','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.659','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat020.909','dflat020','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.106','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.1134','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.1471','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.1696','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.1840','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.1996','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.2181','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.2305','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.232','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.2443','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.2577','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.2998','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.3007','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.3570','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.3910','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.622','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.678','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat021.913','dflat021','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.109','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.1474','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.1700','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.1843','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.2000','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.2185','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.2309','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.236','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.2448','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.2581','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.3001','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.3011','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.3573','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.3911','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.626','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.682','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat022.917','dflat022','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.113','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.1478','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.1704','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.1848','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.2004','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.2189','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.2313','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.241','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.2452','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.2585','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.3006','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.3016','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.3576','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.3912','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.630','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.686','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat023.921','dflat023','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.117','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.1483','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.1707','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.1852','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.2008','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.2193','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.2316','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.245','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.2456','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.2589','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.3010','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.3019','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.3579','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.3913','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.633','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.690','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat024.925','dflat024','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.121','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.1485','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.1711','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.1856','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.2011','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.2198','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.2321','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.2460','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.249','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.2593','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.3013','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.3024','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.3582','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.3914','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.637','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.694','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat025.930','dflat025','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.1489','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.1716','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.1860','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.2202','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.253','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.2597','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.3018','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.3028','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.3584','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.3915','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.641','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat026.698','dflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.1493','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.1720','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.1864','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.2205','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.257','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.2601','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.3022','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.3032','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.3587','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.3916','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.645','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat027.703','dflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.1497','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.1724','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.1868','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.2211','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.2605','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.261','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.3026','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.3035','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.3590','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.3917','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.649','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat028.707','dflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.1502','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.1728','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.1872','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.2215','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.2609','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.265','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.3029','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.3039','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.3593','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.3918','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.653','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat029.711','dflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.1506','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.1732','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.1876','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.2219','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.269','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.3033','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.3043','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.3596','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.3919','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.657','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat030.715','dflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat031.1510','dflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat031.1880','dflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat031.3037','dflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat031.3047','dflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat031.3599','dflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat031.3920','dflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat031.661','dflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat031.719','dflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat032.3041','dflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat032.3921','dflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat032.723','dflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat033.3045','dflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat033.3922','dflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat033.727','dflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat034.3050','dflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat034.3923','dflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat034.63','dflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat034.731','dflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat035.3053','dflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat035.3924','dflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat035.67','dflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat035.735','dflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat036.3058','dflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat036.3925','dflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat036.71','dflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat036.739','dflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat036.753','dflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat037.3926','dflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat037.743','dflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat037.75','dflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat037.757','dflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat038.3927','dflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat038.747','dflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat038.761','dflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat038.79','dflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat039.3928','dflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat039.750','dflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat039.764','dflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat039.83','dflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat040.3929','dflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat040.754','dflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat040.768','dflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat040.87','dflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat041.773','dflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat042.777','dflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('dflat043.781','dflat043','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat026.122','sflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat026.1521','sflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat026.2325','sflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat026.2797','sflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat026.3062','sflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat026.3250','sflat026','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat027.125','sflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat027.126','sflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat027.1525','sflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat027.2329','sflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat027.2801','sflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat027.3065','sflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat027.3254','sflat027','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat028.128','sflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat028.129','sflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat028.130','sflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat028.1529','sflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat028.2333','sflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat028.2805','sflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat028.3070','sflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat028.3258','sflat028','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat029.132','sflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat029.133','sflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat029.134','sflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat029.1534','sflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat029.2336','sflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat029.2809','sflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat029.3074','sflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat029.3262','sflat029','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat030.135','sflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat030.137','sflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat030.138','sflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat030.1539','sflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat030.2340','sflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat030.2613','sflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat030.2813','sflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat030.3077','sflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat030.3266','sflat030','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.139','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.1398','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.141','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.142','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.1543','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.1735','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.2015','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.2223','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.2344','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.2617','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.2817','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.3082','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.3271','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat031.935','sflat031','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.1138','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.1193','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.143','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.145','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.146','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.1547','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.1738','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.1883','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.2019','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.2348','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.2464','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.2621','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.2821','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.3086','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.3275','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.3602','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.665','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat032.938','sflat032','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.1142','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.1197','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.148','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.149','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.150','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.1551','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.1744','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.1887','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.2023','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.2353','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.2468','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.2625','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.2825','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.3090','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.3279','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.3605','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.669','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat033.942','sflat033','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.1146','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.1201','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.151','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.153','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.154','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.1555','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.1747','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.1891','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.2028','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.2357','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.2471','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.2629','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.2827','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.3094','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.3283','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.3608','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.3666','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.532','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.673','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat034.947','sflat034','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.1150','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.1205','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.1559','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.156','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.157','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.158','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.1752','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.1896','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.2031','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.2361','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.2475','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.2633','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.2832','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.3097','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.3287','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.3611','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.3668','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.536','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.677','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat035.951','sflat035','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.1154','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.1209','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.159','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.161','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.162','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.1757','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.1899','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.2035','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.2365','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.2479','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.2637','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.2837','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.3101','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.3291','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.3614','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.540','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.681','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat036.955','sflat036','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.1158','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.1213','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.163','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.165','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.1760','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.1903','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.2038','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.2369','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.2483','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.2641','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.2841','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.3061','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.3106','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.3295','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.3617','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.3670','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.544','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.685','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat037.959','sflat037','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.1162','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.1217','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.167','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.169','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.1764','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.1908','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.2042','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.2487','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.2645','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.2845','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.3064','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.3110','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.3298','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.3621','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.3672','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.548','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.689','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat038.963','sflat038','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.1166','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.171','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.173','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.1768','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.1912','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.2047','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.2491','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.2649','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.2849','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.3068','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.3624','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.3674','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.552','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.693','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat039.967','sflat039','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.175','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.177','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.1772','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.1916','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.2051','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.2495','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.2653','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.3073','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.3627','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.3676','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.556','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.697','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat040.970','sflat040','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.1776','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.179','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.1920','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.2055','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.2499','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.2657','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.3076','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.3630','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.3678','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.560','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.701','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.758','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat041.973','sflat041','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.1781','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.183','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.1924','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.2059','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.2503','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.3081','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.3633','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.564','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.705','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.762','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat042.978','sflat042','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat043.1785','sflat043','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat043.1927','sflat043','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat043.2063','sflat043','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat043.2507','sflat043','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat043.3085','sflat043','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat043.568','sflat043','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat043.709','sflat043','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat043.766','sflat043','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat044.2067','sflat044','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat044.2511','sflat044','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat044.3089','sflat044','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat044.572','sflat044','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat044.713','sflat044','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat044.770','sflat044','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat045.2071','sflat045','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat045.3093','sflat045','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat045.3680','sflat045','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat045.576','sflat045','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat045.774','sflat045','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat046.2075','sflat046','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat046.3096','sflat046','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat046.3682','sflat046','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat046.580','sflat046','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat046.778','sflat046','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat047.3102','sflat047','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat047.782','sflat047','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat048.3104','sflat048','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat048.786','sflat048','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat049.3109','sflat049','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat049.790','sflat049','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat050.794','sflat050','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat051.798','sflat051','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat052.802','sflat052','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat053.806','sflat053','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat054.1170','sflat054','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat055.1174','sflat055','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat121.2079','sflat121','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat122.2083','sflat122','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat123.2087','sflat123','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat124.2091','sflat124','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat125.2095','sflat125','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat126.2099','sflat126','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat127.2103','sflat127','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat128.2107','sflat128','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat129.2111','sflat129','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sflat130.2115','sflat130','CTIO_MOSAIC2','CTIO4m',NULL,'flat',1),('sm35.020930_0908.177.3054','sm35.020930_0908.177','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021002_0900.069.2227','sm35.021002_0900.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021004_0911.092.1563','sm35.021004_0911.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021006_0859.123.912','sm35.021006_0859.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021012_0825.143.2661','sm35.021012_0825.143','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021030_0905.129.1514','sm35.021030_0905.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021101_0833.136.983','sm35.021101_0833.136','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021105_0859.125.273','sm35.021105_0859.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021111_0907.150.3114','sm35.021111_0907.150','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021115_0851.140.1931','sm35.021115_0851.140','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021216_0817.110.1221','sm35.021216_0817.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.021229_0839.083.717','sm35.021229_0839.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.030103_0841.126.187','sm35.030103_0841.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm35.92','sm35','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.020930_0905.176.3055','sm36.020930_0905.176','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021002_0858.068.2231','sm36.021002_0858.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021004_0909.091.1565','sm36.021004_0909.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021006_0857.122.916','sm36.021006_0857.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021012_0822.142.2665','sm36.021012_0822.142','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021030_0903.128.1518','sm36.021030_0903.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021101_0830.135.987','sm36.021101_0830.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021105_0857.124.277','sm36.021105_0857.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021111_0905.149.3118','sm36.021111_0905.149','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021115_0849.139.1935','sm36.021115_0849.139','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021216_0815.109.1225','sm36.021216_0815.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.021229_0837.082.721','sm36.021229_0837.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.030103_0839.125.191','sm36.030103_0839.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm36.96','sm36','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.020930_0903.175.3060','sm43.020930_0903.175','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021002_0856.067.2235','sm43.021002_0856.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021004_0907.090.1570','sm43.021004_0907.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021006_0855.121.920','sm43.021006_0855.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021012_0820.141.2669','sm43.021012_0820.141','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021030_0901.127.1523','sm43.021030_0901.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021101_0827.134.991','sm43.021101_0827.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021105_0855.123.281','sm43.021105_0855.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021111_0903.148.3122','sm43.021111_0903.148','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021115_0846.138.1939','sm43.021115_0846.138','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021216_0812.108.1229','sm43.021216_0812.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.021229_0835.081.725','sm43.021229_0835.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.030103_0837.124.195','sm43.030103_0837.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm43.100','sm43','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.020928_0909.130.3684','sm44.020928_0909.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.020930_0900.174.3063','sm44.020930_0900.174','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021002_0854.066.2239','sm44.021002_0854.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021004_0905.089.1573','sm44.021004_0905.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021006_0853.120.924','sm44.021006_0853.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021012_0817.140.2673','sm44.021012_0817.140','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021030_0858.126.1527','sm44.021030_0858.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021101_0825.133.995','sm44.021101_0825.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021105_0853.122.284','sm44.021105_0853.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021111_0901.147.3125','sm44.021111_0901.147','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021115_0844.137.1943','sm44.021115_0844.137','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021216_0810.107.1233','sm44.021216_0810.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.021229_0833.080.729','sm44.021229_0833.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm44.030103_0834.123.199','sm44.030103_0834.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.020928_0906.129.3686','sm45.020928_0906.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.020930_0856.173.3067','sm45.020930_0856.173','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021002_0851.065.2243','sm45.021002_0851.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021004_0902.088.1577','sm45.021004_0902.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021006_0850.119.928','sm45.021006_0850.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021008_0927.065.181','sm45.021008_0927.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021012_0814.139.2677','sm45.021012_0814.139','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021030_0856.125.1531','sm45.021030_0856.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021101_0822.132.1000','sm45.021101_0822.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021105_0850.121.288','sm45.021105_0850.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021111_0858.146.3128','sm45.021111_0858.146','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021115_0841.136.1947','sm45.021115_0841.136','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021216_0807.106.1236','sm45.021216_0807.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.021229_0831.079.733','sm45.021229_0831.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm45.030103_0832.122.203','sm45.030103_0832.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.020928_0903.128.3688','sm46.020928_0903.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.020930_0853.172.3071','sm46.020930_0853.172','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021002_0848.064.2247','sm46.021002_0848.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021004_0859.087.1581','sm46.021004_0859.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021006_0848.118.932','sm46.021006_0848.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021008_0925.064.185','sm46.021008_0925.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021012_0812.138.2681','sm46.021012_0812.138','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021030_0853.124.1533','sm46.021030_0853.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021101_0819.131.1004','sm46.021101_0819.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021105_0848.120.292','sm46.021105_0848.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021111_0856.145.3132','sm46.021111_0856.145','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021115_0839.135.1951','sm46.021115_0839.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021128_0852.118.1402','sm46.021128_0852.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021206_0845.135.3113','sm46.021206_0845.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021214_0846.067.1789','sm46.021214_0846.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021216_0805.105.1242','sm46.021216_0805.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.021229_0828.078.737','sm46.021229_0828.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm46.030103_0829.121.207','sm46.030103_0829.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.020930_0850.171.3078','sm53.020930_0850.171','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021002_0845.063.2251','sm53.021002_0845.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021004_0857.086.1585','sm53.021004_0857.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021006_0845.117.936','sm53.021006_0845.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021008_0909.080.189','sm53.021008_0909.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021012_0809.137.2685','sm53.021012_0809.137','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021030_0850.123.1537','sm53.021030_0850.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021101_0816.130.1008','sm53.021101_0816.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021105_0846.119.296','sm53.021105_0846.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021111_0853.144.3136','sm53.021111_0853.144','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021115_0836.134.1956','sm53.021115_0836.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021128_0849.117.1406','sm53.021128_0849.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021206_0843.134.3117','sm53.021206_0843.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021214_0844.066.1794','sm53.021214_0844.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021216_0802.104.1246','sm53.021216_0802.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.021229_0826.077.741','sm53.021229_0826.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm53.030103_0826.120.211','sm53.030103_0826.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.020928_0855.126.3690','sm54.020928_0855.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.020930_0844.170.3080','sm54.020930_0844.170','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021002_0840.062.2255','sm54.021002_0840.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021004_0852.085.1589','sm54.021004_0852.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021006_0840.116.940','sm54.021006_0840.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021008_0904.079.193','sm54.021008_0904.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021012_0804.136.2689','sm54.021012_0804.136','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021030_0845.122.1541','sm54.021030_0845.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021101_0811.129.1012','sm54.021101_0811.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021105_0842.118.300','sm54.021105_0842.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021111_0850.143.3141','sm54.021111_0850.143','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021115_0832.133.1960','sm54.021115_0832.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021128_0847.116.1410','sm54.021128_0847.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021206_0841.133.3119','sm54.021206_0841.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021214_0840.065.1798','sm54.021214_0840.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021216_0758.103.1250','sm54.021216_0758.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.021229_0822.076.745','sm54.021229_0822.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm54.030103_0822.119.215','sm54.030103_0822.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.020928_0850.125.3692','sm55.020928_0850.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.020930_0839.169.3083','sm55.020930_0839.169','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021002_0835.061.2259','sm55.021002_0835.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021004_0847.084.1593','sm55.021004_0847.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021006_0835.115.944','sm55.021006_0835.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021008_0859.078.197','sm55.021008_0859.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021012_0759.135.2693','sm55.021012_0759.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021030_0840.121.1546','sm55.021030_0840.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021105_0837.117.305','sm55.021105_0837.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021111_0844.142.3145','sm55.021111_0844.142','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021113_0856.133.2516','sm55.021113_0856.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021115_0827.132.1964','sm55.021115_0827.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021128_0844.115.1414','sm55.021128_0844.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021206_0837.132.3124','sm55.021206_0837.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021214_0835.064.1802','sm55.021214_0835.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021216_0753.102.1254','sm55.021216_0753.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.021229_0817.075.748','sm55.021229_0817.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm55.030103_0817.118.219','sm55.030103_0817.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.020928_0844.124.3694','sm56.020928_0844.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.020930_0833.168.3088','sm56.020930_0833.168','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021002_0830.060.2263','sm56.021002_0830.060','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021004_0842.083.1597','sm56.021004_0842.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021006_0830.114.948','sm56.021006_0830.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021008_0854.077.201','sm56.021008_0854.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021012_0754.134.2695','sm56.021012_0754.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021030_0834.120.1549','sm56.021030_0834.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021101_0803.127.1016','sm56.021101_0803.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021101_0803.128.1020','sm56.021101_0803.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021105_0833.116.308','sm56.021105_0833.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021111_0840.141.3149','sm56.021111_0840.141','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021113_0852.132.2519','sm56.021113_0852.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021115_0823.131.1968','sm56.021115_0823.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021128_0841.114.1418','sm56.021128_0841.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021206_0834.131.3129','sm56.021206_0834.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021212_0845.141.2164','sm56.021212_0845.141','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021214_0831.063.1805','sm56.021214_0831.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021216_0749.101.1258','sm56.021216_0749.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.021229_0814.074.752','sm56.021229_0814.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm56.030103_0813.117.223','sm56.030103_0813.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.020928_0841.123.3696','sm57.020928_0841.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.020930_0829.167.3091','sm57.020930_0829.167','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021002_0827.059.2267','sm57.021002_0827.059','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021004_0838.082.1601','sm57.021004_0838.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021006_0827.113.952','sm57.021006_0827.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021008_0850.076.205','sm57.021008_0850.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021012_0750.133.2701','sm57.021012_0750.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021030_0831.119.1554','sm57.021030_0831.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021101_0800.126.1024','sm57.021101_0800.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021105_0830.115.312','sm57.021105_0830.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021111_0838.140.3152','sm57.021111_0838.140','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021113_0849.131.2523','sm57.021113_0849.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021115_0820.130.1972','sm57.021115_0820.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021128_0838.113.1422','sm57.021128_0838.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021206_0832.130.3133','sm57.021206_0832.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021212_0841.140.2168','sm57.021212_0841.140','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021214_0829.062.1809','sm57.021214_0829.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021216_0746.100.1262','sm57.021216_0746.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.021229_0811.073.756','sm57.021229_0811.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm57.030103_0810.116.227','sm57.030103_0810.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021002_0935.085.2271','sm58.021002_0935.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021002_0937.086.2275','sm58.021002_0937.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021006_0936.140.956','sm58.021006_0936.140','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021105_0827.114.316','sm58.021105_0827.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021109_0859.100.3636','sm58.021109_0859.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021111_0833.139.3158','sm58.021111_0833.139','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021113_0846.130.2527','sm58.021113_0846.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021115_0817.129.1976','sm58.021115_0817.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021128_0835.112.1426','sm58.021128_0835.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021202_0842.138.663','sm58.021202_0842.138','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021206_0830.129.3137','sm58.021206_0830.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021212_0837.139.2172','sm58.021212_0837.139','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021214_0826.061.1813','sm58.021214_0826.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021216_0743.099.1266','sm58.021216_0743.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.021229_0808.072.760','sm58.021229_0808.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm58.030103_0807.115.231','sm58.030103_0807.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.020930_0824.166.3095','sm63.020930_0824.166','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021002_0822.058.2279','sm63.021002_0822.058','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021004_0834.081.1605','sm63.021004_0834.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021006_0810.131.960','sm63.021006_0810.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021008_0846.075.209','sm63.021008_0846.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021012_0746.132.2704','sm63.021012_0746.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021030_0827.118.1558','sm63.021030_0827.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021101_0756.125.1028','sm63.021101_0756.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021105_0824.113.320','sm63.021105_0824.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021109_0856.099.3638','sm63.021109_0856.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021111_0830.138.3162','sm63.021111_0830.138','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021113_0843.129.2531','sm63.021113_0843.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021115_0814.128.1979','sm63.021115_0814.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021128_0832.111.1430','sm63.021128_0832.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021202_0838.137.667','sm63.021202_0838.137','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021204_0846.072.166','sm63.021204_0846.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021206_0827.128.3140','sm63.021206_0827.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021212_0833.138.2176','sm63.021212_0833.138','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021214_0653.116.1817','sm63.021214_0653.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021216_0740.098.1270','sm63.021216_0740.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.021229_0805.071.765','sm63.021229_0805.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm63.030103_0804.114.235','sm63.030103_0804.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.020928_0830.121.3698','sm64.020928_0830.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.020930_0818.165.3100','sm64.020930_0818.165','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021002_0817.057.2283','sm64.021002_0817.057','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021004_0829.080.1610','sm64.021004_0829.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021006_0805.130.964','sm64.021006_0805.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021008_0841.074.213','sm64.021008_0841.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021012_0741.131.2707','sm64.021012_0741.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021030_0821.117.1561','sm64.021030_0821.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021101_0752.124.1032','sm64.021101_0752.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021105_0819.112.324','sm64.021105_0819.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021109_0851.098.3640','sm64.021109_0851.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021111_0824.137.3166','sm64.021111_0824.137','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021113_0838.128.2535','sm64.021113_0838.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021115_0809.127.1983','sm64.021115_0809.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021128_0827.110.1434','sm64.021128_0827.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021202_0832.136.671','sm64.021202_0832.136','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021204_0841.071.170','sm64.021204_0841.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021206_0823.127.3144','sm64.021206_0823.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021210_0843.116.2373','sm64.021210_0843.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021212_0827.137.2180','sm64.021212_0827.137','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021214_0648.115.1821','sm64.021214_0648.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021216_0734.097.1274','sm64.021216_0734.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.021229_0759.070.769','sm64.021229_0759.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm64.030103_0759.113.239','sm64.030103_0759.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.020928_0828.120.3700','sm65.020928_0828.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.020930_0816.164.3105','sm65.020930_0816.164','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021002_0815.056.2287','sm65.021002_0815.056','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021004_0827.079.1614','sm65.021004_0827.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021006_0803.129.968','sm65.021006_0803.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021008_0839.073.216','sm65.021008_0839.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021012_0738.130.2713','sm65.021012_0738.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021014_0921.120.2119','sm65.021014_0921.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021030_0819.116.1566','sm65.021030_0819.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021101_0750.123.1036','sm65.021101_0750.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021105_0817.111.328','sm65.021105_0817.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021109_0848.097.3643','sm65.021109_0848.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021111_0822.136.3170','sm65.021111_0822.136','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021113_0833.127.2539','sm65.021113_0833.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021115_0806.126.1987','sm65.021115_0806.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021128_0823.109.1438','sm65.021128_0823.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021202_0829.135.675','sm65.021202_0829.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021204_0839.070.174','sm65.021204_0839.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021206_0820.126.3148','sm65.021206_0820.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021210_0841.115.2377','sm65.021210_0841.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021212_0824.136.2184','sm65.021212_0824.136','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021214_0645.114.1825','sm65.021214_0645.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021216_0732.096.1278','sm65.021216_0732.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.021229_0757.069.772','sm65.021229_0757.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm65.030103_0757.112.243','sm65.030103_0757.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.020928_0823.119.3702','sm66.020928_0823.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.020930_0810.163.3108','sm66.020930_0810.163','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021002_0810.055.2291','sm66.021002_0810.055','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021004_0822.078.1618','sm66.021004_0822.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021008_0834.072.220','sm66.021008_0834.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021012_0732.129.2715','sm66.021012_0732.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021014_0914.119.2123','sm66.021014_0914.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021030_0814.115.1569','sm66.021030_0814.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021101_0746.122.1040','sm66.021101_0746.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021105_0812.110.332','sm66.021105_0812.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021109_0843.096.3646','sm66.021109_0843.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021111_0817.135.3173','sm66.021111_0817.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021113_0828.126.2543','sm66.021113_0828.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021115_0801.125.1990','sm66.021115_0801.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021128_0818.108.1442','sm66.021128_0818.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021202_0824.134.679','sm66.021202_0824.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021204_0834.069.178','sm66.021204_0834.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021206_0816.125.3153','sm66.021206_0816.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021210_0838.114.2381','sm66.021210_0838.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021212_0818.135.2188','sm66.021212_0818.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021214_0640.113.1829','sm66.021214_0640.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021216_0727.095.1282','sm66.021216_0727.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.021229_0752.068.776','sm66.021229_0752.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm66.030103_0751.111.247','sm66.030103_0751.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.020928_0819.118.3704','sm67.020928_0819.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.020930_0805.162.3112','sm67.020930_0805.162','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021002_0805.054.2295','sm67.021002_0805.054','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021004_0817.077.1622','sm67.021004_0817.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021008_0829.071.224','sm67.021008_0829.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021012_0728.128.2719','sm67.021012_0728.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021014_0910.118.2127','sm67.021014_0910.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021030_0810.114.1574','sm67.021030_0810.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021101_0741.121.1044','sm67.021101_0741.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021105_0809.109.336','sm67.021105_0809.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021109_0840.095.3647','sm67.021109_0840.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021111_0813.134.3178','sm67.021111_0813.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021113_0824.125.2547','sm67.021113_0824.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021115_0758.124.1994','sm67.021115_0758.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021128_0815.107.1445','sm67.021128_0815.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021202_0820.133.683','sm67.021202_0820.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021204_0831.068.182','sm67.021204_0831.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021206_0814.124.3156','sm67.021206_0814.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021210_0834.113.2385','sm67.021210_0834.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021212_0814.134.2192','sm67.021212_0814.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021214_0637.112.1833','sm67.021214_0637.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021216_0723.094.1286','sm67.021216_0723.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.021229_0749.067.780','sm67.021229_0749.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm67.030103_0748.110.250','sm67.030103_0748.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021002_0932.084.2299','sm68.021002_0932.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021006_0931.138.972','sm68.021006_0931.138','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021105_0806.108.340','sm68.021105_0806.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021109_0836.094.3649','sm68.021109_0836.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021111_0809.133.3180','sm68.021111_0809.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021113_0821.124.2551','sm68.021113_0821.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021115_0754.123.1998','sm68.021115_0754.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021128_0811.106.1449','sm68.021128_0811.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021202_0817.132.687','sm68.021202_0817.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021204_0827.067.186','sm68.021204_0827.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021206_0811.123.3160','sm68.021206_0811.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021210_0831.112.2389','sm68.021210_0831.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021212_0810.133.2196','sm68.021212_0810.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021214_0633.111.1837','sm68.021214_0633.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021216_0720.093.1290','sm68.021216_0720.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.021229_0745.066.784','sm68.021229_0745.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm68.030103_0745.109.254','sm68.030103_0745.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021002_0930.083.2303','sm69.021002_0930.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021006_0929.137.976','sm69.021006_0929.137','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021105_0801.107.344','sm69.021105_0801.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021109_0830.093.3651','sm69.021109_0830.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021111_0804.132.3184','sm69.021111_0804.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021113_0818.123.2555','sm69.021113_0818.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021115_0751.122.2002','sm69.021115_0751.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021128_0808.105.1453','sm69.021128_0808.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021202_0813.131.691','sm69.021202_0813.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021204_0825.066.190','sm69.021204_0825.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021206_0808.122.3164','sm69.021206_0808.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021210_0828.111.2393','sm69.021210_0828.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021212_0806.132.2200','sm69.021212_0806.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021214_0630.110.1842','sm69.021214_0630.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021216_0717.092.1294','sm69.021216_0717.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.021229_0743.065.788','sm69.021229_0743.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm69.030103_0741.108.258','sm69.030103_0741.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021002_0928.082.2307','sm73.021002_0928.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021006_0927.136.980','sm73.021006_0927.136','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021105_0757.106.348','sm73.021105_0757.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021109_0826.092.3653','sm73.021109_0826.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021111_0800.131.3188','sm73.021111_0800.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021113_0812.122.2559','sm73.021113_0812.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021115_0747.121.2006','sm73.021115_0747.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021128_0804.104.1459','sm73.021128_0804.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021202_0809.130.695','sm73.021202_0809.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021204_0821.065.194','sm73.021204_0821.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021206_0805.121.3168','sm73.021206_0805.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021210_0824.110.2397','sm73.021210_0824.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021212_0801.131.2204','sm73.021212_0801.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021214_0626.109.1845','sm73.021214_0626.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021216_0713.091.1298','sm73.021216_0713.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.021229_0738.064.792','sm73.021229_0738.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm73.030103_0737.107.262','sm73.030103_0737.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.020928_0813.117.3706','sm74.020928_0813.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.020930_0800.161.3115','sm74.020930_0800.161','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021002_0800.053.2311','sm74.021002_0800.053','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021004_0812.076.1627','sm74.021004_0812.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021006_0749.126.984','sm74.021006_0749.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021008_0824.070.229','sm74.021008_0824.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021010_0928.123.3303','sm74.021010_0928.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021012_0722.127.2724','sm74.021012_0722.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021014_0904.117.2130','sm74.021014_0904.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021030_0754.113.1579','sm74.021030_0754.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021101_0737.120.1047','sm74.021101_0737.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021105_0752.105.353','sm74.021105_0752.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021109_0822.091.3655','sm74.021109_0822.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021111_0756.130.3192','sm74.021111_0756.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021113_0808.121.2563','sm74.021113_0808.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021115_0743.120.2010','sm74.021115_0743.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021128_0759.103.1462','sm74.021128_0759.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021202_0804.129.699','sm74.021202_0804.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021204_0816.064.198','sm74.021204_0816.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021206_0800.120.3172','sm74.021206_0800.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021210_0820.109.2401','sm74.021210_0820.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021212_0755.130.2208','sm74.021212_0755.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021214_0622.108.1850','sm74.021214_0622.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021216_0709.090.1302','sm74.021216_0709.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021229_0721.109.796','sm74.021229_0721.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.021229_0734.063.800','sm74.021229_0734.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm74.030103_0733.106.266','sm74.030103_0733.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.020928_0808.116.3708','sm75.020928_0808.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.020930_0754.160.3120','sm75.020930_0754.160','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021002_0753.052.2315','sm75.021002_0753.052','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021004_0807.075.1631','sm75.021004_0807.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021006_0744.125.988','sm75.021006_0744.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021008_0819.069.233','sm75.021008_0819.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021010_0923.122.3306','sm75.021010_0923.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021012_0717.126.2728','sm75.021012_0717.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021014_0900.116.2135','sm75.021014_0900.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021030_0749.112.1583','sm75.021030_0749.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021101_0733.119.1051','sm75.021101_0733.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021105_0747.104.357','sm75.021105_0747.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021109_0817.090.3657','sm75.021109_0817.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021111_0751.129.3196','sm75.021111_0751.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021113_0803.120.2567','sm75.021113_0803.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021115_0737.119.2014','sm75.021115_0737.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021128_0754.102.1464','sm75.021128_0754.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021202_0759.128.702','sm75.021202_0759.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021204_0811.063.202','sm75.021204_0811.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021206_0756.119.3177','sm75.021206_0756.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021210_0815.108.2405','sm75.021210_0815.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021212_0749.129.2212','sm75.021212_0749.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021214_0617.107.1854','sm75.021214_0617.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021216_0704.089.1306','sm75.021216_0704.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.021229_0716.108.804','sm75.021229_0716.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm75.030103_0728.105.271','sm75.030103_0728.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.020928_0803.115.3710','sm76.020928_0803.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.020930_0749.159.3123','sm76.020930_0749.159','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021002_0748.051.2319','sm76.021002_0748.051','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021004_0802.074.1636','sm76.021004_0802.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021006_0738.124.992','sm76.021006_0738.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021008_0814.068.237','sm76.021008_0814.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021010_0918.121.3309','sm76.021010_0918.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021012_0712.125.2732','sm76.021012_0712.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021014_0844.133.2138','sm76.021014_0844.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021030_0744.111.1587','sm76.021030_0744.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021101_0729.118.1055','sm76.021101_0729.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021105_0742.103.361','sm76.021105_0742.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021109_0812.089.3659','sm76.021109_0812.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021111_0741.128.3200','sm76.021111_0741.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021113_0757.119.2571','sm76.021113_0757.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021115_0732.118.2018','sm76.021115_0732.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021128_0749.101.1468','sm76.021128_0749.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021202_0753.127.706','sm76.021202_0753.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021204_0806.062.206','sm76.021204_0806.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021206_0752.118.3182','sm76.021206_0752.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021210_0810.107.2409','sm76.021210_0810.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021212_0743.128.2216','sm76.021212_0743.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021214_0612.106.1858','sm76.021214_0612.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021216_0659.088.1310','sm76.021216_0659.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.021229_0711.107.808','sm76.021229_0711.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm76.030103_0722.104.275','sm76.030103_0722.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.020928_0758.114.3712','sm77.020928_0758.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.020930_0743.158.3127','sm77.020930_0743.158','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021002_0742.050.2323','sm77.021002_0742.050','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021004_0757.073.1640','sm77.021004_0757.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021006_0733.123.996','sm77.021006_0733.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021008_0809.067.240','sm77.021008_0809.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021010_0912.120.3314','sm77.021010_0912.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021012_0703.124.2736','sm77.021012_0703.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021014_0839.132.2142','sm77.021014_0839.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021030_0738.110.1591','sm77.021030_0738.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021101_0725.117.1059','sm77.021101_0725.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021105_0737.102.365','sm77.021105_0737.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021109_0807.088.3661','sm77.021109_0807.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021111_0736.127.3205','sm77.021111_0736.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021113_0752.118.2575','sm77.021113_0752.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021115_0727.117.2022','sm77.021115_0727.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021128_0744.100.1472','sm77.021128_0744.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021202_0748.126.710','sm77.021202_0748.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021204_0801.061.210','sm77.021204_0801.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021206_0748.117.3187','sm77.021206_0748.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021210_0805.106.2413','sm77.021210_0805.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021212_0736.127.2220','sm77.021212_0736.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021214_0607.105.1862','sm77.021214_0607.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021216_0654.087.1314','sm77.021216_0654.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.021229_0705.106.812','sm77.021229_0705.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm77.030103_0716.103.279','sm77.030103_0716.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.020928_0754.113.3714','sm78.020928_0754.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.020930_0739.157.3131','sm78.020930_0739.157','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021002_0738.049.2327','sm78.021002_0738.049','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021004_0752.072.1644','sm78.021004_0752.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021006_0726.122.999','sm78.021006_0726.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021008_0805.066.244','sm78.021008_0805.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021010_0908.119.3317','sm78.021010_0908.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021012_0659.123.2739','sm78.021012_0659.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021014_0835.131.2146','sm78.021014_0835.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021030_0734.109.1595','sm78.021030_0734.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021101_0722.116.1063','sm78.021101_0722.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021105_0734.101.369','sm78.021105_0734.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021109_0803.087.3663','sm78.021109_0803.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021111_0733.126.3209','sm78.021111_0733.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021113_0749.117.2579','sm78.021113_0749.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021115_0723.116.2026','sm78.021115_0723.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021128_0741.099.1476','sm78.021128_0741.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021202_0744.125.714','sm78.021202_0744.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021204_0758.060.214','sm78.021204_0758.060','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021206_0745.116.3191','sm78.021206_0745.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021210_0802.105.2417','sm78.021210_0802.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021214_0604.104.1866','sm78.021214_0604.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021216_0651.086.1318','sm78.021216_0651.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.021229_0702.105.816','sm78.021229_0702.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm78.030103_0713.102.283','sm78.030103_0713.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021002_0926.081.2331','sm79.021002_0926.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021004_0937.104.1647','sm79.021004_0937.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021006_0925.135.1003','sm79.021006_0925.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021105_0731.100.373','sm79.021105_0731.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021109_0800.086.3665','sm79.021109_0800.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021111_0730.125.3213','sm79.021111_0730.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021113_0746.116.2583','sm79.021113_0746.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021115_0720.115.2030','sm79.021115_0720.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021128_0738.098.1480','sm79.021128_0738.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021202_0741.124.718','sm79.021202_0741.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021204_0755.059.218','sm79.021204_0755.059','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021206_0742.115.3195','sm79.021206_0742.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021210_0758.104.2421','sm79.021210_0758.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021214_0601.103.1870','sm79.021214_0601.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021216_0648.085.1321','sm79.021216_0648.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.021229_0659.104.820','sm79.021229_0659.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm79.030103_0709.101.287','sm79.030103_0709.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021002_0924.080.2335','sm83.021002_0924.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021004_0935.103.1651','sm83.021004_0935.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021006_0923.134.1007','sm83.021006_0923.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021105_0728.099.377','sm83.021105_0728.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021109_0757.085.3667','sm83.021109_0757.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021111_0726.124.3218','sm83.021111_0726.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021113_0742.115.2588','sm83.021113_0742.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021115_0716.114.2034','sm83.021115_0716.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021128_0734.097.1484','sm83.021128_0734.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021202_0738.123.722','sm83.021202_0738.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021204_0752.058.222','sm83.021204_0752.058','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021206_0738.114.3199','sm83.021206_0738.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021210_0754.103.2425','sm83.021210_0754.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021214_0557.102.1874','sm83.021214_0557.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021216_0644.084.1324','sm83.021216_0644.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.021229_0655.103.824','sm83.021229_0655.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm83.030103_0705.100.291','sm83.030103_0705.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.020928_0749.112.3716','sm84.020928_0749.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.020930_0734.156.3135','sm84.020930_0734.156','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021002_0733.048.2338','sm84.021002_0733.048','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021004_0748.071.1655','sm84.021004_0748.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021006_0722.121.1011','sm84.021006_0722.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021008_0800.065.248','sm84.021008_0800.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021010_0904.118.3322','sm84.021010_0904.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021012_0654.122.2745','sm84.021012_0654.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021014_0829.130.2150','sm84.021014_0829.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021030_0729.108.1599','sm84.021030_0729.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021101_0718.115.1067','sm84.021101_0718.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021105_0724.098.381','sm84.021105_0724.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021109_0753.084.3669','sm84.021109_0753.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021111_0722.123.3221','sm84.021111_0722.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021113_0738.114.2592','sm84.021113_0738.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021115_0712.113.2039','sm84.021115_0712.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021128_0730.096.1488','sm84.021128_0730.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021202_0734.122.726','sm84.021202_0734.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021204_0731.098.226','sm84.021204_0731.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021206_0735.113.3202','sm84.021206_0735.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021210_0750.102.2428','sm84.021210_0750.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021214_0554.101.1878','sm84.021214_0554.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021216_0641.083.1328','sm84.021216_0641.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.021229_0651.102.828','sm84.021229_0651.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm84.030103_0701.099.295','sm84.030103_0701.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.020928_0744.111.3718','sm85.020928_0744.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.020930_0728.155.3139','sm85.020930_0728.155','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021002_0728.047.2342','sm85.021002_0728.047','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021004_0743.070.1659','sm85.021004_0743.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021006_0717.120.1015','sm85.021006_0717.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021008_0756.064.252','sm85.021008_0756.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021010_0859.117.3327','sm85.021010_0859.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021012_0649.121.2748','sm85.021012_0649.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021014_0824.129.2154','sm85.021014_0824.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021030_0723.107.1603','sm85.021030_0723.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021101_0713.114.1071','sm85.021101_0713.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021105_0719.097.385','sm85.021105_0719.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021109_0748.083.3671','sm85.021109_0748.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021111_0717.122.3225','sm85.021111_0717.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021113_0733.113.2596','sm85.021113_0733.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021115_0707.112.2043','sm85.021115_0707.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021128_0725.095.1492','sm85.021128_0725.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021202_0729.121.730','sm85.021202_0729.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021204_0726.097.230','sm85.021204_0726.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021206_0729.112.3204','sm85.021206_0729.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021210_0745.101.2432','sm85.021210_0745.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021214_0549.100.1882','sm85.021214_0549.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021216_0636.082.1332','sm85.021216_0636.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.021229_0646.101.832','sm85.021229_0646.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm85.030103_0655.098.299','sm85.030103_0655.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.020928_0736.109.3720','sm86.020928_0736.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.020928_0740.110.3722','sm86.020928_0740.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.020930_0720.153.3143','sm86.020930_0720.153','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.020930_0724.154.3147','sm86.020930_0724.154','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021002_0721.045.2346','sm86.021002_0721.045','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021002_0724.046.2350','sm86.021002_0724.046','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021004_0735.068.1663','sm86.021004_0735.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021004_0739.069.1666','sm86.021004_0739.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021006_0709.118.1017','sm86.021006_0709.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021006_0713.119.1021','sm86.021006_0713.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021008_0738.062.256','sm86.021008_0738.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021008_0741.063.260','sm86.021008_0741.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021010_0851.115.3331','sm86.021010_0851.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021010_0855.116.3334','sm86.021010_0855.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021012_0641.119.2751','sm86.021012_0641.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021012_0645.120.2755','sm86.021012_0645.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021014_0816.127.2158','sm86.021014_0816.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021014_0820.128.2161','sm86.021014_0820.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021030_0716.105.1608','sm86.021030_0716.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021030_0719.106.1611','sm86.021030_0719.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021101_0707.112.1075','sm86.021101_0707.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021101_0710.113.1079','sm86.021101_0710.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021105_0715.096.389','sm86.021105_0715.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021109_0744.082.3673','sm86.021109_0744.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021111_0713.121.3228','sm86.021111_0713.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021113_0729.112.2600','sm86.021113_0729.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021115_0703.111.2046','sm86.021115_0703.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021128_0721.094.1496','sm86.021128_0721.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021202_0725.120.734','sm86.021202_0725.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021204_0722.096.234','sm86.021204_0722.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021206_0725.111.3208','sm86.021206_0725.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021210_0740.100.2437','sm86.021210_0740.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021214_0544.099.1886','sm86.021214_0544.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021216_0632.081.1336','sm86.021216_0632.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.021229_0642.100.836','sm86.021229_0642.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm86.030103_0651.097.303','sm86.030103_0651.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.020928_0728.107.3724','sm87.020928_0728.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.020928_0732.108.3726','sm87.020928_0732.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.020930_0712.151.3151','sm87.020930_0712.151','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.020930_0716.152.3155','sm87.020930_0716.152','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021002_0713.043.2354','sm87.021002_0713.043','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021002_0717.044.2358','sm87.021002_0717.044','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021004_0728.066.1670','sm87.021004_0728.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021004_0732.067.1674','sm87.021004_0732.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021006_0702.116.1026','sm87.021006_0702.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021006_0706.117.1030','sm87.021006_0706.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021008_0722.074.264','sm87.021008_0722.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021008_0726.075.268','sm87.021008_0726.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021010_0844.113.3338','sm87.021010_0844.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021010_0847.114.3342','sm87.021010_0847.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021012_0634.117.2759','sm87.021012_0634.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021012_0638.118.2763','sm87.021012_0638.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021014_0809.125.2165','sm87.021014_0809.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021014_0813.126.2169','sm87.021014_0813.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021030_0708.103.1616','sm87.021030_0708.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021030_0712.104.1620','sm87.021030_0712.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021101_0659.110.1083','sm87.021101_0659.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021101_0703.111.1087','sm87.021101_0703.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021105_0711.095.393','sm87.021105_0711.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021109_0739.081.3675','sm87.021109_0739.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021111_0707.120.3232','sm87.021111_0707.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021113_0725.111.2604','sm87.021113_0725.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021115_0658.110.2050','sm87.021115_0658.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021128_0716.093.1500','sm87.021128_0716.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021202_0720.119.738','sm87.021202_0720.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021204_0718.095.238','sm87.021204_0718.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021206_0720.110.3212','sm87.021206_0720.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021210_0735.099.2441','sm87.021210_0735.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021214_0540.098.1890','sm87.021214_0540.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021216_0627.080.1340','sm87.021216_0627.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.021229_0638.099.840','sm87.021229_0638.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm87.030103_0646.096.307','sm87.030103_0646.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.020928_0704.134.3728','sm88.020928_0704.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.020928_0723.106.3730','sm88.020928_0723.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.020930_0707.150.3159','sm88.020930_0707.150','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021002_0708.042.2362','sm88.021002_0708.042','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021004_0723.065.1678','sm88.021004_0723.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021006_0657.115.1035','sm88.021006_0657.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021008_0717.073.272','sm88.021008_0717.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021010_0838.112.3346','sm88.021010_0838.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021012_0628.116.2767','sm88.021012_0628.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021014_0804.124.2174','sm88.021014_0804.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021030_0702.102.1624','sm88.021030_0702.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021101_0655.109.1091','sm88.021101_0655.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021105_0706.094.397','sm88.021105_0706.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021109_0735.080.3677','sm88.021109_0735.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021111_0703.119.3236','sm88.021111_0703.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021113_0720.110.2608','sm88.021113_0720.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021115_0654.109.2054','sm88.021115_0654.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021128_0712.092.1504','sm88.021128_0712.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021202_0716.118.742','sm88.021202_0716.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021204_0714.094.242','sm88.021204_0714.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021206_0716.109.3216','sm88.021206_0716.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021210_0730.098.2445','sm88.021210_0730.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021214_0535.097.1894','sm88.021214_0535.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021216_0623.079.1344','sm88.021216_0623.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.021229_0633.098.843','sm88.021229_0633.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm88.030103_0642.095.311','sm88.030103_0642.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021002_0922.079.2366','sm89.021002_0922.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021004_0933.102.1682','sm89.021004_0933.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021006_0921.133.1039','sm89.021006_0921.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021012_0912.096.2771','sm89.021012_0912.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021105_0704.093.401','sm89.021105_0704.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021109_0732.079.3679','sm89.021109_0732.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021111_0701.118.3240','sm89.021111_0701.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021113_0718.109.2612','sm89.021113_0718.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021115_0652.108.2058','sm89.021115_0652.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021128_0710.091.1508','sm89.021128_0710.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021202_0714.117.746','sm89.021202_0714.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021204_0712.093.246','sm89.021204_0712.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021206_0714.108.3220','sm89.021206_0714.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021210_0727.097.2449','sm89.021210_0727.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021214_0533.096.1898','sm89.021214_0533.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021216_0621.078.1349','sm89.021216_0621.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.021229_0631.097.847','sm89.021229_0631.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm89.030103_0640.094.315','sm89.030103_0640.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021002_0920.078.2370','sm8a.021002_0920.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021004_0931.101.1686','sm8a.021004_0931.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021006_0919.132.1043','sm8a.021006_0919.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021012_0910.095.2776','sm8a.021012_0910.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021105_0702.092.405','sm8a.021105_0702.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021109_0729.078.3681','sm8a.021109_0729.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021111_0658.117.3244','sm8a.021111_0658.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021113_0715.108.2616','sm8a.021113_0715.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021115_0649.107.2062','sm8a.021115_0649.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021128_0707.090.1512','sm8a.021128_0707.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021202_0711.116.751','sm8a.021202_0711.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021204_0709.092.251','sm8a.021204_0709.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021206_0711.107.3224','sm8a.021206_0711.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021210_0724.096.2453','sm8a.021210_0724.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021214_0531.095.1902','sm8a.021214_0531.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021216_0618.077.1353','sm8a.021216_0618.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.021229_0628.096.852','sm8a.021229_0628.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm8a.030103_0637.093.319','sm8a.030103_0637.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.020928_0928.139.3733','sm93.020928_0928.139','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.020930_0925.183.3163','sm93.020930_0925.183','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021002_0913.075.2374','sm93.021002_0913.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021004_0924.098.1690','sm93.021004_0924.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021006_0912.129.1048','sm93.021006_0912.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021012_0902.092.2781','sm93.021012_0902.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021101_0847.142.1095','sm93.021101_0847.142','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021105_0631.084.409','sm93.021105_0631.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021109_0643.104.3683','sm93.021109_0643.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021111_0625.109.3248','sm93.021111_0625.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021113_0643.100.2620','sm93.021113_0643.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021115_0617.099.2066','sm93.021115_0617.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021128_0630.082.1516','sm93.021128_0630.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021202_0640.108.755','sm93.021202_0640.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021204_0638.084.255','sm93.021204_0638.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021206_0636.099.3230','sm93.021206_0636.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021210_0651.088.2457','sm93.021210_0651.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021214_0459.087.1906','sm93.021214_0459.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021216_0547.069.1357','sm93.021216_0547.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.021229_0558.088.856','sm93.021229_0558.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm93.030103_0604.085.323','sm93.030103_0604.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.020928_0931.140.3735','sm94.020928_0931.140','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021002_0915.076.2378','sm94.021002_0915.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021004_0926.099.1694','sm94.021004_0926.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021006_0914.130.1053','sm94.021006_0914.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021012_0904.093.2785','sm94.021012_0904.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021101_0850.143.1099','sm94.021101_0850.143','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021105_0634.085.413','sm94.021105_0634.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021109_0646.105.3685','sm94.021109_0646.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021111_0629.110.3252','sm94.021111_0629.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021113_0646.101.2623','sm94.021113_0646.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021115_0620.100.2070','sm94.021115_0620.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021128_0635.083.1520','sm94.021128_0635.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021202_0643.109.759','sm94.021202_0643.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021204_0641.085.259','sm94.021204_0641.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021206_0639.100.3234','sm94.021206_0639.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021210_0654.089.2461','sm94.021210_0654.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021214_0502.088.1910','sm94.021214_0502.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021216_0550.070.1361','sm94.021216_0550.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.021229_0600.089.860','sm94.021229_0600.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm94.030103_0607.086.326','sm94.030103_0607.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.020928_0632.127.3737','sm95.020928_0632.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.020930_0631.143.3167','sm95.020930_0631.143','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021002_0618.059.2382','sm95.021002_0618.059','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021004_0648.058.1698','sm95.021004_0648.058','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021006_0620.134.1057','sm95.021006_0620.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021008_0644.066.276','sm95.021008_0644.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021010_0805.105.3350','sm95.021010_0805.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021012_0553.109.2789','sm95.021012_0553.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021014_0731.117.2178','sm95.021014_0731.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021030_0622.095.1628','sm95.021030_0622.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021101_0628.102.1103','sm95.021101_0628.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021105_0638.086.417','sm95.021105_0638.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021109_0650.106.3687','sm95.021109_0650.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021111_0633.111.3256','sm95.021111_0633.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021113_0650.102.2627','sm95.021113_0650.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021115_0624.101.2074','sm95.021115_0624.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021128_0640.084.1524','sm95.021128_0640.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021202_0647.110.763','sm95.021202_0647.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021204_0645.086.263','sm95.021204_0645.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021206_0643.101.3238','sm95.021206_0643.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021210_0658.090.2465','sm95.021210_0658.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021214_0505.089.1914','sm95.021214_0505.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021216_0554.071.1365','sm95.021216_0554.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.021229_0604.090.864','sm95.021229_0604.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm95.030103_0611.087.330','sm95.030103_0611.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.020928_0637.128.3739','sm96.020928_0637.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.020928_0642.129.3741','sm96.020928_0642.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.020930_0637.144.3169','sm96.020930_0637.144','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.020930_0642.145.3174','sm96.020930_0642.145','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021002_0638.038.2386','sm96.021002_0638.038','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021002_0643.039.2390','sm96.021002_0643.039','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021004_0655.059.1702','sm96.021004_0655.059','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021004_0700.060.1706','sm96.021004_0700.060','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021006_0625.135.1061','sm96.021006_0625.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021006_0630.136.1065','sm96.021006_0630.136','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021008_0649.067.280','sm96.021008_0649.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021008_0655.068.285','sm96.021008_0655.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021010_0810.106.3354','sm96.021010_0810.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021010_0815.107.3358','sm96.021010_0815.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021012_0558.110.2793','sm96.021012_0558.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021012_0604.111.2796','sm96.021012_0604.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021014_0736.118.2182','sm96.021014_0736.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021014_0741.119.2186','sm96.021014_0741.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021030_0628.096.1632','sm96.021030_0628.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021030_0633.097.1634','sm96.021030_0633.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021101_0632.103.1107','sm96.021101_0632.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021101_0636.104.1111','sm96.021101_0636.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021105_0642.087.421','sm96.021105_0642.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021109_0654.107.3689','sm96.021109_0654.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021111_0638.112.3260','sm96.021111_0638.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021113_0654.103.2631','sm96.021113_0654.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021115_0628.102.2078','sm96.021115_0628.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021128_0645.085.1528','sm96.021128_0645.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021202_0651.111.767','sm96.021202_0651.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021204_0649.087.267','sm96.021204_0649.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021206_0648.102.3243','sm96.021206_0648.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021210_0702.091.2469','sm96.021210_0702.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021214_0510.090.1918','sm96.021214_0510.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021216_0558.072.1369','sm96.021216_0558.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.021229_0608.091.866','sm96.021229_0608.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm96.030103_0616.088.334','sm96.030103_0616.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.020928_0647.130.3743','sm97.020928_0647.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.020928_0650.131.3745','sm97.020928_0650.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.020930_0648.146.3176','sm97.020930_0648.146','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.020930_0652.147.3181','sm97.020930_0652.147','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021002_0651.038.2394','sm97.021002_0651.038','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021002_0655.039.2398','sm97.021002_0655.039','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021004_0705.061.1712','sm97.021004_0705.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021004_0709.062.1715','sm97.021004_0709.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021006_0636.137.1069','sm97.021006_0636.137','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021008_0700.069.289','sm97.021008_0700.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021008_0703.070.293','sm97.021008_0703.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021010_0820.108.3362','sm97.021010_0820.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021010_0824.109.3366','sm97.021010_0824.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021012_0610.112.2800','sm97.021012_0610.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021012_0614.113.2804','sm97.021012_0614.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021014_0746.120.2190','sm97.021014_0746.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021014_0750.121.2195','sm97.021014_0750.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021030_0638.098.1637','sm97.021030_0638.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021030_0642.099.1641','sm97.021030_0642.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021101_0640.105.1115','sm97.021101_0640.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021101_0644.106.1119','sm97.021101_0644.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021105_0647.088.425','sm97.021105_0647.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021109_0700.108.3691','sm97.021109_0700.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021111_0643.113.3264','sm97.021111_0643.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021113_0659.104.2635','sm97.021113_0659.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021115_0633.103.2082','sm97.021115_0633.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021128_0650.086.1532','sm97.021128_0650.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021202_0656.112.771','sm97.021202_0656.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021204_0654.088.270','sm97.021204_0654.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021206_0655.103.3247','sm97.021206_0655.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021210_0708.092.2474','sm97.021210_0708.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021214_0515.091.1922','sm97.021214_0515.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021216_0603.073.1373','sm97.021216_0603.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.021229_0613.092.870','sm97.021229_0613.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm97.030103_0621.089.338','sm97.030103_0621.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.020928_0654.132.3747','sm98.020928_0654.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.020930_0656.148.3185','sm98.020930_0656.148','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021002_0658.040.2402','sm98.021002_0658.040','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021004_0713.063.1719','sm98.021004_0713.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021006_0647.113.1073','sm98.021006_0647.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021008_0707.071.297','sm98.021008_0707.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021010_0828.110.3370','sm98.021010_0828.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021012_0617.114.2808','sm98.021012_0617.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021014_0754.122.2197','sm98.021014_0754.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021030_0646.100.1645','sm98.021030_0646.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021101_0647.107.1123','sm98.021101_0647.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021105_0651.089.428','sm98.021105_0651.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021109_0704.109.3693','sm98.021109_0704.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021111_0648.114.3268','sm98.021111_0648.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021113_0704.105.2639','sm98.021113_0704.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021115_0638.104.2086','sm98.021115_0638.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021128_0654.087.1536','sm98.021128_0654.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021202_0700.113.775','sm98.021202_0700.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021204_0658.089.274','sm98.021204_0658.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021206_0659.104.3251','sm98.021206_0659.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021210_0712.093.2478','sm98.021210_0712.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021214_0519.092.1926','sm98.021214_0519.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021216_0607.074.1377','sm98.021216_0607.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.021229_0617.093.874','sm98.021229_0617.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm98.030103_0625.090.342','sm98.030103_0625.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.020928_0659.133.3748','sm99.020928_0659.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.020930_0701.149.3189','sm99.020930_0701.149','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021002_0703.041.2406','sm99.021002_0703.041','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021004_0718.064.1723','sm99.021004_0718.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021006_0652.114.1077','sm99.021006_0652.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021008_0712.072.301','sm99.021008_0712.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021010_0833.111.3374','sm99.021010_0833.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021012_0623.115.2812','sm99.021012_0623.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021014_0759.123.2201','sm99.021014_0759.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021030_0651.101.1649','sm99.021030_0651.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021101_0651.108.1127','sm99.021101_0651.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021105_0659.091.432','sm99.021105_0659.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021109_0715.111.3695','sm99.021109_0715.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021111_0655.116.3272','sm99.021111_0655.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021113_0712.107.2643','sm99.021113_0712.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021115_0646.106.2090','sm99.021115_0646.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021128_0703.089.1540','sm99.021128_0703.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021202_0708.115.779','sm99.021202_0708.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021204_0706.091.278','sm99.021204_0706.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021206_0708.106.3255','sm99.021206_0708.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021210_0721.095.2482','sm99.021210_0721.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021214_0527.094.1930','sm99.021214_0527.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021216_0615.076.1381','sm99.021216_0615.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.021229_0625.095.878','sm99.021229_0625.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm99.030103_0633.092.346','sm99.030103_0633.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.020928_0933.141.3750','sm9a.020928_0933.141','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021002_0918.077.2410','sm9a.021002_0918.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021004_0928.100.1727','sm9a.021004_0928.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021006_0916.131.1081','sm9a.021006_0916.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021012_0907.094.2816','sm9a.021012_0907.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021101_0852.144.1131','sm9a.021101_0852.144','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021105_0656.090.436','sm9a.021105_0656.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021109_0712.110.3697','sm9a.021109_0712.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021111_0653.115.3276','sm9a.021111_0653.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021113_0709.106.2647','sm9a.021113_0709.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021115_0643.105.2094','sm9a.021115_0643.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021128_0700.088.1544','sm9a.021128_0700.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021202_0705.114.783','sm9a.021202_0705.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021204_0703.090.282','sm9a.021204_0703.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021206_0704.105.3259','sm9a.021206_0704.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021210_0718.094.2486','sm9a.021210_0718.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021214_0525.093.1934','sm9a.021214_0525.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021216_0612.075.1385','sm9a.021216_0612.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.021229_0623.094.882','sm9a.021229_0623.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sm9a.030103_0630.091.350','sm9a.030103_0630.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.020928_0924.137.3752','sma4.020928_0924.137','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.020930_0920.181.3193','sma4.020930_0920.181','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021002_0909.073.2414','sma4.021002_0909.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021004_0920.096.1731','sma4.021004_0920.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021006_0908.127.1085','sma4.021006_0908.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021012_0857.090.2820','sma4.021012_0857.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021101_0843.140.1135','sma4.021101_0843.140','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021105_0603.077.440','sma4.021105_0603.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021109_0613.097.3699','sma4.021109_0613.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021111_0556.102.3280','sma4.021111_0556.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021113_0614.093.2651','sma4.021113_0614.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021115_0547.092.2098','sma4.021115_0547.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021128_0601.075.1548','sma4.021128_0601.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021202_0611.101.787','sma4.021202_0611.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021204_0610.077.286','sma4.021204_0610.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021206_0605.092.3263','sma4.021206_0605.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021210_0528.071.2490','sma4.021210_0528.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021214_0431.080.1938','sma4.021214_0431.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021216_0519.062.1389','sma4.021216_0519.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.021229_0529.081.886','sma4.021229_0529.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma4.030103_0534.078.354','sma4.030103_0534.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.020928_0608.122.3754','sma5.020928_0608.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.020930_0605.138.3197','sma5.020930_0605.138','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021002_0554.054.2418','sma5.021002_0554.054','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021004_0606.072.1736','sma5.021004_0606.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021006_0556.129.810','sma5.021006_0556.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021008_0613.078.304','sma5.021008_0613.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021010_0732.117.3378','sma5.021010_0732.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021012_0526.104.2823','sma5.021012_0526.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021014_0525.099.2207','sma5.021014_0525.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021030_0557.090.1653','sma5.021030_0557.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021101_0607.097.1139','sma5.021101_0607.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021105_0606.078.444','sma5.021105_0606.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021109_0616.098.3701','sma5.021109_0616.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021111_0559.103.3284','sma5.021111_0559.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021113_0617.094.2655','sma5.021113_0617.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021115_0550.093.2102','sma5.021115_0550.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021128_0603.076.1552','sma5.021128_0603.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021202_0614.102.791','sma5.021202_0614.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021204_0613.078.290','sma5.021204_0613.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021206_0608.093.3267','sma5.021206_0608.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021210_0534.072.2494','sma5.021210_0534.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021214_0434.081.1942','sma5.021214_0434.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021216_0522.063.1393','sma5.021216_0522.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.021229_0532.082.890','sma5.021229_0532.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma5.030103_0537.079.358','sma5.030103_0537.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.020928_0613.123.3756','sma6.020928_0613.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.020930_0611.139.3203','sma6.020930_0611.139','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021002_0559.055.2422','sma6.021002_0559.055','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021004_0611.073.1740','sma6.021004_0611.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021006_0602.130.814','sma6.021006_0602.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021008_0627.062.309','sma6.021008_0627.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021010_0737.118.3382','sma6.021010_0737.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021012_0532.105.2828','sma6.021012_0532.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021014_0537.100.2209','sma6.021014_0537.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021030_0603.091.1657','sma6.021030_0603.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021101_0611.098.1143','sma6.021101_0611.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021105_0610.079.447','sma6.021105_0610.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021109_0620.099.3703','sma6.021109_0620.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021111_0603.104.3288','sma6.021111_0603.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021113_0621.095.2659','sma6.021113_0621.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021115_0554.094.2106','sma6.021115_0554.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021128_0607.077.1556','sma6.021128_0607.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021202_0618.103.795','sma6.021202_0618.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021204_0617.079.294','sma6.021204_0617.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021206_0613.094.3270','sma6.021206_0613.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021210_0537.073.2498','sma6.021210_0537.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021214_0438.082.1946','sma6.021214_0438.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021216_0526.064.1397','sma6.021216_0526.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.021229_0536.083.894','sma6.021229_0536.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma6.030103_0541.080.362','sma6.030103_0541.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.020928_0618.124.3758','sma7.020928_0618.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.020930_0616.140.3206','sma7.020930_0616.140','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021002_0604.056.2426','sma7.021002_0604.056','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021004_0616.074.1742','sma7.021004_0616.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021006_0607.131.818','sma7.021006_0607.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021008_0631.063.313','sma7.021008_0631.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021010_0742.119.3386','sma7.021010_0742.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021012_0537.106.2831','sma7.021012_0537.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021014_0542.101.2214','sma7.021014_0542.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021030_0608.092.1661','sma7.021030_0608.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021101_0616.099.1147','sma7.021101_0616.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021105_0615.080.452','sma7.021105_0615.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021109_0626.100.3705','sma7.021109_0626.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021111_0609.105.3292','sma7.021111_0609.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021113_0626.096.2663','sma7.021113_0626.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021115_0600.095.2110','sma7.021115_0600.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021128_0612.078.1560','sma7.021128_0612.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021202_0623.104.799','sma7.021202_0623.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021204_0622.080.298','sma7.021204_0622.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021206_0618.095.3274','sma7.021206_0618.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021210_0542.074.2502','sma7.021210_0542.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021214_0443.083.1950','sma7.021214_0443.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021216_0531.065.1401','sma7.021216_0531.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.021229_0541.084.898','sma7.021229_0541.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma7.030103_0546.081.366','sma7.030103_0546.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.020928_0623.125.3760','sma8.020928_0623.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.020930_0622.141.3211','sma8.020930_0622.141','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021002_0610.057.2430','sma8.021002_0610.057','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021004_0621.075.1746','sma8.021004_0621.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021004_0641.056.1750','sma8.021004_0641.056','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021006_0612.132.822','sma8.021006_0612.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021008_0636.064.317','sma8.021008_0636.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021010_0747.120.3389','sma8.021010_0747.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021012_0543.107.2835','sma8.021012_0543.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021014_0547.102.2218','sma8.021014_0547.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021030_0614.093.1665','sma8.021030_0614.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021101_0621.100.1151','sma8.021101_0621.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021105_0620.081.456','sma8.021105_0620.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021109_0631.101.3707','sma8.021109_0631.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021111_0614.106.3296','sma8.021111_0614.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021113_0631.097.2667','sma8.021113_0631.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021115_0605.096.2114','sma8.021115_0605.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021128_0617.079.1564','sma8.021128_0617.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021202_0628.105.803','sma8.021202_0628.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021204_0627.081.302','sma8.021204_0627.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021206_0623.096.3278','sma8.021206_0623.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021210_0548.075.2505','sma8.021210_0548.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021214_0448.084.1954','sma8.021214_0448.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021216_0536.066.1405','sma8.021216_0536.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.021229_0546.085.902','sma8.021229_0546.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma8.030103_0552.082.370','sma8.030103_0552.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.020928_0628.126.3762','sma9.020928_0628.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.020930_0627.142.3214','sma9.020930_0627.142','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021002_0615.058.2434','sma9.021002_0615.058','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021004_0645.057.1754','sma9.021004_0645.057','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021006_0617.133.826','sma9.021006_0617.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021008_0641.065.321','sma9.021008_0641.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021010_0802.104.3393','sma9.021010_0802.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021012_0548.108.2838','sma9.021012_0548.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021014_0725.116.2222','sma9.021014_0725.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021030_0619.094.1669','sma9.021030_0619.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021101_0625.101.1155','sma9.021101_0625.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021105_0625.082.460','sma9.021105_0625.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021109_0636.102.3709','sma9.021109_0636.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021111_0619.107.3300','sma9.021111_0619.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021113_0637.098.2672','sma9.021113_0637.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021115_0610.097.2118','sma9.021115_0610.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021128_0622.080.1568','sma9.021128_0622.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021202_0633.106.807','sma9.021202_0633.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021204_0632.082.306','sma9.021204_0632.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021206_0629.097.3282','sma9.021206_0629.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021210_0553.076.2510','sma9.021210_0553.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021214_0453.085.1958','sma9.021214_0453.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021216_0541.067.1409','sma9.021216_0541.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.021229_0551.086.906','sma9.021229_0551.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sma9.030103_0557.083.374','sma9.030103_0557.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.020928_0926.138.3764','smaa.020928_0926.138','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.020930_0922.182.3217','smaa.020930_0922.182','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021002_0911.074.2438','smaa.021002_0911.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021004_0922.097.1758','smaa.021004_0922.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021006_0910.128.830','smaa.021006_0910.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021012_0859.091.2842','smaa.021012_0859.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021101_0845.141.1159','smaa.021101_0845.141','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021105_0629.083.464','smaa.021105_0629.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021109_0641.103.3711','smaa.021109_0641.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021111_0623.108.3304','smaa.021111_0623.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021113_0641.099.2676','smaa.021113_0641.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021115_0614.098.2122','smaa.021115_0614.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021128_0627.081.1572','smaa.021128_0627.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021202_0638.107.811','smaa.021202_0638.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021204_0636.083.310','smaa.021204_0636.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021206_0633.098.3285','smaa.021206_0633.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021210_0557.077.2513','smaa.021210_0557.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021214_0457.086.1962','smaa.021214_0457.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021216_0545.068.1413','smaa.021216_0545.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.021229_0555.087.910','smaa.021229_0555.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smaa.030103_0602.084.378','smaa.030103_0602.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.020928_0922.136.3766','smb5.020928_0922.136','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.020930_0918.180.3223','smb5.020930_0918.180','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021002_0907.072.2442','smb5.021002_0907.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021004_0918.095.1762','smb5.021004_0918.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021006_0906.126.834','smb5.021006_0906.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021012_0854.089.2846','smb5.021012_0854.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021101_0841.139.1163','smb5.021101_0841.139','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021105_0526.089.468','smb5.021105_0526.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021109_0545.091.3713','smb5.021109_0545.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021111_0530.096.3308','smb5.021111_0530.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021113_0546.087.2680','smb5.021113_0546.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021115_0519.086.2126','smb5.021115_0519.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021128_0534.069.1576','smb5.021128_0534.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021202_0544.095.815','smb5.021202_0544.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021204_0544.071.314','smb5.021204_0544.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021206_0537.086.3290','smb5.021206_0537.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021208_0748.061.2853','smb5.021208_0748.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021210_0453.065.2517','smb5.021210_0453.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021210_0620.082.2521','smb5.021210_0620.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021214_0404.074.1966','smb5.021214_0404.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021216_0438.074.1417','smb5.021216_0438.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.021229_0503.075.914','smb5.021229_0503.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb5.030103_0505.072.382','smb5.030103_0505.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.020928_0543.117.3768','smb6.020928_0543.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.020930_0538.133.3227','smb6.020930_0538.133','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021002_0529.049.2446','smb6.021002_0529.049','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021004_0541.067.1766','smb6.021004_0541.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021006_0532.124.839','smb6.021006_0532.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021008_0548.073.325','smb6.021008_0548.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021010_0707.112.3399','smb6.021010_0707.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021012_0500.099.2850','smb6.021012_0500.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021014_0501.095.2226','smb6.021014_0501.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021030_0532.085.1673','smb6.021030_0532.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021101_0547.092.1167','smb6.021101_0547.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021105_0530.090.472','smb6.021105_0530.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021109_0549.092.3715','smb6.021109_0549.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021111_0533.097.3312','smb6.021111_0533.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021113_0550.088.2684','smb6.021113_0550.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021115_0522.087.2131','smb6.021115_0522.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021128_0537.070.1580','smb6.021128_0537.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021202_0548.096.819','smb6.021202_0548.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021204_0547.072.318','smb6.021204_0547.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021206_0541.087.3294','smb6.021206_0541.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021208_0752.062.2857','smb6.021208_0752.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021210_0458.066.2525','smb6.021210_0458.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021210_0625.083.2530','smb6.021210_0625.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021214_0408.075.1970','smb6.021214_0408.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021216_0442.075.1421','smb6.021216_0442.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.021229_0506.076.918','smb6.021229_0506.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb6.030103_0509.073.386','smb6.030103_0509.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.020928_0548.118.3770','smb7.020928_0548.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.020930_0544.134.3231','smb7.020930_0544.134','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021002_0534.050.2450','smb7.021002_0534.050','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021004_0546.068.1770','smb7.021004_0546.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021006_0537.125.844','smb7.021006_0537.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021008_0553.074.329','smb7.021008_0553.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021010_0712.113.3403','smb7.021010_0712.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021012_0505.100.2854','smb7.021012_0505.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021014_0506.096.2230','smb7.021014_0506.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021030_0537.086.1677','smb7.021030_0537.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021101_0551.093.1171','smb7.021101_0551.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021105_0535.091.476','smb7.021105_0535.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021109_0554.093.3717','smb7.021109_0554.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021111_0538.098.3316','smb7.021111_0538.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021113_0555.089.2688','smb7.021113_0555.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021115_0528.088.2134','smb7.021115_0528.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021128_0542.071.1584','smb7.021128_0542.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021202_0553.097.823','smb7.021202_0553.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021204_0552.073.322','smb7.021204_0552.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021206_0546.088.3297','smb7.021206_0546.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021210_0504.067.2534','smb7.021210_0504.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021210_0630.084.2538','smb7.021210_0630.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021214_0413.076.1974','smb7.021214_0413.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021216_0447.076.1425','smb7.021216_0447.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.021229_0511.077.922','smb7.021229_0511.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb7.030103_0515.074.390','smb7.030103_0515.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.020928_0553.119.3772','smb8.020928_0553.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.020930_0549.135.3235','smb8.020930_0549.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021002_0539.051.2454','smb8.021002_0539.051','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021004_0551.069.1774','smb8.021004_0551.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021006_0542.126.848','smb8.021006_0542.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021008_0558.075.333','smb8.021008_0558.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021010_0718.114.3407','smb8.021010_0718.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021012_0511.101.2858','smb8.021012_0511.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021030_0542.087.1681','smb8.021030_0542.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021101_0555.094.1175','smb8.021101_0555.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021105_0541.092.479','smb8.021105_0541.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021109_0559.094.3719','smb8.021109_0559.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021111_0543.099.3320','smb8.021111_0543.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021113_0600.090.2692','smb8.021113_0600.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021115_0533.089.2139','smb8.021115_0533.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021128_0547.072.1588','smb8.021128_0547.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021202_0558.098.827','smb8.021202_0558.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021204_0557.074.327','smb8.021204_0557.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021206_0552.089.3302','smb8.021206_0552.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021210_0511.068.2542','smb8.021210_0511.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021210_0636.085.2545','smb8.021210_0636.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021214_0418.077.1978','smb8.021214_0418.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021216_0452.077.1429','smb8.021216_0452.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.021229_0516.078.926','smb8.021229_0516.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb8.030103_0520.075.394','smb8.030103_0520.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.020928_0558.120.3774','smb9.020928_0558.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.020930_0555.136.3237','smb9.020930_0555.136','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021002_0545.052.2458','smb9.021002_0545.052','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021004_0556.070.1778','smb9.021004_0556.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021006_0547.127.851','smb9.021006_0547.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021008_0603.076.337','smb9.021008_0603.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021010_0723.115.3411','smb9.021010_0723.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021012_0516.102.2862','smb9.021012_0516.102','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021014_0516.097.2234','smb9.021014_0516.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021030_0547.088.1685','smb9.021030_0547.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021101_0559.095.1179','smb9.021101_0559.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021105_0546.093.483','smb9.021105_0546.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021109_0605.095.3721','smb9.021109_0605.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021111_0548.100.3324','smb9.021111_0548.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021113_0606.091.2696','smb9.021113_0606.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021115_0538.090.2143','smb9.021115_0538.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021128_0552.073.1592','smb9.021128_0552.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021202_0603.099.831','smb9.021202_0603.099','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021204_0602.075.331','smb9.021204_0602.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021206_0557.090.3307','smb9.021206_0557.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021210_0641.086.2549','smb9.021210_0641.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021214_0423.078.1982','smb9.021214_0423.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021216_0457.078.1433','smb9.021216_0457.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.021229_0521.079.931','smb9.021229_0521.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smb9.030103_0525.076.398','smb9.030103_0525.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.020928_0603.121.3776','smba.020928_0603.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.020930_0600.137.3241','smba.020930_0600.137','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021002_0550.053.2462','smba.021002_0550.053','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021004_0602.071.1782','smba.021004_0602.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021006_0552.128.855','smba.021006_0552.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021008_0608.077.341','smba.021008_0608.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021010_0728.116.3413','smba.021010_0728.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021012_0522.103.2866','smba.021012_0522.103','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021014_0520.098.2238','smba.021014_0520.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021030_0552.089.1689','smba.021030_0552.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021101_0603.096.1183','smba.021101_0603.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021105_0551.094.488','smba.021105_0551.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021105_0601.076.491','smba.021105_0601.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021109_0610.096.3723','smba.021109_0610.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021111_0553.101.3328','smba.021111_0553.101','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021113_0611.092.2700','smba.021113_0611.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021115_0543.091.2147','smba.021115_0543.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021128_0557.074.1596','smba.021128_0557.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021202_0608.100.835','smba.021202_0608.100','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021204_0607.076.335','smba.021204_0607.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021206_0602.091.3311','smba.021206_0602.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021210_0525.070.2553','smba.021210_0525.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021214_0428.079.1986','smba.021214_0428.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021216_0516.061.1437','smba.021216_0516.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.021229_0526.080.934','smba.021229_0526.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smba.030103_0531.077.402','smba.030103_0531.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.020930_0910.178.3245','smc5.020930_0910.178','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021002_0902.070.2466','smc5.021002_0902.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021004_0913.093.1786','smc5.021004_0913.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021006_0901.124.859','smc5.021006_0901.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021012_0828.144.2870','smc5.021012_0828.144','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021101_0836.137.1187','smc5.021101_0836.137','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021105_0502.083.495','smc5.021105_0502.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021109_0520.085.3725','smc5.021109_0520.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021111_0505.090.3332','smc5.021111_0505.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021113_0521.081.2705','smc5.021113_0521.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021115_0449.080.2151','smc5.021115_0449.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021128_0510.063.1600','smc5.021128_0510.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021202_0521.089.838','smc5.021202_0521.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021204_0520.065.339','smc5.021204_0520.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021206_0510.080.3315','smc5.021206_0510.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021208_0528.054.2860','smc5.021208_0528.054','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021210_0421.059.2557','smc5.021210_0421.059','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021214_0340.068.1992','smc5.021214_0340.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021216_0414.068.1441','smc5.021216_0414.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.021229_0439.069.939','smc5.021229_0439.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc5.030103_0440.066.406','smc5.030103_0440.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.020928_0920.135.3778','smc6.020928_0920.135','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.020930_0914.179.3249','smc6.020930_0914.179','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021002_0904.071.2470','smc6.021002_0904.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021004_0916.094.1790','smc6.021004_0916.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021006_0904.125.863','smc6.021006_0904.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021012_0851.088.2874','smc6.021012_0851.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021101_0839.138.1191','smc6.021101_0839.138','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021105_0505.084.499','smc6.021105_0505.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021109_0524.086.3727','smc6.021109_0524.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021111_0508.091.3336','smc6.021111_0508.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021113_0525.082.2709','smc6.021113_0525.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021115_0452.081.2155','smc6.021115_0452.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021128_0513.064.1604','smc6.021128_0513.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021202_0523.090.842','smc6.021202_0523.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021204_0523.066.343','smc6.021204_0523.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021206_0513.081.3318','smc6.021206_0513.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021208_0532.055.2865','smc6.021208_0532.055','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021210_0646.087.2560','smc6.021210_0646.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021214_0343.069.1995','smc6.021214_0343.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021216_0417.069.1446','smc6.021216_0417.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.021229_0442.070.943','smc6.021229_0442.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc6.030103_0443.067.410','smc6.030103_0443.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.020928_0523.113.3780','smc7.020928_0523.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.020930_0517.129.3253','smc7.020930_0517.129','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021002_0509.045.2473','smc7.021002_0509.045','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021004_0520.063.1793','smc7.021004_0520.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021006_0510.120.867','smc7.021006_0510.120','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021008_0528.069.345','smc7.021008_0528.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021010_0647.108.3417','smc7.021010_0647.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021012_0438.095.2878','smc7.021012_0438.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021014_0440.091.2242','smc7.021014_0440.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021030_0511.081.1693','smc7.021030_0511.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021101_0530.088.1195','smc7.021101_0530.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021105_0508.085.503','smc7.021105_0508.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021109_0527.087.3729','smc7.021109_0527.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021111_0511.092.3340','smc7.021111_0511.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021113_0528.083.2711','smc7.021113_0528.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021115_0456.082.2157','smc7.021115_0456.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021128_0516.065.1607','smc7.021128_0516.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021202_0527.091.846','smc7.021202_0527.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021204_0526.067.347','smc7.021204_0526.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021206_0518.082.3323','smc7.021206_0518.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021208_0701.056.2869','smc7.021208_0701.056','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021210_0427.061.2564','smc7.021210_0427.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021210_0601.078.2568','smc7.021210_0601.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021214_0346.070.1999','smc7.021214_0346.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021216_0421.070.1451','smc7.021216_0421.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.021229_0445.071.946','smc7.021229_0445.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc7.030103_0447.068.414','smc7.030103_0447.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.020928_0528.114.3782','smc8.020928_0528.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.020930_0523.130.3257','smc8.020930_0523.130','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021002_0514.046.2477','smc8.021002_0514.046','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021004_0526.064.1797','smc8.021004_0526.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021006_0516.121.872','smc8.021006_0516.121','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021008_0533.070.349','smc8.021008_0533.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021010_0652.109.3421','smc8.021010_0652.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021012_0443.096.2882','smc8.021012_0443.096','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021014_0445.092.2246','smc8.021014_0445.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021030_0516.082.1697','smc8.021030_0516.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021101_0534.089.1199','smc8.021101_0534.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021105_0513.086.507','smc8.021105_0513.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021109_0532.088.3731','smc8.021109_0532.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021111_0516.093.3344','smc8.021111_0516.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021113_0533.084.2716','smc8.021113_0533.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021115_0501.083.2163','smc8.021115_0501.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021128_0521.066.1612','smc8.021128_0521.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021202_0531.092.850','smc8.021202_0531.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021204_0530.068.351','smc8.021204_0530.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021206_0523.083.3325','smc8.021206_0523.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021208_0725.058.2872','smc8.021208_0725.058','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021210_0433.062.2572','smc8.021210_0433.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021210_0607.079.2576','smc8.021210_0607.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021214_0351.071.2003','smc8.021214_0351.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021216_0425.071.1455','smc8.021216_0425.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.021229_0450.072.950','smc8.021229_0450.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc8.030103_0451.069.418','smc8.030103_0451.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.020928_0533.115.3784','smc9.020928_0533.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.020930_0528.131.3261','smc9.020930_0528.131','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021002_0519.047.2481','smc9.021002_0519.047','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021004_0531.065.1801','smc9.021004_0531.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021006_0521.122.876','smc9.021006_0521.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021008_0538.071.352','smc9.021008_0538.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021010_0657.110.3425','smc9.021010_0657.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021012_0449.097.2886','smc9.021012_0449.097','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021014_0450.093.2250','smc9.021014_0450.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021030_0521.083.1701','smc9.021030_0521.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021101_0538.090.1203','smc9.021101_0538.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021105_0518.087.511','smc9.021105_0518.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021109_0537.089.3732','smc9.021109_0537.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021111_0521.094.3348','smc9.021111_0521.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021113_0538.085.2720','smc9.021113_0538.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021115_0506.084.2167','smc9.021115_0506.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021128_0526.067.1615','smc9.021128_0526.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021202_0536.093.854','smc9.021202_0536.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021204_0535.069.355','smc9.021204_0535.069','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021206_0528.084.3329','smc9.021206_0528.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021210_0442.063.2580','smc9.021210_0442.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021210_0612.080.2584','smc9.021210_0612.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021214_0356.072.2007','smc9.021214_0356.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021216_0430.072.1457','smc9.021216_0430.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.021229_0455.073.954','smc9.021229_0455.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smc9.030103_0457.070.422','smc9.030103_0457.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.020928_0538.116.3786','smca.020928_0538.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.020930_0533.132.3265','smca.020930_0533.132','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021002_0524.048.2485','smca.021002_0524.048','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021004_0536.066.1806','smca.021004_0536.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021006_0527.123.880','smca.021006_0527.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021008_0543.072.356','smca.021008_0543.072','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021010_0702.111.3429','smca.021010_0702.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021012_0454.098.2890','smca.021012_0454.098','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021014_0455.094.2254','smca.021014_0455.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021030_0527.084.1705','smca.021030_0527.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021101_0542.091.1207','smca.021101_0542.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021105_0522.088.515','smca.021105_0522.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021109_0541.090.3734','smca.021109_0541.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021111_0526.095.3352','smca.021111_0526.095','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021113_0542.086.2723','smca.021113_0542.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021115_0511.085.2171','smca.021115_0511.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021128_0530.068.1619','smca.021128_0530.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021202_0541.094.858','smca.021202_0541.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021204_0540.070.359','smca.021204_0540.070','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021206_0533.085.3333','smca.021206_0533.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021208_0744.060.2876','smca.021208_0744.060','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021210_0447.064.2587','smca.021210_0447.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021210_0616.081.2591','smca.021210_0616.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021214_0400.073.2012','smca.021214_0400.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021216_0435.073.1461','smca.021216_0435.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.021229_0459.074.958','smca.021229_0459.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smca.030103_0501.071.426','smca.030103_0501.071','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.020928_0459.108.3788','smd7.020928_0459.108','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.020930_0453.124.3269','smd7.020930_0453.124','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021002_0447.040.2489','smd7.021002_0447.040','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021004_0457.058.1810','smd7.021004_0457.058','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021006_0447.115.884','smd7.021006_0447.115','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021008_0506.064.360','smd7.021008_0506.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021010_0618.106.3433','smd7.021010_0618.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021012_0415.090.2894','smd7.021012_0415.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021014_0417.086.2258','smd7.021014_0417.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021030_0449.076.1709','smd7.021030_0449.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021101_0510.083.1211','smd7.021101_0510.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021105_0446.078.519','smd7.021105_0446.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021109_0503.080.3736','smd7.021109_0503.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021111_0449.085.3356','smd7.021111_0449.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021113_0505.076.2727','smd7.021113_0505.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021115_0432.075.2175','smd7.021115_0432.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021128_0454.058.1623','smd7.021128_0454.058','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021204_0504.060.363','smd7.021204_0504.060','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021206_0453.075.3337','smd7.021206_0453.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021208_0423.044.2881','smd7.021208_0423.044','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021210_0347.060.2595','smd7.021210_0347.060','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021214_0325.063.2016','smd7.021214_0325.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021216_0359.063.1465','smd7.021216_0359.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.021229_0423.064.962','smd7.021229_0423.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd7.030103_0421.061.430','smd7.030103_0421.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.020928_0503.109.3790','smd8.020928_0503.109','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.020930_0458.125.3273','smd8.020930_0458.125','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021002_0451.041.2493','smd8.021002_0451.041','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021004_0501.059.1814','smd8.021004_0501.059','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021006_0450.116.888','smd8.021006_0450.116','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021008_0510.065.364','smd8.021008_0510.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021010_0630.104.3437','smd8.021010_0630.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021012_0420.091.2898','smd8.021012_0420.091','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021014_0421.087.2260','smd8.021014_0421.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021030_0453.077.1713','smd8.021030_0453.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021101_0513.084.1215','smd8.021101_0513.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021105_0449.079.523','smd8.021105_0449.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021109_0507.081.3738','smd8.021109_0507.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021111_0452.086.3360','smd8.021111_0452.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021113_0508.077.2731','smd8.021113_0508.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021115_0435.076.2179','smd8.021115_0435.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021128_0457.059.1626','smd8.021128_0457.059','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021202_0455.084.862','smd8.021202_0455.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021204_0507.061.367','smd8.021204_0507.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021206_0456.076.3341','smd8.021206_0456.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021208_0513.050.2884','smd8.021208_0513.050','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021210_0354.061.2599','smd8.021210_0354.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021214_0327.064.2020','smd8.021214_0327.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021216_0402.064.1469','smd8.021216_0402.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.021229_0426.065.966','smd8.021229_0426.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd8.030103_0425.062.434','smd8.030103_0425.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.020928_0507.110.3792','smd9.020928_0507.110','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.020930_0503.126.3277','smd9.020930_0503.126','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021002_0456.042.2497','smd9.021002_0456.042','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021004_0506.060.1818','smd9.021004_0506.060','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021006_0455.117.892','smd9.021006_0455.117','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021008_0515.066.368','smd9.021008_0515.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021010_0634.105.3441','smd9.021010_0634.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021012_0424.092.2902','smd9.021012_0424.092','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021014_0426.088.2264','smd9.021014_0426.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021030_0458.078.1717','smd9.021030_0458.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021101_0518.085.1219','smd9.021101_0518.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021105_0452.080.527','smd9.021105_0452.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021109_0510.082.3740','smd9.021109_0510.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021111_0452.087.3364','smd9.021111_0452.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021113_0512.078.2737','smd9.021113_0512.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021115_0439.077.2183','smd9.021115_0439.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021128_0500.060.1629','smd9.021128_0500.060','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021202_0458.085.868','smd9.021202_0458.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021204_0510.062.371','smd9.021204_0510.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021206_0500.077.3345','smd9.021206_0500.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021208_0516.051.2888','smd9.021208_0516.051','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021210_0358.062.2603','smd9.021210_0358.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021214_0331.065.2024','smd9.021214_0331.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021216_0405.065.1473','smd9.021216_0405.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.021229_0429.066.969','smd9.021229_0429.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smd9.030103_0430.063.438','smd9.030103_0430.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.020928_0515.111.3794','smda.020928_0515.111','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.020930_0509.127.3281','smda.020930_0509.127','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021002_0501.043.2501','smda.021002_0501.043','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021004_0512.061.1822','smda.021004_0512.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021006_0501.118.896','smda.021006_0501.118','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021008_0521.067.372','smda.021008_0521.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021010_0640.106.3445','smda.021010_0640.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021012_0430.093.2906','smda.021012_0430.093','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021014_0432.089.2268','smda.021014_0432.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021030_0504.079.1721','smda.021030_0504.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021101_0523.086.1223','smda.021101_0523.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021105_0456.081.531','smda.021105_0456.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021109_0514.083.3742','smda.021109_0514.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021111_0459.088.3368','smda.021111_0459.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021113_0515.079.2741','smda.021113_0515.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021115_0443.078.2187','smda.021115_0443.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021128_0504.061.1635','smda.021128_0504.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021202_0501.086.871','smda.021202_0501.086','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021204_0514.063.375','smda.021204_0514.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021206_0504.078.3349','smda.021206_0504.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021208_0520.052.2892','smda.021208_0520.052','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021210_0402.063.2607','smda.021210_0402.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021214_0334.066.2027','smda.021214_0334.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021216_0409.066.1477','smda.021216_0409.066','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.021229_0433.067.974','smda.021229_0433.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smda.030103_0434.064.442','smda.030103_0434.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.020928_0520.112.3796','smdb.020928_0520.112','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.020930_0514.128.3286','smdb.020930_0514.128','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021002_0506.044.2506','smdb.021002_0506.044','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021004_0516.062.1826','smdb.021004_0516.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021006_0507.119.900','smdb.021006_0507.119','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021008_0525.068.376','smdb.021008_0525.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021010_0644.107.3448','smdb.021010_0644.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021012_0434.094.2910','smdb.021012_0434.094','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021014_0436.090.2272','smdb.021014_0436.090','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021030_0508.080.1725','smdb.021030_0508.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021101_0527.087.1227','smdb.021101_0527.087','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021105_0459.082.535','smdb.021105_0459.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021109_0518.084.3744','smdb.021109_0518.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021111_0502.089.3372','smdb.021111_0502.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021113_0519.080.2743','smdb.021113_0519.080','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021115_0446.079.2191','smdb.021115_0446.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021128_0507.062.1639','smdb.021128_0507.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021202_0518.088.875','smdb.021202_0518.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021204_0517.064.379','smdb.021204_0517.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021206_0507.079.3353','smdb.021206_0507.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021208_0524.053.2896','smdb.021208_0524.053','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021210_0406.064.2611','smdb.021210_0406.064','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021214_0338.067.2032','smdb.021214_0338.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021216_0412.067.1481','smdb.021216_0412.067','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.021229_0436.068.977','smdb.021229_0436.068','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('smdb.030103_0437.065.446','smdb.030103_0437.065','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.020928_0450.106.3798','sme8.020928_0450.106','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.020930_0446.122.3289','sme8.020930_0446.122','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021002_0442.038.2509','sme8.021002_0442.038','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021004_0451.056.1830','sme8.021004_0451.056','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021006_0440.113.904','sme8.021006_0440.113','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021008_0500.062.380','sme8.021008_0500.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021010_0611.104.3452','sme8.021010_0611.104','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021012_0405.088.2914','sme8.021012_0405.088','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021014_0407.084.2277','sme8.021014_0407.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021030_0443.074.1729','sme8.021030_0443.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021101_0503.081.1231','sme8.021101_0503.081','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021105_0440.076.539','sme8.021105_0440.076','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021109_0456.078.3746','sme8.021109_0456.078','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021111_0444.083.3376','sme8.021111_0444.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021113_0459.074.2747','sme8.021113_0459.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021115_0426.073.2194','sme8.021115_0426.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021128_0446.056.1642','sme8.021128_0446.056','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021202_0446.082.879','sme8.021202_0446.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021204_0500.058.383','sme8.021204_0500.058','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021206_0447.073.3357','sme8.021206_0447.073','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021208_0416.042.2899','sme8.021208_0416.042','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021210_0340.058.2615','sme8.021210_0340.058','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021214_0317.061.2036','sme8.021214_0317.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021216_0354.061.1487','sme8.021216_0354.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021229_0408.061.981','sme8.021229_0408.061','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.021229_0411.062.985','sme8.021229_0411.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme8.030103_0415.059.450','sme8.030103_0415.059','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.020928_0456.107.3800','sme9.020928_0456.107','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.020930_0449.123.3293','sme9.020930_0449.123','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021002_0444.039.2514','sme9.021002_0444.039','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021004_0454.057.1834','sme9.021004_0454.057','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021006_0444.114.908','sme9.021006_0444.114','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021008_0503.063.384','sme9.021008_0503.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021010_0615.105.3457','sme9.021010_0615.105','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021012_0412.089.2918','sme9.021012_0412.089','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021014_0413.085.2280','sme9.021014_0413.085','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021030_0446.075.1733','sme9.021030_0446.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021101_0507.082.1235','sme9.021101_0507.082','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021105_0444.077.543','sme9.021105_0444.077','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021109_0501.079.3749','sme9.021109_0501.079','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021111_0446.084.3380','sme9.021111_0446.084','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021113_0503.075.2752','sme9.021113_0503.075','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021115_0430.074.2199','sme9.021115_0430.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021128_0451.057.1646','sme9.021128_0451.057','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021202_0452.083.883','sme9.021202_0452.083','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021204_0502.059.387','sme9.021204_0502.059','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021206_0450.074.3361','sme9.021206_0450.074','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021208_0418.043.2904','sme9.021208_0418.043','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021210_0344.059.2619','sme9.021210_0344.059','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021214_0322.062.2040','sme9.021214_0322.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021216_0356.062.1491','sme9.021216_0356.062','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.021229_0421.063.989','sme9.021229_0421.063','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('sme9.030103_0418.060.454','sme9.030103_0418.060','CTIO_MOSAIC2','CTIO4m',NULL,'sm',1),('waa6.020909_0145.116.3930','waa6.020909_0145.116','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa6.020909_0202.121.3931','waa6.020909_0202.121','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa6.020909_0214.126.3932','waa6.020909_0214.126','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa6.020909_0226.131.3933','waa6.020909_0226.131','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa6.020909_0241.136.3934','waa6.020909_0241.136','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa6.020909_0252.141.3935','waa6.020909_0252.141','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa7.020909_0148.117.3936','waa7.020909_0148.117','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa7.020909_0205.122.3937','waa7.020909_0205.122','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa7.020909_0216.127.3938','waa7.020909_0216.127','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa7.020909_0229.132.3939','waa7.020909_0229.132','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa7.020909_0243.137.3940','waa7.020909_0243.137','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('waa7.020909_0255.142.3941','waa7.020909_0255.142','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb5.020909_0705.208.3942','wbb5.020909_0705.208','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb5.020909_0715.212.3943','wbb5.020909_0715.212','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb5.020909_0724.216.3944','wbb5.020909_0724.216','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb5.020909_0738.220.3945','wbb5.020909_0738.220','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb5.020909_0751.224.3946','wbb5.020909_0751.224','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb5.020909_0800.228.3947','wbb5.020909_0800.228','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb6.020909_0701.207.3948','wbb6.020909_0701.207','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb6.020909_0713.211.3949','wbb6.020909_0713.211','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb6.020909_0721.215.3950','wbb6.020909_0721.215','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb6.020909_0736.219.3951','wbb6.020909_0736.219','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb6.020909_0749.223.3952','wbb6.020909_0749.223','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wbb6.020909_0758.227.3953','wbb6.020909_0758.227','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wdd4.020909_0922.249.3954','wdd4.020909_0922.249','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wdd4.020909_0940.252.3955','wdd4.020909_0940.252','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wdd4.020909_0951.255.3956','wdd4.020909_0951.255','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wdd5.020909_0824.233.3957','wdd5.020909_0824.233','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wdd5.020909_0834.236.3958','wdd5.020909_0834.236','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wdd5.020909_0842.239.3959','wdd5.020909_0842.239','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wdd5.020909_0857.242.3960','wdd5.020909_0857.242','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wdd5.020909_0907.245.3961','wdd5.020909_0907.245','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wdd5.020909_0916.248.3962','wdd5.020909_0916.248','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx01.020909_0151.118.3963','wx01.020909_0151.118','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx01.020909_0207.123.3964','wx01.020909_0207.123','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx01.020909_0218.128.3965','wx01.020909_0218.128','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx01.020909_0232.133.3966','wx01.020909_0232.133','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx01.020909_0245.138.3967','wx01.020909_0245.138','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx01.020909_0257.143.3968','wx01.020909_0257.143','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx02.020909_0154.119.3969','wx02.020909_0154.119','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx02.020909_0209.124.3970','wx02.020909_0209.124','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx02.020909_0220.129.3971','wx02.020909_0220.129','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx02.020909_0235.134.3972','wx02.020909_0235.134','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx02.020909_0248.139.3973','wx02.020909_0248.139','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx02.020909_0259.144.3974','wx02.020909_0259.144','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx03.020909_0157.120.3975','wx03.020909_0157.120','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx03.020909_0211.125.3976','wx03.020909_0211.125','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx03.020909_0222.130.3977','wx03.020909_0222.130','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx03.020909_0238.135.3978','wx03.020909_0238.135','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx03.020909_0250.140.3979','wx03.020909_0250.140','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx03.020909_0301.145.3980','wx03.020909_0301.145','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx04.020909_0344.150.3981','wx04.020909_0344.150','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx04.020909_0401.155.3982','wx04.020909_0401.155','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx04.020909_0414.160.3983','wx04.020909_0414.160','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx04.020909_0426.165.3984','wx04.020909_0426.165','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx04.020909_0440.170.3985','wx04.020909_0440.170','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx04.020909_0500.168.3986','wx04.020909_0500.168','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx05.020909_0348.151.3987','wx05.020909_0348.151','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx05.020909_0403.156.3988','wx05.020909_0403.156','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx05.020909_0416.161.3989','wx05.020909_0416.161','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx05.020909_0428.166.3990','wx05.020909_0428.166','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx05.020909_0442.171.3991','wx05.020909_0442.171','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx05.020909_0502.169.3992','wx05.020909_0502.169','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx06.020909_0352.152.3993','wx06.020909_0352.152','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx06.020909_0406.157.3994','wx06.020909_0406.157','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx06.020909_0418.162.3995','wx06.020909_0418.162','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx06.020909_0431.167.3996','wx06.020909_0431.167','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx06.020909_0453.165.3997','wx06.020909_0453.165','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx06.020909_0504.170.3998','wx06.020909_0504.170','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx07.020909_0354.153.3999','wx07.020909_0354.153','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx07.020909_0408.158.4000','wx07.020909_0408.158','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx07.020909_0420.163.4001','wx07.020909_0420.163','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx07.020909_0434.168.4002','wx07.020909_0434.168','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx07.020909_0455.166.4003','wx07.020909_0455.166','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx07.020909_0506.171.4004','wx07.020909_0506.171','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx08.020909_0357.154.4005','wx08.020909_0357.154','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx08.020909_0411.159.4006','wx08.020909_0411.159','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx08.020909_0422.164.4007','wx08.020909_0422.164','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx08.020909_0437.169.4008','wx08.020909_0437.169','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx08.020909_0457.167.4009','wx08.020909_0457.167','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx08.020909_0509.172.4010','wx08.020909_0509.172','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx09.020909_0541.177.4011','wx09.020909_0541.177','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx09.020909_0556.182.4012','wx09.020909_0556.182','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx09.020909_0608.187.4013','wx09.020909_0608.187','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx09.020909_0619.192.4014','wx09.020909_0619.192','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx09.020909_0634.197.4015','wx09.020909_0634.197','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx09.020909_0645.202.4016','wx09.020909_0645.202','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx10.020909_0544.178.4017','wx10.020909_0544.178','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx10.020909_0558.183.4018','wx10.020909_0558.183','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx10.020909_0609.188.4019','wx10.020909_0609.188','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx10.020909_0621.193.4020','wx10.020909_0621.193','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx10.020909_0636.198.4021','wx10.020909_0636.198','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx10.020909_0647.203.4022','wx10.020909_0647.203','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx11.020909_0547.179.4023','wx11.020909_0547.179','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx11.020909_0600.184.4024','wx11.020909_0600.184','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx11.020909_0611.189.4025','wx11.020909_0611.189','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx11.020909_0626.194.4026','wx11.020909_0626.194','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx11.020909_0639.199.4027','wx11.020909_0639.199','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx11.020909_0650.204.4028','wx11.020909_0650.204','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx12.020909_0550.180.4029','wx12.020909_0550.180','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx12.020909_0602.185.4030','wx12.020909_0602.185','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx12.020909_0614.190.4031','wx12.020909_0614.190','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx12.020909_0628.195.4032','wx12.020909_0628.195','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx12.020909_0641.200.4033','wx12.020909_0641.200','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx12.020909_0652.205.4034','wx12.020909_0652.205','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx13.020909_0553.181.4035','wx13.020909_0553.181','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx13.020909_0605.186.4036','wx13.020909_0605.186','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx13.020909_0615.191.4037','wx13.020909_0615.191','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx13.020909_0631.196.4038','wx13.020909_0631.196','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx13.020909_0643.201.4039','wx13.020909_0643.201','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx13.020909_0654.206.4040','wx13.020909_0654.206','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx14.020909_0708.209.4041','wx14.020909_0708.209','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx14.020909_0718.213.4042','wx14.020909_0718.213','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx14.020909_0726.217.4043','wx14.020909_0726.217','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx14.020909_0741.221.4044','wx14.020909_0741.221','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx14.020909_0753.225.4045','wx14.020909_0753.225','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx14.020909_0802.229.4046','wx14.020909_0802.229','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx15.020909_0711.210.4047','wx15.020909_0711.210','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx15.020909_0719.214.4048','wx15.020909_0719.214','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx15.020909_0733.218.4049','wx15.020909_0733.218','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx15.020909_0746.222.4050','wx15.020909_0746.222','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx15.020909_0755.226.4051','wx15.020909_0755.226','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx15.020909_0804.230.4052','wx15.020909_0804.230','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx17.020909_0813.231.4053','wx17.020909_0813.231','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx17.020909_0830.234.4054','wx17.020909_0830.234','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx17.020909_0837.237.4055','wx17.020909_0837.237','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx17.020909_0846.240.4056','wx17.020909_0846.240','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx17.020909_0902.243.4057','wx17.020909_0902.243','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx17.020909_0910.246.4058','wx17.020909_0910.246','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx18.020909_0819.232.4059','wx18.020909_0819.232','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx18.020909_0831.235.4060','wx18.020909_0831.235','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx18.020909_0840.238.4061','wx18.020909_0840.238','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx18.020909_0851.241.4062','wx18.020909_0851.241','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx18.020909_0905.244.4063','wx18.020909_0905.244','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx18.020909_0914.247.4064','wx18.020909_0914.247','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx19.020909_0928.250.4065','wx19.020909_0928.250','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx19.020909_0944.253.4066','wx19.020909_0944.253','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx19.020909_0954.256.4067','wx19.020909_0954.256','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx20.020909_0935.251.4068','wx20.020909_0935.251','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx20.020909_0947.254.4069','wx20.020909_0947.254','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wx20.020909_0958.257.4070','wx20.020909_0958.257','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.020928_0023.058.3802','wxa1.020928_0023.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.020928_0034.063.3804','wxa1.020928_0034.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.020929_2338.034.3299','wxa1.020929_2338.034','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.020929_2352.039.3301','wxa1.020929_2352.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.020930_0007.044.3305','wxa1.020930_0007.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021001_2348.033.2518','wxa1.021001_2348.033','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021002_0000.038.2522','wxa1.021002_0000.038','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021002_0013.043.2526','wxa1.021002_0013.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021003_2351.037.1838','wxa1.021003_2351.037','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021004_0003.042.1841','wxa1.021004_0003.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021004_0016.047.1846','wxa1.021004_0016.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021006_0017.059.1089','wxa1.021006_0017.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021006_0029.064.1093','wxa1.021006_0029.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021006_0043.069.1097','wxa1.021006_0043.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021008_0003.042.388','wxa1.021008_0003.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021008_0019.047.392','wxa1.021008_0019.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021010_0001.041.3461','wxa1.021010_0001.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021010_0023.046.3465','wxa1.021010_0023.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021010_0322.089.3469','wxa1.021010_0322.089','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021011_2355.043.2922','wxa1.021011_2355.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021012_0018.047.2926','wxa1.021012_0018.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021014_0041.055.2286','wxa1.021014_0041.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021014_0105.060.2288','wxa1.021014_0105.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021030_0006.036.1737','wxa1.021030_0006.036','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021030_0016.038.1741','wxa1.021030_0016.038','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021030_0040.040.1745','wxa1.021030_0040.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021105_0010.039.640','wxa1.021105_0010.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021105_0020.041.644','wxa1.021105_0020.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021105_0044.043.648','wxa1.021105_0044.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021111_0028.051.3384','wxa1.021111_0028.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021111_0056.054.3388','wxa1.021111_0056.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021115_0034.045.2203','wxa1.021115_0034.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021115_0047.047.2206','wxa1.021115_0047.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021128_0047.033.1650','wxa1.021128_0047.033','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021128_0056.035.1654','wxa1.021128_0056.035','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021204_0043.038.391','wxa1.021204_0043.038','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021204_0055.040.395','wxa1.021204_0055.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021206_0043.040.3365','wxa1.021206_0043.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021206_0053.042.3369','wxa1.021206_0053.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxa1.021206_0118.044.3373','wxa1.021206_0118.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.020928_0010.054.3806','wxb1.020928_0010.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.020928_0025.059.3808','wxb1.020928_0025.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.020928_0037.064.3810','wxb1.020928_0037.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.020929_2342.035.3310','wxb1.020929_2342.035','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.020929_2355.040.3313','wxb1.020929_2355.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.020930_0009.045.3319','wxb1.020930_0009.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021001_2351.034.2529','wxb1.021001_2351.034','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021002_0002.039.2533','wxb1.021002_0002.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021002_0016.044.2537','wxb1.021002_0016.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021003_2354.038.1849','wxb1.021003_2354.038','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021004_0005.043.1853','wxb1.021004_0005.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021004_0018.048.1857','wxb1.021004_0018.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021006_0019.060.1101','wxb1.021006_0019.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021006_0032.065.1105','wxb1.021006_0032.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021006_0045.070.1109','wxb1.021006_0045.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021008_0008.043.396','wxb1.021008_0008.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021008_0022.048.400','wxb1.021008_0022.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021010_0004.042.3473','wxb1.021010_0004.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021010_0025.047.3477','wxb1.021010_0025.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021010_0330.090.3481','wxb1.021010_0330.090','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021011_2358.044.2930','wxb1.021011_2358.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021012_0021.048.2934','wxb1.021012_0021.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021014_0046.056.2294','wxb1.021014_0046.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021014_0108.061.2297','wxb1.021014_0108.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021101_0023.046.1239','wxb1.021101_0023.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021101_0034.048.1243','wxb1.021101_0034.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021101_0059.050.1247','wxb1.021101_0059.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021109_0025.044.3751','wxb1.021109_0025.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021109_0036.046.3753','wxb1.021109_0036.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021109_0100.048.3755','wxb1.021109_0100.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021113_0040.046.2756','wxb1.021113_0040.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021113_0050.048.2760','wxb1.021113_0050.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021113_0114.050.2765','wxb1.021113_0114.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021130_0048.041.1178','wxb1.021130_0048.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021130_0059.043.1182','wxb1.021130_0059.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021130_0123.045.1186','wxb1.021130_0123.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021202_0030.048.887','wxb1.021202_0030.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021202_0042.050.891','wxb1.021202_0042.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021202_0106.052.895','wxb1.021202_0106.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021208_0041.041.2909','wxb1.021208_0041.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021208_0052.043.2912','wxb1.021208_0052.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021208_0128.045.2916','wxb1.021208_0128.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021210_0042.039.2624','wxb1.021210_0042.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021210_0049.040.2628','wxb1.021210_0049.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021210_0056.041.2632','wxb1.021210_0056.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021216_0050.042.1495','wxb1.021216_0050.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021216_0055.043.1499','wxb1.021216_0055.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021229_0054.046.993','wxb1.021229_0054.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021229_0102.047.997','wxb1.021229_0102.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.021229_0114.048.1001','wxb1.021229_0114.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.030103_0119.044.458','wxb1.030103_0119.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxb1.030103_0122.045.462','wxb1.030103_0122.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.020928_0014.055.3812','wxc1.020928_0014.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.020928_0028.060.3815','wxc1.020928_0028.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.020928_0039.065.3816','wxc1.020928_0039.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.020929_2344.036.3321','wxc1.020929_2344.036','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.020929_2358.041.3326','wxc1.020929_2358.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.020930_0011.046.3330','wxc1.020930_0011.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021001_2354.035.2541','wxc1.021001_2354.035','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021002_0005.040.2546','wxc1.021002_0005.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021002_0019.045.2550','wxc1.021002_0019.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021003_2356.039.1861','wxc1.021003_2356.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021004_0008.044.1865','wxc1.021004_0008.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021004_0020.049.1869','wxc1.021004_0020.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021004_0202.039.1873','wxc1.021004_0202.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021004_0207.040.1877','wxc1.021004_0207.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021004_0332.042.1881','wxc1.021004_0332.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021004_0346.043.1885','wxc1.021004_0346.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021004_0401.044.1889','wxc1.021004_0401.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021006_0024.061.1112','wxc1.021006_0024.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021006_0034.066.1116','wxc1.021006_0034.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021006_0047.071.1120','wxc1.021006_0047.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021008_0011.044.404','wxc1.021008_0011.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021008_0025.049.408','wxc1.021008_0025.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021010_0007.043.3485','wxc1.021010_0007.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021010_0028.048.3489','wxc1.021010_0028.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021010_0335.091.3493','wxc1.021010_0335.091','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021012_0001.045.2938','wxc1.021012_0001.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021012_0023.049.2942','wxc1.021012_0023.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021014_0049.057.2300','wxc1.021014_0049.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021014_0111.062.2306','wxc1.021014_0111.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021030_0009.037.1749','wxc1.021030_0009.037','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021030_0022.039.1751','wxc1.021030_0022.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021030_0043.041.1755','wxc1.021030_0043.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021030_0046.042.1759','wxc1.021030_0046.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021103_0256.081.785','wxc1.021103_0256.081','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021105_0014.040.652','wxc1.021105_0014.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021105_0026.042.656','wxc1.021105_0026.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021105_0047.044.660','wxc1.021105_0047.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021111_0031.052.3392','wxc1.021111_0031.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021111_0038.053.3396','wxc1.021111_0038.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021111_0059.055.3400','wxc1.021111_0059.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021115_0039.046.2210','wxc1.021115_0039.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021115_0052.048.2213','wxc1.021115_0052.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021128_0049.034.1658','wxc1.021128_0049.034','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021128_0102.036.1662','wxc1.021128_0102.036','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021204_0048.039.399','wxc1.021204_0048.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021204_0100.041.403','wxc1.021204_0100.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021206_0047.041.3377','wxc1.021206_0047.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021206_0059.043.3381','wxc1.021206_0059.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021206_0121.045.3385','wxc1.021206_0121.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021214_0047.045.2044','wxc1.021214_0047.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021214_0058.046.2048','wxc1.021214_0058.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxc1.021214_0108.047.2052','wxc1.021214_0108.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.020928_0017.056.3818','wxd1.020928_0017.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.020928_0041.066.3820','wxd1.020928_0041.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.020929_2347.037.3335','wxd1.020929_2347.037','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.020930_0001.042.3339','wxd1.020930_0001.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.020930_0013.047.3343','wxd1.020930_0013.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021001_2356.036.2554','wxd1.021001_2356.036','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021002_0008.041.2558','wxd1.021002_0008.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021002_0021.046.2562','wxd1.021002_0021.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021003_2358.040.1893','wxd1.021003_2358.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021004_0011.045.1897','wxd1.021004_0011.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021004_0022.050.1901','wxd1.021004_0022.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021006_0025.062.1124','wxd1.021006_0025.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021006_0037.067.1128','wxd1.021006_0037.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021006_0049.072.1132','wxd1.021006_0049.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021008_0013.045.412','wxd1.021008_0013.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021008_0027.050.416','wxd1.021008_0027.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021010_0013.044.3496','wxd1.021010_0013.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021010_0031.049.3499','wxd1.021010_0031.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021010_0354.092.3502','wxd1.021010_0354.092','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021012_0008.046.2946','wxd1.021012_0008.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021012_0026.050.2950','wxd1.021012_0026.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021014_0056.058.2310','wxd1.021014_0056.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021014_0113.063.2314','wxd1.021014_0113.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021101_0028.047.1251','wxd1.021101_0028.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021101_0040.049.1255','wxd1.021101_0040.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021101_0102.051.1259','wxd1.021101_0102.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021107_0251.101.103','wxd1.021107_0251.101','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021109_0029.045.3757','wxd1.021109_0029.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021109_0041.047.3759','wxd1.021109_0041.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021109_0103.049.3761','wxd1.021109_0103.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021113_0043.047.2769','wxd1.021113_0043.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021113_0055.049.2773','wxd1.021113_0055.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021113_0117.051.2777','wxd1.021113_0117.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021113_0242.056.2780','wxd1.021113_0242.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021113_0253.057.2784','wxd1.021113_0253.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021130_0053.042.1190','wxd1.021130_0053.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021130_0105.044.1194','wxd1.021130_0105.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021130_0126.046.1198','wxd1.021130_0126.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021202_0036.049.899','wxd1.021202_0036.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021202_0048.051.903','wxd1.021202_0048.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021202_0109.053.907','wxd1.021202_0109.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021208_0044.042.2920','wxd1.021208_0044.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021208_0107.044.2925','wxd1.021208_0107.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxd1.021208_0131.046.2927','wxd1.021208_0131.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.020928_0021.057.3821','wxe1.020928_0021.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.020928_0032.062.3822','wxe1.020928_0032.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.020928_0043.067.3823','wxe1.020928_0043.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.020929_2349.038.3347','wxe1.020929_2349.038','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.020930_0004.043.3351','wxe1.020930_0004.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.020930_0015.048.3355','wxe1.020930_0015.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021001_2358.037.2566','wxe1.021001_2358.037','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021002_0011.042.2570','wxe1.021002_0011.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021002_0023.047.2574','wxe1.021002_0023.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021004_0001.041.1905','wxe1.021004_0001.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021004_0013.046.1909','wxe1.021004_0013.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021004_0025.051.1913','wxe1.021004_0025.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021006_0027.063.1136','wxe1.021006_0027.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021006_0040.068.1140','wxe1.021006_0040.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021006_0051.073.1144','wxe1.021006_0051.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021008_0016.046.420','wxe1.021008_0016.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021008_0030.051.424','wxe1.021008_0030.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021010_0020.045.3505','wxe1.021010_0020.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021010_0034.050.3508','wxe1.021010_0034.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021012_0029.051.2954','wxe1.021012_0029.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021014_0102.059.2317','wxe1.021014_0102.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxe1.021014_0116.064.2320','wxe1.021014_0116.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.020928_0109.068.3824','wxf1.020928_0109.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.020928_0118.073.3825','wxf1.020928_0118.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.020928_0133.078.3826','wxf1.020928_0133.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.020930_0017.049.3359','wxf1.020930_0017.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.020930_0043.038.3363','wxf1.020930_0043.038','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.020930_0057.043.3367','wxf1.020930_0057.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021002_0025.048.2578','wxf1.021002_0025.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021002_0037.053.2582','wxf1.021002_0037.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021002_0051.058.2586','wxf1.021002_0051.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021004_0027.052.1917','wxf1.021004_0027.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021004_0037.057.1921','wxf1.021004_0037.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021004_0051.062.1925','wxf1.021004_0051.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021006_0053.074.1148','wxf1.021006_0053.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021006_0105.079.1152','wxf1.021006_0105.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021006_0120.084.1156','wxf1.021006_0120.084','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021008_0035.052.429','wxf1.021008_0035.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021008_0051.057.433','wxf1.021008_0051.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021010_0036.051.3511','wxf1.021010_0036.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021010_0058.056.3514','wxf1.021010_0058.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021010_0412.093.3517','wxf1.021010_0412.093','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021010_0454.097.3520','wxf1.021010_0454.097','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021012_0031.052.2958','wxf1.021012_0031.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021012_0056.048.2962','wxf1.021012_0056.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021014_0119.065.2324','wxf1.021014_0119.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021014_0141.070.2328','wxf1.021014_0141.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021030_0048.043.1763','wxf1.021030_0048.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021030_0058.045.1767','wxf1.021030_0058.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021030_0122.047.1771','wxf1.021030_0122.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021105_0050.045.664','wxf1.021105_0050.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021105_0059.047.668','wxf1.021105_0059.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021105_0123.049.672','wxf1.021105_0123.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021111_0102.056.3404','wxf1.021111_0102.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021111_0130.059.3408','wxf1.021111_0130.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021128_0122.037.1667','wxf1.021128_0122.037','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021128_0133.039.1671','wxf1.021128_0133.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021204_0126.042.407','wxf1.021204_0126.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021204_0144.044.411','wxf1.021204_0144.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021206_0124.046.3391','wxf1.021206_0124.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021206_0134.048.3394','wxf1.021206_0134.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxf1.021206_0158.050.3397','wxf1.021206_0158.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.020928_0110.069.3827','wxg1.020928_0110.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.020928_0122.074.3828','wxg1.020928_0122.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.020930_0033.034.3371','wxg1.020930_0033.034','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.020930_0046.039.3375','wxg1.020930_0046.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.020930_0100.044.3379','wxg1.020930_0100.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021002_0027.049.2590','wxg1.021002_0027.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021002_0040.054.2594','wxg1.021002_0040.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021002_0053.059.2598','wxg1.021002_0053.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021004_0029.053.1929','wxg1.021004_0029.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021004_0040.058.1933','wxg1.021004_0040.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021004_0053.063.1937','wxg1.021004_0053.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021006_0055.075.1160','wxg1.021006_0055.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021006_0108.080.1164','wxg1.021006_0108.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021006_0122.085.1168','wxg1.021006_0122.085','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021008_0037.053.437','wxg1.021008_0037.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021008_0054.058.441','wxg1.021008_0054.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021010_0039.052.3523','wxg1.021010_0039.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021010_0100.057.3526','wxg1.021010_0100.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021010_0418.094.3529','wxg1.021010_0418.094','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021010_0459.098.3532','wxg1.021010_0459.098','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021012_0034.053.2966','wxg1.021012_0034.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021012_0059.049.2970','wxg1.021012_0059.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021014_0121.066.2332','wxg1.021014_0121.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021014_0143.071.2337','wxg1.021014_0143.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021014_0153.055.2341','wxg1.021014_0153.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021101_0105.052.1263','wxg1.021101_0105.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021101_0115.054.1267','wxg1.021101_0115.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021101_0140.056.1271','wxg1.021101_0140.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021109_0105.050.3763','wxg1.021109_0105.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021109_0116.052.3765','wxg1.021109_0116.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021109_0140.054.3767','wxg1.021109_0140.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021113_0119.052.2787','wxg1.021113_0119.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021113_0132.046.2792','wxg1.021113_0132.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021113_0159.049.2794','wxg1.021113_0159.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021130_0129.047.1202','wxg1.021130_0129.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021130_0138.049.1206','wxg1.021130_0138.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021130_0202.051.1210','wxg1.021130_0202.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021202_0112.054.911','wxg1.021202_0112.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021202_0121.056.915','wxg1.021202_0121.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxg1.021202_0145.058.919','wxg1.021202_0145.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.020928_0112.070.3829','wxh1.020928_0112.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.020928_0125.075.3830','wxh1.020928_0125.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.020928_0137.080.3831','wxh1.020928_0137.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.020930_0036.035.3383','wxh1.020930_0036.035','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021002_0030.050.2602','wxh1.021002_0030.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021002_0043.055.2606','wxh1.021002_0043.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021002_0055.060.2610','wxh1.021002_0055.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021004_0031.054.1941','wxh1.021004_0031.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021004_0043.059.1945','wxh1.021004_0043.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021004_0055.064.1949','wxh1.021004_0055.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021006_0059.076.1172','wxh1.021006_0059.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021006_0111.081.1176','wxh1.021006_0111.081','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021006_0124.086.1181','wxh1.021006_0124.086','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021008_0043.054.445','wxh1.021008_0043.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021008_0057.059.449','wxh1.021008_0057.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021010_0042.053.3535','wxh1.021010_0042.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021010_0103.058.3538','wxh1.021010_0103.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021010_0504.099.3541','wxh1.021010_0504.099','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021012_0037.054.2973','wxh1.021012_0037.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021012_0102.050.2977','wxh1.021012_0102.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021014_0124.067.2345','wxh1.021014_0124.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021014_0156.056.2349','wxh1.021014_0156.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021030_0051.044.1775','wxh1.021030_0051.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021030_0103.046.1779','wxh1.021030_0103.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021030_0125.048.1783','wxh1.021030_0125.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021030_0128.049.1787','wxh1.021030_0128.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021103_0303.082.789','wxh1.021103_0303.082','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021105_0052.046.676','wxh1.021105_0052.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021105_0104.048.680','wxh1.021105_0104.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021105_0125.050.684','wxh1.021105_0125.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021111_0105.057.3412','wxh1.021111_0105.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021111_0112.058.3416','wxh1.021111_0112.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021111_0137.060.3420','wxh1.021111_0137.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021128_0126.038.1675','wxh1.021128_0126.038','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021128_0138.040.1679','wxh1.021128_0138.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021204_0133.043.415','wxh1.021204_0133.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021204_0156.045.419','wxh1.021204_0156.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021206_0127.047.3401','wxh1.021206_0127.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021206_0139.049.3405','wxh1.021206_0139.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021206_0201.051.3409','wxh1.021206_0201.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021210_0059.042.2636','wxh1.021210_0059.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021210_0107.043.2640','wxh1.021210_0107.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021210_0126.044.2644','wxh1.021210_0126.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021216_0102.044.1503','wxh1.021216_0102.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021216_0111.045.1507','wxh1.021216_0111.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021216_0121.046.1511','wxh1.021216_0121.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021229_0117.049.1005','wxh1.021229_0117.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.021229_0142.050.1010','wxh1.021229_0142.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.030103_0152.048.466','wxh1.030103_0152.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxh1.030103_0200.049.470','wxh1.030103_0200.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.020928_0114.071.3832','wxi1.020928_0114.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.020928_0128.076.3833','wxi1.020928_0128.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.020928_0139.081.3834','wxi1.020928_0139.081','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.020930_0038.036.3387','wxi1.020930_0038.036','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.020930_0052.041.3390','wxi1.020930_0052.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.020930_0104.046.3395','wxi1.020930_0104.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021002_0033.051.2614','wxi1.021002_0033.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021002_0045.056.2618','wxi1.021002_0045.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021002_0058.061.2622','wxi1.021002_0058.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021004_0033.055.1953','wxi1.021004_0033.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021004_0045.060.1957','wxi1.021004_0045.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021004_0057.065.1961','wxi1.021004_0057.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021006_0101.077.1184','wxi1.021006_0101.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021006_0113.082.1188','wxi1.021006_0113.082','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021006_0134.061.1192','wxi1.021006_0134.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021008_0046.055.453','wxi1.021008_0046.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021008_0100.060.457','wxi1.021008_0100.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021010_0048.054.3544','wxi1.021010_0048.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021010_0106.059.3547','wxi1.021010_0106.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021010_0523.100.3550','wxi1.021010_0523.100','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021012_0044.055.2981','wxi1.021012_0044.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021012_0105.051.2985','wxi1.021012_0105.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021014_0131.068.2352','wxi1.021014_0131.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021014_0158.057.2356','wxi1.021014_0158.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021101_0108.053.1275','wxi1.021101_0108.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021101_0121.055.1279','wxi1.021101_0121.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021101_0143.057.1283','wxi1.021101_0143.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021107_0300.102.107','wxi1.021107_0300.102','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021109_0109.051.3769','wxi1.021109_0109.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021109_0121.053.3771','wxi1.021109_0121.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021109_0144.055.3773','wxi1.021109_0144.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021113_0122.053.2799','wxi1.021113_0122.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021113_0137.047.2803','wxi1.021113_0137.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021113_0147.048.2807','wxi1.021113_0147.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021113_0202.050.2811','wxi1.021113_0202.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021130_0132.048.1214','wxi1.021130_0132.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021130_0144.050.1218','wxi1.021130_0144.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021130_0205.052.1222','wxi1.021130_0205.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021202_0115.055.923','wxi1.021202_0115.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021202_0127.057.927','wxi1.021202_0127.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxi1.021202_0148.059.929','wxi1.021202_0148.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.020928_0116.072.3835','wxj1.020928_0116.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.020928_0130.077.3836','wxj1.020928_0130.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.020928_0141.082.3837','wxj1.020928_0141.082','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.020930_0041.037.3398','wxj1.020930_0041.037','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.020930_0054.042.3402','wxj1.020930_0054.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.020930_0106.047.3406','wxj1.020930_0106.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021002_0035.052.2626','wxj1.021002_0035.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021002_0048.057.2630','wxj1.021002_0048.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021002_0100.062.2634','wxj1.021002_0100.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021004_0035.056.1965','wxj1.021004_0035.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021004_0048.061.1969','wxj1.021004_0048.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021004_0059.066.1973','wxj1.021004_0059.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021006_0103.078.1196','wxj1.021006_0103.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021006_0116.083.1200','wxj1.021006_0116.083','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021006_0136.062.1204','wxj1.021006_0136.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021008_0048.056.461','wxj1.021008_0048.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021008_0104.061.465','wxj1.021008_0104.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021010_0055.055.3553','wxj1.021010_0055.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021010_0109.060.3556','wxj1.021010_0109.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021012_0054.047.2989','wxj1.021012_0054.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021012_0107.052.2993','wxj1.021012_0107.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021014_0138.069.2360','wxj1.021014_0138.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxj1.021014_0201.058.2364','wxj1.021014_0201.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.020928_0143.083.3838','wxk1.020928_0143.083','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.020928_0159.088.3839','wxk1.020928_0159.088','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.020930_0108.048.3410','wxk1.020930_0108.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.020930_0119.053.3415','wxk1.020930_0119.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.020930_0132.058.3419','wxk1.020930_0132.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021002_0102.063.2638','wxk1.021002_0102.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021002_0113.068.2642','wxk1.021002_0113.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021002_0127.073.2646','wxk1.021002_0127.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021004_0101.067.1977','wxk1.021004_0101.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021004_0112.072.1981','wxk1.021004_0112.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021004_0125.077.1985','wxk1.021004_0125.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021006_0138.063.1208','wxk1.021006_0138.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021006_0149.068.1212','wxk1.021006_0149.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021006_0203.073.1216','wxk1.021006_0203.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021008_0107.062.469','wxk1.021008_0107.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021008_0122.067.473','wxk1.021008_0122.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021010_0111.061.3559','wxk1.021010_0111.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021010_0134.066.3562','wxk1.021010_0134.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021012_0110.053.2997','wxk1.021012_0110.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021012_0133.058.3002','wxk1.021012_0133.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021014_0204.059.2368','wxk1.021014_0204.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021014_0226.064.2372','wxk1.021014_0226.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021030_0131.050.1791','wxk1.021030_0131.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021030_0142.052.1795','wxk1.021030_0142.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021030_0207.054.1799','wxk1.021030_0207.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021105_0129.051.688','wxk1.021105_0129.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021105_0138.053.547','wxk1.021105_0138.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021105_0202.055.551','wxk1.021105_0202.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021111_0146.061.3424','wxk1.021111_0146.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021111_0214.064.3428','wxk1.021111_0214.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021128_0159.041.1683','wxk1.021128_0159.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021128_0209.043.1688','wxk1.021128_0209.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021204_0214.046.423','wxk1.021204_0214.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021204_0238.048.427','wxk1.021204_0238.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021206_0204.052.3414','wxk1.021206_0204.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021206_0215.054.3418','wxk1.021206_0215.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxk1.021206_0240.056.3422','wxk1.021206_0240.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.020928_0145.084.3840','wxl1.020928_0145.084','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.020928_0201.089.3841','wxl1.020928_0201.089','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.020930_0111.049.3423','wxl1.020930_0111.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.020930_0121.054.3426','wxl1.020930_0121.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021002_0104.064.2650','wxl1.021002_0104.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021002_0116.069.2654','wxl1.021002_0116.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021002_0129.074.2658','wxl1.021002_0129.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021004_0104.068.1989','wxl1.021004_0104.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021004_0114.073.1993','wxl1.021004_0114.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021004_0127.078.1997','wxl1.021004_0127.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021006_0141.064.1220','wxl1.021006_0141.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021006_0152.069.1224','wxl1.021006_0152.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021006_0205.074.1228','wxl1.021006_0205.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021008_0110.063.477','wxl1.021008_0110.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021008_0125.068.481','wxl1.021008_0125.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021010_0114.062.3565','wxl1.021010_0114.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021010_0137.067.3568','wxl1.021010_0137.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021012_0113.054.3005','wxl1.021012_0113.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021012_0136.059.3009','wxl1.021012_0136.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021014_0207.060.2376','wxl1.021014_0207.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021014_0229.065.2380','wxl1.021014_0229.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021101_0146.058.1287','wxl1.021101_0146.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021101_0156.060.1291','wxl1.021101_0156.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021101_0220.062.1295','wxl1.021101_0220.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021109_0146.056.3775','wxl1.021109_0146.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021109_0158.058.3777','wxl1.021109_0158.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021109_0223.060.3779','wxl1.021109_0223.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021113_0206.051.2814','wxl1.021113_0206.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021113_0215.053.2818','wxl1.021113_0215.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021113_0304.058.2824','wxl1.021113_0304.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021130_0207.053.1226','wxl1.021130_0207.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021130_0221.041.1230','wxl1.021130_0221.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021130_0245.043.1234','wxl1.021130_0245.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021202_0150.060.933','wxl1.021202_0150.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021202_0200.062.937','wxl1.021202_0200.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxl1.021202_0224.064.941','wxl1.021202_0224.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.020928_0147.085.3842','wxm1.020928_0147.085','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.020928_0203.090.3843','wxm1.020928_0203.090','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.020930_0113.050.3430','wxm1.020930_0113.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.020930_0124.055.3435','wxm1.020930_0124.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.020930_0136.060.3439','wxm1.020930_0136.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021002_0106.065.2662','wxm1.021002_0106.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021002_0118.070.2666','wxm1.021002_0118.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021002_0131.075.2670','wxm1.021002_0131.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021004_0106.069.2001','wxm1.021004_0106.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021004_0117.074.2005','wxm1.021004_0117.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021004_0129.079.2009','wxm1.021004_0129.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021006_0143.065.1232','wxm1.021006_0143.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021006_0155.070.1237','wxm1.021006_0155.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021006_0207.075.1241','wxm1.021006_0207.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021008_0113.064.486','wxm1.021008_0113.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021008_0128.069.490','wxm1.021008_0128.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021010_0117.063.3571','wxm1.021010_0117.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021010_0140.068.3574','wxm1.021010_0140.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021012_0116.055.3014','wxm1.021012_0116.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021012_0139.060.3017','wxm1.021012_0139.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021014_0210.061.2384','wxm1.021014_0210.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021014_0232.066.2388','wxm1.021014_0232.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021030_0135.051.1803','wxm1.021030_0135.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021030_0149.053.1807','wxm1.021030_0149.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021030_0210.055.1811','wxm1.021030_0210.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021030_0212.056.1815','wxm1.021030_0212.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021103_0311.083.793','wxm1.021103_0311.083','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021105_0132.052.555','wxm1.021105_0132.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021105_0144.054.559','wxm1.021105_0144.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021105_0205.056.563','wxm1.021105_0205.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021111_0148.062.3432','wxm1.021111_0148.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021111_0155.063.3436','wxm1.021111_0155.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021111_0217.065.3440','wxm1.021111_0217.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021128_0201.042.1691','wxm1.021128_0201.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021128_0214.044.1695','wxm1.021128_0214.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021204_0226.047.431','wxm1.021204_0226.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021204_0250.049.435','wxm1.021204_0250.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021206_0208.053.3427','wxm1.021206_0208.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021206_0221.055.3431','wxm1.021206_0221.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021206_0243.057.3434','wxm1.021206_0243.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021210_0133.045.2648','wxm1.021210_0133.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021210_0140.046.2652','wxm1.021210_0140.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021210_0159.047.2656','wxm1.021210_0159.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021216_0132.047.1515','wxm1.021216_0132.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021216_0150.048.1519','wxm1.021216_0150.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021216_0156.049.1522','wxm1.021216_0156.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021216_0206.050.1526','wxm1.021216_0206.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021216_0216.051.1530','wxm1.021216_0216.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021229_0206.051.1014','wxm1.021229_0206.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.021229_0212.052.1018','wxm1.021229_0212.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.030103_0127.046.474','wxm1.030103_0127.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxm1.030103_0134.047.478','wxm1.030103_0134.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.020928_0150.086.3844','wxn1.020928_0150.086','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.020928_0205.091.3845','wxn1.020928_0205.091','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.020930_0115.051.3442','wxn1.020930_0115.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.020930_0127.056.3446','wxn1.020930_0127.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.020930_0138.061.3450','wxn1.020930_0138.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021002_0108.066.2674','wxn1.021002_0108.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021002_0121.071.2678','wxn1.021002_0121.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021002_0133.076.2682','wxn1.021002_0133.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021004_0108.070.2013','wxn1.021004_0108.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021004_0120.075.2017','wxn1.021004_0120.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021004_0131.080.2021','wxn1.021004_0131.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021006_0145.066.1245','wxn1.021006_0145.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021006_0157.071.1249','wxn1.021006_0157.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021006_0209.076.1253','wxn1.021006_0209.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021008_0116.065.494','wxn1.021008_0116.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021008_0131.070.498','wxn1.021008_0131.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021010_0124.064.3577','wxn1.021010_0124.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021010_0142.069.3580','wxn1.021010_0142.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021012_0123.056.3021','wxn1.021012_0123.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021012_0142.061.3025','wxn1.021012_0142.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021014_0217.062.2392','wxn1.021014_0217.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021014_0235.067.2396','wxn1.021014_0235.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021101_0150.059.1299','wxn1.021101_0150.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021101_0202.061.1303','wxn1.021101_0202.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021101_0223.063.1307','wxn1.021101_0223.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021107_0307.103.112','wxn1.021107_0307.103','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021109_0151.057.3781','wxn1.021109_0151.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021109_0204.059.3783','wxn1.021109_0204.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021109_0226.061.3785','wxn1.021109_0226.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021113_0208.052.2826','wxn1.021113_0208.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021113_0221.054.2830','wxn1.021113_0221.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021113_0231.055.2834','wxn1.021113_0231.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021113_0307.059.2839','wxn1.021113_0307.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021130_0211.054.1238','wxn1.021130_0211.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021130_0227.042.1240','wxn1.021130_0227.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021130_0248.044.1244','wxn1.021130_0248.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021202_0153.061.945','wxn1.021202_0153.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021202_0205.063.949','wxn1.021202_0205.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021202_0226.065.953','wxn1.021202_0226.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021208_0135.047.2932','wxn1.021208_0135.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021208_0143.048.2936','wxn1.021208_0143.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxn1.021208_0203.049.2940','wxn1.021208_0203.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.020928_0208.092.3846','wxo1.020928_0208.092','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.020930_0117.052.3455','wxo1.020930_0117.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.020930_0129.057.3459','wxo1.020930_0129.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.020930_0141.062.3463','wxo1.020930_0141.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021002_0111.067.2686','wxo1.021002_0111.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021002_0124.072.2690','wxo1.021002_0124.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021002_0136.077.2694','wxo1.021002_0136.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021004_0110.071.2025','wxo1.021004_0110.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021004_0123.076.2029','wxo1.021004_0123.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021004_0151.037.2033','wxo1.021004_0151.037','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021006_0147.067.1257','wxo1.021006_0147.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021006_0200.072.1261','wxo1.021006_0200.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021006_0211.077.1265','wxo1.021006_0211.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021008_0119.066.502','wxo1.021008_0119.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021008_0134.071.506','wxo1.021008_0134.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021010_0131.065.3583','wxo1.021010_0131.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021010_0145.070.3586','wxo1.021010_0145.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021012_0130.057.3030','wxo1.021012_0130.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021012_0144.062.3034','wxo1.021012_0144.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021014_0223.063.2400','wxo1.021014_0223.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxo1.021014_0237.068.2403','wxo1.021014_0237.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.020928_0210.093.3847','wxp1.020928_0210.093','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.020928_0220.097.3848','wxp1.020928_0220.097','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.020928_0223.098.3849','wxp1.020928_0223.098','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.020930_0143.063.3467','wxp1.020930_0143.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.020930_0152.067.3471','wxp1.020930_0152.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.020930_0203.071.3475','wxp1.020930_0203.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021002_0138.078.2698','wxp1.021002_0138.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021002_0157.034.2702','wxp1.021002_0157.034','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021002_0208.038.2706','wxp1.021002_0208.038','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021004_0153.038.2037','wxp1.021004_0153.038','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021004_0417.048.2041','wxp1.021004_0417.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021006_0213.078.1269','wxp1.021006_0213.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021006_0222.082.1273','wxp1.021006_0222.082','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021006_0226.083.1277','wxp1.021006_0226.083','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021008_0137.072.509','wxp1.021008_0137.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021008_0149.076.513','wxp1.021008_0149.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021010_0148.071.3589','wxp1.021010_0148.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021010_0207.075.3592','wxp1.021010_0207.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021012_0147.063.3038','wxp1.021012_0147.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021012_0207.067.3042','wxp1.021012_0207.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021014_0240.069.2407','wxp1.021014_0240.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021014_0259.073.2412','wxp1.021014_0259.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021030_0215.057.1819','wxp1.021030_0215.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021030_0225.059.1823','wxp1.021030_0225.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021030_0249.061.1827','wxp1.021030_0249.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021105_0207.057.567','wxp1.021105_0207.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021105_0217.059.571','wxp1.021105_0217.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021105_0241.061.575','wxp1.021105_0241.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021111_0220.066.3444','wxp1.021111_0220.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021111_0251.069.3449','wxp1.021111_0251.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021115_0104.049.2217','wxp1.021115_0104.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021115_0116.051.2221','wxp1.021115_0116.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021115_0143.054.2225','wxp1.021115_0143.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021128_0235.045.1699','wxp1.021128_0235.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021128_0245.047.1703','wxp1.021128_0245.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021204_0309.050.439','wxp1.021204_0309.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021204_0328.052.443','wxp1.021204_0328.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021206_0247.058.3438','wxp1.021206_0247.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021206_0257.060.3443','wxp1.021206_0257.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxp1.021206_0321.062.3447','wxp1.021206_0321.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.020928_0213.094.3850','wxq1.020928_0213.094','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.020928_0225.099.3851','wxq1.020928_0225.099','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.020930_0145.064.3479','wxq1.020930_0145.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.020930_0154.068.3483','wxq1.020930_0154.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.020930_0216.073.3486','wxq1.020930_0216.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021002_0140.079.2710','wxq1.021002_0140.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021002_0200.035.2714','wxq1.021002_0200.035','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021002_0210.039.2718','wxq1.021002_0210.039','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021004_0420.049.2045','wxq1.021004_0420.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021006_0215.079.1280','wxq1.021006_0215.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021006_0228.084.1284','wxq1.021006_0228.084','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021008_0140.073.517','wxq1.021008_0140.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021008_0152.077.521','wxq1.021008_0152.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021010_0150.072.3595','wxq1.021010_0150.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021010_0210.076.3598','wxq1.021010_0210.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021012_0150.064.3046','wxq1.021012_0150.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021012_0210.068.3049','wxq1.021012_0210.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021014_0243.070.2415','wxq1.021014_0243.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021014_0302.074.2420','wxq1.021014_0302.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021101_0226.064.1311','wxq1.021101_0226.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021101_0236.066.1315','wxq1.021101_0236.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021101_0300.068.1319','wxq1.021101_0300.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021107_0342.108.115','wxq1.021107_0342.108','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021109_0230.062.3787','wxq1.021109_0230.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021109_0240.064.3789','wxq1.021109_0240.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021109_0305.066.3791','wxq1.021109_0305.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021115_0152.056.2229','wxq1.021115_0152.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021115_0202.058.2233','wxq1.021115_0202.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021115_0236.057.2237','wxq1.021115_0236.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021130_0251.045.1248','wxq1.021130_0251.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021130_0301.047.1252','wxq1.021130_0301.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021130_0326.049.1256','wxq1.021130_0326.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021202_0229.066.957','wxq1.021202_0229.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021202_0239.068.961','wxq1.021202_0239.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxq1.021202_0302.070.965','wxq1.021202_0302.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.020928_0227.100.3852','wxr1.020928_0227.100','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.020930_0148.065.3490','wxr1.020930_0148.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.020930_0157.069.3494','wxr1.020930_0157.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.020930_0218.074.3497','wxr1.020930_0218.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021002_0142.080.2722','wxr1.021002_0142.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021002_0202.036.2726','wxr1.021002_0202.036','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021002_0212.040.2730','wxr1.021002_0212.040','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021004_0422.050.2049','wxr1.021004_0422.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021006_0218.080.1288','wxr1.021006_0218.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021006_0230.085.1292','wxr1.021006_0230.085','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021008_0143.074.525','wxr1.021008_0143.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021008_0155.078.529','wxr1.021008_0155.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021010_0153.073.3601','wxr1.021010_0153.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021010_0212.077.3604','wxr1.021010_0212.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021012_0153.065.3051','wxr1.021012_0153.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021012_0213.069.3056','wxr1.021012_0213.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021014_0246.071.2423','wxr1.021014_0246.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021014_0305.075.2427','wxr1.021014_0305.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021030_0218.058.1831','wxr1.021030_0218.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021030_0231.060.1835','wxr1.021030_0231.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021030_0252.062.1839','wxr1.021030_0252.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021030_0255.063.1844','wxr1.021030_0255.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021103_0319.084.797','wxr1.021103_0319.084','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021105_0210.058.579','wxr1.021105_0210.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021105_0222.060.583','wxr1.021105_0222.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021105_0243.062.587','wxr1.021105_0243.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021111_0223.067.3453','wxr1.021111_0223.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021111_0232.068.3456','wxr1.021111_0232.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021111_0254.070.3460','wxr1.021111_0254.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021115_0107.050.2241','wxr1.021115_0107.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021115_0123.052.2245','wxr1.021115_0123.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021115_0133.053.2249','wxr1.021115_0133.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021115_0146.055.2253','wxr1.021115_0146.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021128_0238.046.1708','wxr1.021128_0238.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021128_0250.048.1710','wxr1.021128_0250.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021204_0316.051.448','wxr1.021204_0316.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021204_0340.053.451','wxr1.021204_0340.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021206_0250.059.3451','wxr1.021206_0250.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021206_0302.061.3454','wxr1.021206_0302.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxr1.021206_0324.063.3458','wxr1.021206_0324.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.020928_0218.096.3853','wxs1.020928_0218.096','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.020928_0229.101.3854','wxs1.020928_0229.101','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.020930_0150.066.3500','wxs1.020930_0150.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.020930_0200.070.3503','wxs1.020930_0200.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.020930_0220.075.3507','wxs1.020930_0220.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021002_0156.033.2734','wxs1.021002_0156.033','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021002_0205.037.2738','wxs1.021002_0205.037','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021002_0214.041.2742','wxs1.021002_0214.041','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021004_0425.051.2053','wxs1.021004_0425.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021006_0220.081.1296','wxs1.021006_0220.081','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021006_0233.086.1300','wxs1.021006_0233.086','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021008_0146.075.533','wxs1.021008_0146.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021008_0158.079.537','wxs1.021008_0158.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021010_0200.074.3607','wxs1.021010_0200.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021010_0215.078.3610','wxs1.021010_0215.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021012_0159.066.3059','wxs1.021012_0159.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021012_0215.070.3066','wxs1.021012_0215.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021014_0252.072.2431','wxs1.021014_0252.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021101_0229.065.1323','wxs1.021101_0229.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021101_0242.067.1327','wxs1.021101_0242.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021101_0304.069.1331','wxs1.021101_0304.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021107_0314.104.119','wxs1.021107_0314.104','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021109_0233.063.3793','wxs1.021109_0233.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021109_0246.065.001.3795','wxs1.021109_0246.065.001','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021109_0246.065.002.3797','wxs1.021109_0246.065.002','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021109_0246.065.3799','wxs1.021109_0246.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021109_0308.067.3801','wxs1.021109_0308.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021113_0310.060.2843','wxs1.021113_0310.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021115_0155.057.2257','wxs1.021115_0155.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021115_0224.056.2261','wxs1.021115_0224.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021115_0239.058.2265','wxs1.021115_0239.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021130_0254.046.1260','wxs1.021130_0254.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021130_0306.048.1264','wxs1.021130_0306.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021130_0330.050.1268','wxs1.021130_0330.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021202_0232.067.971','wxs1.021202_0232.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021202_0244.069.975','wxs1.021202_0244.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021202_0305.071.979','wxs1.021202_0305.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021208_0207.050.2943','wxs1.021208_0207.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021208_0214.051.2947','wxs1.021208_0214.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxs1.021208_0233.052.2951','wxs1.021208_0233.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.020928_0231.102.3855','wxt1.020928_0231.102','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.020928_0238.105.3856','wxt1.020928_0238.105','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.020930_0222.076.3509','wxt1.020930_0222.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.020930_0229.079.3512','wxt1.020930_0229.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.020930_0238.082.3515','wxt1.020930_0238.082','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.021002_0216.042.2746','wxt1.021002_0216.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.021002_0224.045.2750','wxt1.021002_0224.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.021002_0232.048.2754','wxt1.021002_0232.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.021006_0235.087.1305','wxt1.021006_0235.087','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.021008_0201.080.541','wxt1.021008_0201.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt1.021008_0211.083.545','wxt1.021008_0211.083','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.020928_0247.108.3857','wxt2.020928_0247.108','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.020928_0254.111.3858','wxt2.020928_0254.111','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.020928_0309.114.3859','wxt2.020928_0309.114','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.020930_0244.085.3518','wxt2.020930_0244.085','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.020930_0252.088.3521','wxt2.020930_0252.088','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.020930_0308.091.3524','wxt2.020930_0308.091','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021002_0238.051.2758','wxt2.021002_0238.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021002_0245.054.2762','wxt2.021002_0245.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021002_0300.057.2766','wxt2.021002_0300.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021004_0407.045.2057','wxt2.021004_0407.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021008_0220.086.549','wxt2.021008_0220.086','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021008_0247.042.553','wxt2.021008_0247.042','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021008_0300.045.557','wxt2.021008_0300.045','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021010_0218.079.3613','wxt2.021010_0218.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021012_0218.071.3069','wxt2.021012_0218.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021014_0310.076.2435','wxt2.021014_0310.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021030_0258.064.1847','wxt2.021030_0258.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021030_0306.065.1851','wxt2.021030_0306.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021030_0325.066.1855','wxt2.021030_0325.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021103_0342.087.801','wxt2.021103_0342.087','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021105_0342.069.591','wxt2.021105_0342.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021105_0349.070.595','wxt2.021105_0349.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021105_0407.071.599','wxt2.021105_0407.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021111_0300.072.3464','wxt2.021111_0300.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021111_0308.073.3468','wxt2.021111_0308.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021111_0326.074.3472','wxt2.021111_0326.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021115_0315.064.2270','wxt2.021115_0315.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021115_0321.065.2273','wxt2.021115_0321.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021115_0331.066.2276','wxt2.021115_0331.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021115_0341.067.2282','wxt2.021115_0341.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021128_0342.051.1714','wxt2.021128_0342.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021128_0349.052.1718','wxt2.021128_0349.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021204_0406.055.455','wxt2.021204_0406.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021206_0356.067.3462','wxt2.021206_0356.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021206_0402.068.3466','wxt2.021206_0402.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021206_0416.069.3470','wxt2.021206_0416.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021210_0230.051.2660','wxt2.021210_0230.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021210_0237.052.2664','wxt2.021210_0237.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021210_0255.053.2668','wxt2.021210_0255.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021216_0226.052.1535','wxt2.021216_0226.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021216_0233.053.1538','wxt2.021216_0233.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021216_0244.054.1542','wxt2.021216_0244.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021229_0230.053.1022','wxt2.021229_0230.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.021229_0238.054.1025','wxt2.021229_0238.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.030103_0246.052.482','wxt2.030103_0246.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxt2.030103_0253.053.485','wxt2.030103_0253.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu1.020930_0225.077.3527','wxu1.020930_0225.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu1.020930_0232.080.3530','wxu1.020930_0232.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu1.020930_0240.083.3533','wxu1.020930_0240.083','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu1.021002_0219.043.2770','wxu1.021002_0219.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu1.021002_0227.046.2774','wxu1.021002_0227.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu1.021002_0234.049.2778','wxu1.021002_0234.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu1.021006_0237.088.1309','wxu1.021006_0237.088','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu1.021008_0204.081.561','wxu1.021008_0204.081','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu1.021008_0214.084.565','wxu1.021008_0214.084','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.020928_0249.109.3860','wxu2.020928_0249.109','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.020928_0259.112.3861','wxu2.020928_0259.112','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.020928_0311.115.3862','wxu2.020928_0311.115','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.020930_0248.086.3536','wxu2.020930_0248.086','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.020930_0258.089.3539','wxu2.020930_0258.089','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.020930_0310.092.3542','wxu2.020930_0310.092','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021002_0241.052.2782','wxu2.021002_0241.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021002_0250.055.2786','wxu2.021002_0250.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021002_0303.058.2790','wxu2.021002_0303.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021004_0411.046.2061','wxu2.021004_0411.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021008_0223.087.569','wxu2.021008_0223.087','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021008_0251.043.573','wxu2.021008_0251.043','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021008_0315.046.577','wxu2.021008_0315.046','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021010_0225.080.3616','wxu2.021010_0225.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021012_0235.073.3072','wxu2.021012_0235.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021014_0317.077.2439','wxu2.021014_0317.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021101_0405.076.1335','wxu2.021101_0405.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021101_0412.077.1339','wxu2.021101_0412.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021101_0432.078.1343','wxu2.021101_0432.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021107_0334.107.123','wxu2.021107_0334.107','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021109_0408.074.3803','wxu2.021109_0408.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021109_0415.075.3805','wxu2.021109_0415.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021109_0434.076.3807','wxu2.021109_0434.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021113_0417.069.2847','wxu2.021113_0417.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021113_0424.070.2851','wxu2.021113_0424.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021113_0434.071.2855','wxu2.021113_0434.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021113_0443.072.2861','wxu2.021113_0443.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021202_0404.078.982','wxu2.021202_0404.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021202_0411.079.986','wxu2.021202_0411.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021202_0429.080.990','wxu2.021202_0429.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021210_0202.048.2671','wxu2.021210_0202.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021210_0209.049.2675','wxu2.021210_0209.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021210_0228.050.2679','wxu2.021210_0228.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021214_0215.054.2056','wxu2.021214_0215.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021214_0223.055.2060','wxu2.021214_0223.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021214_0233.056.2064','wxu2.021214_0233.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021214_0243.057.2068','wxu2.021214_0243.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021216_0254.055.1545','wxu2.021216_0254.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021216_0301.056.1550','wxu2.021216_0301.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021229_0256.055.1029','wxu2.021229_0256.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.021229_0303.056.1033','wxu2.021229_0303.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.030103_0219.050.489','wxu2.030103_0219.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxu2.030103_0227.051.493','wxu2.030103_0227.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.020928_0236.104.3863','wxv1.020928_0236.104','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.020928_0244.107.3864','wxv1.020928_0244.107','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.020930_0227.078.3545','wxv1.020930_0227.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.020930_0235.081.3548','wxv1.020930_0235.081','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.020930_0242.084.3551','wxv1.020930_0242.084','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.021002_0221.044.2795','wxv1.021002_0221.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.021002_0229.047.2798','wxv1.021002_0229.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.021002_0236.050.2802','wxv1.021002_0236.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.021006_0239.089.1312','wxv1.021006_0239.089','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.021008_0208.082.581','wxv1.021008_0208.082','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv1.021008_0217.085.585','wxv1.021008_0217.085','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.020928_0251.110.3865','wxv2.020928_0251.110','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.020928_0314.116.3866','wxv2.020928_0314.116','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.020930_0250.087.3554','wxv2.020930_0250.087','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.020930_0303.090.3557','wxv2.020930_0303.090','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.020930_0312.093.3560','wxv2.020930_0312.093','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021002_0243.053.2806','wxv2.021002_0243.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021002_0255.056.2810','wxv2.021002_0255.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021002_0305.059.2815','wxv2.021002_0305.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021004_0413.047.2065','wxv2.021004_0413.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021008_0226.088.589','wxv2.021008_0226.088','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021008_0255.044.593','wxv2.021008_0255.044','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021008_0325.047.597','wxv2.021008_0325.047','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021012_0242.074.3075','wxv2.021012_0242.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021014_0324.078.2444','wxv2.021014_0324.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021105_0410.072.603','wxv2.021105_0410.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021105_0416.073.607','wxv2.021105_0416.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021105_0425.074.611','wxv2.021105_0425.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021111_0329.075.3476','wxv2.021111_0329.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021111_0336.076.3480','wxv2.021111_0336.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021111_0354.077.3484','wxv2.021111_0354.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021115_0344.068.2284','wxv2.021115_0344.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021115_0351.069.2290','wxv2.021115_0351.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021115_0401.070.2292','wxv2.021115_0401.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021115_0411.071.2298','wxv2.021115_0411.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021128_0408.053.1722','wxv2.021128_0408.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021128_0414.054.1726','wxv2.021128_0414.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021204_0420.056.459','wxv2.021204_0420.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021206_0419.070.3474','wxv2.021206_0419.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021206_0424.071.3478','wxv2.021206_0424.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021210_0258.054.2683','wxv2.021210_0258.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021210_0305.055.2687','wxv2.021210_0305.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021210_0324.056.2691','wxv2.021210_0324.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021214_0246.058.2072','wxv2.021214_0246.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021214_0253.059.2076','wxv2.021214_0253.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021216_0311.057.1553','wxv2.021216_0311.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021216_0318.058.1557','wxv2.021216_0318.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021216_0328.059.1562','wxv2.021216_0328.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021229_0321.057.1037','wxv2.021229_0321.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021229_0328.058.1041','wxv2.021229_0328.058','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.021229_0337.059.1045','wxv2.021229_0337.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.030103_0311.054.497','wxv2.030103_0311.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxv2.030103_0318.055.501','wxv2.030103_0318.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.020928_0317.117.3867','wxw1.020928_0317.117','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.020928_0324.120.3868','wxw1.020928_0324.120','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.020928_0333.123.3869','wxw1.020928_0333.123','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.020930_0315.094.3563','wxw1.020930_0315.094','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.020930_0322.097.3566','wxw1.020930_0322.097','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.020930_0330.100.3569','wxw1.020930_0330.100','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.021002_0307.060.2819','wxw1.021002_0307.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.021002_0313.063.2822','wxw1.021002_0313.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.021002_0321.066.2829','wxw1.021002_0321.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.021008_0337.048.602','wxw1.021008_0337.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.021008_0352.051.605','wxw1.021008_0352.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw1.021008_0427.054.609','wxw1.021008_0427.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.020928_0340.126.3870','wxw2.020928_0340.126','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.020928_0348.129.3871','wxw2.020928_0348.129','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.020928_0404.132.3872','wxw2.020928_0404.132','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.020928_0411.135.3873','wxw2.020928_0411.135','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.020930_0336.103.3572','wxw2.020930_0336.103','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.020930_0343.106.3575','wxw2.020930_0343.106','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.020930_0358.109.3578','wxw2.020930_0358.109','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.020930_0405.112.3581','wxw2.020930_0405.112','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.020930_0412.115.3585','wxw2.020930_0412.115','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.020930_0428.118.3588','wxw2.020930_0428.118','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021002_0327.069.2833','wxw2.021002_0327.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021002_0334.072.2836','wxw2.021002_0334.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021002_0350.075.2840','wxw2.021002_0350.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021002_0357.078.2844','wxw2.021002_0357.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021002_0407.081.2848','wxw2.021002_0407.081','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021002_0426.034.2852','wxw2.021002_0426.034','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021004_0429.052.2069','wxw2.021004_0429.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021006_0352.103.1316','wxw2.021006_0352.103','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021006_0407.106.1320','wxw2.021006_0407.106','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021006_0422.109.1325','wxw2.021006_0422.109','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021012_0248.075.3079','wxw2.021012_0248.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxw2.021014_0330.079.2447','wxw2.021014_0330.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx1.020928_0319.118.3874','wxx1.020928_0319.118','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx1.020930_0317.095.3591','wxx1.020930_0317.095','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx1.020930_0324.098.3594','wxx1.020930_0324.098','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx1.020930_0332.101.3597','wxx1.020930_0332.101','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx1.021002_0309.061.2856','wxx1.021002_0309.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx1.021002_0316.064.2859','wxx1.021002_0316.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx1.021002_0323.067.2863','wxx1.021002_0323.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx1.021008_0341.049.614','wxx1.021008_0341.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx1.021008_0404.052.618','wxx1.021008_0404.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx1.021008_0430.055.621','wxx1.021008_0430.055','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.020928_0343.127.3875','wxx2.020928_0343.127','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.020928_0406.133.3876','wxx2.020928_0406.133','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.020928_0414.136.3877','wxx2.020928_0414.136','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.020928_0425.139.3878','wxx2.020928_0425.139','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.020930_0338.104.3600','wxx2.020930_0338.104','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.020930_0348.107.3603','wxx2.020930_0348.107','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.020930_0400.110.3606','wxx2.020930_0400.110','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.020930_0408.113.3609','wxx2.020930_0408.113','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.020930_0417.116.3612','wxx2.020930_0417.116','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.020930_0430.119.3615','wxx2.020930_0430.119','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021002_0330.070.2867','wxx2.021002_0330.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021002_0339.073.2873','wxx2.021002_0339.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021002_0352.076.2877','wxx2.021002_0352.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021002_0400.079.2880','wxx2.021002_0400.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021002_0412.082.2885','wxx2.021002_0412.082','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021002_0428.035.2887','wxx2.021002_0428.035','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021004_0432.053.2073','wxx2.021004_0432.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021006_0355.104.1329','wxx2.021006_0355.104','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021006_0412.107.1333','wxx2.021006_0412.107','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021006_0425.110.1337','wxx2.021006_0425.110','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021012_0255.076.3084','wxx2.021012_0255.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxx2.021014_0337.080.2451','wxx2.021014_0337.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.020928_0321.119.3879','wxy1.020928_0321.119','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.020928_0330.122.3880','wxy1.020928_0330.122','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.020930_0319.096.3618','wxy1.020930_0319.096','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.020930_0327.099.3619','wxy1.020930_0327.099','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.020930_0334.102.3622','wxy1.020930_0334.102','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.021002_0311.062.2891','wxy1.021002_0311.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.021002_0318.065.2895','wxy1.021002_0318.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.021002_0325.068.2900','wxy1.021002_0325.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.021008_0345.050.625','wxy1.021008_0345.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.021008_0415.053.629','wxy1.021008_0415.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy1.021008_0433.056.634','wxy1.021008_0433.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.020928_0345.128.3881','wxy2.020928_0345.128','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.020928_0358.131.3882','wxy2.020928_0358.131','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.020928_0409.134.3883','wxy2.020928_0409.134','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.020928_0417.137.3884','wxy2.020928_0417.137','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.020930_0341.105.3625','wxy2.020930_0341.105','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.020930_0353.108.3628','wxy2.020930_0353.108','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.020930_0403.111.3631','wxy2.020930_0403.111','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.020930_0410.114.3634','wxy2.020930_0410.114','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.020930_0423.117.3637','wxy2.020930_0423.117','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.020930_0433.120.3641','wxy2.020930_0433.120','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021002_0332.071.2903','wxy2.021002_0332.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021002_0345.074.2907','wxy2.021002_0345.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021002_0355.077.2911','wxy2.021002_0355.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021002_0404.080.2915','wxy2.021002_0404.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021002_0421.033.2919','wxy2.021002_0421.033','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021002_0431.036.2923','wxy2.021002_0431.036','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021004_0434.054.2077','wxy2.021004_0434.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021006_0404.105.1341','wxy2.021006_0404.105','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021006_0417.108.1345','wxy2.021006_0417.108','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021006_0427.111.1348','wxy2.021006_0427.111','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021012_0258.077.3087','wxy2.021012_0258.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wxy2.021014_0340.081.2455','wxy2.021014_0340.081','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wya1.021010_0315.088.3620','wya1.021010_0315.088','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wya3.021006_0251.090.1352','wya3.021006_0251.090','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wya3.021006_0308.094.1356','wya3.021006_0308.094','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wya3.021006_0333.098.1360','wya3.021006_0333.098','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wya3.021012_0303.078.3092','wya3.021012_0303.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021006_0255.091.1364','wyb3.021006_0255.091','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021006_0314.095.1368','wyb3.021006_0314.095','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021006_0336.099.1372','wyb3.021006_0336.099','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021012_0312.079.3098','wyb3.021012_0312.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021012_0334.082.3099','wyb3.021012_0334.082','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021014_0343.082.2459','wyb3.021014_0343.082','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021014_0637.108.2463','wyb3.021014_0637.108','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021014_0700.111.2467','wyb3.021014_0700.111','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021030_0334.067.1859','wyb3.021030_0334.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021030_0349.069.1863','wyb3.021030_0349.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021030_0426.071.1867','wyb3.021030_0426.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021103_0327.085.805','wyb3.021103_0327.085','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021105_0246.063.615','wyb3.021105_0246.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021105_0300.065.620','wyb3.021105_0300.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021105_0336.067.624','wyb3.021105_0336.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021111_0358.078.3488','wyb3.021111_0358.078','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021111_0406.079.3492','wyb3.021111_0406.079','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021115_0241.059.2302','wyb3.021115_0241.059','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021115_0248.060.2304','wyb3.021115_0248.060','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021115_0259.061.2308','wyb3.021115_0259.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021115_0309.062.2312','wyb3.021115_0309.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021128_0311.049.1730','wyb3.021128_0311.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021128_0322.050.1734','wyb3.021128_0322.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021204_0358.054.463','wyb3.021204_0358.054','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021206_0327.064.3482','wyb3.021206_0327.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021206_0335.065.3487','wyb3.021206_0335.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.021206_0353.066.3491','wyb3.021206_0353.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.030103_0337.056.505','wyb3.030103_0337.056','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyb3.030103_0344.057.510','wyb3.030103_0344.057','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021006_0258.092.1376','wyc3.021006_0258.092','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021006_0320.096.1380','wyc3.021006_0320.096','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021006_0339.100.1384','wyc3.021006_0339.100','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021012_0319.080.3103','wyc3.021012_0319.080','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021012_0337.083.3107','wyc3.021012_0337.083','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021014_0646.109.2472','wyc3.021014_0646.109','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021014_0703.112.2476','wyc3.021014_0703.112','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021101_0307.070.1347','wyc3.021101_0307.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021101_0322.072.1351','wyc3.021101_0322.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021101_0359.074.1355','wyc3.021101_0359.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021107_0321.105.127','wyc3.021107_0321.105','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021109_0310.068.3809','wyc3.021109_0310.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021109_0325.070.3811','wyc3.021109_0325.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021109_0402.072.3813','wyc3.021109_0402.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021113_0319.061.2864','wyc3.021113_0319.061','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021113_0333.063.2868','wyc3.021113_0333.063','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021113_0342.064.2871','wyc3.021113_0342.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021113_0410.067.2875','wyc3.021113_0410.067','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021130_0333.051.1272','wyc3.021130_0333.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021202_0308.072.994','wyc3.021202_0308.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021202_0321.074.998','wyc3.021202_0321.074','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021202_0358.076.1002','wyc3.021202_0358.076','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021208_0236.053.2955','wyc3.021208_0236.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021214_0118.048.2080','wyc3.021214_0118.048','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021214_0127.049.2084','wyc3.021214_0127.049','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyc3.021214_0137.050.2088','wyc3.021214_0137.050','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021006_0306.093.1388','wyd3.021006_0306.093','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021006_0328.097.1392','wyd3.021006_0328.097','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021006_0341.101.1396','wyd3.021006_0341.101','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021012_0325.081.3111','wyd3.021012_0325.081','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021012_0350.086.3116','wyd3.021012_0350.086','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021014_0653.110.2480','wyd3.021014_0653.110','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021014_0711.113.2484','wyd3.021014_0711.113','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021030_0342.068.1871','wyd3.021030_0342.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021030_0408.070.1875','wyd3.021030_0408.070','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021030_0429.072.1879','wyd3.021030_0429.072','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021103_0334.086.809','wyd3.021103_0334.086','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021105_0253.064.628','wyd3.021105_0253.064','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021105_0318.066.632','wyd3.021105_0318.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wyd3.021105_0339.068.636','wyd3.021105_0339.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021012_0340.084.3121','wye2.021012_0340.084','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021012_0347.085.3126','wye2.021012_0347.085','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021014_0713.114.2488','wye2.021014_0713.114','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021014_0720.115.2492','wye2.021014_0720.115','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021101_0315.071.1359','wye2.021101_0315.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021101_0340.073.1363','wye2.021101_0340.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021101_0402.075.1367','wye2.021101_0402.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021107_0328.106.131','wye2.021107_0328.106','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021109_0318.069.3814','wye2.021109_0318.069','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021109_0344.071.3817','wye2.021109_0344.071','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021109_0405.073.3819','wye2.021109_0405.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021113_0326.062.2879','wye2.021113_0326.062','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021113_0352.065.2883','wye2.021113_0352.065','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021113_0401.066.2889','wye2.021113_0401.066','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021113_0413.068.2893','wye2.021113_0413.068','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021202_0314.073.1006','wye2.021202_0314.073','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021202_0340.075.1009','wye2.021202_0340.075','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021202_0401.077.1013','wye2.021202_0401.077','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021214_0147.051.2092','wye2.021214_0147.051','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021214_0158.052.2096','wye2.021214_0158.052','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wye2.021214_0208.053.2100','wye2.021214_0208.053','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wza1.021010_0234.081.3623','wza1.021010_0234.081','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wza1.021010_0308.085.3626','wza1.021010_0308.085','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wzb1.021010_0242.082.3629','wzb1.021010_0242.082','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wzb1.021010_0310.086.3632','wzb1.021010_0310.086','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wzc1.021010_0250.083.3635','wzc1.021010_0250.083','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wzc1.021010_0313.087.3639','wzc1.021010_0313.087','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('wzd1.021010_0257.084.3642','wzd1.021010_0257.084','CTIO_MOSAIC2','CTIO4m',NULL,'w',1),('zero001.136','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.1371','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.1567','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.1739','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.1884','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.2081','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.2224','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.2318','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.2496','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.2897','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.2929','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.3495','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.3644','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.4','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.514','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.6','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.638','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.692','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero001.813','zero001','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.10','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.1375','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.140','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.1571','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.1743','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.1888','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.2085','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.2228','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.2322','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.2500','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.2901','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.2931','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.3498','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.3645','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.518','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.642','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.696','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.8','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero002.817','zero002','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.12','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.1379','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.14','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.144','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.1575','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.1748','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.1892','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.2089','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.2232','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.2326','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.2504','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.2905','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.2935','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.3501','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.3648','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.522','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.646','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.700','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero003.821','zero003','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.1383','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.147','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.1578','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.16','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.1753','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.18','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.1895','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.2093','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.2236','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.2330','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.2508','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.2908','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.2939','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.3504','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.3650','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.526','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.650','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.704','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero004.825','zero004','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.1387','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.152','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.1582','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.1756','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.1900','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.20','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.2097','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.22','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.2240','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.2334','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.2512','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.2913','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.2944','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.3506','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.3652','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.530','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.654','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.708','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero005.829','zero005','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.1391','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.155','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.1586','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.1761','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.2101','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.23','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.2515','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.2917','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.2948','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.3510','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.3654','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.534','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero006.712','zero006','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.1395','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.1590','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.160','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.1765','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.2104','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.2520','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.27','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.2921','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.2952','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.3513','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.3656','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.538','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero007.716','zero007','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.1399','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.1594','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.164','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.1769','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.2108','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.2524','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.2924','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.2957','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.31','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.3516','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.3658','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.542','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero008.720','zero008','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.1403','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.1598','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.168','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.1773','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.2112','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.2528','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.2928','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.2959','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.35','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.3519','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.3660','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.546','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero009.724','zero009','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.1407','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.1602','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.172','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.1777','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.2116','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.2933','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.2964','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.3522','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.3662','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.39','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.550','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero010.728','zero010','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero011.1411','zero011','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero011.1780','zero011','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero011.2937','zero011','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero011.2967','zero011','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero011.3525','zero011','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero011.3664','zero011','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero011.43','zero011','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero011.554','zero011','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero011.732','zero011','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero012.47','zero012','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero012.736','zero012','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero013.51','zero013','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero013.740','zero013','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero014.55','zero014','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero014.744','zero014','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero015.59','zero015','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero015.749','zero015','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero021.1','zero021','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero021.1049','zero021','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero021.1276','zero021','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero021.1400','zero021','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero021.2','zero021','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero021.2697','zero021','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero021.2960','zero021','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero021.3130','zero021','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero021.467','zero021','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero022.1019','zero022','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero022.1052','zero022','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero022.1281','zero022','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero022.1404','zero022','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero022.2699','zero022','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero022.2963','zero022','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero022.3','zero022','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero022.3134','zero022','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero022.471','zero022','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero022.5','zero022','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero023.1023','zero023','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero023.1056','zero023','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero023.1285','zero023','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero023.1408','zero023','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero023.2703','zero023','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero023.2968','zero023','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero023.3138','zero023','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero023.475','zero023','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero023.7','zero023','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero023.9','zero023','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero024.1027','zero024','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero024.1060','zero024','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero024.11','zero024','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero024.1289','zero024','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero024.13','zero024','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero024.1412','zero024','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero024.2708','zero024','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero024.2972','zero024','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero024.3142','zero024','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero024.480','zero024','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero025.1031','zero025','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero025.1064','zero025','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero025.1293','zero025','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero025.1416','zero025','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero025.15','zero025','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero025.17','zero025','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero025.2712','zero025','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero025.2976','zero025','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero025.3146','zero025','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero025.484','zero025','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero026.1034','zero026','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero026.1068','zero026','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero026.1297','zero026','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero026.19','zero026','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero026.1904','zero026','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero026.2339','zero026','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero026.487','zero026','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero026.833','zero026','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero027.1038','zero027','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero027.1072','zero027','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero027.1301','zero027','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero027.1907','zero027','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero027.2343','zero027','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero027.24','zero027','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero027.492','zero027','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero027.837','zero027','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero028.1042','zero028','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero028.1076','zero028','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero028.1304','zero028','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero028.1911','zero028','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero028.2347','zero028','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero028.496','zero028','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero028.841','zero028','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero029.1046','zero029','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero029.1080','zero029','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero029.1308','zero029','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero029.1915','zero029','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero029.2351','zero029','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero029.500','zero029','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero029.845','zero029','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero030.1050','zero030','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero030.1084','zero030','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero030.1313','zero030','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero030.1919','zero030','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero030.2355','zero030','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero030.504','zero030','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero030.849','zero030','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero031.1054','zero031','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero031.1088','zero031','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero031.2359','zero031','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero031.508','zero031','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero066.26','zero066','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero066.3885','zero066','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero067.30','zero067','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero067.3886','zero067','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero068.1606','zero068','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero068.34','zero068','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero068.3887','zero068','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero069.1609','zero069','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero069.38','zero069','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero069.3888','zero069','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero070.1613','zero070','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero070.3889','zero070','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero070.42','zero070','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero071.1617','zero071','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero071.3890','zero071','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero072.1621','zero072','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero072.3891','zero072','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero073.21','zero073','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero073.3892','zero073','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero074.2363','zero074','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero074.25','zero074','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero074.3893','zero074','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero075.2367','zero075','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero075.29','zero075','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero075.3894','zero075','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero076.2371','zero076','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero076.33','zero076','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero076.3895','zero076','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero077.2375','zero077','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero077.37','zero077','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero077.3896','zero077','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero078.2379','zero078','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero078.3528','zero078','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero078.3897','zero078','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero079.3531','zero079','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero079.3898','zero079','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero080.3534','zero080','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero080.3899','zero080','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero081.3537','zero081','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero082.3540','zero082','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero084.558','zero084','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero085.562','zero085','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero086.566','zero086','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero087.2120','zero087','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero087.570','zero087','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero088.2124','zero088','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero088.574','zero088','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero089.2128','zero089','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero089.578','zero089','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero090.2133','zero090','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero090.582','zero090','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero091.2137','zero091','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero105.1420','zero105','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero106.1424','zero106','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero107.1428','zero107','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero108.1432','zero108','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero109.1436','zero109','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero111.1092','zero111','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero112.1096','zero112','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero113.1100','zero113','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero114.1104','zero114','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero115.1108','zero115','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero124.3150','zero124','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero125.3154','zero125','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero126.176','zero126','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero126.3157','zero126','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero127.180','zero127','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero127.28','zero127','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero127.3161','zero127','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero128.184','zero128','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero128.3165','zero128','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero128.32','zero128','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero129.188','zero129','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero129.36','zero129','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero130.1415','zero130','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero130.192','zero130','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero130.40','zero130','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero131.1419','zero131','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero131.1923','zero131','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero131.44','zero131','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero132.1423','zero132','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero132.1928','zero132','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero133.1427','zero133','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero133.1932','zero133','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero134.1431','zero134','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero139.512','zero139','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero140.516','zero140','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero141.1784','zero141','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero141.520','zero141','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero141.658','zero141','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero142.1788','zero142','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero142.2105','zero142','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero142.524','zero142','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero142.662','zero142','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero143.1792','zero143','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero143.2109','zero143','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero143.528','zero143','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero143.666','zero143','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero144.1796','zero144','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero144.2113','zero144','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero144.670','zero144','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero145.1800','zero145','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero145.2117','zero145','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero145.674','zero145','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero146.2121','zero146','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero147.2125','zero147','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero148.2129','zero148','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero149.2132','zero149','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero150.2136','zero150','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero151.2140','zero151','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero151.2941','zero151','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero152.2144','zero152','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero152.2945','zero152','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero153.2148','zero153','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero153.2949','zero153','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero154.2152','zero154','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero154.2953','zero154','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero155.2156','zero155','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero155.2956','zero155','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero156.2160','zero156','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero157.1625','zero157','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero158.1630','zero158','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero159.1633','zero159','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero160.1638','zero160','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero161.1643','zero161','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero162.1648','zero162','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1),('zero165.1652','zero165','CTIO_MOSAIC2','CTIO4m',NULL,'bias',1);
+/*!40000 ALTER TABLE `newExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `newImfile`
+--
+
+DROP TABLE IF EXISTS `newImfile`;
+CREATE TABLE `newImfile` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `class` varchar(64) NOT NULL default '',
+  `class_id` varchar(64) NOT NULL default '',
+  `uri` varchar(255) default NULL,
+  PRIMARY KEY  (`exp_tag`,`class`,`class_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `newImfile`
+--
+
+LOCK TABLES `newImfile` WRITE;
+/*!40000 ALTER TABLE `newImfile` DISABLE KEYS */;
+INSERT INTO `newImfile` VALUES ('dflat001.1058','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat001.fits'),('dflat001.1113','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat001.fits'),('dflat001.1317','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat001.fits'),('dflat001.1440','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat001.fits'),('dflat001.2717','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat001.fits'),('dflat001.2980','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat001.fits'),('dflat001.3171','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat001.fits'),('dflat001.41','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat001.fits'),('dflat001.48','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat001.fits'),('dflat001.584','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat001.fits'),('dflat002.1062','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat002.fits'),('dflat002.1117','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat002.fits'),('dflat002.1322','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat002.fits'),('dflat002.1444','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat002.fits'),('dflat002.2721','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat002.fits'),('dflat002.2984','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat002.fits'),('dflat002.3175','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat002.fits'),('dflat002.45','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat002.fits'),('dflat002.52','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat002.fits'),('dflat002.588','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat002.fits'),('dflat003.1066','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat003.fits'),('dflat003.1121','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat003.fits'),('dflat003.1326','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat003.fits'),('dflat003.1448','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat003.fits'),('dflat003.2725','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat003.fits'),('dflat003.2988','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat003.fits'),('dflat003.3179','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat003.fits'),('dflat003.49','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat003.fits'),('dflat003.56','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat003.fits'),('dflat003.592','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat003.fits'),('dflat004.1070','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat004.fits'),('dflat004.1125','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat004.fits'),('dflat004.1330','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat004.fits'),('dflat004.1452','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat004.fits'),('dflat004.2729','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat004.fits'),('dflat004.2992','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat004.fits'),('dflat004.3183','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat004.fits'),('dflat004.53','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat004.fits'),('dflat004.596','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat004.fits'),('dflat004.60','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat004.fits'),('dflat005.1129','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat005.fits'),('dflat005.1334','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat005.fits'),('dflat005.1456','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat005.fits'),('dflat005.2733','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat005.fits'),('dflat005.2996','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat005.fits'),('dflat005.3186','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat005.fits'),('dflat005.57','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat005.fits'),('dflat005.600','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat005.fits'),('dflat005.64','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat005.fits'),('dflat006.1074','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat006.fits'),('dflat006.1133','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat006.fits'),('dflat006.1338','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat006.fits'),('dflat006.1460','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat006.fits'),('dflat006.1936','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat006.fits'),('dflat006.2244','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat006.fits'),('dflat006.2383','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat006.fits'),('dflat006.2735','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat006.fits'),('dflat006.3000','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat006.fits'),('dflat006.3190','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat006.fits'),('dflat006.46','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat006.fits'),('dflat006.604','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat006.fits'),('dflat006.61','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat006.fits'),('dflat006.68','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat006.fits'),('dflat006.853','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat006.fits'),('dflat007.1078','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat007.fits'),('dflat007.1137','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat007.fits'),('dflat007.1342','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat007.fits'),('dflat007.1466','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat007.fits'),('dflat007.1940','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat007.fits'),('dflat007.2248','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat007.fits'),('dflat007.2387','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat007.fits'),('dflat007.2740','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat007.fits'),('dflat007.3004','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat007.fits'),('dflat007.3194','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat007.fits'),('dflat007.50','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat007.fits'),('dflat007.608','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat007.fits'),('dflat007.65','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat007.fits'),('dflat007.72','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat007.fits'),('dflat007.857','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat007.fits'),('dflat008.1082','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat008.fits'),('dflat008.1141','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat008.fits'),('dflat008.1346','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat008.fits'),('dflat008.1470','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat008.fits'),('dflat008.1944','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat008.fits'),('dflat008.2252','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat008.fits'),('dflat008.2391','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat008.fits'),('dflat008.2744','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat008.fits'),('dflat008.3008','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat008.fits'),('dflat008.3198','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat008.fits'),('dflat008.54','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat008.fits'),('dflat008.612','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat008.fits'),('dflat008.69','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat008.fits'),('dflat008.76','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat008.fits'),('dflat008.861','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat008.fits'),('dflat009.1086','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat009.fits'),('dflat009.1145','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat009.fits'),('dflat009.1350','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat009.fits'),('dflat009.1475','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat009.fits'),('dflat009.1948','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat009.fits'),('dflat009.2256','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat009.fits'),('dflat009.2395','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat009.fits'),('dflat009.2749','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat009.fits'),('dflat009.3012','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat009.fits'),('dflat009.3201','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat009.fits'),('dflat009.58','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat009.fits'),('dflat009.616','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat009.fits'),('dflat009.73','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat009.fits'),('dflat009.80','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat009.fits'),('dflat009.865','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat009.fits'),('dflat010.1090','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat010.fits'),('dflat010.1149','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat010.fits'),('dflat010.1354','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat010.fits'),('dflat010.1479','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat010.fits'),('dflat010.1952','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat010.fits'),('dflat010.2262','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat010.fits'),('dflat010.2399','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat010.fits'),('dflat010.2532','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat010.fits'),('dflat010.2753','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat010.fits'),('dflat010.3015','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat010.fits'),('dflat010.3207','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat010.fits'),('dflat010.619','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat010.fits'),('dflat010.62','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat010.fits'),('dflat010.77','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat010.fits'),('dflat010.84','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat010.fits'),('dflat010.869','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat010.fits'),('dflat011.1094','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat011.fits'),('dflat011.1153','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat011.fits'),('dflat011.1358','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat011.fits'),('dflat011.1482','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat011.fits'),('dflat011.1656','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat011.fits'),('dflat011.1955','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat011.fits'),('dflat011.2141','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat011.fits'),('dflat011.2266','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat011.fits'),('dflat011.2404','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat011.fits'),('dflat011.2536','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat011.fits'),('dflat011.2757','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat011.fits'),('dflat011.3020','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat011.fits'),('dflat011.3210','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat011.fits'),('dflat011.3900','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat011.fits'),('dflat011.623','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat011.fits'),('dflat011.66','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat011.fits'),('dflat011.81','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat011.fits'),('dflat011.873','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat011.fits'),('dflat011.88','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat011.fits'),('dflat012.1098','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat012.fits'),('dflat012.1157','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat012.fits'),('dflat012.1362','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat012.fits'),('dflat012.1435','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat012.fits'),('dflat012.1486','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat012.fits'),('dflat012.1660','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat012.fits'),('dflat012.1804','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat012.fits'),('dflat012.1959','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat012.fits'),('dflat012.196','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat012.fits'),('dflat012.2145','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat012.fits'),('dflat012.2269','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat012.fits'),('dflat012.2408','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat012.fits'),('dflat012.2540','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat012.fits'),('dflat012.2761','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat012.fits'),('dflat012.2961','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat012.fits'),('dflat012.2971','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat012.fits'),('dflat012.3023','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat012.fits'),('dflat012.3215','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat012.fits'),('dflat012.3543','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat012.fits'),('dflat012.3901','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat012.fits'),('dflat012.586','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat012.fits'),('dflat012.627','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat012.fits'),('dflat012.70','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat012.fits'),('dflat012.85','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat012.fits'),('dflat012.877','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat012.fits'),('dflat012.91','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat012.fits'),('dflat013.1102','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat013.fits'),('dflat013.1161','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat013.fits'),('dflat013.1366','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat013.fits'),('dflat013.1439','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat013.fits'),('dflat013.1490','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat013.fits'),('dflat013.1664','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat013.fits'),('dflat013.1808','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat013.fits'),('dflat013.1963','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat013.fits'),('dflat013.200','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat013.fits'),('dflat013.2149','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat013.fits'),('dflat013.2274','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat013.fits'),('dflat013.2411','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat013.fits'),('dflat013.2544','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat013.fits'),('dflat013.2764','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat013.fits'),('dflat013.2965','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat013.fits'),('dflat013.2975','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat013.fits'),('dflat013.3027','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat013.fits'),('dflat013.3219','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat013.fits'),('dflat013.3546','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat013.fits'),('dflat013.3902','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat013.fits'),('dflat013.590','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat013.fits'),('dflat013.631','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat013.fits'),('dflat013.74','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat013.fits'),('dflat013.881','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat013.fits'),('dflat013.89','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat013.fits'),('dflat013.95','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat013.fits'),('dflat014.1106','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat014.fits'),('dflat014.1165','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat014.fits'),('dflat014.1370','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat014.fits'),('dflat014.1443','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat014.fits'),('dflat014.1494','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat014.fits'),('dflat014.1668','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat014.fits'),('dflat014.1812','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat014.fits'),('dflat014.1967','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat014.fits'),('dflat014.204','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat014.fits'),('dflat014.2153','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat014.fits'),('dflat014.2278','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat014.fits'),('dflat014.2416','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat014.fits'),('dflat014.2548','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat014.fits'),('dflat014.2768','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat014.fits'),('dflat014.2969','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat014.fits'),('dflat014.2979','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat014.fits'),('dflat014.3031','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat014.fits'),('dflat014.3222','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat014.fits'),('dflat014.3549','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat014.fits'),('dflat014.3903','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat014.fits'),('dflat014.594','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat014.fits'),('dflat014.635','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat014.fits'),('dflat014.78','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat014.fits'),('dflat014.885','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat014.fits'),('dflat014.94','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat014.fits'),('dflat014.99','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat014.fits'),('dflat015.104','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat015.fits'),('dflat015.1110','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat015.fits'),('dflat015.1169','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat015.fits'),('dflat015.1374','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat015.fits'),('dflat015.1447','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat015.fits'),('dflat015.1498','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat015.fits'),('dflat015.1672','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat015.fits'),('dflat015.1816','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat015.fits'),('dflat015.1971','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat015.fits'),('dflat015.208','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat015.fits'),('dflat015.2159','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat015.fits'),('dflat015.2281','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat015.fits'),('dflat015.2419','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat015.fits'),('dflat015.2552','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat015.fits'),('dflat015.2772','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat015.fits'),('dflat015.2974','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat015.fits'),('dflat015.2983','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat015.fits'),('dflat015.3036','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat015.fits'),('dflat015.3226','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat015.fits'),('dflat015.3552','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat015.fits'),('dflat015.3904','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat015.fits'),('dflat015.598','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat015.fits'),('dflat015.639','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat015.fits'),('dflat015.82','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat015.fits'),('dflat015.889','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat015.fits'),('dflat015.98','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat015.fits'),('dflat016.102','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat016.fits'),('dflat016.108','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat016.fits'),('dflat016.1114','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat016.fits'),('dflat016.1173','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat016.fits'),('dflat016.1378','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat016.fits'),('dflat016.1450','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat016.fits'),('dflat016.1501','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat016.fits'),('dflat016.1676','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat016.fits'),('dflat016.1820','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat016.fits'),('dflat016.1975','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat016.fits'),('dflat016.212','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat016.fits'),('dflat016.2162','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat016.fits'),('dflat016.2285','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat016.fits'),('dflat016.2424','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat016.fits'),('dflat016.2556','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat016.fits'),('dflat016.2775','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat016.fits'),('dflat016.2978','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat016.fits'),('dflat016.2987','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat016.fits'),('dflat016.3040','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat016.fits'),('dflat016.3229','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat016.fits'),('dflat016.3555','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat016.fits'),('dflat016.3905','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat016.fits'),('dflat016.601','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat016.fits'),('dflat016.643','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat016.fits'),('dflat016.86','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat016.fits'),('dflat016.893','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat016.fits'),('dflat017.105','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat017.fits'),('dflat017.111','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat017.fits'),('dflat017.1118','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat017.fits'),('dflat017.1177','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat017.fits'),('dflat017.1382','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat017.fits'),('dflat017.1454','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat017.fits'),('dflat017.1505','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat017.fits'),('dflat017.1680','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat017.fits'),('dflat017.1824','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat017.fits'),('dflat017.1980','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat017.fits'),('dflat017.2166','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat017.fits'),('dflat017.217','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat017.fits'),('dflat017.2289','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat017.fits'),('dflat017.2429','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat017.fits'),('dflat017.2561','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat017.fits'),('dflat017.2779','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat017.fits'),('dflat017.2982','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat017.fits'),('dflat017.2991','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat017.fits'),('dflat017.3044','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat017.fits'),('dflat017.3233','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat017.fits'),('dflat017.3558','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat017.fits'),('dflat017.3906','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat017.fits'),('dflat017.606','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat017.fits'),('dflat017.647','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat017.fits'),('dflat017.897','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat017.fits'),('dflat017.90','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat017.fits'),('dflat018.110','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat018.fits'),('dflat018.1122','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat018.fits'),('dflat018.116','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat018.fits'),('dflat018.1180','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat018.fits'),('dflat018.1386','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat018.fits'),('dflat018.1458','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat018.fits'),('dflat018.1509','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat018.fits'),('dflat018.1684','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat018.fits'),('dflat018.1828','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat018.fits'),('dflat018.1984','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat018.fits'),('dflat018.2170','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat018.fits'),('dflat018.221','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat018.fits'),('dflat018.2293','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat018.fits'),('dflat018.2433','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat018.fits'),('dflat018.2565','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat018.fits'),('dflat018.2783','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat018.fits'),('dflat018.2986','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat018.fits'),('dflat018.2995','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat018.fits'),('dflat018.3048','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat018.fits'),('dflat018.3239','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat018.fits'),('dflat018.3561','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat018.fits'),('dflat018.3907','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat018.fits'),('dflat018.610','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat018.fits'),('dflat018.651','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat018.fits'),('dflat018.901','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat018.fits'),('dflat018.93','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat018.fits'),('dflat019.1126','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat019.fits'),('dflat019.114','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat019.fits'),('dflat019.1185','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat019.fits'),('dflat019.120','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat019.fits'),('dflat019.1390','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat019.fits'),('dflat019.1463','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat019.fits'),('dflat019.1513','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat019.fits'),('dflat019.1687','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat019.fits'),('dflat019.1832','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat019.fits'),('dflat019.1988','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat019.fits'),('dflat019.2173','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat019.fits'),('dflat019.225','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat019.fits'),('dflat019.2296','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat019.fits'),('dflat019.2436','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat019.fits'),('dflat019.2569','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat019.fits'),('dflat019.2788','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat019.fits'),('dflat019.2990','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat019.fits'),('dflat019.2999','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat019.fits'),('dflat019.3052','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat019.fits'),('dflat019.3242','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat019.fits'),('dflat019.3564','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat019.fits'),('dflat019.3908','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat019.fits'),('dflat019.613','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat019.fits'),('dflat019.655','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat019.fits'),('dflat019.905','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat019.fits'),('dflat019.97','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat019.fits'),('dflat020.101','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat020.fits'),('dflat020.1130','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat020.fits'),('dflat020.118','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/dflat020.fits'),('dflat020.1189','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/dflat020.fits'),('dflat020.124','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/dflat020.fits'),('dflat020.1394','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/dflat020.fits'),('dflat020.1467','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat020.fits'),('dflat020.1517','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/dflat020.fits'),('dflat020.1692','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat020.fits'),('dflat020.1836','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat020.fits'),('dflat020.1991','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat020.fits'),('dflat020.2177','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat020.fits'),('dflat020.228','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat020.fits'),('dflat020.2301','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat020.fits'),('dflat020.2440','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat020.fits'),('dflat020.2573','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat020.fits'),('dflat020.2791','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/dflat020.fits'),('dflat020.2994','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat020.fits'),('dflat020.3003','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat020.fits'),('dflat020.3057','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/dflat020.fits'),('dflat020.3246','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/dflat020.fits'),('dflat020.3567','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat020.fits'),('dflat020.3909','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat020.fits'),('dflat020.617','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat020.fits'),('dflat020.659','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/dflat020.fits'),('dflat020.909','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat020.fits'),('dflat021.106','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat021.fits'),('dflat021.1134','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/dflat021.fits'),('dflat021.1471','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat021.fits'),('dflat021.1696','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat021.fits'),('dflat021.1840','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat021.fits'),('dflat021.1996','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat021.fits'),('dflat021.2181','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat021.fits'),('dflat021.2305','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat021.fits'),('dflat021.232','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat021.fits'),('dflat021.2443','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat021.fits'),('dflat021.2577','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat021.fits'),('dflat021.2998','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat021.fits'),('dflat021.3007','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat021.fits'),('dflat021.3570','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat021.fits'),('dflat021.3910','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat021.fits'),('dflat021.622','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat021.fits'),('dflat021.678','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat021.fits'),('dflat021.913','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat021.fits'),('dflat022.109','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat022.fits'),('dflat022.1474','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat022.fits'),('dflat022.1700','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat022.fits'),('dflat022.1843','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat022.fits'),('dflat022.2000','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat022.fits'),('dflat022.2185','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat022.fits'),('dflat022.2309','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat022.fits'),('dflat022.236','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat022.fits'),('dflat022.2448','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat022.fits'),('dflat022.2581','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat022.fits'),('dflat022.3001','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat022.fits'),('dflat022.3011','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat022.fits'),('dflat022.3573','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat022.fits'),('dflat022.3911','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat022.fits'),('dflat022.626','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat022.fits'),('dflat022.682','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat022.fits'),('dflat022.917','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat022.fits'),('dflat023.113','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat023.fits'),('dflat023.1478','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat023.fits'),('dflat023.1704','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat023.fits'),('dflat023.1848','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat023.fits'),('dflat023.2004','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat023.fits'),('dflat023.2189','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat023.fits'),('dflat023.2313','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat023.fits'),('dflat023.241','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat023.fits'),('dflat023.2452','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat023.fits'),('dflat023.2585','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat023.fits'),('dflat023.3006','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat023.fits'),('dflat023.3016','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat023.fits'),('dflat023.3576','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat023.fits'),('dflat023.3912','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat023.fits'),('dflat023.630','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat023.fits'),('dflat023.686','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat023.fits'),('dflat023.921','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat023.fits'),('dflat024.117','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat024.fits'),('dflat024.1483','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat024.fits'),('dflat024.1707','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat024.fits'),('dflat024.1852','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat024.fits'),('dflat024.2008','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat024.fits'),('dflat024.2193','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat024.fits'),('dflat024.2316','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat024.fits'),('dflat024.245','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat024.fits'),('dflat024.2456','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat024.fits'),('dflat024.2589','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat024.fits'),('dflat024.3010','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat024.fits'),('dflat024.3019','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat024.fits'),('dflat024.3579','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat024.fits'),('dflat024.3913','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat024.fits'),('dflat024.633','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat024.fits'),('dflat024.690','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat024.fits'),('dflat024.925','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat024.fits'),('dflat025.121','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/dflat025.fits'),('dflat025.1485','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat025.fits'),('dflat025.1711','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat025.fits'),('dflat025.1856','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat025.fits'),('dflat025.2011','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/dflat025.fits'),('dflat025.2198','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat025.fits'),('dflat025.2321','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/dflat025.fits'),('dflat025.2460','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/dflat025.fits'),('dflat025.249','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat025.fits'),('dflat025.2593','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat025.fits'),('dflat025.3013','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat025.fits'),('dflat025.3024','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat025.fits'),('dflat025.3582','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat025.fits'),('dflat025.3914','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat025.fits'),('dflat025.637','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat025.fits'),('dflat025.694','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat025.fits'),('dflat025.930','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/dflat025.fits'),('dflat026.1489','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat026.fits'),('dflat026.1716','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat026.fits'),('dflat026.1860','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat026.fits'),('dflat026.2202','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat026.fits'),('dflat026.253','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat026.fits'),('dflat026.2597','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat026.fits'),('dflat026.3018','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat026.fits'),('dflat026.3028','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat026.fits'),('dflat026.3584','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat026.fits'),('dflat026.3915','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat026.fits'),('dflat026.641','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat026.fits'),('dflat026.698','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat026.fits'),('dflat027.1493','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat027.fits'),('dflat027.1720','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat027.fits'),('dflat027.1864','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat027.fits'),('dflat027.2205','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat027.fits'),('dflat027.257','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat027.fits'),('dflat027.2601','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat027.fits'),('dflat027.3022','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat027.fits'),('dflat027.3032','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat027.fits'),('dflat027.3587','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat027.fits'),('dflat027.3916','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat027.fits'),('dflat027.645','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat027.fits'),('dflat027.703','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat027.fits'),('dflat028.1497','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat028.fits'),('dflat028.1724','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat028.fits'),('dflat028.1868','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat028.fits'),('dflat028.2211','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat028.fits'),('dflat028.2605','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat028.fits'),('dflat028.261','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat028.fits'),('dflat028.3026','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat028.fits'),('dflat028.3035','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat028.fits'),('dflat028.3590','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat028.fits'),('dflat028.3917','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat028.fits'),('dflat028.649','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat028.fits'),('dflat028.707','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat028.fits'),('dflat029.1502','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat029.fits'),('dflat029.1728','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat029.fits'),('dflat029.1872','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat029.fits'),('dflat029.2215','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat029.fits'),('dflat029.2609','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/dflat029.fits'),('dflat029.265','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat029.fits'),('dflat029.3029','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat029.fits'),('dflat029.3039','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat029.fits'),('dflat029.3593','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat029.fits'),('dflat029.3918','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat029.fits'),('dflat029.653','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat029.fits'),('dflat029.711','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat029.fits'),('dflat030.1506','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat030.fits'),('dflat030.1732','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/dflat030.fits'),('dflat030.1876','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat030.fits'),('dflat030.2219','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/dflat030.fits'),('dflat030.269','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/dflat030.fits'),('dflat030.3033','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat030.fits'),('dflat030.3043','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat030.fits'),('dflat030.3596','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat030.fits'),('dflat030.3919','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat030.fits'),('dflat030.657','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat030.fits'),('dflat030.715','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat030.fits'),('dflat031.1510','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/dflat031.fits'),('dflat031.1880','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/dflat031.fits'),('dflat031.3037','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat031.fits'),('dflat031.3047','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/dflat031.fits'),('dflat031.3599','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/dflat031.fits'),('dflat031.3920','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat031.fits'),('dflat031.661','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/dflat031.fits'),('dflat031.719','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat031.fits'),('dflat032.3041','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat032.fits'),('dflat032.3921','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat032.fits'),('dflat032.723','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat032.fits'),('dflat033.3045','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat033.fits'),('dflat033.3922','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat033.fits'),('dflat033.727','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat033.fits'),('dflat034.3050','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat034.fits'),('dflat034.3923','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat034.fits'),('dflat034.63','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/dflat034.fits'),('dflat034.731','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat034.fits'),('dflat035.3053','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat035.fits'),('dflat035.3924','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat035.fits'),('dflat035.67','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/dflat035.fits'),('dflat035.735','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat035.fits'),('dflat036.3058','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/dflat036.fits'),('dflat036.3925','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat036.fits'),('dflat036.71','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/dflat036.fits'),('dflat036.739','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat036.fits'),('dflat036.753','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/dflat036.fits'),('dflat037.3926','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat037.fits'),('dflat037.743','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat037.fits'),('dflat037.75','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/dflat037.fits'),('dflat037.757','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/dflat037.fits'),('dflat038.3927','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat038.fits'),('dflat038.747','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat038.fits'),('dflat038.761','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/dflat038.fits'),('dflat038.79','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/dflat038.fits'),('dflat039.3928','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat039.fits'),('dflat039.750','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat039.fits'),('dflat039.764','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/dflat039.fits'),('dflat039.83','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/dflat039.fits'),('dflat040.3929','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/dflat040.fits'),('dflat040.754','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/dflat040.fits'),('dflat040.768','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/dflat040.fits'),('dflat040.87','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/dflat040.fits'),('dflat041.773','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/dflat041.fits'),('dflat042.777','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/dflat042.fits'),('dflat043.781','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/dflat043.fits'),('sflat026.122','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat026.fits'),('sflat026.1521','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sflat026.fits'),('sflat026.2325','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat026.fits'),('sflat026.2797','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat026.fits'),('sflat026.3062','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat026.fits'),('sflat026.3250','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat026.fits'),('sflat027.125','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat027.fits'),('sflat027.126','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat027.fits'),('sflat027.1525','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sflat027.fits'),('sflat027.2329','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat027.fits'),('sflat027.2801','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat027.fits'),('sflat027.3065','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat027.fits'),('sflat027.3254','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat027.fits'),('sflat028.128','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat028.fits'),('sflat028.129','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat028.fits'),('sflat028.130','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat028.fits'),('sflat028.1529','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sflat028.fits'),('sflat028.2333','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat028.fits'),('sflat028.2805','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat028.fits'),('sflat028.3070','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat028.fits'),('sflat028.3258','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat028.fits'),('sflat029.132','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat029.fits'),('sflat029.133','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat029.fits'),('sflat029.134','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat029.fits'),('sflat029.1534','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sflat029.fits'),('sflat029.2336','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat029.fits'),('sflat029.2809','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat029.fits'),('sflat029.3074','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat029.fits'),('sflat029.3262','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat029.fits'),('sflat030.135','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat030.fits'),('sflat030.137','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat030.fits'),('sflat030.138','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat030.fits'),('sflat030.1539','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sflat030.fits'),('sflat030.2340','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat030.fits'),('sflat030.2613','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat030.fits'),('sflat030.2813','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat030.fits'),('sflat030.3077','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat030.fits'),('sflat030.3266','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat030.fits'),('sflat031.139','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat031.fits'),('sflat031.1398','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sflat031.fits'),('sflat031.141','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat031.fits'),('sflat031.142','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat031.fits'),('sflat031.1543','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sflat031.fits'),('sflat031.1735','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat031.fits'),('sflat031.2015','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat031.fits'),('sflat031.2223','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sflat031.fits'),('sflat031.2344','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat031.fits'),('sflat031.2617','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat031.fits'),('sflat031.2817','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat031.fits'),('sflat031.3082','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat031.fits'),('sflat031.3271','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat031.fits'),('sflat031.935','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat031.fits'),('sflat032.1138','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/sflat032.fits'),('sflat032.1193','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sflat032.fits'),('sflat032.143','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat032.fits'),('sflat032.145','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat032.fits'),('sflat032.146','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat032.fits'),('sflat032.1547','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sflat032.fits'),('sflat032.1738','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat032.fits'),('sflat032.1883','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat032.fits'),('sflat032.2019','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat032.fits'),('sflat032.2348','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat032.fits'),('sflat032.2464','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat032.fits'),('sflat032.2621','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat032.fits'),('sflat032.2821','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat032.fits'),('sflat032.3086','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat032.fits'),('sflat032.3275','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat032.fits'),('sflat032.3602','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat032.fits'),('sflat032.665','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat032.fits'),('sflat032.938','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat032.fits'),('sflat033.1142','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/sflat033.fits'),('sflat033.1197','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sflat033.fits'),('sflat033.148','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat033.fits'),('sflat033.149','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat033.fits'),('sflat033.150','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat033.fits'),('sflat033.1551','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sflat033.fits'),('sflat033.1744','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat033.fits'),('sflat033.1887','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat033.fits'),('sflat033.2023','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat033.fits'),('sflat033.2353','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat033.fits'),('sflat033.2468','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat033.fits'),('sflat033.2625','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat033.fits'),('sflat033.2825','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat033.fits'),('sflat033.3090','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat033.fits'),('sflat033.3279','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat033.fits'),('sflat033.3605','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat033.fits'),('sflat033.669','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat033.fits'),('sflat033.942','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat033.fits'),('sflat034.1146','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/sflat034.fits'),('sflat034.1201','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sflat034.fits'),('sflat034.151','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat034.fits'),('sflat034.153','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat034.fits'),('sflat034.154','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat034.fits'),('sflat034.1555','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sflat034.fits'),('sflat034.1747','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat034.fits'),('sflat034.1891','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat034.fits'),('sflat034.2028','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat034.fits'),('sflat034.2357','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat034.fits'),('sflat034.2471','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat034.fits'),('sflat034.2629','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat034.fits'),('sflat034.2827','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat034.fits'),('sflat034.3094','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat034.fits'),('sflat034.3283','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat034.fits'),('sflat034.3608','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat034.fits'),('sflat034.3666','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sflat034.fits'),('sflat034.532','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat034.fits'),('sflat034.673','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat034.fits'),('sflat034.947','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat034.fits'),('sflat035.1150','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/sflat035.fits'),('sflat035.1205','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sflat035.fits'),('sflat035.1559','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sflat035.fits'),('sflat035.156','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat035.fits'),('sflat035.157','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat035.fits'),('sflat035.158','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat035.fits'),('sflat035.1752','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat035.fits'),('sflat035.1896','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat035.fits'),('sflat035.2031','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat035.fits'),('sflat035.2361','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat035.fits'),('sflat035.2475','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat035.fits'),('sflat035.2633','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat035.fits'),('sflat035.2832','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat035.fits'),('sflat035.3097','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat035.fits'),('sflat035.3287','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat035.fits'),('sflat035.3611','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat035.fits'),('sflat035.3668','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sflat035.fits'),('sflat035.536','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat035.fits'),('sflat035.677','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat035.fits'),('sflat035.951','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat035.fits'),('sflat036.1154','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/sflat036.fits'),('sflat036.1209','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sflat036.fits'),('sflat036.159','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat036.fits'),('sflat036.161','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat036.fits'),('sflat036.162','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sflat036.fits'),('sflat036.1757','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat036.fits'),('sflat036.1899','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat036.fits'),('sflat036.2035','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat036.fits'),('sflat036.2365','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat036.fits'),('sflat036.2479','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat036.fits'),('sflat036.2637','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat036.fits'),('sflat036.2837','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat036.fits'),('sflat036.3101','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat036.fits'),('sflat036.3291','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat036.fits'),('sflat036.3614','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat036.fits'),('sflat036.540','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat036.fits'),('sflat036.681','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat036.fits'),('sflat036.955','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat036.fits'),('sflat037.1158','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/sflat037.fits'),('sflat037.1213','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sflat037.fits'),('sflat037.163','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat037.fits'),('sflat037.165','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat037.fits'),('sflat037.1760','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat037.fits'),('sflat037.1903','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat037.fits'),('sflat037.2038','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat037.fits'),('sflat037.2369','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sflat037.fits'),('sflat037.2483','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat037.fits'),('sflat037.2641','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat037.fits'),('sflat037.2841','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat037.fits'),('sflat037.3061','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat037.fits'),('sflat037.3106','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat037.fits'),('sflat037.3295','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat037.fits'),('sflat037.3617','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat037.fits'),('sflat037.3670','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sflat037.fits'),('sflat037.544','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat037.fits'),('sflat037.685','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat037.fits'),('sflat037.959','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat037.fits'),('sflat038.1162','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/sflat038.fits'),('sflat038.1217','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sflat038.fits'),('sflat038.167','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat038.fits'),('sflat038.169','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat038.fits'),('sflat038.1764','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat038.fits'),('sflat038.1908','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat038.fits'),('sflat038.2042','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat038.fits'),('sflat038.2487','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat038.fits'),('sflat038.2645','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat038.fits'),('sflat038.2845','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat038.fits'),('sflat038.3064','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat038.fits'),('sflat038.3110','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sflat038.fits'),('sflat038.3298','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sflat038.fits'),('sflat038.3621','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat038.fits'),('sflat038.3672','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sflat038.fits'),('sflat038.548','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat038.fits'),('sflat038.689','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat038.fits'),('sflat038.963','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat038.fits'),('sflat039.1166','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/sflat039.fits'),('sflat039.171','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat039.fits'),('sflat039.173','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat039.fits'),('sflat039.1768','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat039.fits'),('sflat039.1912','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat039.fits'),('sflat039.2047','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat039.fits'),('sflat039.2491','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat039.fits'),('sflat039.2649','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat039.fits'),('sflat039.2849','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sflat039.fits'),('sflat039.3068','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat039.fits'),('sflat039.3624','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat039.fits'),('sflat039.3674','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sflat039.fits'),('sflat039.552','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat039.fits'),('sflat039.693','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat039.fits'),('sflat039.967','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat039.fits'),('sflat040.175','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat040.fits'),('sflat040.177','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sflat040.fits'),('sflat040.1772','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat040.fits'),('sflat040.1916','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat040.fits'),('sflat040.2051','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat040.fits'),('sflat040.2495','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat040.fits'),('sflat040.2653','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat040.fits'),('sflat040.3073','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat040.fits'),('sflat040.3627','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat040.fits'),('sflat040.3676','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sflat040.fits'),('sflat040.556','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat040.fits'),('sflat040.697','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat040.fits'),('sflat040.970','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat040.fits'),('sflat041.1776','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat041.fits'),('sflat041.179','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat041.fits'),('sflat041.1920','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat041.fits'),('sflat041.2055','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat041.fits'),('sflat041.2499','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat041.fits'),('sflat041.2657','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sflat041.fits'),('sflat041.3076','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat041.fits'),('sflat041.3630','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat041.fits'),('sflat041.3678','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sflat041.fits'),('sflat041.560','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat041.fits'),('sflat041.701','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat041.fits'),('sflat041.758','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat041.fits'),('sflat041.973','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat041.fits'),('sflat042.1781','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat042.fits'),('sflat042.183','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sflat042.fits'),('sflat042.1924','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat042.fits'),('sflat042.2059','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat042.fits'),('sflat042.2503','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat042.fits'),('sflat042.3081','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat042.fits'),('sflat042.3633','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sflat042.fits'),('sflat042.564','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat042.fits'),('sflat042.705','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat042.fits'),('sflat042.762','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat042.fits'),('sflat042.978','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sflat042.fits'),('sflat043.1785','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sflat043.fits'),('sflat043.1927','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sflat043.fits'),('sflat043.2063','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat043.fits'),('sflat043.2507','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat043.fits'),('sflat043.3085','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat043.fits'),('sflat043.568','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat043.fits'),('sflat043.709','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat043.fits'),('sflat043.766','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat043.fits'),('sflat044.2067','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat044.fits'),('sflat044.2511','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sflat044.fits'),('sflat044.3089','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat044.fits'),('sflat044.572','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat044.fits'),('sflat044.713','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sflat044.fits'),('sflat044.770','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat044.fits'),('sflat045.2071','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat045.fits'),('sflat045.3093','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat045.fits'),('sflat045.3680','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sflat045.fits'),('sflat045.576','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat045.fits'),('sflat045.774','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat045.fits'),('sflat046.2075','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat046.fits'),('sflat046.3096','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat046.fits'),('sflat046.3682','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sflat046.fits'),('sflat046.580','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sflat046.fits'),('sflat046.778','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat046.fits'),('sflat047.3102','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat047.fits'),('sflat047.782','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat047.fits'),('sflat048.3104','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat048.fits'),('sflat048.786','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat048.fits'),('sflat049.3109','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sflat049.fits'),('sflat049.790','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat049.fits'),('sflat050.794','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat050.fits'),('sflat051.798','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat051.fits'),('sflat052.802','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat052.fits'),('sflat053.806','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sflat053.fits'),('sflat054.1170','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/sflat054.fits'),('sflat055.1174','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/sflat055.fits'),('sflat121.2079','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat121.fits'),('sflat122.2083','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat122.fits'),('sflat123.2087','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat123.fits'),('sflat124.2091','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat124.fits'),('sflat125.2095','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat125.fits'),('sflat126.2099','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat126.fits'),('sflat127.2103','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat127.fits'),('sflat128.2107','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat128.fits'),('sflat129.2111','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat129.fits'),('sflat130.2115','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sflat130.fits'),('sm35.020930_0908.177.3054','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm35.020930_0908.177.fits'),('sm35.021002_0900.069.2227','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm35.021002_0900.069.fits'),('sm35.021004_0911.092.1563','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm35.021004_0911.092.fits'),('sm35.021006_0859.123.912','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm35.021006_0859.123.fits'),('sm35.021012_0825.143.2661','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm35.021012_0825.143.fits'),('sm35.021030_0905.129.1514','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm35.021030_0905.129.fits'),('sm35.021101_0833.136.983','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm35.021101_0833.136.fits'),('sm35.021105_0859.125.273','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm35.021105_0859.125.fits'),('sm35.021111_0907.150.3114','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm35.021111_0907.150.fits'),('sm35.021115_0851.140.1931','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm35.021115_0851.140.fits'),('sm35.021216_0817.110.1221','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm35.021216_0817.110.fits'),('sm35.021229_0839.083.717','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm35.021229_0839.083.fits'),('sm35.030103_0841.126.187','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm35.030103_0841.126.fits'),('sm35.92','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/sm35.fits'),('sm36.020930_0905.176.3055','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm36.020930_0905.176.fits'),('sm36.021002_0858.068.2231','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm36.021002_0858.068.fits'),('sm36.021004_0909.091.1565','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm36.021004_0909.091.fits'),('sm36.021006_0857.122.916','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm36.021006_0857.122.fits'),('sm36.021012_0822.142.2665','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm36.021012_0822.142.fits'),('sm36.021030_0903.128.1518','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm36.021030_0903.128.fits'),('sm36.021101_0830.135.987','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm36.021101_0830.135.fits'),('sm36.021105_0857.124.277','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm36.021105_0857.124.fits'),('sm36.021111_0905.149.3118','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm36.021111_0905.149.fits'),('sm36.021115_0849.139.1935','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm36.021115_0849.139.fits'),('sm36.021216_0815.109.1225','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm36.021216_0815.109.fits'),('sm36.021229_0837.082.721','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm36.021229_0837.082.fits'),('sm36.030103_0839.125.191','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm36.030103_0839.125.fits'),('sm36.96','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/sm36.fits'),('sm43.020930_0903.175.3060','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm43.020930_0903.175.fits'),('sm43.021002_0856.067.2235','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm43.021002_0856.067.fits'),('sm43.021004_0907.090.1570','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm43.021004_0907.090.fits'),('sm43.021006_0855.121.920','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm43.021006_0855.121.fits'),('sm43.021012_0820.141.2669','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm43.021012_0820.141.fits'),('sm43.021030_0901.127.1523','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm43.021030_0901.127.fits'),('sm43.021101_0827.134.991','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm43.021101_0827.134.fits'),('sm43.021105_0855.123.281','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm43.021105_0855.123.fits'),('sm43.021111_0903.148.3122','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm43.021111_0903.148.fits'),('sm43.021115_0846.138.1939','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm43.021115_0846.138.fits'),('sm43.021216_0812.108.1229','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm43.021216_0812.108.fits'),('sm43.021229_0835.081.725','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm43.021229_0835.081.fits'),('sm43.030103_0837.124.195','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm43.030103_0837.124.fits'),('sm43.100','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/sm43.fits'),('sm44.020928_0909.130.3684','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm44.020928_0909.130.fits'),('sm44.020930_0900.174.3063','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm44.020930_0900.174.fits'),('sm44.021002_0854.066.2239','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm44.021002_0854.066.fits'),('sm44.021004_0905.089.1573','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm44.021004_0905.089.fits'),('sm44.021006_0853.120.924','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm44.021006_0853.120.fits'),('sm44.021012_0817.140.2673','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm44.021012_0817.140.fits'),('sm44.021030_0858.126.1527','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm44.021030_0858.126.fits'),('sm44.021101_0825.133.995','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm44.021101_0825.133.fits'),('sm44.021105_0853.122.284','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm44.021105_0853.122.fits'),('sm44.021111_0901.147.3125','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm44.021111_0901.147.fits'),('sm44.021115_0844.137.1943','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm44.021115_0844.137.fits'),('sm44.021216_0810.107.1233','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm44.021216_0810.107.fits'),('sm44.021229_0833.080.729','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm44.021229_0833.080.fits'),('sm44.030103_0834.123.199','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm44.030103_0834.123.fits'),('sm45.020928_0906.129.3686','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm45.020928_0906.129.fits'),('sm45.020930_0856.173.3067','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm45.020930_0856.173.fits'),('sm45.021002_0851.065.2243','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm45.021002_0851.065.fits'),('sm45.021004_0902.088.1577','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm45.021004_0902.088.fits'),('sm45.021006_0850.119.928','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm45.021006_0850.119.fits'),('sm45.021008_0927.065.181','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm45.021008_0927.065.fits'),('sm45.021012_0814.139.2677','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm45.021012_0814.139.fits'),('sm45.021030_0856.125.1531','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm45.021030_0856.125.fits'),('sm45.021101_0822.132.1000','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm45.021101_0822.132.fits'),('sm45.021105_0850.121.288','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm45.021105_0850.121.fits'),('sm45.021111_0858.146.3128','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm45.021111_0858.146.fits'),('sm45.021115_0841.136.1947','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm45.021115_0841.136.fits'),('sm45.021216_0807.106.1236','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm45.021216_0807.106.fits'),('sm45.021229_0831.079.733','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm45.021229_0831.079.fits'),('sm45.030103_0832.122.203','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm45.030103_0832.122.fits'),('sm46.020928_0903.128.3688','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm46.020928_0903.128.fits'),('sm46.020930_0853.172.3071','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm46.020930_0853.172.fits'),('sm46.021002_0848.064.2247','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm46.021002_0848.064.fits'),('sm46.021004_0859.087.1581','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm46.021004_0859.087.fits'),('sm46.021006_0848.118.932','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm46.021006_0848.118.fits'),('sm46.021008_0925.064.185','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm46.021008_0925.064.fits'),('sm46.021012_0812.138.2681','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm46.021012_0812.138.fits'),('sm46.021030_0853.124.1533','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm46.021030_0853.124.fits'),('sm46.021101_0819.131.1004','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm46.021101_0819.131.fits'),('sm46.021105_0848.120.292','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm46.021105_0848.120.fits'),('sm46.021111_0856.145.3132','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm46.021111_0856.145.fits'),('sm46.021115_0839.135.1951','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm46.021115_0839.135.fits'),('sm46.021128_0852.118.1402','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm46.021128_0852.118.fits'),('sm46.021206_0845.135.3113','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm46.021206_0845.135.fits'),('sm46.021214_0846.067.1789','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm46.021214_0846.067.fits'),('sm46.021216_0805.105.1242','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm46.021216_0805.105.fits'),('sm46.021229_0828.078.737','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm46.021229_0828.078.fits'),('sm46.030103_0829.121.207','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm46.030103_0829.121.fits'),('sm53.020930_0850.171.3078','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm53.020930_0850.171.fits'),('sm53.021002_0845.063.2251','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm53.021002_0845.063.fits'),('sm53.021004_0857.086.1585','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm53.021004_0857.086.fits'),('sm53.021006_0845.117.936','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm53.021006_0845.117.fits'),('sm53.021008_0909.080.189','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm53.021008_0909.080.fits'),('sm53.021012_0809.137.2685','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm53.021012_0809.137.fits'),('sm53.021030_0850.123.1537','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm53.021030_0850.123.fits'),('sm53.021101_0816.130.1008','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm53.021101_0816.130.fits'),('sm53.021105_0846.119.296','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm53.021105_0846.119.fits'),('sm53.021111_0853.144.3136','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm53.021111_0853.144.fits'),('sm53.021115_0836.134.1956','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm53.021115_0836.134.fits'),('sm53.021128_0849.117.1406','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm53.021128_0849.117.fits'),('sm53.021206_0843.134.3117','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm53.021206_0843.134.fits'),('sm53.021214_0844.066.1794','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm53.021214_0844.066.fits'),('sm53.021216_0802.104.1246','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm53.021216_0802.104.fits'),('sm53.021229_0826.077.741','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm53.021229_0826.077.fits'),('sm53.030103_0826.120.211','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm53.030103_0826.120.fits'),('sm54.020928_0855.126.3690','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm54.020928_0855.126.fits'),('sm54.020930_0844.170.3080','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm54.020930_0844.170.fits'),('sm54.021002_0840.062.2255','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm54.021002_0840.062.fits'),('sm54.021004_0852.085.1589','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm54.021004_0852.085.fits'),('sm54.021006_0840.116.940','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm54.021006_0840.116.fits'),('sm54.021008_0904.079.193','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm54.021008_0904.079.fits'),('sm54.021012_0804.136.2689','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm54.021012_0804.136.fits'),('sm54.021030_0845.122.1541','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm54.021030_0845.122.fits'),('sm54.021101_0811.129.1012','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm54.021101_0811.129.fits'),('sm54.021105_0842.118.300','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm54.021105_0842.118.fits'),('sm54.021111_0850.143.3141','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm54.021111_0850.143.fits'),('sm54.021115_0832.133.1960','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm54.021115_0832.133.fits'),('sm54.021128_0847.116.1410','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm54.021128_0847.116.fits'),('sm54.021206_0841.133.3119','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm54.021206_0841.133.fits'),('sm54.021214_0840.065.1798','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm54.021214_0840.065.fits'),('sm54.021216_0758.103.1250','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm54.021216_0758.103.fits'),('sm54.021229_0822.076.745','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm54.021229_0822.076.fits'),('sm54.030103_0822.119.215','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm54.030103_0822.119.fits'),('sm55.020928_0850.125.3692','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm55.020928_0850.125.fits'),('sm55.020930_0839.169.3083','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm55.020930_0839.169.fits'),('sm55.021002_0835.061.2259','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm55.021002_0835.061.fits'),('sm55.021004_0847.084.1593','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm55.021004_0847.084.fits'),('sm55.021006_0835.115.944','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm55.021006_0835.115.fits'),('sm55.021008_0859.078.197','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm55.021008_0859.078.fits'),('sm55.021012_0759.135.2693','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm55.021012_0759.135.fits'),('sm55.021030_0840.121.1546','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm55.021030_0840.121.fits'),('sm55.021105_0837.117.305','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm55.021105_0837.117.fits'),('sm55.021111_0844.142.3145','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm55.021111_0844.142.fits'),('sm55.021113_0856.133.2516','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm55.021113_0856.133.fits'),('sm55.021115_0827.132.1964','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm55.021115_0827.132.fits'),('sm55.021128_0844.115.1414','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm55.021128_0844.115.fits'),('sm55.021206_0837.132.3124','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm55.021206_0837.132.fits'),('sm55.021214_0835.064.1802','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm55.021214_0835.064.fits'),('sm55.021216_0753.102.1254','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm55.021216_0753.102.fits'),('sm55.021229_0817.075.748','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm55.021229_0817.075.fits'),('sm55.030103_0817.118.219','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm55.030103_0817.118.fits'),('sm56.020928_0844.124.3694','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm56.020928_0844.124.fits'),('sm56.020930_0833.168.3088','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm56.020930_0833.168.fits'),('sm56.021002_0830.060.2263','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm56.021002_0830.060.fits'),('sm56.021004_0842.083.1597','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm56.021004_0842.083.fits'),('sm56.021006_0830.114.948','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm56.021006_0830.114.fits'),('sm56.021008_0854.077.201','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm56.021008_0854.077.fits'),('sm56.021012_0754.134.2695','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm56.021012_0754.134.fits'),('sm56.021030_0834.120.1549','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm56.021030_0834.120.fits'),('sm56.021101_0803.127.1016','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm56.021101_0803.127.fits'),('sm56.021101_0803.128.1020','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm56.021101_0803.128.fits'),('sm56.021105_0833.116.308','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm56.021105_0833.116.fits'),('sm56.021111_0840.141.3149','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm56.021111_0840.141.fits'),('sm56.021113_0852.132.2519','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm56.021113_0852.132.fits'),('sm56.021115_0823.131.1968','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm56.021115_0823.131.fits'),('sm56.021128_0841.114.1418','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm56.021128_0841.114.fits'),('sm56.021206_0834.131.3129','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm56.021206_0834.131.fits'),('sm56.021212_0845.141.2164','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm56.021212_0845.141.fits'),('sm56.021214_0831.063.1805','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm56.021214_0831.063.fits'),('sm56.021216_0749.101.1258','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm56.021216_0749.101.fits'),('sm56.021229_0814.074.752','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm56.021229_0814.074.fits'),('sm56.030103_0813.117.223','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm56.030103_0813.117.fits'),('sm57.020928_0841.123.3696','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm57.020928_0841.123.fits'),('sm57.020930_0829.167.3091','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm57.020930_0829.167.fits'),('sm57.021002_0827.059.2267','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm57.021002_0827.059.fits'),('sm57.021004_0838.082.1601','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm57.021004_0838.082.fits'),('sm57.021006_0827.113.952','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm57.021006_0827.113.fits'),('sm57.021008_0850.076.205','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm57.021008_0850.076.fits'),('sm57.021012_0750.133.2701','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm57.021012_0750.133.fits'),('sm57.021030_0831.119.1554','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm57.021030_0831.119.fits'),('sm57.021101_0800.126.1024','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm57.021101_0800.126.fits'),('sm57.021105_0830.115.312','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm57.021105_0830.115.fits'),('sm57.021111_0838.140.3152','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm57.021111_0838.140.fits'),('sm57.021113_0849.131.2523','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm57.021113_0849.131.fits'),('sm57.021115_0820.130.1972','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm57.021115_0820.130.fits'),('sm57.021128_0838.113.1422','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm57.021128_0838.113.fits'),('sm57.021206_0832.130.3133','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm57.021206_0832.130.fits'),('sm57.021212_0841.140.2168','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm57.021212_0841.140.fits'),('sm57.021214_0829.062.1809','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm57.021214_0829.062.fits'),('sm57.021216_0746.100.1262','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm57.021216_0746.100.fits'),('sm57.021229_0811.073.756','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm57.021229_0811.073.fits'),('sm57.030103_0810.116.227','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm57.030103_0810.116.fits'),('sm58.021002_0935.085.2271','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm58.021002_0935.085.fits'),('sm58.021002_0937.086.2275','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm58.021002_0937.086.fits'),('sm58.021006_0936.140.956','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm58.021006_0936.140.fits'),('sm58.021105_0827.114.316','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm58.021105_0827.114.fits'),('sm58.021109_0859.100.3636','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm58.021109_0859.100.fits'),('sm58.021111_0833.139.3158','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm58.021111_0833.139.fits'),('sm58.021113_0846.130.2527','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm58.021113_0846.130.fits'),('sm58.021115_0817.129.1976','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm58.021115_0817.129.fits'),('sm58.021128_0835.112.1426','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm58.021128_0835.112.fits'),('sm58.021202_0842.138.663','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm58.021202_0842.138.fits'),('sm58.021206_0830.129.3137','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm58.021206_0830.129.fits'),('sm58.021212_0837.139.2172','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm58.021212_0837.139.fits'),('sm58.021214_0826.061.1813','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm58.021214_0826.061.fits'),('sm58.021216_0743.099.1266','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm58.021216_0743.099.fits'),('sm58.021229_0808.072.760','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm58.021229_0808.072.fits'),('sm58.030103_0807.115.231','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm58.030103_0807.115.fits'),('sm63.020930_0824.166.3095','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm63.020930_0824.166.fits'),('sm63.021002_0822.058.2279','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm63.021002_0822.058.fits'),('sm63.021004_0834.081.1605','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm63.021004_0834.081.fits'),('sm63.021006_0810.131.960','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm63.021006_0810.131.fits'),('sm63.021008_0846.075.209','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm63.021008_0846.075.fits'),('sm63.021012_0746.132.2704','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm63.021012_0746.132.fits'),('sm63.021030_0827.118.1558','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm63.021030_0827.118.fits'),('sm63.021101_0756.125.1028','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm63.021101_0756.125.fits'),('sm63.021105_0824.113.320','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm63.021105_0824.113.fits'),('sm63.021109_0856.099.3638','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm63.021109_0856.099.fits'),('sm63.021111_0830.138.3162','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm63.021111_0830.138.fits'),('sm63.021113_0843.129.2531','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm63.021113_0843.129.fits'),('sm63.021115_0814.128.1979','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm63.021115_0814.128.fits'),('sm63.021128_0832.111.1430','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm63.021128_0832.111.fits'),('sm63.021202_0838.137.667','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm63.021202_0838.137.fits'),('sm63.021204_0846.072.166','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm63.021204_0846.072.fits'),('sm63.021206_0827.128.3140','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm63.021206_0827.128.fits'),('sm63.021212_0833.138.2176','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm63.021212_0833.138.fits'),('sm63.021214_0653.116.1817','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm63.021214_0653.116.fits'),('sm63.021216_0740.098.1270','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm63.021216_0740.098.fits'),('sm63.021229_0805.071.765','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm63.021229_0805.071.fits'),('sm63.030103_0804.114.235','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm63.030103_0804.114.fits'),('sm64.020928_0830.121.3698','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm64.020928_0830.121.fits'),('sm64.020930_0818.165.3100','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm64.020930_0818.165.fits'),('sm64.021002_0817.057.2283','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm64.021002_0817.057.fits'),('sm64.021004_0829.080.1610','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm64.021004_0829.080.fits'),('sm64.021006_0805.130.964','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm64.021006_0805.130.fits'),('sm64.021008_0841.074.213','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm64.021008_0841.074.fits'),('sm64.021012_0741.131.2707','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm64.021012_0741.131.fits'),('sm64.021030_0821.117.1561','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm64.021030_0821.117.fits'),('sm64.021101_0752.124.1032','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm64.021101_0752.124.fits'),('sm64.021105_0819.112.324','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm64.021105_0819.112.fits'),('sm64.021109_0851.098.3640','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm64.021109_0851.098.fits'),('sm64.021111_0824.137.3166','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm64.021111_0824.137.fits'),('sm64.021113_0838.128.2535','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm64.021113_0838.128.fits'),('sm64.021115_0809.127.1983','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm64.021115_0809.127.fits'),('sm64.021128_0827.110.1434','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm64.021128_0827.110.fits'),('sm64.021202_0832.136.671','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm64.021202_0832.136.fits'),('sm64.021204_0841.071.170','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm64.021204_0841.071.fits'),('sm64.021206_0823.127.3144','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm64.021206_0823.127.fits'),('sm64.021210_0843.116.2373','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm64.021210_0843.116.fits'),('sm64.021212_0827.137.2180','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm64.021212_0827.137.fits'),('sm64.021214_0648.115.1821','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm64.021214_0648.115.fits'),('sm64.021216_0734.097.1274','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm64.021216_0734.097.fits'),('sm64.021229_0759.070.769','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm64.021229_0759.070.fits'),('sm64.030103_0759.113.239','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm64.030103_0759.113.fits'),('sm65.020928_0828.120.3700','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm65.020928_0828.120.fits'),('sm65.020930_0816.164.3105','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm65.020930_0816.164.fits'),('sm65.021002_0815.056.2287','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm65.021002_0815.056.fits'),('sm65.021004_0827.079.1614','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm65.021004_0827.079.fits'),('sm65.021006_0803.129.968','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm65.021006_0803.129.fits'),('sm65.021008_0839.073.216','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm65.021008_0839.073.fits'),('sm65.021012_0738.130.2713','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm65.021012_0738.130.fits'),('sm65.021014_0921.120.2119','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm65.021014_0921.120.fits'),('sm65.021030_0819.116.1566','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm65.021030_0819.116.fits'),('sm65.021101_0750.123.1036','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm65.021101_0750.123.fits'),('sm65.021105_0817.111.328','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm65.021105_0817.111.fits'),('sm65.021109_0848.097.3643','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm65.021109_0848.097.fits'),('sm65.021111_0822.136.3170','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm65.021111_0822.136.fits'),('sm65.021113_0833.127.2539','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm65.021113_0833.127.fits'),('sm65.021115_0806.126.1987','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm65.021115_0806.126.fits'),('sm65.021128_0823.109.1438','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm65.021128_0823.109.fits'),('sm65.021202_0829.135.675','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm65.021202_0829.135.fits'),('sm65.021204_0839.070.174','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm65.021204_0839.070.fits'),('sm65.021206_0820.126.3148','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm65.021206_0820.126.fits'),('sm65.021210_0841.115.2377','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm65.021210_0841.115.fits'),('sm65.021212_0824.136.2184','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm65.021212_0824.136.fits'),('sm65.021214_0645.114.1825','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm65.021214_0645.114.fits'),('sm65.021216_0732.096.1278','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm65.021216_0732.096.fits'),('sm65.021229_0757.069.772','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm65.021229_0757.069.fits'),('sm65.030103_0757.112.243','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm65.030103_0757.112.fits'),('sm66.020928_0823.119.3702','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm66.020928_0823.119.fits'),('sm66.020930_0810.163.3108','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm66.020930_0810.163.fits'),('sm66.021002_0810.055.2291','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm66.021002_0810.055.fits'),('sm66.021004_0822.078.1618','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm66.021004_0822.078.fits'),('sm66.021008_0834.072.220','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm66.021008_0834.072.fits'),('sm66.021012_0732.129.2715','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm66.021012_0732.129.fits'),('sm66.021014_0914.119.2123','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm66.021014_0914.119.fits'),('sm66.021030_0814.115.1569','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm66.021030_0814.115.fits'),('sm66.021101_0746.122.1040','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm66.021101_0746.122.fits'),('sm66.021105_0812.110.332','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm66.021105_0812.110.fits'),('sm66.021109_0843.096.3646','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm66.021109_0843.096.fits'),('sm66.021111_0817.135.3173','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm66.021111_0817.135.fits'),('sm66.021113_0828.126.2543','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm66.021113_0828.126.fits'),('sm66.021115_0801.125.1990','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm66.021115_0801.125.fits'),('sm66.021128_0818.108.1442','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm66.021128_0818.108.fits'),('sm66.021202_0824.134.679','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm66.021202_0824.134.fits'),('sm66.021204_0834.069.178','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm66.021204_0834.069.fits'),('sm66.021206_0816.125.3153','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm66.021206_0816.125.fits'),('sm66.021210_0838.114.2381','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm66.021210_0838.114.fits'),('sm66.021212_0818.135.2188','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm66.021212_0818.135.fits'),('sm66.021214_0640.113.1829','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm66.021214_0640.113.fits'),('sm66.021216_0727.095.1282','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm66.021216_0727.095.fits'),('sm66.021229_0752.068.776','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm66.021229_0752.068.fits'),('sm66.030103_0751.111.247','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm66.030103_0751.111.fits'),('sm67.020928_0819.118.3704','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm67.020928_0819.118.fits'),('sm67.020930_0805.162.3112','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm67.020930_0805.162.fits'),('sm67.021002_0805.054.2295','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm67.021002_0805.054.fits'),('sm67.021004_0817.077.1622','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm67.021004_0817.077.fits'),('sm67.021008_0829.071.224','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm67.021008_0829.071.fits'),('sm67.021012_0728.128.2719','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm67.021012_0728.128.fits'),('sm67.021014_0910.118.2127','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm67.021014_0910.118.fits'),('sm67.021030_0810.114.1574','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm67.021030_0810.114.fits'),('sm67.021101_0741.121.1044','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm67.021101_0741.121.fits'),('sm67.021105_0809.109.336','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm67.021105_0809.109.fits'),('sm67.021109_0840.095.3647','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm67.021109_0840.095.fits'),('sm67.021111_0813.134.3178','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm67.021111_0813.134.fits'),('sm67.021113_0824.125.2547','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm67.021113_0824.125.fits'),('sm67.021115_0758.124.1994','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm67.021115_0758.124.fits'),('sm67.021128_0815.107.1445','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm67.021128_0815.107.fits'),('sm67.021202_0820.133.683','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm67.021202_0820.133.fits'),('sm67.021204_0831.068.182','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm67.021204_0831.068.fits'),('sm67.021206_0814.124.3156','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm67.021206_0814.124.fits'),('sm67.021210_0834.113.2385','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm67.021210_0834.113.fits'),('sm67.021212_0814.134.2192','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm67.021212_0814.134.fits'),('sm67.021214_0637.112.1833','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm67.021214_0637.112.fits'),('sm67.021216_0723.094.1286','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm67.021216_0723.094.fits'),('sm67.021229_0749.067.780','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm67.021229_0749.067.fits'),('sm67.030103_0748.110.250','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm67.030103_0748.110.fits'),('sm68.021002_0932.084.2299','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm68.021002_0932.084.fits'),('sm68.021006_0931.138.972','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm68.021006_0931.138.fits'),('sm68.021105_0806.108.340','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm68.021105_0806.108.fits'),('sm68.021109_0836.094.3649','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm68.021109_0836.094.fits'),('sm68.021111_0809.133.3180','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm68.021111_0809.133.fits'),('sm68.021113_0821.124.2551','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm68.021113_0821.124.fits'),('sm68.021115_0754.123.1998','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm68.021115_0754.123.fits'),('sm68.021128_0811.106.1449','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm68.021128_0811.106.fits'),('sm68.021202_0817.132.687','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm68.021202_0817.132.fits'),('sm68.021204_0827.067.186','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm68.021204_0827.067.fits'),('sm68.021206_0811.123.3160','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm68.021206_0811.123.fits'),('sm68.021210_0831.112.2389','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm68.021210_0831.112.fits'),('sm68.021212_0810.133.2196','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm68.021212_0810.133.fits'),('sm68.021214_0633.111.1837','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm68.021214_0633.111.fits'),('sm68.021216_0720.093.1290','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm68.021216_0720.093.fits'),('sm68.021229_0745.066.784','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm68.021229_0745.066.fits'),('sm68.030103_0745.109.254','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm68.030103_0745.109.fits'),('sm69.021002_0930.083.2303','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm69.021002_0930.083.fits'),('sm69.021006_0929.137.976','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm69.021006_0929.137.fits'),('sm69.021105_0801.107.344','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm69.021105_0801.107.fits'),('sm69.021109_0830.093.3651','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm69.021109_0830.093.fits'),('sm69.021111_0804.132.3184','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm69.021111_0804.132.fits'),('sm69.021113_0818.123.2555','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm69.021113_0818.123.fits'),('sm69.021115_0751.122.2002','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm69.021115_0751.122.fits'),('sm69.021128_0808.105.1453','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm69.021128_0808.105.fits'),('sm69.021202_0813.131.691','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm69.021202_0813.131.fits'),('sm69.021204_0825.066.190','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm69.021204_0825.066.fits'),('sm69.021206_0808.122.3164','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm69.021206_0808.122.fits'),('sm69.021210_0828.111.2393','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm69.021210_0828.111.fits'),('sm69.021212_0806.132.2200','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm69.021212_0806.132.fits'),('sm69.021214_0630.110.1842','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm69.021214_0630.110.fits'),('sm69.021216_0717.092.1294','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm69.021216_0717.092.fits'),('sm69.021229_0743.065.788','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm69.021229_0743.065.fits'),('sm69.030103_0741.108.258','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm69.030103_0741.108.fits'),('sm73.021002_0928.082.2307','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm73.021002_0928.082.fits'),('sm73.021006_0927.136.980','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm73.021006_0927.136.fits'),('sm73.021105_0757.106.348','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm73.021105_0757.106.fits'),('sm73.021109_0826.092.3653','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm73.021109_0826.092.fits'),('sm73.021111_0800.131.3188','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm73.021111_0800.131.fits'),('sm73.021113_0812.122.2559','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm73.021113_0812.122.fits'),('sm73.021115_0747.121.2006','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm73.021115_0747.121.fits'),('sm73.021128_0804.104.1459','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm73.021128_0804.104.fits'),('sm73.021202_0809.130.695','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm73.021202_0809.130.fits'),('sm73.021204_0821.065.194','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm73.021204_0821.065.fits'),('sm73.021206_0805.121.3168','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm73.021206_0805.121.fits'),('sm73.021210_0824.110.2397','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm73.021210_0824.110.fits'),('sm73.021212_0801.131.2204','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm73.021212_0801.131.fits'),('sm73.021214_0626.109.1845','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm73.021214_0626.109.fits'),('sm73.021216_0713.091.1298','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm73.021216_0713.091.fits'),('sm73.021229_0738.064.792','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm73.021229_0738.064.fits'),('sm73.030103_0737.107.262','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm73.030103_0737.107.fits'),('sm74.020928_0813.117.3706','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm74.020928_0813.117.fits'),('sm74.020930_0800.161.3115','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm74.020930_0800.161.fits'),('sm74.021002_0800.053.2311','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm74.021002_0800.053.fits'),('sm74.021004_0812.076.1627','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm74.021004_0812.076.fits'),('sm74.021006_0749.126.984','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm74.021006_0749.126.fits'),('sm74.021008_0824.070.229','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm74.021008_0824.070.fits'),('sm74.021010_0928.123.3303','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm74.021010_0928.123.fits'),('sm74.021012_0722.127.2724','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm74.021012_0722.127.fits'),('sm74.021014_0904.117.2130','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm74.021014_0904.117.fits'),('sm74.021030_0754.113.1579','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm74.021030_0754.113.fits'),('sm74.021101_0737.120.1047','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm74.021101_0737.120.fits'),('sm74.021105_0752.105.353','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm74.021105_0752.105.fits'),('sm74.021109_0822.091.3655','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm74.021109_0822.091.fits'),('sm74.021111_0756.130.3192','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm74.021111_0756.130.fits'),('sm74.021113_0808.121.2563','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm74.021113_0808.121.fits'),('sm74.021115_0743.120.2010','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm74.021115_0743.120.fits'),('sm74.021128_0759.103.1462','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm74.021128_0759.103.fits'),('sm74.021202_0804.129.699','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm74.021202_0804.129.fits'),('sm74.021204_0816.064.198','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm74.021204_0816.064.fits'),('sm74.021206_0800.120.3172','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm74.021206_0800.120.fits'),('sm74.021210_0820.109.2401','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm74.021210_0820.109.fits'),('sm74.021212_0755.130.2208','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm74.021212_0755.130.fits'),('sm74.021214_0622.108.1850','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm74.021214_0622.108.fits'),('sm74.021216_0709.090.1302','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm74.021216_0709.090.fits'),('sm74.021229_0721.109.796','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm74.021229_0721.109.fits'),('sm74.021229_0734.063.800','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm74.021229_0734.063.fits'),('sm74.030103_0733.106.266','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm74.030103_0733.106.fits'),('sm75.020928_0808.116.3708','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm75.020928_0808.116.fits'),('sm75.020930_0754.160.3120','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm75.020930_0754.160.fits'),('sm75.021002_0753.052.2315','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm75.021002_0753.052.fits'),('sm75.021004_0807.075.1631','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm75.021004_0807.075.fits'),('sm75.021006_0744.125.988','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm75.021006_0744.125.fits'),('sm75.021008_0819.069.233','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm75.021008_0819.069.fits'),('sm75.021010_0923.122.3306','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm75.021010_0923.122.fits'),('sm75.021012_0717.126.2728','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm75.021012_0717.126.fits'),('sm75.021014_0900.116.2135','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm75.021014_0900.116.fits'),('sm75.021030_0749.112.1583','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm75.021030_0749.112.fits'),('sm75.021101_0733.119.1051','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm75.021101_0733.119.fits'),('sm75.021105_0747.104.357','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm75.021105_0747.104.fits'),('sm75.021109_0817.090.3657','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm75.021109_0817.090.fits'),('sm75.021111_0751.129.3196','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm75.021111_0751.129.fits'),('sm75.021113_0803.120.2567','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm75.021113_0803.120.fits'),('sm75.021115_0737.119.2014','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm75.021115_0737.119.fits'),('sm75.021128_0754.102.1464','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm75.021128_0754.102.fits'),('sm75.021202_0759.128.702','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm75.021202_0759.128.fits'),('sm75.021204_0811.063.202','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm75.021204_0811.063.fits'),('sm75.021206_0756.119.3177','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm75.021206_0756.119.fits'),('sm75.021210_0815.108.2405','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm75.021210_0815.108.fits'),('sm75.021212_0749.129.2212','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm75.021212_0749.129.fits'),('sm75.021214_0617.107.1854','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm75.021214_0617.107.fits'),('sm75.021216_0704.089.1306','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm75.021216_0704.089.fits'),('sm75.021229_0716.108.804','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm75.021229_0716.108.fits'),('sm75.030103_0728.105.271','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm75.030103_0728.105.fits'),('sm76.020928_0803.115.3710','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm76.020928_0803.115.fits'),('sm76.020930_0749.159.3123','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm76.020930_0749.159.fits'),('sm76.021002_0748.051.2319','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm76.021002_0748.051.fits'),('sm76.021004_0802.074.1636','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm76.021004_0802.074.fits'),('sm76.021006_0738.124.992','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm76.021006_0738.124.fits'),('sm76.021008_0814.068.237','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm76.021008_0814.068.fits'),('sm76.021010_0918.121.3309','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm76.021010_0918.121.fits'),('sm76.021012_0712.125.2732','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm76.021012_0712.125.fits'),('sm76.021014_0844.133.2138','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm76.021014_0844.133.fits'),('sm76.021030_0744.111.1587','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm76.021030_0744.111.fits'),('sm76.021101_0729.118.1055','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm76.021101_0729.118.fits'),('sm76.021105_0742.103.361','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm76.021105_0742.103.fits'),('sm76.021109_0812.089.3659','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm76.021109_0812.089.fits'),('sm76.021111_0741.128.3200','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm76.021111_0741.128.fits'),('sm76.021113_0757.119.2571','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm76.021113_0757.119.fits'),('sm76.021115_0732.118.2018','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm76.021115_0732.118.fits'),('sm76.021128_0749.101.1468','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm76.021128_0749.101.fits'),('sm76.021202_0753.127.706','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm76.021202_0753.127.fits'),('sm76.021204_0806.062.206','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm76.021204_0806.062.fits'),('sm76.021206_0752.118.3182','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm76.021206_0752.118.fits'),('sm76.021210_0810.107.2409','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm76.021210_0810.107.fits'),('sm76.021212_0743.128.2216','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm76.021212_0743.128.fits'),('sm76.021214_0612.106.1858','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm76.021214_0612.106.fits'),('sm76.021216_0659.088.1310','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm76.021216_0659.088.fits'),('sm76.021229_0711.107.808','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm76.021229_0711.107.fits'),('sm76.030103_0722.104.275','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm76.030103_0722.104.fits'),('sm77.020928_0758.114.3712','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm77.020928_0758.114.fits'),('sm77.020930_0743.158.3127','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm77.020930_0743.158.fits'),('sm77.021002_0742.050.2323','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm77.021002_0742.050.fits'),('sm77.021004_0757.073.1640','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm77.021004_0757.073.fits'),('sm77.021006_0733.123.996','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm77.021006_0733.123.fits'),('sm77.021008_0809.067.240','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm77.021008_0809.067.fits'),('sm77.021010_0912.120.3314','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm77.021010_0912.120.fits'),('sm77.021012_0703.124.2736','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm77.021012_0703.124.fits'),('sm77.021014_0839.132.2142','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm77.021014_0839.132.fits'),('sm77.021030_0738.110.1591','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm77.021030_0738.110.fits'),('sm77.021101_0725.117.1059','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm77.021101_0725.117.fits'),('sm77.021105_0737.102.365','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm77.021105_0737.102.fits'),('sm77.021109_0807.088.3661','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm77.021109_0807.088.fits'),('sm77.021111_0736.127.3205','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm77.021111_0736.127.fits'),('sm77.021113_0752.118.2575','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm77.021113_0752.118.fits'),('sm77.021115_0727.117.2022','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm77.021115_0727.117.fits'),('sm77.021128_0744.100.1472','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm77.021128_0744.100.fits'),('sm77.021202_0748.126.710','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm77.021202_0748.126.fits'),('sm77.021204_0801.061.210','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm77.021204_0801.061.fits'),('sm77.021206_0748.117.3187','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm77.021206_0748.117.fits'),('sm77.021210_0805.106.2413','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm77.021210_0805.106.fits'),('sm77.021212_0736.127.2220','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/sm77.021212_0736.127.fits'),('sm77.021214_0607.105.1862','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm77.021214_0607.105.fits'),('sm77.021216_0654.087.1314','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm77.021216_0654.087.fits'),('sm77.021229_0705.106.812','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm77.021229_0705.106.fits'),('sm77.030103_0716.103.279','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm77.030103_0716.103.fits'),('sm78.020928_0754.113.3714','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm78.020928_0754.113.fits'),('sm78.020930_0739.157.3131','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm78.020930_0739.157.fits'),('sm78.021002_0738.049.2327','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm78.021002_0738.049.fits'),('sm78.021004_0752.072.1644','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm78.021004_0752.072.fits'),('sm78.021006_0726.122.999','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm78.021006_0726.122.fits'),('sm78.021008_0805.066.244','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm78.021008_0805.066.fits'),('sm78.021010_0908.119.3317','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm78.021010_0908.119.fits'),('sm78.021012_0659.123.2739','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm78.021012_0659.123.fits'),('sm78.021014_0835.131.2146','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm78.021014_0835.131.fits'),('sm78.021030_0734.109.1595','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm78.021030_0734.109.fits'),('sm78.021101_0722.116.1063','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm78.021101_0722.116.fits'),('sm78.021105_0734.101.369','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm78.021105_0734.101.fits'),('sm78.021109_0803.087.3663','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm78.021109_0803.087.fits'),('sm78.021111_0733.126.3209','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm78.021111_0733.126.fits'),('sm78.021113_0749.117.2579','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm78.021113_0749.117.fits'),('sm78.021115_0723.116.2026','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm78.021115_0723.116.fits'),('sm78.021128_0741.099.1476','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm78.021128_0741.099.fits'),('sm78.021202_0744.125.714','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm78.021202_0744.125.fits'),('sm78.021204_0758.060.214','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm78.021204_0758.060.fits'),('sm78.021206_0745.116.3191','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm78.021206_0745.116.fits'),('sm78.021210_0802.105.2417','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm78.021210_0802.105.fits'),('sm78.021214_0604.104.1866','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm78.021214_0604.104.fits'),('sm78.021216_0651.086.1318','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm78.021216_0651.086.fits'),('sm78.021229_0702.105.816','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm78.021229_0702.105.fits'),('sm78.030103_0713.102.283','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm78.030103_0713.102.fits'),('sm79.021002_0926.081.2331','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm79.021002_0926.081.fits'),('sm79.021004_0937.104.1647','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm79.021004_0937.104.fits'),('sm79.021006_0925.135.1003','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm79.021006_0925.135.fits'),('sm79.021105_0731.100.373','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm79.021105_0731.100.fits'),('sm79.021109_0800.086.3665','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm79.021109_0800.086.fits'),('sm79.021111_0730.125.3213','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm79.021111_0730.125.fits'),('sm79.021113_0746.116.2583','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm79.021113_0746.116.fits'),('sm79.021115_0720.115.2030','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm79.021115_0720.115.fits'),('sm79.021128_0738.098.1480','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm79.021128_0738.098.fits'),('sm79.021202_0741.124.718','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm79.021202_0741.124.fits'),('sm79.021204_0755.059.218','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm79.021204_0755.059.fits'),('sm79.021206_0742.115.3195','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm79.021206_0742.115.fits'),('sm79.021210_0758.104.2421','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm79.021210_0758.104.fits'),('sm79.021214_0601.103.1870','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm79.021214_0601.103.fits'),('sm79.021216_0648.085.1321','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm79.021216_0648.085.fits'),('sm79.021229_0659.104.820','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm79.021229_0659.104.fits'),('sm79.030103_0709.101.287','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm79.030103_0709.101.fits'),('sm83.021002_0924.080.2335','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm83.021002_0924.080.fits'),('sm83.021004_0935.103.1651','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm83.021004_0935.103.fits'),('sm83.021006_0923.134.1007','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm83.021006_0923.134.fits'),('sm83.021105_0728.099.377','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm83.021105_0728.099.fits'),('sm83.021109_0757.085.3667','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm83.021109_0757.085.fits'),('sm83.021111_0726.124.3218','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm83.021111_0726.124.fits'),('sm83.021113_0742.115.2588','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm83.021113_0742.115.fits'),('sm83.021115_0716.114.2034','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm83.021115_0716.114.fits'),('sm83.021128_0734.097.1484','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm83.021128_0734.097.fits'),('sm83.021202_0738.123.722','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm83.021202_0738.123.fits'),('sm83.021204_0752.058.222','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm83.021204_0752.058.fits'),('sm83.021206_0738.114.3199','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm83.021206_0738.114.fits'),('sm83.021210_0754.103.2425','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm83.021210_0754.103.fits'),('sm83.021214_0557.102.1874','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm83.021214_0557.102.fits'),('sm83.021216_0644.084.1324','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm83.021216_0644.084.fits'),('sm83.021229_0655.103.824','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm83.021229_0655.103.fits'),('sm83.030103_0705.100.291','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm83.030103_0705.100.fits'),('sm84.020928_0749.112.3716','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm84.020928_0749.112.fits'),('sm84.020930_0734.156.3135','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm84.020930_0734.156.fits'),('sm84.021002_0733.048.2338','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm84.021002_0733.048.fits'),('sm84.021004_0748.071.1655','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm84.021004_0748.071.fits'),('sm84.021006_0722.121.1011','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm84.021006_0722.121.fits'),('sm84.021008_0800.065.248','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm84.021008_0800.065.fits'),('sm84.021010_0904.118.3322','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm84.021010_0904.118.fits'),('sm84.021012_0654.122.2745','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm84.021012_0654.122.fits'),('sm84.021014_0829.130.2150','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm84.021014_0829.130.fits'),('sm84.021030_0729.108.1599','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm84.021030_0729.108.fits'),('sm84.021101_0718.115.1067','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm84.021101_0718.115.fits'),('sm84.021105_0724.098.381','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm84.021105_0724.098.fits'),('sm84.021109_0753.084.3669','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm84.021109_0753.084.fits'),('sm84.021111_0722.123.3221','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm84.021111_0722.123.fits'),('sm84.021113_0738.114.2592','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm84.021113_0738.114.fits'),('sm84.021115_0712.113.2039','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm84.021115_0712.113.fits'),('sm84.021128_0730.096.1488','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm84.021128_0730.096.fits'),('sm84.021202_0734.122.726','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm84.021202_0734.122.fits'),('sm84.021204_0731.098.226','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm84.021204_0731.098.fits'),('sm84.021206_0735.113.3202','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm84.021206_0735.113.fits'),('sm84.021210_0750.102.2428','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm84.021210_0750.102.fits'),('sm84.021214_0554.101.1878','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm84.021214_0554.101.fits'),('sm84.021216_0641.083.1328','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm84.021216_0641.083.fits'),('sm84.021229_0651.102.828','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm84.021229_0651.102.fits'),('sm84.030103_0701.099.295','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm84.030103_0701.099.fits'),('sm85.020928_0744.111.3718','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm85.020928_0744.111.fits'),('sm85.020930_0728.155.3139','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm85.020930_0728.155.fits'),('sm85.021002_0728.047.2342','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm85.021002_0728.047.fits'),('sm85.021004_0743.070.1659','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm85.021004_0743.070.fits'),('sm85.021006_0717.120.1015','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm85.021006_0717.120.fits'),('sm85.021008_0756.064.252','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm85.021008_0756.064.fits'),('sm85.021010_0859.117.3327','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm85.021010_0859.117.fits'),('sm85.021012_0649.121.2748','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm85.021012_0649.121.fits'),('sm85.021014_0824.129.2154','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm85.021014_0824.129.fits'),('sm85.021030_0723.107.1603','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm85.021030_0723.107.fits'),('sm85.021101_0713.114.1071','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm85.021101_0713.114.fits'),('sm85.021105_0719.097.385','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm85.021105_0719.097.fits'),('sm85.021109_0748.083.3671','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm85.021109_0748.083.fits'),('sm85.021111_0717.122.3225','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm85.021111_0717.122.fits'),('sm85.021113_0733.113.2596','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm85.021113_0733.113.fits'),('sm85.021115_0707.112.2043','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm85.021115_0707.112.fits'),('sm85.021128_0725.095.1492','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm85.021128_0725.095.fits'),('sm85.021202_0729.121.730','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm85.021202_0729.121.fits'),('sm85.021204_0726.097.230','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm85.021204_0726.097.fits'),('sm85.021206_0729.112.3204','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm85.021206_0729.112.fits'),('sm85.021210_0745.101.2432','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm85.021210_0745.101.fits'),('sm85.021214_0549.100.1882','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm85.021214_0549.100.fits'),('sm85.021216_0636.082.1332','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm85.021216_0636.082.fits'),('sm85.021229_0646.101.832','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm85.021229_0646.101.fits'),('sm85.030103_0655.098.299','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm85.030103_0655.098.fits'),('sm86.020928_0736.109.3720','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm86.020928_0736.109.fits'),('sm86.020928_0740.110.3722','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm86.020928_0740.110.fits'),('sm86.020930_0720.153.3143','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm86.020930_0720.153.fits'),('sm86.020930_0724.154.3147','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm86.020930_0724.154.fits'),('sm86.021002_0721.045.2346','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm86.021002_0721.045.fits'),('sm86.021002_0724.046.2350','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm86.021002_0724.046.fits'),('sm86.021004_0735.068.1663','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm86.021004_0735.068.fits'),('sm86.021004_0739.069.1666','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm86.021004_0739.069.fits'),('sm86.021006_0709.118.1017','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm86.021006_0709.118.fits'),('sm86.021006_0713.119.1021','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm86.021006_0713.119.fits'),('sm86.021008_0738.062.256','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm86.021008_0738.062.fits'),('sm86.021008_0741.063.260','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm86.021008_0741.063.fits'),('sm86.021010_0851.115.3331','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm86.021010_0851.115.fits'),('sm86.021010_0855.116.3334','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm86.021010_0855.116.fits'),('sm86.021012_0641.119.2751','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm86.021012_0641.119.fits'),('sm86.021012_0645.120.2755','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm86.021012_0645.120.fits'),('sm86.021014_0816.127.2158','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm86.021014_0816.127.fits'),('sm86.021014_0820.128.2161','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm86.021014_0820.128.fits'),('sm86.021030_0716.105.1608','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm86.021030_0716.105.fits'),('sm86.021030_0719.106.1611','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm86.021030_0719.106.fits'),('sm86.021101_0707.112.1075','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm86.021101_0707.112.fits'),('sm86.021101_0710.113.1079','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm86.021101_0710.113.fits'),('sm86.021105_0715.096.389','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm86.021105_0715.096.fits'),('sm86.021109_0744.082.3673','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm86.021109_0744.082.fits'),('sm86.021111_0713.121.3228','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm86.021111_0713.121.fits'),('sm86.021113_0729.112.2600','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm86.021113_0729.112.fits'),('sm86.021115_0703.111.2046','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm86.021115_0703.111.fits'),('sm86.021128_0721.094.1496','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm86.021128_0721.094.fits'),('sm86.021202_0725.120.734','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm86.021202_0725.120.fits'),('sm86.021204_0722.096.234','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm86.021204_0722.096.fits'),('sm86.021206_0725.111.3208','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm86.021206_0725.111.fits'),('sm86.021210_0740.100.2437','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm86.021210_0740.100.fits'),('sm86.021214_0544.099.1886','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm86.021214_0544.099.fits'),('sm86.021216_0632.081.1336','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm86.021216_0632.081.fits'),('sm86.021229_0642.100.836','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm86.021229_0642.100.fits'),('sm86.030103_0651.097.303','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm86.030103_0651.097.fits'),('sm87.020928_0728.107.3724','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm87.020928_0728.107.fits'),('sm87.020928_0732.108.3726','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm87.020928_0732.108.fits'),('sm87.020930_0712.151.3151','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm87.020930_0712.151.fits'),('sm87.020930_0716.152.3155','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm87.020930_0716.152.fits'),('sm87.021002_0713.043.2354','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm87.021002_0713.043.fits'),('sm87.021002_0717.044.2358','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm87.021002_0717.044.fits'),('sm87.021004_0728.066.1670','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm87.021004_0728.066.fits'),('sm87.021004_0732.067.1674','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm87.021004_0732.067.fits'),('sm87.021006_0702.116.1026','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm87.021006_0702.116.fits'),('sm87.021006_0706.117.1030','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm87.021006_0706.117.fits'),('sm87.021008_0722.074.264','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm87.021008_0722.074.fits'),('sm87.021008_0726.075.268','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm87.021008_0726.075.fits'),('sm87.021010_0844.113.3338','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm87.021010_0844.113.fits'),('sm87.021010_0847.114.3342','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm87.021010_0847.114.fits'),('sm87.021012_0634.117.2759','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm87.021012_0634.117.fits'),('sm87.021012_0638.118.2763','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm87.021012_0638.118.fits'),('sm87.021014_0809.125.2165','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm87.021014_0809.125.fits'),('sm87.021014_0813.126.2169','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm87.021014_0813.126.fits'),('sm87.021030_0708.103.1616','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm87.021030_0708.103.fits'),('sm87.021030_0712.104.1620','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm87.021030_0712.104.fits'),('sm87.021101_0659.110.1083','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm87.021101_0659.110.fits'),('sm87.021101_0703.111.1087','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm87.021101_0703.111.fits'),('sm87.021105_0711.095.393','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm87.021105_0711.095.fits'),('sm87.021109_0739.081.3675','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm87.021109_0739.081.fits'),('sm87.021111_0707.120.3232','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm87.021111_0707.120.fits'),('sm87.021113_0725.111.2604','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm87.021113_0725.111.fits'),('sm87.021115_0658.110.2050','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm87.021115_0658.110.fits'),('sm87.021128_0716.093.1500','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm87.021128_0716.093.fits'),('sm87.021202_0720.119.738','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm87.021202_0720.119.fits'),('sm87.021204_0718.095.238','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm87.021204_0718.095.fits'),('sm87.021206_0720.110.3212','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm87.021206_0720.110.fits'),('sm87.021210_0735.099.2441','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm87.021210_0735.099.fits'),('sm87.021214_0540.098.1890','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm87.021214_0540.098.fits'),('sm87.021216_0627.080.1340','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm87.021216_0627.080.fits'),('sm87.021229_0638.099.840','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm87.021229_0638.099.fits'),('sm87.030103_0646.096.307','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm87.030103_0646.096.fits'),('sm88.020928_0704.134.3728','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm88.020928_0704.134.fits'),('sm88.020928_0723.106.3730','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm88.020928_0723.106.fits'),('sm88.020930_0707.150.3159','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm88.020930_0707.150.fits'),('sm88.021002_0708.042.2362','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm88.021002_0708.042.fits'),('sm88.021004_0723.065.1678','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm88.021004_0723.065.fits'),('sm88.021006_0657.115.1035','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm88.021006_0657.115.fits'),('sm88.021008_0717.073.272','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm88.021008_0717.073.fits'),('sm88.021010_0838.112.3346','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm88.021010_0838.112.fits'),('sm88.021012_0628.116.2767','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm88.021012_0628.116.fits'),('sm88.021014_0804.124.2174','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm88.021014_0804.124.fits'),('sm88.021030_0702.102.1624','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm88.021030_0702.102.fits'),('sm88.021101_0655.109.1091','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm88.021101_0655.109.fits'),('sm88.021105_0706.094.397','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm88.021105_0706.094.fits'),('sm88.021109_0735.080.3677','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm88.021109_0735.080.fits'),('sm88.021111_0703.119.3236','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm88.021111_0703.119.fits'),('sm88.021113_0720.110.2608','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm88.021113_0720.110.fits'),('sm88.021115_0654.109.2054','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm88.021115_0654.109.fits'),('sm88.021128_0712.092.1504','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm88.021128_0712.092.fits'),('sm88.021202_0716.118.742','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm88.021202_0716.118.fits'),('sm88.021204_0714.094.242','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm88.021204_0714.094.fits'),('sm88.021206_0716.109.3216','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm88.021206_0716.109.fits'),('sm88.021210_0730.098.2445','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm88.021210_0730.098.fits'),('sm88.021214_0535.097.1894','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm88.021214_0535.097.fits'),('sm88.021216_0623.079.1344','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm88.021216_0623.079.fits'),('sm88.021229_0633.098.843','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm88.021229_0633.098.fits'),('sm88.030103_0642.095.311','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm88.030103_0642.095.fits'),('sm89.021002_0922.079.2366','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm89.021002_0922.079.fits'),('sm89.021004_0933.102.1682','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm89.021004_0933.102.fits'),('sm89.021006_0921.133.1039','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm89.021006_0921.133.fits'),('sm89.021012_0912.096.2771','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm89.021012_0912.096.fits'),('sm89.021105_0704.093.401','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm89.021105_0704.093.fits'),('sm89.021109_0732.079.3679','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm89.021109_0732.079.fits'),('sm89.021111_0701.118.3240','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm89.021111_0701.118.fits'),('sm89.021113_0718.109.2612','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm89.021113_0718.109.fits'),('sm89.021115_0652.108.2058','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm89.021115_0652.108.fits'),('sm89.021128_0710.091.1508','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm89.021128_0710.091.fits'),('sm89.021202_0714.117.746','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm89.021202_0714.117.fits'),('sm89.021204_0712.093.246','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm89.021204_0712.093.fits'),('sm89.021206_0714.108.3220','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm89.021206_0714.108.fits'),('sm89.021210_0727.097.2449','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm89.021210_0727.097.fits'),('sm89.021214_0533.096.1898','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm89.021214_0533.096.fits'),('sm89.021216_0621.078.1349','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm89.021216_0621.078.fits'),('sm89.021229_0631.097.847','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm89.021229_0631.097.fits'),('sm89.030103_0640.094.315','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm89.030103_0640.094.fits'),('sm8a.021002_0920.078.2370','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm8a.021002_0920.078.fits'),('sm8a.021004_0931.101.1686','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm8a.021004_0931.101.fits'),('sm8a.021006_0919.132.1043','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm8a.021006_0919.132.fits'),('sm8a.021012_0910.095.2776','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm8a.021012_0910.095.fits'),('sm8a.021105_0702.092.405','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm8a.021105_0702.092.fits'),('sm8a.021109_0729.078.3681','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm8a.021109_0729.078.fits'),('sm8a.021111_0658.117.3244','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm8a.021111_0658.117.fits'),('sm8a.021113_0715.108.2616','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm8a.021113_0715.108.fits'),('sm8a.021115_0649.107.2062','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm8a.021115_0649.107.fits'),('sm8a.021128_0707.090.1512','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm8a.021128_0707.090.fits'),('sm8a.021202_0711.116.751','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm8a.021202_0711.116.fits'),('sm8a.021204_0709.092.251','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm8a.021204_0709.092.fits'),('sm8a.021206_0711.107.3224','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm8a.021206_0711.107.fits'),('sm8a.021210_0724.096.2453','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm8a.021210_0724.096.fits'),('sm8a.021214_0531.095.1902','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm8a.021214_0531.095.fits'),('sm8a.021216_0618.077.1353','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm8a.021216_0618.077.fits'),('sm8a.021229_0628.096.852','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm8a.021229_0628.096.fits'),('sm8a.030103_0637.093.319','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm8a.030103_0637.093.fits'),('sm93.020928_0928.139.3733','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm93.020928_0928.139.fits'),('sm93.020930_0925.183.3163','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm93.020930_0925.183.fits'),('sm93.021002_0913.075.2374','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm93.021002_0913.075.fits'),('sm93.021004_0924.098.1690','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm93.021004_0924.098.fits'),('sm93.021006_0912.129.1048','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm93.021006_0912.129.fits'),('sm93.021012_0902.092.2781','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm93.021012_0902.092.fits'),('sm93.021101_0847.142.1095','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm93.021101_0847.142.fits'),('sm93.021105_0631.084.409','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm93.021105_0631.084.fits'),('sm93.021109_0643.104.3683','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm93.021109_0643.104.fits'),('sm93.021111_0625.109.3248','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm93.021111_0625.109.fits'),('sm93.021113_0643.100.2620','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm93.021113_0643.100.fits'),('sm93.021115_0617.099.2066','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm93.021115_0617.099.fits'),('sm93.021128_0630.082.1516','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm93.021128_0630.082.fits'),('sm93.021202_0640.108.755','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm93.021202_0640.108.fits'),('sm93.021204_0638.084.255','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm93.021204_0638.084.fits'),('sm93.021206_0636.099.3230','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm93.021206_0636.099.fits'),('sm93.021210_0651.088.2457','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm93.021210_0651.088.fits'),('sm93.021214_0459.087.1906','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm93.021214_0459.087.fits'),('sm93.021216_0547.069.1357','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm93.021216_0547.069.fits'),('sm93.021229_0558.088.856','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm93.021229_0558.088.fits'),('sm93.030103_0604.085.323','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm93.030103_0604.085.fits'),('sm94.020928_0931.140.3735','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm94.020928_0931.140.fits'),('sm94.021002_0915.076.2378','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm94.021002_0915.076.fits'),('sm94.021004_0926.099.1694','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm94.021004_0926.099.fits'),('sm94.021006_0914.130.1053','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm94.021006_0914.130.fits'),('sm94.021012_0904.093.2785','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm94.021012_0904.093.fits'),('sm94.021101_0850.143.1099','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm94.021101_0850.143.fits'),('sm94.021105_0634.085.413','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm94.021105_0634.085.fits'),('sm94.021109_0646.105.3685','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm94.021109_0646.105.fits'),('sm94.021111_0629.110.3252','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm94.021111_0629.110.fits'),('sm94.021113_0646.101.2623','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm94.021113_0646.101.fits'),('sm94.021115_0620.100.2070','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm94.021115_0620.100.fits'),('sm94.021128_0635.083.1520','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm94.021128_0635.083.fits'),('sm94.021202_0643.109.759','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm94.021202_0643.109.fits'),('sm94.021204_0641.085.259','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm94.021204_0641.085.fits'),('sm94.021206_0639.100.3234','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm94.021206_0639.100.fits'),('sm94.021210_0654.089.2461','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm94.021210_0654.089.fits'),('sm94.021214_0502.088.1910','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm94.021214_0502.088.fits'),('sm94.021216_0550.070.1361','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm94.021216_0550.070.fits'),('sm94.021229_0600.089.860','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm94.021229_0600.089.fits'),('sm94.030103_0607.086.326','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm94.030103_0607.086.fits'),('sm95.020928_0632.127.3737','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm95.020928_0632.127.fits'),('sm95.020930_0631.143.3167','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm95.020930_0631.143.fits'),('sm95.021002_0618.059.2382','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm95.021002_0618.059.fits'),('sm95.021004_0648.058.1698','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm95.021004_0648.058.fits'),('sm95.021006_0620.134.1057','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm95.021006_0620.134.fits'),('sm95.021008_0644.066.276','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm95.021008_0644.066.fits'),('sm95.021010_0805.105.3350','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm95.021010_0805.105.fits'),('sm95.021012_0553.109.2789','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm95.021012_0553.109.fits'),('sm95.021014_0731.117.2178','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm95.021014_0731.117.fits'),('sm95.021030_0622.095.1628','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm95.021030_0622.095.fits'),('sm95.021101_0628.102.1103','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm95.021101_0628.102.fits'),('sm95.021105_0638.086.417','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm95.021105_0638.086.fits'),('sm95.021109_0650.106.3687','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm95.021109_0650.106.fits'),('sm95.021111_0633.111.3256','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm95.021111_0633.111.fits'),('sm95.021113_0650.102.2627','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm95.021113_0650.102.fits'),('sm95.021115_0624.101.2074','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm95.021115_0624.101.fits'),('sm95.021128_0640.084.1524','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm95.021128_0640.084.fits'),('sm95.021202_0647.110.763','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm95.021202_0647.110.fits'),('sm95.021204_0645.086.263','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm95.021204_0645.086.fits'),('sm95.021206_0643.101.3238','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm95.021206_0643.101.fits'),('sm95.021210_0658.090.2465','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm95.021210_0658.090.fits'),('sm95.021214_0505.089.1914','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm95.021214_0505.089.fits'),('sm95.021216_0554.071.1365','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm95.021216_0554.071.fits'),('sm95.021229_0604.090.864','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm95.021229_0604.090.fits'),('sm95.030103_0611.087.330','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm95.030103_0611.087.fits'),('sm96.020928_0637.128.3739','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm96.020928_0637.128.fits'),('sm96.020928_0642.129.3741','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm96.020928_0642.129.fits'),('sm96.020930_0637.144.3169','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm96.020930_0637.144.fits'),('sm96.020930_0642.145.3174','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm96.020930_0642.145.fits'),('sm96.021002_0638.038.2386','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm96.021002_0638.038.fits'),('sm96.021002_0643.039.2390','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm96.021002_0643.039.fits'),('sm96.021004_0655.059.1702','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm96.021004_0655.059.fits'),('sm96.021004_0700.060.1706','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm96.021004_0700.060.fits'),('sm96.021006_0625.135.1061','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm96.021006_0625.135.fits'),('sm96.021006_0630.136.1065','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm96.021006_0630.136.fits'),('sm96.021008_0649.067.280','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm96.021008_0649.067.fits'),('sm96.021008_0655.068.285','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm96.021008_0655.068.fits'),('sm96.021010_0810.106.3354','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm96.021010_0810.106.fits'),('sm96.021010_0815.107.3358','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm96.021010_0815.107.fits'),('sm96.021012_0558.110.2793','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm96.021012_0558.110.fits'),('sm96.021012_0604.111.2796','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm96.021012_0604.111.fits'),('sm96.021014_0736.118.2182','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm96.021014_0736.118.fits'),('sm96.021014_0741.119.2186','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm96.021014_0741.119.fits'),('sm96.021030_0628.096.1632','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm96.021030_0628.096.fits'),('sm96.021030_0633.097.1634','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm96.021030_0633.097.fits'),('sm96.021101_0632.103.1107','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm96.021101_0632.103.fits'),('sm96.021101_0636.104.1111','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm96.021101_0636.104.fits'),('sm96.021105_0642.087.421','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm96.021105_0642.087.fits'),('sm96.021109_0654.107.3689','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm96.021109_0654.107.fits'),('sm96.021111_0638.112.3260','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm96.021111_0638.112.fits'),('sm96.021113_0654.103.2631','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm96.021113_0654.103.fits'),('sm96.021115_0628.102.2078','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm96.021115_0628.102.fits'),('sm96.021128_0645.085.1528','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm96.021128_0645.085.fits'),('sm96.021202_0651.111.767','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm96.021202_0651.111.fits'),('sm96.021204_0649.087.267','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm96.021204_0649.087.fits'),('sm96.021206_0648.102.3243','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm96.021206_0648.102.fits'),('sm96.021210_0702.091.2469','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm96.021210_0702.091.fits'),('sm96.021214_0510.090.1918','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm96.021214_0510.090.fits'),('sm96.021216_0558.072.1369','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm96.021216_0558.072.fits'),('sm96.021229_0608.091.866','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm96.021229_0608.091.fits'),('sm96.030103_0616.088.334','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm96.030103_0616.088.fits'),('sm97.020928_0647.130.3743','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm97.020928_0647.130.fits'),('sm97.020928_0650.131.3745','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm97.020928_0650.131.fits'),('sm97.020930_0648.146.3176','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm97.020930_0648.146.fits'),('sm97.020930_0652.147.3181','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm97.020930_0652.147.fits'),('sm97.021002_0651.038.2394','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm97.021002_0651.038.fits'),('sm97.021002_0655.039.2398','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm97.021002_0655.039.fits'),('sm97.021004_0705.061.1712','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm97.021004_0705.061.fits'),('sm97.021004_0709.062.1715','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm97.021004_0709.062.fits'),('sm97.021006_0636.137.1069','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm97.021006_0636.137.fits'),('sm97.021008_0700.069.289','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm97.021008_0700.069.fits'),('sm97.021008_0703.070.293','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm97.021008_0703.070.fits'),('sm97.021010_0820.108.3362','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm97.021010_0820.108.fits'),('sm97.021010_0824.109.3366','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm97.021010_0824.109.fits'),('sm97.021012_0610.112.2800','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm97.021012_0610.112.fits'),('sm97.021012_0614.113.2804','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm97.021012_0614.113.fits'),('sm97.021014_0746.120.2190','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm97.021014_0746.120.fits'),('sm97.021014_0750.121.2195','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm97.021014_0750.121.fits'),('sm97.021030_0638.098.1637','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm97.021030_0638.098.fits'),('sm97.021030_0642.099.1641','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm97.021030_0642.099.fits'),('sm97.021101_0640.105.1115','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm97.021101_0640.105.fits'),('sm97.021101_0644.106.1119','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm97.021101_0644.106.fits'),('sm97.021105_0647.088.425','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm97.021105_0647.088.fits'),('sm97.021109_0700.108.3691','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm97.021109_0700.108.fits'),('sm97.021111_0643.113.3264','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm97.021111_0643.113.fits'),('sm97.021113_0659.104.2635','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm97.021113_0659.104.fits'),('sm97.021115_0633.103.2082','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm97.021115_0633.103.fits'),('sm97.021128_0650.086.1532','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm97.021128_0650.086.fits'),('sm97.021202_0656.112.771','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm97.021202_0656.112.fits'),('sm97.021204_0654.088.270','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm97.021204_0654.088.fits'),('sm97.021206_0655.103.3247','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm97.021206_0655.103.fits'),('sm97.021210_0708.092.2474','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm97.021210_0708.092.fits'),('sm97.021214_0515.091.1922','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm97.021214_0515.091.fits'),('sm97.021216_0603.073.1373','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm97.021216_0603.073.fits'),('sm97.021229_0613.092.870','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm97.021229_0613.092.fits'),('sm97.030103_0621.089.338','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm97.030103_0621.089.fits'),('sm98.020928_0654.132.3747','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm98.020928_0654.132.fits'),('sm98.020930_0656.148.3185','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm98.020930_0656.148.fits'),('sm98.021002_0658.040.2402','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm98.021002_0658.040.fits'),('sm98.021004_0713.063.1719','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm98.021004_0713.063.fits'),('sm98.021006_0647.113.1073','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm98.021006_0647.113.fits'),('sm98.021008_0707.071.297','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm98.021008_0707.071.fits'),('sm98.021010_0828.110.3370','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm98.021010_0828.110.fits'),('sm98.021012_0617.114.2808','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm98.021012_0617.114.fits'),('sm98.021014_0754.122.2197','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm98.021014_0754.122.fits'),('sm98.021030_0646.100.1645','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm98.021030_0646.100.fits'),('sm98.021101_0647.107.1123','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm98.021101_0647.107.fits'),('sm98.021105_0651.089.428','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm98.021105_0651.089.fits'),('sm98.021109_0704.109.3693','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm98.021109_0704.109.fits'),('sm98.021111_0648.114.3268','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm98.021111_0648.114.fits'),('sm98.021113_0704.105.2639','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm98.021113_0704.105.fits'),('sm98.021115_0638.104.2086','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm98.021115_0638.104.fits'),('sm98.021128_0654.087.1536','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm98.021128_0654.087.fits'),('sm98.021202_0700.113.775','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm98.021202_0700.113.fits'),('sm98.021204_0658.089.274','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm98.021204_0658.089.fits'),('sm98.021206_0659.104.3251','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm98.021206_0659.104.fits'),('sm98.021210_0712.093.2478','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm98.021210_0712.093.fits'),('sm98.021214_0519.092.1926','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm98.021214_0519.092.fits'),('sm98.021216_0607.074.1377','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm98.021216_0607.074.fits'),('sm98.021229_0617.093.874','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm98.021229_0617.093.fits'),('sm98.030103_0625.090.342','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm98.030103_0625.090.fits'),('sm99.020928_0659.133.3748','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm99.020928_0659.133.fits'),('sm99.020930_0701.149.3189','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sm99.020930_0701.149.fits'),('sm99.021002_0703.041.2406','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm99.021002_0703.041.fits'),('sm99.021004_0718.064.1723','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm99.021004_0718.064.fits'),('sm99.021006_0652.114.1077','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm99.021006_0652.114.fits'),('sm99.021008_0712.072.301','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sm99.021008_0712.072.fits'),('sm99.021010_0833.111.3374','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sm99.021010_0833.111.fits'),('sm99.021012_0623.115.2812','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm99.021012_0623.115.fits'),('sm99.021014_0759.123.2201','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sm99.021014_0759.123.fits'),('sm99.021030_0651.101.1649','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sm99.021030_0651.101.fits'),('sm99.021101_0651.108.1127','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm99.021101_0651.108.fits'),('sm99.021105_0659.091.432','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm99.021105_0659.091.fits'),('sm99.021109_0715.111.3695','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm99.021109_0715.111.fits'),('sm99.021111_0655.116.3272','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm99.021111_0655.116.fits'),('sm99.021113_0712.107.2643','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm99.021113_0712.107.fits'),('sm99.021115_0646.106.2090','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm99.021115_0646.106.fits'),('sm99.021128_0703.089.1540','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm99.021128_0703.089.fits'),('sm99.021202_0708.115.779','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm99.021202_0708.115.fits'),('sm99.021204_0706.091.278','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm99.021204_0706.091.fits'),('sm99.021206_0708.106.3255','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm99.021206_0708.106.fits'),('sm99.021210_0721.095.2482','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm99.021210_0721.095.fits'),('sm99.021214_0527.094.1930','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm99.021214_0527.094.fits'),('sm99.021216_0615.076.1381','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm99.021216_0615.076.fits'),('sm99.021229_0625.095.878','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm99.021229_0625.095.fits'),('sm99.030103_0633.092.346','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm99.030103_0633.092.fits'),('sm9a.020928_0933.141.3750','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sm9a.020928_0933.141.fits'),('sm9a.021002_0918.077.2410','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sm9a.021002_0918.077.fits'),('sm9a.021004_0928.100.1727','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sm9a.021004_0928.100.fits'),('sm9a.021006_0916.131.1081','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sm9a.021006_0916.131.fits'),('sm9a.021012_0907.094.2816','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sm9a.021012_0907.094.fits'),('sm9a.021101_0852.144.1131','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sm9a.021101_0852.144.fits'),('sm9a.021105_0656.090.436','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sm9a.021105_0656.090.fits'),('sm9a.021109_0712.110.3697','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sm9a.021109_0712.110.fits'),('sm9a.021111_0653.115.3276','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sm9a.021111_0653.115.fits'),('sm9a.021113_0709.106.2647','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sm9a.021113_0709.106.fits'),('sm9a.021115_0643.105.2094','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sm9a.021115_0643.105.fits'),('sm9a.021128_0700.088.1544','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sm9a.021128_0700.088.fits'),('sm9a.021202_0705.114.783','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sm9a.021202_0705.114.fits'),('sm9a.021204_0703.090.282','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sm9a.021204_0703.090.fits'),('sm9a.021206_0704.105.3259','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sm9a.021206_0704.105.fits'),('sm9a.021210_0718.094.2486','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sm9a.021210_0718.094.fits'),('sm9a.021214_0525.093.1934','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sm9a.021214_0525.093.fits'),('sm9a.021216_0612.075.1385','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sm9a.021216_0612.075.fits'),('sm9a.021229_0623.094.882','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sm9a.021229_0623.094.fits'),('sm9a.030103_0630.091.350','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sm9a.030103_0630.091.fits'),('sma4.020928_0924.137.3752','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sma4.020928_0924.137.fits'),('sma4.020930_0920.181.3193','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sma4.020930_0920.181.fits'),('sma4.021002_0909.073.2414','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sma4.021002_0909.073.fits'),('sma4.021004_0920.096.1731','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sma4.021004_0920.096.fits'),('sma4.021006_0908.127.1085','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sma4.021006_0908.127.fits'),('sma4.021012_0857.090.2820','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sma4.021012_0857.090.fits'),('sma4.021101_0843.140.1135','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sma4.021101_0843.140.fits'),('sma4.021105_0603.077.440','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sma4.021105_0603.077.fits'),('sma4.021109_0613.097.3699','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sma4.021109_0613.097.fits'),('sma4.021111_0556.102.3280','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sma4.021111_0556.102.fits'),('sma4.021113_0614.093.2651','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sma4.021113_0614.093.fits'),('sma4.021115_0547.092.2098','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sma4.021115_0547.092.fits'),('sma4.021128_0601.075.1548','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sma4.021128_0601.075.fits'),('sma4.021202_0611.101.787','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sma4.021202_0611.101.fits'),('sma4.021204_0610.077.286','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sma4.021204_0610.077.fits'),('sma4.021206_0605.092.3263','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sma4.021206_0605.092.fits'),('sma4.021210_0528.071.2490','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sma4.021210_0528.071.fits'),('sma4.021214_0431.080.1938','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sma4.021214_0431.080.fits'),('sma4.021216_0519.062.1389','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sma4.021216_0519.062.fits'),('sma4.021229_0529.081.886','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sma4.021229_0529.081.fits'),('sma4.030103_0534.078.354','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sma4.030103_0534.078.fits'),('sma5.020928_0608.122.3754','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sma5.020928_0608.122.fits'),('sma5.020930_0605.138.3197','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sma5.020930_0605.138.fits'),('sma5.021002_0554.054.2418','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sma5.021002_0554.054.fits'),('sma5.021004_0606.072.1736','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sma5.021004_0606.072.fits'),('sma5.021006_0556.129.810','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sma5.021006_0556.129.fits'),('sma5.021008_0613.078.304','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sma5.021008_0613.078.fits'),('sma5.021010_0732.117.3378','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sma5.021010_0732.117.fits'),('sma5.021012_0526.104.2823','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sma5.021012_0526.104.fits'),('sma5.021014_0525.099.2207','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sma5.021014_0525.099.fits'),('sma5.021030_0557.090.1653','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sma5.021030_0557.090.fits'),('sma5.021101_0607.097.1139','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sma5.021101_0607.097.fits'),('sma5.021105_0606.078.444','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sma5.021105_0606.078.fits'),('sma5.021109_0616.098.3701','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sma5.021109_0616.098.fits'),('sma5.021111_0559.103.3284','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sma5.021111_0559.103.fits'),('sma5.021113_0617.094.2655','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sma5.021113_0617.094.fits'),('sma5.021115_0550.093.2102','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sma5.021115_0550.093.fits'),('sma5.021128_0603.076.1552','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sma5.021128_0603.076.fits'),('sma5.021202_0614.102.791','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sma5.021202_0614.102.fits'),('sma5.021204_0613.078.290','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sma5.021204_0613.078.fits'),('sma5.021206_0608.093.3267','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sma5.021206_0608.093.fits'),('sma5.021210_0534.072.2494','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sma5.021210_0534.072.fits'),('sma5.021214_0434.081.1942','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sma5.021214_0434.081.fits'),('sma5.021216_0522.063.1393','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sma5.021216_0522.063.fits'),('sma5.021229_0532.082.890','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sma5.021229_0532.082.fits'),('sma5.030103_0537.079.358','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sma5.030103_0537.079.fits'),('sma6.020928_0613.123.3756','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sma6.020928_0613.123.fits'),('sma6.020930_0611.139.3203','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sma6.020930_0611.139.fits'),('sma6.021002_0559.055.2422','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sma6.021002_0559.055.fits'),('sma6.021004_0611.073.1740','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sma6.021004_0611.073.fits'),('sma6.021006_0602.130.814','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sma6.021006_0602.130.fits'),('sma6.021008_0627.062.309','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sma6.021008_0627.062.fits'),('sma6.021010_0737.118.3382','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sma6.021010_0737.118.fits'),('sma6.021012_0532.105.2828','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sma6.021012_0532.105.fits'),('sma6.021014_0537.100.2209','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sma6.021014_0537.100.fits'),('sma6.021030_0603.091.1657','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sma6.021030_0603.091.fits'),('sma6.021101_0611.098.1143','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sma6.021101_0611.098.fits'),('sma6.021105_0610.079.447','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sma6.021105_0610.079.fits'),('sma6.021109_0620.099.3703','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sma6.021109_0620.099.fits'),('sma6.021111_0603.104.3288','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sma6.021111_0603.104.fits'),('sma6.021113_0621.095.2659','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sma6.021113_0621.095.fits'),('sma6.021115_0554.094.2106','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sma6.021115_0554.094.fits'),('sma6.021128_0607.077.1556','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sma6.021128_0607.077.fits'),('sma6.021202_0618.103.795','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sma6.021202_0618.103.fits'),('sma6.021204_0617.079.294','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sma6.021204_0617.079.fits'),('sma6.021206_0613.094.3270','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sma6.021206_0613.094.fits'),('sma6.021210_0537.073.2498','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sma6.021210_0537.073.fits'),('sma6.021214_0438.082.1946','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sma6.021214_0438.082.fits'),('sma6.021216_0526.064.1397','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sma6.021216_0526.064.fits'),('sma6.021229_0536.083.894','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sma6.021229_0536.083.fits'),('sma6.030103_0541.080.362','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sma6.030103_0541.080.fits'),('sma7.020928_0618.124.3758','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sma7.020928_0618.124.fits'),('sma7.020930_0616.140.3206','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sma7.020930_0616.140.fits'),('sma7.021002_0604.056.2426','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sma7.021002_0604.056.fits'),('sma7.021004_0616.074.1742','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sma7.021004_0616.074.fits'),('sma7.021006_0607.131.818','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sma7.021006_0607.131.fits'),('sma7.021008_0631.063.313','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sma7.021008_0631.063.fits'),('sma7.021010_0742.119.3386','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sma7.021010_0742.119.fits'),('sma7.021012_0537.106.2831','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sma7.021012_0537.106.fits'),('sma7.021014_0542.101.2214','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sma7.021014_0542.101.fits'),('sma7.021030_0608.092.1661','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sma7.021030_0608.092.fits'),('sma7.021101_0616.099.1147','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sma7.021101_0616.099.fits'),('sma7.021105_0615.080.452','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sma7.021105_0615.080.fits'),('sma7.021109_0626.100.3705','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sma7.021109_0626.100.fits'),('sma7.021111_0609.105.3292','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sma7.021111_0609.105.fits'),('sma7.021113_0626.096.2663','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sma7.021113_0626.096.fits'),('sma7.021115_0600.095.2110','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sma7.021115_0600.095.fits'),('sma7.021128_0612.078.1560','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sma7.021128_0612.078.fits'),('sma7.021202_0623.104.799','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sma7.021202_0623.104.fits'),('sma7.021204_0622.080.298','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sma7.021204_0622.080.fits'),('sma7.021206_0618.095.3274','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sma7.021206_0618.095.fits'),('sma7.021210_0542.074.2502','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sma7.021210_0542.074.fits'),('sma7.021214_0443.083.1950','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sma7.021214_0443.083.fits'),('sma7.021216_0531.065.1401','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sma7.021216_0531.065.fits'),('sma7.021229_0541.084.898','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sma7.021229_0541.084.fits'),('sma7.030103_0546.081.366','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sma7.030103_0546.081.fits'),('sma8.020928_0623.125.3760','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sma8.020928_0623.125.fits'),('sma8.020930_0622.141.3211','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sma8.020930_0622.141.fits'),('sma8.021002_0610.057.2430','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sma8.021002_0610.057.fits'),('sma8.021004_0621.075.1746','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sma8.021004_0621.075.fits'),('sma8.021004_0641.056.1750','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sma8.021004_0641.056.fits'),('sma8.021006_0612.132.822','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sma8.021006_0612.132.fits'),('sma8.021008_0636.064.317','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sma8.021008_0636.064.fits'),('sma8.021010_0747.120.3389','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sma8.021010_0747.120.fits'),('sma8.021012_0543.107.2835','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sma8.021012_0543.107.fits'),('sma8.021014_0547.102.2218','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sma8.021014_0547.102.fits'),('sma8.021030_0614.093.1665','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sma8.021030_0614.093.fits'),('sma8.021101_0621.100.1151','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sma8.021101_0621.100.fits'),('sma8.021105_0620.081.456','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sma8.021105_0620.081.fits'),('sma8.021109_0631.101.3707','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sma8.021109_0631.101.fits'),('sma8.021111_0614.106.3296','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sma8.021111_0614.106.fits'),('sma8.021113_0631.097.2667','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sma8.021113_0631.097.fits'),('sma8.021115_0605.096.2114','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sma8.021115_0605.096.fits'),('sma8.021128_0617.079.1564','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sma8.021128_0617.079.fits'),('sma8.021202_0628.105.803','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sma8.021202_0628.105.fits'),('sma8.021204_0627.081.302','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sma8.021204_0627.081.fits'),('sma8.021206_0623.096.3278','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sma8.021206_0623.096.fits'),('sma8.021210_0548.075.2505','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sma8.021210_0548.075.fits'),('sma8.021214_0448.084.1954','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sma8.021214_0448.084.fits'),('sma8.021216_0536.066.1405','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sma8.021216_0536.066.fits'),('sma8.021229_0546.085.902','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sma8.021229_0546.085.fits'),('sma8.030103_0552.082.370','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sma8.030103_0552.082.fits'),('sma9.020928_0628.126.3762','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sma9.020928_0628.126.fits'),('sma9.020930_0627.142.3214','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sma9.020930_0627.142.fits'),('sma9.021002_0615.058.2434','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sma9.021002_0615.058.fits'),('sma9.021004_0645.057.1754','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sma9.021004_0645.057.fits'),('sma9.021006_0617.133.826','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sma9.021006_0617.133.fits'),('sma9.021008_0641.065.321','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sma9.021008_0641.065.fits'),('sma9.021010_0802.104.3393','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sma9.021010_0802.104.fits'),('sma9.021012_0548.108.2838','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sma9.021012_0548.108.fits'),('sma9.021014_0725.116.2222','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sma9.021014_0725.116.fits'),('sma9.021030_0619.094.1669','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sma9.021030_0619.094.fits'),('sma9.021101_0625.101.1155','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sma9.021101_0625.101.fits'),('sma9.021105_0625.082.460','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sma9.021105_0625.082.fits'),('sma9.021109_0636.102.3709','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sma9.021109_0636.102.fits'),('sma9.021111_0619.107.3300','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sma9.021111_0619.107.fits'),('sma9.021113_0637.098.2672','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sma9.021113_0637.098.fits'),('sma9.021115_0610.097.2118','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sma9.021115_0610.097.fits'),('sma9.021128_0622.080.1568','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sma9.021128_0622.080.fits'),('sma9.021202_0633.106.807','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sma9.021202_0633.106.fits'),('sma9.021204_0632.082.306','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sma9.021204_0632.082.fits'),('sma9.021206_0629.097.3282','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sma9.021206_0629.097.fits'),('sma9.021210_0553.076.2510','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sma9.021210_0553.076.fits'),('sma9.021214_0453.085.1958','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sma9.021214_0453.085.fits'),('sma9.021216_0541.067.1409','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sma9.021216_0541.067.fits'),('sma9.021229_0551.086.906','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sma9.021229_0551.086.fits'),('sma9.030103_0557.083.374','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sma9.030103_0557.083.fits'),('smaa.020928_0926.138.3764','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smaa.020928_0926.138.fits'),('smaa.020930_0922.182.3217','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smaa.020930_0922.182.fits'),('smaa.021002_0911.074.2438','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smaa.021002_0911.074.fits'),('smaa.021004_0922.097.1758','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smaa.021004_0922.097.fits'),('smaa.021006_0910.128.830','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smaa.021006_0910.128.fits'),('smaa.021012_0859.091.2842','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smaa.021012_0859.091.fits'),('smaa.021101_0845.141.1159','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smaa.021101_0845.141.fits'),('smaa.021105_0629.083.464','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smaa.021105_0629.083.fits'),('smaa.021109_0641.103.3711','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smaa.021109_0641.103.fits'),('smaa.021111_0623.108.3304','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smaa.021111_0623.108.fits'),('smaa.021113_0641.099.2676','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smaa.021113_0641.099.fits'),('smaa.021115_0614.098.2122','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smaa.021115_0614.098.fits'),('smaa.021128_0627.081.1572','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smaa.021128_0627.081.fits'),('smaa.021202_0638.107.811','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smaa.021202_0638.107.fits'),('smaa.021204_0636.083.310','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smaa.021204_0636.083.fits'),('smaa.021206_0633.098.3285','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smaa.021206_0633.098.fits'),('smaa.021210_0557.077.2513','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smaa.021210_0557.077.fits'),('smaa.021214_0457.086.1962','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smaa.021214_0457.086.fits'),('smaa.021216_0545.068.1413','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smaa.021216_0545.068.fits'),('smaa.021229_0555.087.910','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smaa.021229_0555.087.fits'),('smaa.030103_0602.084.378','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smaa.030103_0602.084.fits'),('smb5.020928_0922.136.3766','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smb5.020928_0922.136.fits'),('smb5.020930_0918.180.3223','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smb5.020930_0918.180.fits'),('smb5.021002_0907.072.2442','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smb5.021002_0907.072.fits'),('smb5.021004_0918.095.1762','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smb5.021004_0918.095.fits'),('smb5.021006_0906.126.834','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smb5.021006_0906.126.fits'),('smb5.021012_0854.089.2846','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smb5.021012_0854.089.fits'),('smb5.021101_0841.139.1163','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smb5.021101_0841.139.fits'),('smb5.021105_0526.089.468','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smb5.021105_0526.089.fits'),('smb5.021109_0545.091.3713','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smb5.021109_0545.091.fits'),('smb5.021111_0530.096.3308','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smb5.021111_0530.096.fits'),('smb5.021113_0546.087.2680','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smb5.021113_0546.087.fits'),('smb5.021115_0519.086.2126','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smb5.021115_0519.086.fits'),('smb5.021128_0534.069.1576','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smb5.021128_0534.069.fits'),('smb5.021202_0544.095.815','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smb5.021202_0544.095.fits'),('smb5.021204_0544.071.314','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smb5.021204_0544.071.fits'),('smb5.021206_0537.086.3290','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smb5.021206_0537.086.fits'),('smb5.021208_0748.061.2853','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smb5.021208_0748.061.fits'),('smb5.021210_0453.065.2517','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smb5.021210_0453.065.fits'),('smb5.021210_0620.082.2521','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smb5.021210_0620.082.fits'),('smb5.021214_0404.074.1966','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smb5.021214_0404.074.fits'),('smb5.021216_0438.074.1417','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smb5.021216_0438.074.fits'),('smb5.021229_0503.075.914','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smb5.021229_0503.075.fits'),('smb5.030103_0505.072.382','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smb5.030103_0505.072.fits'),('smb6.020928_0543.117.3768','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smb6.020928_0543.117.fits'),('smb6.020930_0538.133.3227','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smb6.020930_0538.133.fits'),('smb6.021002_0529.049.2446','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smb6.021002_0529.049.fits'),('smb6.021004_0541.067.1766','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smb6.021004_0541.067.fits'),('smb6.021006_0532.124.839','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smb6.021006_0532.124.fits'),('smb6.021008_0548.073.325','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smb6.021008_0548.073.fits'),('smb6.021010_0707.112.3399','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smb6.021010_0707.112.fits'),('smb6.021012_0500.099.2850','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smb6.021012_0500.099.fits'),('smb6.021014_0501.095.2226','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smb6.021014_0501.095.fits'),('smb6.021030_0532.085.1673','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smb6.021030_0532.085.fits'),('smb6.021101_0547.092.1167','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smb6.021101_0547.092.fits'),('smb6.021105_0530.090.472','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smb6.021105_0530.090.fits'),('smb6.021109_0549.092.3715','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smb6.021109_0549.092.fits'),('smb6.021111_0533.097.3312','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smb6.021111_0533.097.fits'),('smb6.021113_0550.088.2684','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smb6.021113_0550.088.fits'),('smb6.021115_0522.087.2131','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smb6.021115_0522.087.fits'),('smb6.021128_0537.070.1580','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smb6.021128_0537.070.fits'),('smb6.021202_0548.096.819','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smb6.021202_0548.096.fits'),('smb6.021204_0547.072.318','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smb6.021204_0547.072.fits'),('smb6.021206_0541.087.3294','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smb6.021206_0541.087.fits'),('smb6.021208_0752.062.2857','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smb6.021208_0752.062.fits'),('smb6.021210_0458.066.2525','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smb6.021210_0458.066.fits'),('smb6.021210_0625.083.2530','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smb6.021210_0625.083.fits'),('smb6.021214_0408.075.1970','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smb6.021214_0408.075.fits'),('smb6.021216_0442.075.1421','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smb6.021216_0442.075.fits'),('smb6.021229_0506.076.918','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smb6.021229_0506.076.fits'),('smb6.030103_0509.073.386','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smb6.030103_0509.073.fits'),('smb7.020928_0548.118.3770','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smb7.020928_0548.118.fits'),('smb7.020930_0544.134.3231','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smb7.020930_0544.134.fits'),('smb7.021002_0534.050.2450','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smb7.021002_0534.050.fits'),('smb7.021004_0546.068.1770','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smb7.021004_0546.068.fits'),('smb7.021006_0537.125.844','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smb7.021006_0537.125.fits'),('smb7.021008_0553.074.329','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smb7.021008_0553.074.fits'),('smb7.021010_0712.113.3403','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smb7.021010_0712.113.fits'),('smb7.021012_0505.100.2854','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smb7.021012_0505.100.fits'),('smb7.021014_0506.096.2230','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smb7.021014_0506.096.fits'),('smb7.021030_0537.086.1677','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smb7.021030_0537.086.fits'),('smb7.021101_0551.093.1171','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smb7.021101_0551.093.fits'),('smb7.021105_0535.091.476','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smb7.021105_0535.091.fits'),('smb7.021109_0554.093.3717','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smb7.021109_0554.093.fits'),('smb7.021111_0538.098.3316','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smb7.021111_0538.098.fits'),('smb7.021113_0555.089.2688','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smb7.021113_0555.089.fits'),('smb7.021115_0528.088.2134','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smb7.021115_0528.088.fits'),('smb7.021128_0542.071.1584','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smb7.021128_0542.071.fits'),('smb7.021202_0553.097.823','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smb7.021202_0553.097.fits'),('smb7.021204_0552.073.322','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smb7.021204_0552.073.fits'),('smb7.021206_0546.088.3297','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smb7.021206_0546.088.fits'),('smb7.021210_0504.067.2534','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smb7.021210_0504.067.fits'),('smb7.021210_0630.084.2538','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smb7.021210_0630.084.fits'),('smb7.021214_0413.076.1974','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smb7.021214_0413.076.fits'),('smb7.021216_0447.076.1425','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smb7.021216_0447.076.fits'),('smb7.021229_0511.077.922','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smb7.021229_0511.077.fits'),('smb7.030103_0515.074.390','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smb7.030103_0515.074.fits'),('smb8.020928_0553.119.3772','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smb8.020928_0553.119.fits'),('smb8.020930_0549.135.3235','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smb8.020930_0549.135.fits'),('smb8.021002_0539.051.2454','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smb8.021002_0539.051.fits'),('smb8.021004_0551.069.1774','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smb8.021004_0551.069.fits'),('smb8.021006_0542.126.848','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smb8.021006_0542.126.fits'),('smb8.021008_0558.075.333','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smb8.021008_0558.075.fits'),('smb8.021010_0718.114.3407','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smb8.021010_0718.114.fits'),('smb8.021012_0511.101.2858','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smb8.021012_0511.101.fits'),('smb8.021030_0542.087.1681','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smb8.021030_0542.087.fits'),('smb8.021101_0555.094.1175','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smb8.021101_0555.094.fits'),('smb8.021105_0541.092.479','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smb8.021105_0541.092.fits'),('smb8.021109_0559.094.3719','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smb8.021109_0559.094.fits'),('smb8.021111_0543.099.3320','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smb8.021111_0543.099.fits'),('smb8.021113_0600.090.2692','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smb8.021113_0600.090.fits'),('smb8.021115_0533.089.2139','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smb8.021115_0533.089.fits'),('smb8.021128_0547.072.1588','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smb8.021128_0547.072.fits'),('smb8.021202_0558.098.827','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smb8.021202_0558.098.fits'),('smb8.021204_0557.074.327','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smb8.021204_0557.074.fits'),('smb8.021206_0552.089.3302','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smb8.021206_0552.089.fits'),('smb8.021210_0511.068.2542','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smb8.021210_0511.068.fits'),('smb8.021210_0636.085.2545','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smb8.021210_0636.085.fits'),('smb8.021214_0418.077.1978','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smb8.021214_0418.077.fits'),('smb8.021216_0452.077.1429','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smb8.021216_0452.077.fits'),('smb8.021229_0516.078.926','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smb8.021229_0516.078.fits'),('smb8.030103_0520.075.394','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smb8.030103_0520.075.fits'),('smb9.020928_0558.120.3774','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smb9.020928_0558.120.fits'),('smb9.020930_0555.136.3237','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smb9.020930_0555.136.fits'),('smb9.021002_0545.052.2458','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smb9.021002_0545.052.fits'),('smb9.021004_0556.070.1778','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smb9.021004_0556.070.fits'),('smb9.021006_0547.127.851','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smb9.021006_0547.127.fits'),('smb9.021008_0603.076.337','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smb9.021008_0603.076.fits'),('smb9.021010_0723.115.3411','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smb9.021010_0723.115.fits'),('smb9.021012_0516.102.2862','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smb9.021012_0516.102.fits'),('smb9.021014_0516.097.2234','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smb9.021014_0516.097.fits'),('smb9.021030_0547.088.1685','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smb9.021030_0547.088.fits'),('smb9.021101_0559.095.1179','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smb9.021101_0559.095.fits'),('smb9.021105_0546.093.483','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smb9.021105_0546.093.fits'),('smb9.021109_0605.095.3721','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smb9.021109_0605.095.fits'),('smb9.021111_0548.100.3324','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smb9.021111_0548.100.fits'),('smb9.021113_0606.091.2696','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smb9.021113_0606.091.fits'),('smb9.021115_0538.090.2143','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smb9.021115_0538.090.fits'),('smb9.021128_0552.073.1592','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smb9.021128_0552.073.fits'),('smb9.021202_0603.099.831','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smb9.021202_0603.099.fits'),('smb9.021204_0602.075.331','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smb9.021204_0602.075.fits'),('smb9.021206_0557.090.3307','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smb9.021206_0557.090.fits'),('smb9.021210_0641.086.2549','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smb9.021210_0641.086.fits'),('smb9.021214_0423.078.1982','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smb9.021214_0423.078.fits'),('smb9.021216_0457.078.1433','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smb9.021216_0457.078.fits'),('smb9.021229_0521.079.931','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smb9.021229_0521.079.fits'),('smb9.030103_0525.076.398','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smb9.030103_0525.076.fits'),('smba.020928_0603.121.3776','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smba.020928_0603.121.fits'),('smba.020930_0600.137.3241','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smba.020930_0600.137.fits'),('smba.021002_0550.053.2462','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smba.021002_0550.053.fits'),('smba.021004_0602.071.1782','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smba.021004_0602.071.fits'),('smba.021006_0552.128.855','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smba.021006_0552.128.fits'),('smba.021008_0608.077.341','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smba.021008_0608.077.fits'),('smba.021010_0728.116.3413','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smba.021010_0728.116.fits'),('smba.021012_0522.103.2866','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smba.021012_0522.103.fits'),('smba.021014_0520.098.2238','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smba.021014_0520.098.fits'),('smba.021030_0552.089.1689','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smba.021030_0552.089.fits'),('smba.021101_0603.096.1183','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smba.021101_0603.096.fits'),('smba.021105_0551.094.488','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smba.021105_0551.094.fits'),('smba.021105_0601.076.491','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smba.021105_0601.076.fits'),('smba.021109_0610.096.3723','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smba.021109_0610.096.fits'),('smba.021111_0553.101.3328','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smba.021111_0553.101.fits'),('smba.021113_0611.092.2700','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smba.021113_0611.092.fits'),('smba.021115_0543.091.2147','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smba.021115_0543.091.fits'),('smba.021128_0557.074.1596','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smba.021128_0557.074.fits'),('smba.021202_0608.100.835','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smba.021202_0608.100.fits'),('smba.021204_0607.076.335','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smba.021204_0607.076.fits'),('smba.021206_0602.091.3311','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smba.021206_0602.091.fits'),('smba.021210_0525.070.2553','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smba.021210_0525.070.fits'),('smba.021214_0428.079.1986','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smba.021214_0428.079.fits'),('smba.021216_0516.061.1437','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smba.021216_0516.061.fits'),('smba.021229_0526.080.934','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smba.021229_0526.080.fits'),('smba.030103_0531.077.402','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smba.030103_0531.077.fits'),('smc5.020930_0910.178.3245','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smc5.020930_0910.178.fits'),('smc5.021002_0902.070.2466','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smc5.021002_0902.070.fits'),('smc5.021004_0913.093.1786','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smc5.021004_0913.093.fits'),('smc5.021006_0901.124.859','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smc5.021006_0901.124.fits'),('smc5.021012_0828.144.2870','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smc5.021012_0828.144.fits'),('smc5.021101_0836.137.1187','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smc5.021101_0836.137.fits'),('smc5.021105_0502.083.495','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smc5.021105_0502.083.fits'),('smc5.021109_0520.085.3725','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smc5.021109_0520.085.fits'),('smc5.021111_0505.090.3332','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smc5.021111_0505.090.fits'),('smc5.021113_0521.081.2705','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smc5.021113_0521.081.fits'),('smc5.021115_0449.080.2151','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smc5.021115_0449.080.fits'),('smc5.021128_0510.063.1600','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smc5.021128_0510.063.fits'),('smc5.021202_0521.089.838','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smc5.021202_0521.089.fits'),('smc5.021204_0520.065.339','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smc5.021204_0520.065.fits'),('smc5.021206_0510.080.3315','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smc5.021206_0510.080.fits'),('smc5.021208_0528.054.2860','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smc5.021208_0528.054.fits'),('smc5.021210_0421.059.2557','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smc5.021210_0421.059.fits'),('smc5.021214_0340.068.1992','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smc5.021214_0340.068.fits'),('smc5.021216_0414.068.1441','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smc5.021216_0414.068.fits'),('smc5.021229_0439.069.939','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smc5.021229_0439.069.fits'),('smc5.030103_0440.066.406','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smc5.030103_0440.066.fits'),('smc6.020928_0920.135.3778','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smc6.020928_0920.135.fits'),('smc6.020930_0914.179.3249','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smc6.020930_0914.179.fits'),('smc6.021002_0904.071.2470','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smc6.021002_0904.071.fits'),('smc6.021004_0916.094.1790','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smc6.021004_0916.094.fits'),('smc6.021006_0904.125.863','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smc6.021006_0904.125.fits'),('smc6.021012_0851.088.2874','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smc6.021012_0851.088.fits'),('smc6.021101_0839.138.1191','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smc6.021101_0839.138.fits'),('smc6.021105_0505.084.499','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smc6.021105_0505.084.fits'),('smc6.021109_0524.086.3727','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smc6.021109_0524.086.fits'),('smc6.021111_0508.091.3336','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smc6.021111_0508.091.fits'),('smc6.021113_0525.082.2709','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smc6.021113_0525.082.fits'),('smc6.021115_0452.081.2155','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smc6.021115_0452.081.fits'),('smc6.021128_0513.064.1604','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smc6.021128_0513.064.fits'),('smc6.021202_0523.090.842','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smc6.021202_0523.090.fits'),('smc6.021204_0523.066.343','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smc6.021204_0523.066.fits'),('smc6.021206_0513.081.3318','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smc6.021206_0513.081.fits'),('smc6.021208_0532.055.2865','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smc6.021208_0532.055.fits'),('smc6.021210_0646.087.2560','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smc6.021210_0646.087.fits'),('smc6.021214_0343.069.1995','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smc6.021214_0343.069.fits'),('smc6.021216_0417.069.1446','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smc6.021216_0417.069.fits'),('smc6.021229_0442.070.943','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smc6.021229_0442.070.fits'),('smc6.030103_0443.067.410','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smc6.030103_0443.067.fits'),('smc7.020928_0523.113.3780','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smc7.020928_0523.113.fits'),('smc7.020930_0517.129.3253','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smc7.020930_0517.129.fits'),('smc7.021002_0509.045.2473','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smc7.021002_0509.045.fits'),('smc7.021004_0520.063.1793','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smc7.021004_0520.063.fits'),('smc7.021006_0510.120.867','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smc7.021006_0510.120.fits'),('smc7.021008_0528.069.345','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smc7.021008_0528.069.fits'),('smc7.021010_0647.108.3417','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smc7.021010_0647.108.fits'),('smc7.021012_0438.095.2878','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smc7.021012_0438.095.fits'),('smc7.021014_0440.091.2242','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smc7.021014_0440.091.fits'),('smc7.021030_0511.081.1693','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smc7.021030_0511.081.fits'),('smc7.021101_0530.088.1195','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smc7.021101_0530.088.fits'),('smc7.021105_0508.085.503','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smc7.021105_0508.085.fits'),('smc7.021109_0527.087.3729','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smc7.021109_0527.087.fits'),('smc7.021111_0511.092.3340','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smc7.021111_0511.092.fits'),('smc7.021113_0528.083.2711','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smc7.021113_0528.083.fits'),('smc7.021115_0456.082.2157','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smc7.021115_0456.082.fits'),('smc7.021128_0516.065.1607','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smc7.021128_0516.065.fits'),('smc7.021202_0527.091.846','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smc7.021202_0527.091.fits'),('smc7.021204_0526.067.347','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smc7.021204_0526.067.fits'),('smc7.021206_0518.082.3323','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smc7.021206_0518.082.fits'),('smc7.021208_0701.056.2869','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smc7.021208_0701.056.fits'),('smc7.021210_0427.061.2564','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smc7.021210_0427.061.fits'),('smc7.021210_0601.078.2568','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smc7.021210_0601.078.fits'),('smc7.021214_0346.070.1999','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smc7.021214_0346.070.fits'),('smc7.021216_0421.070.1451','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smc7.021216_0421.070.fits'),('smc7.021229_0445.071.946','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smc7.021229_0445.071.fits'),('smc7.030103_0447.068.414','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smc7.030103_0447.068.fits'),('smc8.020928_0528.114.3782','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smc8.020928_0528.114.fits'),('smc8.020930_0523.130.3257','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smc8.020930_0523.130.fits'),('smc8.021002_0514.046.2477','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smc8.021002_0514.046.fits'),('smc8.021004_0526.064.1797','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smc8.021004_0526.064.fits'),('smc8.021006_0516.121.872','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smc8.021006_0516.121.fits'),('smc8.021008_0533.070.349','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smc8.021008_0533.070.fits'),('smc8.021010_0652.109.3421','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smc8.021010_0652.109.fits'),('smc8.021012_0443.096.2882','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smc8.021012_0443.096.fits'),('smc8.021014_0445.092.2246','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smc8.021014_0445.092.fits'),('smc8.021030_0516.082.1697','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smc8.021030_0516.082.fits'),('smc8.021101_0534.089.1199','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smc8.021101_0534.089.fits'),('smc8.021105_0513.086.507','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smc8.021105_0513.086.fits'),('smc8.021109_0532.088.3731','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smc8.021109_0532.088.fits'),('smc8.021111_0516.093.3344','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smc8.021111_0516.093.fits'),('smc8.021113_0533.084.2716','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smc8.021113_0533.084.fits'),('smc8.021115_0501.083.2163','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smc8.021115_0501.083.fits'),('smc8.021128_0521.066.1612','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smc8.021128_0521.066.fits'),('smc8.021202_0531.092.850','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smc8.021202_0531.092.fits'),('smc8.021204_0530.068.351','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smc8.021204_0530.068.fits'),('smc8.021206_0523.083.3325','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smc8.021206_0523.083.fits'),('smc8.021208_0725.058.2872','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smc8.021208_0725.058.fits'),('smc8.021210_0433.062.2572','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smc8.021210_0433.062.fits'),('smc8.021210_0607.079.2576','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smc8.021210_0607.079.fits'),('smc8.021214_0351.071.2003','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smc8.021214_0351.071.fits'),('smc8.021216_0425.071.1455','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smc8.021216_0425.071.fits'),('smc8.021229_0450.072.950','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smc8.021229_0450.072.fits'),('smc8.030103_0451.069.418','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smc8.030103_0451.069.fits'),('smc9.020928_0533.115.3784','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smc9.020928_0533.115.fits'),('smc9.020930_0528.131.3261','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smc9.020930_0528.131.fits'),('smc9.021002_0519.047.2481','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smc9.021002_0519.047.fits'),('smc9.021004_0531.065.1801','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smc9.021004_0531.065.fits'),('smc9.021006_0521.122.876','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smc9.021006_0521.122.fits'),('smc9.021008_0538.071.352','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smc9.021008_0538.071.fits'),('smc9.021010_0657.110.3425','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smc9.021010_0657.110.fits'),('smc9.021012_0449.097.2886','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smc9.021012_0449.097.fits'),('smc9.021014_0450.093.2250','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smc9.021014_0450.093.fits'),('smc9.021030_0521.083.1701','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smc9.021030_0521.083.fits'),('smc9.021101_0538.090.1203','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smc9.021101_0538.090.fits'),('smc9.021105_0518.087.511','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smc9.021105_0518.087.fits'),('smc9.021109_0537.089.3732','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smc9.021109_0537.089.fits'),('smc9.021111_0521.094.3348','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smc9.021111_0521.094.fits'),('smc9.021113_0538.085.2720','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smc9.021113_0538.085.fits'),('smc9.021115_0506.084.2167','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smc9.021115_0506.084.fits'),('smc9.021128_0526.067.1615','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smc9.021128_0526.067.fits'),('smc9.021202_0536.093.854','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smc9.021202_0536.093.fits'),('smc9.021204_0535.069.355','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smc9.021204_0535.069.fits'),('smc9.021206_0528.084.3329','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smc9.021206_0528.084.fits'),('smc9.021210_0442.063.2580','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smc9.021210_0442.063.fits'),('smc9.021210_0612.080.2584','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smc9.021210_0612.080.fits'),('smc9.021214_0356.072.2007','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smc9.021214_0356.072.fits'),('smc9.021216_0430.072.1457','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smc9.021216_0430.072.fits'),('smc9.021229_0455.073.954','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smc9.021229_0455.073.fits'),('smc9.030103_0457.070.422','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smc9.030103_0457.070.fits'),('smca.020928_0538.116.3786','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smca.020928_0538.116.fits'),('smca.020930_0533.132.3265','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smca.020930_0533.132.fits'),('smca.021002_0524.048.2485','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smca.021002_0524.048.fits'),('smca.021004_0536.066.1806','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smca.021004_0536.066.fits'),('smca.021006_0527.123.880','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smca.021006_0527.123.fits'),('smca.021008_0543.072.356','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smca.021008_0543.072.fits'),('smca.021010_0702.111.3429','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smca.021010_0702.111.fits'),('smca.021012_0454.098.2890','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smca.021012_0454.098.fits'),('smca.021014_0455.094.2254','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smca.021014_0455.094.fits'),('smca.021030_0527.084.1705','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smca.021030_0527.084.fits'),('smca.021101_0542.091.1207','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smca.021101_0542.091.fits'),('smca.021105_0522.088.515','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smca.021105_0522.088.fits'),('smca.021109_0541.090.3734','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smca.021109_0541.090.fits'),('smca.021111_0526.095.3352','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smca.021111_0526.095.fits'),('smca.021113_0542.086.2723','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smca.021113_0542.086.fits'),('smca.021115_0511.085.2171','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smca.021115_0511.085.fits'),('smca.021128_0530.068.1619','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smca.021128_0530.068.fits'),('smca.021202_0541.094.858','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smca.021202_0541.094.fits'),('smca.021204_0540.070.359','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smca.021204_0540.070.fits'),('smca.021206_0533.085.3333','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smca.021206_0533.085.fits'),('smca.021208_0744.060.2876','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smca.021208_0744.060.fits'),('smca.021210_0447.064.2587','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smca.021210_0447.064.fits'),('smca.021210_0616.081.2591','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smca.021210_0616.081.fits'),('smca.021214_0400.073.2012','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smca.021214_0400.073.fits'),('smca.021216_0435.073.1461','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smca.021216_0435.073.fits'),('smca.021229_0459.074.958','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smca.021229_0459.074.fits'),('smca.030103_0501.071.426','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smca.030103_0501.071.fits'),('smd7.020928_0459.108.3788','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smd7.020928_0459.108.fits'),('smd7.020930_0453.124.3269','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smd7.020930_0453.124.fits'),('smd7.021002_0447.040.2489','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smd7.021002_0447.040.fits'),('smd7.021004_0457.058.1810','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smd7.021004_0457.058.fits'),('smd7.021006_0447.115.884','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smd7.021006_0447.115.fits'),('smd7.021008_0506.064.360','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smd7.021008_0506.064.fits'),('smd7.021010_0618.106.3433','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smd7.021010_0618.106.fits'),('smd7.021012_0415.090.2894','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smd7.021012_0415.090.fits'),('smd7.021014_0417.086.2258','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smd7.021014_0417.086.fits'),('smd7.021030_0449.076.1709','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smd7.021030_0449.076.fits'),('smd7.021101_0510.083.1211','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smd7.021101_0510.083.fits'),('smd7.021105_0446.078.519','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smd7.021105_0446.078.fits'),('smd7.021109_0503.080.3736','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smd7.021109_0503.080.fits'),('smd7.021111_0449.085.3356','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smd7.021111_0449.085.fits'),('smd7.021113_0505.076.2727','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smd7.021113_0505.076.fits'),('smd7.021115_0432.075.2175','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smd7.021115_0432.075.fits'),('smd7.021128_0454.058.1623','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smd7.021128_0454.058.fits'),('smd7.021204_0504.060.363','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smd7.021204_0504.060.fits'),('smd7.021206_0453.075.3337','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smd7.021206_0453.075.fits'),('smd7.021208_0423.044.2881','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smd7.021208_0423.044.fits'),('smd7.021210_0347.060.2595','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smd7.021210_0347.060.fits'),('smd7.021214_0325.063.2016','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smd7.021214_0325.063.fits'),('smd7.021216_0359.063.1465','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smd7.021216_0359.063.fits'),('smd7.021229_0423.064.962','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smd7.021229_0423.064.fits'),('smd7.030103_0421.061.430','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smd7.030103_0421.061.fits'),('smd8.020928_0503.109.3790','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smd8.020928_0503.109.fits'),('smd8.020930_0458.125.3273','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smd8.020930_0458.125.fits'),('smd8.021002_0451.041.2493','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smd8.021002_0451.041.fits'),('smd8.021004_0501.059.1814','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smd8.021004_0501.059.fits'),('smd8.021006_0450.116.888','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smd8.021006_0450.116.fits'),('smd8.021008_0510.065.364','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smd8.021008_0510.065.fits'),('smd8.021010_0630.104.3437','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smd8.021010_0630.104.fits'),('smd8.021012_0420.091.2898','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smd8.021012_0420.091.fits'),('smd8.021014_0421.087.2260','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smd8.021014_0421.087.fits'),('smd8.021030_0453.077.1713','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smd8.021030_0453.077.fits'),('smd8.021101_0513.084.1215','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smd8.021101_0513.084.fits'),('smd8.021105_0449.079.523','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smd8.021105_0449.079.fits'),('smd8.021109_0507.081.3738','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smd8.021109_0507.081.fits'),('smd8.021111_0452.086.3360','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smd8.021111_0452.086.fits'),('smd8.021113_0508.077.2731','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smd8.021113_0508.077.fits'),('smd8.021115_0435.076.2179','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smd8.021115_0435.076.fits'),('smd8.021128_0457.059.1626','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smd8.021128_0457.059.fits'),('smd8.021202_0455.084.862','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smd8.021202_0455.084.fits'),('smd8.021204_0507.061.367','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smd8.021204_0507.061.fits'),('smd8.021206_0456.076.3341','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smd8.021206_0456.076.fits'),('smd8.021208_0513.050.2884','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smd8.021208_0513.050.fits'),('smd8.021210_0354.061.2599','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smd8.021210_0354.061.fits'),('smd8.021214_0327.064.2020','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smd8.021214_0327.064.fits'),('smd8.021216_0402.064.1469','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smd8.021216_0402.064.fits'),('smd8.021229_0426.065.966','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smd8.021229_0426.065.fits'),('smd8.030103_0425.062.434','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smd8.030103_0425.062.fits'),('smd9.020928_0507.110.3792','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smd9.020928_0507.110.fits'),('smd9.020930_0503.126.3277','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smd9.020930_0503.126.fits'),('smd9.021002_0456.042.2497','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smd9.021002_0456.042.fits'),('smd9.021004_0506.060.1818','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smd9.021004_0506.060.fits'),('smd9.021006_0455.117.892','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smd9.021006_0455.117.fits'),('smd9.021008_0515.066.368','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smd9.021008_0515.066.fits'),('smd9.021010_0634.105.3441','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smd9.021010_0634.105.fits'),('smd9.021012_0424.092.2902','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smd9.021012_0424.092.fits'),('smd9.021014_0426.088.2264','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smd9.021014_0426.088.fits'),('smd9.021030_0458.078.1717','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smd9.021030_0458.078.fits'),('smd9.021101_0518.085.1219','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smd9.021101_0518.085.fits'),('smd9.021105_0452.080.527','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smd9.021105_0452.080.fits'),('smd9.021109_0510.082.3740','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smd9.021109_0510.082.fits'),('smd9.021111_0452.087.3364','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smd9.021111_0452.087.fits'),('smd9.021113_0512.078.2737','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smd9.021113_0512.078.fits'),('smd9.021115_0439.077.2183','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smd9.021115_0439.077.fits'),('smd9.021128_0500.060.1629','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smd9.021128_0500.060.fits'),('smd9.021202_0458.085.868','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smd9.021202_0458.085.fits'),('smd9.021204_0510.062.371','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smd9.021204_0510.062.fits'),('smd9.021206_0500.077.3345','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smd9.021206_0500.077.fits'),('smd9.021208_0516.051.2888','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smd9.021208_0516.051.fits'),('smd9.021210_0358.062.2603','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smd9.021210_0358.062.fits'),('smd9.021214_0331.065.2024','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smd9.021214_0331.065.fits'),('smd9.021216_0405.065.1473','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smd9.021216_0405.065.fits'),('smd9.021229_0429.066.969','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smd9.021229_0429.066.fits'),('smd9.030103_0430.063.438','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smd9.030103_0430.063.fits'),('smda.020928_0515.111.3794','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smda.020928_0515.111.fits'),('smda.020930_0509.127.3281','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smda.020930_0509.127.fits'),('smda.021002_0501.043.2501','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smda.021002_0501.043.fits'),('smda.021004_0512.061.1822','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smda.021004_0512.061.fits'),('smda.021006_0501.118.896','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smda.021006_0501.118.fits'),('smda.021008_0521.067.372','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smda.021008_0521.067.fits'),('smda.021010_0640.106.3445','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smda.021010_0640.106.fits'),('smda.021012_0430.093.2906','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smda.021012_0430.093.fits'),('smda.021014_0432.089.2268','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smda.021014_0432.089.fits'),('smda.021030_0504.079.1721','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smda.021030_0504.079.fits'),('smda.021101_0523.086.1223','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smda.021101_0523.086.fits'),('smda.021105_0456.081.531','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smda.021105_0456.081.fits'),('smda.021109_0514.083.3742','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smda.021109_0514.083.fits'),('smda.021111_0459.088.3368','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smda.021111_0459.088.fits'),('smda.021113_0515.079.2741','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smda.021113_0515.079.fits'),('smda.021115_0443.078.2187','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smda.021115_0443.078.fits'),('smda.021128_0504.061.1635','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smda.021128_0504.061.fits'),('smda.021202_0501.086.871','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smda.021202_0501.086.fits'),('smda.021204_0514.063.375','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smda.021204_0514.063.fits'),('smda.021206_0504.078.3349','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smda.021206_0504.078.fits'),('smda.021208_0520.052.2892','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smda.021208_0520.052.fits'),('smda.021210_0402.063.2607','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smda.021210_0402.063.fits'),('smda.021214_0334.066.2027','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smda.021214_0334.066.fits'),('smda.021216_0409.066.1477','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smda.021216_0409.066.fits'),('smda.021229_0433.067.974','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smda.021229_0433.067.fits'),('smda.030103_0434.064.442','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smda.030103_0434.064.fits'),('smdb.020928_0520.112.3796','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/smdb.020928_0520.112.fits'),('smdb.020930_0514.128.3286','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/smdb.020930_0514.128.fits'),('smdb.021002_0506.044.2506','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/smdb.021002_0506.044.fits'),('smdb.021004_0516.062.1826','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/smdb.021004_0516.062.fits'),('smdb.021006_0507.119.900','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/smdb.021006_0507.119.fits'),('smdb.021008_0525.068.376','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/smdb.021008_0525.068.fits'),('smdb.021010_0644.107.3448','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/smdb.021010_0644.107.fits'),('smdb.021012_0434.094.2910','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/smdb.021012_0434.094.fits'),('smdb.021014_0436.090.2272','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/smdb.021014_0436.090.fits'),('smdb.021030_0508.080.1725','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/smdb.021030_0508.080.fits'),('smdb.021101_0527.087.1227','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/smdb.021101_0527.087.fits'),('smdb.021105_0459.082.535','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/smdb.021105_0459.082.fits'),('smdb.021109_0518.084.3744','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/smdb.021109_0518.084.fits'),('smdb.021111_0502.089.3372','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/smdb.021111_0502.089.fits'),('smdb.021113_0519.080.2743','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/smdb.021113_0519.080.fits'),('smdb.021115_0446.079.2191','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/smdb.021115_0446.079.fits'),('smdb.021128_0507.062.1639','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/smdb.021128_0507.062.fits'),('smdb.021202_0518.088.875','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/smdb.021202_0518.088.fits'),('smdb.021204_0517.064.379','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/smdb.021204_0517.064.fits'),('smdb.021206_0507.079.3353','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/smdb.021206_0507.079.fits'),('smdb.021208_0524.053.2896','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/smdb.021208_0524.053.fits'),('smdb.021210_0406.064.2611','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/smdb.021210_0406.064.fits'),('smdb.021214_0338.067.2032','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/smdb.021214_0338.067.fits'),('smdb.021216_0412.067.1481','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/smdb.021216_0412.067.fits'),('smdb.021229_0436.068.977','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/smdb.021229_0436.068.fits'),('smdb.030103_0437.065.446','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/smdb.030103_0437.065.fits'),('sme8.020928_0450.106.3798','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sme8.020928_0450.106.fits'),('sme8.020930_0446.122.3289','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sme8.020930_0446.122.fits'),('sme8.021002_0442.038.2509','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sme8.021002_0442.038.fits'),('sme8.021004_0451.056.1830','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sme8.021004_0451.056.fits'),('sme8.021006_0440.113.904','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sme8.021006_0440.113.fits'),('sme8.021008_0500.062.380','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sme8.021008_0500.062.fits'),('sme8.021010_0611.104.3452','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sme8.021010_0611.104.fits'),('sme8.021012_0405.088.2914','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sme8.021012_0405.088.fits'),('sme8.021014_0407.084.2277','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sme8.021014_0407.084.fits'),('sme8.021030_0443.074.1729','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sme8.021030_0443.074.fits'),('sme8.021101_0503.081.1231','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sme8.021101_0503.081.fits'),('sme8.021105_0440.076.539','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sme8.021105_0440.076.fits'),('sme8.021109_0456.078.3746','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sme8.021109_0456.078.fits'),('sme8.021111_0444.083.3376','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sme8.021111_0444.083.fits'),('sme8.021113_0459.074.2747','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sme8.021113_0459.074.fits'),('sme8.021115_0426.073.2194','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sme8.021115_0426.073.fits'),('sme8.021128_0446.056.1642','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sme8.021128_0446.056.fits'),('sme8.021202_0446.082.879','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sme8.021202_0446.082.fits'),('sme8.021204_0500.058.383','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sme8.021204_0500.058.fits'),('sme8.021206_0447.073.3357','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sme8.021206_0447.073.fits'),('sme8.021208_0416.042.2899','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sme8.021208_0416.042.fits'),('sme8.021210_0340.058.2615','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sme8.021210_0340.058.fits'),('sme8.021214_0317.061.2036','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sme8.021214_0317.061.fits'),('sme8.021216_0354.061.1487','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sme8.021216_0354.061.fits'),('sme8.021229_0408.061.981','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sme8.021229_0408.061.fits'),('sme8.021229_0411.062.985','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sme8.021229_0411.062.fits'),('sme8.030103_0415.059.450','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sme8.030103_0415.059.fits'),('sme9.020928_0456.107.3800','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/sme9.020928_0456.107.fits'),('sme9.020930_0449.123.3293','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/sme9.020930_0449.123.fits'),('sme9.021002_0444.039.2514','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/sme9.021002_0444.039.fits'),('sme9.021004_0454.057.1834','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/sme9.021004_0454.057.fits'),('sme9.021006_0444.114.908','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/sme9.021006_0444.114.fits'),('sme9.021008_0503.063.384','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/sme9.021008_0503.063.fits'),('sme9.021010_0615.105.3457','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/sme9.021010_0615.105.fits'),('sme9.021012_0412.089.2918','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/sme9.021012_0412.089.fits'),('sme9.021014_0413.085.2280','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/sme9.021014_0413.085.fits'),('sme9.021030_0446.075.1733','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/sme9.021030_0446.075.fits'),('sme9.021101_0507.082.1235','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/sme9.021101_0507.082.fits'),('sme9.021105_0444.077.543','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/sme9.021105_0444.077.fits'),('sme9.021109_0501.079.3749','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/sme9.021109_0501.079.fits'),('sme9.021111_0446.084.3380','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/sme9.021111_0446.084.fits'),('sme9.021113_0503.075.2752','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/sme9.021113_0503.075.fits'),('sme9.021115_0430.074.2199','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/sme9.021115_0430.074.fits'),('sme9.021128_0451.057.1646','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/sme9.021128_0451.057.fits'),('sme9.021202_0452.083.883','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/sme9.021202_0452.083.fits'),('sme9.021204_0502.059.387','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/sme9.021204_0502.059.fits'),('sme9.021206_0450.074.3361','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/sme9.021206_0450.074.fits'),('sme9.021208_0418.043.2904','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/sme9.021208_0418.043.fits'),('sme9.021210_0344.059.2619','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/sme9.021210_0344.059.fits'),('sme9.021214_0322.062.2040','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/sme9.021214_0322.062.fits'),('sme9.021216_0356.062.1491','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/sme9.021216_0356.062.fits'),('sme9.021229_0421.063.989','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/sme9.021229_0421.063.fits'),('sme9.030103_0418.060.454','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/sme9.030103_0418.060.fits'),('waa6.020909_0145.116.3930','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa6.020909_0145.116.fits'),('waa6.020909_0202.121.3931','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa6.020909_0202.121.fits'),('waa6.020909_0214.126.3932','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa6.020909_0214.126.fits'),('waa6.020909_0226.131.3933','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa6.020909_0226.131.fits'),('waa6.020909_0241.136.3934','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa6.020909_0241.136.fits'),('waa6.020909_0252.141.3935','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa6.020909_0252.141.fits'),('waa7.020909_0148.117.3936','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa7.020909_0148.117.fits'),('waa7.020909_0205.122.3937','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa7.020909_0205.122.fits'),('waa7.020909_0216.127.3938','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa7.020909_0216.127.fits'),('waa7.020909_0229.132.3939','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa7.020909_0229.132.fits'),('waa7.020909_0243.137.3940','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa7.020909_0243.137.fits'),('waa7.020909_0255.142.3941','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/waa7.020909_0255.142.fits'),('wbb5.020909_0705.208.3942','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb5.020909_0705.208.fits'),('wbb5.020909_0715.212.3943','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb5.020909_0715.212.fits'),('wbb5.020909_0724.216.3944','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb5.020909_0724.216.fits'),('wbb5.020909_0738.220.3945','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb5.020909_0738.220.fits'),('wbb5.020909_0751.224.3946','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb5.020909_0751.224.fits'),('wbb5.020909_0800.228.3947','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb5.020909_0800.228.fits'),('wbb6.020909_0701.207.3948','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb6.020909_0701.207.fits'),('wbb6.020909_0713.211.3949','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb6.020909_0713.211.fits'),('wbb6.020909_0721.215.3950','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb6.020909_0721.215.fits'),('wbb6.020909_0736.219.3951','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb6.020909_0736.219.fits'),('wbb6.020909_0749.223.3952','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb6.020909_0749.223.fits'),('wbb6.020909_0758.227.3953','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wbb6.020909_0758.227.fits'),('wdd4.020909_0922.249.3954','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wdd4.020909_0922.249.fits'),('wdd4.020909_0940.252.3955','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wdd4.020909_0940.252.fits'),('wdd4.020909_0951.255.3956','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wdd4.020909_0951.255.fits'),('wdd5.020909_0824.233.3957','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wdd5.020909_0824.233.fits'),('wdd5.020909_0834.236.3958','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wdd5.020909_0834.236.fits'),('wdd5.020909_0842.239.3959','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wdd5.020909_0842.239.fits'),('wdd5.020909_0857.242.3960','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wdd5.020909_0857.242.fits'),('wdd5.020909_0907.245.3961','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wdd5.020909_0907.245.fits'),('wdd5.020909_0916.248.3962','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wdd5.020909_0916.248.fits'),('wx01.020909_0151.118.3963','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx01.020909_0151.118.fits'),('wx01.020909_0207.123.3964','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx01.020909_0207.123.fits'),('wx01.020909_0218.128.3965','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx01.020909_0218.128.fits'),('wx01.020909_0232.133.3966','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx01.020909_0232.133.fits'),('wx01.020909_0245.138.3967','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx01.020909_0245.138.fits'),('wx01.020909_0257.143.3968','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx01.020909_0257.143.fits'),('wx02.020909_0154.119.3969','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx02.020909_0154.119.fits'),('wx02.020909_0209.124.3970','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx02.020909_0209.124.fits'),('wx02.020909_0220.129.3971','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx02.020909_0220.129.fits'),('wx02.020909_0235.134.3972','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx02.020909_0235.134.fits'),('wx02.020909_0248.139.3973','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx02.020909_0248.139.fits'),('wx02.020909_0259.144.3974','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx02.020909_0259.144.fits'),('wx03.020909_0157.120.3975','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx03.020909_0157.120.fits'),('wx03.020909_0211.125.3976','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx03.020909_0211.125.fits'),('wx03.020909_0222.130.3977','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx03.020909_0222.130.fits'),('wx03.020909_0238.135.3978','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx03.020909_0238.135.fits'),('wx03.020909_0250.140.3979','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx03.020909_0250.140.fits'),('wx03.020909_0301.145.3980','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx03.020909_0301.145.fits'),('wx04.020909_0344.150.3981','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx04.020909_0344.150.fits'),('wx04.020909_0401.155.3982','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx04.020909_0401.155.fits'),('wx04.020909_0414.160.3983','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx04.020909_0414.160.fits'),('wx04.020909_0426.165.3984','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx04.020909_0426.165.fits'),('wx04.020909_0440.170.3985','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx04.020909_0440.170.fits'),('wx04.020909_0500.168.3986','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx04.020909_0500.168.fits'),('wx05.020909_0348.151.3987','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx05.020909_0348.151.fits'),('wx05.020909_0403.156.3988','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx05.020909_0403.156.fits'),('wx05.020909_0416.161.3989','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx05.020909_0416.161.fits'),('wx05.020909_0428.166.3990','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx05.020909_0428.166.fits'),('wx05.020909_0442.171.3991','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx05.020909_0442.171.fits'),('wx05.020909_0502.169.3992','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx05.020909_0502.169.fits'),('wx06.020909_0352.152.3993','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx06.020909_0352.152.fits'),('wx06.020909_0406.157.3994','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx06.020909_0406.157.fits'),('wx06.020909_0418.162.3995','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx06.020909_0418.162.fits'),('wx06.020909_0431.167.3996','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx06.020909_0431.167.fits'),('wx06.020909_0453.165.3997','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx06.020909_0453.165.fits'),('wx06.020909_0504.170.3998','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx06.020909_0504.170.fits'),('wx07.020909_0354.153.3999','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx07.020909_0354.153.fits'),('wx07.020909_0408.158.4000','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx07.020909_0408.158.fits'),('wx07.020909_0420.163.4001','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx07.020909_0420.163.fits'),('wx07.020909_0434.168.4002','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx07.020909_0434.168.fits'),('wx07.020909_0455.166.4003','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx07.020909_0455.166.fits'),('wx07.020909_0506.171.4004','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx07.020909_0506.171.fits'),('wx08.020909_0357.154.4005','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx08.020909_0357.154.fits'),('wx08.020909_0411.159.4006','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx08.020909_0411.159.fits'),('wx08.020909_0422.164.4007','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx08.020909_0422.164.fits'),('wx08.020909_0437.169.4008','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx08.020909_0437.169.fits'),('wx08.020909_0457.167.4009','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx08.020909_0457.167.fits'),('wx08.020909_0509.172.4010','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx08.020909_0509.172.fits'),('wx09.020909_0541.177.4011','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx09.020909_0541.177.fits'),('wx09.020909_0556.182.4012','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx09.020909_0556.182.fits'),('wx09.020909_0608.187.4013','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx09.020909_0608.187.fits'),('wx09.020909_0619.192.4014','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx09.020909_0619.192.fits'),('wx09.020909_0634.197.4015','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx09.020909_0634.197.fits'),('wx09.020909_0645.202.4016','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx09.020909_0645.202.fits'),('wx10.020909_0544.178.4017','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx10.020909_0544.178.fits'),('wx10.020909_0558.183.4018','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx10.020909_0558.183.fits'),('wx10.020909_0609.188.4019','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx10.020909_0609.188.fits'),('wx10.020909_0621.193.4020','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx10.020909_0621.193.fits'),('wx10.020909_0636.198.4021','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx10.020909_0636.198.fits'),('wx10.020909_0647.203.4022','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx10.020909_0647.203.fits'),('wx11.020909_0547.179.4023','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx11.020909_0547.179.fits'),('wx11.020909_0600.184.4024','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx11.020909_0600.184.fits'),('wx11.020909_0611.189.4025','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx11.020909_0611.189.fits'),('wx11.020909_0626.194.4026','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx11.020909_0626.194.fits'),('wx11.020909_0639.199.4027','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx11.020909_0639.199.fits'),('wx11.020909_0650.204.4028','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx11.020909_0650.204.fits'),('wx12.020909_0550.180.4029','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx12.020909_0550.180.fits'),('wx12.020909_0602.185.4030','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx12.020909_0602.185.fits'),('wx12.020909_0614.190.4031','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx12.020909_0614.190.fits'),('wx12.020909_0628.195.4032','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx12.020909_0628.195.fits'),('wx12.020909_0641.200.4033','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx12.020909_0641.200.fits'),('wx12.020909_0652.205.4034','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx12.020909_0652.205.fits'),('wx13.020909_0553.181.4035','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx13.020909_0553.181.fits'),('wx13.020909_0605.186.4036','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx13.020909_0605.186.fits'),('wx13.020909_0615.191.4037','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx13.020909_0615.191.fits'),('wx13.020909_0631.196.4038','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx13.020909_0631.196.fits'),('wx13.020909_0643.201.4039','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx13.020909_0643.201.fits'),('wx13.020909_0654.206.4040','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx13.020909_0654.206.fits'),('wx14.020909_0708.209.4041','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx14.020909_0708.209.fits'),('wx14.020909_0718.213.4042','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx14.020909_0718.213.fits'),('wx14.020909_0726.217.4043','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx14.020909_0726.217.fits'),('wx14.020909_0741.221.4044','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx14.020909_0741.221.fits'),('wx14.020909_0753.225.4045','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx14.020909_0753.225.fits'),('wx14.020909_0802.229.4046','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx14.020909_0802.229.fits'),('wx15.020909_0711.210.4047','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx15.020909_0711.210.fits'),('wx15.020909_0719.214.4048','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx15.020909_0719.214.fits'),('wx15.020909_0733.218.4049','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx15.020909_0733.218.fits'),('wx15.020909_0746.222.4050','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx15.020909_0746.222.fits'),('wx15.020909_0755.226.4051','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx15.020909_0755.226.fits'),('wx15.020909_0804.230.4052','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx15.020909_0804.230.fits'),('wx17.020909_0813.231.4053','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx17.020909_0813.231.fits'),('wx17.020909_0830.234.4054','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx17.020909_0830.234.fits'),('wx17.020909_0837.237.4055','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx17.020909_0837.237.fits'),('wx17.020909_0846.240.4056','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx17.020909_0846.240.fits'),('wx17.020909_0902.243.4057','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx17.020909_0902.243.fits'),('wx17.020909_0910.246.4058','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx17.020909_0910.246.fits'),('wx18.020909_0819.232.4059','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx18.020909_0819.232.fits'),('wx18.020909_0831.235.4060','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx18.020909_0831.235.fits'),('wx18.020909_0840.238.4061','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx18.020909_0840.238.fits'),('wx18.020909_0851.241.4062','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx18.020909_0851.241.fits'),('wx18.020909_0905.244.4063','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx18.020909_0905.244.fits'),('wx18.020909_0914.247.4064','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx18.020909_0914.247.fits'),('wx19.020909_0928.250.4065','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx19.020909_0928.250.fits'),('wx19.020909_0944.253.4066','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx19.020909_0944.253.fits'),('wx19.020909_0954.256.4067','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx19.020909_0954.256.fits'),('wx20.020909_0935.251.4068','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx20.020909_0935.251.fits'),('wx20.020909_0947.254.4069','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx20.020909_0947.254.fits'),('wx20.020909_0958.257.4070','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/wx20.020909_0958.257.fits'),('wxa1.020928_0023.058.3802','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxa1.020928_0023.058.fits'),('wxa1.020928_0034.063.3804','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxa1.020928_0034.063.fits'),('wxa1.020929_2338.034.3299','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxa1.020929_2338.034.fits'),('wxa1.020929_2352.039.3301','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxa1.020929_2352.039.fits'),('wxa1.020930_0007.044.3305','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxa1.020930_0007.044.fits'),('wxa1.021001_2348.033.2518','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxa1.021001_2348.033.fits'),('wxa1.021002_0000.038.2522','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxa1.021002_0000.038.fits'),('wxa1.021002_0013.043.2526','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxa1.021002_0013.043.fits'),('wxa1.021003_2351.037.1838','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxa1.021003_2351.037.fits'),('wxa1.021004_0003.042.1841','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxa1.021004_0003.042.fits'),('wxa1.021004_0016.047.1846','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxa1.021004_0016.047.fits'),('wxa1.021006_0017.059.1089','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxa1.021006_0017.059.fits'),('wxa1.021006_0029.064.1093','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxa1.021006_0029.064.fits'),('wxa1.021006_0043.069.1097','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxa1.021006_0043.069.fits'),('wxa1.021008_0003.042.388','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxa1.021008_0003.042.fits'),('wxa1.021008_0019.047.392','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxa1.021008_0019.047.fits'),('wxa1.021010_0001.041.3461','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxa1.021010_0001.041.fits'),('wxa1.021010_0023.046.3465','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxa1.021010_0023.046.fits'),('wxa1.021010_0322.089.3469','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxa1.021010_0322.089.fits'),('wxa1.021011_2355.043.2922','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxa1.021011_2355.043.fits'),('wxa1.021012_0018.047.2926','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxa1.021012_0018.047.fits'),('wxa1.021014_0041.055.2286','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxa1.021014_0041.055.fits'),('wxa1.021014_0105.060.2288','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxa1.021014_0105.060.fits'),('wxa1.021030_0006.036.1737','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxa1.021030_0006.036.fits'),('wxa1.021030_0016.038.1741','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxa1.021030_0016.038.fits'),('wxa1.021030_0040.040.1745','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxa1.021030_0040.040.fits'),('wxa1.021105_0010.039.640','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxa1.021105_0010.039.fits'),('wxa1.021105_0020.041.644','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxa1.021105_0020.041.fits'),('wxa1.021105_0044.043.648','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxa1.021105_0044.043.fits'),('wxa1.021111_0028.051.3384','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxa1.021111_0028.051.fits'),('wxa1.021111_0056.054.3388','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxa1.021111_0056.054.fits'),('wxa1.021115_0034.045.2203','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxa1.021115_0034.045.fits'),('wxa1.021115_0047.047.2206','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxa1.021115_0047.047.fits'),('wxa1.021128_0047.033.1650','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxa1.021128_0047.033.fits'),('wxa1.021128_0056.035.1654','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxa1.021128_0056.035.fits'),('wxa1.021204_0043.038.391','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxa1.021204_0043.038.fits'),('wxa1.021204_0055.040.395','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxa1.021204_0055.040.fits'),('wxa1.021206_0043.040.3365','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxa1.021206_0043.040.fits'),('wxa1.021206_0053.042.3369','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxa1.021206_0053.042.fits'),('wxa1.021206_0118.044.3373','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxa1.021206_0118.044.fits'),('wxb1.020928_0010.054.3806','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxb1.020928_0010.054.fits'),('wxb1.020928_0025.059.3808','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxb1.020928_0025.059.fits'),('wxb1.020928_0037.064.3810','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxb1.020928_0037.064.fits'),('wxb1.020929_2342.035.3310','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxb1.020929_2342.035.fits'),('wxb1.020929_2355.040.3313','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxb1.020929_2355.040.fits'),('wxb1.020930_0009.045.3319','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxb1.020930_0009.045.fits'),('wxb1.021001_2351.034.2529','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxb1.021001_2351.034.fits'),('wxb1.021002_0002.039.2533','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxb1.021002_0002.039.fits'),('wxb1.021002_0016.044.2537','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxb1.021002_0016.044.fits'),('wxb1.021003_2354.038.1849','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxb1.021003_2354.038.fits'),('wxb1.021004_0005.043.1853','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxb1.021004_0005.043.fits'),('wxb1.021004_0018.048.1857','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxb1.021004_0018.048.fits'),('wxb1.021006_0019.060.1101','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxb1.021006_0019.060.fits'),('wxb1.021006_0032.065.1105','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxb1.021006_0032.065.fits'),('wxb1.021006_0045.070.1109','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxb1.021006_0045.070.fits'),('wxb1.021008_0008.043.396','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxb1.021008_0008.043.fits'),('wxb1.021008_0022.048.400','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxb1.021008_0022.048.fits'),('wxb1.021010_0004.042.3473','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxb1.021010_0004.042.fits'),('wxb1.021010_0025.047.3477','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxb1.021010_0025.047.fits'),('wxb1.021010_0330.090.3481','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxb1.021010_0330.090.fits'),('wxb1.021011_2358.044.2930','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxb1.021011_2358.044.fits'),('wxb1.021012_0021.048.2934','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxb1.021012_0021.048.fits'),('wxb1.021014_0046.056.2294','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxb1.021014_0046.056.fits'),('wxb1.021014_0108.061.2297','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxb1.021014_0108.061.fits'),('wxb1.021101_0023.046.1239','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxb1.021101_0023.046.fits'),('wxb1.021101_0034.048.1243','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxb1.021101_0034.048.fits'),('wxb1.021101_0059.050.1247','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxb1.021101_0059.050.fits'),('wxb1.021109_0025.044.3751','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxb1.021109_0025.044.fits'),('wxb1.021109_0036.046.3753','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxb1.021109_0036.046.fits'),('wxb1.021109_0100.048.3755','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxb1.021109_0100.048.fits'),('wxb1.021113_0040.046.2756','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxb1.021113_0040.046.fits'),('wxb1.021113_0050.048.2760','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxb1.021113_0050.048.fits'),('wxb1.021113_0114.050.2765','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxb1.021113_0114.050.fits'),('wxb1.021130_0048.041.1178','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxb1.021130_0048.041.fits'),('wxb1.021130_0059.043.1182','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxb1.021130_0059.043.fits'),('wxb1.021130_0123.045.1186','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxb1.021130_0123.045.fits'),('wxb1.021202_0030.048.887','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxb1.021202_0030.048.fits'),('wxb1.021202_0042.050.891','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxb1.021202_0042.050.fits'),('wxb1.021202_0106.052.895','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxb1.021202_0106.052.fits'),('wxb1.021208_0041.041.2909','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxb1.021208_0041.041.fits'),('wxb1.021208_0052.043.2912','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxb1.021208_0052.043.fits'),('wxb1.021208_0128.045.2916','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxb1.021208_0128.045.fits'),('wxb1.021210_0042.039.2624','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxb1.021210_0042.039.fits'),('wxb1.021210_0049.040.2628','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxb1.021210_0049.040.fits'),('wxb1.021210_0056.041.2632','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxb1.021210_0056.041.fits'),('wxb1.021216_0050.042.1495','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxb1.021216_0050.042.fits'),('wxb1.021216_0055.043.1499','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxb1.021216_0055.043.fits'),('wxb1.021229_0054.046.993','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxb1.021229_0054.046.fits'),('wxb1.021229_0102.047.997','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxb1.021229_0102.047.fits'),('wxb1.021229_0114.048.1001','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxb1.021229_0114.048.fits'),('wxb1.030103_0119.044.458','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxb1.030103_0119.044.fits'),('wxb1.030103_0122.045.462','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxb1.030103_0122.045.fits'),('wxc1.020928_0014.055.3812','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxc1.020928_0014.055.fits'),('wxc1.020928_0028.060.3815','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxc1.020928_0028.060.fits'),('wxc1.020928_0039.065.3816','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxc1.020928_0039.065.fits'),('wxc1.020929_2344.036.3321','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxc1.020929_2344.036.fits'),('wxc1.020929_2358.041.3326','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxc1.020929_2358.041.fits'),('wxc1.020930_0011.046.3330','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxc1.020930_0011.046.fits'),('wxc1.021001_2354.035.2541','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxc1.021001_2354.035.fits'),('wxc1.021002_0005.040.2546','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxc1.021002_0005.040.fits'),('wxc1.021002_0019.045.2550','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxc1.021002_0019.045.fits'),('wxc1.021003_2356.039.1861','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxc1.021003_2356.039.fits'),('wxc1.021004_0008.044.1865','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxc1.021004_0008.044.fits'),('wxc1.021004_0020.049.1869','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxc1.021004_0020.049.fits'),('wxc1.021004_0202.039.1873','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxc1.021004_0202.039.fits'),('wxc1.021004_0207.040.1877','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxc1.021004_0207.040.fits'),('wxc1.021004_0332.042.1881','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxc1.021004_0332.042.fits'),('wxc1.021004_0346.043.1885','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxc1.021004_0346.043.fits'),('wxc1.021004_0401.044.1889','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxc1.021004_0401.044.fits'),('wxc1.021006_0024.061.1112','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxc1.021006_0024.061.fits'),('wxc1.021006_0034.066.1116','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxc1.021006_0034.066.fits'),('wxc1.021006_0047.071.1120','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxc1.021006_0047.071.fits'),('wxc1.021008_0011.044.404','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxc1.021008_0011.044.fits'),('wxc1.021008_0025.049.408','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxc1.021008_0025.049.fits'),('wxc1.021010_0007.043.3485','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxc1.021010_0007.043.fits'),('wxc1.021010_0028.048.3489','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxc1.021010_0028.048.fits'),('wxc1.021010_0335.091.3493','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxc1.021010_0335.091.fits'),('wxc1.021012_0001.045.2938','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxc1.021012_0001.045.fits'),('wxc1.021012_0023.049.2942','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxc1.021012_0023.049.fits'),('wxc1.021014_0049.057.2300','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxc1.021014_0049.057.fits'),('wxc1.021014_0111.062.2306','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxc1.021014_0111.062.fits'),('wxc1.021030_0009.037.1749','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxc1.021030_0009.037.fits'),('wxc1.021030_0022.039.1751','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxc1.021030_0022.039.fits'),('wxc1.021030_0043.041.1755','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxc1.021030_0043.041.fits'),('wxc1.021030_0046.042.1759','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxc1.021030_0046.042.fits'),('wxc1.021103_0256.081.785','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/wxc1.021103_0256.081.fits'),('wxc1.021105_0014.040.652','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxc1.021105_0014.040.fits'),('wxc1.021105_0026.042.656','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxc1.021105_0026.042.fits'),('wxc1.021105_0047.044.660','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxc1.021105_0047.044.fits'),('wxc1.021111_0031.052.3392','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxc1.021111_0031.052.fits'),('wxc1.021111_0038.053.3396','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxc1.021111_0038.053.fits'),('wxc1.021111_0059.055.3400','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxc1.021111_0059.055.fits'),('wxc1.021115_0039.046.2210','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxc1.021115_0039.046.fits'),('wxc1.021115_0052.048.2213','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxc1.021115_0052.048.fits'),('wxc1.021128_0049.034.1658','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxc1.021128_0049.034.fits'),('wxc1.021128_0102.036.1662','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxc1.021128_0102.036.fits'),('wxc1.021204_0048.039.399','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxc1.021204_0048.039.fits'),('wxc1.021204_0100.041.403','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxc1.021204_0100.041.fits'),('wxc1.021206_0047.041.3377','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxc1.021206_0047.041.fits'),('wxc1.021206_0059.043.3381','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxc1.021206_0059.043.fits'),('wxc1.021206_0121.045.3385','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxc1.021206_0121.045.fits'),('wxc1.021214_0047.045.2044','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wxc1.021214_0047.045.fits'),('wxc1.021214_0058.046.2048','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wxc1.021214_0058.046.fits'),('wxc1.021214_0108.047.2052','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wxc1.021214_0108.047.fits'),('wxd1.020928_0017.056.3818','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxd1.020928_0017.056.fits'),('wxd1.020928_0041.066.3820','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxd1.020928_0041.066.fits'),('wxd1.020929_2347.037.3335','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxd1.020929_2347.037.fits'),('wxd1.020930_0001.042.3339','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxd1.020930_0001.042.fits'),('wxd1.020930_0013.047.3343','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxd1.020930_0013.047.fits'),('wxd1.021001_2356.036.2554','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxd1.021001_2356.036.fits'),('wxd1.021002_0008.041.2558','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxd1.021002_0008.041.fits'),('wxd1.021002_0021.046.2562','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxd1.021002_0021.046.fits'),('wxd1.021003_2358.040.1893','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxd1.021003_2358.040.fits'),('wxd1.021004_0011.045.1897','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxd1.021004_0011.045.fits'),('wxd1.021004_0022.050.1901','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxd1.021004_0022.050.fits'),('wxd1.021006_0025.062.1124','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxd1.021006_0025.062.fits'),('wxd1.021006_0037.067.1128','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxd1.021006_0037.067.fits'),('wxd1.021006_0049.072.1132','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxd1.021006_0049.072.fits'),('wxd1.021008_0013.045.412','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxd1.021008_0013.045.fits'),('wxd1.021008_0027.050.416','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxd1.021008_0027.050.fits'),('wxd1.021010_0013.044.3496','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxd1.021010_0013.044.fits'),('wxd1.021010_0031.049.3499','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxd1.021010_0031.049.fits'),('wxd1.021010_0354.092.3502','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxd1.021010_0354.092.fits'),('wxd1.021012_0008.046.2946','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxd1.021012_0008.046.fits'),('wxd1.021012_0026.050.2950','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxd1.021012_0026.050.fits'),('wxd1.021014_0056.058.2310','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxd1.021014_0056.058.fits'),('wxd1.021014_0113.063.2314','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxd1.021014_0113.063.fits'),('wxd1.021101_0028.047.1251','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxd1.021101_0028.047.fits'),('wxd1.021101_0040.049.1255','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxd1.021101_0040.049.fits'),('wxd1.021101_0102.051.1259','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxd1.021101_0102.051.fits'),('wxd1.021107_0251.101.103','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/wxd1.021107_0251.101.fits'),('wxd1.021109_0029.045.3757','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxd1.021109_0029.045.fits'),('wxd1.021109_0041.047.3759','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxd1.021109_0041.047.fits'),('wxd1.021109_0103.049.3761','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxd1.021109_0103.049.fits'),('wxd1.021113_0043.047.2769','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxd1.021113_0043.047.fits'),('wxd1.021113_0055.049.2773','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxd1.021113_0055.049.fits'),('wxd1.021113_0117.051.2777','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxd1.021113_0117.051.fits'),('wxd1.021113_0242.056.2780','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxd1.021113_0242.056.fits'),('wxd1.021113_0253.057.2784','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxd1.021113_0253.057.fits'),('wxd1.021130_0053.042.1190','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxd1.021130_0053.042.fits'),('wxd1.021130_0105.044.1194','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxd1.021130_0105.044.fits'),('wxd1.021130_0126.046.1198','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxd1.021130_0126.046.fits'),('wxd1.021202_0036.049.899','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxd1.021202_0036.049.fits'),('wxd1.021202_0048.051.903','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxd1.021202_0048.051.fits'),('wxd1.021202_0109.053.907','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxd1.021202_0109.053.fits'),('wxd1.021208_0044.042.2920','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxd1.021208_0044.042.fits'),('wxd1.021208_0107.044.2925','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxd1.021208_0107.044.fits'),('wxd1.021208_0131.046.2927','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxd1.021208_0131.046.fits'),('wxe1.020928_0021.057.3821','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxe1.020928_0021.057.fits'),('wxe1.020928_0032.062.3822','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxe1.020928_0032.062.fits'),('wxe1.020928_0043.067.3823','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxe1.020928_0043.067.fits'),('wxe1.020929_2349.038.3347','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxe1.020929_2349.038.fits'),('wxe1.020930_0004.043.3351','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxe1.020930_0004.043.fits'),('wxe1.020930_0015.048.3355','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxe1.020930_0015.048.fits'),('wxe1.021001_2358.037.2566','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxe1.021001_2358.037.fits'),('wxe1.021002_0011.042.2570','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxe1.021002_0011.042.fits'),('wxe1.021002_0023.047.2574','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxe1.021002_0023.047.fits'),('wxe1.021004_0001.041.1905','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxe1.021004_0001.041.fits'),('wxe1.021004_0013.046.1909','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxe1.021004_0013.046.fits'),('wxe1.021004_0025.051.1913','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxe1.021004_0025.051.fits'),('wxe1.021006_0027.063.1136','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxe1.021006_0027.063.fits'),('wxe1.021006_0040.068.1140','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxe1.021006_0040.068.fits'),('wxe1.021006_0051.073.1144','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxe1.021006_0051.073.fits'),('wxe1.021008_0016.046.420','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxe1.021008_0016.046.fits'),('wxe1.021008_0030.051.424','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxe1.021008_0030.051.fits'),('wxe1.021010_0020.045.3505','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxe1.021010_0020.045.fits'),('wxe1.021010_0034.050.3508','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxe1.021010_0034.050.fits'),('wxe1.021012_0029.051.2954','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxe1.021012_0029.051.fits'),('wxe1.021014_0102.059.2317','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxe1.021014_0102.059.fits'),('wxe1.021014_0116.064.2320','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxe1.021014_0116.064.fits'),('wxf1.020928_0109.068.3824','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxf1.020928_0109.068.fits'),('wxf1.020928_0118.073.3825','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxf1.020928_0118.073.fits'),('wxf1.020928_0133.078.3826','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxf1.020928_0133.078.fits'),('wxf1.020930_0017.049.3359','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxf1.020930_0017.049.fits'),('wxf1.020930_0043.038.3363','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxf1.020930_0043.038.fits'),('wxf1.020930_0057.043.3367','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxf1.020930_0057.043.fits'),('wxf1.021002_0025.048.2578','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxf1.021002_0025.048.fits'),('wxf1.021002_0037.053.2582','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxf1.021002_0037.053.fits'),('wxf1.021002_0051.058.2586','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxf1.021002_0051.058.fits'),('wxf1.021004_0027.052.1917','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxf1.021004_0027.052.fits'),('wxf1.021004_0037.057.1921','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxf1.021004_0037.057.fits'),('wxf1.021004_0051.062.1925','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxf1.021004_0051.062.fits'),('wxf1.021006_0053.074.1148','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxf1.021006_0053.074.fits'),('wxf1.021006_0105.079.1152','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxf1.021006_0105.079.fits'),('wxf1.021006_0120.084.1156','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxf1.021006_0120.084.fits'),('wxf1.021008_0035.052.429','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxf1.021008_0035.052.fits'),('wxf1.021008_0051.057.433','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxf1.021008_0051.057.fits'),('wxf1.021010_0036.051.3511','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxf1.021010_0036.051.fits'),('wxf1.021010_0058.056.3514','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxf1.021010_0058.056.fits'),('wxf1.021010_0412.093.3517','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxf1.021010_0412.093.fits'),('wxf1.021010_0454.097.3520','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxf1.021010_0454.097.fits'),('wxf1.021012_0031.052.2958','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxf1.021012_0031.052.fits'),('wxf1.021012_0056.048.2962','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxf1.021012_0056.048.fits'),('wxf1.021014_0119.065.2324','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxf1.021014_0119.065.fits'),('wxf1.021014_0141.070.2328','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxf1.021014_0141.070.fits'),('wxf1.021030_0048.043.1763','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxf1.021030_0048.043.fits'),('wxf1.021030_0058.045.1767','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxf1.021030_0058.045.fits'),('wxf1.021030_0122.047.1771','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxf1.021030_0122.047.fits'),('wxf1.021105_0050.045.664','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxf1.021105_0050.045.fits'),('wxf1.021105_0059.047.668','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxf1.021105_0059.047.fits'),('wxf1.021105_0123.049.672','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxf1.021105_0123.049.fits'),('wxf1.021111_0102.056.3404','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxf1.021111_0102.056.fits'),('wxf1.021111_0130.059.3408','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxf1.021111_0130.059.fits'),('wxf1.021128_0122.037.1667','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxf1.021128_0122.037.fits'),('wxf1.021128_0133.039.1671','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxf1.021128_0133.039.fits'),('wxf1.021204_0126.042.407','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxf1.021204_0126.042.fits'),('wxf1.021204_0144.044.411','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxf1.021204_0144.044.fits'),('wxf1.021206_0124.046.3391','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxf1.021206_0124.046.fits'),('wxf1.021206_0134.048.3394','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxf1.021206_0134.048.fits'),('wxf1.021206_0158.050.3397','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxf1.021206_0158.050.fits'),('wxg1.020928_0110.069.3827','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxg1.020928_0110.069.fits'),('wxg1.020928_0122.074.3828','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxg1.020928_0122.074.fits'),('wxg1.020930_0033.034.3371','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxg1.020930_0033.034.fits'),('wxg1.020930_0046.039.3375','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxg1.020930_0046.039.fits'),('wxg1.020930_0100.044.3379','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxg1.020930_0100.044.fits'),('wxg1.021002_0027.049.2590','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxg1.021002_0027.049.fits'),('wxg1.021002_0040.054.2594','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxg1.021002_0040.054.fits'),('wxg1.021002_0053.059.2598','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxg1.021002_0053.059.fits'),('wxg1.021004_0029.053.1929','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxg1.021004_0029.053.fits'),('wxg1.021004_0040.058.1933','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxg1.021004_0040.058.fits'),('wxg1.021004_0053.063.1937','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxg1.021004_0053.063.fits'),('wxg1.021006_0055.075.1160','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxg1.021006_0055.075.fits'),('wxg1.021006_0108.080.1164','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxg1.021006_0108.080.fits'),('wxg1.021006_0122.085.1168','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxg1.021006_0122.085.fits'),('wxg1.021008_0037.053.437','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxg1.021008_0037.053.fits'),('wxg1.021008_0054.058.441','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxg1.021008_0054.058.fits'),('wxg1.021010_0039.052.3523','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxg1.021010_0039.052.fits'),('wxg1.021010_0100.057.3526','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxg1.021010_0100.057.fits'),('wxg1.021010_0418.094.3529','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxg1.021010_0418.094.fits'),('wxg1.021010_0459.098.3532','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxg1.021010_0459.098.fits'),('wxg1.021012_0034.053.2966','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxg1.021012_0034.053.fits'),('wxg1.021012_0059.049.2970','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxg1.021012_0059.049.fits'),('wxg1.021014_0121.066.2332','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxg1.021014_0121.066.fits'),('wxg1.021014_0143.071.2337','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxg1.021014_0143.071.fits'),('wxg1.021014_0153.055.2341','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxg1.021014_0153.055.fits'),('wxg1.021101_0105.052.1263','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxg1.021101_0105.052.fits'),('wxg1.021101_0115.054.1267','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxg1.021101_0115.054.fits'),('wxg1.021101_0140.056.1271','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxg1.021101_0140.056.fits'),('wxg1.021109_0105.050.3763','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxg1.021109_0105.050.fits'),('wxg1.021109_0116.052.3765','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxg1.021109_0116.052.fits'),('wxg1.021109_0140.054.3767','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxg1.021109_0140.054.fits'),('wxg1.021113_0119.052.2787','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxg1.021113_0119.052.fits'),('wxg1.021113_0132.046.2792','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxg1.021113_0132.046.fits'),('wxg1.021113_0159.049.2794','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxg1.021113_0159.049.fits'),('wxg1.021130_0129.047.1202','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxg1.021130_0129.047.fits'),('wxg1.021130_0138.049.1206','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxg1.021130_0138.049.fits'),('wxg1.021130_0202.051.1210','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxg1.021130_0202.051.fits'),('wxg1.021202_0112.054.911','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxg1.021202_0112.054.fits'),('wxg1.021202_0121.056.915','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxg1.021202_0121.056.fits'),('wxg1.021202_0145.058.919','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxg1.021202_0145.058.fits'),('wxh1.020928_0112.070.3829','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxh1.020928_0112.070.fits'),('wxh1.020928_0125.075.3830','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxh1.020928_0125.075.fits'),('wxh1.020928_0137.080.3831','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxh1.020928_0137.080.fits'),('wxh1.020930_0036.035.3383','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxh1.020930_0036.035.fits'),('wxh1.021002_0030.050.2602','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxh1.021002_0030.050.fits'),('wxh1.021002_0043.055.2606','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxh1.021002_0043.055.fits'),('wxh1.021002_0055.060.2610','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxh1.021002_0055.060.fits'),('wxh1.021004_0031.054.1941','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxh1.021004_0031.054.fits'),('wxh1.021004_0043.059.1945','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxh1.021004_0043.059.fits'),('wxh1.021004_0055.064.1949','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxh1.021004_0055.064.fits'),('wxh1.021006_0059.076.1172','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxh1.021006_0059.076.fits'),('wxh1.021006_0111.081.1176','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxh1.021006_0111.081.fits'),('wxh1.021006_0124.086.1181','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxh1.021006_0124.086.fits'),('wxh1.021008_0043.054.445','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxh1.021008_0043.054.fits'),('wxh1.021008_0057.059.449','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxh1.021008_0057.059.fits'),('wxh1.021010_0042.053.3535','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxh1.021010_0042.053.fits'),('wxh1.021010_0103.058.3538','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxh1.021010_0103.058.fits'),('wxh1.021010_0504.099.3541','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxh1.021010_0504.099.fits'),('wxh1.021012_0037.054.2973','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxh1.021012_0037.054.fits'),('wxh1.021012_0102.050.2977','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxh1.021012_0102.050.fits'),('wxh1.021014_0124.067.2345','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxh1.021014_0124.067.fits'),('wxh1.021014_0156.056.2349','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxh1.021014_0156.056.fits'),('wxh1.021030_0051.044.1775','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxh1.021030_0051.044.fits'),('wxh1.021030_0103.046.1779','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxh1.021030_0103.046.fits'),('wxh1.021030_0125.048.1783','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxh1.021030_0125.048.fits'),('wxh1.021030_0128.049.1787','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxh1.021030_0128.049.fits'),('wxh1.021103_0303.082.789','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/wxh1.021103_0303.082.fits'),('wxh1.021105_0052.046.676','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxh1.021105_0052.046.fits'),('wxh1.021105_0104.048.680','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxh1.021105_0104.048.fits'),('wxh1.021105_0125.050.684','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxh1.021105_0125.050.fits'),('wxh1.021111_0105.057.3412','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxh1.021111_0105.057.fits'),('wxh1.021111_0112.058.3416','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxh1.021111_0112.058.fits'),('wxh1.021111_0137.060.3420','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxh1.021111_0137.060.fits'),('wxh1.021128_0126.038.1675','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxh1.021128_0126.038.fits'),('wxh1.021128_0138.040.1679','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxh1.021128_0138.040.fits'),('wxh1.021204_0133.043.415','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxh1.021204_0133.043.fits'),('wxh1.021204_0156.045.419','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxh1.021204_0156.045.fits'),('wxh1.021206_0127.047.3401','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxh1.021206_0127.047.fits'),('wxh1.021206_0139.049.3405','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxh1.021206_0139.049.fits'),('wxh1.021206_0201.051.3409','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxh1.021206_0201.051.fits'),('wxh1.021210_0059.042.2636','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxh1.021210_0059.042.fits'),('wxh1.021210_0107.043.2640','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxh1.021210_0107.043.fits'),('wxh1.021210_0126.044.2644','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxh1.021210_0126.044.fits'),('wxh1.021216_0102.044.1503','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxh1.021216_0102.044.fits'),('wxh1.021216_0111.045.1507','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxh1.021216_0111.045.fits'),('wxh1.021216_0121.046.1511','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxh1.021216_0121.046.fits'),('wxh1.021229_0117.049.1005','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxh1.021229_0117.049.fits'),('wxh1.021229_0142.050.1010','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxh1.021229_0142.050.fits'),('wxh1.030103_0152.048.466','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxh1.030103_0152.048.fits'),('wxh1.030103_0200.049.470','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxh1.030103_0200.049.fits'),('wxi1.020928_0114.071.3832','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxi1.020928_0114.071.fits'),('wxi1.020928_0128.076.3833','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxi1.020928_0128.076.fits'),('wxi1.020928_0139.081.3834','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxi1.020928_0139.081.fits'),('wxi1.020930_0038.036.3387','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxi1.020930_0038.036.fits'),('wxi1.020930_0052.041.3390','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxi1.020930_0052.041.fits'),('wxi1.020930_0104.046.3395','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxi1.020930_0104.046.fits'),('wxi1.021002_0033.051.2614','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxi1.021002_0033.051.fits'),('wxi1.021002_0045.056.2618','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxi1.021002_0045.056.fits'),('wxi1.021002_0058.061.2622','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxi1.021002_0058.061.fits'),('wxi1.021004_0033.055.1953','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxi1.021004_0033.055.fits'),('wxi1.021004_0045.060.1957','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxi1.021004_0045.060.fits'),('wxi1.021004_0057.065.1961','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxi1.021004_0057.065.fits'),('wxi1.021006_0101.077.1184','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxi1.021006_0101.077.fits'),('wxi1.021006_0113.082.1188','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxi1.021006_0113.082.fits'),('wxi1.021006_0134.061.1192','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxi1.021006_0134.061.fits'),('wxi1.021008_0046.055.453','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxi1.021008_0046.055.fits'),('wxi1.021008_0100.060.457','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxi1.021008_0100.060.fits'),('wxi1.021010_0048.054.3544','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxi1.021010_0048.054.fits'),('wxi1.021010_0106.059.3547','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxi1.021010_0106.059.fits'),('wxi1.021010_0523.100.3550','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxi1.021010_0523.100.fits'),('wxi1.021012_0044.055.2981','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxi1.021012_0044.055.fits'),('wxi1.021012_0105.051.2985','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxi1.021012_0105.051.fits'),('wxi1.021014_0131.068.2352','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxi1.021014_0131.068.fits'),('wxi1.021014_0158.057.2356','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxi1.021014_0158.057.fits'),('wxi1.021101_0108.053.1275','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxi1.021101_0108.053.fits'),('wxi1.021101_0121.055.1279','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxi1.021101_0121.055.fits'),('wxi1.021101_0143.057.1283','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxi1.021101_0143.057.fits'),('wxi1.021107_0300.102.107','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/wxi1.021107_0300.102.fits'),('wxi1.021109_0109.051.3769','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxi1.021109_0109.051.fits'),('wxi1.021109_0121.053.3771','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxi1.021109_0121.053.fits'),('wxi1.021109_0144.055.3773','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxi1.021109_0144.055.fits'),('wxi1.021113_0122.053.2799','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxi1.021113_0122.053.fits'),('wxi1.021113_0137.047.2803','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxi1.021113_0137.047.fits'),('wxi1.021113_0147.048.2807','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxi1.021113_0147.048.fits'),('wxi1.021113_0202.050.2811','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxi1.021113_0202.050.fits'),('wxi1.021130_0132.048.1214','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxi1.021130_0132.048.fits'),('wxi1.021130_0144.050.1218','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxi1.021130_0144.050.fits'),('wxi1.021130_0205.052.1222','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxi1.021130_0205.052.fits'),('wxi1.021202_0115.055.923','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxi1.021202_0115.055.fits'),('wxi1.021202_0127.057.927','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxi1.021202_0127.057.fits'),('wxi1.021202_0148.059.929','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxi1.021202_0148.059.fits'),('wxj1.020928_0116.072.3835','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxj1.020928_0116.072.fits'),('wxj1.020928_0130.077.3836','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxj1.020928_0130.077.fits'),('wxj1.020928_0141.082.3837','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxj1.020928_0141.082.fits'),('wxj1.020930_0041.037.3398','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxj1.020930_0041.037.fits'),('wxj1.020930_0054.042.3402','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxj1.020930_0054.042.fits'),('wxj1.020930_0106.047.3406','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxj1.020930_0106.047.fits'),('wxj1.021002_0035.052.2626','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxj1.021002_0035.052.fits'),('wxj1.021002_0048.057.2630','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxj1.021002_0048.057.fits'),('wxj1.021002_0100.062.2634','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxj1.021002_0100.062.fits'),('wxj1.021004_0035.056.1965','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxj1.021004_0035.056.fits'),('wxj1.021004_0048.061.1969','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxj1.021004_0048.061.fits'),('wxj1.021004_0059.066.1973','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxj1.021004_0059.066.fits'),('wxj1.021006_0103.078.1196','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxj1.021006_0103.078.fits'),('wxj1.021006_0116.083.1200','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxj1.021006_0116.083.fits'),('wxj1.021006_0136.062.1204','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxj1.021006_0136.062.fits'),('wxj1.021008_0048.056.461','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxj1.021008_0048.056.fits'),('wxj1.021008_0104.061.465','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxj1.021008_0104.061.fits'),('wxj1.021010_0055.055.3553','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxj1.021010_0055.055.fits'),('wxj1.021010_0109.060.3556','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxj1.021010_0109.060.fits'),('wxj1.021012_0054.047.2989','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxj1.021012_0054.047.fits'),('wxj1.021012_0107.052.2993','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxj1.021012_0107.052.fits'),('wxj1.021014_0138.069.2360','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxj1.021014_0138.069.fits'),('wxj1.021014_0201.058.2364','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxj1.021014_0201.058.fits'),('wxk1.020928_0143.083.3838','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxk1.020928_0143.083.fits'),('wxk1.020928_0159.088.3839','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxk1.020928_0159.088.fits'),('wxk1.020930_0108.048.3410','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxk1.020930_0108.048.fits'),('wxk1.020930_0119.053.3415','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxk1.020930_0119.053.fits'),('wxk1.020930_0132.058.3419','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxk1.020930_0132.058.fits'),('wxk1.021002_0102.063.2638','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxk1.021002_0102.063.fits'),('wxk1.021002_0113.068.2642','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxk1.021002_0113.068.fits'),('wxk1.021002_0127.073.2646','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxk1.021002_0127.073.fits'),('wxk1.021004_0101.067.1977','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxk1.021004_0101.067.fits'),('wxk1.021004_0112.072.1981','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxk1.021004_0112.072.fits'),('wxk1.021004_0125.077.1985','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxk1.021004_0125.077.fits'),('wxk1.021006_0138.063.1208','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxk1.021006_0138.063.fits'),('wxk1.021006_0149.068.1212','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxk1.021006_0149.068.fits'),('wxk1.021006_0203.073.1216','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxk1.021006_0203.073.fits'),('wxk1.021008_0107.062.469','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxk1.021008_0107.062.fits'),('wxk1.021008_0122.067.473','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxk1.021008_0122.067.fits'),('wxk1.021010_0111.061.3559','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxk1.021010_0111.061.fits'),('wxk1.021010_0134.066.3562','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxk1.021010_0134.066.fits'),('wxk1.021012_0110.053.2997','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxk1.021012_0110.053.fits'),('wxk1.021012_0133.058.3002','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxk1.021012_0133.058.fits'),('wxk1.021014_0204.059.2368','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxk1.021014_0204.059.fits'),('wxk1.021014_0226.064.2372','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxk1.021014_0226.064.fits'),('wxk1.021030_0131.050.1791','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxk1.021030_0131.050.fits'),('wxk1.021030_0142.052.1795','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxk1.021030_0142.052.fits'),('wxk1.021030_0207.054.1799','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxk1.021030_0207.054.fits'),('wxk1.021105_0129.051.688','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxk1.021105_0129.051.fits'),('wxk1.021105_0138.053.547','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxk1.021105_0138.053.fits'),('wxk1.021105_0202.055.551','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxk1.021105_0202.055.fits'),('wxk1.021111_0146.061.3424','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxk1.021111_0146.061.fits'),('wxk1.021111_0214.064.3428','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxk1.021111_0214.064.fits'),('wxk1.021128_0159.041.1683','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxk1.021128_0159.041.fits'),('wxk1.021128_0209.043.1688','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxk1.021128_0209.043.fits'),('wxk1.021204_0214.046.423','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxk1.021204_0214.046.fits'),('wxk1.021204_0238.048.427','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxk1.021204_0238.048.fits'),('wxk1.021206_0204.052.3414','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxk1.021206_0204.052.fits'),('wxk1.021206_0215.054.3418','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxk1.021206_0215.054.fits'),('wxk1.021206_0240.056.3422','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxk1.021206_0240.056.fits'),('wxl1.020928_0145.084.3840','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxl1.020928_0145.084.fits'),('wxl1.020928_0201.089.3841','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxl1.020928_0201.089.fits'),('wxl1.020930_0111.049.3423','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxl1.020930_0111.049.fits'),('wxl1.020930_0121.054.3426','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxl1.020930_0121.054.fits'),('wxl1.021002_0104.064.2650','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxl1.021002_0104.064.fits'),('wxl1.021002_0116.069.2654','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxl1.021002_0116.069.fits'),('wxl1.021002_0129.074.2658','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxl1.021002_0129.074.fits'),('wxl1.021004_0104.068.1989','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxl1.021004_0104.068.fits'),('wxl1.021004_0114.073.1993','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxl1.021004_0114.073.fits'),('wxl1.021004_0127.078.1997','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxl1.021004_0127.078.fits'),('wxl1.021006_0141.064.1220','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxl1.021006_0141.064.fits'),('wxl1.021006_0152.069.1224','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxl1.021006_0152.069.fits'),('wxl1.021006_0205.074.1228','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxl1.021006_0205.074.fits'),('wxl1.021008_0110.063.477','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxl1.021008_0110.063.fits'),('wxl1.021008_0125.068.481','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxl1.021008_0125.068.fits'),('wxl1.021010_0114.062.3565','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxl1.021010_0114.062.fits'),('wxl1.021010_0137.067.3568','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxl1.021010_0137.067.fits'),('wxl1.021012_0113.054.3005','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxl1.021012_0113.054.fits'),('wxl1.021012_0136.059.3009','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxl1.021012_0136.059.fits'),('wxl1.021014_0207.060.2376','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxl1.021014_0207.060.fits'),('wxl1.021014_0229.065.2380','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxl1.021014_0229.065.fits'),('wxl1.021101_0146.058.1287','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxl1.021101_0146.058.fits'),('wxl1.021101_0156.060.1291','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxl1.021101_0156.060.fits'),('wxl1.021101_0220.062.1295','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxl1.021101_0220.062.fits'),('wxl1.021109_0146.056.3775','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxl1.021109_0146.056.fits'),('wxl1.021109_0158.058.3777','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxl1.021109_0158.058.fits'),('wxl1.021109_0223.060.3779','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxl1.021109_0223.060.fits'),('wxl1.021113_0206.051.2814','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxl1.021113_0206.051.fits'),('wxl1.021113_0215.053.2818','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxl1.021113_0215.053.fits'),('wxl1.021113_0304.058.2824','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxl1.021113_0304.058.fits'),('wxl1.021130_0207.053.1226','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxl1.021130_0207.053.fits'),('wxl1.021130_0221.041.1230','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxl1.021130_0221.041.fits'),('wxl1.021130_0245.043.1234','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxl1.021130_0245.043.fits'),('wxl1.021202_0150.060.933','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxl1.021202_0150.060.fits'),('wxl1.021202_0200.062.937','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxl1.021202_0200.062.fits'),('wxl1.021202_0224.064.941','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxl1.021202_0224.064.fits'),('wxm1.020928_0147.085.3842','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxm1.020928_0147.085.fits'),('wxm1.020928_0203.090.3843','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxm1.020928_0203.090.fits'),('wxm1.020930_0113.050.3430','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxm1.020930_0113.050.fits'),('wxm1.020930_0124.055.3435','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxm1.020930_0124.055.fits'),('wxm1.020930_0136.060.3439','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxm1.020930_0136.060.fits'),('wxm1.021002_0106.065.2662','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxm1.021002_0106.065.fits'),('wxm1.021002_0118.070.2666','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxm1.021002_0118.070.fits'),('wxm1.021002_0131.075.2670','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxm1.021002_0131.075.fits'),('wxm1.021004_0106.069.2001','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxm1.021004_0106.069.fits'),('wxm1.021004_0117.074.2005','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxm1.021004_0117.074.fits'),('wxm1.021004_0129.079.2009','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxm1.021004_0129.079.fits'),('wxm1.021006_0143.065.1232','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxm1.021006_0143.065.fits'),('wxm1.021006_0155.070.1237','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxm1.021006_0155.070.fits'),('wxm1.021006_0207.075.1241','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxm1.021006_0207.075.fits'),('wxm1.021008_0113.064.486','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxm1.021008_0113.064.fits'),('wxm1.021008_0128.069.490','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxm1.021008_0128.069.fits'),('wxm1.021010_0117.063.3571','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxm1.021010_0117.063.fits'),('wxm1.021010_0140.068.3574','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxm1.021010_0140.068.fits'),('wxm1.021012_0116.055.3014','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxm1.021012_0116.055.fits'),('wxm1.021012_0139.060.3017','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxm1.021012_0139.060.fits'),('wxm1.021014_0210.061.2384','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxm1.021014_0210.061.fits'),('wxm1.021014_0232.066.2388','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxm1.021014_0232.066.fits'),('wxm1.021030_0135.051.1803','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxm1.021030_0135.051.fits'),('wxm1.021030_0149.053.1807','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxm1.021030_0149.053.fits'),('wxm1.021030_0210.055.1811','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxm1.021030_0210.055.fits'),('wxm1.021030_0212.056.1815','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxm1.021030_0212.056.fits'),('wxm1.021103_0311.083.793','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/wxm1.021103_0311.083.fits'),('wxm1.021105_0132.052.555','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxm1.021105_0132.052.fits'),('wxm1.021105_0144.054.559','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxm1.021105_0144.054.fits'),('wxm1.021105_0205.056.563','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxm1.021105_0205.056.fits'),('wxm1.021111_0148.062.3432','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxm1.021111_0148.062.fits'),('wxm1.021111_0155.063.3436','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxm1.021111_0155.063.fits'),('wxm1.021111_0217.065.3440','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxm1.021111_0217.065.fits'),('wxm1.021128_0201.042.1691','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxm1.021128_0201.042.fits'),('wxm1.021128_0214.044.1695','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxm1.021128_0214.044.fits'),('wxm1.021204_0226.047.431','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxm1.021204_0226.047.fits'),('wxm1.021204_0250.049.435','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxm1.021204_0250.049.fits'),('wxm1.021206_0208.053.3427','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxm1.021206_0208.053.fits'),('wxm1.021206_0221.055.3431','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxm1.021206_0221.055.fits'),('wxm1.021206_0243.057.3434','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxm1.021206_0243.057.fits'),('wxm1.021210_0133.045.2648','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxm1.021210_0133.045.fits'),('wxm1.021210_0140.046.2652','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxm1.021210_0140.046.fits'),('wxm1.021210_0159.047.2656','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxm1.021210_0159.047.fits'),('wxm1.021216_0132.047.1515','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxm1.021216_0132.047.fits'),('wxm1.021216_0150.048.1519','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxm1.021216_0150.048.fits'),('wxm1.021216_0156.049.1522','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxm1.021216_0156.049.fits'),('wxm1.021216_0206.050.1526','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxm1.021216_0206.050.fits'),('wxm1.021216_0216.051.1530','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxm1.021216_0216.051.fits'),('wxm1.021229_0206.051.1014','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxm1.021229_0206.051.fits'),('wxm1.021229_0212.052.1018','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxm1.021229_0212.052.fits'),('wxm1.030103_0127.046.474','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxm1.030103_0127.046.fits'),('wxm1.030103_0134.047.478','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxm1.030103_0134.047.fits'),('wxn1.020928_0150.086.3844','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxn1.020928_0150.086.fits'),('wxn1.020928_0205.091.3845','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxn1.020928_0205.091.fits'),('wxn1.020930_0115.051.3442','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxn1.020930_0115.051.fits'),('wxn1.020930_0127.056.3446','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxn1.020930_0127.056.fits'),('wxn1.020930_0138.061.3450','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxn1.020930_0138.061.fits'),('wxn1.021002_0108.066.2674','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxn1.021002_0108.066.fits'),('wxn1.021002_0121.071.2678','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxn1.021002_0121.071.fits'),('wxn1.021002_0133.076.2682','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxn1.021002_0133.076.fits'),('wxn1.021004_0108.070.2013','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxn1.021004_0108.070.fits'),('wxn1.021004_0120.075.2017','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxn1.021004_0120.075.fits'),('wxn1.021004_0131.080.2021','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxn1.021004_0131.080.fits'),('wxn1.021006_0145.066.1245','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxn1.021006_0145.066.fits'),('wxn1.021006_0157.071.1249','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxn1.021006_0157.071.fits'),('wxn1.021006_0209.076.1253','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxn1.021006_0209.076.fits'),('wxn1.021008_0116.065.494','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxn1.021008_0116.065.fits'),('wxn1.021008_0131.070.498','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxn1.021008_0131.070.fits'),('wxn1.021010_0124.064.3577','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxn1.021010_0124.064.fits'),('wxn1.021010_0142.069.3580','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxn1.021010_0142.069.fits'),('wxn1.021012_0123.056.3021','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxn1.021012_0123.056.fits'),('wxn1.021012_0142.061.3025','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxn1.021012_0142.061.fits'),('wxn1.021014_0217.062.2392','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxn1.021014_0217.062.fits'),('wxn1.021014_0235.067.2396','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxn1.021014_0235.067.fits'),('wxn1.021101_0150.059.1299','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxn1.021101_0150.059.fits'),('wxn1.021101_0202.061.1303','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxn1.021101_0202.061.fits'),('wxn1.021101_0223.063.1307','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxn1.021101_0223.063.fits'),('wxn1.021107_0307.103.112','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/wxn1.021107_0307.103.fits'),('wxn1.021109_0151.057.3781','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxn1.021109_0151.057.fits'),('wxn1.021109_0204.059.3783','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxn1.021109_0204.059.fits'),('wxn1.021109_0226.061.3785','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxn1.021109_0226.061.fits'),('wxn1.021113_0208.052.2826','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxn1.021113_0208.052.fits'),('wxn1.021113_0221.054.2830','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxn1.021113_0221.054.fits'),('wxn1.021113_0231.055.2834','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxn1.021113_0231.055.fits'),('wxn1.021113_0307.059.2839','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxn1.021113_0307.059.fits'),('wxn1.021130_0211.054.1238','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxn1.021130_0211.054.fits'),('wxn1.021130_0227.042.1240','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxn1.021130_0227.042.fits'),('wxn1.021130_0248.044.1244','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxn1.021130_0248.044.fits'),('wxn1.021202_0153.061.945','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxn1.021202_0153.061.fits'),('wxn1.021202_0205.063.949','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxn1.021202_0205.063.fits'),('wxn1.021202_0226.065.953','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxn1.021202_0226.065.fits'),('wxn1.021208_0135.047.2932','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxn1.021208_0135.047.fits'),('wxn1.021208_0143.048.2936','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxn1.021208_0143.048.fits'),('wxn1.021208_0203.049.2940','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxn1.021208_0203.049.fits'),('wxo1.020928_0208.092.3846','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxo1.020928_0208.092.fits'),('wxo1.020930_0117.052.3455','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxo1.020930_0117.052.fits'),('wxo1.020930_0129.057.3459','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxo1.020930_0129.057.fits'),('wxo1.020930_0141.062.3463','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxo1.020930_0141.062.fits'),('wxo1.021002_0111.067.2686','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxo1.021002_0111.067.fits'),('wxo1.021002_0124.072.2690','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxo1.021002_0124.072.fits'),('wxo1.021002_0136.077.2694','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxo1.021002_0136.077.fits'),('wxo1.021004_0110.071.2025','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxo1.021004_0110.071.fits'),('wxo1.021004_0123.076.2029','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxo1.021004_0123.076.fits'),('wxo1.021004_0151.037.2033','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxo1.021004_0151.037.fits'),('wxo1.021006_0147.067.1257','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxo1.021006_0147.067.fits'),('wxo1.021006_0200.072.1261','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxo1.021006_0200.072.fits'),('wxo1.021006_0211.077.1265','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxo1.021006_0211.077.fits'),('wxo1.021008_0119.066.502','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxo1.021008_0119.066.fits'),('wxo1.021008_0134.071.506','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxo1.021008_0134.071.fits'),('wxo1.021010_0131.065.3583','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxo1.021010_0131.065.fits'),('wxo1.021010_0145.070.3586','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxo1.021010_0145.070.fits'),('wxo1.021012_0130.057.3030','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxo1.021012_0130.057.fits'),('wxo1.021012_0144.062.3034','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxo1.021012_0144.062.fits'),('wxo1.021014_0223.063.2400','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxo1.021014_0223.063.fits'),('wxo1.021014_0237.068.2403','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxo1.021014_0237.068.fits'),('wxp1.020928_0210.093.3847','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxp1.020928_0210.093.fits'),('wxp1.020928_0220.097.3848','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxp1.020928_0220.097.fits'),('wxp1.020928_0223.098.3849','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxp1.020928_0223.098.fits'),('wxp1.020930_0143.063.3467','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxp1.020930_0143.063.fits'),('wxp1.020930_0152.067.3471','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxp1.020930_0152.067.fits'),('wxp1.020930_0203.071.3475','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxp1.020930_0203.071.fits'),('wxp1.021002_0138.078.2698','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxp1.021002_0138.078.fits'),('wxp1.021002_0157.034.2702','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxp1.021002_0157.034.fits'),('wxp1.021002_0208.038.2706','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxp1.021002_0208.038.fits'),('wxp1.021004_0153.038.2037','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxp1.021004_0153.038.fits'),('wxp1.021004_0417.048.2041','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxp1.021004_0417.048.fits'),('wxp1.021006_0213.078.1269','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxp1.021006_0213.078.fits'),('wxp1.021006_0222.082.1273','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxp1.021006_0222.082.fits'),('wxp1.021006_0226.083.1277','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxp1.021006_0226.083.fits'),('wxp1.021008_0137.072.509','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxp1.021008_0137.072.fits'),('wxp1.021008_0149.076.513','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxp1.021008_0149.076.fits'),('wxp1.021010_0148.071.3589','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxp1.021010_0148.071.fits'),('wxp1.021010_0207.075.3592','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxp1.021010_0207.075.fits'),('wxp1.021012_0147.063.3038','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxp1.021012_0147.063.fits'),('wxp1.021012_0207.067.3042','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxp1.021012_0207.067.fits'),('wxp1.021014_0240.069.2407','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxp1.021014_0240.069.fits'),('wxp1.021014_0259.073.2412','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxp1.021014_0259.073.fits'),('wxp1.021030_0215.057.1819','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxp1.021030_0215.057.fits'),('wxp1.021030_0225.059.1823','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxp1.021030_0225.059.fits'),('wxp1.021030_0249.061.1827','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxp1.021030_0249.061.fits'),('wxp1.021105_0207.057.567','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxp1.021105_0207.057.fits'),('wxp1.021105_0217.059.571','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxp1.021105_0217.059.fits'),('wxp1.021105_0241.061.575','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxp1.021105_0241.061.fits'),('wxp1.021111_0220.066.3444','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxp1.021111_0220.066.fits'),('wxp1.021111_0251.069.3449','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxp1.021111_0251.069.fits'),('wxp1.021115_0104.049.2217','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxp1.021115_0104.049.fits'),('wxp1.021115_0116.051.2221','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxp1.021115_0116.051.fits'),('wxp1.021115_0143.054.2225','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxp1.021115_0143.054.fits'),('wxp1.021128_0235.045.1699','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxp1.021128_0235.045.fits'),('wxp1.021128_0245.047.1703','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxp1.021128_0245.047.fits'),('wxp1.021204_0309.050.439','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxp1.021204_0309.050.fits'),('wxp1.021204_0328.052.443','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxp1.021204_0328.052.fits'),('wxp1.021206_0247.058.3438','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxp1.021206_0247.058.fits'),('wxp1.021206_0257.060.3443','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxp1.021206_0257.060.fits'),('wxp1.021206_0321.062.3447','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxp1.021206_0321.062.fits'),('wxq1.020928_0213.094.3850','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxq1.020928_0213.094.fits'),('wxq1.020928_0225.099.3851','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxq1.020928_0225.099.fits'),('wxq1.020930_0145.064.3479','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxq1.020930_0145.064.fits'),('wxq1.020930_0154.068.3483','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxq1.020930_0154.068.fits'),('wxq1.020930_0216.073.3486','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxq1.020930_0216.073.fits'),('wxq1.021002_0140.079.2710','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxq1.021002_0140.079.fits'),('wxq1.021002_0200.035.2714','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxq1.021002_0200.035.fits'),('wxq1.021002_0210.039.2718','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxq1.021002_0210.039.fits'),('wxq1.021004_0420.049.2045','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxq1.021004_0420.049.fits'),('wxq1.021006_0215.079.1280','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxq1.021006_0215.079.fits'),('wxq1.021006_0228.084.1284','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxq1.021006_0228.084.fits'),('wxq1.021008_0140.073.517','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxq1.021008_0140.073.fits'),('wxq1.021008_0152.077.521','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxq1.021008_0152.077.fits'),('wxq1.021010_0150.072.3595','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxq1.021010_0150.072.fits'),('wxq1.021010_0210.076.3598','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxq1.021010_0210.076.fits'),('wxq1.021012_0150.064.3046','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxq1.021012_0150.064.fits'),('wxq1.021012_0210.068.3049','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxq1.021012_0210.068.fits'),('wxq1.021014_0243.070.2415','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxq1.021014_0243.070.fits'),('wxq1.021014_0302.074.2420','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxq1.021014_0302.074.fits'),('wxq1.021101_0226.064.1311','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxq1.021101_0226.064.fits'),('wxq1.021101_0236.066.1315','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxq1.021101_0236.066.fits'),('wxq1.021101_0300.068.1319','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxq1.021101_0300.068.fits'),('wxq1.021107_0342.108.115','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/wxq1.021107_0342.108.fits'),('wxq1.021109_0230.062.3787','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxq1.021109_0230.062.fits'),('wxq1.021109_0240.064.3789','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxq1.021109_0240.064.fits'),('wxq1.021109_0305.066.3791','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxq1.021109_0305.066.fits'),('wxq1.021115_0152.056.2229','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxq1.021115_0152.056.fits'),('wxq1.021115_0202.058.2233','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxq1.021115_0202.058.fits'),('wxq1.021115_0236.057.2237','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxq1.021115_0236.057.fits'),('wxq1.021130_0251.045.1248','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxq1.021130_0251.045.fits'),('wxq1.021130_0301.047.1252','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxq1.021130_0301.047.fits'),('wxq1.021130_0326.049.1256','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxq1.021130_0326.049.fits'),('wxq1.021202_0229.066.957','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxq1.021202_0229.066.fits'),('wxq1.021202_0239.068.961','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxq1.021202_0239.068.fits'),('wxq1.021202_0302.070.965','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxq1.021202_0302.070.fits'),('wxr1.020928_0227.100.3852','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxr1.020928_0227.100.fits'),('wxr1.020930_0148.065.3490','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxr1.020930_0148.065.fits'),('wxr1.020930_0157.069.3494','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxr1.020930_0157.069.fits'),('wxr1.020930_0218.074.3497','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxr1.020930_0218.074.fits'),('wxr1.021002_0142.080.2722','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxr1.021002_0142.080.fits'),('wxr1.021002_0202.036.2726','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxr1.021002_0202.036.fits'),('wxr1.021002_0212.040.2730','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxr1.021002_0212.040.fits'),('wxr1.021004_0422.050.2049','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxr1.021004_0422.050.fits'),('wxr1.021006_0218.080.1288','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxr1.021006_0218.080.fits'),('wxr1.021006_0230.085.1292','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxr1.021006_0230.085.fits'),('wxr1.021008_0143.074.525','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxr1.021008_0143.074.fits'),('wxr1.021008_0155.078.529','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxr1.021008_0155.078.fits'),('wxr1.021010_0153.073.3601','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxr1.021010_0153.073.fits'),('wxr1.021010_0212.077.3604','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxr1.021010_0212.077.fits'),('wxr1.021012_0153.065.3051','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxr1.021012_0153.065.fits'),('wxr1.021012_0213.069.3056','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxr1.021012_0213.069.fits'),('wxr1.021014_0246.071.2423','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxr1.021014_0246.071.fits'),('wxr1.021014_0305.075.2427','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxr1.021014_0305.075.fits'),('wxr1.021030_0218.058.1831','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxr1.021030_0218.058.fits'),('wxr1.021030_0231.060.1835','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxr1.021030_0231.060.fits'),('wxr1.021030_0252.062.1839','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxr1.021030_0252.062.fits'),('wxr1.021030_0255.063.1844','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxr1.021030_0255.063.fits'),('wxr1.021103_0319.084.797','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/wxr1.021103_0319.084.fits'),('wxr1.021105_0210.058.579','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxr1.021105_0210.058.fits'),('wxr1.021105_0222.060.583','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxr1.021105_0222.060.fits'),('wxr1.021105_0243.062.587','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxr1.021105_0243.062.fits'),('wxr1.021111_0223.067.3453','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxr1.021111_0223.067.fits'),('wxr1.021111_0232.068.3456','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxr1.021111_0232.068.fits'),('wxr1.021111_0254.070.3460','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxr1.021111_0254.070.fits'),('wxr1.021115_0107.050.2241','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxr1.021115_0107.050.fits'),('wxr1.021115_0123.052.2245','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxr1.021115_0123.052.fits'),('wxr1.021115_0133.053.2249','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxr1.021115_0133.053.fits'),('wxr1.021115_0146.055.2253','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxr1.021115_0146.055.fits'),('wxr1.021128_0238.046.1708','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxr1.021128_0238.046.fits'),('wxr1.021128_0250.048.1710','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxr1.021128_0250.048.fits'),('wxr1.021204_0316.051.448','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxr1.021204_0316.051.fits'),('wxr1.021204_0340.053.451','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxr1.021204_0340.053.fits'),('wxr1.021206_0250.059.3451','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxr1.021206_0250.059.fits'),('wxr1.021206_0302.061.3454','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxr1.021206_0302.061.fits'),('wxr1.021206_0324.063.3458','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxr1.021206_0324.063.fits'),('wxs1.020928_0218.096.3853','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxs1.020928_0218.096.fits'),('wxs1.020928_0229.101.3854','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxs1.020928_0229.101.fits'),('wxs1.020930_0150.066.3500','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxs1.020930_0150.066.fits'),('wxs1.020930_0200.070.3503','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxs1.020930_0200.070.fits'),('wxs1.020930_0220.075.3507','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxs1.020930_0220.075.fits'),('wxs1.021002_0156.033.2734','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxs1.021002_0156.033.fits'),('wxs1.021002_0205.037.2738','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxs1.021002_0205.037.fits'),('wxs1.021002_0214.041.2742','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxs1.021002_0214.041.fits'),('wxs1.021004_0425.051.2053','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxs1.021004_0425.051.fits'),('wxs1.021006_0220.081.1296','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxs1.021006_0220.081.fits'),('wxs1.021006_0233.086.1300','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxs1.021006_0233.086.fits'),('wxs1.021008_0146.075.533','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxs1.021008_0146.075.fits'),('wxs1.021008_0158.079.537','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxs1.021008_0158.079.fits'),('wxs1.021010_0200.074.3607','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxs1.021010_0200.074.fits'),('wxs1.021010_0215.078.3610','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxs1.021010_0215.078.fits'),('wxs1.021012_0159.066.3059','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxs1.021012_0159.066.fits'),('wxs1.021012_0215.070.3066','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxs1.021012_0215.070.fits'),('wxs1.021014_0252.072.2431','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxs1.021014_0252.072.fits'),('wxs1.021101_0229.065.1323','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxs1.021101_0229.065.fits'),('wxs1.021101_0242.067.1327','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxs1.021101_0242.067.fits'),('wxs1.021101_0304.069.1331','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxs1.021101_0304.069.fits'),('wxs1.021107_0314.104.119','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/wxs1.021107_0314.104.fits'),('wxs1.021109_0233.063.3793','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxs1.021109_0233.063.fits'),('wxs1.021109_0246.065.001.3795','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxs1.021109_0246.065.001.fits'),('wxs1.021109_0246.065.002.3797','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxs1.021109_0246.065.002.fits'),('wxs1.021109_0246.065.3799','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxs1.021109_0246.065.fits'),('wxs1.021109_0308.067.3801','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxs1.021109_0308.067.fits'),('wxs1.021113_0310.060.2843','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxs1.021113_0310.060.fits'),('wxs1.021115_0155.057.2257','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxs1.021115_0155.057.fits'),('wxs1.021115_0224.056.2261','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxs1.021115_0224.056.fits'),('wxs1.021115_0239.058.2265','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxs1.021115_0239.058.fits'),('wxs1.021130_0254.046.1260','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxs1.021130_0254.046.fits'),('wxs1.021130_0306.048.1264','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxs1.021130_0306.048.fits'),('wxs1.021130_0330.050.1268','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wxs1.021130_0330.050.fits'),('wxs1.021202_0232.067.971','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxs1.021202_0232.067.fits'),('wxs1.021202_0244.069.975','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxs1.021202_0244.069.fits'),('wxs1.021202_0305.071.979','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxs1.021202_0305.071.fits'),('wxs1.021208_0207.050.2943','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxs1.021208_0207.050.fits'),('wxs1.021208_0214.051.2947','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxs1.021208_0214.051.fits'),('wxs1.021208_0233.052.2951','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wxs1.021208_0233.052.fits'),('wxt1.020928_0231.102.3855','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxt1.020928_0231.102.fits'),('wxt1.020928_0238.105.3856','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxt1.020928_0238.105.fits'),('wxt1.020930_0222.076.3509','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxt1.020930_0222.076.fits'),('wxt1.020930_0229.079.3512','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxt1.020930_0229.079.fits'),('wxt1.020930_0238.082.3515','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxt1.020930_0238.082.fits'),('wxt1.021002_0216.042.2746','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxt1.021002_0216.042.fits'),('wxt1.021002_0224.045.2750','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxt1.021002_0224.045.fits'),('wxt1.021002_0232.048.2754','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxt1.021002_0232.048.fits'),('wxt1.021006_0235.087.1305','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxt1.021006_0235.087.fits'),('wxt1.021008_0201.080.541','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxt1.021008_0201.080.fits'),('wxt1.021008_0211.083.545','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxt1.021008_0211.083.fits'),('wxt2.020928_0247.108.3857','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxt2.020928_0247.108.fits'),('wxt2.020928_0254.111.3858','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxt2.020928_0254.111.fits'),('wxt2.020928_0309.114.3859','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxt2.020928_0309.114.fits'),('wxt2.020930_0244.085.3518','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxt2.020930_0244.085.fits'),('wxt2.020930_0252.088.3521','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxt2.020930_0252.088.fits'),('wxt2.020930_0308.091.3524','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxt2.020930_0308.091.fits'),('wxt2.021002_0238.051.2758','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxt2.021002_0238.051.fits'),('wxt2.021002_0245.054.2762','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxt2.021002_0245.054.fits'),('wxt2.021002_0300.057.2766','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxt2.021002_0300.057.fits'),('wxt2.021004_0407.045.2057','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxt2.021004_0407.045.fits'),('wxt2.021008_0220.086.549','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxt2.021008_0220.086.fits'),('wxt2.021008_0247.042.553','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxt2.021008_0247.042.fits'),('wxt2.021008_0300.045.557','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxt2.021008_0300.045.fits'),('wxt2.021010_0218.079.3613','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxt2.021010_0218.079.fits'),('wxt2.021012_0218.071.3069','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxt2.021012_0218.071.fits'),('wxt2.021014_0310.076.2435','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxt2.021014_0310.076.fits'),('wxt2.021030_0258.064.1847','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxt2.021030_0258.064.fits'),('wxt2.021030_0306.065.1851','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxt2.021030_0306.065.fits'),('wxt2.021030_0325.066.1855','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wxt2.021030_0325.066.fits'),('wxt2.021103_0342.087.801','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/wxt2.021103_0342.087.fits'),('wxt2.021105_0342.069.591','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxt2.021105_0342.069.fits'),('wxt2.021105_0349.070.595','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxt2.021105_0349.070.fits'),('wxt2.021105_0407.071.599','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxt2.021105_0407.071.fits'),('wxt2.021111_0300.072.3464','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxt2.021111_0300.072.fits'),('wxt2.021111_0308.073.3468','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxt2.021111_0308.073.fits'),('wxt2.021111_0326.074.3472','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxt2.021111_0326.074.fits'),('wxt2.021115_0315.064.2270','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxt2.021115_0315.064.fits'),('wxt2.021115_0321.065.2273','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxt2.021115_0321.065.fits'),('wxt2.021115_0331.066.2276','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxt2.021115_0331.066.fits'),('wxt2.021115_0341.067.2282','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxt2.021115_0341.067.fits'),('wxt2.021128_0342.051.1714','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxt2.021128_0342.051.fits'),('wxt2.021128_0349.052.1718','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxt2.021128_0349.052.fits'),('wxt2.021204_0406.055.455','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxt2.021204_0406.055.fits'),('wxt2.021206_0356.067.3462','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxt2.021206_0356.067.fits'),('wxt2.021206_0402.068.3466','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxt2.021206_0402.068.fits'),('wxt2.021206_0416.069.3470','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxt2.021206_0416.069.fits'),('wxt2.021210_0230.051.2660','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxt2.021210_0230.051.fits'),('wxt2.021210_0237.052.2664','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxt2.021210_0237.052.fits'),('wxt2.021210_0255.053.2668','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxt2.021210_0255.053.fits'),('wxt2.021216_0226.052.1535','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxt2.021216_0226.052.fits'),('wxt2.021216_0233.053.1538','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxt2.021216_0233.053.fits'),('wxt2.021216_0244.054.1542','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxt2.021216_0244.054.fits'),('wxt2.021229_0230.053.1022','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxt2.021229_0230.053.fits'),('wxt2.021229_0238.054.1025','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxt2.021229_0238.054.fits'),('wxt2.030103_0246.052.482','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxt2.030103_0246.052.fits'),('wxt2.030103_0253.053.485','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxt2.030103_0253.053.fits'),('wxu1.020930_0225.077.3527','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxu1.020930_0225.077.fits'),('wxu1.020930_0232.080.3530','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxu1.020930_0232.080.fits'),('wxu1.020930_0240.083.3533','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxu1.020930_0240.083.fits'),('wxu1.021002_0219.043.2770','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxu1.021002_0219.043.fits'),('wxu1.021002_0227.046.2774','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxu1.021002_0227.046.fits'),('wxu1.021002_0234.049.2778','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxu1.021002_0234.049.fits'),('wxu1.021006_0237.088.1309','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxu1.021006_0237.088.fits'),('wxu1.021008_0204.081.561','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxu1.021008_0204.081.fits'),('wxu1.021008_0214.084.565','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxu1.021008_0214.084.fits'),('wxu2.020928_0249.109.3860','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxu2.020928_0249.109.fits'),('wxu2.020928_0259.112.3861','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxu2.020928_0259.112.fits'),('wxu2.020928_0311.115.3862','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxu2.020928_0311.115.fits'),('wxu2.020930_0248.086.3536','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxu2.020930_0248.086.fits'),('wxu2.020930_0258.089.3539','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxu2.020930_0258.089.fits'),('wxu2.020930_0310.092.3542','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxu2.020930_0310.092.fits'),('wxu2.021002_0241.052.2782','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxu2.021002_0241.052.fits'),('wxu2.021002_0250.055.2786','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxu2.021002_0250.055.fits'),('wxu2.021002_0303.058.2790','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxu2.021002_0303.058.fits'),('wxu2.021004_0411.046.2061','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxu2.021004_0411.046.fits'),('wxu2.021008_0223.087.569','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxu2.021008_0223.087.fits'),('wxu2.021008_0251.043.573','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxu2.021008_0251.043.fits'),('wxu2.021008_0315.046.577','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxu2.021008_0315.046.fits'),('wxu2.021010_0225.080.3616','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wxu2.021010_0225.080.fits'),('wxu2.021012_0235.073.3072','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxu2.021012_0235.073.fits'),('wxu2.021014_0317.077.2439','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxu2.021014_0317.077.fits'),('wxu2.021101_0405.076.1335','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxu2.021101_0405.076.fits'),('wxu2.021101_0412.077.1339','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxu2.021101_0412.077.fits'),('wxu2.021101_0432.078.1343','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wxu2.021101_0432.078.fits'),('wxu2.021107_0334.107.123','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/wxu2.021107_0334.107.fits'),('wxu2.021109_0408.074.3803','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxu2.021109_0408.074.fits'),('wxu2.021109_0415.075.3805','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxu2.021109_0415.075.fits'),('wxu2.021109_0434.076.3807','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wxu2.021109_0434.076.fits'),('wxu2.021113_0417.069.2847','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxu2.021113_0417.069.fits'),('wxu2.021113_0424.070.2851','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxu2.021113_0424.070.fits'),('wxu2.021113_0434.071.2855','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxu2.021113_0434.071.fits'),('wxu2.021113_0443.072.2861','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wxu2.021113_0443.072.fits'),('wxu2.021202_0404.078.982','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxu2.021202_0404.078.fits'),('wxu2.021202_0411.079.986','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxu2.021202_0411.079.fits'),('wxu2.021202_0429.080.990','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wxu2.021202_0429.080.fits'),('wxu2.021210_0202.048.2671','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxu2.021210_0202.048.fits'),('wxu2.021210_0209.049.2675','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxu2.021210_0209.049.fits'),('wxu2.021210_0228.050.2679','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxu2.021210_0228.050.fits'),('wxu2.021214_0215.054.2056','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wxu2.021214_0215.054.fits'),('wxu2.021214_0223.055.2060','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wxu2.021214_0223.055.fits'),('wxu2.021214_0233.056.2064','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wxu2.021214_0233.056.fits'),('wxu2.021214_0243.057.2068','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wxu2.021214_0243.057.fits'),('wxu2.021216_0254.055.1545','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxu2.021216_0254.055.fits'),('wxu2.021216_0301.056.1550','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxu2.021216_0301.056.fits'),('wxu2.021229_0256.055.1029','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxu2.021229_0256.055.fits'),('wxu2.021229_0303.056.1033','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxu2.021229_0303.056.fits'),('wxu2.030103_0219.050.489','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxu2.030103_0219.050.fits'),('wxu2.030103_0227.051.493','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxu2.030103_0227.051.fits'),('wxv1.020928_0236.104.3863','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxv1.020928_0236.104.fits'),('wxv1.020928_0244.107.3864','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxv1.020928_0244.107.fits'),('wxv1.020930_0227.078.3545','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxv1.020930_0227.078.fits'),('wxv1.020930_0235.081.3548','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxv1.020930_0235.081.fits'),('wxv1.020930_0242.084.3551','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxv1.020930_0242.084.fits'),('wxv1.021002_0221.044.2795','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxv1.021002_0221.044.fits'),('wxv1.021002_0229.047.2798','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxv1.021002_0229.047.fits'),('wxv1.021002_0236.050.2802','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxv1.021002_0236.050.fits'),('wxv1.021006_0239.089.1312','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxv1.021006_0239.089.fits'),('wxv1.021008_0208.082.581','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxv1.021008_0208.082.fits'),('wxv1.021008_0217.085.585','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxv1.021008_0217.085.fits'),('wxv2.020928_0251.110.3865','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxv2.020928_0251.110.fits'),('wxv2.020928_0314.116.3866','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxv2.020928_0314.116.fits'),('wxv2.020930_0250.087.3554','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxv2.020930_0250.087.fits'),('wxv2.020930_0303.090.3557','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxv2.020930_0303.090.fits'),('wxv2.020930_0312.093.3560','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxv2.020930_0312.093.fits'),('wxv2.021002_0243.053.2806','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxv2.021002_0243.053.fits'),('wxv2.021002_0255.056.2810','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxv2.021002_0255.056.fits'),('wxv2.021002_0305.059.2815','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxv2.021002_0305.059.fits'),('wxv2.021004_0413.047.2065','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxv2.021004_0413.047.fits'),('wxv2.021008_0226.088.589','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxv2.021008_0226.088.fits'),('wxv2.021008_0255.044.593','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxv2.021008_0255.044.fits'),('wxv2.021008_0325.047.597','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxv2.021008_0325.047.fits'),('wxv2.021012_0242.074.3075','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxv2.021012_0242.074.fits'),('wxv2.021014_0324.078.2444','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxv2.021014_0324.078.fits'),('wxv2.021105_0410.072.603','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxv2.021105_0410.072.fits'),('wxv2.021105_0416.073.607','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxv2.021105_0416.073.fits'),('wxv2.021105_0425.074.611','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wxv2.021105_0425.074.fits'),('wxv2.021111_0329.075.3476','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxv2.021111_0329.075.fits'),('wxv2.021111_0336.076.3480','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxv2.021111_0336.076.fits'),('wxv2.021111_0354.077.3484','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wxv2.021111_0354.077.fits'),('wxv2.021115_0344.068.2284','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxv2.021115_0344.068.fits'),('wxv2.021115_0351.069.2290','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxv2.021115_0351.069.fits'),('wxv2.021115_0401.070.2292','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxv2.021115_0401.070.fits'),('wxv2.021115_0411.071.2298','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wxv2.021115_0411.071.fits'),('wxv2.021128_0408.053.1722','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxv2.021128_0408.053.fits'),('wxv2.021128_0414.054.1726','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wxv2.021128_0414.054.fits'),('wxv2.021204_0420.056.459','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wxv2.021204_0420.056.fits'),('wxv2.021206_0419.070.3474','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxv2.021206_0419.070.fits'),('wxv2.021206_0424.071.3478','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wxv2.021206_0424.071.fits'),('wxv2.021210_0258.054.2683','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxv2.021210_0258.054.fits'),('wxv2.021210_0305.055.2687','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxv2.021210_0305.055.fits'),('wxv2.021210_0324.056.2691','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/wxv2.021210_0324.056.fits'),('wxv2.021214_0246.058.2072','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wxv2.021214_0246.058.fits'),('wxv2.021214_0253.059.2076','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wxv2.021214_0253.059.fits'),('wxv2.021216_0311.057.1553','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxv2.021216_0311.057.fits'),('wxv2.021216_0318.058.1557','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxv2.021216_0318.058.fits'),('wxv2.021216_0328.059.1562','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/wxv2.021216_0328.059.fits'),('wxv2.021229_0321.057.1037','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxv2.021229_0321.057.fits'),('wxv2.021229_0328.058.1041','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxv2.021229_0328.058.fits'),('wxv2.021229_0337.059.1045','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/wxv2.021229_0337.059.fits'),('wxv2.030103_0311.054.497','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxv2.030103_0311.054.fits'),('wxv2.030103_0318.055.501','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wxv2.030103_0318.055.fits'),('wxw1.020928_0317.117.3867','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxw1.020928_0317.117.fits'),('wxw1.020928_0324.120.3868','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxw1.020928_0324.120.fits'),('wxw1.020928_0333.123.3869','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxw1.020928_0333.123.fits'),('wxw1.020930_0315.094.3563','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxw1.020930_0315.094.fits'),('wxw1.020930_0322.097.3566','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxw1.020930_0322.097.fits'),('wxw1.020930_0330.100.3569','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxw1.020930_0330.100.fits'),('wxw1.021002_0307.060.2819','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxw1.021002_0307.060.fits'),('wxw1.021002_0313.063.2822','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxw1.021002_0313.063.fits'),('wxw1.021002_0321.066.2829','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxw1.021002_0321.066.fits'),('wxw1.021008_0337.048.602','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxw1.021008_0337.048.fits'),('wxw1.021008_0352.051.605','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxw1.021008_0352.051.fits'),('wxw1.021008_0427.054.609','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxw1.021008_0427.054.fits'),('wxw2.020928_0340.126.3870','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxw2.020928_0340.126.fits'),('wxw2.020928_0348.129.3871','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxw2.020928_0348.129.fits'),('wxw2.020928_0404.132.3872','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxw2.020928_0404.132.fits'),('wxw2.020928_0411.135.3873','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxw2.020928_0411.135.fits'),('wxw2.020930_0336.103.3572','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxw2.020930_0336.103.fits'),('wxw2.020930_0343.106.3575','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxw2.020930_0343.106.fits'),('wxw2.020930_0358.109.3578','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxw2.020930_0358.109.fits'),('wxw2.020930_0405.112.3581','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxw2.020930_0405.112.fits'),('wxw2.020930_0412.115.3585','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxw2.020930_0412.115.fits'),('wxw2.020930_0428.118.3588','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxw2.020930_0428.118.fits'),('wxw2.021002_0327.069.2833','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxw2.021002_0327.069.fits'),('wxw2.021002_0334.072.2836','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxw2.021002_0334.072.fits'),('wxw2.021002_0350.075.2840','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxw2.021002_0350.075.fits'),('wxw2.021002_0357.078.2844','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxw2.021002_0357.078.fits'),('wxw2.021002_0407.081.2848','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxw2.021002_0407.081.fits'),('wxw2.021002_0426.034.2852','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxw2.021002_0426.034.fits'),('wxw2.021004_0429.052.2069','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxw2.021004_0429.052.fits'),('wxw2.021006_0352.103.1316','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxw2.021006_0352.103.fits'),('wxw2.021006_0407.106.1320','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxw2.021006_0407.106.fits'),('wxw2.021006_0422.109.1325','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxw2.021006_0422.109.fits'),('wxw2.021012_0248.075.3079','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxw2.021012_0248.075.fits'),('wxw2.021014_0330.079.2447','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxw2.021014_0330.079.fits'),('wxx1.020928_0319.118.3874','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxx1.020928_0319.118.fits'),('wxx1.020930_0317.095.3591','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxx1.020930_0317.095.fits'),('wxx1.020930_0324.098.3594','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxx1.020930_0324.098.fits'),('wxx1.020930_0332.101.3597','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxx1.020930_0332.101.fits'),('wxx1.021002_0309.061.2856','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxx1.021002_0309.061.fits'),('wxx1.021002_0316.064.2859','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxx1.021002_0316.064.fits'),('wxx1.021002_0323.067.2863','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxx1.021002_0323.067.fits'),('wxx1.021008_0341.049.614','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxx1.021008_0341.049.fits'),('wxx1.021008_0404.052.618','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxx1.021008_0404.052.fits'),('wxx1.021008_0430.055.621','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxx1.021008_0430.055.fits'),('wxx2.020928_0343.127.3875','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxx2.020928_0343.127.fits'),('wxx2.020928_0406.133.3876','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxx2.020928_0406.133.fits'),('wxx2.020928_0414.136.3877','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxx2.020928_0414.136.fits'),('wxx2.020928_0425.139.3878','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxx2.020928_0425.139.fits'),('wxx2.020930_0338.104.3600','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxx2.020930_0338.104.fits'),('wxx2.020930_0348.107.3603','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxx2.020930_0348.107.fits'),('wxx2.020930_0400.110.3606','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxx2.020930_0400.110.fits'),('wxx2.020930_0408.113.3609','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxx2.020930_0408.113.fits'),('wxx2.020930_0417.116.3612','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxx2.020930_0417.116.fits'),('wxx2.020930_0430.119.3615','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxx2.020930_0430.119.fits'),('wxx2.021002_0330.070.2867','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxx2.021002_0330.070.fits'),('wxx2.021002_0339.073.2873','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxx2.021002_0339.073.fits'),('wxx2.021002_0352.076.2877','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxx2.021002_0352.076.fits'),('wxx2.021002_0400.079.2880','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxx2.021002_0400.079.fits'),('wxx2.021002_0412.082.2885','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxx2.021002_0412.082.fits'),('wxx2.021002_0428.035.2887','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxx2.021002_0428.035.fits'),('wxx2.021004_0432.053.2073','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxx2.021004_0432.053.fits'),('wxx2.021006_0355.104.1329','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxx2.021006_0355.104.fits'),('wxx2.021006_0412.107.1333','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxx2.021006_0412.107.fits'),('wxx2.021006_0425.110.1337','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxx2.021006_0425.110.fits'),('wxx2.021012_0255.076.3084','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxx2.021012_0255.076.fits'),('wxx2.021014_0337.080.2451','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxx2.021014_0337.080.fits'),('wxy1.020928_0321.119.3879','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxy1.020928_0321.119.fits'),('wxy1.020928_0330.122.3880','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxy1.020928_0330.122.fits'),('wxy1.020930_0319.096.3618','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxy1.020930_0319.096.fits'),('wxy1.020930_0327.099.3619','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxy1.020930_0327.099.fits'),('wxy1.020930_0334.102.3622','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxy1.020930_0334.102.fits'),('wxy1.021002_0311.062.2891','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxy1.021002_0311.062.fits'),('wxy1.021002_0318.065.2895','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxy1.021002_0318.065.fits'),('wxy1.021002_0325.068.2900','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxy1.021002_0325.068.fits'),('wxy1.021008_0345.050.625','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxy1.021008_0345.050.fits'),('wxy1.021008_0415.053.629','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxy1.021008_0415.053.fits'),('wxy1.021008_0433.056.634','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/wxy1.021008_0433.056.fits'),('wxy2.020928_0345.128.3881','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxy2.020928_0345.128.fits'),('wxy2.020928_0358.131.3882','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxy2.020928_0358.131.fits'),('wxy2.020928_0409.134.3883','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxy2.020928_0409.134.fits'),('wxy2.020928_0417.137.3884','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/wxy2.020928_0417.137.fits'),('wxy2.020930_0341.105.3625','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxy2.020930_0341.105.fits'),('wxy2.020930_0353.108.3628','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxy2.020930_0353.108.fits'),('wxy2.020930_0403.111.3631','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxy2.020930_0403.111.fits'),('wxy2.020930_0410.114.3634','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxy2.020930_0410.114.fits'),('wxy2.020930_0423.117.3637','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxy2.020930_0423.117.fits'),('wxy2.020930_0433.120.3641','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/wxy2.020930_0433.120.fits'),('wxy2.021002_0332.071.2903','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxy2.021002_0332.071.fits'),('wxy2.021002_0345.074.2907','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxy2.021002_0345.074.fits'),('wxy2.021002_0355.077.2911','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxy2.021002_0355.077.fits'),('wxy2.021002_0404.080.2915','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxy2.021002_0404.080.fits'),('wxy2.021002_0421.033.2919','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxy2.021002_0421.033.fits'),('wxy2.021002_0431.036.2923','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/wxy2.021002_0431.036.fits'),('wxy2.021004_0434.054.2077','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/wxy2.021004_0434.054.fits'),('wxy2.021006_0404.105.1341','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxy2.021006_0404.105.fits'),('wxy2.021006_0417.108.1345','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxy2.021006_0417.108.fits'),('wxy2.021006_0427.111.1348','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wxy2.021006_0427.111.fits'),('wxy2.021012_0258.077.3087','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wxy2.021012_0258.077.fits'),('wxy2.021014_0340.081.2455','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wxy2.021014_0340.081.fits'),('wya1.021010_0315.088.3620','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wya1.021010_0315.088.fits'),('wya3.021006_0251.090.1352','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wya3.021006_0251.090.fits'),('wya3.021006_0308.094.1356','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wya3.021006_0308.094.fits'),('wya3.021006_0333.098.1360','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wya3.021006_0333.098.fits'),('wya3.021012_0303.078.3092','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wya3.021012_0303.078.fits'),('wyb3.021006_0255.091.1364','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wyb3.021006_0255.091.fits'),('wyb3.021006_0314.095.1368','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wyb3.021006_0314.095.fits'),('wyb3.021006_0336.099.1372','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wyb3.021006_0336.099.fits'),('wyb3.021012_0312.079.3098','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wyb3.021012_0312.079.fits'),('wyb3.021012_0334.082.3099','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wyb3.021012_0334.082.fits'),('wyb3.021014_0343.082.2459','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wyb3.021014_0343.082.fits'),('wyb3.021014_0637.108.2463','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wyb3.021014_0637.108.fits'),('wyb3.021014_0700.111.2467','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wyb3.021014_0700.111.fits'),('wyb3.021030_0334.067.1859','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wyb3.021030_0334.067.fits'),('wyb3.021030_0349.069.1863','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wyb3.021030_0349.069.fits'),('wyb3.021030_0426.071.1867','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wyb3.021030_0426.071.fits'),('wyb3.021103_0327.085.805','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/wyb3.021103_0327.085.fits'),('wyb3.021105_0246.063.615','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wyb3.021105_0246.063.fits'),('wyb3.021105_0300.065.620','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wyb3.021105_0300.065.fits'),('wyb3.021105_0336.067.624','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wyb3.021105_0336.067.fits'),('wyb3.021111_0358.078.3488','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wyb3.021111_0358.078.fits'),('wyb3.021111_0406.079.3492','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/wyb3.021111_0406.079.fits'),('wyb3.021115_0241.059.2302','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wyb3.021115_0241.059.fits'),('wyb3.021115_0248.060.2304','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wyb3.021115_0248.060.fits'),('wyb3.021115_0259.061.2308','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wyb3.021115_0259.061.fits'),('wyb3.021115_0309.062.2312','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/wyb3.021115_0309.062.fits'),('wyb3.021128_0311.049.1730','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wyb3.021128_0311.049.fits'),('wyb3.021128_0322.050.1734','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/wyb3.021128_0322.050.fits'),('wyb3.021204_0358.054.463','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/wyb3.021204_0358.054.fits'),('wyb3.021206_0327.064.3482','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wyb3.021206_0327.064.fits'),('wyb3.021206_0335.065.3487','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wyb3.021206_0335.065.fits'),('wyb3.021206_0353.066.3491','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/wyb3.021206_0353.066.fits'),('wyb3.030103_0337.056.505','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wyb3.030103_0337.056.fits'),('wyb3.030103_0344.057.510','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/wyb3.030103_0344.057.fits'),('wyc3.021006_0258.092.1376','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wyc3.021006_0258.092.fits'),('wyc3.021006_0320.096.1380','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wyc3.021006_0320.096.fits'),('wyc3.021006_0339.100.1384','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wyc3.021006_0339.100.fits'),('wyc3.021012_0319.080.3103','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wyc3.021012_0319.080.fits'),('wyc3.021012_0337.083.3107','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wyc3.021012_0337.083.fits'),('wyc3.021014_0646.109.2472','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wyc3.021014_0646.109.fits'),('wyc3.021014_0703.112.2476','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wyc3.021014_0703.112.fits'),('wyc3.021101_0307.070.1347','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wyc3.021101_0307.070.fits'),('wyc3.021101_0322.072.1351','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wyc3.021101_0322.072.fits'),('wyc3.021101_0359.074.1355','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wyc3.021101_0359.074.fits'),('wyc3.021107_0321.105.127','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/wyc3.021107_0321.105.fits'),('wyc3.021109_0310.068.3809','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wyc3.021109_0310.068.fits'),('wyc3.021109_0325.070.3811','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wyc3.021109_0325.070.fits'),('wyc3.021109_0402.072.3813','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wyc3.021109_0402.072.fits'),('wyc3.021113_0319.061.2864','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wyc3.021113_0319.061.fits'),('wyc3.021113_0333.063.2868','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wyc3.021113_0333.063.fits'),('wyc3.021113_0342.064.2871','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wyc3.021113_0342.064.fits'),('wyc3.021113_0410.067.2875','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wyc3.021113_0410.067.fits'),('wyc3.021130_0333.051.1272','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/wyc3.021130_0333.051.fits'),('wyc3.021202_0308.072.994','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wyc3.021202_0308.072.fits'),('wyc3.021202_0321.074.998','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wyc3.021202_0321.074.fits'),('wyc3.021202_0358.076.1002','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wyc3.021202_0358.076.fits'),('wyc3.021208_0236.053.2955','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/wyc3.021208_0236.053.fits'),('wyc3.021214_0118.048.2080','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wyc3.021214_0118.048.fits'),('wyc3.021214_0127.049.2084','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wyc3.021214_0127.049.fits'),('wyc3.021214_0137.050.2088','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wyc3.021214_0137.050.fits'),('wyd3.021006_0306.093.1388','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wyd3.021006_0306.093.fits'),('wyd3.021006_0328.097.1392','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wyd3.021006_0328.097.fits'),('wyd3.021006_0341.101.1396','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/wyd3.021006_0341.101.fits'),('wyd3.021012_0325.081.3111','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wyd3.021012_0325.081.fits'),('wyd3.021012_0350.086.3116','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wyd3.021012_0350.086.fits'),('wyd3.021014_0653.110.2480','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wyd3.021014_0653.110.fits'),('wyd3.021014_0711.113.2484','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wyd3.021014_0711.113.fits'),('wyd3.021030_0342.068.1871','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wyd3.021030_0342.068.fits'),('wyd3.021030_0408.070.1875','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wyd3.021030_0408.070.fits'),('wyd3.021030_0429.072.1879','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/wyd3.021030_0429.072.fits'),('wyd3.021103_0334.086.809','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/wyd3.021103_0334.086.fits'),('wyd3.021105_0253.064.628','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wyd3.021105_0253.064.fits'),('wyd3.021105_0318.066.632','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wyd3.021105_0318.066.fits'),('wyd3.021105_0339.068.636','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/wyd3.021105_0339.068.fits'),('wye2.021012_0340.084.3121','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wye2.021012_0340.084.fits'),('wye2.021012_0347.085.3126','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/wye2.021012_0347.085.fits'),('wye2.021014_0713.114.2488','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wye2.021014_0713.114.fits'),('wye2.021014_0720.115.2492','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/wye2.021014_0720.115.fits'),('wye2.021101_0315.071.1359','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wye2.021101_0315.071.fits'),('wye2.021101_0340.073.1363','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wye2.021101_0340.073.fits'),('wye2.021101_0402.075.1367','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/wye2.021101_0402.075.fits'),('wye2.021107_0328.106.131','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/wye2.021107_0328.106.fits'),('wye2.021109_0318.069.3814','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wye2.021109_0318.069.fits'),('wye2.021109_0344.071.3817','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wye2.021109_0344.071.fits'),('wye2.021109_0405.073.3819','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/wye2.021109_0405.073.fits'),('wye2.021113_0326.062.2879','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wye2.021113_0326.062.fits'),('wye2.021113_0352.065.2883','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wye2.021113_0352.065.fits'),('wye2.021113_0401.066.2889','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wye2.021113_0401.066.fits'),('wye2.021113_0413.068.2893','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/wye2.021113_0413.068.fits'),('wye2.021202_0314.073.1006','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wye2.021202_0314.073.fits'),('wye2.021202_0340.075.1009','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wye2.021202_0340.075.fits'),('wye2.021202_0401.077.1013','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/wye2.021202_0401.077.fits'),('wye2.021214_0147.051.2092','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wye2.021214_0147.051.fits'),('wye2.021214_0158.052.2096','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wye2.021214_0158.052.fits'),('wye2.021214_0208.053.2100','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/wye2.021214_0208.053.fits'),('wza1.021010_0234.081.3623','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wza1.021010_0234.081.fits'),('wza1.021010_0308.085.3626','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wza1.021010_0308.085.fits'),('wzb1.021010_0242.082.3629','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wzb1.021010_0242.082.fits'),('wzb1.021010_0310.086.3632','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wzb1.021010_0310.086.fits'),('wzc1.021010_0250.083.3635','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wzc1.021010_0250.083.fits'),('wzc1.021010_0313.087.3639','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wzc1.021010_0313.087.fits'),('wzd1.021010_0257.084.3642','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/wzd1.021010_0257.084.fits'),('zero001.136','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero001.fits'),('zero001.1371','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero001.fits'),('zero001.1567','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero001.fits'),('zero001.1739','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero001.fits'),('zero001.1884','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero001.fits'),('zero001.2081','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero001.fits'),('zero001.2224','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/zero001.fits'),('zero001.2318','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero001.fits'),('zero001.2496','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/zero001.fits'),('zero001.2897','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero001.fits'),('zero001.2929','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero001.fits'),('zero001.3495','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero001.fits'),('zero001.3644','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero001.fits'),('zero001.4','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero001.fits'),('zero001.514','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero001.fits'),('zero001.6','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/zero001.fits'),('zero001.638','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/zero001.fits'),('zero001.692','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero001.fits'),('zero001.813','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/zero001.fits'),('zero002.10','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/zero002.fits'),('zero002.1375','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero002.fits'),('zero002.140','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero002.fits'),('zero002.1571','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero002.fits'),('zero002.1743','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero002.fits'),('zero002.1888','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero002.fits'),('zero002.2085','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero002.fits'),('zero002.2228','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/zero002.fits'),('zero002.2322','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero002.fits'),('zero002.2500','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/zero002.fits'),('zero002.2901','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero002.fits'),('zero002.2931','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero002.fits'),('zero002.3498','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero002.fits'),('zero002.3645','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero002.fits'),('zero002.518','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero002.fits'),('zero002.642','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/zero002.fits'),('zero002.696','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero002.fits'),('zero002.8','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero002.fits'),('zero002.817','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/zero002.fits'),('zero003.12','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero003.fits'),('zero003.1379','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero003.fits'),('zero003.14','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/zero003.fits'),('zero003.144','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero003.fits'),('zero003.1575','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero003.fits'),('zero003.1748','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero003.fits'),('zero003.1892','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero003.fits'),('zero003.2089','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero003.fits'),('zero003.2232','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/zero003.fits'),('zero003.2326','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero003.fits'),('zero003.2504','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/zero003.fits'),('zero003.2905','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero003.fits'),('zero003.2935','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero003.fits'),('zero003.3501','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero003.fits'),('zero003.3648','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero003.fits'),('zero003.522','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero003.fits'),('zero003.646','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/zero003.fits'),('zero003.700','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero003.fits'),('zero003.821','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/zero003.fits'),('zero004.1383','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero004.fits'),('zero004.147','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero004.fits'),('zero004.1578','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero004.fits'),('zero004.16','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero004.fits'),('zero004.1753','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero004.fits'),('zero004.18','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/zero004.fits'),('zero004.1895','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero004.fits'),('zero004.2093','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero004.fits'),('zero004.2236','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/zero004.fits'),('zero004.2330','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero004.fits'),('zero004.2508','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/zero004.fits'),('zero004.2908','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero004.fits'),('zero004.2939','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero004.fits'),('zero004.3504','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero004.fits'),('zero004.3650','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero004.fits'),('zero004.526','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero004.fits'),('zero004.650','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/zero004.fits'),('zero004.704','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero004.fits'),('zero004.825','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/zero004.fits'),('zero005.1387','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero005.fits'),('zero005.152','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero005.fits'),('zero005.1582','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero005.fits'),('zero005.1756','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero005.fits'),('zero005.1900','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero005.fits'),('zero005.20','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero005.fits'),('zero005.2097','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero005.fits'),('zero005.22','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/zero005.fits'),('zero005.2240','fpa','fpa','path://ESSENCE/SM_SN02/sm021210/zero005.fits'),('zero005.2334','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero005.fits'),('zero005.2512','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/zero005.fits'),('zero005.2913','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero005.fits'),('zero005.2944','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero005.fits'),('zero005.3506','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero005.fits'),('zero005.3652','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero005.fits'),('zero005.530','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero005.fits'),('zero005.654','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/zero005.fits'),('zero005.708','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero005.fits'),('zero005.829','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/zero005.fits'),('zero006.1391','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero006.fits'),('zero006.155','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero006.fits'),('zero006.1586','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero006.fits'),('zero006.1761','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero006.fits'),('zero006.2101','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero006.fits'),('zero006.23','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero006.fits'),('zero006.2515','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/zero006.fits'),('zero006.2917','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero006.fits'),('zero006.2948','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero006.fits'),('zero006.3510','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero006.fits'),('zero006.3654','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero006.fits'),('zero006.534','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero006.fits'),('zero006.712','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero006.fits'),('zero007.1395','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero007.fits'),('zero007.1590','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero007.fits'),('zero007.160','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero007.fits'),('zero007.1765','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero007.fits'),('zero007.2104','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero007.fits'),('zero007.2520','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/zero007.fits'),('zero007.27','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero007.fits'),('zero007.2921','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero007.fits'),('zero007.2952','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero007.fits'),('zero007.3513','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero007.fits'),('zero007.3656','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero007.fits'),('zero007.538','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero007.fits'),('zero007.716','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero007.fits'),('zero008.1399','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero008.fits'),('zero008.1594','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero008.fits'),('zero008.164','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero008.fits'),('zero008.1769','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero008.fits'),('zero008.2108','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero008.fits'),('zero008.2524','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/zero008.fits'),('zero008.2924','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero008.fits'),('zero008.2957','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero008.fits'),('zero008.31','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero008.fits'),('zero008.3516','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero008.fits'),('zero008.3658','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero008.fits'),('zero008.542','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero008.fits'),('zero008.720','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero008.fits'),('zero009.1403','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero009.fits'),('zero009.1598','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero009.fits'),('zero009.168','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero009.fits'),('zero009.1773','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero009.fits'),('zero009.2112','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero009.fits'),('zero009.2528','fpa','fpa','path://ESSENCE/SM_SN02/sm021012/zero009.fits'),('zero009.2928','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero009.fits'),('zero009.2959','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero009.fits'),('zero009.35','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero009.fits'),('zero009.3519','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero009.fits'),('zero009.3660','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero009.fits'),('zero009.546','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero009.fits'),('zero009.724','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero009.fits'),('zero010.1407','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero010.fits'),('zero010.1602','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero010.fits'),('zero010.172','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero010.fits'),('zero010.1777','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero010.fits'),('zero010.2116','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero010.fits'),('zero010.2933','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero010.fits'),('zero010.2964','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero010.fits'),('zero010.3522','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero010.fits'),('zero010.3662','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero010.fits'),('zero010.39','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero010.fits'),('zero010.550','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero010.fits'),('zero010.728','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero010.fits'),('zero011.1411','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero011.fits'),('zero011.1780','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero011.fits'),('zero011.2937','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero011.fits'),('zero011.2967','fpa','fpa','path://ESSENCE/SM_SN02/sm020930/zero011.fits'),('zero011.3525','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero011.fits'),('zero011.3664','fpa','fpa','path://ESSENCE/SM_SN02/sm020928/zero011.fits'),('zero011.43','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero011.fits'),('zero011.554','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero011.fits'),('zero011.732','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero011.fits'),('zero012.47','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero012.fits'),('zero012.736','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero012.fits'),('zero013.51','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero013.fits'),('zero013.740','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero013.fits'),('zero014.55','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero014.fits'),('zero014.744','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero014.fits'),('zero015.59','fpa','fpa','path://ESSENCE/SM_SN02/sm021107/zero015.fits'),('zero015.749','fpa','fpa','path://ESSENCE/SM_SN02/sm021103/zero015.fits'),('zero021.1','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero021.fits'),('zero021.1049','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero021.fits'),('zero021.1276','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/zero021.fits'),('zero021.1400','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/zero021.fits'),('zero021.2','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/zero021.fits'),('zero021.2697','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/zero021.fits'),('zero021.2960','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/zero021.fits'),('zero021.3130','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/zero021.fits'),('zero021.467','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero021.fits'),('zero022.1019','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/zero022.fits'),('zero022.1052','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero022.fits'),('zero022.1281','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/zero022.fits'),('zero022.1404','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/zero022.fits'),('zero022.2699','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/zero022.fits'),('zero022.2963','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/zero022.fits'),('zero022.3','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero022.fits'),('zero022.3134','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/zero022.fits'),('zero022.471','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero022.fits'),('zero022.5','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/zero022.fits'),('zero023.1023','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/zero023.fits'),('zero023.1056','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero023.fits'),('zero023.1285','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/zero023.fits'),('zero023.1408','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/zero023.fits'),('zero023.2703','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/zero023.fits'),('zero023.2968','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/zero023.fits'),('zero023.3138','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/zero023.fits'),('zero023.475','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero023.fits'),('zero023.7','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero023.fits'),('zero023.9','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/zero023.fits'),('zero024.1027','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/zero024.fits'),('zero024.1060','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero024.fits'),('zero024.11','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero024.fits'),('zero024.1289','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/zero024.fits'),('zero024.13','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/zero024.fits'),('zero024.1412','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/zero024.fits'),('zero024.2708','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/zero024.fits'),('zero024.2972','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/zero024.fits'),('zero024.3142','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/zero024.fits'),('zero024.480','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero024.fits'),('zero025.1031','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/zero025.fits'),('zero025.1064','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero025.fits'),('zero025.1293','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/zero025.fits'),('zero025.1416','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/zero025.fits'),('zero025.15','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero025.fits'),('zero025.17','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/zero025.fits'),('zero025.2712','fpa','fpa','path://ESSENCE/SM_SN02/sm021208/zero025.fits'),('zero025.2976','fpa','fpa','path://ESSENCE/SM_SN02/sm021206/zero025.fits'),('zero025.3146','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/zero025.fits'),('zero025.484','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero025.fits'),('zero026.1034','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/zero026.fits'),('zero026.1068','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero026.fits'),('zero026.1297','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/zero026.fits'),('zero026.19','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero026.fits'),('zero026.1904','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero026.fits'),('zero026.2339','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero026.fits'),('zero026.487','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero026.fits'),('zero026.833','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/zero026.fits'),('zero027.1038','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/zero027.fits'),('zero027.1072','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero027.fits'),('zero027.1301','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/zero027.fits'),('zero027.1907','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero027.fits'),('zero027.2343','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero027.fits'),('zero027.24','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero027.fits'),('zero027.492','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero027.fits'),('zero027.837','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/zero027.fits'),('zero028.1042','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/zero028.fits'),('zero028.1076','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero028.fits'),('zero028.1304','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/zero028.fits'),('zero028.1911','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero028.fits'),('zero028.2347','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero028.fits'),('zero028.496','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero028.fits'),('zero028.841','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/zero028.fits'),('zero029.1046','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/zero029.fits'),('zero029.1080','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero029.fits'),('zero029.1308','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/zero029.fits'),('zero029.1915','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero029.fits'),('zero029.2351','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero029.fits'),('zero029.500','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero029.fits'),('zero029.845','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/zero029.fits'),('zero030.1050','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/zero030.fits'),('zero030.1084','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero030.fits'),('zero030.1313','fpa','fpa','path://ESSENCE/SM_SN02/sm021128/zero030.fits'),('zero030.1919','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero030.fits'),('zero030.2355','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero030.fits'),('zero030.504','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero030.fits'),('zero030.849','fpa','fpa','path://ESSENCE/SM_SN02/sm021101/zero030.fits'),('zero031.1054','fpa','fpa','path://ESSENCE/SM_SN02/sm021130/zero031.fits'),('zero031.1088','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero031.fits'),('zero031.2359','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero031.fits'),('zero031.508','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero031.fits'),('zero066.26','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/zero066.fits'),('zero066.3885','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero066.fits'),('zero067.30','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/zero067.fits'),('zero067.3886','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero067.fits'),('zero068.1606','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero068.fits'),('zero068.34','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/zero068.fits'),('zero068.3887','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero068.fits'),('zero069.1609','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero069.fits'),('zero069.38','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/zero069.fits'),('zero069.3888','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero069.fits'),('zero070.1613','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero070.fits'),('zero070.3889','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero070.fits'),('zero070.42','fpa','fpa','path://ESSENCE/SM_SN02/sm021008/zero070.fits'),('zero071.1617','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero071.fits'),('zero071.3890','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero071.fits'),('zero072.1621','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero072.fits'),('zero072.3891','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero072.fits'),('zero073.21','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/zero073.fits'),('zero073.3892','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero073.fits'),('zero074.2363','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero074.fits'),('zero074.25','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/zero074.fits'),('zero074.3893','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero074.fits'),('zero075.2367','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero075.fits'),('zero075.29','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/zero075.fits'),('zero075.3894','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero075.fits'),('zero076.2371','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero076.fits'),('zero076.33','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/zero076.fits'),('zero076.3895','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero076.fits'),('zero077.2375','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero077.fits'),('zero077.37','fpa','fpa','path://ESSENCE/SM_SN02/sm021204/zero077.fits'),('zero077.3896','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero077.fits'),('zero078.2379','fpa','fpa','path://ESSENCE/SM_SN02/sm021113/zero078.fits'),('zero078.3528','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero078.fits'),('zero078.3897','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero078.fits'),('zero079.3531','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero079.fits'),('zero079.3898','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero079.fits'),('zero080.3534','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero080.fits'),('zero080.3899','fpa','fpa','path://ESSENCE/SM_SN02/sm020909/zero080.fits'),('zero081.3537','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero081.fits'),('zero082.3540','fpa','fpa','path://ESSENCE/SM_SN02/sm021109/zero082.fits'),('zero084.558','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero084.fits'),('zero085.562','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero085.fits'),('zero086.566','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero086.fits'),('zero087.2120','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero087.fits'),('zero087.570','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero087.fits'),('zero088.2124','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero088.fits'),('zero088.574','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero088.fits'),('zero089.2128','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero089.fits'),('zero089.578','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero089.fits'),('zero090.2133','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero090.fits'),('zero090.582','fpa','fpa','path://ESSENCE/SM_SN02/sm021229/zero090.fits'),('zero091.2137','fpa','fpa','path://ESSENCE/SM_SN02/sm021002/zero091.fits'),('zero105.1420','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/zero105.fits'),('zero106.1424','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/zero106.fits'),('zero107.1428','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/zero107.fits'),('zero108.1432','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/zero108.fits'),('zero109.1436','fpa','fpa','path://ESSENCE/SM_SN02/sm021004/zero109.fits'),('zero111.1092','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero111.fits'),('zero112.1096','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero112.fits'),('zero113.1100','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero113.fits'),('zero114.1104','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero114.fits'),('zero115.1108','fpa','fpa','path://ESSENCE/SM_SN02/sm021216/zero115.fits'),('zero124.3150','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/zero124.fits'),('zero125.3154','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/zero125.fits'),('zero126.176','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero126.fits'),('zero126.3157','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/zero126.fits'),('zero127.180','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero127.fits'),('zero127.28','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero127.fits'),('zero127.3161','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/zero127.fits'),('zero128.184','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero128.fits'),('zero128.3165','fpa','fpa','path://ESSENCE/SM_SN02/sm021010/zero128.fits'),('zero128.32','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero128.fits'),('zero129.188','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero129.fits'),('zero129.36','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero129.fits'),('zero130.1415','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero130.fits'),('zero130.192','fpa','fpa','path://ESSENCE/SM_SN02/sm021105/zero130.fits'),('zero130.40','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero130.fits'),('zero131.1419','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero131.fits'),('zero131.1923','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero131.fits'),('zero131.44','fpa','fpa','path://ESSENCE/SM_SN02/sm030103/zero131.fits'),('zero132.1423','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero132.fits'),('zero132.1928','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero132.fits'),('zero133.1427','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero133.fits'),('zero133.1932','fpa','fpa','path://ESSENCE/SM_SN02/sm021014/zero133.fits'),('zero134.1431','fpa','fpa','path://ESSENCE/SM_SN02/sm021030/zero134.fits'),('zero139.512','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero139.fits'),('zero140.516','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero140.fits'),('zero141.1784','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero141.fits'),('zero141.520','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero141.fits'),('zero141.658','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/zero141.fits'),('zero142.1788','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero142.fits'),('zero142.2105','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero142.fits'),('zero142.524','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero142.fits'),('zero142.662','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/zero142.fits'),('zero143.1792','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero143.fits'),('zero143.2109','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero143.fits'),('zero143.528','fpa','fpa','path://ESSENCE/SM_SN02/sm021202/zero143.fits'),('zero143.666','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/zero143.fits'),('zero144.1796','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero144.fits'),('zero144.2113','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero144.fits'),('zero144.670','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/zero144.fits'),('zero145.1800','fpa','fpa','path://ESSENCE/SM_SN02/sm021115/zero145.fits'),('zero145.2117','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero145.fits'),('zero145.674','fpa','fpa','path://ESSENCE/SM_SN02/sm021006/zero145.fits'),('zero146.2121','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero146.fits'),('zero147.2125','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero147.fits'),('zero148.2129','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero148.fits'),('zero149.2132','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero149.fits'),('zero150.2136','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero150.fits'),('zero151.2140','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero151.fits'),('zero151.2941','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero151.fits'),('zero152.2144','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero152.fits'),('zero152.2945','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero152.fits'),('zero153.2148','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero153.fits'),('zero153.2949','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero153.fits'),('zero154.2152','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero154.fits'),('zero154.2953','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero154.fits'),('zero155.2156','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero155.fits'),('zero155.2956','fpa','fpa','path://ESSENCE/SM_SN02/sm021111/zero155.fits'),('zero156.2160','fpa','fpa','path://ESSENCE/SM_SN02/sm021212/zero156.fits'),('zero157.1625','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero157.fits'),('zero158.1630','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero158.fits'),('zero159.1633','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero159.fits'),('zero160.1638','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero160.fits'),('zero161.1643','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero161.fits'),('zero162.1648','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero162.fits'),('zero165.1652','fpa','fpa','path://ESSENCE/SM_SN02/sm021214/zero165.fits');
+/*!40000 ALTER TABLE `newImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `p1PendingExp`
+--
+
+DROP TABLE IF EXISTS `p1PendingExp`;
+CREATE TABLE `p1PendingExp` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `recipe` varchar(64) default NULL,
+  `p1_version` int(11) default NULL,
+  PRIMARY KEY  (`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `p1PendingExp`
+--
+
+LOCK TABLES `p1PendingExp` WRITE;
+/*!40000 ALTER TABLE `p1PendingExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `p1PendingExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `p2Mask`
+--
+
+DROP TABLE IF EXISTS `p2Mask`;
+CREATE TABLE `p2Mask` (
+  `label` varchar(64) NOT NULL default '',
+  PRIMARY KEY  (`label`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `p2Mask`
+--
+
+LOCK TABLES `p2Mask` WRITE;
+/*!40000 ALTER TABLE `p2Mask` DISABLE KEYS */;
+/*!40000 ALTER TABLE `p2Mask` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `p2PendingExp`
+--
+
+DROP TABLE IF EXISTS `p2PendingExp`;
+CREATE TABLE `p2PendingExp` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `recipe` varchar(64) default NULL,
+  `p1_version` int(11) default NULL,
+  `p2_version` int(11) default NULL,
+  `label` varchar(64) default NULL,
+  PRIMARY KEY  (`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `p2PendingExp`
+--
+
+LOCK TABLES `p2PendingExp` WRITE;
+/*!40000 ALTER TABLE `p2PendingExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `p2PendingExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `p2PendingImfile`
+--
+
+DROP TABLE IF EXISTS `p2PendingImfile`;
+CREATE TABLE `p2PendingImfile` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `class_id` varchar(64) NOT NULL default '',
+  `uri` varchar(255) default NULL,
+  `recipe` varchar(64) default NULL,
+  `p1_version` int(11) default NULL,
+  `p2_version` int(11) default NULL,
+  PRIMARY KEY  (`exp_tag`,`class_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `p2PendingImfile`
+--
+
+LOCK TABLES `p2PendingImfile` WRITE;
+/*!40000 ALTER TABLE `p2PendingImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `p2PendingImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `p2ProcessedExp`
+--
+
+DROP TABLE IF EXISTS `p2ProcessedExp`;
+CREATE TABLE `p2ProcessedExp` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `p1_version` int(11) default NULL,
+  `p2_version` int(11) default NULL,
+  `label` varchar(64) default NULL,
+  PRIMARY KEY  (`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `p2ProcessedExp`
+--
+
+LOCK TABLES `p2ProcessedExp` WRITE;
+/*!40000 ALTER TABLE `p2ProcessedExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `p2ProcessedExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `p2ProcessedImfile`
+--
+
+DROP TABLE IF EXISTS `p2ProcessedImfile`;
+CREATE TABLE `p2ProcessedImfile` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `class_id` varchar(64) NOT NULL default '',
+  `uri` varchar(255) default NULL,
+  `recipe` varchar(64) default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `b1_uri` varchar(255) default NULL,
+  `b2_uri` varchar(255) default NULL,
+  `p1_version` int(11) default NULL,
+  `p2_version` int(11) default NULL,
+  PRIMARY KEY  (`exp_tag`,`class_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `p2ProcessedImfile`
+--
+
+LOCK TABLES `p2ProcessedImfile` WRITE;
+/*!40000 ALTER TABLE `p2ProcessedImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `p2ProcessedImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `p3Mask`
+--
+
+DROP TABLE IF EXISTS `p3Mask`;
+CREATE TABLE `p3Mask` (
+  `label` varchar(64) NOT NULL default '',
+  PRIMARY KEY  (`label`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `p3Mask`
+--
+
+LOCK TABLES `p3Mask` WRITE;
+/*!40000 ALTER TABLE `p3Mask` DISABLE KEYS */;
+/*!40000 ALTER TABLE `p3Mask` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `p3PendingExp`
+--
+
+DROP TABLE IF EXISTS `p3PendingExp`;
+CREATE TABLE `p3PendingExp` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `p2_version` int(11) default NULL,
+  `p3_version` int(11) default NULL,
+  `label` varchar(64) default NULL,
+  PRIMARY KEY  (`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `p3PendingExp`
+--
+
+LOCK TABLES `p3PendingExp` WRITE;
+/*!40000 ALTER TABLE `p3PendingExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `p3PendingExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `p3ProcessedExp`
+--
+
+DROP TABLE IF EXISTS `p3ProcessedExp`;
+CREATE TABLE `p3ProcessedExp` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `uri` varchar(255) default NULL,
+  `recipe` varchar(64) default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `sigma_ra` float default NULL,
+  `sigma_dec` float default NULL,
+  `nastro` int(11) default NULL,
+  `b1_uri` varchar(255) default NULL,
+  `b2_uri` varchar(255) default NULL,
+  `zp_mean` float default NULL,
+  `zp_stdev` float default NULL,
+  `p2_version` int(11) default NULL,
+  `p3_version` int(11) default NULL,
+  `label` varchar(64) default NULL,
+  PRIMARY KEY  (`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `p3ProcessedExp`
+--
+
+LOCK TABLES `p3ProcessedExp` WRITE;
+/*!40000 ALTER TABLE `p3ProcessedExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `p3ProcessedExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `pzDoneExp`
+--
+
+DROP TABLE IF EXISTS `pzDoneExp`;
+CREATE TABLE `pzDoneExp` (
+  `exp_id` varchar(64) NOT NULL default '',
+  `camera` varchar(64) NOT NULL default '',
+  `telescope` varchar(64) NOT NULL default '',
+  PRIMARY KEY  (`exp_id`,`camera`,`telescope`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `pzDoneExp`
+--
+
+LOCK TABLES `pzDoneExp` WRITE;
+/*!40000 ALTER TABLE `pzDoneExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `pzDoneExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `pzDoneImfile`
+--
+
+DROP TABLE IF EXISTS `pzDoneImfile`;
+CREATE TABLE `pzDoneImfile` (
+  `exp_id` varchar(64) NOT NULL default '',
+  `camera` varchar(64) NOT NULL default '',
+  `telescope` varchar(64) NOT NULL default '',
+  `class` varchar(64) NOT NULL default '',
+  `class_id` varchar(64) NOT NULL default '',
+  `exp_tag` varchar(64) default NULL,
+  `uri` varchar(255) default NULL,
+  PRIMARY KEY  (`exp_id`,`camera`,`telescope`,`class`,`class_id`),
+  UNIQUE KEY `exp_tag` (`exp_tag`),
+  KEY `exp_tag_2` (`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `pzDoneImfile`
+--
+
+LOCK TABLES `pzDoneImfile` WRITE;
+/*!40000 ALTER TABLE `pzDoneImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `pzDoneImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `pzPendingExp`
+--
+
+DROP TABLE IF EXISTS `pzPendingExp`;
+CREATE TABLE `pzPendingExp` (
+  `exp_id` varchar(64) NOT NULL default '',
+  `camera` varchar(64) NOT NULL default '',
+  `telescope` varchar(64) NOT NULL default '',
+  PRIMARY KEY  (`exp_id`,`camera`,`telescope`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `pzPendingExp`
+--
+
+LOCK TABLES `pzPendingExp` WRITE;
+/*!40000 ALTER TABLE `pzPendingExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `pzPendingExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `pzPendingImfile`
+--
+
+DROP TABLE IF EXISTS `pzPendingImfile`;
+CREATE TABLE `pzPendingImfile` (
+  `exp_id` varchar(64) NOT NULL default '',
+  `camera` varchar(64) NOT NULL default '',
+  `telescope` varchar(64) NOT NULL default '',
+  `class` varchar(64) NOT NULL default '',
+  `class_id` varchar(64) NOT NULL default '',
+  `exp_tag` varchar(64) default NULL,
+  PRIMARY KEY  (`exp_id`,`camera`,`telescope`,`class`,`class_id`),
+  UNIQUE KEY `exp_tag` (`exp_tag`),
+  KEY `exp_tag_2` (`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `pzPendingImfile`
+--
+
+LOCK TABLES `pzPendingImfile` WRITE;
+/*!40000 ALTER TABLE `pzPendingImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `pzPendingImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `rawDetrendExp`
+--
+
+DROP TABLE IF EXISTS `rawDetrendExp`;
+CREATE TABLE `rawDetrendExp` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `camera` varchar(64) default NULL,
+  `telescope` varchar(64) default NULL,
+  `exp_type` varchar(64) default NULL,
+  `imfiles` int(11) default NULL,
+  `filter` varchar(64) default NULL,
+  `airmass` float default NULL,
+  `ra` double default NULL,
+  `decl` double default NULL,
+  `exp_time` float default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `alt` double default NULL,
+  `az` double default NULL,
+  `ccd_temp` float default NULL,
+  `posang` double default NULL,
+  `object` varchar(64) default NULL,
+  `dateobs` datetime default NULL,
+  PRIMARY KEY  (`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `rawDetrendExp`
+--
+
+LOCK TABLES `rawDetrendExp` WRITE;
+/*!40000 ALTER TABLE `rawDetrendExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `rawDetrendExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `rawImfile`
+--
+
+DROP TABLE IF EXISTS `rawImfile`;
+CREATE TABLE `rawImfile` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `class` varchar(64) default NULL,
+  `class_id` varchar(64) NOT NULL default '',
+  `uri` varchar(255) default NULL,
+  `exp_type` varchar(64) default NULL,
+  `filter` varchar(64) default NULL,
+  `airmass` float default NULL,
+  `ra` double default NULL,
+  `decl` double default NULL,
+  `exp_time` float default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `alt` double default NULL,
+  `az` double default NULL,
+  `ccd_temp` float default NULL,
+  `posang` double default NULL,
+  `object` varchar(64) default NULL,
+  `dateobs` datetime default NULL,
+  PRIMARY KEY  (`exp_tag`,`class_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `rawImfile`
+--
+
+LOCK TABLES `rawImfile` WRITE;
+/*!40000 ALTER TABLE `rawImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `rawImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `rawScienceExp`
+--
+
+DROP TABLE IF EXISTS `rawScienceExp`;
+CREATE TABLE `rawScienceExp` (
+  `exp_tag` varchar(64) NOT NULL default '',
+  `camera` varchar(64) default NULL,
+  `telescope` varchar(64) default NULL,
+  `exp_type` varchar(64) default NULL,
+  `imfiles` int(11) default NULL,
+  `filter` varchar(64) default NULL,
+  `airmass` float default NULL,
+  `ra` double default NULL,
+  `decl` double default NULL,
+  `exp_time` float default NULL,
+  `bg` double default NULL,
+  `bg_stdev` double default NULL,
+  `bg_mean_stdev` double default NULL,
+  `alt` double default NULL,
+  `az` double default NULL,
+  `ccd_temp` float default NULL,
+  `posang` double default NULL,
+  `object` varchar(64) default NULL,
+  `dateobs` datetime default NULL,
+  PRIMARY KEY  (`exp_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `rawScienceExp`
+--
+
+LOCK TABLES `rawScienceExp` WRITE;
+/*!40000 ALTER TABLE `rawScienceExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `rawScienceExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `summitExp`
+--
+
+DROP TABLE IF EXISTS `summitExp`;
+CREATE TABLE `summitExp` (
+  `exp_id` varchar(64) NOT NULL default '',
+  `camera` varchar(64) NOT NULL default '',
+  `telescope` varchar(64) NOT NULL default '',
+  `dateobs` datetime default NULL,
+  `exp_type` varchar(64) default NULL,
+  `uri` varchar(255) default NULL,
+  `imfiles` int(11) default NULL,
+  PRIMARY KEY  (`exp_id`,`camera`,`telescope`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `summitExp`
+--
+
+LOCK TABLES `summitExp` WRITE;
+/*!40000 ALTER TABLE `summitExp` DISABLE KEYS */;
+/*!40000 ALTER TABLE `summitExp` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `summitImfile`
+--
+
+DROP TABLE IF EXISTS `summitImfile`;
+CREATE TABLE `summitImfile` (
+  `exp_id` varchar(64) NOT NULL default '',
+  `camera` varchar(64) NOT NULL default '',
+  `telescope` varchar(64) NOT NULL default '',
+  `file_id` varchar(64) default NULL,
+  `bytes` int(11) default NULL,
+  `md5sum` varchar(32) default NULL,
+  `class` varchar(64) NOT NULL default '',
+  `class_id` varchar(64) NOT NULL default '',
+  `uri` varchar(255) default NULL,
+  PRIMARY KEY  (`exp_id`,`camera`,`telescope`,`class`,`class_id`),
+  KEY `file_id` (`file_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Dumping data for table `summitImfile`
+--
+
+LOCK TABLES `summitImfile` WRITE;
+/*!40000 ALTER TABLE `summitImfile` DISABLE KEYS */;
+/*!40000 ALTER TABLE `summitImfile` ENABLE KEYS */;
+UNLOCK TABLES;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
Index: /tags/sj_tags/sj_root_20080929/DataChallenge/inject_essence.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataChallenge/inject_essence.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataChallenge/inject_essence.pl	(revision 22322)
@@ -0,0 +1,62 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use IPC::Cmd qw( can_run run );
+use File::Spec;
+use File::Find::Rule;
+use PS::IPP::Config;
+my $ipprc = PS::IPP::Config->new();
+
+die "Usage: $0 DIR1 DIR2 DIR3...\n" if scalar @ARGV == 0;
+
+my $pxinject = can_run('pxinject') or die "Can't find pxinject\n";
+
+my $num = 0;
+foreach my $dir ( @ARGV ) {
+    my $absdir = File::Spec->rel2abs( $dir );
+
+    $num += inject('bias', 'zero*.fits', $absdir);
+    $num += inject('flat', '?flat*.fits', $absdir);
+    $num += inject('sm', 'sm*.fits', $absdir);
+    $num += inject('w', 'w*.fits', $absdir);
+}
+
+print "$num files injected.\n";
+
+
+sub inject
+{
+    my $type = shift;		# Type of image
+    my $glob = shift;		# Glob for this type
+    my $dir  = shift;		# Directory to search (absolute path)
+
+    my @files = File::Find::Rule->file->name( $glob )->in( $dir );
+    foreach my $file ( @files ) {
+	my ( $vol, $path, $name ) = File::Spec->splitpath( $file );
+	my ( $exp ) = $name =~ /(.*)\.fits/;
+
+	my $relpath = $ipprc->convert_filename_relative( $file );
+
+	my $command_exp = "$pxinject -newExp -exp_id $exp -inst CTIO_MOSAIC2 -telescope CTIO4m " .
+	    "-exp_type $type -imfiles 1" ; # Command to run
+	my ( $success_exp, $error_code_exp, $full_buf_exp, $stdout_buf_exp, $stderr_buf_exp ) =
+	    run( command => $command_exp, verbose => 1 );
+	die "Unable to inject $exp: $error_code_exp\n" if not $success_exp;
+	
+	my @line = split(/\s+/, $$stdout_buf_exp[0]); # The output line, containing the exposure tag
+	my $exp_tag = $line[2];	# The exposure tag
+
+	my $command_imfile = "$pxinject -newImfile -exp_tag $exp_tag -class fpa -class_id fpa " .
+	    "-uri $relpath"; # Command to run
+	my ( $success_imfile, $error_code_imfile, $full_buf_imfile,
+	     $stdout_buf_imfile, $stderr_buf_imfile ) = run( command => $command_imfile, verbose => 1 );
+	die "Unable to inject $exp imfile: $error_code_imfile\n" if not $success_imfile;
+    }
+
+    return scalar @files;
+}
+
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Build
+META.yml
+Makefile
+Makefile.PL
+_build
+blib
Index: /tags/sj_tags/sj_root_20080929/DataStore/Build.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/Build.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/Build.PL	(revision 22322)
@@ -0,0 +1,40 @@
+use Module::Build;
+# See perldoc Module::Build for details of how this works
+
+Module::Build->new(
+    module_name         => 'DataStore',
+    dist_version_from   => 'lib/DataStore.pm',
+    author              => 'Joshua Hoblitt <jhoblitt@cpan.org>',
+    license             => 'gpl',
+    create_makefile_pl  => 'passthrough',
+    requires            => {
+        'Carp'                  => 0,
+        'Data::Validate::URI'   => '0.01',
+        'Digest::MD5::File'     => '0.03',
+        'IPC::Cmd'              => '0.36',
+        'File::Basename'        => 0,
+        'File::Temp'            => '0.16',
+        'File::Path'            => 0,
+        'File::stat'            => 0,
+        'LWP::UserAgent'        => 0,
+        'Params::Validate'      => '0.77',
+        'YAML'                  => '0.58',
+    },
+    build_requires      => {
+        'CGI'                   => '3',
+        'Net::HTTPServer'       => '1.1.1',
+        'Test::Cmd'             => '1.05',
+        'Test::More'            => 0,
+        'Test::Warn'            => '0.08',
+    },
+    recommends          => {
+        'Test::Distribution'    => '1.22',
+    },
+    script_files        => [qw(
+        scripts/dsget
+        scripts/dsproductls
+        scripts/dsleech
+        scripts/dsfilesetls
+        scripts/dsrootls
+    )],
+)->create_build_script;
Index: /tags/sj_tags/sj_root_20080929/DataStore/Changes
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/Changes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/Changes	(revision 22322)
@@ -0,0 +1,59 @@
+Revision history for Perl module DataStore
+
+0.08
+    - add --copies option to dsget
+    - have dsget set "user.copies" xattr on files downloaded into Nebulous
+    - change dsget to flush() & sync() downloaded files
+    - change ds*ls utilties to always return the HTTP status code - 300 as the
+      exit status
+
+0.07 Mon May 12 12:04:24 HST 2008
+    - add --timeout option to all ds*ls utilties and other minor doc changes
+    - add the ability to all DataStore::* classes that handle HTTP to pass
+      options directly to LWP::UserAgent
+    - add support for checksumming compressed fits files
+    - Change DateStore::File to handle passing args to LWP::UserAgent
+    - change dsget to return the HTTP status code on failure
+    - remove use of parse_neb_key() from dsget
+    - dsget doc updates
+    - fix error reporting bug is dsfilesetls
+    - change dsget to overwrite existing Nebulous keys
+    - trap exceptions from DateStore::File::Parse in DataStore::FileSet being
+      caused by empty file lists
+
+0.06 Thu Feb  1 13:57:56 HST 2007
+    - fix dsleech to handle valid but empty filesets
+    - change dsfilesetls to output "extra" fields
+    - add --overwrite option to dsleech
+    - distcheck fixes
+
+0.05 Fri Sep  1 11:28:13 HST 2006
+    - add --recall, --remember, & rcfile support to dsleech
+    - add a dep on YAML 0.58
+
+0.04 Thu Aug 31 13:01:59 HST 2006
+    - allow upper & lower case characters in DataStore fields
+    - change from lower to upper case all fileset types
+    - change DataStore::File to always return a DataStore::Response (even when
+      there is a problem with the retrieved file)
+    - add dsleech script
+
+0.03 Wed Aug 30 12:46:00 HST 2006
+    - fix a bug in dsproductls where ->status was being called on a
+      DataStore::Response instead of ->status_line
+    - Added a Root object and associated Product::Parser to query the root of
+      the datastore.
+    - The 'is_success' field in a Response now gets its value carried over from
+      the HTTP::Response as intended.
+    - The 'data' field in a Response can now be undef to indicate that no
+      records were parsed.
+    - Root::request, Product::request, FileSet::request now always return a
+      Response object.
+    - FileSets and Files will now have any additional non-standard fields
+      parsed into the @extra array.
+    - Updated tests.
+
+0.01 Thu Feb 23 13:55:57 2006
+    - original version; created by ExtUtils::ModuleMaker 0.44
+
+
Index: /tags/sj_tags/sj_root_20080929/DataStore/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/LICENSE	(revision 22322)
@@ -0,0 +1,260 @@
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
Index: /tags/sj_tags/sj_root_20080929/DataStore/MANIFEST
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/MANIFEST	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/MANIFEST	(revision 22322)
@@ -0,0 +1,39 @@
+Build.PL
+Changes
+LICENSE
+MANIFEST
+META.yml
+Makefile.PL
+README
+Todo
+lib/DataStore.pm
+lib/DataStore/File.pm
+lib/DataStore/File/Parser.pm
+lib/DataStore/FileSet.pm
+lib/DataStore/FileSet/Parser.pm
+lib/DataStore/Product.pm
+lib/DataStore/Product/Parser.pm
+lib/DataStore/Record.pm
+lib/DataStore/Response.pm
+lib/DataStore/Root.pm
+lib/DataStore/Utils.pm
+scripts/dsfilesetls
+scripts/dsget
+scripts/dsleech
+scripts/dsproductls
+scripts/dsrootls
+t/00_distribution.t
+t/01_load.t
+t/02_fileset_parse.t
+t/03_file_parse.t
+t/04_record.t
+t/05_product.t
+t/06_fileset.t
+t/07_file.t
+t/08_response.t
+t/09_datastore.t
+t/10_dsget.t
+t/11_dsproductls.t
+t/12_dsfilesetls.t
+t/13_dsrootls.t
+t/14_root.t
Index: /tags/sj_tags/sj_root_20080929/DataStore/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/README	(revision 22322)
@@ -0,0 +1,15 @@
+pod2text DataStore.pm > README
+
+If this is still here it means the programmer was too lazy to create the readme file.
+
+You can create it now by using the command shown above from this directory.
+
+At the very least you should be able to use this set of instructions
+to install the module...
+
+perl Build.PL
+./Build
+./Build test
+./Build install
+
+If you are on a windows box you should use 'nmake' rather than 'make'.
Index: /tags/sj_tags/sj_root_20080929/DataStore/Todo
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/Todo	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/Todo	(revision 22322)
@@ -0,0 +1,5 @@
+TODO list for Perl module DataStore
+
+- Nothing yet
+
+
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore.pm	(revision 22322)
@@ -0,0 +1,159 @@
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: DataStore.pm,v 1.11 2008-05-13 03:12:52 jhoblitt Exp $
+
+package DataStore;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.08';
+
+=pod
+
+=head1 NAME
+
+DataStore - client interface to a DataStore server
+
+=head1 SYNOPSIS
+
+    use DataStore;
+
+    # equivalent to:
+
+    use DataStore::File::Parser;
+    use DataStore::File;
+    use DataStore::FileSet::Parser;
+    use DataStore::FileSet;
+    use DataStore::Product;
+    use DataStore::Response;
+    use DataStore::Utils;
+
+=head1 DESCRIPTION
+
+This is a convenience module so that that don't have to individualy load (C<use
+...;>) all of the common DataStore inteface modules.  Please see the POD of the
+individual modules for usage information.  If this is your browsing of the
+documentation you may want to start with L<DataStore::Product>.
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+None.
+
+=head1 EXAMPLE PROGRAM
+
+    #!/usr/bin/perl
+
+    use strict;
+    use warnings;
+
+    # loads DataStore::*
+    use DataStore;
+
+    my $dsp = DataStore::Product->new(
+        uri => 'http://example.org/productid/',
+        last_fileset => 'foobarbaz',
+    );
+
+    # returns a DataStore::Response object
+    my $response = $dsp->request;
+
+    unless ($response->is_success) {
+        die $response->status;
+    }
+
+    # arrayref of DataStore::FileSet objects
+    my $filesets = $response->data;
+
+    # the query could be a success but still return no filesets
+    unless ($filesets) {
+        warn "no filesets returned";
+        exit(0);
+    }
+
+    # returns a DataStore::Response object
+    my $response2 = @$filesets[0]->request;
+
+    unless ($response2->is_success) {
+        die $response2->status;
+    }
+
+    # arrayref of DataStore::File objects
+    my $files = $response->data;
+
+    # the query could be a success but still return no files (is that legal?)
+    unless (@$files) {
+        warn "no files returned";
+        exit(0);
+    }
+
+    # requires a filename
+    my $response3 = @$files[0]->request( filename => '/dev/null' );
+    unless ($response3->is_success) {
+        warn $response3->status;
+        die;
+    }
+
+    # $response3->data is '/dev/null'
+
+=cut
+
+use DataStore::File::Parser;
+use DataStore::File;
+use DataStore::FileSet::Parser;
+use DataStore::FileSet;
+use DataStore::Product::Parser;
+use DataStore::Product;
+use DataStore::Root;
+use DataStore::Response;
+use DataStore::Utils;
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the LICENSE file included with
+this module, or in the L<perlgpl> Pod as supplied with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<DataStore::File::Parser>, L<DataStore::File>, L<DataStore::FileSet::Parser>, L<DataStore::FileSet>, L<DataStore::Product>, L<DataStore::Response>
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/File.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/File.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/File.pm	(revision 22322)
@@ -0,0 +1,338 @@
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: File.pm,v 1.18 2008-05-07 03:05:03 jhoblitt Exp $
+
+package DataStore::File;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.03';
+
+use base qw( DataStore::Record );
+
+use Carp qw( carp );
+use DataStore::Response;
+use DataStore::Utils qw( $STD_FIELD $BYTE_FIELD $MD5_FIELD %KNOWN_FILE_TYPES );
+use Digest::MD5::File qw( file_md5_hex );
+use File::Basename qw( basename );
+use File::Temp ();
+use File::stat;
+use IPC::Cmd 0.36 qw( can_run run );
+use Params::Validate qw( validate SCALAR ARRAYREF UNDEF );
+
+use vars qw( @BASE_FIELDS );
+
+@BASE_FIELDS = qw( fileid bytes md5sum type extra compressed );
+
+__PACKAGE__->mk_accessors(@BASE_FIELDS);
+
+=pod
+
+=head1 NAME
+
+DataStore::File - represents a DataStore File
+
+=head1 SYNOPSIS
+
+    use DataStore::file;
+
+    my $dsf = DateStore::File->new(
+        uri         => 'http://example.org/foo',
+        fileid      => '12buckelyourshoe',
+        bytes       => 12345,
+        md5sum      => 'fe6a2b6564c0d4cfb3bbf1db813824ba',
+        type        => 'foo',
+    );
+
+    my $uri     = $dsf->uri;
+    my fileid   = $dsf->fileid;
+    my $bytes   = $dsf-bytes;
+    my $md5sum  = $dsf->md5sum;
+    my $type    = $dsf-type;
+    my DataStore::Response $response = $dsf->request( filename => "/foo/bar" );
+
+=head1 DESCRIPTION
+
+This class I<isa> L<DataStore::Record>
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * C<new()>
+
+Basic constructor.
+
+    my $dsf = DateStore::File->new(
+        uri         => 'http://example.org/foo',
+        fileid      => '12buckelyourshoe',
+        bytes       => 12345,
+        md5sum      => 'fe6a2b6564c0d4cfb3bbf1db813824ba',
+        type        => 'foo',
+    );
+
+Accepts a mandatory hash and returns a L<DataStore::Product> object.
+
+=over 4
+
+=item * uri
+
+A valid I<HTTP> URI as a string.  I<No trailing slash is allowed.>
+
+=item * fileid
+
+The FIle ID as a string.
+
+This key is optional.
+
+=item * bytes
+
+The size of the file as an integer number of bytes.
+
+This key is optional.
+
+=item * md5sum
+
+The hex encoded md5 checksum of the file.
+
+This key is optional.
+
+=item * type
+
+The type of file as a string.
+
+This key is optional.
+
+=back
+
+=cut
+
+sub new
+{
+    my $class = shift;
+
+    # validates uri, doesn't check other params
+    my $self = $class->SUPER::new(@_);
+
+    validate(@_,
+        {
+            uri     => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid uri filename' => sub { $_[0] !~ m|/$| },
+                },
+            },
+            fileid  => {
+                type        => SCALAR,
+                regex       => $STD_FIELD,
+                optional    => 1,
+            },
+            bytes   => {
+                type        => SCALAR,
+                regex       => $BYTE_FIELD,
+                optional    => 1,
+            },
+            md5sum  => {
+                type        => SCALAR,
+                regex       => $MD5_FIELD,
+                optional    => 1,
+            },
+            type    => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid type' =>
+                        sub { exists $KNOWN_FILE_TYPES{$_[0]} },
+                },
+                optional    => 1,
+            },
+            extra   => {
+                type        => SCALAR | ARRAYREF,
+                optional    => 1,
+            },
+            compressed => {
+                type        => SCALAR | UNDEF,
+                optional    => 1,
+            },
+        },
+    );
+
+    return $self;
+}
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * C<uri()>
+
+Basic accessor.
+
+=item * C<fileid()>
+
+Basic accessor.
+
+=item * C<bytes()>
+
+Basic accessor.
+
+=item * C<md5sum()>
+
+Basic accessor.
+
+=item * C<type()>
+
+Basic accessor.
+
+=item * C<request()>
+
+Retrieves and processes the File listing pointed to by the L<uri> of this
+object.
+
+XXX writing the file into memory or to a filehandle will be implemented upon
+request.
+
+    my $response = $dsf->request( filename => "/foo/bar" );
+
+Accepts a mandatory hash and returns a L<DataStore::Response> object.
+
+=over 4
+
+=item * filename
+
+The filename/path to save the file to disk as.  It is the user's responsibility
+to make sure that this is a valid filename/path.
+
+=back
+
+=cut
+
+sub request 
+{
+    my $self = shift;
+
+    my %p = validate(@_,
+        {
+            filename    => {
+                type        => SCALAR,
+                regex       => qr/\S+/, # string with at least 1 non WS char
+            },
+            ua_args     => {
+                optional    => 1,
+            },
+        },
+    );
+
+    my $verbose = 0;
+
+    # make request
+    my $ua;
+    if (defined $p{ua_args}) {
+        $ua = LWP::UserAgent->new(%{$p{ua_args}});
+    } else {
+        $ua = LWP::UserAgent->new;
+    }
+    my $request = HTTP::Request->new(GET => $self->uri);
+    my $filename = $p{filename};
+    my $response = $ua->request($request, $filename);
+
+    if ($response->is_success) {
+        my $funpack;
+        if ($self->compressed) {
+            $funpack = can_run('funpack')
+                or warn "can't find funpack -- unable to compute checksum";
+        }
+
+        my $chk_file = $filename;
+        # so that the tmp file stays in scope until we're done processing
+        my $tmp;
+        if ($funpack) {
+            $tmp = File::Temp->new(
+                DIR         => '/tmp',
+                TEMPLATE    => basename($filename) . '.XXXXXXXX',
+                SUFFIX      => '.tmp',
+                UNLINK      => 1,
+            );
+
+            my $unpacked_path = $tmp->filename;
+
+            my $command = "$funpack -S -C $filename > $unpacked_path";
+            my ($success, $status, $full_buf, $stdout_buf, $stderr_buf)
+                = run(command => $command, verbose => $verbose);
+
+            unless ($success) {
+                die "funpack returned exit status $status\n";
+            }
+
+            $chk_file = $unpacked_path;
+        }
+
+        # check size
+        if (defined $self->bytes) {
+            my $size = stat($chk_file)->size;
+            if (! ($self->bytes == $size)) {
+                unlink $filename;
+                carp "uri: ", $self->uri,
+                     " - expected size: ", $self->bytes,
+                     " got: ", $size;
+                # set the filename to undef to indicate an error
+                $filename = undef;
+            } else {
+                carp "uri: ", $self->uri,
+                     " - expected size: ", $self->bytes,
+                     " got: ", $size
+                if $verbose;
+            }
+        }
+
+        if (defined $filename and defined $self->md5sum) {
+            my $md5 = file_md5_hex($chk_file);
+            if (! ($self->md5sum eq $md5)) {
+                unlink $filename;
+                carp "uri: ", $self->uri,
+                     " - expected md5: ", $self->md5sum,
+                     " got: ", $md5;
+                # set the filename to undef to indicate an error
+                $filename = undef;
+            } else {
+                carp "uri: ", $self->uri,
+                     " - expected md5: ", $self->md5sum,
+                     " got: ", $md5
+                if $verbose;
+            }
+        }
+    }
+
+    # return DS::Response object
+    my $dsr = DataStore::Response->new(
+        is_success  => $response->is_success,
+        code        => $response->code,
+        status_line => $response->status_line,
+        data        => $filename,
+        request     => $self,
+    );
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<DataStore::Response>
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/File/Parser.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/File/Parser.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/File/Parser.pm	(revision 22322)
@@ -0,0 +1,232 @@
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: Parser.pm,v 1.15 2007-09-26 03:41:00 jhoblitt Exp $
+
+package DataStore::File::Parser;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.02';
+
+use base qw( Class::Accessor::Fast );
+
+use Carp qw( carp );
+use Data::Validate::URI qw( is_uri ); 
+use DataStore::Utils qw( $STD_FIELD $BYTE_FIELD $MD5_FIELD %KNOWN_FILE_TYPES );
+use DataStore::File;
+use Params::Validate qw( validate validate_pos SCALAR );
+
+__PACKAGE__->mk_ro_accessors(qw(base_uri));
+
+=pod
+
+=head1 NAME
+
+DataStore::File::Parser - parses the DataStore 'File' list format
+
+=head1 SYNOPSIS
+
+    use DataStore::File::Parser;
+
+    my $parser = DataStore::File::Parser->new(
+        base_uri => 'http://example.org/',
+    );
+
+    my @data = $parser->parse($str);
+        or
+    my $data = $parser->parse($str);
+
+
+=head1 DESCRIPTION
+
+This class parses a DataStore listing of I<File>s into an array of L<DataStore::File> objects.
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * C<new()>
+
+Basic constructor.
+
+    my $parser = DataStore::File::Parser->new(
+        base_uri => 'http://example.org/',
+    );
+
+Accepts an optional hash and returns a L<DataStore::FileSet::Parser> object.
+
+=over 4
+
+=item * base_uri
+
+The base of the URI to set in the created L<DataStore::File> objects.
+
+This key is optional and defaults to C<http://example.org/>.
+
+=back
+
+=cut
+
+sub new
+{
+    my $class = shift;
+
+    my %p = validate(@_,
+        {
+            base_uri => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid http uri' =>
+                        sub { is_uri($_[0]) and $_[0] =~ /^http:/ },
+                    'uri ends with /index.txt' =>
+                        sub { $_[0] =~ m|/index.txt$| },
+
+                },
+                default =>  'http://example.org/index.txt',
+            },
+        },
+    );
+
+    my $self = bless {}, ref $class || $class;
+
+    $p{base_uri} =~ qr|(.*?/)index.txt|;
+    $self->{base_uri} = $1;
+
+    return $self;
+}
+
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * C<base_uri()>
+
+Basic accessor.
+
+=item * C<parse()>
+
+Accepts a string and returns a list in list context or an arrayref is scalar
+context.  An empty list or undef is returned if the string contained no rows.
+
+=cut
+
+sub parse
+{
+    my $self = shift;
+
+    my ($doc) = validate_pos(@_,
+        {
+            type    => SCALAR,
+            regex   => qr/\S+/, # string with at least 1 non WS char
+        }
+    );
+
+    my @data;
+    my $lineno = 1;
+LINE: foreach my $line (split /\n/, $doc) {
+        # blank lines
+        next LINE if $line =~ /^\s*$/;
+
+        # comment lines   
+        next LINE if $line =~ /^\s*\#/;
+
+        my @fields = split /\|/, $line;
+
+        # at least fileid, bytes, md5sum, and type fields are required
+        if (scalar @fields < 4) {
+            carp "line $lineno: not enough fields: $line";
+            next LINE;
+        }
+
+        foreach my $field (@fields) {
+            # fields are not allowed to contain \#
+            if ($field =~ /\#/) {
+                carp "line $lineno: field $field: contains #: $line";
+                next LINE;
+            }
+
+            # strip leading and trailing whitespace
+            $field =~ s/^\s+//;
+            $field =~ s/\s+$//;
+        }
+
+        my ($fileid, $bytes, $md5sum, $type) = @fields;
+
+        # validate format of fileid and type
+        foreach my $field ($fileid, $type) {
+            unless ($field =~ $STD_FIELD) {
+                carp "line $lineno: field $field:"
+                   . " does not conform to $STD_FIELD: $line";
+                next LINE;
+            }
+        }
+
+        # validate format of bytes
+        unless ($bytes =~ $BYTE_FIELD) {
+            carp "line $lineno: field $bytes:"
+               . " does not conform to $BYTE_FIELD: $line";
+            next LINE;
+        }
+
+        # validate format of md5sum, hex encoded
+        unless ($md5sum =~ $MD5_FIELD) {
+            # allow md5sum to be empty
+            unless ($md5sum =~ /^\s*$/) {
+                carp "line $lineno: field $md5sum:"
+                . " does not conform to $MD5_FIELD: $line";
+                next LINE;
+            }
+            $md5sum = undef;
+        }
+
+        unless (exists $KNOWN_FILE_TYPES{$type}) {
+            carp "line $lineno: type $type unknown: $line";
+            next LINE;
+        }
+
+	my @extra = @fields[4 .. $#fields] if $#fields >= 4;
+
+        my %p = (
+            fileid  => $fileid,
+            bytes   => $bytes,
+            type    => $type,
+            extra   => \@extra, 
+            uri     => $self->base_uri . $fileid,
+        );
+        $p{md5sum} = $md5sum if $md5sum;
+
+        # fifo  
+        push @data, DataStore::File->new(%p);
+    } continue {
+        $lineno++;
+    }
+
+    return unless @data;
+    return wantarray ? @data : \@data;
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<DataStore::File>, L<DataStore::FileSet::Parser>
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/FileSet.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/FileSet.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/FileSet.pm	(revision 22322)
@@ -0,0 +1,240 @@
+# Copyright (C) 2006-2008  Joshua Hoblitt
+#
+# $Id: FileSet.pm,v 1.15 2008-05-12 22:04:53 jhoblitt Exp $
+
+package DataStore::FileSet;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.04';
+
+use base qw( DataStore::Record );
+
+use Carp qw( carp );
+use DataStore::File::Parser;
+use DataStore::Response;
+use DataStore::Utils qw( $STD_FIELD $TIME_FIELD %KNOWN_FILESET_TYPES );
+use LWP::UserAgent;
+use Params::Validate qw( validate SCALAR ARRAYREF );
+
+use vars qw( @BASE_FIELDS );
+
+@BASE_FIELDS = qw( fileset datetime type extra );
+
+__PACKAGE__->mk_accessors(@BASE_FIELDS);
+
+=pod
+
+=head1 NAME
+
+DataStore::FileSet - represents a DataStore FileSet 
+
+=head1 SYNOPSIS
+
+    use DataStore::FileSet;
+
+    my $dsfs = DateStore::FileSet->new(
+        uri         => 'http://example.org/',
+        fileset     => '12buckelyourshoe',
+        datetime    => '2042-01-01T00:00:00Z',
+        type        => 'foo',
+    );
+
+    my $uri         = $dsfs->uri;
+    my $fileset     = $dsfs->fileset;
+    my $datatime    = $dsfs->datetime;
+    my $type        = $dsfs->type;
+    my DataStore::Response $response = $dsfs->request;
+
+=head1 DESCRIPTION
+
+This class I<isa> L<DataStore::Record>
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * C<new()>
+
+Basic constructor.
+
+    my $dsfs = DateStore::FileSet->new(
+        uri         => 'http://example.org/',
+        fileset     => '12buckelyourshoe',
+        datetime    => '2042-01-01T00:00:00Z',
+        type        => 'foo',
+    );
+
+Accepts a mandatory hash and returns a L<DataStore::FileSet> object.
+
+=over 4
+
+=item * uri
+
+A valid I<HTTP> URI as a string.  I<A trailing slash is required.>
+
+=item * fileset
+
+The FileSet ID as a string.
+
+This key is optional.
+
+=item * datetime
+
+The time and date as a string.
+
+This key is optional.
+
+=item * type
+
+The I<type> of record as a string.
+
+This key is optional.
+
+=back
+
+=cut
+
+sub new
+{
+    my $class = shift;
+
+    # validates uri, doesn't check other params
+    my $self = $class->SUPER::new(@_);
+
+    validate(@_,
+        {
+            uri         => {
+                type        => SCALAR,
+                callbacks   => {
+                    'uri ends with /index.txt'
+                        => sub { $_[0] =~ m|/index.txt$| },
+                }
+            },
+            fileset     => {
+                type        => SCALAR,
+                regex       => $STD_FIELD,
+                optional    => 1,
+            },
+            datetime    => {
+                type        => SCALAR,
+                regex       => $TIME_FIELD,
+                optional    => 1,
+            },
+            type        => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid type' => 
+                        sub { exists $KNOWN_FILESET_TYPES{$_[0]} },
+                },
+                optional    => 1,
+            },
+            extra       => {
+                type        => SCALAR | ARRAYREF,
+                optional    => 1,
+            },
+        },
+    );
+
+    return $self;
+}
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * C<uri()>
+
+Basic accessor.
+
+=item * C<fileset()>
+
+Basic accessor.
+
+=item * C<datetime()>
+
+Basic accessor.
+
+=item * C<type()>
+
+Basic accessor.
+
+=item * C<request()>
+
+Retrieves and processes the File listing pointed to by the L<uri> of this
+object.
+
+Accepts no parameters and returns a L<DataStore::Response> object.
+
+=cut
+
+sub request 
+{
+    my $self = shift;
+
+    my %p = validate(@_,
+        {
+            ua_args     => {
+                optional    => 1,
+            },
+        },
+    );
+
+    # make request
+    my $ua;
+    if (defined $p{ua_args}) {
+        $ua = LWP::UserAgent->new(%{$p{ua_args}});
+    } else {
+        $ua = LWP::UserAgent->new;
+    }
+    my $request = HTTP::Request->new(GET => $self->uri);
+    my $response = $ua->request($request);
+
+    my $data;
+
+    if ($response->is_success) {
+        # parse document
+        my $parser = DataStore::File::Parser->new(base_uri => $self->uri);
+
+        eval {
+            $data = $parser->parse($response->content);
+        };
+        if ($@) {
+            carp "error parsing DataStore File format: $@";
+        }
+    }
+
+    # return DS::Response object
+    my $dsr = DataStore::Response->new(
+        is_success  => $response->is_success,
+        code        => $response->code,
+        status_line => $response->status_line,
+        data        => $data,
+        request     => $self,
+    );
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<DataStore::Response>
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/FileSet/Parser.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/FileSet/Parser.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/FileSet/Parser.pm	(revision 22322)
@@ -0,0 +1,215 @@
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: Parser.pm,v 1.17 2007-09-26 00:09:52 jhoblitt Exp $
+
+package DataStore::FileSet::Parser;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.02';
+
+use base qw( Class::Accessor::Fast );
+
+use Carp qw( carp );
+use Data::Validate::URI qw( is_uri );
+use DataStore::FileSet;
+use DataStore::Utils qw( $STD_FIELD $TIME_FIELD %KNOWN_FILESET_TYPES );
+use Params::Validate qw( validate validate_pos SCALAR);
+
+__PACKAGE__->mk_ro_accessors(qw(base_uri));
+
+=pod
+
+=head1 NAME
+
+DataStore::FileSet::Parser - parses the DataStore 'FileSet' list format
+
+=head1 SYNOPSIS
+
+    use DataStore::FileSet::Parser;
+
+    my $parser = DataStore::FileSet::Parser->new(
+        base_uri => 'http://example.org/index.txt',
+    );
+
+    my @data = $parser->parse($str);
+        or
+    my $data = $parser->parse($str);
+
+=head1 DESCRIPTION
+
+This class parses a DataStore listing of I<FileSet>s into an array of L<DataStore::FileSet> objects.
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * C<new()>
+
+Basic constructor.
+
+    my $parser = DataStore::FileSet::Parser->new(
+        base_uri => 'http://example.org/index.txt',
+    );
+
+Accepts an optional hash and returns a L<DataStore::FileSet::Parser> object.
+
+=over 4
+
+=item * base_uri
+
+The base of the URI to set in the created L<DataStore::FileSet> objects.
+
+This key is optional and defaults to C<http://example.org/>.
+
+=back
+
+=cut
+
+sub new
+{
+    my $class = shift;
+
+    my %p = validate(@_,
+        {
+            base_uri => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid http uri' =>
+                        sub { is_uri($_[0]) and $_[0] =~ /^http:/ },
+                    'uri ends with /index.txt' =>
+                        sub { $_[0] =~ m|/index.txt$| },
+                },
+                default =>  'http://example.org/index.txt',
+            },
+        },
+    );
+
+    my $self = bless {}, ref $class || $class;
+
+    $p{base_uri} =~ qr|(.*?/)index.txt|;
+    $self->{base_uri} = $1; 
+
+    return $self;
+}
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * C<base_uri()>
+
+Basic accessor.
+
+=item * C<parse()>
+
+Accepts a string and returns a list in list context or an arrayref is scalar
+context.  An empty list or undef is returned if the string contained no rows.
+
+=cut
+
+sub parse
+{
+    my $self = shift;
+
+    my ($doc) = validate_pos(@_,
+        {
+            type    => SCALAR,
+            regex   => qr/\S+/, # string with at least 1 non WS char
+        }
+    );
+
+    my @data;
+    my $lineno = 1;
+LINE: foreach my $line (split /\n/, $doc) {
+        # blank lines
+        next LINE if $line =~ /^\s*$/;
+
+        # comment lines   
+        next LINE if $line =~ /^\s*\#/;
+
+        my @fields = split /\|/, $line;
+
+        # at least fileset, datatime, and type fields are required
+        if (scalar @fields < 3) {
+            carp "line $lineno: not enough fields: $line";
+            next LINE;
+        }
+
+        foreach my $field (@fields) {
+            # fields are not allowed to contain #
+            if ($field =~ /\#/) {
+                carp "line $lineno: field $field: contains #: $line";
+                next LINE;
+            }
+
+            # strip leading and trailing whitespace
+            $field =~ s/^\s+//;
+            $field =~ s/\s+$//;
+        }
+
+        my ($fileset, $datetime, $type) = @fields;
+
+        # validate format of fileset and type
+        foreach my $field ($fileset, $type) {
+            unless ($field =~ $STD_FIELD) {
+                carp "line $lineno: field $field:"
+                   . " does not conform to $STD_FIELD: $line";
+                next LINE;
+            }
+        }
+
+        # validate format of datetime
+        unless ($datetime =~ $TIME_FIELD) {
+            carp "line $lineno: field $datetime:"
+               . " does not conform to $TIME_FIELD";
+            next LINE;
+        }
+
+        unless (exists $KNOWN_FILESET_TYPES{$type}) {
+            carp "line $lineno: type $type unknown: $line";
+            next LINE;
+        } 
+
+        my @extra = @fields[3 .. $#fields] if $#fields >= 3;
+
+        # fifo
+        push @data, DataStore::FileSet->new({
+            fileset     => $fileset,
+            datetime    => $datetime,
+            type        => $type,
+            extra       => \@extra, 
+            uri         => $self->base_uri . $fileset . '/index.txt',
+        });
+    } continue {
+        $lineno++;
+    }
+
+    return unless @data;
+    return wantarray ? @data : \@data;
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<DataStore::FileSet>, L<DataStore::File:Parser>
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Product.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Product.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Product.pm	(revision 22322)
@@ -0,0 +1,224 @@
+# Copyright (C) 2006-2008  Joshua Hoblitt
+#
+# $Id: Product.pm,v 1.11 2008-05-12 22:04:53 jhoblitt Exp $
+
+package DataStore::Product;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.02';
+
+use base qw( DataStore::Record );
+
+use Carp qw( carp );
+use DataStore::FileSet::Parser;
+use DataStore::Record;
+use DataStore::Response;
+use DataStore::Utils qw( $STD_FIELD $TIME_FIELD %KNOWN_PRODUCT_TYPES );
+use LWP::UserAgent;
+use Params::Validate qw( validate SCALAR);
+
+use vars qw( @BASE_FIELDS );
+
+@BASE_FIELDS = qw( product last_fileset last_datetime type desc );
+
+__PACKAGE__->mk_ro_accessors(@BASE_FIELDS);
+
+=pod
+
+=head1 NAME
+
+DataStore::Product - represents a DataStore Product ID
+
+=head1 SYNOPSIS
+
+    use DataStore::Product;
+
+    my $dsp = DataStore::Product->new(
+        uri             => 'http://example.com/productid/',
+        last_fileset    => 'foobar',
+    );
+
+    my $uri     = $dsp->uri;
+    my $fileset = $dsp_fileset;
+    my DataStore::Response $response = $dsp->request;
+
+=head1 DESCRIPTION
+
+This class I<isa> L<DataStore::Record>
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * C<new()>
+
+Basic constructor.
+
+    my $dsp = DataStore::Product->new(
+        uri             => 'http://example.com/productid/',
+        last_fileset    => 'foobar',
+    );
+
+Accepts a mandatory hash and returns a L<DataStore::Product> object.
+
+=over 4
+
+=item * uri
+
+A valid I<HTTP> URI as a string.  I<A trailing slash is required.>
+
+=item * last_fileset
+
+The last FileSet ID that was requested as a string.
+
+This key is optional.
+
+=back
+
+=cut
+
+sub new
+{
+    my $class = shift;
+
+    # validates uri, doesn't check other params
+    my $self = $class->SUPER::new(@_);
+
+    validate(@_,
+        {
+            uri             => {
+                type        => SCALAR,
+                callbacks   => {
+                    'uri ends with /index.txt'
+                        => sub { $_[0] =~ m|/index.txt$| },
+                }
+            },
+            product         => {
+                type        => SCALAR,
+                optional    => 1,
+            },
+            last_fileset    => { 
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid fileset ID' => sub { $_[0] =~ $STD_FIELD },
+                },
+                optional    => 1,
+            },
+            last_datetime    => {
+                type        => SCALAR,
+                regex       => $TIME_FIELD,
+                optional    => 1,
+            },
+            type        => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid type' =>
+                        sub { exists $KNOWN_PRODUCT_TYPES{$_[0]} },
+                },
+                optional    => 1,
+            },
+            desc        => {
+                 type       => SCALAR,
+                 optional   => 1,
+            },
+        },
+    );
+
+    return $self;
+}
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * C<uri()>
+
+Basic accessor.
+
+=item * C<last_fileset()>
+
+Basic accessor.
+
+=item * C<request()>
+
+Retrieves and processes the FileSet listing pointed to by the L<uri> of this
+object.
+
+Accepts no parameters and returns a L<DataStore::Response> object.
+
+=cut
+
+sub request 
+{
+    my $self = shift;
+
+    my %p = validate(@_,
+        {
+            ua_args     => {
+                optional    => 1,
+            },
+        },
+    );
+
+    # make request
+    my $ua;
+    if (defined $p{ua_args}) {
+        $ua = LWP::UserAgent->new(%{$p{ua_args}});
+    } else {
+        $ua = LWP::UserAgent->new;
+    }
+
+    my $request;
+    if ($self->last_fileset) {
+        $request = HTTP::Request->new(
+            GET => $self->uri . "?" . $self->last_fileset,
+        );
+    } else {
+        $request = HTTP::Request->new(GET => $self->uri);
+    }
+    my $response = $ua->request($request);
+
+    my $data;
+
+    if ($response->is_success) {
+        # parse document
+        my $parser = DataStore::FileSet::Parser->new(base_uri => $self->uri);
+
+        $data = $parser->parse($response->content);
+    }
+
+    # return DS::Response object
+    my $dsr = DataStore::Response->new(
+        is_success  => $response->is_success,
+        code        => $response->code,
+        status_line => $response->status_line,
+        data        => $data,
+        request     => $self,
+    );
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<DataStore::Response>
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Product/Parser.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Product/Parser.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Product/Parser.pm	(revision 22322)
@@ -0,0 +1,215 @@
+# Adapted from FileSet/Parser.pm
+#
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: Parser.pm,v 1.2 2007-09-26 00:13:29 jhoblitt Exp $
+
+package DataStore::Product::Parser;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.01';
+
+use base qw( Class::Accessor::Fast );
+
+use Carp qw( carp );
+use Data::Validate::URI qw( is_uri );
+use DataStore::Product;
+use DataStore::Utils qw( $STD_FIELD $TIME_FIELD %KNOWN_PRODUCT_TYPES );
+use Params::Validate qw( validate validate_pos SCALAR);
+
+__PACKAGE__->mk_ro_accessors(qw(base_uri));
+
+=pod
+
+=head1 NAME
+
+DataStore::Product::Parser - parses the DataStore 'Product' list format
+
+=head1 SYNOPSIS
+
+    use DataStore::Product::Parser;
+
+    my $parser = DataStore::Product::Parser->new(
+        base_uri => 'http://example.org/index.txt',
+    );
+
+    my @data = $parser->parse($str);
+        or
+    my $data = $parser->parse($str);
+
+=head1 DESCRIPTION
+
+This class parses a root DataStore listing into an array of L<DataStore::Product> objects.
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * C<new()>
+
+Basic constructor.
+
+    my $parser = DataStore::Product::Parser->new(
+        base_uri => 'http://example.org/index.txt',
+    );
+
+Accepts an optional hash and returns a L<DataStore::Product::Parser> object.
+
+=over 4
+
+=item * base_uri
+
+The base of the URI to set in the created L<DataStore::Product> objects.
+
+This key is optional and defaults to C<http://example.org/>.
+
+=back
+
+=cut
+
+sub new
+{
+    my $class = shift;
+
+    my %p = validate(@_,
+        {
+            base_uri => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid http uri' =>
+                        sub { is_uri($_[0]) and $_[0] =~ /^http:/ },
+                    'uri ends with /index.txt' =>
+                        sub { $_[0] =~ m|/index.txt$| },
+
+                },
+                default =>  'http://example.org/index.txt',
+            },
+        },
+    );
+
+    my $self = bless {}, ref $class || $class;
+
+    $p{base_uri} =~ qr|(.*?/)index.txt|;
+    $self->{base_uri} = $1;
+
+    return $self;
+}
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * C<base_uri()>
+
+Basic accessor.
+
+=item * C<parse()>
+
+Accepts a string and returns a list in list context or an arrayref is scalar
+context.  An empty list or undef is returned if the string contained no rows.
+
+=cut
+
+sub parse
+{
+    my $self = shift;
+
+    my ($doc) = validate_pos(@_,
+        {
+            type    => SCALAR,
+            regex   => qr/\S+/, # string with at least 1 non WS char
+        }
+    );
+
+    my @data;
+    my $lineno = 1;
+LINE: foreach my $line (split /\n/, $doc) {
+        # blank lines
+        next LINE if $line =~ /^\s*$/;
+
+        # comment lines   
+        next LINE if $line =~ /^\s*\#/;
+
+        my @fields = split /\|/, $line;
+
+        # at least fileset, datatime, and type fields are required
+        if (scalar @fields < 5) {
+            carp "line $lineno: not enough fields: $line";
+            next LINE;
+        }
+
+        foreach my $field (@fields) {
+            # fields are not allowed to contain #
+            if ($field =~ /\#/) {
+                carp "line $lineno: field $field: contains #: $line";
+                next LINE;
+            }
+        
+            # strip leading and trailing whitespace
+            $field =~ s/^\s+//;
+            $field =~ s/\s+$//;
+        }
+
+        my ($product, $last_fileset, $last_datetime, $type, $desc) = @fields;
+
+        # validate format of fileset
+        unless ($last_fileset =~ $STD_FIELD) {
+            carp "line $lineno: field $last_fileset:"
+               . " does not conform to $STD_FIELD: $line";
+            next LINE;
+        }
+
+        # validate format of datetime
+        unless ($last_datetime =~ $TIME_FIELD) {
+            carp "line $lineno: field $last_datetime:"
+               . " does not conform to $TIME_FIELD";
+            next LINE;
+        }
+
+        unless (exists $KNOWN_PRODUCT_TYPES{$type}) {
+            carp "line $lineno: type $type unknown: $line";
+            next LINE;
+        } 
+
+        # fifo
+        push @data, DataStore::Product->new({
+            product       => $product,
+            last_fileset  => $last_fileset,
+            last_datetime => $last_datetime,
+            type          => $type,
+            desc          => $desc,
+            uri           => $self->base_uri . $product . '/index.txt',
+        });
+    } continue {
+        $lineno++;
+    }
+
+    return unless @data;
+    return wantarray ? @data : \@data;
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<DataStore::Product>, L<DataStore::FileSet::Parser>
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Record.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Record.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Record.pm	(revision 22322)
@@ -0,0 +1,118 @@
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: Record.pm,v 1.7 2006-03-16 22:03:53 jhoblitt Exp $
+
+package DataStore::Record;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.01';
+
+use base qw( Class::Accessor::Fast );
+
+use Carp qw( carp croak );
+use Data::Validate::URI qw( is_uri );
+use Params::Validate qw( validate_with validate SCALAR );
+
+use vars qw( @BASE_FIELDS );
+
+@BASE_FIELDS = qw( uri );
+
+__PACKAGE__->mk_ro_accessors(@BASE_FIELDS);
+
+=pod
+
+=head1 NAME
+
+DataStore::Record - base class of DataStore list records or rows
+
+=head1 SYNOPSIS
+
+    use base qw( DataStore::Record );
+
+=head1 DESCRIPTION
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * C<new()>
+
+Basic constructor.
+
+    my $dsp = DataStore::Record->new(
+        uri => 'http://example.com/',
+    );
+
+Accepts a mandatory hash and returns a L<DataStore::Record> object.
+
+=over 4
+
+=item * uri
+
+A valid I<HTTP> URI as a string. 
+
+=back
+
+=cut
+
+sub new
+{
+    my $class = shift;
+
+    my %p = validate_with(
+        params  => \@_,
+        spec    => {
+            uri => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid http uri' =>
+                        sub { is_uri($_[0]) and $_[0] =~ /^http:/ },
+                },
+                default =>  'http://example.org/',
+            },
+        },
+        allow_extra => 1,
+    );
+
+    my $self = bless \%p, ref $class || $class;
+
+    return $self;
+}
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * C<uri()>
+
+=cut
+
+=item * C<request()>
+
+This method must be overloaded in sub-classes.
+
+=cut
+
+sub request
+{
+    croak "sub classes of " . __PACKAGE__
+        . " must override the ->request() method";
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Response.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Response.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Response.pm	(revision 22322)
@@ -0,0 +1,173 @@
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: Response.pm,v 1.4 2006-07-22 01:17:33 smalle Exp $
+
+package DataStore::Response;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.01';
+
+use base qw( Class::Accessor::Fast );
+
+use Params::Validate qw( validate ARRAYREF BOOLEAN SCALAR UNDEF );
+
+use vars qw( @BASE_FIELDS );
+
+@BASE_FIELDS = qw( is_success code status_line data request );
+
+__PACKAGE__->mk_accessors(@BASE_FIELDS);
+
+=pod
+
+=head1 NAME
+
+DataStore::Response - represents a DataStore response
+
+=head1 SYNOPSIS
+
+    use DataStore::Response;
+
+    my $dsr = DateStore::Response->new(
+        is_success  => undef,
+        code        => 500,
+        status_line => 'foo',
+        data        => 'bar',
+        request     => DataStore::Product->new( uri => 'http://example.org/' ),
+    );
+
+    if ($data) {
+        my $success     = $dsr->is_success;
+        my $code        = $dsr->code;
+        my $status_line = $dsr->status_line;
+        my $data        = $dsr->data;
+        my DataStore::Response $response = $dsp->request;
+    }
+
+=head1 DESCRIPTION
+
+This class represent the return state of the C<request()> method in
+L<DataStore::Record> sub-classes.
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * C<new()>
+
+Basic constructor.
+
+    my $dsr = DateStore::Response->new(
+        is_success  => undef,
+        code        => 500,
+        status_line => 'foo',
+        data        => 'bar',
+        request     => DataStore::Product->new( uri => 'http://example.org/' ),
+    );
+
+Accepts a mandatory hash and returns a L<DataStore::Product> object.
+
+=over 4
+
+=item * uri
+
+A vvalid I<HTTP> URI as a string.
+
+=item * code
+
+An HTTP status code.
+
+=item * status_line
+
+An HTTP status line.
+
+=item * data
+
+A scalar value, an arrayref of scalar data, or undef.
+
+=item * request
+
+An object that I<isa> L<DataStore::Record>.
+
+=back
+
+=cut
+
+sub new
+{
+    my $class = shift;
+
+    my %p = validate(@_,
+        {
+            is_success  => {
+                type        => BOOLEAN,
+                callbacks   => {
+                    'is 0, 1, or undef' =>
+                        sub { ! defined( $_[0] ) || $_[0] == 0 || $_[0] == 1 },
+                },
+            },
+            code        => {
+                type        => SCALAR,
+                regex       => qr/^\d{3}$/,
+            },
+            status_line => {
+                type        => SCALAR,
+                regex       => qr/\S+/, # string with atleast 1 non WS char
+
+            },
+            data        => {
+                type        => SCALAR | ARRAYREF | UNDEF,
+            },
+            request     => {
+                isa         => qw( DataStore::Record ),
+            },
+        }
+    );
+
+    my $self = bless \%p, ref $class || $class;
+
+    return $self;
+}
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * C<uri()>
+
+Basic accessor.
+
+=item * C<code()>
+
+Basic accessor.
+
+=item * C<status_line()>
+
+Basic accessor.
+
+=item * C<data()>
+
+Basic accessor.
+
+=item * C<request()>
+
+Basic accessor.
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Root.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Root.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Root.pm	(revision 22322)
@@ -0,0 +1,181 @@
+# Adapted from Product.pm
+#
+# Copyright (C) 2006-2008  Joshua Hoblitt
+#
+# $Id: Root.pm,v 1.5 2008-05-12 22:04:53 jhoblitt Exp $
+
+package DataStore::Root;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.02';
+
+use base qw( DataStore::Record );
+
+use Carp qw( carp );
+use DataStore::Product::Parser;
+use DataStore::Record;
+use DataStore::Response;
+use DataStore::Utils qw( $STD_FIELD );
+use LWP::UserAgent;
+use Params::Validate qw( validate SCALAR);
+
+use vars qw( @BASE_FIELDS );
+
+@BASE_FIELDS = qw( last_fileset );
+
+__PACKAGE__->mk_ro_accessors(@BASE_FIELDS);
+
+=pod
+
+=head1 NAME
+
+DataStore::Root - container for root DataStore index
+
+=head1 SYNOPSIS
+
+    use DataStore::Root;
+
+    my $dsp = DataStore::Root->new(
+        uri             => 'http://example.com/productid/',
+    );
+
+    my DataStore::Response $response = $dsp->request;
+
+=head1 DESCRIPTION
+
+This class I<isa> L<DataStore::Record>
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * C<new()>
+
+Basic constructor.
+
+    my $dsp = DataStore::Root->new(
+        uri             => 'http://example.com/productid/',
+    );
+
+Accepts a mandatory hash and returns a L<DataStore::Root> object.
+
+=over 4
+
+=item * uri
+
+A valid I<HTTP> URI as a string.  I<A trailing slash is required.>
+
+=back
+
+=cut
+
+sub new
+{
+    my $class = shift;
+
+    # validates uri, doesn't check other params
+    my $self = $class->SUPER::new(@_);
+
+    validate(@_,
+        {
+            uri             => {
+                type        => SCALAR,
+                callbacks   => {
+                    'uri ends with /index.txt' =>
+                        sub { $_[0] =~ m|/index.txt$| },
+                }
+            },
+       },
+    );
+
+    return $self;
+}
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * C<uri()>
+
+Basic accessor.
+
+=item * C<last_fileset()>
+
+Basic accessor.
+
+=item * C<request()>
+
+Retrieves and processes the FileSet listing pointed to by the L<uri> of this
+object.
+
+Accepts no parameters and returns a L<DataStore::Response> object.
+
+=cut
+
+sub request 
+{
+    my $self = shift;
+
+    my %p = validate(@_,
+        {
+            ua_args     => {
+                optional    => 1,
+            },
+        },
+    );
+
+    # make request
+    my $ua;
+    if (defined $p{ua_args}) {
+        $ua = LWP::UserAgent->new(%{$p{ua_args}});
+    } else {
+        $ua = LWP::UserAgent->new;
+    }
+
+    my $request = HTTP::Request->new(GET => $self->uri);
+    my $response = $ua->request($request);
+
+    my $data;
+
+    if ($response->is_success) {
+        # parse document
+        my $parser = DataStore::Product::Parser->new(base_uri => $self->uri);
+
+        $data = $parser->parse($response->content);
+    }
+
+    # return DS::Response object
+    my $dsr = DataStore::Response->new(
+        is_success  => $response->is_success,
+        code        => $response->code,
+        status_line => $response->status_line,
+        data        => $data,
+        request     => $self,
+    );
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<DataStore::Response>
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Utils.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Utils.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/lib/DataStore/Utils.pm	(revision 22322)
@@ -0,0 +1,104 @@
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: Utils.pm,v 1.13 2008-08-29 01:39:48 bills Exp $
+
+package DataStore::Utils;
+
+use strict;
+use warnings;
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use base qw( Exporter );
+
+use Carp qw( carp );
+use Data::Validate::URI qw( is_uri );
+
+use vars qw(
+    @EXPORT_OK
+    $STD_FIELD
+    $TIME_FIELD
+    $BYTE_FIELD
+    $MD5_FIELD
+    %KNOWN_FILE_TYPES
+    %KNOWN_FILESET_TYPES
+    %KNOWN_PRODUCT_TYPES
+);
+
+@EXPORT_OK = qw(
+    @EXPORT_OK
+    $STD_FIELD
+    $TIME_FIELD
+    $BYTE_FIELD
+    $MD5_FIELD
+    %KNOWN_FILE_TYPES
+    %KNOWN_FILESET_TYPES
+    %KNOWN_PRODUCT_TYPES
+);
+
+$STD_FIELD = qr/^[A-z0-9-_.: ]+$/;
+$TIME_FIELD = qr/^(\d{4})-(\d\d)-(\d\d) T (\d\d):(\d\d):(\d\d) Z$/x;
+$BYTE_FIELD = qr/^\d+$/;
+$MD5_FIELD = qr/^[0-9a-f]{32}$/;
+%KNOWN_FILE_TYPES = map { $_ => 1 } qw( chip psrequest psresults pstamp chipproc warp stack diff ipp-mops table text xml tgz);
+%KNOWN_FILESET_TYPES = map { $_ => 1 } qw( OBJECT BIAS DARK SKYFLAT DOMEFLAT OOF SHACKHARTMANN PSREQUEST PSRESULTS IPP-MOPS XRAY FOCUS MOPS_DETECTABILITY_QUERY MOPS_DETECTABILITY_RESPONSE MOPS_TRANSIENT_DETECTIONS LED notset IPP_PSPS);
+%KNOWN_PRODUCT_TYPES = map { $_ => 1 } qw( image dump psrequest psresults table );
+
+=pod
+
+=head1 NAME
+
+DataStore::Utils - functions and/or regexs common to DataStore::* modules
+
+=head1 SYNOPSIS
+
+    use DataStore::Utils qw( ... );
+
+=head1 DESCRIPTION
+
+This class parses a DataStore listing of I<FileSet>s into an array of L<DataStore::FileSet> objects.
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module exports no I<symbols> by default. It will export these symbols upon request:
+
+=over 4
+
+=item * C<$STD_FIELD>
+
+A C<qr> for the standard DataStore list field type.
+
+=item * C<$TIME_FIELD>
+
+A C<qr> for the standard DataStore time field type.
+
+=item * C<$BYTE_FIELD>
+
+A C<qr> for the standard DataStore byte field type.
+
+=item * C<$MD5_FIELD>
+
+A C<qr> for the standard DataStore hex encoded md5 field type.
+
+=item * C%<KNOWN_FILE_TYPES>
+
+A a hash of the known I<File> types.
+
+=item * C%<KNOWN_FILESET_TYPES>
+
+A a hash of the known I<FileSet> types.
+
+=back
+
+=head2 Methods
+
+None.
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsfilesetls
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsfilesetls	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsfilesetls	(revision 22322)
@@ -0,0 +1,131 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006-2008  Joshua Hoblitt
+#
+# $Id: dsfilesetls,v 1.10 2008-05-20 01:09:29 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use DataStore;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($uri, $timeout);
+
+GetOptions(
+    'uri|u=s'           => \$uri,
+    'timeout|t=s'       => \$timeout,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --uri",
+    -exitval => 3,
+) unless defined $uri;
+
+# default http request timeout is 30s
+$timeout ||= 30;
+
+my $response = DataStore::FileSet->new( uri => $uri )->request(
+        ua_args  => { timeout => $timeout },
+    );
+
+unless (defined $response and $response->is_success) {
+    warn "request failed: ", $response->status_line;
+    exit($response->code - 300);
+}
+
+# file retreival succeed
+
+my $data = $response->data;
+
+unless ($data) {
+    warn "no files found";
+    exit(0);
+}
+
+print "# uri fileid bytes md5sum type \n";
+foreach my $fs (@$data) {
+    print 
+        $fs->uri, " ",
+        $fs->fileid, " ",
+        $fs->bytes, " ",
+        $fs->md5sum || 0, " ",
+        $fs->type;
+    print " ", join(" ", @{$fs->extra}) if defined $fs->extra;   
+    print "\n";
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+dsfilesetls - list all the Files under a FileSet from a DataStore server
+
+=head1 SYNOPSIS
+
+    dsfilesetls --uri <uri> 
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --uri|-u <uri>
+
+The URI of the file to be retrieved.
+
+=item * --timeout|-t
+
+The ammount of time (in seconds) to wait for a response from the DataStore
+after making an HTTP request.  The default is 30s.
+
+Optional.
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<dsget>, L<dsleech>, L<dsrootls>, L<dsproductls>, L<dsfilesetls>, L<DataStore>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsget
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsget	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsget	(revision 22322)
@@ -0,0 +1,330 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006-2008  Joshua Hoblitt
+#
+# $Id: dsget,v 1.35 2008-09-24 21:58:03 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use DataStore;
+use File::Path qw( mkpath );
+use File::Temp ();
+use File::Basename qw( basename dirname );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($uri,
+    $bytes,
+    $copies,
+    $md5,
+    $nebulous,
+    $filename,
+    $volume,
+    $server,
+    $compress,
+    $timeout
+);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'uri|u=s'       => \$uri,
+    'bytes|m=s'     => \$bytes,
+    'copies=i'      => \$copies,
+    'nebulous|n'    => \$nebulous,
+    'volume|v=s'    => \$volume,
+    'server|s=s'    => \$server,
+    'md5|m=s'       => \$md5,
+    'filename|f=s'  => \$filename,
+    'compress|c'    => \$compress,
+    'timeout|t=s'     => \$timeout,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --uri --filename",
+    -exitval => 3,
+) unless defined $uri and defined $filename;
+
+# XXX EAM : should not need parse_neb_key, but how to handle case
+# where we supply --volume and neb://volume/path ? 
+# 1) parse anyway and check for conflict
+# 2) drop --volume option
+# JH: the Nebulous server contains the policy to handle this conflict.
+# At present --volume will overide the volume in neb://volume/...
+
+# --volume implies --nebulous
+if ($volume) {
+    $nebulous ||=1;
+} 
+
+if (defined $copies) {
+    if (not defined $nebulous) {
+        warn "--copies is meanless without --nebulous or --volume";
+        undef $copies;
+    }
+    if ($copies < 1) {
+        die "--copies must be >= 1";
+    }
+}
+
+if ($compress) {
+    $uri = "$uri?compress";
+}
+
+# default http request timeout is 30s
+$timeout ||= 30;
+
+my %p = (
+    uri     => $uri,
+    type    => 'chip',
+);
+
+$p{bytes} = $bytes if defined $bytes; 
+$p{md5sum} = $md5 if defined $md5;
+$p{compressed} = 1 if defined $compress;
+
+my $tmp;
+
+if (!$nebulous) {
+
+    # create path and possibly fix up permissions
+    my $dirname = dirname($filename);
+    if (!-d $dirname) {
+        mkpath([$dirname], 1, 0775);
+    }
+
+    # can we truly write to filename?
+{
+    # open file for read/write but not create
+    my $fh;
+    if (open($fh, '+<', $filename)) {
+        # do nothing and fall through
+    } elsif (open($fh, '>>', $filename)) {
+        # this is the "stomp on it behavior" this probably shouldn't be the
+        # default and it may make sense to check the md5sum/bytes count of the
+        # files that's already in place to see if we really need to re-fetch it
+        unlink $filename or die "can't unlink $filename: $!";
+    } else {
+        die "can't write to $filename";
+    }
+}
+
+    # for non-neb files, use the download directory for the tmp file
+    $tmp = File::Temp->new(
+               DIR         => $dirname,
+               TEMPLATE    => '.' . basename($filename) . '.XXXXXXXX',
+               SUFFIX      => '.tmp',
+               UNLINK      => 1,
+               );
+} else {
+    # for neb files, use /tmp for the tmp file
+    $tmp = File::Temp->new(
+               DIR         => '/tmp',
+               TEMPLATE    => basename($filename) . '.XXXXXXXX',
+               SUFFIX      => '.tmp',
+               UNLINK      => 1,
+               );
+}
+
+my $tmpfilename = $tmp->filename;
+print "downloading file to $tmpfilename\n";
+
+# request the file from the remote data store server (save in tmpfilename)
+my $response = DataStore::File->new(%p)->request(
+        filename => $tmpfilename,
+        ua_args  => { timeout => $timeout },
+    );
+
+
+unless (defined $response and $response->is_success) {
+    warn "request failed: ", $response->status_line;
+    exit($response->code - 300);
+}
+
+die "checksum failed" unless $response->data;
+
+# file retreival succeed
+
+# this can't cross a file system boundry so we can assume that a move will
+# always be a rename operation instead of a copy and delete.
+
+my $neb;
+if ($nebulous) {
+    require Nebulous::Client;
+    import Nebulous::Client;
+    require File::Copy;
+    import File::Copy;
+
+    $neb = Nebulous::Client->new(
+        proxy   => $server,
+    );
+
+    die "can't connected to Nebulous Server: $server"
+        unless defined $neb;
+
+    # XXX see comment above about not blindly stomping the file
+    # - if the key doesn't already exist then create it
+    # - if it does, delete it so that we end up on the right storage volume
+    if ($neb->stat($filename)) {
+        $neb->delete($filename)
+            or die "Nebulous can't delete key $filename";
+    }
+
+    my $fh = $neb->open_create( $filename, $volume )
+        or die "Nebulous can't create key $filename";
+
+    # user.copies is always set to at least 2
+    my $user_copies = 2;
+    if (defined $copies) {
+        $user_copies = $copies if $copies > 2;
+    }
+    $neb->setxattr($filename, "user.copies", 2, "create")
+        or die "Nebulous can't set xattr on key $filename";
+
+    open(my $src_fh, $tmpfilename) or die "can't open file $tmpfilename: $!";
+
+    File::Copy::copy( $src_fh, $fh ) or die "file copy failed";
+
+    close($src_fh) or die "can't close file $tmpfilename: $!";
+    $fh->flush or die "can't flush nebulous filehandle: $!";
+    $fh->sync or die "can't sync nebulous filehandle: $!";
+    close($fh) or die "can't close nebulous filehandle: $!";
+
+    # make any required IMMEDIATE copies
+    if (defined $copies and $copies > 1) {
+        foreach (1 .. ($copies - 1)) {
+            $neb->replicate($filename, 'any')
+                or die "failed to replicate $filename failed: $!";
+        }
+    }
+} else {
+    $tmp->flush or die "can't flush filehandle: $!";
+    $tmp->sync or die "can't sync filehandle: $!";
+    rename $tmpfilename, $filename
+        or die "renaming $tmpfilename to $filename failed: $!";
+}
+
+DESTROY {
+    unlink $tmpfilename if -e $tmpfilename;
+    if ($nebulous and $neb->stat($filename)) {
+        $neb->delete($filename)
+            or die "Nebulous can't delete key $filename";
+    }
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+gsget - download a file from a DataStore server
+
+=head1 SYNOPSIS
+
+    dsget --uri <uri> --filename <filename> [--bytes <nbytes>] [--md5 <hex>]
+        [--compress] [--nebulous] [--volume <volume name>] [--copies <n>]
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --uri|-u <uri>
+
+The URI of the file to be retrieved.
+
+=item * --filename|-f <filename>
+
+The name to use when writing the file to disk.
+
+=item * --bytes|-s <nbytes>
+
+The size of the file in bytes.
+
+Optional, the size check is skipped if no value is specified.
+
+=item * --md5|-m <hex>
+
+The md5 checksum of the file in hex.
+
+Optional, the md5check is skipped if no value is specified.
+
+=item * --nebulous|-n
+
+Flag to enable C<nebulous> support.  When set C<--filename> is used as the
+Nebulous key.
+
+Optional.
+
+=item * --compress|-c
+
+When set C<dsget> will request a compressed version of the file.
+
+Optional.
+
+=item * --volume|-v <volume name>
+
+The volume name to request the first Nebulous instance be stored on.
+
+Optional, implies C<--nebulous>
+
+=item * --timeout|-t
+
+The ammount of time (in seconds) to wait for a response from the DataStore
+after making an HTTP request.  The default is 30s.
+
+Optional.
+
+=item * --copies <n>
+
+If C<--nebulous> is in use, the number of Nebulous replications to make.
+
+Optional.
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<dsget>, L<dsleech>, L<dsrootls>, L<dsproductls>, L<dsfilesetls>, L<DataStore>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsleech
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsleech	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsleech	(revision 22322)
@@ -0,0 +1,321 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006-2008  Joshua Hoblitt
+#
+# $Id: dsleech,v 1.12 2008-05-19 21:24:15 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use DataStore;
+use File::Spec;
+use YAML ();
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($dir, $uri, $last_fileset, $overwrite, $recall, $remember, $verbose, $timeout);
+
+GetOptions(
+    'dir|d=s'           => \$dir,
+    'uri|u=s'           => \$uri,
+    'last_fileset|l=s'  => \$last_fileset,
+    'overwrite|o'       => \$overwrite,
+    'recall'            => \$recall,
+    'remember'          => \$remember,
+    'verbose|v'         => \$verbose,
+    'timeout|t'         => \$timeout,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --uri",
+    -exitval => 3,
+) unless defined $uri;
+
+# default http request timeout is 30s
+$timeout ||= 30;
+
+my %p = (
+    uri     => $uri,
+);
+
+# rc file path
+my $rcfilename = File::Spec->catfile($ENV{'HOME'}, '.dsleechrc');
+
+# load rc file
+my $rc = load_rc($rcfilename) or die "failed to load rcfile";
+
+$p{last_fileset} = $last_fileset if defined $last_fileset; 
+if ($recall) {
+    # if --last_fileset was specified it's value should override --recall
+    if (defined $last_fileset) {
+        warn "--last_fileset overriding --recall" if defined $verbose;
+    } elsif (defined $rc->{$uri}) {
+        $p{last_fileset} = $rc->{$uri};
+    } else {
+        warn "--recall couldn't find a previous fileset ID"
+            if defined $verbose;
+    }
+}
+
+my $response = DataStore::Product->new(%p)->request(
+    ua_args  => { timeout => $timeout },
+);
+
+unless (defined $response and $response->is_success) {
+    warn "request failed: ", $response->status_line;
+    exit($response->code - 300);
+}
+
+# file retreival succeed
+
+my $filesets = $response->data;
+unless ($filesets) {
+    warn "no filesets found";
+    exit(0);
+}
+
+print "@$filesets filesets found\n" if $verbose;
+
+process_filesets($filesets, $dir, $overwrite, $verbose) or die "failed to process filesets";
+
+# if we got this far we're assuming that the downloads completed and we can
+# save the last fileset 
+if ($remember) {
+    my $lowest_fileset = $filesets->[-1];
+    if (defined $lowest_fileset) {
+        $rc->{$uri} = $lowest_fileset->fileset;
+    }
+
+    save_rc($rcfilename, $rc) or die "failed to save rcfile";
+}
+
+sub process_filesets
+{
+    my ($filsets, $dir, $overwrite, $verbose) = @_;
+
+    my $n = 0;
+    foreach my $fileset (@$filesets) {
+        $n++;
+        print "processing fileset: $fileset->fileset $n/@$filesets\n" if $verbose;
+
+        my $response = $fileset->request(
+                ua_args  => { timeout => $timeout },
+            );
+        unless (defined $response->is_success) {
+            warn "request failed: ", $response->status_line;
+            return;
+        }
+
+        my $files = $response->data;
+        # if we get an empty fileset ->request will sucessed but ->data will
+        # return undef   
+        next unless defined $files;
+        unless (@$files) {
+            print "no files in fileset ", $fileset->set, "\n" if $verbose;
+            next;
+        }
+
+        if (!process_files($files, $dir, $overwrite, $verbose)) {
+            warn "failed to process files";
+            return;
+        }
+    }
+
+    return 1;
+}
+
+sub process_files
+{
+    my ($files, $dir, $overwrite, $vebose) = @_;
+
+    foreach my $file (@$files) {
+        # generate the filename to downloda too
+        my $filename;
+        if (defined $dir) {
+            if (!-e $dir) {
+                print "creating dir $dir\n" if $verbose;
+                mkdir $dir or die "can't create dir: $!";
+            }
+            if (!-d $dir) {
+                warn "$dir is not a directory";
+                return;
+            }
+
+            $filename = File::Spec->catfile($dir, $file->fileid);
+        } else {
+            $filename = $file->fileid;
+        }
+
+        # make sure that filename doesn't already exist
+        if (-e $filename) {
+            if ($overwrite) {
+                warn "file $filename already exists, overwriting...";
+            } else {
+                warn "file $filename already exists, skipping...";
+                next;
+            }
+        }
+
+        # use the short version of the filename (with out the path) 
+        print "fetching ", $file->fileid, "..." if $verbose;
+
+        my $response = $file->request(
+                filename => $filename,
+                ua_args  => { timeout => $timeout },
+            );
+        unless (defined $response->is_success) {
+            warn "request failed: ", $response->status_line;
+            return;
+        }
+
+        unless ($response->data) {
+            warn "there was a problem retreiving ", $file->fileid;
+            return;
+        }
+
+        print "    ok\n" if $verbose;
+    }
+
+    return 1;
+}
+
+sub load_rc {
+    my $rcfilename = shift;
+
+    # initalize $sums with an empty hash so if there isn't a pre-existing
+    # database we still end up with a hashref instead of an undef scalar
+    my $rc;
+    if (-e $rcfilename) {
+        if (-f $rcfilename) {
+            $rc = YAML::LoadFile($rcfilename) or die "failed to load rc file";
+        } else {
+            warn "$rcfilename is not a plain file";
+            return;
+        }
+    }
+
+    # if the rcfile exists but is empty YAML will return undef
+    $rc ||= {};
+
+    return $rc;
+}
+
+sub save_rc {
+    my ($rcfilename, $rc) = @_;
+
+    my $yaml = YAML::DumpFile($rcfilename, $rc)
+            or die "failed to write rc file";
+
+    return 1;
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+dsleech - batch retrieve files from a DataStore server product
+
+=head1 SYNOPSIS
+
+    dsleech --uri <uri> [--dir <dir>] [--last_fileset <filesetid>]
+                [--overwrite] [--recall] [--remember] [--verbose]
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --uri|-u <uri>
+
+The URI of the DataStore product to retrive files from.
+
+=item * --dir|-d <dir>
+
+This flag is optional.
+
+The directory to download retrived files into.  The default is to fetch files into the current directory.
+
+=item * --last_fileset|-l <filesetid>
+
+The FileSet ID of the last FileSet that you've seen.  Only filesets registered after C<filesetid> will be polled for files to retreive.
+
+This flag is optional.
+
+=item * --overwrite
+
+If a file already exists, overwrite it.
+
+This flag is optional.
+
+=item * --recall
+
+Load the last fileset ID seen for C<--uri> from the I<rcfile>, if it exists.
+
+This flag is optional.
+
+=item * --remember
+
+Store the last fileset ID seen for C<--uri> in the I<rcfile>.
+
+This flag is optional.
+
+=item * --verbose|-v
+
+Be more verbose in reporting actions.
+
+This flag is optional.
+
+=item * --timeout|-t
+
+The ammount of time (in seconds) to wait for a response from the DataStore
+after making an HTTP request.  The default is 30s.
+
+Optional.
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<dsget>, L<dsleech>, L<dsrootls>, L<dsproductls>, L<dsfilesetls>, L<DataStore>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsproductls
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsproductls	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsproductls	(revision 22322)
@@ -0,0 +1,137 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006-2008  Joshua Hoblitt
+#
+# $Id: dsproductls,v 1.7 2008-07-29 05:49:53 bills Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use DataStore;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($uri, $last_fileset, $timeout);
+
+GetOptions(
+    'uri|u=s'           => \$uri,
+    'last_fileset|l=s'  => \$last_fileset,
+    'timeout|t=s'         => \$timeout,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --uri",
+    -exitval => 3,
+) unless defined $uri;
+
+# default http request timeout is 30s
+$timeout ||= 30;
+
+my %p = (
+    uri     => $uri,
+);
+
+$p{last_fileset} = $last_fileset if defined $last_fileset; 
+
+my $response = DataStore::Product->new(%p)->request(
+        ua_args  => { timeout => $timeout },
+    );
+
+unless (defined $response and $response->is_success) {
+    warn "request failed: ", $response->status_line;
+    exit($response->code - 300);
+}
+
+# file retreival succeed
+
+my $data = $response->data;
+
+unless ($data) {
+    warn "no filesets found";
+    exit(0);
+}
+
+print "# uri fileset datetime type\n";
+foreach my $fs (@$data) {
+    print $fs->uri, " ", $fs->fileset, " ", $fs->datetime, " ", $fs->type, "\n";
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+dsproductls - list all the FileSets under a product from a DataStore server
+
+=head1 SYNOPSIS
+
+    dsproductls --uri <uri> [--last_fileset <filesetid>]
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --uri|-u <uri>
+
+The URI of the file to be retrieved.
+
+=item * --lst_fileset|-l <filesetid>
+
+The FileSet ID of the last FileSet that you've seen.
+
+This flag is optional.
+
+=item * --timeout|-t
+
+The ammount of time (in seconds) to wait for a response from the DataStore
+after making an HTTP request.  The default is 30s.
+
+Optional.
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<dsget>, L<dsleech>, L<dsrootls>, L<dsproductls>, L<dsfilesetls>, L<DataStore>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsrootls
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsrootls	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/scripts/dsrootls	(revision 22322)
@@ -0,0 +1,133 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006-2008  Joshua Hoblitt
+#
+# $Id: dsrootls,v 1.4 2008-05-19 21:24:15 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use DataStore;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($uri, $timeout);
+
+GetOptions(
+    'uri|u=s'       => \$uri,
+    'timeout|t'     => \$timeout,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --uri",
+    -exitval => 3,
+) unless defined $uri;
+
+# default http request timeout is 30s
+$timeout ||= 30;
+
+my %p = (
+    uri     => $uri,
+);
+
+my $response = DataStore::Root->new(%p)->request(
+        ua_args  => { timeout => $timeout },
+    );
+
+unless (defined $response and $response->is_success) {
+    warn "request failed: ", $response->status_line;
+    exit($response->code - 300);
+}
+
+# file retreival succeed
+
+my $data = $response->data;
+
+unless ($data) {
+    warn "no products found";
+    exit(0);
+}
+
+print "# uri product last time type desc\n";
+foreach my $prod (@$data) {
+    print $prod->uri, " ",
+          $prod->product, " ",
+          $prod->last_fileset, " ",
+          $prod->last_datetime, " ",
+          $prod->type, " ",
+          $prod->desc, "\n";
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+dsrootls - list all the Product under the "root" of a DataStore server
+
+=head1 SYNOPSIS
+
+    dsrootls --uri <uri> 
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --uri|-u <uri>
+
+The URI of the file to be retrieved.
+
+=item * --timeout|-t
+
+The ammount of time (in seconds) to wait for a response from the DataStore
+after making an HTTP request.  The default is 30s.
+
+Optional.
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<dsget>, L<dsleech>, L<dsrootls>, L<dsproductls>, L<dsfilesetls>, L<DataStore>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/00_distribution.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/00_distribution.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/00_distribution.t	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 00_distribution.t,v 1.2 2006-03-16 22:04:38 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib ./t );
+
+use Test::More;
+
+# example taken from Test::Distribution Pod
+
+BEGIN {
+    eval {
+        require Test::Distribution;
+    };
+    if($@) {
+        plan skip_all => 'Test::Distribution not installed';
+    } else {
+        import Test::Distribution; # not => qw( podcover );
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/01_load.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/01_load.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/01_load.t	(revision 22322)
@@ -0,0 +1,19 @@
+# -*- perl -*-
+
+# t/001_load.t - check module loading and create testing directory
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 11;
+
+BEGIN { use_ok('DataStore'); }
+BEGIN { use_ok('DataStore::File'); }
+BEGIN { use_ok('DataStore::File::Parser'); }
+BEGIN { use_ok('DataStore::FileSet'); }
+BEGIN { use_ok('DataStore::FileSet::Parser'); }
+BEGIN { use_ok('DataStore::Product'); }
+BEGIN { use_ok('DataStore::Product::Parser'); }
+BEGIN { use_ok('DataStore::Root'); }
+BEGIN { use_ok('DataStore::Record'); }
+BEGIN { use_ok('DataStore::Response'); }
+BEGIN { use_ok('DataStore::Utils'); }
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/02_fileset_parse.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/02_fileset_parse.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/02_fileset_parse.t	(revision 22322)
@@ -0,0 +1,188 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 02_fileset_parse.t,v 1.10 2007-09-25 23:50:34 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 43;
+use Test::Warn;
+
+=head1 NAME
+
+t/02_fileset_parse.t - tests DataStore::FileSet::Parser
+
+=head1 SYNOPSIS
+    
+    prove t/02_fileset_parse.t
+
+=cut
+
+use DataStore::FileSet::Parser;
+#use Test::Warn;
+
+can_ok('DataStore::FileSet::Parser', qw(
+    new
+    parse
+));
+
+# ->new()
+
+{
+    my $parser = DataStore::FileSet::Parser->new;
+
+    isa_ok($parser, 'DataStore::FileSet::Parser');
+}
+
+# ->parse()
+
+eval {
+    my $parser = DataStore::FileSet::Parser->new;
+
+    $parser->parse(undef);
+};
+like($@, qr/is not one of the allowed types/,
+    '->parse() fails when passed undef');
+
+eval {
+    my $parser = DataStore::FileSet::Parser->new;
+
+    $parser->parse('');
+};
+like($@, qr/ did not pass regex check/,
+    '->parse() fails when passed a zero length string');
+
+warning_like {
+    my $parser = DataStore::FileSet::Parser->new;
+
+    is($parser->parse('asdf|asdf'), undef,
+        '->parse() returns undef on failure');
+} qr/not enough fields/,
+    '->parse() fails when there are too few fields';
+
+warning_like {
+    my $parser = DataStore::FileSet::Parser->new;
+
+    is($parser->parse('|2006-01-01T00:03:04Z|OBJECT'), undef,
+        '->parse() returns undef on failure');
+} qr/does not conform /,
+    '->parse() fails when the fileset field is not in the proper format';
+
+warning_like {
+    my $parser = DataStore::FileSet::Parser->new;
+
+    is($parser->parse('foobar|2006-01-0100:03:04Z|OBJECT'), undef,
+        '->parse() returns undef on failure');
+} qr/does not conform /,
+    '->parse() fails when the datetime field is not in the proper format';
+
+warning_like {
+    my $parser = DataStore::FileSet::Parser->new;
+
+    is($parser->parse('foobar|2006-01-01T00:03:04Z| '), undef,
+        '->parse() returns undef on failure');
+} qr/does not conform /,
+    '->parse() fails when the type field is not in the proper format';
+
+warning_like {
+    my $parser = DataStore::FileSet::Parser->new;
+
+    is($parser->parse('foo#bonkbar|2006-01-01T00:03:04Z|baz'), undef,
+        '->parse() returns undef on failure');
+} qr/contains #/,
+    '->parse() fails when a field contains #';
+
+warning_like {
+    my $parser = DataStore::FileSet::Parser->new;
+
+    is($parser->parse('foobar|2006-01-01T00:03:04Z|baz # foo'), undef,
+        '->parse() returns undef on failure');
+} qr/contains #/,
+    '->parse() comments are not allowed after the last field';
+
+{
+    # multi-row example
+my $example1 =<<END;
+# filesetID|time registered    |type   |telescope pointing         |etime|f|airmass|
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+END
+    my $parser  = DataStore::FileSet::Parser->new(
+        base_uri => 'http://foo.com/index.txt'
+    );
+    my @results = $parser->parse($example1);
+
+    is(scalar @results, 4, "correct number of item returned in list context");
+}
+
+{
+    # multi-row example
+my $example1 =<<END;
+# filesetID|time registered    |type   |telescope pointing         |etime|f|airmass|
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+END
+    my $parser  = DataStore::FileSet::Parser->new(
+        base_uri => 'http://foo.com/index.txt'
+    );
+    my $results = $parser->parse($example1);
+
+    is(scalar @$results, 4,
+        "correct number of item returned in scalar context");
+
+    isa_ok(@$results[0], 'DataStore::FileSet');
+    is(@$results[0]->fileset, 'otis0123456', 'correct fileset');
+    is(@$results[0]->datetime, '2006-01-01T00:03:04Z', 'correct datetime');
+    is(@$results[0]->type, 'OBJECT', 'correct type');
+    is(@$results[0]->uri, 'http://foo.com/otis0123456/index.txt', 'correct uri');
+
+    isa_ok(@$results[1], 'DataStore::FileSet');
+    is(@$results[1]->fileset, 'otis0123456', 'correct fileset');
+    is(@$results[1]->datetime, '2006-01-01T00:03:04Z', 'correct datetime');
+    is(@$results[1]->type, 'OBJECT', 'correct type');
+    is(@$results[1]->uri, 'http://foo.com/otis0123456/index.txt', 'correct uri');
+
+    isa_ok(@$results[2], 'DataStore::FileSet');
+    is(@$results[2]->fileset, 'otis0123456', 'correct fileset');
+    is(@$results[2]->datetime, '2006-01-01T00:03:04Z', 'correct datetime');
+    is(@$results[2]->type, 'OBJECT', 'correct type');
+    is(@$results[2]->uri, 'http://foo.com/otis0123456/index.txt', 'correct uri');
+
+    isa_ok(@$results[3], 'DataStore::FileSet');
+    is(@$results[3]->fileset, 'otis0123456', 'correct fileset');
+    is(@$results[3]->datetime, '2006-01-01T00:03:04Z', 'correct datetime');
+    is(@$results[3]->type, 'OBJECT', 'correct type');
+    is(@$results[3]->uri, 'http://foo.com/otis0123456/index.txt', 'correct uri');
+}
+
+{
+    # example with blank lines & comments
+my $example1 =<<END;
+
+
+# filesetID|time registered    |type   |telescope pointing         |etime|f|airmass|
+
+                            # random comment
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+
+                            # random comment
+
+# foo
+END
+    my $results = DataStore::FileSet::Parser->new->parse($example1);
+
+    is(scalar @$results, 1, "correct number of item returned");
+
+    isa_ok(@$results[0], 'DataStore::FileSet');
+    is(@$results[0]->fileset, 'otis0123456', 'correct fileset');
+    is(@$results[0]->datetime, '2006-01-01T00:03:04Z', 'correct datetime');
+    is(@$results[0]->type, 'OBJECT', 'correct type');
+}
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/03_file_parse.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/03_file_parse.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/03_file_parse.t	(revision 22322)
@@ -0,0 +1,200 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 03_file_parse.t,v 1.5 2007-09-26 00:44:43 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 50;
+use Test::Warn;
+
+=head1 NAME
+
+t/03_file_parse.t - tests DataStore::File::Parser
+
+=head1 SYNOPSIS
+    
+    prove t/03_file_parse.t
+
+=cut
+
+use DataStore::File::Parser;
+#use Test::Warn;
+
+can_ok('DataStore::File::Parser', qw(
+    new
+    parse
+));
+
+# ->new()
+
+{
+    my $parser = DataStore::File::Parser->new;
+
+    isa_ok($parser, 'DataStore::File::Parser');
+}
+
+# ->parse()
+
+eval {
+    my $parser = DataStore::File::Parser->new;
+
+    $parser->parse(undef);
+};
+like($@, qr/is not one of the allowed types/,
+    '->parse() fails when passed undef');
+
+eval {
+    my $parser = DataStore::File::Parser->new;
+
+    $parser->parse('');
+};
+like($@, qr/ did not pass regex check/,
+    '->parse() fails when passed a zero length string');
+
+warning_like {
+    my $parser = DataStore::File::Parser->new;
+
+    is($parser->parse('asdf|asdf'), undef,
+        '->parse() returns undef on failure');
+} qr/not enough fields/,
+    '->parse() fails when there are too few fields';
+
+warning_like {
+    my $parser = DataStore::File::Parser->new;
+
+    is($parser->parse(
+    'foo+++bar|83002312|fe6a2b6564c0d4cfb3bbf1db813824ba|chip|'), undef,
+        '->parse() returns undef on failure');
+} qr/does not conform/,
+    '->parse() fails when the fileID field is not in the proper format';
+
+warning_like {
+    my $parser = DataStore::File::Parser->new;
+
+    is($parser->parse(
+    'otis0123456.01|a83002312|fe6a2b6564c0d4cfb3bbf1db813824ba|chip|'), undef,
+        '->parse() returns undef on failure');
+} qr/does not conform/,
+    '->parse() fails when the bytes field is not in the proper format';
+
+warning_like {
+    my $parser = DataStore::File::Parser->new;
+
+    is($parser->parse(
+    'otis0123456.01|83002312|zfe6a2b6564c0d4cfb3bbf1db813824ba|chip|'), undef,
+        '->parse() returns undef on failure');
+} qr/does not conform/,
+    '->parse() fails when the md5sum field is not in the proper format';
+
+warning_like {
+    my $parser = DataStore::File::Parser->new;
+
+    is($parser->parse(
+    'otis0123456.01|83002312|fe6a2b6564c0d4cfb3bbf1db813824ba|c+hip|'), undef,
+        '->parse() returns undef on failure');
+} qr/does not conform/,
+    '->parse() fails when the fileID field is not in the proper format';
+
+warning_like {
+    my $parser = DataStore::File::Parser->new;
+
+    is($parser->parse(
+    'otis012#3456.01|83002312|fe6a2b6564c0d4cfb3bbf1db813824ba|chip|'), undef,
+        '->parse() returns undef on failure');
+} qr/contains #/,
+    '->parse() fails when a field contains #';
+
+warning_like {
+    my $parser = DataStore::File::Parser->new;
+
+    is($parser->parse(
+    'otis0123456.01|83002312|fe6a2b6564c0d4cfb3bbf1db813824ba|chip| # foo'), undef,
+        '->parse() returns undef on failure');
+} qr/contains #/,
+    '->parse() comments are not allowed after the last field';
+
+{
+    # multi-row example
+my $example1 =<<END;
+# fileID      |bytes   |md5sum                          |type|chipname|
+otis0123456.01|83002312|fe6a2b6564c0d4cfb3bbf1db813824ba|chip|ota01   |
+otis0123456.02|83002312|a2b4a7d7b94dc6076c5f3f67239a48c6|chip|ota02   |
+otis0123456.03|83002312|a39b1510484c7833e27454b181f13981|chip|ota03   |
+otis0123456.04|83002312|7bb35e1e30f3f833c0416aea75f90304|chip|ota04   |
+END
+    my @results = DataStore::File::Parser->new->parse($example1);
+    is(scalar @results, 4, "correct number of item returned in list context");
+}
+
+{
+    # multi-row example
+my $example1 =<<END;
+# fileID      |bytes   |md5sum                          |type|chipname|
+otis0123456.01|83002312|fe6a2b6564c0d4cfb3bbf1db813824ba|chip|ota01   |
+otis0123456.02|83002312|a2b4a7d7b94dc6076c5f3f67239a48c6|chip|ota02   |
+otis0123456.03|83002312|a39b1510484c7833e27454b181f13981|chip|ota03   |
+otis0123456.04|83002312|7bb35e1e30f3f833c0416aea75f90304|chip|ota04   |
+END
+    my $results = DataStore::File::Parser->new->parse($example1);
+
+    is(scalar @$results, 4,
+        "correct number of item returned in scalar context");
+
+    isa_ok(@$results[0], 'DataStore::File');
+    is(@$results[0]->uri, 'http://example.org/otis0123456.01', 'correct uri');
+    is(@$results[0]->fileid, 'otis0123456.01', 'correct fileid');
+    is(@$results[0]->bytes, '83002312', 'correct datetime');
+    is(@$results[0]->md5sum, 'fe6a2b6564c0d4cfb3bbf1db813824ba', 'correct md5');
+    is(@$results[0]->type, 'chip', 'correct type');
+
+    isa_ok(@$results[1], 'DataStore::File');
+    is(@$results[1]->uri, 'http://example.org/otis0123456.02', 'correct uri');
+    is(@$results[1]->fileid, 'otis0123456.02', 'correct fileid');
+    is(@$results[1]->bytes, '83002312', 'correct datetime');
+    is(@$results[1]->md5sum, 'a2b4a7d7b94dc6076c5f3f67239a48c6', 'correct md5');
+    is(@$results[1]->type, 'chip', 'correct type');
+
+    isa_ok(@$results[2], 'DataStore::File');
+    is(@$results[2]->uri, 'http://example.org/otis0123456.03', 'correct uri');
+    is(@$results[2]->fileid, 'otis0123456.03', 'correct fileid');
+    is(@$results[2]->bytes, '83002312', 'correct datetime');
+    is(@$results[2]->md5sum, 'a39b1510484c7833e27454b181f13981', 'correct md5');
+    is(@$results[2]->type, 'chip', 'correct type');
+
+    isa_ok(@$results[3], 'DataStore::File');
+    is(@$results[3]->uri, 'http://example.org/otis0123456.04', 'correct uri');
+    is(@$results[3]->fileid, 'otis0123456.04', 'correct fileid');
+    is(@$results[3]->bytes, '83002312', 'correct datetime');
+    is(@$results[3]->md5sum, '7bb35e1e30f3f833c0416aea75f90304', 'correct md5');
+    is(@$results[3]->type, 'chip', 'correct type');
+}
+
+{
+    # example with blank lines & comments
+my $example1 =<<END;
+
+
+# fileID      |bytes   |md5sum                          |type|chipname|
+
+                            # random comment
+otis0123456.01|83002312|fe6a2b6564c0d4cfb3bbf1db813824ba|chip|ota01   |
+
+                            # random comment
+
+# foo
+END
+    my $results = DataStore::File::Parser->new->parse($example1);
+
+    is(scalar @$results, 1, "correct number of item returned");
+
+    isa_ok(@$results[0], 'DataStore::File');
+    is(@$results[0]->fileid, 'otis0123456.01', 'correct fileid');
+    is(@$results[0]->bytes, '83002312', 'correct datetime');
+    is(@$results[0]->md5sum, 'fe6a2b6564c0d4cfb3bbf1db813824ba', 'correct md5');
+    is(@$results[0]->type, 'chip', 'correct type');
+}
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/04_record.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/04_record.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/04_record.t	(revision 22322)
@@ -0,0 +1,81 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 04_record.t,v 1.1 2006-02-28 03:56:46 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 7;
+use DataStore::Record;
+
+=head1 NAME
+
+t/04_record.t - tests DataStore::Record
+
+=head1 SYNOPSIS
+    
+    prove t/04_record.t
+
+=cut
+
+can_ok('DataStore::Record', qw(
+    new
+    uri
+    request
+));
+
+# ->new()
+
+{
+    my $rec = DataStore::Record->new;
+
+    isa_ok($rec, 'DataStore::Record');
+}
+
+{
+    # w/o trailing slash
+    my $rec = DataStore::Record->new(
+        uri => 'http://example.org/foo'
+    );
+
+    isa_ok($rec, 'DataStore::Record');
+}
+
+{
+    # w/ trailing slash
+    my $rec = DataStore::Record->new(
+        uri => 'http://example.org/foo/'
+    );
+
+    isa_ok($rec, 'DataStore::Record');
+}
+
+# ->uri()
+
+{
+    # default value
+    my $rec = DataStore::Record->new;
+    is($rec->uri, 'http://example.org/', '->uri() returned default uri');
+}
+
+{
+    # default value
+    my $rec = DataStore::Record->new(
+        uri => 'http://example.org/foo/'
+    );
+    is($rec->uri, 'http://example.org/foo/', '->uri() returned correct uri');
+}
+
+# ->request()
+
+eval {
+    my $rec = DataStore::Record->new;
+
+    $rec->request;
+};
+like($@, qr/must override the ->request\(\) method/,
+    '->request() dies when not overridden');
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/05_product.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/05_product.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/05_product.t	(revision 22322)
@@ -0,0 +1,181 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 05_product.t,v 1.6 2007-09-25 23:37:44 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 23;
+
+=head1 NAME
+
+t/05_product.t - tests DataStore::Product
+
+=head1 SYNOPSIS
+    
+    prove t/05_product.t
+
+=cut
+
+use DataStore::Product;
+
+can_ok('DataStore::Product', qw(
+    new
+    request
+));
+
+# ->new()
+
+{
+    my $dsp = DataStore::Product->new( uri => 'http://example.org/index.txt' );
+
+    isa_ok($dsp, 'DataStore::Product');
+}
+
+{
+    my $dsp = DataStore::Product->new(
+        uri             => 'http://example.org/index.txt',
+        last_fileset    => '12buckelyourshoe',
+    );
+
+    isa_ok($dsp, 'DataStore::Product');
+}
+
+eval {
+    my $dsp = DataStore::Product->new( uri => 'http://example.org/index.txt', foo => 1 );
+};
+like($@, qr/not listed in the validation options/,
+    '->new() fails whe passed extra params');
+
+eval {
+    my $dsp = DataStore::Product->new(
+        uri             => 'http://example.org/index.txt',
+        last_fileset    => '12buckelyourshoe',
+        foo => 1,
+    );
+};
+like($@, qr/not listed in the validation options/,
+    '->new() fails whe passed extra params');
+
+eval {
+    my $dsp = DataStore::Product->new( uri => 'http://example.org' );
+};
+like($@, qr/uri ends with \/index.txt/,
+    '->new() fails when uri param does not end with /index.txt');
+
+eval {
+    my $dsp = DataStore::Product->new( uri => 'http://example.org/index.html' );
+};
+like($@, qr/uri ends with \/index.txt/,
+    '->new() fails when uri param does not end with /index.txt');
+
+eval {
+    my $dsp = DataStore::Product->new( uri => '://example.org/index.txt' );
+};
+like($@, qr/is valid http uri/,
+    '->new() fails when uri param is not http protocol');
+
+eval {
+    my $dsp = DataStore::Product->new(
+        uri             => 'http://example.org/index.txt',
+        last_fileset    => '++12buckelyourshoe',
+    );
+};
+like($@, qr/is valid fileset ID/,
+    '->new() fails when last_fileset param is not a valid fileset ID');
+
+eval {
+    my $dsp = DataStore::Product->new;
+};
+like($@, qr/Mandatory parameter 'uri'/,
+    '->new() fails when not passed a uri param');
+
+eval {
+    my $dsp = DataStore::Product->new(
+        last_fileset    => '12buckelyourshoe',
+    );
+};
+like($@, qr/Mandatory parameter 'uri'/,
+    '->new() fails when not passed a uri param');
+
+# ->request()
+
+use Net::HTTPServer;
+
+sub bar 
+{
+    my $req = shift;             # Net::HTTPServer::Request object
+    my $res = $req->Response();  # Net::HTTPServer::Response object
+
+    my $string = <<END;
+# filesetID|time registered    |type   |telescope pointing         |etime|f|airmass|
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+otis0123456|2006-01-01T00:03:04Z|OBJECT|11:00:10.33 68:26:59.6 2000|30.0 |r|1.23   |
+END
+
+    $res->Print($string);
+
+    return $res;
+}
+
+$|++;
+
+$SIG{CHLD} = 'IGNORE';
+my $pid = open(my $child, "-|");
+
+unless ($pid) {
+    my $server = new Net::HTTPServer( port => 'scan', log => '/dev/null' );
+
+    # send port number to parent
+    print $server->Start(), "\n";
+
+    $server->RegisterURL('/index.txt', \&bar);
+    $server->Process();  # Run forever
+
+    exit -1;
+}
+
+my $port = <$child>;
+chomp $port;
+
+{
+    my $dsp = DataStore::Product->new( uri => "http://localhost:$port/index.txt" );
+
+    isa_ok($dsp->request, 'DataStore::Response');
+}
+
+{
+    my $dsp = DataStore::Product->new( uri => "http://localhost:$port/index.txt" );
+
+    my $dsr = $dsp->request;
+
+    ok($dsr->is_success, "response->is_success() is correct");
+    is($dsr->code, 200, "response->code() is correct");
+    is($dsr->status_line, "200 OK", "response->status_line() is correct");
+    is(ref $dsr->data, 'ARRAY', "response->data is arrayref is correct");
+    isa_ok($dsr->request, 'DataStore::Record');
+
+    my $data = $dsr->data;
+    is(scalar @$data, 4, "data has the correct number of rows");
+    isa_ok(@$data[0], 'DataStore::FileSet', "data has correct type of rows");
+    isa_ok(@$data[1], 'DataStore::FileSet', "data has correct type of rows");
+    isa_ok(@$data[2], 'DataStore::FileSet', "data has correct type of rows");
+    isa_ok(@$data[3], 'DataStore::FileSet', "data has correct type of rows");
+}
+
+# cleanup HTTP server
+kill 9, $pid;
+
+eval {
+    my $dsp = DataStore::Product->new( uri => 'http://example.org/index.txt' );
+
+    $dsp->request( foo => 1 );
+};
+like($@, qr/not listed in the validation options/,
+    '->request() fails whe passed extra params');
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/06_fileset.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/06_fileset.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/06_fileset.t	(revision 22322)
@@ -0,0 +1,172 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 06_fileset.t,v 1.9 2007-09-25 23:50:34 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 19;
+
+=head1 NAME
+
+t/06_fileset.t - tests DataStore::FileSet
+
+=head1 SYNOPSIS
+    
+    prove t/06_fileset.t
+
+=cut
+
+use DataStore::FileSet;
+
+can_ok('DataStore::FileSet', qw(
+    new
+    request
+));
+
+# ->new()
+
+{
+    my $dsf = DataStore::FileSet->new(
+        uri         => 'http://example.org/index.txt',
+        fileset     => '12buckelyourshoe',
+        datetime    => '2042-01-01T00:00:00Z',
+        type        => 'OBJECT',
+    );
+
+    isa_ok($dsf, 'DataStore::FileSet');
+}
+
+eval {
+    my $dsf = DataStore::FileSet->new(
+        uri         => 'http://example.org/index.txt',
+        fileset     => '12buckelyourshoe',
+        datetime    => '2042-01-01T00:00:00Z',
+        type        => 'OBJECT',
+        foo         => 1,
+    );
+};
+like($@, qr/not listed in the validation options/,
+    '->new() fails whe passed extra params');
+
+eval {
+    my $dsf = DataStore::FileSet->new( uri => 'http://example.org' );
+};
+like($@, qr/uri ends with \/index.txt/,
+    '->new() fails when uri param does not end with /');
+
+eval {
+    my $dsf = DataStore::FileSet->new( uri => 'http://example.org/index.html' );
+};
+like($@, qr/uri ends with \/index.txt/,
+    '->new() fails when uri param does not end with /');
+
+eval {
+    my $dsf = DataStore::FileSet->new( uri => '://example.org/index.txt' );
+};
+like($@, qr/is valid http uri/,
+    '->new() fails when uri param is not http protocol');
+
+eval {
+    my $dsp = DataStore::FileSet->new;
+};
+like($@, qr/Mandatory parameter/,
+    '->new() fails when not passed all manadator params');
+
+# ->request()
+
+use Net::HTTPServer;
+
+sub bar 
+{
+    my $req = shift;             # Net::HTTPServer::Request object
+    my $res = $req->Response();  # Net::HTTPServer::Response object
+
+    my $string = <<END;
+# fileID      |bytes   |md5sum                          |type|chipname|
+otis0123456.01|83002312|fe6a2b6564c0d4cfb3bbf1db813824ba|chip|ota01   |
+otis0123456.02|83002312|a2b4a7d7b94dc6076c5f3f67239a48c6|chip|ota02   |
+otis0123456.03|83002312|a39b1510484c7833e27454b181f13981|chip|ota03   |
+otis0123456.04|83002312|7bb35e1e30f3f833c0416aea75f90304|chip|ota04   |
+END
+
+    $res->Print($string);
+
+    return $res;
+}
+
+$|++;
+
+$SIG{CHLD} = 'IGNORE';
+my $pid = open(my $child, "-|");
+
+unless ($pid) {
+    my $server = new Net::HTTPServer( port => 'scan', log => '/dev/null' );
+
+    # send port number to parent
+    print $server->Start(), "\n";
+
+    $server->RegisterURL('/index.txt', \&bar);
+    $server->Process();  # Run forever
+
+    exit -1;
+}
+
+my $port = <$child>;
+chomp $port;
+
+{
+    my $dsp = DataStore::FileSet->new(
+        uri         => "http://localhost:$port/index.txt",
+        fileset     => '12buckelyourshoe',
+        datetime    => '2042-01-01T00:00:00Z',
+        type        => 'OBJECT',
+    );
+
+    isa_ok($dsp->request, 'DataStore::Response');
+}
+
+{
+    my $dsp = DataStore::FileSet->new(
+        uri         => "http://localhost:$port/index.txt",
+        fileset     => '12buckelyourshoe',
+        datetime    => '2042-01-01T00:00:00Z',
+        type        => 'OBJECT',
+    );
+
+    my $dsr = $dsp->request;
+
+    ok($dsr->is_success, "response->is_success() is correct");
+    is($dsr->code, 200, "response->code() is correct");
+    is($dsr->status_line, "200 OK", "response->status_line() is correct");
+    is(ref $dsr->data, 'ARRAY', "response->data is arrayref is correct");
+    isa_ok($dsr->request, 'DataStore::Record');
+
+    my $data = $dsr->data;
+    is(scalar @$data, 4, "data has the correct number of rows");
+    isa_ok(@$data[0], 'DataStore::File', "data has correct type of rows");
+    isa_ok(@$data[1], 'DataStore::File', "data has correct type of rows");
+    isa_ok(@$data[2], 'DataStore::File', "data has correct type of rows");
+    isa_ok(@$data[3], 'DataStore::File', "data has correct type of rows");
+}
+
+# cleanup HTTP server
+kill 9, $pid;
+
+eval {
+    my $dsf = DataStore::FileSet->new(
+        uri         => 'http://example.org/index.txt',
+        fileset     => '12buckelyourshoe',
+        datetime    => '2042-01-01T00:00:00Z',
+        type        => 'OBJECT',
+    );
+
+    $dsf->request( foo => 1 );
+};
+like($@, qr/not listed in the validation options/,
+    '->request() fails whe passed extra params');
+
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/07_file.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/07_file.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/07_file.t	(revision 22322)
@@ -0,0 +1,180 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 07_file.t,v 1.5 2006-03-17 23:54:35 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 14;
+
+=head1 NAME
+
+t/07_file.t - tests DataStore::File
+
+=head1 SYNOPSIS
+    
+    prove t/07_file.t
+
+=cut
+
+use DataStore::File;
+use File::Temp ();
+
+can_ok('DataStore::File', qw(
+    new
+    request
+));
+
+# ->new()
+
+{
+    my $dsf = DataStore::File->new(
+        uri         => 'http://example.org/foo',
+        fileid      => '12buckelyourshoe',
+        bytes       => 12345,
+        md5sum      => 'fe6a2b6564c0d4cfb3bbf1db813824ba',
+        type        => 'chip',
+    );
+
+    isa_ok($dsf, 'DataStore::File');
+}
+
+eval {
+    my $dsf = DataStore::File->new(
+        uri         => 'http://example.org/foo',
+        fileid      => '12buckelyourshoe',
+        bytes       => 12345,
+        md5sum      => 'fe6a2b6564c0d4cfb3bbf1db813824ba',
+        type        => 'chip',
+        foo         => 1,
+    );
+};
+like($@, qr/not listed in the validation options/,
+    '->new() fails whe passed extra params');
+
+eval {
+    my $dsf = DataStore::File->new( uri => 'http://example.org/' );
+};
+like($@, qr/is valid uri filename/,
+    '->new() fails when uri param does not end with /');
+
+eval {
+    my $dsf = DataStore::File->new( uri => '://example.org' );
+};
+like($@, qr/is valid http uri/,
+    '->new() fails when uri param is not http protocol');
+
+eval {
+    my $dsf = DataStore::File->new;
+};
+like($@, qr/Mandatory parameter/,
+    '->new() fails when not passed all manadator params');
+
+# ->request()
+
+use Net::HTTPServer;
+
+sub bar 
+{
+    my $req = shift;             # Net::HTTPServer::Request object
+    my $res = $req->Response();  # Net::HTTPServer::Response object
+
+    my $string = "foobarbaz"; 
+
+    $res->Print($string);
+
+    return $res;
+}
+
+$|++;
+
+$SIG{CHLD} = 'IGNORE';
+my $pid = open(my $child, "-|");
+
+unless ($pid) {
+    my $server = new Net::HTTPServer( port => 'scan', log => '/dev/null' );
+
+    # send port number to parent
+    print $server->Start(), "\n";
+
+    $server->RegisterURL('/somefile', \&bar);
+    $server->Process();  # Run forever
+
+    exit -1;
+}
+
+my $port = <$child>;
+chomp $port;
+
+{
+    my $dsf = DataStore::File->new(
+        uri         => "http://localhost:$port/somefile",
+        fileid      => '12buckelyourshoe',
+        bytes       => 9,
+        md5sum      => 'a0a6e1a375117c58d77221f10c5ce12e',
+        type        => 'chip',
+    );
+
+    my $fh = File::Temp->new( UNLINK => 1 );
+    $fh->autoflush(1);
+    isa_ok($dsf->request( filename => $fh->filename ), 'DataStore::Response');
+}
+
+{
+    my $dsf = DataStore::File->new(
+        uri         => "http://localhost:$port/somefile",
+        fileid      => '12buckelyourshoe',
+        bytes       => 9,
+        md5sum      => 'a0a6e1a375117c58d77221f10c5ce12e',
+        type        => 'chip',
+    );
+
+    my $fh = File::Temp->new( UNLINK => 1 );
+    $fh->autoflush(1);
+    my $dsr = $dsf->request( filename => $fh->filename );
+
+    ok($dsr->is_success, "response->is_success() is correct");
+    is($dsr->code, 200, "response->code() is correct");
+    is($dsr->status_line, "200 OK", "response->status_line() is correct");
+    is($dsr->data, $fh->filename, "response->data is arrayref is correct");
+    isa_ok($dsr->request, 'DataStore::Record');
+}
+
+# stop HTTP server
+kill 9, $pid;
+
+eval {
+    my $dsf = DataStore::File->new(
+        uri         => 'http://example.org/foo',
+        fileid      => '12buckelyourshoe',
+        bytes       => 12345,
+        md5sum      => 'fe6a2b6564c0d4cfb3bbf1db813824ba',
+        type        => 'chip',
+    );
+    
+    $dsf->request;
+};
+like($@, qr/Mandatory parameter/,
+    '->request() fails when passed no params');
+
+eval {
+    my $dsf = DataStore::File->new(
+        uri         => 'http://example.org/foo',
+        fileid      => '12buckelyourshoe',
+        bytes       => 12345,
+        md5sum      => 'fe6a2b6564c0d4cfb3bbf1db813824ba',
+        type        => 'chip',
+    );
+    
+    my $fh = File::Temp->new( UNLINK => 1 );
+    $dsf->request(
+        filename => $fh->filename,
+        foo => 1,
+    );
+};
+like($@, qr/not listed in the validation options/,
+    '->request() fails whe passed extra params');
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/08_response.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/08_response.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/08_response.t	(revision 22322)
@@ -0,0 +1,96 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 08_response.t,v 1.3 2007-09-25 23:37:44 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 8;
+
+=head1 NAME
+
+t/08_response.t - tests DataStore::Response
+
+=head1 SYNOPSIS
+    
+    prove t/08_response.t
+
+=cut
+
+use CGI;
+use DataStore::Product;
+use DataStore::Response;
+
+can_ok('DataStore::Response', qw(
+    new
+    is_success
+    code
+    status_line
+    data
+    request
+));
+
+# ->new()
+
+{
+    my $dsr = DataStore::Response->new(
+        is_success  => undef,
+        code        => 500,
+        status_line => 'foo',
+        data        => 'bar',
+        request     => DataStore::Product->new( uri => 'http://example.org/index.txt' ),
+    );
+
+    isa_ok($dsr, 'DataStore::Response');
+}
+
+eval {
+    my $dsr = DataStore::Response->new(
+        is_success   => undef,
+        code        => 500,
+        status_line => 'foo',
+        data        => 'bar',
+        request     => DataStore::Product->new( uri => 'http://example.org/index.txt' ),
+        foo         => 1,
+    );
+};
+like($@, qr/not listed in the validation options/,
+    '->new() fails whe passed extra params');
+
+eval {
+    my $dsr = DataStore::Response->new( is_success => 2 );
+};
+like($@, qr/is 0, 1, or undef/,
+    '->new() fails when is_success is not valid');
+
+eval {
+    my $dsr = DataStore::Response->new( code => 5000 );
+};
+like($@, qr/did not pass regex check/,
+    '->new() fails when code is not valid');
+
+eval {
+    my $dsr = DataStore::Response->new( status_line => '' );
+};
+like($@, qr/did not pass regex check/,
+    '->new() fails when status_line is not valid');
+
+eval {
+    my $dsr = DataStore::Response->new(
+        request => CGI->new,
+    );
+};
+like($@, qr/was not a 'DataStore::Record' /,
+    '->new() fails when response is not valid');
+
+eval {
+    my $dsr = DataStore::Response->new;
+};
+like($@, qr/Mandatory parameters/,
+    '->new() fails when not passed all manadator params');
+
+# accessor tests unneeded as they are all generated by Class::Accessor::Fast
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/09_datastore.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/09_datastore.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/09_datastore.t	(revision 22322)
@@ -0,0 +1,33 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 09_datastore.t,v 1.2 2006-03-16 01:23:10 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 7;
+
+=head1 NAME
+
+t/09_datastore.t - tests DataStore
+
+=head1 SYNOPSIS
+    
+    prove t/09_datastore.t
+
+=cut
+
+use DataStore;
+
+ok(%DataStore::, "DataStore is loaded");
+ok(%DataStore::File::Parser::, "DataStore::File::Parser is loaded");
+ok(%DataStore::File::, "DataStore::File is loaded");
+ok(%DataStore::FileSet::Parser::, "DataStore::FileSet::Parser is loaded");
+ok(%DataStore::FileSet::, "DataStore::Set is loaded");
+ok(%DataStore::Product::, "DataStore::Product is loaded");
+ok(%DataStore::Response::, "DataStore::Response is loaded");
+
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/10_dsget.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/10_dsget.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/10_dsget.t	(revision 22322)
@@ -0,0 +1,53 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 10_dsget.t,v 1.2 2006-03-17 21:58:23 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::Cmd;
+use Test::More tests => 7;
+
+=head1 NAME
+
+t/10_dsget.t - tests dsget
+
+=head1 SYNOPSIS
+    
+    prove t/10_dsget.t
+
+=cut
+
+# last ditch effort to make sure dsget is executable
+chmod 0755, 'scripts/dsget';
+
+my $test = Test::Cmd->new(prog => 'scripts/dsget', workdir => '');
+isa_ok($test, 'Test::Cmd');
+
+{
+    $test->run(args => '');
+    missing_args(3, "Required options: --uri --filename");
+}
+
+{
+    $test->run(args => '--uri http://example.org/foo');
+    missing_args(3, "Required options: --uri --filename");
+}
+
+{
+    $test->run(args => '--filename /foo/bar/baz');
+    missing_args(3, "Required options: --uri --filename");
+}
+
+sub missing_args
+{
+    my ($exit, $errstr) = @_;
+
+    is($? >> 8, $exit, "error code is: $exit");
+    like($test->stderr, qr/$errstr/, "error string is: $errstr");
+}
+
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/11_dsproductls.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/11_dsproductls.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/11_dsproductls.t	(revision 22322)
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 11_dsproductls.t,v 1.1 2006-03-18 04:02:35 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::Cmd;
+use Test::More tests => 5;
+
+=head1 NAME
+
+t/11_dsproductls.t - tests dsproductls
+
+=head1 SYNOPSIS
+    
+    prove t/11_dsproductls.t
+
+=cut
+
+# last ditch effort to make sure dsproductls is executable
+chmod 0755, 'scripts/dsproductls';
+
+my $test = Test::Cmd->new(prog => 'scripts/dsproductls', workdir => '');
+isa_ok($test, 'Test::Cmd');
+
+{
+    $test->run(args => '');
+    missing_args(3, "Required options: --uri");
+}
+
+{
+    $test->run(args => '--last_fileset foobar');
+    missing_args(3, "Required options: --uri");
+}
+
+sub missing_args
+{
+    my ($exit, $errstr) = @_;
+
+    is($? >> 8, $exit, "error code is: $exit");
+    like($test->stderr, qr/$errstr/, "error string is: $errstr");
+}
+
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/12_dsfilesetls.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/12_dsfilesetls.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/12_dsfilesetls.t	(revision 22322)
@@ -0,0 +1,45 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 12_dsfilesetls.t,v 1.1 2006-03-20 22:03:53 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::Cmd;
+use Test::More tests => 3;
+
+=head1 NAME
+
+t/12_dsfilesetls.t - tests dsfilesetls
+
+=head1 SYNOPSIS
+    
+    prove t/12_dsfilesetls.t
+
+=cut
+
+my $prog = 'scripts/dsfilesetls';
+
+# last ditch effort to make sure dsproductls is executable
+chmod 0755, $prog;
+
+my $test = Test::Cmd->new( prog => $prog, workdir => '' );
+isa_ok($test, 'Test::Cmd');
+
+{
+    $test->run( args => '' );
+    missing_args(3, "Required options: --uri");
+}
+
+sub missing_args
+{
+    my ($exit, $errstr) = @_;
+
+    is($? >> 8, $exit, "error code is: $exit");
+    like($test->stderr, qr/$errstr/, "error string is: $errstr");
+}
+
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/13_dsrootls.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/13_dsrootls.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/13_dsrootls.t	(revision 22322)
@@ -0,0 +1,45 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 13_dsrootls.t,v 1.1 2007-09-25 22:10:45 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::Cmd;
+use Test::More tests => 3;
+
+=head1 NAME
+
+t/12_dsfilesetls.t - tests dsfilesetls
+
+=head1 SYNOPSIS
+    
+    prove t/12_dsfilesetls.t
+
+=cut
+
+my $prog = 'scripts/dsrootls';
+
+# last ditch effort to make sure dsproductls is executable
+chmod 0755, $prog;
+
+my $test = Test::Cmd->new( prog => $prog, workdir => '' );
+isa_ok($test, 'Test::Cmd');
+
+{
+    $test->run( args => '' );
+    missing_args(3, "Required options: --uri");
+}
+
+sub missing_args
+{
+    my ($exit, $errstr) = @_;
+
+    is($? >> 8, $exit, "error code is: $exit");
+    like($test->stderr, qr/$errstr/, "error string is: $errstr");
+}
+
Index: /tags/sj_tags/sj_root_20080929/DataStore/t/14_root.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStore/t/14_root.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStore/t/14_root.t	(revision 22322)
@@ -0,0 +1,166 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006-2007  Joshua Hoblitt
+#
+# $Id: 14_root.t,v 1.1 2007-09-26 00:51:22 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 21;
+
+=head1 NAME
+
+t/14_root.t - tests DataStore::Root
+
+=head1 SYNOPSIS
+    
+    prove t/14_root.t
+
+=cut
+
+use DataStore::Root;
+
+can_ok('DataStore::Root', qw(
+    new
+    request
+));
+
+# ->new()
+
+{
+    my $dsp = DataStore::Root->new( uri => 'http://example.org/index.txt' );
+
+    isa_ok($dsp, 'DataStore::Root');
+}
+
+eval {
+    my $dsp = DataStore::Root->new(
+        uri => 'http://example.org/index.txt',
+        foo => 1
+    );
+};
+like($@, qr/not listed in the validation options/,
+    '->new() fails whe passed extra params');
+
+eval {
+    my $dsp = DataStore::Root->new(
+        uri             => 'http://example.org/index.txt',
+        last_fileset    => '12buckelyourshoe',
+        foo => 1,
+    );
+};
+like($@, qr/not listed in the validation options/,
+    '->new() fails whe passed extra params');
+
+eval {
+    my $dsp = DataStore::Root->new( uri => 'http://example.org' );
+};
+like($@, qr/uri ends with \/index.txt/,
+    '->new() fails when uri param does not end with /index.txt');
+
+eval {
+    my $dsp = DataStore::Root->new( uri => 'http://example.org/index.html' );
+};
+like($@, qr/uri ends with \/index.txt/,
+    '->new() fails when uri param does not end with /index.txt');
+
+eval {
+    my $dsp = DataStore::Root->new( uri => '://example.org/index.txt' );
+};
+like($@, qr/is valid http uri/,
+    '->new() fails when uri param is not http protocol');
+
+eval {
+    my $dsp = DataStore::Root->new;
+};
+like($@, qr/Mandatory parameter 'uri'/,
+    '->new() fails when not passed a uri param');
+
+eval {
+    my $dsp = DataStore::Root->new(
+        last_fileset    => '12buckelyourshoe',
+    );
+};
+like($@, qr/not listed in the validation options/,
+    '->new() fails when not passed a uri param');
+
+# ->request()
+
+use Net::HTTPServer;
+
+sub bar 
+{
+    my $req = shift;             # Net::HTTPServer::Request object
+    my $res = $req->Response();  # Net::HTTPServer::Response object
+
+    my $string = <<END;
+# productID | Most recent | Time              |type | Description       |
+gpc1|otis_test|2006-08-30T17:41:59Z|image|Gigapixel Camera 1|
+skyprobe|o4348i0010o05|2007-09-04T23:00:06Z|image|Skyprobe|
+ucam|video0102|2007-01-17T14:48:10Z|image|Microcam|
+allskycam|o4367a1075o01|2007-09-24T05:21:44Z|image|All Sky Camera|
+END
+
+    $res->Print($string);
+
+    return $res;
+}
+
+$|++;
+
+$SIG{CHLD} = 'IGNORE';
+my $pid = open(my $child, "-|");
+
+unless ($pid) {
+    my $server = new Net::HTTPServer( port => 'scan', log => '/dev/null' );
+
+    # send port number to parent
+    print $server->Start(), "\n";
+
+    $server->RegisterURL('/index.txt', \&bar);
+    $server->Process();  # Run forever
+
+    exit -1;
+}
+
+my $port = <$child>;
+chomp $port;
+
+{
+    my $dsp = DataStore::Root->new( uri => "http://localhost:$port/index.txt" );
+
+    isa_ok($dsp->request, 'DataStore::Response');
+}
+
+{
+    my $dsp = DataStore::Root->new( uri => "http://localhost:$port/index.txt" );
+
+    my $dsr = $dsp->request;
+
+    ok($dsr->is_success, "response->is_success() is correct");
+    is($dsr->code, 200, "response->code() is correct");
+    is($dsr->status_line, "200 OK", "response->status_line() is correct");
+    is(ref $dsr->data, 'ARRAY', "response->data is arrayref is correct");
+    isa_ok($dsr->request, 'DataStore::Record');
+
+    my $data = $dsr->data;
+    is(scalar @$data, 4, "data has the correct number of rows");
+    isa_ok(@$data[0], 'DataStore::Product', "data has correct type of rows");
+    isa_ok(@$data[1], 'DataStore::Product', "data has correct type of rows");
+    isa_ok(@$data[2], 'DataStore::Product', "data has correct type of rows");
+    isa_ok(@$data[3], 'DataStore::Product', "data has correct type of rows");
+}
+
+# cleanup HTTP server
+kill 9, $pid;
+
+eval {
+    my $dsp = DataStore::Root->new( uri => 'http://example.org/index.txt' );
+
+    $dsp->request( foo => 1 );
+};
+like($@, qr/not listed in the validation options/,
+    '->request() fails whe passed extra params');
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.log
+config.status
+configure
+install-sh
+missing
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = scripts
+
+CLEANFILES = *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=DataStoreServer
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+#($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+#        DIE=1
+#}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+#($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+#        DIE=1
+#}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+#$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+#$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/configure.ac	(revision 22322)
@@ -0,0 +1,16 @@
+AC_PREREQ(2.59)
+
+AC_INIT([DataStoreServer], [1.0.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([scripts])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_MAINTAINER_MODE
+
+AC_PROG_INSTALL
+
+AC_CONFIG_FILES([
+  Makefile
+  scripts/Makefile
+])
+
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/doc/IPPDataStore.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/doc/IPPDataStore.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/doc/IPPDataStore.tex	(revision 22322)
@@ -0,0 +1,306 @@
+The purpose of this note is to describe the implementation of the IPP Data Store.
+Introduction
+The DataStore client interface is defined in document PSDC-940-010-02 which we will hereafter refer to as DSDoc. Basically the DataStore server provides clients with a directory listing and file transfer interface through HTTP. 
+The Data Store virtual file system is arranged in four levels
+1. The root of the Data Store.   Which contains
+2. One or more products.         Which contain
+3. One or more file sets.            Which contain
+4. One or more files.
+The Data Store provides specialized indexing for the first three levels as described in section 3.2 of DSDoc. 
+As an example the IPP Data Store is required to support a number of different products
+* Image Quality and data validity for OTIS
+* Transient Detections, Detectability results for MOPS
+* Postage Stamp Images
+* Postage Stamp Requests (internal to the web interface of the Postage  Stamp Server)
+
+Each product will be visible at the root of the Data Store. As described in DSDoc, HTTP Requests to URLs with basename index.txt return lists in a simple easy to parse format. For example, an http request to the URL http://ippdatastore/ds/index.txt might return the following list of products
+
+# productID  |Most recent |Time registered     |type     |Description                   |
+pstamprequest|none        |2008-02-28T09:01:54Z|psrequest|Postage Stamp Request         |
+pstampresults|test        |2008-03-01T02:18:15Z|psresults|Postage Stamp Request Results |
+ipp_otis     |tootis080305|2008-03-04T22:43:00Z|image    |IPP to OTIS Data Store        |
+ipp_mops     |tomops080305|2008-03-04T22:45:42Z|image    |IPP to MOPS Data Store        |
+
+If index.txt is omitted from the URL, the list is returned in a more readable html format complete with links into the products. The Most recent column gives the name of the file set most recently added to each product and Time registered gives the time that that fileset was added to the Data Store.
+An interesting feature of the interface is related to file set indexing. If the user sets the query string of the URL (the searchpart defined in rfc1738) to the name of an existing file set, the server will return a list that contains only those file sets that were registered later time than the given file set. This makes it easy for clients to find out whats new.
+
+For example if the URL http://ippdatastore/ds/pstampresults/index.txt returns
+# filesetID|time registered     |type     |
+fileset13  |2008-03-01T01:52:18Z|PSRESULTS|
+nfileset1  |2008-03-01T01:54:46Z|PSRESULTS|
+nfileset2  |2008-03-01T01:55:00Z|PSRESULTS|
+fileset43  |2008-03-01T02:01:29Z|PSRESULTS|
+test       |2008-03-01T02:18:15Z|PSRESULTS|
+
+the URL http://ippdatastore/ds/pstampresults/index.txt?nfileset2 would yield
+# filesetID|time registered     |type     |
+fileset43  |2008-03-01T02:01:29Z|PSRESULTS|
+test       |2008-03-01T02:18:15Z|PSRESULTS| 
+
+the set of filesets newer than nfileset2. This feature requires that the Data Stores implementation of the fileset lists be more sophisticated than the simple text file that the URL implies.
+There are other considerations that led us to not try and manage the Data Store contents using simple text file indexes. Since registration requires indexes at multiple levels to be changed, and since multiple clients can be manipulating the Data Store simultaneously, keeping text file indexes consistent just using files in the file system would be problematic.
+Instead we store the contents of the DataStore in an SQL database tables.  The indexes are built on demand using cgi scripts.
+Web Implementation
+Note: although we are beginning our discussion with the specifics of the web interface, this is probably the least complex and concrete part of the DS implementation.
+The web interface for the Data Store is implemented using Apache HTTP server.  The Data Store is configured by adding the file httpd-datastore.conf to the Apache configuration directory /etc/httpd/conf.d.
+ A few directives are used.
+<Directory "/var/www/html/ds/dsroot">
+
+  DirectoryIndex /ds-cgi/dsindex.cgi
+
+  Options +Includes
+
+  # files will be processed for server side includes if they have the
+  # executable bit set (note: this doesnt seem to be necessary)
+  XBitHack on
+
+  AddOutputFilter INCLUDES .txt
+
+  AllowOverride None
+
+  Order allow,deny
+  Allow from all
+</Directory>
+
+<Directory "/cgi-bin/ds-cgi">
+  Options ExecCGI
+
+
+  Order allow,deny
+  Allow from all
+</Directory>
+
+Alias       /ds/     /var/www/html/ds/dsroot/
+ScriptAlias /ds-cgi/ /var/www/cgi-bin/ds-cgi/
+
+This configuration file is written for a set up where the root of the Data Store file system is located at /var/www/html/ds/dsroot/ and http clients see the root of the Data Store at http://server/ds
+The cgi scripts live in /var/www/cgi-bin/ds-cgi which is aliased to /ds-cgi.
+The directories in the Data Store file system are arranged to match the data. That is, product productA is represented by a directory productA in the Data Store root directory. The directory productA contains a subdirectory for each registered file set and in these directories are either the file sets constituent files themselves or to their actual location.  NOTE: locating the files outside the Data Store is not implemented yet.
+The DirectoryIndex directive in the configuration file redirects index requests for all directories to the script dsindex.cgi.
+The index re-directions are intended to insure that only registered products and file sets are available for access over the web interface. Other directories and files may in fact be in the file systems (file sets under construction for example) but they arent visible.
+ISSUE: other directories in the file system arent listable. The dsindex script will return not found, but if an actual file name is known by the client then it will be accessible. Is this a problem?
+dsindex.cgi
+This script is simply
+#!/bin/sh
+PATH_TO_COMMAND/pstamp_runcommand.sh dsgetindex $REQUEST_URI -html 
+
+pstamp_runcommand.sh is a (now misnamed) wrapper script that sets up the environment to run IPP commands on behalf of the web server user apache. That program is listed in appendix A. dsgetindex is a perl script that computes the index corresponding to the URI by querying the data base. 
+The environment variable REQUEST_URI is the set by Apache to the requested URL. For example /ds or /ds/pstampresults
+
+
+
+Index.txt
+The Data Store implementation places a file named index.txt in each registered directory in the Data Store.  The contents of each of these files is the same:
+<!--#include virtual="/ds-cgi/dstxtindex.cgi" -->
+
+That is, index.txt redirects the web server to the cgi script dstxtindex.cgi which is yet another wrapper script
+#!/bin/sh
+PATH_TO_COMMAND/pstamp_runcommand.sh dsgetindex $REQUEST_URI;
+
+ (Note: there are probably better implementations for the index.txt redirection but I havent found any yet. This seems to work fine).
+
+The interesting bit of the web implementation: dsgetindex
+As might be inferred from the last couple of sections the interesting bit is contained in the program dsgetindex. 
+dsgetindex takes the REQUEST_URI from the web server and decides whether it is an index request for the Data Store root, a product, or an individual file set. Based on this level it runs one of the following commands to query the database for the appropriate index
+* dsrootindex
+* dsprodindex
+* dsfsindex
+If the parameter html is provided to dsgetindex the output of these commands is passed through a filter to create a user friendly HTML page.
+The 3 index programs are perl scripts that use the DBI package to access the mysql database. Currently IPP interfaces are only used for looking up configuration information. Thus it would be easy to modify this Data Store implementation to run outside of the IPP environment.
+NOTE: We need to add the statistics specified by the DSDoc.
+ 
+
+Data Store Index Tables
+The logical contents of the Data Store is represented by 4 SQL tables.
+* dsProduct
+* dsFileset
+* dsFile
+* dsFileType
+The tables are defined in the usual ipp style where each row has an integer id which is the primary key. 
+The product table is defined as
+mysql> describe dsProduct;
++-------------+--------------+------+-----+---------+----------------+
+| Field       | Type         | Null | Key | Default | Extra          |
++-------------+--------------+------+-----+---------+----------------+
+| prod_id     | bigint(20)   | NO   | PRI | NULL    | auto_increment | 
+| prod_name   | varchar(64)  | NO   | UNI |         |                | 
+| last_update | datetime     | YES  |     | NULL    |                | 
+| last_fs     | varchar(64)  | YES  |     | NULL    |                | 
+| type        | varchar(64)  | YES  |     | NULL    |                | 
+| description | varchar(255) | YES  |     | NULL    |                | 
+| prod_col_0  | varchar(64)  | YES  |     | NULL    |                | 
+| prod_col_1  | varchar(64)  | YES  |     | NULL    |                | 
+| prod_col_2  | varchar(64)  | YES  |     | NULL    |                | 
+| prod_col_3  | varchar(64)  | YES  |     | NULL    |                | 
+| prod_col_4  | varchar(64)  | YES  |     | NULL    |                | 
+| prod_col_5  | varchar(64)  | YES  |     | NULL    |                | 
+| prod_col_6  | varchar(64)  | YES  |     | NULL    |                | 
+| prod_col_7  | varchar(64)  | YES  |     | NULL    |                | 
++-------------+--------------+------+-----+---------+----------------+
+
+The columns named prod_col_0..7 are used to store the labels for any product specific columns.  For example the DSDoc defines several fields for the gpc1 product: telescope pointing, integration time, filter, airmass, comment.
+A fileset is represented in the database as
++--------------+-------------+------+-----+---------+----------------+
+| Field        | Type        | Null | Key | Default | Extra          |
++--------------+-------------+------+-----+---------+----------------+
+| prod_id      | bigint(20)  | YES  | MUL | NULL    |                | 
+| fileset_id   | bigint(20)  | NO   | PRI | NULL    | auto_increment | 
+| fileset_name | varchar(64) | NO   | UNI |         |                | 
+| reg_time     | datetime    | NO   |     |         |                | 
+| type         | varchar(64) | YES  |     | NULL    |                | 
+| prod_col_0   | varchar(64) | YES  |     | NULL    |                | 
+| prod_col_1   | varchar(64) | YES  |     | NULL    |                | 
+| prod_col_2   | varchar(64) | YES  |     | NULL    |                | 
+| prod_col_3   | varchar(64) | YES  |     | NULL    |                | 
+| prod_col_4   | varchar(64) | YES  |     | NULL    |                | 
+| prod_col_5   | varchar(64) | YES  |     | NULL    |                | 
+| prod_col_6   | varchar(64) | YES  |     | NULL    |                | 
+| prod_col_7   | varchar(64) | YES  |     | NULL    |                | 
++--------------+-------------+------+-----+---------+----------------+
+
+So all filesets in product 2 could be found with
+SELECT * from dsFileset where prod_id = 2;
+
+The columns named prod_col_n give the values of the product specific columns for the fileset.
+ISSUE: fileset_name is mared as UNIQUE but this is too restrictive. The name doesnt have to be unique in dsFileset, it only has to be unique in the product. We could manage this easily by storing prod_name/fileset_name instead of just the fileset_name instead of the. The current dsreg handles the uniqueness requirement by searching the product directory for an existing fileset.
+
+
+The individual files are represented in the table dsFile
+mysql> describe dsFile;
++------------+--------------+------+-----+---------+----------------+
+| Field      | Type         | Null | Key | Default | Extra          |
++------------+--------------+------+-----+---------+----------------+
+| fileset_id | bigint(20)   | YES  | MUL | NULL    |                | 
+| file_id    | bigint(20)   | NO   | PRI | NULL    | auto_increment | 
+| file_name  | varchar(255) | NO   |     |         |                | 
+| bytes      | bigint(20)   | YES  |     | NULL    |                | 
+| md5sum     | varchar(255) | YES  |     | NULL    |                | 
+| type       | varchar(64)  | YES  |     | NULL    |                | 
+| type_col_0 | varchar(64)  | YES  |     | NULL    |                | 
+| type_col_1 | varchar(64)  | YES  |     | NULL    |                | 
+| type_col_2 | varchar(64)  | YES  |     | NULL    |                | 
+| type_col_3 | varchar(64)  | YES  |     | NULL    |                | 
++------------+--------------+------+-----+---------+----------------+
+
+So the list of files in fileset 42 could be found with
+SELECT * from dsFile where fileset_id = 42;
+
+The columns type_col_n contain the values for any columns that are specific to a given file type.
+The names of these colums are contained in the dsFileType table
+mysql> describe dsFileType;
++------------+-------------+------+-----+---------+-------+
+| Field      | Type        | Null | Key | Default | Extra |
++------------+-------------+------+-----+---------+-------+
+| type       | varchar(64) | YES  |     | NULL    |       | 
+| type_col_0 | varchar(64) | YES  |     | NULL    |       | 
+| type_col_1 | varchar(64) | YES  |     | NULL    |       | 
+| type_col_2 | varchar(64) | YES  |     | NULL    |       | 
+| type_col_3 | varchar(64) | YES  |     | NULL    |       | 
++------------+-------------+------+-----+---------+-------+
+
+Currently weve identified the following file types
+* chip (a chip level image file)
+* psrequest (postage stamp request table)
+* psresults (postage stamp results table)
+* pstamp (postage stamp image)
+The chip type has one type specific column the chipname (ota name).
+TODO: we might want to discuss product and fileset types as well. For us they are just labels however.
+ 
+
+Data Store Management
+The script dsregproduct is used to add a product to the data store
+dsregproduct -product myProduct -type image description Data Store for my Images \               		-pc0 telescope pointing         -pc1 etime -pc2 f -pc3 airm \
+         --pc4 comments          
+
+By padding the strings in the product specific column labels the fileset listings will have more regular formatting.
+
+Filesets are added to the data store creating the fileset directory in the data store, populating it with the constituent files and then registering the file set by invoking the program dsreg
+dsreg --add -product myProduct -fileset firstFileset -type SKYFLAT \ 			-pc0 -109.192656 \19.751905 2000 -pc1 129.1 -pc2 g -pc3 1.06 \
+         -pc4 Twilight flat g 2/5 
+         
+dsreg reads the list of files from stdin (TODO: should we give a file argument?) Each line contains
+filename filetype [0  4 type_specific_columns]
+
+dsreg computes the md5sum for each file and looks up the files size and adds the appropriate entries to the database tables dsFile and dsFileset. 
+Filesets may be removed from the database using 
+Dsreg -del myProduct -fileset oldFileset
+
+The registration information is removed from the database. The files however are not removed. TODO: should they be?
+Appendix A
+Note: this program was originally written for the postage stamp server but has found other applications. (To be relocated/renamed)
+pstamp_runcommand.sh
+#!/bin/sh
+###
+#
+# pstamp_runcommand.sh: 
+#
+# Set up the ipp environment to run a ipp commands on behalf of the postage stamp
+# server and execute the command given by the argument list 
+# This script is used by cgi or php scripts.
+
+if [[ $# == 0 ]] ;then
+    echo "usage $0 command" >&2
+    #  EINVAL = 22
+    exit 22
+fi
+
+#### BEGIN LOCAL_CONFIGURATION
+
+# These variables need to be customized for a particular installation
+# XXX: why not pass these on on the command line or in the environment?
+export PSCONFDIR=/export/data0/bills/psconfig
+
+WORK_DIR=/export/data1/bills/pstamp/work
+CMD_DIR=/export/data0/bills/src/ipp/pstamp/scripts:/export/data0/bills/src/ipp/DataStoreServer/web/cgi
+
+#### END LOCAL_CONFIGURATION
+
+export HOME=$WORK_DIR
+
+cd $WORK_DIR
+status=$?
+if [[ $status != 0 ]] ; then
+    echo $0 cannot cd to WORK_DIR: $WORK_DIR status: $status >&2
+    exit $status
+fi
+
+###
+### configure the IPP
+###
+
+source $PSCONFDIR/psconfig.bash default
+status=$?
+if [[ $status != 0 ]] ; then
+    echo error setting up IPP environment >&2
+    exit $status
+fi
+
+###
+### Add our command directory to the path. 
+### XXX: This won't be necessary once the scripts get installed as part of the IPP
+###
+PATH=${CMD_DIR}:${PATH}
+#echo path: $PATH >&2
+#echo command: $* >&2
+
+## make sure we are able to run the desired command
+## XXX: This test is sort of redundant. 
+## We'd get a more useful error by just going ahead and trying the command
+## or by not suppressing the error output of which
+if [[ ! -x `which $1 2> /dev/null` ]] ;then
+    status=$?
+    echo command $1 not found >&2
+    exit $status
+fi
+
+#### Finally, execute the command given by the command line arguments
+
+$*
+
+
+
+
+
+
+
+
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+installdir = $(bindir)
+
+install_files = \
+	dsreg \
+        dsprodtool \
+        dsrootindex \
+        dsprodindex \
+        dsfsindex
+
+install_SCRIPTS = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsdbh.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsdbh.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsdbh.pm	(revision 22322)
@@ -0,0 +1,20 @@
+# create a database handle based on the configuration given
+# in environment variables
+
+sub getDBHandle {
+    my $dbserver = $ENV{DBSERVER};
+    my $dbuser   = $ENV{DBUSER};
+    my $dbpass   = $ENV{DBPASSWORD};
+    my $dbname   = $ENV{DBNAME};
+
+    die "database enviornment not set up" unless defined($dbserver) and defined($dbuser)
+        and defined($dbpass) and defined($dbname);
+
+    my $dsn = "DBI:mysql:host=$dbserver;database=$dbname";
+
+    my $dbh = DBI->connect($dsn, $dbuser, $dbpass) or die "Cannot connect to server\n";
+
+    return $dbh;
+}
+
+return 1;
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsfsindex
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsfsindex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsfsindex	(revision 22322)
@@ -0,0 +1,113 @@
+#!/usr/bin/env perl
+
+# list the filesets in a data store product
+# The only ipp specific code is the function that gets the database handle and the
+# exit status codes
+
+use strict;
+use warnings;
+
+use dsdbh;
+use DBI;
+
+my $PS_EXIT_CONFIG_ERROR = 3;
+my $PS_EXIT_DATA_ERROR = 5;
+
+my $fileset = pop;
+die("must specify fileset to list") unless defined $fileset;
+
+my $product = pop;
+die("must specify product to list") unless defined $product;
+
+my $dbh = getDBHandle();
+
+# Look up the product id
+my $prod_stmt = $dbh->prepare("SELECT prod_id FROM dsProduct WHERE prod_name = '$product'");
+$prod_stmt->execute();
+my $prod = $prod_stmt->fetchrow_hashref();
+if (! defined $prod) {
+    print STDERR "failed to find $product in product list\n";
+    exit ($PS_EXIT_DATA_ERROR);
+}
+
+my $prod_id = $prod->{prod_id};
+
+my $stmt = $dbh->prepare("SELECT fileset_id FROM dsFileset WHERE fileset_name = \'$fileset\'" .
+                    " AND prod_id = $prod_id");
+$stmt->execute();
+my $fs = $stmt->fetchrow_hashref();
+if (!defined $fs) {
+    print STDERR "failed to find $fileset in $product\n";
+    exit ($PS_EXIT_DATA_ERROR);
+}
+
+my $fs_id = $fs->{fileset_id};
+
+
+# get the headers for all of the type specific columns
+# a fileset may contain files with multiple types. We print out a new
+# header each time the type in the list changes.
+# save the header
+
+$stmt = $dbh->prepare("SELECT * FROM dsFileType");
+$stmt->execute();
+my %fileTypes;
+while( my @row = $stmt->fetchrow_array()) {
+    my $type = $row[0];
+    $fileTypes{$type} = \@row;
+}
+
+my $header = "# fileID          |bytes   |md5sum                          |type        |";
+
+$stmt = $dbh->prepare("SELECT * from dsFile WHERE fileset_id = $fs_id");
+$stmt->execute();
+
+my $print_header=1;
+my $last_type = "";
+my $last_header = "";
+while( my $row = $stmt->fetchrow_hashref()) {
+    my $type = $row->{type};
+
+    # if the type changes print out a new header
+    if ($type ne $last_type) {
+        $print_header = 1;
+        $last_type = $type;
+    }
+    if ($print_header) {
+        # add the type specific columns
+        my $typeheader = "";
+        my $fileTypeDef = $fileTypes{$type};
+        for (my $i= 1; $i <=4; $i++) {
+            if (!defined $fileTypeDef->[$i]) {
+                last;
+            }
+            $typeheader .= sprintf "%-8s|", $fileTypeDef->[$i];
+        }
+        my $newheader = "$header$typeheader";
+        # if the header actually changed (due to type specific columns) print it out
+        if ($newheader ne $last_header) {
+            print "$newheader\n";
+            $last_header = $newheader;
+        }
+        $print_header = 0;
+    }
+
+    my $line = sprintf "%-18s|%-8s|%-32s|%-12s|", 
+        $row->{file_name}, $row->{bytes}, $row->{md5sum}, $row->{type};
+
+    # now add any type specific columns that are defined (8 character minimum width is arbitrary)
+    if (defined $row->{type_col_0}) {
+        $line .= sprintf "%-8s|", $row->{type_col_0};
+        if (defined $row->{type_col_1}) {
+            $line .= sprintf "%-8s|", $row->{type_col_1};
+            if (defined $row->{type_col_2}) {
+                $line .= sprintf "%-8s|", $row->{type_col_2};
+                if (defined $row->{type_col_3}) {
+                    $line .= sprintf "%-8s|", $row->{type_col_3};
+                }
+            }
+        }
+    }
+
+    print "$line\n";
+}
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsprodindex
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsprodindex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsprodindex	(revision 22322)
@@ -0,0 +1,138 @@
+#!/usr/bin/env perl
+
+# list the filesets in a data store product
+
+use strict;
+use warnings;
+
+use DBI;
+use dsdbh;
+
+my $PS_EXIT_CONFIG_ERROR = 3;
+my $PS_EXIT_DATA_ERROR = 5;
+my $product = shift;
+die("must specify product to list") unless defined $product;
+
+# optional fileset name. Ignore filesets up to and including $after_fileset
+my $after_fileset = shift;
+
+my $dbh = getDBHandle();
+
+my $prod_stmt = $dbh->prepare("SELECT * FROM dsProduct WHERE prod_name = '$product'");
+$prod_stmt->execute();
+my $prod = $prod_stmt->fetchrow_hashref();
+if (! defined $prod) {
+    print STDERR "failed to find $product in product list\n";
+    exit ($PS_EXIT_DATA_ERROR);
+}
+
+# build the header line
+my $header = "# filesetID|time registered     |type     |";
+
+# now add the product specific columns that are defined
+# not particularly elegant. I guess I should have fetched the row as an array
+# note: we don't do any formatting for these columns. The width of the string
+# in the database should be padded if a nice width is desired
+my $last_prod_col = -1;
+if (defined $prod->{prod_col_0}) {
+    $last_prod_col = 0;
+    $header .= "$prod->{prod_col_0}|";
+    if (defined $prod->{prod_col_1}) {
+        $last_prod_col = 1;
+        $header .= "$prod->{prod_col_1}|";
+        if (defined $prod->{prod_col_2}) {
+            $last_prod_col = 2;
+            $header .= "$prod->{prod_col_2}|";
+            if (defined $prod->{prod_col_3}) {
+                $last_prod_col = 3;
+                $header .= "$prod->{prod_col_3}|";
+                if (defined $prod->{prod_col_4}) {
+                    $last_prod_col = 4;
+                    $header .= "$prod->{prod_col_4}|";
+                    if (defined $prod->{prod_col_5}) {
+                        $last_prod_col = 5;
+                        $header .= "$prod->{prod_col_5}|";
+                        if (defined $prod->{prod_col_6}) {
+                            $last_prod_col = 6;
+                            $header .= "$prod->{prod_col_6}|";
+                            if (defined $prod->{prod_col_7}) {
+                                $last_prod_col = 7;
+                                $header .= "$prod->{prod_col_7}|";
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+    
+
+my $prod_id = $prod->{prod_id};
+
+my $stmt;
+my $after_clause = "";
+if ($after_fileset) {
+    # client wants only filesets registered after $after_fileset
+    # Since fileset_id is auto increment the desired filesets will have a larger fileset_id than
+    # the target. Look up it's fileset_id
+
+    $stmt = $dbh->prepare("SELECT fileset_id FROM dsFileset " .
+                            "WHERE prod_id = $prod_id AND fileset_name = \'$after_fileset\'");
+    $stmt->execute();
+    my $fs = $stmt->fetchrow_hashref();
+    if ($fs) {
+        $after_clause = " AND fileset_id > $fs->{fileset_id}";
+    } else {
+        # $after_fileset not found
+        #
+        # XXX: the spec doesn't say what should happen in this case.
+        # Here we follow the conductor implementation and return the whole list.
+        # This may not be the right thing to do.
+    }
+}
+        
+$stmt = $dbh->prepare("SELECT * FROM dsFileset WHERE prod_id = $prod_id  $after_clause");
+$stmt->execute();
+
+# XXX: dsproductls expects a header even if there are no matching filesets
+print "$header\n";
+
+while( my $row = $stmt->fetchrow_hashref()) {
+
+    my $daytime = $row->{reg_time};
+    (my $date, my $time) = split " ", $daytime;
+
+    my $reg_time = $date . "T" . $time . "Z";
+
+    my $line = sprintf "%-11s|%-20s|%-9s|", $row->{fileset_name}, $reg_time, $row->{type};
+
+    # now add the product specific columns that are defined
+    # again using an array would have made this look nicer
+    if ($last_prod_col >= 0) {
+        $line .= "$row->{prod_col_0}|";
+        if ($last_prod_col >= 1) {
+            $line .= "$row->{prod_col_1}|";
+            if ($last_prod_col >= 2) {
+                $line .= "$row->{prod_col_2}|";
+                if ($last_prod_col >= 3) {
+                    $line .= "$row->{prod_col_3}|";
+                    if ($last_prod_col >= 4) {
+                        $line .= "$row->{prod_col_4}|";
+                        if ($last_prod_col >= 5) {
+                            $line .= "$row->{prod_col_5}|";
+                            if ($last_prod_col >= 6) {
+                                $line .= "$row->{prod_col_6}|";
+                                if ($last_prod_col >= 7) {
+                                    $line .= "$row->{prod_col_7}|";
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    print "$line\n";
+}
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsprodtool
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsprodtool	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsprodtool	(revision 22322)
@@ -0,0 +1,259 @@
+#!/usr/bin/env perl
+#
+# Data Store product  create and delete
+#
+
+use strict;
+use warnings;
+
+use Getopt::Long qw( GetOptions );
+use Pod::Usage qw( pod2usage );
+use DBI;
+use PS::IPP::Metadata::Config;
+use File::Copy;
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+my $product;
+
+# XXX allow group write permission. TODO: set group to something restrictive
+umask 2;
+
+my $ptype;
+my $description;
+
+my ($ps0, $ps1, $ps2, $ps3, $ps4, $ps5, $ps6, $ps7);
+
+my $dsroot;
+my $dbname;
+
+my $add;
+my $del;
+my $remove;
+
+my $we_created_dir = 0;
+
+GetOptions(
+        'add=s'         =>      \$add,
+        'del=s'         =>      \$del,
+	'type=s'	=>	\$ptype,
+        'description=s' =>      \$description,
+        'rm'            =>      \$remove,
+	'ps0=s'		=>	\$ps0,          # product specific columns 0 - 7
+	'ps1=s'		=>	\$ps1,
+	'ps2=s'		=>	\$ps2,
+	'ps3=s'		=>	\$ps3,
+	'ps4=s'		=>	\$ps4,
+	'ps5=s'		=>	\$ps5,
+	'ps6=s'		=>	\$ps6,
+	'ps7=s'		=>	\$ps7,
+        'dbname=s'      =>      \$dbname,
+        'dsroot=s'      =>      \$dsroot,
+) or pod2usage(2);
+
+my $err = "";
+
+# will exit if neither or both -add and -del were specified
+$err .= "Must specify either --add or --del.\n"
+    unless $add xor $del;
+
+if ($add) {
+    $product = $add;
+    if (!$ptype) {
+        $err .= "need to specify Product type to add\n";
+    }
+    if (!$description) {
+        $err .= "need to specify Product description\n";
+    }
+
+} else {
+    $product = $del;
+}
+
+#
+# lookup the location of the Data Store
+#
+
+my $ipprc =  PS::IPP::Config->new(); # IPP Configuration
+my $siteConfig = $ipprc->{_siteConfig};
+if (!$dsroot) {
+    $dsroot = metadataLookupStr($ipprc->{_siteConfig}, 'DATA_STORE_ROOT');
+    if (!$dsroot) {
+        die("Data Store root directory not set");
+    }
+}
+
+my $root_index_script = "$dsroot/index.txt";
+if (!stat($root_index_script)) {
+    $err .= "Data Store not found at '$dsroot'.\n"
+}
+
+# bail if any of the above args were improper
+show_usage($err)
+    if ($err);
+
+my $dbserver = metadataLookupStr($siteConfig, 'DBSERVER');
+$dbname      = metadataLookupStr($siteConfig, 'DBNAME') unless defined $dbname;
+my $dbuser   = metadataLookupStr($siteConfig, 'DBUSER');
+my $dbpass   = metadataLookupStr($siteConfig, 'DBPASSWORD');
+exit ($PS_EXIT_CONFIG_ERROR) unless defined $dbserver and $dbname and $dbuser and $dbpass;
+
+my $dsn = "DBI:mysql:host=$dbserver;database=$dbname";
+
+my $dbh = DBI->connect($dsn, $dbuser, $dbpass) or die "Cannot connect to server\n";
+
+
+my $product_dir = "$dsroot/$product";
+my $index_script_name = "$product_dir/index.txt";
+
+if ($del) {
+    #
+    # delete product
+    #
+    my $stmt = $dbh->prepare("SELECT prod_id, last_fs FROM dsProduct WHERE prod_name = \'$product\'");
+    $stmt->execute();
+    my $prod_row = $stmt->fetchrow_hashref();
+
+    if (!$prod_row) {
+        die("product $product not found");
+    }
+    my $prod_id = $prod_row->{prod_id};
+
+    # if requested, remove the product directory
+    if ($remove) {
+        if (system "rm -r $product_dir") {
+            die("failed to remove $product_dir");
+        }
+    } else  {
+        # otherwise just zap the index script
+        unlink("$index_script_name");
+    }
+
+
+    # loop over filesets in the product and delete them
+    $stmt = $dbh->prepare("SELECT fileset_id FROM dsFileset WHERE prod_id = $prod_id");
+    $stmt->execute();
+    my $fs_row;
+    while ($fs_row = $stmt->fetchrow_hashref()) {
+        my $fileset_id = $fs_row->{fileset_id};
+        $dbh->do("DELETE from dsFile where fileset_id = $fileset_id");
+        $dbh->do("DELETE from dsFileset where fileset_id = $fileset_id");
+    }
+    $dbh->do("DELETE from dsProduct where prod_id = $prod_id");
+
+    exit 0;
+
+} else {
+    #
+    # add a new product
+    #
+    my $stmt = $dbh->prepare("SELECT prod_id FROM dsProduct WHERE prod_name = \'$product\'");
+    $stmt->execute();
+    my $row = $stmt->fetchrow_hashref();
+
+    if ($row) {
+        die("product $product already exists");
+    }
+
+    # set up the product directory
+    if (! -e $product_dir) {
+        $we_created_dir = 1;
+        if (!mkdir $product_dir) {
+            die("failed trying to make product directory $product_dir");
+        }
+    }
+    if (-e $index_script_name ) {
+        if (!unlink($index_script_name)) {
+            die("failed trying to remove old $index_script_name");
+        }
+    }
+    if (!copy($root_index_script, $index_script_name)) {
+        print STDERR "failed trying to copy($root_index_script, $index_script_name)";
+        cleanup();
+        exit 1;
+    }
+
+    my $prodcolstr = make_prodcol_str($ps0, $ps1, $ps2, $ps3, $ps4, $ps5, $ps6, $ps7);
+
+    my $query = "INSERT INTO dsProduct (prod_name, last_update, type, description, " .
+                " prod_col_0, prod_col_1, prod_col_2, prod_col_3, prod_col_4, prod_col_5, prod_col_6, prod_col_7)" .
+                "VALUES('$product', UTC_TIMESTAMP(), '$ptype', '$description' $prodcolstr)"; 
+
+    my $count = $dbh->do($query);
+    if ($count == 0E0) {
+        print STDERR "failed to insert Product $product in database";
+        cleanup();
+        exit 1;
+    }
+
+
+
+    exit 0;
+}
+
+
+sub show_usage {
+    my $str = shift;
+
+    chomp $str;
+
+    my @tmp = split(/\//, $0);
+
+    pod2usage(
+        -msg => 
+"usage: $tmp[$#tmp] [--add prod_name |--del prod_name ] --type prod_type [options]
+
+Commands:
+
+    --add           Add a new Product
+    --del           Remove a Product.
+
+Options:
+    --type          Product type (required)
+    --description   Product Description (required)
+    --ps0 - ps7     Optional product specific data
+    --rm            with --del remove the Product directory from the Data Store
+    --dbname        select the Data Store's database name
+                    (usually required depending on configuration)
+
+
+    
+$str",
+        -exitval => 2
+    );
+}
+
+sub make_prodcol_str {
+
+    my @list = @_;
+    my $string = "";
+
+    foreach my $s (@list) {
+        if ($s) {
+            $string .= ", \'$s\'";
+        } else {
+            $string .= ", NULL";
+        }
+    }
+
+    return $string;
+}
+
+sub cleanup {
+    # don't delete the product directory unless we made it
+    if (!$we_created_dir) {
+        return 0;
+    }
+    if (system "rm -r $product_dir") {
+            print STDERR "failed to remove $product_dir";
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsreg
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsreg	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsreg	(revision 22322)
@@ -0,0 +1,489 @@
+#!/usr/bin/env perl
+#
+# Data Store file set registration and de-registration program
+#
+
+use strict;
+use warnings;
+
+use Getopt::Long qw( GetOptions );
+use Pod::Usage qw( pod2usage );
+use Digest::MD5::File qw( file_md5_hex );
+use DBI;
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use File::Copy;
+use File::Basename;
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+                       $PS_EXIT_UNKNOWN_ERROR
+                       $PS_EXIT_SYS_ERROR
+                       $PS_EXIT_CONFIG_ERROR
+                       $PS_EXIT_PROG_ERROR
+                       $PS_EXIT_DATA_ERROR
+                       $PS_EXIT_TIMEOUT_ERROR
+                       metadataLookupStr
+                       metadataLookupBool
+                       caturi
+                       );
+my $product;
+my $fileset;
+my $filelist;
+
+my $fstype;
+my ($ps0, $ps1, $ps2, $ps3, $ps4, $ps5, $ps6, $ps7);
+
+my $linkfiles;      # if set, put links to files in the Data Store. The actuall directory is $datapath
+my $copyfiles;      # if set, copy files from this directory to the Data Store fileset
+my $datapath;       # source for files required if $linkfiles or $copyfiles
+my $abspath;			# Path specified is absolute
+
+my $dbname;         # Database name
+my $dsroot;         # root directory of the data store
+
+my $add;
+my $del;
+my $remove;
+my $force;          # on --del don't return error if fileset doesn't exist
+
+my $verbose = 0;
+my $no_cleanup = 0; # if something goes wrong don't delete copied or linked files for debug
+
+# XXX allow group write permission: 
+# TODO: set the group to a more restrictive one than users
+umask 2;
+
+#
+# parse args
+#
+
+GetOptions(
+        'add=s'         =>      \$add,
+        'del=s'         =>      \$del,
+        'product=s'     =>      \$product,
+        'list=s'        =>      \$filelist,
+        'type=s'        =>      \$fstype,
+        'datapath=s'    =>      \$datapath,
+	'abspath'       =>      \$abspath,
+        'link'          =>      \$linkfiles,
+        'copy'          =>      \$copyfiles,
+        'rm'            =>      \$remove,
+        'force'         =>      \$force,
+        'ps0=s'         =>      \$ps0,          # product specific columns 0 - 7
+        'ps1=s'         =>      \$ps1,
+        'ps2=s'         =>      \$ps2,
+        'ps3=s'         =>      \$ps3,
+        'ps4=s'         =>      \$ps4,
+        'ps5=s'         =>      \$ps5,
+        'ps6=s'         =>      \$ps6,
+        'ps7=s'         =>      \$ps7,
+        'dbname=s'      =>      \$dbname,
+        'dsroot=s'      =>      \$dsroot,
+        'verbose'       =>      \$verbose,
+) or pod2usage(2);
+
+my $err = "";
+
+$err .= "--product is required\n" unless defined $product;
+
+# will exit if neither --add and --del is specified or if they both are specified
+$err .= "Must specify either --add or --del.\n"
+    unless $add xor $del;
+
+if ($add) {
+    $fileset = $add;
+    $err .= "--type is required\n" unless defined $fstype;
+    $err .= "--list is required\n" unless defined $filelist;
+    if ($linkfiles and $copyfiles) {
+        $err .= "only one of --link and --copy is allowed\n";
+    }
+    if (($linkfiles or $copyfiles) and !$datapath and !$abspath) {
+        $err .= "need to specify datapath with --link or --copy\n";
+    }
+    if ($linkfiles && (substr($datapath, 0, 1) ne "/")) {
+        $err .= "datapath must begin with / when using --link\n";
+    }
+} else {
+    $fileset = $del;
+}
+
+
+#
+# lookup the location of the Data Store
+#
+
+my $ipprc =  PS::IPP::Config->new(); # IPP Configuration
+my $siteConfig = $ipprc->{_siteConfig};
+if (!$dsroot) {
+    $dsroot = metadataLookupStr($ipprc->{_siteConfig}, 'DATA_STORE_ROOT');
+    if (!$dsroot) {
+        die("Data Store root directory not set");
+    }
+}
+
+
+if (!stat("$dsroot/index.txt")) {
+    $err .= "Data Store not found at '$dsroot'.\n"
+}
+
+# bail if any errors above
+show_usage($err)
+    if ($err);
+
+show_usage("Invalid product '$product'.")
+    unless defined stat("$dsroot/$product/index.txt");
+
+$dbname      = metadataLookupStr($siteConfig, 'DBNAME') unless defined $dbname;
+my $dbserver = metadataLookupStr($siteConfig, 'DBSERVER');
+my $dbuser   = metadataLookupStr($siteConfig, 'DBUSER');
+my $dbpass   = metadataLookupStr($siteConfig, 'DBPASSWORD');
+exit ($PS_EXIT_CONFIG_ERROR) unless defined $dbserver and $dbname and $dbuser and $dbpass;
+
+my $dsn = "DBI:mysql:host=$dbserver;database=$dbname";
+my %conn_attrs = (PrintError => 1, RaiseError => 1, AutoCommit => 0);
+
+my $dbh = DBI->connect_cached($dsn, $dbuser, $dbpass, \%conn_attrs)
+        or die "Cannot connect to DB server\n";
+
+print STDERR "dbh: $dbh\n" if $verbose;
+
+my $fileset_dir = "$dsroot/$product/$fileset";
+my $index_script_name = "$fileset_dir/index.txt";
+
+if ($del) {
+    #
+    # delete fileset
+    #
+
+    my $stmt = $dbh->prepare("SELECT prod_id, last_fs FROM dsProduct WHERE prod_name = ?");
+    $stmt->execute($product);
+
+    my $prod_row = $stmt->fetchrow_hashref();
+    my $prod_id = $prod_row->{prod_id};
+    my $old_last_fs = $prod_row->{last_fs};
+
+    if (!$prod_id) {
+        die("product $product not found\n");
+    }
+    $stmt = $dbh->prepare("SELECT fileset_id,fileset_name FROM dsFileset" .
+                                 " WHERE fileset_name = ? AND prod_id = $prod_id");
+
+    $stmt->execute($fileset);
+    my $fs_row = $stmt->fetchrow_hashref();
+
+    if (!$fs_row) {
+        print STDERR "Fileset '$fileset' not found in $product.\n";
+        if ($force) {
+            exit 0;
+        } else {
+            exit 1;
+        }
+    }
+    $stmt->finish();
+
+    my $fileset_id = $fs_row->{fileset_id};
+
+    eval {
+        $dbh->do("DELETE from dsFile where fileset_id = $fileset_id");
+        $dbh->do("DELETE from dsFileset where fileset_id = $fileset_id");
+
+        if ($old_last_fs eq $fs_row->{fileset_name}) {
+            # print STDERR "deleting most recent fileset in $product\n";
+
+            $stmt = $dbh->prepare("SELECT fileset_name from dsFileset " .
+                        "WHERE prod_id = $prod_id ORDER BY reg_time DESC LIMIT 1");
+            $stmt->execute();
+            my $new_last_fs;
+            my $new_newest = $stmt->fetchrow_hashref();
+            if ($new_newest) {
+                $new_last_fs = $new_newest->{fileset_name};
+            } else {
+                $new_last_fs = "none";
+            }
+            $stmt->finish();
+            $dbh->do("UPDATE dsProduct SET last_fs = \'$new_last_fs\' WHERE prod_id = $prod_id");
+        }
+        $dbh->commit();
+    };
+    if ($@) { # an error occured
+        print STDERR "transaction failed, rolling back error was:\n$@\n";
+        eval {$dbh->rollback();};
+        cleanup();
+        exit 1;
+    }
+
+    $dbh->disconnect();
+
+    if ($remove) {
+        if (system "rm -r $fileset_dir") {
+            die("failed to remove $fileset_dir");
+        }
+    } else  {
+        # zap the index script
+        unlink("$index_script_name");
+    }
+    exit 0;
+
+} else {
+    #
+    # add a new fileset
+    #
+
+    if (!$datapath) {
+        $datapath = $fileset_dir;
+    }
+
+    my $stmt = $dbh->prepare("SELECT prod_id FROM dsProduct WHERE prod_name = ?");
+    $stmt->execute($product);
+    my $row = $stmt->fetchrow_hashref();
+    my $prod_id = $row->{prod_id};
+    $stmt->finish();
+
+    if (!$prod_id) {
+        die("product $product not found");
+    }
+    my $count = $dbh->do("SELECT fileset_id FROM dsFileset WHERE fileset_name = ?" .
+                        " AND prod_id = $prod_id", undef, ($fileset));
+    if ($count != 0E0) {
+        print STDERR "Fileset '$fileset' already exists in product $product.\n";
+        exit 1;
+    }
+
+    # Note: There is a race condition here. Somebody could sneak in now and add a fileset with
+    # fileset_name = $fileset. So later we'll check again that only one fileset with the name exists
+
+
+    my $in;
+    if ($filelist eq "-") {
+        $in = *STDIN;
+    } else {
+        open $in, "<$filelist" or die  "cannot open filelist file $filelist\n";
+    }
+
+    my $lineno = 0;
+    my @files;
+    while (<$in>) {
+        chomp;
+        $lineno++;
+
+        my $filename;
+        my $bytes;
+        my $md5sum;
+        my $filetype;
+
+        my @fields = split /\|/;
+        if (@fields < 4) {
+            die "malformed input line at $filelist line $lineno: $_\n";
+        }
+        $filename = shift @fields;
+        $bytes    = shift @fields;  # if this is null it will be calculated below
+        $md5sum   = shift @fields;  # if this is null it will be calculated below
+        $filetype = shift @fields;
+
+        # make sure the length of the type specific columns is 8
+        while (@fields < 8) {
+            push @fields, undef;
+        }
+        my $ts_cols = [@fields];
+
+        my $hashref = {
+            'file'      => $filename,
+            'bytes'     => $bytes,
+            'md5sum'    => $md5sum,
+            'type'      => $filetype,
+            'ts_cols'   => $ts_cols,
+        };
+
+        push @files, $hashref;
+    }
+
+    die("empty filelist\n") if (@files == 0);
+
+    # Prepare the fileset directory
+    if ($linkfiles or $copyfiles) {
+        ## create the fileset directory
+        if (! -e $fileset_dir) {
+            if (!mkdir $fileset_dir) {
+                die("failed trying to create fileset directory $fileset_dir");
+            }
+        }
+        eval {
+            ## then copy or symlink the files into place
+            foreach my $ref (@files) {
+                my $src = (defined $abspath ? '' : "$datapath/") . "$ref->{file}";
+		my $filename = fileparse($ref->{file}, ());
+                my $dest = "$fileset_dir/$filename";
+
+                if ($linkfiles) {
+                    if (! symlink $src, $dest) {
+                        die("failed trying to link $src to $dest");
+                    }
+                } else {
+                    if (!copy($src, $dest)) {
+                        die("copy($src, $dest) failed");
+                    }
+                }
+            }
+        };
+        if ($@) { # an error occured
+            print STDERR "error preparing the fileset directory: $@\n";
+
+            if (!$no_cleanup && system "rm -r $fileset_dir") {
+                die("failed to remove $fileset_dir");
+            }
+            exit $PS_EXIT_UNKNOWN_ERROR;
+        }
+    }
+
+    # now calculate the md5sum and file size if they weren't provided
+    foreach my $file (@files) {
+	my $filename = fileparse($file->{file}, ());
+        my $path = "$fileset_dir/$filename";
+        if (! -e $path ) {
+            die "file $path not found";
+        }
+        if (!$file->{bytes}) {
+            my @finfo = stat($path);
+            unless (@finfo) {
+                die ("can't stat $path");
+            }
+            $file->{bytes}  = $finfo[7];
+        }
+        if (!$file->{md5sum}) {
+            # Get MD5 sum
+            $file->{md5sum} = file_md5_hex($path);
+        }
+    }
+
+
+    if (! $dbh->ping()) {
+        # database connection has gone away, reconnect
+        $dbh->disconnect();
+        $dbh = DBI->connect_cached($dsn, $dbuser, $dbpass, \%conn_attrs)
+            or die "Cannot connect to server\n";
+        print STDERR "ping failed new dbh: $dbh\n" if $verbose;
+    }
+
+    eval {
+        my $qstring = "INSERT into dsFileset" .
+                " (prod_id, fileset_name, reg_time, type," .
+                " prod_col_0, prod_col_1, prod_col_2, prod_col_3, prod_col_4, prod_col_5, " .
+                " prod_col_6, prod_col_7)" .
+                " VALUES(?, ?, UTC_TIMESTAMP(), ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+        $count = $dbh->do($qstring, undef, ($prod_id, $fileset, $fstype, 
+                            $ps0, $ps1, $ps2, $ps3, $ps4, $ps5, $ps6, $ps7));
+
+        die("failed to insert $fileset") if ($count == 0E0);
+
+        my $fileset_id = $dbh->last_insert_id(undef, undef, undef, undef);
+
+        # make sure that no-one got in and created a fileset with this name while we were busy
+        $count = $dbh->do("SELECT fileset_id FROM dsFileset WHERE fileset_name = ?" .
+                        " AND prod_id = $prod_id", undef, ($fileset));
+        if ($count > 1) {
+            die("Fileset '$fileset' already exists under $product.");
+        }
+
+        foreach my $ref (@files) {
+	    my $filename = fileparse($ref->{file}, ());
+            my $query = "INSERT INTO dsFile" .  
+                    " (fileset_id, file_id, file_name, bytes, md5sum, type," .
+                    " type_col_0, type_col_1, type_col_2, type_col_3, type_col_4, type_col_5," .
+                    " type_col_6, type_col_7)" .
+                    " VALUES($fileset_id, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+            my $aref = $ref->{ts_cols};
+            my $do_args =  [($filename, $ref->{bytes}, $ref->{md5sum}, $ref->{type}), @$aref];
+
+            $count = $dbh->do($query, undef, @$do_args);
+
+            if ($count == 0E0) {
+                die("failed to insert $filename into $fileset");
+            }
+        }
+
+        $count = $dbh->do("UPDATE dsProduct SET last_update = UTC_TIMESTAMP(), last_fs = \'$fileset\'"
+                                . " WHERE prod_id = $prod_id");
+        if ($count == 0E0) {
+            die("failed to update dsProduct $prod_id");
+        }
+
+        $dbh->commit();
+
+        # create the cgi script index.txt by making a copy of its parent's
+        if (!copy("$dsroot/$product/index.txt", $index_script_name)) {
+            die("failed to copy index script to file set");
+        }
+    };
+    if ($@) { # an error occured
+        print STDERR "transaction failed, rolling back error was:\n$@\n";
+        eval {$dbh->rollback();};
+        cleanup();
+        exit 1;
+    }
+
+    print STDERR "calling disconnect before exit\n" if $verbose;
+    $dbh->disconnect();
+
+    exit 0;
+}
+
+
+sub show_usage {
+    my $str = shift;
+
+    chomp $str;
+
+    my @tmp = split(/\//, $0);
+
+    pod2usage(
+        -msg =>
+"usage: $tmp[$#tmp] --add fs_name |--del fs_name --product prod_name --type fs_type --list filelist [options]
+
+Commands:
+
+    --add       Add a new fileset with the given metadata to the specified product.
+                File information will be read per-line from the file specifed by the
+                list option.
+                Input lines are in the following format:
+
+                    fileID|size|md5sum|type|ts0|ts1|ts2|ts3
+
+                These entries are read until EOF is reached.
+
+                If either size or md5sum are left blank they will be calculated by dsreg
+
+                (The type specific fields ts[0-3]fields are optional and may be omitted)
+
+    --del       Remove the fileset fs_name.
+
+
+
+Options:
+    --product   the product for the given file set (required)
+    --list      file containing list of files (use - for STDIN) (required)
+    --link      Link files from datapath to Data Store
+    --copy      Copy files from datapath to Data Store
+    --datapath  path to source files for --copy or --link
+    --ps0 - ps7 Optional product specific data
+    --rm        with --del remove the fileset directory from the Data Store
+    --force     with --del do not return error status if the fileset does not exist
+    --dbname    select the Data Store's database name
+                (usually required depending on configuration)
+
+$str",
+        -exitval => 2
+    );
+}
+
+sub cleanup {
+    if (!$no_cleanup && ($linkfiles or $copyfiles)) {
+        if (system "rm -r $fileset_dir") {
+            print STDERR "failed to remove $fileset_dir";
+        }
+    } else {
+        unlink($index_script_name);
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsrootindex
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsrootindex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/dsrootindex	(revision 22322)
@@ -0,0 +1,39 @@
+#!/usr/bin/env perl
+#
+# dsrootindex:  Extract the root data store index from the database.
+# The output is in DataStore index.txt format
+#
+
+use strict;
+use warnings;
+
+use dsdbh;
+
+use DBI;
+
+my $PS_EXIT_CONFIG_ERROR = 3;
+
+my $dbh = getDBHandle();
+
+my $stmt = $dbh->prepare("SELECT * FROM dsProduct");
+$stmt->execute();
+
+print "# productID  |Most recent |Time registered     |type     |Description                     |\n";
+
+while( my $row = $stmt->fetchrow_hashref()) {
+    my $prod_id = $row->{prod_id};
+
+    my ($date, $time) = split / /, $row->{last_update};
+    my $datetime = $date . "T" . $time . "Z";
+
+    my $last_fs =  $row->{last_fs};
+    if (!$last_fs) {
+        # dsrootls is fussy when this field is blank
+        $last_fs = "none";
+    }
+
+    my $line = sprintf "%-13s|%-12s|%-20s|%-9s|%-32s|\n",  $row->{prod_name}, $last_fs, $datetime,
+        $row->{type}, $row->{description};
+
+    print $line;
+}
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/tabledefs.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/tabledefs.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/scripts/tabledefs.sql	(revision 22322)
@@ -0,0 +1,81 @@
+DROP TABLE IF EXISTS dsFileType;
+DROP TABLE IF EXISTS dsFile;
+DROP TABLE IF EXISTS dsFileset;
+DROP TABLE IF EXISTS dsProduct;
+
+#
+# 
+
+CREATE TABLE dsProduct (
+    prod_id     BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+    prod_name   VARCHAR(64) NOT NULL UNIQUE,
+    last_update datetime,
+    last_fs     VARCHAR(64),
+    type        VARCHAR(64),
+    description VARCHAR(255),
+    prod_col_0  VARCHAR(64),        # labels for product specific columns
+    prod_col_1  VARCHAR(64),        # these appear in the header list
+    prod_col_2  VARCHAR(64),
+    prod_col_3  VARCHAR(64),
+    prod_col_4  VARCHAR(64),
+    prod_col_5  VARCHAR(64),
+    prod_col_6  VARCHAR(64),
+    prod_col_7  VARCHAR(64)
+) Engine=InnoDB DEFAULT CHARSET=latin1;
+   
+
+CREATE TABLE dsFileset (
+    prod_id     BIGINT(20),
+    fileset_id  BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+    fileset_name VARCHAR(64) NOT NULL,
+    reg_time    datetime NOT NULL,
+    type        VARCHAR(64),
+    prod_col_0  VARCHAR(64),        # values for product specific columns
+    prod_col_1  VARCHAR(64),
+    prod_col_2  VARCHAR(64),
+    prod_col_3  VARCHAR(64),
+    prod_col_4  VARCHAR(64),
+    prod_col_5  VARCHAR(64),
+    prod_col_6  VARCHAR(64),
+    prod_col_7  VARCHAR(64),
+    FOREIGN KEY  (prod_id) REFERENCES dsProduct(prod_id)
+) Engine=InnoDB DEFAULT CHARSET=latin1;
+
+
+CREATE TABLE dsFile (
+    fileset_id  BIGINT(20),
+    file_id     BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+    file_name   VARCHAR(255) NOT NULL,
+    bytes       BIGINT(20),
+    md5sum      VARCHAR(255),
+    type        VARCHAR(64),
+    type_col_0  varchar(64),        # value for type specific columns
+    type_col_1  varchar(64),
+    type_col_2  varchar(64),
+    type_col_3  varchar(64),
+    type_col_4  varchar(64),
+    type_col_5  varchar(64),
+    type_col_6  varchar(64),
+    type_col_7  varchar(64),
+    FOREIGN KEY (fileset_id) REFERENCES dsFileset(fileset_id)
+) Engine=InnoDB DEFAULT CHARSET=latin1;
+
+#
+# labels for the type specific columns for each Data Store file type
+#
+
+CREATE TABLE dsFileType (
+    type        VARCHAR(64),
+    type_col_0  varchar(64),
+    type_col_1  varchar(64),
+    type_col_2  varchar(64),
+    type_col_3  varchar(64)
+) Engine=InnoDB DEFAULT CHARSET=latin1;
+
+# type specific column for type chip
+INSERT INTO dsFileType (type, type_col_0) VALUES('chip', 'chipname');
+
+# none of these types have any type specific columns
+#INSERT INTO dsFileType (type) VALUES('psrequest');
+#INSERT INTO dsFileType (type) VALUES('psresults');
+#INSERT INTO dsFileType (type) VALUES('pstamp');
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dodsfits
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dodsfits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dodsfits	(revision 22322)
@@ -0,0 +1,42 @@
+#!/usr/bin/env perl
+
+use strict;
+use CGI ':standard';
+
+my $DS_DIR = $ENV{"DS_ROOT"};
+
+my $uri = $ENV{'REQUEST_URI'};
+
+$uri = $ARGV[0];
+#print STDERR "uri is $uri\n";
+
+(my $left, my $param) = split(/\,/, $uri);
+
+#print STDERR "left: $left param: $param\n";
+
+if ($left !~ /\.fits$/) {
+	print header( -status => '404 Not Found' );
+	exit;
+}
+
+my @path = split(/\//, $left);
+
+my $fn = @path[$#path];
+my $fpath = $DS_DIR.'/'.$left;
+
+
+
+print header(	-type => 'image/fits',
+				-attachment => $fn,
+				-content_length => -s $fpath,
+			);
+
+
+open F, $DS_DIR.'/'.$left;
+
+binmode(F);
+binmode(STDOUT);
+
+print while (<F>);
+
+close F;
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsdbh.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsdbh.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsdbh.pm	(revision 22322)
@@ -0,0 +1,20 @@
+# create a database handle based on the configuration given
+# in environment variables
+
+sub getDBHandle {
+    my $dbserver = $ENV{DBSERVER};
+    my $dbuser   = $ENV{DBUSER};
+    my $dbpass   = $ENV{DBPASSWORD};
+    my $dbname   = $ENV{DBNAME};
+
+    die "database enviornment not set up" unless defined($dbserver) and defined($dbuser)
+        and defined($dbpass) and defined($dbname);
+
+    my $dsn = "DBI:mysql:host=$dbserver;database=$dbname";
+
+    my $dbh = DBI->connect($dsn, $dbuser, $dbpass) or die "Cannot connect to server\n";
+
+    return $dbh;
+}
+
+return 1;
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsfits.cgi
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsfits.cgi	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsfits.cgi	(revision 22322)
@@ -0,0 +1,3 @@
+#!/bin/bash
+. ./dsshellconfig
+./dodsfits $*
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsgetindex
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsgetindex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsgetindex	(revision 22322)
@@ -0,0 +1,253 @@
+#!/usr/bin/env perl
+#
+# dsgetindex: Generate response to http GET for a DataStore index
+#
+# The only required argument is the REQUEST_URI.
+# The number of elements in the URI tell us whether we are listing
+# the data store root, a product, or a fileset.
+#
+# if a second argument -html is provided, the output is in HTML format
+# otherwise plain text is used
+#
+# The only thing specific to the ipp used here is the exit codes.
+
+use warnings;
+use strict;
+
+use CGI ':standard';
+use IPC::Cmd 0.36 qw( can_run run );
+
+my $PS_EXIT_CONFIG_ERROR = 3;
+my $PS_EXIT_DATA_ERROR = 5;
+
+my $uri = shift;
+
+if (!$uri) {
+    warn("need uri");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+my $DS_ROOT = $ENV{DS_ROOT};
+
+die("DS_ROOT not found in the environment") unless defined($DS_ROOT);
+
+my @path;
+
+my $html_mode;
+my $arg = shift;
+if ($arg && ($arg eq "-html")) {
+    $html_mode = 1;
+}
+
+#
+# split up the uri into componnts.
+#
+# XXX: Find a simpler way to do this
+
+# collapse any multipe slashes in uri
+$uri = File::Spec->canonpath($uri);
+
+# split the directories from the file
+my $directories;
+my $file = "";
+my $query_str;
+
+if ($html_mode) {
+    # there is no file on the url
+
+    # print STDERR "html mode\n";
+
+    ($directories, $query_str) = split/\?/, $uri;
+
+    # used by the pretty printer
+    @path = split(/\//, $directories);
+
+} else {
+    my $volume;
+    ( $volume, $directories, $file) = File::Spec->splitpath($uri);
+
+    # find the query string if any
+    # (This is only relevant for fileset queries but we follow conductor and do not enforse that restriction)
+    ($file, $query_str) = split/\?/, $file;
+
+    if ($file ne "index.txt") {
+        warn("unexepected index file name: $file");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+}
+
+if (!$query_str) {
+    $query_str = "";
+}
+
+my @dirs = split /\//, $directories;
+
+# shift out the empty string at left of the first /
+shift @dirs;
+# shift out 'ds'
+shift @dirs;
+
+# now we're left with either nothing, a product, or a product and a fileset
+my $product = shift @dirs;
+my $fileset = shift @dirs;
+
+if (@dirs) {
+    # XXX need to output an error page
+    warn("unexpected extra directories: @dirs in uri: $uri");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+my $program;
+if ($fileset) {
+    $program = "dsfsindex";
+} elsif ($product) {
+    $program = "dsprodindex";
+    $fileset .= " $query_str";
+} else {
+    $program = "dsrootindex";
+    $fileset = "";
+    $product = "";
+}
+
+my $missing_tools;
+my $index_program = can_run($program) or (warn "Can't find $program" and $missing_tools = 1);
+if ($missing_tools) {
+    #warn("Can't find required tools.");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+{
+    my $command = "$index_program $product $fileset";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = 
+        run(command => $command, verbose => 0);
+
+    if ($success) {
+        if ($html_mode) {
+            html_index($stdout_buf);
+        } else {
+            print header('text/plain', '200 OK');
+            print @$stdout_buf;
+        }
+    } else {
+        if (0 && $html_mode) {
+            XXX: TODO
+
+        } else {
+            # since we're invoked as a redirect from a text file browsers are expecting
+            # 'text/plain' format
+            print header('text/plain', '404 ERROR');
+            #print header('text/plain', '200 OK');
+            print @$stderr_buf;
+            print STDERR @$stderr_buf;
+        }
+    }
+    exit $error_code >> 8;
+}
+
+
+sub html_index {
+
+    my $out_array_ref = shift;
+    my $outBuf = join( "", @$out_array_ref);
+
+    #print header('text/plain', '200 OK');
+
+    #print "outputting from html_index\n";
+    #print @$out_buf;
+    pprint_pre();
+    my @lines = split /^/, $outBuf;
+    foreach my $line (@lines) {
+        pprint($line);
+    }
+    pprint_post();
+}
+
+#
+# XXX:
+# quick hacky pretty printer
+# Stolen from Eric's Data Store mock up
+#
+
+sub pprint_pre {
+    #use Filesys::Df;
+
+    print header('text/html', '200 OK');
+    print start_html('Data Store');
+
+	# get free space
+#	my $ref = df($DS_ROOT);
+#	printf '<p>Free space: %.2fG (%.1f%%)</p>',
+#		$ref->{bfree}/(1024*1024),
+#		100.0*$ref->{bfree}/$ref->{blocks};
+#        print "\n";
+
+	# return link
+        print a({-href=>"./index.txt"}, "Text Version");
+
+	if ($#path > 1) {
+            print "&nbsp&nbsp&nbsp&nbsp\n";
+
+		my $up = join('/', @path[0..$#path-1]);
+	    print a({-href=>"$up"}, "Up to $up");
+
+	}
+        print "</p>\n";
+
+
+    print '<table cellpadding="3">';
+}
+
+
+sub pprint_post {
+    print '</table>';
+    print p();
+
+    print end_html();
+}
+
+
+sub pprint {
+    my $line = shift;
+
+    my $nolink;
+    my $celltag = 'td';
+
+	# assume a comment line means its the header
+    if ($line =~ /^\s*#/) {
+        $celltag = 'th';
+        $nolink = 1;
+    }
+
+    my @toks = split(/\|/, $line);
+
+    print '<tr>';
+
+    print "<$celltag>";
+
+    if ($nolink) {
+       print $toks[0];
+    }
+
+    else {
+		# assumes id is always first field
+	    my $wpath = join('/',@path)."/$toks[0]";
+
+		# if this is a file request, use a download cgi
+		if ($wpath =~ /\.fits\s*$/) {
+			$wpath = '/ds-cgi/dsfits.cgi?'.substr($wpath, 4); # strip' /ds/'
+		}
+
+        print a({-href=>$wpath}, $toks[0]);
+    }
+
+    print "</$celltag>";
+
+    foreach my $val (@toks[1..$#toks]) {
+		print "<$celltag>";
+		print $val;
+		print "</$celltag>";
+    }
+
+    print '</tr>';
+}
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsindex.cgi
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsindex.cgi	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsindex.cgi	(revision 22322)
@@ -0,0 +1,3 @@
+#!/bin/bash
+. ./dsshellconfig
+dsgetindex $REQUEST_URI -html
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsshellconfig
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsshellconfig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dsshellconfig	(revision 22322)
@@ -0,0 +1,15 @@
+# set up some environment needed by the scripts
+export PERL5LIB=INSERT_VALUE_HERE
+
+# IPC::Cmd::can_run routine doesn't find programs in ds-cgi
+# unless . is in the path
+PATH=.:${PATH}
+
+# setting these here allow the data store to run independnt of the ipp configuration
+export DBSERVER=INSERT_VALUE_HERE
+export DBNAME=INSERT_VALUE_HERE
+export DBPASSWORD=INSERT_VALUE_HERE
+export DBUSER=INSERT_VALUE_HERE
+
+export DS_ROOT=INSERT_VALUE_HERE
+
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dstxtindex.cgi
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dstxtindex.cgi	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/dstxtindex.cgi	(revision 22322)
@@ -0,0 +1,3 @@
+#!/bin/bash
+. ./dsshellconfig
+./dsgetindex $REQUEST_URI;
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/index.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/index.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/web/cgi/index.txt	(revision 22322)
@@ -0,0 +1,1 @@
+<!--#include virtual="/ds-cgi/dstxtindex.cgi" -->
Index: /tags/sj_tags/sj_root_20080929/DataStoreServer/web/conf/httpd-datastore.conf
===================================================================
--- /tags/sj_tags/sj_root_20080929/DataStoreServer/web/conf/httpd-datastore.conf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/DataStoreServer/web/conf/httpd-datastore.conf	(revision 22322)
@@ -0,0 +1,41 @@
+#
+# Root of the data store (where the directory hierarchy lives
+#
+<Directory "/data/ipp000.0/datastore/dsroot">
+
+  DirectoryIndex /ds-cgi/dsindex.cgi
+
+  #Options None
+
+  # turn on server side includes
+  Options +Includes
+
+  # files will be processed for server side includes if they have the
+  # executable bit set
+  XBitHack on
+
+  AddType text/plain .txt
+  AddOutputFilter INCLUDES .txt
+
+  AllowOverride None
+
+  Order allow,deny
+  Allow from all
+</Directory>
+
+
+#
+# Location of the cgi scripts
+#
+
+<Directory "/data/ipp000.0/datastore/ds-cgi">
+  Options ExecCGI
+
+
+  Order allow,deny
+  Allow from all
+</Directory>
+
+Alias       /ds     /data/ipp000.0/datastore/dsroot/
+ScriptAlias /ds-cgi /data/ipp000.0/datastore/ds-cgi/
+
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Build
+META.yml
+Makefile
+Makefile.PL
+_build
+blib
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/Build.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/Build.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/Build.PL	(revision 22322)
@@ -0,0 +1,45 @@
+use Module::Build;
+# See perldoc Module::Build for details of how this works
+
+Module::Build->new(
+    module_name         => 'Nebulous',
+    dist_version_from   => 'lib/Nebulous/Server.pm',
+    author              => 'Joshua Hoblitt <jhoblitt@cpan.org>',
+    license             => 'gpl',
+    create_makefile_pl  => 'passthrough',
+    requires            => {
+        'Apache2::Const'        => 0,
+        'Class::Accessor::Fast' => 0,
+        'Config::YAML'          => '1.42',
+        'DBD::mysql'            => '3.0007',
+        'DBI'                   => '1.53',
+        'Digest::SHA1'          => 0,
+        'File::ExtAttr'         => '1.03',
+        'File::Path'            => '1.08',
+        'File::Spec'            => 0,
+        'File::Spec::Functions' => 0,
+        'File::Temp'            => 0,
+        'Log::Log4perl'         => '0.48',
+        'Net::Server::Daemonize'=> '0.05',
+        'Params::Validate'      => '0.73',
+        'SOAP::Lite'            => '0.69',
+        'Sys::Statistics::Linux::DiskUsage' => '0.02',
+        'URI'                   => '1.30',
+    },
+    build_requires      => {
+        'Test::More'            => '0.49',
+        'Test::URI'             => '1.06',
+    },
+    recommends          => {
+        'Test::Distribution'    => '1.22',
+        'Apache2::SOAP'         => 0,
+        'Apache::DBI'           => '1.05',
+    },
+    script_files        => [qw(
+        bin/neb-voladd
+        bin/neb-admin
+        bin/neb-fsck
+        bin/neb-initdb
+        bin/nebdiskd
+    )],
+)->create_build_script;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/Changes
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/Changes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/Changes	(revision 22322)
@@ -0,0 +1,167 @@
+Revision history for Nebulous
+
+0.16
+    - add so_id/name idxs to storage_object_xattr table
+    - rename neb-addvol -> neb-voladd
+
+0.15 Thu Sep 11 13:00:59 HST 2008
+    - invert soft volume semantics
+
+0.14 Wed Aug 20 16:25:43 HST 2008
+    - change the get_replication_volume_for_ext_id query to choose a storage
+      volume (that meets the critera) at random
+
+0.13 Wed Jul  9 16:36:27 HST 2008
+    - create a Nebulous::Keys object to represent a parsed neb key and
+      impliment other changes to abstract out the handling of keys in order to
+      support "soft" volume requests and the "any" volume
+    - change find_objects_with_unavailable_instances query to only count
+      volumes marked as 'available'
+    - fix nebdiskd so it doesn't removed volumes as being successful stat()'d
+      even when the stat() failed under --debug
+    - add "smart" storage volume selection when ->replicate_object() is invoked without a target volume name
+    - add volume.host field
+    - add --host option to neb-initdb
+    - add --host option to neb-addvol
+    - add --host option to neb-admin
+    - add --host option to nebdiskd
+    - decalare default SQL character set
+
+0.12 Wed May 28 11:03:06 HST 2008
+    - remove the use of LAST_INSERT_ID() from the new_object_instance query
+    - add neb-admin util
+    - fix Nebulous::Server::setxattr_object() so that creating a new xattr with
+      the "replace" flag works
+
+0.11 Tue May 20 10:54:50 HST 2008
+    - add support for the magical ':any' volume to
+      Nebulous::Server:find_instances()
+    - call sync on newly created files from
+      Nebulous::Server::_create_empty_file()
+    - improved error strings in Nebulous::Server::find_instances()
+
+0.10 Wed May 14 17:26:48 HST 2008
+    - cleanup error handling in Nebulous::Server::stat_object()/find_objects()
+    - update Nebulous::Server::lock_object()/unlock_object() fault strings
+    - cleanup database error handling: make sure queries are ->finished before
+      showing an expect, remove DBI->rollback from the error paths of methods
+      that don't insert data
+    - improve getxattr_object() error handling
+    - document xattr methods
+    - change Nebulous::Server::stat() to not die when passed a non-existant key
+    - change listxattr_object() to return an arrayref
+    - change default log level to 'WARN'
+    - fix _is_valid_object_key() to handle nebulous keys
+
+0.09 Tue May  6 12:12:02 HST 2008
+    - allow volume name arguments to be undef
+
+0.08 Thu Apr 17 16:08:16 HST 2008
+    - set permissions on storage instance directories
+
+0.07
+    - add Nebulous::Server::mounts()
+    - server/client split into Nebulous::Server and Nebulous::Client packages
+    - add support for storing instances in multi-tier 'hashed' directories 
+    - pickup test DB settings from the env.
+    - add neb-ls -l|-1 option
+    - add neb-fsck
+    - add neb-stat
+    - add additional fkey constrains to the instance table
+    - fix installation of neb-replicate & neb-cull
+    - redo database schema with foreign keys
+    - add Nebulous::Keys pod
+
+0.06 Fri Jan 25 10:55:59 HST 2008
+    - define Nebulous::Client::find_objects() as returning [] when NO keys are
+      found
+    - change neb-touch to use Nebulous::Client::find_objects() instead of
+      ::find()
+    - add Nebulous::Sever:: neb key/uri support
+    - add Nebulous::Util::parse_neb_key()
+    - change bin/* scripts to conistently use --volume instead of --node,
+      etc.
+    - pod cleanups
+    - add neb-replicate & neb-cull
+
+0.05 2007-05-04 16:59:31(???)
+    - make filesystem xattr support optional & disabled by default
+    - generate instance filenames that leave the "key" at the end so as to not
+      break a pplications that expect a certain "extension" on the filename
+    - set an xattr on all created files of "user.nebulous_key"
+    - add neb-locate
+    - change all Nebulous::Client public methods to behave in list context
+    - rework the getmountedvol() stored procedure to attempt to work around
+      what appears to be some nasty transacational isolation leak throught that
+      was causing getmountedvol() to randomly hang when nebdiskd changed the
+      mount table.
+    - fix a nasty logic bug in Nebulous::Client->replicate()
+    - break 1:1 relationship between key names and on disk file names
+    - overhaul Nebulous::Client->cull()
+    - add the number of instances to ->stat_object()
+    - SQL cleanup
+    - better paramter checking
+    - overhaul instance table
+    - add ->_is_valid_object_key() method
+    - change ->find_instances() to check it's params and only return instances
+      on mounted & available volumes
+    - add volume.available field and logic to use it
+    - add volume.allocate field and logic to use it
+    - add instance.vol_id field
+    - remove $class_id & $comment params from Nebulous::Server->create() and
+      Nebulous::Client->create*() and all supporting functions and tests
+    - add Nebulous::Server->{setxattr_object, listxattr_object,
+      getxattr_object, removexattr_object}()
+    - add neb-cat
+    - add neb-mv
+    - change Nebulous::Client->move() to use ->rename_object()
+    - add Nebulous::Server->rename_object()
+    - add neb-rm
+    - add neb-cp
+    - add neb-ls
+    - add Nebulous::Client->find_objects()
+    - add Nebulous::Server->find_objects()
+    - add neb-touch
+    - add neb-df
+    - change neb-addvol to properly mangle URIs into paths
+
+0.04 Mon Apr 23 13:33:16 HST 2007
+    - include autogen.sh in the tarball
+    - add nebdiskd
+    - [nebclient] VERSION to '0.0.3'
+    - change API to generally return true URIs instead of file paths
+    - add support for some what intelligent volume allocations
+    - minor code & build fixes, ws changes
+
+0.03 Mon Dec 11 14:19:45 HST 2006
+    - change Nebulous::Server instantiate objects (init -> new) instead of
+      working with class data
+    - add a DESTROY method to Nebulous::Server to tear down the database
+      connection
+    - add a Nebulous::Server->config() accessor
+    - make Nebulous::Server->db() Apache::DBI aware
+    - make SQL syntax MySQL 5 compatible
+    - update SOAP, DBI & DBD, and Apache::* deps
+    - change Nebulous::Server::SOAP to be mod_perl aware
+    - change Nebulous::Server::SOAP install stub methods to bypass AUTOLOAD on
+      successive calls to the same method
+    - make Nebulous::Server::Log mod_perl aware
+    - change Nebulous::Server::Log to accept a Nebulous::Server::Config object
+      as a parameter instead of fetching it as a singleton
+    - make Nebulous::Server::Config no longer a singleton
+
+0.02 Fri Dec  2 17:36:59 HST 2005
+    - recommend Test::Distribution 1.22
+    - add t/00_distribution.t
+    - rename test files
+    - [nebclient] VERSION to '0.0.2'
+    - Nebulous::Client, Nebulous::Server, & Nebulous::Server::SQL
+        VERSION to '0.02'
+    - add Nebulous::Client->open_create()
+    - change Nebulous::Client->create() to return just a filename
+    - [nebclient] add nebOpenCreate()
+    - [nebclient] change nebCreate() to return just a filename
+
+0.01 Fri Sep  3 10:23:31 2004
+    - first working version
+	- original version; created by ExtUtils::ModuleMaker 0.32
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/LICENSE	(revision 22322)
@@ -0,0 +1,260 @@
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/MANIFEST
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/MANIFEST	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/MANIFEST	(revision 22322)
@@ -0,0 +1,50 @@
+Build.PL
+Changes
+LICENSE
+MANIFEST
+README
+Todo
+bin/neb-voladd
+bin/neb-admin
+bin/neb-fsck
+bin/neb-initdb
+bin/nebdiskd
+docs/c_api.h
+docs/database_setup.txt
+docs/design.txt
+docs/requirements.txt
+docs/setup.txt
+docs/tmp.txt
+examples/Makefile
+examples/uri_test.pl
+examples/nebexample.c
+examples/nebexample.pl
+lib/Test/Nebulous.pm
+lib/Nebulous/Keys.pm
+lib/Nebulous/Server.pm
+lib/Nebulous/Server.pod
+lib/Nebulous/Server/Apache.pm
+lib/Nebulous/Server/Config.pm
+lib/Nebulous/Server/Log.pm
+lib/Nebulous/Server/SOAP.pm
+lib/Nebulous/Server/SQL.pm
+scripts/bench_test.pl
+scripts/nebulous.cgi
+scripts/ptest.pl
+t/00_distribution.t 
+t/01_load.t
+t/02_server_setup.t
+t/03_server_create_object.t
+t/04_server_replicate_object.t
+t/05_server_lock_object.t
+t/06_server_unlock_object.t
+t/07_server_find_instances.t
+t/08_server_delete_instance.t
+t/09_server_stat_object.t
+t/10_server_is_valid_volume_name.t
+t/11_server_is_valid_object_key.t
+t/12_server_find_objects.t
+t/13_server_rename_object.t
+t/14_server_xattr.t
+t/15_mounts.t
+t/75_parse_neb_key.t
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/README	(revision 22322)
@@ -0,0 +1,16 @@
+pod2text Poi::PixelData.pm > README
+
+If this is still here it means the programmer was too lazy to create the readme file.
+
+You can create it now by using the command shown above from this directory.
+
+At the very least you should be able to use this set of instructions
+to install the module...
+
+perl Build.PL
+./Build
+./Build test
+./Build install
+
+
+If you are on a windows box you should use 'nmake' rather than 'make'.
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/Todo
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/Todo	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/Todo	(revision 22322)
@@ -0,0 +1,37 @@
+TODO list for Perl module Nebulous
+
+- make nebclient lib thread safe
+- handle/check for storage volumes being offline
+- admin interface to control volumes (enable/disable, add/remove)
+- add zeroconf support
+- add support for SQL and/or Apache logging
+- make copy replicate source attributes, implement as replicate/reparent?
+- test that client->delete_instance removes the storage object too
+- review caching of database statement handles
+- code tiding/consistency
+- file creation retrying
+- server config file
+- make Apache::DBI behave
+- split SOAP out of Client.pm
+- Perl client error string support with DBI like 'AutoRaise' exceptions
+- CLI utilities
+- write neb-fsck utility
+- write Linux::statfs module so that statfs.f_bavail can be obtained (not available via df)
+- change neb-ls to have a limit number of keys shown (-n) param and default to
+  something reasonable (say 20)
+- add multiple regex and/or glob support to neb-ls, eg. neb-ls -r 'foo.*' -r 'bar$'
+- store the key name as an xattr on the actual on disk file
+- add an neb-locate --reverse option to map uri/paths into keys
+
+distant future
+--
+- cache some database tables (updated at some interval) 
+- volume size/% in-use discovery
+- server side per client lock tracking
+- per client authentication
+- client side lookup table of filehandles/locks
+- Perl only?, scoped lock release/filehandle destruction
+- restructure database so that storage objects are more like inodes
+- add support for directories
+- FUSE interface
+- hot-spot analysis
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/autogen.sh	(revision 22322)
@@ -0,0 +1,1 @@
+perl Build.PL -apxs /usr/sbin/apxs2 
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-admin
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-admin	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-admin	(revision 22322)
@@ -0,0 +1,277 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005-2008  Joshua Hoblitt
+#
+# $Id: neb-admin,v 1.8 2008-07-10 02:40:33 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use DBI;
+use Nebulous::Server::SQL;
+use Nebulous::Client;
+use URI;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($db, $dbhost, $dbuser, $dbpass, $pending, $limit, $verbose);
+
+$db     = $ENV{'NEB_DB'};
+$dbhost = $ENV{'NEB_DBHOST'} || 'localhost';
+$dbuser = $ENV{'NEB_USER'};
+$dbpass = $ENV{'NEB_PASS'};
+
+GetOptions(
+    'db|d=s'                => \$db,
+    'host=s'                => \$dbhost,
+    'user|u=s'              => \$dbuser,
+    'pass|p=s'              => \$dbpass,
+    'pendingreplicate|r'    => \$pending,
+    'limit|l=i'             => \$limit,
+    'verbose|v'             => \$verbose,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --db --user --pass", -exitval => 2 )
+    unless $db && $dbuser && $dbpass;
+pod2usage( -msg => "--limit is meaningless without --pendingreplicate",
+        -exitval => 2 )
+    if defined $limit and not defined $pending;
+pod2usage( -msg => "no operation specified", -exitval => 2 )
+    unless defined $pending;
+
+# set default limit to 5
+$limit ||= 5;
+
+my $dbh = DBI->connect(
+    "DBI:mysql:database=$db:host=$dbhost",
+    $dbuser,
+    $dbpass,
+    {
+        RaiseError => 1,
+        PrintError => 0,
+        AutoCommit => 1,
+    },
+);
+
+my $sql = Nebulous::Server::SQL->new();
+
+# so_id, ext_id, instances, available_instances, need_recovery, recoverable
+my $query = $dbh->prepare( $sql->find_objects_with_unavailable_instances
+        . " LIMIT $limit" );
+$query->execute;
+
+my @rows;
+while (my $row = $query->fetchrow_hashref) {
+    push @rows, $row;        
+}
+$query->finish;
+    
+exit unless scalar @rows;
+
+print "replicatePending MULTI\n\n";
+
+foreach my $obj (@rows) {
+    if (defined $verbose) {
+        require Data::Dumper;
+        print Data::Dumper::Dumper($obj);
+    }
+
+    my $so_id = $obj->{so_id};
+    my $key = $obj->{ext_id};
+    my $has = $obj->{instances};
+    my $vol_name = $obj->{volume_name};
+    my $vol_host = $obj->{volume_host};
+    my $available = $obj->{available_instances} || 0;
+    my $need_recovery = $obj->{need_recovery};
+    my $recoverable = $obj->{recoverable};
+    # if the copies xattr is unset and the object has 2 or more instances, we
+    # will assume a target of 2 copies
+    my $copies = $obj->{copies} || 2;
+
+    # objects with only a single instance that are offline are unrecoverable so
+    # we don't need to handle that special case
+#    next unless $need_recovery;
+    unless ($recoverable) {
+        warn "so_id: $so_id key: \'$key\' ",
+            "can not be recovered - no available instances\n";
+        next;
+    }
+
+    my $need = $copies - $available;
+    $need = 0 if $need < 0;
+
+    next unless $need;
+
+    my %md = (
+        key         => $key,
+        need_copies => $need,
+        volume_name => $vol_name,
+        volume_host => $vol_host,
+    );
+
+    print_metadata("replicatePending", \%md);
+    print "\n";
+}
+
+sub print_metadata
+{
+    my ($name, $pairs) = @_;
+
+    print "$name METADATA\n";
+
+    # format from formatMetadataItem() in pzMetadataConfig.c
+    foreach my $key (keys %$pairs) {
+        my $value = $pairs->{$key};
+        if (looks_like_number($value)) {
+            printf("   " . "%-15s  %-8s  %-15s\n", $key, "S32", $value);
+        } else {
+            printf("   " . "%-15s  %-8s  %-15s\n", $key, "STR", $value);
+        }
+    }
+    
+    print "END\n";
+}
+
+# looks_like_number() was stolen from Scalar::Util
+#
+# Copyright (c) 1997-2006 Graham Barr <gbarr@pobox.com>. All rights reserved.
+# This program is free software; you can redistribute it and/or modify it
+# under the same terms as Perl itself.
+
+sub looks_like_number {
+  local $_ = shift;
+
+  # checks from perlfaq4
+  return 0 if !defined($_) or ref($_);
+  return 1 if (/^[+-]?\d+$/); # is a +/- integer
+  return 1 if (/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/); # a C float
+  return 1 if ($] >= 5.008 and /^(Inf(inity)?|NaN)$/i) or ($] >= 5.006001 and /^Inf$/i);
+
+  0;
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-admin - perform misc. Nebulous administrative functions
+
+=head1 SYNOPSIS
+
+    neb-admin [--host <db host>] [--db <db name>] [--user <db username>]
+        [--pass <db password>] [--pendingreplicate] [--limit <n>]
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --pendingreplicate|-r
+
+Print out a list of nebulous keys that need additional replications created.
+
+=item * --limit|-l <n>
+
+Limits the number of items return in response to some request (like
+C<--pendingreplicate>). 
+
+Optional.  Defaults to 5.  --limit 0 is treated as asking for the default
+value.
+
+=item * --host <database host>
+
+Name of host on which the database resides.
+
+Optional.  Defaults to C<localhost>.
+
+=item * --db|-d <database>
+
+Name of database (C<namespace>) to create tables in.
+
+Optional if the appropriate environment variable is set.
+
+=item * --user|-u <username>
+
+Username to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --pass|-p <password>
+
+Password to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_DB>
+
+Equivalent to --db|-d
+
+=item * C<NEB_HOST>
+
+Equivalent to --host
+
+=item * C<NEB_USER>
+
+Equivalent to --user|-u
+
+=item * C<NEB_PASS>
+
+Equivalent to --pass|-p 
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-addvol>, L<neb-fsck>, L<neb-initdb>, L<nebdiskd>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-fsck
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-fsck	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-fsck	(revision 22322)
@@ -0,0 +1,224 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005-2008  Joshua Hoblitt
+#
+# $Id: neb-fsck,v 1.2 2008-03-20 21:12:06 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use DBI;
+use Nebulous::Server::SQL;
+use Nebulous::Client;
+use URI;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($db, $dbuser, $dbpass, $server);
+
+$db     = $ENV{'NEB_DB'} unless $db;
+$dbuser = $ENV{'NEB_USER'} unless $dbuser;
+$dbpass = $ENV{'NEB_PASS'} unless $dbpass;
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'db=s'          => \$db,
+    'user=s'        => \$dbuser,
+    'pass=s'        => \$dbpass,
+    'server|s=s'    => \$server,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+pod2usage( -msg => "Required options: --db --user --pass", -exitval => 2 )
+    unless $db && $dbuser && $dbpass;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+my $dbh = DBI->connect(
+    "DBI:mysql:database=$db:host=localhost",
+    $dbuser,
+    $dbpass,
+    {
+        RaiseError => 1,
+        PrintError => 0,
+        AutoCommit => 1,
+    },
+);
+
+my $sql = Nebulous::Server::SQL->new();
+
+print "Pass 1: Look for objects with inaccessable instances\n";
+
+# so_id, ext_id, instances, available_instances, need_recovery, recoverable
+my $query = $dbh->prepare( $sql->find_objects_with_unavailable_instances );
+$query->execute;
+
+my @rows;
+while (my $row = $query->fetchrow_hashref) {
+    push @rows, $row;        
+}
+$query->finish;
+
+foreach my $obj (@rows) {
+#    use Data::Dumper;
+#    print Dumper($obj);
+    my $so_id = $obj->{so_id};
+    my $key = $obj->{ext_id};
+    my $has = $obj->{instances};
+    my $available = $obj->{available_instances} || 0;
+    my $need_recovery = $obj->{need_recovery};
+    my $recoverable = $obj->{recoverable};
+    # if the copies xattr is unset and the object has 2 or more instances, we
+    # will assume a target of 2 copies
+    my $copies = $obj->{copies} || 2;
+
+    # objects with only a single instance that are offline are unrecoverable so
+    # we don't need to handle that special case
+    next unless $need_recovery;
+    unless ($recoverable) {
+        warn "so_id: $so_id key: \'$key\' ",
+            "can not be recovered - no available instances\n";
+        next;
+    }
+
+    my $need = $copies - $available;
+    $need = 0 if $need < 0;
+
+    next unless $need;
+
+    for (; $need > 0; $need--) {
+        warn "so_id: $so_id key: \'$key\' ",
+            "has $available available instances, target $copies -- replicating";
+        $neb->replicate($obj->{ext_id})
+            or warn "so_id: $so_id key: \'$key\' replication failed";
+
+        $available++;
+    }
+}
+
+print "Pass 2: Check volumes for files without an instance\n";
+
+# get a list of volumes and their paths
+# get a list of all instances on a volume
+# get a list of all files under the volumes path
+# compare the two lists
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-fsck - check and repair Nebulous volumes
+
+=head1 SYNOPSIS
+
+    neb-fsck [--db <database>] [--user <username>] [--pass <password>]
+        [--server <server>]
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=item * --db|-d <database>
+
+Name of database (C<namespace>) to create tables in.
+
+Optional if the appropriate environment variable is set.
+
+=item * --user|-u <username>
+
+Username to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --pass|-p <password>
+
+Password to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=item * C<NEB_DB>
+
+Equivalent to --db|-d
+
+=item * C<NEB_USER>
+
+Equivalent to --user|-u
+
+=item * C<NEB_PASS>
+
+Equivalent to --pass|-p 
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-df>, L<nebdiskd>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-initdb
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-initdb	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-initdb	(revision 22322)
@@ -0,0 +1,164 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005-2007  Joshua Hoblitt
+#
+# $Id: neb-initdb,v 1.8 2008-06-30 22:44:12 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.03';
+
+use DBI;
+use Nebulous::Server::SQL;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($db, $dbhost, $dbuser, $dbpass);
+
+$db     = $ENV{'NEB_DB'} unless $db;
+$dbhost = $ENV{'NEB_DBHOST'} || 'localhost';
+$dbuser = $ENV{'NEB_USER'} unless $dbuser;
+$dbpass = $ENV{'NEB_PASS'} unless $dbpass;
+
+GetOptions(
+    'db=s'      => \$db,
+    'host=s'    => \$dbhost,
+    'user=s'    => \$dbuser,
+    'pass=s'    => \$dbpass,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --db --user --pass", -exitval => 2 )
+    unless $db && $dbuser && $dbpass;
+
+my $dbh = DBI->connect(
+    "DBI:mysql:database=$db:host=$dbhost",
+    $dbuser,
+    $dbpass,
+    {
+        RaiseError => 1,
+        PrintError => 0,
+        AutoCommit => 1,
+    },
+);
+
+my $sql = Nebulous::Server::SQL->new();
+
+print "Dropping any existing tables...";
+
+foreach my $statement (@{ $sql->get_db_clear }) {
+    $dbh->do( $statement );
+}
+
+print " OK\nCreating new tables...";
+
+foreach my $statement (@{ $sql->get_db_schema }) {
+    $dbh->do( $statement );
+}
+
+print " OK\n";
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-initdb - initialize a Nebulous server database
+
+=head1 SYNOPSIS
+
+    neb-initdb [--db <database>] [--user <username>] [--pass <password>]
+
+=head1 DESCRIPTION
+
+This program initialize a database for use by L<Nebulous::Server> by creating
+the appropriate set of tables.  Any pre-existing tables with conflicting names
+are first removed.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --db|-d <database>
+
+Name of database (C<namespace>) to create tables in.
+
+Optional if the appropriate environment variable is set.
+
+=item * --user|-u <username>
+
+Username to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --pass|-p <password>
+
+Password to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_DB>
+
+Equivalent to --db|-d
+
+=item * C<NEB_USER>
+
+Equivalent to --user|-u
+
+=item * C<NEB_PASS>
+
+Equivalent to --pass|-p 
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005-2007  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-addvol>, L<neb-df>, L<nebdiskd>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-voladd
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-voladd	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/neb-voladd	(revision 22322)
@@ -0,0 +1,184 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005-2008  Joshua Hoblitt
+#
+# $Id: neb-voladd,v 1.1 2008-09-20 02:42:24 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use DBI;
+use Nebulous::Server::SQL;
+use URI::file;
+use URI;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($db, $dbhost, $dbuser, $dbpass, $vname, $vhost, $uri);
+
+$db     = $ENV{'NEB_DB'} unless $db;
+$dbhost = $ENV{'NEB_DBHOST'} || 'localhost';
+$dbuser = $ENV{'NEB_USER'} unless $dbuser;
+$dbpass = $ENV{'NEB_PASS'} unless $dbpass;
+
+GetOptions(
+    'db|d=s'            => \$db,
+    'host=s'            => \$dbhost,
+    'user|u=s'          => \$dbuser,
+    'pass|p=s'          => \$dbpass,
+    'vname|n=s'         => \$vname,
+    'vhost=s'           => \$vhost,
+    'uri|u=s'           => \$uri,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --db --user --pass --vname --vhost --uri", -exitval => 2 )
+    unless $db && $dbuser && $dbpass && $vname && $vhost && $uri;
+
+my $dbh = DBI->connect(
+    "DBI:mysql:database=$db:host=$dbhost",
+    $dbuser,
+    $dbpass,
+    {
+        RaiseError => 1,
+        PrintError => 0,
+        AutoCommit => 1,
+    },
+);
+
+my $sql = Nebulous::Server::SQL->new();
+
+print "Checking URI...";
+
+my $path = URI->new($uri)->path;
+unless (-d $path) {
+    die "path: $path dirived from URI: $uri does not exist";
+}
+
+print " OK\n";
+
+print "Adding volume...";
+
+my $query = $dbh->prepare( $sql->new_volume );
+$query->execute( $vname, $vhost, $path );
+
+print " OK\n";
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-voladd - add a storage volume to a Nebulous server
+
+=head1 SYNOPSIS
+
+    neb-voladd --volume <volume name> --uri <volume uri>
+        [--db <database>] [--user <username>] [--pass <password>]
+
+=head1 DESCRIPTION
+
+This program initialize a database for use by L<Nebulous::Server> by creating
+the appropriate set of tables.  Any pre-existing tables with conflicting names
+are first removed.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --volume <volume name>
+
+Symbolic name of volume being added.
+
+=item * --uri|-u <volume uri>
+
+URI to the volume being added.
+
+=item * --db|-d <database>
+
+Name of database (C<namespace>) to create tables in.
+
+Optional if the appropriate environment variable is set.
+
+=item * --user|-u <username>
+
+Username to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --pass|-p <password>
+
+Password to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_DB>
+
+Equivalent to --db|-d
+
+=item * C<NEB_HOST>
+
+Equivalent to --host
+
+=item * C<NEB_USER>
+
+Equivalent to --user|-u
+
+=item * C<NEB_PASS>
+
+Equivalent to --pass|-p 
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-df>, L<nebdiskd>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/nebdiskd
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/nebdiskd	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/bin/nebdiskd	(revision 22322)
@@ -0,0 +1,420 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007  Joshua Hoblitt
+# 
+# $Id: nebdiskd,v 1.6 2008-07-03 22:01:03 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use Config::YAML;
+use DBI;
+use File::Spec;
+use Nebulous::Server::SQL;
+use Net::Server::Daemonize qw( daemonize unlink_pid_file );
+use Sys::Statistics::Linux::DiskUsage;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($debug, $db, $dbhost, $dbuser, $dbpass, $pidfile, $stop, $restart, $verbose);
+GetOptions(
+    'db|d=s'    => \$db,
+    'debug'     => \$debug,
+    'host|h=s'  => \$dbhost,
+    'pass|p=s'  => \$dbpass,
+    'pidfile=s' => \$pidfile,
+    'restart|r' => \$restart,
+    'stop|s'    => \$stop,
+    'user|u=s'  => \$dbuser,
+    'verbose|v' => \$verbose,
+) || pod2usage( 2 );
+
+my $rcfile = "$ENV{HOME}/.nebdiskrc";
+$rcfile = File::Spec->canonpath($rcfile);
+
+my $c = read_rcfile($rcfile);
+
+$db     ||= $c->get_db      || $ENV{'NEB_DB'};
+$dbhost ||= $c->get_dbhost  || $ENV{'NEB_DBHOST'} || 'localhost';
+$dbuser ||= $c->get_dbuser  || $ENV{'NEB_USER'};
+$dbpass ||= $c->get_dbpass  || $ENV{'NEB_PASS'};
+$pidfile ||= $c->get_pidfile || "/var/tmp/nebdiskd";
+my $mounts = $c->get_mounts;
+my $poll_interval = $c->get_poll_interval || 5;
+
+if ($restart || $stop) {
+    my $status = kill_pid_file($pidfile);
+    exit($status) if $stop;
+}
+
+$c->set_db($db);
+$c->set_dbhost($dbhost);
+$c->set_dbuser($dbuser);
+$c->set_dbpass($dbpass);
+$c->set_pidfile($pidfile);
+$c->set_poll_interval($poll_interval);
+$c->write;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --db --user --pass", -exitval => 2 )
+    unless $db && $dbuser && $dbpass;
+
+daemonize(
+    $<,     # User
+    $),     # Group
+    $pidfile,  # PID file
+) unless $debug;
+
+$SIG{TERM} = sub { unlink_pid_file($pidfile); exit(); };
+$SIG{INT}  = $SIG{TERM};
+$SIG{HUP}  = sub { $c = read_rcfile($rcfile) }; 
+
+my $dbh = setup_db(
+        db      => $db,
+        dbhost  => $dbhost,
+        dbuser  => $dbuser,
+        dbpass  => $dbpass,
+    );
+
+poll_mounts(
+        dbh             => $dbh,
+        poll_interval   => $poll_interval,
+        debug           => $debug,
+    ) or die "poll_mounts() should not have returned";
+
+sub poll_mounts
+{
+    my %p = @_;
+
+    my $dbh             = $p{dbh} or return;
+    my $poll_interval   = $p{poll_interval} || 60;
+    my $debug           = $p{debug} || 0;
+
+    my $lxs = Sys::Statistics::Linux::DiskUsage->new;
+
+    while (1) {
+        # list of mount points to stat so that the automounter will mount these
+        # volumes if they have been unmounted.
+        stat_dirs($mounts);
+
+        eval {
+            my $query = $dbh->prepare_cached("INSERT INTO mount VALUES(?, ?, ?)");
+            $dbh->do("DELETE FROM mount");
+
+            print "flushed mount table\n" if $debug;
+
+            my $stats = $lxs->get;
+            foreach my $dev (keys %$stats) {
+                my $mnt = $stats->{$dev};
+                my %mount;
+                $query->execute(@$mnt{qw( mountpoint total usage )});
+                print "adding $mnt->{mountpoint} to db\n" if $debug;
+            }
+
+            $dbh->do("call getmountedvol()");
+
+            $dbh->commit;
+        };
+        if ($@) {
+            $db->rollback;
+            warn $@;
+        }
+
+        sleep $poll_interval;
+    }
+}
+
+sub read_rcfile
+{
+    my $rcfile = shift;
+
+    return unless defined $rcfile;
+
+    if (!-f $rcfile) {
+        open(my $fh, '>', $rcfile) or die "can't open file: $!";
+        close($fh) or die "can't close file:$!";
+    }
+
+    return Config::YAML->new(
+            config => $rcfile,
+            output => $rcfile,
+    );
+}
+
+
+sub setup_db
+{
+    my %p = @_;
+
+    return unless defined $p{db}
+              and defined $p{dbhost}
+              and defined $p{dbuser}
+              and defined $p{dbpass};
+
+    my $sql = Nebulous::Server::SQL->new;
+
+    my $dbh = DBI->connect(
+        "DBI:mysql:database=$p{db}:host=$p{dbhost}",
+        $p{dbuser},
+        $p{dbpass},
+        {
+            RaiseError => 1,
+            PrintError => 1,
+            AutoCommit => 0,
+        },
+    );
+
+    eval {
+        $dbh->do( $sql->set_transaction_model );
+        $dbh->commit;
+    };
+    if ($@) { 
+        $db->rollback;
+        die $@;
+    }
+
+    return $dbh;
+}
+
+
+sub stat_dirs
+{
+    my $mounts = shift;
+
+    return unless defined $mounts;
+
+    foreach my $path (@$mounts) {
+        if (stat File::Spec->canonpath($path)) {
+            print "stated $path\n" if $debug;
+        } else {
+            warn "can not stat path: $path";
+        }
+    }
+}
+
+#
+# This function, originally named check_pid_file() was copied from
+# Net::Server::Daemonize '0.05' licensed under the Perl license.  It has been
+# renamed and modified to kill off process who's PID value is stored in pidfile.
+#
+
+sub kill_pid_file {
+  my $pid_file = shift;
+
+  ### no pid_file = return success
+  return 1 unless -e $pid_file;
+
+  ### get the currently listed pid
+  if( ! open(_PID,$pid_file) ){
+    die "Couldn't open existant pid_file \"$pid_file\" [$!]\n";
+  }
+  my $_current_pid = <_PID>;
+  close _PID;
+  my $current_pid = $_current_pid =~ /^(\d{1,10})/ ? $1 : die "Couldn't find pid in existing pid_file";
+
+  my $exists = undef;
+
+  ### try a proc file system
+  if( -d '/proc' ) {
+    $exists = -e "/proc/$current_pid";
+
+  ### try ps
+  #}elsif( -x '/bin/ps' ){ # not as portable
+  # the ps command itself really isn't portable
+  # this follows BSD syntax ps (BSD's and linux)
+  # this will fail on Unix98 syntax ps (Solaris, etc)
+  }elsif( `ps h o pid p $$` =~ /^\s*$$\s*$/ ){ # can I play ps on myself ?
+    $exists = `ps h o pid p $current_pid`;
+
+  }
+
+  ### running process exists, ouch
+    if( $exists ){
+
+        if( $current_pid == $$ ){
+            warn "Pid_file created by this same process. Doing nothing.\n";
+            return 1;
+        }else{
+            kill 'TERM', $current_pid
+                or die "Failed to signal process ($current_pid)";
+            unlink $pid_file || die "Couldn't remove pid_file \"$pid_file\" [$!]\n";
+            return 1;
+
+        }
+    }
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+nebdiskd - Nebulous mounted volume capacity monitoring daemon
+
+=head1 SYNOPSIS
+
+    nebdiskd [--db <db name>] [--user <db username>] [--pass <db password>] [--debug] [--pidfile <filename>] [--stop] [--restart] [--verbose]
+
+=head1 DESCRIPTION
+
+This program looks for mounted volumes, at a configurable interval, and records
+statistics about them into a database.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --db|-d <db name>
+
+Name of database (C<namespace>) to write too.
+
+Optional if defined in the F<.nebdiskdrc> file or the appropriate environment
+variable is set.
+
+=item * --user|-u <db username>
+
+Username to authenticate with.
+
+Optional if defined in the F<.nebdiskdrc> file or the appropriate environment
+variable is set.
+
+=item * --pass|-p <db password>
+
+Password to authenticate with.
+
+Optional if defined in the F<.nebdiskdrc> file or the appropriate environment
+variable is set.
+
+=item * --debug
+
+This flag prevents the program from "daemonizing" so output can be sent to
+C<stdout>/C<stderr> and a debugger used on the running process.
+
+=item * --pidfile <filename>
+
+Filename to log the running daemon's PID too.
+
+Optional, if this option is omitted and no value is defined in the
+F<.nebdiskrc> then no pidfile is created.
+
+=item * --stop|-s
+
+Uses the pidfile to kill an already running daemon.
+
+=item * --restart|-r
+
+Uses the pidfile to kill an already running daemon and then starts a new
+instance.
+
+=item * --verbose|-r
+
+Turns on informational/debugging messages to be sent to the
+C<stderr>/C<stdout>.  This option is probably not very usefully unless combined
+with C<--debug>.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_DB>
+
+Equivalent to --db|-d
+
+=item * C<NEB_USER>
+
+Equivalent to --user|-u
+
+=item * C<NEB_PASS>
+
+Equivalent to --pass|-p 
+
+=back
+
+=head1 RCFILE
+
+This program will attempt to read an C<rcfile> from F<$HOME/.nebdiskrc> and if
+found will use it's values to override program defaults.  Please not that the
+precedence for values is: command line, rcfile, environment variables.
+
+The format of the C<rcfile> is in L<YAML> format and uses the following values:
+
+    ---
+    db: nebulous
+    dbpass: '@neb@'
+    dbuser: nebulous
+    mounts:
+      - /mnt
+      - /tmp
+      - /usr
+    pidfile: /var/tmp/nebdiskd
+    poll_interval: 5
+
+The values C<db>, C<dbpass>, C<dbuser>, and C<pidfile> have the same semantics
+as their command line and/or environment variable equivalents.
+
+=over 4
+
+=item * C<mounts>
+
+A list of "paths" to C<stat(2)> before mounted volumes are polled.  The propose
+of this option is to attempt to keep "automounted" volumes mounted while this
+program is running.
+
+This value may be omitted or left blank.
+
+=item * C<poll_interval>
+
+The number of seconds to wait between checks for mounted volumes.
+
+This value may be omitted or left blank.  The default value is C<60>s.
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<YAML>, L<neb-addvol>, L<neb-df>, L<neb-initdb>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/c_api.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/c_api.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/c_api.h	(revision 22322)
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2004 Joshua Hoblitt
+ *
+ * $Id: c_api.h,v 1.1.1.1 2004-12-22 02:16:23 jhoblitt Exp $
+ */
+
+// should there be an argument for "suggested" storage location?
+int add( const char *ext_id, const char *data, int size )
+
+int delete( const char *ext_id )
+
+// should there be an argument for "suggested" storage location?
+int update( const char *ext_id, const char *data, int size )
+
+// should there be an argument for "suggested" storage location?
+int move( const char *old_ext_id, const char *new_ext_id )
+
+// open returns a fd 
+int open( const char *ext_id )
+
+int close( const char *ext_id )
+
+// maybe open and close are a bad idea, perhaps...
+int read( const char *ext_id, char *data )
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/database_setup.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/database_setup.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/database_setup.txt	(revision 22322)
@@ -0,0 +1,59 @@
+# Copyright (C) 2004  Joshua Hoblitt
+#
+# $Id: database_setup.txt,v 1.9 2008-06-20 03:34:28 jhoblitt Exp $
+
+### Database Setup
+
+### MySQL 4.1
+
+DROP DATABASE IF EXISTS nebulous;
+CREATE DATABASE nebulous;
+GRANT ALL PRIVILEGES ON nebulous.* TO 'nebulous'@'ipp019'
+    IDENTIFIED BY '@neb@' WITH GRANT OPTION;
+FLUSH PRIVILEGES;
+USE nebulous;
+
+### Initialization
+
+# localhost
+
+$ mkdir /tmp/po01 /tmp/po02
+INSERT INTO volume VALUES (1,'po01','file:/tmp/po01');
+INSERT INTO volume VALUES (1,'po02','file:/tmp/po02');
+
+# NFS
+
+INSERT INTO volume VALUES (1,'po01','file:/netdisks/po01');
+INSERT INTO volume VALUES (2,'po02','file:/netdisks/po02');
+INSERT INTO volume VALUES (3,'po03','file:/netdisks/po03');
+INSERT INTO volume VALUES (4,'po04','file:/netdisks/po04');
+INSERT INTO volume VALUES (5,'po05','file:/netdisks/po05');
+INSERT INTO volume VALUES (6,'po06','file:/netdisks/po06');
+INSERT INTO volume VALUES (7,'po07','file:/netdisks/po07');
+INSERT INTO volume VALUES (8,'po08','file:/netdisks/po08');
+
+INSERT INTO class VALUES (1,3,0,'raw image data');
+
+### Example queries
+
+LOAD DATA INFILE '/home/moa/jhoblitt/tmp/storage_objects.txt' INTO TABLE storage_object; 
+LOAD DATA INFILE '/home/moa/jhoblitt/tmp/instances.txt' INTO TABLE instance; 
+
+SELECT * FROM storage_object LEFT JOIN instance ON storage_object.so_id=instance.so_id WHERE storage_object.ext_id = "foobarbaz1.fits";
+SELECT * FROM storage_object LEFT JOIN instance USING (so_id) WHERE storage_object.ext_id = "foobarbaz1.fits";
+
+SET AUTOCOMMIT = 0;
+BEGIN;
+UPDATE instance SET read_lock=read_lock+1 WHERE ins_id=1;
+INSERT INTO lock_record values (1, 'read', NULL);
+COMMIT;
+
+SET AUTOCOMMIT = 0;
+BEGIN;
+INSERT INTO storage_object values (NULL, 'myfooedbar.fits', 'mr. wizard made this', NULL);
+INSERT INTO instance values (NULL, LAST_INSERT_ID(), 'file://tmp/bat/man/12345a', 'da39a3ee5e6b4b0d3255bfef95601890afd80709',0, NULL, NULL);
+COMMIT;
+
+IF select write_lock from instance WHERE id=1 THEN
+ELSE
+ENDIF
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/design.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/design.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/design.txt	(revision 22322)
@@ -0,0 +1,283 @@
+# Copyright (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: design.txt,v 1.6 2005-11-05 01:13:35 jhoblitt Exp $
+
+Abstract
+--
+
+Nebulous is a user-space distributed object (file) storage system.  It is much
+more of a database of objects locations then a file system in the traditional
+sense.  Nebulous is considered distributed because of it's data storage model,
+where objects are stored across a number of storage volumes and may have one
+or more redundant copies.  While IPC is done in the traditional client/server
+model with a single[1] centralized server containing all storage object
+meta-data.  This system was designed, unlike most distributed storage system,
+specifically so that clients have the option of hosting one or more storage
+volumes and that data may be stored and accessed locally on a node.  This
+allows network I/O to be dramatically reduced if the end user does simple data
+locality optimizations.  Although, if you so choose, clients and storage
+volumes can reside on independent hardware.  Above all else, Nebulous is
+designed to make efficient use of both disk and network bandwidth.
+
+Data model
+--
+
+In Nebulous terminology a file is refereed to as a a resource.  Each resource
+is represented inside the Nebulous Server as a "Storage Object".  There is one
+Storage Object per resource (a file) that is being managed by the Nebulous
+Server.  A Storage Object maps an External Identifier (eg. filename) to a
+Storage ID.  In addition, creation time and comments may be stored in a
+Storage Object.  Storage Objects /do not/ contain the storage location of a
+resource as we need to allow for N copies of a resource to be stored.  The
+actual storage location of a resource is held in a Storage Object Instance,
+which maps to a Storage ID in a many-to-one relationship.
+
+A Storage Object Instance, or just Instance, represents a single copy of a
+resource being managed by the Nebulous Server. An Instance contains an
+Instance ID, the parent Storage ID, the location of the resource as a URI, and
+other assorted meta-data.
+
+Storage IDs are a signed 64-bit integer.  Negative values are considered
+reserved for future use.  Allocation should be incremental and, as we are
+unlikely to exceed 2^63 Storage Objects in the life time of the project, never
+reused. Instance IDs follow the same scheme as Storage IDs but exists in a
+unique namespace.
+
+Database record relationships
+--
+* All Storage Objects must have at least once associated Instance.
+
+* All Instances must be associated with a Storage Object.
+
+* All Instances must be associated with a valid URI (file exists).
+
+* All Storage Objects with locks set must have the correct number of
+corresponding Lock Records.
+
+* All Lock Records must be associated with a Storage Object.
+
+Data transport
+--
+XXX
+
+Data Storage
+--
+XXX
+
+
+Components
+--
+
+There are 4 principle components required for a working Nebulous system;
+server, client, data transport, and data storage.  The Nebulous software
+distribution provides only the server and the client software.  Data
+transport[2] and Data storage are left to 3rd party software packages.  As
+those functionalities are typically included with most operating systems.
+Nebulous build upon and benefits from advances made in those technologies.
+
+Server
+--
+
+The server is responsible for keeping track of storage objects, all instances
+of that object, and enforcing locking semantics.  Extensive logging and
+tracing support is provided for debug and to allow for statics generation and
+possible X<hotspot> optimization.
+
+Nebulous uses a centralized server model.  This model was choosen because it
+allows efficient X<pattern matching> of storage object names.  The current
+'best' technique for a distributed metadata store is with distributed hash
+tables.  Unfortunately, no widely available DHT implementation allows efficient
+I<pattern matching> of key names.
+
+
+[1] Multiple Nebulous servers should be possible via database replication.
+[2] A future version of Nebulous may include it's own data transport layer
+based on the WEB DAV protocol.
+
+Client <-> Server IPC
+--
+
+The Nebulous Server and clients communicate via the SOAP protocol.  Since SOAP
+is platform independent this allows native Nebulous clients to be written in a
+variety of languages.  With in the Nebulous Server itself the IPC mechanism is
+abstracted to allow ether multi-protocol support or complete replacement of
+SOAP by another protocol.
+
+XXX SOAP namespace
+
+
+Storage volume state tracking.
+
+Nebulous itself does not attempt to determine the state of storage volumes.
+Instead, it relies on some 3rd party to determine both the state
+(available/not) and capacity (total size + % utilized).  This information is
+then feed into Nebulous via either the administrative API or CLI management
+tools.
+
+XXX Storage Location determination
+
+
+Locking semantics
+--
+Nebulous uses 'advisory locking' where clients are excepted to check for the
+existence of locks but this policy is not enforced.  Locks are set on a
+Storage Object and apply to all Instances of that object.
+
+Two types of locks can be set; 'read' locks and 'write' locks.  Read locks are
+implemented as a semaphore while write locks use an exclusive mutex.
+
+    - Read lock
+In order to obtain a Read Lock on a Storage Object the Write Lock mutex must
+be in the state of 'free'.  When a Read Lock is acquire the Read semaphore is
+incremented by one.  When a Read Lock is released the Read semaphore is
+decremented by one.  This must happen atomically with the
+creation/destruction of a Lock Record.
+
+    - Write lock
+In order to obtain a Write lock on a Storage Object the Write Lock mutex must
+be in the state of 'free' and the Read lock semaphore must have a value of
+zero.  When a Write Lock is acquired the Write mutex is set to 'write'.  When a
+Write Lock is released the Write mutex is set to to 'free'.  This must happen
+atomically with the creation/destruction of a Lock Record.  As an extension, a
+sate of 'wait' may be implemented such that clients waiting on Read Locks to
+be released may prevent any additional Read Locks from being acquired.
+
+XXX Lock Records are currently unimplemented
+    - Lock Records A Lock Record is used to double check the indicated lock
+      state of a Storage Object and can be used to identify logical system
+errors.  A Lock Record contains the Storage ID of the object and the type of
+lock set.  eg, enum( 'read', 'write').
+
+    - Client best practices
+When attempting to acquire a lock a client should either "spin lock" with a
+reasonable pause between lock attempts or fail completely.  Aggressive "spin
+locking" is considered antisocial and may eventually require the Nebulous
+Server to identify and ignore such clients.
+
+
+House keeping
+--
+    - Lock sweeping
+In the event that a Storage Object operation fails to complete successfully
+stale locks will have to be identified and removed from the IPP Pixel Data
+Server Database.  This should be done periodically by comparing the entries
+in the Lock table to the list of active nodes maintained by the IPP
+Controller.  It should also happen as soon as possible after a node goes
+offline (triggered by the IPP Controller marking a node as offline?).  A sweep
+must be /completed/ before an offline node can be marked on-line.
+
+Once a node is determined to be offline all entries in the Lock table set by
+that node should be identified.   The locks on the Storage Object
+Instances pointed to by those entries should then be rolled back and Lock
+Record entries themselves must be removed from the lock table.
+
+    - Consistency sweeping
+Periodically the IPP Pixel Data Server meta-data and Storage Object will need
+to be checked for sanity.  This would be similar to running fsck on a modern
+filesystem.  Consistency sweeping should include Lock sweeping and should be
+considered a super-set.
+
+
+Server Operations
+--
+    - setup
+    - create_object
+    - rename_object
+        XXX unimplemented
+    - link_object
+        XXX unimplemented
+    - replicate object
+    - lock_object
+    - unlock_object
+    - find_objects
+        XXX unimplemented
+    - find_instances
+    - delete_instance
+    - fission_object
+        XXX unimplemented
+    - fuse_objects
+        XXX unimplemented
+    - stat_object
+    - stat_instance
+        XXX unimplemented
+
+Client Operations
+--
+    - create
+Creates and opens new Storage Object
+
+    - replicate
+Adds an Instance to a Storage Object
+
+    - cull
+Removes an Instance from a Storage Object
+
+This operation can not remove the last Instance of a Storage Object.
+
+    - lock
+Trys to acquire a lock on a storage object
+
+This operation times out after a default interval of 10s.
+
+    - unlock
+Trys to release a lock on a storage object
+    
+    - find_instances
+Find all instances of a storage object
+
+Only Instances on active storage volumes are found.
+
+    - find
+Find any instance of a storage object
+
+Only Instances on active storage volumes are found.
+
+    - open
+Open a storage object for read or write
+
+Opening a storage object as 'write' will remove all but one Instance to
+prevent Instances from becoming inconsistent.
+
+    - delete
+Delete a storage object and all of it's instances
+
+Removes a storage object and all of it's instances by sequentially deleting each
+Instance.  When a Storage Object has no more associated instances it is
+automatically removed by the Nebulous server.
+
+    - copy
+Copy a storage object
+
+The new storage object will be created with only one Instance.  Most properties
+of the source Storage Object will be preserved.
+
+    - move
+Rename a storage object
+
+Currently this operation will remove all but one instance of the storage
+object and may change it's storage location.
+
+    - delete_instance
+    
+Remove a storage object instance
+
+Removing the last Instance of a Storage Object will destroy the Storage
+Object.
+
+    - stat
+View the properties of a storage object
+
+
+    - import
+        XXX unimplemented
+Import a file into Nebulous
+
+Creates a new Storage Object with the specified External Identifier.  The
+source file will then be "copied" into the Nebulous system leaving the original
+file unmodified.
+
+    - fission
+        XXX unimplemented
+Create a new Storage Object from an existing one by either splitting off an
+Instance and re-parenting it or by cloning an existing Instance and associating
+it with the new Storage Object.
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/requirements.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/requirements.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/requirements.txt	(revision 22322)
@@ -0,0 +1,28 @@
+# Copyright (C) 2004  Joshua Hoblitt
+#
+# $Id: requirements.txt,v 1.2 2005-09-14 00:46:01 jhoblitt Exp $
+
+- store all image data aquired during the first two years of PS1's
+  operations.  Approximately 10^8 files (OTA level).
+
+- injest new data from the summit at the same rate it is produced when
+  operating at a cadence of 40s between integrations.  Approximately 400Mb/s
+  and 64 files/integration.
+
+- support phase 1 & 2 processing. x files per t_min
+
+- support phase 3 & 4 processing. x files per t_min
+
+- support accessing small blocks of a file (ie., seek)
+
+- tolerate partal system failures without data loss
+
+- execeedly efficent use of both disk and network I/O
+
+what are the science drivers for these 3?
+--
+- data corruption is guarenteed to be detectable.
+
+- relibably store xx.x% of files 
+
+- xx.x% of stored images must be retreivable in xs.
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/setup.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/setup.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/setup.txt	(revision 22322)
@@ -0,0 +1,373 @@
+=pod
+
+=head1 Installing a Nebulous Server
+
+=head2 Create a UNIX group for Nebulous
+
+All Nebulous user's need to be in the same UNIX group as the Nebulous server.
+
+    groupadd -g 877 nebulous
+
+This is a silly hack to generate a gid.
+
+    perl -le 'map { $gid += ord } split //, shift; print $gid' nebulous
+
+=head2 Add the users that you want to be able to access Nebulous to that group
+
+    usermod -G nebulous foouser
+
+=head2 Setup directory permissions for the nebulous group
+
+As root:
+
+    mkdir /data/ipp000.0/nebulous
+    mkdir /data/ipp000.1/nebulous
+    mkdir /data/ipp002.0/nebulous
+    mkdir /data/ipp003.0/nebulous
+
+    # note that this is just an example, this won't work over NFS with
+    # root_squash enablded
+    chown nobody:nebulous /data/ipp00?.?/nebulous
+
+    chmod 0770 /data/ipp00?.?/nebulous
+    ls -lad /data/ipp00?.?/nebulous
+
+    mkdir /data/`hostname`.0/nebulous
+    chown nobody:nebulous /data/`hostname`.0/nebulous
+    chmod 0770 /data/`hostname`.0/nebulous
+
+=head2 Build and install the Nebulous Perl modules (see: README)
+
+=head2 Create and initialize the database
+
+    mysql -u root mysql -p
+    (enter your MySQL root password)
+
+    DROP DATABASE IF EXISTS nebulous;
+    CREATE DATABASE nebulous;
+    GRANT ALL PRIVILEGES ON nebulous.* TO 'nebulous'@'localhost'
+        IDENTIFIED BY '@neb@';
+    FLUSH PRIVILEGES;
+    exit
+
+    export NEB_DB=nebulous
+    export NEB_USER=nebulous
+    export NEB_PASS=@neb@
+
+    neb-initdb
+    neb-addvol --name ipp000.0 --uri file:///data/ipp000.0/nebulous
+    neb-addvol --name ipp000.1 --uri file:///data/ipp000.1/nebulous
+    neb-addvol --name ipp002.0 --uri file:///data/ipp002.0/nebulous
+    neb-addvol --name ipp003.0 --uri file:///data/ipp003.0/nebulous
+
+#    perl -e 'for (1..24) { $i = sprintf "po%02d", $_ ; system "neb-addvol --name $i --uri file:/$i/nebulous" }'
+    .
+    .
+
+If you get an error similar to this while initializing the database it means
+that the creation of the Nebulous's stored procedures failed:
+
+    ERROR 1146 (42S02): Table 'mysql.proc' doesn't exist
+
+It probably means that your instance of MySQL has been upgraded for an older
+version that did not support stored precedures.  You can fix this by running
+the C<mysql_fix_privilege_tables> script that you should have received with
+MySQL.
+
+In order for the test suite to pass the "test" account needs have the
+appropriate permissions to create and modify stored procedures.
+
+    update user set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where user = 'test';
+    update db set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where db = 'test';
+
+    flush privileges;
+
+Please see this webpage for further details:
+
+http://dev.mysql.com/doc/refman/5.0/en/stored-procedure-privileges.html
+
+
+=head2 Setup nebdiskd daemon
+
+Create the .netdiskrc file in the home directory of the user that nebdiskd
+will run as.  This does not need to be/should not be the root user.  Merely a
+user with permissions to see all of the approparite volumes.
+
+Example F<.netdiskrc>:
+
+    db: nebulous
+    dbpass: '@neb@'
+    dbuser: nebulous
+    mounts:
+      - /data/ipp000.0
+      - /data/ipp000.1
+      - /data/ipp002.0
+      - /data/ipp003.0
+    pidfile: /var/tmp/nebdiskd
+    poll_interval: 5
+
+Start the daemon by running the command C<nebdiskd>.  Use this command to
+verify that the daemon is working correclty.
+
+    neb-volstat
+
+nebdiskd should always been running when nebulous is use.  This command is an
+example of how to startup the daemon running as another user.  This could be
+use to start the daemon from the root account while the system init scripts
+are running.
+
+    sudo -H -u jhoblitt nebdiskd
+
+See the nebdiskd POD/man page for further details
+
+=head2 Install Apache2 with mod_perl
+
+=head3 Minimum software requirements:
+
+Nebulous will work with both Apache 1.3 & 2.x.  However, this guide will only
+cover configuraton with Apache 2.x and mod_perl 2.x.
+
+=over 4
+
+=item * Apache2 >= 2.0.54
+
+=item * mod_perl >= 2.0.0
+
+=item * Apache::DBI >= '0.97' [*]
+
+=back
+
+[*] required for connect_on_init() to work under mod_perl >= 2.0.1
+
+=head3 Apache 1.x 
+
+=item * building mod_perl-1.29 from source:
+
+    perl Makefile.PL USE_APXS=1 INSTALLDIRS=vendor WITH_APXS=/usr/sbin/apxs EVERYTHING=1 PERL_DEBUG=1
+
+    #MP_TRACE = 1 MP_DEBUG = 1 MP_USE_DSO = 1 MP_INST_APACHE2 = 1 MP_APXS = /usr/sbin/apxs2
+
+=head3 Apache 2.x 
+
+=head4 removing Apache on RHEl3
+
+On RHEL3 I had to remove these packages in order to fully remove MySQL.
+
+As C<root>:
+
+    rpm -e \
+    httpd-2.0.46-46.2.ent \
+    httpd-devel-2.0.46-46.2.ent \
+    redhat-config-httpd-1.1.0-4.30.2 \
+    mod_perl-1.99_09-10.ent \
+    mod_authz_ldap-0.22-5 \
+    mod_auth_pgsql-2.0.1-4.ent \
+    mod_python-3.0.3-5.ent \
+    mod_auth_mysql-20030510-2.ent \
+    mod_ssl-2.0.46-46.2.ent \
+    webalizer-2.01_10-15.ent \
+    mod_perl-1.99_09-10.ent \
+    mod_auth_pgsql-2.0.1-4.ent \
+    mod_python-3.0.3-5.ent \
+    mod_auth_mysql-20030510-2.ent \
+    mod_ssl-2.0.46-46.2.ent \
+    squirrelmail-1.4.3a-11.EL3 \
+    php-4.3.2-25.ent \
+    php-imap-4.3.2-25.ent \
+    php-ldap-4.3.2-25.ent \
+    php-mysql-4.3.2-25.ent \
+    php-odbc-4.3.2-25.ent \
+    php-pgsql-4.3.2-25.ent 
+
+    rm -rf /etc/httpd
+
+=head4 installing Apache2 from source
+
+    tar -jxvf httpd-2.0.54.tar.bz2
+    cd httpd-2.0.54
+    ./configure \
+    --disable-suexec \
+    --with-perl=/usr/bin/perl \
+    --with-port=80 \
+    --with-program-name=apache2 \
+    --with-devrandom=/dev/urandom \
+    --prefix           /usr \
+    --exec_prefix      /usr \
+    --bindir           /usr/bin \
+    --sbindir          /usr/sbin \
+    --libdir           /usr/lib \
+    --libexecdir       /usr/lib/apache2/modules \
+    --mandir           /usr/share/man \
+    --infodir          /usr/share/info \
+    --includedir       /usr/include/apache2 \
+    --datadir          /var/www/localhost \
+    --sysconfdir       /etc/apache2/conf \
+    --localstatedir    /var
+    make
+    su
+    make install
+    exit
+    cd ..
+
+=head4 installing mod_perl 2.x
+
+    tar -zxvf mod_perl-2.0.1.tar.gz
+    cd mod_perl-2.0.1
+    perl Makefile.PL
+    (apxs = /usr/sbin/apxs)
+    make
+    su
+    make install
+    echo "LoadModule perl_module /usr/lib/apache2/modules/mod_perl.so" >> /etc/apache2/conf/apache2.conf
+    exit
+
+    apachectl configtest
+    Syntax OK
+
+=head4 installing and configuring Nebulous
+
+    tar -zvxf Nebulous-0.01.tar.gz
+    cd Nebulous-0.01
+    perl Build.PL -axps /usr/sbin/apxs
+    ./Build
+    ./Build test
+    su
+    ./Build install
+
+
+    vi /etc/apache2/conf/startup.pl
+    (change Group to "Group nebulous")
+
+    cp fooconf /etc/apache2/conf/startup.pl
+    echo "PerlPostConfigRequire /etc/apache2/conf/startup.pl" >> /etc/apache2/conf/apache2.conf
+
+
+=head3 configurating Apache to act as a Nebulous/SOAP server
+
+=item * add the "nebulous" group to /etc/group
+=item * configure Apache to run as the nebulous group. 
+
+    Group nebulous
+
+Note that in theory the C<perchild> MPM can run vhosts as differenet
+users/groups but this module is currently non-funcitonal.
+
+L<http://httpd.apache.org/docs/trunk/mod/perchild.html>
+
+=item * configure a path for Neublous
+
+Make sure mod_perl is being loaded into Apache.  In order to do this on
+gentoo, you must edit /etc/config.d/apache2 and add C<-DPERL> to
+C<APACHE2_OTPS>.
+
+    cat <<END >> /etc/apache2/conf/apache2.conf
+    <Location /nebulous>
+        SetHandler perl-script
+        PerlResponseHandler Apache2::SOAP
+        PerlSetVar dispatch_to "PerlHandler Nebulous::Server::SOAP"
+        PerlSetVar options "compress_threshold => 10000"
+    </Location>
+    END
+
+    apachectl configtest
+    Syntax OK
+
+=item * initialize Nebulous database connections from C<startup.pl>
+
+    my $dsn         = 'DBI:mysql:database=nebulous:host=localhost';
+    my $dbuser      = 'nebulous';
+    my $dbpasswd    = '@neb@';
+
+    Apache::DBI->connect_on_init( $dsn, $dbuser, $dbpasswd );
+    Nebulous::Server::SOAP->new_on_init(
+        dsn         => $dsn,
+        dbuser      => $dbuser,
+        dbpasswd    => $dbpasswd,
+        log_level   => 'all',
+    );
+
+# out of date from here down...
+
+#
+# CGI interface
+#
+
+ScriptAlias /nebulous /usr/local/apache/perl/imageserver.pl
+
+<Directory "/usr/local/apache/perl">
+        AllowOverride None
+        Options None
+        Order allow,deny
+        Allow from all
+</Directory>
+
+PerlModule Apache::PerlRun
+<Location /perl>
+        SetHandler perl-script
+        PerlHandler Apache::PerlRun
+        Options ExecCGI
+        allow from all
+        PerlSendHeader On
+</Location>
+
+#
+# Nebulous::Apache interface
+#
+
+PerlModule Apache::DBI
+PerlModule Nebulous::Apache
+
+<Location /nebulous>
+        SetHandler perl-script
+        PerlHandler Nebulous::Apache
+</Location>
+
+- setup the server
+
+As a CGI, will with either Apache 1.3.x or 2.0.x
+
+cp scripts/imagesever.pl /var/www/localhost/cgi-bin
+chmod 755 /var/www/localhost/cgi-bin/imageserver.pl
+touch /tmp/imageserver.log
+chmod 1777 /tmp/imageserver.log
+
+=head2 Testing the Nebulous Server
+
+You can test your configuration with the C<telnet> utility.
+
+    $ telnet localhost 80
+    Trying 127.0.0.1...
+    Connected to localhost.localdomain (127.0.0.1).
+    Escape character is '^]'.
+    GET /nebulous HTTP/1.0
+
+    HTTP/1.1 500 Internal Server Error
+    Date: Wed, 31 Aug 2005 01:10:47 GMT
+    Server: Apache/2.0.54 (Unix) mod_perl/2.0.1 Perl/v5.8.6
+    Content-Length: 645
+    Connection: close
+    Content-Type: text/html; charset=iso-8859-1
+    .
+    .
+
+    Connection closed by foreign host.
+
+If you get an HTTP 5xx code check your apache error_log
+
+
+=head2 MySQL Configuration/tuning
+
+in /etc/mysql/my.cnf
+under [mysqld]
+increase the number of alloweed connections, e.g.
+max_connections = 200
+
+Nebulous makes heavy use of MySQL's C<innodb> table type.  Try configuring
+MySQL with these parameters:
+
+    innodb_log_files_in_group = 2
+    innodb_buffer_pool_size = as big as possible (512M+)
+    innodb_log_buffer_size= 16M
+    innodb_log_file_size=10M
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/tmp.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/tmp.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/docs/tmp.txt	(revision 22322)
@@ -0,0 +1,44 @@
+client
+--
+new
+
+new_file
+                new_file
+                open_file
+
+find_file
+    find_instance
+
+open_file
+    find_file
+
+delete_file
+                delete_file
+    nuke
+
+copy_file
+    open_file
+    new_file
+
+move_file
+    copy_file
+    delete_file
+
+replicate_file
+    open_file
+                replicate_file
+
+cull_file
+    find_instance
+    delete_instance
+
+delete_instance
+    nuke
+                delete_instance
+
+find_instance
+                find_instance
+
+# missing
+file_stat
+lock ?
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/Makefile	(revision 22322)
@@ -0,0 +1,5 @@
+NEB_CFLAGS=`pkg-config nebclient --cflags`
+NEB_LIBS=`pkg-config nebclient --libs`
+
+nebexample: nebexample.c
+	$(CC) $(NEB_CFLAGS) $(CFLAGS) $(NEB_LIBS) $< -o $@
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/nebexample.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/nebexample.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/nebexample.c	(revision 22322)
@@ -0,0 +1,45 @@
+// gcc `pkg-config nebclient --cflags --libs` example.c -o example
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <nebclient.h>
+
+#define NEB_SERVER  "http://podb1.ifa.hawaii.edu:80/nebulous"
+#define NEB_KEY     "foobarbaz"
+
+int main (int argc, char **argv) {
+    nebServer       *server = NULL;
+    char            *key = NEB_KEY;
+
+    server = nebServerAlloc(NEB_SERVER);
+    if (!server) {
+        printf("nebServerAlloc() failed\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // make sure there isn't already a file named "foobarbaz" so this example
+    // doesn't cause an error
+    nebDelete(server, key);
+
+    int fh = nebCreate(server, key, 0, NULL, NULL, NULL);
+    if (fh < 0) {
+        printf( "nebCreate() failed: %s\n", nebErr(server));
+        exit(EXIT_FAILURE);
+    }
+
+    // fh is a file descriptor
+    close(fh);
+
+    if (!nebReplicate(server, key, NULL, NULL)) {
+        printf( "nebReplicate() failed: %s\n", nebErr(server));
+        exit(EXIT_FAILURE);
+    }
+
+    if (!nebDelete(server, key)) {
+        printf( "nebDelete() failed: %s\n", nebErr(server));
+        exit(EXIT_FAILURE);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/nebexample.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/nebexample.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/nebexample.pl	(revision 22322)
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Nebulous::Client;
+
+my $neb = Nebulous::Client->new(
+    proxy   => 'http://podb1.ifa.hawaii.edu:80/nebulous'
+);
+
+my $key = shift || 'foobarbaz';
+
+# make sure there isn't already a file named "foobarbaz" so this example
+# doesn't cause an error
+$neb->delete($key);
+
+my $fh = $neb->create($key);
+die "can't create file $key " unless $fh;
+close($fh);
+
+if (!$neb->replicate($key)) {
+    die "can't replicate object $key";
+}
+
+if (!$neb->delete($key)) {
+    die "can't delete object $key";
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/uri_test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/uri_test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/examples/uri_test.pl	(revision 22322)
@@ -0,0 +1,18 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use URI;
+
+my $u1 = URI->new("neb://www.perl.com/bar");
+
+print $u1->scheme, "\n";
+print $u1->opaque, "\n";
+print $u1->path, "\n";
+
+my $u2 = URI->new("neb:/www.perl.com");
+print $u2->scheme, "\n";
+print $u2->opaque, "\n";
+print $u2->path, "\n";
+
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Keys.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Keys.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Keys.pm	(revision 22322)
@@ -0,0 +1,267 @@
+# Copyright (c) 2004  Joshua Hoblitt
+#
+# $Id: Keys.pm,v 1.3 2008-09-09 02:18:47 jhoblitt Exp $
+
+package Nebulous::Keys;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = '0.02';
+
+use base qw( Exporter Class::Accessor::Fast );
+
+use File::Spec;
+use URI::file;
+use URI;
+use overload '""' => \&_stringify_key;
+
+our @EXPORT_OK = qw(
+    parse_neb_key
+    parse_neb_volume
+);
+
+__PACKAGE__->mk_ro_accessors(qw( path volume soft_volume ));
+
+sub parse_neb_key
+{
+    my ($key, $volume) = @_;
+    return unless defined $key;
+
+    # white space is not allowed
+    if ($key =~ qr/\s+/) {
+        die "keys and URIs may not contain whitespace";
+    }
+
+    my $uri = URI->new($key);
+    my $scheme = $uri->scheme;
+    my $path = $uri->opaque;
+    
+    my $volume_name;
+    my $soft_volume;
+    # if this is a valid uri 
+    if (defined $scheme) {
+        # if so, does it use the neb scheme?
+        die "URI does not use the 'neb' scheme"
+            unless $scheme eq 'neb'; 
+
+        # neb:path (not allowed)
+        # neb://<volume name>/...
+        # neb:/path... (leading '/' is stripped)
+        # neb:///path... (same as neb:/path)
+
+        # volume specifier must be at least one character
+        # strip off volume component if it exists
+        $path =~ s|^//([^/]+)||;
+        
+        # ignore key supplied volume name if one is passed as a parameter
+        $volume ||= $1;
+        my $volume_info = parse_neb_volume($volume);
+
+        # magically eat the "any" volume
+        if (defined $volume_info->{volume} and $volume_info->{volume} ne 'any') {
+            $volume_name = $volume_info->{volume};
+            $soft_volume = $volume_info->{soft_volume};
+        }
+
+        # require a leading slash if there is no volume name
+        if ((not defined $volume_name) and (not $path =~ m|^/|)) {
+            die "neb URI scheme requires a leading slash, eg. neb:/";
+        }
+    } else {
+        my $volume_info = parse_neb_volume($volume);
+
+        # magically eat the "any" volume
+        if (defined $volume_info->{volume} and $volume_info->{volume} ne 'any') {
+            $volume_name = $volume_info->{volume};
+            $soft_volume = $volume_info->{soft_volume};
+        }
+    }
+
+    # strip leading slashes
+    $path =~ s|^/+||;
+
+    # remove multiple /'s and trailing slashes
+    $path = File::Spec->canonpath($path);
+
+    return __PACKAGE__->new({
+        volume      => $volume_name,
+        soft_volume => $soft_volume,
+        path        => $path,
+    });
+}
+
+sub parse_neb_volume
+{
+    my $volume = shift;
+    return unless defined $volume;
+
+    my $soft_volume;
+    # check to see if there is a tilde and remove it if found
+    unless (defined $volume and $volume =~ s/^~//) {
+        $soft_volume = 1;
+    }
+
+    return({ volume => $volume, soft_volume => $soft_volume });
+}
+
+sub _stringify_key
+{
+    my $self = shift;
+
+    my $path        = $self->path;
+    my $volume      = $self->volume || "";
+    my $soft_volume = $self->soft_volume ? '~' : "";
+
+    return "neb://${soft_volume}${volume}/$path";
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Nebulous::Keys - Nebulous Keys Explained
+
+=head1 DESCRIPTION
+
+The Nebulous system interprets it's storage object keys in a I<slightly>
+magical manner.  It supports both plain vanilla C<key strings> and keys in the
+form of an L<URI>.  In the L<URI> form a specific storage volume can be implied
+(as a request, not a requirement).  Both key forms are subject to mangling such
+that I<IF> the key I<looks> like a C<POSIX> semantics file path, it is
+canocalized.
+
+=head1 RESERVED SYNTAX
+
+This particular syntax is disallowed and is reserved for future use.
+
+    <neb:<phrase>/<path>
+
+=head1 EXAMPLES
+
+=head2 Key Strings
+
+    key:        foo/bar/baz/quix
+    mangled to: foo/bar/baz/quix
+    volume:     any
+
+    key:        /foo/bar/baz/quix
+    mangled to: foo/bar/baz/quix
+    volume:     any
+
+    key:        //foo/bar/baz/quix
+    mangled to: foo/bar/baz/quix
+    volume:     any
+
+    key:        ///foo/bar/baz/quix
+    mangled to: foo/bar/baz/quix
+    volume:     any
+
+    key:        foo////bar/baz/quix
+    mangled to: foo/bar/baz/quix
+    volume:     any
+
+    key:        foo/bar/baz/quix/
+    mangled to: foo/bar/baz/quix
+    volume:     any
+
+=head2 URI w/ volume name
+
+neb://<volume>/<path>
+
+    key:        neb://foo/bar/baz/quix
+    mangled to: bar/baz/quix
+    volume:     foo
+
+    key:        neb://foo//bar/baz/quix
+    mangled to: bar/baz/quix
+    volume:     foo
+
+    key:        neb://foo///bar/baz/quix
+    mangled to: bar/baz/quix
+    volume:     foo
+
+    key:        neb://foo/bar///baz/quix
+    mangled to: bar/baz/quix
+    volume:     foo
+
+    key:        neb://foo/bar/baz/quix/
+    mangled to: bar/baz/quix
+    volume:     foo
+
+=head2 URI w/o volume name
+
+neb:/<path>
+
+    key:        neb:/foo/bar/baz/quix
+    mangled to: foo/bar/baz/quix
+    volume:     any
+
+    key:        neb:///foo/bar/baz/quix
+    mangled to: foo/bar/baz/quix
+    volume:     any
+
+    key:        neb://///foo/bar/baz/quix
+    mangled to: foo/bar/baz/quix
+    volume:     any
+
+=head2 Forbidden Keys
+
+=head3 Key with whitespace
+
+    "/ foo/bar/baz/quix"
+    " /foo/bar/baz/quix"
+    "/foo/bar/baz/quix "
+
+=head3 URI with whitespace
+
+    "neb ://foo/bar/baz/quix"
+    "neb:// foo/bar/baz/quix"
+    " neb://foo/bar/baz/quix"
+    "neb://foo/bar/baz/quix "
+
+=head3 URI with out a volume requires a leading slash
+
+    neb:foo/bar/baz/quix
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the LICENSE file included with
+this module, or in the L<perlgpl> Pod as supplied with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<Nebulous::Client>, L<nebclient(3)>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server.pm	(revision 22322)
@@ -0,0 +1,1480 @@
+# Copyright (c) 2004-2008  Joshua Hoblitt
+#
+# $Id: Server.pm,v 1.87 2008-09-11 23:01:17 jhoblitt Exp $
+
+package Nebulous::Server;
+
+use strict;
+use warnings FATAL => qw( all );
+no warnings qw( uninitialized );
+
+our $VERSION = '0.15';
+
+use base qw( Class::Accessor::Fast );
+
+use DBI;
+use Digest::SHA1 qw( sha1_hex );
+use File::ExtAttr qw( setfattr );
+use File::Path;
+use File::Spec;
+use Log::Log4perl;
+use Nebulous::Server::Config;
+use Nebulous::Server::Log;
+use Nebulous::Server::SQL;
+use Nebulous::Keys qw( parse_neb_key parse_neb_volume );
+use Params::Validate qw( validate_pos SCALAR SCALARREF UNDEF );
+use URI::file;
+
+__PACKAGE__->mk_accessors(qw( log sql config ));
+
+use constant SUBPATH_DEPTH => 2;
+
+sub new {
+    my $class = shift;
+
+    # let Nebulous::Server::Config validate our params
+    my $config = Nebulous::Server::Config->init( @_ );
+
+    # log4perl is not avaliable until we call init()
+    Nebulous::Server::Log->init($config);
+    my $log = Log::Log4perl::get_logger( "Nebulous::Server" );
+
+    my $sql = Nebulous::Server::SQL->new;
+
+    $log->debug( "entered - @_" );
+
+    my $db;
+    eval {
+        $db = DBI->connect(
+            $config->dsn,
+            $config->dbuser,
+            $config->dbpasswd,
+            {
+                RaiseError => 1,
+                PrintError => 0,
+                AutoCommit => 0,
+            },
+        );
+
+        $db->do( $sql->set_transaction_model );
+
+        $db->commit;
+        $log->debug("commit");
+    };
+    if ( $@ ) {
+        $db->rollback if $db;
+        $log->debug("rollback");
+        $log->logdie( "database error: $@" );
+    }
+
+    $log->debug( "connected to database: ", sub { $db->data_sources; } );
+
+    my $self = bless {}, ref $class || $class;
+    $self->log($log);
+    $self->sql($sql);
+    $self->db($db);
+    $self->config($config);
+
+    $log->debug( "leaving" );
+    
+    return $self;
+}
+
+sub db {
+    my $self = shift;
+
+    if (@_) {
+        $self->{db} = $_[0];
+        return $self;
+    }
+
+    my $log     = $self->log;
+    my $sql     = $self->sql;
+    my $config  = $self->config;
+
+    # if the dbh is still alive, return it
+    if (defined $self->{db} and $self->{db}->ping) {
+        $log->debug("db handle is still alive");
+        return $self->{db};
+    }
+    # otherwise create a new connection
+    $log->debug("db handle is dead");
+
+    # if we're running under mod_perl & Apache::DBI is loaded we want to
+    # reconnect to the database everytime the dbh is requested.  The rational is
+    # that if we're running under mod_perl this is probably a log running
+    # processes and the database might have gone away on us.  Apache::DBI will
+    # take care of getting a valid dbh back.
+    if ($INC{'Apache/DBI.pm'} && $ENV{MOD_PERL}) {
+        my $db;
+        eval {
+            $db = DBI->connect(
+                $config->dsn,
+                $config->dbuser,
+                $config->dbpasswd,
+                {
+                    RaiseError => 1,
+                    PrintError => 0,
+                    AutoCommit => 0,
+                },
+            );
+
+            $db->do( $sql->set_transaction_model );
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ( $@ ) {
+            $db->rollback if $db;
+            $log->debug("rollback");
+            $log->logdie( "database error: $@" );
+        }
+
+        $self->{db} = $db;
+
+        return $db;
+    }
+
+    return $self->{db};
+}
+
+
+sub create_object
+{
+    my $self = shift;
+
+    my ($key, $vol_name) = validate_pos(@_,
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR|UNDEF,
+#            callbacks   => {
+#                # check that the volume requested is valid
+#                'is valid volume name' => sub {
+#                    return 1 if not defined $_[0];
+#                    $self->_is_valid_volume_name($_[0])
+#                },
+#            },
+            optional    => 1,
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug( "entered - @_" );
+
+    # vol_name overrides the key implied volume
+    $key = parse_neb_key($key, $vol_name);
+    $vol_name = $key->volume;
+
+    # the key's volume can't be validiated on input for this method so we have
+    # to check it after parsing the key
+    if (defined $vol_name
+        and not $self->_is_valid_volume_name($key->volume)) {
+        if ($key->soft_volume) {
+            $log->warn( "$vol_name is not a known volume name" );
+            $vol_name = undef;
+        } else {
+            die "$vol_name is not a valid volume name"
+        }
+    }
+        
+    my ($vol_id, $vol_host, $vol_path, $vol_xattr)
+        = $self->_get_storage_volume($vol_name, $key->soft_volume);
+
+    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
+#            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 ($@) {
+#        and not $key->soft_volume
+        $db->rollback;
+        $log->debug("rollback");
+        $log->logdie("error: $@");
+    }
+
+    $log->debug("leaving");
+
+    return "$uri";
+}
+
+sub rename_object
+{
+    my $self = shift;
+
+    my ($key, $newkey) = validate_pos(@_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+        # make sure the "newkey" doesn't already exist
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is not valid object key' => sub { not $self->_is_valid_object_key($_[0]) },
+            },
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug("entered - @_");
+
+    # ignore volumes
+    $key    = parse_neb_key($key);
+    $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: $@");
+    }
+
+    $log->debug("leaving");
+
+    return $newkey;
+}
+
+
+sub replicate_object
+{
+    my $self = shift;
+
+    my ($key, $vol_name) = validate_pos(@_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key'
+                    => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+        {
+            type        => SCALAR|UNDEF,
+#            callbacks   => {
+#                # check that the volume name requested is valid
+#                'is valid volume name' => sub {
+#                    return 1 if not defined $_[0];
+#                    $self->_is_valid_volume_name($_[0])
+#                },
+#            },
+            optional    => 1,
+        },
+    );
+
+    # if a volume name is explicity specified then we should make the
+    # replication onto that volume (even if there is alread an instance on that
+    # volume) if at all possible and throw an error if we can not.
+    # if a volume name IS NOT specified then we should make the replication
+    # onto any (hopefully the best) volume that DOES NOT already have an
+    # instance on it.  If all avilable volume already have an instance on them
+    # then we should throw an error 
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug("entered - @_");
+
+    # vol_name overrides the key implied volume
+    $key = parse_neb_key($key, $vol_name);
+    $vol_name = $key->volume;
+
+    if (defined $vol_name
+        and not $self->_is_valid_volume_name($key->volume)) {
+        if ($key->soft_volume) {
+            $log->warn( "$vol_name is not a known volume name" );
+            $vol_name = undef;
+        } else {
+            die "$vol_name is not a valid volume name"
+        }
+    }
+        
+    my ($vol_id, $vol_host, $vol_path, $vol_xattr);
+    if (defined $vol_name) {
+        ($vol_id, $vol_host, $vol_path, $vol_xattr)
+            = $self->_get_storage_volume($vol_name);
+    } else {
+        ($vol_id, $vol_host, $vol_path, $vol_xattr)
+            = $self->_get_replication_volume($key);
+    }
+
+    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 ) {
+                $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: $@");
+    }
+
+    $log->debug("leaving");
+
+    return "$uri";
+}
+
+sub lock_object
+{
+    my $self = shift;
+
+    my ( $key, $type ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is read or write' => sub { $_[0] =~ /^(?:read|write)$/ },
+            },
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug( "entered - @_" );
+
+    # ignore volume
+    $key = parse_neb_key($key);
+
+    my $so_id;
+    my $read_lock;
+    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 ) {
+                $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: $@");
+    }
+
+    $log->debug("leaving");
+
+    return 1;
+}
+
+sub unlock_object
+{
+    my $self = shift;
+
+    my ( $key, $type ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is read or write' => sub { $_[0] =~ /^(?:read|write)$/ },
+            },
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug( "entered - @_" );
+
+    # ignore volume
+    $key = parse_neb_key($key);
+
+    my $so_id;
+    my $read_lock;
+    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) {
+                $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: $@");
+    }
+
+    $log->debug( "leaving" );
+
+    return 1;
+}
+
+
+sub setxattr_object
+{
+    my $self = shift;
+
+    my ($key, $name, $value, $flags) = validate_pos(@_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is read or write' => sub { $_[0] =~ /^(?:create|replace)$/i },
+            },
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug("entered - @_");
+
+    # ignore volume
+    $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: $@");
+    }
+
+    $log->debug("leaving");
+
+    return 1;
+}
+
+
+sub getxattr_object
+{
+    my $self = shift;
+
+    my ($key, $name) = validate_pos(@_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+        {
+            type        => SCALAR,
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug("entered - @_");
+
+    # ignore volume
+    $key = parse_neb_key($key);
+
+    my $value;
+    eval {
+        my $query = $db->prepare_cached( $sql->get_object_xattr );
+        # ext_id, name
+        my $rows = $query->execute($key->path, $name);
+
+        # no rows returned means that the xattr does not exist
+        if ($rows == 0) {
+            $query->finish;
+            $log->logdie( "xattr $key:$name does not exist" );
+        }
+        # if we go more then one row bad something very bad has happened.
+        unless ($rows == 1) {
+            $query->finish;
+            $log->logdie( "affected row count is $rows instead of 1" );
+        }
+
+        my $row = $query->fetchrow_hashref;
+        # XXX: DBI bug? ->finish is needed here even though $query is going out
+        # of scope
+        $query->finish;
+        $value = $row->{ 'value' };
+    };
+    $log->logdie("database error: $@") if $@;
+
+    $log->debug("leaving");
+
+    return $value;
+}
+
+
+sub listxattr_object
+{
+    my $self = shift;
+
+    my ($key) = validate_pos(@_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug("entered - @_");
+
+    # ignore volume
+    $key = parse_neb_key($key);
+
+    my @xattrs;
+    eval {
+        my $query = $db->prepare_cached( $sql->list_object_xattr );
+        # ext_id
+        my $rows = $query->execute($key->path);
+
+        while (my $row = $query->fetchrow_hashref) {
+            push @xattrs, $row->{ 'name' };
+        }
+    };
+    $log->logdie("database error: $@") if $@;
+
+    $log->debug("leaving");
+
+    return \@xattrs;
+}
+
+
+sub removexattr_object
+{
+    my $self = shift;
+
+    my ($key, $name) = validate_pos(@_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+        {
+            type        => SCALAR,
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug("entered - @_");
+
+    # ignore volume
+    $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: $@");
+    }
+
+    $log->debug("leaving");
+
+    return 1;
+}
+
+
+sub find_objects
+{
+    my $self = shift;
+
+    my ( $pattern ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+            optional    => 1,
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug( "entered - @_" );
+
+    unless ($pattern) {
+        $log->debug( "leaving" );
+        $log->logdie("no keys found");
+    }
+
+    # attempt to strip off neb:// if it exists
+    $pattern =~ s|^neb:||;
+
+    my @keys;
+    eval {
+        my $query = $db->prepare_cached( $sql->find_objects );
+        $query->execute( $pattern );
+
+        while ( my $row = $query->fetchrow_hashref ) {
+            my $key = $row->{ 'ext_id' };
+            push @keys, $key if $key;
+        }
+    };
+    $log->logdie("database error: $@") if $@;
+
+    $log->logdie("no keys found") unless ( scalar @keys );
+
+    $log->debug( "leaving" );
+
+    return \@keys;
+}
+
+
+sub find_instances
+{
+    my $self = shift;
+
+    my ($key, $vol_name) = validate_pos(@_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+        {
+            type        => SCALAR|UNDEF,
+#            callbacks   => {
+#                # check that the volume name requested is valid
+#                'is valid volume name' => sub {
+#                    return 1 if not defined $_[0];
+#                    $self->_is_valid_volume_name($_[0])
+#                },
+#            },
+            optional    => 1,
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug("entered - @_");
+
+    # vol_name overrides the key implied volume
+    $key = parse_neb_key($key, $vol_name);
+    $vol_name = $key->volume;
+
+    # the key's volume can't be validiated on input for this method so we have
+    # to check it after parsing the key
+    if (defined $vol_name
+        and not $self->_is_valid_volume_name($key->volume)) {
+        if ($key->soft_volume) {
+            $log->warn( "$vol_name is not a known volume name" );
+            $vol_name = undef;
+        } else {
+            die "$vol_name is not a valid volume name"
+        }
+    }
+
+    my @locations;
+    eval {
+        my $query;
+        if ($vol_name) {
+            $query = $db->prepare_cached( $sql->get_object_instances_by_vol_name );
+            # ext_id, name, available
+            my $rows = $query->execute($key->path, $vol_name, 1);
+            unless ($rows > 0) {
+                $query->finish;
+                $log->logdie("no instances on storage volume or volume is not avaiable for key: $key volume: $vol_name");
+            }
+        } else {
+            $query = $db->prepare_cached( $sql->get_object_instances );
+            # ext_id, available
+            my $rows = $query->execute($key->path, 1);
+            unless ($rows > 0) {
+                $query->finish;
+                $log->logdie("no instances available for key: $key");
+            }
+        }
+
+        while (my $row = $query->fetchrow_hashref) {
+            my $instance = $row->{ 'uri' };
+            push @locations, $instance if $instance;
+        }
+    };
+    if ($@) {
+        $db->rollback;
+        # handle soft volumes
+        if (defined $vol_name and defined $key->soft_volume) {
+            $log->debug("retrying with 'any' volume");
+            return $self->find_instances($key->path, 'any');
+        }
+        $log->logdie("database error: $@");
+    }
+
+    # XXX remove this?
+    $log->logdie("no instances found") unless (scalar @locations);
+
+    $log->debug("found: @locations");
+
+    $log->debug("leaving");
+
+    return \@locations;
+}
+
+
+sub delete_instance
+{
+    my $self = shift;
+
+    my ( $uri ) = validate_pos( @_,
+        {
+            type => SCALAR|SCALARREF,
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $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 ) {
+                $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: $@" );
+    }
+
+    $log->debug( "leaving" );
+
+    return 1;
+}
+
+
+sub stat_object
+{
+    my $self = shift;
+
+    my ( $key ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+    );
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug("entered - @_");
+
+    # ignore volume
+    $key = parse_neb_key($key);
+
+    my $stat;
+    eval {
+        my $query = $db->prepare_cached( $sql->stat_object );
+        my $rows = $query->execute($key->path);
+
+        unless ($rows == 1) {
+            $log->logdie("no storage object found");
+        }
+
+        $stat = $query->fetchrow_arrayref;
+        $query->finish;
+    };
+    $log->logdie("database error: $@") if $@;
+
+    $log->debug("leaving");
+
+    return $stat;
+}
+
+
+sub mounts
+{
+    my $self = shift;
+
+    validate_pos(@_); 
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug("entered - @_");
+
+    my $stats;
+    my $query;
+    eval {
+        # ask the db to generate the table of mounted Nebulous volume 
+        $db->do("call getmountedvol()");
+
+        $query = $db->prepare_cached( $sql->get_mounted_volumes );
+        $query->execute();
+
+        # suck that table into an AoA
+        $stats = $query->fetchall_arrayref;
+
+        $query->finish;
+    };
+    $log->logdie("database error: $@") if $@;
+
+    $log->logdie("no mounted volumes found") unless (scalar @$stats);
+
+    $log->debug("leaving");
+
+    return $stats;
+}
+
+
+sub _get_storage_volume
+{
+    my $self = shift;
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    no warnings qw( uninitialized );
+    $log->debug( "entered - @_" );
+    use warnings;
+
+    my ($name, $soft_volume) = @_;
+
+    my ($vol_id, $vol_host, $vol_path, $xattr);
+    eval {
+        my $query;
+        my $rows;
+        if ( $name ) {
+            $query = $db->prepare_cached( $sql->get_storage_volume_by_name );
+            # %free, name, avaiable, allocate
+            $rows = $query->execute(0.95, $name, 1, 1);
+            # XXX destinguish between non-existant and unaviable
+            unless ($rows > 0) {
+                $query->finish;
+
+                # if a volume name was specified, and is soft, and we failed to
+                # find it, fall back to any volume
+                if ($soft_volume) {
+                    ($vol_id, $vol_host, $vol_path, $xattr) = $self->_get_storage_volume;
+                    return; # this just returns out of the eval not from the subroutine
+                }
+                $log->logdie("storage volume: $name is not available");
+            }
+            # when matching by name we shouldn't ever match more than once
+            if ($rows > 1) {
+                $query->finish;
+                $log->logdie("affected row count is $rows instead of 1");
+            }
+        } else {
+            $query = $db->prepare_cached( $sql->get_storage_volume );
+            # %free, avaiable, allocate
+            $rows = $query->execute(0.95, 1, 1);
+            # there has to be atleast one storage volume
+            unless ($rows > 0) {
+                $query->finish;
+                $log->logdie("no storage volume is available");
+            }
+        }
+
+        my $free;
+        ($vol_id, $vol_host, $vol_path, $xattr, $free) = $query->fetchrow_array;
+        $query->finish;
+    };
+    $log->logdie("database error: $@") if $@;
+
+    $log->logdie("failed to find a suitable volume" )
+        unless defined $vol_id and defined $vol_path;
+
+    $log->debug( "leaving" );
+
+    return ($vol_id, $vol_host, $vol_path, $xattr);
+}
+
+
+sub _get_replication_volume
+{
+    my $self = shift;
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    no warnings qw( uninitialized );
+    $log->debug( "entered - @_" );
+    use warnings;
+
+    my $key = shift;
+
+    $key = parse_neb_key($key);
+
+    my ($vol_id, $vol_host, $vol_path, $xattr);
+    eval {
+        my $rows;
+        my $query = $db->prepare_cached( $sql->get_replication_volume_for_ext_id );
+        # ext_id, %free, avaiable, allocate
+        $rows = $query->execute($key->path, 0.95, 1, 1);
+        # XXX destinguish between non-existant and unaviable
+        unless ($rows > 0) {
+            $query->finish;
+            $log->logdie("can't find a suitable storage volume to replicate $key to");
+        }
+        # when matching by name we shouldn't ever match more than once
+        if ($rows > 1) {
+            $query->finish;
+            $log->logdie("affected row count is $rows instead of 1");
+        }
+
+        my $free;
+        ($vol_id, $vol_host, $vol_path, $xattr, $free) = $query->fetchrow_array;
+        $query->finish;
+    };
+    $log->logdie("database error: $@") if $@;
+
+    $log->logdie("failed to find a suitable volume" )
+        unless defined $vol_id and defined $vol_path;
+
+    $log->debug( "leaving" );
+
+    return ($vol_id, $vol_host, $vol_path, $xattr);
+}
+
+
+sub _is_valid_object_key
+{
+    my ($self, $key) = @_;
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $key = parse_neb_key($key);
+
+    my $ext_id;
+    eval {
+        my $query = $db->prepare_cached( $sql->get_object ); 
+        $query->execute($key->path);
+        ($ext_id) = $query->fetchrow_array;
+        $query->finish;
+    };
+    if ($@) {
+        $db->rollback;
+        $log->debug("rollback");
+        $log->logdie( "database error: $@" );
+    }
+
+    if (defined $ext_id) {
+        return 1;
+    } 
+
+    return;
+}
+
+
+sub _is_valid_volume_name
+{
+    my ($self, $vol_name) = @_;
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    my $volume_info = parse_neb_volume($vol_name);
+
+    $vol_name = $volume_info->{volume};
+
+    # handle "any" volume
+    if ($vol_name eq 'any') {
+        return 1;
+    }
+
+    my ($vol_id, $vol_path);
+    eval {
+        my $query = $db->prepare_cached( $sql->get_volume_by_name ); 
+        $query->execute( $vol_name );
+        my $free;
+        ($vol_id, $vol_path, $free) = $query->fetchrow_array;
+        $query->finish;
+    };
+    $log->logdie("database error: $@") if $@;
+
+    if (defined $vol_id and defined $vol_path) {
+        return 1;
+    } 
+
+    return;
+}
+
+sub _create_empty_instance_file
+{
+    my $self = shift;
+
+    my ($key, $so_id, $ins_id, $vol_path, $xattr) =  @_;
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  = $self->db;
+
+    my $uri;
+    eval {
+        my $storage_path = $self->_generate_storage_path($key, $vol_path);
+        my $storage_filename = $self->_generate_storage_filename($key, $ins_id);
+        unless (-d $storage_path) {
+            mkpath($storage_path, 0, 0775)
+                or die "can't create storage path: $storage_path";
+        }
+        # check to make sure at least the parent directory has the proper
+        # permissions
+        my $mode = (stat($storage_path))[2] & 07777;
+        unless ($mode == 0775) {
+            $log->error("$storage_path has the wrong permissions of: %04o", $mode);
+            chmod(0775, $storage_path) or die "can't chmod $storage_path: $!";
+        }
+
+        my $fqpn = File::Spec->catfile($storage_path, $storage_filename);
+        $uri = URI::file->new($fqpn);
+        $log->debug("generated uri $uri");
+        $self->_create_empty_file($uri->file);
+    };
+    if ($@) {
+        $log->logdie($@);
+    }
+
+    if ($xattr) {
+        my $path = $uri->file;
+        die "can not set xattr on $path: $!"
+            unless (setfattr($path, 'user.nebulous_key', $key));
+    }
+
+    return $uri;
+}
+
+
+sub _create_empty_file
+{
+    my $self = shift;
+
+    my ($path) = @_;
+
+    # perl's open() can't do an O_CREAT | O_EXCL
+    die "file $path already exists" if (-e $path);
+
+    my $fh;
+    die "can not open $path: $!"
+        unless (open($fh, '>', $path));
+
+    # chmod before fsync() to make sure the changed perms hit the disk too
+    die "can not chmod $path: $!"
+        unless (chmod 0664, $path);
+
+    die "can not flush $path: $!"
+        unless ($fh->flush);
+
+    die "can not sync $path: $!"
+        unless ($fh->sync);
+
+    die "can not close $path: $!"
+        unless (close($fh));
+
+    return $path;
+}
+
+
+sub _generate_storage_filename
+{
+    my $self = shift;
+
+    my ($key, $ins_id) = @_;
+
+    my $filename = $key;
+    # mangle '/'s into ':'
+    $filename =~ s|/|:|g;
+
+    return "$ins_id.$filename"
+}
+
+
+sub _generate_storage_path
+{
+    my $self = shift;
+
+    my ($key, $vol_path) = @_;
+
+    # taken and modified from Cache::File::cache_file_path()
+    # Copyright (C) 2003-2006 Chris Leishman.  All Rights Reserved.
+    my $shakey = sha1_hex($key);
+    my (@path) = unpack('A2' x SUBPATH_DEPTH, $shakey);
+
+    return File::Spec->catdir($vol_path, @path);
+}
+
+
+sub DESTROY
+{
+    my $self = shift;
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    $log->debug( "entered" );
+
+    $self->db->disconnect;        
+
+    $log->debug( "disconnected from database: ", sub { $db->data_sources; } );
+
+    $log->debug( "leaving" );
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server.pod
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server.pod	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server.pod	(revision 22322)
@@ -0,0 +1,240 @@
+=pod
+
+=head1 NAME
+
+Nebulous::Server - Nebulous server component
+
+=head1 SYNOPSIS
+
+    use Nebulous::Server;
+
+    Nebulous::Server->new( $dsn, $username, $passwd );
+    Nebulous::Server->db( $db );
+    Nebulous::Server->log( $log );
+    Nebulous::Server->sql( $sql );
+    Nebulous::Server->config( $config );
+    Nebulous::Server->create_object( $key, $volume );
+    Nebulous::Server->rename_object( $key, $newkey );
+    Nebulous::Server->replicate_object( $key, $volume );
+    Nebulous::Server->lock_object( $key, $type );
+    Nebulous::Server->unlock_object( $key, $type );
+    Nebulous::Server->setxattr_object( $key, $name, $value, $flags );
+    Nebulous::Server->getxattr_object( $key, $name );
+    Nebulous::Server->listxattr_object( $key );
+    Nebulous::Server->removexattr_object( $key, $name );
+    Nebulous::Server->find_objects( $pattern );
+    Nebulous::Server->find_instances( $key, $volume );
+    Nebulous::Server->delete_instance( $uri );
+    Nebulous::Server->stat_object( $key );
+    Nebulous::Server->mounts();
+
+=head1 DESCRIPTION
+
+This is the server side component of the Nebulous system.  It is primarily a
+middle-ware abstraction of an SQL database.
+
+=head1 USAGE
+
+Typically, this module would not be directly interacted with (although, it is
+possible to do so).  It is intended that one or more intermediate transport
+layers are used (SOAP, XML-RPC, etc.)
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Class Methods
+
+=over 4
+
+=item * setup( $dsn, $username, $passwd );
+
+Accepts 3 parameters, all are mandatory.  Returns Boolean true.  Throws an
+exception on error.
+
+=item * create_object( $key, $volume );
+
+Accepts 4 parameters, only the first is mandatory.  Returns a URI.  Throws an
+exception on error.
+
+=item * replicate_object( $key, $volume );
+
+Accepts 2 parameters, only the first is mandatory.  Returns a URI.  Throws an
+exception on error.
+
+=item * lock_object( $key, $type );
+
+Accepts 2 parameters, all are mandatory.  Returns Boolean true.   Throws an
+exception on error.
+
+=item * unlock_object( $key, $type );
+
+Accepts 2 parameters, all are mandatory.  Returns Boolean true.   Throws an
+exception on error.
+
+=item * setxattr_object( $key, $name, $value, $flags );
+
+Accepts 4 parameters, all are mandatory.  
+
+=over 4
+
+=item C<$key>
+
+Nebulous key to create the xattr on.
+
+=item C<$name>
+
+The xattr's name.
+
+=item C<$value>
+
+The xattr's value.
+
+=item C<$flags>
+
+The xattr key insertion mode flag.  Acceptable value are either C<"create"> or
+C<"replace">.  If the xattr already exists then trying to insert another xattr
+with the same name in C<"create"> mode will fail.
+
+=back
+
+Returns Boolean true on success.  Throws an exception on error.
+
+=item * getxattr_object( $key, $name );
+
+Accepts 2 parameters, all are mandatory.  
+
+=over 4
+
+=item C<$key>
+
+Nebulous key to fetch the xattr from.
+
+=item C<$name>
+
+The xattr's name.
+
+=back
+
+Returns the string value of C<$name> on success.  Throws an exception on error.
+
+=item * listxattr_object( $key );
+
+Accepts 1 mandatory parameter. 
+
+=over 4
+
+=item C<$key>
+
+List all xattrs on this Nebulous key.
+
+=back
+
+Returns an C<ARRAYREF> on success.  Throws an exception on error.
+
+=item * removexattr_object( $key, $name );
+
+Accepts 2 parameters, all are mandatory.  
+
+=over 4
+
+=item C<$key>
+
+Nebulous key to remove the xattr from.
+
+=item C<$name>
+
+The xattr's name.
+
+=back
+
+Returns Boolean true on success.  Throws an exception on error.
+
+=item * find_instance( $key, $volume );
+
+Accepts 2 parameters, only the first is mandatory.  Returns an arrayref.
+Throws an exception on error.
+
+=item * delete_instance( $uri );
+
+Accepts 1 parameters, it is mandatory.  Returns Boolean true.  Throws an
+exception on error.
+
+=item * stat_object( $key );
+
+Accepts 1 parameters, it is mandatory.  Returns a list of:
+
+    so_id
+    ext_id
+    read_lock
+    write_lock
+    epoch
+    mtime
+    number of instances
+
+=item * mounts( $key );
+
+Accepts no parameters and returns an AoA of:
+
+    [
+        [
+            mountpoint,
+            total,
+            used,
+            vol_id,
+            name,
+            path,
+            allocate,
+            available,
+            xattr,
+        ],
+        [ ... ],
+        ...
+    ]
+
+=back
+
+=head1 DEVELOPER NOTES
+
+=head2 REFERENCES
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2004-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the LICENSE file included with
+this module, or in the L<perlgpl> Pod as supplied with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Client>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/Apache.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/Apache.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/Apache.pm	(revision 22322)
@@ -0,0 +1,25 @@
+# Copyright (C) 2004  Joshua Hoblitt
+#
+# $Id
+
+package Nebulous::Server::Apache;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = 0.01;
+
+use Nebulous::Server::SOAP;
+use SOAP::Transport::HTTP;
+
+my $server = SOAP::Transport::HTTP::Apache
+    ->dispatch_to( 'Nebulous::Server::SOAP' )
+    ->options({ compress_threshold => 10 * 1024 });
+
+no warnings qw( redefine );
+sub handler { $server->handler(@_); }
+use warnings;
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/Config.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/Config.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/Config.pm	(revision 22322)
@@ -0,0 +1,62 @@
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: Config.pm,v 1.3 2008-03-20 21:10:57 jhoblitt Exp $
+
+package Nebulous::Server::Config;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = 0.02;
+
+use base qw( Class::Accessor::Fast );
+
+use Log::Log4perl qw( :levels );
+use Params::Validate qw( validate SCALAR );
+
+our %LEVELS = (
+    off     => $OFF,
+    fatal   => $FATAL,
+    error   => $ERROR,
+    warn    => $WARN,
+    info    => $INFO,
+    debug   => $DEBUG,
+    all     => $ALL,
+);
+
+my $new_validate = {
+    dsn         => { type => SCALAR },
+    dbuser      => { type => SCALAR },
+    dbpasswd    => { type => SCALAR },
+    log_level   => {
+        type        => SCALAR,
+        optional    => 1,
+        default     => 'all',
+        callbacks   => {
+            'is valid level' => sub {
+                defined $LEVELS{ lc $_[0] };
+            },
+        },
+    },
+};
+
+__PACKAGE__->mk_ro_accessors( keys %$new_validate );
+
+sub init {
+    my $class = shift;
+
+    my %p = validate( @_, $new_validate );
+
+    # normalize log levels to lower-case
+    $p{ log_level } = lc $p{ log_level };
+
+    my $self = \%p;
+
+    bless $self, $class || ref $class;
+
+    return $self;
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/Log.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/Log.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/Log.pm	(revision 22322)
@@ -0,0 +1,62 @@
+# Copyright (c) 2004  Joshua Hoblitt
+#
+# $Id: Log.pm,v 1.7 2008-05-07 00:02:10 jhoblitt Exp $
+
+package Nebulous::Server::Log;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = '0.02';
+
+use Log::Log4perl;
+use Nebulous::Server::Config;
+
+sub init {
+    my ($self, $config) = @_;
+
+    my $dsn         = $config->dsn;
+    my $dbuser      = $config->dbuser;
+    my $dbpasswd    = $config->dbpasswd;
+
+    my $conf = <<END;
+    log4perl.category.Nebulous.Server = WARN, SERVERLOGFILE
+
+    log4perl.appender.SERVERLOGFILE           = Log::Log4perl::Appender::File
+    log4perl.appender.SERVERLOGFILE.filename  = /tmp/nebulous_server.log
+    log4perl.appender.SERVERLOGFILE.mode      = append
+    log4perl.appender.SERVERLOGFILE.layout    = Log::Log4perl::Layout::PatternLayout
+    log4perl.appender.SERVERLOGFILE.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} | %H | %p | %M - %m%n
+#   date | hostname | priority | method/sub - message\n
+
+    log4perl.appender.SQLLOG            = Log::Log4perl::Appender::DBI
+    log4perl.appender.SQLLOG.datasource = $dsn
+    log4perl.appender.SQLLOG.username   = $dbuser
+    log4perl.appender.SQLLOG.password   = $dbpasswd
+    log4perl.appender.SQLLOG.sql        = \
+    INSERT INTO log (timestamp, hostname, level, sub, message) \
+    VALUES          (%d,        %H,       %p,    %M,  %m)
+#        VALUES          (?,         ?,        ?,     ?,   ?)
+#        log4perl.appender.SQLLOG.params.1   = %d
+#        log4perl.appender.SQLLOG.params.2   = %H
+#        log4perl.appender.SQLLOG.params.3   = %p
+#        log4perl.appender.SQLLOG.params.4   = %M
+#        log4perl.appender.SQLLOG.params.5   = %m
+#        log4perl.appender.SQLLOG.usePreparedStmt = 1
+    log4perl.appender.SQLLOG.bufferSize = 2
+    log4perl.appender.SQLLOG.layout     = Log::Log4perl::Layout::NoopLayout
+    log4perl.appender.SQLLOG.warp_message = 0
+END
+
+    if ($INC{'Apache/DBI.pm'} && $ENV{MOD_PERL}) {
+        Log::Log4perl::init_once( \$conf );
+    } else {
+        Log::Log4perl::init( \$conf );
+    }
+
+    return 1;
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/SOAP.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/SOAP.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/SOAP.pm	(revision 22322)
@@ -0,0 +1,78 @@
+# Copyright (c) 2004  Joshua Hoblitt
+#
+# $Id: SOAP.pm,v 1.4 2007-05-02 00:42:52 jhoblitt Exp $
+
+package Nebulous::Server::SOAP;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = '0.02';
+
+import SOAP::Data 'name'; 
+use Apache2::Const -compile => qw(OK);
+use Nebulous::Server;
+use SOAP::Lite;
+
+our $AUTOLOAD;
+
+our @args;
+our $neb;
+
+sub new_on_init {
+    my $self = shift;
+
+    require mod_perl2;
+    require Apache2::Module;
+    require Apache2::ServerUtil;
+
+    @args = @_;
+
+    my $s = Apache2::ServerUtil->server;
+    $s->push_handlers(PerlChildInitHandler => \&init);
+
+    return $self;
+}
+
+sub init {
+    my $self = shift;
+
+    $neb = Nebulous::Server->new(@args);        
+
+    return Apache2::Const::OK;
+}
+
+sub AUTOLOAD {
+    my $self = shift;
+
+    my ( $package, $method ) = $AUTOLOAD =~ m/(?:(.+)::)([^:]+)$/;
+    return undef if $method eq 'DESTROY';
+
+    init() unless defined $neb;
+
+    die "process $$ has not been initialized"
+        unless defined $neb;
+
+    die "method $method does not exist in Nebulous::Server"
+        unless defined ${Nebulous::Server::}{$method};
+
+    # create a sub and install it in the package so successive calls to the
+    # same method don't have to go through AUTOLOAD
+    my $method_sub;
+    eval q|
+    $method_sub = sub {
+        my $self = shift;
+        return name( result => $neb->| . $method . q|( @_ ) );
+    }
+    |;
+    die $@ if $@;
+
+    no strict 'refs';
+    *{"${package}::${method}"} = $method_sub;
+    use strict;
+
+    # invoke the method we just created
+    return $self->$method(@_);
+}
+
+1;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/SQL.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/SQL.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Nebulous/Server/SQL.pm	(revision 22322)
@@ -0,0 +1,535 @@
+# Copyright (c) 2004  Joshua Hoblitt
+#
+# $Id: SQL.pm,v 1.64 2008-09-22 21:22:52 jhoblitt Exp $
+
+package Nebulous::Server::SQL;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = '0.02';
+
+use base qw( Class::Accessor::Fast );
+
+my %sql = (
+    set_transaction_model   => qq{
+        SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED
+    },
+    update_instance_uri => qq{
+        UPDATE instance
+        SET vol_id = ?, uri = ?
+        WHERE ins_id = ?
+    },
+    last_insert_id      => qq{
+        SELECT LAST_INSERT_ID()
+    },
+    new_object          => qq{
+        INSERT INTO storage_object
+        (so_id, ext_id, type)
+        VALUES (?, ?, 'REG_FILE')
+    },
+    new_object_attr  => qq{
+        INSERT INTO storage_object_attr
+        (so_id, read_lock, write_lock)
+        VALUES (?, 0, NULL)
+    },
+    delete_object       => qq{
+        DELETE FROM storage_object
+        WHERE storage_object.so_id = ?
+    },
+    new_object_instance => qq{
+        INSERT INTO instance 
+        (so_id, vol_id, uri)
+        VALUES (LAST_INSERT_ID(), ?, 'error')
+    },
+    new_instance        => qq{
+        INSERT INTO instance
+        (so_id, vol_id, uri)
+        VALUES (?, ?, 'error')
+    },
+    get_object          => qq{
+        SELECT
+            so_id,
+            ext_id,
+            read_lock,
+            write_lock,
+            epoch,
+            mtime
+        FROM storage_object
+        JOIN storage_object_attr
+        USING (so_id)
+        WHERE ext_id = ?
+    },
+    stat_object          => qq{
+        SELECT
+            storage_object.so_id,
+            storage_object.ext_id,
+            read_lock,
+            write_lock,
+            storage_object_attr.epoch,
+            storage_object_attr.mtime,
+            COUNT(instance.so_id) as instances
+        FROM storage_object
+        JOIN storage_object_attr
+            USING (so_id)
+        JOIN instance
+            USING (so_id)
+        WHERE ext_id = ?
+        GROUP BY storage_object.so_id
+    },
+    # Note: this sets an update lock
+    get_object_locks    => qq{
+        SELECT
+            storage_object.so_id,
+            read_lock,
+            write_lock 
+        FROM storage_object
+        JOIN storage_object_attr
+        USING (so_id)
+        WHERE ext_id = ?
+        FOR UPDATE
+    },
+    new_object_xattr  => qq{
+        INSERT INTO storage_object_xattr
+            SELECT
+                so_id,
+                ?,
+                ?        
+            FROM storage_object
+            WHERE ext_id = ?
+    },
+    replace_object_xattr  => qq{
+        REPLACE INTO storage_object_xattr
+            SELECT
+                so_id,
+                ?,
+                ?        
+            FROM storage_object
+            WHERE ext_id = ?
+    },
+    list_object_xattr    => qq{
+        SELECT storage_object_xattr.name
+        FROM storage_object
+        JOIN storage_object_xattr
+        USING (so_id)
+        WHERE ext_id = ?
+    },
+    get_object_xattr    => qq{
+        SELECT storage_object_xattr.value
+        FROM storage_object
+        JOIN storage_object_xattr
+        USING (so_id)
+        WHERE ext_id = ?
+            AND name = ?
+    },
+    remove_object_xattr    => qq{
+        DELETE FROM storage_object_xattr
+        WHERE so_id = (SELECT so_id from storage_object where ext_id = ?)
+            AND name = ?
+    },
+    set_write_lock      => qq{
+        UPDATE storage_object_attr
+        JOIN storage_object
+        USING(so_id)
+        SET write_lock = 'write'
+        WHERE storage_object.ext_id = ?
+    },
+    delete_write_lock   => qq{
+        UPDATE storage_object_attr
+        JOIN storage_object
+        USING(so_id)
+        SET write_lock = NULL
+        WHERE ext_id = ?
+    },
+    increment_read_lock => qq{
+        UPDATE storage_object_attr
+        JOIN storage_object
+        USING(so_id)
+        SET read_lock = read_lock + 1
+        WHERE ext_id = ?
+    },
+    decrement_read_lock => qq{
+        UPDATE storage_object_attr
+        JOIN storage_object
+        USING(so_id)
+        SET read_lock = read_lock - 1
+        WHERE ext_id = ?
+    },
+    delete_instance     => qq{
+        DELETE FROM instance
+        WHERE uri = ?
+    },
+    get_object_from_uri   => qq{
+        SELECT so_id
+        FROM instance
+        WHERE uri = ?
+    },
+    get_instance_count   => qq{
+        SELECT count(ins_id)
+        FROM instance
+        WHERE so_id = ?
+    },
+    get_object_instances    => qq{
+        SELECT
+            storage_object.so_id,
+            uri,
+            assigned_vol_id,
+            available
+        FROM storage_object
+        JOIN instance
+            USING (so_id)
+        JOIN mountedvol
+            USING(vol_id)
+        WHERE ext_id = ?
+            AND available = ?
+    },
+    get_object_instances_by_vol_name => qq{
+        SELECT
+            storage_object.so_id,
+            uri,
+            assigned_vol_id
+        FROM storage_object
+        JOIN instance
+            USING (so_id)
+        JOIN mountedvol
+            USING(vol_id)
+        WHERE ext_id = ?
+            AND name = ?
+            AND available = ?
+    },
+    get_storage_volume_by_name   => qq{
+        SELECT
+            vol_id,
+            host,
+            path,
+            xattr,
+            total - used as free
+        FROM mountedvol
+        WHERE
+            used / total < ?
+            AND name = ?
+            AND available = ?
+            AND allocate = ?
+        ORDER BY free DESC
+        LIMIT 1
+    },
+    get_replication_volume_for_ext_id   => qq{
+        SELECT
+            vol_id,
+            host,
+            path,
+            xattr,
+            total - used as free
+        FROM (
+-- This query works but is slow...
+--             SELECT
+--                  mountedvol.*
+--             FROM mountedvol
+--             WHERE
+--                 vol_id NOT IN(
+--                     SELECT vol_id
+--                     FROM storage_object
+--                     JOIN instance
+--                         USING(so_id)
+--                     WHERE ext_id = --
+--                  )
+                SELECT
+                    m.*
+                FROM mountedvol AS m
+                LEFT JOIN instance AS i
+                    ON m.vol_id = i.vol_id
+                    AND i.so_id = (
+                        SELECT so_id
+                        FROM storage_object
+                        WHERE ext_id = ?
+                    )
+                WHERE
+                    i.vol_id IS NULL
+        ) as Foo 
+        WHERE
+            used / total < ? 
+            AND available = ? 
+            AND allocate = ? 
+        ORDER BY RAND()
+        LIMIT 1
+    },
+    get_storage_volume          => qq{
+        SELECT
+            vol_id,
+            host,
+            path,
+            xattr,
+            total - used as free
+        FROM mountedvol
+        WHERE
+            used / total < ?
+            AND available = ?
+            AND allocate = ?
+        ORDER BY free DESC
+        LIMIT 1
+    },
+    new_volume          => qq{
+        INSERT INTO volume (name, host, path, allocate, available, xattr)
+        VALUES (?, ?, ?, TRUE, TRUE, FALSE)
+    },
+    get_volume_by_name => qq{
+        SELECT vol_id, name, host, path
+        FROM volume
+        WHERE name = ?
+    },
+    find_objects => qq{
+        SELECT *
+        FROM storage_object
+        WHERE ext_id REGEXP ?
+    },
+    rename_object => qq{
+        UPDATE storage_object
+        SET ext_id = ?
+        WHERE ext_id = ?
+    },
+    find_objects_with_unavailable_instances => qq{
+        SELECT
+            storage_object.so_id,
+            ext_id,
+            count(ins_id) as instances,
+            volume.name as volume_name,
+            volume.host as volume_host,
+            count(mountedvol.vol_id) as available_instances,
+            count(mountedvol.vol_id) > 0 as recoverable,
+            storage_object_xattr.value as copies
+        FROM storage_object
+        JOIN instance
+            USING(so_id)
+        JOIN volume
+            USING(vol_id)
+        LEFT JOIN storage_object_xattr
+            ON storage_object.so_id = storage_object_xattr.so_id
+        JOIN mountedvol
+            USING(vol_id)
+        WHERE mountedvol.available = 1
+--        WHERE storage_object_xattr.name = 'user.copies'
+        GROUP BY so_id
+        HAVING available_instances < instances OR instances < copies
+    },
+    get_mounted_volumes => qq{
+        SELECT * FROM mountedvol ORDER BY vol_id
+    },
+);
+
+{
+    my @schema;
+
+    local $/ = '###';
+
+    foreach my $statement (<DATA>) {
+        last unless ( $statement =~ /\S+/ );
+        $statement =~ s/###//g;
+        push @schema, $statement;
+    }
+
+    $sql{get_db_schema} = \@schema;
+}
+
+{
+    my @clear = split /;/, <<END;
+SET FOREIGN_KEY_CHECKS=0;
+DROP TABLE IF EXISTS storage_object;
+DROP TABLE IF EXISTS storage_object_attr;
+DROP TABLE IF EXISTS storage_object_xattr;
+DROP TABLE IF EXISTS instance;
+DROP TABLE IF EXISTS lock_record;
+DROP TABLE IF EXISTS volume;
+DROP TABLE IF EXISTS mount;
+DROP TABLE IF EXISTS log;
+DROP TABLE IF EXISTS mountedvol;
+DROP PROCEDURE IF EXISTS getmountedvol;
+SET FOREIGN_KEY_CHECKS=1
+END
+    $sql{get_db_clear} = \@clear;
+}
+
+__PACKAGE__->mk_ro_accessors( keys %sql );
+
+sub new {
+    my $class = shift;
+
+    my $self = \%sql;
+
+    bless $self, $class || ref $class;
+
+    return $self;
+}
+
+1;
+
+__DATA__
+CREATE TABLE storage_object (
+    so_id BIGINT NOT NULL AUTO_INCREMENT,
+    ext_id VARCHAR(255) NOT NULL UNIQUE,
+    type enum('REG_FILE'),
+    PRIMARY KEY(so_id),
+    KEY(ext_id(64)),
+    KEY(type)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+###
+
+CREATE TABLE storage_object_attr (
+    so_id BIGINT NOT NULL AUTO_INCREMENT,
+    FOREIGN KEY(so_id) REFERENCES storage_object(so_id) ON DELETE CASCADE,
+    read_lock TINYINT DEFAULT 0 NOT NULL,
+    write_lock ENUM( 'write' ),
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    mtime TIMESTAMP,
+    PRIMARY KEY(so_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+###
+
+CREATE TABLE storage_object_xattr (
+    so_id BIGINT NOT NULL AUTO_INCREMENT,
+    FOREIGN KEY(so_id) REFERENCES storage_object(so_id) ON DELETE CASCADE,
+    name VARCHAR(255),
+    value BLOB,
+    PRIMARY KEY(so_id, name),
+    KEY(so_id),
+    KEY(name(64))
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+###
+
+CREATE TABLE lock_record (
+    so_id BIGINT NOT NULL,
+    FOREIGN KEY(so_id) REFERENCES storage_object(so_id),
+    type ENUM( 'read', 'write' ) NOT NULL,
+    epoch TIMESTAMP,
+    KEY(so_ID)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+###
+
+CREATE TABLE volume (
+    vol_id INT NOT NULL AUTO_INCREMENT,
+    name VARCHAR(255) UNIQUE NOT NULL,
+    host VARCHAR(255) NOT NULL,
+    path VARCHAR(255) NOT NULL,
+    allocate BOOLEAN DEFAULT FALSE,
+    available BOOLEAN DEFAULT FALSE,
+    xattr BOOLEAN DEFAULT FALSE,
+    PRIMARY KEY(vol_id),
+    KEY(name(16)),
+    KEY(host(16)),
+    KEY(path(255)),
+    KEY(allocate),
+    KEY(available)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+###
+
+CREATE TABLE instance (
+    ins_id BIGINT NOT NULL AUTO_INCREMENT,
+    so_id BIGINT NOT NULL,
+    FOREIGN KEY(so_id) REFERENCES storage_object(so_id),
+    vol_id INT NOT NULL,
+    FOREIGN KEY(vol_id) REFERENCES volume(vol_id),
+    uri VARCHAR(255) NOT NULL UNIQUE,
+    sha1sum CHAR(40) ASCII,
+    assigned_vol_id INT,
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    mtime TIMESTAMP,
+    PRIMARY KEY(ins_id),
+    KEY(so_id),
+    KEY(vol_id),
+    KEY(uri(64))
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+###
+
+CREATE TABLE mount (
+    mountpoint VARCHAR(255) NOT NULL,
+    total BIGINT NOT NULL,
+    used BIGINT NOT NULL,
+    PRIMARY KEY(mountpoint)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+###
+
+CREATE TABLE log (
+    timestamp TIMESTAMP,
+    hostname VARCHAR(255),
+    level VARCHAR(255),
+    sub VARCHAR(255),
+    message VARCHAR(2048) NOT NULL,
+    PRIMARY KEY(timestamp)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+###
+
+CREATE TABLE mountedvol(
+    mountpoint VARCHAR(255) NOT NULL,
+    FOREIGN KEY(mountpoint) REFERENCES mount(mountpoint) ON DELETE CASCADE,
+    total BIGINT NOT NULL,
+    used BIGINT NOT NULL,
+    vol_id INT NOT NULL,
+    FOREIGN KEY(vol_id) REFERENCES volume(vol_id) ON DELETE CASCADE,
+    name VARCHAR(255) NOT NULL,
+    host VARCHAR(255) NOT NULL,
+    path VARCHAR(255) NOT NULL,
+    FOREIGN KEY(path) REFERENCES volume(path) ON DELETE CASCADE,
+    allocate BOOLEAN DEFAULT FALSE,
+    available BOOLEAN DEFAULT FALSE,
+    xattr BOOLEAN DEFAULT FALSE,
+    KEY(vol_id),
+    KEY(allocate),
+    KEY(available)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+###
+
+CREATE PROCEDURE getmountedvol() DETERMINISTIC
+BEGIN
+    DECLARE done BOOLEAN DEFAULT FALSE;
+    DECLARE vol_idvar INT;
+    DECLARE namevar VARCHAR(255);
+    DECLARE hostvar VARCHAR(255);
+    DECLARE pathvar VARCHAR(255);
+    DECLARE allocatevar BOOLEAN;
+    DECLARE availablevar BOOLEAN;
+    DECLARE xattrvar BOOLEAN;
+    DECLARE trans_level VARCHAR(255);
+    DECLARE cur1 CURSOR FOR SELECT vol_id, name, host, path, allocate, available, xattr FROM volume;
+    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = TRUE;
+
+    -- store the current transaction level
+    SELECT @@session.tx_isolation INTO trans_level; 
+
+    -- set trans level to repeatable-read so the volume table does not change
+    -- out from under our cursor
+    SET @@session.tx_isolation = 'REPEATABLE-READ';
+
+    -- iterate over the volume table finding the coresponding entry in the
+    -- mount table and inserting union of the volume & mount row into the
+    -- mountedvol table
+    OPEN cur1;
+
+    DELETE FROM mountedvol;
+
+    myloop: LOOP
+        FETCH cur1 INTO vol_idvar, namevar, hostvar, pathvar, allocatevar, availablevar, xattrvar;
+        IF `done` THEN LEAVE myloop; END IF;
+        INSERT INTO mountedvol
+            SELECT mountpoint, total, used, vol_idvar, namevar, hostvar, pathvar, allocatevar, availablevar, xattrvar
+            FROM
+                (SELECT *, INSTR(pathvar, mountpoint) = 1 as substring
+                FROM mount
+                ORDER BY substring DESC, LENGTH(mountpoint) DESC
+                LIMIT 1) as bar;
+    END LOOP myloop;
+    
+    CLOSE cur1;
+
+    -- restore the original transaction level
+    SET @@session.tx_isolation = trans_level;
+
+    COMMIT;
+END 
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Test/Nebulous.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Test/Nebulous.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/lib/Test/Nebulous.pm	(revision 22322)
@@ -0,0 +1,101 @@
+# Copyright (C) 2004  Joshua Hoblitt
+#
+# $Id: Nebulous.pm,v 1.4 2008-09-11 22:35:52 jhoblitt Exp $
+
+package Test::Nebulous;
+
+use strict;
+
+our $VERSION = '0.01';
+
+use base qw( Exporter );
+
+use DBI;
+use File::Path qw( mkpath rmtree );
+use File::Temp qw( tempdir );
+use Nebulous::Server::SQL;
+
+our @EXPORT = qw( $NEB_DB $NEB_USER $NEB_PASS );
+
+our $NEB_DB     = $ENV{'NEB_DB'}   || "DBI:mysql:database=test:host=localhost";
+our $NEB_USER   = $ENV{'NEB_USER'} || "test";
+our $NEB_PASS   = $ENV{'NEB_PASS'} || '';
+
+my $dbh = DBI->connect( $NEB_DB, $NEB_USER, $NEB_PASS );
+my $sql = Nebulous::Server::SQL->new;
+
+# suppress uninitalized warnings
+my $dir1 = "";
+my $dir2 = "";
+my $dir3 = "";
+my $dir4 = "";
+my $dir5 = "";
+my $dir6 = "";
+my $dir7 = "";
+
+sub setup {
+    my $self = shift;
+
+    $self->cleanup;
+
+    # create directories after cleanup
+    $dir1 = tempdir( CLEANUP => 0 );
+    $dir2 = tempdir( CLEANUP => 0 );
+    $dir3 = tempdir( CLEANUP => 0 );
+    $dir4 = tempdir( CLEANUP => 0 );
+    $dir5 = tempdir( CLEANUP => 0 );
+    $dir6 = tempdir( CLEANUP => 0 );
+    $dir7 = tempdir( CLEANUP => 0 );
+
+    foreach my $statement (@{ $sql->get_db_schema }) {
+        $dbh->do( $statement );
+    }
+
+    # node01/node02: allocate = TRUE
+    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (1, 'node01', 'node01', ?, TRUE, TRUE) }, undef, $dir1);
+    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir1);
+
+    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (2, 'node02', 'node02', ?, TRUE, TRUE) }, undef, $dir2);
+    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e8) }, undef, $dir2);
+
+    # node03: allocate = TRUE
+    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (3, 'node03', 'node03', ?, TRUE, TRUE) }, undef, $dir3);
+    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e8) }, undef, $dir3);
+
+    # node04: allocate = FALSE, available = FALSE
+    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (4, 'node04', 'node04', ?, FALSE, FALSE) }, undef, $dir4);
+    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir4);
+
+    # node05: allocate = FALSE, available = TRUE
+    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (5, 'node05', 'node05', ?, FALSE, TRUE) }, undef, $dir5);
+    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir5);
+
+    # node06: allocate = TRUE, available = FALSE
+    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (6, 'node06', 'node06', ?, TRUE, FALSE) }, undef, $dir6);
+    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir6);
+
+    # node07: full
+    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (7, 'node07', 'node07', ?, TRUE, TRUE) }, undef, $dir7);
+    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e10) }, undef, $dir7);
+
+    $dbh->do(qq{ call getmountedvol() });
+}
+
+sub cleanup {
+    foreach my $statement (@{ $sql->get_db_clear }) {
+        $dbh->do( $statement );
+    }
+
+    # on the first call to setup the $dir[12] will be ""
+    rmtree([$dir1], 0, 1) if -e $dir1;
+    rmtree([$dir2], 0, 1) if -e $dir2;
+    rmtree([$dir3], 0, 1) if -e $dir3;
+    rmtree([$dir4], 0, 1) if -e $dir4;
+    rmtree([$dir5], 0, 1) if -e $dir5;
+    rmtree([$dir6], 0, 1) if -e $dir6;
+    rmtree([$dir7], 0, 1) if -e $dir7;
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/scripts/bench_test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/scripts/bench_test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/scripts/bench_test.pl	(revision 22322)
@@ -0,0 +1,41 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib "./lib";
+
+use Benchmark qw( timethese );
+use Nebulous::Client;
+
+my $neb = Nebulous::Client->new(
+    proxy   => 'http://localhost:80/nebulous'
+);
+
+my $key = shift || 'foobar';
+
+eval { $neb->delete( $key ); };
+
+my $fh = $neb->create( $key );
+close $fh;
+
+
+timethese( -3,
+    {
+        'stat' => sub {
+            $neb->stat( $key );
+        },
+    }
+);
+
+$neb->delete( $key );
+
+timethese( -3,
+    {
+        'create/delete' => sub {
+            my $fh = $neb->create( $key );
+            close $fh;
+            $neb->delete( $key );
+        },
+    }
+);
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/scripts/nebulous.cgi
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/scripts/nebulous.cgi	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/scripts/nebulous.cgi	(revision 22322)
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2004  Joshua Hoblitt
+#
+# $Id
+ 
+use strict;
+use warnings FATAL => qw( all );
+
+use SOAP::Transport::HTTP;
+use Nebulous::Server;
+   
+SOAP::Transport::HTTP::CGI
+    -> dispatch_to( 'Nebulous::Server' )
+    -> handle;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/scripts/ptest.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/scripts/ptest.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/scripts/ptest.pl	(revision 22322)
@@ -0,0 +1,49 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib "./lib";
+
+use Nebulous::Client;
+
+my $neb = Nebulous::Client->new(
+    proxy   => 'http://localhost:80/nebulous'
+);
+
+my $key = shift || 'foobar';
+my $kids = shift || 1;
+
+foreach my $id ( 1..$kids ) {
+    my $pid = fork;
+
+    unless ( $pid )  {
+        my $fname = "${key}_$id";
+        my $fh = $neb->open_create( $fname );
+        die "can't create file $fname" unless $fh;
+
+        print $fh "fooby\n";
+
+        close $fh;
+
+        $fh = $neb->open( $fname, 'read' ) or die "can't open file";
+        close $fh;
+
+        $neb->lock( $fname, 'read' );
+        $neb->unlock( $fname, 'read' );
+        $neb->replicate( $fname );
+        $neb->cull( $fname );
+        $neb->find( $fname );
+        $neb->copy( $fname, $fname . "_copy" );
+        $neb->move( $fname, $fname . "_move" );
+        $neb->delete( $fname . "_copy" );
+        $neb->delete( $fname . "_move" );
+
+        exit 0;
+    }
+}
+
+while ( $kids ) {
+    wait();
+    $kids --;
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+TEST
+htdocs
+logs
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/00_distribution.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/00_distribution.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/00_distribution.t	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 00_distribution.t,v 1.2 2007-05-02 01:07:56 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib ./t );
+
+use Test::More;
+
+# example taken from Test::Distribution Pod
+
+BEGIN {
+    eval {
+        require Test::Distribution;
+    };
+    if($@) {
+        plan skip_all => 'Test::Distribution not installed';
+    } else {
+        import Test::Distribution not => [qw( versions use podcover )];
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/01_load.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/01_load.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/01_load.t	(revision 22322)
@@ -0,0 +1,18 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 01_load.t,v 1.9 2008-03-21 01:24:00 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./t ./lib );
+
+use Test::More tests => 5;
+
+BEGIN { use_ok( 'Nebulous::Keys' ); }
+BEGIN { use_ok( 'Nebulous::Server' ); }
+BEGIN { use_ok( 'Nebulous::Server::Log' ); }
+BEGIN { use_ok( 'Nebulous::Server::SOAP' ); }
+BEGIN { use_ok( 'Nebulous::Server::SQL' ); }
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/02_server_setup.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/02_server_setup.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/02_server_setup.t	(revision 22322)
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 02_server_setup.t,v 1.6 2008-02-02 01:51:29 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::Nebulous;
+
+Test::Nebulous->setup;
+
+isa_ok(
+    Nebulous::Server->new(
+        dsn         => $NEB_DB,
+        dbuser      => $NEB_USER,
+        dbpasswd    => $NEB_PASS,
+    ),
+    "Nebulous::Server"
+);
+
+Test::Nebulous->setup;
+
+eval {
+    Nebulous::Server->new(
+        dsn         => "DBI:mysql:database=foobar:host=localhost",
+        dbuser      => "baz",
+        dbpasswd    =>"boo",
+    );
+};
+like( $@, qr/DBI connect.*? failed/, "bad dsn/user/pass" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/03_server_create_object.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/03_server_create_object.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/03_server_create_object.t	(revision 22322)
@@ -0,0 +1,442 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 03_server_create_object.t,v 1.30 2008-09-11 22:35:52 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 89;
+
+use lib qw( ./t ./lib );
+
+use File::ExtAttr qw( getfattr );
+use Nebulous::Server;
+use Test::Nebulous;
+use Test::URI;
+use URI::Split qw( uri_split );
+
+my $test_xattr = undef;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("foo");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("neb:/foo");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("neb://node01/foo");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("neb://node02/foo");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("/foo");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), '/foo', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+{
+    # key
+    my $uri = $neb->create_object("neb:///foo");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("/foo/");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), '/foo/', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("neb:/foo/");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), '/foo/', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("foo/");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), 'foo/', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("foo/bar");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo/bar"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), 'foo/bar', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("/foo/bar");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo/bar"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), '/foo/bar', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume
+    my $uri = $neb->create_object("foo", "node01");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # volume name override
+    my $uri = $neb->create_object("neb://node02/foo", "node01");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # volume name override 
+    # OK because the volume arg overrides the key's  implied volume
+    my $uri = $neb->create_object("neb://99/foo", "node01");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # undef volume name
+    # OK because the undef is ignored
+    my $uri = $neb->create_object("neb://node01/foo", undef);
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # soft volume name request
+    my $uri = $neb->create_object("neb://~node01/foo");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # soft volume name request
+    my $uri = $neb->create_object("neb://node01/foo", "~node02");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # any volume name request
+    my $uri = $neb->create_object("neb://any/foo");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # any volume name request
+    my $uri = $neb->create_object("neb://~any/foo");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # any volume name request
+    my $uri = $neb->create_object("neb://node01/foo", "any");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # any volume name request
+    my $uri = $neb->create_object("neb://node01/foo", "~any");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    ok($neb->find_instances("foo"), 'object key exists');
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+    $neb->create_object("foo");
+};
+like($@, qr/Duplicate entry/, "object already exists");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("neb:/foo");
+    $neb->create_object("neb:/foo");
+};
+like($@, qr/Duplicate entry/, "object already exists");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo", '~node07');
+};
+like($@, qr/node07 is not available/, "request volume this is full");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("neb://~node07/foo");
+};
+like($@, qr/node07 is not available/, "request volume this is full");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo", '~node04');
+};
+like($@, qr/node04 is not available/, "request volume with allocate = FALSE, available = FALSE");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("neb://~node04/foo");
+};
+like($@, qr/node04 is not available/, "request volume with allocate = FALSE, available = FALSE");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo", '~node05');
+};
+like($@, qr/node05 is not available/, "request volume with allocate = FALSE , available = TRUE");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("neb://~node05/foo");
+};
+like($@, qr/node05 is not available/, "request volume with allocate = FALSE , available = TRUE");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo", '~node06');
+};
+like($@, qr/node06 is not available/, "request volume with allocate = TRUE, available = FALSE");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("neb://~node06/foo");
+};
+like($@, qr/node06 is not available/, "request volume with allocate = TRUE, available = FALSE");
+
+Test::Nebulous->setup;
+
+{
+    ok($neb->create_object("foo", 99));
+}
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo", "~99");
+};
+like($@, qr/is not a valid volume name/, "volume name doesn't exist");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("neb://~99/foo");
+};
+like($@, qr/is not a valid volume name/, "volume name doesn't exist");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object();
+};
+like($@, qr/1 - 2 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object(1, "node01", 3);
+};
+like($@, qr/1 - 2 were expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/04_server_replicate_object.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/04_server_replicate_object.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/04_server_replicate_object.t	(revision 22322)
@@ -0,0 +1,185 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 04_server_replicate_object.t,v 1.16 2008-09-11 22:35:52 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 26;
+
+use lib qw( ./t ./lib );
+
+use File::ExtAttr qw( getfattr );
+use Nebulous::Server;
+use Test::Nebulous;
+use Test::URI;
+use URI::Split qw( uri_split );
+
+my $test_xattr = undef;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+{
+    # key
+    $neb->create_object("foo");
+    my $uri = $neb->replicate_object("foo");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, $volume
+    $neb->create_object("foo");
+    my $uri = $neb->replicate_object("foo", "node01");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, $volume is undef
+    $neb->create_object("foo");
+    my $uri = $neb->replicate_object("foo", undef);
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "file exists");
+    uri_scheme_ok($uri, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+    is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, $volume
+    $neb->create_object("foo");
+    my $uri1 = $neb->replicate_object("foo", "~node01");
+    my $uri2 = $neb->replicate_object("foo", "~node02");
+
+    {
+        my ($scheme, $auth, $path, $query, $frag) = uri_split($uri1);
+        ok(-e $path, "file exists");
+        uri_scheme_ok($uri1, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+        is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+    }
+
+    {
+        my ($scheme, $auth, $path, $query, $frag) = uri_split($uri2);
+        ok(-e $path, "file exists");
+        uri_scheme_ok($uri2, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+        is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+    }
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, $volume
+    $neb->create_object("foo");
+    my $uri1 = $neb->replicate_object("foo", "any");
+
+    {
+        my ($scheme, $auth, $path, $query, $frag) = uri_split($uri1);
+        ok(-e $path, "file exists");
+        uri_scheme_ok($uri1, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+        is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+    }
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, $volume
+    $neb->create_object("foo");
+    my $uri1 = $neb->replicate_object("foo", "~any");
+
+    {
+        my ($scheme, $auth, $path, $query, $frag) = uri_split($uri1);
+        ok(-e $path, "file exists");
+        uri_scheme_ok($uri1, 'file');
+
+SKIP: {
+    skip "requires xattr support", 1 unless $test_xattr;
+        is(getfattr($path, 'user.nebulous_key'), 'foo', 'user.nebulous_key xattr');
+}
+    }
+}
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->replicate_object('foo');
+};
+like($@, qr/is valid object key/, 'storage object does not exist');
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+    ok($neb->replicate_object('foo', 'bar'),'soft fake storage volume');
+};
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+    $neb->replicate_object('foo', '~bar');
+};
+like($@, qr/is not a valid volume name/, 'storage volume does not exist');
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->replicate_object();
+};
+like($@, qr/1 - 2 were expected/, 'no params');
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+    $neb->replicate_object('foo', 'node01', 3);
+};
+like($@, qr/1 - 2 were expected/, 'too many params');
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/05_server_lock_object.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/05_server_lock_object.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/05_server_lock_object.t	(revision 22322)
@@ -0,0 +1,126 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 05_server_lock_object.t,v 1.10 2008-03-20 21:10:14 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 13;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+{
+    $neb->create_object("foo");
+
+    ok($neb->lock_object("foo", "read"), "read lock");
+}
+
+Test::Nebulous->setup;
+
+{
+    $neb->create_object("foo");
+
+    ok($neb->lock_object("foo", "read"), "read lock");
+    ok($neb->lock_object("foo", "read"), "read lock");
+}
+
+Test::Nebulous->setup;
+
+{
+    $neb->create_object("foo");
+
+    ok($neb->lock_object("foo", "write"), "write lock");
+}
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->lock_object("foo", "read");
+};
+like($@, qr/is valid object key/, "storage object does not exist");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->lock_object("foo", "write");
+};
+like($@, qr/is valid object key/, "storage object does not exist");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->lock_object("foo", "write");
+    $neb->lock_object("foo", "write");
+};
+like($@, qr/can not write lock twice/, "can not write lock twice");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->lock_object("foo", "read");
+    $neb->lock_object("foo", "write");
+};
+like($@, qr/can not write lock after read lock/, "can not write lock after read lock");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->lock_object("foo", "write");
+    $neb->lock_object("foo", "read");
+};
+like($@, qr/can not read lock after write lock/, "can not read lock after write lock");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->lock_object();
+};
+like($@, qr/2 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->lock_object("foo");
+};
+like($@, qr/2 were expected/, "not enough params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->lock_object("foo", "both");
+};
+like($@, qr/is read or write/, "not read or write");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->lock_object("foo", 'read', 3);
+};
+like($@, qr/2 were expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/06_server_unlock_object.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/06_server_unlock_object.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/06_server_unlock_object.t	(revision 22322)
@@ -0,0 +1,141 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 06_server_unlock_object.t,v 1.10 2008-03-20 21:10:14 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 14;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+{
+    $neb->create_object("foo");
+
+    $neb->lock_object("foo", "read");
+
+    ok($neb->unlock_object("foo", "read"), "read unlock");
+}
+
+Test::Nebulous->setup;
+
+{
+    $neb->create_object("foo");
+
+    $neb->lock_object("foo", "read");
+    $neb->lock_object("foo", "read");
+
+    ok($neb->unlock_object("foo", "read"), "read unlock");
+    ok($neb->unlock_object("foo", "read"), "read unlock");
+}
+
+Test::Nebulous->setup;
+
+{
+    $neb->create_object("foo");
+
+    $neb->lock_object("foo", "write");
+
+    ok($neb->unlock_object("foo", "write"), "write unlock");
+}
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->unlock_object("foo", "read");
+};
+like($@, qr/is valid object key/, "storage object does not exist");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->unlock_object("foo", "write");
+};
+like($@, qr/is valid object key/, "storage object does not exist");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->unlock_object("foo", "read");
+};
+like($@, qr/can not remove non-existant read lock/, "no lock set");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->unlock_object("foo", "write");
+};
+like($@, qr/can not remove non-existant write lock/, "no lock set");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+    $neb->lock_object("foo", "write");
+
+    $neb->unlock_object("foo", "read");
+};
+like($@, qr/can not have a read lock under a write lock/, "read unlock under write lock");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+    $neb->lock_object("foo", "read");
+
+    $neb->unlock_object("foo", "write");
+};
+like($@, qr/can not have a write lock under a read lock/, "write unlock under read lock");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->unlock_object();
+};
+like($@, qr/2 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->unlock_object("foo");
+};
+like($@, qr/2 were expected/, "not enough params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->unlock_object("foo", "both");
+};
+like($@, qr/is read or write/, "not read or write");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->unlock_object("foo", 'read', 3);
+};
+like($@, qr/2 were expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/07_server_find_instances.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/07_server_find_instances.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/07_server_find_instances.t	(revision 22322)
@@ -0,0 +1,142 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 07_server_find_instances.t,v 1.16 2008-09-11 22:35:52 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 19;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::URI;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("foo");
+
+    my $locations = $neb->find_instances("foo");
+
+    uri_scheme_ok($locations->[0], 'file');
+    is($uri, $locations->[0], "URIs match");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri1 = $neb->create_object("foo");
+    my $uri2 = $neb->replicate_object("foo");
+
+    my $locations = $neb->find_instances("foo");
+
+    uri_scheme_ok($locations->[0], 'file');
+    uri_scheme_ok($locations->[1], 'file');
+    ok(eq_set([$uri1, $uri2], $locations), "URIs match");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume
+    my $uri = $neb->create_object('foo', 'node01');
+
+    my $locations = $neb->find_instances('foo', 'node01');
+
+    uri_scheme_ok($locations->[0], 'file');
+    is($uri, $locations->[0], "URIs match");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume
+    my $uri = $neb->create_object('foo', 'node01');
+
+    my $locations = $neb->find_instances('foo', undef);
+
+    uri_scheme_ok($locations->[0], 'file');
+    is($uri, $locations->[0], "URIs match");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume
+    my $uri = $neb->create_object('foo', 'node01');
+
+    my $locations = $neb->find_instances('neb://node02/foo', "~any");
+
+    uri_scheme_ok($locations->[0], 'file');
+    is($uri, $locations->[0], "URIs match");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri1 = $neb->create_object("foo");
+    my $uri2 = $neb->replicate_object("foo");
+
+    my $locations = $neb->find_instances("foo", "~any");
+
+    uri_scheme_ok($locations->[0], 'file');
+    uri_scheme_ok($locations->[1], 'file');
+    ok(eq_set([$uri1, $uri2], $locations), "URIs match");
+}
+
+# object exists but instance is on a different volume
+Test::Nebulous->setup;
+
+eval {
+    # key, volume
+    my $uri = $neb->create_object('foo', '~node01');
+
+    my $locations = $neb->find_instances('foo', '~node02');
+};
+like($@, qr/no instances on storage volume/, 'instances on a different volume');
+
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->find_instances('foo');
+};
+like($@, qr/is valid object key/, "storage object does not exist");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+    $neb->find_instances('foo', '~bar');
+};
+like($@, qr/is not a valid volume name/, "storage volume does not exist");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->find_instances();
+};
+like($@, qr/1 - 2 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+    $neb->find_instances('foo', 'node01', 3);
+};
+like($@, qr/1 - 2 were expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/08_server_delete_instance.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/08_server_delete_instance.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/08_server_delete_instance.t	(revision 22322)
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 08_server_delete_instance.t,v 1.10 2008-03-20 21:10:14 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 8;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+{
+    my $uri = $neb->create_object("foo");
+
+    ok($neb->delete_instance($uri), "delete instance");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $uri1 = $neb->create_object("foo");
+    my $uri2 = $neb->replicate_object("foo");
+
+    ok($neb->delete_instance($uri1), "delete instance");
+
+    my $locations = $neb->find_instances("foo");
+
+    is($locations->[0], $uri2, "instance remains");
+
+    ok($neb->delete_instance( $uri2 ), "delete instance");
+
+    eval {
+        $neb->find_instances("foo");
+    };
+    like($@, qr/is valid object key/, "storage object was deleted");
+}
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->delete_instance("file:/foo");
+};
+like($@, qr/no instance is associated with uri/, "uri does not exist");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->delete_instance();
+};
+like($@, qr/1 was expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->delete_instance("foo", 2);
+};
+like($@, qr/1 was expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/09_server_stat_object.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/09_server_stat_object.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/09_server_stat_object.t	(revision 22322)
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 09_server_stat_object.t,v 1.15 2008-05-16 20:29:19 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 12;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+{
+    $neb->create_object("foo");
+
+    my $info = $neb->stat_object("foo");
+
+    is(scalar @$info, 7, "number of columns");
+}
+
+Test::Nebulous->setup;
+
+{
+    $neb->create_object("foo", "node01");
+
+    my $info = $neb->stat_object("foo");
+
+    is(scalar @$info, 7,                       "number of columns");
+    is(@$info[0], 1,                           "so_id");
+    is(@$info[1], "foo",                       "ext_id");
+    is(@$info[2], 0,                           "read lock");
+    is(@$info[3], undef,                       "write lock");
+    like(@$info[4], qr/....-..-.. ..:..:../,   "epoch");
+    like(@$info[5], qr/....-..-.. ..:..:../,   "mtime");
+    is(@$info[6], 1,                           "instances");
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $stat = $neb->stat_object("foo");
+};
+like($@, qr/is valid object key/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->stat_object();
+};
+like($@, qr/1 was expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+    $neb->stat_object("foo", 2);
+};
+like($@, qr/1 was expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/10_server_is_valid_volume_name.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/10_server_is_valid_volume_name.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/10_server_is_valid_volume_name.t	(revision 22322)
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 10_server_is_valid_volume_name.t,v 1.5 2008-03-20 21:10:14 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 3;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+ok($neb->_is_valid_volume_name('node01'), "valid volume name");
+
+Test::Nebulous->setup;
+
+ok($neb->_is_valid_volume_name('node02'), "valid volume name");
+
+Test::Nebulous->setup;
+
+is($neb->_is_valid_volume_name('node99'), undef, "invalid volume name");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/11_server_is_valid_object_key.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/11_server_is_valid_object_key.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/11_server_is_valid_object_key.t	(revision 22322)
@@ -0,0 +1,37 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 11_server_is_valid_object_key.t,v 1.3 2008-03-20 21:10:14 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 2;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+{
+    $neb->create_object('foo');
+
+    ok($neb->_is_valid_object_key('foo'), "valid object key");
+}
+
+Test::Nebulous->setup;
+
+{
+    ok(!$neb->_is_valid_object_key('foo'), "invalid object key");
+}
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/12_server_find_objects.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/12_server_find_objects.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/12_server_find_objects.t	(revision 22322)
@@ -0,0 +1,66 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 12_server_find_objects.t,v 1.4 2008-05-16 20:29:19 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 6;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+# search for a regex of '' should match nothing
+eval {
+    # key
+    my $uri = $neb->create_object("foo");
+
+    my $keys = $neb->find_objects();
+};
+like($@, qr/no keys found/, "no keys found");
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri = $neb->create_object("foo");
+
+    my $keys = $neb->find_objects("foo");
+
+    is(scalar @$keys, 1, 'number of keys found');
+    is($keys->[0], "foo", "key name");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $uri1 = $neb->create_object("foo");
+    my $uri2 = $neb->replicate_object("foo");
+
+    my $keys = $neb->find_objects("foo");
+
+    is(scalar @$keys, 1, 'number of keys found');
+    is($keys->[0], "foo", "key name");
+}
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->find_objects("foo", 3);
+};
+like($@, qr/1 was expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/13_server_rename_object.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/13_server_rename_object.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/13_server_rename_object.t	(revision 22322)
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2007  Joshua Hoblitt
+#
+# $Id: 13_server_rename_object.t,v 1.5 2008-05-16 20:29:19 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 6;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+{
+    my $uri = $neb->create_object("foo");
+
+    $neb->rename_object("foo", "bar");
+
+    eval {
+        $neb->find_objects('^foo$');
+    };
+    like($@, qr/no keys found/, "old key name");
+
+    my $keys = $neb->find_objects('^bar$');
+    is(scalar @$keys, 1, 'number of keys found');
+}
+
+Test::Nebulous->setup;
+
+# destination key exists
+eval {
+    $neb->create_object('foo');
+    $neb->create_object('bar');
+
+    $neb->rename_object('foo', 'bar');
+};
+like($@, qr/is not valid object key/, "too few params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->rename_object();
+};
+like($@, qr/2 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->rename_object("foo");
+};
+like($@, qr/2 were expected/, "too few params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object("foo");
+
+    $neb->rename_object("foo", "bar", "baz");
+};
+like($@, qr/2 were expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/14_server_xattr.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/14_server_xattr.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/14_server_xattr.t	(revision 22322)
@@ -0,0 +1,214 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2007  Joshua Hoblitt
+#
+# $Id: 14_server_xattr.t,v 1.8 2008-07-09 23:32:35 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 36;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+# 1 key / xattr
+
+Test::Nebulous->setup;
+
+{
+    my $uri = $neb->create_object('foo');
+
+    ok($neb->setxattr_object('foo', 'bar', 'baz', 'create'), 'set object xattr');
+    {
+        my $xattrs = $neb->listxattr_object('foo');
+        is(scalar @$xattrs, 1, 'number of xattrs');
+        is(@$xattrs[0], 'bar', 'xattr name');
+    }
+
+    my $value = $neb->getxattr_object('foo', 'bar');
+    is($value, 'baz', 'xattr value');
+
+    ok($neb->removexattr_object('foo', 'bar'), "remove object xattr");
+    {
+        my $xattrs = $neb->listxattr_object('foo');
+        is(scalar @$xattrs, 0, 'number of xattrs');
+    }
+}
+
+# multiple xattrs
+
+Test::Nebulous->setup;
+
+{
+    my $uri = $neb->create_object('foo');
+
+    ok($neb->setxattr_object('foo', 'bar', 'baz', 'create'), 'set object xattr');
+    ok($neb->setxattr_object('foo', 'bonk', 'quix', 'create'), 'set object xattr');
+    
+    {
+        my $xattrs = $neb->listxattr_object('foo');
+        is(scalar @$xattrs, 2, 'number of xattrs');
+        is(@$xattrs[0], 'bar', 'xattr name');
+        is(@$xattrs[1], 'bonk', 'xattr name');
+    }
+
+    my $value = $neb->getxattr_object('foo', 'bar');
+    is($value, 'baz', 'xattr value');
+    $value = $neb->getxattr_object('foo', 'bonk');
+    is($value, 'quix', 'xattr value');
+
+    ok($neb->removexattr_object('foo', 'bar'), "remove object xattr");
+    ok($neb->removexattr_object('foo', 'bonk'), "remove object xattr");
+    {
+        my $xattrs = $neb->listxattr_object('foo');
+        is(scalar @$xattrs, 0, 'number of xattrs');
+    }
+}
+
+# replace xattrs
+
+Test::Nebulous->setup;
+
+{
+    my $uri = $neb->create_object('foo');
+
+    ok($neb->setxattr_object('foo', 'bar', 'baz', 'create'), 'set object xattr');
+    ok($neb->setxattr_object('foo', 'bar', 'quix', 'replace'), 're-set object xattr');
+    
+    {
+        my $xattrs = $neb->listxattr_object('foo');
+        is(scalar @$xattrs, 1, 'number of xattrs');
+        is(@$xattrs[0], 'bar', 'xattr name');
+    }
+
+    my $value = $neb->getxattr_object('foo', 'bar');
+    is($value, 'quix', 'xattr value');
+
+    ok($neb->removexattr_object('foo', 'bar'), "remove object xattr");
+    {
+        my $xattrs = $neb->listxattr_object('foo');
+        is(scalar @$xattrs, 0, 'number of xattrs');
+    }
+}
+
+# setxattr_object
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->setxattr_object('foo', 'bar', 'baz', 'create');
+};
+like($@, qr/is valid object key/, "create xattr on non-existant key");
+
+eval {
+    $neb->setxattr_object('foo', 'bar', 'baz', 'replace');
+};
+like($@, qr/is valid object key/, "replace xattr on non-existant key");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->setxattr_object();
+};
+like($@, qr/4 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+
+    $neb->setxattr_object('foo', 'bar');
+};
+like($@, qr/4 were expected/, "too few params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+
+    $neb->setxattr_object('foo', 'bar', 'baz');
+};
+like($@, qr/4 were expected/, "too few params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+
+    $neb->setxattr_object('foo', 'bar', 'baz', 'create', 'quix');
+};
+like($@, qr/4 were expected/, "too many params");
+
+# getxattr_object
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->getxattr_object('foo', 'bar');
+};
+like($@, qr/is valid object key/, "get xattr from non-existant nebulous key");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+    $neb->getxattr_object('foo', 'bar');
+};
+like($@, qr|xattr neb:///foo:bar does not exist|,
+    "get xattr from non-existant xattr key");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->getxattr_object();
+};
+like($@, qr/2 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+
+    $neb->getxattr_object('foo');
+};
+like($@, qr/2 were expected/, "too few params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+
+    $neb->getxattr_object('foo', 'bar', 'baz');
+};
+like($@, qr/2 were expected/, "too many params");
+
+# listxattr_object
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+
+    $neb->listxattr_object();
+};
+like($@, qr/1 was expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create_object('foo');
+
+    $neb->listxattr_object('foo', 'bar');
+};
+like($@, qr/1 was expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/15_mounts.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/15_mounts.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/15_mounts.t	(revision 22322)
@@ -0,0 +1,67 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2008  Joshua Hoblitt
+#
+# $Id: 15_mounts.t,v 1.3 2008-09-11 22:35:52 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More tests => 18;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Server;
+use Test::URI;
+use Test::Nebulous;
+
+my $neb = Nebulous::Server->new(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+);
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $mounts = $neb->mounts();
+
+    is(scalar @$mounts, 7, "number of rows");
+
+    my %row;
+    # first row
+    @row{qw(mountpoint total used vol_id name host path allocate available xattr)}
+        = @{$mounts->[0]};
+    
+    is($row{total},     100000000000);
+    is($row{used},      100000000);
+    is($row{vol_id},    1);
+    is($row{name},      "node01");
+    is($row{host},      "node01");
+    is($row{allocate},  1);
+    is($row{available}, 1);
+    is($row{xattr},     0);
+
+    # 2nd row
+    @row{qw(mountpoint total used vol_id name host path allocate available xattr)}
+        = @{$mounts->[1]};
+
+    is($row{total},     100000000000);
+    is($row{used},      1000000000);
+    is($row{vol_id},    2);
+    is($row{name},      "node02");
+    is($row{host},      "node02");
+    is($row{allocate},  1);
+    is($row{available}, 1);
+    is($row{xattr},     0);
+}
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->mounts("foo");
+};
+like($@, qr/0 were expected/, "no params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/75_parse_neb_key.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/75_parse_neb_key.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous-Server/t/75_parse_neb_key.t	(revision 22322)
@@ -0,0 +1,268 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 75_parse_neb_key.t,v 1.5 2008-09-09 02:18:47 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Test::More;
+
+plan tests => 80;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Keys qw( parse_neb_key );
+use Test::Nebulous;
+
+# neb://<volume name>/...
+# neb:path...
+# neb:/path... (same as neb:path, leading '/' is stripped)
+# neb:///path... (same as neb:path)
+
+# key
+{
+    my $key = parse_neb_key('foo/bar/baz/quix');
+
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('/foo/bar/baz/quix');
+
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('//foo/bar/baz/quix');
+
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('///foo/bar/baz/quix');
+
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('foo////bar/baz/quix');
+
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('foo/bar/baz/quix/');
+
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+# key w/ volume argument
+{
+    my $key = parse_neb_key('foo/bar/baz/quix', 'boing');
+
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->volume, 'boing', 'volume name');
+    is($key->soft_volume, 1, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('foo/bar/baz/quix', '~boing');
+
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->volume, 'boing', 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+# URI w/ volume name
+{
+    my $key = parse_neb_key('neb://foo/bar/baz/quix');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, 'foo', 'volume name');
+    is($key->soft_volume, 1, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('neb://foo//bar/baz/quix');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, 'foo', 'volume name');
+    is($key->soft_volume, 1, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('neb://foo///bar/baz/quix');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, 'foo', 'volume name');
+    is($key->soft_volume, 1, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('neb://foo/bar///baz/quix');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, 'foo', 'volume name');
+    is($key->soft_volume, 1, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('neb://foo/bar/baz/quix/');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, 'foo', 'volume name');
+    is($key->soft_volume, 1, 'soft volume name');
+}
+
+# URI w/ hard volume name
+{
+    my $key = parse_neb_key('neb://~foo/bar/baz/quix/');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, 'foo', 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+# URI w/ volume name and volume argument
+{
+    my $key = parse_neb_key('neb://foo/bar/baz/quix', 'boing');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, 'boing', 'volume name');
+    is($key->soft_volume, 1, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('neb://foo/bar/baz/quix', '~boing');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, 'boing', 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('neb://foo/bar/baz/quix', undef);
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, 'foo', 'volume name');
+    is($key->soft_volume, 1, 'soft volume name');
+}
+
+# URI w/ ~any volume name
+{
+    my $key = parse_neb_key('neb://~any/bar/baz/quix/');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+# URI w/ any volume argument
+{
+    my $key = parse_neb_key('neb://boing/bar/baz/quix/', 'any');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('neb://boing/bar/baz/quix/', '~any');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+# URI w/ hard volume name
+{
+    my $key = parse_neb_key('neb://~foo/bar/baz/quix/');
+
+    is($key->path, 'bar/baz/quix', 'path');
+    is($key->volume, 'foo', 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+# URI w/o volume name
+
+{
+    my $key = parse_neb_key('neb:/foo/bar/baz/quix');
+
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('neb:///foo/bar/baz/quix');
+
+    is($key->volume, undef, 'volume name');
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+{
+    my $key = parse_neb_key('neb://///foo/bar/baz/quix');
+
+    is($key->path, 'foo/bar/baz/quix', 'path');
+    is($key->volume, undef, 'volume name');
+    is($key->soft_volume, undef, 'soft volume name');
+}
+
+# key w/ whitespace
+eval {
+    parse_neb_key('/ foo/bar/baz/quix');
+};
+like( $@, qr/may not contain whitespace/, "whitespace" );
+
+eval {
+    parse_neb_key(' /foo/bar/baz/quix');
+};
+like( $@, qr/may not contain whitespace/, "whitespace" );
+
+eval {
+    parse_neb_key('/foo/bar/baz/quix ');
+};
+like( $@, qr/may not contain whitespace/, "whitespace" );
+
+# URI w/ whitespace
+
+eval {
+    parse_neb_key('neb ://foo/bar/baz/quix');
+};
+like( $@, qr/may not contain whitespace/, "whitespace" );
+
+eval {
+    parse_neb_key('neb:// foo/bar/baz/quix');
+};
+like( $@, qr/may not contain whitespace/, "whitespace" );
+
+eval {
+    parse_neb_key(' neb://foo/bar/baz/quix');
+};
+like( $@, qr/may not contain whitespace/, "whitespace" );
+
+eval {
+    parse_neb_key('neb://foo/bar/baz/quix ');
+};
+like( $@, qr/may not contain whitespace/, "whitespace" );
+
+# URI w/o volume requires leading slash
+eval {
+    my $key = parse_neb_key('neb:foo/bar/baz/quix');
+};
+like( $@, qr/requires a leading slash/, "leading slash" );
Index: /tags/sj_tags/sj_root_20080929/Nebulous/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Build
+META.yml
+Makefile
+Makefile.PL
+_build
+blib
Index: /tags/sj_tags/sj_root_20080929/Nebulous/Build.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/Build.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/Build.PL	(revision 22322)
@@ -0,0 +1,123 @@
+use Module::Build;
+# See perldoc Module::Build for details of how this works
+
+my $build_pkg   = eval { require Apache::TestMB }
+                ? 'Apache::TestMB'
+                : 'Module::Build';
+
+my $class = $build_pkg->subclass(code => <<'EOF');
+use Cwd;
+
+my $pkg_dir = "./nebclient";
+
+sub ACTION_code {
+    my $self = shift;
+
+    $self->SUPER::ACTION_code(@_);
+
+    my $old_pwd = getcwd();
+    chdir $pkg_dir;
+
+    unless (-e "configure") {
+        system("./autogen.sh") == 0 or die "install failed: $?";
+    }
+
+    chdir $old_pwd;
+}
+
+sub ACTION_build {
+    my $self = shift;
+
+    $self->SUPER::ACTION_build(@_);
+
+    my $old_pwd = getcwd();
+    chdir $pkg_dir;
+
+    unless (-e "Makefile") {
+        system("sh ./configure") == 0 or die "build failed: $?";
+    }
+
+    system("make") == 0 or die "build failed: $?";
+
+    chdir $old_pwd;
+}
+
+# Do not attempt to install nebclient.  It is bundled in this package for
+# testing only.
+#
+# sub ACTION_install {
+#     my $self = shift;
+# 
+#     $self->SUPER::ACTION_install(@_);
+# 
+#     my $old_pwd = getcwd();
+#     chdir $pkg_dir;
+# 
+#     system("make install") == 0 or die "install failed: $?";
+# 
+#     chdir $old_pwd;
+# }
+
+sub ACTION_clean {
+    my $self = shift;
+
+    $self->SUPER::ACTION_clean(@_);
+
+    my $old_pwd = getcwd();
+    chdir $pkg_dir;
+
+    system("make clean") == 0 or die "install failed: $?";
+
+    chdir $old_pwd;
+}
+EOF
+
+$class->new(
+    module_name         => 'Nebulous',
+    dist_version_from   => 'lib/Nebulous/Client.pm',
+    author              => 'Joshua Hoblitt <jhoblitt@cpan.org>',
+    license             => 'gpl',
+    create_makefile_pl  => 'passthrough',
+    requires            => {
+        'Digest::MD5'           => 0,
+        'File::Copy'            => 0,
+        'Time::HiRes'           => 0,
+        'Class::Accessor::Fast' => 0,
+        'File::Spec'            => 0,
+        'File::Spec::Functions' => 0,
+        'Log::Log4perl'         => '0.48',
+        'Params::Validate'      => '0.73',
+        'SOAP::Lite'            => '0.69',
+        'URI'                   => '1.30',
+        'Getopt::Long'          => 0,
+        'Pod::Usage'            => 0,
+    },
+    recommends          => {
+        'Nebulous::Server::Apache'  => 0,
+        'Nebulous::Server'          => 0,
+        'Nebulous::Server::SOAP'    => 0,
+        'Test::Nebulous'            => 0,
+        'Apache2::SOAP'             => 0,
+        'Apache::DBI'               => '1.05',
+        'Apache::Test'              => '1.27',  # bundles Apache::TestMB
+        'Apache::TestMB'            => 0,
+        'Test::More'                => '0.49',
+        'Test::URI'                 => '1.06',
+        'Test::Distribution'        => '1.22',
+    },
+    script_files        => [qw(
+        bin/neb-cat
+        bin/neb-copies
+        bin/neb-cp
+        bin/neb-cull
+        bin/neb-df
+        bin/neb-locate
+        bin/neb-ls
+        bin/neb-mv
+        bin/neb-replicate
+        bin/neb-rm
+        bin/neb-stat
+        bin/neb-touch
+        bin/neb-xattr
+    )],
+)->create_build_script;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/Changes
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/Changes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/Changes	(revision 22322)
@@ -0,0 +1,150 @@
+Revision history for Nebulous
+
+0.10
+    - allow ->replicate() volume param to be undef
+    - fix ->delete() still work if the neb implies a volume on which there is
+      no instance
+    - retry all calls to open()
+
+0.09 Wed Jul  9 16:36:27 HST 2008
+    - change "any" volume usage to corespond to changes in Nebulous::Server
+    - add neb-replicate --copies option
+    - fix neb-stat to work when a neb key is used that implies a storage volume
+      that does not have instances on it
+    - make neb-ls non-recursive by default and add a --recursive flag
+    - add neb-copies util
+    - move print_all_xattrs(), print_xattrs(), write_xattrs(), delete_xattrs(),
+      and parse_xattr_pair() from neb-xattr into Nebulous::Util
+    - change replicate() to report when it fails to cleanup (after a failure)
+    - add neb-xattr util
+    - change Nebulous::Client->find() to handle a volume param and to fall back
+      to searching on the ':any' volume if the first search fails
+    - change open_create() to attempt to open the instance file 10 times before
+      failing
+    - only send fatal errors to the screen
+    - send log messages to stderr instead of stdout
+    - overhaul SOAP fault message handling
+    - suppress non-existant key errors from Nebulous::Client->find_instances()
+    - in Nebulous::Client->stat(), handle the SOAP quirk of thinking an "undef"
+      return is a fault (even when no fault code is set).
+    - add ->setxattr_object(), ->getxattr_object(), ->listxattr_object(),
+      ->removexattr_object()
+    - change ->create() to accept volume == undef
+    - modernize neb-touch API calls
+    - change Nebulous::Client->replicate() to check the md5sum of the
+      replicated file and to not leave empty instances laying around if there
+      is a failure
+    - add a virtual "summary" volume to neb-df
+    - fix neb-df coding issues
+    - set default logging level to warn
+
+0.08 Thu Apr 17 14:07:18 HST 2008
+    - add Nebulous::Client::open() '>' (truncate) mode
+
+0.07
+    - modify neb-df to use Nebulous::Client::mounts()
+    - add Nebulous::Client::mounts()
+    - server/client split into Nebulous::Server and Nebulous::Client packages
+    - add support for storing instances in multi-tier 'hashed' directories 
+    - pickup test DB settings from the env.
+    - add neb-ls -l|-1 option
+    - add neb-fsck
+    - add neb-stat
+    - add additional fkey constrains to the instance table
+    - fix installation of neb-replicate & neb-cull
+    - redo database schema with foreign keys
+    - add Nebulous::Keys pod
+
+0.06 Fri Jan 25 10:55:59 HST 2008
+    - define Nebulous::Client::find_objects() as returning [] when NO keys are
+      found
+    - change neb-touch to use Nebulous::Client::find_objects() instead of
+      ::find()
+    - add Nebulous::Sever:: neb key/uri support
+    - add Nebulous::Util::parse_neb_key()
+    - change bin/* scripts to conistently use --volume instead of --node,
+      etc.
+    - pod cleanups
+    - add neb-replicate & neb-cull
+
+0.05 2007-05-04 16:59:31(???)
+    - make filesystem xattr support optional & disabled by default
+    - generate instance filenames that leave the "key" at the end so as to not
+      break a pplications that expect a certain "extension" on the filename
+    - set an xattr on all created files of "user.nebulous_key"
+    - add neb-locate
+    - change all Nebulous::Client public methods to behave in list context
+    - rework the getmountedvol() stored procedure to attempt to work around
+      what appears to be some nasty transacational isolation leak throught that
+      was causing getmountedvol() to randomly hang when nebdiskd changed the
+      mount table.
+    - fix a nasty logic bug in Nebulous::Client->replicate()
+    - break 1:1 relationship between key names and on disk file names
+    - overhaul Nebulous::Client->cull()
+    - add the number of instances to ->stat_object()
+    - SQL cleanup
+    - better paramter checking
+    - overhaul instance table
+    - add ->_is_valid_object_key() method
+    - change ->find_instances() to check it's params and only return instances
+      on mounted & available volumes
+    - add volume.available field and logic to use it
+    - add volume.allocate field and logic to use it
+    - add instance.vol_id field
+    - remove $class_id & $comment params from Nebulous::Server->create() and
+      Nebulous::Client->create*() and all supporting functions and tests
+    - add Nebulous::Server->{setxattr_object, listxattr_object,
+      getxattr_object, removexattr_object}()
+    - add neb-cat
+    - add neb-mv
+    - change Nebulous::Client->move() to use ->rename_object()
+    - add Nebulous::Server->rename_object()
+    - add neb-rm
+    - add neb-cp
+    - add neb-ls
+    - add Nebulous::Client->find_objects()
+    - add Nebulous::Server->find_objects()
+    - add neb-touch
+    - add neb-df
+    - change neb-addvol to properly mangle URIs into paths
+
+0.04 Mon Apr 23 13:33:16 HST 2007
+    - include autogen.sh in the tarball
+    - add nebdiskd
+    - [nebclient] VERSION to '0.0.3'
+    - change API to generally return true URIs instead of file paths
+    - add support for some what intelligent volume allocations
+    - minor code & build fixes, ws changes
+
+0.03 Mon Dec 11 14:19:45 HST 2006
+    - change Nebulous::Server instantiate objects (init -> new) instead of
+      working with class data
+    - add a DESTROY method to Nebulous::Server to tear down the database
+      connection
+    - add a Nebulous::Server->config() accessor
+    - make Nebulous::Server->db() Apache::DBI aware
+    - make SQL syntax MySQL 5 compatible
+    - update SOAP, DBI & DBD, and Apache::* deps
+    - change Nebulous::Server::SOAP to be mod_perl aware
+    - change Nebulous::Server::SOAP install stub methods to bypass AUTOLOAD on
+      successive calls to the same method
+    - make Nebulous::Server::Log mod_perl aware
+    - change Nebulous::Server::Log to accept a Nebulous::Server::Config object
+      as a parameter instead of fetching it as a singleton
+    - make Nebulous::Server::Config no longer a singleton
+
+0.02 Fri Dec  2 17:36:59 HST 2005
+    - recommend Test::Distribution 1.22
+    - add t/00_distribution.t
+    - rename test files
+    - [nebclient] VERSION to '0.0.2'
+    - Nebulous::Client, Nebulous::Server, & Nebulous::Server::SQL
+        VERSION to '0.02'
+    - add Nebulous::Client->open_create()
+    - change Nebulous::Client->create() to return just a filename
+    - [nebclient] add nebOpenCreate()
+    - [nebclient] change nebCreate() to return just a filename
+
+0.01 Fri Sep  3 10:23:31 2004
+    - first working version
+	- original version; created by ExtUtils::ModuleMaker 0.32
Index: /tags/sj_tags/sj_root_20080929/Nebulous/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/LICENSE	(revision 22322)
@@ -0,0 +1,260 @@
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
Index: /tags/sj_tags/sj_root_20080929/Nebulous/MANIFEST
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/MANIFEST	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/MANIFEST	(revision 22322)
@@ -0,0 +1,107 @@
+Build.PL
+Changes
+LICENSE
+MANIFEST
+README
+Todo
+autogen.sh
+bin/neb-cat
+bin/neb-copies
+bin/neb-cp
+bin/neb-cull
+bin/neb-df
+bin/neb-locate
+bin/neb-ls
+bin/neb-mv
+bin/neb-replicate
+bin/neb-rm
+bin/neb-stat
+bin/neb-touch
+bin/neb-xattr
+docs/c_api.h
+docs/database_setup.txt
+docs/design.txt
+docs/requirements.txt
+docs/setup.txt
+docs/tmp.txt
+examples/Makefile
+examples/nebexample.c
+examples/nebexample.pl
+lib/Nebulous/Client.pm
+lib/Nebulous/Client.pod
+lib/Nebulous/Client/HTTP.pm
+lib/Nebulous/Client/Log.pm
+lib/Nebulous/Client/QuickStart.pod
+lib/Nebulous/Util.pm
+nebclient/Doxyfile.in
+nebclient/Makefile.am
+nebclient/Makefile.in
+nebclient/aclocal.m4
+nebclient/autogen.sh
+nebclient/config.guess
+nebclient/config.h.in
+nebclient/config.sub
+nebclient/configure
+nebclient/configure.ac
+nebclient/depcomp
+nebclient/install-sh
+nebclient/ltmain.sh
+nebclient/missing
+nebclient/nebclient.pc.in
+nebclient/nebulous.wsdl
+nebclient/src/Makefile.am
+nebclient/src/Makefile.in
+nebclient/src/SOAP.nsmap
+nebclient/src/nebclient.c
+nebclient/src/nebclient.h
+nebclient/src/soapC.c
+nebclient/src/soapClient.c
+nebclient/src/soapH.h
+nebclient/src/soapStub.h
+nebclient/src/stdsoap2.c
+nebclient/src/stdsoap2.h
+nebclient/src/xmalloc.c
+nebclient/src/xmalloc.h
+nebclient/tests/tap/INSTALL
+nebclient/tests/tap/Makefile.am
+nebclient/tests/tap/Makefile.in
+nebclient/tests/tap/README
+nebclient/tests/tap/aclocal.m4
+nebclient/tests/tap/compile
+nebclient/tests/tap/configure
+nebclient/tests/tap/configure.in
+nebclient/tests/tap/src/Makefile.am
+nebclient/tests/tap/src/Makefile.in
+nebclient/tests/tap/src/config.h.in
+nebclient/tests/tap/src/tap.c
+nebclient/tests/tap/src/tap.h
+nebclient/tests/Makefile.am
+nebclient/tests/Makefile.in
+nebclient/tests/tests.c
+scripts/bench_test.pl
+scripts/nebulous.cgi
+scripts/ptest.pl
+t/00_distribution.t 
+t/01_load.t
+t/50_client_new.t
+t/51_client_create.t
+t/51_client_open_create.t
+t/52_client_replicate.t
+t/53_client_cull.t
+t/54_client_lock.t
+t/55_client_unlock.t
+t/56_client_find_instances.t
+t/57_client_find.t
+t/58_client_open.t
+t/59_client_delete.t
+t/60_client_copy.t
+t/61_client_move.t
+t/62_client_delete_instance.t
+t/63_client_stat.t
+t/64_client_find_objects.t
+t/65_client_mounts.t
+t/66_client_xattr.t
+t/90_nebclient.t
+t/TEST.PL
+t/conf/extra.conf.in
+t/conf/startup.pl.in
Index: /tags/sj_tags/sj_root_20080929/Nebulous/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/README	(revision 22322)
@@ -0,0 +1,16 @@
+pod2text Poi::PixelData.pm > README
+
+If this is still here it means the programmer was too lazy to create the readme file.
+
+You can create it now by using the command shown above from this directory.
+
+At the very least you should be able to use this set of instructions
+to install the module...
+
+perl Build.PL
+./Build
+./Build test
+./Build install
+
+
+If you are on a windows box you should use 'nmake' rather than 'make'.
Index: /tags/sj_tags/sj_root_20080929/Nebulous/Todo
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/Todo	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/Todo	(revision 22322)
@@ -0,0 +1,43 @@
+TODO list for Perl module Nebulous
+
+- support delete of instances from volumes that are offline
+- mode to replicate all files from a volume (reguardless of how many copies are requested)
+- "inverse" replication to remove extra copies
+- neb-replaceobj util for Paul (???)
+- add a means of listing all instances (online or not)
+
+- make nebclient lib thread safe
+- handle/check for storage volumes being offline
+- admin interface to control volumes (enable/disable, add/remove)
+- add zeroconf support
+- add support for SQL and/or Apache logging
+- make copy replicate source attributes, implement as replicate/reparent?
+- test that client->delete_instance removes the storage object too
+- review caching of database statement handles
+- code tiding/consistency
+- file creation retrying
+- server config file
+- make Apache::DBI behave
+- split SOAP out of Client.pm
+- Perl client error string support with DBI like 'AutoRaise' exceptions
+- CLI utilities
+- write neb-fsck utility
+- write Linux::statfs module so that statfs.f_bavail can be obtained (not available via df)
+- change neb-ls to have a limit number of keys shown (-n) param and default to
+  something reasonable (say 20)
+- add multiple regex and/or glob support to neb-ls, eg. neb-ls -r 'foo.*' -r 'bar$'
+- store the key name as an xattr on the actual on disk file
+- add an neb-locate --reverse option to map uri/paths into keys
+
+distant future
+--
+- cache some database tables (updated at some interval) 
+- volume size/% in-use discovery
+- server side per client lock tracking
+- per client authentication
+- client side lookup table of filehandles/locks
+- Perl only?, scoped lock release/filehandle destruction
+- restructure database so that storage objects are more like inodes
+- add support for directories
+- FUSE interface
+- hot-spot analysis
Index: /tags/sj_tags/sj_root_20080929/Nebulous/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/autogen.sh	(revision 22322)
@@ -0,0 +1,1 @@
+perl Build.PL -apxs /usr/sbin/apxs2 
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-cat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-cat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-cat	(revision 22322)
@@ -0,0 +1,156 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007  Joshua Hoblitt
+#
+# $Id: neb-cat,v 1.1 2007-04-28 02:52:13 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+use constant CHUNK_SIZE => 1024;
+
+my ($server);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Required options: --server <key...>", -exitval => 2 )
+        unless defined $server;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+unless (scalar @ARGV) {
+    no strict;
+    print_fh(STDIN);
+    use strict;
+}
+
+foreach my $key (@ARGV) {
+    my $fh;
+    # copy cat's behavior of treating '-' as stdin
+    if ($key eq '-') {
+        open($fh, '-') or warn "failed to open stdin" and next;
+    } else {
+        $fh = $neb->open( $key, 'read' )
+            or warn "failed to open key: $key" and next;
+    }
+
+    print_fh($fh);
+
+    close ($fh) or die "failed to close filehandle: $!";
+}
+
+sub print_fh
+{
+    my $fh = shift;
+
+    my $status;
+    while ($status = read($fh, my $buf, CHUNK_SIZE)) {
+            print $buf;
+    }
+
+    # read() returns undef on error
+    unless (defined $status) {
+        die "failed to read from filehandle: $!";
+    }
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-cat - concatenate one instance per Nebulous key and print on the standard output
+
+=head1 SYNOPSIS
+
+    neb-cat [--server <URL>] <key...>
+
+=head1 DESCRIPTION
+
+This program selects one instance per Nebulous keys given in C<<key...>> and
+conatenates them together then prints them on the standard output.  Note that
+just as with the C<cat> utility, C<-> can be used to read from the standard
+input.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-copies
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-copies	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-copies	(revision 22322)
@@ -0,0 +1,134 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2008  Joshua Hoblitt
+#
+# $Id: neb-copies,v 1.3 2008-06-28 00:24:08 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+use Nebulous::Util 0.02 qw( :standard );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($server, $delete, $write);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+) || pod2usage( 2 );
+
+my ($key, $value) = (shift, shift);
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+pod2usage( -msg => "missing nebulous key", exitval => 2 )
+    unless defined $key;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connect to Nebulous Server: $server"
+    unless defined $neb;
+
+# key with value write/overwrite user.copies:value
+if (defined $value) {
+    write_xattrs($neb, $key, "user.copies:$value") or die;
+    exit(0);
+}
+
+# key without value means print the value of user.copies.
+print_xattrs($neb, $key, "user.copies") or die;
+exit(0);
+
+
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-copies - get/set how many copies Nebulous should attempt to keep
+
+=head1 SYNOPSIS
+
+    neb-copies [--server <URL>] <key> [<n copies>]
+
+=head1 DESCRIPTION
+
+This program can retrieve or store the xattr attribute C<user.copies>.  If
+invoked with just C<<key>> it will print the value.  If invoked with C<<key>>
+and C<<value>> it will set the value.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Client>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-cp
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-cp	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-cp	(revision 22322)
@@ -0,0 +1,136 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2008  Joshua Hoblitt
+#
+# $Id: neb-cp,v 1.5 2008-01-22 21:40:39 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($volume, $server);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'volume=s'      => \$volume,
+    'server|s=s'    => \$server,
+) || pod2usage( 2 );
+
+my $src = shift;
+my $dst = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server <src key> <dst key>",
+        -exitval => 2 )
+        unless defined $server and defined $src and defined $dst;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+my $status;
+if ($volume) {
+    $status = $neb->copy( $src, $dst, $volume);
+} else {
+    $status = $neb->copy( $src, $dst);
+}
+
+warn "copy failied" unless $status;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-cp - list Nebulous keys
+
+=head1 SYNOPSIS
+
+    neb-cp [--server <URL>] [--volume <dst volume name>] <src key> <dst key>
+
+=head1 DESCRIPTION
+
+Copy a Nebulous storage key
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=item * -volume <volume name>
+
+The volume to place the new key's first storage instance on.
+
+Optional.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-cull
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-cull	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-cull	(revision 22322)
@@ -0,0 +1,134 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2008  Joshua Hoblitt
+#
+# $Id: neb-cull,v 1.1 2008-01-22 22:10:27 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($server, $volume);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+    'volume|v=s'    => \$volume,
+) || pod2usage( 2 );
+
+my $key = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+pod2usage( -msg => "missing key operand", exitval => 2 )
+    unless defined $key;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+if (defined $volume) {
+    $neb->cull($key, $volume)
+        or die "failed to replicate Nebulous key: $key";
+} else {
+    $neb->cull($key)
+        or die "failed to replicate Nebulous key: $key";
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-cull - remove an instance of an object
+
+=head1 SYNOPSIS
+
+    neb-cull [--server <URL>] [--volume <volume name>] <key>
+
+=head1 DESCRIPTION
+
+This program removes an instance of the Nebulous object specified by C<<key>>.  It will I<NOT> remove the last instance of an object (nor the object itself).
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=item * --volume <volume name>
+
+Symbolic name of the volume to create the new instance on.
+
+Optional.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-df
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-df	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-df	(revision 22322)
@@ -0,0 +1,203 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007  Joshua Hoblitt
+#
+# $Id: neb-df,v 1.6 2008-05-07 00:33:18 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($server);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+) || pod2usage( 2 );
+
+my $pattern = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+my $stats = $neb->mounts();
+
+# output a summary of the information in a vaguely POSIX df like format
+print "Filesystem           1K-blocks      Used Available Use% Mounted on\n";
+
+my $total_total = 0;
+my $total_used  = 0;
+foreach my $row (@$stats) {
+    my %vol;
+    @vol{qw(mountpoint total used vol_id name path allocate available xattr)}
+        = @$row;
+
+    my $path        = $vol{path};
+    my $total       = $vol{total};
+    $total_total    += $total;
+    my $used        = $vol{used};
+    $total_used     += $used;
+    my $available   = $total - $used;
+    my $usedper     = ($used / $total) * 100; 
+    my $mountpoint  = $vol{mountpoint};
+
+    # indicated the part of the nebulous volume name that matches up with a
+    # local volume (mountpoint) 
+    #$path =~ s/$mountpoint/[$mountpoint]/;
+
+    # printf formats taken from df.c in coreutils-6.7
+    # Copyright (C) 91, 1995-2006 Free Software Foundation, Inc.
+
+    if (length $path > 20) {
+        printf ("%s\n%20s", $path, "");
+    } else {
+        printf ("%-20s", $path);
+    }
+
+    printf(" %*s %*s %*s %*.0f%% %s\n",
+            9,
+            $total,
+            9,
+            $used,
+            9,
+            $available,
+            3,
+            $usedper,
+            $mountpoint,
+        );
+}
+
+my $total_available   = $total_total - $total_used;
+my $total_usedper     = ($total_used / $total_total) * 100; 
+
+# summary
+printf ("%s\n%20s", "/summmary_of_all_volumes", "");
+printf(" %*s %*s %*s %*.0f%% %s\n",
+        9,
+        $total_total,
+        9,
+        $total_used,
+        9,
+        $total_available,
+        3,
+        $total_usedper,
+        "/total",
+    );
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-df - provide a `df` like summary of nebulous volumes
+
+=head1 SYNOPSIS
+
+    neb-df [--db <database>] [--user <username>] [--pass <password>]
+
+=head1 DESCRIPTION
+
+This program outputs a list of Nebulous volumes and various metadata about
+them.  It attempts to conform to the format of data output by the GNU version
+of C<df> when passed the C<-k> flag. E.g. C<df -k>
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --db|-d <database>
+
+Name of database (C<namespace>) to retreive metadata from.
+
+Optional if the appropriate environment variable is set.
+
+=item * --user|-u <username>
+
+Username to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --pass|-p <password>
+
+Password to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_DB>
+
+Equivalent to --db|-d
+
+=item * C<NEB_USER>
+
+Equivalent to --user|-u
+
+=item * C<NEB_PASS>
+
+Equivalent to --pass|-p 
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-locate
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-locate	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-locate	(revision 22322)
@@ -0,0 +1,172 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2008  Joshua Hoblitt
+#
+# $Id: neb-locate,v 1.3 2008-01-22 21:14:55 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($all, $path, $server, $volume);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'all|a'         => \$all,
+    'path|p'        => \$path,
+    'server|s=s'    => \$server,
+    'volume=s'      => \$volume,
+) || pod2usage( 2 );
+
+my $key = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server <URL> <key>",
+        -exitval => 2 )
+        unless defined $server and defined $key;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+my $uris;
+if (defined $volume) {
+    $uris = $neb->find_instances($key, $volume);
+} else {
+    $uris = $neb->find_instances($key);
+}
+
+unless (defined $uris) {
+    warn "no instances found\n";
+    exit 1;
+}
+
+my @files;
+if (defined $all) { 
+    # print all instances
+    if (defined $path) {
+        @files = map {URI->new($_)->file if $_} @$uris;
+    } else {
+        @files = @$uris;
+    }
+} else {
+    # print just one
+    if (defined $path) {
+        @files = (URI->new(@$uris[0])->file);
+    } else {
+        @files = @$uris[0];
+    }
+}
+
+print join(" ", @files), "\n";
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-locate - list URIs|paths to Nebulous key instances
+
+=head1 SYNOPSIS
+
+    neb-locate [--server <URL>] [--volume <volume name>] [--all] [--path] <key>
+
+=head1 DESCRIPTION
+
+This program will lookup the URIs|paths associated with C<<key>> and list them. By default only one instance is listed.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --all|-a
+
+This flag causes all found instances to be listed.  By default only one (if any
+are found) is reported.
+
+Optional.
+
+=item * --path|-p
+
+This flag causes instances to be listed as C<paths> instead of C<URI>s.
+
+Optional.
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=item * --volume <volume name>
+
+Restrict search for instances to this Nebulous volume.
+
+Optional.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-ls
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-ls	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-ls	(revision 22322)
@@ -0,0 +1,155 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2008  Joshua Hoblitt
+#
+# $Id: neb-ls,v 1.7 2008-06-12 20:03:37 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($server, $long, $recursive);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+    'recursive|r'   => \$recursive,
+    'l|1'           => \$long,
+) || pod2usage( 2 );
+
+my $pattern = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+# default to listing everything (bad idea?)
+$pattern ||= ".*";
+
+if ($recursive) {
+    $pattern = "^" . $pattern . ".*";
+} else {
+    $pattern = "^" . $pattern . "\$";
+}
+
+my $keys = $neb->find_objects($pattern);
+
+if ($keys) {
+    if ($long) {
+        print join("\n", @{ $keys }), "\n";
+    } else {
+        print join(" ", @{ $keys }), "\n";
+    }
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-ls - list Nebulous keys
+
+=head1 SYNOPSIS
+
+    neb-ls [--server <URL>] [-l|-1] [--recursive] <pattern>
+
+=head1 DESCRIPTION
+
+This program list Nebulous keys matched by C<<pattern>>.  Call it with no
+arguments is equivalanet to searching with the pattern C<.*>.  Where
+C<<pattern>> is a POSIX 1003.2 compatable regular repression.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * -l|-1
+
+Use a long listing format.
+
+Optional
+
+=item * --recursive|-r
+
+By default C<neb-ls> will only try to match the exact string provided to it.
+With this option set all keys which match C<<pattern>> as a REGEX or substring
+will be returned.
+
+Optional
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-mv
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-mv	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-mv	(revision 22322)
@@ -0,0 +1,122 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007  Joshua Hoblitt
+#
+# $Id: neb-mv,v 1.1 2007-04-28 01:17:45 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($server);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+) || pod2usage( 2 );
+
+my $src = shift;
+my $dst = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server <src key> <dst key>",
+        -exitval => 2 )
+        unless defined $server and defined $src and defined $dst;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+$neb->move( $src, $dst ) or warn "move failed";
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-mv - move (rename) Nebulous keys
+
+=head1 SYNOPSIS
+
+    neb-mv [--server <URL>] <src key> <dst key>
+
+=head1 DESCRIPTION
+
+This program renames a Nebulous keys.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-replicate
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-replicate	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-replicate	(revision 22322)
@@ -0,0 +1,149 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2008  Joshua Hoblitt
+#
+# $Id: neb-replicate,v 1.2 2008-07-01 00:35:11 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($server, $volume, $copies);
+$copies = 1;  # default to making just one copy
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+    'volume|v=s'    => \$volume,
+    'copies|c=i'    => \$copies,
+) || pod2usage( 2 );
+
+my $key = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+pod2usage( -msg => "missing key", exitval => 2 )
+    unless defined $key;
+pod2usage( -msg => "--copies must be > 0", exitval => 2 )
+    unless $copies > 0;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+for (my $i = 0; $i < $copies; $i++) {
+    if (defined $volume) {
+        $neb->replicate($key, $volume)
+            or die "failed to replicate Nebulous key: $key";
+    } else {
+        $neb->replicate($key)
+            or die "failed to replicate Nebulous key: $key";
+    }
+}
+
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-replicate - create another instance of an object
+
+=head1 SYNOPSIS
+
+    neb-replicate [--server <URL>] [--volume <volume name>]
+                  [--copies <n>] <key>
+
+=head1 DESCRIPTION
+
+This program adds an additonal instance to the Nebulous object with a key of
+C<<key>>.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=item * --volume <volume name>
+
+Symbolic name of the volume to create the new instance on.
+
+Optional.
+
+=item * --copies|c <n>
+
+The number of new replications to create.
+
+Optional.  Defaults to 0.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-rm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-rm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-rm	(revision 22322)
@@ -0,0 +1,120 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007  Joshua Hoblitt
+#
+# $Id: neb-rm,v 1.2 2007-04-28 01:19:59 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($server);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Required options: --server <key...>", -exitval => 2 )
+        unless defined $server;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+foreach my $key (@ARGV) {
+    $neb->delete($key) or warn "failed to delete key: $key";
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-rm - remove Nebulous keys
+
+=head1 SYNOPSIS
+
+    neb-rm [--server <URL>] <key...>
+
+=head1 DESCRIPTION
+
+This program removes Nebulous keys in the whitespace seperated list
+C<<key...>>.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-stat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-stat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-stat	(revision 22322)
@@ -0,0 +1,137 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2008  Joshua Hoblitt
+#
+# $Id: neb-stat,v 1.3 2008-06-09 20:25:27 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($server);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'     => \$server,
+) || pod2usage( 2 );
+
+my $key = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+pod2usage( -msg => "missing key", exitval => 2 )
+    unless defined $key;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+my $stat = $neb->stat($key);
+die "nebulous key: $key not found" unless $stat;   
+my $instances = $neb->find_instances($key, ':any');
+die "no instances found" unless $instances;   
+
+no warnings qw(uninitialized);
+print
+    "object id:     ", @$stat[0], "\n",
+    "key:           ", @$stat[1], "\n",
+    "read lock:     ", @$stat[2], "\n",
+    "write lock:    ", @$stat[3], "\n",
+    "epoch:         ", @$stat[4], "\n",
+    "mtime:         ", @$stat[5], "\n",
+    "n instances:   ", @$stat[6], "\n",
+    "instance location:\n", " " x 4,
+    join("\n" . " " x 4, @$instances), "\n";
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-stat - display Nebulous key status
+
+=head1 SYNOPSIS
+
+    neb-stat [--server <URL>] <key>
+
+=head1 DESCRIPTION
+
+This program lists information about the Nebulous key C<<key>>.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-touch
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-touch	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-touch	(revision 22322)
@@ -0,0 +1,133 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2008  Joshua Hoblitt
+#
+# $Id: neb-touch,v 1.10 2008-05-13 23:19:55 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($server, $volume);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+    'volume=s'      => \$volume,
+) || pod2usage( 2 );
+
+my $key = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+pod2usage( -msg => "missing file operand", exitval => 2 )
+    unless defined $key;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+unless ($neb->stat($key)) {
+    my $fh = $neb->create($key, $volume)
+        or die "failed to create Nebulous key: $key";
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-touch - create an empty file
+
+=head1 SYNOPSIS
+
+    neb-touch [--server <URL>] [--volume <volume name>] <key>
+
+=head1 DESCRIPTION
+
+This program creates an empty file with the Nebulous key of C<<key>>.  At some
+point in the future it will be able to adjust the timestamp of storage
+instances assosiated with a Nebulous key similar to C<touch(1)>.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=item * --volume <volume name>
+
+Symbolic name of the volume to create the first instance on.
+
+Optional.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-xattr
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-xattr	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/bin/neb-xattr	(revision 22322)
@@ -0,0 +1,162 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2008  Joshua Hoblitt
+#
+# $Id: neb-xattr,v 1.9 2008-05-29 04:27:39 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+use Nebulous::Util 0.02 qw( :standard );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($server, $delete, $write);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+    'delete|d'      => \$delete,
+    'write|w'       => \$write,
+) || pod2usage( 2 );
+
+my $key = shift;
+
+#pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+pod2usage( -msg => "missing nebulous key", exitval => 2 )
+    unless defined $key;
+pod2usage( -msg => "--delete and --write can not be combined", exitval => 2 )
+    if defined $delete and defined $write;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connect to Nebulous Server: $server"
+    unless defined $neb;
+
+# -d flag means delete the list of xattrs from key
+if (defined $delete) {
+    die "--delete requires a list of xattr names" unless scalar @ARGV;
+    delete_xattrs($neb, $key, @ARGV) or die;
+    exit(0);
+}
+
+# key with xattr:value pair and -w flag means write/overwrite xattr:value
+if (defined $write) {
+    die "--write requires a list of xattr:value pairs" unless scalar @ARGV;
+    write_xattrs($neb, $key, @ARGV) or die;
+    exit(0);
+}
+
+# key without xattr name list means print all xattr:value pairs
+if (not scalar @ARGV) {
+    print_all_xattrs($neb, $key) or die;
+    exit(0);
+}
+
+# key with xattr name list means print the xattr:value pairs
+print_xattrs($neb, $key, @ARGV) or die;
+exit(0);
+
+
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-xattr - create an empty file
+
+=head1 SYNOPSIS
+
+    neb-xattr [--server <URL>] [--write] <key> [<xattr name>[:<value>]...]
+
+=head1 DESCRIPTION
+
+This program is used to create/update/list or remove xattrs on Nebulous storage objects.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=item * --delete 
+
+Delete all named xattrs.
+
+Optional.
+
+=item * --write
+
+Store the value of all xattr name:value pairs.
+
+Optional.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Client>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/docs/c_api.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/docs/c_api.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/docs/c_api.h	(revision 22322)
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2004 Joshua Hoblitt
+ *
+ * $Id: c_api.h,v 1.1.1.1 2004-12-22 02:16:23 jhoblitt Exp $
+ */
+
+// should there be an argument for "suggested" storage location?
+int add( const char *ext_id, const char *data, int size )
+
+int delete( const char *ext_id )
+
+// should there be an argument for "suggested" storage location?
+int update( const char *ext_id, const char *data, int size )
+
+// should there be an argument for "suggested" storage location?
+int move( const char *old_ext_id, const char *new_ext_id )
+
+// open returns a fd 
+int open( const char *ext_id )
+
+int close( const char *ext_id )
+
+// maybe open and close are a bad idea, perhaps...
+int read( const char *ext_id, char *data )
Index: /tags/sj_tags/sj_root_20080929/Nebulous/docs/database_setup.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/docs/database_setup.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/docs/database_setup.txt	(revision 22322)
@@ -0,0 +1,59 @@
+# Copyright (C) 2004  Joshua Hoblitt
+#
+# $Id: database_setup.txt,v 1.7 2005-11-09 00:49:44 jhoblitt Exp $
+
+### Database Setup
+
+### MySQL 4.1
+
+DROP DATABASE IF EXISTS nebulous;
+CREATE DATABASE nebulous;
+GRANT ALL PRIVILEGES ON *.* TO 'nebulous'@'localhost'
+    IDENTIFIED BY 'pixel' WITH GRANT OPTION;
+FLUSH PRIVILEGES;
+USE nebulous;
+
+### Initialization
+
+# localhost
+
+$ mkdir /tmp/po01 /tmp/po02
+INSERT INTO volume VALUES (1,'po01','file:/tmp/po01');
+INSERT INTO volume VALUES (1,'po02','file:/tmp/po02');
+
+# NFS
+
+INSERT INTO volume VALUES (1,'po01','file:/netdisks/po01');
+INSERT INTO volume VALUES (2,'po02','file:/netdisks/po02');
+INSERT INTO volume VALUES (3,'po03','file:/netdisks/po03');
+INSERT INTO volume VALUES (4,'po04','file:/netdisks/po04');
+INSERT INTO volume VALUES (5,'po05','file:/netdisks/po05');
+INSERT INTO volume VALUES (6,'po06','file:/netdisks/po06');
+INSERT INTO volume VALUES (7,'po07','file:/netdisks/po07');
+INSERT INTO volume VALUES (8,'po08','file:/netdisks/po08');
+
+INSERT INTO class VALUES (1,3,0,'raw image data');
+
+### Example queries
+
+LOAD DATA INFILE '/home/moa/jhoblitt/tmp/storage_objects.txt' INTO TABLE storage_object; 
+LOAD DATA INFILE '/home/moa/jhoblitt/tmp/instances.txt' INTO TABLE instance; 
+
+SELECT * FROM storage_object LEFT JOIN instance ON storage_object.so_id=instance.so_id WHERE storage_object.ext_id = "foobarbaz1.fits";
+SELECT * FROM storage_object LEFT JOIN instance USING (so_id) WHERE storage_object.ext_id = "foobarbaz1.fits";
+
+SET AUTOCOMMIT = 0;
+BEGIN;
+UPDATE instance SET read_lock=read_lock+1 WHERE ins_id=1;
+INSERT INTO lock_record values (1, 'read', NULL);
+COMMIT;
+
+SET AUTOCOMMIT = 0;
+BEGIN;
+INSERT INTO storage_object values (NULL, 'myfooedbar.fits', 'mr. wizard made this', NULL);
+INSERT INTO instance values (NULL, LAST_INSERT_ID(), 'file://tmp/bat/man/12345a', 'da39a3ee5e6b4b0d3255bfef95601890afd80709',0, NULL, NULL);
+COMMIT;
+
+IF select write_lock from instance WHERE id=1 THEN
+ELSE
+ENDIF
Index: /tags/sj_tags/sj_root_20080929/Nebulous/docs/design.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/docs/design.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/docs/design.txt	(revision 22322)
@@ -0,0 +1,283 @@
+# Copyright (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: design.txt,v 1.6 2005-11-05 01:13:35 jhoblitt Exp $
+
+Abstract
+--
+
+Nebulous is a user-space distributed object (file) storage system.  It is much
+more of a database of objects locations then a file system in the traditional
+sense.  Nebulous is considered distributed because of it's data storage model,
+where objects are stored across a number of storage volumes and may have one
+or more redundant copies.  While IPC is done in the traditional client/server
+model with a single[1] centralized server containing all storage object
+meta-data.  This system was designed, unlike most distributed storage system,
+specifically so that clients have the option of hosting one or more storage
+volumes and that data may be stored and accessed locally on a node.  This
+allows network I/O to be dramatically reduced if the end user does simple data
+locality optimizations.  Although, if you so choose, clients and storage
+volumes can reside on independent hardware.  Above all else, Nebulous is
+designed to make efficient use of both disk and network bandwidth.
+
+Data model
+--
+
+In Nebulous terminology a file is refereed to as a a resource.  Each resource
+is represented inside the Nebulous Server as a "Storage Object".  There is one
+Storage Object per resource (a file) that is being managed by the Nebulous
+Server.  A Storage Object maps an External Identifier (eg. filename) to a
+Storage ID.  In addition, creation time and comments may be stored in a
+Storage Object.  Storage Objects /do not/ contain the storage location of a
+resource as we need to allow for N copies of a resource to be stored.  The
+actual storage location of a resource is held in a Storage Object Instance,
+which maps to a Storage ID in a many-to-one relationship.
+
+A Storage Object Instance, or just Instance, represents a single copy of a
+resource being managed by the Nebulous Server. An Instance contains an
+Instance ID, the parent Storage ID, the location of the resource as a URI, and
+other assorted meta-data.
+
+Storage IDs are a signed 64-bit integer.  Negative values are considered
+reserved for future use.  Allocation should be incremental and, as we are
+unlikely to exceed 2^63 Storage Objects in the life time of the project, never
+reused. Instance IDs follow the same scheme as Storage IDs but exists in a
+unique namespace.
+
+Database record relationships
+--
+* All Storage Objects must have at least once associated Instance.
+
+* All Instances must be associated with a Storage Object.
+
+* All Instances must be associated with a valid URI (file exists).
+
+* All Storage Objects with locks set must have the correct number of
+corresponding Lock Records.
+
+* All Lock Records must be associated with a Storage Object.
+
+Data transport
+--
+XXX
+
+Data Storage
+--
+XXX
+
+
+Components
+--
+
+There are 4 principle components required for a working Nebulous system;
+server, client, data transport, and data storage.  The Nebulous software
+distribution provides only the server and the client software.  Data
+transport[2] and Data storage are left to 3rd party software packages.  As
+those functionalities are typically included with most operating systems.
+Nebulous build upon and benefits from advances made in those technologies.
+
+Server
+--
+
+The server is responsible for keeping track of storage objects, all instances
+of that object, and enforcing locking semantics.  Extensive logging and
+tracing support is provided for debug and to allow for statics generation and
+possible X<hotspot> optimization.
+
+Nebulous uses a centralized server model.  This model was choosen because it
+allows efficient X<pattern matching> of storage object names.  The current
+'best' technique for a distributed metadata store is with distributed hash
+tables.  Unfortunately, no widely available DHT implementation allows efficient
+I<pattern matching> of key names.
+
+
+[1] Multiple Nebulous servers should be possible via database replication.
+[2] A future version of Nebulous may include it's own data transport layer
+based on the WEB DAV protocol.
+
+Client <-> Server IPC
+--
+
+The Nebulous Server and clients communicate via the SOAP protocol.  Since SOAP
+is platform independent this allows native Nebulous clients to be written in a
+variety of languages.  With in the Nebulous Server itself the IPC mechanism is
+abstracted to allow ether multi-protocol support or complete replacement of
+SOAP by another protocol.
+
+XXX SOAP namespace
+
+
+Storage volume state tracking.
+
+Nebulous itself does not attempt to determine the state of storage volumes.
+Instead, it relies on some 3rd party to determine both the state
+(available/not) and capacity (total size + % utilized).  This information is
+then feed into Nebulous via either the administrative API or CLI management
+tools.
+
+XXX Storage Location determination
+
+
+Locking semantics
+--
+Nebulous uses 'advisory locking' where clients are excepted to check for the
+existence of locks but this policy is not enforced.  Locks are set on a
+Storage Object and apply to all Instances of that object.
+
+Two types of locks can be set; 'read' locks and 'write' locks.  Read locks are
+implemented as a semaphore while write locks use an exclusive mutex.
+
+    - Read lock
+In order to obtain a Read Lock on a Storage Object the Write Lock mutex must
+be in the state of 'free'.  When a Read Lock is acquire the Read semaphore is
+incremented by one.  When a Read Lock is released the Read semaphore is
+decremented by one.  This must happen atomically with the
+creation/destruction of a Lock Record.
+
+    - Write lock
+In order to obtain a Write lock on a Storage Object the Write Lock mutex must
+be in the state of 'free' and the Read lock semaphore must have a value of
+zero.  When a Write Lock is acquired the Write mutex is set to 'write'.  When a
+Write Lock is released the Write mutex is set to to 'free'.  This must happen
+atomically with the creation/destruction of a Lock Record.  As an extension, a
+sate of 'wait' may be implemented such that clients waiting on Read Locks to
+be released may prevent any additional Read Locks from being acquired.
+
+XXX Lock Records are currently unimplemented
+    - Lock Records A Lock Record is used to double check the indicated lock
+      state of a Storage Object and can be used to identify logical system
+errors.  A Lock Record contains the Storage ID of the object and the type of
+lock set.  eg, enum( 'read', 'write').
+
+    - Client best practices
+When attempting to acquire a lock a client should either "spin lock" with a
+reasonable pause between lock attempts or fail completely.  Aggressive "spin
+locking" is considered antisocial and may eventually require the Nebulous
+Server to identify and ignore such clients.
+
+
+House keeping
+--
+    - Lock sweeping
+In the event that a Storage Object operation fails to complete successfully
+stale locks will have to be identified and removed from the IPP Pixel Data
+Server Database.  This should be done periodically by comparing the entries
+in the Lock table to the list of active nodes maintained by the IPP
+Controller.  It should also happen as soon as possible after a node goes
+offline (triggered by the IPP Controller marking a node as offline?).  A sweep
+must be /completed/ before an offline node can be marked on-line.
+
+Once a node is determined to be offline all entries in the Lock table set by
+that node should be identified.   The locks on the Storage Object
+Instances pointed to by those entries should then be rolled back and Lock
+Record entries themselves must be removed from the lock table.
+
+    - Consistency sweeping
+Periodically the IPP Pixel Data Server meta-data and Storage Object will need
+to be checked for sanity.  This would be similar to running fsck on a modern
+filesystem.  Consistency sweeping should include Lock sweeping and should be
+considered a super-set.
+
+
+Server Operations
+--
+    - setup
+    - create_object
+    - rename_object
+        XXX unimplemented
+    - link_object
+        XXX unimplemented
+    - replicate object
+    - lock_object
+    - unlock_object
+    - find_objects
+        XXX unimplemented
+    - find_instances
+    - delete_instance
+    - fission_object
+        XXX unimplemented
+    - fuse_objects
+        XXX unimplemented
+    - stat_object
+    - stat_instance
+        XXX unimplemented
+
+Client Operations
+--
+    - create
+Creates and opens new Storage Object
+
+    - replicate
+Adds an Instance to a Storage Object
+
+    - cull
+Removes an Instance from a Storage Object
+
+This operation can not remove the last Instance of a Storage Object.
+
+    - lock
+Trys to acquire a lock on a storage object
+
+This operation times out after a default interval of 10s.
+
+    - unlock
+Trys to release a lock on a storage object
+    
+    - find_instances
+Find all instances of a storage object
+
+Only Instances on active storage volumes are found.
+
+    - find
+Find any instance of a storage object
+
+Only Instances on active storage volumes are found.
+
+    - open
+Open a storage object for read or write
+
+Opening a storage object as 'write' will remove all but one Instance to
+prevent Instances from becoming inconsistent.
+
+    - delete
+Delete a storage object and all of it's instances
+
+Removes a storage object and all of it's instances by sequentially deleting each
+Instance.  When a Storage Object has no more associated instances it is
+automatically removed by the Nebulous server.
+
+    - copy
+Copy a storage object
+
+The new storage object will be created with only one Instance.  Most properties
+of the source Storage Object will be preserved.
+
+    - move
+Rename a storage object
+
+Currently this operation will remove all but one instance of the storage
+object and may change it's storage location.
+
+    - delete_instance
+    
+Remove a storage object instance
+
+Removing the last Instance of a Storage Object will destroy the Storage
+Object.
+
+    - stat
+View the properties of a storage object
+
+
+    - import
+        XXX unimplemented
+Import a file into Nebulous
+
+Creates a new Storage Object with the specified External Identifier.  The
+source file will then be "copied" into the Nebulous system leaving the original
+file unmodified.
+
+    - fission
+        XXX unimplemented
+Create a new Storage Object from an existing one by either splitting off an
+Instance and re-parenting it or by cloning an existing Instance and associating
+it with the new Storage Object.
Index: /tags/sj_tags/sj_root_20080929/Nebulous/docs/requirements.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/docs/requirements.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/docs/requirements.txt	(revision 22322)
@@ -0,0 +1,28 @@
+# Copyright (C) 2004  Joshua Hoblitt
+#
+# $Id: requirements.txt,v 1.2 2005-09-14 00:46:01 jhoblitt Exp $
+
+- store all image data aquired during the first two years of PS1's
+  operations.  Approximately 10^8 files (OTA level).
+
+- injest new data from the summit at the same rate it is produced when
+  operating at a cadence of 40s between integrations.  Approximately 400Mb/s
+  and 64 files/integration.
+
+- support phase 1 & 2 processing. x files per t_min
+
+- support phase 3 & 4 processing. x files per t_min
+
+- support accessing small blocks of a file (ie., seek)
+
+- tolerate partal system failures without data loss
+
+- execeedly efficent use of both disk and network I/O
+
+what are the science drivers for these 3?
+--
+- data corruption is guarenteed to be detectable.
+
+- relibably store xx.x% of files 
+
+- xx.x% of stored images must be retreivable in xs.
Index: /tags/sj_tags/sj_root_20080929/Nebulous/docs/setup.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/docs/setup.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/docs/setup.txt	(revision 22322)
@@ -0,0 +1,369 @@
+=pod
+
+=head1 Installing a Nebulous Server
+
+=head2 Create a UNIX group for Nebulous
+
+All Nebulous user's need to be in the same UNIX group as the Nebulous server.
+
+    groupadd -g 877 nebulous
+
+This is a silly hack to generate a gid.
+
+    perl -le 'map { $gid += ord } split //, shift; print $gid' nebulous
+
+=head2 Add the users that you want to be able to access Nebulous to that group
+
+    usermod -G nebulous foouser
+
+=head2 Setup directory permissions for the nebulous group
+
+As root:
+
+    mkdir /data/ipp000.0/nebulous
+    mkdir /data/ipp000.1/nebulous
+    mkdir /data/ipp002.0/nebulous
+    mkdir /data/ipp003.0/nebulous
+
+    # note that this is just an example, this won't work over NFS with
+    # root_squash enablded
+    chown nobody:nebulous /data/ipp00?.?/nebulous
+
+    chmod 0770 /data/ipp00?.?/nebulous
+    ls -lad /data/ipp00?.?/nebulous
+
+=head2 Build and install the Nebulous Perl modules (see: README)
+
+=head2 Create and initialize the database
+
+    mysql -u root mysql -p
+    (enter your MySQL root password)
+
+    DROP DATABASE IF EXISTS nebulous;
+    CREATE DATABASE nebulous;
+    GRANT ALL PRIVILEGES ON nebulous.* TO 'nebulous'@'localhost'
+        IDENTIFIED BY '@neb@';
+    FLUSH PRIVILEGES;
+    exit
+
+    export NEB_DB=nebulous
+    export NEB_USER=nebulous
+    export NEB_PASS=@neb@
+
+    neb-initdb
+    neb-addvol --name ipp000.0 --uri file:///data/ipp000.0/nebulous
+    neb-addvol --name ipp000.1 --uri file:///data/ipp000.1/nebulous
+    neb-addvol --name ipp002.0 --uri file:///data/ipp002.0/nebulous
+    neb-addvol --name ipp003.0 --uri file:///data/ipp003.0/nebulous
+
+#    perl -e 'for (1..24) { $i = sprintf "po%02d", $_ ; system "neb-addvol --name $i --uri file:/$i/nebulous" }'
+    .
+    .
+
+If you get an error similar to this while initializing the database it means
+that the creation of the Nebulous's stored procedures failed:
+
+    ERROR 1146 (42S02): Table 'mysql.proc' doesn't exist
+
+It probably means that your instance of MySQL has been upgraded for an older
+version that did not support stored precedures.  You can fix this by running
+the C<mysql_fix_privilege_tables> script that you should have received with
+MySQL.
+
+In order for the test suite to pass the "test" account needs have the
+appropriate permissions to create and modify stored procedures.
+
+    update user set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where user = 'test';
+    update db set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where db = 'test';
+
+    flush privileges;
+
+Please see this webpage for further details:
+
+http://dev.mysql.com/doc/refman/5.0/en/stored-procedure-privileges.html
+
+
+=head2 Setup nebdiskd daemon
+
+Create the .netdiskrc file in the home directory of the user that nebdiskd
+will run as.  This does not need to be/should not be the root user.  Merely a
+user with permissions to see all of the approparite volumes.
+
+Example F<.netdiskrc>:
+
+    db: nebulous
+    dbpass: '@neb@'
+    dbuser: nebulous
+    mounts:
+      - /data/ipp000.0
+      - /data/ipp000.1
+      - /data/ipp002.0
+      - /data/ipp003.0
+    pidfile: /var/tmp/nebdiskd
+    poll_interval: 5
+
+Start the daemon by running the command C<nebdiskd>.  Use this command to
+verify that the daemon is working correclty.
+
+    neb-volstat
+
+nebdiskd should always been running when nebulous is use.  This command is an
+example of how to startup the daemon running as another user.  This could be
+use to start the daemon from the root account while the system init scripts
+are running.
+
+    sudo -H -u jhoblitt nebdiskd
+
+See the nebdiskd POD/man page for further details
+
+=head2 Install Apache2 with mod_perl
+
+=head3 Minimum software requirements:
+
+Nebulous will work with both Apache 1.3 & 2.x.  However, this guide will only
+cover configuraton with Apache 2.x and mod_perl 2.x.
+
+=over 4
+
+=item * Apache2 >= 2.0.54
+
+=item * mod_perl >= 2.0.0
+
+=item * Apache::DBI >= '0.97' [*]
+
+=back
+
+[*] required for connect_on_init() to work under mod_perl >= 2.0.1
+
+=head3 Apache 1.x 
+
+=item * building mod_perl-1.29 from source:
+
+    perl Makefile.PL USE_APXS=1 INSTALLDIRS=vendor WITH_APXS=/usr/sbin/apxs EVERYTHING=1 PERL_DEBUG=1
+
+    #MP_TRACE = 1 MP_DEBUG = 1 MP_USE_DSO = 1 MP_INST_APACHE2 = 1 MP_APXS = /usr/sbin/apxs2
+
+=head3 Apache 2.x 
+
+=head4 removing Apache on RHEl3
+
+On RHEL3 I had to remove these packages in order to fully remove MySQL.
+
+As C<root>:
+
+    rpm -e \
+    httpd-2.0.46-46.2.ent \
+    httpd-devel-2.0.46-46.2.ent \
+    redhat-config-httpd-1.1.0-4.30.2 \
+    mod_perl-1.99_09-10.ent \
+    mod_authz_ldap-0.22-5 \
+    mod_auth_pgsql-2.0.1-4.ent \
+    mod_python-3.0.3-5.ent \
+    mod_auth_mysql-20030510-2.ent \
+    mod_ssl-2.0.46-46.2.ent \
+    webalizer-2.01_10-15.ent \
+    mod_perl-1.99_09-10.ent \
+    mod_auth_pgsql-2.0.1-4.ent \
+    mod_python-3.0.3-5.ent \
+    mod_auth_mysql-20030510-2.ent \
+    mod_ssl-2.0.46-46.2.ent \
+    squirrelmail-1.4.3a-11.EL3 \
+    php-4.3.2-25.ent \
+    php-imap-4.3.2-25.ent \
+    php-ldap-4.3.2-25.ent \
+    php-mysql-4.3.2-25.ent \
+    php-odbc-4.3.2-25.ent \
+    php-pgsql-4.3.2-25.ent 
+
+    rm -rf /etc/httpd
+
+=head4 installing Apache2 from source
+
+    tar -jxvf httpd-2.0.54.tar.bz2
+    cd httpd-2.0.54
+    ./configure \
+    --disable-suexec \
+    --with-perl=/usr/bin/perl \
+    --with-port=80 \
+    --with-program-name=apache2 \
+    --with-devrandom=/dev/urandom \
+    --prefix           /usr \
+    --exec_prefix      /usr \
+    --bindir           /usr/bin \
+    --sbindir          /usr/sbin \
+    --libdir           /usr/lib \
+    --libexecdir       /usr/lib/apache2/modules \
+    --mandir           /usr/share/man \
+    --infodir          /usr/share/info \
+    --includedir       /usr/include/apache2 \
+    --datadir          /var/www/localhost \
+    --sysconfdir       /etc/apache2/conf \
+    --localstatedir    /var
+    make
+    su
+    make install
+    exit
+    cd ..
+
+=head4 installing mod_perl 2.x
+
+    tar -zxvf mod_perl-2.0.1.tar.gz
+    cd mod_perl-2.0.1
+    perl Makefile.PL
+    (apxs = /usr/sbin/apxs)
+    make
+    su
+    make install
+    echo "LoadModule perl_module /usr/lib/apache2/modules/mod_perl.so" >> /etc/apache2/conf/apache2.conf
+    exit
+
+    apachectl configtest
+    Syntax OK
+
+=head4 installing and configuring Nebulous
+
+    tar -zvxf Nebulous-0.01.tar.gz
+    cd Nebulous-0.01
+    perl Build.PL -axps /usr/sbin/apxs
+    ./Build
+    ./Build test
+    su
+    ./Build install
+
+
+    vi /etc/apache2/conf/startup.pl
+    (change Group to "Group nebulous")
+
+    cp fooconf /etc/apache2/conf/startup.pl
+    echo "PerlPostConfigRequire /etc/apache2/conf/startup.pl" >> /etc/apache2/conf/apache2.conf
+
+
+=head3 configurating Apache to act as a Nebulous/SOAP server
+
+=item * add the "nebulous" group to /etc/group
+=item * configure Apache to run as the nebulous group. 
+
+    Group nebulous
+
+Note that in theory the C<perchild> MPM can run vhosts as differenet
+users/groups but this module is currently non-funcitonal.
+
+L<http://httpd.apache.org/docs/trunk/mod/perchild.html>
+
+=item * configure a path for Neublous
+
+Make sure mod_perl is being loaded into Apache.  In order to do this on
+gentoo, you must edit /etc/config.d/apache2 and add C<-DPERL> to
+C<APACHE2_OTPS>.
+
+    cat <<END >> /etc/apache2/conf/apache2.conf
+    <Location /nebulous>
+        SetHandler perl-script
+        PerlResponseHandler Apache2::SOAP
+        PerlSetVar dispatch_to "PerlHandler Nebulous::Server::SOAP"
+        PerlSetVar options "compress_threshold => 10000"
+    </Location>
+    END
+
+    apachectl configtest
+    Syntax OK
+
+=item * initialize Nebulous database connections from C<startup.pl>
+
+    my $dsn         = 'DBI:mysql:database=nebulous:host=localhost';
+    my $dbuser      = 'nebulous';
+    my $dbpasswd    = '@neb@';
+
+    Apache::DBI->connect_on_init( $dsn, $dbuser, $dbpasswd );
+    Nebulous::Server::SOAP->new_on_init(
+        dsn         => $dsn,
+        dbuser      => $dbuser,
+        dbpasswd    => $dbpasswd,
+        log_level   => 'all',
+    );
+
+# out of date from here down...
+
+#
+# CGI interface
+#
+
+ScriptAlias /nebulous /usr/local/apache/perl/imageserver.pl
+
+<Directory "/usr/local/apache/perl">
+        AllowOverride None
+        Options None
+        Order allow,deny
+        Allow from all
+</Directory>
+
+PerlModule Apache::PerlRun
+<Location /perl>
+        SetHandler perl-script
+        PerlHandler Apache::PerlRun
+        Options ExecCGI
+        allow from all
+        PerlSendHeader On
+</Location>
+
+#
+# Nebulous::Apache interface
+#
+
+PerlModule Apache::DBI
+PerlModule Nebulous::Apache
+
+<Location /nebulous>
+        SetHandler perl-script
+        PerlHandler Nebulous::Apache
+</Location>
+
+- setup the server
+
+As a CGI, will with either Apache 1.3.x or 2.0.x
+
+cp scripts/imagesever.pl /var/www/localhost/cgi-bin
+chmod 755 /var/www/localhost/cgi-bin/imageserver.pl
+touch /tmp/imageserver.log
+chmod 1777 /tmp/imageserver.log
+
+=head2 Testing the Nebulous Server
+
+You can test your configuration with the C<telnet> utility.
+
+    $ telnet localhost 80
+    Trying 127.0.0.1...
+    Connected to localhost.localdomain (127.0.0.1).
+    Escape character is '^]'.
+    GET /nebulous HTTP/1.0
+
+    HTTP/1.1 500 Internal Server Error
+    Date: Wed, 31 Aug 2005 01:10:47 GMT
+    Server: Apache/2.0.54 (Unix) mod_perl/2.0.1 Perl/v5.8.6
+    Content-Length: 645
+    Connection: close
+    Content-Type: text/html; charset=iso-8859-1
+    .
+    .
+
+    Connection closed by foreign host.
+
+If you get an HTTP 5xx code check your apache error_log
+
+
+=head2 MySQL Configuration/tuning
+
+in /etc/mysql/my.cnf
+under [mysqld]
+increase the number of alloweed connections, e.g.
+max_connections = 200
+
+Nebulous makes heavy use of MySQL's C<innodb> table type.  Try configuring
+MySQL with these parameters:
+
+    innodb_log_files_in_group = 2
+    innodb_buffer_pool_size = as big as possible (512M+)
+    innodb_log_buffer_size= 16M
+    innodb_log_file_size=10M
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/docs/tmp.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/docs/tmp.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/docs/tmp.txt	(revision 22322)
@@ -0,0 +1,44 @@
+client
+--
+new
+
+new_file
+                new_file
+                open_file
+
+find_file
+    find_instance
+
+open_file
+    find_file
+
+delete_file
+                delete_file
+    nuke
+
+copy_file
+    open_file
+    new_file
+
+move_file
+    copy_file
+    delete_file
+
+replicate_file
+    open_file
+                replicate_file
+
+cull_file
+    find_instance
+    delete_instance
+
+delete_instance
+    nuke
+                delete_instance
+
+find_instance
+                find_instance
+
+# missing
+file_stat
+lock ?
Index: /tags/sj_tags/sj_root_20080929/Nebulous/examples/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/examples/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/examples/Makefile	(revision 22322)
@@ -0,0 +1,5 @@
+NEB_CFLAGS=`pkg-config nebclient --cflags`
+NEB_LIBS=`pkg-config nebclient --libs`
+
+nebexample: nebexample.c
+	$(CC) $(NEB_CFLAGS) $(CFLAGS) $(NEB_LIBS) $< -o $@
Index: /tags/sj_tags/sj_root_20080929/Nebulous/examples/nebexample.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/examples/nebexample.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/examples/nebexample.c	(revision 22322)
@@ -0,0 +1,45 @@
+// gcc `pkg-config nebclient --cflags --libs` example.c -o example
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <nebclient.h>
+
+#define NEB_SERVER  "http://podb1.ifa.hawaii.edu:80/nebulous"
+#define NEB_KEY     "foobarbaz"
+
+int main (int argc, char **argv) {
+    nebServer       *server = NULL;
+    char            *key = NEB_KEY;
+
+    server = nebServerAlloc(NEB_SERVER);
+    if (!server) {
+        printf("nebServerAlloc() failed\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // make sure there isn't already a file named "foobarbaz" so this example
+    // doesn't cause an error
+    nebDelete(server, key);
+
+    int fh = nebCreate(server, key, 0, NULL, NULL, NULL);
+    if (fh < 0) {
+        printf( "nebCreate() failed: %s\n", nebErr(server));
+        exit(EXIT_FAILURE);
+    }
+
+    // fh is a file descriptor
+    close(fh);
+
+    if (!nebReplicate(server, key, NULL, NULL)) {
+        printf( "nebReplicate() failed: %s\n", nebErr(server));
+        exit(EXIT_FAILURE);
+    }
+
+    if (!nebDelete(server, key)) {
+        printf( "nebDelete() failed: %s\n", nebErr(server));
+        exit(EXIT_FAILURE);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/examples/nebexample.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/examples/nebexample.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/examples/nebexample.pl	(revision 22322)
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Nebulous::Client;
+
+my $neb = Nebulous::Client->new(
+    proxy   => 'http://podb1.ifa.hawaii.edu:80/nebulous'
+);
+
+my $key = shift || 'foobarbaz';
+
+# make sure there isn't already a file named "foobarbaz" so this example
+# doesn't cause an error
+$neb->delete($key);
+
+my $fh = $neb->create($key);
+die "can't create file $key " unless $fh;
+close($fh);
+
+if (!$neb->replicate($key)) {
+    die "can't replicate object $key";
+}
+
+if (!$neb->delete($key)) {
+    die "can't delete object $key";
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client.pm	(revision 22322)
@@ -0,0 +1,999 @@
+# Copyright (c) 2004-2008  Joshua Hoblitt
+#
+# $Id: Client.pm,v 1.60 2008-09-24 00:36:56 jhoblitt Exp $
+
+package Nebulous::Client;
+
+use strict;
+use warnings FATAL => qw( all );
+no warnings qw( uninitialized );
+
+our $VERSION = '0.09';
+
+use Digest::MD5;
+use File::Copy qw();
+use Log::Log4perl qw(get_logger :levels);
+use Nebulous::Client::Log;
+use Nebulous::Util qw( :standard );
+use Params::Validate qw( validate validate_pos SCALAR UNDEF );
+#use SOAP::Lite +trace => [qw( debug )];
+use SOAP::Lite;
+use Time::HiRes qw( sleep );
+use URI;
+
+use constant LOCK_INTERVAL  => 1;
+use constant LOCK_DEFAULT   => 10;    # default to a 10s lock timeout
+
+$SOAP::Constants::PATCH_HTTP_KEEPALIVE = 1;
+my $log;
+
+# TODO remove most of the logdies
+
+sub import {
+    my $class = shift;
+
+	my %args = validate( @_,
+        {
+            trace => {
+                type        => SCALAR,
+                optional    => 1,
+                default     => 'fatal',
+                callbacks   => {
+                    'is valid level' => sub {
+                        defined $LEVELS{ lc $_[0] };
+                    },
+                },
+            },
+        },
+    );
+
+    Nebulous::Client::Log->init;
+    $log = get_logger( "Nebulous::Client" );
+    $log->level( $LEVELS{ lc $args{ 'trace' } } );
+
+    # this has to be after the log level is set
+    $log->debug( "args: @_" );
+
+    return 1;
+}
+
+sub new {
+    my $class = shift;
+
+    my %args = validate( @_,
+        {
+            proxy   => {
+                type        => SCALAR,
+            },
+            uri     => {
+                type        => SCALAR,
+                optional    => 1,
+            },
+        },
+    );
+
+    Nebulous::Client::Log->init;
+    $log = get_logger( "Nebulous::Client" );
+    $log->level( $LEVELS{ lc $args{ 'trace' } } );
+
+    # this has to be after the log level is set
+    $log->debug( "args: @_" );
+
+    $log->debug( "entered - @_" );
+
+    my $lite = SOAP::Lite->new(
+            proxy => $args{ 'proxy' },
+            uri   => $args{ 'uri' } || "urn:Nebulous/Server/SOAP",
+    );
+
+    $log->logdie( "can not create SOAP::Lite object" ) unless $lite;
+
+    my $self = bless( { server => $lite }, ref $class || $class );
+
+    $log->debug( "leaving" );
+
+    return $self;
+}
+
+sub create {
+    my $self = shift;
+
+    my ( $key, @params ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR|UNDEF,
+            optional    => 1,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    # how should already existing files be handled?
+
+    my $response = $self->{ 'server' }->create_object( $key, @params );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+        unless ($response->faultstring =~ /Duplicate entry/) {
+            $log->logdie("unhandled fault - ", $self->err);
+        }
+
+        $log->debug( "leaving" );
+
+        return;
+    }
+
+    my $res = $response->result;
+    $log->debug( "server response: $res" );
+
+    my $uri = URI->new($res);
+    $log->debug( "URI is: $uri" );
+
+    $log->debug( "leaving" );
+
+    return $uri;
+}
+
+sub open_create {
+    my $self = shift;
+
+    my ( $key, @params ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR|UNDEF,
+            optional    => 1,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    # how should already existing files be handled?
+
+    my $response = $self->{ 'server' }->create_object( $key, @params );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+        unless ($response->faultstring =~ /Duplicate entry/) {
+            $log->logdie("unhandled fault - ", $self->err);
+        }
+
+        $log->debug( "leaving" );
+
+        return;
+    }
+
+    my $res = $response->result;
+    $log->debug( "server response: $res" );
+
+    my $uri = URI->new($res);
+    $log->debug( "URI is: $uri" );
+
+    my $fh;
+    eval {
+        $fh = _open_uri( $uri, '+<' );
+    };
+    $log->logdie( $@ ) if $@;
+
+    $log->debug( "leaving" );
+
+    return $fh;
+}
+
+sub replicate {
+    my $self = shift;
+
+    my ( $key, @params ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+        },
+        {
+            # volume
+            type        => SCALAR|UNDEF,
+            optional    => 1,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    # We have to open the instance that we're going to copy from first.  If
+    # we don't do this, it's possible that open will find & open the new
+    # instance that we're in the process of creating
+    my $fh  = $self->open( $key, 'read' );
+    unless ( $fh ) {
+        $log->debug( "can't open $key" );
+        $log->debug( "leaving" );
+
+        return;
+    }
+
+    # ask the server for a new instance attached to our key
+    my $response = $self->{ 'server' }->replicate_object( $key, @params );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    my $res = $response->result;
+    $log->debug( "server response: $res" );
+
+    my $uri = URI->new($res);
+    $log->debug( "URI is: $uri" );
+
+    my $new_fh;
+    eval {
+        # must open read/write so we can check the md5sum
+        $new_fh = _open_uri( $uri, '+>' );
+    };
+    $log->logdie( $@ ) if $@;
+
+    my $success = File::Copy::copy( $fh, $new_fh );
+    unless ($success) {
+        # if the copy failed we now have a zero length instances floating
+        # around that must be removed
+        unless ($self->delete_instance("$uri")) {
+            $log->logdie( "can not copy instance $uri AND FAILED TO CLEANUP EMPTY INSTANCE" );
+        }
+        $log->logdie( "can not copy instance $uri" );
+    } 
+
+    # check md5sum
+    my $src_md5 = Digest::MD5->new->addfile($fh)->hexdigest;
+    $log->debug( "md5sum src instance: $src_md5" );
+    my $dst_md5 = Digest::MD5->new->addfile($new_fh)->hexdigest;
+    $log->debug( "md5sum dst instance: $dst_md5" );
+
+    unless ($src_md5 eq $dst_md5) {
+        $self->delete_instance("$uri");
+        $log->logdie( "md5sum mismatch" );
+    }
+
+    $log->debug( "copied instance" );
+
+    close( $fh ) or $log->logdie( "can not close $key" );
+    close( $new_fh ) or $log->logdie( "can not close $key" );
+
+    $log->debug( "leaving" );
+
+    return $uri;
+}
+
+
+sub cull {
+    my $self = shift;
+
+    my ($key, $vol_name) = validate_pos(@_,
+        {
+            type => SCALAR,
+        },
+        {
+            # volume
+            type        => SCALAR,
+            optional    => 1,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $locations;
+    # if vol_name is specified we need to stat the file to find out how many
+    # instances there are on all volumes.  Otherwise we wouldn't know if it's
+    # safe to remove the last instance on the specified volume.
+    # XXX We need some way to determine which is the best instance to remove
+    if (defined $vol_name) {
+        my @stats = $self->stat($key);
+        my $instances = $stats[6];
+        if (defined $instances and $instances < 2) {
+            $log->debug("can not cull - not enough instances");
+            $log->debug("leaving");
+
+            return;
+        }
+
+        $locations = $self->find_instances($key, $vol_name);
+        unless (defined $locations) {
+            $log->debug( "no instances" );
+            $log->debug( "leaving" );
+
+            return;
+        }
+    } else {
+        $locations = $self->find_instances($key);
+        unless ($locations) {
+            $log->debug("no instances");
+            $log->debug("leaving");
+
+            return;
+        }
+
+        if (scalar @{ $locations } < 2) {
+            $log->debug("can not cull - not enough instances");
+            $log->debug("leaving");
+
+            return;
+        }
+    }
+
+    my $uri = $self->delete_instance( @$locations[0] );
+
+    $log->debug("leaving");
+
+    return $uri;
+}
+
+sub lock {
+    my $self = shift;
+
+    my ( $key, $type, $timeout ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is read or write' => sub { $_[0] =~ /^(?:read|write)$/ },
+            },
+        },
+        {
+            type        => SCALAR,
+            default     => LOCK_DEFAULT,
+            optional    => 1,
+            callbacks   => {
+                'is + integer' => sub { $_[0] =~ /^\d+$/ },
+            },
+        },
+    );
+
+    my $endtime = time() + $timeout;
+
+    $log->debug( "entered - @_" );
+
+    my $locked;
+
+    while ( time() < $endtime ) {
+        $log->debug( "trying to get a $type lock..." );
+
+        my $response = $self->{ 'server' }->lock_object( $key, $type );
+        if ( $response->fault ) {
+            $self->set_err($response->faultstring);
+
+            # "retry" means we couldn't get a lock because one was already held
+            # and we should try again
+            if ( $response->faultstring =~ /retry/ ) {
+                $log->debug( "failed to get a $type lock..." );
+                next;
+            }
+
+            if ($response->faultstring =~ /is valid object key/) {
+                $log->debug( "leaving" );
+                return;
+            }
+
+            $log->logdie("unhandled fault - ", $self->err);
+        }
+
+        $locked = $response->result;
+        if ( $locked ) {
+            $log->debug( "got a $type lock" );
+            last;
+        }
+    } continue {
+        $log->debug( "sleeping..." );
+        sleep LOCK_INTERVAL;
+    }
+
+    $log->debug( "can not get a lock" ) unless $locked;
+
+    $log->debug( "leaving" );
+    
+    $locked ? return 1 : return;
+}
+
+sub unlock {
+    my $self = shift;
+
+    my ( $key, $type ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is read or write' => sub { $_[0] =~ /^(?:read|write)$/ },
+            },
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $unlocked;
+
+    $log->debug( "trying to release a $type lock..." );
+
+    my $response = $self->{ 'server' }->unlock_object( $key, $type );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+
+        # key doesn't exist
+        if ($response->faultstring =~ /is valid object key/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        # lock doesn't exist
+        if ($response->faultstring =~ /non-existant read lock/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        if ($response->faultstring =~ /non-existant write lock/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        if ($response->faultstring =~ /can not have a read lock under a write lock/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        if ($response->faultstring =~ /can not have a write lock under a read lock/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    $unlocked = $response->result;
+    if ( $unlocked ) {
+        $log->debug( "released a $type lock" );
+    } else {
+        $log->debug( "can not release $type lock" );
+    }
+
+    $log->debug( "leaving" );
+    
+    $unlocked ? return 1 : return;
+}
+
+
+sub setxattr
+{
+    my $self = shift;
+
+    my ($key, $name, $value, $flags) = validate_pos(@_,
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is create or replace' => sub { $_[0] =~ /^(?:create|replace)$/i },
+            },
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->setxattr_object( $key, $name, $value, $flags);
+    if ($response->fault) {
+        $self->set_err($response->faultstring);
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    $log->debug( "leaving" );
+    
+    return 1;
+}
+
+
+sub getxattr
+{
+    my $self = shift;
+
+    my ($key, $name) = validate_pos(@_,
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->getxattr_object( $key, $name );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    my $res = $response->result;
+    $log->debug( "server response: $res" );
+
+    $log->debug( "leaving" );
+    
+    return $res;
+}
+
+
+sub listxattr
+{
+    my $self = shift;
+
+    my ($key) = validate_pos(@_,
+        {
+            type        => SCALAR,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->listxattr_object( $key );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    my $res = $response->result;
+    $log->debug( "server response: $res" );
+
+    $log->debug( "leaving" );
+    
+    return $res;
+}
+
+
+sub removexattr
+{
+    my $self = shift;
+
+    my ($key, $name) = validate_pos(@_,
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->removexattr_object( $key, $name );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    $log->debug( "leaving" );
+    
+    return 1;
+}
+
+
+sub find_objects {
+    my $self = shift;
+
+    my @args = validate_pos( @_,
+        {
+            type        => SCALAR,
+            optional    => 1,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->find_objects( @args );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+
+        if ($response->faultstring =~ /no keys found/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    my $keys = $response->result;
+
+    $log->debug( "server found: @$keys" );
+
+#    foreach my $path ( @{ $uris } ) {
+#        $path = _get_file_path( $path );
+#    }
+
+    $log->debug( "leaving" );
+
+    return $keys;
+}
+
+sub find_instances {
+    my $self = shift;
+
+    my ( $key, @params ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+        },
+        {
+            #volume
+            type        => SCALAR|UNDEF,
+            optional    => 1,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->find_instances( $key, @params );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+        # check to see if this failure is because $key doesn't exist
+        if ($response->faultstring =~ /is valid object key/) {
+            $log->debug( "leaving" );
+            return;
+        }
+        # key is valid but no instances are on the specified volume
+        if ($response->faultstring =~ /no instances on storage volume/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    my $uris = $response->result;
+
+    $log->debug( "server found: @$uris" );
+
+    $log->debug( "leaving" );
+
+    return $uris;
+}
+
+sub find {
+    my $self = shift;
+
+    my ( $key, @params ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+        },
+        {
+            #volume
+            type        => SCALAR|UNDEF,
+            optional    => 1,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $locations = $self->find_instances( $key, @params );
+    unless (defined $locations) {
+        unless ($self->err =~ /no instances on storage volume/) {
+            return;
+        }
+
+        # then fall back to looking for isntances on any volume
+        $locations = $self->find_instances( $key, 'any');
+        unless (defined $locations) {
+            return;
+        }
+    }
+
+    my $path;
+    eval {
+        $path = _get_file_path( $locations->[0] );
+    };
+    $log->logdie( $@ ) if $@;
+
+    $log->debug( "leaving" );
+
+    return $path;
+}
+
+sub open {
+    my $self = shift;
+
+    my ( $key, $type ) = validate_pos( @_,
+        {
+            type        => SCALAR,
+        },
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is read or write' => sub { $_[0] =~ /^(?:read|write|>)$/ },
+            },
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $fh;
+
+    # need to figure out which is the most optimal copy to open
+    my $locations = $self->find_instances( $key );
+
+    my $path;
+    eval {
+        $path = _get_file_path( $locations->[0] );
+    };
+    $log->logdie( $@ ) if $@;
+
+    unless ( $path ) {
+        $log->debug( "no instances" );
+
+        if ( $type eq 'write' ) {
+            $log->debug( "creating a new storage object" );
+
+            my $ret = $self->open_create( $key );
+
+            $log->debug( "leaving" );
+
+            return $ret;
+        } else {
+            $log->debug( "leaving" );
+            return;
+        }
+    }
+
+    if ( $type eq 'write' or $type eq '>' ) {
+        my $num_instances = scalar @$locations;
+
+        if ( $num_instances > 1 ) {
+            $log->warn( "write not allowed with multiple instances" );
+            $log->debug( "leaving" );
+            return;
+        }
+
+        eval {
+            $fh = _get_filehandle( $path, '+<' );
+        };
+        $log->logdie( $@ ) if $@;
+
+        if ($type eq '>') {
+            truncate($fh, 0) or $log->logdie("truncate failed: $!");
+        }
+    } elsif ( $type eq 'read' ) {
+        eval {
+            $fh = _get_filehandle( $path, '<' );
+        };
+        $log->logdie( $@ ) if $@;
+    }
+
+    $log->debug( "leaving" );
+
+    return $fh;
+}
+
+sub delete {
+    my $self = shift;
+
+    my ( $key ) = validate_pos( @_,
+        {
+            type => SCALAR,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $locations = $self->find_instances( $key, 'any' );
+
+    return undef unless $locations;
+        
+    # a lock is implicitly removed when the last storage object is deleted
+    foreach my $uri ( @$locations ) {
+        $self->delete_instance( $uri ) or return undef;
+    }
+
+    $log->debug( "leaving" );
+
+    return 1;
+}
+
+sub copy {
+    my $self = shift;
+
+    my ( $key, $new_key, $volume ) = validate_pos( @_,
+        {
+            type => SCALAR,
+        },
+        {
+            type => SCALAR,
+        },
+        {
+            #volume
+            type        => SCALAR,
+            optional    => 1,
+        },
+    );
+
+#    $log->debug( "entered - @_" );
+
+    my $fh      = $self->open( $key, 'read' );
+    unless ( $fh ) {
+        $log->debug( "can not open object" );
+        $log->debug( "leaving" );
+
+        return;
+    }
+
+    my $new_fh;
+    if (defined $volume) {
+        $new_fh  = $self->open_create( $new_key, $volume );
+    } else {
+        $new_fh  = $self->open_create( $new_key );
+    }
+    unless ( $new_fh ) {
+        $log->debug( "can not open object" );
+        $log->debug( "leaving" );
+
+        return;
+    }
+
+    File::Copy::copy( $fh, $new_fh ) or $log->logdie( "can not copy object $key" );
+
+    close( $fh ) or $log->logdie->( "can not close $key" );
+    close( $new_fh ) or $log->logdie->( "can not close $new_key" );
+
+    $log->debug( "leaving" );
+
+    return 1;
+}
+
+sub move {
+    my $self = shift;
+
+    my ( $key, $new_key ) = validate_pos( @_,
+        {
+            type => SCALAR,
+        },
+        {
+            type => SCALAR,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->rename_object( $key, $new_key );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+
+        if ($response->faultstring =~ /is valid object key/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    $log->debug( "leaving" );
+
+    return 1;
+}
+
+sub delete_instance {
+    my $self = shift;
+
+    my ($uri) = validate_pos(@_,
+        {
+            type => SCALAR,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    # it is being assumed here that it is better to have files on disk and not in
+    # the database then the inverse.
+
+    my $path;
+    eval {
+        $path = _get_file_path( $uri );
+    };
+    $log->logdie( $@ ) if $@;
+
+    my $response = $self->{ 'server' }->delete_instance( $uri );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+
+        if ($response->faultstring =~ /no instance is associated with uri/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    $log->debug( "server deleted instance" );
+
+    eval {
+        _nuke_file( $path );
+    };
+    $log->logdie( $@ ) if $@;
+
+    $log->debug( "leaving" );
+
+    return $uri;
+}
+
+
+sub mounts {
+    my $self = shift;
+
+    validate_pos(@_);
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->mounts();
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    my $stats = $response->result;
+
+    $log->debug( "leaving" );
+
+    return $stats;
+}
+
+sub stat {
+    my $self = shift;
+
+    my ( $key ) = validate_pos( @_,
+        {
+            type => SCALAR,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->stat_object( $key );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+        if ($response->faultstring =~ /is valid object key/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    $log->debug( "server returned a stat" );
+
+    my $stats = $response->result;
+
+    $log->debug( "leaving" );
+
+    return $stats;
+}
+
+sub err {
+    my $self = shift;
+
+    die "accepts no params" if @_;
+
+    return $self->{ 'err' };
+}
+
+sub set_err {
+    my $self = shift;
+
+    my $err_string = shift;
+
+    $self->{ 'err' } = $err_string;
+
+    local $Log::Log4perl::caller_depth += 1;
+    $log->error($err_string);
+
+    return $self;
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client.pod
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client.pod	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client.pod	(revision 22322)
@@ -0,0 +1,460 @@
+=pod
+
+=head1 NAME
+
+Nebulous::Client - Nebulous client component
+
+=head1 SYNOPSIS
+
+    use Nebulous::Client;
+
+    my $neb = Nebulous::Client->new(
+        proxy   => 'http://localhost:80/nebulous'
+    );
+
+    my $uri = $neb->create( "key", "node01" );
+    my $fh = $neb->open_create( "key", "node01" );
+    $neb->replicate( "key", "node01" );
+    $neb->cull( "key", "node01" );
+    $neb->lock( "key", 'write' );
+    $neb->unlock( "key", 'write' );
+    $neb->setxattr( $key, $name, $value, $flags );
+    $neb->getxattr( $key, $name );
+    $neb->listxattr( $key );
+    $neb->removexattr( $key, $name );
+    my $uris = $neb->find_objects( $pattern );
+    my $uris = $neb->find_instances( "key", "node01" );
+    my $path = $neb->find( "key" );
+    my $fh = $neb->open( "key", 'read' );
+    $neb->delete( "key" );
+    $neb->copy( "key", "new_key", "node01" );
+    $neb->move( "key", "new_key" );
+    $neb->delete_instance( $uri );
+    my $stats = $neb->stat( "key" );
+    my $mounts = $neb->mounts();
+
+=head1 DESCRIPTION
+
+This component provides client access to the Nebulous storage system.  It
+provides an interface for files to be added, managed, and removed from the
+system.
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts an optional hash as argument to it's C<import> method and
+exports no I<symbols>.
+
+    use Nebulous::Client trace => qw( debug );
+
+The hash is allowed to have a single key, C<trace>.  This controls the level of
+tracing sent to the standard out.  The value maybe one of: C<off>, C<fatal>,
+C<error>, C<warn>, C<info>, C<debug>, C<all>. 
+
+The default level of tracing is C<warn>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * new( ... )
+
+Accepts a mandatory hash.
+
+    my $neb = Nebulous::Client->new(
+        proxy   => 'http://localhost:80/nebulous'
+        uri     => 'urn:Nebulous/Server',
+    );
+
+=over 4
+
+=item * proxy
+
+The URL of the Nebulous server to connect to.
+
+=item * uri
+
+The namespace of the SOAP service.  Unless you know what your doing, don't set
+this key.
+
+This key is optional.
+
+=back
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+
+=item * create($key, $volume).
+
+Creates a new storage object.
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=item * volume
+
+The preferred storage location of initial instance.
+
+This parameter is optional.
+
+=back
+
+Returns a URI or C<undef> on failure.
+
+
+=item * open_create($key, $volume).
+
+Creates and opens new storage object.
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=item * volume
+
+The preferred storage location of initial instance.
+
+This parameter is optional.
+
+=back
+
+Returns a filehandle or C<undef> on failure.
+
+
+=item * replicate($key, $volume)
+
+Adds an instance to a storage object.
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=item * volume
+
+The preferred storage location of initial instance.
+
+This parameter is optional.
+
+=back
+
+Returns true on success.
+
+
+=item * cull($key, $volume)
+
+Removes an instance from a storage object.
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=item * volume
+
+The volume to attempt to remove an instance from.  If there is no instance on
+the specified volume this method will fail.  If there is more then one instance
+on the specified volume I<only one> of them will be removed.
+
+This parameter is optional.
+
+=back
+
+Returns true on success.
+
+
+=item * lock($key, $type, $timeout)
+
+Trys to acquire a lock on a storage object
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=item * type
+
+The type of lock to acquire.  Either C<read> or C<write>.
+
+=item * timeout
+
+The number of seconds to I<try> for a lock.
+
+This parameter is optional.
+
+=back
+
+Returns true on success.
+
+
+=item * unlock($key, $type)
+
+Trys to release a lock on a storage object.
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=item * type
+
+The type of lock to release.  Either C<read> or C<write>.
+
+=back
+
+Returns true on success.
+
+
+=item * find_objects($pattern)
+
+Lists all storage objects that match C<$pattern>.  Where C<$pattern> is a POSIX
+1003.2 compatable regular repression.  Note that this is I<NOT> a Perl or PCRE
+regular expression.
+
+=over 4
+
+=item * pattern
+
+The storage object key (name).
+
+This parameter is optional.
+
+=back
+
+Returns an arrayref of scalars.
+
+
+=item * find_instances($key, $volume)
+
+Lists all instances of a storage object
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=item * volume
+
+Restrict search to this storage location.
+
+This parameter is optional.
+
+=back
+
+Returns an arrayref of scalars.
+
+
+=item * find($key)
+
+Find any instance of a storage object.
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=back
+
+Returns the path to the instance.
+
+
+=item * open($key, $type)
+
+Open a storage object for read or write.
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=item * type
+
+The type of lock to acquire.  One of [C<read>, C<write>, C<>>].
+
+C<>> truncates any pre-existing files to zero length and opens it for writing.
+
+=back
+
+Returns a filehandle or C<undef> on failure.
+
+
+=item * delete($key)
+
+Delete a storage object and all of it's instances.
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=back
+
+Returns true on success.
+
+
+=item * copy($key, $new_key, $volume)
+
+Copy a storage object.
+
+=over 4
+
+=item * key
+
+The source object.
+
+=item * new_key
+
+The destination object.
+
+=item * volume
+
+The preferred storage location of the new instance.
+
+This parameter is optional.
+
+=back
+
+Returns true on success or C<undef> on failure.
+
+
+=item * move($key, $new_key)
+
+Rename a storage object.
+
+=over 4
+
+=item * key
+
+The source object.
+
+=item * new_key
+
+The destination object.
+
+=back
+
+Returns true on success or C<undef> on failure.
+
+
+=item * delete_instance($uri)
+
+Remove a storage object instance.
+
+=over 4
+
+=item * uri
+
+URI of the instance to remove.
+
+=back
+
+Returns true on success or C<undef> on failure.
+
+=item * stat($key)
+
+View the properties of a storage object.
+
+=over 4
+
+=item * key
+
+The storage object key (name).
+
+=back
+
+Returns an arrayref of scalars.
+
+=item * mounts()
+
+Accepts no parameters and returns an AoA of:
+
+    [
+        [
+            mountpoint,
+            total,
+            used,
+            vol_id,
+            name,
+            path,
+            allocate,
+            available,
+            xattr,
+        ],
+        [ ... ],
+        ...
+    ]
+
+=back
+
+=head1 DEVELOPER NOTES
+
+=head1 REFERENCES
+
+=over 4
+
+=item MogileFS
+
+The Nebulous system is similar in function to MogileFS but very different in
+implementation and operation.
+
+L<http://www.danga.com/mogilefs/>
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2004-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the LICENSE file included with
+this module, or in the L<perlgpl> Pod as supplied with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Client::QuickStart>, L<Nebulous::Server>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client/HTTP.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client/HTTP.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client/HTTP.pm	(revision 22322)
@@ -0,0 +1,26 @@
+# Copyright (c) 2004  Joshua Hoblitt
+#
+# $Id: HTTP.pm,v 1.2 2005-06-30 02:35:06 jhoblitt Exp $
+
+package Nebulous::Client::HTTP;
+
+use strict;
+
+our $VERSION = '0.01';
+
+use base qw( SOAP::Lite );
+
+sub call {
+    my $result = $_[0]->SUPER::call( @_ );
+
+    if (( UNIVERSAL::isa( $result, 'SOAP::SOM' )) && $result->fault ) {
+
+        return $result->faultstring;
+    }
+
+    return $result;
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client/Log.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client/Log.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client/Log.pm	(revision 22322)
@@ -0,0 +1,45 @@
+# Copyright (c) 2004  Joshua Hoblitt
+#
+# $Id: Log.pm,v 1.9 2008-05-16 21:09:18 jhoblitt Exp $
+
+package Nebulous::Client::Log;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = '0.01';
+
+use Log::Log4perl;
+use File::Spec;
+
+sub init {
+    my $log_path;
+    if (defined $ENV{HOME}) {
+    	$log_path = $ENV{HOME};
+    } else {
+    	$log_path = File::Spec->tmpdir();
+    }
+    # my $log_path = $ENV{HOME} or File::Spec->tmpdir();
+
+    my $conf = <<END;
+    log4perl.category.Nebulous.Client = FATAL, Screen
+#log4perl.category.Nebulous.Client = DEBUG, CLIENTLOGFILE, Screen
+
+    log4perl.appender.Screen        = Log::Log4perl::Appender::Screen
+    log4perl.appender.Screen.stderr = 1
+    log4perl.appender.Screen.layout = Log::Log4perl::Layout::PatternLayout
+    log4perl.appender.Screen.layout.ConversionPattern = %d | %H | %p | %M - %m%n
+
+    log4perl.appender.CLIENTLOGFILE           = Log::Log4perl::Appender::File
+    log4perl.appender.CLIENTLOGFILE.filename  = $log_path/nebulous_client.log
+    log4perl.appender.CLIENTLOGFILE.mode      = append
+    log4perl.appender.CLIENTLOGFILE.layout    = Log::Log4perl::Layout::PatternLayout
+    log4perl.appender.CLIENTLOGFILE.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} | %H | %p | %M - %m%n
+END
+
+    Log::Log4perl::init( \$conf );
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client/QuickStart.pod
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client/QuickStart.pod	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Client/QuickStart.pod	(revision 22322)
@@ -0,0 +1,177 @@
+=pod 
+
+=head1 NAME
+
+Nebulous::Server::QuickStart - Nebulous QuickStart Guide 
+
+=head1 Perl CLIENT
+
+The Nebulous Perl Client is composed of several modules but the user interface
+is accessed via the L<Nebulous::Client> module.  The API is also documented in
+the L<Nebulous::Client> Pod.
+
+=head2 Tracing
+
+There is extensive support for execution tracing.  This is set by passing
+parameters to the L<Nebulous::Client> module when it is imported. e.g.
+
+    use Nebulous::Client trace => qw( debug );
+
+Please see the L<Nebulous::Client> documentation for further details.
+
+=head2 Example program
+
+    #!/usr/bin/env perl
+
+    use strict;
+    use warnings FATAL => qw( all );
+
+    use Nebulous::Client;
+
+    my $neb = Nebulous::Client->new(
+        proxy   => 'http://podb1.ifa.hawaii.edu:80/nebulous'
+    );
+
+    my $key = shift || 'foobarbaz';
+
+    # make sure there isn't already a file named "foobarbaz" so this example
+    # doesn't cause an error
+    $neb->delete($key);
+
+    my $fh = $neb->open_create($key);
+    die "can't create file $key " unless $fh;
+    close($fh);
+
+    if (!$neb->replicate($key)) {
+        die "can't replicate object $key";
+    }
+
+    if (!$neb->delete($key)) {
+        die "can't delete object $key";
+    }
+
+=head1 C CLIENT
+
+The Nebulous C Client consists of a single library named
+F<libnebclient-x.x.x.so> (there is also a static lib, F<libnebclient.a>) and a
+single header file named F<nebclient.h>. The API is documented in
+C<nebclient(3)>. As a convince, pkgconfig configuration information is provided
+for linking against the shared library.
+
+=head2 pkgconfig
+
+The C<pkg-config> utility needs to be able to find the pkgconfig info file
+(F<nebulous.pc>) that Nebulous installed. If Nebulous was installed to a path
+other than F</usr> you may need to set the environment variable
+C<PKG_CONFIG_PATH> so that C<pkg-config> can find it.  For example, if Nebulous
+was installed under F</usr/local> you would set:
+
+    export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
+
+=head2 Autoconf
+
+If you are using Autoconf to configure your software, it is highly recommended
+that you use pkgconfig to link against Nebulous.  Below is an example suitable
+for inclusion in your F<configure.ac> file.
+
+    PKG_CHECK_MODULES([NEBCLIENT], [nebclient >= 0.0.1])
+
+This will setup the variables C<NEBLCLIENT_CFLAGS> and C<NEBCLIENT_LIBS> for
+you and C<AC_SUBST()> them.
+
+=head2 Example makefile
+
+    NEB_CFLAGS=`pkg-config nebclient --cflags`
+    NEB_LIBS=`pkg-config nebclient --libs`
+
+    nebexample: nebexample.c
+    	$(CC) $(NEB_CFLAGS) $(CFLAGS) $(NEB_LIBS) $< -o $@
+
+=head2 Example program
+
+    #include <stdio.h>
+    #include <stdlib.h>
+    #include <unistd.h>
+    #include <nebclient.h>
+
+    #define NEB_SERVER  "http://podb1.ifa.hawaii.edu:80/nebulous"
+    #define NEB_KEY     "foobarbaz"
+
+    int main (int argc, char **argv) {
+        nebServer       *server = NULL;
+        char            *key = NEB_KEY;
+
+        server = nebServerAlloc(NEB_SERVER);
+        if (!server) {
+            printf("nebServerAlloc() failed\n");
+            exit(EXIT_FAILURE);
+        }
+
+        // make sure there isn't already a file named "foobarbaz" so this
+        // example doesn't cause an error
+        nebDelete(server, key);
+
+        int fh = nebOpenCreate(server, key, 0, NULL, NULL, NULL);
+        if (fh < 0) {
+            printf( "nebOpenCreate() failed: %s\n", nebErr(server));
+            exit(EXIT_FAILURE);
+        }
+
+        // fh is a file descriptor
+        close(fh);
+
+        if (!nebReplicate(server, key, NULL, NULL)) {
+            printf( "nebReplicate() failed: %s\n", nebErr(server));
+            exit(EXIT_FAILURE);
+        }
+
+        if (!nebDelete(server, key)) {
+            printf( "nebDelete() failed: %s\n", nebErr(server));
+            exit(EXIT_FAILURE);
+        }
+
+        exit(EXIT_SUCCESS);
+    }
+
+=head1 DEVELOPER NOTES
+
+All of the examples in this guide are included with the Nebulous distribution
+under the F<./examples> directory.
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the LICENSE file included with
+this module, or in the L<perlgpl> Pod as supplied with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Client>, L<nebclient(3)>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Util.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Util.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/lib/Nebulous/Util.pm	(revision 22322)
@@ -0,0 +1,206 @@
+# Copyright (c) 2004  Joshua Hoblitt
+#
+# $Id: Util.pm,v 1.13 2008-07-10 23:21:27 jhoblitt Exp $
+
+package Nebulous::Util;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = '0.02';
+
+use base qw( Exporter );
+
+use File::Spec::Functions;
+use Log::Log4perl qw( :levels );
+use URI::file;
+use URI;
+
+my @symbols = qw(
+    %LEVELS
+    _nuke_file
+    _get_file_path
+    _get_filehandle
+    _open_uri
+    parse_neb_key
+    print_xattrs
+    print_all_xattrs
+    write_xattrs
+    delete_xattrs
+    parse_xattr_pair
+);
+
+our @EXPORT_OK      = @symbols;
+our %EXPORT_TAGS    = ( standard => [ @symbols ] );
+
+our %LEVELS = (
+    off     => $OFF,
+    fatal   => $FATAL,
+    error   => $ERROR,
+    warn    => $WARN,
+    info    => $INFO,
+    debug   => $DEBUG,
+    all     => $ALL,
+);
+
+
+sub _nuke_file {
+    my $path = shift;
+
+    die "can't unlink file $path: it's a directory" 
+        if -d $path;
+    die "can't unlink file $path: it doesn't exist"
+        unless -e $path;
+    unlink $path or die "can't unlink file $path: $!";
+
+    return 1;
+}
+
+
+sub _get_file_path {
+    my $uri = shift;
+
+    my $path = URI->new( $uri )->path;
+
+    return $path;
+}
+
+
+sub _get_filehandle {
+    my ( $path, $flags ) = @_;
+
+    # XXX this is a attempt to work around some sort of nasty NFS bug where
+    # occasionally stat()/open() on a file on an NFS mounted filesystem will
+    # fail EVEN THOU THE FILE ACTUALLY EXISTS.
+    #
+    # The instance file attempting to be opened should always exist as it was
+    # created by the Nebulous server.
+    my $fh;
+    for (my $i = 0; $i < 60; $i++) {
+        eval {
+            die "can't open file $path: file doesn't exist" 
+                unless -e $path;
+            CORE::open($fh, $flags, $path)
+                or die "can't open file $path: $!";
+        };
+        if ($@ =~ qr/file doesn't exist/) {
+            sleep 1;
+            next;
+        } 
+        if ($@) {
+            die $@;
+        }
+
+        last;
+    }
+    
+    return $fh;
+}
+
+
+sub _open_uri {
+    my ( $uri , $flags ) = @_;
+
+    $uri = URI->new("$uri");
+
+    my $fh = _get_filehandle($uri->file, $flags);
+
+    return $fh;
+}
+
+
+sub print_all_xattrs
+{
+    my ($neb, $key) = @_;
+
+    return unless defined $neb;
+    return unless defined $key;
+
+    my $xattr_names = $neb->listxattr($key) or die $neb->err;
+    foreach my $name (@$xattr_names) {
+        print_xattrs($neb, $key, $name) or return;
+    }
+
+    return 1;
+}
+
+
+sub print_xattrs
+{
+    my ($neb, $key, @xattr_names) = @_;
+
+    return unless defined $neb;
+    return unless defined $key;
+    return unless scalar @xattr_names;
+
+    foreach my $arg (@xattr_names) {
+        my ($name, $value) = parse_xattr_pair($arg);
+        die "can not process $arg because it is in name:value form"
+            if defined $value;
+        $value = $neb->getxattr($key, $name) or die $neb->err;
+        print "$name:$value\n";
+    }
+
+    return 1;
+}
+
+
+sub write_xattrs
+{
+    my ($neb, $key, @xattr_pairs) = @_;
+
+    return unless defined $neb;
+    return unless defined $key;
+    return unless scalar @xattr_pairs;
+
+    foreach my $arg (@xattr_pairs) {
+        my ($name, $value) = parse_xattr_pair($arg);
+        die "can not process $arg because it is not in name:value form"
+            unless defined $name and defined $value;
+        die "xattr name: $name is not in to the form user.name"
+            unless $name =~ /^user\./;
+        $neb->setxattr($key, $name, $value, "replace")
+            or die $neb->err;
+    }
+
+    return 1;
+}
+
+
+sub delete_xattrs
+{
+    my ($neb, $key, @xattr_names) = @_;
+
+    return unless defined $neb;
+    return unless defined $key;
+    return unless scalar @xattr_names;
+
+    foreach my $arg (@xattr_names) {
+        my ($name, $value) = parse_xattr_pair($arg);
+        die "can not process $arg because it is in name:value form"
+            if defined $value;
+        $neb->removexattr($key, $name)
+            or die $neb->err;
+    }
+
+    return 1;
+}
+
+
+sub parse_xattr_pair
+{
+    my $pair = shift;
+    
+    return unless defined $pair;
+
+    no warnings qw( uninitialized );
+    my ($name, $value) = split(/:/, $pair);
+    use warnings;
+
+    return ($name, $value);
+}
+
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/.cvsignore	(revision 22322)
@@ -0,0 +1,21 @@
+Doxyfile
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+docs
+install-sh
+libtool
+ltmain.sh
+missing
+nebclient.pc
+stamp-h1
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/Doxyfile.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/Doxyfile.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/Doxyfile.in	(revision 22322)
@@ -0,0 +1,1219 @@
+# Doxyfile 1.4.2
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = @PACKAGE_NAME@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = @PACKAGE_VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+# @top_builddir@ doesn't work for some reason
+OUTPUT_DIRECTORY       = @builddir@/docs
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like the Qt-style comments (thus requiring an 
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation.
+
+SHOW_DIRECTORIES       = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from the 
+# version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the progam writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = @top_srcdir@/src
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+
+FILE_PATTERNS          = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = @PERL@
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that a graph may be further truncated if the graph's 
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH 
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), 
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, which results in a white background. 
+# Warning: Depending on the platform used, enabling this option may lead to 
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to 
+# read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/Makefile.am	(revision 22322)
@@ -0,0 +1,21 @@
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: Makefile.am,v 1.13 2007-05-04 02:14:04 jhoblitt Exp $
+
+SUBDIRS = src tests
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= @PACKAGE@.pc
+
+EXTRA_DIST = nebulous.wsdl
+
+if HAVE_DOXYGEN
+man_MANS = \
+    $(top_builddir)/docs/man/man3/@PACKAGE@.3
+
+docs/man/man3/@PACKAGE@.3:
+	$(DOXYGEN)
+endif
+
+clean-local:
+	-rm -rf docs
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/autogen.sh	(revision 22322)
@@ -0,0 +1,116 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=nebclient
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=nebulous.wsdl
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+# bypass taps bootstrap.sh
+cd ./tests/tap
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+cd $ORIGDIR
+
+if test -n `which wsdl2h`; then
+    wsdl2h -o src/nebulous.h -c nebulous.wsdl \
+    && soapcpp2 -d src -c src/nebulous.h \
+    && rm -f src/*.xml
+fi
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/configure.ac	(revision 22322)
@@ -0,0 +1,52 @@
+dnl Copyright (C) 2005  Joshua Hoblitt
+dnl
+dnl $Id: configure.ac,v 1.19 2008-09-15 21:26:28 jhoblitt Exp $
+
+AC_PREREQ(2.61)
+
+AC_INIT([nebclient], [0.06], [jhoblitt@cpan.org])
+AC_CONFIG_SRCDIR([nebulous.wsdl])
+
+dnl this enables the building of libtap
+AC_CONFIG_SUBDIRS([tests/tap])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([config.h])
+AM_MAINTAINER_MODE
+
+AC_LANG(C)
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+AC_ARG_ENABLE(doxygen,
+  [AS_HELP_STRING(--enable-doxygen ,enable manpage generation)],
+  [AC_MSG_RESULT(doxygen enabled)
+    AC_PATH_PROG([DOXYGEN], [doxygen], [])
+  ],
+  [AC_MSG_RESULT([doxygen disabled])
+    doxygen=off
+  ]
+)
+AM_CONDITIONAL([HAVE_DOXYGEN], test -n "$DOXYGEN" -a "x$doxygen" != "xoff")
+
+dnl needed by doxygen
+AC_PATH_PROG([PERL], [perl], [missing])
+if test "$PERL" = "missing" ; then
+  AC_MSG_ERROR([perl is required])
+fi
+
+AM_CFLAGS="-Wall -fno-strict-aliasing"
+AC_SUBST([AM_CFLAGS])
+
+dnl default install prefix
+AC_PREFIX_DEFAULT([/usr])
+
+AC_CONFIG_FILES([
+  Makefile
+  nebclient.pc
+  Doxyfile
+  src/Makefile
+  tests/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/nebclient.pc.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/nebclient.pc.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/nebclient.pc.in	(revision 22322)
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: @PACKAGE@
+Description: Nebulous C client
+Version: @VERSION@
+Libs: -L${libdir} -l@PACKAGE@
+Cflags: -I${includedir}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/nebulous.wsdl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/nebulous.wsdl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/nebulous.wsdl	(revision 22322)
@@ -0,0 +1,288 @@
+<?xml version="1.0"?>
+<definitions name="NebulousServer"
+    targetNamespace="urn:Nebulous/Server/SOAP"
+    xmlns:tns="urn:Nebulous/Server/SOAP"
+    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+    xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+    xmlns="http://schemas.xmlsoap.org/wsdl/"
+    xmlns:ns="urn:Nebulous/Server/SOAP">
+
+    <types>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+            targetNamespace="urn:Nebulous/Server/SOAP"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
+ 
+            <complexType name="ArrayOfString">
+                <complexContent>
+                    <restriction base="soapenc:Array">
+                        <attribute ref="soapenc:arrayType" 
+                            wsdl:arrayType="string[]"/>
+                    </restriction>
+                </complexContent>
+            </complexType>
+        </schema>
+    </types>
+
+    <message name="create_objectRequest">
+        <part name="key" type="xsd:string" />
+        <part name="volume" type="xsd:string" />
+    </message>
+    <message name="create_objectResponse">
+        <part name="result" type="xsd:string" />
+    </message>
+
+    <message name="rename_objectRequest">
+        <part name="key" type="xsd:string" />
+        <part name="newkey" type="xsd:string" />
+    </message>
+    <message name="rename_objectResponse">
+        <part name="result" type="xsd:string" />
+    </message>
+
+    <message name="replicate_objectRequest">
+        <part name="key" type="xsd:string" />
+        <part name="volume" type="xsd:string" />
+    </message>
+    <message name="replicate_objectResponse">
+        <part name="result" type="xsd:string" />
+    </message>
+
+    <message name="lock_objectRequest">
+        <part name="key" type="xsd:string" />
+        <part name="type" type="xsd:string" />
+    </message>
+    <message name="lock_objectResponse">
+        <part name="result" type="xsd:int" />
+    </message>
+
+    <message name="unlock_objectRequest">
+        <part name="key" type="xsd:string" />
+        <part name="type" type="xsd:string" />
+    </message>
+    <message name="unlock_objectResponse">
+        <part name="result" type="xsd:int" />
+    </message>
+
+    <message name="find_objectsRequest">
+        <part name="pattern" type="xsd:string" />
+    </message>
+    <message name="find_objectsResponse">
+        <!-- fixme -->
+        <part name="result" type="tns:ArrayOfString" />
+    </message>
+
+    <message name="find_instancesRequest">
+        <part name="key" type="xsd:string" />
+        <part name="volume" type="xsd:string" />
+    </message>
+    <message name="find_instancesResponse">
+        <!-- fixme -->
+        <part name="result" type="tns:ArrayOfString" />
+    </message>
+
+    <message name="delete_instanceRequest">
+        <part name="uri" type="xsd:string" />
+    </message>
+    <message name="delete_instanceResponse">
+        <part name="result" type="xsd:int" />
+    </message>
+
+    <message name="stat_objectRequest">
+        <part name="key" type="xsd:string" />
+    </message>
+    <message name="stat_objectResponse">
+        <!-- fixme -->
+        <part name="result" type="tns:ArrayOfString" />
+    </message>
+
+
+    <portType name="Nebulous/Server/SOAPPort">
+        <operation name="create_object">
+            <input  message="tns:create_objectRequest" />
+            <output message="tns:create_objectResponse" />
+            <!--
+                <fault name="" message="" />
+            -->
+        </operation>
+        <operation name="rename_object">
+            <input  message="tns:rename_objectRequest" />
+            <output message="tns:rename_objectResponse" />
+            <!--
+                <fault name="" message="" />
+            -->
+        </operation>
+        <operation name="replicate_object">
+            <input  message="tns:replicate_objectRequest" />
+            <output message="tns:replicate_objectResponse" />
+            <!--
+                <fault name="" message="" />
+            -->
+        </operation>
+        <operation name="lock_object">
+            <input  message="tns:lock_objectRequest" />
+            <output message="tns:lock_objectResponse" />
+            <!--
+                <fault name="" message="" />
+            -->
+        </operation>
+        <operation name="unlock_object">
+            <input  message="tns:unlock_objectRequest" />
+            <output message="tns:unlock_objectResponse" />
+            <!--
+                <fault name="" message="" />
+            -->
+        </operation>
+        <operation name="find_objects">
+            <input  message="tns:find_objectsRequest" />
+            <output message="tns:find_objectsResponse" />
+            <!--
+                <fault name="" message="" />
+            -->
+        </operation>
+        <operation name="find_instances">
+            <input  message="tns:find_instancesRequest" />
+            <output message="tns:find_instancesResponse" />
+            <!--
+                <fault name="" message="" />
+            -->
+        </operation>
+        <operation name="delete_instance">
+            <input  message="tns:delete_instanceRequest" />
+            <output message="tns:delete_instanceResponse" />
+            <!--
+                <fault name="" message="" />
+            -->
+        </operation>
+        <operation name="stat_object">
+            <input  message="tns:stat_objectRequest" />
+            <output message="tns:stat_objectResponse" />
+            <!--
+                <fault name="" message="" />
+            -->
+        </operation>
+    </portType>
+
+
+    <binding name="SOAP" type="tns:Nebulous/Server/SOAPPort">
+        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
+        <operation name="create_object">
+            <soap:operation soapAction="urn:Nebulous/Server/SOAP#create_object" />
+            <input>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </input>
+            <output>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </output>
+        </operation>
+        <operation name="rename_object">
+            <soap:operation soapAction="urn:Nebulous/Server/SOAP#create_object" />
+            <input>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </input>
+            <output>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </output>
+        </operation>
+        <operation name="rename_object">
+            <soap:operation soapAction="urn:Nebulous/Server/SOAP#rename_object" />
+            <input>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </input>
+            <output>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </output>
+        </operation>
+        <operation name="replicate_object">
+            <soap:operation soapAction="urn:Nebulous/Server/SOAP#replicate_object" />
+            <input>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </input>
+            <output>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </output>
+        </operation>
+        <operation name="lock_object">
+            <soap:operation soapAction="urn:Nebulous/Server/SOAP#lock_object" />
+            <input>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </input>
+            <output>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </output>
+        </operation>
+        <operation name="unlock_object">
+            <soap:operation soapAction="urn:Nebulous/Server/SOAP#unlock_object" />
+            <input>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </input>
+            <output>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </output>
+        </operation>
+        <operation name="find_objects">
+            <soap:operation soapAction="urn:Nebulous/Server/SOAP#find_objects" />
+            <input>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </input>
+            <output>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </output>
+        </operation>
+        <operation name="find_instances">
+            <soap:operation soapAction="urn:Nebulous/Server/SOAP#find_instances" />
+            <input>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </input>
+            <output>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </output>
+        </operation>
+        <operation name="delete_instance">
+            <soap:operation soapAction="urn:Nebulous/Server/SOAP#delete_instance" />
+            <input>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </input>
+            <output>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </output>
+        </operation>
+        <operation name="stat_object">
+            <soap:operation soapAction="urn:Nebulous/Server/SOAP#stat_object" />
+            <input>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </input>
+            <output>
+                <soap:body use="encoded" namespace="urn:Nebulous/Server/SOAP"
+                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
+            </output>
+        </operation>
+    </binding>
+
+
+    <service name="Nebulous/Server/SOAP">
+        <port name="Nebulous/Server/SOAPPort" binding="tns:SOAP">
+            <soap:address location="http://localhost:80/nebulous" />
+        </port>
+    </service>
+</definitions>
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/.cvsignore	(revision 22322)
@@ -0,0 +1,19 @@
+.deps
+.libs
+Makefile
+Makefile.in
+SOAP.nsmap
+libnebclient.la
+nebclient.lo
+nebulous.h
+soapC.c
+soapC.lo
+soapClient.c
+soapClient.lo
+soapClientLib.c
+soapH.h
+soapServer.c
+soapServerLib.c
+soapStub.h
+stdsoap2.lo
+xmalloc.lo
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/Makefile.am	(revision 22322)
@@ -0,0 +1,19 @@
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: Makefile.am,v 1.5 2005-07-27 01:28:04 jhoblitt Exp $
+
+include_HEADERS=nebclient.h
+lib_LTLIBRARIES = libnebclient.la
+libnebclient_la_LDFLAGS = -release @PACKAGE_VERSION@
+libnebclient_la_SOURCES =\
+    nebclient.h\
+    nebclient.c\
+    xmalloc.h\
+    xmalloc.c\
+    SOAP.nsmap\
+    soapC.c\
+    soapClient.c\
+    soapH.h\
+    soapStub.h\
+    stdsoap2.c\
+    stdsoap2.h
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/SOAP.nsmap
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/SOAP.nsmap	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/SOAP.nsmap	(revision 22322)
@@ -0,0 +1,11 @@
+
+#include "soapH.h"
+SOAP_NMAC struct Namespace namespaces[] =
+{
+	{"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/*/soap-envelope", NULL},
+	{"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/*/soap-encoding", NULL},
+	{"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance", NULL},
+	{"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema", NULL},
+	{"ns1", "urn:Nebulous/Server/SOAP", NULL, NULL},
+	{NULL, NULL, NULL, NULL}
+};
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/nebclient.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/nebclient.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/nebclient.c	(revision 22322)
@@ -0,0 +1,927 @@
+/*
+ * nebclient.c - nebulous client
+ *
+ * Copyright (C) 2005  Joshua Hoblitt
+ *
+ * $Id: nebclient.c,v 1.50 2008-09-15 21:23:36 jhoblitt Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <regex.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <time.h>
+#include "xmalloc.h"
+#include "soapH.h"
+#include "SOAP.nsmap"
+#include "nebclient.h"
+
+#define ERRBUF_SIZE 2048
+#define LOCK_TIMEOUT 10
+#define LOCK_INTERVAL 1
+
+#define nebSetErr(server,...) \
+    p_nebSetErr(__FILE__, __LINE__, __func__, server,__VA_ARGS__)
+
+// this is a slightly more useful error message than returned by assert()
+#define REQUIRE_SERVER \
+    if (!server) { \
+        fprintf(stderr, "parameter 'server' may not be NULL"); \
+        abort(); \
+    }
+
+#define NEBCOPYFILE 0
+
+static void p_nebSetErr(const char * filename, unsigned long lineno, const char *function, nebServer *server, const char *format, ...);
+static void nebSetServerErr(nebServer *server);
+#if NEBCOPYFILE
+static off_t nebCopyFile(nebServer *server, const char *source, const char *dest);
+#endif
+static off_t nebCopyFilehandle(nebServer *server, int sourceFH, int destFH);
+
+static size_t nebParseURI(nebServer *server, const char *URI, char **filename);
+static bool nebNukeFile(nebServer *server, const char *filename);
+static char *nullstrncpy(char *dest, const char *src, size_t n);
+
+nebServer *nebServerAlloc(const char *endpoint)
+{
+    nebServer       *server;
+
+    server          = xmalloc(sizeof(nebServer));
+
+    if (endpoint) {
+        server->endpoint = xmalloc(strlen(endpoint) + 1);
+        strcpy(server->endpoint, endpoint);
+    } else {
+        server->endpoint = NULL;
+    }
+
+    server->soap    = xmalloc(sizeof(struct soap));
+    server->err_buf = xmalloc(ERRBUF_SIZE);
+    server->err_buf_size = ERRBUF_SIZE;
+
+    // init err buffer
+    memset(server->err_buf, 0, server->err_buf_size);
+
+    soap_init(server->soap);
+    soap_set_namespaces(server->soap, namespaces); 
+
+    return server;
+}
+
+void nebServerFree(nebServer *server)
+{
+    if (!server) {
+        return;
+    }
+
+    soap_end(server->soap); // remove deserialized data and clean up
+    soap_done(server->soap); // detach the gSOAP environment
+
+    nebFree(server->endpoint);
+    nebFree(server->soap);
+    nebFree(server->err_buf);
+    nebFree(server);
+}
+
+char *nebCreate(nebServer *server, const char *key, const char *volume, char **URI)
+{
+    struct ns1__create_USCOREobjectResponse response;
+    char            *filename;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return NULL;
+    }
+
+    if (soap_call_ns1__create_USCOREobject(server->soap, server->endpoint, NULL,
+            (char *)key, (char *)volume, (char **)&response) != SOAP_OK) {
+        nebSetServerErr(server);
+
+        return NULL;
+    }
+
+    if (!(char *)response.result) {
+        nebSetErr(server, "server returned no result");
+        return NULL;
+    }
+
+    if (!nebParseURI(server, (char *)response.result, &filename)) {
+        nebSetErr(server, "can not parse URI");
+        return NULL;
+    }
+
+    // if URI is not NULL
+    if (URI) {
+        *URI = xmalloc(strlen((char *)response.result) + 1);
+        strcpy(*URI, (char *)response.result);
+    }
+
+    return filename;
+}
+
+int nebOpenCreate(nebServer *server, const char *key, const char *volume, char **URI)
+{
+    char            *filename;
+    int             file;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return -1;
+    }
+
+    filename = nebCreate(server, key, volume, URI);
+
+    file = open(filename, O_RDWR|O_TRUNC, 0660);
+    if (file == -1) {
+        nebSetErr(server, "can not open %s: %s", filename, strerror(errno));
+        nebFree(filename);
+
+        return -1;
+    }
+
+    nebFree(filename);
+
+    return file;
+}
+
+bool nebReplicate(nebServer *server, const char *key, const char *volume, char **URI)
+{
+    struct ns1__replicate_USCOREobjectResponse response;
+    char            *filename;
+    int             sourceFH, destFH;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return false;
+    }
+
+
+    // We have to open the instance that we're going to copy from first.  If
+    // we don't do this, it's possible that open will find & open the new
+    // instance that we're in the process of creating
+    sourceFH = nebOpen(server, (char *)key, NEB_READ);
+    if (sourceFH == -1) {
+        nebSetErr(server, "can not open key %s", key);
+        nebFree(filename);
+
+        return false;
+    }
+
+    // ask the server for a new instance attached to our key
+    if (soap_call_ns1__replicate_USCOREobject(server->soap, server->endpoint,
+            NULL, (char *)key, (char *)volume, (char **)&response) != SOAP_OK) {
+        nebSetServerErr(server);
+
+        return false;
+    }
+
+    if (!nebParseURI(server, (char *)response.result, &filename)) {
+        nebSetErr(server, "can not parse URI");
+
+        return false;
+    }
+
+    destFH = open(filename, O_RDWR|O_TRUNC, 0660);
+    if (destFH == -1) {
+        nebSetErr(server, "can not open %s: %s\n", filename, strerror(errno));
+        nebFree(filename);
+
+        return false;
+    }
+
+    nebCopyFilehandle(server, sourceFH, destFH);
+
+    if (close(sourceFH) == -1) {
+        nebSetErr(server, "can not close file: %s", strerror(errno));
+        nebFree(filename);
+
+        return false;
+    }
+
+    if (close(destFH) == -1) {
+        nebSetErr(server, "can not close %s: %s", filename, strerror(errno));
+        nebFree(filename);
+
+        return false;
+    }
+
+    nebFree(filename);
+
+    // if URI is not NULL
+    if (URI) {
+        *URI = xmalloc(strlen((char *)response.result) + 1);
+        strcpy(*URI, (char *)response.result);
+    }
+
+    return true;
+}
+
+bool nebCull(nebServer *server, const char *key)
+{
+    nebObjectInstances *locations;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return false;
+    }
+
+    locations = nebFindInstances(server, key, NULL);
+    if (!locations) {
+        nebSetErr(server, "can not cull - not enough instances");
+
+        return false;
+    }
+
+    if (!nebDeleteInstance(server, locations->URI[0])) {
+        nebObjectInstancesFree(locations);
+
+        return false;
+    }
+
+    nebObjectInstancesFree(locations);
+
+    return true;
+}
+
+bool nebLock(nebServer *server, const char *key, nebRW flag)
+{
+    time_t          endtime;
+    char            *type;
+    int             response;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return false;
+    }
+
+    type = flag == NEB_READ ? "read" : "write";
+
+    endtime = time(NULL) + LOCK_TIMEOUT;
+
+    while (time(NULL) < endtime) {
+        if (soap_call_ns1__lock_USCOREobject(server->soap, server->endpoint,
+            NULL, (char *)key, type, &response) != SOAP_OK) {
+            nebSetServerErr(server);
+            return false;
+        }
+
+        if (response) {
+            return true;
+        }
+
+        sleep(LOCK_INTERVAL);
+    }
+
+    // else, we never got a lock
+    nebSetErr(server, "can not get a %s lock", type);
+
+    return false;
+}
+
+bool nebUnlock(nebServer *server, const char *key, nebRW flag)
+{
+    char            *type;
+    int             response;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return false;
+    }
+
+    type = flag == NEB_READ ? "read" : "write";
+
+    if (soap_call_ns1__unlock_USCOREobject(server->soap, server->endpoint,
+        NULL, (char *)key, type, &response) != SOAP_OK) {
+        nebSetServerErr(server);
+        return false;
+    }
+
+    if (response) {
+        return true;
+    }
+
+    // else, we never got a lock
+    nebSetErr(server, "can not release %s lock", type);
+
+    return false;
+}
+
+nebObjectInstances *nebFindInstances(nebServer *server, const char *key, const char *volume)
+{
+    struct ns1__find_USCOREinstancesResponse response;
+    int             resultElements = 0;
+    char            **resultArray;
+    nebObjectInstances *locations = NULL;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return NULL;
+    }
+
+    // FIXME is this leaking memory when response goes out of scope?  the gsoap
+    // manual seems to 'suggest' that this is temporary data that gets cleaed
+    // up on the next soap function call
+    if (soap_call_ns1__find_USCOREinstances(server->soap, server->endpoint,
+            NULL, (char *)key, (char *)volume, &response) != SOAP_OK) {
+        nebSetServerErr(server);
+        return NULL;
+    }
+
+    resultElements = response.result->__size;
+
+    if (resultElements) {
+        locations = xmalloc(sizeof(nebObjectInstances));
+        locations->n = resultElements;
+        
+        resultArray    = response.result->__ptr;
+
+        locations->URI = xmalloc(sizeof(char*) * resultElements);
+
+        for (int i = 0; i < resultElements; i++) {
+            locations->URI[i] = xmalloc(strlen(resultArray[i]) + 1);
+            strcpy(locations->URI[i], resultArray[i]);
+        }
+
+    }
+
+    return locations;
+}
+
+void nebObjectInstancesFree(nebObjectInstances *locations)
+{
+    if (!locations) {
+        return;
+    }
+
+    for (int i = 0; i < locations->n; i++) {
+        nebFree(locations->URI[i]);
+    }
+
+    nebFree(locations);
+}
+
+char *nebFind(nebServer *server, const char *key)
+{
+    nebObjectInstances *locations;
+    char            *filename;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return NULL;
+    }
+    
+    locations = nebFindInstances(server, key, NULL);
+    if (!locations) {
+        if (!strstr(nebErr(server), "no instances on storage volume")) {
+            nebSetErr(server, "no instances found");
+            return NULL;
+        }
+
+        locations = nebFindInstances(server, key, "any");
+        if (!locations) {
+            nebSetErr(server, "no instances found");
+            return NULL;
+        }
+    }
+
+    if (!nebParseURI(server, locations->URI[0], &filename)) {
+        nebSetErr(server, "can not parse URI");
+        nebObjectInstancesFree(locations);
+
+        return NULL;
+    }
+
+    nebObjectInstancesFree(locations);
+
+    return filename;
+}
+
+int nebOpen(nebServer *server, const char *key, nebRW flag)
+{
+    nebObjectInstances *locations;
+    char            *filename;
+    int             fh;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return -1;
+    }
+
+    locations = nebFindInstances(server, key, NULL);
+    if (!locations) {
+        nebSetErr(server, "no instances found");
+
+        return -1;
+    }
+
+    if (!nebParseURI(server, locations->URI[0], &filename)) {
+        nebSetErr(server, "can not parse URI");
+        nebObjectInstancesFree(locations);
+
+        return -1;
+    }
+
+    if (flag == NEB_WRITE) {
+        if (locations->n > 1) {
+            nebSetErr(server, "write not allowed with multiple instances");
+            nebFree(filename);
+            nebObjectInstancesFree(locations);
+
+            return -1;
+        }
+        fh = open(filename, O_RDWR);
+    } else {
+        fh = open(filename, O_RDONLY);
+    }
+
+    nebFree(filename);
+    nebObjectInstancesFree(locations);
+
+    if (fh < 0) {
+        nebSetErr(server, "open: %s", strerror(errno));
+
+        return -1;
+    }
+
+    return fh;
+}
+
+bool nebDelete(nebServer *server, const char *key)
+{
+    nebObjectInstances *locations;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return false;
+    }
+
+    locations = nebFindInstances(server, key, "any");
+    if (!locations) {
+        nebSetErr(server, "no instances found");
+
+        return false;
+    }
+
+    for (int i = 0; i < locations->n; i++) {
+        if (!nebDeleteInstance(server, locations->URI[i])) {
+            nebSetErr(server, "can not delete instance");
+            nebObjectInstancesFree(locations);
+
+            return false;
+        }
+    }
+
+    nebObjectInstancesFree(locations);
+
+    return true;
+}
+
+bool nebCopy(nebServer *server, const char *key, const char *newKey)
+{
+    int             sourceFH;
+    int             destFH;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return false;
+    }
+
+    if (!newKey) {
+        nebSetErr(server, "parameter 'newKey' may not be NULL");
+
+        return false;
+    }
+
+    sourceFH = nebOpen(server, (char *)key, NEB_READ);
+    if (sourceFH < 0) {
+        // propigate nebOpen() error
+
+        return false;
+    }
+
+    destFH = nebOpenCreate(server, newKey, NULL, NULL);
+    if (destFH < 0) {
+        // propigate nebCreate() error
+
+        return false;
+    }
+
+    nebCopyFilehandle(server, sourceFH, destFH);
+
+    if (close(sourceFH) == -1) {
+        nebSetErr(server, "can not close file: %s", strerror(errno));
+
+        return false;
+    }
+
+    if (close(destFH) == -1) {
+        nebSetErr(server, "can not close file: %s", strerror(errno));
+
+        return false;
+    }
+
+    return true;
+}
+
+bool nebMove(nebServer *server, const char *key, const char *newKey)
+{
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return false;
+    }
+
+    if (!newKey) {
+        nebSetErr(server, "parameter 'newKey' may not be NULL");
+
+        return false;
+    }
+
+    if (!nebCopy(server, key, newKey)) {
+        return false;
+    }
+
+    if (!nebDelete(server, key)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool nebDeleteInstance(nebServer *server, const char *URI)
+{
+    int             response;
+    char            *filename;
+    bool            status;
+
+    REQUIRE_SERVER;
+
+    if (!URI) {
+        nebSetErr(server, "parameter 'URI' may not be NULL");
+
+        return false;
+    }
+
+    if (!nebParseURI(server, URI, &filename)) {
+        nebSetErr(server, "can not parse URI");
+
+        return false;
+    }
+
+    if (soap_call_ns1__delete_USCOREinstance(server->soap, server->endpoint,
+            NULL, (char *)URI, &response) != SOAP_OK) {
+        nebFree(filename);
+
+        return false;
+    }
+
+    if (response > 0) {
+        status = nebNukeFile(server, filename);
+    } else {
+        status = false;
+    }
+
+    nebFree(filename);
+
+    return status;
+}
+
+nebObjectStat *nebStat(nebServer *server, const char *key)
+{
+    struct ns1__stat_USCOREobjectResponse response;
+    nebObjectStat   *stat;
+    int             resultElements = 0;
+    char            **resultArray = NULL;
+
+    REQUIRE_SERVER;
+
+    if (!key) {
+        nebSetErr(server, "parameter 'key' may not be NULL");
+
+        return NULL;
+    }
+
+    stat = xmalloc(sizeof(nebObjectStat));
+
+    // FIXME is this leaking memory when response goes out of scope?  the gsoap
+    // manual seems to 'suggest' that this is temporary data that gets cleaed
+    // up on the next soap function call
+    if (soap_call_ns1__stat_USCOREobject(server->soap, server->endpoint,
+            NULL, (char *)key, &response) != SOAP_OK) {
+        nebSetServerErr(server);
+        return NULL;
+    }
+
+    resultElements = response.result->__size;
+    resultArray    = response.result->__ptr;
+
+    if (resultElements != 7) {
+        nebSetErr(server, "server didn't return the proper number of stat elements");
+        return NULL;
+    }
+
+    nullstrncpy(stat->so_id, resultArray[0], 256);
+    nullstrncpy(stat->ext_id, resultArray[1], 256);
+    nullstrncpy(stat->read_lock, resultArray[2], 256);
+    nullstrncpy(stat->write_lock, resultArray[3], 256);
+    nullstrncpy(stat->epoch, resultArray[4], 256);
+    nullstrncpy(stat->mtime, resultArray[5], 256);
+    stat->instances = atoi(resultArray[6]);
+
+    return stat;
+}
+
+char *nebErr(nebServer *server)
+{
+    return server->err_buf;
+}
+
+void nebFree(void *ptr) {
+    xfree(ptr);
+}
+
+
+static void p_nebSetErr(const char * filename, unsigned long lineno, const char *function, nebServer *server, const char *format, ...)
+{
+    va_list         args;
+    size_t          err_size;
+    char            err_location[ERRBUF_SIZE];
+    size_t          err_location_size; 
+
+    err_location_size = snprintf(err_location, ERRBUF_SIZE, "%s:%lu %s() - ",
+            filename, lineno, function);
+
+    if (err_location_size < 0) {
+        fprintf(stderr, "failed to set error info");
+
+        abort();
+    }
+
+    // copy error location info to start of error string
+    strncpy(server->err_buf, err_location, ERRBUF_SIZE);
+
+    while (1) {
+        va_start(args, format);
+        // append error string after error location info
+        err_size = vsnprintf(
+                server->err_buf + err_location_size,
+                server->err_buf_size - err_location_size,
+                format, args);
+        va_end(args);
+
+        if (err_size < 0) {
+            fprintf(stderr, "failed to set error message");
+
+            abort();
+        }
+
+        if (err_size < server->err_buf_size) {
+            break;
+        }
+
+        // else: expand err_buf
+        // account for '\0'
+        server->err_buf_size = err_size + 1;
+        xrealloc(server->err_buf, server->err_buf_size);
+    }
+
+}
+
+static void nebSetServerErr(nebServer *server)
+{
+    const char      **code;
+    const char      **string;
+
+    REQUIRE_SERVER;
+
+    code   = soap_faultcode(server->soap);
+    string = soap_faultstring(server->soap);
+
+    nebSetErr(server, "%s - %s", *code, *string);
+}
+
+#if NEBCOPYFILE
+static off_t nebCopyFile(nebServer *server, const char *source, const char *dest)
+{
+    int             sourceFH;
+    int             destFH; 
+    off_t           bytesCopied;
+
+    REQUIRE_SERVER;
+
+    sourceFH = open(source, O_RDONLY);
+    if (sourceFH == -1) {
+        nebSetErr(server, "can not open %s: %s", source, strerror(errno));
+
+        return -1;
+    }
+
+    destFH = open(dest, O_WRONLY|O_CREAT|O_CREAT, 0600);
+    if (destFH == -1) {
+        nebSetErr(server, "can not open %s: %s", dest, strerror(errno));
+
+        return -1;
+    }
+
+    bytesCopied = nebCopyFilehandle(server, sourceFH, destFH);
+    if (bytesCopied < 0) {
+        return -1;
+    }
+
+    if (close(sourceFH) == -1) {
+        nebSetErr(server, "can not close %s: %s", source, strerror(errno));
+
+        return -1;
+    }
+
+    if (close(destFH) == -1) {
+        nebSetErr(server, "can not close %s: %s", dest, strerror(errno));
+
+        return -1;
+    }
+
+    return bytesCopied;
+}
+#endif
+
+static off_t nebCopyFilehandle(nebServer *server, int sourceFH, int destFH)
+{
+    off_t           bytesTotal;
+    off_t           bytesRemaining;
+    off_t           writeSize;
+    char            writeBuf[64 * 1024];
+    struct stat     sourceStat;
+
+    REQUIRE_SERVER;
+
+    if(fstat(sourceFH, &sourceStat)) {
+        nebSetErr(server, "can not stat filehandles: %s", strerror(errno));
+
+        return -1;
+    }
+
+    // the size of the file to copied
+    bytesTotal = sourceStat.st_size;
+
+    /*
+     * If the file size is <= the buffer size call write(2) with the
+     * file size.  If the file size is > the buffer call write with the
+     * buffer size and loop until file size <= buffer size is true.
+    */
+
+    bytesRemaining = bytesTotal;
+
+    while (bytesRemaining) {
+        if (bytesRemaining <= sizeof(writeBuf)) {
+            writeSize = bytesRemaining;
+        } else {
+            writeSize = sizeof(writeBuf);
+        }
+
+        if (read(sourceFH, writeBuf, writeSize) != writeSize) {
+            nebSetErr(server, "can not read filehandle: %s", strerror(errno));
+
+            return -1;
+        }
+
+        if (write(destFH, writeBuf, writeSize) != writeSize) {
+            nebSetErr(server, "can not write filehandles: %s", strerror(errno));
+
+            return -1;
+        }
+
+        bytesRemaining -= writeSize;
+    }
+
+    return bytesTotal;
+}
+
+static size_t nebParseURI(nebServer *server, const char *URI, char **filename)
+{
+    regex_t         myregex;
+    regmatch_t      mymatch[2];
+    char            errbuf[ERRBUF_SIZE];
+    char            *pattern = "^file://(.*)$";
+    int             status;
+    size_t          matchStart, matchEnd;
+    size_t          matchLength;
+    size_t          filename_size;
+
+    REQUIRE_SERVER;
+
+    if (!URI) {
+        nebSetErr(server, "parameter 'URI' may not be NULL");
+
+        return 0;
+    }
+
+    if (!filename) {
+        nebSetErr(server, "parameter 'filename' may not be NULL");
+
+        return 0;
+    }
+
+    status = regcomp(&myregex, pattern, REG_EXTENDED);
+    if (status != 0) {
+        regerror(status, &myregex, errbuf, ERRBUF_SIZE);
+        nebSetErr(server, "regcomp error: %s", errbuf);
+
+        return 0;
+    }
+
+    status = regexec(&myregex, URI, 2, mymatch, 0);
+    if (status != 0) {
+        regerror(status, &myregex, errbuf, ERRBUF_SIZE);
+        nebSetErr(server, "regexec error: %s", errbuf);
+
+        return 0;
+    }
+
+    regfree(&myregex);
+
+    matchStart = (size_t)mymatch[1].rm_so;
+    matchEnd   = (size_t)mymatch[1].rm_eo;
+    matchLength = matchEnd - matchStart;
+
+    if (matchStart == -1) {
+        return 0;
+    }
+
+    filename_size = matchLength + 1;
+    *filename = xmalloc(filename_size);
+
+    snprintf(*filename, filename_size, "%.*s", (int)matchLength, URI + matchStart);
+
+    return strlen(*filename);
+}
+
+static bool nebNukeFile(nebServer *server, const char *filename)
+{
+    REQUIRE_SERVER;
+
+    if (!filename) {
+        nebSetErr(server, "parameter 'filename' may not be NULL");
+
+        return false;
+    }
+
+    if (unlink(filename) < 0) {
+        nebSetErr(server, "unlink: %s", strerror(errno));
+
+        return false;
+    }
+
+    return true;
+}
+
+static char *nullstrncpy(char *dest, const char *src, size_t n) {
+    if (!src) {
+        dest = NULL;
+        return dest;
+    }
+
+    return strncpy(dest, src, n);
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/nebclient.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/nebclient.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/nebclient.h	(revision 22322)
@@ -0,0 +1,263 @@
+/*
+ * nebclient.h -  nebulous client header
+ * 
+ * Copyright (C) 2005  Joshua Hoblitt
+ *
+ * $Id: nebclient.h,v 1.32 2007-05-03 21:26:55 jhoblitt Exp $
+ */
+
+#include <stdbool.h>
+
+#ifndef NEBCLIENT_H
+#define NEBCLIENT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// @addtogroup nebclient
+/// @{
+
+typedef enum { NEB_READ, NEB_WRITE } nebRW;
+
+/** Represents a "connection" to a Nebulous server
+ */
+
+typedef struct {
+    void            *soap;
+    char            *err_buf;
+    size_t          err_buf_size;
+    char            *endpoint;
+} nebServer;
+
+/** The properties of a storage object
+ */
+
+typedef struct {
+    char            so_id[256];         ///< storage object ID
+    char            ext_id[256];        ///< storage object key (name)
+    char            read_lock[256];     ///< read lock value
+    char            write_lock[256];    ///< write lock value
+    char            epoch[256];         ///< creation time stamp
+    char            mtime[256];         ///< modification time stamp
+    int             instances;          ///< number of instances
+} nebObjectStat;
+
+/** The storage location of a storage object's instances
+ */
+
+typedef struct {
+    unsigned int    n;                  ///< number of instances
+    char            **URI;              ///< URLs of instances 
+} nebObjectInstances;
+
+/** Allocates a nebServer object
+ *
+ * Structure used for communicating with a Nebulous server.  If endpoint is
+ * NULL then the default value of "http://localhost:80/nebulous" is used.
+ *
+ * @return A nebServer object or NULL on failure.
+ */
+
+nebServer *nebServerAlloc(
+    const char *endpoint                ///< URL of server
+);
+
+/** Deallocates a nebServer object
+ */
+
+void nebServerFree(
+    nebServer *server                   ///< nebServer object
+);
+
+/** Creates a new storage object
+ *
+ * @return A local filename
+ */
+
+char *nebCreate(
+    nebServer *server,                  ///< nebServer object
+    const char *key,                    ///< storage object key (name)
+    const char *volume,                 ///< preferred storage location of initial instance
+    char **URI                          ///< URL of initial instance, can be NULL
+);
+
+/** Creates and opens new storage object
+ *
+ * @return A filehandle on success or a negative value on failure.
+ */
+
+int nebOpenCreate(
+    nebServer *server,                  ///< nebServer object
+    const char *key,                    ///< storage object key (name)
+    const char *volume,                 ///< preferred storage location of initial instance
+    char **URI                          ///< URL of initial instance, can be NULL
+);
+
+/** Adds an instance to a storage object
+ *
+ * @return true on success
+ */
+
+bool nebReplicate(
+    nebServer *server,                  ///< nebServer object
+    const char *key,                    ///< storage object key (name)
+    const char *volume,                 ///< preferred storage location of new instance
+    char **URI                          ///< URL of new instance, can be NULL
+);
+
+/** Removes an instance from a storage object
+ *
+ * This function can not remove the last instance of a storage object.
+ *
+ * @return true on success
+ */
+
+bool nebCull(
+    nebServer *server,                  ///< nebServer object
+    const char *key                     ///< storage object key (name)
+);
+
+/** Trys to acquire a lock on a storage object
+ *
+ * This function times out after a default interval of 10s (compile time
+ * configurable).
+ *
+ * @return true on success
+ */
+bool nebLock(
+    nebServer *server,                  ///< nebServer object
+    const char *key,                    ///< storage object key (name)
+    nebRW flag                          ///< read only or write lock
+);
+
+/** Trys to release a lock on a storage object
+ *
+ * @return true on success
+ */
+bool nebUnlock(
+    nebServer *server,                  ///< nebServer object
+    const char *key,                    ///< storage object key (name)
+    nebRW flag                          ///< read only or write lock
+);
+
+/** Lists all instances of a storage object
+ *
+ * @return A nebObjectInstances object or NULL on failure.
+ */
+
+nebObjectInstances *nebFindInstances(
+    nebServer *server,                  ///< nebServer object
+    const char *key,                    ///< storage object key (name)
+    const char *volume                  ///< restrict search to this storage volume (non-working)
+);
+
+/** Deallocates a nebObjectInstances object
+ */
+
+void nebObjectInstancesFree(
+    nebObjectInstances *locations       ///< nebObjectInstances object to free 
+);
+
+/** Find any instance of a storage object
+ *
+ * @return A string of the files storage path (not a URL) or NULL on failure.
+ */
+
+char *nebFind(
+    nebServer *server,                  ///< nebServer object
+    const char *key                     ///< storage object key (name)
+);
+
+/** Open a storage object for read or write
+ *
+ * @return A filehandle on success or a negative value on failure.
+ */
+
+int nebOpen(
+    nebServer *server,                  ///< nebServer object
+    const char *key,                    ///< storage object key (name)
+    nebRW flag                          ///< read only or write lock
+);
+
+/** Delete a storage object and all of it's instances
+ *
+ * @return true on success
+ */
+
+bool nebDelete(
+    nebServer *server,                  ///< nebServer object
+    const char *key                     ///< storage object key (name)
+);
+
+/** Copy a storage object
+ *
+ * The new storage object will be created with only one instance.
+ *
+ * @return true on success
+ */
+
+bool nebCopy(
+    nebServer *server,                  ///< nebServer object
+    const char *key,                    ///< storage object key (name)
+    const char *newKey                  ///< new storage object key (name)
+);
+
+/** Rename a storage object
+ *
+ * Currently this operation will remove all but one instance of the storage
+ * object and may change it's storage location.
+ */
+bool nebMove(
+    nebServer *server,                  ///< nebServer object
+    const char *key,                    ///< storage object key (name)
+    const char *newKey                  ///< new storage object key (name)
+);
+
+/** Remove a storage object instance
+ *
+ * Removing the last instance of a storage object with destroy the storage
+ * object as well.
+ *
+ * @return true on success
+ */
+
+bool nebDeleteInstance(
+    nebServer *server,                  ///< nebServer object
+    const char *URI                     ///< URL of instance to remove
+);
+
+/** View the properties of a storage object
+ *
+ * @return A nebObjectStat object or NULL on failure.
+ */
+
+nebObjectStat *nebStat(
+    nebServer *server,                  ///< nebServer object
+    const char *key                     /// storage object key (name)
+);
+
+/** Returns the error message from the last nebclient function that failed.
+ *
+ * This function may not be called unless an error has occurred.  The string
+ * return by this function does not need to be freed but the value will change
+ * at the next nebclient function failure.
+ *
+ * @return A string
+ */
+
+char *nebErr(
+    nebServer *server                   ///< nebServer object
+);
+
+/** Free nebclient allocated memory
+ */
+void nebFree(void *ptr);
+
+/// @}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // NEBLIENT_H
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/nebulous.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/nebulous.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/nebulous.h	(revision 22322)
@@ -0,0 +1,668 @@
+/* src/nebulous.h
+   Generated by wsdl2h 1.2.11 from nebulous.wsdl and typemap.dat
+   2008-09-13 01:14:23 GMT
+   Copyright (C) 2001-2008 Robert van Engelen, Genivia Inc. All Rights Reserved.
+   This part of the software is released under one of the following licenses:
+   GPL or Genivia's license for commercial use.
+*/
+
+/* NOTE:
+
+ - Compile this file with soapcpp2 to complete the code generation process.
+ - Use soapcpp2 option -I to specify paths for #import
+   To build with STL, 'stlvector.h' is imported from 'import' dir in package.
+ - Use wsdl2h options -c and -s to generate pure C code or C++ code without STL.
+ - Use 'typemap.dat' to control namespace bindings and type mappings.
+   It is strongly recommended to customize the names of the namespace prefixes
+   generated by wsdl2h. To do so, modify the prefix bindings in the Namespaces
+   section below and add the modified lines to 'typemap.dat' to rerun wsdl2h.
+ - Use Doxygen (www.doxygen.org) to browse this file.
+ - Use wsdl2h option -l to view the software license terms.
+
+   DO NOT include this file directly into your project.
+   Include only the soapcpp2-generated headers and source code files.
+*/
+
+//gsoapopt cw
+
+/******************************************************************************\
+ *                                                                            *
+ * urn:Nebulous/Server/SOAP                                                   *
+ *                                                                            *
+\******************************************************************************/
+
+
+/******************************************************************************\
+ *                                                                            *
+ * Import                                                                     *
+ *                                                                            *
+\******************************************************************************/
+
+
+/******************************************************************************\
+ *                                                                            *
+ * Schema Namespaces                                                          *
+ *                                                                            *
+\******************************************************************************/
+
+
+/* NOTE:
+
+It is strongly recommended to customize the names of the namespace prefixes
+generated by wsdl2h. To do so, modify the prefix bindings below and add the
+modified lines to typemap.dat to rerun wsdl2h:
+
+ns1 = "urn:Nebulous/Server/SOAP"
+
+*/
+
+//gsoap ns1   schema namespace:	urn:Nebulous/Server/SOAP
+//gsoap ns1   schema form:	unqualified
+
+/******************************************************************************\
+ *                                                                            *
+ * Schema Types                                                               *
+ *                                                                            *
+\******************************************************************************/
+
+
+
+/// "urn:Nebulous/Server/SOAP":ArrayOfString is a complexType with complexContent restriction of SOAP-ENC:Array.
+/// SOAP encoded array of xs:string
+struct ArrayOfString
+{
+/// Pointer to array of char*.
+    char*                               *__ptr                         ;
+/// Size of the dynamic array.
+    int                                  __size                        ;
+/// Offset for partially transmitted arrays (uncomment only when required).
+//  int                                  __offset                      ;
+};
+
+/******************************************************************************\
+ *                                                                            *
+ * Services                                                                   *
+ *                                                                            *
+\******************************************************************************/
+
+
+//gsoap ns1  service name:	SOAP 
+//gsoap ns1  service type:	Nebulous_x002fServer_x002fSOAPPort 
+//gsoap ns1  service port:	http://localhost:80/nebulous 
+//gsoap ns1  service namespace:	urn:Nebulous/Server/SOAP 
+//gsoap ns1  service transport:	http://schemas.xmlsoap.org/soap/http 
+
+/** @mainpage NebulousServer Definitions
+
+@section NebulousServer_bindings Bindings
+  - @ref SOAP
+
+*/
+
+/**
+
+@page SOAP Binding "SOAP"
+
+@section SOAP_operations Operations of Binding  "SOAP"
+  - @ref ns1__create_USCOREobject
+  - @ref ns1__rename_USCOREobject
+  - @ref ns1__rename_USCOREobject_
+  - @ref ns1__replicate_USCOREobject
+  - @ref ns1__lock_USCOREobject
+  - @ref ns1__unlock_USCOREobject
+  - @ref ns1__find_USCOREobjects
+  - @ref ns1__find_USCOREinstances
+  - @ref ns1__delete_USCOREinstance
+  - @ref ns1__stat_USCOREobject
+
+@section SOAP_ports Endpoints of Binding  "SOAP"
+  - http://localhost:80/nebulous
+
+Note: use wsdl2h option -N to change the service binding prefix name
+
+*/
+
+/******************************************************************************\
+ *                                                                            *
+ * SOAP                                                                       *
+ *                                                                            *
+\******************************************************************************/
+
+
+/******************************************************************************\
+ *                                                                            *
+ * ns1__create_USCOREobject                                                   *
+ *                                                                            *
+\******************************************************************************/
+
+
+/// Operation "ns1__create_USCOREobject" of service binding "SOAP"
+
+/**
+
+Operation details:
+
+  - SOAP RPC encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+  - SOAP action="urn:Nebulous/Server/SOAP#create_object"
+
+C stub function (defined in soapClient.c[pp] generated by soapcpp2):
+@code
+  int soap_call_ns1__create_USCOREobject(
+    struct soap *soap,
+    NULL, // char *endpoint = NULL selects default endpoint for this operation
+    NULL, // char *action = NULL selects default action for this operation
+    // request parameters:
+    char*                               key,
+    char*                               volume,
+    // response parameters:
+    char*                              *result
+  );
+@endcode
+
+C server function (called from the service dispatcher defined in soapServer.c[pp]):
+@code
+  int ns1__create_USCOREobject(
+    struct soap *soap,
+    // request parameters:
+    char*                               key,
+    char*                               volume,
+    // response parameters:
+    char*                              *result
+  );
+@endcode
+
+*/
+
+//gsoap ns1  service method-style:	create_USCOREobject rpc
+//gsoap ns1  service method-encoding:	create_USCOREobject http://schemas.xmlsoap.org/soap/encoding/
+//gsoap ns1  service method-action:	create_USCOREobject urn:Nebulous/Server/SOAP#create_object
+int ns1__create_USCOREobject(
+    char*                               key,	///< Request parameter
+    char*                               volume,	///< Request parameter
+    char*                              *result	///< Response parameter
+);
+
+/******************************************************************************\
+ *                                                                            *
+ * ns1__rename_USCOREobject                                                   *
+ *                                                                            *
+\******************************************************************************/
+
+
+/// Operation "ns1__rename_USCOREobject" of service binding "SOAP"
+
+/**
+
+Operation details:
+
+  - SOAP RPC encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+  - SOAP action="urn:Nebulous/Server/SOAP#create_object"
+
+C stub function (defined in soapClient.c[pp] generated by soapcpp2):
+@code
+  int soap_call_ns1__rename_USCOREobject(
+    struct soap *soap,
+    NULL, // char *endpoint = NULL selects default endpoint for this operation
+    NULL, // char *action = NULL selects default action for this operation
+    // request parameters:
+    char*                               key,
+    char*                               newkey,
+    // response parameters:
+    char*                              *result
+  );
+@endcode
+
+C server function (called from the service dispatcher defined in soapServer.c[pp]):
+@code
+  int ns1__rename_USCOREobject(
+    struct soap *soap,
+    // request parameters:
+    char*                               key,
+    char*                               newkey,
+    // response parameters:
+    char*                              *result
+  );
+@endcode
+
+*/
+
+//gsoap ns1  service method-style:	rename_USCOREobject rpc
+//gsoap ns1  service method-encoding:	rename_USCOREobject http://schemas.xmlsoap.org/soap/encoding/
+//gsoap ns1  service method-action:	rename_USCOREobject urn:Nebulous/Server/SOAP#create_object
+int ns1__rename_USCOREobject(
+    char*                               key,	///< Request parameter
+    char*                               newkey,	///< Request parameter
+    char*                              *result	///< Response parameter
+);
+
+/******************************************************************************\
+ *                                                                            *
+ * ns1__rename_USCOREobject_                                                  *
+ *                                                                            *
+\******************************************************************************/
+
+
+/// Operation "ns1__rename_USCOREobject_" of service binding "SOAP"
+
+/**
+
+Operation details:
+
+  - SOAP RPC encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+  - SOAP action="urn:Nebulous/Server/SOAP#rename_object"
+
+C stub function (defined in soapClient.c[pp] generated by soapcpp2):
+@code
+  int soap_call_ns1__rename_USCOREobject_(
+    struct soap *soap,
+    NULL, // char *endpoint = NULL selects default endpoint for this operation
+    NULL, // char *action = NULL selects default action for this operation
+    // request parameters:
+    char*                               key,
+    char*                               newkey,
+    // response parameters:
+    char*                              *result
+  );
+@endcode
+
+C server function (called from the service dispatcher defined in soapServer.c[pp]):
+@code
+  int ns1__rename_USCOREobject_(
+    struct soap *soap,
+    // request parameters:
+    char*                               key,
+    char*                               newkey,
+    // response parameters:
+    char*                              *result
+  );
+@endcode
+
+*/
+
+//gsoap ns1  service method-style:	rename_USCOREobject_ rpc
+//gsoap ns1  service method-encoding:	rename_USCOREobject_ http://schemas.xmlsoap.org/soap/encoding/
+//gsoap ns1  service method-action:	rename_USCOREobject_ urn:Nebulous/Server/SOAP#rename_object
+int ns1__rename_USCOREobject_(
+    char*                               key,	///< Request parameter
+    char*                               newkey,	///< Request parameter
+    char*                              *result	///< Response parameter
+);
+
+/******************************************************************************\
+ *                                                                            *
+ * ns1__replicate_USCOREobject                                                *
+ *                                                                            *
+\******************************************************************************/
+
+
+/// Operation "ns1__replicate_USCOREobject" of service binding "SOAP"
+
+/**
+
+Operation details:
+
+  - SOAP RPC encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+  - SOAP action="urn:Nebulous/Server/SOAP#replicate_object"
+
+C stub function (defined in soapClient.c[pp] generated by soapcpp2):
+@code
+  int soap_call_ns1__replicate_USCOREobject(
+    struct soap *soap,
+    NULL, // char *endpoint = NULL selects default endpoint for this operation
+    NULL, // char *action = NULL selects default action for this operation
+    // request parameters:
+    char*                               key,
+    char*                               volume,
+    // response parameters:
+    char*                              *result
+  );
+@endcode
+
+C server function (called from the service dispatcher defined in soapServer.c[pp]):
+@code
+  int ns1__replicate_USCOREobject(
+    struct soap *soap,
+    // request parameters:
+    char*                               key,
+    char*                               volume,
+    // response parameters:
+    char*                              *result
+  );
+@endcode
+
+*/
+
+//gsoap ns1  service method-style:	replicate_USCOREobject rpc
+//gsoap ns1  service method-encoding:	replicate_USCOREobject http://schemas.xmlsoap.org/soap/encoding/
+//gsoap ns1  service method-action:	replicate_USCOREobject urn:Nebulous/Server/SOAP#replicate_object
+int ns1__replicate_USCOREobject(
+    char*                               key,	///< Request parameter
+    char*                               volume,	///< Request parameter
+    char*                              *result	///< Response parameter
+);
+
+/******************************************************************************\
+ *                                                                            *
+ * ns1__lock_USCOREobject                                                     *
+ *                                                                            *
+\******************************************************************************/
+
+
+/// Operation "ns1__lock_USCOREobject" of service binding "SOAP"
+
+/**
+
+Operation details:
+
+  - SOAP RPC encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+  - SOAP action="urn:Nebulous/Server/SOAP#lock_object"
+
+C stub function (defined in soapClient.c[pp] generated by soapcpp2):
+@code
+  int soap_call_ns1__lock_USCOREobject(
+    struct soap *soap,
+    NULL, // char *endpoint = NULL selects default endpoint for this operation
+    NULL, // char *action = NULL selects default action for this operation
+    // request parameters:
+    char*                               key,
+    char*                               type,
+    // response parameters:
+    int                                *result
+  );
+@endcode
+
+C server function (called from the service dispatcher defined in soapServer.c[pp]):
+@code
+  int ns1__lock_USCOREobject(
+    struct soap *soap,
+    // request parameters:
+    char*                               key,
+    char*                               type,
+    // response parameters:
+    int                                *result
+  );
+@endcode
+
+*/
+
+//gsoap ns1  service method-style:	lock_USCOREobject rpc
+//gsoap ns1  service method-encoding:	lock_USCOREobject http://schemas.xmlsoap.org/soap/encoding/
+//gsoap ns1  service method-action:	lock_USCOREobject urn:Nebulous/Server/SOAP#lock_object
+int ns1__lock_USCOREobject(
+    char*                               key,	///< Request parameter
+    char*                               type,	///< Request parameter
+    int                                *result	///< Response parameter
+);
+
+/******************************************************************************\
+ *                                                                            *
+ * ns1__unlock_USCOREobject                                                   *
+ *                                                                            *
+\******************************************************************************/
+
+
+/// Operation "ns1__unlock_USCOREobject" of service binding "SOAP"
+
+/**
+
+Operation details:
+
+  - SOAP RPC encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+  - SOAP action="urn:Nebulous/Server/SOAP#unlock_object"
+
+C stub function (defined in soapClient.c[pp] generated by soapcpp2):
+@code
+  int soap_call_ns1__unlock_USCOREobject(
+    struct soap *soap,
+    NULL, // char *endpoint = NULL selects default endpoint for this operation
+    NULL, // char *action = NULL selects default action for this operation
+    // request parameters:
+    char*                               key,
+    char*                               type,
+    // response parameters:
+    int                                *result
+  );
+@endcode
+
+C server function (called from the service dispatcher defined in soapServer.c[pp]):
+@code
+  int ns1__unlock_USCOREobject(
+    struct soap *soap,
+    // request parameters:
+    char*                               key,
+    char*                               type,
+    // response parameters:
+    int                                *result
+  );
+@endcode
+
+*/
+
+//gsoap ns1  service method-style:	unlock_USCOREobject rpc
+//gsoap ns1  service method-encoding:	unlock_USCOREobject http://schemas.xmlsoap.org/soap/encoding/
+//gsoap ns1  service method-action:	unlock_USCOREobject urn:Nebulous/Server/SOAP#unlock_object
+int ns1__unlock_USCOREobject(
+    char*                               key,	///< Request parameter
+    char*                               type,	///< Request parameter
+    int                                *result	///< Response parameter
+);
+
+/******************************************************************************\
+ *                                                                            *
+ * ns1__find_USCOREobjects                                                    *
+ *                                                                            *
+\******************************************************************************/
+
+/// Operation response struct "ns1__find_USCOREobjectsResponse" of service binding "SOAP" operation "ns1__find_USCOREobjects"
+struct ns1__find_USCOREobjectsResponse
+{
+    struct ArrayOfString*               result;
+};
+
+/// Operation "ns1__find_USCOREobjects" of service binding "SOAP"
+
+/**
+
+Operation details:
+
+  - SOAP RPC encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+  - SOAP action="urn:Nebulous/Server/SOAP#find_objects"
+
+C stub function (defined in soapClient.c[pp] generated by soapcpp2):
+@code
+  int soap_call_ns1__find_USCOREobjects(
+    struct soap *soap,
+    NULL, // char *endpoint = NULL selects default endpoint for this operation
+    NULL, // char *action = NULL selects default action for this operation
+    // request parameters:
+    char*                               pattern,
+    // response parameters:
+    struct ns1__find_USCOREobjectsResponse*
+  );
+@endcode
+
+C server function (called from the service dispatcher defined in soapServer.c[pp]):
+@code
+  int ns1__find_USCOREobjects(
+    struct soap *soap,
+    // request parameters:
+    char*                               pattern,
+    // response parameters:
+    struct ns1__find_USCOREobjectsResponse*
+  );
+@endcode
+
+*/
+
+//gsoap ns1  service method-style:	find_USCOREobjects rpc
+//gsoap ns1  service method-encoding:	find_USCOREobjects http://schemas.xmlsoap.org/soap/encoding/
+//gsoap ns1  service method-action:	find_USCOREobjects urn:Nebulous/Server/SOAP#find_objects
+int ns1__find_USCOREobjects(
+    char*                               pattern,	///< Request parameter
+    struct ns1__find_USCOREobjectsResponse*	///< Response struct parameter
+);
+
+/******************************************************************************\
+ *                                                                            *
+ * ns1__find_USCOREinstances                                                  *
+ *                                                                            *
+\******************************************************************************/
+
+/// Operation response struct "ns1__find_USCOREinstancesResponse" of service binding "SOAP" operation "ns1__find_USCOREinstances"
+struct ns1__find_USCOREinstancesResponse
+{
+    struct ArrayOfString*               result;
+};
+
+/// Operation "ns1__find_USCOREinstances" of service binding "SOAP"
+
+/**
+
+Operation details:
+
+  - SOAP RPC encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+  - SOAP action="urn:Nebulous/Server/SOAP#find_instances"
+
+C stub function (defined in soapClient.c[pp] generated by soapcpp2):
+@code
+  int soap_call_ns1__find_USCOREinstances(
+    struct soap *soap,
+    NULL, // char *endpoint = NULL selects default endpoint for this operation
+    NULL, // char *action = NULL selects default action for this operation
+    // request parameters:
+    char*                               key,
+    char*                               volume,
+    // response parameters:
+    struct ns1__find_USCOREinstancesResponse*
+  );
+@endcode
+
+C server function (called from the service dispatcher defined in soapServer.c[pp]):
+@code
+  int ns1__find_USCOREinstances(
+    struct soap *soap,
+    // request parameters:
+    char*                               key,
+    char*                               volume,
+    // response parameters:
+    struct ns1__find_USCOREinstancesResponse*
+  );
+@endcode
+
+*/
+
+//gsoap ns1  service method-style:	find_USCOREinstances rpc
+//gsoap ns1  service method-encoding:	find_USCOREinstances http://schemas.xmlsoap.org/soap/encoding/
+//gsoap ns1  service method-action:	find_USCOREinstances urn:Nebulous/Server/SOAP#find_instances
+int ns1__find_USCOREinstances(
+    char*                               key,	///< Request parameter
+    char*                               volume,	///< Request parameter
+    struct ns1__find_USCOREinstancesResponse*	///< Response struct parameter
+);
+
+/******************************************************************************\
+ *                                                                            *
+ * ns1__delete_USCOREinstance                                                 *
+ *                                                                            *
+\******************************************************************************/
+
+
+/// Operation "ns1__delete_USCOREinstance" of service binding "SOAP"
+
+/**
+
+Operation details:
+
+  - SOAP RPC encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+  - SOAP action="urn:Nebulous/Server/SOAP#delete_instance"
+
+C stub function (defined in soapClient.c[pp] generated by soapcpp2):
+@code
+  int soap_call_ns1__delete_USCOREinstance(
+    struct soap *soap,
+    NULL, // char *endpoint = NULL selects default endpoint for this operation
+    NULL, // char *action = NULL selects default action for this operation
+    // request parameters:
+    char*                               uri,
+    // response parameters:
+    int                                *result
+  );
+@endcode
+
+C server function (called from the service dispatcher defined in soapServer.c[pp]):
+@code
+  int ns1__delete_USCOREinstance(
+    struct soap *soap,
+    // request parameters:
+    char*                               uri,
+    // response parameters:
+    int                                *result
+  );
+@endcode
+
+*/
+
+//gsoap ns1  service method-style:	delete_USCOREinstance rpc
+//gsoap ns1  service method-encoding:	delete_USCOREinstance http://schemas.xmlsoap.org/soap/encoding/
+//gsoap ns1  service method-action:	delete_USCOREinstance urn:Nebulous/Server/SOAP#delete_instance
+int ns1__delete_USCOREinstance(
+    char*                               uri,	///< Request parameter
+    int                                *result	///< Response parameter
+);
+
+/******************************************************************************\
+ *                                                                            *
+ * ns1__stat_USCOREobject                                                     *
+ *                                                                            *
+\******************************************************************************/
+
+/// Operation response struct "ns1__stat_USCOREobjectResponse" of service binding "SOAP" operation "ns1__stat_USCOREobject"
+struct ns1__stat_USCOREobjectResponse
+{
+    struct ArrayOfString*               result;
+};
+
+/// Operation "ns1__stat_USCOREobject" of service binding "SOAP"
+
+/**
+
+Operation details:
+
+  - SOAP RPC encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+  - SOAP action="urn:Nebulous/Server/SOAP#stat_object"
+
+C stub function (defined in soapClient.c[pp] generated by soapcpp2):
+@code
+  int soap_call_ns1__stat_USCOREobject(
+    struct soap *soap,
+    NULL, // char *endpoint = NULL selects default endpoint for this operation
+    NULL, // char *action = NULL selects default action for this operation
+    // request parameters:
+    char*                               key,
+    // response parameters:
+    struct ns1__stat_USCOREobjectResponse*
+  );
+@endcode
+
+C server function (called from the service dispatcher defined in soapServer.c[pp]):
+@code
+  int ns1__stat_USCOREobject(
+    struct soap *soap,
+    // request parameters:
+    char*                               key,
+    // response parameters:
+    struct ns1__stat_USCOREobjectResponse*
+  );
+@endcode
+
+*/
+
+//gsoap ns1  service method-style:	stat_USCOREobject rpc
+//gsoap ns1  service method-encoding:	stat_USCOREobject http://schemas.xmlsoap.org/soap/encoding/
+//gsoap ns1  service method-action:	stat_USCOREobject urn:Nebulous/Server/SOAP#stat_object
+int ns1__stat_USCOREobject(
+    char*                               key,	///< Request parameter
+    struct ns1__stat_USCOREobjectResponse*	///< Response struct parameter
+);
+
+/* End of src/nebulous.h */
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapC.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapC.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapC.c	(revision 22322)
@@ -0,0 +1,3296 @@
+/* soapC.c
+   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
+   This part of the software is released under one of the following licenses:
+   GPL, the gSOAP public license, or Genivia's license for commercial use.
+*/
+
+#include "soapH.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SOAP_SOURCE_STAMP("@(#) soapC.c ver 2.7.11 2008-09-13 01:14:40 GMT")
+
+
+#ifndef WITH_NOGLOBAL
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serializeheader(struct soap *soap)
+{
+	if (soap->header)
+		soap_serialize_SOAP_ENV__Header(soap, soap->header);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_putheader(struct soap *soap)
+{
+	if (soap->header)
+	{	soap->part = SOAP_IN_HEADER;
+		if (soap_out_SOAP_ENV__Header(soap, "SOAP-ENV:Header", 0, soap->header, NULL))
+			return soap->error;
+		soap->part = SOAP_END_HEADER;
+	}
+	return SOAP_OK;
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_getheader(struct soap *soap)
+{
+	soap->part = SOAP_IN_HEADER;
+	soap->header = soap_in_SOAP_ENV__Header(soap, "SOAP-ENV:Header", NULL, NULL);
+	soap->part = SOAP_END_HEADER;
+	return soap->header == NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_header(struct soap *soap)
+{
+	if (!soap->header)
+	{	soap->header = (struct SOAP_ENV__Header*)soap_malloc(soap, sizeof(struct SOAP_ENV__Header));
+		soap_default_SOAP_ENV__Header(soap, soap->header);
+	}
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_fault(struct soap *soap)
+{
+	if (!soap->fault)
+	{	soap->fault = (struct SOAP_ENV__Fault*)soap_malloc(soap, sizeof(struct SOAP_ENV__Fault));
+		if (!soap->fault)
+			return;
+		soap_default_SOAP_ENV__Fault(soap, soap->fault);
+	}
+	if (soap->version == 2 && !soap->fault->SOAP_ENV__Code)
+	{	soap->fault->SOAP_ENV__Code = (struct SOAP_ENV__Code*)soap_malloc(soap, sizeof(struct SOAP_ENV__Code));
+		soap_default_SOAP_ENV__Code(soap, soap->fault->SOAP_ENV__Code);
+	}
+	if (soap->version == 2 && !soap->fault->SOAP_ENV__Reason)
+	{	soap->fault->SOAP_ENV__Reason = (struct SOAP_ENV__Reason*)soap_malloc(soap, sizeof(struct SOAP_ENV__Reason));
+		soap_default_SOAP_ENV__Reason(soap, soap->fault->SOAP_ENV__Reason);
+	}
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serializefault(struct soap *soap)
+{
+	if (soap->fault)
+		soap_serialize_SOAP_ENV__Fault(soap, soap->fault);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_putfault(struct soap *soap)
+{
+	if (soap->fault)
+		return soap_put_SOAP_ENV__Fault(soap, soap->fault, "SOAP-ENV:Fault", NULL);
+	return SOAP_OK;
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_getfault(struct soap *soap)
+{
+	return (soap->fault = soap_get_SOAP_ENV__Fault(soap, NULL, "SOAP-ENV:Fault", NULL)) == NULL;
+}
+
+SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultcode(struct soap *soap)
+{
+	soap_fault(soap);
+	if (soap->version == 2)
+		return (const char**)&soap->fault->SOAP_ENV__Code->SOAP_ENV__Value;
+	return (const char**)&soap->fault->faultcode;
+}
+
+SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultsubcode(struct soap *soap)
+{
+	soap_fault(soap);
+	if (soap->version == 2)
+	{	if (!soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode)
+		{	soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode = (struct SOAP_ENV__Code*)soap_malloc(soap, sizeof(struct SOAP_ENV__Code));
+			soap_default_SOAP_ENV__Code(soap, soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode);
+		}
+		return (const char**)&soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode->SOAP_ENV__Value;
+	}
+	return (const char**)&soap->fault->faultcode;
+}
+
+SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultstring(struct soap *soap)
+{
+	soap_fault(soap);
+	if (soap->version == 2)
+		return (const char**)&soap->fault->SOAP_ENV__Reason->SOAP_ENV__Text;
+	return (const char**)&soap->fault->faultstring;
+}
+
+SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultdetail(struct soap *soap)
+{
+	soap_fault(soap);
+	if (soap->version == 1)
+	{	if (!soap->fault->detail)
+		{	soap->fault->detail = (struct SOAP_ENV__Detail*)soap_malloc(soap, sizeof(struct SOAP_ENV__Detail));
+			soap_default_SOAP_ENV__Detail(soap, soap->fault->detail);
+		}
+		return (const char**)&soap->fault->detail->__any;
+	}
+	if (!soap->fault->SOAP_ENV__Detail)
+	{	soap->fault->SOAP_ENV__Detail = (struct SOAP_ENV__Detail*)soap_malloc(soap, sizeof(struct SOAP_ENV__Detail));
+		soap_default_SOAP_ENV__Detail(soap, soap->fault->SOAP_ENV__Detail);
+	}
+	return (const char**)&soap->fault->SOAP_ENV__Detail->__any;
+}
+
+#endif
+
+#ifndef WITH_NOIDREF
+SOAP_FMAC3 int SOAP_FMAC4 soap_getindependent(struct soap *soap)
+{
+	int t;
+	for (;;)
+		if (!soap_getelement(soap, &t))
+			if (soap->error || soap_ignore_element(soap))
+				break;
+	if (soap->error == SOAP_NO_TAG || soap->error == SOAP_EOF)
+		soap->error = SOAP_OK;
+	return soap->error;
+}
+#endif
+
+#ifndef WITH_NOIDREF
+SOAP_FMAC3 void * SOAP_FMAC4 soap_getelement(struct soap *soap, int *type)
+{
+	if (soap_peek_element(soap))
+		return NULL;
+	if (!*soap->id || !(*type = soap_lookup_type(soap, soap->id)))
+		*type = soap_lookup_type(soap, soap->href);
+	switch (*type)
+	{
+	case SOAP_TYPE_byte:
+		return soap_in_byte(soap, NULL, NULL, "xsd:byte");
+	case SOAP_TYPE_int:
+		return soap_in_int(soap, NULL, NULL, "xsd:int");
+	case SOAP_TYPE_ns1__stat_USCOREobject:
+		return soap_in_ns1__stat_USCOREobject(soap, NULL, NULL, "ns1:stat_object");
+	case SOAP_TYPE_ns1__stat_USCOREobjectResponse:
+		return soap_in_ns1__stat_USCOREobjectResponse(soap, NULL, NULL, "ns1:stat_objectResponse");
+	case SOAP_TYPE_ns1__delete_USCOREinstance:
+		return soap_in_ns1__delete_USCOREinstance(soap, NULL, NULL, "ns1:delete_instance");
+	case SOAP_TYPE_ns1__delete_USCOREinstanceResponse:
+		return soap_in_ns1__delete_USCOREinstanceResponse(soap, NULL, NULL, "ns1:delete_instanceResponse");
+	case SOAP_TYPE_ns1__find_USCOREinstances:
+		return soap_in_ns1__find_USCOREinstances(soap, NULL, NULL, "ns1:find_instances");
+	case SOAP_TYPE_ns1__find_USCOREinstancesResponse:
+		return soap_in_ns1__find_USCOREinstancesResponse(soap, NULL, NULL, "ns1:find_instancesResponse");
+	case SOAP_TYPE_ns1__find_USCOREobjects:
+		return soap_in_ns1__find_USCOREobjects(soap, NULL, NULL, "ns1:find_objects");
+	case SOAP_TYPE_ns1__find_USCOREobjectsResponse:
+		return soap_in_ns1__find_USCOREobjectsResponse(soap, NULL, NULL, "ns1:find_objectsResponse");
+	case SOAP_TYPE_ns1__unlock_USCOREobject:
+		return soap_in_ns1__unlock_USCOREobject(soap, NULL, NULL, "ns1:unlock_object");
+	case SOAP_TYPE_ns1__unlock_USCOREobjectResponse:
+		return soap_in_ns1__unlock_USCOREobjectResponse(soap, NULL, NULL, "ns1:unlock_objectResponse");
+	case SOAP_TYPE_ns1__lock_USCOREobject:
+		return soap_in_ns1__lock_USCOREobject(soap, NULL, NULL, "ns1:lock_object");
+	case SOAP_TYPE_ns1__lock_USCOREobjectResponse:
+		return soap_in_ns1__lock_USCOREobjectResponse(soap, NULL, NULL, "ns1:lock_objectResponse");
+	case SOAP_TYPE_ns1__replicate_USCOREobject:
+		return soap_in_ns1__replicate_USCOREobject(soap, NULL, NULL, "ns1:replicate_object");
+	case SOAP_TYPE_ns1__replicate_USCOREobjectResponse:
+		return soap_in_ns1__replicate_USCOREobjectResponse(soap, NULL, NULL, "ns1:replicate_objectResponse");
+	case SOAP_TYPE_ns1__rename_USCOREobject_:
+		return soap_in_ns1__rename_USCOREobject_(soap, NULL, NULL, "ns1:rename_object");
+	case SOAP_TYPE_ns1__rename_USCOREobject_Response:
+		return soap_in_ns1__rename_USCOREobject_Response(soap, NULL, NULL, "ns1:rename_object-Response");
+	case SOAP_TYPE_ns1__rename_USCOREobject:
+		return soap_in_ns1__rename_USCOREobject(soap, NULL, NULL, "ns1:rename_object");
+	case SOAP_TYPE_ns1__rename_USCOREobjectResponse:
+		return soap_in_ns1__rename_USCOREobjectResponse(soap, NULL, NULL, "ns1:rename_objectResponse");
+	case SOAP_TYPE_ns1__create_USCOREobject:
+		return soap_in_ns1__create_USCOREobject(soap, NULL, NULL, "ns1:create_object");
+	case SOAP_TYPE_ns1__create_USCOREobjectResponse:
+		return soap_in_ns1__create_USCOREobjectResponse(soap, NULL, NULL, "ns1:create_objectResponse");
+	case SOAP_TYPE_ArrayOfString:
+		return soap_in_ArrayOfString(soap, NULL, NULL, "xsd:string");
+	case SOAP_TYPE_PointerTons1__stat_USCOREobjectResponse:
+		return soap_in_PointerTons1__stat_USCOREobjectResponse(soap, NULL, NULL, "ns1:stat_objectResponse");
+	case SOAP_TYPE_PointerTons1__find_USCOREinstancesResponse:
+		return soap_in_PointerTons1__find_USCOREinstancesResponse(soap, NULL, NULL, "ns1:find_instancesResponse");
+	case SOAP_TYPE_PointerTons1__find_USCOREobjectsResponse:
+		return soap_in_PointerTons1__find_USCOREobjectsResponse(soap, NULL, NULL, "ns1:find_objectsResponse");
+	case SOAP_TYPE_PointerToArrayOfString:
+		return soap_in_PointerToArrayOfString(soap, NULL, NULL, "xsd:string");
+	case SOAP_TYPE_PointerToint:
+		return soap_in_PointerToint(soap, NULL, NULL, "xsd:int");
+	case SOAP_TYPE_PointerTostring:
+		return soap_in_PointerTostring(soap, NULL, NULL, "xsd:string");
+	case SOAP_TYPE_string:
+	{	char **s;
+		s = soap_in_string(soap, NULL, NULL, "xsd:string");
+		return s ? *s : NULL;
+	}
+	default:
+	{	const char *t = soap->type;
+		if (!*t)
+			t = soap->tag;
+		if (!soap_match_tag(soap, t, "xsd:byte"))
+		{	*type = SOAP_TYPE_byte;
+			return soap_in_byte(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "xsd:int"))
+		{	*type = SOAP_TYPE_int;
+			return soap_in_int(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:stat_object"))
+		{	*type = SOAP_TYPE_ns1__stat_USCOREobject;
+			return soap_in_ns1__stat_USCOREobject(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:stat_objectResponse"))
+		{	*type = SOAP_TYPE_ns1__stat_USCOREobjectResponse;
+			return soap_in_ns1__stat_USCOREobjectResponse(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:delete_instance"))
+		{	*type = SOAP_TYPE_ns1__delete_USCOREinstance;
+			return soap_in_ns1__delete_USCOREinstance(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:delete_instanceResponse"))
+		{	*type = SOAP_TYPE_ns1__delete_USCOREinstanceResponse;
+			return soap_in_ns1__delete_USCOREinstanceResponse(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:find_instances"))
+		{	*type = SOAP_TYPE_ns1__find_USCOREinstances;
+			return soap_in_ns1__find_USCOREinstances(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:find_instancesResponse"))
+		{	*type = SOAP_TYPE_ns1__find_USCOREinstancesResponse;
+			return soap_in_ns1__find_USCOREinstancesResponse(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:find_objects"))
+		{	*type = SOAP_TYPE_ns1__find_USCOREobjects;
+			return soap_in_ns1__find_USCOREobjects(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:find_objectsResponse"))
+		{	*type = SOAP_TYPE_ns1__find_USCOREobjectsResponse;
+			return soap_in_ns1__find_USCOREobjectsResponse(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:unlock_object"))
+		{	*type = SOAP_TYPE_ns1__unlock_USCOREobject;
+			return soap_in_ns1__unlock_USCOREobject(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:unlock_objectResponse"))
+		{	*type = SOAP_TYPE_ns1__unlock_USCOREobjectResponse;
+			return soap_in_ns1__unlock_USCOREobjectResponse(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:lock_object"))
+		{	*type = SOAP_TYPE_ns1__lock_USCOREobject;
+			return soap_in_ns1__lock_USCOREobject(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:lock_objectResponse"))
+		{	*type = SOAP_TYPE_ns1__lock_USCOREobjectResponse;
+			return soap_in_ns1__lock_USCOREobjectResponse(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:replicate_object"))
+		{	*type = SOAP_TYPE_ns1__replicate_USCOREobject;
+			return soap_in_ns1__replicate_USCOREobject(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:replicate_objectResponse"))
+		{	*type = SOAP_TYPE_ns1__replicate_USCOREobjectResponse;
+			return soap_in_ns1__replicate_USCOREobjectResponse(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:rename_object"))
+		{	*type = SOAP_TYPE_ns1__rename_USCOREobject_;
+			return soap_in_ns1__rename_USCOREobject_(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:rename_object-Response"))
+		{	*type = SOAP_TYPE_ns1__rename_USCOREobject_Response;
+			return soap_in_ns1__rename_USCOREobject_Response(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:rename_object"))
+		{	*type = SOAP_TYPE_ns1__rename_USCOREobject;
+			return soap_in_ns1__rename_USCOREobject(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:rename_objectResponse"))
+		{	*type = SOAP_TYPE_ns1__rename_USCOREobjectResponse;
+			return soap_in_ns1__rename_USCOREobjectResponse(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:create_object"))
+		{	*type = SOAP_TYPE_ns1__create_USCOREobject;
+			return soap_in_ns1__create_USCOREobject(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "ns1:create_objectResponse"))
+		{	*type = SOAP_TYPE_ns1__create_USCOREobjectResponse;
+			return soap_in_ns1__create_USCOREobjectResponse(soap, NULL, NULL, NULL);
+		}
+		if (*soap->arrayType && !soap_match_array(soap, "xsd:string"))
+		{	*type = SOAP_TYPE_ArrayOfString;
+			return soap_in_ArrayOfString(soap, NULL, NULL, NULL);
+		}
+		if (!soap_match_tag(soap, t, "xsd:string"))
+		{	char **s;
+			*type = SOAP_TYPE_string;
+			s = soap_in_string(soap, NULL, NULL, NULL);
+			return s ? *s : NULL;
+		}
+		t = soap->tag;
+		if (!soap_match_tag(soap, t, "xsd:QName"))
+		{	char **s;
+			*type = SOAP_TYPE__QName;
+			s = soap_in__QName(soap, NULL, NULL, NULL);
+			return s ? *s : NULL;
+		}
+	}
+	}
+	soap->error = SOAP_TAG_MISMATCH;
+	return NULL;
+}
+#endif
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_ignore_element(struct soap *soap)
+{
+	if (!soap_peek_element(soap))
+	{	int t;
+		DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Unexpected element '%s' in input (level=%u, %d)\n", soap->tag, soap->level, soap->body));
+		if (soap->mustUnderstand && !soap->other)
+			return soap->error = SOAP_MUSTUNDERSTAND;
+		if (((soap->mode & SOAP_XML_STRICT) && soap->part != SOAP_IN_HEADER) || !soap_match_tag(soap, soap->tag, "SOAP-ENV:"))
+		{	DBGLOG(TEST, SOAP_MESSAGE(fdebug, "REJECTING element '%s'\n", soap->tag));
+			return soap->error = SOAP_TAG_MISMATCH;
+		}
+		if (!*soap->id || !soap_getelement(soap, &t))
+		{	soap->peeked = 0;
+			if (soap->fignore)
+				soap->error = soap->fignore(soap, soap->tag);
+			else
+				soap->error = SOAP_OK;
+			DBGLOG(TEST, if (!soap->error) SOAP_MESSAGE(fdebug, "IGNORING element '%s'\n", soap->tag));
+			if (!soap->error && soap->body)
+			{	soap->level++;
+				while (!soap_ignore_element(soap))
+					;
+				if (soap->error == SOAP_NO_TAG)
+					soap->error = soap_element_end_in(soap, NULL);
+			}
+		}
+	}
+	return soap->error;
+}
+
+#ifndef WITH_NOIDREF
+SOAP_FMAC3 int SOAP_FMAC4 soap_putindependent(struct soap *soap)
+{
+	int i;
+	struct soap_plist *pp;
+	if (soap->version == 1 && soap->encodingStyle && !(soap->mode & (SOAP_XML_TREE | SOAP_XML_GRAPH)))
+		for (i = 0; i < SOAP_PTRHASH; i++)
+			for (pp = soap->pht[i]; pp; pp = pp->next)
+				if (pp->mark1 == 2 || pp->mark2 == 2)
+					if (soap_putelement(soap, pp->ptr, "id", pp->id, pp->type))
+						return soap->error;
+	return SOAP_OK;
+}
+#endif
+
+#ifndef WITH_NOIDREF
+SOAP_FMAC3 int SOAP_FMAC4 soap_putelement(struct soap *soap, const void *ptr, const char *tag, int id, int type)
+{
+	switch (type)
+	{
+	case SOAP_TYPE_byte:
+		return soap_out_byte(soap, tag, id, (const char *)ptr, "xsd:byte");
+	case SOAP_TYPE_int:
+		return soap_out_int(soap, tag, id, (const int *)ptr, "xsd:int");
+	case SOAP_TYPE_ns1__stat_USCOREobject:
+		return soap_out_ns1__stat_USCOREobject(soap, tag, id, (const struct ns1__stat_USCOREobject *)ptr, "ns1:stat_object");
+	case SOAP_TYPE_ns1__stat_USCOREobjectResponse:
+		return soap_out_ns1__stat_USCOREobjectResponse(soap, tag, id, (const struct ns1__stat_USCOREobjectResponse *)ptr, "ns1:stat_objectResponse");
+	case SOAP_TYPE_ns1__delete_USCOREinstance:
+		return soap_out_ns1__delete_USCOREinstance(soap, tag, id, (const struct ns1__delete_USCOREinstance *)ptr, "ns1:delete_instance");
+	case SOAP_TYPE_ns1__delete_USCOREinstanceResponse:
+		return soap_out_ns1__delete_USCOREinstanceResponse(soap, tag, id, (const struct ns1__delete_USCOREinstanceResponse *)ptr, "ns1:delete_instanceResponse");
+	case SOAP_TYPE_ns1__find_USCOREinstances:
+		return soap_out_ns1__find_USCOREinstances(soap, tag, id, (const struct ns1__find_USCOREinstances *)ptr, "ns1:find_instances");
+	case SOAP_TYPE_ns1__find_USCOREinstancesResponse:
+		return soap_out_ns1__find_USCOREinstancesResponse(soap, tag, id, (const struct ns1__find_USCOREinstancesResponse *)ptr, "ns1:find_instancesResponse");
+	case SOAP_TYPE_ns1__find_USCOREobjects:
+		return soap_out_ns1__find_USCOREobjects(soap, tag, id, (const struct ns1__find_USCOREobjects *)ptr, "ns1:find_objects");
+	case SOAP_TYPE_ns1__find_USCOREobjectsResponse:
+		return soap_out_ns1__find_USCOREobjectsResponse(soap, tag, id, (const struct ns1__find_USCOREobjectsResponse *)ptr, "ns1:find_objectsResponse");
+	case SOAP_TYPE_ns1__unlock_USCOREobject:
+		return soap_out_ns1__unlock_USCOREobject(soap, tag, id, (const struct ns1__unlock_USCOREobject *)ptr, "ns1:unlock_object");
+	case SOAP_TYPE_ns1__unlock_USCOREobjectResponse:
+		return soap_out_ns1__unlock_USCOREobjectResponse(soap, tag, id, (const struct ns1__unlock_USCOREobjectResponse *)ptr, "ns1:unlock_objectResponse");
+	case SOAP_TYPE_ns1__lock_USCOREobject:
+		return soap_out_ns1__lock_USCOREobject(soap, tag, id, (const struct ns1__lock_USCOREobject *)ptr, "ns1:lock_object");
+	case SOAP_TYPE_ns1__lock_USCOREobjectResponse:
+		return soap_out_ns1__lock_USCOREobjectResponse(soap, tag, id, (const struct ns1__lock_USCOREobjectResponse *)ptr, "ns1:lock_objectResponse");
+	case SOAP_TYPE_ns1__replicate_USCOREobject:
+		return soap_out_ns1__replicate_USCOREobject(soap, tag, id, (const struct ns1__replicate_USCOREobject *)ptr, "ns1:replicate_object");
+	case SOAP_TYPE_ns1__replicate_USCOREobjectResponse:
+		return soap_out_ns1__replicate_USCOREobjectResponse(soap, tag, id, (const struct ns1__replicate_USCOREobjectResponse *)ptr, "ns1:replicate_objectResponse");
+	case SOAP_TYPE_ns1__rename_USCOREobject_:
+		return soap_out_ns1__rename_USCOREobject_(soap, tag, id, (const struct ns1__rename_USCOREobject_ *)ptr, "ns1:rename_object");
+	case SOAP_TYPE_ns1__rename_USCOREobject_Response:
+		return soap_out_ns1__rename_USCOREobject_Response(soap, tag, id, (const struct ns1__rename_USCOREobject_Response *)ptr, "ns1:rename_object-Response");
+	case SOAP_TYPE_ns1__rename_USCOREobject:
+		return soap_out_ns1__rename_USCOREobject(soap, tag, id, (const struct ns1__rename_USCOREobject *)ptr, "ns1:rename_object");
+	case SOAP_TYPE_ns1__rename_USCOREobjectResponse:
+		return soap_out_ns1__rename_USCOREobjectResponse(soap, tag, id, (const struct ns1__rename_USCOREobjectResponse *)ptr, "ns1:rename_objectResponse");
+	case SOAP_TYPE_ns1__create_USCOREobject:
+		return soap_out_ns1__create_USCOREobject(soap, tag, id, (const struct ns1__create_USCOREobject *)ptr, "ns1:create_object");
+	case SOAP_TYPE_ns1__create_USCOREobjectResponse:
+		return soap_out_ns1__create_USCOREobjectResponse(soap, tag, id, (const struct ns1__create_USCOREobjectResponse *)ptr, "ns1:create_objectResponse");
+	case SOAP_TYPE_ArrayOfString:
+		return soap_out_ArrayOfString(soap, tag, id, (const struct ArrayOfString *)ptr, "xsd:string");
+	case SOAP_TYPE_PointerTons1__stat_USCOREobjectResponse:
+		return soap_out_PointerTons1__stat_USCOREobjectResponse(soap, tag, id, (struct ns1__stat_USCOREobjectResponse *const*)ptr, "ns1:stat_objectResponse");
+	case SOAP_TYPE_PointerTons1__find_USCOREinstancesResponse:
+		return soap_out_PointerTons1__find_USCOREinstancesResponse(soap, tag, id, (struct ns1__find_USCOREinstancesResponse *const*)ptr, "ns1:find_instancesResponse");
+	case SOAP_TYPE_PointerTons1__find_USCOREobjectsResponse:
+		return soap_out_PointerTons1__find_USCOREobjectsResponse(soap, tag, id, (struct ns1__find_USCOREobjectsResponse *const*)ptr, "ns1:find_objectsResponse");
+	case SOAP_TYPE_PointerToArrayOfString:
+		return soap_out_PointerToArrayOfString(soap, tag, id, (struct ArrayOfString *const*)ptr, "xsd:string");
+	case SOAP_TYPE_PointerToint:
+		return soap_out_PointerToint(soap, tag, id, (int *const*)ptr, "xsd:int");
+	case SOAP_TYPE_PointerTostring:
+		return soap_out_PointerTostring(soap, tag, id, (char **const*)ptr, "xsd:string");
+	case SOAP_TYPE__QName:
+		return soap_out_string(soap, "xsd:QName", id, (char*const*)&ptr, NULL);
+	case SOAP_TYPE_string:
+		return soap_out_string(soap, tag, id, (char*const*)&ptr, "xsd:string");
+	}
+	return SOAP_OK;
+}
+#endif
+
+#ifndef WITH_NOIDREF
+SOAP_FMAC3 void SOAP_FMAC4 soap_markelement(struct soap *soap, const void *ptr, int type)
+{
+	(void)soap; (void)ptr; (void)type; /* appease -Wall -Werror */
+	switch (type)
+	{
+	case SOAP_TYPE_ns1__stat_USCOREobject:
+		soap_serialize_ns1__stat_USCOREobject(soap, (const struct ns1__stat_USCOREobject *)ptr);
+		break;
+	case SOAP_TYPE_ns1__stat_USCOREobjectResponse:
+		soap_serialize_ns1__stat_USCOREobjectResponse(soap, (const struct ns1__stat_USCOREobjectResponse *)ptr);
+		break;
+	case SOAP_TYPE_ns1__delete_USCOREinstance:
+		soap_serialize_ns1__delete_USCOREinstance(soap, (const struct ns1__delete_USCOREinstance *)ptr);
+		break;
+	case SOAP_TYPE_ns1__delete_USCOREinstanceResponse:
+		soap_serialize_ns1__delete_USCOREinstanceResponse(soap, (const struct ns1__delete_USCOREinstanceResponse *)ptr);
+		break;
+	case SOAP_TYPE_ns1__find_USCOREinstances:
+		soap_serialize_ns1__find_USCOREinstances(soap, (const struct ns1__find_USCOREinstances *)ptr);
+		break;
+	case SOAP_TYPE_ns1__find_USCOREinstancesResponse:
+		soap_serialize_ns1__find_USCOREinstancesResponse(soap, (const struct ns1__find_USCOREinstancesResponse *)ptr);
+		break;
+	case SOAP_TYPE_ns1__find_USCOREobjects:
+		soap_serialize_ns1__find_USCOREobjects(soap, (const struct ns1__find_USCOREobjects *)ptr);
+		break;
+	case SOAP_TYPE_ns1__find_USCOREobjectsResponse:
+		soap_serialize_ns1__find_USCOREobjectsResponse(soap, (const struct ns1__find_USCOREobjectsResponse *)ptr);
+		break;
+	case SOAP_TYPE_ns1__unlock_USCOREobject:
+		soap_serialize_ns1__unlock_USCOREobject(soap, (const struct ns1__unlock_USCOREobject *)ptr);
+		break;
+	case SOAP_TYPE_ns1__unlock_USCOREobjectResponse:
+		soap_serialize_ns1__unlock_USCOREobjectResponse(soap, (const struct ns1__unlock_USCOREobjectResponse *)ptr);
+		break;
+	case SOAP_TYPE_ns1__lock_USCOREobject:
+		soap_serialize_ns1__lock_USCOREobject(soap, (const struct ns1__lock_USCOREobject *)ptr);
+		break;
+	case SOAP_TYPE_ns1__lock_USCOREobjectResponse:
+		soap_serialize_ns1__lock_USCOREobjectResponse(soap, (const struct ns1__lock_USCOREobjectResponse *)ptr);
+		break;
+	case SOAP_TYPE_ns1__replicate_USCOREobject:
+		soap_serialize_ns1__replicate_USCOREobject(soap, (const struct ns1__replicate_USCOREobject *)ptr);
+		break;
+	case SOAP_TYPE_ns1__replicate_USCOREobjectResponse:
+		soap_serialize_ns1__replicate_USCOREobjectResponse(soap, (const struct ns1__replicate_USCOREobjectResponse *)ptr);
+		break;
+	case SOAP_TYPE_ns1__rename_USCOREobject_:
+		soap_serialize_ns1__rename_USCOREobject_(soap, (const struct ns1__rename_USCOREobject_ *)ptr);
+		break;
+	case SOAP_TYPE_ns1__rename_USCOREobject_Response:
+		soap_serialize_ns1__rename_USCOREobject_Response(soap, (const struct ns1__rename_USCOREobject_Response *)ptr);
+		break;
+	case SOAP_TYPE_ns1__rename_USCOREobject:
+		soap_serialize_ns1__rename_USCOREobject(soap, (const struct ns1__rename_USCOREobject *)ptr);
+		break;
+	case SOAP_TYPE_ns1__rename_USCOREobjectResponse:
+		soap_serialize_ns1__rename_USCOREobjectResponse(soap, (const struct ns1__rename_USCOREobjectResponse *)ptr);
+		break;
+	case SOAP_TYPE_ns1__create_USCOREobject:
+		soap_serialize_ns1__create_USCOREobject(soap, (const struct ns1__create_USCOREobject *)ptr);
+		break;
+	case SOAP_TYPE_ns1__create_USCOREobjectResponse:
+		soap_serialize_ns1__create_USCOREobjectResponse(soap, (const struct ns1__create_USCOREobjectResponse *)ptr);
+		break;
+	case SOAP_TYPE_ArrayOfString:
+		soap_serialize_ArrayOfString(soap, (const struct ArrayOfString *)ptr);
+		break;
+	case SOAP_TYPE_PointerTons1__stat_USCOREobjectResponse:
+		soap_serialize_PointerTons1__stat_USCOREobjectResponse(soap, (struct ns1__stat_USCOREobjectResponse *const*)ptr);
+		break;
+	case SOAP_TYPE_PointerTons1__find_USCOREinstancesResponse:
+		soap_serialize_PointerTons1__find_USCOREinstancesResponse(soap, (struct ns1__find_USCOREinstancesResponse *const*)ptr);
+		break;
+	case SOAP_TYPE_PointerTons1__find_USCOREobjectsResponse:
+		soap_serialize_PointerTons1__find_USCOREobjectsResponse(soap, (struct ns1__find_USCOREobjectsResponse *const*)ptr);
+		break;
+	case SOAP_TYPE_PointerToArrayOfString:
+		soap_serialize_PointerToArrayOfString(soap, (struct ArrayOfString *const*)ptr);
+		break;
+	case SOAP_TYPE_PointerToint:
+		soap_serialize_PointerToint(soap, (int *const*)ptr);
+		break;
+	case SOAP_TYPE_PointerTostring:
+		soap_serialize_PointerTostring(soap, (char **const*)ptr);
+		break;
+	case SOAP_TYPE__QName:
+		soap_serialize_string(soap, (char*const*)&ptr);
+		break;
+	case SOAP_TYPE_string:
+		soap_serialize_string(soap, (char*const*)&ptr);
+		break;
+	}
+}
+#endif
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_byte(struct soap *soap, char *a)
+{	(void)soap; /* appease -Wall -Werror */
+#ifdef SOAP_DEFAULT_byte
+	*a = SOAP_DEFAULT_byte;
+#else
+	*a = (char)0;
+#endif
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_byte(struct soap *soap, const char *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_byte);
+	if (soap_out_byte(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_byte(struct soap *soap, const char *tag, int id, const char *a, const char *type)
+{
+	return soap_outbyte(soap, tag, id, a, type, SOAP_TYPE_byte);
+}
+
+SOAP_FMAC3 char * SOAP_FMAC4 soap_get_byte(struct soap *soap, char *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_byte(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 char * SOAP_FMAC4 soap_in_byte(struct soap *soap, const char *tag, char *a, const char *type)
+{
+	return soap_inbyte(soap, tag, a, type, SOAP_TYPE_byte);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_int(struct soap *soap, int *a)
+{	(void)soap; /* appease -Wall -Werror */
+#ifdef SOAP_DEFAULT_int
+	*a = SOAP_DEFAULT_int;
+#else
+	*a = (int)0;
+#endif
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_int(struct soap *soap, const int *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_int);
+	if (soap_out_int(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_int(struct soap *soap, const char *tag, int id, const int *a, const char *type)
+{
+	return soap_outint(soap, tag, id, a, type, SOAP_TYPE_int);
+}
+
+SOAP_FMAC3 int * SOAP_FMAC4 soap_get_int(struct soap *soap, int *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_int(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 int * SOAP_FMAC4 soap_in_int(struct soap *soap, const char *tag, int *a, const char *type)
+{
+	return soap_inint(soap, tag, a, type, SOAP_TYPE_int);
+}
+
+#ifndef WITH_NOGLOBAL
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Fault(struct soap *soap, struct SOAP_ENV__Fault *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default__QName(soap, &a->faultcode);
+	soap_default_string(soap, &a->faultstring);
+	soap_default_string(soap, &a->faultactor);
+	a->detail = NULL;
+	a->SOAP_ENV__Code = NULL;
+	a->SOAP_ENV__Reason = NULL;
+	soap_default_string(soap, &a->SOAP_ENV__Node);
+	soap_default_string(soap, &a->SOAP_ENV__Role);
+	a->SOAP_ENV__Detail = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Fault(struct soap *soap, const struct SOAP_ENV__Fault *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize__QName(soap, &a->faultcode);
+	soap_serialize_string(soap, &a->faultstring);
+	soap_serialize_string(soap, &a->faultactor);
+	soap_serialize_PointerToSOAP_ENV__Detail(soap, &a->detail);
+	soap_serialize_PointerToSOAP_ENV__Code(soap, &a->SOAP_ENV__Code);
+	soap_serialize_PointerToSOAP_ENV__Reason(soap, &a->SOAP_ENV__Reason);
+	soap_serialize_string(soap, &a->SOAP_ENV__Node);
+	soap_serialize_string(soap, &a->SOAP_ENV__Role);
+	soap_serialize_PointerToSOAP_ENV__Detail(soap, &a->SOAP_ENV__Detail);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Fault(struct soap *soap, const struct SOAP_ENV__Fault *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_SOAP_ENV__Fault);
+	if (soap_out_SOAP_ENV__Fault(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Fault(struct soap *soap, const char *tag, int id, const struct SOAP_ENV__Fault *a, const char *type)
+{
+	const char *soap_tmp_faultcode = soap_QName2s(soap, a->faultcode);
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_SOAP_ENV__Fault), type))
+		return soap->error;
+	if (soap_out__QName(soap, "faultcode", -1, (char*const*)&soap_tmp_faultcode, ""))
+		return soap->error;
+	if (soap_out_string(soap, "faultstring", -1, &a->faultstring, ""))
+		return soap->error;
+	if (soap_out_string(soap, "faultactor", -1, &a->faultactor, ""))
+		return soap->error;
+	if (soap_out_PointerToSOAP_ENV__Detail(soap, "detail", -1, &a->detail, ""))
+		return soap->error;
+	if (soap_out_PointerToSOAP_ENV__Code(soap, "SOAP-ENV:Code", -1, &a->SOAP_ENV__Code, ""))
+		return soap->error;
+	if (soap_out_PointerToSOAP_ENV__Reason(soap, "SOAP-ENV:Reason", -1, &a->SOAP_ENV__Reason, ""))
+		return soap->error;
+	if (soap_out_string(soap, "SOAP-ENV:Node", -1, &a->SOAP_ENV__Node, ""))
+		return soap->error;
+	if (soap_out_string(soap, "SOAP-ENV:Role", -1, &a->SOAP_ENV__Role, ""))
+		return soap->error;
+	if (soap_out_PointerToSOAP_ENV__Detail(soap, "SOAP-ENV:Detail", -1, &a->SOAP_ENV__Detail, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Fault * SOAP_FMAC4 soap_get_SOAP_ENV__Fault(struct soap *soap, struct SOAP_ENV__Fault *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_SOAP_ENV__Fault(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Fault * SOAP_FMAC4 soap_in_SOAP_ENV__Fault(struct soap *soap, const char *tag, struct SOAP_ENV__Fault *a, const char *type)
+{
+	size_t soap_flag_faultcode = 1;
+	size_t soap_flag_faultstring = 1;
+	size_t soap_flag_faultactor = 1;
+	size_t soap_flag_detail = 1;
+	size_t soap_flag_SOAP_ENV__Code = 1;
+	size_t soap_flag_SOAP_ENV__Reason = 1;
+	size_t soap_flag_SOAP_ENV__Node = 1;
+	size_t soap_flag_SOAP_ENV__Role = 1;
+	size_t soap_flag_SOAP_ENV__Detail = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct SOAP_ENV__Fault *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_SOAP_ENV__Fault, sizeof(struct SOAP_ENV__Fault), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_SOAP_ENV__Fault(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_faultcode && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in__QName(soap, "faultcode", &a->faultcode, ""))
+				{	soap_flag_faultcode--;
+					continue;
+				}
+			if (soap_flag_faultstring && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "faultstring", &a->faultstring, "xsd:string"))
+				{	soap_flag_faultstring--;
+					continue;
+				}
+			if (soap_flag_faultactor && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "faultactor", &a->faultactor, "xsd:string"))
+				{	soap_flag_faultactor--;
+					continue;
+				}
+			if (soap_flag_detail && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToSOAP_ENV__Detail(soap, "detail", &a->detail, ""))
+				{	soap_flag_detail--;
+					continue;
+				}
+			if (soap_flag_SOAP_ENV__Code && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToSOAP_ENV__Code(soap, "SOAP-ENV:Code", &a->SOAP_ENV__Code, ""))
+				{	soap_flag_SOAP_ENV__Code--;
+					continue;
+				}
+			if (soap_flag_SOAP_ENV__Reason && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToSOAP_ENV__Reason(soap, "SOAP-ENV:Reason", &a->SOAP_ENV__Reason, ""))
+				{	soap_flag_SOAP_ENV__Reason--;
+					continue;
+				}
+			if (soap_flag_SOAP_ENV__Node && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "SOAP-ENV:Node", &a->SOAP_ENV__Node, "xsd:string"))
+				{	soap_flag_SOAP_ENV__Node--;
+					continue;
+				}
+			if (soap_flag_SOAP_ENV__Role && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "SOAP-ENV:Role", &a->SOAP_ENV__Role, "xsd:string"))
+				{	soap_flag_SOAP_ENV__Role--;
+					continue;
+				}
+			if (soap_flag_SOAP_ENV__Detail && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToSOAP_ENV__Detail(soap, "SOAP-ENV:Detail", &a->SOAP_ENV__Detail, ""))
+				{	soap_flag_SOAP_ENV__Detail--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct SOAP_ENV__Fault *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_SOAP_ENV__Fault, 0, sizeof(struct SOAP_ENV__Fault), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Reason(struct soap *soap, struct SOAP_ENV__Reason *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->SOAP_ENV__Text);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Reason(struct soap *soap, const struct SOAP_ENV__Reason *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->SOAP_ENV__Text);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Reason(struct soap *soap, const struct SOAP_ENV__Reason *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_SOAP_ENV__Reason);
+	if (soap_out_SOAP_ENV__Reason(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Reason(struct soap *soap, const char *tag, int id, const struct SOAP_ENV__Reason *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_SOAP_ENV__Reason), type))
+		return soap->error;
+	if (soap->lang)
+		soap_set_attr(soap, "xml:lang", soap->lang);
+	if (soap_out_string(soap, "SOAP-ENV:Text", -1, &a->SOAP_ENV__Text, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Reason * SOAP_FMAC4 soap_get_SOAP_ENV__Reason(struct soap *soap, struct SOAP_ENV__Reason *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_SOAP_ENV__Reason(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Reason * SOAP_FMAC4 soap_in_SOAP_ENV__Reason(struct soap *soap, const char *tag, struct SOAP_ENV__Reason *a, const char *type)
+{
+	size_t soap_flag_SOAP_ENV__Text = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct SOAP_ENV__Reason *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_SOAP_ENV__Reason, sizeof(struct SOAP_ENV__Reason), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_SOAP_ENV__Reason(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_SOAP_ENV__Text && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "SOAP-ENV:Text", &a->SOAP_ENV__Text, "xsd:string"))
+				{	soap_flag_SOAP_ENV__Text--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct SOAP_ENV__Reason *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_SOAP_ENV__Reason, 0, sizeof(struct SOAP_ENV__Reason), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Detail(struct soap *soap, struct SOAP_ENV__Detail *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->__type = 0;
+	a->fault = NULL;
+	a->__any = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Detail(struct soap *soap, const struct SOAP_ENV__Detail *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_markelement(soap, a->fault, a->__type);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Detail(struct soap *soap, const struct SOAP_ENV__Detail *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_SOAP_ENV__Detail);
+	if (soap_out_SOAP_ENV__Detail(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Detail(struct soap *soap, const char *tag, int id, const struct SOAP_ENV__Detail *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_SOAP_ENV__Detail), type))
+		return soap->error;
+	if (soap_putelement(soap, a->fault, "fault", -1, a->__type))
+		return soap->error;
+	soap_outliteral(soap, "-any", &a->__any, NULL);
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Detail * SOAP_FMAC4 soap_get_SOAP_ENV__Detail(struct soap *soap, struct SOAP_ENV__Detail *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_SOAP_ENV__Detail(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Detail * SOAP_FMAC4 soap_in_SOAP_ENV__Detail(struct soap *soap, const char *tag, struct SOAP_ENV__Detail *a, const char *type)
+{
+	size_t soap_flag_fault = 1;
+	size_t soap_flag___any = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct SOAP_ENV__Detail *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_SOAP_ENV__Detail, sizeof(struct SOAP_ENV__Detail), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_SOAP_ENV__Detail(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_fault && soap->error == SOAP_TAG_MISMATCH)
+				if ((a->fault = soap_getelement(soap, &a->__type)))
+				{	soap_flag_fault = 0;
+					continue;
+				}
+			if (soap_flag___any && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_inliteral(soap, "-any", &a->__any))
+				{	soap_flag___any--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct SOAP_ENV__Detail *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_SOAP_ENV__Detail, 0, sizeof(struct SOAP_ENV__Detail), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Code(struct soap *soap, struct SOAP_ENV__Code *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default__QName(soap, &a->SOAP_ENV__Value);
+	a->SOAP_ENV__Subcode = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Code(struct soap *soap, const struct SOAP_ENV__Code *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize__QName(soap, &a->SOAP_ENV__Value);
+	soap_serialize_PointerToSOAP_ENV__Code(soap, &a->SOAP_ENV__Subcode);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Code(struct soap *soap, const struct SOAP_ENV__Code *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_SOAP_ENV__Code);
+	if (soap_out_SOAP_ENV__Code(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Code(struct soap *soap, const char *tag, int id, const struct SOAP_ENV__Code *a, const char *type)
+{
+	const char *soap_tmp_SOAP_ENV__Value = soap_QName2s(soap, a->SOAP_ENV__Value);
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_SOAP_ENV__Code), type))
+		return soap->error;
+	if (soap_out__QName(soap, "SOAP-ENV:Value", -1, (char*const*)&soap_tmp_SOAP_ENV__Value, ""))
+		return soap->error;
+	if (soap_out_PointerToSOAP_ENV__Code(soap, "SOAP-ENV:Subcode", -1, &a->SOAP_ENV__Subcode, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Code * SOAP_FMAC4 soap_get_SOAP_ENV__Code(struct soap *soap, struct SOAP_ENV__Code *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_SOAP_ENV__Code(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Code * SOAP_FMAC4 soap_in_SOAP_ENV__Code(struct soap *soap, const char *tag, struct SOAP_ENV__Code *a, const char *type)
+{
+	size_t soap_flag_SOAP_ENV__Value = 1;
+	size_t soap_flag_SOAP_ENV__Subcode = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct SOAP_ENV__Code *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_SOAP_ENV__Code, sizeof(struct SOAP_ENV__Code), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_SOAP_ENV__Code(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_SOAP_ENV__Value && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in__QName(soap, "SOAP-ENV:Value", &a->SOAP_ENV__Value, ""))
+				{	soap_flag_SOAP_ENV__Value--;
+					continue;
+				}
+			if (soap_flag_SOAP_ENV__Subcode && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToSOAP_ENV__Code(soap, "SOAP-ENV:Subcode", &a->SOAP_ENV__Subcode, ""))
+				{	soap_flag_SOAP_ENV__Subcode--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct SOAP_ENV__Code *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_SOAP_ENV__Code, 0, sizeof(struct SOAP_ENV__Code), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Header(struct soap *soap, struct SOAP_ENV__Header *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Header(struct soap *soap, const struct SOAP_ENV__Header *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Header(struct soap *soap, const struct SOAP_ENV__Header *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_SOAP_ENV__Header);
+	if (soap_out_SOAP_ENV__Header(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Header(struct soap *soap, const char *tag, int id, const struct SOAP_ENV__Header *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_SOAP_ENV__Header), type))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Header * SOAP_FMAC4 soap_get_SOAP_ENV__Header(struct soap *soap, struct SOAP_ENV__Header *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_SOAP_ENV__Header(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Header * SOAP_FMAC4 soap_in_SOAP_ENV__Header(struct soap *soap, const char *tag, struct SOAP_ENV__Header *a, const char *type)
+{
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct SOAP_ENV__Header *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_SOAP_ENV__Header, sizeof(struct SOAP_ENV__Header), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_SOAP_ENV__Header(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct SOAP_ENV__Header *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_SOAP_ENV__Header, 0, sizeof(struct SOAP_ENV__Header), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+#endif
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__stat_USCOREobject(struct soap *soap, struct ns1__stat_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->key);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__stat_USCOREobject(struct soap *soap, const struct ns1__stat_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->key);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__stat_USCOREobject(struct soap *soap, const struct ns1__stat_USCOREobject *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__stat_USCOREobject);
+	if (soap_out_ns1__stat_USCOREobject(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__stat_USCOREobject(struct soap *soap, const char *tag, int id, const struct ns1__stat_USCOREobject *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__stat_USCOREobject), type))
+		return soap->error;
+	if (soap_out_string(soap, "key", -1, &a->key, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__stat_USCOREobject * SOAP_FMAC4 soap_get_ns1__stat_USCOREobject(struct soap *soap, struct ns1__stat_USCOREobject *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__stat_USCOREobject(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__stat_USCOREobject * SOAP_FMAC4 soap_in_ns1__stat_USCOREobject(struct soap *soap, const char *tag, struct ns1__stat_USCOREobject *a, const char *type)
+{
+	size_t soap_flag_key = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__stat_USCOREobject *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__stat_USCOREobject, sizeof(struct ns1__stat_USCOREobject), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__stat_USCOREobject(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_key && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "key", &a->key, "xsd:string"))
+				{	soap_flag_key--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__stat_USCOREobject *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__stat_USCOREobject, 0, sizeof(struct ns1__stat_USCOREobject), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__stat_USCOREobjectResponse(struct soap *soap, struct ns1__stat_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->result = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__stat_USCOREobjectResponse(struct soap *soap, const struct ns1__stat_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_PointerToArrayOfString(soap, &a->result);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__stat_USCOREobjectResponse(struct soap *soap, const struct ns1__stat_USCOREobjectResponse *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__stat_USCOREobjectResponse);
+	if (soap_out_ns1__stat_USCOREobjectResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__stat_USCOREobjectResponse(struct soap *soap, const char *tag, int id, const struct ns1__stat_USCOREobjectResponse *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__stat_USCOREobjectResponse), type))
+		return soap->error;
+	if (soap_out_PointerToArrayOfString(soap, "result", -1, &a->result, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__stat_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__stat_USCOREobjectResponse(struct soap *soap, struct ns1__stat_USCOREobjectResponse *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__stat_USCOREobjectResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__stat_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__stat_USCOREobjectResponse(struct soap *soap, const char *tag, struct ns1__stat_USCOREobjectResponse *a, const char *type)
+{
+	size_t soap_flag_result = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__stat_USCOREobjectResponse *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__stat_USCOREobjectResponse, sizeof(struct ns1__stat_USCOREobjectResponse), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__stat_USCOREobjectResponse(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_result && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToArrayOfString(soap, "result", &a->result, "xsd:string"))
+				{	soap_flag_result--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__stat_USCOREobjectResponse *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__stat_USCOREobjectResponse, 0, sizeof(struct ns1__stat_USCOREobjectResponse), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__delete_USCOREinstance(struct soap *soap, struct ns1__delete_USCOREinstance *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->uri);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__delete_USCOREinstance(struct soap *soap, const struct ns1__delete_USCOREinstance *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->uri);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__delete_USCOREinstance(struct soap *soap, const struct ns1__delete_USCOREinstance *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__delete_USCOREinstance);
+	if (soap_out_ns1__delete_USCOREinstance(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__delete_USCOREinstance(struct soap *soap, const char *tag, int id, const struct ns1__delete_USCOREinstance *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__delete_USCOREinstance), type))
+		return soap->error;
+	if (soap_out_string(soap, "uri", -1, &a->uri, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__delete_USCOREinstance * SOAP_FMAC4 soap_get_ns1__delete_USCOREinstance(struct soap *soap, struct ns1__delete_USCOREinstance *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__delete_USCOREinstance(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__delete_USCOREinstance * SOAP_FMAC4 soap_in_ns1__delete_USCOREinstance(struct soap *soap, const char *tag, struct ns1__delete_USCOREinstance *a, const char *type)
+{
+	size_t soap_flag_uri = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__delete_USCOREinstance *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__delete_USCOREinstance, sizeof(struct ns1__delete_USCOREinstance), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__delete_USCOREinstance(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_uri && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "uri", &a->uri, "xsd:string"))
+				{	soap_flag_uri--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__delete_USCOREinstance *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__delete_USCOREinstance, 0, sizeof(struct ns1__delete_USCOREinstance), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__delete_USCOREinstanceResponse(struct soap *soap, struct ns1__delete_USCOREinstanceResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->result = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__delete_USCOREinstanceResponse(struct soap *soap, const struct ns1__delete_USCOREinstanceResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_PointerToint(soap, &a->result);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__delete_USCOREinstanceResponse(struct soap *soap, const struct ns1__delete_USCOREinstanceResponse *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__delete_USCOREinstanceResponse);
+	if (soap_out_ns1__delete_USCOREinstanceResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__delete_USCOREinstanceResponse(struct soap *soap, const char *tag, int id, const struct ns1__delete_USCOREinstanceResponse *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__delete_USCOREinstanceResponse), type))
+		return soap->error;
+	if (soap_out_PointerToint(soap, "result", -1, &a->result, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__delete_USCOREinstanceResponse * SOAP_FMAC4 soap_get_ns1__delete_USCOREinstanceResponse(struct soap *soap, struct ns1__delete_USCOREinstanceResponse *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__delete_USCOREinstanceResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__delete_USCOREinstanceResponse * SOAP_FMAC4 soap_in_ns1__delete_USCOREinstanceResponse(struct soap *soap, const char *tag, struct ns1__delete_USCOREinstanceResponse *a, const char *type)
+{
+	size_t soap_flag_result = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__delete_USCOREinstanceResponse *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__delete_USCOREinstanceResponse, sizeof(struct ns1__delete_USCOREinstanceResponse), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__delete_USCOREinstanceResponse(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_result && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToint(soap, "result", &a->result, "xsd:int"))
+				{	soap_flag_result--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__delete_USCOREinstanceResponse *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__delete_USCOREinstanceResponse, 0, sizeof(struct ns1__delete_USCOREinstanceResponse), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__find_USCOREinstances(struct soap *soap, struct ns1__find_USCOREinstances *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->key);
+	soap_default_string(soap, &a->volume);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__find_USCOREinstances(struct soap *soap, const struct ns1__find_USCOREinstances *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->key);
+	soap_serialize_string(soap, &a->volume);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__find_USCOREinstances(struct soap *soap, const struct ns1__find_USCOREinstances *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__find_USCOREinstances);
+	if (soap_out_ns1__find_USCOREinstances(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__find_USCOREinstances(struct soap *soap, const char *tag, int id, const struct ns1__find_USCOREinstances *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__find_USCOREinstances), type))
+		return soap->error;
+	if (soap_out_string(soap, "key", -1, &a->key, ""))
+		return soap->error;
+	if (soap_out_string(soap, "volume", -1, &a->volume, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREinstances * SOAP_FMAC4 soap_get_ns1__find_USCOREinstances(struct soap *soap, struct ns1__find_USCOREinstances *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__find_USCOREinstances(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREinstances * SOAP_FMAC4 soap_in_ns1__find_USCOREinstances(struct soap *soap, const char *tag, struct ns1__find_USCOREinstances *a, const char *type)
+{
+	size_t soap_flag_key = 1;
+	size_t soap_flag_volume = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__find_USCOREinstances *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__find_USCOREinstances, sizeof(struct ns1__find_USCOREinstances), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__find_USCOREinstances(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_key && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "key", &a->key, "xsd:string"))
+				{	soap_flag_key--;
+					continue;
+				}
+			if (soap_flag_volume && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "volume", &a->volume, "xsd:string"))
+				{	soap_flag_volume--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__find_USCOREinstances *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__find_USCOREinstances, 0, sizeof(struct ns1__find_USCOREinstances), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__find_USCOREinstancesResponse(struct soap *soap, struct ns1__find_USCOREinstancesResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->result = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__find_USCOREinstancesResponse(struct soap *soap, const struct ns1__find_USCOREinstancesResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_PointerToArrayOfString(soap, &a->result);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__find_USCOREinstancesResponse(struct soap *soap, const struct ns1__find_USCOREinstancesResponse *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__find_USCOREinstancesResponse);
+	if (soap_out_ns1__find_USCOREinstancesResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__find_USCOREinstancesResponse(struct soap *soap, const char *tag, int id, const struct ns1__find_USCOREinstancesResponse *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__find_USCOREinstancesResponse), type))
+		return soap->error;
+	if (soap_out_PointerToArrayOfString(soap, "result", -1, &a->result, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREinstancesResponse * SOAP_FMAC4 soap_get_ns1__find_USCOREinstancesResponse(struct soap *soap, struct ns1__find_USCOREinstancesResponse *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__find_USCOREinstancesResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREinstancesResponse * SOAP_FMAC4 soap_in_ns1__find_USCOREinstancesResponse(struct soap *soap, const char *tag, struct ns1__find_USCOREinstancesResponse *a, const char *type)
+{
+	size_t soap_flag_result = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__find_USCOREinstancesResponse *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__find_USCOREinstancesResponse, sizeof(struct ns1__find_USCOREinstancesResponse), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__find_USCOREinstancesResponse(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_result && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToArrayOfString(soap, "result", &a->result, "xsd:string"))
+				{	soap_flag_result--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__find_USCOREinstancesResponse *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__find_USCOREinstancesResponse, 0, sizeof(struct ns1__find_USCOREinstancesResponse), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__find_USCOREobjects(struct soap *soap, struct ns1__find_USCOREobjects *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->pattern);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__find_USCOREobjects(struct soap *soap, const struct ns1__find_USCOREobjects *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->pattern);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__find_USCOREobjects(struct soap *soap, const struct ns1__find_USCOREobjects *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__find_USCOREobjects);
+	if (soap_out_ns1__find_USCOREobjects(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__find_USCOREobjects(struct soap *soap, const char *tag, int id, const struct ns1__find_USCOREobjects *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__find_USCOREobjects), type))
+		return soap->error;
+	if (soap_out_string(soap, "pattern", -1, &a->pattern, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREobjects * SOAP_FMAC4 soap_get_ns1__find_USCOREobjects(struct soap *soap, struct ns1__find_USCOREobjects *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__find_USCOREobjects(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREobjects * SOAP_FMAC4 soap_in_ns1__find_USCOREobjects(struct soap *soap, const char *tag, struct ns1__find_USCOREobjects *a, const char *type)
+{
+	size_t soap_flag_pattern = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__find_USCOREobjects *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__find_USCOREobjects, sizeof(struct ns1__find_USCOREobjects), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__find_USCOREobjects(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_pattern && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "pattern", &a->pattern, "xsd:string"))
+				{	soap_flag_pattern--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__find_USCOREobjects *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__find_USCOREobjects, 0, sizeof(struct ns1__find_USCOREobjects), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__find_USCOREobjectsResponse(struct soap *soap, struct ns1__find_USCOREobjectsResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->result = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__find_USCOREobjectsResponse(struct soap *soap, const struct ns1__find_USCOREobjectsResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_PointerToArrayOfString(soap, &a->result);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__find_USCOREobjectsResponse(struct soap *soap, const struct ns1__find_USCOREobjectsResponse *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__find_USCOREobjectsResponse);
+	if (soap_out_ns1__find_USCOREobjectsResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__find_USCOREobjectsResponse(struct soap *soap, const char *tag, int id, const struct ns1__find_USCOREobjectsResponse *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__find_USCOREobjectsResponse), type))
+		return soap->error;
+	if (soap_out_PointerToArrayOfString(soap, "result", -1, &a->result, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREobjectsResponse * SOAP_FMAC4 soap_get_ns1__find_USCOREobjectsResponse(struct soap *soap, struct ns1__find_USCOREobjectsResponse *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__find_USCOREobjectsResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREobjectsResponse * SOAP_FMAC4 soap_in_ns1__find_USCOREobjectsResponse(struct soap *soap, const char *tag, struct ns1__find_USCOREobjectsResponse *a, const char *type)
+{
+	size_t soap_flag_result = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__find_USCOREobjectsResponse *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__find_USCOREobjectsResponse, sizeof(struct ns1__find_USCOREobjectsResponse), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__find_USCOREobjectsResponse(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_result && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToArrayOfString(soap, "result", &a->result, "xsd:string"))
+				{	soap_flag_result--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__find_USCOREobjectsResponse *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__find_USCOREobjectsResponse, 0, sizeof(struct ns1__find_USCOREobjectsResponse), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__unlock_USCOREobject(struct soap *soap, struct ns1__unlock_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->key);
+	soap_default_string(soap, &a->type);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__unlock_USCOREobject(struct soap *soap, const struct ns1__unlock_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->key);
+	soap_serialize_string(soap, &a->type);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__unlock_USCOREobject(struct soap *soap, const struct ns1__unlock_USCOREobject *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__unlock_USCOREobject);
+	if (soap_out_ns1__unlock_USCOREobject(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__unlock_USCOREobject(struct soap *soap, const char *tag, int id, const struct ns1__unlock_USCOREobject *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__unlock_USCOREobject), type))
+		return soap->error;
+	if (soap_out_string(soap, "key", -1, &a->key, ""))
+		return soap->error;
+	if (soap_out_string(soap, "type", -1, &a->type, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__unlock_USCOREobject * SOAP_FMAC4 soap_get_ns1__unlock_USCOREobject(struct soap *soap, struct ns1__unlock_USCOREobject *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__unlock_USCOREobject(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__unlock_USCOREobject * SOAP_FMAC4 soap_in_ns1__unlock_USCOREobject(struct soap *soap, const char *tag, struct ns1__unlock_USCOREobject *a, const char *type)
+{
+	size_t soap_flag_key = 1;
+	size_t soap_flag_type = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__unlock_USCOREobject *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__unlock_USCOREobject, sizeof(struct ns1__unlock_USCOREobject), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__unlock_USCOREobject(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_key && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "key", &a->key, "xsd:string"))
+				{	soap_flag_key--;
+					continue;
+				}
+			if (soap_flag_type && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "type", &a->type, "xsd:string"))
+				{	soap_flag_type--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__unlock_USCOREobject *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__unlock_USCOREobject, 0, sizeof(struct ns1__unlock_USCOREobject), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__unlock_USCOREobjectResponse(struct soap *soap, struct ns1__unlock_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->result = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__unlock_USCOREobjectResponse(struct soap *soap, const struct ns1__unlock_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_PointerToint(soap, &a->result);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__unlock_USCOREobjectResponse(struct soap *soap, const struct ns1__unlock_USCOREobjectResponse *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__unlock_USCOREobjectResponse);
+	if (soap_out_ns1__unlock_USCOREobjectResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__unlock_USCOREobjectResponse(struct soap *soap, const char *tag, int id, const struct ns1__unlock_USCOREobjectResponse *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__unlock_USCOREobjectResponse), type))
+		return soap->error;
+	if (soap_out_PointerToint(soap, "result", -1, &a->result, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__unlock_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__unlock_USCOREobjectResponse(struct soap *soap, struct ns1__unlock_USCOREobjectResponse *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__unlock_USCOREobjectResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__unlock_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__unlock_USCOREobjectResponse(struct soap *soap, const char *tag, struct ns1__unlock_USCOREobjectResponse *a, const char *type)
+{
+	size_t soap_flag_result = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__unlock_USCOREobjectResponse *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__unlock_USCOREobjectResponse, sizeof(struct ns1__unlock_USCOREobjectResponse), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__unlock_USCOREobjectResponse(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_result && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToint(soap, "result", &a->result, "xsd:int"))
+				{	soap_flag_result--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__unlock_USCOREobjectResponse *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__unlock_USCOREobjectResponse, 0, sizeof(struct ns1__unlock_USCOREobjectResponse), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__lock_USCOREobject(struct soap *soap, struct ns1__lock_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->key);
+	soap_default_string(soap, &a->type);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__lock_USCOREobject(struct soap *soap, const struct ns1__lock_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->key);
+	soap_serialize_string(soap, &a->type);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__lock_USCOREobject(struct soap *soap, const struct ns1__lock_USCOREobject *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__lock_USCOREobject);
+	if (soap_out_ns1__lock_USCOREobject(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__lock_USCOREobject(struct soap *soap, const char *tag, int id, const struct ns1__lock_USCOREobject *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__lock_USCOREobject), type))
+		return soap->error;
+	if (soap_out_string(soap, "key", -1, &a->key, ""))
+		return soap->error;
+	if (soap_out_string(soap, "type", -1, &a->type, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__lock_USCOREobject * SOAP_FMAC4 soap_get_ns1__lock_USCOREobject(struct soap *soap, struct ns1__lock_USCOREobject *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__lock_USCOREobject(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__lock_USCOREobject * SOAP_FMAC4 soap_in_ns1__lock_USCOREobject(struct soap *soap, const char *tag, struct ns1__lock_USCOREobject *a, const char *type)
+{
+	size_t soap_flag_key = 1;
+	size_t soap_flag_type = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__lock_USCOREobject *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__lock_USCOREobject, sizeof(struct ns1__lock_USCOREobject), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__lock_USCOREobject(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_key && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "key", &a->key, "xsd:string"))
+				{	soap_flag_key--;
+					continue;
+				}
+			if (soap_flag_type && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "type", &a->type, "xsd:string"))
+				{	soap_flag_type--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__lock_USCOREobject *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__lock_USCOREobject, 0, sizeof(struct ns1__lock_USCOREobject), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__lock_USCOREobjectResponse(struct soap *soap, struct ns1__lock_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->result = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__lock_USCOREobjectResponse(struct soap *soap, const struct ns1__lock_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_PointerToint(soap, &a->result);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__lock_USCOREobjectResponse(struct soap *soap, const struct ns1__lock_USCOREobjectResponse *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__lock_USCOREobjectResponse);
+	if (soap_out_ns1__lock_USCOREobjectResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__lock_USCOREobjectResponse(struct soap *soap, const char *tag, int id, const struct ns1__lock_USCOREobjectResponse *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__lock_USCOREobjectResponse), type))
+		return soap->error;
+	if (soap_out_PointerToint(soap, "result", -1, &a->result, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__lock_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__lock_USCOREobjectResponse(struct soap *soap, struct ns1__lock_USCOREobjectResponse *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__lock_USCOREobjectResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__lock_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__lock_USCOREobjectResponse(struct soap *soap, const char *tag, struct ns1__lock_USCOREobjectResponse *a, const char *type)
+{
+	size_t soap_flag_result = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__lock_USCOREobjectResponse *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__lock_USCOREobjectResponse, sizeof(struct ns1__lock_USCOREobjectResponse), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__lock_USCOREobjectResponse(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_result && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerToint(soap, "result", &a->result, "xsd:int"))
+				{	soap_flag_result--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__lock_USCOREobjectResponse *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__lock_USCOREobjectResponse, 0, sizeof(struct ns1__lock_USCOREobjectResponse), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__replicate_USCOREobject(struct soap *soap, struct ns1__replicate_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->key);
+	soap_default_string(soap, &a->volume);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__replicate_USCOREobject(struct soap *soap, const struct ns1__replicate_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->key);
+	soap_serialize_string(soap, &a->volume);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__replicate_USCOREobject(struct soap *soap, const struct ns1__replicate_USCOREobject *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__replicate_USCOREobject);
+	if (soap_out_ns1__replicate_USCOREobject(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__replicate_USCOREobject(struct soap *soap, const char *tag, int id, const struct ns1__replicate_USCOREobject *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__replicate_USCOREobject), type))
+		return soap->error;
+	if (soap_out_string(soap, "key", -1, &a->key, ""))
+		return soap->error;
+	if (soap_out_string(soap, "volume", -1, &a->volume, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__replicate_USCOREobject * SOAP_FMAC4 soap_get_ns1__replicate_USCOREobject(struct soap *soap, struct ns1__replicate_USCOREobject *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__replicate_USCOREobject(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__replicate_USCOREobject * SOAP_FMAC4 soap_in_ns1__replicate_USCOREobject(struct soap *soap, const char *tag, struct ns1__replicate_USCOREobject *a, const char *type)
+{
+	size_t soap_flag_key = 1;
+	size_t soap_flag_volume = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__replicate_USCOREobject *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__replicate_USCOREobject, sizeof(struct ns1__replicate_USCOREobject), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__replicate_USCOREobject(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_key && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "key", &a->key, "xsd:string"))
+				{	soap_flag_key--;
+					continue;
+				}
+			if (soap_flag_volume && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "volume", &a->volume, "xsd:string"))
+				{	soap_flag_volume--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__replicate_USCOREobject *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__replicate_USCOREobject, 0, sizeof(struct ns1__replicate_USCOREobject), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__replicate_USCOREobjectResponse(struct soap *soap, struct ns1__replicate_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->result = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__replicate_USCOREobjectResponse(struct soap *soap, const struct ns1__replicate_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_PointerTostring(soap, &a->result);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__replicate_USCOREobjectResponse(struct soap *soap, const struct ns1__replicate_USCOREobjectResponse *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__replicate_USCOREobjectResponse);
+	if (soap_out_ns1__replicate_USCOREobjectResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__replicate_USCOREobjectResponse(struct soap *soap, const char *tag, int id, const struct ns1__replicate_USCOREobjectResponse *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__replicate_USCOREobjectResponse), type))
+		return soap->error;
+	if (soap_out_PointerTostring(soap, "result", -1, &a->result, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__replicate_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__replicate_USCOREobjectResponse(struct soap *soap, struct ns1__replicate_USCOREobjectResponse *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__replicate_USCOREobjectResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__replicate_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__replicate_USCOREobjectResponse(struct soap *soap, const char *tag, struct ns1__replicate_USCOREobjectResponse *a, const char *type)
+{
+	size_t soap_flag_result = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__replicate_USCOREobjectResponse *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__replicate_USCOREobjectResponse, sizeof(struct ns1__replicate_USCOREobjectResponse), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__replicate_USCOREobjectResponse(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_result && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerTostring(soap, "result", &a->result, "xsd:string"))
+				{	soap_flag_result--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__replicate_USCOREobjectResponse *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__replicate_USCOREobjectResponse, 0, sizeof(struct ns1__replicate_USCOREobjectResponse), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__rename_USCOREobject_(struct soap *soap, struct ns1__rename_USCOREobject_ *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->key);
+	soap_default_string(soap, &a->newkey);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__rename_USCOREobject_(struct soap *soap, const struct ns1__rename_USCOREobject_ *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->key);
+	soap_serialize_string(soap, &a->newkey);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__rename_USCOREobject_(struct soap *soap, const struct ns1__rename_USCOREobject_ *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__rename_USCOREobject_);
+	if (soap_out_ns1__rename_USCOREobject_(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__rename_USCOREobject_(struct soap *soap, const char *tag, int id, const struct ns1__rename_USCOREobject_ *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__rename_USCOREobject_), type))
+		return soap->error;
+	if (soap_out_string(soap, "key", -1, &a->key, ""))
+		return soap->error;
+	if (soap_out_string(soap, "newkey", -1, &a->newkey, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__rename_USCOREobject_ * SOAP_FMAC4 soap_get_ns1__rename_USCOREobject_(struct soap *soap, struct ns1__rename_USCOREobject_ *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__rename_USCOREobject_(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__rename_USCOREobject_ * SOAP_FMAC4 soap_in_ns1__rename_USCOREobject_(struct soap *soap, const char *tag, struct ns1__rename_USCOREobject_ *a, const char *type)
+{
+	size_t soap_flag_key = 1;
+	size_t soap_flag_newkey = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__rename_USCOREobject_ *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__rename_USCOREobject_, sizeof(struct ns1__rename_USCOREobject_), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__rename_USCOREobject_(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_key && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "key", &a->key, "xsd:string"))
+				{	soap_flag_key--;
+					continue;
+				}
+			if (soap_flag_newkey && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "newkey", &a->newkey, "xsd:string"))
+				{	soap_flag_newkey--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__rename_USCOREobject_ *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__rename_USCOREobject_, 0, sizeof(struct ns1__rename_USCOREobject_), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__rename_USCOREobject_Response(struct soap *soap, struct ns1__rename_USCOREobject_Response *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->result = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__rename_USCOREobject_Response(struct soap *soap, const struct ns1__rename_USCOREobject_Response *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_PointerTostring(soap, &a->result);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__rename_USCOREobject_Response(struct soap *soap, const struct ns1__rename_USCOREobject_Response *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__rename_USCOREobject_Response);
+	if (soap_out_ns1__rename_USCOREobject_Response(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__rename_USCOREobject_Response(struct soap *soap, const char *tag, int id, const struct ns1__rename_USCOREobject_Response *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__rename_USCOREobject_Response), type))
+		return soap->error;
+	if (soap_out_PointerTostring(soap, "result", -1, &a->result, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__rename_USCOREobject_Response * SOAP_FMAC4 soap_get_ns1__rename_USCOREobject_Response(struct soap *soap, struct ns1__rename_USCOREobject_Response *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__rename_USCOREobject_Response(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__rename_USCOREobject_Response * SOAP_FMAC4 soap_in_ns1__rename_USCOREobject_Response(struct soap *soap, const char *tag, struct ns1__rename_USCOREobject_Response *a, const char *type)
+{
+	size_t soap_flag_result = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__rename_USCOREobject_Response *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__rename_USCOREobject_Response, sizeof(struct ns1__rename_USCOREobject_Response), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__rename_USCOREobject_Response(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_result && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerTostring(soap, "result", &a->result, "xsd:string"))
+				{	soap_flag_result--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__rename_USCOREobject_Response *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__rename_USCOREobject_Response, 0, sizeof(struct ns1__rename_USCOREobject_Response), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__rename_USCOREobject(struct soap *soap, struct ns1__rename_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->key);
+	soap_default_string(soap, &a->newkey);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__rename_USCOREobject(struct soap *soap, const struct ns1__rename_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->key);
+	soap_serialize_string(soap, &a->newkey);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__rename_USCOREobject(struct soap *soap, const struct ns1__rename_USCOREobject *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__rename_USCOREobject);
+	if (soap_out_ns1__rename_USCOREobject(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__rename_USCOREobject(struct soap *soap, const char *tag, int id, const struct ns1__rename_USCOREobject *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__rename_USCOREobject), type))
+		return soap->error;
+	if (soap_out_string(soap, "key", -1, &a->key, ""))
+		return soap->error;
+	if (soap_out_string(soap, "newkey", -1, &a->newkey, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__rename_USCOREobject * SOAP_FMAC4 soap_get_ns1__rename_USCOREobject(struct soap *soap, struct ns1__rename_USCOREobject *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__rename_USCOREobject(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__rename_USCOREobject * SOAP_FMAC4 soap_in_ns1__rename_USCOREobject(struct soap *soap, const char *tag, struct ns1__rename_USCOREobject *a, const char *type)
+{
+	size_t soap_flag_key = 1;
+	size_t soap_flag_newkey = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__rename_USCOREobject *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__rename_USCOREobject, sizeof(struct ns1__rename_USCOREobject), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__rename_USCOREobject(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_key && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "key", &a->key, "xsd:string"))
+				{	soap_flag_key--;
+					continue;
+				}
+			if (soap_flag_newkey && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "newkey", &a->newkey, "xsd:string"))
+				{	soap_flag_newkey--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__rename_USCOREobject *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__rename_USCOREobject, 0, sizeof(struct ns1__rename_USCOREobject), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__rename_USCOREobjectResponse(struct soap *soap, struct ns1__rename_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->result = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__rename_USCOREobjectResponse(struct soap *soap, const struct ns1__rename_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_PointerTostring(soap, &a->result);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__rename_USCOREobjectResponse(struct soap *soap, const struct ns1__rename_USCOREobjectResponse *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__rename_USCOREobjectResponse);
+	if (soap_out_ns1__rename_USCOREobjectResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__rename_USCOREobjectResponse(struct soap *soap, const char *tag, int id, const struct ns1__rename_USCOREobjectResponse *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__rename_USCOREobjectResponse), type))
+		return soap->error;
+	if (soap_out_PointerTostring(soap, "result", -1, &a->result, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__rename_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__rename_USCOREobjectResponse(struct soap *soap, struct ns1__rename_USCOREobjectResponse *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__rename_USCOREobjectResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__rename_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__rename_USCOREobjectResponse(struct soap *soap, const char *tag, struct ns1__rename_USCOREobjectResponse *a, const char *type)
+{
+	size_t soap_flag_result = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__rename_USCOREobjectResponse *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__rename_USCOREobjectResponse, sizeof(struct ns1__rename_USCOREobjectResponse), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__rename_USCOREobjectResponse(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_result && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerTostring(soap, "result", &a->result, "xsd:string"))
+				{	soap_flag_result--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__rename_USCOREobjectResponse *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__rename_USCOREobjectResponse, 0, sizeof(struct ns1__rename_USCOREobjectResponse), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__create_USCOREobject(struct soap *soap, struct ns1__create_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_default_string(soap, &a->key);
+	soap_default_string(soap, &a->volume);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__create_USCOREobject(struct soap *soap, const struct ns1__create_USCOREobject *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_string(soap, &a->key);
+	soap_serialize_string(soap, &a->volume);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__create_USCOREobject(struct soap *soap, const struct ns1__create_USCOREobject *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__create_USCOREobject);
+	if (soap_out_ns1__create_USCOREobject(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__create_USCOREobject(struct soap *soap, const char *tag, int id, const struct ns1__create_USCOREobject *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__create_USCOREobject), type))
+		return soap->error;
+	if (soap_out_string(soap, "key", -1, &a->key, ""))
+		return soap->error;
+	if (soap_out_string(soap, "volume", -1, &a->volume, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__create_USCOREobject * SOAP_FMAC4 soap_get_ns1__create_USCOREobject(struct soap *soap, struct ns1__create_USCOREobject *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__create_USCOREobject(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__create_USCOREobject * SOAP_FMAC4 soap_in_ns1__create_USCOREobject(struct soap *soap, const char *tag, struct ns1__create_USCOREobject *a, const char *type)
+{
+	size_t soap_flag_key = 1;
+	size_t soap_flag_volume = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__create_USCOREobject *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__create_USCOREobject, sizeof(struct ns1__create_USCOREobject), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__create_USCOREobject(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_key && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "key", &a->key, "xsd:string"))
+				{	soap_flag_key--;
+					continue;
+				}
+			if (soap_flag_volume && (soap->error == SOAP_TAG_MISMATCH || soap->error == SOAP_NO_TAG))
+				if (soap_in_string(soap, "volume", &a->volume, "xsd:string"))
+				{	soap_flag_volume--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__create_USCOREobject *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__create_USCOREobject, 0, sizeof(struct ns1__create_USCOREobject), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__create_USCOREobjectResponse(struct soap *soap, struct ns1__create_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	a->result = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__create_USCOREobjectResponse(struct soap *soap, const struct ns1__create_USCOREobjectResponse *a)
+{
+	(void)soap; (void)a; /* appease -Wall -Werror */
+	soap_serialize_PointerTostring(soap, &a->result);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__create_USCOREobjectResponse(struct soap *soap, const struct ns1__create_USCOREobjectResponse *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_ns1__create_USCOREobjectResponse);
+	if (soap_out_ns1__create_USCOREobjectResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__create_USCOREobjectResponse(struct soap *soap, const char *tag, int id, const struct ns1__create_USCOREobjectResponse *a, const char *type)
+{
+	if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_ns1__create_USCOREobjectResponse), type))
+		return soap->error;
+	if (soap_out_PointerTostring(soap, "result", -1, &a->result, ""))
+		return soap->error;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ns1__create_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__create_USCOREobjectResponse(struct soap *soap, struct ns1__create_USCOREobjectResponse *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ns1__create_USCOREobjectResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__create_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__create_USCOREobjectResponse(struct soap *soap, const char *tag, struct ns1__create_USCOREobjectResponse *a, const char *type)
+{
+	size_t soap_flag_result = 1;
+	if (soap_element_begin_in(soap, tag, 0, type))
+		return NULL;
+	a = (struct ns1__create_USCOREobjectResponse *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ns1__create_USCOREobjectResponse, sizeof(struct ns1__create_USCOREobjectResponse), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ns1__create_USCOREobjectResponse(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		for (;;)
+		{	soap->error = SOAP_TAG_MISMATCH;
+			if (soap_flag_result && soap->error == SOAP_TAG_MISMATCH)
+				if (soap_in_PointerTostring(soap, "result", &a->result, "xsd:string"))
+				{	soap_flag_result--;
+					continue;
+				}
+			if (soap->error == SOAP_TAG_MISMATCH)
+				soap->error = soap_ignore_element(soap);
+			if (soap->error == SOAP_NO_TAG)
+				break;
+			if (soap->error)
+				return NULL;
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__create_USCOREobjectResponse *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ns1__create_USCOREobjectResponse, 0, sizeof(struct ns1__create_USCOREobjectResponse), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ArrayOfString(struct soap *soap, struct ArrayOfString *a)
+{
+	a->__size = 0;
+	a->__ptr = NULL;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ArrayOfString(struct soap *soap, struct ArrayOfString const*a)
+{
+	int i;
+	if (a->__ptr && !soap_array_reference(soap, a, (struct soap_array*)&a->__ptr, 1, SOAP_TYPE_ArrayOfString))
+		for (i = 0; i < a->__size; i++)
+		{
+			soap_serialize_string(soap, a->__ptr + i);
+		}
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ArrayOfString(struct soap *soap, const struct ArrayOfString *a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, (struct soap_array*)&a->__ptr, 1, tag, SOAP_TYPE_ArrayOfString);
+	if (soap_out_ArrayOfString(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ArrayOfString(struct soap *soap, const char *tag, int id, const struct ArrayOfString *a, const char *type)
+{
+	int i, n = a->__size;
+	char *t = soap_putsize(soap, "xsd:string", a->__size);
+	id = soap_element_id(soap, tag, id, a, (struct soap_array*)&a->__ptr, 1, type, SOAP_TYPE_ArrayOfString);
+	if (id < 0)
+		return soap->error;
+	if (soap_array_begin_out(soap, tag, id, t, NULL))
+		return soap->error;
+	for (i = 0; i < n; i++)
+	{
+		soap->position = 1;
+		soap->positions[0] = i;
+		soap_out_string(soap, "item", -1, &a->__ptr[i], "");
+	}
+	soap->position = 0;
+	return soap_element_end_out(soap, tag);
+}
+
+SOAP_FMAC3 struct ArrayOfString * SOAP_FMAC4 soap_get_ArrayOfString(struct soap *soap, struct ArrayOfString *p, const char *tag, const char *type)
+{
+	if ((p = soap_in_ArrayOfString(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ArrayOfString * SOAP_FMAC4 soap_in_ArrayOfString(struct soap *soap, const char *tag, struct ArrayOfString *a, const char *type)
+{	int i, j;
+	char **p;
+	if (soap_element_begin_in(soap, tag, 1, NULL))
+		return NULL;
+	if (soap_match_array(soap, type))
+	{	soap->error = SOAP_TYPE;
+		return NULL;
+	}
+	a = (struct ArrayOfString *)soap_id_enter(soap, soap->id, a, SOAP_TYPE_ArrayOfString, sizeof(struct ArrayOfString), 0, NULL, NULL, NULL);
+	if (!a)
+		return NULL;
+	soap_default_ArrayOfString(soap, a);
+	if (soap->body && !*soap->href)
+	{
+		a->__size = soap_getsize(soap->arraySize, soap->arrayOffset, &j);
+		if (a->__size >= 0)
+		{	a->__ptr = (char **)soap_malloc(soap, sizeof(char *) * a->__size);
+			for (i = 0; i < a->__size; i++)
+				a->__ptr[i] = NULL;
+			for (i = 0; i < a->__size; i++)
+			{	soap_peek_element(soap);
+				if (soap->position)
+				{	i = soap->positions[0]-j;
+					if (i < 0 || i >= a->__size)
+					{	soap->error = SOAP_IOB;
+						return NULL;
+					}
+				}
+				if (!soap_in_string(soap, NULL, a->__ptr + i, "xsd:string"))
+				{	if (soap->error != SOAP_NO_TAG)
+						return NULL;
+					soap->error = SOAP_OK;
+					break;
+				}
+			}
+		}
+		else
+		{	if (soap_new_block(soap) == NULL)
+				return NULL;
+			for (a->__size = 0; ; a->__size++)
+			{	p = (char **)soap_push_block(soap, NULL, sizeof(char *));
+				if (!p)
+					return NULL;
+				*p = NULL;
+				if (!soap_in_string(soap, NULL, p, "xsd:string"))
+				{	if (soap->error != SOAP_NO_TAG)
+						return NULL;
+					soap->error = SOAP_OK;
+					break;
+				}
+			}
+			soap_pop_block(soap, NULL);
+			a->__ptr = (char **)soap_malloc(soap, soap->blist->size);
+			soap_save_block(soap, NULL, (char*)a->__ptr, 1);
+		}
+		if (soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	else
+	{	a = (struct ArrayOfString *)soap_id_forward(soap, soap->href, (void*)a, 0, SOAP_TYPE_ArrayOfString, 0, sizeof(struct ArrayOfString), 0, NULL);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+#ifndef WITH_NOGLOBAL
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Reason(struct soap *soap, struct SOAP_ENV__Reason *const*a)
+{
+	if (!soap_reference(soap, *a, SOAP_TYPE_SOAP_ENV__Reason))
+		soap_serialize_SOAP_ENV__Reason(soap, *a);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Reason(struct soap *soap, struct SOAP_ENV__Reason *const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_PointerToSOAP_ENV__Reason);
+	if (soap_out_PointerToSOAP_ENV__Reason(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Reason(struct soap *soap, const char *tag, int id, struct SOAP_ENV__Reason *const*a, const char *type)
+{
+	id = soap_element_id(soap, tag, id, *a, NULL, 0, type, SOAP_TYPE_SOAP_ENV__Reason);
+	if (id < 0)
+		return soap->error;
+	return soap_out_SOAP_ENV__Reason(soap, tag, id, *a, type);
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Reason ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Reason(struct soap *soap, struct SOAP_ENV__Reason **p, const char *tag, const char *type)
+{
+	if ((p = soap_in_PointerToSOAP_ENV__Reason(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Reason ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Reason(struct soap *soap, const char *tag, struct SOAP_ENV__Reason **a, const char *type)
+{
+	if (soap_element_begin_in(soap, tag, 1, NULL))
+		return NULL;
+	if (!a)
+		if (!(a = (struct SOAP_ENV__Reason **)soap_malloc(soap, sizeof(struct SOAP_ENV__Reason *))))
+			return NULL;
+	*a = NULL;
+	if (!soap->null && *soap->href != '#')
+	{	soap_revert(soap);
+		if (!(*a = soap_in_SOAP_ENV__Reason(soap, tag, *a, type)))
+			return NULL;
+	}
+	else
+	{	a = (struct SOAP_ENV__Reason **)soap_id_lookup(soap, soap->href, (void**)a, SOAP_TYPE_SOAP_ENV__Reason, sizeof(struct SOAP_ENV__Reason), 0);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Detail(struct soap *soap, struct SOAP_ENV__Detail *const*a)
+{
+	if (!soap_reference(soap, *a, SOAP_TYPE_SOAP_ENV__Detail))
+		soap_serialize_SOAP_ENV__Detail(soap, *a);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Detail(struct soap *soap, struct SOAP_ENV__Detail *const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_PointerToSOAP_ENV__Detail);
+	if (soap_out_PointerToSOAP_ENV__Detail(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Detail(struct soap *soap, const char *tag, int id, struct SOAP_ENV__Detail *const*a, const char *type)
+{
+	id = soap_element_id(soap, tag, id, *a, NULL, 0, type, SOAP_TYPE_SOAP_ENV__Detail);
+	if (id < 0)
+		return soap->error;
+	return soap_out_SOAP_ENV__Detail(soap, tag, id, *a, type);
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Detail ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Detail(struct soap *soap, struct SOAP_ENV__Detail **p, const char *tag, const char *type)
+{
+	if ((p = soap_in_PointerToSOAP_ENV__Detail(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Detail ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Detail(struct soap *soap, const char *tag, struct SOAP_ENV__Detail **a, const char *type)
+{
+	if (soap_element_begin_in(soap, tag, 1, NULL))
+		return NULL;
+	if (!a)
+		if (!(a = (struct SOAP_ENV__Detail **)soap_malloc(soap, sizeof(struct SOAP_ENV__Detail *))))
+			return NULL;
+	*a = NULL;
+	if (!soap->null && *soap->href != '#')
+	{	soap_revert(soap);
+		if (!(*a = soap_in_SOAP_ENV__Detail(soap, tag, *a, type)))
+			return NULL;
+	}
+	else
+	{	a = (struct SOAP_ENV__Detail **)soap_id_lookup(soap, soap->href, (void**)a, SOAP_TYPE_SOAP_ENV__Detail, sizeof(struct SOAP_ENV__Detail), 0);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Code(struct soap *soap, struct SOAP_ENV__Code *const*a)
+{
+	if (!soap_reference(soap, *a, SOAP_TYPE_SOAP_ENV__Code))
+		soap_serialize_SOAP_ENV__Code(soap, *a);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Code(struct soap *soap, struct SOAP_ENV__Code *const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_PointerToSOAP_ENV__Code);
+	if (soap_out_PointerToSOAP_ENV__Code(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Code(struct soap *soap, const char *tag, int id, struct SOAP_ENV__Code *const*a, const char *type)
+{
+	id = soap_element_id(soap, tag, id, *a, NULL, 0, type, SOAP_TYPE_SOAP_ENV__Code);
+	if (id < 0)
+		return soap->error;
+	return soap_out_SOAP_ENV__Code(soap, tag, id, *a, type);
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Code ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Code(struct soap *soap, struct SOAP_ENV__Code **p, const char *tag, const char *type)
+{
+	if ((p = soap_in_PointerToSOAP_ENV__Code(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct SOAP_ENV__Code ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Code(struct soap *soap, const char *tag, struct SOAP_ENV__Code **a, const char *type)
+{
+	if (soap_element_begin_in(soap, tag, 1, NULL))
+		return NULL;
+	if (!a)
+		if (!(a = (struct SOAP_ENV__Code **)soap_malloc(soap, sizeof(struct SOAP_ENV__Code *))))
+			return NULL;
+	*a = NULL;
+	if (!soap->null && *soap->href != '#')
+	{	soap_revert(soap);
+		if (!(*a = soap_in_SOAP_ENV__Code(soap, tag, *a, type)))
+			return NULL;
+	}
+	else
+	{	a = (struct SOAP_ENV__Code **)soap_id_lookup(soap, soap->href, (void**)a, SOAP_TYPE_SOAP_ENV__Code, sizeof(struct SOAP_ENV__Code), 0);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+#endif
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerTons1__stat_USCOREobjectResponse(struct soap *soap, struct ns1__stat_USCOREobjectResponse *const*a)
+{
+	if (!soap_reference(soap, *a, SOAP_TYPE_ns1__stat_USCOREobjectResponse))
+		soap_serialize_ns1__stat_USCOREobjectResponse(soap, *a);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerTons1__stat_USCOREobjectResponse(struct soap *soap, struct ns1__stat_USCOREobjectResponse *const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_PointerTons1__stat_USCOREobjectResponse);
+	if (soap_out_PointerTons1__stat_USCOREobjectResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerTons1__stat_USCOREobjectResponse(struct soap *soap, const char *tag, int id, struct ns1__stat_USCOREobjectResponse *const*a, const char *type)
+{
+	id = soap_element_id(soap, tag, id, *a, NULL, 0, type, SOAP_TYPE_ns1__stat_USCOREobjectResponse);
+	if (id < 0)
+		return soap->error;
+	return soap_out_ns1__stat_USCOREobjectResponse(soap, tag, id, *a, type);
+}
+
+SOAP_FMAC3 struct ns1__stat_USCOREobjectResponse ** SOAP_FMAC4 soap_get_PointerTons1__stat_USCOREobjectResponse(struct soap *soap, struct ns1__stat_USCOREobjectResponse **p, const char *tag, const char *type)
+{
+	if ((p = soap_in_PointerTons1__stat_USCOREobjectResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__stat_USCOREobjectResponse ** SOAP_FMAC4 soap_in_PointerTons1__stat_USCOREobjectResponse(struct soap *soap, const char *tag, struct ns1__stat_USCOREobjectResponse **a, const char *type)
+{
+	if (soap_element_begin_in(soap, tag, 1, NULL))
+		return NULL;
+	if (!a)
+		if (!(a = (struct ns1__stat_USCOREobjectResponse **)soap_malloc(soap, sizeof(struct ns1__stat_USCOREobjectResponse *))))
+			return NULL;
+	*a = NULL;
+	if (!soap->null && *soap->href != '#')
+	{	soap_revert(soap);
+		if (!(*a = soap_in_ns1__stat_USCOREobjectResponse(soap, tag, *a, type)))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__stat_USCOREobjectResponse **)soap_id_lookup(soap, soap->href, (void**)a, SOAP_TYPE_ns1__stat_USCOREobjectResponse, sizeof(struct ns1__stat_USCOREobjectResponse), 0);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerTons1__find_USCOREinstancesResponse(struct soap *soap, struct ns1__find_USCOREinstancesResponse *const*a)
+{
+	if (!soap_reference(soap, *a, SOAP_TYPE_ns1__find_USCOREinstancesResponse))
+		soap_serialize_ns1__find_USCOREinstancesResponse(soap, *a);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerTons1__find_USCOREinstancesResponse(struct soap *soap, struct ns1__find_USCOREinstancesResponse *const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_PointerTons1__find_USCOREinstancesResponse);
+	if (soap_out_PointerTons1__find_USCOREinstancesResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerTons1__find_USCOREinstancesResponse(struct soap *soap, const char *tag, int id, struct ns1__find_USCOREinstancesResponse *const*a, const char *type)
+{
+	id = soap_element_id(soap, tag, id, *a, NULL, 0, type, SOAP_TYPE_ns1__find_USCOREinstancesResponse);
+	if (id < 0)
+		return soap->error;
+	return soap_out_ns1__find_USCOREinstancesResponse(soap, tag, id, *a, type);
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREinstancesResponse ** SOAP_FMAC4 soap_get_PointerTons1__find_USCOREinstancesResponse(struct soap *soap, struct ns1__find_USCOREinstancesResponse **p, const char *tag, const char *type)
+{
+	if ((p = soap_in_PointerTons1__find_USCOREinstancesResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREinstancesResponse ** SOAP_FMAC4 soap_in_PointerTons1__find_USCOREinstancesResponse(struct soap *soap, const char *tag, struct ns1__find_USCOREinstancesResponse **a, const char *type)
+{
+	if (soap_element_begin_in(soap, tag, 1, NULL))
+		return NULL;
+	if (!a)
+		if (!(a = (struct ns1__find_USCOREinstancesResponse **)soap_malloc(soap, sizeof(struct ns1__find_USCOREinstancesResponse *))))
+			return NULL;
+	*a = NULL;
+	if (!soap->null && *soap->href != '#')
+	{	soap_revert(soap);
+		if (!(*a = soap_in_ns1__find_USCOREinstancesResponse(soap, tag, *a, type)))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__find_USCOREinstancesResponse **)soap_id_lookup(soap, soap->href, (void**)a, SOAP_TYPE_ns1__find_USCOREinstancesResponse, sizeof(struct ns1__find_USCOREinstancesResponse), 0);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerTons1__find_USCOREobjectsResponse(struct soap *soap, struct ns1__find_USCOREobjectsResponse *const*a)
+{
+	if (!soap_reference(soap, *a, SOAP_TYPE_ns1__find_USCOREobjectsResponse))
+		soap_serialize_ns1__find_USCOREobjectsResponse(soap, *a);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerTons1__find_USCOREobjectsResponse(struct soap *soap, struct ns1__find_USCOREobjectsResponse *const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_PointerTons1__find_USCOREobjectsResponse);
+	if (soap_out_PointerTons1__find_USCOREobjectsResponse(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerTons1__find_USCOREobjectsResponse(struct soap *soap, const char *tag, int id, struct ns1__find_USCOREobjectsResponse *const*a, const char *type)
+{
+	id = soap_element_id(soap, tag, id, *a, NULL, 0, type, SOAP_TYPE_ns1__find_USCOREobjectsResponse);
+	if (id < 0)
+		return soap->error;
+	return soap_out_ns1__find_USCOREobjectsResponse(soap, tag, id, *a, type);
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREobjectsResponse ** SOAP_FMAC4 soap_get_PointerTons1__find_USCOREobjectsResponse(struct soap *soap, struct ns1__find_USCOREobjectsResponse **p, const char *tag, const char *type)
+{
+	if ((p = soap_in_PointerTons1__find_USCOREobjectsResponse(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ns1__find_USCOREobjectsResponse ** SOAP_FMAC4 soap_in_PointerTons1__find_USCOREobjectsResponse(struct soap *soap, const char *tag, struct ns1__find_USCOREobjectsResponse **a, const char *type)
+{
+	if (soap_element_begin_in(soap, tag, 1, NULL))
+		return NULL;
+	if (!a)
+		if (!(a = (struct ns1__find_USCOREobjectsResponse **)soap_malloc(soap, sizeof(struct ns1__find_USCOREobjectsResponse *))))
+			return NULL;
+	*a = NULL;
+	if (!soap->null && *soap->href != '#')
+	{	soap_revert(soap);
+		if (!(*a = soap_in_ns1__find_USCOREobjectsResponse(soap, tag, *a, type)))
+			return NULL;
+	}
+	else
+	{	a = (struct ns1__find_USCOREobjectsResponse **)soap_id_lookup(soap, soap->href, (void**)a, SOAP_TYPE_ns1__find_USCOREobjectsResponse, sizeof(struct ns1__find_USCOREobjectsResponse), 0);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToArrayOfString(struct soap *soap, struct ArrayOfString *const*a)
+{
+	if (*a)
+		soap_serialize_ArrayOfString(soap, *a);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToArrayOfString(struct soap *soap, struct ArrayOfString *const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_PointerToArrayOfString);
+	if (soap_out_PointerToArrayOfString(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToArrayOfString(struct soap *soap, const char *tag, int id, struct ArrayOfString *const*a, const char *type)
+{
+	id = soap_element_id(soap, tag, id, *a, (struct soap_array*)&(*a)->__ptr, 1, type, SOAP_TYPE_ArrayOfString);
+	if (id < 0)
+		return soap->error;
+	return soap_out_ArrayOfString(soap, tag, id, *a, type);
+}
+
+SOAP_FMAC3 struct ArrayOfString ** SOAP_FMAC4 soap_get_PointerToArrayOfString(struct soap *soap, struct ArrayOfString **p, const char *tag, const char *type)
+{
+	if ((p = soap_in_PointerToArrayOfString(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 struct ArrayOfString ** SOAP_FMAC4 soap_in_PointerToArrayOfString(struct soap *soap, const char *tag, struct ArrayOfString **a, const char *type)
+{
+	if (soap_element_begin_in(soap, tag, 1, NULL))
+		return NULL;
+	if (!a)
+		if (!(a = (struct ArrayOfString **)soap_malloc(soap, sizeof(struct ArrayOfString *))))
+			return NULL;
+	*a = NULL;
+	if (!soap->null && *soap->href != '#')
+	{	soap_revert(soap);
+		if (!(*a = soap_in_ArrayOfString(soap, tag, *a, type)))
+			return NULL;
+	}
+	else
+	{	a = (struct ArrayOfString **)soap_id_lookup(soap, soap->href, (void**)a, SOAP_TYPE_ArrayOfString, sizeof(struct ArrayOfString), 0);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToint(struct soap *soap, int *const*a)
+{
+	soap_reference(soap, *a, SOAP_TYPE_int);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToint(struct soap *soap, int *const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_PointerToint);
+	if (soap_out_PointerToint(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToint(struct soap *soap, const char *tag, int id, int *const*a, const char *type)
+{
+	id = soap_element_id(soap, tag, id, *a, NULL, 0, type, SOAP_TYPE_int);
+	if (id < 0)
+		return soap->error;
+	return soap_out_int(soap, tag, id, *a, type);
+}
+
+SOAP_FMAC3 int ** SOAP_FMAC4 soap_get_PointerToint(struct soap *soap, int **p, const char *tag, const char *type)
+{
+	if ((p = soap_in_PointerToint(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 int ** SOAP_FMAC4 soap_in_PointerToint(struct soap *soap, const char *tag, int **a, const char *type)
+{
+	if (soap_element_begin_in(soap, tag, 1, NULL))
+		return NULL;
+	if (!a)
+		if (!(a = (int **)soap_malloc(soap, sizeof(int *))))
+			return NULL;
+	*a = NULL;
+	if (!soap->null && *soap->href != '#')
+	{	soap_revert(soap);
+		if (!(*a = soap_in_int(soap, tag, *a, type)))
+			return NULL;
+	}
+	else
+	{	a = (int **)soap_id_lookup(soap, soap->href, (void**)a, SOAP_TYPE_int, sizeof(int), 0);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerTostring(struct soap *soap, char **const*a)
+{
+	if (!soap_reference(soap, *a, SOAP_TYPE_string))
+		soap_serialize_string(soap, *a);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerTostring(struct soap *soap, char **const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_PointerTostring);
+	if (soap_out_PointerTostring(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerTostring(struct soap *soap, const char *tag, int id, char **const*a, const char *type)
+{
+	id = soap_element_id(soap, tag, id, *a, NULL, 0, type, SOAP_TYPE_string);
+	if (id < 0)
+		return soap->error;
+	return soap_out_string(soap, tag, id, *a, type);
+}
+
+SOAP_FMAC3 char *** SOAP_FMAC4 soap_get_PointerTostring(struct soap *soap, char ***p, const char *tag, const char *type)
+{
+	if ((p = soap_in_PointerTostring(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 char *** SOAP_FMAC4 soap_in_PointerTostring(struct soap *soap, const char *tag, char ***a, const char *type)
+{
+	if (soap_element_begin_in(soap, tag, 1, NULL))
+		return NULL;
+	if (!a)
+		if (!(a = (char ***)soap_malloc(soap, sizeof(char **))))
+			return NULL;
+	*a = NULL;
+	if (!soap->null && *soap->href != '#')
+	{	soap_revert(soap);
+		if (!(*a = soap_in_string(soap, tag, *a, type)))
+			return NULL;
+	}
+	else
+	{	a = (char ***)soap_id_lookup(soap, soap->href, (void**)a, SOAP_TYPE_string, sizeof(char *), 1);
+		if (soap->body && soap_element_end_in(soap, tag))
+			return NULL;
+	}
+	return a;
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default__QName(struct soap *soap, char **a)
+{	soap_default_string(soap, a);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize__QName(struct soap *soap, char *const*a)
+{	soap_serialize_string(soap, a);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put__QName(struct soap *soap, char *const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE__QName);
+	if (soap_out__QName(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out__QName(struct soap *soap, const char *tag, int id, char *const*a, const char *type)
+{
+	return soap_outstring(soap, tag, id, a, type, SOAP_TYPE__QName);
+}
+
+SOAP_FMAC3 char ** SOAP_FMAC4 soap_get__QName(struct soap *soap, char **p, const char *tag, const char *type)
+{
+	if ((p = soap_in__QName(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 char * * SOAP_FMAC4 soap_in__QName(struct soap *soap, const char *tag, char **a, const char *type)
+{
+	return soap_instring(soap, tag, a, type, SOAP_TYPE__QName, 2, -1, -1);
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_string(struct soap *soap, char **a)
+{	(void)soap; /* appease -Wall -Werror */
+#ifdef SOAP_DEFAULT_string
+	*a = SOAP_DEFAULT_string;
+#else
+	*a = (char *)0;
+#endif
+}
+
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_string(struct soap *soap, char *const*a)
+{
+	soap_reference(soap, *a, SOAP_TYPE_string);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_string(struct soap *soap, char *const*a, const char *tag, const char *type)
+{
+	register int id = soap_embed(soap, (void*)a, NULL, 0, tag, SOAP_TYPE_string);
+	if (soap_out_string(soap, tag, id, a, type))
+		return soap->error;
+	return soap_putindependent(soap);
+}
+
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_string(struct soap *soap, const char *tag, int id, char *const*a, const char *type)
+{
+	return soap_outstring(soap, tag, id, a, type, SOAP_TYPE_string);
+}
+
+SOAP_FMAC3 char ** SOAP_FMAC4 soap_get_string(struct soap *soap, char **p, const char *tag, const char *type)
+{
+	if ((p = soap_in_string(soap, tag, p, type)))
+		if (soap_getindependent(soap))
+			return NULL;
+	return p;
+}
+
+SOAP_FMAC3 char * * SOAP_FMAC4 soap_in_string(struct soap *soap, const char *tag, char **a, const char *type)
+{
+	return soap_instring(soap, tag, a, type, SOAP_TYPE_string, 1, -1, -1);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/* End of soapC.c */
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapClient.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapClient.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapClient.c	(revision 22322)
@@ -0,0 +1,587 @@
+/* soapClient.c
+   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
+   This part of the software is released under one of the following licenses:
+   GPL, the gSOAP public license, or Genivia's license for commercial use.
+*/
+#include "soapH.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SOAP_SOURCE_STAMP("@(#) soapClient.c ver 2.7.11 2008-09-13 01:14:39 GMT")
+
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__create_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *volume, char **result)
+{	struct ns1__create_USCOREobject soap_tmp_ns1__create_USCOREobject;
+	struct ns1__create_USCOREobjectResponse *soap_tmp_ns1__create_USCOREobjectResponse;
+	if (!soap_endpoint)
+		soap_endpoint = "http://localhost:80/nebulous";
+	if (!soap_action)
+		soap_action = "urn:Nebulous/Server/SOAP#create_object";
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	soap_tmp_ns1__create_USCOREobject.key = key;
+	soap_tmp_ns1__create_USCOREobject.volume = volume;
+	soap_begin(soap);
+	soap_serializeheader(soap);
+	soap_serialize_ns1__create_USCOREobject(soap, &soap_tmp_ns1__create_USCOREobject);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__create_USCOREobject(soap, &soap_tmp_ns1__create_USCOREobject, "ns1:create_object", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	}
+	if (soap_end_count(soap))
+		return soap->error;
+	if (soap_connect(soap, soap_endpoint, soap_action)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__create_USCOREobject(soap, &soap_tmp_ns1__create_USCOREobject, "ns1:create_object", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap_closesock(soap);
+	if (!result)
+		return soap_closesock(soap);
+	*result = NULL;
+	if (soap_begin_recv(soap)
+	 || soap_envelope_begin_in(soap)
+	 || soap_recv_header(soap)
+	 || soap_body_begin_in(soap))
+		return soap_closesock(soap);
+	soap_tmp_ns1__create_USCOREobjectResponse = soap_get_ns1__create_USCOREobjectResponse(soap, NULL, "ns1:create_objectResponse", "");
+	if (soap->error)
+	{	if (soap->error == SOAP_TAG_MISMATCH && soap->level == 2)
+			return soap_recv_fault(soap);
+		return soap_closesock(soap);
+	}
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap_closesock(soap);
+	if (result && soap_tmp_ns1__create_USCOREobjectResponse->result)
+		*result = *soap_tmp_ns1__create_USCOREobjectResponse->result;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__rename_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *newkey, char **result)
+{	struct ns1__rename_USCOREobject soap_tmp_ns1__rename_USCOREobject;
+	struct ns1__rename_USCOREobjectResponse *soap_tmp_ns1__rename_USCOREobjectResponse;
+	if (!soap_endpoint)
+		soap_endpoint = "http://localhost:80/nebulous";
+	if (!soap_action)
+		soap_action = "urn:Nebulous/Server/SOAP#create_object";
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	soap_tmp_ns1__rename_USCOREobject.key = key;
+	soap_tmp_ns1__rename_USCOREobject.newkey = newkey;
+	soap_begin(soap);
+	soap_serializeheader(soap);
+	soap_serialize_ns1__rename_USCOREobject(soap, &soap_tmp_ns1__rename_USCOREobject);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__rename_USCOREobject(soap, &soap_tmp_ns1__rename_USCOREobject, "ns1:rename_object", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	}
+	if (soap_end_count(soap))
+		return soap->error;
+	if (soap_connect(soap, soap_endpoint, soap_action)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__rename_USCOREobject(soap, &soap_tmp_ns1__rename_USCOREobject, "ns1:rename_object", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap_closesock(soap);
+	if (!result)
+		return soap_closesock(soap);
+	*result = NULL;
+	if (soap_begin_recv(soap)
+	 || soap_envelope_begin_in(soap)
+	 || soap_recv_header(soap)
+	 || soap_body_begin_in(soap))
+		return soap_closesock(soap);
+	soap_tmp_ns1__rename_USCOREobjectResponse = soap_get_ns1__rename_USCOREobjectResponse(soap, NULL, "ns1:rename_objectResponse", "");
+	if (soap->error)
+	{	if (soap->error == SOAP_TAG_MISMATCH && soap->level == 2)
+			return soap_recv_fault(soap);
+		return soap_closesock(soap);
+	}
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap_closesock(soap);
+	if (result && soap_tmp_ns1__rename_USCOREobjectResponse->result)
+		*result = *soap_tmp_ns1__rename_USCOREobjectResponse->result;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__rename_USCOREobject_(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *newkey, char **result)
+{	struct ns1__rename_USCOREobject_ soap_tmp_ns1__rename_USCOREobject_;
+	struct ns1__rename_USCOREobject_Response *soap_tmp_ns1__rename_USCOREobject_Response;
+	if (!soap_endpoint)
+		soap_endpoint = "http://localhost:80/nebulous";
+	if (!soap_action)
+		soap_action = "urn:Nebulous/Server/SOAP#create_object";
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	soap_tmp_ns1__rename_USCOREobject_.key = key;
+	soap_tmp_ns1__rename_USCOREobject_.newkey = newkey;
+	soap_begin(soap);
+	soap_serializeheader(soap);
+	soap_serialize_ns1__rename_USCOREobject_(soap, &soap_tmp_ns1__rename_USCOREobject_);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__rename_USCOREobject_(soap, &soap_tmp_ns1__rename_USCOREobject_, "ns1:rename_object", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	}
+	if (soap_end_count(soap))
+		return soap->error;
+	if (soap_connect(soap, soap_endpoint, soap_action)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__rename_USCOREobject_(soap, &soap_tmp_ns1__rename_USCOREobject_, "ns1:rename_object", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap_closesock(soap);
+	if (!result)
+		return soap_closesock(soap);
+	*result = NULL;
+	if (soap_begin_recv(soap)
+	 || soap_envelope_begin_in(soap)
+	 || soap_recv_header(soap)
+	 || soap_body_begin_in(soap))
+		return soap_closesock(soap);
+	soap_tmp_ns1__rename_USCOREobject_Response = soap_get_ns1__rename_USCOREobject_Response(soap, NULL, "ns1:rename_object-Response", "");
+	if (soap->error)
+	{	if (soap->error == SOAP_TAG_MISMATCH && soap->level == 2)
+			return soap_recv_fault(soap);
+		return soap_closesock(soap);
+	}
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap_closesock(soap);
+	if (result && soap_tmp_ns1__rename_USCOREobject_Response->result)
+		*result = *soap_tmp_ns1__rename_USCOREobject_Response->result;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__replicate_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *volume, char **result)
+{	struct ns1__replicate_USCOREobject soap_tmp_ns1__replicate_USCOREobject;
+	struct ns1__replicate_USCOREobjectResponse *soap_tmp_ns1__replicate_USCOREobjectResponse;
+	if (!soap_endpoint)
+		soap_endpoint = "http://localhost:80/nebulous";
+	if (!soap_action)
+		soap_action = "urn:Nebulous/Server/SOAP#replicate_object";
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	soap_tmp_ns1__replicate_USCOREobject.key = key;
+	soap_tmp_ns1__replicate_USCOREobject.volume = volume;
+	soap_begin(soap);
+	soap_serializeheader(soap);
+	soap_serialize_ns1__replicate_USCOREobject(soap, &soap_tmp_ns1__replicate_USCOREobject);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__replicate_USCOREobject(soap, &soap_tmp_ns1__replicate_USCOREobject, "ns1:replicate_object", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	}
+	if (soap_end_count(soap))
+		return soap->error;
+	if (soap_connect(soap, soap_endpoint, soap_action)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__replicate_USCOREobject(soap, &soap_tmp_ns1__replicate_USCOREobject, "ns1:replicate_object", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap_closesock(soap);
+	if (!result)
+		return soap_closesock(soap);
+	*result = NULL;
+	if (soap_begin_recv(soap)
+	 || soap_envelope_begin_in(soap)
+	 || soap_recv_header(soap)
+	 || soap_body_begin_in(soap))
+		return soap_closesock(soap);
+	soap_tmp_ns1__replicate_USCOREobjectResponse = soap_get_ns1__replicate_USCOREobjectResponse(soap, NULL, "ns1:replicate_objectResponse", "");
+	if (soap->error)
+	{	if (soap->error == SOAP_TAG_MISMATCH && soap->level == 2)
+			return soap_recv_fault(soap);
+		return soap_closesock(soap);
+	}
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap_closesock(soap);
+	if (result && soap_tmp_ns1__replicate_USCOREobjectResponse->result)
+		*result = *soap_tmp_ns1__replicate_USCOREobjectResponse->result;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__lock_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *type, int *result)
+{	struct ns1__lock_USCOREobject soap_tmp_ns1__lock_USCOREobject;
+	struct ns1__lock_USCOREobjectResponse *soap_tmp_ns1__lock_USCOREobjectResponse;
+	if (!soap_endpoint)
+		soap_endpoint = "http://localhost:80/nebulous";
+	if (!soap_action)
+		soap_action = "urn:Nebulous/Server/SOAP#lock_object";
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	soap_tmp_ns1__lock_USCOREobject.key = key;
+	soap_tmp_ns1__lock_USCOREobject.type = type;
+	soap_begin(soap);
+	soap_serializeheader(soap);
+	soap_serialize_ns1__lock_USCOREobject(soap, &soap_tmp_ns1__lock_USCOREobject);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__lock_USCOREobject(soap, &soap_tmp_ns1__lock_USCOREobject, "ns1:lock_object", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	}
+	if (soap_end_count(soap))
+		return soap->error;
+	if (soap_connect(soap, soap_endpoint, soap_action)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__lock_USCOREobject(soap, &soap_tmp_ns1__lock_USCOREobject, "ns1:lock_object", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap_closesock(soap);
+	if (!result)
+		return soap_closesock(soap);
+	soap_default_int(soap, result);
+	if (soap_begin_recv(soap)
+	 || soap_envelope_begin_in(soap)
+	 || soap_recv_header(soap)
+	 || soap_body_begin_in(soap))
+		return soap_closesock(soap);
+	soap_tmp_ns1__lock_USCOREobjectResponse = soap_get_ns1__lock_USCOREobjectResponse(soap, NULL, "ns1:lock_objectResponse", "");
+	if (soap->error)
+	{	if (soap->error == SOAP_TAG_MISMATCH && soap->level == 2)
+			return soap_recv_fault(soap);
+		return soap_closesock(soap);
+	}
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap_closesock(soap);
+	if (result && soap_tmp_ns1__lock_USCOREobjectResponse->result)
+		*result = *soap_tmp_ns1__lock_USCOREobjectResponse->result;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__unlock_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *type, int *result)
+{	struct ns1__unlock_USCOREobject soap_tmp_ns1__unlock_USCOREobject;
+	struct ns1__unlock_USCOREobjectResponse *soap_tmp_ns1__unlock_USCOREobjectResponse;
+	if (!soap_endpoint)
+		soap_endpoint = "http://localhost:80/nebulous";
+	if (!soap_action)
+		soap_action = "urn:Nebulous/Server/SOAP#unlock_object";
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	soap_tmp_ns1__unlock_USCOREobject.key = key;
+	soap_tmp_ns1__unlock_USCOREobject.type = type;
+	soap_begin(soap);
+	soap_serializeheader(soap);
+	soap_serialize_ns1__unlock_USCOREobject(soap, &soap_tmp_ns1__unlock_USCOREobject);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__unlock_USCOREobject(soap, &soap_tmp_ns1__unlock_USCOREobject, "ns1:unlock_object", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	}
+	if (soap_end_count(soap))
+		return soap->error;
+	if (soap_connect(soap, soap_endpoint, soap_action)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__unlock_USCOREobject(soap, &soap_tmp_ns1__unlock_USCOREobject, "ns1:unlock_object", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap_closesock(soap);
+	if (!result)
+		return soap_closesock(soap);
+	soap_default_int(soap, result);
+	if (soap_begin_recv(soap)
+	 || soap_envelope_begin_in(soap)
+	 || soap_recv_header(soap)
+	 || soap_body_begin_in(soap))
+		return soap_closesock(soap);
+	soap_tmp_ns1__unlock_USCOREobjectResponse = soap_get_ns1__unlock_USCOREobjectResponse(soap, NULL, "ns1:unlock_objectResponse", "");
+	if (soap->error)
+	{	if (soap->error == SOAP_TAG_MISMATCH && soap->level == 2)
+			return soap_recv_fault(soap);
+		return soap_closesock(soap);
+	}
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap_closesock(soap);
+	if (result && soap_tmp_ns1__unlock_USCOREobjectResponse->result)
+		*result = *soap_tmp_ns1__unlock_USCOREobjectResponse->result;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__find_USCOREobjects(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *pattern, struct ns1__find_USCOREobjectsResponse *_param_1)
+{	struct ns1__find_USCOREobjects soap_tmp_ns1__find_USCOREobjects;
+	if (!soap_endpoint)
+		soap_endpoint = "http://localhost:80/nebulous";
+	if (!soap_action)
+		soap_action = "urn:Nebulous/Server/SOAP#find_objects";
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	soap_tmp_ns1__find_USCOREobjects.pattern = pattern;
+	soap_begin(soap);
+	soap_serializeheader(soap);
+	soap_serialize_ns1__find_USCOREobjects(soap, &soap_tmp_ns1__find_USCOREobjects);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__find_USCOREobjects(soap, &soap_tmp_ns1__find_USCOREobjects, "ns1:find_objects", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	}
+	if (soap_end_count(soap))
+		return soap->error;
+	if (soap_connect(soap, soap_endpoint, soap_action)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__find_USCOREobjects(soap, &soap_tmp_ns1__find_USCOREobjects, "ns1:find_objects", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap_closesock(soap);
+	if (!_param_1)
+		return soap_closesock(soap);
+	soap_default_ns1__find_USCOREobjectsResponse(soap, _param_1);
+	if (soap_begin_recv(soap)
+	 || soap_envelope_begin_in(soap)
+	 || soap_recv_header(soap)
+	 || soap_body_begin_in(soap))
+		return soap_closesock(soap);
+	soap_get_ns1__find_USCOREobjectsResponse(soap, _param_1, "ns1:find_objectsResponse", "");
+	if (soap->error)
+	{	if (soap->error == SOAP_TAG_MISMATCH && soap->level == 2)
+			return soap_recv_fault(soap);
+		return soap_closesock(soap);
+	}
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap_closesock(soap);
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__find_USCOREinstances(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *volume, struct ns1__find_USCOREinstancesResponse *_param_2)
+{	struct ns1__find_USCOREinstances soap_tmp_ns1__find_USCOREinstances;
+	if (!soap_endpoint)
+		soap_endpoint = "http://localhost:80/nebulous";
+	if (!soap_action)
+		soap_action = "urn:Nebulous/Server/SOAP#find_instances";
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	soap_tmp_ns1__find_USCOREinstances.key = key;
+	soap_tmp_ns1__find_USCOREinstances.volume = volume;
+	soap_begin(soap);
+	soap_serializeheader(soap);
+	soap_serialize_ns1__find_USCOREinstances(soap, &soap_tmp_ns1__find_USCOREinstances);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__find_USCOREinstances(soap, &soap_tmp_ns1__find_USCOREinstances, "ns1:find_instances", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	}
+	if (soap_end_count(soap))
+		return soap->error;
+	if (soap_connect(soap, soap_endpoint, soap_action)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__find_USCOREinstances(soap, &soap_tmp_ns1__find_USCOREinstances, "ns1:find_instances", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap_closesock(soap);
+	if (!_param_2)
+		return soap_closesock(soap);
+	soap_default_ns1__find_USCOREinstancesResponse(soap, _param_2);
+	if (soap_begin_recv(soap)
+	 || soap_envelope_begin_in(soap)
+	 || soap_recv_header(soap)
+	 || soap_body_begin_in(soap))
+		return soap_closesock(soap);
+	soap_get_ns1__find_USCOREinstancesResponse(soap, _param_2, "ns1:find_instancesResponse", "");
+	if (soap->error)
+	{	if (soap->error == SOAP_TAG_MISMATCH && soap->level == 2)
+			return soap_recv_fault(soap);
+		return soap_closesock(soap);
+	}
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap_closesock(soap);
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__delete_USCOREinstance(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *uri, int *result)
+{	struct ns1__delete_USCOREinstance soap_tmp_ns1__delete_USCOREinstance;
+	struct ns1__delete_USCOREinstanceResponse *soap_tmp_ns1__delete_USCOREinstanceResponse;
+	if (!soap_endpoint)
+		soap_endpoint = "http://localhost:80/nebulous";
+	if (!soap_action)
+		soap_action = "urn:Nebulous/Server/SOAP#delete_instance";
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	soap_tmp_ns1__delete_USCOREinstance.uri = uri;
+	soap_begin(soap);
+	soap_serializeheader(soap);
+	soap_serialize_ns1__delete_USCOREinstance(soap, &soap_tmp_ns1__delete_USCOREinstance);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__delete_USCOREinstance(soap, &soap_tmp_ns1__delete_USCOREinstance, "ns1:delete_instance", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	}
+	if (soap_end_count(soap))
+		return soap->error;
+	if (soap_connect(soap, soap_endpoint, soap_action)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__delete_USCOREinstance(soap, &soap_tmp_ns1__delete_USCOREinstance, "ns1:delete_instance", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap_closesock(soap);
+	if (!result)
+		return soap_closesock(soap);
+	soap_default_int(soap, result);
+	if (soap_begin_recv(soap)
+	 || soap_envelope_begin_in(soap)
+	 || soap_recv_header(soap)
+	 || soap_body_begin_in(soap))
+		return soap_closesock(soap);
+	soap_tmp_ns1__delete_USCOREinstanceResponse = soap_get_ns1__delete_USCOREinstanceResponse(soap, NULL, "ns1:delete_instanceResponse", "");
+	if (soap->error)
+	{	if (soap->error == SOAP_TAG_MISMATCH && soap->level == 2)
+			return soap_recv_fault(soap);
+		return soap_closesock(soap);
+	}
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap_closesock(soap);
+	if (result && soap_tmp_ns1__delete_USCOREinstanceResponse->result)
+		*result = *soap_tmp_ns1__delete_USCOREinstanceResponse->result;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__stat_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, struct ns1__stat_USCOREobjectResponse *_param_3)
+{	struct ns1__stat_USCOREobject soap_tmp_ns1__stat_USCOREobject;
+	if (!soap_endpoint)
+		soap_endpoint = "http://localhost:80/nebulous";
+	if (!soap_action)
+		soap_action = "urn:Nebulous/Server/SOAP#stat_object";
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	soap_tmp_ns1__stat_USCOREobject.key = key;
+	soap_begin(soap);
+	soap_serializeheader(soap);
+	soap_serialize_ns1__stat_USCOREobject(soap, &soap_tmp_ns1__stat_USCOREobject);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__stat_USCOREobject(soap, &soap_tmp_ns1__stat_USCOREobject, "ns1:stat_object", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	}
+	if (soap_end_count(soap))
+		return soap->error;
+	if (soap_connect(soap, soap_endpoint, soap_action)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__stat_USCOREobject(soap, &soap_tmp_ns1__stat_USCOREobject, "ns1:stat_object", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap_closesock(soap);
+	if (!_param_3)
+		return soap_closesock(soap);
+	soap_default_ns1__stat_USCOREobjectResponse(soap, _param_3);
+	if (soap_begin_recv(soap)
+	 || soap_envelope_begin_in(soap)
+	 || soap_recv_header(soap)
+	 || soap_body_begin_in(soap))
+		return soap_closesock(soap);
+	soap_get_ns1__stat_USCOREobjectResponse(soap, _param_3, "ns1:stat_objectResponse", "");
+	if (soap->error)
+	{	if (soap->error == SOAP_TAG_MISMATCH && soap->level == 2)
+			return soap_recv_fault(soap);
+		return soap_closesock(soap);
+	}
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap_closesock(soap);
+	return soap_closesock(soap);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/* End of soapClient.c */
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapClientLib.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapClientLib.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapClientLib.c	(revision 22322)
@@ -0,0 +1,14 @@
+/* soapClientLib.c
+   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
+   This part of the software is released under one of the following licenses:
+   GPL, the gSOAP public license, or Genivia's license for commercial use.
+*/
+#ifndef WITH_NOGLOBAL
+#define WITH_NOGLOBAL
+#endif
+#define SOAP_FMAC3 static
+#include "soapC.c"
+#include "soapClient.c"
+
+/* End of soapClientLib.c */
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapH.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapH.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapH.h	(revision 22322)
@@ -0,0 +1,440 @@
+/* soapH.h
+   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
+   This part of the software is released under one of the following licenses:
+   GPL, the gSOAP public license, or Genivia's license for commercial use.
+*/
+
+#ifndef soapH_H
+#define soapH_H
+#include "soapStub.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifndef WITH_NOIDREF
+SOAP_FMAC3 void SOAP_FMAC4 soap_markelement(struct soap*, const void*, int);
+SOAP_FMAC3 int SOAP_FMAC4 soap_putelement(struct soap*, const void*, const char*, int, int);
+SOAP_FMAC3 void *SOAP_FMAC4 soap_getelement(struct soap*, int*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_putindependent(struct soap*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_getindependent(struct soap*);
+#endif
+SOAP_FMAC3 int SOAP_FMAC4 soap_ignore_element(struct soap*);
+
+#ifndef SOAP_TYPE_byte
+#define SOAP_TYPE_byte (3)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_byte(struct soap*, char *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_byte(struct soap*, const char *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_byte(struct soap*, const char*, int, const char *, const char*);
+SOAP_FMAC3 char * SOAP_FMAC4 soap_get_byte(struct soap*, char *, const char*, const char*);
+SOAP_FMAC3 char * SOAP_FMAC4 soap_in_byte(struct soap*, const char*, char *, const char*);
+
+#ifndef SOAP_TYPE_int
+#define SOAP_TYPE_int (1)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_int(struct soap*, int *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_int(struct soap*, const int *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_int(struct soap*, const char*, int, const int *, const char*);
+SOAP_FMAC3 int * SOAP_FMAC4 soap_get_int(struct soap*, int *, const char*, const char*);
+SOAP_FMAC3 int * SOAP_FMAC4 soap_in_int(struct soap*, const char*, int *, const char*);
+
+#ifndef WITH_NOGLOBAL
+
+#ifndef SOAP_TYPE_SOAP_ENV__Fault
+#define SOAP_TYPE_SOAP_ENV__Fault (51)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Fault(struct soap*, struct SOAP_ENV__Fault *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Fault(struct soap*, const struct SOAP_ENV__Fault *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Fault(struct soap*, const struct SOAP_ENV__Fault *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Fault(struct soap*, const char*, int, const struct SOAP_ENV__Fault *, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Fault * SOAP_FMAC4 soap_get_SOAP_ENV__Fault(struct soap*, struct SOAP_ENV__Fault *, const char*, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Fault * SOAP_FMAC4 soap_in_SOAP_ENV__Fault(struct soap*, const char*, struct SOAP_ENV__Fault *, const char*);
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+#ifndef SOAP_TYPE_SOAP_ENV__Reason
+#define SOAP_TYPE_SOAP_ENV__Reason (50)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Reason(struct soap*, const struct SOAP_ENV__Reason *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Reason(struct soap*, const struct SOAP_ENV__Reason *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Reason(struct soap*, const char*, int, const struct SOAP_ENV__Reason *, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Reason * SOAP_FMAC4 soap_get_SOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason *, const char*, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Reason * SOAP_FMAC4 soap_in_SOAP_ENV__Reason(struct soap*, const char*, struct SOAP_ENV__Reason *, const char*);
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+#ifndef SOAP_TYPE_SOAP_ENV__Detail
+#define SOAP_TYPE_SOAP_ENV__Detail (47)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Detail(struct soap*, const struct SOAP_ENV__Detail *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Detail(struct soap*, const struct SOAP_ENV__Detail *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Detail(struct soap*, const char*, int, const struct SOAP_ENV__Detail *, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Detail * SOAP_FMAC4 soap_get_SOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail *, const char*, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Detail * SOAP_FMAC4 soap_in_SOAP_ENV__Detail(struct soap*, const char*, struct SOAP_ENV__Detail *, const char*);
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+#ifndef SOAP_TYPE_SOAP_ENV__Code
+#define SOAP_TYPE_SOAP_ENV__Code (45)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Code(struct soap*, const struct SOAP_ENV__Code *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Code(struct soap*, const struct SOAP_ENV__Code *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Code(struct soap*, const char*, int, const struct SOAP_ENV__Code *, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Code * SOAP_FMAC4 soap_get_SOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code *, const char*, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Code * SOAP_FMAC4 soap_in_SOAP_ENV__Code(struct soap*, const char*, struct SOAP_ENV__Code *, const char*);
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+#ifndef SOAP_TYPE_SOAP_ENV__Header
+#define SOAP_TYPE_SOAP_ENV__Header (44)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Header(struct soap*, struct SOAP_ENV__Header *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Header(struct soap*, const struct SOAP_ENV__Header *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Header(struct soap*, const struct SOAP_ENV__Header *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Header(struct soap*, const char*, int, const struct SOAP_ENV__Header *, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Header * SOAP_FMAC4 soap_get_SOAP_ENV__Header(struct soap*, struct SOAP_ENV__Header *, const char*, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Header * SOAP_FMAC4 soap_in_SOAP_ENV__Header(struct soap*, const char*, struct SOAP_ENV__Header *, const char*);
+
+#endif
+
+#ifndef SOAP_TYPE_ns1__stat_USCOREobject
+#define SOAP_TYPE_ns1__stat_USCOREobject (43)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__stat_USCOREobject(struct soap*, struct ns1__stat_USCOREobject *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__stat_USCOREobject(struct soap*, const struct ns1__stat_USCOREobject *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__stat_USCOREobject(struct soap*, const struct ns1__stat_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__stat_USCOREobject(struct soap*, const char*, int, const struct ns1__stat_USCOREobject *, const char*);
+SOAP_FMAC3 struct ns1__stat_USCOREobject * SOAP_FMAC4 soap_get_ns1__stat_USCOREobject(struct soap*, struct ns1__stat_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 struct ns1__stat_USCOREobject * SOAP_FMAC4 soap_in_ns1__stat_USCOREobject(struct soap*, const char*, struct ns1__stat_USCOREobject *, const char*);
+
+#ifndef SOAP_TYPE_ns1__stat_USCOREobjectResponse
+#define SOAP_TYPE_ns1__stat_USCOREobjectResponse (40)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__stat_USCOREobjectResponse(struct soap*, struct ns1__stat_USCOREobjectResponse *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__stat_USCOREobjectResponse(struct soap*, const struct ns1__stat_USCOREobjectResponse *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__stat_USCOREobjectResponse(struct soap*, const struct ns1__stat_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__stat_USCOREobjectResponse(struct soap*, const char*, int, const struct ns1__stat_USCOREobjectResponse *, const char*);
+SOAP_FMAC3 struct ns1__stat_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__stat_USCOREobjectResponse(struct soap*, struct ns1__stat_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 struct ns1__stat_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__stat_USCOREobjectResponse(struct soap*, const char*, struct ns1__stat_USCOREobjectResponse *, const char*);
+
+#ifndef SOAP_TYPE_ns1__delete_USCOREinstance
+#define SOAP_TYPE_ns1__delete_USCOREinstance (39)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__delete_USCOREinstance(struct soap*, struct ns1__delete_USCOREinstance *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__delete_USCOREinstance(struct soap*, const struct ns1__delete_USCOREinstance *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__delete_USCOREinstance(struct soap*, const struct ns1__delete_USCOREinstance *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__delete_USCOREinstance(struct soap*, const char*, int, const struct ns1__delete_USCOREinstance *, const char*);
+SOAP_FMAC3 struct ns1__delete_USCOREinstance * SOAP_FMAC4 soap_get_ns1__delete_USCOREinstance(struct soap*, struct ns1__delete_USCOREinstance *, const char*, const char*);
+SOAP_FMAC3 struct ns1__delete_USCOREinstance * SOAP_FMAC4 soap_in_ns1__delete_USCOREinstance(struct soap*, const char*, struct ns1__delete_USCOREinstance *, const char*);
+
+#ifndef SOAP_TYPE_ns1__delete_USCOREinstanceResponse
+#define SOAP_TYPE_ns1__delete_USCOREinstanceResponse (38)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__delete_USCOREinstanceResponse(struct soap*, struct ns1__delete_USCOREinstanceResponse *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__delete_USCOREinstanceResponse(struct soap*, const struct ns1__delete_USCOREinstanceResponse *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__delete_USCOREinstanceResponse(struct soap*, const struct ns1__delete_USCOREinstanceResponse *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__delete_USCOREinstanceResponse(struct soap*, const char*, int, const struct ns1__delete_USCOREinstanceResponse *, const char*);
+SOAP_FMAC3 struct ns1__delete_USCOREinstanceResponse * SOAP_FMAC4 soap_get_ns1__delete_USCOREinstanceResponse(struct soap*, struct ns1__delete_USCOREinstanceResponse *, const char*, const char*);
+SOAP_FMAC3 struct ns1__delete_USCOREinstanceResponse * SOAP_FMAC4 soap_in_ns1__delete_USCOREinstanceResponse(struct soap*, const char*, struct ns1__delete_USCOREinstanceResponse *, const char*);
+
+#ifndef SOAP_TYPE_ns1__find_USCOREinstances
+#define SOAP_TYPE_ns1__find_USCOREinstances (36)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__find_USCOREinstances(struct soap*, struct ns1__find_USCOREinstances *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__find_USCOREinstances(struct soap*, const struct ns1__find_USCOREinstances *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__find_USCOREinstances(struct soap*, const struct ns1__find_USCOREinstances *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__find_USCOREinstances(struct soap*, const char*, int, const struct ns1__find_USCOREinstances *, const char*);
+SOAP_FMAC3 struct ns1__find_USCOREinstances * SOAP_FMAC4 soap_get_ns1__find_USCOREinstances(struct soap*, struct ns1__find_USCOREinstances *, const char*, const char*);
+SOAP_FMAC3 struct ns1__find_USCOREinstances * SOAP_FMAC4 soap_in_ns1__find_USCOREinstances(struct soap*, const char*, struct ns1__find_USCOREinstances *, const char*);
+
+#ifndef SOAP_TYPE_ns1__find_USCOREinstancesResponse
+#define SOAP_TYPE_ns1__find_USCOREinstancesResponse (33)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__find_USCOREinstancesResponse(struct soap*, struct ns1__find_USCOREinstancesResponse *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__find_USCOREinstancesResponse(struct soap*, const struct ns1__find_USCOREinstancesResponse *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__find_USCOREinstancesResponse(struct soap*, const struct ns1__find_USCOREinstancesResponse *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__find_USCOREinstancesResponse(struct soap*, const char*, int, const struct ns1__find_USCOREinstancesResponse *, const char*);
+SOAP_FMAC3 struct ns1__find_USCOREinstancesResponse * SOAP_FMAC4 soap_get_ns1__find_USCOREinstancesResponse(struct soap*, struct ns1__find_USCOREinstancesResponse *, const char*, const char*);
+SOAP_FMAC3 struct ns1__find_USCOREinstancesResponse * SOAP_FMAC4 soap_in_ns1__find_USCOREinstancesResponse(struct soap*, const char*, struct ns1__find_USCOREinstancesResponse *, const char*);
+
+#ifndef SOAP_TYPE_ns1__find_USCOREobjects
+#define SOAP_TYPE_ns1__find_USCOREobjects (32)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__find_USCOREobjects(struct soap*, struct ns1__find_USCOREobjects *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__find_USCOREobjects(struct soap*, const struct ns1__find_USCOREobjects *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__find_USCOREobjects(struct soap*, const struct ns1__find_USCOREobjects *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__find_USCOREobjects(struct soap*, const char*, int, const struct ns1__find_USCOREobjects *, const char*);
+SOAP_FMAC3 struct ns1__find_USCOREobjects * SOAP_FMAC4 soap_get_ns1__find_USCOREobjects(struct soap*, struct ns1__find_USCOREobjects *, const char*, const char*);
+SOAP_FMAC3 struct ns1__find_USCOREobjects * SOAP_FMAC4 soap_in_ns1__find_USCOREobjects(struct soap*, const char*, struct ns1__find_USCOREobjects *, const char*);
+
+#ifndef SOAP_TYPE_ns1__find_USCOREobjectsResponse
+#define SOAP_TYPE_ns1__find_USCOREobjectsResponse (28)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__find_USCOREobjectsResponse(struct soap*, struct ns1__find_USCOREobjectsResponse *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__find_USCOREobjectsResponse(struct soap*, const struct ns1__find_USCOREobjectsResponse *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__find_USCOREobjectsResponse(struct soap*, const struct ns1__find_USCOREobjectsResponse *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__find_USCOREobjectsResponse(struct soap*, const char*, int, const struct ns1__find_USCOREobjectsResponse *, const char*);
+SOAP_FMAC3 struct ns1__find_USCOREobjectsResponse * SOAP_FMAC4 soap_get_ns1__find_USCOREobjectsResponse(struct soap*, struct ns1__find_USCOREobjectsResponse *, const char*, const char*);
+SOAP_FMAC3 struct ns1__find_USCOREobjectsResponse * SOAP_FMAC4 soap_in_ns1__find_USCOREobjectsResponse(struct soap*, const char*, struct ns1__find_USCOREobjectsResponse *, const char*);
+
+#ifndef SOAP_TYPE_ns1__unlock_USCOREobject
+#define SOAP_TYPE_ns1__unlock_USCOREobject (27)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__unlock_USCOREobject(struct soap*, struct ns1__unlock_USCOREobject *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__unlock_USCOREobject(struct soap*, const struct ns1__unlock_USCOREobject *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__unlock_USCOREobject(struct soap*, const struct ns1__unlock_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__unlock_USCOREobject(struct soap*, const char*, int, const struct ns1__unlock_USCOREobject *, const char*);
+SOAP_FMAC3 struct ns1__unlock_USCOREobject * SOAP_FMAC4 soap_get_ns1__unlock_USCOREobject(struct soap*, struct ns1__unlock_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 struct ns1__unlock_USCOREobject * SOAP_FMAC4 soap_in_ns1__unlock_USCOREobject(struct soap*, const char*, struct ns1__unlock_USCOREobject *, const char*);
+
+#ifndef SOAP_TYPE_ns1__unlock_USCOREobjectResponse
+#define SOAP_TYPE_ns1__unlock_USCOREobjectResponse (26)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__unlock_USCOREobjectResponse(struct soap*, struct ns1__unlock_USCOREobjectResponse *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__unlock_USCOREobjectResponse(struct soap*, const struct ns1__unlock_USCOREobjectResponse *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__unlock_USCOREobjectResponse(struct soap*, const struct ns1__unlock_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__unlock_USCOREobjectResponse(struct soap*, const char*, int, const struct ns1__unlock_USCOREobjectResponse *, const char*);
+SOAP_FMAC3 struct ns1__unlock_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__unlock_USCOREobjectResponse(struct soap*, struct ns1__unlock_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 struct ns1__unlock_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__unlock_USCOREobjectResponse(struct soap*, const char*, struct ns1__unlock_USCOREobjectResponse *, const char*);
+
+#ifndef SOAP_TYPE_ns1__lock_USCOREobject
+#define SOAP_TYPE_ns1__lock_USCOREobject (24)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__lock_USCOREobject(struct soap*, struct ns1__lock_USCOREobject *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__lock_USCOREobject(struct soap*, const struct ns1__lock_USCOREobject *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__lock_USCOREobject(struct soap*, const struct ns1__lock_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__lock_USCOREobject(struct soap*, const char*, int, const struct ns1__lock_USCOREobject *, const char*);
+SOAP_FMAC3 struct ns1__lock_USCOREobject * SOAP_FMAC4 soap_get_ns1__lock_USCOREobject(struct soap*, struct ns1__lock_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 struct ns1__lock_USCOREobject * SOAP_FMAC4 soap_in_ns1__lock_USCOREobject(struct soap*, const char*, struct ns1__lock_USCOREobject *, const char*);
+
+#ifndef SOAP_TYPE_ns1__lock_USCOREobjectResponse
+#define SOAP_TYPE_ns1__lock_USCOREobjectResponse (23)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__lock_USCOREobjectResponse(struct soap*, struct ns1__lock_USCOREobjectResponse *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__lock_USCOREobjectResponse(struct soap*, const struct ns1__lock_USCOREobjectResponse *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__lock_USCOREobjectResponse(struct soap*, const struct ns1__lock_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__lock_USCOREobjectResponse(struct soap*, const char*, int, const struct ns1__lock_USCOREobjectResponse *, const char*);
+SOAP_FMAC3 struct ns1__lock_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__lock_USCOREobjectResponse(struct soap*, struct ns1__lock_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 struct ns1__lock_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__lock_USCOREobjectResponse(struct soap*, const char*, struct ns1__lock_USCOREobjectResponse *, const char*);
+
+#ifndef SOAP_TYPE_ns1__replicate_USCOREobject
+#define SOAP_TYPE_ns1__replicate_USCOREobject (20)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__replicate_USCOREobject(struct soap*, struct ns1__replicate_USCOREobject *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__replicate_USCOREobject(struct soap*, const struct ns1__replicate_USCOREobject *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__replicate_USCOREobject(struct soap*, const struct ns1__replicate_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__replicate_USCOREobject(struct soap*, const char*, int, const struct ns1__replicate_USCOREobject *, const char*);
+SOAP_FMAC3 struct ns1__replicate_USCOREobject * SOAP_FMAC4 soap_get_ns1__replicate_USCOREobject(struct soap*, struct ns1__replicate_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 struct ns1__replicate_USCOREobject * SOAP_FMAC4 soap_in_ns1__replicate_USCOREobject(struct soap*, const char*, struct ns1__replicate_USCOREobject *, const char*);
+
+#ifndef SOAP_TYPE_ns1__replicate_USCOREobjectResponse
+#define SOAP_TYPE_ns1__replicate_USCOREobjectResponse (19)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__replicate_USCOREobjectResponse(struct soap*, struct ns1__replicate_USCOREobjectResponse *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__replicate_USCOREobjectResponse(struct soap*, const struct ns1__replicate_USCOREobjectResponse *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__replicate_USCOREobjectResponse(struct soap*, const struct ns1__replicate_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__replicate_USCOREobjectResponse(struct soap*, const char*, int, const struct ns1__replicate_USCOREobjectResponse *, const char*);
+SOAP_FMAC3 struct ns1__replicate_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__replicate_USCOREobjectResponse(struct soap*, struct ns1__replicate_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 struct ns1__replicate_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__replicate_USCOREobjectResponse(struct soap*, const char*, struct ns1__replicate_USCOREobjectResponse *, const char*);
+
+#ifndef SOAP_TYPE_ns1__rename_USCOREobject_
+#define SOAP_TYPE_ns1__rename_USCOREobject_ (17)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__rename_USCOREobject_(struct soap*, struct ns1__rename_USCOREobject_ *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__rename_USCOREobject_(struct soap*, const struct ns1__rename_USCOREobject_ *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__rename_USCOREobject_(struct soap*, const struct ns1__rename_USCOREobject_ *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__rename_USCOREobject_(struct soap*, const char*, int, const struct ns1__rename_USCOREobject_ *, const char*);
+SOAP_FMAC3 struct ns1__rename_USCOREobject_ * SOAP_FMAC4 soap_get_ns1__rename_USCOREobject_(struct soap*, struct ns1__rename_USCOREobject_ *, const char*, const char*);
+SOAP_FMAC3 struct ns1__rename_USCOREobject_ * SOAP_FMAC4 soap_in_ns1__rename_USCOREobject_(struct soap*, const char*, struct ns1__rename_USCOREobject_ *, const char*);
+
+#ifndef SOAP_TYPE_ns1__rename_USCOREobject_Response
+#define SOAP_TYPE_ns1__rename_USCOREobject_Response (16)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__rename_USCOREobject_Response(struct soap*, struct ns1__rename_USCOREobject_Response *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__rename_USCOREobject_Response(struct soap*, const struct ns1__rename_USCOREobject_Response *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__rename_USCOREobject_Response(struct soap*, const struct ns1__rename_USCOREobject_Response *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__rename_USCOREobject_Response(struct soap*, const char*, int, const struct ns1__rename_USCOREobject_Response *, const char*);
+SOAP_FMAC3 struct ns1__rename_USCOREobject_Response * SOAP_FMAC4 soap_get_ns1__rename_USCOREobject_Response(struct soap*, struct ns1__rename_USCOREobject_Response *, const char*, const char*);
+SOAP_FMAC3 struct ns1__rename_USCOREobject_Response * SOAP_FMAC4 soap_in_ns1__rename_USCOREobject_Response(struct soap*, const char*, struct ns1__rename_USCOREobject_Response *, const char*);
+
+#ifndef SOAP_TYPE_ns1__rename_USCOREobject
+#define SOAP_TYPE_ns1__rename_USCOREobject (14)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__rename_USCOREobject(struct soap*, struct ns1__rename_USCOREobject *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__rename_USCOREobject(struct soap*, const struct ns1__rename_USCOREobject *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__rename_USCOREobject(struct soap*, const struct ns1__rename_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__rename_USCOREobject(struct soap*, const char*, int, const struct ns1__rename_USCOREobject *, const char*);
+SOAP_FMAC3 struct ns1__rename_USCOREobject * SOAP_FMAC4 soap_get_ns1__rename_USCOREobject(struct soap*, struct ns1__rename_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 struct ns1__rename_USCOREobject * SOAP_FMAC4 soap_in_ns1__rename_USCOREobject(struct soap*, const char*, struct ns1__rename_USCOREobject *, const char*);
+
+#ifndef SOAP_TYPE_ns1__rename_USCOREobjectResponse
+#define SOAP_TYPE_ns1__rename_USCOREobjectResponse (13)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__rename_USCOREobjectResponse(struct soap*, struct ns1__rename_USCOREobjectResponse *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__rename_USCOREobjectResponse(struct soap*, const struct ns1__rename_USCOREobjectResponse *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__rename_USCOREobjectResponse(struct soap*, const struct ns1__rename_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__rename_USCOREobjectResponse(struct soap*, const char*, int, const struct ns1__rename_USCOREobjectResponse *, const char*);
+SOAP_FMAC3 struct ns1__rename_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__rename_USCOREobjectResponse(struct soap*, struct ns1__rename_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 struct ns1__rename_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__rename_USCOREobjectResponse(struct soap*, const char*, struct ns1__rename_USCOREobjectResponse *, const char*);
+
+#ifndef SOAP_TYPE_ns1__create_USCOREobject
+#define SOAP_TYPE_ns1__create_USCOREobject (11)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__create_USCOREobject(struct soap*, struct ns1__create_USCOREobject *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__create_USCOREobject(struct soap*, const struct ns1__create_USCOREobject *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__create_USCOREobject(struct soap*, const struct ns1__create_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__create_USCOREobject(struct soap*, const char*, int, const struct ns1__create_USCOREobject *, const char*);
+SOAP_FMAC3 struct ns1__create_USCOREobject * SOAP_FMAC4 soap_get_ns1__create_USCOREobject(struct soap*, struct ns1__create_USCOREobject *, const char*, const char*);
+SOAP_FMAC3 struct ns1__create_USCOREobject * SOAP_FMAC4 soap_in_ns1__create_USCOREobject(struct soap*, const char*, struct ns1__create_USCOREobject *, const char*);
+
+#ifndef SOAP_TYPE_ns1__create_USCOREobjectResponse
+#define SOAP_TYPE_ns1__create_USCOREobjectResponse (10)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__create_USCOREobjectResponse(struct soap*, struct ns1__create_USCOREobjectResponse *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__create_USCOREobjectResponse(struct soap*, const struct ns1__create_USCOREobjectResponse *);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__create_USCOREobjectResponse(struct soap*, const struct ns1__create_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__create_USCOREobjectResponse(struct soap*, const char*, int, const struct ns1__create_USCOREobjectResponse *, const char*);
+SOAP_FMAC3 struct ns1__create_USCOREobjectResponse * SOAP_FMAC4 soap_get_ns1__create_USCOREobjectResponse(struct soap*, struct ns1__create_USCOREobjectResponse *, const char*, const char*);
+SOAP_FMAC3 struct ns1__create_USCOREobjectResponse * SOAP_FMAC4 soap_in_ns1__create_USCOREobjectResponse(struct soap*, const char*, struct ns1__create_USCOREobjectResponse *, const char*);
+
+#ifndef SOAP_TYPE_ArrayOfString
+#define SOAP_TYPE_ArrayOfString (7)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_ArrayOfString(struct soap*, struct ArrayOfString *);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ArrayOfString(struct soap*, struct ArrayOfString const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_ArrayOfString(struct soap*, const struct ArrayOfString *, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_ArrayOfString(struct soap*, const char*, int, const struct ArrayOfString *, const char*);
+SOAP_FMAC3 struct ArrayOfString * SOAP_FMAC4 soap_get_ArrayOfString(struct soap*, struct ArrayOfString *, const char*, const char*);
+SOAP_FMAC3 struct ArrayOfString * SOAP_FMAC4 soap_in_ArrayOfString(struct soap*, const char*, struct ArrayOfString *, const char*);
+
+#ifndef WITH_NOGLOBAL
+
+#ifndef SOAP_TYPE_PointerToSOAP_ENV__Reason
+#define SOAP_TYPE_PointerToSOAP_ENV__Reason (53)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason *const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason *const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Reason(struct soap*, const char *, int, struct SOAP_ENV__Reason *const*, const char *);
+SOAP_FMAC3 struct SOAP_ENV__Reason ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason **, const char*, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Reason ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Reason(struct soap*, const char*, struct SOAP_ENV__Reason **, const char*);
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+#ifndef SOAP_TYPE_PointerToSOAP_ENV__Detail
+#define SOAP_TYPE_PointerToSOAP_ENV__Detail (52)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail *const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail *const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Detail(struct soap*, const char *, int, struct SOAP_ENV__Detail *const*, const char *);
+SOAP_FMAC3 struct SOAP_ENV__Detail ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail **, const char*, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Detail ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Detail(struct soap*, const char*, struct SOAP_ENV__Detail **, const char*);
+
+#endif
+
+#ifndef WITH_NOGLOBAL
+
+#ifndef SOAP_TYPE_PointerToSOAP_ENV__Code
+#define SOAP_TYPE_PointerToSOAP_ENV__Code (46)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code *const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code *const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Code(struct soap*, const char *, int, struct SOAP_ENV__Code *const*, const char *);
+SOAP_FMAC3 struct SOAP_ENV__Code ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code **, const char*, const char*);
+SOAP_FMAC3 struct SOAP_ENV__Code ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Code(struct soap*, const char*, struct SOAP_ENV__Code **, const char*);
+
+#endif
+
+#ifndef SOAP_TYPE_PointerTons1__stat_USCOREobjectResponse
+#define SOAP_TYPE_PointerTons1__stat_USCOREobjectResponse (41)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerTons1__stat_USCOREobjectResponse(struct soap*, struct ns1__stat_USCOREobjectResponse *const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerTons1__stat_USCOREobjectResponse(struct soap*, struct ns1__stat_USCOREobjectResponse *const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerTons1__stat_USCOREobjectResponse(struct soap*, const char *, int, struct ns1__stat_USCOREobjectResponse *const*, const char *);
+SOAP_FMAC3 struct ns1__stat_USCOREobjectResponse ** SOAP_FMAC4 soap_get_PointerTons1__stat_USCOREobjectResponse(struct soap*, struct ns1__stat_USCOREobjectResponse **, const char*, const char*);
+SOAP_FMAC3 struct ns1__stat_USCOREobjectResponse ** SOAP_FMAC4 soap_in_PointerTons1__stat_USCOREobjectResponse(struct soap*, const char*, struct ns1__stat_USCOREobjectResponse **, const char*);
+
+#ifndef SOAP_TYPE_PointerTons1__find_USCOREinstancesResponse
+#define SOAP_TYPE_PointerTons1__find_USCOREinstancesResponse (34)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerTons1__find_USCOREinstancesResponse(struct soap*, struct ns1__find_USCOREinstancesResponse *const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerTons1__find_USCOREinstancesResponse(struct soap*, struct ns1__find_USCOREinstancesResponse *const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerTons1__find_USCOREinstancesResponse(struct soap*, const char *, int, struct ns1__find_USCOREinstancesResponse *const*, const char *);
+SOAP_FMAC3 struct ns1__find_USCOREinstancesResponse ** SOAP_FMAC4 soap_get_PointerTons1__find_USCOREinstancesResponse(struct soap*, struct ns1__find_USCOREinstancesResponse **, const char*, const char*);
+SOAP_FMAC3 struct ns1__find_USCOREinstancesResponse ** SOAP_FMAC4 soap_in_PointerTons1__find_USCOREinstancesResponse(struct soap*, const char*, struct ns1__find_USCOREinstancesResponse **, const char*);
+
+#ifndef SOAP_TYPE_PointerTons1__find_USCOREobjectsResponse
+#define SOAP_TYPE_PointerTons1__find_USCOREobjectsResponse (30)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerTons1__find_USCOREobjectsResponse(struct soap*, struct ns1__find_USCOREobjectsResponse *const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerTons1__find_USCOREobjectsResponse(struct soap*, struct ns1__find_USCOREobjectsResponse *const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerTons1__find_USCOREobjectsResponse(struct soap*, const char *, int, struct ns1__find_USCOREobjectsResponse *const*, const char *);
+SOAP_FMAC3 struct ns1__find_USCOREobjectsResponse ** SOAP_FMAC4 soap_get_PointerTons1__find_USCOREobjectsResponse(struct soap*, struct ns1__find_USCOREobjectsResponse **, const char*, const char*);
+SOAP_FMAC3 struct ns1__find_USCOREobjectsResponse ** SOAP_FMAC4 soap_in_PointerTons1__find_USCOREobjectsResponse(struct soap*, const char*, struct ns1__find_USCOREobjectsResponse **, const char*);
+
+#ifndef SOAP_TYPE_PointerToArrayOfString
+#define SOAP_TYPE_PointerToArrayOfString (29)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToArrayOfString(struct soap*, struct ArrayOfString *const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToArrayOfString(struct soap*, struct ArrayOfString *const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToArrayOfString(struct soap*, const char *, int, struct ArrayOfString *const*, const char *);
+SOAP_FMAC3 struct ArrayOfString ** SOAP_FMAC4 soap_get_PointerToArrayOfString(struct soap*, struct ArrayOfString **, const char*, const char*);
+SOAP_FMAC3 struct ArrayOfString ** SOAP_FMAC4 soap_in_PointerToArrayOfString(struct soap*, const char*, struct ArrayOfString **, const char*);
+
+#ifndef SOAP_TYPE_PointerToint
+#define SOAP_TYPE_PointerToint (21)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToint(struct soap*, int *const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToint(struct soap*, int *const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToint(struct soap*, const char *, int, int *const*, const char *);
+SOAP_FMAC3 int ** SOAP_FMAC4 soap_get_PointerToint(struct soap*, int **, const char*, const char*);
+SOAP_FMAC3 int ** SOAP_FMAC4 soap_in_PointerToint(struct soap*, const char*, int **, const char*);
+
+#ifndef SOAP_TYPE_PointerTostring
+#define SOAP_TYPE_PointerTostring (8)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerTostring(struct soap*, char **const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerTostring(struct soap*, char **const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerTostring(struct soap*, const char *, int, char **const*, const char *);
+SOAP_FMAC3 char *** SOAP_FMAC4 soap_get_PointerTostring(struct soap*, char ***, const char*, const char*);
+SOAP_FMAC3 char *** SOAP_FMAC4 soap_in_PointerTostring(struct soap*, const char*, char ***, const char*);
+
+#ifndef SOAP_TYPE__QName
+#define SOAP_TYPE__QName (5)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default__QName(struct soap*, char **);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize__QName(struct soap*, char *const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put__QName(struct soap*, char *const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out__QName(struct soap*, const char*, int, char*const*, const char*);
+SOAP_FMAC3 char ** SOAP_FMAC4 soap_get__QName(struct soap*, char **, const char*, const char*);
+SOAP_FMAC3 char * * SOAP_FMAC4 soap_in__QName(struct soap*, const char*, char **, const char*);
+
+#ifndef SOAP_TYPE_string
+#define SOAP_TYPE_string (4)
+#endif
+SOAP_FMAC3 void SOAP_FMAC4 soap_default_string(struct soap*, char **);
+SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_string(struct soap*, char *const*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_put_string(struct soap*, char *const*, const char*, const char*);
+SOAP_FMAC3 int SOAP_FMAC4 soap_out_string(struct soap*, const char*, int, char*const*, const char*);
+SOAP_FMAC3 char ** SOAP_FMAC4 soap_get_string(struct soap*, char **, const char*, const char*);
+SOAP_FMAC3 char * * SOAP_FMAC4 soap_in_string(struct soap*, const char*, char **, const char*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/* End of soapH.h */
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapServer.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapServer.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapServer.c	(revision 22322)
@@ -0,0 +1,538 @@
+/* soapServer.c
+   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
+   This part of the software is released under one of the following licenses:
+   GPL, the gSOAP public license, or Genivia's license for commercial use.
+*/
+#include "soapH.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SOAP_SOURCE_STAMP("@(#) soapServer.c ver 2.7.11 2008-09-13 01:14:39 GMT")
+
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap *soap)
+{
+#ifndef WITH_FASTCGI
+	unsigned int k = soap->max_keep_alive;
+#endif
+
+	do
+	{
+#ifdef WITH_FASTCGI
+		if (FCGI_Accept() < 0)
+		{
+			soap->error = SOAP_EOF;
+			return soap_send_fault(soap);
+		}
+#endif
+
+		soap_begin(soap);
+
+#ifndef WITH_FASTCGI
+		if (soap->max_keep_alive > 0 && !--k)
+			soap->keep_alive = 0;
+#endif
+
+		if (soap_begin_recv(soap))
+		{	if (soap->error < SOAP_STOP)
+			{
+#ifdef WITH_FASTCGI
+				soap_send_fault(soap);
+#else 
+				return soap_send_fault(soap);
+#endif
+			}
+			soap_closesock(soap);
+
+			continue;
+		}
+
+		if (soap_envelope_begin_in(soap)
+		 || soap_recv_header(soap)
+		 || soap_body_begin_in(soap)
+		 || soap_serve_request(soap)
+		 || (soap->fserveloop && soap->fserveloop(soap)))
+		{
+#ifdef WITH_FASTCGI
+			soap_send_fault(soap);
+#else
+			return soap_send_fault(soap);
+#endif
+		}
+
+#ifdef WITH_FASTCGI
+		soap_destroy(soap);
+		soap_end(soap);
+	} while (1);
+#else
+	} while (soap->keep_alive);
+#endif
+	return SOAP_OK;
+}
+
+#ifndef WITH_NOSERVEREQUEST
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap *soap)
+{
+	soap_peek_element(soap);
+	if (!soap_match_tag(soap, soap->tag, "ns1:create_object"))
+		return soap_serve_ns1__create_USCOREobject(soap);
+	if (!soap_match_tag(soap, soap->tag, "ns1:rename_object"))
+		return soap_serve_ns1__rename_USCOREobject(soap);
+	if (!soap_match_tag(soap, soap->tag, "ns1:rename_object"))
+		return soap_serve_ns1__rename_USCOREobject_(soap);
+	if (!soap_match_tag(soap, soap->tag, "ns1:replicate_object"))
+		return soap_serve_ns1__replicate_USCOREobject(soap);
+	if (!soap_match_tag(soap, soap->tag, "ns1:lock_object"))
+		return soap_serve_ns1__lock_USCOREobject(soap);
+	if (!soap_match_tag(soap, soap->tag, "ns1:unlock_object"))
+		return soap_serve_ns1__unlock_USCOREobject(soap);
+	if (!soap_match_tag(soap, soap->tag, "ns1:find_objects"))
+		return soap_serve_ns1__find_USCOREobjects(soap);
+	if (!soap_match_tag(soap, soap->tag, "ns1:find_instances"))
+		return soap_serve_ns1__find_USCOREinstances(soap);
+	if (!soap_match_tag(soap, soap->tag, "ns1:delete_instance"))
+		return soap_serve_ns1__delete_USCOREinstance(soap);
+	if (!soap_match_tag(soap, soap->tag, "ns1:stat_object"))
+		return soap_serve_ns1__stat_USCOREobject(soap);
+	return soap->error = SOAP_NO_METHOD;
+}
+#endif
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__create_USCOREobject(struct soap *soap)
+{	struct ns1__create_USCOREobject soap_tmp_ns1__create_USCOREobject;
+	struct ns1__create_USCOREobjectResponse soap_tmp_ns1__create_USCOREobjectResponse;
+	char * soap_tmp_string;
+	soap_default_ns1__create_USCOREobjectResponse(soap, &soap_tmp_ns1__create_USCOREobjectResponse);
+	soap_tmp_string = NULL;
+	soap_tmp_ns1__create_USCOREobjectResponse.result = &soap_tmp_string;
+	soap_default_ns1__create_USCOREobject(soap, &soap_tmp_ns1__create_USCOREobject);
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	if (!soap_get_ns1__create_USCOREobject(soap, &soap_tmp_ns1__create_USCOREobject, "ns1:create_object", NULL))
+		return soap->error;
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap->error;
+	soap->error = ns1__create_USCOREobject(soap, soap_tmp_ns1__create_USCOREobject.key, soap_tmp_ns1__create_USCOREobject.volume, &soap_tmp_string);
+	if (soap->error)
+		return soap->error;
+	soap_serializeheader(soap);
+	soap_serialize_ns1__create_USCOREobjectResponse(soap, &soap_tmp_ns1__create_USCOREobjectResponse);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__create_USCOREobjectResponse(soap, &soap_tmp_ns1__create_USCOREobjectResponse, "ns1:create_objectResponse", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	};
+	if (soap_end_count(soap)
+	 || soap_response(soap, SOAP_OK)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__create_USCOREobjectResponse(soap, &soap_tmp_ns1__create_USCOREobjectResponse, "ns1:create_objectResponse", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap->error;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__rename_USCOREobject(struct soap *soap)
+{	struct ns1__rename_USCOREobject soap_tmp_ns1__rename_USCOREobject;
+	struct ns1__rename_USCOREobjectResponse soap_tmp_ns1__rename_USCOREobjectResponse;
+	char * soap_tmp_string;
+	soap_default_ns1__rename_USCOREobjectResponse(soap, &soap_tmp_ns1__rename_USCOREobjectResponse);
+	soap_tmp_string = NULL;
+	soap_tmp_ns1__rename_USCOREobjectResponse.result = &soap_tmp_string;
+	soap_default_ns1__rename_USCOREobject(soap, &soap_tmp_ns1__rename_USCOREobject);
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	if (!soap_get_ns1__rename_USCOREobject(soap, &soap_tmp_ns1__rename_USCOREobject, "ns1:rename_object", NULL))
+		return soap->error;
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap->error;
+	soap->error = ns1__rename_USCOREobject(soap, soap_tmp_ns1__rename_USCOREobject.key, soap_tmp_ns1__rename_USCOREobject.newkey, &soap_tmp_string);
+	if (soap->error)
+		return soap->error;
+	soap_serializeheader(soap);
+	soap_serialize_ns1__rename_USCOREobjectResponse(soap, &soap_tmp_ns1__rename_USCOREobjectResponse);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__rename_USCOREobjectResponse(soap, &soap_tmp_ns1__rename_USCOREobjectResponse, "ns1:rename_objectResponse", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	};
+	if (soap_end_count(soap)
+	 || soap_response(soap, SOAP_OK)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__rename_USCOREobjectResponse(soap, &soap_tmp_ns1__rename_USCOREobjectResponse, "ns1:rename_objectResponse", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap->error;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__rename_USCOREobject_(struct soap *soap)
+{	struct ns1__rename_USCOREobject_ soap_tmp_ns1__rename_USCOREobject_;
+	struct ns1__rename_USCOREobject_Response soap_tmp_ns1__rename_USCOREobject_Response;
+	char * soap_tmp_string;
+	soap_default_ns1__rename_USCOREobject_Response(soap, &soap_tmp_ns1__rename_USCOREobject_Response);
+	soap_tmp_string = NULL;
+	soap_tmp_ns1__rename_USCOREobject_Response.result = &soap_tmp_string;
+	soap_default_ns1__rename_USCOREobject_(soap, &soap_tmp_ns1__rename_USCOREobject_);
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	if (!soap_get_ns1__rename_USCOREobject_(soap, &soap_tmp_ns1__rename_USCOREobject_, "ns1:rename_object", NULL))
+		return soap->error;
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap->error;
+	soap->error = ns1__rename_USCOREobject_(soap, soap_tmp_ns1__rename_USCOREobject_.key, soap_tmp_ns1__rename_USCOREobject_.newkey, &soap_tmp_string);
+	if (soap->error)
+		return soap->error;
+	soap_serializeheader(soap);
+	soap_serialize_ns1__rename_USCOREobject_Response(soap, &soap_tmp_ns1__rename_USCOREobject_Response);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__rename_USCOREobject_Response(soap, &soap_tmp_ns1__rename_USCOREobject_Response, "ns1:rename_object-Response", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	};
+	if (soap_end_count(soap)
+	 || soap_response(soap, SOAP_OK)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__rename_USCOREobject_Response(soap, &soap_tmp_ns1__rename_USCOREobject_Response, "ns1:rename_object-Response", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap->error;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__replicate_USCOREobject(struct soap *soap)
+{	struct ns1__replicate_USCOREobject soap_tmp_ns1__replicate_USCOREobject;
+	struct ns1__replicate_USCOREobjectResponse soap_tmp_ns1__replicate_USCOREobjectResponse;
+	char * soap_tmp_string;
+	soap_default_ns1__replicate_USCOREobjectResponse(soap, &soap_tmp_ns1__replicate_USCOREobjectResponse);
+	soap_tmp_string = NULL;
+	soap_tmp_ns1__replicate_USCOREobjectResponse.result = &soap_tmp_string;
+	soap_default_ns1__replicate_USCOREobject(soap, &soap_tmp_ns1__replicate_USCOREobject);
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	if (!soap_get_ns1__replicate_USCOREobject(soap, &soap_tmp_ns1__replicate_USCOREobject, "ns1:replicate_object", NULL))
+		return soap->error;
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap->error;
+	soap->error = ns1__replicate_USCOREobject(soap, soap_tmp_ns1__replicate_USCOREobject.key, soap_tmp_ns1__replicate_USCOREobject.volume, &soap_tmp_string);
+	if (soap->error)
+		return soap->error;
+	soap_serializeheader(soap);
+	soap_serialize_ns1__replicate_USCOREobjectResponse(soap, &soap_tmp_ns1__replicate_USCOREobjectResponse);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__replicate_USCOREobjectResponse(soap, &soap_tmp_ns1__replicate_USCOREobjectResponse, "ns1:replicate_objectResponse", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	};
+	if (soap_end_count(soap)
+	 || soap_response(soap, SOAP_OK)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__replicate_USCOREobjectResponse(soap, &soap_tmp_ns1__replicate_USCOREobjectResponse, "ns1:replicate_objectResponse", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap->error;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__lock_USCOREobject(struct soap *soap)
+{	struct ns1__lock_USCOREobject soap_tmp_ns1__lock_USCOREobject;
+	struct ns1__lock_USCOREobjectResponse soap_tmp_ns1__lock_USCOREobjectResponse;
+	int soap_tmp_int;
+	soap_default_ns1__lock_USCOREobjectResponse(soap, &soap_tmp_ns1__lock_USCOREobjectResponse);
+	soap_default_int(soap, &soap_tmp_int);
+	soap_tmp_ns1__lock_USCOREobjectResponse.result = &soap_tmp_int;
+	soap_default_ns1__lock_USCOREobject(soap, &soap_tmp_ns1__lock_USCOREobject);
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	if (!soap_get_ns1__lock_USCOREobject(soap, &soap_tmp_ns1__lock_USCOREobject, "ns1:lock_object", NULL))
+		return soap->error;
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap->error;
+	soap->error = ns1__lock_USCOREobject(soap, soap_tmp_ns1__lock_USCOREobject.key, soap_tmp_ns1__lock_USCOREobject.type, &soap_tmp_int);
+	if (soap->error)
+		return soap->error;
+	soap_serializeheader(soap);
+	soap_serialize_ns1__lock_USCOREobjectResponse(soap, &soap_tmp_ns1__lock_USCOREobjectResponse);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__lock_USCOREobjectResponse(soap, &soap_tmp_ns1__lock_USCOREobjectResponse, "ns1:lock_objectResponse", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	};
+	if (soap_end_count(soap)
+	 || soap_response(soap, SOAP_OK)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__lock_USCOREobjectResponse(soap, &soap_tmp_ns1__lock_USCOREobjectResponse, "ns1:lock_objectResponse", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap->error;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__unlock_USCOREobject(struct soap *soap)
+{	struct ns1__unlock_USCOREobject soap_tmp_ns1__unlock_USCOREobject;
+	struct ns1__unlock_USCOREobjectResponse soap_tmp_ns1__unlock_USCOREobjectResponse;
+	int soap_tmp_int;
+	soap_default_ns1__unlock_USCOREobjectResponse(soap, &soap_tmp_ns1__unlock_USCOREobjectResponse);
+	soap_default_int(soap, &soap_tmp_int);
+	soap_tmp_ns1__unlock_USCOREobjectResponse.result = &soap_tmp_int;
+	soap_default_ns1__unlock_USCOREobject(soap, &soap_tmp_ns1__unlock_USCOREobject);
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	if (!soap_get_ns1__unlock_USCOREobject(soap, &soap_tmp_ns1__unlock_USCOREobject, "ns1:unlock_object", NULL))
+		return soap->error;
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap->error;
+	soap->error = ns1__unlock_USCOREobject(soap, soap_tmp_ns1__unlock_USCOREobject.key, soap_tmp_ns1__unlock_USCOREobject.type, &soap_tmp_int);
+	if (soap->error)
+		return soap->error;
+	soap_serializeheader(soap);
+	soap_serialize_ns1__unlock_USCOREobjectResponse(soap, &soap_tmp_ns1__unlock_USCOREobjectResponse);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__unlock_USCOREobjectResponse(soap, &soap_tmp_ns1__unlock_USCOREobjectResponse, "ns1:unlock_objectResponse", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	};
+	if (soap_end_count(soap)
+	 || soap_response(soap, SOAP_OK)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__unlock_USCOREobjectResponse(soap, &soap_tmp_ns1__unlock_USCOREobjectResponse, "ns1:unlock_objectResponse", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap->error;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__find_USCOREobjects(struct soap *soap)
+{	struct ns1__find_USCOREobjects soap_tmp_ns1__find_USCOREobjects;
+	struct ns1__find_USCOREobjectsResponse _param_1;
+	soap_default_ns1__find_USCOREobjectsResponse(soap, &_param_1);
+	soap_default_ns1__find_USCOREobjects(soap, &soap_tmp_ns1__find_USCOREobjects);
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	if (!soap_get_ns1__find_USCOREobjects(soap, &soap_tmp_ns1__find_USCOREobjects, "ns1:find_objects", NULL))
+		return soap->error;
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap->error;
+	soap->error = ns1__find_USCOREobjects(soap, soap_tmp_ns1__find_USCOREobjects.pattern, &_param_1);
+	if (soap->error)
+		return soap->error;
+	soap_serializeheader(soap);
+	soap_serialize_ns1__find_USCOREobjectsResponse(soap, &_param_1);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__find_USCOREobjectsResponse(soap, &_param_1, "ns1:find_objectsResponse", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	};
+	if (soap_end_count(soap)
+	 || soap_response(soap, SOAP_OK)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__find_USCOREobjectsResponse(soap, &_param_1, "ns1:find_objectsResponse", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap->error;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__find_USCOREinstances(struct soap *soap)
+{	struct ns1__find_USCOREinstances soap_tmp_ns1__find_USCOREinstances;
+	struct ns1__find_USCOREinstancesResponse _param_2;
+	soap_default_ns1__find_USCOREinstancesResponse(soap, &_param_2);
+	soap_default_ns1__find_USCOREinstances(soap, &soap_tmp_ns1__find_USCOREinstances);
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	if (!soap_get_ns1__find_USCOREinstances(soap, &soap_tmp_ns1__find_USCOREinstances, "ns1:find_instances", NULL))
+		return soap->error;
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap->error;
+	soap->error = ns1__find_USCOREinstances(soap, soap_tmp_ns1__find_USCOREinstances.key, soap_tmp_ns1__find_USCOREinstances.volume, &_param_2);
+	if (soap->error)
+		return soap->error;
+	soap_serializeheader(soap);
+	soap_serialize_ns1__find_USCOREinstancesResponse(soap, &_param_2);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__find_USCOREinstancesResponse(soap, &_param_2, "ns1:find_instancesResponse", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	};
+	if (soap_end_count(soap)
+	 || soap_response(soap, SOAP_OK)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__find_USCOREinstancesResponse(soap, &_param_2, "ns1:find_instancesResponse", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap->error;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__delete_USCOREinstance(struct soap *soap)
+{	struct ns1__delete_USCOREinstance soap_tmp_ns1__delete_USCOREinstance;
+	struct ns1__delete_USCOREinstanceResponse soap_tmp_ns1__delete_USCOREinstanceResponse;
+	int soap_tmp_int;
+	soap_default_ns1__delete_USCOREinstanceResponse(soap, &soap_tmp_ns1__delete_USCOREinstanceResponse);
+	soap_default_int(soap, &soap_tmp_int);
+	soap_tmp_ns1__delete_USCOREinstanceResponse.result = &soap_tmp_int;
+	soap_default_ns1__delete_USCOREinstance(soap, &soap_tmp_ns1__delete_USCOREinstance);
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	if (!soap_get_ns1__delete_USCOREinstance(soap, &soap_tmp_ns1__delete_USCOREinstance, "ns1:delete_instance", NULL))
+		return soap->error;
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap->error;
+	soap->error = ns1__delete_USCOREinstance(soap, soap_tmp_ns1__delete_USCOREinstance.uri, &soap_tmp_int);
+	if (soap->error)
+		return soap->error;
+	soap_serializeheader(soap);
+	soap_serialize_ns1__delete_USCOREinstanceResponse(soap, &soap_tmp_ns1__delete_USCOREinstanceResponse);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__delete_USCOREinstanceResponse(soap, &soap_tmp_ns1__delete_USCOREinstanceResponse, "ns1:delete_instanceResponse", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	};
+	if (soap_end_count(soap)
+	 || soap_response(soap, SOAP_OK)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__delete_USCOREinstanceResponse(soap, &soap_tmp_ns1__delete_USCOREinstanceResponse, "ns1:delete_instanceResponse", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap->error;
+	return soap_closesock(soap);
+}
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__stat_USCOREobject(struct soap *soap)
+{	struct ns1__stat_USCOREobject soap_tmp_ns1__stat_USCOREobject;
+	struct ns1__stat_USCOREobjectResponse _param_3;
+	soap_default_ns1__stat_USCOREobjectResponse(soap, &_param_3);
+	soap_default_ns1__stat_USCOREobject(soap, &soap_tmp_ns1__stat_USCOREobject);
+	soap->encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/";
+	if (!soap_get_ns1__stat_USCOREobject(soap, &soap_tmp_ns1__stat_USCOREobject, "ns1:stat_object", NULL))
+		return soap->error;
+	if (soap_body_end_in(soap)
+	 || soap_envelope_end_in(soap)
+	 || soap_end_recv(soap))
+		return soap->error;
+	soap->error = ns1__stat_USCOREobject(soap, soap_tmp_ns1__stat_USCOREobject.key, &_param_3);
+	if (soap->error)
+		return soap->error;
+	soap_serializeheader(soap);
+	soap_serialize_ns1__stat_USCOREobjectResponse(soap, &_param_3);
+	if (soap_begin_count(soap))
+		return soap->error;
+	if (soap->mode & SOAP_IO_LENGTH)
+	{	if (soap_envelope_begin_out(soap)
+		 || soap_putheader(soap)
+		 || soap_body_begin_out(soap)
+		 || soap_put_ns1__stat_USCOREobjectResponse(soap, &_param_3, "ns1:stat_objectResponse", "")
+		 || soap_body_end_out(soap)
+		 || soap_envelope_end_out(soap))
+			 return soap->error;
+	};
+	if (soap_end_count(soap)
+	 || soap_response(soap, SOAP_OK)
+	 || soap_envelope_begin_out(soap)
+	 || soap_putheader(soap)
+	 || soap_body_begin_out(soap)
+	 || soap_put_ns1__stat_USCOREobjectResponse(soap, &_param_3, "ns1:stat_objectResponse", "")
+	 || soap_body_end_out(soap)
+	 || soap_envelope_end_out(soap)
+	 || soap_end_send(soap))
+		return soap->error;
+	return soap_closesock(soap);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/* End of soapServer.c */
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapServerLib.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapServerLib.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapServerLib.c	(revision 22322)
@@ -0,0 +1,14 @@
+/* soapServerLib.c
+   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
+   This part of the software is released under one of the following licenses:
+   GPL, the gSOAP public license, or Genivia's license for commercial use.
+*/
+#ifndef WITH_NOGLOBAL
+#define WITH_NOGLOBAL
+#endif
+#define SOAP_FMAC3 static
+#include "soapC.c"
+#include "soapServer.c"
+
+/* End of soapServerLib.c */
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapStub.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapStub.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/soapStub.h	(revision 22322)
@@ -0,0 +1,416 @@
+/* soapStub.h
+   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
+   This part of the software is released under one of the following licenses:
+   GPL, the gSOAP public license, or Genivia's license for commercial use.
+*/
+
+#ifndef soapStub_H
+#define soapStub_H
+#include "stdsoap2.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+ *                                                                            *
+ * Enumerations                                                               *
+ *                                                                            *
+\******************************************************************************/
+
+
+/******************************************************************************\
+ *                                                                            *
+ * Classes and Structs                                                        *
+ *                                                                            *
+\******************************************************************************/
+
+
+#if 0 /* volatile type: do not redeclare here */
+
+#endif
+
+#ifndef SOAP_TYPE_ArrayOfString
+#define SOAP_TYPE_ArrayOfString (7)
+/* SOAP encoded array of xsd:string schema type: */
+struct ArrayOfString
+{
+	char **__ptr;
+	int __size;
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__create_USCOREobjectResponse
+#define SOAP_TYPE_ns1__create_USCOREobjectResponse (10)
+/* ns1:create_objectResponse */
+struct ns1__create_USCOREobjectResponse
+{
+	char **result;	/* SOAP 1.2 RPC return element (when namespace qualified) */	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__create_USCOREobject
+#define SOAP_TYPE_ns1__create_USCOREobject (11)
+/* ns1:create_object */
+struct ns1__create_USCOREobject
+{
+	char *key;	/* optional element of type xsd:string */
+	char *volume;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__rename_USCOREobjectResponse
+#define SOAP_TYPE_ns1__rename_USCOREobjectResponse (13)
+/* ns1:rename_objectResponse */
+struct ns1__rename_USCOREobjectResponse
+{
+	char **result;	/* SOAP 1.2 RPC return element (when namespace qualified) */	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__rename_USCOREobject
+#define SOAP_TYPE_ns1__rename_USCOREobject (14)
+/* ns1:rename_object */
+struct ns1__rename_USCOREobject
+{
+	char *key;	/* optional element of type xsd:string */
+	char *newkey;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__rename_USCOREobject_Response
+#define SOAP_TYPE_ns1__rename_USCOREobject_Response (16)
+/* ns1:rename_object-Response */
+struct ns1__rename_USCOREobject_Response
+{
+	char **result;	/* SOAP 1.2 RPC return element (when namespace qualified) */	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__rename_USCOREobject_
+#define SOAP_TYPE_ns1__rename_USCOREobject_ (17)
+/* ns1:rename_object */
+struct ns1__rename_USCOREobject_
+{
+	char *key;	/* optional element of type xsd:string */
+	char *newkey;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__replicate_USCOREobjectResponse
+#define SOAP_TYPE_ns1__replicate_USCOREobjectResponse (19)
+/* ns1:replicate_objectResponse */
+struct ns1__replicate_USCOREobjectResponse
+{
+	char **result;	/* SOAP 1.2 RPC return element (when namespace qualified) */	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__replicate_USCOREobject
+#define SOAP_TYPE_ns1__replicate_USCOREobject (20)
+/* ns1:replicate_object */
+struct ns1__replicate_USCOREobject
+{
+	char *key;	/* optional element of type xsd:string */
+	char *volume;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__lock_USCOREobjectResponse
+#define SOAP_TYPE_ns1__lock_USCOREobjectResponse (23)
+/* ns1:lock_objectResponse */
+struct ns1__lock_USCOREobjectResponse
+{
+	int *result;	/* SOAP 1.2 RPC return element (when namespace qualified) */	/* optional element of type xsd:int */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__lock_USCOREobject
+#define SOAP_TYPE_ns1__lock_USCOREobject (24)
+/* ns1:lock_object */
+struct ns1__lock_USCOREobject
+{
+	char *key;	/* optional element of type xsd:string */
+	char *type;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__unlock_USCOREobjectResponse
+#define SOAP_TYPE_ns1__unlock_USCOREobjectResponse (26)
+/* ns1:unlock_objectResponse */
+struct ns1__unlock_USCOREobjectResponse
+{
+	int *result;	/* SOAP 1.2 RPC return element (when namespace qualified) */	/* optional element of type xsd:int */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__unlock_USCOREobject
+#define SOAP_TYPE_ns1__unlock_USCOREobject (27)
+/* ns1:unlock_object */
+struct ns1__unlock_USCOREobject
+{
+	char *key;	/* optional element of type xsd:string */
+	char *type;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__find_USCOREobjectsResponse
+#define SOAP_TYPE_ns1__find_USCOREobjectsResponse (28)
+/* ns1:find_objectsResponse */
+struct ns1__find_USCOREobjectsResponse
+{
+	struct ArrayOfString *result;	/* SOAP 1.2 RPC return element (when namespace qualified) */	/* optional element of type ArrayOfstring */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__find_USCOREobjects
+#define SOAP_TYPE_ns1__find_USCOREobjects (32)
+/* ns1:find_objects */
+struct ns1__find_USCOREobjects
+{
+	char *pattern;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__find_USCOREinstancesResponse
+#define SOAP_TYPE_ns1__find_USCOREinstancesResponse (33)
+/* ns1:find_instancesResponse */
+struct ns1__find_USCOREinstancesResponse
+{
+	struct ArrayOfString *result;	/* SOAP 1.2 RPC return element (when namespace qualified) */	/* optional element of type ArrayOfstring */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__find_USCOREinstances
+#define SOAP_TYPE_ns1__find_USCOREinstances (36)
+/* ns1:find_instances */
+struct ns1__find_USCOREinstances
+{
+	char *key;	/* optional element of type xsd:string */
+	char *volume;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__delete_USCOREinstanceResponse
+#define SOAP_TYPE_ns1__delete_USCOREinstanceResponse (38)
+/* ns1:delete_instanceResponse */
+struct ns1__delete_USCOREinstanceResponse
+{
+	int *result;	/* SOAP 1.2 RPC return element (when namespace qualified) */	/* optional element of type xsd:int */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__delete_USCOREinstance
+#define SOAP_TYPE_ns1__delete_USCOREinstance (39)
+/* ns1:delete_instance */
+struct ns1__delete_USCOREinstance
+{
+	char *uri;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__stat_USCOREobjectResponse
+#define SOAP_TYPE_ns1__stat_USCOREobjectResponse (40)
+/* ns1:stat_objectResponse */
+struct ns1__stat_USCOREobjectResponse
+{
+	struct ArrayOfString *result;	/* SOAP 1.2 RPC return element (when namespace qualified) */	/* optional element of type ArrayOfstring */
+};
+#endif
+
+#ifndef SOAP_TYPE_ns1__stat_USCOREobject
+#define SOAP_TYPE_ns1__stat_USCOREobject (43)
+/* ns1:stat_object */
+struct ns1__stat_USCOREobject
+{
+	char *key;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_SOAP_ENV__Header
+#define SOAP_TYPE_SOAP_ENV__Header (44)
+/* SOAP Header: */
+struct SOAP_ENV__Header
+{
+#ifdef WITH_NOEMPTYSTRUCT
+	char dummy;	/* dummy member to enable compilation */
+#endif
+};
+#endif
+
+#ifndef SOAP_TYPE_SOAP_ENV__Code
+#define SOAP_TYPE_SOAP_ENV__Code (45)
+/* SOAP Fault Code: */
+struct SOAP_ENV__Code
+{
+	char *SOAP_ENV__Value;	/* optional element of type xsd:QName */
+	struct SOAP_ENV__Code *SOAP_ENV__Subcode;	/* optional element of type SOAP-ENV:Code */
+};
+#endif
+
+#ifndef SOAP_TYPE_SOAP_ENV__Detail
+#define SOAP_TYPE_SOAP_ENV__Detail (47)
+/* SOAP-ENV:Detail */
+struct SOAP_ENV__Detail
+{
+	int __type;	/* any type of element <fault> (defined below) */
+	void *fault;	/* transient */
+	char *__any;
+};
+#endif
+
+#ifndef SOAP_TYPE_SOAP_ENV__Reason
+#define SOAP_TYPE_SOAP_ENV__Reason (50)
+/* SOAP-ENV:Reason */
+struct SOAP_ENV__Reason
+{
+	char *SOAP_ENV__Text;	/* optional element of type xsd:string */
+};
+#endif
+
+#ifndef SOAP_TYPE_SOAP_ENV__Fault
+#define SOAP_TYPE_SOAP_ENV__Fault (51)
+/* SOAP Fault: */
+struct SOAP_ENV__Fault
+{
+	char *faultcode;	/* optional element of type xsd:QName */
+	char *faultstring;	/* optional element of type xsd:string */
+	char *faultactor;	/* optional element of type xsd:string */
+	struct SOAP_ENV__Detail *detail;	/* optional element of type SOAP-ENV:Detail */
+	struct SOAP_ENV__Code *SOAP_ENV__Code;	/* optional element of type SOAP-ENV:Code */
+	struct SOAP_ENV__Reason *SOAP_ENV__Reason;	/* optional element of type SOAP-ENV:Reason */
+	char *SOAP_ENV__Node;	/* optional element of type xsd:string */
+	char *SOAP_ENV__Role;	/* optional element of type xsd:string */
+	struct SOAP_ENV__Detail *SOAP_ENV__Detail;	/* optional element of type SOAP-ENV:Detail */
+};
+#endif
+
+/******************************************************************************\
+ *                                                                            *
+ * Types with Custom Serializers                                              *
+ *                                                                            *
+\******************************************************************************/
+
+
+/******************************************************************************\
+ *                                                                            *
+ * Typedefs                                                                   *
+ *                                                                            *
+\******************************************************************************/
+
+#ifndef SOAP_TYPE__QName
+#define SOAP_TYPE__QName (5)
+typedef char *_QName;
+#endif
+
+#ifndef SOAP_TYPE__XML
+#define SOAP_TYPE__XML (6)
+typedef char *_XML;
+#endif
+
+
+/******************************************************************************\
+ *                                                                            *
+ * Typedef Synonyms                                                           *
+ *                                                                            *
+\******************************************************************************/
+
+
+/******************************************************************************\
+ *                                                                            *
+ * Externals                                                                  *
+ *                                                                            *
+\******************************************************************************/
+
+
+/******************************************************************************\
+ *                                                                            *
+ * Service Operations                                                         *
+ *                                                                            *
+\******************************************************************************/
+
+
+SOAP_FMAC5 int SOAP_FMAC6 ns1__create_USCOREobject(struct soap*, char *key, char *volume, char **result);
+
+SOAP_FMAC5 int SOAP_FMAC6 ns1__rename_USCOREobject(struct soap*, char *key, char *newkey, char **result);
+
+SOAP_FMAC5 int SOAP_FMAC6 ns1__rename_USCOREobject_(struct soap*, char *key, char *newkey, char **result);
+
+SOAP_FMAC5 int SOAP_FMAC6 ns1__replicate_USCOREobject(struct soap*, char *key, char *volume, char **result);
+
+SOAP_FMAC5 int SOAP_FMAC6 ns1__lock_USCOREobject(struct soap*, char *key, char *type, int *result);
+
+SOAP_FMAC5 int SOAP_FMAC6 ns1__unlock_USCOREobject(struct soap*, char *key, char *type, int *result);
+
+SOAP_FMAC5 int SOAP_FMAC6 ns1__find_USCOREobjects(struct soap*, char *pattern, struct ns1__find_USCOREobjectsResponse *_param_1);
+
+SOAP_FMAC5 int SOAP_FMAC6 ns1__find_USCOREinstances(struct soap*, char *key, char *volume, struct ns1__find_USCOREinstancesResponse *_param_2);
+
+SOAP_FMAC5 int SOAP_FMAC6 ns1__delete_USCOREinstance(struct soap*, char *uri, int *result);
+
+SOAP_FMAC5 int SOAP_FMAC6 ns1__stat_USCOREobject(struct soap*, char *key, struct ns1__stat_USCOREobjectResponse *_param_3);
+
+/******************************************************************************\
+ *                                                                            *
+ * Stubs                                                                      *
+ *                                                                            *
+\******************************************************************************/
+
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__create_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *volume, char **result);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__rename_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *newkey, char **result);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__rename_USCOREobject_(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *newkey, char **result);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__replicate_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *volume, char **result);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__lock_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *type, int *result);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__unlock_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *type, int *result);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__find_USCOREobjects(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *pattern, struct ns1__find_USCOREobjectsResponse *_param_1);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__find_USCOREinstances(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, char *volume, struct ns1__find_USCOREinstancesResponse *_param_2);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__delete_USCOREinstance(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *uri, int *result);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_call_ns1__stat_USCOREobject(struct soap *soap, const char *soap_endpoint, const char *soap_action, char *key, struct ns1__stat_USCOREobjectResponse *_param_3);
+
+/******************************************************************************\
+ *                                                                            *
+ * Skeletons                                                                  *
+ *                                                                            *
+\******************************************************************************/
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__create_USCOREobject(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__rename_USCOREobject(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__rename_USCOREobject_(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__replicate_USCOREobject(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__lock_USCOREobject(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__unlock_USCOREobject(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__find_USCOREobjects(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__find_USCOREinstances(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__delete_USCOREinstance(struct soap*);
+
+SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__stat_USCOREobject(struct soap*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/* End of soapStub.h */
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/stdsoap2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/stdsoap2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/stdsoap2.c	(revision 22322)
@@ -0,0 +1,15052 @@
+/*
+	stdsoap2.c[pp] 2.7.11-upd
+
+	gSOAP runtime engine
+
+gSOAP XML Web services tools
+Copyright (C) 2000-2008, Robert van Engelen, Genivia Inc., All Rights Reserved.
+This part of the software is released under ONE of the following licenses:
+GPL, the gSOAP public license, OR Genivia's license for commercial use.
+--------------------------------------------------------------------------------
+Contributors:
+
+Wind River Systems Inc., for the following additions under gSOAP public license:
+  - vxWorks compatible
+--------------------------------------------------------------------------------
+gSOAP public license.
+
+The contents of this file are subject to the gSOAP Public License Version 1.3
+(the "License"); you may not use this file except in compliance with the
+License. You may obtain a copy of the License at
+http://www.cs.fsu.edu/~engelen/soaplicense.html
+Software distributed under the License is distributed on an "AS IS" basis,
+WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+for the specific language governing rights and limitations under the License.
+
+The Initial Developer of the Original Code is Robert A. van Engelen.
+Copyright (C) 2000-2008, Robert van Engelen, Genivia Inc., All Rights Reserved.
+--------------------------------------------------------------------------------
+GPL license.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 USA
+
+Author contact information:
+engelen@genivia.com / engelen@acm.org
+
+This program is released under the GPL with the additional exemption that
+compiling, linking, and/or using OpenSSL is allowed.
+--------------------------------------------------------------------------------
+A commercial use license is available from Genivia, Inc., contact@genivia.com
+--------------------------------------------------------------------------------
+
+Installation note:
+
+Win32 build needs winsock.dll (Visual C++ "wsock32.lib")
+To do this in Visual C++ 6.0, go to "Project", "settings", select the "Link"
+tab (the project file needs to be selected in the file view) and add
+"wsock32.lib" to the "Object/library modules" entry
+
+On Mac OS X with gcc (GCC) 3.1 20020420 (prerelease) you MUST compile with
+-fstack_check when using -O2 because gcc 3.1 has a bug that smashes the stack
+when locally allocated data exceeds 64K.
+
+*/
+
+#ifdef AS400
+# pragma convert(819)	/* EBCDIC to ASCII */
+#endif
+
+#include "stdsoap2.h"
+
+#ifdef __BORLANDC__
+# pragma warn -8060
+#else
+# ifdef WIN32
+#  ifdef UNDER_CE
+#   pragma comment(lib, "winsock.lib")
+#  else
+#   pragma comment(lib, "wsock32.lib")
+#  endif
+#  pragma warning(disable : 4996) /* disable deprecation warnings */
+# endif
+#endif
+
+#ifdef __cplusplus
+SOAP_SOURCE_STAMP("@(#) stdsoap2.cpp ver 2.7.11 2008-07-26 00:00:00 GMT")
+extern "C" {
+#else
+SOAP_SOURCE_STAMP("@(#) stdsoap2.c ver 2.7.11 2008-07-26 00:00:00 GMT")
+#endif
+
+/* 8bit character representing unknown/nonrepresentable character data (e.g. not supported by current locale with multibyte support enabled) */
+#ifndef SOAP_UNKNOWN_CHAR
+#define SOAP_UNKNOWN_CHAR (127)
+#endif
+
+/*      EOF=-1 */
+#define SOAP_LT (soap_wchar)(-2) /* XML character '<' */
+#define SOAP_TT (soap_wchar)(-3) /* XML character '</' */
+#define SOAP_GT (soap_wchar)(-4) /* XML character '>' */
+#define SOAP_QT (soap_wchar)(-5) /* XML character '"' */
+#define SOAP_AP (soap_wchar)(-6) /* XML character ''' */
+
+#define soap_blank(c)		((c) >= 0 && (c) <= 32)
+#define soap_notblank(c)	((c) > 32)
+
+#if defined(WIN32) && !defined(UNDER_CE)
+#define soap_hash_ptr(p)	((PtrToUlong(p) >> 3) & (SOAP_PTRHASH - 1))
+#else
+#define soap_hash_ptr(p)	((size_t)(((unsigned long)(p) >> 3) & (SOAP_PTRHASH-1)))
+#endif
+
+#ifdef SOAP_DEBUG
+static void soap_init_logs(struct soap*);
+static void soap_close_logfile(struct soap*, int);
+static void soap_set_logfile(struct soap*, int, const char*);
+#endif
+
+#ifdef SOAP_MEM_DEBUG
+static void soap_init_mht(struct soap*);
+static void soap_free_mht(struct soap*);
+static void soap_track_unlink(struct soap*, const void*);
+#endif
+
+#ifndef PALM_2
+static int soap_set_error(struct soap*, const char*, const char*, const char*, const char*, int);
+static int soap_copy_fault(struct soap*, const char*, const char*, const char*, const char*);
+static int soap_getattrval(struct soap*, char*, size_t, soap_wchar);
+#endif
+
+#ifndef PALM_1
+static soap_wchar soap_char(struct soap*);
+static soap_wchar soap_get_pi(struct soap*);
+static int soap_isxdigit(int);
+static void *fplugin(struct soap*, const char*);
+#ifndef WITH_NOIDREF
+static void soap_update_ptrs(struct soap*, char*, char*, char*, char*);
+static int soap_has_copies(struct soap*, const char*, const char*);
+static void soap_init_iht(struct soap*);
+static void soap_free_iht(struct soap*);
+static void soap_init_pht(struct soap*);
+static void soap_free_pht(struct soap*);
+#endif
+#endif
+
+#ifndef WITH_LEAN
+static const char *soap_set_validation_fault(struct soap*, const char*, const char*);
+static int soap_isnumeric(struct soap*, const char*);
+static struct soap_nlist *soap_push_ns(struct soap *soap, const char *id, const char *ns, short utilized);
+static void soap_pop_ns(struct soap *soap);
+static void soap_utilize_ns(struct soap *soap, const char *tag, size_t n);
+#endif
+
+#ifndef WITH_LEANER
+#ifndef PALM_1
+static struct soap_multipart *soap_new_multipart(struct soap*, struct soap_multipart**, struct soap_multipart**, char*, size_t);
+static int soap_putdimefield(struct soap*, const char*, size_t);
+static char *soap_getdimefield(struct soap*, size_t);
+static void soap_select_mime_boundary(struct soap*);
+static int soap_valid_mime_boundary(struct soap*);
+static void soap_resolve_attachment(struct soap*, struct soap_multipart*);
+#endif
+#endif
+
+#ifdef WITH_GZIP
+static int soap_getgziphdr(struct soap*);
+#endif
+
+#ifdef WITH_OPENSSL
+int soap_ssl_init_done = 0;
+
+static int ssl_auth_init(struct soap*);
+static int ssl_verify_callback(int, X509_STORE_CTX*);
+static int ssl_verify_callback_allow_expired_certificate(int, X509_STORE_CTX*);
+static int ssl_password(char*, int, int, void *);
+/* The callback below is included for future references:
+static DH *ssl_tmp_dh(SSL*, int, int);
+*/
+#endif
+
+#if !defined(WITH_NOHTTP) || !defined(WITH_LEANER)
+#ifndef PALM_1
+static const char *soap_decode(char*, size_t, const char*, const char*);
+#endif
+#endif
+
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static soap_wchar soap_getchunkchar(struct soap*);
+static const char *http_error(struct soap*, int);
+static int http_post(struct soap*, const char*, const char*, int, const char*, const char*, size_t);
+static int http_get(struct soap*);
+static int http_put(struct soap*);
+static int http_del(struct soap*);
+static int http_head(struct soap*);
+static int http_send_header(struct soap*, const char*);
+static int http_post_header(struct soap*, const char*, const char*);
+static int http_response(struct soap*, int, size_t);
+static int http_parse(struct soap*);
+static int http_parse_header(struct soap*, const char*, const char*);
+#endif
+#endif
+
+#ifndef WITH_NOIO
+
+#ifndef PALM_1
+static int fsend(struct soap*, const char*, size_t);
+static size_t frecv(struct soap*, char*, size_t);
+static int tcp_init(struct soap*);
+static const char *tcp_error(struct soap*);
+#ifndef WITH_LEAN
+static size_t frecv_stop(struct soap*, char*, size_t);
+#endif
+#ifndef WITH_IPV6
+static int tcp_gethost(struct soap*, const char *addr, struct in_addr *inaddr);
+#endif
+static SOAP_SOCKET tcp_connect(struct soap*, const char *endpoint, const char *host, int port);
+static SOAP_SOCKET tcp_accept(struct soap*, SOAP_SOCKET, struct sockaddr*, int*);
+static int tcp_disconnect(struct soap*);
+static int tcp_closesocket(struct soap*, SOAP_SOCKET);
+static int tcp_shutdownsocket(struct soap*, SOAP_SOCKET, int);
+static const char *soap_strerror(struct soap*);
+#endif
+
+#if defined(WIN32)
+  #define SOAP_SOCKBLOCK(fd) \
+  { u_long blocking = 0; \
+    ioctlsocket(fd, FIONBIO, &blocking); \
+  }
+  #define SOAP_SOCKNONBLOCK(fd) \
+  { u_long nonblocking = 1; \
+    ioctlsocket(fd, FIONBIO, &nonblocking); \
+  }
+#elif defined(VXWORKS)
+  #define SOAP_SOCKBLOCK(fd) \
+  { u_long blocking = 0; \
+    ioctl(fd, FIONBIO, (int)(&blocking)); \
+  }
+  #define SOAP_SOCKNONBLOCK(fd) \
+  { u_long nonblocking = 1; \
+    ioctl(fd, FIONBIO, (int)(&nonblocking)); \
+  }
+#elif defined(PALM)
+  #define SOAP_SOCKBLOCK(fd) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0)&~O_NONBLOCK);
+  #define SOAP_SOCKNONBLOCK(fd) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0)|O_NONBLOCK);
+#elif defined(SYMBIAN)
+  #define SOAP_SOCKBLOCK(fd) \
+  { long blocking = 0; \
+    ioctl(fd, 0/*FIONBIO*/, &blocking); \
+  }
+  #define SOAP_SOCKNONBLOCK(fd) \
+  { long nonblocking = 1; \
+    ioctl(fd, 0/*FIONBIO*/, &nonblocking); \
+  }
+#else
+  #define SOAP_SOCKBLOCK(fd) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)&~O_NONBLOCK);
+  #define SOAP_SOCKNONBLOCK(fd) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK);
+#endif
+
+#endif
+
+#if defined(PALM) && !defined(PALM_2)
+unsigned short errno;
+#endif
+
+#ifndef PALM_1
+static const char soap_env1[42] = "http://schemas.xmlsoap.org/soap/envelope/";
+static const char soap_enc1[42] = "http://schemas.xmlsoap.org/soap/encoding/";
+static const char soap_env2[40] = "http://www.w3.org/2003/05/soap-envelope";
+static const char soap_enc2[40] = "http://www.w3.org/2003/05/soap-encoding";
+static const char soap_rpc[35] = "http://www.w3.org/2003/05/soap-rpc";
+#endif
+
+#ifndef PALM_1
+const struct soap_double_nan soap_double_nan = {0xFFFFFFFF, 0xFFFFFFFF};
+static const char soap_base64o[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char soap_base64i[81] = "\76XXX\77\64\65\66\67\70\71\72\73\74\75XXXXXXX\00\01\02\03\04\05\06\07\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31XXXXXX\32\33\34\35\36\37\40\41\42\43\44\45\46\47\50\51\52\53\54\55\56\57\60\61\62\63";
+#endif
+
+#ifndef WITH_LEAN
+static const char soap_indent[11] = "\n\t\t\t\t\t\t\t\t\t";
+/* Alternative indentation form for SOAP_XML_INDENT:
+static const char soap_indent[21] = "\n                   ";
+*/
+#endif
+
+#ifndef SOAP_CANARY
+# define SOAP_CANARY (0xC0DE)
+#endif
+
+static const char soap_padding[4] = "\0\0\0";
+#define SOAP_STR_PADDING (soap_padding)
+#define SOAP_STR_EOS (soap_padding)
+#define SOAP_NON_NULL (soap_padding)
+
+#ifndef WITH_LEAN
+static const struct soap_code_map html_entity_codes[] = /* entities for XHTML parsing */
+{ { 160, "nbsp" },
+  { 161, "iexcl" },
+  { 162, "cent" },
+  { 163, "pound" },
+  { 164, "curren" },
+  { 165, "yen" },
+  { 166, "brvbar" },
+  { 167, "sect" },
+  { 168, "uml" },
+  { 169, "copy" },
+  { 170, "ordf" },
+  { 171, "laquo" },
+  { 172, "not" },
+  { 173, "shy" },
+  { 174, "reg" },
+  { 175, "macr" },
+  { 176, "deg" },
+  { 177, "plusmn" },
+  { 178, "sup2" },
+  { 179, "sup3" },
+  { 180, "acute" },
+  { 181, "micro" },
+  { 182, "para" },
+  { 183, "middot" },
+  { 184, "cedil" },
+  { 185, "sup1" },
+  { 186, "ordm" },
+  { 187, "raquo" },
+  { 188, "frac14" },
+  { 189, "frac12" },
+  { 190, "frac34" },
+  { 191, "iquest" },
+  { 192, "Agrave" },
+  { 193, "Aacute" },
+  { 194, "Acirc" },
+  { 195, "Atilde" },
+  { 196, "Auml" },
+  { 197, "Aring" },
+  { 198, "AElig" },
+  { 199, "Ccedil" },
+  { 200, "Egrave" },
+  { 201, "Eacute" },
+  { 202, "Ecirc" },
+  { 203, "Euml" },
+  { 204, "Igrave" },
+  { 205, "Iacute" },
+  { 206, "Icirc" },
+  { 207, "Iuml" },
+  { 208, "ETH" },
+  { 209, "Ntilde" },
+  { 210, "Ograve" },
+  { 211, "Oacute" },
+  { 212, "Ocirc" },
+  { 213, "Otilde" },
+  { 214, "Ouml" },
+  { 215, "times" },
+  { 216, "Oslash" },
+  { 217, "Ugrave" },
+  { 218, "Uacute" },
+  { 219, "Ucirc" },
+  { 220, "Uuml" },
+  { 221, "Yacute" },
+  { 222, "THORN" },
+  { 223, "szlig" },
+  { 224, "agrave" },
+  { 225, "aacute" },
+  { 226, "acirc" },
+  { 227, "atilde" },
+  { 228, "auml" },
+  { 229, "aring" },
+  { 230, "aelig" },
+  { 231, "ccedil" },
+  { 232, "egrave" },
+  { 233, "eacute" },
+  { 234, "ecirc" },
+  { 235, "euml" },
+  { 236, "igrave" },
+  { 237, "iacute" },
+  { 238, "icirc" },
+  { 239, "iuml" },
+  { 240, "eth" },
+  { 241, "ntilde" },
+  { 242, "ograve" },
+  { 243, "oacute" },
+  { 244, "ocirc" },
+  { 245, "otilde" },
+  { 246, "ouml" },
+  { 247, "divide" },
+  { 248, "oslash" },
+  { 249, "ugrave" },
+  { 250, "uacute" },
+  { 251, "ucirc" },
+  { 252, "uuml" },
+  { 253, "yacute" },
+  { 254, "thorn" },
+  { 255, "yuml" },
+  {   0, NULL }
+};
+#endif
+
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+static const struct soap_code_map h_error_codes[] =
+{
+#ifdef HOST_NOT_FOUND   
+  { HOST_NOT_FOUND, "Host not found" },
+#endif
+#ifdef TRY_AGAIN
+  { TRY_AGAIN, "Try Again" },
+#endif
+#ifdef NO_RECOVERY  
+  { NO_RECOVERY, "No Recovery" },
+#endif
+#ifdef NO_DATA
+  { NO_DATA, "No Data" },
+#endif
+#ifdef NO_ADDRESS
+  { NO_ADDRESS, "No Address" },
+#endif
+  { 0, NULL }
+};
+#endif
+#endif
+
+#ifndef WITH_NOHTTP
+#ifndef WITH_LEAN
+static const struct soap_code_map h_http_error_codes[] =
+{ { 200, "OK" },
+  { 201, "Created" },
+  { 202, "Accepted" },
+  { 203, "Non-Authoritative Information" },
+  { 204, "No Content" },
+  { 205, "Reset Content" },
+  { 206, "Partial Content" },
+  { 300, "Multiple Choices" },
+  { 301, "Moved Permanently" },
+  { 302, "Found" },
+  { 303, "See Other" },
+  { 304, "Not Modified" },
+  { 305, "Use Proxy" },
+  { 307, "Temporary Redirect" },
+  { 400, "Bad Request" },
+  { 401, "Unauthorized" },
+  { 402, "Payment Required" },
+  { 403, "Forbidden" },
+  { 404, "Not Found" },
+  { 405, "Method Not Allowed" },
+  { 406, "Not Acceptable" },
+  { 407, "Proxy Authentication Required" },
+  { 408, "Request Time-out" },
+  { 409, "Conflict" },
+  { 410, "Gone" },
+  { 411, "Length Required" },
+  { 412, "Precondition Failed" },
+  { 413, "Request Entity Too Large" },
+  { 414, "Request-URI Too Large" },
+  { 415, "Unsupported Media Type" },
+  { 416, "Requested range not satisfiable" },
+  { 417, "Expectation Failed" },
+  { 500, "Internal Server Error" },
+  { 501, "Not Implemented" },
+  { 502, "Bad Gateway" },
+  { 503, "Service Unavailable" },
+  { 504, "Gateway Time-out" },
+  { 505, "HTTP Version not supported" },
+  {   0, NULL }
+};
+#endif
+#endif
+
+#ifdef WITH_OPENSSL
+static const struct soap_code_map h_ssl_error_codes[] =
+{
+#define _SSL_ERROR(e) { e, #e }
+  _SSL_ERROR(SSL_ERROR_SSL),
+  _SSL_ERROR(SSL_ERROR_ZERO_RETURN),
+  _SSL_ERROR(SSL_ERROR_WANT_READ),
+  _SSL_ERROR(SSL_ERROR_WANT_WRITE),
+  _SSL_ERROR(SSL_ERROR_WANT_CONNECT),
+  _SSL_ERROR(SSL_ERROR_WANT_X509_LOOKUP),
+  _SSL_ERROR(SSL_ERROR_SYSCALL),
+  { 0, NULL }
+};
+#endif
+
+#ifndef WITH_LEANER
+static const struct soap_code_map mime_codes[] =
+{ { SOAP_MIME_7BIT,		"7bit" },
+  { SOAP_MIME_8BIT,		"8bit" },
+  { SOAP_MIME_BINARY,		"binary" },
+  { SOAP_MIME_QUOTED_PRINTABLE, "quoted-printable" },
+  { SOAP_MIME_BASE64,		"base64" },
+  { SOAP_MIME_IETF_TOKEN,	"ietf-token" },
+  { SOAP_MIME_X_TOKEN,		"x-token" },
+  { 0,				NULL }
+};
+#endif
+
+#ifdef WIN32
+static int tcp_done = 0;
+#endif
+
+#if defined(HP_UX) && defined(HAVE_GETHOSTBYNAME_R)
+extern int h_errno;
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static int
+fsend(struct soap *soap, const char *s, size_t n)
+{ register int nwritten, err;
+#if defined(__cplusplus) && !defined(WITH_LEAN)
+  if (soap->os)
+  { soap->os->write(s, (std::streamsize)n);
+    if (soap->os->good())
+      return SOAP_OK;
+    soap->errnum = 0;
+    return SOAP_EOF;
+  }
+#endif
+  while (n)
+  { if (soap_valid_socket(soap->socket))
+    { 
+#ifndef WITH_LEAN
+      if (soap->send_timeout)
+      {
+#ifndef WIN32
+        if ((int)soap->socket >= (int)FD_SETSIZE)
+          return SOAP_FD_EXCEEDED;	/* Hint: MUST increase FD_SETSIZE */
+#endif
+        for (;;)
+        { struct timeval timeout;
+          fd_set fd;
+          register int r;
+          if (soap->send_timeout > 0)
+          { timeout.tv_sec = soap->send_timeout;
+            timeout.tv_usec = 0;
+          }
+          else
+          { timeout.tv_sec = -soap->send_timeout/1000000;
+            timeout.tv_usec = -soap->send_timeout%1000000;
+          }
+          FD_ZERO(&fd);
+          FD_SET(soap->socket, &fd);
+#ifdef WITH_OPENSSL
+          if (soap->ssl)
+            r = select((int)soap->socket + 1, &fd, &fd, &fd, &timeout);
+          else
+#endif
+          r = select((int)soap->socket + 1, NULL, &fd, &fd, &timeout);
+          if (r > 0)
+            break;
+          if (!r)
+          { soap->errnum = 0;
+            return SOAP_EOF;
+          }
+          err = soap_socket_errno(soap->socket);
+          if (err != SOAP_EINTR && err != SOAP_EAGAIN && err != SOAP_EWOULDBLOCK)
+          { soap->errnum = err;
+            return SOAP_EOF;
+          }
+        }
+      }
+#endif
+#ifdef WITH_OPENSSL
+      if (soap->ssl)
+        nwritten = SSL_write(soap->ssl, s, (int)n);
+      else if (soap->bio)
+        nwritten = BIO_write(soap->bio, s, (int)n);
+      else
+#endif
+#ifndef WITH_LEAN
+      if ((soap->omode & SOAP_IO_UDP))
+      { if (soap->peerlen)
+          nwritten = sendto(soap->socket, s, (SOAP_WINSOCKINT)n, soap->socket_flags, (struct sockaddr*)&soap->peer, (SOAP_WINSOCKINT)soap->peerlen);
+        else
+          nwritten = send(soap->socket, s, (SOAP_WINSOCKINT)n, soap->socket_flags);
+        /* retry and back-off algorithm */
+        /* TODO: this is not very clear from specs so verify and limit conditions under which we should loop (e.g. ENOBUFS) */
+        if (nwritten < 0)
+        { struct timeval timeout;
+          fd_set fd;
+          int udp_repeat;
+          int udp_delay;
+#ifndef WIN32
+          if ((int)soap->socket >= (int)FD_SETSIZE)
+            return SOAP_FD_EXCEEDED;	/* Hint: MUST increase FD_SETSIZE */
+#endif
+          if ((soap->connect_flags & SO_BROADCAST))
+            udp_repeat = 3; /* SOAP-over-UDP MULTICAST_UDP_REPEAT - 1 */
+          else
+            udp_repeat = 1; /* SOAP-over-UDP UNICAST_UDP_REPEAT - 1 */
+          udp_delay = (soap_random % 201) + 50; /* UDP_MIN_DELAY .. UDP_MAX_DELAY */
+          do
+          { timeout.tv_sec = 0;
+            timeout.tv_usec = 1000 * udp_delay; /* ms */
+            FD_ZERO(&fd);
+            FD_SET(soap->socket, &fd);
+            select((int)soap->socket + 1, NULL, NULL, &fd, &timeout);
+            if (soap->peerlen)
+              nwritten = sendto(soap->socket, s, (SOAP_WINSOCKINT)n, soap->socket_flags, (struct sockaddr*)&soap->peer, (SOAP_WINSOCKINT)soap->peerlen);
+            else
+              nwritten = send(soap->socket, s, (SOAP_WINSOCKINT)n, soap->socket_flags);
+            udp_delay <<= 1;
+            if (udp_delay > 500) /* UDP_UPPER_DELAY */
+              udp_delay = 500;
+          }
+          while (nwritten < 0 && --udp_repeat > 0);
+        }
+      }
+      else
+#endif
+#if !defined(PALM) && !defined(AS400)
+        nwritten = send(soap->socket, s, (int)n, soap->socket_flags);
+#else
+        nwritten = send(soap->socket, (void*)s, n, soap->socket_flags);
+#endif
+      if (nwritten <= 0)
+      {
+#if defined(WITH_OPENSSL) || !defined(WITH_LEAN)
+        register int r = 0;
+#endif
+        err = soap_socket_errno(soap->socket);
+#ifdef WITH_OPENSSL
+        if (soap->ssl && (r = SSL_get_error(soap->ssl, nwritten)) != SSL_ERROR_NONE && r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE)
+        { soap->errnum = err;
+          return SOAP_EOF;
+        }
+#endif
+        if (err == SOAP_EWOULDBLOCK || err == SOAP_EAGAIN)
+        {
+#ifndef WITH_LEAN
+          struct timeval timeout;
+          fd_set fd;
+#ifndef WIN32
+          if ((int)soap->socket >= (int)FD_SETSIZE)
+            return SOAP_FD_EXCEEDED; /* Hint: MUST increase FD_SETSIZE */
+#endif
+          if (soap->send_timeout > 0)
+          { timeout.tv_sec = soap->send_timeout;
+            timeout.tv_usec = 0;
+          }
+          else if (soap->send_timeout < 0)
+          { timeout.tv_sec = -soap->send_timeout/1000000;
+            timeout.tv_usec = -soap->send_timeout%1000000;
+          }
+          else
+          { timeout.tv_sec = 0;
+            timeout.tv_usec = 10000;
+          }
+          FD_ZERO(&fd);
+          FD_SET(soap->socket, &fd);
+#ifdef WITH_OPENSSL
+          if (soap->ssl && r == SSL_ERROR_WANT_READ)
+            r = select((int)soap->socket + 1, &fd, NULL, &fd, &timeout);
+          else
+            r = select((int)soap->socket + 1, NULL, &fd, &fd, &timeout);
+#else
+          r = select((int)soap->socket + 1, NULL, &fd, &fd, &timeout);
+#endif
+          if (r < 0 && (r = soap_socket_errno(soap->socket)) != SOAP_EINTR)
+          { soap->errnum = r;
+            return SOAP_EOF;
+          }
+#endif
+        }
+        else if (err && err != SOAP_EINTR)
+        { soap->errnum = err;
+          return SOAP_EOF;
+        }
+        nwritten = 0; /* and call write() again */
+      }
+    }
+    else
+    {
+#ifdef WITH_FASTCGI
+      nwritten = fwrite((void*)s, 1, n, stdout);
+      fflush(stdout);
+#else
+#ifdef UNDER_CE
+      nwritten = fwrite(s, 1, n, soap->sendfd);
+#else
+#ifdef VXWORKS
+#ifdef WMW_RPM_IO
+      if (soap->rpmreqid)
+        nwritten = (httpBlockPut(soap->rpmreqid, s, n) == 0) ? n : -1; 
+      else
+#endif
+        nwritten = fwrite(s, sizeof(char), n, fdopen(soap->sendfd, "w"));
+#else
+      nwritten = write(soap->sendfd, s, (unsigned int)n);
+#endif
+#endif
+#endif
+      if (nwritten <= 0)
+      { 
+#ifndef WITH_FASTCGI
+        err = soap_errno;
+#else
+        err = EOF;
+#endif
+        if (err && err != SOAP_EINTR && err != SOAP_EWOULDBLOCK && err != SOAP_EAGAIN)
+        { soap->errnum = err;
+          return SOAP_EOF;
+        }
+        nwritten = 0; /* and call write() again */
+      }
+    }
+    n -= nwritten;
+    s += nwritten;
+  }
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_send_raw(struct soap *soap, const char *s, size_t n)
+{ if (!n)
+    return SOAP_OK;
+  if (soap->mode & SOAP_IO_LENGTH)
+  { soap->count += n;
+#ifndef WITH_LEANER
+    if (soap->fpreparesend && (soap->mode & SOAP_IO) != SOAP_IO_STORE)
+      return soap->error = soap->fpreparesend(soap, s, n);
+#endif
+    return SOAP_OK;
+  }
+  if (soap->mode & SOAP_IO)
+  { register size_t i = SOAP_BUFLEN - soap->bufidx;
+    while (n >= i)
+    { memcpy(soap->buf + soap->bufidx, s, i);
+      soap->bufidx = SOAP_BUFLEN;
+      if (soap_flush(soap))
+        return soap->error;
+      s += i;
+      n -= i;
+      i = SOAP_BUFLEN;
+    }
+    memcpy(soap->buf + soap->bufidx, s, n);
+    soap->bufidx += n;
+    return SOAP_OK;
+  }
+  return soap_flush_raw(soap, s, n);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_flush(struct soap *soap)
+{ register size_t n = soap->bufidx;
+  if (n)
+  { soap->bufidx = 0;
+#ifdef WITH_ZLIB
+    if (soap->mode & SOAP_ENC_ZLIB)
+    { soap->d_stream->next_in = (Byte*)soap->buf;
+      soap->d_stream->avail_in = (unsigned int)n;
+#ifdef WITH_GZIP
+      soap->z_crc = crc32(soap->z_crc, (Byte*)soap->buf, (unsigned int)n);
+#endif
+      do
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Deflating %u bytes\n", soap->d_stream->avail_in));
+        if (deflate(soap->d_stream, Z_NO_FLUSH) != Z_OK)
+        { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Unable to deflate: %s\n", soap->d_stream->msg?soap->d_stream->msg:SOAP_STR_EOS));
+          return soap->error = SOAP_ZLIB_ERROR;
+        }
+        if (!soap->d_stream->avail_out)
+        { if (soap_flush_raw(soap, soap->z_buf, SOAP_BUFLEN))
+            return soap->error;
+          soap->d_stream->next_out = (Byte*)soap->z_buf;
+          soap->d_stream->avail_out = SOAP_BUFLEN;
+        }
+      } while (soap->d_stream->avail_in);
+    }
+    else
+#endif
+      return soap_flush_raw(soap, soap->buf, n);
+  }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_flush_raw(struct soap *soap, const char *s, size_t n)
+{ if ((soap->mode & SOAP_IO) == SOAP_IO_STORE)
+  { register char *t;
+    if (!(t = (char*)soap_push_block(soap, NULL, n)))
+      return soap->error = SOAP_EOM;
+    memcpy(t, s, n);
+#ifndef WITH_LEANER
+    if (soap->fpreparesend)
+      return soap->error = soap->fpreparesend(soap, s, n);
+#endif
+    return SOAP_OK;
+  }
+#ifndef WITH_LEANER
+  if ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK)
+  { char t[16];
+    sprintf(t, "\r\n%lX\r\n" + (soap->chunksize ? 0 : 2), (unsigned long)n);
+    DBGMSG(SENT, t, strlen(t));
+    if ((soap->error = soap->fsend(soap, t, strlen(t))))
+      return soap->error;
+    soap->chunksize += n;
+  }
+  DBGMSG(SENT, s, n);
+#endif
+  return soap->error = soap->fsend(soap, s, n);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_send(struct soap *soap, const char *s)
+{ if (s)
+    return soap_send_raw(soap, s, strlen(s));
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_send2(struct soap *soap, const char *s1, const char *s2)
+{ if (soap_send(soap, s1))
+    return soap->error;
+  return soap_send(soap, s2);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_send3(struct soap *soap, const char *s1, const char *s2, const char *s3)
+{ if (soap_send(soap, s1)
+   || soap_send(soap, s2))
+    return soap->error;
+  return soap_send(soap, s3);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static size_t
+frecv(struct soap *soap, char *s, size_t n)
+{ register int r;
+#ifndef WITH_LEAN
+  register int retries = 100; /* max 100 retries with non-blocking sockets */
+#endif
+  soap->errnum = 0;
+#if defined(__cplusplus) && !defined(WITH_LEAN)
+  if (soap->is)
+  { if (soap->is->good())
+      return soap->is->read(s, (std::streamsize)n).gcount();
+    return 0;
+  }
+#endif
+  if (soap_valid_socket(soap->socket))
+  { for (;;)
+    { 
+#ifdef WITH_OPENSSL
+      register int err = 0;
+#endif
+#ifndef WITH_LEAN
+#ifdef WITH_OPENSSL
+      if (soap->recv_timeout && !soap->ssl) /* SSL: sockets are nonblocking */
+#else
+      if (soap->recv_timeout)
+#endif
+      {
+#ifndef WIN32
+        if ((int)soap->socket >= (int)FD_SETSIZE)
+        { soap->error = SOAP_FD_EXCEEDED;
+          return 0;	/* Hint: MUST increase FD_SETSIZE */
+        }
+#endif
+        for (;;)
+        { struct timeval timeout;
+          fd_set fd;
+          if (soap->recv_timeout > 0)
+          { timeout.tv_sec = soap->recv_timeout;
+            timeout.tv_usec = 0;
+          }
+          else
+          { timeout.tv_sec = -soap->recv_timeout/1000000;
+            timeout.tv_usec = -soap->recv_timeout%1000000;
+          }
+          FD_ZERO(&fd);
+          FD_SET(soap->socket, &fd);
+          r = select((int)soap->socket + 1, &fd, NULL, &fd, &timeout);
+          if (r > 0)
+            break;
+          if (!r)
+          { soap->errnum = 0;
+            return 0;
+          }
+          r = soap_socket_errno(soap->socket);
+          if (r != SOAP_EINTR && r != SOAP_EAGAIN && r != SOAP_EWOULDBLOCK)
+          { soap->errnum = r;
+            return 0;
+          }
+        }
+      }
+#endif
+#ifdef WITH_OPENSSL
+      if (soap->ssl)
+      { r = SSL_read(soap->ssl, s, (int)n);
+        if (r > 0)
+          return (size_t)r;
+        err = SSL_get_error(soap->ssl, r);
+        if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
+          return 0;
+      }
+      else if (soap->bio)
+      { r = BIO_read(soap->bio, s, (int)n);
+        if (r > 0)
+          return (size_t)r;
+        return 0;
+      }
+      else
+#endif
+      { 
+#ifndef WITH_LEAN
+        if ((soap->omode & SOAP_IO_UDP))
+        { SOAP_SOCKLEN_T k = (SOAP_SOCKLEN_T)sizeof(soap->peer);
+          memset((void*)&soap->peer, 0, sizeof(soap->peer));
+          r = recvfrom(soap->socket, s, (SOAP_WINSOCKINT)n, soap->socket_flags, (struct sockaddr*)&soap->peer, &k);	/* portability note: see SOAP_SOCKLEN_T definition in stdsoap2.h */
+          soap->peerlen = (size_t)k;
+#ifndef WITH_IPV6
+          soap->ip = ntohl(soap->peer.sin_addr.s_addr);
+#endif
+        }
+        else
+#endif
+          r = recv(soap->socket, s, (int)n, soap->socket_flags);
+#ifdef PALM
+        /* CycleSyncDisplay(curStatusMsg); */
+#endif
+        if (r >= 0)
+          return (size_t)r;
+        r = soap_socket_errno(soap->socket);
+        if (r != SOAP_EINTR && r != SOAP_EAGAIN && r != SOAP_EWOULDBLOCK)
+        { soap->errnum = r;
+          return 0;
+        }
+      }
+#ifndef WITH_LEAN
+      { struct timeval timeout;
+        fd_set fd;
+        if (soap->recv_timeout > 0)
+        { timeout.tv_sec = soap->recv_timeout;
+          timeout.tv_usec = 0;
+        }
+        else if (soap->recv_timeout < 0)
+        { timeout.tv_sec = -soap->recv_timeout/1000000;
+          timeout.tv_usec = -soap->recv_timeout%1000000;
+        }
+        else
+        { timeout.tv_sec = 5;
+          timeout.tv_usec = 0;
+        }
+#ifndef WIN32
+        if ((int)soap->socket >= (int)FD_SETSIZE)
+        { soap->error = SOAP_FD_EXCEEDED;
+          return 0;	/* Hint: MUST increase FD_SETSIZE */
+        }
+#endif
+        FD_ZERO(&fd);
+        FD_SET(soap->socket, &fd);
+#ifdef WITH_OPENSSL
+        if (soap->ssl && err == SSL_ERROR_WANT_WRITE)
+          r = select((int)soap->socket + 1, NULL, &fd, &fd, &timeout);
+        else
+          r = select((int)soap->socket + 1, &fd, NULL, &fd, &timeout);
+#else
+        r = select((int)soap->socket + 1, &fd, NULL, &fd, &timeout);
+#endif
+        if (!r && soap->recv_timeout)
+        { soap->errnum = 0;
+          return 0;
+        }
+        if (r < 0)
+        { r = soap_socket_errno(soap->socket);
+          if (r != SOAP_EINTR && r != SOAP_EAGAIN && r != SOAP_EWOULDBLOCK)
+          { soap->errnum = r;
+            return 0;
+          }
+        }
+        if (retries-- <= 0)
+        { soap->errnum = soap_socket_errno(soap->socket);
+          return 0;
+        }
+      }
+#endif
+#ifdef PALM
+      r = soap_socket_errno(soap->socket);
+      if (r != SOAP_EINTR && retries-- <= 0)
+      { soap->errnum = r;
+        return 0;
+      }
+#endif
+    }
+  }
+#ifdef WITH_FASTCGI
+  return fread(s, 1, n, stdin);
+#else
+#ifdef UNDER_CE
+  return fread(s, 1, n, soap->recvfd);
+#else
+#ifdef WMW_RPM_IO
+  if (soap->rpmreqid)
+    r = httpBlockRead(soap->rpmreqid, s, n);
+  else
+#endif
+    r = read(soap->recvfd, s, (unsigned int)n);
+  if (r >= 0)
+    return (size_t)r;
+  soap->errnum = soap_errno;
+  return 0;
+#endif
+#endif
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static size_t
+frecv_stop(struct soap *soap, char *s, size_t n)
+{ return 0;
+}
+#endif
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static soap_wchar
+soap_getchunkchar(struct soap *soap)
+{ if (soap->bufidx < soap->buflen)
+    return soap->buf[soap->bufidx++];
+  soap->bufidx = 0;
+  soap->buflen = soap->chunkbuflen = soap->frecv(soap, soap->buf, SOAP_BUFLEN);
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Read %u bytes from socket %d\n", (unsigned int)soap->buflen, soap->socket));
+  DBGMSG(RECV, soap->buf, soap->buflen);
+  if (soap->buflen)
+    return soap->buf[soap->bufidx++];
+  return EOF;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+static int
+soap_isxdigit(int c)
+{ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_recv_raw(struct soap *soap)
+{ register size_t ret;
+#ifdef WITH_ZLIB
+  if (soap->mode & SOAP_ENC_ZLIB)
+  { if (soap->d_stream->next_out == Z_NULL)
+      return EOF;
+    if (soap->d_stream->avail_in || !soap->d_stream->avail_out)
+    { register int r;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Inflating\n"));
+      soap->d_stream->next_out = (Byte*)soap->buf;
+      soap->d_stream->avail_out = SOAP_BUFLEN;
+      r = inflate(soap->d_stream, Z_NO_FLUSH);
+      if (r == Z_OK || r == Z_STREAM_END)
+      { soap->bufidx = 0;
+        ret = soap->buflen = SOAP_BUFLEN - soap->d_stream->avail_out;
+        if (soap->zlib_in == SOAP_ZLIB_GZIP)
+          soap->z_crc = crc32(soap->z_crc, (Byte*)soap->buf, (unsigned int)ret);
+        if (r == Z_STREAM_END)
+        { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Inflated %lu->%lu bytes\n", soap->d_stream->total_in, soap->d_stream->total_out));
+          soap->z_ratio_in = (float)soap->d_stream->total_in / (float)soap->d_stream->total_out;
+          soap->d_stream->next_out = Z_NULL;
+        }
+        if (ret)
+        { soap->count += ret;
+          DBGLOG(RECV, SOAP_MESSAGE(fdebug, "\n---- decompressed ----\n"));
+          DBGMSG(RECV, soap->buf, ret);
+          DBGLOG(RECV, SOAP_MESSAGE(fdebug, "\n----\n"));
+          return SOAP_OK;
+        }
+      }
+      else if (r != Z_BUF_ERROR)
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Inflate error: %s\n", soap->d_stream->msg?soap->d_stream->msg:SOAP_STR_EOS));
+        soap->d_stream->next_out = Z_NULL;
+        soap->error = SOAP_ZLIB_ERROR;
+        return EOF;
+      }
+    }
+zlib_again:
+    if ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK && !soap->chunksize)
+    { memcpy(soap->buf, soap->z_buf, SOAP_BUFLEN);
+      soap->buflen = soap->z_buflen;
+    }
+    DBGLOG(RECV, SOAP_MESSAGE(fdebug, "\n---- compressed ----\n"));
+  }
+#endif
+#ifndef WITH_NOHTTP
+  if ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK) /* read HTTP chunked transfer */
+  { for (;;)
+    { register soap_wchar c;
+      char *t, tmp[8];
+      if (soap->chunksize)
+      { soap->buflen = ret = soap->frecv(soap, soap->buf, soap->chunksize > SOAP_BUFLEN ? SOAP_BUFLEN : soap->chunksize);
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Getting chunk: read %u bytes\n", (unsigned int)ret));
+        DBGMSG(RECV, soap->buf, ret);
+        soap->bufidx = 0;
+        soap->chunksize -= ret;
+        break;
+      }
+      t = tmp;
+      if (!soap->chunkbuflen)
+      { soap->chunkbuflen = ret = soap->frecv(soap, soap->buf, SOAP_BUFLEN);
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Read %u bytes (chunked) from socket %d\n", (unsigned int)ret, soap->socket));
+        DBGMSG(RECV, soap->buf, ret);
+        soap->bufidx = 0;
+        if (!ret)
+          return soap->ahead = EOF;
+      }
+      else
+        soap->bufidx = soap->buflen;
+      soap->buflen = soap->chunkbuflen;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Getting chunk size (idx=%u len=%u)\n", (unsigned int)soap->bufidx, (unsigned int)soap->buflen));
+      while (!soap_isxdigit((int)(c = soap_getchunkchar(soap))))
+      { if ((int)c == EOF)
+          return soap->ahead = EOF;
+      }
+      do
+        *t++ = (char)c;
+      while (soap_isxdigit((int)(c = soap_getchunkchar(soap))) && t - tmp < 7);
+      while ((int)c != EOF && c != '\n')
+        c = soap_getchunkchar(soap);
+      if ((int)c == EOF)
+        return soap->ahead = EOF;
+      *t = '\0';
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Chunk size = %s (hex)\n", tmp));
+      soap->chunksize = soap_strtoul(tmp, &t, 16);
+      if (!soap->chunksize)
+      { soap->chunkbuflen = 0;
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "End of chunked message\n"));
+        while ((int)c != EOF && c != '\n')
+          c = soap_getchunkchar(soap);
+        ret = 0;
+        soap->ahead = EOF;
+	break;
+      }
+      soap->buflen = soap->bufidx + soap->chunksize;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Moving buf len to idx=%u len=%u (%s)\n", (unsigned int)soap->bufidx, (unsigned int)soap->buflen, tmp));
+      if (soap->buflen > soap->chunkbuflen)
+      { soap->buflen = soap->chunkbuflen;
+        soap->chunksize -= soap->buflen - soap->bufidx;
+        soap->chunkbuflen = 0;
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Passed end of buffer for chunked HTTP (%u bytes left)\n", (unsigned int)(soap->buflen - soap->bufidx)));
+      }
+      else if (soap->chunkbuflen)
+        soap->chunksize = 0;
+      ret = soap->buflen - soap->bufidx;
+      if (ret)
+        break;
+    }
+  }
+  else
+#endif
+  { soap->bufidx = 0;
+    soap->buflen = ret = soap->frecv(soap, soap->buf, SOAP_BUFLEN);
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Read %u bytes from socket %d\n", (unsigned int)ret, soap->socket));
+    DBGMSG(RECV, soap->buf, ret);
+  }
+#ifndef WITH_LEANER
+  if (soap->fpreparerecv && (soap->error = soap->fpreparerecv(soap, soap->buf, ret)))
+    return soap->error;
+#endif
+#ifdef WITH_ZLIB
+  if (soap->mode & SOAP_ENC_ZLIB)
+  { register int r;
+    memcpy(soap->z_buf, soap->buf, SOAP_BUFLEN);
+    soap->d_stream->next_in = (Byte*)(soap->z_buf + soap->bufidx);
+    soap->d_stream->avail_in = (unsigned int)ret;
+    soap->d_stream->next_out = (Byte*)soap->buf;
+    soap->d_stream->avail_out = SOAP_BUFLEN;
+    r = inflate(soap->d_stream, Z_NO_FLUSH);
+    if (r == Z_OK || r == Z_STREAM_END)
+    { soap->bufidx = 0;
+      soap->z_buflen = soap->buflen;
+      soap->buflen = SOAP_BUFLEN - soap->d_stream->avail_out;
+      if (soap->zlib_in == SOAP_ZLIB_GZIP)
+        soap->z_crc = crc32(soap->z_crc, (Byte*)soap->buf, (unsigned int)soap->buflen);
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Inflated %u bytes\n", (unsigned int)soap->buflen));
+      if (ret && !soap->buflen)
+        goto zlib_again;
+      ret = soap->buflen;
+      if (r == Z_STREAM_END)
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Inflated total %lu->%lu bytes\n", soap->d_stream->total_in, soap->d_stream->total_out));
+        soap->z_ratio_in = (float)soap->d_stream->total_in / (float)soap->d_stream->total_out;
+        soap->d_stream->next_out = Z_NULL;
+      }
+      DBGLOG(RECV, SOAP_MESSAGE(fdebug, "\n---- decompressed ----\n"));
+      DBGMSG(RECV, soap->buf, ret);
+    }
+    else
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Unable to inflate: (%d) %s\n", r, soap->d_stream->msg?soap->d_stream->msg:SOAP_STR_EOS));
+      soap->d_stream->next_out = Z_NULL;
+      soap->error = SOAP_ZLIB_ERROR;
+      return EOF;
+    }
+  }
+#endif
+  soap->count += ret;
+  return !ret;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_recv(struct soap *soap)
+{ 
+#ifndef WITH_LEANER
+  if (soap->mode & SOAP_ENC_DIME)
+  { if (soap->dime.buflen)
+    { char *s;
+      int i;
+      unsigned char tmp[12];
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "DIME hdr for chunked DIME is in buffer\n"));
+      soap->count += soap->dime.buflen - soap->buflen;
+      soap->buflen = soap->dime.buflen;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Skip padding (%ld bytes)\n", -(long)soap->dime.size&3));
+      for (i = -(long)soap->dime.size&3; i > 0; i--)
+      { soap->bufidx++;
+        if (soap->bufidx >= soap->buflen)
+          if (soap_recv_raw(soap))
+            return EOF;
+      }
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Get DIME hdr for next chunk\n"));
+      s = (char*)tmp;
+      for (i = 12; i > 0; i--)
+      { *s++ = soap->buf[soap->bufidx++];
+        if (soap->bufidx >= soap->buflen)
+          if (soap_recv_raw(soap))
+            return EOF;
+      }
+      soap->dime.flags = tmp[0] & 0x7;
+      soap->dime.size = ((size_t)tmp[8] << 24) | ((size_t)tmp[9] << 16) | ((size_t)tmp[10] << 8) | ((size_t)tmp[11]);
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Get DIME chunk (%u bytes)\n", (unsigned int)soap->dime.size));
+      if (soap->dime.flags & SOAP_DIME_CF)
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "More chunking\n"));
+        soap->dime.chunksize = soap->dime.size;
+        if (soap->buflen - soap->bufidx >= soap->dime.size)
+        { soap->dime.buflen = soap->buflen;
+          soap->buflen = soap->bufidx + soap->dime.chunksize;
+        }
+        else
+          soap->dime.chunksize -= soap->buflen - soap->bufidx;
+      }
+      else
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Last chunk\n"));
+        soap->dime.buflen = 0;
+        soap->dime.chunksize = 0;
+      }
+      soap->count = soap->buflen - soap->bufidx;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%u bytes remaining\n", (unsigned int)soap->count));
+      return SOAP_OK;
+    }
+    if (soap->dime.chunksize)
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Get next DIME hdr for chunked DIME (%u bytes chunk)\n", (unsigned int)soap->dime.chunksize));
+      if (soap_recv_raw(soap))
+        return EOF;
+      if (soap->buflen - soap->bufidx >= soap->dime.chunksize)
+      { soap->dime.buflen = soap->buflen;
+        soap->count -= soap->buflen - soap->bufidx - soap->dime.chunksize;
+        soap->buflen = soap->bufidx + soap->dime.chunksize;
+      }
+      else
+        soap->dime.chunksize -= soap->buflen - soap->bufidx;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%lu bytes remaining, count=%u\n", (unsigned long)(soap->buflen-soap->bufidx), (unsigned int)soap->count));
+      return SOAP_OK;
+    }
+  }
+#endif
+  return soap_recv_raw(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+soap_wchar
+SOAP_FMAC2
+soap_getchar(struct soap *soap)
+{ register soap_wchar c;
+  c = soap->ahead;
+  if (c)
+  { if (c != EOF)
+      soap->ahead = 0;
+    return c;
+  }
+  return soap_get1(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+const struct soap_code_map*
+SOAP_FMAC2
+soap_code(const struct soap_code_map *code_map, const char *str)
+{ if (code_map && str)
+  { while (code_map->string)
+    { if (!strcmp(str, code_map->string)) /* case sensitive */
+        return code_map;
+      code_map++;
+    }
+  }
+  return NULL;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+long
+SOAP_FMAC2
+soap_code_int(const struct soap_code_map *code_map, const char *str, long other)
+{ if (code_map)
+  { while (code_map->string)
+    { if (!soap_tag_cmp(str, code_map->string)) /* case insensitive */
+        return code_map->code;
+      code_map++;
+    }
+  }
+  return other;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_code_str(const struct soap_code_map *code_map, long code)
+{ if (!code_map)
+    return NULL;
+  while (code_map->code != code && code_map->string)
+    code_map++;
+  return code_map->string;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+long
+SOAP_FMAC2
+soap_code_bits(const struct soap_code_map *code_map, const char *str)
+{ register long bits = 0;
+  if (code_map)
+  { while (str && *str)
+    { const struct soap_code_map *p;
+      for (p = code_map; p->string; p++)
+      { register size_t n = strlen(p->string);
+        if (!strncmp(p->string, str, n) && soap_blank(str[n]))
+        { bits |= p->code;
+          str += n;
+          while (*str > 0 && *str <= 32)
+            str++;
+          break;
+        }
+      }
+      if (!p->string)
+        return 0;
+    }
+  }
+  return bits;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_code_list(struct soap *soap, const struct soap_code_map *code_map, long code)
+{ register char *t = soap->tmpbuf;
+  if (code_map)
+  { while (code_map->string)
+    { if (code_map->code & code)
+      { register const char *s = code_map->string;
+        if (t != soap->tmpbuf)
+          *t++ = ' ';
+        while (*s && t < soap->tmpbuf + sizeof(soap->tmpbuf) - 1)
+          *t++ = *s++;
+        if (t == soap->tmpbuf + sizeof(soap->tmpbuf) - 1)
+          break;
+      }
+      code_map++;
+    }
+  }
+  *t = '\0';
+  return soap->tmpbuf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+static soap_wchar
+soap_char(struct soap *soap)
+{ char tmp[8];
+  register int i;
+  register soap_wchar c;
+  register char *s = tmp;
+  for (i = 0; i < 7; i++)
+  { c = soap_get1(soap);
+    if (c == ';' || (int)c == EOF)
+      break;
+    *s++ = (char)c;
+  }
+  *s = '\0';
+  if (*tmp == '#')
+  { if (tmp[1] == 'x' || tmp[1] == 'X')
+      return soap_strtol(tmp + 2, NULL, 16);
+    return atol(tmp + 1);
+  }
+  if (!strcmp(tmp, "lt"))
+    return '<';
+  if (!strcmp(tmp, "gt"))
+    return '>';
+  if (!strcmp(tmp, "amp"))
+    return '&';
+  if (!strcmp(tmp, "quot"))
+    return '"';
+  if (!strcmp(tmp, "apos"))
+    return '\'';
+#ifndef WITH_LEAN
+  return (soap_wchar)soap_code_int(html_entity_codes, tmp, SOAP_UNKNOWN_CHAR);
+#else
+  return SOAP_UNKNOWN_CHAR; /* use this to represent unknown code */
+#endif
+}
+#endif
+
+/******************************************************************************/
+#ifdef WITH_LEAN
+#ifndef PALM_1
+soap_wchar
+soap_get0(struct soap *soap)
+{ if (soap->bufidx >= soap->buflen && soap_recv(soap))
+    return EOF;
+  return (unsigned char)soap->buf[soap->bufidx];
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifdef WITH_LEAN
+#ifndef PALM_1
+soap_wchar
+soap_get1(struct soap *soap)
+{ if (soap->bufidx >= soap->buflen && soap_recv(soap))
+    return EOF;
+  return (unsigned char)soap->buf[soap->bufidx++];
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+soap_wchar
+SOAP_FMAC2
+soap_get(struct soap *soap)
+{ register soap_wchar c;
+  c = soap->ahead;
+  if (c)
+  { if ((int)c != EOF)
+      soap->ahead = 0;
+  }
+  else
+    c = soap_get1(soap);
+  while ((int)c != EOF)
+  { if (soap->cdata)
+    { if (c == ']')
+      { c = soap_get1(soap);
+        if (c == ']')
+        { c = soap_get0(soap);
+          if (c == '>')
+          { soap->cdata = 0;
+            soap_get1(soap);
+            c = soap_get1(soap);
+          }
+          else
+          { soap_unget(soap, ']');
+            return ']';
+          }
+        }
+        else
+        { soap_revget1(soap);
+          return ']';
+        }
+      }
+      else
+        return c;
+    }
+    switch (c)
+    { case '<':
+        do c = soap_get1(soap);
+        while (soap_blank(c));
+        if (c == '!' || c == '?' || c == '%')
+        { register int k = 1;
+          if (c == '!')
+          { c = soap_get1(soap);
+            if (c == '[')
+            { do c = soap_get1(soap);
+              while ((int)c != EOF && c != '[');
+              if ((int)c == EOF)
+                break;
+              soap->cdata = 1;
+              c = soap_get1(soap);
+              continue;
+            }
+            if (c == '-' && (c = soap_get1(soap)) == '-')
+            { do
+              { c = soap_get1(soap);
+                if (c == '-' && (c = soap_get1(soap)) == '-')
+                  break;
+              } while ((int)c != EOF);
+            }
+          }
+          else if (c == '?')
+            c = soap_get_pi(soap);
+          while ((int)c != EOF)
+          { if (c == '<')
+              k++;
+            else if (c == '>')
+            { if (--k <= 0)
+                break;
+            }
+            c = soap_get1(soap);
+          }
+          if ((int)c == EOF)
+            break;
+          c = soap_get1(soap);
+          continue;
+        }
+        if (c == '/')
+          return SOAP_TT;
+        soap_revget1(soap);
+        return SOAP_LT;
+      case '>':
+        return SOAP_GT;
+      case '"':
+        return SOAP_QT;
+      case '\'':
+        return SOAP_AP;
+      case '&':
+        return soap_char(soap) | 0x80000000;
+    }
+    break;
+  }
+  return c;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+static soap_wchar
+soap_get_pi(struct soap *soap)
+{ char buf[64];
+  register char *s = buf;
+  register int i = sizeof(buf);
+  register soap_wchar c = soap_getchar(soap);
+  /* This is a quick way to parse XML PI and we could use a callback instead to
+   * enable applications to intercept processing instructions */
+  while ((int)c != EOF && c != '?')
+  { if (--i > 0)
+    { if (soap_blank(c))
+        c = ' ';
+      *s++ = (char)c;
+    }
+    c = soap_getchar(soap);
+  }
+  *s = '\0';
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "XML PI <?%s?>\n", buf));
+  if (!strncmp(buf, "xml ", 4))
+  { s = strstr(buf, " encoding=");
+    if (s && s[10])
+    { if (!soap_tag_cmp(s + 11, "iso-8859-1*")
+       || !soap_tag_cmp(s + 11, "latin1*"))
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Switching to latin1 encoding\n"));
+        soap->mode |= SOAP_ENC_LATIN;
+      }
+      else if (!soap_tag_cmp(s + 11, "utf-8*"))
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Switching to utf-8 encoding\n"));
+        soap->mode &= ~SOAP_ENC_LATIN;
+      }
+    }
+  }
+  if ((int)c != EOF)
+    c = soap_getchar(soap);
+  return c;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_move(struct soap *soap, long n)
+{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Moving %ld bytes forward\n", (long)n));
+  for (; n > 0; n--)
+    if ((int)soap_getchar(soap) == EOF)
+      return SOAP_EOF;
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+size_t
+SOAP_FMAC2
+soap_tell(struct soap *soap)
+{ return soap->count - soap->buflen + soap->bufidx - (soap->ahead != 0);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_pututf8(struct soap *soap, register unsigned long c)
+{ char tmp[16];
+  if (c < 0x80 && c > 0)
+  { *tmp = (char)c;
+    return soap_send_raw(soap, tmp, 1);
+  }
+#ifndef WITH_LEAN
+  if (c >= 0x80)
+  { register char *t = tmp;
+    if (c < 0x0800)
+      *t++ = (char)(0xC0 | ((c >> 6) & 0x1F));
+    else
+    { if (c < 0x010000)
+        *t++ = (char)(0xE0 | ((c >> 12) & 0x0F));
+      else
+      { if (c < 0x200000)
+          *t++ = (char)(0xF0 | ((c >> 18) & 0x07));
+        else
+        { if (c < 0x04000000)
+            *t++ = (char)(0xF8 | ((c >> 24) & 0x03));
+          else
+          { *t++ = (char)(0xFC | ((c >> 30) & 0x01));
+            *t++ = (char)(0x80 | ((c >> 24) & 0x3F));
+          }
+          *t++ = (char)(0x80 | ((c >> 18) & 0x3F));
+        }     
+        *t++ = (char)(0x80 | ((c >> 12) & 0x3F));
+      }
+      *t++ = (char)(0x80 | ((c >> 6) & 0x3F));
+    }
+    *t++ = (char)(0x80 | (c & 0x3F));
+    *t = '\0';
+  }
+#else
+  sprintf(tmp, "&#%lu;", c);
+#endif
+  return soap_send(soap, tmp);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+soap_wchar
+SOAP_FMAC2
+soap_getutf8(struct soap *soap)
+{ register soap_wchar c, c1, c2, c3, c4;
+  c = soap->ahead;
+  if (c > 0xFF)
+  { soap->ahead = 0;
+    return c;
+  }
+again:
+  c = soap_get(soap);
+  if (c < 0x80 || (soap->mode & SOAP_ENC_LATIN))
+    return c;
+  c1 = soap_get1(soap);
+  if (c1 < 0x80)
+  { soap_revget1(soap); /* doesn't look like this is UTF8 */
+    return c;
+  }
+  c1 &= 0x3F;
+  if (c < 0xE0)
+    return ((soap_wchar)(c & 0x1F) << 6) | c1;
+  c2 = (soap_wchar)soap_get1(soap) & 0x3F;
+  if (c == 0xEF && c1 == 0x3B && c2 == 0x3F)	/* ignore UTF-8 BOM */
+    goto again;
+  if (c < 0xF0)
+    return ((soap_wchar)(c & 0x0F) << 12) | (c1 << 6) | c2;
+  c3 = (soap_wchar)soap_get1(soap) & 0x3F;
+  if (c < 0xF8)
+    return ((soap_wchar)(c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
+  c4 = (soap_wchar)soap_get1(soap) & 0x3F;
+  if (c < 0xFC)
+    return ((soap_wchar)(c & 0x03) << 24) | (c1 << 18) | (c2 << 12) | (c3 << 6) | c4;
+  return ((soap_wchar)(c & 0x01) << 30) | (c1 << 24) | (c2 << 18) | (c3 << 12) | (c4 << 6) | (soap_wchar)(soap_get1(soap) & 0x3F);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_puthex(struct soap *soap, const unsigned char *s, int n)
+{ char d[2];
+  register int i;
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+  { if (!(soap->dom->data = soap_s2hex(soap, s, NULL, n)))
+      return soap->error;
+    return SOAP_OK;
+  }
+#endif
+  for (i = 0; i < n; i++)
+  { register int m = *s++;
+    d[0] = (char)((m >> 4) + (m > 159 ? '7' : '0'));
+    m &= 0x0F;
+    d[1] = (char)(m + (m > 9 ? '7' : '0'));
+    if (soap_send_raw(soap, d, 2))
+      return soap->error;
+  }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+unsigned char*
+SOAP_FMAC2
+soap_gethex(struct soap *soap, int *n)
+{
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+  { soap->dom->data = soap_string_in(soap, 0, -1, -1);
+    return (unsigned char*)soap_hex2s(soap, soap->dom->data, NULL, 0, n);
+  }
+#endif
+#ifdef WITH_FAST
+  soap->labidx = 0;
+  for (;;)
+  { register char *s;
+    register size_t i, k;
+    if (soap_append_lab(soap, NULL, 0))
+      return NULL;
+    s = soap->labbuf + soap->labidx;
+    k = soap->lablen - soap->labidx;
+    soap->labidx = soap->lablen;
+    for (i = 0; i < k; i++)
+    { register char d1, d2;
+      register soap_wchar c;
+      c = soap_get(soap);
+      if (soap_isxdigit(c))
+      { d1 = (char)c;
+        c = soap_get(soap); 
+        if (soap_isxdigit(c))
+          d2 = (char)c;
+        else 
+        { soap->error = SOAP_TYPE;
+          return NULL;
+        }
+      }
+      else
+      { unsigned char *p;
+        soap_unget(soap, c);
+        if (n)
+          *n = (int)(soap->lablen + i - k);
+        p = (unsigned char*)soap_malloc(soap, soap->lablen + i - k);
+        if (p)
+          memcpy(p, soap->labbuf, soap->lablen + i - k);
+        return p;
+      }
+      *s++ = ((d1 >= 'A' ? (d1 & 0x7) + 9 : d1 - '0') << 4) + (d2 >= 'A' ? (d2 & 0x7) + 9 : d2 - '0');
+    }
+  }
+#else
+  if (soap_new_block(soap) == NULL)
+    return NULL;
+  for (;;)
+  { register int i;
+    register char *s = (char*)soap_push_block(soap, NULL, SOAP_BLKLEN);
+    if (!s)
+    { soap_end_block(soap, NULL);
+      return NULL;
+    }
+    for (i = 0; i < SOAP_BLKLEN; i++)
+    { register char d1, d2;
+      register soap_wchar c = soap_get(soap);
+      if (soap_isxdigit(c))
+      { d1 = (char)c;
+        c = soap_get(soap); 
+        if (soap_isxdigit(c))
+          d2 = (char)c;
+        else 
+        { soap_end_block(soap, NULL);
+          soap->error = SOAP_TYPE;
+          return NULL;
+        }
+      }
+      else
+      { unsigned char *p;
+        soap_unget(soap, c);
+        if (n)
+          *n = (int)soap_size_block(soap, NULL, i);
+        p = (unsigned char*)soap_save_block(soap, NULL, 0);
+        return p;
+      }
+      *s++ = ((d1 >= 'A' ? (d1 & 0x7) + 9 : d1 - '0') << 4) + (d2 >= 'A' ? (d2 & 0x7) + 9 : d2 - '0');
+    }
+  }
+#endif
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_putbase64(struct soap *soap, const unsigned char *s, int n)
+{ register int i;
+  register unsigned long m;
+  char d[4];
+  if (!s)
+    return SOAP_OK;
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+  { if (!(soap->dom->data = soap_s2base64(soap, s, NULL, n)))
+      return soap->error;
+    return SOAP_OK;
+  }
+#endif
+  for (; n > 2; n -= 3, s += 3)
+  { m = s[0];
+    m = (m << 8) | s[1];
+    m = (m << 8) | s[2];
+    for (i = 4; i > 0; m >>= 6)
+      d[--i] = soap_base64o[m & 0x3F];
+    if (soap_send_raw(soap, d, 4))
+      return soap->error;
+  }
+  if (n > 0)
+  { m = 0;
+    for (i = 0; i < n; i++)
+      m = (m << 8) | *s++;
+    for (; i < 3; i++)
+      m <<= 8;
+    for (i++; i > 0; m >>= 6)
+      d[--i] = soap_base64o[m & 0x3F];
+    for (i = 3; i > n; i--)
+      d[i] = '=';
+    if (soap_send_raw(soap, d, 4))
+      return soap->error;
+  }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+unsigned char*
+SOAP_FMAC2
+soap_getbase64(struct soap *soap, int *n, int malloc_flag)
+{ 
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+  { soap->dom->data = soap_string_in(soap, 0, -1, -1);
+    return (unsigned char*)soap_base642s(soap, soap->dom->data, NULL, 0, n);
+  }
+#endif
+#ifdef WITH_FAST
+  soap->labidx = 0;
+  for (;;)
+  { register size_t i, k;
+    register char *s;
+    if (soap_append_lab(soap, NULL, 2))
+      return NULL;
+    s = soap->labbuf + soap->labidx;
+    k = soap->lablen - soap->labidx;
+    soap->labidx = 3 * (soap->lablen / 3);
+    if (!s)
+      return NULL;
+    if (k > 2)
+    { for (i = 0; i < k - 2; i += 3)
+      { register unsigned long m = 0;
+        register int j = 0;
+        do
+        { register soap_wchar c = soap_get(soap);
+          if (c == '=' || c < 0)
+          { unsigned char *p;
+            switch (j)
+            { case 2:
+                *s++ = (char)((m >> 4) & 0xFF);
+                i++;
+                break;
+              case 3:
+                *s++ = (char)((m >> 10) & 0xFF);
+                *s++ = (char)((m >> 2) & 0xFF);
+                i += 2;
+            }
+            if (n)
+              *n = (int)(soap->lablen + i - k);
+            p = (unsigned char*)soap_malloc(soap, soap->lablen + i - k);
+            if (p)
+              memcpy(p, soap->labbuf, soap->lablen + i - k);
+            if (c >= 0)
+            { while ((int)((c = soap_get(soap)) != EOF) && c != SOAP_LT && c != SOAP_TT)
+                ;
+            }
+            soap_unget(soap, c);
+            return p;
+          }
+          c -= '+';
+          if (c >= 0 && c <= 79)
+          { register int b = soap_base64i[c];
+            if (b >= 64)
+            { soap->error = SOAP_TYPE;
+              return NULL;  
+            }
+            m = (m << 6) + b;
+            j++;
+          }
+          else if (!soap_blank(c + '+'))
+          { soap->error = SOAP_TYPE;
+            return NULL;  
+          }
+        } while (j < 4);
+        *s++ = (char)((m >> 16) & 0xFF);
+        *s++ = (char)((m >> 8) & 0xFF);
+        *s++ = (char)(m & 0xFF);
+      }
+    }
+  }
+#else
+  if (soap_new_block(soap) == NULL)
+    return NULL;
+  for (;;)
+  { register int i;
+    register char *s = (char*)soap_push_block(soap, NULL, 3 * SOAP_BLKLEN); /* must be multiple of 3 */
+    if (!s)
+    { soap_end_block(soap, NULL);
+      return NULL;
+    }
+    for (i = 0; i < SOAP_BLKLEN; i++)
+    { register unsigned long m = 0;
+      register int j = 0;
+      do
+      { register soap_wchar c = soap_get(soap);
+        if (c == '=' || c < 0)
+        { unsigned char *p;
+          i *= 3;
+          switch (j)
+          { case 2:
+              *s++ = (char)((m >> 4) & 0xFF);
+              i++;
+              break;
+            case 3:
+              *s++ = (char)((m >> 10) & 0xFF);
+              *s++ = (char)((m >> 2) & 0xFF);
+              i += 2;
+          }
+          if (n)
+            *n = (int)soap_size_block(soap, NULL, i);
+          p = (unsigned char*)soap_save_block(soap, NULL, 0);
+          if (c >= 0)
+          { while ((int)((c = soap_get(soap)) != EOF) && c != SOAP_LT && c != SOAP_TT)
+              ;
+          }
+          soap_unget(soap, c);
+          return p;
+        }
+        c -= '+';
+        if (c >= 0 && c <= 79)
+        { int b = soap_base64i[c];
+          if (b >= 64)
+          { soap->error = SOAP_TYPE;
+            return NULL;  
+          }
+          m = (m << 6) + b;
+          j++;
+        }
+        else if (!soap_blank(c))
+        { soap->error = SOAP_TYPE;
+          return NULL;  
+        }
+      } while (j < 4);
+      *s++ = (char)((m >> 16) & 0xFF);
+      *s++ = (char)((m >> 8) & 0xFF);
+      *s++ = (char)(m & 0xFF);
+    }
+  }
+#endif
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_xop_forward(struct soap *soap, unsigned char **ptr, int *size, char **id, char **type, char **options)
+{ /* Check MTOM xop:Include element (within hex/base64Binary) */
+  /* TODO: this code to be obsoleted with new import/xop.h conventions */
+  int body = soap->body; /* should save type too? */
+  if (!soap_peek_element(soap))
+  { if (!soap_element_begin_in(soap, "xop:Include", 0, NULL) && *soap->href)
+    { if (soap_dime_forward(soap, ptr, size, id, type, options))
+        return soap->error;
+    }
+    if (soap->body && soap_element_end_in(soap, NULL))
+      return soap->error;
+  }
+  soap->body = body;
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_dime_forward(struct soap *soap, unsigned char **ptr, int *size, char **id, char **type, char **options)
+{ struct soap_xlist *xp;
+  *ptr = NULL;
+  *size = 0;
+  *id = NULL;
+  *type = NULL;
+  *options = NULL;
+  if (!*soap->href)
+    return SOAP_OK;
+  *id = soap_strdup(soap, soap->href);
+  xp = (struct soap_xlist*)SOAP_MALLOC(soap, sizeof(struct soap_xlist));
+  if (!xp)
+    return soap->error = SOAP_EOM;
+  xp->next = soap->xlist;
+  xp->ptr = ptr;
+  xp->size = size;
+  xp->id = *id;
+  xp->type = type;
+  xp->options = options;
+  soap->xlist = xp;
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_strdup(struct soap *soap, const char *s)
+{ char *t = NULL;
+  if (s && (t = (char*)soap_malloc(soap, strlen(s) + 1)))
+    strcpy(t, s);
+  return t;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+wchar_t *
+SOAP_FMAC2
+soap_wstrdup(struct soap *soap, const wchar_t *s)
+{ wchar_t *t = NULL;
+  if (s)
+  { size_t n = 0;
+    while (s[n])
+      n++;
+    if ((t = (wchar_t*)soap_malloc(soap, sizeof(wchar_t)*(n+1))))
+      memcpy(t, s, sizeof(wchar_t)*(n+1));
+  }
+  return t;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+struct soap_blist*
+SOAP_FMAC2
+soap_new_block(struct soap *soap)
+{ struct soap_blist *p;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "New block sequence (prev=%p)\n", soap->blist));
+  if (!(p = (struct soap_blist*)SOAP_MALLOC(soap, sizeof(struct soap_blist))))
+  { soap->error = SOAP_EOM;   
+    return NULL;
+  }
+  p->next = soap->blist; 
+  p->ptr = NULL;
+  p->size = 0;
+  soap->blist = p;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void*
+SOAP_FMAC2
+soap_push_block(struct soap *soap, struct soap_blist *b, size_t n)
+{ char *p;
+  if (!b)
+    b = soap->blist;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Push block of %u bytes (%u bytes total)\n", (unsigned int)n, (unsigned int)b->size + (unsigned int)n));
+  if (!(p = (char*)SOAP_MALLOC(soap, n + sizeof(char*) + sizeof(size_t))))
+  { soap->error = SOAP_EOM;
+    return NULL;
+  }
+  *(char**)p = b->ptr;
+  *(size_t*)(p + sizeof(char*)) = n;
+  b->ptr = p;
+  b->size += n;
+  return p + sizeof(char*) + sizeof(size_t);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_pop_block(struct soap *soap, struct soap_blist *b)
+{ char *p;
+  if (!b)
+    b = soap->blist;
+  if (!b->ptr)
+    return;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Pop block\n"));
+  p = b->ptr;
+  b->size -= *(size_t*)(p + sizeof(char*));
+  b->ptr = *(char**)p;
+  SOAP_FREE(soap, p);
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_1
+static void
+soap_update_ptrs(struct soap *soap, char *start, char *end, char *p1, char *p2)
+{ int i;
+  register struct soap_ilist *ip = NULL;
+  register struct soap_flist *fp = NULL;
+#ifndef WITH_LEANER
+  register struct soap_xlist *xp = NULL;
+#endif
+  register void *p, **q;
+  for (i = 0; i < SOAP_IDHASH; i++)
+  { for (ip = soap->iht[i]; ip; ip = ip->next)
+    { if (ip->ptr && (char*)ip->ptr >= start && (char*)ip->ptr < end)
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Update id='%s' %p -> %p\n", ip->id, ip->ptr, (char*)ip->ptr + (p1-p2)));
+        ip->ptr = (char*)ip->ptr + (p1-p2);
+      }
+      for (q = &ip->link; q; q = (void**)p)
+      { p = *q;
+        if (p && (char*)p >= start && (char*)p < end)
+        { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Link update id='%s' %p\n", ip->id, p));
+          *q = (char*)p + (p1-p2);
+        }
+      }
+      for (q = &ip->copy; q; q = (void**)p)
+      { p = *q;
+        if (p && (char*)p >= start && (char*)p < end)
+        { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Copy chain update id='%s' %p\n", ip->id, p));
+          *q = (char*)p + (p1-p2);
+        }
+      }
+      for (fp = ip->flist; fp; fp = fp->next)
+      { if ((char*)fp->ptr >= start && (char*)fp->ptr < end)
+        { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Copy list update id='%s' %p\n", ip->id, fp));
+          fp->ptr = (char*)fp->ptr + (p1-p2);
+        }
+      }
+    }
+  }
+#ifndef WITH_LEANER
+  for (xp = soap->xlist; xp; xp = xp->next)
+  { if (xp->ptr && (char*)xp->ptr >= start && (char*)xp->ptr < end)
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Update id='%s' %p -> %p\n", xp->id?xp->id:SOAP_STR_EOS, xp->ptr, (char*)xp->ptr + (p1-p2)));
+      xp->ptr = (unsigned char**)((char*)xp->ptr + (p1-p2));
+      xp->size = (int*)((char*)xp->size + (p1-p2));
+      xp->type = (char**)((char*)xp->type + (p1-p2));
+      xp->options = (char**)((char*)xp->options + (p1-p2));
+    }
+  }
+#endif
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_1
+static int
+soap_has_copies(struct soap *soap, register const char *start, register const char *end)
+{ register int i;
+  register struct soap_ilist *ip = NULL;
+  register struct soap_flist *fp = NULL;
+  register const char *p;
+  for (i = 0; i < SOAP_IDHASH; i++)
+  { for (ip = soap->iht[i]; ip; ip = ip->next)
+    { for (p = (const char*)ip->copy; p; p = *(const char**)p)
+        if (p >= start && p < end)
+          return SOAP_ERR;
+      for (fp = ip->flist; fp; fp = fp->next)
+        if ((const char*)fp->ptr >= start && (const char*)fp->ptr < end)
+          return SOAP_ERR;
+    }
+  }
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_resolve(struct soap *soap)
+{ register int i;
+  register struct soap_ilist *ip = NULL;
+  register struct soap_flist *fp = NULL;
+  short flag;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resolving forwarded data\n"));
+  for (i = 0; i < SOAP_IDHASH; i++)
+  { for (ip = soap->iht[i]; ip; ip = ip->next)
+    { if (ip->ptr)
+      { register void *p, **q, *r;
+        q = (void**)ip->link;
+        ip->link = NULL;
+        r = ip->ptr;
+        DBGLOG(TEST, if (q) SOAP_MESSAGE(fdebug, "Traversing link chain to resolve id='%s'\n", ip->id));
+        while (q)
+        { p = *q;
+          *q = r;
+          DBGLOG(TEST,SOAP_MESSAGE(fdebug, "... link %p -> %p\n", q, r));
+          q = (void**)p;
+        }
+      }
+      else if (*ip->id == '#')
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Missing data for id='%s'\n", ip->id));
+        strcpy(soap->id, ip->id + 1);
+        return soap->error = SOAP_MISSING_ID;
+      }
+    }
+  }
+  do
+  { flag = 0;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resolution phase\n"));
+    for (i = 0; i < SOAP_IDHASH; i++)
+    { for (ip = soap->iht[i]; ip; ip = ip->next)
+      { if (ip->ptr && !soap_has_copies(soap, (const char*)ip->ptr, (const char*)ip->ptr + ip->size))
+        { if (ip->copy)
+          { register void *p, **q = (void**)ip->copy;
+            DBGLOG(TEST, if (q) SOAP_MESSAGE(fdebug, "Traversing copy chain to resolve id='%s'\n", ip->id));
+            ip->copy = NULL;
+            do
+            { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "... copy %p -> %p (%u bytes)\n", ip->ptr, q, (unsigned int)ip->size));
+              p = *q;
+              memcpy(q, ip->ptr, ip->size);
+              q = (void**)p;
+            } while (q);
+            flag = 1;
+          }
+          for (fp = ip->flist; fp; fp = ip->flist)
+          { register unsigned int k = fp->level;
+            register void *p = ip->ptr;
+            DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resolving forwarded data type=%d location=%p level=%u,%u id='%s'\n", ip->type, p, ip->level, fp->level, ip->id));
+            while (ip->level < k)
+            { register void **q = (void**)soap_malloc(soap, sizeof(void*));  
+              if (!q)
+                return soap->error;
+              *q = p;
+              DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Descending one level, new location=%p holds=%p...\n", q, *q));
+              p = (void*)q;
+              k--;
+            }
+            if (fp->fcopy)
+              fp->fcopy(soap, ip->type, fp->type, fp->ptr, fp->len, p, ip->size);
+            else
+              soap_fcopy(soap, ip->type, fp->type, fp->ptr, fp->len, p, ip->size);
+            ip->flist = fp->next;
+            SOAP_FREE(soap, fp);
+            flag = 1;
+          }
+        }
+      }
+    }
+  } while (flag);
+#ifdef SOAP_DEBUG
+  for (i = 0; i < SOAP_IDHASH; i++)
+  { for (ip = soap->iht[i]; ip; ip = ip->next)
+    { if (ip->copy || ip->flist)
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resolution error: forwarded data for id='%s' could not be propagated, please report this problem to the developers\n", ip->id));
+      }
+    }
+  }
+#endif
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resolution done\n"));
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+size_t
+SOAP_FMAC2
+soap_size_block(struct soap *soap, struct soap_blist *b, size_t n)
+{ if (!b)
+    b = soap->blist;
+  if (b->ptr)
+  { b->size -= *(size_t*)(b->ptr + sizeof(char*)) - n;
+    *(size_t*)(b->ptr + sizeof(char*)) = n;
+  }
+  return b->size;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+char*
+SOAP_FMAC2
+soap_first_block(struct soap *soap, struct soap_blist *b)
+{ char *p, *q, *r;
+  if (!b)
+    b = soap->blist;
+  p = b->ptr;
+  if (!p)
+    return NULL;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "First block\n"));
+  r = NULL;
+  do
+  { q = *(char**)p;
+    *(char**)p = r;
+    r = p;
+    p = q;
+  } while (p);
+  b->ptr = r;
+  return r + sizeof(char*) + sizeof(size_t);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+char*
+SOAP_FMAC2
+soap_next_block(struct soap *soap, struct soap_blist *b)
+{ char *p;
+  if (!b)
+    b = soap->blist;
+  p = b->ptr;
+  if (p)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Next block\n"));
+    b->ptr = *(char**)p;
+    SOAP_FREE(soap, p);
+    if (b->ptr)
+      return b->ptr + sizeof(char*) + sizeof(size_t);
+  }
+  return NULL;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+size_t
+SOAP_FMAC2
+soap_block_size(struct soap *soap, struct soap_blist *b)
+{ if (!b)
+    b = soap->blist;
+  return *(size_t*)(b->ptr + sizeof(char*));
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_end_block(struct soap *soap, struct soap_blist *b)
+{ char *p, *q;
+  if (!b)
+    b = soap->blist;
+  if (b)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "End of block sequence, free all remaining blocks\n"));
+    for (p = b->ptr; p; p = q)
+    { q = *(char**)p;
+      SOAP_FREE(soap, p);
+    }
+    if (soap->blist == b)
+      soap->blist = b->next;
+    else
+    { struct soap_blist *bp;
+      for (bp = soap->blist; bp; bp = bp->next)
+      { if (bp->next == b)
+        { bp->next = b->next;
+	  break;
+        }
+      }
+    }
+    SOAP_FREE(soap, b);
+  }
+  DBGLOG(TEST, if (soap->blist) SOAP_MESSAGE(fdebug, "Restore previous block sequence\n"));
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+char*
+SOAP_FMAC2
+soap_save_block(struct soap *soap, struct soap_blist *b, char *p, int flag)
+{ register size_t n;
+  register char *q, *s;
+  if (!b)
+    b = soap->blist;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Save all blocks in contiguous memory space of %u bytes (%p->%p)\n", (unsigned int)b->size, b->ptr, p));
+  if (b->size)
+  { if (!p)
+      p = (char*)soap_malloc(soap, b->size);
+    if (p)
+    { for (s = p, q = soap_first_block(soap, b); q; q = soap_next_block(soap, b))
+      { n = soap_block_size(soap, b);
+#ifndef WITH_NOIDREF
+        if (flag)
+          soap_update_ptrs(soap, q, q + n, s, q);
+#endif
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Copy %u bytes from %p to %p\n", (unsigned int)n, q, s));
+        memcpy(s, q, n);
+        s += n;
+      }
+    }
+    else
+      soap->error = SOAP_EOM;
+  }
+  soap_end_block(soap, b);
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_putsize(struct soap *soap, const char *type, int size)
+{ return soap_putsizes(soap, type, &size, 1);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_putsizes(struct soap *soap, const char *type, const int *size, int dim)
+{ return soap_putsizesoffsets(soap, type, size, NULL, dim);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_putsizesoffsets(struct soap *soap, const char *type, const int *size, const int *offset, int dim)
+{ int i;
+  if (!type)
+    return NULL;
+  if (soap->version == 2)
+  { sprintf(soap->type, "%s[%d", type, size[0]);
+    for (i = 1; i < dim; i++)
+      sprintf(soap->type + strlen(soap->type), " %d", size[i]);
+  }
+  else
+  { if (offset)
+    { sprintf(soap->type, "%s[%d", type, size[0] + offset[0]);
+      for (i = 1; i < dim; i++)
+        sprintf(soap->type + strlen(soap->type), ",%d", size[i] + offset[i]);
+    }
+    else
+    { sprintf(soap->type, "%s[%d", type, size[0]);
+      for (i = 1; i < dim; i++)
+        sprintf(soap->type + strlen(soap->type), ",%d", size[i]);
+    }
+    strcat(soap->type, "]");
+  }
+  return soap->type;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_putoffset(struct soap *soap, int offset)
+{ return soap_putoffsets(soap, &offset, 1);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_putoffsets(struct soap *soap, const int *offset, int dim)
+{ register int i;
+  sprintf(soap->arrayOffset, "[%d", offset[0]);
+  for (i = 1; i < dim; i++)
+    sprintf(soap->arrayOffset + strlen(soap->arrayOffset), ",%d", offset[i]);
+  strcat(soap->arrayOffset, "]");
+  return soap->arrayOffset;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_size(const int *size, int dim)
+{ register int i, n = size[0];
+  for (i = 1; i < dim; i++)
+    n *= size[i];
+  return n;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_getoffsets(const char *attr, const int *size, int *offset, int dim)
+{ register int i, j = 0;
+  if (offset)
+    for (i = 0; i < dim && attr && *attr; i++)
+    { attr++;
+      j *= size[i];
+      j += offset[i] = (int)atol(attr);
+      attr = strchr(attr, ',');
+    }
+  else
+    for (i = 0; i < dim && attr && *attr; i++)
+    { attr++;
+      j *= size[i];
+      j += (int)atol(attr);
+      attr = strchr(attr, ',');
+    }
+  return j;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_getsize(const char *attr1, const char *attr2, int *j)
+{ register int n, k;
+  char *s;
+  *j = 0;
+  if (!*attr1)
+    return -1;
+  if (*attr1 == '[')
+    attr1++;
+  n = 1;
+  for (;;)
+  { k = (int)soap_strtol(attr1, &s, 10);
+    n *= k;
+    if (k < 0 || n > SOAP_MAXARRAYSIZE || s == attr1)
+      return -1;
+    attr1 = strchr(s, ',');
+    if (!attr1)
+      attr1 = strchr(s, ' ');
+    if (attr2 && *attr2)
+    { attr2++;
+      *j *= k;
+      k = (int)soap_strtol(attr2, &s, 10);
+      *j += k;
+      if (k < 0)
+        return -1;
+      attr2 = s;
+    }
+    if (!attr1)
+      break;
+    attr1++;
+  }
+  return n - *j;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_getsizes(const char *attr, int *size, int dim)
+{ register int i, k, n;
+  if (!*attr)
+    return -1;
+  i = (int)strlen(attr);
+  n = 1;
+  do
+  { for (i = i-1; i >= 0; i--)
+      if (attr[i] == '[' || attr[i] == ',' || attr[i] == ' ')
+        break;
+    k = (int)atol(attr + i + 1);
+    n *= size[--dim] = k;
+    if (k < 0 || n > SOAP_MAXARRAYSIZE)
+      return -1;
+  } while (i >= 0 && attr[i] != '[');
+  return n;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_getposition(const char *attr, int *pos)
+{ register int i, n;
+  if (!*attr)
+    return -1;
+  n = 0;
+  i = 1;
+  do
+  { pos[n++] = (int)atol(attr + i);
+    while (attr[i] && attr[i] != ',' && attr[i] != ']')
+      i++;
+    if (attr[i] == ',')
+      i++;
+  } while (n < SOAP_MAXDIMS && attr[i] && attr[i] != ']');
+  return n;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_push_namespace(struct soap *soap, const char *id, const char *ns)
+{ register struct soap_nlist *np;
+  register struct Namespace *p;
+  register short i = -1;
+  register size_t n, k;
+  n = strlen(id);
+  k = strlen(ns) + 1;
+  p = soap->local_namespaces;
+  if (p)
+  { for (i = 0; p->id; p++, i++)
+    { if (p->ns && !strcmp(ns, p->ns))
+      { if (p->out)
+        { SOAP_FREE(soap, p->out);
+          p->out = NULL;
+        }
+        break;
+      }
+      if (p->out)
+      { if (!strcmp(ns, p->out))
+          break;
+      }
+      else if (p->in)
+      { if (!soap_tag_cmp(ns, p->in))
+        { if ((p->out = (char*)SOAP_MALLOC(soap, k)))
+            strcpy(p->out, ns);
+          break;
+        }
+      }
+    }
+    if (!p || !p->id)
+      i = -1;
+  }
+  if (i >= 0)
+    k = 0;
+  np = (struct soap_nlist*)SOAP_MALLOC(soap, sizeof(struct soap_nlist) + n + k);
+  if (!np)
+    return soap->error = SOAP_EOM;
+  np->next = soap->nlist;
+  soap->nlist = np;
+  np->level = soap->level;
+  np->index = i;
+  strcpy(np->id, id);
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Push namespace binding (level=%u) '%s' '%s'\n", soap->level, id, ns));
+  if (i < 0)
+  { np->ns = np->id + n + 1;
+    strcpy(np->ns, ns);
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Push NOT OK: no match found for '%s' in namespace mapping table (added to stack anyway)\n", ns));
+  }
+  else
+  { np->ns = NULL;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Push OK ('%s' matches '%s' in namespace table)\n", id, p->id));
+  }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_pop_namespace(struct soap *soap)
+{ register struct soap_nlist *np, *nq;
+  for (np = soap->nlist; np && np->level >= soap->level; np = nq)
+  { nq = np->next;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Pop namespace binding (level=%u) '%s'\n", soap->level, np->id));
+    SOAP_FREE(soap, np);
+  }
+  soap->nlist = np;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_match_namespace(struct soap *soap, const char *id1, const char *id2, size_t n1, size_t n2) 
+{ register struct soap_nlist *np = soap->nlist;
+  while (np && (strncmp(np->id, id1, n1) || np->id[n1]))
+    np = np->next;
+  if (np)
+  { if (np->index < 0
+     || (soap->local_namespaces[np->index].id
+      && (strncmp(soap->local_namespaces[np->index].id, id2, n2)
+       || soap->local_namespaces[np->index].id[n2])))
+      return SOAP_NAMESPACE;
+    return SOAP_OK;
+  }
+  if (n1 == 3 && n1 == n2 && !strncmp(id1, "xml", 3) && !strncmp(id1, id2, 3))
+    return SOAP_OK;
+  return soap->error = SOAP_SYNTAX_ERROR; 
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_current_namespace(struct soap *soap, const char *tag)
+{ register struct soap_nlist *np;
+  register const char *s;
+  if (!tag || !strncmp(tag, "xml", 3))
+    return NULL;
+  np = soap->nlist;
+  if (!(s = strchr(tag, ':')))
+  { while (np && *np->id) /* find default namespace, if present */
+      np = np->next;
+  }
+  else
+  { while (np && (strncmp(np->id, tag, s - tag) || np->id[s - tag]))
+      np = np->next;
+    if (!np)
+      soap->error = SOAP_NAMESPACE;
+  }
+  if (np)
+  { if (np->index >= 0)
+      return soap->namespaces[np->index].ns;
+    if (np->ns)
+      return soap_strdup(soap, np->ns);
+  }
+  return NULL;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_tag_cmp(const char *s, const char *t)
+{ for (;;)
+  { register int c1 = *s;
+    register int c2 = *t;
+    if (!c1 || c1 == '"')
+      break;
+    if (c2 != '-')
+    { if (c1 != c2)
+      { if (c1 >= 'A' && c1 <= 'Z')
+          c1 += 'a' - 'A';
+        if (c2 >= 'A' && c2 <= 'Z')
+          c2 += 'a' - 'A';
+      }
+      if (c1 != c2)
+      { if (c2 != '*')
+          return 1;
+        c2 = *++t;
+        if (!c2)
+          return 0;
+        if (c2 >= 'A' && c2 <= 'Z')
+          c2 += 'a' - 'A';
+        for (;;)
+        { c1 = *s;
+          if (!c1 || c1 == '"')
+            break;
+          if (c1 >= 'A' && c1 <= 'Z')
+            c1 += 'a' - 'A';
+          if (c1 == c2 && !soap_tag_cmp(s + 1, t + 1))
+            return 0;
+          s++;
+        }
+        break;
+      }
+    }
+    s++;
+    t++;
+  }
+  if (*t == '*' && !t[1])
+    return 0;
+  return *t;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_match_tag(struct soap *soap, const char *tag1, const char *tag2)
+{ register const char *s, *t;
+  register int err;
+  if (!tag1 || !tag2 || !*tag2)
+    return SOAP_OK;
+  s = strchr(tag1, ':');
+  t = strchr(tag2, ':');
+  if (t)
+  { if (s)
+    { if (t[1] && SOAP_STRCMP(s + 1, t + 1))
+        return SOAP_TAG_MISMATCH;
+      if (t != tag2 && (err = soap_match_namespace(soap, tag1, tag2, s - tag1, t - tag2)))
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Tags '%s' and '%s' match but namespaces differ\n", tag1, tag2));
+        if (err == SOAP_NAMESPACE)
+          return SOAP_TAG_MISMATCH;
+        return err;
+      }
+    } 
+    else if (SOAP_STRCMP(tag1, t + 1))
+    { return SOAP_TAG_MISMATCH;
+    }
+    else if (t != tag2 && (err = soap_match_namespace(soap, tag1, tag2, 0, t - tag2)))
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Tags '%s' and '%s' match but namespaces differ\n", tag1, tag2));
+      if (err == SOAP_NAMESPACE)
+        return SOAP_TAG_MISMATCH;
+      return err;
+    }
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Tags and (default) namespaces match: '%s' '%s'\n", tag1, tag2));
+    return SOAP_OK;
+  }
+  if (s)
+  { if (SOAP_STRCMP(s + 1, tag2))
+      return SOAP_TAG_MISMATCH;
+  }
+  else if (SOAP_STRCMP(tag1, tag2))
+    return SOAP_TAG_MISMATCH;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Tags match: '%s' '%s'\n", tag1, tag2));
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_match_array(struct soap *soap, const char *type)
+{ if (*soap->arrayType)
+    if (soap_match_tag(soap, soap->arrayType, type)
+     && soap_match_tag(soap, soap->arrayType, "xsd:anyType")
+     && soap_match_tag(soap, soap->arrayType, "xsd:ur-type")
+    )
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Array type mismatch: '%s' '%s'\n", soap->arrayType, type));
+      return SOAP_TAG_MISMATCH;
+    }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************\
+ *
+ *	SSL
+ *
+\******************************************************************************/
+
+#ifdef WITH_OPENSSL
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_rand()
+{ unsigned char buf[4];
+  if (!soap_ssl_init_done)
+    soap_ssl_init();
+  RAND_pseudo_bytes(buf, 4);
+  return *(int*)buf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_ssl_server_context(struct soap *soap, unsigned short flags, const char *keyfile, const char *password, const char *cafile, const char *capath, const char *dhfile, const char *randfile, const char *sid)
+{ int err;
+  soap->keyfile = keyfile;
+  soap->password = password;
+  soap->cafile = cafile;
+  soap->capath = capath;
+  soap->dhfile = dhfile;
+  soap->randfile = randfile;
+  soap->ssl_flags = flags | (dhfile == NULL ? SOAP_SSL_RSA : 0);
+  if (!(err = soap->fsslauth(soap)))
+  { if (sid)
+      SSL_CTX_set_session_id_context(soap->ctx, (unsigned char*)sid, (unsigned int)strlen(sid));
+  }
+  return err; 
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_ssl_client_context(struct soap *soap, unsigned short flags, const char *keyfile, const char *password, const char *cafile, const char *capath, const char *randfile)
+{ soap->keyfile = keyfile;
+  soap->password = password;
+  soap->cafile = cafile;
+  soap->capath = capath;
+  soap->dhfile = NULL;
+  soap->ssl_flags = flags;
+  soap->randfile = randfile;
+  soap->fsslverify = (flags & SOAP_SSL_ALLOW_EXPIRED_CERTIFICATE) == 0 ? ssl_verify_callback : ssl_verify_callback_allow_expired_certificate;
+  return soap->fsslauth(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_ssl_init()
+{ /* Note: for MT systems, the main program MUST call soap_ssl_init() before any threads are started */
+  if (!soap_ssl_init_done)
+  { soap_ssl_init_done = 1;
+    SSL_library_init();
+#ifndef WITH_LEAN
+    SSL_load_error_strings();
+#endif
+    if (!RAND_load_file("/dev/urandom", 1024))
+    { char buf[1024];
+      RAND_seed(buf, sizeof(buf));
+      while (!RAND_status())
+      { int r = rand();
+        RAND_seed(&r, sizeof(int));
+      }
+    }
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+const char *
+SOAP_FMAC2
+soap_ssl_error(struct soap *soap, int ret)
+{ int err = SSL_get_error(soap->ssl, ret);
+  const char *msg = soap_code_str(h_ssl_error_codes, err);
+  if (msg)
+    strcpy(soap->msgbuf, msg);
+  else
+    return ERR_error_string(err, soap->msgbuf);
+  if (ERR_peek_error())
+  { unsigned long r;
+    strcat(soap->msgbuf, "\n");
+    while ((r = ERR_get_error()))
+      ERR_error_string_n(r, soap->msgbuf + strlen(soap->msgbuf), sizeof(soap->msgbuf) - strlen(soap->msgbuf));
+  } 
+  else
+  { switch (ret)
+    { case 0:
+        strcpy(soap->msgbuf, "EOF was observed that violates the protocol. The client probably provided invalid authentication information.");
+        break;
+      case -1:
+        sprintf(soap->msgbuf, "Error observed by underlying BIO: %s", strerror(errno));  
+        break;
+    }
+  }
+  return soap->msgbuf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+static int
+ssl_password(char *buf, int num, int rwflag, void *userdata)
+{ if (num < (int)strlen((char*)userdata) + 1)
+    return 0;
+  return (int)strlen(strcpy(buf, (char*)userdata));
+}
+#endif
+
+/******************************************************************************/
+/* This callback is included for future references. It should not be deleted
+#ifndef PALM_2
+static DH *
+ssl_tmp_dh(SSL *ssl, int is_export, int keylength)
+{ static DH *dh512 = NULL;
+  static DH *dh1024 = NULL;
+  DH *dh;
+  switch (keylength)
+  { case 512:
+      if (!dh512)
+      { BIO *bio = BIO_new_file("dh512.pem", "r");
+        if (bio)
+        { dh512 = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+          BIO_free(bio);
+          return dh512;
+        }
+      }
+      else
+        return dh512;
+    default:
+      if (!dh1024)
+      { BIO *bio = BIO_new_file("dh1024.pem", "r");
+        if (bio)
+        { dh1024 = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+          BIO_free(bio);
+        }
+      }
+      dh = dh1024;
+  }
+  return dh;
+}
+#endif
+*/
+
+/******************************************************************************/
+#ifndef PALM_1
+static int
+ssl_auth_init(struct soap *soap)
+{ long flags;
+  int mode;
+  if (!soap_ssl_init_done)
+    soap_ssl_init();
+  ERR_clear_error();
+  if (!soap->ctx)
+  { if (!(soap->ctx = SSL_CTX_new(SSLv23_method())))
+      return soap_set_receiver_error(soap, "SSL error", "Can't setup context", SOAP_SSL_ERROR);
+    /* Alters the behavior of SSL read/write:
+    SSL_CTX_set_mode(soap->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_AUTO_RETRY);
+    */
+  }
+  if (soap->randfile)
+  { if (!RAND_load_file(soap->randfile, -1))
+      return soap_set_receiver_error(soap, "SSL error", "Can't load randomness", SOAP_SSL_ERROR);
+  }
+  if (soap->cafile || soap->capath)
+  { if (!SSL_CTX_load_verify_locations(soap->ctx, soap->cafile, soap->capath))
+      return soap_set_receiver_error(soap, "SSL error", "Can't read CA file and directory", SOAP_SSL_ERROR);
+    if (soap->cafile && (soap->ssl_flags & SOAP_SSL_REQUIRE_CLIENT_AUTHENTICATION))
+      SSL_CTX_set_client_CA_list(soap->ctx, SSL_load_client_CA_file(soap->cafile));
+  }
+  if (!SSL_CTX_set_default_verify_paths(soap->ctx))
+    return soap_set_receiver_error(soap, "SSL error", "Can't read default CA file and/or directory", SOAP_SSL_ERROR);
+/* This code assumes a typical scenario, see alternative code below */
+  if (soap->keyfile)
+  { if (!SSL_CTX_use_certificate_chain_file(soap->ctx, soap->keyfile))
+      return soap_set_receiver_error(soap, "SSL error", "Can't read certificate key file", SOAP_SSL_ERROR);
+    if (soap->password)
+    { SSL_CTX_set_default_passwd_cb_userdata(soap->ctx, (void*)soap->password);
+      SSL_CTX_set_default_passwd_cb(soap->ctx, ssl_password);
+    }
+    if (!SSL_CTX_use_PrivateKey_file(soap->ctx, soap->keyfile, SSL_FILETYPE_PEM))
+      return soap_set_receiver_error(soap, "SSL error", "Can't read key file", SOAP_SSL_ERROR);
+  }
+/* Suggested alternative approach to check the key file for certs (cafile=NULL):
+  if (soap->password)
+  { SSL_CTX_set_default_passwd_cb_userdata(soap->ctx, (void*)soap->password);
+    SSL_CTX_set_default_passwd_cb(soap->ctx, ssl_password);
+  }
+  if (!soap->cafile || !SSL_CTX_use_certificate_chain_file(soap->ctx, soap->cafile))
+  { if (soap->keyfile)
+    { if (!SSL_CTX_use_certificate_chain_file(soap->ctx, soap->keyfile))
+        return soap_set_receiver_error(soap, "SSL error", "Can't read certificate or key file", SOAP_SSL_ERROR);
+      if (!SSL_CTX_use_PrivateKey_file(soap->ctx, soap->keyfile, SSL_FILETYPE_PEM))
+        return soap_set_receiver_error(soap, "SSL error", "Can't read key file", SOAP_SSL_ERROR);
+    }
+  }
+*/
+  if ((soap->ssl_flags & SOAP_SSL_RSA))
+  { RSA *rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL);
+    if (!SSL_CTX_set_tmp_rsa(soap->ctx, rsa))
+    { if (rsa)
+        RSA_free(rsa);
+      return soap_set_receiver_error(soap, "SSL error", "Can't set RSA key", SOAP_SSL_ERROR);
+    }
+    RSA_free(rsa);
+  }
+  else if (soap->dhfile)
+  { DH *dh = 0;
+    BIO *bio;
+    bio = BIO_new_file(soap->dhfile, "r");
+    if (!bio)
+      return soap_set_receiver_error(soap, "SSL error", "Can't read DH file", SOAP_SSL_ERROR);
+    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+    BIO_free(bio);
+    if (SSL_CTX_set_tmp_dh(soap->ctx, dh) < 0)
+    { if (dh)
+        DH_free(dh);
+      return soap_set_receiver_error(soap, "SSL error", "Can't set DH parameters", SOAP_SSL_ERROR);
+    }
+    DH_free(dh);
+  }
+  flags = (SSL_OP_ALL | SSL_OP_NO_SSLv2);
+  if ((soap->ssl_flags & SOAP_SSLv3))
+    flags |= SSL_OP_NO_TLSv1;
+  if ((soap->ssl_flags & SOAP_TLSv1))
+    flags |= SSL_OP_NO_SSLv3;
+  SSL_CTX_set_options(soap->ctx, flags);
+  if ((soap->ssl_flags & SOAP_SSL_REQUIRE_CLIENT_AUTHENTICATION))
+    mode = (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+  else if ((soap->ssl_flags & SOAP_SSL_REQUIRE_SERVER_AUTHENTICATION))
+    mode = SSL_VERIFY_PEER;
+  else
+    mode = SSL_VERIFY_NONE;
+  SSL_CTX_set_verify(soap->ctx, mode, soap->fsslverify);
+#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
+  SSL_CTX_set_verify_depth(soap->ctx, 1); 
+#else
+  SSL_CTX_set_verify_depth(soap->ctx, 9); 
+#endif  
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+static int
+ssl_verify_callback(int ok, X509_STORE_CTX *store)
+{
+#ifdef SOAP_DEBUG
+  if (!ok) 
+  { char data[256];
+    X509 *cert = X509_STORE_CTX_get_current_cert(store);
+    fprintf(stderr, "SSL verify error or warning with certificate at depth %d: %s\n", X509_STORE_CTX_get_error_depth(store), X509_verify_cert_error_string(X509_STORE_CTX_get_error(store)));
+    X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data));
+    fprintf(stderr, "certificate issuer %s\n", data);
+    X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data));
+    fprintf(stderr, "certificate subject %s\n", data);
+  }
+#endif
+  /* Note: return 1 to continue, but unsafe progress will be terminated by SSL */
+  return ok;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+static int
+ssl_verify_callback_allow_expired_certificate(int ok, X509_STORE_CTX *store)
+{ ok = ssl_verify_callback(ok, store);
+  if (ok == 0 && X509_STORE_CTX_get_error(store) == X509_V_ERR_CERT_HAS_EXPIRED)
+  {
+#ifdef SOAP_DEBUG
+    fprintf(stderr, "ignoring certificate expiration\n");
+#endif
+    X509_STORE_CTX_set_error(store, X509_V_OK);
+    ok = 1;
+  }
+  /* Note: return 1 to continue, but unsafe progress will be terminated by SSL */
+  return ok;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_ssl_accept(struct soap *soap)
+{ BIO *bio;
+  int retries, r, s;
+  if (!soap_valid_socket(soap->socket))
+    return soap_set_receiver_error(soap, "SSL error", "No socket in soap_ssl_accept()", SOAP_SSL_ERROR);
+  if (!soap->ctx && (soap->error = soap->fsslauth(soap)))
+    return SOAP_INVALID_SOCKET;
+  if (!soap->ssl)
+  { soap->ssl = SSL_new(soap->ctx);
+    if (!soap->ssl)
+      return soap_set_receiver_error(soap, "SSL error", "SSL_new() failed in soap_ssl_accept()", SOAP_SSL_ERROR);
+  }
+  else
+    SSL_clear(soap->ssl);
+  soap->imode |= SOAP_ENC_SSL;
+  soap->omode |= SOAP_ENC_SSL;
+  /* Set SSL sockets to non-blocking */
+  SOAP_SOCKNONBLOCK(soap->socket)
+  bio = BIO_new_socket((int)soap->socket, BIO_NOCLOSE);
+  SSL_set_bio(soap->ssl, bio, bio);
+  retries = 100; /* 10 sec retries, 100 times 0.1 sec */
+  while ((r = SSL_accept(soap->ssl)) <= 0)
+  { int err = SSL_get_error(soap->ssl, r);
+    if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
+    { struct timeval timeout;
+      fd_set fd;
+#ifndef WIN32
+      if ((int)soap->socket >= (int)FD_SETSIZE)
+        return SOAP_FD_EXCEEDED;	/* Hint: MUST increase FD_SETSIZE */
+#endif
+      timeout.tv_sec = 0;
+      timeout.tv_usec = 100000;
+      FD_ZERO(&fd);
+      FD_SET(soap->socket, &fd);
+      if (err == SSL_ERROR_WANT_READ)
+        s = select((int)soap->socket + 1, &fd, NULL, &fd, &timeout);
+      else
+        s = select((int)soap->socket + 1, NULL, &fd, &fd, &timeout);
+      if (s < 0 && (s = soap_socket_errno(soap->socket)) != SOAP_EINTR)
+      { soap->errnum = s;
+        break;
+      }
+    }
+    else
+    { soap->errnum = err;
+      break;
+    }
+    if (retries-- <= 0)
+      break;
+  }
+  if (r <= 0)
+  { soap_set_receiver_error(soap, soap_ssl_error(soap, r), "SSL_accept() failed in soap_ssl_accept()", SOAP_SSL_ERROR);
+    soap_closesock(soap);
+    return SOAP_SSL_ERROR;
+  }
+  if ((soap->ssl_flags & SOAP_SSL_REQUIRE_CLIENT_AUTHENTICATION))
+  { X509 *peer;
+    int err;
+    if ((err = SSL_get_verify_result(soap->ssl)) != X509_V_OK)
+    { soap_closesock(soap);
+      return soap_set_sender_error(soap, X509_verify_cert_error_string(err), "SSL certificate presented by peer cannot be verified in soap_ssl_accept()", SOAP_SSL_ERROR);
+    }
+    peer = SSL_get_peer_certificate(soap->ssl);
+    if (!peer)
+    { soap_closesock(soap);
+      return soap_set_sender_error(soap, "SSL error", "No SSL certificate was presented by the peer in soap_ssl_accept()", SOAP_SSL_ERROR);
+    }
+    X509_free(peer);
+  }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#endif /* WITH_OPENSSL */
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static int
+tcp_init(struct soap *soap)
+{ soap->errmode = 1;
+#ifdef WIN32
+  if (tcp_done)
+    return 0;
+  else
+  { WSADATA w;
+    if (WSAStartup(MAKEWORD(1, 1), &w))
+      return -1;
+    tcp_done = 1;
+  }
+#endif
+  return 0;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_done(struct soap *soap)
+{ 
+#ifdef SOAP_DEBUG
+  int i;
+#endif
+  if (soap_check_state(soap))
+    return;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Done with context\n"));
+  soap_free_temp(soap);
+  while (soap->clist)
+  { struct soap_clist *p = soap->clist->next;
+    SOAP_FREE(soap, soap->clist);
+    soap->clist = p;
+  }
+  soap->keep_alive = 0; /* to force close the socket */
+  soap_closesock(soap);
+#ifdef WITH_COOKIES
+  soap_free_cookies(soap);
+#endif
+  while (soap->plugins)
+  { register struct soap_plugin *p = soap->plugins->next;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Removing plugin '%s'\n", soap->plugins->id));
+    if (soap->plugins->fcopy || soap->state == SOAP_INIT)
+      soap->plugins->fdelete(soap, soap->plugins);
+    SOAP_FREE(soap, soap->plugins);
+    soap->plugins = p;
+  }
+  soap->fplugin = fplugin;
+  soap->fmalloc = NULL;
+#ifndef WITH_NOHTTP
+  soap->fpost = http_post;
+  soap->fget = http_get;
+  soap->fput = http_put;
+  soap->fdel = http_del;
+  soap->fhead = http_head;
+  soap->fform = NULL;
+  soap->fposthdr = http_post_header;
+  soap->fresponse = http_response;
+  soap->fparse = http_parse;
+  soap->fparsehdr = http_parse_header;
+#endif
+  soap->fheader = NULL;
+#ifndef WITH_NOIO
+#ifndef WITH_IPV6
+  soap->fresolve = tcp_gethost;
+#else
+  soap->fresolve = NULL;
+#endif
+  soap->faccept = tcp_accept;
+  soap->fopen = tcp_connect;
+  soap->fclose = tcp_disconnect;
+  soap->fclosesocket = tcp_closesocket;
+  soap->fshutdownsocket = tcp_shutdownsocket;
+  soap->fsend = fsend;
+  soap->frecv = frecv;
+  soap->fpoll = soap_poll;
+#else
+  soap->fopen = NULL;
+  soap->fclose = NULL;
+  soap->fpoll = NULL;
+#endif
+#ifndef WITH_LEANER
+  soap->fprepareinit = NULL;
+  soap->fpreparesend = NULL;
+  soap->fpreparerecv = NULL;
+  soap->fpreparefinal = NULL;
+#endif
+  soap->fseterror = NULL;
+  soap->fignore = NULL;
+  soap->fserveloop = NULL;
+#ifdef WITH_OPENSSL
+  if (soap->session)
+  { SSL_SESSION_free(soap->session);
+    soap->session = NULL;
+  }
+#endif
+  if (soap->state == SOAP_INIT)
+  { if (soap_valid_socket(soap->master))
+    { soap->fclosesocket(soap, soap->master);
+      soap->master = SOAP_INVALID_SOCKET;
+    }
+  }
+#ifdef WITH_OPENSSL
+  if (soap->ssl)
+  { SSL_free(soap->ssl);
+    soap->ssl = NULL;
+  }
+  if (soap->state == SOAP_INIT)
+  { if (soap->ctx)
+    { SSL_CTX_free(soap->ctx);
+      soap->ctx = NULL;
+    }
+  }
+#endif
+#ifdef WITH_OPENSSL
+  ERR_remove_state(0);
+#endif
+#ifdef WITH_C_LOCALE
+  freelocale(soap->c_locale);
+#endif
+#ifdef WITH_ZLIB
+  if (soap->d_stream)
+  { SOAP_FREE(soap, (void*)soap->d_stream);
+    soap->d_stream = NULL;
+  }
+  if (soap->z_buf)
+  { SOAP_FREE(soap, (void*)soap->z_buf);
+    soap->z_buf = NULL;
+  }
+#endif
+#ifdef SOAP_DEBUG
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Free logfiles\n"));
+  for (i = 0; i < SOAP_MAXLOGS; i++)
+  { if (soap->logfile[i])
+    { SOAP_FREE(soap, (void*)soap->logfile[i]);
+      soap->logfile[i] = NULL;
+    }
+    soap_close_logfile(soap, i);
+  }
+  soap->state = SOAP_NONE;
+#endif
+#ifdef SOAP_MEM_DEBUG
+  soap_free_mht(soap);
+#endif
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_cleanup(struct soap *soap)
+{ soap_done(soap);
+#ifdef WIN32
+  if (!tcp_done)
+    return;
+  tcp_done = 0;
+  WSACleanup();
+#endif
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static const char*
+tcp_error(struct soap *soap)
+{ register const char *msg = NULL;
+  switch (soap->errmode)
+  { case 0:
+      msg = soap_strerror(soap);
+      break;
+    case 1:
+      msg = "WSAStartup failed";
+      break;
+    case 2:
+    {
+#ifndef WITH_LEAN
+      msg = soap_code_str(h_error_codes, soap->errnum);
+      if (!msg)
+#endif
+      { sprintf(soap->msgbuf, "TCP/UDP IP error %d", soap->errnum);
+        msg = soap->msgbuf;
+      }
+    }
+  }
+  return msg;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static const char*
+http_error(struct soap *soap, int status)
+{ register const char *msg = SOAP_STR_EOS;
+#ifndef WITH_LEAN
+  msg = soap_code_str(h_http_error_codes, status);
+  if (!msg)
+    msg = SOAP_STR_EOS;
+#endif
+  return msg;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_IPV6
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static int
+tcp_gethost(struct soap *soap, const char *addr, struct in_addr *inaddr)
+{ soap_int32 iadd = -1;
+  struct hostent hostent, *host = &hostent;
+#ifdef VXWORKS
+  int hostint;
+  /* inet_addr(), and hostGetByName() expect "char *"; addr is a "const char *". */
+  iadd = inet_addr((char*)addr);
+#else
+#if defined(_AIX43) || ((defined(TRU64) || defined(HP_UX)) && defined(HAVE_GETHOSTBYNAME_R))
+  struct hostent_data ht_data;
+#endif
+#ifdef AS400
+  iadd = inet_addr((void*)addr);
+#else
+  iadd = inet_addr(addr);
+#endif
+#endif
+  if (iadd != -1)
+  { memcpy(inaddr, &iadd, sizeof(iadd));
+    return SOAP_OK;
+  }
+#if defined(__GLIBC__) || (defined(HAVE_GETHOSTBYNAME_R) && (defined(FREEBSD) || defined(__FreeBSD__)))
+  if (gethostbyname_r(addr, &hostent, soap->buf, SOAP_BUFLEN, &host, &soap->errnum) < 0)
+    host = NULL;
+#elif defined(_AIX43) || ((defined(TRU64) || defined(HP_UX)) && defined(HAVE_GETHOSTBYNAME_R))
+  memset((void*)&ht_data, 0, sizeof(ht_data));
+  if (gethostbyname_r(addr, &hostent, &ht_data) < 0)
+  { host = NULL;
+    soap->errnum = h_errno;
+  }
+#elif defined(HAVE_GETHOSTBYNAME_R)
+  host = gethostbyname_r(addr, &hostent, soap->buf, SOAP_BUFLEN, &soap->errnum);
+#elif defined(VXWORKS)
+  /* If the DNS resolver library resolvLib has been configured in the vxWorks
+   * image, a query for the host IP address is sent to the DNS server, if the
+   * name was not found in the local host table. */
+  hostint = hostGetByName((char*)addr);
+  if (hostint == ERROR)
+  { host = NULL;
+    soap->errnum = soap_errno; 
+  }
+#else
+#ifdef AS400
+  if (!(host = gethostbyname((void*)addr)))
+    soap->errnum = h_errno;
+#else
+  if (!(host = gethostbyname(addr)))
+    soap->errnum = h_errno;
+#endif
+#endif
+  if (!host)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Host name not found\n"));
+    return SOAP_ERR;
+  }
+#ifdef VXWORKS
+  inaddr->s_addr = hostint;
+#else
+  memcpy(inaddr, host->h_addr, host->h_length);
+#endif
+  return SOAP_OK;
+}
+#endif
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static SOAP_SOCKET
+tcp_connect(struct soap *soap, const char *endpoint, const char *host, int port)
+{
+#ifdef WITH_IPV6
+  struct addrinfo hints, *res, *ressave;
+#endif
+  SOAP_SOCKET fd;
+  int err = 0;
+#ifndef WITH_LEAN
+  int retry = 10;
+  int len = SOAP_BUFLEN;
+  int set = 1;
+#endif
+  if (soap_valid_socket(soap->socket))
+    soap->fclosesocket(soap, soap->socket);
+  soap->socket = SOAP_INVALID_SOCKET;
+  if (tcp_init(soap))
+  { soap->errnum = 0;
+    soap_set_sender_error(soap, tcp_error(soap), "TCP init failed in tcp_connect()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+  soap->errmode = 0;
+#ifdef WITH_IPV6
+  memset((void*)&hints, 0, sizeof(hints));
+  hints.ai_family = PF_UNSPEC;
+#ifndef WITH_LEAN
+  if ((soap->omode & SOAP_IO_UDP))
+    hints.ai_socktype = SOCK_DGRAM;
+  else
+#endif
+    hints.ai_socktype = SOCK_STREAM;
+  soap->errmode = 2;
+  if (soap->proxy_host)
+    err = getaddrinfo(soap->proxy_host, soap_int2s(soap, soap->proxy_port), &hints, &res);
+  else
+    err = getaddrinfo(host, soap_int2s(soap, port), &hints, &res);
+  if (err)
+  { soap_set_sender_error(soap, SOAP_GAI_STRERROR(err), "getaddrinfo failed in tcp_connect()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+  ressave = res;
+again:
+  fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+  soap->errmode = 0;
+#else
+#ifndef WITH_LEAN
+again:
+#endif
+#ifndef WITH_LEAN
+  if ((soap->omode & SOAP_IO_UDP))
+    fd = socket(AF_INET, SOCK_DGRAM, 0);
+  else
+#endif
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+#endif
+  if (!soap_valid_socket(fd))
+  {
+#ifdef WITH_IPV6
+    if (res->ai_next)
+    { res = res->ai_next;
+      goto again;
+    }
+#endif
+    soap->errnum = soap_socket_errno(fd);
+    soap_set_sender_error(soap, tcp_error(soap), "socket failed in tcp_connect()", SOAP_TCP_ERROR);
+#ifdef WITH_IPV6
+    freeaddrinfo(ressave);
+#endif
+    return SOAP_INVALID_SOCKET;
+  }
+#ifdef SOCKET_CLOSE_ON_EXEC
+#ifdef WIN32
+#ifndef UNDER_CE
+  SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0);
+#endif
+#else
+  fcntl(fd, F_SETFD, 1);
+#endif
+#endif
+#ifndef WITH_LEAN
+  if (soap->connect_flags == SO_LINGER)
+  { struct linger linger;
+    memset((void*)&linger, 0, sizeof(linger));
+    linger.l_onoff = 1;
+    linger.l_linger = soap->linger_time;
+    if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(struct linger)))
+    { soap->errnum = soap_socket_errno(fd);
+      soap_set_sender_error(soap, tcp_error(soap), "setsockopt SO_LINGER failed in tcp_connect()", SOAP_TCP_ERROR);
+      soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+      freeaddrinfo(ressave);
+#endif
+      return SOAP_INVALID_SOCKET;
+    }
+  }
+  else if (soap->connect_flags && setsockopt(fd, SOL_SOCKET, soap->connect_flags, (char*)&set, sizeof(int)))
+  { soap->errnum = soap_socket_errno(fd);
+    soap_set_sender_error(soap, tcp_error(soap), "setsockopt failed in tcp_connect()", SOAP_TCP_ERROR);
+    soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+    freeaddrinfo(ressave);
+#endif
+    return SOAP_INVALID_SOCKET;
+  }
+  if ((soap->keep_alive || soap->tcp_keep_alive) && setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&set, sizeof(int)))
+  { soap->errnum = soap_socket_errno(fd);
+    soap_set_sender_error(soap, tcp_error(soap), "setsockopt SO_KEEPALIVE failed in tcp_connect()", SOAP_TCP_ERROR);
+    soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+    freeaddrinfo(ressave);
+#endif
+    return SOAP_INVALID_SOCKET;
+  }
+  if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char*)&len, sizeof(int)))
+  { soap->errnum = soap_socket_errno(fd);
+    soap_set_sender_error(soap, tcp_error(soap), "setsockopt SO_SNDBUF failed in tcp_connect()", SOAP_TCP_ERROR);
+    soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+    freeaddrinfo(ressave);
+#endif
+    return SOAP_INVALID_SOCKET;
+  }
+  if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&len, sizeof(int)))
+  { soap->errnum = soap_socket_errno(fd);
+    soap_set_sender_error(soap, tcp_error(soap), "setsockopt SO_RCVBUF failed in tcp_connect()", SOAP_TCP_ERROR);
+    soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+    freeaddrinfo(ressave);
+#endif
+    return SOAP_INVALID_SOCKET;
+  }
+#ifdef TCP_KEEPIDLE
+  if (soap->tcp_keep_idle && setsockopt((SOAP_SOCKET)fd, IPPROTO_TCP, TCP_KEEPIDLE, (unsigned int*)&(soap->tcp_keep_idle), sizeof(int)))
+  { soap->errnum = soap_socket_errno(fd);
+    soap_set_sender_error(soap, tcp_error(soap), "setsockopt TCP_KEEPIDLE failed in tcp_connect()", SOAP_TCP_ERROR);
+    soap->fclosesocket(soap, (SOAP_SOCKET)fd);
+#ifdef WITH_IPV6
+    freeaddrinfo(ressave);
+#endif
+    return SOAP_INVALID_SOCKET;
+  }
+#endif
+#ifdef TCP_KEEPINTVL
+  if (soap->tcp_keep_intvl && setsockopt((SOAP_SOCKET)fd, IPPROTO_TCP, TCP_KEEPINTVL, (unsigned int*)&(soap->tcp_keep_intvl), sizeof(int)))
+  { soap->errnum = soap_socket_errno(fd);
+    soap_set_sender_error(soap, tcp_error(soap), "setsockopt TCP_KEEPINTVL failed in tcp_connect()", SOAP_TCP_ERROR);
+    soap->fclosesocket(soap, (SOAP_SOCKET)fd);
+#ifdef WITH_IPV6
+    freeaddrinfo(ressave);
+#endif
+    return SOAP_INVALID_SOCKET;
+  }
+#endif
+#ifdef TCP_KEEPCNT
+  if (soap->tcp_keep_cnt && setsockopt((SOAP_SOCKET)fd, IPPROTO_TCP, TCP_KEEPCNT, (unsigned int*)&(soap->tcp_keep_cnt), sizeof(int)))
+  { soap->errnum = soap_socket_errno(fd);
+    soap_set_sender_error(soap, tcp_error(soap), "setsockopt TCP_KEEPCNT failed in tcp_connect()", SOAP_TCP_ERROR);
+    soap->fclosesocket(soap, (SOAP_SOCKET)fd);
+#ifdef WITH_IPV6
+    freeaddrinfo(ressave);
+#endif
+    return SOAP_INVALID_SOCKET;
+  }
+#endif
+#ifdef TCP_NODELAY
+  if (!(soap->omode & SOAP_IO_UDP) && setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&set, sizeof(int)))
+  { soap->errnum = soap_socket_errno(fd);
+    soap_set_sender_error(soap, tcp_error(soap), "setsockopt TCP_NODELAY failed in tcp_connect()", SOAP_TCP_ERROR);
+    soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+    freeaddrinfo(ressave);
+#endif
+    return SOAP_INVALID_SOCKET;
+  }
+#endif
+#ifdef WITH_IPV6
+  if ((soap->omode & SOAP_IO_UDP) && soap->ipv6_multicast_if)
+  { struct sockaddr_in6 *in6addr = (struct sockaddr_in6*)res->ai_addr;
+    in6addr->sin6_scope_id = soap->ipv6_multicast_if;
+  }
+#else
+  if ((soap->omode & SOAP_IO_UDP) && soap->ipv4_multicast_if)
+  { if (soap->ipv4_multicast_ttl > 0)
+    { char ttl = (char)(soap->ipv4_multicast_ttl);
+      if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)))
+      { soap->errnum = soap_socket_errno(fd);
+        soap_set_sender_error(soap, tcp_error(soap), "setsockopt IP_MULTICAST_TTL failed in tcp_connect()", SOAP_TCP_ERROR);
+        soap->fclosesocket(soap, fd);
+        return SOAP_INVALID_SOCKET;
+      }
+    }
+#ifndef WINDOWS
+    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, soap->ipv4_multicast_if, sizeof(struct in_addr))) 
+    { soap->errnum = soap_socket_errno(fd);
+      soap_set_sender_error(soap, tcp_error(soap), "setsockopt IP_MULTICAST_IF failed in tcp_connect()", SOAP_TCP_ERROR);
+      soap->fclosesocket(soap, fd);
+      return SOAP_INVALID_SOCKET;
+    }
+#else
+#ifndef IP_MULTICAST_IF
+#define IP_MULTICAST_IF 2
+#endif
+    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, soap->ipv4_multicast_if, sizeof(struct in_addr))) 
+    { soap->errnum = soap_socket_errno(fd);
+      soap_set_sender_error(soap, tcp_error(soap), "setsockopt IP_MULTICAST_IF failed in tcp_connect()", SOAP_TCP_ERROR);
+      soap->fclosesocket(soap, fd);
+      return SOAP_INVALID_SOCKET;
+    }
+#endif
+  }
+#endif
+#endif
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Opening socket %d to host='%s' port=%d\n", fd, host, port));
+#ifndef WITH_IPV6
+  soap->peerlen = sizeof(soap->peer);
+  memset((void*)&soap->peer, 0, sizeof(soap->peer));
+  soap->peer.sin_family = AF_INET;
+  soap->errmode = 2;
+  if (soap->proxy_host)
+  { if (soap->fresolve(soap, soap->proxy_host, &soap->peer.sin_addr))
+    { soap_set_sender_error(soap, tcp_error(soap), "get proxy host by name failed in tcp_connect()", SOAP_TCP_ERROR);
+      soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+      freeaddrinfo(ressave);
+#endif
+      return SOAP_INVALID_SOCKET;
+    }
+    soap->peer.sin_port = htons((short)soap->proxy_port);
+  }
+  else
+  { if (soap->fresolve(soap, host, &soap->peer.sin_addr))
+    { soap_set_sender_error(soap, tcp_error(soap), "get host by name failed in tcp_connect()", SOAP_TCP_ERROR);
+      soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+      freeaddrinfo(ressave);
+#endif
+      return SOAP_INVALID_SOCKET;
+    }
+    soap->peer.sin_port = htons((short)port);
+  }
+  soap->errmode = 0;
+#ifndef WITH_LEAN
+  if ((soap->omode & SOAP_IO_UDP))
+  {
+#ifdef WITH_IPV6
+    freeaddrinfo(ressave);
+#endif
+    return fd;
+  }
+#endif
+#endif
+#ifndef WITH_LEAN
+  if (soap->connect_timeout)
+    SOAP_SOCKNONBLOCK(fd)
+  else
+    SOAP_SOCKBLOCK(fd)
+#endif
+  for (;;)
+  { 
+#ifdef WITH_IPV6
+    if (connect(fd, res->ai_addr, (int)res->ai_addrlen))
+#else
+    if (connect(fd, (struct sockaddr*)&soap->peer, sizeof(soap->peer)))
+#endif
+    { err = soap_socket_errno(fd);
+#ifndef WITH_LEAN
+      if (err == SOAP_EADDRINUSE)
+      { soap->fclosesocket(soap, fd);
+        if (retry-- > 0)
+          goto again;
+      }
+      else if (soap->connect_timeout && (err == SOAP_EINPROGRESS || err == SOAP_EAGAIN || err == SOAP_EWOULDBLOCK))
+      {
+        SOAP_SOCKLEN_T k;
+#ifndef WIN32
+        if ((int)soap->socket >= (int)FD_SETSIZE)
+        { soap->error = SOAP_FD_EXCEEDED;
+#ifdef WITH_IPV6
+          freeaddrinfo(ressave);
+#endif
+          return SOAP_INVALID_SOCKET;	/* Hint: MUST increase FD_SETSIZE */
+        }
+#endif
+        for (;;)
+        { struct timeval timeout;
+          fd_set fds;
+          register int r;
+          if (soap->connect_timeout > 0)
+          { timeout.tv_sec = soap->connect_timeout;
+            timeout.tv_usec = 0;
+          }
+          else
+          { timeout.tv_sec = -soap->connect_timeout/1000000;
+            timeout.tv_usec = -soap->connect_timeout%1000000;
+          }
+          FD_ZERO(&fds);
+          FD_SET(fd, &fds);
+          r = select((int)fd + 1, NULL, &fds, NULL, &timeout);
+          if (r > 0)
+            break;
+          if (!r)
+          { soap->errnum = 0;
+            DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Connect timeout\n"));
+            soap_set_sender_error(soap, "Timeout", "connect failed in tcp_connect()", SOAP_TCP_ERROR);
+            soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+            freeaddrinfo(ressave);
+#endif
+            return SOAP_INVALID_SOCKET;
+          }
+          r = soap_socket_errno(fd);
+          if (r != SOAP_EINTR)
+          { soap->errnum = r;
+            DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not connect to host\n"));
+            soap_set_sender_error(soap, tcp_error(soap), "connect failed in tcp_connect()", SOAP_TCP_ERROR);
+            soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+            freeaddrinfo(ressave);
+#endif
+            return SOAP_INVALID_SOCKET;
+          }
+        }
+        k = (SOAP_SOCKLEN_T)sizeof(soap->errnum);
+        if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&soap->errnum, &k) && !soap->errnum)	/* portability note: see SOAP_SOCKLEN_T definition in stdsoap2.h */
+          break;
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not connect to host\n"));
+        if (!soap->errnum)
+          soap->errnum = soap_socket_errno(fd);
+        soap_set_sender_error(soap, tcp_error(soap), "connect failed in tcp_connect()", SOAP_TCP_ERROR);
+        soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+        freeaddrinfo(ressave);
+#endif
+        return SOAP_INVALID_SOCKET;
+      }
+#endif
+#ifdef WITH_IPV6
+      if (res->ai_next)
+      { res = res->ai_next;
+        soap->fclosesocket(soap, fd);
+        goto again;
+      }
+#endif
+      if (err && err != SOAP_EINTR)
+      { soap->errnum = err;
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not connect to host\n"));
+        soap_set_sender_error(soap, tcp_error(soap), "connect failed in tcp_connect()", SOAP_TCP_ERROR);
+        soap->fclosesocket(soap, fd);
+#ifdef WITH_IPV6
+        freeaddrinfo(ressave);
+#endif
+        return SOAP_INVALID_SOCKET;
+      }
+    }  
+    else
+      break;
+  }
+#ifdef WITH_IPV6
+  soap->peerlen = 0; /* IPv6: already connected so use send() */
+  freeaddrinfo(ressave);
+#endif
+#ifndef WITH_LEAN
+  if (soap->recv_timeout || soap->send_timeout)
+    SOAP_SOCKNONBLOCK(fd)
+  else
+    SOAP_SOCKBLOCK(fd)
+#endif
+  soap->socket = fd;
+  soap->imode &= ~SOAP_ENC_SSL;
+  soap->omode &= ~SOAP_ENC_SSL;
+  if (!soap_tag_cmp(endpoint, "https:*"))
+  {
+#ifdef WITH_OPENSSL
+    BIO *bio;
+    int r;
+    if (soap->proxy_host)
+    { soap_mode m = soap->omode; /* make sure we only parse HTTP */
+      size_t n = soap->count; /* save the content length */
+      char *userid, *passwd;
+      soap->omode &= ~SOAP_ENC; /* mask IO and ENC */
+      soap->omode |= SOAP_IO_BUFFER;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Connecting to %s proxy server\n", soap->proxy_http_version));
+      sprintf(soap->tmpbuf, "CONNECT %s:%d HTTP/%s", host, port, soap->proxy_http_version);
+      if (soap_begin_send(soap)
+       || (soap->error = soap->fposthdr(soap, soap->tmpbuf, NULL)))
+      { soap->fclosesocket(soap, fd);
+        return SOAP_INVALID_SOCKET;
+      }
+#ifndef WITH_LEAN
+      if (soap->proxy_userid && soap->proxy_passwd && strlen(soap->proxy_userid) + strlen(soap->proxy_passwd) < 761)
+      { sprintf(soap->tmpbuf + 262, "%s:%s", soap->proxy_userid, soap->proxy_passwd);
+        strcpy(soap->tmpbuf, "Basic ");
+        soap_s2base64(soap, (const unsigned char*)(soap->tmpbuf + 262), soap->tmpbuf + 6, (int)strlen(soap->tmpbuf + 262));
+        if ((soap->error = soap->fposthdr(soap, "Proxy-Authorization", soap->tmpbuf)))
+        { soap->fclosesocket(soap, fd);
+          return soap->error;
+        }
+      }
+#endif
+      if ((soap->error = soap->fposthdr(soap, NULL, NULL))
+       || soap_flush(soap))
+      { soap->fclosesocket(soap, fd);
+        return SOAP_INVALID_SOCKET;
+      }
+      soap->omode = m;
+      m = soap->imode;
+      soap->imode &= ~SOAP_ENC; /* mask IO and ENC */
+      userid = soap->userid; /* preserve */
+      passwd = soap->passwd; /* preserve */
+      if ((soap->error = soap->fparse(soap)))
+      { soap->fclosesocket(soap, fd);
+        return SOAP_INVALID_SOCKET;
+      }
+      soap->userid = userid; /* restore */
+      soap->passwd = passwd; /* restore */
+      soap->imode = m; /* restore */
+      soap->count = n; /* restore */
+      if (soap_begin_send(soap))
+      { soap->fclosesocket(soap, fd);
+        return SOAP_INVALID_SOCKET;
+      }
+      if (endpoint)
+        strncpy(soap->endpoint, endpoint, sizeof(soap->endpoint)-1); /* restore */
+    }
+    if (!soap->ctx && (soap->error = soap->fsslauth(soap)))
+    { soap_set_sender_error(soap, "SSL error", "SSL authentication failed in tcp_connect(): check password, key file, and ca file.", SOAP_SSL_ERROR);
+      soap->fclosesocket(soap, fd);
+      return SOAP_INVALID_SOCKET;
+    }
+    if (!soap->ssl)
+    { soap->ssl = SSL_new(soap->ctx);
+      if (!soap->ssl)
+      { soap->fclosesocket(soap, fd);
+        soap->error = SOAP_SSL_ERROR;
+        return SOAP_INVALID_SOCKET;
+      }
+    }
+    else
+      SSL_clear(soap->ssl);
+    if (soap->session)
+    { if (!strcmp(soap->session_host, host) && soap->session_port == port)
+        SSL_set_session(soap->ssl, soap->session);
+      SSL_SESSION_free(soap->session);
+      soap->session = NULL;
+    }
+    soap->imode |= SOAP_ENC_SSL;
+    soap->omode |= SOAP_ENC_SSL;
+    bio = BIO_new_socket((int)fd, BIO_NOCLOSE);
+    SSL_set_bio(soap->ssl, bio, bio);
+#ifndef WITH_LEAN
+    /* Connect timeout: set SSL sockets to non-blocking */
+    if (soap->connect_timeout)
+      SOAP_SOCKNONBLOCK(fd)
+    else
+      SOAP_SOCKBLOCK(fd)
+    /* Try connecting until success or timeout */
+    do
+    { if ((r = SSL_connect(soap->ssl)) <= 0)
+      { int err;
+        if ((err = SSL_get_error(soap->ssl, r)) == SSL_ERROR_NONE)
+	  break;
+        if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
+        { soap_set_sender_error(soap, soap_ssl_error(soap, r), "SSL connect failed in tcp_connect()", SOAP_SSL_ERROR);
+          soap->fclosesocket(soap, fd);
+          return SOAP_INVALID_SOCKET;
+        }
+        if (soap->connect_timeout)
+        {
+#ifndef WIN32
+          if ((int)soap->socket >= (int)FD_SETSIZE)
+          { soap->error = SOAP_FD_EXCEEDED;
+            return SOAP_INVALID_SOCKET;	/* Hint: MUST increase FD_SETSIZE */
+          }
+#endif
+          for (;;)
+          { struct timeval timeout;
+            fd_set fds;
+            register int r;
+            if (soap->connect_timeout > 0)
+            { timeout.tv_sec = soap->connect_timeout;
+              timeout.tv_usec = 0;
+            }
+            else
+            { timeout.tv_sec = -soap->connect_timeout/1000000;
+              timeout.tv_usec = -soap->connect_timeout%1000000;
+            }
+            FD_ZERO(&fds);
+            FD_SET(fd, &fds);
+	    if (err == SSL_ERROR_WANT_READ)
+              r = select((int)fd + 1, &fds, NULL, NULL, &timeout);
+	    else
+              r = select((int)fd + 1, NULL, &fds, NULL, &timeout);
+            if (r >= 1)
+            { r = 1;
+	      break;
+	    }
+            else
+            { soap->errnum = 0;
+              DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Connect timeout\n"));
+              soap_set_sender_error(soap, "Timeout", "connect failed in tcp_connect()", SOAP_TCP_ERROR);
+              soap->fclosesocket(soap, fd);
+              return SOAP_INVALID_SOCKET;
+            }
+          }
+          continue;
+        }
+      }
+      break;
+    } while (r == 1 && !SSL_is_init_finished(soap->ssl));
+    /* Set SSL sockets to nonblocking */
+    SOAP_SOCKNONBLOCK(fd)
+#endif
+    /* Check server credentials when required */
+    if ((soap->ssl_flags & SOAP_SSL_REQUIRE_SERVER_AUTHENTICATION))
+    { int err;
+      if ((err = SSL_get_verify_result(soap->ssl)) != X509_V_OK)
+      { soap_set_sender_error(soap, X509_verify_cert_error_string(err), "SSL certificate presented by peer cannot be verified in tcp_connect()", SOAP_SSL_ERROR);
+        soap->fclosesocket(soap, fd);
+        return SOAP_INVALID_SOCKET;
+      }
+      if (!(soap->ssl_flags & SOAP_SSL_SKIP_HOST_CHECK))
+      { X509_NAME *subj;
+        int ext_count;
+        int ok = 0;
+        X509 *peer;
+        peer = SSL_get_peer_certificate(soap->ssl);
+        if (!peer)
+        { soap_set_sender_error(soap, "SSL error", "No SSL certificate was presented by the peer in tcp_connect()", SOAP_SSL_ERROR);
+          soap->fclosesocket(soap, fd);
+          return SOAP_INVALID_SOCKET;
+        }
+        ext_count = X509_get_ext_count(peer);
+        if (ext_count > 0)
+        { int i;
+          for (i = 0; i < ext_count; i++)
+          { X509_EXTENSION *ext = X509_get_ext(peer, i);
+            const char *ext_str = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
+            if (ext_str && !strcmp(ext_str, "subjectAltName"))
+            { X509V3_EXT_METHOD *meth = X509V3_EXT_get(ext);
+              void *ext_data;
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
+              const unsigned char *data;
+#else
+              unsigned char *data;
+#endif
+              STACK_OF(CONF_VALUE) *val;
+              int j;
+              if (!meth)
+                break;
+              data = ext->value->data;
+#if (OPENSSL_VERSION_NUMBER > 0x00907000L)
+              if (meth->it) 
+                ext_data = ASN1_item_d2i(NULL, &data, ext->value->length, ASN1_ITEM_ptr(meth->it));
+              else
+              { /* OpenSSL not perfectly portable at this point (?):
+                   Some compilers appear to prefer
+                   meth->d2i(NULL, (const unsigned char**)&data, ...
+                   or
+                   meth->d2i(NULL, &data, ext->value->length);
+                */
+                ext_data = meth->d2i(NULL, &data, ext->value->length);
+              }
+#else
+              ext_data = meth->d2i(NULL, &data, ext->value->length);
+#endif
+              val = meth->i2v(meth, ext_data, NULL);
+              for (j = 0; j < sk_CONF_VALUE_num(val); j++)
+              { CONF_VALUE *nval = sk_CONF_VALUE_value(val, j);
+                if (nval && !strcmp(nval->name, "DNS") && !strcmp(nval->value, host))
+                { ok = 1;
+                  break;
+                }
+              }
+            }
+            if (ok)
+              break;
+          }
+        }
+        if (!ok && (subj = X509_get_subject_name(peer)))
+        { int i = -1;
+          do
+          { ASN1_STRING *name;
+            i = X509_NAME_get_index_by_NID(subj, NID_commonName, i);
+            if (i == -1)
+              break;
+            name = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, i));
+            if (name)
+            { if (!soap_tag_cmp(host, (const char*)name))
+                ok = 1;
+              else
+              { unsigned char *tmp = NULL;
+                ASN1_STRING_to_UTF8(&tmp, name);
+                if (tmp)
+                { if (!soap_tag_cmp(host, (const char*)tmp))
+                    ok = 1;
+                  OPENSSL_free(tmp);
+                }
+              }
+            }
+          } while (!ok);
+        }
+        X509_free(peer);
+        if (!ok)
+        { soap_set_sender_error(soap, "SSL error", "SSL certificate host name mismatch in tcp_connect()", SOAP_SSL_ERROR);
+          soap->fclosesocket(soap, fd);
+          return SOAP_INVALID_SOCKET;
+        }
+      }
+    }
+#else
+    soap->fclosesocket(soap, fd);
+    soap->error = SOAP_SSL_ERROR;
+    return SOAP_INVALID_SOCKET;
+#endif
+  }
+  return fd;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+SOAP_FMAC1
+SOAP_SOCKET
+SOAP_FMAC2
+soap_bind(struct soap *soap, const char *host, int port, int backlog)
+{
+#ifdef WITH_IPV6
+  struct addrinfo *addrinfo = NULL;
+  struct addrinfo hints;
+  struct addrinfo res;
+  int err;
+#endif
+#ifndef WITH_LEAN
+  int len = SOAP_BUFLEN;
+  int set = 1;
+#endif
+  if (soap_valid_socket(soap->master))
+  { soap->fclosesocket(soap, soap->master);
+    soap->master = SOAP_INVALID_SOCKET;
+  }
+  soap->socket = SOAP_INVALID_SOCKET;
+  soap->errmode = 1;
+  if (tcp_init(soap))
+  { soap_set_receiver_error(soap, tcp_error(soap), "TCP init failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+#ifdef WITH_IPV6
+  memset((void*)&hints, 0, sizeof(hints));
+  hints.ai_family = PF_UNSPEC;
+#ifndef WITH_LEAN
+  if ((soap->omode & SOAP_IO_UDP))
+    hints.ai_socktype = SOCK_DGRAM;
+  else
+#endif
+    hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_PASSIVE;
+  soap->errmode = 2;
+  err = getaddrinfo(host, soap_int2s(soap, port), &hints, &addrinfo);
+  if (addrinfo)
+  { res = *addrinfo;
+    memcpy(&soap->peer, addrinfo->ai_addr, addrinfo->ai_addrlen);
+    soap->peerlen = addrinfo->ai_addrlen;
+    res.ai_addr = (struct sockaddr*)&soap->peer;
+    res.ai_addrlen = soap->peerlen;
+    freeaddrinfo(addrinfo);
+  }
+  if (err || !addrinfo)
+  { soap_set_receiver_error(soap, SOAP_GAI_STRERROR(err), "getaddrinfo failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+  soap->master = (int)socket(res.ai_family, res.ai_socktype, res.ai_protocol);
+#else
+#ifndef WITH_LEAN
+  if ((soap->omode & SOAP_IO_UDP))
+    soap->master = (int)socket(AF_INET, SOCK_DGRAM, 0);
+  else
+#endif
+    soap->master = (int)socket(AF_INET, SOCK_STREAM, 0);
+#endif
+  soap->errmode = 0;
+  if (!soap_valid_socket(soap->master))
+  { soap->errnum = soap_socket_errno(soap->master);
+    soap_set_receiver_error(soap, tcp_error(soap), "socket failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+#ifndef WITH_LEAN
+  if ((soap->omode & SOAP_IO_UDP))
+    soap->socket = soap->master;
+#endif
+#ifdef SOCKET_CLOSE_ON_EXEC
+#ifdef WIN32
+#ifndef UNDER_CE
+  SetHandleInformation((HANDLE)soap->master, HANDLE_FLAG_INHERIT, 0);
+#endif
+#else
+  fcntl(soap->master, F_SETFD, 1);
+#endif
+#endif
+#ifndef WITH_LEAN
+  if (soap->bind_flags && setsockopt(soap->master, SOL_SOCKET, soap->bind_flags, (char*)&set, sizeof(int)))
+  { soap->errnum = soap_socket_errno(soap->master);
+    soap_set_receiver_error(soap, tcp_error(soap), "setsockopt failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+  if (((soap->imode | soap->omode) & SOAP_IO_KEEPALIVE) && setsockopt(soap->master, SOL_SOCKET, SO_KEEPALIVE, (char*)&set, sizeof(int)))
+  { soap->errnum = soap_socket_errno(soap->master);
+    soap_set_receiver_error(soap, tcp_error(soap), "setsockopt SO_KEEPALIVE failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+  if (setsockopt(soap->master, SOL_SOCKET, SO_SNDBUF, (char*)&len, sizeof(int)))
+  { soap->errnum = soap_socket_errno(soap->master);
+    soap_set_receiver_error(soap, tcp_error(soap), "setsockopt SO_SNDBUF failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+  if (setsockopt(soap->master, SOL_SOCKET, SO_RCVBUF, (char*)&len, sizeof(int)))
+  { soap->errnum = soap_socket_errno(soap->master);
+    soap_set_receiver_error(soap, tcp_error(soap), "setsockopt SO_RCVBUF failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+#ifdef TCP_NODELAY
+  if (!(soap->omode & SOAP_IO_UDP) && setsockopt(soap->master, IPPROTO_TCP, TCP_NODELAY, (char*)&set, sizeof(int)))
+  { soap->errnum = soap_socket_errno(soap->master);
+    soap_set_receiver_error(soap, tcp_error(soap), "setsockopt TCP_NODELAY failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+#endif
+#endif
+#ifdef WITH_IPV6
+  soap->errmode = 0;
+  if (bind(soap->master, res.ai_addr, (int)res.ai_addrlen))
+  { soap->errnum = soap_socket_errno(soap->master);
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not bind to host\n"));
+    soap_closesock(soap);
+    soap_set_receiver_error(soap, tcp_error(soap), "bind failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }  
+#else
+  soap->peerlen = sizeof(soap->peer);
+  memset((void*)&soap->peer, 0, sizeof(soap->peer));
+  soap->peer.sin_family = AF_INET;
+  soap->errmode = 2;
+  if (host)
+  { if (soap->fresolve(soap, host, &soap->peer.sin_addr))
+    { soap_set_receiver_error(soap, tcp_error(soap), "get host by name failed in soap_bind()", SOAP_TCP_ERROR);
+      return SOAP_INVALID_SOCKET;
+    }
+  }
+  else
+    soap->peer.sin_addr.s_addr = htonl(INADDR_ANY);
+  soap->peer.sin_port = htons((short)port);
+  soap->errmode = 0;
+  if (bind(soap->master, (struct sockaddr*)&soap->peer, (int)soap->peerlen))
+  { soap->errnum = soap_socket_errno(soap->master);
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not bind to host\n"));
+    soap_closesock(soap);
+    soap_set_receiver_error(soap, tcp_error(soap), "bind failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+#endif
+  if (!(soap->omode & SOAP_IO_UDP) && listen(soap->master, backlog))
+  { soap->errnum = soap_socket_errno(soap->master);
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not bind to host\n"));
+    soap_closesock(soap);
+    soap_set_receiver_error(soap, tcp_error(soap), "listen failed in soap_bind()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }  
+  return soap->master;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_poll(struct soap *soap)
+{ 
+#ifndef WITH_LEAN
+  struct timeval timeout;
+  fd_set rfd, sfd, xfd;
+  register int r;
+#ifndef WIN32
+  if ((int)soap->socket >= (int)FD_SETSIZE)
+    return SOAP_FD_EXCEEDED;	/* Hint: MUST increase FD_SETSIZE */
+#endif
+  timeout.tv_sec = 0;
+  timeout.tv_usec = 0;
+  FD_ZERO(&rfd);
+  FD_ZERO(&sfd);
+  FD_ZERO(&xfd);
+  if (soap_valid_socket(soap->socket))
+  { FD_SET(soap->socket, &rfd);
+    FD_SET(soap->socket, &sfd);
+    FD_SET(soap->socket, &xfd);
+    r = select((int)soap->socket + 1, &rfd, &sfd, &xfd, &timeout);
+    if (r > 0 && FD_ISSET(soap->socket, &xfd))
+      r = -1;
+  }
+  else if (soap_valid_socket(soap->master))
+  { FD_SET(soap->master, &sfd);
+    r = select((int)soap->master + 1, NULL, &sfd, NULL, &timeout);
+  }
+  else
+    return SOAP_OK;
+  if (r > 0)
+  {
+#ifdef WITH_OPENSSL
+    if (soap->imode & SOAP_ENC_SSL)
+    {
+      if (soap_valid_socket(soap->socket)
+       && FD_ISSET(soap->socket, &sfd)
+       && (!FD_ISSET(soap->socket, &rfd)
+        || SSL_peek(soap->ssl, soap->tmpbuf, 1) > 0))
+        return SOAP_OK;
+    }
+    else
+#endif
+      if (soap_valid_socket(soap->socket)
+       && FD_ISSET(soap->socket, &sfd)
+       && (!FD_ISSET(soap->socket, &rfd)
+        || recv(soap->socket, soap->tmpbuf, 1, MSG_PEEK) > 0))
+        return SOAP_OK;
+  }
+  else if (r < 0)
+  { soap->errnum = soap_socket_errno(soap->master);
+    if ((soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) && soap_socket_errno(soap->master) != SOAP_EINTR)
+    { soap_set_receiver_error(soap, tcp_error(soap), "select failed in soap_poll()", SOAP_TCP_ERROR);
+      return soap->error = SOAP_TCP_ERROR;
+    }
+  }
+  else
+    soap->errnum = 0;
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Polling: other end down on socket=%d select=%d\n", soap->socket, r));
+  return SOAP_EOF;
+#else
+  return SOAP_OK;
+#endif
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static SOAP_SOCKET
+tcp_accept(struct soap *soap, SOAP_SOCKET s, struct sockaddr *a, int *n)
+{ SOAP_SOCKET fd;
+  fd = accept(s, a, (SOAP_SOCKLEN_T*)n);	/* portability note: see SOAP_SOCKLEN_T definition in stdsoap2.h */
+#ifdef SOCKET_CLOSE_ON_EXEC
+#ifdef WIN32
+#ifndef UNDER_CE
+  SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0);
+#endif
+#else
+  fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+#endif
+  return fd;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+SOAP_FMAC1
+SOAP_SOCKET
+SOAP_FMAC2
+soap_accept(struct soap *soap)
+{ int n = (int)sizeof(soap->peer);
+#ifndef WITH_LEAN
+  int len = SOAP_BUFLEN;
+  int set = 1;
+#endif
+  soap->error = SOAP_OK;
+#ifndef WITH_LEAN
+  if ((soap->omode & SOAP_IO_UDP))
+    return soap->socket = soap->master;
+#endif
+  memset((void*)&soap->peer, 0, sizeof(soap->peer));
+  soap->socket = SOAP_INVALID_SOCKET;
+  soap->errmode = 0;
+  soap->keep_alive = 0;
+  if (soap_valid_socket(soap->master))
+  { register int err;
+    for (;;)
+    { 
+#ifndef WITH_LEAN
+      if (soap->accept_timeout || soap->send_timeout || soap->recv_timeout)
+      {
+#ifndef WIN32
+        if ((int)soap->socket >= (int)FD_SETSIZE)
+        { soap->error = SOAP_FD_EXCEEDED;
+          return SOAP_INVALID_SOCKET;	/* Hint: MUST increase FD_SETSIZE */
+        }
+#endif
+        for (;;)
+        { struct timeval timeout;
+          fd_set fd;
+          register int r;
+          if (soap->accept_timeout > 0)
+          { timeout.tv_sec = soap->accept_timeout;
+            timeout.tv_usec = 0;
+          }
+          else if (soap->accept_timeout < 0)
+          { timeout.tv_sec = -soap->accept_timeout/1000000;
+            timeout.tv_usec = -soap->accept_timeout%1000000;
+          }
+	  else
+          { timeout.tv_sec = 60;
+            timeout.tv_usec = 0;
+          }
+          FD_ZERO(&fd);
+          FD_SET(soap->master, &fd);
+          r = select((int)soap->master + 1, &fd, &fd, &fd, &timeout);
+          if (r > 0)
+            break;
+          if (!r && soap->accept_timeout)
+          { soap->errnum = 0;
+            soap_set_receiver_error(soap, "Timeout", "accept failed in soap_accept()", SOAP_TCP_ERROR);
+            return SOAP_INVALID_SOCKET;
+          }
+	  if (r < 0)
+          { r = soap_socket_errno(soap->master);
+            if (r != SOAP_EINTR)
+            { soap->errnum = r;
+              soap_closesock(soap);
+              soap_set_sender_error(soap, tcp_error(soap), "accept failed in soap_accept()", SOAP_TCP_ERROR);
+              return SOAP_INVALID_SOCKET;
+            }
+	  }
+        }
+      }
+      if (soap->accept_timeout || soap->send_timeout || soap->recv_timeout)
+        SOAP_SOCKNONBLOCK(soap->master)
+      else
+        SOAP_SOCKBLOCK(soap->master)
+#endif
+      soap->socket = soap->faccept(soap, soap->master, (struct sockaddr*)&soap->peer, &n);
+      soap->peerlen = (size_t)n;
+      if (soap_valid_socket(soap->socket))
+      {
+#ifdef WITH_IPV6
+/* Use soap->host to store the numeric form of the remote host */
+        getnameinfo((struct sockaddr*)&soap->peer, n, soap->host, sizeof(soap->host), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); 
+        DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Accept socket %d from %s\n", soap->socket, soap->host));
+        soap->ip = 0; /* info stored in soap->peer and soap->host */
+        soap->port = 0; /* info stored in soap->peer and soap->host */
+#else
+        soap->ip = ntohl(soap->peer.sin_addr.s_addr);
+        soap->port = (int)ntohs(soap->peer.sin_port); /* does not return port number on some systems */
+        DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Accept socket %d at port %d from IP %d.%d.%d.%d\n", soap->socket, soap->port, (int)(soap->ip>>24)&0xFF, (int)(soap->ip>>16)&0xFF, (int)(soap->ip>>8)&0xFF, (int)soap->ip&0xFF));
+#endif
+#ifndef WITH_LEAN
+        if (soap->accept_flags == SO_LINGER)
+        { struct linger linger;
+          memset((void*)&linger, 0, sizeof(linger));
+          linger.l_onoff = 1;
+          linger.l_linger = soap->linger_time;
+          if (setsockopt(soap->socket, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(struct linger)))
+          { soap->errnum = soap_socket_errno(soap->socket);
+            soap_set_receiver_error(soap, tcp_error(soap), "setsockopt SO_LINGER failed in soap_accept()", SOAP_TCP_ERROR);
+            soap_closesock(soap);
+            return SOAP_INVALID_SOCKET;
+          }
+        }
+        else if (soap->accept_flags && setsockopt(soap->socket, SOL_SOCKET, soap->accept_flags, (char*)&set, sizeof(int)))
+        { soap->errnum = soap_socket_errno(soap->socket);
+          soap_set_receiver_error(soap, tcp_error(soap), "setsockopt failed in soap_accept()", SOAP_TCP_ERROR);
+          soap_closesock(soap);
+          return SOAP_INVALID_SOCKET;
+        }
+        if (((soap->imode | soap->omode) & SOAP_IO_KEEPALIVE) && setsockopt(soap->socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&set, sizeof(int)))
+        { soap->errnum = soap_socket_errno(soap->socket);
+          soap_set_receiver_error(soap, tcp_error(soap), "setsockopt SO_KEEPALIVE failed in soap_accept()", SOAP_TCP_ERROR);
+          soap_closesock(soap);
+          return SOAP_INVALID_SOCKET;
+        }
+        if (setsockopt(soap->socket, SOL_SOCKET, SO_SNDBUF, (char*)&len, sizeof(int)))
+        { soap->errnum = soap_socket_errno(soap->socket);
+          soap_set_receiver_error(soap, tcp_error(soap), "setsockopt SO_SNDBUF failed in soap_accept()", SOAP_TCP_ERROR);
+          soap_closesock(soap);
+          return SOAP_INVALID_SOCKET;
+        }
+        if (setsockopt(soap->socket, SOL_SOCKET, SO_RCVBUF, (char*)&len, sizeof(int)))
+        { soap->errnum = soap_socket_errno(soap->socket);
+          soap_set_receiver_error(soap, tcp_error(soap), "setsockopt SO_RCVBUF failed in soap_accept()", SOAP_TCP_ERROR);
+          soap_closesock(soap);
+          return SOAP_INVALID_SOCKET;
+        }
+#ifdef TCP_NODELAY
+        if (!(soap->omode & SOAP_IO_UDP) && setsockopt(soap->socket, IPPROTO_TCP, TCP_NODELAY, (char*)&set, sizeof(int)))
+        { soap->errnum = soap_socket_errno(soap->socket);
+          soap_set_receiver_error(soap, tcp_error(soap), "setsockopt TCP_NODELAY failed in soap_accept()", SOAP_TCP_ERROR);
+          soap_closesock(soap);
+          return SOAP_INVALID_SOCKET;
+        }
+#endif
+#endif
+        soap->keep_alive = (((soap->imode | soap->omode) & SOAP_IO_KEEPALIVE) != 0);
+        return soap->socket;
+      }
+      err = soap_socket_errno(soap->socket);
+      if (err != 0 && err != SOAP_EINTR && err != SOAP_EAGAIN && err != SOAP_EWOULDBLOCK)
+      { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Accept failed from %s\n", soap->host));
+        soap->errnum = err;
+        soap_set_receiver_error(soap, tcp_error(soap), "accept failed in soap_accept()", SOAP_TCP_ERROR);
+        soap_closesock(soap);
+        return SOAP_INVALID_SOCKET;
+      }
+    }
+  }
+  else
+  { soap->errnum = 0;
+    soap_set_receiver_error(soap, tcp_error(soap), "no master socket in soap_accept()", SOAP_TCP_ERROR);
+    return SOAP_INVALID_SOCKET;
+  }
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static int
+tcp_disconnect(struct soap *soap)
+{
+#ifdef WITH_OPENSSL
+  if (soap->ssl)
+  { int r, s = 0;
+    if (soap->session)
+    { SSL_SESSION_free(soap->session);
+      soap->session = NULL;
+    }
+    if (*soap->host)
+    { soap->session = SSL_get1_session(soap->ssl);
+      if (soap->session)
+      { strcpy(soap->session_host, soap->host);
+        soap->session_port = soap->port;
+      }
+    }
+    r = SSL_shutdown(soap->ssl);
+    if (r == 0)
+    { if (soap_valid_socket(soap->socket))
+      { struct timeval timeout;
+        fd_set fd;
+        if (soap->fshutdownsocket(soap, soap->socket, 1))
+        { /*
+          wait up to 10 seconds for close_notify to be sent by peer (if peer not
+          present, this avoids calling SSL_shutdown() which has a lengthy return
+          timeout)
+          */
+#ifndef WIN32
+          if ((int)soap->socket < (int)FD_SETSIZE)
+          {
+#endif
+            timeout.tv_sec = 10;
+            timeout.tv_usec = 0;
+            FD_ZERO(&fd);
+            FD_SET(soap->socket, &fd);
+            r = select((int)soap->socket + 1, &fd, NULL, &fd, &timeout);
+            if (r <= 0 && soap_socket_errno(soap->socket) != SOAP_EINTR)
+            { soap->errnum = 0;
+              DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Connection lost...\n"));
+              soap->fclosesocket(soap, soap->socket);
+              soap->socket = SOAP_INVALID_SOCKET;
+              ERR_remove_state(0);
+              return SOAP_OK;
+            }
+#ifndef WIN32
+          }
+#endif
+        }
+      }
+      r = SSL_shutdown(soap->ssl);
+    }
+    if (r != 1)
+    { s = ERR_get_error();
+      if (s)
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Shutdown failed: %d\n", SSL_get_error(soap->ssl, r)));
+        if (soap_valid_socket(soap->socket) && !(soap->omode & SOAP_IO_UDP))
+        { soap->fclosesocket(soap, soap->socket);
+          soap->socket = SOAP_INVALID_SOCKET;
+        }
+      }
+    }
+    SSL_free(soap->ssl);
+    soap->ssl = NULL;
+    if (s)
+      return SOAP_SSL_ERROR;
+    ERR_remove_state(0);
+  }
+#endif
+  if (soap_valid_socket(soap->socket) && !(soap->omode & SOAP_IO_UDP))
+  { soap->fshutdownsocket(soap, soap->socket, 2);
+    soap->fclosesocket(soap, soap->socket);
+    soap->socket = SOAP_INVALID_SOCKET;
+  }
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static int
+tcp_closesocket(struct soap *soap, SOAP_SOCKET fd)
+{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Close socket %d\n", (int)fd));
+  return soap_closesocket(fd);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static int
+tcp_shutdownsocket(struct soap *soap, SOAP_SOCKET fd, int how)
+{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Shutdown socket %d how=%d\n", (int)fd, how));
+  return shutdown(fd, how);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_closesock(struct soap *soap)
+{ register int status = soap->error;
+  if (status == SOAP_EOF || status == SOAP_TCP_ERROR || status == SOAP_SSL_ERROR || !soap->keep_alive)
+  { if (soap->fclose && (soap->error = soap->fclose(soap)))
+      return soap->error;
+    soap->keep_alive = 0;
+  }
+#ifdef WITH_ZLIB
+  if (soap->zlib_state == SOAP_ZLIB_DEFLATE)
+    deflateEnd(soap->d_stream);
+  else if (soap->zlib_state == SOAP_ZLIB_INFLATE)
+    inflateEnd(soap->d_stream);
+  soap->zlib_state = SOAP_ZLIB_NONE;
+#endif
+  return soap->error = status;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+size_t
+SOAP_FMAC2
+soap_hash(register const char *s)
+{ register size_t h = 0;
+  while (*s)
+    h = 65599*h + *s++;
+  return h % SOAP_IDHASH;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_1
+static void
+soap_init_pht(struct soap *soap)
+{ register int i;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Initializing pointer hashtable\n"));
+  soap->pblk = NULL;
+  soap->pidx = 0;
+  for (i = 0; i < (int)SOAP_PTRHASH; i++)
+    soap->pht[i] = NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+struct soap*
+SOAP_FMAC2
+soap_new1(soap_mode mode)
+{ return soap_new2(mode, mode);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+struct soap*
+SOAP_FMAC2
+soap_new()
+{ return soap_new2(SOAP_IO_DEFAULT, SOAP_IO_DEFAULT);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+struct soap*
+SOAP_FMAC2
+soap_new2(soap_mode imode, soap_mode omode)
+{ struct soap *soap = (struct soap*)malloc(sizeof(struct soap));
+  if (soap)
+    soap_init2(soap, imode, omode);
+  return soap;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_free(struct soap *soap)
+{ soap_done(soap);
+  free(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_del(struct soap *soap)
+{ free(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_1
+static void
+soap_free_pht(struct soap *soap)
+{ register struct soap_pblk *pb, *next;
+  register int i;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Free pointer hashtable\n"));
+  for (pb = soap->pblk; pb; pb = next)
+  { next = pb->next;
+    SOAP_FREE(soap, pb);
+  }
+  soap->pblk = NULL;
+  soap->pidx = 0;
+  for (i = 0; i < (int)SOAP_PTRHASH; i++)
+    soap->pht[i] = NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_embed(struct soap *soap, const void *p, const struct soap_array *a, int n, const char *tag, int type)
+{ register int i;
+  struct soap_plist *pp;
+  if (soap->version != 1)
+    soap->encoding = 1;
+  if (a)
+    i = soap_array_pointer_lookup(soap, p, a, n, type, &pp);
+  else
+    i = soap_pointer_lookup(soap, p, type, &pp);
+  if (i)
+  { if (soap_is_embedded(soap, pp)
+     || soap_is_single(soap, pp))
+      return 0;
+    soap_set_embedded(soap, pp);
+  }
+  return i;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_pointer_lookup(struct soap *soap, const void *p, int type, struct soap_plist **ppp)
+{ register struct soap_plist *pp;
+  *ppp = NULL;
+  if (p)
+  { for (pp = soap->pht[soap_hash_ptr(p)]; pp; pp = pp->next)
+    { if (pp->ptr == p && pp->type == type)
+      { *ppp = pp;
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Lookup location=%p type=%d id=%d\n", p, type, pp->id));
+        return pp->id;
+      }
+    }
+  }
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Lookup location=%p type=%d: not found\n", p, type));
+  return 0;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_pointer_enter(struct soap *soap, const void *p, const struct soap_array *a, int n, int type, struct soap_plist **ppp)
+{ register size_t h;
+  register struct soap_plist *pp;
+  if (!soap->pblk || soap->pidx >= SOAP_PTRBLK)
+  { register struct soap_pblk *pb = (struct soap_pblk*)SOAP_MALLOC(soap, sizeof(struct soap_pblk));
+    if (!pb)
+    { soap->error = SOAP_EOM;
+      return 0;
+    }
+    pb->next = soap->pblk;
+    soap->pblk = pb;
+    soap->pidx = 0;
+  }
+  *ppp = pp = &soap->pblk->plist[soap->pidx++];
+  if (a)
+    h = soap_hash_ptr(a->__ptr);
+  else
+    h = soap_hash_ptr(p);
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Pointer enter location=%p array=%p size=%d dim=%d type=%d id=%d\n", p, a?a->__ptr:NULL, a?a->__size:0, n, type, soap->idnum+1));
+  pp->next = soap->pht[h];
+  pp->type = type;
+  pp->mark1 = 0;
+  pp->mark2 = 0;
+  pp->ptr = p;
+  pp->array = a;
+  soap->pht[h] = pp;
+  pp->id = ++soap->idnum;
+  return pp->id;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_array_pointer_lookup(struct soap *soap, const void *p, const struct soap_array *a, int n, int type, struct soap_plist **ppp)
+{ register struct soap_plist *pp;
+  *ppp = NULL;
+  if (!p || !a->__ptr)
+    return 0;
+  for (pp = soap->pht[soap_hash_ptr(a->__ptr)]; pp; pp = pp->next)
+  { if (pp->type == type && pp->array && pp->array->__ptr == a->__ptr)
+    { register int i;
+      for (i = 0; i < n; i++)
+        if (((const int*)&pp->array->__size)[i] != ((const int*)&a->__size)[i])
+          break;
+      if (i == n)
+      { *ppp = pp;
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Array lookup location=%p type=%d id=%d\n", a->__ptr, type, pp->id));
+        return pp->id;
+      }
+    }
+  }
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Array lookup location=%p type=%d: not found\n", a->__ptr, type));
+  return 0;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_begin_count(struct soap *soap)
+{
+#ifndef WITH_LEANER
+  if ((soap->mode & SOAP_ENC_DIME) || (soap->omode & SOAP_ENC_DIME))
+    soap->mode = soap->omode | SOAP_IO_LENGTH | SOAP_ENC_DIME;
+  else
+#endif
+  { soap->mode = soap->omode;
+    if ((soap->mode & SOAP_IO) == SOAP_IO_STORE
+     || (((soap->mode & SOAP_IO) == SOAP_IO_CHUNK || (soap->mode & SOAP_ENC_XML))
+#ifndef WITH_LEANER
+      && !soap->fpreparesend
+#endif
+      ))
+      soap->mode &= ~SOAP_IO_LENGTH;
+    else
+      soap->mode |= SOAP_IO_LENGTH;
+  }
+#ifdef WITH_ZLIB
+  if ((soap->mode & SOAP_ENC_ZLIB) && (soap->mode & SOAP_IO) == SOAP_IO_FLUSH)
+  { if (!(soap->mode & SOAP_ENC_DIME))
+      soap->mode &= ~SOAP_IO_LENGTH;
+    if (soap->mode & SOAP_ENC_XML)
+      soap->mode |= SOAP_IO_BUFFER;
+    else
+      soap->mode |= SOAP_IO_STORE;
+  }
+#endif
+  if (!soap->encodingStyle && !(soap->mode & SOAP_XML_GRAPH))
+    soap->mode |= SOAP_XML_TREE;
+#ifndef WITH_LEANER
+  if ((soap->mode & SOAP_ENC_MTOM) && (soap->mode & SOAP_ENC_DIME))
+    soap->mode |= SOAP_ENC_MIME;
+  else
+    soap->mode &= ~SOAP_ENC_MTOM;
+  if (soap->mode & SOAP_ENC_MIME)
+    soap_select_mime_boundary(soap);
+  soap->dime.list = soap->dime.last;	/* keep track of last DIME attachment */
+#endif
+  soap->count = 0;
+  soap->ns = 0;
+  soap->null = 0;
+  soap->position = 0;
+  soap->mustUnderstand = 0;
+  soap->encoding = 0;
+  soap->part = SOAP_BEGIN;
+  soap->idnum = 0;
+  soap_clr_attr(soap);
+  soap_set_local_namespaces(soap);
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Begin count phase (socket=%d mode=0x%x count=%lu)\n", soap->socket, soap->mode, (unsigned long)soap->count));
+#ifndef WITH_LEANER
+  soap->dime.count = 0; /* count # of attachments */
+  soap->dime.size = 0; /* accumulate total size of attachments */
+  if (soap->fprepareinit && (soap->mode & SOAP_IO) != SOAP_IO_STORE)
+    return soap->error = soap->fprepareinit(soap);   
+#endif
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_end_count(struct soap *soap)
+{ 
+#ifndef WITH_LEANER
+  if (soap->fpreparefinal)
+    return soap->error = soap->fpreparefinal(soap);
+#endif
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "End of count phase\n"));
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_begin_send(struct soap *soap)
+{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Initializing for output\n"));
+  soap->error = SOAP_OK;
+  soap->mode = soap->omode | (soap->mode & (SOAP_IO_LENGTH | SOAP_ENC_DIME));
+#ifdef WITH_ZLIB
+  if ((soap->mode & SOAP_ENC_ZLIB) && (soap->mode & SOAP_IO) == SOAP_IO_FLUSH)
+  { if (soap->mode & SOAP_ENC_XML)
+      soap->mode |= SOAP_IO_BUFFER;
+    else
+      soap->mode |= SOAP_IO_STORE;
+  }
+#endif
+#ifndef WITH_LEAN
+  if ((soap->mode & SOAP_IO_UDP))
+  { soap->mode |= SOAP_ENC_XML;
+    if (soap->count > SOAP_BUFLEN)
+      return soap->error = SOAP_UDP_ERROR;
+  }
+#endif
+  if ((soap->mode & SOAP_IO) == SOAP_IO_FLUSH && soap_valid_socket(soap->socket))
+  { if (soap->count || (soap->mode & SOAP_IO_LENGTH) || (soap->mode & SOAP_ENC_XML))
+      soap->mode |= SOAP_IO_BUFFER;
+    else
+      soap->mode |= SOAP_IO_STORE;
+  }
+  soap->mode &= ~SOAP_IO_LENGTH;
+  if ((soap->mode & SOAP_IO) == SOAP_IO_STORE)
+    if (soap_new_block(soap) == NULL)
+      return soap->error;
+  if (!(soap->mode & SOAP_IO_KEEPALIVE))
+    soap->keep_alive = 0;
+  if (!soap->encodingStyle && !(soap->mode & SOAP_XML_GRAPH))
+    soap->mode |= SOAP_XML_TREE;
+#ifndef WITH_LEANER
+  if ((soap->mode & SOAP_ENC_MTOM) && (soap->mode & SOAP_ENC_DIME))
+  { soap->mode |= SOAP_ENC_MIME;
+    soap->mode &= ~SOAP_ENC_DIME;
+  }
+  else
+    soap->mode &= ~SOAP_ENC_MTOM;
+  if (soap->mode & SOAP_ENC_MIME)
+    soap_select_mime_boundary(soap);
+#ifdef WIN32
+#ifndef UNDER_CE
+#ifndef WITH_FASTCGI
+  if (!soap_valid_socket(soap->socket)) /* Set win32 stdout or soap->sendfd to BINARY, e.g. to support DIME */
+#ifdef __BORLANDC__
+    setmode(soap->sendfd, O_BINARY);
+#else
+    _setmode(soap->sendfd, _O_BINARY);
+#endif
+#endif
+#endif
+#endif
+#endif
+  if (soap->mode & SOAP_IO)
+  { soap->bufidx = 0;
+    soap->buflen = 0;
+  }
+  soap->chunksize = 0;
+  soap->ns = 0;
+  soap->null = 0;
+  soap->position = 0;
+  soap->mustUnderstand = 0;
+  soap->encoding = 0;
+  soap->idnum = 0;
+  soap->level = 0;
+  soap_clr_attr(soap);
+  soap_set_local_namespaces(soap);
+#ifdef WITH_ZLIB
+  soap->z_ratio_out = 1.0;
+  if ((soap->mode & SOAP_ENC_ZLIB) && soap->zlib_state != SOAP_ZLIB_DEFLATE)
+  { if (!soap->z_buf)
+      soap->z_buf = (char*)SOAP_MALLOC(soap, SOAP_BUFLEN);
+    soap->d_stream->next_out = (Byte*)soap->z_buf;
+    soap->d_stream->avail_out = SOAP_BUFLEN;
+#ifdef WITH_GZIP
+    if (soap->zlib_out != SOAP_ZLIB_DEFLATE)
+    { memcpy(soap->z_buf, "\37\213\10\0\0\0\0\0\0\377", 10);
+      soap->d_stream->next_out = (Byte*)soap->z_buf + 10;
+      soap->d_stream->avail_out = SOAP_BUFLEN - 10;
+      soap->z_crc = crc32(0L, NULL, 0);
+      soap->zlib_out = SOAP_ZLIB_GZIP;
+      if (deflateInit2(soap->d_stream, soap->z_level, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK)
+        return soap->error = SOAP_ZLIB_ERROR;
+    }
+    else
+#endif
+    if (deflateInit(soap->d_stream, soap->z_level) != Z_OK)
+      return soap->error = SOAP_ZLIB_ERROR;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Deflate initialized\n"));
+    soap->zlib_state = SOAP_ZLIB_DEFLATE;
+  }
+#endif
+#ifdef WITH_OPENSSL
+  if (soap->ssl)
+    ERR_clear_error();
+#endif
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Begin send phase (socket=%d mode=0x%x count=%lu)\n", soap->socket, soap->mode, (unsigned long)soap->count));
+  soap->part = SOAP_BEGIN;
+#ifndef WITH_LEANER
+  if (soap->fprepareinit && (soap->mode & SOAP_IO) == SOAP_IO_STORE)
+    soap->fprepareinit(soap);   
+#endif
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_embedded(struct soap *soap, const void *p, int t)
+{ struct soap_plist *pp;
+  if (soap_pointer_lookup(soap, p, t, &pp))
+  { pp->mark1 = 1;
+    pp->mark2 = 1;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Embedded %p type=%d mark set to 1\n", p, t));
+  }
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_reference(struct soap *soap, const void *p, int t)
+{ struct soap_plist *pp;
+  if (!p || (soap->mode & SOAP_XML_TREE))
+    return 1;
+  if (soap_pointer_lookup(soap, p, t, &pp))
+  { if (pp->mark1 == 0)
+    { pp->mark1 = 2;
+      pp->mark2 = 2;
+    }
+  }
+  else if (soap_pointer_enter(soap, p, NULL, 0, t, &pp))
+  { pp->mark1 = 0;
+    pp->mark2 = 0;
+  }
+  else
+    return 1;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Reference %p type=%d (%d %d)\n", p, t, (int)pp->mark1, (int)pp->mark2));
+  return pp->mark1;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_array_reference(struct soap *soap, const void *p, const struct soap_array *a, int n, int t)
+{ register int i;
+  struct soap_plist *pp;
+  if (!p || !a->__ptr)
+    return 1;
+  i = soap_array_pointer_lookup(soap, p, a, n, t, &pp);
+  if (i)
+  { if (pp->mark1 == 0)
+    { pp->mark1 = 2;
+      pp->mark2 = 2;
+    }
+  }
+  else if (!soap_pointer_enter(soap, p, a, n, t, &pp))
+    return 1;
+  else
+  { pp->mark1 = 0;
+    pp->mark2 = 0;
+  }
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Array reference %p ptr=%p dim=%d type=%d (%d %d)\n", p, a->__ptr, n, t, (int)pp->mark1, (int)pp->mark2));
+  return pp->mark1;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_embedded_id(struct soap *soap, int id, const void *p, int t)
+{ struct soap_plist *pp = NULL;
+  if (soap->mode & SOAP_XML_TREE)
+    return id;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Embedded_id %p type=%d id=%d\n", p, t, id));
+  if (soap->version == 1 && soap->encodingStyle && !(soap->mode & SOAP_XML_GRAPH) && soap->part != SOAP_IN_HEADER)
+  { if (id < 0)
+    { id = soap_pointer_lookup(soap, p, t, &pp);
+      if (id)
+      { if (soap->mode & SOAP_IO_LENGTH)
+          pp->mark1 = 2;
+        else
+          pp->mark2 = 2;
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Embedded_id multiref id=%d %p type=%d = (%d %d)\n", id, p, t, (int)pp->mark1, (int)pp->mark2));
+      }
+      return -1;
+    }
+    return id;
+  }
+  if (id < 0)
+    id = soap_pointer_lookup(soap, p, t, &pp);
+  else if (id && !soap_pointer_lookup(soap, p, t, &pp))
+    return 0;
+  if (id && pp)
+  { if (soap->mode & SOAP_IO_LENGTH)
+      pp->mark1 = 1;
+    else
+      pp->mark2 = 1;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Embedded_id embedded ref id=%d %p type=%d = (%d %d)\n", id, p, t, (int)pp->mark1, (int)pp->mark2));
+  }
+  return id;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_is_embedded(struct soap *soap, struct soap_plist *pp)
+{ if (!pp)
+    return 0;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Is embedded? %d %d\n", (int)pp->mark1, (int)pp->mark2));
+  if (soap->version == 1 && soap->encodingStyle && !(soap->mode & SOAP_XML_GRAPH) && soap->part != SOAP_IN_HEADER)
+  { if (soap->mode & SOAP_IO_LENGTH)
+      return pp->mark1 != 0;
+    return pp->mark2 != 0;
+  }
+  if (soap->mode & SOAP_IO_LENGTH)
+    return pp->mark1 == 1;
+  return pp->mark2 == 1;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_is_single(struct soap *soap, struct soap_plist *pp)
+{ if (soap->part == SOAP_IN_HEADER)
+    return 1;
+  if (!pp)
+    return 0;
+  if (soap->mode & SOAP_IO_LENGTH)
+    return pp->mark1 == 0;
+  return pp->mark2 == 0;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_set_embedded(struct soap *soap, struct soap_plist *pp)
+{ if (!pp)
+    return;
+  if (soap->mode & SOAP_IO_LENGTH)
+    pp->mark1 = 1;
+  else
+    pp->mark2 = 1;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_attachment(struct soap *soap, const char *tag, int id, const void *p, const struct soap_array *a, const char *aid, const char *atype, const char *aoptions, int n, const char *type, int t) 
+{
+#ifndef WITH_NOIDREF
+  struct soap_plist *pp;
+  int i;
+  if (!p || !a->__ptr || (!aid && !atype))
+    return soap_element_id(soap, tag, id, p, a, n, type, t);
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Attachment tag='%s' id='%s' (%d) type='%s'\n", tag, aid?aid:SOAP_STR_EOS, id, atype?atype:SOAP_STR_EOS));
+  i = soap_array_pointer_lookup(soap, p, a, n, t, &pp);
+  if (!i)
+  { i = soap_pointer_enter(soap, p, a, n, t, &pp);
+    if (!i)
+    { soap->error = SOAP_EOM;
+      return -1;
+    }
+  }
+  if (id <= 0)
+    id = i;
+  if (!aid)
+  { sprintf(soap->tmpbuf, soap->dime_id_format, id);
+    aid = soap_strdup(soap, soap->tmpbuf);
+  }
+  /* Add MTOM xop:Include element when necessary */
+  /* TODO: this code to be obsoleted with new import/xop.h conventions */
+  if ((soap->mode & SOAP_ENC_MTOM) && strcmp(tag, "xop:Include"))
+  { if (soap_element_begin_out(soap, tag, 0, type)
+     || soap_element_href(soap, "xop:Include", 0, "href", aid)
+     || soap_element_end_out(soap, tag))
+      return soap->error;
+  }
+  else if (soap_element_href(soap, tag, 0, "href", aid))
+    return soap->error;
+  if (soap->mode & SOAP_IO_LENGTH)
+  { if (pp->mark1 != 3)
+    { struct soap_multipart *content;
+      if (soap->mode & SOAP_ENC_MTOM)
+        content = soap_new_multipart(soap, &soap->mime.first, &soap->mime.last, (char*)a->__ptr, a->__size);
+      else
+        content = soap_new_multipart(soap, &soap->dime.first, &soap->dime.last, (char*)a->__ptr, a->__size);
+      if (!content)
+      { soap->error = SOAP_EOM;
+        return -1;
+      }
+      if (!strncmp(aid, "cid:", 4)) /* RFC 2111 */
+      { if (soap->mode & SOAP_ENC_MTOM)
+        { char *s = (char*)soap_malloc(soap, strlen(aid) - 1);
+          if (s)
+          { *s = '<';
+            strcpy(s + 1, aid + 4);
+            strcat(s, ">");
+            content->id = s;
+          }
+        }
+        else
+          content->id = aid + 4;
+      }
+      else
+        content->id = aid;
+      content->type = atype;
+      content->options = aoptions;
+      content->encoding = SOAP_MIME_BINARY;
+      pp->mark1 = 3;
+    }
+  }
+  else
+    pp->mark2 = 3;
+#endif
+  return -1;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_1
+static void
+soap_init_iht(struct soap *soap)
+{ register int i;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Initializing ID hashtable\n"));
+  for (i = 0; i < SOAP_IDHASH; i++)
+    soap->iht[i] = NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_1
+static void
+soap_free_iht(struct soap *soap)
+{ register int i;
+  register struct soap_ilist *ip = NULL, *p = NULL;
+  register struct soap_flist *fp = NULL, *fq = NULL;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Free ID hashtable\n"));
+  for (i = 0; i < SOAP_IDHASH; i++)
+  { for (ip = soap->iht[i]; ip; ip = p)
+    { for (fp = ip->flist; fp; fp = fq)
+      { fq = fp->next;
+        SOAP_FREE(soap, fp);
+      }
+      p = ip->next;
+      SOAP_FREE(soap, ip);
+    }
+    soap->iht[i] = NULL;
+  }
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+struct soap_ilist *
+SOAP_FMAC2
+soap_lookup(struct soap *soap, const char *id)
+{ register struct soap_ilist *ip = NULL;
+  for (ip = soap->iht[soap_hash(id)]; ip; ip = ip->next)
+    if (!strcmp(ip->id, id))
+      return ip;
+  return NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+struct soap_ilist *
+SOAP_FMAC2
+soap_enter(struct soap *soap, const char *id)
+{ register size_t h;
+  register struct soap_ilist *ip;
+  ip = (struct soap_ilist*)SOAP_MALLOC(soap, sizeof(struct soap_ilist) + strlen(id));
+  if (ip)
+  { h = soap_hash(id);
+    strcpy(ip->id, id);
+    ip->next = soap->iht[h];
+    soap->iht[h] = ip;
+  }
+  return ip;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void*
+SOAP_FMAC2
+soap_malloc(struct soap *soap, size_t n)
+{ register char *p;
+  if (!n)
+    return (void*)SOAP_NON_NULL;
+  if (!soap)
+    return SOAP_MALLOC(soap, n);
+  if (soap->fmalloc)
+    p = (char*)soap->fmalloc(soap, n);
+  else
+  { n += sizeof(short);
+    n += (-(long)n) & (sizeof(void*)-1); /* align at 4-, 8- or 16-byte boundary */
+    if (!(p = (char*)SOAP_MALLOC(soap, n + sizeof(void*) + sizeof(size_t))))
+    { soap->error = SOAP_EOM;
+      return NULL;
+    }
+    /* set the canary to detect corruption */
+    *(short*)(p + n - sizeof(short)) = (short)SOAP_CANARY;
+    /* keep chain of alloced cells for destruction */
+    *(void**)(p + n) = soap->alist;
+    *(size_t*)(p + n + sizeof(void*)) = n;
+    soap->alist = p + n;
+  }
+  soap->alloced = 1;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_MEM_DEBUG
+static void
+soap_init_mht(struct soap *soap)
+{ register int i;
+  for (i = 0; i < (int)SOAP_PTRHASH; i++)
+    soap->mht[i] = NULL;
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_MEM_DEBUG
+static void
+soap_free_mht(struct soap *soap)
+{ register int i;
+  register struct soap_mlist *mp, *mq;
+  for (i = 0; i < (int)SOAP_PTRHASH; i++)
+  { for (mp = soap->mht[i]; mp; mp = mq)
+    { mq = mp->next;
+      if (mp->live)
+        fprintf(stderr, "%s(%d): malloc() = %p not freed (memory leak or forgot to call soap_end()?)\n", mp->file, mp->line, mp->ptr);
+      free(mp);
+    }
+    soap->mht[i] = NULL;
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_MEM_DEBUG
+SOAP_FMAC1
+void*
+SOAP_FMAC2
+soap_track_malloc(struct soap *soap, const char *file, int line, size_t size)
+{ register void *p = malloc(size);
+  if (soap)
+  { register size_t h = soap_hash_ptr(p);
+    register struct soap_mlist *mp = (struct soap_mlist*)malloc(sizeof(struct soap_mlist));
+    if (soap->fdebug[SOAP_INDEX_TEST])
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): malloc(%lu) = %p\n", file, line, (unsigned long)size, p));
+    }
+    mp->next = soap->mht[h];
+    mp->ptr = p;
+    mp->file = file;
+    mp->line = line;
+    mp->live = 1;
+    soap->mht[h] = mp;
+  }
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_MEM_DEBUG
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_track_free(struct soap *soap, const char *file, int line, void *p)
+{ register size_t h = soap_hash_ptr(p);
+  register struct soap_mlist *mp;
+  for (mp = soap->mht[h]; mp; mp = mp->next)
+    if (mp->ptr == p)
+      break;
+  if (mp)
+  { if (mp->live)
+    { free(p);
+      if (soap->fdebug[SOAP_INDEX_TEST])
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): free(%p)\n", file, line, p));
+      }
+      mp->live = 0;
+    }
+    else
+      fprintf(stderr, "%s(%d): free(%p) double free of pointer malloced at %s(%d)\n", file, line, p, mp->file, mp->line);
+  }
+  else
+    fprintf(stderr, "%s(%d): free(%p) pointer not malloced\n", file, line, p);
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_MEM_DEBUG
+static void
+soap_track_unlink(struct soap *soap, const void *p)
+{ register size_t h = soap_hash_ptr(p);
+  register struct soap_mlist *mp;
+  for (mp = soap->mht[h]; mp; mp = mp->next)
+    if (mp->ptr == p)
+      break;
+  if (mp)
+    mp->live = 0;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_dealloc(struct soap *soap, void *p)
+{ if (soap_check_state(soap))
+    return;
+  if (p)
+  { register char **q;
+    for (q = (char**)&soap->alist; *q; q = *(char***)q)
+    { 
+      if (*(short*)(char*)(*q - sizeof(short)) != (short)SOAP_CANARY)
+      {
+#ifdef SOAP_MEM_DEBUG
+        fprintf(stderr, "Data corruption in dynamic allocation (see logs)\n");
+#endif
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Data corruption:\n"));
+        DBGHEX(TEST, *q - 200, 200);
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\n"));
+        soap->error = SOAP_MOE;
+        return;
+      }
+      if (p == (void*)(*q - *(size_t*)(*q + sizeof(void*))))
+      { *q = **(char***)q;
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Freed data at %p\n", p));
+        SOAP_FREE(soap, p);
+        return;
+      }
+    }
+    soap_delete(soap, p);
+  }
+  else
+  { register char *q;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Free all soap_malloc() data\n"));
+    while (soap->alist)
+    { q = (char*)soap->alist;
+      if (*(short*)(char*)(q - sizeof(short)) != (short)SOAP_CANARY)
+      {
+#ifdef SOAP_MEM_DEBUG
+        fprintf(stderr, "Data corruption in dynamic allocation (see logs)\n");
+#endif
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Data corruption:\n"));
+        DBGHEX(TEST, q - 200, 200);
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\n"));
+        soap->error = SOAP_MOE;
+        return;
+      }
+      soap->alist = *(void**)q;
+      q -= *(size_t*)(q + sizeof(void*));
+      SOAP_FREE(soap, q);
+    }
+    /* we must assume these were deallocated: */
+    soap->action = NULL;
+    soap->fault = NULL;
+    soap->header = NULL;
+    soap->userid = NULL;
+    soap->passwd = NULL;
+    soap->authrealm = NULL;
+    soap->http_content = NULL;
+#ifndef WITH_LEANER
+    soap_clr_mime(soap);
+#endif
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_delete(struct soap *soap, void *p)
+{ register struct soap_clist **cp;
+  if (soap_check_state(soap))
+    return;
+  cp = &soap->clist;
+  if (p)
+  { while (*cp)
+    { if (p == (*cp)->ptr)
+      { register struct soap_clist *q = *cp;
+        *cp = q->next;
+        if (q->fdelete(q))
+        { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not dealloc data %p: deletion callback failed for object type %d\n", q->ptr, q->type));
+#ifdef SOAP_MEM_DEBUG
+          fprintf(stderr, "new(object type = %d) = %p not freed: deletion callback failed\n", q->type, q->ptr);
+#endif
+        }
+        SOAP_FREE(soap, q);
+        return;
+      }
+      cp = &(*cp)->next;
+    }
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not dealloc data %p: address not in list\n", p));
+  }
+  else
+  { while (*cp)
+    { register struct soap_clist *q = *cp;
+      *cp = q->next;
+      if (q->fdelete(q))
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not dealloc data %p: deletion callback failed for object type %d\n", q->ptr, q->type));
+#ifdef SOAP_MEM_DEBUG
+        fprintf(stderr, "new(object type = %d) = %p not freed: deletion callback failed\n", q->type, q->ptr);
+#endif
+      }
+      SOAP_FREE(soap, q);
+    }
+  }
+  soap->fault = NULL; /* this was possibly deallocated */
+  soap->header = NULL; /* this was possibly deallocated */
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+struct soap_clist *
+SOAP_FMAC2
+soap_link(struct soap *soap, void *p, int t, int n, int (*fdelete)(struct soap_clist*))
+{ register struct soap_clist *cp;
+  if ((cp = (struct soap_clist*)SOAP_MALLOC(soap, sizeof(struct soap_clist))))
+  { cp->next = soap->clist;
+    cp->type = t;
+    cp->size = n; 
+    cp->ptr = p;
+    cp->fdelete = fdelete;
+    soap->clist = cp;
+  }
+  return cp;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_unlink(struct soap *soap, const void *p)
+{ register char **q;
+  register struct soap_clist **cp;
+  if (!soap || !p)
+    return;
+  for (q = (char**)&soap->alist; *q; q = *(char***)q)
+  { if (p == (void*)(*q - *(size_t*)(*q + sizeof(void*))))
+    { *q = **(char***)q;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Unlinked data %p\n", p));
+#ifdef SOAP_MEM_DEBUG
+      soap_track_unlink(soap, p);
+#endif
+      return;
+    }
+  }
+  for (cp = &soap->clist; *cp; cp = &(*cp)->next)
+  { if (p == (*cp)->ptr)
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Unlinked class instance %p\n", p));
+      q = (char**)*cp;
+      *cp = (*cp)->next;
+      SOAP_FREE(soap, q);
+      return;
+    }
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_lookup_type(struct soap *soap, const char *id)
+{ register struct soap_ilist *ip;
+  if (id && *id)
+  { ip = soap_lookup(soap, id);
+    if (ip)
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Lookup id='%s' type=%d\n", id, ip->type));
+      return ip->type;
+    }
+  }
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "lookup type id='%s' NOT FOUND! Need to get it from xsi:type\n", id));
+  return 0;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+void*
+SOAP_FMAC2
+soap_id_lookup(struct soap *soap, const char *id, void **p, int t, size_t n, unsigned int k)
+{ struct soap_ilist *ip;
+  void **q;
+  if (!p || !id || !*id)
+    return p;
+  ip = soap_lookup(soap, id); /* lookup pointer to hash table entry for string id */
+  if (!ip)
+  { if (!(ip = soap_enter(soap, id))) /* new hash table entry for string id */
+      return NULL;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Forwarding first href='%s' type=%d %p (%u bytes)\n", id, t, p, (unsigned int)n));
+    ip->type = t;
+    ip->size = n; 
+    ip->link = p;
+    ip->copy = NULL;
+    ip->flist = NULL;
+    ip->ptr = NULL;
+    ip->level = k;
+    *p = NULL;
+  }
+  else if (ip->ptr)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resolved href='%s' type=%d location=%p (%u bytes)\n", id, t, ip->ptr, (unsigned int)n));
+    if (ip->type != t)
+    { strcpy(soap->id, id);
+      soap->error = SOAP_HREF;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Type incompatibility: href='%s' id-type=%d href-type=%d\n", id, ip->type, t));
+      return NULL;
+    }
+    while (ip->level < k)
+    { q = (void**)soap_malloc(soap, sizeof(void*));  
+      if (!q)
+        return NULL;
+      *p = (void*)q;
+      p = q;
+      k--;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Descending one level...\n"));
+    }
+    *p = ip->ptr;
+  }
+  else if (ip->level > k)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resolving level %u pointers to href='%s'\n", ip->level, id));
+    while (ip->level > k)
+    { void *s, **r = &ip->link;
+      q = (void**)ip->link;
+      while (q)
+      { *r = (void*)soap_malloc(soap, sizeof(void*));
+        if (!*r)
+	  return NULL;
+        s = *q;
+        *q = *r;
+        r = (void**)*r;
+        q = (void**)s;
+      }
+      *r = NULL;
+      ip->size = n; 
+      ip->copy = NULL;
+      ip->level = ip->level - 1;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Descending one level...\n"));
+    }
+    q = (void**)ip->link;
+    ip->link = p;
+    *p = (void*)q;
+  }
+  else
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Forwarded href='%s' type=%d location=%p (%u bytes)\n", id, t, p, (unsigned int)n));
+    while (ip->level < k)
+    { q = (void**)soap_malloc(soap, sizeof(void*));  
+      if (!q)
+        return NULL;
+      *p = q;
+      p = q;
+      k--;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Descending one level...\n"));
+    }
+    q = (void**)ip->link;
+    ip->link = p;
+    *p = (void*)q;
+  }
+  return p;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIDREF
+#ifndef PALM_2
+SOAP_FMAC1
+void*
+SOAP_FMAC2
+soap_id_forward(struct soap *soap, const char *href, void *p, size_t len, int st, int tt, size_t n, unsigned int k, void (*fcopy)(struct soap*, int, int, void*, size_t, const void*, size_t))
+{ struct soap_ilist *ip;
+  if (!p || !href || !*href)
+    return p;
+  ip = soap_lookup(soap, href); /* lookup pointer to hash table entry for string id */
+  if (!ip)
+  { if (!(ip = soap_enter(soap, href))) /* new hash table entry for string id */
+      return NULL;
+    ip->type = st;
+    ip->size = n;
+    ip->link = NULL;
+    ip->copy = NULL;
+    ip->ptr = NULL;
+    ip->level = 0;
+    ip->flist = NULL;
+    DBGLOG(TEST,SOAP_MESSAGE(fdebug, "New entry href='%s' type=%d size=%lu level=%d location=%p\n", href, st, (unsigned long)n, k, p));
+  }
+  else if (ip->type != st || (ip->level == k && ip->size != n))
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Type incompatibility id='%s' expect type=%d size=%lu level=%u got type=%d size=%lu\n", href, ip->type, (unsigned long)ip->size, k, st, (unsigned long)n));
+    strcpy(soap->id, href);
+    soap->error = SOAP_HREF;
+    return NULL;
+  }
+  if (fcopy || n < sizeof(void*) || *href != '#')
+  { register struct soap_flist *fp = (struct soap_flist*)SOAP_MALLOC(soap, sizeof(struct soap_flist));
+    if (!fp)
+    { soap->error = SOAP_EOM;
+      return NULL;
+    }
+    fp->next = ip->flist;
+    fp->type = tt;
+    fp->ptr = p;
+    fp->level = k;
+    fp->len = len;
+    if (fcopy)
+      fp->fcopy = fcopy;
+    else
+      fp->fcopy = soap_fcopy;
+    ip->flist = fp;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Forwarding type=%d (target type=%d) size=%lu location=%p level=%u len=%lu href='%s'\n", st, tt, (unsigned long)n, p, k, (unsigned long)len, href));
+  }
+  else
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Forwarding copying address %p for type=%d href='%s'\n", p, st, href));
+    *(void**)p = ip->copy;
+    ip->copy = p;
+  }
+  return p;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void*
+SOAP_FMAC2
+soap_id_enter(struct soap *soap, const char *id, void *p, int t, size_t n, unsigned int k, const char *type, const char *arrayType, void *(*finstantiate)(struct soap*, int, const char*, const char*, size_t*))
+{
+#ifndef WITH_NOIDREF
+  struct soap_ilist *ip;
+#endif
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Enter id='%s' type=%d loc=%p size=%lu level=%u\n", id, t, p, (unsigned long)n, k));
+  soap->alloced = 0;
+  if (!p)
+  { if (finstantiate)
+      p = finstantiate(soap, t, type, arrayType, &n);
+    else
+      p = soap_malloc(soap, n);
+    if (p)
+      soap->alloced = 1;
+  }
+#ifndef WITH_NOIDREF
+  if (!id || !*id)
+#endif
+    return p;
+#ifndef WITH_NOIDREF
+  ip = soap_lookup(soap, id); /* lookup pointer to hash table entry for string id */
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Lookup entry id='%s for location=%p'\n", id, p));
+  if (!ip)
+  { if (!(ip = soap_enter(soap, id))) /* new hash table entry for string id */
+      return NULL;
+    ip->type = t;
+    ip->link = NULL;
+    ip->copy = NULL;
+    ip->flist = NULL;
+    ip->size = n;
+    ip->ptr = p;
+    ip->level = k;
+    DBGLOG(TEST,SOAP_MESSAGE(fdebug, "New entry id='%s' type=%d size=%lu level=%u location=%p\n", id, t, (unsigned long)n, k, p));
+  }
+  else if ((ip->type != t || (ip->level == k && ip->size != n)) && (ip->copy || ip->flist))
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Type incompatibility id='%s' expect type=%d size=%lu level=%u got type=%d size=%lu\n", id, ip->type, (unsigned long)ip->size, k, t, (unsigned long)n));
+    strcpy(soap->id, id);
+    soap->error = SOAP_HREF;
+    return NULL;
+  }
+  else if (ip->ptr)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Multiply defined id='%s'\n", id));
+    strcpy(soap->id, id);
+    soap->error = SOAP_DUPLICATE_ID;
+    return NULL;
+  }
+  else 
+  { ip->size = n;
+    ip->ptr = p;
+    ip->level = k;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Update entry id='%s' type=%d location=%p size=%lu level=%u\n", id, t, p, (unsigned long)n, k));
+  }
+  return ip->ptr;
+#endif
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_fcopy(struct soap *soap, int st, int tt, void *p, size_t len, const void *q, size_t n)
+{ DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Copying data type=%d (target type=%d) %p -> %p (%lu bytes)\n", st, tt, q, p, (unsigned long)n));
+  memcpy(p, q, n);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_end_send(struct soap *soap)
+{ 
+#ifndef WITH_LEANER
+  if (soap->dime.list)
+  { /* SOAP body referenced attachments must appear first */
+    soap->dime.last->next = soap->dime.first;
+    soap->dime.first = soap->dime.list->next;
+    soap->dime.list->next = NULL;
+    soap->dime.last = soap->dime.list;
+  }
+  if (soap_putdime(soap) || soap_putmime(soap))
+    return soap->error;
+  soap->mime.list = NULL;
+  soap->mime.first = NULL;
+  soap->mime.last = NULL;
+  soap->dime.list = NULL;
+  soap->dime.first = NULL;
+  soap->dime.last = NULL;
+#endif
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "End send\n"));
+  if (soap->mode & SOAP_IO) /* need to flush the remaining data in buffer */
+  { if (soap_flush(soap))
+#ifdef WITH_ZLIB
+    { if (soap->mode & SOAP_ENC_ZLIB && soap->zlib_state == SOAP_ZLIB_DEFLATE)
+      { soap->zlib_state = SOAP_ZLIB_NONE;
+        deflateEnd(soap->d_stream);
+      }
+      return soap->error;
+    }
+#else
+      return soap->error;
+#endif
+#ifdef WITH_ZLIB
+    if (soap->mode & SOAP_ENC_ZLIB)
+    { int r;
+      soap->d_stream->avail_in = 0;
+      do
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Deflating remainder\n"));
+        r = deflate(soap->d_stream, Z_FINISH);
+        if (soap->d_stream->avail_out != SOAP_BUFLEN)
+        { if (soap_flush_raw(soap, soap->z_buf, SOAP_BUFLEN - soap->d_stream->avail_out))
+          { soap->zlib_state = SOAP_ZLIB_NONE;
+            deflateEnd(soap->d_stream);
+            return soap->error;
+          }
+          soap->d_stream->next_out = (Byte*)soap->z_buf;
+          soap->d_stream->avail_out = SOAP_BUFLEN;
+        }
+      } while (r == Z_OK);
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Deflated total %lu->%lu bytes\n", soap->d_stream->total_in, soap->d_stream->total_out));
+      soap->z_ratio_out = (float)soap->d_stream->total_out / (float)soap->d_stream->total_in;
+      soap->mode &= ~SOAP_ENC_ZLIB;
+      soap->zlib_state = SOAP_ZLIB_NONE;
+      if (deflateEnd(soap->d_stream) != Z_OK || r != Z_STREAM_END)
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Unable to end deflate: %s\n", soap->d_stream->msg?soap->d_stream->msg:SOAP_STR_EOS));
+        return soap->error = SOAP_ZLIB_ERROR;
+      }
+#ifdef WITH_GZIP
+      if (soap->zlib_out != SOAP_ZLIB_DEFLATE)
+      { soap->z_buf[0] = soap->z_crc & 0xFF;
+        soap->z_buf[1] = (soap->z_crc >> 8) & 0xFF;
+        soap->z_buf[2] = (soap->z_crc >> 16) & 0xFF;
+        soap->z_buf[3] = (soap->z_crc >> 24) & 0xFF;
+        soap->z_buf[4] = soap->d_stream->total_in & 0xFF;
+        soap->z_buf[5] = (soap->d_stream->total_in >> 8) & 0xFF;
+        soap->z_buf[6] = (soap->d_stream->total_in >> 16) & 0xFF;
+        soap->z_buf[7] = (soap->d_stream->total_in >> 24) & 0xFF;
+        if (soap_flush_raw(soap, soap->z_buf, 8))
+          return soap->error;
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "gzip crc32=%lu\n", (unsigned long)soap->z_crc));
+      }
+#endif
+    }
+#endif
+    if ((soap->mode & SOAP_IO) == SOAP_IO_STORE)
+    { char *p;
+#ifndef WITH_NOHTTP
+      if (!(soap->mode & SOAP_ENC_XML))
+      { soap->mode--;
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Sending buffered message of length %u\n", (unsigned int)soap->blist->size));
+        if (soap->status >= SOAP_POST)
+          soap->error = soap->fpost(soap, soap->endpoint, soap->host, soap->port, soap->path, soap->action, soap->blist->size);
+        else if (soap->status != SOAP_STOP)
+          soap->error = soap->fresponse(soap, soap->status, soap->blist->size);
+        if (soap->error || soap_flush(soap))
+          return soap->error;
+        soap->mode++;
+      }
+#endif
+      for (p = soap_first_block(soap, NULL); p; p = soap_next_block(soap, NULL))
+      { DBGMSG(SENT, p, soap_block_size(soap, NULL));
+        if ((soap->error = soap->fsend(soap, p, soap_block_size(soap, NULL))))
+        { soap_end_block(soap, NULL);
+          return soap->error;
+        }
+      }
+      soap_end_block(soap, NULL);
+    }
+#ifndef WITH_LEANER
+    else if ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK)
+    { DBGMSG(SENT, "\r\n0\r\n\r\n", 7);
+      if ((soap->error = soap->fsend(soap, "\r\n0\r\n\r\n", 7)))
+        return soap->error;
+    }
+#endif
+  }
+#ifdef WITH_TCPFIN
+#ifdef WITH_OPENSSL
+  if (!soap->ssl && soap_valid_socket(soap->socket) && !soap->keep_alive && !(soap->omode & SOAP_IO_UDP))
+    soap->fshutdownsocket(soap, soap->socket, 1); /* Send TCP FIN */
+#else
+  if (soap_valid_socket(soap->socket) && !soap->keep_alive && !(soap->omode & SOAP_IO_UDP))
+    soap->fshutdownsocket(soap, soap->socket, 1); /* Send TCP FIN */
+#endif
+#endif
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "End of send phase\n"));
+  soap->omode &= ~SOAP_XML_SEC;
+  soap->count = 0;
+  soap->part = SOAP_END;
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_end_recv(struct soap *soap)
+{ soap->part = SOAP_END;
+#ifndef WITH_LEANER
+  if ((soap->mode & SOAP_ENC_DIME) && soap_getdime(soap))
+  { soap->dime.first = NULL;
+    soap->dime.last = NULL;
+    return soap->error;
+  }
+  soap->dime.list = soap->dime.first;
+  soap->dime.first = NULL;
+  soap->dime.last = NULL;
+  /* Check if MIME attachments and mime-post-check flag is set, if set call soap_resolve() and return */
+  if (soap->mode & SOAP_ENC_MIME)
+  { 
+#ifndef WITH_NOIDREF
+    if (soap->mode & SOAP_MIME_POSTCHECK)
+    { soap_resolve(soap);
+      return SOAP_OK;
+    }
+#endif
+    if (soap_getmime(soap))
+      return soap->error;
+  }
+  soap->mime.list = soap->mime.first;
+  soap->mime.first = NULL;
+  soap->mime.last = NULL;
+  soap->mime.boundary = NULL;
+  if (soap->xlist)
+  { struct soap_multipart *content;
+    for (content = soap->mime.list; content; content = content->next)
+      soap_resolve_attachment(soap, content);
+  }
+#endif
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "End of receive message ok\n"));
+#ifdef WITH_ZLIB
+  if (soap->mode & SOAP_ENC_ZLIB)
+  { /* Make sure end of compressed content is reached */
+    while (soap->d_stream->next_out != Z_NULL)
+      if ((int)soap_get1(soap) == EOF)
+        break;
+    soap->mode &= ~SOAP_ENC_ZLIB;
+    memcpy(soap->buf, soap->z_buf, SOAP_BUFLEN);
+    soap->bufidx = (char*)soap->d_stream->next_in - soap->z_buf;
+    soap->buflen = soap->z_buflen;
+    soap->zlib_state = SOAP_ZLIB_NONE;
+    if (inflateEnd(soap->d_stream) != Z_OK)
+      return soap->error = SOAP_ZLIB_ERROR;
+    DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Inflate end ok\n"));
+#ifdef WITH_GZIP
+    if (soap->zlib_in == SOAP_ZLIB_GZIP)
+    { soap_wchar c;
+      short i;
+      DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Inflate gzip crc check\n"));
+      for (i = 0; i < 8; i++)
+      { if ((int)(c = soap_get1(soap)) == EOF)
+          return soap->error = SOAP_EOF;
+        soap->z_buf[i] = (char)c;
+      }
+      if (soap->z_crc != ((uLong)(unsigned char)soap->z_buf[0] | ((uLong)(unsigned char)soap->z_buf[1] << 8) | ((uLong)(unsigned char)soap->z_buf[2] << 16) | ((uLong)(unsigned char)soap->z_buf[3] << 24)))
+      { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Gzip error: crc check failed, message corrupted? (crc32=%lu)\n", (unsigned long)soap->z_crc));
+        return soap->error = SOAP_ZLIB_ERROR;
+      }
+      if (soap->d_stream->total_out != ((uLong)(unsigned char)soap->z_buf[4] | ((uLong)(unsigned char)soap->z_buf[5] << 8) | ((uLong)(unsigned char)soap->z_buf[6] << 16) | ((uLong)(unsigned char)soap->z_buf[7] << 24)))
+      { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Gzip error: incorrect message length\n"));
+        return soap->error = SOAP_ZLIB_ERROR;
+      }
+    }
+    soap->zlib_in = SOAP_ZLIB_NONE;
+#endif
+  }
+#endif
+  if ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK)
+    while ((int)soap_getchar(soap) != EOF) /* advance to last chunk */
+      ;
+  if (soap->fdisconnect && (soap->error = soap->fdisconnect(soap)))
+    return soap->error;
+#ifndef WITH_NOIDREF
+  if (soap_resolve(soap))
+    return soap->error;
+#endif
+#ifndef WITH_LEANER
+  if (soap->xlist)
+  { if (soap->mode & SOAP_ENC_MTOM)
+      return soap->error = SOAP_MIME_HREF;
+    return soap->error = SOAP_DIME_HREF;
+  }
+#endif
+  soap_free_temp(soap);
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_free_temp(struct soap *soap)
+{ register struct soap_nlist *np, *nq;
+  register struct soap_attribute *tp, *tq;
+  register struct Namespace *ns;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Free namespace stack\n"));
+  for (np = soap->nlist; np; np = nq)
+  { nq = np->next;
+    SOAP_FREE(soap, np);
+  }
+  soap->nlist = NULL;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Free any remaining temp blocks\n"));
+  while (soap->blist)
+    soap_end_block(soap, NULL);
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Free attribute storage\n"));
+  for (tp = soap->attributes; tp; tp = tq)
+  { tq = tp->next;
+    if (tp->value)
+      SOAP_FREE(soap, tp->value);
+    SOAP_FREE(soap, tp);
+  }
+  soap->attributes = NULL;
+#ifdef WITH_FAST
+  if (soap->labbuf)
+    SOAP_FREE(soap, soap->labbuf);
+  soap->labbuf = NULL;
+  soap->lablen = 0;
+  soap->labidx = 0;
+#endif
+  ns = soap->local_namespaces;
+  if (ns)
+  { for (; ns->id; ns++)
+    { if (ns->out)
+      { if (soap->encodingStyle == ns->out)
+          soap->encodingStyle = SOAP_STR_EOS;
+        SOAP_FREE(soap, ns->out);
+        ns->out = NULL;
+      }
+      if (soap->encodingStyle == ns->ns)
+        soap->encodingStyle = SOAP_STR_EOS;
+    }
+    SOAP_FREE(soap, soap->local_namespaces);
+    soap->local_namespaces = NULL;
+  }
+#ifndef WITH_LEANER
+  while (soap->xlist)
+  { struct soap_xlist *xp = soap->xlist->next;
+    SOAP_FREE(soap, soap->xlist);
+    soap->xlist = xp;
+  }
+#endif
+#ifndef WITH_NOIDREF
+  soap_free_pht(soap);
+  soap_free_iht(soap);
+#endif
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_DEBUG
+static void
+soap_init_logs(struct soap *soap)
+{ int i;
+  for (i = 0; i < SOAP_MAXLOGS; i++)
+  { soap->logfile[i] = NULL;
+    soap->fdebug[i] = NULL;
+  }
+}
+#endif
+
+/******************************************************************************/
+#if !defined(WITH_LEAN) || defined(SOAP_DEBUG)
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_open_logfile(struct soap *soap, int i)
+{ if (soap->logfile[i])
+    soap->fdebug[i] = fopen(soap->logfile[i], i < 2 ? "ab" : "a");
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_DEBUG
+static void
+soap_close_logfile(struct soap *soap, int i)
+{ if (soap->fdebug[i])
+  { fclose(soap->fdebug[i]);
+    soap->fdebug[i] = NULL;
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_DEBUG
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_close_logfiles(struct soap *soap)
+{ int i;
+  for (i = 0; i < SOAP_MAXLOGS; i++)
+    soap_close_logfile(soap, i);
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_DEBUG
+static void
+soap_set_logfile(struct soap *soap, int i, const char *logfile)
+{ const char *s;
+  char *t = NULL;
+  soap_close_logfile(soap, i);
+  s = soap->logfile[i];
+  soap->logfile[i] = logfile;
+  if (s)
+    SOAP_FREE(soap, (void*)s);
+  if (logfile)
+    if ((t = (char*)SOAP_MALLOC(soap, strlen(logfile) + 1)))
+      strcpy(t, logfile);
+  soap->logfile[i] = t;
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_DEBUG
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_set_recv_logfile(struct soap *soap, const char *logfile)
+{ soap_set_logfile(soap, SOAP_INDEX_RECV, logfile);
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_DEBUG
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_set_sent_logfile(struct soap *soap, const char *logfile)
+{ soap_set_logfile(soap, SOAP_INDEX_SENT, logfile);
+}
+#endif
+
+/******************************************************************************/
+#ifdef SOAP_DEBUG
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_set_test_logfile(struct soap *soap, const char *logfile)
+{ soap_set_logfile(soap, SOAP_INDEX_TEST, logfile);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+struct soap*
+SOAP_FMAC2
+soap_copy(const struct soap *soap)
+{ return soap_copy_context((struct soap*)malloc(sizeof(struct soap)), soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+struct soap*
+SOAP_FMAC2
+soap_copy_context(struct soap *copy, const struct soap *soap)
+{ if (soap_check_state(soap))
+    return NULL;
+  if (copy)
+  { register struct soap_plugin *p = NULL;
+#ifdef __cplusplus
+    *copy = *soap;
+#else
+    memcpy(copy, soap, sizeof(struct soap));
+#endif
+    copy->state = SOAP_COPY;
+    copy->error = SOAP_OK;
+    copy->userid = NULL;
+    copy->passwd = NULL;
+    copy->nlist = NULL;
+    copy->blist = NULL;
+    copy->clist = NULL;
+    copy->alist = NULL;
+    copy->attributes = NULL;
+    copy->labbuf = NULL;
+    copy->lablen = 0;
+    copy->labidx = 0;
+#ifdef SOAP_MEM_DEBUG
+    soap_init_mht(copy);
+#endif
+#ifdef SOAP_DEBUG
+    soap_init_logs(copy);
+    soap_set_recv_logfile(copy, soap->logfile[SOAP_INDEX_RECV]);
+    soap_set_sent_logfile(copy, soap->logfile[SOAP_INDEX_SENT]);
+    soap_set_test_logfile(copy, soap->logfile[SOAP_INDEX_TEST]);
+#endif
+#ifdef WITH_C_LOCALE
+    copy->c_locale = duplocale(soap->c_locale);
+#else
+    copy->c_locale = NULL;
+#endif
+#ifdef WITH_OPENSSL
+    copy->bio = NULL;
+    copy->ssl = NULL;
+#endif
+#ifdef WITH_ZLIB
+    copy->d_stream = (z_stream*)SOAP_MALLOC(copy, sizeof(z_stream));
+    copy->d_stream->zalloc = Z_NULL;
+    copy->d_stream->zfree = Z_NULL;
+    copy->d_stream->opaque = Z_NULL;
+    copy->z_buf = NULL;
+#endif
+    copy->local_namespaces = NULL;
+#ifndef WITH_NOIDREF
+    soap_init_iht(copy);
+    soap_init_pht(copy);
+#endif
+    copy->header = NULL;
+    copy->fault = NULL;
+    copy->action = NULL;
+#ifndef WITH_LEAN
+#ifdef WITH_COOKIES
+    copy->cookies = soap_copy_cookies(copy, soap);
+#else
+    copy->cookies = NULL;
+#endif
+#endif
+    copy->plugins = NULL;
+    for (p = soap->plugins; p; p = p->next)
+    { register struct soap_plugin *q = (struct soap_plugin*)SOAP_MALLOC(copy, sizeof(struct soap_plugin));
+      if (!q)
+        return NULL;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Copying plugin '%s'\n", p->id));
+      *q = *p;
+      if (p->fcopy && p->fcopy(copy, q, p))
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not copy plugin '%s'\n", p->id));
+        SOAP_FREE(copy, q);
+        return NULL;
+      }
+      q->next = copy->plugins;
+      copy->plugins = q;
+    }
+  }
+  return copy;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_copy_stream(struct soap *copy, struct soap *soap)
+{ copy->mode = soap->mode;
+  copy->imode = soap->imode;
+  copy->omode = soap->omode;
+  copy->socket = soap->socket;
+  copy->recv_timeout = soap->recv_timeout;
+  copy->send_timeout = soap->send_timeout;
+#if defined(__cplusplus) && !defined(WITH_LEAN)
+  copy->os = soap->os;
+  copy->is = soap->is;
+#endif
+  copy->sendfd = soap->sendfd;
+  copy->recvfd = soap->recvfd;
+  copy->bufidx = soap->bufidx;
+  copy->buflen = soap->buflen;
+  copy->ahead = soap->ahead;
+  copy->cdata = soap->cdata;
+  copy->chunksize = soap->chunksize;
+  copy->chunkbuflen = soap->chunkbuflen;
+  copy->keep_alive = soap->keep_alive;
+  copy->tcp_keep_alive = soap->tcp_keep_alive;
+  copy->tcp_keep_idle = soap->tcp_keep_idle;
+  copy->tcp_keep_intvl = soap->tcp_keep_intvl;
+  copy->tcp_keep_cnt = soap->tcp_keep_cnt;
+  copy->max_keep_alive = soap->max_keep_alive;
+#ifndef WITH_NOIO
+  copy->peer = soap->peer;
+  copy->peerlen = soap->peerlen;
+#endif
+#ifdef WITH_OPENSSL
+  copy->bio = soap->bio;
+  copy->ssl = soap->ssl;
+  copy->ctx = soap->ctx;
+#endif
+#ifdef WITH_ZLIB
+  copy->zlib_state = soap->zlib_state;
+  copy->zlib_in = soap->zlib_in;
+  copy->zlib_out = soap->zlib_out;
+  copy->d_stream = (z_stream*)SOAP_MALLOC(copy, sizeof(z_stream));
+  memcpy(copy->d_stream, soap->d_stream, sizeof(z_stream));
+  copy->z_crc = soap->z_crc;
+  copy->z_ratio_in = soap->z_ratio_in;
+  copy->z_ratio_out = soap->z_ratio_out;
+  copy->z_buf = NULL;
+  copy->z_buflen = soap->z_buflen;
+  copy->z_level = soap->z_level;
+  if (soap->z_buf && soap->zlib_state != SOAP_ZLIB_NONE)
+  { copy->z_buf = (char*)SOAP_MALLOC(copy, SOAP_BUFLEN);
+    memcpy(copy->z_buf, soap->z_buf, sizeof(soap->z_buf));
+  }
+#endif
+  memcpy(copy->buf, soap->buf, sizeof(soap->buf));
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_init(struct soap *soap)
+{ soap->state = SOAP_INIT;
+#ifdef SOAP_MEM_DEBUG
+  soap_init_mht(soap);
+#endif
+#ifdef SOAP_DEBUG
+  soap_init_logs(soap);
+  soap_set_recv_logfile(soap, "RECV.log");
+  soap_set_sent_logfile(soap, "SENT.log");
+  soap_set_test_logfile(soap, "TEST.log");
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Initializing context\n"));
+#endif
+  soap->version = 0;
+  soap_imode(soap, SOAP_IO_DEFAULT);
+  soap_omode(soap, SOAP_IO_DEFAULT);
+  soap->plugins = NULL;
+  soap->user = NULL;
+  soap->userid = NULL;
+  soap->passwd = NULL;
+#ifndef WITH_NOHTTP
+  soap->fpost = http_post;
+  soap->fget = http_get;
+  soap->fput = http_put;
+  soap->fdel = http_del;
+  soap->fhead = http_head;
+  soap->fform = NULL;
+  soap->fposthdr = http_post_header;
+  soap->fresponse = http_response;
+  soap->fparse = http_parse;
+  soap->fparsehdr = http_parse_header;
+#endif
+  soap->fheader = NULL;
+  soap->fconnect = NULL;
+  soap->fdisconnect = NULL;
+#ifndef WITH_NOIO
+  soap->ipv6_multicast_if = 0;
+  soap->ipv4_multicast_if = NULL;
+#ifndef WITH_IPV6
+  soap->fresolve = tcp_gethost;
+#else
+  soap->fresolve = NULL;
+#endif
+  soap->faccept = tcp_accept;
+  soap->fopen = tcp_connect;
+  soap->fclose = tcp_disconnect;
+  soap->fclosesocket = tcp_closesocket;
+  soap->fshutdownsocket = tcp_shutdownsocket;
+  soap->fsend = fsend;
+  soap->frecv = frecv;
+  soap->fpoll = soap_poll;
+#else
+  soap->fopen = NULL;
+  soap->fclose = NULL;
+  soap->fpoll = NULL;
+#endif
+  soap->fseterror = NULL;
+  soap->fignore = NULL;
+  soap->fserveloop = NULL;
+  soap->fplugin = fplugin;
+  soap->fmalloc = NULL;
+#ifndef WITH_LEANER
+  soap->fprepareinit = NULL;
+  soap->fpreparesend = NULL;
+  soap->fpreparerecv = NULL;
+  soap->fpreparefinal = NULL;
+  soap->fdimereadopen = NULL;
+  soap->fdimewriteopen = NULL;
+  soap->fdimereadclose = NULL;
+  soap->fdimewriteclose = NULL;
+  soap->fdimeread = NULL;
+  soap->fdimewrite = NULL;
+  soap->fmimereadopen = NULL;
+  soap->fmimewriteopen = NULL;
+  soap->fmimereadclose = NULL;
+  soap->fmimewriteclose = NULL;
+  soap->fmimeread = NULL;
+  soap->fmimewrite = NULL;
+#endif
+  soap->float_format = "%.9G"; /* Alternative: use "%G" */
+  soap->double_format = "%.17lG"; /* Alternative: use "%lG" */
+  soap->dime_id_format = "cid:id%d"; /* default DIME id format */
+  soap->http_version = "1.1";
+  soap->proxy_http_version = "1.0";
+  soap->http_content = NULL;
+  soap->actor = NULL;
+  soap->lang = "en";
+  soap->keep_alive = 0;
+  soap->tcp_keep_alive = 0;
+  soap->tcp_keep_idle = 0;
+  soap->tcp_keep_intvl = 0;
+  soap->tcp_keep_cnt = 0;
+  soap->max_keep_alive = SOAP_MAXKEEPALIVE;
+  soap->recv_timeout = 0;
+  soap->send_timeout = 0;
+  soap->connect_timeout = 0;
+  soap->accept_timeout = 0;
+  soap->socket_flags = 0;
+  soap->connect_flags = 0;
+  soap->bind_flags = 0;
+  soap->accept_flags = 0;
+  soap->linger_time = 0;
+  soap->ip = 0;
+  soap->labbuf = NULL;
+  soap->lablen = 0;
+  soap->labidx = 0;
+  soap->encodingStyle = SOAP_STR_EOS;
+#ifndef WITH_NONAMESPACES
+  soap->namespaces = namespaces;
+#else
+  soap->namespaces = NULL;
+#endif
+  soap->local_namespaces = NULL;
+  soap->nlist = NULL;
+  soap->blist = NULL;
+  soap->clist = NULL;
+  soap->alist = NULL;
+  soap->attributes = NULL;
+  soap->header = NULL;
+  soap->fault = NULL;
+  soap->master = SOAP_INVALID_SOCKET;
+  soap->socket = SOAP_INVALID_SOCKET;
+  soap->os = NULL;
+  soap->is = NULL;
+#ifndef WITH_LEANER
+  soap->dom = NULL;
+  soap->dime.list = NULL;
+  soap->dime.first = NULL;
+  soap->dime.last = NULL;
+  soap->mime.list = NULL;
+  soap->mime.first = NULL;
+  soap->mime.last = NULL;
+  soap->mime.boundary = NULL;
+  soap->mime.start = NULL;
+  soap->xlist = NULL;
+#endif
+#ifndef UNDER_CE
+  soap->recvfd = 0;
+  soap->sendfd = 1;
+#else
+  soap->recvfd = stdin;
+  soap->sendfd = stdout;
+#endif 
+  soap->host[0] = '\0';
+  soap->port = 0;
+  soap->action = NULL;
+  soap->proxy_host = NULL;
+  soap->proxy_port = 8080;
+  soap->proxy_userid = NULL;
+  soap->proxy_passwd = NULL;
+  soap->authrealm = NULL;
+  soap->prolog = NULL;
+#ifdef WITH_ZLIB
+  soap->zlib_state = SOAP_ZLIB_NONE;
+  soap->zlib_in = SOAP_ZLIB_NONE;
+  soap->zlib_out = SOAP_ZLIB_NONE;
+  soap->d_stream = (z_stream*)SOAP_MALLOC(soap, sizeof(z_stream));
+  soap->d_stream->zalloc = Z_NULL;
+  soap->d_stream->zfree = Z_NULL;
+  soap->d_stream->opaque = Z_NULL;
+  soap->z_buf = NULL;
+  soap->z_level = 6;
+#endif
+#ifndef WITH_LEAN
+  soap->c14ninclude = NULL;
+  soap->c14nexclude = NULL;
+  soap->cookies = NULL;
+  soap->cookie_domain = NULL;
+  soap->cookie_path = NULL;
+  soap->cookie_max = 32;
+#endif
+#ifdef WMW_RPM_IO
+  soap->rpmreqid = NULL;
+#endif
+#ifdef PALM
+  palmNetLibOpen();
+#endif
+#ifndef WITH_NOIDREF
+  soap_init_iht(soap);
+  soap_init_pht(soap);
+#endif
+#ifdef WITH_OPENSSL
+  if (!soap_ssl_init_done)
+  { soap_ssl_init();
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Initializing OpenSSL, version=%ld\n", (long)OPENSSL_VERSION_NUMBER));
+  }
+  soap->fsslauth = ssl_auth_init;
+  soap->fsslverify = ssl_verify_callback;
+  soap->bio = NULL;
+  soap->ssl = NULL;
+  soap->ctx = NULL;
+  soap->ssl_flags = SOAP_SSL_DEFAULT;
+  soap->keyfile = NULL;
+  soap->password = NULL;
+  soap->dhfile = NULL;
+  soap->cafile = NULL;
+  soap->capath = NULL;
+  soap->crlfile = NULL;
+  soap->randfile = NULL;
+  soap->session = NULL;
+#endif
+#ifdef WITH_C_LOCALE
+  soap->c_locale = newlocale(LC_ALL_MASK, "C", NULL);
+#else
+  soap->c_locale = NULL;
+#endif
+  soap_begin(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_init1(struct soap *soap, soap_mode mode)
+{ soap_init2(soap, mode, mode);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_init2(struct soap *soap, soap_mode imode, soap_mode omode)
+{ soap_init(soap);
+  soap_imode(soap, imode);
+  soap_omode(soap, omode);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_begin(struct soap *soap)
+{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Reinitializing context\n"));
+  if (!soap->keep_alive)
+  { soap->buflen = 0;
+    soap->bufidx = 0;
+  }
+  soap->keep_alive = (((soap->imode | soap->omode) & SOAP_IO_KEEPALIVE) != 0);
+  soap->null = 0;
+  soap->position = 0;
+  soap->encoding = 0;
+  soap->mustUnderstand = 0;
+  soap->mode = 0;
+  soap->ns = 0;
+  soap->part = SOAP_END;
+  soap->alloced = 0;
+  soap->count = 0;
+  soap->length = 0;
+  soap->cdata = 0;
+  soap->error = SOAP_OK;
+  soap->peeked = 0;
+  soap->ahead = 0;
+  soap->idnum = 0;
+  soap->level = 0;
+  soap->endpoint[0] = '\0';
+#ifndef WITH_LEANER
+  soap->dime.chunksize = 0;
+  soap->dime.buflen = 0;
+#endif
+  soap_free_temp(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_end(struct soap *soap)
+{ if (soap_check_state(soap))
+    return;
+  soap_free_temp(soap);
+  soap_dealloc(soap, NULL);
+  while (soap->clist)
+  { register struct soap_clist *cp = soap->clist->next;
+    SOAP_FREE(soap, soap->clist);
+    soap->clist = cp;
+  }
+  soap_closesock(soap);
+#ifdef SOAP_DEBUG
+  soap_close_logfiles(soap);
+#endif
+#ifdef PALM
+  palmNetLibClose();
+#endif
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_set_namespaces(struct soap *soap, const struct Namespace *p)
+{ register struct Namespace *ns = soap->local_namespaces;
+  register struct soap_nlist *np, *nq, *nr;
+  register unsigned int level = soap->level;
+  soap->namespaces = p;
+  soap->local_namespaces = NULL;
+  soap_set_local_namespaces(soap);
+  /* reverse the namespace list */
+  np = soap->nlist;
+  soap->nlist = NULL;
+  if (np)
+  { nq = np->next;
+    np->next = NULL;
+    while (nq)
+    { nr = nq->next;
+      nq->next = np;
+      np = nq;
+      nq = nr;
+    }
+  }
+  /* then push on new stack */
+  while (np)
+  { register const char *s;
+    soap->level = np->level; /* preserve element nesting level */
+    s = np->ns;
+    if (!s && np->index >= 0 && ns)
+    { s = ns[np->index].out;
+      if (!s)
+        s = ns[np->index].ns;
+    }
+    if (s && soap_push_namespace(soap, np->id, s))
+      return soap->error;
+    nq = np;
+    np = np->next;
+    SOAP_FREE(soap, nq);
+  }
+  if (ns)
+  { register int i;
+    for (i = 0; ns[i].id; i++)
+    { if (ns[i].out)
+      { SOAP_FREE(soap, ns[i].out);
+        ns[i].out = NULL;
+      }
+    }
+    SOAP_FREE(soap, ns);
+  }
+  soap->level = level; /* restore level */
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_set_local_namespaces(struct soap *soap)
+{ if (soap->namespaces && !soap->local_namespaces)
+  { register const struct Namespace *ns1;
+    register struct Namespace *ns2;
+    register size_t n = 1;
+    for (ns1 = soap->namespaces; ns1->id; ns1++)
+      n++;
+    n *= sizeof(struct Namespace);
+    ns2 = (struct Namespace*)SOAP_MALLOC(soap, n);
+    if (ns2)
+    { memcpy(ns2, soap->namespaces, n);
+      if (ns2[0].ns)
+      { if (!strcmp(ns2[0].ns, soap_env1))
+          soap->version = 1;
+        else
+          soap->version = 2;
+      }
+      soap->local_namespaces = ns2;
+    }
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+#ifndef PALM_1
+SOAP_FMAC1
+const char *
+SOAP_FMAC2
+soap_strsearch(const char *big, const char *little)
+{ size_t n = strlen(little);
+  const char *s = big;
+  while (s) 
+  { if (!strncmp(s, little, n) && (s[n] == '\0' || s[n] == ' '))
+      return s;
+    s = strchr(s, ' ');
+    if (s)
+      s++;
+  }
+  return NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+#ifndef PALM_1
+SOAP_FMAC1
+struct soap_nlist *
+SOAP_FMAC2
+soap_lookup_ns(struct soap *soap, const char *tag, size_t n)
+{ register struct soap_nlist *np;
+  for (np = soap->nlist; np; np = np->next)
+  { if (!strncmp(np->id, tag, n) && !np->id[n])
+      return np;
+  }
+  return NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+static struct soap_nlist *
+soap_push_ns(struct soap *soap, const char *id, const char *ns, short utilized)
+{ register struct soap_nlist *np;
+  size_t n, k;
+  if (soap_strsearch(soap->c14nexclude, id))
+    return NULL;
+  if (!utilized)
+  { for (np = soap->nlist; np; np = np->next)
+    { if (!strcmp(np->id, id) && (!np->ns || !strcmp(np->ns, ns)))
+        break;
+    }
+    if (np)
+    { if ((np->level < soap->level || !np->ns) && np->index == 1)
+        utilized = 1;
+      else
+        return NULL;
+    }
+  }
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Adding namespace binding (level=%u) '%s' '%s' utilized=%d\n", soap->level, id, ns?ns:"(null)", utilized));
+  n = strlen(id);
+  if (ns)
+    k = strlen(ns);
+  else
+    k = 0;
+  np = (struct soap_nlist*)SOAP_MALLOC(soap, sizeof(struct soap_nlist) + n + k + 1);
+  if (!np)
+  { soap->error = SOAP_EOM;
+    return NULL;
+  }
+  np->next = soap->nlist;
+  soap->nlist = np;
+  strcpy(np->id, id);
+  if (ns)
+  { np->ns = np->id + n + 1;
+    strcpy(np->ns, ns);
+  }
+  else
+    np->ns = NULL;
+  np->level = soap->level;
+  np->index = utilized;
+  return np;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+static void
+soap_utilize_ns(struct soap *soap, const char *tag, size_t n)
+{ register struct soap_nlist *np = soap_lookup_ns(soap, tag, n);
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Utilizing namespace of '%s'\n", tag));
+  if (np)
+  { if (np->index == 0)
+      soap_push_ns(soap, np->id, np->ns, 1);
+  }
+  else if (strncmp(tag, "xml", 3))
+  { strncpy(soap->tmpbuf, tag, n);
+    soap->tmpbuf[n] = '\0';
+    soap_push_ns(soap, soap->tmpbuf, NULL, 1);
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+static void
+soap_pop_ns(struct soap *soap)
+{ soap_pop_namespace(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element(struct soap *soap, const char *tag, int id, const char *type)
+{
+#ifdef WITH_XMLNS
+  register const char *s;
+#endif
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Element begin tag='%s' id='%d' type='%s'\n", tag, id, type?type:SOAP_STR_EOS));
+#ifdef WITH_DOM
+  if (soap->part == SOAP_BEGIN_SECURITY && (soap->mode & SOAP_XML_CANONICAL) && !(soap->mode & SOAP_DOM_ASIS))
+  { register struct soap_nlist *np;
+    /* wsu:Id found: clear xmlns renderings, so re-emit them for exc-c14n */
+    for (np = soap->nlist; np; np = np->next)
+    { if (np->index == 2)
+        np->index = 0;
+    }
+  }
+  if (soap->mode & SOAP_XML_DOM)
+  { register struct soap_dom_element *elt = (struct soap_dom_element*)soap_malloc(soap, sizeof(struct soap_dom_element));
+    if (!elt)
+      return soap->error;
+    elt->soap = soap;
+    elt->next = NULL;
+    elt->prnt = soap->dom;
+    elt->name = soap_strdup(soap, tag);
+    elt->elts = NULL;
+    elt->atts = NULL;
+    elt->nstr = NULL;
+    elt->data = NULL;
+    elt->wide = NULL;
+    elt->node = NULL;
+    elt->type = 0;
+    elt->head = NULL;
+    elt->tail = NULL;
+    if (soap->dom)
+    { struct soap_dom_element *p = soap->dom->elts;
+      if (p)
+      { while (p->next)
+          p = p->next;
+        p->next = elt;
+      }
+      else
+        soap->dom->elts = elt;
+    }
+    soap->dom = elt;
+  }
+  else
+  {
+#endif
+    soap->level++;
+#ifndef WITH_LEAN
+    if (!soap->ns)
+    { if (!(soap->mode & SOAP_XML_CANONICAL)
+       && soap_send(soap, soap->prolog ? soap->prolog : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"))
+        return soap->error;
+    }
+    else if (soap->mode & SOAP_XML_INDENT)
+    { if (soap->ns == 1 && soap_send_raw(soap, soap_indent, soap->level < sizeof(soap_indent) ? soap->level : sizeof(soap_indent) - 1))
+        return soap->error;
+      soap->body = 1;
+    }
+#endif
+#ifdef WITH_XMLNS
+    s = strchr(tag, ':');
+    if (s && strncmp(tag, "SOAP-ENV", s - tag))
+    { struct Namespace *ns = soap->local_namespaces;
+      size_t n = s - tag;
+      if (soap_send_raw(soap, "<", 1)
+       || soap_send(soap, s + 1))
+        return soap->error;
+      if (soap->nlist && !strncmp(soap->nlist->id, tag, n) && !soap->nlist->id[n])
+        ns = NULL;
+      for (; ns && ns->id; ns++)
+      { if (*ns->id && (ns->out || ns->ns) && !strncmp(ns->id, tag, n) && !ns->id[n])
+        { soap_push_ns(soap, ns->id, ns->out ? ns->out : ns->ns, 0);
+          if (soap_attribute(soap, "xmlns", ns->out ? ns->out : ns->ns))
+            return soap->error;
+          break;
+        }
+      }   
+    }
+    else
+#endif
+    if (soap_send_raw(soap, "<", 1)
+     || soap_send(soap, tag))
+      return soap->error;
+#ifdef WITH_DOM
+  }
+#endif
+  if (!soap->ns)
+  { struct Namespace *ns;
+    for (ns = soap->local_namespaces; ns && ns->id; ns++)
+    { if (*ns->id && (ns->out || ns->ns))
+      { sprintf(soap->tmpbuf, "xmlns:%s", ns->id);
+        if (soap_attribute(soap, soap->tmpbuf, ns->out ? ns->out : ns->ns))
+          return soap->error;
+      }
+    }   
+  }
+  soap->ns = 1; /* namespace table control: ns = 0 or 2 to start, then 1 to stop dumping the table  */
+#ifndef WITH_LEAN
+  if (soap->mode & SOAP_XML_CANONICAL)
+  { const char *t = strchr(tag, ':');
+    if (t)
+      soap_utilize_ns(soap, tag, t - tag);
+  }
+#endif
+  if (id > 0)
+  { sprintf(soap->tmpbuf, "_%d", id);
+    if (soap_attribute(soap, "id", soap->tmpbuf))
+      return soap->error;
+  }
+  if (type && *type && (!(soap->mode & SOAP_XML_SEC) || soap->part == SOAP_IN_BODY))
+  { if (soap_attribute(soap, "xsi:type", type))
+      return soap->error;
+#ifndef WITH_LEAN
+    if (soap->mode & SOAP_XML_CANONICAL)
+    { const char *t = strchr(type, ':');
+      if (t)
+        soap_utilize_ns(soap, type, t - type);
+    }
+#endif
+  }
+  if (soap->null && soap->position > 0)
+  { register int i;
+    sprintf(soap->tmpbuf, "[%d", soap->positions[0]);
+    for (i = 1; i < soap->position; i++)
+      sprintf(soap->tmpbuf + strlen(soap->tmpbuf), ",%d", soap->positions[i]);
+    strcat(soap->tmpbuf, "]");
+    if (soap_attribute(soap, "SOAP-ENC:position", soap->tmpbuf))
+      return soap->error;
+  }
+  if (soap->mustUnderstand)
+  { if (soap->actor && *soap->actor)
+    { if (soap_attribute(soap, soap->version == 2 ? "SOAP-ENV:role" : "SOAP-ENV:actor", soap->actor))
+        return soap->error;
+    }
+    if (soap_attribute(soap, "SOAP-ENV:mustUnderstand", soap->version == 2 ? "true" : "1"))
+      return soap->error;
+    soap->mustUnderstand = 0;
+  }
+  if (soap->encoding)
+  { if (soap->encodingStyle && soap->local_namespaces)
+    { if (!*soap->encodingStyle)
+      { if (soap->local_namespaces[1].out)
+          soap->encodingStyle = soap->local_namespaces[1].out;
+        else
+          soap->encodingStyle = soap->local_namespaces[1].ns;
+      }
+      if (soap_attribute(soap, "SOAP-ENV:encodingStyle", soap->encodingStyle))
+        return soap->error;
+    }
+    soap->encoding = 0;
+  }
+  soap->null = 0;
+  soap->position = 0;
+  if (soap->part == SOAP_BEGIN_SECURITY && (soap->mode & SOAP_XML_CANONICAL))
+    soap->part = SOAP_IN_SECURITY;
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element_begin_out(struct soap *soap, const char *tag, int id, const char *type)
+{ if (*tag == '-')
+    return SOAP_OK;
+  if (soap_element(soap, tag, id, type))
+    return soap->error;
+  return soap_element_start_end_out(soap, NULL);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+#ifndef HAVE_STRRCHR
+SOAP_FMAC1
+char*
+SOAP_FMAC2
+soap_strrchr(const char *s, int t)
+{ register char *r = NULL;
+  while (*s)
+    if (*s++ == t)
+      r = (char*)s - 1;
+  return r;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+#ifndef HAVE_STRTOL
+SOAP_FMAC1
+long
+SOAP_FMAC2
+soap_strtol(const char *s, char **t, int b)
+{ register long n = 0;
+  register int c;
+  while (*s > 0 && *s <= 32)
+    s++;
+  if (b == 10)
+  { short neg = 0;
+    if (*s == '-')
+    { s++;
+      neg = 1;
+    }
+    else if (*s == '+')
+      s++;
+    while ((c = *s) && c >= '0' && c <= '9')
+    { if (n >= 214748364 && (n > 214748364 || c >= '8'))
+        break;
+      n *= 10;
+      n += c - '0';
+      s++;
+    }
+    if (neg)
+      n = -n;
+  }
+  else /* b == 16 and value is always positive */
+  { while ((c = *s))
+    { if (c >= '0' && c <= '9')
+        c -= '0';
+      else if (c >= 'A' && c <= 'F')
+        c -= 'A' - 10;
+      else if (c >= 'a' && c <= 'f')
+        c -= 'a' - 10;
+      if (n > 0x07FFFFFF)
+        break;
+      n <<= 4;
+      n += c;
+      s++;
+    }
+  }
+  if (t)
+    *t = (char*)s;
+  return n;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+#ifndef HAVE_STRTOUL
+SOAP_FMAC1
+unsigned long
+SOAP_FMAC2
+soap_strtoul(const char *s, char **t, int b)
+{ unsigned long n = 0;
+  register int c;
+  while (*s > 0 && *s <= 32)
+    s++;
+  if (b == 10)
+  { if (*s == '+')
+      s++;
+    while ((c = *s) && c >= '0' && c <= '9')
+    { if (n >= 429496729 && (n > 429496729 || c >= '6'))
+        break;
+      n *= 10;
+      n += c - '0';
+      s++;
+    }
+  }
+  else /* b == 16 */
+  { while ((c = *s))
+    { if (c >= '0' && c <= '9')
+        c -= '0';
+      else if (c >= 'A' && c <= 'F')
+        c -= 'A' - 10;
+      else if (c >= 'a' && c <= 'f')
+        c -= 'a' - 10;
+      if (n > 0x0FFFFFFF)
+        break;
+      n <<= 4;
+      n += c;
+      s++;
+    }
+  }
+  if (t)
+    *t = (char*)s;
+  return n;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_array_begin_out(struct soap *soap, const char *tag, int id, const char *type, const char *offset)
+{ if (soap_element(soap, tag, id, "SOAP-ENC:Array"))
+    return soap->error;
+  if (soap->version == 2)
+  { const char *s;
+    s = soap_strrchr(type, '[');
+    if ((size_t)(s - type) < sizeof(soap->tmpbuf))
+    { strncpy(soap->tmpbuf, type, s - type);
+      soap->tmpbuf[s - type] = '\0';
+      if (type && *type && (soap_attribute(soap, "SOAP-ENC:itemType", soap->tmpbuf)))
+        return soap->error;
+      if (s && (soap_attribute(soap, "SOAP-ENC:arraySize", s + 1)))
+        return soap->error;
+    }
+  }
+  else
+  { if (offset && (soap_attribute(soap, "SOAP-ENC:offset", offset)))
+      return soap->error;
+    if (type && *type && (soap_attribute(soap, "SOAP-ENC:arrayType", type)))
+      return soap->error;
+  }
+#ifndef WITH_LEAN
+  if (type && *type && (soap->mode & SOAP_XML_CANONICAL))
+  { const char *s = strchr(type, ':');
+    if (s)
+      soap_utilize_ns(soap, type, s - type);
+  }
+#endif
+  return soap_element_start_end_out(soap, NULL);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element_start_end_out(struct soap *soap, const char *tag)
+{ register struct soap_attribute *tp;
+#ifndef WITH_LEAN
+  if (soap->mode & SOAP_XML_CANONICAL)
+  { struct soap_nlist *np;
+    for (tp = soap->attributes; tp; tp = tp->next)
+    { if (tp->visible && tp->name)
+      { const char *s = strchr(tp->name, ':');
+        if (s)
+          soap_utilize_ns(soap, tp->name, s - tp->name);
+      }
+    }
+    for (np = soap->nlist; np; np = np->next)
+    { if (np->index == 1 && np->ns)
+      { sprintf(soap->tmpbuf, "xmlns:%s", np->id);
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Enabling utilized binding (level=%u) %s='%s'\n", np->level, soap->tmpbuf, np->ns));
+        soap_set_attr(soap, soap->tmpbuf, np->ns);
+        np->index = 2;
+      }
+    }
+  }
+#endif
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+  { register struct soap_dom_attribute **att;
+    att = &soap->dom->atts;
+    for (tp = soap->attributes; tp; tp = tp->next)
+    { if (tp->visible)
+      { *att = (struct soap_dom_attribute*)soap_malloc(soap, sizeof(struct soap_dom_attribute));
+        if (!*att)
+          return soap->error;
+        (*att)->next = NULL;
+        (*att)->nstr = NULL;
+        (*att)->name = soap_strdup(soap, tp->name);
+        (*att)->data = soap_strdup(soap, tp->value);
+        (*att)->wide = NULL;
+        (*att)->soap = soap;
+        att = &(*att)->next;
+        tp->visible = 0;
+      }
+    }
+    return SOAP_OK;
+  }
+#endif
+  for (tp = soap->attributes; tp; tp = tp->next)
+  { if (tp->visible)
+    {
+#ifdef WITH_XMLNS
+      const char *s = strchr(tp->name, ':');
+      if (s)
+      { size_t n = s - tp->name;
+        if (soap->nlist && !strncmp(soap->nlist->id, tp->name, n) && !soap->nlist->id[n])
+          s++;
+        else
+          s = tp->name;
+        if (soap_send(soap, " ") || soap_send(soap, s))
+          return soap->error;
+      }
+      else
+#endif
+      if (soap_send(soap, " ") || soap_send(soap, tp->name))
+        return soap->error;
+      if (tp->visible == 2 && tp->value)
+        if (soap_send_raw(soap, "=\"", 2)
+         || soap_string_out(soap, tp->value, 1)
+         || soap_send_raw(soap, "\"", 1))
+          return soap->error;
+      tp->visible = 0;
+    }
+  }
+  if (tag)
+  { 
+#ifndef WITH_LEAN
+    if (soap->mode & SOAP_XML_CANONICAL)
+    { if (soap_send_raw(soap, ">", 1)
+       || soap_element_end_out(soap, tag))
+        return soap->error;
+      return SOAP_OK;
+    }
+#endif
+    soap->level--;	/* decrement level just before /> */
+    if (soap_send_raw(soap, "/>", 2))
+      return soap->error;
+    return SOAP_OK;
+  }
+  return soap_send_raw(soap, ">", 1);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element_end_out(struct soap *soap, const char *tag)
+{ if (*tag == '-')
+    return SOAP_OK;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Element ending tag='%s'\n", tag));
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+  { if (soap->dom->prnt)
+      soap->dom = soap->dom->prnt;
+    return SOAP_OK;
+  }
+#endif
+#ifndef WITH_LEAN
+  if (soap->mode & SOAP_XML_CANONICAL)
+    soap_pop_ns(soap);
+  if (soap->mode & SOAP_XML_INDENT)
+  { if (!soap->body)
+    { if (soap_send_raw(soap, soap_indent, soap->level < sizeof(soap_indent) ? soap->level : sizeof(soap_indent) - 1))
+        return soap->error;
+    }
+    soap->body = 0;
+  }
+#endif
+#ifdef WITH_XMLNS
+  { const char *s = strchr(tag, ':');
+    if (s && strncmp(tag, "SOAP-ENV", s - tag))
+    { soap_pop_ns(soap);
+      tag = s + 1;
+    }
+  }
+#endif
+  if (soap_send_raw(soap, "</", 2)
+   || soap_send(soap, tag))
+    return soap->error;
+  soap->level--;	/* decrement level just before > */
+  return soap_send_raw(soap, ">", 1);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element_ref(struct soap *soap, const char *tag, int id, int href)
+{ register int n = 0;
+  const char *s = "href";
+  if (soap->version == 2)
+  { s = "SOAP-ENC:ref";
+    n = 1;
+  }
+  sprintf(soap->href, "#_%d", href);
+  return soap_element_href(soap, tag, id, s, soap->href + n);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element_href(struct soap *soap, const char *tag, int id, const char *ref, const char *val)
+{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Element '%s' reference %s='%s'\n", tag, ref, val));
+  if (soap_element(soap, tag, id, NULL)
+   || soap_attribute(soap, ref, val)
+   || soap_element_start_end_out(soap, tag))
+    return soap->error;
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element_null(struct soap *soap, const char *tag, int id, const char *type)
+{ struct soap_attribute *tp;
+  for (tp = soap->attributes; tp; tp = tp->next)
+    if (tp->visible)
+      break;
+  if (tp || (soap->version == 2 && soap->position > 0) || id > 0 || (soap->mode & SOAP_XML_NIL))
+  { if (soap_element(soap, tag, id, type))
+      return soap->error;
+    if (!tp && soap_attribute(soap, "xsi:nil", "true"))
+      return soap->error;
+    return soap_element_start_end_out(soap, tag);
+  }
+  soap->null = 1;
+  soap->position = 0;
+  soap->mustUnderstand = 0;
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element_id(struct soap *soap, const char *tag, int id, const void *p, const struct soap_array *a, int n, const char *type, int t) 
+{ if (!p || (a && !a->__ptr))
+  { soap_element_null(soap, tag, id, type);
+    return -1;
+  }
+#ifndef WITH_NOIDREF
+  if (soap->mode & SOAP_XML_TREE)
+    return 0;
+  if (id < 0)
+  { struct soap_plist *pp;
+    if (a)
+      id = soap_array_pointer_lookup(soap, p, a, n, t, &pp);
+    else
+      id = soap_pointer_lookup(soap, p, t, &pp);
+    if (id)
+    { if (soap_is_embedded(soap, pp))
+      { soap_element_ref(soap, tag, 0, id);
+        return -1;
+      }
+      if (soap_is_single(soap, pp))
+        return 0;
+      soap_set_embedded(soap, pp);
+    }
+  }
+  return id;
+#else
+  return 0;
+#endif
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element_result(struct soap *soap, const char *tag)
+{ if (soap->version == 2 && soap->encodingStyle)
+  { if (soap_element(soap, "SOAP-RPC:result", 0, NULL)
+     || soap_attribute(soap, "xmlns:SOAP-RPC", soap_rpc)
+     || soap_element_start_end_out(soap, NULL)
+     || soap_string_out(soap, tag, 0)
+     || soap_element_end_out(soap, "SOAP-RPC:result"))
+      return soap->error;
+  }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_check_result(struct soap *soap, const char *tag)
+{ if (soap->version == 2 && soap->encodingStyle)
+  { soap_instring(soap, ":result", NULL, NULL, 0, 2, -1, -1);
+    /* just ignore content for compliance reasons, but should compare tag to element's QName value? */
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_attribute(struct soap *soap, const char *name, const char *value)
+{ 
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && !(soap->mode & SOAP_XML_CANONICAL) && soap->dom)
+  { register struct soap_dom_attribute *a = (struct soap_dom_attribute*)soap_malloc(soap, sizeof(struct soap_dom_attribute));
+    if (!a)
+      return soap->error;
+    a->next = soap->dom->atts;
+    a->nstr = NULL;
+    a->name = soap_strdup(soap, name);
+    a->data = soap_strdup(soap, value);
+    a->wide = NULL;
+    a->soap = soap;
+    soap->dom->atts = a;
+    return SOAP_OK;
+  }
+#endif
+#ifndef WITH_LEAN
+  if (soap->mode & SOAP_XML_CANONICAL)
+  { /* TODO: consider using this code to handle default namespace bindings
+    if (!strncmp(name, "xmlns", 5) && (name[5] == ':' || name[5] == '\0'))
+    { if (name[5] == ':')
+        soap_push_ns(soap, name + 6, value, 0);
+      else
+        soap_push_ns(soap, "", value, 0);
+    }
+    */
+    if (!strncmp(name, "xmlns:", 6))
+      soap_push_ns(soap, name + 6, value, 0);
+    else if (soap_set_attr(soap, name, value))
+      return soap->error;
+  }
+  else
+#endif
+  { if (soap_send(soap, " ") || soap_send(soap, name))
+      return soap->error;
+    if (value)
+      if (soap_send_raw(soap, "=\"", 2)
+       || soap_string_out(soap, value, 1)
+       || soap_send_raw(soap, "\"", 1))
+        return soap->error;
+  }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element_begin_in(struct soap *soap, const char *tag, int nillable, const char *type)
+{ if (!soap_peek_element(soap))
+  { if (soap->other)
+      return soap->error = SOAP_TAG_MISMATCH;
+    if (tag && *tag == '-')
+      return SOAP_OK;
+    if (!(soap->error = soap_match_tag(soap, soap->tag, tag)))
+    { soap->peeked = 0;
+      if (type && *soap->type && soap_match_tag(soap, soap->type, type))
+        return soap->error = SOAP_TYPE;
+      if (!nillable && soap->null && (soap->mode & SOAP_XML_STRICT))
+        return soap->error = SOAP_NULL;
+      if (soap->body)
+        soap->level++;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Begin element found (level=%u) '%s'='%s'\n", soap->level, soap->tag, tag?tag:SOAP_STR_EOS ));
+    }
+  }
+  else if (soap->error == SOAP_NO_TAG && tag && *tag == '-')
+    soap->error = SOAP_OK;
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_element_end_in(struct soap *soap, const char *tag)  
+{ register soap_wchar c;
+  register char *s;
+  register int n = 0;
+  if (tag && *tag == '-')
+    return SOAP_OK;
+  if (soap->error == SOAP_NO_TAG)
+    soap->error = SOAP_OK;
+#ifdef WITH_DOM
+  /* this whitespace or mixed content is not insignificant for DOM */
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+  { if (!soap->peeked && !soap_string_in(soap, 3, -1, -1))
+      return soap->error;
+    if (soap->dom->prnt)
+      soap->dom = soap->dom->prnt;
+  }
+#endif
+  if (soap->peeked)
+  { if (*soap->tag)
+      n++;
+    soap->peeked = 0;
+  }
+  do
+  { while (((c = soap_get(soap)) != SOAP_TT))
+    { if ((int)c == EOF)
+        return soap->error = SOAP_EOF;
+      if (c == SOAP_LT)
+        n++;
+      else if (c == '/')
+      { c = soap_get(soap);
+        if (c == SOAP_GT)
+          n--;
+        else
+          soap_unget(soap, c);
+      }
+    }
+  } while (n--);
+  s = soap->tag;
+  n = sizeof(soap->tag);
+  while (soap_notblank(c = soap_get(soap)))
+  { if (--n > 0)
+      *s++ = (char)c;
+  }
+  *s = '\0';
+  if ((int)c == EOF)
+    return soap->error = SOAP_EOF;
+  while (soap_blank(c))
+    c = soap_get(soap);
+  if (c != SOAP_GT)
+    return soap->error = SOAP_SYNTAX_ERROR;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "End element found (level=%u) '%s'='%s'\n", soap->level, soap->tag, tag?tag:SOAP_STR_EOS));
+#ifndef WITH_LEAN
+  if (tag && (soap->mode & SOAP_XML_STRICT))
+  { soap_pop_namespace(soap);
+    if (soap_match_tag(soap, soap->tag, tag))
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "End element tag name does not match\n"));
+      return soap->error = SOAP_SYNTAX_ERROR;
+    }
+  }
+#endif
+  soap->level--;
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char *
+SOAP_FMAC2
+soap_attr_value(struct soap *soap, const char *name, int flag)
+{ register struct soap_attribute *tp;
+  if (*name == '-')
+    return SOAP_STR_EOS;
+  for (tp = soap->attributes; tp; tp = tp->next)
+  { if (tp->visible && !soap_match_tag(soap, tp->name, name))
+      break;
+  }
+  if (tp)
+  { if (flag == 2 && (soap->mode & SOAP_XML_STRICT))
+      soap->error = SOAP_PROHIBITED;
+    else
+      return tp->value;
+  }
+  else if (flag == 1 && (soap->mode & SOAP_XML_STRICT))
+    soap->error = SOAP_REQUIRED;
+  return NULL;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_set_attr(struct soap *soap, const char *name, const char *value)
+{ register struct soap_attribute *tp;
+  if (*name == '-')
+    return SOAP_OK;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Set attribute %s='%s'\n", name, value?value:SOAP_STR_EOS));
+  for (tp = soap->attributes; tp; tp = tp->next)
+  { if (!strcmp(tp->name, name))
+      break;
+  }
+  if (!tp)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Allocate attribute %s\n", name));
+    if (!(tp = (struct soap_attribute*)SOAP_MALLOC(soap, sizeof(struct soap_attribute) + strlen(name))))
+      return soap->error = SOAP_EOM;
+    tp->ns = NULL;
+#ifndef WITH_LEAN
+    if (soap->mode & SOAP_XML_CANONICAL)
+    { struct soap_attribute **tpp = &soap->attributes;
+      const char *s = strchr(name, ':');
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Inserting attribute %s for c14n\n", name))
+      if (!strncmp(name, "xmlns", 5))
+      { for (; *tpp; tpp = &(*tpp)->next)
+          if (strncmp((*tpp)->name, "xmlns", 5) || strcmp((*tpp)->name + 5, name + 5) > 0)
+            break;
+      }
+      else if (!s)
+      { for (; *tpp; tpp = &(*tpp)->next)
+          if (strncmp((*tpp)->name, "xmlns", 5) && ((*tpp)->ns || strcmp((*tpp)->name, name) > 0))
+            break;
+      }
+      else
+      { int k;
+        for (; *tpp; tpp = &(*tpp)->next)
+        { if (!strncmp((*tpp)->name, "xmlns:", 6) && !strncmp((*tpp)->name + 6, name, s - name) && !(*tpp)->name[6 + s - name])
+          { if (!tp->ns)
+            { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Canonicalization: prefix %s=%p (%s)\n", name, (*tpp)->ns, (*tpp)->ns));
+              tp->ns = (*tpp)->ns;
+            }
+          }
+          else if (strncmp((*tpp)->name, "xmlns", 5) && (*tpp)->ns && tp->ns && ((k = strcmp((*tpp)->ns, tp->ns)) > 0 || (!k && strcmp((*tpp)->name, name) > 0)))
+            break;
+        }
+      }
+      tp->next = *tpp;
+      *tpp = tp;
+    }
+    else
+#endif
+    { tp->next = soap->attributes;
+      soap->attributes = tp;
+    }
+    strcpy(tp->name, name);
+    tp->value = NULL;
+  }
+  else if (tp->visible)
+  { return SOAP_OK;
+  }
+  else if (value && tp->value && tp->size <= strlen(value))
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Free attribute value of %s (free %p)\n", name, tp->value));
+    SOAP_FREE(soap, tp->value);
+    tp->value = NULL;
+    tp->ns = NULL;
+  }
+  if (value)
+  { if (!tp->value)
+    { tp->size = strlen(value) + 1;
+      if (!(tp->value = (char*)SOAP_MALLOC(soap, tp->size)))
+        return soap->error = SOAP_EOM;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Allocate attribute value for %s (%p)\n", tp->name, tp->value));
+    }
+    strcpy(tp->value, value);
+    if (!strncmp(tp->name, "xmlns:", 6))
+      tp->ns = tp->value;
+    tp->visible = 2;
+#ifndef WITH_LEAN
+    if (!strcmp(name, "wsu:Id"))
+    { soap->part = SOAP_BEGIN_SECURITY;
+      strncpy(soap->id, value, sizeof(soap->id));
+      soap->id[sizeof(soap->id)-1] = '\0';
+    }
+#endif
+  }
+  else
+    tp->visible = 1;
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_clr_attr(struct soap *soap)
+{ register struct soap_attribute *tp;
+#ifndef WITH_LEAN
+  if ((soap->mode & SOAP_XML_CANONICAL))
+  { while (soap->attributes)
+    { tp = soap->attributes->next;
+      if (soap->attributes->value)
+        SOAP_FREE(soap, soap->attributes->value);
+      SOAP_FREE(soap, soap->attributes);
+      soap->attributes = tp;
+    }
+  }
+  else
+#endif
+  { for (tp = soap->attributes; tp; tp = tp->next)
+      tp->visible = 0;
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+static int
+soap_getattrval(struct soap *soap, char *s, size_t n, soap_wchar d)
+{ register size_t i;
+  for (i = 0; i < n; i++)
+  { register soap_wchar c = soap_get(soap);
+    switch (c)
+    {
+    case SOAP_TT:
+      *s++ = '<';
+      soap_unget(soap, '/');
+      break;
+    case SOAP_LT:
+      *s++ = '<';
+      break;
+    case SOAP_GT:
+      if (d == ' ')
+      { soap_unget(soap, c);
+        *s = '\0';
+        return SOAP_OK;
+      }
+      *s++ = '>';
+      break;
+    case SOAP_QT:
+      if (c == d)
+      { *s = '\0';
+        return SOAP_OK;
+      }
+      *s++ = '"';
+      break;
+    case SOAP_AP:
+      if (c == d)
+      { *s = '\0';
+        return SOAP_OK;
+      }
+      *s++ = '\'';
+      break;
+    case '\t':
+    case '\n':
+    case '\r':
+    case ' ':
+    case '/':
+      if (d == ' ')
+      { soap_unget(soap, c);
+        *s = '\0';
+        return SOAP_OK;
+      }
+    default:
+      if ((int)c == EOF)
+        return soap->error = SOAP_EOF;
+      *s++ = (char)c;
+    }
+  }
+  return soap->error = SOAP_EOM;
+}
+#endif
+
+/******************************************************************************/
+#ifdef WITH_FAST
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_store_lab(struct soap *soap, const char *s, size_t n)
+{ soap->labidx = 0;
+  return soap_append_lab(soap, s, n);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifdef WITH_FAST
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_append_lab(struct soap *soap, const char *s, size_t n)
+{ if (soap->labidx + n >= soap->lablen)
+  { register char *t = soap->labbuf;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Enlarging look-aside buffer to append data, old size=%lu", (unsigned long)soap->lablen));
+    if (soap->lablen == 0)
+      soap->lablen = SOAP_LABLEN;
+    while (soap->labidx + n >= soap->lablen)
+      soap->lablen <<= 1;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, ", new size=%lu\n", (unsigned long)soap->lablen));
+    soap->labbuf = (char*)SOAP_MALLOC(soap, soap->lablen);
+    if (!soap->labbuf)
+    { if (t)
+        SOAP_FREE(soap, t);
+      return soap->error = SOAP_EOM;
+    }
+    if (t)
+    { memcpy(soap->labbuf, t, soap->labidx);
+      SOAP_FREE(soap, t);
+    }
+  }
+  if (s)
+  { memcpy(soap->labbuf + soap->labidx, s, n);
+    soap->labidx += n;
+  }
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_peek_element(struct soap *soap)
+{
+#ifdef WITH_DOM
+  register struct soap_dom_attribute **att = NULL;
+  register char *lead = NULL;
+#endif
+  register struct soap_attribute *tp, *tq = NULL;
+  const char *t;
+  register char *s;
+  register soap_wchar c;
+  register int i;
+  if (soap->peeked)
+  { if (!*soap->tag)
+      return soap->error = SOAP_NO_TAG;
+    return SOAP_OK;
+  }
+  soap->peeked = 1;
+  soap->id[0] = '\0';
+  soap->href[0] = '\0';
+  soap->type[0] = '\0';
+  soap->arrayType[0] = '\0';
+  soap->arraySize[0] = '\0';
+  soap->arrayOffset[0] = '\0';
+  soap->other = 0;
+  soap->root = -1;
+  soap->position = 0;
+  soap->null = 0;
+  soap->mustUnderstand = 0;
+  c = soap_getutf8(soap);
+#ifdef WITH_DOM
+  /* whitespace leading to start tag is not insignificant for DOM */
+  if (soap_blank(c))
+  { soap->labidx = 0;
+    do
+    { if (soap_append_lab(soap, NULL, 0))
+        return SOAP_EOM;
+      s = soap->labbuf + soap->labidx;
+      i = soap->lablen - soap->labidx;
+      soap->labidx = soap->lablen;
+      while (soap_blank(c) && i--)
+      { *s++ = c;
+        c = soap_getutf8(soap);
+      }
+    }
+    while (soap_blank(c));
+    *s = '\0';
+    lead = soap_strdup(soap, soap->labbuf);
+  }
+#else
+  while (soap_blank(c))
+    c = soap_getutf8(soap);
+#endif
+  if (c != SOAP_LT)
+  { *soap->tag = '\0';
+    if ((int)c == EOF)
+      return soap->error = SOAP_EOF;
+    soap_unget(soap, c);
+#ifdef WITH_DOM
+    /* whitespace leading to end tag is not insignificant for DOM */
+    if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+      soap->dom->tail = soap_strdup(soap, lead);
+#endif
+    return soap->error = SOAP_NO_TAG;
+  }
+  s = soap->tag;
+  do c = soap_get1(soap);
+  while (soap_blank(c));
+  i = sizeof(soap->tag);
+  while (c != '>' && c != '/' && soap_notblank(c) && (int)c != EOF)
+  { if (--i > 0)
+      *s++ = (char)c;
+    c = soap_get1(soap);
+  }
+  while (soap_blank(c))
+    c = soap_get1(soap);
+  *s = '\0';
+#ifdef WITH_DOM
+  if (soap->mode & SOAP_XML_DOM)
+  { register struct soap_dom_element *elt;
+    elt = (struct soap_dom_element*)soap_malloc(soap, sizeof(struct soap_dom_element));
+    if (!elt)
+      return soap->error;
+    elt->next = NULL;
+    elt->nstr = NULL;
+    elt->name = soap_strdup(soap, soap->tag);
+    elt->prnt = soap->dom;
+    elt->elts = NULL;
+    elt->atts = NULL;
+    elt->data = NULL;
+    elt->wide = NULL;
+    elt->type = 0;
+    elt->node = NULL;
+    elt->head = lead;
+    elt->tail = NULL;
+    elt->soap = soap;
+    if (soap->dom)
+    { struct soap_dom_element *p = soap->dom->elts;
+      if (p)
+      { while (p->next)
+          p = p->next;
+        p->next = elt;
+      }
+      else
+        soap->dom->elts = elt;
+    }
+    soap->dom = elt;
+    att = &elt->atts;
+  }
+#endif
+  soap_pop_namespace(soap);
+  for (tp = soap->attributes; tp; tp = tp->next)
+    tp->visible = 0;
+  while ((int)c != EOF && c != '>' && c != '/')
+  { s = soap->tmpbuf;
+    i = sizeof(soap->tmpbuf);
+    while (c != '=' && c != '>' && c != '/' && soap_notblank(c) && (int)c != EOF)
+    { if (--i > 0)
+        *s++ = (char)c;
+      c = soap_get1(soap);
+    }
+    *s = '\0';
+    if (i == sizeof(soap->tmpbuf))
+      return soap->error = SOAP_SYNTAX_ERROR;
+#ifdef WITH_DOM
+    /* add attribute name to dom */
+    if (att)
+    { *att = (struct soap_dom_attribute*)soap_malloc(soap, sizeof(struct soap_dom_attribute));
+       if (!*att)
+         return soap->error;
+       (*att)->next = NULL;
+       (*att)->nstr = NULL;
+       (*att)->name = soap_strdup(soap, soap->tmpbuf);
+       (*att)->data = NULL;
+       (*att)->wide = NULL;
+       (*att)->soap = soap;
+    }
+#endif
+    if (!strncmp(soap->tmpbuf, "xmlns", 5))
+    { if (soap->tmpbuf[5] == ':')
+        t = soap->tmpbuf + 6;
+      else if (soap->tmpbuf[5])
+        t = NULL;
+      else
+        t = SOAP_STR_EOS;
+    }
+    else
+      t = NULL;
+    tq = NULL;
+    for (tp = soap->attributes; tp; tq = tp, tp = tp->next)
+    { if (!SOAP_STRCMP(tp->name, soap->tmpbuf))
+        break;
+    }
+    if (!tp)
+    { tp = (struct soap_attribute*)SOAP_MALLOC(soap, sizeof(struct soap_attribute) + strlen(soap->tmpbuf));
+      if (!tp)
+        return soap->error = SOAP_EOM;
+      strcpy(tp->name, soap->tmpbuf);
+      tp->value = NULL;
+      tp->size = 0;
+      /* if attribute name is qualified, append it to the end of the list */
+      if (tq && strchr(soap->tmpbuf, ':'))
+      { tq->next = tp;
+        tp->next = NULL;
+      }
+      else
+      { tp->next = soap->attributes;
+        soap->attributes = tp;
+      }
+    }
+    while (soap_blank(c))
+      c = soap_get1(soap);
+    if (c == '=')
+    { do c = soap_getutf8(soap);
+      while (soap_blank(c));
+      if (c != SOAP_QT && c != SOAP_AP)
+      { soap_unget(soap, c);
+        c = ' '; /* blank delimiter */
+      }
+      if (soap_getattrval(soap, tp->value, tp->size, c))
+      {
+#ifdef WITH_FAST
+        if (soap->error != SOAP_EOM)
+          return soap->error;
+        soap->error = SOAP_OK;
+        if (soap_store_lab(soap, tp->value, tp->size))
+          return soap->error;
+        if (tp->value)
+          SOAP_FREE(soap, tp->value);
+        for (;;)
+        { if (soap_getattrval(soap, soap->labbuf + soap->labidx, soap->lablen - soap->labidx, c))
+          { if (soap->error != SOAP_EOM)
+              return soap->error;
+            soap->error = SOAP_OK;
+            soap->labidx = soap->lablen;
+            if (soap_append_lab(soap, NULL, 0))
+              return soap->error;
+          }
+          else
+            break;
+        }
+        if (soap->labidx)
+          tp->size = soap->lablen;
+        else
+        { tp->size = strlen(soap->labbuf) + 1;
+          if (tp->size < SOAP_LABLEN)
+            tp->size = SOAP_LABLEN;
+        }
+        if (!(tp->value = (char*)SOAP_MALLOC(soap, tp->size)))
+          return soap->error = SOAP_EOM;
+        strcpy(tp->value, soap->labbuf);
+#else
+        size_t n;
+        if (soap->error != SOAP_EOM)
+          return soap->error;
+        soap->error = SOAP_OK;
+        if (soap_new_block(soap) == NULL)
+          return soap->error;
+        for (;;)
+        { if (!(s = (char*)soap_push_block(soap, NULL, SOAP_BLKLEN)))
+            return soap->error;
+          if (soap_getattrval(soap, s, SOAP_BLKLEN, c))
+          { if (soap->error != SOAP_EOM)
+              return soap->error;
+            soap->error = SOAP_OK;
+          }
+          else
+            break;
+        }
+        n = tp->size + soap->blist->size;
+        if (!(s = (char*)SOAP_MALLOC(soap, n)))
+          return soap->error = SOAP_EOM;
+        if (tp->value)
+        { memcpy(s, tp->value, tp->size);
+          SOAP_FREE(soap, tp->value);
+        }
+        soap_save_block(soap, NULL, s + tp->size, 0);
+        tp->value = s;
+        tp->size = n;
+#endif
+      }
+      do c = soap_get1(soap);
+      while (soap_blank(c));
+      tp->visible = 2; /* seen this attribute w/ value */
+#ifdef WITH_DOM
+      if (att)
+        (*att)->data = soap_strdup(soap, tp->value);
+#endif
+    }
+    else
+      tp->visible = 1; /* seen this attribute w/o value */
+#ifdef WITH_DOM
+    if (att)
+      att = &(*att)->next;
+#endif
+    if (t && tp->value)
+    { if (soap_push_namespace(soap, t, tp->value))
+        return soap->error;
+    }
+  }
+#ifdef WITH_DOM
+  if (att)
+  { soap->dom->nstr = soap_current_namespace(soap, soap->tag);
+    for (att = &soap->dom->atts; *att; att = &(*att)->next)
+      (*att)->nstr = soap_current_namespace(soap, (*att)->name);
+  }
+#endif
+  if ((int)c == EOF)
+    return soap->error = SOAP_EOF;
+  if (!(soap->body = (c != '/')))
+    do c = soap_get1(soap);
+    while (soap_blank(c));
+#ifdef WITH_DOM
+  if (soap->mode & SOAP_XML_DOM)
+  { if (!soap->body && soap->dom->prnt)
+      soap->dom = soap->dom->prnt;
+  }
+#endif
+  for (tp = soap->attributes; tp; tp = tp->next)
+  { if (tp->visible && tp->value)
+    { 
+#ifndef WITH_NOIDREF
+      if (!strcmp(tp->name, "id"))
+      { if ((soap->version > 0 && !(soap->mode & SOAP_XML_TREE))
+         || (soap->mode & SOAP_XML_GRAPH))
+        { *soap->id = '#';
+          strncpy(soap->id + 1, tp->value, sizeof(soap->id) - 2);
+          soap->id[sizeof(soap->id)-1] = '\0';
+        }
+      }
+      else if (!strcmp(tp->name, "href"))
+      { if (soap->version == 1
+         || (soap->mode & SOAP_XML_GRAPH)
+         || (soap->mode & SOAP_ENC_MTOM))
+        { strncpy(soap->href, tp->value, sizeof(soap->href) - 1);
+          soap->href[sizeof(soap->href)-1] = '\0';
+        }
+      }
+      else
+#endif
+      if (!soap_match_tag(soap, tp->name, "xsi:type"))
+      { strncpy(soap->type, tp->value, sizeof(soap->type) - 1);
+        soap->type[sizeof(soap->type)-1] = '\0';
+      }
+      else if ((!soap_match_tag(soap, tp->name, "xsi:null")
+             || !soap_match_tag(soap, tp->name, "xsi:nil"))
+            && (!strcmp(tp->value, "1")
+             || !strcmp(tp->value, "true")))
+      { soap->null = 1;
+      }
+      else if (soap->version == 1)
+      { if (!soap_match_tag(soap, tp->name, "SOAP-ENC:arrayType"))
+        { s = soap_strrchr(tp->value, '[');
+          if (s && (size_t)(s - tp->value) < sizeof(soap->arrayType))
+          { strncpy(soap->arrayType, tp->value, s - tp->value);
+            soap->arrayType[s - tp->value] = '\0';
+            strncpy(soap->arraySize, s, sizeof(soap->arraySize) - 1);
+          }
+          else
+            strncpy(soap->arrayType, tp->value, sizeof(soap->arrayType) - 1);
+          soap->arraySize[sizeof(soap->arrayType)-1] = '\0';
+          soap->arrayType[sizeof(soap->arrayType)-1] = '\0';
+        }
+        else if (!soap_match_tag(soap, tp->name, "SOAP-ENC:offset"))
+          strncpy(soap->arrayOffset, tp->value, sizeof(soap->arrayOffset));
+        else if (!soap_match_tag(soap, tp->name, "SOAP-ENC:position"))
+          soap->position = soap_getposition(tp->value, soap->positions);
+        else if (!soap_match_tag(soap, tp->name, "SOAP-ENC:root"))
+          soap->root = ((!strcmp(tp->value, "1") || !strcmp(tp->value, "true")));
+        else if (!soap_match_tag(soap, tp->name, "SOAP-ENV:mustUnderstand")
+              && (!strcmp(tp->value, "1") || !strcmp(tp->value, "true")))
+          soap->mustUnderstand = 1;
+        else if (!soap_match_tag(soap, tp->name, "SOAP-ENV:actor"))
+        { if ((!soap->actor || strcmp(soap->actor, tp->value))
+           && strcmp(tp->value, "http://schemas.xmlsoap.org/soap/actor/next"))
+            soap->other = 1;
+        }
+      }
+      else if (soap->version == 2)
+      {
+#ifndef WITH_NOIDREF
+        if (!strcmp(tp->name, "ref")
+         || !soap_match_tag(soap, tp->name, "SOAP-ENC:ref"))
+        { *soap->href = '#';
+          strncpy(soap->href + 1, tp->value, sizeof(soap->href) - 2);
+          soap->href[sizeof(soap->href)-1] = '\0';
+        }
+        else
+#endif
+	if (!soap_match_tag(soap, tp->name, "SOAP-ENC:itemType"))
+          strncpy(soap->arrayType, tp->value, sizeof(soap->arrayType) - 1);
+        else if (!soap_match_tag(soap, tp->name, "SOAP-ENC:arraySize"))
+          strncpy(soap->arraySize, tp->value, sizeof(soap->arraySize) - 1);
+        else if (!soap_match_tag(soap, tp->name, "SOAP-ENV:mustUnderstand")
+              && (!strcmp(tp->value, "1") || !strcmp(tp->value, "true")))
+          soap->mustUnderstand = 1;
+        else if (!soap_match_tag(soap, tp->name, "SOAP-ENV:role"))
+        { if ((!soap->actor || strcmp(soap->actor, tp->value))
+           && strcmp(tp->value, "http://www.w3.org/2003/05/soap-envelope/role/next"))
+            soap->other = 1;
+        }
+      }
+    }
+  }
+  return soap->error = SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_retry(struct soap *soap)
+{ soap->error = SOAP_OK;
+  soap_revert(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_revert(struct soap *soap)
+{ if (!soap->peeked)
+  { soap->peeked = 1;
+    if (soap->body)
+      soap->level--;
+  }
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Reverting last element (level=%u)\n", soap->level));
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_string_out(struct soap *soap, const char *s, int flag)
+{ register const char *t;
+  register soap_wchar c;
+  register soap_wchar mask = (soap_wchar)0xFFFFFF80UL;
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+  { soap->dom->data = soap_strdup(soap, s);
+    return SOAP_OK;
+  }
+#endif
+  if (soap->mode & SOAP_C_UTFSTRING)
+    mask = 0;
+  t = s;
+  while ((c = *t++))
+  { switch (c)
+    { 
+    case 0x09:
+      if (flag)
+      { if (soap_send_raw(soap, s, t - s - 1) || soap_send_raw(soap, "&#x9;", 5))
+          return soap->error;
+        s = t;
+      }
+      break;
+    case 0x0A:
+      if (flag || !(soap->mode & SOAP_XML_CANONICAL))
+      { if (soap_send_raw(soap, s, t - s - 1) || soap_send_raw(soap, "&#xA;", 5))
+          return soap->error;
+        s = t;
+      }
+      break;
+    case 0x0D:
+      if (soap_send_raw(soap, s, t - s - 1) || soap_send_raw(soap, "&#xD;", 5))
+        return soap->error;
+      s = t;
+      break;
+    case '&':
+      if (soap_send_raw(soap, s, t - s - 1) || soap_send_raw(soap, "&amp;", 5))
+        return soap->error;
+      s = t;
+      break;
+    case '<':
+      if (soap_send_raw(soap, s, t - s - 1) || soap_send_raw(soap, "&lt;", 4))
+        return soap->error;
+      s = t;
+      break;
+    case '>':
+      if (!flag)
+      { if (soap_send_raw(soap, s, t - s - 1) || soap_send_raw(soap, "&gt;", 4))
+          return soap->error;
+        s = t;
+      }
+      break;
+    case '"':
+      if (flag)
+      { if (soap_send_raw(soap, s, t - s - 1) || soap_send_raw(soap, "&quot;", 6))
+          return soap->error;
+        s = t;
+      }
+      break;
+    default:
+#ifndef WITH_LEANER
+#ifdef HAVE_MBTOWC
+      if (soap->mode & SOAP_C_MBSTRING)
+      { wchar_t wc;
+        register int m = mbtowc(&wc, t - 1, MB_CUR_MAX);
+        if (m > 0 && wc != c)
+        { if (soap_send_raw(soap, s, t - s - 1) || soap_pututf8(soap, wc))
+            return soap->error;
+          s = t += m - 1;
+          continue;
+        }
+      }
+#endif
+#endif
+#ifndef WITH_NOSTRINGTOUTF8
+      if ((c & mask) || !(c & 0xFFFFFFE0UL))
+      { if (soap_send_raw(soap, s, t - s - 1) || soap_pututf8(soap, (unsigned char)c))
+          return soap->error;
+        s = t;
+      }
+#endif
+    }
+  }
+  return soap_send_raw(soap, s, t - s - 1);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_string_in(struct soap *soap, int flag, long minlen, long maxlen)
+{ register char *s;
+  char *t = NULL;
+  register size_t i;
+  register long l = 0;
+  register int n = 0, f = 0, m = 0;
+  register soap_wchar c;
+#if !defined(WITH_LEANER) && defined(HAVE_WCTOMB)
+  char buf[MB_LEN_MAX > 8 ? MB_LEN_MAX : 8];
+#else
+  char buf[8];
+#endif
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Reading string content\n"));
+  if (soap->peeked)
+  { if (*soap->tag)
+    {
+#ifndef WITH_LEAN
+      struct soap_attribute *tp;
+      t = soap->tmpbuf;
+      *t = '<';
+      t[sizeof(soap->tmpbuf)-1] = '\0';
+      strncpy(t + 1, soap->tag, sizeof(soap->tmpbuf) - 2);
+      t += strlen(t);
+      for (tp = soap->attributes; tp; tp = tp->next)
+      { if (tp->visible)
+        { if (t >= soap->tmpbuf + sizeof(soap->tmpbuf) - 2)
+            break;
+          *t++ = ' ';
+          strcpy(t, tp->name);
+          t += strlen(t);
+          if (t >= soap->tmpbuf + sizeof(soap->tmpbuf) - 2)
+            break; /* too many or large attribute values */
+          if (tp->value)
+          { *t++ = '=';
+            *t++ = '"';
+            strcpy(t, tp->value);
+            t += strlen(t);
+            *t++ = '"';
+          }
+        }
+      }
+      if (!soap->body)
+        *t++ = '/';
+      *t++ = '>';
+      *t = '\0';
+      t = soap->tmpbuf;
+      m = (int)strlen(soap->tmpbuf);
+#endif
+      if (soap->body)
+        n = 1;
+      f = 1;
+      soap->peeked = 0;
+    }
+  }
+#ifdef WITH_CDATA
+  if (!flag)
+  { register int state = 0;
+#ifdef WITH_FAST
+    soap->labidx = 0;			/* use look-aside buffer */
+#else
+    if (soap_new_block(soap) == NULL)
+      return NULL;
+#endif
+    for (;;)
+    { 
+#ifdef WITH_FAST
+      register size_t k;
+      if (soap_append_lab(soap, NULL, 0))	/* allocate more space in look-aside buffer if necessary */
+        return NULL;
+      s = soap->labbuf + soap->labidx;	/* space to populate */
+      k = soap->lablen - soap->labidx;	/* number of bytes available */
+      soap->labidx = soap->lablen;	/* claim this space */
+#else
+      register size_t k = SOAP_BLKLEN;
+      if (!(s = (char*)soap_push_block(soap, NULL, k)))
+        return NULL;
+#endif
+      for (i = 0; i < k; i++)
+      { if (m > 0)
+        { *s++ = *t++;	/* copy multibyte characters */
+          m--;
+          continue;
+        }
+        c = soap_getchar(soap);
+        if ((int)c == EOF)
+          goto end;
+        if (c >= 0x80 && state != 1 && !(soap->mode & SOAP_ENC_LATIN))
+        { soap_unget(soap, c);
+          c = soap_getutf8(soap);
+          if (soap->mode & SOAP_C_UTFSTRING)
+          { if ((c & 0x80000000) && c >= -0x7FFFFF80 && c < SOAP_AP)
+            { c &= 0x7FFFFFFF;
+              t = buf;
+              if (c < 0x0800)
+                *t++ = (char)(0xC0 | ((c >> 6) & 0x1F));
+              else
+              { if (c < 0x010000)
+                  *t++ = (char)(0xE0 | ((c >> 12) & 0x0F));
+                else
+                { if (c < 0x200000)
+                    *t++ = (char)(0xF0 | ((c >> 18) & 0x07));
+                  else
+                  { if (c < 0x04000000)
+                      *t++ = (char)(0xF8 | ((c >> 24) & 0x03));
+                    else
+                    { *t++ = (char)(0xFC | ((c >> 30) & 0x01));
+                      *t++ = (char)(0x80 | ((c >> 24) & 0x3F));
+                    }
+                    *t++ = (char)(0x80 | ((c >> 18) & 0x3F));
+                  }     
+                  *t++ = (char)(0x80 | ((c >> 12) & 0x3F));
+                }
+                *t++ = (char)(0x80 | ((c >> 6) & 0x3F));
+              }
+              *t++ = (char)(0x80 | (c & 0x3F));
+              m = (int)(t - buf) - 1;
+              t = buf;
+              *s++ = *t++;
+              continue;
+            }
+          }
+        }
+        switch (state)
+        { case 1:
+            if (c == ']')
+              state = 4;
+            *s++ = c;
+            continue;
+          case 2:
+            if (c == '-')
+              state = 6;
+            *s++ = c;
+            continue;
+          case 3:
+            if (c == '?')
+              state = 8;
+            *s++ = c;
+            continue;
+          /* CDATA */
+          case 4:
+            if (c == ']')
+              state = 5;
+            else
+              state = 1;
+            *s++ = c;
+            continue;
+          case 5:
+            if (c == '>')
+              state = 0;
+            else
+              state = 1;
+            *s++ = c;
+            continue;
+          /* comment */
+          case 6:
+            if (c == '-')
+              state = 7;
+            else
+              state = 2;
+            *s++ = c;
+            continue;
+          case 7:
+            if (c == '>')
+              state = 0;
+            else
+              state = 2;
+            *s++ = c;
+            continue;
+          /* PI */
+          case 8:
+            if (c == '>')
+              state = 0;
+            else
+              state = 3;
+            *s++ = c;
+            continue;
+        }
+        switch (c)
+        {
+        case '/':
+          if (n > 0)
+          { c = soap_getchar(soap);
+            if (c == '>')
+              n--;
+            soap_unget(soap, c);
+          }
+          *s++ = '/';
+          break;
+        case '<':
+          c = soap_getchar(soap);
+          if (c == '/')
+          { if (n == 0)
+            { c = SOAP_TT;
+              goto end;
+            }
+            n--;
+          }
+          else if (c == '!')
+          { c = soap_getchar(soap);
+            if (c == '[')
+            { do c = soap_getchar(soap);
+              while ((int)c != EOF && c != '[');
+              if ((int)c == EOF)
+                 goto end;
+              t = (char*)"![CDATA[";
+              m = 8;
+              state = 1;
+            }
+            else if (c == '-')
+            { if ((c = soap_getchar(soap)) == '-')
+                state = 2;
+              t = (char*)"!-";
+              m = 2;
+              soap_unget(soap, c);
+            }
+            else
+            { t = (char*)"!";
+              m = 1;
+              soap_unget(soap, c);
+            }
+            *s++ = '<';
+            break;
+          }
+          else if (c == '?')
+            state = 3;
+          else
+            n++;
+          soap_unget(soap, c);
+          *s++ = '<';
+          break;
+        case '>':
+          *s++ = '>';
+          break;
+        case '"':
+          *s++ = '"';
+          break;
+        default:
+#ifndef WITH_LEANER
+#ifdef HAVE_WCTOMB
+          if (soap->mode & SOAP_C_MBSTRING)
+          { m = wctomb(buf, c & 0x7FFFFFFF);
+            if (m >= 1 && m <= (int)MB_CUR_MAX)
+            { t = buf;
+              *s++ = *t++;
+              m--;
+            }
+            else
+            { *s++ = SOAP_UNKNOWN_CHAR;
+              m = 0;
+            }
+          }
+          else
+#endif
+#endif
+            *s++ = (char)(c & 0xFF);
+        }
+        l++;
+        if ((soap->mode & SOAP_XML_STRICT) && maxlen >= 0 && l > maxlen)
+        { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "String too long: maxlen=%ld\n", maxlen));
+          soap->error = SOAP_LENGTH;
+          return NULL;
+        }
+      }
+    }
+  }
+#endif
+#ifdef WITH_FAST
+  soap->labidx = 0;			/* use look-aside buffer */
+#else
+  if (soap_new_block(soap) == NULL)
+    return NULL;
+#endif
+  for (;;)
+  { 
+#ifdef WITH_FAST
+    register size_t k;
+    if (soap_append_lab(soap, NULL, 0))	/* allocate more space in look-aside buffer if necessary */
+      return NULL;
+    s = soap->labbuf + soap->labidx;	/* space to populate */
+    k = soap->lablen - soap->labidx;	/* number of bytes available */
+    soap->labidx = soap->lablen;	/* claim this space */
+#else
+    register size_t k = SOAP_BLKLEN;
+    if (!(s = (char*)soap_push_block(soap, NULL, k)))
+      return NULL;
+#endif
+    for (i = 0; i < k; i++)
+    { if (m > 0)
+      { *s++ = *t++;	/* copy multibyte characters */
+        m--;
+        continue;
+      }
+      if (soap->mode & SOAP_C_UTFSTRING)
+      { if (((c = soap_get(soap)) & 0x80000000) && c >= -0x7FFFFF80 && c < SOAP_AP)
+        { c &= 0x7FFFFFFF;
+          t = buf;
+          if (c < 0x0800)
+            *t++ = (char)(0xC0 | ((c >> 6) & 0x1F));
+          else
+          { if (c < 0x010000)
+              *t++ = (char)(0xE0 | ((c >> 12) & 0x0F));
+            else
+            { if (c < 0x200000)
+                *t++ = (char)(0xF0 | ((c >> 18) & 0x07));
+              else
+              { if (c < 0x04000000)
+                  *t++ = (char)(0xF8 | ((c >> 24) & 0x03));
+                else
+                { *t++ = (char)(0xFC | ((c >> 30) & 0x01));
+                  *t++ = (char)(0x80 | ((c >> 24) & 0x3F));
+                }
+                *t++ = (char)(0x80 | ((c >> 18) & 0x3F));
+              }     
+              *t++ = (char)(0x80 | ((c >> 12) & 0x3F));
+            }
+            *t++ = (char)(0x80 | ((c >> 6) & 0x3F));
+          }
+          *t++ = (char)(0x80 | (c & 0x3F));
+          m = (int)(t - buf) - 1;
+          t = buf;
+          *s++ = *t++;
+          continue;
+        }
+      }
+      else
+        c = soap_getutf8(soap);
+      switch (c)
+      {
+      case SOAP_TT:
+        if (n == 0)
+          goto end;
+        n--;
+        *s++ = '<';
+        t = (char*)"/";
+        m = 1;
+        break;
+      case SOAP_LT:
+	if (f && n == 0)
+	  goto end;
+        n++;
+        *s++ = '<';
+        break;
+      case SOAP_GT:
+        *s++ = '>';
+        break;
+      case SOAP_QT:
+        *s++ = '"';
+        break;
+      case SOAP_AP:
+        *s++ = '\'';
+        break;
+      case '/':
+        if (n > 0)
+        { c = soap_get(soap);
+          if (c == SOAP_GT)
+            n--;
+          soap_unget(soap, c);
+        }
+        *s++ = '/';
+        break;
+      case (soap_wchar)('<' | 0x80000000):
+        if (flag)
+          *s++ = '<';
+        else
+        { *s++ = '&';
+          t = (char*)"lt;";
+          m = 3;
+        }
+        break;
+      case (soap_wchar)('>' | 0x80000000):
+        if (flag)
+          *s++ = '>';
+        else
+        { *s++ = '&';
+          t = (char*)"gt;";
+          m = 3;
+        }
+        break;
+      case (soap_wchar)('&' | 0x80000000):
+        if (flag)
+          *s++ = '&';
+        else
+        { *s++ = '&';
+          t = (char*)"amp;";
+          m = 4;
+        }
+        break;
+      case (soap_wchar)('"' | 0x80000000):
+        if (flag)
+          *s++ = '"';
+        else
+        { *s++ = '&';
+          t = (char*)"quot;";
+          m = 5;
+        }
+        break;
+      case (soap_wchar)('\'' | 0x80000000):
+        if (flag)
+          *s++ = '\'';
+        else
+        { *s++ = '&';
+          t = (char*)"apos;";
+          m = 5;
+        }
+        break;
+      default:
+        if ((int)c == EOF)
+          goto end;
+#ifndef WITH_LEANER
+#ifdef HAVE_WCTOMB
+        if (soap->mode & SOAP_C_MBSTRING)
+        { m = wctomb(buf, c & 0x7FFFFFFF);
+          if (m >= 1 && m <= (int)MB_CUR_MAX)
+          { t = buf;
+            *s++ = *t++;
+            m--;
+          }
+          else
+          { *s++ = SOAP_UNKNOWN_CHAR;
+            m = 0;
+          }
+        }
+        else
+#endif
+#endif
+          *s++ = (char)(c & 0xFF);
+      }
+      l++;
+      if ((soap->mode & SOAP_XML_STRICT) && maxlen >= 0 && l > maxlen)
+      { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "String too long: maxlen=%ld\n", maxlen));
+        soap->error = SOAP_LENGTH;
+        return NULL;
+      }
+    }
+  }
+end:
+  soap_unget(soap, c);
+  *s = '\0';
+#ifdef WITH_FAST
+  t = soap_strdup(soap, soap->labbuf);
+#else
+  soap_size_block(soap, NULL, i+1);
+  t = soap_save_block(soap, NULL, 0);
+#endif
+  if ((soap->mode & SOAP_XML_STRICT) && l < minlen)
+  { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "String too short: %ld chars, minlen=%ld\n", l, minlen));
+    soap->error = SOAP_LENGTH;
+    return NULL;
+  }
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+  { if (flag == 3)
+      soap->dom->tail = t;
+    else
+      soap->dom->data = t;
+  }
+#endif
+  if (flag == 2)
+    if (soap_s2QName(soap, t, &t))
+      return NULL;
+  return t;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_wstring_out(struct soap *soap, const wchar_t *s, int flag)
+{ const char *t;
+  char tmp;
+  register soap_wchar c;
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+  { wchar_t *r = (wchar_t*)s;
+    int n = 1;
+    while (*r++)
+      n++;
+    soap->dom->wide = r = (wchar_t*)soap_malloc(soap, n * sizeof(wchar_t));
+    while (n--)
+      *r++ = *s++;
+    return SOAP_OK;
+  }
+#endif
+  while ((c = *s++))
+  { switch (c)
+    { 
+    case 0x09:
+      if (flag)
+        t = "&#x9;";
+      else
+        t = "\t";
+      break;
+    case 0x0A:
+      if (flag || !(soap->mode & SOAP_XML_CANONICAL))
+        t = "&#xA;";
+      else
+        t = "\n";
+      break;
+    case 0x0D:
+      t = "&#xD;";
+      break;
+    case '&':
+      t = "&amp;";
+      break;
+    case '<':
+      t = "&lt;";
+      break;
+    case '>':
+      if (flag)
+        t = ">";
+      else
+        t = "&gt;";
+      break;
+    case '"':
+      if (flag)
+        t = "&quot;";
+      else
+        t = "\"";
+      break;
+    default:
+      if (c >= 0x20 && c < 0x80)
+      { tmp = (char)c;
+        if (soap_send_raw(soap, &tmp, 1))
+          return soap->error;
+      }
+      else if (soap_pututf8(soap, (unsigned long)c))
+        return soap->error;
+      continue;
+    }
+    if (soap_send(soap, t))
+      return soap->error;
+  }
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_2
+SOAP_FMAC1
+wchar_t *
+SOAP_FMAC2
+soap_wstring_in(struct soap *soap, int flag, long minlen, long maxlen)
+{ wchar_t *s;
+  register int i, n = 0, f = 0;
+  register long l = 0;
+  register soap_wchar c;
+  char *t = NULL;
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Reading wide string content\n"));
+  if (soap->peeked)
+  { if (*soap->tag)
+    {
+#ifndef WITH_LEAN
+      struct soap_attribute *tp;
+      t = soap->tmpbuf;
+      *t = '<';
+      t[sizeof(soap->tmpbuf)-1] = '\0';
+      strncpy(t + 1, soap->tag, sizeof(soap->tmpbuf) - 2);
+      t += strlen(t);
+      for (tp = soap->attributes; tp; tp = tp->next)
+      { if (tp->visible)
+        { if (t >= soap->tmpbuf + sizeof(soap->tmpbuf) - 2)
+            break;
+          *t++ = ' ';
+          strcpy(t, tp->name);
+          t += strlen(t);
+          if (t >= soap->tmpbuf + sizeof(soap->tmpbuf) - 2)
+            break;
+          if (tp->value)
+          { *t++ = '=';
+            *t++ = '"';
+            strcpy(t, tp->value);
+            t += strlen(t);
+            *t++ = '"';
+          }
+        }
+      }
+      if (!soap->body)
+        *t++ = '/';
+      *t++ = '>';
+      *t = '\0';
+      t = soap->tmpbuf;
+#endif
+      if (soap->body)
+        n = 1;
+      f = 1;
+      soap->peeked = 0;
+    }
+  }
+  if (soap_new_block(soap) == NULL)
+    return NULL;
+  for (;;)
+  { if (!(s = (wchar_t*)soap_push_block(soap, NULL, sizeof(wchar_t)*SOAP_BLKLEN)))
+      return NULL;
+    for (i = 0; i < SOAP_BLKLEN; i++)
+    { if (t)
+      { *s++ = (wchar_t)*t++;
+        if (!*t)
+          t = NULL;
+        continue;
+      }
+      c = soap_getutf8(soap);
+      switch (c)
+      {
+      case SOAP_TT:
+        if (n == 0)
+          goto end;
+        n--;
+        *s++ = '<';
+        soap_unget(soap, '/');
+        break;
+      case SOAP_LT:
+        if (f && n == 0)
+	  goto end;
+        n++;
+        *s++ = '<';
+        break;
+      case SOAP_GT:
+        *s++ = '>';
+        break;
+      case SOAP_QT:
+        *s++ = '"';
+        break;
+      case SOAP_AP:
+        *s++ = '\'';
+        break;
+      case '/':
+        if (n > 0)
+        { c = soap_getutf8(soap);
+          if (c == SOAP_GT)
+            n--;
+          soap_unget(soap, c);
+        }
+        *s++ = '/';
+        break;
+      case '<':
+        if (flag)
+          *s++ = (soap_wchar)'<';
+        else
+        { *s++ = (soap_wchar)'&';
+          t = (char*)"lt;";
+        }
+        break;
+      case '>':
+        if (flag)
+          *s++ = (soap_wchar)'>';
+        else
+        { *s++ = (soap_wchar)'&';
+          t = (char*)"gt;";
+        }
+        break;
+      case '"':
+        if (flag)
+          *s++ = (soap_wchar)'"';
+        else
+        { *s++ = (soap_wchar)'&';
+          t = (char*)"quot;";
+        }
+        break;
+      default:
+        if ((int)c == EOF)
+          goto end;
+        *s++ = (wchar_t)c & 0x7FFFFFFF;
+      }
+      l++;
+      if ((soap->mode & SOAP_XML_STRICT) && maxlen >= 0 && l > maxlen)
+      { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "String too long: maxlen=%ld\n", maxlen));
+        soap->error = SOAP_LENGTH;
+        return NULL;
+      }
+    }
+  }
+end:
+  soap_unget(soap, c);
+  *s = '\0';
+  soap_size_block(soap, NULL, sizeof(wchar_t) * (i + 1));
+  if ((soap->mode & SOAP_XML_STRICT) && l < minlen)
+  { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "String too short: %ld chars, minlen=%ld\n", l, minlen));
+    soap->error = SOAP_LENGTH;
+    return NULL;
+  }
+  s = (wchar_t*)soap_save_block(soap, NULL, NULL, 0);
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+    soap->dom->wide = s;
+#endif
+  return s;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_int2s(struct soap *soap, int n)
+{ return soap_long2s(soap, (long)n);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outint(struct soap *soap, const char *tag, int id, const int *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_long2s(soap, (long)*p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2int(struct soap *soap, const char *s, int *p)
+{ if (s)
+  { char *r;
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+    soap_reset_errno;
+#endif
+#endif
+    *p = (int)soap_strtol(s, &r, 10);
+    if ((s == r && (soap->mode & SOAP_XML_STRICT)) || *r
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+     || soap_errno == SOAP_ERANGE
+#endif
+#endif
+    )
+      soap->error = SOAP_TYPE;
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int *
+SOAP_FMAC2
+soap_inint(struct soap *soap, const char *tag, int *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":int")
+   && soap_match_tag(soap, soap->type, ":short")
+   && soap_match_tag(soap, soap->type, ":byte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+#endif
+  p = (int*)soap_id_enter(soap, soap->id, p, t, sizeof(int), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (int*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(int), 0, NULL);
+  else if (p)
+  { if (soap_s2int(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_long2s(struct soap *soap, long n)
+{ sprintf(soap->tmpbuf, "%ld", n);
+  return soap->tmpbuf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outlong(struct soap *soap, const char *tag, int id, const long *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_long2s(soap, *p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2long(struct soap *soap, const char *s, long *p)
+{ if (s)
+  { char *r;
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+    soap_reset_errno;
+#endif
+#endif
+    *p = soap_strtol(s, &r, 10);
+    if ((s == r && (soap->mode & SOAP_XML_STRICT)) || *r
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+     || soap_errno == SOAP_ERANGE
+#endif
+#endif
+    )
+      soap->error = SOAP_TYPE;
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+long *
+SOAP_FMAC2
+soap_inlong(struct soap *soap, const char *tag, long *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":int")
+   && soap_match_tag(soap, soap->type, ":short")
+   && soap_match_tag(soap, soap->type, ":byte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+#endif
+  p = (long*)soap_id_enter(soap, soap->id, p, t, sizeof(long), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (long*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(long), 0, NULL);
+  else if (p)
+  { if (soap_s2long(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_LONG642s(struct soap *soap, LONG64 n)
+{ sprintf(soap->tmpbuf, SOAP_LONG_FORMAT, n);
+  return soap->tmpbuf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outLONG64(struct soap *soap, const char *tag, int id, const LONG64 *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_LONG642s(soap, *p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2LONG64(struct soap *soap, const char *s, LONG64 *p)
+{ if (s)
+  {
+#ifdef HAVE_STRTOLL
+    char *r;
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+    soap_reset_errno;
+#endif
+#endif
+    *p = strtoll(s, &r, 10);
+    if ((s == r && (soap->mode & SOAP_XML_STRICT)) || *r
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+       || soap_errno == SOAP_ERANGE
+#endif
+#endif
+      )
+#else
+# ifdef HAVE_SSCANF
+    if (sscanf(s, SOAP_LONG_FORMAT, p) != 1)
+# endif
+#endif
+      soap->error = SOAP_TYPE;
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+LONG64 *
+SOAP_FMAC2
+soap_inLONG64(struct soap *soap, const char *tag, LONG64 *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":integer")
+   && soap_match_tag(soap, soap->type, ":positiveInteger")
+   && soap_match_tag(soap, soap->type, ":negativeInteger")
+   && soap_match_tag(soap, soap->type, ":nonPositiveInteger")
+   && soap_match_tag(soap, soap->type, ":nonNegativeInteger")
+   && soap_match_tag(soap, soap->type, ":long")
+   && soap_match_tag(soap, soap->type, ":int")
+   && soap_match_tag(soap, soap->type, ":short")
+   && soap_match_tag(soap, soap->type, ":byte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+#endif
+  p = (LONG64*)soap_id_enter(soap, soap->id, p, t, sizeof(LONG64), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (LONG64*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(LONG64), 0, NULL);
+  else if (p)
+  { if (soap_s2LONG64(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_byte2s(struct soap *soap, char n)
+{ return soap_long2s(soap, (long)n);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outbyte(struct soap *soap, const char *tag, int id, const char *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_long2s(soap, (long)*p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2byte(struct soap *soap, const char *s, char *p)
+{ if (s)
+  { long n;
+    char *r;
+    n = soap_strtol(s, &r, 10);
+    if (s == r || *r || n < -128 || n > 127)
+      soap->error = SOAP_TYPE;
+    *p = (char)n;
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_inbyte(struct soap *soap, const char *tag, char *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":byte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+#endif
+  p = (char*)soap_id_enter(soap, soap->id, p, t, sizeof(char), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (char*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(char), 0, NULL);
+  else if (p)
+  { if (soap_s2byte(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_short2s(struct soap *soap, short n)
+{ return soap_long2s(soap, (long)n);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outshort(struct soap *soap, const char *tag, int id, const short *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_long2s(soap, (long)*p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2short(struct soap *soap, const char *s, short *p)
+{ if (s)
+  { long n;
+    char *r;
+    n = soap_strtol(s, &r, 10);
+    if (s == r || *r || n < -32768 || n > 32767)
+      soap->error = SOAP_TYPE;
+    *p = (short)n;
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+short *
+SOAP_FMAC2
+soap_inshort(struct soap *soap, const char *tag, short *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":short")
+   && soap_match_tag(soap, soap->type, ":byte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+#endif
+  p = (short*)soap_id_enter(soap, soap->id, p, t, sizeof(short), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (short*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(short), 0, NULL);
+  else if (p)
+  { if (soap_s2short(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_float2s(struct soap *soap, float n)
+{ char *s;
+  if (soap_isnan((double)n))
+    return "NaN";
+  if (soap_ispinff(n))
+    return "INF";
+  if (soap_isninff(n))
+    return "-INF";
+  s = soap->tmpbuf;
+#if defined(HAVE_SPRINTF_L)
+  sprintf_l(s, soap->c_locale, soap->float_format, n);
+#else
+  sprintf(s, soap->float_format, n);
+  s = strchr(s, ',');	/* convert decimal comma to DP */
+  if (s)
+    *s = '.';
+#endif
+  return soap->tmpbuf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outfloat(struct soap *soap, const char *tag, int id, const float *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_float2s(soap, *p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2float(struct soap *soap, const char *s, float *p)
+{ if (s)
+  { if (!*s)
+      return soap->error = SOAP_TYPE;
+    if (!soap_tag_cmp(s, "INF"))
+      *p = FLT_PINFTY;
+    else if (!soap_tag_cmp(s, "+INF"))
+      *p = FLT_PINFTY;
+    else if (!soap_tag_cmp(s, "-INF"))
+      *p = FLT_NINFTY;
+    else if (!soap_tag_cmp(s, "NaN"))
+      *p = FLT_NAN;
+    else
+    {
+/* On some systems, strtof appears to be broken or doesn't link: use with caution */
+#if defined(HAVE_STRTOF_L)
+      char *r;
+      *p = strtof_l((char*)s, &r, soap->c_locale);
+      if (*r)
+#elif defined(HAVE_STRTOD_L)
+      char *r;
+      *p = (float)strtod_l(s, &r, soap->c_locale);
+      if (*r)
+#elif defined(HAVE_STRTOF)
+      char *r;
+      *p = strtof((char*)s, &r);
+      if (*r)
+#elif defined(HAVE_STRTOD)
+      char *r;
+      *p = (float)strtod(s, &r);
+      if (*r)
+#endif
+      {
+#if defined(HAVE_SSCANF_L) && !defined(HAVE_STRTOF_L) && !defined(HAVE_STRTOD_L)
+        if (sscanf_l(s, soap->c_locale, "%g", p) != 1)
+          soap->error = SOAP_TYPE;
+#elif defined(HAVE_SSCANF)
+        if (sscanf(s, "%g", p) != 1)
+          soap->error = SOAP_TYPE;
+#else
+        soap->error = SOAP_TYPE;
+#endif
+      }
+    }
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+static int soap_isnumeric(struct soap *soap, const char *type)
+{ if (soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":float")
+   && soap_match_tag(soap, soap->type, ":double")
+   && soap_match_tag(soap, soap->type, ":decimal")
+   && soap_match_tag(soap, soap->type, ":integer")
+   && soap_match_tag(soap, soap->type, ":positiveInteger")
+   && soap_match_tag(soap, soap->type, ":negativeInteger")
+   && soap_match_tag(soap, soap->type, ":nonPositiveInteger")
+   && soap_match_tag(soap, soap->type, ":nonNegativeInteger")
+   && soap_match_tag(soap, soap->type, ":long")
+   && soap_match_tag(soap, soap->type, ":int")
+   && soap_match_tag(soap, soap->type, ":short")
+   && soap_match_tag(soap, soap->type, ":byte")
+   && soap_match_tag(soap, soap->type, ":unsignedLong")
+   && soap_match_tag(soap, soap->type, ":unsignedInt")
+   && soap_match_tag(soap, soap->type, ":unsignedShort")
+   && soap_match_tag(soap, soap->type, ":unsignedByte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return SOAP_ERR;
+  }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+float *
+SOAP_FMAC2
+soap_infloat(struct soap *soap, const char *tag, float *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type != '\0' && soap_isnumeric(soap, type))
+    return NULL;
+#endif
+  p = (float*)soap_id_enter(soap, soap->id, p, t, sizeof(float), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (float*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(float), 0, NULL);
+  else if (p)
+  { if (soap_s2float(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_double2s(struct soap *soap, double n)
+{ char *s;
+  if (soap_isnan(n))
+    return "NaN";
+  if (soap_ispinfd(n))
+    return "INF";
+  if (soap_isninfd(n))
+    return "-INF";
+  s = soap->tmpbuf;
+#if defined(HAVE_SPRINTF_L)
+  sprintf_l(s, soap->c_locale, soap->double_format, n);
+#else
+  sprintf(s, soap->double_format, n);
+  s = strchr(s, ',');	/* convert decimal comma to DP */
+  if (s)
+    *s = '.';
+#endif
+  return soap->tmpbuf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outdouble(struct soap *soap, const char *tag, int id, const double *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_double2s(soap, *p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2double(struct soap *soap, const char *s, double *p)
+{ if (s)
+  { if (!*s)
+      return soap->error = SOAP_TYPE;
+    if (!soap_tag_cmp(s, "INF"))
+      *p = DBL_PINFTY;
+    else if (!soap_tag_cmp(s, "+INF"))
+      *p = DBL_PINFTY;
+    else if (!soap_tag_cmp(s, "-INF"))
+      *p = DBL_NINFTY;
+    else if (!soap_tag_cmp(s, "NaN"))
+      *p = DBL_NAN;
+    else
+    {
+#if defined(HAVE_STRTOD_L)
+      char *r;
+      *p = strtod_l(s, &r, soap->c_locale);
+      if (*r)
+#elif defined(HAVE_STRTOD)
+      char *r;
+      *p = strtod(s, &r);
+      if (*r)
+#endif
+      {
+#if defined(HAVE_SSCANF_L) && !defined(HAVE_STRTOF_L) && !defined(HAVE_STRTOD_L)
+        if (sscanf_l(s, soap->c_locale, "%lg", p) != 1)
+          soap->error = SOAP_TYPE;
+#elif defined(HAVE_SSCANF)
+        if (sscanf(s, "%lg", p) != 1)
+          soap->error = SOAP_TYPE;
+#else
+        soap->error = SOAP_TYPE;
+#endif
+      }
+    }
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+double *
+SOAP_FMAC2
+soap_indouble(struct soap *soap, const char *tag, double *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type != '\0' && soap_isnumeric(soap, type))
+    return NULL;
+#endif
+  p = (double*)soap_id_enter(soap, soap->id, p, t, sizeof(double), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (double*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(double), 0, NULL);
+  else if (p)
+  { if (soap_s2double(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_unsignedByte2s(struct soap *soap, unsigned char n)
+{ return soap_unsignedLong2s(soap, (unsigned long)n);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outunsignedByte(struct soap *soap, const char *tag, int id, const unsigned char *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_unsignedLong2s(soap, (unsigned long)*p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2unsignedByte(struct soap *soap, const char *s, unsigned char *p)
+{ if (s)
+  { unsigned long n;
+    char *r;
+    n = soap_strtoul(s, &r, 10);
+    if (s == r || *r || n > 255)
+      soap->error = SOAP_TYPE;
+    *p = (unsigned char)n;
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+unsigned char *
+SOAP_FMAC2
+soap_inunsignedByte(struct soap *soap, const char *tag, unsigned char *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":unsignedByte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+#endif
+  p = (unsigned char*)soap_id_enter(soap, soap->id, p, t, sizeof(unsigned char), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (unsigned char*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(unsigned char), 0, NULL);
+  else if (p)
+  { if (soap_s2unsignedByte(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_unsignedShort2s(struct soap *soap, unsigned short n)
+{ return soap_unsignedLong2s(soap, (unsigned long)n);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outunsignedShort(struct soap *soap, const char *tag, int id, const unsigned short *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_unsignedLong2s(soap, (unsigned long)*p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2unsignedShort(struct soap *soap, const char *s, unsigned short *p)
+{ if (s)
+  { unsigned long n;
+    char *r;
+    n = soap_strtoul(s, &r, 10);
+    if (s == r || *r || n > 65535)
+      soap->error = SOAP_TYPE;
+    *p = (unsigned short)n;
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+unsigned short *
+SOAP_FMAC2
+soap_inunsignedShort(struct soap *soap, const char *tag, unsigned short *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":unsignedShort")
+   && soap_match_tag(soap, soap->type, ":unsignedByte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+#endif
+  p = (unsigned short*)soap_id_enter(soap, soap->id, p, t, sizeof(unsigned short), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (unsigned short*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(unsigned short), 0, NULL);
+  else if (p)
+  { if (soap_s2unsignedShort(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_unsignedInt2s(struct soap *soap, unsigned int n)
+{ return soap_unsignedLong2s(soap, (unsigned long)n);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outunsignedInt(struct soap *soap, const char *tag, int id, const unsigned int *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_unsignedLong2s(soap, (unsigned long)*p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2unsignedInt(struct soap *soap, const char *s, unsigned int *p)
+{ if (s)
+  { char *r;
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+    soap_reset_errno;
+#endif
+#endif
+    *p = (unsigned int)soap_strtoul(s, &r, 10);
+    if ((s == r && (soap->mode & SOAP_XML_STRICT)) || *r
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+     || soap_errno == SOAP_ERANGE
+#endif
+#endif
+    )
+      soap->error = SOAP_TYPE;
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+unsigned int *
+SOAP_FMAC2
+soap_inunsignedInt(struct soap *soap, const char *tag, unsigned int *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":unsignedInt")
+   && soap_match_tag(soap, soap->type, ":unsignedShort")
+   && soap_match_tag(soap, soap->type, ":unsignedByte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+#endif
+  p = (unsigned int*)soap_id_enter(soap, soap->id, p, t, sizeof(unsigned int), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (unsigned int*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(unsigned int), 0, NULL);
+  else if (p)
+  { if (soap_s2unsignedInt(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_unsignedLong2s(struct soap *soap, unsigned long n)
+{ sprintf(soap->tmpbuf, "%lu", n);
+  return soap->tmpbuf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outunsignedLong(struct soap *soap, const char *tag, int id, const unsigned long *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_unsignedLong2s(soap, *p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2unsignedLong(struct soap *soap, const char *s, unsigned long *p)
+{ if (s)
+  { char *r;
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+    soap_reset_errno;
+#endif
+#endif
+    *p = soap_strtoul(s, &r, 10);
+    if ((s == r && (soap->mode & SOAP_XML_STRICT)) || *r
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+     || soap_errno == SOAP_ERANGE
+#endif
+#endif
+    )
+      soap->error = SOAP_TYPE;
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+unsigned long *
+SOAP_FMAC2
+soap_inunsignedLong(struct soap *soap, const char *tag, unsigned long *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+#ifndef WITH_LEAN
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":unsignedInt")
+   && soap_match_tag(soap, soap->type, ":unsignedShort")
+   && soap_match_tag(soap, soap->type, ":unsignedByte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+#endif
+  p = (unsigned long*)soap_id_enter(soap, soap->id, p, t, sizeof(unsigned long), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (unsigned long*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(unsigned long), 0, NULL);
+  else if (p)
+  { if (soap_s2unsignedLong(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_ULONG642s(struct soap *soap, ULONG64 n)
+{ sprintf(soap->tmpbuf, SOAP_ULONG_FORMAT, n);
+  return soap->tmpbuf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outULONG64(struct soap *soap, const char *tag, int id, const ULONG64 *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_ULONG642s(soap, *p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2ULONG64(struct soap *soap, const char *s, ULONG64 *p)
+{ if (s)
+  {
+#ifdef HAVE_STRTOULL
+    char *r;
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+    soap_reset_errno;
+#endif
+#endif
+    *p = strtoull(s, &r, 10);
+    if ((s == r && (soap->mode & SOAP_XML_STRICT)) || *r
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+       || soap_errno == SOAP_ERANGE
+#endif
+#endif
+      )
+#else
+#ifdef HAVE_SSCANF
+    if (sscanf(s, SOAP_ULONG_FORMAT, p) != 1)
+#endif
+#endif
+      soap->error = SOAP_TYPE;
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+ULONG64 *
+SOAP_FMAC2
+soap_inULONG64(struct soap *soap, const char *tag, ULONG64 *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":positiveInteger")
+   && soap_match_tag(soap, soap->type, ":nonNegativeInteger")
+   && soap_match_tag(soap, soap->type, ":unsignedLong")
+   && soap_match_tag(soap, soap->type, ":unsignedInt")
+   && soap_match_tag(soap, soap->type, ":unsignedShort")
+   && soap_match_tag(soap, soap->type, ":unsignedByte"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+  p = (ULONG64*)soap_id_enter(soap, soap->id, p, t, sizeof(ULONG64), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (ULONG64*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(ULONG64), 0, NULL);
+  else if (p)
+  { if (soap_s2ULONG64(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2string(struct soap *soap, const char *s, char **t)
+{ if (s)
+  { if (!(*t = soap_strdup(soap, s)))
+      return soap->error = SOAP_EOM;
+    if (!(soap->mode & (SOAP_ENC_LATIN | SOAP_C_UTFSTRING)))
+    { /* TODO: consider truncating UTF8 to ASCII for regular XML attribute strings? */
+    }
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2QName(struct soap *soap, const char *s, char **t)
+{ if (s)
+  { soap->labidx = 0;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Normalized namespace(s) of QNames '%s'", s));
+    /* convert (by prefix normalize prefix) all QNames in s */
+    for (;;)
+    { size_t n;
+      struct soap_nlist *np;
+      register const char *p;
+      /* skip blanks */
+      while (*s && soap_blank(*s))
+        s++;
+      if (!*s)
+        break;
+      /* find next QName */
+      n = 1;
+      while (s[n] && !soap_blank(s[n]))
+        n++;
+      np = soap->nlist;
+      /* if there is no namespace stack, or prefix is "xml" then copy string */
+      if (!np || !strncmp(s, "xml:", 4))
+      { soap_append_lab(soap, s, n);
+      }
+      else /* we normalize the QName by replacing its prefix */
+      { p = strchr(s, ':');
+        if (p)
+        { size_t k = p - s;
+          while (np && (strncmp(np->id, s, k) || np->id[k]))
+            np = np->next;
+          p++;
+        }
+        else
+        { while (np && *np->id)
+            np = np->next;
+          p = s;
+        }
+        /* replace prefix */
+        if (np)
+        { if (np->index >= 0 && soap->local_namespaces)
+          { const char *q = soap->local_namespaces[np->index].id;
+            if (q)
+              soap_append_lab(soap, q, strlen(q));
+          }
+          else if (np->ns)
+          { soap_append_lab(soap, "\"", 1);
+            soap_append_lab(soap, np->ns, strlen(np->ns));
+            soap_append_lab(soap, "\"", 1);
+          }
+          else
+          { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\nNamespace prefix of '%s' not defined (index=%d, URI=%s)\n", s, np->index, np->ns?np->ns:SOAP_STR_EOS));
+            return soap->error = SOAP_NAMESPACE; 
+          }
+        }
+        else /* no namespace: assume default "" namespace */
+        { soap_append_lab(soap, "\"\"", 2);
+        } 
+        soap_append_lab(soap, ":", 1);
+        soap_append_lab(soap, p, n - (p-s));
+      }
+      /* advance to next and add spacing */
+      s += n;
+      if (*s)
+        soap_append_lab(soap, " ", 1);
+    }
+    soap_append_lab(soap, SOAP_STR_EOS, 1);
+    *t = soap_strdup(soap, soap->labbuf);
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, " into '%s'\n", *t));
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_QName2s(struct soap *soap, const char *s)
+{ const char *t = NULL;
+  if (s)
+  { soap->labidx = 0;
+    for (;;)
+    { size_t n;
+      /* skip blanks */
+      while (*s && soap_blank(*s))
+        s++;
+      if (!*s)
+        break;
+      /* find next QName */
+      n = 1;
+      while (s[n] && !soap_blank(s[n]))
+        n++;
+      /* normal prefix: pass string as is */
+      if (*s != '"')
+      { soap_append_lab(soap, s, n);
+#ifndef WITH_LEAN
+        if ((soap->mode & SOAP_XML_CANONICAL))
+        { const char *r = strchr(s, ':');
+          if (r)
+            soap_utilize_ns(soap, s, r - s);
+        }
+#endif
+      }
+      else /* URL-based string prefix */
+      { const char *q;
+        s++;
+        q = strchr(s, '"');
+        if (q)
+        { struct Namespace *p = soap->local_namespaces;
+          if (p)
+          { for (; p->id; p++)
+            { if (p->ns)
+                if (!soap_tag_cmp(s, p->ns))
+                  break;
+              if (p->in)
+                if (!soap_tag_cmp(s, p->in))
+                  break;
+            }
+          }
+          /* URL is in the namespace table? */
+          if (p && p->id)
+          { soap_append_lab(soap, p->id, strlen(p->id));
+          }
+          else /* not in namespace table: create xmlns binding */
+          { char *r = soap_strdup(soap, s);
+            r[q-s] = '\0';
+            sprintf(soap->tmpbuf, "xmlns:_%d", soap->idnum++);
+            soap_set_attr(soap, soap->tmpbuf, r);
+            soap_append_lab(soap, soap->tmpbuf + 6, strlen(soap->tmpbuf + 6));
+          }
+          soap_append_lab(soap, q + 1, n - (q-s) - 1);
+        }
+      }
+      /* advance to next and add spacing */
+      s += n;
+      if (*s)
+        soap_append_lab(soap, " ", 1);
+    }
+    soap_append_lab(soap, SOAP_STR_EOS, 1);
+    t = soap_strdup(soap, soap->labbuf);
+  }
+  return t;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2wchar(struct soap *soap, const char *s, wchar_t **t)
+{ if (s)
+  { wchar_t *r;
+    *t = r = (wchar_t*)soap_malloc(soap, sizeof(wchar_t) * (strlen(s) + 1));
+    if (!r)
+      return soap->error;
+    if (soap->mode & SOAP_ENC_LATIN)
+    { while (*s)
+        *r++ = (wchar_t)*s++;
+    }
+    else
+    { /* Convert UTF8 to wchar */
+      while (*s)
+      { register soap_wchar c, c1, c2, c3, c4;
+        c = (unsigned char)*s++;
+        if (c < 0x80)
+          *r++ = (wchar_t)c;
+        else
+        { c1 = (soap_wchar)*s++ & 0x3F;
+          if (c < 0xE0)
+            *r++ = (wchar_t)(((soap_wchar)(c & 0x1F) << 6) | c1);
+          else
+          { c2 = (soap_wchar)*s++ & 0x3F;
+            if (c < 0xF0)
+              *r++ = (wchar_t)(((soap_wchar)(c & 0x0F) << 12) | (c1 << 6) | c2);
+            else
+            { c3 = (soap_wchar)*s++ & 0x3F;
+              if (c < 0xF8)
+                *r++ = (wchar_t)(((soap_wchar)(c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3);
+              else
+              { c4 = (soap_wchar)*s++ & 0x3F;
+                if (c < 0xFC)
+                  *r++ = (wchar_t)(((soap_wchar)(c & 0x03) << 24) | (c1 << 18) | (c2 << 12) | (c3 << 6) | c4);
+                else
+                  *r++ = (wchar_t)(((soap_wchar)(c & 0x01) << 30) | (c1 << 24) | (c2 << 18) | (c3 << 12) | (c4 << 6) | (soap_wchar)(*s++ & 0x3F));
+              }
+            }
+          }
+        }
+      }
+    }
+    *r = L'\0';
+  }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_wchar2s(struct soap *soap, const wchar_t *s)
+{ register soap_wchar c;
+  register char *r, *t;
+  const wchar_t *q = s;
+  size_t n = 0;
+  while ((c = *q++))
+  { if (c > 0 && c < 0x80)
+      n++;
+    else
+      n += 6;
+  }
+  r = t = (char*)soap_malloc(soap, n + 1);
+  if (r)
+  { /* Convert wchar to UTF8 */
+    while ((c = *s++))
+    { if (c > 0 && c < 0x80)
+        *t++ = (char)c;
+      else
+      { if (c < 0x0800)
+          *t++ = (char)(0xC0 | ((c >> 6) & 0x1F));
+        else
+        { if (c < 0x010000)
+            *t++ = (char)(0xE0 | ((c >> 12) & 0x0F));
+          else
+          { if (c < 0x200000)
+              *t++ = (char)(0xF0 | ((c >> 18) & 0x07));
+            else
+            { if (c < 0x04000000)
+                *t++ = (char)(0xF8 | ((c >> 24) & 0x03));
+              else
+              { *t++ = (char)(0xFC | ((c >> 30) & 0x01));
+                *t++ = (char)(0x80 | ((c >> 24) & 0x3F));
+              }
+              *t++ = (char)(0x80 | ((c >> 18) & 0x3F));
+            }     
+            *t++ = (char)(0x80 | ((c >> 12) & 0x3F));
+          }
+          *t++ = (char)(0x80 | ((c >> 6) & 0x3F));
+        }
+        *t++ = (char)(0x80 | (c & 0x3F));
+      }
+    }
+    *t = '\0';
+  }
+  return r;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outstring(struct soap *soap, const char *tag, int id, char *const*p, const char *type, int n) 
+{ id = soap_element_id(soap, tag, id, *p, NULL, 0, type, n);
+  if (id < 0)
+    return soap->error;
+  if (!**p && (soap->mode & SOAP_C_NILSTRING))
+    return soap_element_null(soap, tag, id, type);
+  if (soap_element_begin_out(soap, tag, id, type)
+   || soap_string_out(soap, *p, 0)
+   || soap_element_end_out(soap, tag))
+    return soap->error;
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+char **
+SOAP_FMAC2
+soap_instring(struct soap *soap, const char *tag, char **p, const char *type, int t, int flag, long minlen, long maxlen)
+{ if (soap_element_begin_in(soap, tag, 1, NULL))
+  { if (!tag || *tag != '-' || soap->error != SOAP_NO_TAG)
+      return NULL;
+    soap->error = SOAP_OK;
+  }
+  if (!p)
+  { if (!(p = (char**)soap_malloc(soap, sizeof(char*))))
+      return NULL;
+  }
+  if (soap->body)
+  { *p = soap_string_in(soap, flag, minlen, maxlen);
+    if (!*p || !(char*)soap_id_enter(soap, soap->id, *p, t, sizeof(char*), 0, NULL, NULL, NULL))
+      return NULL;
+    if (!**p && tag && *tag == '-')
+    { soap->error = SOAP_NO_TAG;
+      return NULL;
+    }
+  }
+  else if (tag && *tag == '-')
+  { soap->error = SOAP_NO_TAG;
+    return NULL;
+  }
+  else if (soap->null)
+    *p = NULL;
+  else
+    *p = soap_strdup(soap, SOAP_STR_EOS);
+  if (*soap->href)
+    p = (char**)soap_id_lookup(soap, soap->href, (void**)p, t, sizeof(char**), 0);
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outwstring(struct soap *soap, const char *tag, int id, wchar_t *const*p, const char *type, int n) 
+{ id = soap_element_id(soap, tag, id, *p, NULL, 0, type, n);
+  if (id < 0)
+    return soap->error;
+  if (!**p && (soap->mode & SOAP_C_NILSTRING))
+    return soap_element_null(soap, tag, id, type);
+  if (soap_element_begin_out(soap, tag, id, type)
+   || soap_wstring_out(soap, *p, 0)
+   || soap_element_end_out(soap, tag))
+    return soap->error;
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_2
+SOAP_FMAC1
+wchar_t **
+SOAP_FMAC2
+soap_inwstring(struct soap *soap, const char *tag, wchar_t **p, const char *type, int t, long minlen, long maxlen)
+{ if (soap_element_begin_in(soap, tag, 1, NULL))
+  { if (!tag || *tag != '-' || soap->error != SOAP_NO_TAG)
+      return NULL;
+    soap->error = SOAP_OK;
+  }
+  if (!p)
+  { if (!(p = (wchar_t**)soap_malloc(soap, sizeof(wchar_t*))))
+      return NULL;
+  }
+  if (soap->body)
+  { *p = soap_wstring_in(soap, 1, minlen, maxlen);
+    if (!*p || !(wchar_t*)soap_id_enter(soap, soap->id, *p, t, sizeof(wchar_t*), 0, NULL, NULL, NULL))
+      return NULL;
+    if (!**p && tag && *tag == '-')
+    { soap->error = SOAP_NO_TAG;
+      return NULL;
+    }
+  }
+  else if (tag && *tag == '-')
+  { soap->error = SOAP_NO_TAG;
+    return NULL;
+  }
+  else if (soap->null)
+    *p = NULL;
+  else
+    *p = soap_wstrdup(soap, (wchar_t*)SOAP_STR_EOS);
+  if (*soap->href)
+    p = (wchar_t**)soap_id_lookup(soap, soap->href, (void**)p, t, sizeof(wchar_t**), 0);
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+time_t
+SOAP_FMAC2
+soap_timegm(struct tm *T)
+{
+#if defined(HAVE_TIMEGM)
+  return timegm(T);
+#else
+  time_t t, g, z;
+  struct tm tm;
+  t = mktime(T);
+  if (t == (time_t)-1)
+    return (time_t)-1;
+#ifdef HAVE_GMTIME_R
+  gmtime_r(&t, &tm);
+#else
+  tm = *gmtime(&t);
+#endif
+  tm.tm_isdst = 0;
+  g = mktime(&tm);
+  if (g == (time_t)-1)
+    return (time_t)-1;
+  z = g - t;
+  return t - z;
+#endif
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_dateTime2s(struct soap *soap, time_t n)
+{ struct tm T, *pT = &T;
+#if defined(HAVE_GMTIME_R)
+  if (gmtime_r(&n, pT))
+    strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%SZ", pT);
+#elif defined(HAVE_GMTIME)
+  if ((pT = gmtime(&n)))
+    strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%SZ", pT);
+#elif defined(HAVE_GETTIMEOFDAY)
+  struct timezone tz;
+  memset((void*)&tz, 0, sizeof(tz));
+#if defined(HAVE_LOCALTIME_R)
+  if (localtime_r(&n, pT))
+  { struct timeval tv;
+    gettimeofday(&tv, &tz);
+    strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S", pT);
+    sprintf(soap->tmpbuf + strlen(soap->tmpbuf), "%+03d:%02d", -tz.tz_minuteswest/60+(pT->tm_isdst!=0), abs(tz.tz_minuteswest)%60);
+  }
+#else
+  if ((pT = localtime(&n)))
+  { struct timeval tv;
+    gettimeofday(&tv, &tz);
+    strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S", pT);
+    sprintf(soap->tmpbuf + strlen(soap->tmpbuf), "%+03d:%02d", -tz.tz_minuteswest/60+(pT->tm_isdst!=0), abs(tz.tz_minuteswest)%60);
+  }
+#endif
+#elif defined(HAVE_FTIME)
+  struct timeb t;
+  memset((void*)&t, 0, sizeof(t));
+#if defined(HAVE_LOCALTIME_R)
+  if (localtime_r(&n, pT))
+  {
+#ifdef __BORLANDC__
+    ::ftime(&t);
+#else
+    ftime(&t);
+#endif
+    strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S", pT);
+    sprintf(soap->tmpbuf + strlen(soap->tmpbuf), "%+03d:%02d", -t.timezone/60+(pT->tm_isdst!=0), abs(t.timezone)%60);
+  }
+#else
+  if ((pT = localtime(&n)))
+  {
+#ifdef __BORLANDC__
+    ::ftime(&t);
+#else
+    ftime(&t);
+#endif
+    strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S", pT);
+    sprintf(soap->tmpbuf + strlen(soap->tmpbuf), "%+03d:%02d", -t.timezone/60+(pT->tm_isdst!=0), abs(t.timezone)%60);
+  }
+#endif
+#elif defined(HAVE_LOCALTIME_R)
+  if (localtime_r(&n, pT))
+    strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S", pT);
+#else
+  if ((pT = localtime(&n)))
+    strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S", pT);
+#endif
+  else
+    strcpy(soap->tmpbuf, "1969-12-31T23:59:59Z");
+  return soap->tmpbuf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outdateTime(struct soap *soap, const char *tag, int id, const time_t *p, const char *type, int n)
+{ if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, p, n), type)
+   || soap_string_out(soap, soap_dateTime2s(soap, *p), 0))
+    return soap->error;
+  return soap_element_end_out(soap, tag);
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_s2dateTime(struct soap *soap, const char *s, time_t *p)
+{ if (s)
+  { char zone[32];
+    struct tm T;
+    const char *t;
+    *zone = '\0';
+    memset((void*)&T, 0, sizeof(T));
+    if (strchr(s, '-'))
+      t = "%d-%d-%dT%d:%d:%d%31s";
+    else if (strchr(s, ':'))
+      t = "%4d%2d%2dT%d:%d:%d%31s";
+    else /* parse non-XSD-standard alternative ISO 8601 format */
+      t = "%4d%2d%2dT%2d%2d%2d%31s";
+    if (sscanf(s, t, &T.tm_year, &T.tm_mon, &T.tm_mday, &T.tm_hour, &T.tm_min, &T.tm_sec, zone) < 6)
+      return soap->error = SOAP_TYPE;
+    if (T.tm_year == 1)
+      T.tm_year = 70;
+    else
+      T.tm_year -= 1900;
+    T.tm_mon--;
+    if (*zone == '.')
+    { for (s = zone + 1; *s; s++)
+        if (*s < '0' || *s > '9')
+          break;
+    }
+    else
+      s = zone;
+    if (*s)
+    {
+#ifndef WITH_NOZONE
+      if (*s == '+' || *s == '-')
+      { int h = 0, m = 0;
+        if (s[3] == ':')
+        { /* +hh:mm */
+	  sscanf(s, "%d:%d", &h, &m);
+          if (h < 0)
+            m = -m;
+        }
+        else /* +hhmm */
+        { m = (int)atol(s);
+          h = m / 100;
+          m = m % 100;
+        }
+	T.tm_min -= m;
+	T.tm_hour -= h;
+	/* put hour and min in range */
+        T.tm_hour += T.tm_min / 60;
+        T.tm_min %= 60;
+        if (T.tm_min < 0)
+          T.tm_min += 60;
+        T.tm_mday += T.tm_hour / 24;
+        T.tm_hour %= 24;
+        if (T.tm_hour < 0)
+          T.tm_hour += 24;
+	/* note: day of the month may be out of range, timegm() handles it */
+      }
+#endif
+      *p = soap_timegm(&T);
+    }
+    else
+      *p = mktime(&T);
+  }
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+time_t *
+SOAP_FMAC2
+soap_indateTime(struct soap *soap, const char *tag, time_t *p, const char *type, int t)
+{ if (soap_element_begin_in(soap, tag, 0, NULL))
+    return NULL;
+  if (*soap->type
+   && soap_match_tag(soap, soap->type, type)
+   && soap_match_tag(soap, soap->type, ":dateTime"))
+  { soap->error = SOAP_TYPE;
+    soap_revert(soap);
+    return NULL;
+  }
+  p = (time_t*)soap_id_enter(soap, soap->id, p, t, sizeof(time_t), 0, NULL, NULL, NULL);
+  if (*soap->href)
+    p = (time_t*)soap_id_forward(soap, soap->href, p, 0, t, 0, sizeof(time_t), 0, NULL);
+  else if (p)
+  { if (soap_s2dateTime(soap, soap_value(soap), p))
+      return NULL;
+  }
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outliteral(struct soap *soap, const char *tag, char *const*p, const char *type)
+{ int i;
+  const char *t = NULL;
+  if (tag && *tag != '-')
+  { if (soap->local_namespaces && (t = strchr(tag, ':')))
+    { strncpy(soap->tmpbuf, tag, t-tag);
+      soap->tmpbuf[t-tag] = '\0';
+      for (i = 0; soap->local_namespaces[i].id; i++)
+        if (!strcmp(soap->tmpbuf, soap->local_namespaces[i].id))
+          break;
+      t++;
+      if (soap_element(soap, t, 0, type)
+       || soap_attribute(soap, "xmlns", soap->local_namespaces[i].ns ? soap->local_namespaces[i].ns : SOAP_STR_EOS)
+       || soap_element_start_end_out(soap, NULL))
+        return soap->error;
+    }
+    else
+    { t = tag;
+      if (soap_element_begin_out(soap, t, 0, type))
+        return soap->error;
+    }
+  }
+  if (p && *p)
+  { if (soap_send(soap, *p))
+      return soap->error;
+  }
+  if (t)
+    return soap_element_end_out(soap, t);
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+char **
+SOAP_FMAC2
+soap_inliteral(struct soap *soap, const char *tag, char **p)
+{ if (soap_element_begin_in(soap, tag, 1, NULL))
+  { if (soap->error != SOAP_NO_TAG || soap_unget(soap, soap_get(soap)) == SOAP_TT)
+      return NULL;
+    soap->error = SOAP_OK;
+  }
+  if (!p)
+  { if (!(p = (char**)soap_malloc(soap, sizeof(char*))))
+      return NULL;
+  }
+  if (soap->body || (tag && *tag == '-'))
+  { *p = soap_string_in(soap, 0, -1, -1);
+    if (!*p)
+      return NULL;
+    if (!**p && tag && *tag == '-')
+    { soap->error = SOAP_NO_TAG;
+      return NULL;
+    }
+  }
+  else if (soap->null)
+    *p = NULL;
+  else
+    *p = soap_strdup(soap, SOAP_STR_EOS);
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_outwliteral(struct soap *soap, const char *tag, wchar_t *const*p, const char *type)
+{ int i;
+  const char *t = NULL;
+  if (tag && *tag != '-')
+  { if (soap->local_namespaces && (t = strchr(tag, ':')))
+    { strncpy(soap->tmpbuf, tag, t-tag);
+      soap->tmpbuf[t-tag] = '\0';
+      for (i = 0; soap->local_namespaces[i].id; i++)
+        if (!strcmp(soap->tmpbuf, soap->local_namespaces[i].id))
+          break;
+      t++;
+      if (soap_element(soap, t, 0, type)
+       || soap_attribute(soap, "xmlns", soap->local_namespaces[i].ns ? soap->local_namespaces[i].ns : SOAP_STR_EOS)
+       || soap_element_start_end_out(soap, NULL))
+        return soap->error;
+    }
+    else
+    { t = tag;
+      if (soap_element_begin_out(soap, t, 0, type))
+        return soap->error;
+    }
+    if (soap_send(soap, soap->tmpbuf))
+      return soap->error;
+  }
+  if (p)
+  { wchar_t c;
+    const wchar_t *s = *p;
+    while ((c = *s++))
+    { if (soap_pututf8(soap, (unsigned long)c))
+        return soap->error;
+    }
+  }
+  if (t)
+    return soap_element_end_out(soap, t);
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_2
+SOAP_FMAC1
+wchar_t **
+SOAP_FMAC2
+soap_inwliteral(struct soap *soap, const char *tag, wchar_t **p)
+{ if (soap_element_begin_in(soap, tag, 1, NULL))
+  { if (soap->error != SOAP_NO_TAG || soap_unget(soap, soap_get(soap)) == SOAP_TT)
+      return NULL;
+    soap->error = SOAP_OK;
+  }
+  if (!p)
+  { if (!(p = (wchar_t**)soap_malloc(soap, sizeof(wchar_t*))))
+      return NULL;
+  }
+  if (soap->body)
+  { *p = soap_wstring_in(soap, 0, -1, -1);
+    if (!*p)
+      return NULL;
+    if (!**p && tag && *tag == '-')
+    { soap->error = SOAP_NO_TAG;
+      return NULL;
+    }
+  }
+  else if (tag && *tag == '-')
+  { soap->error = SOAP_NO_TAG;
+    return NULL;
+  }
+  else if (soap->null)
+    *p = NULL;
+  else
+    *p = soap_wstrdup(soap, (wchar_t*)SOAP_STR_EOS);
+  if (soap->body && soap_element_end_in(soap, tag))
+    return NULL;
+  return p;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+const char *
+SOAP_FMAC2
+soap_value(struct soap *soap)
+{ register size_t i;
+  register soap_wchar c = 0;
+  register char *s = soap->tmpbuf;
+  if (!soap->body)
+    return SOAP_STR_EOS;
+  do c = soap_get(soap);
+  while (soap_blank(c));
+  for (i = 0; i < sizeof(soap->tmpbuf) - 1; i++)
+  { if (c == SOAP_TT || (int)c == EOF)
+      break;
+    *s++ = (char)c;
+    c = soap_get(soap);
+  }
+  for (s--; i > 0; i--, s--)
+  { if (!soap_blank(*s))
+      break;
+  }
+  s[1] = '\0';
+  if ((int)c == EOF || c == SOAP_TT)
+    soap_unget(soap, c);
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Element content value='%s'\n", soap->tmpbuf));
+#ifdef WITH_DOM
+  if ((soap->mode & SOAP_XML_DOM) && soap->dom)
+    soap->dom->data = soap_strdup(soap, soap->tmpbuf);
+#endif
+  return soap->tmpbuf; /* return non-null pointer */
+}
+#endif
+
+/******************************************************************************/
+#if !defined(WITH_LEANER) || !defined(WITH_NOHTTP)
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_getline(struct soap *soap, char *s, int len)
+{ int i = len;
+  soap_wchar c = 0;
+  for (;;)
+  { while (--i > 0)
+    { c = soap_getchar(soap);
+      if (c == '\r' || c == '\n')
+        break;
+      if ((int)c == EOF)
+        return soap->error = SOAP_EOF;
+      *s++ = (char)c;
+    }
+    if (c != '\n')
+      c = soap_getchar(soap); /* got \r or something else, now get \n */
+    if (c == '\n')
+    { *s = '\0';
+      if (i+1 == len) /* empty line: end of HTTP/MIME header */
+        break;
+      c = soap_get0(soap);
+      if (c != ' ' && c != '\t') /* HTTP line continuation? */
+        break;
+    }
+    else if ((int)c == EOF)
+      return soap->error = SOAP_EOF;
+  }
+  if (i < 0)
+    return soap->error = SOAP_HDR;
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+static size_t
+soap_count_attachments(struct soap *soap)
+{ 
+#ifndef WITH_LEANER
+  register struct soap_multipart *content;
+  register size_t count = soap->count;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Calculating the message size with attachments, current count=%lu\n", (unsigned long)count));
+  if ((soap->mode & SOAP_ENC_DIME) && !(soap->mode & SOAP_ENC_MTOM))
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Calculating the size of DIME attachments\n"));
+    for (content = soap->dime.first; content; content = content->next)
+    { count += 12 + ((content->size+3)&(~3));
+      if (content->id)
+        count += ((strlen(content->id)+3)&(~3));
+      if (content->type)
+        count += ((strlen(content->type)+3)&(~3));
+      if (content->options)
+        count += ((((unsigned char)content->options[2] << 8) | ((unsigned char)content->options[3]))+7)&(~3);
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Size of DIME attachment content is %lu bytes\n", (unsigned long)content->size));
+    }
+  }
+  if ((soap->mode & SOAP_ENC_MIME) && soap->mime.boundary)
+  { register size_t n = strlen(soap->mime.boundary);
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Calculating the size of MIME attachments\n"));
+    for (content = soap->mime.first; content; content = content->next)
+    { register const char *s;
+      /* count \r\n--boundary\r\n */
+      count += 6 + n;
+      /* count Content-Type: ...\r\n */
+      if (content->type)
+        count += 16 + strlen(content->type);
+      /* count Content-Transfer-Encoding: ...\r\n */
+      s = soap_code_str(mime_codes, content->encoding);
+      if (s)
+        count += 29 + strlen(s);
+      /* count Content-ID: ...\r\n */
+      if (content->id)
+        count += 14 + strlen(content->id);
+      /* count Content-Location: ...\r\n */
+      if (content->location)
+        count += 20 + strlen(content->location);
+      /* count Content-Description: ...\r\n */
+      if (content->description)
+        count += 23 + strlen(content->description);
+      /* count \r\n...content */
+      count += 2 + content->size;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Size of MIME attachment content is %lu bytes\n", (unsigned long)content->size));
+    }
+    /* count \r\n--boundary-- */
+    count += 6 + n;
+  }
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "New count is %lu bytes\n", (unsigned long)count));
+  return count;
+#else
+  return soap->count;
+#endif
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+static int
+soap_putdimefield(struct soap *soap, const char *s, size_t n)
+{ if (soap_send_raw(soap, s, n))
+    return soap->error;
+  return soap_send_raw(soap, SOAP_STR_PADDING, -(long)n&3);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_dime_option(struct soap *soap, unsigned short optype, const char *option)
+{ size_t n;
+  char *s = NULL;
+  if (option)
+  { n = strlen(option);
+    s = (char*)soap_malloc(soap, n + 5);
+    if (s)
+    { s[0] = (char)(optype >> 8);
+      s[1] = (char)(optype & 0xFF);
+      s[2] = (char)(n >> 8);
+      s[3] = (char)(n & 0xFF);
+      strcpy(s + 4, option);
+    }
+  }
+  return s;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_putdimehdr(struct soap *soap)
+{ unsigned char tmp[12];
+  size_t optlen = 0, idlen = 0, typelen = 0;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Put DIME header id='%s'\n", soap->dime.id?soap->dime.id:SOAP_STR_EOS));
+  if (soap->dime.options)
+    optlen = (((unsigned char)soap->dime.options[2] << 8) | ((unsigned char)soap->dime.options[3])) + 4;
+  if (soap->dime.id)
+  { idlen = strlen(soap->dime.id);
+    if (idlen > 0x0000FFFF)
+      idlen = 0x0000FFFF;
+  }
+  if (soap->dime.type)
+  { typelen = strlen(soap->dime.type);
+    if (typelen > 0x0000FFFF)
+      typelen = 0x0000FFFF;
+  }
+  tmp[0] = SOAP_DIME_VERSION | (soap->dime.flags & 0x7);
+  tmp[1] = soap->dime.flags & 0xF0;
+  tmp[2] = (char)(optlen >> 8);
+  tmp[3] = (char)(optlen & 0xFF);
+  tmp[4] = (char)(idlen >> 8);
+  tmp[5] = (char)(idlen & 0xFF);
+  tmp[6] = (char)(typelen >> 8);
+  tmp[7] = (char)(typelen & 0xFF);
+  tmp[8] = (char)(soap->dime.size >> 24);
+  tmp[9] = (char)((soap->dime.size >> 16) & 0xFF);
+  tmp[10] = (char)((soap->dime.size >> 8) & 0xFF);
+  tmp[11] = (char)(soap->dime.size & 0xFF);
+  if (soap_send_raw(soap, (char*)tmp, 12)
+   || soap_putdimefield(soap, soap->dime.options, optlen)
+   || soap_putdimefield(soap, soap->dime.id, idlen)
+   || soap_putdimefield(soap, soap->dime.type, typelen))
+    return soap->error;
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_putdime(struct soap *soap)
+{ struct soap_multipart *content;
+  if (!(soap->mode & SOAP_ENC_DIME))
+    return SOAP_OK;
+  for (content = soap->dime.first; content; content = content->next)
+  { void *handle;
+    soap->dime.size = content->size;
+    soap->dime.id = content->id;
+    soap->dime.type = content->type;
+    soap->dime.options = content->options;
+    soap->dime.flags = SOAP_DIME_VERSION | SOAP_DIME_MEDIA;
+    if (soap->fdimereadopen && ((handle = soap->fdimereadopen(soap, (void*)content->ptr, content->id, content->type, content->options)) || soap->error))
+    { size_t size = content->size;
+      if (!handle)
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "fdimereadopen failed\n"));
+        return soap->error;
+      }
+      if (!size && ((soap->mode & SOAP_ENC_XML) || (soap->mode & SOAP_IO) == SOAP_IO_CHUNK || (soap->mode & SOAP_IO) == SOAP_IO_STORE))
+      { size_t chunksize = sizeof(soap->tmpbuf);
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Chunked streaming DIME\n"));
+        do 
+        { size = soap->fdimeread(soap, handle, soap->tmpbuf, chunksize);
+          DBGLOG(TEST, SOAP_MESSAGE(fdebug, "fdimeread returned %lu bytes\n", (unsigned long)size));
+          if (size < chunksize)
+          { soap->dime.flags &= ~SOAP_DIME_CF;
+            if (!content->next)
+              soap->dime.flags |= SOAP_DIME_ME;
+          }
+          else
+            soap->dime.flags |= SOAP_DIME_CF;
+          soap->dime.size = size;
+          if (soap_putdimehdr(soap)
+           || soap_putdimefield(soap, soap->tmpbuf, size))
+            break;
+          if (soap->dime.id)
+          { soap->dime.flags &= ~(SOAP_DIME_MB | SOAP_DIME_MEDIA);
+            soap->dime.id = NULL;
+            soap->dime.type = NULL;
+            soap->dime.options = NULL;
+          }  
+        } while (size >= chunksize);
+      }
+      else
+      { if (!content->next)
+          soap->dime.flags |= SOAP_DIME_ME;
+        if (soap_putdimehdr(soap))
+          return soap->error;
+        do
+        { size_t bufsize;
+          if (size < sizeof(soap->tmpbuf))
+            bufsize = size;
+          else
+            bufsize = sizeof(soap->tmpbuf);
+          if (!(bufsize = soap->fdimeread(soap, handle, soap->tmpbuf, bufsize)))
+          { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "fdimeread failed: insufficient data (%lu bytes remaining from %lu bytes)\n", (unsigned long)size, (unsigned long)content->size));
+            soap->error = SOAP_EOF;
+            break;
+          }
+          if (soap_send_raw(soap, soap->tmpbuf, bufsize))
+            break;
+          size -= bufsize;
+        } while (size);
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "fdimereadclose\n"));
+        soap_send_raw(soap, SOAP_STR_PADDING, -(long)soap->dime.size&3);
+      }
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "fdimereadclose\n"));
+      if (soap->fdimereadclose)
+        soap->fdimereadclose(soap, handle);
+    }
+    else
+    { if (!content->next)
+        soap->dime.flags |= SOAP_DIME_ME;
+      if (soap_putdimehdr(soap)
+       || soap_putdimefield(soap, (char*)content->ptr, content->size))
+        return soap->error;
+    }
+  }
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+static char *
+soap_getdimefield(struct soap *soap, size_t n)
+{ register soap_wchar c;
+  register size_t i;
+  register char *s;
+  register char *p = NULL;
+  if (n)
+  { p = (char*)soap_malloc(soap, n + 1);
+    if (p)
+    { s = p;
+      for (i = n; i > 0; i--)
+      { if ((int)(c = soap_get1(soap)) == EOF)
+        { soap->error = SOAP_EOF;
+          return NULL;
+        }
+        *s++ = (char)c;
+      }
+      *s = '\0';
+      if ((soap->error = soap_move(soap, -(long)n&3)))
+        return NULL;
+    }
+    else
+      soap->error = SOAP_EOM;
+  }
+  return p;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_getdimehdr(struct soap *soap)
+{ register soap_wchar c;
+  register char *s;
+  register int i;
+  unsigned char tmp[12];
+  size_t optlen, idlen, typelen;
+  if (!(soap->mode & SOAP_ENC_DIME))
+    return soap->error = SOAP_DIME_END;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Get DIME header\n"));
+  if (soap->dime.buflen || soap->dime.chunksize)
+  { if (soap_move(soap, (long)(soap->dime.size - soap_tell(soap))))
+      return soap->error = SOAP_EOF;
+    soap_unget(soap, soap_getchar(soap)); /* skip padding and get hdr */
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "... From chunked\n"));
+    return SOAP_OK;
+  }
+  s = (char*)tmp;
+  for (i = 12; i > 0; i--)
+  { if ((int)(c = soap_getchar(soap)) == EOF)
+      return soap->error = SOAP_EOF;
+    *s++ = (char)c;
+  }
+  if ((tmp[0] & 0xF8) != SOAP_DIME_VERSION)
+    return soap->error = SOAP_DIME_MISMATCH;
+  soap->dime.flags = (tmp[0] & 0x7) | (tmp[1] & 0xF0);
+  optlen = (tmp[2] << 8) | tmp[3];
+  idlen = (tmp[4] << 8) | tmp[5];
+  typelen = (tmp[6] << 8) | tmp[7];
+  soap->dime.size = (tmp[8] << 24) | (tmp[9] << 16) | (tmp[10] << 8) | tmp[11];
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "DIME size=%lu flags=0x%X\n", (unsigned long)soap->dime.size, soap->dime.flags));
+  if (!(soap->dime.options = soap_getdimefield(soap, optlen)) && soap->error)
+    return soap->error;
+  if (!(soap->dime.id = soap_getdimefield(soap, idlen)) && soap->error)
+    return soap->error;
+  if (!(soap->dime.type = soap_getdimefield(soap, typelen)) && soap->error)
+    return soap->error;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "DIME id=%s, type=%s, options=%s\n", soap->dime.id?soap->dime.id:SOAP_STR_EOS, soap->dime.type?soap->dime.type:"", soap->dime.options?soap->dime.options+4:SOAP_STR_EOS));
+  if (soap->dime.flags & SOAP_DIME_ME)
+    soap->mode &= ~SOAP_ENC_DIME;
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_getdime(struct soap *soap)
+{ while (soap->dime.flags & SOAP_DIME_CF)
+  { if (soap_getdimehdr(soap))
+      return soap->error;
+    if (soap_move(soap, (long)soap->dime.size))
+      return soap->error = SOAP_EOF;
+  }
+  if (soap_move(soap, (long)(((soap->dime.size+3)&(~3))-soap_tell(soap))))
+    return soap->error = SOAP_EOF;
+  for (;;)
+  { register struct soap_multipart *content;
+    if (soap_getdimehdr(soap))
+      break;
+    if (soap->fdimewriteopen && ((soap->dime.ptr = (char*)soap->fdimewriteopen(soap, soap->dime.id, soap->dime.type, soap->dime.options)) || soap->error))
+    { const char *id, *type, *options;
+      size_t size, n;
+      if (!soap->dime.ptr)
+        return soap->error;
+      id = soap->dime.id;
+      type = soap->dime.type;
+      options = soap->dime.options;
+      for (;;)
+      { size = soap->dime.size;
+        for (;;)
+        { n = soap->buflen - soap->bufidx;
+          if (size < n)
+            n = size;
+          if ((soap->error = soap->fdimewrite(soap, (void*)soap->dime.ptr, soap->buf + soap->bufidx, n)))
+            break;
+          size -= n;
+          if (!size)
+          { soap->bufidx += n;
+            break;
+          }
+          if (soap_recv(soap))
+          { soap->error = SOAP_EOF;
+            goto end;
+          }
+        }
+        if (soap_move(soap, -(long)soap->dime.size&3))
+        { soap->error = SOAP_EOF;
+          break;
+        }
+        if (!(soap->dime.flags & SOAP_DIME_CF))
+          break;
+        if (soap_getdimehdr(soap))
+          break;
+      }
+end:
+      if (soap->fdimewriteclose)
+        soap->fdimewriteclose(soap, (void*)soap->dime.ptr);
+      soap->dime.size = 0;
+      soap->dime.id = id;
+      soap->dime.type = type;
+      soap->dime.options = options;
+    }
+    else if (soap->dime.flags & SOAP_DIME_CF)
+    { const char *id, *type, *options;
+      id = soap->dime.id;
+      type = soap->dime.type;
+      options = soap->dime.options;
+      if (soap_new_block(soap) == NULL)
+        return SOAP_EOM;
+      for (;;)
+      { register soap_wchar c;
+        register size_t i;
+        register char *s;
+        s = (char*)soap_push_block(soap, NULL, soap->dime.size);
+        if (!s)
+          return soap->error = SOAP_EOM;
+        for (i = soap->dime.size; i > 0; i--)
+        { if ((int)(c = soap_get1(soap)) == EOF)
+            return soap->error = SOAP_EOF;
+          *s++ = (char)c;
+        }
+        if (soap_move(soap, -(long)soap->dime.size&3))
+          return soap->error = SOAP_EOF;
+        if (!(soap->dime.flags & SOAP_DIME_CF))
+          break;
+        if (soap_getdimehdr(soap))
+          return soap->error;
+      }
+      soap->dime.size = soap->blist->size++; /* allocate one more for '\0' */
+      if (!(soap->dime.ptr = soap_save_block(soap, NULL, NULL, 0)))
+        return soap->error;
+      soap->dime.ptr[soap->dime.size] = '\0'; /* force 0-terminated */
+      soap->dime.id = id;
+      soap->dime.type = type;
+      soap->dime.options = options;
+    }
+    else
+      soap->dime.ptr = soap_getdimefield(soap, soap->dime.size);
+    content = soap_new_multipart(soap, &soap->dime.first, &soap->dime.last, soap->dime.ptr, soap->dime.size);
+    if (!content)
+      return soap->error = SOAP_EOM;
+    content->id = soap->dime.id;
+    content->type = soap->dime.type;
+    content->options = soap->dime.options;
+    if (soap->error)
+      return soap->error;
+    soap_resolve_attachment(soap, content);
+  }
+  if (soap->error != SOAP_DIME_END)
+    return soap->error;
+  return soap->error = SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_getmimehdr(struct soap *soap)
+{ struct soap_multipart *content;
+  do
+  { if (soap_getline(soap, soap->msgbuf, sizeof(soap->msgbuf)))
+      return soap->error;
+  }
+  while (!*soap->msgbuf);
+  if (soap->msgbuf[0] == '-' && soap->msgbuf[1] == '-')
+  { char *s = soap->msgbuf + strlen(soap->msgbuf) - 1;
+    /* remove white space */
+    while (soap_blank(*s))
+      s--;
+    s[1] = '\0';
+    if (soap->mime.boundary)
+    { if (strcmp(soap->msgbuf + 2, soap->mime.boundary))
+        return soap->error = SOAP_MIME_ERROR;
+    }
+    else
+      soap->mime.boundary = soap_strdup(soap, soap->msgbuf + 2);
+    if (soap_getline(soap, soap->msgbuf, sizeof(soap->msgbuf)))
+      return soap->error;
+  }
+  if (soap_set_mime_attachment(soap, NULL, 0, SOAP_MIME_NONE, NULL, NULL, NULL, NULL))
+    return soap->error = SOAP_EOM;
+  content = soap->mime.last;
+  for (;;)
+  { register char *key = soap->msgbuf;
+    register char *val;
+    if (!*key)
+      break;
+    DBGLOG(TEST,SOAP_MESSAGE(fdebug, "MIME header: %s\n", key));
+    val = strchr(soap->msgbuf, ':');
+    if (val)
+    { *val = '\0';
+      do val++;
+      while (*val && *val <= 32);
+      if (!soap_tag_cmp(key, "Content-ID"))
+        content->id = soap_strdup(soap, val);
+      else if (!soap_tag_cmp(key, "Content-Location"))
+        content->location = soap_strdup(soap, val);
+      else if (!soap_tag_cmp(key, "Content-Disposition"))
+        content->id = soap_strdup(soap, soap_get_header_attribute(soap, val, "name"));
+      else if (!soap_tag_cmp(key, "Content-Type"))
+        content->type = soap_strdup(soap, val);
+      else if (!soap_tag_cmp(key, "Content-Description"))
+        content->description = soap_strdup(soap, val);
+      else if (!soap_tag_cmp(key, "Content-Transfer-Encoding"))
+        content->encoding = (enum soap_mime_encoding)soap_code_int(mime_codes, val, (long)SOAP_MIME_NONE);
+    }
+    if (soap_getline(soap, key, sizeof(soap->msgbuf)))
+      return soap->error;
+  }
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_getmime(struct soap *soap)
+{ while (soap_get_mime_attachment(soap, NULL))
+    ;
+  return soap->error;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_post_check_mime_attachments(struct soap *soap)
+{ soap->imode |= SOAP_MIME_POSTCHECK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_check_mime_attachments(struct soap *soap)
+{ if (soap->mode & SOAP_MIME_POSTCHECK)
+    return soap_get_mime_attachment(soap, NULL) != NULL;
+  return 0;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+struct soap_multipart *
+SOAP_FMAC2
+soap_get_mime_attachment(struct soap *soap, void *handle)
+{ register soap_wchar c = 0;
+  register size_t i, m = 0;
+  register char *s, *t = NULL;
+  register struct soap_multipart *content;
+  register short flag = 0;
+  if (!(soap->mode & SOAP_ENC_MIME))
+    return NULL;
+  content = soap->mime.last;
+  if (!content)
+  { if (soap_getmimehdr(soap))
+      return NULL;
+    content = soap->mime.last;
+  }
+  else if (content != soap->mime.first)
+  { if (soap->fmimewriteopen && ((content->ptr = (char*)soap->fmimewriteopen(soap, (void*)handle, content->id, content->type, content->description, content->encoding)) || soap->error))
+    { if (!content->ptr)
+        return NULL;
+    }
+  }
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Parsing MIME content id=%s type=%s\n", content->id?content->id:SOAP_STR_EOS, content->type?content->type:SOAP_STR_EOS));
+  if (!content->ptr && soap_new_block(soap) == NULL)
+  { soap->error = SOAP_EOM;
+    return NULL;
+  }
+  for (;;)
+  { if (content->ptr)
+      s = soap->tmpbuf;
+    else if (!(s = (char*)soap_push_block(soap, NULL, sizeof(soap->tmpbuf))))
+    { soap->error = SOAP_EOM;
+      return NULL;
+    }
+    for (i = 0; i < sizeof(soap->tmpbuf); i++)
+    { if (m > 0)
+      { *s++ = *t++;
+        m--;
+      }
+      else
+      { if (!flag)
+        { c = soap_get1(soap);
+          if ((int)c == EOF)
+          { soap->error = SOAP_EOF;
+            return NULL;
+          }
+        }
+        if (flag || c == '\r')
+        { t = soap->msgbuf;
+          memset(t, 0, sizeof(soap->msgbuf));
+          strcpy(t, "\n--");
+          if (soap->mime.boundary)
+            strncat(t, soap->mime.boundary, sizeof(soap->msgbuf)-4);
+          do c = soap_getchar(soap);
+          while (c == *t++);
+          if ((int)c == EOF)
+          { soap->error = SOAP_EOF;
+            return NULL;
+          }
+          if (!*--t)
+            goto end;
+          *t = (char)c;
+          flag = (c == '\r');
+          m = t - soap->msgbuf + 1 - flag;
+          t = soap->msgbuf;
+          c = '\r';
+        }
+        *s++ = (char)c;
+      }
+    }
+    if (content->ptr && soap->fmimewrite)
+    { if ((soap->error = soap->fmimewrite(soap, (void*)content->ptr, soap->tmpbuf, i)))
+        break;
+    }
+  }
+end:
+  *s = '\0'; /* force 0-terminated */
+  if (content->ptr)
+  { if (!soap->error && soap->fmimewrite)
+      soap->error = soap->fmimewrite(soap, (void*)content->ptr, soap->tmpbuf, i);
+    if (soap->fmimewriteclose)
+      soap->fmimewriteclose(soap, (void*)content->ptr);
+    if (soap->error)
+      return NULL;
+  }
+  else
+  { content->size = soap_size_block(soap, NULL, i+1)-1;
+    content->ptr = soap_save_block(soap, NULL, NULL, 0);
+  }
+  soap_resolve_attachment(soap, content);
+  if (c == '-' && soap_getchar(soap) == '-')
+  { soap->mode &= ~SOAP_ENC_MIME;
+    if ((soap->mode & SOAP_MIME_POSTCHECK) && soap_end_recv(soap))
+      return NULL;
+  }
+  else
+  { while (c != '\r' && (int)c != EOF && soap_blank(c))
+      c = soap_getchar(soap);
+    if (c != '\r' || soap_getchar(soap) != '\n')
+    { soap->error = SOAP_MIME_ERROR;
+      return NULL;
+    }
+    if (soap_getmimehdr(soap))
+      return NULL;
+  }
+  return content;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_match_cid(struct soap *soap, const char *s, const char *t)
+{ register size_t n;
+  if (!s)
+    return 1;
+  if (!strcmp(s, t))
+    return 0;
+  if (!strncmp(s, "cid:", 4))
+    s += 4;
+  n = strlen(t);
+  if (*t == '<')
+  { t++;
+    n -= 2;
+  }
+  if (!strncmp(s, t, n) && !s[n])
+    return 0;
+  soap_decode(soap->tmpbuf, sizeof(soap->tmpbuf), s, SOAP_STR_EOS);
+  if (!strncmp(soap->tmpbuf, t, n) && !soap->tmpbuf[n])
+    return 0;
+  return 1;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+static void
+soap_resolve_attachment(struct soap *soap, struct soap_multipart *content)
+{ if (content->id)
+  { register struct soap_xlist **xp = &soap->xlist;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resolving attachment data for id=%s\n", content->id));
+    while (*xp)
+    { register struct soap_xlist *xq = *xp;
+      if (!soap_match_cid(soap, xq->id, content->id))
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Found matching attachment %s for content id=%s\n", xq->id, content->id));
+        *xp = xq->next;
+        *xq->ptr = (unsigned char*)content->ptr;
+        *xq->size = (int)content->size;
+        *xq->type = (char*)content->type;
+        if (content->options)
+          *xq->options = (char*)content->options;
+        else
+          *xq->options = (char*)content->description;
+        SOAP_FREE(soap, xq);
+      }
+      else
+        xp = &(*xp)->next;
+    }
+  }
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_putmimehdr(struct soap *soap, struct soap_multipart *content)
+{ const char *s;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "MIME attachment type=%s\n", content->type?content->type:SOAP_STR_EOS));
+  if (soap_send3(soap, "\r\n--", soap->mime.boundary, "\r\n"))
+    return soap->error;
+  if (content->type && soap_send3(soap, "Content-Type: ", content->type, "\r\n"))
+    return soap->error;
+  s = soap_code_str(mime_codes, content->encoding);
+  if (s && soap_send3(soap, "Content-Transfer-Encoding: ", s, "\r\n"))
+    return soap->error;
+  if (content->id && soap_send3(soap, "Content-ID: ", content->id, "\r\n"))
+    return soap->error;
+  if (content->location && soap_send3(soap, "Content-Location: ", content->location, "\r\n"))
+    return soap->error;
+  if (content->description && soap_send3(soap, "Content-Description: ", content->description, "\r\n"))
+    return soap->error;
+  return soap_send_raw(soap, "\r\n", 2);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_putmime(struct soap *soap)
+{ struct soap_multipart *content;
+  if (!(soap->mode & SOAP_ENC_MIME) || !soap->mime.boundary)
+    return SOAP_OK;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Sending MIME attachments\n"));
+  for (content = soap->mime.first; content; content = content->next)
+  { void *handle;
+    if (soap->fmimereadopen && ((handle = soap->fmimereadopen(soap, (void*)content->ptr, content->id, content->type, content->description)) || soap->error))
+    { size_t size = content->size;
+      if (!handle)
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "fmimereadopen failed\n"));
+        return soap->error;
+      }
+      if (soap_putmimehdr(soap, content))
+        return soap->error;
+      if (!size)
+      { if ((soap->mode & SOAP_ENC_XML) || (soap->mode & SOAP_IO) == SOAP_IO_CHUNK || (soap->mode & SOAP_IO) == SOAP_IO_STORE)
+        { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Chunked streaming MIME\n"));
+          do 
+          { size = soap->fmimeread(soap, handle, soap->tmpbuf, sizeof(soap->tmpbuf));
+            DBGLOG(TEST, SOAP_MESSAGE(fdebug, "fmimeread returned %lu bytes\n", (unsigned long)size));
+            if (soap_send_raw(soap, soap->tmpbuf, size))
+              break;
+          } while (size); 
+        }
+        else
+        { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error: cannot chunk streaming MIME (no HTTP chunking)\n"));
+        }
+      }
+      else
+      { do
+        { size_t bufsize;
+          if (size < sizeof(soap->tmpbuf))
+            bufsize = size;
+          else
+            bufsize = sizeof(soap->tmpbuf);
+          if (!(bufsize = soap->fmimeread(soap, handle, soap->tmpbuf, bufsize)))
+          { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "fmimeread failed: insufficient data (%lu bytes remaining from %lu bytes)\n", (unsigned long)size, (unsigned long)content->size));
+            soap->error = SOAP_EOF;
+            break;
+          }
+          if (soap_send_raw(soap, soap->tmpbuf, bufsize))
+            break;
+          size -= bufsize;
+        } while (size);
+      }
+      if (soap->fmimereadclose)
+        soap->fmimereadclose(soap, handle);
+    }
+    else
+    { if (soap_putmimehdr(soap, content)
+       || soap_send_raw(soap, content->ptr, content->size))
+        return soap->error;
+    }
+  }
+  return soap_send3(soap, "\r\n--", soap->mime.boundary, "--");
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_set_dime(struct soap *soap)
+{ soap->omode |= SOAP_ENC_DIME;
+  soap->dime.first = NULL;
+  soap->dime.last = NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_set_mime(struct soap *soap, const char *boundary, const char *start)
+{ soap->omode |= SOAP_ENC_MIME;
+  soap->mime.first = NULL;
+  soap->mime.last = NULL;
+  soap->mime.boundary = soap_strdup(soap, boundary);
+  soap->mime.start = soap_strdup(soap, start);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_clr_dime(struct soap *soap)
+{ soap->omode &= ~SOAP_ENC_DIME;
+  soap->dime.first = NULL;
+  soap->dime.last = NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_clr_mime(struct soap *soap)
+{ soap->omode &= ~SOAP_ENC_MIME;
+  soap->mime.first = NULL;
+  soap->mime.last = NULL;
+  soap->mime.boundary = NULL;
+  soap->mime.start = NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+static struct soap_multipart*
+soap_new_multipart(struct soap *soap, struct soap_multipart **first, struct soap_multipart **last, char *ptr, size_t size)
+{ struct soap_multipart *content;
+  content = (struct soap_multipart*)soap_malloc(soap, sizeof(struct soap_multipart));
+  if (content)
+  { content->next = NULL;
+    content->ptr = ptr;
+    content->size = size;
+    content->id = NULL;
+    content->type = NULL;
+    content->options = NULL;
+    content->encoding = SOAP_MIME_NONE;
+    content->location = NULL;
+    content->description = NULL;
+    if (!*first)
+      *first = content;
+    if (*last)
+      (*last)->next = content;
+    *last = content;
+  }
+  return content;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_set_dime_attachment(struct soap *soap, char *ptr, size_t size, const char *type, const char *id, unsigned short optype, const char *option)
+{ struct soap_multipart *content = soap_new_multipart(soap, &soap->dime.first, &soap->dime.last, ptr, size);
+  if (!content)
+    return SOAP_EOM;
+  content->id = soap_strdup(soap, id);
+  content->type = soap_strdup(soap, type);
+  content->options = soap_dime_option(soap, optype, option);
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_set_mime_attachment(struct soap *soap, char *ptr, size_t size, enum soap_mime_encoding encoding, const char *type, const char *id, const char *location, const char *description)
+{ struct soap_multipart *content = soap_new_multipart(soap, &soap->mime.first, &soap->mime.last, ptr, size);
+  if (!content)
+    return SOAP_EOM;
+  content->id = soap_strdup(soap, id);
+  content->type = soap_strdup(soap, type);
+  content->encoding = encoding;
+  content->location = soap_strdup(soap, location);
+  content->description = soap_strdup(soap, description);
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+SOAP_FMAC1
+struct soap_multipart*
+SOAP_FMAC2
+soap_next_multipart(struct soap_multipart *content)
+{ if (content)
+    return content->next;
+  return NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+static void
+soap_select_mime_boundary(struct soap *soap)
+{ while (!soap->mime.boundary || soap_valid_mime_boundary(soap))
+  { register char *s = soap->mime.boundary;
+    register size_t n = 0;
+    if (s)
+      n = strlen(s);
+    if (n < 16)
+    { n = 64;
+      s = soap->mime.boundary = (char*)soap_malloc(soap, n + 1);
+      if (!s)
+        return;
+    }
+    strcpy(s, "==");
+    s += 2;
+    n -= 4;
+    while (n)
+    { *s++ = soap_base64o[soap_random & 0x3F];
+      n--;
+    }
+    strcpy(s, "==");
+  }
+  if (!soap->mime.start)
+    soap->mime.start = "<SOAP-ENV:Envelope>";
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEANER
+#ifndef PALM_1
+static int
+soap_valid_mime_boundary(struct soap *soap)
+{ register struct soap_multipart *content;
+  register size_t k;
+  if (soap->fmimeread)
+    return SOAP_OK;
+  k = strlen(soap->mime.boundary);
+  for (content = soap->mime.first; content; content = content->next)
+  { if (content->ptr && content->size >= k)
+    { register const char *p = (const char*)content->ptr; 
+      register size_t i;
+      for (i = 0; i < content->size - k; i++, p++)
+      { if (!strncmp(p, soap->mime.boundary, k))
+          return SOAP_ERR;
+      }
+    }
+  }
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************\
+ *
+ *	HTTP cookie handling
+ *
+\******************************************************************************/
+
+#ifdef WITH_COOKIES
+/******************************************************************************/
+SOAP_FMAC1
+size_t
+SOAP_FMAC2
+soap_encode_cookie(const char *s, char *t, size_t len)
+{ register int c;
+  register size_t n = len;
+  while ((c = *s++) && --n > 0)
+  { if (c > ' ' && c < 128 && !strchr("()<>@,;:\\\"/[]?={}", c))
+      *t++ = c;
+    else if (n > 2)
+    { *t++ = '%';
+      *t++ = (c >> 4) + (c > 159 ? '7' : '0');
+      c &= 0xF;
+      *t++ = c + (c > 9 ? '7' : '0');
+      n -= 2;
+    }
+    else
+      break;
+  }
+  *t = '\0';
+  return len - n;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+struct soap_cookie*
+SOAP_FMAC2
+soap_cookie(struct soap *soap, const char *name, const char *domain, const char *path)
+{ struct soap_cookie *p;
+  size_t n;
+  if (!domain)
+    domain = soap->cookie_domain;
+  if (!path)
+    path = soap->cookie_path;
+  if (!path)
+    path = SOAP_STR_EOS;
+  else if (*path == '/')
+    path++;
+  n = strlen(path);
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Search cookie %s domain=%s path=%s\n", name, domain?domain:"(null)", path?path:"(null)"));
+  for (p = soap->cookies; p; p = p->next)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Cookie in database: %s=%s domain=%s path=%s env=%hd\n", p->name, p->value?p->value:"(null)", p->domain?p->domain:"(null)", p->path?p->path:"(null)", p->env));
+    if (!strcmp(p->name, name)
+     && p->domain
+     && p->path
+     && !strcmp(p->domain, domain)
+     && !strncmp(p->path, path, n))
+      break;
+  }
+  return p;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+struct soap_cookie*
+SOAP_FMAC2
+soap_set_cookie(struct soap *soap, const char *name, const char *value, const char *domain, const char *path)
+{ struct soap_cookie **p, *q;
+  int n;
+  if (!domain)
+    domain = soap->cookie_domain;
+  if (!path)
+    path = soap->cookie_path;
+  if (!path)
+    path = SOAP_STR_EOS;
+  else if (*path == '/')
+    path++;
+  q = soap_cookie(soap, name, domain, path);
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Set %scookie: %s=%s domain=%s path=%s\n", q ? SOAP_STR_EOS : "new ", name, value?value:"(null)", domain?domain:"(null)", path?path:"(null)"));
+  if (!q)
+  { if ((q = (struct soap_cookie*)SOAP_MALLOC(soap, sizeof(struct soap_cookie))))
+    { if ((q->name = (char*)SOAP_MALLOC(soap, strlen(name)+1)))
+        strcpy(q->name, name);
+      q->value = NULL;
+      q->domain = NULL;
+      q->path = NULL;
+      q->expire = 0;
+      q->maxage = -1;
+      q->version = 1;
+      q->secure = 0;
+      q->modified = 0;
+      for (p = &soap->cookies, n = soap->cookie_max; *p && n; p = &(*p)->next, n--)
+        if (!strcmp((*p)->name, name) && (*p)->path && path && strcmp((*p)->path, path) < 0)
+          break;
+      if (n)
+      { q->next = *p;
+        *p = q;
+      }
+      else
+      { SOAP_FREE(soap, q->name);
+        SOAP_FREE(soap, q);
+        q = NULL;
+      }
+    }
+  }
+  else
+    q->modified = 1;
+  if (q)
+  { if (q->value)
+    { SOAP_FREE(soap, q->value);
+      q->value = NULL;
+    }
+    if (q->domain)
+    { SOAP_FREE(soap, q->domain);
+      q->domain = NULL;
+    }
+    if (q->path)
+    { SOAP_FREE(soap, q->path);
+      q->path = NULL;
+    }
+    if (value && *value && (q->value = (char*)SOAP_MALLOC(soap, strlen(value)+1)))
+      strcpy(q->value, value);
+    if (domain && (q->domain = (char*)SOAP_MALLOC(soap, strlen(domain)+1)))
+      strcpy(q->domain, domain);
+    if (path && (q->path = (char*)SOAP_MALLOC(soap, strlen(path)+1)))
+      strcpy(q->path, path);
+    q->session = 1;
+    q->env = 0;
+  }
+  return q;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_clr_cookie(struct soap *soap, const char *name, const char *domain, const char *path)
+{ struct soap_cookie **p, *q;
+  if (!domain)
+    domain = soap->cookie_domain;
+  if (!domain)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error in clear cookie %s: cookie domain not set\n", name?name:"(null)"));
+    return;
+  }
+  if (!path)
+    path = soap->cookie_path;
+  if (!path)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error in clear cookie %s: cookie path not set\n", name?name:"(null)"));
+    return;
+  }
+  if (*path == '/')
+    path++;
+  for (p = &soap->cookies, q = *p; q; q = *p)
+  { if (!strcmp(q->name, name) && !strcmp(q->domain, domain) && !strncmp(q->path, path, strlen(q->path)))
+    { if (q->value)
+        SOAP_FREE(soap, q->value);
+      if (q->domain)
+        SOAP_FREE(soap, q->domain);
+      if (q->path)
+        SOAP_FREE(soap, q->path);
+      *p = q->next;
+      SOAP_FREE(soap, q);
+    }
+    else
+      p = &q->next;
+  }
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_cookie_value(struct soap *soap, const char *name, const char *domain, const char *path)
+{ struct soap_cookie *p;
+  if ((p = soap_cookie(soap, name, domain, path)))
+    return p->value;
+  return NULL;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+char *
+SOAP_FMAC2
+soap_env_cookie_value(struct soap *soap, const char *name, const char *domain, const char *path)
+{ struct soap_cookie *p;
+  if ((p = soap_cookie(soap, name, domain, path)) && p->env)
+    return p->value;
+  return NULL;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+time_t
+SOAP_FMAC2
+soap_cookie_expire(struct soap *soap, const char *name, const char *domain, const char *path)
+{ struct soap_cookie *p;
+  if ((p = soap_cookie(soap, name, domain, path)))
+    return p->expire;
+  return -1;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_set_cookie_expire(struct soap *soap, const char *name, long expire, const char *domain, const char *path)
+{ struct soap_cookie *p;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Set cookie expiration max-age %ld: %s domain=%s path=%s\n", expire, name, domain?domain:"(null)", path?path:"(null)"));
+  if ((p = soap_cookie(soap, name, domain, path)))
+  { p->maxage = expire;
+    p->modified = 1;
+    return SOAP_OK;
+  }
+  return SOAP_ERR;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_set_cookie_session(struct soap *soap, const char *name, const char *domain, const char *path)
+{ struct soap_cookie *p;
+  if ((p = soap_cookie(soap, name, domain, path)))
+  { p->session = 1;
+    p->modified = 1;
+    return SOAP_OK;
+  }
+  return SOAP_ERR;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_clr_cookie_session(struct soap *soap, const char *name, const char *domain, const char *path)
+{ struct soap_cookie *p;
+  if ((p = soap_cookie(soap, name, domain, path)))
+  { p->session = 0;
+    p->modified = 1;
+    return SOAP_OK;
+  }
+  return SOAP_ERR;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_putsetcookies(struct soap *soap)
+{ struct soap_cookie *p;
+  char *s, tmp[4096];
+  const char *t;
+  for (p = soap->cookies; p; p = p->next)
+  {
+    if (p->modified
+#ifdef WITH_OPENSSL
+     || (!p->env && !soap->ssl == !p->secure)
+#endif
+       )
+    { s = tmp;
+      if (p->name)
+        s += soap_encode_cookie(p->name, s, tmp-s+4064);
+      if (p->value && *p->value)
+      { *s++ = '=';
+        s += soap_encode_cookie(p->value, s, tmp-s+4064);
+      }
+      if (p->domain && (int)strlen(p->domain) < tmp-s+4064)
+      { strcpy(s, ";Domain=");
+        strcat(s, p->domain);
+      }
+      else if (soap->cookie_domain && (int)strlen(soap->cookie_domain) < tmp-s+4064)
+      { strcpy(s, ";Domain=");
+        strcat(s, soap->cookie_domain);
+      }
+      strcat(s, ";Path=/");
+      s += strlen(s);
+      if (p->path)
+        t = p->path;
+      else
+        t = soap->cookie_path;
+      if (t)
+      { if (*t == '/')
+          t++;
+        if ((int)strlen(t) < tmp-s+4064)
+        { if (strchr(t, '%'))	/* already URL encoded? */
+          { strcpy(s, t);
+            s += strlen(s);
+          }
+          else
+            s += soap_encode_cookie(t, s, tmp-s+4064);
+        }
+      }
+      if (p->version > 0 && s-tmp < 4060)
+      { sprintf(s, ";Version=%u", p->version);
+        s += strlen(s);
+      }
+      if (p->maxage >= 0 && s-tmp < 4060)
+      { sprintf(s, ";Max-Age=%ld", p->maxage);
+        s += strlen(s);
+      }
+      if (s-tmp < 4073
+       && (p->secure
+#ifdef WITH_OPENSSL
+       || soap->ssl
+#endif
+         ))
+        strcpy(s, ";Secure");
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Set-Cookie: %s\n", tmp));
+      if ((soap->error = soap->fposthdr(soap, "Set-Cookie", tmp)))
+        return soap->error;
+    }
+  }
+  return SOAP_OK;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_putcookies(struct soap *soap, const char *domain, const char *path, int secure)
+{ struct soap_cookie **p, *q;
+  unsigned int version = 0;
+  time_t now = time(NULL);
+  char *s, tmp[4096];
+  p = &soap->cookies;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Sending cookies for domain=%s path=%s\n", domain, path));
+  if (*path == '/')
+    path++;
+  while ((q = *p))
+  { if (q->expire && now > q->expire)
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Cookie %s expired\n", q->name));
+      SOAP_FREE(soap, q->name);
+      if (q->value)
+        SOAP_FREE(soap, q->value);
+      if (q->domain)
+        SOAP_FREE(soap, q->domain);
+      if (q->path)
+        SOAP_FREE(soap, q->path);
+      *p = q->next;
+      SOAP_FREE(soap, q);
+    }
+    else
+    { int flag;
+      char *t = q->domain;
+      size_t n = 0;
+      if (!t)
+        flag = 1;
+      else
+      { const char *r = strchr(t, ':');
+        if (r)
+          n = r - t;
+        else
+          n = strlen(t);
+        flag = !strncmp(t, domain, n);
+      }
+      /* domain-level cookies, cannot compile when WITH_NOIO set */
+#ifndef WITH_NOIO
+      if (!flag)
+      { struct hostent *hostent = gethostbyname((char*)domain);
+        if (hostent)
+        { const char *r = strchr(hostent->h_name, '.');
+          if (!r)
+            r = hostent->h_name;
+          flag = !strncmp(t, r, n);
+        }
+      }
+#endif
+      if (flag
+          && (!q->path || !strncmp(q->path, path, strlen(q->path)))
+          && (!q->secure || secure))
+      { s = tmp;
+        if (q->version != version)
+        { sprintf(s, "$Version=%u;", q->version);
+          version = q->version;
+        }
+        if (q->name)
+          s += soap_encode_cookie(q->name, s, tmp-s+4080);
+        if (q->value && *q->value)
+        { *s++ = '=';
+          s += soap_encode_cookie(q->value, s, tmp-s+4080);
+        }
+        if (q->path && *q->path && (int)strlen(q->path) < tmp-s+4080)
+        { sprintf(s, ";$Path=\"/%s\"", (*q->path == '/' ? q->path + 1 : q->path));
+          s += strlen(s);
+        }
+        if (q->domain && (int)strlen(q->domain) < tmp-s+4080)
+          sprintf(s, ";$Domain=\"%s\"", q->domain);
+        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Cookie: %s\n", tmp));
+        if ((soap->error = soap->fposthdr(soap, "Cookie", tmp)))
+          return soap->error;
+      }
+      p = &q->next;
+    }
+  }
+  return SOAP_OK;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_getcookies(struct soap *soap, const char *val)
+{ struct soap_cookie *p = NULL, *q;
+  const char *s;
+  char *t, tmp[4096]; /* cookie size is up to 4096 bytes [RFC2109] */
+  char *domain = NULL;
+  char *path = NULL;
+  unsigned int version = 0;
+  time_t now = time(NULL);
+  if (!val)
+    return;
+  s = val;
+  while (*s)
+  { s = soap_decode_key(tmp, sizeof(tmp), s);
+    if (!soap_tag_cmp(tmp, "$Version"))
+    { if ((s = soap_decode_val(tmp, sizeof(tmp), s)))
+      { if (p)
+          p->version = (int)atol(tmp);
+        else
+          version = (int)atol(tmp);
+      }
+    }
+    else if (!soap_tag_cmp(tmp, "$Path"))
+    { s = soap_decode_val(tmp, sizeof(tmp), s);
+      if (*tmp)
+      { if ((t = (char*)SOAP_MALLOC(soap, strlen(tmp)+1)))
+          strcpy(t, tmp);
+      }
+      else
+        t = NULL;
+      if (p)
+      { if (p->path)
+          SOAP_FREE(soap, p->path);
+        p->path = t;
+      }
+      else
+      { if (path)
+          SOAP_FREE(soap, path);
+        path = t;
+      }
+    }
+    else if (!soap_tag_cmp(tmp, "$Domain"))
+    { s = soap_decode_val(tmp, sizeof(tmp), s);
+      if (*tmp)
+      { if ((t = (char*)SOAP_MALLOC(soap, strlen(tmp)+1)))
+          strcpy(t, tmp);
+      }
+      else
+        t = NULL;
+      if (p)
+      { if (p->domain)
+          SOAP_FREE(soap, p->domain);
+        p->domain = t;
+      }
+      else
+      { if (domain)
+          SOAP_FREE(soap, domain);
+        domain = t;
+      }
+    }
+    else if (p && !soap_tag_cmp(tmp, "Path"))
+    { if (p->path)
+        SOAP_FREE(soap, p->path);
+      s = soap_decode_val(tmp, sizeof(tmp), s);
+      if (*tmp)
+      { if ((p->path = (char*)SOAP_MALLOC(soap, strlen(tmp)+1)))
+          strcpy(p->path, tmp);
+      }
+      else
+        p->path = NULL;
+    }
+    else if (p && !soap_tag_cmp(tmp, "Domain"))
+    { if (p->domain)
+        SOAP_FREE(soap, p->domain);
+      s = soap_decode_val(tmp, sizeof(tmp), s);
+      if (*tmp)
+      { if ((p->domain = (char*)SOAP_MALLOC(soap, strlen(tmp)+1)))
+          strcpy(p->domain, tmp);
+      }
+      else
+        p->domain = NULL;
+    }
+    else if (p && !soap_tag_cmp(tmp, "Version"))
+    { s = soap_decode_val(tmp, sizeof(tmp), s);
+      p->version = (unsigned int)atol(tmp);
+    }
+    else if (p && !soap_tag_cmp(tmp, "Max-Age"))
+    { s = soap_decode_val(tmp, sizeof(tmp), s);
+      p->expire = now + atol(tmp);
+    }
+    else if (p && !soap_tag_cmp(tmp, "Expires"))
+    { struct tm T;
+      char a[3]; 
+      static const char mns[] = "anebarprayunulugepctovec";
+      s = soap_decode_val(tmp, sizeof(tmp), s);
+      if (strlen(tmp) > 20)
+      { memset((void*)&T, 0, sizeof(T));
+        a[0] = tmp[4];
+        a[1] = tmp[5];
+        a[2] = '\0';
+        T.tm_mday = (int)atol(a);
+        a[0] = tmp[8];
+        a[1] = tmp[9];
+        T.tm_mon = (int)(strstr(mns, a) - mns) / 2;
+        a[0] = tmp[11];
+        a[1] = tmp[12];
+        T.tm_year = 100 + (int)atol(a);
+        a[0] = tmp[13];
+        a[1] = tmp[14];
+        T.tm_hour = (int)atol(a);
+        a[0] = tmp[16];
+        a[1] = tmp[17];
+        T.tm_min = (int)atol(a);
+        a[0] = tmp[19];
+        a[1] = tmp[20];
+        T.tm_sec = (int)atol(a);
+        p->expire = soap_timegm(&T);
+      }
+    }
+    else if (p && !soap_tag_cmp(tmp, "Secure"))
+      p->secure = 1;
+    else
+    { if (p)
+      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Got environment cookie %s=%s domain=%s path=%s expire=%ld secure=%d\n", p->name, p->value?p->value:"(null)", p->domain?p->domain:"(null)", p->path?p->path:"(null)", p->expire, p->secure));
+        if ((q = soap_set_cookie(soap, p->name, p->value, p->domain, p->path)))
+        { q->version = p->version;
+          q->expire = p->expire;
+          q->secure = p->secure;
+          q->env = 1;
+        }
+        if (p->name)
+          SOAP_FREE(soap, p->name);
+        if (p->value)
+          SOAP_FREE(soap, p->value);
+        if (p->domain)
+          SOAP_FREE(soap, p->domain);
+        if (p->path)
+          SOAP_FREE(soap, p->path);
+        SOAP_FREE(soap, p);
+      }
+      if ((p = (struct soap_cookie*)SOAP_MALLOC(soap, sizeof(struct soap_cookie))))
+      { p->name = (char*)SOAP_MALLOC(soap, strlen(tmp)+1);
+        strcpy(p->name, tmp);
+        s = soap_decode_val(tmp, sizeof(tmp), s);
+        if (*tmp)
+        { p->value = (char*)SOAP_MALLOC(soap, strlen(tmp)+1);
+          strcpy(p->value, tmp);
+        }
+        else
+          p->value = NULL;
+        if (domain)
+          p->domain = domain;
+        else if (*soap->host)
+        { p->domain = (char*)SOAP_MALLOC(soap, strlen(soap->host)+1);
+          strcpy(p->domain, soap->host);
+        }
+        else
+          p->domain = NULL;
+        if (path)
+          p->path = path;
+        else if (soap->path && *soap->path)
+        { p->path = (char*)SOAP_MALLOC(soap, strlen(soap->path)+1);
+          strcpy(p->path, soap->path);
+        }
+        else
+        { p->path = (char*)SOAP_MALLOC(soap, 2);
+          strcpy(p->path, "/");
+        }
+        p->expire = 0;
+        p->secure = 0;
+        p->version = version;
+      }
+    }
+  }
+  if (p)
+  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Got environment cookie %s=%s domain=%s path=%s expire=%ld secure=%d\n", p->name, p->value?p->value:"(null)", p->domain?p->domain:"(null)", p->path?p->path:"(null)", p->expire, p->secure));
+    if ((q = soap_set_cookie(soap, p->name, p->value, p->domain, p->path)))
+    { q->version = p->version;
+      q->expire = p->expire;
+      q->secure = p->secure;
+      q->env = 1;
+    }
+    if (p->name)
+      SOAP_FREE(soap, p->name);
+    if (p->value)
+      SOAP_FREE(soap, p->value);
+    if (p->domain)
+      SOAP_FREE(soap, p->domain);
+    if (p->path)
+      SOAP_FREE(soap, p->path);
+    SOAP_FREE(soap, p);
+  }
+  if (domain)
+    SOAP_FREE(soap, domain);
+  if (path)
+    SOAP_FREE(soap, path);
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_getenv_cookies(struct soap *soap)
+{ struct soap_cookie *p;
+  const char *s;
+  char key[4096], val[4096]; /* cookie size is up to 4096 bytes [RFC2109] */
+  if (!(s = getenv("HTTP_COOKIE")))
+    return SOAP_ERR;
+  do
+  { s = soap_decode_key(key, sizeof(key), s);
+    s = soap_decode_val(val, sizeof(val), s);
+    p = soap_set_cookie(soap, key, val, NULL, NULL);
+    if (p)
+      p->env = 1;
+  } while (*s);
+  return SOAP_OK;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+struct soap_cookie*
+SOAP_FMAC2
+soap_copy_cookies(struct soap *copy, const struct soap *soap)
+{ struct soap_cookie *p, **q, *r;
+  q = &r;
+  for (p = soap->cookies; p; p = p->next)
+  { if (!(*q = (struct soap_cookie*)SOAP_MALLOC(copy, sizeof(struct soap_cookie))))
+      return r;
+    **q = *p;
+    if (p->name)
+    { if (((*q)->name = (char*)SOAP_MALLOC(copy, strlen(p->name)+1)))
+        strcpy((*q)->name, p->name);
+    }
+    if (p->value)
+    { if (((*q)->value = (char*)SOAP_MALLOC(copy, strlen(p->value)+1)))
+        strcpy((*q)->value, p->value);
+    }
+    if (p->domain)
+    { if (((*q)->domain = (char*)SOAP_MALLOC(copy, strlen(p->domain)+1)))
+        strcpy((*q)->domain, p->domain);
+    }
+    if (p->path)
+    { if (((*q)->path = (char*)SOAP_MALLOC(copy, strlen(p->path)+1)))
+        strcpy((*q)->path, p->path);
+    }
+    q = &(*q)->next;
+  }
+  *q = NULL;
+  return r;
+}
+
+/******************************************************************************/
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_free_cookies(struct soap *soap)
+{ struct soap_cookie *p;
+  for (p = soap->cookies; p; p = soap->cookies)
+  { soap->cookies = p->next;
+    SOAP_FREE(soap, p->name);
+    if (p->value)
+      SOAP_FREE(soap, p->value);
+    if (p->domain)
+      SOAP_FREE(soap, p->domain);
+    if (p->path)
+      SOAP_FREE(soap, p->path);
+    SOAP_FREE(soap, p);
+  }
+}
+
+/******************************************************************************/
+#endif /* WITH_COOKIES */
+
+/******************************************************************************/
+#ifdef WITH_GZIP
+#ifndef PALM_1
+static int
+soap_getgziphdr(struct soap *soap)
+{ int i;
+  soap_wchar c = 0, f = 0;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Get gzip header\n"));
+  for (i = 0; i < 9; i++)
+  { if ((int)(c = soap_get1(soap) == EOF))
+      return soap->error = SOAP_EOF;
+    if (i == 2)
+      f = c;
+  }
+  if (f & 0x04) /* FEXTRA */
+  { for (i = soap_get1(soap) | (soap_get1(soap) << 8); i; i--)
+    { if ((int)soap_get1(soap) == EOF)
+        return soap->error = SOAP_EOF;
+    }
+  }
+  if (f & 0x08) /* FNAME */
+  { do
+      c = soap_get1(soap);
+    while (c && (int)c != EOF);
+  }
+  if ((int)c != EOF && (f & 0x10)) /* FCOMMENT */
+  { do
+      c = soap_get1(soap);
+    while (c && (int)c != EOF);
+  }
+  if ((int)c != EOF && (f & 0x01)) /* FHCRC */
+  { if ((int)(c = soap_get1(soap)) != EOF)
+      c = soap_get1(soap);
+  }
+  if ((int)c == EOF)
+    return soap->error = SOAP_EOF;
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_begin_recv(struct soap *soap)
+{ soap_wchar c;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Initializing for input\n"));
+  soap->error = SOAP_OK;
+  soap_free_temp(soap);
+  soap_set_local_namespaces(soap);
+  soap->version = 0;	/* don't assume we're parsing SOAP content by default */
+#ifndef WITH_NOIDREF
+  soap_free_iht(soap);
+#endif
+  if ((soap->imode & SOAP_IO) == SOAP_IO_CHUNK)
+    soap->omode |= SOAP_IO_CHUNK;
+  soap->imode &= ~SOAP_IO;
+  soap->mode = soap->imode;
+  if (!soap->keep_alive)
+  { soap->buflen = 0;
+    soap->bufidx = 0;
+  }
+  if (!(soap->mode & SOAP_IO_KEEPALIVE))
+    soap->keep_alive = 0;
+  soap->ahead = 0;
+  soap->peeked = 0;
+  soap->level = 0;
+  soap->part = SOAP_BEGIN;
+  soap->alloced = 0;
+  soap->count = 0;
+  soap->length = 0;
+  soap->cdata = 0;
+  *soap->endpoint = '\0';
+  soap->action = NULL;
+  soap->header = NULL;
+  soap->fault = NULL;
+  soap->status = 0;
+#ifndef WITH_LEANER
+  soap->dom = NULL;
+  soap->dime.chunksize = 0;
+  soap->dime.buflen = 0;
+  soap->dime.list = NULL;
+  soap->dime.first = NULL;
+  soap->dime.last = NULL;
+  soap->mime.list = NULL;
+  soap->mime.first = NULL;
+  soap->mime.last = NULL;
+  soap->mime.boundary = NULL;
+  soap->mime.start = NULL;
+  soap->xlist = NULL;
+#endif
+#ifdef WIN32
+#ifndef UNDER_CE
+#ifndef WITH_FASTCGI
+  if (!soap_valid_socket(soap->socket))
+#ifdef __BORLANDC__
+    setmode(soap->recvfd, O_BINARY);
+#else
+    _setmode(soap->recvfd, _O_BINARY);
+#endif
+#endif
+#endif
+#endif
+#ifdef WITH_ZLIB
+  soap->mode &= ~SOAP_ENC_ZLIB;
+  soap->zlib_in = SOAP_ZLIB_NONE;
+  soap->zlib_out = SOAP_ZLIB_NONE;
+  soap->d_stream->next_in = Z_NULL;
+  soap->d_stream->avail_in = 0;
+  soap->d_stream->next_out = (Byte*)soap->buf;
+  soap->d_stream->avail_out = SOAP_BUFLEN;
+  soap->z_ratio_in = 1.0;
+#endif
+#ifdef WITH_OPENSSL
+  if (soap->ssl)
+    ERR_clear_error();
+#endif
+#ifndef WITH_LEANER
+  if (soap->fprepareinit)
+    soap->fprepareinit(soap);
+#endif
+  c = soap_getchar(soap);
+#ifdef WITH_GZIP
+  if (c == 0x1F)
+  { if (soap_getgziphdr(soap))
+      return soap->error;
+    if (inflateInit2(soap->d_stream, -MAX_WBITS) != Z_OK)
+      return soap->error = SOAP_ZLIB_ERROR;
+    soap->zlib_state = SOAP_ZLIB_INFLATE;
+    soap->mode |= SOAP_ENC_ZLIB;
+    soap->zlib_in = SOAP_ZLIB_GZIP;
+    soap->z_crc = crc32(0L, NULL, 0);
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "gzip initialized\n"));
+    if (!soap->z_buf)
+      soap->z_buf = (char*)SOAP_MALLOC(soap, SOAP_BUFLEN);
+    memcpy(soap->z_buf, soap->buf, SOAP_BUFLEN);
+    /* should not chunk over plain transport, so why bother to check? */
+    /* if ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK) */
+    /*   soap->z_buflen = soap->bufidx; */
+    /* else */
+    soap->d_stream->next_in = (Byte*)(soap->z_buf + soap->bufidx);
+    soap->d_stream->avail_in = soap->buflen - soap->bufidx;
+    soap->z_buflen = soap->buflen;
+    soap->buflen = soap->bufidx;
+    c = soap_getchar(soap);
+  }  
+#endif
+#ifndef WITH_LEANER
+  if (c == '-' && soap_get0(soap) == '-')
+    soap->mode |= SOAP_ENC_MIME;
+  else if ((c & 0xFFFC) == (SOAP_DIME_VERSION | SOAP_DIME_MB) && (soap_get0(soap) & 0xFFF0) == 0x20)
+    soap->mode |= SOAP_ENC_DIME;
+  else
+#endif
+  { while (soap_blank(c))
+      c = soap_getchar(soap);
+  }
+  if ((int)c == EOF)
+    return soap->error = SOAP_EOF;
+  soap_unget(soap, c);
+#ifndef WITH_NOHTTP
+  /* if not XML or (start of)BOM or MIME/DIME/ZLIB, assume HTTP header */
+  if (c != '<' && c != 0xEF && !(soap->mode & (SOAP_ENC_MIME | SOAP_ENC_DIME | SOAP_ENC_ZLIB)))
+  { soap->mode &= ~SOAP_IO;
+    soap->error = soap->fparse(soap);
+    if (soap->error && soap->error < SOAP_STOP)
+    { soap->keep_alive = 0; /* force close later */
+      return soap->error;
+    }
+    if (soap->error == SOAP_STOP)
+      return soap->error;
+    soap->mode = soap->imode; /* if imode is changed, copy */
+    if ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK)
+    { soap->chunkbuflen = soap->buflen;
+      soap->buflen = soap->bufidx;
+      soap->chunksize = 0;
+    }
+#ifndef WITH_LEANER
+    else if (soap->fpreparerecv && soap->buflen != soap->bufidx)
+      soap->fpreparerecv(soap, soap->buf + soap->bufidx, soap->buflen - soap->bufidx);
+#endif
+    /* Note: fparse should not use soap_unget to push back last char */
+    if (soap_get0(soap) == (int)EOF)
+    { if (soap->status == 200)
+        return soap->error = SOAP_NO_DATA;
+      return soap->error = soap->status;
+    }
+#ifdef WITH_ZLIB
+    if (soap->zlib_in != SOAP_ZLIB_NONE)
+    {
+#ifdef WITH_GZIP
+      if (soap->zlib_in != SOAP_ZLIB_DEFLATE)
+      { c = soap_get1(soap);
+        if (c == 0x1F)
+        { if (soap_getgziphdr(soap))
+            return soap->error;
+          if (inflateInit2(soap->d_stream, -MAX_WBITS) != Z_OK)
+            return soap->error = SOAP_ZLIB_ERROR;
+          soap->z_crc = crc32(0L, NULL, 0);
+          DBGLOG(TEST, SOAP_MESSAGE(fdebug, "gzip initialized\n"));
+        }
+        else
+        { soap_revget1(soap);
+          if (inflateInit(soap->d_stream) != Z_OK)
+            return soap->error = SOAP_ZLIB_ERROR;
+          soap->zlib_in = SOAP_ZLIB_DEFLATE;
+        }
+      }
+      else
+#endif
+      if (inflateInit(soap->d_stream) != Z_OK)
+        return soap->error = SOAP_ZLIB_ERROR;
+      soap->zlib_state = SOAP_ZLIB_INFLATE;
+      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Inflate initialized\n"));
+      soap->mode |= SOAP_ENC_ZLIB;
+      if (!soap->z_buf)
+        soap->z_buf = (char*)SOAP_MALLOC(soap, SOAP_BUFLEN);
+      memcpy(soap->z_buf, soap->buf, SOAP_BUFLEN);
+      soap->d_stream->next_in = (Byte*)(soap->z_buf + soap->bufidx);
+      soap->d_stream->avail_in = soap->buflen - soap->bufidx;
+      soap->z_buflen = soap->buflen;
+      soap->buflen = soap->bufidx;
+    }
+#endif
+    if (soap->error)
+    { if (soap->error == SOAP_FORM && soap->fform)
+      { soap->error = soap->fform(soap);
+        if (soap->error == SOAP_OK)
+          soap->error = SOAP_STOP; /* prevents further processing */
+      }
+      return soap->error;
+    }
+  }
+#endif
+#ifndef WITH_LEANER
+  if (soap->mode & SOAP_ENC_MIME)
+  { if (soap_getmimehdr(soap))
+      return soap->error;
+    if (soap->mime.start)
+    { do
+      { if (!soap->mime.last->id)
+          break;
+        if (!soap_match_cid(soap, soap->mime.start, soap->mime.last->id))
+          break;
+      } while (soap_get_mime_attachment(soap, NULL));
+    }
+    if (soap_get_header_attribute(soap, soap->mime.first->type, "application/dime"))
+      soap->mode |= SOAP_ENC_DIME;
+  }
+  if (soap->mode & SOAP_ENC_DIME)
+  { if (soap_getdimehdr(soap))
+      return soap->error;
+    if (soap->dime.flags & SOAP_DIME_CF)
+    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Chunked DIME SOAP message\n"));
+      soap->dime.chunksize = soap->dime.size;
+      if (soap->buflen - soap->bufidx >= soap->dime.chunksize)
+      { soap->dime.buflen = soap->buflen;
+        soap->buflen = soap->bufidx + soap->dime.chunksize;
+      }
+      else
+        soap->dime.chunksize -= soap->buflen - soap->bufidx;
+    }
+    soap->count = soap->buflen - soap->bufidx;
+  }
+#endif
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static int
+http_parse(struct soap *soap)
+{ char header[SOAP_HDRLEN], *s;
+  unsigned short httpcmd = 0, status = 0, k = 0;
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Waiting for HTTP request/response...\n"));
+  *soap->endpoint = '\0';
+  soap->length = 0;
+  soap->userid = NULL;
+  soap->passwd = NULL;
+  soap->action = NULL;
+  soap->authrealm = NULL;
+  soap->proxy_from = NULL;
+  soap->http_content = NULL;
+  soap->status = 0;
+  do
+  { if (soap_getline(soap, soap->msgbuf, sizeof(soap->msgbuf)))
+    { if (soap->error == SOAP_EOF)
+        return SOAP_EOF;
+      return soap->error = 414;
+    }
+    DBGLOG(TEST,SOAP_MESSAGE(fdebug, "HTTP status: %s\n", soap->msgbuf));
+    for (;;)
+    { if (soap_getline(soap, header, SOAP_HDRLEN))
+      { if (soap->error == SOAP_EOF)
+        { soap->error = SOAP_OK;
+          DBGLOG(TEST,SOAP_MESSAGE(fdebug, "EOF in HTTP header, continue anyway\n"));
+          break;
+        }
+        return soap->error;
+      }
+      if (!*header)
+        break;
+      DBGLOG(TEST,SOAP_MESSAGE(fdebug, "HTTP header: %s\n", header));
+      s = strchr(header, ':');
+      if (s)
+      { char *t;
+        *s = '\0';
+        do s++;
+        while (*s && *s <= 32);
+        if (*s == '"')
+          s++;
+        t = s + strlen(s) - 1;
+        while (t > s && *t <= 32)
+          t--;
+        if (t >= s && *t == '"')
+          t--;
+        t[1] = '\0';
+        if ((soap->error = soap->fparsehdr(soap, header, s)))
+        { if (soap->error < SOAP_STOP)
+            return soap->error;
+          status = soap->error;
+          soap->error = SOAP_OK;
+        }
+      }
+    }
+    if ((s = strchr(soap->msgbuf, ' ')))
+    { k = (unsigned short)soap_strtoul(s, &s, 10);
+      if (!soap_blank(*s))
+        k = 0;
+    }
+    else
+      k = 0;
+  } while (k == 100);
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Finished HTTP header parsing, status = %d\n", k));
+  s = strstr(soap->msgbuf, "HTTP/");
+  if (s && s[7] != '1')
+  { if (soap->keep_alive == 1)
+      soap->keep_alive = 0;
+    if (k == 0 && (soap->omode & SOAP_IO) == SOAP_IO_CHUNK) /* k == 0 for HTTP request */
+    { soap->imode |= SOAP_IO_CHUNK;
+      soap->omode = (soap->omode & ~SOAP_IO) | SOAP_IO_STORE;
+    }
+  }
+  if (soap->keep_alive < 0)
+    soap->keep_alive = 1;
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Keep alive connection = %d\n", soap->keep_alive));
+  if (k == 0)
+  { size_t l = 0;
+    if (s)
+    { if (!strncmp(soap->msgbuf, "POST ", l = 5))
+        httpcmd = 1;
+      else if (!strncmp(soap->msgbuf, "GET ", l = 4))
+        httpcmd = 2;
+      else if (!strncmp(soap->msgbuf, "PUT ", l = 4))
+        httpcmd = 3;
+      else if (!strncmp(soap->msgbuf, "DELETE ", l = 7))
+        httpcmd = 4;
+      else if (!strncmp(soap->msgbuf, "HEAD ", l = 5))
+        httpcmd = 5;
+    }
+    if (s && httpcmd) 
+    { size_t m = strlen(soap->endpoint);
+      size_t n = m + (s - soap->msgbuf) - l - 1;
+      if (m > n)
+        m = n;
+      if (n >= sizeof(soap->endpoint))
+        n = sizeof(soap->endpoint) - 1;
+      strncpy(soap->path, soap->msgbuf + l, n - m);
+      soap->path[n - m] = '\0';
+      strcat(soap->endpoint, soap->path);
+      DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Target endpoint='%s'\n", soap->endpoint));
+      if (httpcmd > 1)
+      { switch (httpcmd)
+        { case  2: soap->error = soap->fget(soap); break;
+          case  3: soap->error = soap->fput(soap); break;
+          case  4: soap->error = soap->fdel(soap); break;
+          case  5: soap->error = soap->fhead(soap); break;
+	  default: soap->error = SOAP_HTTP_METHOD; break;
+	}
+        if (soap->error == SOAP_OK)
+          soap->error = SOAP_STOP; /* prevents further processing */
+        return soap->error;
+      }
+      if (status)
+        return soap->error = status;
+    }
+    else if (status)
+      return soap->error = status;
+    else if (s)
+      return soap->error = 405;
+  }
+  soap->status = k;
+  /* Status OK (HTTP 200) */
+  if (k == 0 || k == 200)
+    return SOAP_OK;
+  /* Status 201 (Created), 202 (Accepted), ... and HTTP 400 and 500 errors.
+     Only keep parsing HTTP body when content-length>0 or chunked is set.
+  */
+  if (((k > 200 && k <= 299) || k == 400 || k == 500) && (soap->length > 0 || (soap->imode & SOAP_IO) == SOAP_IO_CHUNK))
+    return SOAP_OK;
+  /* HTTP 400 and 500 headers are supposed to set content-length or chunked.
+     For those that don't we keep parsing the body only if content type is
+     given and connection closes.
+  */
+  if ((k == 400 || k == 500) && (soap->http_content || soap->keep_alive == 0))
+    return SOAP_OK;
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "HTTP error %d\n", k));
+  return soap_set_receiver_error(soap, "HTTP Error", soap->msgbuf, k);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static int
+http_parse_header(struct soap *soap, const char *key, const char *val)
+{ if (!soap_tag_cmp(key, "Host"))
+  { 
+#ifdef WITH_OPENSSL
+    if (soap->imode & SOAP_ENC_SSL)
+      strcpy(soap->endpoint, "https://");
+    else
+#endif
+      strcpy(soap->endpoint, "http://");
+    strncat(soap->endpoint, val, sizeof(soap->endpoint) - 8);
+    soap->endpoint[sizeof(soap->endpoint) - 1] = '\0';
+  }
+#ifndef WITH_LEANER
+  else if (!soap_tag_cmp(key, "Content-Type"))
+  { const char *action;
+    soap->http_content = soap_strdup(soap, val);
+    if (soap_get_header_attribute(soap, val, "application/dime"))
+      soap->imode |= SOAP_ENC_DIME;
+    else if (soap_get_header_attribute(soap, val, "multipart/related")
+          || soap_get_header_attribute(soap, val, "multipart/form-data"))
+    { soap->mime.boundary = soap_strdup(soap, soap_get_header_attribute(soap, val, "boundary"));
+      soap->mime.start = soap_strdup(soap, soap_get_header_attribute(soap, val, "start"));
+      soap->imode |= SOAP_ENC_MIME;
+    }
+    action = soap_get_header_attribute(soap, val, "action");
+    if (action)
+    { if (*action == '"')
+      { soap->action = soap_strdup(soap, action + 1);
+        soap->action[strlen(soap->action) - 1] = '\0';
+      }
+      else
+        soap->action = soap_strdup(soap, action);
+    }
+  }
+#endif
+  else if (!soap_tag_cmp(key, "Content-Length"))
+  { soap->length = soap_strtoul(val, NULL, 10);
+  }
+  else if (!soap_tag_cmp(key, "Content-Encoding"))
+  { if (!soap_tag_cmp(val, "deflate"))
+#ifdef WITH_ZLIB
+      soap->zlib_in = SOAP_ZLIB_DEFLATE;
+#else
+      return SOAP_ZLIB_ERROR;
+#endif
+    else if (!soap_tag_cmp(val, "gzip"))
+#ifdef WITH_GZIP
+      soap->zlib_in = SOAP_ZLIB_GZIP;
+#else
+      return SOAP_ZLIB_ERROR;
+#endif
+  }
+#ifdef WITH_ZLIB
+  else if (!soap_tag_cmp(key, "Accept-Encoding"))
+  {
+#ifdef WITH_GZIP
+    if (strchr(val, '*') || soap_get_header_attribute(soap, val, "gzip"))
+      soap->zlib_out = SOAP_ZLIB_GZIP;
+    else
+#endif
+    if (strchr(val, '*') || soap_get_header_attribute(soap, val, "deflate"))
+      soap->zlib_out = SOAP_ZLIB_DEFLATE;
+    else
+      soap->zlib_out = SOAP_ZLIB_NONE;
+  }
+#endif
+  else if (!soap_tag_cmp(key, "Transfer-Encoding"))
+  { soap->imode &= ~SOAP_IO;
+    if (!soap_tag_cmp(val, "chunked"))
+      soap->imode |= SOAP_IO_CHUNK;
+  }
+  else if (!soap_tag_cmp(key, "Connection"))
+  { if (!soap_tag_cmp(val, "keep-alive"))
+      soap->keep_alive = -soap->keep_alive;
+    else if (!soap_tag_cmp(val, "close"))
+      soap->keep_alive = 0;
+  }
+#ifndef WITH_LEAN
+  else if (!soap_tag_cmp(key, "Authorization"))
+  { if (!soap_tag_cmp(val, "Basic *"))
+    { int n;
+      char *s;
+      soap_base642s(soap, val + 6, soap->tmpbuf, sizeof(soap->tmpbuf) - 1, &n);
+      soap->tmpbuf[n] = '\0';
+      if ((s = strchr(soap->tmpbuf, ':')))
+      { *s = '\0';
+        soap->userid = soap_strdup(soap, soap->tmpbuf);
+        soap->passwd = soap_strdup(soap, s + 1);
+      }
+    }
+  }
+  else if (!soap_tag_cmp(key, "WWW-Authenticate"))
+  { soap->authrealm = soap_strdup(soap, soap_get_header_attribute(soap, val + 6, "realm"));
+  }
+  else if (!soap_tag_cmp(key, "Expect"))
+  { if (!soap_tag_cmp(val, "100-continue"))
+    { if ((soap->error = soap->fposthdr(soap, "HTTP/1.1 100 Continue", NULL))
+       || (soap->error = soap->fposthdr(soap, NULL, NULL)))
+        return soap->error;
+    }
+  }
+#endif
+  else if (!soap_tag_cmp(key, "SOAPAction"))
+  { if (*val == '"')
+    { soap->action = soap_strdup(soap, val + 1);
+      soap->action[strlen(soap->action) - 1] = '\0';
+    }
+    else
+      soap->action = soap_strdup(soap, val);
+  }
+  else if (!soap_tag_cmp(key, "Location"))
+  { strncpy(soap->endpoint, val, sizeof(soap->endpoint));
+    soap->endpoint[sizeof(soap->endpoint) - 1] = '\0';
+  }
+  else if (!soap_tag_cmp(key, "X-Forwarded-For"))
+  { soap->proxy_from = soap_strdup(soap, val);
+  }
+#ifdef WITH_COOKIES
+  else if (!soap_tag_cmp(key, "Cookie")
+   || !soap_tag_cmp(key, "Cookie2")
+   || !soap_tag_cmp(key, "Set-Cookie")
+   || !soap_tag_cmp(key, "Set-Cookie2"))
+  { soap_getcookies(soap, val);
+  }
+#endif
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#if !defined(WITH_NOHTTP) || !defined(WITH_LEANER)
+#ifndef PALM_1
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_get_header_attribute(struct soap *soap, const char *line, const char *key)
+{ register const char *s = line;
+  if (s)
+  { while (*s)
+    { register short flag;
+      s = soap_decode_key(soap->tmpbuf, sizeof(soap->tmpbuf), s);
+      flag = soap_tag_cmp(soap->tmpbuf, key);
+      s = soap_decode_val(soap->tmpbuf, sizeof(soap->tmpbuf), s);
+      if (!flag)
+        return soap->tmpbuf;
+    }
+  }
+  return NULL;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#if !defined(WITH_NOHTTP) || !defined(WITH_LEANER)
+#ifndef PALM_1
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_decode_key(char *buf, size_t len, const char *val)
+{ return soap_decode(buf, len, val, "=,;");
+}
+#endif
+#endif
+
+/******************************************************************************/
+#if !defined(WITH_NOHTTP) || !defined(WITH_LEANER)
+#ifndef PALM_1
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_decode_val(char *buf, size_t len, const char *val)
+{ if (*val != '=')
+  { *buf = '\0';
+    return val;
+  }
+  return soap_decode(buf, len, val + 1, ",;");
+}
+#endif
+#endif
+
+/******************************************************************************/
+#if !defined(WITH_NOHTTP) || !defined(WITH_LEANER)
+#ifndef PALM_1
+static const char*
+soap_decode(char *buf, size_t len, const char *val, const char *sep)
+{ const char *s;
+  char *t = buf;
+  for (s = val; *s; s++)
+    if (*s != ' ' && *s != '\t' && !strchr(sep, *s))
+      break;
+  if (*s == '"')
+  { s++;
+    while (*s && *s != '"' && --len)
+      *t++ = *s++;
+  }
+  else
+  { while (*s && !soap_blank(*s) && !strchr(sep, *s) && --len)
+    { if (*s == '%')
+      { *t++ = ((s[1] >= 'A' ? (s[1] & 0x7) + 9 : s[1] - '0') << 4)
+              + (s[2] >= 'A' ? (s[2] & 0x7) + 9 : s[2] - '0');
+        s += 3;
+      }
+      else
+        *t++ = *s++;
+    }
+  }
+  *t = '\0';
+  while (*s && !strchr(sep, *s))
+    s++;
+  return s;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_envelope_begin_out(struct soap *soap)
+{
+#ifndef WITH_LEANER
+  size_t n = 0;
+  if ((soap->mode & SOAP_ENC_MIME) && soap->mime.boundary && soap->mime.start && strlen(soap->mime.boundary) + strlen(soap->mime.start) < sizeof(soap->tmpbuf) - 80 )
+  { const char *s;
+    if ((soap->mode & SOAP_ENC_DIME) && !(soap->mode & SOAP_ENC_MTOM))
+      s = "application/dime";
+    else if (soap->version == 2)
+    { if (soap->mode & SOAP_ENC_MTOM)
+        s = "application/xop+xml; charset=utf-8; type=\"application/soap+xml\"";
+      else
+        s = "application/soap+xml; charset=utf-8";
+    }
+    else if (soap->mode & SOAP_ENC_MTOM)
+      s = "application/xop+xml; text/xml; charset=utf-8";
+    else
+      s = "text/xml; charset=utf-8";
+    sprintf(soap->tmpbuf, "--%s\r\nContent-Type: %s\r\nContent-Transfer-Encoding: binary\r\nContent-ID: %s\r\n\r\n", soap->mime.boundary, s, soap->mime.start);
+    n = strlen(soap->tmpbuf);
+    if (soap_send_raw(soap, soap->tmpbuf, n))
+      return soap->error;
+  }
+  if (soap->mode & SOAP_IO_LENGTH)
+    soap->dime.size = soap->count;	/* DIME in MIME correction */
+  if (!(soap->mode & SOAP_IO_LENGTH) && (soap->mode & SOAP_ENC_DIME))
+  { if (soap_putdimehdr(soap))
+      return soap->error;
+  }
+#endif
+  soap->part = SOAP_IN_ENVELOPE;
+  return soap_element_begin_out(soap, "SOAP-ENV:Envelope", 0, NULL);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_envelope_end_out(struct soap *soap)
+{ if (soap_element_end_out(soap, "SOAP-ENV:Envelope"))
+    return soap->error;
+#ifndef WITH_LEANER
+  if ((soap->mode & SOAP_IO_LENGTH) && (soap->mode & SOAP_ENC_DIME) && !(soap->mode & SOAP_ENC_MTOM))
+  { soap->dime.size = soap->count - soap->dime.size;	/* DIME in MIME correction */
+    sprintf(soap->id, soap->dime_id_format, 0);
+    soap->dime.id = soap->id;
+    if (soap->local_namespaces)
+    { if (soap->local_namespaces[0].out)
+        soap->dime.type = (char*)soap->local_namespaces[0].out;
+      else
+        soap->dime.type = (char*)soap->local_namespaces[0].ns;
+    }
+    soap->dime.options = NULL;
+    soap->dime.flags = SOAP_DIME_MB | SOAP_DIME_ABSURI;
+    if (!soap->dime.first)
+      soap->dime.flags |= SOAP_DIME_ME;
+    soap->count += 12 + ((strlen(soap->dime.id)+3)&(~3)) + (soap->dime.type ? ((strlen(soap->dime.type)+3)&(~3)) : 0);
+  }
+  if ((soap->mode & SOAP_ENC_DIME) && !(soap->mode & SOAP_ENC_MTOM))
+    return soap_send_raw(soap, SOAP_STR_PADDING, -(long)soap->dime.size&3);
+#endif
+  soap->part = SOAP_END_ENVELOPE;
+  return SOAP_OK;
+} 
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_envelope_begin_in(struct soap *soap)
+{ register struct Namespace *p;
+  soap->part = SOAP_IN_ENVELOPE;
+  if (soap_element_begin_in(soap, "SOAP-ENV:Envelope", 0, NULL))
+  { 
+#ifndef WITH_LEAN
+    if (soap->error == SOAP_TAG_MISMATCH && !soap_element_begin_in(soap, "html", 0, NULL))
+    { /* get HTML from buffer, stop receiving to avoid HTML parsing issues */
+      char *s;
+#ifndef WITH_NOIO
+      size_t (*f)(struct soap*, char*, size_t) = soap->frecv;
+      soap->frecv = frecv_stop;
+#endif
+      soap_revert(soap);
+      s = soap_string_in(soap, 1, -1, -1);
+#ifndef WITH_NOIO
+      soap->frecv = f;
+#endif
+      return soap_set_receiver_error(soap, "HTTP Error", s, SOAP_HTTP_ERROR);
+    }
+#endif
+    if (soap->error == SOAP_TAG_MISMATCH)
+      return soap->error = SOAP_VERSIONMISMATCH;
+    return soap->error;
+  }
+  p = soap->local_namespaces;
+  if (p)
+  { const char *ns = p[0].out;
+    if (!ns)
+      ns = p[0].ns;
+    if (!strcmp(ns, soap_env1))
+    { soap->version = 1; /* make sure we use SOAP 1.1 */
+      if (p[1].out)
+        SOAP_FREE(soap, p[1].out);
+      if ((p[1].out = (char*)SOAP_MALLOC(soap, sizeof(soap_enc1))))
+        strcpy(p[1].out, soap_enc1);
+    }
+    else if (!strcmp(ns, soap_env2))
+    { soap->version = 2; /* make sure we use SOAP 1.2 */
+      if (p[1].out)
+        SOAP_FREE(soap, p[1].out);
+      if ((p[1].out = (char*)SOAP_MALLOC(soap, sizeof(soap_enc2))))
+        strcpy(p[1].out, soap_enc2);
+    }
+  }
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_envelope_end_in(struct soap *soap)
+{ soap->part = SOAP_END_ENVELOPE;
+  return soap_element_end_in(soap, "SOAP-ENV:Envelope");
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_body_begin_out(struct soap *soap)
+{ soap->part = SOAP_IN_BODY;
+  if (soap->version == 1)
+    soap->encoding = 1;
+#ifndef WITH_LEAN
+  if ((soap->mode & SOAP_XML_SEC) && soap_set_attr(soap, "wsu:Id", "Body"))
+    return soap->error;
+#endif
+  if (soap_element(soap, "SOAP-ENV:Body", 0, NULL))
+    return soap->error;
+  return soap_element_start_end_out(soap, NULL);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_body_end_out(struct soap *soap)
+{ if (soap_element_end_out(soap, "SOAP-ENV:Body"))
+    return soap->error;
+  soap->part = SOAP_END_BODY;
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_body_begin_in(struct soap *soap)
+{ soap->part = SOAP_IN_BODY;
+  if (soap_element_begin_in(soap, "SOAP-ENV:Body", 0, NULL))
+    return soap->error;
+  if (!soap->body)
+    soap->part = SOAP_NO_BODY;
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_body_end_in(struct soap *soap)
+{ if (soap->part == SOAP_NO_BODY)
+    return SOAP_OK;
+  soap->part = SOAP_END_BODY;
+  return soap_element_end_in(soap, "SOAP-ENV:Body");
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_recv_header(struct soap *soap)
+{ if (soap_getheader(soap) && soap->error == SOAP_TAG_MISMATCH)
+    soap->error = SOAP_OK;
+  else if (soap->error == SOAP_OK && soap->fheader)
+    soap->error = soap->fheader(soap);
+  return soap->error;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_set_endpoint(struct soap *soap, const char *endpoint)
+{ register const char *s;
+  register size_t i, n;
+  soap->endpoint[0] = '\0';
+  soap->host[0] = '\0';
+  soap->path[0] = '/';
+  soap->path[1] = '\0';
+  soap->port = 80;
+  if (!endpoint || !*endpoint)
+    return;
+#ifdef WITH_OPENSSL
+  if (!soap_tag_cmp(endpoint, "https:*"))
+    soap->port = 443;
+#endif
+  strncpy(soap->endpoint, endpoint, sizeof(soap->endpoint) - 1);
+  soap->endpoint[sizeof(soap->endpoint) - 1] = '\0';
+  s = strchr(endpoint, ':');
+  if (s && s[1] == '/' && s[2] == '/')
+    s += 3;
+  else
+    s = endpoint;
+  n = strlen(s);
+  if (n >= sizeof(soap->host))
+    n = sizeof(soap->host) - 1;
+#ifdef WITH_IPV6
+  if (s[0] == '[')
+  { s++;
+    for (i = 0; i < n; i++)
+    { if (s[i] == ']')
+      { s++;
+        --n;
+        break; 
+      }
+      soap->host[i] = s[i];
+    }
+  }
+  else
+  { for (i = 0; i < n; i++)
+    { soap->host[i] = s[i];
+      if (s[i] == '/' || s[i] == ':')
+        break; 
+    }
+  }
+#else
+  for (i = 0; i < n; i++)
+  { soap->host[i] = s[i];
+    if (s[i] == '/' || s[i] == ':')
+      break; 
+  }
+#endif
+  soap->host[i] = '\0';
+  if (s[i] == ':')
+  { soap->port = (int)atol(s + i + 1);
+    for (i++; i < n; i++)
+      if (s[i] == '/')
+        break;
+  }
+  if (i < n && s[i])
+  { strncpy(soap->path, s + i, sizeof(soap->path));
+    soap->path[sizeof(soap->path) - 1] = '\0';
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_connect(struct soap *soap, const char *endpoint, const char *action)
+{ return soap_connect_command(soap, SOAP_POST, endpoint, action);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_connect_command(struct soap *soap, int http_command, const char *endpoint, const char *action)
+{ char host[sizeof(soap->host)];
+  int port;
+  size_t count;
+  soap->error = SOAP_OK;
+  strcpy(host, soap->host); /* save previous host name to compare */
+  port = soap->port; /* save previous port to compare */
+  soap->status = http_command;
+  soap_set_endpoint(soap, endpoint);
+#ifndef WITH_LEANER
+  if (soap->fconnect)
+  { if ((soap->error = soap->fconnect(soap, endpoint, soap->host, soap->port)))
+      return soap->error;
+  }
+  else
+#endif
+  if (soap->fopen && *soap->host)
+  { if (!soap->keep_alive || !soap_valid_socket(soap->socket) || strcmp(soap->host, host) || soap->port != port || !soap->fpoll || soap->fpoll(soap))
+    { soap->keep_alive = 0; /* to force close */
+      soap->omode &= ~SOAP_IO_UDP; /* to force close */
+      soap_closesock(soap);
+      DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Connect/reconnect to host='%s' path='%s' port=%d\n", soap->host, soap->path, soap->port));
+#ifndef WITH_LEAN
+      if (!strncmp(endpoint, "soap.udp:", 9))
+        soap->omode |= SOAP_IO_UDP;
+#endif
+      soap->socket = soap->fopen(soap, endpoint, soap->host, soap->port);
+      if (soap->error)
+        return soap->error;
+      soap->keep_alive = ((soap->omode & SOAP_IO_KEEPALIVE) != 0);
+    }
+  }
+  count = soap_count_attachments(soap);
+  if (soap_begin_send(soap))
+    return soap->error;
+  if (http_command != SOAP_POST)
+  { soap->mode &= ~SOAP_IO;
+    soap->mode |= SOAP_IO_BUFFER;
+  }
+#ifndef WITH_NOHTTP
+  soap->action = soap_strdup(soap, action);
+  if ((soap->mode & SOAP_IO) != SOAP_IO_STORE && !(soap->mode & SOAP_ENC_XML) && endpoint)
+  { unsigned int k = soap->mode;
+    soap->mode &= ~(SOAP_IO | SOAP_ENC_ZLIB);
+    if ((k & SOAP_IO) != SOAP_IO_FLUSH)
+      soap->mode |= SOAP_IO_BUFFER;
+    if ((soap->error = soap->fpost(soap, endpoint, soap->host, soap->port, soap->path, action, count)))
+      return soap->error;
+#ifndef WITH_LEANER
+    if ((k & SOAP_IO) == SOAP_IO_CHUNK)
+    { if (soap_flush(soap))
+        return soap->error;
+    }
+#endif
+    soap->mode = k;
+  }
+  if (http_command != SOAP_POST)
+    return soap_end_send(soap);
+#endif
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+char*
+SOAP_FMAC2
+soap_s2base64(struct soap *soap, const unsigned char *s, char *t, int n)
+{ register int i;
+  register unsigned long m;
+  register char *p;
+  if (!t)
+    t = (char*)soap_malloc(soap, (n + 2) / 3 * 4 + 1);
+  if (!t)
+    return NULL;
+  p = t;
+  t[0] = '\0';
+  if (!s)
+    return p;
+  for (; n > 2; n -= 3, s += 3)
+  { m = s[0];
+    m = (m << 8) | s[1];
+    m = (m << 8) | s[2];
+    for (i = 4; i > 0; m >>= 6)
+      t[--i] = soap_base64o[m & 0x3F];
+    t += 4;
+  }
+  t[0] = '\0';
+  if (n > 0)
+  { m = 0;
+    for (i = 0; i < n; i++)
+      m = (m << 8) | *s++;
+    for (; i < 3; i++)
+      m <<= 8;
+    for (i++; i > 0; m >>= 6)
+      t[--i] = soap_base64o[m & 0x3F];
+    for (i = 3; i > n; i--)
+      t[i] = '=';
+    t[4] = '\0';
+  }
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_base642s(struct soap *soap, const char *s, char *t, size_t l, int *n)
+{ register int i, j, c;
+  register unsigned long m;
+  register const char *p;
+  if (!s || !*s)
+  { if (n)
+      *n = 0;
+    if (soap->error)
+      return NULL;
+    return SOAP_NON_NULL;
+  }
+  if (!t)
+  { l = (strlen(s) + 3) / 4 * 3;
+    t = (char*)soap_malloc(soap, l);
+  }
+  if (!t)
+    return NULL;
+  p = t;
+  if (n)
+    *n = 0;
+  for (;;)
+  { for (i = 0; i < SOAP_BLKLEN; i++)
+    { m = 0;
+      j = 0;
+      while (j < 4)
+      { c = *s++;
+        if (c == '=' || !c)
+        { i *= 3;
+          switch (j)
+          { case 2:
+              *t++ = (char)((m >> 4) & 0xFF);
+              i++;
+              break;
+            case 3:
+              *t++ = (char)((m >> 10) & 0xFF);
+              *t++ = (char)((m >> 2) & 0xFF);
+              i += 2;
+          }
+          if (n)
+            *n += i;
+          return p;
+        }
+        c -= '+';
+        if (c >= 0 && c <= 79)
+        { int b = soap_base64i[c];
+          if (b >= 64)
+          { soap->error = SOAP_TYPE;
+            return NULL;  
+          }
+          m = (m << 6) + b;
+          j++;
+        }
+        else if (!soap_blank(c + '+'))
+        { soap->error = SOAP_TYPE;
+          return NULL;  
+        }
+      }
+      *t++ = (char)((m >> 16) & 0xFF);
+      *t++ = (char)((m >> 8) & 0xFF);
+      *t++ = (char)(m & 0xFF);
+      if (l < 3)
+      { if (n)
+          *n += i;
+        return p;
+      }
+      l -= 3;
+    }
+    if (n)
+      *n += 3 * SOAP_BLKLEN;
+  }
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+char*
+SOAP_FMAC2
+soap_s2hex(struct soap *soap, const unsigned char *s, char *t, int n)
+{ register char *p;
+  if (!t)
+    t = (char*)soap_malloc(soap, 2 * n + 1);
+  if (!t)
+    return NULL;
+  p = t;
+  t[0] = '\0';
+  if (s)
+  { for (; n > 0; n--)
+    { register int m = *s++;
+      *t++ = (char)((m >> 4) + (m > 159 ? 'a' - 10 : '0'));
+      m &= 0x0F;
+      *t++ = (char)(m + (m > 9 ? 'a' - 10 : '0'));
+    }
+  }
+  *t++ = '\0';
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+SOAP_FMAC1
+const char*
+SOAP_FMAC2
+soap_hex2s(struct soap *soap, const char *s, char *t, size_t l, int *n)
+{ register const char *p;
+  if (!s || !*s)
+  { if (n)
+      *n = 0;
+    if (soap->error)
+      return NULL;
+    return SOAP_NON_NULL;
+  }
+  if (!t)
+  { l = strlen(s) / 2;
+    t = (char*)soap_malloc(soap, l);
+  }
+  if (!t)
+    return NULL;
+  p = t;
+  while (l)
+  { register int d1, d2;
+    d1 = *s++;
+    if (!d1)
+      break;
+    d2 = *s++;
+    if (!d2)
+      break;
+    *t++ = ((d1 >= 'A' ? (d1 & 0x7) + 9 : d1 - '0') << 4) + (d2 >= 'A' ? (d2 & 0x7) + 9 : d2 - '0');
+    l--;
+  }
+  if (n)
+    *n = (int)(t - p);
+  return p;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_puthttphdr(struct soap *soap, int status, size_t count)
+{ if (soap->status != SOAP_GET)
+  { register const char *s = "text/xml; charset=utf-8";
+    register int err = SOAP_OK;
+#ifndef WITH_LEANER
+    register const char *r = NULL;
+#endif
+    if (status == SOAP_FILE && soap->http_content)
+      s = soap->http_content;
+    else if (status == SOAP_HTML)
+      s = "text/html; charset=utf-8";
+    else if (count || ((soap->omode & SOAP_IO) == SOAP_IO_CHUNK))
+    { if (soap->version == 2)
+        s = "application/soap+xml; charset=utf-8";
+    }
+#ifndef WITH_LEANER
+    if (soap->mode & (SOAP_ENC_DIME | SOAP_ENC_MTOM))
+    { if (soap->mode & SOAP_ENC_MTOM)
+      { r = s;
+        s = "application/xop+xml; charset=utf-8";
+      }
+      else
+        s = "application/dime";
+    }
+    if ((soap->mode & SOAP_ENC_MIME) && soap->mime.boundary && strlen(soap->mime.boundary) + strlen(soap->mime.start ? soap->mime.start : SOAP_STR_EOS) < sizeof(soap->tmpbuf) - 80)
+    { register const char *t = strchr(s, ';');
+      sprintf(soap->tmpbuf, "multipart/related; boundary=\"%s\"; type=\"", soap->mime.boundary);
+      if (t)
+      { strncat(soap->tmpbuf, s, t - s);
+        soap->tmpbuf[sizeof(soap->tmpbuf)-1] = '\0';
+      }
+      else
+        strcat(soap->tmpbuf, s);
+      if (soap->mime.start)
+      { strcat(soap->tmpbuf, "\"; start=\"");
+        strcat(soap->tmpbuf, soap->mime.start);
+      }
+      strcat(soap->tmpbuf, "\"");
+      if (r)
+      { strcat(soap->tmpbuf, "; start-info=\"");
+        strcat(soap->tmpbuf, r);
+        strcat(soap->tmpbuf, "\"");
+      }
+      s = soap->tmpbuf;
+    }
+#endif
+    if (s && (err = soap->fposthdr(soap, "Content-Type", s)))
+      return err;
+#ifdef WITH_ZLIB
+    if ((soap->omode & SOAP_ENC_ZLIB))
+    { 
+#ifdef WITH_GZIP
+      err = soap->fposthdr(soap, "Content-Encoding", soap->zlib_out == SOAP_ZLIB_DEFLATE ? "deflate" : "gzip");
+#else
+      err = soap->fposthdr(soap, "Content-Encoding", "deflate");
+#endif
+      if (err)
+        return err;
+    }
+#endif
+#ifndef WITH_LEANER
+    if ((soap->omode & SOAP_IO) == SOAP_IO_CHUNK)
+      err = soap->fposthdr(soap, "Transfer-Encoding", "chunked");
+    else
+#endif
+    if (s)
+    { sprintf(soap->tmpbuf, "%lu", (unsigned long)count);
+      err = soap->fposthdr(soap, "Content-Length", soap->tmpbuf);
+    }
+    if (err)
+      return err;
+  }
+  return soap->fposthdr(soap, "Connection", soap->keep_alive ? "keep-alive" : "close");
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static int
+http_get(struct soap *soap)
+{ return SOAP_GET_METHOD;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static int
+http_put(struct soap *soap)
+{ return SOAP_PUT_METHOD;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static int
+http_del(struct soap *soap)
+{ return SOAP_DEL_METHOD;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static int
+http_head(struct soap *soap)
+{ return SOAP_HEAD_METHOD;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static int
+http_post(struct soap *soap, const char *endpoint, const char *host, int port, const char *path, const char *action, size_t count)
+{ register const char *s;
+  register int err;
+  if (soap->status == SOAP_GET)
+    s = "GET";
+  else
+    s = "POST";
+#ifdef PALM
+  if (!endpoint || (soap_tag_cmp(endpoint, "http:*") && soap_tag_cmp(endpoint, "https:*") && strncmp(endpoint, "httpg:", 6)) && strncmp(endpoint, "_beam:", 6) && strncmp(endpoint, "_local:", 7) && strncmp(endpoint, "_btobex:", 8))
+#else
+  if (!endpoint || (soap_tag_cmp(endpoint, "http:*") && soap_tag_cmp(endpoint, "https:*") && strncmp(endpoint, "httpg:", 6)))
+#endif
+    return SOAP_OK;
+  if (strlen(endpoint) + strlen(soap->http_version) > sizeof(soap->tmpbuf) - 80)
+    return soap->error = SOAP_EOM;
+  if (soap->proxy_host && soap_tag_cmp(endpoint, "https:*"))
+    sprintf(soap->tmpbuf, "%s %s HTTP/%s", s, endpoint, soap->http_version);
+  else
+    sprintf(soap->tmpbuf, "%s /%s HTTP/%s", s, (*path == '/' ? path + 1 : path), soap->http_version);
+  if ((err = soap->fposthdr(soap, soap->tmpbuf, NULL)))
+    return err;
+#ifdef WITH_OPENSSL
+  if ((soap->ssl && soap->port != 443) || (!soap->ssl && soap->port != 80))
+    sprintf(soap->tmpbuf, "%s:%d", host, port);
+  else
+    strcpy(soap->tmpbuf, host); 
+#else
+  if (port != 80)
+    sprintf(soap->tmpbuf, "%s:%d", host, port);
+  else
+    strcpy(soap->tmpbuf, host); 
+#endif
+  if ((err = soap->fposthdr(soap, "Host", soap->tmpbuf))
+   || (err = soap->fposthdr(soap, "User-Agent", "gSOAP/2.7"))
+   || (err = soap_puthttphdr(soap, SOAP_OK, count)))
+    return err;
+#ifdef WITH_ZLIB
+#ifdef WITH_GZIP
+  if ((err = soap->fposthdr(soap, "Accept-Encoding", "gzip, deflate")))
+#else
+  if ((err = soap->fposthdr(soap, "Accept-Encoding", "deflate")))
+#endif
+    return err;
+#endif
+#ifndef WITH_LEAN
+  if (soap->userid && soap->passwd && strlen(soap->userid) + strlen(soap->passwd) < 761)
+  { sprintf(soap->tmpbuf + 262, "%s:%s", soap->userid, soap->passwd);
+    strcpy(soap->tmpbuf, "Basic ");
+    soap_s2base64(soap, (const unsigned char*)(soap->tmpbuf + 262), soap->tmpbuf + 6, (int)strlen(soap->tmpbuf + 262));
+    if ((err = soap->fposthdr(soap, "Authorization", soap->tmpbuf)))
+      return err;
+  }
+  if (soap->proxy_userid && soap->proxy_passwd && strlen(soap->proxy_userid) + strlen(soap->proxy_passwd) < 761)
+  { sprintf(soap->tmpbuf + 262, "%s:%s", soap->proxy_userid, soap->proxy_passwd);
+    strcpy(soap->tmpbuf, "Basic ");
+    soap_s2base64(soap, (const unsigned char*)(soap->tmpbuf + 262), soap->tmpbuf + 6, (int)strlen(soap->tmpbuf + 262));
+    if ((err = soap->fposthdr(soap, "Proxy-Authorization", soap->tmpbuf)))
+      return err;
+  }
+#endif
+#ifdef WITH_COOKIES
+#ifdef WITH_OPENSSL
+  if (soap_putcookies(soap, host, path, soap->ssl != NULL))
+    return soap->error;
+#else
+  if (soap_putcookies(soap, host, path, 0))
+    return soap->error;
+#endif
+#endif
+  if (soap->status != SOAP_GET && (soap->version == 1 || (action && *action)))
+  { sprintf(soap->tmpbuf, "\"%s\"", action && strlen(action) < sizeof(soap->tmpbuf) - 3 ? action : SOAP_STR_EOS);
+    if ((err = soap->fposthdr(soap, "SOAPAction", soap->tmpbuf)))
+      return err;
+  }
+  return soap->fposthdr(soap, NULL, NULL);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static int
+http_send_header(struct soap *soap, const char *s)
+{ register const char *t;
+  do
+  { t = strchr(s, '\n'); /* disallow \n in HTTP headers */
+    if (!t)
+      t = s + strlen(s);
+    if (soap_send_raw(soap, s, t - s))
+      return soap->error;
+    s = t + 1;
+  } while (*t);
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static int
+http_post_header(struct soap *soap, const char *key, const char *val)
+{ if (key)
+  { if (http_send_header(soap, key))
+      return soap->error;
+    if (val && (soap_send_raw(soap, ": ", 2) || http_send_header(soap, val)))
+      return soap->error;
+  }
+  return soap_send_raw(soap, "\r\n", 2);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+static int
+http_response(struct soap *soap, int status, size_t count)
+{ register int err;
+#ifdef WMW_RPM_IO
+  if (soap->rpmreqid)
+    httpOutputEnable(soap->rpmreqid);
+#endif
+  if (strlen(soap->http_version) > 4)
+    return soap->error = SOAP_EOM;
+  if (!status || status == SOAP_HTML || status == SOAP_FILE)
+  { const char *s;
+    if (count || ((soap->omode & SOAP_IO) == SOAP_IO_CHUNK))
+      s = "200 OK";
+    else
+      s = "202 ACCEPTED";
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Status = %s\n", s));
+#ifdef WMW_RPM_IO
+    if (soap->rpmreqid || soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) /* RPM behaves as if standalone */
+#else
+    if (soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) /* standalone application */
+#endif
+    { sprintf(soap->tmpbuf, "HTTP/%s %s", soap->http_version, s);
+      if ((err = soap->fposthdr(soap, soap->tmpbuf, NULL)))
+        return err;
+    }
+    else if ((err = soap->fposthdr(soap, "Status", s)))
+      return err;
+  }
+  else if (status >= 200 && status < 600)
+  { sprintf(soap->tmpbuf, "HTTP/%s %d %s", soap->http_version, status, http_error(soap, status));
+    if ((err = soap->fposthdr(soap, soap->tmpbuf, NULL)))
+      return err;
+#ifndef WITH_LEAN 
+    if (status == 401)
+    { sprintf(soap->tmpbuf, "Basic realm=\"%s\"", (soap->authrealm && strlen(soap->authrealm) < sizeof(soap->tmpbuf) - 14) ? soap->authrealm : "gSOAP Web Service");
+      if ((err = soap->fposthdr(soap, "WWW-Authenticate", soap->tmpbuf)))
+        return err;
+    }
+    else if ((status >= 301 && status <= 303) || status == 307)
+    { if ((err = soap->fposthdr(soap, "Location", soap->endpoint)))
+        return err;
+    }
+#endif
+  }
+  else
+  { const char *s = *soap_faultcode(soap);
+    if (soap->version == 2 && (!s || !strcmp(s, "SOAP-ENV:Sender")))
+      s = "400 Bad Request";
+    else
+      s = "500 Internal Server Error";
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error %s (status=%d)\n", s, status));
+#ifdef WMW_RPM_IO
+    if (soap->rpmreqid || soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) /* RPM behaves as if standalone */
+#else
+    if (soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) /* standalone application */
+#endif
+    { sprintf(soap->tmpbuf, "HTTP/%s %s", soap->http_version, s);
+      if ((err = soap->fposthdr(soap, soap->tmpbuf, NULL)))
+        return err;
+    }
+    else if ((err = soap->fposthdr(soap, "Status", s)))	/* CGI */
+      return err;
+  }
+  if ((err = soap->fposthdr(soap, "Server", "gSOAP/2.7"))
+   || (err = soap_puthttphdr(soap, status, count)))
+    return err;
+#ifdef WITH_COOKIES
+  if (soap_putsetcookies(soap))
+    return soap->error;
+#endif
+  return soap->fposthdr(soap, NULL, NULL);
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_response(struct soap *soap, int status)
+{ register size_t count;
+  if (!(soap->omode & (SOAP_ENC_XML | SOAP_IO_STORE /* this tests for chunking too */))
+   && (status == SOAP_HTML || status == SOAP_FILE))
+    soap->omode = (soap->omode & ~SOAP_IO) | SOAP_IO_STORE;
+  soap->status = status;
+  count = soap_count_attachments(soap);
+  if (soap_begin_send(soap))
+    return soap->error;
+#ifndef WITH_NOHTTP
+  if ((soap->mode & SOAP_IO) != SOAP_IO_STORE && !(soap->mode & SOAP_ENC_XML))
+  { register int n = soap->mode;
+    soap->mode &= ~(SOAP_IO | SOAP_ENC_ZLIB);
+    if ((n & SOAP_IO) != SOAP_IO_FLUSH)
+      soap->mode |= SOAP_IO_BUFFER;
+    if ((soap->error = soap->fresponse(soap, status, count)))
+      return soap->error;
+#ifndef WITH_LEANER
+    if ((n & SOAP_IO) == SOAP_IO_CHUNK)
+    { if (soap_flush(soap))
+        return soap->error;
+    }
+#endif
+    soap->mode = n;
+  }
+#endif
+  return SOAP_OK;
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_LEAN
+static const char*
+soap_set_validation_fault(struct soap *soap, const char *s, const char *t)
+{ if (*soap->tag)
+    sprintf(soap->msgbuf, "Validation constraint violation: %s%s in element <%s>", s, t?t:SOAP_STR_EOS, soap->tag);
+  else
+    sprintf(soap->msgbuf, "Validation constraint violation: %s%s", s, t?t:SOAP_STR_EOS);
+  return soap->msgbuf;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_set_fault(struct soap *soap)
+{ const char **c = soap_faultcode(soap);
+  const char **s = soap_faultstring(soap);
+  if (soap->fseterror)
+    soap->fseterror(soap, c, s);
+  if (!*c)
+  { if (soap->version == 2)
+      *c = "SOAP-ENV:Sender";
+    else
+      *c = "SOAP-ENV:Client";
+  }
+  if (*s)
+    return;
+  switch (soap->error)
+  {
+#ifndef WITH_LEAN
+    case SOAP_CLI_FAULT:
+      *s = "Client fault";
+      break;
+    case SOAP_SVR_FAULT:
+      *s = "Server fault";
+      break;
+    case SOAP_TAG_MISMATCH:
+      *s = soap_set_validation_fault(soap, "tag name or namespace mismatch", NULL);
+      break;
+    case SOAP_TYPE:
+      *s = soap_set_validation_fault(soap, "data type mismatch ", soap->type);
+      break;
+    case SOAP_SYNTAX_ERROR:
+      *s = "Well-formedness violation";
+      break;
+    case SOAP_NO_TAG:
+      *s = "No XML element tag";
+      break;
+    case SOAP_MUSTUNDERSTAND:
+      *c = "SOAP-ENV:MustUnderstand";
+      sprintf(soap->msgbuf, "The data in element '%s' must be understood but cannot be handled", soap->tag);
+      *s = soap->msgbuf;
+      break;
+    case SOAP_VERSIONMISMATCH:
+      *c = "SOAP-ENV:VersionMismatch";
+      *s = "Invalid SOAP message or SOAP version mismatch";
+      break;
+    case SOAP_DATAENCODINGUNKNOWN:
+      *c = "SOAP-ENV:DataEncodingUnknown";
+      *s = "Unsupported SOAP data encoding";
+      break;
+    case SOAP_NAMESPACE:
+      *s = soap_set_validation_fault(soap, "namespace error", NULL);
+      break;
+    case SOAP_USER_ERROR:
+      *s = "User error";
+      break;
+    case SOAP_FATAL_ERROR:
+      *s = "Fatal error";
+      break;
+    case SOAP_NO_METHOD:
+      sprintf(soap->msgbuf, "Method '%s' not implemented: method name or namespace not recognized", soap->tag);
+      *s = soap->msgbuf;
+      break;
+    case SOAP_NO_DATA:
+      *s = "Data required for operation";
+      break;
+    case SOAP_GET_METHOD:
+      *s = "HTTP GET method not implemented";
+      break;
+    case SOAP_PUT_METHOD:
+      *s = "HTTP PUT method not implemented";
+      break;
+    case SOAP_HEAD_METHOD:
+      *s = "HTTP HEAD method not implemented";
+      break;
+    case SOAP_HTTP_METHOD:
+      *s = "HTTP method not implemented";
+      break;
+    case SOAP_EOM:
+      *s = "Out of memory";
+      break;
+    case SOAP_MOE:
+      *s = "Memory overflow or memory corruption error";
+      break;
+    case SOAP_HDR:
+      *s = "Header line too long";
+      break;
+    case SOAP_IOB:
+      *s = "Array index out of bounds";
+      break;
+    case SOAP_NULL:
+      *s = soap_set_validation_fault(soap, "nil not allowed", NULL);
+      break;
+    case SOAP_DUPLICATE_ID:
+      *s = soap_set_validation_fault(soap, "multiple definitions of id ", soap->id);
+      if (soap->version == 2)
+        *soap_faultsubcode(soap) = "SOAP-ENC:DuplicateID";
+      break;
+    case SOAP_MISSING_ID:
+      *s = soap_set_validation_fault(soap, "missing id for ref ", soap->id);
+      if (soap->version == 2)
+        *soap_faultsubcode(soap) = "SOAP-ENC:MissingID";
+      break;
+    case SOAP_HREF:
+      *s = soap_set_validation_fault(soap, "incompatible object type ref/id pair ", soap->id);
+      break;
+    case SOAP_FAULT:
+      break;
+#ifndef WITH_NOIO
+    case SOAP_UDP_ERROR:
+      *s = "Message too large for UDP packet";
+      break;
+    case SOAP_TCP_ERROR:
+      *s = tcp_error(soap);
+      break;
+#endif
+    case SOAP_HTTP_ERROR:
+      *s = "An HTTP processing error occurred";
+      break;
+    case SOAP_SSL_ERROR:
+#ifdef WITH_OPENSSL
+      *s = "SSL error";
+#else
+      *s = "OpenSSL not installed: recompile with -DWITH_OPENSSL";
+#endif
+      break;
+    case SOAP_PLUGIN_ERROR:
+      *s = "Plugin registry error";
+      break;
+    case SOAP_DIME_ERROR:
+      *s = "DIME format error";
+      break;
+    case SOAP_DIME_HREF:
+      *s = "DIME href to missing attachment";
+      break;
+    case SOAP_DIME_MISMATCH:
+      *s = "DIME version/transmission error";
+      break;
+    case SOAP_DIME_END:
+      *s = "End of DIME error";
+      break;
+    case SOAP_MIME_ERROR:
+      *s = "MIME format error";
+      break;
+    case SOAP_MIME_HREF:
+      *s = "MIME href to missing attachment";
+      break;
+    case SOAP_MIME_END:
+      *s = "End of MIME error";
+      break;
+    case SOAP_ZLIB_ERROR:
+#ifdef WITH_ZLIB
+      sprintf(soap->msgbuf, "Zlib/gzip error: '%s'", soap->d_stream->msg?soap->d_stream->msg:SOAP_STR_EOS);
+      *s = soap->msgbuf;
+#else
+      *s = "Zlib/gzip not installed for (de)compression: recompile with -DWITH_GZIP";
+#endif
+      break;
+    case SOAP_REQUIRED:
+      *s = soap_set_validation_fault(soap, "missing required attribute", NULL);
+      break;
+    case SOAP_PROHIBITED:
+      *s = soap_set_validation_fault(soap, "prohibited attribute present", NULL);
+      break;
+    case SOAP_OCCURS:
+      *s = soap_set_validation_fault(soap, "occurrence violation", NULL);
+      break;
+    case SOAP_LENGTH:
+      *s = soap_set_validation_fault(soap, "content length violation", NULL);
+      break;
+    case SOAP_FD_EXCEEDED:
+      *s = "Maximum number of open connections was reached";
+      break;
+    case SOAP_STOP:
+      *s = "Stopped: no response sent";
+      break;
+#endif
+    case SOAP_EOF:
+#ifndef WITH_NOIO
+      strcpy(soap->msgbuf, soap_strerror(soap));
+#ifndef WITH_LEAN
+      if (strlen(soap->msgbuf) + 25 < sizeof(soap->msgbuf))
+      { memmove(soap->msgbuf + 25, soap->msgbuf, strlen(soap->msgbuf) + 1);
+        memcpy(soap->msgbuf, "End of file or no input: ", 25);
+      }
+#endif
+      *s = soap->msgbuf;
+      break;
+#else
+      *s = "End of file or no input";
+      break;
+#endif
+    default:
+#ifndef WITH_NOHTTP
+#ifndef WITH_LEAN
+      if (soap->error > 200 && soap->error < 600)
+      { sprintf(soap->msgbuf, "HTTP Error: %d %s", soap->error, http_error(soap, soap->error));
+        *s = soap->msgbuf;
+      }
+      else
+#endif
+#endif
+      { sprintf(soap->msgbuf, "Error %d", soap->error);
+        *s = soap->msgbuf;
+      }
+    }
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_send_fault(struct soap *soap)
+{ register int status = soap->error;
+  int r = 1;
+  if (status == SOAP_STOP)
+    return status;
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Sending back fault struct for error code %d\n", soap->error));
+  soap->keep_alive = 0; /* to terminate connection */
+  soap_set_fault(soap);
+#ifndef WITH_NOIO
+#ifndef WITH_LEAN
+  if (soap_valid_socket(soap->socket))
+  { struct timeval timeout;
+    fd_set rfd, sfd;
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 0;
+    FD_ZERO(&rfd);
+    FD_ZERO(&sfd);
+    FD_SET(soap->socket, &rfd);
+    FD_SET(soap->socket, &sfd);
+    r = select((int)soap->socket + 1, &rfd, &sfd, NULL, &timeout);
+    if (r > 0)
+    { if (!FD_ISSET(soap->socket, &sfd)
+       || (FD_ISSET(soap->socket, &rfd)
+        && recv(soap->socket, soap->tmpbuf, 1, MSG_PEEK) < 0))
+        r = 0;
+    }
+  }
+#endif
+#endif
+  if ((status != SOAP_EOF || (!soap->recv_timeout && !soap->send_timeout)) && r > 0)
+  { soap->error = SOAP_OK;
+    soap_serializeheader(soap);
+    soap_serializefault(soap);
+    soap_begin_count(soap);
+    if (soap->mode & SOAP_IO_LENGTH)
+    { soap_envelope_begin_out(soap);
+      soap_putheader(soap);
+      soap_body_begin_out(soap);
+      soap_putfault(soap);
+      soap_body_end_out(soap);
+      soap_envelope_end_out(soap);
+    }
+    soap_end_count(soap);
+    if (soap_response(soap, status)
+     || soap_envelope_begin_out(soap)
+     || soap_putheader(soap)
+     || soap_body_begin_out(soap)
+     || soap_putfault(soap)
+     || soap_body_end_out(soap)
+     || soap_envelope_end_out(soap))
+      return soap_closesock(soap);
+    soap_end_send(soap);
+  }
+  soap->error = status;
+  return soap_closesock(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_recv_fault(struct soap *soap)
+{ register int status = soap->error;
+  DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Receiving SOAP Fault\n"));
+  soap->error = SOAP_OK;
+  if (soap_getfault(soap))
+  { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Error: soap_get_soapfault() failed. Is this a SOAP message at all?\n"));
+    *soap_faultcode(soap) = (soap->version == 2 ? "SOAP-ENV:Sender" : "SOAP-ENV:Client");
+    soap->error = status;
+    soap_set_fault(soap);
+  }
+  else
+  { register const char *s = *soap_faultcode(soap);
+    if (!soap_match_tag(soap, s, "SOAP-ENV:Server") || !soap_match_tag(soap, s, "SOAP-ENV:Receiver"))
+      status = SOAP_SVR_FAULT; 
+    else if (!soap_match_tag(soap, s, "SOAP-ENV:Client") || !soap_match_tag(soap, s, "SOAP-ENV:Sender"))
+      status = SOAP_CLI_FAULT;
+    else if (!soap_match_tag(soap, s, "SOAP-ENV:MustUnderstand"))
+      status = SOAP_MUSTUNDERSTAND;
+    else if (!soap_match_tag(soap, s, "SOAP-ENV:VersionMismatch"))
+      status = SOAP_VERSIONMISMATCH;
+    else
+    { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Fault code %s\n", s));
+      status = SOAP_FAULT;
+    }
+    if (soap_body_end_in(soap)
+     || soap_envelope_end_in(soap)
+     || soap_end_recv(soap))
+      return soap_closesock(soap);
+    soap->error = status;
+  }
+  return soap_closesock(soap);
+}
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_send_empty_response(struct soap *soap, int httpstatuscode)
+{ register soap_mode m = soap->omode;
+  soap->count = 0;
+  if ((m & SOAP_IO) == SOAP_IO_CHUNK)
+    soap->omode = (m & ~SOAP_IO) | SOAP_IO_BUFFER;
+  if (soap_response(soap, httpstatuscode) || soap_end_send(soap))
+  { soap->omode = m;
+    return soap_closesock(soap);
+  }
+  soap->omode = m;
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOHTTP
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_recv_empty_response(struct soap *soap)
+{ if (soap_begin_recv(soap) || soap_end_recv(soap))
+  { if (soap->error != 202)
+      return soap_closesock(soap);
+    soap->error = SOAP_OK;
+  }
+  return SOAP_OK;
+}
+#endif
+#endif
+
+/******************************************************************************/
+#ifndef WITH_NOIO
+#ifndef PALM_1
+static const char*
+soap_strerror(struct soap *soap)
+{ register int err = soap->errnum;
+  if (err)
+  {
+#ifndef WIN32
+    return strerror(err);
+#else
+#ifndef UNDER_CE
+    DWORD len;
+    *soap->msgbuf = '\0';
+    len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)soap->msgbuf, (DWORD)sizeof(soap->msgbuf), NULL);
+#else
+    DWORD i, len;
+    *soap->msgbuf = '\0';
+    len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, (LPTSTR)soap->msgbuf, (DWORD)(sizeof(soap->msgbuf)/sizeof(TCHAR)), NULL);
+    for (i = 0; i <= len; i++)
+    { if (((TCHAR*)soap->msgbuf)[i] < 0x80)
+        soap->msgbuf[i] = (char)((TCHAR*)soap->msgbuf)[i];
+      else
+        soap->msgbuf[i] = '?';
+    }
+#endif
+    return soap->msgbuf;
+#endif
+  }
+#ifndef WITH_LEAN
+  if (soap->recv_timeout > 0)
+  { if (soap->send_timeout > 0)
+      sprintf(soap->msgbuf, "Operation interrupted or timed out after %ds send or %ds receive delay", soap->send_timeout, soap->recv_timeout);
+    else
+      sprintf(soap->msgbuf, "Operation interrupted or timed out after %ds receive delay", soap->recv_timeout);
+    return soap->msgbuf;
+  }
+#endif
+  return "Operation interrupted or timed out";
+}
+#endif
+#endif 
+
+/******************************************************************************/
+#ifndef PALM_2
+static int
+soap_set_error(struct soap *soap, const char *faultcode, const char *faultsubcodeQName, const char *faultstring, const char *faultdetailXML, int soaperror)
+{ *soap_faultcode(soap) = faultcode;
+  if (faultsubcodeQName)
+    *soap_faultsubcode(soap) = faultsubcodeQName;
+  *soap_faultstring(soap) = faultstring;
+  if (faultdetailXML && *faultdetailXML)
+  { register const char **s = soap_faultdetail(soap);
+    if (s)
+      *s = faultdetailXML;
+  }
+  return soap->error = soaperror;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_set_sender_error(struct soap *soap, const char *faultstring, const char *faultdetailXML, int soaperror)
+{ return soap_set_error(soap, soap->version == 2 ? "SOAP-ENV:Sender" : "SOAP-ENV:Client", NULL, faultstring, faultdetailXML, soaperror);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_set_receiver_error(struct soap *soap, const char *faultstring, const char *faultdetailXML, int soaperror)
+{ return soap_set_error(soap, soap->version == 2 ? "SOAP-ENV:Receiver" : "SOAP-ENV:Server", NULL, faultstring, faultdetailXML, soaperror);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+static int
+soap_copy_fault(struct soap *soap, const char *faultcode, const char *faultsubcodeQName, const char *faultstring, const char *faultdetailXML)
+{ char *r = NULL, *s = NULL, *t = NULL;
+  if (faultsubcodeQName)
+    r = soap_strdup(soap, faultsubcodeQName);
+  if (faultstring)
+    s = soap_strdup(soap, faultstring);
+  if (faultdetailXML)
+    t = soap_strdup(soap, faultdetailXML);
+  return soap_set_error(soap, faultcode, r, s, t, SOAP_FAULT);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_sender_fault(struct soap *soap, const char *faultstring, const char *faultdetailXML)
+{ return soap_sender_fault_subcode(soap, NULL, faultstring, faultdetailXML);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_sender_fault_subcode(struct soap *soap, const char *faultsubcodeQName, const char *faultstring, const char *faultdetailXML)
+{ return soap_copy_fault(soap, soap->version == 2 ? "SOAP-ENV:Sender" : "SOAP-ENV:Client", faultsubcodeQName, faultstring, faultdetailXML);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_receiver_fault(struct soap *soap, const char *faultstring, const char *faultdetailXML)
+{ return soap_receiver_fault_subcode(soap, NULL, faultstring, faultdetailXML);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_receiver_fault_subcode(struct soap *soap, const char *faultsubcodeQName, const char *faultstring, const char *faultdetailXML)
+{ return soap_copy_fault(soap, soap->version == 2 ? "SOAP-ENV:Receiver" : "SOAP-ENV:Server", faultsubcodeQName, faultstring, faultdetailXML);
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+#ifndef WITH_NOSTDLIB
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_print_fault(struct soap *soap, FILE *fd)
+{ if (soap_check_state(soap))
+    fprintf(fd, "Error: soap struct state not initialized\n");
+  else if (soap->error)
+  { const char *c, *v = NULL, *s, **d;
+    d = soap_faultcode(soap);
+    if (!*d)
+      soap_set_fault(soap);
+    c = *d;
+    if (soap->version == 2)
+      v = *soap_faultsubcode(soap);
+    s = *soap_faultstring(soap);
+    d = soap_faultdetail(soap);
+    fprintf(fd, "%s%d fault: %s [%s]\n\"%s\"\nDetail: %s\n", soap->version ? "SOAP 1." : "Error ", soap->version ? (int)soap->version : soap->error, c, v ? v : "no subcode", s ? s : "[no reason]", d && *d ? *d : "[no detail]");
+  }
+}
+#endif
+#endif
+ 
+/******************************************************************************/
+#ifdef __cplusplus
+#ifndef WITH_LEAN
+#ifndef WITH_NOSTDLIB
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_stream_fault(struct soap *soap, std::ostream& os)
+{ if (soap_check_state(soap))
+    os << "Error: soap struct state not initialized\n";
+  else if (soap->error)
+  { const char *c, *v = NULL, *s, **d;
+    d = soap_faultcode(soap);
+    if (!*d)
+      soap_set_fault(soap);
+    c = *d;
+    if (soap->version == 2)
+      v = *soap_faultsubcode(soap);
+    s = *soap_faultstring(soap);
+    d = soap_faultdetail(soap);
+    os << (soap->version ? "SOAP 1." : "Error ")
+       << (soap->version ? (int)soap->version : soap->error)
+       << " fault: " << c
+       << "[" << (v ? v : "no subcode") << "]"
+       << std::endl
+       << "\"" << (s ? s : "[no reason]") << "\""
+       << std::endl
+       << "Detail: " << (d && *d ? *d : "[no detail]")
+       << std::endl;
+  }
+}
+#endif
+#endif
+#endif
+ 
+/******************************************************************************/
+#ifndef WITH_LEAN
+#ifndef WITH_NOSTDLIB
+SOAP_FMAC1
+char*
+SOAP_FMAC2
+soap_sprint_fault(struct soap *soap, char *buf, size_t len)
+{ if (soap_check_state(soap))
+    strncpy(buf, "Error: soap struct not initialized", len);
+  else if (soap->error)
+  { const char *c, *v = NULL, *s, **d;
+    d = soap_faultcode(soap);
+    if (!*d)
+      soap_set_fault(soap);
+    c = *d;
+    if (soap->version == 2)
+      v = *soap_faultsubcode(soap);
+    s = *soap_faultstring(soap);
+    d = soap_faultdetail(soap);
+#ifdef WIN32
+    _snprintf
+#else
+    snprintf
+#endif
+      (buf, len, "%s%d fault: %s [%s]\n\"%s\"\nDetail: %s\n", soap->version ? "SOAP 1." : "Error ", soap->version ? (int)soap->version : soap->error, c, v ? v : "no subcode", s ? s : "[no reason]", d && *d ? *d : "[no detail]");
+  }
+  return buf;
+}
+#endif
+#endif
+ 
+/******************************************************************************/
+#ifndef PALM_1
+#ifndef WITH_NOSTDLIB
+SOAP_FMAC1
+void
+SOAP_FMAC2
+soap_print_fault_location(struct soap *soap, FILE *fd)
+{ 
+#ifndef WITH_LEAN
+  int i, j, c1, c2;
+  if (soap->error && soap->bufidx <= soap->buflen && soap->buflen > 0 && soap->buflen <= SOAP_BUFLEN)
+  { i = (int)soap->bufidx - 1;
+    if (i <= 0)
+      i = 0;
+    c1 = soap->buf[i];
+    soap->buf[i] = '\0';
+    if ((int)soap->buflen >= i + 1024)
+      j = i + 1023;
+    else
+      j = (int)soap->buflen - 1;
+    c2 = soap->buf[j];
+    soap->buf[j] = '\0';
+    fprintf(fd, "%s%c\n<!-- ** HERE ** -->\n", soap->buf, c1);
+    if (soap->bufidx < soap->buflen)
+      fprintf(fd, "%s\n", soap->buf + soap->bufidx);
+    soap->buf[i] = c1;
+    soap->buf[j] = c2;
+  }
+#endif
+}
+#endif
+#endif
+ 
+/******************************************************************************/
+#ifndef PALM_1
+SOAP_FMAC1
+int
+SOAP_FMAC2
+soap_register_plugin_arg(struct soap *soap, int (*fcreate)(struct soap*, struct soap_plugin*, void*), void *arg)
+{ register struct soap_plugin *p;
+  register int r;
+  if (!(p = (struct soap_plugin*)SOAP_MALLOC(soap, sizeof(struct soap_plugin))))
+    return soap->error = SOAP_EOM;
+  p->id = NULL;
+  p->data = NULL;
+  p->fcopy = NULL;
+  p->fdelete = NULL;
+  r = fcreate(soap, p, arg);
+  if (!r && p->fdelete)
+  { p->next = soap->plugins;
+    soap->plugins = p;
+    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Registered '%s' plugin\n", p->id));
+    return SOAP_OK;
+  }
+  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not register plugin '%s': plugin returned error %d (or fdelete callback not set)\n", p->id?p->id:"?", r));
+  SOAP_FREE(soap, p);
+  return r;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_1
+static void *
+fplugin(struct soap *soap, const char *id)
+{ register struct soap_plugin *p;
+  for (p = soap->plugins; p; p = p->next)
+    if (p->id == id || !strcmp(p->id, id))
+      return p->data;
+  return NULL;
+}
+#endif
+
+/******************************************************************************/
+#ifndef PALM_2
+SOAP_FMAC1
+void *
+SOAP_FMAC2
+soap_lookup_plugin(struct soap *soap, const char *id)
+{ return soap->fplugin(soap, id);
+}
+#endif
+
+/******************************************************************************/
+#ifdef __cplusplus
+}
+#endif
+
+/******************************************************************************\
+ *
+ *	C++ soap struct methods
+ *
+\******************************************************************************/
+
+#ifdef __cplusplus
+soap::soap()
+{ soap_init(this);
+}
+#endif
+
+/******************************************************************************/
+#ifdef __cplusplus
+soap::soap(soap_mode m)
+{ soap_init1(this, m);
+}
+#endif
+
+/******************************************************************************/
+#ifdef __cplusplus
+soap::soap(soap_mode im, soap_mode om)
+{ soap_init2(this, im, om);
+}
+#endif
+
+/******************************************************************************/
+#ifdef __cplusplus
+soap::soap(struct soap& soap)
+{ soap_copy_context(this, &soap);
+}
+#endif
+
+/******************************************************************************/
+#ifdef __cplusplus
+soap::~soap()
+{ soap_destroy(this);
+  soap_end(this);
+  soap_done(this);
+}
+#endif
+
+/******************************************************************************/
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/stdsoap2.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/stdsoap2.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/stdsoap2.h	(revision 22322)
@@ -0,0 +1,2304 @@
+/*
+	stdsoap2.h 2.7.11-upd
+
+	gSOAP runtime engine
+
+gSOAP XML Web services tools
+Copyright (C) 2000-2008, Robert van Engelen, Genivia Inc., All Rights Reserved.
+This part of the software is released under ONE of the following licenses:
+GPL, the gSOAP public license, OR Genivia's license for commercial use.
+--------------------------------------------------------------------------------
+Contributors:
+
+Wind River Systems, Inc., for the following additions
+  - vxWorks compatible
+--------------------------------------------------------------------------------
+gSOAP public license.
+
+The contents of this file are subject to the gSOAP Public License Version 1.3
+(the "License"); you may not use this file except in compliance with the
+License. You may obtain a copy of the License at
+http://www.cs.fsu.edu/~engelen/soaplicense.html
+Software distributed under the License is distributed on an "AS IS" basis,
+WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+for the specific language governing rights and limitations under the License.
+
+The Initial Developer of the Original Code is Robert A. van Engelen.
+Copyright (C) 2000-2008, Robert van Engelen, Genivia Inc., All Rights Reserved.
+--------------------------------------------------------------------------------
+GPL license.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 USA
+
+Author contact information:
+engelen@genivia.com / engelen@acm.org
+
+This program is released under the GPL with the additional exemption that
+compiling, linking, and/or using OpenSSL is allowed.
+--------------------------------------------------------------------------------
+A commercial use license is available from Genivia, Inc., contact@genivia.com
+--------------------------------------------------------------------------------
+*/
+
+#ifdef WITH_SOAPDEFS_H
+# include "soapdefs.h"		/* include user-defined stuff */
+#endif
+
+#ifndef _THREAD_SAFE
+# define _THREAD_SAFE
+#endif
+
+#ifndef OPENSERVER
+# ifndef _REENTRANT
+#  define _REENTRANT
+# endif
+#endif
+
+#ifndef SOAP_FMAC1	/* stdsoap2.h declaration macro */
+# define SOAP_FMAC1
+#endif
+
+#ifndef SOAP_FMAC2	/* stdsoap2.h declaration macro */
+# define SOAP_FMAC2
+#endif
+
+#ifndef SOAP_FMAC3	/* (de)serializer declaration macro */
+# define SOAP_FMAC3
+#endif
+
+#ifndef SOAP_FMAC3S	/* string converter for (de)serializer declaration macro */
+# define SOAP_FMAC3S SOAP_FMAC3
+#endif
+
+#ifndef SOAP_FMAC4	/* (de)serializer declaration macro */
+# define SOAP_FMAC4
+#endif
+
+#ifndef SOAP_FMAC4S	/* string converter for (de)serializer declaration macro */
+# define SOAP_FMAC4S SOAP_FMAC4
+#endif
+
+#ifndef SOAP_FMAC5	/* stub/skeleton declaration macro */
+# define SOAP_FMAC5
+#endif
+
+#ifndef SOAP_FMAC6	/* stub/skeleton declaration macro */
+# define SOAP_FMAC6
+#endif
+
+#ifndef SOAP_CMAC	/* class declaration macro */
+# define SOAP_CMAC
+#endif
+
+#ifndef SOAP_NMAC	/* namespace table declaration macro */
+# define SOAP_NMAC
+#endif
+
+#ifndef SOAP_SOURCE_STAMP
+# define SOAP_SOURCE_STAMP(str)
+#endif
+
+/* gSOAP 2.7.4 and higher: fast look-aside buffering is stable */
+#ifndef WITH_FAST
+# define WITH_FAST
+#endif
+
+#ifdef WITH_LEANER
+# ifndef WITH_LEAN
+#  define WITH_LEAN
+# endif
+#endif
+
+#ifdef WITH_LEAN
+# ifdef WITH_COOKIES
+#  error "Cannot build WITH_LEAN code WITH_COOKIES enabled"
+# endif
+#endif
+
+#ifndef STDSOAP_H
+#define STDSOAP_H
+
+#if defined(__vxworks) || defined(__VXWORKS__)
+# define VXWORKS
+#endif
+
+#ifdef _WIN32
+# ifndef WIN32
+#  define WIN32
+# endif
+#endif
+
+#ifdef _WIN32_WCE
+# ifndef UNDER_CE
+#  define UNDER_CE _WIN32_WCE
+# endif
+#endif
+
+#ifdef UNDER_CE
+# ifndef WIN32
+#  define WIN32
+# endif
+#endif
+
+#ifdef __BORLANDC__
+# ifdef __WIN32__
+#  ifndef WIN32
+#   define WIN32
+#  endif
+# endif
+#endif
+
+#ifdef __CYGWIN__
+# ifndef CYGWIN
+#  define CYGWIN
+# endif
+#endif
+
+#ifdef __SYMBIAN32__ 
+# define SYMBIAN
+# undef WIN32
+#endif
+
+#if defined(__palmos__) || defined(PALM_GCC) || defined(__PALMOS_TRAPS__)
+# ifndef PALM
+#  define PALM
+# endif
+#endif
+
+#if defined(__hpux)
+# ifndef HP_UX
+#  define HP_UX
+# endif
+#endif
+
+#if defined(__digital__) && defined(__unix__)
+# ifndef TRU64
+#  define TRU64 
+# endif
+#endif
+
+#ifdef __MVS__
+# ifndef OS390
+#  define OS390
+# endif
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# if defined(WITH_OPENSSL)
+#  ifndef HAVE_OPENSSL_SSL_H
+#   undef WITH_OPENSSL
+#  endif
+# endif
+# if defined(WITH_ZLIB) || defined(WITH_GZIP)
+#  ifndef HAVE_ZLIB_H
+#   undef WITH_ZLIB
+#   undef WITH_GZIP
+#  endif
+# endif
+#else
+# if defined(UNDER_CE)
+#  define WITH_NOEMPTYSTRUCT
+#  define WITH_LEAN
+#  define HAVE_SSCANF
+# elif defined(WIN32)
+#  define WITH_NOEMPTYSTRUCT
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_FTIME
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+#  define SOAP_LONG_FORMAT "%I64d"
+#  define SOAP_ULONG_FORMAT "%I64u"
+# elif defined(CYGWIN)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_FTIME
+#  define HAVE_RAND_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+# elif defined(__APPLE__)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOD_L
+#  define HAVE_SSCANF_L
+#  define HAVE_SPRINTF_L
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_RAND_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_TIMEGM
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+# elif defined(_AIX43)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_FTIME
+#  define HAVE_RAND_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+# elif defined(_AIX41)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+# elif defined(HP_UX)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_FTIME
+#  define HAVE_RAND_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+# elif defined(FREEBSD) || defined(__FreeBSD__)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOD_L
+#  define HAVE_SSCANF_L
+#  define HAVE_SPRINTF_L
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_STRTOLL
+#  define HAVE_STRTOULL
+#  define HAVE_GETTIMEOFDAY
+#  define HAVE_RAND_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+#  define SOAP_LONG_FORMAT "%qd"
+#  define SOAP_ULONG_FORMAT "%qu"
+# elif defined(__VMS)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_FTIME
+#  define HAVE_RAND_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+# elif defined(__GLIBC__) || defined(__GNU__)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOD_L
+#  define HAVE_SSCANF_L
+#  define HAVE_SPRINTF_L
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_STRTOLL
+#  define HAVE_STRTOULL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_FTIME
+#  define HAVE_RAND_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_TIMEGM
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+#  define HAVE_ISNAN
+# elif defined(TRU64)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_GETTIMEOFDAY
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_RAND_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define __USE_STD_IOSTREAM
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+#  define SOAP_LONG_FORMAT "%ld"
+#  define SOAP_ULONG_FORMAT "%lu"
+# elif defined(MAC_CARBON)
+#  define WITH_NOIO
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOD_L
+#  define HAVE_SSCANF_L
+#  define HAVE_SPRINTF_L
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_FTIME
+#  define HAVE_RAND_R
+#  define HAVE_GETHOSTBYNAME_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+# elif defined(PALM)
+#  define WITH_LEAN
+#  define HAVE_STRTOD   /* strtod() is defined in palmFunctions.h */
+#  include <stdlib.h>	/* Needs to be included before unix headers */
+#  include <sys_types.h>
+#  define IGNORE_STDIO_STUBS
+#  include <StdIOPalm.h>
+#  define O_NONBLOCK FNONBIO
+#  include <sys_socket.h>
+#  include "palmFunctions.h"
+# elif defined(SYMBIAN)
+#  define WITH_LEAN
+#  define WITH_NONAMESPACES
+#  define HAVE_STRTOD	/* use STRTOD since sscanf doesn't seem to work */
+#  include <e32def.h>
+#  include <sys/ioctl.h>
+# elif defined(VXWORKS)
+#  ifdef _WRS_KERNEL
+#   define _POSIX_THREADS 1
+#  endif
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_GMTIME
+#  define HAVE_LOCALTIME
+#  define HAVE_MKTIME
+# elif defined(OS390)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_FTIME
+#  define HAVE_RAND_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+# elif defined(AS400)
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_FTIME
+#  define HAVE_RAND_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+# elif defined(__QNX__) || defined(QNX)
+/* QNX does not have a working version of strtof */
+#  undef HAVE_STRTOF
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_FTIME
+#  define HAVE_RAND_R
+#  define HAVE_GETHOSTBYNAME_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+#  define LONG64 long
+#  define ULONG64 unsigned LONG64
+#  define SOAP_LONG_FORMAT "%ld"
+#  define SOAP_ULONG_FORMAT "%lu"
+# else
+/* Default asumptions on supported functions */
+#  define HAVE_STRRCHR
+#  define HAVE_STRTOD
+#  define HAVE_SSCANF
+#  define HAVE_STRTOL
+#  define HAVE_STRTOUL
+#  define HAVE_SYS_TIMEB_H
+#  define HAVE_FTIME
+#  define HAVE_RAND_R
+#  define HAVE_GETHOSTBYNAME_R
+#  define HAVE_GMTIME_R
+#  define HAVE_LOCALTIME_R
+#  define HAVE_WCTOMB
+#  define HAVE_MBTOWC
+# endif
+#endif
+
+#ifdef WITH_C_LOCALE
+# include <xlocale.h>
+#else
+# undef HAVE_STRTOF_L
+# undef HAVE_STRTOD_L
+# undef HAVE_SSCANF_L
+# undef HAVE_SPRINTF_L
+#endif
+
+#ifndef WITH_NOSTDLIB
+# include <stdlib.h>
+# ifndef PALM
+#  include <stdio.h>
+#  include <string.h>
+# endif
+# include <ctype.h>
+# include <limits.h>
+#endif
+
+#if defined(__cplusplus) && !defined(WITH_LEAN)
+# include <string>
+# include <iostream>
+#endif
+
+#ifdef WITH_NOHTTP
+# ifndef WITH_NOIO
+#  define WITH_NOIO
+#  undef WITH_COOKIES
+# endif
+#endif
+
+/* Suggestion when SOAP_FD_EXCEEDED error occurs:
+   Some systems allow increasing FD_SETSIZE before including sys/types.h:
+#define FD_SETSIZE (2048)
+*/
+
+#ifndef UNDER_CE
+# ifndef PALM
+#  ifndef WITH_NOIO
+#   include <errno.h>
+#   include <sys/types.h>
+#  endif
+#  ifndef WITH_LEAN
+#   ifdef HAVE_SYS_TIMEB_H
+#    include <sys/timeb.h>		/* for ftime() */
+#   endif
+#   include <time.h>
+#  endif
+# endif
+#endif
+
+#ifdef OPENSERVER
+# include <sys/socket.h>
+# include <sys/stream.h>
+# include <sys/protosw.h>
+  extern int h_errno;
+#endif
+
+#ifndef WITH_NOIO
+# ifndef WIN32
+#  ifndef PALM
+#   include <sys/socket.h>
+#   ifdef VXWORKS
+#    include <sockLib.h>
+#    include <selectLib.h>
+#    ifndef _WRS_KERNEL
+#     include <strings.h>
+#    endif
+#   else
+#    ifndef SYMBIAN
+#     include <strings.h>
+#    endif
+#   endif
+#   ifdef SUN_OS
+#    include <sys/stream.h>		/* SUN */
+#    include <sys/socketvar.h>		/* SUN < 2.8 (?) */
+#   endif
+#   ifdef VXWORKS
+#    ifdef _WRS_KERNEL
+#     include <sys/times.h>
+#    endif
+#   else
+#    include <sys/time.h>
+#   endif
+#   include <netinet/in.h>
+#   ifdef OS390
+#    include <netinet/tcp_var.h>
+#   else
+#    include <netinet/tcp.h>          /* TCP_NODELAY */
+#   endif
+#   include <arpa/inet.h>
+#  endif
+# endif
+#endif
+
+#ifdef WIN32
+# define SOAP_WINSOCKINT int
+#else
+# define SOAP_WINSOCKINT size_t
+#endif
+
+#ifdef WIN32
+# ifndef UNDER_CE
+#  include <io.h>
+#  include <fcntl.h>
+# endif
+# ifdef WITH_IPV6
+#  include <winsock2.h> /* Visual Studio 2005 users: you must install the Platform SDK (R2) */
+#  include <ws2tcpip.h>
+#  include <wspiapi.h>
+#  define SOAP_GAI_STRERROR gai_strerrorA
+# else
+#  include <winsock.h> /* Visual Studio 2005 users: you must install the Platform SDK (R2) */
+/* # include <winsock2.h> */ /* Alternative: use winsock2 (not available with eVC) */
+# endif
+#else
+# ifdef VXWORKS
+#  include <hostLib.h>
+#  include <ioctl.h>
+#  include <ioLib.h>
+# endif
+# ifndef WITH_NOIO
+#  ifndef PALM
+#   include <netdb.h>
+#   include <netinet/in.h>
+#   include <unistd.h>
+#   include <fcntl.h>
+#   ifdef _AIX41
+#    include <sys/select.h>
+#   endif
+#  endif
+# endif
+#endif
+
+#ifdef WITH_FASTCGI
+# include <fcgi_stdio.h>
+#endif
+
+#ifdef WITH_OPENSSL
+# define OPENSSL_NO_KRB5
+# include <openssl/bio.h>
+# include <openssl/err.h>
+# include <openssl/rand.h>
+# include <openssl/ssl.h>
+# include <openssl/x509v3.h>
+# ifndef ALLOW_OLD_VERSIONS
+#  if (OPENSSL_VERSION_NUMBER < 0x00905100L)
+#   error "Must use OpenSSL 0.9.6 or later"
+#  endif
+# endif
+#endif
+
+#ifdef WITH_GZIP
+# ifndef WITH_ZLIB
+#  define WITH_ZLIB
+# endif
+#endif
+
+#ifdef WITH_CASEINSENSITIVETAGS
+# define SOAP_STRCMP soap_tag_cmp	/* case insensitve XML element/attribute names */
+#else
+# define SOAP_STRCMP strcmp		/* case sensitive XML element/attribute names */
+#endif
+
+#ifdef WITH_ZLIB
+# include <zlib.h>
+#endif
+
+#ifndef WITH_NOSTDLIB
+# ifndef PALM
+#  include <math.h>	/* for isnan() */
+# endif
+#endif
+
+/* #define DEBUG */ /* Uncomment to debug sending (in file SENT.log) receiving (in file RECV.log) and messages (in file TEST.log) */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Portability: define SOAP_SOCKLEN_T */
+#if defined(_AIX)
+# if defined(_AIX43)
+#  define SOAP_SOCKLEN_T socklen_t
+# else
+#  define SOAP_SOCKLEN_T int
+# endif
+#elif defined(SOCKLEN_T)
+# define SOAP_SOCKLEN_T SOCKLEN_T
+#elif defined(__socklen_t_defined) || defined(_SOCKLEN_T) || defined(CYGWIN) || defined(FREEBSD) || defined(__FreeBSD__) || defined(__QNX__) || defined(QNX)
+# define SOAP_SOCKLEN_T socklen_t
+#elif defined(IRIX) || defined(WIN32) || defined(__APPLE__) || defined(HP_UX) || defined(SUN_OS) || defined(OPENSERVER) || defined(TRU64) || defined(VXWORKS)
+# define SOAP_SOCKLEN_T int
+#else
+# define SOAP_SOCKLEN_T size_t
+#endif
+
+#ifndef SOAP_SOCKET
+# ifdef WIN32
+#  define SOAP_SOCKET SOCKET
+#  define soap_closesocket(n) closesocket(n)
+# else
+#  define SOAP_SOCKET int
+#  define soap_closesocket(n) close(n)
+# endif
+#endif
+
+#define SOAP_INVALID_SOCKET ((SOAP_SOCKET)-1)
+#define soap_valid_socket(n) ((n) != SOAP_INVALID_SOCKET)
+
+#ifndef SOAP_GAI_STRERROR
+# define SOAP_GAI_STRERROR gai_strerror
+#endif
+
+#ifndef FD_SETSIZE
+# define FD_SETSIZE (1024)
+#endif
+
+#if defined(SYMBIAN)
+# define LONG64 long
+# define ULONG64 unsigned LONG64
+#elif !defined(WIN32) || defined(CYGWIN) || defined(__GLIBC__) || defined(__GNU__)
+# ifndef LONG64
+#  if defined(HAVE_STDINT_H)
+#   include <stdint.h>
+#   define LONG64 int64_t
+#   define ULONG64 uint64_t
+#  elif defined(__GLIBC__)
+#   include <bits/wordsize.h>
+#   if (__WORDSIZE == 64)
+#    define LONG64 int64_t
+#    define ULONG64 uint64_t
+#    ifndef SOAP_LONG_FORMAT
+#     define SOAP_LONG_FORMAT "%ld"
+#    endif
+#    ifndef SOAP_ULONG_FORMAT
+#     define SOAP_ULONG_FORMAT "%lu"
+#    endif
+#   else
+#    define LONG64 long long
+#    define ULONG64 unsigned LONG64
+#   endif
+#  else
+#   define LONG64 long long
+#   define ULONG64 unsigned LONG64
+#  endif
+# endif
+#elif defined(UNDER_CE)
+# define LONG64 __int64
+# define ULONG64 unsigned LONG64
+#elif defined(__BORLANDC__)
+# define LONG64 __int64
+# define ULONG64 unsigned LONG64
+#endif
+
+#ifndef SOAP_LONG_FORMAT
+# define SOAP_LONG_FORMAT "%lld"	/* printf format for 64 bit ints */
+#endif
+
+#ifndef SOAP_ULONG_FORMAT
+# define SOAP_ULONG_FORMAT "%llu"	/* printf format for unsigned 64 bit ints */
+#endif
+
+#if defined(WIN32) && !defined(CYGWIN)
+# define soap_int32 __int32
+#elif defined(SYMBIAN)
+# define soap_int32 long
+#elif defined(PALM)
+# define soap_int32 Int32
+#elif defined(_AIX)
+# if defined(_AIX43)
+#  define soap_int32 int32_t
+# else
+#  define soap_int32 signed int
+# endif
+#else
+# define soap_int32 int32_t
+#endif
+
+#ifdef WIN32
+# define SOAP_ERANGE ERANGE
+# define SOAP_EINTR WSAEINTR
+# define SOAP_EAGAIN WSAEWOULDBLOCK
+# define SOAP_EWOULDBLOCK WSAEWOULDBLOCK
+# define SOAP_EINPROGRESS WSAEINPROGRESS
+# define SOAP_EADDRINUSE WSAEADDRINUSE
+#else
+# define SOAP_ERANGE ERANGE
+# define SOAP_EINTR EINTR
+# define SOAP_EAGAIN EAGAIN
+# define SOAP_EADDRINUSE EADDRINUSE
+# ifdef SYMBIAN
+#  define SOAP_EWOULDBLOCK 9898
+#  define SOAP_EINPROGRESS 9899
+# else
+#  define SOAP_EWOULDBLOCK EWOULDBLOCK
+#  define SOAP_EINPROGRESS EINPROGRESS
+# endif
+#endif
+
+#ifdef WIN32
+# ifdef UNDER_CE
+#  define soap_errno GetLastError()
+#  define soap_socket_errno(s) GetLastError()
+#  define soap_reset_errno SetLastError(0)
+# else
+#  define soap_errno GetLastError()
+#  define soap_socket_errno(s) WSAGetLastError()
+#  define soap_reset_errno SetLastError(0)
+# endif
+#else
+# ifndef WITH_NOIO
+#  define soap_errno errno
+#  define soap_socket_errno(s) errno
+#  define soap_reset_errno (errno = 0)
+# else
+#  define soap_errno 0
+#  define soap_socket_errno(s) 0
+#  define soap_reset_errno
+# endif
+#endif
+
+#ifndef SOAP_BUFLEN
+# ifndef WITH_LEAN
+#  define SOAP_BUFLEN (65536) /* buffer length for socket packets, also used by gethostbyname_r and UDP messages, so don't make this too small */
+# else
+#  define SOAP_BUFLEN  (2048)
+# endif
+#endif
+#ifndef SOAP_LABLEN
+# define SOAP_LABLEN     (256) /* initial look-aside buffer length */
+#endif
+#ifndef SOAP_PTRBLK
+# define SOAP_PTRBLK     (32) /* block allocation for pointer hash table chains */
+#endif
+#ifndef SOAP_PTRHASH
+# ifndef WITH_LEAN
+#  define SOAP_PTRHASH (1024) /* size of pointer analysis hash table (must be power of 2) */
+# else
+#  define SOAP_PTRHASH   (32)
+# endif
+#endif
+#ifndef SOAP_IDHASH
+# ifndef WITH_LEAN
+#  define SOAP_IDHASH  (1999) /* prime size of hash table for parsed id/ref */
+# else
+#  define SOAP_IDHASH    (19) /* 19, 199 */
+# endif
+#endif
+#ifndef SOAP_BLKLEN
+# ifndef WITH_LEAN
+#  define SOAP_BLKLEN   (256) /* size of blocks to collect long strings and XML attributes */
+# else
+#  define SOAP_BLKLEN    (32)
+# endif
+#endif
+#ifndef SOAP_TAGLEN
+# ifndef WITH_LEAN
+#  define SOAP_TAGLEN  (1024) /* maximum length of XML element tag/attribute name or host/path name + 1 */
+# else
+#  define SOAP_TAGLEN    (64)
+# endif
+#endif
+#ifndef SOAP_HDRLEN
+# ifndef WITH_LEAN
+#  define SOAP_HDRLEN  (8192) /* maximum length of HTTP header line (must be >4096 to read cookies) */
+# else
+#  define SOAP_HDRLEN  (1024)
+# endif
+#endif
+#ifndef SOAP_MAXDIMS
+# ifndef WITH_LEAN
+#  define SOAP_MAXDIMS	 (16) /* maximum array dimensions (array nestings) must be less than 64 to protect soap->tmpbuf */
+# else
+#  define SOAP_MAXDIMS	  (4)
+# endif
+#endif
+
+#ifndef SOAP_MAXLOGS
+# define SOAP_MAXLOGS	  (3) /* max number of debug logs per struct soap environment */
+# define SOAP_INDEX_RECV  (0)
+# define SOAP_INDEX_SENT  (1)
+# define SOAP_INDEX_TEST  (2)
+#endif
+
+#ifndef SOAP_MAXKEEPALIVE
+# define SOAP_MAXKEEPALIVE (100) /* max iterations to keep server connection alive */
+#endif
+
+#ifndef SOAP_MAXARRAYSIZE
+# define SOAP_MAXARRAYSIZE (1000000) /* "trusted" max size of inbound SOAP array for compound array allocation */
+#endif
+
+#ifdef VXWORKS
+# ifdef __INCmathh 
+#  include <private/mathP.h>
+#  ifndef HAVE_ISNAN
+#   define HAVE_ISNAN
+#  endif
+#  define soap_isnan(num) isNan(num)
+# endif
+#endif
+
+#ifdef WIN32 
+# include <float.h>
+# ifndef HAVE_ISNAN
+#  define HAVE_ISNAN
+# endif
+# define soap_isnan(num) _isnan(num)
+#endif
+
+#ifdef SUN_OS
+# define HAVE_ISNAN
+#endif
+
+#ifdef __APPLE__
+# ifdef __cplusplus
+#  ifndef isnan
+extern "C" int isnan(double);
+#  endif
+# endif
+# define HAVE_ISNAN
+#endif
+
+#if !defined(HAVE_ISNAN) && (defined(_MATH_H) || defined(_MATH_INCLUDED))
+# define HAVE_ISNAN
+#endif
+
+extern const struct soap_double_nan { unsigned int n1, n2; } soap_double_nan;
+
+#ifdef VXWORKS
+# ifndef FLT_MAX
+#  define FLT_MAX _ARCH_FLT_MAX
+# endif
+# ifndef DBL_MAX
+#  define DBL_MAX _ARCH_DBL_MAX
+# endif
+#endif
+
+#ifndef FLT_NAN
+#  define FLT_NAN (*(float*)(void*)&soap_double_nan)
+#endif
+
+#ifndef FLT_PINFTY
+# if defined(FLT_MAX)
+#  define FLT_PINFTY FLT_MAX
+# elif defined(HUGE_VALF)
+#  define FLT_PINFTY (float)HUGE_VALF
+# elif defined(HUGE_VAL)
+#  define FLT_PINFTY (float)HUGE_VAL
+# elif defined(FLOAT_MAX)
+#  define FLT_PINFTY FLOAT_MAX
+# else
+#  define FLT_PINFTY (3.40282347e+38F)
+# endif
+#endif
+
+#ifndef FLT_NINFTY
+# define FLT_NINFTY (-FLT_PINFTY)
+#endif
+
+#ifndef DBL_NAN
+#  define DBL_NAN (*(double*)(void*)&soap_double_nan)
+#endif
+
+#ifndef DBL_PINFTY
+# if defined(DBL_MAX)
+#  define DBL_PINFTY DBL_MAX
+# elif defined(HUGE_VALF)
+#  define DBL_PINFTY (double)HUGE_VALF
+# elif defined(HUGE_VAL)
+#  define DBL_PINFTY (double)HUGE_VAL
+# elif defined(DOUBLE_MAX)
+#  define DBL_PINFTY DOUBLE_MAX
+# else
+#  define DBL_PINFTY (1.7976931348623157e+308)
+# endif
+#endif
+
+#ifndef DBL_NINFTY
+# define DBL_NINFTY (-DBL_PINFTY)
+#endif
+
+#ifndef soap_isnan
+# ifdef HAVE_ISNAN
+#  define soap_isnan(n) isnan(n)
+# else
+#  define soap_isnan(n) (0)
+# endif
+#endif
+
+#define soap_ispinfd(n) ((n) >= DBL_PINFTY)
+#define soap_ispinff(n) ((n) >= FLT_PINFTY)
+#define soap_isninfd(n) ((n) <= DBL_NINFTY)
+#define soap_isninff(n) ((n) <= FLT_NINFTY)
+
+/* gSOAP error codes */
+
+#define SOAP_EOF			EOF
+#define SOAP_ERR			EOF
+#define SOAP_OK				0
+#define SOAP_CLI_FAULT			1
+#define SOAP_SVR_FAULT			2
+#define SOAP_TAG_MISMATCH		3
+#define SOAP_TYPE			4
+#define SOAP_SYNTAX_ERROR		5
+#define SOAP_NO_TAG			6
+#define SOAP_IOB			7
+#define SOAP_MUSTUNDERSTAND		8
+#define SOAP_NAMESPACE			9
+#define SOAP_USER_ERROR			10
+#define SOAP_FATAL_ERROR		11
+#define SOAP_FAULT			12
+#define SOAP_NO_METHOD			13
+#define SOAP_NO_DATA			14
+#define SOAP_GET_METHOD			15
+#define SOAP_PUT_METHOD			16
+#define SOAP_DEL_METHOD			17
+#define SOAP_HEAD_METHOD		18
+#define SOAP_HTTP_METHOD		19
+#define SOAP_EOM			20
+#define SOAP_MOE			21
+#define SOAP_HDR			22
+#define SOAP_NULL			23
+#define SOAP_DUPLICATE_ID		24
+#define SOAP_MISSING_ID			25
+#define SOAP_HREF			26
+#define SOAP_UDP_ERROR			27
+#define SOAP_TCP_ERROR			28
+#define SOAP_HTTP_ERROR			29
+#define SOAP_SSL_ERROR			30
+#define SOAP_ZLIB_ERROR			31
+#define SOAP_DIME_ERROR			32
+#define SOAP_DIME_HREF			33
+#define SOAP_DIME_MISMATCH		34
+#define SOAP_DIME_END			35
+#define SOAP_MIME_ERROR			36
+#define SOAP_MIME_HREF			37
+#define SOAP_MIME_END			38
+#define SOAP_VERSIONMISMATCH		39
+#define SOAP_PLUGIN_ERROR		40
+#define SOAP_DATAENCODINGUNKNOWN	41
+#define SOAP_REQUIRED			42
+#define SOAP_PROHIBITED			43
+#define SOAP_OCCURS			44
+#define SOAP_LENGTH			45
+#define SOAP_FD_EXCEEDED		46
+
+#define soap_xml_error_check(e) ((e) == SOAP_TAG_MISMATCH || (e) == SOAP_NO_TAG || (e) == SOAP_SYNTAX_ERROR || (e) == SOAP_NAMESPACE || (e) == SOAP_DUPLICATE_ID || (e) == SOAP_MISSING_ID || (e) == SOAP_REQUIRED || (e) == SOAP_PROHIBITED || (e) == SOAP_OCCURS || (e) == SOAP_LENGTH || (e) == SOAP_NULL || (e) == SOAP_HREF)
+#define soap_soap_error_check(e) ((e) == SOAP_CLI_FAULT || (e) == SOAP_SVR_FAULT || (e) == SOAP_VERSIONMISMATCH || (e) == SOAP_MUSTUNDERSTAND || (e) == SOAP_FAULT || (e) == SOAP_NO_METHOD)
+#define soap_tcp_error_check(e) ((e) == SOAP_EOF || (e) == SOAP_TCP_ERROR)
+#define soap_ssl_error_check(e) ((e) == SOAP_SSL_ERROR)
+#define soap_zlib_error_check(e) ((e) == SOAP_ZLIB_ERROR)
+#define soap_http_error_check(e) ((e) == SOAP_HTTP_ERROR || ((e) >= SOAP_GET_METHOD && (e) <= SOAP_HTTP_METHOD)|| (e) == SOAP_NO_DATA || ((e) >= 100 && (e) < 600))
+
+/* gSOAP HTTP response status codes 100 to 599 are reserved */
+
+/* Codes 600 to 999 are user definable */
+
+/* Exceptional gSOAP HTTP response status codes >= 1000 */
+
+#define SOAP_STOP		1000	/* No HTTP response */
+#define SOAP_FORM		1001	/* Form request/response */
+#define SOAP_HTML		1002	/* Custom HTML response */
+#define SOAP_FILE		1003	/* Custom file-based response */
+
+/* gSOAP HTTP method codes */
+
+#define SOAP_POST		2000
+#define SOAP_GET		2001
+
+/* gSOAP DIME */
+
+#define SOAP_DIME_CF		0x01
+#define SOAP_DIME_ME		0x02
+#define SOAP_DIME_MB		0x04
+#define SOAP_DIME_VERSION	0x08 /* DIME version 1 */
+#define SOAP_DIME_MEDIA		0x10
+#define SOAP_DIME_ABSURI	0x20
+
+/* gSOAP ZLIB */
+
+#define SOAP_ZLIB_NONE		0x00
+#define SOAP_ZLIB_DEFLATE	0x01
+#define SOAP_ZLIB_INFLATE	0x02
+#define SOAP_ZLIB_GZIP		0x02
+
+/* gSOAP transport, connection, and content encoding modes */
+
+typedef soap_int32 soap_mode;
+
+#define SOAP_IO			0x00000003	/* IO mask */
+#define SOAP_IO_FLUSH		0x00000000	/* flush output immediately, no buffering */
+#define SOAP_IO_BUFFER		0x00000001	/* buffer output in packets of size SOAP_BUFLEN */
+#define SOAP_IO_STORE		0x00000002	/* store entire output to determine length for transport */
+#define SOAP_IO_CHUNK		0x00000003	/* use HTTP chunked transfer AND buffer packets */
+
+#define SOAP_IO_UDP		0x00000004	/* TCP or UDP */
+
+#define SOAP_IO_LENGTH		0x00000008	/* calc message length (internal) */
+#define SOAP_IO_KEEPALIVE	0x00000010	/* keep connection alive */
+
+#define SOAP_ENC_LATIN		0x00000020	/* accept iso-8859-1 encoding */
+#define SOAP_ENC_XML		0x00000040	/* plain XML encoding, no HTTP header */
+#define SOAP_ENC_DIME		0x00000080
+#define SOAP_ENC_MIME		0x00000100
+#define SOAP_ENC_MTOM		0x00000200
+#define SOAP_ENC_ZLIB		0x00000400
+#define SOAP_ENC_SSL		0x00000800
+
+#define SOAP_ENC		0x00000FFF	/* IO and ENC mask */
+
+#define SOAP_XML_STRICT		0x00001000	/* apply strict validation */
+#define SOAP_XML_INDENT		0x00002000	/* emit indented XML */
+#define SOAP_XML_CANONICAL	0x00004000	/* EXC C14N canonical XML */
+#define SOAP_XML_TREE		0x00008000	/* emit XML tree (no id/ref) */
+#define SOAP_XML_GRAPH		0x00010000
+#define SOAP_XML_NIL		0x00020000
+#define SOAP_XML_DOM		0x00040000
+#define SOAP_XML_SEC		0x00080000	/* reserved for WS security */
+
+#define SOAP_C_NOIOB		0x00100000	/* don't fault on array index out of bounds (just ignore) */
+#define SOAP_C_UTFSTRING	0x00200000	/* (de)serialize strings with UTF8 content */
+#define SOAP_C_MBSTRING		0x00400000	/* (de)serialize strings with multi-byte content */
+#define SOAP_C_NILSTRING	0x00800000	/* serialize empty strings as nil (omitted) */
+
+#define SOAP_DOM_TREE		0x01000000
+#define SOAP_DOM_NODE		0x02000000
+#define SOAP_DOM_ASIS		0x04000000
+
+#define SOAP_MIME_POSTCHECK	0x10000000	/* MIME flag (internal) */
+
+#define SOAP_IO_DEFAULT		SOAP_IO_FLUSH
+
+/* SSL client/server authentication settings */
+
+#define SOAP_SSL_NO_AUTHENTICATION		0x00	/* for testing purposes */
+#define SOAP_SSL_REQUIRE_SERVER_AUTHENTICATION	0x01	/* client requires server to authenticate */
+#define SOAP_SSL_REQUIRE_CLIENT_AUTHENTICATION	0x02	/* server requires client to authenticate */
+#define SOAP_SSL_SKIP_HOST_CHECK		0x04	/* client does not check the common name of the host in certificate */
+#define SOAP_SSL_ALLOW_EXPIRED_CERTIFICATE	0x08	/* client does not check the expiration date of the host certificate */
+#define SOAP_SSL_RSA				0x10	/* use RSA */
+#define SOAP_SSLv3				0x20	/* SSL v3 only */
+#define SOAP_TLSv1				0x40	/* TLS v1 only */
+#define SOAP_SSLv3_TLSv1			0x00	/* SSL v3 and TLS v1 support by default */
+
+#define SOAP_SSL_DEFAULT			(SOAP_SSL_REQUIRE_SERVER_AUTHENTICATION | SOAP_SSLv3_TLSv1)
+
+/* state */
+
+#define SOAP_NONE	0
+#define SOAP_INIT	1
+#define SOAP_COPY	2
+
+#define soap_check_state(soap) (!(soap) || ((soap)->state != SOAP_INIT && (soap)->state != SOAP_COPY))
+
+/* part */
+
+#define SOAP_BEGIN		0
+#define SOAP_IN_ENVELOPE	2
+#define SOAP_IN_HEADER		3
+#define SOAP_END_HEADER		4
+#define SOAP_NO_BODY		5
+#define SOAP_IN_BODY		6
+#define SOAP_END_BODY		7
+#define SOAP_END_ENVELOPE	8
+#define SOAP_END		9
+#define SOAP_BEGIN_SECURITY	10
+#define SOAP_IN_SECURITY	11
+#define SOAP_END_SECURITY	12
+
+/* DEBUG macros */
+
+#ifndef WITH_LEAN
+# ifdef DEBUG
+#  ifndef SOAP_DEBUG
+#   define SOAP_DEBUG
+#  endif
+#  ifndef SOAP_MEM_DEBUG
+#   define SOAP_MEM_DEBUG
+#  endif
+# endif
+#endif
+
+#ifdef SOAP_MEM_DEBUG
+# ifndef SOAP_MALLOC
+#  define SOAP_MALLOC(soap, size) soap_track_malloc(soap, __FILE__, __LINE__, size)
+# endif
+# ifndef SOAP_FREE
+#  define SOAP_FREE(soap, ptr) soap_track_free(soap, __FILE__, __LINE__, ptr)
+# endif
+#endif
+
+#ifndef SOAP_MALLOC			/* use libc malloc */
+# define SOAP_MALLOC(soap, size) malloc(size)
+#endif
+
+#ifndef SOAP_FREE			/* use libc free */
+# define SOAP_FREE(soap, ptr) free(ptr)
+#endif
+
+#ifdef SOAP_DEBUG
+# ifndef SOAP_MESSAGE
+#  define SOAP_MESSAGE fprintf
+# endif
+# ifndef DBGLOG
+#  define DBGLOG(DBGFILE, CMD) \
+{ if (soap)\
+  { if (!soap->fdebug[SOAP_INDEX_##DBGFILE])\
+      soap_open_logfile((struct soap*)soap, SOAP_INDEX_##DBGFILE);\
+    if (soap->fdebug[SOAP_INDEX_##DBGFILE])\
+    { FILE *fdebug = soap->fdebug[SOAP_INDEX_##DBGFILE];\
+      CMD;\
+      fflush(fdebug);\
+    }\
+  }\
+}
+# endif
+# ifndef DBGMSG
+#  define DBGMSG(DBGFILE, MSG, LEN) \
+{ if (soap)\
+  { if (!soap->fdebug[SOAP_INDEX_##DBGFILE])\
+      soap_open_logfile((struct soap*)soap, SOAP_INDEX_##DBGFILE);\
+    if (soap->fdebug[SOAP_INDEX_##DBGFILE])\
+    { fwrite((MSG), 1, (LEN), soap->fdebug[SOAP_INDEX_##DBGFILE]);\
+      fflush(soap->fdebug[SOAP_INDEX_##DBGFILE]);\
+    }\
+  }\
+}
+# endif
+# ifndef DBGFUN
+#  define DBGFUN(FNAME) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s()\n", __FILE__, __LINE__, FNAME))
+#  define DBGFUN1(FNAME, FMT, ARG) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s("FMT")\n", __FILE__, __LINE__, FNAME, (ARG)))
+#  define DBGFUN2(FNAME, FMT1, ARG1, FMT2, ARG2) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s("FMT1", "FMT2")\n", __FILE__, __LINE__, FNAME, (ARG1), (ARG2)))
+#  define DBGFUN3(FNAME, FMT1, ARG1, FMT2, ARG2, FMT3, ARG3) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s("FMT1", "FMT2", "FMT3")\n", __FILE__, __LINE__, FNAME, (ARG1), (ARG2), (ARG3)))
+# endif
+# ifndef DBGHEX
+#  define DBGHEX(DBGFILE, MSG, LEN) \
+{ if (soap)\
+  { if (!soap->fdebug[SOAP_INDEX_##DBGFILE])\
+      soap_open_logfile(soap, SOAP_INDEX_##DBGFILE);\
+    if (soap->fdebug[SOAP_INDEX_##DBGFILE])\
+    { int i; char *s;\
+      for (s = (char*)(MSG), i = (LEN); i; i--)\
+        fprintf(soap->fdebug[SOAP_INDEX_##DBGFILE], "%2.2X  ", (int)*s++&0xFF);\
+      fflush(soap->fdebug[SOAP_INDEX_##DBGFILE]);\
+    }\
+  }\
+}
+# endif
+#else
+# define DBGLOG(DBGFILE, CMD)
+# define DBGMSG(DBGFILE, MSG, LEN)
+# define DBGFUN(FNAME)
+# define DBGFUN1(FNAME, FMT, ARG)
+# define DBGFUN2(FNAME, FMT1, ARG1, FMT2, ARG2)
+# define DBGFUN3(FNAME, FMT1, ARG1, FMT2, ARG2, FMT3, ARG3)
+# define DBGHEX(DBGFILE, MSG, LEN)
+#endif
+
+/* UCS-4 requires 32 bits (0-7FFFFFFF, the sign bit is used by gSOAP to distinguish XML entities) */
+typedef soap_int32 soap_wchar;
+
+/* namespace table row */
+struct Namespace
+{ const char *id;
+  const char *ns;
+  const char *in;
+  char *out;
+};
+
+/* namespace stack */
+struct soap_nlist
+{ struct soap_nlist *next;
+  unsigned int level; /* nesting depth level */
+  short index; /* corresponding entry in ns mapping table */
+  char *ns; /* only set when parsed ns URI is not in the ns mapping table */
+  char id[1]; /* the actual string value flows into the allocated region below this struct */
+};
+
+/* block stack for nested block allocations */
+struct soap_blist
+{ struct soap_blist *next;
+  char *ptr;
+  size_t size;
+};
+
+/* array layout */
+struct soap_array
+{ void *__ptr;
+  int __size;
+};
+
+/* pointer serialization management */
+struct soap_plist
+{ struct soap_plist *next;
+  const void *ptr;
+  const struct soap_array *array;
+  int type;
+  int id;
+  char mark1;
+  char mark2;
+};
+
+/* block allocation for pointer serialization management */
+struct soap_pblk
+{ struct soap_pblk *next;
+  struct soap_plist plist[SOAP_PTRBLK];
+};
+
+#ifdef SOAP_MEM_DEBUG
+/* malloc/free tracking for debugging */
+struct soap_mlist
+{ struct soap_mlist *next;
+  const void *ptr;
+  const char *file;
+  int line;
+  short live;
+};
+#endif
+
+/* class allocation list */
+struct soap_clist
+{ struct soap_clist *next;
+  void *ptr;
+  int type;
+  int size;
+  int (*fdelete)(struct soap_clist*);
+};
+
+/* attributes */
+struct soap_attribute
+{ struct soap_attribute *next;
+  char *value;
+  size_t size;
+  char *ns;
+  short visible;
+  char name[1]; /* the actual name string flows into the allocated region below this struct */
+};
+
+#ifndef WITH_LEAN
+struct soap_cookie
+{ struct soap_cookie *next;
+  char *name;
+  char *value;
+  char *domain;
+  char *path;
+  time_t expire;	/* client-side: local time to expire */
+  long maxage;		/* server-side: seconds to expire */
+  unsigned int version;
+  short secure;
+  short session;	/* server-side */
+  short env;		/* server-side: got cookie from client and should not be (re)send */
+  short modified;	/* server-side: client cookie was modified and should be send */
+};
+#endif
+
+#ifdef __cplusplus
+SOAP_FMAC1 struct soap_multipart* SOAP_FMAC2 soap_next_multipart(struct soap_multipart*);
+
+class soap_multipart_iterator
+{ public:
+  struct soap_multipart *content;
+  bool operator==(const soap_multipart_iterator& iter) const
+    { return content == iter.content; }
+  bool operator!=(const soap_multipart_iterator& iter) const
+    { return content != iter.content; }
+  struct soap_multipart &operator*() const
+    { return *content; }
+  soap_multipart_iterator &operator++()
+    { content = soap_next_multipart(content); return *this; }
+  soap_multipart_iterator() : content(NULL)
+    { }
+  soap_multipart_iterator(struct soap_multipart *p) : content(p)
+    { }
+};
+#endif
+
+#ifndef WITH_LEANER
+struct soap_dime
+{ size_t count;
+  size_t size;
+  size_t chunksize;
+  size_t buflen;
+  char flags;
+  char *ptr;
+  const char *id;
+  const char *type;
+  const char *options;
+  struct soap_multipart *list;		/* list of DIME attachments received */
+  struct soap_multipart *first, *last;	/* temporary in/out queue */
+#ifdef __cplusplus
+  soap_multipart_iterator begin()
+    { soap_multipart_iterator iter(list); return iter; };
+  soap_multipart_iterator end()
+    { soap_multipart_iterator iter(NULL); return iter; };
+#endif
+};
+#endif
+
+#ifndef WITH_LEANER
+struct soap_mime
+{ char *boundary;			/* MIME boundary */
+  const char *start;			/* MIME start ID */
+  struct soap_multipart *list;		/* list of MIME attachments received */
+  struct soap_multipart *first, *last;	/* temporary in/out queue */
+#ifdef __cplusplus
+  soap_multipart_iterator begin()
+    { soap_multipart_iterator iter(list); return iter; };
+  soap_multipart_iterator end()
+    { soap_multipart_iterator iter(NULL); return iter; };
+#endif
+};
+#endif
+
+#ifndef WITH_LEANER
+/* RFC2045 MIME content transfer encodings */
+enum soap_mime_encoding
+{ SOAP_MIME_NONE,
+  SOAP_MIME_7BIT,
+  SOAP_MIME_8BIT,
+  SOAP_MIME_BINARY,
+  SOAP_MIME_QUOTED_PRINTABLE,
+  SOAP_MIME_BASE64,
+  SOAP_MIME_IETF_TOKEN,
+  SOAP_MIME_X_TOKEN
+};
+#endif
+
+#ifndef WITH_LEANER
+/* DIME/MIME multipart list */
+struct soap_multipart
+{ struct soap_multipart *next;
+  char *ptr;				/* points to raw data content */
+  size_t size;				/* size of data content */
+  const char *id;			/* DIME/MIME content ID or form data name */
+  const char *type;			/* DIME/MIME type (MIME type format) */
+  const char *options;			/* DIME options */
+  enum soap_mime_encoding encoding;	/* MIME Content-Transfer-Encoding */
+  const char *location;			/* MIME Content-Location (optional) */
+  const char *description;		/* MIME Content-Description (optional) */
+#ifdef __cplusplus
+  typedef soap_multipart_iterator iterator;
+#endif
+};
+#endif
+
+#ifndef WITH_LEANER
+/* attachment DIME and MTOM XOP forwarding */
+struct soap_xlist
+{ struct soap_xlist *next;
+  unsigned char **ptr;
+  int *size;
+  char *id;
+  char **type;
+  char **options;
+};
+#endif
+
+/******************************************************************************/
+
+#ifndef WITH_LEANER
+#ifdef __cplusplus
+class soap_dom_attribute_iterator
+{ public:
+  struct soap_dom_attribute *att;
+  const char *nstr;
+  const char *name;
+  bool operator==(const soap_dom_attribute_iterator&) const;
+  bool operator!=(const soap_dom_attribute_iterator&) const;
+  struct soap_dom_attribute &operator*() const;
+  soap_dom_attribute_iterator &operator++();
+  soap_dom_attribute_iterator();
+  soap_dom_attribute_iterator(struct soap_dom_attribute*);
+  ~soap_dom_attribute_iterator();
+};
+#endif
+#endif
+
+#ifndef WITH_LEANER
+struct soap_dom_attribute
+{ struct soap_dom_attribute *next;
+  const char *nstr;
+  char *name;
+  char *data;
+  wchar_t *wide;
+  struct soap *soap;
+#ifdef __cplusplus
+  typedef soap_dom_attribute_iterator iterator;
+  struct soap_dom_attribute &set(const char *nstr, const char *name);	/* set namespace and name */
+  struct soap_dom_attribute &set(const char *data);		/* set data */
+  soap_dom_attribute_iterator begin();
+  soap_dom_attribute_iterator end();
+  soap_dom_attribute_iterator find(const char *nstr, const char *name);
+  void unlink();
+  soap_dom_attribute();
+  soap_dom_attribute(struct soap *soap);
+  soap_dom_attribute(struct soap *soap, const char *nstr, const char *name, const char *data);
+  ~soap_dom_attribute();
+#endif
+};
+#endif
+
+#ifndef WITH_LEANER
+#ifdef __cplusplus
+class soap_dom_element_iterator
+{ public:
+  struct soap_dom_element *elt;
+  const char *nstr;
+  const char *name;
+  int type;
+  bool operator==(const soap_dom_element_iterator&) const;
+  bool operator!=(const soap_dom_element_iterator&) const;
+  struct soap_dom_element &operator*() const;
+  soap_dom_element_iterator &operator++();
+  soap_dom_element_iterator();
+  soap_dom_element_iterator(struct soap_dom_element*);
+  ~soap_dom_element_iterator();
+};
+#endif
+#endif
+
+#ifndef WITH_LEANER
+struct soap_dom_element
+{ struct soap_dom_element *next;	/* next sibling */
+  struct soap_dom_element *prnt;	/* parent */
+  struct soap_dom_element *elts;	/* list of child elements */
+  struct soap_dom_attribute *atts;	/* list of attributes */
+  const char *nstr;			/* namespace string */
+  char *name;				/* element tag name */
+  char *data;				/* element content data (with SOAP_C_UTFSTRING flag set) */
+  wchar_t *wide;			/* element content data */
+  int type;				/* optional: serialized C/C++ data type */
+  void *node;				/* optional: pointer to serialized C/C++ data */
+  char *head;				/* leading whitespace to start tag */
+  char *tail;				/* leading whitespace to end tag */
+  struct soap *soap;			/* soap context that manages this node */
+#ifdef __cplusplus
+  typedef soap_dom_element_iterator iterator;
+  struct soap_dom_element &set(const char *nstr, const char *name);
+  struct soap_dom_element &set(const char *data);
+  struct soap_dom_element &set(void *node, int type);
+  struct soap_dom_element &add(struct soap_dom_element*);
+  struct soap_dom_element &add(struct soap_dom_element&);
+  struct soap_dom_element &add(struct soap_dom_attribute*);
+  struct soap_dom_element &add(struct soap_dom_attribute&);
+  soap_dom_element_iterator begin();
+  soap_dom_element_iterator end();
+  soap_dom_element_iterator find(const char *nstr, const char *name);
+  soap_dom_element_iterator find(int type);
+  void unlink();
+  soap_dom_element();
+  soap_dom_element(struct soap *soap);
+  soap_dom_element(struct soap *soap, const char *nstr, const char *name);
+  soap_dom_element(struct soap *soap, const char *nstr, const char *name, const char *data);
+  soap_dom_element(struct soap *soap, const char *nstr, const char *name, void *node, int type);
+  ~soap_dom_element();
+#endif
+};
+SOAP_FMAC1 struct soap_dom_element * SOAP_FMAC2 soap_dom_next_element(struct soap_dom_element *elt);
+SOAP_FMAC1 struct soap_dom_attribute * SOAP_FMAC2 soap_dom_next_attribute(struct soap_dom_attribute *att);
+#endif
+
+#if defined(__cplusplus) && !defined(WITH_LEAN)
+}
+extern std::ostream &operator<<(std::ostream&, const struct soap_dom_element&);
+extern std::istream &operator>>(std::istream&, struct soap_dom_element&);
+extern "C" {
+#endif
+
+/******************************************************************************/
+
+#ifdef WIN32
+# ifdef SOAP_STD_EXPORTS
+#  define SOAP_STD_API __declspec(dllexport)
+# else
+#  define SOAP_STD_API
+# endif
+#else
+# define SOAP_STD_API
+#endif
+
+struct SOAP_STD_API soap
+{ short state;			/* 0 = uninitialized, 1 = initialized, 2 = copy of another soap struct */
+  short version;		/* 1 = SOAP1.1 and 2 = SOAP1.2 (set automatically from namespace URI in nsmap table) */
+  soap_mode mode;
+  soap_mode imode;
+  soap_mode omode;
+  const char *float_format;	/* user-definable format string for floats (<1024 chars) */
+  const char *double_format;	/* user-definable format string for doubles (<1024 chars) */
+  const char *dime_id_format;	/* user-definable format string for integer DIME id (<SOAP_TAGLEN chars) */
+  const char *http_version;	/* HTTP version used "1.0" or "1.1" */
+  const char *http_content;	/* optional custom response content type (with SOAP_FILE) */
+  const char *encodingStyle;	/* default = NULL which means that SOAP encoding is used */
+  const char *actor;		/* SOAP-ENV:actor or role attribute value */
+  const char *lang;		/* xml:lang attribute value of SOAP-ENV:Text */
+  int recv_timeout;		/* when > 0, gives socket recv timeout in seconds, < 0 in usec */
+  int send_timeout;		/* when > 0, gives socket send timeout in seconds, < 0 in usec */
+  int connect_timeout;		/* when > 0, gives socket connect() timeout in seconds, < 0 in usec */
+  int accept_timeout;		/* when > 0, gives socket accept() timeout in seconds, < 0 in usec */
+  int socket_flags;		/* socket recv() and send() flags, e.g. set to MSG_NOSIGNAL to disable sigpipe */
+  int connect_flags;		/* connect() SOL_SOCKET sockopt flags, e.g. set to SO_DEBUG to debug socket */
+  int bind_flags;		/* bind() SOL_SOCKET sockopt flags, e.g. set to SO_REUSEADDR to enable reuse */
+  int accept_flags;		/* accept() SOL_SOCKET sockopt flags */
+  unsigned short linger_time;	/* linger time for SO_LINGER option */
+  const struct Namespace *namespaces;	/* Pointer to global namespace mapping table */
+  struct Namespace *local_namespaces;	/* Local namespace mapping table */
+  struct soap_nlist *nlist;	/* namespace stack */
+  struct soap_blist *blist;	/* block allocation stack */
+  struct soap_clist *clist;	/* class instance allocation list */
+  void *alist;			/* memory allocation (malloc) list */
+  struct soap_ilist *iht[SOAP_IDHASH];
+  struct soap_plist *pht[SOAP_PTRHASH];
+  struct soap_pblk *pblk;	/* plist block allocation */
+  short pidx;			/* plist block allocation */
+  struct SOAP_ENV__Header *header;
+  struct SOAP_ENV__Fault *fault;
+  int idnum;
+  void *user;			/* to pass user-defined data */
+  struct soap_plugin *plugins;	/* linked list of plug-in data */
+  char *userid;			/* HTTP Basic authorization userid */
+  char *passwd;			/* HTTP Basic authorization passwd */
+  int (*fpost)(struct soap*, const char*, const char*, int, const char*, const char*, size_t);
+  int (*fget)(struct soap*);
+  int (*fput)(struct soap*);
+  int (*fdel)(struct soap*);
+  int (*fhead)(struct soap*);
+  int (*fform)(struct soap*);
+  int (*fposthdr)(struct soap*, const char*, const char*);
+  int (*fresponse)(struct soap*, int, size_t);
+  int (*fparse)(struct soap*);
+  int (*fparsehdr)(struct soap*, const char*, const char*);
+  int (*fheader)(struct soap*);
+  int (*fresolve)(struct soap*, const char*, struct in_addr* inaddr);
+  int (*fconnect)(struct soap*, const char*, const char*, int);
+  int (*fdisconnect)(struct soap*);
+  int (*fclosesocket)(struct soap*, SOAP_SOCKET);
+  int (*fshutdownsocket)(struct soap*, SOAP_SOCKET, int);
+  SOAP_SOCKET (*fopen)(struct soap*, const char*, const char*, int);
+  SOAP_SOCKET (*faccept)(struct soap*, SOAP_SOCKET, struct sockaddr*, int *n);
+  int (*fclose)(struct soap*);
+  int (*fsend)(struct soap*, const char*, size_t);
+  size_t (*frecv)(struct soap*, char*, size_t);
+  int (*fpoll)(struct soap*);
+  void (*fseterror)(struct soap*, const char **c, const char **s);
+  int (*fignore)(struct soap*, const char*);
+  int (*fserveloop)(struct soap*);
+  void *(*fplugin)(struct soap*, const char*);
+  void *(*fmalloc)(struct soap*, size_t);
+#ifndef WITH_LEANER
+  int (*fprepareinit)(struct soap*);
+  int (*fpreparesend)(struct soap*, const char*, size_t);
+  int (*fpreparerecv)(struct soap*, const char*, size_t);
+  int (*fpreparefinal)(struct soap*);
+  void *(*fdimereadopen)(struct soap*, void*, const char*, const char*, const char*);
+  void *(*fdimewriteopen)(struct soap*, const char*, const char*, const char*);
+  void (*fdimereadclose)(struct soap*, void*);
+  void (*fdimewriteclose)(struct soap*, void*);
+  size_t (*fdimeread)(struct soap*, void*, char*, size_t);
+  int (*fdimewrite)(struct soap*, void*, const char*, size_t);
+  void *(*fmimereadopen)(struct soap*, void*, const char*, const char*, const char*);
+  void *(*fmimewriteopen)(struct soap*, void*, const char*, const char*, const char*, enum soap_mime_encoding);
+  void (*fmimereadclose)(struct soap*, void*);
+  void (*fmimewriteclose)(struct soap*, void*);
+  size_t (*fmimeread)(struct soap*, void*, char*, size_t);
+  int (*fmimewrite)(struct soap*, void*, const char*, size_t);
+#endif
+  SOAP_SOCKET master;
+  SOAP_SOCKET socket;
+#if defined(__cplusplus) && !defined(WITH_LEAN)
+  std::ostream *os;
+  std::istream *is;
+#else
+  void *os;		/* preserve struct size */
+  void *is;		/* preserve struct size */
+#endif
+#ifndef UNDER_CE
+  int sendfd;
+  int recvfd;
+#else
+  FILE *sendfd;
+  FILE *recvfd;
+#endif
+  size_t bufidx;	/* index in soap.buf[] */
+  size_t buflen;	/* length of soap.buf[] content */
+  soap_wchar ahead;	/* parser lookahead */
+  short cdata;		/* CDATA parser state */
+  short body;		/* parsed XML element has a body or not */
+  unsigned int level;	/* XML nesting level */
+  size_t count;		/* message length counter */
+  size_t length;	/* message length as set by HTTP header */
+  char *labbuf;		/* look-aside buffer */
+  size_t lablen;	/* look-aside buffer allocated length */
+  size_t labidx;	/* look-aside buffer index to available part */
+  char buf[SOAP_BUFLEN];/* send and receive buffer */
+  char msgbuf[1024];	/* in/out buffer for HTTP/MIME headers >=1024 bytes */
+  char tmpbuf[1024];	/* in/out buffer for HTTP/MIME headers, simpleType values, element and attribute tag names, and DIME must be >=1024 bytes */
+  char tag[SOAP_TAGLEN];
+  char id[SOAP_TAGLEN];
+  char href[SOAP_TAGLEN];
+  char type[SOAP_TAGLEN];
+  char arrayType[SOAP_TAGLEN];
+  char arraySize[SOAP_TAGLEN];
+  char arrayOffset[SOAP_TAGLEN];
+  short other;
+  short position;
+  int positions[SOAP_MAXDIMS];
+  short root;
+  struct soap_attribute *attributes;	/* attribute list */
+  short encoding;	/* when set, output encodingStyle */
+  short mustUnderstand;	/* a mustUnderstand element was parsed or is output */
+  short null;		/* parsed XML is xsi:nil */
+  short ns;		/* when not set, output full xmlns bindings */
+  short part;		/* parsing state */
+  short alloced;
+  short peeked;
+  size_t chunksize;
+  size_t chunkbuflen;
+  char endpoint[SOAP_TAGLEN];
+  char path[SOAP_TAGLEN];
+  char host[SOAP_TAGLEN];
+  char *action;
+  char *authrealm;		/* HTTP authentication realm */
+  char *prolog;			/* XML declaration prolog */
+  unsigned long ip;		/* IP number */
+  int port;			/* port number */
+  short keep_alive;		/* connection should be kept open */
+  short tcp_keep_alive;		/* enable SO_KEEPALIVE */
+  unsigned int tcp_keep_idle; 	/* set TCP_KEEPIDLE */
+  unsigned int tcp_keep_intvl; 	/* set TCP_KEEPINTVL */
+  unsigned int tcp_keep_cnt; 	/* set TCP_KEEPCNT */
+  unsigned int max_keep_alive;  /* maximum keep-alive session (default=100) */
+  const char *proxy_http_version;/* HTTP version of proxy "1.0" or "1.1" */
+  const char *proxy_host;	/* Proxy Server host name */
+  int proxy_port;		/* Proxy Server port (default = 8080) */
+  const char *proxy_userid;	/* Proxy Authorization user name */
+  const char *proxy_passwd;	/* Proxy Authorization password */
+  const char *proxy_from;	/* X-Forwarding-For header returned by proxy */
+  int status;			/* -1 when request, else error code to be returned by server */
+  int error;
+  int errmode;
+  int errnum;
+#ifndef WITH_LEANER
+  struct soap_dom_element *dom;
+  struct soap_dime dime;
+  struct soap_mime mime;
+  struct soap_xlist *xlist;
+#endif
+#if !defined(WITH_LEAN) || defined(SOAP_DEBUG)
+  const char *logfile[SOAP_MAXLOGS];
+  FILE *fdebug[SOAP_MAXLOGS];
+  struct soap_mlist *mht[SOAP_PTRHASH];
+#endif
+#ifndef WITH_LEAN
+  const char *c14ninclude;
+  const char *c14nexclude;
+  struct soap_cookie *cookies;
+  const char *cookie_domain;
+  const char *cookie_path;
+  int cookie_max;
+#endif
+#ifndef WITH_NOIO
+  int ipv6_multicast_if; /* always include this to keep the soap struct size the same in v4 and v6 */
+  char* ipv4_multicast_if; /* always include this to keep the soap struct size the same in v4 and v6 */
+  int ipv4_multicast_ttl; /* multicast scope */
+#ifdef WITH_IPV6
+  struct sockaddr_storage peer;	/* IPv6: set by soap_accept and by UDP recv */
+#else
+  struct sockaddr_in peer;	/* IPv4: set by soap_connect/soap_accept and by UDP recv */
+#endif
+#endif
+  size_t peerlen;
+#ifdef WITH_OPENSSL
+  int (*fsslauth)(struct soap*);
+  int (*fsslverify)(int, X509_STORE_CTX*);
+  BIO *bio;
+  SSL *ssl;
+  SSL_CTX *ctx;
+  SSL_SESSION *session;
+#else
+  void *fsslauth;		/* dummy members, to preserve struct size */
+  void *fsslverify;
+  void *bio;
+  void *ssl;
+  void *ctx;
+  void *session;
+#endif
+  unsigned short ssl_flags;
+  const char *keyfile;
+  const char *password;
+  const char *dhfile;
+  const char *cafile;
+  const char *capath;
+  const char *crlfile;
+  const char *randfile;
+  char session_host[SOAP_TAGLEN];
+  int session_port;
+#ifdef WITH_C_LOCALE
+  locale_t c_locale;		/* set to C locale by default */
+#else
+  void *c_locale;
+#endif
+#ifdef WITH_ZLIB
+  z_stream *d_stream;		/* decompression stream */
+  uLong z_crc;			/* internal gzip crc */
+#else
+  void *d_stream;		/* dummy members, to preserve struct size */
+  soap_int32 z_crc;
+#endif
+  short zlib_state;		/* SOAP_ZLIB_NONE, SOAP_ZLIB_DEFLATE, or SOAP_ZLIB_INFLATE */
+  short zlib_in;		/* SOAP_ZLIB_NONE, SOAP_ZLIB_DEFLATE, or SOAP_ZLIB_GZIP */
+  short zlib_out;		/* SOAP_ZLIB_NONE, SOAP_ZLIB_DEFLATE, or SOAP_ZLIB_GZIP */
+  char *z_buf;			/* buffer */
+  size_t z_buflen;
+  unsigned short z_level;	/* compression level to be used (0=none, 1=fast to 9=best) */
+  float z_ratio_in;		/* detected compression ratio compressed_length/length of inbound message */
+  float z_ratio_out;		/* detected compression ratio compressed_length/length of outbound message */
+#ifdef WMW_RPM_IO
+  void *rpmreqid;
+#endif
+#ifdef __cplusplus
+  soap();
+  soap(soap_mode);
+  soap(soap_mode, soap_mode);
+  soap(struct soap&);
+  virtual ~soap();
+#else
+  void (*dummy)();
+#endif
+};
+
+struct soap_code_map
+{ long code;
+  const char *string;
+};
+
+/* forwarding list */
+struct soap_flist
+{ struct soap_flist *next;
+  int type;
+  void *ptr;
+  unsigned int level;
+  size_t len;
+  void (*fcopy)(struct soap*, int, int, void*, size_t, const void*, size_t);
+};
+
+/* id-ref forwarding list */
+struct soap_ilist
+{ struct soap_ilist *next;
+  int type;
+  size_t size;
+  void *link;
+  void *copy;
+  struct soap_flist *flist;
+  void *ptr;
+  unsigned int level;
+  char id[1]; /* the actual id string value flows into the allocated region below this struct */
+};
+
+struct soap_plugin
+{ struct soap_plugin *next;
+  const char *id;
+  void *data;
+  int (*fcopy)(struct soap *soap, struct soap_plugin *dst, struct soap_plugin *src);
+  void (*fdelete)(struct soap *soap, struct soap_plugin *p); /* should delete fields of plugin only and not free(p) */
+};
+
+#ifndef WITH_NONAMESPACES
+extern SOAP_NMAC struct Namespace namespaces[];
+#endif
+
+#ifndef WITH_LEAN
+# define soap_get0(soap) (((soap)->bufidx>=(soap)->buflen && soap_recv(soap)) ? EOF : (unsigned char)(soap)->buf[(soap)->bufidx])
+# define soap_get1(soap) (((soap)->bufidx>=(soap)->buflen && soap_recv(soap)) ? EOF : (unsigned char)(soap)->buf[(soap)->bufidx++])
+#else
+soap_wchar soap_get0(struct soap*);
+soap_wchar soap_get1(struct soap*);
+#endif
+
+#define soap_revget1(soap) ((soap)->bufidx--)
+#define soap_unget(soap, c) ((soap)->ahead = c)
+#define soap_register_plugin(soap, plugin) soap_register_plugin_arg(soap, plugin, NULL)
+#define soap_imode(soap, n) ((soap)->mode = (soap)->imode = (n))
+#define soap_set_imode(soap, n) ((soap)->imode |= (n))
+#define soap_clr_imode(soap, n) ((soap)->imode &= ~(n))
+#define soap_omode(soap, n) ((soap)->mode = (soap)->omode = (n))
+#define soap_set_omode(soap, n) ((soap)->omode |= (n))
+#define soap_clr_omode(soap, n) ((soap)->omode &= ~(n))
+#define soap_set_mode(soap, n) ((soap)->imode |= (n), (soap)->omode |= (n))
+#define soap_clr_mode(soap, n) ((soap)->imode &= ~(n), (soap)->omode &= ~(n))
+#define soap_destroy(soap) soap_delete((soap), NULL)
+
+#ifdef HAVE_STRRCHR
+# define soap_strrchr(s, t) strrchr(s, t)
+#else
+ SOAP_FMAC1 char* SOAP_FMAC2 soap_strrchr(const char *s, int t);
+#endif
+
+#ifdef HAVE_STRTOL
+# define soap_strtol(s, t, b) strtol(s, t, b)
+#else
+ SOAP_FMAC1 long SOAP_FMAC2 soap_strtol(const char *s, char **t, int b);
+#endif
+
+#ifdef HAVE_STRTOUL
+# define soap_strtoul(s, t, b) strtoul(s, t, b)
+#else
+ SOAP_FMAC1 unsigned long SOAP_FMAC2 soap_strtoul(const char *s, char **t, int b);
+#endif
+
+#if defined(WITH_OPENSSL)
+# define soap_random soap_rand()
+SOAP_FMAC1 int SOAP_FMAC2 soap_rand(void);
+#elif defined(HAVE_RANDOM)
+# define soap_random (int)random()
+#else
+# define soap_random rand()
+#endif
+
+#ifdef WITH_NOIDREF
+# define soap_embedded(s, p, t) (0)
+# define soap_id_lookup(s, i, p, t, n, k) (p)
+# define soap_id_forward(s, h, p, len, st, tt, n, k, fc) (p)
+# define soap_reference(s, a, t) (1)
+# define soap_array_reference(s, p, a, n, t) (1)
+# define soap_embed(s, p, a, n, t, pp) (0)
+# define soap_embedded_id(s, i, p, t) (i)
+# define soap_is_embedded(s, p) (0)
+# define soap_is_single(s, p) (1)
+# define soap_lookup_type(s, i) (0)
+# define soap_getindependent(s) (0)
+# define soap_putindependent(s) (0)
+# define soap_getelement(s, n) (n)
+# define soap_putelement(s, p, t, i, n) (0)
+# define soap_markelement(s, p, n) (0)
+#endif
+
+SOAP_FMAC1 void SOAP_FMAC2 soap_header(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_fault(struct soap*);
+SOAP_FMAC1 const char** SOAP_FMAC2 soap_faultcode(struct soap*);
+SOAP_FMAC1 const char** SOAP_FMAC2 soap_faultsubcode(struct soap*);
+SOAP_FMAC1 const char** SOAP_FMAC2 soap_faultstring(struct soap*);
+SOAP_FMAC1 const char** SOAP_FMAC2 soap_faultdetail(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_serializeheader(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_putheader(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_getheader(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_serializefault(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_putfault(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_getfault(struct soap*);
+
+SOAP_FMAC1 void SOAP_FMAC2 soap_ssl_init();
+SOAP_FMAC1 int SOAP_FMAC2 soap_poll(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_connect_command(struct soap*, int, const char*, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_connect(struct soap*, const char*, const char*);
+SOAP_FMAC1 SOAP_SOCKET SOAP_FMAC2 soap_bind(struct soap*, const char*, int, int);
+SOAP_FMAC1 SOAP_SOCKET SOAP_FMAC2 soap_accept(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_ssl_accept(struct soap*);
+SOAP_FMAC1 const char * SOAP_FMAC2 soap_ssl_error(struct soap*, int);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_ssl_server_context(struct soap*, unsigned short, const char*, const char*, const char*, const char*, const char*, const char*, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_ssl_client_context(struct soap*, unsigned short, const char*, const char*, const char*, const char*, const char*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_puthttphdr(struct soap*, int status, size_t count);
+
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_get_header_attribute(struct soap*, const char*, const char*);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_decode_key(char*, size_t, const char*);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_decode_val(char*, size_t, const char*);
+
+SOAP_FMAC1 size_t SOAP_FMAC2 soap_hash(const char*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_set_endpoint(struct soap*, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_flush_raw(struct soap*, const char*, size_t);
+SOAP_FMAC1 int SOAP_FMAC2 soap_flush(struct soap*);
+SOAP_FMAC1 soap_wchar SOAP_FMAC2 soap_get(struct soap*);
+SOAP_FMAC1 soap_wchar SOAP_FMAC2 soap_getchar(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_tag_cmp(const char*, const char*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_set_fault(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_sender_fault(struct soap*, const char*, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_sender_fault_subcode(struct soap*, const char*, const char*, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_receiver_fault(struct soap*, const char*, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_receiver_fault_subcode(struct soap*, const char*, const char*, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_set_sender_error(struct soap*, const char*, const char*, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_set_receiver_error(struct soap*, const char*, const char*, int);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_send_raw(struct soap*, const char*, size_t);
+SOAP_FMAC1 int SOAP_FMAC2 soap_recv_raw(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_recv(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_send(struct soap*, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_send2(struct soap*, const char*, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_send3(struct soap*, const char*, const char*, const char*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_pututf8(struct soap*, unsigned long);
+SOAP_FMAC1 soap_wchar SOAP_FMAC2 soap_getutf8(struct soap*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_putbase64(struct soap*, const unsigned char*, int);
+SOAP_FMAC1 unsigned char* SOAP_FMAC2 soap_getbase64(struct soap*, int*, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_puthex(struct soap*, const unsigned char*, int);
+SOAP_FMAC1 unsigned char* SOAP_FMAC2 soap_gethex(struct soap*, int*);
+
+#ifndef WITH_LEANER
+SOAP_FMAC1 int SOAP_FMAC2 soap_xop_forward(struct soap*, unsigned char**, int*, char**, char**, char**);
+SOAP_FMAC1 int SOAP_FMAC2 soap_dime_forward(struct soap*, unsigned char**, int*, char**, char**, char**);
+#endif
+
+#ifndef WITH_NOIDREF
+SOAP_FMAC1 int SOAP_FMAC2 soap_pointer_lookup_id(struct soap*, void *p, int t, struct soap_plist**);
+SOAP_FMAC1 int SOAP_FMAC2 soap_pointer_lookup(struct soap*, const void *p, int t, struct soap_plist**);
+SOAP_FMAC1 int SOAP_FMAC2 soap_pointer_enter(struct soap*, const void *p, const struct soap_array *a, int n, int t, struct soap_plist**);
+SOAP_FMAC1 int SOAP_FMAC2 soap_array_pointer_lookup(struct soap*, const void *p, const struct soap_array *a, int n, int t, struct soap_plist**);
+SOAP_FMAC1 int SOAP_FMAC2 soap_embed(struct soap *soap, const void *p, const struct soap_array *a, int n, const char *tag, int type);
+SOAP_FMAC1 struct soap_ilist* SOAP_FMAC2 soap_lookup(struct soap*, const char*);
+SOAP_FMAC1 struct soap_ilist* SOAP_FMAC2 soap_enter(struct soap*, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_resolve(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_embedded(struct soap*, const void *p, int t);
+SOAP_FMAC1 int SOAP_FMAC2 soap_reference(struct soap*, const void *p, int t);
+SOAP_FMAC1 int SOAP_FMAC2 soap_array_reference(struct soap*, const void *p, const struct soap_array *a, int n, int t);
+SOAP_FMAC1 int SOAP_FMAC2 soap_embedded_id(struct soap*, int id, const void *p, int t);
+SOAP_FMAC1 int SOAP_FMAC2 soap_is_embedded(struct soap*, struct soap_plist*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_is_single(struct soap*, struct soap_plist*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_set_embedded(struct soap*, struct soap_plist*);
+#endif
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_begin_count(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_end_count(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_begin_send(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_end_send(struct soap*);
+
+SOAP_FMAC1 const struct soap_code_map* SOAP_FMAC2 soap_code(const struct soap_code_map*, const char*);
+SOAP_FMAC1 long SOAP_FMAC2 soap_code_int(const struct soap_code_map*, const char*, long);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_code_str(const struct soap_code_map*, long);
+SOAP_FMAC1 long SOAP_FMAC2 soap_code_bits(const struct soap_code_map*, const char*);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_code_list(struct soap*, const struct soap_code_map*, long);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_getline(struct soap*, char*, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_begin_recv(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_end_recv(struct soap*);
+
+SOAP_FMAC1 void* SOAP_FMAC2 soap_malloc(struct soap*, size_t);
+SOAP_FMAC1 void SOAP_FMAC2 soap_dealloc(struct soap*, void*);
+SOAP_FMAC1 struct soap_clist * SOAP_FMAC2 soap_link(struct soap*, void*, int, int, int (*fdelete)(struct soap_clist*));
+SOAP_FMAC1 void SOAP_FMAC2 soap_unlink(struct soap*, const void*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_free_temp(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_del(struct soap*);
+
+SOAP_FMAC1 void* SOAP_FMAC2 soap_track_malloc(struct soap*, const char*, int, size_t);
+SOAP_FMAC1 void SOAP_FMAC2 soap_track_free(struct soap*, const char*, int, void*);
+
+#ifndef WITH_NOIDREF
+SOAP_FMAC1 int SOAP_FMAC2 soap_lookup_type(struct soap*, const char *id);
+SOAP_FMAC1 void* SOAP_FMAC2 soap_id_lookup(struct soap*, const char *id, void **p, int t, size_t n, unsigned int k);
+SOAP_FMAC1 void* SOAP_FMAC2 soap_id_forward(struct soap*, const char *id, void *p, size_t len, int st, int tt, size_t n, unsigned int k, void(*fcopy)(struct soap*, int, int, void*, size_t, const void*, size_t));
+#endif
+SOAP_FMAC1 void* SOAP_FMAC2 soap_id_enter(struct soap*, const char *id, void *p, int t, size_t n, unsigned int k, const char *type, const char *arrayType, void *(*finstantiate)(struct soap*, int, const char*, const char*, size_t*));
+SOAP_FMAC1 void SOAP_FMAC2 soap_fcopy(struct soap *soap, int st, int tt, void *p, size_t, const void *q, size_t n);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_size(const int *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_getoffsets(const char *, const int *, int *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_getsize(const char *, const char *, int *);
+SOAP_FMAC1 int SOAP_FMAC2 soap_getsizes(const char *, int *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_getposition(const char *, int *);
+
+SOAP_FMAC1 char* SOAP_FMAC2 soap_putsize(struct soap*, const char *, int);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_putsizesoffsets(struct soap*, const char *, const int *, const int *, int);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_putsizes(struct soap*, const char *, const int *, int);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_putoffset(struct soap*, int);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_putoffsets(struct soap*, const int *, int);
+ 
+SOAP_FMAC1 int SOAP_FMAC2 soap_closesock(struct soap*);
+
+SOAP_FMAC1 struct soap *SOAP_FMAC2 soap_new(void);
+SOAP_FMAC1 struct soap *SOAP_FMAC2 soap_new1(soap_mode);
+SOAP_FMAC1 struct soap *SOAP_FMAC2 soap_new2(soap_mode, soap_mode);
+SOAP_FMAC1 void SOAP_FMAC2 soap_free(struct soap*);
+SOAP_FMAC1 struct soap *SOAP_FMAC2 soap_copy(const struct soap*);
+SOAP_FMAC1 struct soap *SOAP_FMAC2 soap_copy_context(struct soap*, const struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_copy_stream(struct soap*, struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_init(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_init1(struct soap*, soap_mode);
+SOAP_FMAC1 void SOAP_FMAC2 soap_init2(struct soap*, soap_mode, soap_mode);
+SOAP_FMAC1 void SOAP_FMAC2 soap_done(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_cleanup(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_begin(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_end(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_delete(struct soap*, void*);
+
+#ifdef SOAP_DEBUG
+SOAP_FMAC1 void SOAP_FMAC2 soap_set_recv_logfile(struct soap*, const char*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_set_sent_logfile(struct soap*, const char*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_set_test_logfile(struct soap*, const char*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_close_logfiles(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_open_logfile(struct soap*, int);
+#endif
+
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_value(struct soap*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_match_tag(struct soap*, const char*, const char *);
+SOAP_FMAC1 int SOAP_FMAC2 soap_match_array(struct soap*, const char*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_element(struct soap*, const char*, int, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_element_begin_out(struct soap*, const char *tag, int id, const char *type);
+SOAP_FMAC1 int SOAP_FMAC2 soap_array_begin_out(struct soap*, const char *tag, int id, const char *type, const char *offset);
+SOAP_FMAC1 int SOAP_FMAC2 soap_element_ref(struct soap*, const char *tag, int id, int href);
+SOAP_FMAC1 int SOAP_FMAC2 soap_element_href(struct soap*, const char *tag, int id, const char *ref, const char *val);
+SOAP_FMAC1 int SOAP_FMAC2 soap_element_null(struct soap*, const char *tag, int id, const char *type);
+SOAP_FMAC1 int SOAP_FMAC2 soap_element_id(struct soap*, const char *tag, int id, const void *p, const struct soap_array *a, int d, const char *type, int n);
+SOAP_FMAC1 int SOAP_FMAC2 soap_element_result(struct soap*, const char *tag);
+SOAP_FMAC1 void SOAP_FMAC2 soap_check_result(struct soap*, const char *tag);
+SOAP_FMAC1 int SOAP_FMAC2 soap_element_end_out(struct soap*, const char *tag);
+SOAP_FMAC1 int SOAP_FMAC2 soap_element_start_end_out(struct soap*, const char *tag);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_attribute(struct soap*, const char*, const char*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_element_begin_in(struct soap*, const char *tag, int nillable, const char *type);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_element_end_in(struct soap*, const char *tag);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_peek_element(struct soap*);
+
+SOAP_FMAC1 void SOAP_FMAC2 soap_retry(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_revert(struct soap*);
+
+SOAP_FMAC1 char* SOAP_FMAC2 soap_strdup(struct soap*, const char*);
+SOAP_FMAC1 wchar_t* SOAP_FMAC2 soap_wstrdup(struct soap*, const wchar_t*);
+SOAP_FMAC1 const char * SOAP_FMAC2 soap_strsearch(const char *big, const char *little);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_string_out(struct soap*, const char *s, int flag);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_string_in(struct soap*, int, long, long);
+
+#ifndef WITH_LEANER
+SOAP_FMAC1 int SOAP_FMAC2 soap_wstring_out(struct soap*, const wchar_t *s, int flag);
+SOAP_FMAC1 wchar_t* SOAP_FMAC2 soap_wstring_in(struct soap*, int, long, long);
+#endif
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_match_namespace(struct soap*, const char *, const char*, size_t n1, size_t n2);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_set_namespaces(struct soap*, const struct Namespace*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_set_local_namespaces(struct soap*);
+
+SOAP_FMAC1 void SOAP_FMAC2 soap_pop_namespace(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_push_namespace(struct soap*, const char *,const char *);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_current_namespace(struct soap *soap, const char *tag);
+
+SOAP_FMAC1 struct soap_nlist* SOAP_FMAC2 soap_lookup_ns(struct soap *soap, const char *tag, size_t n);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_store_lab(struct soap*, const char*, size_t);
+SOAP_FMAC1 int SOAP_FMAC2 soap_append_lab(struct soap*, const char*, size_t);
+
+SOAP_FMAC1 struct soap_blist* SOAP_FMAC2 soap_new_block(struct soap*);
+SOAP_FMAC1 void* SOAP_FMAC2 soap_push_block(struct soap*, struct soap_blist*, size_t);
+SOAP_FMAC1 void SOAP_FMAC2 soap_pop_block(struct soap*, struct soap_blist*);
+SOAP_FMAC1 size_t SOAP_FMAC2 soap_size_block(struct soap*, struct soap_blist*, size_t);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_first_block(struct soap*, struct soap_blist*);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_next_block(struct soap*, struct soap_blist*);
+SOAP_FMAC1 size_t SOAP_FMAC2 soap_block_size(struct soap*, struct soap_blist*);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_save_block(struct soap*, struct soap_blist*, char*, int);
+SOAP_FMAC1 void SOAP_FMAC2 soap_end_block(struct soap*, struct soap_blist*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_envelope_begin_out(struct soap*);
+SOAP_FMAC1 int soap_envelope_end_out(struct soap*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_envelope_begin_in(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_envelope_end_in(struct soap*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_body_begin_out(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_body_end_out(struct soap*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_body_begin_in(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_body_end_in(struct soap*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_recv_header(struct soap*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_response(struct soap*, int);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_send_empty_response(struct soap*, int status);
+SOAP_FMAC1 int SOAP_FMAC2 soap_recv_empty_response(struct soap*);
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_send_fault(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_recv_fault(struct soap*);
+
+#ifndef WITH_NOSTDLIB
+SOAP_FMAC1 void SOAP_FMAC2 soap_print_fault(struct soap*, FILE*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_print_fault_location(struct soap*, FILE*);
+# ifndef WITH_LEAN
+#  ifdef __cplusplus
+SOAP_FMAC1 void SOAP_FMAC2 soap_stream_fault(struct soap*, std::ostream&);
+#  endif
+SOAP_FMAC1 char* SOAP_FMAC2 soap_sprint_fault(struct soap*, char*, size_t);
+# endif
+#endif
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2byte(struct soap*, const char*, char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2short(struct soap*, const char*, short*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2int(struct soap*, const char*, int*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2long(struct soap*, const char*, long*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2LONG64(struct soap*, const char*, LONG64*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2float(struct soap*, const char*, float*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2double(struct soap*, const char*, double*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2unsignedByte(struct soap*, const char*, unsigned char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2unsignedShort(struct soap*, const char*, unsigned short*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2unsignedInt(struct soap*, const char*, unsigned int*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2unsignedLong(struct soap*, const char*, unsigned long*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2ULONG64(struct soap*, const char*, ULONG64*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2string(struct soap*, const char*, char**);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2QName(struct soap*, const char*, char**);
+
+#ifndef WITH_LEAN
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2wchar(struct soap*, const char*, wchar_t**);
+SOAP_FMAC1 int SOAP_FMAC2 soap_s2dateTime(struct soap*, const char*, time_t*);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_s2base64(struct soap*, const unsigned char*, char*, int);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_s2hex(struct soap*, const unsigned char*, char*, int);
+#endif
+
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_byte2s(struct soap*, char);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_short2s(struct soap*, short);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_int2s(struct soap*, int);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_long2s(struct soap*, long);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_LONG642s(struct soap*, LONG64);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_float2s(struct soap*, float);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_double2s(struct soap*, double);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_unsignedByte2s(struct soap*, unsigned char);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_unsignedShort2s(struct soap*, unsigned short);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_unsignedInt2s(struct soap*, unsigned int);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_unsignedLong2s(struct soap*, unsigned long);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_ULONG642s(struct soap*, ULONG64);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_QName2s(struct soap*, const char*);
+
+#ifndef WITH_LEAN
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_wchar2s(struct soap*, const wchar_t*);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_dateTime2s(struct soap*, time_t);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_base642s(struct soap*, const char*, char*, size_t, int*);
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_hex2s(struct soap*, const char*, char*, size_t, int*);
+#endif
+
+
+SOAP_FMAC1 int* SOAP_FMAC2 soap_inint(struct soap*, const char *tag, int *p, const char *, int);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_inbyte(struct soap*, const char *tag, char *p, const char *, int);
+SOAP_FMAC1 long* SOAP_FMAC2 soap_inlong(struct soap*, const char *tag, long *p, const char *, int);
+SOAP_FMAC1 LONG64* SOAP_FMAC2 soap_inLONG64(struct soap*, const char *tag, LONG64 *p, const char *, int);
+SOAP_FMAC1 short* SOAP_FMAC2 soap_inshort(struct soap*, const char *tag, short *p, const char *, int);
+SOAP_FMAC1 float* SOAP_FMAC2 soap_infloat(struct soap*, const char *tag, float *p, const char *, int);
+SOAP_FMAC1 double* SOAP_FMAC2 soap_indouble(struct soap*, const char *tag, double *p, const char *, int);
+SOAP_FMAC1 unsigned char* SOAP_FMAC2 soap_inunsignedByte(struct soap*, const char *tag, unsigned char *p, const char *, int);
+SOAP_FMAC1 unsigned short* SOAP_FMAC2 soap_inunsignedShort(struct soap*, const char *tag, unsigned short *p, const char *, int);
+SOAP_FMAC1 unsigned int* SOAP_FMAC2 soap_inunsignedInt(struct soap*, const char *tag, unsigned int *p, const char *, int);
+SOAP_FMAC1 unsigned long* SOAP_FMAC2 soap_inunsignedLong(struct soap*, const char *tag, unsigned long *p, const char *, int);
+SOAP_FMAC1 ULONG64* SOAP_FMAC2 soap_inULONG64(struct soap*, const char *tag, ULONG64 *p, const char *, int);
+SOAP_FMAC1 char** SOAP_FMAC2 soap_instring(struct soap*, const char *tag, char **p, const char *, int, int, long, long);
+SOAP_FMAC1 char** SOAP_FMAC2 soap_inliteral(struct soap*, const char *tag, char **p);
+
+#ifndef WITH_LEAN
+SOAP_FMAC1 time_t* SOAP_FMAC2 soap_indateTime(struct soap*, const char *tag, time_t *p, const char *, int);
+SOAP_FMAC1 time_t SOAP_FMAC2 soap_timegm(struct tm*);
+#endif
+
+#ifndef WITH_LEANER
+SOAP_FMAC1 wchar_t** SOAP_FMAC2 soap_inwstring(struct soap*, const char *tag, wchar_t **p, const char *, int, long, long);
+SOAP_FMAC1 wchar_t** SOAP_FMAC2 soap_inwliteral(struct soap*, const char *tag, wchar_t **p);
+#endif
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_outbyte(struct soap*, const char *tag, int id, const char *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outshort(struct soap*, const char *tag, int id, const short *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outint(struct soap*, const char *tag, int id, const int *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outlong(struct soap*, const char *tag, int id, const long *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outLONG64(struct soap*, const char *tag, int id, const LONG64 *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outfloat(struct soap*, const char *tag, int id, const float *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outdouble(struct soap*, const char *tag, int id, const double *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outunsignedByte(struct soap*, const char *tag, int id, const unsigned char *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outunsignedShort(struct soap*, const char *tag, int id, const unsigned short *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outunsignedInt(struct soap*, const char *tag, int id, const unsigned int *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outunsignedLong(struct soap*, const char *tag, int id, const unsigned long *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outULONG64(struct soap*, const char *tag, int id, const ULONG64 *p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outstring(struct soap*, const char *tag, int id, char *const*p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outliteral(struct soap*, const char *tag, char *const*p, const char *type);
+
+#ifndef WITH_LEAN
+SOAP_FMAC1 int SOAP_FMAC2 soap_outdateTime(struct soap*, const char *tag, int id, const time_t *p, const char *, int);
+#endif
+
+#ifndef WITH_LEANER
+SOAP_FMAC1 int SOAP_FMAC2 soap_outwstring(struct soap*, const char *tag, int id, wchar_t *const*p, const char *, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_outwliteral(struct soap*, const char *tag, wchar_t *const*p, const char *type);
+#endif
+
+#ifndef WITH_LEANER
+SOAP_FMAC1 int SOAP_FMAC2 soap_attachment(struct soap *, const char*, int, const void*, const struct soap_array*, const char*, const char*, const char*, int, const char*, int);
+SOAP_FMAC1 int SOAP_FMAC2 soap_move(struct soap*, long);
+SOAP_FMAC1 size_t SOAP_FMAC2 soap_tell(struct soap*);
+SOAP_FMAC1 char* SOAP_FMAC2 soap_dime_option(struct soap*, unsigned short, const char*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_getdimehdr(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_getdime(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_putdimehdr(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_putdime(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_getmimehdr(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_getmime(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_putmimehdr(struct soap*, struct soap_multipart*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_putmime(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_set_dime(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_set_mime(struct soap*, const char *boundary, const char *start);
+SOAP_FMAC1 void SOAP_FMAC2 soap_clr_dime(struct soap*);
+SOAP_FMAC1 void SOAP_FMAC2 soap_clr_mime(struct soap*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_set_dime_attachment(struct soap*, char *ptr, size_t size, const char *type, const char *id, unsigned short optype, const char *option);
+SOAP_FMAC1 int SOAP_FMAC2 soap_set_mime_attachment(struct soap*, char *ptr, size_t size, enum soap_mime_encoding encoding, const char *type, const char *id, const char *location, const char *description);
+SOAP_FMAC1 void SOAP_FMAC2 soap_post_check_mime_attachments(struct soap *soap);
+SOAP_FMAC1 int SOAP_FMAC2 soap_check_mime_attachments(struct soap *soap);
+SOAP_FMAC1 struct soap_multipart* SOAP_FMAC2 soap_get_mime_attachment(struct soap *soap, void *handle);
+SOAP_FMAC1 struct soap_multipart* SOAP_FMAC2 soap_next_multipart(struct soap_multipart*);
+SOAP_FMAC1 int SOAP_FMAC2 soap_match_cid(struct soap*, const char*, const char*);
+#endif
+
+SOAP_FMAC1 int SOAP_FMAC2 soap_register_plugin_arg(struct soap*, int (*fcreate)(struct soap*, struct soap_plugin*, void*), void*);
+SOAP_FMAC1 void* SOAP_FMAC2 soap_lookup_plugin(struct soap*, const char*);
+
+SOAP_FMAC1 const char* SOAP_FMAC2 soap_attr_value(struct soap *soap, const char *name, int flag);
+SOAP_FMAC1 int SOAP_FMAC2 soap_set_attr(struct soap *soap, const char *name, const char *value);
+SOAP_FMAC1 void SOAP_FMAC2 soap_clr_attr(struct soap *soap);
+
+#ifdef WITH_COOKIES
+SOAP_FMAC1 void SOAP_FMAC2 soap_getcookies(struct soap *soap, const char *val);
+SOAP_FMAC1 size_t SOAP_FMAC2 soap_encode_cookie(const char*, char*, size_t);
+SOAP_FMAC1 extern struct soap_cookie* SOAP_FMAC2 soap_set_cookie(struct soap*, const char*, const char*, const char*, const char*);
+SOAP_FMAC1 extern struct soap_cookie* SOAP_FMAC2 soap_cookie(struct soap*, const char*, const char*, const char*);
+SOAP_FMAC1 extern char* SOAP_FMAC2 soap_cookie_value(struct soap*, const char*, const char*, const char*);
+SOAP_FMAC1 extern char* SOAP_FMAC2 soap_env_cookie_value(struct soap*, const char*, const char*, const char*);
+SOAP_FMAC1 extern time_t SOAP_FMAC2 soap_cookie_expire(struct soap*, const char*, const char*, const char*);
+SOAP_FMAC1 extern int SOAP_FMAC2 soap_set_cookie_expire(struct soap*, const char*, long, const char*, const char*);
+SOAP_FMAC1 extern int SOAP_FMAC2 soap_set_cookie_session(struct soap*, const char*, const char*, const char*);
+SOAP_FMAC1 extern int SOAP_FMAC2 soap_clr_cookie_session(struct soap*, const char*, const char*, const char*);
+SOAP_FMAC1 extern void SOAP_FMAC2 soap_clr_cookie(struct soap*, const char*, const char*, const char*);
+SOAP_FMAC1 extern int SOAP_FMAC2 soap_getenv_cookies(struct soap*);
+SOAP_FMAC1 extern struct soap_cookie* SOAP_FMAC2 soap_copy_cookies(struct soap*, const struct soap*);
+SOAP_FMAC1 extern void SOAP_FMAC2 soap_free_cookies(struct soap*);
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/xmalloc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/xmalloc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/xmalloc.c	(revision 22322)
@@ -0,0 +1,74 @@
+/*
+ * xmalloc.c - malloc that can't fail
+ *
+ * Copyright (C) 2003-2005  Joshua Hoblitt, 2003  Robert Lupton
+ *
+ * $Id: xmalloc.c,v 1.4 2008-09-15 21:24:30 jhoblitt Exp $
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "xmalloc.h"
+
+//#define USE_EFF 1
+#ifdef USE_EFF
+#include "efence.h"
+#endif
+
+/*****************************************************************************/
+/*
+ * Wrappers for malloc/free.  xmalloc cannot fail.
+ */
+void * x_xmalloc(
+                const char *file,
+                unsigned int lineno,
+                const char *func,
+                size_t n)
+{
+#ifdef USE_EFF
+   void *ptr = _eff_malloc(n, file, lineno);
+#else
+   void *ptr = malloc(n);
+#endif
+    
+   if (ptr == NULL) {
+      perror("malloc");
+      exit(EXIT_FAILURE);
+   }
+ 
+   return(ptr);
+}
+
+void * x_xrealloc(
+                const char *file,
+                unsigned int lineno,
+                const char *func,
+                void *ptr,
+                size_t size)
+{
+#ifdef USE_EFF
+    void *newptr = _eff_realloc(ptr, size, file, lineno);
+#else
+    void *newptr = realloc(ptr, size);
+#endif
+
+    if (!newptr) {
+        perror("realloc");
+        exit(EXIT_FAILURE);
+    } 
+
+    return(newptr);
+}
+ 
+void x_xfree(
+            const char *file,
+            unsigned int lineno,
+            const char *func,
+            void *ptr)
+{
+#ifdef USE_EFF
+    _eff_free(ptr, file, lineno);
+#else
+   free(ptr);
+#endif
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/xmalloc.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/xmalloc.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/src/xmalloc.h	(revision 22322)
@@ -0,0 +1,52 @@
+/*
+ * xmalloc.h - malloc that can't fail
+ *
+ * Copyright (C) 2003-2005  Joshua Hoblitt, 2003  Robert Lupton
+ *
+ * $Id: xmalloc.h,v 1.5 2008-09-15 21:24:30 jhoblitt Exp $
+ */
+
+#ifndef XMALLOC_H
+#define XMALLOC_H 1
+
+#define xmalloc(size) \
+x_xmalloc(__FILE__, __LINE__, __func__, size)
+
+void * x_xmalloc(
+    const char *file,                  ///< File of caller
+    unsigned int lineno,               ///< Line number of caller
+    const char *func,                  ///< Function name of caller
+    size_t n
+#ifdef __GNUC__
+) __attribute__((malloc));
+# else // ifdef __GNUC__
+);
+#endif // ifdef __GNUC__
+
+
+#define xrealloc(ptr, size) \
+x_xrealloc(__FILE__, __LINE__, __func__, ptr, size)
+
+void * x_xrealloc(
+    const char *file,                  ///< File of caller
+    unsigned int lineno,               ///< Line number of caller
+    const char *func,                  ///< Function name of caller
+    void *ptr,
+    size_t size
+#ifdef __GNUC__
+) __attribute__((malloc));
+# else // ifdef __GNUC__
+);
+#endif // ifdef __GNUC__
+ 
+#define xfree(ptr) \
+x_xfree(__FILE__, __LINE__, __func__, ptr)
+
+void x_xfree(
+    const char *file,                  ///< File of caller
+    unsigned int lineno,               ///< Line number of caller
+    const char *func,                  ///< Function name of caller
+    void *ptr
+);
+
+#endif // XMALLOC_H
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+.deps
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+SUBDIRS = tap
+
+TESTS = tests
+
+AM_CPPFLAGS = \
+    -I$(top_srcdir)/tests/tap/src \
+    -I$(top_srcdir)/src
+
+AM_LDFLAGS = \
+    $(top_builddir)/tests/tap/src/libtap.la \
+    $(top_builddir)/src/libnebclient.la
+
+check_PROGRAMS = tests
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/.cvsignore	(revision 22322)
@@ -0,0 +1,13 @@
+.in
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.log
+config.status
+configure
+libtool
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/INSTALL	(revision 22322)
@@ -0,0 +1,8 @@
+Quick Installation
+
+    ./configure
+    make
+    make check
+    make install
+
+Run "configure --help" for additional options.
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/LICENSE	(revision 22322)
@@ -0,0 +1,23 @@
+Copyright (c) 2004 Nik Clayton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/Makefile.am	(revision 22322)
@@ -0,0 +1,5 @@
+SUBDIRS  = src
+#SUBDIRS += tests
+
+prove:
+	prove -v -r
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/README	(revision 22322)
@@ -0,0 +1,11 @@
+NAME
+     tap -- write tests that implement the Test Anything Protocol
+
+SYNOPSIS
+     #include <tap.h>
+
+DESCRIPTION
+     The tap library provides functions for writing test scripts that produce
+     output consistent with the Test Anything Protocol.  A test harness that
+     parses this protocol can run these tests and produce useful reports indi-
+     cating their success or failure.
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/bootstrap.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/bootstrap.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/bootstrap.sh	(revision 22322)
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+set -x
+aclocal || exit 1
+autoheader || autoheader || exit 1
+libtoolize -c -f || libtoolize -c -f || glibtoolize -c -f || exit 1
+automake -a -c || automake -a -c || exit 1
+autoconf || autoconf || exit 1
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/compile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/compile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/compile	(revision 22322)
@@ -0,0 +1,142 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand `-c -o'.
+
+scriptversion=2004-10-12.08
+
+# Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+case $1 in
+  '')
+     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand `-c -o'.
+Remove `-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file `INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit 0
+    ;;
+  -v | --v*)
+    echo "compile $scriptversion"
+    exit 0
+    ;;
+esac
+
+ofile=
+cfile=
+eat=
+
+for arg
+do
+  if test -n "$eat"; then
+    eat=
+  else
+    case $1 in
+      -o)
+	# configure might choose to run compile as `compile cc -o foo foo.c'.
+	# So we strip `-o arg' only if arg is an object.
+	eat=1
+	case $2 in
+	  *.o | *.obj)
+	    ofile=$2
+	    ;;
+	  *)
+	    set x "$@" -o "$2"
+	    shift
+	    ;;
+	esac
+	;;
+      *.c)
+	cfile=$1
+	set x "$@" "$1"
+	shift
+	;;
+      *)
+	set x "$@" "$1"
+	shift
+	;;
+    esac
+  fi
+  shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+  # If no `-o' option was seen then we might have been invoked from a
+  # pattern rule where we don't need one.  That is ok -- this is a
+  # normal compilation that the losing compiler can handle.  If no
+  # `.c' file was seen then we are probably linking.  That is also
+  # ok.
+  exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use `[/.-]' here to ensure that we don't use the same name
+# that we are using for the .o file.  Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d
+while true; do
+  if mkdir "$lockdir" >/dev/null 2>&1; then
+    break
+  fi
+  sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+  mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+  mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/configure.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/configure.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/configure.in	(revision 22322)
@@ -0,0 +1,44 @@
+AC_INIT(tap, 1.01)
+AC_CONFIG_SRCDIR(src/tap.c)
+AM_INIT_AUTOMAKE([foreign])
+AC_CONFIG_HEADERS([src/config.h])
+AC_GNU_SOURCE
+AC_PROG_CC
+AC_PROG_LIBTOOL
+AC_PROG_INSTALL
+
+# Checks for libraries
+case "$host" in
+	*-*-*freebsd4*)
+		LDFLAGS="$LDFLAGS -pthread"
+		HAVE_LIBPTHREAD=1
+		;;
+	*)
+		AC_CHECK_LIB(pthread, main)
+		;;
+esac
+
+dnl build tests at the same time as the source code
+AC_ARG_ENABLE(tests,
+  [AS_HELP_STRING(--enable-tests,build tests at same time as source)],
+  [AC_MSG_RESULT(test building enabled)
+   tests=true],
+   [tests=false])
+AM_CONDITIONAL(BUILD_TESTS, test x$tests = xtrue)
+
+# Checks for header files
+AC_HEADER_STDC
+AC_CHECK_HEADERS([stdlib.h])
+AC_CHECK_HEADERS([pthread.h])
+
+# Checks for  typedefs, structures, and compiler characteristics.
+AC_C_CONST
+
+# Checks for library functions.
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS([atexit])
+
+AC_CONFIG_FILES([Makefile
+		 src/Makefile
+		])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/.cvsignore	(revision 22322)
@@ -0,0 +1,13 @@
+.deps
+.libs
+Makefile
+Makefile.in
+libtap.la
+tap.lo
+config.h
+config.h.in
+stamp-h1
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/Makefile.am	(revision 22322)
@@ -0,0 +1,9 @@
+TEST_LTLIBS = libtap.la
+libtap_la_SOURCES = tap.c tap.h
+noinst_HEADERS = tap.h
+
+noinst_LTLIBRARIES = $(TEST_LTLIBS)
+
+#man_MANS = tap.3
+EXTRA_DIST = $(man_MANS)
+
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/tap.3
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/tap.3	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/tap.3	(revision 22322)
@@ -0,0 +1,380 @@
+.Dd December 20, 2004
+.Os
+.Dt TAP 3
+.Sh NAME
+.Nm tap
+.Nd write tests that implement the Test Anything Protocol
+.Sh SYNOPSIS
+.In tap.h
+.Sh DESCRIPTION
+The
+.Nm
+library provides functions for writing test scripts that produce output
+consistent with the Test Anything Protocol.  A test harness that parses
+this protocol can run these tests and produce useful reports indicating
+their success or failure.
+.Ss PRINTF STRINGS
+In the descriptions that follow, for any function that takes as the
+last two parameters
+.Dq Fa char * , Fa ...
+it can be assumed that the
+.Fa char *
+is a
+.Fn printf
+-like format string, and the optional arguments are values to be placed
+in that string.
+.Ss TEST PLANS
+.Bl -tag -width indent
+.It Xo
+.Ft int
+.Fn plan_tests "unsigned int"
+.Xc
+.It Xo
+.Ft int
+.Fn plan_no_plan "void"
+.Xc
+.It Xo
+.Ft int
+.Fn plan_skip_all "char *" "..."
+.Xc
+.El
+.Pp
+You must first specify a test plan.  This indicates how many tests you
+intend to run, and allows the test harness to notice if any tests were
+missed, or if the test program exited prematurely.
+.Pp
+To do this, use
+.Fn plan_tests ,
+which returns the number of planned tests.  The function will cause
+your program to exit prematurely if you specify 0 tests.
+.Pp
+In some situations you may not know how many tests you will be running, or
+you are developing your test program, and do not want to update the
+.Fn plan_tests
+parameter every time you make a change.  For those situations use
+.Fn plan_no_plan .
+It returns 1, and indicates to the test harness that an indeterminate number
+of tests will be run.
+.Pp
+Both
+.Fn plan_tests
+and
+.Fn plan_no_plan
+will cause your test program to exit prematurely with a diagnostic
+message if they are called more than once.
+.Pp
+If your test program detects at run time that some required functionality
+is missing (for example, it relies on a database connection which is not
+present, or a particular configuration option that has not been included
+in the running kernel) use
+.Fn plan_skip_all ,
+passing as parameters a string to display indicating the reason for skipping
+the tests.
+.Ss SIMPLE TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft unsigned int
+.Fn ok "expression" "char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn ok1 "expression"
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn pass "char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn fail "char *" "..."
+.Xc
+.El
+.Pp
+Tests are implemented as expressions checked by calls to the
+.Fn ok
+and
+.Fn ok1
+macros.  In both cases
+.Fa expression
+should evaluate to true if the test succeeded.
+.Pp
+.Fn ok
+allows you to specify a name, or comment, describing the test which will
+be included in the output.
+.Fn ok1
+is for those times when the expression to be tested is self
+explanatory and does not need an associated comment.  In those cases
+the test expression becomes the comment.
+.Pp
+These four calls are equivalent:
+.Bd -literal -offset indent
+int i = 5;
+
+ok(i == 5, "i equals 5");      /* Overly verbose */
+ok(i == 5, "i equals %d", i);  /* Just to demonstrate printf-like
+                                  behaviour of the test name */
+ok(i == 5, "i == 5");          /* Needless repetition */
+ok1(i == 5);                   /* Just right */
+.Ed
+.Pp
+It is good practice to ensure that the test name describes the meaning
+behind the test rather than what you are testing.  Viz
+.Bd -literal -offset indent
+ok(db != NULL, "db is not NULL");            /* Not bad, but */
+ok(db != NULL, "Database conn. succeeded");  /* this is better */
+.Ed
+.Pp
+.Fn ok
+and
+.Fn ok1
+return 1 if the expression evaluated to true, and 0 if it evaluated to
+false.  This lets you chain calls from
+.Fn ok
+to
+.Fn diag
+to only produce diagnostic output if the test failed.  For example, this
+code will include diagnostic information about why the database connection
+failed, but only if the test failed.
+.Bd -literal -offset indent
+ok(db != NULL, "Database conn. succeeded") ||
+    diag("Database error code: %d", dberrno);
+.Ed
+.Pp
+You also have
+.Fn pass
+and
+.Fn fail .
+From the Test::More documentation:
+.Bd -literal -offset indent
+Sometimes you just want to say that the tests have passed.
+Usually the case is you've got some complicated condition
+that is difficult to wedge into an ok().  In this case,
+you can simply use pass() (to declare the test ok) or fail
+(for not ok).
+
+Use these very, very, very sparingly.
+.Ed
+.Pp
+These are synonyms for ok(1, ...) and ok(0, ...).
+.Ss SKIPPING TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft int
+.Fn skip "unsigned int" "char *" "..."
+.Xc
+.It Xo
+.Fn skip_start "expression" "unsigned int" "char *" "..."
+.Xc
+.It Xo
+.Fn skip_end
+.Xc
+.El
+.Pp
+Sets of tests can be skipped.  Ordinarily you would do this because
+the test can't be run in this particular testing environment.
+.Pp
+For example, suppose some tests should be run as root.  If the test is
+not being run as root then the tests should be skipped.  In this 
+implementation, skipped tests are flagged as being ok, with a special
+message indicating that they were skipped.  It is your responsibility
+to ensure that the number of tests skipped (the first parameter to
+.Fn skip )
+is correct for the number of tests to skip.
+.Pp
+One way of implementing this is with a
+.Dq do { } while(0);
+loop, or an
+.Dq if( ) { } else { }
+construct, to ensure that there are no additional side effects from the
+skipped tests.
+.Bd -literal -offset indent
+if(getuid() != 0) {
+        skip(1, "because test only works as root");
+} else {
+        ok(do_something_as_root() == 0, "Did something as root");
+}
+.Ed
+.Pp
+Two macros are provided to assist with this.  The previous example could
+be re-written as follows.
+.Bd -literal -offset indent
+skip_start(getuid() != 0, 1, "because test only works as root");
+
+ok(do_something_as_root() == 0, "Did something as root");
+
+skip_end();
+.Ed
+.Ss MARKING TESTS AS Dq TODO
+.Bl -tag -width indent
+.It Xo
+.Ft void
+.Fn todo_start "char *" "..."
+.Xc
+.It Xo
+.Ft void
+.Fn todo_end "void"
+.Xc
+.El
+.Pp
+Sets of tests can be flagged as being
+.Dq TODO .
+These are tests that you expect to fail, probably because you haven't
+fixed a bug, or finished a new feature yet.  These tests will still be
+run, but with additional output that indicates that they are expected
+to fail.  Should a test start to succeed unexpectedly, tools like
+.Xr prove 1
+will indicate this, and you can move the test out of the todo
+block.  This is much more useful than simply commenting out (or
+.Dq #ifdef 0 ... #endif )
+the tests.
+.Bd -literal -offset indent
+todo_start("dwim() not returning true yet");
+
+ok(dwim(), "Did what the user wanted");
+
+todo_end();
+.Ed
+.Pp
+Should
+.Fn dwim
+ever start succeeding you will know about it as soon as you run the
+tests.  Note that
+.Em unlike
+the
+.Fn skip_*
+family, additional code between
+.Fn todo_start
+and
+.Fn todo_end
+.Em is
+executed.
+.Ss SKIP vs. TODO
+From the Test::More documentation;
+.Bd -literal -offset indent
+If it's something the user might not be able to do, use SKIP.
+This includes optional modules that aren't installed, running
+under an OS that doesn't have some feature (like fork() or
+symlinks), or maybe you need an Internet connection and one
+isn't available.
+
+If it's something the programmer hasn't done yet, use TODO.
+This is for any code you haven't written yet, or bugs you have
+yet to fix, but want to put tests in your testing script 
+(always a good idea).
+.Ed
+.Ss DIAGNOSTIC OUTPUT
+.Bl -tag -width indent
+.It Xo
+.Fr unsigned int
+.Fn diag "char *" "..."
+.Xc
+.El
+.Pp
+If your tests need to produce diagnostic output, use
+.Fn diag .
+It ensures that the output will not be considered by the TAP test harness.
+.Fn diag
+adds the necessary trailing
+.Dq \en
+for you.
+.Bd -literal -offset indent
+diag("Expected return code 0, got return code %d", rcode);
+.Ed
+.Pp
+.Fn diag
+always returns 0.
+.Ss EXIT STATUS
+.Bl -tag -width indent
+.It Xo
+.Fr int
+.Fn exit_status void
+.Xc
+.El
+.Pp
+For maximum compatability your test program should return a particular
+exit code.  This is calculated by
+.Fn exit_status
+so it is sufficient to always return from
+.Fn main
+with either
+.Dq return exit_status();
+or
+.Dq exit(exit_status());
+as appropriate.
+.Sh ENVIRONMENT
+The following environment variables affect
+.Nm .
+.Bl -tag -width indent
+.It Ev HARNESS_ACTIVE
+Causes an extra
+.Dq \en
+to be printed before any diagnostic failure output generated by
+.Nm .
+This variable is normally set if tests are being run under Perl's
+Test::Harness.
+.El
+.Sh EXAMPLES
+The
+.Pa tests
+directory in the source distribution contains numerous tests of
+.Nm
+functionality, written using
+.Nm .
+Examine them for examples of how to construct test suites.
+.Sh COMPATABILITY
+.Nm
+strives to be compatible with the Perl Test::More and Test::Harness 
+modules.  The test suite verifies that
+.Nm
+is bug-for-bug compatible with their behaviour.  This is why some
+functions which would more naturally return nothing return constant
+values.
+.Pp
+If the
+.Lb libpthread
+is found at compile time,
+.Nm
+.Em should
+be thread safe.  Indications to the contrary (and test cases that expose
+incorrect behaviour) are very welcome.
+.Sh SEE ALSO
+.Xr Test::More 1 ,
+.Xr Test::Harness 1 ,
+.Xr prove 1
+.Sh STANDARDS
+.Nm
+requires a
+.St -isoC-99
+compiler.  Some of the
+.Nm
+functionality is implemented as variadic macros, and that functionality
+was not formally codified until C99.  Patches to use
+.Nm
+with earlier compilers that have their own implementation of variadic
+macros will be gratefully received.
+.Sh HISTORY
+.Nm
+was written to help improve the quality and coverage of the FreeBSD
+regression test suite, and released in the hope that others find it
+a useful tool to help improve the quality of their code.
+.Sh AUTHORS
+.An "Nik Clayton" Aq nik@ngo.org.uk ,
+.Aq nik@FreeBSD.org
+.Pp
+.Nm
+would not exist without the efforts of
+.An "Michael G Schwern" Aq schqern@pobox.com ,
+.An "Andy Lester" Aq andy@petdance.com ,
+and the countless others who have worked on the Perl QA programme.
+.Sh BUGS
+Ideally, running the tests would have no side effects on the behaviour
+of the application you are testing.  However, it is not always possible
+to avoid them.  The following side effects of using
+.Nm
+are known.
+.Bl -bullet -offset indent
+.It
+stdout is set to unbuffered mode after calling any of the
+.Fn plan_*
+functions.
+.El
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/tap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/tap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/tap.c	(revision 22322)
@@ -0,0 +1,430 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap.h"
+
+static int no_plan = 0;
+static int skip_all = 0;
+static int have_plan = 0;
+static unsigned int test_count = 0; /* Number of tests that have been run */
+static unsigned int e_tests = 0; /* Expected number of tests to run */
+static unsigned int failures = 0; /* Number of tests that failed */
+static char *todo_msg = NULL;
+static char *todo_msg_fixed = "libtap malloc issue";
+static int todo = 0;
+static int test_died = 0;
+
+/* Encapsulate the pthread code in a conditional.  In the absence of
+   libpthread the code does nothing */
+#ifdef HAVE_LIBPTHREAD
+#include <pthread.h>
+static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK pthread_mutex_lock(&M);
+# define UNLOCK pthread_mutex_unlock(&M);
+#else
+# define LOCK
+# define UNLOCK
+#endif
+
+static void _expected_tests(unsigned int);
+static void _tap_init(void);
+static void _cleanup(void);
+
+/*
+ * Generate a test result.
+ *
+ * ok -- boolean, indicates whether or not the test passed.
+ * test_name -- the name of the test, may be NULL
+ * test_comment -- a comment to print afterwards, may be NULL
+ */
+unsigned int
+_gen_result(int ok, const char *func, char *file, unsigned int line,
+            char *test_name, ...)
+{
+    va_list ap;
+    char *local_test_name = NULL;
+    char *c;
+    int name_is_digits;
+
+    LOCK;
+
+    test_count++;
+
+    /* Start by taking the test name and performing any printf()
+       expansions on it */
+    if(test_name != NULL) {
+        va_start(ap, test_name);
+        vasprintf(&local_test_name, test_name, ap);
+        va_end(ap);
+
+        /* Make sure the test name contains more than digits
+           and spaces.  Emit an error message and exit if it
+           does */
+        if(local_test_name) {
+            name_is_digits = 1;
+            for(c = local_test_name; *c != '\0'; c++) {
+                if(!isdigit(*c) && !isspace(*c)) {
+                    name_is_digits = 0;
+                    break;
+                }
+            }
+
+            if(name_is_digits) {
+                diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
+                diag("    Very confusing.");
+            }
+        }
+    }
+
+    if(!ok) {
+        printf("not ");
+        failures++;
+    }
+
+    printf("ok %d", test_count);
+
+    if(test_name != NULL) {
+        printf(" - ");
+
+        /* Print the test name, escaping any '#' characters it
+           might contain */
+        if(local_test_name != NULL) {
+            flockfile(stdout);
+            for(c = local_test_name; *c != '\0'; c++) {
+                if(*c == '#')
+                    fputc('\\', stdout);
+                fputc((int)*c, stdout);
+            }
+            funlockfile(stdout);
+        } else { /* vasprintf() failed, use a fixed message */
+            printf("%s", todo_msg_fixed);
+        }
+    }
+
+    /* If we're in a todo_start() block then flag the test as being
+       TODO.  todo_msg should contain the message to print at this
+       point.  If it's NULL then asprintf() failed, and we should
+       use the fixed message.
+
+       This is not counted as a failure, so decrement the counter if
+       the test failed. */
+    if(todo) {
+        printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
+        if(!ok)
+            failures--;
+    }
+
+    printf("\n");
+
+    if(!ok) {
+        if(getenv("HARNESS_ACTIVE") != NULL)
+            fputs("\n", stderr);
+
+        diag("    Failed %stest (%s:%s() at line %d)",
+             todo ? "(TODO) " : "", file, func, line);
+    }
+    free(local_test_name);
+
+    UNLOCK;
+
+    /* We only care (when testing) that ok is positive, but here we
+       specifically only want to return 1 or 0 */
+    return ok ? 1 : 0;
+}
+
+/*
+ * Initialise the TAP library.  Will only do so once, however many times it's
+ * called.
+ */
+void
+_tap_init(void)
+{
+    static int run_once = 0;
+
+    if(!run_once) {
+        atexit(_cleanup);
+
+        /* stdout needs to be unbuffered so that the output appears
+           in the same place relative to stderr output as it does 
+           with Test::Harness */
+        setbuf(stdout, 0);
+        run_once = 1;
+    }
+}
+
+/*
+ * Note that there's no plan.
+ */
+int
+plan_no_plan(void)
+{
+
+    LOCK;
+
+    _tap_init();
+
+    if(have_plan != 0) {
+        fprintf(stderr, "You tried to plan twice!\n");
+        test_died = 1;
+        UNLOCK;
+        exit(255);
+    }
+
+    have_plan = 1;
+    no_plan = 1;
+
+    UNLOCK;
+
+    return 1;
+}
+
+/*
+ * Note that the plan is to skip all tests
+ */
+int
+plan_skip_all(char *reason)
+{
+
+    LOCK;
+
+    _tap_init();
+
+    skip_all = 1;
+
+    printf("1..0");
+
+    if(reason != NULL)
+        printf(" # Skip %s", reason);
+
+    printf("\n");
+
+    UNLOCK;
+
+    exit(0);
+}
+
+/*
+ * Note the number of tests that will be run.
+ */
+int
+plan_tests(unsigned int tests)
+{
+
+    LOCK;
+
+    _tap_init();
+
+    if(have_plan != 0) {
+        fprintf(stderr, "You tried to plan twice!\n");
+        test_died = 1;
+        UNLOCK;
+        exit(255);
+    }
+
+    if(tests == 0) {
+        fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
+        test_died = 1;
+        UNLOCK;
+        exit(255);
+    }
+
+    have_plan = 1;
+
+    _expected_tests(tests);
+
+    UNLOCK;
+
+    return e_tests;
+}
+
+unsigned int
+diag(char *fmt, ...)
+{
+    va_list ap;
+
+    fputs("# ", stderr);
+
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+
+    fputs("\n", stderr);
+
+    return 0;
+}
+
+void
+_expected_tests(unsigned int tests)
+{
+
+    printf("1..%d\n", tests);
+    e_tests = tests;
+}
+
+int
+skip(unsigned int n, char *fmt, ...)
+{
+    va_list ap;
+    char *skip_msg;
+
+    LOCK;
+
+    va_start(ap, fmt);
+    asprintf(&skip_msg, fmt, ap);
+    va_end(ap);
+
+    while(n-- > 0) {
+        test_count++;
+        printf("ok %d # skip %s\n", test_count,
+               skip_msg != NULL ?
+               skip_msg : "libtap():malloc() failed");
+    }
+
+    free(skip_msg);
+
+    UNLOCK;
+
+    return 1;
+}
+
+void
+todo_start(char *fmt, ...)
+{
+    va_list ap;
+
+    LOCK;
+
+    va_start(ap, fmt);
+    vasprintf(&todo_msg, fmt, ap);
+    va_end(ap);
+
+    todo = 1;
+
+    UNLOCK;
+}
+
+void
+todo_end(void)
+{
+
+    LOCK;
+
+    todo = 0;
+    free(todo_msg);
+
+    UNLOCK;
+}
+
+int
+exit_status(void)
+{
+    int r;
+
+    LOCK;
+
+    /* If there's no plan, just return the number of failures */
+    if(no_plan || !have_plan) {
+        UNLOCK;
+        return failures;
+    }
+
+    /* Ran too many tests?  Return the number of tests that were run
+       that shouldn't have been */
+    if(e_tests < test_count) {
+        r = test_count - e_tests;
+        UNLOCK;
+        return r;
+    }
+
+    /* Return the number of tests that failed + the number of tests
+       that weren't run */
+    r = failures + e_tests - test_count;
+    UNLOCK;
+
+    return r;
+}
+
+/*
+ * Cleanup at the end of the run, produce any final output that might be
+ * required.
+ */
+void
+_cleanup(void)
+{
+
+    LOCK;
+
+    /* If plan_no_plan() wasn't called, and we don't have a plan,
+       and we're not skipping everything, then something happened
+       before we could produce any output */
+    if(!no_plan && !have_plan && !skip_all) {
+        diag("Looks like your test died before it could output anything.");
+        UNLOCK;
+        return;
+    }
+
+    if(test_died) {
+        diag("Looks like your test died just after %d.", test_count);
+        UNLOCK;
+        return;
+    }
+
+
+    /* No plan provided, but now we know how many tests were run, and can
+       print the header at the end */
+    if(!skip_all && (no_plan || !have_plan)) {
+        printf("1..%d\n", test_count);
+    }
+
+    if((have_plan && !no_plan) && e_tests < test_count) {
+        diag("Looks like you planned %d %s but ran %d extra.",
+             e_tests, e_tests == 1 ? "test" : "tests", test_count - e_tests);
+        UNLOCK;
+        return;
+    }
+
+    if((have_plan || !no_plan) && e_tests > test_count) {
+        diag("Looks like you planned %d %s but only ran %d.",
+             e_tests, e_tests == 1 ? "test" : "tests", test_count);
+        UNLOCK;
+        return;
+    }
+
+    if(failures)
+        diag("Looks like you failed %d %s of %d.",
+             failures, failures == 1 ? "test" : "tests", test_count);
+
+    UNLOCK;
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/tap.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/tap.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/src/tap.h	(revision 22322)
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* '## __VA_ARGS__' is a gcc'ism. C99 doesn't allow the token pasting
+   and requires the caller to add the final comma if they've ommitted
+   the optional arguments */
+#ifdef __GNUC__
+# define ok(e, test, ...) ((e) ?					\
+			   _gen_result(1, __func__, __FILE__, __LINE__,	\
+				       test, ## __VA_ARGS__) :		\
+			   _gen_result(0, __func__, __FILE__, __LINE__,	\
+				       test, ## __VA_ARGS__))
+
+# define ok1(e) ((e) ?							\
+		 _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+		 _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(test, ...) ok(1, test, ## __VA_ARGS__);
+# define fail(test, ...) ok(0, test, ## __VA_ARGS__);
+
+# define skip_start(test, n, fmt, ...)			\
+	do {						\
+		if((test)) {				\
+			skip(n, fmt, ## __VA_ARGS__);	\
+			continue;			\
+		}
+#elif __STDC_VERSION__ >= 199901L /* __GNUC__ */
+# define ok(e, ...) ((e) ?						\
+		     _gen_result(1, __func__, __FILE__, __LINE__,	\
+				 __VA_ARGS__) :				\
+		     _gen_result(0, __func__, __FILE__, __LINE__,	\
+				 __VA_ARGS__))
+
+# define ok1(e) ((e) ?							\
+		 _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+		 _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(...) ok(1, __VA_ARGS__);
+# define fail(...) ok(0, __VA_ARGS__);
+
+# define skip_start(test, n, ...)			\
+	do {						\
+		if((test)) {				\
+			skip(n,  __VA_ARGS__);		\
+			continue;			\
+		}
+#else /* __STDC_VERSION__ */
+# error "Needs gcc or C99 compiler for variadic macros."
+#endif /* __STDC_VERSION__ */
+
+#define skip_end() } while(0);
+
+unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...);
+
+int plan_no_plan(void);
+int plan_skip_all(char *);
+int plan_tests(unsigned int);
+
+unsigned int diag(char *, ...);
+
+int skip(unsigned int, char *, ...);
+
+void todo_start(char *, ...);
+void todo_end(void);
+
+int exit_status(void);
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/Makefile.am	(revision 22322)
@@ -0,0 +1,7 @@
+SUBDIRS=	diag
+SUBDIRS+=	fail
+SUBDIRS+=	ok
+SUBDIRS+=	pass
+SUBDIRS+=	plan
+SUBDIRS+=	skip
+SUBDIRS+=	todo
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/README	(revision 22322)
@@ -0,0 +1,12 @@
+Most of the tests follow the same pattern.
+
+ * test.pl that uses Test::More, and demonstrates whatever functionality 
+   that we're trying to test.  This is the reference code.
+
+ * test.c, which tests the libtap reimplementation of the same functionality.
+
+ * test.t, which compiles the .c program, runs both test scripts, and then 
+   diffs their output to make sure it's identical.
+
+   Right now, test.t is identical in every directory.  This sucks somewhat.
+   It should either be a symlink to a common script
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	plan_tests(2);
+
+	rc = diag("A diagnostic message");
+	diag("Returned: %d", rc);
+
+	/* Make sure the failure is passed through */
+	ok(1, "test 1") || diag("ok() failed, and shouldn't");
+	ok(0, "test 2") || diag("ok() passed, and shouldn't");
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/test.pl	(revision 22322)
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+plan tests => 2;
+
+$rc = diag("A diagnostic message");
+diag("Returned: $rc");
+
+ok(1, 'test 1') or diag "ok() failed, and shouldn't";
+ok(0, 'test 2') or diag "ok() passed, and shouldn't";
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/diag/test.t	(revision 22322)
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+echo '1..7'
+
+perl $srcdir/test.pl 2>/dev/null >test.pl.out
+perlstatus=$?
+
+./test > test.c.out 2>&1
+cstatus=$?
+
+if grep "^# A diagnostic message$" test.c.out > /dev/null ; then
+    echo "ok 1 - found a diagnostic message"
+else
+    echo "not ok 1 - found a diagnostic message"
+    retval=1
+fi
+
+if grep "^# Returned: 0$" test.c.out > /dev/null ; then
+    echo "ok 2 - diag() expected return value" 
+else
+    echo "not ok 2 - diag() expected return value" 
+    retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 43)$" test.c.out > /dev/null ; then
+    echo "ok 3 - 'failed test at line' diag" 
+else
+    echo "not ok 3 - 'failed test at line' diag" 
+    retval=1
+fi
+
+if grep "^# ok() passed, and shouldn't$" test.c.out > /dev/null ; then
+    echo "ok 4 - expected diag"
+else
+    echo "ok 4 - expected diag"
+    retval=1
+fi
+
+if grep "^# Looks like you failed 1 test of 2.$" test.c.out > /dev/null ; then
+    echo "ok 5 - failed 1 test"
+ else
+    echo "ok 5 - failed 1 test"
+    retval=1
+fi
+
+sed -e '/^#/D' test.c.out > tmp
+mv tmp test.c.out
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 6 - TAP output is identical'
+else
+	retval=1
+	echo 'not ok 6 - TAP output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 7 - status code'
+else
+	retval=1
+	echo 'not ok 7 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/.cvsignore	(revision 22322)
@@ -0,0 +1,15 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(2);
+	diag("Returned: %d", rc);
+
+	rc = fail("test to fail");
+	diag("Returned: %d", rc);
+
+	rc = fail("test to fail %s", "with extra string");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 2;
+diag("Returned: " . sprintf('%d', $rc));
+
+$rc = fail('test to fail');
+diag("Returned: $rc");
+
+$rc = fail('test to fail with extra string');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/fail/test.t	(revision 22322)
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+echo '1..8'
+
+perl $srcdir/test.pl 2>/dev/null > test.pl.out
+perlstatus=$?
+
+./test > test.c.out 2>&1 
+cstatus=$?
+
+if grep "^# Returned: 2$" test.c.out >/dev/null ; then
+  echo "ok 1 - expected return value";
+else
+  echo "not ok 1 - expected return value";
+  retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 39)$" test.c.out >/dev/null ; then
+  echo "ok 2 - failed expected test";
+else
+  echo "not ok 2 - failed expected test";
+  retval=1
+fi
+
+if grep "^# Returned: 0$" test.c.out >/dev/null ; then
+  echo "ok 3 - expected return value";
+else
+  echo "not ok 3 - expected return value";
+  retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 42)$" test.c.out >/dev/null ; then
+  echo "ok 4 - failed expected test";
+else
+  echo "not ok 4 - failed expected test";
+  retval=1
+fi
+  
+if grep "^# Returned: 0$" test.c.out >/dev/null ; then
+  echo "ok 5 - expected return value";
+else
+  echo "not ok 5 - expected return value";
+  retval=1
+fi
+
+if grep "^# Looks like you failed 2 tests of 2.$" test.c.out >/dev/null ; then
+  echo "ok 6 - expected return value";
+else
+  echo "not ok 6 - expected return value";
+  retval=1
+fi
+
+sed -e '/^#/D' test.c.out > tmp
+mv tmp test.c.out
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 7 - output is identical'
+else
+	retval=1
+	echo 'not ok 7 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 8 - status code'
+else
+	retval=1
+	echo 'not ok 8 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS  =	ok
+SUBDIRS +=	ok-hash
+SUBDIRS +=	ok-numeric
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/test.c	(revision 22322)
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(4);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Test with no hash");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Test with one # hash");
+	diag("Returned: %d", rc);
+
+        rc = ok(1, "Test with # two # hashes");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Test with ## back to back hashes");
+	diag("Returned: %d", rc);
+	
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/test.pl	(revision 22322)
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 4;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1, 'Test with no hash');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with one # hash');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with # two # hashes');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with ## back to back hashes');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-hash/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(3);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "First test");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "1");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Third test");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/test.pl	(revision 22322)
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 3;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1, 'First test');
+diag("Returned: $rc");
+
+$rc = ok(1, '1');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Third test');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok-numeric/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/test.c	(revision 22322)
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(5);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals 1");
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals %d", 1);
+	diag("Returned: %d", rc);
+
+	rc = ok1(1 == 1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 2, "1 equals 2");
+	diag("Returned: %d", rc);
+
+	rc = ok1(1 == 2);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/test.pl	(revision 22322)
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 1, '1 equals 1'); # Used for %d testing in test.c
+diag("Returned: $rc");
+
+$rc = ok(1 == 1, '1 == 1');	# Test ok1() passes when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 2, '1 equals 2');	# Test ok() fails when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 2, '1 == 2');	# Test ok1() fails when it should
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/ok/ok/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(2);
+	diag("Returned: %d", rc);
+
+	rc = pass("test to pass");
+	diag("Returned: %d", rc);
+
+	rc = pass("test to pass %s", "with extra string");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 2;
+diag("Returned: " . sprintf('%d', $rc));
+
+$rc = pass('test to pass');
+diag("Returned: $rc");
+
+$rc = pass('test to pass with extra string');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/pass/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/Makefile.am	(revision 22322)
@@ -0,0 +1,7 @@
+SUBDIRS  =	no-tests
+SUBDIRS +=	no_plan
+SUBDIRS +=	not-enough-tests
+SUBDIRS +=	too-many-plans
+SUBDIRS +=	too-many-tests
+SUBDIRS +=	sane
+SUBDIRS +=	skip_all
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(0);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 0;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no-tests/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_no_plan();
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+my $rc = 0;
+
+use Test::More;
+
+$rc = plan qw(no_plan);
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/no_plan/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/test.pl	(revision 22322)
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/not-enough-tests/test.t	(revision 22322)
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	# comment this out until we're exit-code compatible with Test::More
+	#retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/sane/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/plan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/plan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/plan.c	(revision 22322)
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap.h"
+
+/* Run pre-defined tests on the test library to make sure that the basic
+   functionality works, and it can be used to test itself afterwards */
+
+int
+main(int argc, char *argv[])
+{
+	plan_skip_all("No good reason");
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/test.c	(revision 22322)
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_skip_all("No good reason");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/test.pl	(revision 22322)
@@ -0,0 +1,11 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan skip_all => "No good reason";
+diag("Returned: " . sprintf("%d", $rc));
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/skip_all/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(0, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/test.pl	(revision 22322)
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = plan tests => 1;
+diag("Returned: $rc");
+
+$rc = ok(0);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-plans/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(5);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = ok(0, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(0);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/plan/too-many-tests/test.t	(revision 22322)
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+    # we're not exit-status compatible with Test::More yet
+	#retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/test.c	(revision 22322)
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+	unsigned int side_effect = 0;
+
+	rc = plan_tests(4);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals 1");	/* Should always work */
+	diag("Returned: %d", rc);
+
+	do {
+		if(1) {
+			rc = skip(1, "Testing skipping");
+			continue;
+		}
+		
+		side_effect++;
+
+		ok(side_effect == 1, "side_effect checked out");
+
+	} while(0);
+
+	diag("Returned: %d", rc);
+
+	skip_start(1 == 1, 1, "Testing skipping #2");
+
+	side_effect++;
+	rc = ok(side_effect == 1, "side_effect checked out");
+	diag("Returned: %d", rc);
+
+	skip_end();
+
+	rc = ok(side_effect == 0, "side_effect is %d", side_effect);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/test.pl	(revision 22322)
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 4;
+diag("Returned: " . sprintf("%d", $rc));
+
+my $side_effect = 0;		# Check whether skipping has side effects
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+# Start skipping
+SKIP: {
+	$rc = skip "Testing skipping", 1;
+
+	$side_effect++;
+
+	$rc = ok($side_effect == 1, '$side_effect checked out');
+}
+
+diag("Returned: $rc");
+
+SKIP: {
+	$rc = skip "Testing skipping #2", 1;
+	diag("Returned: $rc");
+
+	$side_effect++;
+
+	$rc = ok($side_effect == 1, '$side_effect checked out');
+	diag("Returned: $rc");
+}
+
+$rc = ok($side_effect == 0, "side_effect is $side_effect");
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/skip/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/test.c	(revision 22322)
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+	unsigned int side_effect = 0;
+
+	rc = plan_tests(5);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals 1");	/* Should always work */
+	diag("Returned: %d", rc);
+
+	todo_start("For testing purposes");
+
+	side_effect++;
+
+	/* This test should fail */
+	rc = ok(side_effect == 0, "side_effect checked out");
+	diag("Returned: %d", rc);
+
+	/* This test should unexpectedly succeed */
+	rc = ok(side_effect == 1, "side_effect checked out");
+	diag("Returned: %d", rc);
+
+	todo_end();
+
+	todo_start("Testing printf() %s in todo_start()", "expansion");
+
+	rc = ok(0, "dummy test");
+	diag("Returned: %d", rc);
+
+	todo_end();
+
+	rc = ok(side_effect == 1, "side_effect is %d", side_effect);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/test.pl	(revision 22322)
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+my $side_effect = 0;		# Check whether TODO has side effects
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+# Start TODO tests
+TODO: {
+	local $TODO = 'For testing purposes';
+
+	$side_effect++;
+
+	# This test should fail
+	$rc = ok($side_effect == 0, 'side_effect checked out');
+	diag("Returned: $rc");
+
+	# This test should unexpectedly succeed
+	$rc = ok($side_effect == 1, 'side_effect checked out');
+	diag("Returned: $rc");
+}
+
+TODO: {
+	local $TODO = 'Testing printf() expansion in todo_start()';
+
+	$rc = ok(0, 'dummy test');
+	diag("Returned: $rc");
+}
+
+$rc = ok($side_effect == 1, "side_effect is $side_effect");
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tap/tests/todo/test.t	(revision 22322)
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+# Test:;More prints diagnostic from TODO tests on stdout
+# http://rt.cpan.org/Ticket/Display.html?id=14982
+sed '/^#/D' test.pl.out > tmp
+mv tmp test.pl.out
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tests.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tests.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/nebclient/tests/tests.c	(revision 22322)
@@ -0,0 +1,131 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include "nebclient.h"
+#include "tap.h"
+
+#define neb_ok(server, ...) \
+    if (!ok(__VA_ARGS__)) { \
+        diag("nebErr: %s", nebErr(server)); \
+    }
+
+#define tst_nebFree(foo) \
+    if (foo) nebFree(foo)
+
+int main (int argc, char **argv) {
+    nebServer       *server = NULL;
+    char            *key = "foobarbaz";
+
+    plan_tests(22);
+
+    if (getenv("NEB_SERVER")) {
+        server = nebServerAlloc(getenv("NEB_SERVER"));
+    } else {
+        server = nebServerAlloc(NULL);
+    }
+    ok(server, "server not NULL");
+
+    printf("# server set to %s\n", server->endpoint);
+
+    {
+        char        *URI = NULL;
+        char        *filename;
+       
+        filename = nebCreate(server, key, NULL, &URI);
+        diag("filename is %s", filename);
+        neb_ok(server, filename, "create object");
+        ok(URI, "URI not NULL");
+        neb_ok(server, nebDelete(server, key), "delete object");
+
+        tst_nebFree(URI);
+        tst_nebFree(filename);
+    }
+
+    {
+        char        *URI = NULL;
+        int         fh;
+       
+        fh = nebOpenCreate(server, key, NULL, &URI);
+
+        neb_ok(server, fh > -1, "create new object filehandle");
+        ok(URI, "URI not NULL");
+
+        tst_nebFree(URI);
+        close(fh);
+    }
+
+    {
+        int         fh;
+
+        fh = nebOpen(server, key, NEB_READ);
+        diag("fh is %d", fh);
+
+        neb_ok(server, fh > 0, "open object for reading");
+
+        close(fh);
+    }
+
+    {
+        int         fh;
+
+        fh = nebOpen(server, key, NEB_WRITE);
+        diag("fh is %d", fh);
+
+        neb_ok(server, fh > 0, "open object for writing");
+
+        close(fh);
+    }
+
+
+    neb_ok(server, nebReplicate(server, key, NULL, NULL), "replicate object");
+
+    neb_ok(server, nebOpen(server, key, NEB_WRITE) < 0, "write to object with multiple instances");
+
+    neb_ok(server, nebCull(server, key), "cull object");
+
+    neb_ok(server, nebStat(server, key), "stat object");
+
+    neb_ok(server, nebLock(server, key, NEB_WRITE), "lock object write");
+    neb_ok(server, nebUnlock(server, key, NEB_WRITE), "unlock object write");
+
+    neb_ok(server, nebLock(server, key, NEB_READ), "lock object read");
+    neb_ok(server, nebUnlock(server, key, NEB_READ), "unlock object read");
+
+    {
+        nebObjectInstances *locations = NULL;
+
+        locations = nebFindInstances(server, key, NULL);
+
+        neb_ok(server, locations, "locations not NULL");
+        ok(locations && locations->n == 1, "find instances");
+
+        nebObjectInstancesFree(locations);
+    }
+
+    {
+        char        *filename = NULL;
+       
+        filename = nebFind(server, key);
+
+        neb_ok(server, filename, "find file name");
+
+        tst_nebFree(filename);
+    }
+
+
+    neb_ok(server, nebCopy(server, key, "copyiedfile"), "copy object");
+
+    neb_ok(server, nebDelete(server, key), "delete object");
+
+    neb_ok(server, nebMove(server, "copyiedfile", "movedfile"), "move object");
+
+    if (!nebDelete(server, "movedfile")) {
+        diag( "cleanup failed %s\n", nebErr(server));
+    }
+
+    nebServerFree(server);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/scripts/bench_test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/scripts/bench_test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/scripts/bench_test.pl	(revision 22322)
@@ -0,0 +1,41 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib "./lib";
+
+use Benchmark qw( timethese );
+use Nebulous::Client;
+
+my $neb = Nebulous::Client->new(
+    proxy   => 'http://localhost:80/nebulous'
+);
+
+my $key = shift || 'foobar';
+
+eval { $neb->delete( $key ); };
+
+my $fh = $neb->create( $key );
+close $fh;
+
+
+timethese( -3,
+    {
+        'stat' => sub {
+            $neb->stat( $key );
+        },
+    }
+);
+
+$neb->delete( $key );
+
+timethese( -3,
+    {
+        'create/delete' => sub {
+            my $fh = $neb->create( $key );
+            close $fh;
+            $neb->delete( $key );
+        },
+    }
+);
Index: /tags/sj_tags/sj_root_20080929/Nebulous/scripts/nebulous.cgi
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/scripts/nebulous.cgi	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/scripts/nebulous.cgi	(revision 22322)
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2004  Joshua Hoblitt
+#
+# $Id
+ 
+use strict;
+use warnings FATAL => qw( all );
+
+use SOAP::Transport::HTTP;
+use Nebulous::Server;
+   
+SOAP::Transport::HTTP::CGI
+    -> dispatch_to( 'Nebulous::Server' )
+    -> handle;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/scripts/ptest.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/scripts/ptest.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/scripts/ptest.pl	(revision 22322)
@@ -0,0 +1,49 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib "./lib";
+
+use Nebulous::Client;
+
+my $neb = Nebulous::Client->new(
+    proxy   => 'http://localhost:80/nebulous'
+);
+
+my $key = shift || 'foobar';
+my $kids = shift || 1;
+
+foreach my $id ( 1..$kids ) {
+    my $pid = fork;
+
+    unless ( $pid )  {
+        my $fname = "${key}_$id";
+        my $fh = $neb->open_create( $fname );
+        die "can't create file $fname" unless $fh;
+
+        print $fh "fooby\n";
+
+        close $fh;
+
+        $fh = $neb->open( $fname, 'read' ) or die "can't open file";
+        close $fh;
+
+        $neb->lock( $fname, 'read' );
+        $neb->unlock( $fname, 'read' );
+        $neb->replicate( $fname );
+        $neb->cull( $fname );
+        $neb->find( $fname );
+        $neb->copy( $fname, $fname . "_copy" );
+        $neb->move( $fname, $fname . "_move" );
+        $neb->delete( $fname . "_copy" );
+        $neb->delete( $fname . "_move" );
+
+        exit 0;
+    }
+}
+
+while ( $kids ) {
+    wait();
+    $kids --;
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+TEST
+htdocs
+logs
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/00_distribution.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/00_distribution.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/00_distribution.t	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 00_distribution.t,v 1.2 2007-05-02 01:07:56 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib ./t );
+
+use Test::More;
+
+# example taken from Test::Distribution Pod
+
+BEGIN {
+    eval {
+        require Test::Distribution;
+    };
+    if($@) {
+        plan skip_all => 'Test::Distribution not installed';
+    } else {
+        import Test::Distribution not => [qw( versions use podcover )];
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/01_load.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/01_load.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/01_load.t	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 01_load.t,v 1.8 2008-03-20 23:21:58 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./t ./lib );
+
+use Test::More tests => 4;
+
+BEGIN { use_ok( 'Nebulous::Client' ); }
+BEGIN { use_ok( 'Nebulous::Client::Log' ); }
+BEGIN { use_ok( 'Nebulous::Client::HTTP' ); }
+BEGIN { use_ok( 'Nebulous::Util' ); }
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/50_client_new.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/50_client_new.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/50_client_new.t	(revision 22322)
@@ -0,0 +1,60 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 50_client_new.t,v 1.1 2005-12-03 02:52:31 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 4;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    isa_ok( $neb, "Nebulous::Client" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+        uri   => "http://example.com/PS/IPP/Nebulous/Client",
+    );
+
+    isa_ok( $neb, "Nebulous::Client" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    Nebulous::Client->new;
+};
+like( $@, qr/Mandatory parameter/, "no proxy" );
+
+Test::Nebulous->setup;
+
+eval {
+    Nebulous::Client->new(
+        proxy => "foo",
+        dog => "do"
+    );
+};
+like( $@, qr/not listed in the validation options/, "bad param" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/51_client_create.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/51_client_create.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/51_client_create.t	(revision 22322)
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 51_client_create.t,v 1.6 2008-05-15 03:24:58 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 9;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Test::Nebulous;
+use Test::URI;
+use URI::Split qw( uri_split );
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $uri = $neb->create("foo");
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "good filename");
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $uri = $neb->create("foo", "node01");
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "good filename");
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume == undef
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $uri = $neb->create("foo", undef);
+
+    my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+    ok(-e $path, "good filename");
+    uri_scheme_ok($uri, 'file');
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create("foo");
+    is($neb->create("foo"), undef, "object already exists");
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create();
+};
+like($@, qr/1 - 2 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create(1, 2, 3);
+};
+like($@, qr/1 - 2 were expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/51_client_open_create.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/51_client_open_create.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/51_client_open_create.t	(revision 22322)
@@ -0,0 +1,80 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 51_client_open_create.t,v 1.4 2008-05-15 02:40:23 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 5;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $fh = $neb->open_create("foo");
+
+    is(ref $fh, 'GLOB', "good filehandle");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $fh = $neb->open_create("foo", "node01");
+
+    is(ref $fh, 'GLOB', "good filehandle");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->open_create("foo");
+    is($neb->open_create("foo"), undef, "object already exists");
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->open_create();
+};
+like($@, qr/1 - 2 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->open_create(1, 2, 3);
+};
+like($@, qr/1 - 2 were expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/52_client_replicate.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/52_client_replicate.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/52_client_replicate.t	(revision 22322)
@@ -0,0 +1,116 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 52_client_replicate.t,v 1.2 2008-09-24 00:36:56 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 13;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $uri = $neb->replicate( "foo" );
+
+    ok( $uri, "good replication" );
+    ok( -e _get_file_path( $uri ), "file exists" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, $volume
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $uri = $neb->replicate( "foo", "node01" );
+
+    ok( $uri, "good replication" );
+    ok( -e _get_file_path( $uri ), "file exists" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, $volume = undef (same as saying no volume)
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "neb://node01/foo" );
+
+    my $uri = $neb->replicate( "neb://node01/foo", undef );
+
+    ok( $uri, "good replication" );
+    ok( -e _get_file_path( $uri ), "file exists" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, $volume
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $uri1 = $neb->replicate( "foo", "node01" );
+    my $uri2 = $neb->replicate( "foo", "node02" );
+
+    ok( $uri1, "good replication" );
+    ok( $uri2, "good replication" );
+    ok( -e _get_file_path( $uri1 ), "file exists" );
+    ok( -e _get_file_path( $uri2 ), "file exists" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    my $uri = $neb->replicate( "foo" );
+
+    is( $uri, undef, "storage object does not exist" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->replicate();
+};
+like( $@, qr/1 - 2 were expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->replicate( 1, 2, 3 );
+};
+like( $@, qr/1 - 2 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/53_client_cull.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/53_client_cull.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/53_client_cull.t	(revision 22322)
@@ -0,0 +1,128 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 53_client_cull.t,v 1.5 2007-05-03 03:21:28 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 15;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create("foo");
+    $neb->replicate("foo");
+
+    my $uri = $neb->cull("foo");
+
+    ok($uri, "good cull");
+    ok(! -e _get_file_path($uri), "file doesn't exist");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, $volume
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create("foo");
+    $neb->replicate("foo", "node01");
+
+    my $uri = $neb->cull("foo", "node01");
+
+    ok($uri, "good cull");
+    ok(! -e _get_file_path($uri), "file exists");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create("foo");
+
+    $neb->replicate("foo", "node01");
+    $neb->replicate("foo", "node02");
+
+    my $uri1 = $neb->cull("foo");
+    my $uri2 = $neb->cull("foo");
+
+    ok($uri1, "good cull");
+    ok($uri2, "good cull");
+    ok(! -e _get_file_path($uri1), "file exists");
+    ok(! -e _get_file_path($uri2), "file exists");
+}
+
+Test::Nebulous->setup;
+
+{
+
+    # key, $volume
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    $neb->replicate( "foo", "node01" );
+    $neb->replicate( "foo", "node02" );
+
+    my $uri1 = $neb->cull( "foo", "node01" );
+    my $uri2 = $neb->cull( "foo", "node02" );
+
+    ok( $uri1, "good cull" );
+    ok( $uri2, "good cull" );
+    ok( ! -e _get_file_path( $uri1 ), "file exists" );
+    ok( ! -e _get_file_path( $uri2 ), "file exists" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    my $uri = $neb->cull( "foo" );
+
+    is( $uri, undef, "storage object does not exist" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->cull();
+};
+like( $@, qr/1 - 2 were expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->cull( 1, 2, 3 );
+};
+like( $@, qr/1 - 2 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/54_client_lock.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/54_client_lock.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/54_client_lock.t	(revision 22322)
@@ -0,0 +1,273 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 54_client_lock.t,v 1.1 2005-12-03 02:52:31 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 19;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    # key, type
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    ok( $neb->lock( "foo", "read" ), "read lock" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, type, timeout
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    ok( $neb->lock( "foo", "read", 10 ), "read lock" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    ok( $neb->lock( "foo", "read" ), "read lock" );
+    ok( $neb->lock( "foo", "read" ), "read lock" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, type
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    ok( $neb->lock( "foo", "write" ), "write lock" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, type, timeout
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    ok( $neb->lock( "foo", "write", 10 ), "write lock" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    is( $neb->lock( "foo", "read" ), undef, "storage object does not exist" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    is( $neb->lock( "foo", "write" ), undef, "storage object does not exist" );
+}
+
+Test::Nebulous->setup;
+
+diag "testing a spinlock, this will be a litle slow...";
+
+eval {
+    local $SIG{ALRM} = sub { alarm 0; die "timeout"; };
+    alarm 8;
+
+    # key, type, timeout
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    $neb->lock( "foo", "read" );
+    $neb->lock( "foo", "write" );
+
+    alarm 0;
+};
+like( $@, qr/timeout/, "default spinlock" );
+
+Test::Nebulous->setup;
+
+diag "testing a spinlock, this will be a litle slow...";
+
+eval {
+    local $SIG{ALRM} = sub { alarm 0; die "timeout"; };
+    alarm 12;
+
+    # key, type, timeout
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    $neb->lock( "foo", "read" );
+    unless ( $neb->lock( "foo", "write" ) ) {
+        alarm 0;
+        die "gaveup";
+    }
+
+    alarm 0;
+};
+like( $@, qr/gaveup/, "default spinlock" );
+
+Test::Nebulous->setup;
+
+diag "testing a spinlock, this will be a litle slow...";
+
+eval {
+    local $SIG{ALRM} = sub { alarm 0; die "timeout"; };
+    alarm 3;
+
+    # key, type, timeout
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    $neb->lock( "foo", "read" );
+    $neb->lock( "foo", "write", 5 );
+
+    alarm 0;
+};
+like( $@, qr/timeout/, "specified spinlock" );
+
+Test::Nebulous->setup;
+
+diag "testing a spinlock, this will be a litle slow...";
+
+eval {
+    local $SIG{ALRM} = sub { alarm 0; die "timeout"; };
+    alarm 7;
+
+    # key, type, timeout
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    $neb->lock( "foo", "read" );
+    unless ( $neb->lock( "foo", "write", 5 ) ) {
+        alarm 0;
+        die "gaveup";
+    }
+
+    alarm 0;
+};
+
+like( $@, qr/gaveup/, "specified spinlock" );
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    $neb->lock( "foo", "write" );
+    is( $neb->lock( "foo", "write", 1 ), undef, "can not write lock twice" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    $neb->lock( "foo", "read" );
+    is( $neb->lock( "foo", "write", 1 ), undef, "can not write lock after read lock" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    $neb->lock( "foo", "write" );
+    is( $neb->lock( "foo", "read", 1 ), undef, "can not read lock after write lock" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->lock();
+};
+like( $@, qr/2 - 3 were expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->lock( "foo" );
+};
+like( $@, qr/2 - 3 were expected/, "not enough params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->lock( "foo", "both" );
+};
+like( $@, qr/is read or write/, "not read or write" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->lock( "foo", 'read', 3, "bar" );
+};
+like( $@, qr/2 - 3 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/55_client_unlock.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/55_client_unlock.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/55_client_unlock.t	(revision 22322)
@@ -0,0 +1,170 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 55_client_unlock.t,v 1.1 2005-12-03 02:52:31 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 14;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    $neb->lock( "foo", "read" );
+
+    ok( $neb->unlock( "foo", "read" ), "read unlock" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    $neb->lock( "foo", "read" );
+    $neb->lock( "foo", "read" );
+
+    ok( $neb->unlock( "foo", "read" ), "read unlock" );
+    ok( $neb->unlock( "foo", "read" ), "read unlock" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    $neb->lock( "foo", "write" );
+
+    ok( $neb->unlock( "foo", "write" ), "write unlock" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    is( $neb->unlock( "foo", "read" ), undef, "storage object does not exist" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    is( $neb->unlock( "foo", "write" ), undef, "storage object does not exist" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    is( $neb->unlock( "foo", "read" ), undef, "no lock set" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    is( $neb->unlock( "foo", "write" ), undef, "no lock set" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    $neb->lock( "foo", "write" );
+
+    is( $neb->unlock( "foo", "read" ), undef, "read unlock under write lock" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    $neb->lock( "foo", "read" );
+
+    is( $neb->unlock( "foo", "write" ), undef, "write unlock under read lock" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->unlock();
+};
+like( $@, qr/2 were expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->unlock( "foo" );
+};
+like( $@, qr/2 were expected/, "not enough params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->unlock( "foo", "both" );
+};
+like( $@, qr/is read or write/, "not read or write" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->unlock( "foo", 'read', 3 );
+};
+like( $@, qr/2 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/56_client_find_instances.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/56_client_find_instances.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/56_client_find_instances.t	(revision 22322)
@@ -0,0 +1,186 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 56_client_find_instances.t,v 1.5 2008-09-10 23:50:26 bills Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 29;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $locations = $neb->find_instances( "foo" );
+
+    is( scalar @$locations, 1, "found 1" );
+    like( @$locations[0], qr/file:/, "URIs match" );
+    ok( -e _get_file_path( @$locations[0] ), "URI matches file" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    $neb->replicate( "foo" );
+
+    my $locations = $neb->find_instances( "foo" );
+
+    is( scalar @$locations, 2, "found 2" );
+    like( @$locations[0], qr/file:/, "URIs match" );
+    like( @$locations[1], qr/file:/, "URIs match" );
+    ok( -e _get_file_path( @$locations[0] ), "URI matches file" );
+    ok( -e _get_file_path( @$locations[1] ), "URI matches file" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $locations = $neb->find_instances( "foo", "node01" );
+
+    is( scalar @$locations, 1, "found 1" );
+    like( @$locations[0], qr/file:/, "URIs match" );
+    ok( -e _get_file_path( @$locations[0] ), "URI matches file" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume == undef
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $locations = $neb->find_instances( "foo", undef );
+
+    is( scalar @$locations, 1, "found 1" );
+    like( @$locations[0], qr/file:/, "URIs match" );
+    ok( -e _get_file_path( @$locations[0] ), "URI matches file" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # volume/key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $locations = $neb->find_instances( "neb://node01/foo");
+
+    is( scalar @$locations, 1, "found 1" );
+    like( @$locations[0], qr/file:/, "URIs match" );
+    ok( -e _get_file_path( @$locations[0] ), "URI matches file" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # volume/key, volume override
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo", "~node01" );
+
+    my $locations = $neb->find_instances( "neb://node01/foo", "~node02" );
+
+    is($locations, undef, "no instances on specified volume" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # volume/key, volume override
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo", "node01" );
+
+    my $locations = $neb->find_instances( "neb://node02/foo", "any" );
+
+    is( scalar @$locations, 1, "found 1" );
+    like( @$locations[0], qr/file:/, "URIs match" );
+    ok( -e _get_file_path( @$locations[0] ), "URI matches file" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo", "node01" );
+    $neb->replicate( "foo", "node01" );
+
+    my $locations = $neb->find_instances( "foo", "node01" );
+
+    is( scalar @$locations, 2, "found 2" );
+    like( @$locations[0], qr/file:/, "URIs match" );
+    like( @$locations[1], qr/file:/, "URIs match" );
+    ok( -e _get_file_path( @$locations[0] ), "URI matches file" );
+    ok( -e _get_file_path( @$locations[1] ), "URI matches file" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    is( $neb->find_instances( "foo" ), undef, "storage object does not exist" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->find_instances();
+};
+like( $@, qr/1 - 2 were expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->find_instances( "foo", 'read', 3 );
+};
+like( $@, qr/1 - 2 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/57_client_find.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/57_client_find.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/57_client_find.t	(revision 22322)
@@ -0,0 +1,113 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 57_client_find.t,v 1.3 2008-07-10 02:38:24 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 7;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    my $uri = $neb->create( "foo" );
+
+    my $path = $neb->find( "foo" );
+
+    ok( -e $path, "file exists" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    my $uri = $neb->create( "foo", "node01" );
+
+    my $path = $neb->find( "foo", "node01" );
+
+    ok( -e $path, "file exists" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume does not hold key but this works anyways ans find will fall
+    # back to looking for "any"
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    my $uri = $neb->create( "foo", "node01" );
+
+    my $path = $neb->find( "foo", "node02" );
+
+    ok( -e $path, "file exists" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key, volume any
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    my $uri = $neb->create( "foo", "node01" );
+
+    my $path = $neb->find( "foo", "any" );
+
+    ok( -e $path, "file exists" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $path = $neb->find( "foo" );
+
+    is($path, undef, "file doesn't exist" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->find();
+};
+like( $@, qr/1 - 2 were expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->find( "foo", "bar", "bong" );
+};
+like( $@, qr/1 - 2 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/58_client_open.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/58_client_open.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/58_client_open.t	(revision 22322)
@@ -0,0 +1,129 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 58_client_open.t,v 1.2 2008-04-18 00:08:23 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 10;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+use File::stat;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    my $fh = $neb->open( "foo", 'read' );
+
+    is( ref $fh, 'GLOB', "good filehandle" );
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    my $fh = $neb->open( "foo", 'write' );
+
+    is( ref $fh, 'GLOB', "good filehandle" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    is( ref $neb->open( "foo", 'write' ), 'GLOB', "create new object" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    is( $neb->open( "foo", 'read' ), undef, "can't create new object" );
+}
+
+Test::Nebulous->setup;
+
+# truncation test
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    my $fh = $neb->open( "foo", 'write' );
+    print $fh "foobar";
+    close $fh;
+
+    $fh = $neb->open( "foo", '>' );
+    ok($fh, "open in trucate mode" );
+    my $sb = stat($fh);
+    is($sb->size, 0, "file was truncated");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    $neb->replicate( "foo" );
+
+    is( $neb->open( "foo", 'write' ), undef,
+        "write to object with multiple instances" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->open( "foo", 'bar' );
+};
+like( $@, qr/is read or write/, "2nd params not read or write" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->open();
+};
+like( $@, qr/2 were expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->open( 1, 'read', 3 );
+};
+like( $@, qr/2 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/59_client_delete.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/59_client_delete.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/59_client_delete.t	(revision 22322)
@@ -0,0 +1,98 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 59_client_delete.t,v 1.2 2008-08-01 23:55:26 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 9;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    ok( $neb->delete( "foo" ), "delete object" );
+
+    my $locations = $neb->find_instances( "foo" );
+
+    is( $locations, undef, "no instances" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    $neb->replicate( "foo" );
+
+    ok( $neb->delete( "foo" ), "delete object" );
+
+    my $locations = $neb->find_instances( "foo" );
+
+    is( $locations, undef, "no instances" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "neb://node01/foo" );
+
+    ok( $neb->delete( "neb://node02/foo" ), "delete object" );
+
+    my $locations = $neb->find_instances( "foo" );
+
+    is( $locations, undef, "no instances" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    is( $neb->delete( "foo" ), undef, "delete non-existant object" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->delete();
+};
+like( $@, qr/1 was expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->delete( "foo", 2 );
+};
+like( $@, qr/1 was expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/60_client_copy.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/60_client_copy.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/60_client_copy.t	(revision 22322)
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 60_client_copy.t,v 1.2 2007-04-27 23:50:57 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 11;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    ok( $neb->copy( "foo", "foobar" ), "copied object" );
+
+    my $locations1 = $neb->find_instances( "foobar" );
+    my $locations2 = $neb->find_instances( "foobar" );
+
+    is( scalar @$locations1, 1, "old object has an instances" );
+    is( scalar @$locations2, 1, "new object has an instances" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    ok( $neb->copy( "foo", "foobar", "node01" ), "copied object" );
+
+    my $locations1 = $neb->find_instances( "foobar" );
+    my $locations2 = $neb->find_instances( "foobar" );
+
+    is( scalar @$locations1, 1, "old object has an instances" );
+    is( scalar @$locations2, 1, "new object has an instances" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    ok( ! $neb->copy( "foo", "foobar" ), "copy non-existant object" );
+
+    my $locations = $neb->find_instances( "foobar" );
+
+    is( $locations, undef, "new object has no instances" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->copy();
+};
+like( $@, qr/2 - 3 were expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->copy( "foo" );
+};
+like( $@, qr/2 - 3 were expected/, "not enough params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->copy( "foo", "bar", "baz", "bong" );
+};
+like( $@, qr/2 - 3 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/61_client_move.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/61_client_move.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/61_client_move.t	(revision 22322)
@@ -0,0 +1,83 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 61_client_move.t,v 1.1 2005-12-03 02:52:31 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 8;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    ok( $neb->move( "foo", "foobar" ), "move object" );
+
+    my $locations1 = $neb->find_instances( "foo" );
+    my $locations2 = $neb->find_instances( "foobar" );
+
+    is( $locations1, undef, "old object has no instances" );
+    is( scalar @$locations2, 1, "new object has an instances" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    ok( ! $neb->move( "foo", "foobar" ), "move non-existant object" );
+
+    my $locations = $neb->find_instances( "foobar" );
+
+    is( $locations, undef, "new object has no instances" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->move();
+};
+like( $@, qr/2 were expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->move( "foo" );
+};
+like( $@, qr/2 were expected/, "not enough params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->move( "foo", "bar", "baz" );
+};
+like( $@, qr/2 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/62_client_delete_instance.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/62_client_delete_instance.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/62_client_delete_instance.t	(revision 22322)
@@ -0,0 +1,92 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 62_client_delete_instance.t,v 1.1 2005-12-03 02:52:31 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 9;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $locations = $neb->find_instances( "foo" );
+
+    my $uri = $neb->delete_instance( @$locations[0] );
+
+    is( $uri, @$locations[0], "delete instance" );
+
+    ok( ! -e _get_file_path( @$locations[0] ), "deleted file" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    $neb->replicate( "foo" );
+
+    my $uri1 = $neb->find_instances( "foo" )->[0];
+
+    ok( $neb->delete_instance( $uri1 ), "delete instance" );
+
+    my $uri2 = $neb->find_instances( "foo" )->[0];
+
+    isnt( $uri1, $uri2, "other instance remains" );
+
+    ok( $neb->delete_instance( $uri2 ), "delete instance" );
+
+    my $locations = $neb->find_instances( "foo" );
+
+    is( $locations, undef, "no remaning instances" );
+}
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    my $uri = $neb->delete_instance( "file:/foo" );
+
+    is( $uri, undef, "uri does not exist" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->delete_instance();
+};
+like( $@, qr/1 was expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->delete_instance( "foo", 2 );
+};
+like( $@, qr/1 was expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/63_client_stat.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/63_client_stat.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/63_client_stat.t	(revision 22322)
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 63_client_stat.t,v 1.3 2007-05-03 22:10:14 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 12;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $info = $neb->stat( "foo" );
+
+    is( scalar @$info, 7, "number of columns" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo", "node01" );
+
+    my $info = $neb->stat( "foo" );
+
+    is( scalar @$info, 7,                       "number of columns" );
+    is( @$info[0], 1,                           "so_id" );
+    is( @$info[1], "foo",                       "ext_id" );
+    is( @$info[2], 0,                           "read lock" );
+    is( @$info[3], undef,                       "write lock" );
+    like( @$info[4], qr/....-..-.. ..:..:../,   "epoch" );
+    like( @$info[5], qr/....-..-.. ..:..:../,   "mtime" );
+    is( @$info[6], 1,                           "instances" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    my $info = $neb->stat( "foo" );
+
+    is( $info, undef, "object does not exist" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->stat();
+};
+like( $@, qr/1 was expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->stat( "foo", 2 );
+};
+like( $@, qr/1 was expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/64_client_find_objects.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/64_client_find_objects.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/64_client_find_objects.t	(revision 22322)
@@ -0,0 +1,92 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 64_client_find_objects.t,v 1.3 2008-05-15 03:26:11 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 7;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $keys = $neb->find_objects();
+
+    # currently returning everything is turned off
+    is($keys, undef, 'no keys found');
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+
+    my $keys = $neb->find_objects( "foo" );
+
+    is(scalar @$keys, 1, 'number of keys found');
+    is($keys->[0], "foo", "key name");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    $neb->create( "foo" );
+    $neb->replicate( "foo" );
+
+    my $keys = $neb->find_objects( "foo" );
+
+    is(scalar @$keys, 1, 'number of keys found');
+    is($keys->[0], "foo", "key name");
+}
+
+Test::Nebulous->setup;
+
+{
+    # key does not exist
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $keys = $neb->find_objects( "bar" );
+
+    is($keys, undef, 'no keys found');
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->find_objects( "foo", 3 );
+};
+like( $@, qr/1 was expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/65_client_mounts.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/65_client_mounts.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/65_client_mounts.t	(revision 22322)
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 65_client_mounts.t,v 1.3 2008-09-11 22:59:15 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 18;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    # key
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+    my $mounts = $neb->mounts();
+
+    is(scalar @$mounts, 7, "number of rows");
+
+    my %row;
+    # first row
+    @row{qw(mountpoint total used vol_id name host path allocate available xattr)}
+        = @{$mounts->[0]};
+
+    is($row{total},     100000000000);
+    is($row{used},      100000000);
+    is($row{vol_id},    1);
+    is($row{name},      "node01");
+    is($row{host},      "node01");
+    is($row{allocate},  1);
+    is($row{available}, 1);
+    is($row{xattr},     0);
+
+    # 2nd row
+    @row{qw(mountpoint total used vol_id name host path allocate available xattr)}
+        = @{$mounts->[1]};
+
+    is($row{total},     100000000000);
+    is($row{used},      1000000000);
+    is($row{vol_id},    2);
+    is($row{name},      "node02");
+    is($row{host},      "node02");
+    is($row{allocate},  1);
+    is($row{available}, 1);
+    is($row{xattr},     0);
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->mounts("foo");
+};
+like( $@, qr/0 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/66_client_xattr.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/66_client_xattr.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/66_client_xattr.t	(revision 22322)
@@ -0,0 +1,233 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2007-2008  Joshua Hoblitt
+#
+# $Id: 66_client_xattr.t,v 1.1 2008-05-20 00:54:30 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+plan tests => 32;
+
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+# 1 key / xattr
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $uri = $neb->create('foo');
+
+    ok($neb->setxattr('foo', 'bar', 'baz', 'create'), 'set object xattr');
+    {
+        my $xattrs = $neb->listxattr('foo');
+        is(scalar @$xattrs, 1, 'number of xattrs');
+        is(@$xattrs[0], 'bar', 'xattr name');
+    }
+
+    my $value = $neb->getxattr('foo', 'bar');
+    is($value, 'baz', 'xattr value');
+
+    ok($neb->removexattr('foo', 'bar'), "remove object xattr");
+    {
+        my $xattrs = $neb->listxattr('foo');
+        is(scalar @$xattrs, 0, 'number of xattrs');
+    }
+}
+
+# multiple xattrs
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $uri = $neb->create('foo');
+
+    ok($neb->setxattr('foo', 'bar', 'baz', 'create'), 'set object xattr');
+    ok($neb->setxattr('foo', 'bonk', 'quix', 'create'), 'set object xattr');
+    
+    {
+        my $xattrs = $neb->listxattr('foo');
+        is(scalar @$xattrs, 2, 'number of xattrs');
+        is(@$xattrs[0], 'bar', 'xattr name');
+        is(@$xattrs[1], 'bonk', 'xattr name');
+    }
+
+    my $value = $neb->getxattr('foo', 'bar');
+    is($value, 'baz', 'xattr value');
+    $value = $neb->getxattr('foo', 'bonk');
+    is($value, 'quix', 'xattr value');
+
+    ok($neb->removexattr('foo', 'bar'), "remove object xattr");
+    ok($neb->removexattr('foo', 'bonk'), "remove object xattr");
+    {
+        my $xattrs = $neb->listxattr('foo');
+        is(scalar @$xattrs, 0, 'number of xattrs');
+    }
+}
+
+# replace xattrs
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $uri = $neb->create('foo');
+
+    ok($neb->setxattr('foo', 'bar', 'baz', 'create'), 'set object xattr');
+    ok($neb->setxattr('foo', 'bar', 'quix', 'replace'), 're-set object xattr');
+    
+    {
+        my $xattrs = $neb->listxattr('foo');
+        is(scalar @$xattrs, 1, 'number of xattrs');
+        is(@$xattrs[0], 'bar', 'xattr name');
+    }
+
+    my $value = $neb->getxattr('foo', 'bar');
+    is($value, 'quix', 'xattr value');
+
+    ok($neb->removexattr('foo', 'bar'), "remove object xattr");
+    {
+        my $xattrs = $neb->listxattr('foo');
+        is(scalar @$xattrs, 0, 'number of xattrs');
+    }
+}
+
+# setxattr
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->setxattr();
+};
+like($@, qr/4 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create('foo');
+
+    $neb->setxattr('foo', 'bar');
+};
+like($@, qr/4 were expected/, "too few params");
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create('foo');
+
+    $neb->setxattr('foo', 'bar', 'baz');
+};
+like($@, qr/4 were expected/, "too few params");
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create('foo');
+
+    $neb->setxattr('foo', 'bar', 'baz', 'create', 'quix');
+};
+like($@, qr/4 were expected/, "too many params");
+
+# listxattr
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create('foo');
+
+    $neb->listxattr();
+};
+like($@, qr/1 was expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create('foo');
+
+    $neb->listxattr('foo', 'bar');
+};
+like($@, qr/1 was expected/, "too many params");
+
+# getxattr
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->getxattr();
+};
+like($@, qr/2 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create('foo');
+
+    $neb->getxattr('foo');
+};
+like($@, qr/2 were expected/, "too few params");
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create('foo');
+
+    $neb->getxattr('foo', 'bar', 'baz');
+};
+like($@, qr/2 were expected/, "too many params");
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/90_nebclient.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/90_nebclient.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/90_nebclient.t	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 90_nebclient.t,v 1.2 2007-04-26 22:27:13 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+use lib qw( ./t ./lib );
+
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+$ENV{'NEB_SERVER'} = "http://" . Apache::Test->config->{ 'hostport' } . "/nebulous";
+
+chdir "./nebclient";
+system "make check";
+
+Test::Nebulous->cleanup;
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/TEST.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/TEST.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/TEST.PL	(revision 22322)
@@ -0,0 +1,12 @@
+#file:t/TEST.PL
+#--------------
+#!perl
+
+use strict;
+use warnings FATAL => 'all';
+
+use lib qw(lib);
+
+use Apache::TestRunPerl ();
+
+Apache::TestRunPerl->new->run(@ARGV);
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/conf/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/conf/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/conf/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+apache_test_config.pm
+extra.conf
+httpd.conf
+modperl_inc.pl
+modperl_startup.pl
+startup.pl
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/conf/extra.conf.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/conf/extra.conf.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/conf/extra.conf.in	(revision 22322)
@@ -0,0 +1,11 @@
+#file:t/conf/extra.conf.in
+#-------------------------
+# this file will be Include-d by @ServerRoot@/httpd.conf
+
+<Location /nebulous>
+    SetHandler perl-script
+    PerlResponseHandler Apache2::SOAP
+    PerlSetVar dispatch_to "PerlHandler Nebulous::Server::SOAP"
+    PerlSetVar options "compress_threshold => 10000"
+</Location>
+
Index: /tags/sj_tags/sj_root_20080929/Nebulous/t/conf/startup.pl.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Nebulous/t/conf/startup.pl.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Nebulous/t/conf/startup.pl.in	(revision 22322)
@@ -0,0 +1,22 @@
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( @ServerRoot@/../lib );
+use lib qw( @ServerRoot@ );
+
+use Apache::DBI;
+use DBI;
+use Nebulous::Server::SOAP;
+use Nebulous::Server::Apache;
+use Nebulous::Server;
+use Test::Nebulous;
+
+Apache::DBI->connect_on_init( $NEB_DB, $NEB_USER, $NEB_PASS );
+Nebulous::Server::SOAP->new_on_init(
+    dsn         => $NEB_DB,
+    dbuser      => $NEB_USER,
+    dbpasswd    => $NEB_PASS,
+    log_level   => 'all',
+);
+
+1;
Index: /tags/sj_tags/sj_root_20080929/Ohana/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+help
+bin
+lib
+include
+man
+CVS
+ohana-config
+Makefile
+Makefile.System
Index: /tags/sj_tags/sj_root_20080929/Ohana/Configure.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/Configure.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/Configure.in	(revision 22322)
@@ -0,0 +1,28 @@
+# Configure file for Ohana Package
+# the values of the form @WORD@ are filled in by configure
+
+# location of the package and architecture
+ROOT    = @ROOTDIR@
+ARCH    = @ARCHVAL@
+
+# destination directories:
+DESTBIN	 = @BINDIR@
+DESTINC	 = @INCDIR@
+DESTLIB	 = @LIBDIR@
+DESTMAN	 = @MANDIR@
+DESTDATA = @DATADIR@
+
+# C compiler options
+CC       = @CC@
+CFLAGS   = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS  = @LDFLAGS@
+DLLTYPE  = @DLLTYPE@
+
+# flags for external dependencies
+INCDIRS  = @INCDIRS@
+LIBDIRS  = @LIBDIRS@
+LIBFLAGS = @LIBFLAGS@
+
+# select the appropriate version of ranlib
+RANLIB = @RANLIB@
Index: /tags/sj_tags/sj_root_20080929/Ohana/Makefile.Common
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/Makefile.Common	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/Makefile.Common	(revision 22322)
@@ -0,0 +1,113 @@
+# this file must be added to the makefile *after* LIB,BIN, etc are defined
+
+# (INC)     & (LIB)     are the program's local include & lib directories
+# (DESTINC) & (DESTLIB) are the target installation include & lib directories
+# (INCDIRS) & (LIBDIRS) are all of the probed include & lib directories
+# (LIBFLAGS) is the list of -lXXX directives from configure for external libs
+
+BASE_CFLAGS   =	$(CFLAGS)
+BASE_CPPFLAGS =	$(CPPFLAGS) -I$(INC) -I$(DESTINC) $(INCDIRS) -D$(ARCH)
+BASE_LDFLAGS  = $(LDFLAGS) -L$(LIB) -L$(DESTLIB) $(LIBDIRS) $(LIBFLAGS)
+
+DVO_LIBS  = $(DESTLIB)/libdvo.a $(DESTLIB)/libFITS.a $(DESTLIB)/libohana.a
+KAPA_LIBS = $(DVO_LIBS) $(DESTLIB)/libkapa.a 
+
+DVO_INCS  = $(DESTINC)/dvo.h $(DESTINC)/gfitsio.h $(DESTINC)/ohana.h $(DESTINC)/autocode.h 
+KAPA_INCS = $(DVO_INCS) $(DESTINC)/kapa.h 
+
+.PRECIOUS: %.$(ARCH).o
+.PRECIOUS: $(BIN)/%.$(ARCH)
+
+# is this line needed?
+.SUFFIXES: .$(ARCH).o
+
+$(DESTINC)/%: $(INC)/%
+	@if [ ! -d $(DESTINC) ]; then mkdir -p $(DESTINC); fi
+	rm -f $@
+	cp $< $@
+
+%.$(ARCH).o : %.c
+	$(CC) $(FULL_CFLAGS) $(FULL_CPPFLAGS) -c $< -o $@
+
+$(BIN)/%.$(ARCH):
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	$(CC) $(FULL_CFLAGS) -o $@ $^ $(FULL_LDFLAGS)
+	@echo "compiled $*"
+	@echo ""
+
+$(DESTBIN)/%: $(BIN)/%.$(ARCH) 
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	rm -f $(DESTBIN)/$*
+	cp $(BIN)/$*.$(ARCH) $(DESTBIN)/$*
+	@echo "installed $*"
+	@echo ""
+
+$(LIB):
+	@if [ ! -d $(LIB) ]; then mkdir -p $(LIB); fi
+
+$(LIB)/%.$(ARCH).a:
+	@if [ ! -d $(LIB) ]; then mkdir -p $(LIB); fi
+	rm -f $@
+	ar rcv $@ $^
+	$(RANLIB) $@
+	@echo "compiled static library $*"
+	@echo ""
+
+$(DESTLIB)/%.a: $(LIB)/%.$(ARCH).a
+	@if [ ! -d $(DESTLIB) ]; then mkdir -p $(DESTLIB); fi
+	rm -f $@
+	cp $< $@
+	@echo "installed static library $*"
+	@echo ""
+
+$(LIB)/%.$(ARCH).so:
+	@if [ ! -d $(LIB) ]; then mkdir -p $(LIB); fi
+	rm -f $@
+	gcc -shared -Wl,-soname,$*.so -o $@ $^ -lc
+	@echo "compiled shared library $*"
+	@echo ""
+
+$(LIB)/%.$(ARCH).dylib:
+	@if [ ! -d $(LIB) ]; then mkdir -p $(LIB); fi
+	rm -f $@
+	gcc -dynamiclib -o $@ $^ -lc $(FULL_LDFLAGS)
+	@echo "compiled shared library $*"
+	@echo ""
+
+# shared objects have a different compilation on solaris:
+# gcc -shared -o $@ $^ -lc
+
+$(DESTLIB)/%.so: $(LIB)/%.$(ARCH).so
+	@if [ ! -d $(DESTLIB) ]; then mkdir -p $(DESTLIB); fi
+	rm -f $@
+	cp $< $@
+	@echo "installed shared library $*"
+	@echo ""
+
+$(DESTLIB)/%.dylib: $(LIB)/%.$(ARCH).dylib
+	@if [ ! -d $(DESTLIB) ]; then mkdir -p $(DESTLIB); fi
+	rm -f $@
+	cp $< $@
+	@echo "installed shared library $*"
+	@echo ""
+
+$(DESTMAN)/%: $(MAN)/%
+	@if [ ! -d $(DESTMAN) ]; then mkdir -p $(DESTMAN); fi
+	rm -f $@
+	cp $< $@
+
+clean:
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
+
+dist: clean
+	rm -rf $(BIN)
+	rm -rf $(LIB)
+
+# $@ : target filename
+# $* : matched word (%)
+# $< : first prereq
+# $^ : all prereqs
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/Makefile.System.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/Makefile.System.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/Makefile.System.in	(revision 22322)
@@ -0,0 +1,28 @@
+# Configure file for Ohana Package
+# the values of the form @WORD@ are filled in by configure
+
+# location of the package and architecture
+ROOT    = @ROOTDIR@
+ARCH    = @ARCHVAL@
+
+# destination directories:
+DESTBIN	 = @BINDIR@
+DESTINC	 = @INCDIR@
+DESTLIB	 = @LIBDIR@
+DESTMAN	 = @MANDIR@
+DESTDATA = @DATADIR@
+
+# C compiler options
+CC       = @CC@
+CFLAGS   = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS  = @LDFLAGS@
+DLLTYPE  = @DLLTYPE@
+
+# flags for external dependencies
+INCDIRS  = @INCDIRS@
+LIBDIRS  = @LIBDIRS@
+LIBFLAGS = @LIBFLAGS@
+
+# select the appropriate version of ranlib
+RANLIB = @RANLIB@
Index: /tags/sj_tags/sj_root_20080929/Ohana/Makefile.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/Makefile.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/Makefile.in	(revision 22322)
@@ -0,0 +1,149 @@
+# --------------------------------------------------------------------------- #
+# --------------------------------------------------------------------------- #
+#									      #
+#                 OOO    HH   HH    AAA    NN   NN    AAA		      #
+#               OO   OO  HH   HH  AA   AA  NNN  NN  AA   AA		      #
+#               OO   OO  HHHHHHH  AAAAAAA  NN N NN  AAAAAAA		      #
+#               OO   OO  HH   HH  AA   AA  NN  NNN  AA   AA		      #
+#                 OOO    HH   HH  AA   AA  NN   NN  AA   AA		      #
+#      								              #
+# --------------------------------------------------------------------------- #
+# --------------------------------------------------------------------------- #
+
+# This makefile compiles all Ohana package programs in the Elixir
+# system, and their libraries.  Most of the components in this
+# directory were created for Ohana, but there are a few libraries
+# which are from external sources: libjpeg, libpng, zlib, and
+# readline.  Extra care should be taken in compiling those libraries. 
+
+# Edit Configure to reflect the location of your installation 
+# or use the script 'configure'
+
+include Makefile.System
+
+# .SILENT:								       
+default: all
+
+# The collection of Ohana programs.  Other components in the src
+# directory may be compiled by going to those directories. 
+PROGRAM =   \
+addstar     \
+delstar     \
+dvosplit    \
+elixir      \
+gastro      \
+gastro2     \
+getstar     \
+gcompare    \
+imclean     \
+imregister  \
+kapa2       \
+misc        \
+mosastro    \
+nightd      \
+opihi       \
+perl        \
+relphot     \
+relastro    \
+shell       \
+skycalc     \
+tools       \
+tcl         \
+uniphot
+
+# any of these programs can be built with 'make (name)' 
+EXTRAS =     \
+opihi.v1    \
+fixcat      \
+gophot      \
+getusno     \
+lightcurve  \
+markrock    \
+photdbc     \
+markstar    \
+mosastro.v1 \
+mosastro.v2
+
+OLD = mana status addusno addrefs addspphot
+
+LIBS = libtap libohana libfits libdvo libkapa
+
+# general build targets:
+libs:
+	@if [ "$(ARCH)" = "" ]; then echo ""; echo " *** please define ARCH ***"; echo; exit 1; fi
+	mkdir -p $(DESTINC)
+	mkdir -p $(DESTLIB)
+	mkdir -p $(DESTBIN)
+	for i in $(LIBS); do make $$i.install || exit; done
+	chmod +x ohana-config
+	cp -f ohana-config $(DESTBIN)/
+
+bins: 
+	for i in $(PROGRAM); do make $$i; done
+
+all:
+	make libs || exit
+	for i in $(PROGRAM); do make $$i || exit; done
+
+extras:
+	for i in $(EXTRAS); do make $$i || exit; done
+
+pantasks:
+	make libs || exit
+	cd src/opihi; make pclient.install && exit
+	cd src/opihi; make pcontrol.install && exit
+	cd src/opihi; make pantasks.install && exit
+
+mana:
+	make libs
+	make kapa2.install
+	cd src/opihi; make mana.install && exit
+
+dvoshell:
+	make libs
+	make kapa2.install
+	cd src/opihi; make dvo.install && exit
+
+clean:
+	@if [ "$(ARCH)" = "" ]; then echo ""; echo " *** please define ARCH ***"; echo; exit 1; fi
+	for i in $(LIBS); do make $$i.clean || exit; done
+	for i in $(PROGRAM); do make $$i.clean || exit; done
+	for i in $(EXTRAS); do make $$i.clean || exit; done
+	@rm -f `find . -name .mana`
+	@rm -f `find . -name .dvo`
+	@echo -n -e "\0033]0; *** Ohana: done $@ *** \0007" \
+
+dist:
+	@if [ "$(ARCH)" = "" ]; then echo ""; echo " *** please define ARCH ***"; echo; exit 1; fi
+	for i in $(LIBS); do make $$i.dist || exit; done
+	for i in $(PROGRAM); do make $$i.dist || exit; done
+	@echo -n -e "\0033]0; *** Ohana: done $@ *** \0007" \
+
+install:
+	@if [ "$(ARCH)" = "" ]; then echo ""; echo " *** please define ARCH ***"; echo; exit 1; fi
+	for i in $(LIBS); do make $$i.install || exit; done
+	for i in $(PROGRAM); do make $$i.install || exit; done
+	@echo -n -e "\0033]0; *** Ohana: done $@ *** \0007" \
+
+install.extras:
+	for i in $(EXTRAS); do make $$i.install || exit; done
+	@echo -n -e "\0033]0; *** Ohana: done $@ *** \0007" \
+
+# standard rules: targets are foo, foo.clean, foo.install, foo.dist
+$(PROGRAM) $(LIBS) $(EXTRAS): 
+	@if [ -d "src/$@" ]; then \
+	  echo -n -e "\0033]0; *** Ohana: $@ *** \0007"; \
+	  (cd src/$@ && make); \
+	fi
+	@echo -n -e "\0033]0; *** Ohana: done $@ *** \0007" \
+
+%.install:
+	@echo -n -e "\0033]0; *** Ohana: $@ *** \0007" \
+	mkdir -p bin/$(ARCH)
+	if [ -d "src/$*" ]; then (cd src/$* && make install); fi
+
+%.clean:
+	if [ -d "src/$*" ]; then (cd src/$* && make clean); fi
+
+%.dist:
+	(cd src/$* && make dist)
Index: /tags/sj_tags/sj_root_20080929/Ohana/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/README	(revision 22322)
@@ -0,0 +1,5 @@
+
+This is the top-level directory for Ohana.  The programs include here
+may be used on their own, or in conjunction with the full Pan-STARRS
+Image Processing Pipeline (IPP).
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/autogen.sh	(revision 22322)
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+for arg in $*; do
+    case $arg in
+        --no-configure)
+	    exit 0
+            ;;
+        *)
+            ;;
+    esac
+done
+
+./configure.tcsh $*
Index: /tags/sj_tags/sj_root_20080929/Ohana/config.tools
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/config.tools	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/config.tools	(revision 22322)
@@ -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.System | sed "s|$argv[2]|$argv[3]|" > Makefile.System.tmp
+mv -f Makefile.System.tmp Makefile.System
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/configure
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/configure	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/configure	(revision 22322)
@@ -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: /tags/sj_tags/sj_root_20080929/Ohana/configure.tcsh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/configure.tcsh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/configure.tcsh	(revision 22322)
@@ -0,0 +1,677 @@
+#!/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 vararch = 0
+set optimize = 0
+set pedantic = 0
+set memcheck = 0
+set use_tcmalloc = 0
+
+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")
+  # options passed by jhbuild or others which we ignore
+  case --enable-maintainer-mode
+  case --no-create
+  case --no-recursion
+  case --sbindir*
+  case --libexecdir*
+  case --sharedstatedir*
+  case --localstatedir*
+  case --oldincludedir*
+  case --infodir*
+   # 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 --disable-shared
+  case --enable-shared
+  case --disable-static
+  case --enable-static
+    echo ""
+    echo "WARNING: Ohana can't turn shared/static builds on/off."
+    echo ""
+    breaksw;
+  case --vararch
+   set vararch = 1
+   breaksw;
+  case --enable-optimize
+   set optimize = 1
+   breaksw;
+  case --enable-profile
+   set profile = 1
+   breaksw;
+  case --enable-memcheck
+   set memcheck = 1
+   breaksw;
+  case --use-tcmalloc
+   set use_tcmalloc = 1
+   breaksw;
+  case --pedantic
+   set pedantic = 1
+   breaksw;
+  case --prefix*
+   if ("$1" == "--prefix") then
+     shift
+     set prefix = $1
+   else
+     set prefix = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --exec-prefix*
+   if ("$1" == "--exec-prefix") then
+     shift
+     set exec_prefix = $1
+   else
+     set exec_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 --libdir*
+   if ("$1" == "--libdir") then
+     shift
+     set libdir = $1
+   else
+     set libdir = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --includedir*
+   if ("$1" == "--includedir") then
+     shift
+     set incdir = $1
+   else
+     set incdir = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --sysconfdir*
+   if ("$1" == "--sysconfdir") then
+     shift
+     set sysconfdir = $1
+   else
+     set sysconfdir = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --datadir*
+   if ("$1" == "--datadir") then
+     shift
+     set datadir = $1
+   else
+     set datadir = `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
+
+if ("$exec_prefix" == "") then
+  set exec_prefix = $prefix
+endif
+
+# replace $exec_prefix in variables
+#echo "setting libdir ($libdir)"
+#set libdir = `echo $libdir | sed 's|\$exec_prefix|$exec_prefix|'`
+
+# set values for CC, CFLAGS, CPPFLAGS, LDFLAGS
+if (! $?CC) then
+  set CC = gcc
+endif  
+
+if (! $?CFLAGS) then
+  set CFLAGS = "-g -O0"
+endif  
+# optimize overrides user-supplied CFLAGS
+if ($optimize) set CFLAGS = "-O2"
+
+# profiler code
+if ($profile) set CFLAGS = "$CFLAGS -pg"
+
+if (! $?CPPFLAGS) then
+  set CPPFLAGS = ""
+endif  
+if ($pedantic) set CPPFLAGS = "$CPPFLAGS-Wall -Werror "
+if ($memcheck) set CPPFLAGS = "$CPPFLAGS-DOHANA_MEMORY "
+
+if (! $?LDFLAGS) then
+  set LDFLAGS = 
+endif  
+if ($profile) set LDFLAGS = "$LDFLAGS -Wl,--start-group -Wl,-Bstatic -Wl,-Bdynamic"
+
+set syslibpath = "/lib /usr/lib /usr/X11R6/lib /usr/local/lib"
+set needlibs   = ""
+set needlibs   = "$needlibs png"
+set needlibs   = "$needlibs z"
+set needlibs   = "$needlibs jpeg"
+set needlibs   = "$needlibs readline"
+set needlibs   = "$needlibs X11"
+set needlibs   = "$needlibs pthread"
+set needlibs   = "$needlibs m"
+
+set optlibs    = ""
+set optlibs    = "$optlibs mysqlclient"
+if ($use_tcmalloc) set optlibs = "$optlibs tcmalloc"
+
+set sysincpath = "/usr/include /usr/local/include /usr/X11R6/include"
+
+set needincs = ""
+set needincs = "$needincs X11/Xatom.h"
+set needincs = "$needincs X11/Xlib.h"
+set needincs = "$needincs X11/Xresource.h"
+set needincs = "$needincs X11/Xutil.h"
+set needincs = "$needincs X11/cursorfont.h"
+set needincs = "$needincs X11/keysym.h"
+set needincs = "$needincs X11/keysymdef.h"
+set needincs = "$needincs arpa/inet.h"
+set needincs = "$needincs ctype.h"
+set needincs = "$needincs errno.h"
+set needincs = "$needincs fcntl.h"
+set needincs = "$needincs glob.h"
+set needincs = "$needincs jpeglib.h"
+set needincs = "$needincs math.h"
+set needincs = "$needincs netdb.h"
+set needincs = "$needincs netinet/ip.h"
+set needincs = "$needincs png.h"
+set needincs = "$needincs pthread.h"
+set needincs = "$needincs readline/history.h"
+set needincs = "$needincs readline/readline.h"
+set needincs = "$needincs signal.h"
+set needincs = "$needincs stdio.h"
+set needincs = "$needincs stdlib.h"
+set needincs = "$needincs string.h"
+set needincs = "$needincs sys/ipc.h"
+set needincs = "$needincs sys/resource.h"
+set needincs = "$needincs sys/sem.h"
+set needincs = "$needincs sys/socket.h"
+set needincs = "$needincs sys/stat.h"
+set needincs = "$needincs sys/time.h"
+set needincs = "$needincs sys/types.h"
+set needincs = "$needincs sys/uio.h"
+set needincs = "$needincs sys/un.h"
+set needincs = "$needincs sys/wait.h"
+set needincs = "$needincs time.h"
+set needincs = "$needincs unistd.h"
+set needincs = "$needincs zlib.h"
+
+set optincs  = ""
+set optincs = "$optincs mysql.h"
+
+# XXX need to have options for non-ANSI includes? (ie, varargs.h)
+# set needincs = "$needincs cfuncs.h" - from non-ANSI option in ohana.h
+# set needincs = "$needincs float.h" - is from missing_proto (CFHT)
+# set needincs = "$needincs floatingpoint.h" - is from missing_proto (CFHT)
+# set needincs = "$needincs stdarg.h" - from std includes (in gcc path)
+# set needincs = "$needincs varargs.h" - from std includes (in gcc path)
+
+# XXX I was probing for these before, but RHL claims we don't need them.
+# I suspect they may be needed on older systems a la CFHT.
+# set needincs = "$needincs malloc.h"
+# set needincs = "$needincs memory.h"
+# set needincs = "$needincs values.h"
+
+# check the hardware architecture:
+set sys=`uname -s` 
+set ranlib = "ranlib"
+set dlltype = "so"
+switch ($sys)
+ case IRIX64:
+   set arch="irix";
+   breaksw;
+ case SunOS:
+   set ver=`uname -r | awk '{print substr($1,1,1)}'`;
+   if ($ver == 5) then
+     set arch="sol";
+   else 
+     set arch="sun4";
+   endif
+   # sun (at least) seems to need the socket library (linux does not)
+   set syslibpath = "$syslibpath /usr/openwin/lib"
+   set sysincpath = "$sysincpath /usr/openwin/include"
+   set needlibs = "$needlibs libsocket libnsl"
+   set ranlib = "touch"
+   breaksw;
+ case Linux:
+   set arch="linux";
+   if (-e /etc/sidious.config) set arch="sid";
+   set mach=`uname -m`
+   if ("$mach" == "x86_64") then
+    set arch="lin64";
+    set syslibpath = "/lib64 /usr/lib64 /usr/X11R6/lib64 $syslibpath"
+   endif
+   breaksw;
+ case Darwin:
+   set arch="darwin";
+   set mach=`uname -m`
+   if ("$mach" == "i386") then
+    set arch="darwin_x86";
+   endif
+   set syslibpath = "$syslibpath /sw/lib"
+   set sysincpath = "$sysincpath /sw/include"
+   set dlltype = dylib
+   set CFLAGS = "$CFLAGS -D_DARWIN_C_SOURCE"
+   set defines = "-D_DARWIN_C_SOURCE"
+   breaksw;
+ case HP-UX:
+    set arch="hpux";
+    breaksw;
+ default:
+   echo "unknown architecture";
+   exit 1;
+   breaksw;
+endsw
+echo "setting architecture to: $arch" 
+
+# set up the basic directory names:
+set root = `pwd`
+if ($prefix == "") set prefix = $root
+
+# set the install include directory
+if ($incdir == "") then
+  if ($vararch) then
+    set incdir = $prefix/include/$arch
+  else
+    set incdir = $prefix/include
+  endif
+endif
+
+# set the install lib directory
+if ($libdir == "") then
+  if ($vararch) then
+    set libdir = $prefix/lib/$arch
+  else
+    set libdir = $prefix/lib
+  endif
+endif
+
+if ($?LIBRARY_PATH) then 
+  set libpath = `echo $LIBRARY_PATH | tr ':' ' '`
+else
+  set libpath = ""
+endif
+
+# check for basic libraries
+echo ""
+echo "searching for required external libraries..."
+set faillibs = ""
+set libflags = ""
+set libdirs  = ""
+set nonomatch
+foreach f ( $needlibs )
+  foreach g ( $libpath $libdir $syslibpath )
+    set name = $g/lib$f.a
+    if (-e $name[1]) goto got_lib;
+    set name = $g/lib$f.$dlltype
+    if (-e $name[1]) goto got_lib;
+  end
+  echo "missing lib$f"
+  set faillibs = "$faillibs lib$f"
+  continue
+got_lib:
+  echo "found lib$f ($name[1])"
+  set gotlibdir = `dirname $name[1]`
+  echo "$libdirs" | grep -- "-L$gotlibdir " > /dev/null
+  if ($status) then
+    set libdirs  = "$libdirs-L$gotlibdir "
+  endif
+  echo "$libflags" | grep -- "-l$f " > /dev/null
+  if ($status) then
+    set libflags = "$libflags-l$f "
+  endif
+end
+
+# we need a curses library; can choose one of the following:
+# check for termcap, curses, etc
+foreach f ( ncurses curses termcap )
+    foreach g ( $libpath $libdir $syslibpath )
+        # echo "trying $g"
+        set name = $g/lib$f.a
+        # echo "trying $name"
+        if (-e $name[1]) goto got_curses;
+        set name = $g/lib$f.$dlltype
+        # echo "trying $name"
+        if (-e $name[1]) goto got_curses;
+    end
+    # try versioned libraries such as .so.N
+    foreach g ( $libpath $libdir $syslibpath )
+        # echo "trying $g"
+        set name = $g/lib$f.$dlltype.*
+	# echo "$#name : $name[1]"
+        if ($#name < 2) continue
+        # echo "trying $name[1]"
+        if (-e $name[1]) goto got_curses;
+    end
+end
+set faillibs = "$faillibs (ncurses | curses | termcap)"
+echo "missing a valid curses library"
+echo "missing: $faillibs"
+echo "please find one of them and install them in $libpath"
+exit 1
+
+got_curses:
+  echo "found $f ($name[$#name])"
+  echo "$libdirs" | grep -- "-L$g " > /dev/null
+  if ($status) then
+    set libdirs  = "$libdirs-L$g "
+  endif
+  echo "$libflags" | grep -- "-l$f " > /dev/null
+  if ($status) then
+    set libflags = "$libflags-l$f "
+  endif
+
+if ("$faillibs" != "") then
+  echo "your installation is missing some important libraries"
+  echo "missing: $faillibs"
+  echo "please find them and install them in $lib"
+  exit 1
+endif    
+
+# check for optional libraries
+echo ""
+echo "searching for optional external libraries..."
+foreach f ( $optlibs )
+  foreach g ( $libpath $libdir $syslibpath )
+    set name = $g/lib$f.a
+    if (-e $name[1]) goto got_optlib;
+    set name = $g/lib$f.$dlltype
+    if (-e $name[1]) goto got_optlib;
+    set name = $g/*/lib$f.a
+    if (-e $name[1]) goto got_optlib;
+    set name = $g/*/lib$f.$dlltype
+    if (-e $name[1]) goto got_optlib;
+  end
+  echo "missing lib$f; skipping"
+  continue
+got_optlib:
+  echo "found lib$f ($name[1])"
+  set gotlibdir = `dirname $name[1]`
+  echo "$libdirs" | grep -- "-L$gotlibdir " > /dev/null
+  if ($status) then
+    set libdirs  = "$libdirs-L$gotlibdir "
+  endif
+  echo "$libflags" | grep -- "-l$f " > /dev/null
+  if ($status) then
+    set libflags = "$libflags-l$f "
+  endif
+end
+
+# add the CPATH
+if ($?CPATH) then 
+  set incpath = `echo $CPATH | tr ':' ' '`
+else
+  set incpath = ""
+endif
+
+# check for required headers (including in subdirectories)
+echo ""
+echo "searching for required external header files..."
+set failincs = ""
+set incdirs = ""
+foreach f ( $needincs )
+  foreach g ( $incdir $incpath $sysincpath )
+    set name = "$g/$f"
+    if (-e $name) goto got_inc;
+    set nonomatch
+    set name = $g/*/$f
+    echo "$name" | grep "*" > /dev/null
+    if (! $status) continue
+    unset nonomatch
+    if (-e $name[1]) goto got_inc;
+  end
+  echo "missing $f"
+  set failincs = "$failincs $f"
+  continue
+got_inc:
+  set gotinc = $name[1]
+  echo "found $f ($gotinc)"
+  set gotincdir = `dirname $name[1]`
+  if (`dirname $f` != ".") then
+    set gotincdir = `dirname $gotincdir`
+  endif
+  echo "$incdirs" | grep -- "-I$gotincdir " > /dev/null
+  if ($status) then
+    set incdirs = "$incdirs-I$gotincdir "
+  endif
+end
+
+if ("$failincs" != "") then
+  echo "your installation is missing some important library headers"
+  echo "please find them and install them in $inc"
+  exit 1
+endif    
+
+# check for optional headers (including in subdirectories)
+echo ""
+echo "searching for optional external header files..."
+foreach f ( $optincs )
+  foreach g ( $incdir $incpath $sysincpath )
+    set name = "$g/$f"
+    if (-e $name) goto got_optinc;
+    set nonomatch
+    set name = $g/*/$f
+    echo "$name" | grep "*" > /dev/null
+    if (! $status) continue
+    unset nonomatch
+    if (-e $name[1]) goto got_optinc;
+  end
+  echo "missing $f; skipping"
+  continue
+got_optinc:
+  set gotinc = $name[1]
+  echo "found $f ($gotinc)"
+  set gotincdir = `dirname $name[1]`
+  if (`dirname $f` != ".") then
+    set gotincdir = `dirname $gotincdir`
+  endif
+  echo "$incdirs" | grep -- "-I$gotincdir " > /dev/null
+  if ($status) then
+    set incdirs = "$incdirs-I$gotincdir "
+  endif
+  set haveflag = `echo $f | tr '[:lower:]' '[:upper:]' | tr '.' '_'`
+  set CPPFLAGS = "$CPPFLAGS-DHAVE_$haveflag "
+end
+
+echo 
+echo "Compiler options:"
+echo "CC: $CC"
+echo "CFLAGS: $CFLAGS"
+echo "CPPFLAGS: $CPPFLAGS"
+echo "LDFLAGS: $LDFLAGS"
+
+echo
+echo "Additional compiler flags:"
+echo "INCDIRS: $incdirs"
+echo "LIBDIRS: $libdirs"
+echo "LIBFLAGS: $libflags"
+
+echo 
+echo "ARCH: $arch"
+echo "ROOT: $root"
+echo "PREFIX: $prefix"
+echo 
+
+# the config.tools fixconf operations below interpolate values in Makefile.System
+if (-e Makefile.System) mv Makefile.System Makefile.System.bak
+cp Makefile.System.in Makefile.System
+
+# we don't currently need to modify the Makefile but since configure
+# should create a new Makefile, we need to do this:
+cp -f Makefile.in Makefile
+
+# the ROOTDIR defines the location of the source tree
+./config.tools fixconf @ROOTDIR@  "$root"
+
+# the following entries define the target installation locations 
+
+# BINDIR (DESTBIN) holds the output binary files
+if ("$bindir" == "") then
+  set subdir = bin
+  set subpath = bin
+  if ($vararch) then 
+    set subdir = 'bin/$(ARCH)'
+    set subpath = "bin/$arch"
+  endif
+  set bindir = $prefix/$subdir
+  set binpath = $prefix/$subpath
+endif
+set bindir = `./config.tools fixpath $bindir`
+./config.tools fixconf @BINDIR@ $bindir
+echo DESTBIN $bindir
+
+# INCDIR (DESTINC) holds the output header files
+if ("$incdir" == "") then
+  set subdir = include
+  if ($vararch) set subdir = 'include/$(ARCH)'
+  set incdir = $prefix/$subdir
+endif
+set incdir = `./config.tools fixpath $incdir`
+./config.tools fixconf @INCDIR@ $incdir
+echo DESTINC $incdir
+
+# LIBDIR (DESTLIB) holds the output library files
+if ("$libdir" == "") then
+  set subdir = lib
+  if ($vararch) set subdir = 'lib/$(ARCH)'
+  set libdir = $prefix/$subdir
+endif
+set libdir = `./config.tools fixpath $libdir`
+./config.tools fixconf @LIBDIR@ $libdir
+echo DESTLIB $libdir
+
+# 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
+
+# DATADIR (DESTDATA) holds the general non-binary files
+if ("$datadir" == "") then
+  set datadir = $prefix/share
+endif
+set datadir = `./config.tools fixpath $datadir`
+./config.tools fixconf @DATADIR@ $datadir
+echo DESTDATA $datadir
+
+# the vararch option defines an automatic arch-dependent directory 
+# tree for DESTBIN, DESTLIB, DESTINC
+if ($vararch) then
+  ./config.tools fixconf "^\s*ARCH" "# ARCH"
+else 
+  ./config.tools fixconf @ARCHVAL@ $arch
+endif 
+
+# INCDIRS, LIBDIRS, LIBFLAGS define include and library flags needed
+# by the externally-supplied libraries
+./config.tools fixconf @INCDIRS@  "$incdirs"
+./config.tools fixconf @LIBDIRS@  "$libdirs"
+./config.tools fixconf @LIBFLAGS@ "$libflags"
+
+# these are the compiler options
+./config.tools fixconf @CC@ "$CC"
+./config.tools fixconf @CFLAGS@ "$CFLAGS"
+./config.tools fixconf @CPPFLAGS@ "$CPPFLAGS"
+./config.tools fixconf @LDFLAGS@ "$LDFLAGS"
+./config.tools fixconf @DLLTYPE@  "$dlltype"
+
+# other architecture dependent options
+./config.tools fixconf @RANLIB@ "$ranlib"
+
+cat ohana-config.in | sed "s|@INCDIR@|$incdir|" | sed "s|@LIBDIR@|$libdir|" | sed "s|(ARCH)|ARCH|" | sed "s|@DEFINES@|$defines|" > ohana-config
+
+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: /tags/sj_tags/sj_root_20080929/Ohana/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,4 @@
+
+2006.08.23:
+  base-1-4 : added doc and test to base
+	     minor updates to configure
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/GSCRegion.to.SkyRegions.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/GSCRegion.to.SkyRegions.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/GSCRegion.to.SkyRegions.txt	(revision 22322)
@@ -0,0 +1,12 @@
+
+addstar : all GSCRegion entries removed / moved to old
+delstar : all GSCRegion entries removed / moved to old
+markrock : needs a re-write, keep this program?  (is mini-mops)
+markstar : needs a major re-write.  does this have a future?
+opihi : all dropped / moved to old 
+	(used by some deprecated functions, re-write if these are reserrected)
+relphot : all GSCRegion entries removed / moved to old
+relastro : no real work has been done; take from relphot
+
+photdbc : needs re-write, what is the purpose?
+fixcat : needs re-write, what is the purpose?
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/INSTALL	(revision 22322)
@@ -0,0 +1,74 @@
+
+The Elixir system consists of two main software packages: Ohana (main
+developer Eugene Magnier) and Flips (main developer Jean-Charles
+Cuillandre).  The Elixir system combines these two packages, along
+with other external software products, into a complete system
+providing many utilities and functions for image processing at CFHT
+and elsewhere.  This document describes how to install the Ohana
+portion of the Elixir system.  Ohana may be installed from CVS or from
+a distributed tar ball.  Within the Elixir development community, the
+Ohana source tree may be checked out of the Elixir CVS tree.  Ohana is
+also distributed as a tar ball to users outside of the Elixir
+development community.
+
+1) Installing Ohana from source tar ball:
+
+  un-gzip and untar:  
+
+    > gunzip -c ohana.tgz | tar xvf -
+
+  enter the ohana top-level directory:
+
+    > cd ohana
+
+  construct the input Configure file [1]:
+
+    > configure
+
+  this step will note the binary installation path.  This should be
+  added to your PATH
+
+  compile the software
+
+    > make
+
+  install the software
+
+    > make install
+
+2) Installing Ohana from CVS (Developer's version):
+
+  check out the ohana source tree:
+
+    > setenv CVSROOT poma:/data/elixir2/srcdir/cvs
+    > cvs co ohana
+
+  enter the ohana top-level directory:
+
+    > cd ohana
+
+  construct the input Configure file [1]:
+
+    > configure
+
+  this step will note the binary installation path.  This should be
+  added to your PATH
+
+  compile the software
+
+    > make
+
+  install the software
+
+    > make install
+
+***** Notes *****
+
+[1] Ohana is configured for multiple architecture development, which
+is convenient for end users as well.  The libraries, include files,
+and programs, are all installed in directories with the architecture
+type appended: bin/linux, bin/sol, lib/linux, etc.  To take advantage
+of this multiple architecture organization when compiling, end users
+should arrange for the environment variable ARCH to be set based on
+the machine architecture, and for the PATH to depend on ARCH, rather
+than setting it to a fixed quantity.  
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/README	(revision 22322)
@@ -0,0 +1,46 @@
+Documenation for Ohana:
+
+ Ohana includes:
+
+   1) fits - FITS I/O library (Eugene Magnier) (V 1.0)
+	These are clean, C-like FITS interaction routines which are
+	meant to be easy to program with and easy to read, etc.
+
+   2) readline - the GNU readline library. (FSF / GNU) (V1.1)
+	This routines allow for easy emacs / vi-like cursor interactions.
+
+   3) opihi - a shell template program (Eugene Magnier) (V1.0)
+      This program provides a tcsh-like interaction with macros
+	and math capabilities.  It is meant to be a template for any
+	program which may require a shell-like interaction, macros, and math.
+
+   4) mana - an image manipulation program. (Eugene Magnier) (V1.0)
+	This program is in someways similar to MIDAS, IRAF, or Vista (the standard
+	astrophysics analysis packages).  However, mana is meant to be more 
+	stripped down (it is not a "do everything in the world" program!).
+	Mana also has three major advantages over the large programs:
+	  a) it is easy to modify (clean, friendly code!)
+	  b) it has a nice macro language with math at the shell input
+	  c) it has a tcsh-like cursor interaction
+
+   5) kii - display program for mana.
+	This is the X window display tool used by mana.  It is not meant to be run
+	by the user, only by mana.
+
+
+
+-----------------------------------------------------------------------------------------
+Notes
+
+Compiled successfully for HP730, gcc & cc
+
+Compiled successfully for Sparc 
+
+
+
+
+-----------------------------------------------------------------------------------------
+Problems / Bugs
+
+mana / kii are rather slow in general - can we do anything about this?
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/README.include
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/README.include	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/README.include	(revision 22322)
@@ -0,0 +1,23 @@
+
+this top level directory contains the header files associated with
+ohana library packages or basic ohana code snipets not in libraries:
+
+fitsio.h - libFITS.h
+ohana.h, Xohana.h - libohana
+skycalc.h - libskycalc
+loneos.h - FITS database interactions
+
+the architecture-dependent subdirectories allow us to install
+libraries for one architecture which are not needed and would conflict
+on another architecture.  this is mostly a problem for solaris, which
+does not have various libraries installed by default.  we can install
+an older solaris version of these libraries and not cause conflicts
+with the more recent linux versions:
+
+libpng
+libjpeg
+libreadling
+zlib
+
+
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/README.mana
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/README.mana	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/README.mana	(revision 22322)
@@ -0,0 +1,133 @@
+
+ April 26, 2000
+
+ I am providing the first release of 'mana' and the supporting
+ packages.  Here I will describe the needed steps to get the program
+ compiled and running. 
+
+Directory organization and Architecture selection:
+
+ Mana is part of a large collection of software called 'ohana'.  This
+ distribution of mana only includes the components of ohana needed for
+ mana.  The ohana collection is contained within a directory tree that
+ eases the use of the package on a multi-system network.  To this end,
+ there are separate bin and lib directories for different architecture
+ machines.  At the top level of 'ohana' there are the src, bin, lib,
+ and include directories.  bin and lib have subdirectories named
+ e.g. bin/linux, bin/sol, or bin/hp.  The Makefiles throughout 'ohana'
+ use an environment variable ARCH to decide which of these directories
+ is the appropriate destination.  Therefore:
+
+ BEFORE compiling any of the ohana programs, please set the ARCH
+ environment variable appropriately for your machine.  To do this
+ automatically, I use the following lines in my .cshrc:
+ 
+     ### automatically setting the ARCH variable:
+     set sys=`uname -s` 
+     switch ($sys)
+      case IRIX64:
+        setenv ARCH irix;
+        breaksw;
+      case SunOS:
+        set ver=`uname -r | awk '{print substr($1,1,1)}'`;
+        if ($ver == 5) then
+          setenv ARCH sol
+        else 
+          setenv ARCH sun4
+        endif
+        breaksw;
+      case Linux:
+        setenv ARCH linux;
+        breaksw;
+      case HP-UX:
+        setenv ARCH hp;
+        breaksw;
+      default:
+        echo "unknown architecture";
+        setenv ARCH unknown;
+        breaksw;
+     endsw
+
+  This mechanism also lets you set your PATH correctly, no matter what
+  architecture you are on.  just include in your PATH
+  /h/fred/src/ohana/bin/{$ARCH} (or whatever the path is to the ohana
+  package!).  
+
+  In order to configure the ohana package for your local installation,
+  edit the file ohana/Configure.  Change the entry for ROOT to point
+  to the correct location of the ohana package.  Also in Configure,
+  you can tell ohana to install things in a different location from
+  the ohana directory structure.
+
+  
+Necessary Libraries:
+
+  I've included in this distribution all of the necessary libraries to
+  compile mana.  Two of these are part of the ohana package: the fits
+  and ohana libraries.  These two should compile trivially when you
+  run make.  There are several other libraries that are typically
+  included in various UNIX installations, but are sometimes missing.
+  I've included them in this distribution in case you need them, but
+  the Makefiles are not well tuned to fit with the ohana layout.  In
+  particular, libjpeg and libpng you will have to compile by hand (ie,
+  by going into those directories and compiling there, not from the
+  top level ohana directory).  Install the resulting libraries and
+  include files in the appropriate locations (either in your system,
+  or just in the appropriate lib and include directores in ohana).
+  The readline library might compile correctly from the ohana
+  Makefile, but I am not sure of this. 
+
+  It is a good idea to try the mana compilation without dealing with
+  these libraries.  You'll find out soon enough which ones your
+  systems doesn't have!
+
+  The other oddity is the situation with libnsl and libsocket.  These
+  are required libraries for Suns, but not available (not even needed)
+  under linux.  To keep the same link lines in the Makefiles, I have
+  made a dummy version of libnsl and libsocket, which should be put in
+  ohana/lib/linux.  see the ohana/src/libdummy/Makefile for details.  
+
+Compiling and installing:
+
+  First of all, move Makefile.mana to Makefile.
+
+  If the libaries are all OK, then you should be able to compile and
+  install mana and the display programs kapa and kii with the command:
+
+  make install
+
+  
+Running Mana:
+
+  Just a couple of notes about running mana.  I'm hoping the commands
+  are generally obvious and the help files helpful...
+
+  When mana starts, it loads a resource file ~/.manarc.  This is just
+  a file with a bunch of mana commands.  They can include common macro
+  definitions, defining variables, or whatever.  I've included an
+  example as mana.rc in ohana/src/mana/doc/
+
+  Mana uses two external programs to display images and vector plots,
+  nominally called 'kii' and 'kapa'.  By default, it assumes those two
+  names, but user may change the names if needed.  Mana looks at the
+  variables (mana variables, not env variables) KAPA and KII to decide
+  the program names.  By default these are set to 'kapa' and 'kii'.
+  Type "echo $KAPA" or "echo $KII" in mana to see the current values.
+  If you need to place these programs outside your path or give them
+  different names, just assign these variables (perhaps in the
+  .manarc) to the correct names and they should work fine.
+
+  In the same way, mana looks for the help files in a preset location,
+  specifically the directory of the mana executable, subdirectory
+  mana.hlp.  So, if you have mana in /usr/local/bin, mana will look
+  for the help files in /usr/local/bin/mana.hlp.  But again, this can
+  be reassigned by the user by changing the mana variable HELPDIR.  
+
+  Two useful mana commands are ? and ??.  The first lists all commands
+  and gives a little helpline.  the second lists are currently defined
+  mana variables.
+
+
+
+  Eugene Magnier
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/allocate.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/allocate.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/allocate.txt	(revision 22322)
@@ -0,0 +1,40 @@
+
+# define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+# define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+
+# ifndef ALLOCATE
+# define ALLOCATE(X,T,S)  \
+  X=(T *)malloc((unsigned) ((S)*sizeof(T)));\
+  if(X==NULL) \
+    { \
+      fprintf(stderr,"failed to malloc\n");\
+        exit(0);\
+    } 
+# define REALLOCATE(X,T,S) \
+  X=(T *)realloc(X,(unsigned) ((S)*sizeof(T))); \
+  if(X==NULL) \
+    { \
+       fprintf(stderr,"failed to realloc\n"); \
+       exit(0); \
+    }
+# endif /* ALLOCATE */
+
+example () {
+
+  float *fred;
+  int Nfred, NFRED, i;
+
+  NFRED = 100;
+  ALLOCATE (fred, float, NFRED);
+
+  for (i = 0; fscanf (stdin, "%f", &fred[i]) != EOF; i++) {
+    if (i == NFRED - 1) {
+      NFRED += 100;
+      REALLOCATE (fred, float, NFRED);
+    }
+  }
+  Nfred = i;
+  REALLOCATE (fred, float, MAX (Nfred, 1));
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/arguments.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/arguments.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/arguments.txt	(revision 22322)
@@ -0,0 +1,63 @@
+# include <stdio.h>
+
+int get_argument (int argc, char **argv, char *arg) {
+
+  int i;
+
+  for (i = 0; i < argc; i++) {
+    if (!strcmp(argv[i], arg))
+      return (i);
+  }
+  
+  return ((int) NULL);
+}
+
+int remove_argument (int N, int *argc, char **argv) {
+
+  int i;
+
+  if ((N != (int) NULL) && (N != 0)) {
+    (*argc)--;
+    for (i = N; i < *argc; i++) {
+      argv[i] = argv[i+1];
+    }
+  }
+
+  return (N);
+    
+}
+
+main (int argc, char **argv) {
+
+  int VERBOSE;
+  float size;
+
+  /* example of an optional argument */
+  VERBOSE = FALSE;
+  if (N = get_argument (argc, argv, "-v")) {
+    remove_argument (i, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  /* one way to do an required argument */
+  size = -1;
+  if (N = get_argument (argc, argv, "-s")) {
+    remove_argument (i, &argc, argv);
+    size = atof (argv[N]);
+    remove_argument (i, &argc, argv);
+  }
+  if (size < 0) {
+    fprintf (stderr, "USAGE: program (file) -s (size) \n");
+    exit (1);
+  }
+
+  /* another way to demand an argument */
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: program (file) -s (size) \n");
+    exit (1);
+  }
+
+}
+
+
+   
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/compiling.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/compiling.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/compiling.txt	(revision 22322)
@@ -0,0 +1,24 @@
+
+There are a few steps needed to compile spicam.  First, untar the
+ohana.tgz file.  Edit the file Configure in the top-level directory.
+ROOT should be the path of the top-level directory.  There are some
+destination directories, places where compiled things will go.  there
+are also two lines for the X library and include directories, though
+spicam does not require this information.  
+
+There are a couple of libraries that need to be compiled to make
+spicam.  first,  type "make install.ohana".  this will compile a set
+of miscellaneous libraries and install the file libohana.a in your
+LDIR (defined in Configure).  Next, type "make install.fits".  this
+will compile the FITS library and install libFITS.a in LDIR (note that
+this is not the Goddard fitsio package...).  Next, you need readline,
+so if you don't have it already, type "make install.readline".  same
+thing.  
+
+finally, you are ready to compile spicam.  type "make install.spicam"
+and it should compile OK.  
+
+good luck!
+
+gene
+ 
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/development.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/development.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/development.txt	(revision 22322)
@@ -0,0 +1,95 @@
+
+This document discusses the Elixir / Ohana development environment.
+The Elixir Ohana components are now maintained under CVS for software
+version control.  This document discusses the motivations for this
+migration and the interaction between the developers and the CVS
+system.  It also discusses the use of the Elixir Bugzilla for problem
+reporting and feature requests.
+
+Background
+
+Previously, the Ohana system used RCS as its version control
+method. RCS is simple and easy, but has some disadvantages that make
+it difficult to use as the Elixir development continues forward in a
+wider context.  First, RCS is not network friendly: it is difficult to
+use when developers are located at different sites working on
+different timescales.  Second, it while it does a good job of tracking
+changes to individual files (revisions), it does a poor job of
+connecting those changes together across a larger software
+collection.  In particular, it does not have the ability to define a
+single collection of revisions of a group of files as a single
+software release.  CVS has been widely used for large software
+development projects and solves both of these problems well.
+
+Ohana Organization & Version Control
+
+The Ohana software system consists of many different libraries,
+programs, and scripts in perl, csh, and tcl.  These are all located in
+the Ohana source tree, with different components placed in different
+directories in the src directory.  For example, the Ohana FITS library
+package is located in the directory src/libfits, while the imclean
+program is in the src/imclean directory.  Some additional groups of
+programs are packaged together: The raw image / detrend image database
+tools and related programs are located together in the src/imregister
+directory, while a collection of small programs (consisting of
+individual C files) are located together in the src/misc directory.
+The scripts are grouped together in the src/shell, src/tcl, src/perl
+directories.  Until the introduction of CVS, only the imregister and
+misc collection of programs and the scripts were rigorously tracked
+under RCS.  The other packages were only tracked informally.  CVS
+allows us to use the existing RCS versions of these files, and
+continue the numbering sequence from where they left off.  In
+addition, CVS allows us to tag a snapshot of the revisions for an
+entire software collections with labels which can be used as
+identifiers within the software.  To this end, the software modules
+will be tagged when new releases of those packages are made.
+
+CVS Interactions: Development vs Release
+
+Elixir and Ohana are at a stage of development that it is necessary to
+separate the development and production software.  This has been done
+in the past by designating a developement directory tree
+(/data/elixir2/srcdir) and a binary distribution tree (/apps/elixir).
+Development could proceed within the srcdir tree, with software builds
+going to the local binary directories (srcdir/ohana/bin, etc).  These
+would not impact the production version of the software until the
+developer was ready to push the new version to the development tree.
+This method is sufficient when one or two developers work on the
+software and coordinate the distribution of the new software.  It does
+not allow multiple developers to work on new versions of the software
+without releasing that software until it is ready.  For example,
+consider two developers, jcc & eugene.  jcc is working on improving
+the astrometry tool, gastro, and has made some progres, but does not
+have a tested, working version yet.  Meanwhile, eugene has found and
+repaired a critical bug in the program imsearch.  In order for eugene
+to push the fix to the production version of the software, he would
+need to coordinate carefully with jcc.  jcc would need to remove the
+new version of gastro and replace it with the known working version
+for the moment when eugene pushes the new version of imsearch to the
+production system, then revert back.  
+
+Under the CVS system, each developer maintains their own version of
+the software tree.  So, for example, jcc would have a local copy
+(perhaps in his home directory) of the tree (or even a portion), while
+eugene would have a different version in his own location.  They can
+work and develop the code independently, publishing changes when
+needed to the repository (the details of these commands will be
+discussed later).  When the developer is satisfied with the changes to
+a packages (say, imclean) and feels it is ready for release to the
+public, he then labels the new release with an appropriate tag.  He
+then goes to the production version of the source tree (ie,
+/data/elixir2/srcdir) and checks out the labeled version.  He then
+compiles it and then pushes the changes to the production binary
+tree.  Note that the same is true of the configuration data saved in
+/data/elixir2/srcdir/config as well as the programs.  
+
+User Commands
+
+cvs co ohana (checkout the complete development tree)
+cvs update
+cvs commit
+cvs add
+cvs remove
+
+Bugzilla
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/dvo2.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/dvo2.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/dvo2.txt	(revision 22322)
@@ -0,0 +1,92 @@
+
+DVO-2 upgrades the DVO concepts to meet the needs of Pan-STARRS.  The
+main constraints that Pan-STARRS has that DVO-1 cannot satisfy are:
+
+- throughput  : upload ~1e6 stars per image, allowing 5 seconds
+- precision   : DVO tables have a restricted number of bits for some
+	        entries which need to be extended for Pan-STARRS
+- flexibility : DVO uses a rigid concept for the sky layout 
+- parallel    : DVO interfaces are poorly designed for parallel I/O
+
+
+Here are the code changes I envision to get from DVO to DVO-2:
+
+- clean the elixir code organization to unify as many DVO things as
+  possible under a single library used by the related programs.
+
+  * this change would be somewhat productive for reducing the number
+    of APIs and generally clarifying the scope of the existing DVO
+    code. 
+
+- add the concept of the mosaic image which groups a set of chips
+  together.  this will include the concept of astrometric information
+  about the focal plane independent of the individual chips
+
+  * this particular change can be a stop gap to get me working with
+    mosaic astrometry within the DVO-1 framework.  Other changes,
+    listed below, will require a variety of fairly fundamental
+    changes.
+
+  * there is no additional cost to adding the mosaic files to the
+    current system, except that 'addstar' requires NSTARS > 0 (or at
+    least NASTRO > 0).  Everything else will work fine with images
+    that have distortion in them (already demonstrated in the past)
+    and the new work on coordops.c makes reverse lookup of RD_to_XY
+    accurate for Npolyterms > 1.
+
+  * chips within a mosaic framework need to have a matched mosaic
+    image.  the coordinates of the mosaic need to be registered
+    somehow with coordops (as a static entry?  not very robust, but an
+    option.  As an implied coords[0], coords[1] passed to the
+    functions?  this can be merged with the existing definitions by
+    only requiring it for entries with one ctype, but not for another
+    cartesian polynomial term (WRP vs PLY? this would make PLY mean
+    Cartesian, not Zenithal).  This is probably safe since only LONEOS
+    data has used the PLY terms in the past.  on the other hand, why
+    break it.  I could use the term PLY for the mosaic term and WRP vs
+    DIS for the two cartesian concepts.  Not very good naming.
+    Another point is that the old LONOES code uses the older style
+    tables, and probably need to be translated anyway.  I could just
+    define a loneos conversion function which converts PLY to DIS and
+    fixes the data format... (this would also let me remove the old
+    table entries from 'loneos.h', or maybe keep them there and move
+    the loneos.h file to a better name.  dvo.h?  elixir.h? 
+
+    - needed functions:
+
+      * int FindMosaicForImage (Image *images, int entry) 
+
+	returns the matching DIS entry for the given WRP entry
+	(matches by time and photcode? would need to define mosaic
+	photcodes that match the chip photcodes.  define an instrument
+	(camera) code in the Image entry?  does not exist at the
+	moment, but would need to be added (space exists in dummy[20],
+	which is surprisingly unused).
+
+- skydb library APIs : we need a collection of functions to define and
+  work with the sky tiling pattern.  These APIs need to specify things
+  like "given RA,DEC, find all overlapping tables"
+
+- convert all tables to FITS tables (currently only average/measure
+  tables are FITS).  this includes migrating the photcode and other
+  external photometry / astrometry tables into the single database
+  repository, rather than having a global table.
+
+- update the average / measure tables to include enough dynamic range.
+
+- place all tables, including average / measure under the FITS db
+  autocoder 
+
+- add proper motion velocity vectors to the average parameters
+
+- add the concept of orphans as a separate table in addition to the
+  measures
+
+- remove any average mag from the average table / place all average
+  magnitudes in their own table (ie, equivalent to secfilt, but with a
+  new name)
+
+- define a client / server interaction.  each server runs on a
+  specific host, associated with specific data tables through the
+  sky.db tables.  servers on a specific host are only responsible for
+  their own data
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/dvotools.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/dvotools.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/dvotools.txt	(revision 22322)
@@ -0,0 +1,48 @@
+
+The number of DVO support programs probably needs to expand.  I am
+specifically considering the types of functions I need to perform the
+photometric and astrometric calibrations needed by Pan-STARRS, ie the
+AP Survey.
+
+dvo DB manipulations:
+
+  - create a subset of a database (restrict by magnitude limits, time, 
+    region, etc)
+  - re-organize the table structure using a new SkyTable file
+  - remove the automatic extension of the secfilt table; make a mode to 
+    add a new secondary filter
+
+dvo DB organization mods:
+
+  - drop the primary photcodes and put all primaries in the 'secfilt' table
+  - construct a FITS table for the photcodes
+  - tables for the camera and filter data which is currently in the config
+  - tools to update the photcode table
+
+addstar : need a few additional new variations on the addstar concept
+
+  - load2mass : the raw 2MASS format is so broken, I needed to create
+  a special stand-alone program to load the 2MASS catalog data to a
+  given DVO database.
+
+  - sedstar : this program uses an existing DVO database, and fits the
+    (calibrated) stellar photometry to a set of stellar photometry
+    models.  One goal is to predict magnitudes in other filters from
+    the observed data.  Another goal is to determine the fundamental
+    stellar parameters (eg, Temp, Av, logg, distance).  Another goal
+    is to determine the stellar locus coordinates for use in other
+    fitting functions.
+
+    output magnitudes should be in photcodes of DEP type? or SED type?
+
+    
+relastro : the basic concept of this program is fairly clear, but we
+  need to work out the details a bit.  The program should determine
+  the image astrometry terms, the average stellar coordinates, and
+  things like proper-motion and parallax.  It needs to include both
+  the raw astrometry from the images as well as (high-quality)
+  external reference sources.  Dave Monet thinks the initial
+  conditions (errors and systematics in the external catalogs) drive
+  the solutions too strongly.  He is exploring doing direct
+  (non-iterative) solutions using the psSparse tools.  I'm not totally
+  convinced yet...
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/elixir-cvs.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/elixir-cvs.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/elixir-cvs.txt	(revision 22322)
@@ -0,0 +1,181 @@
+
+Proposal for Elixir/Ohana Software Version Control
+
+Current Practice
+
+- RCS on each file
+- source code in /data/elixir2/srcdir/ohana
+- some scripts (mana/status/dimm?) in /data/elixir2/srcdir/config/mana
+- configuration information in /data/elixir2/srcdir/config
+- software compiled in place
+- installed in /apps/elixir/bin, /apps/elixir/config
+
+Proposal
+
+- Use CVS to control software versions
+- Identify software releases, tag with CVS release tags
+- use /data/elixir2/srcdir/ohana as development site (jcc)
+- use /cfht/src/eos/
+
+----
+
+work to be done to finish CFHT elixir conversion to CVS:
+
+- verify perl & shell scripts are current
+- verify config files are current
+- convert to opihi.v2:
+  - test mana scripts under opihi.v2
+  - move 'status' scripts to 'dvo'
+- define a complete set of packages for release
+
+---
+
+In order to convert the operational CFHT version of Elixir / Ohana to
+the CVS-controlled version, we need to do several things:
+
+1) merge the changes between the CFHT and IfA trees.
+
+2) move the CFHT version of Ohana to the new opihi.v2 system from the
+   opihi.v1 system:
+   - move to the name 'dvo' rather than 'status'
+   - use the v2 version of mana
+
+3) define the Elixir / Ohana packages and tag them with a starting
+   release name 
+
+4) define an initial distribution for Elixir / Ohana using the
+   complete set of released packages.
+
+I have been working to unify the CFHT and IfA Elixir / Ohana trees.  I
+have imported all of the CFHT version of the entries in the config
+directories and the CFHT versions of the shell and perl scripts.  
+
+I have checked on the use of the 'status' program within the Elixir /
+Ohana scripts.  I have modified sp_plot and el_plot to use 'dvo'
+instead.  I have also modified elixir.photreport.  
+
+---
+
+Working with Elixir / Ohana and CVS:
+
+The CVS repositories allows the developer to work on a copy of the
+Ohana software, make changes to their copy of the code, test the
+changes within their own version of the Elixir configuration system,
+share the changes with other developers, and freeze a tested, working
+snapshot of portions of the software.
+
+The Ohana software tree is divided into packages, each possibly
+containing a single program, a group of related programs, or a C
+library.  Within the Ohana software version control system, each of
+these packages should be released as a single entity.  Changes may be
+made to any of the elements which make up the package.  The new
+changes should be tested so that the developer is confident that the
+new version of the software may be safely introduced into the
+production system.  At this point, the entire package is tagged with
+an identifying name and the new release of the package may be loaded
+into the production tree.
+
+In the following discussion, we illustrate the process of creating a
+new release of a package within Elixir. 
+
+First, check out the Ohana tree:
+
+set CVSROOT to username@machine:/data/elixir2/srcdir/cvs where
+'username' is your user ID and machine is one of the CFHT linux
+machines which mount /data/elixir2/srcdir.  
+
+check out your copy of the software tree:
+
+ cvs co ohana
+
+you may also check out specific releases of packages with the command:
+ cvs co -r TAG PACKAGE  eg:
+ cvs co -r libfits-1-0 libfits
+
+this version should be executed from the same directory level as the
+cvs co ohana command; each package is placed in the directory
+ohana/src/PACKAGE.
+
+The entire ohana package may be compiled from the ohana directory
+with:
+
+> configure
+> make
+> make install
+
+Make edits, changes, etc to the programs in some package.  For
+example, you might edit the .c files in the directory
+ohana/src/gastro/src.  Occasionally publish your changes to the CVS
+repository:
+
+cvs update (check for changes by other developers)
+cvs commit [filename] (publish the changes)
+
+also:
+
+cvs add (filename)  (add a new file to the repository).  
+
+Once the changes have been made, make sure to compile and test your
+copy of the program.  If you are acting as a developer, you must have
+your version of the ohana/bin/ARCH directory in your path (before the
+production installation path, /apps/elixir/bin).  Once the code has
+been tested, and the package is ready for release, commit all of your
+changes to the repository (cvs commit).  Now you may tag the new
+release of the package.  Within the top-level directory of the
+package, give the command:
+
+cvs tag -c TAG 
+
+where TAG is the name of this release.  Within the Ohana system,
+packages are generally tagged with names of the form PACKAGE-N-M where
+N and M are major and minor version numbers.  For example, libfits has
+tag names starting from libfits-1-0.  If the changes are minor, and
+don't affect interaction with other packages, then it is only
+necessary to change the minor version number.  If the new package
+forces changes on other packages, it is best to change the major
+version as well.  Major version changes should be introduced only with
+discussion among the affected parties!   It is possible to check the
+currenly assigned tags for a package with the command:
+cvs status -v (filename) 
+where (filename) is some file that is part of the package.  A
+convenient file to use is the Makefile for the package.  Assigning the
+tag affects the repository directly (there is no need to run 'commit'
+afterwards).  If you have outstanding changes, the -c option will
+force CVS to complain, so you will not tag different versions of the
+files from what you expect.
+
+Once the tag has been assigned, the new version of the package may be
+introduced to the production system. This is a two step process:
+first the new source tree is placed in the appropriate location and
+compiled.  Then the new binaries are pushed to their final locations.
+There are two options for installing the new package source code.
+First, you may make a new elixir distribution.  To do this, go into
+the ohana/etc directory, and edit the distributions.txt file.  This
+defines the package releases which make up a complete distribution.
+There are entries for the ohana/elixir distributions, mana
+distributions, etc.  Make a new distribution version, update the
+appropriate package, and save the file.  This distribution (or any
+other older distribution) may now be constructed by executing the
+command 'mkdist (distribution) (version)', for example:
+mkdist elixir 1.0
+This command extracts from the CVS repository all of the package
+versions which are specified to be part of the given distribution
+version.  These are placed in a single directory, along with the ohana
+infrastructure pieces, with the directory name matching the package
+name and version number.  This new directory may be moved or copied to
+the official production source code location
+(/data/elixir2/srcdir/ohana).  The entire tree may then be compiled.  
+
+An alternative to the above is to checkout only the new package
+version.  To do this, go to the directory containing the production
+code (ie, /data/elixir2/srcdir).  Perform a checkout of the specific
+tagged release of the package of interest:
+cvs co -r TAG PACKAGE (ie, cvs co -r libfits-1-0 libfits).  This will
+replace the old version of the package with the new version.  Now the
+software may be recompiled from the ohana directory. 
+
+Once a new version of the software has been compiled, it may be pushed
+to the production binary directory.  From the /data/elixir2/srcdir
+directory, issue the command 'make install' (make install-test will
+show what programs are new).
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/exclude
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/exclude	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/exclude	(revision 22322)
@@ -0,0 +1,4 @@
+find ohana -name "*.o" -print > exclude.files 
+find ohana -name "bin" -print >> exclude.files
+find ohana -name "*.a" -print >> exclude.files
+tar cvfX - exclude.files ohana | gzip > ohana.tgz.8.97 
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/exclude.list
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/exclude.list	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/exclude.list	(revision 22322)
@@ -0,0 +1,19 @@
+Configure
+bin
+*.sol*
+*.sid*
+*.linux*
+*.lin64*
+*.o
+*.a
+*.so
+*~
+#*
+.mana
+.status
+.dvo
+RCS
+lib/sol
+lib/sid
+lib/linux
+lib/lin64
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/gnu.license
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/gnu.license	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/gnu.license	(revision 22322)
@@ -0,0 +1,344 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/graphics.info
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/graphics.info	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/graphics.info	(revision 22322)
@@ -0,0 +1,1595 @@
+This is Info file graphics.info, produced by Makeinfo-1.55 from the
+input file graphics.texi.
+
+   This file documents GNU graphics.
+
+   Copyright (C) 1989 Free Software Foundation, Inc.
+
+
+File: graphics.info,  Node: Top,  Up: (dir)
+
+* Menu:
+
+* introduction::         About the GNU graphics utilities
+* graph examples::       Using Graph to create plot files from data
+* graph invocation::     Command line options for Graph.
+* plot2ps examples::     converting plot files to PostScript
+* plot2ps invocation::   Plot2ps Command Line Options
+* xplot::                a plot file previewer for X11
+* xplot invocation::     xplot command line options and X11 resources
+* plot2fig examples::    converting plot files to Fig graphics editor format
+* plot2fig invocation::  command line options for plot2fig
+* plot2tek invocation::  Tektronix 4010 output
+* latex example::        Including a Figure in an Article
+* psfig::                Options to `psfig' for Including Figures
+* atend.pl::             A perl script for use with psfig
+* dvips::                How to Get `dvips'
+* idraw::                How to Get `idraw'
+* ghostscript::          an X11 PostScript interpreter/previewer
+* libps::                The Library of Plot Functions for PostScript Output
+* files::                The Plot File Format
+* Acknowledgements::     The Contributors
+* Function Index::       An Index to the libps Functions
+
+
+File: graphics.info,  Node: introduction,  Next: graph examples,  Up: Top
+
+The GNU Graphics Utilities
+**************************
+
+   The GNU graphics utilities are a set of programs for plotting
+scientific data.  The program `graph' reads data files and writes a
+stream of plotting commands in a device independent format refered to
+below as a GNU plot file.  The remaining programs provide support for
+displaying GNU plot files on tektronix 4010, PostScript (TM)*, and X
+window system compatible output devices.
+
+   `graph' reads both ascii and binary data files and writes a plot
+file with or without axes and labels.  You can specify labels and ranges
+for the axes, and you can set the sizes and position of the plot on the
+page.  Each invocation of graph produces a plot with single set of axes
+and data.  You can place an arbitrary number of plots on the page by
+concatenating the plot output of several invocations.
+
+   `plot2ps' is a utility for converting plot files into PostScript.
+The `plot2ps' utility reads plotting commands from named files or the
+standard input and writes PostScript to the standard output.  You can
+then print the PostScript output on a printer, or edit it using the
+`idraw' graphics editor.  You can also easily include the output in
+LaTeX documents using `dvips' and the LaTeX command `psfig'.
+
+   Why is this useful? The plot file format is a common standard on un*x
+systems.  To produces figures for publication, you might need to take
+data sets, and produce labeled figures from them.  This can be done
+using `graph', `plot2ps' and the `idraw' editor.  You can also include
+these figures in LaTeX documents using the `dvips' utility.  All of
+these utilities, as well as the plot file format and plot library, are
+discussed in the following sections.
+
+   This documentation is under revision.  Any comments, suggestions, or
+additions would greatly benifit GNU users.  Please mail them to either
+`bug-gnu-utils@prep.ai.mit.edu' or `Rich@Rice.edu'.
+
+   * PostScript is a trademark of Adobe Systems Incorporated.
+
+
+File: graphics.info,  Node: graph examples,  Next: graph invocation,  Prev: introduction,  Up: Top
+
+`graph' Examples
+================
+
+   Each invocation of `graph' plots data read from stdin or named files
+togeather in a single plot with or without axes and labels.  The
+following sections show common usage of `graph'.
+
+* Menu:
+
+* Invocation::		Simple examples using graph
+* Input Files::		What the input looks like
+* Overlaying::		Combining several data sets in one plot
+* Multiple Plots::	Multiple plots on one page
+* Binary Data::		Reading other data formats
+
+
+File: graphics.info,  Node: Invocation,  Next: Input Files,  Up: graph examples
+
+Simple examples using graph
+===========================
+
+   By default, `graph' reads ascii data from the standard input or
+files specified on the command line.  `graph' reads pairs of values, x
+and y coordinates:
+
+     0.0  0.0
+     1.0  0.2
+     2.0  0.0
+     3.0  0.4
+     4.0  0.2
+     5.0  0.6
+
+   To plot this data, you might use
+
+     graph < ascii_data_file |plot
+
+   where ascii_data_file could contain data similar to the above
+example.  You can replace the command `plot' with `plot2tek' if you have
+a tektronix 4010 compatible graphics terminal, `plot2ps' if you have a
+postscript compatible printer or previewer, or `xplot' if you have an X
+window system display.
+
+   Note that `graph' is commonly supplied with some operating systems.
+If so, some confusion may arise if the system supplied version is
+executed mistakenly.  On unix systems, you can determine which version
+you invoke by typing the command `which graph', which prints the file
+name of the version you invoke by default.
+
+   To reduce the change the size of the plot and position it in the
+middle of the display, you could use
+
+     graph -h .4 -w .4 -r .2 -u .2 < ascii_data_file |plot
+
+   where `h' and `w' are the height and width of the plot and `r' and
+`u' indicate how far up and to the right the plot is positioned.
+
+   You can put symbols at each data point using
+
+     graph -S 2 .01 < ascii_data_file |plot
+
+   where 2 indicates which symbol to plot, and .01 indicates it's size.
+
+   You can choose the type of line draw on each curve:
+
+     graph -m 2 < ascii_data_file |plot
+
+   where 2 indicates what kind of line connects the data points.
+
+
+File: graphics.info,  Node: Input Files,  Next: Overlaying,  Prev: Invocation,  Up: graph examples
+
+The format of input to `graph'
+==============================
+
+   As mentioned above, by default `graph' reads ascii pairs of values,
+x and y coordinates, from the standard input or files specified on the
+command line.  Optional labels may be placed after each coordinate in
+the data file.  The label ends at the end of the line:
+
+     3.0  0.4 this is a label for point (3.0, 0.4).
+
+   The label must be enclosed in double quotes if it would otherwise be
+appear to be a coordinate:
+
+     3.0  0.4 "5.0 looks like a value."
+
+   You can use the `-b' to break lines after each label in the input.
+Use a pair of empty double quotes if you need to break a line, but do
+not need a label.
+
+     0.0  0.0
+     2.0  0.0
+     1.0  0.2
+     ""
+     0.0  0.1
+     2.0  0.2
+     4.0  0.3
+
+   You can also break cuves using `-M' option to break lines whenever
+the abscissal values between successive pairs of points decrease.  When
+using `-M', each continuous curve has monotonicly increasing abscissal
+values.
+
+     0.0  0.0 first data set
+     2.0  0.0
+     4.0  0.2
+     0.0  0.1 second data set
+     2.0  0.2
+     4.0  0.3
+
+   `graph' will automaticly generate abscissal values for you if you
+specify the `-a' option.  Only ordinate values are given in the data,
+and the data is then assumed to be equally sampled along the abscissa.
+The values following `-a' on the command line specify the sampling
+interval and the abscissal value of the first data point.
+
+     0.0
+     0.1
+     0.2 label for point (2.0, 0.2)
+     0.3
+     0.2
+     0.3
+
+
+File: graphics.info,  Node: Overlaying,  Next: Multiple Plots,  Prev: Input Files,  Up: graph examples
+
+Combining several data sets in one plot
+=======================================
+
+   There are cases where you will want to superimpose several data sets
+or several plots on top of each other.  If for example, the data sets
+are in seperate files, you can specify each by it's name on the command
+line.  Since `graph' reads the standard input only if no files are
+named on the command line, you must add the name `--' if you want
+`graph' to read the standard input as well.
+
+     graph data-file-one data-file-two data-file-three |plot
+
+   For comparison sake, you might wish to distinguish the data in one
+set from another either by using different symbols at each point or by
+distinguishing the type of line draw.  You can do this by preceeding
+each file name with options affecting the synbol or line style used to
+plot that data.
+
+     graph -S 1 data-file-one -S 3 data-file-two -S 4 data-file-three |plot
+     
+     or
+     
+     graph -m 1 data-file-one -m 3 data-file-two -S 4 data-file-three |plot
+
+   If you need to superimpose several data sets, but must invoke graph
+seperately for each, you will have to specify the limits of the axes.
+
+     graph -x 0 100 -y -3 3 -S 3 -m -1 < ascii_data_file_1 >> plot_file
+
+   where `-x 0 100' specifies the limits on the x axis, `-y -3 3'
+specifies the limits on the y axis, `-S 2' specifies a box to be drawn
+at each point, and `-m -1' specifies that no line is to be draw
+connecting the points.  You can overlay a second data set on the first
+by using:
+
+     graph -s -g 0 -x 0 100 -y -3 3 -m 0 < ascii_data_file_1 >> plot_file
+
+   where `-s' avoids erasing the page, `-g 0' avoids drawing the axis,
+tick marks and labels which were drawn previously, and `-m 0' specifies
+that solid lines are draw connecting the points.
+
+
+File: graphics.info,  Node: Multiple Plots,  Next: Binary Data,  Prev: Overlaying,  Up: graph examples
+
+How to put multiple plots on one page
+=====================================
+
+   The command
+     graph -h .4 -w .4 -r .1 -u .1 < ascii_data_file_1 >  plot_file
+
+   will put a single box containing the plot in the lower left hand
+quarter of the page.  You can add another plot to the upper left hand
+corner of the page using the command
+
+     graph -s -h .4 -w .4 -r .1 -u .6 < ascii_data_file_2 >> plot_file
+
+   Be sure you use the `-s' option so the the first plot isn't erased.
+
+   Likewise you can add plots to the right hand side of the page using
+
+     graph -s -h .4 -w .4 -r .6 -u .1 < ascii_data_file_3 >> plot_file
+     graph -s -h .4 -w .4 -r .6 -u .6 < ascii_data_file_4 >> plot_file
+
+   The tick marks can be moved inside the box and labels moved to the
+opposite sides using
+
+     graph -T -.005 < ascii_data_file >> plot_file
+
+
+File: graphics.info,  Node: Binary Data,  Prev: Multiple Plots,  Up: graph examples
+
+Reading other data formats
+==========================
+
+   `graph' will read binary data in integer, short integer, float, and
+double float format when you use the `-d' option followed by `f', or
+`d', respectively.  There are two advantage to using binary data: 1)
+`graph' runs significantly faster because the computational overhead
+for converting data from ascii to binary is eliminated, and 2) the
+input files can be significantly smaller than the ascii format would
+be.  Double float is the fastest format to read, while short integer is
+the most space conservative.  If you have very large data sets, using a
+binary format can reduce storage and runtime costs.
+
+   For example, you can create double float data as output from C
+language programs:
+
+     #include <stdio.h>
+     void write_point (x, y)
+       double x, y;
+     {
+       fwrite(&x, sizeof (double), 1, stdout);
+       fwrite(&y, sizeof (double), 1, stdout);
+     }
+
+   You can then plot data written this way using:
+
+     graph -d d <datafile >plotfile
+
+
+File: graphics.info,  Node: graph invocation,  Next: plot2ps examples,  Prev: graph examples,  Up: Top
+
+Graph Command Line Options
+==========================
+
+   The following table describes each of the command line arguments to
+graph.  Each option which takes an argument is followed by the type and
+default values of the argument in parentheses.
+
+`-a [STEP_SIZE [LOWER_LIMIT]]'
+`+auto-abscissa [STEP_SIZE [LOWER_LIMIT]]'
+     (floats, defaults 1 and 0) Automaticly generate abscissa (x)
+     values.  This option specifies that the data contains only
+     ordinate (y) values.  The difference between successive x values
+     will be STEP_SIZE, and the first x value will be LOWER_LIMIT.  To
+     return to reading abscissal values from the input you can specify
+     `-a 0', which disables automatic generation of the abscissa and
+     returns STEP_SIZE and LOWER_LIMIT to their default values.
+
+`-b'
+`+break-on-labels'
+     Assumes multiple data sets are in the data file, and the data sets
+     are separated by a label.  The default is don't break on labels.
+
+`-M'
+`+break-non-monotone'
+     When successive abscissa (x) values decrease, a separate data set
+     is assumed.  This allows multiple data sets in each file.  Similar
+     to `-b'.
+
+`-f SIZE'
+`+fontsize SIZE'
+     Specify the size of the desired font as SIZE points.  Not all
+     display devices will honor this command.
+
+`-N X|Y'
+`+no-label X|Y'
+     By default, values at each tick mark are labeled beside the axis.
+     This option removes the labeling of the tick marks on the specified
+     axis.
+
+`-R'
+`+dont-round-to-next-tick'
+     By default, the ends of the axes are extended to the next tick
+     mark.  This option prohibits rounding the limits of the axes to
+     the next tick mark.
+
+`-c STRING'
+`+point-label STRING'
+     This option defines STRING as the default label for each point.
+     Any label in the input will override this default.
+
+`-S SYMBOL_NUMBER [SYMBOL_SIZE]'
+`+symbol SYMBOL_NUMBER [SYMBOL_SIZE]'
+     (integer and float, defaults -1 and 0.01) Draw a symbol at each
+     point in the data. SYMBOL_NUMBER specifies the shape of the symbol
+     according to the following table and SYMBOL_SIZE specifies the
+     fractional size of the symbol with respect to the height and width
+     of the plot.  Note that you can specify symbols to be drawn
+     without any line connecting them by specifying the option `-m -1'.
+    -1 no symbol at all
+    0 plus sign (+)
+    1 cross (x)
+    2 star (*)
+    3 box
+    4 diamond
+    5 circle
+`-T TICK_SIZE'
+`+ticksize TICK_SIZE'
+     (float, default .01) TICK_SIZE is the fractional size of the tick
+     marks on each axis.  A value of 1.0 produces tick marks on the x
+     (y) axis whose length is equal to the width (height) of the plot.
+
+`-X X_LABEL'
+`+xtitle X_LABEL'
+     (string, default blank) X_LABEL is a label printed below the x
+     axis.
+
+`-Y Y_LABEL'
+`+ytitle Y_LABEL'
+     (string, default blank) Y_LABEL is a label printed to the right of
+     the y axis.
+
+`-d DATA-FORMAT'
+`+data-format DATA-FORMAT'
+     This specifies what format the input data is in.  Note labels can
+     be used only in ascii format input files.
+    `a'
+    `A'
+          ascii data
+
+    `i'
+    `I'
+          binary integer data
+
+    `s'
+    `S'
+          binary short integer data
+
+    `f'
+    `F'
+          binary float data
+
+    `d'
+    `D'
+          binary double data
+
+`+debug'
+     Debugging information, including the data read in, is sent to the
+     standard error output.
+
+`-g GRID_STYLE'
+`+grid GRID_STYLE'
+     (integer, default 1) GRID_STYLE specifies the type of box framing
+     the plot and whether grid lines are drawn inside the box.
+    0 no box around plot, no axes, no labels.
+    1 box containing a grid and axes with tick marks and labels.
+    2 box around plot, tick marks around the box and labels.
+    3 box around plot, ticks on left and lower sides only and labels.
+    4 axes intersect at the origin without a box or grid.
+`-h HEIGHT'
+`+height-plot HEIGHT'
+     (float, default 0.8) HEIGHT specifies the fractional height of the
+     plot with respect to the height of the plotting area.  A value of
+     1.0 will produce a box which fills the available area.  Note that
+     the tick marks and labels are outside this area so that values
+     less than 1.0 are generally used.
+
+`-L TOP_LABEL'
+`+toptitle TOP_LABEL'
+     (string, default blank) TOP_LABEL is a label placed above the plot.
+
+`-m LINE_MODE'
+`+linestyle LINE_MODE'
+     (integer, default 0) LINE_MODE specifies the mode (or style) of
+     lines drawn between data points.
+    -1 no line at all
+    0 solid
+    1 dotted
+    2 shortdashed
+    3 dotdashed
+    4 longdashed
+    5 disconnected
+`-r RIGHT'
+`+right-margin-posn RIGHT'
+     (float, default 0.1) Move the plot to the right by a fractional
+     amount RIGHT with respect to the width of the plotting area.  This
+     produces a margin on the left hand side of the plot.  A value of
+     0.5 will produce a margin half the width of the available area.
+     Note that the tick marks and labels are drawn in the margin.
+
+`-u UP'
+`+bottom-margin-posn UP'
+     (float, default 0.1) Move the plot up by a fractional amount UP
+     with respect to the height of the plotting area.  This produces a
+     margin below the plot.  A value of 0.5 will produce a margin half
+     the height of the available area.  Note that the tick marks and
+     labels are drawn in the margin.
+
+`-s'
+`+save-screen'
+     Save the screen. This option prevent graph from erasing the
+     previous contents of the graphics window or device.
+
+`-t'
+`+transpose'
+     Transpose the abscissa and ordinate.  This option causes the axes
+     to be interchanged, and the options which apply to each axis to be
+     applied to the opposite axis.  That is, data is read in as (y, x)
+     pairs and `-x', `-X' and `-lx' apply to the y axis.
+
+`-w WIDTH'
+`+width-plot WIDTH'
+     (float, default 0.8) WIDTH specifies the fractional width of the
+     plot with respect to the width of the plotting area.  A value of
+     1.0 will produce a box which fills the available area.  Note that
+     the tick marks and labels are outside this area, so values less
+     than 1.0 are generally used.
+
+`-x LOWER_LIMIT UPPER_LIMIT'
+`+xlimits LOWER_LIMIT UPPER_LIMIT'
+     (floats) The arguments LOWER_LIMIT and UPPER_LIMIT specify the
+     limits of the x axis. By default the upper and lower limits are
+     taken from the data.  If unspecified the limits of the data are
+     used.
+
+`-y LOWER_LIMIT UPPER_LIMIT'
+`+ylimits LOWER_LIMIT UPPER_LIMIT'
+     These arguments specify the scale and limits of the y axis as for
+     the x axis above.
+
+`-l X|Y'
+`+log-axis X|Y'
+     The argument indicates which axis should be a log axis.  Either
+     one or both x- and y-axes can be specified by using the
+     appropriate letter.  Use XY or YX to specify both.
+
+`+high-byte-first'
+`+low-byte-first'
+     These options force graph to use the specified byte order when
+     writing out the plot file.  By default the byte order is host
+     dependent.
+
+
+File: graphics.info,  Node: plot2ps examples,  Next: plot2ps invocation,  Prev: graph invocation,  Up: Top
+
+Examples Using `plot2ps'
+========================
+
+   To produce a plot of data arranged in ordered pairs of x and y
+coordinates in an ASCII file, you can use:
+
+     graph <asciiDataFile | plot2ps | lpr -Plw
+
+   To create a simple PostScript figure you can use:
+
+     echo 0 0 1 1 2 0 | spline | graph | plot2ps > test.ps
+
+   To edit the plot:
+
+     idraw test.ps
+
+   To use the previewer to look at the plot:
+
+     gs test.ps
+
+
+File: graphics.info,  Node: plot2ps invocation,  Next: xplot,  Prev: plot2ps examples,  Up: Top
+
+Plot2ps Command Line Options
+============================
+
+   `plot2ps' is a relatively simple utility in that there are few
+command line options to choose from.  The plot file format does not
+contain methods for specifying font or font size, so you must specify
+these things with options.  There are no other options for controlling
+the picture.
+
+   The plot file format is machine dependent on the byte order of
+unformatted, signed, two byte integer coordinates contained in plot
+commands.  The `-high-byte-first' or `-low-byte-first' option specifies
+this order explicitly.  `plot2ps' attempts to determine the byte order
+from commands early in the plot file, but the method is heuristic and
+is not foolproof.  Several standard plot sizes specified by the `open'
+command are used to recognize byte order by `plot2ps'.  If these sizes
+are recognized in byte reversed order, `plot2ps' adjusts accordingly.
+These sizes include 504x504, 2048x2048 (versatek plotters), 2100x2100,
+3120x3120(tektronix 4010 terminals) and 4096x4096 (gsi 300 terminals).
+
+   The remaining command line options may be used specify an alternate
+PostScript prologue and to print the licensing information.
+
+   Input plot files names may be specified anywhere on the command line.
+If no file names are specified, or the name `-' is specified, the
+standard input is read for plotting instructions.  Only the font or
+font size options which precede a file name will affect the text for
+that file.
+`-H'
+`+help'
+     The help option prints a summary of command line syntax for
+     `plot2ps', a list of the font names (the standard builtin
+     PostScript fonts), and version, copyright and warranty information.
+     Specifying this options causes plot2ps to ignore files on the
+     standard input.  You can specify a file on the standard input
+     explicitly with the option `-' if you want it to read the standard
+     input as well.
+
+`-v'
+`-V'
+`+version'
+     This option prints version, copyright and warranty information.
+
+`-fontsize SIZE'
+`-f SIZE'
+     The fontsize options specifies the default size in printer's points
+     (1/72 inch) of all text appearing in the plot.  If unspecified, the
+     size defaults to 14 points.
+
+     Some sizes are supported better than others under X windows.  The
+     standard sizes distributed with X windows are 8, 10, 12, 14, 18,
+     and 24 points.  Text at these point sizes will display correctly
+     in the `idraw' editor.  Other font sizes will print correctly on a
+     PostScript device such as the laserwriter, but may not appear at
+     the correct size in the `idraw' editor.
+
+`-font NAME'
+`-fo NAME'
+     The font name option specifies the name of the default font for all
+     text appearing in the plot.  `plot2ps -help' prints a listing of
+     the font names on the standard output.  These names include the
+     available builtin fonts on standard PostScript printers.
+
+`-high-byte-first'
+`-h'
+     The high-byte-first option specifies explicitly that the higher
+     order byte of each signed, two byte integer occurs first in the
+     file.  It disables determination of byte order from the file
+     itself.
+
+`-line-width WIDTH'
+     WIDTH is the width of lines drawn in the plot, and defaults to a
+     value of 0.  A value of 0 will produce the thinest line possible
+     in a device dependent fashion, however this is known to cause
+     problems for older versions of idraw.  The line width is device
+     independent for a positive values of WIDTH.
+
+`-low-byte-first'
+`-l'
+     The low-byte-first option specifies explicitly that the lower order
+     byte of each signed, two byte integer occurs first in the file.  It
+     disables determination of byte order from the file itself.
+
+`-prologue FILENAME'
+`-p FILENAME'
+     The prologue option specifies the name of an alternate PostScript
+     prologue FILENAME to be used in place of the default `idraw'
+     prologue.  The prologue declares procedures used to draw each
+     graphic object.  The default prologue was generously provided by
+     John Interante and is a part of the InterViews distribution,
+     version 2.5.
+
+`-copying'
+`-warranty'
+     The copying and warranty options print a copy of the GNU General
+     Public License on the standard error output.  Included is
+     conditions for copying `plot2ps' and information on the lack of any
+     warranty.
+
+     These conditions do not cover the output of `plot2ps'.  The only
+     conditions imposed on the output are those which come from the
+     prologue that you are using.
+
+`-signed'
+`-unsigned'
+     The signed and unsigned options specify whether coordinates in the
+     plot file are signed.  By convention, coordinates are always
+     signed.  Some plot files do not follow this convention, and you
+     can use the unsigned option to convert those files.
+
+`-bbox'
+     The bbox option specifies that a bounding box comment will be
+     written at the end of the output file.  This information is useful
+     for document preparation systems which determine how to size and
+     place the figure using the bounding box.  See also the atend
+     script.
+
+
+File: graphics.info,  Node: xplot,  Next: xplot invocation,  Prev: plot2ps invocation,  Up: Top
+
+A plot file previewer for X11
+=============================
+
+   `xplot' is a plot file previewer for the X window system.  It reads
+GNU plot commands from its standard input and draws the resulting
+graphics in an X window.
+
+   After xplot reaches the end-of-file on the input, it puts itself in
+the background (forks).  Control returns to the calling program, while
+xplot continues, remaining on screen.
+
+   To exit, click the left mouse button in the xplot window.  Note that
+xplot ignores SIGHUP signals, so you must use another signal to kill
+xplot if necessary.
+
+
+File: graphics.info,  Node: xplot invocation,  Next: Plot2fig Examples,  Prev: xplot,  Up: Top
+
+Xplot Options
+=============
+
+   `xplot' accepts all of the standard X toolkit command line options,
+and the initial geometry specification determines the resolution, with a
+default geometry of 500x500 pixels.
+
+   The following standard X Toolkit command line arguments may be used
+with `xplot':
+`-bg COLOR'
+     This specifies the color to use for the background of the window.
+     The default is WHITE.
+
+`-bd COLOR'
+     This specifies the color to use for the border of the window.  The
+     default is BLACK.
+
+`-bw NUMBER'
+     This specifies the width in pixels of the border surrounding the
+     window.
+
+`-fg COLOR'
+     This specifies the color to use for displaying text.  The default
+     is BLACK.
+
+`-fn FONT'
+     This specifies the font to be used for displaying normal text.  The
+     default is 6X10.
+
+`-rv'
+     This indicates that reverse video should be simulated by swapping
+     the foreground and background colors.
+
+`-geometry GEOMETRY'
+     This specifies the preferred size and position of the plot window.
+
+`-display HOST:DISPLAY'
+     This specifies the X server to contact.
+
+`-xrm RESOURCESTRING'
+     This specifies a toolkit resource property.  See the manual page
+     for xrdb.
+
+   `xplot' uses the athena Command widget in the X Toolkit.  So, it
+understands all of the core resource names and classes as well as:
+`reverseVideo'
+     (class ReverseVideo) Specifies that the foreground and background
+     colors should be reversed.
+
+Example
+-------
+
+   The resources:
+
+     Xplot.font: 6x9
+     Xplot.geometry: 300x300
+
+   will set the font used in the plot window to `6x9' and the size of
+the window to 300 by 300 pixels.
+
+
+File: graphics.info,  Node: Plot2fig Examples,  Next: plot2fig invocation,  Prev: xplot invocation,  Up: Top
+
+Using Plot2fig
+==============
+
+   To create a simple plot file one can use:
+
+     echo 0 0 1 1 2 0 | spline | graph | plot2fig > test.fig
+
+   To edit the plot:
+
+     fig test.fig
+
+   To convert the fig file into dvi code, create a latex file containing
+a document which includes the figure:
+
+     \\documentstyle\[\]{article}
+     \\begin{document}
+     \\input{test}
+     \\end{document}
+
+   Then, run transfig on the figure and latex on the document:
+
+     % transfig -L latex test.fig
+     % make test.tex
+     % latex t.tex
+
+   To edit a plot of data arranged in ordered pairs of x and y
+coordinates in an ascii file, one can use:
+
+     % graph <asciiDataFile | plot2fig >file.fig
+     % fig file.fig
+
+
+File: graphics.info,  Node: plot2fig invocation,  Next: plot2tek invocation,  Prev: plot2fig examples,  Up: Top
+
+A plot file to fig file translator
+==================================
+
+   `plot2fig' reads plotting instructions from the specified input
+files and/or the standard input and produces `Fig' compatible code on
+it's standard output.  This output file can be edited with the fig
+(Facility for Interactive Generation of figures) graphics editor.  The
+output can subsequently be converted to pictex, PostScript, latex, epic,
+eepic, and tpic languages using the `transfig' translator.
+
+   Any unrecognized options on the command line are assumed to be input
+files.  The standard input is read by default only if no other files
+specified on the command line are successfully opened.  A single dash
+(-) on the command line indicates the standard input is to be read.
+Each option is set and each file read in the order they are specified on
+the command line.
+
+   For compatibility with pic2fig, plot2fig ignores leading white space
+in labels.  Labels containing all white space are ignored.
+
+`-fn NAME'
+`-fontname NAME'
+     Default: the default font of the transfig output device.  This
+     option sets the font for all subsequent text to NAME.  Recognized
+     font names are typewriter, modern, italic, bold, and times.  In
+     addition, courier is an alias for typewriter and roman is an alias
+     for times.  Note that the fonts are device dependent.
+
+`-fs SIZE'
+`-fontsize SIZE'
+     Default: 12. This option sets the size of subsequent text to SIZE
+     (in printer's points).
+
+`-h'
+`-high-byte-first'
+     This option specifies that the byte ordering of two byte integers
+     in the input plot file is high byte first.
+
+`-l'
+`-low-byte-first'
+     This option specifies that the byte ordering of two byte integers
+     in the input plot file is low byte first.
+
+`-warranty'
+`-copying'
+     This option prints out the copying conditions and warranty
+     information.
+
+`-signed'
+`-unsigned'
+     Default: signed.  This option specifies whether two byte integers
+     in the input plot file are unsigned or signed.
+
+`-'
+     This option specifies explicitly that the standard input should be
+     read for plotting instructions.
+
+
+File: graphics.info,  Node: plot2tek invocation,  Next: latex example,  Prev: plot2fig invocation,  Up: Top
+
+Tektronix 4010 output
+=====================
+
+`-H'
+`+help'
+     The help option prints a summary of command line syntax, a list of
+     the known font names, and version, copyright and warranty
+     information.  Specifying this options causes plot to ignore the
+     standard input, so you must specify the option `-' if you want it
+     to read the standard input as well.
+
+`-v'
+`-V'
+`+version'
+     This option prints version, copyright and warranty information.
+
+`-f SIZE'
+`+fontsize SIZE'
+     The fontsize options specifies the default size in printer's points
+     (1/72 inch) of all text appearing in the plot.  If unspecified, the
+     size defaults to 14 points.
+
+`+high-byte-first'
+`+low-byte-first'
+     These options force graph to use the specified byte order when
+     writing out the plot file.  By default the byte order is host
+     dependent.
+
+`-F NAME'
+`+fontname NAME'
+     The font name option specifies the name of the default font for all
+     text appearing in the plot.  `plot -help' prints a listing of the
+     font names on the standard output.  These names include the
+     available builtin fonts on standard PostScript printers.
+
+`-h'
+`+high-byte-first'
+     The high-byte-first option specifies explicitly that the higher
+     order byte of each signed, two byte integer occurs first in the
+     file.  It disables determination of byte order from the file
+     itself.
+
+`-l'
+`+low-byte-first'
+     The low-byte-first option specifies explicitly that the lower order
+     byte of each signed, two byte integer occurs first in the file.  It
+     disables determination of byte order from the file itself.
+
+`-p FILENAME'
+`+prologue FILENAME'
+     The prologue option specifies the name of an alternate PostScript
+     prologue FILENAME to be used in place of the default `idraw'
+     prologue.  The prologue declares procedures used to draw each
+     graphic object.  The default prologue was generously provided by
+     John Interante and is a part of the InterViews distribution,
+     version 2.5.
+
+`+copying'
+`+C'
+`+warranty'
+`+W'
+     The copying and warranty options print a copy of the GNU General
+     Public License on the standard error output.  Included is
+     conditions for copying `plot' and information on the lack of any
+     warranty.
+
+`-s'
+`+signed-input'
+`-u'
+`+unsigned-input'
+     The signed and unsigned options specify whether coordinates in the
+     plot file are signed.  By convention, coordinates are always
+     signed.  Some plot files do not follow this convention, and you
+     can use the unsigned option to convert those files.
+
+
+File: graphics.info,  Node: latex example,  Next: psfig,  Prev: plot2tek invocation,  Up: Top
+
+Including a Figure in an Article
+================================
+
+   This is an example of LaTeX code which places the figure generated in
+the previous example in a page of text.
+
+     \documentstyle[]{article}
+     \input{psfig}
+     \begin{document}
+     \title{Title of the article.}
+     \author{The Author's name}
+     \maketitle
+     This is an example of how to include PostScript figures in LaTeX documents.
+     \begin{figure}[h]
+     \centerline{\psfig{figure=test.ps,height=3in}}
+     \caption{Here is a description of the figure which will appear below it.}
+     \end{figure}
+     Note that the above figure was included using dvips.
+     \end{document}
+
+   If the above LaTeX code is contained in a file called `mytext.tex'
+you can use the commands
+
+     latex mytext
+     dvips mytext.dvi >mytext.ps
+     lpr -Plw mytext.ps
+
+   to format and print the example text.
+
+
+File: graphics.info,  Node: psfig,  Next: atend.pl,  Prev: latex example,  Up: Top
+
+Options to `psfig' for Including Figures
+========================================
+
+   `psfig' is a LaTeX command used to insert a PostScript figure into a
+document.
+
+   `psfig' can be used to insert `plot2ps' generated PostScript into a
+LaTeX document.  The placement of the `psfig' command tells LaTeX where
+in the document to place the PostScript, and arguments to the command
+give the name of the file containing the PostScript, and the desired
+size of the figure.  Arguments are separated by commas or blanks, and
+are of the form `KEYWORD=VALUE'.  The following is a list of valid
+arguments for the `psfig' command:
+
+`file=NAME'
+     The file name of the PostScript figure.
+
+`height=SIZE'
+     The height of the figure (eg. 3in).  If you specify only a height
+     or only a width, the width and height are scaled equally.  If you
+     specify both a width and a height the aspect ratio will be
+     affected.
+
+`width=SIZE'
+     The width of the figure (eg. 3in).
+
+`bbllx=COORDINATE'
+     The bounding box lower left-hand x coordinate.  Any PostScript file
+     which conforms to the PostScript Document Structuring Conventions
+     version 2.0 should contain a bounding box information at the head
+     of the file.  `plot2ps' output conforms to the version 2.0
+     conventions so that you should not need to use any of the bounding
+     box options.
+
+`bblly=COORDINATE'
+     The bounding box lower left-hand y coordinate.
+
+`bburx=COORDINATE'
+     The bounding box upper right-hand x coordinate.
+
+`bbury=COORDINATE'
+     The bounding box upper right-hand y coordinate.
+
+`rheight=SIZE'
+     Horizontal space to reserve for the figure.
+
+`rwidth=SIZE'
+     Vertical space to reserve for the figure.
+
+`clip='
+     Clip the figure.  `clip=' is a switch and takes no value, but the
+     `=' must be present.  This option is useful for including
+     PostScript figures which use the size of the clipping path to size
+     themselves.
+
+
+File: graphics.info,  Node: atend.pl,  Next: dvips,  Prev: psfig,  Up: Top
+
+A perl script for use with psfig
+================================
+
+   `atend.pl' is a perl script which moves the bounding box comment
+from the trailer to the header.  Although either is legal, most document
+preparation software, such as `psfig', will only accept bounding box
+comments in the header.  If you use `psfig' and the `-bbox' option
+togeather, run `atend.pl' on the output of `plot2ps' before importing
+the graphics using `psfig'.
+
+   `atend.pl' can be used as a filter:
+
+     echo 0 0 1 1 2 0 | spline | graph >spline.pl
+     plot2ps -bbox |atend.pl - >spline.ps
+
+   Or, `atend.pl' can be used to fix the output file in place:
+
+     echo 0 0 1 1 2 0 | spline | graph |plot2ps -bbox >spline.ps
+     atend.pl spline.ps
+
+
+File: graphics.info,  Node: dvips,  Next: idraw,  Prev: atend.pl,  Up: Top
+
+How to Get `dvips'
+==================
+
+   The `dvips' utility mentioned previously is used convert dvi files
+generated by LaTeX into post-script.  It also has support for inclusion
+of PostScript figures into LaTeX documents.  It is available via
+anonymous ftp from `labrea.stanford.edu' (36.8.0.47).  Look for
+`pub/dvips.tar.Z'.
+
+
+File: graphics.info,  Node: idraw,  Next: ghostscript,  Prev: dvips,  Up: Top
+
+How to Get `idraw'
+==================
+
+   The `idraw' utility mentioned previously is an interactive graphics
+editor which is distributed with InterViews.  InterViews is available
+via anonymous ftp from `interviews.stanford.edu' (36.22.0.175) in the
+file `InterViews/2.5.tar.Z'.
+
+
+File: graphics.info,  Node: ghostscript,  Next: libps,  Prev: idraw,  Up: Top
+
+How to Get `ghostscript'
+========================
+
+   Ghostscript is a previewer which is intended to be compatible with
+the PostScript language.  It supports several output devices including
+the X window system and ega displays.  Version 2.0 is suitable for
+previewing LaTeX documents with imbedded encapsulated PostScript
+figures, the type of PostScript figures generated by plot2ps and idraw.
+It is available via anonymous ftp from `prep.ai.mit.edu' (18.71.0.38)
+- Look for `pub/gnu/ghostscript.tar.Z'.
+
+
+File: graphics.info,  Node: libps,  Next: files,  Prev: ghostscript,  Up: Top
+
+`libps', a Library of Plot Functions
+====================================
+
+   Libps is a library of plot functions for drawing graphic object
+using PostScript.  Before drawing any objects or using any of the other
+functions, a program should call `openpl'.  Before exiting and after
+all other libps calls a program should call `closepl'.
+
+* Menu:
+
+The standard plot library includes:
+
+* arc::          draw an arc
+* circle::       draw a circle
+* closepl::      close the device for output
+* cont::	 continue a line
+* erase::        erase the page
+* label::        print a label
+* line::         draw a line
+* linemod::      change the line mode (style)
+* move::         move to a new coordinate
+* openpl::       open the device for output
+* point::        draw a point
+* space::        define the user's plot space (size)
+
+The extensions provided only in libps to take advantage of PostScript
+features include:
+
+* alabel::       print a vertically or horizontally justified label
+* color::        change the color
+* fill::         fill pattern for closed paths
+* fontname::     set the font name
+* fontsize::     set the font size
+* rotate::       rotate subsequent text
+
+
+File: graphics.info,  Node: alabel,  Next: arc,  Up: libps
+
+`alabel'
+--------
+
+   int `alabel' (char X_JUSTIFY, char Y_JUSTIFY, char *LABEL);
+
+   `alabel' takes three arguments X_JUSTIFY, Y_JUSTIFY, and LABEL and
+places the label according to the x and y axis adjustments specified in
+X_JUSTIFY and Y_JUSTIFY respectively.  X_JUSTIFY is a character
+containing either `l', `c', or `r' for left, center or right justified
+with respect to the current x coordinate.  Y_JUSTIFY is a character
+containing either `b', `c', or `t' for placing the bottom center or top
+of the label even with the current y coordinate.  *LABEL is a string
+containing the label.  The current point is moved to follow the end of
+the text.
+
+   *Note fontname:: on how to change the default font.  *Note
+fontsize:: on how to change the font size.
+
+
+File: graphics.info,  Node: arc,  Next: circle,  Prev: alabel,  Up: libps
+
+`arc'
+-----
+
+   int `arc' (int X, int Y, int X0, int Y0, int X1, int Y1)
+
+   `arc' takes six integer arguments specifying the coordinates of the
+center (X, Y), beginning (X0, Y0), and ending (X1, Y1) of a circular
+arc.  The current point becomes (X, Y).
+
+
+File: graphics.info,  Node: circle,  Next: closepl,  Prev: arc,  Up: libps
+
+`circle'
+--------
+
+   int `circle' (int X, int Y, int R)
+
+   `circle' takes three integer arguments specifying the center (X, Y)
+of the circle and its radius (R).  The current point becomes (X, Y).
+
+
+File: graphics.info,  Node: closepl,  Next: color,  Prev: circle,  Up: libps
+
+`closepl'
+---------
+
+   int `closepl' ()
+
+   `closepl' takes no arguments.  It merely outputs the PostScript
+trailer containing a `showpage' command.
+
+
+File: graphics.info,  Node: color,  Next: cont,  Prev: closepl,  Up: libps
+
+`color'
+-------
+
+   int `color' (int RED, int GREEN, int BLUE);
+
+   `color' sets the foreground color of all the following objects.  The
+arguments RED, GREEN and BLUE indicate the intensity of red, green and
+blue components of the foreground color respectively.  Each is a
+unsigned integer specifying an intensity in the range from 0 to 0xFFFF.
+A value of (0, 0, 0) represents black and a value of (0xFFFF, 0xFFFF,
+0xFFFF) indicates white.
+
+
+File: graphics.info,  Node: cont,  Next: erase,  Prev: color,  Up: libps
+
+`cont'
+------
+
+   int `cont' (int X, int Y)
+
+   `cont' takes two integer arguments specifying the coordinate (X, Y)
+for the continuation of a line.  This draws a line segment from the
+current point to the point (X, Y).  The current point then becomes (X,
+Y).
+
+
+File: graphics.info,  Node: erase,  Next: fill,  Prev: cont,  Up: libps
+
+`erase'
+-------
+
+   int `erase' ()
+
+   `erase' normally erases all the graphics from the display before a
+plot is viewed.  Since we start off with a blank page in PostScript and
+`idraw' this function does nothing.
+
+
+File: graphics.info,  Node: fill,  Next: fontname,  Prev: erase,  Up: libps
+
+`fill'
+------
+
+   int `fill' (int LEVEL);
+
+   `fill' sets the intensity of the filler for closed paths.  The
+argument LEVEL indicates the grey level of the fill pattern.  It's
+value ranges from 1 to 0xFFFF.  A value of 1 represents black and a
+value of 0xFFFF indicates white.  A value of 0 represents no fill, or
+transparent.
+
+
+File: graphics.info,  Node: fontname,  Next: fontsize,  Prev: fill,  Up: libps
+
+`fontname'
+----------
+
+   int `fontname' (char *FONT_NAME);
+
+   `fontname' takes a single string argument, FONT_NAME, specifying the
+name of the font to be used for following text.  The laser writer
+builtin fonts are supported:
+
+             courier-bold
+             courier-boldoblique
+             courier-oblique
+             courier
+             helvetica-bold
+             helvetica-boldoblique
+             helvetica-oblique
+             helvetica
+             symbol
+             times-bold
+             times-bolditalic
+             times-italic
+             times-roman
+
+
+File: graphics.info,  Node: fontsize,  Next: label,  Prev: fontname,  Up: libps
+
+`fontsize'
+----------
+
+   int `fontsize' (int SIZE);
+
+   `fontsize' takes a single integer argument SIZE in printer's points
+(1/72 inch) and sets the font size accordingly.
+
+
+File: graphics.info,  Node: label,  Next: line,  Prev: fontsize,  Up: libps
+
+`label'
+-------
+
+   int `label' (char *S)
+
+   `label' takes a single string argument S and draws the text
+contained in S at the most recently used coordinate in the current
+font.  By default the text is left justified and centered vertically
+with respect to the current coordinate.
+
+
+File: graphics.info,  Node: line,  Next: linemod,  Prev: label,  Up: libps
+
+`line'
+------
+
+   int `line' (int X1, int y1, int X2, int Y2)
+
+   `line' takes four integer arguments specifying the beginning (X1,
+Y1) and ending (X2, Y2) points of a line.  The current point becomes
+(X2, Y2).
+
+   *Note linemod:: for how to specify the style or pattern of line.
+
+
+File: graphics.info,  Node: linemod,  Next: move,  Prev: line,  Up: libps
+
+`linemod'
+---------
+
+   int `linemod' (char *S)
+
+   `linemod' takes a single string argument S containing the name of
+the line style desired.  The names supported are longdashed,
+disconnected, dotdashed, dotted, solid and shortdashed.  These
+correspond to the following sixteen bit patterns:
+
+     solid             --------------------------------
+     longdashed        -------         -------
+     disconnected      -               -
+     dotdashed         -----------  -  -----------  -
+     dotted            - - - - - - - - - - - - - - - -
+     shortdashed       --              --
+
+
+File: graphics.info,  Node: move,  Next: openpl,  Prev: linemod,  Up: libps
+
+`move'
+------
+
+   int `move' (int X, int Y)
+
+   `move' takes two integer arguments specifying the coordinate (X, Y)
+for the beginning of a new line.  This is equivalent to lifting the pen
+on a plotter and moving it to a new position without drawing any line.
+The current point becomes (X, Y).
+
+
+File: graphics.info,  Node: openpl,  Next: point,  Prev: move,  Up: libps
+
+`openpl'
+--------
+
+   int `openpl' ()
+
+   `openpl' normally opens the device.  For PostScript we just print
+out the PostScript prologue.  The following global variables defined in
+`openpl' specify what prologue is written to the output.
+
+   USER_HAS_PROLOGUE is a flag.  If it is non-zero then the open
+routine should output the user specified prologue contained in the file
+specified in the string USERS_PROLOGUE.
+
+   USERS_PROLOGUE is a string containing the file name for any user
+specified PostScript prologue.  This file is a substitute for the
+default prologue.
+
+
+File: graphics.info,  Node: point,  Next: rotate,  Prev: openpl,  Up: libps
+
+`point'
+-------
+
+   int `point' (int X, int Y)
+
+   `point' takes a pair of integer arguments specifying the coordinate
+(X, Y) for a single point.  The current point then becomes (X, Y).
+
+
+File: graphics.info,  Node: rotate,  Next: space,  Prev: point,  Up: libps
+
+`rotate'
+--------
+
+   int `rotate' (int ANGLE);
+
+   `rotate' takes three integer arguments.  The last argument, ANGLE,
+specifies the angle in degrees counter-clockwise from the x
+(horizontal) axis following text.
+
+
+File: graphics.info,  Node: space,  Prev: rotate,  Up: libps
+
+`space'
+-------
+
+   int `space' (int X0, int Y0, int X1, int Y1)
+
+   `space' takes two pair of integers arguments specifying the lower,
+left-hand and upper, right-hand limits of the range of plot
+coordinates.  The scaling of input to output coordinate conversion is
+adjusted to fit these ranges into the page.  Note however that if the
+ranges of x and y coordinates are different the smallest scaling of the
+two is used to avoid affecting the aspect ratio of the plot.  This
+means that although the plot is scaled to fit on the page, the axes are
+not stretched with respect to each other.
+
+
+File: graphics.info,  Node: files,  Next: Acknowledgements,  Prev: libps,  Up: Top
+
+The Plot File Format
+====================
+
+   The plot file is a set of plotting commands and data.  Each command
+is a single ascii character indicating which operation is to be
+performed.  The data following a command is either a newline terminated
+ascii string or several signed, two byte integers in binary format.
+For example, the command to move the current point to the coordinate
+(3,5) would be `m\000\003\000\005'.
+
+   Note that the byte order of the binary representation of the signed,
+two byte integers is machine dependent, so on some machines, this
+command might appear as `m\003\000\005\000'.  `plot2ps' tries to guess
+the byte order from the arguments to the `openpl' command and adjust
+the order accordingly.
+
+   The following table lists each single character commands followed by
+the name of the corresponding libps function called to handle the data
+and a description of the command and data.
+
+`Command'
+     Description
+
+`a'
+     The arc command is followed by three pair of signed, two byte
+     integers indicating the center, starting and ending points for a
+     circular arc.  The center becomes the the current point.  This is
+     equivalent to the `arc' function (*note arc::.).
+
+`c'
+     The circle command is followed by three signed, two byte integers.
+     The first two indicate the x and y coordinates of the center of
+     the circle and the third indicates the radius of the circle.  The
+     center becomes the the current point.  This is equivalent to the
+     `circle' function (*note circle::.).
+
+`C'
+     The color command is followed by three unsigned, two byte integer
+     which indicate the intensity of RED, GREEN and BLUE components
+     respectively of the background color.  For each component the
+     range of intensity is from 0 to 65535.  A value of (0, 0, 0)
+     represents black and (65535, 65535, 65535) represents white. This
+     is equivalent to the `color' function (*note color::.).
+
+`e'
+     The erase command is followed by no data.  The erase command is not
+     needed since in `idraw' and PostScript we start off with a blank
+     page.  For this reason the erase command does not actually output
+     any PostScript. This is equivalent to the `erase' function (*note
+     erase::.).
+
+`f'
+     The linemod command is followed by a newline terminated string
+     containing the name of the line mode (or style) for all subsequent
+     lines, circles and arcs. This is equivalent to the `linemod'
+     function (*note linemod::.) which describes the line styles and
+     their names.
+
+`F'
+     The the fontname command is followed by a newline terminated string
+     containing the name of the font to be used for all subsequent
+     text. This is equivalent to the `fontname' function (*note
+     fontname::.).
+
+`l'
+     The line command is followed by two pair of signed, two byte
+     integers which indicate the starting and ending points of the
+     line.  The second pair becomes the the current point. This is
+     equivalent to the `line' function (*note line::.).
+
+`L'
+     The fill command is followed by an unsigned, two byte integer
+     indicating the intensity of the fill for closed paths.  A value of
+     1 represents black and a value of 0xFFFF indicates white.  The
+     value 0 is special in that is indicates that no solid fill should
+     occur, and that the interior of the respective path is
+     transparent. This is equivalent to the `fill' function (*note
+     fill::.).
+
+`m'
+     The move command is followed by a pair of signed, two byte integers
+     containing the location of the new current point.  No line is
+     drawn to this point as opposed to the continue command (`c') which
+     draws a line.  This is equivalent to the `move' function (*note
+     move::.).
+
+`n'
+     The continue command is followed by pair of signed, two byte
+     integers containing the coordinates of the endpoint of a line
+     segment.  A line is drawn from the previous current point if it
+     was set using a command such as move or continue.  This then
+     becomes the the current point. This is equivalent to the `cont'
+     function (*note cont::.).
+
+`p'
+     The point command is followed by pair of signed, two byte integers
+     containing the location of single point to be drawn.  This then
+     becomes the the current point. This is equivalent to the `point'
+     function (*note point::.).
+
+`r'
+     The rotate command is followed by one signed, two byte integer.  It
+     indicates the rotation of all subsequent text.  The rotation is in
+     degrees counter-clockwise from the x (horizontal) axis. This is
+     equivalent to the `rotate' function (*note rotate::.).
+
+`s'
+     The space command is followed by two pair of signed, two byte
+     integers which indicate the the lower right-hand and upper
+     left-hand corners of the range of plot coordinate space.
+     `plot2ps' uses the third signed, two byte integer (the right-hand
+     limit) to try to determine the byte order. This is equivalent to
+     the `space' function (*note space::.) which describes the
+     recognized sizes.
+
+`S'
+     The fontsize command is followed by an signed, two byte integer
+     containing the size in printers points of all subsequent text.
+     This is equivalent to the `fontsize' function (*note fontsize::.).
+
+`t'
+     The label command is followed by a newline terminated string
+     contains a label which is printed at the current point.  It is
+     left justified and centered vertically with respect to the current
+     point.  The current point is then set at the end of the text. This
+     is equivalent to the `label' function (*note label::.).
+
+`T'
+     The adjusted label command is followed by two characters which
+     indicate the horizontal and vertical justification respectively
+     and a newline terminated string containing the label.  The label
+     is drawn with the specified justification and the current point is
+     set at the end of the text.  This is equivalent to the `alabel'
+     function (*note alabel::.) which describes how to specify
+     justification.
+
+
+File: graphics.info,  Node: Acknowledgements,  Next: Function Index,  Prev: files,  Up: Top
+
+Acknowledgements
+================
+
+   Rich Murphey <Rich@Rice.edu> wrote the first version of the graph,
+plot2tek, plot2ps and tek2plot and the documentation.  Richard Stallman
+<Rms@ai.mit.edu> further directed development of the programs and
+editorial support for the documentation.  John Interrante generously
+provided the PostScript prologue and helpful comments on the program.
+
+   Arthur Smith (Lassp, Cornell University)
+<arthur@helios.tn.cornell.edu> has generously provided his code for the
+xplot utility.
+
+   Ray Toy <toy@dino.ecse.rpi.edu> provided code for graph and tek 4011
+and rewrote the tick mark spacing code, incorporated gnu getopt and
+provided the statistics package.
+
+   David B. Rosen <rosen@bu.edu>, jeffrey templon
+<templon@copper.ucs.indiana.edu> and David W. Forslund
+<dwf%hope.ACL@lanl.gov> tested alpha versions.
+
+
+File: graphics.info,  Node: Function Index,  Prev: Acknowledgements,  Up: Top
+
+Function Index
+==============
+
+* Menu:
+
+* alabel (char X_JUSTIFY, char Y_JUSTIFY, char *LABEL): alabel.
+* arc (int X, int Y, int X0, int Y0, int X1, int Y1): arc.
+* circle (int X, int Y, int R):         circle.
+* closepl ():                           closepl.
+* color (int RED, int GREEN, int BLUE): color.
+* cont (int X, int Y):                  cont.
+* erase():                              erase.
+* fill (int LEVEL):                     fill.
+* fontname (char *FONT_NAME):           fontname.
+* fontsize (int SIZE):                  fontsize.
+* label (char *S):                      label.
+* linemod (char *S):                    linemod.
+* line (int X1, int y1, int X2, int Y2): line.
+* move (int X, int Y):                  move.
+* openpl():                             openpl.
+* point (int X, int Y):                 point.
+* rotate (int W, int H, int ANGLE):     rotate.
+* space (int X0, int Y0, int X1, int Y1): space.
+
+
+
+Tag Table:
+Node: Top187
+Node: introduction1407
+Node: graph examples3457
+Node: Invocation4024
+Node: Input Files5739
+Node: Overlaying7374
+Node: Multiple Plots9252
+Node: Binary Data10198
+Node: graph invocation11307
+Node: plot2ps examples18376
+Node: plot2ps invocation18914
+Node: xplot24137
+Node: xplot invocation24803
+Node: Plot2fig Examples26556
+Node: plot2fig invocation27374
+Node: plot2tek invocation29619
+Node: latex example32322
+Node: psfig33301
+Node: atend.pl35322
+Node: dvips36130
+Node: idraw36538
+Node: ghostscript36899
+Node: libps37487
+Node: alabel38741
+Node: arc39560
+Node: circle39892
+Node: closepl40169
+Node: color40400
+Node: cont40919
+Node: erase41255
+Node: fill41545
+Node: fontname41952
+Node: fontsize42615
+Node: label42872
+Node: line43234
+Node: linemod43593
+Node: move44259
+Node: openpl44632
+Node: point45278
+Node: rotate45544
+Node: space45836
+Node: files46490
+Node: Acknowledgements52660
+Node: Function Index53597
+
+End Tag Table
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/keywords.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/keywords.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/keywords.txt	(revision 22322)
@@ -0,0 +1,129 @@
+
+here are keywords used in Elixir:
+
+Elixir defined keywords:
+(detrend dB)
+TVSTART
+TVSTOP
+FILTER
+EXPTIME
+OBSTYPE
+NENTRY - values in dB
+
+(phot dB)
+NIMAGES 
+
+(imregister / iminfo)
+EXPTIME-KEYWORD   : ExptimeKeyword   : EXPTIME : Exposure time
+IMAGETYPE-KEYWORD : ImagetypeKeyword : OBSTYPE / EXPTYPE : Image type (DARK, OBJECT, etc)
+CCDNUM-KEYWORD    : CCDnumKeyword    : IMAGEID : CCD number
+FILTER-KEYWORD    : FilterKeyword    : FILTER  : filter name
+AIRMASS-KEYWORD   : AirmassKeyword   : AIRMASS : airmass
+FOCUS-KEYWORD     : FocusKeyword     : TELFOCUS: telescope focus setting
+ROTATION-KEYWORD  : RotationKeyword  : ROTANGLE: rotation angle
+DETTEMP-KEYWORD   : DettempKeyword   : DETTEM  : detector temperature
+TELDATA1-KEYWORD  : Teldata1Keyword  : XPROBE  : bonnette probe x position
+TELDATA2-KEYWORD  : Teldata2Keyword  : YPROBE  : bonnette probe y position
+TELDATA3-KEYWORD  : Teldata3Keyword  : ZPROBE  : bonnette focus
+RA-DDD-KEYWORD    : RADecDegKeyword  : CRVAL1  : RA in dec. degrees
+DEC-DDD-KEYWORD   : DECDecDegKeyword : CRVAL2  : DEC in dec. degrees
+RA-HMS-KEYWORD    : RASexigKeyword   : CRVAL1  : RA in hour,min,sec
+DEC-DMS-KEYWORD   : DECSexigKeyword  : CRVAL2  : DEC in dec,min,sec
+CAMERA-KEYWORD    : CameraKeyword    : INSTRUME: unique name of camera
+
+(time keywords already abstracted)
+DATE-KEYWORD	  : DateKeyword	     : DATE-OBS   : date
+DATE-MODE	  : DateMode	     : yyyy-mm-dd : format for DATE-KEYWORD
+UT-KEYWORD	  : UTKeyword	     : UTC-OBS	  : UT
+JD-KEYWORD	  : JDKeyword	     : NONE	  : JD
+MJD-KEYWORD	  : MJDKeyword	     : NONE	  : MJD
+
+(photcode)
+CAMERA-KEYWORD    : CameraKeyword    : DETECTOR: unique name of camera
+FILTER-KEYWORD    : FilterKeyword    : FILTER  : filter name
+CCDNUM-KEYWORD    : CCDnumKeyword    : IMAGEID : CCD number
+
+
+EXTEND  : extensions present (FITS required keyword for MEF)
+NEXTEND : extension number (not necessarily IMAGEID)
+
+(gophot)
+SATVALUE: saturation value for this image
+GAIN    : gain (e/DN) for this image
+NEWGAIN : a stupid name for an altered gain.
+RDNOISE : read noise (in electrons) ***
+DNNOISE : read noise (in DN)        ***
+
+(gastro / coordops in ohana)
+WCS keywords (these do not need to be abstracted)
+CD1_1
+CD1_2
+CD2_1
+CD2_2
+CDELT1
+CDELT2
+CROTA2
+CRPIX1
+CRPIX2
+CRVAL1
+CRVAL2
+CTYPE2
+NPLYTERM
+PC001001
+PC001002
+PC002001
+PC002002
+PCA1X0Y2
+PCA1X0Y3
+PCA1X1Y1
+PCA1X1Y2
+PCA1X2Y0
+PCA1X2Y1
+PCA1X3Y0
+PCA2X0Y2
+PCA2X0Y3
+PCA2X1Y1
+PCA2X1Y2
+PCA2X2Y0
+PCA2X2Y1
+PCA2X3Y0
+DEC_O
+DEC_X
+DEC_Y
+RA_O
+RA_X
+RA_Y
+EPOCH
+EQUINOX
+
+NAXIS1  : FITS mandatory keyword
+NAXIS2  : FITS mandatory keyword
+RA	: RA in sexigesimal (for rough astrometry)
+DEC	: DEC in sexigesimal (for rough astrometry)
+NSTARS  : elixir-added keyword (number of stars, from imclean)
+
+EXPTIME
+AIRMASS 
+IMAGEID
+
+ZERO_PT : Elixir-added keyword (imclean)
+NASTRO  : Elixir-added keyword (imclean)
+CERROR  : Elixir-added keyword (gastro)
+PHOTCODE: Elixir-added keyword (imclean)
+APMIFIT : Elixir-added keyword (imclean)
+dAPMIFIT: Elixir-added keyword (imclean)
+FLIMIT  : Elixir-added keyword (imclean)
+FSATUR  : Elixir-added keyword (imclean)
+FWHM_X  : Elixir-added keyword (imclean)
+FWHM_Y  : Elixir-added keyword (imclean)
+TZERO   : Elixir-added keyword (imclean)
+TRATE   : Elixir-added keyword (imclean)
+NSTARS  : Elixir-added keyword (imclean)
+
+NIMAGES : (Elixir keyword for phot db)
+
+parse_time.c:    fits_scan (header, JDKeyword, "%lf", 1, &jd);
+parse_time.c:    fits_scan (header, MJDKeyword, "%lf", 1, &jd);
+parse_time.c:  fits_scan (header, UTKeyword, "%s", 1, line);
+parse_time.c:  fits_scan (header, DateKeyword, "%s",  1, line);
+wimage.c:  fits_scan (&header, "NIMAGES", "%d", 1, &Nimages);
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/locks.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/locks.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/locks.txt	(revision 22322)
@@ -0,0 +1,70 @@
+
+NFS locks & file locks in ohana / elixir
+
+There are three types of locks in use in the elixir system:
+
+SOFT, XCLD, HARD
+
+SOFT - a soft lock on a file prevents a xcld or hard lock on that
+       file,  but allows other soft locks to be set.  the soft lock
+       uses NFS locking and is ephmeral, vanishing if the holding
+       program ends, whether or not the program explicitly removes the
+       lock. 
+
+XCLD - an xcld lock on a file prevents all other locks: soft, xcld, or
+       hard; only one xcld lock may be active on a file at a time.
+       The xcld lock uses NFS locking is ephemeral, vanisihing when
+       the program exits.
+
+HARD - a hard lock on a file prevents all other locks: soft, xcld, or
+       hard; only one hard lock may be active on a file at a time.
+       The hard lock is sticky and will remain unless the program
+       actively removes the lock.  It is implemented by first setting
+       an XCLD lock in NFS and then creating a lock file (.file.lck)
+       which contains the work BUSY.  until this file is removed or
+       the contents changed, the lock remains.
+
+basic lock APIs:
+
+int setlockfile2 (char *filename, double timeout, int type, int *state)
+FILE *fsetlockfile (char *filename, double timeout, int type, int *state)
+
+setlockfile2 sets a lock of the requested type on the given file,
+  returning an open file descriptor to the file.  If the requested
+  lock is SOFT and the file does not exist, setlockfile2 returns -1
+  and sets the state to  LCK_EMPTY - the lock is not set.  In the case
+  of XCLD or HARD locks, if the file does not exist, it is created.
+  If the lock cannot be set immediately, setlockfile2 will block for
+  up to 'timeout' seconds, at which point it will return -1 and exit
+  with state LCK_TIMEOUT.  
+
+fsetlockfile behaves identically to setlockfile2, but generates and
+returns a stream associated with the open file descriptor.  An error
+will result in the return of (FILE *) NULL.
+
+int clearlockfile2 (char *filename, int fd, int type, int *state)
+int fclearlockfile (char *filename, FILE *f, int type, int *state)
+
+clearlockfile2 clears the lock associated with a file and file
+descriptor.  If the type is HARD, it will clear the associated hard
+lock file before removing the NFS level file lock.  If only the
+filename is passed to clearlockfile2, the NFS-level locks cannot be
+unset, but the hardlock will be unset.  this is useful if a program is
+known to then exit.
+
+fclearlockfile behaves identically to setlockfile2, but requires the
+associated stream, rather than the file descriptor.  
+
+
+-----
+
+
+int lock_catalog (Catalog *catalog, int lockmode)
+
+lock_catalog attempts to set a lock on the specified photometry
+catalog file.  If successful, the catalog file pointer is set to the
+beginning of the catalog and the function returns the value 1.  If the
+file is empty, the return value is 2.  This only indicates an error if
+the file pointer is NULL, which occurs if the requested locktype is
+SOFT. 
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/ohana-cvs.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/ohana-cvs.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/ohana-cvs.txt	(revision 22322)
@@ -0,0 +1,105 @@
+
+The goal is to have two source trees: a development tree in your own
+private location and an official release tree, which contains the
+production version of the code.  In practice, you never edit the
+production version of the code: it is essentially read-only.  you just
+compile that tree and install it wherever the main-line production
+sources go (eg, /apps/elixir/bin).  New releases of the elixir code
+are done by replacing that tree with a new tree extracted from a
+released tarball.
+
+Start by creating you own CVS checkouts of the elixir ohana and config
+source tree.  this can be in your home directory, if you like, or
+where ever.  now check out the complete ohana and config cvs trees:
+
+- make sure you have CVSROOT set:
+
+setenv CVSROOT jcc@naupaka:/data/elixir2/srcdir/cvs
+
+- check out ohana:
+
+cvs co ohana
+
+- check out config:
+
+cvs co config
+
+now you should compile ohana.  I think you have the ARCH variable
+being setup correctly in your environment.  in this case, you can
+configure ohana to recognize that variable and allow for multiplatform
+compilation:
+
+- go into ohana, run configure, make, make install:
+
+cd ohana
+configure --vararch
+(note the last line of the configure output: it tells you how to set
+your path)
+
+make
+make install
+
+The compilation should run to the end without any trouble.  if you
+have any errors, you should let me know so we can fix them.
+
+You should adjust your path to point at the new installation of
+ohana.  when you ran configure, it told you how to set your path.
+if your path is set correctly, you should see the new versions of the
+elixir programs.
+
+Next, you should setup the elixir config data.  go into the config
+directory which you checked out and copy elixir.site.in to
+elixir.site.  Edit this file to reflect the local (CFHT) elixir data
+locations (the settings in this file are probably already correct for
+CFHT).  
+
+at this point, you should be able to run the elixir programs as jcc
+using this new copy and interacting with the standard CFHT elixir
+data.
+
+Now, let's create a production distribution tarball.  To do this, go
+into ohana/etc and run the distribution creation tool:
+
+- mkdist elixir-ohana 1.1
+
+This will create a new tarball for you containing all of the elixir
+ohana components based on the correct tagged versions of the software
+in the repository.  No matter what code changes you make and commit to
+CVS, when you run this command in the future you will get this
+specific snapshot of the elixir ohana software. 
+
+Install this new tarball in the production location:
+
+- cd /data/elixir2/srcdir
+- mv ohana ohana-preCVS
+- tar xvzf (PATH to tarball elixir-ohana-1.1.tgz)
+- ln -s elixir-ohana-1.1 ohana
+
+Compile the new ohana distribution:
+
+- cd ohana
+- configure --vararch
+- make install
+
+This last step will compile it in the local ohana path. At this point,
+from the viewpoint of the rest of Elixir, everything looks the same as
+it does before we started this process.  You can now go into
+/data/elixir2/srcdir and run the make install-test and make install
+just as you have in the past to install updates to the ohana software.
+
+Same process for the config system.  go back to your checked-out copy
+of ohana.  run 'mkdist elixir-config 1.1'.  go to
+/data/elixir2/srcdir.  save the old version of config
+(config-preCVS).  extract the tarball: ' tar xvzf (PATH to
+elixir-config-1.1.tgz)'.  link the config system to the old name: 'ln
+-s elixir-config-1.1 config'  copy config/elixir.site.in to
+config/elixir.site.  edit to match the existing site.  install in
+/apps/elixir/config (cd /data/elixir2/srcdir; make install).
+
+ 
+--
+
+some useful tricks:
+
+ cvs rdiff -s -r TAG MODULE
+ mkdist -diff GROUP VERSION
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/elixir.fig
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/elixir.fig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/elixir.fig	(revision 22322)
@@ -0,0 +1,76 @@
+#FIG 3.2
+Portrait
+Center
+Inches
+Letter 
+90.00
+Single
+-2
+1200 2
+1 1 0 1 0 7 0 0 -1 4.000 1 0.0000 5175 3000 1650 975 5175 3000 6825 3975
+1 1 0 1 0 7 0 0 -1 4.000 1 0.0000 8400 10575 1350 825 8400 10575 9750 11400
+1 1 0 1 0 7 0 0 -1 4.000 1 0.0000 2475 10650 1350 825 2475 10650 3825 11475
+2 4 0 1 0 7 0 0 20 0.000 0 0 7 0 0 5
+	 8400 8700 6600 8700 6600 6375 8400 6375 8400 8700
+2 4 0 1 0 7 2 0 20 0.000 0 0 7 0 0 5
+	 9450 8250 7650 8250 7650 5925 9450 5925 9450 8250
+2 4 0 1 0 7 0 0 20 0.000 0 0 7 0 0 5
+	 7425 9150 5625 9150 5625 6825 7425 6825 7425 9150
+2 4 0 1 0 7 0 0 -1 4.000 0 0 7 0 0 5
+	 3300 9150 3300 6825 1500 6825 1500 9150 3300 9150
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 1
+	 5850 3900
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 1 0 2
+	1 0 1.00 60.00 120.00
+	 6225 3750 7950 5925
+2 1 0 1 0 7 20 0 -1 0.000 0 0 -1 1 0 2
+	1 0 1.00 60.00 120.00
+	 4275 3900 2400 6825
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 1 1 2
+	1 0 1.00 60.00 120.00
+	1 0 1.00 60.00 120.00
+	 2475 9150 2475 9825
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 1 0 2
+	1 0 1.00 60.00 120.00
+	 7350 9150 7875 9825
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 1 0 2
+	1 0 1.00 60.00 120.00
+	 9150 8250 8475 9750
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 1 0 2
+	1 0 1.00 60.00 120.00
+	 7950 8700 8175 9750
+2 1 1 1 0 7 0 0 -1 4.000 0 0 -1 1 0 2
+	1 0 1.00 60.00 120.00
+	 3300 9975 5625 8325
+2 1 1 1 0 7 0 0 -1 4.000 0 0 -1 1 0 2
+	1 0 1.00 60.00 120.00
+	 7050 10500 3300 8325
+2 2 0 1 7 7 1 0 20 0.000 0 0 -1 0 0 5
+	 2925 4875 4275 4875 4275 5625 2925 5625 2925 4875
+2 1 0 1 0 7 1 0 -1 0.000 0 0 -1 1 0 2
+	1 0 1.00 60.00 120.00
+	 5550 3975 6000 6825
+2 1 0 1 0 7 2 0 -1 0.000 0 0 -1 1 0 2
+	1 0 1.00 60.00 120.00
+	 5850 3900 6975 6375
+2 2 0 1 7 7 1 0 20 0.000 0 0 -1 0 0 5
+	 5775 4575 7125 4575 7125 5400 5775 5400 5775 4575
+4 0 0 0 0 0 36 0.0000 4 375 8385 1200 1500 Elixir: Calibration and Data Evaluation\001
+4 0 0 0 0 0 18 0.0000 4 255 915 5850 7425 Ptolemy\001
+4 0 0 0 0 0 18 0.0000 4 195 465 5850 8175 data\001
+4 0 0 0 0 0 18 0.0000 4 195 1155 5850 8550 evaluation\001
+4 0 0 0 0 0 18 0.0000 4 195 720 1650 7425 FLIPS\001
+4 0 0 0 0 0 18 0.0000 4 255 1320 7650 10350 Photometry\001
+4 0 0 0 0 0 18 0.0000 4 255 1320 7725 10875 Astrometry\001
+4 0 0 0 0 0 18 0.0000 4 195 1260 1800 10425 Calibration\001
+4 0 0 0 0 0 18 0.0000 4 255 780 2025 10875 Images\001
+4 0 0 0 0 0 18 0.0000 4 225 720 4800 3075 and/or\001
+4 0 0 0 0 0 18 0.0000 4 255 1110 3975 2625 Telescope\001
+4 0 0 0 0 0 18 0.0000 4 195 930 5325 3525 Archive\001
+4 0 0 0 0 0 18 0.0000 4 195 1185 1650 8550 creation &\001
+4 0 0 0 0 0 18 0.0000 4 195 1155 1650 8865 evaluation\001
+4 0 0 0 0 0 18 0.0000 4 195 1200 1650 8175 calibration\001
+4 0 0 0 0 0 18 0.0000 4 255 765 6225 5250 images\001
+4 0 0 0 0 0 18 0.0000 4 255 765 3225 5475 images\001
+4 0 0 0 0 0 18 0.0000 4 195 1200 3000 5175 calibration\001
+4 0 0 0 0 0 18 0.0000 4 195 870 6000 4875 science \001
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/flags.fig
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/flags.fig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/flags.fig	(revision 22322)
@@ -0,0 +1,29 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3
+	 2400 975 4050 975 4050 4125
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3
+	 6525 1425 7650 1425 7650 4200
+4 0 0 0 0 2 14 0.0000 4 195 1695 600 975 Bit 15: ID_Moving\001
+4 0 0 0 0 2 14 0.0000 4 180 1590 600 1425 Bit 14: ID_USNO\001
+4 0 0 0 0 2 14 0.0000 4 195 2085 4275 1425 Bit 14: ID_Bad_Object\001
+4 0 0 0 0 2 14 0.0000 4 195 1695 7875 1875 Lower Byte Values\001
+4 0 0 0 0 2 14 0.0000 4 150 1095 8025 2325 0x01: Ghost\001
+4 0 0 0 0 2 14 0.0000 4 150 1005 8025 2625 0x02: Trail\001
+4 0 0 0 0 2 14 0.0000 4 150 1050 8025 2925 0x03: Bleed\001
+4 0 0 0 0 2 14 0.0000 4 180 1785 600 1875 Bit 13: ID_Variable\001
+4 0 0 0 0 2 14 0.0000 4 180 1875 600 2325 Bit 12: ID_Transient\001
+4 0 0 0 0 2 14 0.0000 4 180 1905 600 2775 Bit 11: ID_Bad_Data\001
+4 0 0 0 0 2 14 0.0000 4 195 1635 600 3225 Bit 10: ID_Proper\001
+4 0 0 0 0 2 14 0.0000 4 150 1620 600 3675 Bit 09: Undefined\001
+4 0 0 0 0 2 14 0.0000 4 150 1620 600 4125 Bit 08: Undefined\001
+4 0 0 0 0 0 12 0.0000 4 180 510 6750 1350 (if yes)\001
+4 0 0 0 0 0 12 0.0000 4 180 510 2925 900 (if yes)\001
+4 0 0 0 0 2 14 0.0000 4 180 1770 4275 1875 Bit 13: ID_Asteroid\001
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/loneos.fig
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/loneos.fig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/loneos.fig	(revision 22322)
@@ -0,0 +1,174 @@
+#FIG 3.2
+Portrait
+Center
+Inches
+Letter 
+90.00
+Single
+-2
+1200 2
+6 2775 9450 8325 11250
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 5475 10500 825 450 5475 10500 6300 10950
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 3675 10500 825 450 3675 10500 4500 10950
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 7275 10500 825 450 7275 10500 8100 10950
+2 4 1 1 0 7 0 0 -1 4.000 0 0 7 0 0 5
+	 8325 11250 8325 9450 2775 9450 2775 11250 8325 11250
+2 1 1 1 0 7 0 0 -1 4.000 0 0 -1 0 0 2
+	 6375 9450 6375 11250
+4 0 0 0 0 0 18 0.0000 4 195 840 5025 10650 Star dB\001
+4 0 0 0 0 0 18 0.0000 4 255 1050 3150 10650 Image dB\001
+4 0 0 0 0 0 18 0.0000 4 195 945 6825 10650 Rock dB\001
+4 0 0 0 0 0 18 0.0000 4 195 1440 3825 9900    Photom dB\001
+-6
+6 3975 8400 5175 8850
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 3975 8400 5175 8400 5175 8850 3975 8850 3975 8400
+4 0 0 0 0 0 18 0.0000 4 195 810 4125 8775 addstar\001
+-6
+6 2100 3150 3075 3525
+2 2 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 5
+	 2100 3150 3075 3150 3075 3525 2100 3525 2100 3150
+4 0 0 0 0 0 18 0.0000 4 195 735 2175 3450 flatten\001
+-6
+6 375 10050 1725 11250
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 1050 10650 675 600 1050 10650 1725 11250
+4 0 0 0 0 0 18 0.0000 4 195 750 750 10725 USNO\001
+-6
+6 375 8625 1725 9825
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 1050 9225 675 600 1050 9225 1725 9825
+4 0 0 0 0 0 18 0.0000 4 195 1125 450 9300 HST GSC\001
+-6
+6 3225 6525 4425 6975
+6 3225 6525 4425 6975
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 3225 6525 4425 6525 4425 6975 3225 6975 3225 6525
+4 0 0 0 0 0 18 0.0000 4 195 495 3525 6825 fstat\001
+-6
+-6
+6 2550 4800 3750 5250
+6 2550 4800 3750 5250
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 2550 4800 3750 4800 3750 5250 2550 5250 2550 4800
+4 0 0 0 0 0 18 0.0000 4 255 780 2700 5175 dophot\001
+-6
+-6
+6 2925 5475 4125 6225
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 3525 5850 600 375 3525 5850 4125 6225
+4 0 0 0 0 0 18 0.0000 4 255 345 3300 5925 obj\001
+-6
+6 2250 3825 3450 4575
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 2850 4200 600 375 2850 4200 3450 4575
+4 0 0 0 0 0 18 0.0000 4 195 255 2700 4275 flt\001
+-6
+6 3600 7275 4800 8025
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 4200 7650 600 375 4200 7650 4800 8025
+4 0 0 0 0 0 18 0.0000 4 195 480 3975 7725 cmp\001
+-6
+6 5925 8325 7125 8775
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 5925 8325 7125 8325 7125 8775 5925 8775 5925 8325
+4 0 0 0 0 0 18 0.0000 4 195 660 6075 8625 filters\001
+-6
+6 6150 6375 7800 7575
+6 6150 6375 7800 7575
+1 1 0 1 0 7 0 0 -1 4.000 1 0.0000 6975 6975 825 600 6975 6975 7800 7575
+4 0 0 0 0 0 18 0.0000 4 195 510 6750 6900 alert\001
+-6
+4 0 0 0 0 0 18 0.0000 4 195 465 6750 7275 files\001
+-6
+6 1650 7425 2850 7875
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 1650 7425 2850 7425 2850 7875 1650 7875 1650 7425
+4 0 0 0 0 0 18 0.0000 4 225 675 1800 7800 gastro\001
+-6
+6 2775 12600 3975 13050
+6 2925 12750 3825 12975
+4 0 0 0 0 0 18 0.0000 4 195 915 2925 12975 addusno\001
+-6
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 2775 12600 3975 12600 3975 13050 2775 13050 2775 12600
+-6
+6 4200 12600 5400 13050
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 4200 12600 5400 12600 5400 13050 4200 13050 4200 12600
+4 0 0 0 0 0 18 0.0000 4 195 1005 4350 12975 markstar\001
+-6
+6 5625 12600 6825 13050
+6 5625 12600 6825 13050
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 5625 12600 6825 12600 6825 13050 5625 13050 5625 12600
+-6
+4 0 0 0 0 0 18 0.0000 4 195 1080 5775 12975 markrock\001
+-6
+6 7050 12600 8250 13050
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 7050 12600 8250 12600 8250 13050 7050 13050 7050 12600
+4 0 0 0 0 0 18 0.0000 4 255 765 7200 12975 nrphot\001
+-6
+6 8100 6450 9750 7650
+1 1 0 1 0 7 0 0 -1 4.000 1 0.0000 8925 7050 825 600 8925 7050 9750 7650
+4 0 0 0 0 0 18 0.0000 4 255 885 8475 7290 pictures\001
+4 0 0 0 0 0 18 0.0000 4 240 795 8475 6975 results,\001
+-6
+6 7725 8250 8925 8700
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 7725 8250 8925 8250 8925 8700 7725 8700 7725 8250
+4 0 0 0 0 0 18 0.0000 4 165 645 7875 8625 status\001
+-6
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 1425 1950 750 525 1425 1950 2175 2475
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 3750 1950 750 525 3750 1950 4500 2475
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 3525 7650 2850 7650
+2 4 1 1 0 7 0 0 -1 4.000 0 0 7 0 0 5
+	 2025 12375 2025 8550 150 8550 150 12375 2025 12375
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 3375 11250 3375 12600
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 1 2
+	0 0 1.00 60.00 120.00
+	 2250 3075 1425 2475
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 1 2
+	0 0 1.00 60.00 120.00
+	 2850 3075 3750 2475
+2 1 1 1 0 7 0 0 -1 4.000 0 0 -1 1 0 2
+	0 0 1.20 100.00 200.00
+	 2550 3525 4875 9450
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 4800 11250 4800 12600
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 6225 11250 6225 12600
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 7575 11250 7575 12600
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 1 3
+	0 0 1.00 60.00 120.00
+	 2775 12825 1050 12825 1050 12375
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 1 3
+	0 0 1.00 60.00 120.00
+	 1650 7650 1050 7650 1050 8550
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 6525 8325 6675 7500
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 6225 9450 6361 8772
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 8039 9453 8189 8703
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 8339 8253 8489 7503
+4 0 0 0 0 0 18 0.0000 4 195 1155 525 12075 references\001
+4 0 0 0 0 0 18 0.0000 4 165 840 675 11775 astrom.\001
+4 0 0 0 0 0 18 0.0000 4 195 825 3375 2025 archive\001
+4 0 0 0 0 0 18 0.0000 4 255 1020 900 2025 telescope\001
+4 0 0 0 0 0 36 0.0000 4 495 4200 5250 4425 Variability Pipeline\001
+4 0 0 0 0 0 36 0.0000 4 435 3870 5550 3450 U.W. / LONEOS \001
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/params.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/params.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/params.txt	(revision 22322)
@@ -0,0 +1,125 @@
+# Configuration file for Loneos analysis pipeline 
+
+# important files and directories
+IN_DIR			/pallas
+OUT_DIR			/irene/d11/workspace
+#
+CATDIR			/hebe/d27/database
+IMAGE_CATALOG		/hebe/d27/database/Images.dat
+IMAGE_CATALOG_TEMPLATE	/hebe/d27/database/template.cat
+CATALOG_TEMPLATE	/hebe/d27/database/template.cat
+ROCK_CATALOG		/hebe/d27/database/Rocks.dat
+#
+GSCFILE			/metis/d11/references/GSC/GSCregions.tbl
+GSC_DIR			/metis/d11/references/GSC
+USNO_CDROM		/metis/d11/references/USNO
+DOPHOT_PARAMS		/metis/d11/references/config/default_parameters
+FLATTEN_SCRIPT		/metis/d11/references/config/flatten.pro
+LONEOS_REGIONS		/metis/d11/references/config/regions.map
+
+# parameters for "fstat"
+# instrumental mag zero point
+ZERO_PT			24.5
+DOPHOT_CHAR_LINE	129
+DOPHOT_TYPE_FIELD	5
+DOPHOT_AP_FIELD		91
+DOPHOT_PSF_FIELD	52
+MIN_SN_FSTAT		6.0
+
+# parameters for "gastro"
+DEFAULT_RADIUS   32.0
+MINIMUM_RADIUS   1.5
+MIN_ERROR	 0.6
+MIN_PRECISE	 0.12
+CCD_PC1_1	 1
+CCD_PC2_2	 -1    # for loneos
+CCD_PC1_2	 0
+CCD_PC2_1	 0
+ASEC_PIX	 2.82
+NFIELD		 3.0
+MMIN		 6.0
+ROT_ZERO	 0
+dROT		 1.0
+NROT		 2
+POLAR_AXIS_RA	 -90.0
+POLAR_AXIS_DEC   89.88
+RA_OFFSET        -90.0
+DEC_OFFSET        0.0
+
+# parameters for "addstar"
+# airmass extinction coefficient (in mag / airmass)
+NSIGMA			3.0
+ALPHA			0.03
+XOVERSCAN		60
+YOVERSCAN		50
+
+# parameters for "controller"
+NEW_IMAGES		images.dat
+USERNAME		gene
+PASSWORD		gene
+
+# here we list all available machines
+NMACHINES		6
+MACHINE0		metis
+MACHINE1		hebe
+MACHINE2		iris
+MACHINE3		irene
+#MACHINE4		juno
+MACHINE4		ceres
+MACHINE5		vesta
+
+# here we define which machines which are always free
+fMACHINE0		1
+fMACHINE1		1
+fMACHINE2		1
+fMACHINE3		1
+fMACHINE4		0
+fMACHINE5		0
+fMACHINE6		0
+
+LOCAL_MACHINE		1  # machine with catalog on local disk
+
+# parameters for "status"
+TIME_REFERENCE		90/01/01 00:00:00
+
+# parameters for "nrphot"
+# make this one small to boost change of getting an early region 
+TAU			3.0  		
+SCATTER_LIM		15.0
+MAG_LIM			17.0
+IMAGE_SCATTER		0.075
+NIMAGE_SCATTER		1.5
+STAR_SCATTER		0.05
+
+### parameters for "markstar"
+SEARCH_RADIUS		360   # region for initial trail hunt, in arcsec 
+TRAIL_WIDTH		5.0   # expected trail width, in arcsec
+NANGLE_BINS		180   # Number of angular bins in 180 degrees
+NPTSINLINE		5     # minimum points needed for trail
+MIN_DENSITY		0.025 # minimum linear density in star/arcsec
+SPACE_SIGMA		10.0  # how much denser than average for trail?
+# parameters defining bright star exclusion regions
+BRIGHT_XTRAIL_WIDTH	  5.0 # faintest sat. star
+BRIGHT_XTRAIL_MAG	  9.5 # faintest sat. star
+BRIGHT_XTRAIL_SLOPE	-80.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_YTRAIL_WIDTH	  5.0 # faintest sat. star
+BRIGHT_YTRAIL_MAG	 11.0 # faintest sat. star
+BRIGHT_YTRAIL_SLOPE    -400.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_HALO_MAG		  8.5 # faintest sat. star
+BRIGHT_HALO_SLOPE	-28.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+# parameters which define the ghosts
+GHOST_MAG		7.5
+GHOST_RADIUS		200   # in arcsec
+OPTICAL_AXIS1		2154.0
+OPTICAL_AXIS2		2193.0
+
+# parameters for "addusno"
+USNO_RADIUS		5.0   # in arcsec
+USNO_PROPER		20.0   # in arcsec
+USNO_RED		1000  # code for USNO Red data
+USNO_BLUE		1001  # code for USNO Blue data
+
+# parameters for "markrock"
+ROCK_RADIUS		2.0   # in arcsec
+ROCK_MAX_RADIUS		20.0  # in arcsec
+ROCK_MAX_SPEED          0.1   # in arcsec / sec
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline.c	(revision 22322)
@@ -0,0 +1,22 @@
+\documentstyle[astro]{article]
+\GoodMargins
+\title{Variability Pipeline}
+\author{Eugene Magnier}
+\begin{document}
+\maketitle
+
+\section{Overview}
+
+\section{Photometry -- Dophot}
+
+\section{Astrometry -- gastro}
+
+\section{The Photometry Database}
+
+\section{Visualization Tools}
+
+\section{Software Distribution}
+
+\section{Programming Details}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline.tex	(revision 22322)
@@ -0,0 +1,997 @@
+%\documentstyle[astro,psfig]{article}
+\documentclass{article}
+\usepackage{psfig}
+\usepackage{astro}
+\usepackage{times}
+\GoodMargins
+\newcommand{\note}[1]{({\bf #1})}
+\title{Variability Pipeline}
+\author{Eugene Magnier}
+\begin{document}
+\maketitle
+
+\section{Overview}
+
+The Loneos Pipeline provides automated reduction of images, including
+photometry and astrometry, with the final data stored in a photometry
+database organized by star position on the sky.
+
+Fig.~\ref{pipeline} shows a schematic of the analysis pipeline.
+Images are written to the disk from the camera by the software which
+runs it, {\tt astrocam}.  The resulting images are FITS files (which
+may be arbitrarily sized) and have header keyword parameters RA and
+DEC specifying rough celestial coordinates.  Images are first analyzed
+by {\tt dophot}, which produces instrumental photometry and pixel
+coordinate positions for stars and other objects in the images,
+writing files with a particular format, called OBJ files in this
+discussion.  The results from {\tt dophot} are cleaned up by a program
+called {\tt fstat} which merges the header information from the image
+with the most relevant parts of the OBJ file, creating a new file with
+the CMP format.  Astrometry is performed on the CMP file with the
+program {\tt gastro}.  {\tt Gastro} makes a comparison with an
+astrometric reference database and only changes CMP header keywords as
+a result.  Finally, the processed image CMP file is added to the
+photometry database with the program {\tt addstar}.  The photometry
+database is maintained in a large number of files (CPT files), each
+representing a small region of sky.  There is no relationship between
+the image locations and the boundaries of CPT file.  All of these
+processes occur on individual image, and would typically be performed
+while the observations are occuring, or during the day following, but
+may be performed at any time to add new or archived data to the
+database.
+
+Once large batches of images have been processed, a series of programs
+should be run to clean up the photometry database.  Typically, these
+routines would be run at the end of a night on all of the CPT files to
+which new data were added.  First, {\tt markstar} identifies bad data
+(the trails of satelites, the bleeding columns from stars, ghost
+images), and flags these data points appropriately.  Second, {\tt
+  addusno} matches the catalog stars with stars from the USNO database
+and adds the USNO magnitudes to the photometry database.  Next, {\tt
+  markrock} searches for likely asteroids in the CPT file, marks them,
+and writes the asteroid observations to an asteroid database.  It also
+notes measurements which are likely to be noise and cosmic rays.
+Finally, {\tt nrphot} determines relative photometric calibrations for
+observations in a set of CPT files.  
+
+With the exception of {\tt dophot}, all of the programs in the
+pipeline refer to a single configuration file to determine the large
+number of parameters necessary in the process.  The default parameter
+file is hardwired into the programs, but an alternate configuration
+file may be specified with the environment variable {\tt
+  PIPE\_CONFIG}.  This option makes independent processing of multiple
+data sources trivial.  For example, if the pipeline is to be used for
+both Loneos and SDSS image analysis and display, there could be two
+configuration files (eg, {\tt loneos.txt} and {\tt sdss.txt}), one for
+each data source.  Several important parameters would be different
+between these two, for example, the initial plate scale guess for the
+astrometry routine, or the parameters which define the rough chip
+orientation.  This scheme allows for analysis of different types of
+images, with a common destination photometry database, or for
+independent photometry databases.
+
+\begin{figure}
+\psfig{file=schematic.eps,width=9cm}
+\caption{\label{pipeline} 
+Schematic of the photometry pipeline.  Rectangles represent programs,
+while ovals represent data products.  Arrows show the direction of
+travel of information.  The flatten routine is a filter whose position
+may change as the pipeline matures.
+}
+\end{figure}
+
+Data from different sources can be processed by the pipeline and
+stored in the same photometry database, since all photometry is tagged
+with a source ID in the database (as are the images in the image
+database).  This allows us to include, for example, the USNO catalog
+magnitude measurements (or a subset) in the photometry database for a
+quick comparion.  Different sources may be considered different
+filters, so color-color and color-magnitude diagrams could be
+extracted by specifying choices for source catalog.  Currently each
+object has a single primary photometric system, and individual
+observations may be combined to determine that average (eg, if they
+are different photometry sources with roughly the filter bandpass) or
+they may be ignored when calculating relative magnitudes (eg, the USNO
+magnitudes would be ignored).  This may be expanded upon by defining
+multiple average magnitudes and allowing different measurements to
+contribute only to the appropriate average.  The determination of the
+photometry code for a particular image is currently not well defined.
+There are several points in the analysis where the user (or the
+controlling program) may specify the photometry source.  A table
+should be kept in the photometry catalog to define the various
+photometry codes.  
+
+\section{Image Analysis Pipeline}
+\subsection{Photometry -- Dophot}
+
+Photometry is performed using a varient of {\tt Dophot}.  The version
+of dophot is based on Dophot 2.0, but it has been adapted to
+streamline processing of many files.  First, with this version of
+dophot, any FITS format data is safely read in.  Instead of compiling
+to a fixed maximum size, only the memory needed to contain the image
+matrix is allocated.  The image is also converted to REAL*32 from
+whatever BITPIX for processing.  Finally, only a single, complete
+parameter file is loaded by dophot.  There is no option for a modified
+and a default parameter file.  The parameter file to be used is
+defined, along with the input image and the output object list on the
+command line when the program is run: \\
+{\tt dophot image.fits image.obj parameter\_file} \\
+To achive this, the code from dophot was wrapped within a C program
+which interprets the command line arguments, allocates the necessary
+memory, and reads in the image.  The rest of the processing is
+performed by dophot in the standad manner.  Notice that we use a
+consistent naming convention through the pipeline reductions.  The
+image is called {\tt foo.fits}, while the resulting dophot-produced
+photometry list is called {\tt foo.obj}.  
+
+\subsection{Cleanup -- fstat}
+
+After an image is processed by dophot, the object file ({\tt foo.obj})
+is converted to a more-compact, more-complete file called a CMP file,
+with a name of the form ({\tt foo.cmp}).  This conversion is done with
+the program {\tt fstat}.  The CMP file consists of the FITS header
+from the original image ({\tt foo.fits}), with some additional
+keywords to be used at later stages in the analysis, followed by an
+ASCII list of the interesting data from ({\tt foo.obj}).  In this
+list, types 6 and 8 are excluded, and only the following values are
+kept:
+\begin{verbatim}
+X       Y     Mag   dMag t log(sky) 
+1342.0  106.1 14.166 000 4 3.2
+\end{verbatim}
+Objects with a signal-to-noise ratio lower than a specified cutoff
+({\tt MIN\_SN\_FSTAT}) are also excluded.  Some general information
+about the image is derived by {\tt fstat} (FWHM, saturation and
+completeness limits, number of each dophot type) and stored as
+keywords in the header.  The photometry source must be specified at
+this stage as one of the command line arguments, though the value used
+may be overridden in one of the stages belowe.  The resulting file can
+now stand on its own without reference to the original image.  The
+keywords used by {\tt fstat} include the minimum signal to noise, a
+rough guess at the zero point ({\tt ZERO\_PT}), and four numbers
+defining the format of the {\tt dophot} OBJ file.
+
+\subsection{Astrometry -- gastro}
+
+Astrometry is performed automatically by the program {\tt gastro}.
+{\tt gastro} loads a CMP file and determines an initial guess for the
+center coordinates (based on the RA and DEC header keywords).  The
+program also uses the configuration information about the plate scale
+and rough orientation of the image to get a close to the final
+solution.  Also, the true sky position of the telescope pole may be
+defined to allow astrometry on images taken close to the pole.  The
+comparison is made with the astrometric catalog, which may be the HST
+Guide Star Catalog, or it may be any source if the data is placed in
+the correct format.
+
+\section{The Photometry Database}
+
+\subsection{Photometry File Format}
+The photometry database consists of a large number of photometry files
+representing sections of the sky and a file containing information on
+the images incorporated in the database.  These files are called
+alternatively ``region files'' or CPT files (because they have the
+extension {\tt .cpt}).  The photometry files are sorted by Declination
+into different directories, each consisting of a band 7.5\degree\ 
+wide.  These directories have names like n0730, representing the band
+starting at a Declination of +07:30.  The image database (Images.dat)
+is stored in the upper level directory of this directory tree.  In
+fact, the locations of the Images.dat file, the photometry database,
+and such are all flexible and may be changed by altering the
+configuration file.
+
+The name and location of each sky region comes from the Hubble Space
+Telescope Guide Star Catalog.  There is a file, GSCregions.tbl, which
+can be used to find the region file appropriate for any location on
+the sky.  A number of routines exist to make such a query.  
+
+Each photometry file in the photometry database contains all
+observations and all average values for all stars observed within the
+appropriate region on the sky.  The identity of stars is essentially
+determined by the RA and DEC coordinates.  In very crowded regions,
+the end user may need to double check that a neighboring star is not
+contaminating individual detections (see the discusion on {\tt
+  addstar} below).
+
+The photometry files are stored in a binary format, both to reduce the
+volume and to speed access.  As a result, the interpretation of the
+data is machine dependent: little endian machines need to swap the
+data intelligently.  Fortunately, this operation is taken care of
+appropriately, if the programs are compiled with the BYTESWAP option
+set as needed, which is automatically set by the Makefile in the case
+of a Linux machine.  For reference, Suns, SGI, and HP are all big
+endian, while PCs and DECs are little endian.  The automatic
+conversion takes place by using the funcions {\tt Fread} and {\tt Fwrite}
+to substitute for the standard C library {\tt fread} and {\tt fwrite}
+functions.  {\tt Fread} and {\tt Fwrite} are told the datatype being read,
+and they know the layout of the bytes for a particular datatype.
+Anytime the definitions of the relevant structures (see below) are
+changed, {\tt Fread} and {\tt Fwrite} must be updated.  Fortunately, there
+is only one file ({\tt Fread.c}), and it has a single byteswapping
+section, making it easy to adjust the code appropriately.
+
+The format of a single photometry database file consists four
+sections:  
+\begin{itemize}
+\item {\bf header} -- this is a standard FITS header with room for comments
+  about the file or whatever.  Since the rest of the file is not a
+  standard FITS file, the SIMPLE keyword is set to False.  The three
+  necessary keywords are NSTARS, NMEAS, and NMISS, which define the
+  sizes of the next three sections.
+\item {\bf average} -- this section contains all average measurement
+  quantities for each star in the file.  There are NSTARS entries, and
+  each entry is the data from a structure of type Average (defined in
+  loneos.h, and discussed in more detail below).  Thus, the total size
+  in bytes of this section can be found by NSTARS*sizeof(Average).
+\item {\bf measure} -- this section contains the individual
+  measurements for each star in the database.  There are NMEAS
+  entries, and like the average section, each entry is a single
+  structure, in this case of type Measure (in loneos.h, see below).
+  All measurements of a single star are consecutive.  The number of
+  measurements for a single star is defined by average.Nm and the
+  starting entry for a single star is given by average.offset.
+  Therefore, the first measurement of a specific star average[500]
+  would be given by measure[average[500].offset], while the second is 
+  measure[average[500].offset + 1], and so forth.  
+\item {\bf missing} -- this section contains references to all missing
+  measurements of a star.  This means all of those occasions when an
+  image enclosed the location of a star in the database, but no star
+  was detected on the image.  Similar to the case of the measure
+  section, each star has a number of missing entries (which may be 0)
+  given by average.Nn, and the first missing entry is given by
+  average.missing.  Each missing entry value is again a structure of
+  type Missing.  Currently, the only data in the structure is the time
+  of the observation, which allows for an unambiguous recovery of the
+  image where the star was missed.
+\end{itemize}
+
+\subsubsection*{Average Structure defined in loneos.h}
+\begin{verbatim}
+typedef struct {
+  float R;                    /* RA  in decimal degrees */
+  float D;                    /* DEC in decimal degrees */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned short int Nm, Nn;  /* number of measurements, missing */
+  short int Xp, Xm;           /* log(chisq) values in tenths */
+  unsigned short int code;    /* an ID code (ie, star, ghost, satelite, etc) */
+  signed int offset;          /* offset to first measurement */
+  signed int missing;         /* offset to first missing obs */
+} Average; /* 28 bytes / Average */
+\end{verbatim}
+The structure above defines the average data stored in the photometry
+database for each star.  Most of the entries are self-explantory, but
+some need a bit of clarification.  The two $\chi^2$ entries ({\tt Xp}
+and {\tt Xm}) are the $\chi^2$ values of the average position and the
+average magnitude.  These are stored as logarthmic quantities because
+high dynamic range is not needed.  For the magnitude, the assumption
+is that the star brightness is constant and the $\chi^2$ incorporates
+the photometric errors on each measurement.  For the postion, the
+a default constant error is used \note{True?} and again the assumption
+is that the postion is constant.  As discussed above, the values of
+{\tt Nm} and {\tt Nn} give the number of measurements and missing
+observations for this star, while the entries {\tt offset} and {\tt
+  missing} point to the first {\bf measure} and {\bf missing} entry
+for this star.  The entry {\tt M} is the current best average
+magnitude solution for this star (see the section on Relative
+Photometry).  Finally, the entry {\tt code} is an ID code for each
+object, which may define a variety of things about the object.  For
+example, if the object is known to be variable, or if the object is
+associated with a USNO star, or if the object is a measurement of an
+asteroid.  See below for details of these definitions.
+
+\subsubsection*{Measure Structure defined in loneos.h}
+{\small
+\begin{verbatim}
+typedef struct {
+  short int dR, dD;           /* 1/100 of arcsec (-327.67 to +327.67 valid range) */
+  short int M;                /* thousandths of mag (-32.767 to 32.767 valid range) */
+  short int Mcal;             /* image cal mag, thousandths of mag (-32.767 to 32.767 valid range) */
+  unsigned char dM;           /* thousandths of mag (0.000 to 0.255 valid range) */
+  char dophot;                /* dophot type (1-9) */
+  unsigned short int source;  /* code to identify photometry source */
+  unsigned int   t;           /* time in seconds (0 - 143 years valid range) */
+  unsigned int average;       /* reference to corresponding average entry */
+  /* upper byte of Measure.average stores flags:
+   limit of 16,777,215 stars (Naverage) in a file (=0xFFFFFF) 
+   flags: average & 0x1000000 */
+} Measure; /* 20 bytes / Measure */
+\end{verbatim}
+}
+The structure above defines the individual measurement data stored for
+each observation of each star.  The first two entries, dR and dD, are
+the difference between the average coordinates for this star and the
+coordinates determined for this observation.  Clearly, if this
+residual is too large, the individual measurement will not be
+successfully matched with the average star position.  {\tt M} and {\tt
+  dM} are the instrumental magnitude and error for this measurement,
+after correction for the exposure time and a zero point (defined in
+the configuration file as ZERO\_PT).  The entry {\tt t} determines the
+time of the individual observation.  This is important not only for
+timing purposes, but also to determine the source image for this
+observation.  The time and the entry {\tt source} uniquely determine
+the image in the image database that generated this measurement.
+Furthermore, this correspondence allows for determination of the pixel
+coordinates on the chip, by going throught the image astrometry stored
+in the Image database (see below).  The entry {\tt source} is a code which
+identifies the particular CCD/Telescope/Filter setup.  Each unique
+{\tt source} should be treated as an independent filter set for the
+purposes of accurate relative photometric comparisons.  This entry
+also allows external catalog data, such as the USNO or Sloan
+photometry, to be added to the database, as desired.  The entry {\tt
+  average} is a pointer back to the {\bf average} section to allow
+determination of the star from the measurements, in addition to the
+reverse.  The bits of the upper byte of this value are reserved for
+flags used to define particular situations for this measurement.
+Possible values are:
+\begin{itemize}
+\item 1 = BLEND\_IMAGE - the star on the image matched more than one
+  catalog star
+\item 2 = BLEND\_CATALOG - the star in the catalog matched more than
+  one image star
+\item 4 = UPPER\_LIMIT - \note{not really used?}
+\item 8 = CALIBRATED - relative photometry has been performed at least
+  once.
+\end{itemize}
+The entry {\tt dophot} stores the dophot type for this particular
+measurement (1-9: four extra bits in this field).   
+Finally, the value {\tt Mcal} determines the photometric calibration
+of this specific measurment.  This value is an offset appropriate to
+this image (and if needed, this point in time and this chip position)
+to bring the observations of the stars on multiple images to a common
+system (see the section on relative photometry).
+
+\subsection{Image Database}
+
+All images for which photometry has been included in the photometry
+database also have entries in an image database.  The image database
+is (currently) a single file in the upper directory of the photometry
+database.  The name of the file is determined by the IMAGE\_CATALOG
+entry in the configuration file, and is currently set to Images.dat.
+Like the photometry database files, the image database consists of a
+FITS header followed by binary data.  There is only one type of binary
+data: each image has an entry, which is the data from a structure of
+type Image (loneos.h).  The number of images in the file is determined
+by the {\tt NIMAGES} keyword.  If the number of images becomes too
+large, extrapolation of the system to a set of image database files
+with a reference list is quite straightforeward, and could be modeled
+on the organization of the region files (eg, one image database file
+for each Declination directory).
+
+\subsubsection*{Image Structure defined in loneos.h}
+{\small
+\begin{verbatim}
+typedef struct {
+  Coords         coords;            /* 120 bytes */
+  unsigned int   tzero;             /* readout time row 0 in sec (0 - 142 years valid range) */
+  unsigned int   nstar;             /* number of stars on image */
+  short int      secz;              /* thousanths of airmass (valid range -32.000 -- 32.000) */
+  short int      NX, NY;            /* dimensions of image */
+  short int      apmifit, dapmifit; /* aperture correction and error in thousandths of mag */
+  short int      source;            /* identifier for CCD (each ever used will have a unique letter) */
+  short int      Mcal;              /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      dMcal;             /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      Xm;                /* 10*log(image chi-square) */
+  char           name[32];          /* name of original image */
+  unsigned char  detection_limit;   /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  saturation_limit;  /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  cerror;            /* astrometric error: 1/50 of arcsec (0 -- 5.12 valid range) */
+  unsigned char  fwhm_x, fwhm_y;    /* PSF terms in 25*arcsec (valid range 0.0 -- 10.2" ") */
+  unsigned char  trate;             /* 10000 * scan rate in sec/pix (0 -- 0.0256 valid range, 
+                                       typically 0.0146. this is used only to determine the time
+                                       of the observation, not to find the coordinates.  1 byte
+                                       gives 0.11 sec accuracy */
+  char           code;              /* flag to mark an image as bad or whatever */
+  char           dummy[7];          /* extra space for the future?  (seems like a lot!) */
+  float          exptime;           /* exposure time, seconds */
+} Image;  /* 192 bytes / Image */
+\end{verbatim}}
+The structure above lists all of the data stored for each image.  A
+substantial amount of information is stored for each image.  First,
+the astrometric calibration information is stored in a {\tt Coords}
+structure (see coordinate systems, above).  The upper and lower limits
+of the photometry in the database (in instrumental magnitudes) is
+stored in {\tt detection\_limit} and {\tt saturation\_limit}.  These
+values are useful for comparison for those stars where observations
+are missing.  The relationship between time and pixel coordinates is
+given by {\tt tzero} and {\tt trate}.  Drift images will have a scan
+rate stored in {\tt trate}, while staring images will have a value of
+0.0 for this entry ({\tt tzero} then applies to the whole image).
+Other important parameters are kept: the astrometric error ({\tt
+  cerror}), the airmass ({\tt secz}), the exposure time ({\tt
+  exptime}), the number of stars detected ({\tt nstar}), the FWHM
+values ({\tt fwhm\_x, fwhm\_y}), the image name ({\tt name}), and the
+size of the image ({\tt NX, NY}).  The entry {\tt source} is used as
+above to define the CCD/Telescope/Filter used for this observation.
+The entry {\tt code} is used to flag images in various ways.
+Currently, only one bit is used, to define if the image has
+unacceptable photometric scatter.  Seven extra bytes are stored for
+future additions.
+
+\subsection{Data Incorporation -- {\tt addstar}}
+
+Images which are completely processed ({\tt dophot, fstat, gastro}) are
+then incorporated into the photometry database with the program {\tt 
+addstar}.  This program decides which region files are appropriate
+for this particular image, then one-by-one adds stars from the image
+to the appropriate region file.  Stars already in the catalog are
+matched with stars in the new image purely by a positional comparison.
+In order to avoid the difficulty of comparisons in the RA and DEC
+coordinate frame, a cartesian projection is performed.  The stars from
+the image being processed and the database stars in the same area are
+projected onto a tangent plane and positional comparisons made in this
+(locally cartesian) coordinate frame.  This avoids the dangerous
+singularities at the pole and also makes the RA 0,360\degree\ boundary
+a trivial problem.  
+
+Several choices must be made in the comparison process.  If a star in
+the catalog is matched with a star in the image, the new measurements
+of that star are added to the database.  If a star in the database
+lies in the field of the image, but is not detected, this information
+is added to the list of missing data.  The only data stored in this
+case is the time and source, which is sufficient to unambiguously
+identify the source image from the image database.  This allows later
+programs to find relevant statistics from the image database, if
+necessary.  If a star is detected in the image, but is not already in
+the database, a new entry is added to the database.  In addition, the
+same ``missing data'' from all previous observations images which have
+covered this location (ie, images already in the image database) are
+included.  This last step is necessary so that the image processing
+order is not important.  
+
+Stars also run the danger of being crowded together.  Since all
+comparisons are performed on the basis of position alone, crowded
+fields may make for ambiguous cross-identifications.  A pair of stars
+are matched if the difference in their positions is less than a
+specified search radius (usually dependent on the astrometric for the
+image).  If more than one catalog stars is correlated with a star in
+an image, the new measurement is added to {\em both} catalog stars,
+and a flag is set noting that this observation had a blended IMAGE.
+Conversely, if a single catalog star is matched with more than one
+image star, both new measurements are added to the one catalog star,
+and a different flat is set, noting that this observation had a
+blended CATALOG.   
+
+\section{Catalog Update and Cleaning}
+
+Occasionally, once a large number of images have been added to the
+photometry database, a set of programs should be run to clean and
+update the CPT files.  This may best be done on CPT files after a
+night's worth of data is incorporated.  These routines define bad
+data, identify the USNO catalog stars, and search for asteroids and
+other junk.  Most of the results from these routines are the addition
+of object flags to the Average data structures (Average.code).  Most
+of these routines process data within a single CPT file at a time.
+
+\subsection{Markstar}
+
+The first of the update programs, {\tt markstar}, identifies bad data
+and flags it.  It makes three types of identifications: bright stars,
+ghost stars, and trails.  In the first case, it searches the HST guide
+star catalog for bright stars within the field of the current CPT
+file.  Three types of pixels are flagged for bright stars.  Any
+measurements within a circular region centered on the star are suspect
+(the ``halo'' of the bright star).  Second, points along the X axis
+are likely to contain flux from the diffraction spikes.  Finally,
+points along the Y axis are likely to have bleeding in addition to the
+diffraction spikes.  Each of these three regions has a different size
+and range which must be defined in the configuration file.  The
+regions are represented by a representative magnitude and a scale
+parameter.  For example, the diameter $d$ of the halo for a star with
+magnitude $m$ is defined as 
+$d = \mbox{BRIGHT\_HALO\_SLOPE} (m - \mbox{BRIGHT\_HALO\_MAG})$.  The X and
+Y axis points are defined by the length of the diffraction spike, with
+an equivalent formula, and the width of the spike.  Since these are
+each independent, there are 8 parameters to define the exclusion
+regions of bright stars.  One of the current drawbacks of this system
+is the reliance on the HST Guide Star photometry.  In the HST Guide
+Star Catalog, only one photometry band is given, so no color
+information is available.  The parameters currently used were defined
+based on a large number of bright stars, with no adjustment for the
+different effective filter system of the Loneos telescope and the HST
+GSC.  Thus, occasionally very red stars will be several magnitudes
+brighter in the real data than reported in the catalog.  As a result,
+large numbers of bad data points in these halos and spikes may be
+missed.  
+
+The second type of bad data flagged by {\tt markstar} are the trails
+from satelite and other space junk.  This portion of the program
+searches for objects which land along a line and which have a high
+linear density.  Parameters may be adjusted to define the width and
+the necessary density or number of points in the line.  Objects in
+lines are also only identified if all observations come from a common
+image.  If an object only has one measurement and it is in a trail,
+the entire Average is flagged to be bad.  However, if an object has
+many measurements and only one is bad, the object is only flagged as
+having some bad data points.  
+
+The third type of bad dat are ghost star images.  The HST GSC is
+searched for stars projected across the telescope optical axis, which
+is defined by the user.  All objects within a region of fixed size is
+marked for ghost stars brighter than a specific cutoff magnitude.
+Similar to the trails, the average is marked as bad only if all
+detections of an object are ghost images.  Otherwise, it is noted that
+some data may be bad.
+
+\subsection{Addusno}
+
+The program {\tt addusno} makes two types of identifications of stars
+with the USNO catalog.  First, it searches for coincidences within a
+small, specified radius.  The choice of this radius is somewhat
+tricky: it is important to catch the association, but also to avoid
+making too many associations.  Unfortunately, astrometric errors in
+the USNO catalog make it necessary to choose a surprisingly large
+radius of 5\asec.  To avoid making double matches (matching the two
+USNO stars to the same photometry database object), only the USNO
+object closest to the object is associated.  There are also a
+substantial number of stars with significant proper motion between the
+USNO epochs and the current date.  It is not unusual to see 8-15\asec\ 
+discrepancy for specific stars.  To catch these objects, any objects
+which do not match the USNO database, but which have more than a
+minimum number of measurements (2 or 3?) are searched for more distant
+USNO companions.  A large, user-specified radius is used for this
+search.  Of course, only USNO stars which are not already matched to
+the database may be candidates for the proper motion match.  This
+stage is suceptible to errors in that a slow-moving solar-system
+object may be associated with a faint USNO star.  For this reason,
+objects flagged as high proper motion stars should be taken with some
+caution.  Of course, during the {\tt addusno} stage, no objects
+already flagged as bad by {\tt markstar} will be matched with USNO
+stars.  
+
+\subsection{Markrock}
+
+Once stars have been identified with the USNO database, it is possible
+to search for asteroid detections.  The program {\tt markrock}
+searches within a specific CPT file for objects which are moving along
+straight lines in (X,Y,t) space.  In order for an object to be found
+by {\tt markrock}, it must have three points, each with only single
+measurements.  The third point must land within a specified distance
+of the line projected from the first two data points.  These
+comparisons are made in the three dimensional space of image position
+and time.  Also, objects are only accepted if they have a speed less
+than a threshold.  If an object is moving too fast, it will make a
+streak on each individual image and will probably be missed anyway.
+Moving objects detected in this way are flagged in the photometry
+database and are also written to a Rock database.  Currently, the Rock
+database just contains ASCII lists of the RA, DEC, Mag, and time for
+each detection.  In fact, the data are stored in pairs of detections,
+with the middle detection saved twice.  This format makes it easy to
+plot lines connecting the points using the connect-the-dots plotting
+style of status/kapa (see below).
+
+One of the current drawbacks of {\tt markrock} is the insistence on
+three observations with only 1 measurement each.  If an object is
+moving too slowly, two of the dectections of the object may be merged
+together.  Enough data is stored in the database to extract these
+individual measurements, so a more sophisticated search is possible.
+It is also currently the case that USNO proper motion stars are
+ignored, but some of these will be asteroid detections which are
+simply too close to a faint USNO stars (it has to be faint because it
+would otherwise be associated with a star in the photometry
+database).  
+
+\begin{figure}
+\psfig{file=flags.eps,width=9cm}
+\caption{\label{flags} 
+Pictoral representation of the Average.code flags.  The lower byte
+value is used for specific definitions of object types.
+}
+\end{figure}
+
+\subsection{Data Flags}
+
+The identifications made by the above routines are noted as a set of
+flags in the Average.code structure entry.  Some of these flags are
+mutually exclusive, so a certain bit may have more than one meaning,
+depending on the value of other bits.  Figure~\ref{flags} shows a
+pictoral representation of the meaning of the different bit fields.
+The uppermost bit (bit 15) determines if the object may be considered
+a ``fixed'' star or something which has no fixed location.  If it is
+not fixed, and this bit is 1, then there are several options.  The
+second uppermost bit (bit 14) will be set if this object is one of the
+three types of bad data flagged by {\tt markstar}.  The lower byte of
+code is used to define the type of bad data: ghost, trail, or bleed.
+Thus, any datapoints determined to be a satelite trail will have a
+code value of 0xc002 (49154 decimal).  If an object is a moving
+object, but not a bad datapoint, it may be identified as a ``rock''
+(an asteroid) by the program {\tt markrock}.  In this case, the flag
+for an asteroid is turned on (bit 13).  Thus anything identified as a
+rock by {\tt markrock} will have a code value of 0xa000.  The lower
+byte of code is not yet defined for asteroids, but could be reserved
+for distinguishing different classes of asteroids (ie, on the basis of
+orbital speeds, and so forth).  Objects which are fixed may have a
+variety of possible flags.  First, an object may be identified with a
+USNO star (bit 14).  It may be found to exhibit variability (bit 13),
+implying that the relative photometry routine should ignore it.  If
+may be a transient object (bit 12).  Note that, while an object
+associated with a USNO object may not be transient, an object which is
+variable may also not be associated with a USNO object.  Thus, we need
+seperate bits for variable vs transient.  Different types of variable
+objects may be represented with different values of the lower byte.
+For example, a Cepheid may have a specific lower byte code of 0x01,
+which an RR Lyra may have a lower byte code of 0x02.  Thus a Cepheid
+associated with the USNO catalog would have a total code value of
+0x6001. Finally, any of these types of non-moving objects may have
+some bad individual measurements (bit 12) or may be found to have a
+significant proper motion (bit 11).
+
+\section{Visualization -- Status}
+
+Visualization of the data in the photometry database can best be
+performed with the program {\tt status}.  This is a command-line
+driven program with a math and macro language which makes it easy to
+perform complex tasks.  
+
+First, a few notes about the user interface.  The interface has an
+interaction similar to {\tt tcsh}.  The arrows allow editing of
+previous commands.  You can also use emacs-like commands such as
+cntl-a to reach the beginning of the line and cntl-e to reach the end.
+There is command and file completion: if you type part of a command
+(as the first thing on a line) and then type tab, it will fill in as
+much as possible, until the word is not unique.  Typing tab twice at
+that point will list the possible endings.  For any but the first word
+on a line, the same thing will happen for the files in the current
+directory.  It is also possible to type just a fraction of a command,
+as long as it is unique.  An ambiguous command will list the possible
+alternatives.  For example:
+\begin{verbatim}
+status: c
+ambiguous command: c ( catalog cgrid clear create cursor )
+\end{verbatim}
+
+The shell is essentially an interpretive programming language.
+Variables are set as follows:
+\begin{verbatim}
+status: $fred = 10
+\end{verbatim}
+Any expression within curly brackets \{\} is assumed
+to be an arithmetical expression and is evaluated before the line is
+executed.  For example:
+\begin{verbatim}
+echo {$fred*dcos(45)}
+\end{verbatim}
+would give the response 7.07107.  There are math functions cos, sin,
+and tan, which operate on radian expressions, and also dcos, dsin,
+dtan, which operate on degree expressions.  There are also the
+equivalent inverse functions: eg., asin and dasin return radians and
+degrees, respectively.  The help section on Math defines all of the
+available math functions.  
+
+\subsection{Miscellaneous Commands}
+\begin{verbatim}
+!                         -- system call
+?                         -- list commands 
+??                        -- list variables 
+echo                      -- type this line 
+exec                      -- system call
+exit                      -- exit program 
+help                      -- get help on a function 
+output                    -- redirect output to file
+quit                      -- exit program 
+scan                      -- scan line from keyboard or file to variable 
+wait                      -- wait until return is typed
+which                     -- show command 
+\end{verbatim}
+Most of these are self-explanatory.  The command ?? prints the system
+variables.  The {\tt help} command will provide help on a single
+command or, without any arguments, will list all available help
+files (this includes general help not associated with a specific
+command).  
+
+\subsection{Shell Programing}
+\begin{verbatim}
+break                     -- escape from function 
+for                       -- loops 
+if                        -- logical cases 
+input                     -- read command lines from a file 
+macro                     -- deal with the macros 
+\end{verbatim}
+
+There are several options for programming in {\tt status}.  First, a
+file which contains a series of commands can be executed with {\tt
+  input (filename)}.  It is also possible to define macros which will
+behave much like regular commands.  A macro is defined by typing {\tt
+  macro name} or {\tt macro create name} followed by the commands.
+Arguments to the macro are assigned to the variables \$1 .. \$N and
+the number of arguments is given by \$0.  Macros may be defined in
+{\tt input} files, and in fact when {\tt status} is started, it loads
+the file {\tt \~/.statusrc} which may contain default macros.  Simple
+loops and if statements can be performed, and are quite useful for
+complex macros.  
+
+``If'' statements are similar in syntax to C if statements, but only
+the following logical operators are available: $>$, $<$, $=$, !, $|$,
+and \&.  Notice that (currently) there is no $>=$ or $<=$ symbol.  The
+operator ! means ``not equal to'', but cannot be used to negate a
+logical value.  The operators $|$ and \& have the meaning of ``or'' and
+``and'' respectively.  Math expresions in the if statement must be
+contained in curly braces, as elsewhere.  Variables with string values
+may use the logical $=$ operator to test if two strings are the same.
+``For'' loops are quite simplistic.  The form is:
+\begin{verbatim}
+for var first last delta
+ (commands)
+end
+\end{verbatim}
+The value of {\tt \$var} will start at the value {\tt first} and increment by
+{\tt delta} after each loop.  The loop will stop after {\tt \$var} is greater
+than {\tt stop}.  The value {\tt delta} is optional, with 1 assumed.
+The value of {\tt \$var} may be changed during the loop, and if set
+beyong the value of {\tt last} will end the loop early.  
+
+\subsection{Vector Plotting}
+
+\begin{verbatim}
+box                       -- draw a box on the plot
+clear                     -- erase plot
+create                    -- create a new vector
+cursor                    -- get coords from cursor
+grid                      -- plot cartesian grid
+hist                      -- create histogram from a vector
+labels                    -- define labels for plot
+limits                    -- define plot limits
+plot                      -- plot a pair of vectors
+print                     -- write vectors to file
+ps                        -- define labels for plot
+set                       -- vector math
+style                     -- set the style for graph plots
+vectors                   -- list vectors
+zplot                     -- plot scaled points 
+\end{verbatim}
+
+In addition to scalar variables, {\tt status} can manipulate and
+display 1-D vector variables.  Many of the commands which extract data
+from the photometry database place the data in vectors as well as
+plotting them.  A vector can also be created based on a number
+sequence with the command {\tt create name Nelements start delta}.
+The resulting vector has $Nelements$ entries, starting at a value of
+$start$ and running until $start + delta*Nelements$.  If $delta$ is
+0.0, all elements will have the value of $start$.  A histogram of a vector
+may be made with the command {\tt hist}, which creates a new vector
+containing the histogram of the first vector.  The data range and bin
+size of the histogram are defined in same way as with create.  This
+makes it easy to create the index vector that goes with a histogram
+vector:  
+\begin{verbatim}
+hist y Ny 1 100 0.1
+create dx 1 100 0.1
+\end{verbatim}
+The above will create a histogram of y in Ny and the index in dx.
+Plotting this with {\tt plot dx Ny} will show the histogram.
+
+Vector math is performed with a command of the form {\tt set new =
+  (expression)}.  The expression is some math function employing
+vectors and scalars.  A complete listing of the math operators
+available in {\tt set} can be found in the help for {\tt set}.
+
+Once vectors are defined, they may be plotted.  A pair of vectors can
+be plotted against each other if they have the same number of entries.
+The plotting is performed on the graphics window, Kapa.  There are
+actually several graphics windows available to {\tt status}, any of
+which may be used to plot at any time.  Some of the more complex
+operations default to either graphics window 0 or 1, depending on the
+context.  Except for those functions with a pre-defined window, all
+plotting functions apply to the current graphics window unless an
+option {\tt -n N} is given to specify a different window.  The
+plotting style is determined by the command {\tt style} which can set
+the line width, the line type (solid, dashed, dotted, etc), the point
+type (box, cross, etc), the point size, the color, and whether a pair
+of vectors is plotted as a sequence of points, a set of connected
+lines, or a histogram.  Some functions which make plots use their own
+styles, as discussed below.  The function {\tt limits} lets the user
+set the range of the plot axes, or check the current setting.  The
+command {\tt plot} will plot a pair of vectors on the current graphics
+window using the current plotting style for that window.  The command
+{\tt zplot} will plot a pair of vectors with the point size scaled by
+a third vector, with maximum and minimum point sizes representing
+specified values.  The {\tt cursor} command goes the other way: this
+command puts the Kapa window in cursor mode and waits for input from
+Kapa.  The user can then type any alphanumeric key on the graphics
+windows and will be told both the pointer location (in the graphics
+coordinates) and will have the coordinates stored in {\tt status}
+variables.  For example, by typing ``1'' in the sky display window,
+the RA and DEC of the pointer are stored in the variables {\tt \$R1}
+and {\tt \$D1}.  This command can be used to let the user define
+locations or regions of interest on the Kapa window. (Future addition:
+{\tt button}, which does the same with the mouse buttons).  
+
+\subsection{Database Functions}
+
+\begin{verbatim}
+gcat                        -- get catalog at location
+gimages                     -- get images at location
+gstar                       -- get star statistics
+extract                     -- extract average vectors from catalogs
+mextract                    -- extract measurement vectors from catalogs
+imstats                    -- plot image statistics
+imextract                   -- extract image vectors from database
+lcat                        -- list catalogs in display region
+cmatch                      -- match two catalogs
+\end{verbatim}
+
+There are a variety of other commands which directly refer to the
+photometry database.  Some of these functions extract data of various
+types from the database, others perform more complex plotting
+operations.  The commands listed above are those which simply extract
+data from the database.  The first three list information relevant to
+a specific RA, DEC location on the sky: {\tt gcat (RA) (DEC)} lists
+the catalog at the specified location and places the name in the
+variable {\tt \$CATNAME}, {\tt gimages (RA) (DEC)} lists all images
+which overlap the specified location, , {\tt gstars (RA) (DEC)
+  (RADIUS)} lists data about the stars within a specified radius of
+the specified location (all numbers above are given in decimal
+degrees).  Similarly, {\tt lcat} lists the catalogs in the region.
+Imstats lists statistics about each image
+
+The next three commands extract a specific piece of information from
+the photometry database and places it in a vector.  First, {\tt
+  extract} will extract average values for each star and place it in a
+vector.  Next, {\tt mextract} will extract measurement values for each
+star and place it in a vector: as a result a single star may have
+multiple entries in the measurement vectors.  Finally, {\tt imextract}
+will extract image statistics into vectors (not yet implemented).
+
+
+\begin{verbatim}
+catalog                    -- plot catalog stars
+cgrid                      -- plot sky coordinate grid
+cplot                      -- plot vectors in sky coordinates
+czplot                     -- plot scaled vectors in sky coordinates
+images                     -- plot image boxes
+imdense                    -- image density plot
+lcurve                     -- plot lightcurve for a star
+pcat                       -- plot catalog boundaries
+region                     -- define sky region for plot
+resid                      -- plot residuals
+simage                     -- plot stars in an image
+\end{verbatim}
+
+There are two types of database plotting functions: those that display
+or refer to the spatial charateristics of the data and those that
+refer to other types of charatersitics, such as the time domain.  The
+graphics window 0 is reserved for all plots of objects on the sky.
+The command {\tt region} defines the current sky coordinates for plots
+in graphic window 0.  The command {\tt pcat} plots the outline of all
+photometry database files which are within the currently defined
+region (and by default, only those with data).  {\tt images} plots the
+outline of the images in the image database, while {\tt imdense} shows
+the number of images at a location by randomly spacing dots within the
+boundary of the images.  The command {\tt cgrid}
+draws a grid in celestial coordinates on the for the current region.
+
+The most complex, but also one of the most useful command is {\tt
+  catalog}, which plots the positions of stars in the photometry
+database (and others) on the sky.  There are many options to this
+command.  One set allows the user to plot stars from the photometry
+database (the default), from the HST GSC, or from an ASCII text file
+with RA, DEC, and Mag in specified columns.  If the ASCII file has a
+fixed number of bytes per line, the data can be more quickly loaded.
+The size of the points may be scaled by the star magnitude, by the
+number of observations of the star, or by the number of missing
+datapoints for the star.  In addition, points may be plotted only if
+they land in specified magnitude ranges, or with specified numbers of
+measurements, or missed measurements.  Also, objects may be plotted
+only if they have a specified Average.code, so that only asteroids or
+only perfect stars may be plotted.  The plotted vectors may be saved,
+if desired, and the source catalog epoch may be specified as different
+from J2000 (only valid for ASCII data).
+
+Several other commands relate to non-spatial charateristics of images
+and stars.  {\tt lcurve} will plot a light curve for all stars within
+some radius of a point.  {\tt resid} plots the photometry residuals
+for a particular region file.  
+
+\section{Some Examples}
+
+\begin{figure}
+\psfig{file=allsky.ps,width=16cm}
+\caption{\label{allsky} 
+Map of the entire sky, and images added to database.
+}
+\end{figure}
+
+Fig.~\ref{allsky} shows a map of the entire sky, and the location
+of the images currently in the database.  This picture was made with
+the following commands: (output is not shown) \\
+\begin{verbatim}
+status: region 0 0 90 gls
+status: cgrid
+status: style -lw 2 -c red 
+status: images
+status: ps
+\end{verbatim}
+In this example, on the graphics window, the image boxes are shown in
+red.  The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth.  
+
+\begin{figure}
+\psfig{file=catalog.ps,width=9cm}
+\caption{\label{catalog} 
+Comparison between HST GSC and photometry database astrometry. 
+}
+\end{figure}
+
+Fig.~\ref{catalog} shows an example comparison of the photometry
+database star positions and the HST Guide Star Catalog star
+positions.  The crosses are from the photometry database while the
+boxes are from the HST GSC.  The size of both points is a function of
+brightness.  This plot was made with the following commands (starting
+from the previous image):
+\begin{verbatim}
+status: cursor  (typed 1 on region of interest)
+1 137.097858 22.698305
+q 137.097858 22.698305
+status: region $R1 $D1 0.5 TAN
+status: cgrid
+status: box
+status: style -pt 0 
+status: gcat $R1 $D1 
+  0 n2230/1951.cpt *
+status: catalog n2230/1951.cpt -m 12 18
+status: style -pt 2
+status: catalog n2230/1951.cpt -m 12 18 -g
+\end{verbatim}
+
+\begin{figure}
+\psfig{file=lightcurve.ps,width=9cm}
+\caption{\label{lightcurve} 
+Lightcurve for several image stars.
+}
+\end{figure}
+
+Fig.~\ref{lightcurve} shows an example lightcurve from three stars in
+the above field.  The x axis shows the time in seconds, which the y
+axis shows the star magnitudes.  This plot was made with the following commands (starting
+from the previous image):
+{\small
+\begin{verbatim}
+status: cursor
+1 137.062562 22.791860
+q 137.062562 22.791860
+status: lcurve $R1 $D1 0.01 -l
+status: box
+status: gstar $R1 $D1 0.01
+read 11318 stars from catalog file /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt (22462 total measurements)
+508 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.068619 22.785400 18.438000  1 (2)  1.000000 1.000000
+498 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.062820 22.792747 12.882000  3 (0)  1.000000 1.000000
+501 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.064499 22.800817 15.480000  3 (0)  1.000000 1.000000
+\end{verbatim}}
+The last lines show the database information on the three stars.  
+
+\section{Software Distribution}
+
+\section{Programming Details}
+
+\section {Comparison with USNO catalog}
+
+I have made several comparisons between the USNO catalog and results
+from running images from Loneos through the photometry pipeline.  In
+general, there is a good agreement between the astrometry of the two
+datasets.  There are specfic areas where the two disagree, however.
+First, as discussed above, the USNO star positions may be
+significantly different from the observed star positions simply
+because of proper motion.  The first picure in the ``Comparisons
+between Loneos + USNO'' pages shows several stars with significant
+proper motion.  These are currently being flagged in the photometry
+database as discussed.  Other differences evident in this first
+picture include the classification of bright stars and the splitting
+of faint and bright stars.  In the upper right of the image, two
+bright stars are visible, one of which is actually a double.  The USNO
+catalog does a good job of detecting both, but it also detects and
+includes as stars several diffraction spike points.  The HST GSC only
+detected a single point and did not split the star at all.  The Loneos
+data in our pipeline did not detect the star at all due to saturation.
+A star in the upper left corner shows the ability of {\tt dophot} in
+the Loneos pipeline to split double stars.  This star is, on close
+inspection, clearly a double.  The USNO catalog only detects one.  The
+Pipeline not only splits the two stars, but it also makes the correct
+cross-identification of the stars.  
+
+The second set of pictures shows the ability of the Loneos pipeline to
+deal with small galaxies.  There are two small galaxies in this field,
+both of which are broken into 4 or 5 objects by the USNO catalog, but
+maintained as single objects in the Loneos pipeline.  In another
+example (not shown) a bright star within the disk of a small galaxies
+was clearly split from the galaxy by the pipeline (and also by USNO).
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline2.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline2.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline2.tex	(revision 22322)
@@ -0,0 +1,1284 @@
+%\documentstyle[astro,psfig]{article}
+\documentclass{article}
+\usepackage{psfig}
+\usepackage{astro}
+\usepackage{times}
+\GoodMargins
+\newcommand{\note}[1]{({\bf #1})}
+\title{Variability Pipeline}
+\author{Eugene Magnier}
+\begin{document}
+\maketitle
+
+\section{Overview}
+
+The Loneos Pipeline provides automated reduction of images, including
+photometry and astrometry, with the final data stored in a photometry
+database organized by star position on the sky.
+
+Fig.~\ref{pipeline} shows a schematic of the analysis pipeline.
+Images are written to the disk from the camera by the software which
+runs it, {\tt astrocam}.  The resulting images are FITS files (which
+may be arbitrarily sized) and have header keyword parameters RA and
+DEC specifying rough celestial coordinates.  Images are first analyzed
+by {\tt dophot}, which produces instrumental photometry and pixel
+coordinate positions for stars and other objects in the images,
+writing files with a particular format, called OBJ files in this
+discussion.  The results from {\tt dophot} are cleaned up by a program
+called {\tt fstat} which merges the header information from the image
+with the most relevant parts of the OBJ file, creating a new file with
+the CMP format.  Astrometry is performed on the CMP file with the
+program {\tt gastro}.  {\tt Gastro} makes a comparison with an
+astrometric reference database and only changes CMP header keywords as
+a result.  Finally, the processed image CMP file is added to the
+photometry database with the program {\tt addstar}.  The photometry
+database is maintained in a large number of files (CPT files), each
+representing a small region of sky.  There is no relationship between
+the image locations and the boundaries of CPT file.  All of these
+processes occur on individual image, and would typically be performed
+while the observations are occuring, or during the day following, but
+may be performed at any time to add new or archived data to the
+database.
+
+Once large batches of images have been processed, a series of programs
+should be run to clean up the photometry database.  Typically, these
+routines would be run at the end of a night on all of the CPT files to
+which new data were added.  First, {\tt markstar} identifies bad data
+(the trails of satelites, the bleeding columns from stars, ghost
+images), and flags these data points appropriately.  Second, {\tt
+  addusno} matches the catalog stars with stars from the USNO database
+and adds the USNO magnitudes to the photometry database.  Next, {\tt
+  markrock} searches for likely asteroids in the CPT file, marks them,
+and writes the asteroid observations to an asteroid database.  It also
+notes measurements which are likely to be noise and cosmic rays.
+Finally, {\tt nrphot} determines relative photometric calibrations for
+observations in a set of CPT files.  
+
+With the exception of {\tt dophot}, all of the programs in the
+pipeline refer to a single configuration file to determine the large
+number of parameters necessary in the process.  The default parameter
+file is hardwired into the programs, but an alternate configuration
+file may be specified with the environment variable {\tt
+  PIPE\_CONFIG}.  This option makes independent processing of multiple
+data sources trivial.  For example, if the pipeline is to be used for
+both Loneos and SDSS image analysis and display, there could be two
+configuration files (eg, {\tt loneos.txt} and {\tt sdss.txt}), one for
+each data source.  Several important parameters would be different
+between these two, for example, the initial plate scale guess for the
+astrometry routine, or the parameters which define the rough chip
+orientation.  This scheme allows for analysis of different types of
+images, with a common destination photometry database, or for
+independent photometry databases.
+
+\begin{figure}
+\psfig{file=schematic.eps,width=9cm}
+\caption{\label{pipeline} 
+Schematic of the photometry pipeline.  Rectangles represent programs,
+while ovals represent data products.  Arrows show the direction of
+travel of information.  The flatten routine is a filter whose position
+may change as the pipeline matures.
+}
+\end{figure}
+
+Data from different sources can be processed by the pipeline and
+stored in the same photometry database, since all photometry is tagged
+with a source ID in the database (as are the images in the image
+database).  This allows us to include, for example, the USNO catalog
+magnitude measurements (or a subset) in the photometry database for a
+quick comparion.  Different sources may be considered different
+filters, so color-color and color-magnitude diagrams could be
+extracted by specifying choices for source catalog.  Currently each
+object has a single primary photometric system, and individual
+observations may be combined to determine that average (eg, if they
+are different photometry sources with roughly the filter bandpass) or
+they may be ignored when calculating relative magnitudes (eg, the USNO
+magnitudes would be ignored).  This may be expanded upon by defining
+multiple average magnitudes and allowing different measurements to
+contribute only to the appropriate average.  The determination of the
+photometry code for a particular image is currently not well defined.
+There are several points in the analysis where the user (or the
+controlling program) may specify the photometry source.  A table
+should be kept in the photometry catalog to define the various
+photometry codes.  
+
+\section{Image Analysis Pipeline}
+\label{parallel}
+\subsection{Photometry -- Dophot}
+
+Photometry is performed using a varient of {\tt Dophot}.  The version
+of dophot is based on Dophot 2.0, but it has been adapted to
+streamline processing of many files.  First, with this version of
+dophot, any FITS format data is safely read in.  Instead of compiling
+to a fixed maximum size, only the memory needed to contain the image
+matrix is allocated.  The image is also converted to REAL*32 from
+whatever BITPIX for processing.  Finally, only a single, complete
+parameter file is loaded by dophot.  There is no option for a modified
+and a default parameter file.  The parameter file to be used is
+defined, along with the input image and the output object list on the
+command line when the program is run: \\
+{\tt dophot image.fits image.obj parameter\_file} \\
+To achive this, the code from dophot was wrapped within a C program
+which interprets the command line arguments, allocates the necessary
+memory, and reads in the image.  The rest of the processing is
+performed by dophot in the standad manner.  Notice that we use a
+consistent naming convention through the pipeline reductions.  The
+image is called {\tt foo.fits}, while the resulting dophot-produced
+photometry list is called {\tt foo.obj}.  
+
+\subsection{Cleanup -- fstat}
+
+After an image is processed by dophot, the object file ({\tt foo.obj})
+is converted to a more-compact, more-complete file called a CMP file,
+with a name of the form ({\tt foo.cmp}).  This conversion is done with
+the program {\tt fstat}.  The CMP file consists of the FITS header
+from the original image ({\tt foo.fits}), with some additional
+keywords to be used at later stages in the analysis, followed by an
+ASCII list of the interesting data from ({\tt foo.obj}).  In this
+list, types 6 and 8 are excluded, and only the following values are
+kept:
+\begin{verbatim}
+X       Y     Mag   dMag t log(sky) 
+1342.0  106.1 14.166 000 4 3.2
+\end{verbatim}
+Objects with a signal-to-noise ratio lower than a specified cutoff
+({\tt MIN\_SN\_FSTAT}) are also excluded.  Some general information
+about the image is derived by {\tt fstat} (FWHM, saturation and
+completeness limits, number of each dophot type) and stored as
+keywords in the header.  The photometry source must be specified at
+this stage as one of the command line arguments, though the value used
+may be overridden in one of the stages belowe.  The resulting file can
+now stand on its own without reference to the original image.  The
+keywords used by {\tt fstat} include the minimum signal to noise, a
+rough guess at the zero point ({\tt ZERO\_PT}), and four numbers
+defining the format of the {\tt dophot} OBJ file.
+
+\subsection{Astrometry -- gastro}
+
+Astrometry is performed automatically by the program {\tt gastro}.
+{\tt gastro} loads a CMP file and determines an initial guess for the
+center coordinates (based on the RA and DEC header keywords).  The
+program also uses the configuration information about the plate scale
+and rough orientation of the image to get a close to the final
+solution.  Also, the true sky position of the telescope pole may be
+defined to allow astrometry on images taken close to the pole.  The
+comparison is made with the astrometric catalog, which may be the HST
+Guide Star Catalog, or it may be any source if the data is placed in
+the correct format.
+
+\section{The Photometry Database}
+
+\subsection{Photometry File Format}
+The photometry database consists of a large number of photometry files
+representing sections of the sky and a file containing information on
+the images incorporated in the database.  These files are called
+alternatively ``region files'' or CPT files (because they have the
+extension {\tt .cpt}).  The photometry files are sorted by Declination
+into different directories, each consisting of a band 7.5\degree\ 
+wide.  These directories have names like n0730, representing the band
+starting at a Declination of +07:30.  The image database (Images.dat)
+is stored in the upper level directory of this directory tree.  In
+fact, the locations of the Images.dat file, the photometry database,
+and such are all flexible and may be changed by altering the
+configuration file.
+
+The name and location of each sky region comes from the Hubble Space
+Telescope Guide Star Catalog.  There is a file, GSCregions.tbl, which
+can be used to find the region file appropriate for any location on
+the sky.  A number of routines exist to make such a query.  
+
+Each photometry file in the photometry database contains all
+observations and all average values for all stars observed within the
+appropriate region on the sky.  The identity of stars is essentially
+determined by the RA and DEC coordinates.  In very crowded regions,
+the end user may need to double check that a neighboring star is not
+contaminating individual detections (see the discusion on {\tt
+  addstar} below).
+
+The photometry files are stored in a binary format, both to reduce the
+volume and to speed access.  As a result, the interpretation of the
+data is machine dependent: little endian machines need to swap the
+data intelligently.  Fortunately, this operation is taken care of
+appropriately, if the programs are compiled with the BYTESWAP option
+set as needed, which is automatically set by the Makefile in the case
+of a Linux machine.  For reference, Suns, SGI, and HP are all big
+endian, while PCs and DECs are little endian.  The automatic
+conversion takes place by using the funcions {\tt Fread} and {\tt Fwrite}
+to substitute for the standard C library {\tt fread} and {\tt fwrite}
+functions.  {\tt Fread} and {\tt Fwrite} are told the datatype being read,
+and they know the layout of the bytes for a particular datatype.
+Anytime the definitions of the relevant structures (see below) are
+changed, {\tt Fread} and {\tt Fwrite} must be updated.  Fortunately, there
+is only one file ({\tt Fread.c}), and it has a single byteswapping
+section, making it easy to adjust the code appropriately.
+
+The format of a single photometry database file consists four
+sections:  
+\begin{itemize}
+\item {\bf header} -- this is a standard FITS header with room for comments
+  about the file or whatever.  Since the rest of the file is not a
+  standard FITS file, the SIMPLE keyword is set to False.  The three
+  necessary keywords are NSTARS, NMEAS, and NMISS, which define the
+  sizes of the next three sections.
+\item {\bf average} -- this section contains all average measurement
+  quantities for each star in the file.  There are NSTARS entries, and
+  each entry is the data from a structure of type Average (defined in
+  loneos.h, and discussed in more detail below).  Thus, the total size
+  in bytes of this section can be found by NSTARS*sizeof(Average).
+\item {\bf measure} -- this section contains the individual
+  measurements for each star in the database.  There are NMEAS
+  entries, and like the average section, each entry is a single
+  structure, in this case of type Measure (in loneos.h, see below).
+  All measurements of a single star are consecutive.  The number of
+  measurements for a single star is defined by average.Nm and the
+  starting entry for a single star is given by average.offset.
+  Therefore, the first measurement of a specific star average[500]
+  would be given by measure[average[500].offset], while the second is 
+  measure[average[500].offset + 1], and so forth.  
+\item {\bf missing} -- this section contains references to all missing
+  measurements of a star.  This means all of those occasions when an
+  image enclosed the location of a star in the database, but no star
+  was detected on the image.  Similar to the case of the measure
+  section, each star has a number of missing entries (which may be 0)
+  given by average.Nn, and the first missing entry is given by
+  average.missing.  Each missing entry value is again a structure of
+  type Missing.  Currently, the only data in the structure is the time
+  of the observation, which allows for an unambiguous recovery of the
+  image where the star was missed.
+\end{itemize}
+
+\subsubsection*{Average Structure defined in loneos.h}
+\begin{verbatim}
+typedef struct {
+  float R;                    /* RA  in decimal degrees */
+  float D;                    /* DEC in decimal degrees */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned short int Nm, Nn;  /* number of measurements, missing */
+  short int Xp, Xm;           /* log(chisq) values in tenths */
+  unsigned short int code;    /* an ID code (ie, star, ghost, satelite, etc) */
+  signed int offset;          /* offset to first measurement */
+  signed int missing;         /* offset to first missing obs */
+} Average; /* 28 bytes / Average */
+\end{verbatim}
+The structure above defines the average data stored in the photometry
+database for each star.  Most of the entries are self-explantory, but
+some need a bit of clarification.  The two $\chi^2$ entries ({\tt Xp}
+and {\tt Xm}) are the $\chi^2$ values of the average position and the
+average magnitude.  These are stored as logarthmic quantities because
+high dynamic range is not needed.  For the magnitude, the assumption
+is that the star brightness is constant and the $\chi^2$ incorporates
+the photometric errors on each measurement.  For the postion, the
+a default constant error is used \note{True?} and again the assumption
+is that the postion is constant.  As discussed above, the values of
+{\tt Nm} and {\tt Nn} give the number of measurements and missing
+observations for this star, while the entries {\tt offset} and {\tt
+  missing} point to the first {\bf measure} and {\bf missing} entry
+for this star.  The entry {\tt M} is the current best average
+magnitude solution for this star (see the section on Relative
+Photometry).  Finally, the entry {\tt code} is an ID code for each
+object, which may define a variety of things about the object.  For
+example, if the object is known to be variable, or if the object is
+associated with a USNO star, or if the object is a measurement of an
+asteroid.  See below for details of these definitions.
+
+\subsubsection*{Measure Structure defined in loneos.h}
+{\small
+\begin{verbatim}
+typedef struct {
+  short int dR, dD;           /* 1/100 of arcsec (-327.67 to +327.67 valid range) */
+  short int M;                /* thousandths of mag (-32.767 to 32.767 valid range) */
+  short int Mcal;             /* image cal mag, thousandths of mag (-32.767 to 32.767 valid range) */
+  unsigned char dM;           /* thousandths of mag (0.000 to 0.255 valid range) */
+  char dophot;                /* dophot type (1-9) */
+  unsigned short int source;  /* code to identify photometry source */
+  unsigned int   t;           /* time in seconds (0 - 143 years valid range) */
+  unsigned int average;       /* reference to corresponding average entry */
+  /* upper byte of Measure.average stores flags:
+   limit of 16,777,215 stars (Naverage) in a file (=0xFFFFFF) 
+   flags: average & 0x1000000 */
+} Measure; /* 20 bytes / Measure */
+\end{verbatim}
+}
+The structure above defines the individual measurement data stored for
+each observation of each star.  The first two entries, dR and dD, are
+the difference between the average coordinates for this star and the
+coordinates determined for this observation.  Clearly, if this
+residual is too large, the individual measurement will not be
+successfully matched with the average star position.  {\tt M} and {\tt
+  dM} are the instrumental magnitude and error for this measurement,
+after correction for the exposure time and a zero point (defined in
+the configuration file as ZERO\_PT).  The entry {\tt t} determines the
+time of the individual observation.  This is important not only for
+timing purposes, but also to determine the source image for this
+observation.  The time and the entry {\tt source} uniquely determine
+the image in the image database that generated this measurement.
+Furthermore, this correspondence allows for determination of the pixel
+coordinates on the chip, by going throught the image astrometry stored
+in the Image database (see below).  The entry {\tt source} is a code which
+identifies the particular CCD/Telescope/Filter setup.  Each unique
+{\tt source} should be treated as an independent filter set for the
+purposes of accurate relative photometric comparisons.  This entry
+also allows external catalog data, such as the USNO or Sloan
+photometry, to be added to the database, as desired.  The entry {\tt
+  average} is a pointer back to the {\bf average} section to allow
+determination of the star from the measurements, in addition to the
+reverse.  The bits of the upper byte of this value are reserved for
+flags used to define particular situations for this measurement.
+Possible values are:
+\begin{itemize}
+\item 1 = BLEND\_IMAGE - the star on the image matched more than one
+  catalog star
+\item 2 = BLEND\_CATALOG - the star in the catalog matched more than
+  one image star
+\item 4 = UPPER\_LIMIT - \note{not really used?}
+\item 8 = CALIBRATED - relative photometry has been performed at least
+  once.
+\end{itemize}
+The entry {\tt dophot} stores the dophot type for this particular
+measurement (1-9: four extra bits in this field).   
+Finally, the value {\tt Mcal} determines the photometric calibration
+of this specific measurment.  This value is an offset appropriate to
+this image (and if needed, this point in time and this chip position)
+to bring the observations of the stars on multiple images to a common
+system (see the section on relative photometry).
+
+\subsection{Image Database}
+
+All images for which photometry has been included in the photometry
+database also have entries in an image database.  The image database
+is (currently) a single file in the upper directory of the photometry
+database.  The name of the file is determined by the IMAGE\_CATALOG
+entry in the configuration file, and is currently set to Images.dat.
+Like the photometry database files, the image database consists of a
+FITS header followed by binary data.  There is only one type of binary
+data: each image has an entry, which is the data from a structure of
+type Image (loneos.h).  The number of images in the file is determined
+by the {\tt NIMAGES} keyword.  If the number of images becomes too
+large, extrapolation of the system to a set of image database files
+with a reference list is quite straightforeward, and could be modeled
+on the organization of the region files (eg, one image database file
+for each Declination directory).
+
+\subsubsection*{Image Structure defined in loneos.h}
+{\small
+\begin{verbatim}
+typedef struct {
+  Coords         coords;            /* 120 bytes */
+  unsigned int   tzero;             /* readout time row 0 in sec (0 - 142 years valid range) */
+  unsigned int   nstar;             /* number of stars on image */
+  short int      secz;              /* thousanths of airmass (valid range -32.000 -- 32.000) */
+  short int      NX, NY;            /* dimensions of image */
+  short int      apmifit, dapmifit; /* aperture correction and error in thousandths of mag */
+  short int      source;            /* identifier for CCD (each ever used will have a unique letter) */
+  short int      Mcal;              /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      dMcal;             /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      Xm;                /* 10*log(image chi-square) */
+  char           name[32];          /* name of original image */
+  unsigned char  detection_limit;   /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  saturation_limit;  /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  cerror;            /* astrometric error: 1/50 of arcsec (0 -- 5.12 valid range) */
+  unsigned char  fwhm_x, fwhm_y;    /* PSF terms in 25*arcsec (valid range 0.0 -- 10.2" ") */
+  unsigned char  trate;             /* 10000 * scan rate in sec/pix (0 -- 0.0256 valid range, 
+                                       typically 0.0146. this is used only to determine the time
+                                       of the observation, not to find the coordinates.  1 byte
+                                       gives 0.11 sec accuracy */
+  char           code;              /* flag to mark an image as bad or whatever */
+  char           dummy[7];          /* extra space for the future?  (seems like a lot!) */
+  float          exptime;           /* exposure time, seconds */
+} Image;  /* 192 bytes / Image */
+\end{verbatim}}
+The structure above lists all of the data stored for each image.  A
+substantial amount of information is stored for each image.  First,
+the astrometric calibration information is stored in a {\tt Coords}
+structure (see coordinate systems, above).  The upper and lower limits
+of the photometry in the database (in instrumental magnitudes) is
+stored in {\tt detection\_limit} and {\tt saturation\_limit}.  These
+values are useful for comparison for those stars where observations
+are missing.  The relationship between time and pixel coordinates is
+given by {\tt tzero} and {\tt trate}.  Drift images will have a scan
+rate stored in {\tt trate}, while staring images will have a value of
+0.0 for this entry ({\tt tzero} then applies to the whole image).
+Other important parameters are kept: the astrometric error ({\tt
+  cerror}), the airmass ({\tt secz}), the exposure time ({\tt
+  exptime}), the number of stars detected ({\tt nstar}), the FWHM
+values ({\tt fwhm\_x, fwhm\_y}), the image name ({\tt name}), and the
+size of the image ({\tt NX, NY}).  The entry {\tt source} is used as
+above to define the CCD/Telescope/Filter used for this observation.
+The entry {\tt code} is used to flag images in various ways.
+Currently, only one bit is used, to define if the image has
+unacceptable photometric scatter.  Seven extra bytes are stored for
+future additions.
+
+\subsection{Data Incorporation -- {\tt addstar}}
+
+Images which are completely processed ({\tt dophot, fstat, gastro}) are
+then incorporated into the photometry database with the program {\tt 
+addstar}.  This program decides which region files are appropriate
+for this particular image, then one-by-one adds stars from the image
+to the appropriate region file.  Stars already in the catalog are
+matched with stars in the new image purely by a positional comparison.
+In order to avoid the difficulty of comparisons in the RA and DEC
+coordinate frame, a cartesian projection is performed.  The stars from
+the image being processed and the database stars in the same area are
+projected onto a tangent plane and positional comparisons made in this
+(locally cartesian) coordinate frame.  This avoids the dangerous
+singularities at the pole and also makes the RA 0,360\degree\ boundary
+a trivial problem.  
+
+Several choices must be made in the comparison process.  If a star in
+the catalog is matched with a star in the image, the new measurements
+of that star are added to the database.  If a star in the database
+lies in the field of the image, but is not detected, this information
+is added to the list of missing data.  The only data stored in this
+case is the time and source, which is sufficient to unambiguously
+identify the source image from the image database.  This allows later
+programs to find relevant statistics from the image database, if
+necessary.  If a star is detected in the image, but is not already in
+the database, a new entry is added to the database.  In addition, the
+same ``missing data'' from all previous observations images which have
+covered this location (ie, images already in the image database) are
+included.  This last step is necessary so that the image processing
+order is not important.  
+
+Stars also run the danger of being crowded together.  Since all
+comparisons are performed on the basis of position alone, crowded
+fields may make for ambiguous cross-identifications.  A pair of stars
+are matched if the difference in their positions is less than a
+specified search radius (usually dependent on the astrometric for the
+image).  If more than one catalog stars is correlated with a star in
+an image, the new measurement is added to {\em both} catalog stars,
+and a flag is set noting that this observation had a blended IMAGE.
+Conversely, if a single catalog star is matched with more than one
+image star, both new measurements are added to the one catalog star,
+and a different flat is set, noting that this observation had a
+blended CATALOG.   
+
+\section{Catalog Update and Cleaning}
+
+Occasionally, once a large number of images have been added to the
+photometry database, a set of programs should be run to clean and
+update the CPT files.  This may best be done on CPT files after a
+night's worth of data is incorporated.  These routines define bad
+data, identify the USNO catalog stars, and search for asteroids and
+other junk.  Most of the results from these routines are the addition
+of object flags to the Average data structures (Average.code).  Most
+of these routines process data within a single CPT file at a time.
+
+\subsection{Markstar}
+
+The first of the update programs, {\tt markstar}, identifies bad data
+and flags it.  It makes three types of identifications: bright stars,
+ghost stars, and trails.  In the first case, it searches the HST guide
+star catalog for bright stars within the field of the current CPT
+file.  Three types of pixels are flagged for bright stars.  Any
+measurements within a circular region centered on the star are suspect
+(the ``halo'' of the bright star).  Second, points along the X axis
+are likely to contain flux from the diffraction spikes.  Finally,
+points along the Y axis are likely to have bleeding in addition to the
+diffraction spikes.  Each of these three regions has a different size
+and range which must be defined in the configuration file.  The
+regions are represented by a representative magnitude and a scale
+parameter.  For example, the diameter $d$ of the halo for a star with
+magnitude $m$ is defined as 
+$d = \mbox{BRIGHT\_HALO\_SLOPE} (m - \mbox{BRIGHT\_HALO\_MAG})$.  The X and
+Y axis points are defined by the length of the diffraction spike, with
+an equivalent formula, and the width of the spike.  Since these are
+each independent, there are 8 parameters to define the exclusion
+regions of bright stars.  One of the current drawbacks of this system
+is the reliance on the HST Guide Star photometry.  In the HST Guide
+Star Catalog, only one photometry band is given, so no color
+information is available.  The parameters currently used were defined
+based on a large number of bright stars, with no adjustment for the
+different effective filter system of the Loneos telescope and the HST
+GSC.  Thus, occasionally very red stars will be several magnitudes
+brighter in the real data than reported in the catalog.  As a result,
+large numbers of bad data points in these halos and spikes may be
+missed.  
+
+The second type of bad data flagged by {\tt markstar} are the trails
+from satelite and other space junk.  This portion of the program
+searches for objects which land along a line and which have a high
+linear density.  Parameters may be adjusted to define the width and
+the necessary density or number of points in the line.  Objects in
+lines are also only identified if all observations come from a common
+image.  If an object only has one measurement and it is in a trail,
+the entire Average is flagged to be bad.  However, if an object has
+many measurements and only one is bad, the object is only flagged as
+having some bad data points.  
+
+The third type of bad dat are ghost star images.  The HST GSC is
+searched for stars projected across the telescope optical axis, which
+is defined by the user.  All objects within a region of fixed size is
+marked for ghost stars brighter than a specific cutoff magnitude.
+Similar to the trails, the average is marked as bad only if all
+detections of an object are ghost images.  Otherwise, it is noted that
+some data may be bad.
+
+\subsection{Addusno}
+
+The program {\tt addusno} makes two types of identifications of stars
+with the USNO catalog.  First, it searches for coincidences within a
+small, specified radius.  The choice of this radius is somewhat
+tricky: it is important to catch the association, but also to avoid
+making too many associations.  Unfortunately, astrometric errors in
+the USNO catalog make it necessary to choose a surprisingly large
+radius of 5\asec.  To avoid making double matches (matching the two
+USNO stars to the same photometry database object), only the USNO
+object closest to the object is associated.  There are also a
+substantial number of stars with significant proper motion between the
+USNO epochs and the current date.  It is not unusual to see 8-15\asec\ 
+discrepancy for specific stars.  To catch these objects, any objects
+which do not match the USNO database, but which have more than a
+minimum number of measurements (2 or 3?) are searched for more distant
+USNO companions.  A large, user-specified radius is used for this
+search.  Of course, only USNO stars which are not already matched to
+the database may be candidates for the proper motion match.  This
+stage is suceptible to errors in that a slow-moving solar-system
+object may be associated with a faint USNO star.  For this reason,
+objects flagged as high proper motion stars should be taken with some
+caution.  Of course, during the {\tt addusno} stage, no objects
+already flagged as bad by {\tt markstar} will be matched with USNO
+stars.  
+
+\subsection{Markrock}
+
+Once stars have been identified with the USNO database, it is possible
+to search for asteroid detections.  The program {\tt markrock}
+searches within a specific CPT file for objects which are moving along
+straight lines in (X,Y,t) space.  In order for an object to be found
+by {\tt markrock}, it must have three points, each with only single
+measurements.  The third point must land within a specified distance
+of the line projected from the first two data points.  These
+comparisons are made in the three dimensional space of image position
+and time.  Also, objects are only accepted if they have a speed less
+than a threshold.  If an object is moving too fast, it will make a
+streak on each individual image and will probably be missed anyway.
+Moving objects detected in this way are flagged in the photometry
+database and are also written to a Rock database.  Currently, the Rock
+database just contains ASCII lists of the RA, DEC, Mag, and time for
+each detection.  In fact, the data are stored in pairs of detections,
+with the middle detection saved twice.  This format makes it easy to
+plot lines connecting the points using the connect-the-dots plotting
+style of status/kapa (see below).
+
+One of the current drawbacks of {\tt markrock} is the insistence on
+three observations with only 1 measurement each.  If an object is
+moving too slowly, two of the dectections of the object may be merged
+together.  Enough data is stored in the database to extract these
+individual measurements, so a more sophisticated search is possible.
+It is also currently the case that USNO proper motion stars are
+ignored, but some of these will be asteroid detections which are
+simply too close to a faint USNO stars (it has to be faint because it
+would otherwise be associated with a star in the photometry
+database).  
+
+\begin{figure}
+\psfig{file=flags.eps,width=9cm}
+\caption{\label{flags} 
+Pictoral representation of the Average.code flags.  The lower byte
+value is used for specific definitions of object types.
+}
+\end{figure}
+
+\subsection{Data Flags}
+
+The identifications made by the above routines are noted as a set of
+flags in the Average.code structure entry.  Some of these flags are
+mutually exclusive, so a certain bit may have more than one meaning,
+depending on the value of other bits.  Figure~\ref{flags} shows a
+pictoral representation of the meaning of the different bit fields.
+The uppermost bit (bit 15) determines if the object may be considered
+a ``fixed'' star or something which has no fixed location.  If it is
+not fixed, and this bit is 1, then there are several options.  The
+second uppermost bit (bit 14) will be set if this object is one of the
+three types of bad data flagged by {\tt markstar}.  The lower byte of
+code is used to define the type of bad data: ghost, trail, or bleed.
+Thus, any datapoints determined to be a satelite trail will have a
+code value of 0xc002 (49154 decimal).  If an object is a moving
+object, but not a bad datapoint, it may be identified as a ``rock''
+(an asteroid) by the program {\tt markrock}.  In this case, the flag
+for an asteroid is turned on (bit 13).  Thus anything identified as a
+rock by {\tt markrock} will have a code value of 0xa000.  The lower
+byte of code is not yet defined for asteroids, but could be reserved
+for distinguishing different classes of asteroids (ie, on the basis of
+orbital speeds, and so forth).  Objects which are fixed may have a
+variety of possible flags.  First, an object may be identified with a
+USNO star (bit 14).  It may be found to exhibit variability (bit 13),
+implying that the relative photometry routine should ignore it.  If
+may be a transient object (bit 12).  Note that, while an object
+associated with a USNO object may not be transient, an object which is
+variable may also not be associated with a USNO object.  Thus, we need
+seperate bits for variable vs transient.  Different types of variable
+objects may be represented with different values of the lower byte.
+For example, a Cepheid may have a specific lower byte code of 0x01,
+which an RR Lyra may have a lower byte code of 0x02.  Thus a Cepheid
+associated with the USNO catalog would have a total code value of
+0x6001. Finally, any of these types of non-moving objects may have
+some bad individual measurements (bit 12) or may be found to have a
+significant proper motion (bit 11).
+
+\section{Holding it Together}
+
+The preceeding sections describe the step-by-step analysis and
+incorporation of the data from individual images.  However, the power
+of the Pipeline comes in apply it to large numbers of images.  At
+LONEOS, we are using four Pentinum II computers to analyse the roughly 200
+images that are observed each night.  
+
+It is best to think of the analysis of images occurring on groups of
+images from a given night.  Of the set of processes discussed above,
+those in section \ref{parallel} are performed on individual images
+essentially independently of the other images.  The rest of the steps,
+both the data incorporation ({\tt addstar} and the database cleaning
+routines occur on the entire batch, one image at a time.  Thus, the
+former routines can easily be run in parallel, but the latter routines
+are run in serial to avoid having two programs writing to same part of
+the database at the same time.  In this model, we would process all of
+the images from a night through the parallel stage of the analysis and
+then run the serial stage only after the entire night is finished in
+the parallel stage.  We thus have a natural division of the pipeline
+into two stages.  
+
+The implementation of the two stages involves several programs, some
+of which are specific to the LONEOS problem, and others which are more
+general.  There are two layers of programs which enable batch
+processing of many images and repeated batch processing over many
+nights.  At the highest level, are Perl scripts which decide on a new
+set of data.  At the next level down, these scripts call two main C
+programs which run the images of a given night through the pipeline.  
+A fixed set of files keeps a log of the nights and images that have
+been processed, so the programs may know if a given night has already
+been analysed. 
+
+\subsection{lastnight}
+
+The Perl script {\tt lastnight} looks at the disks used by the
+observers to write the images from a given night.  Images are written
+to a directory called /pallas/d[1-6]/NNNNNN where NNNNNN is an
+abbreviation for the current night's date in the form YYMMDD (ie,
+990223).  All of the images are stored in this directory with names
+yyyyMMDDxxxxb.fits where yyyy is the full year, MM is month, DD is
+date, and xxxx is a sequence number.  The 'b' refers to the 'b' CCD,
+and the .fits extension is required.  The script {\tt lastnight} is
+called by a cron job scheduled for 7am on {\tt hebe.lowell.edu}.  The
+script searches the disks /pallas/d[1-6] for a directory of the right
+form (basically any directory beginning with a number).  By default,
+the program demands that the directory have a name of today's date,
+but with the flag -any, it will accept any directory.  It then checks
+to see if this directory have been already included in the
+'processed.log' file.  If not, it lists all files with the right
+ending (*b.fits) and places these names in a file.  It also writes the
+name of the directory (along with some other data) in a file
+'extracted.log'.  When it has successfully ended and identified some
+data, it calls the next script, {\tt stage1} and then quits.  
+
+\subsection{slurp}
+
+An alternative to {\tt lastnight} is the Perl script {\tt slurp},
+which looks for data on the tape archive.  This script looks at the
+tape.log file and compares it to the file processed.log.  It tries to
+identify nights listed in the tape.log which have not been included in
+the processed.log.  If it finds any, it tries to download as much of
+the data as will fit on the disk /pallas/d1.  This script is called
+from within the {\tt stage1} script and is invoked if {\tt stage1} is
+run without any data already in extracted.log.  
+
+\subsection{stage1}
+
+The Perl script {\tt stage1} takes the image list produced by {\tt
+lastnight} or {\tt slurp} and passes the list to the controller
+program {\tt control1} which performs the parallel analysis.  The
+script has a few features to decide which files have been analysed
+through the pipeline and to what extent.  If {\tt stage1} is called,
+it should be able to figure out 1) if there is data ready to be
+analysed, 2) if the data has been partially analysed, 3) which data
+needs to be run through which part of the pipeline, and so forth.
+These features are most useful if the analysis crashes in the middle
+or needs to be restarted.  The {\tt stage1} script is meant to be run
+without any arguments and it will figure out what needs to be done.
+The one possible argument is the -once flag.  This flag tells {\tt
+stage1} to analyse the data it finds and quit when it finishes, and
+not call another instance of itself.  The script writes a log file
+called \$scriptdir/stage1.NN.log where NN is a number in sequence
+between all runs of {\tt stage1}.  A file stage1.count keeps track of
+which number NN we are on.  When {\tt stage1} has finished the
+analysis of a night's data, it writes the date in processed.log to
+keep {\tt slurp} and {\tt lastnight} from re-running a night.
+
+\subsection{stage2}
+The serial stage processes are run by the Perl scrip {\tt stage2}.
+Normally, this script is always running.  It waits for the name of a
+night to be added to the processed.log file.  Once if finds a new name
+in this file, it looks for the appropriate images in the file
+YYMMDD.addlist.  It then submits the list to the second controlling
+program {\tt control2}, which runs the images through {\tt addstar}.
+Images which are successfully run through addstar have their *bf.fits
+(flattened image) and *.obj (dophot output) files deleted.  When the
+entire night is done, the rest of the *.obj and *bf.fits images are
+deleted by {\tt stage2}, the directory with the *.cmp and *.log files
+is tared and gzipped into the file YYMMNN.tgz.  The directory is then
+deleted.  Finally, {\tt stage2} calls the script {\tt cleaning.pl}
+which runs the cleanup programs ({\tt markstar - nrphot}) on the
+database.  After a successful run, {\tt stage2} will automatically
+respawn a new copy that waits until more data arrives.
+
+\subsection{control1 / control2}
+These two C-programs perform the tasks of farming the images out to
+the different machines for analysis.  The two programs are essentially
+identical, but {\tt control1} is allowed to use all machines and {\tt
+control2} can only use one (hebe.lowell.edu, which has the database
+local to it, mostly).  The two programs also run a different set of
+program.  These controller programs start off by executing remote
+shells (using ssh) on a list of machines.  These shells are then used
+to execute the remote commands and are kept open the entire time the
+process runs.  They maintain a list of images in a set of queues.  A
+set of rules defines the order in which each image travels through the
+queues.  Each queue is associated with a single process (flatten,
+astrometry, etc).  Images which successfully run through a queue are
+sent on to the defined 'next' queue, while images which fail are
+instead sent to the defined 'fail' queue.  In most cases, the 'fail'
+queue is a process which marks the image as bad and write the name in
+a file, and then puts the image in the 'done' queue.  However, it is
+possible for the 'fail' queue to be another attempt, such as in the
+case of astrometry.  In this example, images which fail the default
+astrometry are run through a second queue which tries again with the
+-loneos flag set (this flag makes the assumption that the header
+information about the LONEOS region number for the image is correct,
+not the RA/DEC information).
+
+The connections on the remote machines are handled as child processes
+with communication through the stdin and stdout/stderr pipes.
+Messages can be sent back and forth though these pipes, and messages
+which are received from a process associated with an image are written
+to a log file with the name imagename.log (where imagename is the root
+name of the image).  The only required messages from the program are
+messages saying ``SUCCESS'' or ``ERROR''.  The programs therefore all
+end with either of these words.  The programs are started within the
+controller as a command to the shell followed by and echo ``PROCESS
+DONE''.  If the controller sees the message ``PROCESS DONE'' without
+either ``SUCCESS'' or ``FAILURE'' (after waiting an appropriately long
+time), it assumes the program crashed.  Currently, no timeout is
+available for the programs.  The flat-field process is run in mana and
+is a slightly special case.  We avoid re-loading the same flat many
+times by starting a mana process once and running the flatten
+procedures as mana function calls.  The images which are analysed
+(with an indicator of success or failure at each stage) are written to
+a file named YYMMDD.addlist by the {\tt control1} and YYMMDD.outlist
+by {\tt control2}.
+
+\section{Lock files}
+
+A variety of locking mechanisms are used to avoid overwriting the
+database or running more than one version of the controlling
+programs.  there is a perl program, locks, which allows the user to
+set or check the state of the different locks. 
+
+\section{File locations}
+
+\begin{verbatim}
+database: /hebe/d27/database (also in /metis/d27/database as linked directories).
+ lock files also go in this directory
+workspace: /irene/d11/workspace - temporary storage of result files
+dumpspace: /pallas/d1 - disk to store data dumped from the archive by slurp.
+scriptdir: /metis/d11/logs - all log files
+\end{verbatim}
+
+\section{Visualization -- Status}
+
+Visualization of the data in the photometry database can best be
+performed with the program {\tt status}.  This is a command-line
+driven program with a math and macro language which makes it easy to
+perform complex tasks.  
+
+First, a few notes about the user interface.  The interface has an
+interaction similar to {\tt tcsh}.  The arrows allow editing of
+previous commands.  You can also use emacs-like commands such as
+cntl-a to reach the beginning of the line and cntl-e to reach the end.
+There is command and file completion: if you type part of a command
+(as the first thing on a line) and then type tab, it will fill in as
+much as possible, until the word is not unique.  Typing tab twice at
+that point will list the possible endings.  For any but the first word
+on a line, the same thing will happen for the files in the current
+directory.  It is also possible to type just a fraction of a command,
+as long as it is unique.  An ambiguous command will list the possible
+alternatives.  For example:
+\begin{verbatim}
+status: c
+ambiguous command: c ( catalog cgrid clear create cursor )
+\end{verbatim}
+
+The shell is essentially an interpretive programming language.
+Variables are set as follows:
+\begin{verbatim}
+status: $fred = 10
+\end{verbatim}
+Any expression within curly brackets \{\} is assumed
+to be an arithmetical expression and is evaluated before the line is
+executed.  For example:
+\begin{verbatim}
+echo {$fred*dcos(45)}
+\end{verbatim}
+would give the response 7.07107.  There are math functions cos, sin,
+and tan, which operate on radian expressions, and also dcos, dsin,
+dtan, which operate on degree expressions.  There are also the
+equivalent inverse functions: eg., asin and dasin return radians and
+degrees, respectively.  The help section on Math defines all of the
+available math functions.  
+
+\subsection{Miscellaneous Commands}
+\begin{verbatim}
+!                         -- system call
+?                         -- list commands 
+??                        -- list variables 
+echo                      -- type this line 
+exec                      -- system call
+exit                      -- exit program 
+help                      -- get help on a function 
+output                    -- redirect output to file
+quit                      -- exit program 
+scan                      -- scan line from keyboard or file to variable 
+wait                      -- wait until return is typed
+which                     -- show command 
+\end{verbatim}
+Most of these are self-explanatory.  The command ?? prints the system
+variables.  The {\tt help} command will provide help on a single
+command or, without any arguments, will list all available help
+files (this includes general help not associated with a specific
+command).  
+
+\subsection{Shell Programing}
+\begin{verbatim}
+break                     -- escape from function 
+for                       -- loops 
+if                        -- logical cases 
+input                     -- read command lines from a file 
+macro                     -- deal with the macros 
+\end{verbatim}
+
+There are several options for programming in {\tt status}.  First, a
+file which contains a series of commands can be executed with {\tt
+  input (filename)}.  It is also possible to define macros which will
+behave much like regular commands.  A macro is defined by typing {\tt
+  macro name} or {\tt macro create name} followed by the commands.
+Arguments to the macro are assigned to the variables \$1 .. \$N and
+the number of arguments is given by \$0.  Macros may be defined in
+{\tt input} files, and in fact when {\tt status} is started, it loads
+the file {\tt \~/.statusrc} which may contain default macros.  Simple
+loops and if statements can be performed, and are quite useful for
+complex macros.  
+
+``If'' statements are similar in syntax to C if statements, but only
+the following logical operators are available: $>$, $<$, $=$, !, $|$,
+and \&.  Notice that (currently) there is no $>=$ or $<=$ symbol.  The
+operator ! means ``not equal to'', but cannot be used to negate a
+logical value.  The operators $|$ and \& have the meaning of ``or'' and
+``and'' respectively.  Math expresions in the if statement must be
+contained in curly braces, as elsewhere.  Variables with string values
+may use the logical $=$ operator to test if two strings are the same.
+``For'' loops are quite simplistic.  The form is:
+\begin{verbatim}
+for var first last delta
+ (commands)
+end
+\end{verbatim}
+The value of {\tt \$var} will start at the value {\tt first} and increment by
+{\tt delta} after each loop.  The loop will stop after {\tt \$var} is greater
+than {\tt stop}.  The value {\tt delta} is optional, with 1 assumed.
+The value of {\tt \$var} may be changed during the loop, and if set
+beyong the value of {\tt last} will end the loop early.  
+
+\subsection{Vector Plotting}
+
+\begin{verbatim}
+box                       -- draw a box on the plot
+clear                     -- erase plot
+create                    -- create a new vector
+cursor                    -- get coords from cursor
+grid                      -- plot cartesian grid
+hist                      -- create histogram from a vector
+labels                    -- define labels for plot
+limits                    -- define plot limits
+plot                      -- plot a pair of vectors
+print                     -- write vectors to file
+ps                        -- define labels for plot
+set                       -- vector math
+style                     -- set the style for graph plots
+vectors                   -- list vectors
+zplot                     -- plot scaled points 
+\end{verbatim}
+
+In addition to scalar variables, {\tt status} can manipulate and
+display 1-D vector variables.  Many of the commands which extract data
+from the photometry database place the data in vectors as well as
+plotting them.  A vector can also be created based on a number
+sequence with the command {\tt create name Nelements start delta}.
+The resulting vector has $Nelements$ entries, starting at a value of
+$start$ and running until $start + delta*Nelements$.  If $delta$ is
+0.0, all elements will have the value of $start$.  A histogram of a vector
+may be made with the command {\tt hist}, which creates a new vector
+containing the histogram of the first vector.  The data range and bin
+size of the histogram are defined in same way as with create.  This
+makes it easy to create the index vector that goes with a histogram
+vector:  
+\begin{verbatim}
+hist y Ny 1 100 0.1
+create dx 1 100 0.1
+\end{verbatim}
+The above will create a histogram of y in Ny and the index in dx.
+Plotting this with {\tt plot dx Ny} will show the histogram.
+
+Vector math is performed with a command of the form {\tt set new =
+  (expression)}.  The expression is some math function employing
+vectors and scalars.  A complete listing of the math operators
+available in {\tt set} can be found in the help for {\tt set}.
+
+Once vectors are defined, they may be plotted.  A pair of vectors can
+be plotted against each other if they have the same number of entries.
+The plotting is performed on the graphics window, Kapa.  There are
+actually several graphics windows available to {\tt status}, any of
+which may be used to plot at any time.  Some of the more complex
+operations default to either graphics window 0 or 1, depending on the
+context.  Except for those functions with a pre-defined window, all
+plotting functions apply to the current graphics window unless an
+option {\tt -n N} is given to specify a different window.  The
+plotting style is determined by the command {\tt style} which can set
+the line width, the line type (solid, dashed, dotted, etc), the point
+type (box, cross, etc), the point size, the color, and whether a pair
+of vectors is plotted as a sequence of points, a set of connected
+lines, or a histogram.  Some functions which make plots use their own
+styles, as discussed below.  The function {\tt limits} lets the user
+set the range of the plot axes, or check the current setting.  The
+command {\tt plot} will plot a pair of vectors on the current graphics
+window using the current plotting style for that window.  The command
+{\tt zplot} will plot a pair of vectors with the point size scaled by
+a third vector, with maximum and minimum point sizes representing
+specified values.  The {\tt cursor} command goes the other way: this
+command puts the Kapa window in cursor mode and waits for input from
+Kapa.  The user can then type any alphanumeric key on the graphics
+windows and will be told both the pointer location (in the graphics
+coordinates) and will have the coordinates stored in {\tt status}
+variables.  For example, by typing ``1'' in the sky display window,
+the RA and DEC of the pointer are stored in the variables {\tt \$R1}
+and {\tt \$D1}.  This command can be used to let the user define
+locations or regions of interest on the Kapa window. (Future addition:
+{\tt button}, which does the same with the mouse buttons).  
+
+\subsection{Database Functions}
+
+\begin{verbatim}
+gcat                        -- get catalog at location
+gimages                     -- get images at location
+gstar                       -- get star statistics
+extract                     -- extract average vectors from catalogs
+mextract                    -- extract measurement vectors from catalogs
+imstats                    -- plot image statistics
+imextract                   -- extract image vectors from database
+lcat                        -- list catalogs in display region
+cmatch                      -- match two catalogs
+\end{verbatim}
+
+There are a variety of other commands which directly refer to the
+photometry database.  Some of these functions extract data of various
+types from the database, others perform more complex plotting
+operations.  The commands listed above are those which simply extract
+data from the database.  The first three list information relevant to
+a specific RA, DEC location on the sky: {\tt gcat (RA) (DEC)} lists
+the catalog at the specified location and places the name in the
+variable {\tt \$CATNAME}, {\tt gimages (RA) (DEC)} lists all images
+which overlap the specified location, , {\tt gstars (RA) (DEC)
+  (RADIUS)} lists data about the stars within a specified radius of
+the specified location (all numbers above are given in decimal
+degrees).  Similarly, {\tt lcat} lists the catalogs in the region.
+Imstats lists statistics about each image
+
+The next three commands extract a specific piece of information from
+the photometry database and places it in a vector.  First, {\tt
+  extract} will extract average values for each star and place it in a
+vector.  Next, {\tt mextract} will extract measurement values for each
+star and place it in a vector: as a result a single star may have
+multiple entries in the measurement vectors.  Finally, {\tt imextract}
+will extract image statistics into vectors (not yet implemented).
+
+
+\begin{verbatim}
+catalog                    -- plot catalog stars
+cgrid                      -- plot sky coordinate grid
+cplot                      -- plot vectors in sky coordinates
+czplot                     -- plot scaled vectors in sky coordinates
+images                     -- plot image boxes
+imdense                    -- image density plot
+lcurve                     -- plot lightcurve for a star
+pcat                       -- plot catalog boundaries
+region                     -- define sky region for plot
+resid                      -- plot residuals
+simage                     -- plot stars in an image
+\end{verbatim}
+
+There are two types of database plotting functions: those that display
+or refer to the spatial charateristics of the data and those that
+refer to other types of charatersitics, such as the time domain.  The
+graphics window 0 is reserved for all plots of objects on the sky.
+The command {\tt region} defines the current sky coordinates for plots
+in graphic window 0.  The command {\tt pcat} plots the outline of all
+photometry database files which are within the currently defined
+region (and by default, only those with data).  {\tt images} plots the
+outline of the images in the image database, while {\tt imdense} shows
+the number of images at a location by randomly spacing dots within the
+boundary of the images.  The command {\tt cgrid}
+draws a grid in celestial coordinates on the for the current region.
+
+The most complex, but also one of the most useful command is {\tt
+  catalog}, which plots the positions of stars in the photometry
+database (and others) on the sky.  There are many options to this
+command.  One set allows the user to plot stars from the photometry
+database (the default), from the HST GSC, or from an ASCII text file
+with RA, DEC, and Mag in specified columns.  If the ASCII file has a
+fixed number of bytes per line, the data can be more quickly loaded.
+The size of the points may be scaled by the star magnitude, by the
+number of observations of the star, or by the number of missing
+datapoints for the star.  In addition, points may be plotted only if
+they land in specified magnitude ranges, or with specified numbers of
+measurements, or missed measurements.  Also, objects may be plotted
+only if they have a specified Average.code, so that only asteroids or
+only perfect stars may be plotted.  The plotted vectors may be saved,
+if desired, and the source catalog epoch may be specified as different
+from J2000 (only valid for ASCII data).
+
+Several other commands relate to non-spatial charateristics of images
+and stars.  {\tt lcurve} will plot a light curve for all stars within
+some radius of a point.  {\tt resid} plots the photometry residuals
+for a particular region file.  
+
+\section{Some Examples}
+
+\begin{figure}
+\psfig{file=allsky.ps,width=16cm}
+\caption{\label{allsky} 
+Map of the entire sky, and images added to database.
+}
+\end{figure}
+
+Fig.~\ref{allsky} shows a map of the entire sky, and the location
+of the images currently in the database.  This picture was made with
+the following commands: (output is not shown) \\
+\begin{verbatim}
+status: region 0 0 90 gls
+status: cgrid
+status: style -lw 2 -c red 
+status: images
+status: ps
+\end{verbatim}
+In this example, on the graphics window, the image boxes are shown in
+red.  The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth.  
+
+\begin{figure}
+\psfig{file=catalog.ps,width=9cm}
+\caption{\label{catalog} 
+Comparison between HST GSC and photometry database astrometry. 
+}
+\end{figure}
+
+Fig.~\ref{catalog} shows an example comparison of the photometry
+database star positions and the HST Guide Star Catalog star
+positions.  The crosses are from the photometry database while the
+boxes are from the HST GSC.  The size of both points is a function of
+brightness.  This plot was made with the following commands (starting
+from the previous image):
+\begin{verbatim}
+status: cursor  (typed 1 on region of interest)
+1 137.097858 22.698305
+q 137.097858 22.698305
+status: region $R1 $D1 0.5 TAN
+status: cgrid
+status: box
+status: style -pt 0 
+status: gcat $R1 $D1 
+  0 n2230/1951.cpt *
+status: catalog n2230/1951.cpt -m 12 18
+status: style -pt 2
+status: catalog n2230/1951.cpt -m 12 18 -g
+\end{verbatim}
+
+\begin{figure}
+\psfig{file=lightcurve.ps,width=9cm}
+\caption{\label{lightcurve} 
+Lightcurve for several image stars.
+}
+\end{figure}
+
+Fig.~\ref{lightcurve} shows an example lightcurve from three stars in
+the above field.  The x axis shows the time in seconds, which the y
+axis shows the star magnitudes.  This plot was made with the following commands (starting
+from the previous image):
+{\small
+\begin{verbatim}
+status: cursor
+1 137.062562 22.791860
+q 137.062562 22.791860
+status: lcurve $R1 $D1 0.01 -l
+status: box
+status: gstar $R1 $D1 0.01
+read 11318 stars from catalog file /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt (22462 total measurements)
+508 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.068619 22.785400 18.438000  1 (2)  1.000000 1.000000
+498 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.062820 22.792747 12.882000  3 (0)  1.000000 1.000000
+501 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.064499 22.800817 15.480000  3 (0)  1.000000 1.000000
+\end{verbatim}}
+The last lines show the database information on the three stars.  
+
+\section{Software Distribution}
+
+\section{Programming Details}
+
+\section {Comparison with USNO catalog}
+
+I have made several comparisons between the USNO catalog and results
+from running images from Loneos through the photometry pipeline.  In
+general, there is a good agreement between the astrometry of the two
+datasets.  There are specfic areas where the two disagree, however.
+First, as discussed above, the USNO star positions may be
+significantly different from the observed star positions simply
+because of proper motion.  The first picure in the ``Comparisons
+between Loneos + USNO'' pages shows several stars with significant
+proper motion.  These are currently being flagged in the photometry
+database as discussed.  Other differences evident in this first
+picture include the classification of bright stars and the splitting
+of faint and bright stars.  In the upper right of the image, two
+bright stars are visible, one of which is actually a double.  The USNO
+catalog does a good job of detecting both, but it also detects and
+includes as stars several diffraction spike points.  The HST GSC only
+detected a single point and did not split the star at all.  The Loneos
+data in our pipeline did not detect the star at all due to saturation.
+A star in the upper left corner shows the ability of {\tt dophot} in
+the Loneos pipeline to split double stars.  This star is, on close
+inspection, clearly a double.  The USNO catalog only detects one.  The
+Pipeline not only splits the two stars, but it also makes the correct
+cross-identification of the stars.  
+
+The second set of pictures shows the ability of the Loneos pipeline to
+deal with small galaxies.  There are two small galaxies in this field,
+both of which are broken into 4 or 5 objects by the USNO catalog, but
+maintained as single objects in the Loneos pipeline.  In another
+example (not shown) a bright star within the disk of a small galaxies
+was clearly split from the galaxy by the pipeline (and also by USNO).
+
+\appendix
+\section{Parameter file params.txt}
+\begin{verbatim}
+# Configuration file for Loneos analysis pipeline
+
+# important files and directories
+GSCFILE                 /host/pikake/d1/gene/gsc/GSCregions.tbl
+GSC_DIR                 /host/pikake/d1/gene/gsc
+CATDIR                  /host/radon/rad3/gene/photcat2
+IMAGE_CATALOG           /host/radon/rad3/gene/photcat2/Images.dat
+IMAGE_CATALOG_TEMPLATE  /host/radon/rad3/gene/photcat2/template.cat
+CATALOG_TEMPLATE        /host/radon/rad3/gene/photcat2/template.cat
+ROCK_CATALOG            /host/radon/rad3/gene/photcat2/Rocks.dat
+USNO_CDROM              /host/tycho/mnt/md0/kevin/zones
+LONEOS_REGIONS          /host/pikake/h1/gene/loneos3/regions.map
+
+# parameters for "fstat"
+# instrumental mag zero point
+ZERO_PT                 24.5
+DOPHOT_CHAR_LINE        129
+DOPHOT_TYPE_FIELD       5
+DOPHOT_AP_FIELD         91
+DOPHOT_PSF_FIELD        52
+MIN_SN_FSTAT            6.0
+
+# parameters for "gastro"
+DEFAULT_RADIUS   32.0
+MINIMUM_RADIUS   1.5
+MIN_ERROR        0.6
+MIN_PRECISE      0.12
+CCD_PC1_1        1
+CCD_PC2_2        -1    # for loneos
+CCD_PC1_2        0
+CCD_PC2_1        0
+ASEC_PIX         2.82
+NFIELD           3.0
+MMIN             6.0
+ROT_ZERO         0
+dROT             1.0
+NROT             2
+POLAR_AXIS_RA    -90.0
+POLAR_AXIS_DEC   89.88
+RA_OFFSET        -90.0
+DEC_OFFSET        0.0
+
+# parameters for "addstar"
+# airmass extinction coefficient (in mag / airmass)
+NSIGMA                  3.0
+ALPHA                   0.1
+XOVERSCAN               60
+YOVERSCAN               50
+
+# parameters for "controller"
+IN_DIR                  /host/pikake/h1/gene
+OUT_DIR                 /host/pikake/h1/gene/loneos4/
+DOPHOT_PARAMS           /host/pikake/h1/gene/src/ohana/config/default_parameters
+FLATTEN_SCRIPT          /host/pikake/h1/gene/src/ohana/config/test.pro
+NEW_IMAGES              images.dat
+USERNAME                gene
+PASSWORD                gene
+
+# here we list all available machines
+NMACHINES               2
+MACHINE0                pikake.astro.washington.edu
+MACHINE1                mildew.astro.washington.edu
+
+# here we define which machines which are always free
+fMACHINE0               1
+fMACHINE1               0
+
+# here we define the machine which runs the database 
+LOCAL_MACHINE           0
+
+# parameters for "status"
+TIME_REFERENCE          90/01/01 00:00:00
+
+# parameters for "nrphot"
+# make this one small to boost change of getting an early region 
+TAU                     3.0             
+SCATTER_LIM             15.0
+MAG_LIM                 17.0
+IMAGE_SCATTER           0.075
+NIMAGE_SCATTER          1.5
+STAR_SCATTER            0.05
+
+### parameters for "markstar"
+SEARCH_RADIUS           360   # region for initial trail hunt, in arcsec 
+TRAIL_WIDTH             5.0   # expected trail width, in arcsec
+NANGLE_BINS             180   # Number of angular bins in 180 degrees
+NPTSINLINE              5     # minimum points needed for trail
+MIN_DENSITY             0.025 # minimum linear density in star/arcsec
+SPACE_SIGMA             10.0  # how much denser than average for trail?
+# parameters defining bright star exclusion regions
+BRIGHT_XTRAIL_WIDTH       5.0 # faintest sat. star
+BRIGHT_XTRAIL_MAG         9.5 # faintest sat. star
+BRIGHT_XTRAIL_SLOPE     -80.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_YTRAIL_WIDTH       5.0 # faintest sat. star
+BRIGHT_YTRAIL_MAG        11.0 # faintest sat. star
+BRIGHT_YTRAIL_SLOPE    -400.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_HALO_MAG           8.5 # faintest sat. star
+BRIGHT_HALO_SLOPE       -28.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+# parameters which define the ghosts
+GHOST_MAG               7.5
+GHOST_RADIUS            200   # in arcsec
+OPTICAL_AXIS1           2154.0
+OPTICAL_AXIS2           2193.0
+
+# parameters for "addusno"
+USNO_RADIUS             5.0   # in arcsec
+USNO_PROPER             20.0   # in arcsec
+USNO_RED                1000  # code for USNO Red data
+USNO_BLUE               1001  # code for USNO Blue data
+# /mnt/cdrom
+
+# parameters for "markrock"
+ROCK_RADIUS             2.0   # in arcsec
+ROCK_MAX_RADIUS         20.0  # in arcsec
+ROCK_MAX_SPEED          0.1   # in arcsec / sec
+\end{verbatim}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline3.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline3.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline3.tex	(revision 22322)
@@ -0,0 +1,1463 @@
+%\documentstyle[astro,psfig]{article}
+\documentclass{article}
+\usepackage{psfig}
+\usepackage{astro}
+\usepackage{times}
+\GoodMargins
+\newcommand{\note}[1]{({\bf #1})}
+\title{Variability Pipeline}
+\author{Eugene Magnier}
+\begin{document}
+\maketitle
+
+\section{Overview}
+
+The Loneos Pipeline provides automated reduction of images, including
+photometry and astrometry, with the final data stored in a photometry
+database organized by star position on the sky.
+
+Fig.~\ref{pipeline} shows a schematic of the analysis pipeline.
+Images are written to the disk from the camera by the software which
+runs it, {\tt astrocam}.  The resulting images are FITS files (which
+may be arbitrarily sized) and have header keyword parameters RA and
+DEC specifying rough celestial coordinates.  Images are first analyzed
+by {\tt dophot}, which produces instrumental photometry and pixel
+coordinate positions for stars and other objects in the images,
+writing files with a particular format, called OBJ files in this
+discussion.  The results from {\tt dophot} are cleaned up by a program
+called {\tt fstat} which merges the header information from the image
+with the most relevant parts of the OBJ file, creating a new file with
+the CMP format.  Astrometry is performed on the CMP file with the
+program {\tt gastro}.  {\tt Gastro} makes a comparison with an
+astrometric reference database and only changes CMP header keywords as
+a result.  Finally, the processed image CMP file is added to the
+photometry database with the program {\tt addstar}.  The photometry
+database is maintained in a large number of files (CPT files), each
+representing a small region of sky.  There is no relationship between
+the image locations and the boundaries of CPT file.  All of these
+processes occur on individual image, and would typically be performed
+while the observations are occuring, or during the day following, but
+may be performed at any time to add new or archived data to the
+database.
+
+Once large batches of images have been processed, a series of programs
+should be run to clean up the photometry database.  Typically, these
+routines would be run at the end of a night on all of the CPT files to
+which new data were added.  First, {\tt markstar} identifies bad data
+(the trails of satelites, the bleeding columns from stars, ghost
+images), and flags these data points appropriately.  Second, {\tt
+  addusno} matches the catalog stars with stars from the USNO database
+and adds the USNO magnitudes to the photometry database.  Next, {\tt
+  markrock} searches for likely asteroids in the CPT file, marks them,
+and writes the asteroid observations to an asteroid database.  It also
+notes measurements which are likely to be noise and cosmic rays.
+Finally, {\tt nrphot} determines relative photometric calibrations for
+observations in a set of CPT files.  
+
+With the exception of {\tt dophot}, all of the programs in the
+pipeline refer to a single configuration file to determine the large
+number of parameters necessary in the process.  The default parameter
+file is hardwired into the programs, but an alternate configuration
+file may be specified with the environment variable {\tt
+  PIPE\_CONFIG}.  This option makes independent processing of multiple
+data sources trivial.  For example, if the pipeline is to be used for
+both Loneos and SDSS image analysis and display, there could be two
+configuration files (eg, {\tt loneos.txt} and {\tt sdss.txt}), one for
+each data source.  Several important parameters would be different
+between these two, for example, the initial plate scale guess for the
+astrometry routine, or the parameters which define the rough chip
+orientation.  This scheme allows for analysis of different types of
+images, with a common destination photometry database, or for
+independent photometry databases.
+
+\begin{figure}
+\psfig{file=schematic.eps,width=9cm}
+\caption{\label{pipeline} 
+Schematic of the photometry pipeline.  Rectangles represent programs,
+while ovals represent data products.  Arrows show the direction of
+travel of information.  The flatten routine is a filter whose position
+may change as the pipeline matures.
+}
+\end{figure}
+
+Data from different sources can be processed by the pipeline and
+stored in the same photometry database, since all photometry is tagged
+with a source ID in the database (as are the images in the image
+database).  This allows us to include, for example, the USNO catalog
+magnitude measurements (or a subset) in the photometry database for a
+quick comparion.  Different sources may be considered different
+filters, so color-color and color-magnitude diagrams could be
+extracted by specifying choices for source catalog.  Currently each
+object has a single primary photometric system, and individual
+observations may be combined to determine that average (eg, if they
+are different photometry sources with roughly the filter bandpass) or
+they may be ignored when calculating relative magnitudes (eg, the USNO
+magnitudes would be ignored).  This may be expanded upon by defining
+multiple average magnitudes and allowing different measurements to
+contribute only to the appropriate average.  The determination of the
+photometry code for a particular image is currently not well defined.
+There are several points in the analysis where the user (or the
+controlling program) may specify the photometry source.  A table
+should be kept in the photometry catalog to define the various
+photometry codes.  
+
+\section{Image Analysis Pipeline}
+\label{parallel}
+\subsection{Photometry -- Dophot}
+
+Photometry is performed using a varient of {\tt Dophot}.  The version
+of dophot is based on Dophot 2.0, but it has been adapted to
+streamline processing of many files.  First, with this version of
+dophot, any FITS format data is safely read in.  Instead of compiling
+to a fixed maximum size, only the memory needed to contain the image
+matrix is allocated.  The image is also converted to REAL*32 from
+whatever BITPIX for processing.  Finally, only a single, complete
+parameter file is loaded by dophot.  There is no option for a modified
+and a default parameter file.  The parameter file to be used is
+defined, along with the input image and the output object list on the
+command line when the program is run: \\
+{\tt dophot image.fits image.obj parameter\_file} \\
+To achive this, the code from dophot was wrapped within a C program
+which interprets the command line arguments, allocates the necessary
+memory, and reads in the image.  The rest of the processing is
+performed by dophot in the standad manner.  Notice that we use a
+consistent naming convention through the pipeline reductions.  The
+image is called {\tt foo.fits}, while the resulting dophot-produced
+photometry list is called {\tt foo.obj}.  
+
+\subsection{Cleanup -- fstat}
+
+After an image is processed by dophot, the object file ({\tt foo.obj})
+is converted to a more-compact, more-complete file called a CMP file,
+with a name of the form ({\tt foo.cmp}).  This conversion is done with
+the program {\tt fstat}.  The CMP file consists of the FITS header
+from the original image ({\tt foo.fits}), with some additional
+keywords to be used at later stages in the analysis, followed by an
+ASCII list of the interesting data from ({\tt foo.obj}).  In this
+list, types 6 and 8 are excluded, and only the following values are
+kept:
+\begin{verbatim}
+X       Y     Mag   dMag t log(sky) 
+1342.0  106.1 14.166 000 4 3.2
+\end{verbatim}
+Objects with a signal-to-noise ratio lower than a specified cutoff
+({\tt MIN\_SN\_FSTAT}) are also excluded.  Some general information
+about the image is derived by {\tt fstat} (FWHM, saturation and
+completeness limits, number of each dophot type) and stored as
+keywords in the header.  The photometry source must be specified at
+this stage as one of the command line arguments, though the value used
+may be overridden in one of the stages belowe.  The resulting file can
+now stand on its own without reference to the original image.  The
+keywords used by {\tt fstat} include the minimum signal to noise, a
+rough guess at the zero point ({\tt ZERO\_PT}), and four numbers
+defining the format of the {\tt dophot} OBJ file.
+
+\subsection{Astrometry -- gastro}
+
+Astrometry is performed automatically by the program {\tt gastro}.
+{\tt gastro} loads a CMP file and determines an initial guess for the
+center coordinates (based on the RA and DEC header keywords).  The
+program also uses the configuration information about the plate scale
+and rough orientation of the image to get a close to the final
+solution.  Also, the true sky position of the telescope pole may be
+defined to allow astrometry on images taken close to the pole.  The
+comparison is made with the astrometric catalog, which may be the HST
+Guide Star Catalog, or it may be any source if the data is placed in
+the correct format.
+
+\section{The Photometry Database}
+
+\subsection{Photometry File Format}
+The photometry database consists of a large number of photometry files
+representing sections of the sky and a file containing information on
+the images incorporated in the database.  These files are called
+alternatively ``region files'' or CPT files (because they have the
+extension {\tt .cpt}).  The photometry files are sorted by Declination
+into different directories, each consisting of a band 7.5\degree\ 
+wide.  These directories have names like n0730, representing the band
+starting at a Declination of +07:30.  The image database (Images.dat)
+is stored in the upper level directory of this directory tree.  In
+fact, the locations of the Images.dat file, the photometry database,
+and such are all flexible and may be changed by altering the
+configuration file.
+
+The name and location of each sky region comes from the Hubble Space
+Telescope Guide Star Catalog.  There is a file, GSCregions.tbl, which
+can be used to find the region file appropriate for any location on
+the sky.  A number of routines exist to make such a query.  
+
+Each photometry file in the photometry database contains all
+observations and all average values for all stars observed within the
+appropriate region on the sky.  The identity of stars is essentially
+determined by the RA and DEC coordinates.  In very crowded regions,
+the end user may need to double check that a neighboring star is not
+contaminating individual detections (see the discusion on {\tt
+  addstar} below).
+
+The photometry files are stored in a binary format, both to reduce the
+volume and to speed access.  As a result, the interpretation of the
+data is machine dependent: little endian machines need to swap the
+data intelligently.  Fortunately, this operation is taken care of
+appropriately, if the programs are compiled with the BYTESWAP option
+set as needed, which is automatically set by the Makefile in the case
+of a Linux machine.  For reference, Suns, SGI, and HP are all big
+endian, while PCs and DECs are little endian.  The automatic
+conversion takes place by using the funcions {\tt Fread} and {\tt Fwrite}
+to substitute for the standard C library {\tt fread} and {\tt fwrite}
+functions.  {\tt Fread} and {\tt Fwrite} are told the datatype being read,
+and they know the layout of the bytes for a particular datatype.
+Anytime the definitions of the relevant structures (see below) are
+changed, {\tt Fread} and {\tt Fwrite} must be updated.  Fortunately, there
+is only one file ({\tt Fread.c}), and it has a single byteswapping
+section, making it easy to adjust the code appropriately.
+
+The format of a single photometry database file consists four
+sections:  
+\begin{itemize}
+\item {\bf header} -- this is a standard FITS header with room for comments
+  about the file or whatever.  Since the rest of the file is not a
+  standard FITS file, the SIMPLE keyword is set to False.  The three
+  necessary keywords are NSTARS, NMEAS, and NMISS, which define the
+  sizes of the next three sections.
+\item {\bf average} -- this section contains all average measurement
+  quantities for each star in the file.  There are NSTARS entries, and
+  each entry is the data from a structure of type Average (defined in
+  loneos.h, and discussed in more detail below).  Thus, the total size
+  in bytes of this section can be found by NSTARS*sizeof(Average).
+\item {\bf measure} -- this section contains the individual
+  measurements for each star in the database.  There are NMEAS
+  entries, and like the average section, each entry is a single
+  structure, in this case of type Measure (in loneos.h, see below).
+  All measurements of a single star are consecutive.  The number of
+  measurements for a single star is defined by average.Nm and the
+  starting entry for a single star is given by average.offset.
+  Therefore, the first measurement of a specific star average[500]
+  would be given by measure[average[500].offset], while the second is 
+  measure[average[500].offset + 1], and so forth.  
+\item {\bf missing} -- this section contains references to all missing
+  measurements of a star.  This means all of those occasions when an
+  image enclosed the location of a star in the database, but no star
+  was detected on the image.  Similar to the case of the measure
+  section, each star has a number of missing entries (which may be 0)
+  given by average.Nn, and the first missing entry is given by
+  average.missing.  Each missing entry value is again a structure of
+  type Missing.  Currently, the only data in the structure is the time
+  of the observation, which allows for an unambiguous recovery of the
+  image where the star was missed.
+\end{itemize}
+
+\subsubsection*{Average Structure defined in loneos.h}
+\begin{verbatim}
+typedef struct {
+  float R;                    /* RA  in decimal degrees */
+  float D;                    /* DEC in decimal degrees */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned short int Nm, Nn;  /* number of measurements, missing */
+  short int Xp;               /* scatter in 1/100 arcsec (-327.67 to +327.67 valid range) */
+  short int Xm;               /* 1000*log(chisq) for magnitude measurement */
+  unsigned short int code;    /* an ID code (ie, star, ghost, satelite, etc) */
+  signed int offset;          /* offset to first measurement */
+  signed int missing;         /* offset to first missing obs */
+} Average; /* 28 bytes / Average */
+\end{verbatim}
+The structure above defines the average data stored in the photometry
+database for each star.  Most of the entries are self-explantory, but
+some need a bit of clarification.  The two $\chi^2$ entries ({\tt Xp}
+and {\tt Xm}) are the $\chi^2$ values of the average position and the
+average magnitude.  These are stored as logarthmic quantities because
+high dynamic range is not needed.  For the magnitude, the assumption
+is that the star brightness is constant and the $\chi^2$ incorporates
+the photometric errors on each measurement.  For the postion, the
+a default constant error is used \note{True?} and again the assumption
+is that the postion is constant.  As discussed above, the values of
+{\tt Nm} and {\tt Nn} give the number of measurements and missing
+observations for this star, while the entries {\tt offset} and {\tt
+  missing} point to the first {\bf measure} and {\bf missing} entry
+for this star.  The entry {\tt M} is the current best average
+magnitude solution for this star (see the section on Relative
+Photometry).  Finally, the entry {\tt code} is an ID code for each
+object, which may define a variety of things about the object.  For
+example, if the object is known to be variable, or if the object is
+associated with a USNO star, or if the object is a measurement of an
+asteroid.  See below for details of these definitions.
+
+\subsubsection*{Measure Structure defined in loneos.h}
+{\small
+\begin{verbatim}
+typedef struct {
+  short int dR, dD;           /* 1/100 of arcsec (-327.67 to +327.67 valid range) */
+  short int M;                /* thousandths of mag (-32.767 to 32.767 valid range) */
+  short int Mcal;             /* image cal mag, thousandths of mag (-32.767 to 32.767 valid range) */
+  unsigned char dM;           /* thousandths of mag (0.000 to 0.255 valid range) */
+  char dophot;                /* dophot type (1-9) */
+  unsigned short int source;  /* code to identify photometry source */
+  unsigned int   t;           /* time in seconds (0 - 143 years valid range) */
+  unsigned int average;       /* reference to corresponding average entry */
+  /* upper byte of Measure.average stores flags:
+   limit of 16,777,215 stars (Naverage) in a file (=0xFFFFFF) 
+   flags: average & 0x1000000 */
+} Measure; /* 20 bytes / Measure */
+\end{verbatim}
+}
+The structure above defines the individual measurement data stored for
+each observation of each star.  The first two entries, dR and dD, are
+the difference between the average coordinates for this star and the
+coordinates determined for this observation.  Clearly, if this
+residual is too large, the individual measurement will not be
+successfully matched with the average star position.  {\tt M} and {\tt
+  dM} are the instrumental magnitude and error for this measurement,
+after correction for the exposure time and a zero point (defined in
+the configuration file as ZERO\_PT).  The entry {\tt t} determines the
+time of the individual observation.  This is important not only for
+timing purposes, but also to determine the source image for this
+observation.  The time and the entry {\tt source} uniquely determine
+the image in the image database that generated this measurement.
+Furthermore, this correspondence allows for determination of the pixel
+coordinates on the chip, by going throught the image astrometry stored
+in the Image database (see below).  The entry {\tt source} is a code which
+identifies the particular CCD/Telescope/Filter setup.  Each unique
+{\tt source} should be treated as an independent filter set for the
+purposes of accurate relative photometric comparisons.  This entry
+also allows external catalog data, such as the USNO or Sloan
+photometry, to be added to the database, as desired.  The entry {\tt
+  average} is a pointer back to the {\bf average} section to allow
+determination of the star from the measurements, in addition to the
+reverse.  The bits of the upper byte of this value are reserved for
+flags used to define particular situations for this measurement.
+Possible values are:
+\begin{itemize}
+\item 1 = BLEND\_IMAGE - the star on the image matched more than one
+  catalog star
+\item 2 = BLEND\_CATALOG - the star in the catalog matched more than
+  one image star
+\item 4 = UPPER\_LIMIT - \note{not really used?}
+\item 8 = CALIBRATED - relative photometry has been performed at least
+  once.
+\end{itemize}
+The entry {\tt dophot} stores the dophot type for this particular
+measurement (1-9: four extra bits in this field).   
+Finally, the value {\tt Mcal} determines the photometric calibration
+of this specific measurment.  This value is an offset appropriate to
+this image (and if needed, this point in time and this chip position)
+to bring the observations of the stars on multiple images to a common
+system (see the section on relative photometry).
+
+\subsection{Image Database}
+
+All images for which photometry has been included in the photometry
+database also have entries in an image database.  The image database
+is (currently) a single file in the upper directory of the photometry
+database.  The name of the file is determined by the IMAGE\_CATALOG
+entry in the configuration file, and is currently set to Images.dat.
+Like the photometry database files, the image database consists of a
+FITS header followed by binary data.  There is only one type of binary
+data: each image has an entry, which is the data from a structure of
+type Image (loneos.h).  The number of images in the file is determined
+by the {\tt NIMAGES} keyword.  If the number of images becomes too
+large, extrapolation of the system to a set of image database files
+with a reference list is quite straightforeward, and could be modeled
+on the organization of the region files (eg, one image database file
+for each Declination directory).
+
+\subsubsection*{Image Structure defined in loneos.h}
+{\small
+\begin{verbatim}
+typedef struct {
+  Coords         coords;            /* 120 bytes */
+  unsigned int   tzero;             /* readout time row 0 in sec (0 - 142 years valid range) */
+  unsigned int   nstar;             /* number of stars on image */
+  short int      secz;              /* thousanths of airmass (valid range -32.000 -- 32.000) */
+  short int      NX, NY;            /* dimensions of image */
+  short int      apmifit, dapmifit; /* aperture correction and error in thousandths of mag */
+  short int      source;            /* identifier for CCD (each ever used will have a unique letter) */
+  short int      Mcal;              /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      dMcal;             /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      Xm;                /* 10*log(image chi-square) */
+  char           name[32];          /* name of original image */
+  unsigned char  detection_limit;   /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  saturation_limit;  /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  cerror;            /* astrometric error: 1/50 of arcsec (0 -- 5.12 valid range) */
+  unsigned char  fwhm_x, fwhm_y;    /* PSF terms in 25*arcsec (valid range 0.0 -- 10.2" ") */
+  unsigned char  trate;             /* 10000 * scan rate in sec/pix (0 -- 0.0256 valid range, 
+                                       typically 0.0146. this is used only to determine the time
+                                       of the observation, not to find the coordinates.  1 byte
+                                       gives 0.11 sec accuracy */
+  float          exptime;           /* exposure time, seconds */
+  char           code;              /* flag to mark an image as bad or whatever */
+  char           dummy[21];         /* extra space for the future */
+  short int      order;             /* number of terms used for Mrel */
+  short int      Mx, My;
+  short int      Mxx, Mxy, Myy;
+  short int      Mxxx, Mxxy, Mxyy, Myyy;
+  short int      Mxxxx, Mxxxy, Mxxyy, Mxyyy, Myyyy;
+} Image;  /* 240 bytes / Image */
+\end{verbatim}}
+The structure above lists all of the data stored for each image.  A
+substantial amount of information is stored for each image.  First,
+the astrometric calibration information is stored in a {\tt Coords}
+structure (see coordinate systems, above).  The upper and lower limits
+of the photometry in the database (in instrumental magnitudes) is
+stored in {\tt detection\_limit} and {\tt saturation\_limit}.  These
+values are useful for comparison for those stars where observations
+are missing.  The relationship between time and pixel coordinates is
+given by {\tt tzero} and {\tt trate}.  Drift images will have a scan
+rate stored in {\tt trate}, while staring images will have a value of
+0.0 for this entry ({\tt tzero} then applies to the whole image).
+Other important parameters are kept: the astrometric error ({\tt
+  cerror}), the airmass ({\tt secz}), the exposure time ({\tt
+  exptime}), the number of stars detected ({\tt nstar}), the FWHM
+values ({\tt fwhm\_x, fwhm\_y}), the image name ({\tt name}), and the
+size of the image ({\tt NX, NY}).  The entry {\tt source} is used as
+above to define the CCD/Telescope/Filter used for this observation.
+The entry {\tt code} is used to flag images in various ways.
+Currently, only one bit is used, to define if the image has
+unacceptable photometric scatter.  Seven extra bytes are stored for
+future additions.
+
+\subsection{Data Incorporation -- {\tt addstar}}
+
+Images which are completely processed ({\tt dophot, fstat, gastro}) are
+then incorporated into the photometry database with the program {\tt 
+addstar}.  This program decides which region files are appropriate
+for this particular image, then one-by-one adds stars from the image
+to the appropriate region file.  Stars already in the catalog are
+matched with stars in the new image purely by a positional comparison.
+In order to avoid the difficulty of comparisons in the RA and DEC
+coordinate frame, a cartesian projection is performed.  The stars from
+the image being processed and the database stars in the same area are
+projected onto a tangent plane and positional comparisons made in this
+(locally cartesian) coordinate frame.  This avoids the dangerous
+singularities at the pole and also makes the RA 0,360\degree\ boundary
+a trivial problem.  
+
+Several choices must be made in the comparison process.  If a star in
+the catalog is matched with a star in the image, the new measurements
+of that star are added to the database.  If a star in the database
+lies in the field of the image, but is not detected, this information
+is added to the list of missing data.  The only data stored in this
+case is the time and source, which is sufficient to unambiguously
+identify the source image from the image database.  This allows later
+programs to find relevant statistics from the image database, if
+necessary.  If a star is detected in the image, but is not already in
+the database, a new entry is added to the database.  In addition, the
+same ``missing data'' from all previous observations images which have
+covered this location (ie, images already in the image database) are
+included.  This last step is necessary so that the image processing
+order is not important.  
+
+Stars also run the danger of being crowded together.  Since all
+comparisons are performed on the basis of position alone, crowded
+fields may make for ambiguous cross-identifications.  A pair of stars
+are matched if the difference in their positions is less than a
+specified search radius (usually dependent on the astrometric for the
+image).  If more than one catalog stars is correlated with a star in
+an image, the new measurement is added to {\em both} catalog stars,
+and a flag is set noting that this observation had a blended IMAGE.
+Conversely, if a single catalog star is matched with more than one
+image star, both new measurements are added to the one catalog star,
+and a different flat is set, noting that this observation had a
+blended CATALOG.   
+
+\section{Catalog Update and Cleaning}
+
+Occasionally, once a large number of images have been added to the
+photometry database, a set of programs should be run to clean and
+update the CPT files.  This may best be done on CPT files after a
+night's worth of data is incorporated.  These routines define bad
+data, identify the USNO catalog stars, and search for asteroids and
+other junk.  Most of the results from these routines are the addition
+of object flags to the Average data structures (Average.code).  Most
+of these routines process data within a single CPT file at a time.
+
+\subsection{Markstar}
+
+The first of the update programs, {\tt markstar}, identifies bad data
+and flags it.  It makes three types of identifications: bright stars,
+ghost stars, and trails.  In the first case, it searches the HST guide
+star catalog for bright stars within the field of the current CPT
+file.  Three types of pixels are flagged for bright stars.  Any
+measurements within a circular region centered on the star are suspect
+(the ``halo'' of the bright star).  Second, points along the X axis
+are likely to contain flux from the diffraction spikes.  Finally,
+points along the Y axis are likely to have bleeding in addition to the
+diffraction spikes.  Each of these three regions has a different size
+and range which must be defined in the configuration file.  The
+regions are represented by a representative magnitude and a scale
+parameter.  For example, the diameter $d$ of the halo for a star with
+magnitude $m$ is defined as 
+$d = \mbox{BRIGHT\_HALO\_SLOPE} (m - \mbox{BRIGHT\_HALO\_MAG})$.  The X and
+Y axis points are defined by the length of the diffraction spike, with
+an equivalent formula, and the width of the spike.  Since these are
+each independent, there are 8 parameters to define the exclusion
+regions of bright stars.  One of the current drawbacks of this system
+is the reliance on the HST Guide Star photometry.  In the HST Guide
+Star Catalog, only one photometry band is given, so no color
+information is available.  The parameters currently used were defined
+based on a large number of bright stars, with no adjustment for the
+different effective filter system of the Loneos telescope and the HST
+GSC.  Thus, occasionally very red stars will be several magnitudes
+brighter in the real data than reported in the catalog.  As a result,
+large numbers of bad data points in these halos and spikes may be
+missed.  
+
+The second type of bad data flagged by {\tt markstar} are the trails
+from satelite and other space junk.  This portion of the program
+searches for objects which land along a line and which have a high
+linear density.  Parameters may be adjusted to define the width and
+the necessary density or number of points in the line.  Objects in
+lines are also only identified if all observations come from a common
+image.  If an object only has one measurement and it is in a trail,
+the entire Average is flagged to be bad.  However, if an object has
+many measurements and only one is bad, the object is only flagged as
+having some bad data points.  
+
+The third type of bad dat are ghost star images.  The HST GSC is
+searched for stars projected across the telescope optical axis, which
+is defined by the user.  All objects within a region of fixed size is
+marked for ghost stars brighter than a specific cutoff magnitude.
+Similar to the trails, the average is marked as bad only if all
+detections of an object are ghost images.  Otherwise, it is noted that
+some data may be bad.
+
+\subsection{Addusno}
+
+The program {\tt addusno} makes two types of identifications of stars
+with the USNO catalog.  First, it searches for coincidences within a
+small, specified radius.  The choice of this radius is somewhat
+tricky: it is important to catch the association, but also to avoid
+making too many associations.  Unfortunately, astrometric errors in
+the USNO catalog make it necessary to choose a surprisingly large
+radius of 5\asec.  To avoid making double matches (matching the two
+USNO stars to the same photometry database object), only the USNO
+object closest to the object is associated.  There are also a
+substantial number of stars with significant proper motion between the
+USNO epochs and the current date.  It is not unusual to see 8-15\asec\ 
+discrepancy for specific stars.  To catch these objects, any objects
+which do not match the USNO database, but which have more than a
+minimum number of measurements (2 or 3?) are searched for more distant
+USNO companions.  A large, user-specified radius is used for this
+search.  Of course, only USNO stars which are not already matched to
+the database may be candidates for the proper motion match.  This
+stage is suceptible to errors in that a slow-moving solar-system
+object may be associated with a faint USNO star.  For this reason,
+objects flagged as high proper motion stars should be taken with some
+caution.  Of course, during the {\tt addusno} stage, no objects
+already flagged as bad by {\tt markstar} will be matched with USNO
+stars.  
+
+\subsection{Markrock}
+
+Once stars have been identified with the USNO database, it is possible
+to search for asteroid detections.  The program {\tt markrock}
+searches within a specific CPT file for objects which are moving along
+straight lines in (X,Y,t) space.  In order for an object to be found
+by {\tt markrock}, it must have three points, each with only single
+measurements.  The third point must land within a specified distance
+of the line projected from the first two data points.  These
+comparisons are made in the three dimensional space of image position
+and time.  Also, objects are only accepted if they have a speed less
+than a threshold.  If an object is moving too fast, it will make a
+streak on each individual image and will probably be missed anyway.
+Moving objects detected in this way are flagged in the photometry
+database and are also written to a Rock database.  Currently, the Rock
+database just contains ASCII lists of the RA, DEC, Mag, and time for
+each detection.  In fact, the data are stored in pairs of detections,
+with the middle detection saved twice.  This format makes it easy to
+plot lines connecting the points using the connect-the-dots plotting
+style of status/kapa (see below).
+
+One of the current drawbacks of {\tt markrock} is the insistence on
+three observations with only 1 measurement each.  If an object is
+moving too slowly, two of the dectections of the object may be merged
+together.  Enough data is stored in the database to extract these
+individual measurements, so a more sophisticated search is possible.
+It is also currently the case that USNO proper motion stars are
+ignored, but some of these will be asteroid detections which are
+simply too close to a faint USNO stars (it has to be faint because it
+would otherwise be associated with a star in the photometry
+database).  
+
+\begin{figure}
+\psfig{file=flags.eps,width=9cm}
+\caption{\label{flags} 
+Pictoral representation of the Average.code flags.  The lower byte
+value is used for specific definitions of object types.
+}
+\end{figure}
+
+\subsection{Data Flags}
+
+The identifications made by the above routines are noted as a set of
+flags in the Average.code structure entry.  Some of these flags are
+mutually exclusive, so a certain bit may have more than one meaning,
+depending on the value of other bits.  Figure~\ref{flags} shows a
+pictoral representation of the meaning of the different bit fields.
+The uppermost bit (bit 15) determines if the object may be considered
+a ``fixed'' star or something which has no fixed location.  If it is
+not fixed, and this bit is 1, then there are several options.  The
+second uppermost bit (bit 14) will be set if this object is one of the
+three types of bad data flagged by {\tt markstar}.  The lower byte of
+code is used to define the type of bad data: ghost, trail, or bleed.
+Thus, any datapoints determined to be a satelite trail will have a
+code value of 0xc002 (49154 decimal).  If an object is a moving
+object, but not a bad datapoint, it may be identified as a ``rock''
+(an asteroid) by the program {\tt markrock}.  In this case, the flag
+for an asteroid is turned on (bit 13).  Thus anything identified as a
+rock by {\tt markrock} will have a code value of 0xa000.  The lower
+byte of code is not yet defined for asteroids, but could be reserved
+for distinguishing different classes of asteroids (ie, on the basis of
+orbital speeds, and so forth).  Objects which are fixed may have a
+variety of possible flags.  First, an object may be identified with a
+USNO star (bit 14).  It may be found to exhibit variability (bit 13),
+implying that the relative photometry routine should ignore it.  If
+may be a transient object (bit 12).  Note that, while an object
+associated with a USNO object may not be transient, an object which is
+variable may also not be associated with a USNO object.  Thus, we need
+seperate bits for variable vs transient.  Different types of variable
+objects may be represented with different values of the lower byte.
+For example, a Cepheid may have a specific lower byte code of 0x01,
+which an RR Lyra may have a lower byte code of 0x02.  Thus a Cepheid
+associated with the USNO catalog would have a total code value of
+0x6001. Finally, any of these types of non-moving objects may have
+some bad individual measurements (bit 12) or may be found to have a
+significant proper motion (bit 11).
+
+\section{Holding it Together}
+
+The preceeding sections describe the step-by-step analysis and
+incorporation of the data from individual images.  However, the power
+of the Pipeline comes in applying it to large numbers of images.  At
+LONEOS, we are using four Pentinum II computers to analyse the roughly 200
+images that are observed each night.  
+
+It is best to think of the analysis of images occurring on groups of
+images from a given night.  Of the set of processes discussed above,
+those in section \ref{parallel} are performed on individual images
+essentially independently of the other images.  The rest of the steps,
+both the data incorporation ({\tt addstar} and the database cleaning
+routines occur on the entire batch, one image at a time.  Thus, the
+former routines can easily be run in parallel, but the latter routines
+are run in serial to avoid having two programs writing to same part of
+the database at the same time.  In this model, we would process all of
+the images from a night through the parallel stage of the analysis and
+then run the serial stage only after the entire night is finished in
+the parallel stage.  We thus have a natural division of the pipeline
+into two stages.  
+
+The implementation of the two stages involves several programs, some
+of which are specific to the LONEOS situation, and others which are
+more general.  There are two layers of programs which enable batch
+processing of many images and repeated batch processing over many
+nights.  At the highest level, are Perl scripts which choose what data
+needs to be analysed and when.  These scripts tend to be more
+dependent on the details of the LONEOS implementation. At the next
+level down, these scripts call two main C programs which run the
+images of a given night through the pipeline.  These C programs are
+more general and do not depend on the details of the LONEOS
+implementation.  A defined set of files holds a log of the nights and
+images that have been processed, both for the information of people
+who might want to monitor the progress, but also used by the programs
+to guide decisions about what to analyse next.
+
+\subsection{data and file organization}
+
+There are a set of standard locations for all of the files used and
+created by the Pipeline.  These locations have both abstract names
+used by the pipeline programs and specific definitions used in the
+LONEOS implementation.  In general, the definitions of the files are
+given in the configuration file, 'config.txt' (currently located in a
+hard-wired location, but this may soon change to the mechanism used by
+the lastest version of spicam (3.0)).  The C programs all refer to the
+config file for the definitions, but the Perl scripts currently have
+the definitions hardwired as variable names defined in the beginning
+of each file.  This should be changed to a mechanism that looks in
+the config file, perhaps implemented by a standard Perl function.
+
+Here is a list of the main directories and their meanings:
+
+logdir:         /metis/d11/logs - all log files and the files used by
+the Perl scripts to control their actions (except locks) go here.
+
+workspace:      /irene/d11/workspace - the intermediate analysis
+stages go here, and the tar, gzipped directory files stay here.
+
+dumpspace:      /pallad/d1      - used by slurp to store images
+downloaded from the tape archive.
+
+database:       /hebe/d27/database      - the result database, as also
+the locks and the Rocks.dat file.
+
+references:     /metis/d11/references   - a variety of reference data,
+including the HST GSC, the USNO catalog, the config files, and so
+forth.
+
+source:         /metis/d11/src/ohana    - all of the programs source, 
+binaries, and scripts relevant to running the Pipeline are contained
+in these directories.  
+
+\begin{verbatim}
+database: /hebe/d27/database (also in /metis/d27/database as linked directories).
+ lock files also go in this directory
+workspace: /irene/d11/workspace - temporary storage of result files
+dumpspace: /pallas/d1 - disk to store data dumped from the archive by slurp.
+scriptdir: /metis/d11/logs - all log files
+\end{verbatim}
+
+\subsection{lastnight}
+
+The first step in the process is the Perl script {\tt lastnight},
+which is highly dependent on the specifics of the LONEOS setup.  It
+looks at the disks used by the observers to write the images from a
+given night.  Images are written to a directory called
+/pallas/d[1-6]/YYMMDD where 'YYMMDD' is an abbreviation for the current
+night's date (ie, 990223).  Many files use this naming scheme, so we
+will refer to such a name as 'YYMMDD'.  All of the images are stored
+in this directory with names yyyyMMDDxxxxb.fits where yyyy is the full
+year, MM is month, DD is date, and xxxx is a sequence number.  The 'b'
+refers to the 'b' CCD, and the .fits extension is required.  The
+script {\tt lastnight} is called by a cron job scheduled for 7am on
+{\tt hebe.lowell.edu} (note that the clock on hebe is set to PST, not
+local MST - fix this some day!).  The script searches the disks
+{\tt /pallas/d[1-6]} for a directory of the right form (basically any
+directory beginning with a number).  By default, the program demands
+that the directory have the name derived from today's date, but with
+the flag -any, it will accept any directory.  It then checks to see if
+this directory has been already been analysed (ie, it is included in
+the 'processed.log' file).  If not, it lists all files in that
+directory with the right ending (*b.fits) and places these names in a
+file, {\tt /metis/d11/logs/YYMMDD.inlist}.  It also writes the name of the
+directory (along with some other data) in a file 'extracted.log'.
+When it has successfully ended and identified some data, it calls the
+next script, {\tt stage1} and then quits.
+
+\subsection{slurp}
+
+An alternative to {\tt lastnight} is the Perl script {\tt slurp},
+which looks for data on the tape archive.  This script looks at the
+tape.log file and compares it to the file 'processed.log'.  It tries
+to identify nights listed in the file 'tape.log' which have not been
+included in the 'processed.log'.  If it finds any, it tries to
+download as much of the data as will fit on the disk {\tt /pallas/d1}.
+This script is called from within the {\tt stage1} script and is
+invoked if {\tt stage1} is run without any data already in
+the file 'extracted.log'.
+
+\subsection{stage1}
+
+The Perl script {\tt stage1} takes the image list produced by {\tt
+lastnight} or {\tt slurp} (YYMMDD.inlist) and passes the list to the
+controller program {\tt control1} which performs the parallel
+analysis.  The script has a few features to decide which files have
+been analysed through the pipeline and to what extent.  If {\tt
+stage1} is called, it should be able to figure out 1) if there is data
+ready to be analysed (ie, there is a new date reference in the file
+'extracted.log'), 2) if the data has been partially analysed (ie, the
+date reference in 'extracted.log' has not been put in 'processed.log',
+but there are entries in the file YYMMDD.outlist), and 3) which data
+needs to be run through which part of the pipeline (ie, it decides
+which of the images in YYMMDD.outlist have succeeded at which stages
+of the analysis, and starts them at the appropriate spot).  These
+features are most useful if the analysis crashes in the middle or
+needs to be restarted.  The {\tt stage1} script is meant to be run
+without any arguments and it will figure out what needs to be done.
+The one possible argument is the -once flag.  This flag tells {\tt
+stage1} to analyse the data it finds and quit when it finishes, and
+not call another instance of itself.  Otherwise, when it is done, it
+will restart itself, and since there is no new data in
+'extracted.log', it will start a slurp process to get data from the
+archive.  If there is no data in the archive either, this process will
+sleep for an hour and try again.  After a few attempts (2 days?), {\tt
+stage1} will give up and quit without restarting itself.  The script
+writes a log file called \$scriptdir/stage1.NN.log where NN is a
+number in sequence between all runs of {\tt stage1}.  A file
+stage1.count keeps track of which number NN we are on.  When {\tt
+stage1} has finished the analysis of a night's data, it writes the
+date in 'processed.log' to let other processes know what has been
+analysed.  The bulk of {\tt stage1} is not dependent on details of the
+LONEOS setup, but it does expect the files to have the naming scheme
+described above.
+
+\subsection{stage2}
+The serial stage processes are run by the Perl scrip {\tt stage2}.
+Normally, this script is always running.  It waits for the name of a
+night to be added to the 'processed.log file, presumably be {\tt
+stage1}.  Once if finds a new name in this file, it looks for the
+appropriate images in the file 'YYMMDD.addlist'.  It then submits the
+list to the second controlling program {\tt control2}, which runs the
+images through {\tt addstar}.  Images which are successfully run
+through addstar have their *bf.fits (flattened image) and *.obj
+(dophot output) files deleted.  When the entire night is done, the
+rest of the *.obj and *bf.fits images are deleted by {\tt stage2}, the
+directory with the *.cmp and *.log files is tared and gzipped into the
+file YYMMNN.tgz.  The directory is then deleted.  Finally, {\tt
+stage2} calls the scripts {\tt cleaning.pl} and {\tt run.phot} which
+runs the cleanup programs ({\tt markstar - nrphot}) on the database.
+After a successful run, {\tt stage2} will automatically respawn a new
+copy that waits until more data arrives.
+
+\subsection{control1 / control2}
+These two C-programs perform the tasks of farming the images out to
+the different machines for analysis.  The two programs are essentially
+identical, but {\tt control1} is allowed to use all machines and {\tt
+control2} can only use one (hebe.lowell.edu, which has the database
+local to it).  These controller programs start off by executing remote
+shells (using ssh) on a set of machines.  These shells are then used
+to execute the remote commands and are kept open the entire time the
+process runs.  
+
+Both control programs maintain a list of images in a set of queues.  A
+set of rules defines the order in which each image travels through the
+queues.  Each queue is associated with a single process (ie, flatten,
+astrometry, etc).  Images which successfully run through a queue are
+sent on to the defined 'next' queue, while images which fail are
+instead sent to the defined 'fail' queue.  In most cases, the 'fail'
+queue is a process which marks the image as bad and write the name in
+a file, and then puts the image in the 'done' queue.  However, it is
+possible for the 'fail' queue to be another attempt, such as in the
+case of astrometry.  In this example, images which fail the default
+astrometry are run through a second queue which tries again with the
+-loneos flag set (this flag makes the assumption that the header
+information about the LONEOS region number for the image is correct,
+not the RA/DEC information).
+
+The connections on the remote machines are handled as child processes
+with communication through the stdin and stdout/stderr pipes.
+Messages can be sent back and forth though these pipes, and messages
+which are received from a process associated with an image are written
+to a log file with the name imagename.log (where imagename is the root
+name of the image).  The only required messages from the program are
+messages saying ``SUCCESS'' or ``ERROR''.  The programs therefore all
+end with either of these words.  The programs are started within the
+controller as a command to the shell followed by and echo ``PROCESS
+DONE''.  If the controller sees the message ``PROCESS DONE'' without
+either ``SUCCESS'' or ``FAILURE'' (after waiting an appropriately long
+time), it assumes the program crashed.  Currently, no timeout is
+available for the programs.  The flat-field process is run in mana and
+is a slightly special case.  We avoid re-loading the same flat many
+times by starting a mana process once and running the flatten
+procedures as mana function calls.  The images which are analysed
+(with an indicator of success or failure at each stage) are written to
+a file named 'YYMMDD.addlist' by the {\tt control1} and 'YYMMDD.outlist'
+by {\tt control2}.
+
+\subsection{Lock files}
+
+A variety of locking mechanisms are used to avoid overwriting the
+database or running more than one version of the controlling
+programs.  There is a perl program, {\tt locks}, which allows the user to
+set or check the state of the different locks. All programs which
+write to the database check for the existence of a lock on the
+database and refuse to run if it exists.  The control1/2 programs and the
+stage1/2 programs check for their own lock files and only run if they
+don't exist.  Finally, the use can set the lock files 'kill' or 'halt'
+for either stage1 or stage2.  The first tells the process to stop
+immediately, taking care of bookkeeping first.  The second tells the
+process to finish the current set of images but not start a new
+version.  
+
+\subsection{Other Perl Scripts} 
+
+there are several other scripts which are either used to do some
+simple function or are used by the maintainer to check up / work on
+the pipeline.  The two programs 'checkoutlist' and 'checkaddlist' take
+a list of YYMMDD.outlist or YYMMDD.addlist files and list the number
+of images in the file and the number of successes at each analysis
+stage.  There programs 'cleaning.pl' and 'run.phot' deal with the
+cleanup programs and the running of nrphot.  the program
+'backup.stuff' take the first 4 characters of the year/month file
+names and backs up to the jukebox tape all tar files for the given
+month.
+
+\section{Operational Issues and Subtleties}
+
+The current LONEOS system runs principally on four Intel machines
+under linux.  The existing system has been generally quite stable over
+the past year of operation, but there are some subtleties.  One
+significant issue is the RAID disk attached to hebe.lowell.edu.  This
+device is a set of three 9 GB disks merged into one 27GB raid disk.
+The database is stored mostly on this dist and the RAID disk mounted
+on metis.  It is easy to split the database across multiple file
+systems by using links.  Within the database directory, there are
+directories for each of 24 Declination bands (7.5 degrees tall).  The
+easiest way of splitting up the data is to move some of these
+subdirectories to another device and creating links to them at the
+appropriate place.  This is currently done by having a directory
+/metis/d27/database with the remote directories.  
+
+The current problem with the /hebe/d27 RAID results from the poor way
+it was setup.  It was set up without much knowledge of the (at the
+time) rather rudimentary linux RAID system.  As a result, the
+implementation is such that the disk will not automatically be
+configured and mounted at boot time.  There is a script /root/mdstart
+which lists the step to setup the RAID correctly.  It would be helpful
+if the rc.local or rc.sysinit files were fixed to start, fsck and
+mount the RAID automatically at boot.  The setup on metis is
+substantially more mature and is correctly implemented.  It comes up
+when the machine boots.
+
+\section{Visualization -- Status}
+
+Visualization of the data in the photometry database can best be
+performed with the program {\tt status}.  This is a command-line
+driven program with a math and macro language which makes it easy to
+perform complex tasks.  
+
+First, a few notes about the user interface.  The interface has an
+interaction similar to {\tt tcsh}.  The arrows allow editing of
+previous commands.  You can also use emacs-like commands such as
+cntl-a to reach the beginning of the line and cntl-e to reach the end.
+There is command and file completion: if you type part of a command
+(as the first thing on a line) and then type tab, it will fill in as
+much as possible, until the word is not unique.  Typing tab twice at
+that point will list the possible endings.  For any but the first word
+on a line, the same thing will happen for the files in the current
+directory.  It is also possible to type just a fraction of a command,
+as long as it is unique.  An ambiguous command will list the possible
+alternatives.  For example:
+\begin{verbatim}
+status: c
+ambiguous command: c ( catalog cgrid clear create cursor )
+\end{verbatim}
+
+The shell is essentially an interpretive programming language.
+Variables are set as follows:
+\begin{verbatim}
+status: $fred = 10
+\end{verbatim}
+Any expression within curly brackets \{\} is assumed
+to be an arithmetical expression and is evaluated before the line is
+executed.  For example:
+\begin{verbatim}
+echo {$fred*dcos(45)}
+\end{verbatim}
+would give the response 7.07107.  There are math functions cos, sin,
+and tan, which operate on radian expressions, and also dcos, dsin,
+dtan, which operate on degree expressions.  There are also the
+equivalent inverse functions: eg., asin and dasin return radians and
+degrees, respectively.  The help section on Math defines all of the
+available math functions.  
+
+\subsection{Miscellaneous Commands}
+\begin{verbatim}
+!                         -- system call
+?                         -- list commands 
+??                        -- list variables 
+echo                      -- type this line 
+exec                      -- system call
+exit                      -- exit program 
+help                      -- get help on a function 
+output                    -- redirect output to file
+quit                      -- exit program 
+scan                      -- scan line from keyboard or file to variable 
+wait                      -- wait until return is typed
+which                     -- show command 
+\end{verbatim}
+Most of these are self-explanatory.  The command ?? prints the system
+variables.  The {\tt help} command will provide help on a single
+command or, without any arguments, will list all available help
+files (this includes general help not associated with a specific
+command).  
+
+\subsection{Shell Programing}
+\begin{verbatim}
+break                     -- escape from function 
+for                       -- loops 
+if                        -- logical cases 
+input                     -- read command lines from a file 
+macro                     -- deal with the macros 
+\end{verbatim}
+
+There are several options for programming in {\tt status}.  First, a
+file which contains a series of commands can be executed with {\tt
+  input (filename)}.  It is also possible to define macros which will
+behave much like regular commands.  A macro is defined by typing {\tt
+  macro name} or {\tt macro create name} followed by the commands.
+Arguments to the macro are assigned to the variables \$1 .. \$N and
+the number of arguments is given by \$0.  Macros may be defined in
+{\tt input} files, and in fact when {\tt status} is started, it loads
+the file {\tt \~/.statusrc} which may contain default macros.  Simple
+loops and if statements can be performed, and are quite useful for
+complex macros.  
+
+``If'' statements are similar in syntax to C if statements, but only
+the following logical operators are available: $>$, $<$, $=$, !, $|$,
+and \&.  Notice that (currently) there is no $>=$ or $<=$ symbol.  The
+operator ! means ``not equal to'', but cannot be used to negate a
+logical value.  The operators $|$ and \& have the meaning of ``or'' and
+``and'' respectively.  Math expresions in the if statement must be
+contained in curly braces, as elsewhere.  Variables with string values
+may use the logical $=$ operator to test if two strings are the same.
+``For'' loops are quite simplistic.  The form is:
+\begin{verbatim}
+for var first last delta
+ (commands)
+end
+\end{verbatim}
+The value of {\tt \$var} will start at the value {\tt first} and increment by
+{\tt delta} after each loop.  The loop will stop after {\tt \$var} is greater
+than {\tt stop}.  The value {\tt delta} is optional, with 1 assumed.
+The value of {\tt \$var} may be changed during the loop, and if set
+beyong the value of {\tt last} will end the loop early.  
+
+\subsection{Vector Plotting}
+
+\begin{verbatim}
+box                       -- draw a box on the plot
+clear                     -- erase plot
+create                    -- create a new vector
+cursor                    -- get coords from cursor
+grid                      -- plot cartesian grid
+hist                      -- create histogram from a vector
+labels                    -- define labels for plot
+limits                    -- define plot limits
+plot                      -- plot a pair of vectors
+print                     -- write vectors to file
+ps                        -- define labels for plot
+set                       -- vector math
+style                     -- set the style for graph plots
+vectors                   -- list vectors
+zplot                     -- plot scaled points 
+\end{verbatim}
+
+In addition to scalar variables, {\tt status} can manipulate and
+display 1-D vector variables.  Many of the commands which extract data
+from the photometry database place the data in vectors as well as
+plotting them.  A vector can also be created based on a number
+sequence with the command {\tt create name Nelements start delta}.
+The resulting vector has $Nelements$ entries, starting at a value of
+$start$ and running until $start + delta*Nelements$.  If $delta$ is
+0.0, all elements will have the value of $start$.  A histogram of a vector
+may be made with the command {\tt hist}, which creates a new vector
+containing the histogram of the first vector.  The data range and bin
+size of the histogram are defined in same way as with create.  This
+makes it easy to create the index vector that goes with a histogram
+vector:  
+\begin{verbatim}
+hist y Ny 1 100 0.1
+create dx 1 100 0.1
+\end{verbatim}
+The above will create a histogram of y in Ny and the index in dx.
+Plotting this with {\tt plot dx Ny} will show the histogram.
+
+Vector math is performed with a command of the form {\tt set new =
+  (expression)}.  The expression is some math function employing
+vectors and scalars.  A complete listing of the math operators
+available in {\tt set} can be found in the help for {\tt set}.
+
+Once vectors are defined, they may be plotted.  A pair of vectors can
+be plotted against each other if they have the same number of entries.
+The plotting is performed on the graphics window, Kapa.  There are
+actually several graphics windows available to {\tt status}, any of
+which may be used to plot at any time.  Some of the more complex
+operations default to either graphics window 0 or 1, depending on the
+context.  Except for those functions with a pre-defined window, all
+plotting functions apply to the current graphics window unless an
+option {\tt -n N} is given to specify a different window.  The
+plotting style is determined by the command {\tt style} which can set
+the line width, the line type (solid, dashed, dotted, etc), the point
+type (box, cross, etc), the point size, the color, and whether a pair
+of vectors is plotted as a sequence of points, a set of connected
+lines, or a histogram.  Some functions which make plots use their own
+styles, as discussed below.  The function {\tt limits} lets the user
+set the range of the plot axes, or check the current setting.  The
+command {\tt plot} will plot a pair of vectors on the current graphics
+window using the current plotting style for that window.  The command
+{\tt zplot} will plot a pair of vectors with the point size scaled by
+a third vector, with maximum and minimum point sizes representing
+specified values.  The {\tt cursor} command goes the other way: this
+command puts the Kapa window in cursor mode and waits for input from
+Kapa.  The user can then type any alphanumeric key on the graphics
+windows and will be told both the pointer location (in the graphics
+coordinates) and will have the coordinates stored in {\tt status}
+variables.  For example, by typing ``1'' in the sky display window,
+the RA and DEC of the pointer are stored in the variables {\tt \$R1}
+and {\tt \$D1}.  This command can be used to let the user define
+locations or regions of interest on the Kapa window. (Future addition:
+{\tt button}, which does the same with the mouse buttons).  
+
+\subsection{Database Functions}
+
+\begin{verbatim}
+gcat                        -- get catalog at location
+gimages                     -- get images at location
+gstar                       -- get star statistics
+extract                     -- extract average vectors from catalogs
+mextract                    -- extract measurement vectors from catalogs
+imstats                    -- plot image statistics
+imextract                   -- extract image vectors from database
+lcat                        -- list catalogs in display region
+cmatch                      -- match two catalogs
+\end{verbatim}
+
+There are a variety of other commands which directly refer to the
+photometry database.  Some of these functions extract data of various
+types from the database, others perform more complex plotting
+operations.  The commands listed above are those which simply extract
+data from the database.  The first three list information relevant to
+a specific RA, DEC location on the sky: {\tt gcat (RA) (DEC)} lists
+the catalog at the specified location and places the name in the
+variable {\tt \$CATNAME}, {\tt gimages (RA) (DEC)} lists all images
+which overlap the specified location, , {\tt gstars (RA) (DEC)
+  (RADIUS)} lists data about the stars within a specified radius of
+the specified location (all numbers above are given in decimal
+degrees).  Similarly, {\tt lcat} lists the catalogs in the region.
+Imstats lists statistics about each image
+
+The next three commands extract a specific piece of information from
+the photometry database and places it in a vector.  First, {\tt
+  extract} will extract average values for each star and place it in a
+vector.  Next, {\tt mextract} will extract measurement values for each
+star and place it in a vector: as a result a single star may have
+multiple entries in the measurement vectors.  Finally, {\tt imextract}
+will extract image statistics into vectors (not yet implemented).
+
+
+\begin{verbatim}
+catalog                    -- plot catalog stars
+cgrid                      -- plot sky coordinate grid
+cplot                      -- plot vectors in sky coordinates
+czplot                     -- plot scaled vectors in sky coordinates
+images                     -- plot image boxes
+imdense                    -- image density plot
+lcurve                     -- plot lightcurve for a star
+pcat                       -- plot catalog boundaries
+region                     -- define sky region for plot
+resid                      -- plot residuals
+simage                     -- plot stars in an image
+\end{verbatim}
+
+There are two types of database plotting functions: those that display
+or refer to the spatial charateristics of the data and those that
+refer to other types of charatersitics, such as the time domain.  The
+graphics window 0 is reserved for all plots of objects on the sky.
+The command {\tt region} defines the current sky coordinates for plots
+in graphic window 0.  The command {\tt pcat} plots the outline of all
+photometry database files which are within the currently defined
+region (and by default, only those with data).  {\tt images} plots the
+outline of the images in the image database, while {\tt imdense} shows
+the number of images at a location by randomly spacing dots within the
+boundary of the images.  The command {\tt cgrid}
+draws a grid in celestial coordinates on the for the current region.
+
+The most complex, but also one of the most useful command is {\tt
+  catalog}, which plots the positions of stars in the photometry
+database (and others) on the sky.  There are many options to this
+command.  One set allows the user to plot stars from the photometry
+database (the default), from the HST GSC, or from an ASCII text file
+with RA, DEC, and Mag in specified columns.  If the ASCII file has a
+fixed number of bytes per line, the data can be more quickly loaded.
+The size of the points may be scaled by the star magnitude, by the
+number of observations of the star, or by the number of missing
+datapoints for the star.  In addition, points may be plotted only if
+they land in specified magnitude ranges, or with specified numbers of
+measurements, or missed measurements.  Also, objects may be plotted
+only if they have a specified Average.code, so that only asteroids or
+only perfect stars may be plotted.  The plotted vectors may be saved,
+if desired, and the source catalog epoch may be specified as different
+from J2000 (only valid for ASCII data).
+
+Several other commands relate to non-spatial charateristics of images
+and stars.  {\tt lcurve} will plot a light curve for all stars within
+some radius of a point.  {\tt resid} plots the photometry residuals
+for a particular region file.  
+
+\section{Some Examples}
+
+\begin{figure}
+\psfig{file=allsky.ps,width=16cm}
+\caption{\label{allsky} 
+Map of the entire sky, and images added to database.
+}
+\end{figure}
+
+Fig.~\ref{allsky} shows a map of the entire sky, and the location
+of the images currently in the database.  This picture was made with
+the following commands: (output is not shown) \\
+\begin{verbatim}
+status: region 0 0 90 gls
+status: cgrid
+status: style -lw 2 -c red 
+status: images
+status: ps
+\end{verbatim}
+In this example, on the graphics window, the image boxes are shown in
+red.  The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth.  
+
+\begin{figure}
+\psfig{file=catalog.ps,width=9cm}
+\caption{\label{catalog} 
+Comparison between HST GSC and photometry database astrometry. 
+}
+\end{figure}
+
+Fig.~\ref{catalog} shows an example comparison of the photometry
+database star positions and the HST Guide Star Catalog star
+positions.  The crosses are from the photometry database while the
+boxes are from the HST GSC.  The size of both points is a function of
+brightness.  This plot was made with the following commands (starting
+from the previous image):
+\begin{verbatim}
+status: cursor  (typed 1 on region of interest)
+1 137.097858 22.698305
+q 137.097858 22.698305
+status: region $R1 $D1 0.5 TAN
+status: cgrid
+status: box
+status: style -pt 0 
+status: gcat $R1 $D1 
+  0 n2230/1951.cpt *
+status: catalog n2230/1951.cpt -m 12 18
+status: style -pt 2
+status: catalog n2230/1951.cpt -m 12 18 -g
+\end{verbatim}
+
+\begin{figure}
+\psfig{file=lightcurve.ps,width=9cm}
+\caption{\label{lightcurve} 
+Lightcurve for several image stars.
+}
+\end{figure}
+
+Fig.~\ref{lightcurve} shows an example lightcurve from three stars in
+the above field.  The x axis shows the time in seconds, which the y
+axis shows the star magnitudes.  This plot was made with the following commands (starting
+from the previous image):
+{\small
+\begin{verbatim}
+status: cursor
+1 137.062562 22.791860
+q 137.062562 22.791860
+status: lcurve $R1 $D1 0.01 -l
+status: box
+status: gstar $R1 $D1 0.01
+read 11318 stars from catalog file /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt (22462 total measurements)
+508 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.068619 22.785400 18.438000  1 (2)  1.000000 1.000000
+498 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.062820 22.792747 12.882000  3 (0)  1.000000 1.000000
+501 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.064499 22.800817 15.480000  3 (0)  1.000000 1.000000
+\end{verbatim}}
+The last lines show the database information on the three stars.  
+
+\section{Software Distribution}
+
+All of the code, binaries, and scripts for the Pipeline are stored in
+the ohana tree at /metis/d11/src/ohana.  The ohana directory contains
+subdirs of bin, lib, include, src, config, and doc.  There is a
+Makefile at the top level which calls lower-level Makefiles to build
+the programs desired.  One of the important concepts in the ohana
+distribution is the design of the directory layout to enable binary
+support for multiple architectures.  The Makefiles use the environment
+variable ARCH to determine the appropriate architecture.  The binary
+and library files are stored in subdirectories of the form bin/\$ARCH
+and lib/\$ARCH.  It is therefore necessary that users of the ohana
+system architecture define the ARCH variable correctly.  It is also
+necessary to include in the user's PATH the directory
+/metis/d11/ohana/bin/\$ARCH.  At LONEOS,
+with the solaris sparc station and the linux machines both in use,
+ARCH can take on the values of 'linux' and 'sol'.  The values 'sun4',
+'irix', and 'hp' have also been used at various sites.  A simple bit
+of csh script in the users .cshrc or equivalent can make this
+assignment transparently:
+
+\begin{verbatim}
+set sys=`uname -s` 
+switch ($sys)
+ case IRIX64:
+   setenv ARCH irix;
+   breaksw;
+ case SunOS:
+   set ver=`uname -r | awk '{print substr($1,1,1)}'`;
+   if ($ver == 5) then
+     setenv ARCH sol
+   else 
+     setenv ARCH sun4
+   endif
+   breaksw;
+ case Linux:
+   setenv ARCH linux;
+   breaksw;
+ default:
+   echo "unknown architecture";
+   setenv ARCH unknown;
+   breaksw;
+endsw
+\end{verbatim}
+
+The Makefiles use the ARCH variable not only for destination
+directories, but also for destination binary names.  All files of the
+form fred.c are compiled to fred.ARCH.o (instead of fred.o), 
+uninstalled libraries get the names libfred.ARCH.a (instead of
+libfred.a), while uninstalled programs are called fred.ARCH.  
+
+The perl scripts are kept in ohana/src/perl, with links to the
+appropriate bin directories.  All programs are stored in directories
+of the form ohana/src/program.  The exceptions are gastro (stored in
+ohana/src/astro), and control1/2 (stored in ohana/src/astro).  Also, a
+variety of simple, user-level functions are available in
+ohana/src/misc. 
+
+Most of the ohana package of programs use several common libraries.
+These include the readline library (except for dophot, this is the only
+part of ohana that is not written by Eugene Magnier), which is used
+for command-line interface systems.  Other important libraries are the
+fits library for implementing the FITS specifications in a convenient
+C-friendly environment, and the ohana library, consisting of basic
+string and other basic operations.  
+
+For more details on the programming aspects of parts of the pipeline,
+see the associated document, 'A Programmer's Guide to the Loneos
+Pipeline'
+
+\section {Comparison with USNO catalog}
+
+I have made several comparisons between the USNO catalog and results
+from running images from Loneos through the photometry pipeline.  In
+general, there is a good agreement between the astrometry of the two
+datasets.  There are specfic areas where the two disagree, however.
+First, as discussed above, the USNO star positions may be
+significantly different from the observed star positions simply
+because of proper motion.  The first picure in the ``Comparisons
+between Loneos + USNO'' pages shows several stars with significant
+proper motion.  These are currently being flagged in the photometry
+database as discussed.  Other differences evident in this first
+picture include the classification of bright stars and the splitting
+of faint and bright stars.  In the upper right of the image, two
+bright stars are visible, one of which is actually a double.  The USNO
+catalog does a good job of detecting both, but it also detects and
+includes as stars several diffraction spike points.  The HST GSC only
+detected a single point and did not split the star at all.  The Loneos
+data in our pipeline did not detect the star at all due to saturation.
+A star in the upper left corner shows the ability of {\tt dophot} in
+the Loneos pipeline to split double stars.  This star is, on close
+inspection, clearly a double.  The USNO catalog only detects one.  The
+Pipeline not only splits the two stars, but it also makes the correct
+cross-identification of the stars.  
+
+The second set of pictures shows the ability of the Loneos pipeline to
+deal with small galaxies.  There are two small galaxies in this field,
+both of which are broken into 4 or 5 objects by the USNO catalog, but
+maintained as single objects in the Loneos pipeline.  In another
+example (not shown) a bright star within the disk of a small galaxies
+was clearly split from the galaxy by the pipeline (and also by USNO).
+
+\appendix
+\section{Parameter file params.txt}
+\begin{verbatim}
+# Configuration file for Loneos analysis pipeline
+
+# important files and directories
+GSCFILE                 /host/pikake/d1/gene/gsc/GSCregions.tbl
+GSC_DIR                 /host/pikake/d1/gene/gsc
+CATDIR                  /host/radon/rad3/gene/photcat2
+IMAGE_CATALOG           /host/radon/rad3/gene/photcat2/Images.dat
+IMAGE_CATALOG_TEMPLATE  /host/radon/rad3/gene/photcat2/template.cat
+CATALOG_TEMPLATE        /host/radon/rad3/gene/photcat2/template.cat
+ROCK_CATALOG            /host/radon/rad3/gene/photcat2/Rocks.dat
+USNO_CDROM              /host/tycho/mnt/md0/kevin/zones
+LONEOS_REGIONS          /host/pikake/h1/gene/loneos3/regions.map
+
+# parameters for "fstat"
+# instrumental mag zero point
+ZERO_PT                 24.5
+DOPHOT_CHAR_LINE        129
+DOPHOT_TYPE_FIELD       5
+DOPHOT_AP_FIELD         91
+DOPHOT_PSF_FIELD        52
+MIN_SN_FSTAT            6.0
+
+# parameters for "gastro"
+DEFAULT_RADIUS   32.0
+MINIMUM_RADIUS   1.5
+MIN_ERROR        0.6
+MIN_PRECISE      0.12
+CCD_PC1_1        1
+CCD_PC2_2        -1    # for loneos
+CCD_PC1_2        0
+CCD_PC2_1        0
+ASEC_PIX         2.82
+NFIELD           3.0
+MMIN             6.0
+ROT_ZERO         0
+dROT             1.0
+NROT             2
+POLAR_AXIS_RA    -90.0
+POLAR_AXIS_DEC   89.88
+RA_OFFSET        -90.0
+DEC_OFFSET        0.0
+
+# parameters for "addstar"
+# airmass extinction coefficient (in mag / airmass)
+NSIGMA                  3.0
+ALPHA                   0.1
+XOVERSCAN               60
+YOVERSCAN               50
+
+# parameters for "controller"
+IN_DIR                  /host/pikake/h1/gene
+OUT_DIR                 /host/pikake/h1/gene/loneos4/
+DOPHOT_PARAMS           /host/pikake/h1/gene/src/ohana/config/default_parameters
+FLATTEN_SCRIPT          /host/pikake/h1/gene/src/ohana/config/test.pro
+NEW_IMAGES              images.dat
+USERNAME                gene
+PASSWORD                gene
+
+# here we list all available machines
+NMACHINES               2
+MACHINE0                pikake.astro.washington.edu
+MACHINE1                mildew.astro.washington.edu
+
+# here we define which machines which are always free
+fMACHINE0               1
+fMACHINE1               0
+
+# here we define the machine which runs the database 
+LOCAL_MACHINE           0
+
+# parameters for "status"
+TIME_REFERENCE          90/01/01 00:00:00
+
+# parameters for "nrphot"
+# make this one small to boost change of getting an early region 
+TAU                     3.0             
+SCATTER_LIM             15.0
+MAG_LIM                 17.0
+IMAGE_SCATTER           0.075
+NIMAGE_SCATTER          1.5
+STAR_SCATTER            0.05
+
+### parameters for "markstar"
+SEARCH_RADIUS           360   # region for initial trail hunt, in arcsec 
+TRAIL_WIDTH             5.0   # expected trail width, in arcsec
+NANGLE_BINS             180   # Number of angular bins in 180 degrees
+NPTSINLINE              5     # minimum points needed for trail
+MIN_DENSITY             0.025 # minimum linear density in star/arcsec
+SPACE_SIGMA             10.0  # how much denser than average for trail?
+# parameters defining bright star exclusion regions
+BRIGHT_XTRAIL_WIDTH       5.0 # faintest sat. star
+BRIGHT_XTRAIL_MAG         9.5 # faintest sat. star
+BRIGHT_XTRAIL_SLOPE     -80.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_YTRAIL_WIDTH       5.0 # faintest sat. star
+BRIGHT_YTRAIL_MAG        11.0 # faintest sat. star
+BRIGHT_YTRAIL_SLOPE    -400.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_HALO_MAG           8.5 # faintest sat. star
+BRIGHT_HALO_SLOPE       -28.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+# parameters which define the ghosts
+GHOST_MAG               7.5
+GHOST_RADIUS            200   # in arcsec
+OPTICAL_AXIS1           2154.0
+OPTICAL_AXIS2           2193.0
+
+# parameters for "addusno"
+USNO_RADIUS             5.0   # in arcsec
+USNO_PROPER             20.0   # in arcsec
+USNO_RED                1000  # code for USNO Red data
+USNO_BLUE               1001  # code for USNO Blue data
+# /mnt/cdrom
+
+# parameters for "markrock"
+ROCK_RADIUS             2.0   # in arcsec
+ROCK_MAX_RADIUS         20.0  # in arcsec
+ROCK_MAX_SPEED          0.1   # in arcsec / sec
+\end{verbatim}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline4.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline4.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline4.tex	(revision 22322)
@@ -0,0 +1,1520 @@
+%\documentstyle[astro,psfig]{article}
+\documentclass{article}
+\usepackage{psfig}
+\usepackage{astro}
+\usepackage{times}
+\GoodMargins
+\newcommand{\note}[1]{({\bf #1})}
+\title{Variability Pipeline}
+\author{Eugene Magnier}
+\begin{document}
+\maketitle
+
+\section{Overview}
+
+The Loneos Pipeline provides automated reduction of images, including
+photometry and astrometry, with the final data stored in a photometry
+database organized by star position on the sky.
+
+Fig.~\ref{pipeline} shows a schematic of the analysis pipeline.
+Images are written to the disk from the camera by the software which
+runs it, {\tt astrocam}.  The resulting images are FITS files (which
+may be arbitrarily sized) and have header keyword parameters RA and
+DEC specifying rough celestial coordinates.  Images are first analyzed
+by {\tt dophot}, which produces instrumental photometry and pixel
+coordinate positions for stars and other objects in the images,
+writing files with a particular format, called OBJ files in this
+discussion.  The results from {\tt dophot} are cleaned up by a program
+called {\tt fstat} which merges the header information from the image
+with the most relevant parts of the OBJ file, creating a new file with
+the CMP format.  Astrometry is performed on the CMP file with the
+program {\tt gastro}.  {\tt Gastro} makes a comparison with an
+astrometric reference database and only changes CMP header keywords as
+a result.  Finally, the processed image CMP file is added to the
+photometry database with the program {\tt addstar}.  The photometry
+database is maintained in a large number of files (CPT files), each
+representing a small region of sky.  There is no relationship between
+the image locations and the boundaries of CPT file.  All of these
+processes occur on individual image, and would typically be performed
+while the observations are occuring, or during the day following, but
+may be performed at any time to add new or archived data to the
+database.
+
+Once large batches of images have been processed, a series of programs
+should be run to clean up the photometry database.  Typically, these
+routines would be run at the end of a night on all of the CPT files to
+which new data were added.  First, {\tt markstar} identifies bad data
+(the trails of satelites, the bleeding columns from stars, ghost
+images), and flags these data points appropriately.  Second, {\tt
+  addusno} matches the catalog stars with stars from the USNO database
+and adds the USNO magnitudes to the photometry database.  Next, {\tt
+  markrock} searches for likely asteroids in the CPT file, marks them,
+and writes the asteroid observations to an asteroid database.  It also
+notes measurements which are likely to be noise and cosmic rays.
+Finally, {\tt nrphot} determines relative photometric calibrations for
+observations in a set of CPT files.  
+
+With the exception of {\tt dophot}, all of the programs in the
+pipeline refer to a single configuration file to determine the large
+number of parameters necessary in the process.  The default parameter
+file is hardwired into the programs, but an alternate configuration
+file may be specified with the environment variable {\tt
+  PIPE\_CONFIG}.  This option makes independent processing of multiple
+data sources trivial.  For example, if the pipeline is to be used for
+both Loneos and SDSS image analysis and display, there could be two
+configuration files (eg, {\tt loneos.txt} and {\tt sdss.txt}), one for
+each data source.  Several important parameters would be different
+between these two, for example, the initial plate scale guess for the
+astrometry routine, or the parameters which define the rough chip
+orientation.  This scheme allows for analysis of different types of
+images, with a common destination photometry database, or for
+independent photometry databases.
+
+\begin{figure}
+\psfig{file=schematic.eps,width=9cm}
+\caption{\label{pipeline} 
+Schematic of the photometry pipeline.  Rectangles represent programs,
+while ovals represent data products.  Arrows show the direction of
+travel of information.  The flatten routine is a filter whose position
+may change as the pipeline matures.
+}
+\end{figure}
+
+Data from different sources can be processed by the pipeline and
+stored in the same photometry database, since all photometry is tagged
+with a source ID in the database (as are the images in the image
+database).  This allows us to include, for example, the USNO catalog
+magnitude measurements (or a subset) in the photometry database for a
+quick comparion.  Different sources may be considered different
+filters, so color-color and color-magnitude diagrams could be
+extracted by specifying choices for source catalog.  Currently each
+object has a single primary photometric system, and individual
+observations may be combined to determine that average (eg, if they
+are different photometry sources with roughly the filter bandpass) or
+they may be ignored when calculating relative magnitudes (eg, the USNO
+magnitudes would be ignored).  This may be expanded upon by defining
+multiple average magnitudes and allowing different measurements to
+contribute only to the appropriate average.  The determination of the
+photometry code for a particular image is currently not well defined.
+There are several points in the analysis where the user (or the
+controlling program) may specify the photometry source.  A table
+should be kept in the photometry catalog to define the various
+photometry codes.  
+
+\section{Image Analysis Pipeline}
+\label{parallel}
+\subsection{Flat Fielding -- Mana / Flatten}
+
+Flat fielding is performed on each image after it has been written to
+disk by the acquisition software.  The processing is performed with
+mana (see mana user's guide) using a script written to work with the
+analysis pipeline processing software.  There are several important
+aspects of the script which make it work with the containing programs.
+First, the script is contained within a file, flatten.pro, in the
+references directory.  the script is invoked by calling it on the
+command line wehn mana is started.  If mana is started with a file
+name on the command line, that file is loaded with in 'input'.  The
+file defines a macro 'flatten', and also loads the flat-field file.
+The flattening process is actually done by calling within mana the
+macro flatten, with the input and output file names as arguments.  By
+loading the flat-field at the start of mana, instead of on each
+execution of flatten, we save the load time of each flat-field image
+(which is 2x the size of the basic image, being 4byte floats per
+pixel).  The location of the flat field is currently hard-wired in the
+flatten script - this should probably be changed in the future.  One
+of the aspects of this is that the macro loads a flatfield with a
+specific name and location.  This file is actually a link; The
+higher-level controlling process (stage1) creates a link to the
+correct flatfield.  This allows the analysis process to use different
+flatfields for different situations.  At LONEOS, we currently use a
+flat field generated from a well-understood night.  This is probably
+not the best solution as the flatfield changes with time.  As
+discussed below, the photometry solution program, nrphot, fits a
+high-order polynomial to the relative photometry, allowing for
+differences from the correct flatfield.
+ 
+\subsection{Photometry -- Dophot}
+
+Photometry is performed using a varient of {\tt Dophot}.  The version
+of dophot is based on Dophot 2.0, but it has been adapted to
+streamline processing of many files.  First, with this version of
+dophot, any FITS format data is safely read in.  Instead of compiling
+to a fixed maximum size, only the memory needed to contain the image
+matrix is allocated.  The image is also converted to REAL*32 from
+whatever BITPIX for processing.  Finally, only a single, complete
+parameter file is loaded by dophot.  There is no option for a modified
+and a default parameter file.  The parameter file to be used is
+defined, along with the input image and the output object list on the
+command line when the program is run: \\
+{\tt dophot image.fits image.obj parameter\_file} \\
+To achive this, the code from dophot was wrapped within a C program
+which interprets the command line arguments, allocates the necessary
+memory, and reads in the image.  The rest of the processing is
+performed by dophot in the standad manner.  Notice that we use a
+consistent naming convention through the pipeline reductions.  The
+image is called {\tt foo.fits}, while the resulting dophot-produced
+photometry list is called {\tt foo.obj}.  
+
+\subsection{Cleanup -- fstat}
+
+After an image is processed by dophot, the object file ({\tt foo.obj})
+is converted to a more-compact, more-complete file called a CMP file,
+with a name of the form ({\tt foo.cmp}).  This conversion is done with
+the program {\tt fstat}.  The CMP file consists of the FITS header
+from the original image ({\tt foo.fits}), with some additional
+keywords to be used at later stages in the analysis, followed by an
+ASCII list of the interesting data from ({\tt foo.obj}).  In this
+list, types 6 and 8 are excluded, and only the following values are
+kept:
+\begin{verbatim}
+X       Y     Mag   dMag t log(sky) 
+1342.0  106.1 14.166 000 4 3.2
+\end{verbatim}
+Objects with a signal-to-noise ratio lower than a specified cutoff
+({\tt MIN\_SN\_FSTAT}) are also excluded.  Some general information
+about the image is derived by {\tt fstat} (FWHM, saturation and
+completeness limits, number of each dophot type) and stored as
+keywords in the header.  The photometry source must be specified at
+this stage as one of the command line arguments, though the value used
+may be overridden in one of the stages belowe.  The resulting file can
+now stand on its own without reference to the original image.  The
+keywords used by {\tt fstat} include the minimum signal to noise, a
+rough guess at the zero point ({\tt ZERO\_PT}), and four numbers
+defining the format of the {\tt dophot} OBJ file.
+
+\subsection{Astrometry -- gastro}
+
+Astrometry is performed automatically by the program {\tt gastro}.
+{\tt gastro} loads a CMP file and determines an initial guess for the
+center coordinates (based on the RA and DEC header keywords).  The
+program also uses the configuration information about the plate scale
+and rough orientation of the image to get a close to the final
+solution.  Also, the true sky position of the telescope pole may be
+defined to allow astrometry on images taken close to the pole.  The
+comparison is made with the astrometric catalog, which may be the HST
+Guide Star Catalog, or it may be any source if the data is placed in
+the correct format.
+
+\section{The Photometry Database}
+
+\subsection{Photometry File Format}
+The photometry database consists of a large number of photometry files
+representing sections of the sky and a file containing information on
+the images incorporated in the database.  These files are called
+alternatively ``region files'' or CPT files (because they have the
+extension {\tt .cpt}).  The photometry files are sorted by Declination
+into different directories, each consisting of a band 7.5\degree\ 
+wide.  These directories have names like n0730, representing the band
+starting at a Declination of +07:30.  The image database (Images.dat)
+is stored in the upper level directory of this directory tree.  In
+fact, the locations of the Images.dat file, the photometry database,
+and such are all flexible and may be changed by altering the
+configuration file.
+
+The name and location of each sky region comes from the Hubble Space
+Telescope Guide Star Catalog.  There is a file, GSCregions.tbl, which
+can be used to find the region file appropriate for any location on
+the sky.  A number of routines exist to make such a query.  
+
+Each photometry file in the photometry database contains all
+observations and all average values for all stars observed within the
+appropriate region on the sky.  The identity of stars is essentially
+determined by the RA and DEC coordinates.  In very crowded regions,
+the end user may need to double check that a neighboring star is not
+contaminating individual detections (see the discusion on {\tt
+  addstar} below).
+
+The photometry files are stored in a binary format, both to reduce the
+volume and to speed access.  As a result, the interpretation of the
+data is machine dependent: little endian machines need to swap the
+data intelligently.  Fortunately, this operation is taken care of
+appropriately, if the programs are compiled with the BYTESWAP option
+set as needed, which is automatically set by the Makefile in the case
+of a Linux machine.  For reference, Suns, SGI, and HP are all big
+endian, while PCs and DECs are little endian.  The automatic
+conversion takes place by using the funcions {\tt Fread} and {\tt Fwrite}
+to substitute for the standard C library {\tt fread} and {\tt fwrite}
+functions.  {\tt Fread} and {\tt Fwrite} are told the datatype being read,
+and they know the layout of the bytes for a particular datatype.
+Anytime the definitions of the relevant structures (see below) are
+changed, {\tt Fread} and {\tt Fwrite} must be updated.  Fortunately, there
+is only one file ({\tt Fread.c}), and it has a single byteswapping
+section, making it easy to adjust the code appropriately.
+
+The format of a single photometry database file consists four
+sections:  
+\begin{itemize}
+\item {\bf header} -- this is a standard FITS header with room for comments
+  about the file or whatever.  Since the rest of the file is not a
+  standard FITS file, the SIMPLE keyword is set to False.  The three
+  necessary keywords are NSTARS, NMEAS, and NMISS, which define the
+  sizes of the next three sections.
+\item {\bf average} -- this section contains all average measurement
+  quantities for each star in the file.  There are NSTARS entries, and
+  each entry is the data from a structure of type Average (defined in
+  loneos.h, and discussed in more detail below).  Thus, the total size
+  in bytes of this section can be found by NSTARS*sizeof(Average).
+\item {\bf measure} -- this section contains the individual
+  measurements for each star in the database.  There are NMEAS
+  entries, and like the average section, each entry is a single
+  structure, in this case of type Measure (in loneos.h, see below).
+  All measurements of a single star are consecutive.  The number of
+  measurements for a single star is defined by average.Nm and the
+  starting entry for a single star is given by average.offset.
+  Therefore, the first measurement of a specific star average[500]
+  would be given by measure[average[500].offset], while the second is 
+  measure[average[500].offset + 1], and so forth.  
+\item {\bf missing} -- this section contains references to all missing
+  measurements of a star.  This means all of those occasions when an
+  image enclosed the location of a star in the database, but no star
+  was detected on the image.  Similar to the case of the measure
+  section, each star has a number of missing entries (which may be 0)
+  given by average.Nn, and the first missing entry is given by
+  average.missing.  Each missing entry value is again a structure of
+  type Missing.  Currently, the only data in the structure is the time
+  of the observation, which allows for an unambiguous recovery of the
+  image where the star was missed.
+\end{itemize}
+
+\subsubsection*{Average Structure defined in loneos.h}
+{\footnotesize
+\begin{verbatim}
+typedef struct {
+  float R;                    /* RA  in decimal degrees */
+  float D;                    /* DEC in decimal degrees */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned short int Nm, Nn;  /* number of measurements, missing */
+  short int Xp;               /* scatter in 1/100 arcsec (-327.67 to +327.67 valid range) */
+  short int Xm;               /* 1000*log(chisq) for magnitude measurement */
+  unsigned short int code;    /* an ID code (ie, star, ghost, satelite, etc) */
+  signed int offset;          /* offset to first measurement */
+  signed int missing;         /* offset to first missing obs */
+} Average; /* 28 bytes / Average */
+\end{verbatim} }
+The structure above defines the average data stored in the photometry
+database for each star.  Most of the entries are self-explantory, but
+some need a bit of clarification.  The two $\chi^2$ entries ({\tt Xp}
+and {\tt Xm}) are the $\chi^2$ values of the average position and the
+average magnitude.  These are stored as logarthmic quantities because
+high dynamic range is not needed.  For the magnitude, the assumption
+is that the star brightness is constant and the $\chi^2$ incorporates
+the photometric errors on each measurement.  For the postion, the
+a default constant error is used \note{True?} and again the assumption
+is that the postion is constant.  As discussed above, the values of
+{\tt Nm} and {\tt Nn} give the number of measurements and missing
+observations for this star, while the entries {\tt offset} and {\tt
+  missing} point to the first {\bf measure} and {\bf missing} entry
+for this star.  The entry {\tt M} is the current best average
+magnitude solution for this star (see the section on Relative
+Photometry).  Finally, the entry {\tt code} is an ID code for each
+object, which may define a variety of things about the object.  For
+example, if the object is known to be variable, or if the object is
+associated with a USNO star, or if the object is a measurement of an
+asteroid.  See below for details of these definitions.
+
+\subsubsection*{Measure Structure defined in loneos.h}
+{\footnotesize
+\begin{verbatim}
+typedef struct {
+  short int dR, dD;           /* 1/100 of arcsec (-327.67 to +327.67 valid range) */
+  short int M;                /* thousandths of mag (-32.767 to 32.767 valid range) */
+  short int Mcal;             /* image cal mag, thousandths of mag (-32.767 to 32.767 valid range) */
+  unsigned char dM;           /* thousandths of mag (0.000 to 0.255 valid range) */
+  char dophot;                /* dophot type (1-9) */
+  unsigned short int source;  /* code to identify photometry source */
+  unsigned int   t;           /* time in seconds (0 - 143 years valid range) */
+  unsigned int average;       /* reference to corresponding average entry */
+  /* upper byte of Measure.average stores flags:
+   limit of 16,777,215 stars (Naverage) in a file (=0xFFFFFF) 
+   flags: average & 0x1000000 */
+} Measure; /* 20 bytes / Measure */
+\end{verbatim} }
+The structure above defines the individual measurement data stored for
+each observation of each star.  The first two entries, dR and dD, are
+the difference between the average coordinates for this star and the
+coordinates determined for this observation.  Clearly, if this
+residual is too large, the individual measurement will not be
+successfully matched with the average star position.  {\tt M} and {\tt
+  dM} are the instrumental magnitude and error for this measurement,
+after correction for the exposure time and a zero point (defined in
+the configuration file as ZERO\_PT).  The entry {\tt t} determines the
+time of the individual observation.  This is important not only for
+timing purposes, but also to determine the source image for this
+observation.  The time and the entry {\tt source} uniquely determine
+the image in the image database that generated this measurement.
+Furthermore, this correspondence allows for determination of the pixel
+coordinates on the chip, by going throught the image astrometry stored
+in the Image database (see below).  The entry {\tt source} is a code which
+identifies the particular CCD/Telescope/Filter setup.  Each unique
+{\tt source} should be treated as an independent filter set for the
+purposes of accurate relative photometric comparisons.  This entry
+also allows external catalog data, such as the USNO or Sloan
+photometry, to be added to the database, as desired.  The entry {\tt
+  average} is a pointer back to the {\bf average} section to allow
+determination of the star from the measurements, in addition to the
+reverse.  The bits of the upper byte of this value are reserved for
+flags used to define particular situations for this measurement.
+Possible values are:
+\begin{itemize}
+\item 1 = BLEND\_IMAGE - the star on the image matched more than one
+  catalog star
+\item 2 = BLEND\_CATALOG - the star in the catalog matched more than
+  one image star
+\item 4 = UPPER\_LIMIT - \note{not really used?}
+\item 8 = CALIBRATED - relative photometry has been performed at least
+  once.
+\end{itemize}
+The entry {\tt dophot} stores the dophot type for this particular
+measurement (1-9: four extra bits in this field).   
+Finally, the value {\tt Mcal} determines the photometric calibration
+of this specific measurment.  This value is an offset appropriate to
+this image (and if needed, this point in time and this chip position)
+to bring the observations of the stars on multiple images to a common
+system (see the section on relative photometry).
+
+\subsection{Image Database}
+
+All images for which photometry has been included in the photometry
+database also have entries in an image database.  The image database
+is (currently) a single file in the upper directory of the photometry
+database.  The name of the file is determined by the IMAGE\_CATALOG
+entry in the configuration file, and is currently set to Images.dat.
+Like the photometry database files, the image database consists of a
+FITS header followed by binary data.  There is only one type of binary
+data: each image has an entry, which is the data from a structure of
+type Image (loneos.h).  The number of images in the file is determined
+by the {\tt NIMAGES} keyword.  If the number of images becomes too
+large, extrapolation of the system to a set of image database files
+with a reference list is quite straightforeward, and could be modeled
+on the organization of the region files (eg, one image database file
+for each Declination directory).
+
+\subsubsection*{Image Structure defined in loneos.h}
+{\footnotesize
+\begin{verbatim}
+typedef struct {
+  Coords         coords;            /* 120 bytes */
+  unsigned int   tzero;             /* readout time row 0 in sec (0 - 142 years valid range) */
+  unsigned int   nstar;             /* number of stars on image */
+  short int      secz;              /* thousanths of airmass (valid range -32.000 -- 32.000) */
+  short int      NX, NY;            /* dimensions of image */
+  short int      apmifit, dapmifit; /* aperture correction and error in thousandths of mag */
+  short int      source;            /* identifier for CCD (each ever used will have a unique letter) */
+  short int      Mcal;              /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      dMcal;             /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      Xm;                /* 10*log(image chi-square) */
+  char           name[32];          /* name of original image */
+  unsigned char  detection_limit;   /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  saturation_limit;  /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  cerror;            /* astrometric error: 1/50 of arcsec (0 -- 5.12 valid range) */
+  unsigned char  fwhm_x, fwhm_y;    /* PSF terms in 25*arcsec (valid range 0.0 -- 10.2" ") */
+  unsigned char  trate;             /* 10000 * scan rate in sec/pix (0 -- 0.0256 valid range, 
+                                       typically 0.0146. this is used only to determine the time
+                                       of the observation, not to find the coordinates.  1 byte
+                                       gives 0.11 sec accuracy */
+  float          exptime;           /* exposure time, seconds */
+  char           code;              /* flag to mark an image as bad or whatever */
+  char           dummy[21];         /* extra space for the future */
+  short int      order;             /* number of terms used for Mrel */
+  short int      Mx, My;
+  short int      Mxx, Mxy, Myy;
+  short int      Mxxx, Mxxy, Mxyy, Myyy;
+  short int      Mxxxx, Mxxxy, Mxxyy, Mxyyy, Myyyy;
+} Image;  /* 240 bytes / Image */
+\end{verbatim}}
+The structure above lists all of the data stored for each image.  A
+substantial amount of information is stored for each image.  First,
+the astrometric calibration information is stored in a {\tt Coords}
+structure (see coordinate systems, above).  The upper and lower limits
+of the photometry in the database (in instrumental magnitudes) is
+stored in {\tt detection\_limit} and {\tt saturation\_limit}.  These
+values are useful for comparison for those stars where observations
+are missing.  The relationship between time and pixel coordinates is
+given by {\tt tzero} and {\tt trate}.  Drift images will have a scan
+rate stored in {\tt trate}, while staring images will have a value of
+0.0 for this entry ({\tt tzero} then applies to the whole image).
+Other important parameters are kept: the astrometric error ({\tt
+  cerror}), the airmass ({\tt secz}), the exposure time ({\tt
+  exptime}), the number of stars detected ({\tt nstar}), the FWHM
+values ({\tt fwhm\_x, fwhm\_y}), the image name ({\tt name}), and the
+size of the image ({\tt NX, NY}).  The entry {\tt source} is used as
+above to define the CCD/Telescope/Filter used for this observation.
+The entry {\tt code} is used to flag images in various ways.
+Currently, only one bit is used, to define if the image has
+unacceptable photometric scatter.  Seven extra bytes are stored for
+future additions.
+
+\subsection{Data Incorporation -- {\tt addstar}}
+
+Images which are completely processed ({\tt dophot, fstat, gastro}) are
+then incorporated into the photometry database with the program {\tt 
+addstar}.  This program decides which region files are appropriate
+for this particular image, then one-by-one adds stars from the image
+to the appropriate region file.  Stars already in the catalog are
+matched with stars in the new image purely by a positional comparison.
+In order to avoid the difficulty of comparisons in the RA and DEC
+coordinate frame, a cartesian projection is performed.  The stars from
+the image being processed and the database stars in the same area are
+projected onto a tangent plane and positional comparisons made in this
+(locally cartesian) coordinate frame.  This avoids the dangerous
+singularities at the pole and also makes the RA 0,360\degree\ boundary
+a trivial problem.  
+
+Several choices must be made in the comparison process.  If a star in
+the catalog is matched with a star in the image, the new measurements
+of that star are added to the database.  If a star in the database
+lies in the field of the image, but is not detected, this information
+is added to the list of missing data.  The only data stored in this
+case is the time and source, which is sufficient to unambiguously
+identify the source image from the image database.  This allows later
+programs to find relevant statistics from the image database, if
+necessary.  If a star is detected in the image, but is not already in
+the database, a new entry is added to the database.  In addition, the
+same ``missing data'' from all previous observations images which have
+covered this location (ie, images already in the image database) are
+included.  This last step is necessary so that the image processing
+order is not important.  
+
+Stars also run the danger of being crowded together.  Since all
+comparisons are performed on the basis of position alone, crowded
+fields may make for ambiguous cross-identifications.  A pair of stars
+are matched if the difference in their positions is less than a
+specified search radius (usually dependent on the astrometric for the
+image).  If more than one catalog stars is correlated with a star in
+an image, the new measurement is added to {\em both} catalog stars,
+and a flag is set noting that this observation had a blended IMAGE.
+Conversely, if a single catalog star is matched with more than one
+image star, both new measurements are added to the one catalog star,
+and a different flat is set, noting that this observation had a
+blended CATALOG.   
+
+\section{Catalog Update and Cleaning}
+
+Occasionally, once a large number of images have been added to the
+photometry database, a set of programs should be run to clean and
+update the CPT files.  This may best be done on CPT files after a
+night's worth of data is incorporated.  These routines define bad
+data, identify the USNO catalog stars, and search for asteroids and
+other junk.  Most of the results from these routines are the addition
+of object flags to the Average data structures (Average.code).  Most
+of these routines process data within a single CPT file at a time.
+
+\subsection{Markstar}
+
+The first of the update programs, {\tt markstar}, identifies bad data
+and flags it.  It makes three types of identifications: bright stars,
+ghost stars, and trails.  In the first case, it searches the HST guide
+star catalog for bright stars within the field of the current CPT
+file.  Three types of pixels are flagged for bright stars.  Any
+measurements within a circular region centered on the star are suspect
+(the ``halo'' of the bright star).  Second, points along the X axis
+are likely to contain flux from the diffraction spikes.  Finally,
+points along the Y axis are likely to have bleeding in addition to the
+diffraction spikes.  Each of these three regions has a different size
+and range which must be defined in the configuration file.  The
+regions are represented by a representative magnitude and a scale
+parameter.  For example, the diameter $d$ of the halo for a star with
+magnitude $m$ is defined as 
+$d = \mbox{BRIGHT\_HALO\_SLOPE} (m - \mbox{BRIGHT\_HALO\_MAG})$.  The X and
+Y axis points are defined by the length of the diffraction spike, with
+an equivalent formula, and the width of the spike.  Since these are
+each independent, there are 8 parameters to define the exclusion
+regions of bright stars.  One of the current drawbacks of this system
+is the reliance on the HST Guide Star photometry.  In the HST Guide
+Star Catalog, only one photometry band is given, so no color
+information is available.  The parameters currently used were defined
+based on a large number of bright stars, with no adjustment for the
+different effective filter system of the Loneos telescope and the HST
+GSC.  Thus, occasionally very red stars will be several magnitudes
+brighter in the real data than reported in the catalog.  As a result,
+large numbers of bad data points in these halos and spikes may be
+missed.  
+
+The second type of bad data flagged by {\tt markstar} are the trails
+from satelite and other space junk.  This portion of the program
+searches for objects which land along a line and which have a high
+linear density.  Parameters may be adjusted to define the width and
+the necessary density or number of points in the line.  Objects in
+lines are also only identified if all observations come from a common
+image.  If an object only has one measurement and it is in a trail,
+the entire Average is flagged to be bad.  However, if an object has
+many measurements and only one is bad, the object is only flagged as
+having some bad data points.  
+
+The third type of bad dat are ghost star images.  The HST GSC is
+searched for stars projected across the telescope optical axis, which
+is defined by the user.  All objects within a region of fixed size is
+marked for ghost stars brighter than a specific cutoff magnitude.
+Similar to the trails, the average is marked as bad only if all
+detections of an object are ghost images.  Otherwise, it is noted that
+some data may be bad.
+
+\subsection{Addusno}
+
+The program {\tt addusno} makes two types of identifications of stars
+with the USNO catalog.  First, it searches for coincidences within a
+small, specified radius.  The choice of this radius is somewhat
+tricky: it is important to catch the association, but also to avoid
+making too many associations.  Unfortunately, astrometric errors in
+the USNO catalog make it necessary to choose a surprisingly large
+radius of 5\asec.  To avoid making double matches (matching the two
+USNO stars to the same photometry database object), only the USNO
+object closest to the object is associated.  There are also a
+substantial number of stars with significant proper motion between the
+USNO epochs and the current date.  It is not unusual to see 8-15\asec\ 
+discrepancy for specific stars.  To catch these objects, any objects
+which do not match the USNO database, but which have more than a
+minimum number of measurements (2 or 3?) are searched for more distant
+USNO companions.  A large, user-specified radius is used for this
+search.  Of course, only USNO stars which are not already matched to
+the database may be candidates for the proper motion match.  This
+stage is suceptible to errors in that a slow-moving solar-system
+object may be associated with a faint USNO star.  For this reason,
+objects flagged as high proper motion stars should be taken with some
+caution.  Of course, during the {\tt addusno} stage, no objects
+already flagged as bad by {\tt markstar} will be matched with USNO
+stars.  
+
+\subsection{Markrock}
+
+Once stars have been identified with the USNO database, it is possible
+to search for asteroid detections.  The program {\tt markrock}
+searches within a specific CPT file for objects which are moving along
+straight lines in (X,Y,t) space.  In order for an object to be found
+by {\tt markrock}, it must have three points, each with only single
+measurements.  The third point must land within a specified distance
+of the line projected from the first two data points.  These
+comparisons are made in the three dimensional space of image position
+and time.  Also, objects are only accepted if they have a speed less
+than a threshold.  If an object is moving too fast, it will make a
+streak on each individual image and will probably be missed anyway.
+Moving objects detected in this way are flagged in the photometry
+database and are also written to a Rock database.  Currently, the Rock
+database just contains ASCII lists of the RA, DEC, Mag, and time for
+each detection.  In fact, the data are stored in pairs of detections,
+with the middle detection saved twice.  This format makes it easy to
+plot lines connecting the points using the connect-the-dots plotting
+style of status/kapa (see below).
+
+One of the current drawbacks of {\tt markrock} is the insistence on
+three observations with only 1 measurement each.  If an object is
+moving too slowly, two of the dectections of the object may be merged
+together.  Enough data is stored in the database to extract these
+individual measurements, so a more sophisticated search is possible.
+It is also currently the case that USNO proper motion stars are
+ignored, but some of these will be asteroid detections which are
+simply too close to a faint USNO stars (it has to be faint because it
+would otherwise be associated with a star in the photometry
+database).  
+
+\section{Relative Photometry -- nrphot}
+
+Once the data has been incorporated in the photometry database and the
+data validation routines have been run, it is possible to determine
+photometric solutions for all of the images.  The program which does
+this is nrphot.  It tries to find a set of solutions for each image
+which reduce both the scatter per image and the scatter per star.  To
+see how this works, we start by defining a measurement of star $j$ on
+image $i$ as $m_{i,j}$.  Every star has an apparent magnitude of
+$M_j$.  Every image has a correction magnitude of $M_i$, defined as
+$m_{i,j}= M_i + M_j$.  (USE SOME OF THE TEXT FROM THE M31 paper).  
+
+The Pipeline implementation of relative photometry includes the
+possibility for up to third order polynomial terms across the field.
+This is needed to account for the different flat fields, particularly
+between images taken with and without the red-block filter.  
+
+\begin{figure}
+\psfig{file=flags.eps,width=9cm}
+\caption{\label{flags} 
+Pictoral representation of the Average.code flags.  The lower byte
+value is used for specific definitions of object types.
+}
+\end{figure}
+
+\subsection{Data Flags}
+
+The identifications made by the above routines are noted as a set of
+flags in the Average.code structure entry.  Some of these flags are
+mutually exclusive, so a certain bit may have more than one meaning,
+depending on the value of other bits.  Figure~\ref{flags} shows a
+pictoral representation of the meaning of the different bit fields.
+The uppermost bit (bit 15) determines if the object may be considered
+a ``fixed'' star or something which has no fixed location.  If it is
+not fixed, and this bit is 1, then there are several options.  The
+second uppermost bit (bit 14) will be set if this object is one of the
+three types of bad data flagged by {\tt markstar}.  The lower byte of
+code is used to define the type of bad data: ghost, trail, or bleed.
+Thus, any datapoints determined to be a satelite trail will have a
+code value of 0xc002 (49154 decimal).  If an object is a moving
+object, but not a bad datapoint, it may be identified as a ``rock''
+(an asteroid) by the program {\tt markrock}.  In this case, the flag
+for an asteroid is turned on (bit 13).  Thus anything identified as a
+rock by {\tt markrock} will have a code value of 0xa000.  The lower
+byte of code is not yet defined for asteroids, but could be reserved
+for distinguishing different classes of asteroids (ie, on the basis of
+orbital speeds, and so forth).  Objects which are fixed may have a
+variety of possible flags.  First, an object may be identified with a
+USNO star (bit 14).  It may be found to exhibit variability (bit 13),
+implying that the relative photometry routine should ignore it.  If
+may be a transient object (bit 12).  Note that, while an object
+associated with a USNO object may not be transient, an object which is
+variable may also not be associated with a USNO object.  Thus, we need
+seperate bits for variable vs transient.  Different types of variable
+objects may be represented with different values of the lower byte.
+For example, a Cepheid may have a specific lower byte code of 0x01,
+which an RR Lyra may have a lower byte code of 0x02.  Thus a Cepheid
+associated with the USNO catalog would have a total code value of
+0x6001. Finally, any of these types of non-moving objects may have
+some bad individual measurements (bit 12) or may be found to have a
+significant proper motion (bit 11).
+
+\section{Holding it Together}
+
+The preceeding sections describe the step-by-step analysis and
+incorporation of the data from individual images.  However, the power
+of the Pipeline comes in applying it to large numbers of images.  At
+LONEOS, we are using four Pentinum II computers to analyse the roughly 200
+images that are observed each night.  
+
+It is best to think of the analysis of images occurring on groups of
+images from a given night.  Of the set of processes discussed above,
+those in section \ref{parallel} are performed on individual images
+essentially independently of the other images.  The rest of the steps,
+both the data incorporation ({\tt addstar} and the database cleaning
+routines occur on the entire batch, one image at a time.  Thus, the
+former routines can easily be run in parallel, but the latter routines
+are run in serial to avoid having two programs writing to same part of
+the database at the same time.  In this model, we would process all of
+the images from a night through the parallel stage of the analysis and
+then run the serial stage only after the entire night is finished in
+the parallel stage.  We thus have a natural division of the pipeline
+into two stages.  
+
+The implementation of the two stages involves several programs, some
+of which are specific to the LONEOS situation, and others which are
+more general.  There are two layers of programs which enable batch
+processing of many images and repeated batch processing over many
+nights.  At the highest level, are Perl scripts which choose what data
+needs to be analysed and when.  These scripts tend to be more
+dependent on the details of the LONEOS implementation. At the next
+level down, these scripts call two main C programs which run the
+images of a given night through the pipeline.  These C programs are
+more general and do not depend on the details of the LONEOS
+implementation.  A defined set of files holds a log of the nights and
+images that have been processed, both for the information of people
+who might want to monitor the progress, but also used by the programs
+to guide decisions about what to analyse next.
+
+\subsection{data and file organization}
+
+There are a set of standard locations for all of the files used and
+created by the Pipeline.  These locations have both abstract names
+used by the pipeline programs and specific definitions used in the
+LONEOS implementation.  In general, the definitions of the files are
+given in the configuration file, 'config.txt' (currently located in a
+hard-wired location, but this may soon change to the mechanism used by
+the lastest version of spicam (3.0)).  The C programs all refer to the
+config file for the definitions, but the Perl scripts currently have
+the definitions hardwired as variable names defined in the beginning
+of each file.  This should be changed to a mechanism that looks in
+the config file, perhaps implemented by a standard Perl function.
+
+Here is a list of the main directories and their meanings:
+
+logdir:         /metis/d11/logs - all log files and the files used by
+the Perl scripts to control their actions (except locks) go here.
+
+workspace:      /irene/d11/workspace - the intermediate analysis
+stages go here, and the tar, gzipped directory files stay here.
+
+dumpspace:      /pallad/d1      - used by slurp to store images
+downloaded from the tape archive.
+
+database:       /hebe/d27/database      - the result database, as also
+the locks and the Rocks.dat file.
+
+references:     /metis/d11/references   - a variety of reference data,
+including the HST GSC, the USNO catalog, the config files, and so
+forth.
+
+source:         /metis/d11/src/ohana    - all of the programs source, 
+binaries, and scripts relevant to running the Pipeline are contained
+in these directories.  
+
+\begin{verbatim}
+database: /hebe/d27/database (also in /metis/d27/database as linked directories).
+ lock files also go in this directory
+workspace: /irene/d11/workspace - temporary storage of result files
+dumpspace: /pallas/d1 - disk to store data dumped from the archive by slurp.
+scriptdir: /metis/d11/logs - all log files
+\end{verbatim}
+
+\subsection{lastnight}
+
+The first step in the process is the Perl script {\tt lastnight},
+which is highly dependent on the specifics of the LONEOS setup.  It
+looks at the disks used by the observers to write the images from a
+given night.  Images are written to a directory called
+/pallas/d[1-6]/YYMMDD where 'YYMMDD' is an abbreviation for the current
+night's date (ie, 990223).  Many files use this naming scheme, so we
+will refer to such a name as 'YYMMDD'.  All of the images are stored
+in this directory with names yyyyMMDDxxxxb.fits where yyyy is the full
+year, MM is month, DD is date, and xxxx is a sequence number.  The 'b'
+refers to the 'b' CCD, and the .fits extension is required.  The
+script {\tt lastnight} is called by a cron job scheduled for 7am on
+{\tt hebe.lowell.edu} (note that the clock on hebe is set to PST, not
+local MST - fix this some day!).  The script searches the disks
+{\tt /pallas/d[1-6]} for a directory of the right form (basically any
+directory beginning with a number).  By default, the program demands
+that the directory have the name derived from today's date, but with
+the flag -any, it will accept any directory.  It then checks to see if
+this directory has been already been analysed (ie, it is included in
+the 'processed.log' file).  If not, it lists all files in that
+directory with the right ending (*b.fits) and places these names in a
+file, {\tt /metis/d11/logs/YYMMDD.inlist}.  It also writes the name of the
+directory (along with some other data) in a file 'extracted.log'.
+When it has successfully ended and identified some data, it calls the
+next script, {\tt stage1} and then quits.
+
+\subsection{slurp}
+
+An alternative to {\tt lastnight} is the Perl script {\tt slurp},
+which looks for data on the tape archive.  This script looks at the
+tape.log file and compares it to the file 'processed.log'.  It tries
+to identify nights listed in the file 'tape.log' which have not been
+included in the 'processed.log'.  If it finds any, it tries to
+download as much of the data as will fit on the disk {\tt /pallas/d1}.
+This script is called from within the {\tt stage1} script and is
+invoked if {\tt stage1} is run without any data already in
+the file 'extracted.log'.
+
+A related pair program is the load/unload C programs which are needed
+for loading and unloading the tape jukebox.  The first, load N, takes
+tape number N and loads it into the tape drive.  The second takes the
+tape a returns it to its slot.  It is important to check on the other
+users of the tape drive to be sure it is not occupied.  This is done
+with the llock, lunlock and llockstat programs.  llockstat lists the
+devices which have been allocated.  llock lets you lock a particular
+service, etc.
+
+\subsection{stage1}
+
+The Perl script {\tt stage1} takes the image list produced by {\tt
+lastnight} or {\tt slurp} (YYMMDD.inlist) and passes the list to the
+controller program {\tt control1} which performs the parallel
+analysis.  The script has a few features to decide which files have
+been analysed through the pipeline and to what extent.  If {\tt
+stage1} is called, it should be able to figure out 1) if there is data
+ready to be analysed (ie, there is a new date reference in the file
+'extracted.log'), 2) if the data has been partially analysed (ie, the
+date reference in 'extracted.log' has not been put in 'processed.log',
+but there are entries in the file YYMMDD.outlist), and 3) which data
+needs to be run through which part of the pipeline (ie, it decides
+which of the images in YYMMDD.outlist have succeeded at which stages
+of the analysis, and starts them at the appropriate spot).  These
+features are most useful if the analysis crashes in the middle or
+needs to be restarted.  The {\tt stage1} script is meant to be run
+without any arguments and it will figure out what needs to be done.
+The one possible argument is the -once flag.  This flag tells {\tt
+stage1} to analyse the data it finds and quit when it finishes, and
+not call another instance of itself.  Otherwise, when it is done, it
+will restart itself, and since there is no new data in
+'extracted.log', it will start a slurp process to get data from the
+archive.  If there is no data in the archive either, this process will
+sleep for an hour and try again.  After a few attempts (2 days?), {\tt
+stage1} will give up and quit without restarting itself.  The script
+writes a log file called \$scriptdir/stage1.NN.log where NN is a
+number in sequence between all runs of {\tt stage1}.  A file
+stage1.count keeps track of which number NN we are on.  When {\tt
+stage1} has finished the analysis of a night's data, it writes the
+date in 'processed.log' to let other processes know what has been
+analysed.  The bulk of {\tt stage1} is not dependent on details of the
+LONEOS setup, but it does expect the files to have the naming scheme
+described above.
+
+\subsection{stage2}
+The serial stage processes are run by the Perl scrip {\tt stage2}.
+Normally, this script is always running.  It waits for the name of a
+night to be added to the 'processed.log file, presumably be {\tt
+stage1}.  Once if finds a new name in this file, it looks for the
+appropriate images in the file 'YYMMDD.addlist'.  It then submits the
+list to the second controlling program {\tt control2}, which runs the
+images through {\tt addstar}.  Images which are successfully run
+through addstar have their *bf.fits (flattened image) and *.obj
+(dophot output) files deleted.  When the entire night is done, the
+rest of the *.obj and *bf.fits images are deleted by {\tt stage2}, the
+directory with the *.cmp and *.log files is tared and gzipped into the
+file YYMMNN.tgz.  The directory is then deleted.  Finally, {\tt
+stage2} calls the scripts {\tt cleaning.pl} and {\tt run.phot} which
+runs the cleanup programs ({\tt markstar - nrphot}) on the database.
+After a successful run, {\tt stage2} will automatically respawn a new
+copy that waits until more data arrives.
+
+\subsection{control1 / control2}
+These two C-programs perform the tasks of farming the images out to
+the different machines for analysis.  The two programs are essentially
+identical, but {\tt control1} is allowed to use all machines and {\tt
+control2} can only use one (hebe.lowell.edu, which has the database
+local to it).  These controller programs start off by executing remote
+shells (using ssh) on a set of machines.  These shells are then used
+to execute the remote commands and are kept open the entire time the
+process runs.  
+
+Both control programs maintain a list of images in a set of queues.  A
+set of rules defines the order in which each image travels through the
+queues.  Each queue is associated with a single process (ie, flatten,
+astrometry, etc).  Images which successfully run through a queue are
+sent on to the defined 'next' queue, while images which fail are
+instead sent to the defined 'fail' queue.  In most cases, the 'fail'
+queue is a process which marks the image as bad and write the name in
+a file, and then puts the image in the 'done' queue.  However, it is
+possible for the 'fail' queue to be another attempt, such as in the
+case of astrometry.  In this example, images which fail the default
+astrometry are run through a second queue which tries again with the
+-loneos flag set (this flag makes the assumption that the header
+information about the LONEOS region number for the image is correct,
+not the RA/DEC information).
+
+The connections on the remote machines are handled as child processes
+with communication through the stdin and stdout/stderr pipes.
+Messages can be sent back and forth though these pipes, and messages
+which are received from a process associated with an image are written
+to a log file with the name imagename.log (where imagename is the root
+name of the image).  The only required messages from the program are
+messages saying ``SUCCESS'' or ``ERROR''.  The programs therefore all
+end with either of these words.  The programs are started within the
+controller as a command to the shell followed by and echo ``PROCESS
+DONE''.  If the controller sees the message ``PROCESS DONE'' without
+either ``SUCCESS'' or ``FAILURE'' (after waiting an appropriately long
+time), it assumes the program crashed.  Currently, no timeout is
+available for the programs.  The flat-field process is run in mana and
+is a slightly special case.  We avoid re-loading the same flat many
+times by starting a mana process once and running the flatten
+procedures as mana function calls.  The images which are analysed
+(with an indicator of success or failure at each stage) are written to
+a file named 'YYMMDD.addlist' by the {\tt control1} and 'YYMMDD.outlist'
+by {\tt control2}.
+
+\subsection{Lock files}
+
+A variety of locking mechanisms are used to avoid overwriting the
+database or running more than one version of the controlling
+programs.  There is a perl program, {\tt locks}, which allows the user to
+set or check the state of the different locks. All programs which
+write to the database check for the existence of a lock on the
+database and refuse to run if it exists.  The control1/2 programs and the
+stage1/2 programs check for their own lock files and only run if they
+don't exist.  Finally, the use can set the lock files 'kill' or 'halt'
+for either stage1 or stage2.  The first tells the process to stop
+immediately, taking care of bookkeeping first.  The second tells the
+process to finish the current set of images but not start a new
+version.  
+
+\subsection{Other Perl Scripts} 
+
+there are several other scripts which are either used to do some
+simple function or are used by the maintainer to check up / work on
+the pipeline.  The two programs 'checkoutlist' and 'checkaddlist' take
+a list of YYMMDD.outlist or YYMMDD.addlist files and list the number
+of images in the file and the number of successes at each analysis
+stage.  There programs 'cleaning.pl' and 'run.phot' deal with the
+cleanup programs and the running of nrphot.  the program
+'backup.stuff' take the first 4 characters of the year/month file
+names and backs up to the jukebox tape all tar files for the given
+month.
+
+\section{Operational Issues and Subtleties}
+
+The current LONEOS system runs principally on four Intel machines
+under linux.  The existing system has been generally quite stable over
+the past year of operation, but there are some subtleties.  One
+significant issue is the RAID disk attached to hebe.lowell.edu.  This
+device is a set of three 9 GB disks merged into one 27GB raid disk.
+The database is stored mostly on this dist and the RAID disk mounted
+on metis.  It is easy to split the database across multiple file
+systems by using links.  Within the database directory, there are
+directories for each of 24 Declination bands (7.5 degrees tall).  The
+easiest way of splitting up the data is to move some of these
+subdirectories to another device and creating links to them at the
+appropriate place.  This is currently done by having a directory
+/metis/d27/database with the remote directories.  
+
+The current problem with the /hebe/d27 RAID results from the poor way
+it was setup.  It was set up without much knowledge of the (at the
+time) rather rudimentary linux RAID system.  As a result, the
+implementation is such that the disk will not automatically be
+configured and mounted at boot time.  There is a script /root/mdstart
+which lists the step to setup the RAID correctly.  It would be helpful
+if the rc.local or rc.sysinit files were fixed to start, fsck and
+mount the RAID automatically at boot.  The setup on metis is
+substantially more mature and is correctly implemented.  It comes up
+when the machine boots.
+
+\section{Visualization -- Status}
+
+Visualization of the data in the photometry database can best be
+performed with the program {\tt status}.  This is a command-line
+driven program with a math and macro language which makes it easy to
+perform complex tasks.  
+
+First, a few notes about the user interface.  The interface has an
+interaction similar to {\tt tcsh}.  The arrows allow editing of
+previous commands.  You can also use emacs-like commands such as
+cntl-a to reach the beginning of the line and cntl-e to reach the end.
+There is command and file completion: if you type part of a command
+(as the first thing on a line) and then type tab, it will fill in as
+much as possible, until the word is not unique.  Typing tab twice at
+that point will list the possible endings.  For any but the first word
+on a line, the same thing will happen for the files in the current
+directory.  It is also possible to type just a fraction of a command,
+as long as it is unique.  An ambiguous command will list the possible
+alternatives.  For example:
+\begin{verbatim}
+status: c
+ambiguous command: c ( catalog cgrid clear create cursor )
+\end{verbatim}
+
+The shell is essentially an interpretive programming language.
+Variables are set as follows:
+\begin{verbatim}
+status: $fred = 10
+\end{verbatim}
+Any expression within curly brackets \{\} is assumed
+to be an arithmetical expression and is evaluated before the line is
+executed.  For example:
+\begin{verbatim}
+echo {$fred*dcos(45)}
+\end{verbatim}
+would give the response 7.07107.  There are math functions cos, sin,
+and tan, which operate on radian expressions, and also dcos, dsin,
+dtan, which operate on degree expressions.  There are also the
+equivalent inverse functions: eg., asin and dasin return radians and
+degrees, respectively.  The help section on Math defines all of the
+available math functions.  
+
+\subsection{Miscellaneous Commands}
+\begin{verbatim}
+!                         -- system call
+?                         -- list commands 
+??                        -- list variables 
+echo                      -- type this line 
+exec                      -- system call
+exit                      -- exit program 
+help                      -- get help on a function 
+output                    -- redirect output to file
+quit                      -- exit program 
+scan                      -- scan line from keyboard or file to variable 
+wait                      -- wait until return is typed
+which                     -- show command 
+\end{verbatim}
+Most of these are self-explanatory.  The command ?? prints the system
+variables.  The {\tt help} command will provide help on a single
+command or, without any arguments, will list all available help
+files (this includes general help not associated with a specific
+command).  
+
+\subsection{Shell Programing}
+\begin{verbatim}
+break                     -- escape from function 
+for                       -- loops 
+if                        -- logical cases 
+input                     -- read command lines from a file 
+macro                     -- deal with the macros 
+\end{verbatim}
+
+There are several options for programming in {\tt status}.  First, a
+file which contains a series of commands can be executed with {\tt
+  input (filename)}.  It is also possible to define macros which will
+behave much like regular commands.  A macro is defined by typing {\tt
+  macro name} or {\tt macro create name} followed by the commands.
+Arguments to the macro are assigned to the variables \$1 .. \$N and
+the number of arguments is given by \$0.  Macros may be defined in
+{\tt input} files, and in fact when {\tt status} is started, it loads
+the file {\tt \~/.statusrc} which may contain default macros.  Simple
+loops and if statements can be performed, and are quite useful for
+complex macros.  
+
+``If'' statements are similar in syntax to C if statements, but only
+the following logical operators are available: $>$, $<$, $=$, !, $|$,
+and \&.  Notice that (currently) there is no $>=$ or $<=$ symbol.  The
+operator ! means ``not equal to'', but cannot be used to negate a
+logical value.  The operators $|$ and \& have the meaning of ``or'' and
+``and'' respectively.  Math expresions in the if statement must be
+contained in curly braces, as elsewhere.  Variables with string values
+may use the logical $=$ operator to test if two strings are the same.
+``For'' loops are quite simplistic.  The form is:
+\begin{verbatim}
+for var first last delta
+ (commands)
+end
+\end{verbatim}
+The value of {\tt \$var} will start at the value {\tt first} and increment by
+{\tt delta} after each loop.  The loop will stop after {\tt \$var} is greater
+than {\tt stop}.  The value {\tt delta} is optional, with 1 assumed.
+The value of {\tt \$var} may be changed during the loop, and if set
+beyong the value of {\tt last} will end the loop early.  
+
+\subsection{Vector Plotting}
+
+\begin{verbatim}
+box                       -- draw a box on the plot
+clear                     -- erase plot
+create                    -- create a new vector
+cursor                    -- get coords from cursor
+grid                      -- plot cartesian grid
+hist                      -- create histogram from a vector
+labels                    -- define labels for plot
+limits                    -- define plot limits
+plot                      -- plot a pair of vectors
+print                     -- write vectors to file
+ps                        -- define labels for plot
+set                       -- vector math
+style                     -- set the style for graph plots
+vectors                   -- list vectors
+zplot                     -- plot scaled points 
+\end{verbatim}
+
+In addition to scalar variables, {\tt status} can manipulate and
+display 1-D vector variables.  Many of the commands which extract data
+from the photometry database place the data in vectors as well as
+plotting them.  A vector can also be created based on a number
+sequence with the command {\tt create name Nelements start delta}.
+The resulting vector has $Nelements$ entries, starting at a value of
+$start$ and running until $start + delta*Nelements$.  If $delta$ is
+0.0, all elements will have the value of $start$.  A histogram of a vector
+may be made with the command {\tt hist}, which creates a new vector
+containing the histogram of the first vector.  The data range and bin
+size of the histogram are defined in same way as with create.  This
+makes it easy to create the index vector that goes with a histogram
+vector:  
+\begin{verbatim}
+hist y Ny 1 100 0.1
+create dx 1 100 0.1
+\end{verbatim}
+The above will create a histogram of y in Ny and the index in dx.
+Plotting this with {\tt plot dx Ny} will show the histogram.
+
+Vector math is performed with a command of the form {\tt set new =
+  (expression)}.  The expression is some math function employing
+vectors and scalars.  A complete listing of the math operators
+available in {\tt set} can be found in the help for {\tt set}.
+
+Once vectors are defined, they may be plotted.  A pair of vectors can
+be plotted against each other if they have the same number of entries.
+The plotting is performed on the graphics window, Kapa.  There are
+actually several graphics windows available to {\tt status}, any of
+which may be used to plot at any time.  Some of the more complex
+operations default to either graphics window 0 or 1, depending on the
+context.  Except for those functions with a pre-defined window, all
+plotting functions apply to the current graphics window unless an
+option {\tt -n N} is given to specify a different window.  The
+plotting style is determined by the command {\tt style} which can set
+the line width, the line type (solid, dashed, dotted, etc), the point
+type (box, cross, etc), the point size, the color, and whether a pair
+of vectors is plotted as a sequence of points, a set of connected
+lines, or a histogram.  Some functions which make plots use their own
+styles, as discussed below.  The function {\tt limits} lets the user
+set the range of the plot axes, or check the current setting.  The
+command {\tt plot} will plot a pair of vectors on the current graphics
+window using the current plotting style for that window.  The command
+{\tt zplot} will plot a pair of vectors with the point size scaled by
+a third vector, with maximum and minimum point sizes representing
+specified values.  The {\tt cursor} command goes the other way: this
+command puts the Kapa window in cursor mode and waits for input from
+Kapa.  The user can then type any alphanumeric key on the graphics
+windows and will be told both the pointer location (in the graphics
+coordinates) and will have the coordinates stored in {\tt status}
+variables.  For example, by typing ``1'' in the sky display window,
+the RA and DEC of the pointer are stored in the variables {\tt \$R1}
+and {\tt \$D1}.  This command can be used to let the user define
+locations or regions of interest on the Kapa window. (Future addition:
+{\tt button}, which does the same with the mouse buttons).  
+
+\subsection{Database Functions}
+
+\begin{verbatim}
+gcat                        -- get catalog at location
+gimages                     -- get images at location
+gstar                       -- get star statistics
+extract                     -- extract average vectors from catalogs
+mextract                    -- extract measurement vectors from catalogs
+imstats                    -- plot image statistics
+imextract                   -- extract image vectors from database
+lcat                        -- list catalogs in display region
+cmatch                      -- match two catalogs
+\end{verbatim}
+
+There are a variety of other commands which directly refer to the
+photometry database.  Some of these functions extract data of various
+types from the database, others perform more complex plotting
+operations.  The commands listed above are those which simply extract
+data from the database.  The first three list information relevant to
+a specific RA, DEC location on the sky: {\tt gcat (RA) (DEC)} lists
+the catalog at the specified location and places the name in the
+variable {\tt \$CATNAME}, {\tt gimages (RA) (DEC)} lists all images
+which overlap the specified location, , {\tt gstars (RA) (DEC)
+  (RADIUS)} lists data about the stars within a specified radius of
+the specified location (all numbers above are given in decimal
+degrees).  Similarly, {\tt lcat} lists the catalogs in the region.
+Imstats lists statistics about each image
+
+The next three commands extract a specific piece of information from
+the photometry database and places it in a vector.  First, {\tt
+  extract} will extract average values for each star and place it in a
+vector.  Next, {\tt mextract} will extract measurement values for each
+star and place it in a vector: as a result a single star may have
+multiple entries in the measurement vectors.  Finally, {\tt imextract}
+will extract image statistics into vectors (not yet implemented).
+
+
+\begin{verbatim}
+catalog                    -- plot catalog stars
+cgrid                      -- plot sky coordinate grid
+cplot                      -- plot vectors in sky coordinates
+czplot                     -- plot scaled vectors in sky coordinates
+images                     -- plot image boxes
+imdense                    -- image density plot
+lcurve                     -- plot lightcurve for a star
+pcat                       -- plot catalog boundaries
+region                     -- define sky region for plot
+resid                      -- plot residuals
+simage                     -- plot stars in an image
+\end{verbatim}
+
+There are two types of database plotting functions: those that display
+or refer to the spatial charateristics of the data and those that
+refer to other types of charatersitics, such as the time domain.  The
+graphics window 0 is reserved for all plots of objects on the sky.
+The command {\tt region} defines the current sky coordinates for plots
+in graphic window 0.  The command {\tt pcat} plots the outline of all
+photometry database files which are within the currently defined
+region (and by default, only those with data).  {\tt images} plots the
+outline of the images in the image database, while {\tt imdense} shows
+the number of images at a location by randomly spacing dots within the
+boundary of the images.  The command {\tt cgrid}
+draws a grid in celestial coordinates on the for the current region.
+
+The most complex, but also one of the most useful command is {\tt
+  catalog}, which plots the positions of stars in the photometry
+database (and others) on the sky.  There are many options to this
+command.  One set allows the user to plot stars from the photometry
+database (the default), from the HST GSC, or from an ASCII text file
+with RA, DEC, and Mag in specified columns.  If the ASCII file has a
+fixed number of bytes per line, the data can be more quickly loaded.
+The size of the points may be scaled by the star magnitude, by the
+number of observations of the star, or by the number of missing
+datapoints for the star.  In addition, points may be plotted only if
+they land in specified magnitude ranges, or with specified numbers of
+measurements, or missed measurements.  Also, objects may be plotted
+only if they have a specified Average.code, so that only asteroids or
+only perfect stars may be plotted.  The plotted vectors may be saved,
+if desired, and the source catalog epoch may be specified as different
+from J2000 (only valid for ASCII data).
+
+Several other commands relate to non-spatial charateristics of images
+and stars.  {\tt lcurve} will plot a light curve for all stars within
+some radius of a point.  {\tt resid} plots the photometry residuals
+for a particular region file.  
+
+\section{Some Examples}
+
+\begin{figure}
+\psfig{file=allsky.ps,width=16cm}
+\caption{\label{allsky} 
+Map of the entire sky, and images added to database.
+}
+\end{figure}
+
+Fig.~\ref{allsky} shows a map of the entire sky, and the location
+of the images currently in the database.  This picture was made with
+the following commands: (output is not shown) \\
+\begin{verbatim}
+status: region 0 0 90 gls
+status: cgrid
+status: style -lw 2 -c red 
+status: images
+status: ps
+\end{verbatim}
+In this example, on the graphics window, the image boxes are shown in
+red.  The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth.  
+
+\begin{figure}
+\psfig{file=catalog.ps,width=9cm}
+\caption{\label{catalog} 
+Comparison between HST GSC and photometry database astrometry. 
+}
+\end{figure}
+
+Fig.~\ref{catalog} shows an example comparison of the photometry
+database star positions and the HST Guide Star Catalog star
+positions.  The crosses are from the photometry database while the
+boxes are from the HST GSC.  The size of both points is a function of
+brightness.  This plot was made with the following commands (starting
+from the previous image):
+\begin{verbatim}
+status: cursor  (typed 1 on region of interest)
+1 137.097858 22.698305
+q 137.097858 22.698305
+status: region $R1 $D1 0.5 TAN
+status: cgrid
+status: box
+status: style -pt 0 
+status: gcat $R1 $D1 
+  0 n2230/1951.cpt *
+status: catalog n2230/1951.cpt -m 12 18
+status: style -pt 2
+status: catalog n2230/1951.cpt -m 12 18 -g
+\end{verbatim}
+
+\begin{figure}
+\psfig{file=lightcurve.ps,width=9cm}
+\caption{\label{lightcurve} 
+Lightcurve for several image stars.
+}
+\end{figure}
+
+Fig.~\ref{lightcurve} shows an example lightcurve from three stars in
+the above field.  The x axis shows the time in seconds, which the y
+axis shows the star magnitudes.  This plot was made with the following commands (starting
+from the previous image):
+{\footnotesize
+\begin{verbatim}
+status: cursor
+1 137.062562 22.791860
+q 137.062562 22.791860
+status: lcurve $R1 $D1 0.01 -l
+status: box
+status: gstar $R1 $D1 0.01
+read 11318 stars from catalog file /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt (22462 total measurements)
+508 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.068619 22.785400 18.438000  1 (2)  1.000000 1.000000
+498 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.062820 22.792747 12.882000  3 (0)  1.000000 1.000000
+501 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.064499 22.800817 15.480000  3 (0)  1.000000 1.000000
+\end{verbatim}}
+The last lines show the database information on the three stars.  
+
+\section{Software Distribution}
+
+All of the code, binaries, and scripts for the Pipeline are stored in
+the ohana tree at /metis/d11/src/ohana.  The ohana directory contains
+subdirs of bin, lib, include, src, config, and doc.  There is a
+Makefile at the top level which calls lower-level Makefiles to build
+the programs desired.  One of the important concepts in the ohana
+distribution is the design of the directory layout to enable binary
+support for multiple architectures.  The Makefiles use the environment
+variable ARCH to determine the appropriate architecture.  The binary
+and library files are stored in subdirectories of the form bin/\$ARCH
+and lib/\$ARCH.  It is therefore necessary that users of the ohana
+system architecture define the ARCH variable correctly.  It is also
+necessary to include in the user's PATH the directory
+/metis/d11/ohana/bin/\$ARCH.  At LONEOS,
+with the solaris sparc station and the linux machines both in use,
+ARCH can take on the values of 'linux' and 'sol'.  The values 'sun4',
+'irix', and 'hp' have also been used at various sites.  A simple bit
+of csh script in the users .cshrc or equivalent can make this
+assignment transparently:
+
+\begin{verbatim}
+set sys=`uname -s` 
+switch ($sys)
+ case IRIX64:
+   setenv ARCH irix;
+   breaksw;
+ case SunOS:
+   set ver=`uname -r | awk '{print substr($1,1,1)}'`;
+   if ($ver == 5) then
+     setenv ARCH sol
+   else 
+     setenv ARCH sun4
+   endif
+   breaksw;
+ case Linux:
+   setenv ARCH linux;
+   breaksw;
+ default:
+   echo "unknown architecture";
+   setenv ARCH unknown;
+   breaksw;
+endsw
+\end{verbatim}
+
+The Makefiles use the ARCH variable not only for destination
+directories, but also for destination binary names.  All files of the
+form fred.c are compiled to fred.ARCH.o (instead of fred.o), 
+uninstalled libraries get the names libfred.ARCH.a (instead of
+libfred.a), while uninstalled programs are called fred.ARCH.  
+
+The perl scripts are kept in ohana/src/perl, with links to the
+appropriate bin directories.  All programs are stored in directories
+of the form ohana/src/program.  The exceptions are gastro (stored in
+ohana/src/astro), and control1/2 (stored in ohana/src/astro).  Also, a
+variety of simple, user-level functions are available in
+ohana/src/misc. 
+
+Most of the ohana package of programs use several common libraries.
+These include the readline library (except for dophot, this is the only
+part of ohana that is not written by Eugene Magnier), which is used
+for command-line interface systems.  Other important libraries are the
+fits library for implementing the FITS specifications in a convenient
+C-friendly environment, and the ohana library, consisting of basic
+string and other basic operations.  
+
+For more details on the programming aspects of parts of the pipeline,
+see the associated document, 'A Programmer's Guide to the Loneos
+Pipeline'
+
+\section {Comparison with USNO catalog}
+
+I have made several comparisons between the USNO catalog and results
+from running images from Loneos through the photometry pipeline.  In
+general, there is a good agreement between the astrometry of the two
+datasets.  There are specfic areas where the two disagree, however.
+First, as discussed above, the USNO star positions may be
+significantly different from the observed star positions simply
+because of proper motion.  The first picure in the ``Comparisons
+between Loneos + USNO'' pages shows several stars with significant
+proper motion.  These are currently being flagged in the photometry
+database as discussed.  Other differences evident in this first
+picture include the classification of bright stars and the splitting
+of faint and bright stars.  In the upper right of the image, two
+bright stars are visible, one of which is actually a double.  The USNO
+catalog does a good job of detecting both, but it also detects and
+includes as stars several diffraction spike points.  The HST GSC only
+detected a single point and did not split the star at all.  The Loneos
+data in our pipeline did not detect the star at all due to saturation.
+A star in the upper left corner shows the ability of {\tt dophot} in
+the Loneos pipeline to split double stars.  This star is, on close
+inspection, clearly a double.  The USNO catalog only detects one.  The
+Pipeline not only splits the two stars, but it also makes the correct
+cross-identification of the stars.  
+
+The second set of pictures shows the ability of the Loneos pipeline to
+deal with small galaxies.  There are two small galaxies in this field,
+both of which are broken into 4 or 5 objects by the USNO catalog, but
+maintained as single objects in the Loneos pipeline.  In another
+example (not shown) a bright star within the disk of a small galaxies
+was clearly split from the galaxy by the pipeline (and also by USNO).
+
+\appendix
+\section{Parameter file params.txt}
+{\footnotesize
+\begin{verbatim}
+# Configuration file for Loneos analysis pipeline
+
+# important files and directories
+GSCFILE                 /host/pikake/d1/gene/gsc/GSCregions.tbl
+GSC_DIR                 /host/pikake/d1/gene/gsc
+CATDIR                  /host/radon/rad3/gene/photcat2
+IMAGE_CATALOG           /host/radon/rad3/gene/photcat2/Images.dat
+IMAGE_CATALOG_TEMPLATE  /host/radon/rad3/gene/photcat2/template.cat
+CATALOG_TEMPLATE        /host/radon/rad3/gene/photcat2/template.cat
+ROCK_CATALOG            /host/radon/rad3/gene/photcat2/Rocks.dat
+USNO_CDROM              /host/tycho/mnt/md0/kevin/zones
+LONEOS_REGIONS          /host/pikake/h1/gene/loneos3/regions.map
+
+# parameters for "fstat"
+# instrumental mag zero point
+ZERO_PT                 24.5
+DOPHOT_CHAR_LINE        129
+DOPHOT_TYPE_FIELD       5
+DOPHOT_AP_FIELD         91
+DOPHOT_PSF_FIELD        52
+MIN_SN_FSTAT            6.0
+
+# parameters for "gastro"
+DEFAULT_RADIUS   32.0
+MINIMUM_RADIUS   1.5
+MIN_ERROR        0.6
+MIN_PRECISE      0.12
+CCD_PC1_1        1
+CCD_PC2_2        -1    # for loneos
+CCD_PC1_2        0
+CCD_PC2_1        0
+ASEC_PIX         2.82
+NFIELD           3.0
+MMIN             6.0
+ROT_ZERO         0
+dROT             1.0
+NROT             2
+POLAR_AXIS_RA    -90.0
+POLAR_AXIS_DEC   89.88
+RA_OFFSET        -90.0
+DEC_OFFSET        0.0
+
+# parameters for "addstar"
+# airmass extinction coefficient (in mag / airmass)
+NSIGMA                  3.0
+ALPHA                   0.1
+XOVERSCAN               60
+YOVERSCAN               50
+
+# parameters for "controller"
+IN_DIR                  /host/pikake/h1/gene
+OUT_DIR                 /host/pikake/h1/gene/loneos4/
+DOPHOT_PARAMS           /host/pikake/h1/gene/src/ohana/config/default_parameters
+FLATTEN_SCRIPT          /host/pikake/h1/gene/src/ohana/config/test.pro
+NEW_IMAGES              images.dat
+USERNAME                gene
+PASSWORD                gene
+
+# here we list all available machines
+NMACHINES               2
+MACHINE0                pikake.astro.washington.edu
+MACHINE1                mildew.astro.washington.edu
+
+# here we define which machines which are always free
+fMACHINE0               1
+fMACHINE1               0
+
+# here we define the machine which runs the database 
+LOCAL_MACHINE           0
+
+# parameters for "status"
+TIME_REFERENCE          90/01/01 00:00:00
+
+# parameters for "nrphot"
+# make this one small to boost change of getting an early region 
+TAU                     3.0             
+SCATTER_LIM             15.0
+MAG_LIM                 17.0
+IMAGE_SCATTER           0.075
+NIMAGE_SCATTER          1.5
+STAR_SCATTER            0.05
+
+### parameters for "markstar"
+SEARCH_RADIUS           360   # region for initial trail hunt, in arcsec 
+TRAIL_WIDTH             5.0   # expected trail width, in arcsec
+NANGLE_BINS             180   # Number of angular bins in 180 degrees
+NPTSINLINE              5     # minimum points needed for trail
+MIN_DENSITY             0.025 # minimum linear density in star/arcsec
+SPACE_SIGMA             10.0  # how much denser than average for trail?
+# parameters defining bright star exclusion regions
+BRIGHT_XTRAIL_WIDTH       5.0 # faintest sat. star
+BRIGHT_XTRAIL_MAG         9.5 # faintest sat. star
+BRIGHT_XTRAIL_SLOPE     -80.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_YTRAIL_WIDTH       5.0 # faintest sat. star
+BRIGHT_YTRAIL_MAG        11.0 # faintest sat. star
+BRIGHT_YTRAIL_SLOPE    -400.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_HALO_MAG           8.5 # faintest sat. star
+BRIGHT_HALO_SLOPE       -28.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+# parameters which define the ghosts
+GHOST_MAG               7.5
+GHOST_RADIUS            200   # in arcsec
+OPTICAL_AXIS1           2154.0
+OPTICAL_AXIS2           2193.0
+
+# parameters for "addusno"
+USNO_RADIUS             5.0   # in arcsec
+USNO_PROPER             20.0   # in arcsec
+USNO_RED                1000  # code for USNO Red data
+USNO_BLUE               1001  # code for USNO Blue data
+# /mnt/cdrom
+
+# parameters for "markrock"
+ROCK_RADIUS             2.0   # in arcsec
+ROCK_MAX_RADIUS         20.0  # in arcsec
+ROCK_MAX_SPEED          0.1   # in arcsec / sec
+\end{verbatim}}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline5.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline5.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline5.tex	(revision 22322)
@@ -0,0 +1,1990 @@
+%\documentstyle[astro,psfig]{article}
+\documentclass{article}
+\usepackage{psfig}
+\usepackage{astro}
+\usepackage{times}
+\GoodMargins
+\newcommand{\note}[1]{({\bf #1})}
+\title{Variability Pipeline}
+\author{Eugene Magnier}
+\begin{document}
+\maketitle
+
+\section{Overview}
+
+The Loneos Pipeline provides automated reduction of images, including
+photometry and astrometry, with the final data stored in a photometry
+database organized by star position on the sky.
+
+Fig.~\ref{pipeline} shows a schematic of the analysis pipeline.
+Images are written to the disk from the camera by the software which
+runs it, {\tt astrocam}.  The resulting images are FITS files (which
+may be arbitrarily sized) and have header keyword parameters RA and
+DEC specifying rough celestial coordinates.  Images are first analyzed
+by {\tt dophot}, which produces instrumental photometry and pixel
+coordinate positions for stars and other objects in the images,
+writing files with a particular format, called OBJ files in this
+discussion.  The results from {\tt dophot} are cleaned up by a program
+called {\tt fstat} which merges the header information from the image
+with the most relevant parts of the OBJ file, creating a new file with
+the CMP format.  Astrometry is performed on the CMP file with the
+program {\tt gastro}.  {\tt Gastro} makes a comparison with an
+astrometric reference database and only changes CMP header keywords as
+a result.  Finally, the processed image CMP file is added to the
+photometry database with the program {\tt addstar}.  The photometry
+database is maintained in a large number of files (CPT files), each
+representing a small region of sky.  There is no relationship between
+the image locations and the boundaries of CPT file.  All of these
+processes occur on individual images, and would typically be performed
+while the observations are occuring, or during the day following, but
+may be performed at any time to add new or archived data to the
+database.
+
+Once large batches of images have been processed, a series of programs
+should be run to clean up the photometry database.  Typically, these
+routines would be run at the end of a night on all of the CPT files to
+which new data were added.  First, {\tt markstar} identifies bad data
+(the trails of satelites, the bleeding columns from stars, ghost
+images), and flags these data points appropriately.  Second, {\tt
+  addusno} matches the catalog stars with stars from the USNO database
+and adds the USNO magnitudes to the photometry database.  Next, {\tt
+  markrock} searches for likely asteroids in the CPT file, marks them,
+and writes the asteroid observations to an asteroid database.  It also
+notes measurements which are likely to be noise and cosmic rays.
+Finally, {\tt nrphot} determines relative photometric calibrations for
+observations in a set of CPT files.  
+
+With the exception of {\tt dophot}, all of the programs in the
+pipeline refer to a single configuration file to determine the large
+number of parameters necessary in the process.  The default parameter
+file is hardwired into the programs, but an alternate configuration
+file may be specified with the environment variable {\tt
+  PIPE\_CONFIG}.  This option makes independent processing of multiple
+data sources trivial.  For example, if the pipeline is to be used for
+both Loneos and SDSS image analysis and display, there could be two
+configuration files (eg, {\tt loneos.txt} and {\tt sdss.txt}), one for
+each data source.  Several important parameters would be different
+between these two, for example, the initial plate scale guess for the
+astrometry routine, or the parameters which define the rough chip
+orientation.  This scheme allows for analysis of different types of
+images, with a common destination photometry database, or for
+independent photometry databases.
+
+\begin{figure}
+\psfig{file=schematic.eps,width=9cm}
+\caption{\label{pipeline} \small
+  Schematic of the photometry pipeline.  Rectangles represent
+  programs, while ovals represent data products.  Arrows show the
+  direction of travel of information.  The flatten routine is a filter
+  whose position may change as the pipeline matures.  }
+\end{figure}
+
+Data from different sources can be processed by the pipeline and
+stored in the same photometry database, since all photometry is tagged
+with a source ID in the database (as are the images in the image
+database).  This allows us to include, for example, the USNO catalog
+magnitude measurements (or a subset) in the photometry database for a
+quick comparion.  Different sources may be considered different
+filters, so color-color and color-magnitude diagrams could be
+extracted by specifying choices for source catalog.  Currently each
+object has a single primary photometric system, and individual
+observations may be combined to determine that average (eg, if they
+are different photometry sources with roughly the filter bandpass) or
+they may be ignored when calculating relative magnitudes (eg, the USNO
+magnitudes would be ignored).  This may be expanded upon by defining
+multiple average magnitudes and allowing different measurements to
+contribute only to the appropriate average.  The determination of the
+photometry code for a particular image is currently not well defined.
+There are several points in the analysis where the user (or the
+controlling program) may specify the photometry source.  A table
+should be kept in the photometry catalog to define the various
+photometry codes.  
+
+\section{Image Analysis Pipeline}
+\label{parallel}
+\subsection{Flat Fielding -- Mana / Flatten}
+
+Flat fielding is performed on each image after it has been written to
+disk by the acquisition software.  The processing is performed with
+mana (see mana user's guide) using a script written to work with the
+analysis pipeline processing software.  There are several important
+aspects of the script which make it work with the containing programs.
+First, the script is contained within a file, flatten.pro, in the
+references directory.  The script is invoked by calling it on the
+command line when mana is started.  If mana is started with a file
+name on the command line, that file is loaded with in 'input'.  The
+file defines a macro 'flatten', and also loads the flat-field file.
+The flattening process is actually done by calling within mana the
+macro flatten, with the input and output file names as arguments.  By
+loading the flat-field at the start of mana, instead of on each
+execution of flatten, we save the load time of each flat-field image
+(which is 2x the size of the basic image, being 4byte floats per
+pixel).  \note{The location of the flat field is currently hard-wired in the
+flatten script - this should probably be changed in the future.}  One
+of the aspects of this is that the macro loads a flatfield with a
+specific name and location.  This file is actually a link; The
+higher-level controlling process (stage1) creates a link to the
+correct flatfield.  This allows the analysis process to use different
+flatfields for different situations.  At LONEOS, we currently use a
+flat field generated from a well-understood night.  This is probably
+not the best solution as the flatfield changes with time.  As
+discussed below, the photometry solution program, nrphot, fits a
+high-order polynomial to the relative photometry, allowing for
+differences from the correct flatfield.
+ 
+\subsection{Photometry -- Dophot}
+
+Photometry is performed using a varient of {\tt Dophot}.  The version
+of dophot is based on Dophot 2.0, but it has been adapted to
+streamline processing of many files.  First, with this version of
+dophot, any FITS format data is safely read in.  Instead of compiling
+to a fixed maximum size, only the memory needed to contain the image
+matrix is allocated.  The image is also converted to REAL*32 from
+whatever BITPIX for processing.  Finally, only a single, complete
+parameter file is loaded by dophot.  There is no option for a modified
+and a default parameter file.  The parameter file to be used is
+defined, along with the input image and the output object list on the
+command line when the program is run: \\
+{\tt dophot image.fits image.obj parameter\_file} \\
+To achive this, the code from dophot was wrapped within a C program
+which interprets the command line arguments, allocates the necessary
+memory, and reads in the image.  The rest of the processing is
+performed by dophot in the standad manner.  Notice that we use a
+consistent naming convention through the pipeline reductions.  The
+image is called {\tt foo.fits}, while the resulting dophot-produced
+photometry list is called {\tt foo.obj}.  
+
+\subsection{Cleanup -- fstat}
+
+After an image is processed by dophot, the object file ({\tt foo.obj})
+is converted to a more-compact, more-complete file called a CMP file,
+with a name of the form ({\tt foo.cmp}).  This conversion is done with
+the program {\tt fstat}.  The CMP file consists of the FITS header
+from the original image ({\tt foo.fits}), with some additional
+keywords to be used at later stages in the analysis, followed by an
+ASCII list of the interesting data from ({\tt foo.obj}).  In this
+list, types 6 and 8 are excluded, and only the following values are
+kept:
+\begin{verbatim}
+X       Y     Mag   dMag t log(sky) 
+1342.0  106.1 14.166 000 4 3.2
+\end{verbatim}
+Objects with a signal-to-noise ratio lower than a specified cutoff
+({\tt MIN\_SN\_FSTAT}) are also excluded.  Some general information
+about the image is derived by {\tt fstat} (FWHM, saturation and
+completeness limits, number of each dophot type) and stored as
+keywords in the header.  The photometry source must be specified at
+this stage as one of the command line arguments, though the value used
+may be overridden in one of the stages belowe.  The resulting file can
+now stand on its own without reference to the original image.  The
+keywords used by {\tt fstat} include the minimum signal to noise, a
+rough guess at the zero point ({\tt ZERO\_PT}), and four numbers
+defining the format of the {\tt dophot} OBJ file.
+
+\subsection{Astrometry -- gastro}
+
+Astrometry is performed automatically by the program {\tt gastro}.
+{\tt gastro} loads a CMP file and determines an initial guess for the
+center coordinates (based on the RA and DEC header keywords).  The
+program also uses the configuration information about the plate scale
+and rough orientation of the image to get close to the final solution.
+Also, the true sky position of the telescope pole may be defined to
+allow astrometry on images taken close to the pole.  The comparison is
+made with the astrometric catalog, which may be the HST Guide Star
+Catalog, or it may be any source if the data is placed in the correct
+format.
+
+\section{The Photometry Database}
+
+\subsection{Photometry File Format}
+The photometry database consists of a large number of photometry files
+representing sections of the sky and a file containing information on
+the images incorporated in the database.  These files are called
+alternatively ``region files'' or CPT files (because they have the
+extension {\tt .cpt}).  The photometry files are sorted by Declination
+into different directories, each consisting of a band 7.5\degree\ 
+wide.  These directories have names like n0730, representing the band
+starting at a Declination of +07:30.  The image database (Images.dat)
+is stored in the upper level directory of this directory tree.  In
+fact, the locations of the Images.dat file, the photometry database,
+and such are all flexible and may be changed by altering the
+configuration file.
+
+The name and location of each sky region comes from the Hubble Space
+Telescope Guide Star Catalog.  There is a file, GSCregions.tbl, which
+can be used to find the region file appropriate for any location on
+the sky.  A number of routines exist to make such a query.  
+
+Each photometry file in the photometry database contains all
+observations and all average values for all stars observed within the
+appropriate region on the sky.  The identity of stars is essentially
+determined by the RA and DEC coordinates.  In very crowded regions,
+the end user may need to double check that a neighboring star is not
+contaminating individual detections (see the discusion on {\tt
+  addstar} below).
+
+The photometry files are stored in a binary format, both to reduce the
+volume and to speed access.  As a result, the interpretation of the
+data is machine dependent: little endian machines need to swap the
+data intelligently.  Fortunately, this operation is taken care of
+appropriately, if the programs are compiled with the BYTESWAP option
+set as needed, which is automatically set by the Makefile in the case
+of a Linux machine.  For reference, Suns, SGI, and HP are all big
+endian, while PCs and DECs are little endian.  The automatic
+conversion takes place by using the funcions {\tt Fread} and {\tt Fwrite}
+to substitute for the standard C library {\tt fread} and {\tt fwrite}
+functions.  {\tt Fread} and {\tt Fwrite} are told the datatype being read,
+and they know the layout of the bytes for a particular datatype.
+Anytime the definitions of the relevant structures (see below) are
+changed, {\tt Fread} and {\tt Fwrite} must be updated.  Fortunately, there
+is only one file ({\tt Fread.c}), and it has a single byteswapping
+section, making it easy to adjust the code appropriately.
+
+The format of a single photometry database file consists four
+sections:  
+\begin{itemize}
+\item {\bf header} -- this is a standard FITS header with room for comments
+  about the file or whatever.  Since the rest of the file is not a
+  standard FITS file, the SIMPLE keyword is set to False.  The three
+  necessary keywords are NSTARS, NMEAS, and NMISS, which define the
+  sizes of the next three sections.
+\item {\bf average} -- this section contains all average measurement
+  quantities for each star in the file.  There are NSTARS entries, and
+  each entry is the data from a structure of type Average (defined in
+  loneos.h, and discussed in more detail below).  Thus, the total size
+  in bytes of this section can be found by NSTARS*sizeof(Average).
+\item {\bf measure} -- this section contains the individual
+  measurements for each star in the database.  There are NMEAS
+  entries, and like the average section, each entry is a single
+  structure, in this case of type Measure (in loneos.h, see below).
+  All measurements of a single star are consecutive.  The number of
+  measurements for a single star is defined by average.Nm and the
+  starting entry for a single star is given by average.offset.
+  Therefore, the first measurement of a specific star average[500]
+  would be given by measure[average[500].offset], while the second is 
+  measure[average[500].offset + 1], and so forth.  
+\item {\bf missing} -- this section contains references to all missing
+  measurements of a star.  This means all of those occasions when an
+  image enclosed the location of a star in the database, but no star
+  was detected on the image.  Similar to the case of the measure
+  section, each star has a number of missing entries (which may be 0)
+  given by average.Nn, and the first missing entry is given by
+  average.missing.  Each missing entry value is again a structure of
+  type Missing.  Currently, the only data in the structure is the time
+  of the observation, which allows for an unambiguous recovery of the
+  image where the star was missed.
+\end{itemize}
+
+\subsubsection*{Average Structure defined in loneos.h}
+\begin{verbatim}
+typedef struct {
+  float R;                    /* RA  in decimal degrees */
+  float D;                    /* DEC in decimal degrees */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned short int Nm, Nn;  /* number of measurements, missing */
+  short int Xp;               /* scatter in 1/100 arcsec (-327.67 to +327.67 valid range) */
+  short int Xm;               /* 1000*log(chisq) for magnitude measurement */
+  unsigned short int code;    /* an ID code (ie, star, ghost, satelite, etc) */
+  signed int offset;          /* offset to first measurement */
+  signed int missing;         /* offset to first missing obs */
+} Average; /* 28 bytes / Average */
+\end{verbatim}
+The structure above defines the average data stored in the photometry
+database for each star.  Most of the entries are self-explantory, but
+some need a bit of clarification.  The entry {\tt Xm} is the magnitude
+$\chi^2$ value for the star, under the assumption that the star
+brightness is constant.  This $\chi^2$ incorporates the photometric
+errors on each measurement and is stored as 1000 times the logarithm
+of the $\chi^2$ because high dynamic range is not needed.  The entry
+{\tt Xp} is the scatter about the average postion, in 10
+milli-arcseconds.  As discussed above, the values of {\tt Nm} and {\tt
+  Nn} give the number of measurements and missing observations for
+this star, while the entries {\tt offset} and {\tt missing} point to
+the first {\bf measure} and {\bf missing} entry for this star.  The
+entry {\tt M} is the current best average magnitude solution for this
+star (see the section on Relative Photometry).  Finally, the entry
+{\tt code} is an ID code for each object, which may define a variety
+of things about the object.  For example, if the object is known to be
+variable, or if the object is associated with a USNO star, or if the
+object is a measurement of an asteroid.  See below for details of
+these definitions.
+
+\subsubsection*{Measure Structure defined in loneos.h}
+{\small
+\begin{verbatim}
+typedef struct {
+  short int dR, dD;           /* 1/100 of arcsec (-327.67 to +327.67 valid range) */
+  short int M;                /* thousandths of mag (-32.767 to 32.767 valid range) */
+  short int Mcal;             /* image cal mag, thousandths of mag (-32.767 to 32.767 valid range) */
+  unsigned char dM;           /* thousandths of mag (0.000 to 0.255 valid range) */
+  char dophot;                /* dophot type (1-9) */
+  unsigned short int source;  /* code to identify photometry source */
+  unsigned int   t;           /* time in seconds (0 - 143 years valid range) */
+  unsigned int average;       /* reference to corresponding average entry */
+  /* upper byte of Measure.average stores flags:
+   limit of 16,777,215 stars (Naverage) in a file (=0xFFFFFF) 
+   flags: average & 0x1000000 */
+} Measure; /* 20 bytes / Measure */
+\end{verbatim}
+}
+The structure above defines the individual measurement data stored for
+each observation of each star.  The first two entries, dR and dD, are
+the difference between the average coordinates for this star and the
+coordinates determined for this observation.  Clearly, if this
+residual is too large, the individual measurement will not be
+successfully matched with the average star position.  {\tt M} and {\tt
+  dM} are the instrumental magnitude and error for this measurement,
+after correction for the exposure time and a zero point (defined in
+the configuration file as ZERO\_PT).  The entry {\tt t} determines the
+time of the individual observation.  This is important not only for
+timing purposes, but also to determine the source image for this
+observation.  The time and the entry {\tt source} uniquely determine
+the image in the image database that generated this measurement.
+Furthermore, this correspondence allows for determination of the pixel
+coordinates on the chip, by going throught the image astrometry stored
+in the Image database (see below).  The entry {\tt source} is a code which
+identifies the particular CCD/Telescope/Filter setup.  Each unique
+{\tt source} should be treated as an independent filter set for the
+purposes of accurate relative photometric comparisons.  This entry
+also allows external catalog data, such as the USNO or Sloan
+photometry, to be added to the database, as desired.  The entry {\tt
+  average} is a pointer back to the {\bf average} section to allow
+determination of the star from the measurements, in addition to the
+reverse.  The bits of the upper byte of this value are reserved for
+flags used to define particular situations for this measurement.
+Possible values are:
+\begin{itemize}
+\item 1 = BLEND\_IMAGE - the star on the image matched more than one
+  catalog star
+\item 2 = BLEND\_CATALOG - the star in the catalog matched more than
+  one image star
+\item 4 = UPPER\_LIMIT - \note{not really used?}
+\item 8 = CALIBRATED - relative photometry has been performed at least
+  once.
+\end{itemize}
+The entry {\tt dophot} stores the dophot type for this particular
+measurement (1-9: four extra bits in this field).   
+Finally, the value {\tt Mcal} determines the photometric calibration
+of this specific measurment.  This value is an offset appropriate to
+this image (and if needed, this point in time and this chip position)
+to bring the observations of the stars on multiple images to a common
+system (see the section on relative photometry).
+
+\subsection{Image Database}
+
+All images for which photometry has been included in the photometry
+database also have entries in an image database.  The image database
+is (currently) a single file in the upper directory of the photometry
+database.  The name of the file is determined by the IMAGE\_CATALOG
+entry in the configuration file, and is currently set to Images.dat.
+Like the photometry database files, the image database consists of a
+FITS header followed by binary data.  There is only one type of binary
+data: each image has an entry, which is the data from a structure of
+type Image (loneos.h).  The number of images in the file is determined
+by the {\tt NIMAGES} keyword.  If the number of images becomes too
+large, extrapolation of the system to a set of image database files
+with a reference list is quite straightforeward, and could be modeled
+on the organization of the region files (eg, one image database file
+for each Declination directory).
+
+\subsubsection*{Image Structure defined in loneos.h}
+{\small
+\begin{verbatim}
+typedef struct {
+  Coords         coords;            /* 120 bytes */
+  unsigned int   tzero;             /* readout time row 0 in sec (0 - 142 years valid range) */
+  unsigned int   nstar;             /* number of stars on image */
+  short int      secz;              /* thousanths of airmass (valid range -32.000 -- 32.000) */
+  short int      NX, NY;            /* dimensions of image */
+  short int      apmifit, dapmifit; /* aperture correction and error in thousandths of mag */
+  short int      source;            /* identifier for CCD (each ever used will have a unique letter) */
+  short int      Mcal;              /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      dMcal;             /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      Xm;                /* 10*log(image chi-square) */
+  char           name[32];          /* name of original image */
+  unsigned char  detection_limit;   /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  saturation_limit;  /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  cerror;            /* astrometric error: 1/50 of arcsec (0 -- 5.12 valid range) */
+  unsigned char  fwhm_x, fwhm_y;    /* PSF terms in 25*arcsec (valid range 0.0 -- 10.2" ") */
+  unsigned char  trate;             /* 10000 * scan rate in sec/pix (0 -- 0.0256 valid range, 
+                                       typically 0.0146. this is used only to determine the time
+                                       of the observation, not to find the coordinates.  1 byte
+                                       gives 0.11 sec accuracy */
+  float          exptime;           /* exposure time, seconds */
+  char           code;              /* flag to mark an image as bad or whatever */
+  char           dummy[21];         /* extra space for the future */
+  short int      order;             /* number of terms used for Mrel */
+  short int      Mx, My;
+  short int      Mxx, Mxy, Myy;
+  short int      Mxxx, Mxxy, Mxyy, Myyy;
+  short int      Mxxxx, Mxxxy, Mxxyy, Mxyyy, Myyyy;
+} Image;  /* 240 bytes / Image */
+\end{verbatim}}
+
+The structure above lists all of the data stored for each image.  A
+substantial amount of information is stored for each image.  First,
+the astrometric calibration information is stored in a {\tt Coords}
+structure (see coordinate systems, above).  The upper and lower limits
+of the photometry in the database (in instrumental magnitudes) is
+stored in {\tt detection\_limit} and {\tt saturation\_limit}.  These
+values are useful for comparison for those stars where observations
+are missing.  The relationship between time and pixel coordinates is
+given by {\tt tzero} and {\tt trate}.  Drift images will have a scan
+rate stored in {\tt trate}, while staring images will have a value of
+0.0 for this entry ({\tt tzero} then applies to the whole image).
+Other important parameters are kept: the astrometric error ({\tt
+  cerror}), the airmass ({\tt secz}), the exposure time ({\tt
+  exptime}), the number of stars detected ({\tt nstar}), the FWHM
+values ({\tt fwhm\_x, fwhm\_y}), the image name ({\tt name}), and the
+size of the image ({\tt NX, NY}).  The entry {\tt source} is used as
+above to define the CCD/Telescope/Filter used for this observation.
+The entry {\tt code} is used to flag images in various ways.
+Currently, only two bits are used, to define if the image has been
+photometrically calibrated and if the calibration is acceptable.
+There are 21 extra bytes are stored for future additions.
+
+\subsection{Data Incorporation -- {\tt addstar}}
+
+Images which are completely processed ({\tt dophot, fstat, gastro}) are
+then incorporated into the photometry database with the program {\tt 
+addstar}.  This program decides which region files are appropriate
+for this particular image, then one-by-one adds stars from the image
+to the appropriate region file.  Stars already in the catalog are
+matched with stars in the new image purely by a positional comparison.
+In order to avoid the difficulty of comparisons in the RA and DEC
+coordinate frame, a cartesian projection is performed.  The stars from
+the image being processed and the database stars in the same area are
+projected onto a tangent plane and positional comparisons made in this
+(locally cartesian) coordinate frame.  This avoids the dangerous
+singularities at the pole and also makes the RA 0,360\degree\ boundary
+a trivial problem.  
+
+Several choices must be made in the comparison process.  If a star in
+the catalog is matched with a star in the image, the new measurements
+of that star are added to the database.  If a star in the database
+lies in the field of the image, but is not detected, this information
+is added to the list of missing data.  The only data stored in this
+case is the time and source, which is sufficient to unambiguously
+identify the source image from the image database.  This allows later
+programs to find relevant statistics from the image database, if
+necessary.  If a star is detected in the image, but is not already in
+the database, a new entry is added to the database.  In addition, the
+same ``missing data'' from all previous observations images which have
+covered this location (ie, images already in the image database) are
+included.  This last step is necessary so that the image processing
+order is not important.  
+
+Stars also run the danger of being crowded together.  Since all
+comparisons are performed on the basis of position alone, crowded
+fields may make for ambiguous cross-identifications.  A pair of stars
+are matched if the difference in their positions is less than a
+specified search radius (dependent on the scatter in the astrometric
+solution for the image).  If more than one catalog stars are correlated
+with a star in an image, the new measurement is added to {\em both}
+catalog stars, and a flag is set noting that this observation had a
+blended IMAGE.  Conversely, if a single catalog star is matched with
+more than one image star, both new measurements are added to the one
+catalog star, and a different flat is set, noting that this
+observation had a blended CATALOG.
+
+\section{Catalog Update and Cleaning}
+
+Occasionally, once a large number of images have been added to the
+photometry database, a set of programs should be run to clean and
+update the CPT files.  This may best be done on CPT files after a
+night's worth of data is incorporated.  These routines define bad
+data, identify the USNO catalog stars, and search for asteroids and
+other junk.  Most of the results from these routines are the addition
+of object flags to the Average data structures (Average.code).  Most
+of these routines process data within a single CPT file at a time.
+
+\subsection{Markstar}
+
+The first of the update programs, {\tt markstar}, identifies bad data
+and flags it.  It makes three types of identifications: bright stars,
+ghost stars, and trails.  In the first case, it searches the HST guide
+star catalog for bright stars within the field of the current CPT
+file.  Three types of pixels are flagged for bright stars.  Any
+measurements within a circular region centered on the star are suspect
+(the ``halo'' of the bright star).  Second, points along the X axis
+are likely to contain flux from the diffraction spikes.  Finally,
+points along the Y axis are likely to have bleeding in addition to the
+diffraction spikes.  Each of these three regions has a different size
+and range which must be defined in the configuration file.  The
+regions are represented by a representative magnitude and a scale
+parameter.  For example, the diameter $d$ of the halo for a star with
+magnitude $m$ is defined as 
+$d = \mbox{BRIGHT\_HALO\_SLOPE} (m - \mbox{BRIGHT\_HALO\_MAG})$.  The X and
+Y axis points are defined by the length of the diffraction spike, with
+an equivalent formula, and the width of the spike.  Since these are
+each independent, there are 8 parameters to define the exclusion
+regions of bright stars.  One of the current drawbacks of this system
+is the reliance on the HST Guide Star photometry.  In the HST Guide
+Star Catalog, only one photometry band is given, so no color
+information is available.  The parameters currently used were defined
+based on a large number of bright stars, with no adjustment for the
+different effective filter system of the Loneos telescope and the HST
+GSC.  Thus, occasionally very red stars will be several magnitudes
+brighter in the real data than reported in the catalog.  As a result,
+large numbers of bad data points in these halos and spikes may be
+missed.  
+
+The second type of bad data flagged by {\tt markstar} are the trails
+from satelite and other space junk.  This portion of the program
+searches for objects which land along a line and which have a high
+linear density.  Parameters may be adjusted to define the width and
+the necessary density or number of points in the line.  Objects in
+lines are also only identified if all observations come from a common
+image.  If an object only has one measurement and it is in a trail,
+the entire Average is flagged to be bad.  However, if an object has
+many measurements and only one is bad, the object is only flagged as
+having some bad data points.  
+
+The third type of bad data are ghost star images.  The HST GSC is
+searched for stars projected across the telescope optical axis, which
+is defined by the user.  All objects within a region of fixed size are
+marked for ghost stars brighter than a specific cutoff magnitude.
+Similar to the trails, the average is marked as bad only if all
+detections of an object are ghost images.  Otherwise, it is noted that
+some data may be bad.
+
+To run 'markstar' by hand, the syntax is: {\tt markstar (file.cpt)}.
+There are several optional flags, which are listed if you type
+markstar by itself.
+
+\subsection{Addusno}
+
+The program {\tt addusno} makes two types of identifications of stars
+with the USNO catalog.  First, it searches for coincidences within a
+small, specified radius.  The choice of this radius is somewhat
+tricky: it is important to catch the association, but also to avoid
+making too many associations.  Unfortunately, astrometric errors in
+the USNO catalog make it necessary to choose a surprisingly large
+radius of 5\asec.  To avoid making double matches (matching the two
+USNO stars to the same photometry database object), only the USNO
+object closest to the object is associated.  There are also a
+substantial number of stars with significant proper motion between the
+USNO epochs and the current date.  It is not unusual to see 8-15\asec\ 
+discrepancy for specific stars.  To catch these objects, any objects
+which do not match the USNO database, but which have more than a
+minimum number of measurements (2 or 3?) are searched for more distant
+USNO companions.  A large, user-specified radius is used for this
+search.  Of course, only USNO stars which are not already matched to
+the database may be candidates for the proper motion match.  This
+stage is susceptible to errors in that a slow-moving solar-system
+object may be associated with a faint USNO star.  For this reason,
+objects flagged as high proper motion stars should be taken with some
+caution.  Of course, during the {\tt addusno} stage, no objects
+already flagged as bad by {\tt markstar} will be matched with USNO
+stars.  
+
+To run 'addusno' by hand, the syntax is: {\tt addusno (file.cpt)}.
+There are several optional flags, which are listed if you type
+addusno by itself.
+
+\subsection{Markrock}
+
+Once stars have been identified with the USNO database, it is possible
+to search for asteroid detections.  The program {\tt markrock}
+searches within a specific CPT file for objects which are moving along
+straight lines in (X,Y,t) space.  In order for an object to be found
+by {\tt markrock}, it must have three points, each with only single
+measurements.  The third point must land within a specified distance
+of the line projected from the first two data points.  These
+comparisons are made in the three dimensional space of image position
+and time.  Also, objects are only accepted if they have a speed less
+than a threshold.  If an object is moving too fast, it will make a
+streak on each individual image and will probably be missed anyway.
+Moving objects detected in this way are flagged in the photometry
+database and are also written to a Rock database.  Currently, the Rock
+database just contains ASCII lists of the RA, DEC, Mag, and time for
+each detection.  In fact, the data are stored in pairs of detections,
+with the middle detection saved twice.  This format makes it easy to
+plot lines connecting the points using the connect-the-dots plotting
+style of status/kapa (see below).
+
+One of the current drawbacks of {\tt markrock} is the insistence on
+three observations with only 1 measurement each.  If an object is
+moving too slowly, two of the dectections of the object may be merged
+together.  Enough data is stored in the database to extract these
+individual measurements, so a more sophisticated search is possible.
+It is also currently the case that USNO proper motion stars are
+ignored, but some of these will be asteroid detections which are
+simply too close to a faint USNO stars (it has to be faint because it
+would otherwise be associated with a star in the photometry
+database).  
+
+To run 'markrock' by hand, the syntax is: {\tt markrock (file.cpt)}.
+There are several optional flags, which are listed if you type
+markrock by itself.
+
+\section{Relative Photometry -- nrphot}
+
+Once the data has been incorporated in the photometry database and the
+data validation routines have been run, it is possible to determine
+photometric solutions for all of the images.  The program which does
+this is nrphot.  It tries to find a set of solutions for each image
+which reduce both the scatter per image and the scatter per star.
+\note{the following section is quoted verbatim from the M31 paper
+  Magnier et al 1992, A\&A Supp 96, 379.  It could use a little
+  editing to fit the reality of the LONEOS implementation.}
+
+In order to convert from instrumental magnitudes reported by DOPHOT
+to a standard photometric system, relative photometry was used, and
+all values converted to one internal magnitude system for the entire
+set of data taken with each filter.  Note that this is not {\em
+  differential}\ photometry on a single image, but rather {\em
+  relative}\ photometry between many images.  This method was
+advantageous for several reasons.  First, since many of our nights
+were not photometric, relative photometry allowed us to use all of our
+data, even that obtained on the cloudy nights.  Second, it allowed us
+to use photometry from several of the recent CCD photometric studies
+(Massey \etal\ 1986; Hodge \& Lee 1988; Freedman 1989; Richer \etal\ 
+1990) in small fields of M31 to calibrate our data to a standard
+system.  Third, relative photometry allowed us to check the agreement
+of the photometry of the calibration data.
+
+        In order to perform relative photometry, we used multiple
+measurements of individual stars on overlapping images to connect
+neighboring images.  A standard method of converting between
+instrumental magnitudes and a standard photometric system is to use a
+photometry equation of the form:
+\begin{equation}        
+M_{app} = c_{\lambda} + m + a_{\lambda}*\zeta + \gamma_{color}*(color) \\
+\label{M_mag}
+\end{equation}
+where $m$ is the instrumental magnitude,
+\begin{equation}        
+m = -2.5\log(N_{el}) + 2.5\log(t). \\
+\label{inst_mag}
+\end{equation}
+$N_{el}$ is the number of electrons measured in a single star, $t$ is
+the exposure time for the image, $M_{app}$ is the apparent magnitude
+of the star in the standard system, $\zeta$ is the airmass of the
+image, and $color$ is the value of a color index (\ie\ \B\ - \V\ or
+\V\ - \R) for the particular star.  The subscript $\lambda$ refers to
+the filter and the subscript $color$ refers to the color index.  For a
+given filter, several possible color indices could be used (\ie\, for
+calibration of \V\ data, both \B\ - \V\ and \V\ - \R\ could be used to
+measure the response of the detector with the color of the star);
+however the colors of stars are highly correlated, so only one color
+index need be used at a time.  $c_{\lambda}$, $a_{\lambda}$, and
+$\gamma_{color}$ are calibration coefficients dependent on the
+instruments and the site.  $c_{\lambda}$ is a factor to scale the
+response of the detector.  In fact, the value determined for
+$c_{\lambda}$ depends on what color index is used in the calibration,
+so one might in fact write $c_{\lambda}$ as $c_{\lambda, color}$.
+However, for the sake of readability, this second subscript will be
+dropped.  The terms $\gamma_{color}$ and $a_{\lambda}$ give the
+variation of the sensitivity with the color of the star and the
+airmass.  For a detailed discussion of photometric calibrations, see
+Hardie (1962).  Equation (\ref{M_mag}) is only valid for photometric
+conditions.  Under non-photometric conditions, we need to add another
+term to account for clouds:
+\begin{equation}        
+M_{app} = c_{\lambda} + m + a_{\lambda}*\zeta + \gamma_{color}*(color) + clouds \\
+\label{M_cloud}
+\end{equation}
+Now, we can regroup the terms in (\ref{M_cloud}) as follows:
+\begin{equation}        
+m = [M_{app} - \gamma_{color}*(color)] - [c_{\lambda} + a_{\lambda}*\zeta + clouds]
+\label{M_sep}
+\end{equation}
+Of the two terms on the right side of equation (\ref{M_sep}), the variables
+in the first pair of brackets are dependent on the properties of the
+star, those in the second pair of brackets are dependent on the image parameters,
+except for $c_{\lambda}$ which is independent of both image and star.
+We can now define the ``relative magnitude'' and the ``calibration
+magnitude'' from (\ref{M_sep}) as follows:
+\begin{eqnarray}
+        m       & = & M_{rel} + M_{cal} \label{rel_phot1}\\
+        M_{rel} & = & [M_{app} - \gamma_{color}*(color) + \Delta] \label{color}\\
+        M_{cal} & = & - [c_{\lambda} + a_{\lambda}*\zeta + clouds + \Delta] \label{M_cal}
+\end{eqnarray}
+where $M_{rel}$ is the relative photometric magnitude of the star in
+the internal system and $M_{cal}$ is a correction factor to convert
+instrumental magnitudes to relative magnitudes.  The terms $\Delta$
+are included in eqs. (\ref{color}) and (\ref{M_cal}) to make explicit
+the fact that an arbitrary constant can be added to $M_{rel}$ and
+subtracted from $M_{cal}$, as this fact will be important (see below).
+The goal of relative photometry is to determine $M_{cal}$ for each
+image, then use this $M_{cal}$ to find $M_{rel}$ for all stars from
+eq. (\ref{rel_phot1}).  Once one has $M_{rel}$ for each star, one can
+then convert them to a standard system ($M_{app}$) using equation
+(\ref{color}).
+
+        An equation of the form (\ref{rel_phot1}) exists for each star
+detected on each image.  As described, the value $m$ depends on both
+the particular image and the particular star, the value of $M_{cal}$
+only depends on the image, and the value of $M_{rel}$ only depends on
+the particular star.  Let us label the images with the subscript $i$
+and the stars with the subscript $j$.  Equation (\ref{rel_phot1}) can
+then be written in the form:
+\begin{equation}
+m_{i,j} = M_{rel,j} + M_{cal,i} \label{rel_phot}
+\end{equation}
+Of course, $m_{i,j}$ only exists for certain combinations of images
+and stars: not all stars appear on all images.  In the entire system
+of equations, only one value of $\Delta$ (from eqs. \ref{color} and
+\ref{M_cal}) can be used, but, as noted above, the {\em value}\ of
+$\Delta$ is arbitrary.
+
+        In equation (\ref{rel_phot}), both $M_{rel,j}$ and $M_{cal,i}$
+are unknown quantities.  We can use a method of least squares to find
+these values for the entire set of data.  We try to minimize the chi
+square, defined as:
+\begin{equation}
+\chisq = \sum_{i,j}(m_{i,j} - M_{rel,j} - M_{cal,i})^2 / \sigma_{i,j}^2 \label{chisquare}
+\end{equation}
+where $\sigma_{i,j}$ is the error in the measurement $m_{i,j}$, and
+the index $i$ runs over all images, while the index $j$ runs over all
+stars which are multiply measured, \ie\ those stars which appear on more
+than one image.
+
+        We attempt to minimize \chisq\ analytically by finding the
+derivatives of \chisq\ with respect to $M_{rel,j}$ and $M_{cal,i}$
+and setting them equal to zero:
+\begin{eqnarray}
+\frac{\partial\chisq}{\partial M_{rel,j}} = \sum_{i}-2(m_{i,j} -
+M_{rel,j} - M_{cal,i}) / \sigma_{i,j}^2 = 0  \nonumber \\
+\frac{\partial\chisq}{\partial M_{cal,i}} = \sum_{j}-2(m_{i,j} -
+M_{rel,j} - M_{cal,i}) / \sigma_{i,j}^2 = 0
+\end{eqnarray}
+In these summations, the index $i$ runs over all images in which star
+$j$ appears, and the index $j$ runs over all stars which appear on
+image $i$ which are multiply measured.  Solving the equality leads to
+the following system of equations:
+\begin{eqnarray}
+R_{j}M_{rel,j} = \sum_{i}(m_{i,j} - M_{cal,i}) / \sigma_{i,j}^2 \label{system1} \\
+R_{i}M_{cal,i} = \sum_{j}(m_{i,j} - M_{rel,j}) / \sigma_{i,j}^2 \label{system2}
+\end{eqnarray}
+where 
+\begin{eqnarray}
+R_{i} = \sum_{j}\frac{1}{\sigma_{i,j}^2} \nonumber \\
+R_{j} = \sum_{i}\frac{1}{\sigma_{i,j}^2} 
+\end{eqnarray}
+Again, the index $i$ runs over all images in which star $j$ appears,
+and the index $j$ runs over all stars which appear on image $i$, with
+the restriction of multiple measurements.  The term $\Delta$ from
+eqs. (\ref{color}) and (\ref{M_cal}) acts as an arbitrary zero point
+between the relative and apparent photometric systems.
+
+        Equations (\ref{system1}) and (\ref{system2}) are a set of
+$N_{images} + N_{stars}$ linear equations with $N_{images} +
+N_{stars}$ unknowns.  The unknown parameters are the terms $M_{rel,j}$
+and $M_{cal,i}$.  Unfortunately, $N_{images} + N_{stars}$ is a number
+on the order of 5000 - 10000, thus an analytical solution is
+impractical and an iteration method must be used.  A simple iteration
+method is as follows:
+
+%\SingleSpace
+\begin{enumerate}
+\setlength{\parskip}{-0.05in}
+\item Guess at values for each $M_{cal,i}$.
+\item Use equation (\ref{system1}) to find values for $M_{rel,j}$.  
+\item Substitute these $M_{rel,j}$ values into (\ref{system2}) to get a new
+set of $M_{cal,i}$ values.  
+\item Return to step 1.
+\end{enumerate}
+%\DoubleSpace
+This process is repeated until some criterion is reached, such as the
+\chisq\ no longer changes or reaches a desired minimum.  Although a
+minimum can be found, error propagation causes severe problems
+since images are only connected to neighboring images.  The end point
+images are connected by \approx 50 intermediate images, and errors
+propagating over such a long distance can cause severe errors.  Also,
+$M_{rel,j}$ and $M_{cal,i}$ are not uniquely defined, because of the
+term $\Delta$.  Therefore, during the iteration process, the values of
+$M_{rel,j}$ and $M_{cal,i}$ tend to wander and do not tend to converge
+($M_{cal,i}$ may be incorrect by typically 0.5 mag).  These problems
+can be overcome if some of the values $M_{cal,i}$ or $M_{rel,j}$ are
+known.  In this situation, the known values can be held fixed,
+providing a reference point for the remaining values.  If enough
+values are known across a large enough portion of the data, no image
+is separated from the fixed values by many images, and errors will not
+build up.
+
+        Although many values $M_{rel,j}$ were known from other CCD
+photometry studies, we chose not to use them.  The reason for this
+decision was threefold: 1) By not using them, we are able to check the
+effectiveness of our method.  2) We are able to check the accuracy of
+the reference data.  3) The known $M_{rel,j}$ values were in a very
+small number of highly concentrated regions (the locations of the CCD
+photometry studies).  This was particularly a problem in \R\ and \I,
+where we only had one or two reference fields.  We therefore chose to
+fix certain values of $M_{cal,i}$
+
+        Images taken under photometric conditions provide a set of
+known $M_{cal,i}$ which can be held fixed.  This is true since, if the
+image is taken under photometric conditions, the term $clouds$ in eq.
+(\ref{M_cal}) is by definition 0.  The other terms in this equation
+are either known ($a_{\lambda}*\zeta$) or are constant for all images
+($c_{\lambda}$) and can thus be assigned to a set value, which can
+later be removed with the zero point calibration.  We used the values
+of $a_{\lambda}$ reported by Pilachowski \etal\ (1991): $a_B$, $a_V$,
+$a_R$, $a_I$ = (0.24, 0.15, 0.11, 0.07).  We used the long-term
+average coefficients, not those measured in October 1991, since the
+observations were performed before the eruption of Mount Pinatubo.
+The task that remains is to determine which images were photometric.
+
+        Since the conditions during observations varied greatly from
+night to night, we decided to determine which images were photometric
+from the images themselves.  To do this, we used a variation on the
+iteration method described above.  Before starting the iterations, we
+assume a value for $c_{\lambda}$.  We can choose this arbitrarily, and
+correct for our choice in the correction to an absolute photometric
+system.  Now, we perform the following iteration:
+
+%\SingleSpace
+\begin{enumerate}
+\setlength{\parskip}{-0.05in}
+\item Assume that all images are photometric and determine $M_{cal,i}$.
+\item Find the corresponding values of $M_{rel,j}$ from equation (\ref{system1}).
+\item Use these $M_{rel,j}$ values to find new values for $M_{cal,i}$.
+\item Find the values of $clouds$ for each image from these $M_{cal,i}$
+\item Assume that any image with a value $clouds$ more than 1 standard
+deviation from the mean value (over images) has a substantial cloud
+layer.  
+\item Return to step 1, but use the iterated $M_{cal,i}$ values for those
+images with substantial clouds.
+\end{enumerate}
+%\DoubleSpace
+This process is continued until \chisq (eq. \ref{chisquare}) does not
+change substantially.  Figure (\ref{fig:Mrel}) shows a histogram of
+the occurrences of different $clouds$ values.  There are a few
+important points to make about this figure.  First, there is a
+concentrated peak at 0.  All of these images have essentially zero
+cloud level.  As one would expect, there are a large number of images
+to the right of this peak.  These are the images which were taken
+through some amount of clouds.  Since the exposures were stopped if
+the clouds got too thick, it is not surprising that these images taper
+off at higher cloud levels.  The most curious and telling part of the
+plot is the area below the 0-cloud peak.  There are many images on the
+``negative cloud'' side of the peak.  Because ``negative clouds'' are
+unphysical, there must be a simple explanation for this.  In fact, 112
+of the 122 images more than 0.02 mag below the peak had exposure times
+of less than 20 seconds.  Since the shutter at MDM is accurate to only
+1 - 2 seconds, it is likely that these images had an exposure time
+which was longer than reported, and therefore have artificially
+enhanced values of $M_{cal,i}$.
+
+        We calculate the error on the relative calibration of each
+image by finding the scatter of the $M_{cal,i}$ values for each image
+and dividing by $\sqrt{N stars}$.  A histogram of this error for all
+images is shown in Figure (\ref{fig:dMrel}).  We also show the
+variation of the formal photometric error reported by \dophot\ with
+magnitude for each filter in Figure (\ref{fig:dm}).  Now that we have
+the values $M_{cal,i}$, we can calculate the values $M_{rel,j}$ for
+every star on every image.
+
+The Pipeline implementation of relative photometry includes the
+possibility for up to third order polynomial terms across the field.
+This is needed to account for the different flat fields, particularly
+between images taken with and without the red-block filter.  
+
+\begin{figure}
+\psfig{file=flags.eps,width=9cm}
+\caption{\label{flags} \small
+  Pictoral representation of the Average.code flags.  The lower byte
+  value is used for specific definitions of object types.  }
+\end{figure}
+
+To run 'nrphot' by hand, the syntax is: {\tt nrphot (file.cpt)}.
+There are several optional flags, which are listed if you type
+nrphot by itself.
+
+\subsection{Data Flags}
+
+The identifications made by the above routines are noted as a set of
+flags in the Average.code structure entry.  Some of these flags are
+mutually exclusive, so a certain bit may have more than one meaning,
+depending on the value of other bits.  Figure~\ref{flags} shows a
+pictoral representation of the meaning of the different bit fields.
+The uppermost bit (bit 15) determines if the object may be considered
+a ``fixed'' star or something which has no fixed location.  If it is
+not fixed, and this bit is 1, then there are several options.  The
+second uppermost bit (bit 14) will be set if this object is one of the
+three types of bad data flagged by {\tt markstar}.  The lower byte of
+code is used to define the type of bad data: ghost, trail, or bleed.
+Thus, any datapoints determined to be a satelite trail will have a
+code value of 0xc002 (49154 decimal).  If an object is a moving
+object, but not a bad datapoint, it may be identified as a ``rock''
+(an asteroid) by the program {\tt markrock}.  In this case, the flag
+for an asteroid is turned on (bit 13).  Thus anything identified as a
+rock by {\tt markrock} will have a code value of 0xa000.  The lower
+byte of code is not yet defined for asteroids, but could be reserved
+for distinguishing different classes of asteroids (ie, on the basis of
+orbital speeds, and so forth).  Objects which are fixed may have a
+variety of possible flags.  First, an object may be identified with a
+USNO star (bit 14).  It may be found to exhibit variability (bit 13),
+implying that the relative photometry routine should ignore it.  If
+may be a transient object (bit 12).  Note that, while an object
+associated with a USNO object may not be transient, an object which is
+variable may also not be associated with a USNO object.  Thus, we need
+seperate bits for variable vs transient.  Different types of variable
+objects may be represented with different values of the lower byte.
+For example, a Cepheid may have a specific lower byte code of 0x01,
+which an RR Lyrae may have a lower byte code of 0x02.  Thus a Cepheid
+associated with the USNO catalog would have a total code value of
+0x6001. Finally, any of these types of non-moving objects may have
+some bad individual measurements (bit 12) or may be found to have a
+significant proper motion (bit 11).  It should be noted that the above
+guidelines for variable stars are suggestions only, and to date (July
+7, 1999), no programs currently assign or define these variability flags. 
+
+\section{Holding it Together}
+
+The preceeding sections describe the step-by-step analysis and
+incorporation of the data from individual images.  However, the power
+of the Pipeline comes in applying it to large numbers of images.  At
+LONEOS, we are using four Pentinum II computers to analyse the roughly 200
+images that are observed each night.  
+
+It is best to think of the analysis of images occurring on groups of
+images from a given night.  Of the set of processes discussed above,
+those in section \ref{parallel} are performed on individual images
+essentially independently of the other images.  The rest of the steps,
+both the data incorporation ({\tt addstar} and the database cleaning
+routines occur on the entire batch, one image at a time.  Thus, the
+former routines can easily be run in parallel, but the latter routines
+are run in serial to avoid having two programs writing to same part of
+the database at the same time.  In this model, we would process all of
+the images from a night through the parallel stage of the analysis and
+then run the serial stage only after the entire night is finished in
+the parallel stage.  We thus have a natural division of the pipeline
+into two stages.  
+
+The implementation of the two stages involves several programs, some
+of which are specific to the LONEOS situation, and others which are
+more general.  There are two layers of programs which enable batch
+processing of many images and repeated batch processing over many
+nights.  At the highest level, are Perl scripts which choose what data
+needs to be analysed and when.  These scripts tend to be more
+dependent on the details of the LONEOS implementation. At the next
+level down, these scripts call two main C programs which run the
+images of a given night through the pipeline.  These C programs are
+more general and do not depend on the details of the LONEOS
+implementation.  A defined set of files holds a log of the nights and
+images that have been processed, both for the information of people
+who might want to monitor the progress, but also used by the programs
+to guide decisions about what to analyse next.
+
+\subsection{data and file organization}
+
+There are a set of standard locations for all of the files used and
+created by the Pipeline.  These locations have both abstract names
+used by the pipeline programs and specific definitions used in the
+LONEOS implementation.  In general, the definitions of the files are
+given in the configuration file, 'config.txt' (currently located in a
+hard-wired location, but this may soon change to the mechanism used by
+the lastest version of spicam (3.0)).  The C programs all refer to the
+config file for the definitions, but the Perl scripts currently have
+the definitions hardwired as variable names defined in the beginning
+of each file.  This should be changed to a mechanism that looks in
+the config file, perhaps implemented by a standard Perl function.
+
+Here is a list of the main directories and their meanings:
+
+logdir:         /metis/d11/logs - all log files and the files used by
+the Perl scripts to control their actions (except locks) go here.
+
+workspace:      /irene/d11/workspace - the intermediate analysis
+stages go here, and the tar, gzipped directory files stay here.
+
+dumpspace:      /pallad/d1      - used by slurp to store images
+downloaded from the tape archive.
+
+database:       /hebe/d27/database      - the result database, as also
+the locks and the Rocks.dat file.
+
+references:     /metis/d11/references   - a variety of reference data,
+including the HST GSC, the USNO catalog, the config files, and so
+forth.
+
+source:         /metis/d11/src/ohana    - all of the programs source, 
+binaries, and scripts relevant to running the Pipeline are contained
+in these directories.  
+
+\begin{verbatim}
+database: /hebe/d27/database (also in /metis/d27/database as linked directories).
+ lock files also go in this directory
+workspace: /irene/d11/workspace - temporary storage of result files
+dumpspace: /pallas/d1 - disk to store data dumped from the archive by slurp.
+scriptdir: /metis/d11/logs - all log files
+\end{verbatim}
+
+\subsection{lastnight}
+
+The first step in the process is the Perl script {\tt lastnight},
+which is highly dependent on the specifics of the LONEOS setup.  It
+looks at the disks used by the observers to write the images from a
+given night.  Images are written to a directory called
+/pallas/d[1-6]/YYMMDD where 'YYMMDD' is an abbreviation for the current
+night's date (ie, 990223).  Many files use this naming scheme, so we
+will refer to such a name as 'YYMMDD'.  All of the images are stored
+in this directory with names yyyyMMDDxxxxb.fits where yyyy is the full
+year, MM is month, DD is date, and xxxx is a sequence number.  The 'b'
+refers to the 'b' CCD, and the .fits extension is required.  The
+script {\tt lastnight} is called by a cron job scheduled for 7am on
+{\tt hebe.lowell.edu} (note that the clock on hebe is set to PST, not
+local MST - fix this some day!).  The script searches the disks
+{\tt /pallas/d[1-6]} for a directory of the right form (basically any
+directory beginning with a number).  By default, the program demands
+that the directory have the name derived from today's date, but with
+the flag -any, it will accept any directory.  It then checks to see if
+this directory has been already been analysed (ie, it is included in
+the 'processed.log' file).  If not, it lists all files in that
+directory with the right ending (*b.fits) and places these names in a
+file, {\tt /metis/d11/logs/YYMMDD.inlist}.  It also writes the name of the
+directory (along with some other data) in a file 'extracted.log'.
+When it has successfully ended and identified some data, it calls the
+next script, {\tt stage1} and then quits.
+
+\subsection{slurp}
+
+An alternative to {\tt lastnight} is the Perl script {\tt slurp},
+which looks for data on the tape archive.  This script looks at the
+tape.log file and compares it to the file 'processed.log'.  It tries
+to identify nights listed in the file 'tape.log' which have not been
+included in the 'processed.log'.  If it finds any, it tries to
+download as much of the data as will fit on the disk {\tt /pallas/d1}.
+This script is called from within the {\tt stage1} script and is
+invoked if {\tt stage1} is run without any data already in
+the file 'extracted.log'.
+
+A related pair program is the load/unload C programs which are needed
+for loading and unloading the tape jukebox.  The first, load N, takes
+tape number N and loads it into the tape drive.  The second takes the
+tape a returns it to its slot.  It is important to check on the other
+users of the tape drive to be sure it is not occupied.  This is done
+with the llock, lunlock and llockstat programs.  llockstat lists the
+devices which have been allocated.  llock lets you lock a particular
+service, etc.
+
+\subsection{stage1}
+
+The Perl script {\tt stage1} takes the image list produced by {\tt
+lastnight} or {\tt slurp} (YYMMDD.inlist) and passes the list to the
+controller program {\tt control1} which performs the parallel
+analysis.  The script has a few features to decide which files have
+been analysed through the pipeline and to what extent.  If {\tt
+stage1} is called, it should be able to figure out 1) if there is data
+ready to be analysed (ie, there is a new date reference in the file
+'extracted.log'), 2) if the data has been partially analysed (ie, the
+date reference in 'extracted.log' has not been put in 'processed.log',
+but there are entries in the file YYMMDD.outlist), and 3) which data
+needs to be run through which part of the pipeline (ie, it decides
+which of the images in YYMMDD.outlist have succeeded at which stages
+of the analysis, and starts them at the appropriate spot).  These
+features are most useful if the analysis crashes in the middle or
+needs to be restarted.  The {\tt stage1} script is meant to be run
+without any arguments and it will figure out what needs to be done.
+The one possible argument is the -once flag.  This flag tells {\tt
+stage1} to analyse the data it finds and quit when it finishes, and
+not call another instance of itself.  Otherwise, when it is done, it
+will restart itself, and since there is no new data in
+'extracted.log', it will start a slurp process to get data from the
+archive.  If there is no data in the archive either, this process will
+sleep for an hour and try again.  After a few attempts (2 days?), {\tt
+stage1} will give up and quit without restarting itself.  The script
+writes a log file called \$scriptdir/stage1.NN.log where NN is a
+number in sequence between all runs of {\tt stage1}.  A file
+stage1.count keeps track of which number NN we are on.  When {\tt
+stage1} has finished the analysis of a night's data, it writes the
+date in 'processed.log' to let other processes know what has been
+analysed.  The bulk of {\tt stage1} is not dependent on details of the
+LONEOS setup, but it does expect the files to have the naming scheme
+described above.
+
+\subsection{stage2}
+The serial stage processes are run by the Perl scrip {\tt stage2}.
+Normally, this script is always running.  It waits for the name of a
+night to be added to the 'processed.log file, presumably by {\tt
+stage1}.  Once it finds a new name in this file, it looks for the
+appropriate images in the file 'YYMMDD.addlist'.  It then submits the
+list to the second controlling program {\tt control2}, which runs the
+images through {\tt addstar}.  Images which are successfully run
+through addstar have their *bf.fits (flattened image) and *.obj
+(dophot output) files deleted.  When the entire night is done, the
+rest of the *.obj and *bf.fits images are deleted by {\tt stage2}, the
+directory with the *.cmp and *.log files is tared and gzipped into the
+file YYMMNN.tgz.  The directory is then deleted.  Finally, {\tt
+stage2} calls the scripts {\tt cleaning.pl} and {\tt run.phot} which
+runs the cleanup programs ({\tt markstar - nrphot}) on the database.
+After a successful run, {\tt stage2} will automatically respawn a new
+copy that waits until more data arrives.
+
+\subsection{control1 / control2}
+These two C-programs perform the tasks of farming the images out to
+the different machines for analysis.  The two programs are essentially
+identical, but {\tt control1} is allowed to use all machines and {\tt
+control2} can only use one (hebe.lowell.edu, which has the database
+local to it).  These controller programs start off by executing remote
+shells (using ssh) on a set of machines.  These shells are then used
+to execute the remote commands and are kept open the entire time the
+process runs.  
+
+Both control programs maintain a list of images in a set of queues.  A
+set of rules defines the order in which each image travels through the
+queues.  Each queue is associated with a single process (ie, flatten,
+astrometry, etc).  Images which successfully run through a queue are
+sent on to the defined 'next' queue, while images which fail are
+instead sent to the defined 'fail' queue.  In most cases, the 'fail'
+queue is a process which marks the image as bad and write the name in
+a file, and then puts the image in the 'done' queue.  However, it is
+possible for the 'fail' queue to be another attempt, such as in the
+case of astrometry.  In this example, images which fail the default
+astrometry are run through a second queue which tries again with the
+-loneos flag set (this flag makes the assumption that the header
+information about the LONEOS region number for the image is correct,
+not the RA/DEC information).
+
+The connections on the remote machines are handled as child processes
+with communication through the stdin and stdout/stderr pipes.
+Messages can be sent back and forth though these pipes, and messages
+which are received from a process associated with an image are written
+to a log file with the name imagename.log (where imagename is the root
+name of the image).  The only required messages from the program are
+messages saying ``SUCCESS'' or ``ERROR''.  The programs therefore all
+end with either of these words.  The programs are started within the
+controller as a command to the shell followed by and echo ``PROCESS
+DONE''.  If the controller sees the message ``PROCESS DONE'' without
+either ``SUCCESS'' or ``FAILURE'' (after waiting an appropriately long
+time), it assumes the program crashed.  Currently, no timeout is
+available for the programs.  The flat-field process is run in mana and
+is a slightly special case.  We avoid re-loading the same flat many
+times by starting a mana process once and running the flatten
+procedures as mana function calls.  The images which are analysed
+(with an indicator of success or failure at each stage) are written to
+a file named 'YYMMDD.addlist' by the {\tt control1} and 'YYMMDD.outlist'
+by {\tt control2}.
+
+\subsection{Lock files}
+
+A variety of locking mechanisms are used to avoid overwriting the
+database or running more than one version of the controlling
+programs.  There is a perl program, {\tt locks}, which allows the user to
+set or check the state of the different locks. All programs which
+write to the database check for the existence of a lock on the
+database and refuse to run if it exists.  The control1/2 programs and the
+stage1/2 programs check for their own lock files and only run if they
+don't exist.  Finally, the use can set the lock files 'kill' or 'halt'
+for either stage1 or stage2.  The first tells the process to stop
+immediately, taking care of bookkeeping first.  The second tells the
+process to finish the current set of images but not start a new
+version.  
+
+\subsection{Other Perl Scripts} 
+
+there are several other scripts which are either used to do some
+simple function or are used by the maintainer to check up or work on
+the pipeline.  The two programs 'checkoutlist' and 'checkaddlist' take
+a list of YYMMDD.outlist or YYMMDD.addlist files and list the number
+of images in the file and the number of successes at each analysis
+stage.  There programs 'cleaning.pl' and 'run.phot' deal with the
+cleanup programs and the running of nrphot.  the program
+'backup.stuff' takes the first 4 characters of the year/month file
+names and backs up to the jukebox tape all tar files for the given
+month.
+
+\section{Operational Issues and Subtleties}
+
+The current LONEOS system runs principally on four Intel machines
+under linux.  The existing system has been generally quite stable over
+the past year of operation, but there are some subtleties.  One
+significant issue is the RAID disk attached to hebe.lowell.edu.  This
+device is a set of three 9 GB disks merged into one 27GB raid disk.
+The database is stored mostly on this disk and the RAID disk mounted
+on metis.  It is easy to split the database across multiple file
+systems by using links.  Within the database directory, there are
+directories for each of 24 Declination bands (7.5 degrees tall).  The
+easiest way of splitting up the data is to move some of these
+subdirectories to another device and creating links to them at the
+appropriate place.  This is currently done by having a directory
+/metis/d27/database with the remote directories.  
+
+The current problem with the /hebe/d27 RAID results from the poor way
+it was setup.  It was set up without much knowledge of the (at the
+time) rather rudimentary linux RAID system.  As a result, the
+implementation is such that the disk will not automatically be
+configured and mounted at boot time.  There is a script /root/mdstart
+which lists the step to setup the RAID correctly.  It would be helpful
+if the rc.local or rc.sysinit files were fixed to start, fsck and
+mount the RAID automatically at boot.  The setup on metis is
+substantially more mature and is correctly implemented.  It comes up
+when the machine boots.
+
+\section{Visualization -- Status}
+
+Visualization of the data in the photometry database can best be
+performed with the program {\tt status}.  This is a command-line
+driven program with a math and macro language which makes it easy to
+perform complex tasks.  
+
+First, a few notes about the user interface.  The interface has an
+interaction similar to {\tt tcsh}.  The arrows allow editing of
+previous commands.  You can also use emacs-like commands such as
+cntl-a to reach the beginning of the line and cntl-e to reach the end.
+There is command and file completion: if you type part of a command
+(as the first thing on a line) and then type tab, it will fill in as
+much as possible, until the word is not unique.  Typing tab twice at
+that point will list the possible endings.  For any but the first word
+on a line, the same thing will happen for the files in the current
+directory.  It is also possible to type just a fraction of a command,
+as long as it is unique.  An ambiguous command will list the possible
+alternatives.  For example:
+\begin{verbatim}
+status: c
+ambiguous command: c ( catalog cgrid clear create cursor )
+\end{verbatim}
+
+The shell is essentially an interpretive programming language.
+Variables are set as follows:
+\begin{verbatim}
+status: $fred = 10
+\end{verbatim}
+Any expression within curly brackets \{\} is assumed
+to be an arithmetical expression and is evaluated before the line is
+executed.  For example:
+\begin{verbatim}
+echo {$fred*dcos(45)}
+\end{verbatim}
+would give the response 7.07107.  There are math functions cos, sin,
+and tan, which operate on radian expressions, and also dcos, dsin,
+dtan, which operate on degree expressions.  There are also the
+equivalent inverse functions: eg., asin and dasin return radians and
+degrees, respectively.  The help section on Math defines all of the
+available math functions.  
+
+\subsection{Miscellaneous Commands}
+\begin{verbatim}
+!                         -- system call
+?                         -- list commands 
+??                        -- list variables 
+echo                      -- type this line 
+exec                      -- system call
+exit                      -- exit program 
+help                      -- get help on a function 
+output                    -- redirect output to file
+quit                      -- exit program 
+scan                      -- scan line from keyboard or file to variable 
+wait                      -- wait until return is typed
+which                     -- show command 
+\end{verbatim}
+Most of these are self-explanatory.  The command ?? prints the system
+variables.  The {\tt help} command will provide help on a single
+command or, without any arguments, will list all available help
+files (this includes general help not associated with a specific
+command).  
+
+\subsection{Shell Programing}
+\begin{verbatim}
+break                     -- escape from function 
+for                       -- loops 
+if                        -- logical cases 
+input                     -- read command lines from a file 
+macro                     -- deal with the macros 
+\end{verbatim}
+
+There are several options for programming in {\tt status}.  First, a
+file which contains a series of commands can be executed with {\tt
+  input (filename)}.  It is also possible to define macros which will
+behave much like regular commands.  A macro is defined by typing {\tt
+  macro name} or {\tt macro create name} followed by the commands.
+Arguments to the macro are assigned to the variables \$1 .. \$N and
+the number of arguments is given by \$0.  Macros may be defined in
+{\tt input} files, and in fact when {\tt status} is started, it loads
+the file {\tt \~/.statusrc} which may contain default macros.  Simple
+loops and if statements can be performed, and are quite useful for
+complex macros.  
+
+``If'' statements are similar in syntax to C if statements, but only
+the following logical operators are available: $>$, $<$, $=$, !, $|$,
+and \&.  Notice that (currently) there is no $>=$ or $<=$ symbol.  The
+operator ! means ``not equal to'', but cannot be used to negate a
+logical value.  The operators $|$ and \& have the meaning of ``or'' and
+``and'' respectively.  Math expresions in the if statement must be
+contained in curly braces, as elsewhere.  Variables with string values
+may use the logical $=$ operator to test if two strings are the same.
+``For'' loops are quite simplistic.  The form is:
+\begin{verbatim}
+for var first last delta
+ (commands)
+end
+\end{verbatim}
+The value of {\tt \$var} will start at the value {\tt first} and increment by
+{\tt delta} after each loop.  The loop will stop after {\tt \$var} is greater
+than {\tt stop}.  The value {\tt delta} is optional, with 1 assumed.
+The value of {\tt \$var} may be changed during the loop, and if set
+beyong the value of {\tt last} will end the loop early.  
+
+\subsection{Vector Plotting}
+
+\begin{verbatim}
+box                       -- draw a box on the plot
+clear                     -- erase plot
+create                    -- create a new vector
+cursor                    -- get coords from cursor
+grid                      -- plot cartesian grid
+hist                      -- create histogram from a vector
+labels                    -- define labels for plot
+limits                    -- define plot limits
+plot                      -- plot a pair of vectors
+print                     -- write vectors to file
+ps                        -- define labels for plot
+set                       -- vector math
+style                     -- set the style for graph plots
+vectors                   -- list vectors
+zplot                     -- plot scaled points 
+\end{verbatim}
+
+In addition to scalar variables, {\tt status} can manipulate and
+display 1-D vector variables.  Many of the commands which extract data
+from the photometry database place the data in vectors as well as
+plotting them.  A vector can also be created based on a number
+sequence with the command {\tt create name Nelements start delta}.
+The resulting vector has $Nelements$ entries, starting at a value of
+$start$ and running until $start + delta*Nelements$.  If $delta$ is
+0.0, all elements will have the value of $start$.  A histogram of a vector
+may be made with the command {\tt hist}, which creates a new vector
+containing the histogram of the first vector.  The data range and bin
+size of the histogram are defined in same way as with create.  This
+makes it easy to create the index vector that goes with a histogram
+vector:  
+\begin{verbatim}
+hist y Ny 1 100 0.1
+create dx 1 100 0.1
+\end{verbatim}
+The above will create a histogram of y in Ny and the index in dx.
+Plotting this with {\tt plot dx Ny} will show the histogram.
+
+Vector math is performed with a command of the form {\tt set new =
+  (expression)}.  The expression is some math function employing
+vectors and scalars.  A complete listing of the math operators
+available in {\tt set} can be found in the help for {\tt set}.
+
+Once vectors are defined, they may be plotted.  A pair of vectors can
+be plotted against each other if they have the same number of entries.
+The plotting is performed on the graphics window, Kapa.  There are
+actually several graphics windows available to {\tt status}, any of
+which may be used to plot at any time.  Some of the more complex
+operations default to either graphics window 0 or 1, depending on the
+context.  Except for those functions with a pre-defined window, all
+plotting functions apply to the current graphics window unless an
+option {\tt -n N} is given to specify a different window.  The
+plotting style is determined by the command {\tt style} which can set
+the line width, the line type (solid, dashed, dotted, etc), the point
+type (box, cross, etc), the point size, the color, and whether a pair
+of vectors is plotted as a sequence of points, a set of connected
+lines, or a histogram.  Some functions which make plots use their own
+styles, as discussed below.  The function {\tt limits} lets the user
+set the range of the plot axes, or check the current setting.  The
+command {\tt plot} will plot a pair of vectors on the current graphics
+window using the current plotting style for that window.  The command
+{\tt zplot} will plot a pair of vectors with the point size scaled by
+a third vector, with maximum and minimum point sizes representing
+specified values.  The {\tt cursor} command goes the other way: this
+command puts the Kapa window in cursor mode and waits for input from
+Kapa.  The user can then type any alphanumeric key on the graphics
+windows and will be told both the pointer location (in the graphics
+coordinates) and will have the coordinates stored in {\tt status}
+variables.  For example, by typing ``1'' in the sky display window,
+the RA and DEC of the pointer are stored in the variables {\tt \$R1}
+and {\tt \$D1}.  This command can be used to let the user define
+locations or regions of interest on the Kapa window. (Future addition:
+{\tt button}, which does the same with the mouse buttons).  
+
+\subsection{Database Functions}
+
+\begin{verbatim}
+gcat                        -- get catalog at location
+gimages                     -- get images at location
+gstar                       -- get star statistics
+extract                     -- extract average vectors from catalogs
+mextract                    -- extract measurement vectors from catalogs
+imstats                    -- plot image statistics
+imextract                   -- extract image vectors from database
+lcat                        -- list catalogs in display region
+cmatch                      -- match two catalogs
+\end{verbatim}
+
+There are a variety of other commands which directly refer to the
+photometry database.  Some of these functions extract data of various
+types from the database, others perform more complex plotting
+operations.  The commands listed above are those which simply extract
+data from the database.  The first three list information relevant to
+a specific RA, DEC location on the sky: {\tt gcat (RA) (DEC)} lists
+the catalog at the specified location and places the name in the
+variable {\tt \$CATNAME}, {\tt gimages (RA) (DEC)} lists all images
+which overlap the specified location, {\tt gstars (RA) (DEC) (RADIUS)}
+lists data about the stars within a specified radius of the specified
+location (all numbers above are given in decimal degrees).  Similarly,
+{\tt lcat} lists the catalogs in the region.  Imstats lists statistics
+about each image
+
+The next three commands extract a specific piece of information from
+the photometry database and places it in a vector.  First, {\tt
+  extract} will extract average values for each star and place it in a
+vector.  Next, {\tt mextract} will extract measurement values for each
+star and place it in a vector: as a result a single star may have
+multiple entries in the measurement vectors.  Finally, {\tt imextract}
+will extract image statistics into vectors (not yet implemented).
+
+
+\begin{verbatim}
+catalog                    -- plot catalog stars
+cgrid                      -- plot sky coordinate grid
+cplot                      -- plot vectors in sky coordinates
+czplot                     -- plot scaled vectors in sky coordinates
+images                     -- plot image boxes
+imdense                    -- image density plot
+lcurve                     -- plot lightcurve for a star
+pcat                       -- plot catalog boundaries
+region                     -- define sky region for plot
+resid                      -- plot residuals
+simage                     -- plot stars in an image
+\end{verbatim}
+
+There are two types of database plotting functions: those that display
+or refer to the spatial charateristics of the data and those that
+refer to other types of charatersitics, such as the time domain.  The
+graphics window 0 is reserved for all plots of objects on the sky.
+The command {\tt region} defines the current sky coordinates for plots
+in graphic window 0.  The command {\tt pcat} plots the outline of all
+photometry database files which are within the currently defined
+region (and by default, only those with data).  {\tt images} plots the
+outline of the images in the image database, while {\tt imdense} shows
+the number of images at a location by randomly spacing dots within the
+boundary of the images.  The command {\tt cgrid}
+draws a grid in celestial coordinates on the for the current region.
+
+The most complex, but also one of the most useful command is {\tt
+  catalog}, which plots the positions of stars in the photometry
+database (and others) on the sky.  There are many options to this
+command.  One set allows the user to plot stars from the photometry
+database (the default), from the HST GSC, or from an ASCII text file
+with RA, DEC, and Mag in specified columns.  If the ASCII file has a
+fixed number of bytes per line, the data can be more quickly loaded.
+The size of the points may be scaled by the star magnitude, by the
+number of observations of the star, or by the number of missing
+datapoints for the star.  In addition, points may be plotted only if
+they land in specified magnitude ranges, or with specified numbers of
+measurements, or missed measurements.  Also, objects may be plotted
+only if they have a specified Average.code, so that only asteroids or
+only perfect stars may be plotted.  The plotted vectors may be saved,
+if desired, and the source catalog epoch may be specified as different
+from J2000 (only valid for ASCII data).
+
+Several other commands relate to non-spatial charateristics of images
+and stars.  {\tt lcurve} will plot a light curve for all stars within
+some radius of a point.  {\tt resid} plots the photometry residuals
+for a particular region file.  
+
+\section{Some Examples}
+
+\begin{figure}
+\psfig{file=allsky.ps,width=16cm}
+\caption{\label{allsky} \small
+  Map of the entire sky, and images added to database.  }
+\end{figure}
+
+Fig.~\ref{allsky} shows a map of the entire sky, and the location
+of the images currently in the database.  This picture was made with
+the following commands: (output is not shown) \\
+\begin{verbatim}
+status: region 0 0 90 gls
+status: cgrid
+status: style -lw 2 -c red 
+status: images
+status: ps
+\end{verbatim}
+In this example, on the graphics window, the image boxes are shown in
+red.  The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth.  
+
+\begin{figure}
+\psfig{file=catalog.ps,width=9cm}
+\caption{\label{catalog} \small
+  Comparison between HST GSC and photometry database astrometry.  }
+\end{figure}
+
+Fig.~\ref{catalog} shows an example comparison of the photometry
+database star positions and the HST Guide Star Catalog star
+positions.  The crosses are from the photometry database while the
+boxes are from the HST GSC.  The size of both points is a function of
+brightness.  This plot was made with the following commands (starting
+from the previous image):
+\begin{verbatim}
+status: cursor  (typed 1 on region of interest)
+1 137.097858 22.698305
+q 137.097858 22.698305
+status: region $R1 $D1 0.5 TAN
+status: cgrid
+status: box
+status: style -pt 0 
+status: gcat $R1 $D1 
+  0 n2230/1951.cpt *
+status: catalog n2230/1951.cpt -m 12 18
+status: style -pt 2
+status: catalog n2230/1951.cpt -m 12 18 -g
+\end{verbatim}
+
+\begin{figure}
+\psfig{file=lightcurve.ps,width=9cm}
+\caption{\label{lightcurve} \small
+  Lightcurve for several image stars.  }
+\end{figure}
+
+Fig.~\ref{lightcurve} shows an example lightcurve from three stars in
+the above field.  The x axis shows the time in seconds, which the y
+axis shows the star magnitudes.  This plot was made with the following
+commands (starting from the previous image): {\small
+\begin{verbatim}
+status: cursor
+1 137.062562 22.791860
+q 137.062562 22.791860
+status: lcurve $R1 $D1 0.01 -l
+status: box
+status: gstar $R1 $D1 0.01
+read 11318 stars from catalog file /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt (22462 total measurements)
+508 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.068619 22.785400 18.438000  1 (2)  1.000000 1.000000
+498 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.062820 22.792747 12.882000  3 (0)  1.000000 1.000000
+501 /host/pikake/d1/gene/loneos/allstars/n2230/1951.cpt 137.064499 22.800817 15.480000  3 (0)  1.000000 1.000000
+\end{verbatim}}
+The last lines show the database information on the three stars.  
+
+\section{Software Distribution}
+
+All of the code, binaries, and scripts for the Pipeline are stored in
+the ohana tree at /metis/d11/src/ohana.  The ohana directory contains
+subdirs of bin, lib, include, src, config, and doc.  There is a
+Makefile at the top level which calls lower-level Makefiles to build
+the programs desired.  One of the important concepts in the ohana
+distribution is the design of the directory layout to enable binary
+support for multiple architectures.  The Makefiles use the environment
+variable ARCH to determine the appropriate architecture.  The binary
+and library files are stored in subdirectories of the form bin/\$ARCH
+and lib/\$ARCH.  It is therefore necessary that users of the ohana
+system architecture define the ARCH variable correctly.  It is also
+necessary to include in the user's PATH the directory
+/metis/d11/ohana/bin/\$ARCH.  At LONEOS,
+with the solaris sparc station and the linux machines both in use,
+ARCH can take on the values of 'linux' and 'sol'.  The values 'sun4',
+'irix', and 'hp' have also been used at various sites.  A simple bit
+of csh script in the users .cshrc or equivalent can make this
+assignment transparently:
+
+\begin{verbatim}
+set sys=`uname -s` 
+switch ($sys)
+ case IRIX64:
+   setenv ARCH irix;
+   breaksw;
+ case SunOS:
+   set ver=`uname -r | awk '{print substr($1,1,1)}'`;
+   if ($ver == 5) then
+     setenv ARCH sol
+   else 
+     setenv ARCH sun4
+   endif
+   breaksw;
+ case Linux:
+   setenv ARCH linux;
+   breaksw;
+ default:
+   echo "unknown architecture";
+   setenv ARCH unknown;
+   breaksw;
+endsw
+\end{verbatim}
+
+The Makefiles use the ARCH variable not only for destination
+directories, but also for destination binary names.  All files of the
+form fred.c are compiled to fred.ARCH.o (instead of fred.o), 
+uninstalled libraries get the names libfred.ARCH.a (instead of
+libfred.a), while uninstalled programs are called fred.ARCH.  
+
+The perl scripts are kept in ohana/src/perl, with links to the
+appropriate bin directories.  All programs are stored in directories
+of the form ohana/src/program.  The exceptions are gastro (stored in
+ohana/src/astro), and control1/2 (stored in ohana/src/astro).  Also, a
+variety of simple, user-level functions are available in
+ohana/src/misc. 
+
+Most of the ohana package of programs use several common libraries.
+These include the readline library (except for dophot, this is the only
+part of ohana that is not written by Eugene Magnier), which is used
+for command-line interface systems.  Other important libraries are the
+fits library for implementing the FITS specifications in a convenient
+C-friendly environment, and the ohana library, consisting of basic
+string and other basic operations.  
+
+For more details on the programming aspects of parts of the pipeline,
+see the associated document, 'A Programmer's Guide to the Loneos
+Pipeline'
+
+\section {Comparison with USNO catalog}
+
+I have made several comparisons between the USNO catalog and results
+from running images from Loneos through the photometry pipeline.  In
+general, there is a good agreement between the astrometry of the two
+datasets.  There are specfic areas where the two disagree, however.
+First, as discussed above, the USNO star positions may be
+significantly different from the observed star positions simply
+because of proper motion.  The first picure in the ``Comparisons
+between Loneos + USNO'' pages shows several stars with significant
+proper motion.  These are currently being flagged in the photometry
+database as discussed.  Other differences evident in this first
+picture include the classification of bright stars and the splitting
+of faint and bright stars.  In the upper right of the image, two
+bright stars are visible, one of which is actually a double.  The USNO
+catalog does a good job of detecting both, but it also detects and
+includes as stars several diffraction spike points.  The HST GSC only
+detected a single point and did not split the star at all.  The Loneos
+data in our pipeline did not detect the star at all due to saturation.
+A star in the upper left corner shows the ability of {\tt dophot} in
+the Loneos pipeline to split double stars.  This star is, on close
+inspection, clearly a double.  The USNO catalog only detects one.  The
+Pipeline not only splits the two stars, but it also makes the correct
+cross-identification of the stars.  
+
+The second set of pictures shows the ability of the Loneos pipeline to
+deal with small galaxies.  There are two small galaxies in this field,
+both of which are broken into 4 or 5 objects by the USNO catalog, but
+maintained as single objects in the Loneos pipeline.  In another
+example (not shown) a bright star within the disk of a small galaxies
+was clearly split from the galaxy by the pipeline (and also by USNO).
+
+\appendix
+\section{Parameter file params.txt}
+\begin{verbatim}
+# Configuration file for Loneos analysis pipeline 
+
+# important files and directories
+IN_DIR                  /pallas
+OUT_DIR                 /irene/d11/workspace
+#
+CATDIR                  /hebe/d27/database
+IMAGE_CATALOG           /hebe/d27/database/Images.dat
+IMAGE_CATALOG_TEMPLATE  /hebe/d27/database/template.cat
+CATALOG_TEMPLATE        /hebe/d27/database/template.cat
+ROCK_CATALOG            /hebe/d27/database/Rocks.dat
+#
+GSCFILE                 /metis/d11/references/GSC/GSCregions.tbl
+GSC_DIR                 /metis/d11/references/GSC
+USNO_CDROM              /metis/d11/references/USNO
+DOPHOT_PARAMS           /metis/d11/references/config/default_parameters
+FLATTEN_SCRIPT          /metis/d11/references/config/flatten.pro
+LONEOS_REGIONS          /metis/d11/references/config/regions.map
+
+# parameters for "fstat"
+# instrumental mag zero point
+ZERO_PT                 24.5
+DOPHOT_CHAR_LINE        129
+DOPHOT_TYPE_FIELD       5
+DOPHOT_AP_FIELD         91
+DOPHOT_PSF_FIELD        52
+MIN_SN_FSTAT            6.0
+
+# parameters for "gastro"
+DEFAULT_RADIUS   32.0
+MINIMUM_RADIUS   1.5
+MIN_ERROR        0.6
+MIN_PRECISE      0.12
+CCD_PC1_1        1
+CCD_PC2_2        -1    # for loneos
+CCD_PC1_2        0
+CCD_PC2_1        0
+ASEC_PIX         2.82
+NFIELD           3.0
+MMIN             6.0
+ROT_ZERO         0
+dROT             1.0
+NROT             2
+POLAR_AXIS_RA    -90.0
+POLAR_AXIS_DEC   89.88
+RA_OFFSET        -90.0
+DEC_OFFSET        0.0
+
+# parameters for "addstar"
+# airmass extinction coefficient (in mag / airmass)
+NSIGMA                  3.0
+ALPHA                   0.03
+XOVERSCAN               60
+YOVERSCAN               50
+
+# parameters for "controller"
+NEW_IMAGES              images.dat
+USERNAME                gene
+PASSWORD                gene
+
+# here we list all available machines
+NMACHINES               6
+MACHINE0                metis
+MACHINE1                hebe
+MACHINE2                iris
+MACHINE3                irene
+#MACHINE4               juno
+MACHINE4                ceres
+MACHINE5                vesta
+
+# here we define which machines which are always free
+fMACHINE0               1
+fMACHINE1               1
+fMACHINE2               1
+fMACHINE3               1
+fMACHINE4               0
+fMACHINE5               0
+fMACHINE6               0
+
+LOCAL_MACHINE           1  # machine with catalog on local disk
+
+# parameters for "status"
+TIME_REFERENCE          90/01/01 00:00:00
+
+# parameters for "nrphot"
+# make this one small to boost change of getting an early region 
+TAU                     3.0             
+SCATTER_LIM             15.0
+MAG_LIM                 17.0
+IMAGE_SCATTER           0.075
+NIMAGE_SCATTER          1.5
+STAR_SCATTER            0.05
+
+### parameters for "markstar"
+SEARCH_RADIUS           360   # region for initial trail hunt, in arcsec 
+TRAIL_WIDTH             5.0   # expected trail width, in arcsec
+NANGLE_BINS             180   # Number of angular bins in 180 degrees
+NPTSINLINE              5     # minimum points needed for trail
+MIN_DENSITY             0.025 # minimum linear density in star/arcsec
+SPACE_SIGMA             10.0  # how much denser than average for trail?
+# parameters defining bright star exclusion regions
+BRIGHT_XTRAIL_WIDTH       5.0 # faintest sat. star
+BRIGHT_XTRAIL_MAG         9.5 # faintest sat. star
+BRIGHT_XTRAIL_SLOPE     -80.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_YTRAIL_WIDTH       5.0 # faintest sat. star
+BRIGHT_YTRAIL_MAG        11.0 # faintest sat. star
+BRIGHT_YTRAIL_SLOPE    -400.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_HALO_MAG           8.5 # faintest sat. star
+BRIGHT_HALO_SLOPE       -28.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+# parameters which define the ghosts
+GHOST_MAG               7.5
+GHOST_RADIUS            200   # in arcsec
+OPTICAL_AXIS1           2154.0
+OPTICAL_AXIS2           2193.0
+
+# parameters for "addusno"
+USNO_RADIUS             5.0   # in arcsec
+USNO_PROPER             20.0   # in arcsec
+USNO_RED                1000  # code for USNO Red data
+USNO_BLUE               1001  # code for USNO Blue data
+
+# parameters for "markrock"
+ROCK_RADIUS             2.0   # in arcsec
+ROCK_MAX_RADIUS         20.0  # in arcsec
+ROCK_MAX_SPEED          0.1   # in arcsec / sec
+\end{verbatim}
+
+\section{Creating New Flats}
+
+The LONEOS implementation of the analysis pipeline has a limited
+ability to choose a flatfield image.  There is a mechanism in the
+stage1 script to select a flatfield on the basis of the photcode
+entry.  It works like this:  there is a fixed directory for all
+flatfield files, currently /metis/d11/reference/.  This directory is
+written in the file flatten.pro, and also in the script stage1.  Each
+flatfield has a name like flatcodeN.fits, where N is the relevant
+photcode.  The mana macro 'flatten' in flatten.pro loads the file
+\$flatdir/flatfield.fits (OR SOMETHING LIKE THAT), which is a link to
+the correct flat.  The script stage1 has the job of deciding which is
+the correct photcode and setting the link appropriately.
+
+This then begs the question of how this flatfield file should be
+created and assigned a new photcode.  The flatfield is NOT made
+nightly from the entire set of data, as one might expect.  This is not
+done because of the problems of scattered light in the telescope.  The
+widefield of the LONEOS telescope makes it somewhat succeptible to
+light at relatively large incident angles.  As a result, a median
+image made from images of the night sky will have different amounts of
+scattered light depending on the overall sky brightness.  In our
+experience, the presence of light clouds and just a small moon (as
+little as a quarter) make the resulting median image a very poor
+representation of the chip response.  Our solution to this problem is
+to choose a good night, in which the sky is very dark, and make a
+flatfield from that collection of images.  As a result, there is too
+much decision-making involved to allow the pipeline to make this
+decision.  Instead, we have chosen to only infrequently make a new
+flat from the data, choosing those nights that we are confident will
+not introduce excess scattered light.  
+
+There are a set of scripts and programs to make the flatfield image.
+The process involves first removing the bias and dark from the image,
+by subtracting a dark image, then medianing the resulting images.
+The mana script FRED performs the dark-subtraction and then
+medianfilter performs the medianing.  In fact, instead of medianing,
+medianfilter returns the average of the fraction of pixels with values
+between f1 and f2 percent of the total range of the pixel data. 
+
+\section{Daily Monitoring of the Pipeline}
+
+On a daily basis, the pipeline generally runs by itself without any
+intervention.  There are a few checks that can be made to be sure the
+process is running OK.
+
+First, it is good to see if the disk space is sufficient.  There is a
+component to stage1 which will keep it from running the analysis if
+there is less space on the destination disk (/irene/d11/workspace)
+than 1.1 times the amount of space in the source directory
+(/pallas/d?/YYMMDD).
+
+Second, it is a good idea to see if the nightly cron job is checking
+for new data and if the data is getting analysed.  If data was created
+lastnight, there should be a set of files in the log directory
+(/metis/d11/logs) of the form YYMMDD.inlist, YYMMDD.addlist, and
+YYMMDD.outlist.  If these files do not exist, it may just mean that no
+data was created last night.  In that case, the tail of the file
+/metis/d11/logs/lastnight should say something like 'there is no new
+data to analyse'.
+
+If data was created, and these files exist, it is a good idea then to
+check that the analysis of the images was reasonable and didn't fail
+on most of the images.  There are two Perl scripts to check on this.
+First, 'checkaddlist [filenames]' will take a list of *.addlist files
+and, for each file, list the total number of images, then the number
+that failed for each of the four stage1 processes, flatten, dophot,
+fstat, and gastro.  A typical run might look like this:
+
+\begin{verbatim}
+hebe: checkaddlist 9907??.addlist
+990701: 206 0 0 0 0
+990703: 199 5 0 0 0
+990705: 150 10 1 0 1
+\end{verbatim}
+
+It is actually unusual for any except the flatten process to fail, but
+it might be typical for 1 or 2 to fail, since they will represent
+aborted images or images which failed to write everything to disk.
+The second Perl script is 'checkoutlist' which works just the same
+with the YYMMDD.outlist files, but only give statistics on the
+'addstar' process.  Here, it is more common for several images to fail
+'addstar'.  At the current time, 'gastro' does not return a fail
+status if it finds a bogus solution.   Images which fail 'gastro' are
+mostly those which were taken when the sky was too bright and all that
+is seen is the sky itself, or the focus frames, or the very short
+images taken of bright stars to check the coords.  If the images fail
+'gastro', they are rejected by 'addstar'.  
+
+\section{Restarting the Pipeline}
+
+It is difficult to predict the future, so it is hard to give guidance
+on solving future problems.  Some of the things which might happen
+would include having one of the machines which serve a disk go down or
+having the disks fill up.  If this happens, it is possible that the
+run of a given night will have failed completely, or mostly.  There
+are a variety of things to do to recover from these situations.
+
+\subsection{Deleting data from the archive}
+
+The program 'delstar' lets the user delete a set of data from the
+database.  There are several options for this.  The first example is:
+{\tt delstar 199902040015b.cmp}.  In this form, delstar uses the
+astrometry information in the image header to find the measurements
+and delete them from the database, along with the image from the image
+database.  This form requires the datafile *.cmp to exist in the local
+directory.  The second form, {\tt delstar 911012323 1}, uses the given
+time in standard FORM (NEED TO DISCUSS TIME FORMATS AND GIVE THEM
+NAMES) to identify the image in the image database and delete both the
+image and the measurements.  It is easy to associate time with image
+with the status function 'findimages' or 'gimage' which list images
+covering a specific location.
+
+The third form of delstar is used to clean up the database if an image
+is deleted but not all measurements from that image are removed.  The
+routine 'addstar' uses the astrometric information for an image and
+the X,Y coordinates of each object in the image to locate the object
+in the database.  Several other routines which do the reverse process of
+finding location of objects within the images.  The function which
+performs this association needs to find all database *.cpt files which
+are covered by the image.  The existing implemenation is relatively
+quick, but not the most general possible.  If an image has extreme
+astrometric problems, yet somehow makes it through the pipeline, it
+will introduce stars which are not easily found by the inversion
+process.  This may be because the image has astrometric parameters
+which make it extremely non-rectilinear.  If such an image is deleted,
+which will probably be desired, some of the stars from that image may
+get left behind.  These orphaned stars can be deleted with the command
+{\tt delstar -orphan STUFF}.
+
+\subsection{Deleting a night from the pipeline}
+
+If the analysis for a specific night fails miserably, it is probably
+necessary to delete the entire night and then encourage the system to
+redo that night.  It is easy to delete an entire night with delstar by
+giving a time interval which includes the night in question.  It is
+also easy to find the value of the time in seconds with the status
+function 'ctimes'.  (At some point, delstar should incorporate the
+code from status which does the time format conversion automatically).
+This process will remove the night in question from the database.  But
+before the pipeline can be re-run, the pipeline needs to be forget
+that it ran that night before.  Every night which has been analysed
+gets included in the log files 'processed.log' and 'addstared.log'.
+The appropriate entry from these files needs to be deleted.  It is
+also necessary to delete the files 'YYMMDD.addlist' and
+'YYMMDD.outlist' from the log directory.  If the 'extracted.log' file
+exists, it should be deleted as well.  All of this is predicated on
+the assumption that the user has halted all processing by the pipeline
+before trying to fix anything.  At this point, it is possible to
+restart the database.  This can be done in several possible ways.  If
+the night in question has already been put on tape, it has to be
+extracted.  The program 'slurp' will automatically find data which has
+not been extracted from the tape, download it, and set up the
+appropriate files.  Unfortunately, 'slurp' uses the file 'tape.log'
+which is not automatically generated.  It is therefore necessary to
+add a line to 'tape.log' (in /metis/d11/logs) for the night in
+question.  Instead of actually running 'slurp', however, the user
+should just run 'stage1 -once' which will perform one run of stage1
+and then end.  If there is no pending data (nothing in 'extracted.log'
+that has not been analysed), then stage1 will automatically call
+'slurp' in the correct fashion (ie, on pallas), run through the
+analysis, and then exit without restarting.
+
+If the night's data has not been deleted yet, then it is not necessary
+to run 'slurp' as the script 'lastnight' will do the job.  The
+cronscript for 'lastnight' uses the -today option so that the
+automatic analysis will not reattempt any nights still on pallas.
+Instead, it will be necessary for the user to run 'lastnight' with the
+-any flag, which will also take care of running 'stage1'. This should
+be safe at anytime, as the lock functions keep multiple instances of
+the programs from running, but it is better to do this with enough
+time to allow the process to finish before the next night's data is
+ready for processing.
+
+If the pipeline has been interrupted, it will probably be necessary to
+restart 'stage2' as well.  Since 'stage2' always restarts itself on
+exit, there is no cron job for it to make the initial start.  (This
+probably should be introduced into the /etc/rc.local file, or some
+cron implementation for stage2 worked out).  Note that both 'stage1'
+and 'stage2' take no arguments except the -once flag.  They make their
+own decisions about the correct log file number and so forth.
+
+If the 'lastnight' cron job gets lost, there is a copy of the
+necessary cron script /metis/d11/src/ohana/src/perl/lastnight.cron.
+This can be submitted with 'crontab lastnight.cron'.  
+
+\section{Data backup}
+
+Backups are the still rather primitive in implementation for the
+LONEOS system.  There are two things which should be backed up, but
+only one currently has a good scheme.  The database itself does not
+have a backup process at the moment.  This is a bad situation, but not
+an easy one to solve.  The database is very large, over 30 GB, which
+is larger than the space on a single AIC tape.  At the moment, we are
+satisfied with simply saving the intermediate results directories, all
+of the YYMMDD.tgz files.  If the entire database is destroyed, these
+files can be used to rebuild the database in a relatively short time
+(currently, about a week).  The script 'backup.stuff' will backup a
+group of files.  A good method is to backup a month at a time.  These
+will become a single tar file on the AIC tape, and a month's worth of
+data is a reasonable amount of disk space for future downloads.
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline6.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline6.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/pipeline6.tex	(revision 22322)
@@ -0,0 +1,2047 @@
+%\documentstyle[astro,psfig]{article}
+\documentclass{article}
+\usepackage{psfig}
+\usepackage{astro}
+\usepackage{times}
+\GoodMargins
+\newcommand{\note}[1]{({\bf #1})}
+\newcommand\pr{\em}                   % font for program names
+\newcommand\ff{\bf}                   % font for file names
+\newcommand\cc{\tt}                   % font for program code
+\newcommand\ty{\sc}                   % font for file types
+
+\title{Variability Pipeline}
+\author{Eugene Magnier}
+
+\begin{document}
+\maketitle
+
+\section{Overview}
+
+Ptolemy is a collection of programs which act together to provide
+automated reduction of images, including photometry and astrometry,
+data validation, and incorporation of the resulting measurements in a
+photometry database organized by star position on the sky.  Ptolemy
+was designed to deal with images produced by LONEOS, the Lowell
+Observatory Near Earth Object Search, to study the data stream with
+astronomical goals in mind beyond just the Near-Earth Objects.  The
+document deals with both the generalities of Ptolemy and also the
+specifics of the LONEOS implementation.  Why Ptolemy?  Two reasons:
+First, Ptolemy, the Roman Astronomery (AD 100-170) was the the first
+to make a systematic map of the entire sky visible from Greece, much
+like LONEOS is doing.  Second, Ptolemy I (367-283 BC), as the founder
+of the Library of Alexandria, represents the ideals of collection and
+organization of large quantities of knowledge).
+
+Fig.~\ref{pipeline} shows a schematic of the {\pr Ptolemy} program
+organzation.  Images to be analysed may be provided to Ptolemy either
+from the telescope or from a tape archive.  Images are first analyzed
+by {\pr dophot}, which produces instrumental photometry and pixel
+coordinate positions for stars and other objects in the images,
+writing files with a particular format, called {\ty obj} files in this
+discussion.  The results from {\pr dophot} are cleaned up by a program
+called {\pr fstat} which merges the header information from the image
+with the most relevant parts of the {\ty obj} file, creating a new
+file with the {\ty cmp} format.  Astrometry is performed on the {\ty
+cmp} file with the program {\pr gastro}.  The program {\pr gastro}
+makes a comparison with an astrometric reference database and only
+changes {\ty cmp} header keywords as a result.  Finally, the processed
+image {\ty cmp} file is added to the photometry database with the
+program {\pr addstar}.  The photometry database is maintained in a
+large number of files ({\ty cpt} files), each representing a small
+region of sky.  There is no relationship between the image locations
+and the boundaries of {\ty cpt} file.  All of the preceeding processes
+occur on individual images, and would typically be performed while the
+observations are occuring, or during the day following, but may be
+performed at any time to add new or archived data to the database.
+
+\begin{figure}
+\psfig{file=schematic.eps,width=9cm}
+\caption{\label{pipeline} \small
+  Schematic of the photometry pipeline.  Rectangles represent
+  programs, while ovals represent data products.  Arrows show the
+  direction of travel of information.  The flatten routine is a filter
+  whose position may change as the pipeline matures.  }
+\end{figure}
+
+Once large batches of images have been processed, a series of programs
+should be run to clean up the photometry database.  First, {\pr
+markstar} identifies bad data (the trails of satelites, the bleeding
+columns from stars, ghost images), and flags these data points
+appropriately.  Second, {\pr addusno} matches the catalog stars with
+stars from the USNO database and adds the USNO magnitudes to the
+photometry database.  Next, {\pr markrock} searches for likely
+asteroids in the {\ty cpt} file, marks them, and writes the asteroid
+observations to an asteroid database.  It also notes measurements
+which are likely to be noise and cosmic rays.  Finally, {\pr nrphot}
+determines relative photometric calibrations for observations in a set
+of {\ty cpt} files.
+
+\section{Image Analysis Pipeline}
+\label{parallel}
+
+\subsection{Configuration and Generalization}
+With the exception of {\pr dophot}, all of the Ptolemy programs refer
+to a single configuration file to determine the large number of
+parameters necessary in the process.  The default parameter file is
+hardwired into the programs, but an alternate configuration file may
+be specified with the environment variable {\ff PIPE\_CONFIG}.  This
+option makes independent processing of multiple data sources trivial.
+For example, if the pipeline is to be used for both Loneos and SDSS
+image analysis and display, there could be two configuration files
+(eg, {\ff loneos.txt} and {\ff sdss.txt}), one for each data source.
+Several important parameters would be different between these two, for
+example, the initial plate scale guess for the astrometry routine, or
+the parameters which define the rough chip orientation.  This scheme
+allows for analysis of different types of images, with a common
+destination photometry database, or for independent photometry
+databases.
+
+Data from different sources can be processed by the pipeline and
+stored in the same photometry database, since all photometry is tagged
+with a source ID in the database (as are the images in the image
+database).  This allows us to include, for example, the USNO catalog
+magnitude measurements (or a subset) in the photometry database for a
+quick comparion.  Different sources may be considered different
+filters, so color-color and color-magnitude diagrams could be
+extracted by specifying choices for source catalog.  Currently each
+object has a single primary photometric system, and individual
+observations may be combined to determine that average (eg, if they
+are different photometry sources with roughly the filter bandpass) or
+they may be ignored when calculating relative magnitudes (eg, the USNO
+magnitudes would be ignored).  This may be expanded upon by defining
+multiple average magnitudes and allowing different measurements to
+contribute only to the appropriate average.  The determination of the
+photometry code for a particular image is currently not well defined.
+There are several points in the analysis where the user (or the
+controlling program) may specify the photometry source.  A table
+should be kept in the photometry catalog to define the various
+photometry codes.  
+
+file names: Notice that we use a consistent naming convention through
+the pipeline reductions.  The image is called {\tt foo.fits}, while
+the resulting dophot-produced photometry list is called {\tt foo.obj}.
+
+\subsection{Time representations in Ptolemy}
+
+Every measurement in the Ptolemy database has a time stamp associated
+with it, as do all of the images.  Throughout the software, the time
+is represented consistently using the standard UNIX representation of
+time, which consists of an unsigned integer representing the number of
+seconds since 1970 Jan 01, midnight.  The time used roughly represents
+UT time, though there is some subtlety here.  In reality, the time
+reported at LONEOS is reported by a GPS clock, and therefore uses the
+GPS zero point.  There are small, well defined differences between UT,
+GPS, and WHAT IS THE THIRD ONE?  GPS - UT is a constant NN seconds,
+while UT - OTHER is a function of time, as leap seconds are
+accumulated in one and not the other.  While the programs all refer to
+this seconds representation of GPS time, the user of the programs
+usually has a few more choices.  In {\pr status}, all(?) functions
+which require a time argument expect to see one of three formats. If
+there is a number of the form NNNNNNNNNs, this is assumed to be the
+UNIX seconds form.  If there is a number of the form NNNNNNj, this is
+assumed to be a Julian date.  If there is a number of the form
+YY:MM:DD,hh:mm:ss, this is assumed to be a UT date string.  Note that
+the difference is in the presence of either 's', 'j', or something
+else at the end of the string.  In the case of a date, it is
+acceptable for the separators to be any non-numerical character, and
+there may even be a trailing separator, as long as it is neither 's'
+nor 'j'.  Command arguments which expect a time range may provide one
+of the following units: 's' (seconds), 'm' (minutes), 'h' (hours), 'd'
+(days).  Also, it is only necessary to provide as many significant
+places in the date as desired.  So, if you are interested in the
+images taken on UT 1999 March 03, you could run {\pr images -im
+99:03:03 1d}.  
+
+The validity range of the UNIX seconds representation of time is 143
+years, so this measurement representation will become ambiguous in the
+year 2113.  Note that the Y2K problem is dealt in the date string by
+assuming any date with year smaller that 70 means 21st century, and
+others are 19th century.  Of course, the user may explicitly type the
+full 4 digit year to ensure an unambiguous answer.  Finally, the user
+may refer to the current time as NOW and the current date as TODAY.
+
+\subsection{Flat Fielding -- Mana / Flatten}
+
+Flat fielding is performed on each image with {\pr mana} (see {\pr
+mana} user's guide) using a script which interacts with the Ptolemy
+controlling software.  The script is contained within a file, {\ff
+flatten.pro}, in the references directory.  The script is invoked by
+calling it on the command line when {\pr mana} is started.  If {\pr
+mana} is started with a file name on the command line, that file is
+loaded with in 'input'.  The file defines a macro '{\pr flatten}', and
+also loads the flat-field file.  The flattening process is actually
+done by calling within {\pr mana} the macro {\pr flatten}, with the
+input and output file names as arguments.  By loading the flat-field
+at the start of {\pr mana}, instead of on each execution of {\pr
+flatten}, we save the load time of each flat-field image (which is 2x
+the size of the basic image, being 4byte floats per pixel).  \note{The
+location of the flat field is currently hard-wired in the {\pr
+flatten} script - this should probably be changed in the future.}  The
+macro loads a flatfield with a specific name and location, which is
+actually a link; The higher-level controlling process ({\pr stage1})
+creates a link to the correct flatfield.  This allows the analysis
+process to use different flatfields for different situations.  At
+LONEOS, we currently use a flat field generated from a well-understood
+night.  This is probably not the best solution as the flatfield
+changes with time.  As discussed below, the photometry solution
+program, nrphot, fits a high-order polynomial to the relative
+photometry, allowing for differences from the correct flatfield.
+ 
+\subsection{Photometry -- Dophot}
+
+Photometry is performed using a varient of {\pr dophot}, based on {\pr
+Dophot 2.0} and adapted to streamline processing of many files.  With
+this version of {\pr dophot}, any FITS format data is safely read in.
+Instead of using staticly defined memory blocks for the images, the
+necessary memory is allocated dynamically.  The image is also
+converted to REAL*32 from the given BITPIX for processing.  Finally,
+only a single, complete parameter file is loaded by dophot.  There is
+no option for a modified and a default parameter file.  The parameter
+file to be used is given, along with the input image and the output
+object list on the command line when the program is run: {\cc dophot
+image.fits image.obj parameter\_file}. To achive these changes, the
+code from {\pr dophot} was wrapped within a C program which interprets
+the command line arguments, allocates the necessary memory, and reads
+in the image.  The rest of the processing is performed by {\pr dophot} in
+the standard manner.  
+
+\subsection{Cleanup -- fstat}
+
+After an image is processed by {\pr dophot}, the object file ({\ff
+foo.obj}) is converted to a more-compact, more-complete file called a
+{\ty cmp} file, with a name of the form ({\ff foo.cmp}).  This
+conversion is done with the program {\pr fstat}.  The {\ty cmp} file
+consists of the FITS header from the original image ({\ff foo.fits}),
+with some additional keywords to be used at later stages in the
+analysis, followed by an ASCII list of the interesting data from ({\ff
+foo.obj}).  In this list, types 6 and 8 are excluded, and only the
+following values are kept:
+\begin{verbatim}
+X       Y     Mag   dMag t log(sky) 
+1342.0  106.1 14.166 000 4 3.2
+\end{verbatim}
+Objects with a signal-to-noise ratio lower than a specified cutoff
+({\cc MIN\_SN\_FSTAT}) are also excluded.  Some general information
+about the image is derived by {\pr fstat} (FWHM, saturation and
+completeness limits, number of each dophot type) and stored as
+keywords in the header.  The photometry source must be specified at
+this stage as one of the command line arguments, though the value used
+may be overridden in one of the stages belowe.  The resulting file can
+now stand on its own without reference to the original image.  The
+keywords used by {\pr fstat} include the minimum signal to noise, a
+rough guess at the zero point ({\cc ZERO\_PT}), and four numbers
+defining the format of the {\pr dophot} {\ty obj} file.
+
+\subsection{Astrometry -- gastro}
+
+Astrometry is performed automatically by the program {\pr gastro}.
+{\pr gastro} loads a {\ty cmp} file and determines an initial guess
+for the center coordinates (based on the RA and DEC header keywords).
+The program also uses the configuration information about the plate
+scale and rough orientation of the image to get close to the final
+solution.  Also, the true sky position of the telescope pole may be
+defined to allow astrometry on images taken close to the pole.  The
+comparison is made with the astrometric catalog, which may be the HST
+Guide Star Catalog, or it may be any source if the data is placed in
+the correct format.  \note{the astrometric reference catalog
+is currently stored in an ASCII format with very limited efficiency.
+It would be nice to make a standard format that has a bit of meta-data
+documentation and probably a binary format to save space.}
+
+\section{The Photometry Database}
+
+\subsection{Photometry File Format}
+The photometry database consists of a large number of photometry files
+representing sections of the sky and a file containing information on
+the images incorporated in the database.  These files are called
+alternatively ``region files'' or {\ty cpt} files (because they have
+the extension {\cc .cpt}).  The photometry files are sorted by
+Declination into different directories, each consisting of a band
+7.5\degree\ wide.  These directories have names like n0730,
+representing the band starting at a Declination of +07:30.  The image
+database ({\ff Images.dat}) is stored in the upper level directory of
+this directory tree.  In fact, the locations of the {\ff Images.dat}
+file, the photometry database, and such are all flexible and may be
+changed by altering the configuration file.  
+
+At LONEOS, the database is stored mostly on a RAID disk on {\ff hebe}
+and a RAID disk mounted on {\ff metis}.  It is easy to split the
+database across multiple file systems by using links.  Within the
+database directory, there are directories for each of 24 Declination
+bands (7.5 degrees tall).  The easiest way of splitting up the data is
+to move some of these subdirectories to another device and creating
+links to them at the appropriate place.  This is currently done by
+having a directory{\ff /metis/d27/database} with the remote
+directories.
+
+The name and location of each sky region comes from the Hubble Space
+Telescope Guide Star Catalog.  There is a file, {\ff GSCregions.tbl},
+which can be used to find the region file appropriate for any location
+on the sky.  A number of routines exist to make such a query.
+
+Each photometry file in the photometry database contains all
+observations and all average values for all stars observed within the
+appropriate region on the sky.  The identity of stars is essentially
+determined by their RA and DEC coordinates.  In very crowded regions,
+the end user may need to double check that a neighboring star is not
+contaminating individual detections (see the discusion on {\pr
+addstar} below).
+
+The photometry files are stored in a binary format, both to reduce the
+volume and to speed access.  As a result, the interpretation of the
+data is machine dependent: little endian machines need to swap the
+data intelligently.  Fortunately, this operation is taken care of
+appropriately, if the programs are compiled with the BYTESWAP option
+set as needed, which is automatically set by the Makefile in the case
+of a Linux machine.  For reference, Suns, SGI, and HP are all big
+endian, while PCs and DECs are little endian.  The automatic
+conversion takes place by using the funcions {\tt Fread} and {\tt Fwrite}
+to substitute for the standard C library {\tt fread} and {\tt fwrite}
+functions.  {\tt Fread} and {\tt Fwrite} are told the datatype being read,
+and they know the layout of the bytes for a particular datatype.
+Anytime the definitions of the relevant structures (see below) are
+changed, {\tt Fread} and {\tt Fwrite} must be updated.  Fortunately, there
+is only one file ({\tt Fread.c}), and it has a single byteswapping
+section, making it easy to adjust the code appropriately.
+
+The format of a single photometry database file consists four
+sections:  
+\begin{itemize}
+\item {\bf header} -- this is a standard FITS header with room for comments
+  about the file or whatever.  Since the rest of the file is not a
+  standard FITS file, the SIMPLE keyword is set to False.  The three
+  necessary keywords are NSTARS, NMEAS, and NMISS, which define the
+  sizes of the next three sections.
+\item {\bf average} -- this section contains all average measurement
+  quantities for each star in the file.  There are NSTARS entries, and
+  each entry is the data from a structure of type Average (defined in
+  loneos.h, and discussed in more detail below).  Thus, the total size
+  in bytes of this section can be found by NSTARS*sizeof(Average).
+\item {\bf measure} -- this section contains the individual
+  measurements for each star in the database.  There are NMEAS
+  entries, and like the average section, each entry is a single
+  structure, in this case of type Measure (in loneos.h, see below).
+  All measurements of a single star are consecutive.  The number of
+  measurements for a single star is defined by average.Nm and the
+  starting entry for a single star is given by average.offset.
+  Therefore, the first measurement of a specific star average[500]
+  would be given by measure[average[500].offset], while the second is 
+  measure[average[500].offset + 1], and so forth.  
+\item {\bf missing} -- this section contains references to all missing
+  measurements of a star.  This means all of those occasions when an
+  image enclosed the location of a star in the database, but no star
+  was detected on the image.  Similar to the case of the measure
+  section, each star has a number of missing entries (which may be 0)
+  given by average.Nn, and the first missing entry is given by
+  average.missing.  Each missing entry value is again a structure of
+  type Missing.  Currently, the only data in the structure is the time
+  of the observation, which allows for an unambiguous recovery of the
+  image where the star was missed.
+\end{itemize}
+
+\subsubsection*{Average Structure defined in loneos.h}
+\begin{verbatim}
+typedef struct {
+  float R;                    /* RA  in decimal degrees */
+  float D;                    /* DEC in decimal degrees */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned short int Nm, Nn;  /* number of measurements, missing */
+  short int Xp;               /* scatter in 1/100 arcsec (-327.67 to +327.67 valid range) */
+  short int Xm;               /* 1000*log(chisq) for magnitude measurement */
+  unsigned short int code;    /* an ID code (ie, star, ghost, satelite, etc) */
+  signed int offset;          /* offset to first measurement */
+  signed int missing;         /* offset to first missing obs */
+} Average; /* 28 bytes / Average */
+\end{verbatim}
+The structure above defines the average data stored in the photometry
+database for each star.  Most of the entries are self-explantory, but
+some need a bit of clarification.  The entry {\tt Xm} is the magnitude
+$\chi^2$ value for the star, under the assumption that the star
+brightness is constant.  This $\chi^2$ incorporates the photometric
+errors on each measurement and is stored as 1000 times the logarithm
+of the $\chi^2$ because high dynamic range is not needed.  The entry
+{\tt Xp} is the scatter about the average postion, in 10
+milli-arcseconds.  As discussed above, the values of {\tt Nm} and {\tt
+  Nn} give the number of measurements and missing observations for
+this star, while the entries {\tt offset} and {\tt missing} point to
+the first {\bf measure} and {\bf missing} entry for this star.  The
+entry {\tt M} is the current best average magnitude solution for this
+star (see the section on Relative Photometry).  Finally, the entry
+{\tt code} is an ID code for each object, which may define a variety
+of things about the object.  For example, if the object is known to be
+variable, or if the object is associated with a USNO star, or if the
+object is a measurement of an asteroid.  See below for details of
+these definitions.
+
+\subsubsection*{Measure Structure defined in loneos.h}
+{\small
+\begin{verbatim}
+typedef struct {
+  short int dR, dD;           /* 1/100 of arcsec (-327.67 to +327.67 valid range) */
+  short int M;                /* thousandths of mag (-32.767 to 32.767 valid range) */
+  short int Mcal;             /* image cal mag, thousandths of mag (-32.767 to 32.767 valid range) */
+  unsigned char dM;           /* thousandths of mag (0.000 to 0.255 valid range) */
+  char dophot;                /* dophot type (1-9) */
+  unsigned short int source;  /* code to identify photometry source */
+  unsigned int   t;           /* time in seconds (0 - 143 years valid range) */
+  unsigned int average;       /* reference to corresponding average entry */
+  /* upper byte of Measure.average stores flags:
+   limit of 16,777,215 stars (Naverage) in a file (=0xFFFFFF) 
+   flags: average & 0x1000000 */
+} Measure; /* 20 bytes / Measure */
+\end{verbatim}
+}
+The structure above defines the individual measurement data stored for
+each observation of each star.  The first two entries, dR and dD, are
+the difference between the average coordinates for this star and the
+coordinates determined for this observation.  Clearly, if this
+residual is too large, the individual measurement will not be
+successfully matched with the average star position.  {\tt M} and {\tt
+  dM} are the instrumental magnitude and error for this measurement,
+after correction for the exposure time and a zero point (defined in
+the configuration file as ZERO\_PT).  The entry {\tt t} determines the
+time of the individual observation.  This is important not only for
+timing purposes, but also to determine the source image for this
+observation.  The time and the entry {\tt source} uniquely determine
+the image in the image database that generated this measurement.
+Furthermore, this correspondence allows for determination of the pixel
+coordinates on the chip, by going throught the image astrometry stored
+in the Image database (see below).  The entry {\tt source} is a code which
+identifies the particular CCD/Telescope/Filter setup.  Each unique
+{\tt source} should be treated as an independent filter set for the
+purposes of accurate relative photometric comparisons.  This entry
+also allows external catalog data, such as the USNO or Sloan
+photometry, to be added to the database, as desired.  The entry {\tt
+  average} is a pointer back to the {\bf average} section to allow
+determination of the star from the measurements, in addition to the
+reverse.  The bits of the upper byte of this value are reserved for
+flags used to define particular situations for this measurement.
+Possible values are:
+\begin{itemize}
+\item 1 = BLEND\_IMAGE - the star on the image matched more than one
+  catalog star
+\item 2 = BLEND\_CATALOG - the star in the catalog matched more than
+  one image star
+\item 4 = UPPER\_LIMIT - \note{not really used?}
+\item 8 = CALIBRATED - relative photometry has been performed at least
+  once.
+\end{itemize}
+The entry {\tt dophot} stores the dophot type for this particular
+measurement (1-9: four extra bits in this field).   
+Finally, the value {\tt Mcal} determines the photometric calibration
+of this specific measurment.  This value is an offset appropriate to
+this image (and if needed, this point in time and this chip position)
+to bring the observations of the stars on multiple images to a common
+system (see the section on relative photometry).
+
+\subsection{Image Database}
+
+All images for which photometry has been included in the photometry
+database also have entries in an image database.  The image database
+is (currently) a single file in the upper directory of the photometry
+database.  The name of the file is determined by the IMAGE\_CATALOG
+entry in the configuration file, and is currently set to Images.dat.
+Like the photometry database files, the image database consists of a
+FITS header followed by binary data.  There is only one type of binary
+data: each image has an entry, which is the data from a structure of
+type Image (loneos.h).  The number of images in the file is determined
+by the {\tt NIMAGES} keyword.  If the number of images becomes too
+large, extrapolation of the system to a set of image database files
+with a reference list is quite straightforeward, and could be modeled
+on the organization of the region files (eg, one image database file
+for each Declination directory).
+
+\subsubsection*{Image Structure defined in loneos.h}
+{\small
+\begin{verbatim}
+typedef struct {
+  Coords         coords;            /* 120 bytes */
+  unsigned int   tzero;             /* readout time row 0 in sec (0 - 142 years valid range) */
+  unsigned int   nstar;             /* number of stars on image */
+  short int      secz;              /* thousanths of airmass (valid range -32.000 -- 32.000) */
+  short int      NX, NY;            /* dimensions of image */
+  short int      apmifit, dapmifit; /* aperture correction and error in thousandths of mag */
+  short int      source;            /* identifier for CCD (each ever used will have a unique letter) */
+  short int      Mcal;              /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      dMcal;             /* thousandths of mag (-32.000 -- 32.000 valid range) */
+  short int      Xm;                /* 10*log(image chi-square) */
+  char           name[32];          /* name of original image */
+  unsigned char  detection_limit;   /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  saturation_limit;  /* tenths of mag (0.0 - 25.6 valid range) */
+  unsigned char  cerror;            /* astrometric error: 1/50 of arcsec (0 -- 5.12 valid range) */
+  unsigned char  fwhm_x, fwhm_y;    /* PSF terms in 25*arcsec (valid range 0.0 -- 10.2" ") */
+  unsigned char  trate;             /* 10000 * scan rate in sec/pix (0 -- 0.0256 valid range, 
+                                       typically 0.0146. this is used only to determine the time
+                                       of the observation, not to find the coordinates.  1 byte
+                                       gives 0.11 sec accuracy */
+  float          exptime;           /* exposure time, seconds */
+  char           code;              /* flag to mark an image as bad or whatever */
+  char           dummy[21];         /* extra space for the future */
+  short int      order;             /* number of terms used for Mrel */
+  short int      Mx, My;
+  short int      Mxx, Mxy, Myy;
+  short int      Mxxx, Mxxy, Mxyy, Myyy;
+  short int      Mxxxx, Mxxxy, Mxxyy, Mxyyy, Myyyy;
+} Image;  /* 240 bytes / Image */
+\end{verbatim}}
+
+The structure above lists all of the data stored for each image.  A
+substantial amount of information is stored for each image.  First,
+the astrometric calibration information is stored in a {\tt Coords}
+structure (see coordinate systems, above).  The upper and lower limits
+of the photometry in the database (in instrumental magnitudes) is
+stored in {\tt detection\_limit} and {\tt saturation\_limit}.  These
+values are useful for comparison for those stars where observations
+are missing.  The relationship between time and pixel coordinates is
+given by {\tt tzero} and {\tt trate}.  Drift images will have a scan
+rate stored in {\tt trate}, while staring images will have a value of
+0.0 for this entry ({\tt tzero} then applies to the whole image).
+Other important parameters are kept: the astrometric error ({\tt
+  cerror}), the airmass ({\tt secz}), the exposure time ({\tt
+  exptime}), the number of stars detected ({\tt nstar}), the FWHM
+values ({\tt fwhm\_x, fwhm\_y}), the image name ({\tt name}), and the
+size of the image ({\tt NX, NY}).  The entry {\tt source} is used as
+above to define the CCD/Telescope/Filter used for this observation.
+The entry {\tt code} is used to flag images in various ways.
+Currently, only two bits are used, to define if the image has been
+photometrically calibrated and if the calibration is acceptable.
+There are 21 extra bytes are stored for future additions.
+
+\subsection{Data Incorporation -- {\pr addstar}}
+
+Images which are completely processed ({\pr dophot, fstat, gastro}) are
+then incorporated into the photometry database with the program {\pr 
+addstar}.  This program decides which region files are appropriate
+for this particular image, then one-by-one adds stars from the image
+to the appropriate region file.  Stars already in the catalog are
+matched with stars in the new image purely by a positional comparison.
+In order to avoid the difficulty of comparisons in the RA and DEC
+coordinate frame, a cartesian projection is performed.  The stars from
+the image being processed and the database stars in the same area are
+projected onto a tangent plane and positional comparisons made in this
+(locally cartesian) coordinate frame.  This avoids the dangerous
+singularities at the pole and also makes the RA 0,360\degree\ boundary
+a trivial problem.  
+
+Several choices must be made in the comparison process.  If a star in
+the catalog is matched with a star in the image, the new measurements
+of that star are added to the database.  If a star in the database
+lies in the field of the image, but is not detected, this information
+is added to the list of missing data.  The only data stored in this
+case is the time and source, which is sufficient to unambiguously
+identify the source image from the image database.  This allows later
+programs to find relevant statistics from the image database, if
+necessary.  If a star is detected in the image, but is not already in
+the database, a new entry is added to the database.  In addition, the
+same ``missing data'' from all previous observations images which have
+covered this location (ie, images already in the image database) are
+included.  This last step is necessary so that the image processing
+order is not important.  
+
+Stars also run the danger of being crowded together.  Since all
+comparisons are performed on the basis of position alone, crowded
+fields may make for ambiguous cross-identifications.  A pair of stars
+are matched if the difference in their positions is less than a
+specified search radius (dependent on the scatter in the astrometric
+solution for the image).  If more than one catalog stars are correlated
+with a star in an image, the new measurement is added to {\em both}
+catalog stars, and a flag is set noting that this observation had a
+blended IMAGE.  Conversely, if a single catalog star is matched with
+more than one image star, both new measurements are added to the one
+catalog star, and a different flat is set, noting that this
+observation had a blended CATALOG.
+
+\section{Catalog Update and Cleaning}
+
+Occasionally, once a large number of images have been added to the
+photometry database, a set of programs should be run to clean and
+update the {\ty cpt} files.  This may best be done on {\ty cpt} files after a
+night's worth of data is incorporated.  These routines define bad
+data, identify the USNO catalog stars, and search for asteroids and
+other junk.  Most of the results from these routines are the addition
+of object flags to the Average data structures (Average.code).  Most
+of these routines process data within a single {\ty cpt} file at a time.
+
+\subsection{Markstar}
+
+The first of the update programs, {\pr markstar}, identifies bad data
+and flags it.  It makes three types of identifications: bright stars,
+ghost stars, and trails.  In the first case, it searches the HST guide
+star catalog for bright stars within the field of the current {\ty cpt}
+file.  Three types of pixels are flagged for bright stars.  Any
+measurements within a circular region centered on the star are suspect
+(the ``halo'' of the bright star).  Second, points along the X axis
+are likely to contain flux from the diffraction spikes.  Finally,
+points along the Y axis are likely to have bleeding in addition to the
+diffraction spikes.  Each of these three regions has a different size
+and range which must be defined in the configuration file.  The
+regions are represented by a representative magnitude and a scale
+parameter.  For example, the diameter $d$ of the halo for a star with
+magnitude $m$ is defined as 
+$d = \mbox{BRIGHT\_HALO\_SLOPE} (m - \mbox{BRIGHT\_HALO\_MAG})$.  The X and
+Y axis points are defined by the length of the diffraction spike, with
+an equivalent formula, and the width of the spike.  Since these are
+each independent, there are 8 parameters to define the exclusion
+regions of bright stars.  One of the current drawbacks of this system
+is the reliance on the HST Guide Star photometry.  In the HST Guide
+Star Catalog, only one photometry band is given, so no color
+information is available.  The parameters currently used were defined
+based on a large number of bright stars, with no adjustment for the
+different effective filter system of the Loneos telescope and the HST
+GSC.  Thus, occasionally very red stars will be several magnitudes
+brighter in the real data than reported in the catalog.  As a result,
+large numbers of bad data points in these halos and spikes may be
+missed.  
+
+The second type of bad data flagged by {\pr markstar} are the trails
+from satelite and other space junk.  This portion of the program
+searches for objects which land along a line and which have a high
+linear density.  Parameters may be adjusted to define the width and
+the necessary density or number of points in the line.  Objects in
+lines are also only identified if all observations come from a common
+image.  If an object only has one measurement and it is in a trail,
+the entire Average is flagged to be bad.  However, if an object has
+many measurements and only one is bad, the object is only flagged as
+having some bad data points.  
+
+The third type of bad data are ghost star images.  The HST GSC is
+searched for stars projected across the telescope optical axis, which
+is defined by the user.  All objects within a region of fixed size are
+marked for ghost stars brighter than a specific cutoff magnitude.
+Similar to the trails, the average is marked as bad only if all
+detections of an object are ghost images.  Otherwise, it is noted that
+some data may be bad.
+
+To run 'markstar' by hand, the syntax is: {\pr markstar (file.cpt)}.
+There are several optional flags, which are listed if you type
+markstar by itself.
+
+\subsection{Addusno}
+
+The program {\pr addusno} makes two types of identifications of stars
+with the USNO catalog.  First, it searches for coincidences within a
+small, specified radius.  The choice of this radius is somewhat
+tricky: it is important to catch the association, but also to avoid
+making too many associations.  Unfortunately, astrometric errors in
+the USNO catalog make it necessary to choose a surprisingly large
+radius of 5\asec.  To avoid making double matches (matching the two
+USNO stars to the same photometry database object), only the USNO
+object closest to the object is associated.  There are also a
+substantial number of stars with significant proper motion between the
+USNO epochs and the current date.  It is not unusual to see 8-15\asec\ 
+discrepancy for specific stars.  To catch these objects, any objects
+which do not match the USNO database, but which have more than a
+minimum number of measurements (2 or 3?) are searched for more distant
+USNO companions.  A large, user-specified radius is used for this
+search.  Of course, only USNO stars which are not already matched to
+the database may be candidates for the proper motion match.  This
+stage is susceptible to errors in that a slow-moving solar-system
+object may be associated with a faint USNO star.  For this reason,
+objects flagged as high proper motion stars should be taken with some
+caution.  Of course, during the {\pr addusno} stage, no objects
+already flagged as bad by {\pr markstar} will be matched with USNO
+stars.  
+
+To run 'addusno' by hand, the syntax is: {\pr addusno (file.cpt)}.
+There are several optional flags, which are listed if you type
+addusno by itself.
+
+\subsection{Markrock}
+
+Once stars have been identified with the USNO database, it is possible
+to search for asteroid detections.  The program {\pr markrock}
+searches within a specific {\ty cpt} file for objects which are moving along
+straight lines in (X,Y,t) space.  In order for an object to be found
+by {\pr markrock}, it must have three points, each with only single
+measurements.  The third point must land within a specified distance
+of the line projected from the first two data points.  These
+comparisons are made in the three dimensional space of image position
+and time.  Also, objects are only accepted if they have a speed less
+than a threshold.  If an object is moving too fast, it will make a
+streak on each individual image and will probably be missed anyway.
+Moving objects detected in this way are flagged in the photometry
+database and are also written to a Rock database.  Currently, the Rock
+database just contains ASCII lists of the RA, DEC, Mag, and time for
+each detection.  In fact, the data are stored in pairs of detections,
+with the middle detection saved twice.  This format makes it easy to
+plot lines connecting the points using the connect-the-dots plotting
+style of status/kapa (see below).
+
+One of the current drawbacks of {\pr markrock} is the insistence on
+three observations with only 1 measurement each.  If an object is
+moving too slowly, two of the dectections of the object may be merged
+together.  Enough data is stored in the database to extract these
+individual measurements, so a more sophisticated search is possible.
+It is also currently the case that USNO proper motion stars are
+ignored, but some of these will be asteroid detections which are
+simply too close to a faint USNO stars (it has to be faint because it
+would otherwise be associated with a star in the photometry
+database).  
+
+To run 'markrock' by hand, the syntax is: {\pr markrock (file.cpt)}.
+There are several optional flags, which are listed if you type
+markrock by itself.
+
+\section{Relative Photometry -- nrphot}
+
+Once the data has been incorporated in the photometry database and the
+data validation routines have been run, it is possible to determine
+photometric solutions for all of the images.  The program which does
+this is nrphot.  It tries to find a set of solutions for each image
+which reduce both the scatter per image and the scatter per star.
+\note{the following section is taken from Magnier et al
+1992, A\&A Supp 96, 379.  It could use a little editing to fit the
+reality of the LONEOS implementation.}
+
+In order bring the different measurements to a common photometric
+system, relative photometry was performed to determine calibrations
+for each image.  We used multiple measurements of individual stars on
+overlapping images to connect neighboring images.  It is useful to
+discuss some of the details of this process.  
+
+A typical photometry equation can be written in a form such as:
+\begin{equation}        
+M_{app} = c_{\lambda} + m + a_{\lambda}*\zeta + \gamma_{\Delta\lambda}*(\Delta\lambda) \\
+\label{M_mag}
+\end{equation}
+where $m$ is the instrumental magnitude,
+\begin{equation}        
+m = -2.5\log(N_{e}) + 2.5\log(t). \\
+\label{inst_mag}
+\end{equation}
+$N_{e}$ is the number of electrons measured in a single star, $t$ is
+the exposure time for the image, $M_{app}$ is the apparent magnitude
+of the star in the standard system, $\zeta$ is the airmass of the
+image, and $\Delta\lambda$ is the value of a color index (\ie\ $B-V$
+or $V-I$) for the particular star.  The subscript $\lambda$ refers to
+the filter and the subscript $\Delta\lambda$ refers to the color
+index.  Notice that we are ignoring the second-order color-airmass
+crossterm, and any higher order terms in $\Delta\lambda$ or $\zeta$.
+In all situations, $c_{\lambda}$, $a_{\lambda}$, and
+$\gamma_{\Delta\lambda}$ are calibration coefficients dependent on the
+instruments and the site.  The term $c_{\lambda}$ is a factor to scale
+the response of the detector.  The terms $\gamma_{\Delta\lambda}$ and
+$a_{\lambda}$ give the variation of the sensitivity with the color of
+the star and the airmass.  Also, Eqn. (\ref{M_mag}) is only valid for
+photometric conditions.  Under non-photometric conditions, we need to
+add another term to account for clouds:
+\begin{equation}        
+M_{app} = c_{\lambda} + m + a_{\lambda}*\zeta + \gamma_{\Delta\lambda}*(\Delta\lambda) + clouds \\
+\label{M_cloud}
+\end{equation}
+Now, we can regroup the terms in (\ref{M_cloud}) as follows:
+\begin{equation}        
+m = [M_{app} - \gamma_{\Delta\lambda}*(\Delta\lambda)] - [c_{\lambda} + a_{\lambda}*\zeta + clouds]
+\label{M_sep}
+\end{equation}
+Of the two terms on the right side of equation (\ref{M_sep}), the variables
+in the first pair of brackets are dependent on the properties of the
+star, those in the second pair of brackets are dependent on the image parameters,
+except for $c_{\lambda}$ which is independent of both image and star.
+We can now define the ``relative magnitude'' and the ``calibration
+magnitude'' from (\ref{M_sep}) as follows:
+\begin{eqnarray}
+        m       & = & M_{rel} + M_{cal} \label{rel_phot1}\\
+        M_{rel} & = & [M_{app} - \gamma_{\Delta\lambda}*(\Delta\lambda) + \Delta] \label{color}\\
+        M_{cal} & = & - [c_{\lambda} + a_{\lambda}*\zeta + clouds + \Delta] \label{M_cal}
+\end{eqnarray}
+where $M_{rel}$ is the relative photometric magnitude of the star in
+the internal system and $M_{cal}$ is a correction factor to convert
+instrumental magnitudes to relative magnitudes.  The terms $\Delta$
+are included in eqs. (\ref{color}) and (\ref{M_cal}) to make explicit
+the fact that an arbitrary constant can be added to $M_{rel}$ and
+subtracted from $M_{cal}$.  The goal of relative photometry is to
+determine $M_{cal}$ for each image, then use this $M_{cal}$ to find
+$M_{rel}$ for all stars from eq. (\ref{rel_phot1}).  Once one has
+$M_{rel}$ for each star, one can then convert them to a standard
+system ($M_{app}$) using an equation of the form of
+Eqn. (\ref{color}).  In the case of the LONEOS photometry, the absence
+of a filter makes an effective bandpass which is so wide that it may
+be meaningless to force it to a specific, standard filter.  For the
+sake of variability measurements, it is probably more sensible simply
+to refer to the LONEOS system magnitudes and present a set of color
+terms which give the rough relationship between stars of various
+colors and the LONEOS system. 
+
+In fact, the discussion above make a significant simplification which
+assumes that all images will have a single calibration magnitude
+$M_{cal}$.  In fact, it is the case in the LONEOS system that a
+variety of effects introduce systematic variations across the images.
+For this reason, in practice we extrapolate the above discussion to a
+situation where the value of $M_{cal}$ is allowed to be a function of
+position, and is fit with a 2-D polynomial of some degree.  With the
+LONEOS system, we allow the fit to take polynomials of up to 4th
+order, but only accept the minimum order which significantly reduces
+the scatter for the particular image.
+
+        An equation of the form (\ref{rel_phot1}) exists for each star
+detected on each image.  As described, the value $m$ depends on both
+the particular image and the particular star, the value of $M_{cal}$
+only depends on the image, and the value of $M_{rel}$ only depends on
+the particular star.  Let us label the images with the subscript $i$
+and the stars with the subscript $j$.  Equation (\ref{rel_phot1}) can
+then be written in the form:
+\begin{equation}
+m_{i,j} = M_{rel,j} + M_{cal,i} \label{rel_phot}
+\end{equation}
+Of course, $m_{i,j}$ only exists for certain combinations of images
+and stars: not all stars appear on all images.  In the entire system
+of equations, only one value of $\Delta$ (from eqs. \ref{color} and
+\ref{M_cal}) can be used, but, as noted above, the {\em value}\ of
+$\Delta$ is arbitrary.
+
+        In equation (\ref{rel_phot}), both $M_{rel,j}$ and $M_{cal,i}$
+are unknown quantities.  We can use a method of least squares to find
+these values for the entire set of data.  We try to minimize the chi
+square, defined as:
+\begin{equation}
+\chisq = \sum_{i,j}(m_{i,j} - M_{rel,j} - M_{cal,i})^2 / \sigma_{i,j}^2 \label{chisquare}
+\end{equation}
+where $\sigma_{i,j}$ is the error in the measurement $m_{i,j}$, and
+the index $i$ runs over all images, while the index $j$ runs over all
+stars which are multiply measured, \ie\ those stars which appear on more
+than one image.
+
+        We attempt to minimize \chisq\ analytically by finding the
+derivatives of \chisq\ with respect to $M_{rel,j}$ and $M_{cal,i}$
+and setting them equal to zero:
+\begin{eqnarray}
+\frac{\partial\chisq}{\partial M_{rel,j}} = \sum_{i}-2(m_{i,j} -
+M_{rel,j} - M_{cal,i}) / \sigma_{i,j}^2 = 0  \nonumber \\
+\frac{\partial\chisq}{\partial M_{cal,i}} = \sum_{j}-2(m_{i,j} -
+M_{rel,j} - M_{cal,i}) / \sigma_{i,j}^2 = 0
+\end{eqnarray}
+In these summations, the index $i$ runs over all images in which star
+$j$ appears, and the index $j$ runs over all stars which appear on
+image $i$ which are multiply measured.  Solving the equality leads to
+the following system of equations:
+\begin{eqnarray}
+R_{j}M_{rel,j} = \sum_{i}(m_{i,j} - M_{cal,i}) / \sigma_{i,j}^2 \label{system1} \\
+R_{i}M_{cal,i} = \sum_{j}(m_{i,j} - M_{rel,j}) / \sigma_{i,j}^2 \label{system2}
+\end{eqnarray}
+where 
+\begin{eqnarray}
+R_{i} = \sum_{j}\frac{1}{\sigma_{i,j}^2} \nonumber \\
+R_{j} = \sum_{i}\frac{1}{\sigma_{i,j}^2} 
+\end{eqnarray}
+Again, the index $i$ runs over all images in which star $j$ appears,
+and the index $j$ runs over all stars which appear on image $i$, with
+the restriction of multiple measurements.  The term $\Delta$ from
+eqs. (\ref{color}) and (\ref{M_cal}) acts as an arbitrary zero point
+between the relative and apparent photometric systems.
+
+        Equations (\ref{system1}) and (\ref{system2}) are a set of
+$N_{images} + N_{stars}$ linear equations with $N_{images} +
+N_{stars}$ unknowns.  The unknown parameters are the terms $M_{rel,j}$
+and $M_{cal,i}$.  Unfortunately, $N_{images} + N_{stars}$ is a number
+on the order of 5000 - 10000, thus an analytical solution is
+impractical and an iteration method must be used.  A simple iteration
+method is as follows:
+
+%\SingleSpace
+\begin{enumerate}
+\setlength{\parskip}{-0.05in}
+\item Guess at values for each $M_{cal,i}$.
+\item Use equation (\ref{system1}) to find values for $M_{rel,j}$.  
+\item Substitute these $M_{rel,j}$ values into (\ref{system2}) to get a new
+set of $M_{cal,i}$ values.  
+\item Return to step 1.
+\end{enumerate}
+%\DoubleSpace
+This process is repeated until some criterion is reached, such as the
+\chisq\ no longer changes or reaches a desired minimum.  Although a
+minimum can be found, error propagation causes severe problems
+since images are only connected to neighboring images.  The end point
+images are connected by \approx 50 intermediate images, and errors
+propagating over such a long distance can cause severe errors.  Also,
+$M_{rel,j}$ and $M_{cal,i}$ are not uniquely defined, because of the
+term $\Delta$.  Therefore, during the iteration process, the values of
+$M_{rel,j}$ and $M_{cal,i}$ tend to wander and do not tend to converge
+($M_{cal,i}$ may be incorrect by typically 0.5 mag).  These problems
+can be overcome if some of the values $M_{cal,i}$ or $M_{rel,j}$ are
+known.  In this situation, the known values can be held fixed,
+providing a reference point for the remaining values.  If enough
+values are known across a large enough portion of the data, no image
+is separated from the fixed values by many images, and errors will not
+build up.
+
+        Images taken under photometric conditions provide a set of
+known $M_{cal,i}$ which can be held fixed.  This is true since, if the
+image is taken under photometric conditions, the term $clouds$ in eq.
+(\ref{M_cal}) is by definition 0.  The other terms in this equation
+are either known ($a_{\lambda}*\zeta$) or are constant for all images
+($c_{\lambda}$) and can thus be assigned to a set value, which can
+later be removed with the zero point calibration.  We make an initial
+assumption of the value of $a_\lambda$, but use the data themselves to
+determine a best fit choice for $a_\lambda$.  The task that remains is
+to determine which images were photometric.
+
+        Since the conditions during observations varied greatly from
+night to night, we decided to determine which images were photometric
+from the images themselves.  To do this, we used a variation on the
+iteration method described above.  Before starting the iterations, we
+assume a value for $c_{\lambda}$.  We can choose this arbitrarily, and
+correct for our choice in the correction to an absolute photometric
+system.  Now, we perform the following iteration:
+
+%\SingleSpace
+\begin{enumerate}
+\setlength{\parskip}{-0.05in}
+\item Assume that all images are photometric and determine $M_{cal,i}$.
+\item Find the corresponding values of $M_{rel,j}$ from equation (\ref{system1}).
+\item Use these $M_{rel,j}$ values to find new values for $M_{cal,i}$.
+\item Find the values of $clouds$ for each image from these $M_{cal,i}$
+\item Assume that any image with a value $clouds$ more than 1 standard
+deviation from the mean value (over images) has a substantial cloud
+layer.  
+\item Return to step 1, but use the iterated $M_{cal,i}$ values for those
+images with substantial clouds.
+\end{enumerate}
+%\DoubleSpace
+This process is continued until \chisq (eq. \ref{chisquare}) does not
+change substantially.  Figure (\ref{fig:Mrel}) shows a histogram of
+the occurrences of different $clouds$ values.  There are a few
+important points to make about this figure.  First, there is a
+concentrated peak at 0.  All of these images have essentially zero
+cloud level.  As one would expect, there are a large number of images
+to the right of this peak.  These are the images which were taken
+through some amount of clouds.  Since the exposures were stopped if
+the clouds got too thick, it is not surprising that these images taper
+off at higher cloud levels.  The most curious and telling part of the
+plot is the area below the 0-cloud peak.  There are many images on the
+``negative cloud'' side of the peak.  Because ``negative clouds'' are
+unphysical, there must be a simple explanation for this.
+
+        We calculate the error on the relative calibration of each
+image by finding the scatter of the $M_{cal,i}$ values for each image
+and dividing by $\sqrt{N stars}$.  A histogram of this error for all
+images is shown in Figure (\ref{fig:dMrel}).  We also show the
+variation of the formal photometric error reported by \dophot\ with
+magnitude for each filter in Figure (\ref{fig:dm}).  Now that we have
+the values $M_{cal,i}$, we can calculate the values $M_{rel,j}$ for
+every star on every image.
+
+To run 'nrphot' by hand, the syntax is: {\pr nrphot (file.cpt)}.
+There are several optional flags, which are listed if you type nrphot
+by itself.  \note{discuss the optional flags in some detail}
+
+\begin{figure}
+\psfig{file=flags.eps,width=9cm}
+\caption{\label{flags} \small
+  Pictoral representation of the Average.code flags.  The lower byte
+  value is used for specific definitions of object types.  }
+\end{figure}
+
+\subsection{Data Flags}
+
+The identifications made by the above routines are noted as a set of
+flags in the Average.code structure entry.  Some of these flags are
+mutually exclusive, so a certain bit may have more than one meaning,
+depending on the value of other bits.  Figure~\ref{flags} shows a
+pictoral representation of the meaning of the different bit fields.
+The uppermost bit (bit 15) determines if the object may be considered
+a ``fixed'' star or something which has no fixed location.  If it is
+not fixed, and this bit is 1, then there are several options.  The
+second uppermost bit (bit 14) will be set if this object is one of the
+three types of bad data flagged by {\pr markstar}.  The lower byte of
+code is used to define the type of bad data: ghost, trail, or bleed.
+Thus, any datapoints determined to be a satelite trail will have a
+code value of 0xc002 (49154 decimal).  If an object is a moving
+object, but not a bad datapoint, it may be identified as a ``rock''
+(an asteroid) by the program {\pr markrock}.  In this case, the flag
+for an asteroid is turned on (bit 13).  Thus anything identified as a
+rock by {\pr markrock} will have a code value of 0xa000.  The lower
+byte of code is not yet defined for asteroids, but could be reserved
+for distinguishing different classes of asteroids (ie, on the basis of
+orbital speeds, and so forth).  Objects which are fixed may have a
+variety of possible flags.  First, an object may be identified with a
+USNO star (bit 14).  It may be found to exhibit variability (bit 13),
+implying that the relative photometry routine should ignore it.  If
+may be a transient object (bit 12).  Note that, while an object
+associated with a USNO object may not be transient, an object which is
+variable may also not be associated with a USNO object.  Thus, we need
+seperate bits for variable vs transient.  Different types of variable
+objects may be represented with different values of the lower byte.
+For example, a Cepheid may have a specific lower byte code of 0x01,
+which an RR Lyrae may have a lower byte code of 0x02.  Thus a Cepheid
+associated with the USNO catalog would have a total code value of
+0x6001. Finally, any of these types of non-moving objects may have
+some bad individual measurements (bit 12) or may be found to have a
+significant proper motion (bit 11).  It should be noted that the above
+guidelines for variable stars are suggestions only, and to date (July
+7, 1999), no programs currently assign or define these variability flags. 
+
+\section{Holding it Together}
+
+The preceeding sections describe the step-by-step analysis and
+incorporation of the data from individual images.  However, the power
+of the Pipeline comes in applying it to large numbers of images.  At
+LONEOS, we are using four Pentinum II computers to analyse the roughly 200
+images that are observed each night.  
+
+It is best to think of the analysis of images occurring on groups of
+images from a given night.  Of the set of processes discussed above,
+those in section \ref{parallel} are performed on individual images
+essentially independently of the other images.  The rest of the steps,
+both the data incorporation ({\pr addstar}) and the database cleaning
+routines occur on the entire batch, one image at a time.  Thus, the
+former routines can easily be run in parallel, but the latter routines
+are run in serial to avoid having two programs writing to same part of
+the database at the same time.  In this model, we would process all of
+the images from a night through the parallel stage of the analysis and
+then run the serial stage only after the entire night is finished in
+the parallel stage.  We thus have a natural division of the pipeline
+into two stages.  
+
+The implementation of the two stages involves several programs, some
+of which are specific to the LONEOS situation, and others which are
+more general.  There are two layers of programs which enable batch
+processing of many images and repeated batch processing over many
+nights.  At the highest level, are Perl scripts which choose what data
+needs to be analysed and when.  These scripts tend to be more
+dependent on the details of the LONEOS implementation. At the next
+level down, these scripts principally call two C programs which run
+the images of a given night through the pipeline.  These C programs
+are more general and do not depend on the details of the LONEOS
+implementation.  A defined set of files holds a log of the nights and
+images that have been processed, both for the information of people
+who might want to monitor the progress, but also used by the programs
+to guide decisions about what to analyse next.
+
+\subsection{Data and File Organization}
+
+There are a set of standard locations for all of the files used and
+created by Ptolemy.  These locations have both abstract names used by
+the pipeline programs and specific definitions used in the LONEOS
+implementation.  In general, the definitions of the files are given in
+the configuration file, 'config.txt' (currently located in a
+hard-wired location, but this may soon change to the mechanism used by
+the lastest version of {\pr spicam} (3.0)).  The C programs all refer
+to the config file for the definitions, but the Perl scripts currently
+have the definitions hardwired as variable names defined in the
+beginning of each file.  This should be changed to a mechanism that
+looks in the config file, perhaps implemented by a standard Perl
+function.
+
+Here is a list of the main directories and their meanings:
+
+{\bf logdir: {\ff /metis/d11/logs} - all log files and the files used
+by the Perl scripts to control their actions (except locks) go here.
+
+{\bf workspace}: {\ff /irene/d11/workspace} - the intermediate
+analysis stages go here, and the tarred, gzipped directory files stay
+here.
+
+{\bf dumpspace}: {\ff /pallas/d1} - used by slurp to store images
+downloaded from the tape archive.
+
+{\bf database}: {\ff /hebe/d27/database} - the result database, and
+also the locks and the {\ff Rocks.dat} file.
+
+{\bf references}: {\ff /metis/d11/references} - a variety of reference
+data, including the HST GSC, the USNO catalog, the config files, and
+so forth.
+
+{\bf source}: {\ff /metis/d11/src/ohana} - all of the programs source,
+binaries, and scripts relevant to running the Pipeline are contained
+in these directories.
+
+\subsection{lastnight}
+
+The first step in the process is the Perl script {\pr lastnight},
+which is highly dependent on the specifics of the LONEOS setup.  It
+looks at the disks used by the observers to write the images from a
+given night.  Images are written to a directory called {\ff
+/pallas/d[1-6]/YYMMDD} where {\ff YYMMDD} is an abbreviation for the
+current night's date (ie, 990223).  Many files use this naming scheme,
+so we will refer to such a name as {\ff YYMMDD}.  All of the images
+are stored in this directory with names {\ff yyyyMMDDxxxxb.fits} where
+{\ff yyyy} is the full year, {\ff MM} is month, {\ff DD} is date, and
+{\ff xxxx} is a sequence number.  The 'b' refers to the 'b' CCD, and
+the '.fits' extension is required.  The script {\pr lastnight} is
+called by a cron job scheduled for 7am on {\ff hebe.lowell.edu} (note
+that the clock on hebe is set to PST, not local MST - fix this some
+day!  see also man crontab for details on setting up a cron job).  The
+script searches the disks {\ff /pallas/d[1-6]} for a directory of the
+right form (basically any directory beginning with a number).  By
+default, the program demands that the directory have the name derived
+from today's date, but with the flag {\pr -any}, it will accept any
+directory.  It then checks to see if this directory has been already
+been analysed (ie, it is included in the {\ff processed.log} file).
+If not, it lists all files in that directory with the right ending
+(\ff b.fits}) and places these names in a file, {\ff
+/metis/d11/logs/YYMMDD.inlist}.  It also writes the name of the
+directory (along with some other data) in a file {\ff extracted.log}.
+When it has successfully ended and identified some data, it calls the
+next script, {\pr stage1} and then quits.
+
+\subsection{slurp}
+
+An alternative to {\pr lastnight} is the Perl script {\pr slurp},
+which looks for data on the tape archive.  This script looks at the
+{\ff tape.log} file and compares it to the file {\ff processed.log}|.
+It tries to identify nights listed in the file {\ff tape.log} which
+have not been included in the {\ff processed.log}.  If it finds any,
+it tries to download as much of the data as will fit on the disk {\ff
+/pallas/d1}.  This script is called from within the {\pr stage1}
+script and is invoked if {\pr stage1} is run without any data already
+in the file {\ff extracted.log.
+
+A related pair program are the {\pr load} \& {\pr unload} C programs
+which are needed for loading and unloading the tape jukebox.  The
+first is called with, {\pr load N}, which takes tape number N and
+loads it into the tape drive.  The second version, called with {\pr
+unload N} takes the tape and returns it to its slot.  Note that the
+jukebox does not know by itself what number tape is in the drive, so
+{\pr unload} needs to be told the correct one.  If it is mis-led, it
+will complain and refuse to return the tape.  It is important to check
+on the other users of the tape drive to be sure it is not occupied.
+This is done with the {\pr llock}, {\pr lunlock} and {\pr llockstat}
+programs.  The first, {\pr llock} lets you lock a particular service,
+etc, while the last {\pr llockstat} lists the devices which have been
+allocated.
+
+\subsection{stage1}
+
+The Perl script {\pr stage1} takes the image list produced by {\pr
+lastnight} or {\pr slurp} ({\ff YYMMDD.inlist}) and passes the list to
+the controller program {\pr control1} which performs the parallel
+analysis.  The script has a few features to decide which files have
+been analysed through the pipeline and to what extent.  If {\pr
+stage1} is called, it should be able to figure out 1) if there is data
+ready to be analysed (ie, there is a new date reference in the file
+{\ff extracted.log}), 2) if the data has been partially analysed (ie,
+the date reference in {\ff extracted.log} has not been put in {\ff
+processed.log}, but there are entries in the file {\ff
+YYMMDD.outlist}), and 3) which data needs to be run through which part
+of the pipeline (ie, it decides which of the images in {\ff
+YYMMDD.outlist} have succeeded at which stages of the analysis, and
+starts them at the appropriate spot).  These features are most useful
+if the analysis crashes in the middle or needs to be restarted.  The
+{\pr stage1} script is meant to be run without any arguments and it
+will figure out what needs to be done.  The only possible argument is
+the -once flag.  This flag tells {\pr stage1} to analyse the data it
+finds and quit when it finishes, and not call another instance of
+itself.  Otherwise, when it is done, it will restart itself, and since
+there is no new data in {\ff extracted.log}, it will start a {\pr
+slurp} process to get data from the archive.  If there is no data in
+the archive either, this process will sleep for an hour and try again.
+After a few attempts (2 days?), {\pr stage1} will give up and quit
+without restarting itself.  The script writes a log file called {\ff
+\$scriptdir/stage1.NN.log} where NN is a number in sequence between
+all runs of {\pr stage1}.  A file {\ff stage1.count} keeps track of
+which number NN we are on.  When {\pr stage1} has finished the
+analysis of a night's data, it writes the date in {\ff processed.log}
+to let other processes know what has been analysed.  The bulk of {\pr
+stage1} is not dependent on details of the LONEOS setup, but it does
+expect the files to have the naming scheme described above.
+
+\subsection{stage2}
+The serial stage processes are run by the Perl scrip {\pr stage2}.
+Normally, this script is always running.  It waits for the name of a
+night to be added to the {\ff processed.log} file, presumably by {\pr
+stage1}.  Once it finds a new name in this file, it looks for the
+appropriate images in the file {\ff YYMMDD.addlist}.  It then submits
+the list to the second controlling program {\pr control2}, which runs
+the images through {\pr addstar}.  Images which are successfully run
+through addstar have their {\ff *bf.fits} (flattened image) and {\ff
+*.obj} (dophot output) files deleted.  When the entire night is done,
+the rest of the {\ff *.obj} and {\ff *bf.fits} images are deleted by
+{\pr stage2}, the directory with the {\ff *.cmp} and {\ff *.log} files
+is tared and gzipped into the file {\ff YYMMNN.tgz}.  The directory is
+then deleted.  Finally, {\pr stage2} calls the scripts {\pr
+cleaning.pl} and {\pr run.phot} which runs the cleanup programs ({\pr
+markstar - nrphot}) on the database.  After a successful run, {\pr
+stage2} will automatically respawn a new copy that waits until more
+data arrives.
+
+\subsection{control1 / control2}
+These two C-programs perform the tasks of farming the images out to
+the different machines for analysis.  The two programs are essentially
+identical, but {\pr control1} is allowed to use all machines and {\pr
+control2} can only use one ({\ff hebe.lowell.edu}, which has the database
+local to it).  These controller programs start off by executing remote
+shells (using {\pr ssh}) on a set of machines.  These shells are then used
+to execute the remote commands and are kept open the entire time the
+process runs.  
+
+Both control programs maintain a list of images in a set of queues.  A
+set of rules defines the order in which each image travels through the
+queues.  Each queue is associated with a single process (ie, {\pr
+flatten}, astrometry, etc).  Images which successfully run through a
+queue are sent on to the defined 'next' queue, while images which fail
+are instead sent to the defined 'fail' queue.  In most cases, the
+'fail' queue is a process which marks the image as bad and write the
+name in a file, and then puts the image in the 'done' queue.  However,
+it is possible for the 'fail' queue to be another attempt, such as in
+the case of astrometry.  In this example, images which fail the
+default astrometry are run through a second queue which tries again
+with the {\pr -loneos} flag set (this flag makes the assumption that
+the header information about the LONEOS region number for the image is
+correct, not the RA/DEC information).
+
+The {\pr ssh} connections on the remote machines are handled as child
+processes with communication through the {\ff stdin} and {\ff
+stdout/stderr} pipes.  Messages can be sent back and forth though
+these pipes, and messages which are received from a process associated
+with an image are written to a log file with the name {\ff
+imagename.log} (where imagename is the root name of the image).  The
+only required messages from the program are messages saying
+``SUCCESS'' or ``ERROR''.  The programs therefore all end with either
+of these words.  The programs are started within the controller as a
+command to the shell followed by an echo of ``PROCESS DONE''.  If the
+controller sees the message ``PROCESS DONE'' without either
+``SUCCESS'' or ``FAILURE'' (after waiting an appropriately long time),
+it assumes the program crashed.  Currently, no timeout is available
+for the programs.  The flat-field process is run in {\pr mana} and is
+a slightly special case.  We avoid re-loading the same flat many times
+by starting a {\pr mana} process once and running the flatten
+procedures as {\pr mana} function calls.  The images which are
+analysed (with an indicator of success or failure at each stage) are
+written to a file named {\ff YYMMDD.addlist} by the {\pr control1} and
+\ff YYMMDD.outlist} by {\pr control2}.
+
+\subsection{Lock files}
+
+A variety of locking mechanisms are used to avoid overwriting the
+database or running more than one version of the controlling programs.
+There is a Perl program, {\pr locks}, which allows the user to set or
+check the state of the different locks. All programs which write to
+the database check for the existence of a lock on the database and
+refuse to run if it exists.  The {\pr control1/2} programs and the
+{\pr stage1/2} programs check for their own lock files and only run if
+they don't exist.  Finally, the user can set the locks 'kill' or
+'halt' for either {\pr stage1} or {\pr stage2}.  The first tells the
+process to stop immediately, taking care of bookkeeping first.  The
+second tells the process to finish the current set of images but not
+start a new version.
+
+\subsection{Other Perl Scripts} 
+
+there are several other scripts which are either used to do some
+simple function or may be used by the maintainer to check on the
+status of the Ptolemy exectution.  The two programs {\pr checkoutlist}
+and {\pr checkaddlist} take a list of {\ff YYMMDD.outlist} or {\ff
+YYMMDD.addlist} files and list the number of images in the file and
+the number of successes at each analysis stage.  There programs {\pr
+cleaning.pl} and {\pr run.phot} deal with the cleanup programs and the
+running of {\pr nrphot}.  The program {\pr backup.stuff} takes the
+first 4 characters of the year/month file names and backs up to the
+jukebox tape all tar files for the given month.
+
+\section{Operational Issues and Subtleties}
+
+The current LONEOS system runs principally on four Intel machines
+under Linux.  The existing system has been generally quite stable over
+the past year of operation, but there are some subtleties.  One
+significant issue is the RAID disk attached to {\ff hebe.lowell.edu}.
+This device is a set of three 9 GB disks merged into one 27GB RAID
+disk.  The current problem with the /hebe/d27 RAID results from the
+poor way it was setup.  It was set up without much knowledge of the
+(at the time) rather rudimentary linux RAID system.  As a result, the
+implementation is such that the disk will not automatically be
+configured and mounted at boot time.  There is a script {\ff
+/root/mdstart} which lists the step to setup the RAID correctly.  It
+would be helpful if the {\ff rc.local} or {\ff rc.sysinit} files were
+fixed to start, fsck and mount the RAID automatically at boot.  The
+setup on {\ff metis} is substantially more mature and is correctly
+implemented.  It comes up when the machine boots.
+
+\section{Visualization -- Status}
+
+Visualization of the data in the photometry database can best be
+performed with the program {\pr status}.  This is a command-line
+driven program with a math and macro language which makes it easy to
+perform complex tasks.  
+
+First, a few notes about the user interface.  The interface has an
+interaction similar to {\pr tcsh}.  The arrows allow editing of
+previous commands.  You can also use emacs-like commands such as
+cntl-a to reach the beginning of the line and cntl-e to reach the end.
+There is command and file completion: if you type part of a command
+(as the first thing on a line) and then type tab, it will fill in as
+much as possible, until the word is not unique.  Typing tab twice at
+that point will list the possible endings.  For any but the first word
+on a line, the same thing will happen for the files in the current
+directory.  It is also possible to type just a fraction of a command,
+as long as it is unique.  An ambiguous command will list the possible
+alternatives.  For example:
+\begin{verbatim}
+status: c
+ambiguous command: c ( catalog cgrid clear create cursor )
+\end{verbatim}
+
+The shell is essentially an interpretive programming language.
+Variables are set as follows:
+\begin{verbatim}
+status: $fred = 10
+\end{verbatim}
+Any expression within curly brackets \{\} is assumed
+to be an arithmetical expression and is evaluated before the line is
+executed.  For example:
+\begin{verbatim}
+echo {$fred*dcos(45)}
+\end{verbatim}
+would give the response 7.07107.  There are math functions cos, sin,
+and tan, which operate on radian expressions, and also dcos, dsin,
+dtan, which operate on degree expressions.  There are also the
+equivalent inverse functions: eg., asin and dasin return radians and
+degrees, respectively.  The help section on Math defines all of the
+available math functions.  
+
+\subsection{Miscellaneous Commands}
+\begin{verbatim}
+!                         -- system call
+?                         -- list commands 
+??                        -- list variables 
+echo                      -- type this line 
+exec                      -- system call
+exit                      -- exit program 
+help                      -- get help on a function 
+output                    -- redirect output to file
+quit                      -- exit program 
+scan                      -- scan line from keyboard or file to variable 
+wait                      -- wait until return is typed
+which                     -- show command 
+\end{verbatim}
+Most of these are self-explanatory.  The command ?? prints the system
+variables.  The {\tt help} command will provide help on a single
+command or, without any arguments, will list all available help
+files (this includes general help not associated with a specific
+command).  
+
+\subsection{Shell Programing}
+\begin{verbatim}
+break                     -- escape from function 
+for                       -- loops 
+if                        -- logical cases 
+input                     -- read command lines from a file 
+macro                     -- deal with the macros 
+\end{verbatim}
+
+There are several options for programming in {\pr status}.  First, a
+file which contains a series of commands can be executed with {\tt
+  input (filename)}.  It is also possible to define macros which will
+behave much like regular commands.  A macro is defined by typing {\tt
+  macro name} or {\tt macro create name} followed by the commands.
+Arguments to the macro are assigned to the variables \$1 .. \$N and
+the number of arguments is given by \$0.  Macros may be defined in
+{\tt input} files, and in fact when {\tt status} is started, it loads
+the file {\tt \~/.statusrc} which may contain default macros.  Simple
+loops and if statements can be performed, and are quite useful for
+complex macros.  
+
+``If'' statements are similar in syntax to C if statements, but only
+the following logical operators are available: $>$, $<$, $=$, !, $|$,
+and \&.  Notice that (currently) there is no $>=$ or $<=$ symbol.  The
+operator ! means ``not equal to'', but cannot be used to negate a
+logical value.  The operators $|$ and \& have the meaning of ``or'' and
+``and'' respectively.  Math expresions in the if statement must be
+contained in curly braces, as elsewhere.  Variables with string values
+may use the logical $=$ operator to test if two strings are the same.
+``For'' loops are quite simplistic.  The form is:
+\begin{verbatim}
+for var first last delta
+ (commands)
+end
+\end{verbatim}
+The value of {\tt \$var} will start at the value {\tt first} and increment by
+{\tt delta} after each loop.  The loop will stop after {\tt \$var} is greater
+than {\tt stop}.  The value {\tt delta} is optional, with 1 assumed.
+The value of {\tt \$var} may be changed during the loop, and if set
+beyong the value of {\tt last} will end the loop early.  
+
+\subsection{Vector Plotting}
+
+\begin{verbatim}
+box                       -- draw a box on the plot
+clear                     -- erase plot
+create                    -- create a new vector
+cursor                    -- get coords from cursor
+grid                      -- plot cartesian grid
+hist                      -- create histogram from a vector
+labels                    -- define labels for plot
+limits                    -- define plot limits
+plot                      -- plot a pair of vectors
+print                     -- write vectors to file
+ps                        -- define labels for plot
+set                       -- vector math
+style                     -- set the style for graph plots
+vectors                   -- list vectors
+zplot                     -- plot scaled points 
+\end{verbatim}
+
+In addition to scalar variables, {\tt status} can manipulate and
+display 1-D vector variables.  Many of the commands which extract data
+from the photometry database place the data in vectors as well as
+plotting them.  A vector can also be created based on a number
+sequence with the command {\tt create name Nelements start delta}.
+The resulting vector has $Nelements$ entries, starting at a value of
+$start$ and running until $start + delta*Nelements$.  If $delta$ is
+0.0, all elements will have the value of $start$.  A histogram of a vector
+may be made with the command {\tt hist}, which creates a new vector
+containing the histogram of the first vector.  The data range and bin
+size of the histogram are defined in same way as with create.  This
+makes it easy to create the index vector that goes with a histogram
+vector:  
+\begin{verbatim}
+hist y Ny 1 100 0.1
+create dx 1 100 0.1
+\end{verbatim}
+The above will create a histogram of y in Ny and the index in dx.
+Plotting this with {\tt plot dx Ny} will show the histogram.
+
+Vector math is performed with a command of the form {\tt set new =
+  (expression)}.  The expression is some math function employing
+vectors and scalars.  A complete listing of the math operators
+available in {\tt set} can be found in the help for {\tt set}.
+
+Once vectors are defined, they may be plotted.  A pair of vectors can
+be plotted against each other if they have the same number of entries.
+The plotting is performed on the graphics window, Kapa.  There are
+actually several graphics windows available to {\tt status}, any of
+which may be used to plot at any time.  Some of the more complex
+operations default to either graphics window 0 or 1, depending on the
+context.  Except for those functions with a pre-defined window, all
+plotting functions apply to the current graphics window unless an
+option {\tt -n N} is given to specify a different window.  The
+plotting style is determined by the command {\tt style} which can set
+the line width, the line type (solid, dashed, dotted, etc), the point
+type (box, cross, etc), the point size, the color, and whether a pair
+of vectors is plotted as a sequence of points, a set of connected
+lines, or a histogram.  Some functions which make plots use their own
+styles, as discussed below.  The function {\tt limits} lets the user
+set the range of the plot axes, or check the current setting.  The
+command {\tt plot} will plot a pair of vectors on the current graphics
+window using the current plotting style for that window.  The command
+{\tt zplot} will plot a pair of vectors with the point size scaled by
+a third vector, with maximum and minimum point sizes representing
+specified values.  The {\tt cursor} command goes the other way: this
+command puts the Kapa window in cursor mode and waits for input from
+Kapa.  The user can then type any alphanumeric key on the graphics
+windows and will be told both the pointer location (in the graphics
+coordinates) and will have the coordinates stored in {\tt status}
+variables.  For example, by typing ``1'' in the sky display window,
+the RA and DEC of the pointer are stored in the variables {\tt \$R1}
+and {\tt \$D1}.  This command can be used to let the user define
+locations or regions of interest on the Kapa window. (Future addition:
+{\tt button}, which does the same with the mouse buttons).  
+
+\subsection{Database Functions}
+
+\begin{verbatim}
+gcat                        -- get catalog at location
+gimages                     -- get images at location
+gstar                       -- get star statistics
+extract                     -- extract average vectors from catalogs
+mextract                    -- extract measurement vectors from catalogs
+imstats                    -- plot image statistics
+imextract                   -- extract image vectors from database
+lcat                        -- list catalogs in display region
+cmatch                      -- match two catalogs
+\end{verbatim}
+
+There are a variety of other commands which directly refer to the
+photometry database.  Some of these functions extract data of various
+types from the database, others perform more complex plotting
+operations.  The commands listed above are those which simply extract
+data from the database.  The first three list information relevant to
+a specific RA, DEC location on the sky: {\tt gcat (RA) (DEC)} lists
+the catalog at the specified location and places the name in the
+variable {\tt \$CATNAME}, {\tt gimages (RA) (DEC)} lists all images
+which overlap the specified location, {\tt gstars (RA) (DEC) (RADIUS)}
+lists data about the stars within a specified radius of the specified
+location (all numbers above are given in decimal degrees).  Similarly,
+{\tt lcat} lists the catalogs in the region.  Imstats lists statistics
+about each image
+
+The next three commands extract a specific piece of information from
+the photometry database and places it in a vector.  First, {\tt
+  extract} will extract average values for each star and place it in a
+vector.  Next, {\tt mextract} will extract measurement values for each
+star and place it in a vector: as a result a single star may have
+multiple entries in the measurement vectors.  Finally, {\tt imextract}
+will extract image statistics into vectors (not yet implemented).
+
+
+\begin{verbatim}
+catalog                    -- plot catalog stars
+cgrid                      -- plot sky coordinate grid
+cplot                      -- plot vectors in sky coordinates
+czplot                     -- plot scaled vectors in sky coordinates
+images                     -- plot image boxes
+imdense                    -- image density plot
+lcurve                     -- plot lightcurve for a star
+pcat                       -- plot catalog boundaries
+region                     -- define sky region for plot
+resid                      -- plot residuals
+simage                     -- plot stars in an image
+\end{verbatim}
+
+There are two types of database plotting functions: those that display
+or refer to the spatial charateristics of the data and those that
+refer to other types of charatersitics, such as the time domain.  The
+graphics window 0 is reserved for all plots of objects on the sky.
+The command {\tt region} defines the current sky coordinates for plots
+in graphic window 0.  The command {\tt pcat} plots the outline of all
+photometry database files which are within the currently defined
+region (and by default, only those with data).  {\tt images} plots the
+outline of the images in the image database, while {\tt imdense} shows
+the number of images at a location by randomly spacing dots within the
+boundary of the images.  The command {\tt cgrid}
+draws a grid in celestial coordinates on the for the current region.
+
+The most complex, but also one of the most useful command is {\tt
+  catalog}, which plots the positions of stars in the photometry
+database (and others) on the sky.  There are many options to this
+command.  One set allows the user to plot stars from the photometry
+database (the default), from the HST GSC, or from an ASCII text file
+with RA, DEC, and Mag in specified columns.  If the ASCII file has a
+fixed number of bytes per line, the data can be more quickly loaded.
+The size of the points may be scaled by the star magnitude, by the
+number of observations of the star, or by the number of missing
+datapoints for the star.  In addition, points may be plotted only if
+they land in specified magnitude ranges, or with specified numbers of
+measurements, or missed measurements.  Also, objects may be plotted
+only if they have a specified Average.code, so that only asteroids or
+only perfect stars may be plotted.  The plotted vectors may be saved,
+if desired, and the source catalog epoch may be specified as different
+from J2000 (only valid for ASCII data).
+
+Several other commands relate to non-spatial charateristics of images
+and stars.  {\tt lcurve} will plot a light curve for all stars within
+some radius of a point.  {\tt resid} plots the photometry residuals
+for a particular region file.  
+
+\section{Some Examples}
+
+\note{we need some better and more relevant examples}
+
+\begin{figure}
+\psfig{file=fullsky.ps,width=16cm}
+\caption{\label{allsky} \small
+  Map of the entire sky, and images added to database.  }
+\end{figure}
+
+Fig.~\ref{allsky} shows a map of the entire sky, and the location
+of the images currently in the database.  This picture was made with
+the following commands: (output is not shown) \\
+\begin{verbatim}
+status: region 0 0 90 gls
+status: cgrid
+status: style -lw 2 -c red 
+status: images
+status: ps
+\end{verbatim}
+In this example, on the graphics window, the image boxes are shown in
+red.  The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth.  
+
+\begin{figure}
+\psfig{file=polar.ps,width=16cm}
+\caption{\label{polar} \small
+  Map of the sky in polar project, and images added to database.  }
+\end{figure}
+
+Fig.~\ref{allsky} shows a map of the entire sky, and the location of
+the images currently in the database from a polar project.  This
+picture was made with the following commands: (output is not shown) \\ 
+\begin{verbatim}
+status: region 0 0 90 zea
+status: cgrid
+status: style -lw 2 -c red 
+status: images
+status: ps
+\end{verbatim}
+In this example, on the graphics window, the image boxes are shown in
+red.  The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth.  
+
+\begin{figure}
+\psfig{file=catalog.ps,width=9cm}
+\caption{\label{catalog} \small
+  Comparison between HST GSC and photometry database astrometry.  }
+\end{figure}
+
+Fig.~\ref{catalog} shows an example comparison of the photometry
+database star positions and the HST Guide Star Catalog star positions.
+The crosses are all objects in the photometry database, while the
+boxes are only the stars identified as USNO stars.  The circles are
+the stars from the HST GSC.  The size of both points is a function of
+brightness.  This plot was made with the following commands (starting
+from the previous image):
+\begin{verbatim}
+status: cursor  (typed 1 on region of interest)
+1 137.097858 22.698305
+q 137.097858 22.698305
+status: region $R1 $D1 0.2 TAN
+status: cgrid
+status: box
+status: style -pt 0 
+status: gcat $R1 $D1 
+  0 n2230/1951.cpt *
+status: style -pt 2; cat -all -m 12 18
+status: style -pt 1; cat -all -m 12 18 -ID $USNO
+status: style -pt 7; cat -all -m 12 18 -g
+\end{verbatim}
+
+\section{Software Distribution}
+
+All of the code, binaries, and scripts for the Pipeline are stored in
+the ohana tree at {\ff /metis/d11/src/ohana}.  The ohana directory contains
+subdirs of bin, lib, include, src, config, and doc.  There is a
+Makefile at the top level which calls lower-level Makefiles to build
+the programs desired.  One of the important concepts in the ohana
+distribution is the design of the directory layout to enable binary
+support for multiple architectures.  The Makefiles use the environment
+variable ARCH to determine the appropriate architecture.  The binary
+and library files are stored in subdirectories of the form {\ff bin/\$ARCH}
+and {\ff lib/\$ARCH}.  It is therefore necessary that users of the ohana
+system architecture define the ARCH variable correctly.  It is also
+necessary to include in the user's PATH the directory
+{\ff /metis/d11/ohana/bin/\$ARCH}.  At LONEOS,
+with the solaris sparc station and the linux machines both in use,
+ARCH can take on the values of 'linux' and 'sol'.  The values 'sun4',
+'irix', and 'hp' have also been used at various sites.  A simple bit
+of csh script in the users .cshrc or equivalent can make this
+assignment transparently:
+
+\begin{verbatim}
+set sys=`uname -s` 
+switch ($sys)
+ case IRIX64:
+   setenv ARCH irix;
+   breaksw;
+ case SunOS:
+   set ver=`uname -r | awk '{print substr($1,1,1)}'`;
+   if ($ver == 5) then
+     setenv ARCH sol
+   else 
+     setenv ARCH sun4
+   endif
+   breaksw;
+ case Linux:
+   setenv ARCH linux;
+   breaksw;
+ default:
+   echo "unknown architecture";
+   setenv ARCH unknown;
+   breaksw;
+endsw
+\end{verbatim}
+
+The Makefiles use the ARCH variable not only for destination
+directories, but also for destination binary names.  All files of the
+form fred.c are compiled to fred.ARCH.o (instead of fred.o), 
+uninstalled libraries get the names libfred.ARCH.a (instead of
+libfred.a), while uninstalled programs are called fred.ARCH.  
+
+The perl scripts are kept in {\ff ohana/src/perl}, with links to the
+appropriate bin directories.  All programs are stored in directories
+of the form {\ff ohana/src/program}.  The exceptions are {\pr gastro}
+(stored in {\ff ohana/src/astro}), and {\pr control1/2} (stored in
+{\ff ohana/src/astro}).  Also, a variety of simple, user-level
+functions are available in {\ff ohana/src/misc}.
+
+Most of the ohana package of programs use several common libraries.
+These include the {\pr readline} library (except for {\pr dophot},
+this is the only part of ohana that is not written by Eugene Magnier),
+which is used for command-line interface systems.  Other important
+libraries are the {\pr fits} library for implementing the FITS
+specifications in a convenient C-friendly environment, and the {\pr
+ohana} library, consisting of basic string and other basic operations.
+
+\section {Comparison with USNO catalog}
+
+I have made several comparisons between the USNO catalog and results
+from running images from Loneos through the photometry pipeline.  In
+general, there is a good agreement between the astrometry of the two
+datasets.  There are specfic areas where the two disagree, however.
+First, as discussed above, the USNO star positions may be
+significantly different from the observed star positions simply
+because of proper motion.  The first picure in the ``Comparisons
+between Loneos + USNO'' pages shows several stars with significant
+proper motion.  These are currently being flagged in the photometry
+database as discussed.  Other differences evident in this first
+picture include the classification of bright stars and the splitting
+of faint and bright stars.  In the upper right of the image, two
+bright stars are visible, one of which is actually a double.  The USNO
+catalog does a good job of detecting both, but it also detects and
+includes as stars several diffraction spike points.  The HST GSC only
+detected a single point and did not split the star at all.  The Loneos
+data in our pipeline did not detect the star at all due to saturation.
+A star in the upper left corner shows the ability of {\pr dophot} in
+the Loneos pipeline to split double stars.  This star is, on close
+inspection, clearly a double.  The USNO catalog only detects one.  The
+Pipeline not only splits the two stars, but it also makes the correct
+cross-identification of the stars.  
+
+The second set of pictures shows the ability of the Loneos pipeline to
+deal with small galaxies.  There are two small galaxies in this field,
+both of which are broken into 4 or 5 objects by the USNO catalog, but
+maintained as single objects in the Loneos pipeline.  In another
+example (not shown) a bright star within the disk of a small galaxies
+was clearly split from the galaxy by the pipeline (and also by USNO).
+
+\appendix
+\section{Parameter file params.txt}
+\note{replace with up-to-date version from Lowell}
+\begin{verbatim}
+# Configuration file for Loneos analysis pipeline 
+
+# important files and directories
+IN_DIR                  /pallas
+OUT_DIR                 /irene/d11/workspace
+#
+CATDIR                  /hebe/d27/database
+IMAGE_CATALOG           /hebe/d27/database/Images.dat
+IMAGE_CATALOG_TEMPLATE  /hebe/d27/database/template.cat
+CATALOG_TEMPLATE        /hebe/d27/database/template.cat
+ROCK_CATALOG            /hebe/d27/database/Rocks.dat
+#
+GSCFILE                 /metis/d11/references/GSC/GSCregions.tbl
+GSC_DIR                 /metis/d11/references/GSC
+USNO_CDROM              /metis/d11/references/USNO
+DOPHOT_PARAMS           /metis/d11/references/config/default_parameters
+FLATTEN_SCRIPT          /metis/d11/references/config/flatten.pro
+LONEOS_REGIONS          /metis/d11/references/config/regions.map
+
+# parameters for "fstat"
+# instrumental mag zero point
+ZERO_PT                 24.5
+DOPHOT_CHAR_LINE        129
+DOPHOT_TYPE_FIELD       5
+DOPHOT_AP_FIELD         91
+DOPHOT_PSF_FIELD        52
+MIN_SN_FSTAT            6.0
+
+# parameters for "gastro"
+DEFAULT_RADIUS   32.0
+MINIMUM_RADIUS   1.5
+MIN_ERROR        0.6
+MIN_PRECISE      0.12
+CCD_PC1_1        1
+CCD_PC2_2        -1    # for loneos
+CCD_PC1_2        0
+CCD_PC2_1        0
+ASEC_PIX         2.82
+NFIELD           3.0
+MMIN             6.0
+ROT_ZERO         0
+dROT             1.0
+NROT             2
+POLAR_AXIS_RA    -90.0
+POLAR_AXIS_DEC   89.88
+RA_OFFSET        -90.0
+DEC_OFFSET        0.0
+
+# parameters for "addstar"
+# airmass extinction coefficient (in mag / airmass)
+NSIGMA                  3.0
+ALPHA                   0.03
+XOVERSCAN               60
+YOVERSCAN               50
+
+# parameters for "controller"
+NEW_IMAGES              images.dat
+USERNAME                gene
+PASSWORD                gene
+
+# here we list all available machines
+NMACHINES               6
+MACHINE0                metis
+MACHINE1                hebe
+MACHINE2                iris
+MACHINE3                irene
+#MACHINE4               juno
+MACHINE4                ceres
+MACHINE5                vesta
+
+# here we define which machines which are always free
+fMACHINE0               1
+fMACHINE1               1
+fMACHINE2               1
+fMACHINE3               1
+fMACHINE4               0
+fMACHINE5               0
+fMACHINE6               0
+
+LOCAL_MACHINE           1  # machine with catalog on local disk
+
+# parameters for "status"
+TIME_REFERENCE          90/01/01 00:00:00
+
+# parameters for "nrphot"
+# make this one small to boost change of getting an early region 
+TAU                     3.0             
+SCATTER_LIM             15.0
+MAG_LIM                 17.0
+IMAGE_SCATTER           0.075
+NIMAGE_SCATTER          1.5
+STAR_SCATTER            0.05
+
+### parameters for "markstar"
+SEARCH_RADIUS           360   # region for initial trail hunt, in arcsec 
+TRAIL_WIDTH             5.0   # expected trail width, in arcsec
+NANGLE_BINS             180   # Number of angular bins in 180 degrees
+NPTSINLINE              5     # minimum points needed for trail
+MIN_DENSITY             0.025 # minimum linear density in star/arcsec
+SPACE_SIGMA             10.0  # how much denser than average for trail?
+# parameters defining bright star exclusion regions
+BRIGHT_XTRAIL_WIDTH       5.0 # faintest sat. star
+BRIGHT_XTRAIL_MAG         9.5 # faintest sat. star
+BRIGHT_XTRAIL_SLOPE     -80.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_YTRAIL_WIDTH       5.0 # faintest sat. star
+BRIGHT_YTRAIL_MAG        11.0 # faintest sat. star
+BRIGHT_YTRAIL_SLOPE    -400.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+BRIGHT_HALO_MAG           8.5 # faintest sat. star
+BRIGHT_HALO_SLOPE       -28.0 # exclusion radius = BRIGHT_SLOPE * (mag - BRIGHT_MAG)
+# parameters which define the ghosts
+GHOST_MAG               7.5
+GHOST_RADIUS            200   # in arcsec
+OPTICAL_AXIS1           2154.0
+OPTICAL_AXIS2           2193.0
+
+# parameters for "addusno"
+USNO_RADIUS             5.0   # in arcsec
+USNO_PROPER             20.0   # in arcsec
+USNO_RED                1000  # code for USNO Red data
+USNO_BLUE               1001  # code for USNO Blue data
+
+# parameters for "markrock"
+ROCK_RADIUS             2.0   # in arcsec
+ROCK_MAX_RADIUS         20.0  # in arcsec
+ROCK_MAX_SPEED          0.1   # in arcsec / sec
+\end{verbatim}
+
+\section{Creating New Flats}
+
+The LONEOS implementation of the analysis pipeline has a limited
+ability to choose a flatfield image.  There is a mechanism in the
+stage1 script to select a flatfield on the basis of the photcode
+entry.  It works like this: there is a fixed directory for all
+flatfield files, currently {\ff /metis/d11/reference}.  This directory
+is written in the file {\ff flatten.pro}, and also in the script {\pr
+stage1}.  Each flatfield has a name like {\ff flatcodeN.fits}, where N
+is the relevant photcode.  The {\pr mana} macro '{\pr flatten}' in
+{\ff flatten.pro} loads the file {\ff \$flatdir/flatfield.fits} (OR
+SOMETHING LIKE THAT), which is a link to the correct flat.  The script
+{\pr stage1} has the job of deciding which is the correct photcode and
+setting the link appropriately.
+
+This then begs the question of how this flatfield file should be
+created and assigned a new photcode.  The flatfield is NOT made
+nightly from the entire set of data, as one might expect.  This is not
+done because of the problems of scattered light in the telescope.  The
+widefield of the LONEOS telescope makes it somewhat succeptible to
+light at relatively large incident angles.  As a result, a median
+image made from images of the night sky will have different amounts of
+scattered light depending on the overall sky brightness.  In our
+experience, the presence of light clouds and just a small moon (as
+little as a quarter) make the resulting median image a very poor
+representation of the chip response.  Our solution to this problem is
+to choose a good night, in which the sky is very dark, and make a
+flatfield from that collection of images.  As a result, there is too
+much decision-making involved to allow the pipeline to make this
+decision.  Instead, we have chosen to only infrequently make a new
+flat from the data, choosing those nights that we are confident will
+not introduce excess scattered light.  
+
+There are a set of scripts and programs to make the flatfield image.
+The process involves first removing the bias and dark from the image,
+by subtracting a dark image, then medianing the resulting images.
+\note{find these scripts...}  In fact, instead of medianing,
+medianfilter returns the average of the fraction of pixels with values
+between f1 and f2 percent of the total range of the pixel data.
+
+\section{Daily Monitoring of the Pipeline}
+
+On a daily basis, the pipeline generally runs by itself without any
+intervention.  There are a few checks that can be made to be sure the
+process is running OK.
+
+First, it is good to see if the disk space is sufficient.  There is a
+component to stage1 which will keep it from running the analysis if
+there is less space on the destination disk ({\ff /irene/d11/workspace})
+than 1.1 times the amount of space in the source directory
+({\ff /pallas/d?/YYMMDD}).
+
+Second, it is a good idea to see if the nightly cron job is checking
+for new data and if the data is getting analysed.  If data was created
+last night, there should be a set of files in the log directory ({\ff
+/metis/d11/logs}) of the form {\ff YYMMDD.inlist}, {\ff
+YYMMDD.addlist}, and {\ff YYMMDD.outlist}.  If these files do not
+exist, it may just mean that no data was created last night.  In that
+case, the tail of the file {\ff /metis/d11/logs/lastnight} should say
+something like 'there is no new data to analyse'.  \note{It would be
+nice to include some notification scripts at the end of {\pr
+lastnight}, {\pr stage1} and {\pr stage2} which sent some mail
+describing what had been accomplished the previous night}.
+
+If data was created, and these files exist, it is a good idea then to
+check that the analysis of the images was reasonable and didn't fail
+on most of the images.  There are two Perl scripts to check on this.
+First, {\pr checkaddlist [filenames]} will take a list of {\ff
+*.addlist} files and, for each file, list the total number of images,
+then the number that failed for each of the four {\pr stage1}
+processes, {\pr flatten}, {\pr dophot}, {\pr fstat}, and {\pr gastro}.
+A typical run might look like this:
+
+\begin{verbatim}
+hebe: checkaddlist 9907??.addlist
+990701: 206 0 0 0 0
+990703: 199 5 0 0 0
+990705: 150 10 1 0 1
+\end{verbatim}
+
+It is actually unusual for any except the {\pr flatten} process to
+fail, and it might be typical for 1 or 2 of those to fail, since they
+will represent aborted images or images which failed to write
+everything to disk.  The second Perl script is {\pr checkoutlist}
+which works just the same with the {\ff YYMMDD.outlist} files, but
+only give statistics on the {\pr addstar} process.  Here, it is more
+common for several images to fail {\pr addstar}.  At the current time,
+{\pr gastro} does not always return a fail status if it finds a bogus
+solution (recent work in late July may have fixed all of those fail
+status messages).  Images which fail {\pr gastro} are mostly those
+which were taken when the sky was too bright and all that is seen is
+the sky itself, or the focus frames, or the very short images taken of
+bright stars to check the coords.  If the images fail {\pr gastro} but
+do not get rejected with an error status, they are rejected by {\pr
+addstar} on the basis of header keywords which will say 0 stars used
+astrometry.  It is also the case that {\pr gastro} should return a
+failure status if the astrometric solution is sufficiently far from
+Cartesian.  In general, the astrometry does not fail with a solution
+close to the correct solution.  I have found several examples of
+images with extremely wrong astrometric solutions, with very large
+non-Cartesian images on the sky.  There are also a number of failures
+near the pole with signficantly off astrometry, but close to an
+accurate solution.  The should now be rejected by {\pr gastro}.
+
+\section{Restarting the Pipeline}
+
+It is difficult to predict the future, so it is hard to give guidance
+on solving future problems.  Some of the things which might happen
+would include having one of the machines which serve a disk go down or
+having the disks fill up.  If this happens, it is possible that the
+run of a given night will have failed completely, or mostly.  There
+are a variety of things to do to recover from these situations.
+
+\subsection{Deleting data from the archive}
+
+The program {\pr delstar} lets the user delete a set of data from the
+database.  There are several options for this.  The first example is:
+{\pr delstar 199902040015b.cmp}.  In this form, delstar uses the
+astrometry information in the image header to find the measurements
+and delete them from the database, along with the image from the image
+database.  This form requires the datafile *.cmp to exist in the local
+directory.  The second form, {\pr delstar 911012323 1}, uses the given
+time in standard UNIX seconds form to identify the image in the image
+database and delete both the image and the measurements.  \note{does
+delstar expect a UNIX seconds date, or can it take any of the
+variations available in 'status'?}  It is easy to associate time with
+image with the status function {\pr findimages} or {\pr gimage} which
+list images covering a specific location.
+
+The third form of {\pr delstar} is used to clean up the database if an
+image is deleted but not all measurements from that image are removed.
+The routine {\pr addstar} uses the astrometric information for an
+image and the X,Y coordinates of each object in the image to locate
+the object in the database.  Several other routines do the reverse
+process of finding the location of objects within the images.  The
+function which performs this association needs to find all database
+*.cpt files which are covered by the image.  The existing
+implemenation is relatively quick, but not the most general possible.
+In some rare cases, parts of an image will not be found.  This is
+normally not an issue since few functions need to find all stars in a
+given image from scratch.  However, if such an image is deleted, some
+of the stars from that image may get left behind.  These orphaned
+stars can be deleted with the command {\pr delstar region.cpt -orphan
+STUFF??}.
+
+\subsection{Deleting a night from the pipeline}
+
+If the analysis for a specific night fails miserably, it is probably
+necessary to delete the entire night and then encourage the system to
+redo that night.  It is easy to delete an entire night with {\pr
+delstar} by giving a time interval which includes (only) the night in
+question.  It is also easy to find the value of the time in seconds
+with the {\pr status} function {\pr ctimes}.  (At some point, {\pr
+delstar} should incorporate the code from {\pr status} which does the
+time format conversion automatically).  This process will remove the
+night in question from the database.  But before the analysis can be
+re-run, Ptolemy needs to forget that it already ran that night before.
+Every night which has been analysed gets included in the log files
+{\ff processed.log} and {\pr addstared.log}.  The appropriate entry
+from these files needs to be deleted.  It is also necessary to delete
+the files {\ff YYMMDD.addlist} and {\ff YYMMDD.outlist} from the log
+directory.  If the {\ff extracted.log} file exists, it should be
+deleted as well.  All of this is predicated on the assumption that the
+user has halted all processing by Ptolemy before trying to fix
+anything.  At this point, it is possible to restart the analysis.
+This can be done in several possible ways.  If the night in question
+has already been put on tape, it has to be extracted.  The program
+{\pr slurp} will automatically find data which has not been extracted
+from the tape, download it, and set up the appropriate files.
+Unfortunately, {\pr slurp} uses the file {\ff tape.log} which is not
+automatically generated.  It is therefore necessary to add a line to
+{\ff tape.log} (in {\ff /metis/d11/logs}) for the night in question,
+which should look just like the entry in the directory {\ff
+pallas:/root/tape.log}.  Instead of actually running {\pr slurp},
+however, the user should just run {\pr stage1 -once} which will
+perform one run of {\pr stage1} and then end.  If there is no pending
+data (nothing in {\ff extracted.log} that has not been analysed), then
+{\pr stage1} will automatically call {\pr slurp} in the correct
+fashion (ie, on {\ff pallas}), run through the analysis, and then exit
+without restarting.
+
+If the night's data has not been deleted yet, then it is not necessary
+to run {\pr slurp} as the script {\pr lastnight} will do the job.  The
+cronscript for {\pr lastnight} uses the -today option so that the
+automatic analysis will not reattempt any nights still on pallas.
+Instead, it will be necessary for the user to run {\pr lastnight
+-any}, which will also take care of running {\pr stage1}. This should be
+safe at anytime, as the lock functions keep multiple instances of the
+programs from running, but it is better to do this with enough time to
+allow the process to finish before the next night's data is ready for
+processing.
+
+If the pipeline has been interrupted, it will probably be necessary to
+restart {\pr stage2} as well.  Since {\pr stage2} always restarts
+itself on exit, there is no cron job for it to make the initial start.
+(This probably should be introduced into the /etc/rc.local file, or
+some cron implementation for {\pr stage2} worked out).  Note that both
+{\pr stage1} and {\pr stage2} take no arguments except the optional
+{\pr -once} flag.  They make their own decisions about the correct log
+file number and so forth.
+
+If the {\pr lastnight} cron job gets lost, there is a copy of the
+necessary cron script {\ff
+/metis/d11/src/ohana/src/perl/lastnight.cron}.  This can be submitted
+with {\pr crontab lastnight.cron}.
+
+\section{Data backup}
+
+Backups are the still rather primitive in implementation for the
+LONEOS system.  There are two things which should be backed up, but
+only one currently has a good scheme.  The database itself does not
+have a backup process at the moment.  This is a bad situation, but not
+an easy one to solve.  The database is very large, over 30 GB, which
+is comparable to the space on a single AIC tape.  At the moment, we
+are satisfied with simply saving the intermediate results directories,
+all of the {\ff YYMMDD.tgz} files.  If the entire database is
+destroyed, these files can be used to rebuild the database in a
+relatively short time (currently, about a week).  The script {\pr
+backup.stuff} will backup a group of files.  A good method is to
+backup a month at a time.  These will become a single tar file on the
+AIC tape, and a month's worth of data is a reasonable amount of disk
+space for future downloads.
+
+\end{document}
+
+At LONEOS, the images are
+written to the disk from the camera by {\pr astrocam}, the camera
+controller user interface.  The resulting images are FITS files (which
+may be arbitrarily sized) and have header keyword parameters RA and
+DEC specifying rough celestial coordinates.  
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/schematic.fig
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/schematic.fig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/schematic.fig	(revision 22322)
@@ -0,0 +1,214 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+6 2400 375 3600 825
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 2400 375 3600 375 3600 825 2400 825 2400 375
+4 0 0 0 0 0 18 0.0000 4 150 975 2550 750 astrocam\001
+-6
+6 2400 2250 3600 2700
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 2400 2250 3600 2250 3600 2700 2400 2700 2400 2250
+4 0 0 0 0 0 18 0.0000 4 240 750 2550 2625 dophot\001
+-6
+6 2400 1125 3600 1875
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 3000 1500 600 375 3000 1500 3600 1875
+4 0 0 0 0 0 18 0.0000 4 180 540 2625 1575 *.fits\001
+-6
+6 2475 2925 3675 3675
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 3075 3300 600 375 3075 3300 3675 3675
+4 0 0 0 0 0 18 0.0000 4 240 540 2700 3375 *.obj\001
+-6
+6 2475 4650 3675 5400
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 3075 5025 600 375 3075 5025 3675 5400
+4 0 0 0 0 0 18 0.0000 4 240 660 2700 5100 *.cmp\001
+-6
+6 600 4800 1800 5250
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 600 4800 1800 4800 1800 5250 600 5250 600 4800
+4 0 0 0 0 0 18 0.0000 4 210 660 750 5175 gastro\001
+-6
+6 4425 4800 5625 5250
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 4425 4800 5625 4800 5625 5250 4425 5250 4425 4800
+4 0 0 0 0 0 18 0.0000 4 180 780 4575 5175 addstar\001
+-6
+6 8775 4425 9975 4875
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 8775 4425 9975 4425 9975 4875 8775 4875 8775 4425
+4 0 0 0 0 0 18 0.0000 4 150 615 8925 4800 status\001
+-6
+6 525 2850 1875 4050
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 1200 3450 675 600 1200 3450 1875 4050
+4 0 0 0 0 0 18 0.0000 4 210 975 750 3525 Astro dB\001
+4 0 0 0 0 0 18 0.0000 4 225 690 825 3825 (HST)\001
+-6
+6 8475 1725 10125 2925
+1 1 0 1 0 7 0 0 -1 4.000 1 0.0000 9300 2325 825 600 9300 2325 10125 2925
+4 0 0 0 0 0 18 0.0000 4 240 855 8850 2565 pictures\001
+4 0 0 0 0 0 18 0.0000 4 210 765 8850 2250 results,\001
+-6
+6 6300 1725 7950 2925
+1 1 0 1 0 7 0 0 -1 4.000 1 0.0000 7125 2325 825 600 7125 2325 7950 2925
+4 0 0 0 0 0 18 0.0000 4 180 480 6900 2250 alert\001
+-6
+6 4200 750 5850 1950
+1 1 0 1 0 7 0 0 -1 4.000 1 0.0000 5025 1350 825 600 5025 1350 5850 1950
+4 0 0 0 0 0 18 0.0000 4 210 885 4575 1275 postage \001
+4 0 0 0 0 0 18 0.0000 4 210 750 4575 1590 stamps\001
+-6
+6 750 1275 1725 1650
+2 2 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 5
+	 750 1275 1725 1275 1725 1650 750 1650 750 1275
+4 0 0 0 0 0 18 0.0000 4 180 690 825 1575 flatten\001
+-6
+6 2475 3900 3675 4350
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 2475 3900 3675 3900 3675 4350 2475 4350 2475 3900
+4 0 0 0 0 0 18 0.0000 4 180 465 2625 4275 fstat\001
+-6
+6 4425 6225 5625 6675
+6 4575 6375 5475 6600
+4 0 0 0 0 0 18 0.0000 4 180 900 4575 6600 addusno\001
+-6
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 4425 6225 5625 6225 5625 6675 4425 6675 4425 6225
+-6
+6 4425 5550 5625 6000
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 4425 5550 5625 5550 5625 6000 4425 6000 4425 5550
+4 0 0 0 0 0 18 0.0000 4 180 945 4575 5925 markstar\001
+-6
+6 4425 6900 5625 7350
+6 4425 6900 5625 7350
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 4425 6900 5625 6900 5625 7350 4425 7350 4425 6900
+-6
+4 0 0 0 0 0 18 0.0000 4 180 1035 4575 7275 markrock\001
+-6
+6 4425 7575 5625 8025
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 4425 7575 5625 7575 5625 8025 4425 8025 4425 7575
+4 0 0 0 0 0 18 0.0000 4 240 705 4575 7950 nrphot\001
+-6
+6 6225 4425 8025 7725
+6 6450 5925 7800 7125
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 7125 6525 675 600 7125 6525 7800 7125
+4 0 0 0 0 0 18 0.0000 4 210 810 6675 6600 Star dB\001
+-6
+6 6450 4650 7800 5850
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 7125 5250 675 600 7125 5250 7800 5850
+4 0 0 0 0 0 18 0.0000 4 240 1050 6675 5325 Image dB\001
+-6
+2 4 1 1 0 7 0 0 -1 4.000 0 0 7 0 0 5
+	 8025 7725 8025 4425 6225 4425 6225 7725 8025 7725
+4 0 0 0 0 0 18 0.0000 4 210 1395 6375 7575    Photom dB\001
+-6
+6 1500 5700 2850 6900
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 2175 6300 675 600 2175 6300 2850 6900
+4 0 0 0 0 0 18 0.0000 4 210 1110 1650 6375 USNO dB\001
+-6
+6 1500 7200 2850 8400
+1 1 0 1 0 7 0 0 -1 0.000 1 0.0000 2175 7800 675 600 2175 7800 2850 8400
+4 0 0 0 0 0 18 0.0000 4 210 945 1725 7875 Rock dB\001
+-6
+2 2 1 1 0 7 0 0 -1 4.000 0 0 -1 0 0 5
+	 2775 975 3150 975 3150 1050 2775 1050 2775 975
+2 2 1 1 0 7 0 0 -1 4.000 0 0 -1 0 0 5
+	 2775 2025 3225 2025 3225 2100 2775 2100 2775 2025
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 2
+	 3075 3675 3075 3900
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 3075 4350 3075 4650
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 3000 2700 3000 2925
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 3000 1875 3000 2250
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 3000 825 3000 1125
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 2475 5025 1800 5025
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 3675 5025 4425 5025
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 5625 5025 6225 5025
+2 1 3 1 0 7 0 0 -1 4.000 0 0 -1 0 1 3
+	0 0 1.00 60.00 120.00
+	 2775 975 1350 975 1350 1275
+2 1 3 1 0 7 0 0 -1 4.000 0 0 -1 0 1 4
+	0 0 1.00 60.00 120.00
+	 2775 2025 1425 2025 1350 2025 1350 1650
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 6450 3525 7650 3525 7650 3975 6450 3975 6450 3525
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 5625 5775 6225 5775
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 7050 3975 7050 4425
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 3600 4875 4575 3150
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 3600 1575 4575 2850
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5
+	 4575 2775 5775 2775 5775 3225 4575 3225 4575 2775
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 4950 3225 4950 4800
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 7050 3525 7050 2925
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 4950 2775 4950 1950
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 1 2
+	0 0 1.00 60.00 120.00
+	 8775 4650 8025 4650
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 1
+	 9375 4425
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 9300 4425 9300 2925
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 5625 6450 6225 6450
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 5625 7125 6225 7125
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 5625 7800 6225 7575
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 2850 7800 4425 7125
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 2850 6300 4425 6450
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 1200 4050 1200 4800
+4 0 0 0 0 0 18 0.0000 4 180 465 6900 2625 files\001
+4 0 0 0 0 0 18 0.0000 4 225 810 6675 3825 (filters)\001
+4 0 0 0 0 0 18 0.0000 4 180 675 4800 3075 imdiff\001
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/survey.fig
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/survey.fig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/pipeline/survey.fig	(revision 22322)
@@ -0,0 +1,16 @@
+#FIG 3.2
+Portrait
+Center
+Inches
+Letter 
+100.00
+Single
+-2
+1200 2
+2 5 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 5
+	0 polar.ps
+	 975 823 9127 823 9127 9375 975 9375 975 823
+2 5 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 5
+	0 fullsky.ps
+	 975 8273 9127 8273 9127 12225 975 12225 975 8273
+4 0 0 0 0 0 36 0.0000 4 375 6030 2250 1125 LONEOS SURVEY MAPS\001
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/projection.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/projection.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/projection.txt	(revision 22322)
@@ -0,0 +1,51 @@
+
+/* 
+here is some C code to project from R,D to x,y (all in decimal
+degrees) in a SIN projection, with Ro, Do as projection center:
+*/
+
+# define DEG_RAD 57.295779513082322
+# define RAD_DEG  0.017453292519943
+
+project (double *x, double *y, double R, double D, double Ro, double Do) {
+
+  double sdp, cdp, salp, calp, sdel, cdel, stht, sphi, cphi;
+		 
+  sdp  = sin(RAD_DEG*Do);
+  cdp  = cos(RAD_DEG*Do);
+  salp = sin(RAD_DEG*(ra - Ro));
+  calp = cos(RAD_DEG*(ra - Ro));
+  sdel = sin(RAD_DEG*dec);
+  cdel = cos(RAD_DEG*dec);
+  
+  stht = sdel*sdp + cdel*cdp*calp;    /* sin(theta) */
+  sphi = cdel*salp;                   /* = cos(theta)*sin(phi) */
+  cphi = cdel*sdp*calp - sdel*cdp;    /* = cos(theta)*cos(phi) */
+  if (stht < 0) { return 0; /* projection from the wrong side of the sphere */ }
+  
+  X =  DEG_RAD * sphi;
+  Y = -DEG_RAD * cphi;
+  
+# if (0) 
+  
+  /* 
+     these lines allow for a rotation / distortion 2x2 matrix (pci_j),
+     a (two direction) plate-scale shift (cdelt1, cdelt2), 
+     and a reference center offset of Xo, Yo, if desired. 
+  */
+
+  tmp_d = 1.0 / (pc_1_1*pc_2_2 - pc_1_2*pc_2_1); 
+  *x = tmp_d * (pc_2_2*X - pc_1_2*Y) / cdelt1 + Xo;
+  *y = tmp_d * (pc_1_1*Y - pc_2_1*X) / cdelt2 + Yo;
+
+# else
+
+  *x = X;
+  *y = Y;
+  
+# endif
+
+
+  return (1);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/solaris.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/solaris.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/solaris.txt	(revision 22322)
@@ -0,0 +1,23 @@
+
+2007.02.12 
+
+I am trying to build ohana on a solaris box:
+
+panstarrs / SunOS panstarrs 5.8 Generic_117350-26 sun4u sparc SUNW,Sun-Fire-V240 Solaris
+
+I have encountered a few issues:
+
+* configure.tcsh
+** I have added /usr/local/lib and /usr/local/include to the library search path
+** configure.tcsh now skips directories it does not find
+** I now list explicitly all of the external include files required
+
+* includes: 
+** netinet/ip.h does not work (though it exists), we need to use:
+
+# include <sys/socket.h>
+# include <netinet/in.h>
+
+* linking:
+** the vscanf family of functions are missing from solaris. 
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/status.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/status.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/status.txt	(revision 22322)
@@ -0,0 +1,72 @@
+
+hi christian,
+
+The path you need depends on the machine type, either:
+
+/h/eugene/src/ohana/bin/linux  or  /h/eugene/src/ohana/bin/sol
+
+(the linux version is usually a little more up to date, but both
+should work just fine for you). 
+
+you also need to copy the following files to you home directory:
+
+ ~dougados/.ptolemyrc
+ ~eugene/.statusrc
+
+The configuration scheme is much too complex to go into here, but
+basically, the first defines the location of certain data, and in this
+case, by choosing Catherine's .ptolemyrc file, you'll be looking at
+the Taurus data by default, instead of the archive analysis I've been
+running.  
+
+The second file defines various macros for you, in particular
+'ecliptic' and 'galactic', which draw the ecliptic and galactic
+planes.  
+
+start the program by running 'status'.  It is command-line driven, and
+has some limited help files.  type '?' to see a list of commands and
+'help' to get a list of help files.  
+
+I think most things you'd want to do, you can do.  In some cases the
+syntax can be a little cumbersome.  To get you started, here is a
+macro to generate a box from (10,20) to (15,25), with an offset.
+this could be more easily done by loading the reference coordinates
+from a file with the 'data (filename)' and 'read' commands.  I'll try
+to help you figure things out when you have had a chance to play with
+it a bit.
+
+gene
+
+the syntax for somr
+macro skybox
+ create ra 0 2
+ set ra = ra * 0
+ set dec = ra * 0
+ concat {10+$1} ra 
+ concat {15+$1} ra 
+	      
+ concat {15+$1} ra 
+ concat {15+$1} ra 
+	      
+ concat {15+$1} ra 
+ concat {10+$1} ra 
+	      
+ concat {10+$1} ra 
+ concat {10+$1} ra 
+
+ concat {20+$2}  dec
+ concat {20+$2}  dec
+       	       
+ concat {20+$2}  dec
+ concat {25+$2}  dec
+       	       
+ concat {25+$2}  dec
+ concat {25+$2}  dec
+       	       
+ concat {25+$2}  dec
+ concat {20+$2}  dec
+
+ style -x 2 -c red -lw 3 -pt 100
+ cplot ra dec
+end
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/systest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/systest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/systest.c	(revision 22322)
@@ -0,0 +1,98 @@
+# include <stdio.h>
+
+main (argc, argv) 
+int argc;
+char **argv;
+{
+
+  int i;
+  FILE *f;
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: %s testfile \n", argv[0]);
+    exit (0);
+  }
+
+  fprintf (stderr, "char:   %d bytes\n", sizeof (char));
+  fprintf (stderr, "short:  %d bytes\n", sizeof (short));
+  fprintf (stderr, "int:    %d bytes\n", sizeof (int));
+  fprintf (stderr, "long:   %d bytes\n", sizeof (long)); 
+  fprintf (stderr, "float:  %d bytes\n", sizeof (float));
+  fprintf (stderr, "double: %d bytes\n", sizeof (double));
+
+  f = fopen (argv[1], "w");
+  
+# define TYPE char
+  { 
+    TYPE I[20];
+    
+    I[0] = 1;
+    for (i = 1; i < 20; i++) {
+      I[i] = I[i-1] * 2;
+    }
+    fprintf (f, "20 of type TYPE, %d bytes each\n", sizeof(TYPE));
+    fwrite (I, sizeof(TYPE), 20, f);
+  }
+
+# define TYPE short
+  { 
+    TYPE I[20];
+    
+    I[0] = 1;
+    for (i = 1; i < 20; i++) {
+      I[i] = I[i-1] * 2;
+    }
+    fprintf (f, "20 of type TYPE, %d bytes each\n", sizeof(TYPE));
+    fwrite (I, sizeof(TYPE), 20, f);
+  }
+
+# define TYPE int
+  { 
+    TYPE I[20];
+    
+    I[0] = 1;
+    for (i = 1; i < 20; i++) {
+      I[i] = I[i-1] * 2;
+    }
+    fprintf (f, "20 of type TYPE, %d bytes each\n", sizeof(TYPE));
+    fwrite (I, sizeof(TYPE), 20, f);
+  }
+
+# define TYPE long
+  { 
+    TYPE I[20];
+    
+    I[0] = 1;
+    for (i = 1; i < 20; i++) {
+      I[i] = I[i-1] * 2;
+    }
+    fprintf (f, "20 of type TYPE, %d bytes each\n", sizeof(TYPE));
+    fwrite (I, sizeof(TYPE), 20, f);
+  }
+
+# define TYPE float
+  { 
+    TYPE I[20];
+    
+    I[0] = 1;
+    for (i = 1; i < 20; i++) {
+      I[i] = I[i-1] * 2;
+    }
+    fprintf (f, "20 of type TYPE, %d bytes each\n", sizeof(TYPE));
+    fwrite (I, sizeof(TYPE), 20, f);
+  }
+
+# define TYPE double
+  { 
+    TYPE I[20];
+    
+    I[0] = 1;
+    for (i = 1; i < 20; i++) {
+      I[i] = I[i-1] * 2;
+    }
+    fprintf (f, "20 of type TYPE, %d bytes each\n", sizeof(TYPE));
+    fwrite (I, sizeof(TYPE), 20, f);
+  }
+
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/thread.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/thread.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/thread.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include <stdio.h>
+# include <pthread.h>
+
+static int loop1, loop2;
+
+void *subloop (void *arg) {
+  
+  char *input;
+  
+  input = (char *) arg;
+  fprintf (stderr, "starting thread %s\n", input);
+
+  while (loop1) {
+    fprintf (stderr, "loop2: %d\n", loop2);
+    usleep (300000);
+  }
+  pthread_exit (0);
+}
+
+main () {
+
+  int var;
+  pthread_t thread1;
+  pthread_attr_t thread_attr;
+
+  loop1 = 1;
+  loop2 = 10;
+  pthread_create (&thread1, NULL, subloop, "test");
+
+  while (fscanf (stdin, "%d", &var) != EOF) {
+    loop2 = var;
+    if (loop2 == 0) loop1 = 0;
+  }
+}
+
+// a comment
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/todo.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/todo.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/todo.txt	(revision 22322)
@@ -0,0 +1,32 @@
+
+- KAPA & KII
+  - library functions to access KAPA / KII interfaces
+    - mana uses a text command interface, do the same?
+    - make a text I/F function, and supply a set of basic wrapped functions
+    - see plotstuff.c in various locations
+
+- DVO Images
+  - define a standard I/F to get at the images (dvo-images.c)
+  - include an iterator which loops over blocks?
+  - include the opihi/dvo/LoadImages functions which loads the 
+    images into a static global?
+  - allow for the intenal/external conversions:
+    we need to have internal floats for Mags (just like RA,DEC)
+  - should we keep the 2D Mcal fit structures?
+  - the skyprobe image tables place the 'sky' somewhere odd (Myyyy)
+    - make a conversion to limit the number of formats?
+
+- DVO Catalog tools
+  - unify the USNO, HST-GSC, 2MASS, etc lookup functions
+  - place under libohana
+  - split DVO functions from libohana?
+  - rename the functions for accessing the average/measure tables?
+  - allow for different spatial granularity of average/measure tables?
+
+- Docs
+  - go though */doc directories and cleanup notes, move into doc/www
+  - need more extensive descriptions!
+
+- gastro2
+  - final cleanups and some further testing
+  - translate to psLib & add to IPP!
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/wcs.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/wcs.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/wcs.txt	(revision 22322)
@@ -0,0 +1,54 @@
+
+DETSEC - It looks like it is defining the real pixel coordinates of
+each chip relative to the mosaic, including the gaps.  This means it
+is not affected by binning.  For now, let's leave it off from
+subrastering until we can calculate the correct coornates in that
+case.
+
+CRPIX1,2 - these need to be adjusted to the effective coordinate of
+the same pixel in the new coordinate frame:
+
+if you read a subraster (x1 - x2, y1 - y2) [unbinned pixels] with
+binning dx, 
+
+CRPIX1 = (CRPIX1' - x1) / dx
+CRPIX2 = (CRPIX2' - y1) / dy
+
+CD1_1, CD1_2, CD2_1, CD2_2 just need to be rescaled:
+
+CD1_1' = CD1_1 / dx
+CD2_1' = CD2_1 / dx
+
+CD1_2' = CD1_2 / dy
+CD2_2' = CD2_2 / dy
+
+GAIN stays fixed (we are providing the sum of the binned pixels, not
+the average). 
+
+what about the terms PIXSIZEn, PIXSCALn (I don't know whether these
+refer to physical pixels or image pixels.  in the former case, they
+stay constant, in the later case, they get scaled by dx, dy).
+
+As for the issue of the number of valid pixels...  Well, I can see
+arguments both ways.  Perhaps we *should* stick with the old
+definition of the valid data area.  In that case, make it:
+
+X1 = 6
+X2 = (Nx + 1) / dx
+Y1 = 5
+Y2 = (Ny + 4) / dy
+
+for DATASEC, which seems to match what we use now.
+
+you are right about the BIASSEC, we want the vertical region, not the corner.
+
+X1 = Nx / dx + 2
+X2 = Nx / dx + Sx
+Y1 = 5
+Y2 = (Ny + 4) / dy
+
+(currently we don't use the full Y-direction overscan -- BIASSEC is
+[6:2049,5:4100]).
+
+gene
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+Configure
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/Configure.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/Configure.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/Configure.in	(revision 22322)
@@ -0,0 +1,8 @@
+# place the target location here; last value is used
+# cfht values:
+ROOT = /Instruments/Elixir
+WWW  = /h/www/cfhti/Instruments/Elixir
+
+# kawelu values:
+ROOT = /eugene/idx
+WWW  = /var/www/kiawe/eugene/idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/Makefile	(revision 22322)
@@ -0,0 +1,15 @@
+include Configure
+
+all: idx install
+
+idx: 
+	mkidx html
+
+install:
+	rsync -auvz --delete --delete-excluded --exclude=CVS/ --exclude="*.htm" --exclude="*.idx" html/ $(WWW)
+
+clean:	
+	rm -f `find . -name "index.idx"`
+	rm -f `find . -name "*.html"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+*.html
+index.idx
+download
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/DVO-shell.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/DVO-shell.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/DVO-shell.htm	(revision 22322)
@@ -0,0 +1,175 @@
+<meta name=file  content=dvo>
+<meta name=title content=DVO Examples>
+<meta name=page  content=DVO Examples>
+
+<h3> Database Functions </h3>
+
+<pre>
+gcat                        -- get catalog at location
+gimages                     -- get images at location
+gstar                       -- get star statistics
+extract                     -- extract average vectors from catalogs
+mextract                    -- extract measurement vectors from catalogs
+imstats                    -- plot image statistics
+imextract                   -- extract image vectors from database
+lcat                        -- list catalogs in display region
+cmatch                      -- match two catalogs
+</pre>
+
+<p>
+There are a variety of other commands which directly refer to the
+photometry database.  Some of these functions extract data of various
+types from the database, others perform more complex plotting
+operations.  The commands listed above are those which simply extract
+data from the database.  The first three list information relevant to
+a specific RA, DEC location on the sky: <tt>gcat (RA) (DEC)</tt> lists
+the catalog at the specified location and places the name in the
+variable <tt>$CATNAME</tt>, <tt>gimages (RA) (DEC)</tt> lists all images
+which overlap the specified location, <tt>gstars (RA) (DEC) (RADIUS)</tt>
+lists data about the stars within a specified radius of the specified
+location (all numbers above are given in decimal degrees).  Similarly,
+<tt>lcat</tt> lists the catalogs in the region.  Imstats lists statistics
+about each image
+
+<p>
+The next three commands extract a specific piece of information from
+the photometry database and places it in a vector.  First,
+<tt>extract</tt> will extract average values for each star and place
+it in a vector.  Next, <tt>mextract</tt> will extract measurement
+values for each star and place it in a vector: as a result a single
+star may have multiple entries in the measurement vectors.  Finally,
+<tt>imextract</tt> will extract image statistics into vectors (not yet
+implemented).
+
+
+<pre>
+catalog                    -- plot catalog stars
+cgrid                      -- plot sky coordinate grid
+cplot                      -- plot vectors in sky coordinates
+czplot                     -- plot scaled vectors in sky coordinates
+images                     -- plot image boxes
+imdense                    -- image density plot
+lcurve                     -- plot lightcurve for a star
+pcat                       -- plot catalog boundaries
+region                     -- define sky region for plot
+resid                      -- plot residuals
+simage                     -- plot stars in an image
+</pre>
+
+<p>
+There are two types of database plotting functions: those that display
+or refer to the spatial charateristics of the data and those that
+refer to other types of charatersitics, such as the time domain.  The
+graphics window 0 is reserved for all plots of objects on the sky.
+The command <tt>region</tt> defines the current sky coordinates for plots
+in graphic window 0.  The command <tt>pcat</tt> plots the outline of all
+photometry database files which are within the currently defined
+region (and by default, only those with data).  <tt>images</tt> plots the
+outline of the images in the image database, while <tt>imdense</tt> shows
+the number of images at a location by randomly spacing dots within the
+boundary of the images.  The command <tt>cgrid</tt>
+draws a grid in celestial coordinates on the for the current region.
+
+<p>
+The most complex, but also one of the most useful command is
+<tt>catalog</tt>, which plots the positions of stars in the photometry
+database (and others) on the sky.  There are many options to this
+command.  One set allows the user to plot stars from the photometry
+database (the default), from the HST GSC, or from an ASCII text file
+with RA, DEC, and Mag in specified columns.  If the ASCII file has a
+fixed number of bytes per line, the data can be more quickly loaded.
+The size of the points may be scaled by the star magnitude, by the
+number of observations of the star, or by the number of missing
+datapoints for the star.  In addition, points may be plotted only if
+they land in specified magnitude ranges, or with specified numbers of
+measurements, or missed measurements.  Also, objects may be plotted
+only if they have a specified Average.code, so that only asteroids or
+only perfect stars may be plotted.  The plotted vectors may be saved,
+if desired, and the source catalog epoch may be specified as different
+from J2000 (only valid for ASCII data).
+
+<p>
+Several other commands relate to non-spatial charateristics of images
+and stars.  <tt>lcurve</tt> will plot a light curve for all stars within
+some radius of a point.  <tt>resid</tt> plots the photometry residuals
+for a particular region file.  
+
+<h3> Some Examples </h3>
+
+\begin{figure}
+%\psfig{file=fullsky.ps,width=16cm}
+\caption{\label{allsky} \small
+  Map of the entire sky, and images added to database.  }
+\end{figure}
+
+<p>
+Fig.~\ref{allsky} shows a map of the entire sky, and the location
+of the images currently in the database.  This picture was made with
+the following commands: (output is not shown) 
+
+<pre>
+dvo: region 0 0 90 gls
+dvo: cgrid
+dvo: style -lw 2 -c red 
+dvo: images
+dvo: ps
+</pre>
+
+<p>
+In this example, on the graphics window, the image boxes are shown in
+red.  The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth.  
+
+\begin{figure}
+%\psfig{file=polar.ps,width=16cm}
+\caption{\label{polar} \small
+  Map of the sky in polar project, and images added to database.  }
+\end{figure}
+
+<p>
+Fig.~\ref{allsky} shows a map of the entire sky, and the location of
+the images currently in the database from a polar project.  This
+picture was made with the following commands: (output is not shown)
+
+<pre>
+dvo: region 0 0 90 zea
+dvo: cgrid
+dvo: style -lw 2 -c red 
+dvo: images
+dvo: ps
+</pre>
+
+<p>
+In this example, on the graphics window, the image boxes are shown in
+red.  The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth.  
+
+\begin{figure}
+%\psfig{file=catalog.ps,width=9cm}
+\caption{\label{catalog} \small
+  Comparison between HST GSC and photometry database astrometry.  }
+\end{figure}
+
+<p>
+Fig.~\ref{catalog} shows an example comparison of the photometry
+database star positions and the HST Guide Star Catalog star positions.
+The crosses are all objects in the photometry database, while the
+boxes are only the stars identified as USNO stars.  The circles are
+the stars from the HST GSC.  The size of both points is a function of
+brightness.  This plot was made with the following commands (starting
+from the previous image):
+
+<pre>
+dvo: cursor  (typed 1 on region of interest)
+1 137.097858 22.698305
+q 137.097858 22.698305
+dvo: region $R1 $D1 0.2 TAN
+dvo: cgrid
+dvo: box
+dvo: style -pt 0 
+dvo: gcat $R1 $D1 
+  0 n2230/1951.cpt *
+dvo: style -pt 2; cat -all -m 12 18
+dvo: style -pt 1; cat -all -m 12 18 -ID $USNO
+dvo: style -pt 7; cat -all -m 12 18 -g
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/addstar.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/addstar.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/addstar.htm	(revision 22322)
@@ -0,0 +1,208 @@
+<meta name=file  content=addstar>
+<meta name=title content=addstar>
+<meta name=page  content=addstar>
+
+<p>
+This program takes a photometry file, with astrometry, provided by
+earlier routines and incorporates the stellar measurements into the
+photometry database.  This routine does NOT try to calibrate or check
+the photometry, nor does it try to improve the coordinate
+determinations or calculate chisquares for position or photometry -
+those tasks are performed by other routines.
+
+<p>
+Addstar starts by loading in the photometry file and converting the
+pixel coordinates to sky coordinates using the astrometry information
+in the header of the file.  It also gets the detection limits and
+stores the image astrometry information.
+
+<p>
+Next, addstar determines which of all the previous images overlaps
+with this image and also which of the catalog regions overlaps with
+this image.
+
+<p>
+Addstar considers each catalog region in turn, and compares the
+positions of stars in the catalog with stars in the image.  If a
+catalog star is detected, a new measurement is added to the catalog.
+If a catalog star is not detected, but is in the field of view of this
+image, the detection limit for this image is added to the list of
+measurements as an upper limit.  If the image contains a star which is
+not already included in the catalog, the star is added to the catalog,
+and all previous images are checked to see if they included this
+location.  If so, upper limits are added to the list of measurements.
+
+<p>
+To deal with blended images, we have taken the philosophy that all
+measurements compatible with the coordinates of a given star should be
+included in the list of measurements.  This assumes that programs or people
+downstream will figure out which is the "correct" measurement.  To
+this end, if a catalog star is consistent with multiple measurements,
+they are all added to the catalog measurement and the measurements are
+given a flag to say that there was blending in the catalog (ie, it is
+probably the catalog star which is a blend).  If multiple catalog
+stars are consistent with the same image star, that measurement is
+added to each catalog star, and given a flag to say that there was
+blending on the image.
+
+<p>
+After all catalogs have been checked, updated, and written to disk,
+the image is added to the database of images.
+
+<p>
+see <a href=database.html> DVO database </a> for discussion of the
+database storage format.   
+
+<hr>
+
+<h2> Usage </h2>
+
+<pre>
+ addstar (filename)
+ addstar -cat (catalog)
+ addstar -ref (filename)
+</pre>
+
+<p>
+In the first form, addstar loads the photometry information for a
+single image into the database.  The file must be in one of the Elixir
+cmp/smp/smf formats, and must have been astrometrized (eg, with
+gastro, gastro2, and/or mosastro).  The file must also be provided
+with a valid photcode in the header (keyword PHOTCODE) or the photcode
+must be supplied as an option to addstar (see below).  The photcode
+must be listed in the photcode table (see config variable
+PHOTCODE_FILE).  In the case that the image has been astrometrized
+with mosastro (two-level mosaic astrometry), the corresponding mosaic
+header unit must be supplied as an option (see below).  
+
+<p>
+In the second form, addstar will load photometry from the specified
+reference catalog, located in a known location, and will load it into
+the database.  Allowed catalog names are: <tt> USNO, GSC, 2MASS,
+2MASS-ALLSKY, 2MASS-DR </tt>.  Note that USNO corresponds to the
+USNO-A catalog, 2MASS corresponds to the 2MASS-ALLSKY catalog.  It is
+necessary to have defined an appropriate photcode for the given
+catalog.  The following photcodes are expected to exist:
+
+<table>
+<tr><th> catalog </th><th> allowed photcodes   	     </th></tr>
+<tr><td> GSC     </td><td> GSC_M               	     </th></tr>
+<tr><td> USNO    </td><td> USNO_RED, USNO_BLUE 	     </th></tr>
+<tr><td> 2MASS*  </td><td> 2MASS_J, 2MASS_H, 2MASS_K </th></tr>
+</table>
+
+When more than one photcode is allowed, one must be selected; there
+are no defaults in that case. <em>It is advised that addstar -cat be
+used with a restricted region of the sky; the default behavior will
+load the reference catalog for the entire sky into the DB!</em>
+
+<p>
+In the third form, addstar will load data from the specified ASCII
+text file with a fixed format.  The file must contain lines with one
+line per star.  The first four columns must contain the values RA,
+DEC, Magnitude, Magnitude error.  The RA and DEC must be in J2000
+decimal degrees.  Blank lines are allowed, and lines may be commented
+with the hash sign (#).  The provided photometry must all be for a
+single photcode, which must be provided as a command-line option.
+
+<h2> Additional Options </h2>
+
+<pre>
+  -region ra ra dec dec           : only add data in specified region (-ref mode only)
+  -p (photcode)                   : specify photcode (override header)
+  -time (YYYY/MM/DD,HH:MM:SS)     : specify date/time (override header)
+  -mosaic (filename)              : identify associated mosaic frame for chip image
+  -fits                           : input file is FITS table, not TEXT table
+  -existing-regions               : only add measurements to existing catalog files
+  -only-match                     : only add measurements to existing objects
+  -missed                         : skipped 'missed' entries
+  -replace                        : replace time/photcode measurements (no duplication)
+  -image                          : only insert image data
+  -cal                            : perform zero-point calibration
+  -skyprobe                       : specify skyprobe mode
+  -2massquality                   : define 2MASS quality flags to keep
+  -accept                         : accept bad astrometry from header
+  -force                          : force read of database with inconsistent info
+  -v                              : verbose mode
+  -dump (mode)                    : output test data
+  -help                           : print this list
+  -h                              : print this list
+</pre>
+
+
+<h2> Elixir Configuration Data </h2>
+
+addstar uses the following Elixir configuration variables:
+
+<ul>
+<li> EXPTIME-KEYWORD  : header keyword defining exposure time
+<li> DATE-KEYWORD  : header keyword defining the date 
+<li> DATE-MODE  : mode for header date information: use a combination
+of Ys, Ms, Ds to specify order.  If only 2 digits are specified for
+the year, numbers greater than 50 are assumed in the 1900s, less than
+in the 2000s.  eg YYYY/MM/DD
+<li> UT-KEYWORD  : header keyword defining UT
+<li> MJD-KEYWORD  : header keyword defining MJD
+<li> JD-KEYWORD  : header keyword defining JD
+<li> AIRMASS-KEYWORD
+<li> CCDNUM-KEYWORD
+<li> ST-KEYWORD
+
+<li> RADIUS : radius for matches; may have the value "header", in which
+case the astrometric error listed in the header, along with
+<tt>NSIGMA</tt> is used to define a match.  Otherwise, defines a
+radius in arcseconds.
+
+<li> NSIGMA : number of astrometric error sigma to use for match
+radius (see <tt>RADIUS</tt>).
+
+<li> XOVERSCAN : used to define valid pixels in the image. subtracted
+from image NAXIS1 value to define image box. 
+
+<li> YOVERSCAN : used to define valid pixels in the image. subtracted
+from image NAXIS2 value to define image box. 
+
+<li> ADDSTAR_XMIN : this value, and the following three, are used to
+define a region on the image where the photometry is considered
+reliable.  Stars outside this region are marked as having poor
+measurements.
+
+<li> ADDSTAR_XMAX : see <tt>ADDSTAR_XMIN</tt>
+<li> ADDSTAR_YMIN : see <tt>ADDSTAR_XMIN</tt>
+<li> ADDSTAR_YMAX : see <tt>ADDSTAR_XMIN</tt>
+
+<li> MIN_SN_FSTAT : only include objects in the DB with S/N greater
+than this value.
+
+<li> GSCDIR        : location of the HST Guide Star Catalog
+<li> USNO_CDROM    : location of the USNO-A data
+<li> 2MASS_DIR_AS  : location of the 2MASS allsky data 
+<li> 2MASS_DIR_DR2 : location of the 2MASS DR2 data 
+
+2MASS, USNO, and GSC are all loaded from their idiosyncratic formats.
+
+<li> GSCFILE  : location of the GSC layout file (for GSC and ptolemy)
+<li> CATDIR  : location of the ptolemy data
+<li> IMAGE_CATALOG : location of the image database table
+
+<li> CATMODE : storage mode for the database.  may be one of RAW,
+MEF, SPLIT, MYSQL.  SPLIT and MYSQL are currently unsupported.  This
+defines the output format for new tables; the input format is auto-detected.
+
+<li> CATFORMAT : data format for the database.  may be one of LONEOS,
+ELIXIR, PANSTARRS.
+
+<li> PHOTCODE_FILE : location of the photcode file describing the
+photometry system of interest.
+
+<li> OBSERVATORY-LATITUDE : hard-wired observatory latitude.  used for
+precision airmass for skyprobe.  <em> should be replaced with a more
+sophisticated approach</em>
+
+<li> SUBPIX_DATAFILE : table for subpixel structure corrections (skyprobe)
+
+<li> IMAGE_CATALOG_TEMPLATE : deprecated, was used to supply a FITS
+header template
+<li> CATALOG_TEMPLATE : deprecated, was used to supply a FITS
+header template
+</ul>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/commands.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/commands.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/commands.htm	(revision 22322)
@@ -0,0 +1,111 @@
+<meta name=file  content=dvo-cmds>
+<meta name=title content=DVO commands>
+<meta name=page  content=DVO commands>
+
+<table>
+ <tr><td> abszero        </td><td> find filter zeropts </td></tr>
+ <tr><td> applyfit       </td><td> apply fit to new vector </td></tr>
+ <tr><td> applyfit2d     </td><td> apply 2-d fit to new vector </td></tr>
+ <tr><td> badimages      </td><td> look for images with anomalous astrometry </td></tr>
+ <tr><td> box            </td><td> draw a box on the plot </td></tr>
+ <tr><td> buffers        </td><td> list the currently allocated buffers </td></tr>
+ <tr><td> calextract  	 </td><td> extract photometry calibration </td></tr>
+ <tr><td> calmextract 	 </td><td> extract photometry calibration </td></tr>
+ <tr><td> cals           </td><td> plot calibration data </td></tr>
+ <tr><td> catalog        </td><td> plot catalog stars </td></tr>
+ <tr><td> ccd         	 </td><td> plot color-color diagram </td></tr>
+ <tr><td> ccdextract     </td><td> extract star coords from color-color diagram </td></tr>
+ <tr><td> center      	 </td><td> center image on coords </td></tr>
+ <tr><td> cgrid          </td><td> plot sky coordinate grid </td></tr>
+ <tr><td> clear          </td><td> erase plot </td></tr>
+ <tr><td> cmatch         </td><td> match two catalogs </td></tr>
+ <tr><td> cmd            </td><td> plot cmd of stars in current region </td></tr>
+ <tr><td> cmdextract     </td><td> extract stars based on cmd regions </td></tr>
+ <tr><td> cmpread        </td><td> read data from cmp format files </td></tr>
+ <tr><td> concat         </td><td> reduce vector dimension </td></tr>
+ <tr><td> contour        </td><td> create contour from image </td></tr>
+ <tr><td> cplot          </td><td> plot vectors in sky coordinates </td></tr>
+ <tr><td> create         </td><td> create a new vector </td></tr>
+ <tr><td> csystem        </td><td> convert between coordinate systems </td></tr>
+ <tr><td> ctimes         </td><td> convert between time formats </td></tr>
+ <tr><td> cursor         </td><td> get coords from cursor </td></tr>
+ <tr><td> czplot         </td><td> plot scaled vectors in sky coordinates </td></tr>
+ <tr><td> datafile       </td><td> define file to read vectors </td></tr>
+ <tr><td> date        	 </td><td> get current date </td></tr>
+ <tr><td> ddmagextr      </td><td> plot magnitude differences </td></tr>
+ <tr><td> ddmags         </td><td> plot magnitude differences </td></tr>
+ <tr><td> delete         </td><td> delete vectors or matrices </td></tr>
+ <tr><td> device         </td><td> set / get current graphics device </td></tr>
+ <tr><td> dmagextract    </td><td> extract stars based on differential magnitudes between filters </td></tr>
+ <tr><td> dmags          </td><td> plot differential magnitudes between filters </td></tr>
+ <tr><td> dmt            </td><td> plot mag scatter </td></tr>
+ <tr><td> dumpmags       </td><td> custom dB dumping thingy </td></tr>
+ <tr><td> elixir         </td><td> get status info from elixir </td></tr>
+ <tr><td> extract        </td><td> extract vectors from catalogs </td></tr>
+ <tr><td> file           </td><td> test for a file </td></tr>
+ <tr><td> fit            </td><td> fit polynomial to vector pair </td></tr>
+ <tr><td> fit2d          </td><td> fit 2-d polynomial to vector triplet </td></tr>
+ <tr><td> gaussj      	 </td><td> solve Ax = B (N-D)\n </td></tr>
+ <tr><td> gcat           </td><td> get catalog at location </td></tr>
+ <tr><td> gimages        </td><td> get images at location </td></tr>
+ <tr><td> grid           </td><td> plot cartesian grid </td></tr>
+ <tr><td> gstar          </td><td> get star statistics </td></tr>
+ <tr><td> gtypes         </td><td> get type fractions </td></tr>
+ <tr><td> histogram      </td><td> generate histogram from vector </td></tr>
+ <tr><td> images         </td><td> plot image boxes </td></tr>
+ <tr><td> imbox          </td><td> plot expected image box </td></tr>
+ <tr><td> imdata         </td><td> extract data for specific images </td></tr>
+ <tr><td> imdense        </td><td> image density plot </td></tr>
+ <tr><td> imextract      </td><td> extract vectors from catalogs </td></tr>
+ <tr><td> imlist         </td><td> list image info </td></tr>
+ <tr><td> imphot         </td><td> image photometry info </td></tr>
+ <tr><td> imrough        </td><td> get info from imruf database </td></tr>
+ <tr><td> imsearch       </td><td> get info from imreg database </td></tr>
+ <tr><td> imstats        </td><td> plot image statistics </td></tr>
+ <tr><td> interpolate    </td><td> interpolate between vector pairs </td></tr>
+ <tr><td> jpeg           </td><td> write text line on graph </td></tr>
+ <tr><td> labels         </td><td> define labels for plot </td></tr>
+ <tr><td> lcat           </td><td> list catalogs in region </td></tr>
+ <tr><td> lcurve         </td><td> plot lightcurve for a star </td></tr>
+ <tr><td> limits         </td><td> define plot limits </td></tr>
+ <tr><td> mcreate     	 </td><td> create a matrix </td></tr>
+ <tr><td> mextract       </td><td> extract vectors from catalogs </td></tr>
+ <tr><td> mget        	 </td><td> extract a vector from a matrix </td></tr>
+ <tr><td> mset        	 </td><td> insert a vector in a matrix </td></tr>
+ <tr><td> pcat           </td><td> plot catalog boundaries </td></tr>
+ <tr><td> photcodes      </td><td> list photometry codes </td></tr>
+ <tr><td> photresid      </td><td> plot photometry residuals </td></tr>
+ <tr><td> plot           </td><td> plot a pair of vectors </td></tr>
+ <tr><td> pmeasure       </td><td> plot individual measurements </td></tr>
+ <tr><td> precess        </td><td> precess coordinates </td></tr>
+ <tr><td> print          </td><td> write vectors to file </td></tr>
+ <tr><td> procks         </td><td> plot rocks </td></tr>
+ <tr><td> ps             </td><td> define labels for plot </td></tr>
+ <tr><td> rd             </td><td> load fits image </td></tr>
+ <tr><td> read           </td><td> read vectors from datafile </td></tr>
+ <tr><td> region         </td><td> define sky region for plot </td></tr>
+ <tr><td> resid          </td><td> plot residuals </td></tr>
+ <tr><td> resize         </td><td> set graphics/image window size </td></tr>
+ <tr><td> section        </td><td> define section of graph </td></tr>
+ <tr><td> set            </td><td> vector math </td></tr>
+ <tr><td> simage         </td><td> plot stars in an image </td></tr>
+ <tr><td> sort           </td><td> sort list of vectors </td></tr>
+ <tr><td> sprintf     	 </td><td> formated print to variable </td></tr>
+ <tr><td> stats          </td><td> give statistics on a portion of a buffer </td></tr>
+ <tr><td> style          </td><td> set the style for graph plots </td></tr>
+ <tr><td> subpix      	 </td><td> get subpixel positions </td></tr>
+ <tr><td> subraster      </td><td> subraster of fits image </td></tr>
+ <tr><td> subset         </td><td> expand vector dimension </td></tr>
+ <tr><td> textline       </td><td> write text line on graph </td></tr>
+ <tr><td> tv         	 </td><td> display an image on the Kii window </td></tr>
+ <tr><td> uniq           </td><td> create a uniq vector subset from a vector </td></tr>
+ <tr><td> vclip       	 </td><td> clip a vector </td></tr>
+ <tr><td> vectobuf       </td><td> convert vector triplet to buffer </td></tr>
+ <tr><td> vectors        </td><td> list vectors </td></tr>
+ <tr><td> vstat          </td><td> get info from imreg database </td></tr>
+ <tr><td> wd         	 </td><td> write an image to a file </td></tr>
+ <tr><td> write          </td><td> write vectors to datafile </td></tr>
+ <tr><td> zap         	 </td><td> delete pixels </td></tr>
+ <tr><td> zeropts        </td><td> show filter zeropts </td></tr>
+ <tr><td> zplot          </td><td> plot x y with size scaled by z </td></tr>
+</table>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/database.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/database.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/database.htm	(revision 22322)
@@ -0,0 +1,121 @@
+
+This discussion is somewhat short, and only valid for the original
+Elixir-RAW style format/mode.  
+
+<pre>
+The format for a catalog region file is as follows:
+
+-- 
+Header
+--
+Averages
+--
+Measurements
+--
+</pre>
+
+<p>
+The Header follows the format of a FITS header but always stored with
+3 blocks to speed up read time: 2880 x 3 bytes.  Two important
+keywords in the header: NSTARS & NMEAS
+
+<p>
+All average values are stored consecutively in a single block.  They
+are written as an array with NSTARS elements of the Average structure
+defined below.  The individual measurements follow the averages as a
+block.  To find a measurement for a given star, you need to get the
+value of the offset for the star.  this is the element number for the
+first measurement of this star, and are followed by next measurements
+for this star, until the value Nm is reached.
+
+Here are the structures being used for the average and measurement
+values:
+
+<pre>
+
+typedef struct {
+  float R;                    /* RA  in decimal degrees */
+  float D;                    /* DEC in decimal degrees */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned short int Nm;      /* number of measurements */
+  unsigned short int Xp, Xm;  /* chisq values in tenths */
+  unsigned int offset;        /* offset to first Measure-ment */
+} Average;                    /* = 20 bytes / average */
+
+typedef struct {
+  char dR, dD;                /* tenths of arcsec (-12.7 to +12.7 valid range) */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned char dM;           /* thousandths of mag (0.000 -- 0.255 valid range) */
+  float t;                    /* time in seconds (what is reference?) */
+  unsigned int average;       /* reference to corresponding Average entry, 
+				 upper byte of value contains flags.
+				 limit of 16,777,215 stars (Naverage) 
+				 in a file (=0xFFFFFF).
+				 flags = average & 0x1000000 */
+} Measure;                    /* = 13 bytes / measure */
+</pre>
+
+<p>
+The above two structures define the entries in the photometry
+database.  The database consists of a large number of files
+representing a small patch on the sky (roughly 1.5 degree$^2$ in most
+places).  These files are organized into directories representing
+bands of Declination.  A reference file determines the coordinate
+boundaries for each of the files so that a given point on the sky can
+unambiguously be associated with a specific file in a specific
+directory.  The sky coordinates for each file is the same as those
+used by the HST Guide Star catalog, except for the region around the
+North celestial pole, for which all stars are included in a single
+file.  
+
+<p>
+Within a given file, the data are stored in a binary format, with an
+ASCII FITS-like header.  The header is examply in the format of a
+normal FITS header, but with the exception that all files have a fixed
+number of header blocks (for now 3 blocks = 8640 bytes).  This is done
+to speed loading the header and finding the beginning of the binary
+data.  The number of 3 blocks seems quite generous, as currently only
+a few FITS keywords have been defined for each file, basically
+keywords to define the number of stars and the total number of
+measurements stored in the file, as well as values to define the RA
+and DEC range of the file.  
+
+<p>
+The first section of data following the header blocks consists of
+average measurements for each uniquely observed star.  Each star
+occupies 20 bytes, the size of the Average structure defined above.
+The Average structure contains the average Ra, Dec, and Magnitude for
+the star, as well as the number of measurements, and \chisq\ values
+for the magnitude and position.  Finally, there is a 32 bit integer
+which defines the offset to the first measurement for this star.  This
+offset is defined as the number of Measure records from the start of
+the Measure structure.
+
+<p>
+The second section of data, following the Average data contains all
+measurements for each star listed in Average.  Each measurement
+occupies 13 bytes, the size of the Measure structure.  This structure
+contains the difference of this position from the average RA and DEC,
+and the instrumental magnitude of this measurement (in the units
+defined by the fstat program, which give m = -2.5*log(cts) + Mo, where
+Mo is currently 24.5 [10/15]).  There is also the magnitude error for
+this measurement, the time of the measurement (in seconds relative to
+a to-be-determined zero point), and a reference to the entry in the
+Average structure so we can relate a given measurement with a given star.
+This last entry also includes a byte of flags, of which only 4 have
+currently been defined.  This means the Average offset can only be as
+large as  16,777,215 (0xffffff), limiting the possible number of stars
+allowed in a given file.  This does not seem like a long term problem,
+though:  aside from the fact that this number is very large and we
+only expect in the vicinity of 20,000 stars per file, the file can
+easily be divided into pieces at a later date if needed.  this last
+step is trivial, consisting of splitting the data up into smaller
+RA,DEC regions, and updating the reference catalog.  With the above
+definitions for the Average and Measure structures, we will typically
+expect 20,000 * 15 measurements per year and 20,000 average entries in
+a given file.  This implies a file size of about 4.3 MB at the end of
+a year.  It is also possible that we will choose to split the files up
+in the future if the number of measurements makes their size
+unweildy.  4.3 MB is sufficiently small that this is not a problem,
+but after only 5 years, the files will be over 21 MB each, getting to
+be fairly significant.  
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/dmagoptions.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/dmagoptions.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/dmagoptions.txt	(revision 22322)
@@ -0,0 +1,98 @@
+
+mextract from value [options]
+avextract from value [options]
+
+imextract value [options]
+
+dmags F1 - F2 : F3 [options]
+dmagmeas F1 - F2 : value [options]
+dmagaves F1 - F2 : value [options]
+
+cmd F1 - F2 : F3 [options]
+ccd F1 - F2 : F3 - F4 [options]
+
+calextract F1 - F2 [options]
+calmextract F1 - F2 [options]
+
+options:
+
+ -photcode value
+  for avextract and dmagaves, -photcode defines the which code is used
+  to calculate certain values (mag, dmag, etc).  objects which lack a 
+  photcode return a logical NaN for that value.
+
+ -magrange min max
+
+this can apply to any photcode
+
+ -imaglim min 
+ -flag value
+ -time start stop
+ -fwhm (value)
+ -type (value)
+
+these naturally apply to the REF and DEP photcodes, but cannot apply to 
+to PRI and SEC.  (note that -type is interpretted a bit differently here
+from below. 
+
+ -chisq max
+ -errorlim max
+ -type (value)
+ -typefrac (type) (frac)
+ -nphot (value)
+ -ncode (value)
+ -fwhmfrac (value) (frac)
+
+these naturally apply to PRI and SEC photcodes, but cannot apply to
+REF.  For DEP, they could apply to the equivalent photcode.  eg
+
+  ccd I - 2MASS_J : 2MASS_J - 2MASS_K -chisq 2.0
+
+  this will exclude on chisq for the I value, and ignore the limit
+  for the 2MASS values
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -chisq 2.0
+
+  this will exclude on chisq for the I value, and ignore the limit
+  for the 2MASS values, and exclude on R (equiv to CFH12K.R.00).
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -time start stop
+
+  by default, this will filter the CFH12K.R.00 and 2MASS measurements 
+  based on
+
+we need to be able to 
+
+
+measure values:
+  ra
+  dec
+  mag
+  dmag
+  airmass
+  exptime
+  photcode
+  time
+  dR
+  dD
+  fwhm
+  dophot
+  FLAGS
+  XCCD
+  YCCD
+  XMOSAIC
+  YMOSAIC
+
+average values:
+  ra
+  dec
+  dmag
+  Nmeas
+  Nmiss
+  Xp
+  Xm
+  flag
+  type
+  typefrac
+  Nphot
+  Ncode
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/example.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/example.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/example.htm	(revision 22322)
@@ -0,0 +1,82 @@
+
+<p>
+This <a href=../download/dvo.sample.tgz>tarball</a> contains a
+collection of data from 6 Megacam standard star image obtained in
+September, 2003.  There are two sets of three images, taken two weeks
+apart (03 Sep and 21 Sep).  In each set, there is one each of g', r',
+and i' images, all short exposures of 3.0 seconds.  This dataset makes
+a useful demonstration of the DVO system.
+
+<p>
+The tarball contains 6 directories, one for each image.  The images
+have been processed with the Elixir system and the photflat-corrected
+flat-field images for each filter, though no fringe correction has
+been applied.  Sextractor has been run on all of the images, and only
+the sextractor-derived photometry is available in the tarballs.  No
+image pixel data is kept.  The directories are named after the
+exposure odometer numbers, eg 718688o.
+
+<p>
+Within each directory, the data is separated into a number of files
+for each of the 36 detector, with names created by appending the chip
+number to the odometer number of the image, eg 718688o00.  For each
+detector, the directory contains:
+
+<ul>
+<li> the Elixir processing log file: *.log
+<li> the raw sextractor output: *.sx
+<li> the photometry data in Elixir cmp format: *.smp
+<li> the photometry data in FITS table format: *.smf
+<li> the photometry data with mosaic astromtry: *.xmf
+</ul>
+
+<p>
+The raw sextrator output consists of a fixed-format ASCII table with
+columns defined by the Elixir processing system.  The Elixir cmp
+format merges the sextractor photometry table with the image header to
+form a pseudo-FITS table.  The smf format contains essentially the
+same information as the smp format, but the table portion of the data
+is written as a FITS table in the first extension, with appropriate
+table description in the table header unit.  The primary header of
+this file consists of the original image header, with the value of
+NAXIS set to 0.  This has the effect of making the file a valid FITS
+file, while keeping the information of NAXIS1 and NAXIS2 (the original
+image dimensions), while eliminated the data taken by the image array.
+
+<p>
+Both the .smp and .smf files include the astrometric solution in the
+form of WCS header entries.  In the case of these files, the
+astrometry requires a linear solution across the chip.  The linear
+solution provides a robust solution, but results in systematic errors
+which are substantial, roughly 0.5-1.0 arcsec, due to the optical
+system distortion.  A better solution can be obtained by fitting
+higher order polynomials.  We use a two-layer solution, with one set
+of third-order polynomials to represent the warping of each detector,
+and a second third-order polynomial to represent the telescope optical
+distortion.  The .xmf files represent the same data as the .smf files,
+also in the FITS table format, but with the two-layer mosaic
+astrometric solutions in their headers.  In addition, these files
+require the single .phu file which represents the full-mosaic PHU and
+contains the top-level telescope distortion solution.
+
+It is possible to add any of these data to the DVO database using the
+addstar command.  To add any of the .smp files, simply use the
+command:
+
+<pre>
+addstar filename.smp
+</pre>
+
+To add any of the .smf files, it is necessary to specify the
+<tt>-fits</tt> flag:
+
+<pre>
+addstar -fits filename.smf
+</pre>
+
+To add any of the .xmf files, it is necessary to specify the
+<tt>-fits</tt> flag and to identify the mosaic astrometry file:
+
+<pre>
+addstar -mosaic root.phu -fits filename.smf
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/extract.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/extract.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/extract.htm	(revision 22322)
@@ -0,0 +1,147 @@
+<meta name=title content=DVO extractions>
+<meta name=page  content=extractions>
+
+<p>
+DVO provides several ways to access the photometry information stored
+in the database.  Several simple commands allow the user to extract 1
+dimensional information directly from one of the primary database
+tables.  The commands are:
+<ul>
+<li> imextract
+<li> avextract
+<li> mextract
+<li> imsearch
+<li> detsearch
+</ul>
+
+<h3> imextract </h3> 
+
+This command allows the user to extract one of the columns from the
+image table, applying filtering as desired:
+
+<pre>
+  dvo: imextract
+  USAGE: imextract (value) [-region] [-time start range] [-photcode photcode]
+  dvo: imextract help
+  value may be one of the following:
+   ra dec airmass Mcal dMcal Xm photcode time fwhm exptime nstar ncal sky flag
+</pre>
+
+<p>
+The extracted data is saved in a vector with the same name used to
+select the column.  The vector name will have the same case as the
+choice given, but the column selection is case-insensitive (since
+there are no ambiguities in the database columns names by case).  
+
+<h3> avextract </h3>
+
+This command allows the user to extract data from one of the Average
+table columns:
+
+<pre>
+  dvo: avextract
+  USAGE: avextract (from) (value) [options]
+    from: cpt name or 'all'
+    value: average.parameter or photcode
+  dvo: avextract all help
+  value may be one of the following:
+   ra dec dmag Nmeas Nmiss Xm Xp Nphot Ncode flag type
+</pre>
+
+<p>
+This command takes as the first argument the name of one of the
+database regions.  Alternatively, all regions currently displayed may
+be selection with the word 'all'.  The second option specifies which
+column to select from the Average table.  In addition to the basic
+data columns (ra, dec, etc), the magnitude-related average values
+(mag, dmag, Xm, Nphot, Ncode) are coupled to a photcode, which is thus
+required for these selections.  The value 'mag' may also be subsituted
+with a primary or secondary photcode.  Eg:
+
+<pre>
+  avextract all ra : select ra for all objects in displayed region
+  avextract all g  : select g magnitudes
+  avextract all mag -photcode r : select r magnitudes 
+  avextract all Xm -photcode r  : select chisq values for r average mags
+</pre>
+
+<h3> mextract </h3>
+
+This command allows the user to extract data from one of the Measure
+table columns:
+
+<pre>
+  dvo: mextract
+  USAGE: mextract (from) (value) [options]
+    from: cpt name or 'all'
+    value: measure.parameter or photcode
+  dvo: mextract all help
+  value may be one of the following:
+   ra dR dec dD mag dmag Mrel Mcal photcode time fwhm dophot xccd yccd xmosaic ymosaic flags
+</pre>
+
+<p>
+This command takes as the first argument the name of one of the
+database regions.  Alternatively, all regions currently displayed may
+be selection with the word 'all'.  The second option specifies which
+column to select from the Measure table.  In addition to the basic
+data columns (ra, dec, etc), the magnitude-related average values
+(mag, dmag) are coupled to a photcode, which is thus required for
+these selections.  The value 'mag' may also be subsituted with a
+primary or secondary photcode.  Eg:
+
+<pre>
+  mextract all ra : select ra for all objects in displayed region
+  mextract all g  : select g magnitudes
+  mextract all mag -photcode r : select r magnitudes 
+  mextract all Xm -photcode r  : select chisq values for r average mags
+</pre>
+
+<h3> option filtering </h3>
+
+The following extraction options allow the user to restrict the
+selections:
+
+<p>
+<tt> -time (start) (range)<tt><br> 
+
+select data for images within the given time range.  The start date is
+given in the format YYYY/MM/DD,hh:mm:ss (any of these element may be
+dropped, in which case they default to 00 [for hh,mm,ss] or 01 [for
+MM,DD]).  The date may also be written as a number of days followed by
+j (JD) or J (MJD).  The two special date names "now" and "today" are
+also valid.  The range is written as a number followed by a unit, with
+valid units of s (seconds), m (minutes), h (hours), d (days).  Times
+are in UT.  For example:
+
+<pre>
+ -time 2001/1/1 30d : select the range starting at midnight on 2001/1/1
+  and ending 30 days (86400*30 seconds) later.
+
+ -time now -3h : select the time range starting three hours ago and
+  ending now.
+</pre>
+
+<tt> -region<tt><br> 
+
+restrict the selection to the currently display portion of the sky.
+This filter expects a portion of the sky to be plotted, and will only
+select data for images in the part of the sky.  The algorithm for
+selecting the displayed region may not be perfect, so images near the
+boundaries may be unexpected included or excluded (this depends on the
+exact overlap details).  Multiple queries with the same region will
+result in the same subset of images selected.
+
+<tt>-photcode (photcode) </tt><br>
+
+This restricts the selection to the given photcode.  Images may only
+have photcodes of 'Dependent' type (eg, CFH12K.R.00).  This filter
+allows the selection of images by exact match (selected photcode is
+Dependent type) or by equivalence match (selected photcode is Primary
+or Secondary type).  Thus, selecting images based on CFH12K.R.00 will
+ignore images with photcode CFH12K.R.10, while selecting images based
+on photcode 'r' will return all images with CFH12K.R.*, since all are
+equivalent to 'r'.  (NOTE: these details depend on the layout of the
+photcode table).
+
+<em> fill this out with all other restrictions provided by photometry.c</em>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/index.htm	(revision 22322)
@@ -0,0 +1,19 @@
+<meta name=file  content=DVO>
+<meta name=title content=DVO System>
+<meta name=page  content=The DVO System>
+
+<h3> Introduction </h3>
+
+DVO (the Desktop Virtual Observatory) is system for manipulating
+collections of images and measurements related to astronomical
+objects.  The DVO system consists of several programs which insert
+data in the database system, or manipulate data in the database.  One
+of these is the <tt>DVO shell</tt>, which provides a command-line
+driven, programmable user interface to the objects and measurements in
+the database.  The DVO shell is based on the Opihi shell, which
+provides the tools to manipulate and display data in various
+representations.  The DVO shell adds to this basic command set a
+collection of functions which provide direct access to the contents of
+the DVO database tables.
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/navigate.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/navigate.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/navigate.htm	(revision 22322)
@@ -0,0 +1,44 @@
+
+<p>
+The <a href=navigate.pro>navigate</a> script provides a convenient
+graphical user interface to the DVO shell and database.  To use it,
+simply load it into the DVO shell with the <tt>input</tt> command.  It
+is useful to always load this script by including the input command in
+your <tt>~/.dvorc</tt> file.  Once you have loaded the script, you
+can start it by typing <tt>navigate</tt>.  You can navigate around the
+sky, then exit the script at any time by typing 'q'.  This allows you
+to issue DVO commands related to your current position.  You can then
+resume navigating by restarting the script.
+
+<p>
+The navigate script lets you move about the sky by placing your cursor
+in the display window and clicking various keys.  Below is a summary
+of the basic key stokes:
+
+<pre>
+1 - zoom in factor of 2
+2 - zoom in factor of 1.2
+3 - recenter at cursor
+4 - zoom out factor of 1.2
+5 - zoom out factor of 2
+6 - zoom out factor of 10
+z - zoom to radius (requires 2nd keystroke)
+f - show full sky
+
+q - quit
+g - toggle skygrid on/off
+r - plot detected asteroids (rocks)
+l - plot HST GSC
+L - plot Landolt stars
+m - list measurements for stars within 1 pixel of cursor
+M - list measurements for stars within 1.8 arcsec of cursor
+i - list info about images touching cursor location 
+I - list info about images, with pixel coords of cursor position
+x - plot stars scaled by magnitude Chisq
+X - plot stars by magnitude scatter
+S - toggle auto-plotting of stars
+t - plot light curve for star within 2 arcsec of cursor position
+T - plot 'galaxy' light curve for star within 2 arcsec of cursor position
+c - plot status catalog boundaries
+C - list catalog at cursor location
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/navigate.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/navigate.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/navigate.pro	(revision 22322)
@@ -0,0 +1,379 @@
+# -*- perl -*-
+
+macro navigate
+  style -n 0
+  limits
+  $DSTARS = -1
+  $ZOOM = 180 / ($YMAX - $YMIN)
+  for i 1 100
+    $REDRAW = 0
+    cursor -g 1
+    # zoom controls
+    if ("$KEY" == "1")
+      $ZOOM = $ZOOM * 2
+      $REDRAW = 1
+      $Rnum = $R$KEY		      
+      $Dnum = $D$KEY
+      $KEY = num
+    end
+    if ("$KEY" == "2")
+      $ZOOM = $ZOOM * 1.2
+      $REDRAW = 1
+      $Rnum = $R$KEY		      
+      $Dnum = $D$KEY
+      $KEY = num
+    end
+    if ("$KEY" == "3")
+      $REDRAW = 1
+      $Rnum = $R$KEY		      
+      $Dnum = $D$KEY
+      $KEY = num
+    end
+    if ("$KEY" == "4")
+      $ZOOM = $ZOOM / 1.2
+      $REDRAW = 1
+      $Rnum = $R$KEY		      
+      $Dnum = $D$KEY
+      $KEY = num
+    end
+    if ("$KEY" == "5")
+      $ZOOM = $ZOOM / 2
+      $REDRAW = 1
+      $Rnum = $R$KEY		      
+      $Dnum = $D$KEY
+      $KEY = num
+    end
+    if ("$KEY" == "6")
+      $ZOOM = $ZOOM / 20
+      $REDRAW = 1
+      $Rnum = $R$KEY		      
+      $Dnum = $D$KEY
+      $KEY = num
+    end
+
+    # help list
+    if ("$KEY" == "h")
+     echo "1 - zoom in factor of 2"
+     echo "2 - zoom in factor of 1.2"
+     echo "3 - recenter at cursor"
+     echo "4 - zoom out factor of 1.2"
+     echo "5 - zoom out factor of 2"
+     echo "6 - zoom out factor of 10"
+     echo "z - zoom to radius (requires 2nd keystroke)"
+     echo "f - show full sky"
+     echo ""
+     echo "q - quit"
+     echo "g - toggle skygrid on/off"
+     echo "r - plot detected asteroids (rocks)"
+     echo "l - plot HST GSC"
+     echo "L - plot Landolt stars"
+     echo "m - list measurements for stars within 1 pixel of cursor"
+     echo "M - list measurements for stars within 1.8 arcsec of cursor"
+     echo "i - list info about images touching cursor location" 
+     echo "I - list info about images, with pixel coords of cursor position"
+     echo "x - plot stars scaled by magnitude Chisq"
+     echo "X - plot stars by magnitude scatter"
+     echo "S - toggle auto-plotting of stars"
+     echo "t - plot light curve for star within 2 arcsec of cursor position"
+     echo "T - plot 'galaxy' light curve for star within 2 arcsec of cursor position"
+     echo "c - plot status catalog boundaries"
+     echo "C - list catalog at cursor location"
+    end
+
+    # quit from navigate
+    if ("$KEY" == "q") 
+      break
+    end
+
+    # measure distance
+    if ("$KEY" == "d")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type at radius"
+      cursor -g 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = 3600*((dcos($d0)*($r0-$r1))^2 + ($d0-$d1)^2)^0.5
+      echo "$dr arcsec"
+    end
+    # show ra, dec
+    if ("$KEY" == "w")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      echo "$tmp $D$KEY" 
+      exec echo $tmp $D$KEY | radec -hh
+    end
+    # zoom to radius
+    if ("$KEY" == "z")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type at radius"
+      cursor -g 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = (($r0-$r1)^2 + ($d0-$d1)^2)^0.5
+      $ZOOM = $RAD / $dr
+      $REDRAW = 1
+      $KEY = $ok
+      $R$KEY = $r0
+      $D$KEY = $d0
+    end
+
+    # adjust mag scaling
+    if ("$KEY" == "J")
+      $MAG = $MAG - 0.5
+      $REDRAW = 2
+    end
+    if ("$KEY" == "K")
+      $MAG = $MAG + 0.5
+      $REDRAW = 2
+    end
+    if ("$KEY" == "j")
+      $dMAG = $dMAG * 0.8
+      $REDRAW = 2
+    end
+    if ("$KEY" == "k")
+      $dMAG = $dMAG * 1.25
+      $REDRAW = 2
+    end
+    echo "mag, dmag: $MAG, $dMAG"
+
+    # redraw region 
+    if ($REDRAW == 1)
+      region $R$KEY $D$KEY {$RAD/$ZOOM} sin
+      $RMIN = $R$KEY + $XMIN
+      $RMAX = $R$KEY + $XMAX
+      $DMIN = $D$KEY + $YMIN
+      $DMAX = $D$KEY + $YMAX
+      if (($ZOOM < 20) && ($GRID == 1)) 
+        style -c red; cgrid
+      end
+      if (($ZOOM > 20) && ($DSTARS == 1))
+        style -pt 7
+	pmeasure -all -m $MAG {$MAG + $dMAG}
+      end    
+      style -c black
+       images
+#       showtile
+    end
+
+    # redraw region 
+    if ($REDRAW == 2)
+      clear
+      if (($ZOOM < 20) && ($GRID == 1)) 
+        style -c red; cgrid
+      end
+      if (($ZOOM > 20) && ($DSTARS == 1))
+        style -pt 2
+	pmeasure -all -m $MAG {$MAG + $dMAG}
+      end    
+      style -c black
+      images
+    end
+
+    # turn grid on / off
+    if ("$KEY" == "g")
+      if ($GRID == 1) 
+        $GRID = 0
+        style -c white; cgrid
+        style -c black
+      else
+        $GRID = 1 
+        style -c black; cgrid
+      end
+    end
+
+    # plot full sky
+    if ("$KEY" == "f") 
+      echo "full"
+      $ZOOM = 1
+      resize 1150 600		      
+      region 0 0 90 ait
+      $RMIN = 0
+      $RMAX = 360
+      $DMIN = -90
+      $DMAX = +90
+      style -c red; cgrid
+      style -c black
+      images
+    end
+
+    # plot rocks
+    if ("$KEY" == "r") 
+#      plot.rocks
+      style -c blue   -pt 1; procks -speed 0.0041 1
+      style -c red    -pt 1; procks -speed 0.00041 0.0041
+      style -c indigo -pt 1; procks -speed 0 0.00041
+      style -c black -lw 0;
+    end
+    # plot HST-GSC
+    if ("$KEY" == "l") 
+      style -c blue -pt 7; cat -all -g -m 9 16
+      style -c black
+    end
+    # plot Landolt
+    if ("$KEY" == "L") 
+#      style -c red  -lw 2 -pt 3; cat -a 1 2 3 /data/elixir/srcdir/refs/stetson/stetsonBn.txt -m 9 18
+#      style -c blue -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.fix -m 9 18
+      style -c red -lw 2 -pt 7; cat -a 1 2 3 /data/elixir/srcdir/refs/sdss/g_SDSS.dat -m 9 14
+#      style -c red -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.hq -m 9 18
+#      style -c red -lw 2 -pt 3; cat -a 22 23 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.unfix -m 9 18
+#      style -c blue -lw 2 -pt 7; cat -a 1 2 4 /data/elixir/srcdir/refs/landolt/extreme/extreme.match -m 0 20
+#      style -x 2 -c red -pt 7 ; cplot RA DEC
+      style -c black -lw 0
+    end
+
+    # list star measurements
+    if ("$KEY" == "m") 
+        $dR = $RAD/$ZOOM/300
+        if ($dR < 0.0005)
+	 $dR = 0.0005
+        end
+	gstar $R$KEY $D$KEY $dR -m
+    end
+
+    # plot mag residuals
+    if ("$KEY" == "R") 
+      echo "filter: "
+      cursor 1
+      clear -n 1 -s; lim 10 22 -0.2 0.2; clear; box
+      dmags $KEY\:rel - $KEY : $KEY -type 0
+      plot -x 2 -pt 0 -sz 0.3 -c red yv xv
+      dmags $KEY\:rel - $KEY : $KEY -type 0 -flag 0 -nphot +3 -chisq 2.0
+      plot -x 2 -pt 2 -sz 0.5 -c black yv xv
+      $KEY = R
+      style -n 0
+    end
+
+    if ("$KEY" == "M") 
+	gstar $R$KEY $D$KEY 0.0005 -m
+    end
+    # list images
+    if ("$KEY" == "i") 
+	gimages $R$KEY $D$KEY
+    end
+    if ("$KEY" == "I") 
+	gimages $R$KEY $D$KEY -pix
+    end
+    # turn stars on / off
+    if ("$KEY" == "S")
+      $DSTARS = $DSTARS * -1
+      if (($ZOOM > 20) && ($DSTARS == 1))
+       style -pt 7
+       pmeasure -all -m $MAG {$MAG + $dMAG}
+      end
+    end
+    # plot light-curve interactive
+    if ("$KEY" == "t")
+      style -n 1 -pt 2 -x 2
+      clear
+      if ($R$KEY < 0) 
+       $R$KEY = $R$KEY + 360
+      end
+      lcurve -l $R$KEY $D$KEY {30/3600} -d -v time mag
+      box
+      lcv
+      style -n 0
+    end
+    # plot light-curve 
+    if ("$KEY" == "T")
+      style -n 1 -pt 1 -c red -x 2
+      lcurve $R$KEY $D$KEY {30/3600} -d
+      style -c black
+      style -n 0 
+    end
+    # plot catalogs
+    if ("$KEY" == "c")
+      style -c blue; pcat; style -c black
+    end
+    # list catalogs
+    if ("$KEY" == "C")
+      gcat $R$KEY $D$KEY
+    end
+
+    # plot image chisqs
+    if ("$KEY" == "x") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME Xm -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c blue
+       czplot ra dec Xm 3 30
+       style -c black -pt 1
+    end
+    # plot meas errors
+    if ("$KEY" == "X") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME dM -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c red
+       czplot ra dec dM 0 30
+       style -c black -pt 1
+    end
+    # temp plot for skyprobe
+    if ("$KEY" == "u") 
+      imextract -region time
+      imextract -region mcal
+      imextract -region airmass
+      imextract -region nstar
+      vstat time
+      clear -n 1;
+      section a 0 0.00 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} -0.8 -0.5; box; plot time mcal
+      section b 0 0.33 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3}  0.95 3.0; box; plot time airmass
+      section c 0 0.66 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} 0 3000; box; plot time nstar
+      style -n 0
+    end
+    if ("$KEY" == "s")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      $line = `echo $tmp $D$KEY | radec -hh`
+      imextract -region photcode
+      imextract -region time
+     
+      subset t = time if (int(photcode/100) == 1)
+      uniq t T
+      $Bn = t[]
+      $BN = T[]
+      
+      subset t = time if (int(photcode/100) == 2)
+      uniq t T
+      $Vn = t[]
+      $VN = T[]
+      
+      subset t = time if (int(photcode/100) == 3)
+      uniq t T
+      $Rn = t[]
+      $RN = T[]
+      
+      subset t = time if (int(photcode/100) == 4)
+      uniq t T
+      $In = t[]
+      $IN = T[]
+     
+      echo "$line  $Bn $BN  $Vn $VN  $Rn $RN  $In $IN"
+    end
+
+    if ("$KEY" == "p") 
+      echo "P - new coords; p - old coords"
+      cursor -g 1
+      exec echo $Rp $Dp $RP $DP >> fix.coords
+    end
+
+    if ("$KEY" == "y")
+      ccd I - 2MASS_J : 2MASS_J - 2MASS_K
+      lim -n 1 -1 10 -1 3; clear; box; plot -x 2 -pt 2 -sz 0.5 xv yv
+      dev -n 0 -g
+    end
+  end
+end
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/photcodes.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/photcodes.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/photcodes.htm	(revision 22322)
@@ -0,0 +1,30 @@
+<meta name=file  content=photcodes>
+<meta name=title content=photcodes>
+<meta name=page  content=photcodes>
+
+<p>
+The DVO photcodes define a photometric system, consisting of a
+detector, a filter, and a telescope.  
+
+<p>
+Many DVO commands which accept a photcode as an argument allow for
+the photcode name to be appended with a modifier which changes
+slightly the type of magnitude returned to the user.  
+
+<p>
+The type of photcode is
+specified by adding :type to a photcode.  Valid types consist of:
+<ul>
+<li> inst : instrumental magnitude
+<li>  cat : catalog magnitude = inst + nominal ZP
+<li>  sys : system magnitude = color + airmass corrected mag
+<li>  rel : relative magnitude = applies color, airmass, and relative magnitude offset
+corrections
+<li>  cal : calibrated magnitude : applies zero-point and color
+terms from dependent to primary/secondary magnitude system)
+<li>  ref : reference system
+</ul>
+
+<p>
+<em>This page needs serious work to clarify the meaning of a photcode,
+and the selection criteria </em>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/relphot.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/relphot.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/relphot.htm	(revision 22322)
@@ -0,0 +1,224 @@
+
+<h2> relphot outline </h2>
+
+<ul>
+<li> load data
+     <ul>
+     <li> images: match photcode and time range, reset flags
+     <li> measure: select subset matching restritions on:
+          photcode, time, dM, Minst, Mag, dophot == 1
+     </ul>
+<li> iterate to find Mcal, image.flags:
+<li> write out modified Mcal values to image table
+<li> write out modified Mcal values to measure table
+<li> write out modified Mrel values to average table
+</ul>
+
+<h2> relphot overview </h2>
+
+<p> relphot has two primary purposes:<br>
+<ul>
+<li> calculate <b>Mcal</b> for images / measures
+<li> calculate <b>Mrel</b> for stars
+</ul>
+
+<p> relphot can also be used to determine the mosaic grid used to generate
+photometrically corrected flats (-grid option).
+
+<h2> data exclusion </h2>
+
+<p> relphot uses only a subset of the photometry data to calculate
+Mcal and Mrel.  In the first stage, calculation of the Mcal values,
+relphot loads the photometry data from each relevant catalog and
+creates an internal subset catalog with the function
+<em>bcatalog</em>, excluding some of the irrelevant data.  In
+addition, it uses flags to mark some of the data as invalid for the
+processing.
+
+<b> bcatalog exclusions </b>
+<ul>
+<li> measure.photcode not equivalent to requested photcode
+<li> measure.dophot != 1
+<li> measure.Mcat > MAG_LIM
+<li> measure.dM > SIGMA_LIM
+<li> measure.Minst out of range (ImagMin - ImagMax) [optional]
+<li> measure.t out of range (TSTART, TSTOP)
+</ul>
+
+<b> flagged data </b>
+
+<b> flagged image data </b>
+
+images can be flagged by setting bits of <b>image.code</b>
+
+stars can be flagged by setting bits of <b>average.code</b>
+
+measures can be flagged by setting bits of <b>measure.flag</b>
+
+<b>image.code</b>
+
+ID_IMAGE_NOCAL : ignore, irrelevant
+ID_IMAGE_POOR  : image measured bad
+ID_IMAGE_SKIP  : externally known bad
+
+dMcal > VALUE       FLAG_IMAGE_SCATTER <em>clean_images</em>
+fabs(Mcal) > VALUE  FLAG_IMAGE_ZEROPT  <em>clean_images</em>
+
+dMcal > VALUE       FLAG_IMAGE_SCATTER <em>clean_mosaics</em>
+fabs(Mcal) > VALUE  FLAG_IMAGE_ZEROPT  <em>clean_mosaics</em>
+
+<em> mark_images </em> does not seem to do anything useful?
+
+<b>average.code</b>
+
+Ngood < MEAS_TOOFEW                     <em>setMrel</em>
+Ngood < MEAS_TOOFEW                     <em>clean_measures</em>
+ChiSq > STAR_CHISQ                      <em>clean_stars</em>
+dM    > STAR_SCATTER                    <em>clean_stars</em>
+
+average.code (STAR_BAD) is not saved by relphot: it is set by
+clean_stars, clean_measures, and setMrel, but not setMrelOutput.
+STAR_BAD should only be internal since it depends on the photcode, but
+is not associated with a specific photcode in the data.  Just in case,
+it is reset to 0 in setMrelFinal.
+
+<b>measure.flag</b>
+
+X,Y out of range                        <em>setExclusions</em>
+3 sigma clipping                        <em>clean_measures</em>
+
+<h2> setting Mrel final value </h2>
+
+setMrelFinal is used to set the final average.Mrel values.  We do this
+in 4 stages.  In each stage, we set the Mrel values for stars which
+have not already been set, based on the current exclusion settings.
+At successive stages, we relax the exclusions, allowing the more
+spurious objects to have a valid Mrel value to be set.  In this loop,
+we actually run setMrelOutput twice: once to get the approximate Mrel
+value, then we flag the outlier measurements with clean_measure,
+then we redetermine the Mrel values on this basis, and mark the stars
+for exclusion from the next iteration.  
+
+<pre>
+exclude on
+ photcode       0 1 2 3
+ time range     0 1 2 3
+ MEAS_POOR      0 1 2 3
+ MEAS_TOOFEW    0 1 2 3
+ dophot == 10   0 1 2 
+ inst mag       0 1 2 
+ dophot != 1,2  0 1  
+ ID_IMAGE_POOR  0 1
+ ID_IMAGE_SKIP  0 1
+ dophot != 1    0
+ measure.dM     0 
+</pre>
+
+for all relphot runs, Mrel is re-calculated, and measures are marked
+at least if they are outliers in mag or ccd area.
+
+setMrel.output needs to do a few things differently from setMrel:
+<ul>
+<li> set measure.Mcal (skipped in setMrel.basic)
+<li> set average.Mrel if N < TOO_FEW (not STAR_BAD) (optional!)
+<li> use MAX (stats.error, stats.sigma) (optionally)
+<li> allow STAR_BAD?
+</ul>
+
+<h2> imphotset </h2>
+
+imphotset allows you to set certain phot.image table entries.  here
+are the options:
+
+imphotset [-photcode code] [-name foo] [-trange (start) (stop)] -flag and value
+
+
+Here is a complete list of relphot configuration variable names, a
+quick description, and reasonable values to start with:
+
+<pre>
+--- configuration variables used by relphot ---
+MAG_LIM : float
+  ignore measurements fainter than this absolute magnitude
+
+SIGMA_LIM : float
+  ignore measurements with magnitude error larger than this value
+
+STAR_SCATTER : float
+  mark stars as bad if their scatter is larger than this value
+
+IMAGE_SCATTER : float
+  mark images as bad if their scatter is larger than this value
+
+IMAGE_OFFSET : float
+  mark images as bad if the absolute value of thie zero point offset
+  is larger than this number
+
+STAR_CHISQ : float
+  mark stars as variable if their reduced chisq are larger than this value
+
+STAR_TOOFEW : int
+  mark stars as bad if the have fewer than this number of valid measurements
+
+IMAGE_TOOFEW : int
+  mark images as bad if the have fewer than this number of valid measurements
+
+IMAGE_GOOD_FRACTION : float
+  mark images as bad if the have fewer than this fraction of valid measurements  
+
+IMAGE_CATALOG : string
+  name of the image catalog file
+
+IMAGE_CATALOG_TEMPLATE : string
+  name of the template file to create the image catalog file
+
+CATALOG_TEMPLATE : string
+  name of the template file to create the catalog file
+
+GSCFILE : string
+  name of the GSC region table
+
+CATDIR : string
+  directory where the database is stored
+
+PHOTCODE_FILE : string
+  file containing photometry code information
+
+ZERO_PT : float
+  default zero point for random data
+
+RELPHOT_GRID_X : int
+  scale of mosaic correction grid 
+
+RELPHOT_GRID_Y : int
+  scale of mosaic correction grid 
+
+RELPHOT_GRID_BINNING : int
+  deprecated
+
+CAMERA_CONFIG : string
+  name of the file containing descriptive information about the camera
+
+--- sample ConfigFile entries with typical values ---
+
+MAG_LIM                	 24.0
+SIGMA_LIM              	  0.05
+STAR_SCATTER           	  0.05
+IMAGE_SCATTER          	  0.05
+IMAGE_OFFSET           	  0.2
+STAR_CHISQ             	 10.0
+STAR_TOOFEW            	  3
+IMAGE_TOOFEW           	 10
+IMAGE_GOOD_FRACTION    	 
+IMAGE_CATALOG          	 $CATDIR/Images.dat
+IMAGE_CATALOG_TEMPLATE 	 $REFSDIR/elixir/template.cat
+CATALOG_TEMPLATE       	 $REFSDIR/elixir/template.cat
+GSCFILE                	 $REFSDIR/gsc/GSCregions.tbl
+CATDIR                 	 $CATDIR
+PHOTCODE_FILE          	 $CONFDIR/camera/$CAMERA.photcode
+ZERO_PT                	 25.0
+RELPHOT_GRID_X         	 4
+RELPHOT_GRID_Y         	 8
+RELPHOT_GRID_BINNING   	 512
+CAMERA_CONFIG          	 $CONFDIR/camera/$CAMERA.config
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/sequence.idx
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/sequence.idx	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/DVO/sequence.idx	(revision 22322)
@@ -0,0 +1,11 @@
+DVO-shell.htm
+commands.htm
+example.htm
+navigate.htm
+addstar.htm
+delstar.htm
+relphot.htm
+uniphot.htm
+markrock.htm
+markstar.htm
+imphotsearch.htm
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/detrend.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/detrend.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/detrend.htm	(revision 22322)
@@ -0,0 +1,84 @@
+<meta name=file  content=detrend>
+<meta name=title content=detrend.db tools>
+<meta name=page  content=detrend>
+
+<table>
+<tr><td><b><tt> detregister (filename) [elixir options] </tt></b></td></tr>
+
+<tr><td> 
+
+Register a detrend image in the detrend database.  The image
+parameters are automatically loaded from the image header, but can be
+forced with command-line options if needed.  The database consists of
+a database table and a hierarchy of directories.  The registered image
+is copied to the directory structure, assigned an appropriate name
+based on the data type, and an entry added to the database table.
+Below, the command-line options are listed. The matched keyword define
+the header keywords used to automatically identify the values.
+Keywords starting with $ represent the configuration variables which
+provide abstract names to define the real keywords.  The idiosyncratic
+header filter name is converted to the Elixir internal filter name
+based on the filter name table $FILTER_LIST.
+
+<table>
+<tr><th> option               </th><th> equivalent keyword </th><th> description </th></tr>
+<tr><td> -time (start) (stop) </td><td> TV_START, TV_STOP  </td><td>
+<tr><td> -label (label)	      </td><td>	N/A		   </td><td>
+<tr><td> -ID (ID)	      </td><td>	N/A		   </td><td>
+<tr><td> -order (N)	      </td><td>	N/A		   </td><td>
+<tr><td> -type (type)	      </td><td>	$IMAGETYPE-KEYWORD </td><td>
+<tr><td> -ccd (ccdID)	      </td><td>	$CCDNUM-KEYWORD    </td><td>
+<tr><td> -exptime (seconds)   </td><td>	$EXPTIME-KEYWORD   </td><td>
+<tr><td> -filter (name)       </td><td>	$FILTER-KEYWORD	   </td><td>
+</table>
+</tr></td> 
+
+<tr><td><b><tt> detsearch [search options] [elixir options] </tt></b></td></tr>
+
+<tr><td> 
+
+Examine the contents of the detrend database, with options to filter
+the output.  
+
+<table>
+<tr><th colspan=2> options which limit the image selection </th></tr>
+<tr><td> -label (label)	       </td><td> select images by label </td><td>
+<tr><td> -ID (ID)	       </td><td> select images by ID    </td><td>
+<tr><td> -order (N)	       </td><td> select images by order </td><td>
+<tr><td> -type (type)	       </td><td> select images by type (FLAT, etc) </td><td>
+<tr><td> -mode (mode)	       </td><td> select images by mode (MEF, etc)  </td><td>
+<tr><td> -ccd (ccdID)	       </td><td> select images by CCD ID </td><td>
+<tr><td> -exptime (seconds)    </td><td> select images by exposure time </td><td>
+<tr><td> -filter (name)        </td><td> select images by filter </td><td>
+<tr><td> -time (time)          </td><td> select images for given point in time </td><td>
+<tr><td> -trange (start) (stop)</td><td> select images for given time range  </td><td>
+<tr><td> -entry (entry)        </td><td> select images by entry number </td><td>
+<tr><td> -match (number)       </td><td> select images by match number </td><td>
+<tr><td> -name (name)          </td><td> select images by name </td><td>
+<tr><td> -mosaic (filename)    </td><td> select images appropriate to given mosaic </td><td>                
+<tr><td> -image (file) (ccd) (mode)</td><td> select images appropriate to given CCD image </td><td>
+
+<tr><th colspan=2> options which modify the output format </th></tr>
+<tr><td> -tstop                </td><td> print stop time, not start time </td><td>
+<tr><td> -treg                 </td><td> print registration time, not start time </td><td>
+<tr><td> -ve                   </td><td> print SUCCESS / FAILURE on exit </td><td>
+<tr><td> -quiet                </td><td> do not print SUCCESS / FAILURE on exit </td><td>
+<tr><td> -fits (output)        </td><td> write ASCII FITS table (appropriate for CFHT-LS) </td><td>
+<tr><td> -binfits(output)      </td><td> write binary FITS table (can be used as database table) </td><td>
+<tr><td> -chipname             </td><td> provide chip-specific name for MEF entry (ie flat.fits[ccd00]) </td><td>
+
+<tr><th colspan=2> options which modify the behavior </th></tr>
+<tr><td> -select               </td><td> select 'best' detrend image, include complete path </td><td>
+<tr><td> -recipe               </td><td> select all detrend images in recipe for this filter </td><td>
+<tr><td> -h, --help            </td><td> list help information </td><td>
+<tr><td> -criteria             </td><td> list input criteria, not database entries </td><td>
+
+<tr><th colspan=2> options which modify the database table </th></tr>
+<tr><td> -delete, -del         </td><td> delete the selected images </td><td>
+<tr><td> -altpath add          </td><td> add selected images to altpath </td><td>
+<tr><td> -altpath del          </td><td> remove selected images from altpath </td><td>
+<tr><td> -altpath update       </td><td> update altpath value based on file existence </td><td>
+<tr><td> -modify               </td><td> modify selected database fields </td><td>
+</table>
+</tr></td> 
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/imreg.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/imreg.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/imreg.htm	(revision 22322)
@@ -0,0 +1,92 @@
+<meta name=file  content=imreg>
+<meta name=title content=imreg.db tools>
+<meta name=page  content=imreg>
+
+<h3> add information about gconfig variables used </h3>
+
+<table>
+<tr><td><b><tt> imregister (filename) [-split] [-noreg] [-v] [-needtype] [elixir options] </tt></b></td></tr>
+
+<tr><td> 
+
+Register an image in the imreg database.  The -split option tells
+imregister that a single CCD frame is one of multiple frames in a
+split image.  The -noreg option shows the processing information
+without updating the database table.  The -v turns on verbosity.  The
+-needtype requires the image type to be one of the known types to be
+registered.
+
+</tr></td> 
+
+<tr><td><b><tt> imsort (filename) [-split] [-noreg] [-v] [-needtype] [elixir options] </tt></b></td></tr>
+
+<tr><td> identical to 'imregister', but also send entries as
+appropriate to the imstats and ptolemy elixir fifos.  If -noreg is
+selected, the fifo entries are created without the imreg.db
+update. </tr></td>
+
+<tr><td><b><tt> showiminfo (filename) [-split] [-noreg] [-v] [elixir options] </tt></b></td></tr>
+
+<tr><td> list image information extracted from the header, as used to
+update imreg.  </tr></td>
+
+<tr><td><b><tt> imstatreg (fits) (stats) (sdat) [-split] [-noreg] [-v] [-needtype] [elixir options] </tt></b></td></tr>
+
+<tr><td> add image statistics (seeing, bias, sky) to the image entry
+in the imreg table.  The (fits) file need only contain the relevant
+header of the image (used to find the appropriate image in the table).
+The (stats) file consists of the values sky and bias for MEF and SPLIT
+images, while for other modes, there must be an addtional entry
+starting each line: CCD.  The meaning is undefined for SINGLE, and
+usually set to 0.  For CUBE it is the number of slices in the cube,
+while for SLICE, it is the slice entry.  </tr></td>
+
+<tr><td><b><tt> imstatreg -daemon [-v] [-status] [-kill] [-delay Nsec] [elixir options] </tt></b></td></tr>
+
+<tr><td> Start / Stop / Query the imstatreg daemon.  To facility the
+database I/O, imstatreg updates are written to a buffer table
+(imreg.reg.bfr), which is occasionally merged with the master table
+with the imstatreg daemon.The -delay option defines the frequency at
+which imstatreg -daemon updates the database table.  </tr></td>
+
+<tr><td><b><tt> imregtable (filename) [elixir options] </tt></b></td></tr>
+
+<tr><td> 
+merge the given imreg.db table with the current master table.
+</tr></td> 
+
+<tr><td><b><tt> imsearch </tt></b></td></tr>
+
+<tr><td> 
+search the imreg.db table for images, filterd by the options given in the arguments.  
+The table below lists all options to imsearch.
+
+<table>
+<tr><th colspan=2> options which limit the image selection </th></tr>
+<tr><td> [-type type]                 </td><td> limit by image type (BIAS, DARK, OBJECT, etc)         </tr></td> 
+<tr><td> [-mode mode] 		      </td><td> limit by image mode (SINGLE, SPLIT, MEF, CUBE, SLICE)	 </tr></td> 
+<tr><td> [-trange start stop/range]   </td><td> limit by date range (second argument may be an end date or a time range) </tr></td> 
+<tr><td> [-ccd name]		      </td><td> limit by CCD; use CCD ids, not sequence numbers (ccd00 not 00) </tr></td> 
+<tr><td> [-etime exptime] 	      </td><td> limit by exposure time (seconds)	 </tr></td> 
+<tr><td> [-filter name] 	      </td><td> limit by filter name	 </tr></td> 
+<tr><td> [-name string] 	      </td><td> limit by image name (matches substring of full image name) </tr></td> 
+<tr><td> [-proc t/f]		      </td><td> include only processed / unprocessed images (bias != 0)	 </tr></td> 
+<tr><td> [-dist t/f]		      </td><td> include only distributed images (table send to CADC)	 </tr></td> 
+<tr><td> </tr></td>
+				      	 	 	 
+<tr><th colspan=2> options which modify the output format </th></tr>
+<tr><td> [-treg] 		      </td><td> list the registered times, not exposure times </tr></td> 
+<tr><td> [-seq] 		      </td><td> print CCD sequence numbers, not IDs </tr></td> 
+<tr><td> [-pt] 			      </td><td> print the list in ptolemy input format </tr></td> 
+<tr><td> [-table] 		      </td><td> generate a complete ascii FITS table output </tr></td> 
+<tr><td> [-cadctable] 		      </td><td> generate the CADC raw table (one row per mosaic, includes mosaic IQ stats) </tr></td> 
+<tr><td> [-bintable]		      </td><td> generate a complete binary FITS table (can be used as new database) </tr></td> 
+
+<tr><th colspan=2> options which modify the database table </th></tr>
+<tr><td> [-delete] 		      </td><td> delete the selected images </tr></td> 
+<tr><td> [-modify (options)]	      </td><td> modify certain fields of the selected images </tr></td> 
+</table>
+
+</tr></td> 
+</table>
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/index.htm	(revision 22322)
@@ -0,0 +1,18 @@
+<meta name=file  content=Foobar>
+<meta name=title content=Elixir Database Tools>
+<meta name=page  content=Elixir Database Tools>
+
+This section describes Elixir Database interface tools provided by
+Ohana.  The Elixir database consists of the following tables:
+
+<ul>
+<li> imreg : all of the raw images
+<li> detrend : the master detrend images
+<li> photcode : the nominal filter transformation information
+<li> zeropoints : the measured zero-point data values
+<li> transparency : the measured transpareny over long periods (nights or lunations)
+</ul>
+
+In addition, the DVO program is used to maintain a database of the
+photometric measurements of all detected objects.  DVO is described in
+a separate section.
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/mkfringetable.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/mkfringetable.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/mkfringetable.txt	(revision 22322)
@@ -0,0 +1,35 @@
+
+mkfringetable (layout) (output)
+
+'mkfringetable' generates a MEF FITS ASCII Table of fringe couples
+based on a simple template file.  The layout file defines the basic
+constraints of the fringe couple lists (filter, time range, ccds,
+etc), and identifies the files used for each CCD.  The output file is
+a MEF FITS file with one extension per CCD.  Each extension consists
+of a table with four columns: X_MIN, Y_MIN, X_MAX, Y_MAX.  An example
+layout file is given below.  The primary header unit of the FITS file
+contains the basic information of the CAMERA, FILTER, etc.  
+
+
+Example layout file:
+
+# layout file for mkfringetable
+
+FILTER   I
+CAMERA   megaprime
+TVSTART  1999/9/1
+TVSTOP   2010/1/1
+NCCD     12
+CCD00    00  CFH12K-I-CCD00-FringeCoord.reg
+CCD01    01  CFH12K-I-CCD01-FringeCoord.reg
+CCD02    02  CFH12K-I-CCD02-FringeCoord.reg
+CCD03    03  CFH12K-I-CCD03-FringeCoord.reg
+CCD04    04  CFH12K-I-CCD04-FringeCoord.reg
+CCD05    05  CFH12K-I-CCD05-FringeCoord.reg
+CCD06    06  CFH12K-I-CCD06-FringeCoord.reg
+CCD07    07  CFH12K-I-CCD07-FringeCoord.reg
+CCD08    08  CFH12K-I-CCD08-FringeCoord.reg
+CCD09    09  CFH12K-I-CCD09-FringeCoord.reg
+CCD10    10  CFH12K-I-CCD10-FringeCoord.reg
+CCD11    11  CFH12K-I-CCD11-FringeCoord.reg
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/photcode.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/photcode.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-DB/photcode.htm	(revision 22322)
@@ -0,0 +1,5 @@
+<meta name=file  content=photcode>
+<meta name=title content=photcode>
+<meta name=page  content=photcode>
+
+Sorry!  The page you requested is not yet available.
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/elixir.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/elixir.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/elixir.htm	(revision 22322)
@@ -0,0 +1,437 @@
+<meta name=file  content=gcontrol>
+<meta name=title content=gcontrol: the program-organization program>
+<meta name=page  content=gcontrol>
+
+<b> Overview </b>
+
+<p>
+In a large data analysis project such as Elixir, there are usually
+certain sub-systems which resemble an assembly line: A continuous
+stream of objects, each one very similar to the next, enters a
+factory.  They pass through a series of stages at which some process
+occurs, each step along the way being performed in essentially the
+same way for each object it encounters.  At the end of the assembly
+line, a new object (or objects) appears, fashioned from the original
+that entered at the other end.  
+
+<p>
+Consider, for example, a scientific analysis of images from the
+telescope.  Each image is essentially the same as any other, differing
+only in detail.  A sequence of steps is performed on each image: for
+example, detrending, object detection, and astrometry.  As the
+analysis proceeds, each step performs a transformation on its input
+object, creating a new, slightly modified object.  The result of the
+analysis step may create a new data file, or they may simply alter
+some portion of the file provided.  
+
+<p>
+Because each step in the analysis is identical for every image, there
+is a regularity to the commands which are executed.  For example,
+consider an input science image called <tt> image01.fits </tt>.  The series
+of commands needed to arrive at the final results might consist of:
+
+<pre>
+detrend image01.fits image01.flt
+detect  image01.flt  image01.dat
+astrom  image01.dat  image01.astro
+</pre>
+
+<p>
+Each step in this analysis sequence produces a new file, and each file
+is only different in a portion of the filename.  If the input image
+had been called <tt>image02.fits</tt>, we could easily reconstruct the
+above sequence of commands, replacing <tt>image01</tt> for
+<tt>image02</tt>.
+
+<p>
+We developed the program `elixir' to make it very easy to define a
+sequence of steps which are performed identically for a sequence of
+similar objects in a list.  Elixir is very flexible:  the sequence of
+operations for a given implementation are defined in a simple text
+configuration file, and can consist of essentially any operations
+which fit into the assembly-line model.  Each implementation can be
+drastically different, and need not operated just on images, but any
+anslyis sequence.  We call a specific implementation an `elixir', and
+usually give the elixir a name relevant to its task.
+
+<p>
+Because of the assembly-line nature of the elixir model, it is easy to
+extend the concept from one computer to a network of computers.  Not
+only does `elixir' construct the commands as needed, it also executes
+those commands on any of a collection of computers, keeping track of
+which computers are currently being used and which are currently
+free.  
+
+<p class=figure>
+<img class=figure src=pics/elixir-node.png alt="Elixir node"><br>
+Figure 1: Elixir node
+</p>
+
+<b> List-based command generation </b>
+
+<p>
+To visualize the elixir process, consider each of the analysis steps
+as a node, as shown in Figure 1.  Each node has three queues, an input
+queue of pending objects, a 'success' queue, and a 'failure' queue.
+Each node represents a single command, such as 'detrend' in the
+example above.  The node receives as input a word or a set of words
+which define the object being analysed.  Associated with each node is
+a set of rules to generate the complete command, including possible
+variations on the input words, as necessary.  In the example above,
+the input list might simply consist of the input image names, <tt>
+image01.fits </tt>, <tt> image02.fits </tt>, etc.  The rule for the `detrend'
+node would construct the command <tt> detrend image01.fits
+image01.flt </tt>.
+
+<p>
+Each node in Elixir uses its rule to generate a command, such as <tt>
+detrend image01.fits image01.flt </tt> and start its execution.  When this
+command is finished being executed, the node passes the associated
+words to either the `success' or the `failure' queues, based on the
+command's exit status.  Each success and failure queues are connected
+in turn to the pending queue of another node.  In this way, a
+successful `detrend' command for <tt> image01.fits </tt> would pass the
+word <tt> image01.fits </tt> to its success queue, which is in turn
+connected to the pending queue of the `detect' node.  There is a
+special node called 'global' which represents the entire elixir
+process.  The pending, success, and failure queues of the global node
+represent the global input and output of the elixir analysis.  Thus,
+objects are introduced into the process by placing them in the global
+input queue.  The objects are finished when they reach either the
+global success or failure queues (having a status for the entire
+process of 'success' or 'failure').
+
+<p>
+In order for a node to execute a command, it needs to be given a
+machine on which to run the command.  Elixir maintains a pool of
+available computers.  When a node is ready to execute a command, it
+requests a machine from the pool of free computers.  If none are free,
+it waits for a while then tries again.  If a computer is free, the
+node receives control of the machine, and starts the execution of its
+process.  The node then monitors the output from the machine, saving
+all messages in a log file specific to the current object.
+Eventually, the process finishes, and the node passes the object to
+the appropriate output queue.  At this point, it returns the machine
+to the pool of free machines, and grabs the next object from its
+pending queue, if any.  This process limits the number of executing
+processes to the number of available machines.  This is a very simple,
+but very reliable way of balancing the load on each machine.  As long
+as none of the analysis steps individually overloads the machine
+resources, each machine will be used at an appropriate level.
+
+<p>
+The machines which are available to the Elixir process are simply
+listed in the elixir configuration file.  This makes it easy to change
+the collection of machines used for a given elixir implementation.  In
+practice, if the analysis steps for a particular elixir underload each
+of the machines available to it, multiple entries for each machine can
+be included in the configuration list, increasing the demands on the
+individual machines.
+
+<p class=figure>
+<img class=figure src=pics/elixir-collection.png alt="elixir command example"><br>
+Figure 2: Example elixir command system
+</p>
+
+<b> Elixir configuration </b> 
+
+<p>
+The Elixir configuration file makes if very easy to define a sequence
+of commands and the rules for generating the command line arguments.
+In the configuration file, most lines consist of keyword / value
+pairs.  The keyword can consist of any non-white space.  The value may
+contain any ASCII characters at all, including spaces.  If there are multiple
+entries of the same keyword, the last one provides the value.  
+Keywords defined in the configuration file may be referred to
+elsewhere by appending a dollar sign: $KEYWORD.  The entire
+configuration file is loaded before variables such as this are parsed,
+so order is not important.  
+
+<p>
+In addition to the keyword / value entries, there are a limited number
+of other possible entries.  Lines with a leading hash mark (#) are
+commented out.  A line beginning with the word 'input' is a command to
+load additional configuration information from another file.  In this
+special case, variables used to define the filename must already exist
+in the configuration file loaded so far.  These details about the
+configuration file are relevant to other programs in addition to
+`elixir'.  
+
+<p>
+Finally, we come to the portion of the configuration file which
+defines the components of the specific elixir implementation.  Each
+node in the elixir process is defined by a block which looks like
+this:
+
+<pre>
+process detrend
+detrend.arg      0        detrend
+detrend.arg      5 %s     &0
+detrend.arg      0 %s.flt BASE(&0) 
+detrend.success           detect
+detrend.failure	          global
+</pre>
+
+<p>
+The first line here, <tt> process detrend </tt>, defines the node.  The
+next three lines, beginning with <tt> detrend.arg </tt>, define the syntax
+of the command associated with that node.  The first entry is the
+program, the next two lines generate the first and second argument to
+the program.  The executed command (<tt> detrend </tt>) and the name of the
+node are not required to be the same.  It is particularly important to
+note that the order of these lines in the configuration file matters:
+the order of the command-line arguments is the order of the lines in
+the configuration file.  
+
+<p>
+These lines define how to generate the arguments by providing a
+C-style format statement and a set of arguments to the format
+statement.  Let us first consider the input list.  The input list to
+Elixir describes a set of objects to be analysed.  In our example
+above, these consisted of the names of the raw science images:
+
+<pre>
+image01.fits
+image02.fits
+image03.fits
+</pre>
+
+and so on. In this example, there is only one word on each line to
+describe the objects.  It is possible to have multiple words to
+describe a single object.  For example, to define a specific CCD image
+in both MEF & SPLIT style images, one could use lines like:
+
+<pre>
+image01.fits X SPLIT
+image.fits 00 MEF
+image.fits 01 MEF
+image.fits 02 MEF
+image03.fits X SPLIT
+</pre>
+
+In this example, the first word is a file name, the second defines the
+CCD for MEF images (and is ignored for SPLIT), the third identifies
+the image type, SPLIT vs MEF.  In the elixir configuration file, the
+first word on each line is identified by &0, the second by &1, etc.
+Thus, in the <tt> process detrend </tt> example above, the line which reads
+<tt> detrend.arg 5 %s &0 </tt> constructs an argument which just consists
+of the first word for each line.  
+
+<p>
+The following line shows the use of the filename manipulation
+functions available within the Elixir configuration system.  These
+functions are only available when defining the Elixir node command
+line arguments.  In this example, <tt> detrend.arg 0 %s.flt
+BASE(&0) </tt>, the argument is constructed by taking the first word
+(&0), stripping off the path and extension from the file name (ie,
+taking /path/file.ext and returning 'file'), and appending the string
+'.flt' to that word.  This de-construction of the filename is provided
+by the BASE function.  Other string manipulation functions are
+available, mostly functions relevant to manipulating file names.  Some
+of the available functions are: 
+
+<ul>
+<li> BASE   -  return file basename
+<li> EXT    -  return file extension
+<li> PATH   -  return file pathname
+</ul>
+
+Other functions will be added as they become necessary.
+
+<p>
+These configuration formatting lines are somewhat limited compared
+with standard C formatting commands: There can only be one word
+defined by the format (ie., no white space is allowed).  The only
+formatting command currently allowed is %s.  
+
+<p>
+The only remaining portion of these format lines which has not yet
+been described is the number in the second position.  The number in
+the second space on each line represents part of the flow control
+process.  A positive definite number here says that the word formed by
+this line is a filename which must exist before the process should be
+run.  The number tells how many seconds elixir should wait for the
+object to appear before giving up on this object.  This timeout is
+provided for various possible uses.  One basic use is to avoid NFS
+latency problems.  It is not unusual under NFS for a file created on
+one machine not to appear on a cross-mounted disk on a second machine
+for some tenths of seconds or so.  
+
+<p>
+The final two lines which define this elixir node are used to make the
+connection between the node and other nodes.  The first, <tt>
+detrend.success detect </tt>, tells elixir to connect the success queue of the
+detrend process to the pending queue of the `detect' process.  The
+second line, <tt> detrend.failure global </tt>, tells elixir to connect the
+failure queue of this process to the global failure queue.  Similarly,
+the success queue of the last node should be connected to the global
+success queue.  
+
+<p>
+There is also a set of configuration entries which define global
+concepts for the elixir, including the actions of the global input /
+output queue.  The required entries of this type of listed here:
+<pre>
+global.success	/path/analysis.success
+global.failure	/path/analysis.failure
+global.source	/path/analysis.source
+global.msg	/path/analysis.msg
+global.end	/path/analysis.end
+global.Nargs	3
+global.logfile  0 %s.log  BASE(&0)
+global.pending	detrend
+global.timeout  1200.0
+</pre>
+
+<p>
+The first two entries, <tt> global.success </tt> and <tt>
+global.failure </tt>
+define the output files for the globals success and failure queues.
+When an object lands on either of these queues, the object and the
+exit status are written to the named file, and the object is marked as
+being completed.  The entry <tt> global.pending </tt> defines the first
+node in the process.  <tt> global.timeout </tt> defines a timeout period in
+seconds: if a process provides not output for this much time, elixir
+decides that it has hung and kills it.  The entry <tt> global.logfile </tt>
+defines the rules for generating a log file name for each object,
+using a syntax identical to the command-line argument generation
+lines.  <tt> global.end </tt> is a file to which elixir writes a collection
+of processing statistics after a complete analysis is done.   <tt>
+global.msg </tt> provides a way for elixir to communicate with other
+processes.  Finally, we leave the description of <tt> global.source </tt>
+until after we describe how objects are passed to Elixir.
+
+<b> Elixir Communication Issues </b>
+
+<p>
+The input to an elixir process is a list consisting of lines with
+words which define the specific objects, such as a list of image
+names.  There are two ways in which elixir may be presented such a
+list.  In the simple case, when the program is started, a filename is
+given as a command line argument.  This file contain a complete list
+of names for the elixir run.  The lines from this list are loaded and
+passed to the global.pending queue, which in turn passes them to the
+first node.  Once the elixir process has finished with all of the
+entries in the file, it exits, and the process is complete.
+
+<p>
+The other possibility is to use a mechanism we call an input FIFO.  In
+our implementaton, a FIFO is not a standard UNIX fifo-type special
+file.  We have avoided the use of true fifos for two reasons.  First,
+it would be necessary for a special file to be created for each
+implementation of elixir (by root), which makes it a non-trival task
+to create a new elixir implementation.  Second, the data in a standard
+UNIX fifo is ephemeral.  If the programs on either end of the fifo are
+not running, there is no guarantee that the data will remain (the
+situation with socketed connections is even worse).  Instead, we have
+chosen to implement a type of FIFO by using a normal UNIX file which
+one program writes to and another reads the data from.  To avoid
+conflicts between programs, we simply lock the file each time we are
+accessing it.  Messages are passed to Elixir using this mechanism, and
+so are the lines which define new objects.
+
+<p>
+If elixir is not invoked with a list file on the command line, it
+instead monitors the file defined by <tt> global.source </tt>.  Anytime
+elixir looks for object entries in this file, the file is locked
+first.  Then, when the lines have been loaded, the file is cleared.
+In this way, a second program can add lines to this file at arbitrary
+times without danger of losing any entries, whether or not the
+particular elixir is already running.  When the external program
+writes to the file, it also locks it, so elixir will not load the
+entries before it is ready. Then, when it has written the entries, the
+file is unlocked, ready for elixir to grab the new list of names.  
+
+<p>
+In this mode, elixir runs continuously, waiting for more entries in
+the FIFO file.  A mechanism to end the elixir run is made possible by
+having elixir recognized a special word in the FIFO.  If elixir
+encounters the word EOF, it will no longer accept input from the FIFO,
+and will exit when all of the entries it has already loaded have been
+processed.  The same goal can be accomplished by passing the elixir a
+message saying 'STOP' via the message FIFO.
+
+<p>
+Elixir runs like a daemon, in the background with no output directly
+to the screen (except for serious errors).  It is possible to monitor
+the progress of the elixir run by communicating through the message
+FIFO.  Elixir monitors the message fifo for several specific requests,
+and responds to them as they arrive.  Typically a request will include
+a command, such as STATUS and a filename, where the requested
+information should be placed.  Other messages include 'STOP' (halt
+processing and exit when all objects have been processed), 'KILL'
+(halt all processing immediately), and 'TIME' (provide a set of
+statistics on process times).
+
+<p>
+Elixir uses either the rsh or ssh commands to make the connections to
+the remote machines.  All machines are treated as remote, even the
+machine on which elixir is run.  Elixir forks off a process which logs
+into the remote machine and starts a new shell (csh).  The STDIN,
+STDOUT, and STDERR connections are used for communication between the
+remote shell and elixir.  Programs are started by executing the
+command in the shell. 
+
+<p>
+The configuration file used by Elixir may also contain configuration
+information for a variety of other programs.  Elixir saves a copy of
+the complete configuration and passes this filename to those programs
+which can interpret it with the PTOLEMY environment variable.  This
+name tells the Elixir programs what configuration file to load.  By
+specifying this value as an environment variable, it will override
+other choices so that elixir can guarantee that the programs it
+launches have a consitent, known set of configuration choices.  
+
+<p>
+The other unique feature in the command line aids the process control.
+Every command is followed by an echo of the words "PROCESS DONE".  As
+the programs run, elixir monitors the output stream looking for three
+special phrases.  One is this 'PROCESS DONE' phrase.  Another is the
+word "SUCCESS" and the third is the word "ERROR".  By looking for
+combinations of these words, elixir can determine if the program ended
+successfully, failed, or if the computer crashed.  To interact well
+with Elixir, programs should send the correct word "SUCCESS" or
+"ERROR" on exit.  If necessary, one can wrap the program in a shell
+which monitors the exit status and sends the correct word.  If this is
+not possible, it is always possible to assume the process always ends
+successfully and include these words as an echo on the command line.
+
+<font size=-1>
+
+<p>
+<b> A short note about our use of filelocks </b>.  We have implemented a
+somewhat complex type of file lock mechanism.  We do not want to use a
+standard NFS implemented lock, particularly for the locks on our
+database files.   The problem the standard filelocking mechanism under
+UNIX/NFS is that the lock only exists if the program holding the lock
+is running.  This is insufficient to maintain data integrity.
+Consider a database which consists of several distinct files.  A
+particular write operation to the database may need to manipulate more
+than one file, and it needs to be sure those files are not also
+changed by another program in the meantime.  To avoid this, it should
+set a lock (on the files or on the whole database).  Then it should
+write to the files, then it should clear the lock.  Consider, though,
+what would happen in the program were to crash or be killed after
+writing the first file, but before writing the second.  The data in
+the two would be inconsistent.  At that point, if a second program
+tried to write to the database, it could easily corrupt the data,
+making it rather difficult to correct the problem.  With the standard
+UNIX/NFS locks, the lock is ephemeral - it only exists as long as the
+program is running (and certainly only while the machine is up).  In
+this example, it is easy to imagine that, by the time the second
+program comes along, the files are out of sync but the lock is no
+longer set.  We wanted a lock that would be guaranteed to exist until
+it was actively cleared, either by the program in its usual way, or by
+a person (or program) which has found the problem and fixed it.   
+
+<p>
+To implement such a lock, we create a lock in two stages.  The file
+that is being locked (ie, filename) is associated with the lockfile of
+the form .filename.lck.  The lockfile is locked with NFS style locks.
+Then the word BUSY is written to the lockfile.  At this point the NFS
+lock doesn't matter, and can be cleared or left.  Even if the program
+dies, the word BUSY remains to prevent another program from taking the
+lock.  When it is time to clear the lock, the file is either deleted
+or the word IDLE is written to it.  
+
+</font>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/index.htm	(revision 22322)
@@ -0,0 +1,15 @@
+<meta name=file  content=Foobar>
+<meta name=title content=Elixir System Infrastructure>
+<meta name=page  content=Elixir System Infrastructure Components>
+
+This section describes the Ohana programs used to tie the Elixir
+System together.  These include
+
+<ul>
+<li> elixir : the parallel-processing program organization tool
+<li> mkdetrend : the detrend analysis tool
+<li> mkfringe : the fringe-frame analysis tool
+<li> nightd : the nightly process launching system
+<li> postrun : the end-of-run analysis management tool
+<li> gconfig : the Elixir configuration database interface tool
+</ul>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/mkdetrend.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/mkdetrend.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/mkdetrend.htm	(revision 22322)
@@ -0,0 +1,69 @@
+<meta name=file  content=mkdetrend>
+<meta name=title content=mkdetrend User's Guide>
+<meta name=page  content=mkdetrend>
+
+<p>
+mkdetrend is the interface to the Elixir detrend creation system.  
+
+<p>
+All date/times are in the form YYYY/MM/DD,HH:MM:SS, with HH:MM:SS optional.  
+
+<p>
+By default, commands apply to the currently active camera/run, but
+these may be specified on the command line with the -camera (camera)
+and -run (run) flags.
+
+<h2> primary user commands </h2>
+
+<table>
+<tr><td> create (camera) (run) (start) (stop) </td><td> create a new mkdetrend run for the given camera, normally automatically performed by ert. </td></tr>
+<tr><td> create run (runid)                   </td><td> create a new mkdetrend run based on the mkrun table information. </td></tr>
+<tr><td> config (camera) (run)                </td><td> set the current active config </td></tr>
+<tr><td> state                                </td><td> show the state of the active config </td></tr>
+<tr><td> init                                 </td><td> start the init stage of mkdetrend processing </td></tr>
+<tr><td> run                                  </td><td> start the main processing, or re-processing </td></tr>
+<tr><td> reg                                  </td><td> register the completed images in the detrend database </td></tr>
+<tr><td> clean                                </td><td> clean the camera/run directories; recovery from this command will require a complete reprocessing. </td></tr>
+<tr><td> reset (level)                        </td><td> reset the configs. if level is 'hard', all will be reset.  if level is 'update', configs may be reset to 'init' or 'update', depending on their state.  </td></tr>
+<tr><td> update                               </td><td> perform an update run (identify new images, process residuals, but do not generate master). </td></tr>
+<tr><td> auto (cmd)                           </td><td> automatic processing commands </td></tr>
+</table>
+
+<h2> low-level user comands </h2>
+<table>
+<tr><td> dup (config)                         </td><td> duplicate the given config </td></tr>
+<tr><td> del (config)                         </td><td> delete the given config </td></tr>
+<tr><td> def (config) (start) (stop)          </td><td> set the start & stop times for the given config </td></tr>
+<tr><td> set (config) (state)                 </td><td> manually set the config state </td></tr>
+<tr><td> list.init                            </td><td> run just the list.init stage </td></tr>
+<tr><td> split                                </td><td> run just the split stage </td></tr>
+<tr><td> flips                                </td><td> run just the flips stage </td></tr>
+<tr><td> norm                                 </td><td> run just the norm stage </td></tr>
+<tr><td> scat                                 </td><td> run just the scat stage </td></tr>
+<tr><td> merge                                </td><td> run just the merge stage </td></tr>
+<tr><td> update.list                          </td><td> run just the update.list stage </td></tr>
+<tr><td> update.stats                         </td><td> run just the update.stats stage </td></tr>
+</table>
+
+<h2> HTML cgibin commands </h2>
+<table>
+<tr><td> htmldup </td><td>  </td></tr>
+<tr><td> htmldef </td><td>  </td></tr>
+<tr><td> htmldel </td><td>  </td></tr>
+<tr><td> htmlmod </td><td>  </td></tr>
+<tr><td> htmlkeep </td><td>  </td></tr>
+<tr><td> htmlconfig </td><td>  </td></tr>
+<tr><td> html1 </td><td>  </td></tr>
+<tr><td> html2 </td><td>  </td></tr>
+<tr><td> html3 </td><td>  </td></tr>
+</table>
+
+<h2> other support commands </h2>
+<table>
+<tr><td> dads </td><td>  </td></tr>
+<tr><td> dads.top </td><td>  </td></tr>
+<tr><td> meval </td><td>  </td></tr>
+<tr><td> eval </td><td>  </td></tr>
+<tr><td> fix.masters </td><td>  </td></tr>
+<tr><td> check.splits </td><td>  </td></tr>
+</table>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/mkfringe.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/mkfringe.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/mkfringe.htm	(revision 22322)
@@ -0,0 +1,69 @@
+<meta name=file  content=mkfringe>
+<meta name=title content=mkfringe User's Guide>
+<meta name=page  content=mkfringe>
+
+<p>
+mkfringe is the interface to the Elixir fringe creation system.  
+
+<p>
+All date/times are in the form YYYY/MM/DD,HH:MM:SS, with HH:MM:SS optional.  
+
+<p>
+By default, commands apply to the currently active camera/run, but
+these may be specified on the command line with the -camera (camera)
+and -run (run) flags.
+
+<h2> primary user commands </h2>
+
+<table>
+<tr><td> create (camera) (run) (start) (stop) </td><td> create a new mkfringe run for the given camera, normally automatically performed by ert. </td></tr>
+<tr><td> create run (runid)                   </td><td> create a new mkfringe run based on the mkrun table information. </td></tr>
+<tr><td> mkconfig (camera) (run)              </td><td> create an mkfringe config table based on an existing mkdetrend date range </td></tr>
+<tr><td> config (camera) (run)                </td><td> set the current active config </td></tr>
+<tr><td> state                                </td><td> show the state of the active config </td></tr>
+<tr><td> init                                 </td><td> start the init stage of mkfringe processing </td></tr>
+<tr><td> run                                  </td><td> start the main processing, or re-processing </td></tr>
+<tr><td> reg                                  </td><td> register the completed images in the detrend database </td></tr>
+<tr><td> map                                  </td><td> start the map run (requires manually setting state to map) </td></tr>
+<tr><td> map.reg                              </td><td> register mode maps </td></tr>
+<tr><td> clean                                </td><td> clean the camera/run directories; recovery from this command will require a complete reprocessing. </td></tr>
+</table>
+
+<h2> low-level user comands </h2>
+<table>
+<tr><td> dup (config)                         </td><td> duplicate the given config </td></tr>
+<tr><td> del (config)                         </td><td> delete the given config </td></tr>
+<tr><td> def (config) (start) (stop)          </td><td> set the start & stop times for the given config </td></tr>
+<tr><td> set (config) (state)                 </td><td> manually set the config state </td></tr>
+<tr><td> list.init                            </td><td> run just the list.init stage </td></tr>
+<tr><td> list.reinit                          </td><td> run just the list.reinit stage </td></tr>
+<tr><td> detrend                              </td><td> run just the detrend stage </td></tr>
+<tr><td> merge                                </td><td> run just the merge stage </td></tr>
+<tr><td> mkrough                              </td><td> run just the mkrough stage </td></tr>
+<tr><td> defringe                             </td><td> run just the defringe stage </td></tr>
+<tr><td> mksmooth                             </td><td> run just the mksmooth stage </td></tr>
+<tr><td> regimage                             </td><td> run just the regimage stage </td></tr>
+</table>
+
+<h2> HTML cgibin commands </h2>
+<table>
+<tr><td> htmldup </td><td>  </td></tr>
+<tr><td> htmldef </td><td>  </td></tr>
+<tr><td> htmldel </td><td>  </td></tr>
+<tr><td> htmlmod </td><td>  </td></tr>
+<tr><td> htmlkeep </td><td>  </td></tr>
+<tr><td> htmlconfig </td><td>  </td></tr>
+<tr><td> html1 </td><td>  </td></tr>
+<tr><td> html2 </td><td>  </td></tr>
+<tr><td> html3 </td><td>  </td></tr>
+<tr><td> htmlmodes </td><td>  </td></tr>
+<tr><td> htmlmaps </td><td>  </td></tr>
+<tr><td> htmlmapkeep </td><td>  </td></tr>
+</table>
+
+<h2> other support commands </h2>
+<table>
+<tr><td> dads </td><td>  </td></tr>
+<tr><td> dads.top </td><td>  </td></tr>
+<tr><td> eval </td><td>  </td></tr>
+</table>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/nightd.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/nightd.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/nightd.htm	(revision 22322)
@@ -0,0 +1,94 @@
+<meta name=file  content=nightd>
+<meta name=title content=nightd user's guide>
+<meta name=page  content=nightd>
+
+<p>
+The nightd daemon is a easily-customized daemon which performs a
+defined set of tasks on a nightly basis.  The program behavior is
+defined by the resource file, loaded at the start of execution.  The
+program can have any name, and loads the resource file with the name
+~/.programrc; ie, the implementation used by elixir to control the
+night elixir real-time systems is called 'ert' and it loads the file
+~/.ertrc.  
+
+<pre>
+HOME      /h/skyprobe
+
+DATA_PATH $HOME/data
+PID_FILE  $HOME/.skyprobe.pid
+LOG_FILE  $HOME/sp_daemon.log
+
+CCD_TEMP -20
+EXPTIME   30
+
+INIT_COMMAND sp_command cool $CCD_TEMP
+INIT_COMMAND sp_command init $DATA_PATH/&DATE
+MAIN_COMMAND sp_command expose $EXPTIME $DATA_PATH/&DATE/sp_&DATE_&TIME.fits
+DONE_COMMAND sp_command warm
+
+NIGHT_START 18:00
+NIGHT_STOP  06:00
+
+PERIOD 60
+TIMEOUT 300
+</pre>
+
+<p>
+Above is shown a typical nightd configuration script.  The nightd
+configuration defines commands and execution time scales.  The start
+and end times of the night are defined by the entries NIGHT_START and
+NIGHT_STOP.  The nightd configuration script defines three classes of
+commands: INIT, MAIN, DONE.  The INIT commands are executed in the
+order listed in the configuration script at the start of the night,
+while the DONE commands are executed at the end of the night.  The
+MAIN commands are executed on a regular basis from START to STOP using
+the interval defined by PERIOD (seconds).  The program monitors these
+processes and checks for completion within the TIMEOUT period;
+otherwise, the command is sent the kill signal.  
+
+<p>
+The entries PID_FILE and LOG_FILE are required entries. The PID_FILE
+stores the process id, user and machine for the given nightd
+implementation, and prevents multiple intances of the same process.
+The LOG_FILE is used for all error messages.  The other entries in the
+configuration script define variables to be use elsewhere in the
+script.  There are a few special variables: \&DATE, \&TIME, ???, which
+are expanded by nightd before the execution.
+
+<p>
+The nightd program takes several possible arguments on the command
+line:
+
+<pre>
+nightd start
+nightd stop
+nightd status
+nightd config
+</pre>
+
+<p>
+The 'start' command starts up the nightd program.  Currently, nightd
+does not fork itself into the background, so it is necessary to place
+it in the background manually (nightd start &).  It is not necessary
+to redirect the output; nightd will send all data to LOG_FILE if it
+can be opened and written to.  On start, nightd will check for the
+existence of the PID_FILE and give an error to stdout (not LOG_FILE)
+if it exists; only one implementation may run at a time.  Currently,
+nightd does not check for the existence of the process identified in
+the PID_FILE.  It is necessary for the user to confirm that such a
+process exists on the named machine.
+
+<p>
+The 'stop' command halts execution of the given nightd program.  It
+uses the information in the PID_FILE to remotely log onto the machine
+where the process is being executed and send the STOP signal.  Nightd
+will give an error if there is no PID_FILE, implying no currently
+running nightd.  It is necessary to have rsh access between machines
+for this to work.
+
+<p>
+The status command shows the pid information for the currently running
+nightd, or says that it is not running.
+
+<p>
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/postrun.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/postrun.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/postrun.htm	(revision 22322)
@@ -0,0 +1,58 @@
+<meta name=file  content=postrun>
+<meta name=title content=Elixir Postrun Systems>
+<meta name=page  content=Elixir Postrun Systems>
+
+The Elixir postrun system (<tt>elixir.postrun</tt>) manages the
+end-of-run processing of the calibration and science data.  The
+command <tt>elixir.postrun</tt> manages the stages of the processing.
+The elixir.postrun commands are as follows: <br><br>
+
+<tt>
+<table>
+<tr><td>elixir.postrun state               </td><td> check the current postrun status </td></tr>
+<tr><td>elixir.postrun update              </td><td> update the status, start the next stage </td></tr>
+<tr><td>elixir.postrun set (entry) (value) </td><td> set the state for a specific entry </td></tr>
+<tr><td>elixir.postrun (entry)             </td><td> run a specific entry </td></tr>
+</table>
+</tt>
+
+<p>
+The Elixir postrun system consists of a series of processing stages,
+illustrated by the command <tt> elixir.postrun state </tt>: <br><br>
+
+<tt>
+<table>
+<tr><td> imstats   </td><td> process the unprocessed entries in the imstats database </td></tr>
+<tr><td> mkdetrend </td><td> run the mkdetrend system to generate the detrend data </td></tr>
+<tr><td> mkfringe  </td><td> run the mkfringe system to generate the fringe data </td></tr>
+<tr><td> ptolemy   </td><td> run the science images through the ptolemy system </td></tr>
+<tr><td> standards </td><td> process the standard stars and generate zero-point information </td></tr>
+<tr><td> validate  </td><td> perform the image validation stages </td></tr>
+</table>
+</tt>
+
+<p>
+These stages must be performed in sequence, and the postrun system
+automatically enforces the sequencing.  At the beginning of a run,
+each of these stages are in the state 'init'.  At any stage, the
+command <tt>elixir.postrun update</tt> will attempt to identify the
+state of each of the processing stages, and run the first which is
+ready in the init state.  While stages are running, elixir.postrun
+will set their state to 'running', and as the stages finish, they are
+set to the value 'done'.  
+
+<p> In fact, the description above is an ideal, but in practice, the
+elixir.postrun script does not quite follow these rules.  For example,
+elixir.postrun will not run the mkdetrend or mkfringe systems; these
+must be run manually with the mkdetrend and mkfringe commands.  In
+addition, the mkfringe and ptolemy systems are required to be run in
+sequence by elixir.postrun, but these do not really require this
+sequencing: ptolemy can be run independently of mkfringe since ptolemy
+does not require the fringe frames.  
+
+<p> Furthermore, the error checking in the elixir.postrun system is
+not very well refined, and manual intervention is occasionally needed.
+It is possible, therefore, to set the state to appropriate values as
+needed with the elixir.postrun set (entry) (value) command.  Also, the
+elixir.postrun (entry) command can be used to run the specific stage
+manually.
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/sequence.idx
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/sequence.idx	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-System/sequence.idx	(revision 22322)
@@ -0,0 +1,6 @@
+elixir
+mkdetrend
+mkfringe
+nightd
+postrun
+gconfig
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/applyscat.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/applyscat.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/applyscat.htm	(revision 22322)
@@ -0,0 +1,3 @@
+applyscat takes a raw master flat-field image and a scatter correction
+frame and constructs a corrected flat-field image. 
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/fhead.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/fhead.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/fhead.htm	(revision 22322)
@@ -0,0 +1,12 @@
+<pre>
+  fhead (fitsfile) ... [-x N]
+
+  print out the FITS header, adding return chars to the end of each
+  line, stopping at the END (not printed).  By default, the primary
+  header unit is printed.  If the -x is provided the Nth extension's
+  header unit is printed, with 0 being the first extension after the
+  PHU.  Multiple files may be provided, in which case the headers are
+  printed in sequence, with tags between the headers.
+
+
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/fields.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/fields.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/fields.htm	(revision 22322)
@@ -0,0 +1,10 @@
+<pre>
+  fields (key) ... [-x N]
+
+  Read from stdin a list of file names.  For each file name, print in
+  sequence the value of the given FITS header keywords.  Any number of
+  keys may be present.  By default, the key/values are taken from
+  primary header unit.  If the -x is provided the Nth extension's
+  header unit is used instead, with 0 being the first extension after
+  the PHU.
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/fits_insert.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/fits_insert.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/fits_insert.htm	(revision 22322)
@@ -0,0 +1,17 @@
+<pre>
+  fields (file) (header) [-X N] [-comment N line]
+
+  Add header entries to the given file.  The header information to be
+  inserted is read from the provided header file, which contains
+  abbreviated FITS header lines:  each line must consist of a keyword
+  and data value, following the FITS conventions, but lines are broken
+  with returns, and there are no required keys or minimum number of
+  lines.  By default, the primary header unit is used, but if the -X
+  flag is used, the Nth extension is used instead, where 0 corresponds
+  to the first extension after the PHU.  If the -comment option is
+  provided, the Nth comment is replaced with the provided line.  By
+  default, this function will search for blank lines and overwrite the
+  data in-situ.  However, if it is necessary to extend the header, a
+  new file will be written.
+
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/ftable.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/ftable.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/ftable.htm	(revision 22322)
@@ -0,0 +1,24 @@
+<pre>
+  ftable (filename) [options]
+
+  options:
+  -x N:       operate on extension N (0 is default)
+  -n EXTNAME: operate on named extension (incompatible with -x)
+  -row N:     print row number N
+  -column n:  print column named n
+  -ncolumn N: print column number N
+  -list:      print extension names
+  -layout     print the table layout (fields)
+
+  Print out FITS table data, or FITS table header data.  By default,
+  the entire FITS table is printed.  Command-line options may be used
+  to restrict the output to a single column or single row.  Additional
+  options may be used to list all extension names or to print out the
+  complete table fields for the selected extension.  By default, the
+  key/values are taken from the first extension.  If the -x is
+  provided the Nth extension's header unit is used instead, with 0
+  being the first extension after the PHU.  If the -n option is
+  provided, the named extension (matching keyword EXTNAME) is used.  
+
+
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/gastro.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/gastro.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/gastro.htm	(revision 22322)
@@ -0,0 +1,69 @@
+<meta name=file  content=gastro>
+<meta name=title content=gastro>
+<meta name=page  content=gastro>
+
+<p>
+The gastro program determines the astrometric transformation for an
+image based on the cmp/smp file.  This file contains both the table of
+object data and the complete image FITS header.  Using the header
+astrometric information as a starting point, gastro loads the
+reference astrometry objects appropriate for the region and determines
+the matching objects between the image and the reference.  These
+objects are used, with iteration to improve the sample, to determine
+the astrometric transformation with up to 3rd order polynomials.  
+
+<pre>
+gastro filtname.cmp
+options:
+  -h
+  -help
+  -v
+  -p (photcode)
+  -plot
+  -maglims
+  -nstars
+  -fx
+  -fy
+  -coords (ra) (dec)
+  -dump
+  -mdmp
+  -cdmp
+  -loneos
+
+gastro configuration variables:
+OFFSET_RADIUS     - max allowed offset in gcenter
+MIN_MATCHES       - min number of matched stars for linear fit
+MINIMUM_RADIUS    - target matching radius in gfit iterations
+MAX_ERROR         - maximum allowed error in solution
+MAX_NONLINEAR     - maximum angle between axes
+MAX_PRECISE       - unused?
+DEFAULT_RADIUS    - unused?
+
+ROUGH_ASTROMETRY  - source of initial astrometry terms
+
+CCD_PC1_1         - initial guess pc1_1
+CCD_PC2_2         - initial guess pc2_2
+CCD_PC1_2         - initial guess pc1_2
+CCD_PC2_1         - initial guess pc2_1
+ASEC_PIX          - initial guess arcsec / pix
+
+POLAR_ALIGNMENT   - provide polar alignment of telescope mount?
+POLAR_AXIS_RA     - RA of telescope polar axis
+POLAR_AXIS_DEC    - DEC of telescope polar axis
+RA_OFFSET         - RA offset
+DEC_OFFSET        - DEC offset (degenerate?)
+
+NFIELD            - select stars from region NFIELD x detector size
+NPOLYTERMS	  - number of orders to allow
+MMIN              - minimum magnitude of allowed star 
+ROT_ZERO          - zero of tested rotation range
+dROT              - rotation step size
+NROT              - number of rotation tests = 2NROT + 1
+GSCFILE           - name of HST GSC table file
+GSCDIR            - name of HST GSC data directory
+LONEOS_REGIONS    - table of location coordinates
+USNO_CDROM        - name of USNO data directory
+ASTRO_REFCAT      - reference catalog format to read
+CATDIR            - name of ptolemy database
+PHOTCODE_FILE     - table of photcodes 
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/gastro2.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/gastro2.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/gastro2.htm	(revision 22322)
@@ -0,0 +1,68 @@
+<meta name=file  content=gastro2>
+<meta name=title content=gastro2>
+<meta name=page  content=gastro2>
+
+<p>
+gastro2 is the successor to gastro.  The gastro2 program determines
+the astrometric transformation for an image based on the cmp/smp file.
+This file contains both the table of object data and the complete
+image FITS header.  Using the header astrometric information as a
+starting point, gastro2 loads the reference astrometry objects
+appropriate for the region and determines the matching objects between
+the image and the reference.  These objects are used, with iteration
+to improve the sample, to determine the astrometric transformation
+with up to 3rd order polynomials.
+
+<pre>
+gastro filtname.cmp
+options:
+  -h
+  -help
+  -v
+  -p (photcode)
+  -plot
+  -maglims
+  -nstars
+  -fx
+  -fy
+  -coords (ra) (dec)
+  -dump
+  -mdmp
+  -cdmp
+  -loneos
+
+gastro2 configuration variables:
+OFFSET_RADIUS     - unused
+MIN_MATCHES	  - unused
+DEFAULT_RADIUS	  - unused
+MINIMUM_RADIUS	  -
+MAX_ERROR	  -
+MAX_NONLINEAR	  -
+MAX_PRECISE	  -
+CCD_PC1_1	  -
+CCD_PC2_2	  -
+CCD_PC1_2	  -
+CCD_PC2_1	  -
+ASEC_PIX	  -
+NFIELD		  -
+NPOLYTERMS	  -
+ROT_ZERO	  -
+dROT		  -
+NROT		  -
+POLAR_ALIGNMENT	  -
+GSCFILE		  -
+GSCDIR		  -
+2MASS_DIR         -
+POLAR_AXIS_RA     -
+POLAR_AXIS_DEC    -
+RA_OFFSET         -
+DEC_OFFSET        -
+LONEOS_REGIONS    - 
+USNO_CDROM        -
+ASTRO_REFCAT      - 
+CATDIR            - 
+ROUGH_ASTROMETRY  - 
+PHOTCODE_FILE     -
+GASTRO_MAX_NSTARS -
+
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/gcompare.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/gcompare.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/gcompare.htm	(revision 22322)
@@ -0,0 +1,21 @@
+<meta name=file  content=gcompare>
+<meta name=title content=gcompare>
+<meta name=page  content=gcompare>
+
+<p>
+The gcompare program determines compares two lists of objects with X,Y
+coordinates and prints the matched entries.  A radius of matched is
+given, and all pairs of objects between the two lists which lie within
+the given radius are printed.  The delta coordinates may also be
+optionally printer, or the unmatched objects in either the first or
+second list.  An offset for the center can be optionally applied.
+
+<pre>
+gcompare file1 Nx Ny file2 Nx Ny radius [options]
+options:
+ -d
+ -m
+ -n1
+ -n2
+ -c x y
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/glockfile.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/glockfile.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/glockfile.htm	(revision 22322)
@@ -0,0 +1,22 @@
+<pre>
+  glockfile (filename) (type) (duration)
+
+  lock the given file, using the Elixir locking functions, for the
+  specified number of seconds.  The lock type may be SOFT, XCLD, or
+  HARD.  These have the following meanings:
+
+  SOFT : read-only lock; will not block a SOFT lock.  will only last
+         for the duration of the program.
+
+  XCLD : write lock; blocks, and is blocked by, all lock types.  will
+         only last for the duration of the program.
+
+  HARD : write lock; blocks, and is blocked by, all lock types.  If
+  the program is interrupted, the lock will remain until cleared by
+  hand (by deleting the file with the name .filename.lck, where
+  'filename' is replaced the the name of the locked file.
+
+
+  
+  
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/imclean.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/imclean.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/imclean.htm	(revision 22322)
@@ -0,0 +1,24 @@
+<meta name=file  content=imclean>
+<meta name=title content=imclean>
+<meta name=page  content=imclean>
+
+<p>
+The imclean program converts the basic photometry output list from
+dophot, sextractor, or gophot, into a standard format for other Elixir
+subsystems, with both the table of object data and the complete image
+FITS header. 
+
+<pre>
+imclean filename.fits filename.obj filtname.cmp
+imclean -sex filename.fits filename.sx filename.smp
+options:
+  -h
+  -help
+  -v
+  -coords (ra) (dec)
+  -p (photcode)
+  -astrom (file)
+  -sex
+  -chad
+  -key KEYWORD VALUE
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/index.htm	(revision 22322)
@@ -0,0 +1,9 @@
+<meta name=file  content=Foobar>
+<meta name=title content=Elixir Tools>
+<meta name=page  content=Elixir Tools>
+
+This section describes basic Elixir tools provided by Ohana.  These
+include a set of FITS file interaction programs (fields, fhead,
+ftable, fits_insert), a few data analysis tools (gastro, gastro2,
+mosastro, imclean), and a variety of other useful for astronomy and
+data-handling programs.
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/list_astro.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/list_astro.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/list_astro.htm	(revision 22322)
@@ -0,0 +1,20 @@
+<pre>
+  list_astro
+
+  read a series of lines consisting of 4 columns.  the columns
+  represent the X,Y coordinate of a series of objects in first one
+  coordinate frame, then in a second coordinate frame.  list_astro
+  will return the linear transformation (and errors) from the first
+  frame to the second frame in the form:
+
+  X0 X1 X2 dX
+  Y0 Y1 Y2 dY
+
+  where
+
+  X = X0 + X1*x + X2*y
+  Y = Y0 + Y1*x + Y2*y
+
+  and dX and dY are the errors in the output coordinate frame.
+
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/medianfilter.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/medianfilter.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/medianfilter.htm	(revision 22322)
@@ -0,0 +1,13 @@
+<pre>
+  medianfilter output (fmin) (fmax) 
+
+  Read a series of image names from standard input.  The input images
+  must all be the same dimensions, and are expected to correspond to
+  matching pixels.  For each pixel, the data values are sorted.  The
+  data values within the range (fmin) to (fmax) are selected, where
+  (fmin) and (fmax) are fractions between 0 and 1, and represent the
+  fraction of the sorted stack of pixels.  The mean of the selected
+  data within this range is then calculated.  The output file consists
+  of pixels with these output values.  
+
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/mktemp.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/mktemp.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/mktemp.htm	(revision 22322)
@@ -0,0 +1,12 @@
+<pre>
+  mktemp [template]
+
+  provides the mktemp function for systems which do not provide such a
+  UNIX command tool.  mktemp constructs the name of a unique file and
+  returns it on stdout.  By default, the filename will be
+  /tmp/tmp.XXXXXX where XXXXXX is substituted with a string of
+  characters to enforce uniqueness.  If the template filename is
+  provided, consisting of a valid filename ending in .XXXXXX, then
+  this template is used to construct the temporary file.  
+
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/mosastro.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/mosastro.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/mosastro.htm	(revision 22322)
@@ -0,0 +1,180 @@
+<meta name=file  content=gastro>
+<meta name=title content=gastro>
+<meta name=page  content=gastro>
+
+<p>
+mosastro takes a collection of data from mosaic CCD images, all
+individually astrometrized, and determines a single global astrometric
+solution for the complete system.  In this process, it determines a
+distortion model for the telescope arising from the optical system, as
+well as mapping solutions relating the coordinate systems of the
+individual chip pixels to the focal plane.  Both of these
+transformations may involve up to 3rd order polynomials.  
+
+<p>
+The suggested operation is to use gastro (or gastro2) to determine
+linear astrometric solutions for the individual chips before running
+mosastro.  Mosastro requires the individual chip astrometry have an
+accuracy of roughly 1 arcsec or better in order to select the match
+between the observed stars and the astrometric reference catalog.   
+This two stage approach allows a more robust linear solution for the
+individual chips, which may have too few reference star matches to
+define reliable high-order solution.  The mosaic analysis determines a
+single distortion model representing the physical contribution of the
+telescope optics.
+
+Mosastro is run assuming the user has a collection of Elixir-style
+astrometry / photometry files in one of the CMP/SMP/SMF set of
+formats.  Mosastro will auto-detect the data format and load the
+stellar astrometric and photometric measurements.  The collection of
+data is assumed to consist of one file per chip, with names which are
+sufficiently consistent that they can be identified with a single
+filename including wild-cards.
+
+<p>
+The user command looks like:
+<pre>
+mosastro (glob) (ext) (phu)
+</pre>
+
+The first argument is an expression containing wild-cards which
+expands into the collection of files containing the astrometric data.
+The mosastro program must receive the wild-card expression
+<em>without</em> expansion by the shell.  The user call needs to
+protect the wild-card against expansion, which can usually be done by
+placing the expression within double-quote marks.  The second argument
+is the new output extension.  The stellar photometry will be written
+out to files using the same names as the input, replacing the final
+filename extension with the provided extension.  The standard input
+extensions are one of the following: 'cmp', 'smp' (used for dophot or
+sextractor output files in raw text format), 'cmf, 'smf' (used for
+dophot or sextractor output files in fits table format).  The
+recommended output extensions replace the 'c' or 's' with 'x': 'xmp'
+for raw text format, or 'xmf' for FITS table format.  The final
+argument is the name of the output 'primary header unit' file.  The
+standard usage here is to use the filename root (without chip
+identifiers) with the extension 'phu'.  The output telescope boresite
+and optical distortion terms are written to this primary header entry,
+which is constructed from the first of the chip files (true?).  
+
+<em> future expansions: allow input list of files from a file, allow
+input MEF collection of chip astrom/photom </em>
+
+<p>
+In the following discussion, we refer to conversions between
+several coordinate frames.  We use the term 'project' to describe the
+projection of the celestial coordinates to the linear (focal plane or
+chip) coordinates; we use the term 'deproject' to describe the
+conversion from the linear chip or focal-plane coordinates to the
+spherical celestial coordinates.
+
+<p>
+mosastro performs the following steps in the analysis.  
+<ul>
+<li> Load the raw stellar astrometry data from the chip files
+<li> Deproject the stars using the approximate chip astrometry
+<li> Determine the RA and DEC range of the observed star measurements
+<li> Define the initial guess telescope boresite / distortion model
+<li> Project the observed star coordinates to the focal plane
+<li> Load the astrometric reference catalog.
+<li> Project the reference catalog to the focal plane
+<li> Match obs and ref on the focal plane
+<li> Measure the local gradient of the matched star coordinate in the
+tangent plane as a function of focal plane coordinate.  
+<li> Fit the local gradient values as a function of focal plane coordinates
+<li> Use the measured gradient model to modify the distortion model
+<li> Fit low-order solution for the chip model
+<li> Clip outlier matches
+<li> Fit high-order solution for the chip model
+<li> Perform several clip / fit iterations
+<li> Write out the new solutions / data to the output file
+</ul>
+
+<h3> options </h3>
+
+The following command-line options are available to the user:
+
+<ul>
+<li> -help or -h : print summary help information
+<li> -v : turn on verbosity
+
+<li> -dump (selection) : write out matched stars data at some
+processing stage.  The selection specifies where in the analysis to
+write out the result.  The following options are available:
+<ul>
+<li> rawstars : write out the raw input observed star positions, after
+the initial projection
+<li> refcat   : write out the reference catalog data (after initial
+projection). 
+
+<li> rawmatch : write out the obs and ref stars after the first match,
+before any fit is performed
+ 
+<li> fitgrads : write out the obs and ref stars after correction for
+the local gradient
+ 
+<li> fitchips_unclip : write out the obs and ref stars after fitting
+the initial chip term
+
+<li> fitchips : write out the obs and ref stars after the clipping
+iterations. 
+</ul>
+
+<li> -save-residuals : save table of obs and ref star matches, with
+coordinates in the multiple frames, as an extension to the PHU file.
+
+<li> -chips : load the initial chip focal-plane mapping (not yet implemented)
+
+<li> -field : load the inital field rotation, boresite, plate-scale
+from reference file
+
+<li> -order : define the polynomial order of the telescope distortion
+model (default is 0).
+
+-chiporder : define the polynomial order of the chip mapping model
+(default is 1).
+</ul>
+
+<h3> Elixir Configuration Data </h3>
+
+Mosastro uses the following Elixir configuration variables:
+
+<ul>
+<li> EXPTIME-KEYWORD  : header keyword defining exposure time
+<li> DATE-KEYWORD  : header keyword defining the date 
+<li> DATE-MODE  : mode for header date information: use a combination
+of Ys, Ms, Ds to specify order.  If only 2 digits are specified for
+the year, numbers greater than 50 are assumed in the 1900s, less than
+in the 2000s.  eg YYYY/MM/DD
+<li> UT-KEYWORD  : header keyword defining UT
+<li> MJD-KEYWORD  : header keyword defining MJD
+<li> JD-KEYWORD  : header keyword defining JD
+
+note that only one of the combination JD, MJD, or DATE/UT needs to be
+defined. 
+
+<li> ASTRO_REFCAT  : desired astrometric reference catalog.  allowed
+values are <tt>GSC, USNO, 2MASS, PTOLEMY, STONE</tt>.  
+
+<li> GSCDIR      : location of the HST Guide Star Catalog
+<li> USNO_CDROM  : location of the USNO-A data
+<li> STONE_DIR   : location of the Stone et al reference data
+<li> 2MASS_DIR   : location of the 2MASS data 
+<li> CATDIR      : location of the ptolemy data
+<li> GSCFILE     : location of the GSC layout file (for GSC and ptolemy)
+
+note that 'ptolemy' refers to data using the DVO format.  2MASS data
+must be in a DVO format database.  USNO and GSC are both loaded from
+their idiosyncratic formats.  
+
+<li> RADIUS  : matching radius in arcsec to select reference stars
+<li> SIGMA_LIM  : exclude stars with formal error larger than this
+
+<li> ZERO_PT  : default zero point, nominally 25.0.  don't set this to
+a 'real' zero point.  it is used only for reference.
+
+<li> INST_MAG_MIN  : exclude stars brighter than this instrumental magnitude
+<li> INST_MAG_MAX  : exclude stars fainter than this instrumental magnitude
+<li> INST_BRIGHT  : use stars brighter than this value to measure
+systematic error limit.
+</ul>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/precess.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/precess.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/precess.htm	(revision 22322)
@@ -0,0 +1,10 @@
+<pre>
+  precess (in) (out)
+
+  read a series of RA,DEC coordinate pairs (in decimal degrees) and
+  precess them from the (in) equinox to the (out) equinox.  Equinoxes
+  are decimal years in the J2000 system, or in the B1950 system if
+  prepended by 'B'.  B and J may be used to substitude for B1950 and
+  J2000 respectively.
+
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/radec.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/radec.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Elixir-Tools/radec.htm	(revision 22322)
@@ -0,0 +1,11 @@
+<pre>
+  radec [-hh | -hms]
+
+  read a series of coordinate pairs and convert between decimal and
+  sexigesimal representations.  If the option -hh is given, the input
+  is expected to be decimal degrees and the output will be sexigesimal
+  (hours minutes seconds).  If the option -hms is given, the input is
+  expected to be sexigesimal and the output will be decimal degree.
+  Whitespace must be used for all separators.
+
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/index.htm	(revision 22322)
@@ -0,0 +1,17 @@
+<meta name=title content=IPP Subsystems>
+<meta name=page  content=IPP Architectural Subsystems>
+
+The Pan-STARRS IPP is using several programs from the Ohana collection
+for portions of the IPP Infrastructure.  These subsystems include:
+
+<ul> 
+<li> psched: the IPP scheduler program
+<li> pcontrol : the IPP parallel process controller
+<li> pclient : the pcontrol remote client
+</ul>
+
+In addition to these program, the IPP will use DVO for object
+photometry manipulation.  IPP requires the upgraded capability which
+will be provided by DVO after Fall 2005, including enhanced
+throughput. DVO is discussed in its own section.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pclient/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pclient/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pclient/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pclient/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pclient/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pclient/index.htm	(revision 22322)
@@ -0,0 +1,50 @@
+<meta name=file  content=index>
+<meta name=title content=PCLIENT SUMMARY>
+<meta name=page  content=pclient summary>
+
+<tt>pclient</tt> is the remote process monitor for pcontrol, the parallel
+process controller.
+
+<h3>Overview</h3>
+
+<p>
+The program <tt>pclient</tt> is used to support the remote jobs which are
+run on the remote hosts by <tt>pcontrol</tt>.  The concept of pclient is to
+act as a buffer between the job running on the remote host and
+pcontrol.  The pcontrol design uses (by default) ssh connections
+initiated by pcontrol to the remote hosts.  These connections execute
+the remote program of pclient.  The use of a remote login process lets
+the UNIX system take care of the user authentication issues.  In this
+case, the recommended practice is to set up ssh to allow the
+connection to the remote host without additional authentication using
+the appropriate authorized keys (see <a href=ssh-issues>this
+article<a> on ssh issues).  
+
+<p>
+It is convenient to keep a continuous connection to the remote hosts.
+This avoids incurring the overhead of authentication for each command
+which is executed, while keeping a high-quality user authentication
+process in place.  
+
+<p>
+pclient acts as a buffer between pcontrol and the remote background
+process, allowing the continuous connection to remain viable without
+samping pcontrol with output from the jobs.  
+
+<h3>Command Summary</h3>
+
+<p>
+pclient has a very limited command set, as follows:
+
+<pre>
+job           : start the job (UNIX command) in the background.
+check         : return the current job status
+status        : return the current job status (?)
+stdout        : dump the stdout stream accumulated from the job
+                back to the calling program.
+stderr        : dump the stderr stream accumulated from the job
+                back to the calling program.
+reset         : kill (if needed) the job and reset to accept
+                another job.
+</pre>
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pcontrol/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pcontrol/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pcontrol/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pcontrol/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pcontrol/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pcontrol/index.htm	(revision 22322)
@@ -0,0 +1,261 @@
+<meta name=file  content=index>
+<meta name=title content=PCONTROL.SUMMARY>
+<meta name=page  content=pcontrol.summary>
+
+<tt>pcontrol</tt> is the IPP parallel process controller.
+
+<h3>Overview</h3>
+
+<p>
+The IPP uses a group of computers to store and process images and to
+manipulate collections of detections.  These computers perform any of
+a large number of analysis stages or other processing tasks without
+significant interprocess communication.  It is necessary to have a
+mechanism which initiates computing tasks on the different computers,
+which monitors the tasks as they are executed, which handles the
+output and the errors from these tasks, and which reacts to the
+failure of any of the computing nodes.  The system responsible for the
+tasks in the IPP is <tt>pcontrol</tt>.
+
+<h3>Host States</h3>
+
+<p>
+<tt>pcontrol</tt> maintains a table of available processing computers
+(<em>hosts</em>) and tracks their status.  Hosts managed by
+<tt>pcontrol</tt> are allowed to be in one of several states:
+<tt>off</tt>, <tt>down</tt>, <tt>idle</tt>, <tt>busy</tt>, and
+<tt>done</tt>.  These states have the following meanings:
+
+<p>
+If the host is <tt>off</tt>, it is known to pcontrol, but pcontrol
+does not have an active connection to the machine.  Hosts which are
+<tt>off</tt> are not available for jobs, and pcontrol does not attempt
+to initiate a connection to them.
+
+<p>
+When pcontrol is told to consider a machine on, the machine is moved
+from the <tt>off</tt> state to the <tt>down</tt> state.  Pcontrol
+attempts to initiate a connection to the host.  Connections are made
+by running a remote client on the host, using the specified connection
+method.  The connection method may be <tt>ssh</tt>, <tt>rsh</tt>, or
+an equivalent remote shell connection. The choice is specified by the
+COMMAND Opihi variable.  The remote connection starts a dedicated
+remote client which must accept the pcontrol client commands and
+respond appropriately.  The provided remote client is called
+<tt>pclient</tt>, though in principal other equivalent programs could
+be used by setting the Opihi variable SHELL (this feature more
+generally allows a user to specify a path to the remote client, if it
+is not in the user's path).  A pcontrol user may force a host to
+transition to the <tt>off</tt> state with the command <tt>host off
+(hostname)</tt>.  (<em> Note that this command will set only one of
+the connections to the named host to <tt>off</tt>.  If multiple
+connections to a machine have been defined, multiple <tt>off</tt>
+commands must be sent</em>).
+
+<p>
+If the remote connection is successful, the connected host is moved by
+pcontrol from the <tt>down</tt> state to the <tt>idle</tt> state.  If
+the connection is unsuccessful, pcontrol will try again after a
+certain period of time.  If the connection continues to be
+unsuccessful, the retry period is doubled for each successiver
+connection attempt.  If the user wants to force pcontrol to retry the
+connection to a machine (if, for example, the timeout is now very
+long, but the user knows the machine's ethernet cable has been
+re-inserted...), this can be achieved with the command <tt>host retry
+(hostname)</tt>.  A host which is <tt>down</tt> is in the limbo state
+between <tt>off</tt> and <tt>idle</tt>.  
+
+<p>
+Once pcontrol has made a successful connection to the host, the host
+is in the <tt>idle</tt> state.  At this point, it is ready to accept
+jobs from pcontrol for execution.  Pcontrol repeatedly queries the
+hosts to check that they are still alive.  If a host is discovered to
+be unresponsive, and particularly if the remote pipe connection has
+closed, then the machine is moved back to the <tt>down</tt> state.  
+
+<p>
+Hosts which are <tt>idle</tt> may accept a job from pcontrol.  A job simply
+consists of a bare UNIX command, without redirection of standard input
+or standard output.  The host will initiate the job, and pcontrol will
+place the host into the <tt>busy</tt> state.  The remote client, pclient,
+runs the job in the background and will continue to accept input from
+pcontrol.  pcontrol will continue to check the status of the host, and
+now also the status of the specific job.  As before, if the connection
+breaks, pcontrol will migrate the host to the <tt>down</tt> state.  Any job
+already initiated on a host which goes down will be returned for later
+processing, so the job will not be lost. 
+
+<p>
+When the job exits, pclient tells pcontrol that the job is completed,
+and specifies the exit status.  At this point, pcontrol will move the
+host from <tt>busy</tt> to <tt>done</tt> state.  It will stay in this
+state until pcontrol can determine the ending conditions and reset the
+remote client.  pcontrol requests the standard error and standard
+output from the job from pclient.  pcontrol stores this data with its
+information about the completed job, and send a reset command to the
+remote client.  Once these cleanup tasks are successfully completed,
+pcontrol will move the host to the <tt>idle</tt> state, ready for
+further jobs.
+
+<p>
+Each physical computer may have multiple processors.
+<tt>pcontrol</tt> treats each processor independently.  It is up to
+the system configuration if each computer needs to reserve one of its
+CPUs to manage background tasks or if <tt>pcontrol</tt> should attempt
+to send one task per CPU and let the operating system handle the I/O
+load.  <em>some of this behavior will probably be eventually more
+intelligent.  For example, the commands which turn a host on or off
+should be able to do the same operation to all host connections for
+the same machine name.</em>
+
+<p>
+A machine may be completely removed from pcontrol's host tables with
+the command <tt>host delete (hostname)</tt>.  
+
+<h3>Jobs</h3>
+
+<p>
+The <tt>pcontrol</tt> accepts new jobs with the command <tt>job
+...</tt>, in which the ellipsis represents the command and arguments
+of a valid UNIX command.  The commands are run under <tt>sh</tt>, and
+are executed in the user's home directory.  (<em>If it is desired, we
+can easily add a command to tell pclient to perform <tt>cd</tt></em>).
+Users should be wary of the conditions under which the remote jobs are
+run.  If the nodes in question all cross-mount the same home
+directories, multiple jobs which interact with the same named file may
+produce unexpected results.  The controller cannot enforce good
+behavior on the part of the remote jobs; it is the responsibility of
+the user to ensure that conflicts do not arise by, eg, always using
+unique output file names.
+
+<p>
+Other issues may arise from the fact that pcontrol may be choosing any
+of the hosts to run the job.  Typical failures arise if the user does
+not realize that specific jobs do not behave the same on all machines,
+or if a necessary resource (eg, some input data file) is only
+available or accessible from some of the hosts.  It is the
+responsibility of the task to wait for network lags (ie, NFS delays).
+
+<p>
+<tt>pcontrol</tt> gives each task a unique internal identifier (Job
+ID) equivalent to the process ID used in UNIX.  When a job is
+submitted to pcontrol, the command echoes back the Job ID.  This ID
+may be used by other pcontrol commands to obtain information about or
+interact with the job.
+
+<p>
+A job may specify a specific host for the task execution.  The host
+specified for a job may be <b>required</b>, or <b>desired</b>.  In the
+first case, pcontrol, will only run the job on the specified host,
+waiting until it is available before attempting the job.  In the
+second case, pcontrol will attempt to send the job to the specified
+host, but if the host is unavailable (<em>how long? what
+conditions?</em>), pcontrol will allow the job to be sent to an
+alternative host.  <tt>pcontrol</tt> attempts to honor the requests
+for required and desired hosts, giving priority first to required-host
+jobs, then to the desired-host jobs, and finally to all other jobs.
+To specify a host for a job, the following commands are used:
+
+<pre>
+job -host (command and arguments...)
+job +host (command and arguments...)
+</pre>
+
+The first case specifies a desired host, while the second specifies a
+required host.  It is also possible to specify the special host name
+<tt>anyhost</tt>, which is equivalent to not specifying a host at all.
+
+<p>
+<em>Job priority / urgency levels are not implemented at this time.</em>
+
+<p>
+<em>I/O vs CPU tasks are not currently distinguished by pcontrol</em>
+
+<p>
+<tt>pcontrol</tt> stores the stdout and stderr for each completed job.
+To retrieve these data from these streams, the user issues the
+commands <tt>stdout (JobID)</tt> and <tt>stderr (JobID)</tt>.  The
+result is a single line specifying the number of bytes to expect,
+followed by a dump of the buffers, followed by the prompt. It is the
+user's responsibility to relieve pcontrol of this data load by
+deleting jobs once they are no longer needed.  Job deletion is
+performed with the command <tt>delete (JobID)</tt>.
+
+<p>
+Jobs are moved between the following states by pcontrol:
+<ul>
+<li> pending: the job has not yet been executed.
+<li> busy: the job is currently being executed.
+<li> done: the job has completed, but the stdout/stderr has not been processed by
+     pcontrol.
+<li> exit: the job has completed with a valid exit status
+<li>crash: the job has completed with a crash status (exit on signal).
+</ul>
+
+<h3>Miscellaneous Commands</h3>
+
+<p>
+It is possible to check the status of a single host or job with the
+user command <tt>check</tt>.
+
+<p>
+pcontrol continuously examines the stack of jobs, adjusting their
+state as needed and extracting their output when it is ready.  These
+checks are performed in the background, with pcontrol ready to accept
+further commands from the user in the foreground.  These checks are
+performed after every keystroke, and also after an inactivity timeout.
+The interrupt interval defaults to 1 second, but may be adjusted with
+the <tt>pulse</tt> command, which takes as an argument, the number of
+microseconds for the timeout.
+
+<p>
+the pcontrol system status may be examined with the command
+<tt>status</tt>.  This provides a dump of the job stacks and the host
+stacks.  
+
+<p>
+It is possible to list the jobs currently in a specific stack,
+corresponding to the list of jobs with a given state.  This is done
+with the command <tt>jobstack (stackname)</tt>.  The valid stack names are
+pending, busy, exit, crash, and done.  The result is a list of all
+jobs on the specified stack.  This is useful to determine quickly
+which jobs have exited or crashed.  
+
+<p>
+A specific job may be killed with the command <tt>kill (JobID)</tt>.
+This command is only valid for a job in the <tt>busy</tt> state.  Any
+job in the <tt>pending</tt>, <tt>exit</tt>, or <tt>crash</tt> state
+may be deleted with the <tt>delete (JobID)</tt> command.  This is
+necessary to free the memory associated with the job and its output
+streams.
+
+<p>
+The command <tt>verbose (mode)</tt> turns the verbosity of the
+pcontrol operations on or off.
+
+<p>
+The <tt>pcontrol</tt> and the IPP Image Server have related needs for
+information from the combined storage-and-processing nodes regarding
+which nodes are available.  It is not yet clear if this information is
+best stored in a single location (either <tt>pcontrol</tt> or IPP
+Image Server), which provides the information to other systems on
+demand, or if both systems should maintain the information.  Also, it
+may be necessary to distinguish nodes which are available for
+processing from those that are available to serve data as part of the
+IPP Image Server.
+
+<h3>Command Summary</h3>
+
+<pre>
+check                -- get job or host status
+delete               -- delete job
+host                 -- add / delete / modify host
+job                  -- add job
+jobstack             -- list jobs for a single stack
+kill                 -- kill job
+pulse                -- set system pulse
+status               -- get system status
+stderr               -- get stderr buffer for job
+stdout               -- get stdout buffer for job
+verbose              -- set the verbose mode for job
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pcontrol/speedtest.hts
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pcontrol/speedtest.hts	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/pcontrol/speedtest.hts	(revision 22322)
@@ -0,0 +1,42 @@
+
+I ran 8 x 32 fake image copies on the 32 po nodes. each one should
+last a minimal amount of time (no real action, no sleep).  The time to
+process the stack is essentially the speed at which pcontrol can cycle
+though the nodes:
+
+-rw-rw-r--  1 eugene users   17 2005-08-11 19:28:50.000000000 -1000 file.00.032.fits
+-rw-rw-r--  1 eugene users   17 2005-08-11 19:29:20.000000000 -1000 file.07.015.fits
+
+that is 30 seconds to run 8 x 32 jobs: 
+
+0.117 sec / job
+3.750 sec / (job / node)
+
+I ran the same 8 x 32 jobs on one node, with the following rate:
+
+-rw-rw-r--  1 eugene users   17 2005-08-11 19:34:39.000000000 -1000 file.00.001.fits
+-rw-rw-r--  1 eugene users   17 2005-08-11 19:35:30.000000000 -1000 file.07.029.fits
+
+or 51 seconds to run 8 x 32 jobs on 1 node.
+
+0.199 sec / job
+0.199 sec / (job / node)
+
+--
+
+I ran the same test getting 56 seconds for 8 x 32 copies on 32 nodes.
+This time, I used one of the machines in the po cluster (po33) for the
+test:
+
+0.219 sec / job
+
+-rw-rw-r--  1 eugene users   17 2005-08-11 19:45:25.000000000 -1000 file.00.030.fits
+-rw-rw-r--  1 eugene users   17 2005-08-11 19:46:21.000000000 -1000 file.07.028.fits
+
+--
+
+running on psched, from kiawe to po01-po32
+-rw-rw-r--  1 eugene users    17 2005-08-11 20:11:25.000000000 -1000 file.00.003.fits
+-rw-rw-r--  1 eugene users    17 2005-08-11 20:12:30.000000000 -1000 file.07.032.fits
+
+55 seconds; roughly equivalent.  
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/chip.host
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/chip.host	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/chip.host	(revision 22322)
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 1) { die "USAGE: chip.host (chip)\n"; }
+
+$chip = $ARGV[0];
+
+if ($chip eq "ccd00") {
+    print "po04\n";
+    exit 0;
+}
+if ($chip eq "ccd01") {
+    print "po01\n";
+    exit 0;
+}
+if ($chip eq "ccd02") {
+    print "po02\n";
+    exit 0;
+}
+if ($chip eq "ccd03") {
+    print "po03\n";
+    exit 0;
+}
+print "anyhost\n";
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/copy.image
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/copy.image	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/copy.image	(revision 22322)
@@ -0,0 +1,19 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 2) { die "USAGE: copy.image (filename) (chip)\n"; }
+
+$filename = $ARGV[0];
+$chip     = $ARGV[1];
+
+@word = split ("/", $filename);
+$base = $word[-1];
+$output = "/data/alala/swtests/psched/$base";
+
+open (FILE, ">$output");
+$host = `hostname`;
+printf FILE "chip $chip $host\n";
+close (FILE);
+
+printf STDOUT "$filename $chip\n";
+
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/index.htm	(revision 22322)
@@ -0,0 +1,385 @@
+
+This article describes <tt>psched</tt>, the Pan-STARRS IPP task
+scheduler.
+
+<h3>Overview</h3>
+
+<p>
+The purpose of <tt>psched</tt> is to manage the automatic construction
+and execution of inter-related (often repetative) operations.
+<tt>Psched</tt> uses a set of rules to define UNIX commands, and
+their corresponding command-line arguments, to be performed on some
+regular, repeated basis.  The utility of <tt>psched</tt> is that it
+can easily define an analysis system which is completely
+state-based, as opposed to an event-driven system.  
+
+<p>
+Consider, for example, a telescope which obtains a collection of
+images over the course of a night.  Every minute or two, it takes an
+image and writes the image to some disk.  An event-driven analysis
+system would involve having the telescope initiate a process at the
+end of the exposure.  This process would perform an analysis, write
+some output, then send trigger another process.  This type of
+operation works very well for a simple set up with reliable
+hardware.  Such a system becomes more difficult to maintain when
+hardware failures occur or when multiple systems need to interact
+with each other.  When failures occur, the triggering information
+(the events) is easily lost, thus some mechanisms are needed to
+detect these failures and either re-send the trigger or send an
+alternative failure-mode trigger.  Or, if two systems need to
+interact, one or the other system must block for results from the
+first.  Stopping and restarting such an analysis system is very
+delicate since the appropriate triggers must be set up some how, eg
+by noticing which images have not succeeded and restarting them at
+the appropriate stage.  All of these types of methods of handling
+complexity and failures are essentially state-based rules.
+<tt>Psched</tt> allows the easy definition of a totally state-based
+analysis system.
+
+<p>
+In a state-based system, some mechanism examines the state of the
+system and decides which actions to perform based on the current
+state.  In the illustration above, the mechanism could examine the
+images available (either by examining the disk or by examining the
+state of a data table) and decide to perform an operation based on
+what images are available.  This makes it very easy to handle
+complexity and errors.  If an analysis fails, the state either is
+not successfully updated or the error state is recorded, both
+situations being easy to detect and easy to handle.  Restarting the
+system simply involves starting the state-monitoring mechanism.
+Combining results from multiple input sources simply involves
+watching for the multiple inputs to be available.  <tt>Psched</tt>
+provides a mechanism to define state monitors, and to define the
+actions which are performed when those states occur.
+<tt>Psched</tt> action consist of initiating UNIX commands, where
+the arguments of those commands may depend on the results of the
+state tests.
+
+<h3> Tasks vs Jobs </h3>
+
+<p>
+The primary function of <tt>psched</tt> is to repeatedly perform
+<b>tasks</b>, and execute <b>jobs</b> on the basis of those tasks.
+A task consists of a set of rules which describe system state tests
+to perform on a regular time scale.  Based on the results of those
+state tests, the task will then choose whether or not to construct a
+job.  The task also defines actions to perform upon the completion
+of a job, based upon the output and exit status of the job.  A task
+thus defines the repeat period.  It may optionally define valid or
+invalid time ranges (eg, Mon-Fri or 10:00-17:00, etc).  The task may
+also specify that the job be run locally (ie, in the background on
+the same computer as psched) or remotely by the parallel process
+controller (<tt>pcontrol</tt>).  A job may even be restricted to a
+specific computer managed by <tt>pcontrol</tt>.
+
+An example of a simple tasks is given below.  
+
+<pre>
+  task datalist
+    command ls /data/foo
+    periods -exec 5.0
+    periods -timeout 50.0
+    periods -poll 1.0
+
+    task.exit 0
+      queueprint stdout
+      queuedelete stdout
+    end
+ 
+    task.exit 1
+      queuepush failure "task failed"
+    end
+  end
+</pre>
+
+<p>
+This task does not perform any system state tests; it is simply
+constructs a new job every 5.0 seconds.  The job in this case is
+always the same: <tt> ls /data/foo </tt>.  When the job finished,
+if the job exit status is 0 (normal UNIX success status), the
+resulting output is printed to the screen.  If the job returns an
+exit status of 1 (a failure), the failure queue receives a single
+entry.  Although they are not defined in this case, it is also
+possible to specify the action to be taken if the job crashes (does
+not exit normally) or if it times out (runs beyond the specified
+timeout period).
+
+A slightly more complex task which performs a state test and
+constructs a command based on that test is shown below
+
+<pre>
+  task datalist
+    periods -exec 5.0
+    periods -timeout 50.0
+    periods -poll 1.0
+
+    task.exec 
+      $file = `next.file`
+      if ($file == "none")
+        break
+      end
+      command cp /data/foo/$file /data/bar
+    end
+
+    task.exit 0
+      queueprint stdout
+      queuedelete stdout
+      queuepush copied $file
+    end
+ 
+    task.exit 1
+      queuepush failure $file
+    end
+  end
+</pre>
+
+The <tt>task.exec</tt> macro is executed by psched every 5.0
+seconds.  This macro executes a (hypothetical user-defined) UNIX
+command (<tt>next.file</tt>) which examines the system state, return
+either a filename or the word "none".  If the result of this test is
+"none", the task does nothing: no job is constructed.  Otherwise, a
+job is constructed using the name of the file returned by the state
+test.  Successful jobs have the filename added to the 'copied'
+queue, while failed jobs add the filename to the 'failure' queue.
+
+<h3> Parallel vs Local Job Processing </h3>
+
+Job which are generated by psched tasks may either be run locally
+(forked in the background on the same machine as psched) or run on
+the IPP parallel process controller, <tt>pcontrol</tt>.  The default
+is for the job to be run locally.  If a job should be run on the
+parallel controller, this can be specified by including the command
+<tt>host (hostname)</tt> in the definition of a task.  If the value
+of (hostname) is 'anyhost', then pcontrol may select any of its host
+computers to run the job according to its own rules.  If the value
+of (hostname) is one of the computers managed by pcontrol, then that
+machine will be selected for the job, if it is available.  This
+amounts to a preference to use that machine, but pcontrol is allowed
+to substitute a different machine if it chooses.  If the
+<tt>host</tt> command is given the option <tt>-required</tt>, then
+pcontrol is forced to use the named host, even if the machine is
+down, unknown, or otherwise unavailable.  If the machine is not
+available, pcontrol will simply hold onto the job until the machine
+is available or the job is deleted.  Note that psched may delete
+jobs from pcontrol if they remain pending for too long (see
+<tt>period -timeout</tt>).  
+
+<p>
+It is possible to interact directly with the parallel processor to
+examine the current status, halt the parallel processor, etc.
+Commands to the parallel processor are defined under the
+<tt>controller</tt> command.  The following controller commands are
+available:
+
+<ul>
+  <li> <tt>controller host (command) (hostname)</tt>: Manage the
+  parallel controller collection of hosts.  This command can be used
+  to <tt>add</tt> a new host, the <tt>delete</tt> one of the existing
+  hosts, to turn a host <tt>on</tt> or <tt>off</tt>, and to
+  <tt>check</tt> the status of a host
+    <ul>
+      <li> <tt>controller host add (hostname)</tt>: add a new host.
+      <li> <tt>controller host delete (hostname)</tt>: delete a host.
+      <li> <tt>controller host on (hostname)</tt>: tell pcontrol that the host is on.
+      <li> <tt>controller host off (hostname)</tt>: tell pcontrol that the host is off.
+      <li> <tt>controller host retry (hostname)</tt>: tell pcontrol to retry the host connection.
+      <li> <tt>controller host check (hostname)</tt>: check the current status of a host.
+    </ul>
+  <li><tt>controller exit</tt>: stop controller execution.
+  <li><tt>controller status</tt>: report controller current status.
+  <li><tt>controller check</tt>: check job or host status.
+  <li><tt>controller output</tt>: print accumulated messages from the controller.
+</ul>
+
+It is also possible to specify a host for a task which has not been
+identified to the controller.  If such a host is required, the
+controller will simply keep the associated jobs in the pending state
+until such a machine exists.  See the <a href=../pcontrol>pcontrol</a>
+documentation for further discussion of the controller manipuation of
+jobs and hosts.
+
+<h3> Task Restrictions </h3>
+
+Tasks may have restrictions on when they create jobs and how
+frequently they create jobs.  The task command <tt>trange</tt> is
+used to specify a valid or invalid time range for a task.  A valid
+time range limits the task evaluation to that time period.  An
+invalid time range excludes task evaluation from the time period.
+Any number of time range restrictions may be defined, and the union
+of all restrictions will define if a job may be created.  By
+default, the time range is an inclusive time range: the task is
+evaluated only if the current time falls within the specified time
+range.  Alternatively, if the <tt>-exclude</tt> flag is given, the
+time range is exclusive, in which case the task is <em>not</em>
+evaluated if the current time falls within this range.
+
+<p>
+The time range may be given as a range of absolute dates as follows:
+
+<pre>
+trange YYYY/MM/DD,HH:MM:SS YYYY/MM/DD,HH:MM:SS 
+</pre>
+
+where the two dates specify the start and end of the time range.  In
+either of these date representations, the least-significant elements
+of the date and time may be dropped, defaulting to 00 (in the case
+of hours, minutes, and seconds) or 01 (in the case of day and
+months).  Rather than specifying an end date, it is also valid to
+specify a time interval from the starting date.  The time interval
+is specified as a number followed by a unit indicated by a single
+letter: d (days), h (hours), m (minutes), s (seconds).  
+
+<p>
+The time range may also be specified as a repeated period of time,
+either as a time of day or a day and time of week.  In the first
+case, the time range is specified as follows:
+
+<pre> 
+trange HH:MM:SS HH:MM:SS
+</pre>
+
+where again the least-significant elements may be dropped and
+default to 00.  This type of restriction defines a time range which
+is valid every day.  The alternative is to specify a time range
+within the week, in the following form:
+
+<pre>
+trange DAY@HH:MM:SS DAY@HH:MM:SS
+</pre>
+
+where the value of DAY may take on any of the three letter
+day-of-week names (Sun, Mon, Tue, etc).  This restriction specifies
+a start and end time within a week which is evaluated for each
+week. 
+
+<p>
+Below are several examples of valid time range restrictions
+
+<pre>
+trange 2005/01/01 2005/12/31   (only run during 2005!)
+trange 18:00 00:00             (only run from 6pm until midnight)
+trange 00:00 06:00             (only run from midnight until 6am)
+trange Mon@08:00 Fri@17:00     (only run between Mon morning and Fri afternoon)
+trange -exclude 12:00 13:00    (skip 1 hour from noon)
+</pre>
+
+<em>Note that the current definition of trange does not include time
+zone information.  This means that <b>all</b> times are relative to
+UT.  This should be addressed by adding a timezone environment
+variable to psched and by allowing the trange to define a timezone
+offset.</em>
+
+<p>
+It is also possible to restrict the total number of jobs which are
+spawned for a given task.  This is done with the <tt>nmax</tt>
+command, which is given as part of the task definition.  Once a task
+has constructed nmax jobs, it stops task evaluation.  It is possible
+to redefine the value of nmax at any time by redefining the task.
+Any time the task is redefined, the new values for any task concept
+will override the existing values for the task concept.  
+
+<h3> Inter-Task and Inter-Job Communications </h3>
+
+There are several ways in which the results of jobs may be used to
+influence other jobs.  These include:
+<ul>
+<li> external communications
+<li> job exit status
+<li> job stdout parsing
+</ul>
+
+<p>
+It is always possible for the interprocess communication to be
+performed externally: all jobs may simply write results to an
+external data source which is queried as part of the task
+evaluation.  Psched may interact with UNIX programs using Opihi
+system interaction functions.  These interaction methods include:
+the backticks for setting Opihi variables:
+
+<pre>
+$variable = `UNIX Command`
+</pre>
+
+The exec command (which executes a UNIX command) and the backticks
+both receive the UNIX command exit status, setting the variable
+$STATUS.  It is also possible to set a variable list to the output
+of a UNIX command:
+
+<pre>
+list var -x "UNIX Command"
+</pre>
+
+In this last case, the values $var:0 - $var:N-1 are set to the value
+of the stdout lines from the UNIX command, and the value $var:n is
+set to the number of output lines.
+
+<p>
+Fine-grained control over the job exit status is available with the
+<tt>task.exit</tt> macro command.  This allows a task to define an
+exit macro which is performed for different exit status conditions.
+The argument to the <tt>task.exit</tt> command is the exit status
+value which triggers the macro.  This may consist of any valid
+numeric exit status value (0-255).  It may also have the value
+<tt>crash</tt>, in which case the macro is executed if the program
+exited as a result of a signal (ie, segmentation fault, etc).
+Finally, if may have the value <tt>default</tt>, in which case, the
+macro is run if no other macro describes the exit status.
+
+<p>
+Jobs may transmit their results back to psched for further
+evaluation through the standard output and standard error streams.
+Whenever a job exits, the complete stdout and stderr streams from
+the job are pushed onto the psched queues <tt>stdout</tt> and
+<tt>stderr</tt>.  The job exit macros may then parse these queues,
+moving the results into other psched / Opihi data containers
+(queues, variables, vectors, whatever is appropriate).  <em>Note
+that currently, the output data is simply pushed onto these output
+queues.  It is currently the responsibility of the psched programmer
+to use or dispose of the data in these queues.  This may change in
+the future: the queues may be flushed for each job completion.</em>
+
+<h3>Running the scheduler</h3>
+
+Once a set of tasks has been defined, the scheduler can be started.
+The scheduler will run in the background, at regular intervals
+examining the collection of tasks and jobs.  In these periods, the
+scheduler attempts to construct new jobs and checks on the status of
+jobs which may have finished, either locally or on the controller.
+To start the scheduler, give the command <tt>run</tt>.  To stop the
+scheduler, given the command <tt>stop</tt>.  The current status of
+the scheduler, controller, and any jobs which have been spawned are
+listed with the <tt>status</tt> command.
+
+<p>
+It is also possible to kill or delete individual jobs by hand with
+the commands <tt>kill (jobID)</tt> or <tt>delete (jobID)</tt>.
+
+<h3>Other features</h3>
+
+The command <tt>verbose (mode)</tt> turns the verbosity of the
+scheduler operations on or off.
+
+It is possible to change the rate at which the scheduler checks the
+task and job lists with the command <tt>pulse (usec)</tt), which
+takes as an argument, the number of microseconds between timeouts.
+
+<h3>Command Summary</h3>
+
+<pre>
+controller                  -- controller commands
+task                        -- define a schedulable task
+host                        -- define host machine for a task
+nmax                        -- define maximum number of jobs for a task
+trange                      -- define valid/invalid time periods for a task
+task.exit                   -- define exit macros for a task
+task.exec                   -- define pre-exec macro for a task
+command                     -- define executed command for a task
+periods                     -- define time scales for a task
+run                         -- run the scheduler
+stop                        -- stop the scheduler
+pulse                       -- set the scheduler update period
+status                      -- get system status
+kill                        -- kill job
+delete                      -- delete job
+verbose                     -- set/toggle verbose mode
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/new.images
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/new.images	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/new.images	(revision 22322)
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+
+# a dummy script to simulate data arriving from OTIS
+
+# read lines 'new.list', send to 'raw.list'
+
+open (FILE, "new.last");
+@list = <FILE>;
+close (FILE);
+($Nend) = split (" ", $list[0]);
+# print "Nend: $Nend\n";
+
+open (FILE, "new.list");
+@list = <FILE>;
+close (FILE);
+
+$Nend += 5;
+for ($i = 0; $i < $Nend; $i++) {
+    ($name, $chip, $state) = split (" ", $list[$i]);
+    if ($state ne "new") { next; }
+    print "$list[$i]";
+}
+
+open (FILE, ">new.last");
+print FILE "$Nend\n";
+close (FILE);
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/pcopy.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/pcopy.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/pcopy.htm	(revision 22322)
@@ -0,0 +1,138 @@
+
+Below is an example script for psched which demonstrates the
+scheduling system. This parallel-copying script implements the
+Pan-STARRS image copying system, which requests images from the summit
+and copies them to the appropriate computer.  The first task in the
+script queries an external system for new image names with the
+function <tt>new.images</tt>.  In the case of Pan-STARRS, this would
+be a request from OTIS, the observatory controlling system.  The
+second task initiates the individual image copies, with separate CCDs
+being copied to separate computers.  This script uses the concept of
+having specific machines assigned to specific CCDs (as Pan-STARRS
+intends to operate). The association is determined by calling the
+external function <tt>chip.host</tt>, providing the identifier of the
+chip in question.  This returns an appropriate host.  The
+<tt>copy.image</tt> function copies the file and also sends a message
+to the summit system to inform it that the image has been successfully
+copied.
+
+<p>
+You may download just the <a href="pcopy.pro">pcopy.pro</a> script for
+a copy free of HTML code.  Also available are the dummy scripts <a
+href="new.images">new.images</a>, <a href="copy.image">copy.image</a>,
+and <a href="chip.host">chip.host</a>.
+
+<hr>
+
+<pre>
+verbose on
+queueinit newImages
+exec echo 0 > new.last
+exec cp -f raw.list new.list
+
+controller host add po01
+controller host add po02
+controller host add po03
+controller host add po04
+
+# identify the images ready for copy 
+# new entries are added to queue newImages
+# need to compare the new list with the ones already being processed
+task	       new.images
+  command      new.images
+  host         local
+
+  periods      -poll 1
+  periods      -exec 5
+  periods      -timeout 5
+
+  # success
+  task.exit    0
+    local i j Nstdout Nimages
+    # compare output with new.image queue
+    # keep only new entries
+    queuesize stdout -var Nstdout
+    for i 0 $Nstdout
+      queuepop stdout -var line
+      queuepush newImages -uniq -key 0 "$line"
+    end
+  end
+
+  # locked list
+  task.exit    1
+    echo       "new.images: exec failure"
+    $new.image.failure ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "new.images: timeout"
+    $new.image.failure ++
+  end
+end
+
+# copy new images, sending job to desired host
+task	       copy.images
+  periods      -poll 0.2
+  periods      -exec 1
+  periods      -timeout 5
+
+  task.exec
+    queuesize  newImages -var N
+    if ($N == 0) break
+    # if ($network == 0) break
+    # if ($filesystem == 1) break
+    
+    queuepop newImages -var line
+    list tmp -split $line
+    $filename   = $tmp:0
+    $chip       = $tmp:1
+    $state      = $tmp:2
+    if ($state == new) 
+      # copy this image
+      queuepush newImages -replace -key 0 "$filename $chip run"
+    else
+      # ignore this image
+      queuepush newImages -replace -key 0 "$filename $chip $state"
+      break
+    end
+    # echo $chip
+    $host = `chip.host $chip`
+    # echo $host
+    host $host
+    # echo "starting copy for $filename on $host..."
+    command copy.image $filename $chip
+  end
+
+  # can I have access to argc,argv?
+
+  # success
+  task.exit    0
+    echo "done copy..."
+    queuepop stdout -var line
+    list tmp -split $line
+    $filename   = $tmp:0
+    $chip       = $tmp:1
+    exec mark.image $filename
+    queuepush newImages -replace -key 0 "$filename $chip copy"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "new.images: timeout"
+    $new.image.failure ++
+  end
+end
+<pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/pcopy.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/pcopy.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/pcopy.pro	(revision 22322)
@@ -0,0 +1,111 @@
+
+verbose on
+queueinit newImages
+exec echo 0 > new.last
+exec cp -f raw.list new.list
+
+controller host add po01
+controller host add po02
+controller host add po03
+controller host add po04
+
+# identify the images ready for copy 
+# new entries are added to queue newImages
+# need to compare the new list with the ones already being processed
+task	       new.images
+  command      new.images
+  host         local
+
+  periods      -poll 1
+  periods      -exec 5
+  periods      -timeout 5
+
+  # success
+  task.exit    0
+    local i j Nstdout Nimages
+    # compare output with new.image queue
+    # keep only new entries
+    queuesize stdout -var Nstdout
+    for i 0 $Nstdout
+      queuepop stdout -var line
+      queuepush newImages -uniq -key 0 "$line"
+    end
+  end
+
+  # locked list
+  task.exit    1
+    echo       "new.images: exec failure"
+    $new.image.failure ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "new.images: timeout"
+    $new.image.failure ++
+  end
+end
+
+# copy new images, sending job to desired host
+task	       copy.images
+  periods      -poll 0.2
+  periods      -exec 1
+  periods      -timeout 5
+
+  task.exec
+    queuesize  newImages -var N
+    if ($N == 0) break
+    # if ($network == 0) break
+    # if ($filesystem == 1) break
+    
+    queuepop newImages -var line
+    list tmp -split $line
+    $filename   = $tmp:0
+    $chip       = $tmp:1
+    $state      = $tmp:2
+    if ($state == new) 
+      # copy this image
+      queuepush newImages -replace -key 0 "$filename $chip run"
+    else
+      # ignore this image
+      queuepush newImages -replace -key 0 "$filename $chip $state"
+      break
+    end
+    # echo $chip
+    $host = `chip.host $chip`
+    # echo $host
+    host $host
+    # echo "starting copy for $filename on $host..."
+    command copy.image $filename $chip
+  end
+
+  # can I have access to argc,argv?
+
+  # success
+  task.exit    0
+    echo "done copy..."
+    queuepop stdout -var line
+    list tmp -split $line
+    $filename   = $tmp:0
+    $chip       = $tmp:1
+    exec mark.image $filename
+    queuepush newImages -replace -key 0 "$filename $chip copy"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "new.images: timeout"
+    $new.image.failure ++
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/speedtests.hts
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/speedtests.hts	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/psched/speedtests.hts	(revision 22322)
@@ -0,0 +1,15 @@
+
+CheckController is critical for speed
+
+parse jobs: 1.5 usec per job + 20 usec
+
+check exit stack: 2.7ms average, up to 10ms: this seems to be the
+kernel scheduler putting pcontrol in the background every now and
+again.  
+
+with 1024 jobs and 4 machines, the jobs being sent to machines (should
+take only 1 second, but timeouts add in delays) takes up the first few
+seconds.  initially
+
+
+*write this up, add some tests for timing... *
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/sequence.idx
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/sequence.idx	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/IPP-subsystems/sequence.idx	(revision 22322)
@@ -0,0 +1,3 @@
+psched
+pcontrol
+pclient
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/DIMM/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/DIMM/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/DIMM/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/DIMM/commands.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/DIMM/commands.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/DIMM/commands.htm	(revision 22322)
@@ -0,0 +1,59 @@
+<meta name=file  content=dimm>
+ <meta name=title content=dimm user's guide>
+ <meta name=page  content=dimm>
+ 
+ <table>
+  <tr><td> altaz         </td><td>      altaz / celestial coord conversions </td></tr>
+  <tr><td> box           </td><td>      draw a box on the plot </td></tr>
+  <tr><td> buffers       </td><td>      list the currently allocated buffers </td></tr>
+  <tr><td> camera        </td><td>      camera functions </td></tr>
+  <tr><td> center        </td><td>      center the Kii window at coords </td></tr>
+  <tr><td> clear         </td><td>      erase plot </td></tr>
+  <tr><td> clip          </td><td>      clip values in a buffer to be within a range </td></tr>
+  <tr><td> concat        </td><td>      expand vector dimension </td></tr>
+  <tr><td> coords        </td><td>      load coordinates for buffer from file </td></tr>
+  <tr><td> create        </td><td>      create a new vector </td></tr>
+  <tr><td> cursor        </td><td>      get coordinates from the Kii window </td></tr>
+  <tr><td> cut           </td><td>      extract a cut across an image </td></tr>
+  <tr><td> datafile      </td><td>      define file to read vectors </td></tr>
+  <tr><td> delete        </td><td>      delete a buffer </td></tr>
+  <tr><td> erase         </td><td>      erase objects on an overlay </td></tr>
+  <tr><td> extract       </td><td>      extract a portion of a buffer into another buffer </td></tr>
+  <tr><td> file          </td><td>      test file </td></tr>
+  <tr><td> findstars     </td><td>      find objects on image </td></tr>
+  <tr><td> focus         </td><td>      skyprobe focus </td></tr>
+  <tr><td> gauss         </td><td>      get statistics on a star, assuming gaussian profile </td></tr>
+  <tr><td> getchr        </td><td>      find character in string </td></tr>
+  <tr><td> header        </td><td>      print buffer header </td></tr>
+  <tr><td> histogram     </td><td>      histogram of vector </td></tr>
+  <tr><td> imhist        </td><td>      histogram of an image region </td></tr>
+  <tr><td> jpeg          </td><td>      interpolate between vector pairs </td></tr>
+  <tr><td> keyword       </td><td>      extract a FITS keyword from buffer header </td></tr>
+  <tr><td> labels        </td><td>      define labels for plot </td></tr>
+  <tr><td> limits        </td><td>      define plot limits </td></tr>
+  <tr><td> load          </td><td>      load an SAOimage style overlay </td></tr>
+  <tr><td> mcreate       </td><td>      create a matrix </td></tr>
+  <tr><td> plot          </td><td>      plot a pair of vectors </td></tr>
+  <tr><td> ps            </td><td>      make PS file from Kii </td></tr>
+  <tr><td> rd            </td><td>      read an image from a file </td></tr>
+  <tr><td> read          </td><td>      read vectors from datafile </td></tr>
+  <tr><td> rebin         </td><td>      rebin data by factor of N </td></tr>
+  <tr><td> resize        </td><td>      change Kii window size </td></tr>
+  <tr><td> save          </td><td>      save an SAOimage style overlay </td></tr>
+  <tr><td> set           </td><td>      do image arithmetic </td></tr>
+  <tr><td> sort          </td><td>      sort vectors by key </td></tr>
+  <tr><td> sprintf       </td><td>      formatted print to a variable </td></tr>
+  <tr><td> star          </td><td>      star stats at rough coords </td></tr>
+  <tr><td> stats         </td><td>      give statistics on a portion of a buffer </td></tr>
+  <tr><td> strlen        </td><td>      string length </td></tr>
+  <tr><td> style         </td><td>      set the style for graph plots </td></tr>
+  <tr><td> subset        </td><td>      reduce vector dimension </td></tr>
+  <tr><td> substr        </td><td>      substring </td></tr>
+  <tr><td> telescope     </td><td>      telescope communications </td></tr>
+  <tr><td> textline      </td><td>      write text on Kapa window </td></tr>
+  <tr><td> tv            </td><td>      display an image on the Kii window </td></tr>
+  <tr><td> vectors       </td><td>      list vectors </td></tr>
+  <tr><td> vstat         </td><td>      vector statistics </td></tr>
+  <tr><td> wd            </td><td>      write an image to a file </td></tr>
+  <tr><td> write         </td><td>      write vectors to datafile </td></tr>
+ </table>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/DIMM/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/DIMM/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/DIMM/index.htm	(revision 22322)
@@ -0,0 +1,20 @@
+<meta name=file  content=DIMM>
+<meta name=title content=DIMM user's guide>
+<meta name=page  content=DIMM>
+
+<h3> Introduction </h3>
+
+DIMM is a control shell for the CFHT DIMM Telescope based on the Opihi
+shell.  See the Opihi documentation for further descriptions of the
+tools provided by the Opihi shell.  In addition to the basic Opihi
+functions and the astronomy functions from Mana, DIMM provides a
+collection of telescope and camera control functions.
+
+<h3> Mana astronomy functions</h3>
+
+<pre>
+altaz                      -- altaz / celestial coord conversions
+camera                     -- camera functions
+findstars                  -- find objects on image
+telescope                  -- telescope communications
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/Mana/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/Mana/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/Mana/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/Mana/commands.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/Mana/commands.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/Mana/commands.htm	(revision 22322)
@@ -0,0 +1,108 @@
+<meta name=file  content=mana>
+ <meta name=title content=mana user's guide>
+ <meta name=page  content=mana>
+ 
+ <table>
+ <tr><td> accum            </td><td> accumulate vector values in another vector                 </td></tr> 
+ <tr><td> applyfit         </td><td> apply fit coeffs to generate vector                        </td></tr> 
+ <tr><td> applyfit2d       </td><td> apply fit coeffs to generate vector                        </td></tr> 
+ <tr><td> biassub          </td><td> subtract medianed overscan row or column                   </td></tr> 
+ <tr><td> box              </td><td> draw a box on the plot                                     </td></tr> 
+ <tr><td> buffers          </td><td> list the currently allocated buffers                       </td></tr> 
+ <tr><td> buftovec         </td><td> convert an image buffer to a single vector                 </td></tr> 
+ <tr><td> center           </td><td> center the Kii window at coords                            </td></tr> 
+ <tr><td> clear            </td><td> erase plot                                                 </td></tr> 
+ <tr><td> clip             </td><td> clip values in a buffer to be within a range               </td></tr> 
+ <tr><td> cmpload          </td><td> load stars from a cmp file on overlay                      </td></tr> 
+ <tr><td> concat           </td><td> expand vector dimension                                    </td></tr> 
+ <tr><td> contour          </td><td> send contour to overlay                                    </td></tr> 
+ <tr><td> coords           </td><td> load coordinates for buffer from file                      </td></tr> 
+ <tr><td> create           </td><td> create a new vector                                        </td></tr> 
+ <tr><td> cursor           </td><td> get coordinates from the Kii window                        </td></tr> 
+ <tr><td> cut              </td><td> extract a cut across an image                              </td></tr> 
+ <tr><td> cval             </td><td> small median image                                         </td></tr> 
+ <tr><td> datafile         </td><td> define file to read vectors                                </td></tr> 
+ <tr><td> delete           </td><td> delete a buffer                                            </td></tr> 
+ <tr><td> demux            </td><td> demux an image pixels interleaved in Nx by Ny sections     </td></tr> 
+ <tr><td> device           </td><td> set / get current graphics device </td><td>                </td></tr> 
+ <tr><td> dimendown        </td><td> convert matrix to vector                                   </td></tr> 
+ <tr><td> dimenup          </td><td> convert vector to matrix                                   </td></tr> 
+ <tr><td> drizzle          </td><td> transform image to image                                   </td></tr> 
+ <tr><td> erase            </td><td> erase objects on an overlay                                </td></tr> 
+ <tr><td> extract          </td><td> extract a portion of a buffer into another buffer          </td></tr> 
+ <tr><td> fft1d            </td><td> fft on the pixel-stream in an image                        </td></tr> 
+ <tr><td> fft2d            </td><td> fft on an image                                            </td></tr> 
+ <tr><td> file             </td><td> test file                                                  </td></tr> 
+ <tr><td> fit              </td><td> least-squares fit to vectors                               </td></tr> 
+ <tr><td> fit2d            </td><td> least-squares fit to vectors                               </td></tr> 
+ <tr><td> flux             </td><td> flux in a convex contour                                   </td></tr> 
+ <tr><td> gauss            </td><td> get statistics on a star, assuming gaussian profile        </td></tr> 
+ <tr><td> gaussj           </td><td> solve Ax = B (N-D)                                         </td></tr> 
+ <tr><td> getchr           </td><td> find character in string                                   </td></tr> 
+ <tr><td> grid             </td><td> wait until return is typed                                 </td></tr> 
+ <tr><td> header           </td><td> print buffer header                                        </td></tr> 
+ <tr><td> histogram        </td><td> histogram of vector                                        </td></tr> 
+ <tr><td> imfit            </td><td> fit function                                               </td></tr> 
+ <tr><td> imhist           </td><td> histogram of an image region                               </td></tr> 
+ <tr><td> integrate        </td><td> integrate a vector                                         </td></tr> 
+ <tr><td> interpolate      </td><td> interpolate between vector pairs                           </td></tr> 
+ <tr><td> jpeg             </td><td> interpolate between vector pairs                           </td></tr> 
+ <tr><td> kern             </td><td> convolve with 3x3 kernel                                   </td></tr> 
+ <tr><td> keyword          </td><td> extract a FITS keyword from buffer header                  </td></tr> 
+ <tr><td> labels           </td><td> define labels for plot                                     </td></tr> 
+ <tr><td> limits           </td><td> define plot limits                                         </td></tr> 
+ <tr><td> load             </td><td> load an SAOimage style overlay                             </td></tr> 
+ <tr><td> mcreate          </td><td> create a matrix                                            </td></tr> 
+ <tr><td> medianmap        </td><td> small median image                                         </td></tr> 
+ <tr><td> memory           </td><td> long listing of the allocated memory                       </td></tr> 
+ <tr><td> mget             </td><td> extract a vector from a matrix                             </td></tr> 
+ <tr><td> minterp          </td><td> interpolate image pixels                                   </td></tr> 
+ <tr><td> mkgauss          </td><td> insert a gaussian in an image                              </td></tr> 
+ <tr><td> mset             </td><td> insert a vector in a matrix                                </td></tr> 
+ <tr><td> multifit         </td><td> solve for overlapping orders in ESI data                   </td></tr> 
+ <tr><td> objload          </td><td> load stars from an obj file on overlay                     </td></tr> 
+ <tr><td> outline          </td><td> vector statistics                                          </td></tr> 
+ <tr><td> peak             </td><td> find vector peak in range                                  </td></tr> 
+ <tr><td> plot             </td><td> plot a pair of vectors                                     </td></tr> 
+ <tr><td> point            </td><td> load overlay with single point                             </td></tr> 
+ <tr><td> profile          </td><td> radial profile at X, Y                                     </td></tr> 
+ <tr><td> ps               </td><td> make PS file from Kii                                      </td></tr> 
+ <tr><td> rd               </td><td> read an image from a file                                  </td></tr> 
+ <tr><td> read             </td><td> read vectors from datafile                                 </td></tr> 
+ <tr><td> rebin            </td><td> rebin data by factor of N                                  </td></tr> 
+ <tr><td> resize           </td><td> change Kii window size                                     </td></tr> 
+ <tr><td> roll             </td><td> roll image to new start point                              </td></tr> 
+ <tr><td> rotate           </td><td> rotate image                                               </td></tr> 
+ <tr><td> save             </td><td> save an SAOimage style overlay                             </td></tr> 
+ <tr><td> scalen           </td><td> get / set real bzero / bscale values                       </td></tr> 
+ <tr><td> section          </td><td> define section of graph                                    </td></tr> 
+ <tr><td> select           </td><td> selective vector assignment                                </td></tr> 
+ <tr><td> set              </td><td> do image arithmetic                                        </td></tr> 
+ <tr><td> sexigesimal      </td><td> convert to/from sexigesimal/decimal                        </td></tr> 
+ <tr><td> shift            </td><td> shift data in an image                                     </td></tr> 
+ <tr><td> simsignal        </td><td> simulate sine-wave with Nbit A/D and given S/N             </td></tr> 
+ <tr><td> sort             </td><td> sort vectors by key                                        </td></tr>                        
+ <tr><td> spline.apply     </td><td> apply spline fit to generate an image                      </td></tr> 
+ <tr><td> spline.construct </td><td> create spline 2nd deriv. terms                             </td></tr> 
+ <tr><td> sprintf          </td><td> formatted print to a variable                              </td></tr> 
+ <tr><td> star             </td><td> star stats at rough coords                                 </td></tr> 
+ <tr><td> stats            </td><td> give statistics on a portion of a buffer                   </td></tr> 
+ <tr><td> strlen           </td><td> string length                                              </td></tr> 
+ <tr><td> style            </td><td> set the style for graph plots                              </td></tr> 
+ <tr><td> subset           </td><td> reduce vector dimension                                    </td></tr> 
+ <tr><td> substr           </td><td> substring                                                  </td></tr> 
+ <tr><td> svd              </td><td> singular value decomposition of a matrix                   </td></tr> 
+ <tr><td> swapbytes        </td><td> byte swap thing                                            </td></tr> 
+ <tr><td> textline         </td><td> write text on Kapa window                                  </td></tr> 
+ <tr><td> tv               </td><td> display an image on the Kii window                         </td></tr> 
+ <tr><td> uniq             </td><td> create a uniq vector subset from a vector                  </td></tr> 
+ <tr><td> unsign           </td><td> toggle the UNSIGN status                                   </td></tr> 
+ <tr><td> vclip            </td><td> clip values in a vector to be within a range               </td></tr> 
+ <tr><td> vcontour         </td><td> create contour vectors                                     </td></tr> 
+ <tr><td> vectors          </td><td> list vectors                                               </td></tr> 
+ <tr><td> vstat            </td><td> vector statistics                                          </td></tr> 
+ <tr><td> wd               </td><td> write an image to a file                                   </td></tr> 
+ <tr><td> write            </td><td> write vectors to datafile                                  </td></tr> 
+ <tr><td> zap              </td><td> delete pixels                                              </td></tr> 
+ <tr><td> zplot            </td><td> plot x y with size scaled by z                             </td></tr> 
+ </table>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/Mana/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/Mana/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/Mana/index.htm	(revision 22322)
@@ -0,0 +1,48 @@
+<meta name=file  content=Mana>
+<meta name=title content=Mana user's guide>
+<meta name=page  content=Mana>
+
+<h3> Introduction </h3>
+
+Mana is a basic image and data analysis program based on the Opihi
+shell.  See the Opihi documentation for further descriptions of the
+tools provided by the Opihi shell.  In addition to these functions,
+Mana provides a collection of astronomical functions.
+
+<h3> Mana astronomy functions</h3>
+
+<pre>
+  findpeaks              -- find image peaks
+  fitcontour             -- fit ellipse contour
+  starcontour            -- object contour
+  rawstars               -- find raw star stats
+  biassub                -- subtract medianed overscan row or column
+  cgrid                  -- plot sky coordinate grid
+  coords                 -- load coordinates for buffer from file
+  cplot                  -- plot vectors in sky coordinates
+  csystem                -- convert between coordinate systems
+  ctimes                 -- convert between time formats
+  cval                   -- cosmic ray flux?
+  czplot                 -- plot scaled vectors in sky coordinates
+  drizzle                -- transform image to image
+  flux                   -- flux in a convex contour
+  gauss                  -- get statistics on a star, assuming gaussian profile
+  getvel                 -- rotcurve to velocities
+  imfit                  -- fit function
+  imsub                  -- subtract function
+  medianmap              -- small median image
+  mkgauss                -- generate a 2-D gaussian centered in image
+  multifit               -- fit multi-order spectrum
+  objload                -- plot obj data on Ximage 
+  outline                -- fit outline region
+  polar                  -- convert polar image to cartesian
+  precess                -- precess coordinates
+  profile                -- radial profile at X, Y
+  region                 -- define sky region for plot
+  rotcurve               -- convert CO images to polar coords
+  scale                  -- get / set real bzero / bscale values
+  sexigesimal            -- convert to/from sexigesimal/decimal
+  spec                   -- extract a spectrum
+  star                   -- star stats at rough coords
+  transform              -- geometric transformation of image
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/Opihi-Programs/index.htm	(revision 22322)
@@ -0,0 +1,563 @@
+<meta name=file  content=opihi>
+<meta name=title content=opihi user's guide>
+<meta name=page  content=opihi>
+
+<h3> Introduction </h3>
+
+<p>
+Opihi is a generic command-line interpreter which has been used as the
+front-end for Mana, DVO, and other high-level programs in the Elixir
+system.  The interpreter has a variety of features of shell-scripting
+languages: if statements, for loops, macros with command-line
+arguments, and so forth.  The shell also allows for variables,
+arithmetic on variables, input from source files, and a variety of
+other useful tools.  Opihi has a simple command-line interaction that
+resembles the UNIX <tt>tcsh</tt>, but with many additional useful
+features.  It can also be used as a scripting language much like
+<tt>sh</tt> or perl.
+
+<p>
+Opihi includes tools to manipulate and display 1-D (vector) and 2-D
+(image) data.  Two external programs are used for graphical display:
+the image display tool (Kii) and the graphing tool (Kapa).  Various
+functions are available to perform math, statistical, and other
+operations on vectors and images.
+
+<h3> User Interface </h3>
+
+<p>
+The command-line interaction is based on the readline libraries and
+behaves like <tt>tcsh</tt>.  Arrows can be used for editing.  There is both
+command and file completion with the TAB key.  You can also use
+emacs-like commands such as ctrl-a to reach the beginning of the line
+and ctrl-e to reach the end.  It is also possible to type just a
+fraction of a command, as long as it is unique.  An ambiguous command
+will list the possible alternatives.  For example:
+
+<pre>
+opihi: c
+ambiguous command: c ( catalog cgrid clear create cursor )
+</pre>
+
+Multiple commands can generally be placed on one line with semi-colons
+as separators.
+
+<h3> Data Representations</h3>
+
+<h4> Simple Scalar Variables </h4>
+
+<p>
+Scalar variables in Opihi are prepended with a dollar sign ($).  A
+variable may be created and the value assigned by a line which looks
+like:
+
+<p class=eq>
+$var = (expression)
+</p>
+
+where (expression) is some math expression.  The math expression may
+consist of the standard math operators (+,-,*,/) as well as any
+already-defined variables and the functions log(), ln(), sqrt(),
+exp(), ten() (10<sup>x</sup>), sin(), cos(), etc.  Other special
+operators are the carret (^) for exponetiation (eg, 2^3 is 8) and the
+@ symbol as a binary arc-tangent (eg, 1@3 is 18.43 degrees).  In
+addition, the operator <tt><<</tt> returns the minimum of the two
+surrounding values, and <tt>>></tt> returns the maximum.  Variables
+can be numeric or character strings.  If the shell does not understand
+the syntax of the line as a math expression, it is assumed to be a
+string.
+
+<em>local variables</em>
+
+<p>
+If there is a pair of curly brackets {} anywhere on a command line,
+whatever is inside is assumed to be a math expression and evaluated
+before the line is executed.  This later feature allows functions of
+variables to be passed as arguments to Opihi functions.  (see also the
+discussion below about temporary vectors and images).
+For example:
+
+<pre>
+echo {$fred*dcos(45)}
+</pre>
+
+would give the response 7.07107 if $fred had the value of 10.0.  There
+are math functions <tt>cos</tt>, <tt>sin</tt>, and <tt>tan</tt>, which operate on radian
+expressions, and also <tt>dcos</tt>, <tt>dsin</tt>, <tt>dtan</tt>, which operate on
+degree expressions.  There are also the equivalent inverse functions:
+eg., <tt>asin</tt> and <tt>dasin</tt> return radians and degrees, respectively.
+The help section on Math defines all of the available math functions.
+
+<h4> Lists </h4>
+
+Opihi lists are grouped sets of scalar variables (which may be
+strings).  A list consists of N variables with names of the form
+<tt>name:i</tt>, where the value of i ranges from 0 to N-1.  In addition,
+the list length is defined as the value <tt>name:n</tt>.  Since these are
+just informally grouped, a list may be defined by hand (ie, by
+defining each element and the length).  There is also the command
+<tt>list</tt> which builds a list from the following lines until reaching
+a line consisting of the single word <tt>end</tt>:
+
+<pre>
+list sample
+ value 0
+ value 1
+ test line
+end
+</pre>
+
+will define the variables <tt>$sample:0 - $sample:2</tt>, and <tt>$sample:n</tt>,
+with value of 3.
+
+<p>
+The list command may also construct a list from the output of a UNIX
+command:
+
+<pre>
+list sample -x "ls /tmp"
+</pre>
+
+will result in a list consisting of one entry for each file in the
+listing.  
+
+<p>
+The list command may also be used to split a string by whitespace:
+
+<pre>
+list sample -split this is a test
+</pre>
+will result in a list with 4 elements, one for each word. 
+
+<h4> queues </h4>
+
+<pre>
+queuelist                 -- list existing queues
+queuesize                 -- return the length of the given queue
+queueinit                 -- create a zero-length queues
+queuedelete               -- delete a queues
+queuepush                 -- add an item to the end of the queue
+queuepop                  -- remove and return the first item in the queue
+</pre>
+
+A queue is a data construct consisting of a sequence of lines from
+which simple selections can be made.  Data items are added and removed
+from the queue with <tt>queuepush</tt> and <tt>queuepop</tt> commands which push
+entries on the end of the queue and pop them off the beginning.  The
+available queues may be obtained with the command <tt>queuelist</tt>, and
+the length of a specific queue may be determined with the <tt>queuesize</tt>
+command.  The contents of a queue may be printed with <tt>queueprint</tt>.
+Pushing data onto a non-existent queue will create the queue.  An
+empty queue may be created with the command <tt>queueinit</tt> and a queue
+may be deleted with <tt>queuedelete</tt>.  
+
+<p>
+The <tt>queuepush</tt> commands allows for additional options which modify
+how the data is pushed on the queue.  The <tt>-uniq</tt> flag specifies that
+the queue should be search for an existing match and not add the new
+data item if a matching item already exists.  The <tt>-replace</tt> flag is
+similar to the <tt>-uniq</tt> flag, but instead the new item will replace
+the existing match, if a match is found.  These two options have
+identical results if the match is made based on the entire line.
+However, they may be more usefully distinguished by specifying a
+restriction on the match with the <tt>-key</tt> flag.  This flag specified
+which whitespace-separated element of the line to use for the match,
+with the first element being element 0.  
+
+<p>
+Numerical vectors and images (matrices) are discussed later in this
+document.
+
+<h3> Opihi Shell Programing and Flow-control</h3>
+
+<pre>
+input                     -- read command lines from a file 
+macro                     -- deal with the macros 
+for                       -- for loop 
+if                        -- logical cases 
+while                     -- while loop
+break                     -- escape from function 
+continue                  -- next loop iteration
+</pre>
+
+<p>
+There are several options for programming in the opihi shell.  First,
+a file which contains a series of commands can be executed with
+<tt>input (filename)</tt>.  When an opihi-based program is started, it
+loads a file from the user's home directory, with a name of the form
+<tt>~/.programrc</tt>.  This resource file may contain any commands,
+and allows a user to customize his or her environment.
+
+<p>
+It is also possible to define macros which will behave much like
+regular commands.  A macro is defined by typing <tt>macro name</tt> or
+<tt>macro create name</tt> followed by the commands.  Arguments to the
+macro are assigned to the variables $1 .. $N and the number of
+arguments is given by $0.  Macros may be defined in <tt>input</tt>
+files.  The following is a sample macro:
+
+<pre>
+macro test
+ echo "this is a macro"
+ echo "number of arguments $0"
+ echo "first argument $1"
+ if ($1 = 10) 
+   echo first argument is 10
+ end
+end
+</pre>
+
+<p>
+Opihi has several types of flow-control features.  These include
+for-loops, while-loops, if-else blocks.  These blocks are defined by
+the corresponding command (<tt>for</tt>, <tt>while</tt>, <tt>if</tt>) and are terminated
+with by a line with the single word <tt>end</tt>.  
+
+<p>
+The for-loop syntax is simplistic.  The <tt>for</tt> command specifies
+the loop variable, the starting value, the ending value, and
+optionally the delta for each loop.  The implicit loop test is always
+to check if the loop variable is still less than the end value (or
+greater than if the delta value is negative).  The definitions of this
+loop syntax and the value of the list length (<tt>$list:n</tt>) and
+the vector length (<tt>vector[]</tt>) make for natural loops over all
+elements of a list or vector.  Below are a few examples:
+
+<pre>
+for i 1 10 0.1
+ echo $i
+end
+</pre>
+This runs the loop with the variable <tt>$i</tt> running from 1.0, 1.1, up to 9.9
+ (inclusive).
+
+<pre>
+for i 0 $list:n
+ echo $list:$i
+end
+</pre>
+This would print all the elements of the list.
+
+<pre>
+for i 0 vector[]
+ echo vector[$i]
+end
+</pre>
+This would print all the elements of the vector.
+
+<p>
+The if-block begins with a line of the form: <tt>if (condition)</tt> and
+ends with a single <tt>end</tt>.  A line with a single <tt>else</tt> specifies the
+optional else portion of the block.  The conditional expression is a
+valid math inequality with approximately C-syntax: 
+
+<pre>
+(($i < 10) && ($i > 4))
+</pre>
+
+The elements of the inequality may also be string comparisons.  The
+only valid string comparisons are <tt>==</tt> and <tt>!=</tt>.  
+
+<p>
+The while loop begins with a line of the form <tt>while (condition)</tt> and
+ends with a single <tt>end</tt>.  The conditions follow the same rules as
+the if conditional statements.
+
+<em>continue, break, auto-break concepts</em>
+
+
+<h3> Miscellaneous Commands </h3>
+
+<pre>
+!                         -- system call
+?                         -- list commands 
+??                        -- list variables 
+echo                      -- type this line 
+exec                      -- system call
+exit                      -- exit program 
+help                      -- get help on a function 
+config                    -- reload configuration information
+exit                      -- exit program 
+quit                      -- exit program 
+wait                      -- wait until return is typed
+sleep                     -- sleep for N seconds
+usleep                    -- sleep for N microseconds
+which                     -- show command 
+cd                        -- change current working directory
+pwd                       -- report current working directory
+date                      -- return the current date
+file                      -- test file existence
+memory                    -- long listing of the allocated memory
+version                   -- list version information
+</pre>
+
+Most of these commands should be clear from the simple descriptions
+above, or from the online help.  The command ?? prints the system
+variables.  The <tt>help</tt> command will provide help on a single
+command or, without any arguments, will list all available help files
+(this includes general help not associated with a specific command).
+
+<h3> String Manipulation and other data commands </h3>
+
+<pre>
+local                     -- define local variables
+output                    -- redirect output to file
+scan                      -- scan line from keyboard or file to variable 
+sprintf                   -- formatted print to a variable
+fprintf                   -- formatted print to standard output
+getchr                    -- find character in string
+strlen                    -- string length
+substr                    -- substring
+</pre>
+
+These commands provide a few tools to manipulate data in opihi
+variables.  The #local/# command specifies that the given variable is
+local to its macro.  The #output/# command changes the default output
+destination for functions which print results to the screen.  This
+function does not zero the file length; new output is appended to the
+file.  The command #scan/# reads specific lines from a file, an
+optionally places them in an opihi variable (EOF is returned at the
+end of a file).  The group of commands #getchr/#, #strlen/#, and
+#substr/# provide the ability to manipulate the elements of a string.
+Finally, #sprintf/# and #fprintf/# perform formatted output to a
+variable and to the screen, respectively.  
+
+<h3> Vectors</h3>
+
+<h4>Vector Manipulation Functions</h4>
+
+<pre>
+create                    -- create a new vector
+hist                      -- create histogram from a vector
+print                     -- write vectors to file
+vectors                   -- list vectors
+accum                     -- accumulate vector values in another vector
+applyfit                  -- apply fit to new vector
+applyfit2d                -- apply 2-d fit to new vector
+circstats                 -- circular statistics
+concat                    -- reduce vector dimension
+datafile                  -- define file to read vectors
+delete                    -- delete vectors or matrices
+fft1d                     -- fft on the pixel-stream in an image
+fit                       -- fit polynomial to vector pair
+gaussdev                  -- generate a gaussian deviate vector
+gaussint                  -- return the integrated gaussian vector
+histogram                 -- generate histogram from vector
+integrate                 -- integrate a vector
+interpolate               -- interpolate between vector pairs
+vectors                   -- list vectors
+medacc                    -- accumulate vector values in another vector
+peak                      -- find vector peak in range
+read                      -- read vectors from datafile
+set                       -- image and vector math
+sort                      -- sort list of vectors
+subset                    -- expand vector dimension
+uniq                      -- create a uniq vector subset from a vector
+vbin                      -- bin values in a vector to be within a range
+vclip                     -- clip values in a vector to be within a range
+select                    -- selective vector assignment
+vgauss         
+vgrid          
+vload                     -- load vectors on Kii
+vstat                     -- get info from imreg database
+vsmooth                   -- gaussian smooth of a vector
+vroll                     -- roll vector elements
+vpop                      -- remove first element
+write                     -- write vectors to datafile
+</pre>
+
+<h4>The Graphing Window (Kapa)</h4>
+
+<pre>
+box                       -- draw a box on the plot
+clear                     -- erase plot
+cursor                    -- get coords from cursor
+grid                      -- plot cartesian grid
+labels                    -- define labels for plot
+limits                    -- define plot limits
+plot                      -- plot a pair of vectors
+ps                        -- define labels for plot
+style                     -- set the style for graph plots
+zplot                     -- plot scaled points 
+device                    -- set / get current graphics device
+jpeg                      -- write text line on graph
+line                      -- plot line
+dot                       -- plot a single point
+resize                    -- set graphics/image window size
+section                   -- define section of graph
+textline                  -- write text line on graph
+zplot                     -- plot x y with size scaled by z
+</pre>
+
+<p>
+Opihi has a variety of commands to manipulate 1-D data (vectors).
+Vectors can be loaded from a data file or created with uniform
+spacing.  New vectors can be defined as the arithmetical combinations
+of other vectors.  For example, we could create a sine wave with the
+following two lines:
+
+<p>
+A vector can be created based on a number sequence with the command
+<tt>create name Nelements start delta</tt>.  The resulting vector has
+#Nelements/# entries, starting at a value of #start/# and running
+until #start + delta*Nelements/#.  If #delta/# is 0.0, all elements
+will have the value of #start/#.  A histogram of a vector may be made
+with the command <tt>hist</tt>, which creates a new vector containing
+the histogram of the first vector.  The data range and bin size of the
+histogram are defined in same way as with create.  This makes it easy
+to create the index vector that goes with a histogram vector:
+
+<pre>
+hist y Ny 1 100 0.1
+create dx 1 100 0.1
+</pre>
+
+<p>
+The above will create a histogram of y in Ny and the index in dx.
+Plotting this with <tt>plot dx Ny</tt> will show the histogram.
+
+<p>
+Vector math is performed with a command of the form <tt>set new =
+  (expression)</tt>.  The expression is some math function employing
+  vectors and scalars.  A complete listing of the math operators
+  available in <tt>set</tt> can be found in the help for <tt>set</tt>.
+
+<p>
+Once vectors are defined, they may be plotted.  A pair of vectors can
+be plotted against each other if they have the same number of entries.
+The plotting is performed on the graphics window, Kapa.  There are
+actually several graphics windows available to <tt>status</tt>, any of
+which may be used to plot at any time.  Some of the more complex
+operations default to either graphics window 0 or 1, depending on the
+context.  Except for those functions with a pre-defined window, all
+plotting functions apply to the current graphics window unless an
+option <tt>-n N</tt> is given to specify a different window.  The
+plotting style is determined by the command <tt>style</tt> which can set
+the line width, the line type (solid, dashed, dotted, etc), the point
+type (box, cross, etc), the point size, the color, and whether a pair
+of vectors is plotted as a sequence of points, a set of connected
+lines, or a histogram.  Some functions which make plots use their own
+styles, as discussed below.  The function <tt>limits</tt> lets the user
+set the range of the plot axes, or check the current setting.  The
+command <tt>plot</tt> will plot a pair of vectors on the current graphics
+window using the current plotting style for that window.  The command
+<tt>zplot</tt> will plot a pair of vectors with the point size scaled by
+a third vector, with maximum and minimum point sizes representing
+specified values.  The <tt>cursor</tt> command goes the other way: this
+command puts the Kapa window in cursor mode and waits for input from
+Kapa.  The user can then type any alphanumeric key on the graphics
+windows and will be told both the pointer location (in the graphics
+coordinates) and will have the coordinates stored in <tt>status</tt>
+variables.  For example, by typing ``1'' in the sky display window,
+the RA and DEC of the pointer are stored in the variables <tt>$R1</tt>
+and <tt>$D1</tt>.  This command can be used to let the user define
+locations or regions of interest on the Kapa window. (Future addition:
+<tt>button</tt>, which does the same with the mouse buttons).  
+
+<pre>
+create x 1 20 0.01
+set y = sin(x)
+</pre>
+
+We can plot this pair of vectors on the Kapa graphing window:
+
+<pre>
+limit x y
+plot x y
+box
+</pre>
+
+The Kapa window has a variety of style options to change the plotting
+type (line, histogram, point), point type, line color, weight and
+style, and the errorbar style (if errorbars are plotted).
+
+<h3> 2D Data and the Image Display Window </h3>
+
+<p>
+Opihi has many commands to manipulate 2-D data (images).  Images can be
+loaded from FITS files and mathematical operations applied to them.
+Various other operations, such as rebinning, shifting, rotating, and
+so forth are also available.  Images can be displayed using the Kii
+image window.  In an 8-bit visual, the image window has a dynamic
+colormap to allow the user to change the relationship between pixel
+value and the displayed color.  There are three colormaps which can be
+selected by pressing the middle three buttons on the bottom row.  The
+PS button produces a PostScript file from the image.  The user can
+change the magnification and the position of the image with the mouse
+(left - recenter at cursor; middle - zoom out; right - zoom in).
+There are four color overlays which the user can draw objects of
+different shapes (circles, boxes, lines) or draw a contour.  The four
+buttons labeled R, G, B, Y turn on or off the display of the red,
+green, blue, and yellow overlays.
+
+<h4>Image Manipulation Functions</h4>
+
+<pre>
+clip                      -- clip values in a buffer to be within a range
+contour                   -- create contour from image
+cut                       -- extract a cut across an image
+extract                   -- extract a portion of a buffer into another buffer
+fft2d                     -- fft on an image
+fit2d                     -- fit 2-d polynomial to vector triplet
+header                    -- print buffer header
+imcut                     -- linear image cut between arbitrary coords
+imhist                    -- histogram of an image region
+imsmooth                  -- circular gaussian smoothing
+kern                      -- convolve with 3x3 kernel
+keyword                   -- extract a FITS keyword from buffer header
+buffers                   -- list the currently allocated buffers
+mcreate                   -- create a matrix
+minterp                   -- interpolate image pixels
+rd                        -- load fits image
+rdseg                     -- read a segment of an image from a file
+rebin                     -- rebin data by factor of N
+roll                      -- roll image to new start point
+rotate                    -- rotate image
+set                       -- image and vector math
+shift                     -- shift data in an image
+spline.apply              -- apply spline fit to generate an image
+spline.const              -- create spline 2nd deriv. terms
+stats                     -- give statistics on a portion of a buffer
+subraster                 -- subraster of fits image
+unsign                    -- toggle the UNSIGN status
+wd                        -- write an image to a file
+zap                       -- delete pixels
+</pre>
+
+<h4>The Image Display Window (Kii)</h4>
+
+<pre>
+ps                        -- define labels for plot
+center                    -- center image on coords
+device                    -- set / get current graphics device
+erase                     -- erase objects on an overlay
+jpeg                      -- write text line on graph
+load                      -- load an SAOimage style overlay
+point                     -- load overlay with single point
+save                      -- save an SAOimage style overlay
+resize                    -- set graphics/image window size
+tv                        -- display an image on the Kii window
+tvcontour                 -- send contour to overlay
+tvgrid                    -- wait until return is typed
+</pre>
+
+
+<h4>Other data functions</h4>
+
+<pre>
+gaussj                    -- solve Ax = B (N-D)
+gridify                   -- convert vector triplet to buffer
+ungridify                 -- convert buffer region to vector triplet
+dimendown                 -- convert matrix to vector
+dimenup                   -- convert vector to matrix
+mget                      -- extract a vector from a matrix
+mset                      -- insert a vector in a matrix
+svd                       -- singular value decomposition of a matrix
+swapbytes                 -- byte swap thing
+</pre>
+
+
+
+<h3> Programming Considerations </h3>
+The programming structure of the Opihi front-end
+makes it very easy to add commands to the package.
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/bugzilla.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/bugzilla.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/bugzilla.htm	(revision 22322)
@@ -0,0 +1,11 @@
+<meta name=file  content=bugzilla>
+<meta name=title content=Bugzilla>
+<meta name=page  content=Bugzilla>
+
+<p>
+Ohana bugs and other issues may be reported on the Ohana portion of
+the
+
+<a href="http://kiawe.ifa.hawaii.edu/bugzilla">Elixir Bugzilla</a>
+
+web pages
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/download.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/download.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/download.htm	(revision 22322)
@@ -0,0 +1,31 @@
+<meta name=file  content=download>
+<meta name=title content=Download Ohana>
+<meta name=page  content=download>
+
+<p>
+This page lists Ohana distributions which are available as complete
+tar packages.  There are different distributions representing
+different subsets of the Ohana collection of software.  The Mana
+distribution provides the libraries and components needed to compile
+the <em>mana</em> and <em>dvo</em> programs and their associated
+tools.  The Elixir distribution provides the complete set of Ohana
+tools used by the Elixir system at CFHT.  For <em>dvo</em> or the
+Elixir distribution, you will also need the Elixir configuration
+package.  Other library requirements for the complete Elixir include:
+
+<ul>
+<li> libjpeg 
+<li> libpng
+<li> the readline library
+<li> zlib
+<li> apache
+<li> flips (v.2)
+<li> sextractor
+<li> Xvnc
+</ul>
+
+<ul>
+<li><a href=download/mana-1.0.tgz>mana-1.0.tgz </a>
+<li><a href=download/elixir-ohana-1.2.tgz>elixir-ohana-1.2.tgz </a>
+<li><a href=download/elixir-ohana-head.tgz>elixir-ohana-head.tgz </a>
+<li><a href=download/elixir-config-1.2.tgz>elixir-config-1.2.tgz </a>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/download/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/download/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/download/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.html *.tgz
+index.idx
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/install.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/install.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/install.htm	(revision 22322)
@@ -0,0 +1,78 @@
+<meta name=file  content=install>
+<meta name=title content=Installling Ohana>
+<meta name=page  content=install>
+
+<p>
+The Elixir system consists of two main software packages: Ohana (main
+developer Eugene Magnier) and Flips (main developer Jean-Charles
+Cuillandre).  The Elixir system combines these two packages, along
+with other external software products, into a complete system
+providing many utilities and functions for image processing at CFHT
+and elsewhere.  This document describes how to install the Ohana
+portion of the Elixir system.  Ohana may be installed from CVS or from
+a distributed tar ball.  Within the Elixir development community, the
+Ohana source tree may be checked out of the Elixir CVS tree.  Ohana is
+also distributed as a tar ball to users outside of the Elixir
+development community.
+
+<p>
+1) Installing Ohana from source tar ball: <br><br>
+
+  un-gzip and untar:   <br><br>
+
+    <tt> > gunzip -c ohana.tgz | tar xvf - </tt></br></br>
+
+  enter the ohana top-level directory: <br><br>
+
+    <tt> > cd ohana</tt><br><br>
+
+  construct the input Configure file [1]: <br><br>
+
+    <tt> > configure</tt><br><br>
+
+  this step will note the binary installation path.  This should be
+  added to your PATH.  Next, compile the software: <br><br>
+
+    <tt> > make</tt><br><br>
+
+  install the software <br><br>
+
+    <tt> > make install</tt><br><br>
+
+<p>
+2) Installing Ohana from CVS (Developer's version): <br><br>
+
+  check out the ohana source tree: <br><br>
+
+    <tt> > setenv CVSROOT poma:/data/elixir2/srcdir/cvs</tt><br><br>
+    <tt> > cvs co ohana</tt><br><br>
+
+  enter the ohana top-level directory: <br><br>
+
+    <tt> > cd ohana</tt><br><br>
+
+  construct the input Configure file [1]: <br><br>
+
+    <tt> > configure</tt><br><br>
+
+  this step will note the binary installation path.  This should be
+  added to your PATH.  Next, compile the software: <br><br>
+
+    <tt> > make</tt><br><br>
+
+  install the software <br><br>
+
+    <tt> > make install</tt><br><br>
+
+<hr>
+<h3> Notes </h3>
+
+<p>
+[1] Ohana is configured for multiple architecture development, which
+is convenient for end users as well.  The libraries, include files,
+and programs, are all installed in directories with the architecture
+type appended: bin/linux, bin/sol, lib/linux, etc.  To take advantage
+of this multiple architecture organization when compiling, end users
+should arrange for the environment variable ARCH to be set based on
+the machine architecture, and for the PATH to depend on ARCH, rather
+than setting it to a fixed quantity.  
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/kapa.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/kapa.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/kapa.htm	(revision 22322)
@@ -0,0 +1,5 @@
+<meta name=file  content=kapa>
+<meta name=title content=kapa>
+<meta name=page  content=kapa>
+
+Sorry!  The page you requested is not yet available.
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/kii.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/kii.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/kii.htm	(revision 22322)
@@ -0,0 +1,5 @@
+<meta name=file  content=kii>
+<meta name=title content=kii>
+<meta name=page  content=kii>
+
+Sorry!  The page you requested is not yet available.
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/libfits.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/libfits.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/libfits.htm	(revision 22322)
@@ -0,0 +1,116 @@
+<meta name=file  content=libfits>
+<meta name=title content=libfits>
+<meta name=page  content=libfits>
+
+<h3> FITS-related constants </h3>
+
+<pre>
+# define FT_TEXT_LENGTH          18  /* max length text header field */
+# define FT_MAX_NAXES            10  /* max number of axes */
+# define FT_FIELD_LENGTH          8  /* max length header field */   
+# define FT_COMMENT_LENGTH       47  /* max length comment field */
+# define FT_HISTORY_LENGTH       72  /* max length history / comment */
+# define FT_LINE_LENGTH          80  /* FITS header line length */
+# define FT_RECORD_SIZE        2880  /* FITS block size */
+</pre>
+
+<h3> misc FITS library functions </h3>
+
+<pre>
+char *fits_version ();
+</pre>
+
+<h3> FITS Header Manipulation functions </h3>
+
+<pre>
+int    fits_init_header (Header *header);
+int    fits_create_header (Header *header); 
+int    fits_copy_header (Header *in, Header *out); 
+
+int    fits_read_header (char *filename, Header *header);
+int    fits_write_header (char *filename, Header *header); 
+int    fits_save_header (FILE *f, Header *header);
+int    fits_load_header (FILE *f, Header *header);
+int    fits_fread_header (FILE *f, Header *header);
+void   fits_free_header (Header *header); 
+int    fits_fwrite_header (FILE *f, Header *header); 
+int    fits_read_Xheader (char *filename, Header *header, int N);
+char  *fits_header_field (Header *header, char *field, int N); 
+
+int    fits_modify (Header *header, char *field, char *mode, int N,...); 
+int    fits_print (Header *header, char *field, char *mode, int N,...); 
+int    fits_scan (Header *header, char *field, char *mode, int N,...);
+int    fits_vscan (Header *header, char *field, char *mode, int N, va_list argp);
+int    fits_delete (Header *header, char *field, int N); 
+char  *fits_keyword_end (char *line);
+int    fits_stripwhite (char *string);
+</pre>
+
+<h3> FITS Matrix (Image) Manipulation functions </h3>
+
+<pre>
+int    fits_create_matrix (Header *header, Matrix *); 
+int    fits_copy_matrix (Matrix *in, Matrix *out); 
+
+int    fits_read_matrix (char *filename, Matrix *matrix);      
+int    fits_write_matrix (char *filename, Matrix *matrix); 
+int    fits_fread_matrix (FILE *f, Matrix *matrix, Header *header);
+void   fits_free_matrix (Matrix *matrix); 
+int    fits_fwrite_matrix (FILE *f, Matrix *matrix);      
+int    fits_read_portion (char *filename, Matrix *matrix, int Nskip, int Npix);
+int    fits_read_segment (char *filename, Matrix *matrix, char *region);
+int    fits_load_matrix (FILE *f, Matrix *matrix, Header *header);
+
+int    fits_matrix_size (Header *header);
+int    fits_convert_format (Header *header, Matrix *matrix, int outBitpix, double outScale, double outZero, int outUnsign);
+void   fits_insert_array (Matrix *matrix, Matrix *array, int x, int y); 
+
+void   fits_set_matrix_value (Matrix *matrix, int x, int y, double); 
+double fits_get_matrix_value (Matrix *, int x, int y); 
+void   fits_add_matrix_value (Matrix *matrix, int x, int y, double value); 
+
+int    fits_divide_matrix (Matrix *numerator, Matrix *denomenator, Matrix *result); 
+int    fits_multiply_matrix (Matrix *matrix1, Matrix *matrix2, Matrix *result); 
+</pre>
+
+<h3> FITS Table Manipulation functions </h3>
+
+<pre>
+int   fits_load_Theader (FILE *f, Header *Theader);
+int   fits_read_Theader (char *filename, Header *header);     
+int   fits_write_Theader (char *filename, Header *header);
+int   fits_read_table (char *filename, FTable *ftable); 
+int   fits_write_table (char *filename, FTable *ftable); 
+int   fits_fwrite_table (FILE *f, FTable *ftable);
+int   fits_fwrite_vtable (FILE *f, VTable *ftable);
+int   fits_fread_ftable (FILE *f, FTable *ftable, char *extname); 
+int   fits_fread_vtable (FILE *f, VTable *ftable, char *extname, int Nrow, int *row);
+int   fits_fread_ftable_data (FILE *f, FTable *ftable);
+int   fits_fwrite_table (FILE *f, FTable *ftable); 
+int   fits_fwrite_vtable (FILE *f, VTable *vtable); 
+int   fits_read_ftable (char *filename, FTable *ftable, char *extname);
+int   fits_create_table_header (Header *header, char *type, char *extname);
+int   fits_create_table (Header *header, FTable *ftable);
+int   fits_free_table (FTable *ftable);
+int   fits_free_vtable (VTable *ftable);
+
+int   fits_bintable_format (char *format, char *type, int *Nval, int *Nbytes);
+int   fits_table_format (char *format, char *type, int *Nval, int *Nbytes);
+int   fits_set_bintable_column (Header *header, FTable *ftable, char *label, void *data, int Nrow);
+int   fits_set_table_column (Header *header, FTable *ftable, char *label, void *data, int Nrow);
+int   fits_define_bintable_column (Header *header, char *format, char *label, char *comment, char *unit, double bscale, double bzero);
+int   fits_define_table_column (Header *header, char *format, char *label, char *comment, char *unit);
+
+int   fits_vtable_from_ftable (FTable *ftable, VTable *vtable, int *row, int Nrow);
+int   fits_table_to_vtable (FTable *ftable, VTable *vtable, int start, int Nkeep);
+int   fits_vadd_rows (VTable *vtable, char *data, int Nrow, int Nbytes);
+int   fits_add_rows (FTable *ftable, char *data, int Nrow, int Nbytes);
+char *fits_table_print (FTable *ftable,...);
+int   fits_create_Theader (Header *header, char *type);
+int   fits_table_column (FTable *ftable, char *field, char *mode,...); 
+int   fits_table_column (FTable *ftable, char *field, char *mode,...);
+int   fits_get_bintable_column_type (Header *header, char *label, char *type, int *Nval);
+int   fits_get_bintable_column (Header *header, FTable *ftable, char *label, void **data);
+int   fits_get_table_column_type (Header *header, char *label, char *type);
+int   fits_get_table_column (Header *header, FTable *ftable, char *label, void **data);
+</pre>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/libohana.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/libohana.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/developer/libohana.htm	(revision 22322)
@@ -0,0 +1,278 @@
+<meta name=title content=libohana API reference>
+<meta name=page  content=libohana API reference>
+
+<h2> generic ohana functions </h2>
+
+<h3> basic macros </h3>
+
+<pre>
+TRUE
+FALSE
+SIGN(X)  
+ROUND(X) 
+SQR(X)   
+SQ(X)    
+MIN(X,Y) 
+MAX(X,Y) 
+SWAP(X,Y) 
+whitespace (char c)
+</pre>
+
+<h3> ohana memory management functions </h3>
+
+<pre> 
+ALLOCATE (void *ptr, type, int Nalloc);
+REALLOCATE (void *ptr, type, int Nalloc);
+CHECK_REALLOCATE (void *ptr, type, int Nalloc, int Nused, int Ndelta);
+free (void *ptr); 
+
+void  ohana_memregister (void *ptr);
+void  ohana_memdump (int mode);
+void  ohana_memcheck (int mode);
+</pre>
+
+<h3> string manipulation functions (string.c) </h3>
+
+<pre> 
+int   get_argument (int argc, char **argv, char *arg);
+int   remove_argument (int Narg, int *argc, char **argv);
+char *strcreate (char *string);
+char *strncreate (char *string, int Nbytes);
+int   strnumcmp (char *str1, char *str2);
+int   stripwhite (char *string);
+int   scan_line (FILE *f, char *line);
+int   dparse (double *value, int Nentry, char *line);
+int   fparse (double *value, int Nentry, char *line);
+char *_parse_nextword (char *string);
+</pre>
+
+<pre> int get_argument (int argc, char **argv, char *arg); </pre>
+
+search argv, argc list (starting at argv[1]) for desired argument.
+returns entry number if found, 0 otherwise. 
+<hr>
+
+<pre> int remove_argument (int Narg, int *argc, char **argv); </pre>
+
+remove the given entry Narg from the argv list, adjusting argc
+appropriately. 
+<hr>
+
+<pre> int stripwhite (char *string); </pre>
+
+strip whitespace (space or tab) from start and end of string.
+<hr>
+
+<pre> int strnumcmp (char *str1, char *str2); </pre>
+
+compare str1 and str2 as strings, or as numbers if both are pure
+numeric values (base 10 only).  returns TRUE / FALSE.
+<hr>
+
+<h3> file system functions (findexec.c) </h3>
+
+<pre> 
+char *pathname (char *file);
+char *filerootname (char *file);
+char *fileextname (char *file);
+char *filebasename (char *file);
+char *findexec (int argc, char **argv);
+int   mkdirhier (char *path);
+char *getcwd_cfht (char *path, int size);
+void  make_backup (char *filename);
+int   Fseek (FILE *f, long offset, int whence);
+</pre>
+
+<h3> lockfile functions (glockfile.c) </h3>
+
+<pre> 
+FILE *fsetlockfile (char *filename, double timeout, int type, int *state);
+int   fclearlockfile (char *filename, FILE *f, int type, int *state);
+</pre>
+
+associated constants:
+
+<pre>
+LCK_SOFT - block writing
+LCK_XCLD - block reading and writing
+LCK_HARD - block reading and writing, persistent
+
+LCK_UNLOCK   - file is unlocked 
+LCK_ACCESS   - can't get access to file 
+LCK_TIMEOUT  - timeout setting lock 
+LCK_HARDLCK  - error setting hard lockfile 
+LCK_HUNLOCK  - error clearing hard lockfile 
+            
+LCK_EMPTY    - locked file is empty 
+LCK_FULL     - locked file is not empty 
+LCK_UNKNOWN  - can't stat file to get size 
+</pre>
+
+<h3> time / radec manipulation functions (time.c) </h3>
+
+<pre>
+int    dms_to_ddd (double *Value, char *string);
+int    str_to_radec (double *ra, double *dec, char *str1, char *str2);
+int    chk_time (char *line);
+int    str_to_time (char *line, time_t *second);
+int    str_to_dtime (char *line, double *second);
+double sec_to_jd (time_t second);
+time_t jd_to_sec (double jd);
+double sec_to_mjd (time_t second);
+time_t mjd_to_sec (double mjd);
+char  *sec_to_date (time_t second);
+time_t date_to_sec (char *date);
+time_t short_date_to_sec (char *date);
+int    hstgsc_hms_to_deg (double *h0, double *h1, double *d0, double *d1, char *string);
+</pre>
+
+<h3> functions in config.c </h3>
+
+<pre> 
+char *SelectConfigFile (int *argc, char **argv, char *progname);
+char *LoadConfigFile (char *filename);
+char *ScanConfig (char *config, char *field, char *mode, int Nentry, ...);
+char *expandline (char *line, char *config);
+char *LoadRawConfigFile (char *filename, int options);
+</pre>
+
+<h3> miscellaneous functions </h3>
+
+<pre> char *ohana_version (); </pre>
+
+<h2> DVO data structures and catalog I/O functions </h2>
+
+<h3> read/write (Fread.c) </h3>
+
+<pre> 
+int Fread (char ptr, int size, int nitems, FILE *f, char *type);
+int Fwrite (char ptr, int size, int nitems, FILE *f, char *type);
+int ByteSwap (char *ptr, int size, int nitems, char *type);
+int ConvertStruct (char *buffer, int size, int nitems, char *type);
+</pre>
+
+Several of these functions are used to perform byte-swapping for
+structures in the pre-autocode days.  They should be deprecated and
+dropped, but the autocoding must superceed the hard-wired structure
+first.
+
+<h3> coordinate structure manipulations (coordops.c) </h3>
+
+<pre> 
+int XY_to_RD (double *ra, double *dec, double x,  double y,   Coords *coords);
+int RD_to_XY (double *x,  double *y,   double ra, double dec, Coords *coords);
+int fXY_to_RD (float *ra, float *dec, float x,  float y,   Coords *coords);
+int fRD_to_XY (float *x,  float *y,   float ra, float dec, Coords *coords);
+int GetCoords (Coords *coords, Header *header);
+int coords_precess (double *ra, double *dec, double in_epoch, double out_epoch);
+</pre>
+
+<h3> photcode manipulations (LoadPhotcodes.c) </h3>
+
+<pre>
+int     LoadPhotcodes (char *filename, PhotCodeData *photcodes);
+int     FreePhotcodes (PhotCodeData *photcodes);
+
+PhotCode *GetPhotcodebyName (char *name);
+PhotCode *GetPhotcodeEquivbyName (char *name);
+PhotCode *GetPhotcodebyCode (int code);
+PhotCode *GetPhotcodeEquivbyCode (int code);
+PhotCode *GetPhotcodebyNsec (int Nsec);
+char     *GetPhotcodeNamebyCode (int code);
+
+int     GetPhotcodeCodebyName (char *name);
+int     GetPhotcodeEquivCodebyName (char *name);
+int     GetPhotcodeEquivCodebyCode (int code);
+int     GetPhotcodeNsec (int code);
+int     GetPhotcodeNsecfilt ();
+int      *GetPhotcodeEquivList (int code, int *nlist);
+
+void    SetZeroPoint (double ZP);
+void    ParseColorTerms (char *terms, float *X, int *N);
+
+int       PhotColor (Average *average, SecFilt *secfilt, Measure *measure, int c1, int c2, double *color);
+short   iPhotColor (Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+
+double  PhotInst (Measure *measure);
+double  PhotCat (Measure *measure);
+double  PhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+double  PhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+double  PhotCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+double  PhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+double  PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+double  PhotdM (PhotCode *code, Average *average, SecFilt *secfilt);
+double  PhotXm (PhotCode *code, Average *average, SecFilt *secfilt);
+
+short   iPhotInst (Measure *measure);
+short   iPhotCat (Measure *measure);
+short   iPhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+short   iPhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+short   iPhotCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+short   iPhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+short   iPhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+short   iPhotdM (PhotCode *code, Average *average, SecFilt *secfilt);
+short   iPhotXm (PhotCode *code, Average *average, SecFilt *secfilt);
+</pre>
+
+<h3> photometry catalog I/O functions (phot_catalog.c) </h3>
+
+<pre>
+int lock_catalog (Catalog *catalog, int lockmode);
+int unlock_catalog (Catalog *catalog);
+int load_catalog (Catalog *catalog, char mode, int VERBOSE);
+int save_catalog (Catalog *catalog, char VERBOSE);
+</pre>
+
+<h3> DVO format conversions (dvo_convert.c) </h3>
+
+<pre>
+Measure *FixOldMeasure (OldMeasure *in, int Nvalues); 
+Average *FixOldAverage (OldAverage *in, int Nvalues); 
+SecFilt *FixOldSecFilt (OldSecFilt *in, int Nvalues); 
+</pre>
+
+<h3> image type / mode lookups (imreg_datatypes.c) </h3>
+
+<pre>
+int   get_image_type (char *name); 
+char *get_type_name (int type); 
+int   get_image_mode (char *name); 
+char *get_mode_name (int mode); 
+</pre>
+
+<h3> mosaic astrometry functions (mosaic_astrom.c) </h3>
+
+<pre>
+int FindMosaicForImage (Image *images, int Nimages, int entry); 
+int FindMosaicForImage_TableSearch (Image *images, int Nimages, int entry); 
+int FindMosaicForImage_MatchSearch (Image *images, int Nimages, int entry); 
+int BuildChipMatch (Image *images, int Nimages); 
+</pre>
+
+<h3> FITS DB I/O functions (fits_db.c) </h3>
+
+<pre>
+int fits_db_init (FITS_DB *db); 
+int fits_db_create (FITS_DB *db); 
+int fits_db_lock (FITS_DB *db, char *filename); 
+int fits_db_load (FITS_DB *db); 
+int fits_db_save (FITS_DB *db); 
+int fits_db_update (FITS_DB *db, VTable *vtable); 
+int fits_db_free (FITS_DB *db); 
+int fits_db_close (FITS_DB *db); 
+</pre>
+
+<h3> functions in photfits.c </h3>
+
+<pre>
+short int putMi (double value); 
+double getMi (short int value); 
+void returnMcal (Image *image, double *c); 
+void assignMcal (Image *image, double *c, int order); 
+double applyMcal (Image *image, double x, double y); 
+double findscatter (double *X, double *Y, double *M, double *dM, int N, double *c, int order); 
+</pre>
+
+These functions manipulate the high-order polynomial fit of magnitude
+offset as a function of image position for the Image structure.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/download.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/download.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/download.htm	(revision 22322)
@@ -0,0 +1,101 @@
+<meta name=file  content=download>
+<meta name=title content=Download Ohana>
+<meta name=page  content=download>
+
+<p>
+This page lists Ohana packages which are available as complete
+tarballs.  There are different packages representing different subsets
+of the Ohana collection of software.  To install these, simply run
+configure, make, and make install (see <a href="install.html">the
+installation instructions</a> for further details).  Also list on this
+page are the Elixir configuration tarballs, which provide information
+needed by some of the Elixir programs.
+
+<p>
+The following external libraries are needed for Ohana.
+
+<ul>
+<li> libjpeg 
+<li> libpng
+<li> the readline library
+<li> zlib
+</ul>
+
+<h4> Mana Distributions </h4> 
+
+The Mana distribution provides the libraries and components needed to
+compile <tt>mana</tt>, an data and image analysis tool, and the
+associated display tools, <tt>Kapa</tt> and <tt>Kii</tt>.  
+
+<ul>
+<li><a href=download/tarballs/mana-1.1.tgz>mana-1.1.tgz </a>
+<li><a href=download/tarballs/mana-1.0.tgz>mana-1.0.tgz </a>
+</ul>
+
+<h4> Elixir-Ohana Distributions </h4> 
+
+The Elixir-Ohana distribution provides the complete set of Ohana
+software used by the Elixir system at CFHT.  To use the Elixir system,
+you will also need the Elixir configuration package.
+
+<ul>
+<li><a href=download/tarballs/elixir-ohana-head.tgz>elixir-ohana-head.tgz </a>
+<li><a href=download/tarballs/elixir-ohana-1.6.tgz>elixir-ohana-1.6.tgz </a>
+<li><a href=download/tarballs/elixir-ohana-1.5.tgz>elixir-ohana-1.5.tgz </a>
+<li><a href=download/tarballs/elixir-ohana-1.4.tgz>elixir-ohana-1.4.tgz </a>
+<li><a href=download/tarballs/elixir-ohana-1.3.tgz>elixir-ohana-1.3.tgz </a>
+<li><a href=download/tarballs/elixir-ohana-1.2.tgz>elixir-ohana-1.2.tgz </a>
+<li><a href=download/tarballs/elixir-ohana-1.1.tgz>elixir-ohana-1.1.tgz </a>
+<li><a href=download/tarballs/elixir-ohana-1.0.tgz>elixir-ohana-1.0.tgz </a>
+</ul>
+
+<p>
+Other library requirements for the complete Elixir system include:
+<ul>
+<li> apache
+<li> flips (v.2)
+<li> sextractor
+<li> Xvnc
+</ul>
+
+<h4> Opihi Distributions </h4> 
+
+The Opihi distribution provides all of the opihi programs (<tt>mana</tt>,
+<tt>dvo</tt>, <tt>psched</tt>, <tt>pcontrol</tt>, and <tt>pclient</tt>) and the supporting
+programs <tt>Kapa</tt> and <tt>Kii</tt>.
+
+<ul>
+<li><a href=download/tarballs/opihi-2.3.tgz>opihi-2.3.tgz </a>
+</ul>
+
+<h4> Psched Distributions </h4> 
+
+The Psched distribution provides the related programs <tt>psched</tt>,
+<tt>pcontrol</tt>, and <tt>pclient</tt>.  These programs are used for the
+Pan-STARRS IPP scheduling and parallel-processing system.
+
+<ul>
+<li><a href=download/tarballs/psched-0.1.tgz>psched-0.1.tgz </a>
+</ul>
+
+<h4> Ohana Tools Distributions </h4> 
+
+The Ohana Tools distribution provides the various useful tools from
+the Ohana system, including FITS-file manipulation program, precession
+/ coordinate coversions, and the <tt>gcompare</tt> tool.  
+
+<ul>
+<li><a href=download/tarballs/ohana-tools-1.0.tgz>ohana-tools-1.0.tgz </a>
+</ul>
+
+<h4> Elixir Configuration Distributions </h4> 
+
+The Elixir Configuratin distribution provides the configuration files
+needed by several of the Ohana Elixir program.
+
+<ul>
+<li><a href=download/tarballs/elixir-config-1.2.tgz>elixir-config-1.2.tgz </a>
+<li><a href=download/tarballs/elixir-config-1.1.tgz>elixir-config-1.1.tgz </a>
+<li><a href=download/tarballs/elixir-config-1.0.tgz>elixir-config-1.0.tgz </a>
+</ul>
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/icons/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/icons/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/icons/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+.xvpics
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/index.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/index.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/index.htm	(revision 22322)
@@ -0,0 +1,60 @@
+<meta name=file  content=home>
+<meta name=title content=Ohana User's Guide>
+<meta name=page  content=Ohana>
+
+<p>
+Ohana is the collection of programs written by Eugene Magnier which
+form a major portion of the Elixir software.  These pages document the
+user interface to the various Ohana programs.
+
+<p>
+Within the Ohana software system, there are several types of programs
+and program groups.  There are basic, independent C programs, groups
+of related C programs which address a common Elixir system, C programs
+which run as daemons, and C programs which provide an interactive
+command language environment.  There are also a number of perl, csh,
+and tcl scripts.  
+
+<h4> Software Organization</h4>
+
+The top-level of the Ohana software tree includes the Makefile, a
+system-dependent configuration file, and the directories src, bin,
+lib, and include.  Within the bin, lib, and include directories, there
+are subdirectories by computer architecture.  These allow a
+segregation of the architecture dependent binary files, and provide
+some flexibility for working will various types of hardware.  The
+Makefiles require the environment variable ARCH to be set to make
+complilation choices and to place the binaries in their appropriate
+locations.  The ohana user may choose to include this directory in
+their path, in which case they should define the variable in their
+resource files (.cshrc / .bashrc / etc) using the sample given in
+getarch, and set the pathname to the value ohana/bin/$ARCH, which will
+then correctly expand depending on what machine the user is one. An
+alternative is to treat the ohana tree as the program development
+tree, and to export the binaries to an architecture-dependent external
+location. 
+
+<p>
+The src directory contains all of the program and library source-code
+subdirectories.  Many of the programs are contained within their own
+unique src subdirectory, but there are a few execeptions.  First, the
+perl, csh, and tcl scripts are collected together in perl, csh, and
+tcl subdirectories.  Second, there are a variety of simple, small
+C-programs in the directory misc.  Third, there is a collection of
+related C programs which provide user interfaces to the Elixir
+databases.  These are collected together in the imregister-3.0
+subdirectory.
+
+<p>
+Each of the program and library subdirectories contain their own src,
+bin, and include directories.  When the programs are compiled, the C
+files in src are compiled to .o files with names which include the
+$ARCH value: src/foo.c -> src/foo.linux.o.  The output executable is
+written to the bin directory with a name which also includes the
+binary: bin/foo.linux.  These are then installed in the appropriate
+ohana/bin/ARCH directories with 'make install'.  In the case of the
+program directories which include multiple C programs, the make file
+takes commands of the form 'make program', 'make program.install',
+'make program.clean', etc, which perform the requested operation on
+the specific program.  The generic commands 'make' and 'make install'
+will operate on the complete collection of programs.
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/install.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/install.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/install.txt	(revision 22322)
@@ -0,0 +1,74 @@
+
+The Elixir system consists of two main software packages: Ohana (main
+developer Eugene Magnier) and Flips (main developer Jean-Charles
+Cuillandre).  The Elixir system combines these two packages, along
+with other external software products, into a complete system
+providing many utilities and functions for image processing at CFHT
+and elsewhere.  This document describes how to install the Ohana
+portion of the Elixir system.  Ohana may be installed from CVS or from
+a distributed tar ball.  Within the Elixir development community, the
+Ohana source tree may be checked out of the Elixir CVS tree.  Ohana is
+also distributed as a tar ball to users outside of the Elixir
+development community.
+
+1) Installing Ohana from source tar ball:
+
+  un-gzip and untar:  
+
+    > gunzip -c ohana.tgz | tar xvf -
+
+  enter the ohana top-level directory:
+
+    > cd ohana
+
+  construct the input Configure file [1]:
+
+    > configure
+
+  this step will note the binary installation path.  This should be
+  added to your PATH
+
+  compile the software
+
+    > make
+
+  install the software
+
+    > make install
+
+2) Installing Ohana from CVS (Developer's version):
+
+  check out the ohana source tree:
+
+    > setenv CVSROOT poma:/data/elixir2/srcdir/cvs
+    > cvs co ohana
+
+  enter the ohana top-level directory:
+
+    > cd ohana
+
+  construct the input Configure file [1]:
+
+    > configure
+
+  this step will note the binary installation path.  This should be
+  added to your PATH
+
+  compile the software
+
+    > make
+
+  install the software
+
+    > make install
+
+***** Notes *****
+
+[1] Ohana is configured for multiple architecture development, which
+is convenient for end users as well.  The libraries, include files,
+and programs, are all installed in directories with the architecture
+type appended: bin/linux, bin/sol, lib/linux, etc.  To take advantage
+of this multiple architecture organization when compiling, end users
+should arrange for the environment variable ARCH to be set based on
+the machine architecture, and for the PATH to depend on ARCH, rather
+than setting it to a fixed quantity.  
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/sequence.idx
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/sequence.idx	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/sequence.idx	(revision 22322)
@@ -0,0 +1,10 @@
+Elixir-System
+Elixir-Tools
+Elixir-DB
+Opihi-Programs
+IPP-subsystems
+DVO
+developer
+download.htm
+bugzilla.htm
+install.txt
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/template.css
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/template.css	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/template.css	(revision 22322)
@@ -0,0 +1,75 @@
+
+/* heading styles */
+H1   		{ color: #000000; font-size: xx-large; }
+H2		{ color: #000000; font-size: x-large;  }
+H3		{ color: #000000; font-size: large;    }
+H4		{ color: #000000; font-size: normal;   }
+H5		{ color: #000000; font-size: small;    }
+
+span.title      { font-size: 200% }
+span.line       { color: #ff0000 }
+
+/* document-specific styles come next */
+
+a:link, a:visited, a:active { text-decoration: underline; font-weight: bold; color: #000080 }
+
+body  { bgcolor: #e0d8f0; background-color: #e0d8f0; padding: 0px; margin: 0px }
+table.outline { 
+	text-align: left; 
+	font-size: small; 
+	font-weight: normal; 
+	background: #5c0ca8; 
+	border: 2px solid #5c0ca8; 
+	padding: 0px; 
+}
+
+table.toparea { border-width: 0px}
+
+a.menutop  { text-decoration: none; color: #ffffff; font-weight: bold }
+a.menulink { text-decoration: none; font-weight: normal; color: #000000}
+a.menusele { text-decoration: none; font-weight: normal; color: #ff0000}
+
+td.menutop  { text-align: left; background: #000000; font-size: small; color: #ffffff; padding: 2px; }
+td.menulink { text-align: left; background: #ffffff; font-size: small; color: #000000; padding: 2px; font-weight: normal; text-indent: 8px; }
+td.menumiss { text-align: left; background: #e0e0e0; font-size: small; color: #000000; padding: 2px; text-indent: 8px; }
+td.menusel  { text-align: left; background: #00ffff; font-size: small; color: #ff0000; padding: 2px; font-weight: normal; text-indent: 8px; }
+
+a.ln { text-decoration: none; font-weight: normal; color: #000000}
+a.dl { text-decoration: none; font-weight: normal; color: #000000}
+a.sl { text-decoration: none; font-weight: normal; color: #ff0000}
+
+td.dir0 { background: #c0b0b0; color: #000000; padding: 2px; text-indent: 0px;  }
+td.dir1 { background: #d0c0c0; color: #000000; padding: 2px; text-indent: 8px;  }
+td.dir2 { background: #e0d0d0; color: #000000; padding: 2px; text-indent: 16px; }
+td.dir3 { background: #f0e0e0; color: #000000; padding: 2px; text-indent: 24px; }
+
+td.dsl0 { background: #c0b0b0; color: #ff0000; padding: 2px; text-indent: 0px;  }
+td.dsl1 { background: #d0c0c0; color: #ff0000; padding: 2px; text-indent: 8px;  }
+td.dsl2 { background: #e0d0d0; color: #ff0000; padding: 2px; text-indent: 16px; }
+td.dsl3 { background: #f0e0e0; color: #ff0000; padding: 2px; text-indent: 24px; }
+
+td.doc0 { background: #a090f0; color: #000000; padding: 2px; text-indent: 0px;  }
+td.doc1 { background: #b0a0f0; color: #000000; padding: 2px; text-indent: 8px;  }
+td.doc2 { background: #c0b0f0; color: #000000; padding: 2px; text-indent: 16px; }
+td.doc3 { background: #d0c0f0; color: #000000; padding: 2px; text-indent: 24px; }
+
+td.sel0 { background: #a090f0; color: #ff0000; padding: 2px; text-indent: 0px;  }
+td.sel1 { background: #b0a0f0; color: #ff0000; padding: 2px; text-indent: 8px;  }
+td.sel2 { background: #c0b0f0; color: #ff0000; padding: 2px; text-indent: 16px; }
+td.sel3 { background: #d0c0f0; color: #ff0000; padding: 2px; text-indent: 24px; }
+
+td.title { background-color: #ffffff; padding: 5px; font-size: x-large; text-align: left; vertical-align: top}
+td.body  { background-color: #ffffff; padding: 5px; font-size: normal;  text-align: left; vertical-align: top }
+
+a.submenu { text-decoration: underline; font-weight: bold; color: #ffffff}
+a.sublist { text-decoration: none; font-weight: bold; color: #0000ff}
+a.subplot { text-decoration: underline; font-weight: bold; color: #000000}
+
+td.subtitle  { background: #a02020; font-size: large; font-weight: bold; color: #ffffff; padding: 2px }
+td.submenu   { background: #000000; text-align: center; font-size: small; font-weight: bold; color: #ffffff; padding: 2px }
+td.subsel    { background: #b0b0b0; text-align: center; font-size: small; font-weight: bold; color: #000000; padding: 2px }
+
+table.pretty    { background: #ffffff; border: 1px solid #000000; padding: 0px; cellspacing: 0px; }
+table.pretty td { border: 1px solid #000000; padding: 3px; text-align: left}
+table.pretty th { border: 1px solid #000000; padding: 3px; text-align: left}
+table.pretty caption { font-weight: bold; }
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/template.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/template.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/html/template.htm	(revision 22322)
@@ -0,0 +1,42 @@
+<html>
+<head>
+<title>
+<meta name=page>
+</title>
+</head>
+
+<link rel="STYLESHEET" HREF="ROOT/template.css">
+<!-- link rel="STYLESHEET" HREF="template.css" -->
+
+<body>
+
+<table class=toparea cellspacing=10><tr><td valign=top>
+<!-- Index -->
+<table width=188px class=outline cellspacing=0>
+<tr><td><a href=ROOT>
+<IMG border=0 width=188px SRC="ROOT/icons/logo-pw.png"></a></td></tr>
+<tr><td class=menutop>  <a class=menutop href=ROOT> Ohana Home </a></td></tr>
+<tr><td class=menutop>  <a class=menutop href=ROOT/..> Elixir Home </a></td></tr>
+<tr><td class=menutop>  <a class=menutop href=/> CFHT Home </a></td></tr>
+
+<meta name=index file=index.idx>
+
+</table> <br>
+
+</td><td width=100% valign=top> 
+
+<!-- Main Content -->
+<table width=100% class=outline>
+<tr><td class=title>
+<meta name=title>
+</td></tr>
+
+<!-- Body of Content -->
+<tr><td class=body>
+<meta name=body>
+</td></tr>
+</table>
+
+</td></tr></table>
+</body>
+</html>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/ohana.css
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/ohana.css	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/ohana.css	(revision 22322)
@@ -0,0 +1,44 @@
+
+/* heading styles */
+H1   		{ color: #000000; font-size: xx-large; }
+H2		{ color: #000000; font-size: x-large;  }
+H3		{ color: #000000; font-size: large;    }
+H4		{ color: #000000; font-size: normal;   }
+H5		{ color: #000000; font-size: small;    }
+
+span.title      { font-size: 200% }
+span.line       { color: #ff0000 }
+
+/* document-specific styles come next */
+
+a:link, a:visited, a:active { text-decoration: underline; font-weight: bold; color: #000080 }
+
+body  { bgcolor: #20d0e0; background-color: #20d0e0; padding: 0px; margin: 0px }
+table.outline { background: #ffffff; border: 5px solid #0850c0; padding: 0px; }
+
+table.toparea { border-width: 0px}
+
+a.menutop  { text-decoration: none; color: #ffffff; font-weight: bold }
+a.menulink { text-decoration: none; font-weight: normal; color: #000000}
+
+td.menutop  { text-align: left; background: #000000; font-size: small; color: #ffffff; padding: 2px; }
+td.menuhead { text-align: left; background: #b0b0b0; font-size: small; color: #000080; padding: 2px; font-weight: bold; }
+td.menulink { text-align: left; background: #ffffff; font-size: small; color: #000000; padding: 2px; font-weight: normal; text-indent: 8px; }
+td.menumiss { text-align: left; background: #e0e0e0; font-size: small; color: #000000; padding: 2px; text-indent: 8px; }
+td.menusel  { text-align: left; background: #ffffff; font-size: small; color: #ff0000; padding: 2px; font-weight: normal; text-indent: 8px; }
+
+td.title { background-color: #ffffff; padding: 5px; font-size: x-large; text-align: left; vertical-align: top}
+td.body  { background-color: #ffffff; padding: 5px; font-size: normal;  text-align: left; vertical-align: top }
+
+a.submenu { text-decoration: underline; font-weight: bold; color: #ffffff}
+a.sublist { text-decoration: none; font-weight: bold; color: #0000ff}
+a.subplot { text-decoration: underline; font-weight: bold; color: #000000}
+
+td.subtitle  { background: #0754ba; font-size: large; font-weight: bold; color: #ffffff; padding: 2px }
+td.submenu   { background: #000000; text-align: center; font-size: small; font-weight: bold; color: #ffffff; padding: 2px }
+td.subsel    { background: #b0b0b0; text-align: center; font-size: small; font-weight: bold; color: #000000; padding: 2px }
+
+table.pretty    { background: #ffffff; border: 1px solid #0000ff; padding: 0px; cellspacing: 0px; }
+table.pretty td { border: 1px solid #0000ff; padding: 3px; text-align: left}
+table.pretty th { border: 1px solid #0000ff; padding: 3px; text-align: left}
+table.pretty caption { font-weight: bold; }
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/ohana.src
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/ohana.src	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/ohana.src	(revision 22322)
@@ -0,0 +1,72 @@
+<html>
+<head> 
+<!-- Page Title -->
+</head>
+<link rel="STYLESHEET" HREF="$HREF/ohana.css">
+<body>
+
+<table class=toparea cellspacing=10><tr><td valign=top>
+
+<!-- Elixir Selection Menu Begin -->
+<table width=188px class=outline cellspacing=0>
+<tr><td><a href=$HREF/home.html><IMG border=0 width=188px SRC="$HREF/icons/logo.png"></a></td></tr>
+<tr><td class=menutop>  <a class=menutop href=$HREF/home.html> Ohana Home                                         </a></td></tr>
+<tr><td class=menutop>  <a class=menutop href=$HREF/..> Elixir Home                                               </a></td></tr>
+<tr><td class=menutop>  <a class=menutop href=/> CFHT Home                                                        </a></td></tr>
+
+<tr><td class=menuhead> User's Guides                                                                                 </td></tr>
+<tr><td class=menuhead> Top-Level Systems                                                                             </td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/opihi.html>                        Opihi shell               </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/dvo.html>                          DVO examples              </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/mana.html>                         Mana command              </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/dvo-cmds.html>                     DVO commands              </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/dimm.html>                         DIMM commands             </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/mkdetrend.html>                    mkdetrend                 </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/mkfringe.html>                     mkfringe                  </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/gcontrol.html>                     gcontrol (elixir)         </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/nightd.html>                       nightd                    </a></td></tr>
+
+<tr><td class=menuhead> Components                                                                                    </td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/imreg.html>                        imreg                     </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/detrend.html>                      detrend                   </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/imclean.html>                      imclean                   </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/gastro.html>                       gastro                    </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/gcompare.html>                     gcompare                  </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/photcode.html>                     photcode                  </a></td></tr>
+
+<tr><td class=menuhead> Perl Scripts                                                                                  </td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/mkdetrend.html>                    mkdetrend components      </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/mkfringe.html>                     mkfringe components       </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/postrun.html>                      elixir.postrun            </a></td></tr>
+
+<tr><td class=menuhead> Programmer's Reference                                                                        </td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/install.html>                      installing                </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/download.html>                     download                  </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/kapa.html>                         kapa                      </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/kii.html>                          kii                       </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/includes.html>                     include                   </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/libfits.html>                      libfits                   </a></td></tr>
+<tr><td class=menulink> <a class=menulink href=$HREF/libohana.html>                     libohana                  </a></td></tr>
+
+<tr><td></td></tr>
+</table>
+<!-- Elixir Selection Menu End -->
+
+</td><td width=100% valign=top> 
+
+<!-- Main Content -->
+<table width=100% class=outline>
+<tr><td class=title>
+<!-- Title --> 
+</td></tr>
+
+<tr><td class=body>
+<!-- Body Begin -->
+
+<!-- Body End -->
+</td></tr>
+</table>
+
+</td></tr></table>
+</body>
+</html>
Index: /tags/sj_tags/sj_root_20080929/Ohana/doc/www/sample.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/doc/www/sample.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/doc/www/sample.htm	(revision 22322)
@@ -0,0 +1,18 @@
+<meta name=file  content=home>
+<meta name=title content=The Elixir System>
+<meta name=page  content=Elixir>
+
+<p>
+The Elixir Project at CFHT has the goal of enabling the optimal use of
+data by our observers.  The current target of the Elixir project is
+data from large mosaic cameras.  We call the project `Elixir' after
+the fabled goal of the ancient alchemists: the Elixir which could
+restore youth or turn lead into gold.  The Elixir project seeks to
+restore youth to archived data and turn weighty mass of unmined data
+collected at CFHT into nuggets of gold.
+
+<p>
+Elixir is not a single program or package; rather it is a collection
+of programs, databases, and other tools related to the processing and
+evaluation of the telescope data.  This document provides a starting
+point for a variety of documentation related to the Elixir project.
Index: /tags/sj_tags/sj_root_20080929/Ohana/ohana-config.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/ohana-config.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/ohana-config.in	(revision 22322)
@@ -0,0 +1,25 @@
+#!/bin/csh -f
+
+if ($#argv != 1) then
+  echo "USAGE: ohana-config [option]"
+  echo "valid options:"
+  echo " --libs"
+  echo " --cflags"
+  echo " --prefix"
+  exit 2
+endif
+
+switch ($1) 
+  case "--libs":
+    echo "-L@LIBDIR@ -lkapa -ldvo -lFITS -lohana"
+    shift
+    breaksw
+  case "--cflags":
+    echo "-I@INCDIR@ @DEFINES@"
+    shift
+    breaksw
+  default:
+    echo "unknown option $1"
+    exit 2
+endsw
+ 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/absphot/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/absphot/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/absphot/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+bin
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/absphot/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/absphot/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/absphot/doc/notes.txt	(revision 22322)
@@ -0,0 +1,17 @@
+
+absphot
+
+  we have relphot to determine the relative photometry on small spatial scales
+  we have uniphot to determine the zero point trend with time.
+  the result of these two should be a single, consistent photometry system in the internal filter system
+
+  the goal of absphot is to determine the relationships between the
+  internal magnitude system and an external reference
+
+usage:
+
+  absphot -target (photcode) -ref (photcode) [-region RA RA DEC DEC]
+
+  - determine the fit M_ref = M_target + \Sum_i A_i * color^i
+
+    
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/absphot/src/absphot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/absphot/src/absphot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/absphot/src/absphot.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include "relphot.h"
+
+int main (int argc, char **argv) {
+
+  // select the list of database region files, based on region
+  // loop over the catalogs, load the magnitudes in the selected bands
+  // determine the fit
+
+  int i, status, Ncatalog;
+  Catalog *catalog;
+  FITS_DB db;
+
+  SkyList *skylist = NULL;
+
+  /* get configuration info, args */
+  initialize (argc, argv);
+
+  /* register database handle with shutdown procedure */
+  set_db (&db);
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+
+  /* load regions based on specified sky patch */
+  skylist = load_regions (&db, &UserPatch, UserPatchSelect);
+
+  /* load catalog data from region files */
+  catalog = load_catalogs (skylist, &Ncatalog);
+  
+  fitTrends (catalog);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+bin
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/Makefile	(revision 22322)
@@ -0,0 +1,260 @@
+default: addstar
+help:
+	@echo "make options: addstar (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/addstar
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -Wall -Werror
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+addstar     : $(BIN)/addstar.$(ARCH)
+addstard    : $(BIN)/addstard.$(ARCH)
+addstart    : $(BIN)/addstart.$(ARCH)
+addstarc    : $(BIN)/addstarc.$(ARCH)
+mkacc-2mass : $(BIN)/mkacc-2mass.$(ARCH)
+sedstar     : $(BIN)/sedstar.$(ARCH)
+load2mass   : $(BIN)/load2mass.$(ARCH)
+gztest      : $(BIN)/gztest.$(ARCH)
+
+all: addstar addstarc addstard addstart sedstar load2mass skycells
+
+ADDSTAR = \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SkyListForStars.$(ARCH).o \
+$(SRC)/SkyRegionUtils.$(ARCH).o \
+$(SRC)/SkyTableFromTychoIndex.$(ARCH).o \
+$(SRC)/addstar.$(ARCH).o \
+$(SRC)/airmass.$(ARCH).o \
+$(SRC)/args.$(ARCH).o \
+$(SRC)/build_links.$(ARCH).o \
+$(SRC)/calibrate.$(ARCH).o \
+$(SRC)/dump.$(ARCH).o \
+$(SRC)/edge_check.$(ARCH).o \
+$(SRC)/fakeimage.$(ARCH).o \
+$(SRC)/find_matches.$(ARCH).o \
+$(SRC)/find_matches_closest.$(ARCH).o \
+$(SRC)/find_matches_refstars.$(ARCH).o \
+$(SRC)/find_subset.$(ARCH).o \
+$(SRC)/get2mass.$(ARCH).o \
+$(SRC)/get2mass_as.$(ARCH).o \
+$(SRC)/get2mass_as_raw.$(ARCH).o \
+$(SRC)/get2mass_dr2.$(ARCH).o \
+$(SRC)/get2mass_ops.$(ARCH).o \
+$(SRC)/getgsc.$(ARCH).o \
+$(SRC)/gettycho.$(ARCH).o \
+$(SRC)/getusno.$(ARCH).o \
+$(SRC)/getusnob.$(ARCH).o \
+$(SRC)/greference.$(ARCH).o \
+$(SRC)/grefstars.$(ARCH).o \
+$(SRC)/LoadStars.$(ARCH).o \
+$(SRC)/LoadHeaders.$(ARCH).o \
+$(SRC)/MatchHeaders.$(ARCH).o \
+$(SRC)/LoadData.$(ARCH).o \
+$(SRC)/LoadDataSDSS.$(ARCH).o \
+$(SRC)/LoadDataPMM.$(ARCH).o \
+$(SRC)/in_image.$(ARCH).o \
+$(SRC)/load_subpix.$(ARCH).o \
+$(SRC)/opening_angle.$(ARCH).o \
+$(SRC)/parse_time.$(ARCH).o \
+$(SRC)/replace_match.$(ARCH).o \
+$(SRC)/resort_catalog.$(ARCH).o \
+$(SRC)/ReadStarsFITS.$(ARCH).o \
+$(SRC)/ReadStarsTEXT.$(ARCH).o \
+$(SRC)/ReadStarsSDSS.$(ARCH).o \
+$(SRC)/FilterStars.$(ARCH).o \
+$(SRC)/ImageOptions.$(ARCH).o \
+$(SRC)/GetFileMode.$(ARCH).o \
+$(SRC)/ReadImageHeader.$(ARCH).o \
+$(SRC)/UpdateImageIDs.$(ARCH).o \
+$(SRC)/update_coords.$(ARCH).o
+
+ADDSTARD = \
+$(SRC)/CheckPassword.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/NewImage.$(ARCH).o \
+$(SRC)/NewRefcat.$(ARCH).o \
+$(SRC)/NewReflist.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SkyListForStars.$(ARCH).o \
+$(SRC)/SkyRegionUtils.$(ARCH).o \
+$(SRC)/SkyTableFromTychoIndex.$(ARCH).o \
+$(SRC)/SocketOps.$(ARCH).o \
+$(SRC)/UpdateDatabase_Image.$(ARCH).o \
+$(SRC)/UpdateDatabase_Refcat.$(ARCH).o \
+$(SRC)/UpdateDatabase_Reflist.$(ARCH).o \
+$(SRC)/addstard.$(ARCH).o \
+$(SRC)/airmass.$(ARCH).o \
+$(SRC)/args_server.$(ARCH).o \
+$(SRC)/build_links.$(ARCH).o \
+$(SRC)/calibrate.$(ARCH).o \
+$(SRC)/edge_check.$(ARCH).o \
+$(SRC)/find_matches.$(ARCH).o \
+$(SRC)/find_matches_closest.$(ARCH).o \
+$(SRC)/find_matches_refstars.$(ARCH).o \
+$(SRC)/find_subset.$(ARCH).o \
+$(SRC)/get2mass.$(ARCH).o \
+$(SRC)/get2mass_as.$(ARCH).o \
+$(SRC)/get2mass_as_raw.$(ARCH).o \
+$(SRC)/get2mass_dr2.$(ARCH).o \
+$(SRC)/get2mass_ops.$(ARCH).o \
+$(SRC)/getgsc.$(ARCH).o \
+$(SRC)/gettycho.$(ARCH).o \
+$(SRC)/getusno.$(ARCH).o \
+$(SRC)/getusnob.$(ARCH).o \
+$(SRC)/ImageOptions.$(ARCH).o \
+$(SRC)/greference.$(ARCH).o \
+$(SRC)/in_image.$(ARCH).o \
+$(SRC)/load_subpix.$(ARCH).o \
+$(SRC)/opening_angle.$(ARCH).o \
+$(SRC)/replace_match.$(ARCH).o \
+$(SRC)/update_coords.$(ARCH).o
+
+ADDSTART = \
+$(SRC)/addstart.$(ARCH).o \
+$(SRC)/ImageOptions.$(ARCH).o \
+$(SRC)/DatasetOps.$(ARCH).o \
+$(SRC)/SocketOps.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/args_server.$(ARCH).o \
+$(SRC)/CheckPassword.$(ARCH).o \
+$(SRC)/UpdateDatabase_Image.$(ARCH).o \
+$(SRC)/UpdateDatabase_Reflist.$(ARCH).o \
+$(SRC)/UpdateDatabase_Refcat.$(ARCH).o \
+$(SRC)/airmass.$(ARCH).o \
+$(SRC)/build_links.$(ARCH).o \
+$(SRC)/calibrate.$(ARCH).o \
+$(SRC)/find_matches.$(ARCH).o \
+$(SRC)/find_matches_closest.$(ARCH).o \
+$(SRC)/find_matches_refstars.$(ARCH).o \
+$(SRC)/find_subset.$(ARCH).o \
+$(SRC)/get2mass.$(ARCH).o \
+$(SRC)/get2mass_as.$(ARCH).o \
+$(SRC)/get2mass_as_raw.$(ARCH).o \
+$(SRC)/get2mass_dr2.$(ARCH).o \
+$(SRC)/get2mass_ops.$(ARCH).o \
+$(SRC)/getgsc.$(ARCH).o \
+$(SRC)/getusno.$(ARCH).o \
+$(SRC)/getusnob.$(ARCH).o \
+$(SRC)/gettycho.$(ARCH).o \
+$(SRC)/greference.$(ARCH).o \
+$(SRC)/in_image.$(ARCH).o \
+$(SRC)/load_subpix.$(ARCH).o \
+$(SRC)/replace_match.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/SkyRegionUtils.$(ARCH).o \
+$(SRC)/SkyListForStars.$(ARCH).o \
+$(SRC)/update_coords.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/edge_check.$(ARCH).o \
+$(SRC)/opening_angle.$(ARCH).o \
+$(SRC)/SkyTableFromTychoIndex.$(ARCH).o \
+$(SRC)/ListenClients_Thread.$(ARCH).o \
+$(SRC)/NewImage_Thread.$(ARCH).o \
+$(SRC)/NewReflist_Thread.$(ARCH).o \
+$(SRC)/NewRefcat_Thread.$(ARCH).o 
+
+ADDSTARC = \
+$(SRC)/addstarc.$(ARCH).o \
+$(SRC)/airmass.$(ARCH).o \
+$(SRC)/SocketOps.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/LoadStars.$(ARCH).o \
+$(SRC)/LoadHeaders.$(ARCH).o \
+$(SRC)/MatchHeaders.$(ARCH).o \
+$(SRC)/LoadData.$(ARCH).o \
+$(SRC)/LoadDataSDSS.$(ARCH).o \
+$(SRC)/LoadDataPMM.$(ARCH).o \
+$(SRC)/grefstars.$(ARCH).o \
+$(SRC)/load_subpix.$(ARCH).o \
+$(SRC)/parse_time.$(ARCH).o \
+$(SRC)/ReadStarsFITS.$(ARCH).o \
+$(SRC)/GetFileMode.$(ARCH).o \
+$(SRC)/ReadImageHeader.$(ARCH).o \
+$(SRC)/ReadStarsTEXT.$(ARCH).o \
+$(SRC)/ReadStarsSDSS.$(ARCH).o \
+$(SRC)/UpdateImageIDs.$(ARCH).o \
+$(SRC)/FilterStars.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/args_client.$(ARCH).o
+
+### this program may need to be repaired (EAM 2006.08)
+MKACC-2MASS = \
+$(SRC)/mkacc-2mass.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/get2mass_as.$(ARCH).o
+
+LOAD-2MASS = \
+$(SRC)/load2mass.$(ARCH).o \
+$(SRC)/load2mass_as_rawdata.$(ARCH).o \
+$(SRC)/load2mass_catalog.$(ARCH).o \
+$(SRC)/get2mass_ops.$(ARCH).o \
+$(SRC)/get2mass_full.$(ARCH).o \
+$(SRC)/find_matches_refstars.$(ARCH).o \
+$(SRC)/args_load2mass.$(ARCH).o \
+$(SRC)/replace_match.$(ARCH).o \
+$(SRC)/update_coords.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o
+
+SEDSTAR = \
+$(SRC)/sedstar.$(ARCH).o \
+$(SRC)/SEDtableLoad.$(ARCH).o \
+$(SRC)/SEDfit.$(ARCH).o \
+$(SRC)/SEDops.$(ARCH).o \
+$(SRC)/args_sedstar.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o
+
+SKYCELLS = \
+$(SRC)/skycells.$(ARCH).o \
+$(SRC)/args_skycells.$(ARCH).o \
+$(SRC)/ConfigInit_skycells.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/sky_tessalation.$(ARCH).o
+
+$(ADDSTARC) : $(INC)/addstar.h
+$(ADDSTARD) : $(INC)/addstar.h
+$(ADDSTART) : $(INC)/addstar.h
+$(ADDSTAR)  : $(INC)/addstar.h
+$(SKYCELLS) : $(INC)/addstar.h
+$(LOAD-2MASS) : $(INC)/addstar.h $(INC)/2mass.h
+
+$(BIN)/addstar.$(ARCH) : $(ADDSTAR)
+$(BIN)/addstard.$(ARCH) : $(ADDSTARD)
+$(BIN)/addstart.$(ARCH) : $(ADDSTART)
+$(BIN)/addstarc.$(ARCH) : $(ADDSTARC)
+$(BIN)/mkacc-2mass.$(ARCH) : $(MKACC-2MASS)
+$(BIN)/sedstar.$(ARCH)   : $(SEDSTAR)
+$(BIN)/load2mass.$(ARCH) : $(LOAD-2MASS)
+$(BIN)/skycells.$(ARCH) : $(SKYCELLS)
+
+INSTALL = addstar addstarc addstard addstart sedstar load2mass skycells
+
+# dependancy rules for binary code #########################
+$(INSTALL): % : $(BIN)/%.$(ARCH)
+
+%.clean :
+	rm -f $(BIN)/$*.$(ARCH)
+
+%.install:
+	make $(DESTBIN)/$*
+
+install:
+	for i in $(INSTALL); do make $$i.install || exit; done
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,5 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/2mass.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/2mass.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/2mass.txt	(revision 22322)
@@ -0,0 +1,182 @@
+
+2MASS PSC fields:
+
+ra, dec : obvious
+
+m       -> mag
+cmsig   -> dM
+msigcom -> dMcal (dMcal^2 + dM^2 = msigcom^2)
+stdap   -> Map 
+snr     : skipped
+
+err_maj -> fhwm_x
+err_min -> fhwm_y
+err_ang -> angle
+
+the following 2MASS values are used to set bits in the phot_flags field:
+
+ph_qual:
+
+X = 0x0000  There is a detection at this location, but no valid brightness estimate can be extracted using any algorithm. rd_flg="9" and default magnitude is null.
+U = 0x0001  Upper limit on magnitude. Source is not detected in this band (rd_flg="0"), or it is detected, but not resolved in a consistent fashion with other bands (rd_flg="6"). A value of ph_qual="U" does not necessarily mean that there is no flux detected in this band at the location. Whether or not flux has been detected can be determined from the value of rd_flg. When rd_flg="0", no flux has been detected. When rd_flg="6", flux has been detected at the location where the images were not deblended consistently in all three bands (JHKs).
+F = 0x0002  This category includes rd_flg="1" or rd_flg="3" sources where a reliable estimate of the photometric error, [jhk]_cmsig, could not be determined. The uncertainties reported for these sources in [jhk]_cmsig and [jhk]_msigcom are flags and have numeric values >8.0.
+E = 0x0003  This category includes detections where the goodness-of-fit quality of the profile-fit photometry was very poor (rd_flg=2 and [jhk]psf_chi>10.0), or detections where psf fit photometry did not converge and an aperture magnitude is reported (rd_flg=4), or detections where the number of frames was too small in relation to the number of frames in which a detection was geometrically possible (rd_flg="1" or rd_flg="2").
+A = 0x0004  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>10 AND [jhk]_cmsig<0.10857.
+B = 0x0005  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>7 AND [jhk]_cmsig<0.15510.
+C = 0x0006  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>5 AND [jhk]_cmsig<0.21714.
+D = 0x0007  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with no [jhk]_snr or [jhk]_cmsig requirement.
+
+rd_flag:
+
+0 = 0x0000  Source is not detected in this band. The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20".
+1 = 0x0010  The default magnitude is derived from aperture photometry measurements on the 51 ms "Read_1" exposures. The aperture radius is 4", with the sky background measured in an annulus with an inner radius of 14" and an outer radius of 20". Used for sources that saturate one or more of the 1.3s "Read_2" exposures, but are not saturated on at least one of the 51 ms "Read_1" frames.
+2 = 0x0020  The default magnitude is derived from a profile-fitting measurement made on the 1.3 sec "Read_2" exposures. The profile-fit magnitudes are normalized to curve-of-growth-corrected aperture magnitudes. This is the most common type in the PSC, and is used for sources that have no saturated pixels in any of the 1.3 sec exposures.
+3 = 0x0030  The default magnitude is derived from a 1-d radial profile fitting measurement made on the 51 ms "Read_1" exposures. Used for very bright sources that saturate all of the 51 ms "Read 1" exposures.
+4 = 0x0040  The default magnitude is derived from curve-of-growth-corrected 4" radius aperture photometry measurements on the 1.3 s "Read_2" exposures. This is used for sources that are not saturated in any of the Read_2 frames, but where the profile-fitting measurements fail to converge to a solution. These magnitudes are the same as the standard aperture magnitudes (j_m_stdap, h_m_stdap, k_m_stdap), but when they are the default magnitudes, it generally implies that they are low quality measurements.
+6 = 0x0050  The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20". This is used for pairs of sources which are detected and resolved in another band, but are detected and not resolved in this band. This differs from a rd_flg="0" because in this case there is a detection of the source in this band, but it is not consistently resolved across all bands.
+9 = 0x0060  The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20". This is used for sources that were nominally detected in this band, but which could not have a useful brightness measurement from either profile fitting or aperture photometry. This often occurs in highly confused regions, or very near Tile edges where a significant fraction of the measurement aperture of sky annulus falls off the focal plane.
+
+cc_flag:
+
+p = 0x0000  Persistence. Source may be contaminated by a latent image left by a nearby bright star.
+c = 0x0100  Photometric Confusion. Source photometry is biased by a nearby star that has contaminated the background estimation. This is very common in high source density regions.
+d = 0x0200  Diffraction spike confusion. Source may be contaminated by a diffraction spike from a nearby star.
+s = 0x0300  Electronic stripe. Source measurement may be contaminated by a stripe from a nearby bright star.
+b = 0x0400  Bandmerge confusion. In the process of merging detections in the different bands for this source, there was more than one possible match between the different band components. This occurs in regions of very high source density, or when multiple sources were split in one band but not another.
+0 = 0x0500  Source is unaffected by known artifacts, or is not detected in the band.
+
+bl_flag: (0,1 : not blended)
+
+anything except 0 or 1 -> 0x0008
+
+gal_flag: (0,1 : not extended)
+
+anything except 0 or 1 -> 0x0080
+
+mp_flag: 
+
+anything except 0 or 1 -> 0x0800
+
+dup_flag:
+
+anything except 0 or 1 -> 0x1000
+
+use_flag:
+
+anything except 0 or 1 -> 0x2000
+
+
+2MASS PSC fields:
+
+ra, dec : obvious
+
+err_maj, err_min, err_ang : I only have ra_err and dec_err.  should I upgrade my tables?
+(put these in FHx, FWy, theta)
+
+designation (2mass ID) : ignore this?  potentially derivable from position
+
+j_m -> M
+j_cmsig -> dM
+j_msigcom : I don't yet have a systematic error term; add this in
+j_snr : another way of representing the error : the implied error is the MODE of dM for the scan
+
+(repeat for h, k)
+
+ph_qual[j] : using letters to represent 8 states: (3bit needed)
+
+0x0000 * "X" - There is a detection at this location, but no valid brightness estimate can be extracted using any algorithm. rd_flg="9" and default magnitude is null.
+0x0001 * "U" - Upper limit on magnitude. Source is not detected in this band (rd_flg="0"), or it is detected, but not resolved in a consistent fashion with other bands (rd_flg="6"). A value of ph_qual="U" does not necessarily mean that there is no flux detected in this band at the location. Whether or not flux has been detected can be determined from the value of rd_flg. When rd_flg="0", no flux has been detected. When rd_flg="6", flux has been detected at the location where the images were not deblended consistently in all three bands (JHKs).
+0x0002 * "F" - This category includes rd_flg="1" or rd_flg="3" sources where a reliable estimate of the photometric error, [jhk]_cmsig, could not be determined. The uncertainties reported for these sources in [jhk]_cmsig and [jhk]_msigcom are flags and have numeric values >8.0.
+0x0003 * "E" - This category includes detections where the goodness-of-fit quality of the profile-fit photometry was very poor (rd_flg=2 and [jhk]psf_chi>10.0), or detections where psf fit photometry did not converge and an aperture magnitude is reported (rd_flg=4), or detections where the number of frames was too small in relation to the number of frames in which a detection was geometrically possible (rd_flg="1" or rd_flg="2").
+0x0004 * "A" - Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>10 AND [jhk]_cmsig<0.10857.
+0x0005 * "B" - Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>7 AND [jhk]_cmsig<0.15510.
+0x0006 * "C" - Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>5 AND [jhk]_cmsig<0.21714.
+0x0007 * "D" - Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with no [jhk]_snr or [jhk]_cmsig requirement.
+
+rd_flag[j] : using numbers to represent 7 states:
+
+0x0000 * "0" - Source is not detected in this band. The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20".
+0x0010 * "1" - The default magnitude is derived from aperture photometry measurements on the 51 ms "Read_1" exposures. The aperture radius is 4", with the sky background measured in an annulus with an inner radius of 14" and an outer radius of 20". Used for sources that saturate one or more of the 1.3s "Read_2" exposures, but are not saturated on at least one of the 51 ms "Read_1" frames.
+0x0020 * "2" - The default magnitude is derived from a profile-fitting measurement made on the 1.3 sec "Read_2" exposures. The profile-fit magnitudes are normalized to curve-of-growth-corrected aperture magnitudes. This is the most common type in the PSC, and is used for sources that have no saturated pixels in any of the 1.3 sec exposures.
+0x0030 * "3" - The default magnitude is derived from a 1-d radial profile fitting measurement made on the 51 ms "Read_1" exposures. Used for very bright sources that saturate all of the 51 ms "Read 1" exposures.
+0x0040 * "4" - The default magnitude is derived from curve-of-growth-corrected 4" radius aperture photometry measurements on the 1.3 s "Read_2" exposures. This is used for sources that are not saturated in any of the Read_2 frames, but where the profile-fitting measurements fail to converge to a solution. These magnitudes are the same as the standard aperture magnitudes (j_m_stdap, h_m_stdap, k_m_stdap), but when they are the default magnitudes, it generally implies that they are low quality measurements.
+0x0050 * "6" - The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20". This is used for pairs of sources which are detected and resolved in another band, but are detected and not resolved in this band. This differs from a rd_flg="0" because in this case there is a detection of the source in this band, but it is not consistently resolved across all bands.
+0x0060 * "9" - The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20". This is used for sources that were nominally detected in this band, but which could not have a useful brightness measurement from either profile fitting or aperture photometry. This often occurs in highly confused regions, or very near Tile edges where a significant fraction of the measurement aperture of sky annulus falls off the focal plane.
+
+bl_flag[j] : 3 states:
+
+    * "0" - Source is not detected, or is inconsistently deblended in that band.
+    * "1" - One component was fit to the source in R_2 profile-fitting photometry (rd_flg="2"), or default magnitudes are from aperture photometry (rd_flg="1" or "4") or saturated star 1-d radial profile-fitting (rd_flg="3").
+    * ">1" - More than one component was fit simultaneously during R2 profile-fit photometry, where the value of the field is the number of components simultaneously fit. The maximum number of components is 7 in any band for the PSC, so this bl_flg is always a three character flag. Multi-component fitting occurs only for profile-fitting, and only when more than one detection is found within ~5". Single detections that are not well-fit by a single PSF are not split.
+
+cc_flag[j] : 6 states:
+
+    * "p" = Persistence. Source may be contaminated by a latent image left by a nearby bright star.
+    * "c" = Photometric Confusion. Source photometry is biased by a nearby star that has contaminated the background estimation. This is very common in high source density regions.
+    * "d" = Diffraction spike confusion. Source may be contaminated by a diffraction spike from a nearby star.
+    * "s" = Electronic stripe. Source measurement may be contaminated by a stripe from a nearby bright star.
+    * "b" = Bandmerge confusion. In the process of merging detections in the different bands for this source, there was more than one possible match between the different band components. This occurs in regions of very high source density, or when multiple sources were split in one band but not another.
+    * "0" = Source is unaffected by known artifacts, or is not detected in the band.
+
+ndet : number of frames detected, number of frames available (carry this somehow?)
+
+prox : distance to nearest neighbor (unneeded)
+pxpa : direction to nearest neighbor (unneeded)
+pxcntr : id of nearest neighbor
+
+gal_contam : is source close to or part of an extended source? (3 states)
+
+mp_flg : associated with know Solar System object
+
+pts_key/cntr : 2MASS internal object ID
+
+hemis : (which observatory -- replace with a photcode?)
+
+date -> ignore (not accurate)
+
+scan : scan in which it was detected (unneeded)
+
+glon, glat : derivable
+
+x_scan : pixel location in x direction -> Xccd
+
+jdate : time stamp with ~30 sec accuracy
+
+j_psfchi : fit chi sqare -> psfProb (needs to be changed in measure.d anyway)
+
+j_m_stdap : aperture magnitude -> Mgal (change to Map anyway)
+
+j_msig_stdap : aperture magnitude error (keep ??)
+
+dist_edge_ns, ew, flg : information about distance to scan edge (ns or ew)
+
+dup_src : duplicate scan 
+use_src : ??
+
+(ignore the fields for cross-correlation with other surveys)
+
+counting the flag bits (no attempt to account for any duplicate info; ignoring dist_edge info):
+
+ph_qual	   : 3 bits
+rd_flag	   : 3 bits
+bl_flag	   : 2 bits 
+cc_flag	   : 3 bits
+gal_contam : 2 bits
+mp_flag	   : 1 bit
+dup_src	   : 1 bit
+use_src	   : 1 bit
+
+total      : 17 bit (1 too many...)
+
+counting the flag bits (trying to minimize duplicate info; ignoring dist_edge info):
+
+ph_qual	   : 3 bits 0x0001 - 0x0007
+bl_flag	   : 1 bit  0x0008 (bl_flag = 0 or 1 -> 0; bl_flag > 1 -> 1)
+rd_flag	   : 3 bits 0x0010 - 0x0070
+gal_contam : 1 bit  0x0080 (gal_contam = 0 or 1 -> 0; gal_contam = 2 -> 1) (set extNsigma to a high value if gal_contam = 1) 
+cc_flag	   : 3 bits 0x0100 - 0x0700
+mp_flag	   : 1 bit  0x0800
+dup_src	   : 1 bit  0x1000 (0 or 1 -> 0; > 1 -> 1)
+use_src	   : 1 bit  0x2000 
+
+total      : 14 bits
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,77 @@
+
+- addstar 1.7 (2006.10.04)
+  * converted to gfits APIs (forces libfits 1.6)
+  * added the -resort option
+  * added timeout for large operations
+  * dropped old versions now not needed
+  * converted to the new DVO APIs (forces libdvo 1.3)
+  * added 'sedstar'
+  * added 'load2mass'
+  * more error checks on config
+  * converted to 'check_dir_access' 
+  * check that catalog stars fall in image (or skip catalog)
+  * fixed memory leak issues in find_matches
+  * changed match projection to ZEA
+  * fixed some 0,360 boundary errors
+
+- addstar 1.6
+  * added the 'fakeimage' mode
+
+- addstar 1.5
+  * fixed up skyregion errors
+  * added untested threaded server version
+  * added Tycho catalog
+  * added USNO-B catalog
+  * added proper motion and parallax
+  * fixed 2MASS to work with skyregions correctly
+  * dropped IMAGE_CATALOG from config
+  * changed to used dvo_image_lock,unlock
+  * cleaned up excess verbosity for timing tests
+  * added -photcode (supplement -p)
+  * added PMTEST format
+
+- addstar 1.4
+  * major work to support SkyRegions
+  * cleaned up signed/unsigned inconsistencies
+  * fixed 2MASS time to use JD 
+
+2005.10.20 : addstar-1.3
+
+	This release incorportates many substantial improvements
+	needed to handle the panstarrs data problems:
+	- internal data representation now high precision
+	- multiple data storage formats (LONEOS, ELIXIR, PANSTARRS)
+	- multiple data storage modes (RAW, MEF, SPLIT)
+	- alternative matching scheme (-closest)
+	- sorted output tables now optional
+	- incremental updates possible for inserts
+	- client / server set now defined
+
+2005.08.15 : addstar-1.2
+
+        This is a snapshot release before I begin serious work on the
+	code to handle alternate formats and so forth needed for the
+	panstarrs support.  Minor updates since v1.1, mostly to fix
+	2MASS issues and to stay in sync with the libs.  
+
+2005.07.06 : addstar-1.1
+
+	I have made a variety of fairly substantial changes since
+        v1.0.  I have added autocode for the basic DVO data types.  I
+        have merged the functionality of addspphot, addrefs, and
+        addusno into the single addstar function.  There are now
+        options to add an ascii text table or stars from known
+        external reference catalogs (HST GSC, USNO, 2MASS).  Also, the
+        functions added for skyprobe (in-flight Mcal calculation,
+        color terms) are also incorporated.  I also substantially
+        re-organized the basic code to make the different modes more
+        sensible.  There are still a few changes needed for addstar
+        v2.0 (which will be able to handle the pan-starrs bandwidth).
+        specifically, addstar needs to handle the catalog files using
+        the TBD sky.db interfaces, it needs to interact with a DVO
+        server in a client/server way (to divide up the sky), it needs
+        to handle multiple versions of the table formats, and it might
+        need to know about the mysql backend interface.  
+
+2005.02.24 : I am adding the mosaic two-level astrometry information to the Image table.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/camera-sample.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/camera-sample.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/camera-sample.txt	(revision 22322)
@@ -0,0 +1,81 @@
+# example camera layout file
+
+# arcsec per mm
+PLATE_SCALE  25.0
+
+DPLATE_X     1.0e-5
+DPLATE_Y     1.0e-5
+
+NCHIPS        64
+NAXIS1      4800
+NAXIS2      4800
+
+# mm per pixel
+PIXEL_SCALE   0.010
+
+# for now, we force the chip rotation to be 0 and parity to be 1,1
+# ref      name  dX      dY
+CHIP.000   x0y0 -19200  -19200
+CHIP.001   x1y0 -14400  -19200
+CHIP.002   x2y0  -9600  -19200
+CHIP.003   x3y0  -4800  -19200
+CHIP.004   x4y0      0  -19200
+CHIP.005   x5y0   4800  -19200
+CHIP.006   x6y0   9600  -19200
+CHIP.007   x7y0  14400  -19200
+CHIP.008   x0y1 -19200  -14400
+CHIP.009   x1y1 -14400  -14400
+CHIP.010   x2y1  -9600  -14400
+CHIP.011   x3y1  -4800  -14400
+CHIP.012   x4y1      0  -14400
+CHIP.013   x5y1   4800  -14400
+CHIP.014   x6y1   9600  -14400
+CHIP.015   x7y1  14400  -14400
+CHIP.016   x0y2 -19200   -9600
+CHIP.017   x1y2 -14400   -9600
+CHIP.018   x2y2  -9600   -9600
+CHIP.019   x3y2  -4800   -9600
+CHIP.020   x4y2      0   -9600
+CHIP.021   x5y2   4800   -9600
+CHIP.022   x6y2   9600   -9600
+CHIP.023   x7y2  14400   -9600
+CHIP.024   x0y3 -19200   -4800
+CHIP.025   x1y3 -14400   -4800
+CHIP.026   x2y3  -9600   -4800
+CHIP.027   x3y3  -4800   -4800
+CHIP.028   x4y3      0   -4800
+CHIP.029   x5y3   4800   -4800
+CHIP.030   x6y3   9600   -4800
+CHIP.031   x7y3  14400   -4800
+CHIP.032   x0y4 -19200       0
+CHIP.033   x1y4 -14400       0
+CHIP.034   x2y4  -9600       0
+CHIP.035   x3y4  -4800       0
+CHIP.036   x4y4      0       0
+CHIP.037   x5y4   4800       0
+CHIP.038   x6y4   9600       0
+CHIP.039   x7y4  14400       0
+CHIP.040   x0y5 -19200    4800
+CHIP.041   x1y5 -14400    4800
+CHIP.042   x2y5  -9600    4800
+CHIP.043   x3y5  -4800    4800
+CHIP.044   x4y5      0    4800
+CHIP.045   x5y5   4800    4800
+CHIP.046   x6y5   9600    4800
+CHIP.047   x7y5  14400    4800
+CHIP.048   x0y6 -19200    9600
+CHIP.049   x1y6 -14400    9600
+CHIP.050   x2y6  -9600    9600
+CHIP.051   x3y6  -4800    9600
+CHIP.052   x4y6      0    9600
+CHIP.053   x5y6   4800    9600
+CHIP.054   x6y6   9600    9600
+CHIP.055   x7y6  14400    9600
+CHIP.056   x0y7 -19200   14400
+CHIP.057   x1y7 -14400   14400
+CHIP.058   x2y7  -9600   14400
+CHIP.059   x3y7  -4800   14400
+CHIP.060   x4y7      0   14400
+CHIP.061   x5y7   4800   14400
+CHIP.062   x6y7   9600   14400
+CHIP.063   x7y7  14400   14400
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/config.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/config.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/config.txt	(revision 22322)
@@ -0,0 +1,68 @@
+
+addstar is expecting to get the following information from the header.
+
+Required keywords:
+
+NAXIS1, NAXIS2
+NASTRO (non-zero for valid astrometry solution)
+NSTARS 
+CERROR (astrometric error)
+
+a valid WCS:
+
+CTYPE1,2
+CRVAL1,2
+CRPIX1,2
+CDELT1,2
+PC001001
+PC001002
+PC002001
+PC002002
+
+PHOTCODE
+
+optional keywords:
+
+APMIFIT, dAPMIFIT
+FLIMIT, FSATUR
+FWHM_X, FWHM_Y, 
+TRATE (for driftscanning)
+ZERO_PT 
+
+Note that these entries are used to define the actual keywords:
+
+(eg, in the dvo.config file, DATE-KEYWORD tells addstar what keyword
+to use for the DATE).
+
+For Date/Time, one of the following three sets is needed:
+
+ * DATE-KEYWORD (date in format specified below)
+ * DATE-MODE (YYYY/MM/DD, MM/DD/YYYY, DD/MM/YYYY)
+ * UT-KEYWORD (HH:MM:SS for UT)
+
+ or:
+
+ * MJD-KEYWORD (MJD)
+
+ or: 
+
+ * JD-KEYWORD (JD)
+
+For Exposure time:
+
+ * EXPTIME-KEYWORD (seconds)
+
+For Airmass:
+
+ * AIRMASS-KEYWORD
+
+CCDNUM-KEYWORD??
+
+For sidereal time (optional):
+
+ * ST-KEYWORD
+
+For alternate EXTNAME:
+
+ * EXTNAME-KEYWORD
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/notes.txt	(revision 22322)
@@ -0,0 +1,532 @@
+
+2008.02.23
+
+  features I need to add / finish in dvo:
+
+  * detID / objID / imageID
+
+    some questions:
+
+      db_id
+      table_id (chip / warp / stack / diff)
+      chip_id / warp_id / stack_id / diff_id
+      
+      detections from stand-alone analysis would require an alternate sequence?
+      detections without an associated image get image_id = 0
+      
+    * imageID : we will define a new, unique running ID within dvo
+      against which joins are performed.  in addition, I will keep the
+      externally supplied ids as a reference (32bit id + 16bit source)
+
+    * detID : the detection (measure) should carry the imageID + detID as a
+      unique value. 
+
+    * objID : objects will get a unique ID when they are created
+      (sequence in table + table ID? or use the same bits as Maria?)
+
+  * extended measurement tables
+  * SDSS ingest
+  * 2MASS flags
+  
+
+2008.01.31
+
+  Adding extended sources:
+
+  * create a libautocode extended source table description (DONE)
+  * load the extended source measurements:
+    this could be done within LoadStars or as a separate pass over the file, loading the extended source tables
+    * we can call this function only optionally
+    * need to make the association between the psf and ext measurements via det ID
+  * in find_matches, we could/should record the reference from Stars to the matched average entry
+    that would let us use the PSF position to get the average object match:
+
+  * find_matches_extended
+    * loop over extended sources
+    * use ID to find the corresponding Stars entry
+    * use the averef to find the corresponding Average entry
+    * update the extended source table
+
+  * Write out ext sources with the rest of the data
+
+  where do we put the table? require split?
+
+  (160 byte per detection; PS1 will have 5e8 => 400 GB)
+
+2007.01.04
+
+  Use of options.photcode:
+    - M_REFLIST: used to assign a photcode value to the loaded data
+    - M_REFCAT: used to select data from the reference of the given photcode
+    - M_FAKEIMAGE: used to assign a photcode value to the generated data
+    - M_RESORT: unused
+    - M_IMAGE: is used to override or supply the image header photcode
+	       * only valid for single chip runs
+
+  Re: MOSAIC_PHU
+      - the mosaic phu needs to have the time to enable the image/mosaic match
+
+      - NX,NY are used for plotting the mosaic outline.  the center of
+        the mosaic is 0,0, and the mosaic outline is drawn from
+        -NX/2,-NY/2 to NX/2,NY/2.  A problem: NX,NY in Image is using
+        a short int.  If the units of the FPA/TPA coords are microns, this overflows.
+	* change NX,NY to int?
+	* require the use of mm not microns?
+	* for GPC, NX,NY ~ 384000 um excluding gaps
+
+      - only the astrometry, time, and NX,NY are loaded for MOSAIC_PHU headers.
+
+2007.01.03
+
+  I am working on adding the ability to read MEF files containing
+  multiple tables and multiple image headers.  Here are the details:
+
+  SIMPLE CMP:
+    PHU:
+      NAXIS = 2 or
+      SIMPLE = FALSE or
+      TEXTMODE = TRUE (use option; global variable)
+
+  SIMPLE CMF:
+    PHU: 
+      NAXIS = 0
+      NAXIS1 exists, > 0
+      NAXIS2 exists, > 0
+    EXT:
+      EXTNAME = SMPDATA, PS1DATA, etc (defines layout)
+
+  SIMPLE MEF:
+    PHU: 
+      NAXIS = 0
+      NAXIS1 does not exist
+      NAXIS2 does not exist
+      &CTYPE[4] is not "-DIS"
+      EXTEND is true
+    EXT HEAD:
+      EXTTYPE = IMAGE
+      EXTNAME = image.name
+      EXTDATA = table.name
+    EXT DATA:
+      EXTTYPE = SMPDATA, PS1DATA, etc (defines layout)
+      EXTNAME = table.name
+      EXTHEAD = image.name
+
+  MOSAIC MEF:
+    PHU: 
+      NAXIS = 0
+      NAXIS1 does not exist
+      NAXIS2 does not exist
+      &CTYPE[4] is "-DIS"
+      EXTEND is true
+    EXT HEAD:
+      EXTTYPE = IMAGE
+      EXTNAME = image.name
+      EXTDATA = table.name
+    EXT DATA:
+      EXTTYPE = SMPDATA, PS1DATA, etc (defines layout)
+      EXTNAME = table.name
+      EXTHEAD = image.name
+
+  MOSAIC PHU:
+    PHU: 
+      NAXIS = 0
+      NAXIS1 does not exist
+      NAXIS2 does not exist
+      &CTYPE[4] is "-DIS"
+      NEXTEND = 0 or  
+      EXTEND is false
+
+    
+
+2006.04.02
+
+  I am considering an option to insert fake camera images into the
+  database.  The camera would be described with a file specifying the
+  dimensions of each chip (Nx,Ny,pixel scale), the location of each
+  chip in the focal plane, and the optical parameters of the camera
+  (plate scale at center, x,y distortion terms).  Each call to addstar
+  would specify the boresite center:
+
+  addstar -fakeimage ra dec theta
+
+  the camera parameters should be loaded via the config information.
+
+2005.10.24
+
+  I have changed addstar to use the new, more flexible, SkyRegion
+  concept to define the sky layout rather than just the GSCRegion
+  files.  This includes the ability to subdivide the existing
+  GSCRegion concept, and to have a more flexible sky table
+  arrangement.  
+
+  Names of catalog files are a bit tricky.  The SkyRegion functions
+  return base names without fullpath or extensions (ie, n0000/0012).
+  There are a few places in addstar which need the fullname:
+
+  SkyListExitingSubset () requires the path and adds .cpt
+
+  load_pt_catalog () takes the path and adds .cpt.  it also allocates
+  a filename entry for catalog, which is freed later in the loop in
+  addstars
+
+  mkcatalog uses the name in the catalog structure and replaces .cpt
+  with .cpm, .cpn, .cps as needed.
+
+  *** I need to fix the name method: common function to set it once 
+  in the dvo functions.  perhaps a pointer in the SkyRegion structure.
+  This needs some 32/64 bit care, though.  use a long long (or other 
+  type guaranteed to be 64 bit), and case to the appropriate type (size_t) 
+  when it is used?  also needs to be freed.
+
+todo:
+
+  * sky db tools 
+  * parallel servers
+  * threaded addstard
+  * dvo_load daemon
+  * getstar (program and c-client)
+  * parallal / proper-motion data
+  * other additional Average / Measure columns
+  * deal with Myyyy as sky value
+  * better way to interpret flips header info for sky?
+  * fix unlink empty file problem
+  * programs like relphot and dvo need to be provided 
+    with sorted measure tables; add this as a feature 
+    of the load_catalog API?
+
+done:
+
+  o move addstar/gstars to AddstarClientOptions
+  o add thiscode to AddstarClientOptions
+  o address MOSAIC somehow
+  o replace skyprobe mode
+  o complete server db interactions
+  o address the cat & ref modes in client/server
+  o add airmass quality to options
+
+2005.10.19
+
+	I have added the client / server mode, and tested it out to a
+	fair degree.  It handles all of the available addstar modes,
+	including -ref and -cat.  A handful of things still need to be
+	done, including pushing some of the logic to enforce sorting
+	into the dvo load_catalog functions.  Also, I need to correct
+	the idiosyncratic problem with sky vs Myyyy for the skyprobe
+	data.  There are a handful of other improvements that are
+	needed before addstar / DVO is ready for IPP release.
+	However, at this point, it is ready for internal use by the
+	grad students, but not yet ready for CFHT use with Elixir.
+
+2005.10.14
+
+  the current db interactions are somewhat in efficient wrt the image
+  table.  the basic outline is as follows:
+
+  - load image overlaps (needs entire image table)
+  - find detection matches (uses overlap information for missed)
+  - save new image (needs calculated calibration value from detection
+    matches)
+  
+  currently, I access the image table twice, and both times I load the
+  entire thing.  I really only need to append in the second case.  To
+  get to that point, I need to:
+
+  - split image overlaps from image db load
+  - define image partial load (analogous with catalog LOAD_MEAS_META)
+  
+  for the moment, I'm being wasteful so I can defer this issue.
+
+2005.10.13
+
+  I am working to minimize usage of globals and cleanup addstar
+  functions to make them more appropriate for the client / server
+  model.  
+
+  I have dropped save_pt_catalog, and modified load_pt_catalog, as
+  well as the load_catalog functions.  Now, the choice of elements to
+  load is made by setting flags in the catalog structure itself
+  (catalog.catflags).  Thus this information is carried around by the
+  catalog variable.  the load_pt_catalog function now only returns
+  true.  it is now necessary to check the state of the disk file by
+  examining Nave_disk to decide if the file is empty or not.  This is
+  perhaps too limiting: it prevents us from using only one of the
+  catalog tables (say measure without average).  The concern I have is
+  to manage the process of cleaning up the catalogs if no data is
+  added to them:  I don't want addstar to leave behind empty files
+  (plus, I think this breaks the lock/load state logic).  Thus,
+  whenever addstar unlocks / closes a file, if the Nave_disk +
+  Naverage is 0, the file should be unlinked.
+
+2005.10.12
+
+  By friday (2005.10.07), I managed to get the update process
+  working well, and tested it with some example data to demonstrate
+  that the partial update improved speed hugely.  Saturday, I started
+  to work on the client/server separation of addstar.  The first step
+  was to test out socket communication issues.  I set up a simple
+  client/server pair, in which one program listens on a socket while
+  the client connects to the socket and sends some command.  I set up
+  two autocode functions to send and receive complete structures
+  (Send_*, Recv_*) and added these to libautocode.  I have been able
+  to demonstrate sending Stars, Image, and Options to the server.
+
+  The next difficulty is in pulling out the command-line and
+  configuration options.  My original, somewhat lazy, design over-used
+  global variables to pass information between program sections.  This
+  needs to be cleaned up, especially if the server needs to have
+  multiple threads interacting with the db tables.  
+
+  Of the global variables that get set, some simply carry options on
+  how gstars should behave when it loads the data (including the
+  header interpretation information).  This can probably stay in the
+  globals for now.  Another group are configuration information used
+  only by the server-related functions, such as defining the location
+  of the database files and their format, etc.  These can also stay as
+  global, at least for now.  Another set of variables basically pass
+  information from the client to the server on how to behave *this
+  time*.  I have defined the AddstarClientOptions structure to carry
+  these, and will pass them from the client to the server along with
+  the stars and the image data.
+
+  Some outstanding issues:
+
+  sidereal time / latitude
+
+  I am carrying these as globals simply because the image structure
+  did not have them.  This is silly: I can define them as part of the
+  internal image structure without breaking any of the I/O issues.  
+
+  thiscode
+
+  I have been using this as a static photcode structure.  The
+  Send_*/Recv_* mechanisms are better suited to simple structures.  I
+  probably need to change this to just the code, and use the lookup
+  functions to the full structures where needed.  It can be part of
+  the options, and I should re-write functions which use it to accept
+  the options as an argument rather than rely on it as a global.
+
+  mosaic
+
+  this stores the coords structure for a needed mosaic.  this should
+  be kept, filled as needed by the client, and sent to the server with
+  the image.  perhaps it can be moved out of global.
+
+2005.10.06
+
+  I am getting seg faults from find_matches_closest, apparently
+  related to the partial Measure load.  They occur when trying to
+  realloc secfilt.  I tried to debug using the ohana_allocate stuff,
+  but it causes problems because fitsio does not fall under the ohana
+  memory system.  This is fairly bad; I should probably deal with that
+  issue before pursuing the other stuff.  Do I have to make libfits
+  depend on libohana, in which case I should probably split off libdvo
+  from libohana.  That is probably not a bad plan in any case...
+
+2005.10.07
+
+	I have finished the basic implementation of the update mode.
+	I have been able to demonstrate substantial improvements in
+	speed when the number of existing measurements dominates the
+	total number of measurements and the number of averages is
+	typically small compared to the number of measurements (ie,
+	most objects are real, detected in most images, and each new
+	image supplies many new measurements of objects which exist
+	and not many of objects which don't exist already).  The speed
+	gain is significant in this context because the average table
+	is small compared to the measure table; since both update and
+	full-load methods require the complete average table, there is
+	no difference in the load time for the average table.  
+
+	I was having some memory collision problems, and attempting to
+	use the ohana_allocate functions reminded me that the libFITS
+	functions were not supported under ohana_allocate.  This was
+	unhelpful.  I bit the bullet and split libohana into libohana
+	(base functions only, including ohana_allocate) and libdvo
+	(functions based on the libautocode structures).  Doing this
+	allowed me to make libFITS depend on libohana (including
+	ohana_allocate).  BUT, this forced me to change all LDFLAGS
+	entries in ohana to swap -lohana -lFITS for -lFITS -lohana,
+	and to add include <fitsio.h> in some cases.
+
+2005.10.06
+	split / nosort / update
+	I have added a few new concepts to addstar recently: split
+	catalog files, nosort for the measurement table, and
+	update-only.
+
+	split mode
+
+	The split mode is quite straightforeward.  In this mode, each
+	catalog is represented by a set of four files: *.cpt, *.cpm,
+	*.cpn, *.cps.  Each file contains only one FITS table of the
+	data, along with basic header and empty matrix.  Having
+	individual tables for each component of the database lets me
+	add entries without re-writing the entire table.  This should
+	save on I/O operations in the long run.  
+
+	The first file contains the table of averages, and is the file
+	normally identified in the table lookup functions.  The header
+	of this file contains the names of the other table files
+	(paths relative to the directory containing the cpt file).
+	The names and extensions are specified in 'mkcatalog.c'; all
+	other functions use the defined filename references, rather
+	than expecting a naming convention.
+
+	The additional files contain the measures (cpm), missings
+	(cpn), and secfilt (cps) elements of the catalog tables.  
+
+	To facilitate the handling of the additional filenames, file
+	pointers, and headers, Catalog was extended to include
+	pointers to the measure, missing, and secfilt files as
+	additional catalogs.  When the data are loaded into memory,
+	these catalogs are locked (as usual), and file information is
+	stored in the individual Catalog entries; the data segements
+	are all loaded into the main catalog pointers (eg, measures
+	are loaded into catalog[0].measure, rather than
+	catalog[0].measure_catalog[0].measure).
+	
+	The function 'load_catalog' auto-recognizes the SPLIT format
+	by looking for the header keyword MEASURE, identifying the
+	file containing the measures.  The identification of the RAW
+	format and the SPLIT format are not cross-checked: if the
+	NAXIS keyword is set to 2, the file is assumed to be RAW, even
+	if the MEASURE keyword is present.  Careful with this (though
+	there is no reason the main matrix should be used in a basic
+	database table).
+
+	nosort 
+
+	the nosort option by itself provides a minor processing
+	speed-up by deferring the re-sorting of the measurement table
+	until after multiple addstar processes are run.  addstar
+	should not require the measurements to be sorted, so this step
+	can be safetly deferred if only addstars are being performed.
+	the other DVO operations require the sorted table, so the sort
+	must be performed before they are run (either as part of the
+	catalog load, not implemented yet, or with a call to addstar
+	without the -nosort option set.  the real goal of the nosort
+	option is to enable the -update concept in addstar, in which
+	only the new rows are written out; this will only work if
+	addstar can handle unsorted measures. 
+
+	the nosort option required the addition of a 'sorted' element
+	in the Catalog structure to track if the data are sorted or
+	not.  On load, this flag is set based on the value of the
+	header keyword SORTED; if the data is sorted during addstar,
+	the flag is appropriately set, otherwise it is set FALSE be
+	default.  
+
+	The nosort option requires a function which can generate the
+	'next_meas' link sequence based on the measure table.  there
+	is now a function called 'build_meas_link' which generates a
+	correct link list; there is also the pair of functions
+	'init_meas_links' and'init_miss_links' to generate the links
+	in the event that the table is sorted (should be must
+	quicker).
+
+	The 'missing' table is problematic: the LONEOS and ELIXIR
+	formats do not carry an averef entry, thus they do not have
+	enough information to define the links based only on the
+	missing table.  This means we are forced to write out a sorted
+	missing table; the nosort option is invalid for the missing
+	table.  One future upgrade path is to add the averef entry to
+	the PANSTARRS format and then only require the missing table
+	to be sorted if the format is old and does not support
+	-nosort.  (Note also that, for the moment, the missing table
+	has only a single valid format).
+
+	In the process of defining the nosort option, I also cleaned
+	up a bit the find_matches functions to use clearer functions
+	for the links.  
+	
+	update
+
+	The 'update' process in principle allows addstar to
+	substantially reduce the amount of I/O it needs to perform by
+	only requiring addstar to write out new measures and new
+	average/secfilt entries.
+
+	The 'missing' table is problematic: since the format does not
+	support the 'nosort' option, it is not possible to use update
+	with the missing table.  This means we are forced to write out
+	a complete, sorted missing table.  This is currently
+	implemented in update_catalog_split by simply writing out the
+	complete missing table.  In fact, this choice is still flawed
+	because the average table, since it is not written out in full
+	each time, is inconsistent with the missing table: the Nn
+	entries for each average, which identifies the number of
+	missing entries, are not updated.  In practice, this means
+	that the -update option forces the use of the -missed option,
+	though at the moment, this is not forced or checked in any
+	way.
+
+	Note that the 'missed' table contains duplicate information
+	and can, in principle, be completely regenerated at any time.
+	This should be an addstar option: to re-construct the missing
+	table, potentially with constraints on the images which are
+	searched for matches.  
+
+2005.10.04
+	- moved measure/missing list manipulation to separate functions
+	- added concept of sorted / unsorted measure catalog
+	- defined build_meas_links and reorder_measure,missing
+	- some cleanup of both find_matches.c and find_matches_closest.c
+
+2005.10.03:
+	- dropping GSCRegion *region entry from find_matches (unused!)
+	- adding function find_matches_closest (alternate matches)
+
+2005.08.19:
+	changed load_photcode to handle CATMODE and CATFORMAT variations
+	- addstar.h: added CATMODE and CATFORMAT globals
+	- ConfigInit: read CATMODE and CATFORMAT from config
+	- gcatalog: set catalog.catmode from CATMODE
+	- mkcatalog: set CATFORMAT and CATMODE for new catalog
+	- wcatalog: set CATFORMAT for new catalog
+	
+	using full photometry conversions in find_matches
+	added SetZeroPoint to gstars to enable phot conversions
+	
+2005.08.15:
+	cleanup of the minor Wall,Werror messages
+
+2005.03.07 : notes related to new version of addstar
+
+- USNO seems to work fine now:
+
+  addstar -region 9.0 12.0 9.0 12.0 -cat usno -p USNO_RED
+  addstar -region 9.0 12.0 9.0 12.0 -cat usno -p USNO_BLUE
+
+- 2MASS seems to work as well:
+
+  addstar -region 3.0 12.0 12.0 14.0 -cat 2mass -p 2MASS_J
+  addstar -region 3.0 12.0 12.0 14.0 -cat 2mass -p 2MASS_H
+  addstar -region 3.0 12.0 12.0 14.0 -cat 2mass -p 2MASS_K
+
+- both of these are somewhat inefficient since they need to search
+  through the (larger) USNO/2MASS data files for each output ptolemy
+  region file.
+
+- GSC seems to work fine now:
+
+  addstar -region 3.0 12.0 12.0 14.0 -cat gsc 
+  (photcode is not needed here since there is only one)
+
+- images seems to work fine now
+
+- general concerns:
+
+  - RA 0,360 boundary is likely to have problems, especially with
+    region selection
+  - mosaic astrometry concepts still need to be added.
+
+  - average magnitudes are being set incorrectly in addstar (not
+    taking account of the zero-points).  I don't really want to apply
+    the colors here, but I need to do something, perhaps?
+
+    one option is to only apply the correction for the first
+    measurement of an object, unlike the way astrometry works
+    currently.  both of these should be updated based on
+    relphot/uniphot and eqiuvalent relastro/uniastro independently
+    from the addstar step.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/pmm.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/pmm.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/pmm.txt	(revision 22322)
@@ -0,0 +1,164 @@
+	
+--
+
+As usual, I have totally forgotten the previous discussion with respect
+to rummaging around in the USNO-B database, but that is just normal for me.
+I think that there are two ways to proceed, and both stem from the exercise
+of attaching WCS headers to the individual PMM camera footprints that we
+did for Google.  I should note that Andy Connolly swears that Google
+will soon serve the 10 TPixels that we gave them, but I am not going
+to hold my breath.
+
+The PMM scanned each Schmidt plate as 588 individual footprints.  The
+scale is about 0.9 arcsec/pixel (2X optics, 6.8 micron camera pixels,
+67.5 arcsec/mm for Schmidt plates).  The readout format was 1394x1037
+(not even an integer multiple of 4!) of which we claimed that the
+useful area was from (67:1378 and 3:1035).  There are no dead pixels
+in either camera, the bias frames are zero, and we have the flats.
+Each image covers 17.8 by 14.0 millimeters but the step-and-stop
+was done on a 16 by 12 millimeter grid giving about 2 millimeters
+(about 2.2 arcminutes) overlap between footprints.
+
+I wrote software that extracted each footprint from each plate,
+made a short, paired list of (X,Y) and (RA,Dec), fed this to imwcs(),
+and saved the image+WCS as separate FITS files.  We scanned 12070
+Schmidt plates of which we saved the pixels for all but the 897 UJ
+(3 minute exposure blue plates from POSS-II).  So far as I know,
+We have 588 times 11,173 FITS files spinning on our disks somewhere.
+These files have been given to Google and SDSC, and they are available
+to anybody else that could seriously use them.  We do not have the
+bandwidth to make soft copies, so we must negotiate for reasonable
+ways to copy the 10 TPixels involved.  We have yet to play the same
+game with the Yale Astrograph plates, but will eventually add these
+3 TPixels of FITS images to the archive.
+
+So Option 1 would be to just hand you guys the FITS WCS images and let you
+play.  You can figure out whether IPP or SExtractor works on photographic
+pixels, how to do photometry, how to handle the overlap zones, and
+all of the other unpleasant things I had to do for PMM for which I
+have been so roundly criticized.
+
+Option 2 would require a small hack in the pixel processing pipeline
+that would save the PMM detection lists and the WCS transformations
+in some reasonable way.  This would save you the work and bandwidth
+of dealing with the images, but would then rely on the PMM's image
+processing software for computation of the detection parameters.
+
+Depending on which, if either, seems to be a reasonable way to
+proceed, I think that we have enough bandwidth (or could use DVDs)
+so send a small sample of what the data would look like.  Did I send
+you the flat ASCII file of the nominal pointings and epochs (12070 lines)?
+
+Let me know.  I will be in Flagstaff for the next couple of weeks,
+but then my travel schedule goes nuts.
+
+-Dave
+
+--
+
+I had thought that I passed you this file before.  The files
+I would be passing you would start with the 7 character root
+in the third column, SSnnnnn where SS is a unique survey
+identifier, and nnnnn is a unique plate number from that
+observatory.  You can safely ignore the first 2 columns.
+The plate is the third, followed by DDMonYYYY HH:MM
+HHMMSS sDDMMSS Emulsion Filter Exposure H:MM.  It is
+believed that the UT is for the start of the exposure,
+but it might be for the middle.  Many things are lost in
+the cobwebs of antiquity.  I can give you a table of
+nominal wavelengths for various Emulsion+Filter combinations.
+The final column is the hour angle of mid-exposure.
+
+-Dave
+
+example:
+
+0         1         2         3         4         5         6         7         8         9         0         
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+so0001 /nofs/leo1/sel/jb0/se100/so0001a.inf PO00570 23AUG1952 09:22 194300 +900000 103AO NONE    10 0:13E
+so0002 /nofs/leo1/sel/jb0/se100/so0002a.inf PO00567 22AUG1952 08:21 223100 +900000 103AO NONE    10 1:14E
+so0003 /nofs/leo1/sel/jb0/se100/so0003a.inf PO00568 22AUG1952 10:53 000624 +843140 103AO NONE    10 1:10W
+so0004 /nofs/leo1/sel/jb0/se100/so0004a.inf PO01277 23NOV1954 06:45 025031 +842224 103AO NONE    12 0:29W
+so0005 /nofs/leo1/sel/jb0/se100/so0005a.inf PO01328 29JAN1955 06:42 052420 +840404 103AO NONE    12 2:19W
+
+
+emulsions:
+098
+098-0
+103AD
+103AE
+103AO
+IIIAF
+IIIAJ
+IVN
+
+filters:
+#12
+AMB2
+AMB3
+AMB4
+AMB5
+AMB6
+AMB7
+AMB8
+GG358
+GG385
+GG395
+MULTI
+NONE
+OG590
+RED
+RED66
+RED67
+RED68
+RED69
+RED70
+RED71
+RED73
+RG2444
+RG600
+RG610
+RG630
+RG715
+RG9
+RP2444
+WR88A
+YEL3
+YEL8
+
+combinations:
+098 RED 
+098-0 RED70 
+098-0 RG630 
+103AD MULTI 
+103AD YEL3 
+103AD YEL8 
+103AE #12 
+103AE AMB2 
+103AE AMB3 
+103AE AMB4 
+103AE AMB5 
+103AE AMB6 
+103AE AMB7 
+103AE AMB8 
+103AE NONE 
+103AE RED66 
+103AE RED67 
+103AE RED68 
+103AE RED69 
+103AE RED70 
+103AE RED71 
+103AE RED73 
+103AE RG2444 
+103AE RP2444 
+103AO NONE 
+IIIAF OG590 
+IIIAF RG600 
+IIIAF RG610 
+IIIAF RG630 
+IIIAJ GG358 
+IIIAJ GG385 
+IIIAJ GG395 
+IVN RG715 
+IVN RG9 
+IVN WR88A 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/sdss-tsObj.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/sdss-tsObj.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/sdss-tsObj.txt	(revision 22322)
@@ -0,0 +1,686 @@
+.
+  Calibrated Objects
+
+*File Format:* FITS binary table
+
+*Name:* /tsObj-rrrrrr-c-v-ffff.fit/, where /rrrrrr/ is the imaging run
+number, /c/ is the camera column (1-6), and /v/ is the rerun number and
+ffff is the first field number of the segment.
+
+*Produced by:* ts
+
+*Used by:* ts, sx
+
+*Size:* 7 * 2880 + (2240 * Nstars) {rounded up to nearest multiple of
+2880) * Nfields. For a single pipeline run with 1000 fields, 400 objects
+per field, 887.06 Mb.
+
+*Archived?* Yes, by sx.
+
+
+    Description
+
+Calibrated version of the object lists put out by the /frames/ pipeline.
+
+
+    Primary Header
+
+SIMPLE  =                    T
+BITPIX  =                    8
+NAXIS   =                    0
+EXTEND  =                    T
+RUN     =                  XXX / Imaging run number.
+CAMCOL  =                  XXX / Column in the imaging camera.
+RERUN   =                  XXX / Rerun number
+FIELD0  =                  XXX / First field reduced.
+NFIELDS =                  XXX / Number of fields reduced.
+STRIPE  =                  XXX / Stripe number
+STRIP   = 'X       '           / N - north, S - south
+EQUINOX =          XXXX.XXXXXX / Equinox of great circle scanned (Julian years)
+NODE    =           XXX.XXXXXX / Ascending node of great circle scanned (deg)
+INCL    =           XXX.XXXXXX / Inclincation of great circle scanned (deg)
+C_OBS   =            XXXXX.XXX / CCD clock rate (usec/unbinned-row)
+TRACKING=            XXXXX.XXX / Tracking rate (arcsec/TAI-sec)
+PHOTO_ID= 'XXX     '           / Photometric pipeline run id.
+TASTR_ID= 'XXX     '           / Astrometric pipeline run id for target.
+EASTR_ID= 'XXX     '           / Astrometric pipeline run id for export.
+TFCAL_ID= 'XXX     '           / Photometric calibration run id for target.
+EFCAL_ID= 'XXX     '           / Photometric calibration run id for export.
+PHOT_VER= 'XXX     '           / Version of frames pipeline used.
+TAST_VER= 'XXX     '           / Version of astrometric pipeline for target.
+EAST_VER= 'XXX     '           / Version of astrometric pipeline for export.
+TFCA_VER= 'XXX     '           / Version of fcalib for target.
+EFCA_VER= 'XXX     '           / Version of fcalib for export.
+TARG_VER= 'XXX     '           / Version of target pipeline used.
+EXPO_VER= 'XXX     '           / Version of export pipeline used.
+FILTERS = 'XXX XXX ...'        / Filter order in array fields
+OBJ_SRC = 'XXX     '           / Source of objects:
+COMMENT                        /    OPDB_PRELIM = OPDB, preliminary data
+COMMENT                        /    OPDB_EXPORT = OPDB, exported to SX
+COMMENT                        /    SX          = Science Database
+COMMENT                        /    FLATFILES   = Flat files
+TARG_SRC= 'XXX     '           / Source of target info:
+COMMENT                        /    OPDB_PRELIM = OPDB, preliminary data
+COMMENT                        /    OPDB_EXPORT = OPDB, exported to SX
+COMMENT                        /    SX          = Science Database
+COMMENT                        /    FLATFILES   = Flat files
+RESOLVED=                    T / Data from a resolved segment
+EXPORTED=                  XXX / Type of objects exported in this file:
+COMMENT                        /    1 = spectroscopic targets only
+COMMENT                        /    2 = primaries only
+COMMENT                        /    3 = primaries and secondaries
+COMMENT                        /    4 = all objects
+FAMILIES=                    T / Contains family members of included objects
+REF_BAND=                    2 / band used for canonical centre
+DARK_VAR= '4 4 4 4 4'          / Per-pixel variances at zero DN (DN^2)
+GAIN    = '3.5 3.5 3.5 3.5 3.5' / Mean gains of amplifiers (e/DN)
+END                         
+
+
+    Table Header
+
+There is one HDU of this type in the file for each field, ordered by
+field number. The "QUALITY" keyword records the quality of data in the
+field, in terms of its acceptance for the survey, and can take the
+following meanings:
+
+  'BAD      '   /* Not acceptable for the survey */
+  'ACCEPTABLE'  /* Acceptable for the survey, but we'd better data */
+  'GOOD     '   /* Fully acceptable --- no desire for better data */
+  'MISSING  '   /* No objects in this field, because data is missing, but we
+                 * accept the field into the survey as a hole in the survey */
+  'HOLE     '   /* Data in this field is not acceptable, but we will accept the
+                 * field into the survey as a hole in the survey, meaning none
+                 * of the objects in the field are part of the survey */
+
+The "STATUS_X" keywords record the status of the PSF fit for that field
+in that filter, and can take the following meanings:
+
+  'UNKNOWN  '    /* ??? */
+  'OK       '    /* PSF fitted by 2nd order parabolas */
+  'PSF22    '    /* PSF fitted by linear functions */
+  'PSF11    '    /* PSF fitted by a constant */
+  'NOPSF    '    /* PSF fit from previous frame used*/
+  'ABORTED  '    /* ??? */
+  'MISSING  '    /* ??? */
+
+PSP_STAT is the maximum value of STATUS_X over all 5 filters.
+
+The /objc_type/, /objc_flags/, /objc_flags2/, /flags/, /flags2/, and
+/type/ fields are enumerated types or bit masks, with the same set of
+legal values as found for the same fields in the fpObjc <fpObjc.html> file.
+
+The /status/ field is a bit mask specifying the status of the object in
+the survey. The bits are defined as:
+
+  AR_OBJECT_STATUS_SET        = 0x1,    /* This object's status has been set */
+					/* in reference to its own run.      */
+  AR_OBJECT_STATUS_GOOD       = 0x2,    /* Good as determined by its         */
+					/* object flags.  Absence implies    */
+                                        /* bad.  This flag is set by         */
+                                        /* "setObjectStatus".                */
+  AR_OBJECT_STATUS_DUPLICATE  = 0x4,    /* This object has one or more       */
+					/* duplicate detections in an        */
+					/* adjacent field of the same Frames */
+					/* Pipeline Run.  This is set by     */
+					/* "setObjectStatus".                */
+  AR_OBJECT_STATUS_OK_RUN     = 0x10,   /* Located within the primary range  */
+					/* of rows for this field.  This is  */
+                                        /* usable object.  This flag is set  */
+                                        /* by "setObjectStatus".             */
+  AR_OBJECT_STATUS_RESOLVED   = 0x20,   /* This object has been resolved     */
+					/* against other runs.               */
+  AR_OBJECT_STATUS_PSEGMENT   = 0x40,   /* Belongs to a PRIMARY segment.     */
+					/* This does not imply that this is a*/
+					/* primary object.                   */
+  AR_OBJECT_STATUS_FIRST_FIELD= 0x100,  /* Belongs to the first field in its */
+					/* segment.  Used to distinguish     */
+					/* objects in fields shared by two   */
+					/* segments.                         */
+  AR_OBJECT_STATUS_OK_SCANLINE= 0x200,  /* Lies within valid nu range        */
+					/* for its scanline.                 */
+  AR_OBJECT_STATUS_OK_STRIPE  = 0x400,  /* Lies within valid eta range for   */
+					/* its stripe.                       */
+  AR_OBJECT_STATUS_SECONDARY  = 0x1000, /* This is a secondary survey object.*/
+  AR_OBJECT_STATUS_PRIMARY    = 0x2000, /* This is a primary survey object.  */
+  AR_OBJECT_STATUS_TARGET     = 0x4000  /* This is a spectroscopic target.   */
+
+The /primTarget/ field is a bit mask specifying which primary target
+catagories the object was selected in. The bits are defined as:
+
+  AR_TARGET_QSO_HIZ           = 0x1,
+  AR_TARGET_QSO_CAP           = 0x2,
+  AR_TARGET_QSO_SKIRT         = 0x4,
+  AR_TARGET_QSO_FIRST_CAP     = 0x8,
+  AR_TARGET_QSO_FIRST_SKIRT   = 0x10,
+  AR_TARGET_QSO_MAG_OUTLIER  = 0x2000000,
+  AR_TARGET_QSO_REJECT	      = 0x20000000,
+  AR_TARGET_GALAXY_RED        = 0x20,
+  AR_TARGET_GALAXY_RED_II     = 0x4000000,
+  AR_TARGET_GALAXY            = 0x40,
+  AR_TARGET_GALAXY_BIG        = 0x80,
+  AR_TARGET_GALAXY_BRIGHT_CORE= 0x100,
+  AR_TARGET_ROSAT_A           = 0x200,
+  AR_TARGET_ROSAT_B           = 0x400,
+  AR_TARGET_ROSAT_C           = 0x800,
+  AR_TARGET_ROSAT_D           = 0x1000,
+  AR_TARGET_ROSAT_E           = 0x8000000,
+  AR_TARGET_STAR_BHB          = 0x2000,
+  AR_TARGET_STAR_CARBON       = 0x4000,
+  AR_TARGET_STAR_BROWN_DWARF  = 0x8000,
+  AR_TARGET_STAR_SUB_DWARF    = 0x10000,
+  AR_TARGET_STAR_CATY_VAR     = 0x20000,
+  AR_TARGET_STAR_RED_DWARF    = 0x40000,
+  AR_TARGET_STAR_WHITE_DWARF  = 0x80000,
+  AR_TARGET_STAR_PN           = 0x10000000,
+  AR_TARGET_SERENDIP_BLUE     = 0x100000,
+  AR_TARGET_SERENDIP_FIRST    = 0x200000,
+  AR_TARGET_SERENDIP_RED      = 0x400000,
+  AR_TARGET_SERENDIP_DISTANT  = 0x800000,
+  AR_TARGET_SERENDIP_MANUAL   = 0x1000000
+
+The /secTarget/ field is a bit mask specifying which secondary target
+catagories the object was selected in. The bits are defined as:
+
+  TAR_TARGET_LIGHT_TRAP        = 0x1,
+  TAR_TARGET_REDDEN_STD        = 0x2,
+  TAR_TARGET_TEST_TARGET       = 0x4,
+  TAR_TARGET_QA                = 0x8,
+  TAR_TARGET_SKY               = 0x10,
+  TAR_TARGET_SPECTROPHOTO_STD  = 0x20,
+  TAR_TARGET_GUIDE_STAR        = 0x40,
+  TAR_TARGET_BUNDLE_HOLE       = 0x80,
+  TAR_TARGET_QUALITY_HOLE      = 0x100,
+  TAR_TARGET_HOT_STD           = 0x200
+
+The /CULLED/ keyword is a bit mask with the same bit values as the
+/primTarget/ field. If a bit is set, then all targets of that target
+type were culled during target selection, and thus this field is
+excluded from that science sample.
+
+XTENSION= 'BINTABLE'          
+BITPIX  =                    8
+NAXIS   =                    2
+NAXIS1  =                 2732
+NAXIS2  =                  XXX
+PCOUNT  =                    0
+GCOUNT  =                    1
+TFIELDS =                  146
+FIELD   =                  XXX / Field sequence number within the run.
+QUALITY = 'XXX     '           / Quality of field in terms of survey acceptance
+CULLED  =                  XXX / Culling bit mask
+MJD_U   =         XXXXX.XXXXXX / MJD(TAI) when row 0 of u' frame was read
+MJD_G   =         XXXXX.XXXXXX / MJD(TAI) when row 0 of g' frame was read
+MJD_R   =         XXXXX.XXXXXX / MJD(TAI) when row 0 of r' frame was read
+MJD_I   =         XXXXX.XXXXXX / MJD(TAI) when row 0 of i' frame was read
+MJD_Z   =         XXXXX.XXXXXX / MJD(TAI) when row 0 of z' frame was read
+SEEING_U=                 XX.X / Seeing on the u' frame (FWHM, arcsecs)
+SEEING_G=                 XX.X / Seeing on the g' frame (FWHM, arcsecs)
+SEEING_R=                 XX.X / Seeing on the r' frame (FWHM, arcsecs)
+SEEING_I=                 XX.X / Seeing on the i' frame (FWHM, arcsecs)
+SEEING_Z=                 XX.X / Seeing on the z' frame (FWHM, arcsecs)
+STATUS_U= 'XXX     '           / PSF fit status on u' frame
+STATUS_G= 'XXX     '           / PSF fit status on g' frame
+STATUS_R= 'XXX     '           / PSF fit status on r' frame
+STATUS_I= 'XXX     '           / PSF fit status on i' frame
+STATUS_Z= 'XXX     '           / PSF fit status on z' frame
+PSP_STAT= 'XXX     '           / Maximum of STATUS value on all 5 frames
+PSFERR_U=               XXX.XX / Photometric err due to imperfect PSF model(mag)
+PSFERR_G=               XXX.XX / Photometric err due to imperfect PSF model(mag)
+PSFERR_R=               XXX.XX / Photometric err due to imperfect PSF model(mag)
+PSFERR_I=               XXX.XX / Photometric err due to imperfect PSF model(mag)
+PSFERR_Z=               XXX.XX / Photometric err due to imperfect PSF model(mag)
+SKY_U   =               XXX.XX / Global sky value after obj sub (mag/arcsec^2)
+SKY_G   =               XXX.XX / Global sky value after obj sub (mag/arcsec^2)
+SKY_R   =               XXX.XX / Global sky value after obj sub (mag/arcsec^2)
+SKY_I   =               XXX.XX / Global sky value after obj sub (mag/arcsec^2)
+SKY_Z   =               XXX.XX / Global sky value after obj sub (mag/arcsec^2)
+TFORM1  = '1J      '          
+TTYPE1  = 'run     '           / Run ID
+TUNIT1  = 'unitless'          
+TFORM2  = '1J      '          
+TTYPE2  = 'camCol  '           / Camera column
+TUNIT2  = 'unitless'          
+TFORM3  = '1J      '          
+TTYPE3  = 'rerun   '           / Rerun number
+TUNIT3  = 'unitless'          
+TFORM4  = '1J      '          
+TTYPE4  = 'field   '           / Field ID
+TUNIT4  = 'unitless'          
+TFORM5  = '1J      '          
+TTYPE5  = 'parent  '           / Parent
+TUNIT5  = 'unitless'          
+TFORM6  = '1J      '          
+TTYPE6  = 'id      '           / Object ID, unique within its field (1 indexed)
+TUNIT6  = 'unitless'          
+TFORM7  = '1J      '          
+TTYPE7  = 'nchild  '           / Number of Children
+TUNIT7  = 'unitless'          
+TFORM8  = '1J      '          
+TTYPE8  = 'objc_type'          / Classification
+TUNIT8  = 'unitless'          
+TFORM9  = '1E      '
+TTYPE9  = 'objc_prob_psf'      / Probablity object is a star
+TUNIT9  = 'unitless'          
+TFORM10 = '1J      '          
+TTYPE10 = 'catID   '           / Catalog ID
+TUNIT10 = 'unitless'          
+TFORM11 = '1J      '          
+TTYPE11 = 'objc_flags'         / Object flags
+TUNIT11 = 'unitless'          
+TFORM12 = '1J      '          
+TTYPE12 = 'objc_flags2'        / More object flags
+TUNIT12 = 'unitless'          
+TFORM13 = '1E      '          
+TTYPE13*= 'objc_rowc'          / Nominal row position (r' coordinates)
+TUNIT13 = 'pixels  '          
+TFORM14 = '1E      '          
+TTYPE14*= 'objc_rowcErr'       / Nominal row position (r' coordinates) error
+COMMENT                        / This is the centroiding error only.  It does
+COMMENT                        / not include errors from the TRANS structures.
+TUNIT14 = 'pixels  '          
+TFORM15 = '1E      '          
+TTYPE15*= 'objc_colc'          / Nominal column position (r' coordinates)
+TUNIT15 = 'pixels  '          
+TFORM16 = '1E      '          
+TTYPE16*= 'objc_colcErr'       / Nominal column position (r' coordinates) error
+COMMENT                        / This is the centroiding error only.  It does
+COMMENT                        / not include errors from the TRANS structures.
+TUNIT16 = 'pixels  '          
+TFORM17 = '1E      '          
+TTYPE17 = 'rowv    '           / Row-component of object's velocity
+TUNIT17 = 'deg/day '          
+TFORM18 = '1E      '          
+TTYPE18 = 'rowvErr '           / Row-component of object's velocity error
+TUNIT18 = 'deg/day '          
+TFORM19 = '1E      '          
+TTYPE19 = 'colv'               / Column-component of object's velocity 
+TUNIT19 = 'deg/day'          
+TFORM20 = '1E      '          
+TTYPE20 = 'colvErr'            / Column-component of obejct's velocity error
+TUNIT20 = 'deg/day'          
+TFORM21 = '5E      '          
+TTYPE21 = 'rowc    '           / Row center
+TUNIT21 = 'pixels  '          
+TFORM22 = '5E      '          
+TTYPE22 = 'rowcErr '           / Row center error
+TUNIT22 = 'pixels  '          
+TFORM23 = '5E      '          
+TTYPE23 = 'colc    '           / Center colum 
+TUNIT23 = 'pixels  '          
+TFORM24 = '5E      '          
+TTYPE24 = 'colcErr '           / Center column error
+TUNIT24 = 'pixels  '          
+TFORM25 = '5E      '          
+TTYPE25 = 'sky     '           / Sky flux
+TUNIT25 = 'asinh mag/arcsec^2'         
+TFORM26 = '5E      '          
+TTYPE26 = 'skyErr  '           / Sky flux error
+TUNIT26 = 'asinh mag/arcsec^2'         
+TFORM27 = '5E      '          
+TTYPE27 = 'psfCounts'          / PSF flux
+TUNIT27 = 'asinh mag     '          
+TFORM28 = '5E      '          
+TTYPE28 = 'psfCountsErr'       / PSF flux error
+TUNIT28 = 'asinh mag     '          
+TFORM29 = '5E      '          
+TTYPE29 = 'fiberCounts'        / Flux in 3 arcsec diameter fiber radius
+TUNIT29 = 'asinh mag     '          
+TFORM30 = '5E      '          
+TTYPE30 = 'fiberCountsErr'     / Flux in 3 arcsec diameter fiber radius error
+TUNIT30 = 'asinh mag     '          
+TFORM31 = '5E      '          
+TTYPE31 = 'petroCounts'        / Petrosian flux
+TUNIT31 = 'asinh mag     '          
+TFORM32 = '5E      '          
+TTYPE32 = 'petroCountsErr'     / Petrosian flux error
+TUNIT32 = 'asinh mag     '          
+TFORM33 = 5E      '          
+TTYPE33 = 'petroRad'           / Petrosian radius
+TUNIT33 = 'arcsec '          
+TFORM34 = '5E      '          
+TTYPE34 = 'petroRadErr'        / Petrosian radius error
+TUNIT34 = 'arcsec '          
+TFORM35 = '5E      '          
+TTYPE35 = 'petroR50'           / Radius with 50 percent of Petrosian light
+TUNIT35 = 'arcsec '          
+TFORM36 = '5E      '          
+TTYPE36 = 'petroR50Err'        / Radius with 50 percent of Petrosian light error
+TUNIT36 = 'arcsec '          
+TFORM37 = '5E      '          
+TTYPE37 = 'petroR90'           / Radius with 90 percent of Petrosian light
+TUNIT37 = 'arcsec '          
+TFORM38 = '5E      '          
+TTYPE38 = 'petroR90Err'        / Radius with 90 percent of Petrosian light error
+TUNIT38 = 'arcsec  '          
+TFORM39 = '5E      '          
+TTYPE39 = 'Q       '           / Stokes Q parameter
+TUNIT39 = 'unitless'          
+TFORM40 = '5E      '          
+TTYPE40 = 'QErr    '           / Stokes Q parameter error
+TUNIT40 = 'unitless'          
+TFORM41 = '5E      '          
+TTYPE41 = 'U       '           / Stokes U parameter
+TUNIT41 = 'unitless'          
+TFORM42 = '5E      '          
+TTYPE42 = 'UErr    '           / Stokes U parameter error
+TUNIT42 = 'unitless'          
+TFORM43 = '5E      '
+TTYPE43 = 'M_e1    '           / Adaptive E1 shape measure
+TUNIT43 = 'unitless'          
+TFORM44 = '5E      '
+TTYPE44 = 'M_e2    '           / Adaptive E2 shape measure
+TUNIT44 = 'unitless'          
+TFORM45 = '5E      '
+TTYPE45 = 'M_e1e1Err'          / Covariance in E1-E1 shape measure
+TUNIT45 = 'unitless'          
+TFORM46 = '5E      '
+TTYPE46 = 'M_e1e2Err'          / Covariance in E1-E2 shape measure
+TUNIT46 = 'unitless'          
+TFORM47 = '5E      '
+TTYPE47 = 'M_e2e2Err'          / Covariance in E2-E2 shape measure
+TUNIT47 = 'unitless'          
+TFORM48 = '5E      '
+TTYPE48 = 'M_rr_cc '           / Adaptive ( + )
+TUNIT48 = 'unitless'          
+TFORM49 = '5E      '
+TTYPE49 = 'M_rr_ccErr'         / Error in adaptive ( + )
+TUNIT49 = 'unitless'          
+TFORM50 = '5E      '
+TTYPE50 = 'M_cr4   '           / Adaptive fourth moment
+TUNIT50 = 'unitless'          
+TFORM51 = '5E      '
+TTYPE51 = 'M_e1_psf'           / Adaptive E1 for PSF
+TUNIT51 = 'unitless'          
+TFORM52 = '5E      '
+TTYPE52 = 'M_e2_psf'           / Adaptive E2 for PSF
+TUNIT52 = 'unitless'          
+TFORM53 = '5E      '
+TTYPE53 = 'M_rr_cc_psf'        / Adaptive () for PSF
+TUNIT53 = 'unitless'          
+TFORM54 = '5E      '
+TTYPE54 = 'M_cr4_psf'          / Adaptive fourth moment for PSF
+TUNIT54 = 'unitless'          
+TFORM55 = '5E      '          
+TTYPE55 = 'iso_rowc'           / Isophotal row centroid
+TUNIT55 = 'pixels  '          
+TFORM56 = '5E      '          
+TTYPE56 = 'iso_rowcErr'        / Isophotal row centroid error
+TUNIT56 = 'pixels  '          
+TFORM57 = '5E      '          
+TTYPE57 = 'iso_rowcGrad'       / Gradient in row centroid with isophote
+TUNIT57 = 'pixels/mag/arcsec^2'
+TFORM58 = '5E      '          
+TTYPE58 = 'iso_colc'           / Isophotal column centroid
+TUNIT58 = 'pixels  '          
+TFORM59 = '5E      '          
+TTYPE59 = 'iso_colcErr'        / Isophotal column centroid error
+TUNIT59 = 'pixels  '          
+TFORM60 = '5E      '          
+TTYPE60 = 'iso_colcGrad'       / Gradient in column centroid with isophote
+TUNIT60 = 'pixels/mag/arcsec^2'
+TFORM61 = '5E      '          
+TTYPE61 = 'iso_a   '           / Isophotal major axis
+TUNIT61 = 'arcsec  '          
+TFORM62 = '5E      '          
+TTYPE62 = 'iso_aErr'           / Isophotal major axis error
+TUNIT62 = 'arcsec  '          
+TFORM63 = '5E      '          
+TTYPE63 = 'iso_aGrad'          / Gradient in major axis with isophote
+TUNIT63 = 'arcsec/mag/arcsec^2'
+TFORM64 = '5E      '          
+TTYPE64 = 'iso_b   '           / Isophotal minor axis
+TUNIT64 = 'arcsec  '          
+TFORM65 = '5E      '          
+TTYPE65 = 'iso_bErr'           / Isophotal minor axis error
+TUNIT65 = 'arcsec  '          
+TFORM66 = '5E      '          
+TTYPE66 = 'iso_bGrad'          / Gradient in minor axis with isophote
+TUNIT66 = 'arcsec/mag/arcsec^2'
+TFORM67 = '5E      '          
+TTYPE67 = 'iso_phi '           / Isophotal position angle
+TUNIT67 = 'degrees (+N thru E)'          
+TFORM68 = '5E      '          
+TTYPE68 = 'iso_phiErr'         / Isophotal position angle error
+TUNIT68 = 'degrees '          
+TFORM69 = '5E      '          
+TTYPE69 = 'iso_phiGrad'        / Gradient in positional angle with isophote
+TUNIT69 = 'degrees/mag/arcsec^2'
+TFORM70 = '5E      '          
+TTYPE70 = 'r_deV   '           / De Vaucouleurs fit scale radius
+TUNIT70 = 'arcsec  '          
+TFORM71 = '5E      '          
+TTYPE71 = 'r_deVErr'           / De Vaucouleurs fit scale radius error
+TUNIT71 = 'arcsec  '          
+TFORM72 = '5E      '          
+TTYPE72 = 'ab_deV  '           / De Vaucouleurs fit a/b
+TUNIT72 = 'unitless'          
+TFORM73 = '5E      '          
+TTYPE73 = 'ab_deVErr'          / De Vaucouleurs fit a/b error
+TUNIT73 = 'unitless'          
+TFORM74 = '5E      '          
+TTYPE74 = 'phi_deV '           / De Vaucouleurs fit position angle
+TUNIT74 = 'degrees (+N thru E)'          
+TFORM75 = '5E      '          
+TTYPE75 = 'phi_deVErr'         / De Vaucouleurs fit position angle error
+TUNIT75 = 'degrees '          
+TFORM76 = '5E      '          
+TTYPE76 = 'counts_deV'         / De Vaucouleurs magnitude fit
+TUNIT76 = 'asinh mag '          
+TFORM77 = '5E      '          
+TTYPE77 = 'counts_deVErr'      / De Vaucouleurs magnitude fit error
+TUNIT77 = 'asinh mag '          
+TFORM78 = '5E      '          
+TTYPE78 = 'r_exp   '           / Exponetial fit scale radius
+TUNIT78 = 'arcsec  '          
+TFORM79 = '5E      '          
+TTYPE79 = 'r_expErr'           / Exponetial fit scale radius error
+TUNIT79 = 'arcsec '          
+TFORM80 = '5E      '          
+TTYPE80 = 'ab_exp  '           / Exponential fit a/b
+TUNIT80 = 'unitless'          
+TFORM81 = '5E      '          
+TTYPE81 = 'ab_expErr'          / Exponential fit a/b error
+TUNIT81 = 'unitless'          
+TFORM82 = '5E      '          
+TTYPE82 = 'phi_exp '           / Exponential fit position angle
+TUNIT82 = 'degrees (+N thru E)'          
+TFORM83 = '5E      '          
+TTYPE83 = 'phi_expErr'         / Exponential fit position angle error
+TUNIT83 = 'degrees '          
+TFORM84 = '5E      '          
+TTYPE84 = 'counts_exp'         / Exponential fit
+TUNIT84 = 'asinh mag '          
+TFORM85 = '5E      '          
+TTYPE85 = 'counts_expErr'      / Exponential fit error
+TUNIT85 = 'asinh mag '          
+TFORM86 = '5E      '          
+TTYPE86 = 'counts_model'       / Better of DeV/Exp magnitude fit
+TUNIT86 = 'asinh mag '          
+TFORM87 = '5E      '          
+TTYPE87 = 'counts_modelErr'    / Better of DeV/Exp magnitude fit error
+TUNIT87 = 'asinh mag '          
+TFORM88 = '5E      '          
+TTYPE88 = 'texture '           / Texture parameter
+TUNIT88 = 'unitless'          
+TFORM89 = '5E      '          
+TTYPE89 = 'star_L  '           / Star likelihood
+TUNIT89 = 'unitless'          
+TFORM90 = '5E      '          
+TTYPE90 = 'star_lnL'           / Star ln(likelihood)
+TUNIT90 = 'unitless'          
+TFORM91 = '5E      '          
+TTYPE91 = 'exp_L   '           / Exponential disk fit likelihood
+TUNIT91 = 'unitless'          
+TFORM92 = '5E      '          
+TTYPE92 = 'exp_lnL '           / Exponential disk fit ln(likelihood)
+TUNIT92 = 'unitless'          
+TFORM93 = '5E      '          
+TTYPE93 = 'deV_L   '           / De Vaucouleurs fit likelihood
+TUNIT93 = 'unitless'          
+TFORM94 = '5E      '          
+TTYPE94 = 'deV_lnL '           / De Vaucouleurs fit ln(likelihood)
+TUNIT94 = 'unitless'          
+TFORM95 = '5E      '          
+TTYPE95 = 'fracPSF '           / Fraction of light in PSF
+TUNIT95 = 'unitless'          
+TFORM96 = '5J      '          
+TTYPE96 = 'flags   '           / Object flags
+TUNIT96 = 'unitless'          
+TFORM97 = '5J      '          
+TTYPE97 = 'flags2  '           / More object flags
+TUNIT97 = 'unitless'          
+TFORM98 = '5J      '          
+TTYPE98 = 'type    '           / Object type
+TUNIT98 = 'unitless'          
+TFORM99 = '5E      '
+TTYPE99 = 'prob_psf'           / Probablity object is a star in each filter
+TUNIT99 = 'unitless'          
+TFORM100= '5J      '          
+TTYPE100= 'nprof   '           / Number of radial bins
+TUNIT100= 'unitless'          
+TFORM101= '75E     '          
+TTYPE101= 'profMean'           / Mean pixel flux in annulus
+TUNIT101= 'maggies/arcsec^2'         
+TDIM101 = '(15,5)  '          
+TFORM102= '75E     '          
+TTYPE102= 'profErr '           / Standard deviation of profMean
+TUNIT102= 'maggies/arcsec^2'        
+TDIM102 = '(15,5)  '          
+TFORM103= '1J      '          
+TTYPE103= 'status  '           / Survey status
+TUNIT103= 'unitless'          
+TFORM104= '1D      '          
+TTYPE104= 'ra      '           / J2000 right ascension (r')
+TUNIT104= 'degrees '          
+TFORM105= '1D      '          
+TTYPE105= 'dec     '           / J2000 declination (r')
+TUNIT105= 'degrees '          
+TFORM106= '1D      '          
+TTYPE106= 'lambda  '           / Survey longitude (r')
+TUNIT106= 'degrees '          
+TFORM107= '1D      '          
+TTYPE107= 'eta     '           / Survey latitude (r')
+TUNIT107= 'degrees '          
+TFORM108= '1D      '          
+TTYPE108= 'l       '           / Galactic longitude (lII, r')
+TUNIT108= 'degrees '          
+TFORM109= '1D      '          
+TTYPE109= 'b       '           / Galactic latitude (bII, r')
+TUNIT109= 'degrees '          
+TFORM110= '5E      '          
+TTYPE110= 'offsetRa'           / Filter position ra minus final ra (* cos(dec))
+TUNIT110= 'arcsec  '          
+TFORM111= '5E      '          
+TTYPE111= 'offsetDec'          / Filter position dec minus final dec
+TUNIT111= 'arcsec  '          
+TFORM112= '1J      '
+TTYPE112= 'primTarget'         / Target selection flags
+TUNIT112= 'unitless'          
+TFORM113= '1J      '
+TTYPE113= 'secTarget'          / Additional Target selection flags
+TUNIT113= 'unitless'          
+TFORM114= '5E      '
+TTYPE114= 'reddening'          / Reddening in each filter
+TUNIT114= 'mag   '
+TFORM115= '1J      '
+TTYPE115= 'propermotionmatch'  / 1= match to USNO catatalog
+TUNIT115= 'unitless'          
+TFORM116= '1E      '
+TTYPE116= 'propermotiondelta'  / Difference in positions between USNO and
+COMMENT                        / photo object (r' band)
+TUNIT116= 'arcsec'
+TFORM117= '1E      '
+TTYPE117= 'propermotion'       / Proper motion of object
+TUNIT117= 'arcsec/century'
+TFORM118= '1E      '
+TTYPE118= 'propermotionangle'  / Direction of proper motion
+TUNIT118= 'degrees (+N thru E)'
+TFORM119= '1E      '
+TTYPE119= 'usnoBlue'           / Blue magntiude of matching USNO object
+TUNIT119= 'mag     '
+TFORM120= '1E      '
+TTYPE120= 'usnoRed '           / Red magntiude of matching USNO object
+TUNIT120= 'mag     '
+TFORM121= '1J      '
+TTYPE121= 'firstMatch'         / Number of FIRST sources matched (0=no match)
+TUNIT121= 'unitless'          
+TFORM122= '1J      '
+TTYPE122= 'firstId '           / FIRST catalog id of one matching source
+TUNIT122= 'unitless'          
+TFORM123= '1D      '
+TTYPE123= 'firstLambda'        / Composite radio source survey longitude
+TUNIT123= 'degrees '
+TFORM124= '1D      '
+TTYPE124= 'firstEta'           / Composite radio source survey latitude
+TUNIT124= 'degrees '
+TFORM125= '1E      '
+TTYPE125= 'firstDelta'         / Position difference between first and photo object (r' band)
+TUNIT125= 'arcsec'
+TFORM126= '1E      '
+TTYPE126= 'firstPeak'          / Peak first radio flux
+TUNIT126= 'mJy'
+TFORM127= '1E      '
+TTYPE127= 'firstInt'           / Integrated first radio flux
+TUNIT127= 'mJy'
+TFORM128= '1E      '
+TTYPE128= 'firstRms'           / Rms error in flux
+TUNIT128= 'mJy'
+TFORM129= '1E      '
+TTYPE129= 'firstMajor'         / Major axis (deconvolved)
+TUNIT129= 'arcsec'
+TFORM130= '1E      '
+TTYPE130= 'firstMinor'         / Minor axis (deconvolved)
+TUNIT130= 'arcsec'
+TFORM131= '1E      '
+TTYPE131= 'firstPa'            / Position angle (east of north)
+TUNIT131= 'degrees'
+TFORM132= '1J      '
+TTYPE132= 'rosatMatch'         / ID of matching ROSAT source (0 = no match)
+TUNIT132= 'unitless'          
+TFORM133= '1E      '
+TTYPE133= 'rosatDelta'         / Position difference between ROSAT and photo object (r' band)
+TUNIT133= 'arcsec'
+TFORM134= '1E      '                                                           
+TTYPE134= 'rosatPosErr'        / Error in ROSAT position
+TUNIT134= 'arcsec '
+TFORM135= '1E      '
+TTYPE135= 'rosatCps'           / Integrated ROSAT counts
+TUNIT135= 'counts/sec'
+TFORM136= '1E      '
+TTYPE136= 'rosatCpsErr'        / Error in integrated ROSAT counts
+TUNIT136= 'counts/sec'
+TFORM137= '1E      '
+TTYPE137= 'rosatHr1'           / ROSAT hardness ratio 1
+TUNIT137= 'unitless'          
+TFORM138= '1E      '
+TTYPE138= 'rosatHr1Err'        / Error in ROSAT hardness ratio 1
+TUNIT138= 'unitless'          
+TFORM139= '1E      '
+TTYPE139= 'rosatHr2'           / ROSAT hardness ratio 2
+TUNIT139= 'unitless'          
+TFORM140= '1E      '
+TTYPE140= 'rosatHr2Err'        / Error in ROSAT hardness ratio 2
+TUNIT140= 'unitless'          
+TFORM141= '1E      '
+TTYPE141= 'rosatExt'           / ROSAT extent parameter
+TUNIT141= 'arcsec'
+TFORM142= '1E      '
+TTYPE142= 'rosatExtLike'       / ROSAT extent parameter likelihood
+TUNIT142= 'unitless'          
+TFORM143= '1E      '
+TTYPE143= 'rosatDetectLike'    / ROSAT detection likelihood
+TUNIT143= 'unitless'          
+TFORM144= '1E      '
+TTYPE144= 'rosatExposure'      / ROSAT exposure time
+TUNIT144= 'seconds '
+TFORM145= '1J      '
+TTYPE145= 'priority'           / Priority bits
+TSCAL145= 1.00000000000000E+00
+TZERO145= 2.14748364800000E+09
+TUNIT145= 'unitless'          
+TFORM146= '50J     '
+TTYPE146= 'matchid '           / Link ids (run,camCol,rerun,field,id)
+TUNIT146= 'unitless'          
+TDIM146 = '(5,10)  '
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/sdss.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/sdss.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/doc/sdss.txt	(revision 22322)
@@ -0,0 +1,60 @@
+
+adding SDSS object tables:
+
+* autodetect the SDSS files from the header?
+* do I need to read multiple files for a single data set?
+* how are we going to determine photcodes?
+  * do we need to set the photcode on a per-object basis in LoadData or equivalent?
+
+* some re-org might be needed for LoadStars
+
+
+** fields to keep:
+
+name          sdss 
+ X            'objc_colc' (or colc)
+ Y            'objc_rowc' (or rowc)
+ dX           'objc_colcErr' (or colcErr)
+ dY           'objc_rowcErr' (or rowcErr)
+ R            ra  + offset Ra (get cos des right)
+ D            dec + offset Dec
+ dR           (calculate from dX * plate scale?)
+ dD           (calculate from dX * plate scale?)
+ uR           (from 'propermotion' and 'propermotionangle')
+ uD           (from 'propermotion' and 'propermotionangle')
+ duR          ?
+ duD          ?
+ P            X
+ dP           X
+ M            psfCounts (is already a mag?)
+ dM           psfCountsErr (is already a mag?)
+ sky          sky (convert to flux?)
+ dsky         skyErr (convert to flux?)
+ fx           ?
+ fy           ?
+ df           ?
+ Mgal         X
+ Map          X
+ Mpeak        X
+ detID        X
+ found        -1
+ t            MJD_U + Y*rate
+ dt           fixed? NY*rate
+ psfChisq     
+ crNsigma     
+ extNsigma    
+ psfQual      
+ Mcal         
+ airmass      
+ az            
+ code         
+ nFrames      
+ flags        
+ dophot       
+ dummy        
+
+we can use these header fields to get the exp times, and I suppose alt, az, airmass, etc:
+NODE    =           XXX.XXXXXX / Ascending node of great circle scanned (deg)
+INCL    =           XXX.XXXXXX / Inclincation of great circle scanned (deg)
+C_OBS   =            XXXXX.XXX / CCD clock rate (usec/unbinned-row)
+TRACKING=            XXXXX.XXX / Tracking rate (arcsec/TAI-sec)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/etc/phottemp.cat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/etc/phottemp.cat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/etc/phottemp.cat	(revision 22322)
@@ -0,0 +1,296 @@
+SIMPLE  =                    F  / ASTROCAM PHOTOMETRY FILE                    \
+BITPIX  =                   16 /                                              \
+NAXIS   =                    2  / NUMBER OF AXES                              \
+NAXIS1  =                 1106  / NUMBER OF COLUMNS                           \
+NAXIS2  =                 1024  / NUMBER OF ROWS                              \
+BSCALE  =             1.000000 /                                              \
+BZERO   =             0.000000 /                                              \
+DATE    = '19/12/94'            / UT Date of file creation (DD/MM/YY)         \
+ORIGIN  = 'MDM Observatory'     / Michigan-Dartmouth-MIT                      \
+LATITUDE=              31.9500  / Latitude (degrees N)                        \
+LONGITUD=            -111.6150  / Longitude (degrees E)                       \
+OBSERVER= 'Metzger '            / Name of observer                            \
+TELESCOP= '1.3m McGraw-Hill'    / Telescope used for observation              \
+INSTRUME= 'Charlotte Direct'    / Instrument used for observation             \
+DETECTOR= 'Charlotte/Tek 1024^2 CCD'  / Detector used for observation         \
+FRAME   =                   32  / Frame number of observation                 \
+CCDPICNO=                   32  / Frame number of observation                 \
+OBJECT  = 'ocl0327 '            / Name of object                              \
+IMAGETYP= 'OBJECT  '            / Type of observation                         \
+EXPTIME =              250.000  / Integration time (seconds)                  \
+DARKTIME=              250.067  / Dark current time (seconds)                 \
+DATE-OBS= '19/12/94'            / UT Date of observation (DD/MM/YY)           \
+UT      = ' 05:25:44.00'        / Universal time (UTC) at exposure start      \
+JD      =       2449705.726204                                                \
+RA      =           50.700      / Right Ascension                             \
+DEC     =           89.000      / Declination                                 \
+DIRECTN =                 -1    / Moving South                                \
+EQUINOX =             1950.000  / Equinox of RA and DEC                       \
+HA      = ' 02:14:06.72'        / Hour angle at start                         \
+ST      = ' 03:49:36.85'        / Sidereal time at start                      \
+ZD      = ' 36:26:07.17'        / Zenith distance (degrees)                   \
+AIRMASS =                1.243  / Airmass at start                            \
+FILTER  = 'I KP    '            / Filter description                          \
+GAIN    =                3.350  / Nominal gain (e-/ADU)                       \
+SECPIX1 =                0.508  / Arcseconds per pixel in fast dir            \
+SECPIX2 =                0.508  / Arcseconds per pixel in slow dir            \
+CCDBIN1 =                    1  / On-chip column binning (fast dir)           \
+CCDBIN2 =                    1  / On-chip row binning (slow dir)              \
+GPROBE  = '  5000.00   7000.00'  / Guide probe X Y                            \
+DATASEC = '[51:1074,1:1024]'    / Image area of frame                         \
+CCDSEC  = '[1:1074,1:1024]'     / Image area relative to full chip            \
+BIASSEC = '[1080:1106,1:1023]'  / Overscan area of frame                      \
+UNSIGN  =                    T /                                              \
+NSTARS  =                    0  / NUMBER OF stars                             \
+CTYPE1  = 'RA---SIN          ' /                                               
+CTYPE2  = 'DEC--SIN          ' /                                               
+CDELT1  =             0.000733 /                                               
+CDELT2  =             0.000733 /                                               
+CRVAL1  =            50.191418 /                                               
+CRVAL2  =            88.998663 /                                               
+CRPIX1  =          1028.644173 /                                               
+CRPIX2  =           503.129830 /                                               
+PC001001=             0.999906 /                                               
+PC001002=             0.012081 /                                               
+PC002001=            -0.011936 /                                               
+PC002002=             0.999950 /                                               
+END                                                                           \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+ 246.5  352.1 13.000 020 1 3.2
+ 246.5  352.1 14.100 020 1 3.2
+ 279.4  328.9 13.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 436.6  413.5 14.300 020 1 3.2
+   9.3  856.5 13.000 020 1 3.2
+   9.3  856.2 13.600 020 1 3.2
+ 319.7  697.9 12.000 020 1 3.2
+ 319.7  697.7 12.100 020 1 3.2
+ 244.3    9.5 14.100 020 1 3.2
+ 284.7  334.4 14.000 020 1 3.2
+ 127.1  122.8 14.200 020 1 3.2
+ 140.2  791.1 13.700 020 1 3.2
+ 140.2  790.8 14.400 020 1 3.2
+ 131.7  636.3 13.800 020 1 3.2
+ 423.8  700.6 13.800 020 1 3.2
+ 423.7  700.7 12.500 020 1 3.2
+ 266.2  290.7 13.800 020 1 3.2
+ 266.0  290.7 13.100 020 1 3.2
+ 570.7  721.6 13.600 020 1 3.2
+ 570.7  721.6 13.100 020 1 3.2
+ 548.7  673.0 13.700 020 1 3.2
+ 264.2  219.7 12.800 020 1 3.2
+ 264.0  219.7 12.100 020 1 3.2
+ 161.9  797.4 11.900 020 1 3.2
+ 162.0  797.2 12.200 020 1 3.2
+ 505.8  851.8 13.700 020 1 3.2
+  88.2  419.0 11.400 020 1 3.2
+  88.2  419.0 11.800 020 1 3.2
+ 426.9  613.2  9.400 020 1 3.2
+ 427.4  613.5  7.600 020 1 3.2
+ 326.1  595.2 12.100 020 1 3.2
+ 326.2  595.1 12.600 020 1 3.2
+ 444.4  845.2 14.200 020 1 3.2
+ 254.1  916.8 14.500 020 1 3.2
+ 502.6  882.4 14.200 020 1 3.2
+ 128.3  275.7 12.300 020 1 3.2
+ 128.3  275.7 12.900 020 1 3.2
+ 230.9  554.8 13.300 020 1 3.2
+ 230.9  554.7 14.200 020 1 3.2
+  47.5  685.2 13.500 020 1 3.2
+  47.5  684.9 14.200 020 1 3.2
+ 382.6  658.7 14.000 020 1 3.2
+ 182.2  330.3 14.200 020 1 3.2
+ 245.1  548.3 12.600 020 1 3.2
+ 245.3  548.0 13.200 020 1 3.2
+  50.3  849.0 13.700 020 1 3.2
+  50.5  848.8 14.300 020 1 3.2
+  17.9  772.7 14.200 020 1 3.2
+ 499.8  712.8 13.500 020 1 3.2
+ 499.8  712.6 14.400 020 1 3.2
+ 294.5  349.4 14.000 020 1 3.2
+ 497.7  747.8 11.000 020 1 3.2
+ 497.5  747.8 10.900 020 1 3.2
+ 109.7  323.5 14.000 020 1 3.2
+  59.5  917.6 14.300 020 1 3.2
+  59.4  917.8 13.700 020 1 3.2
+  82.2  880.5 13.800 020 1 3.2
+ 145.1  341.2 14.500 020 1 3.2
+ 452.7  592.4 14.400 020 1 3.2
+ 623.4  820.0 13.700 020 1 3.2
+ 609.7  829.3 13.500 020 1 3.2
+ 481.8  938.3 14.400 020 1 3.2
+ 617.0  792.7 13.500 020 1 3.2
+ 183.2  539.7 14.400 020 1 3.2
+ 133.9  679.6 14.400 020 1 3.2
+ 423.7  667.8  6.500 020 1 3.2
+ 434.7  111.9 13.200 020 1 3.2
+ 434.5  111.9 13.200 020 1 3.2
+ 434.6  111.9 14.200 020 1 3.2
+1222.3  684.2 13.900 020 1 3.2
+1077.6  282.5 14.000 020 1 3.2
+1077.8  282.5 13.400 020 1 3.2
+ 720.8  562.1 14.100 020 1 3.2
+ 721.1  561.9 14.400 020 1 3.2
+1182.1  944.4 12.100 020 1 3.2
+1181.9  944.4 11.800 020 1 3.2
+1181.9  944.4 12.300 020 1 3.2
+ 761.2  423.1 14.300 020 1 3.2
+ 761.0  423.1 14.100 020 1 3.2
+ 926.7  436.8 13.400 020 1 3.2
+ 926.6  436.8 14.200 020 1 3.2
+1209.1   45.0 13.200 020 1 3.2
+1209.2   43.6 12.800 020 1 3.2
+1135.2  653.1  8.100 020 1 3.2
+1135.6  653.4  8.200 020 1 3.2
+1123.5  735.8 12.000 020 1 3.2
+1123.6  735.8 11.800 020 1 3.2
+1123.7  735.7 11.600 020 1 3.2
+ 730.7   28.6 12.100 020 1 3.2
+ 730.7   28.6 12.200 020 1 3.2
+ 783.1  267.5 14.200 020 1 3.2
+ 783.2  267.5 13.400 020 1 3.2
+ 876.2  462.7 13.800 020 1 3.2
+ 876.4  462.7 14.200 020 1 3.2
+ 917.2  399.9 13.700 020 1 3.2
+ 917.3  399.9 13.200 020 1 3.2
+1227.5  218.4 10.400 020 1 3.2
+1227.7  218.4 10.500 020 1 3.2
+1022.7  146.0 13.300 020 1 3.2
+1022.5  146.0 14.100 020 1 3.2
+1324.7   72.3 12.900 020 1 3.2
+1324.6   72.3 13.200 020 1 3.2
+ 743.2  379.4 14.300 020 1 3.2
+ 742.9  379.4 13.900 020 1 3.2
+1255.9  544.0 12.600 020 1 3.2
+1256.0  543.9 12.300 020 1 3.2
+ 848.9  251.1 13.200 020 1 3.2
+ 849.0  251.1 12.700 020 1 3.2
+ 495.6  147.4 11.400 020 1 3.2
+ 495.6  147.4 11.300 020 1 3.2
+ 495.5  147.4 11.000 020 1 3.2
+1016.4  735.5 14.200 020 1 3.2
+1016.3  735.7 13.900 020 1 3.2
+ 795.2  675.9 14.400 020 1 3.2
+ 887.2  968.8 10.800 020 1 3.2
+ 626.8  726.9 14.400 020 1 3.2
+ 718.6  800.1  9.700 020 1 3.2
+ 675.4  301.6 14.400 020 1 3.2
+ 801.6  436.8 14.200 020 1 3.2
+1066.4  870.0 13.700 020 1 3.2
+1066.5  869.9 13.800 020 1 3.2
+ 644.5  662.9  9.900 020 1 3.2
+ 639.1  760.4 13.000 020 1 3.2
+ 822.7  663.1 12.800 020 1 3.2
+ 822.7  663.1 13.100 020 1 3.2
+ 677.2  716.2 12.900 020 1 3.2
+ 765.6  641.5 13.200 020 1 3.2
+ 765.7  641.4 13.500 020 1 3.2
+ 998.3  425.8 14.400 020 1 3.2
+ 713.7  716.2 13.800 020 1 3.2
+ 814.7  887.6  2.000 020 1 3.2
+ 808.7  884.1  8.200 020 1 3.2
+1812.3  259.3 14.500 020 1 3.2
+1655.9  655.4 12.200 020 1 3.2
+1655.9  655.4 12.100 020 1 3.2
+1577.2  839.6 11.500 020 1 3.2
+1577.2  839.6 11.400 020 1 3.2
+1643.7  597.0 14.100 020 1 3.2
+1552.8  251.1 10.100 020 1 3.2
+1552.8  251.1 10.300 020 1 3.2
+1410.0  999.9 13.900 020 1 3.2
+1335.6  687.0 13.000 020 1 3.2
+1335.8  686.8 12.700 020 1 3.2
+1471.4  262.0 14.200 020 1 3.2
+1479.6  608.3 14.200 020 1 3.2
+1478.1  211.5 12.000 020 1 3.2
+1478.3  211.5 12.000 020 1 3.2
+1552.5   46.4 14.500 020 1 3.2
+2042.4  548.3 10.800 020 1 3.2
+2042.6  548.3 11.000 020 1 3.2
+1448.0  915.7 14.100 020 1 3.2
+1462.3  989.2 13.000 020 1 3.2
+1462.3  989.0 12.700 020 1 3.2
+1316.9  787.3 14.100 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1677.2  106.4 13.100 020 1 3.2
+1677.0  106.4 13.300 020 1 3.2
+1324.2  429.9  9.200 020 1 3.2
+1324.2  429.9  9.000 020 1 3.2
+1462.8  214.3 13.700 020 1 3.2
+1462.8  214.3 14.200 020 1 3.2
+1405.4   95.5 11.700 020 1 3.2
+1405.2   95.5 11.600 020 1 3.2
+1604.8   90.0 11.200 020 1 3.2
+1604.8   90.0 11.300 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1459.8  473.6 13.400 020 1 3.2
+1459.7  473.6 13.800 020 1 3.2
+1438.7   77.8 12.200 020 1 3.2
+1438.6   77.8 12.200 020 1 3.2
+1355.0  114.6 13.000 020 1 3.2
+1354.9  114.6 13.200 020 1 3.2
+1941.4  940.7 13.000 020 1 3.2
+1941.6  940.7 12.900 020 1 3.2
+1994.0  800.8 13.900 020 1 3.2
+1993.8  800.8 14.000 020 1 3.2
+2024.4  907.0 13.700 020 1 3.2
+2024.4  907.0 13.800 020 1 3.2
+2035.7  881.6 13.900 020 1 3.2
+2035.9  881.6 13.800 020 1 3.2
+1994.0  924.2 12.100 020 1 3.2
+1994.0  924.2 12.000 020 1 3.2
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/etc/template.cat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/etc/template.cat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/etc/template.cat	(revision 22322)
@@ -0,0 +1,296 @@
+SIMPLE  =                    F  / ASTROCAM PHOTOMETRY FILE                    \
+BITPIX  =                   16 /                                              \
+NAXIS   =                    2  / NUMBER OF AXES                              \
+NAXIS1  =                 1106  / NUMBER OF COLUMNS                           \
+NAXIS2  =                 1024  / NUMBER OF ROWS                              \
+BSCALE  =             1.000000 /                                              \
+BZERO   =             0.000000 /                                              \
+DATE    = '19/12/94'            / UT Date of file creation (DD/MM/YY)         \
+ORIGIN  = 'MDM Observatory'     / Michigan-Dartmouth-MIT                      \
+LATITUDE=              31.9500  / Latitude (degrees N)                        \
+LONGITUD=            -111.6150  / Longitude (degrees E)                       \
+OBSERVER= 'Metzger '            / Name of observer                            \
+TELESCOP= '1.3m McGraw-Hill'    / Telescope used for observation              \
+INSTRUME= 'Charlotte Direct'    / Instrument used for observation             \
+DETECTOR= 'Charlotte/Tek 1024^2 CCD'  / Detector used for observation         \
+FRAME   =                   32  / Frame number of observation                 \
+CCDPICNO=                   32  / Frame number of observation                 \
+OBJECT  = 'ocl0327 '            / Name of object                              \
+IMAGETYP= 'OBJECT  '            / Type of observation                         \
+EXPTIME =              250.000  / Integration time (seconds)                  \
+DARKTIME=              250.067  / Dark current time (seconds)                 \
+DATE-OBS= '19/12/94'            / UT Date of observation (DD/MM/YY)           \
+UT      = ' 05:25:44.00'        / Universal time (UTC) at exposure start      \
+JD      =       2449705.726204                                                \
+RA      =           50.700      / Right Ascension                             \
+DEC     =           89.000      / Declination                                 \
+DIRECTN =                 -1    / Moving South                                \
+EQUINOX =             1950.000  / Equinox of RA and DEC                       \
+HA      = ' 02:14:06.72'        / Hour angle at start                         \
+ST      = ' 03:49:36.85'        / Sidereal time at start                      \
+ZD      = ' 36:26:07.17'        / Zenith distance (degrees)                   \
+AIRMASS =                1.243  / Airmass at start                            \
+FILTER  = 'I KP    '            / Filter description                          \
+GAIN    =                3.350  / Nominal gain (e-/ADU)                       \
+SECPIX1 =                0.508  / Arcseconds per pixel in fast dir            \
+SECPIX2 =                0.508  / Arcseconds per pixel in slow dir            \
+CCDBIN1 =                    1  / On-chip column binning (fast dir)           \
+CCDBIN2 =                    1  / On-chip row binning (slow dir)              \
+GPROBE  = '  5000.00   7000.00'  / Guide probe X Y                            \
+DATASEC = '[51:1074,1:1024]'    / Image area of frame                         \
+CCDSEC  = '[1:1074,1:1024]'     / Image area relative to full chip            \
+BIASSEC = '[1080:1106,1:1023]'  / Overscan area of frame                      \
+UNSIGN  =                    T /                                              \
+NSTARS  =                    0  / NUMBER OF stars                             \
+CTYPE1  = 'RA---SIN          ' /                                               
+CTYPE2  = 'DEC--SIN          ' /                                               
+CDELT1  =             0.000733 /                                               
+CDELT2  =             0.000733 /                                               
+CRVAL1  =            50.191418 /                                               
+CRVAL2  =            88.998663 /                                               
+CRPIX1  =          1028.644173 /                                               
+CRPIX2  =           503.129830 /                                               
+PC001001=             0.999906 /                                               
+PC001002=             0.012081 /                                               
+PC002001=            -0.011936 /                                               
+PC002002=             0.999950 /                                               
+END                                                                           \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+ 246.5  352.1 13.000 020 1 3.2
+ 246.5  352.1 14.100 020 1 3.2
+ 279.4  328.9 13.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 436.6  413.5 14.300 020 1 3.2
+   9.3  856.5 13.000 020 1 3.2
+   9.3  856.2 13.600 020 1 3.2
+ 319.7  697.9 12.000 020 1 3.2
+ 319.7  697.7 12.100 020 1 3.2
+ 244.3    9.5 14.100 020 1 3.2
+ 284.7  334.4 14.000 020 1 3.2
+ 127.1  122.8 14.200 020 1 3.2
+ 140.2  791.1 13.700 020 1 3.2
+ 140.2  790.8 14.400 020 1 3.2
+ 131.7  636.3 13.800 020 1 3.2
+ 423.8  700.6 13.800 020 1 3.2
+ 423.7  700.7 12.500 020 1 3.2
+ 266.2  290.7 13.800 020 1 3.2
+ 266.0  290.7 13.100 020 1 3.2
+ 570.7  721.6 13.600 020 1 3.2
+ 570.7  721.6 13.100 020 1 3.2
+ 548.7  673.0 13.700 020 1 3.2
+ 264.2  219.7 12.800 020 1 3.2
+ 264.0  219.7 12.100 020 1 3.2
+ 161.9  797.4 11.900 020 1 3.2
+ 162.0  797.2 12.200 020 1 3.2
+ 505.8  851.8 13.700 020 1 3.2
+  88.2  419.0 11.400 020 1 3.2
+  88.2  419.0 11.800 020 1 3.2
+ 426.9  613.2  9.400 020 1 3.2
+ 427.4  613.5  7.600 020 1 3.2
+ 326.1  595.2 12.100 020 1 3.2
+ 326.2  595.1 12.600 020 1 3.2
+ 444.4  845.2 14.200 020 1 3.2
+ 254.1  916.8 14.500 020 1 3.2
+ 502.6  882.4 14.200 020 1 3.2
+ 128.3  275.7 12.300 020 1 3.2
+ 128.3  275.7 12.900 020 1 3.2
+ 230.9  554.8 13.300 020 1 3.2
+ 230.9  554.7 14.200 020 1 3.2
+  47.5  685.2 13.500 020 1 3.2
+  47.5  684.9 14.200 020 1 3.2
+ 382.6  658.7 14.000 020 1 3.2
+ 182.2  330.3 14.200 020 1 3.2
+ 245.1  548.3 12.600 020 1 3.2
+ 245.3  548.0 13.200 020 1 3.2
+  50.3  849.0 13.700 020 1 3.2
+  50.5  848.8 14.300 020 1 3.2
+  17.9  772.7 14.200 020 1 3.2
+ 499.8  712.8 13.500 020 1 3.2
+ 499.8  712.6 14.400 020 1 3.2
+ 294.5  349.4 14.000 020 1 3.2
+ 497.7  747.8 11.000 020 1 3.2
+ 497.5  747.8 10.900 020 1 3.2
+ 109.7  323.5 14.000 020 1 3.2
+  59.5  917.6 14.300 020 1 3.2
+  59.4  917.8 13.700 020 1 3.2
+  82.2  880.5 13.800 020 1 3.2
+ 145.1  341.2 14.500 020 1 3.2
+ 452.7  592.4 14.400 020 1 3.2
+ 623.4  820.0 13.700 020 1 3.2
+ 609.7  829.3 13.500 020 1 3.2
+ 481.8  938.3 14.400 020 1 3.2
+ 617.0  792.7 13.500 020 1 3.2
+ 183.2  539.7 14.400 020 1 3.2
+ 133.9  679.6 14.400 020 1 3.2
+ 423.7  667.8  6.500 020 1 3.2
+ 434.7  111.9 13.200 020 1 3.2
+ 434.5  111.9 13.200 020 1 3.2
+ 434.6  111.9 14.200 020 1 3.2
+1222.3  684.2 13.900 020 1 3.2
+1077.6  282.5 14.000 020 1 3.2
+1077.8  282.5 13.400 020 1 3.2
+ 720.8  562.1 14.100 020 1 3.2
+ 721.1  561.9 14.400 020 1 3.2
+1182.1  944.4 12.100 020 1 3.2
+1181.9  944.4 11.800 020 1 3.2
+1181.9  944.4 12.300 020 1 3.2
+ 761.2  423.1 14.300 020 1 3.2
+ 761.0  423.1 14.100 020 1 3.2
+ 926.7  436.8 13.400 020 1 3.2
+ 926.6  436.8 14.200 020 1 3.2
+1209.1   45.0 13.200 020 1 3.2
+1209.2   43.6 12.800 020 1 3.2
+1135.2  653.1  8.100 020 1 3.2
+1135.6  653.4  8.200 020 1 3.2
+1123.5  735.8 12.000 020 1 3.2
+1123.6  735.8 11.800 020 1 3.2
+1123.7  735.7 11.600 020 1 3.2
+ 730.7   28.6 12.100 020 1 3.2
+ 730.7   28.6 12.200 020 1 3.2
+ 783.1  267.5 14.200 020 1 3.2
+ 783.2  267.5 13.400 020 1 3.2
+ 876.2  462.7 13.800 020 1 3.2
+ 876.4  462.7 14.200 020 1 3.2
+ 917.2  399.9 13.700 020 1 3.2
+ 917.3  399.9 13.200 020 1 3.2
+1227.5  218.4 10.400 020 1 3.2
+1227.7  218.4 10.500 020 1 3.2
+1022.7  146.0 13.300 020 1 3.2
+1022.5  146.0 14.100 020 1 3.2
+1324.7   72.3 12.900 020 1 3.2
+1324.6   72.3 13.200 020 1 3.2
+ 743.2  379.4 14.300 020 1 3.2
+ 742.9  379.4 13.900 020 1 3.2
+1255.9  544.0 12.600 020 1 3.2
+1256.0  543.9 12.300 020 1 3.2
+ 848.9  251.1 13.200 020 1 3.2
+ 849.0  251.1 12.700 020 1 3.2
+ 495.6  147.4 11.400 020 1 3.2
+ 495.6  147.4 11.300 020 1 3.2
+ 495.5  147.4 11.000 020 1 3.2
+1016.4  735.5 14.200 020 1 3.2
+1016.3  735.7 13.900 020 1 3.2
+ 795.2  675.9 14.400 020 1 3.2
+ 887.2  968.8 10.800 020 1 3.2
+ 626.8  726.9 14.400 020 1 3.2
+ 718.6  800.1  9.700 020 1 3.2
+ 675.4  301.6 14.400 020 1 3.2
+ 801.6  436.8 14.200 020 1 3.2
+1066.4  870.0 13.700 020 1 3.2
+1066.5  869.9 13.800 020 1 3.2
+ 644.5  662.9  9.900 020 1 3.2
+ 639.1  760.4 13.000 020 1 3.2
+ 822.7  663.1 12.800 020 1 3.2
+ 822.7  663.1 13.100 020 1 3.2
+ 677.2  716.2 12.900 020 1 3.2
+ 765.6  641.5 13.200 020 1 3.2
+ 765.7  641.4 13.500 020 1 3.2
+ 998.3  425.8 14.400 020 1 3.2
+ 713.7  716.2 13.800 020 1 3.2
+ 814.7  887.6  2.000 020 1 3.2
+ 808.7  884.1  8.200 020 1 3.2
+1812.3  259.3 14.500 020 1 3.2
+1655.9  655.4 12.200 020 1 3.2
+1655.9  655.4 12.100 020 1 3.2
+1577.2  839.6 11.500 020 1 3.2
+1577.2  839.6 11.400 020 1 3.2
+1643.7  597.0 14.100 020 1 3.2
+1552.8  251.1 10.100 020 1 3.2
+1552.8  251.1 10.300 020 1 3.2
+1410.0  999.9 13.900 020 1 3.2
+1335.6  687.0 13.000 020 1 3.2
+1335.8  686.8 12.700 020 1 3.2
+1471.4  262.0 14.200 020 1 3.2
+1479.6  608.3 14.200 020 1 3.2
+1478.1  211.5 12.000 020 1 3.2
+1478.3  211.5 12.000 020 1 3.2
+1552.5   46.4 14.500 020 1 3.2
+2042.4  548.3 10.800 020 1 3.2
+2042.6  548.3 11.000 020 1 3.2
+1448.0  915.7 14.100 020 1 3.2
+1462.3  989.2 13.000 020 1 3.2
+1462.3  989.0 12.700 020 1 3.2
+1316.9  787.3 14.100 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1677.2  106.4 13.100 020 1 3.2
+1677.0  106.4 13.300 020 1 3.2
+1324.2  429.9  9.200 020 1 3.2
+1324.2  429.9  9.000 020 1 3.2
+1462.8  214.3 13.700 020 1 3.2
+1462.8  214.3 14.200 020 1 3.2
+1405.4   95.5 11.700 020 1 3.2
+1405.2   95.5 11.600 020 1 3.2
+1604.8   90.0 11.200 020 1 3.2
+1604.8   90.0 11.300 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1459.8  473.6 13.400 020 1 3.2
+1459.7  473.6 13.800 020 1 3.2
+1438.7   77.8 12.200 020 1 3.2
+1438.6   77.8 12.200 020 1 3.2
+1355.0  114.6 13.000 020 1 3.2
+1354.9  114.6 13.200 020 1 3.2
+1941.4  940.7 13.000 020 1 3.2
+1941.6  940.7 12.900 020 1 3.2
+1994.0  800.8 13.900 020 1 3.2
+1993.8  800.8 14.000 020 1 3.2
+2024.4  907.0 13.700 020 1 3.2
+2024.4  907.0 13.800 020 1 3.2
+2035.7  881.6 13.900 020 1 3.2
+2035.9  881.6 13.800 020 1 3.2
+1994.0  924.2 12.100 020 1 3.2
+1994.0  924.2 12.000 020 1 3.2
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/2mass.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/2mass.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/2mass.h	(revision 22322)
@@ -0,0 +1,55 @@
+
+/* structure for data on a catalog region */
+typedef struct {
+  char filename[256];
+  double RA[2];
+  int Nrec;
+} TM_Region;
+
+typedef struct {
+  double Rmin, Rmax, Dmin, Dmax;
+  int index[20];
+  int Nindex;
+} TMBands;
+
+typedef struct {
+  double R, D;
+  int offset;
+  int flag;
+} TMStars;
+
+short TM_J, TM_H, TM_K;
+
+SkyTable *get2mass_acc (SkyRegion *patch, char *path, char *accel);
+Stars    *get2mass_2DR_data (SkyRegion *region, char *filename, SkyRegion *patch, int photcode, int *nstars);
+Stars    *get2mass_AS_data (SkyRegion *region, char *filename, SkyRegion *patch, int phocode, int *nstars);
+Stars    *get2mass_AS_rawdata (SkyRegion *region, char *filename, SkyRegion *patch, int phocode, int *nstars);
+
+SkyTable *scan2mass_acc (char *path, char *accel);
+int       scan2mass_as_data (char *filename);
+
+char     *skipNbounds (char *line, char bound, int Nbound, int Nbyte);
+e_time    get2mass_time (char *ptr, int Nbound, int Nbyte);
+e_time    get2mass_date (char *ptr, int Nbound, int Nmax);
+
+int       load2mass_as_rawdata (SkyTable *skytable, char *filename, AddstarClientOptions options);
+SkyTable *load2mass_acc (char *path, char *accel);
+int       get2mass_3star (Stars *star, char *line, int Nmax);
+int       load2mass_catalog (Catalog *catalog, Stars *stars, int Nstars);
+
+int       get2mass_setup (int photcode);
+int       get2mass_coords (char *line, double *R, double *D, int Nmax);
+int       get2mass_star (Stars *star, char *line, int Nmax);
+int       get2mass_3star (Stars *star, char *line, int Nmax);
+
+int get2mass_3star_full (Stars **star, char *line, int Nmax);
+char *next2MASSfield (char *line);
+int set2MASS_ph_qual (Stars *star, char qual);
+int set2MASS_rd_flag (Stars *star, char qual);
+int set2MASS_cc_flag (Stars *star, char qual);
+int set2MASS_bl_flag (Stars *star, char qual);
+int set2MASS_gal_flag (Stars *star, char qual);
+int set2MASS_mp_flag (Stars *star, char qual);
+int set2MASS_dup_flag (Stars *star, char qual);
+int set2MASS_use_flag (Stars *star, char qual);
+int get2mass_sortStars (TMStars *tstars, int Ntstars);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/addstar.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/addstar.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/addstar.h	(revision 22322)
@@ -0,0 +1,278 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+# include <sys/time.h>
+# include <time.h>
+# include <zlib.h>
+
+/* solaris requires both of these instead of ip.h:
+   # include <sys/socket.h>
+   # include <netinet/in.h>
+*/
+
+/* linux is happy with this, not solaris */
+# include <netinet/ip.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <glob.h>
+
+/* used in find_matches, find_matches_refstars */
+# define IN_REGION(R,D) ( \
+((D) >= region[0].Dmin) && ((D) < region[0].Dmax) && \
+((R) >= region[0].Rmin)  && ((R) < region[0].Rmax))
+
+/* grab named photcode */
+# define NAMED_PHOTCODE(CODE,NAME) \
+  CODE = GetPhotcodeCodebyName (NAME); \
+  if (!CODE) { \
+    fprintf (stderr, "ERROR:  photcode %s not found in photcode table\n", NAME); \
+    exit (0); }
+
+typedef struct {
+  int Nstars;
+  Stars *stars;
+  int Nimages;
+  Image *images;
+  Coords *mosaic;
+  SkyRegion *patch;
+  AddstarClientOptions *options;
+  char *refcat;
+} DVO_DATA;
+
+typedef struct {
+  char *exthead;
+  char *extdata;
+  char *exttype;
+  int extnum_head;
+  int extnum_data;
+} HeaderSet;
+
+typedef struct sockaddr_in SockAddress;
+
+enum {M_IMAGE, M_REFLIST, M_REFCAT, M_FAKEIMAGE, M_RESORT};
+enum {NONE, SIMPLE_CMP, SIMPLE_CMF, SIMPLE_MEF, MOSAIC_CMP, MOSAIC_CMF, MOSAIC_MEF, MOSAIC_PHU, SDSS_OBJ};
+/* note: MEF implies CMF */
+
+/* globals which define database info / data sources (KEEP) */
+char   ImageCat[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char   TWO_MASS_DIR_AS[256];
+char   TWO_MASS_DIR_DR2[256];
+char   GSCDIR[256];
+char   USNO_A_DIR[256];
+char   USNO_B_DIR[256];
+char   TYCHO_DIR[256];
+char   SubpixDatafile[256];
+char   PASSWORD[80];
+char   HOSTNAME[80];
+int    NVALID, *VALID_IP;
+char   SKY_TABLE[256];
+int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+char   CameraLayout[256];
+SkyTable *ServerSky;
+char  *PMM_CCD_TABLE;
+
+/* used to select entries from header (gstars or parse_time) (KEEP) */
+char   DateKeyword[64];
+char   DateMode[64];
+char   UTKeyword[64];
+char   MJDKeyword[64];
+char   JDKeyword[64];
+char   ExptimeKeyword[64];
+char   AirmassKeyword[64];
+char   CCDNumKeyword[64];
+char   STKeyword[64];
+char   ExtnameKeyword[64];
+
+/* these globals modify the behavior of gstars (KEEP) */
+double 	SNLIMIT;
+int    	ACCEPT_ASTROM;  // accept even bad astrometry solutions (NASTRO == 0)
+int    	ACCEPT_MOTION;  // accept reference proper motion measurements
+int    	ACCEPT_TIME;    // accept time stamp (or 0)
+int    	NO_STARS;       // ignore the stars
+int    	TEXTMODE;       // force input file to be loaded as RAW
+int     SUBPIX;         // apply a subpix correction
+char   *DUMP;           // dump out intermediate results
+int    	XOVERSCAN;      // used to modify stored image dimensions 
+int    	YOVERSCAN;      // used to modify stored image dimensions 
+int    	XMIN;           // used to filter loaded star list 
+int    	XMAX;           // used to filter loaded star list 
+int    	YMIN;           // used to filter loaded star list 
+int    	YMAX;           // used to filter loaded star list 
+double 	Latitude;       // carried into image structure from config
+double 	Longitude;      // carried into image structure from config
+double  FAKE_RA;        // boresite coords for fake images
+double  FAKE_DEC;       // boresite coords for fake images
+double  FAKE_THETA;     // boresite angle for fake images
+
+// carries the mosaic into gstars
+
+/* these globals are used separately by both client and server (KEEP) */
+double CAL_INSTMAG_MAX;
+double CAL_INSTMAG_MIN;
+int    VERBOSE;
+int    PLOT;
+double MAX_CERROR;
+double MIN_FWHM_X;
+double MIN_FWHM_Y;
+
+/* modify server behavior (make this an addstar cleanup mode?) */
+int    FORCE_READ;
+
+// XXX this should be replaced with 
+// 1) an airmass accuracy option
+// 2) an alternative CATFORMAT with the sky element correctly defined.
+
+/* these depend on HOW we implement the client/server interaction for CAT/REF modes */
+time_t    TIMEREF;    // used by MODE REF
+SkyRegion UserPatch;  // used by MODE CAT
+char     *SELECT_2MASS_QUALITY;  // used only by get2mass_as
+int NREFSTAR_GROUP;
+
+/*** addstar prototypes ***/
+
+void       AddToCalibration       PROTO((Average *average, SecFilt *secfilt, Measure *measure, Measure *new, int *next, int Nstar));
+AddstarClientOptions ConfigInit   PROTO((int *argc, char **argv));
+void       FindCalibration        PROTO((Image *image));
+FILE      *GetDB                  PROTO((int *state));
+void       InitCalibration        PROTO(());
+void       SaveCalibration        PROTO((float Mo, float dMo, float Mr, float dMr, float Mi, int N));
+void       SetProtect             PROTO((int mode));
+int        SetSignals             PROTO(());
+int        Shutdown               PROTO((char *message, ...); ) 
+void       TrapSignal             PROTO((int sig));
+float      airmass                PROTO((float secz_image, double ra, double dec, double st, double latitude));
+void       SetAirmassQuality      PROTO((int quality));
+SkyTable  *SkyTableFromTychoIndex PROTO((char *filename, int VERBOSE));
+AddstarClientOptions args         PROTO((int argc, char **argv, AddstarClientOptions options));
+void       check_permissions      PROTO((char *basefile));
+int        dump_rawstars          PROTO((Stars *stars, int Nstars));
+int        edge_check             PROTO((double *x1, double *y1, double *x2, double *y2));
+Image     *fakeimage              PROTO((char *rootname, int *Nimage, int photcode));
+int        find_matches           PROTO((SkyRegion *region, Stars *stars, int Nstars, Catalog *catalog, AddstarClientOptions options));
+int        find_matches_closest   PROTO((SkyRegion *region, Stars *stars, int Nstars, Catalog *catalog, AddstarClientOptions options));
+int        find_matches_refstars  PROTO((SkyRegion *region, Stars **stars, int Nstars, Catalog *catalog, AddstarClientOptions options));
+Stars    **find_subset            PROTO((SkyRegion *region, Stars *stars, int Nstars, int *NSTARS));
+int        gcatalog               PROTO((Catalog *catalog));
+Stars     *get2mass               PROTO((SkyRegion *patch, int photcode, int mode, int *NSTARS));
+double     get_subpix             PROTO((double x, double y));
+Stars     *getgsc                 PROTO((SkyRegion *patch, int *NSTARS));
+Stars     *gettycho               PROTO((SkyRegion *catstats, int photcode, int *Nstars));
+Stars     *getusno                PROTO((SkyRegion *catstats, int photcode, int *Nstars));
+Stars     *getusnob               PROTO((SkyRegion *catstats, int photcode, int *Nstars));
+Image     *gimages                PROTO((FITS_DB *db, Image *image, Coords *mosaic, int *Npimage));
+Stars     *grefcat                PROTO((char *Refcat, SkyRegion *catstats, int photcode, int *nstars));
+Stars     *grefstars              PROTO((char *file, int photcode, int *Nstars));
+
+Stars     *LoadStars              PROTO((char *file, int *Nstars, Image **images, int *Nimages, AddstarClientOptions *options));
+Header   **LoadHeaders            PROTO((FILE *f, int *mode, int *Nheader));
+HeaderSet *MatchHeaders           PROTO((int **extsize, int *nimage, int mode, Header **headers, int Nheaders));
+int        LoadData               PROTO((FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars, Header **headers, int *extsize, HeaderSet *headerSets, int NheaderSets));
+
+int        in_image               PROTO((double r, double d, Image *image));
+int        load_pt_catalog        PROTO((Catalog *catalog, SkyRegion *region));  /*** choose new name ***/
+void       load_subpix            PROTO(());
+void       lock_image_db          PROTO((FITS_DB *db, char *filename));
+int        main                   PROTO((int argc, char **argv));
+void       make_backup            PROTO((char *filename));
+int        match_refstars         PROTO((Stars *stars, int Nstars));
+void       mkcatalog              PROTO((SkyRegion *region, Catalog *catalog));
+double     opening_angle          PROTO((double x1, double y1, double x2, double y2, double x3, double y3));
+int        parse_time             PROTO((Header *header));
+Stars     *rd_gsc                 PROTO((char *filename, int *nstars));
+int        replace_match          PROTO((Average *average, Measure *measure, Stars *star));
+void       resort_catalog         PROTO((Catalog *catalog));
+Stars     *rfits                  PROTO((FILE *f, unsigned int *nstars));
+Stars     *rtext                  PROTO((FILE *f, unsigned int *nstars));
+Stars     *ReadStarsFITS          PROTO((FILE *f, Header *header, Header *in_theader, unsigned int *nstars));
+Stars     *ReadStarsTEXT          PROTO((FILE *f, unsigned int *nstars));
+Stars     *ReadStarsSDSS          PROTO((FILE *f, char *name, Header *header, Header *in_theader, Image *images, int *nimages, unsigned int *nstars));
+int        ReadImageHeader        PROTO((Header *header, Image *image, int photcode));
+Stars     *FilterStars            PROTO((Stars *instars, Image *image, unsigned int imageID));
+Stars     *MergeStars             PROTO((Stars *stars, int *Nstars, Stars *instars, int Ninstars));
+void       save_pt_catalog        PROTO((Catalog *catalog));  /*** choose new name ***/
+double     scat_subpix            PROTO((double x, double y));
+time_t     short_date_to_sec      PROTO((char *date));
+void       sort_lists             PROTO((float *X, float *Y, int *S, int N));
+void       sort_stars             PROTO((Stars *stars, int N));
+int        str_to_radec           PROTO((double *ra, double *dec, char *str1, char *str2));
+void       unlock_image_db        PROTO((FITS_DB *db));
+void       update_coords          PROTO((Average *average, Measure *measure, int *next));
+void       wcatalog               PROTO((Catalog *catalog));
+void       free_catalog           PROTO((Catalog *catalog));
+void       wimage                 PROTO((FITS_DB *db, Image *image));
+void       create_image_db        PROTO((FITS_DB *db));
+void       set_db                 PROTO((FITS_DB *in));
+void       uppercase              PROTO((char *string));
+void       fsort                  PROTO((float *X, int N));
+void       fsort2                 PROTO((float *X, float *Y, int N));
+int 	  *init_measure_links     PROTO((Average *average, int Naverage, Measure *measure, int Nmeasure));
+int 	  *init_missing_links     PROTO((Average *average, int Naverage, Missing *missing, int Nmissing));
+int 	   add_meas_link     	  PROTO((Average *average, int *next, int Nmeasure, int NMEASURE));
+int 	   add_miss_link     	  PROTO((Average *average, int *next, int Nmissing));
+int 	  *build_measure_links    PROTO((Average *average, int Naverage, Measure *measure, int Nmeasure));
+Measure   *sort_measure     	  PROTO((Average *average, int Naverage, Measure *measure, int Nmeasure, int *next));
+Missing   *sort_missing     	  PROTO((Average *average, int Naverage, Missing *missing, int Nmissing, int *next_miss));
+int        ImageOptions		  PROTO((AddstarClientOptions *options, Image *images, int Nimages));
+int        GetFileMode		  PROTO((Header *header));
+AddstarClientOptions args_client  PROTO((int argc, char **argv, AddstarClientOptions options));
+AddstarClientOptions args_load2mass PROTO((int argc, char **argv, AddstarClientOptions options));
+AddstarClientOptions args_sedstar PROTO((int argc, char **argv, AddstarClientOptions options));
+void	   args_server		  PROTO((int argc, char **argv));
+int 	   CheckPassword	  PROTO((int BindSocket));
+int 	   NewImage		  PROTO((int BindSocket));
+int 	   NewReflist		  PROTO((int BindSocket));
+int 	   NewRefcat		  PROTO((int BindSocket));
+int 	   InitServerSocket	  PROTO((SockAddress *Address));
+int 	   WaitServerSocket	  PROTO((int InitSocket, SockAddress *Address, int *validIP, int Nvalid));
+int 	   GetClientSocket	  PROTO((char *hostname));
+int 	   UpdateDatabase_Image	  PROTO((AddstarClientOptions *options, Image *images, int Nimages, Coords *mosaic, Stars *stars, int Nstars));
+int 	   UpdateDatabase_Reflist PROTO((AddstarClientOptions *options, Stars *stars, int Nstars));
+int 	   UpdateDatabase_Refcat  PROTO((AddstarClientOptions *options, SkyRegion *UserPatch, char *refcat));
+SkyList   *SkyListForStars	  PROTO((SkyTable *table, int depth, Stars *stars, int Nstars));
+SkyList   *SkyListExistingSubset  PROTO((SkyList *input, char *path));
+int        SkyListSetPath	  PROTO((SkyList *list, char *path));
+SkyTable  *SkyTableLoadOptimal	  PROTO(());
+int 	   InitDataset		  PROTO(());
+int 	   PushDataset		  PROTO((DVO_DATA *data));
+DVO_DATA  *PopDataset		  PROTO((void));
+void	  *ListenClients_Thread	  PROTO((void *data));
+int 	   NewImage_Thread	  PROTO((int BindSocket));
+int 	   NewRefcat_Thread	  PROTO((int BindSocket));
+int 	   NewReflist_Thread	  PROTO((int BindSocket));
+
+Stars     *Convert_SMPDATA	  PROTO((FTable *table, int *nstars));
+Stars     *Convert_PS1_DEV_0	  PROTO((FTable *table, int *nstars));
+Stars     *Convert_PS1_DEV_1	  PROTO((FTable *table, int *nstars));
+
+int args_skycells (int argc, char **argv);
+int ConfigInit_skycells (int *argc, char **argv);
+int UpdateImageIDs (Stars *stars, int Nstars, Image *images, int Nimages);
+int LoadDataSDSS (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars, Header **headers, int *extsize, HeaderSet *headerSets, int Nimages);
+int altaz (double *alt, double *az, double ha, double dec, double latitude);
+
+int LoadDataPMM (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars);
+PhotCode *LoadMetadataPMM (char *datafile, Image *image);
+time_t pmm_date_to_sec (char *date, char *time);
+double pmm_get_ra (char *RA);
+double pmm_get_dec (char *DEC);
+PhotCode *pmm_get_photcode (char *emulsion, char *filter);
+
+// this is a gnu extension?? caution!
+void *memrchr(const void *s, int c, size_t n);
+
+/** 
+    there is an inconsistency to be resolved: fixed structures (like Image)
+    need a fixed bit-length time (e_time), but these functions all use the
+    UNIX time_t types, which may be 32 or 64 bits, depending on the machine.
+    This can be resolved by using time_t with these functions, but casting 
+    between e_time and time_t when necessary (ie, cannot return data to an
+    e_time pointer from one of these functions)
+**/
+
+
+/** function for client / server **/ 
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/sedstar.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/sedstar.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/sedstar.h	(revision 22322)
@@ -0,0 +1,45 @@
+# include "addstar.h"
+# include "kapa.h"
+
+typedef enum {
+  SED_FIT,
+  SED_REQ,
+  SED_MODEL,
+  SED_SAMPLE,
+} SEDtableModes;
+
+typedef struct {
+  float *mags;
+  float color;
+  float Temp;
+  float Av;
+} SEDtableRow;
+
+typedef struct {
+  float chisq;
+  float Md;
+  int row;
+} SEDfit;
+
+typedef struct {
+  int Nfilter;
+  float *wavecode;
+  float *vegaToAB;
+  int *mode;
+  int *hashcode;
+  int *code;
+  int codeP;
+  int codeM;
+  SEDtableRow **row;
+  int Nrow;
+} SEDtable;
+
+SEDtableRow **sort_SEDtable (SEDtableRow *raw, int N);
+SEDfit SEDchisq (SEDtableRow *ref, SEDtableRow *data, SEDtableRow *error, int Nfilter);
+SEDtable *SEDtableLoad (char *filename);
+int SEDcolorBracket (SEDtable *table, float color, float delta);
+int SEDfitInit (SEDtable *table);
+int SEDfitPlot (SEDtable *table, double R, double D, SEDfit *minFit, SEDtableRow *sourceValue, SEDtableRow *sourceError);
+int SEDfitClear ();
+int SEDfitCatalog (Catalog *outcat, Catalog *incat, SEDtable *table);
+void SetLimitsRaw (float *xvec, float *yvec, int Nelements, Graphdata *graphmode);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/skycells.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/skycells.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/include/skycells.h	(revision 22322)
@@ -0,0 +1,92 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+# include <sys/time.h>
+# include <time.h>
+# include <zlib.h>
+
+/* linux is happy with this, not solaris */
+# include <netinet/ip.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <glob.h>
+
+enum {SQUARES, TRIANGLES};
+
+typedef struct {
+  double x, y, z;
+} Point;
+
+typedef struct {
+  Point vertex[3];	      // triangle vertices (3d)
+  Point center;		      // triangle center (3d)
+  double r, d;		      // triangle center (2d)
+  double rv[3], dv[3];	      // triangle center (2d)
+} SkyTriangle;
+
+typedef struct {
+  Coords coords;
+  int NX;
+  int NY;
+  int code;
+  char name[64];
+} SkyRectangle;
+
+/* globals which define database info / data sources (KEEP) */
+char   ImageCat[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char   PASSWORD[80];
+char   HOSTNAME[80];
+int    NVALID, *VALID_IP;
+char   SKY_TABLE[256];
+int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+char   CameraLayout[256];
+SkyTable *ServerSky;
+
+/* used to select entries from header (gstars or parse_time) (KEEP) */
+char   DateKeyword[64];
+char   DateMode[64];
+char   UTKeyword[64];
+char   MJDKeyword[64];
+char   JDKeyword[64];
+char   ExptimeKeyword[64];
+char   AirmassKeyword[64];
+char   CCDNumKeyword[64];
+char   STKeyword[64];
+
+int    VERBOSE;
+int    MODE;
+int    FIX_NS;
+int    NMAX;
+int    NX_SUB, NY_SUB;
+double SCALE;
+double PADDING;
+
+double EULER_A;
+double EULER_B;
+
+void         SetProtect                PROTO((int mode));
+int          SetSignals                PROTO(());
+int          Shutdown                  PROTO((char *message, ...); ) 
+void         TrapSignal                PROTO((int sig));
+
+int 	     args_skycells 	       PROTO((int argc, char **argv));
+int 	     ConfigInit_skycells       PROTO((int *argc, char **argv));
+int 	     sky_tessalation 	       PROTO((FITS_DB *db, int level, int Nmax, int mode, double scale));
+int          sky_tessalation_init      PROTO((double scale));
+int 	     sky_tessalation_triangles PROTO((FITS_DB *db, int level, int Nmax));
+int 	     sky_tessalation_squares   PROTO((FITS_DB *db, int level, int Nmax));
+int 	     sky_triangle_to_image     PROTO((Image *image, SkyTriangle *triangle));
+int 	     sky_triangle_to_rectangle PROTO((SkyRectangle *image, SkyTriangle *triangle));
+int 	     sky_subdivide_image       PROTO((Image *output, SkyRectangle *input, int Nx, int Ny));
+int 	     sky_triangle_coords       PROTO((SkyTriangle *triangle));
+SkyTriangle *sky_divide_triangles      PROTO((SkyTriangle *in, int *ntriangles));
+SkyTriangle *sky_base_triangles        PROTO((int *ntriangles));
+int          sky_base_rotation         PROTO((SkyTriangle *base, int Nbase));
+Point        sky_divide_edge           PROTO((Point v1, Point v2));
+
+// XXX migrate to libdvo eventually
+int dvo_image_clear_vtable             PROTO((FITS_DB *db));
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/CheckPassword.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/CheckPassword.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/CheckPassword.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "addstar.h"
+
+int CheckPassword (int BindSocket) {
+
+  IOBuffer message;
+  int status;
+
+  status = ExpectCommand (BindSocket, strlen(PASSWORD), 0.1, &message);
+  if (status != 0) {
+    if (VERBOSE) fprintf (stderr, "failed connection\n");
+    FreeIOBuffer (&message);
+    close (BindSocket);
+    return (FALSE);
+  }
+  if (strncmp (message.buffer, PASSWORD, strlen(PASSWORD))) {
+    if (VERBOSE) fprintf (stderr, "invalid password\n");
+    close (BindSocket);
+    return (FALSE);
+  }
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,190 @@
+# include "addstar.h"
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr);
+
+AddstarClientOptions ConfigInit (int *argc, char **argv) {
+
+  double ZERO_POINT;
+  char *config, *file;
+  char RadiusWord[80];
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+  AddstarClientOptions options;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+
+  /* exclude overscan region from the dB image boundaries */
+  XOVERSCAN = YOVERSCAN = 0;
+  ScanConfig (config, "XOVERSCAN",              "%d",  0, &XOVERSCAN);
+  ScanConfig (config, "YOVERSCAN",              "%d",  0, &YOVERSCAN);
+
+  /* only upload stars within region; a value of 0 means ignore the limit */
+  XMIN = XMAX = YMIN = YMAX = 0;
+  ScanConfig (config, "ADDSTAR_XMIN",           "%d",  0, &XMIN);
+  ScanConfig (config, "ADDSTAR_XMAX",           "%d",  0, &XMAX);
+  ScanConfig (config, "ADDSTAR_YMIN",           "%d",  0, &YMIN);
+  ScanConfig (config, "ADDSTAR_YMAX",           "%d",  0, &YMAX);
+
+  /* exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT) */
+  SNLIMIT = 0;
+  ScanConfig (config, "MIN_SN_FSTAT",           "%lf", 0, &SNLIMIT);
+  ScanConfig (config, "ADDSTAR_SNLIMIT",        "%lf", 0, &SNLIMIT);
+
+  MAX_CERROR = 0.5; // arcseconds
+  ScanConfig (config, "ADDSTAR_MAX_CERROR",     "%lf", 0, &MAX_CERROR);
+
+  MIN_FWHM_X = 0.0; // arcseconds
+  ScanConfig (config, "ADDSTAR_MIN_FWHM_X",     "%lf", 0, &MIN_FWHM_X);
+  MIN_FWHM_Y = 0.0; // arcseconds
+  ScanConfig (config, "ADDSTAR_MIN_FWHM_Y",     "%lf", 0, &MIN_FWHM_Y);
+
+  /* used by parse_time to find time-related keywords */
+  strcpy (DateKeyword, "NONE");
+  strcpy (DateMode, "NONE");
+  strcpy (UTKeyword, "NONE");
+  strcpy (JDKeyword, "NONE");
+  strcpy (MJDKeyword, "NONE");
+  ScanConfig (config, "DATE-KEYWORD",           "%s",  0, DateKeyword);
+  ScanConfig (config, "DATE-MODE",              "%s",  0, DateMode);
+  ScanConfig (config, "UT-KEYWORD",             "%s",  0, UTKeyword);
+  ScanConfig (config, "MJD-KEYWORD",            "%s",  0, MJDKeyword);
+  ScanConfig (config, "JD-KEYWORD",             "%s",  0, JDKeyword);
+
+  ScanConfig (config, "EXPTIME-KEYWORD",        "%s",  0, ExptimeKeyword);
+  ScanConfig (config, "AIRMASS-KEYWORD",        "%s",  0, AirmassKeyword);
+  ScanConfig (config, "CCDNUM-KEYWORD",         "%s",  0, CCDNumKeyword);
+  ScanConfig (config, "ST-KEYWORD",             "%s",  0, STKeyword);
+  ScanConfig (config, "OBSERVATORY-LATITUDE",   "%lf", 0, &Latitude);
+  ScanConfig (config, "OBSERVATORY-LONGITUDE",  "%lf", 0, &Longitude);
+  ScanConfig (config, "SUBPIX_DATAFILE",        "%s",  0, SubpixDatafile);
+
+  if (!ScanConfig (config, "EXTNAME-KEYWORD",        "%s",  0, ExtnameKeyword)) {
+      strcpy (ExtnameKeyword, "EXTNAME"); 
+  }
+
+  /* instrumental magnitude range for calibration mode */
+  CAL_INSTMAG_MAX =  -9.0;
+  CAL_INSTMAG_MIN = -13.0;
+  ScanConfig (config, "CAL_INSTMAG_MAX",        "%lf", 0, &CAL_INSTMAG_MAX);
+  ScanConfig (config, "CAL_INSTMAG_MIN",        "%lf", 0, &CAL_INSTMAG_MIN);
+
+  /* location of needed data sources */
+  ScanConfig (config, "2MASS_DIR_AS",           "%s",  0, TWO_MASS_DIR_AS);
+  ScanConfig (config, "2MASS_DIR_DR2",          "%s",  0, TWO_MASS_DIR_DR2);
+  ScanConfig (config, "GSCDIR",                 "%s",  0, GSCDIR);
+
+  if (!ScanConfig (config, "USNO_A_DIR",        "%s",  0, USNO_A_DIR)) {
+    ScanConfig (config, "USNO_CDROM",           "%s",  0, USNO_A_DIR);
+  }
+  ScanConfig (config, "USNO_B_DIR",             "%s",  0, USNO_B_DIR);
+
+  ScanConfig (config, "TYCHO_DIR",             	"%s",  0, TYCHO_DIR);
+
+  GetConfig (config, "GSCFILE",                	"%s",  0, GSCFILE);
+  GetConfig (config, "CATDIR",                 	"%s",  0, CATDIR);
+  GetConfig (config, "PHOTCODE_FILE",          	"%s",  0, MasterPhotcodeFile);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = SKY_DEPTH_HST;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+  GetConfig (config, "ZERO_PT",                "%lf", 0, &ZERO_POINT);
+  SetZeroPoint (ZERO_POINT);
+
+  if (!ScanConfig (config, "IMAGE_TABLE",       "%s",  0, ImageCat)) {
+    if (!ScanConfig (config, "IMAGE_CATALOG",   "%s",  0, ImageCat)) {
+      sprintf (ImageCat, "%s/Images.dat", CATDIR);
+    }
+  }
+
+  ScanConfig (config, "CAMERA_LAYOUT",          "%s",  0, CameraLayout);
+
+  /* used by client/server setup */
+  ScanConfig (config, "PASSWORD",               "%s",  0, PASSWORD);
+  ScanConfig (config, "HOSTNAME",               "%s",  0, HOSTNAME);
+  
+  /* load valid ip list */
+  {
+    int i, Nvalid, ip1, ip2, ip3, ip4, test, status;
+    char string[80];
+
+    Nvalid = 0;
+    NVALID = 10;
+    ALLOCATE (VALID_IP, int, NVALID);
+    for (i = 0; ScanConfig (config, "VALID_IP", "%s", i, string) != NULL; i++) {
+      status = sscanf (string, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4);
+      test = TRUE;
+      test &= (status == 4);
+      test &= ((ip1 > 0) && (ip1 < 256)); 
+      test &= ((ip2 > 0) && (ip2 < 256)); 
+      test &= ((ip3 > 0) && (ip3 < 256)); 
+      test &= ((ip4 >=0) && (ip4 < 256)); 
+      if (!test) {
+	fprintf (stderr, "invalid IP address %s\n", string);
+	exit (2);
+      }
+      VALID_IP[Nvalid] = ip1 | (ip2 << 8) | (ip3 << 16) | (ip4 << 24);
+      Nvalid ++;
+      CHECK_REALLOCATE (VALID_IP, int, NVALID, Nvalid, 10);
+    }
+    NVALID = Nvalid;
+    REALLOCATE (VALID_IP, int, NVALID);
+    if (NVALID == 0) {
+      free (VALID_IP);
+      VALID_IP = NULL;
+    }
+  }
+
+  /* set the default search radius */
+  if (!ScanConfig (config, "ADDSTAR_RADIUS", "%s", 0, RadiusWord)) {
+    GetConfig (config, "RADIUS", "%s", 0, RadiusWord);
+  }
+  /* XXX this does not work for refcat and reflist modes... */
+  if (!strcasecmp (RadiusWord, "header")) {
+    options.radius = 0;
+    if (!ScanConfig (config, "ADDSTAR_NSIGMA", "%lf", 0, &options.Nsigma)) {
+      GetConfig (config, "NSIGMA", "%lf", 0, &options.Nsigma);
+    }
+  } else {
+    options.radius = atof (RadiusWord);
+  }
+
+  /* default mode, format, if not specified */
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+  return (options);
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ConfigInit_skycells.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ConfigInit_skycells.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ConfigInit_skycells.c	(revision 22322)
@@ -0,0 +1,77 @@
+# include "skycells.h"
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr);
+
+int ConfigInit_skycells (int *argc, char **argv) {
+
+  char *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  /* used by parse_time to find time-related keywords */
+  strcpy (DateKeyword, "NONE");
+  strcpy (DateMode, "NONE");
+  strcpy (UTKeyword, "NONE");
+  strcpy (JDKeyword, "NONE");
+  strcpy (MJDKeyword, "NONE");
+  ScanConfig (config, "DATE-KEYWORD",           "%s",  0, DateKeyword);
+  ScanConfig (config, "DATE-MODE",              "%s",  0, DateMode);
+  ScanConfig (config, "UT-KEYWORD",             "%s",  0, UTKeyword);
+  ScanConfig (config, "MJD-KEYWORD",            "%s",  0, MJDKeyword);
+  ScanConfig (config, "JD-KEYWORD",             "%s",  0, JDKeyword);
+
+  ScanConfig (config, "EXPTIME-KEYWORD",        "%s",  0, ExptimeKeyword);
+  ScanConfig (config, "AIRMASS-KEYWORD",        "%s",  0, AirmassKeyword);
+  ScanConfig (config, "CCDNUM-KEYWORD",         "%s",  0, CCDNumKeyword);
+  ScanConfig (config, "ST-KEYWORD",             "%s",  0, STKeyword);
+
+  GetConfig (config, "GSCFILE",                	"%s",  0, GSCFILE);
+  GetConfig (config, "CATDIR",                 	"%s",  0, CATDIR);
+  GetConfig (config, "PHOTCODE_FILE",          	"%s",  0, MasterPhotcodeFile);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = SKY_DEPTH_HST;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  /* default mode, format, if not specified */
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+  return (TRUE);
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/DatasetOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/DatasetOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/DatasetOps.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include "addstar.h"
+# include <pthread.h>
+
+/* the init function is an alternative */
+/* pthread_mutex_init(&mutex, const pthread_mutexattr_t *mutexattr); */
+pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
+
+static int Ndataset = 0;
+static int NDATASET = 0;
+static DVO_DATA **dataset = NULL;
+
+int InitDataset () {
+
+  int i;
+
+
+  Ndataset = 0;
+  NDATASET = 100;
+  ALLOCATE (dataset, DVO_DATA *, NDATASET);
+  for (i = 0; i < NDATASET; i++) {
+    dataset[i] = NULL;
+  }
+  return (TRUE);
+}
+
+/* data a set dataset to the end of the stack */
+int PushDataset (DVO_DATA *data) {
+
+  int N;
+
+  pthread_mutex_lock(&fastmutex);
+
+  N = Ndataset;
+  Ndataset ++;
+  CHECK_REALLOCATE (dataset, DVO_DATA *, NDATASET, Ndataset, 100);
+
+  dataset[N] = data;
+
+  pthread_mutex_unlock(&fastmutex);
+  return (TRUE);
+}
+
+/* remove and return first dataset */
+DVO_DATA *PopDataset (void) {
+
+  int i;
+  DVO_DATA *data;
+
+  pthread_mutex_lock(&fastmutex);
+
+  if (Ndataset == 0) {
+    pthread_mutex_unlock(&fastmutex);
+    return (NULL);
+  }
+
+  data = dataset[0];
+  
+  for (i = 0; i < Ndataset - 1; i++) {
+    dataset[i] = dataset[i+1];
+  }
+  Ndataset --;
+  if ((Ndataset < NDATASET / 2) && (Ndataset > 50)) {
+    NDATASET = Ndataset / 2;
+    REALLOCATE (dataset, DVO_DATA *, NDATASET);
+  }
+
+  pthread_mutex_unlock(&fastmutex);
+  return (data);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/FilterStars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/FilterStars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/FilterStars.c	(revision 22322)
@@ -0,0 +1,103 @@
+# include "addstar.h"
+
+Stars *FilterStars (Stars *instars, Image *image, unsigned int imageID) {
+
+  int j, N;
+  float MTIME, dMs, dMx;
+  Stars *stars;
+  float RMIN, RMAX, DMIN, DMAX;
+
+  /* correct instrumental mags for exposure time */
+  MTIME = (image[0].exptime > 0) ? 2.500*log10(image[0].exptime) : 0.0;
+
+  RMIN = 360.0;
+  RMAX =   0.0;
+  DMIN = +90.0;
+  DMAX = -90.0;
+
+  /* modify resulting star list */
+  ALLOCATE (stars, Stars, image[0].nstar);
+  for (N = j = 0; j < image[0].nstar; j++) {
+    /* allow for some dynamic filtering of star list */
+    if (SNLIMIT && instars[j].dM > SNLIMIT) continue;
+    if (XMAX && (instars[j].X > XMAX)) continue;
+    if (XMIN && (instars[j].X < XMIN)) continue;
+    if (YMAX && (instars[j].Y > YMAX)) continue;
+    if (YMIN && (instars[j].Y < YMIN)) continue;
+    stars[N] = instars[j];
+
+    XY_to_RD (&stars[N].R, &stars[N].D, stars[N].X, stars[N].Y, &image[0].coords);
+    while (stars[N].R <    0.0) stars[N].R += 360.0;
+    while (stars[N].R >= 360.0) stars[N].R -= 360.0;
+    stars[N].found = -1;
+    stars[N].code = image[0].photcode;
+
+    RMIN = MIN (RMIN, stars[N].R);
+    RMAX = MAX (RMAX, stars[N].R);
+    DMIN = MIN (DMIN, stars[N].D);
+    DMAX = MAX (DMAX, stars[N].D);
+    /** additional quantities to supply to Stars based on the image data **/
+
+    /* calculate accurate per-star airmass */
+    stars[N].airmass = airmass (image[0].secz, stars[N].R, stars[N].D, image[0].sidtime, image[0].latitude);
+    stars[N].Mcal    = image[0].Mcal;
+    stars[N].t       = image[0].tzero + 1e-4*stars[N].Y*image[0].trate;  /* trate is in 0.1 msec / row */
+    stars[N].dt      = MTIME;
+
+    if ((stars[N].M > 25.0) && (stars[N].M < 32.0)) {
+      fprintf (stderr, "*");
+    }
+
+    // stars->M is either NAN or a valid inst magnitude
+    // stars->dM is either NAN or a valid error
+
+    dMs  = 0.0;
+    dMx = 0.0;
+    if (SUBPIX) {
+      dMs = get_subpix (stars[N].X, stars[N].Y);
+      dMx = scat_subpix (stars[N].X, stars[N].Y);
+      if (!isnan(stars[N].dM)) {
+	stars[N].dM = hypot (stars[N].dM, dMx);
+      }
+    }
+
+    if (!isnan(stars[N].M)) {
+      stars[N].M += MTIME - dMs;
+    }
+    if (!isnan(stars[N].Map)) {
+      stars[N].Map += MTIME - dMs;
+    }
+    
+    // XXX currently, this ID is internal only; 
+    // should we use the psphot / other external ID, if available?
+    stars[N].detID = N; // sequence number within image
+    stars[N].imageID = imageID; // does this need to be updated?
+
+    N ++;
+  }
+  image[0].nstar = N;
+  REALLOCATE (stars, Stars, image[0].nstar);
+  free (instars);
+
+  if (VERBOSE) fprintf (stderr, "read %d stars from target file\n", image[0].nstar);
+  if (VERBOSE) fprintf (stderr, "stars cover region %f,%f - %f,%f\n", RMIN, DMIN, RMAX, DMAX);
+  return (stars);
+}
+
+Stars *MergeStars (Stars *stars, int *Nstars, Stars *instars, int Ninstars) {
+
+  int i, j;
+
+  if (stars == NULL) {
+    ALLOCATE (stars, Stars, Ninstars);
+  } else {
+    REALLOCATE (stars, Stars, *Nstars + Ninstars);
+  }
+
+  for (j = 0, i = *Nstars; i < *Nstars + Ninstars; i++, j++) {
+    stars[i] = instars[j];
+  }
+  
+  *Nstars += Ninstars;
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/GetFileMode.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/GetFileMode.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/GetFileMode.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "addstar.h"
+
+// examine the PHU of this file and determine the file mode
+int GetFileMode (Header *header) {
+
+  char ctype[80];
+  int Naxis;
+  int simple, extend, haveNaxis, haveCTYPE;
+
+  gfits_scan (header, "SIMPLE", "%t", 1, &simple);
+  haveNaxis = gfits_scan (header, "NAXIS",  "%d", 1, &Naxis);
+  haveCTYPE = gfits_scan (header, "CTYPE1", "%s", 1, ctype);
+
+  gfits_scan (header, "EXTEND", "%t", 1, &extend);
+    
+  { 
+    int tmp, haveCAMCOL, haveSTRIPE;
+    
+    // SDSS tsObj files have CAMCOL & STRIP keywords present in the header
+    haveCAMCOL = gfits_scan (header, "CAMCOL",  "%d", 1, &tmp);
+    haveSTRIPE = gfits_scan (header, "STRIPE",  "%d", 1, &tmp);
+    if (haveCAMCOL && haveSTRIPE) return SDSS_OBJ;
+  }
+
+  if ((Naxis == 2) || TEXTMODE || !simple) {
+    if (!strcmp (&ctype[4], "-WRP")) {
+      return MOSAIC_CMP;
+    }
+    return SIMPLE_CMP;
+  }
+
+  if (!extend && strcmp (&ctype[4], "-DIS")) {
+    if (!strcmp (&ctype[4], "-WRP")) {
+      return MOSAIC_CMF;
+    }
+    return SIMPLE_CMF;
+  }
+
+  if (!extend && !strcmp (&ctype[4], "-DIS")) {
+    return MOSAIC_PHU;
+  }
+
+  if (extend && strcmp (&ctype[4], "-DIS")) {
+    return SIMPLE_MEF;
+  }
+
+  if (extend && !strcmp (&ctype[4], "-DIS")) {
+    return MOSAIC_MEF;
+  }
+
+  return (NONE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ImageOptions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ImageOptions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ImageOptions.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "addstar.h"
+
+/* set specific options based on the image collection */
+int ImageOptions (AddstarClientOptions *options, Image *images, int Nimages) {
+
+  int i, equivPhotcode, consistent;
+  float maxError;
+  PhotCode *photcode;
+
+  // set the radius to the maximum error circle
+  if (options[0].radius == 0) {
+    maxError = 0;
+    for (i = 0; i < Nimages; i++) {
+      maxError = MAX (maxError, 0.02 * images[i].cerror);
+    }
+    options[0].radius = options[0].Nsigma * maxError;
+  }     
+
+  // check that all images have the same equiv photcode and save it
+  // XXX this is only used to allow use to calculate an average mag
+  // if we have mis-matched photcodes, leave this as 0;
+  options[0].photcode = 0;
+  equivPhotcode = 0;
+  consistent = TRUE;
+
+  for (i = 0; i < Nimages; i++) {
+    /* MOSAIC_PHU images do not have a photcode */
+    if (!strcmp (&images[0].coords.ctype[4], "-DIS")) continue;
+
+    photcode = GetPhotcodebyCode (images[i].photcode);
+
+    if (equivPhotcode) {
+      if (equivPhotcode != photcode[0].equiv) {
+	consistent = FALSE;
+	break;
+      }
+    } else {
+      equivPhotcode = photcode[0].equiv;
+    }
+  }
+  if (consistent) {
+    options[0].photcode = equivPhotcode;
+  }
+
+  options[0].imageID = 0;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ListenClients_Thread.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ListenClients_Thread.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ListenClients_Thread.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "addstar.h"
+
+/* wait for incoming messages from clients */
+void *ListenClients_Thread (void *data) {
+
+  int status, InitSocket, BindSocket;
+  SockAddress Address;
+  IOBuffer message;
+  
+  /* if we have multiple threads, each one creates its own socket? */
+  InitSocket = InitServerSocket (&Address);
+  
+  while (1) {
+
+    /* wait for clients to make connection */
+    BindSocket = WaitServerSocket (InitSocket, &Address, VALID_IP, NVALID);
+    if (BindSocket == -1) continue;
+
+    /* validate : wait for password */
+    if (!CheckPassword (BindSocket)) continue;
+    
+    /* accept command : XXX EAM : long-enough timeout? */
+    status = ExpectCommand (BindSocket, 5, 0.1, &message);
+    if (status != 0) {
+      if (VERBOSE) fprintf (stderr, "failed connection\n");
+      FreeIOBuffer (&message);
+      close (BindSocket);
+      continue;
+    }
+
+    /* message options */
+    if (!strcmp (message.buffer, "IMAGE")) {
+      fprintf (stderr, "Image\n");
+      NewImage_Thread (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "REFLS")) {
+      fprintf (stderr, "Reflist\n");
+      NewReflist_Thread (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "REFCT")) {
+      fprintf (stderr, "Refcat\n");
+      NewRefcat_Thread (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "EXIT")) {
+      /* need to send this signal to the main thread */
+      fprintf (stderr, "Exit\n");
+      exit (2);
+    }
+  }    
+  exit (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadData.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadData.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadData.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include "addstar.h"
+# define DVO_IMAGE_NAME_LEN 128
+
+// XXX this function is somewhat specific to the elixir format output files 
+// it is capable of distinguishing several format versions defined for elixir/psphot
+
+// examine the header sets and set the Image entries for the the valid images
+int LoadData (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars, Header **headers, int *extsize, HeaderSet *headerSets, int Nimages) {
+
+  char *name;
+  int i, j, Nvalid, Nhead, Ndata, Nskip;
+  Stars *inStars;
+
+  if (images[0] == NULL) {
+    Nvalid = 0;
+    NVALID = 10;
+    ALLOCATE (images[0], Image, NVALID);
+  } else {
+    Nvalid = *nvalid;
+    NVALID = Nvalid + 10;
+    REALLOCATE (images[0], Image, NVALID);
+  }    
+
+  // find image rootname
+  name = filebasename (file);
+
+  // now run through the images, interpret the headers and read the stars
+  for (i = 0; i < Nimages; i++) {
+    Nhead = headerSets[i].extnum_head;
+
+    if (VERBOSE) fprintf (stderr, "reading header for %s (%s)\n", headerSets[i].exthead, headerSets[i].extdata);
+    if (!ReadImageHeader (headers[Nhead], &images[0][Nvalid], 0)) {
+      fprintf (stderr, "skipping %s\n", headerSets[i].exthead);
+      continue;
+    }
+    images[0][Nvalid].imageID = Nvalid;
+    images[0][Nvalid].externID = 0;
+    images[0][Nvalid].sourceID = 0;
+
+    // XXX EAM : I seemed to have dropped the ability to support TEXT (old-style cmp format files).
+    // I need to detect them here and load them with ReadStarsTEXT instead of calling the code
+    // below.
+
+    // XXX use something to set the chip name? EXTNAME?
+    if (!strcmp(headerSets[i].exthead, "PHU") && (Nimages == 1)) {
+      snprintf (images[0][Nvalid].name, DVO_IMAGE_NAME_LEN, "%s", name);
+    } else {
+      snprintf (images[0][Nvalid].name, DVO_IMAGE_NAME_LEN, "%s[%s]", name, headerSets[i].exthead);
+    }
+
+    // skip the table if there is no data segment (eg, mosaic WRP image)
+    if (!strcmp(headerSets[i].extdata, "NONE")) {
+      Nvalid++;
+      CHECK_REALLOCATE (images[0], Image, NVALID, Nvalid, 10);
+      continue;
+    }
+
+    // advance the pointer to the start of the corresponding table block
+    Ndata = headerSets[i].extnum_data;
+    Nskip = 0;
+    for (j = 0; j < Ndata; j++) {
+      Nskip += extsize[j];
+    }
+    fseek (f, Nskip, SEEK_SET); 
+	 
+    inStars = ReadStarsFITS (f, headers[Nhead], headers[Ndata], &images[0][Nvalid].nstar);
+    inStars = FilterStars (inStars, &images[0][Nvalid], Nvalid);
+    *stars = MergeStars (*stars, Nstars, inStars, images[0][Nvalid].nstar);
+    Nvalid++;
+    CHECK_REALLOCATE (images[0], Image, NVALID, Nvalid, 10);
+  }
+  free (name);
+  *nvalid = Nvalid;
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadDataPMM.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadDataPMM.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadDataPMM.c	(revision 22322)
@@ -0,0 +1,446 @@
+# include "addstar.h"
+# define DVO_IMAGE_NAME_LEN 128
+
+/* .asc files look like:
+0         1         2         3
+0123456789012345678901234567890123456789
+187.498117^  2.659253^21.06$
+187.498672^  2.713833^17.80$
+(^ = tab char)
+*/
+
+# define NLINE_ASC 10000
+# define NBYTE_ASC_TABLE 28
+# define RA_INDEX_ASC 0
+# define DEC_INDEX_ASC 11
+# define MAG_INDEX_ASC 22
+
+int LoadDataPMM (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars) {
+
+  char *name, *buffer;
+  int i, fd, Nbyte, Nline, Nvalid, code;
+  double ra, dec, mag, airmass, az, ZeroPoint, ZeroPt;
+  unsigned int Ninstars, NINSTARS;
+  Stars *inStars;
+  PhotCode *photcode;
+  gzFile gz;
+
+  double minR0, minR1, maxR0, maxR1, minD, maxD;
+
+  if (images[0] == NULL) {
+    Nvalid = 0;
+    NVALID = 1;
+    ALLOCATE (images[0], Image, NVALID);
+  } else {
+    Nvalid = *nvalid;
+    NVALID = Nvalid ++;
+    REALLOCATE (images[0], Image, NVALID);
+  }    
+
+  // find image rootname
+  name = filebasename (file);
+
+  // there is only one PMM image per file
+  if (VERBOSE) fprintf (stderr, "reading data for %s\n", file);
+
+  ZeroPt = GetZeroPoint();
+
+  // need to get the metadata from the PMM_CCD_TABLE
+  photcode = LoadMetadataPMM (name, &images[0][Nvalid]);
+  code = photcode[0].code;
+  ZeroPoint = 0.001*photcode[0].C;
+  // XXX NOTE : as of 2008.02.27, the zero point is still carried internally in millimags
+
+  ALLOCATE (buffer, char, NLINE_ASC*NBYTE_ASC_TABLE);
+
+  // use the following to get alt, az:
+  // altaz (&alt, &az, 15.0*images[N].sidtime - images[N].coords.crval1, images[N].coords.crval2, Latitude);
+  // these two can be calculated from HA and LATITUDE (need a table of observatory LAT)
+  airmass = 1.0;
+  az = 0.0;
+
+  NINSTARS = 10000;
+  ALLOCATE (inStars, Stars, NINSTARS);
+
+  minR0 = minR1 = 360.0;
+  maxR0 = maxR1 =   0.0;
+  minD = +90.0;
+  maxD = -90.0;
+
+  fd = fileno (f);
+  gz = gzdopen (dup(fd), "rb");
+
+  // read in a big chunk at a time, parse the lines assuming fixes line sizes and fields
+  Ninstars = 0;
+  while (1) {
+
+    Nbyte = gzread (gz, buffer, NLINE_ASC*NBYTE_ASC_TABLE);
+    if (Nbyte == 0) break;
+
+    assert (Nbyte % NBYTE_ASC_TABLE == 0);
+    Nline = Nbyte / NBYTE_ASC_TABLE;
+
+    for (i = 0; i < Nline; i++) {
+      // fscanf (f, "%lf %lf %lf", &ra, &dec, &mag) != EOF) {
+
+      dparse (&ra,  1, &buffer[i*NBYTE_ASC_TABLE]);
+      dparse (&dec, 2, &buffer[i*NBYTE_ASC_TABLE]);
+      dparse (&mag, 3, &buffer[i*NBYTE_ASC_TABLE]);
+
+      memset (&inStars[Ninstars], 0, sizeof(Stars));
+
+      if (ra > 180) {
+	minR1 = MIN(minR1, ra);
+	maxR1 = MAX(maxR1, ra);
+      } else {
+	minR0 = MIN(minR0, ra);
+	maxR0 = MAX(maxR0, ra);
+      }
+      minD = MIN(minD, dec);
+      maxD = MAX(maxD, dec);
+
+      inStars[Ninstars].M       = mag - ZeroPoint + ZeroPt;
+      inStars[Ninstars].R       = ra;
+      inStars[Ninstars].D       = dec;
+      inStars[Ninstars].t       = images[0][0].tzero;
+      inStars[Ninstars].dt      = images[0][0].exptime;
+      inStars[Ninstars].code    = code;
+      inStars[Ninstars].airmass = airmass;
+      inStars[Ninstars].az      = az;
+      inStars[Ninstars].found   = -1; // found starts at -1 == not yet found
+      Ninstars++;
+      CHECK_REALLOCATE (inStars, Stars, NINSTARS, Ninstars, 10000);
+    }
+  }
+
+  fprintf (stderr, "ra ranges: %f - %f, %f - %f; dec ranges: %f - %f\n", minR0, maxR0, minR1, maxR1, minD, maxD);
+
+  images[0][0].nstar = Ninstars;
+  images[0][0].imageID = 0;
+
+  *stars = MergeStars (*stars, Nstars, inStars, Ninstars);
+
+  free (inStars);
+  free (name);
+  *nvalid = Nvalid + 1;
+
+  gzclose (gz);
+  free (buffer);
+  return (TRUE);
+}
+
+# define NBYTE_PMM_TABLE 106
+# define FILE_ID_INDEX  44
+# define DATE_INDEX     52
+# define TIME_INDEX     62
+# define RA_INDEX       68
+# define DEC_INDEX      75
+# define EMULSION_INDEX 83
+# define FILTER_INDEX   89
+# define EXPTIME_INDEX  89
+
+// these are a guess...
+# define PLATE_NX 26500
+# define PLATE_NY 26500
+PhotCode *LoadMetadataPMM (char *datafile, Image *image) {
+
+  PhotCode *photcode;
+  char fileID[8], date[10], timestr[6], RA[7], DEC[8], emulsion[6], filter[7], EXPTIME[4];
+  char line[NBYTE_PMM_TABLE+1];
+  FILE *f;
+
+  if (!PMM_CCD_TABLE) abort ();
+
+  strncpy (fileID, datafile, 7);
+  fileID[7] = 0;
+
+  f = fopen (PMM_CCD_TABLE, "r");
+  if (f == NULL) {
+    fprintf (stderr, "unable to open PMM table: %s\n", PMM_CCD_TABLE);
+    exit (2);
+  }
+  
+  while (fread (line, 1, NBYTE_PMM_TABLE, f) == NBYTE_PMM_TABLE) {
+    line[NBYTE_PMM_TABLE] = 0;
+    
+    if (strncmp (fileID, &line[FILE_ID_INDEX], 7)) continue;
+    
+    strncpy (date, &line[DATE_INDEX], 9);
+    date[9] = 0;
+
+    strncpy (timestr, &line[TIME_INDEX], 5);
+    timestr[5] = 0;
+
+    strncpy (RA, &line[RA_INDEX], 6);
+    RA[6] = 0;
+
+    strncpy (DEC, &line[DEC_INDEX], 7);
+    DEC[7] = 0;
+
+    strncpy (emulsion, &line[EMULSION_INDEX], 5);
+    emulsion[5] = 0;
+
+    strncpy (filter, &line[FILTER_INDEX], 6);
+    filter[6] = 0;
+
+    strncpy (EXPTIME, &line[EXPTIME_INDEX], 3);
+    EXPTIME[3] = 0;
+
+    image[0].tzero   = pmm_date_to_sec (date, timestr);
+    image[0].exptime = atof(EXPTIME)*60.0;
+
+    photcode = pmm_get_photcode (emulsion, filter);
+    image[0].photcode = photcode[0].code;
+
+    // XXX for now, we define a totally fake coordinate system centered on the plate center
+    strcpy (image[0].coords.ctype, "RA---TAN");
+    
+    image[0].coords.crval1  = pmm_get_ra (RA);
+    image[0].coords.crval2  = pmm_get_dec (DEC);
+
+    coords_precess (&image[0].coords.crval1, &image[0].coords.crval2, 1950.0, 2000.0);
+
+    image[0].coords.crpix1 = 0.5*PLATE_NX;
+    image[0].coords.crpix2 = 0.5*PLATE_NY;
+    image[0].coords.cdelt1 = image[0].coords.cdelt2 = 0.9 / 3600.0;
+
+    image[0].coords.pc1_1 = 0.0;
+    image[0].coords.pc1_2 = 1.0;
+    image[0].coords.pc2_1 = 1.0;
+    image[0].coords.pc2_2 = 0.0;
+
+    image[0].coords.Npolyterms = 0;
+    memset (image[0].coords.polyterms, 0, 2*7*sizeof(float));
+
+    image[0].NX = PLATE_NX;
+    image[0].NY = PLATE_NY;
+
+    image[0].cerror = 0.0;
+ 
+    image[0].apmifit = 0.0;
+    image[0].dapmifit = 0.0;
+    image[0].detection_limit = 0.0; 
+    image[0].saturation_limit = 0.0;
+    image[0].fwhm_x = 0.0;
+    image[0].fwhm_y = 0.0;
+
+    // XXX need to determine long & lat for observatories
+    // jd = ohana_sec_to_jd (image[0].tzero);
+    // image[0].sidtime  = ohana_lst (jd, Longitude);
+    // image[0].latitude = Latitude;
+    // altaz (&alt, &az, 15.0*image[0].sidtime - image[0].coords.crval1, image[0].coords.crval2, Latitude);
+
+    image[0].trate = 0.0;
+    image[0].secz = 1.0;
+    image[0].ccdnum = 0;
+
+    // secz is in units milli-airmass
+    image[0].Mcal = 0.0;
+    image[0].Xm   = NAN_S_SHORT;
+    image[0].code = 0;
+
+    image[0].nstar = 0;
+  
+    image[0].imageID  = 0;
+    image[0].externID = 0;
+    image[0].sourceID = 0;
+
+    // save the filename
+    snprintf (image[0].name, DVO_IMAGE_NAME_LEN, "%s", datafile);
+    return photcode;
+  }
+  fprintf (stderr, "failed to match image!\n");
+  abort ();
+}
+
+/* emulsion / filter combinations:
+   098 RED 
+   098-0 RED70 
+   098-0 RG630 
+   103AD MULTI 
+   103AD YEL3 
+   103AD YEL8 
+   103AE #12 
+   103AE AMB2 
+   103AE AMB3 
+   103AE AMB4 
+   103AE AMB5 
+   103AE AMB6 
+   103AE AMB7 
+   103AE AMB8 
+   103AE NONE 
+   103AE RED66 
+   103AE RED67 
+   103AE RED68 
+   103AE RED69 
+   103AE RED70 
+   103AE RED71 
+   103AE RED73 
+   103AE RG2444 
+   103AE RP2444 
+   103AO NONE 
+   IIIAF OG590 
+   IIIAF RG600 
+   IIIAF RG610 
+   IIIAF RG630 
+   IIIAJ GG358 
+IIIAJ GG385 
+IIIAJ GG395 
+IVN RG715 
+IVN RG9 
+IVN WR88A 
+*/
+
+// date in format DDMonYYYY
+// time in format HH:MM
+time_t pmm_date_to_sec (char *date, char *time) {
+  
+  time_t second;
+  double jd;
+  struct tm now;
+  char *p1, *p2;
+  
+  bzero (&now, sizeof(now));
+
+  p1 = date;
+  now.tm_mday = strtod (p1, &p2);
+  assert (p2 == p1 + 2);
+  
+  // month runs from 0 - 11
+  p1 = date + 2;
+  if (!strncasecmp (p1, "JAN", 3)) { now.tm_mon =  0; goto got_month; }
+  if (!strncasecmp (p1, "FEB", 3)) { now.tm_mon =  1; goto got_month; }
+  if (!strncasecmp (p1, "MAR", 3)) { now.tm_mon =  2; goto got_month; }
+  if (!strncasecmp (p1, "APR", 3)) { now.tm_mon =  3; goto got_month; }
+  if (!strncasecmp (p1, "MAY", 3)) { now.tm_mon =  4; goto got_month; }
+  if (!strncasecmp (p1, "JUN", 3)) { now.tm_mon =  5; goto got_month; }
+  if (!strncasecmp (p1, "JUL", 3)) { now.tm_mon =  6; goto got_month; }
+  if (!strncasecmp (p1, "AUG", 3)) { now.tm_mon =  7; goto got_month; }
+  if (!strncasecmp (p1, "SEP", 3)) { now.tm_mon =  8; goto got_month; }
+  if (!strncasecmp (p1, "OCT", 3)) { now.tm_mon =  9; goto got_month; }
+  if (!strncasecmp (p1, "NOV", 3)) { now.tm_mon = 10; goto got_month; }
+  if (!strncasecmp (p1, "DEC", 3)) { now.tm_mon = 11; goto got_month; }
+  fprintf (stderr, "error interpretting month: %s\n", date);
+  abort ();
+
+got_month:
+  p1 = date + 5;
+  now.tm_year = strtod (p1, &p2);
+  assert (p2 == p1 + 4);
+
+  p1 = time;
+  now.tm_hour = strtod (p1, &p2);
+  assert (p2 == p1 + 2);
+
+  p1 = time + 3;
+  now.tm_min = strtod (p1, &p2);
+  assert (p2 == p1 + 2);
+
+  jd = now.tm_mday - 32075 + (int)(1461*(now.tm_year + 4800 + (int)(((now.tm_mon+1)-14)/12))/4)
+    + (int)(367*((now.tm_mon+1) - 2 - (int)(((now.tm_mon+1) - 14)/12)*12)/12)
+    - (int)(3*(int)((1900 + now.tm_year + 4900 + (int)(((now.tm_mon+1) - 14)/12))/100)/4) - 0.5;
+  
+  second = (jd - 2440587.5)*86400 + 3600.0*now.tm_hour + now.tm_min*60.0 + now.tm_sec;
+
+  return (second);
+}
+
+// RA in format HHMMSS
+double pmm_get_ra (char *RA) {
+  
+  char tmp[3];
+  double h, m, s, ra;
+  
+  strncpy (tmp, &RA[0], 2);
+  tmp[2] = 0;
+  h = atof (tmp);
+
+  strncpy (tmp, &RA[2], 2);
+  tmp[2] = 0;
+  m = atof (tmp);
+
+  strncpy (tmp, &RA[4], 2);
+  tmp[2] = 0;
+  s = atof (tmp);
+
+  ra = 15.0 * (h + m / 60.0 + s / 3600.0);
+  return (ra);
+}
+
+// DEC in format sDDMMSS
+double pmm_get_dec (char *DEC) {
+
+  char tmp[3];
+  double d, m, s, dec;
+
+  strncpy (tmp, &DEC[1], 2);
+  tmp[2] = 0;
+  d = atof (tmp);
+
+  strncpy (tmp, &DEC[3], 2);
+  tmp[2] = 0;
+  m = atof (tmp);
+
+  strncpy (tmp, &DEC[5], 2);
+  tmp[2] = 0;
+  s = atof (tmp);
+
+  dec = d + m / 60.0 + s / 3600.0;
+
+  if (tmp[0] == '-') dec *= -1.0;
+  return (dec);
+}
+
+PhotCode *pmm_get_photcode (char *emulsion, char *filter) {
+
+  PhotCode *photcode;
+  char codename[32];
+
+  /* emulsion / filter combinations */
+  if (!strcmp(emulsion, "098  ") && !strcmp(filter, "RED   ")) { strcpy (codename, "USNO.098.RED");      goto got_photcode; }
+  if (!strcmp(emulsion, "098-0") && !strcmp(filter, "RED70 ")) { strcpy (codename, "USNO.098-0.RED70");  goto got_photcode; }
+  if (!strcmp(emulsion, "098-0") && !strcmp(filter, "RG630 ")) { strcpy (codename, "USNO.098-0.RG630");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AD") && !strcmp(filter, "MULTI ")) { strcpy (codename, "USNO.103AD.MULTI");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AD") && !strcmp(filter, "YEL3  ")) { strcpy (codename, "USNO.103AD.YEL3");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AD") && !strcmp(filter, "YEL8  ")) { strcpy (codename, "USNO.103AD.YEL8");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "#12   ")) { strcpy (codename, "USNO.103AE.#12");    goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB2  ")) { strcpy (codename, "USNO.103AE.AMB2");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB3  ")) { strcpy (codename, "USNO.103AE.AMB3");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB4  ")) { strcpy (codename, "USNO.103AE.AMB4");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB5  ")) { strcpy (codename, "USNO.103AE.AMB5");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB6  ")) { strcpy (codename, "USNO.103AE.AMB6");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB7  ")) { strcpy (codename, "USNO.103AE.AMB7");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB8  ")) { strcpy (codename, "USNO.103AE.AMB8");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "NONE  ")) { strcpy (codename, "USNO.103AE.NONE");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED66 ")) { strcpy (codename, "USNO.103AE.RED66");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED67 ")) { strcpy (codename, "USNO.103AE.RED67");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED68 ")) { strcpy (codename, "USNO.103AE.RED68");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED69 ")) { strcpy (codename, "USNO.103AE.RED69");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED70 ")) { strcpy (codename, "USNO.103AE.RED70");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED71 ")) { strcpy (codename, "USNO.103AE.RED71");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED73 ")) { strcpy (codename, "USNO.103AE.RED73");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RG2444")) { strcpy (codename, "USNO.103AE.RG2444"); goto got_photcode; } 
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RP2444")) { strcpy (codename, "USNO.103AE.RP2444"); goto got_photcode; } 
+  if (!strcmp(emulsion, "103AO") && !strcmp(filter, "NONE  ")) { strcpy (codename, "USNO.103AO.NONE");   goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "OG590 ")) { strcpy (codename, "USNO.IIIAF.OG590");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "RG600 ")) { strcpy (codename, "USNO.IIIAF.RG600");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "RG610 ")) { strcpy (codename, "USNO.IIIAF.RG610");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "RG630 ")) { strcpy (codename, "USNO.IIIAF.RG630");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAJ") && !strcmp(filter, "GG358 ")) { strcpy (codename, "USNO.IIIAJ.GG358");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAJ") && !strcmp(filter, "GG385 ")) { strcpy (codename, "USNO.IIIAJ.GG385");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAJ") && !strcmp(filter, "GG395 ")) { strcpy (codename, "USNO.IIIAJ.GG395");  goto got_photcode; }
+  if (!strcmp(emulsion, "IVN  ") && !strcmp(filter, "RG715 ")) { strcpy (codename, "USNO.IVN.RG715");    goto got_photcode; }
+  if (!strcmp(emulsion, "IVN  ") && !strcmp(filter, "RG9   ")) { strcpy (codename, "USNO.IVN.RG9");      goto got_photcode; }
+  if (!strcmp(emulsion, "IVN  ") && !strcmp(filter, "WR88A ")) { strcpy (codename, "USNO.IVN.WR88A");    goto got_photcode; }
+  fprintf (stderr, "error interpretting emulsion and filter: %s, %s\n", emulsion, filter);
+  abort ();
+
+got_photcode:
+  photcode = GetPhotcodebyName (codename);
+  if (photcode == NULL) {
+    fprintf (stderr, "unknown photcode %s\n", codename);
+    abort ();
+  }    
+
+  return photcode;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadDataSDSS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadDataSDSS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadDataSDSS.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "addstar.h"
+
+// examine the header sets and set the Image entries for the the valid images
+// there should only be a single data set (phu + table) in this file
+// each SDSS data set corresponds to 5 images (ugriz)
+int LoadDataSDSS (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars, Header **headers, int *extsize, HeaderSet *headerSets, int Nimages) {
+
+  char *name;
+  int j, Nvalid, Nhead, Ndata, Nskip;
+  unsigned int Ninstars;
+  Stars *inStars;
+
+  if (images[0] == NULL) {
+    Nvalid = 0;
+    NVALID = 5;
+    ALLOCATE (images[0], Image, NVALID);
+  } else {
+    Nvalid = *nvalid;
+    NVALID = Nvalid + 5;
+    REALLOCATE (images[0], Image, NVALID);
+  }    
+
+  // find image rootname
+  name = filebasename (file);
+
+  // there is only one SDSS image per file (TRUE?)
+  Nhead = headerSets[0].extnum_head;
+
+  // XXX parse the information needed from the PHU header
+  if (VERBOSE) fprintf (stderr, "reading header for %s (%s)\n", headerSets[0].exthead, headerSets[0].extdata);
+
+  // advance the pointer to the start of the corresponding table block
+  Ndata = headerSets[0].extnum_data;
+  Nskip = 0;
+  for (j = 0; j < Ndata; j++) {
+    Nskip += extsize[j];
+  }
+  fseek (f, Nskip, SEEK_SET); 
+	 
+  // XXX I think this is an error?  should this be &images[0][Nvalid] ??
+  inStars = ReadStarsSDSS (f, name, headers[Nhead], headers[Ndata], images[0], &Nvalid, &Ninstars);
+  *stars = MergeStars (*stars, Nstars, inStars, Ninstars);
+
+  free (name);
+  *nvalid = Nvalid;
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadHeaders.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadHeaders.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadHeaders.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "addstar.h"
+
+// load all of the headers, jump in file to skip data segments
+Header **LoadHeaders (FILE *f, int *mode, int *Nheaders) {
+
+  int i, status, Nskip, NHEADERS;
+  Header **headers;
+
+  /* we need to examine the extensions to determine the headers and the data */
+  NHEADERS = 10;
+  ALLOCATE (headers, Header *, NHEADERS);
+
+  // load all headers into memory
+  for (i = 0;; i++) {
+    ALLOCATE (headers[i], Header, 1);
+    status = gfits_fread_header (f, headers[i]);
+    if (!status) { 
+      *Nheaders = i;
+      return (headers);
+    }
+
+    // check the mode for this file
+    if (i == 0) {
+      *mode = GetFileMode (headers[0]);
+      if ((*mode == SIMPLE_CMP) || (*mode == MOSAIC_CMP)) {
+	*Nheaders = i;
+	return (headers);
+      }
+    }
+
+    // advance to the next header
+    Nskip = gfits_data_size (headers[i]);
+    fseek (f, Nskip, SEEK_CUR); 
+    if (i == NHEADERS - 1) {
+      NHEADERS += 10;
+      REALLOCATE (headers, Header *, NHEADERS);
+    }
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadStars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadStars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/LoadStars.c	(revision 22322)
@@ -0,0 +1,118 @@
+# include "addstar.h"
+
+Stars *LoadStars (char *filename, int *Nstars, Image **images, int *Nimages, AddstarClientOptions *options) {
+
+  int i, Nfile, NFILE, Nheaders, NheaderSets, mode, *extsize;
+  char **file, line[1024];
+  FILE *f;
+  glob_t globList;
+  Header **headers;
+  Stars *stars;
+  HeaderSet *headerSets;
+
+  if (options[0].filelist) {
+    // read the list of input files from the supplied file
+    f = fopen (filename, "r");
+    if (f == NULL) {
+      fprintf (stderr, "can't read input list %s, giving up\n", filename);
+      exit (1);
+    }
+
+    NFILE = 10;
+    ALLOCATE (file, char *, NFILE);
+    for (i = 0; (fscanf (f, "%s", line) != EOF); i++) {
+      // filename limited to 1024 chars
+      fprintf (stderr, "file: %s\n", line);
+      file[i] = strcreate (line);
+      if (i == NFILE - 1) {
+	NFILE += 10;
+	REALLOCATE (file, char *, NFILE);
+      }
+    }
+    Nfile = i;
+  } else {
+    // parse the filename as a glob
+    globList.gl_offs = 0;
+    glob (filename, 0, NULL, &globList);
+
+    // if the glob does not match, save the literal word:
+    // otherwise save all glob matches
+    if (globList.gl_pathc == 0) {
+      Nfile = 1;
+      ALLOCATE (file, char *, Nfile);
+      file[0] = strcreate (filename);
+    } else {
+      Nfile = globList.gl_pathc;
+      ALLOCATE (file, char *, Nfile);
+      for (i = 0; i < Nfile; i++) {
+	file[i] = strcreate (globList.gl_pathv[i]);
+      }
+    }
+  }
+
+  *Nimages = 0;
+  *Nstars = 0;
+  *images = NULL;
+  stars = NULL;
+
+  for (i = 0; i < Nfile; i++) {
+    f = fopen (file[i], "r");
+    if (f == NULL) {
+      fprintf (stderr, "can't read file %s, skipping\n", file[i]);
+      continue;
+    }
+
+    if (PMM_CCD_TABLE != NULL) {
+      LoadDataPMM (f, file[i], images, Nimages, &stars, Nstars);
+      continue;
+    }
+
+    headers = LoadHeaders (f, &mode, &Nheaders);
+    headerSets = MatchHeaders (&extsize, &NheaderSets, mode, headers, Nheaders);
+    if (headerSets == NULL) {
+      fprintf (stderr, "ERROR: can't read headers for %s\n", file[i]);
+      continue;
+    }
+    if (NheaderSets == 0) {
+      fprintf (stderr, "no object data in file %s, skipping\n", file[i]);
+      continue;
+    }
+    if (VERBOSE) fprintf (stderr, "file %s has %d headers, including %d images\n", file[i], Nheaders, NheaderSets);
+
+    /* supplied photcode is incompatible with multi-chip images */
+    if ((NheaderSets > 1) && options[0].photcode) {
+      fprintf (stderr, "ERROR: photcode cannot be supplied to multi-chip images -- manually adjust the headers\n");
+      exit (1);
+    }
+
+    if (headerSets[0].exttype && !strcmp (headerSets[0].exttype, "SDSS_OBJ")) {
+      LoadDataSDSS (f, file[i], images, Nimages, &stars, Nstars, headers, extsize, headerSets, NheaderSets);
+      continue;
+    }
+
+    LoadData (f, file[i], images, Nimages, &stars, Nstars, headers, extsize, headerSets, NheaderSets);
+
+    // XXX add a function to (optionally) load the extended source measurements
+    # if (0)
+    if (extSources) {
+      // not sure how to link the measurements here to the psf measurements above (though there is an ID in the det list)
+      LoadDataXSRC (f, file[i], images, Nimages, &stars, Nstars, headers, extsize, headerSets, NheaderSets);
+    }
+    if (extFits) {
+      LoadDataXFIT (f, file[i], images, Nimages, &stars, Nstars, headers, extsize, headerSets, NheaderSets);
+    }
+    # endif
+
+  }
+
+  if (*Nimages == 0) {
+    if (Nfile == 1) 
+      fprintf (stderr, "no valid image data in any of these files, giving up\n");
+    else 
+      fprintf (stderr, "no valid image data in this file, giving up\n");
+    exit (0);
+  }
+
+  return stars;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/MatchHeaders.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/MatchHeaders.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/MatchHeaders.c	(revision 22322)
@@ -0,0 +1,104 @@
+# include "addstar.h"
+
+// XXX largely psphot specific
+
+HeaderSet *MatchHeaders (int **extsize, int *nimage, int mode, Header **headers, int Nheaders) {
+
+  int i, j, Nimage, NIMAGE;
+  char extname[80], exttype[80], exthead[80];
+  HeaderSet *headerSets;
+
+  ALLOCATE (extsize[0], int, Nheaders);
+
+  Nimage = 0;
+  NIMAGE = 10;
+  ALLOCATE (headerSets, HeaderSet, NIMAGE);
+
+  // what is the mode of the first header (ie, do we have a PHU DIS image?)
+  mode = GetFileMode (headers[0]);
+
+  if (mode == MOSAIC_MEF) {
+    headerSets[Nimage].exthead     = strcreate ("PHU");
+    headerSets[Nimage].extdata     = strcreate ("NONE");
+    headerSets[Nimage].extnum_data = -1;
+    headerSets[Nimage].extnum_head =  0;
+    Nimage ++;
+  }
+
+  if (mode == SDSS_OBJ) {
+    // XXX these should have two headers (phu + table)
+    assert (Nheaders == 2);
+    headerSets[0].extdata     = strcreate ("SDSS_OBJ");
+    headerSets[0].exttype     = strcreate ("SDSS_OBJ");
+    headerSets[0].exthead     = strcreate ("PHU");
+    headerSets[0].extnum_head = 0;
+    headerSets[0].extnum_data = 1;
+    extsize[0][0] = headers[0][0].size;
+    *nimage = 1;
+    return headerSets;
+  }
+
+  // now examine the headers, count the table entries, find corresponding headers
+  for (i = 0; i < Nheaders; i++) {
+    if (mode == SIMPLE_CMP) {
+      extsize[0][i] = headers[i][0].size;
+    } else {
+      extsize[0][i] = headers[i][0].size + gfits_data_size (headers[i]);
+    }
+
+    gfits_scan (headers[i], "EXTTYPE", "%s", 1, exttype);
+    if (!strcmp (exttype, "SMPDATA")) goto keep;
+    if (!strcmp (exttype, "PS1_DEV_0")) goto keep;
+    if (!strcmp (exttype, "PS1_DEV_1")) goto keep;
+    continue;
+
+  keep:
+    headerSets[Nimage].exttype = strcreate (exttype);
+
+    gfits_scan (headers[i], ExtnameKeyword, "%s", 1, extname);
+    gfits_scan (headers[i], "EXTHEAD", "%s", 1, exthead);
+
+    headerSets[Nimage].extdata     = strcreate (extname);
+    headerSets[Nimage].exthead     = strcreate (exthead);
+    headerSets[Nimage].extnum_data = i;
+    headerSets[Nimage].extnum_head = -1;
+
+    // find the matching exthead entry
+    for (j = 0; j < Nheaders; j++) {
+      if (!gfits_scan (headers[j], ExtnameKeyword, "%s", 1, extname)) continue;
+      if (strcmp (extname, headerSets[Nimage].exthead)) continue;
+      headerSets[Nimage].extnum_head = j;
+      break;
+    }
+
+    // skip or crash on table with missing matching header?
+    if (headerSets[Nimage].extnum_head == -1) {
+      return NULL;
+    }
+    Nimage ++;
+    if (Nimage == NIMAGE) {
+      NIMAGE += 10;
+      REALLOCATE (headerSets, HeaderSet, NIMAGE);
+    }
+  }
+
+  // some old format files did not write EXTTYPE.  they have a single table in the first
+  // extension matched to the header in the PHU
+  if (Nimage == 0) {
+    extsize[0][0] = headers[0][0].size + gfits_data_size (headers[0]);
+    extsize[0][1] = headers[1][0].size + gfits_data_size (headers[1]);
+    gfits_scan (headers[1], ExtnameKeyword, "%s", 1, extname);
+    if (!strcmp (extname, "SMPFILE")) {
+      headerSets[Nimage].extdata     = strcreate (extname);
+      headerSets[Nimage].exttype     = strcreate ("SMPDATA");
+      headerSets[Nimage].exthead     = strcreate ("PHU");
+      headerSets[Nimage].extnum_head = 0;
+      headerSets[Nimage].extnum_data = 1;
+      Nimage = 1;
+    }
+  }
+  
+  *nimage = Nimage;
+  return (headerSets);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewImage.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "addstar.h"
+
+int NewImage (int BindSocket) {
+
+  int N, Nstars, Nimages;
+  Stars *stars;
+  Image *images;
+  Coords *mosaic;
+  AddstarClientOptions *options;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_Image (BindSocket, &images, &Nimages)) {
+    fprintf (stderr, "error: problem receiving image data\n");
+    goto reject;
+  }
+
+  if (options[0].mosaic) {
+    if (!Recv_Coords (BindSocket, &mosaic, &N)) {
+      fprintf (stderr, "error: problem receiving mosaic coordinates\n");
+      goto reject;
+    }
+    if (N != 1) {
+      fprintf (stderr, "error: invalid number of mosaic coords (%d)\n", N);
+      goto reject;
+    }
+  }    
+
+  if (!Recv_Stars (BindSocket, &stars, &Nstars)) {
+    fprintf (stderr, "error: problem receiving star data\n");
+    goto reject;
+  }
+  fprintf (stderr, "accepted %d images, %d stars\n", Nimages, Nstars);
+
+  /* add to db */
+  UpdateDatabase_Image (options, images, Nimages, mosaic, stars, Nstars);
+
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewImage_Thread.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewImage_Thread.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewImage_Thread.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "addstar.h"
+
+int NewImage_Thread (int BindSocket) {
+
+  int N, Nstars, Nimages;
+  Stars *stars;
+  Image *images;
+  Coords *mosaic;
+  AddstarClientOptions *options;
+  DVO_DATA *dataset;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_Image (BindSocket, &images, &Nimages)) {
+    fprintf (stderr, "error: problem receiving image data\n");
+    goto reject;
+  }
+
+  if (options[0].mosaic) {
+    if (!Recv_Coords (BindSocket, &mosaic, &N)) {
+      fprintf (stderr, "error: problem receiving mosaic coordinates\n");
+      goto reject;
+    }
+    if (N != 1) {
+      fprintf (stderr, "error: invalid number of mosaic coords (%d)\n", N);
+      goto reject;
+    }
+  }    
+
+  if (!Recv_Stars (BindSocket, &stars, &Nstars)) {
+    fprintf (stderr, "error: problem receiving star data\n");
+    goto reject;
+  }
+  fprintf (stderr, "accepted %d, %d stars\n", Nimages, Nstars);
+
+  /* create new dataset to store the incoming data */
+  ALLOCATE (dataset, DVO_DATA, 1);
+  dataset[0].options = options;
+  dataset[0].patch   = NULL;
+  dataset[0].refcat  = NULL;
+  dataset[0].images  = images;
+  dataset[0].Nimages = Nimages;
+  dataset[0].mosaic  = mosaic;
+  dataset[0].stars   = stars;
+  dataset[0].Nstars  = Nstars;
+
+  /* place on dataset stack */
+  PushDataset (dataset);
+
+  /* close connection, return */
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewRefcat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewRefcat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewRefcat.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "addstar.h"
+
+int NewRefcat (int BindSocket) {
+
+  int N, status;
+  AddstarClientOptions *options;
+  IOBuffer message;
+  SkyRegion *patch;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_SkyRegion (BindSocket, &patch, &N)) {
+    fprintf (stderr, "error: problem receiving patch\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many patches (%d)\n", N);
+    goto reject;
+  }
+
+  status = ExpectMessage (BindSocket, 0.25, &message);
+  if (status != 0) {
+    if (VERBOSE) fprintf (stderr, "failed connection\n");
+    FreeIOBuffer (&message);
+    goto reject;
+  }
+
+  /* add to db */
+  UpdateDatabase_Refcat (options, patch, message.buffer);
+
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewRefcat_Thread.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewRefcat_Thread.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewRefcat_Thread.c	(revision 22322)
@@ -0,0 +1,59 @@
+# include "addstar.h"
+
+int NewRefcat_Thread (int BindSocket) {
+
+  int N, status;
+  AddstarClientOptions *options;
+  IOBuffer message;
+  SkyRegion *patch;
+  DVO_DATA *dataset;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_SkyRegion (BindSocket, &patch, &N)) {
+    fprintf (stderr, "error: problem receiving patch\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many patches (%d)\n", N);
+    goto reject;
+  }
+
+  status = ExpectMessage (BindSocket, 0.25, &message);
+  if (status != 0) {
+    if (VERBOSE) fprintf (stderr, "failed connection\n");
+    FreeIOBuffer (&message);
+    goto reject;
+  }
+
+  /* add to db */
+  UpdateDatabase_Refcat (options, patch, message.buffer);
+
+  /* create new dataset to store the incoming data */
+  ALLOCATE (dataset, DVO_DATA, 1);
+  dataset[0].options = options;
+  dataset[0].patch   = patch;
+  dataset[0].refcat  = message.buffer;
+  dataset[0].images  = NULL;
+  dataset[0].mosaic  = NULL;
+  dataset[0].stars   = NULL;
+  dataset[0].Nstars  = 0;
+
+  /* place on dataset stack */
+  PushDataset (dataset);
+
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewReflist.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewReflist.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewReflist.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "addstar.h"
+
+int NewReflist (int BindSocket) {
+
+  int N, Nstars;
+  Stars *stars;
+  AddstarClientOptions *options;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_Stars (BindSocket, &stars, &Nstars)) {
+    fprintf (stderr, "error: problem receiving star data\n");
+    goto reject;
+  }
+  fprintf (stderr, "accepted %d stars\n", Nstars);
+
+  /* add to db */
+  UpdateDatabase_Reflist (options, stars, Nstars);
+
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewReflist_Thread.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewReflist_Thread.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/NewReflist_Thread.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "addstar.h"
+
+int NewReflist_Thread (int BindSocket) {
+
+  int N, Nstars;
+  Stars *stars;
+  AddstarClientOptions *options;
+  DVO_DATA *dataset;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_Stars (BindSocket, &stars, &Nstars)) {
+    fprintf (stderr, "error: problem receiving star data\n");
+    goto reject;
+  }
+  fprintf (stderr, "accepted %d stars\n", Nstars);
+
+  /* create new dataset to store the incoming data */
+  ALLOCATE (dataset, DVO_DATA, 1);
+  dataset[0].options = options;
+  dataset[0].patch   = NULL;
+  dataset[0].refcat  = NULL;
+  dataset[0].images  = NULL;
+  dataset[0].mosaic  = NULL;
+  dataset[0].stars   = stars;
+  dataset[0].Nstars  = Nstars;
+
+  /* place on dataset stack */
+  PushDataset (dataset);
+
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadImageHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadImageHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadImageHeader.c	(revision 22322)
@@ -0,0 +1,194 @@
+# include "addstar.h"
+
+/* read an image header corresponding to a CMF / CMP data block */
+// XXX need to pass AddstarClientOptions?
+int ReadImageHeader (Header *header, Image *image, int photcode) {
+
+  int Nastro, ccdnum, hour, min, Nx, Ny, haveNx, haveNy;
+  double tmp, sec, Cerror, ZeroPt, FWHM_X, FWHM_Y;
+  char *c, photname[64], line[80];
+
+  // zero out the entire image structure
+  memset (image, 0, sizeof(Image));
+
+  /* get astrometry information */
+  if (!GetCoords (&image[0].coords, header)) {
+    fprintf (stderr, "no astrometric solution in header\n");
+    return (FALSE);
+  }
+  // XXX currently, image uses an unsigned short for NX,XY. this is rather restrictive
+  // and needs to be at least checked.
+  haveNx = gfits_scan (header, "NAXIS1",   "%d", 1, &Nx);
+  haveNy = gfits_scan (header, "NAXIS2",   "%d", 1, &Ny);
+
+  if (!haveNx && !haveNy) {
+      haveNx = gfits_scan (header, "IMNAXIS1",   "%d", 1, &Nx);
+      haveNy = gfits_scan (header, "IMNAXIS2",   "%d", 1, &Ny);
+  }
+
+  if (!haveNx || !haveNy) {
+      fprintf (stderr, "missing image dimensions in header\n");
+      return (FALSE);
+  }
+
+  if ((Nx < 0) || (Nx > 0xffff)) {
+    fprintf (stderr, "WARNING: NX, NY out of range : image boundary will be wrong\n");
+  }
+
+  image[0].NX = Nx;
+  image[0].NY = Ny;
+
+  if (!gfits_scan (header, "TZERO",   "%d",  1, &image[0].tzero) && !ACCEPT_TIME) {
+    image[0].tzero = parse_time (header);
+  }
+
+  /* only load astrometry, NAXIS1,2, and time if this is a MOSAIC_PHU (ctype is ....-DIS) */
+  if (!strcmp (&image[0].coords.ctype[4], "-DIS")) {
+    RegisterMosaic (&image[0].coords);
+    return (TRUE);
+  }
+
+  /* require Nastro > 0 unless or ACCEPT_ASTROM */
+  Nastro = 0;
+  gfits_scan (header, "NASTRO", "%d", 1, &Nastro);
+  Cerror = 0;
+  gfits_scan (header, "CERROR", "%lf", 1, &Cerror);
+  FWHM_X = FWHM_Y = 0;
+  gfits_scan (header, "FWHM_X", "%lf", 1, &FWHM_X);
+  gfits_scan (header, "FWHM_Y", "%lf", 1, &FWHM_Y);
+  if (((Nastro == 0) || (Cerror > MAX_CERROR)) && !ACCEPT_ASTROM) {
+    fprintf (stderr, "bad astrometric solution in header\n");
+    return (FALSE);
+  }
+  if ((MIN_FWHM_X > 0.01) && (FWHM_X < MIN_FWHM_X)) {
+    fprintf (stderr, "bad psf, skipping\n");
+    return (FALSE);
+  }
+  if ((MIN_FWHM_Y > 0.01) && (FWHM_Y < MIN_FWHM_Y)) {
+    fprintf (stderr, "bad psf, skipping\n");
+    return (FALSE);
+  }
+  if (!strcmp (&image[0].coords.ctype[4], "-WRP")) {
+    if (!isRegisteredMosaic()) {
+      fprintf (stderr, "no mosaic for WRP image (use -mosaic)\n");
+      return (FALSE);
+    }
+  } else {
+    /* force image to lie in 0-360 range */
+    while (image[0].coords.crval1 < 0) image[0].coords.crval1 += 360.0;
+    while (image[0].coords.crval1 > 360.0) image[0].coords.crval1 -= 360.0;
+  }
+
+  { 
+    double R, D;
+    /* sanity check on the image coordinates */
+    XY_to_RD (&R, &D, 0.5*Nx, 0.5*Ny, &image[0].coords);
+    if (!finite(R) || !finite(D)) {
+      fprintf (stderr, "corrupted header coordinates, skipping\n");
+      return (FALSE);
+    }
+  }
+    
+  /* CERROR in data file is in arcsec */
+  if (!gfits_scan (header, "CERROR",   "%lf", 1, &tmp)) tmp = 1.0;
+  image[0].cerror = tmp * 50.0;
+ 
+  /* get photcode from header */
+  if (photcode == 0) {
+    if (!gfits_scan (header, "PHOTCODE", "%s", 1, photname)) {
+      fprintf (stderr, "photcode not supplied in header\n");
+      return (FALSE);
+    }
+    photcode = GetPhotcodeCodebyName (photname);
+    if (photcode == 0) {
+      fprintf (stderr, "photcode %s not found in photcode table\n", photname);
+      return (FALSE);
+    }
+  }
+  if (photcode == 0) { 
+    fprintf (stderr, "no valid photcode is supplied\n");
+    return (FALSE);
+  }
+  image[0].photcode = photcode;
+
+  image[0].NX -= XOVERSCAN;
+  image[0].NY -= YOVERSCAN;
+  gfits_scan (header, ExptimeKeyword,  "%lf", 1, &tmp);
+  image[0].exptime = tmp;
+  
+  /*** why are we no longer using APMIFIT?? ***/
+  tmp = 0;
+  /* gfits_scan (header, "APMIFIT",  "%lf", 1, &tmp); */
+  image[0].apmifit = tmp;
+
+  tmp = 0;
+  /* gfits_scan (header, "dAPMIFIT", "%lf", 1, &tmp); */
+  image[0].dapmifit = tmp;
+
+  tmp = 0;
+  gfits_scan (header, "FLIMIT",   "%lf", 1, &tmp);
+  image[0].detection_limit = tmp * 10.0;
+
+  tmp = 0;
+  gfits_scan (header, "FSATUR",   "%lf", 1, &tmp);
+  image[0].saturation_limit = tmp * 10.0;
+
+  tmp = 0;
+  gfits_scan (header, "FWHM_X",   "%lf", 1, &tmp);
+  image[0].fwhm_x = tmp * 25.0 * image[0].coords.cdelt1 * 3600.0;
+
+  tmp = 0;
+  gfits_scan (header, "FWHM_Y",   "%lf", 1, &tmp);
+  image[0].fwhm_y = tmp * 25.0 * image[0].coords.cdelt1 * 3600.0;
+
+  if (STKeyword[0]) {
+    /* get ST (used for airmass calculation) */
+    gfits_scan (header, STKeyword, "%s", 1, line);
+    /* remove ':' characters */
+    for (c = strchr (line, ':'); c != (char *) NULL; c = strchr (line, ':')) { *c = ' '; }
+    sscanf (line, "%d %d %lf", &hour, &min, &sec);
+    image[0].sidtime = hour + min/60.0 + sec/3600.0;
+  } else {
+    double jd;
+    jd = ohana_sec_to_jd (image[0].tzero);
+    image[0].sidtime  = ohana_lst (jd, Longitude);
+  }
+  image[0].latitude = Latitude;
+
+  tmp = 0;
+  if (gfits_scan (header, "TRATE",   "%lf", 1, &tmp)) {
+    image[0].trate = 10000 * tmp;
+  } else {
+    image[0].trate = 0.0;
+  }
+
+  image[0].secz = NAN;
+  if (gfits_scan (header, AirmassKeyword, "%lf", 1, &tmp)) {
+    image[0].secz = tmp;
+  } 
+
+  if (!gfits_scan (header, CCDNumKeyword, "%d", 1, &ccdnum)) {
+    image[0].ccdnum = 0xff;
+  } else {
+    image[0].ccdnum = ccdnum;
+  }
+
+  gfits_scan (header, "ZERO_PT", "%lf", 1, &ZeroPt);
+  if (ZeroPt != GetZeroPoint()) {
+      fprintf (stderr, "inconsistent zero point values: image: %f, config: %f\n", ZeroPt, GetZeroPoint()); 
+      return (FALSE);
+  }
+
+  /* secz is in units milli-airmass */
+  image[0].Mcal = 0.0;
+  image[0].Xm   = NAN_S_SHORT;
+  image[0].code = 0;
+
+  /* find expected number of stars */
+  if (!gfits_scan (header, "NSTARS", "%d", 1, &image[0].nstar) && !NO_STARS) {
+    fprintf (stderr, "can't get NSTARS from header\n");
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadSDSSHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadSDSSHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadSDSSHeader.c	(revision 22322)
@@ -0,0 +1,58 @@
+# include "addstar.h"
+
+// determine relevant image information from the PHU header
+int ReadSDSSHeader (Header *header, Image *image, int photcode) {
+
+  int Nastro, ccdnum, hour, min, Nx, Ny;
+  double tmp, sec, Cerror, ZeroPt;
+  char *c, photname[64], line[80];
+
+  // I need to convert a single obj file into a set of 5 images
+
+  // XXX how do I define the image boundaries?
+  // image[0].coords
+  // image[0].NX, NY
+
+  // image[0].tzero : MJD_U,G,R,I,Z in table header
+
+  // test astrometry quality? 
+  // image[0].cerror : ??
+ 
+  // set photcodes for the 5 images (SDSS_U,G,R,I,Z)
+  photcode = GetPhotcodeCodebyName ("SDSS_U");
+  if (photcode == 0) {
+    fprintf (stderr, "photcode %s not found in photcode table\n", photname);
+    return (FALSE);
+  }
+  image[0].photcode = photcode;
+
+  // calculate this from : C_OBS, TRACKING, and NY
+  image[0].exptime = tmp;
+  
+  // image[0].apmifit = tmp;
+  // image[0].dapmifit = tmp;
+  // image[0].detection_limit 
+  // image[0].saturation_limit
+  // image[0].fwhm_x : SEEING_U, etc in table header
+  // image[0].fwhm_y : SEEING_U, etc in table header
+
+  // XXX longitude and latitude are known for SDSS
+  // jd = ohana_sec_to_jd (image[0].tzero);
+  // image[0].sidtime  = ohana_lst (jd, Longitude);
+  // image[0].latitude = Latitude;
+
+  // image[0].trate : from C_OBS
+  // image[0].secz : ??
+  // image[0].ccdnum : COLNUM?
+
+  // secz is in units milli-airmass
+  image[0].Mcal = 0.0;
+  image[0].Xm   = NAN_S_SHORT;
+  image[0].code = 0;
+  memset (image[0].dummy, 0, sizeof(image[0].dummy));
+
+  // NAXIS2 for table:
+  // image[0].nstar
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadStarsFITS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadStarsFITS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadStarsFITS.c	(revision 22322)
@@ -0,0 +1,228 @@
+# include "addstar.h"
+
+// given a file with the pointer at the start of the table block and the 
+// corresponding image header, load the stars from the table
+Stars *ReadStarsFITS (FILE *f, Header *header, Header *in_theader, unsigned int *nstars) {
+
+  int Nskip, Nstars;
+  char type[80];
+  Header theader;
+  FTable table;
+  Stars *stars;
+  
+  if (in_theader == NULL) {
+    table.header = &theader;
+    if (!gfits_fread_header (f, table.header)) Shutdown ("ERROR: can't read table header");
+  } else {
+    table.header = in_theader;
+    Nskip = in_theader[0].size;
+    fseek (f, Nskip, SEEK_CUR); 
+  }
+
+  /* load the table data */
+  if (!gfits_fread_ftable_data (f, &table)) {
+    fprintf (stderr, "ERROR: can't read table header\n");
+    exit (1);
+  }
+
+  if (!gfits_scan (table.header, "EXTTYPE", "%s", 1, type)) {
+    strcpy (type, "SMPDATA");
+  }
+
+  stars = NULL;
+  if (!strcmp (type, "SMPDATA")) {
+    stars = Convert_SMPDATA (&table, &Nstars);
+  }
+  if (!strcmp (type, "PS1_DEV_0")) {
+    stars = Convert_PS1_DEV_0 (&table, &Nstars);
+  }
+  if (!strcmp (type, "PS1_DEV_1")) {
+    stars = Convert_PS1_DEV_1 (&table, &Nstars);
+  }
+  if (stars == NULL) {
+    fprintf (stderr, "ERROR: invalid table type %s\n", type);
+    exit (1);
+  }
+  // Nstars is not necessarily == *nstars (The former is the number of detections, the
+  // latter are the 'good' detections reported by the photometry system.
+  *nstars = Nstars;
+
+  return stars;
+}
+
+Stars *Convert_SMPDATA (FTable *table, int *nstars) {
+
+  int i, Nstars, swapped;
+  double ZeroPt;
+  Stars *stars = NULL;
+  SMPData *smpdata = NULL;
+
+  swapped = FALSE;
+  smpdata = gfits_table_get_SMPData (table, &Nstars, &swapped);
+  ZeroPt = GetZeroPoint();
+
+  /* XXX we need to check at least the size of the loaded table */
+  // XXX use memset to zero-out the elements before setting
+
+  ALLOCATE (stars, Stars, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    stars[i].X      = smpdata[i].X;
+    stars[i].Y      = smpdata[i].Y;
+
+    if ((smpdata[i].M >= ZeroPt) || isnan(smpdata[i].M)) {
+      stars[i].M    = NAN;
+      stars[i].Map  = NAN;
+    } else {
+      stars[i].M    = smpdata[i].M;
+      stars[i].Map  = smpdata[i].M;
+    }
+
+    stars[i].dM     = smpdata[i].dM*0.001;
+    stars[i].dophot = smpdata[i].dophot;
+
+    stars[i].fx     = smpdata[i].fx;
+    stars[i].fy     = smpdata[i].fy;
+    stars[i].df     = smpdata[i].df;
+  }    
+  *nstars = Nstars;
+  return (stars);
+}
+
+Stars *Convert_PS1_DEV_0 (FTable *table, int *nstars) {
+
+  int i, Nstars;
+  double ZeroPt;
+  Stars *stars;
+  PS1_DEV_0 *ps1data;
+
+  ps1data = gfits_table_get_PS1_DEV_0 (table, &Nstars, NULL);
+  ZeroPt = GetZeroPoint();
+
+  ALLOCATE (stars, Stars, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    stars[i].X       = ps1data[i].X;
+    stars[i].Y       = ps1data[i].Y;
+    stars[i].dX      = ps1data[i].dX;
+    stars[i].dY      = ps1data[i].dY;
+    if ((ps1data[i].M >= 0.0) || isnan(ps1data[i].M)) {
+      stars[i].M     = NAN;
+    } else {
+      stars[i].M     = ps1data[i].M + ZeroPt;
+    }
+    stars[i].dM      = ps1data[i].dM;
+    stars[i].Mpeak   = ps1data[i].Mpeak;
+
+    stars[i].sky     = ps1data[i].sky;
+    stars[i].dsky    = ps1data[i].dSky;
+
+    stars[i].fx      = ps1data[i].fx;
+    stars[i].fy      = ps1data[i].fy;
+    stars[i].df      = ps1data[i].df;
+
+    stars[i].psfChisq = ps1data[i].psfChisq;
+    stars[i].psfQual  = ps1data[i].psfQual;
+
+    stars[i].detID   = ps1data[i].detID;
+
+    /* these are set elsewhere */
+    stars[i].R       = 0.0;
+    stars[i].D       = 0.0;
+    stars[i].dR      = 0.0;
+    stars[i].dD      = 0.0;
+
+    stars[i].uR      = 0.0;
+    stars[i].uD      = 0.0;
+    stars[i].duR     = 0.0;
+    stars[i].duD     = 0.0;
+
+    stars[i].P       = 0.0;
+    stars[i].dP      = 0.0;
+
+    stars[i].Mcal    = 0;
+    stars[i].t       = 0;
+    stars[i].dt      = 0;
+    stars[i].airmass = 0;
+    stars[i].code    = 0;
+    stars[i].found   = 0;
+
+    /* these are not used */
+    stars[i].Map     = NAN;
+    stars[i].dophot  = 0;
+  }    
+  *nstars = Nstars;
+  return (stars);
+}
+
+// XXX I need to make the IPP I/O functions and these functions
+// consistent wrt ZERO_POINT....
+Stars *Convert_PS1_DEV_1 (FTable *table, int *nstars) {
+
+  int i, Nstars;
+  double ZeroPt;
+  Stars *stars;
+  PS1_DEV_1 *ps1data;
+
+  ps1data = gfits_table_get_PS1_DEV_1 (table, &Nstars, NULL);
+  ZeroPt = GetZeroPoint();
+
+  ALLOCATE (stars, Stars, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    stars[i].X       = ps1data[i].X;
+    stars[i].Y       = ps1data[i].Y;
+    stars[i].dX      = ps1data[i].dX;
+    stars[i].dY      = ps1data[i].dY;
+    if ((ps1data[i].M >= 0.0) || isnan(ps1data[i].M)) {
+	stars[i].M   = NAN;
+    } else {
+	stars[i].M   = ps1data[i].M + ZeroPt;
+    }
+    stars[i].dM      = ps1data[i].dM;
+    stars[i].Mpeak   = ps1data[i].Mpeak;
+
+    stars[i].sky     = ps1data[i].sky;
+    stars[i].dsky    = ps1data[i].dSky;
+
+    stars[i].fx      = ps1data[i].fx;
+    stars[i].fy      = ps1data[i].fy;
+    stars[i].df      = ps1data[i].df;
+
+    stars[i].psfChisq  = ps1data[i].psfChisq;
+    stars[i].psfQual   = ps1data[i].psfQual;
+    stars[i].crNsigma  = ps1data[i].crNsigma;
+    stars[i].extNsigma = ps1data[i].extNsigma;
+
+    stars[i].detID     = ps1data[i].detID;
+    stars[i].flags     = ps1data[i].flags;
+
+    // XXX not defined anyway
+    // stars[i].stargal   = ps1data[i].stargal;
+
+    /* these are set elsewhere */
+    stars[i].R       = 0.0;
+    stars[i].D       = 0.0;
+    stars[i].dR      = 0.0;
+    stars[i].dD      = 0.0;
+
+    stars[i].uR      = 0.0;
+    stars[i].uD      = 0.0;
+    stars[i].duR     = 0.0;
+    stars[i].duD     = 0.0;
+
+    stars[i].P       = 0.0;
+    stars[i].dP      = 0.0;
+
+    stars[i].Mcal    = 0;
+    stars[i].t       = 0;
+    stars[i].dt      = 0;
+    stars[i].airmass = 0;
+    stars[i].az      = 0;
+    stars[i].code    = 0;
+    stars[i].found   = 0;
+
+    /* these are not used */
+    stars[i].Map     = NAN;
+    stars[i].dophot  = 0;
+  }    
+  *nstars = Nstars;
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadStarsSDSS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadStarsSDSS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadStarsSDSS.c	(revision 22322)
@@ -0,0 +1,311 @@
+# include "addstar.h"
+# define DVO_IMAGE_NAME_LEN 128
+int SetSDSSFlags (Stars *star, unsigned int flags1, unsigned int flags2);
+
+# define NFILTER 5
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+# define GET_COLUMN_5(NAME,TYPE) \
+  TYPE *NAME; \
+  NAME = (TYPE *) gfits_get_bintable_column_data (table.header, &table, #NAME, type, &Nrow, &Ncol); \
+  assert (NAME); assert (!strcmp (type, #TYPE)); assert (Nrow == Nstars); assert (Ncol == NFILTER);
+
+# define GET_COLUMN_1(NAME,TYPE) \
+  TYPE *NAME; \
+  NAME = (TYPE *) gfits_get_bintable_column_data (table.header, &table, #NAME, type, &Nrow, &Ncol); \
+  assert (NAME); assert (!strcmp (type, #TYPE)); assert (Nrow == Nstars); assert (Ncol == 1);
+
+/* grab named photcode */
+# define NAMED_PHOTCODE_AND_ZP(CODE,ZP,NAME) { \
+  PhotCode *code; \
+  code = GetPhotcodebyName (NAME); \
+  if (code == NULL) { \
+    fprintf (stderr, "ERROR:  photcode %s not found in photcode table\n", NAME); \
+    exit (0); } \
+  CODE = code[0].code; \
+  ZP = 0.001*code[0].C; }
+
+// XXX NOTE : as of 2008.02.27, the zero point is still carried internally in millimags
+
+// given a file with the pointer at the start of the table block and the 
+// corresponding image header, load the stars from the table
+Stars *ReadStarsSDSS (FILE *f, char *name, Header *header, Header *in_theader, Image *images, int *nimages, unsigned int *nstars) {
+
+  int i, j, N, Nskip, Nstars, camcol;
+  char type[80];
+  Header theader;
+  FTable table;
+  Stars *stars;
+  double clockRate, mjd[5], jd, sidtime, alt, az;
+  float seeing[5], photErr[5], zeropt[5], ZeroPt;
+  time_t tzero[5];
+  char filtname[16][5];
+  int photcode[5];
+  int Nrow, Ncol; // used in the GET_COLUMN_1,5 macros above
+  
+  if (in_theader == NULL) {
+    table.header = &theader;
+    if (!gfits_fread_header (f, table.header)) Shutdown ("ERROR: can't read table header");
+  } else {
+    table.header = in_theader;
+    Nskip = in_theader[0].size;
+    fseek (f, Nskip, SEEK_CUR); 
+  }
+
+  /* load the table data */
+  if (!gfits_fread_ftable_data (f, &table)) {
+    fprintf (stderr, "ERROR: can't read table header\n");
+    exit (1);
+  }
+
+  strcpy (filtname[0], "u");
+  strcpy (filtname[1], "g");
+  strcpy (filtname[2], "r");
+  strcpy (filtname[3], "i");
+  strcpy (filtname[4], "z");
+
+  NAMED_PHOTCODE_AND_ZP (photcode[0], zeropt[0], "U_SDSS");
+  NAMED_PHOTCODE_AND_ZP (photcode[1], zeropt[1], "G_SDSS");
+  NAMED_PHOTCODE_AND_ZP (photcode[2], zeropt[2], "R_SDSS");
+  NAMED_PHOTCODE_AND_ZP (photcode[3], zeropt[3], "I_SDSS");
+  NAMED_PHOTCODE_AND_ZP (photcode[4], zeropt[4], "Z_SDSS");
+
+  // various header values needed to calculate per-star data below
+  gfits_scan (header, "C_OBS", "%lf", 1, &clockRate); // value in header is usec / unbinned row
+  clockRate *= 1e-6; // convert to seconds / unbinned row
+
+  gfits_scan (table.header, "MJD_U", "%lf", 1, &mjd[0]);
+  gfits_scan (table.header, "MJD_G", "%lf", 1, &mjd[1]);
+  gfits_scan (table.header, "MJD_R", "%lf", 1, &mjd[2]);
+  gfits_scan (table.header, "MJD_I", "%lf", 1, &mjd[3]);
+  gfits_scan (table.header, "MJD_Z", "%lf", 1, &mjd[4]);
+  tzero[0] = ohana_mjd_to_sec (mjd[0]);
+  tzero[1] = ohana_mjd_to_sec (mjd[1]);
+  tzero[2] = ohana_mjd_to_sec (mjd[2]);
+  tzero[3] = ohana_mjd_to_sec (mjd[3]);
+  tzero[4] = ohana_mjd_to_sec (mjd[4]);
+
+  gfits_scan (table.header, "SEEING_U", "%f", 1, &seeing[0]);
+  gfits_scan (table.header, "SEEING_G", "%f", 1, &seeing[1]);
+  gfits_scan (table.header, "SEEING_R", "%f", 1, &seeing[2]);
+  gfits_scan (table.header, "SEEING_I", "%f", 1, &seeing[3]);
+  gfits_scan (table.header, "SEEING_Z", "%f", 1, &seeing[4]);
+
+  gfits_scan (table.header, "PSFERR_U", "%f", 1, &photErr[0]);
+  gfits_scan (table.header, "PSFERR_G", "%f", 1, &photErr[1]);
+  gfits_scan (table.header, "PSFERR_R", "%f", 1, &photErr[2]);
+  gfits_scan (table.header, "PSFERR_I", "%f", 1, &photErr[3]);
+  gfits_scan (table.header, "PSFERR_Z", "%f", 1, &photErr[4]);
+
+  gfits_scan (header, "CAMCOL", "%d", 1, &camcol); // value in header is usec / unbinned row
+
+  ZeroPt = GetZeroPoint();
+
+  // create a Star entry for each filter and detection
+  Nstars = table.header[0].Naxis[1];
+  ALLOCATE (stars, Stars, NFILTER*Nstars);
+
+  GET_COLUMN_5 (rowc, float);
+  GET_COLUMN_5 (colc, float);
+  GET_COLUMN_5 (sky, float);
+  GET_COLUMN_5 (psfCounts, float);
+  GET_COLUMN_5 (fiberCounts, float);
+  GET_COLUMN_5 (offsetRa, float);
+  GET_COLUMN_5 (offsetDec, float);
+  GET_COLUMN_5 (flags, int);
+  GET_COLUMN_5 (flags2, int);
+
+  GET_COLUMN_5 (prob_psf, float);
+
+  GET_COLUMN_1 (ra, double);
+  GET_COLUMN_1 (dec, double);
+
+  GET_COLUMN_5 (rowcErr, float);
+  GET_COLUMN_5 (colcErr, float);
+  GET_COLUMN_5 (skyErr, float);
+  GET_COLUMN_5 (psfCountsErr, float);
+
+  // the value of stars[].M is supposed to be the instrumental magnitude offset by the
+  // default zero point 25.0 (-2.5*log_10(counts/sec) + ZeroPt).  The magnitude reported
+  // by SDSS is the calibrated mag: -2.5*log_10(counts/sec) + C_0.  Adjust magnitudes to
+  // compensate for the difference.
+
+  for (i = 0; i < Nstars; i++) {
+    for (j = 0; j < NFILTER; j++) {
+      N = NFILTER*i + j;
+      stars[N].X         = colc[N];
+      stars[N].Y         = rowc[N];
+      stars[N].dX        = colcErr[N];
+      stars[N].dY        = rowcErr[N];
+      stars[N].M         = psfCounts[N] + ZeroPt - zeropt[j];
+      stars[N].dM        = psfCountsErr[N];
+      stars[N].Map       = fiberCounts[N] + ZeroPt - zeropt[j];
+      stars[N].Mpeak     = NAN;
+      stars[N].sky       = sky[N]; // adjust this to counts?
+      stars[N].dsky      = skyErr[N];
+      stars[N].fx        = seeing[j]; // reported in arcsec?
+      stars[N].fy        = seeing[j];
+      stars[N].df        = 0.0;
+      stars[N].psfChisq  = prob_psf[N]; // XXX not really the correct value...
+      stars[N].psfQual   = 0.0;
+      stars[N].crNsigma  = 0.0;
+      stars[N].extNsigma = 0.0;
+      stars[N].detID     = N;
+      stars[N].R         = ra[i] + dCOS(dec[i]) * offsetRa[N] / 3600.0;
+      stars[N].D         = dec[i] + offsetDec[N] / 3600.0;
+      stars[N].dR        = NAN;
+      stars[N].dD        = NAN;
+      stars[N].uR        = 0.0; // I don't trust the SDSS proper motions
+      stars[N].uD        = 0.0; // I don't trust the SDSS proper motions
+      stars[N].duR       = 0.0;
+      stars[N].duD       = 0.0;
+      stars[N].P         = 0.0;
+      stars[N].dP        = 0.0;
+      stars[N].Mcal      = 0.0;
+      stars[N].t         = tzero[j] + clockRate*rowc[N]; // time since row 0
+      stars[N].dt        = 53.907456; // is this 2048*clockRate ?
+
+      SetSDSSFlags (&stars[N], flags[N], flags2[N]);
+
+      // longitude and latitude for SDSS:
+      // Latitude 32° 46' 49.30" N, Longitude 105° 49' 13.50" W
+      // longitude = 105.820419312 deg = 7.05469417
+      // latitude = 32.7803611755 deg
+
+      double Longitude = 7.05469417;   // hours (+ = W)
+      double Latitude = 32.7803611755; // degrees
+
+      jd = ohana_sec_to_jd (stars[N].t);
+      sidtime  = 15.0*ohana_lst (jd, Longitude); // sidtime in degrees
+      altaz (&alt, &az, sidtime - stars[N].R, stars[N].D, Latitude);
+
+      stars[N].airmass   = 1.0 / dCOS(90.0 - alt);
+      stars[N].az        = az;
+
+      stars[N].code      = photcode[j];
+      stars[N].found     = -1;
+      stars[N].dophot    = 0;
+    }
+  }    
+
+  for (i = 0; i < NFILTER; i++) {
+
+    N = i + *nimages;
+    
+    // XXX for now, we define a totally fake coordinate system centered on the first listed star
+    strcpy (images[N].coords.ctype, "RA---TAN");
+    
+    images[N].coords.crval1 = stars[0].R;
+    images[N].coords.crval2 = stars[0].D;
+    
+    images[N].coords.crpix1 = stars[0].X;
+    images[N].coords.crpix2 = stars[0].Y;
+    images[N].coords.cdelt1 = images[N].coords.cdelt2 = 0.4 / 3600.0;
+
+    images[N].coords.pc1_1 = 0.0;
+    images[N].coords.pc1_2 = 1.0;
+    images[N].coords.pc2_1 = 1.0;
+    images[N].coords.pc2_2 = 0.0;
+
+    images[N].coords.Npolyterms = 0;
+    memset (images[N].coords.polyterms, 0, 2*7*sizeof(float));
+
+    images[N].NX = 2048;
+    images[N].NY = 1490;
+
+    images[N].tzero = tzero[i];
+    images[N].cerror = 0.0;
+ 
+    // set photcodes for the 5 images (SDSS_U,G,R,I,Z)
+    images[N].photcode = photcode[i];
+
+    // calculate this from : C_OBS, TRACKING, and NY
+    images[N].exptime = 2048*clockRate;
+  
+    images[N].apmifit = 0.0;
+    images[N].dapmifit = 0.0;
+    images[N].detection_limit = 0.0; 
+    images[N].saturation_limit = 0.0;
+    images[N].fwhm_x = seeing[i];
+    images[N].fwhm_y = seeing[i];
+
+    // XXX longitude and latitude are known for SDSS
+    // SDSS is at : Latitude 32° 46' 49.30" N, Longitude 105° 49' 13.50" W
+    // longitude = 105.820419312 deg = 7.05469417
+    // latitude = 32.7803611755 deg
+
+    double Longitude = 7.05469417;   // hours (+ = W)
+    double Latitude = 32.7803611755; // degrees
+
+    jd = ohana_sec_to_jd (images[N].tzero);
+    images[N].sidtime  = ohana_lst (jd, Longitude);
+    images[N].latitude = Latitude;
+
+    altaz (&alt, &az, 15.0*images[N].sidtime - images[N].coords.crval1, images[N].coords.crval2, Latitude);
+
+    images[N].trate = clockRate * 1e-4;
+    images[N].secz = stars[0].airmass;
+    images[N].ccdnum = camcol;
+
+    // secz is in units milli-airmass
+    images[N].Mcal = 0.0;
+    images[N].Xm   = NAN_S_SHORT;
+    images[N].code = 0;
+
+    images[N].nstar = Nstars;
+  
+    images[N].imageID = N;
+    images[N].externID = 0;
+    images[N].sourceID = 0;
+
+    // save the filename
+    snprintf (images[N].name, DVO_IMAGE_NAME_LEN, "%s[%s]", name, filtname[i]);
+  }
+
+  *nimages += NFILTER;
+  *nstars = Nstars*NFILTER;
+  return (stars);
+}
+
+// ha/dec -> alt/az
+int altaz (double *alt, double *az, double ha, double dec, double latitude) {
+
+  double sind, sinh, cosh;
+
+  sind = dSIN (dec) * dSIN (latitude) + dCOS (dec) * dCOS (ha) * dCOS (latitude);
+  *alt  = DEG_RAD * asin (sind);
+
+  sinh = - dCOS (dec) * dSIN (ha);
+  cosh =   dSIN (dec) * dCOS (latitude) - dCOS (dec) * dCOS (ha) * dSIN (latitude);
+
+  *az = DEG_RAD * atan2 (sinh, cosh);
+
+  // I may need to use the parallactic angle to get the right plate rotation...
+  // sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(latitude) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(latitude);
+  // cosh = -dSIN(az) * dSIN(ha) * dSIN(latitude) - dCOS(az) * dCOS(ha);
+  // rot = -DEG_RAD * atan2 (sinh, cosh);
+  return TRUE;
+}
+
+int SetSDSSFlags (Stars *star, unsigned int flags1, unsigned int flags2) {
+
+  // XXX this is wrong, need to roll left to set the correct bit 
+  if (flags1 & 0x00000002) star[0].flags |= 0x0001; // BRIGHT            - 1  1
+  if (flags1 & 0x00000004) star[0].flags |= 0x0002; // EDGE              - 1  2
+  if (flags1 & 0x00000008) star[0].flags |= 0x0004; // BLENDED           - 1  3
+  if (flags1 & 0x00000010) star[0].flags |= 0x0008; // CHILD             - 1  4
+  if (flags1 & 0x00000020) star[0].flags |= 0x0010; // PEAKCENTER        - 1  5
+  if (flags1 & 0x00000040) star[0].flags |= 0x0020; // NODEBLEND         - 1  6
+  if (flags1 & 0x00040000) star[0].flags |= 0x0040; // SATUR             - 1 18
+  if (flags1 & 0x00080000) star[0].flags |= 0x0080; // NOTCHECKED        - 1 19
+  if (flags1 & 0x10000000) star[0].flags |= 0x0100; // BINNED1           - 1 28
+  if (flags1 & 0x20000000) star[0].flags |= 0x0200; // BINNED2           - 1 29
+  if (flags1 & 0x40000000) star[0].flags |= 0x0400; // BINNED4           - 1 30
+  if (flags2 & 0x00000040) star[0].flags |= 0x0800; // LOCAL_EDGE        - 2  7
+  if (flags2 & 0x00000800) star[0].flags |= 0x1000; // INTERP_CENTER     - 2 12
+  if (flags2 & 0x00002000) star[0].flags |= 0x2000; // DEBLEND_NOPEAK    - 2 14
+  if (flags2 & 0x02000000) star[0].flags |= 0x4000; // NOTCHECKED_CENTER - 2 26
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadStarsTEXT.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadStarsTEXT.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/ReadStarsTEXT.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include "addstar.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+Stars *ReadStarsTEXT (FILE *f, unsigned int *nstars) {
+
+  int j, N, Nextra, Ninstar, Nskip, Nbytes, nbytes;
+  int done;
+  char *buffer, *c, *c2;
+  double tmp;
+  double ZeroPt;
+  Stars *stars;
+  
+  ZeroPt = GetZeroPoint();
+
+  /* load in stars by blocks of 1000 */
+  N = 0;
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR) + 1);
+  buffer[BLOCK*BYTES_STAR] = 0;
+  Nextra = 0;
+
+  ALLOCATE (stars, Stars, *nstars);
+
+  while (N < *nstars) {
+    /* load next data block */
+    Nbytes = BYTES_STAR * BLOCK - Nextra;
+    nbytes = fread (&buffer[Nextra], 1, Nbytes, f);
+    if (nbytes == 0) {
+      *nstars = N;
+      return (stars);
+    }
+    nbytes += Nextra;
+
+    /* check line-by-line integrity */
+    c = buffer;
+    done = FALSE;
+    while ((c < buffer + nbytes) && (!done)) { 
+      for (c2 = c; *c2 == '\n'; c2++);
+      if (c2 > c) { /* extra return chars */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	memset (buffer + nbytes, 0, Nskip);
+	if (VERBOSE) fprintf (stderr, "deleted %d extra return chars\n", Nskip);
+      }
+      c2 = strchr (c, '\n');
+      if (c2 == (char *) NULL) {
+	done = TRUE;	
+	continue;
+      }
+      c2++;
+      if ((c2 - c) != BYTES_STAR) { /* bad line, delete it */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	memset (buffer + nbytes, 0, Nskip);
+	if (VERBOSE) fprintf (stderr, "deleted line, %d extra chars\n", Nskip);
+      } else {
+	c = c2;
+      }
+    }
+
+    /* extract data for stars */
+    Ninstar = nbytes / BYTES_STAR;
+    Nextra = nbytes % BYTES_STAR;
+    for (j = 0; (j < Ninstar) && (N < *nstars); j++, N++) {
+      dparse (&stars[N].X,  1, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Y,  2, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].M,  3, &buffer[j*BYTES_STAR]);
+      if ((stars[N].M > ZeroPt) || isnan(stars[N].M)) {
+	stars[N].M = NAN;
+      }
+
+      /* cmp files carry dM in millimags */
+      dparse (&tmp, 4, &buffer[j*BYTES_STAR]);
+      stars[N].dM = 0.001*tmp;
+
+      dparse (&tmp,         5, &buffer[j*BYTES_STAR]);
+      stars[N].dophot = tmp;
+
+      // XXX I've removed the Mgal field from the measure.d table, and am using Map
+      // instead.  DVO has not to date been used to track and study objects which are
+      // extended, but it is about to.  Related to this, I have created the concept of two
+      // extended source attribute tables, to carry the information being measured by the
+      // IPP.
+
+      // dparse (&stars[N].Mgal, 7, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Map,  8, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].fx,   9, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].fy,  10, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].df,  11, &buffer[j*BYTES_STAR]);
+    }
+  }
+  *nstars = N;
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SEDfit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SEDfit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SEDfit.c	(revision 22322)
@@ -0,0 +1,221 @@
+# include "sedstar.h"
+
+// XXX a couple of fixes should speed this up a bit: test
+
+int SEDfitCatalog (Catalog *outcat, Catalog *incat, SEDtable *table) {
+  
+  int i, j, m, n, idx, start, done, row, Nsec, Nfit, Nphot;
+  int Nave, Nmeas, NAVE, NMEAS, Nmodel, Nreq;
+  unsigned short USNOred, USNOblu;
+  float color;
+  int *found, valid, *modelRow, *reqRow;
+
+  SEDtableRow sourceValue, sourceError;
+  SEDfit minFit, testFit;
+
+  Nmodel = Nreq = 0;
+  for (i = 0; i < table[0].Nfilter; i++) {
+    if (table[0].mode[i] == SED_REQ) Nreq ++;
+    if (table[0].mode[i] == SED_MODEL) Nmodel ++;
+  }
+  if (Nmodel < 1) {
+    fprintf (stderr, "no model filter defined\n!");
+    exit (2);
+  }
+  if (Nreq < 1) {
+    fprintf (stderr, "no required filter defined\n!");
+    exit (2);
+  }
+
+  ALLOCATE (modelRow, int, Nmodel);
+  for (j = i = 0; i < table[0].Nfilter; i++) {
+    if (table[0].mode[i] == SED_MODEL) { 
+      modelRow[j] = i;
+      j++;
+    }
+  }
+  ALLOCATE (reqRow, int, Nreq);
+  for (j = i = 0; i < table[0].Nfilter; i++) {
+    if (table[0].mode[i] == SED_REQ) { 
+      reqRow[j] = i;
+      j++;
+    }
+  }
+
+  sourceValue.mags = NULL;
+  sourceError.mags = NULL;
+
+  Nsec = GetPhotcodeNsecfilt ();
+  Nave = outcat[0].Naverage;
+  Nmeas = outcat[0].Nmeasure;
+
+  NAVE = 100;
+  NMEAS = 100;
+  REALLOCATE (outcat[0].average, Average, NAVE);
+  REALLOCATE (outcat[0].secfilt, SecFilt, NAVE*Nsec);
+  REALLOCATE (outcat[0].measure, Measure, NMEAS);
+
+  // artificially set USNOred and blu errors to 0.3
+  USNOred = GetPhotcodeCodebyName ("USNO_RED");
+  USNOblu = GetPhotcodeCodebyName ("USNO_BLUE");
+
+  // create holder for the source data
+  ALLOCATE (sourceValue.mags, float, table[0].Nfilter);
+  ALLOCATE (sourceError.mags, float, table[0].Nfilter);
+  ALLOCATE (found, int, table[0].Nfilter);
+
+  if (PLOT) SEDfitInit (table);
+
+  // perform the fit to all sources
+  for (i = 0; i < incat[0].Naverage; i++) {
+
+    // blank out the source array
+    for (j = 0; j < table[0].Nfilter; j++) {
+      sourceValue.mags[j] = 100;
+      found[j] = FALSE;
+    }	
+
+    // load the measurements for this source
+    m = incat[0].average[i].measureOffset;
+    Nphot = 0;
+    for (j = 0; j < incat[0].average[i].Nmeasure; j++) {
+      idx = table[0].hashcode[incat[0].measure[m+j].photcode];
+      if (idx == -1) continue;
+      // only fit the selected photcodes (mode == "fit")
+      if (table[0].mode[idx] == SED_MODEL) continue; 
+      if (table[0].mode[idx] == SED_SAMPLE) continue; 
+      // XXX do something more clever if more than one value exists per photcode
+      sourceValue.mags[idx] = incat[0].measure[m+j].M + table[0].vegaToAB[idx];
+      sourceError.mags[idx] = incat[0].measure[m+j].dM;
+      if (incat[0].measure[m+j].photcode == USNOred) sourceError.mags[idx] = 0.3;
+      if (incat[0].measure[m+j].photcode == USNOblu) sourceError.mags[idx] = 0.3;
+      found[idx] = TRUE;
+      Nphot ++;
+    }
+    if (Nphot < 3) continue;
+
+    // XXX pre-select list of REQ entries; loop over only those?
+    valid = TRUE;
+    for (j = 0; valid && (j < Nreq); j++) {
+      if ((table[0].mode[reqRow[j]] == SED_REQ) && !found[reqRow[j]]) valid = FALSE;
+    }
+    if (!valid) continue;
+
+    // skip sources without ref color
+    if (sourceValue.mags[table[0].codeP] > 50) continue;
+    if (sourceValue.mags[table[0].codeM] > 50) continue;
+    color = sourceValue.mags[table[0].codeP] - sourceValue.mags[table[0].codeM];
+
+    // find tableRow within 0.1 mag of color 
+    // XXX : check on the delta value
+    start = SEDcolorBracket (table, color, 0.05);
+    minFit = SEDchisq (table[0].row[start], &sourceValue, &sourceError, table[0].Nfilter);
+    minFit.row = start;
+
+    // search for min chisq backwards
+    // XXX : check on the delta value
+    done = FALSE;
+    row = start - 1;
+    while (!done && (row > 0)) {
+      testFit = SEDchisq (table[0].row[row], &sourceValue, &sourceError, table[0].Nfilter);
+      if (testFit.chisq < minFit.chisq) {
+	minFit = testFit;
+	minFit.row = row;
+      }
+      if (fabs(table[0].row[row][0].color - color) > 0.25) done = TRUE;
+      row --;
+    }
+
+    // search for min chisq forwards
+    // XXX : check on the delta value
+    done = FALSE;
+    row = start + 1;
+    while (!done && (row < table[0].Nrow)) {
+      testFit = SEDchisq (table[0].row[row], &sourceValue, &sourceError, table[0].Nfilter);
+      if (testFit.chisq < minFit.chisq) {
+	minFit = testFit;
+	minFit.row = row;
+      }
+      if (fabs(table[0].row[row][0].color - color) > 0.25) done = TRUE;
+      row ++;
+    }
+
+    Nfit ++;
+    // create the vectors for the example plots
+    if (PLOT) SEDfitPlot (table, incat[0].average[i].R, incat[0].average[i].D, &minFit, &sourceValue, &sourceError);
+
+    // construct an average object for this object
+    // XXX for now, the output objects will have limited astrometric interpretation...
+    outcat[0].average[Nave].R         = incat[0].average[i].R;
+    outcat[0].average[Nave].D         = incat[0].average[i].D;
+    outcat[0].average[Nave].dR    = 0;
+    outcat[0].average[Nave].dD    = 0;
+    outcat[0].average[Nave].uR    = 0;
+    outcat[0].average[Nave].uD    = 0;
+    outcat[0].average[Nave].duR   = 0;
+    outcat[0].average[Nave].duD   = 0;
+    outcat[0].average[Nave].P     = 0;
+    outcat[0].average[Nave].dP    = 0;
+
+    // XXX for now, set the average mag data to NULL
+    outcat[0].average[Nave].Nmeasure  	  = 0;
+    outcat[0].average[Nave].Nmissing  	  = 0;
+    outcat[0].average[Nave].Xp        	  = NAN_S_SHORT;
+    outcat[0].average[Nave].measureOffset = Nmeas;
+    outcat[0].average[Nave].missingOffset = -1;
+    outcat[0].average[Nave].code          = 0;
+
+    for (j = 0; j < Nsec; j++) {
+      outcat[0].secfilt[Nave*Nsec+j].M  = NAN;
+      outcat[0].secfilt[Nave*Nsec+j].dM = NAN;
+      outcat[0].secfilt[Nave*Nsec+j].Xm = NAN_S_SHORT;
+    }
+
+    // we now have the min chisq row. use this to supply the other filter values....
+    // XXX pre-select the SED_MODEL rows...
+    if (Nmeas + table[0].Nfilter >= NMEAS) {
+	NMEAS += 100 + table[0].Nfilter;
+	REALLOCATE (outcat[0].measure, Measure, NMEAS);
+    }
+
+    for (j = 0; valid && (j < Nmodel); j++) {
+      n = modelRow[j];
+      outcat[0].measure[Nmeas].dR       = 0.0;
+      outcat[0].measure[Nmeas].dD       = 0.0;
+      outcat[0].measure[Nmeas].M        = table[0].row[minFit.row][0].mags[n] + minFit.Md;
+      outcat[0].measure[Nmeas].dM       = 0.0;
+      outcat[0].measure[Nmeas].Mcal     = 0;
+      outcat[0].measure[Nmeas].t        = TIMEREF;
+      outcat[0].measure[Nmeas].averef   = Nave;
+      outcat[0].measure[Nmeas].photcode = table[0].code[n];
+      outcat[0].measure[Nmeas].dophot   = 0;
+      outcat[0].measure[Nmeas].dbFlags  = 0;
+      outcat[0].measure[Nmeas].dt       = 0xffff;
+
+      outcat[0].measure[Nmeas].airmass  = 0;
+      outcat[0].measure[Nmeas].FWx      = NAN_S_SHORT;
+      outcat[0].measure[Nmeas].FWy      = NAN_S_SHORT;
+      outcat[0].measure[Nmeas].theta    = NAN_S_SHORT;
+
+      outcat[0].average[Nave].Nmeasure++;
+      Nmeas ++;
+    }
+
+    Nave ++;
+    if (Nave >= NAVE) {
+      NAVE += 100;
+      REALLOCATE (outcat[0].average, Average, NAVE);
+      REALLOCATE (outcat[0].secfilt, SecFilt, NAVE*Nsec);
+    }
+  }
+  outcat[0].Naverage  = Nave;
+  outcat[0].Nmeasure  = Nmeas;
+  outcat[0].Nsecf_mem = Nave*Nsec;
+  
+  free (sourceValue.mags);
+  free (sourceError.mags);
+  free (found);
+
+  SEDfitClear ();
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SEDops.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SEDops.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SEDops.c	(revision 22322)
@@ -0,0 +1,249 @@
+# include "sedstar.h"
+
+// fit the data (with errors) to the given table row
+SEDfit SEDchisq (SEDtableRow *ref, SEDtableRow *data, SEDtableRow *error, int Nfilter) {
+
+  int i;
+  double Sm, Sd, S2, wt, dM;
+  SEDfit fit;
+
+  Sm = Sd = S2 = 0.0;
+
+  for (i = 0; i < Nfilter; i++) {
+    if (data[0].mags[i] > 50.0) continue;
+
+    if (error[0].mags[i] == 0.0) {
+      wt = 1.0;
+    } else {
+      wt = 1.0 / SQ(error[0].mags[i]);
+    }
+
+    dM = data[0].mags[i] - ref[0].mags[i];
+    S2 += SQ(dM) * wt;
+    Sm += dM * wt;
+    Sd += wt;
+  }
+    
+  // row is assigned after fit
+  fit.row = -1;
+  fit.Md = Sm / Sd;
+  fit.chisq = S2 + SQ(fit.Md) * Sd - 2*fit.Md*Sm;
+
+  return (fit);
+}
+
+// find the first table row within 0.1 mag of the requested color (or within 10)
+int SEDcolorBracket (SEDtable *table, float color, float delta) {
+
+  int Nlo, Nhi, N;
+  float tcolor;
+
+  N = Nlo = 0; Nhi = table[0].Nrow;
+  tcolor = table[0].row[Nlo][0].color;
+  while ((Nhi - Nlo > 10) && (fabs(tcolor-color) > delta)) {
+    N = 0.5*(Nlo + Nhi);
+    N = MAX (N, 0);
+    N = MIN (N, table[0].Nrow - 1);
+    tcolor = table[0].row[N][0].color;
+    if (tcolor < color) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  return (N);
+}
+
+SEDtableRow **sort_SEDtable (SEDtableRow *raw, int N) {
+
+  int i;
+
+  SEDtableRow **value;
+  
+  if (N <= 0) return (NULL);
+
+  ALLOCATE (value, SEDtableRow *, N);
+  for (i = 0; i < N; i++) {
+    value[i] = &raw[i];
+  }
+
+# define SWAPFUNC(A,B){ SEDtableRow *temp = value[A]; value[A] = value[B]; value[B] = temp; }
+# define COMPARE(A,B)(value[A][0].color < value[B][0].color)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+  return (value);
+}
+
+static Graphdata graphdata;
+static KapaSection magSection, resSection;
+static int Xgraph;
+static float *fitmags, *fiterrs;
+
+int SEDfitInit (SEDtable *table) {
+
+  Xgraph = KiiOpen ("kapa", "sedstar");
+  KapaInitGraph (&graphdata);
+  SetLimitsRaw (table[0].wavecode, NULL, table[0].Nfilter, &graphdata);
+  graphdata.style = 2;
+  graphdata.ptype = 2;
+  KapaClearSections (Xgraph);
+  magSection.name = strcreate ("mag");
+  magSection.x  = 0;
+  magSection.dx = 1;
+  magSection.y  = 0.5;
+  magSection.dy = 0.5;
+  resSection.name = strcreate ("res");
+  resSection.x  = 0.0;
+  resSection.dx = 1.0;
+  resSection.y  = 0.0;
+  resSection.dy = 0.5;
+    
+  KiiResize (Xgraph, 900, 500);
+  KapaSetFont (Xgraph, "helvetica", 14);
+  ALLOCATE (fitmags, float, table[0].Nfilter);
+  ALLOCATE (fiterrs, float, table[0].Nfilter);
+  return (TRUE);
+}
+
+int SEDfitClear () {
+
+  free (fitmags);
+  free (fiterrs);
+  KiiClose (Xgraph);
+  return (TRUE);
+}
+
+int SEDfitPlot (SEDtable *table, double R, double D, SEDfit *minFit, SEDtableRow *sourceValue, SEDtableRow *sourceError) {
+
+  int j, minRow, Nfilter;
+  double X, Y, Z, RA, DEC;
+  char line[1024], key[20];
+
+  minRow = minFit[0].row;
+  Nfilter = table[0].Nfilter;
+
+  // we want to plot the OBSERVED magnitudes
+  for (j = 0; j < Nfilter; j++) {
+    fitmags[j] = table[0].row[minRow][0].mags[j] + minFit[0].Md;
+  }
+
+  // find plot range
+  SetLimitsRaw (NULL, fitmags, Nfilter, &graphdata);
+  SWAP (graphdata.ymin, graphdata.ymax);
+
+  KapaClearSections (Xgraph);
+  KapaSetSection (Xgraph, &magSection);
+  KapaSetLimits (Xgraph, &graphdata);
+  KapaBox (Xgraph, &graphdata);
+  graphdata.color = KapaColorByName ("blue");
+  graphdata.etype = 0;
+  graphdata.ptype = 7;
+  KapaPrepPlot (Xgraph, Nfilter, &graphdata);
+  KapaPlotVector (Xgraph, Nfilter, table[0].wavecode, "x");
+  KapaPlotVector (Xgraph, Nfilter, fitmags, "y");
+
+  graphdata.color = KapaColorByName ("red");
+  graphdata.etype = 1;
+  graphdata.ptype = 2;
+  for (j = 0; j < Nfilter; j++) {
+    fitmags[j] = 100;
+    fiterrs[j] = 0;
+    if (sourceValue[0].mags[j] > 50) continue;
+    fitmags[j] = sourceValue[0].mags[j];
+    fiterrs[j] = sourceError[0].mags[j];
+  }
+  KapaPrepPlot (Xgraph, Nfilter, &graphdata);
+  KapaPlotVector (Xgraph, Nfilter, table[0].wavecode, "x");
+  KapaPlotVector (Xgraph, Nfilter, fitmags, "x");
+  KapaPlotVector (Xgraph, Nfilter, fiterrs, "dym");
+  KapaPlotVector (Xgraph, Nfilter, fiterrs, "dyp");
+  KapaSendLabel (Xgraph, "model,fit (mags)", 1);
+
+  sprintf (line, "star: %10.6f %10.6f  T: %5.0fK  A_V|: %4.2f  M_D|: %5.2f  &sc&h^2|: %5.2f", 
+	   R, D, 
+	   table[0].row[minRow][0].Temp, 
+	   table[0].row[minRow][0].Av, 
+	   minFit[0].Md, minFit[0].chisq);
+  KapaSendLabel (Xgraph, line, 2);
+  KapaSendLabel (Xgraph, "model,fit (mags)", 1);
+
+  KapaSetSection (Xgraph, &resSection);
+  graphdata.ymin = -1.0;
+  graphdata.ymax = +1.0;
+  KapaSetLimits (Xgraph, &graphdata);
+  KapaBox (Xgraph, &graphdata);
+  graphdata.color = KapaColorByName ("red");
+  graphdata.etype = 1;
+
+  for (j = 0; j < Nfilter; j++) {
+    fitmags[j] = 100;
+    fiterrs[j] = 0;
+    if (sourceValue[0].mags[j] > 50) continue;
+    fitmags[j] = sourceValue[0].mags[j] - minFit[0].Md - table[0].row[minRow][0].mags[j];
+    fiterrs[j] = sourceError[0].mags[j];
+  }
+  KapaPrepPlot (Xgraph, Nfilter, &graphdata);
+  KapaPlotVector (Xgraph, Nfilter, table[0].wavecode, "x");
+  KapaPlotVector (Xgraph, Nfilter, fitmags, "y");
+  KapaPlotVector (Xgraph, Nfilter, fiterrs, "dym");
+  KapaPlotVector (Xgraph, Nfilter, fiterrs, "dyp");
+  KapaSendLabel (Xgraph, "wavelength (nm)", 0);
+  KapaSendLabel (Xgraph, "resid (mags)", 1);
+
+  KiiCursorOn (Xgraph);
+  while (KiiCursorRead (Xgraph, &X, &Y, &Z, &RA, &DEC, key)) {
+    // fprintf (stderr, "window: %f %f (%s)\n", X, Y, key);
+    if (!strcasecmp (key, "Q")) {
+      KiiCursorOff (Xgraph);
+      break;
+    }
+    if (!strcasecmp (key, "ESCAPE")) {
+      KiiCursorOff (Xgraph);
+      PLOT = FALSE;
+      return (TRUE);
+    }
+    if (!strcasecmp (key, "X")) {
+      KiiCursorOff (Xgraph);
+      Shutdown ("quitting sedstar");
+    }
+  }
+  return (TRUE);
+}
+
+void SetLimitsRaw (float *xvec, float *yvec, int Nelements, Graphdata *graphmode) {
+
+  double maxX, minX, maxY, minY, range;
+  int i;
+
+  if (xvec != NULL) {
+    maxX = minX = xvec[0];
+    for (i = 1; i < Nelements; i++) {
+      if (!finite(xvec[i])) continue;
+      maxX = MAX (maxX, xvec[i]);
+      minX = MIN (minX, xvec[i]);
+    }
+    range = maxX - minX;
+    if (range == 0) range = 0.001 * maxX;
+    if (range == 0) range = 0.001;
+    graphmode[0].xmin = minX - 0.05*range;
+    graphmode[0].xmax = maxX + 0.05*range;
+  }
+
+  if (yvec != NULL) {
+    maxY = minY = yvec[0];
+    for (i = 1; i < Nelements; i++) {
+      if (!finite(yvec[i])) continue;
+      maxY = MAX (maxY, yvec[i]);
+      minY = MIN (minY, yvec[i]);
+    }
+    range = maxY - minY;
+    if (range == 0) range = 0.0011 * maxY;
+    if (range == 0) range = 0.0011;
+    graphmode[0].ymin = minY - 0.05*range;
+    graphmode[0].ymax = maxY + 0.05*range;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SEDtableLoad.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SEDtableLoad.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SEDtableLoad.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "sedstar.h"
+
+// XXX where do the colorP and colorM codes come from?
+SEDtable *SEDtableLoad (char *filename) {
+
+  FILE *f;
+  char line[1024], name[64], mode[64];
+  char colornameP[64], colornameM[64];
+  int colorP, colorM, code;
+  int i, Nrow, NROW;
+  SEDtable *table;
+  SEDtableRow *raw;
+
+  ALLOCATE (table, SEDtable, 1);
+
+  // load SED table
+  f = fopen (filename, "r");
+  if (f == NULL) Shutdown ("failure to open SED table");
+
+
+  // XXX add error checks for header data
+  scan_line (f, line);
+  sscanf (line, "%*s %*s %d", &table[0].Nfilter);
+
+  // load SED table photcodes, generate the photcode hashtable
+  ALLOCATE (table[0].hashcode, int, 0x10000);
+  ALLOCATE (table[0].wavecode, float, table[0].Nfilter);
+  ALLOCATE (table[0].vegaToAB, float, table[0].Nfilter);
+  ALLOCATE (table[0].mode,     int,   table[0].Nfilter);
+  ALLOCATE (table[0].code,     int,   table[0].Nfilter);
+
+  for (i = 0; i < 0x10000; i++) table[0].hashcode[i] = -1;
+  for (i = 0; i < table[0].Nfilter; i++) {
+    scan_line (f, line);
+    sscanf (line, "%*s %s %f %f %s", name, &table[0].wavecode[i], &table[0].vegaToAB[i], mode);
+    code = GetPhotcodeCodebyName (name);
+    table[0].code[i] = code;
+    if (code == 0) Shutdown ("undefined photcode in SED table");
+    table[0].hashcode[code] = i;
+
+    table[0].mode[i] = -1;     
+    if (!strcasecmp(mode, "fit")) table[0].mode[i] = SED_FIT; 
+    if (!strcasecmp(mode, "req")) table[0].mode[i] = SED_REQ; 
+    if (!strcasecmp(mode, "model")) table[0].mode[i] = SED_MODEL; 
+    if (!strcasecmp(mode, "sample")) table[0].mode[i] = SED_SAMPLE; 
+    if (table[0].mode[i] == -1) Shutdown ("invalid photcode mode in SED table");
+  }
+
+  // load color key
+  scan_line (f, line);
+
+  // define the selection color codes
+  sscanf (line, "# %s - %s", colornameP, colornameM);
+  colorP = GetPhotcodeCodebyName (colornameP);
+  colorM = GetPhotcodeCodebyName (colornameM);
+  table[0].codeP = table[0].hashcode[colorP];
+  table[0].codeM = table[0].hashcode[colorM];
+  if (table[0].codeP == -1) Shutdown ("missing positive color filter");
+  if (table[0].codeM == -1) Shutdown ("missing positive color filter");
+    
+  // load the SED raw table rows
+  Nrow = 0;
+  NROW = 100;
+  ALLOCATE (raw, SEDtableRow, NROW);
+  while (scan_line(f, line) != EOF) {
+    stripwhite (line);
+    if (line[0] == '#') continue;
+    fparse (&raw[Nrow].Temp, 1, line);
+    fparse (&raw[Nrow].Av, 2, line);
+    ALLOCATE (raw[Nrow].mags, float, table[0].Nfilter);
+    for (i = 0; i < table[0].Nfilter; i++) {
+      fparse (&raw[Nrow].mags[i], i + 3, line);
+    }
+    raw[Nrow].color = raw[Nrow].mags[table[0].codeP] - raw[Nrow].mags[table[0].codeM];
+    Nrow ++;
+    CHECK_REALLOCATE (raw, SEDtableRow, NROW, Nrow, 100);
+  }      
+
+  // sort the SEDtable by the reference colors
+  table[0].row = sort_SEDtable (raw, Nrow);
+  table[0].Nrow = Nrow;
+  return (table);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SetSignals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SetSignals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SetSignals.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "addstar.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect these signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored (POSIX.1-1990) */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored (POSIX.1-1990) */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore (POSIX.1-1990) */
+    case SIGCONT:    /* continue - maintain this action (POSIX.1-1990) */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? (POSIX.1-1990) */
+    case SIGURG:     /* socket signal, ignore this (POSIX.1-2001) */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/Shutdown.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/Shutdown.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/Shutdown.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "addstar.h"
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format) + 2);
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  fprintf (stderr, "ERROR: addstar halted\n");
+  exit (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SkyListForStars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SkyListForStars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SkyListForStars.c	(revision 22322)
@@ -0,0 +1,58 @@
+# include "addstar.h"
+
+SkyList *SkyListForStars (SkyTable *table, int depth, Stars *stars, int Nstars) {
+  
+  int i, j, Nr, NR;
+  SkyList *here;
+  SkyList *list;
+  
+  Nr = 0;
+  NR = 10;
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions,  SkyRegion *, NR);
+  ALLOCATE (list[0].filename, char *, NR);
+  list[0].Nregions = Nr;
+  list[0].ownElements = FALSE; // free these elements when freeing the list
+
+  sort_stars_ra (stars, Nstars); /* sort by RA */
+  for (i = 0; i < Nstars; i++) {
+    if (stars[i].found) continue;
+    here = SkyRegionByPoint (table, depth, stars[i].R, stars[i].D);
+    stars[i].found = TRUE;
+    /* search forward for all contained stars */
+    for (j = i; j < Nstars; j++) {
+      if (stars[j].R >= here[0].regions[0][0].Rmax) break;
+      if (stars[j].R <  here[0].regions[0][0].Rmin) break;
+      if (stars[j].D <  here[0].regions[0][0].Dmin) continue;
+      if (stars[j].D >= here[0].regions[0][0].Dmax) continue;
+      stars[j].found = TRUE;
+    }
+    list[0].regions[Nr] = here[0].regions[0];
+    list[0].filename[Nr] = here[0].filename[0];
+    SkyListFree (here); 
+    Nr ++;
+    if (Nr >= NR) {
+	NR += 32;
+	REALLOCATE (list[0].regions, SkyRegion *, NR);
+	REALLOCATE (list[0].filename, char *, NR);
+    }
+    list[0].Nregions = Nr;
+  }
+
+  /* reset to -1 for all stars: required start for find_match_refstars */
+  for (i = 0; i < Nstars; i++) stars[i].found = -1; 
+  return (list);
+}
+
+/* given a list of stars, find all region files which contain them 
+
+   - sort by ra
+   - loop over stars
+     - find region file which contains star
+     - go forwards until ra > Rmax
+       - mark all stars in this region file
+     
+   - use DEC band information?
+   - would like to minimize the number of disk reads
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SkyRegionUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SkyRegionUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SkyRegionUtils.c	(revision 22322)
@@ -0,0 +1,27 @@
+# include "addstar.h"
+
+SkyList *SkyListExistingSubset (SkyList *input, char *path) {
+  
+  int i, status, Nsubset, NSUBSET;
+  SkyList *subset;
+  struct stat filestats;
+  
+  Nsubset = 0;
+  NSUBSET = 100;
+  ALLOCATE (subset, SkyList, 1);
+  ALLOCATE (subset[0].regions, SkyRegion *, NSUBSET);
+  subset[0].ownElements = FALSE; // free these elements when freeing the list
+
+  /* match the basename against the GSCRegion file names */
+  for (i = 0; i < input[0].Nregions; i++) {
+    status = stat (input[0].filename[i], &filestats);
+    if ((status == -1) && (errno == ENOENT)) continue;
+    /* give an error for other conditions? */
+
+    subset[0].regions[Nsubset] = input[0].regions[i];
+    Nsubset ++;
+    CHECK_REALLOCATE (subset[0].regions, SkyRegion *, NSUBSET, Nsubset, 100);
+    subset[0].Nregions = Nsubset;
+  }
+  return (subset);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SkyTableFromTychoIndex.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SkyTableFromTychoIndex.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SkyTableFromTychoIndex.c	(revision 22322)
@@ -0,0 +1,152 @@
+# include "dvo.h"
+# define NBANDS 24
+# define NDIV 4
+
+static double DecBands[] = {0.0, +7.5, +15.0, +22.5, +30.0, +37.5, +45.0, +52.5, +60.0, +67.5, +75.0, +82.5, +90.0,
+			    0.0, -7.5, -15.0, -22.5, -30.0, -37.5, -45.0, -52.5, -60.0, -67.5, -75.0, -82.5, -90.0};
+
+static char *DecNames[] = {"n0000", "n0730", "n1500", "n2230", "n3000", "n3730", "n4500", "n5230", "n6000", "n6730", "n7500", "n8230", "none",
+			   "s0000", "s0730", "s1500", "s2230", "s3000", "s3730", "s4500", "s5230", "s6000", "s6730", "s7500", "s8230", "none"};
+
+SkyTable *SkyTableFromTychoIndex (char *filename, int VERBOSE) {
+
+  int i, No, Nr, NR, Ntycho;
+  double Dmin, Dmax;
+  char line[256];
+  FILE *f;
+  SkyTable *skytable;
+  SkyRegion *regions;
+  SkyRegion *tycho;
+  
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    if (VERBOSE) fprintf (stderr, "can't find Tycho Index file %s\n", filename);
+    return (NULL);
+  }
+
+  /* load in table data */
+  ALLOCATE (tycho, SkyRegion, 10000);
+  for (i = 0; i < 10000; i++) {
+    if (scan_line (f, line) == EOF) break;
+    tycho[i].Rmin = atof (&line[15]);
+    tycho[i].Rmax = atof (&line[22]);
+    tycho[i].Dmin = atof (&line[29]);
+    tycho[i].Dmax = atof (&line[36]);
+
+    memset (tycho[i].name, 0, 21);
+    strncpy (tycho[i].name, line, 7);
+    tycho[i].name[7] = 0;
+  }
+  Ntycho = i;
+  fclose (f);
+
+  /* build supporting level 0 and 1 regions */
+  Nr = 0;
+  NR = 100;
+  ALLOCATE (regions, SkyRegion, 100);
+  
+  /* level 0 : full sky */
+  regions[Nr].Rmin 	=   0;
+  regions[Nr].Rmax 	= 360;
+  regions[Nr].Dmin 	= -90;
+  regions[Nr].Dmax 	= +90;
+  regions[Nr].index  	=  0;
+  regions[Nr].depth  	=  0;
+  regions[Nr].parent 	= -1;
+  regions[Nr].child  	=  TRUE;
+  regions[Nr].table  	=  FALSE;
+  memset (regions[Nr].name, 0, 21);
+  strcpy (regions[Nr].name, "fullsky");
+  
+  No = Nr;
+  Nr ++;
+
+  /* level 1 : add the dec bands */
+  regions[No].childS = Nr;
+  /* first north */
+  for (i = 0; i < 12; i++, Nr++) {
+    regions[Nr].Rmin   	  =   0;
+    regions[Nr].Rmax   	  = 360;
+    regions[Nr].Dmin   	  = DecBands[i];
+    regions[Nr].Dmax   	  = DecBands[i+1];
+    regions[Nr].index  	  =  i+1;
+    regions[Nr].depth  	  =  1;
+    regions[Nr].parent 	  =  0;
+    regions[Nr].child  	  =  TRUE;
+    regions[Nr].table  	  =  FALSE;
+    memset (regions[Nr].name, 0, 21);
+    strcpy (regions[Nr].name, DecNames[i]);
+  }
+  /* now south */
+  for (i = 0; i < 12; i++, Nr++) {
+    regions[Nr].Rmin   	  =   0;
+    regions[Nr].Rmax   	  = 360;
+    regions[Nr].Dmin   	  = DecBands[i+14];
+    regions[Nr].Dmax   	  = DecBands[i+13];
+    regions[Nr].index  	  =  i+1;
+    regions[Nr].depth  	  =  1;
+    regions[Nr].parent 	  =  0;
+    regions[Nr].child  	  =  TRUE;
+    regions[Nr].table  	  =  FALSE;
+    memset (regions[Nr].name, 0, 21);
+    strcpy (regions[Nr].name, DecNames[i+13]);
+  }
+  regions[No].childE = Nr;
+
+  CHECK_REALLOCATE (regions, SkyRegion, NR, Nr, 100);
+
+  /* level 2 : copy the data from the GSC Region files */
+  No = 1;
+  Dmin = regions[No].Dmin - 0.2;
+  Dmax = regions[No].Dmax + 0.2;
+  regions[No].childS = Nr;
+
+  for (i = 0; i < Ntycho; i++) {
+    /* if we are outside of current region, go to the next one */
+    if ((tycho[i].Dmin < Dmin) || (tycho[i].Dmax > Dmax)) {
+      regions[No].childE = Nr;
+
+      No++;
+      Dmin = regions[No].Dmin - 0.2;
+      Dmax = regions[No].Dmax + 0.2;
+      regions[No].childS = Nr;
+
+      if ((tycho[i].Dmin < Dmin) || (tycho[i].Dmax > Dmax)) {
+	fprintf (stderr, "ERROR: tycho index is not in order!\n");
+	exit (1);
+      }
+    }
+
+    /* set the values for this region */
+    regions[Nr].Rmin = tycho[i].Rmin;
+    regions[Nr].Rmax = tycho[i].Rmax;
+    regions[Nr].Dmin = tycho[i].Dmin;
+    regions[Nr].Dmax = tycho[i].Dmax;
+    strcpy (regions[Nr].name, tycho[i].name);
+
+    regions[Nr].index    =  Nr;
+    regions[Nr].depth    =  2;
+    regions[Nr].parent   =  No;
+    regions[Nr].child    =  FALSE;
+    regions[Nr].table    =  FALSE;
+    regions[Nr].childS   =  0;
+    regions[Nr].childE   =  0;
+
+    Nr ++;
+    CHECK_REALLOCATE (regions, SkyRegion, NR, Nr, 100);
+  }
+
+  free (tycho);
+
+  ALLOCATE (skytable, SkyTable, 1);
+  skytable[0].regions = regions;
+  skytable[0].Nregions = Nr;
+
+  ALLOCATE (skytable[0].filename, char *, skytable[0].Nregions);
+  for (i = 0; i < skytable[0].Nregions; i++) {
+    skytable[0].filename[i] = NULL;
+  }
+  if (VERBOSE) fprintf (stderr, "loaded %d tables from tycho index\n", skytable[0].Nregions);
+
+  return (skytable);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SocketOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SocketOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/SocketOps.c	(revision 22322)
@@ -0,0 +1,211 @@
+# include "addstar.h"
+
+# define MY_PORT 2000
+# define MY_WAIT 500
+
+int InitServerSocket (SockAddress *Address) {
+
+  int status, InitSocket, length;
+
+# if (0)
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  memset (hostip, 0, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+# endif
+  
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = MY_PORT;
+  Address[0].sin_addr.s_addr = INADDR_ANY; // use this line to bind any address / port?
+
+# if (0)  
+  status = inet_aton (hostip, &Address[0].sin_addr);
+  if (!status) {
+    fprintf (stderr, "invalid address\n");
+    exit (2);
+  }
+# endif
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (2);
+  }
+
+  fprintf (stderr, "init sock: %d, len: %d\n", InitSocket, length);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+    perror ("bind: ");
+    exit (2);
+  }
+
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (2);
+  }
+
+  // if (VERBOSE) fprintf (stderr, "socket listening on %s (%s:%d)\n", host[0].h_name, hostip, MY_PORT);
+  return (InitSocket);
+}
+
+int WaitServerSocket (int InitSocket, SockAddress *Address, int *validIP, int Nvalid) {
+
+  int i, BindSocket;
+  socklen_t length;
+  SockAddress Address_in;
+  u_int32_t addr;
+
+  Address_in = Address[0];
+
+  length = sizeof(Address_in);
+
+  /* this is a blocking wait; use in a separate thread */
+  fcntl (InitSocket, F_SETFL, !O_NONBLOCK); 
+
+  fprintf (stderr, "init sock: %d, len: %d\n", InitSocket, length);
+  BindSocket = accept (InitSocket, (struct sockaddr *) &Address_in, &length);
+  fprintf (stderr, "bind sock: %d\n", BindSocket);
+  if (BindSocket == -1) {
+    perror ("accept: ");
+    exit (2);
+  }
+
+  addr = Address_in.sin_addr.s_addr;
+  if (VERBOSE) {
+    fprintf (stderr, "incoming connection from: ");
+    fprintf (stderr, " %u", (0xff & (addr >>  0)));
+    fprintf (stderr, ".%u", (0xff & (addr >>  8)));
+    fprintf (stderr, ".%u", (0xff & (addr >> 16)));
+    fprintf (stderr, ".%u", (0xff & (addr >> 24)));
+    fprintf (stderr, "\n");
+  }
+
+  if (Nvalid == 0) goto accepted;
+
+  for (i = 0; i < Nvalid; i++) {
+    /* valid IP addresses may be machines (120.90.121.142) or 
+       class C networks (120.90.121.0) */
+       
+    /* for machine, address must match */
+    if ((0xff & (validIP[i] >> 24)) != 0) {
+      if (addr == validIP[i]) goto accepted;
+    }
+
+    /* for network, lower three bytes of address must match */
+    if ((0xff & (validIP[i] >> 24)) == 0) {
+      if ((0x00ffffff & addr) == validIP[i]) goto accepted;
+    }
+  }
+
+  if (VERBOSE) fprintf (stderr, "connection rejected\n");
+  close (BindSocket);
+  return (-1);
+
+accepted:
+  if (VERBOSE) fprintf (stderr, "connection accepted\n");
+  fcntl (BindSocket, F_SETFL, O_NONBLOCK); 
+  return (BindSocket);
+}
+
+int GetClientSocket (char *hostname) {
+
+  int i, status, InitSocket, length;
+  SockAddress Address;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  memset (hostip, 0, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+
+  if (VERBOSE) {
+    fprintf (stderr, "trying %s (%s:%d)...", host[0].h_name, hostip, MY_PORT);
+  }
+
+  Address.sin_family = AF_INET;
+  Address.sin_port   = MY_PORT;
+  status = inet_aton (hostip, &Address.sin_addr);
+  if (!status) {
+    fprintf (stderr, "invalid address\n");
+    exit (2);
+  }
+
+  length = sizeof(Address);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (2);
+  }
+
+  status = connect (InitSocket, (struct sockaddr *) &Address, length);
+  if (status == -1) {
+    perror ("connect: ");
+    exit (2);
+  }
+
+  if (VERBOSE) fprintf (stderr, "connected\n");
+  fcntl (InitSocket, F_SETFL, O_NONBLOCK); 
+  return (InitSocket);
+}
+
+int InitServerSocket_Named (char *hostname, SockAddress *Address) {
+
+  int i, status, InitSocket, length;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  memset (hostip, 0, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+  
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = MY_PORT;
+  status = inet_aton (hostip, &Address[0].sin_addr);
+  if (!status) {
+    fprintf (stderr, "invalid address\n");
+    exit (2);
+  }
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (2);
+  }
+
+  fprintf (stderr, "init sock: %d, len: %d\n", InitSocket, length);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+    perror ("bind: ");
+    exit (2);
+  }
+
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (2);
+  }
+
+  if (VERBOSE) fprintf (stderr, "socket listening on %s (%s:%d)\n", host[0].h_name, hostip, MY_PORT);
+  return (InitSocket);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateDatabase_Image.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateDatabase_Image.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateDatabase_Image.c	(revision 22322)
@@ -0,0 +1,121 @@
+# include "addstar.h"
+
+int UpdateDatabase_Image (AddstarClientOptions *options, Image *images, int Nimages, Coords *mosaic, Stars *stars, int Nstars) {
+
+  int i, status;
+  Catalog catalog;
+  SkyList *skylist, *newlist;
+
+  if (options[0].mode != M_IMAGE) {
+    fprintf (stderr, "error: expecting only IMAGE mode\n");
+    return (FALSE);
+  }
+
+  catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+  if (options[0].update) catalog.catflags = LOAD_AVES | LOAD_MISS | LOAD_SECF;
+  
+  // XXX this is probably not needed anymore
+  SetAirmassQuality (options[0].quality_airmass);
+
+  /*** update catalog: average, measure, etc ***/
+
+  /* find correpsonding regions for image */
+  RegisterMosaic (mosaic);
+  skylist = NULL;
+  newlist = NULL;
+  for (i = 0; i < Nimages; i++) {
+      newlist = SkyListByImage (ServerSky, -1, &images[i]);
+      SkyListMerge (&skylist, newlist);
+      SkyListFree (newlist);
+  }
+
+  ImageOptions (options, images, Nimages);
+
+  /* reduce regions to existing subset, if necessary */
+  if (options[0].only_match || options[0].existing_regions) {
+    SkyList *tmp;
+    tmp = SkyListExistingSubset (skylist, CATDIR);
+    SkyListFree (skylist);
+    skylist = tmp;
+  }
+  if (VERBOSE) fprintf (stderr, "writing to %d regions\n", skylist[0].Nregions);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set the parameters which guide catalog open/load/create
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    catalog.filename  = skylist[0].filename[i];
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    if (options[0].update) catalog.catflags = LOAD_AVES | LOAD_MISS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+
+    // Naves_disk == 0 implies an empty catalog file
+    // for only_match, skip empty catalogs 
+    if ((catalog.Naves_disk == 0) && options[0].only_match) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    if (options[0].closest) {
+      find_matches_closest (skylist[0].regions[i], stars, Nstars, &catalog, options[0]);
+    } else {
+      find_matches (skylist[0].regions[i], stars, Nstars, &catalog, options[0]);
+    }
+
+    if (!options[0].only_images) {
+      SetProtect (TRUE);
+      if (options[0].update) {
+	catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	dvo_catalog_update (&catalog, VERBOSE);
+      } else {
+	dvo_catalog_save (&catalog, VERBOSE);
+      }
+      SetProtect (FALSE);
+    }
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+
+  if (options[0].calibrate) { FindCalibration (&images[0]); }
+  
+  /*** load image db, save new image ***/
+  { 
+    FITS_DB db;
+
+    /*** update the image table ***/
+    /* setup image table format and lock */
+    db.mode   = dvo_catalog_catmode (CATMODE);
+    db.format = dvo_catalog_catformat (CATFORMAT);
+    status = dvo_image_lock (&db, ImageCat, 60.0, LCK_XCLD);
+    if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+    /* load or create the image table */
+    if (db.dbstate == LCK_EMPTY) {
+      if (VERBOSE) fprintf (stderr, "can't find %s, creating a new one\n", ImageCat);
+      dvo_image_create (&db, GetZeroPoint());
+    } else {
+      if (!dvo_image_load (&db, VERBOSE, FORCE_READ)) {
+	Shutdown ("can't read image catalog %s", db.filename);
+      }
+    }
+
+    /* write out new image */
+    dvo_image_addrows (&db, images, Nimages);
+    SetProtect (TRUE);
+    dvo_image_update (&db, VERBOSE);
+    SetProtect (FALSE);
+    dvo_image_unlock (&db);
+  }
+  free (mosaic);
+  free (images);
+  free (stars);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateDatabase_Refcat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateDatabase_Refcat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateDatabase_Refcat.c	(revision 22322)
@@ -0,0 +1,77 @@
+# include "addstar.h"
+
+int UpdateDatabase_Refcat (AddstarClientOptions *options, SkyRegion *patch, char *refcat) {
+
+  int i, Nstars, Nsubset;
+  Catalog catalog;
+  Stars *stars, **subset;
+  SkyList *skylist;
+
+  if (options[0].mode != M_REFCAT) {
+    fprintf (stderr, "error: expecting only REFCAT mode\n");
+    return (FALSE);
+  }
+
+  if (!check_dir_access (CATDIR, VERBOSE)) exit (1);
+
+  /*** update catalog: average, measure, etc ***/
+  
+  /* find correpsonding regions for image */
+  skylist = SkyListByPatch (ServerSky, -1, patch);
+
+  /* reduce regions to existing subset, if necessary */
+  if (options[0].only_match || options[0].existing_regions) {
+    SkyList *tmp;
+    tmp = SkyListExistingSubset (skylist, CATDIR);
+    SkyListFree (skylist);
+    skylist = tmp;
+  }
+  if (VERBOSE) fprintf (stderr, "writing to %d regions\n", skylist[0].Nregions);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set the parameters which guide catalog open/load/create
+    catalog.filename  = skylist[0].filename[i];
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    if (options[0].update) catalog.catflags = LOAD_AVES | LOAD_MISS | LOAD_SECF;
+  
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+
+    // Naves_disk == 0 implies an empty catalog file
+    // for only_match, skip empty catalogs
+    if ((catalog.Naves_disk == 0) && options[0].only_match) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // load stars from corresponding catalog 
+    stars = grefcat (refcat, skylist[0].regions[i], options[0].photcode, &Nstars);
+
+    subset = find_subset (skylist[0].regions[i], stars, Nstars, &Nsubset);
+    find_matches_refstars (skylist[0].regions[i], subset, Nsubset, &catalog, options[0]);
+    if (Nsubset) free (subset);
+
+    if (!options[0].only_images) {
+      SetProtect (TRUE);
+      if (options[0].update) {
+	catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	dvo_catalog_update (&catalog, VERBOSE);
+      } else {
+	dvo_catalog_save (&catalog, VERBOSE);
+      }
+      SetProtect (FALSE);
+    }
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+    free (stars);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateDatabase_Reflist.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateDatabase_Reflist.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateDatabase_Reflist.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "addstar.h"
+
+int UpdateDatabase_Reflist (AddstarClientOptions *options, Stars *stars, int Nstars) {
+
+  int i, Nsubset;
+  Catalog catalog;
+  Stars **subset;
+  SkyList *skylist;
+
+  if (options[0].mode != M_REFLIST) {
+    fprintf (stderr, "error: expecting only REFLIST mode\n");
+    return (FALSE);
+  }
+
+  if (!check_dir_access (CATDIR, VERBOSE)) exit (1);
+
+  /*** update catalog: average, measure, etc ***/
+  
+  /* find correpsonding regions for image */
+  skylist = SkyListForStars (ServerSky, -1, stars, Nstars);
+
+  /* reduce regions to existing subset, if necessary */
+  if (options[0].only_match || options[0].existing_regions) {
+    SkyList *tmp;
+    tmp = SkyListExistingSubset (skylist, CATDIR);
+    SkyListFree (skylist);
+    skylist = tmp;
+  }
+  if (VERBOSE) fprintf (stderr, "writing to %d regions\n", skylist[0].Nregions);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set the parameters which guide catalog open/load/create
+    catalog.filename  = skylist[0].filename[i];
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    if (options[0].update) catalog.catflags = LOAD_AVES | LOAD_MISS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+
+    /* for only_match, skip empty catalogs XXX EAM : this leaves behind empty files */
+    if ((catalog.Naves_disk == 0) && options[0].only_match) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    subset = find_subset (skylist[0].regions[i], stars, Nstars, &Nsubset);
+    find_matches_refstars (skylist[0].regions[i], subset, Nsubset, &catalog, options[0]);
+    if (Nsubset) free (subset);
+
+    if (!options[0].only_images) {
+      SetProtect (TRUE);
+      if (options[0].update) {
+	catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	dvo_catalog_update (&catalog, VERBOSE);
+      } else {
+	dvo_catalog_save (&catalog, VERBOSE);
+      }
+      SetProtect (FALSE);
+    }
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+
+  free (stars);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateImageIDs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateImageIDs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/UpdateImageIDs.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "addstar.h"
+
+int UpdateImageIDs (Stars *stars, int Nstars, Image *images, int Nimages) {
+
+  int i, status, isEmpty;
+  unsigned int imageID;
+  FITS_DB db;
+
+  /*** update the image table ***/
+  /* setup image table format and lock */
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  status    = dvo_image_lock (&db, ImageCat, 3600.0, LCK_XCLD);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  /* load or create the image table */
+  if (db.dbstate == LCK_EMPTY) {
+    if (VERBOSE) fprintf (stderr, "can't find %s, creating a new one\n", ImageCat);
+    dvo_image_create (&db, GetZeroPoint());
+    isEmpty = TRUE;
+  } else {
+    /* position to start of file */
+    Fseek (db.f, 0, SEEK_SET);
+    if (!gfits_fread_header (db.f, &db.header)) {
+      Shutdown ("can't read image table phu %s", db.filename);
+    }
+    isEmpty = FALSE;
+  }
+
+  // note that imageID is unsigned int
+  status = gfits_scan (&db.header, "IMAGEID", "%u", 1, &imageID);
+  if (!status) {
+    status = gfits_scan (&db.header, "NIMAGES", "%u", 1, &imageID);
+  }
+
+  // XXX should the first image ID be 1, not 0?
+  if (imageID == 0) imageID = 1;
+
+  // update the image IDs already written to stars and images:
+  for (i = 0; i < Nimages; i++) {
+    images[0].imageID += imageID;
+  }
+
+  for (i = 0; i < Nstars; i++) {
+    stars[0].imageID += imageID;
+  }
+
+  imageID += Nimages;
+  status = gfits_modify (&db.header, "IMAGEID", "%u", 1, imageID);
+ 
+
+  if (isEmpty) {
+    dvo_image_addrows (&db, NULL, 0);
+    SetProtect (TRUE);
+    dvo_image_update (&db, VERBOSE);
+    SetProtect (FALSE);
+  } else {
+    // write just the PHU
+    // position to start of file
+    Fseek (db.f, 0, SEEK_SET);
+    SetProtect (TRUE);
+    if (!gfits_fwrite_header (db.f, &db.header)) {
+      Shutdown ("can't write image table phu %s", db.filename);
+    }
+    SetProtect (FALSE);
+  }
+
+  dvo_image_unlock (&db);
+
+  return TRUE;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstar.c	(revision 22322)
@@ -0,0 +1,213 @@
+# include "addstar.h"
+
+int main (int argc, char **argv) {
+
+  int Nmatch, status, loadObjects;
+  int i, Nstars, Nimages, Nsubset;
+  unsigned long long Naverage, Nmeasure;
+  Stars *stars, **subset;
+  Image *images;
+  Catalog catalog;
+  FITS_DB db;
+  AddstarClientOptions options;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+  SkyList *newlist = NULL;
+
+  double dtime;
+  struct timeval start, stop;
+
+  gettimeofday (&start, NULL);
+
+  SetSignals ();
+  options = ConfigInit (&argc, argv);
+  options = args (argc, argv, options);
+
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  if (sky == NULL) {
+      fprintf (stderr, "ERROR: unable to load sky table data\n");
+      exit (2);
+  }
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  stars = NULL;
+
+  /*** load in the new data (images, stars) ***/
+  switch (options.mode) {
+    case M_IMAGE:
+      stars = LoadStars (argv[1], &Nstars, &images, &Nimages, &options);
+
+      // set and update the imageID sequence
+      UpdateImageIDs (stars, Nstars, images, Nimages);
+
+      if ((DUMP != NULL) && !strcmp (DUMP, "rawstars")) dump_rawstars (stars, Nstars);
+      for (i = 0; i < Nimages; i++) {
+	newlist = SkyListByImage (sky, -1, &images[i]);
+	SkyListMerge (&skylist, newlist);
+	if (VERBOSE) fprintf (stderr, "added %d regions to yield %d total\n", newlist[0].Nregions, skylist[0].Nregions);
+	SkyListFree (newlist);
+      }
+      ImageOptions (&options, images, Nimages);
+      break;
+    case M_REFLIST:
+      stars = grefstars (argv[1], options.photcode, &Nstars);
+      skylist = SkyListForStars (sky, -1, stars, Nstars);
+      break;
+    case M_RESORT:
+    case M_REFCAT:
+      skylist = SkyListByPatch (sky, -1, &UserPatch);
+      break;
+    case M_FAKEIMAGE:
+      images = fakeimage (argv[1], &Nimages, options.photcode);
+      ALLOCATE (skylist, SkyList, 1);
+      skylist[0].Nregions = 0;
+      skylist[0].ownElements = FALSE;
+      break;
+
+    default:
+      fprintf (stderr, "ERROR: invalid mode\n");
+      exit (2);
+  }
+
+  // in these cases, limit the sky catalogs to an existing subset
+  if (options.only_match || options.existing_regions) {
+    SkyList *tmp;
+    tmp = SkyListExistingSubset (skylist, CATDIR);
+    SkyListFree (skylist);
+    skylist = tmp;
+  }
+  if (VERBOSE) fprintf (stderr, "writing to %d regions\n", skylist[0].Nregions);
+
+  /* don't load the object tables for only_images, unless we are getting the calibration. */
+  loadObjects = !options.only_images || options.calibrate;
+
+  /* match stars to existing catalog data (or otherwise manipulate catalog data) */
+  Nmatch = Naverage = Nmeasure = 0;
+  for (i = 0; loadObjects && (i < skylist[0].Nregions); i++) {
+
+    // set the parameters which guide catalog open/load/create
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    // XXX need to do something to enforce consistency.  we can change options.update
+    // based on the value of catmode as below.  however, this seems to be a problem if
+    // there is an inconsistency between the config values and the existing db.  not so
+    // bad if the existing db is SPLIT, but a problem if we request SPLIT and the existing
+    // db is MEF.
+    if (catalog.catmode != DVO_MODE_SPLIT) options.update = FALSE;
+
+    if (options.update) catalog.catflags = LOAD_AVES | LOAD_MISS | LOAD_SECF;
+
+    // open as read or write, depending on desire
+    if (options.only_images && options.calibrate) {
+      if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "r")) {
+	continue;
+      }
+    } else {
+      // an error exit status here is a significant error (disk I/O or file access)
+      // XXX should this be "a" for options.update?
+      if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+	fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+	exit (2);
+      }
+    }
+
+    // Naves_disk == 0 implies an empty catalog file
+    // for only_match, skip empty catalogs
+    if ((catalog.Naves_disk == 0) && options.only_match) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    switch (options.mode) {
+      case M_IMAGE:
+	Nsubset = Nstars;
+	if (options.closest) {
+	  Nmatch += find_matches_closest (skylist[0].regions[i], stars, Nstars, &catalog, options);
+	} else {
+	  Nmatch += find_matches (skylist[0].regions[i], stars, Nstars, &catalog, options);
+	}
+	break;
+      case M_REFCAT:
+	stars = grefcat (argv[1], skylist[0].regions[i], options.photcode, &Nstars);
+      case M_REFLIST:
+	subset = find_subset (skylist[0].regions[i], stars, Nstars, &Nsubset);
+	Nmatch += find_matches_refstars (skylist[0].regions[i], subset, Nsubset, &catalog, options);
+	if (Nsubset) free (subset);
+	break;
+      case M_RESORT:
+	if (options.nosort == 3) catalog.sorted = FALSE;
+	resort_catalog (&catalog);
+	Nsubset = 1;
+	break;
+    }
+    /* report total updated values */
+    Naverage += catalog.Naverage;
+    Nmeasure += catalog.Nmeasure;
+
+    // write out catalog, if appropriate
+    if (Nsubset && !options.only_images) {
+      SetProtect (TRUE);
+      if (options.update) {
+	catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	dvo_catalog_update (&catalog, VERBOSE);
+      } else {
+	dvo_catalog_save (&catalog, VERBOSE);
+      }
+      SetProtect (FALSE);
+    }
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+
+    if (options.mode == M_REFCAT) free (stars);
+  }
+
+  // We only measure a single value for the entire mosaic (add all images to this function)
+  if (options.calibrate) { FindCalibration (&images[0]); }
+
+  /*** update the image table ***/
+  /* setup image table format and lock */
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  status    = dvo_image_lock (&db, ImageCat, 3600.0, LCK_XCLD);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  /* load or create the image table */
+  if (db.dbstate == LCK_EMPTY) {
+    if (VERBOSE) fprintf (stderr, "can't find %s, creating a new one\n", ImageCat);
+    dvo_image_create (&db, GetZeroPoint());
+  } else {
+    if (!dvo_image_load (&db, VERBOSE, FORCE_READ)) {
+      Shutdown ("can't read image catalog %s", db.filename);
+    }
+  }
+
+  /* add the new images and save */
+  if (options.mode == M_IMAGE) {
+    dvo_image_addrows (&db, images, Nimages);
+    SetProtect (TRUE);
+    dvo_image_update (&db, VERBOSE);
+    SetProtect (FALSE);
+  }
+  dvo_image_unlock (&db); /* unlock? */
+
+  gettimeofday (&stop, NULL);
+  dtime = DTIME (stop, start);
+  fprintf (stderr, "SUCCESS: elapsed time %9.4f sec for %5d stars (%5d matches), %6lld average, %7lld measure\n", dtime, Nstars, Nmatch, Naverage, Nmeasure);
+
+  exit (0);
+}
+
+/* names:
+   catalog - existing object db table
+   regions - sky area which may or may not contain data
+   patch   - RA,DEC bounded portion of sky
+*/
+
+// add in case of failures:
+// ohana_memcheck (FALSE);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstarc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstarc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstarc.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "addstar.h"
+
+int main (int argc, char **argv) {
+
+  int Nstars, Nimages;
+  int BindSocket;
+  AddstarClientOptions options;
+  Stars *stars;
+  Image *images;
+  Coords MOSAIC;
+
+  /* load config and options */
+  options = ConfigInit (&argc, argv);
+  options = args_client (argc, argv, options);
+
+  /* set up server connection */
+  BindSocket = GetClientSocket (HOSTNAME);
+  SendCommand (BindSocket, strlen(PASSWORD), PASSWORD);
+
+  /* send new data to server */
+  switch (options.mode) {
+    case M_IMAGE:
+      /* load data */
+      stars = LoadStars (argv[1], &Nstars, &images, &Nimages, &options);
+
+      // set and update the imageID sequence
+      UpdateImageIDs (stars, Nstars, images, Nimages);
+
+      /* send data to server */
+      SendCommand (BindSocket, 5, "IMAGE");
+      Send_AddstarClientOptions (BindSocket, &options, 1, TRUE);
+      Send_Image (BindSocket, images, Nimages, FALSE);
+      if (options.mosaic) {
+	GetRegisteredMosaic (&MOSAIC);
+	Send_Coords (BindSocket, &MOSAIC, 1, FALSE);
+      }
+      Send_Stars (BindSocket, stars, Nstars, FALSE);
+      break;
+
+    case M_REFLIST:
+      /* load data */
+      stars = grefstars (argv[1], options.photcode, &Nstars);
+      
+      /* send data to server */
+      SendCommand (BindSocket, 5, "REFLS");
+      Send_AddstarClientOptions (BindSocket, &options, 1, TRUE);
+      Send_Stars (BindSocket, stars, Nstars, FALSE);
+      break;
+
+    case M_REFCAT:
+      /* send data to server */
+      SendCommand (BindSocket, 5, "REFCT");
+      Send_AddstarClientOptions (BindSocket, &options, 1, TRUE);
+      Send_SkyRegion (BindSocket, &UserPatch, 1, TRUE);
+      SendMessage (BindSocket, argv[1]);
+      break;
+
+    default:
+      fprintf (stderr, "unknown addstar mode\n");
+      exit (1);
+  }
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstard.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstard.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstard.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "addstar.h"
+
+int main (int argc, char **argv) {
+
+  int status, InitSocket, BindSocket;
+  SockAddress Address;
+  IOBuffer message;
+  AddstarClientOptions options;
+
+  options = ConfigInit (&argc, argv);
+  args_server (argc, argv);
+
+  /* store the sky table in a global for internal use */
+  ServerSky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (ServerSky, CATDIR, "cpt");
+
+  /* if we separate the incoming data from db update, spawn db thread here */
+
+  VERBOSE = TRUE;
+  InitSocket = InitServerSocket (&Address);
+  
+  while (1) {
+
+    /* wait for clients to make connection */
+    BindSocket = WaitServerSocket (InitSocket, &Address, VALID_IP, NVALID);
+    if (BindSocket == -1) continue;
+
+    /* validate : wait for password */
+    if (!CheckPassword (BindSocket)) continue;
+    
+    /* accept command : XXX EAM : long-enough timeout? */
+    status = ExpectCommand (BindSocket, 5, 0.1, &message);
+    if (status != 0) {
+      if (VERBOSE) fprintf (stderr, "failed connection\n");
+      FreeIOBuffer (&message);
+      close (BindSocket);
+      continue;
+    }
+
+    /* message options */
+    if (!strcmp (message.buffer, "IMAGE")) {
+      fprintf (stderr, "Image\n");
+      NewImage (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "REFLS")) {
+      fprintf (stderr, "Reflist\n");
+      NewReflist (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "REFCT")) {
+      fprintf (stderr, "Refcat\n");
+      NewRefcat (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "EXIT")) {
+      fprintf (stderr, "Exit\n");
+      exit (2);
+    }
+  }    
+  exit (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstart.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstart.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/addstart.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "addstar.h"
+# include <pthread.h>
+
+int main (int argc, char **argv) {
+
+  AddstarClientOptions options;
+  pthread_t thread;
+  DVO_DATA *dataset;
+
+  options = ConfigInit (&argc, argv);
+  args_server (argc, argv);
+
+  /* store the sky table in a global for internal use */
+  ServerSky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (ServerSky, CATDIR, "cpt");
+
+  VERBOSE = TRUE;
+
+  InitDataset ();
+
+  // launch thread to listen for client data
+  pthread_create (&thread, NULL, &ListenClients_Thread, NULL);
+
+  // XXX need to watch for shutdown message
+  while (1) {
+
+    dataset = PopDataset ();
+    if (dataset == NULL) {
+      usleep (50000);
+    }
+
+    switch (dataset[0].options[0].mode) {
+      case M_IMAGE:
+	UpdateDatabase_Image (dataset[0].options, dataset[0].images, dataset[0].Nimages, dataset[0].mosaic, dataset[0].stars, dataset[0].Nstars);
+	continue;
+
+      case M_REFLIST:
+	UpdateDatabase_Reflist (dataset[0].options, dataset[0].stars, dataset[0].Nstars);
+	continue;
+
+      case M_REFCAT:
+	UpdateDatabase_Refcat (dataset[0].options, dataset[0].patch, dataset[0].refcat);
+	continue;
+
+      default:
+	fprintf (stderr, "error: unexpected dataset\n");
+    }
+  }    
+  exit (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/airmass.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/airmass.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/airmass.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "addstar.h"
+
+static int AirmassQuality = FALSE;
+
+void SetAirmassQuality (int quality) {
+  AirmassQuality = quality;
+}
+
+float airmass (float secz_image, double ra, double dec, double st, double latitude) {
+
+  double hour, cosz, secz;
+  double rdec, rlat;
+
+  if (!AirmassQuality) return (secz_image);
+
+  /*** make this optional? we may not have ST... ***/
+  /* ra, dec, latitude in dec deg; st in dec hours */
+
+  /* hour : hour angle in degrees */
+  rdec = RAD_DEG*dec;
+  rlat = RAD_DEG*latitude;
+  hour = 15.0*st - ra;
+  cosz = sin (rdec) * sin (rlat) + cos (rdec) * cos (RAD_DEG*hour) * cos (rlat);
+  secz = 1.000 / cosz;
+  return (secz);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args.c	(revision 22322)
@@ -0,0 +1,374 @@
+# include "addstar.h"
+static void help (void);
+
+AddstarClientOptions args (int argc, char **argv, AddstarClientOptions options) {
+  
+  int i, N;
+  int QUALITY_AIRMASS;
+
+  // a global used by find_matches_refstars.c (value is 1 except for load2mass)
+  NREFSTAR_GROUP = 1;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /*** check for command line options ***/
+
+  /* basic mode: image, list, refcat */
+  options.mode = M_IMAGE;
+  if ((N = get_argument (argc, argv, "-ref"))) {
+    options.mode = M_REFLIST;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-cat"))) {
+    options.mode = M_REFCAT;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-fakeimage"))) {
+    options.mode = M_FAKEIMAGE;
+    remove_argument (N, &argc, argv);
+    FAKE_RA = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    FAKE_DEC = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    FAKE_THETA = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-resort"))) {
+    options.mode = M_RESORT;
+    remove_argument (N, &argc, argv);
+  }
+
+  options.filelist = FALSE;
+  if ((N = get_argument (argc, argv, "-list"))) {
+    options.filelist = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PMM_CCD_TABLE = NULL;
+  if ((N = get_argument (argc, argv, "-pmm"))) {
+    remove_argument (N, &argc, argv);
+    PMM_CCD_TABLE = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /*** provide additional data ***/ 
+  /* restrict to a portion of the sky? (REFCAT only) */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  } else {
+      if (options.mode == M_IMAGE) goto allow;
+      if (options.mode == M_FAKEIMAGE) goto allow;
+      if (options.mode == M_REFLIST) goto allow;
+      if (options.mode == M_REFCAT) {
+	  fprintf (stderr, "you have requested uploading from a catalog to the entire sky in one pass\n");
+      }
+      if (options.mode == M_RESORT) {
+	  fprintf (stderr, "you have requested resorting the entire sky in one pass\n");
+      }
+      fprintf (stderr, "this could be a time consuming operation.  type Ctrl-C within 5 seconds to cancel\n");
+      for (i = 5; i > 0; i--) {
+	  fprintf (stderr, "%d.. ", i);
+	  usleep (1000000);
+      }
+      fprintf (stderr, "\n");
+  }
+allow:
+  /* override any header PHOTCODE values */
+  options.photcode = 0;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    options.photcode = GetPhotcodeCodebyName (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    options.photcode = GetPhotcodeCodebyName (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* provide a time for dataset */
+  TIMEREF = 0; 
+  if ((N = get_argument (argc, argv, "-time"))) {
+    time_t tmp;
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tmp)) { 
+      fprintf (stderr, "syntax error in time\n");
+      exit (1);
+    }
+    TIMEREF = tmp;
+    remove_argument (N, &argc, argv);
+  }
+  /* provide a mosaic for distortion */
+  options.mosaic = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    Header header;
+    Coords MOSAIC;
+
+    remove_argument (N, &argc, argv);
+    if (!gfits_read_header (argv[N], &header)) {
+      fprintf (stderr, "ERROR: can't read header for mosaic %s\n", argv[N]);
+      exit (1);
+    }
+    if (!GetCoords (&MOSAIC, &header)) {
+      fprintf (stderr, "ERROR: no astrometric solution in header\n");
+      exit (1);
+    }
+    if (strcmp(&MOSAIC.ctype[4], "-DIS")) {
+      fprintf (stderr, "ERROR: not a mosaic distortion header\n");
+      exit (1);
+    }
+    RegisterMosaic (&MOSAIC);
+    remove_argument (N, &argc, argv);
+    gfits_free_header (&header);
+    options.mosaic = TRUE;
+  }
+  
+  /*** modify behavior ***/
+  /* only add to existing objects */
+  options.existing_regions = FALSE;
+  if ((N = get_argument (argc, argv, "-existing-regions"))) {
+    options.existing_regions = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* only add to existing objects */
+  options.only_match = FALSE;
+  if ((N = get_argument (argc, argv, "-only-match"))) {
+    options.only_match = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* don't add missed pts to Missed table (image only) */
+  options.skip_missed = TRUE;
+  if ((N = get_argument (argc, argv, "-missed"))) {
+    options.skip_missed = TRUE;
+    remove_argument (N, &argc, argv);
+    fprintf (stderr, "ERROR: addstar no longer supports -missed\n");
+    exit (2);
+  }
+  /* replace measurement, don't duplicate (ref/cat only) */
+  options.replace = FALSE;
+  if ((N = get_argument (argc, argv, "-replace"))) {
+    options.replace = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* use 'closest star' matching by default */
+  options.closest = TRUE;
+  if ((N = get_argument (argc, argv, "-closest"))) {
+    options.closest = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-all-matches"))) {
+    options.closest = FALSE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* don't re-sort the measure sequence */
+  options.nosort = FALSE;
+  if ((N = get_argument (argc, argv, "-nosort"))) {
+    options.nosort = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* only add new rows (-update) or re-write complete measure table (forces -nosort) */
+  options.update = FALSE;
+  if ((N = get_argument (argc, argv, "-update"))) {
+    options.update = TRUE;
+    options.nosort = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-force-sort"))) {
+    options.nosort = 3;  // temporary mode to mean 'force-sort'
+    remove_argument (N, &argc, argv);
+  }
+
+  /* only add image potion to image table */
+  options.only_images = FALSE;
+  if ((N = get_argument (argc, argv, "-image"))) {
+    options.only_images = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* apply average zpt offset calibration (image only) */
+  options.calibrate = FALSE;
+  if ((N = get_argument (argc, argv, "-cal"))) {
+    options.calibrate = TRUE;
+    remove_argument (N, &argc, argv);
+    InitCalibration (FALSE);
+  }
+  if ((N = get_argument (argc, argv, "-excal"))) {
+    options.calibrate = TRUE;
+    remove_argument (N, &argc, argv);
+    InitCalibration (FALSE);
+  }
+  if ((N = get_argument (argc, argv, "-incal"))) {
+    options.calibrate = TRUE;
+    remove_argument (N, &argc, argv);
+    InitCalibration (TRUE);
+  }
+
+  /*** optional situations ***/
+  /* choose high quality airmass vs low quality airmass (per-star vs per-image) */
+  QUALITY_AIRMASS = FALSE;
+  if ((N = get_argument (argc, argv, "-quality-airmass"))) {
+    remove_argument (N, &argc, argv);
+    QUALITY_AIRMASS = TRUE;
+  }
+  /* choose high quality airmass vs low quality airmass (per-star vs per-image) */
+  SUBPIX = FALSE;
+  if ((N = get_argument (argc, argv, "-subpix"))) {
+    remove_argument (N, &argc, argv);
+    SUBPIX = TRUE;
+  }
+  /* skyprobe means: subpix correction and quality airmass */ 
+  if ((N = get_argument (argc, argv, "-skyprobe"))) {
+    remove_argument (N, &argc, argv);
+    QUALITY_AIRMASS = TRUE;
+    SUBPIX = TRUE;
+  }
+  SetAirmassQuality (QUALITY_AIRMASS);
+  if (SUBPIX) load_subpix ();
+
+  /* define 2MASS quality flags to keep */
+  SELECT_2MASS_QUALITY = NULL;
+  if ((N = get_argument (argc, argv, "-2massquality"))) {
+    remove_argument (N, &argc, argv);
+    SELECT_2MASS_QUALITY = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* accept bad header astrometry */
+  ACCEPT_ASTROM = FALSE;
+  if ((N = get_argument (argc, argv, "-accept"))) {
+    ACCEPT_ASTROM = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-accept-astrom"))) {
+    ACCEPT_ASTROM = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* accept proper-motion data from reference */
+  ACCEPT_MOTION = FALSE;
+  if ((N = get_argument (argc, argv, "-accept-motion"))) {
+    ACCEPT_MOTION = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* accept bad header astrometry */
+  ACCEPT_TIME = FALSE;
+  if ((N = get_argument (argc, argv, "-accept-time"))) {
+    ACCEPT_TIME = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* skip the stars */
+  NO_STARS = FALSE;
+  if ((N = get_argument (argc, argv, "-no-stars"))) {
+    NO_STARS = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* force read of image database with mismatched NSTARS & size */ 
+  FORCE_READ = FALSE;
+  if ((N = get_argument (argc, argv, "-force"))) {
+    FORCE_READ = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* force read of image database with mismatched NSTARS & size */ 
+  TEXTMODE = FALSE;
+  if ((N = get_argument (argc, argv, "-textmode"))) {
+    TEXTMODE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  DUMP = NULL;
+  if ((N = get_argument (argc, argv, "-dump"))) {
+    remove_argument (N, &argc, argv);
+    DUMP = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((options.mode == M_RESORT) && (argc == 1)) return (options);
+  if (argc == 2) return (options);
+
+  if ((options.mode == M_REFLIST) && (options.photcode == 0)) {
+    fprintf (stderr, "photcode must be specified for -ref\n");
+    exit (2);
+  }
+
+  fprintf (stderr, "USAGE: addstar (filename)\n");
+  fprintf (stderr, "USAGE: addstar -cat (catalog)\n");
+  fprintf (stderr, "USAGE: addstar -ref (filename)\n");
+  fprintf (stderr, "USAGE: addstar -fakeimage (ra) (dec) (theta) (name)\n");
+  fprintf (stderr, "USAGE: addstar -resort (SkyRegion)\n");
+  exit (2);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  addstar (filename)\n");
+  fprintf (stderr, "     add specified image (cmp format) to database\n\n");
+  fprintf (stderr, "  addstar -ref (filename)");
+  fprintf (stderr, "     add ASCII data (ra dec mag dmag) to database\n\n");
+  fprintf (stderr, "  addstar -cat (catalog)");
+  fprintf (stderr, "     add data from catalog (USNO/2MASS/GSC) to database\n\n");
+  fprintf (stderr, "  addstar -resort (SkyRegion)");
+  fprintf (stderr, "     perform measure sorting for the specified catalog\n\n");
+
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -region ra ra dec dec 	  : only add data in specified region (-ref mode only)\n");
+  fprintf (stderr, "  -p (photcode)         	  : specify photcode (-ref / -cat / or override header)\n");
+  fprintf (stderr, "  -photcode (photcode)    	  : specify photcode (-ref / -cat / or override header)\n");
+  fprintf (stderr, "  -time (YYYY/MM/DD,HH:MM:SS) : specify date/time (override header)\n");
+  fprintf (stderr, "  -mosaic (filename)    	  : identify associated mosaic frame for chip image\n");
+  fprintf (stderr, "  -textmode                	  : input file is RAW TEXT table, not FITS table\n");
+  fprintf (stderr, "  -existing-regions           : only add measurements to existing catalog files\n");
+  fprintf (stderr, "  -only-match           	  : only add measurements to existing objects\n");
+  fprintf (stderr, "  -missed               	  : skipped 'missed' entries\n");
+  fprintf (stderr, "  -replace              	  : replace time/photcode measurements (no duplication)\n");
+  fprintf (stderr, "  -closest             	  : use closest-star algorith\n");
+  fprintf (stderr, "  -nosort             	  : don't re-sort the measure entries (improves speed)\n");
+  fprintf (stderr, "  -update             	  : only update the new rows (forces -nosort)\n");
+  fprintf (stderr, "  -image                	  : only insert image data\n");
+  fprintf (stderr, "  -cal                  	  : perform zero-point calibration\n");
+  fprintf (stderr, "  -skyprobe             	  : specify skyprobe mode\n");
+  fprintf (stderr, "  -accept               	  : accept bad astrometry from header\n");
+  fprintf (stderr, "  -accept-astrom          	  : accept bad astrometry from header\n");
+  fprintf (stderr, "  -accept-motion           	  : accept proper-motion data from reference\n");
+  fprintf (stderr, "  -force                	  : force read of database with inconsistent info\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -dump (mode)          	  : output test data\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
+
+/** addstar modes:
+ 
+    addstar (image.smp)  - add cmp/smp image data to db
+    addstar -ref (file.dat) (photcode) 
+    addstar -cat (USNO/2MASS/GSC) -region (ra dec - ra dec)
+
+    -replace : ref/cat - replace existing match (photcode/time)
+    -match   : ref/cat - only add measures to existing averages
+
+    ref types: 
+    ASCII - RA,DEC,M,dM in a table
+
+    addstar 
+
+**/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_client.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_client.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_client.c	(revision 22322)
@@ -0,0 +1,271 @@
+# include "addstar.h"
+static void help (void);
+
+AddstarClientOptions args_client (int argc, char **argv, AddstarClientOptions options) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  // a global used by find_matches_refstars.c (value is 1 except for load2mass)
+  NREFSTAR_GROUP = 1;
+
+  /*** check for command line options ***/
+
+  /* basic mode: image, list, refcat */
+  options.mode = M_IMAGE;
+  if ((N = get_argument (argc, argv, "-ref"))) {
+    options.mode = M_REFLIST;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-cat"))) {
+    options.mode = M_REFCAT;
+    remove_argument (N, &argc, argv);
+  }
+
+  /*** provide additional data ***/ 
+  /* restrict to a portion of the sky? (REFCAT only) */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax= 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* override any header PHOTCODE values */
+  options.photcode = 0;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    options.photcode = GetPhotcodeCodebyName (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* provide a time for dataset */
+  options.timeref = 0; 
+  if ((N = get_argument (argc, argv, "-time"))) {
+    time_t tmp;
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tmp)) { 
+      fprintf (stderr, "syntax error in time\n");
+      exit (1);
+    }
+    options.timeref = tmp;
+    remove_argument (N, &argc, argv);
+  }
+  /* provide a mosaic for distortion */
+  options.mosaic = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    Header header;
+    Coords MOSAIC;
+
+    remove_argument (N, &argc, argv);
+    if (!gfits_read_header (argv[N], &header)) {
+      fprintf (stderr, "ERROR: can't read header for mosaic %s\n", argv[N]);
+      exit (1);
+    }
+    if (!GetCoords (&MOSAIC, &header)) {
+      fprintf (stderr, "ERROR: no astrometric solution in header\n");
+      exit (1);
+    }
+    if (strcmp(&MOSAIC.ctype[4], "-DIS")) {
+      fprintf (stderr, "ERROR: not a mosaic distortion header\n");
+      exit (1);
+    }
+    RegisterMosaic (&MOSAIC);
+    remove_argument (N, &argc, argv);
+    gfits_free_header (&header);
+    options.mosaic = TRUE;
+  }
+  
+  /*** modify behavior ***/
+  /* only add to existing objects */
+  options.existing_regions = FALSE;
+  if ((N = get_argument (argc, argv, "-existing-regions"))) {
+    options.existing_regions = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* only add to existing objects */
+  options.only_match = FALSE;
+  if ((N = get_argument (argc, argv, "-only-match"))) {
+    options.only_match = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* don't add missed pts to Missed table (image only) */
+  options.skip_missed = FALSE;
+  if ((N = get_argument (argc, argv, "-missed"))) {
+    options.skip_missed = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* replace measurement, don't duplicate (ref/cat only) */
+  options.replace = FALSE;
+  if ((N = get_argument (argc, argv, "-replace"))) {
+    options.replace = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* use 'closest star' matching by default */
+  options.closest = TRUE;
+  if ((N = get_argument (argc, argv, "-closest"))) {
+    options.closest = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-all-matches"))) {
+    options.closest = FALSE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* don't re-sort the measure sequence */
+  options.nosort = FALSE;
+  if ((N = get_argument (argc, argv, "-nosort"))) {
+    options.nosort = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* only add new rows (-update) or re-write complete measure table (forces -nosort) */
+  options.update = FALSE;
+  if ((N = get_argument (argc, argv, "-update"))) {
+    options.update = TRUE;
+    options.nosort = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* only add image potion to image table */
+  options.only_images = FALSE;
+  if ((N = get_argument (argc, argv, "-image"))) {
+    options.only_images = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* apply average zpt offset calibration (image only) */
+  options.calibrate = FALSE;
+  if ((N = get_argument (argc, argv, "-cal"))) {
+    options.calibrate = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /*** optional situations ***/
+  /* choose high quality airmass vs low quality airmass (per-star vs per-image) */
+  options.quality_airmass = FALSE;
+  if ((N = get_argument (argc, argv, "-quality-airmass"))) {
+    remove_argument (N, &argc, argv);
+    options.quality_airmass = TRUE;
+  }
+  /* choose high quality airmass vs low quality airmass (per-star vs per-image) */
+  SUBPIX = FALSE;
+  if ((N = get_argument (argc, argv, "-subpix"))) {
+    remove_argument (N, &argc, argv);
+    SUBPIX = TRUE;
+  }
+  /* skyprobe means: subpix correction and quality airmass */ 
+  if ((N = get_argument (argc, argv, "-skyprobe"))) {
+    remove_argument (N, &argc, argv);
+    options.quality_airmass = TRUE;
+    SUBPIX = TRUE;
+  }
+  if (SUBPIX) load_subpix ();
+
+  /* define 2MASS quality flags to keep */
+  SELECT_2MASS_QUALITY = NULL;
+  if ((N = get_argument (argc, argv, "-2massquality"))) {
+    remove_argument (N, &argc, argv);
+    SELECT_2MASS_QUALITY = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* accept bad header astrometry */
+  ACCEPT_ASTROM = FALSE;
+  if ((N = get_argument (argc, argv, "-accept"))) {
+    ACCEPT_ASTROM = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* force read of image database with mismatched NSTARS & size */ 
+  FORCE_READ = FALSE;
+  if ((N = get_argument (argc, argv, "-force"))) {
+    FORCE_READ = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* over-ride autointerpretation of input data format */ 
+  TEXTMODE = FALSE;
+  if ((N = get_argument (argc, argv, "-textmode"))) {
+    TEXTMODE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  DUMP = NULL;
+  if ((N = get_argument (argc, argv, "-dump"))) {
+    remove_argument (N, &argc, argv);
+    DUMP = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: addstarc (filename)\n");
+    exit (2);
+  }
+  return (options);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  addstar (filename)\n");
+  fprintf (stderr, "     add specified image (cmp format) to database\n\n");
+  fprintf (stderr, "  addstar -ref (filename)");
+  fprintf (stderr, "     add ASCII data (ra dec mag dmag) to database\n\n");
+  fprintf (stderr, "  addstar -cat (catalog)");
+  fprintf (stderr, "     add data from catalog (USNO/2MASS/GSC) to database\n\n");
+
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -region ra ra dec dec 	  : only add data in specified region (-ref mode only)\n");
+  fprintf (stderr, "  -p (photcode)         	  : specify photcode (override header)\n");
+  fprintf (stderr, "  -time (YYYY/MM/DD,HH:MM:SS) : specify date/time (override header)\n");
+  fprintf (stderr, "  -mosaic (filename)    	  : identify associated mosaic frame for chip image\n");
+  fprintf (stderr, "  -fits                 	  : input file is FITS table, not TEXT table\n");
+  fprintf (stderr, "  -existing-regions           : only add measurements to existing catalog files\n");
+  fprintf (stderr, "  -only-match           	  : only add measurements to existing objects\n");
+  fprintf (stderr, "  -missed               	  : skipped 'missed' entries\n");
+  fprintf (stderr, "  -replace              	  : replace time/photcode measurements (no duplication)\n");
+  fprintf (stderr, "  -closest             	  : use closest-star algorith\n");
+  fprintf (stderr, "  -nosort             	  : don't re-sort the measure entries (improves speed)\n");
+  fprintf (stderr, "  -update             	  : only update the new rows (foreces -nosort)\n");
+  fprintf (stderr, "  -image                	  : only insert image data\n");
+  fprintf (stderr, "  -cal                  	  : perform zero-point calibration\n");
+  fprintf (stderr, "  -skyprobe             	  : specify skyprobe mode\n");
+  fprintf (stderr, "  -accept               	  : accept bad astrometry from header\n");
+  fprintf (stderr, "  -force                	  : force read of database with inconsistent info\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -dump (mode)          	  : output test data\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
+
+/** addstar modes:
+ 
+    addstar (image.smp)  - add cmp/smp image data to db
+    addstar -ref (file.dat) (photcode) 
+    addstar -cat (USNO/2MASS/GSC) -region (ra dec - ra dec)
+
+    -replace : ref/cat - replace existing match (photcode/time)
+    -match   : ref/cat - only add measures to existing averages
+
+    ref types: 
+    ASCII - RA,DEC,M,dM in a table
+
+    addstar 
+
+**/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_load2mass.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_load2mass.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_load2mass.c	(revision 22322)
@@ -0,0 +1,118 @@
+# include "addstar.h"
+static void help (void);
+
+AddstarClientOptions args_load2mass (int argc, char **argv, AddstarClientOptions options) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  // a global used by find_matches_refstars.c (value is 1 except for load2mass)
+  NREFSTAR_GROUP = 3;
+
+  /*** check for command line options ***/
+
+  /* basic mode: image, list, refcat */
+  options.mode = M_REFCAT;
+
+  /*** provide additional data ***/ 
+  /* restrict to a portion of the sky? */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax= 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* only add to existing objects */
+  options.only_match = FALSE;
+  if ((N = get_argument (argc, argv, "-only-match"))) {
+    options.only_match = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* replace measurement, don't duplicate (ref/cat only) */
+  options.replace = FALSE;
+  if ((N = get_argument (argc, argv, "-replace"))) {
+    options.replace = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* other addstar options which cannot be used in load2mass */
+  options.photcode = 0;
+  options.timeref = 0; 
+  options.mosaic = FALSE;
+  options.existing_regions = FALSE;
+  options.skip_missed = FALSE;
+  options.closest = FALSE;
+  options.nosort = FALSE;
+  options.update = FALSE;
+  options.only_images = FALSE;
+  options.calibrate = FALSE;
+  options.quality_airmass = FALSE;
+  ACCEPT_ASTROM = FALSE;
+  FORCE_READ = FALSE;
+  TEXTMODE = FALSE;
+  SUBPIX = FALSE;
+  DUMP = NULL;
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: load2mass\n");
+    exit (2);
+  }
+  return (options);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE: load2mass [options]\n");
+  fprintf (stderr, "  add data from 2MASS catalog to fullsky\n\n");
+
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -region Rmin Rmax Dmin Dmax : update only this portion of the sky\n");
+  fprintf (stderr, "  -only-match           	  : only add measurements to existing objects\n");
+  fprintf (stderr, "  -replace              	  : replace time/photcode measurements (no duplication)\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
+
+# if (0)
+  /* select quality flags */
+  SELECT_2MASS_QUALITY = NULL;
+  if ((N = get_argument (argc, argv, "-2massquality"))) {
+    remove_argument (N, &argc, argv);
+    SELECT_2MASS_QUALITY = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+# endif
+
+# if (0)  
+  /* override any header PHOTCODE values */
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    options.photcode = GetPhotcodeCodebyName (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+# endif
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_sedstar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_sedstar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_sedstar.c	(revision 22322)
@@ -0,0 +1,92 @@
+# include "addstar.h"
+static void help (void);
+
+AddstarClientOptions args_sedstar (int argc, char **argv, AddstarClientOptions options) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  // a global used by find_matches_refstars.c (value is 1 except for load2mass)
+  NREFSTAR_GROUP = 1;
+
+  /*** check for command line options ***/
+
+  /* basic mode: image, list, refcat */
+  options.mode = M_REFCAT;
+
+  /*** provide additional data ***/ 
+  /* restrict to a portion of the sky? (UNUSED) */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax= 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* extra error messages */
+  PLOT = FALSE;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    PLOT = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* other defaults */
+  options.timeref = 0; 
+  options.mosaic = FALSE;
+  options.existing_regions = FALSE;
+  options.skip_missed = FALSE;
+  options.closest = FALSE;
+  options.only_match = FALSE;
+  options.replace = FALSE;
+  options.nosort = FALSE;
+  options.update = FALSE;
+  options.only_images = FALSE;
+  options.calibrate = FALSE;
+  options.quality_airmass = FALSE;
+  ACCEPT_ASTROM = FALSE;
+  FORCE_READ = FALSE;
+  TEXTMODE = FALSE;
+  SUBPIX = FALSE;
+  DUMP = NULL;
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: sedstar (sedtable) (outcatalog) [-region Rmin Rmax Dmin Dmax]\n");
+    exit (2);
+  }
+  return (options);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  sedstar (SEDtable)");
+  fprintf (stderr, "     fit objects to stellar SEDs\n\n");
+
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -region Rmin Rmax Dmin Dmax : sky region for analysis\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_server.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_server.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_server.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "addstar.h"
+static void help (void);
+
+void args_server (int argc, char **argv) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  // a global used by find_matches_refstars.c (value is 1 except for load2mass)
+  NREFSTAR_GROUP = 1;
+
+  /* restrict to a portion of the sky? (REFCAT only) */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  /* define 2MASS quality flags to keep */
+  SELECT_2MASS_QUALITY = NULL;
+  if ((N = get_argument (argc, argv, "-2massquality"))) {
+    remove_argument (N, &argc, argv);
+    SELECT_2MASS_QUALITY = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* force read of image database with mismatched NSTARS & size */ 
+  FORCE_READ = FALSE;
+  if ((N = get_argument (argc, argv, "-force"))) {
+    FORCE_READ = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: addstard\n");
+    exit (2);
+  }
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  addstard\n");
+  fprintf (stderr, "  -force                	  : force read of database with inconsistent info\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_skycells.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_skycells.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/args_skycells.c	(revision 22322)
@@ -0,0 +1,109 @@
+# include "skycells.h"
+static void help (void);
+
+int args_skycells (int argc, char **argv) {
+  
+  int N;
+  char *ptr;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* what type of output files? */
+  MODE = SQUARES;
+  if ((N = get_argument (argc, argv, "-triangles"))) {
+    MODE = TRIANGLES;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* what type of output files? */
+  FIX_NS = FALSE;
+  if ((N = get_argument (argc, argv, "-fix-ns"))) {
+    FIX_NS = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* pixel scale (arcsec/pixel) */
+  SCALE = 1.0;
+  if ((N = get_argument (argc, argv, "-scale"))) {
+    remove_argument (N, &argc, argv);
+    SCALE = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* pixel scale (arcsec/pixel) */
+  EULER_A = EULER_B = 0.0;
+  if ((N = get_argument (argc, argv, "-euler"))) {
+    remove_argument (N, &argc, argv);
+    EULER_A = RAD_DEG*atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    EULER_B = RAD_DEG*atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* pixel scale (arcsec/pixel) */
+  PADDING = 0.0;
+  if ((N = get_argument (argc, argv, "-padding"))) {
+    remove_argument (N, &argc, argv);
+    PADDING = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* max number of skycells kept in memory */
+  NMAX = 200000;
+  if ((N = get_argument (argc, argv, "-nmax"))) {
+    remove_argument (N, &argc, argv);
+    NMAX = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* max number of skycells kept in memory */
+  NX_SUB = NY_SUB = 1;
+  if ((N = get_argument (argc, argv, "-nx"))) {
+    remove_argument (N, &argc, argv);
+    NX_SUB = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-ny"))) {
+    remove_argument (N, &argc, argv);
+    NY_SUB = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc == 2) {
+    strtol (argv[1], &ptr, 10);
+    if (*ptr == 0) return (TRUE);
+  }
+
+  fprintf (stderr, "USAGE: skycells (level) [-scale arcsec/pix] [-nx (Nx cells)] [-ny (Ny cells)]\n");
+  fprintf (stderr, "  [-h for details and other options]\n");
+  exit (2);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  skycells\n\n");
+
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -triangles                  : save base triangles instead of skycells\n");
+  fprintf (stderr, "  -fix-ns                     : orient skycells with y-axis aligned with Dec\n");
+  fprintf (stderr, "  -scale                      : set pixel scale (default 1.0 arcsec / pixel)\n");
+  fprintf (stderr, "  -padding                    : pad skycells by this fraction in each dimension\n");
+  fprintf (stderr, "  -nmax                    	  : only keep nmax skycells in memory\n");
+  fprintf (stderr, "  -nx                    	  : subdivide skycell projection in x\n");
+  fprintf (stderr, "  -ny                    	  : subdivide skycell projection in y\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/build_links.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/build_links.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/build_links.c	(revision 22322)
@@ -0,0 +1,200 @@
+# include "addstar.h"
+
+/* build the initial links assuming the table is sorted, 
+   not partial, and has a correct set of average[].measureOffset,Nmeasure values */
+int *init_measure_links (Average *average, int Naverage, Measure *measure, int Nmeasure) {
+
+  int i, j, N;
+  int *next;
+
+  N = 0;
+
+  ALLOCATE (next, int, Nmeasure);
+  for (i = 0; i < Naverage; i++, N++) {
+    for (j = 0; j < average[i].Nmeasure - 1; j++, N++) {
+      next[N] = N + 1;
+      if (N >= Nmeasure) {
+	fprintf (stderr, "WARNING: N out of bounds (1)\n");
+      }
+    }
+    next[N] = -1;
+    if (N >= Nmeasure) {
+      fprintf (stderr, "WARNING: N out of bounds (2)\n");
+    }
+
+    if (N >= Nmeasure) {
+      fprintf (stderr, "overflow in init_measure_links\n");
+      abort ();
+    }
+  }
+  return (next);
+}
+
+/* build the initial links assuming the table is sorted */
+int *init_missing_links (Average *average, int Naverage, Missing *missing, int Nmissing) {
+
+  int i, j, N;
+  int *next;
+
+  N = 0;
+
+  ALLOCATE (next, int, Nmissing);
+  for (i = 0; i < Naverage; i++) {
+    for (j = 0; j < average[i].Nmissing - 1; j++, N++) {
+      next[N] = N + 1;
+    }
+    if (average[i].Nmissing > 0) {
+      next[N] = -1;
+      if (N >= Nmissing) {
+	fprintf (stderr, "overflow in init_missing_links");
+	abort ();
+      }
+      N++;
+    }
+
+  }
+  return (next);
+}
+
+/* average[].measureOffset, average[].Nmeasure are valid within an addstar run */
+int add_meas_link (Average *average, int *next, int Nmeasure, int NMEASURE) {
+
+  int k, m;
+
+  /* if we have trouble, check validity of next[m] : m < Nmeasure */
+  m = average[0].measureOffset;  
+
+  for (k = 0; k < average[0].Nmeasure - 1; k++)  {
+    m = next[m];
+    if (m >= NMEASURE) {
+      fprintf (stderr, "WARNING: m out of bounds (3)\n");
+    }
+  }
+
+  /* set up references */
+  next[Nmeasure] = -1;
+  if (Nmeasure >= NMEASURE) {
+    fprintf (stderr, "WARNING: Nmeasure out of bounds (1)\n");
+  }
+
+  if (m == -1) {
+    average[0].measureOffset = Nmeasure;
+  } else {
+    next[m] = Nmeasure;
+    if (m >= NMEASURE) {
+      fprintf (stderr, "WARNING: m out of bounds (4)\n");
+    }
+  }
+
+  return (TRUE);
+}
+
+int add_miss_link (Average *average, int *next, int Nmissing) {
+
+  int k, m;
+
+  /* there may be 0 Nmiss; this is not true for Nmeas */
+  if (average[0].Nmissing < 1) {
+    average[0].missingOffset = Nmissing;
+    next[Nmissing] = -1;
+    return (TRUE);
+  }
+
+  m = average[0].missingOffset;  
+  for (k = 0; k < average[0].Nmissing - 1; k++) m = next[m];
+  /* set up references */
+  next[Nmissing] = -1;
+  next[m] = Nmissing;
+  return (TRUE);
+}
+
+/* construct measure links which are valid FOR THIS LOAD
+ * - if we have a full load, we will get links which can
+ *   be used by other programs (eg, relphot, etc)
+ * - if we have a partial load, the links are only valid
+ *   for that partial load
+ */ 
+
+int *build_measure_links (Average *average, int Naverage, Measure *measure, int Nmeasure) {
+
+  int i, m, k, Nm, averef;
+  int *next;
+
+  ALLOCATE (next, int, Nmeasure);
+
+  /* reset the Nm, offset values for average */
+  for (i = 0; i < Naverage; i++) {
+    average[i].measureOffset = -1;
+    average[i].Nmeasure     =  0;
+  }
+
+  for (Nm = 0; Nm < Nmeasure; Nm++) {
+    averef = measure[Nm].averef;
+    m = average[averef].measureOffset;  
+    next[Nm] = -1;
+
+    if (m == -1) { /* no links yet for source */
+      average[averef].measureOffset = Nm;
+      average[averef].Nmeasure     = 1;
+      continue;
+    }
+
+    for (k = 0; next[m] != -1; k++) {
+      m = next[m];
+      if (m >= Nmeasure) {
+	fprintf (stderr, "WARNING: m out of bounds (1)\n");
+      }
+    }
+
+    average[averef].Nmeasure = k + 2;
+    next[m] = Nm;
+    if (m >= Nmeasure) {
+      fprintf (stderr, "WARNING: m out of bounds (2)\n");
+    }
+  }
+  return (next);
+}
+
+/* Missing does not carry enough information to reconstruct the links
+   we must always save the missing table, if it exists */
+
+Measure *sort_measure (Average *average, int Naverage, Measure *measure, int Nmeasure, int *next) {
+
+  int i, k, n, N;
+  Measure *tmpmeasure;
+
+  /* fix order of Measure (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmeasure, Measure, Nmeasure);
+  for (i = 0; i < Naverage; i++) {
+    n = average[i].measureOffset;
+    average[i].measureOffset = N;
+    for (k = 0; k < average[i].Nmeasure; k++, N++) {
+      tmpmeasure[N] = measure[n]; 
+      tmpmeasure[N].averef = i;
+      n = next[n];
+    }
+  }
+  free (measure);
+  return (tmpmeasure);
+}
+
+Missing *sort_missing (Average *average, int Naverage, Missing *missing, int Nmissing, int *next) {
+
+  int i, k, n, N;
+  Missing *tmpmissing;
+
+  /* fix order of Missing (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmissing, Missing, Nmissing);
+  for (i = 0; i < Naverage; i++) {
+    n = average[i].missingOffset;
+    average[i].missingOffset = N;
+    for (k = 0; k < average[i].Nmissing; k++, N++) {
+      tmpmissing[N] = missing[n]; 
+      n = next[n];
+    }
+  }
+  free (missing);
+  return (tmpmissing);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/calibrate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/calibrate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/calibrate.c	(revision 22322)
@@ -0,0 +1,233 @@
+# include "addstar.h"
+
+static int InternalCal;
+static int Ncal, NCAL, *Nstar;
+static float *Mobs, *dMobs, *Mref, *dMref, *Minst;
+
+void InitCalibration (int mode) {
+
+  InternalCal = mode;
+
+  fprintf (stderr, "calibrating the image...\n");
+  Ncal = 0;
+  NCAL = 1000;
+  ALLOCATE (Mobs,  float,  NCAL);
+  ALLOCATE (dMobs, float, NCAL);
+  ALLOCATE (Mref,  float,  NCAL);
+  ALLOCATE (dMref, float,  NCAL);
+  ALLOCATE (Minst, float,  NCAL);
+  ALLOCATE (Nstar, int, NCAL);
+}
+  
+void SaveCalibration (float Mo, float dMo, float Mr, float dMr, float Mi, int N) {
+
+  Mobs[Ncal]  = Mo;
+  dMobs[Ncal] = dMo;
+  Mref[Ncal]  = Mr;
+  dMref[Ncal] = dMr;
+  Minst[Ncal] = Mi;
+  Nstar[Ncal] = N;
+  Ncal ++;
+
+  if (Ncal == NCAL) {
+    NCAL += 1000;
+    REALLOCATE (Mobs,  float, NCAL);
+    REALLOCATE (dMobs, float, NCAL);
+    REALLOCATE (Mref,  float, NCAL);
+    REALLOCATE (dMref, float, NCAL);
+    REALLOCATE (Minst, float, NCAL);
+    REALLOCATE (Nstar, int,   NCAL);
+  }
+}
+
+/* use the linked list to navigate the measures; safe for unsorted measures */
+void AddToCalibration (Average *average, SecFilt *secfilt, Measure *measure, Measure *new, int *next, int Nstar) {
+
+  int i, j, m, Nsec, found0, found1, found2;
+  float CalM0, CalM1, CalM2, dCalM;
+  float Mcal, color, factor, Minst;
+  short CalC0, CalC1, CalC2;
+
+  PhotCode *mycode;  // photcode of this measurement
+  PhotCode *incode;  // mycode.equiv (internal reference)
+  PhotCode *excode;  // incode.equiv (external reference)
+
+  found0 = found1 = found2 = FALSE;
+  CalM0 = CalM1 = CalM2 = dCalM = NAN;
+
+  // we have two options here: 
+  //  - calibrate to internal system (Mcal)
+  //  - calibrate to external system (Mref)
+
+  mycode = GetPhotcodebyCode (new[0].photcode);
+  incode = GetPhotcodebyCode (mycode[0].equiv);
+  excode = GetPhotcodebyCode (incode[0].equiv);
+
+  if (InternalCal) {
+    CalC0 = incode[0].code;
+    Nsec  = GetPhotcodeNsec (CalC0);
+  } else {
+    CalC0 = excode[0].code;
+    Nsec  = GetPhotcodeNsec (CalC0);
+  }
+  /* check if this reference code is an average magnitude */
+  if (Nsec != -1) {
+    CalM0 = secfilt[Nsec].M;
+    dCalM = secfilt[Nsec].dM;
+    found0 = TRUE;
+  }
+
+  CalC1 = mycode[0].c1;
+  Nsec  = GetPhotcodeNsec (CalC1);
+  if (Nsec != -1) {
+    CalM1 = secfilt[Nsec].M;
+    found1 = TRUE;
+  }
+
+  CalC2 = mycode[0].c2;
+  Nsec  = GetPhotcodeNsec (CalC2);
+  if (Nsec != -1) {
+    CalM2 = secfilt[Nsec].M;
+    found2 = TRUE;
+  }
+
+  if (!CalC1 && !CalC2) {
+    found1 = found2 = TRUE;
+    CalM1 = CalM2 = 0.0;
+  }
+
+  m = average[0].measureOffset;
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    if (measure[m].photcode == CalC0) { 
+      found0 = TRUE; 
+      CalM0  = measure[m].M; 
+      dCalM  = measure[m].dM; 
+    }
+    if (measure[m].photcode == CalC1) { 
+      found1 = TRUE; 
+      CalM1  = measure[m].M; 
+    }
+    if (measure[m].photcode == CalC2) { 
+      found2 = TRUE; 
+      CalM2  = measure[m].M; 
+    }
+    if (found0 && found1 && found2) {
+      Mcal   = new[0].M + 0.001*mycode[0].C + mycode[0].K*(new[0].airmass - 1.0) - GetZeroPoint();
+      color  = CalM1 - CalM2 - 0.001*mycode[0].dX;
+      factor = color;
+      for (j = 0; j < mycode[0].Nc; j++) {
+	Mcal += mycode[0].X[j]*factor;
+	factor *= color;
+      }
+      if (!InternalCal) {
+	Mcal += 0.001*incode[0].C;
+      }
+      // if we want to apply a Mcal -> Mref color correction, we need the additional color term
+      Minst = new[0].M - GetZeroPoint() - new[0].dt;
+      SaveCalibration (Mcal, new[0].dM, CalM0, dCalM, Minst, Nstar);
+      if ((DUMP != NULL) && !strcmp (DUMP, "cal")) {
+	fprintf (stdout, "cal-match : %10.6f %10.6f : %7.4f %6.4f  %7.4f %6.4f   %7.4f : %7.4f %7.4f\n", average[0].R, average[0].D, Mcal, new[0].dM, CalM0, dCalM, Minst, new[0].airmass, color);
+      }
+      return;
+    }
+    m = next[m];
+  }
+  return;
+}
+
+void FindCalibration (Image *image) {
+
+  int i, MaxN, *Nlist, Nkeep;
+  float N, M1, M2, *Dmag, *dDmag;
+  float dMo, dMr, Mw, Dmed, W1, W2, NSigma;
+
+  /* reject multiple matched-stars */
+  /* find maximum value of Nstar[] */
+  MaxN = -1;
+  for (i = 0; i < Ncal; i++) {
+    MaxN = MAX (Nstar[i], MaxN);
+  }
+  if (MaxN == -1) {
+    fprintf (stderr, "no clean stars\n");
+    image[0].Mcal = 10.000;
+    image[0].dMcal = 10.000;
+    return;
+  }
+  /* create a hash array from Nstar[] entries */
+  ALLOCATE (Nlist, int, MaxN + 1);
+  memset (Nlist, 0, MAX (0, MaxN*sizeof(int)));
+  for (i = 0; i < Ncal; i++) {
+    Nlist[Nstar[i]] ++;
+  }
+  
+  /* accumulate delta mags */
+  ALLOCATE (Dmag, float, Ncal);
+  ALLOCATE (dDmag, float, Ncal);
+  Nkeep = 0;
+  for (i = 0; i < Ncal; i++) {
+    /* if this entry has too many (or two few?) matches, skip it */
+    if (Nlist[Nstar[i]] != 1) continue;
+
+    /* clip by instrumental magnitude */
+    if (Minst[i] > CAL_INSTMAG_MAX) continue;
+    if (Minst[i] < CAL_INSTMAG_MIN) continue;
+    
+    /* XXX EAM: note the artificial 0.005 dmag here */
+    dMr = MAX (0.005, dMref[i]);
+    dMo = MAX (0.005, dMobs[i]);
+
+    Dmag[Nkeep] = (Mobs[i] - Mref[i]);
+    dDmag[Nkeep] = (dMr*dMr + dMo*dMo);
+    Nkeep ++;
+  }
+
+  if (Nkeep < 5) {
+    fprintf (stderr, "too few stars\n");
+    image[0].Mcal = 10.000;
+    image[0].dMcal = 10.000;
+    return;
+  }
+  fsortpair (Dmag, dDmag, Nkeep);
+
+  /* take sort list of Dmag, find median */
+  Dmed = Dmag[(int)(0.5*Nkeep)];
+
+  /* exclude points with abs(Dmag - Dmed) / dDmag > 2.5 */
+
+  /* accumulate delta mags (25% - 75% of clipped range) */
+  W1 = 0.0;
+  W2 = 0.0;
+  M1 = 0.0;
+  M2 = 0.0;
+  N  = 0.0;
+  for (i = 0; i < Nkeep; i++) {
+    NSigma = fabs (Dmag[i] - Dmed) / sqrt (dDmag[i]);
+    if (NSigma > 2.5) continue;
+    W1 += Dmag[i] / dDmag[i];
+    W2 += 1 / dDmag[i];
+    M1 += Dmag[i];
+    M2 += SQ (Dmag[i]);
+    N  += 1.0; 
+  }
+
+  if (N > 1) {
+    M1 = M1 / N;
+    M2 = sqrt (fabs(M2/N - M1*M1));
+    Mw = W1 / W2;
+    fprintf (stdout, "STATUS: SUCCESS\n");
+    fprintf (stdout, "ZERO_POINT_MEAN      = %7.4f\n", M1);
+    fprintf (stdout, "ZERO_POINT_WTMEAN    = %7.4f\n", Mw);
+    fprintf (stdout, "ZERO_POINT_STDEV     = %7.4f\n", M2);
+    fprintf (stdout, "ZERO_POINT_PRECISION = %7.4f\n", M2 / sqrt (N));
+    fprintf (stdout, "ZERO_POINT_NSTARS    =    %4.0f\n", N);
+    // fprintf (stderr, "N: %.0f, mean: %f, wt mean: %f, stdev: %f, precision: %f\n", N, M1, Mw, M2, M2 / sqrt (N));
+    image[0].Mcal = M1;
+    image[0].dMcal = M2 / sqrt (N);
+    image[0].Mxxxx = N;
+  } else {
+    fprintf (stderr, "too few stars\n");
+    image[0].Mcal = 10.000;
+    image[0].dMcal = 10.000;
+    image[0].Mxxxx = 0;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/dump.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/dump.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/dump.c	(revision 22322)
@@ -0,0 +1,21 @@
+# include "addstar.h"
+
+int dump_rawstars (Stars *stars, int Nstars) {
+
+  int i;
+  FILE *f;
+
+  f = fopen ("stars.dat", "w");
+
+  for (i = 0; i < Nstars; i++) {
+    fprintf (f, "%4d  %10.6f %10.6f  %8.2f %8.2f  %7.2f %7.2f\n", 
+	     i, 
+	     stars[i].R, stars[i].D,
+	     stars[i].X, stars[i].Y,
+	     stars[i].M, stars[i].dM);
+  }
+
+  fclose (f);
+  exit (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/edge_check.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/edge_check.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/edge_check.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "addstar.h"
+
+int edge_check (double *x1, double *y1, double *x2, double *y2) {
+
+  double theta1, theta2;
+  double Theta1, Theta2;
+
+  theta1 = opening_angle (x1[0], y1[0], x2[0], y2[0], x1[1], y1[1]); 
+  theta2 = opening_angle (x1[0], y1[0], x2[0], y2[0], x2[1], y2[1]); 
+
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  Theta1 = theta1;
+  Theta2 = theta2;
+  theta1 = opening_angle (x2[0], y2[0], x1[1], y1[1], x2[1], y2[1]); 
+  theta2 = opening_angle (x2[0], y2[0], x1[1], y1[1], x1[0], y1[0]); 
+  
+ 
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/fakeimage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/fakeimage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/fakeimage.c	(revision 22322)
@@ -0,0 +1,199 @@
+# include "addstar.h"
+
+Image *fakeimage (char *rootname, int *Nimage, int photcode) {
+
+  int i, j, Nx, Ny, Nchips;
+  double pltscale, pixscale;
+  double Rmin, Rmax, Dmin, Dmax;
+  // double Xmin, Xmax, Ymin, Ymax;
+  double dX, dY, r, d;
+  char chipname[80], chipdata[256], name[80];
+  char *config;
+  Image *image;
+  e_time MosaicTime;
+  Coords MOSAIC;
+
+  /* this is a somewhat bogus method to set a time for the exposure */
+  struct timeval now;
+  long int seedval;
+
+  gettimeofday (&now, NULL);
+  seedval = now.tv_sec + now.tv_usec;
+  srand48(seedval);
+
+  MosaicTime = 0xffffffff * drand48();
+  fprintf (stderr, "time: %x\n", MosaicTime);
+
+  /* load in the camera layout file */
+  config = LoadConfigFile (CameraLayout);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find camera layout file %s\n", CameraLayout);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded camera layout file: %s\n", CameraLayout);
+
+  /* create a mosaic distortion structure */
+  strcpy (MOSAIC.ctype, "RA---DIS");
+  MOSAIC.crval1 = FAKE_RA;
+  MOSAIC.crval2 = FAKE_DEC;
+  
+  MOSAIC.crpix1 = MOSAIC.crpix2 = 0.0;
+  
+  /* mosaic 'pixels' are millimeters */
+  ScanConfig (config, "PLATE_SCALE",   "%lf", 0, &pltscale);
+  MOSAIC.cdelt1 = MOSAIC.cdelt2 = pltscale / 3600.0;
+
+  MOSAIC.pc1_1 =  cos(FAKE_THETA*RAD_DEG);
+  MOSAIC.pc1_2 = -sin(FAKE_THETA*RAD_DEG);
+  MOSAIC.pc2_1 =  sin(FAKE_THETA*RAD_DEG);
+  MOSAIC.pc2_2 =  cos(FAKE_THETA*RAD_DEG);
+
+  MOSAIC.Npolyterms = 3;
+  for (i = 0; i < 7; i++) {
+    MOSAIC.polyterms[i][0] = 0;
+    MOSAIC.polyterms[i][1] = 0;
+  }
+  ScanConfig (config, "DPLATE_X",   "%lf", 0, &pltscale);
+  MOSAIC.polyterms[3][0] = pltscale;  // L : X^3 Y^0
+  MOSAIC.polyterms[5][0] = pltscale;  // L : X^1 Y^2
+  ScanConfig (config, "DPLATE_Y",   "%lf", 0, &pltscale);
+  MOSAIC.polyterms[4][1] = pltscale;  // M : X^0 Y^0
+  MOSAIC.polyterms[6][1] = pltscale;  // M : X^2 Y^0
+
+  RegisterMosaic (&MOSAIC);
+
+  /* some basic data about the chisp */
+  ScanConfig (config, "NCHIPS", "%d", 0, &Nchips);
+  ScanConfig (config, "NAXIS1", "%d", 0, &Nx);
+  ScanConfig (config, "NAXIS2", "%d", 0, &Ny);
+  ScanConfig (config, "PIXEL_SCALE", "%lf", 0, &pixscale);
+
+  ALLOCATE (image, Image, Nchips + 1);
+  
+  Rmin = Rmax = Dmin = Dmax = 0;
+
+  /* define the chip images (1 - Nchips) */
+  for (i = 0; i < Nchips; i++) {
+    /* this is the addstar name for the chip in the camera */
+    sprintf (chipname, "CHIP.%03d", i);
+    ScanConfig (config, chipname, "%s", 0, chipdata);
+
+    sscanf (chipdata, "%s %lf %lf", chipname, &dX, &dY);
+    // if (VERBOSE) fprintf (stderr, "chip %s (%f,%f)\n", chipname, dX, dY);
+
+    /* this is the camera-specific name of a chip */
+    sprintf (name, "%s.%s", rootname, chipname);
+    strcpy (image[i+1].name, name);
+
+    strcpy (image[i+1].coords.ctype, "RA---WRP");
+    
+    image[i+1].coords.crval1 = dX*pixscale;
+    image[i+1].coords.crval2 = dY*pixscale;
+    
+    image[i+1].coords.crpix1 = image[i+1].coords.crpix2 = 0.0;
+    image[i+1].coords.cdelt1 = image[i+1].coords.cdelt2 = pixscale;
+
+    image[i+1].coords.pc1_1 = 1.0;
+    image[i+1].coords.pc1_2 = 0.0;
+    image[i+1].coords.pc2_1 = 0.0;
+    image[i+1].coords.pc2_2 = 1.0;
+
+    image[i+1].coords.Npolyterms = 0;
+    for (j = 0; j < 7; j++) {
+      image[i+1].coords.polyterms[j][0] = 0;
+      image[i+1].coords.polyterms[j][1] = 0;
+    }
+
+    image[i+1].sidtime  = 0.0;
+    image[i+1].latitude = 0.0;
+
+    image[i+1].cerror = 0.0;
+    
+    image[i+1].NX = Nx;
+    image[i+1].NY = Ny;
+
+    image[i+1].photcode = photcode;
+
+    image[i+1].exptime = 0.0;
+  
+    image[i+1].apmifit = 0.0;
+    image[i+1].dapmifit = 0.0;
+
+    image[i+1].detection_limit = 0.0;
+    image[i+1].saturation_limit = 0.0;
+    image[i+1].fwhm_x = 0.0;
+    image[i+1].fwhm_y = 0.0;
+    image[i+1].tzero = MosaicTime;
+    image[i+1].trate = 0;
+    image[i+1].secz = 1.0;
+    image[i+1].ccdnum = 0xff;
+
+    image[i+1].Mcal = 0.0;
+    image[i+1].Xm   = NAN_S_SHORT;
+    image[i+1].code = 0;
+
+    image[i+1].nstar = 0;
+    image[i+1].Myyyy = 0;
+
+    /* check if chip hits outer bounds of mosaic */
+    XY_to_RD (&r, &d, 0, 0, &image[i+1].coords);
+    Rmin = MIN (Rmin, r);
+    Rmax = MAX (Rmax, r);
+    Dmin = MIN (Dmin, d);
+    Dmax = MAX (Dmax, d);
+    XY_to_RD (&r, &d, Nx, 0, &image[i+1].coords);
+    Rmin = MIN (Rmin, r);
+    Rmax = MAX (Rmax, r);
+    Dmin = MIN (Dmin, d);
+    Dmax = MAX (Dmax, d);
+    XY_to_RD (&r, &d, 0, Ny, &image[i+1].coords);
+    Rmin = MIN (Rmin, r);
+    Rmax = MAX (Rmax, r);
+    Dmin = MIN (Dmin, d);
+    Dmax = MAX (Dmax, d);
+    XY_to_RD (&r, &d, Nx, Ny, &image[i+1].coords);
+    Rmin = MIN (Rmin, r);
+    Rmax = MAX (Rmax, r);
+    Dmin = MIN (Dmin, d);
+    Dmax = MAX (Dmax, d);
+  }
+
+  /* define the mosaic image */
+  strcpy (image[0].name, rootname);
+
+  image[0].coords = MOSAIC;
+  strcpy (image[0].coords.ctype, MOSAIC.ctype);
+
+  image[0].sidtime  = 0.0;
+  image[0].latitude = 0.0;
+  image[0].cerror = 0.0;
+    
+  // RD_to_XY (&Xmax, &Ymax, Rmax, Dmax, MOSAIC);
+  // RD_to_XY (&Xmin, &Ymin, Rmin, Dmin, MOSAIC);
+  image[0].NX = Rmax - Rmin;
+  image[0].NY = Dmax - Dmin;
+
+  image[0].photcode = photcode;
+
+  image[0].exptime = 0.0;
+  image[0].apmifit = 0.0;
+  image[0].dapmifit = 0.0;
+  image[0].detection_limit = 0.0;
+  image[0].saturation_limit = 0.0;
+  image[0].fwhm_x = 0.0;
+  image[0].fwhm_y = 0.0;
+  image[0].tzero = MosaicTime;
+  image[0].trate = 0;
+  image[0].secz = 1.0;
+  image[0].ccdnum = 0xff;
+  image[0].Mcal = 0.0;
+  image[0].Xm   = NAN_S_SHORT;
+  image[0].code = 0;
+  image[0].nstar = 0;
+  image[0].Myyyy = 0;
+
+  // XXX need to set the imageID here
+
+  *Nimage = Nchips + 1;
+  return (image);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_matches.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_matches.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_matches.c	(revision 22322)
@@ -0,0 +1,396 @@
+# include "addstar.h"
+
+int find_matches (SkyRegion *region, Stars *stars, int NstarsIn, Catalog *catalog, AddstarClientOptions options) {
+
+  int i, j, n, N, J, status, Nstars;
+  double RADIUS, RADIUS2;
+  double *X1, *Y1, *X2, *Y2;
+  double dX, dY, dR;
+  int *N1, *N2,  *next_meas;
+  int Nave, NAVE, Nmeas, NMEAS, Nmatch;
+  int Nsecfilt, Nsec;
+  unsigned int objID, catID;
+  Coords tcoords;
+
+  /* photcode data - must by of type DEP; options.photcode is equiv photcode for all input
+     images this function requires incoming stars to have the same photcode.equiv value.  if
+     this value is not a valid photcode (ie, 0), then no modification is made to the average 
+     magnitudes (Nsec will be -1) */
+
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  Nsec     = GetPhotcodeNsec (options.photcode);
+
+  /** allocate local arrays (stars) **/
+  ALLOCATE (X1, double, NstarsIn);
+  ALLOCATE (Y1, double, NstarsIn);
+  ALLOCATE (N1, int,   NstarsIn);
+
+  /** allocate local arrays (catalog) **/
+  NAVE = Nave = catalog[0].Naverage;
+  ALLOCATE (X2, double, NAVE);
+  ALLOCATE (Y2, double, NAVE);
+  ALLOCATE (N2, int,   NAVE);
+  ALLOCATE (catalog[0].found, int, NAVE);
+  /* for secfilt j and star i, secfilt[i*Nsecfilt+j] */
+
+  /* internal counters */
+  Nmatch = 0;
+  NMEAS = Nmeas = catalog[0].Nmeasure;
+  
+  // current max obj ID for this catalog
+  objID = catalog[0].objID;
+  catID = catalog[0].catID;
+
+  /* project onto rectilinear grid with 1 arcsec pixels. the choice of ZEA projection has the
+   * advantage that every point in R,D has a mapping to a unique X,Y.  However, note that not all
+   * possible X,Y points map back to R,D and the local plate scale changes substantially far from
+   * the projection pole.  a better mapping might be ARC, not yet implemented (see
+   * coordops.update.c).  We use the center of the region (catalog) for crval1,2. 
+   */
+  tcoords.crval1 = 0.5*(region[0].Rmin + region[0].Rmax);
+  if (region[0].Dmax < 90) {
+    tcoords.crval2 = 0.5*(region[0].Dmin + region[0].Dmax);
+  } else {
+    tcoords.crval2 = 90.0;
+  }
+  tcoords.crpix1 = 0;
+  tcoords.crpix2 = 0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+  tcoords.Npolyterms = 1;
+  strcpy (tcoords.ctype, "RA---ARC");
+
+  /* build spatial index (RA sort) */
+  Nstars = 0;
+  for (i = 0; i < NstarsIn; i++) {
+    status = RD_to_XY (&X1[Nstars], &Y1[Nstars], stars[i].R, stars[i].D, &tcoords);
+    if (!status) continue;
+    N1[Nstars] = i;
+    Nstars ++;
+  }
+  if (Nstars < 1) {
+    if (VERBOSE) fprintf (stderr, "skipping %s, no overlapping stars\n", catalog[0].filename);
+    free (catalog[0].found);
+    free (X1);
+    free (Y1);
+    free (N1);
+    free (X2);
+    free (Y2);
+    free (N2);
+    return (0);
+  }
+  if (Nstars > 1) sort_coords_index (X1, Y1, N1, Nstars);
+  
+  /* build spatial index (RA sort) */
+  for (i = 0; i < Nave; i++) {
+    RD_to_XY (&X2[i], &Y2[i], catalog[0].average[i].R, catalog[0].average[i].D, &tcoords);
+    N2[i] = i;
+    catalog[0].found[N2[i]] = -1;
+  }
+  if (Nave > 1) sort_coords_index (X2, Y2, N2, Nave);
+
+  /* set up pointers for linked list of measure */
+  if (catalog[0].sorted && (catalog[0].Nmeasure == catalog[0].Nmeas_disk)) {
+    next_meas = init_measure_links (catalog[0].average, Nave, catalog[0].measure, Nmeas);
+  } else {
+    next_meas = build_measure_links (catalog[0].average, Nave, catalog[0].measure, Nmeas);
+  }    
+
+  /* choose a radius for matches (defined in args.c or ImageOptions.c) */
+  RADIUS = options.radius;
+  RADIUS2 = RADIUS*RADIUS;
+
+  /** find matched stars **/
+  for (i = j = 0; (i < Nstars) && (j < Nave); ) {
+        if (!finite(X1[i]) || !finite(Y1[i])) { 
+      i++; 
+      continue;
+    }
+    if (!finite(X2[j]) || !finite(Y2[j])) { 
+      j++; 
+      continue;
+    }
+
+    /* negative dX: j is too large */
+    dX = X1[i] - X2[j];
+    if (dX <= -2*RADIUS) {
+      i++;
+      continue;
+    }
+    /* positive dX, i is too large */
+    if (dX >= 2*RADIUS) {
+      j++;
+      continue;
+    }
+
+    /* within match range; look for matches */
+    for (J = j; (dX > -2*RADIUS) && (J < Nave); J++) {
+      dX = X1[i] - X2[J];
+      dY = Y1[i] - Y2[J];
+      dR = dX*dX + dY*dY;
+      if (dR > RADIUS2) continue;
+
+      /* make sure there is space for next entry */
+      if (Nmeas >= NMEAS) {
+	NMEAS = Nmeas + 1000;
+	REALLOCATE (next_meas, int, NMEAS);
+	REALLOCATE (catalog[0].measure, Measure, NMEAS);
+      }
+
+      Nmatch ++;
+      n = N2[J];
+      N = N1[i];
+
+      /* add to end of measurement list */
+      add_meas_link (&catalog[0].average[n], next_meas, Nmeas, NMEAS);
+
+      /** add measurements for this star **/
+      /** dR,dD now represent arcsec **/
+      catalog[0].measure[Nmeas].dR       = 3600.0*(catalog[0].average[n].R - stars[N].R);
+      if (catalog[0].measure[Nmeas].dR > +180.0*3600.0) {
+	  // average on high end of boundary, move star up
+	  stars[N].R += 360.0;
+	  catalog[0].measure[Nmeas].dR = 3600.0*(catalog[0].average[n].R - stars[N].R);
+      }
+      if (catalog[0].measure[Nmeas].dR < -180.0*3600.0) {
+	  // average on low end of boundary, move star down
+	  stars[N].R -= 360.0;
+	  catalog[0].measure[Nmeas].dR = 3600.0*(catalog[0].average[n].R - stars[N].R);
+      }
+      if (fabs(catalog[0].measure[Nmeas].dR) > 10*RADIUS) {
+	  fprintf (stderr, "error: %10.6f,%10.6f vs %10.6f,%10.6f (%f,%f vs %f,%f)\n", 
+		   catalog[0].average[n].R, catalog[0].average[n].D, 
+		   stars[N].R, stars[N].D,
+		   X1[i], X2[J], 
+		   Y1[i], Y2[J]);
+      }
+      catalog[0].measure[Nmeas].dD       = 3600.0*(catalog[0].average[n].D - stars[N].D);
+
+      catalog[0].measure[Nmeas].Xccd     = stars[N].X;
+      catalog[0].measure[Nmeas].Yccd     = stars[N].Y;
+
+      catalog[0].measure[Nmeas].M        = stars[N].M;
+      catalog[0].measure[Nmeas].dM       = stars[N].dM;  /* error in input files stored in thousandths of mag */
+      catalog[0].measure[Nmeas].Mcal     = stars[N].Mcal;
+      catalog[0].measure[Nmeas].t        = stars[N].t;
+      catalog[0].measure[Nmeas].averef   = n;              /* this must be an absolute sequence number, if partial average is loaded */
+      catalog[0].measure[Nmeas].photcode = stars[N].code;  /* photcode */
+      catalog[0].measure[Nmeas].dophot   = stars[N].dophot;  
+      catalog[0].measure[Nmeas].dbFlags  = 0;
+      catalog[0].measure[Nmeas].dt       = stars[N].dt;
+      catalog[0].measure[Nmeas].airmass  = stars[N].airmass;
+
+      catalog[0].measure[Nmeas].photFlags = stars[N].flags;
+      catalog[0].measure[Nmeas].qPSF      = stars[N].psfQual;
+      catalog[0].measure[Nmeas].psfChisq  = stars[N].psfChisq;
+      catalog[0].measure[Nmeas].crNsigma  = stars[N].crNsigma;
+      catalog[0].measure[Nmeas].extNsigma = stars[N].extNsigma;
+      catalog[0].measure[Nmeas].Sky       = stars[N].sky;
+      catalog[0].measure[Nmeas].dSky      = stars[N].dsky;
+
+      catalog[0].measure[Nmeas].stargal   = 0; // XXX not yet set
+
+      catalog[0].measure[Nmeas].detID     = stars[N].detID;
+      catalog[0].measure[Nmeas].imageID   = options.imageID;
+
+      catalog[0].measure[Nmeas].dXccd     = stars[N].dX;
+      catalog[0].measure[Nmeas].dYccd     = stars[N].dY;
+
+      catalog[0].measure[Nmeas].Map      = stars[N].Map;
+      catalog[0].measure[Nmeas].FWx      = 100*stars[N].fx;
+      catalog[0].measure[Nmeas].FWy      = 100*stars[N].fy;
+      catalog[0].measure[Nmeas].theta    = (0xffff/360.0)*stars[N].df;
+	
+      /* adds the measurement to the calibration if appropriate color terms are found */
+      /* we call this before (optionally) setting the average magnitude to avoid auto-correlations */
+      if (options.calibrate) {
+	AddToCalibration (&catalog[0].average[n], &catalog[0].secfilt[n*Nsecfilt], catalog[0].measure, &catalog[0].measure[Nmeas], next_meas, N);
+      }
+
+      /* set the average magnitude if not already set and if photcode.equiv is not 0 */
+      /* in UPDATE mode, this value is not saved; use relphot to recalculate */
+      if (Nsec > -1) { 
+	if (isnan(catalog[0].secfilt[n*Nsecfilt+Nsec].M)) {
+	  catalog[0].secfilt[n*Nsecfilt+Nsec].M = PhotCat (&catalog[0].measure[Nmeas]);
+	}
+      }
+
+      /*** flag multiple stars */
+      /* this image star matches more than one catalog star */
+      if (stars[N].found > -1) {
+	catalog[0].measure[stars[N].found].dbFlags |= ID_MEAS_BLEND_MEAS;
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_MEAS;
+      } 
+      if (stars[N].found == -2) { /* this image star matches a catalog star on a neighboring catalog */
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_MEAS_X;
+      } 
+      if (stars[N].found == -1) { /* this image star matches only this catalog star */
+	stars[N].found = Nmeas;  /* save first match, in case coincidences are found */
+      }
+      /* this catalog star matches more than one image star */
+      if (catalog[0].found[n] > -1) {
+	catalog[0].measure[catalog[0].found[n]].dbFlags |= ID_MEAS_BLEND_OBJ;
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_OBJ;
+      } else {
+	catalog[0].found[n] = Nmeas;
+      }
+      /* Nm is updated, but not written out in -update mode (for existing entries)
+	 Nm is recalculated in build_meas_links if loaded table is not sorted */
+      catalog[0].average[n].Nmeasure ++;
+      Nmeas ++;
+
+      if (!options.update) {
+	/* in UPDATE mode, newly calculated coordinates are not saved */
+	update_coords (&catalog[0].average[n], &catalog[0].measure[0], next_meas);
+      }
+    }
+    i++;
+  }
+
+  /* incorporate unmatched image stars, if this star is in field of this catalog */
+  /* these new entries are all written out in UPDATE mode */ 
+  for (i = 0; (i < Nstars) && !options.only_match; i++) {
+    /* make sure there is space for next entry */
+    if (Nmeas >= NMEAS) {
+      NMEAS = Nmeas + 1000;
+      REALLOCATE (next_meas, int, NMEAS);
+      REALLOCATE (catalog[0].measure, Measure, NMEAS);
+    }
+    if (Nave >= NAVE) {
+      NAVE = Nave + 1000;
+      REALLOCATE (catalog[0].average, Average, NAVE);
+      REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*catalog[0].Nsecfilt);
+    }
+
+    N = N1[i];
+    if (stars[N].found >= 0) continue;
+    if (!IN_REGION (stars[N].R, stars[N].D)) continue;
+
+    catalog[0].average[Nave].R         	   = stars[N].R;
+    catalog[0].average[Nave].D         	   = stars[N].D;
+    catalog[0].average[Nave].Xp        	   = 0;
+    catalog[0].average[Nave].Nmeasure      = 1;
+    catalog[0].average[Nave].Nmissing      = 0;
+    catalog[0].average[Nave].measureOffset = Nmeas;
+    catalog[0].average[Nave].missingOffset = -1;
+    catalog[0].average[Nave].code          = 0;
+
+    catalog[0].average[Nave].dR        = 0;
+    catalog[0].average[Nave].dD        = 0;
+    catalog[0].average[Nave].uR        = 0;
+    catalog[0].average[Nave].uD        = 0;
+    catalog[0].average[Nave].duR       = 0;
+    catalog[0].average[Nave].duD       = 0;
+    catalog[0].average[Nave].P         = 0;
+    catalog[0].average[Nave].dP        = 0;
+
+    catalog[0].average[Nave].objID     = objID;
+    catalog[0].average[Nave].catID     = catID;
+    objID ++;
+
+    for (j = 0; j < Nsecfilt; j++) {
+      catalog[0].secfilt[Nave*Nsecfilt+j].M  = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].dM = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].Xm = NAN_S_SHORT;
+    }
+
+    catalog[0].measure[Nmeas].Xccd     = stars[N].X;
+    catalog[0].measure[Nmeas].Yccd     = stars[N].Y;
+
+    catalog[0].measure[Nmeas].dR       	= 0.0;
+    catalog[0].measure[Nmeas].dD       	= 0.0;
+    catalog[0].measure[Nmeas].M        	= stars[N].M;
+    catalog[0].measure[Nmeas].dM       	= stars[N].dM;
+    catalog[0].measure[Nmeas].Mcal  	= stars[N].Mcal;
+    catalog[0].measure[Nmeas].t        	= stars[N].t;
+    catalog[0].measure[Nmeas].averef   	= Nave;           /* XXX EAM : must be absolute Nave if partial read */
+    catalog[0].measure[Nmeas].photcode  = stars[N].code;  /* photcode */
+    catalog[0].measure[Nmeas].dophot   	= stars[N].dophot;  
+    catalog[0].measure[Nmeas].dbFlags 	= 0;
+    catalog[0].measure[Nmeas].dt    	= stars[N].dt;
+    catalog[0].measure[Nmeas].airmass   = stars[N].airmass;
+
+    catalog[0].measure[Nmeas].photFlags = stars[N].flags;
+    catalog[0].measure[Nmeas].qPSF      = stars[N].psfQual;
+    catalog[0].measure[Nmeas].psfChisq  = stars[N].psfChisq;
+    catalog[0].measure[Nmeas].crNsigma  = stars[N].crNsigma;
+    catalog[0].measure[Nmeas].extNsigma = stars[N].extNsigma;
+    catalog[0].measure[Nmeas].Sky       = stars[N].sky;
+    catalog[0].measure[Nmeas].dSky      = stars[N].dsky;
+
+    catalog[0].measure[Nmeas].stargal   = 0; // XXX not yet set
+
+    catalog[0].measure[Nmeas].detID     = stars[N].detID;
+    catalog[0].measure[Nmeas].imageID   = stars[N].imageID;
+
+    catalog[0].measure[Nmeas].dXccd     = stars[N].dX;
+    catalog[0].measure[Nmeas].dYccd     = stars[N].dY;
+
+    catalog[0].measure[Nmeas].Map  	= stars[N].Map;
+    catalog[0].measure[Nmeas].FWx      	= 100*stars[N].fx;
+    catalog[0].measure[Nmeas].FWy      	= 100*stars[N].fy;
+    catalog[0].measure[Nmeas].theta    	= (0xffff/360.0)*stars[N].df;
+    /* XXX replace df here with theta, right? */
+
+    /* set the average magnitude if not already set and the photcode.equiv is not 0 */
+    /* in UPDATE mode, this value is not saved; use relphot to recalculate */
+    if (Nsec > -1) { 
+	catalog[0].secfilt[Nave*Nsecfilt+Nsec].M = PhotCat (&catalog[0].measure[Nmeas]);
+    }
+
+    /* next[Nmeas] should always be -1 in this context (it is always the only
+       measurement for the star) */
+    stars[N].found = Nmeas;
+    next_meas[Nmeas] = -1;
+    Nmeas ++;
+    Nave ++;
+  }
+      
+  REALLOCATE (catalog[0].average, Average, Nave);
+  REALLOCATE (catalog[0].measure, Measure, Nmeas);
+ 
+  if (options.nosort) {
+    catalog[0].sorted = FALSE;
+  } else {
+    catalog[0].sorted = TRUE;
+    catalog[0].measure = sort_measure (catalog[0].average, Nave, catalog[0].measure, Nmeas, next_meas);
+  }
+
+  /* note stars which have been found in this catalog */
+  for (i = 0; i < NstarsIn; i++) {
+    if (stars[i].found > -1) {
+      stars[i].found = -2;
+    } else {
+      stars[i].found = -3;
+    }
+  }
+
+  /* check if the catalog has changed?  if no change, no need to write */
+  catalog[0].objID    = objID; // new max value, save on catalog close
+  catalog[0].Naverage = Nave;
+  catalog[0].Nmeasure = Nmeas;
+  catalog[0].Nsecf_mem = Nave*Nsecfilt;
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: %d %d %d, (%d matches)\n", Nstars, Nave, Nmeas, Nmatch);
+
+  free (catalog[0].found);
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (X2);
+  free (Y2);
+  free (N2);
+  return (Nmatch);
+}
+
+/* 
+   notes:
+   
+   for finding if a catalog star is in an image or an image star is in the catalog:
+   
+   catalogs have boundaries defined by RA and DEC, but they may curve in projection
+   images have boundaries which are lines in pixels coords, but curve in RA and DEC
+   
+   catalog[0].found[Ncat] but stars[Nstar].found
+   
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_matches_closest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_matches_closest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_matches_closest.c	(revision 22322)
@@ -0,0 +1,399 @@
+# include "addstar.h"
+
+int find_matches_closest (SkyRegion *region, Stars *stars, int NstarsIn, Catalog *catalog, AddstarClientOptions options) {
+
+  int i, j, n, N, J, Jmin, status, Nstars;
+  double RADIUS, RADIUS2, Rmin;
+  double *X1, *Y1, *X2, *Y2;
+  double dX, dY, dR;
+  int *N1, *N2,  *next_meas, *next_miss;
+  int Nave, NAVE, Nmeas, NMEAS, Nmiss, NMISS, Nmatch;
+  int Nsecfilt, Nsec;
+  unsigned int objID, catID;
+  Coords tcoords;
+
+  /* photcode data - must by of type DEP; options.photcode is equiv photcode for all input
+     images this function requires incoming stars to have the same photcode.equiv value.  if
+     this value is not a valid photcode (ie, 0), then no modification is made to the average 
+     magnitudes (Nsec will be -1) */
+
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  Nsec     = GetPhotcodeNsec (options.photcode);
+
+  /** allocate local arrays (stars) **/
+  ALLOCATE (X1, double, NstarsIn);
+  ALLOCATE (Y1, double, NstarsIn);
+  ALLOCATE (N1, int,   NstarsIn);
+
+  /** allocate local arrays (catalog) **/
+  NAVE = Nave = catalog[0].Naverage;
+  ALLOCATE (X2, double, NAVE);
+  ALLOCATE (Y2, double, NAVE);
+  ALLOCATE (N2, int,   NAVE);
+  ALLOCATE (catalog[0].found, int, NAVE);
+  /* for secfilt j and star i, secfilt[i*Nsecfilt+j] */
+
+  /* internal counters */
+  Nmatch = 0;
+  NMEAS = Nmeas = catalog[0].Nmeasure;
+  NMISS = Nmiss = catalog[0].Nmissing;
+
+  // current max obj ID for this catalog
+  objID = catalog[0].objID;
+  catID = catalog[0].catID;
+
+  /* project onto rectilinear grid with 1 arcsec pixels. the choice of ZEA projection has the
+   * advantage that every point in R,D has a mapping to a unique X,Y.  However, note that not all
+   * possible X,Y points map back to R,D and the local plate scale changes substantially far from
+   * the projection pole.  a better mapping might be ARC, not yet implemented (see
+   * coordops.update.c).  We use the center of the region (catalog) for crval1,2. 
+   */
+  tcoords.crval1 = 0.5*(region[0].Rmin + region[0].Rmax);
+  if (region[0].Dmax < 90) {
+    tcoords.crval2 = 0.5*(region[0].Dmin + region[0].Dmax);
+  } else {
+    tcoords.crval2 = 90.0;
+  }
+  tcoords.crpix1 = 0;
+  tcoords.crpix2 = 0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+  tcoords.Npolyterms = 1;
+  strcpy (tcoords.ctype, "RA---ARC");
+
+  /* build spatial index (RA sort) referencing input array sequence */
+  Nstars = 0;
+  for (i = 0; i < NstarsIn; i++) {
+    status = RD_to_XY (&X1[Nstars], &Y1[Nstars], stars[i].R, stars[i].D, &tcoords);
+    if (!status) continue;
+    N1[Nstars] = i;
+    Nstars ++;
+  }
+  if (Nstars < 1) {
+    if (VERBOSE) fprintf (stderr, "skipping %s, no overlapping stars\n", catalog[0].filename);
+    free (catalog[0].found);
+    free (X1);
+    free (Y1);
+    free (N1);
+    free (X2);
+    free (Y2);
+    free (N2);
+    return (0);
+  }
+  if (Nstars > 1) sort_coords_index (X1, Y1, N1, Nstars);
+
+  /* build spatial index (RA sort) */
+  for (i = 0; i < Nave; i++) {
+    RD_to_XY (&X2[i], &Y2[i], catalog[0].average[i].R, catalog[0].average[i].D, &tcoords);
+    N2[i] = i;
+    catalog[0].found[N2[i]] = -1;
+  }
+  if (Nave > 1) sort_coords_index (X2, Y2, N2, Nave);
+
+  /* set up pointers for linked list of measure, missing */
+  if (catalog[0].sorted && (catalog[0].Nmeasure == catalog[0].Nmeas_disk)) {
+    // this version is only valid if we have done a full catalog load, and if the catalog
+    // is sorted while processed
+    next_meas = init_measure_links (catalog[0].average, Nave, catalog[0].measure, Nmeas);
+  } else {
+    next_meas = build_measure_links (catalog[0].average, Nave, catalog[0].measure, Nmeas);
+  }    
+  next_miss = init_missing_links (catalog[0].average, Nave, catalog[0].missing, Nmiss);
+  /* missing MUST be written 'sorted', or not at all */
+
+  /* choose a radius for matches */
+  RADIUS = options.radius; /* provided by config */
+  RADIUS2 = RADIUS*RADIUS;
+
+  /** find matched stars **/
+  for (i = j = 0; (i < Nstars) && (j < Nave); ) {
+    if (!finite(X1[i]) || !finite(Y1[i])) { 
+      i++; 
+      continue;
+    }
+    if (!finite(X2[j]) || !finite(Y2[j])) { 
+      j++; 
+      continue;
+    }
+    
+    /* negative dX: j is too large */
+    dX = X1[i] - X2[j];
+    if (dX <= -2*RADIUS) {
+      i++;
+      continue;
+    }
+    /* positive dX, i is too large */
+    if (dX >= 2*RADIUS) {
+      j++;
+      continue;
+    }
+
+    Jmin = -1;
+    Rmin = RADIUS2;
+    for (J = j; (dX > -2*RADIUS) && (J < Nave); J++) {
+      /* find closest match for this detection */
+      dX = X1[i] - X2[J];
+      dY = Y1[i] - Y2[J];
+      dR = dX*dX + dY*dY;
+      if (dR > RADIUS2) continue;
+      if (dR < Rmin) {
+	Rmin = dR;
+	Jmin  = J;
+      }
+    }
+
+    /* no match, try next detection */ 
+    if (Jmin == -1) {
+      i++;
+      continue;
+    }
+
+    /*** a match is found, add to average, measure ***/
+
+    /* make sure there is space for next entry */
+    if (Nmeas >= NMEAS) {
+      NMEAS = Nmeas + 1000;
+      REALLOCATE (next_meas, int, NMEAS);
+      REALLOCATE (catalog[0].measure, Measure, NMEAS);
+    }
+
+    Nmatch ++;
+    n = N2[Jmin];
+    N = N1[i];
+
+    /* add to end of measurement list */
+    add_meas_link (&catalog[0].average[n], next_meas, Nmeas, NMEAS);
+	
+    /** add measurements for this star **/
+    /** dR,dD now represent arcsec **/
+    catalog[0].measure[Nmeas].dR       = 3600.0*(catalog[0].average[n].R - stars[N].R);
+    if (catalog[0].measure[Nmeas].dR > +180.0*3600.0) {
+      // average on high end of boundary, move star up
+      stars[N].R += 360.0;
+      catalog[0].measure[Nmeas].dR = 3600.0*(catalog[0].average[n].R - stars[N].R);
+    }
+    if (catalog[0].measure[Nmeas].dR < -180.0*3600.0) {
+      // average on low end of boundary, move star down
+      stars[N].R -= 360.0;
+      catalog[0].measure[Nmeas].dR = 3600.0*(catalog[0].average[n].R - stars[N].R);
+    }
+    if (fabs(catalog[0].measure[Nmeas].dR) > 10*RADIUS) {
+      fprintf (stderr, "error: %10.6f,%10.6f vs %10.6f,%10.6f (%f,%f vs %f,%f)\n", 
+	       catalog[0].average[n].R, catalog[0].average[n].D, 
+	       stars[N].R, stars[N].D,
+	       X1[i], X2[Jmin], 
+	       Y1[i], Y2[Jmin]);
+    }
+    catalog[0].measure[Nmeas].dD       = 3600.0*(catalog[0].average[n].D - stars[N].D);
+
+    /* XXX need to add dX, dY : need to load into stars[N].dX,dY */
+    /* XXX need to add stargal, Sky, dSky, qPSF, detID, imageID */
+    catalog[0].measure[Nmeas].Xccd     = stars[N].X;
+    catalog[0].measure[Nmeas].Yccd     = stars[N].Y;
+
+    catalog[0].measure[Nmeas].M        = stars[N].M;
+    catalog[0].measure[Nmeas].dM       = stars[N].dM;  /* error in input files stored in thousandths of mag */
+    catalog[0].measure[Nmeas].Mcal     = stars[N].Mcal;
+    catalog[0].measure[Nmeas].t        = stars[N].t;
+    catalog[0].measure[Nmeas].averef   = n;
+    catalog[0].measure[Nmeas].photcode = stars[N].code;  /* photcode */
+    catalog[0].measure[Nmeas].dophot   = stars[N].dophot;  
+    catalog[0].measure[Nmeas].dbFlags  = 0;
+    catalog[0].measure[Nmeas].dt       = stars[N].dt;
+    catalog[0].measure[Nmeas].airmass  = stars[N].airmass;
+
+    catalog[0].measure[Nmeas].photFlags = stars[N].flags;
+    catalog[0].measure[Nmeas].qPSF      = stars[N].psfQual;
+    catalog[0].measure[Nmeas].psfChisq  = stars[N].psfChisq;
+    catalog[0].measure[Nmeas].crNsigma  = stars[N].crNsigma;
+    catalog[0].measure[Nmeas].extNsigma = stars[N].extNsigma;
+    catalog[0].measure[Nmeas].Sky       = stars[N].sky;
+    catalog[0].measure[Nmeas].dSky      = stars[N].dsky;
+
+    catalog[0].measure[Nmeas].stargal   = 0;
+
+    catalog[0].measure[Nmeas].detID     = stars[N].detID;
+    catalog[0].measure[Nmeas].imageID   = options.imageID;
+
+    catalog[0].measure[Nmeas].dXccd     = stars[N].dX;
+    catalog[0].measure[Nmeas].dYccd     = stars[N].dY;
+
+    catalog[0].measure[Nmeas].Map       = stars[N].Map;
+
+    // XXX saturate range for FWx, FWy, theta
+    // XXX convert to unsigned int for these...
+    catalog[0].measure[Nmeas].FWx      = 100*stars[N].fx;
+    catalog[0].measure[Nmeas].FWy      = 100*stars[N].fy;
+    catalog[0].measure[Nmeas].theta    = (0xffff/360.0)*stars[N].df;
+	
+    /* set the average magnitude if not already set and the photcode.equiv is not 0 */
+    /* in UPDATE mode, this value is not saved; use relphot to recalculate */
+    if (Nsec > -1) { 
+      if (isnan(catalog[0].secfilt[n*Nsecfilt+Nsec].M)) {
+	catalog[0].secfilt[n*Nsecfilt+Nsec].M = PhotCat (&catalog[0].measure[Nmeas]);
+      }
+    }
+
+    /* adds the measurement to the calibration if appropriate color terms are found */
+    if (options.calibrate) {
+      AddToCalibration (&catalog[0].average[n], &catalog[0].secfilt[n*Nsecfilt], catalog[0].measure, &catalog[0].measure[Nmeas], next_meas, N);
+    }
+
+    /* if we choose to flag close encounters, see find_matches.c */
+    /* if we choose to calculate RA,DEC averages, see update_coords.c */
+
+    /* Nm is updated, but not written out in -update mode (for existing entries)
+       Nm is recalculated in build_meas_links if loaded table is not sorted */
+    stars[N].found = Nmeas;
+    catalog[0].found[n] = Nmeas;
+    catalog[0].average[n].Nmeasure ++;
+    Nmeas ++;
+    i++;
+  }
+
+  /** incorporate unmatched image stars, if this star is in field of this catalog **/
+  /* these new entries are all written out in UPDATE mode */ 
+  for (i = 0; (i < Nstars) && !options.only_match; i++) {
+    /* make sure there is space for next entry */
+    if (Nmeas >= NMEAS) {
+      NMEAS = Nmeas + 1000;
+      REALLOCATE (next_meas, int, NMEAS);
+      REALLOCATE (catalog[0].measure, Measure, NMEAS);
+    }
+    if (Nave >= NAVE) {
+      NAVE = Nave + 1000;
+      REALLOCATE (catalog[0].average, Average, NAVE);
+      REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*catalog[0].Nsecfilt);
+    }
+
+    N = N1[i];
+    if (stars[N].found >= 0) continue;
+    if (!IN_REGION (stars[N].R, stars[N].D)) continue;
+
+    catalog[0].average[Nave].R         	   = stars[N].R;
+    catalog[0].average[Nave].D         	   = stars[N].D;
+    catalog[0].average[Nave].Xp        	   = 0;
+    catalog[0].average[Nave].Nmeasure  	   = 1;
+    catalog[0].average[Nave].Nmissing  	   = 0;
+    catalog[0].average[Nave].measureOffset = Nmeas;
+    catalog[0].average[Nave].missingOffset = -1;
+    catalog[0].average[Nave].code          = 0;
+
+    catalog[0].average[Nave].dR        = 0;
+    catalog[0].average[Nave].dD        = 0;
+    catalog[0].average[Nave].uR        = 0;
+    catalog[0].average[Nave].uD        = 0;
+    catalog[0].average[Nave].duR       = 0;
+    catalog[0].average[Nave].duD       = 0;
+    catalog[0].average[Nave].P         = 0;
+    catalog[0].average[Nave].dP        = 0;
+
+    catalog[0].average[Nave].objID     = objID;
+    catalog[0].average[Nave].catID     = catID;
+    objID ++;
+
+    for (j = 0; j < Nsecfilt; j++) {
+      catalog[0].secfilt[Nave*Nsecfilt+j].M  = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].dM = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].Xm = NAN_S_SHORT;
+    }
+
+    catalog[0].measure[Nmeas].Xccd     = stars[N].X;
+    catalog[0].measure[Nmeas].Yccd     = stars[N].Y;
+
+    catalog[0].measure[Nmeas].dR       = 0.0;
+    catalog[0].measure[Nmeas].dD       = 0.0;
+    catalog[0].measure[Nmeas].M        = stars[N].M;
+    catalog[0].measure[Nmeas].dM       = stars[N].dM;
+    catalog[0].measure[Nmeas].Mcal     = stars[N].Mcal;
+    catalog[0].measure[Nmeas].t        = stars[N].t;
+    catalog[0].measure[Nmeas].averef   = Nave;
+    catalog[0].measure[Nmeas].photcode = stars[N].code;  /* photcode */
+    catalog[0].measure[Nmeas].dophot   = stars[N].dophot;  
+    catalog[0].measure[Nmeas].dbFlags  = 0;
+    catalog[0].measure[Nmeas].dt       = stars[N].dt;
+    catalog[0].measure[Nmeas].airmass  = stars[N].airmass;
+
+    catalog[0].measure[Nmeas].photFlags = stars[N].flags;
+    catalog[0].measure[Nmeas].qPSF      = stars[N].psfQual;
+    catalog[0].measure[Nmeas].psfChisq  = stars[N].psfChisq;
+    catalog[0].measure[Nmeas].crNsigma  = stars[N].crNsigma;
+    catalog[0].measure[Nmeas].extNsigma = stars[N].extNsigma;
+    catalog[0].measure[Nmeas].Sky       = stars[N].sky;
+    catalog[0].measure[Nmeas].dSky      = stars[N].dsky;
+
+    catalog[0].measure[Nmeas].stargal   = 0;
+
+    catalog[0].measure[Nmeas].detID     = stars[N].detID;
+    catalog[0].measure[Nmeas].imageID   = options.imageID;
+
+    catalog[0].measure[Nmeas].dXccd     = stars[N].dX;
+    catalog[0].measure[Nmeas].dYccd     = stars[N].dY;
+
+    catalog[0].measure[Nmeas].Map      = stars[N].Map;
+    catalog[0].measure[Nmeas].FWx      = 100*stars[N].fx;
+    catalog[0].measure[Nmeas].FWy      = 100*stars[N].fy;
+    catalog[0].measure[Nmeas].theta    = (0xffff/360.0)*stars[N].df;
+
+    /* set the average magnitude if not already set and the photcode.equiv is not 0 */
+    /* in UPDATE mode, this value is not saved; use relphot to recalculate */
+    if (Nsec > -1) { 
+	catalog[0].secfilt[Nave*Nsecfilt+Nsec].M = PhotCat (&catalog[0].measure[Nmeas]);
+    }
+
+    /* next[Nmeas] should always be -1 in this context (it is always the only
+       measurement for the star) */
+    stars[N].found = Nmeas;
+    next_meas[Nmeas] = -1;
+    Nmeas ++;
+    Nave ++;
+  }
+      
+  REALLOCATE (catalog[0].average, Average, Nave);
+  REALLOCATE (catalog[0].measure, Measure, Nmeas);
+ 
+  if (options.nosort) {
+    catalog[0].sorted = FALSE;
+  } else {
+    catalog[0].sorted = TRUE;
+    catalog[0].measure = sort_measure (catalog[0].average, Nave, catalog[0].measure, Nmeas, next_meas);
+  }
+
+  /* note stars which have been found in this catalog */
+  for (i = 0; i < NstarsIn; i++) {
+    if (stars[i].found > -1) {
+      stars[i].found = -2;
+    } else {
+      stars[i].found = -3;
+    }
+  }
+
+  /* check if the catalog has changed?  if no change, no need to write */
+  catalog[0].objID    = objID; // new max value, save on catalog close
+  catalog[0].Naverage = Nave;
+  catalog[0].Nmeasure = Nmeas;
+  catalog[0].Nsecf_mem = Nave*Nsecfilt;
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: %d %d %d, (%d matches)\n", Nstars, Nave, Nmeas, Nmatch);
+
+  free (catalog[0].found);
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (X2);
+  free (Y2);
+  free (N2);
+  return (Nmatch);
+}
+
+/* 
+   notes:
+   
+   for finding if a catalog star is in an image or an image star is in the catalog:
+   
+   catalogs have boundaries defined by RA and DEC, but they may curve in projection
+   images have boundaries which are lines in pixels coords, but curve in RA and DEC
+   
+   catalog[0].found[Ncat] but stars[Nstar].found
+   
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_matches_refstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_matches_refstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_matches_refstars.c	(revision 22322)
@@ -0,0 +1,397 @@
+# include "addstar.h"
+
+int find_matches_refstars (SkyRegion *region, Stars **stars, int Nstars, Catalog *catalog, AddstarClientOptions options) {
+
+  int i, j, k, n, m, N, J;
+  double RADIUS, RADIUS2;
+  double *X1, *Y1, *X2, *Y2;
+  double dX, dY, dR;
+  int *N1, *N2,  *next, *next_miss, last, last_miss;
+  int Nave, NAVE, Nmeas, NMEAS, Nmiss, NMISS, Nmatch;
+  unsigned int objID, catID;
+  Measure *tmpmeasure;
+  Missing *tmpmissing;
+  Coords tcoords;
+  int Nsecfilt;
+
+  if ((NREFSTAR_GROUP != 1) && (NREFSTAR_GROUP != 3)) {
+      fprintf (stderr, "ERROR: NREFSTAR_GROUP NOT SET!\n");
+      exit (1);
+  }
+
+  /* photcode data -- should not have to modify secfilt / average */
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /** allocate local arrays (stars) **/
+  ALLOCATE (X1, double, Nstars);
+  ALLOCATE (Y1, double, Nstars);
+  ALLOCATE (N1, int,   Nstars);
+
+  /** allocate local arrays (catalog) **/
+  Nave = catalog[0].Naverage;
+  NAVE = Nave + 1000;
+  ALLOCATE (X2, double, NAVE);
+  ALLOCATE (Y2, double, NAVE);
+  ALLOCATE (N2, int, NAVE);
+  ALLOCATE (catalog[0].found, int, NAVE);
+  REALLOCATE (catalog[0].average, Average, NAVE);
+  REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*catalog[0].Nsecfilt);
+  /* for secfilt j and star i, secfilt[i*Nsecfilt+j] */
+
+  Nmatch = 0;
+  Nmeas = catalog[0].Nmeasure;
+  NMEAS = Nmeas + 1000;
+  ALLOCATE (next, int, NMEAS);
+  REALLOCATE (catalog[0].measure, Measure, NMEAS);
+  
+  Nmiss = catalog[0].Nmissing;
+  NMISS = Nmiss + 1000;
+  if ((NMISS < 1) || (NMISS > 1e10)) {
+      fprintf (stderr, "weird value for NMISS: %d\n", NMISS);
+  }
+  ALLOCATE (next_miss, int, NMISS);
+  REALLOCATE (catalog[0].missing, Missing, NMISS);
+  
+  // current max obj ID for this catalog
+  objID = catalog[0].objID;
+  catID = catalog[0].catID;
+
+  /* project onto rectilinear grid with 1 arcsec pixels, sort by X */
+  /* reference for coords is catalog center */
+  tcoords.crval1 = 0.5*(region[0].Rmin + region[0].Rmax);
+  tcoords.crval2 = 0.5*(region[0].Dmin + region[0].Dmax);
+  tcoords.crpix1 = tcoords.crpix2 = 0.0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+  strcpy (tcoords.ctype, "RA---ZEA");
+  tcoords.Npolyterms = 0;
+  
+  for (i = 0; i < Nstars; i++) {
+    RD_to_XY (&X1[i], &Y1[i], stars[i][0].R, stars[i][0].D, &tcoords);
+    N1[i] = i;
+  }
+  if (Nstars > 1) sort_coords_index (X1, Y1, N1, Nstars);
+  
+  for (i = 0; i < Nave; i++) {
+    RD_to_XY (&X2[i], &Y2[i], catalog[0].average[i].R, catalog[0].average[i].D, &tcoords);
+    N2[i] = i;
+    catalog[0].found[N2[i]] = -1;
+  }
+  if (Nave > 1) sort_coords_index (X2, Y2, N2, Nave);
+
+  /* set up pointers for linked list of measurements */
+  for (i = 0; i < Nmeas - 1; i++) {
+    next[i] = i+1;
+  }
+  next[i] = -1;
+  last = i;
+
+  for (i = 0; i < Nmiss - 1; i++) {
+    next_miss[i] = i+1;
+  }
+  next_miss[i] = -1;
+  last_miss = i;
+
+  /* choose a radius for matches */
+  if (options.radius == 0) {
+    RADIUS = 2.0; /* hardwired default for refstars */
+  } else {
+    RADIUS = options.radius; /* provided by config */
+  }
+  RADIUS2 = RADIUS*RADIUS;
+
+  /** find matched stars **/
+  // XXX could use NREFSTAR_GROUP to do this match more quicky
+  for (i = j = 0; (i < Nstars) && (j < Nave); ) {
+    
+    dX = X1[i] - X2[j];
+    if (dX <= -2*RADIUS) {
+      i++;
+      continue;
+    }
+    if (dX >= 2*RADIUS) {
+      j++;
+      continue;
+    }
+
+    /* negative dX: j is too large, positive dX, i is too large */
+    for (J = j; (dX > -2*RADIUS) && (J < Nave); J++) {
+      dX = X1[i] - X2[J];
+      dY = Y1[i] - Y2[J];
+      dR = dX*dX + dY*dY;
+      if (dR > RADIUS2) continue;
+
+      Nmatch ++;
+      n = N2[J];
+      N = N1[i];
+      m = catalog[0].average[n].measureOffset;  
+
+      /** in replace mode, search for entry and replace values M, dM, R, D */
+      if (options.replace && replace_match (&catalog[0].average[n], &catalog[0].measure[m], stars[N])) continue;
+
+      /** insert star in measurement list */
+      /* find last measurement of this star */
+      for (k = 0; k < catalog[0].average[n].Nmeasure - 1; k++) m = next[m];
+      /* set up references */
+      next[Nmeas] = next[m];
+      next[m] = Nmeas;
+      /* last just was moved */
+      if (next[Nmeas] == -1) last = Nmeas;
+	
+      /** add measurements for this star **/
+      /** *** dR,dD now in arcsec *** **/
+      catalog[0].measure[Nmeas].dR       = 3600.0*(catalog[0].average[n].R - stars[N][0].R);
+      catalog[0].measure[Nmeas].dD       = 3600.0*(catalog[0].average[n].D - stars[N][0].D);
+      catalog[0].measure[Nmeas].M        = stars[N][0].M;
+      catalog[0].measure[Nmeas].dM       = stars[N][0].dM;
+      catalog[0].measure[Nmeas].Mcal     = 0;
+      catalog[0].measure[Nmeas].t        = (TIMEREF == 0) ? stars[N][0].t : TIMEREF; /** careful : time_t vs e_time **/
+      catalog[0].measure[Nmeas].averef   = n;
+      catalog[0].measure[Nmeas].photcode = stars[N][0].code;
+      catalog[0].measure[Nmeas].dophot   = 0;
+      catalog[0].measure[Nmeas].dbFlags  = 0;
+      catalog[0].measure[Nmeas].dt       = 0xffff;
+
+      catalog[0].measure[Nmeas].Map      = NAN;
+      catalog[0].measure[Nmeas].airmass  = 0;
+      catalog[0].measure[Nmeas].FWx      = stars[N][0].fx; // XXX make sure these are zero'ed as needed
+      catalog[0].measure[Nmeas].FWy      = stars[N][0].fy; // XXX make sure these are zero'ed as needed
+      catalog[0].measure[Nmeas].theta    = stars[N][0].df; // XXX make sure these are zero'ed as needed
+	
+      catalog[0].measure[Nmeas].photFlags = stars[N][0].flags; // XXX make sure these are zero'ed as needed
+      catalog[0].measure[Nmeas].qPSF      = 0;
+      catalog[0].measure[Nmeas].psfChisq  = 0;
+      catalog[0].measure[Nmeas].crNsigma  = 0;
+      catalog[0].measure[Nmeas].extNsigma = 0;
+      catalog[0].measure[Nmeas].Sky       = 0;
+      catalog[0].measure[Nmeas].dSky      = 0;
+
+      catalog[0].measure[Nmeas].stargal   = 0; // XXX not yet set
+
+      catalog[0].measure[Nmeas].detID     = 0;
+      catalog[0].measure[Nmeas].imageID   = 0;
+
+      catalog[0].measure[Nmeas].dXccd     = 0;
+      catalog[0].measure[Nmeas].dYccd     = 0;
+
+      catalog[0].measure[Nmeas].Xccd     = stars[N][0].X; // XXX make sure these are zero'ed as needed
+      catalog[0].measure[Nmeas].Yccd     = stars[N][0].Y; // XXX make sure these are zero'ed as needed
+
+      if (ACCEPT_MOTION) {
+	catalog[0].average[n].uR         = stars[N][0].uR;
+	catalog[0].average[n].uD         = stars[N][0].uD;
+	catalog[0].average[n].duR        = stars[N][0].duR;
+	catalog[0].average[n].duD        = stars[N][0].duD;
+	catalog[0].average[n].P          = stars[N][0].P;
+	catalog[0].average[n].dP         = stars[N][0].dP;
+      }
+
+      /** don't update average / secfilt values for REF photcodes **/
+
+      /*** handle multiple stars */
+      /* this image star matches more than one catalog star */
+      if (stars[N][0].found > -1) {
+	catalog[0].measure[stars[N][0].found].dbFlags |= ID_MEAS_BLEND_MEAS;
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_MEAS;
+      } 
+      if (stars[N][0].found == -2) { /* this image star matches a catalog star on a neighboring catalog */
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_MEAS_X;
+      } 
+      if (stars[N][0].found == -1) { /* this image star matches only this catalog star */
+	stars[N][0].found = Nmeas;  /* save first match, in case coincidences are found */
+      }
+      /* this catalog star matches more than one image star */
+      if (catalog[0].found[n] > -1) {
+	catalog[0].measure[catalog[0].found[n]].dbFlags |= ID_MEAS_BLEND_OBJ;
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_OBJ;
+      } else {
+	catalog[0].found[n] = Nmeas;
+      }
+
+      catalog[0].average[n].Nmeasure ++;
+      Nmeas ++;
+      if (Nmeas == NMEAS) {
+	NMEAS = Nmeas + 1000;
+	REALLOCATE (next, int, NMEAS);
+	REALLOCATE (catalog[0].measure, Measure, NMEAS);
+      }
+
+      update_coords (&catalog[0].average[n], &catalog[0].measure[0], next);
+    }
+    i++;
+  }
+
+  /* we don't add missed entries for refcat
+     (already in database, not refcat) */
+
+  /* incorporate unmatched refcat stars */
+  /* skip if we want to require matches
+     combined with -replace, this lets us keep 
+     the reference up-to-date with known stars only */
+
+  for (i = 0; (i < Nstars) && !options.only_match; i+=NREFSTAR_GROUP) {
+    N = N1[i];
+    if (stars[N][0].found >= 0) continue;
+
+    catalog[0].average[Nave].R         	   = stars[N][0].R;
+    catalog[0].average[Nave].D         	   = stars[N][0].D;
+    catalog[0].average[Nave].Xp        	   = 0;
+    catalog[0].average[Nave].Nmeasure      = NREFSTAR_GROUP;
+    catalog[0].average[Nave].Nmissing      = 0;
+    catalog[0].average[Nave].measureOffset = Nmeas;
+    catalog[0].average[Nave].missingOffset = -1;
+    catalog[0].average[Nave].code          = 0;
+
+    if (ACCEPT_MOTION) {
+      catalog[0].average[Nave].dR    = stars[N][0].dR;
+      catalog[0].average[Nave].dD    = stars[N][0].dD;
+      catalog[0].average[Nave].uR    = stars[N][0].uR;
+      catalog[0].average[Nave].uD    = stars[N][0].uD;
+      catalog[0].average[Nave].duR   = stars[N][0].duR;
+      catalog[0].average[Nave].duD   = stars[N][0].duD;
+      catalog[0].average[Nave].P     = stars[N][0].P;
+      catalog[0].average[Nave].dP    = stars[N][0].dP;
+    } else {
+      catalog[0].average[Nave].dR    = 0;
+      catalog[0].average[Nave].dD    = 0;
+      catalog[0].average[Nave].uR    = 0;
+      catalog[0].average[Nave].uD    = 0;
+      catalog[0].average[Nave].duR   = 0;
+      catalog[0].average[Nave].duD   = 0;
+      catalog[0].average[Nave].P     = 0;
+      catalog[0].average[Nave].dP    = 0;
+    }
+
+    catalog[0].average[Nave].objID     = objID;
+    catalog[0].average[Nave].catID     = catID;
+    objID ++;
+
+    for (j = 0; j < Nsecfilt; j++) {
+      catalog[0].secfilt[Nave*Nsecfilt+j].M  = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].dM = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].Xm = NAN_S_SHORT;
+    }
+
+    for (j = 0; j < NREFSTAR_GROUP; j++) {
+      N = N1[i + j];
+      catalog[0].measure[Nmeas].dR       = 0.0;
+      catalog[0].measure[Nmeas].dD       = 0.0;
+      catalog[0].measure[Nmeas].M        = stars[N][0].M;
+      catalog[0].measure[Nmeas].dM       = stars[N][0].dM;
+      catalog[0].measure[Nmeas].Mcal     = 0;
+      catalog[0].measure[Nmeas].t        = (stars[N][0].t == 0) ? TIMEREF : stars[N][0].t; /** careful : time_t vs e_time **/
+      catalog[0].measure[Nmeas].averef   = Nave;
+      catalog[0].measure[Nmeas].photcode = stars[N][0].code;
+      catalog[0].measure[Nmeas].dophot   = 0;
+      catalog[0].measure[Nmeas].dbFlags  = 0;
+      catalog[0].measure[Nmeas].dt       = 0xffff;
+
+      catalog[0].measure[Nmeas].photFlags = stars[N][0].flags; // XXX make sure these are zero'ed as needed
+      catalog[0].measure[Nmeas].qPSF      = 0;
+      catalog[0].measure[Nmeas].psfChisq  = 0;
+      catalog[0].measure[Nmeas].crNsigma  = 0;
+      catalog[0].measure[Nmeas].extNsigma = 0;
+      catalog[0].measure[Nmeas].Sky       = 0;
+      catalog[0].measure[Nmeas].dSky      = 0;
+
+      catalog[0].measure[Nmeas].stargal   = 0; // XXX not yet set
+
+      catalog[0].measure[Nmeas].detID     = 0;
+      catalog[0].measure[Nmeas].imageID   = 0;
+
+      catalog[0].measure[Nmeas].dXccd     = 0;
+      catalog[0].measure[Nmeas].dYccd     = 0;
+
+      catalog[0].measure[Nmeas].Xccd     = stars[N][0].X; // XXX make sure these are zero'ed as needed
+      catalog[0].measure[Nmeas].Yccd     = stars[N][0].Y; // XXX make sure these are zero'ed as needed
+
+      catalog[0].measure[Nmeas].airmass  = 0;
+      catalog[0].measure[Nmeas].Map      = NAN;
+      catalog[0].measure[Nmeas].FWx      = stars[N][0].fx; // XXX make sure these are zero'ed as needed
+      catalog[0].measure[Nmeas].FWy      = stars[N][0].fy; // XXX make sure these are zero'ed as needed
+      catalog[0].measure[Nmeas].theta    = stars[N][0].df; // XXX make sure these are zero'ed as needed
+
+      catalog[0].measure[Nmeas].Xccd     = 0.0;
+      catalog[0].measure[Nmeas].Yccd     = 0.0;
+
+      stars[N][0].found = Nmeas;
+      next[last] = Nmeas;
+      next[Nmeas] = -1;
+      last = Nmeas;
+      Nmeas ++;
+      if (Nmeas == NMEAS) {
+	NMEAS = Nmeas + 1000;
+	REALLOCATE (next, int, NMEAS);
+	REALLOCATE (catalog[0].measure, Measure, NMEAS);
+      }
+    }
+
+    Nave ++;
+    if (Nave == NAVE) {
+      NAVE = Nave + 1000;
+      REALLOCATE (catalog[0].average, Average, NAVE);
+      REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*catalog[0].Nsecfilt);
+    }
+  }
+      
+  free (catalog[0].found);
+  REALLOCATE (catalog[0].average, Average, Nave);
+
+  /* fix order of Measure (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmeasure, Measure, Nmeas);
+  for (i = 0; i < Nave; i++) {
+    n = catalog[0].average[i].measureOffset;
+    catalog[0].average[i].measureOffset = N;
+    for (k = 0; k < catalog[0].average[i].Nmeasure; k++, N++) {
+      tmpmeasure[N] = catalog[0].measure[n]; 
+      tmpmeasure[N].averef = i;
+      n = next[n];
+    }
+  }
+  free (catalog[0].measure);
+  catalog[0].measure = tmpmeasure;
+    
+  /* fix order of Missing (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmissing, Missing, Nmiss);
+  for (i = 0; i < Nave; i++) {
+    if (catalog[0].average[i].Nmissing > 0) {
+      n = catalog[0].average[i].missingOffset;
+      catalog[0].average[i].missingOffset = N;
+      for (k = 0; k < catalog[0].average[i].Nmissing; k++, N++) {
+	tmpmissing[N] = catalog[0].missing[n]; 
+	n = next_miss[n];
+      }
+    }
+  }
+  free (catalog[0].missing);
+  catalog[0].missing = tmpmissing;
+
+  /* note stars which have been found in this catalog */
+  for (i = 0; i < Nstars; i++) {
+    if (stars[i][0].found > -1) {
+      stars[i][0].found = -2;
+    } else {
+      stars[i][0].found = -3;
+    }
+  }
+
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (X2);
+  free (Y2);
+  free (N2);
+  free (next);
+  free (next_miss);
+
+  catalog[0].objID    = objID; // new max value, save on catalog close
+  catalog[0].Naverage = Nave;
+  catalog[0].Nmeasure = Nmeas;
+  catalog[0].Nmissing = Nmiss;
+  catalog[0].Nsecf_mem = Nave*Nsecfilt;
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas, Nmiss: %d %d %d %d, (%d matches)\n", Nstars, Nave, Nmeas, Nmiss, Nmatch);
+  return (Nmatch);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_missing.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_missing.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_missing.c	(revision 22322)
@@ -0,0 +1,52 @@
+
+/* XXX I have dropped the -missed capability of addstar. This is an expensive operation which
+ * is only rarely needed.  It is more efficient to perform this operation as a crawler like
+ * relphot.  I am saving in this file the code which was used in find_matches to perform the
+ * missing matches.
+ */
+
+/** code to add references from all previous non-detection observations of this spot on the sky */
+for (j = 0; (j < Noverlap) && !options.skip_missed; j++) {
+  /* make sure there is space for next entry */
+  if (Nmiss >= NMISS) {
+    NMISS = Nmiss + 1000;
+    REALLOCATE (next_miss, int, NMISS);
+    REALLOCATE (catalog[0].missing, Missing, NMISS);
+  }
+  if (!FindMosaicForImage (overlap, Noverlap, j)) continue;
+  if (!in_image (catalog[0].average[Nave].R, catalog[0].average[Nave].D, &overlap[j])) continue;
+  add_miss_link (&catalog[0].average[Nave], next_miss, Nmiss);
+
+  /* get time of exposure of this portion of the image */
+  RD_to_XY (&X, &Y, catalog[0].average[Nave].R, catalog[0].average[Nave].D, &overlap[j].coords);	  
+  catalog[0].missing[Nmiss].t  = overlap[j].tzero + 1e-4*Y*overlap[j].trate;  /* rough guess at time */
+  catalog[0].average[Nave].Nn ++;
+  Nmiss ++;
+}
+
+/* add reference for undetected catalog stars */
+/* XXX allow this option only for single images? */
+if (!strcmp (&image[0].coords.ctype[4], "-WRP")) RegisterMosaic (mosaic);
+for (j = 0; (j < Nave) && !options.skip_missed; j++) {
+  n = N2[j];
+  if (catalog[0].found[n] < 0) { 
+    /* make sure there is space for next entry */
+    if (Nmiss >= NMISS) {
+      NMISS = Nmiss + 1000;
+      REALLOCATE (next_miss, int, NMISS);
+      REALLOCATE (catalog[0].missing, Missing, NMISS);
+    }
+
+    /* should the catalog star be on this image? project into image coords */
+    if (!in_image (catalog[0].average[n].R, catalog[0].average[n].D, image)) continue;
+    add_miss_link (&catalog[0].average[n], next_miss, Nmiss);
+
+    /* calculate time of exposure for this coordinate in the image */
+    RD_to_XY (&X, &Y, catalog[0].average[n].R, catalog[0].average[n].D, &image[0].coords);	  
+    catalog[0].missing[Nmiss].t  = image[0].tzero + 1e-4*Y*image[0].trate;  /* trate is in 0.1 msec / row */
+    catalog[0].average[n].Nn ++;
+    Nmiss ++;
+  }
+}
+  catalog[0].missing = sort_missing (catalog[0].average, Nave, catalog[0].missing, Nmiss, next_miss);
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_proper.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_proper.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_proper.c	(revision 22322)
@@ -0,0 +1,158 @@
+# include "addusno.h"
+
+find_proper (catstats, catalog, usnostats, usno, Nusno)
+CatStats catstats[];
+Catalog catalog[];
+USNOstats usnostats[];
+USNOdata usno[];
+int Nusno;
+{
+
+  int i, j, k, n, m, N, first_j;
+  double RADIUS2, PROPER2;
+  float *X1, *Y1, *X2, *Y2, *usnodist;
+  float dX, dY, dR, dR2;
+  int *N1, *N2,  *next, last;
+  int Nave, Nmeas, NMEAS, Nmatch;
+  unsigned int flags;
+  Measure *tmpmeasure;
+  Coords *tcoords;
+  int already_matched, far_enough;
+
+  X1 = catstats[0].X;
+  Y1 = catstats[0].Y;
+  N1 = catstats[0].N;
+  Nave = catalog[0].Naverage;
+
+  /* no need to do this twice!! */
+  ALLOCATE (usnodist, float, Nave);
+  memset (usnodist, 0, Nave*sizeof(float));
+
+  X2 = usnostats[0].X;
+  Y2 = usnostats[0].Y;
+  N2 = usnostats[0].N;
+
+ /* set up link listed pointers for new measurements */
+  Nmatch = 0;
+  Nmeas = catalog[0].Nmeasure;
+  NMEAS = Nmeas + 1000;
+  ALLOCATE (next, int, NMEAS);
+  REALLOCATE (catalog[0].measure, Measure, NMEAS);
+  /* set up pointers for linked list of measurements */
+  for (i = 0; i < Nmeas - 1; i++) {
+    next[i] = i+1;
+  }
+  next[i] = -1;
+  last = i;
+  
+  /* choose a radius for matches */
+  PROPER2 = PROPER*PROPER;
+  RADIUS2 = RADIUS*RADIUS;
+
+  /** find matched stars **/
+  for (i = j = 0; (i < Nave) && (j < Nusno); ) {
+    if (catalog[0].average[N1[i]].code & ID_MOVING) { 
+      /* this is not a star, skip */
+      i++;
+      continue;
+    }
+    if (catalog[0].average[N1[i]].Nm < 3) { 
+      /* may just be a noise spike, skip */
+      i++;
+      continue;
+    }
+    if (catalog[0].average[N1[i]].code & ID_USNO) {
+      /* already matched with USNO, skip this one */
+      i++;
+      continue;
+    }
+    dX = X1[i] - X2[j];
+    if (dX <= -2*PROPER) {
+      i++;
+      continue;
+    }
+    if (dX >= 2*PROPER) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    for (; (dX > -2*PROPER) && (j < Nusno); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      dR = dX*dX + dY*dY;
+      if (dR < PROPER2) {  
+	n = N1[i];  /* N1 refers to the average[] list */
+	N = N2[j];  /* N2 refers to the usno[] list */
+	if (usnostats[0].match[N] > -1) 
+	  continue;
+	if ((catalog[0].average[n].code & ID_USNO) && (usnodist[i] < dR)) {
+	  /* existing USNO match is closer than this new one, skip this one */
+	  continue;
+	}
+	usnodist[i] = dR;
+	m = catalog[0].average[n].offset;  /* first measurement of this star */
+	for (k = 0; k < catalog[0].average[n].Nm - 1; k++)
+	  m = next[m];
+	next[Nmeas+1] = next[m]; /* insert 2 measurements in linked list */
+	next[Nmeas] = Nmeas + 1;
+	next[m] = Nmeas;
+	if (next[Nmeas+1] == -1) { /* last just was moved */
+	  last = Nmeas+1;
+	}
+	Nmatch ++;
+	
+	/** add measurements for this star **/
+	catalog[0].measure[Nmeas].dR  = 360000.0*(catalog[0].average[n].R - usno[N].R);
+	catalog[0].measure[Nmeas].dD  = 360000.0*(catalog[0].average[n].D - usno[N].D);
+	catalog[0].measure[Nmeas].M   = 1000.0*fabs(usno[N].r);
+	catalog[0].measure[Nmeas].Mcal= 0;    /* above measurement is exact */
+	catalog[0].measure[Nmeas].dM  = 100;  /* error in input files stored in thousandths of mag */
+	catalog[0].measure[Nmeas].t   = 0;    /* a flag: if 0, image is not in database */
+	catalog[0].measure[Nmeas].averef  = n;
+	catalog[0].measure[Nmeas].photcode = USNO_RED; 
+	catalog[0].measure[Nmeas+1].dR  = catalog[0].measure[Nmeas].dR;
+	catalog[0].measure[Nmeas+1].dD  = catalog[0].measure[Nmeas].dD;
+	catalog[0].measure[Nmeas+1].M   = 1000.0*fabs(usno[N].b);
+	catalog[0].measure[Nmeas+1].Mcal= 0;    /* above measurement is exact */
+	catalog[0].measure[Nmeas+1].dM  = 100;  /* error in input files stored in thousandths of mag */
+	catalog[0].measure[Nmeas+1].t   = 0;    /* a flag: if 0, image is not in database */
+	catalog[0].measure[Nmeas+1].averef  = n;
+	catalog[0].measure[Nmeas+1].photcode = USNO_BLUE; 
+	/* add flag in average to mark as matched with the USNO catalog */
+	catalog[0].average[n].code |= (ID_PROPER | ID_USNO);
+	
+	/* we add two measurement for each star: 1 in r, 1 in b */
+	catalog[0].average[n].Nm +=2;
+	Nmeas +=2;  
+	if (Nmeas == NMEAS - 2) {  /* just to be safe... */
+	  NMEAS = Nmeas + 1000;
+	  REALLOCATE (next, int, NMEAS);
+	  REALLOCATE (catalog[0].measure, Measure, NMEAS);
+	}
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  
+  REALLOCATE (catalog[0].measure, Measure, Nmeas);
+
+  /* fix order of Measure (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmeasure, Measure, Nmeas);
+  for (i = 0; i < Nave; i++) {
+    n = catalog[0].average[i].offset;
+    catalog[0].average[i].offset = N;
+    for (k = 0; k < catalog[0].average[i].Nm; k++, N++) {
+      tmpmeasure[N] = catalog[0].measure[n]; 
+      n = next[n];
+    }
+  }
+  free (catalog[0].measure);
+  catalog[0].measure = tmpmeasure;
+    
+  catalog[0].Nmeasure = Nmeas;
+  if (VERBOSE) fprintf (stderr, "Nusno, Nave, Nmeas: %d %d %d, (%d matches)\n", Nusno, Nave, Nmeas, Nmatch);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_subset.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_subset.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/find_subset.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "addstar.h"
+
+/* find stars within this region */
+Stars **find_subset (SkyRegion *region, Stars *stars, int Nstars, int *Nsubset) {
+
+  int i, N, NSUBSET;
+  Stars **subset;
+  double RA0, RA1, DEC0, DEC1;
+
+  NSUBSET = 1000;
+  ALLOCATE (subset, Stars *, NSUBSET);
+
+  RA0  = region[0].Rmin;
+  RA1  = region[0].Rmax;
+  DEC0 = region[0].Dmin;
+  DEC1 = region[0].Dmax;
+
+  if (VERBOSE) fprintf (stderr, "subset for %f - %f, %f - %f\n", RA0, RA1, DEC0, DEC1);
+
+  /* find stars within ra,dec region */
+  for (i = N = 0; i < Nstars; i++) {
+    if (stars[i].R <  RA0)  continue;
+    if (stars[i].R >= RA1)  continue;
+    if (stars[i].D <  DEC0) continue;
+    if (stars[i].D >= DEC1) continue;
+
+    subset[N] = &stars[i];
+    N++;
+    if (N == NSUBSET - 1) {
+      NSUBSET += 1000;
+      REALLOCATE (subset, Stars *, NSUBSET);
+    }
+  }
+  *Nsubset = N;
+  return (subset);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass.c	(revision 22322)
@@ -0,0 +1,124 @@
+# include "addstar.h"
+# include "2mass.h"
+
+Stars *get2mass (SkyRegion *patch, int photcode, int mode, int *NSTARS) {
+  
+  char *path;
+  char gzname[1024];
+  int i, status, Nstars, Nrefcat; 
+  Stars    *stars;
+  Stars    *refcat;
+  SkyTable *sky;
+  struct stat filestat;
+
+  fprintf (stderr, "please use the load2mass program instead\n");
+  exit (1);
+
+  NAMED_PHOTCODE (TM_J, "2MASS_J");
+  NAMED_PHOTCODE (TM_H, "2MASS_H");
+  NAMED_PHOTCODE (TM_K, "2MASS_K");
+  if (photcode == TM_J) goto good_code;
+  if (photcode == TM_H) goto good_code;
+  if (photcode == TM_K) goto good_code;
+  Shutdown ("2MASS photcode not specified");
+
+good_code:
+  path = TWO_MASS_DIR_AS;
+  if (mode == 1) path = TWO_MASS_DIR_DR2;
+
+  // the accel.dat file has the raw filenames
+  // test if the file exists, or else try the .gz version
+  sky = get2mass_acc (patch, path, "accel.dat");
+  
+  Nstars = 0;
+  ALLOCATE (stars, Stars, 1);
+
+  for (i = 0; i < sky[0].Nregions; i++) {
+    refcat = NULL;
+    switch (mode) {
+      case 0:
+	// XXX put filename from table here
+	status = stat (sky[0].filename[i], &filestat);
+	if ((status == -1) && (errno == ENOENT)) {
+	  sprintf (gzname, "%s.gz", sky[0].filename[i]);
+	  refcat = get2mass_AS_data (&sky[0].regions[i], gzname, patch, photcode, &Nrefcat);
+	} else {
+	  refcat = get2mass_AS_rawdata (&sky[0].regions[i], sky[0].filename[i], patch, photcode, &Nrefcat);
+	}
+	if (VERBOSE) fprintf (stderr, "loaded %d stars from 2MASS (allsky) : %s\n", Nrefcat, sky[0].filename[i]);
+	break;
+      case 1:
+	refcat = get2mass_2DR_data (&sky[0].regions[i], sky[0].filename[i], patch, photcode, &Nrefcat);
+	if (VERBOSE) fprintf (stderr, "loaded %d stars from 2MASS (dr2)\n", Nrefcat);
+	break;
+    }
+
+    REALLOCATE (stars, Stars, MAX (1, Nstars + Nrefcat));
+    memcpy (&stars[Nstars], refcat, Nrefcat*sizeof(Stars));
+    Nstars += Nrefcat;
+
+    free (refcat);
+  }
+  
+  if (VERBOSE) fprintf (stderr, "loaded total %d stars from 2MASS\n", Nstars);
+  *NSTARS = Nstars;
+  return (stars);
+}  
+
+/* watch for patches which cross 0,360 boundary */
+SkyTable *get2mass_acc (SkyRegion *patch, char *path, char *accel) {
+
+  int Nregions, NREGIONS, Nrec;
+  char accelfile[1024], line[256], filename[128], datafile[256], **filenames;
+  FILE *f;
+  double Rs, Re, Ds, De;
+
+  SkyTable *sky;
+  SkyRegion *regions;
+
+  sprintf (accelfile, "%s/%s", path, accel);
+  f = fopen (accelfile, "r");
+  if (f == NULL) Shutdown ("can't read data from accelerator %s", accelfile);
+
+  Nregions = 0;
+  NREGIONS = 200;
+  ALLOCATE (regions, SkyRegion, NREGIONS);
+  ALLOCATE (filenames, char *, NREGIONS);
+
+  /* read in stars line-by-line */
+  while (scan_line (f, line) != EOF) {
+    stripwhite (line);
+    if (line[0] == 0) continue;
+    if (line[0] == '#') continue;
+    sscanf (line, "%s %lf %lf %lf %lf %d", filename, &Rs, &Re, &Ds, &De, &Nrec);
+    Rs *= 15.0;
+    Re *= 15.0;
+    if (Rs > patch[0].Rmax) continue;
+    if (Re < patch[0].Rmin) continue;
+    if (Ds > patch[0].Dmax) continue;
+    if (De < patch[0].Dmin) continue;
+    regions[Nregions].Rmin = Rs;
+    regions[Nregions].Rmax = Re;
+    regions[Nregions].Dmin = Ds;
+    regions[Nregions].Dmax = De;
+    regions[Nregions].childE = Nrec; // a cheat since 2MASS only has one depth
+    fprintf (stderr, "choosing: %10.6f - %10.6f, %10.6f - %10.6f\n", Rs, Re, Ds, De);
+
+    sprintf (datafile, "%s/%s", path, filename);
+    filenames[Nregions] = strcreate (datafile);
+
+    Nregions ++;
+    if (Nregions >= NREGIONS) {
+	NREGIONS += 20;
+	REALLOCATE (regions, SkyRegion, NREGIONS);
+	REALLOCATE (filenames, char *, NREGIONS);
+    }
+  }    
+  fclose (f);
+
+  ALLOCATE (sky, SkyTable, 1);
+  sky[0].regions = regions;
+  sky[0].filename = filenames;
+  sky[0].Nregions = Nregions;
+  return (sky);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_as.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_as.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_as.c	(revision 22322)
@@ -0,0 +1,128 @@
+# include "addstar.h"
+# include "2mass.h"
+
+/* unlike the DR2 data, the AS data is NOT fixed bytes/row 
+ * we need to handle fractional lines at the end of each read block
+ */
+
+/* read in chunks of ~16MB */
+# define NBYTE 0x1000000
+
+Stars *get2mass_AS_data (SkyRegion *region, char *filename, SkyRegion *patch, int photcode, int *nstars) {
+  
+  int Nstars, NSTARS, Nbyte, Nextra;
+  Stars *stars;
+  gzFile gf;
+  char *buffer;
+  char *p, *q, *tmp;
+  double RA, DEC;
+  double RA0, RA1, DEC0, DEC1;
+
+  ALLOCATE (buffer, char, NBYTE);
+
+  RA0  = MAX (patch[0].Rmin, UserPatch.Rmin);
+  RA1  = MIN (patch[0].Rmax, UserPatch.Rmax);
+  DEC0 = MAX (patch[0].Dmin, UserPatch.Dmin);
+  DEC1 = MIN (patch[0].Dmax, UserPatch.Dmax);
+
+  get2mass_setup (photcode);
+
+  gf = gzopen (filename, "rb");
+  if (gf == NULL) Shutdown ("can't read 2mass data file: %s", filename);
+
+  Nstars = 0;
+  NSTARS = 10000;
+  ALLOCATE (stars, Stars, NSTARS);
+
+  /* I want to add a seek-ahead test to find a good starting position in the file
+     this is very expensive using gzseek / gzread.  */
+
+  Nextra = 0;
+  while ((Nbyte = gzread (gf, &buffer[Nextra], NBYTE-Nextra)) != 0) {
+    if (Nbyte == -1) Shutdown ("error reading from gzipped file %s", filename);
+    Nbyte += Nextra;
+
+    if (VERBOSE) fprintf (stderr, ".");
+
+    /* find bounds on first complete line */
+    p = buffer;
+    q = memchr (p, '\n', Nbyte);
+    if (q == NULL) Shutdown ("incomplete line at end of file\n");
+
+    while (1) {
+
+      get2mass_coords (p, &RA, &DEC, Nbyte - (p - buffer));
+
+      /* skip stars which are outside desired region */
+      if (DEC > DEC1) goto skip_star;
+      if (DEC < DEC0) goto skip_star;
+      if (RA <  RA0)  goto skip_star;
+      if (RA >  RA1)  goto skip_star;
+
+      get2mass_star (&stars[Nstars], p, Nbyte - (p - buffer));
+
+      Nstars ++;
+      CHECK_REALLOCATE (stars, Stars, NSTARS, Nstars, 5000);
+
+    skip_star:
+      /* start of the next line */
+      tmp = p;
+      p = q + 1;
+      if (p - buffer == Nbyte) {
+	Nextra = 0;
+	break;
+      }
+      /* end of the next line */
+      q = memchr (p, '\n', Nbyte - (p - buffer));
+      if (q == NULL) {
+	Nextra = Nbyte - (p - buffer);
+	memmove (buffer, p, Nextra);
+	break;
+      } 
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "\n");
+  
+  gzclose (gf);
+  free (buffer);
+  *nstars = Nstars;
+  return (stars);
+}
+
+/* this just scans along in the file.  file is sorted by dec, so we 
+   should be skipping large chunks - but we would need to have
+   the size from the accel file (won't fit in SkyRegion) and need
+   to use gzseek, if it exists.
+*/
+/* don't bother to seek ahead : position is not sufficiently predictable 
+   and gzseek is as expensive as gzread */
+/* 
+   Noffset = region[0].Nrec * (patch[0].DEC[0] + 90) / 180.0;
+   gzseek (gf, Noffset * NBYTE, SEEK_SET);
+   Nbyte = gzread (gf, buffer, NLINE*NBYTE);
+*/
+
+# if (0)
+    /** need to re-think this test **/
+    if (0) {
+      /* search for end of last complete line */
+      p = memrchr (buffer, '\n', Nbyte);
+      if (p == NULL) Shutdown ("incomplete line in at end of file\n");
+
+      /* search for start of last complete line */
+      /* last block may be only one line */
+      q = memrchr (buffer, '\n', (p - buffer - 1));
+      if (q == NULL) {
+	q = buffer;
+      } else {
+	q ++;
+      }
+
+      /* skip past block not yet in range */
+      RA = strtod (q, NULL);
+      ptr = skipNbounds (q, '|', 1, Nbyte - (q - buffer));
+      DEC = strtod (ptr, NULL);
+      if (DEC < DEC0) continue;
+    }
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_as_raw.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_as_raw.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_as_raw.c	(revision 22322)
@@ -0,0 +1,89 @@
+# include "addstar.h"
+# include "2mass.h"
+
+/* unlike the DR2 data, the AS data is NOT fixed bytes/row 
+ * we need to handle fractional lines at the end of each read block
+ */
+
+/* read in chunks of ~16MB */
+# define NBYTE 0x1000000
+# define NBREC 330
+
+Stars *get2mass_AS_rawdata (SkyRegion *region, char *filename, SkyRegion *patch, int photcode, int *nstars) {
+  
+  int Nstars, NSTARS, Nbyte, Nextra;
+  Stars *stars;
+  FILE *f;
+  char *buffer;
+  char *p, *q, *tmp;
+  double RA, DEC;
+  double RA0, RA1, DEC0, DEC1;
+
+  ALLOCATE (buffer, char, NBYTE);
+
+  RA0  = MAX (patch[0].Rmin, UserPatch.Rmin);
+  RA1  = MIN (patch[0].Rmax, UserPatch.Rmax);
+  DEC0 = MAX (patch[0].Dmin, UserPatch.Dmin);
+  DEC1 = MIN (patch[0].Dmax, UserPatch.Dmax);
+
+  get2mass_setup (photcode);
+
+  f = fopen (filename, "r");
+  if (f == NULL) Shutdown ("can't read 2mass data file: %s", filename);
+  // test if this is a raw datafile or gzipped...
+
+  Nstars = 0;
+  NSTARS = 10000;
+  ALLOCATE (stars, Stars, NSTARS);
+
+  Nextra = 0;
+  while ((Nbyte = fread (&buffer[Nextra], 1, NBYTE-Nextra, f)) != 0) {
+    if (Nbyte == -1) Shutdown ("error reading from raw file %s", filename);
+    Nbyte += Nextra;
+
+    if (VERBOSE) fprintf (stderr, ".");
+
+    /* find bounds on first complete line */
+    p = buffer;
+    q = memchr (p, '\n', Nbyte);
+    if (q == NULL) Shutdown ("incomplete line at end of file\n");
+
+    while (1) {
+
+      get2mass_coords (p, &RA, &DEC, Nbyte - (p - buffer));
+
+      /* skip stars which are outside desired region */
+      if (DEC > DEC1) goto skip_star;
+      if (DEC < DEC0) goto skip_star;
+      if (RA <  RA0)  goto skip_star;
+      if (RA >  RA1)  goto skip_star;
+
+      get2mass_star (&stars[Nstars], p, Nbyte - (p - buffer));
+
+      Nstars ++;
+      CHECK_REALLOCATE (stars, Stars, NSTARS, Nstars, 5000);
+
+    skip_star:
+      /* start of the next line */
+      tmp = p;
+      p = q + 1;
+      if (p - buffer == Nbyte) {
+	Nextra = 0;
+	break;
+      }
+      /* end of the next line */
+      q = memchr (p, '\n', Nbyte - (p - buffer));
+      if (q == NULL) {
+	Nextra = Nbyte - (p - buffer);
+	memmove (buffer, p, Nextra);
+	break;
+      } 
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "\n");
+  
+  fclose (f);
+  free (buffer);
+  *nstars = Nstars;
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_dr2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_dr2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_dr2.c	(revision 22322)
@@ -0,0 +1,111 @@
+# include "addstar.h"
+# include "2mass.h"
+
+# define NBYTE 302
+# define NLINE 30000
+
+Stars *get2mass_2DR_data (SkyRegion *region, char *filename, SkyRegion *patch, int photcode, int *nstars) {
+  
+  int i, Nstars, NSTARS, Nbyte, Nline;
+  Stars *stars;
+  gzFile gf;
+  char *buffer;
+  char line[303];
+  double RA, DEC, J, H, K, dJ, dH, dK;
+  double RA0, RA1, DEC0, DEC1;
+
+  ALLOCATE (buffer, char, (NBYTE*NLINE));
+
+  if (region == NULL) exit (2);
+  if (patch == NULL) exit (3);
+
+  RA0  = MAX (patch[0].Rmin, UserPatch.Rmin);
+  RA1  = MIN (patch[0].Rmax, UserPatch.Rmax);
+  DEC0 = MAX (patch[0].Dmin, UserPatch.Dmin);
+  DEC1 = MIN (patch[0].Dmax, UserPatch.Dmax);
+
+  fprintf (stderr, "overlap: %f - %f, %f - %f\n", RA0, RA1, DEC0, DEC1);
+
+  gf = gzopen (filename, "rb");
+  if (gf == NULL) Shutdown ("can't read 2mass data file: %s", filename);
+
+  Nstars = 0;
+  NSTARS = 10000;
+  ALLOCATE (stars, Stars, NSTARS);
+
+  while ((Nbyte = gzread (gf, buffer, NLINE*NBYTE)) != 0) {
+    if (Nbyte ==  0) Shutdown ("error reading from gzipped file %s", filename);
+    if (Nbyte == -1) Shutdown ("error reading from gzipped file %s", filename);
+    if (Nbyte % NBYTE) Shutdown ("error reading complete line from gzipped file %s", filename);
+    Nline = Nbyte / NBYTE;
+
+    /* skip past block not yet in range */
+    sscanf (&buffer[NBYTE*(Nline - 1)], "%lf %lf", &RA, &DEC);
+    if (DEC < DEC0) continue;
+
+    memcpy (line, &buffer[NBYTE*(Nline-1)], NBYTE);
+    line[302] = 0;
+
+    for (i = 0; i < Nline; i++) {
+      
+      dparse (&RA,  1, &buffer[NBYTE*i+  0]);
+      dparse (&DEC, 2, &buffer[NBYTE*i+  0]);
+
+      /* dr2 is nicely sorted in dec order */
+      if (DEC > DEC1) goto finished;
+      if (DEC < DEC0) continue;
+      if (RA <  RA0) continue;
+      if (RA >  RA1) continue;
+
+      stars[Nstars].R  	  = RA;
+      stars[Nstars].D  	  = DEC;
+      stars[Nstars].t  	  = short_date_to_sec (&buffer[NBYTE*i + 164]);
+      stars[Nstars].found = -1;
+      stars[Nstars].detID   = 0;
+      stars[Nstars].imageID = 0;
+
+      if (photcode == TM_J) {
+	dparse (&J,  1, &buffer[NBYTE*i + 53]);
+	dparse (&dJ, 2, &buffer[NBYTE*i + 53]);
+	stars[Nstars].M    = J;
+	stars[Nstars].dM   = dJ;
+	stars[Nstars].code = TM_J;
+      }
+      if (photcode == TM_H) {
+	dparse (&H,  1, &buffer[NBYTE*i + 72]);
+	dparse (&dH, 2, &buffer[NBYTE*i + 72]);
+	stars[Nstars].M    = H;
+	stars[Nstars].dM   = dH;
+	stars[Nstars].code = TM_H;
+      }
+      if (photcode == TM_K) {
+	dparse (&K,  1, &buffer[NBYTE*i + 91]);
+	dparse (&dK, 2, &buffer[NBYTE*i + 91]);
+	stars[Nstars].M    = K;
+	stars[Nstars].dM   = dK;
+	stars[Nstars].code = TM_K;
+      }
+      Nstars ++;
+      CHECK_REALLOCATE (stars, Stars, NSTARS, Nstars, 5000);
+    }
+  }  
+finished:
+  gzclose (gf);
+  free (buffer);
+
+  *nstars = Nstars;
+  return (stars);
+}
+
+/* this just scans along in the file.  file is sorted by dec, so we should be
+   skipping large chunks - but we would need to have the size from the accel
+   file in (and need to use gzseek, if the data is compressed)
+*/
+/* don't bother to seek ahead : position is not sufficiently predictable 
+   and gzseek is as expensive as gzread */
+/* 
+   Noffset = region[0].Nrec * (patch[0].Dmin + 90) / 180.0;
+   gzseek (gf, Noffset * NBYTE, SEEK_SET);
+   Nbyte = gzread (gf, buffer, NLINE*NBYTE);
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_full.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_full.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_full.c	(revision 22322)
@@ -0,0 +1,276 @@
+# include "addstar.h"
+# include "2mass.h"
+
+// fill in the data for a JHK triplet star.  takes a pointer to the start of the line the
+// RA and DEC have already been set
+int get2mass_3star_full (Stars **star, char *line, int Nmax) {
+
+  char *ptr;
+  double dMfull;
+  double jd;
+
+  if (line == NULL) Shutdown ("format error in 2mass");
+
+  ptr = line;		      // ra  (assumed to be already set)
+  ptr = next2MASSfield (ptr); // dec (assumed to be already set)
+  ptr = next2MASSfield (ptr); // err_maj
+  star[0][0].fx = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // err_min
+  star[0][0].fy = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // err_ang
+  star[0][0].df = strtod (ptr, NULL);
+
+  star[2][0].fx = star[2][0].fx = star[0][0].fx;
+  star[2][0].fy = star[2][0].fy = star[0][0].fy;
+  star[2][0].df = star[2][0].df = star[0][0].df;
+
+  ptr = next2MASSfield (ptr); // designation (skip)
+
+  ptr = next2MASSfield (ptr); // j_m
+  star[0][0].M  = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // j_cmsig
+  star[0][0].dM = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // j_msigcom
+  dMfull = strtod (ptr, NULL);
+  star[0][0].dMcal = sqrt (SQ(dMfull) - SQ(star[0][0].dM));
+  ptr = next2MASSfield (ptr); // j_snr (skip)
+
+  ptr = next2MASSfield (ptr); // h_m
+  star[1][0].M  = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // h_cmsig
+  star[1][0].dM = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // h_msigcom
+  dMfull = strtod (ptr, NULL);
+  star[1][0].dMcal = sqrt (SQ(dMfull) - SQ(star[1][0].dM));
+  ptr = next2MASSfield (ptr); // h_snr (skip)
+
+  ptr = next2MASSfield (ptr); // k_m
+  star[2][0].M  = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // k_cmsig
+  star[2][0].dM = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // k_msigcom
+  dMfull = strtod (ptr, NULL);
+  star[2][0].dMcal = sqrt (SQ(dMfull) - SQ(star[2][0].dM));
+  ptr = next2MASSfield (ptr); // k_snr (skip)
+
+  star[2][0].flags = star[1][0].flags = star[0][0].flags = 0;
+
+  ptr = next2MASSfield (ptr); // ph_qual
+  set2MASS_ph_qual (star[0], ptr[0]);
+  set2MASS_ph_qual (star[1], ptr[1]);
+  set2MASS_ph_qual (star[2], ptr[2]);
+
+  ptr = next2MASSfield (ptr); // rd_flg
+  set2MASS_rd_flag (star[0], ptr[0]);
+  set2MASS_rd_flag (star[1], ptr[1]);
+  set2MASS_rd_flag (star[2], ptr[2]);
+
+  ptr = next2MASSfield (ptr); // bl_flg
+  set2MASS_bl_flag (star[0], ptr[0]);
+  set2MASS_bl_flag (star[1], ptr[1]);
+  set2MASS_bl_flag (star[2], ptr[2]);
+
+  ptr = next2MASSfield (ptr); // cc_flg
+  set2MASS_cc_flag (star[0], ptr[0]);
+  set2MASS_cc_flag (star[1], ptr[1]);
+  set2MASS_cc_flag (star[2], ptr[2]);
+
+  ptr = next2MASSfield (ptr); // ndet (skip for now, XXX use somehow?)
+  ptr = next2MASSfield (ptr); // prox (skip)
+  ptr = next2MASSfield (ptr); // pxpa (skip)
+  ptr = next2MASSfield (ptr); // pxcntr (skip)
+
+  ptr = next2MASSfield (ptr); // gal_contam (one flag for all filters)
+  set2MASS_gal_flag (star[0], ptr[0]);
+  set2MASS_gal_flag (star[1], ptr[0]);
+  set2MASS_gal_flag (star[2], ptr[0]);
+
+  ptr = next2MASSfield (ptr); // mp_flg (one flag for all filters)
+  set2MASS_mp_flag (star[0], ptr[0]);
+  set2MASS_mp_flag (star[1], ptr[0]);
+  set2MASS_mp_flag (star[2], ptr[0]);
+
+  ptr = next2MASSfield (ptr); // pts_key (skip for now, XXX use somehow?)
+  ptr = next2MASSfield (ptr); // hemis (skip)
+  ptr = next2MASSfield (ptr); // date (skip)
+  ptr = next2MASSfield (ptr); // scan (skip)
+  ptr = next2MASSfield (ptr); // glon (skip)
+  ptr = next2MASSfield (ptr); // glat (skip)
+
+  ptr = next2MASSfield (ptr); // x_scan
+  star[0][0].X = strtod (ptr, NULL);
+  star[2][0].X = star[1][0].X = star[0][0].X;
+
+  ptr = next2MASSfield (ptr); // jdate (julian date)
+  jd = strtod (ptr, NULL);
+  star[0][0].t = ohana_jd_to_sec (jd);
+  star[2][0].t = star[1][0].t = star[0][0].t;
+
+  ptr = next2MASSfield (ptr); // j_psfchi
+  star[0][0].psfChisq = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // h_psfchi
+  star[1][0].psfChisq = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // k_psfchi
+  star[2][0].psfChisq = strtod (ptr, NULL);
+
+  ptr = next2MASSfield (ptr); // j_m_stdap
+  star[0][0].Map = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // j_msig_stdap (skip?)
+
+  ptr = next2MASSfield (ptr); // h_m_stdap
+  star[1][0].Map = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // h_msig_stdap (skip?)
+
+  ptr = next2MASSfield (ptr); // k_m_stdap
+  star[2][0].Map = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // k_msig_stdap (skip?)
+
+  ptr = next2MASSfield (ptr); // dist_edge_ns (skip)
+  ptr = next2MASSfield (ptr); // dist_edge_ew (skip)
+  ptr = next2MASSfield (ptr); // dist_edge_flg (skip)
+
+  ptr = next2MASSfield (ptr); // dup_src (one flag for all filters)
+  set2MASS_dup_flag (star[0], ptr[0]);
+  set2MASS_dup_flag (star[1], ptr[0]);
+  set2MASS_dup_flag (star[2], ptr[0]);
+
+  ptr = next2MASSfield (ptr); // use_src (one flag for all filters)
+  set2MASS_use_flag (star[0], ptr[0]);
+  set2MASS_use_flag (star[1], ptr[0]);
+  set2MASS_use_flag (star[2], ptr[0]);
+
+  star[0][0].code  = TM_J;
+  star[0][0].found = -1;
+  star[0][0].detID   = 0;
+  star[0][0].imageID = 0;
+
+  star[1][0].code  = TM_H;
+  star[1][0].found = -1;
+  star[1][0].detID   = 0;
+  star[1][0].imageID = 0;
+
+  star[2][0].code  = TM_K;
+  star[2][0].found = -1;
+  star[2][0].detID   = 0;
+  star[2][0].imageID = 0;
+
+  return TRUE;
+}
+
+/* return a pointer to the first char after the next field separator (|) */
+char *next2MASSfield (char *line) {
+
+  char *p, *q;
+
+  p = line;
+  q = strchr (p, '|');
+  if (q == NULL) return (NULL);
+  p = q + 1;
+  if (*p == 0) return (NULL);
+  return (p);
+}
+
+int set2MASS_ph_qual (Stars *star, char qual) {
+
+  switch (qual) {
+    case 'X': star[0].flags |= 0x0000; break;
+    case 'U': star[0].flags |= 0x0001; break;
+    case 'F': star[0].flags |= 0x0002; break;
+    case 'E': star[0].flags |= 0x0003; break;
+    case 'A': star[0].flags |= 0x0004; break;
+    case 'B': star[0].flags |= 0x0005; break;
+    case 'C': star[0].flags |= 0x0006; break;
+    case 'D': star[0].flags |= 0x0007; break;
+    default: 
+      fprintf (stderr, "error!\n");
+      exit (2);
+  }      
+  return (TRUE);
+}
+
+int set2MASS_rd_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags |= 0x0000; break;
+    case '1': star[0].flags |= 0x0010; break;
+    case '2': star[0].flags |= 0x0020; break;
+    case '3': star[0].flags |= 0x0030; break;
+    case '4': star[0].flags |= 0x0040; break;
+    case '6': star[0].flags |= 0x0050; break;
+    case '9': star[0].flags |= 0x0060; break;
+    default: 
+      fprintf (stderr, "error!\n");
+      exit (2);
+  }      
+  return (TRUE);
+}
+
+int set2MASS_cc_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case 'p': star[0].flags |= 0x0000; break;
+    case 'c': star[0].flags |= 0x0100; break;
+    case 'd': star[0].flags |= 0x0200; break;
+    case 's': star[0].flags |= 0x0300; break;
+    case 'b': star[0].flags |= 0x0400; break;
+    case '0': star[0].flags |= 0x0500; break;
+    default: 
+      fprintf (stderr, "error!\n");
+      exit (2);
+  }      
+  return (TRUE);
+}
+
+int set2MASS_bl_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags &= ~0x0008; break;
+    case '1': star[0].flags &= ~0x0008; break;
+    default:  star[0].flags |=  0x0008; break;
+  }      
+  return (TRUE);
+}
+
+int set2MASS_gal_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags &= ~0x0080; break;
+    case '1': star[0].flags &= ~0x0080; break;
+    default:  
+      star[0].flags |= 0x0080; 
+      star[0].extNsigma = 100.0;
+      break;
+  }      
+  return (TRUE);
+}
+
+int set2MASS_mp_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags &= ~0x0800; break;
+    case '1': star[0].flags &= ~0x0800; break;
+    default:  star[0].flags |=  0x0800; break;
+  }      
+  return (TRUE);
+}
+
+int set2MASS_dup_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags &= ~0x1000; break;
+    case '1': star[0].flags &= ~0x1000; break;
+    default:  star[0].flags |=  0x1000; break;
+  }      
+  return (TRUE);
+}
+
+int set2MASS_use_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags &= ~0x2000; break;
+    case '1': star[0].flags |=  0x2000; break;
+    default:  abort();
+  }      
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_ops.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_ops.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/get2mass_ops.c	(revision 22322)
@@ -0,0 +1,270 @@
+# include "addstar.h"
+# include "2mass.h"
+
+static int FilterSkip;
+static int TimeSkip;
+static int Qentry;
+static int Photcode;
+
+int get2mass_setup (int photcode) {
+
+  NAMED_PHOTCODE (TM_J, "2MASS_J");
+  NAMED_PHOTCODE (TM_H, "2MASS_H");
+  NAMED_PHOTCODE (TM_K, "2MASS_K");
+
+  FilterSkip = TimeSkip = Qentry = 0;
+  Photcode = photcode;
+  if (photcode == -1) return TRUE;
+
+  if (photcode == TM_J) {
+      FilterSkip = 6;
+      TimeSkip = 28;
+      Qentry   = 0;
+  }
+  if (photcode == TM_H) {
+      FilterSkip = 10;
+      TimeSkip = 24;
+      Qentry   = 1;
+  }
+  if (photcode == TM_K) {
+      FilterSkip = 14;
+      TimeSkip = 20;
+      Qentry   = 2;
+  }
+  if (!FilterSkip) Shutdown ("invalid photcode %s", GetPhotcodeNamebyCode(photcode));
+  return TRUE;
+}
+
+// fill in the data for a single star.  takes a pointer to the start of the line
+int get2mass_star (Stars *star, char *line, int Nmax) {
+
+  char *ptr, qc;
+  double M, dM;
+  e_time time;
+
+  ptr = skipNbounds (line, '|', FilterSkip, Nmax);
+  if (ptr == NULL) Shutdown ("format error in 2mass");
+  M  = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 1, Nmax - (ptr - line));
+  dM = strtod (ptr, NULL);
+  time = get2mass_time (ptr, TimeSkip, Nmax - (ptr - line));
+
+  /* filter on the ph_qual flag for this filter (field 19) */
+  if (SELECT_2MASS_QUALITY != NULL) {
+    ptr = skipNbounds (line, '|', 18, Nmax);
+    qc  = ptr[Qentry];
+    if (strchr (SELECT_2MASS_QUALITY, qc) == NULL) return (FALSE);
+  }
+
+  star[0].M       = M;
+  star[0].dM      = dM;
+  star[0].code    = Photcode;
+  star[0].t       = time;
+  star[0].found   = -1;
+  star[0].detID   = 0;
+  star[0].imageID = 0;
+
+  return TRUE;
+}
+
+// fill in the data for a JHK triplet star.  takes a pointer to the start of the line
+int get2mass_3star (Stars *star, char *line, int Nmax) {
+
+  char *ptr;
+  char Jquality, Hquality, Kquality;
+  double J, dJ, H, dH, K, dK;
+  e_time time;
+
+  ptr = line;
+  if (ptr == NULL) Shutdown ("format error in 2mass");
+
+  ptr = skipNbounds (ptr, '|', 6, Nmax - (ptr - line));
+  J  = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 1, Nmax - (ptr - line));
+  dJ = strtod (ptr, NULL);
+
+  ptr = skipNbounds (ptr, '|', 3, Nmax - (ptr - line));
+  H  = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 1, Nmax - (ptr - line));
+  dH = strtod (ptr, NULL);
+
+  ptr = skipNbounds (ptr, '|', 3, Nmax - (ptr - line));
+  K  = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 1, Nmax - (ptr - line));
+  dK = strtod (ptr, NULL);
+
+  /* filter on the ph_qual flag for this filter (field 19) */
+  if (SELECT_2MASS_QUALITY != NULL) {
+    ptr = skipNbounds (ptr, '|', 3, Nmax - (ptr - line));
+    Jquality = (strchr (SELECT_2MASS_QUALITY, ptr[0]) != NULL);
+    Hquality = (strchr (SELECT_2MASS_QUALITY, ptr[1]) != NULL);
+    Kquality = (strchr (SELECT_2MASS_QUALITY, ptr[2]) != NULL);
+    time = get2mass_time (ptr, 18, Nmax - (ptr - line));
+  } else {
+    time = get2mass_time (ptr, 20, Nmax - (ptr - line));
+  }
+
+  // how many bits are being used for the 2mass flags; can we just set photFlags based on them?
+
+  star[0].M     = J;
+  star[0].dM    = dJ;
+  star[0].code  = TM_J;
+  star[0].t     = time;
+  star[0].found = -1;
+  star[0].detID   = 0;
+  star[0].imageID = 0;
+
+  star[1].M     = H;
+  star[1].dM    = dH;
+  star[1].code  = TM_H;
+  star[1].t     = time;
+  star[1].found = -1;
+  star[1].detID   = 0;
+  star[1].imageID = 0;
+
+  star[2].M     = K;
+  star[2].dM    = dK;
+  star[2].code  = TM_K;
+  star[2].t     = time;
+  star[2].found = -1;
+  star[2].detID   = 0;
+  star[2].imageID = 0;
+
+  return TRUE;
+}
+
+// fill in the coords for a single star.  takes a pointer to the start of the line
+int get2mass_coords (char *line, double *R, double *D, int Nmax) {
+
+  char *ptr;
+
+  *R = strtod (line, NULL);
+  ptr = skipNbounds (line, '|', 1, Nmax);
+  *D = strtod (ptr, NULL);
+  if (*D > 90) Shutdown ("weird DEC value: something is wrong");
+
+  return TRUE;
+}
+
+// this function retrieves the time from the DATE field
+e_time get2mass_date (char *ptr, int Nbound, int Nmax) {
+
+  e_time time;
+  char *p, *end;
+
+  p = skipNbounds (ptr, '|', Nbound, Nmax);
+  if (p == NULL) Shutdown ("format error in 2mass");
+  end = memchr (p, '|', Nmax - (p - ptr));
+  if (end == NULL) Shutdown ("format error in 2mass");
+  *end = 0;
+  time = ohana_date_to_sec (ptr);
+  *end = '|';
+
+  return (time);
+}
+
+// this function retrieves the time from the JDATE field (%12.4f)
+e_time get2mass_time (char *ptr, int Nbound, int Nmax) {
+
+  e_time time;
+  double jd;
+  char *p, *end;
+
+  p = skipNbounds (ptr, '|', Nbound, Nmax);
+  if (p == NULL) Shutdown ("format error in 2mass");
+  end = memchr (p, '|', Nmax - (p - ptr));
+  if (end == NULL) Shutdown ("format error in 2mass");
+  *end = 0;
+  jd = strtod (p, NULL);
+  time = ohana_jd_to_sec (jd);
+  *end = '|';
+
+  return (time);
+}
+
+/* return a pointer to the first char after Nbound of value bound */
+char *skipNbounds (char *line, char bound, int Nbound, int Nbyte) {
+
+  int i;
+  char *p, *q;
+
+  p = line;
+  for (i = 0; i < Nbound; i++) {
+    q = memchr (p, bound, Nbyte - (p - line));
+    if (q == NULL) return (NULL);
+    p = q + 1;
+    if (p - line == Nbyte) return (NULL);
+  }
+  return (p);
+}
+  
+/* watch for patches which cross 0,360 boundary */
+SkyTable *load2mass_acc (char *path, char *accel) {
+
+  int Nregions, NREGIONS, Nrec;
+  char accelfile[1024], line[256], filename[128], datafile[256], **filenames;
+  FILE *f;
+  double Rs, Re, Ds, De;
+
+  SkyTable *sky;
+  SkyRegion *regions;
+
+  sprintf (accelfile, "%s/%s", path, accel);
+  f = fopen (accelfile, "r");
+  if (f == NULL) Shutdown ("can't read data from accelerator %s", accelfile);
+
+  Nregions = 0;
+  NREGIONS = 200;
+  ALLOCATE (regions, SkyRegion, NREGIONS);
+  ALLOCATE (filenames, char *, NREGIONS);
+
+  /* read in stars line-by-line */
+  while (scan_line (f, line) != EOF) {
+    stripwhite (line);
+    if (line[0] == 0) continue;
+    if (line[0] == '#') continue;
+    sscanf (line, "%s %lf %lf %lf %lf %d", filename, &Rs, &Re, &Ds, &De, &Nrec);
+    Rs *= 15.0;
+    Re *= 15.0;
+
+    // don't restrict by RA, but limit by DEC
+    if (De < UserPatch.Dmin) continue;
+    if (Ds > UserPatch.Dmax) continue;
+
+    regions[Nregions].Rmin = Rs;
+    regions[Nregions].Rmax = Re;
+    regions[Nregions].Dmin = Ds;
+    regions[Nregions].Dmax = De;
+    regions[Nregions].childE = Nrec; // a cheat since 2MASS only has one depth
+
+    sprintf (datafile, "%s/%s", path, filename);
+    filenames[Nregions] = strcreate (datafile);
+
+    Nregions ++;
+    if (Nregions >= NREGIONS) {
+	NREGIONS += 20;
+	REALLOCATE (regions, SkyRegion, NREGIONS);
+	REALLOCATE (filenames, char *, NREGIONS);
+    }
+  }    
+  fclose (f);
+
+  ALLOCATE (sky, SkyTable, 1);
+  sky[0].regions = regions;
+  sky[0].filename = filenames;
+  sky[0].Nregions = Nregions;
+  return (sky);
+}
+
+int get2mass_sortStars (TMStars *tstars, int Ntstars) {
+
+# define SWAPFUNC(A,B){ TMStars temp = tstars[A]; tstars[A] = tstars[B]; tstars[B] = temp; }
+# define COMPARE(A,B)(tstars[A].R < tstars[B].R)
+
+  OHANA_SORT (Ntstars, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+  
+  return TRUE;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/getgsc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/getgsc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/getgsc.c	(revision 22322)
@@ -0,0 +1,101 @@
+# include "addstar.h"
+# define BYTES_STAR 23
+# define BLOCK 1000
+# define DNSTARS 1000
+
+static short GSC_M;
+static SkyTable *sky = NULL;
+
+Stars *getgsc (SkyRegion *patch, int *NSTARS) {
+  
+  int i, Ngsc, Nstars; 
+  Stars *gsc;
+  Stars *stars;
+  SkyList *skylist;
+
+  NAMED_PHOTCODE (GSC_M, "GSC");
+
+  /* load regions from GSC table, restrict to patch */
+  if (!sky) {
+    sky = SkyTableFromGSC (GSCFILE, SKY_DEPTH_HST, VERBOSE);
+    SkyTableSetFilenames (sky, GSCDIR, "cpt");
+  }
+  skylist = SkyListByPatch (sky, -1, patch);
+  
+  Nstars = 0;
+  ALLOCATE (stars, Stars, 1);
+  
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    gsc = rd_gsc (skylist[0].filename[i], &Ngsc);
+
+    REALLOCATE (stars, Stars, MAX (1, Nstars + Ngsc));
+    memcpy (&stars[Nstars], gsc, Ngsc*sizeof(Stars));
+    Nstars += Ngsc;
+
+    free (gsc);
+  }
+  
+  // XXX don't free because we only load it once and save it
+  SkyListFree (skylist);
+  // SkyTableFree (sky);
+
+  if (VERBOSE) fprintf (stderr, "%d stars from HST GSC\n", Nstars);
+  *NSTARS = Nstars;
+  return (stars);
+}  
+
+Stars *rd_gsc (char *filename, int *nstars) {
+  
+  int i, NSTARS, Nstars, Nbytes, nbytes, Nline, Nbyte;
+  char *buffer;
+  FILE *f;
+  Stars *stars;
+
+  Nstars = 0;
+  NSTARS = 1000;
+  ALLOCATE (stars, Stars, NSTARS);
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't find catalog file %s\n", filename);
+    exit (2);
+  }
+  
+  Nbytes = BLOCK*BYTES_STAR;
+  ALLOCATE (buffer, char, Nbytes);
+  while ((nbytes = fread (buffer, 1, Nbytes, f)) > 0) {
+    Nline = nbytes / BYTES_STAR;
+    for (i = 0; i < Nline; i++) {
+      Nbyte = i*BYTES_STAR;
+      dparse (&stars[Nstars].R, 1, &buffer[Nbyte]);
+      dparse (&stars[Nstars].D, 2, &buffer[Nbyte]);
+      if (stars[Nstars].R < UserPatch.Rmin) continue;
+      if (stars[Nstars].R > UserPatch.Rmax) continue;
+      if (stars[Nstars].D < UserPatch.Dmin) continue;
+      if (stars[Nstars].D > UserPatch.Dmax) continue;
+
+      dparse (&stars[Nstars].M, 3, &buffer[Nbyte]);
+      stars[Nstars].dM 	  = NAN;
+      stars[Nstars].t  	  = 0;
+      stars[Nstars].code  = GSC_M;
+      stars[Nstars].found = -1;
+
+      stars[Nstars].detID   = 0;
+      stars[Nstars].imageID = 0;
+
+      stars[Nstars].X  = 0;
+      stars[Nstars].Y  = 0;
+      stars[Nstars].fx = 0;
+      stars[Nstars].fy = 0;
+      stars[Nstars].df = 0;
+
+      Nstars ++;
+      CHECK_REALLOCATE (stars, Stars, NSTARS, Nstars, 1000);
+    }
+  }
+  fclose (f);
+  free (buffer);
+  REALLOCATE (stars, Stars, Nstars);
+  *nstars = Nstars;
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/gettycho.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/gettycho.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/gettycho.c	(revision 22322)
@@ -0,0 +1,142 @@
+# include "addstar.h"
+# define NZONE 180
+# define NBYTE 207
+# define NITEM 100
+
+static SkyTable *tychoTable = NULL;
+
+Stars *gettycho (SkyRegion *catstats, int photcode, int *nstars) {
+
+  int i, j, nitems;
+  char *buffer;
+  char filename[128], *line;
+  FILE *f;
+  int NTYCHO, Ntycho;
+  int firstRow, firstByte;
+  short int TYCHO_B, TYCHO_V;
+  e_time TychoEpoch;
+  SkyRegion *region;
+  SkyList  *skylist;
+  Stars *stars;
+
+  /* require photcode */
+  NAMED_PHOTCODE (TYCHO_B, "TYCHO_B");
+  NAMED_PHOTCODE (TYCHO_V, "TYCHO_V");
+  if (photcode == TYCHO_B) goto good_code;
+  if (photcode == TYCHO_V) goto good_code;
+  Shutdown ("TYCHO photcode not specified");
+good_code:
+
+  fprintf (stderr, "loading TYCHO catalog\n");
+
+  /* load tycho index file into sky table */
+  if (tychoTable == NULL) {
+    sprintf (filename, "%s/index.dat", TYCHO_DIR);
+    tychoTable = SkyTableFromTychoIndex (filename, VERBOSE);
+    if (tychoTable == NULL) {
+      fprintf (stderr, "ERROR: problem loading tycho data\n");
+      exit (2);
+    }
+  }
+
+  /* identify tycho region files overlapping the requested region */
+  skylist = SkyListByPatch (tychoTable, 2, catstats);
+
+  /* open the Tycho catalog file */
+  sprintf (filename, "%s/tycho.dat", TYCHO_DIR);
+  f = fopen (filename, "r");
+
+  Ntycho = 0;
+  NTYCHO = 5000;
+  ALLOCATE (stars, Stars, NTYCHO);
+
+  ALLOCATE (buffer, char, NITEM*NBYTE);
+
+  TychoEpoch = ohana_date_to_sec ("1991/04/02,07:30:00");
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    region = skylist[0].regions[i];
+    if (VERBOSE) fprintf (stderr, "section %d (%f - %f, %f - %f)...", i, 
+			  region[0].Rmin, region[0].Rmax, region[0].Dmin, region[0].Dmax);
+
+    firstRow  = atoi (region[0].name);
+    firstByte = firstRow * NBYTE;
+    fseek (f, firstByte, SEEK_SET);
+
+    while (1) {
+      nitems = fread (buffer, NBYTE, NITEM, f);
+      if (nitems == 0) break;
+
+      for (j = 0; j < nitems; j++) {
+	line = &buffer[j*NBYTE];
+	stars[Ntycho].R = atof (&line[15]);
+	stars[Ntycho].D = atof (&line[28]);
+
+	if (stars[Ntycho].R < region[0].Rmin) goto next_section;
+	if (stars[Ntycho].R > region[0].Rmax) goto next_section;
+	if (stars[Ntycho].D < region[0].Dmin) goto next_section;
+	if (stars[Ntycho].D > region[0].Dmax) goto next_section;
+
+	if (stars[Ntycho].R < UserPatch.Rmin) continue;
+	if (stars[Ntycho].R > UserPatch.Rmax) continue;
+	if (stars[Ntycho].D < UserPatch.Dmin) continue;
+	if (stars[Ntycho].D > UserPatch.Dmax) continue;
+
+	stars[Ntycho].dR  = 1000 * atof (&line[57]);
+	stars[Ntycho].dD  = 1000 * atof (&line[64]);
+
+	/* XXX : we need to apply uR,uD to R,D to advance to 2000.0 */
+	stars[Ntycho].uR  = atof (&line[41]);
+	stars[Ntycho].uD  = atof (&line[49]);
+
+	stars[Ntycho].duR = atof (&line[69]);
+	stars[Ntycho].duD = atof (&line[75]);
+	
+	stars[Ntycho].P   = 0;
+	stars[Ntycho].dP  = 0;
+
+	stars[Ntycho].detID   = 0;
+	stars[Ntycho].imageID = 0;
+
+	/* Tycho uses J2000 equinox and 1991.25 epoch for coordinates */
+	/* the magnitudes have no temporal information */ 
+	stars[Ntycho].t   = TychoEpoch;
+	stars[Ntycho].found = -1;
+      
+	stars[Ntycho].X  = 0;
+	stars[Ntycho].Y  = 0;
+	stars[Ntycho].fx = 0;
+	stars[Ntycho].fy = 0;
+	stars[Ntycho].df = 0;
+
+	/* one pass of addstar does either r or b */
+	if (photcode == TYCHO_B) {
+	  stars[Ntycho].M     = atof (&line[110]);
+	  stars[Ntycho].dM    = atof (&line[117]);
+	  stars[Ntycho].code  = TYCHO_B;
+	} else {
+	  stars[Ntycho].M     = atof (&line[123]);
+	  stars[Ntycho].dM    = atof (&line[130]);
+	  stars[Ntycho].code  = TYCHO_V;
+	}	
+      
+	Ntycho ++;
+	CHECK_REALLOCATE (stars, Stars, NTYCHO, Ntycho, 5000);
+      }
+
+      if (nitems != NITEM) break;
+    }
+  next_section:
+    if (VERBOSE) fprintf (stderr, "%d stars\n", Ntycho);
+  }
+  fclose (f);
+
+  SkyListFree (skylist);
+
+  free (buffer);
+  REALLOCATE (stars, Stars, MAX (1, Ntycho));
+
+  *nstars = Ntycho;
+  if (VERBOSE) fprintf (stderr, "%d stars from Tycho\n", Ntycho);
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/getusno.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/getusno.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/getusno.c	(revision 22322)
@@ -0,0 +1,168 @@
+# include "addstar.h"
+# define NZONE 24
+# define NBYTE  4
+# define NELEM  3
+
+Stars *getusno (SkyRegion *catstats, int photcode, int *nstars) {
+
+  long int offset;
+  int i, bin, first, last, nitems, Nitems, Nbins, Nstars;
+  float hours[100];
+  int start[100], number[100], *buffer, *buf;
+  char filename[128];
+  FILE *f;
+  int iRA0, iRA1, iDEC0, iDEC1;
+  double dec;
+  int spd, spd_start, spd_end;
+  int NUSNO, Nusno;
+  Stars *stars;
+  short int USNO_RED, USNO_BLUE;
+
+  /* require photcode */
+  NAMED_PHOTCODE (USNO_RED, "USNO_RED");
+  NAMED_PHOTCODE (USNO_BLUE, "USNO_BLUE");
+  if (photcode == USNO_RED) goto good_code;
+  if (photcode == USNO_BLUE) goto good_code;
+  Shutdown ("USNO photcode not specified");
+good_code:
+
+  /* identify ra & dec range of interest */
+  /* note: the use of UserPatch to restrict here limits general utility of function */
+  iRA0  =  MAX (catstats[0].Rmin, UserPatch.Rmin) * 360000.0;
+  iRA1  =  MIN (catstats[0].Rmax, UserPatch.Rmax) * 360000.0;
+  iDEC0 = (MAX (catstats[0].Dmin, UserPatch.Dmin) + 90.0) * 360000.0;
+  iDEC1 = (MIN (catstats[0].Dmax, UserPatch.Dmax) + 90.0) * 360000.0;
+  
+  /* data is organized in south-pole distance zones */
+  spd_start = (int)((catstats[0].Dmin + 90) / 7.5) * 75.0;
+  dec = (catstats[0].Dmax + 90) / 7.5;
+  if (dec > (int)(dec)) {
+    spd_end =   (int)(1 + (catstats[0].Dmax + 90) / 7.5) * 75.0;
+  } else {
+    spd_end =   (int)(0 + (catstats[0].Dmax + 90) / 7.5) * 75.0;
+  }
+
+  Nusno = 0;
+  NUSNO = 5000;
+  ALLOCATE (stars, Stars, NUSNO);
+
+  for (spd = spd_start; spd < spd_end; spd += 75) {
+    
+    /* load accelerator file */
+    sprintf (filename, "%s/zone%04d.acc", USNO_A_DIR, spd); 
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open accelerator file %s\n", filename);
+      exit (1);  
+    }
+    for (i = 0; fscanf (f, "%f %d %d", &hours[i], &start[i], &number[i]) != EOF; i++);
+    Nbins = i;
+    fclose (f);
+    
+    first = catstats[0].Rmin / 3.75;
+    if ((catstats[0].Rmax / 3.75) == (int) (catstats[0].Rmax / 3.75)) 
+      last  = catstats[0].Rmax / 3.75;
+    else 
+      last  = 1 + catstats[0].Rmax / 3.75;
+
+    if ((first > Nbins) || (last > Nbins)) {
+      fprintf (stderr, "ERROR: RA out of range\n");
+      exit (1);
+    }
+    
+    /* open data file */
+    sprintf (filename, "%s/zone%04d.cat", USNO_A_DIR, spd);
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file %s\n", filename);
+      exit (1);
+    }
+
+    /** USNO-A consists of 3 x 4byte (int) records **/
+    /* advance file pointer to first slice */
+    offset = NELEM*NBYTE*(start[first] - 1);
+    fseek (f, offset, SEEK_SET);
+
+    /* sum the number of stars in data segment of interest */
+    Nstars = 0;
+    for (bin = first; bin < last; bin++) {
+      Nstars += number[bin];
+    }
+    Nitems = NELEM*Nstars;  
+    /* number of blocks to read -- we need to use Fread for byte-swapping read */
+
+    /* read stars from catalog */
+    ALLOCATE (buffer, int, Nitems);
+    nitems = Fread (buffer, NBYTE, Nitems, f, "int");
+    if (nitems != Nitems) {
+      fprintf (stderr, "ERROR: failure reading data from file %s\n", filename);
+      exit (1);
+    }
+
+    /* extract the data of interest from segment (in RA and DEC range) */
+    buf = buffer;
+    for (i = 0; i < Nstars; i++, buf += NELEM) {
+      if (buf[0] < iRA0) continue;
+      if (buf[0] > iRA1) continue;
+      if (buf[1] < iDEC0) continue;
+      if (buf[1] > iDEC1) continue;
+
+      memset (&stars[Nusno], 0, sizeof(Stars));
+      stars[Nusno].R     = buf[0]/360000.0;
+      stars[Nusno].D     = buf[1]/360000.0 - 90.0;
+      stars[Nusno].dM    = NAN;
+      stars[Nusno].t     = 0;
+      stars[Nusno].found = -1;
+
+      stars[Nusno].detID   = 0;
+      stars[Nusno].imageID = 0;
+
+      stars[Nusno].X  = 0;
+      stars[Nusno].Y  = 0;
+      stars[Nusno].fx = 0;
+      stars[Nusno].fy = 0;
+      stars[Nusno].df = 0;
+
+      /* one pass of addstar does either r or b */
+      if (photcode == USNO_RED) {
+	stars[Nusno].code  = USNO_RED;
+	stars[Nusno].M     = fabs (0.1*(buf[2] - 1000*((int)(buf[2]/1000))));
+      } 
+      if (photcode == USNO_BLUE) {	
+	stars[Nusno].code  = USNO_BLUE;
+	stars[Nusno].M     = fabs (0.1*((int)(buf[2] - 1000000*((int)(buf[2]/1000000))) / 1000));
+      }
+      Nusno ++;
+      CHECK_REALLOCATE (stars, Stars, NUSNO, Nusno, 5000);
+    }
+    free (buffer);
+    fclose (f);
+  }
+  REALLOCATE (stars, Stars, Nusno);
+
+  *nstars = Nusno;
+  if (VERBOSE) fprintf (stderr, "%d stars from USNO 1.0\n", Nusno);
+  return (stars);
+}
+
+
+
+
+/* these entries are legacy code incase you want to read from one of the USNO CDRoms
+int SPDzone[]  = {0, 75, 450, 375, 1500, 1650, 300, 1425, 1725, 525, 1275, 225, 675, 150, 600, 1575, 750, 975, 900, 1050, 1125, 1200, 825, 1350};
+int USNOdisk[] = {1,  1,   1,   2,    2,    2,   3,    3,    3,   4,    4,   5,   5,   6,   6,    6,   7,   7,   8,    8,    9,    9,  10,   10};
+
+    disk = -1;
+    for (i = 0; i < NZONE; i++) {
+      if (spd == SPDzone[i]) 
+	disk = USNOdisk[i];
+    }
+    if (disk < 0) {
+      fprintf (stderr, "ERROR: can't find cdrom for spd %d\n",  spd);
+      exit (1);
+    }
+
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/getusnob.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/getusnob.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/getusnob.c	(revision 22322)
@@ -0,0 +1,199 @@
+# include "addstar.h"
+# define NBYTE   4
+# define NELEM  20
+
+Stars *getusnob (SkyRegion *catstats, int photcode, int *nstars) {
+
+  long int offset;
+  int i, bin, first, last, nitems, Nitems, Nbins, Nitemsum;
+  float hours[100];
+  int start[100], number[100], *buffer, *buf;
+  char filename[128];
+  FILE *f;
+  double dec;
+  float m1, m2, mag;
+  int iDEC0, iDEC1, iRA0, iRA1;
+  int spd, spd_start, spd_end;
+  int NUSNO, Nusno, Nstars;
+  short int USNO_RED, USNO_BLUE, USNO_N;
+  e_time USNOepoch;
+  Stars *stars;
+
+  m1 = m2 = 0.0;
+
+  /* require photcode */
+  NAMED_PHOTCODE (USNO_RED,  "USNO_RED");
+  NAMED_PHOTCODE (USNO_BLUE, "USNO_BLUE");
+  NAMED_PHOTCODE (USNO_N,    "USNO_N");
+  if (photcode == USNO_RED)  goto good_code;
+  if (photcode == USNO_BLUE) goto good_code;
+  if (photcode == USNO_N)    goto good_code;
+  Shutdown ("USNO photcode not specified");
+good_code:
+
+  fprintf (stderr, "loading USNO-B 1.0\n");
+
+  /* identify ra & dec range of interest */
+  /* note: the use of UserPatch to restrict here limits general utility of function */
+  iRA0  =  MAX (catstats[0].Rmin, UserPatch.Rmin) * 360000.0;
+  iRA1  =  MIN (catstats[0].Rmax, UserPatch.Rmax) * 360000.0;
+  iDEC0 = (MAX (catstats[0].Dmin, UserPatch.Dmin) + 90.0) * 360000.0;
+  iDEC1 = (MIN (catstats[0].Dmax, UserPatch.Dmax) + 90.0) * 360000.0;
+  /* note that iDECn is in SPD, while both have units of 0.01 degrees */
+  
+  /* data is organized in south-pole distance zones, 1 deg per direction, 0.1 deg per file */
+  spd_start = (int)(10*(catstats[0].Dmin + 90));
+  dec = 10*(catstats[0].Dmax + 90);
+  if (dec > (int)(dec)) {
+    spd_end =   (int)(1 + 10*(catstats[0].Dmax + 90));
+  } else {
+    spd_end =   (int)(0 + 10*(catstats[0].Dmax + 90));
+  }
+
+  Nitemsum = 0;
+  Nusno = 0;
+  NUSNO = 5000;
+  ALLOCATE (stars, Stars, NUSNO);
+
+  for (spd = spd_start; spd < spd_end; spd ++) {
+    
+    /* load accelerator file */
+    sprintf (filename, "%s/%03d/b%04d.acc", USNO_B_DIR, (int)(spd/10), spd); 
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open accelerator file %s\n", filename);
+      exit (1);  
+    }
+    for (i = 0; fscanf (f, "%f %d %d", &hours[i], &start[i], &number[i]) != EOF; i++);
+    Nbins = i;
+    fclose (f);
+    
+    first = catstats[0].Rmin / 3.75;
+    if ((catstats[0].Rmax / 3.75) == (int) (catstats[0].Rmax / 3.75)) 
+      last  = catstats[0].Rmax / 3.75;
+    else 
+      last  = 1 + catstats[0].Rmax / 3.75;
+
+    if ((first > Nbins) || (last > Nbins)) {
+      fprintf (stderr, "ERROR: RA out of range\n");
+      exit (1);
+    }
+    
+    /* open data file */
+    sprintf (filename, "%s/%03d/b%04d.cat", USNO_B_DIR, (int)(spd/10), spd); 
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file %s\n", filename);
+      exit (1);
+    }
+
+    /** USNO-B consists of 20 x 4byte (int) records **/
+    /* advance file pointer to first slice */
+    offset = NELEM*NBYTE*(start[first] - 1);
+    fseek (f, offset, SEEK_SET);
+
+    /* sum the number of stars in data segment of interest */
+    Nstars = 0;
+    for (bin = first; bin < last; bin++) {
+      Nstars += number[bin];
+    }
+    Nitems = NELEM*Nstars;  
+    /* number of blocks to read -- we need to use Fread for byte-swapping read */
+
+    /* allocate space for stars in segment and read */
+    ALLOCATE (buffer, int, Nitems);
+    // data has the WRONG byte order?
+    // nitems = Fread (buffer, sizeof(int), Nitems, f, "int");
+    nitems = fread (buffer, NBYTE, Nitems, f);
+    if (nitems != Nitems) {
+      fprintf (stderr, "ERROR: failure reading data from file %s\n", filename);
+      exit (1);
+    }
+
+    USNOepoch = ohana_date_to_sec ("2000/01/01,00:00:00");
+
+    buf = buffer;
+    /* print out data from slice within RA and DEC range */
+    for (i = 0; i < Nstars; i++, buf += NELEM) {
+      if (buf[0] < iRA0) continue;
+      if (buf[0] > iRA1) continue;
+      if (buf[1] < iDEC0) continue;
+      if (buf[1] > iDEC1) continue;
+      
+      /* USNO coords are reported for J2000 / epoch 2000.0 */
+      /* extract the basic stellar data */
+      memset (&stars[Nusno], 0, sizeof(Stars));
+      stars[Nusno].R   = buf[0]/360000.0;
+      stars[Nusno].D   = buf[1]/360000.0 - 90.0;
+
+      /* XXX uR cos(D) or just uR ??? */
+      stars[Nusno].uR  = 2.0 * ((buf[2]       % 10000) - 5000);
+      stars[Nusno].uD  = 2.0 * ((buf[2]/10000 % 10000) - 5000);
+
+      stars[Nusno].duR = (buf[3]      % 1000);
+      stars[Nusno].duD = (buf[3]/1000 % 1000);
+
+      stars[Nusno].dR  = 0.001 * (buf[4]      % 1000);
+      stars[Nusno].dD  = 0.001 * (buf[4]/1000 % 1000);
+
+      stars[Nusno].P   = 0;
+      stars[Nusno].dP  = 0;
+
+      /* USNO magnitude errors are reported as a fixed 0.3 mag */
+      stars[Nusno].dM    = 0.3;
+      stars[Nusno].found = -1;
+      
+      stars[Nusno].detID   = 0;
+      stars[Nusno].imageID = 0;
+
+      stars[Nusno].X  = 0;
+      stars[Nusno].Y  = 0;
+      stars[Nusno].fx = 0;
+      stars[Nusno].fy = 0;
+      stars[Nusno].df = 0;
+
+      /* USNO-B uses J2000 equinox and 2000.0 epoch for coordinates */
+      /* the magnitudes have no temporal information */ 
+      stars[Nusno].t     = USNOepoch;
+
+      /* one pass of addstar does r, b, or n */
+      if (photcode == USNO_BLUE) {
+	  m1 = fabs(0.01 * (buf[5] % 10000)); /* 1st blue mag */
+	  m2 = fabs(0.01 * (buf[7] % 10000)); /* 1st blue mag */
+      }
+      if (photcode == USNO_RED) {
+	  m1 = fabs(0.01 * (buf[6] % 10000)); /* 1st red mag */
+	  m2 = fabs(0.01 * (buf[8] % 10000)); /* 2nd red mag */
+      }
+      if (photcode == USNO_N) {
+	  m1 = fabs(0.01 * (buf[9] % 10000)); /* N (IR) survey */
+	  m2 = 0;
+      }
+      stars[Nusno].code = photcode;
+
+      /* if two mags are available, get an average */
+      if (m1 && m2) {
+	mag = 0.5*(m1 + m2);
+      } else {
+	mag = (m1) ? m1 : m2;
+      }
+      stars[Nusno].M = (mag == 0.0) ? 32.0 : mag;
+
+      Nusno ++;
+      CHECK_REALLOCATE (stars, Stars, NUSNO, Nusno, 5000);
+    }
+    free (buffer);
+    Nitemsum += Nitems;
+    fprintf (stderr, "Nusno: %d, Nitems: %d, Nitemsum : %d, Nbytes stars: %d\n",
+	     Nusno, Nitems, Nitemsum, (unsigned int) (Nusno*sizeof(Stars)));
+    fclose (f);
+  }
+
+  REALLOCATE (stars, Stars, MAX (1, Nusno));
+
+  *nstars = Nusno;
+  if (VERBOSE) fprintf (stderr, "%d stars from USNO-B 1.0\n", Nusno);
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/greference.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/greference.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/greference.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "addstar.h"
+# define LOAD_ALLSKY 0
+# define LOAD_DR2    1
+
+Stars *grefcat (char *Refcat, SkyRegion *region, int photcode, int *nstars) {
+
+  int Nstars;
+  Stars *stars;
+
+  if (VERBOSE) fprintf (stderr, "loading reference catalog data from %s\n", Refcat); 
+  if (VERBOSE) fprintf (stderr, "full region: %f - %f, %f - %f\n", region[0].Rmin, region[0].Rmax, region[0].Dmin, region[0].Dmax);
+
+  Nstars = 0;
+  stars = NULL;
+
+  /* get stars from USNO for the given region */
+  if (!strcasecmp (Refcat, "USNO")) {
+    stars = getusno (region, photcode, &Nstars);
+  }
+
+  /* get stars from the USNO B catalog for the given region */
+  if (!strcasecmp (Refcat, "USNOB")) {
+    stars = getusnob (region, photcode, &Nstars);
+  }
+
+  /* get stars from the USNO B catalog for the given region */
+  if (!strcasecmp (Refcat, "TYCHO")) {
+    stars = gettycho (region, photcode, &Nstars);
+  }
+
+  /* get stars from HST GSC for the given region */
+  if (!strcasecmp (Refcat, "GSC")) {
+    stars = getgsc (region, &Nstars);
+  }
+  
+  /* get stars from 2MASS for the given region */
+  if (!strcasecmp (Refcat, "2MASS")) {
+    stars = get2mass (region, photcode, LOAD_ALLSKY, &Nstars);
+  }
+  
+  /* get stars from 2MASS for the given region */
+  if (!strcasecmp (Refcat, "2MASS-ALLSKY")) {
+    stars = get2mass (region, photcode, LOAD_ALLSKY, &Nstars);
+  }
+  
+  /* get stars from 2MASS for the given region */
+  if (!strcasecmp (Refcat, "2MASS-DR2")) {
+    stars = get2mass (region, photcode, LOAD_DR2, &Nstars);
+  }
+  
+  if (VERBOSE && (Nstars == 0)) fprintf (stderr, "no ref objects in region %s\n", region[0].name);
+
+  *nstars = Nstars;
+  return (stars);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/grefstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/grefstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/grefstars.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "addstar.h"
+
+/* read ASCII file with ref star data */
+Stars *grefstars (char *file, int photcode, int *Nstars) {
+
+  FILE *f;
+  int N, NSTARS;
+  Stars *stars;
+  char line[256];
+
+  /* open file */
+  f = fopen (file, "r");
+  if (f == NULL) Shutdown ("can't read data from %s", file);
+
+  NSTARS = 100;
+  ALLOCATE (stars, Stars, NSTARS);
+
+  /* read in stars line-by-line */
+  for (N = 0; scan_line (f, line) != EOF; N++) {
+    stripwhite (line);
+    if (line[0] == 0) continue;
+    if (line[0] == '#') continue;
+    dparse (&stars[N].R,  1, line);
+    dparse (&stars[N].D,  2, line);
+    dparse (&stars[N].M,  3, line);
+    dparse (&stars[N].dM, 4, line);
+    while (stars[N].R < 0.0) stars[N].R += 360.0;
+    while (stars[N].R >= 360.0) stars[N].R -= 360.0;
+    stars[N].t = 0;
+    stars[N].code = photcode;
+    stars[N].found = FALSE;
+    stars[N].detID   = 0;
+    stars[N].imageID = 0;
+
+    stars[N].X  = 0;
+    stars[N].Y  = 0;
+    stars[N].fx = 0;
+    stars[N].fy = 0;
+    stars[N].df = 0;
+
+    CHECK_REALLOCATE (stars, Stars, NSTARS, N+1, 100);
+  }
+  *Nstars = N;
+  return (stars);
+}
+
+/* stars.found is set here to FALSE.
+   find_match_refstars uses stars.found to identify the seq number of the
+   star which is found.  it requires an initial value of -1 XXX
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/gztest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/gztest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/gztest.c	(revision 22322)
@@ -0,0 +1,99 @@
+# include "ohana.h"
+# include "zlib.h"
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+main (int argc, char **argv) {
+
+  int N, Nread, Nseek, Nback;
+  char *filename, *buffer;
+  gzFile gf;
+  FILE *f;
+  struct timeval start, stop;
+
+  if (argc != 6) {
+    fprintf (stderr, "USAGE: gztest (mode) (file) (Nseek) (Nread) (Nback)\n");
+    exit (2);
+  }
+
+  filename = argv[2];
+  Nseek = atoi (argv[3]);
+  Nread = atoi (argv[4]);
+  Nback = -1 * atoi (argv[5]);
+  ALLOCATE (buffer, char, Nread);
+
+  if (!strcmp (argv[1], "gz")) {
+    gf = gzopen (filename, "rb");
+    if (gf == NULL) {
+      fprintf (stderr, "can't read data file: %s", filename);
+      exit (1);
+    }
+
+    gettimeofday (&start, NULL);
+    N = gzseek (gf, Nseek, SEEK_SET);
+    gettimeofday (&stop, NULL);
+    if (N != Nseek) {
+      fprintf (stderr, "error seeking\n");
+      exit (1);
+    }
+    fprintf (stdout, "seek: %f\n", DTIME (stop, start));
+
+    gettimeofday (&start, NULL);
+    N = gzread (gf, buffer, Nread);
+    if (N != Nread) {
+      fprintf (stderr, "error reading\n");
+      exit (1);
+    }
+    gettimeofday (&stop, NULL);
+    fprintf (stdout, "read: %f\n", DTIME (stop, start));
+
+    gettimeofday (&start, NULL);
+    N = gzseek (gf, Nback, SEEK_CUR);
+    gettimeofday (&stop, NULL);
+    if (N == -1) {
+      fprintf (stderr, "error seeking\n");
+      exit (1);
+    }
+    fprintf (stdout, "back: %f\n", DTIME (stop, start));
+    exit (0);
+  } 
+
+  if (!strcmp (argv[1], "raw")) {
+    f = fopen (filename, "r");
+    if (f == NULL) {
+      fprintf (stderr, "can't read data file: %s", filename);
+      exit (1);
+    }
+
+    gettimeofday (&start, NULL);
+    N = fseek (f, Nseek, SEEK_SET);
+    gettimeofday (&stop, NULL);
+    if (N) {
+      fprintf (stderr, "error seeking\n");
+      exit (1);
+    }
+    fprintf (stdout, "seek: %f\n", DTIME (stop, start));
+
+    gettimeofday (&start, NULL);
+    N = fread (buffer, 1, Nread, f);
+    gettimeofday (&stop, NULL);
+    if (N != Nread) {
+      fprintf (stderr, "error reading\n");
+      exit (1);
+    }
+    fprintf (stdout, "read: %f\n", DTIME (stop, start));
+
+    gettimeofday (&start, NULL);
+    N = fseek (f, Nback, SEEK_CUR);
+    gettimeofday (&stop, NULL);
+    if (N) {
+      fprintf (stderr, "error seeking\n");
+      exit (1);
+    }
+    fprintf (stdout, "back: %f\n", DTIME (stop, start));
+    exit (0);
+  }
+    
+  
+  fprintf (stderr, "unknown mode\n");
+  exit (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/in_image.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/in_image.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/in_image.c	(revision 22322)
@@ -0,0 +1,14 @@
+# include "addstar.h"
+
+int in_image (double r, double d, Image *image) {
+
+  double X, Y;
+
+  RD_to_XY (&X, &Y, r, d, &image[0].coords);
+  if (X < 0) return (FALSE);
+  if (Y < 0) return (FALSE);
+  if (X >= image[0].NX) return (FALSE);
+  if (Y >= image[0].NY) return (FALSE);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load2mass.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load2mass.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load2mass.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "addstar.h"
+# include "2mass.h"
+
+int main (int argc, char **argv) {
+
+  char *path;
+  int i;
+  SkyTable *sky, *sky2mass;
+  AddstarClientOptions options;
+
+  // need to construct these options with args_load2mass...
+  options = ConfigInit (&argc, argv);
+  options = args_load2mass (argc, argv, options);
+
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  path = TWO_MASS_DIR_AS;
+
+  // the accel.dat file has the raw filenames
+  // test if the file exists, or else try the .gz version
+  sky2mass = load2mass_acc (path, "accel.dat");
+  
+  for (i = 0; i < sky2mass[0].Nregions; i++) {
+    fprintf (stderr, "loading %s\n", sky2mass[0].filename[i]);
+    load2mass_as_rawdata (sky, sky2mass[0].filename[i], options);
+  }
+  exit (0);
+}  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load2mass_as_rawdata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load2mass_as_rawdata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load2mass_as_rawdata.c	(revision 22322)
@@ -0,0 +1,229 @@
+# include "addstar.h"
+# include "2mass.h"
+
+/* unlike the DR2 data, the AS data is NOT fixed bytes/row 
+ * we need to handle fractional lines at the end of each read block
+ */
+
+/* read in chunks of ~64MB */
+# define NBYTE 0x4000000
+# define DEBUG 0
+
+int load2mass_as_rawdata (SkyTable *skytable, char *filename, AddstarClientOptions options) {
+  
+  int i, j, verbose;
+  int Nstars, NSTARS, Ntstars, NTSTARS;
+  int Nbyte, Nextra, Ntotal, offset;
+
+  double Rmin, Rmax, Dmin, Dmax;
+
+  FILE *f;
+  char *buffer, *p, *q;
+
+  Stars **stars; // this is an array of pointers to be consistent with input to find_match_refstars
+  TMStars *tstars;
+  SkyList *skylist;
+  SkyRegion *region;
+  Catalog catalog;
+
+  get2mass_setup (-1);
+
+  ALLOCATE (buffer, char, NBYTE);
+
+  // scan through the entire 2MASS file
+  f = fopen (filename, "r");
+  if (f == NULL) Shutdown ("can't read 2mass data file: %s", filename);
+  // test if this is a raw datafile or gzipped...
+
+  Nextra = 0;  // number excess bytes from lsat partial row
+  Ntotal = 0;  // track the total number of bytes read 
+  while ((Nbyte = fread (&buffer[Nextra], 1, NBYTE-Nextra, f)) != 0) {
+    if (Nbyte == -1) Shutdown ("error reading from raw file %s", filename);
+    if (DEBUG) fprintf (stderr, "read %d bytes", Nbyte);
+
+    Nbyte += Nextra;
+
+    if (VERBOSE) fprintf (stderr, "read .. ");
+
+    /* find bounds on first complete line */
+    p = buffer;
+    q = memchr (p, '\n', Nbyte);
+    if (q == NULL) Shutdown ("incomplete line at end of file\n");
+    offset = p - buffer; // offset within this scan
+
+    Ntstars = 0;
+    NTSTARS = 10000;
+    ALLOCATE (tstars, TMStars, NTSTARS);
+
+    Rmin = 360.0;
+    Rmax =   0.0;
+    Dmin = +90.0;
+    Dmax = -90.0;
+
+    // scan through entire buffer for star coords
+    while (1) {
+      get2mass_coords (p, &tstars[Ntstars].R, &tstars[Ntstars].D, Nbyte - offset);
+      tstars[Ntstars].offset = offset; // offset within scan
+      tstars[Ntstars].flag = FALSE;
+
+      if (VERBOSE) {
+	Rmin = MIN (Rmin, tstars[Ntstars].R);
+	Rmax = MAX (Rmax, tstars[Ntstars].R);
+	Dmin = MIN (Dmin, tstars[Ntstars].D);
+	Dmax = MAX (Dmax, tstars[Ntstars].D);
+      }
+
+      Ntstars ++;
+      CHECK_REALLOCATE (tstars, TMStars, NTSTARS, Ntstars, 10000);
+
+      /* start of the next line */
+      p = q + 1;
+      offset = p - buffer; // offset within this scan
+      if (offset == Nbyte) {
+	// last line in buffer is a complete line
+	Nextra = 0;
+	break;
+      }
+      /* end of the next line */
+      q = memchr (p, '\n', Nbyte - offset);
+      if (q == NULL) {
+	// last, incomplete line in buffer
+	Nextra = Nbyte - offset;
+	break;
+      } 
+    }
+    if (VERBOSE) fprintf (stderr, "scan %d stars (%10.6f - %10.6f, %10.6f - %10.6f) .. ", Ntstars, Rmin, Rmax, Dmin, Dmax);
+
+    // sort the tstars by RA
+    get2mass_sortStars (tstars, Ntstars);
+
+    // scan through the stars, loading the containing catalogs
+    // skip through table for unsaved stars
+    for (i = 0; i < Ntstars; i++) {
+      if (tstars[i].flag) continue;
+
+      // scan forward until we read the UserPatch
+      if (tstars[i].R < UserPatch.Rmin) continue;
+      if (tstars[i].R > UserPatch.Rmax) break;
+      if (tstars[i].D < UserPatch.Dmin) continue;
+      if (tstars[i].D > UserPatch.Dmax) continue;
+
+      // collect array of (Stars *) stars in a new output catalog
+      Nstars = 0;
+      NSTARS = 3000;
+      ALLOCATE (stars, Stars *, NSTARS);
+
+      // identify the relevant catalog
+      skylist = SkyRegionByPoint (skytable, -1, tstars[i].R, tstars[i].D);
+      region = skylist[0].regions[0];
+      if (DEBUG) fprintf (stderr, "writing to %s\n", skylist[0].filename[0]);
+
+      for (j = i; j < Ntstars; j++) {
+	if (tstars[j].flag) continue;
+
+	// check if in skyregion
+	if (tstars[j].R < region[0].Rmin) continue;
+	if (tstars[j].R > region[0].Rmax) break;
+	if (tstars[j].D < region[0].Dmin) continue;
+	if (tstars[j].D > region[0].Dmax) continue;
+	  
+	// check if in UserPatch
+	if (tstars[j].R < UserPatch.Rmin) continue;
+	if (tstars[j].R > UserPatch.Rmax) break;
+	if (tstars[j].D < UserPatch.Dmin) continue;
+	if (tstars[j].D > UserPatch.Dmax) continue;
+	  
+	offset = tstars[j].offset;
+
+	ALLOCATE (stars[Nstars+0], Stars, 1);
+	ALLOCATE (stars[Nstars+1], Stars, 1);
+	ALLOCATE (stars[Nstars+2], Stars, 1);
+
+	stars[Nstars+0][0].R = tstars[j].R;
+	stars[Nstars+0][0].D = tstars[j].D;
+	stars[Nstars+1][0].R = tstars[j].R;
+	stars[Nstars+1][0].D = tstars[j].D;
+	stars[Nstars+2][0].R = tstars[j].R;
+	stars[Nstars+2][0].D = tstars[j].D;
+	get2mass_3star_full (&stars[Nstars], &buffer[offset], Nbyte - offset);
+	// get2mass_star (&stars[Nstars], &buffer[offset], Nbyte - offset);
+
+	tstars[j].flag = TRUE;
+
+	Nstars += 3;
+	if (Nstars >= NSTARS - 3) {
+	  NSTARS += 3000;
+	  REALLOCATE (stars, Stars *, NSTARS);
+	}
+      }
+
+      if (!Nstars) {
+	free (stars);
+	continue;
+      }
+
+      if (DEBUG) fprintf (stderr, "selected %d stars (%10.6f - %10.6f, %10.6f - %10.6f\n", Nstars, 
+			  region[0].Rmin, region[0].Rmax, region[0].Dmin, region[0].Dmax);
+
+      if (1) {
+	verbose = VERBOSE;
+	VERBOSE = FALSE;
+
+	// now we have all of the loaded stars in this catalog
+	catalog.filename = skylist[0].filename[0];
+	catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+	catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+	catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+	// an error exit status here is a significant error
+	if (!dvo_catalog_open (&catalog, skylist[0].regions[0], VERBOSE, "w")) {
+	  fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+	  exit (2);
+	}
+
+	// assume no input star matches an existing star 
+	// simply add to the existing table
+	// load2mass_catalog (&catalog, stars, Nstars);
+	find_matches_refstars (skylist[0].regions[0], stars, Nstars, &catalog, options);
+	// load2mass_catalog (&catalog, stars, Nstars);
+
+	dvo_catalog_save (&catalog, VERBOSE);
+	dvo_catalog_unlock (&catalog);
+	dvo_catalog_free (&catalog);
+	// free (catalog.filename);
+	// XXX don't free this! it points to an element of the skytable
+      }
+
+      SkyListFree (skylist);
+      for (j = 0; j < Nstars; j++) free (stars[j]);
+      free (stars);
+      VERBOSE = verbose;
+    }
+    free (tstars);
+    if (VERBOSE) fprintf (stderr, "done\n");
+
+    // at end, p points at the start of last, partial line
+    if (Nextra) memmove (buffer, p, Nextra);
+  }
+
+  if (VERBOSE) fprintf (stderr, "\n");
+  
+  fclose (f);
+  free (buffer);
+  return (TRUE);
+}
+
+/*
+  for each 2mass file:
+  for each data block
+  generate a table of: R, D, byte, flag
+  for each unsaved star
+  find containing catalog
+  load catalog
+  find all contained stars
+  add to catalog
+  save catalog 
+  mark all contained stars
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load2mass_catalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load2mass_catalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load2mass_catalog.c	(revision 22322)
@@ -0,0 +1,88 @@
+# include "addstar.h"
+
+int load2mass_catalog (Catalog *catalog, Stars *stars, int Nstars) {
+
+  int i, j, Nsec, Nmeas, Nave, NMEAS, NAVE;
+
+  Nsec = GetPhotcodeNsecfilt ();
+  Nave = catalog[0].Naverage;
+  Nmeas = catalog[0].Nmeasure;
+   
+  NAVE = Nave + 100;
+  NMEAS = Nmeas + 100;
+  REALLOCATE (catalog[0].average, Average, NAVE);
+  REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*Nsec);
+  REALLOCATE (catalog[0].measure, Measure, NMEAS);
+
+  for (i = 0; i < Nstars; i+=3) {
+
+    // construct an average object for this object
+    // XXX for now, the output objects will have limited astrometric interpretation...
+    // XXX every 3 stars represents 3 measurements and 1 average
+    catalog[0].average[Nave].R     = stars[i].R;
+    catalog[0].average[Nave].D     = stars[i].D;
+    catalog[0].average[Nave].dR    = 0;
+    catalog[0].average[Nave].dD    = 0;
+    catalog[0].average[Nave].uR    = 0;
+    catalog[0].average[Nave].uD    = 0;
+    catalog[0].average[Nave].duR   = 0;
+    catalog[0].average[Nave].duD   = 0;
+    catalog[0].average[Nave].P     = 0;
+    catalog[0].average[Nave].dP    = 0;
+
+    // XXX for now, set the average mag data to NULL
+    catalog[0].average[Nave].Nmeasure  	   = 0;
+    catalog[0].average[Nave].Nmissing  	   = 0;
+    catalog[0].average[Nave].Xp        	   = NAN_S_SHORT;
+    catalog[0].average[Nave].measureOffset = Nmeas;
+    catalog[0].average[Nave].missingOffset = -1;
+    catalog[0].average[Nave].code          = 0;
+
+    for (j = 0; j < Nsec; j++) {
+      catalog[0].secfilt[Nave*Nsec+j].M  = NAN;
+      catalog[0].secfilt[Nave*Nsec+j].dM = NAN;
+      catalog[0].secfilt[Nave*Nsec+j].Xm = NAN_S_SHORT;
+    }
+
+    // we now have the min chisq row. use this to supply the other filter values....
+    for (j = 0; j < 3; j++) {
+      catalog[0].measure[Nmeas].dR        = 0.0;
+      catalog[0].measure[Nmeas].dD        = 0.0;
+      catalog[0].measure[Nmeas].Xccd      = stars[i+j].X;
+      catalog[0].measure[Nmeas].Yccd      = stars[i+j].Y;
+      catalog[0].measure[Nmeas].dXccd     = 0.0;
+      catalog[0].measure[Nmeas].dYccd     = 0.0;
+      catalog[0].measure[Nmeas].M         = stars[i+j].M;
+      catalog[0].measure[Nmeas].dM        = stars[i+j].dM;
+      catalog[0].measure[Nmeas].Mcal      = 0;
+      catalog[0].measure[Nmeas].dMcal     = stars[i+j].dMcal;
+      catalog[0].measure[Nmeas].t         = stars[i+j].t;
+      catalog[0].measure[Nmeas].averef    = Nave;
+      catalog[0].measure[Nmeas].photcode  = stars[i+j].code;
+      catalog[0].measure[Nmeas].dophot    = 0;
+      catalog[0].measure[Nmeas].photFlags = stars[i+j].flags;
+      catalog[0].measure[Nmeas].dbFlags   = 0;
+      catalog[0].measure[Nmeas].dt        = 0xffff;
+					  
+      catalog[0].measure[Nmeas].airmass   = 0;
+      catalog[0].measure[Nmeas].FWx       = stars[i+j].fx;
+      catalog[0].measure[Nmeas].FWy       = stars[i+j].fy;
+      catalog[0].measure[Nmeas].theta     = stars[i+j].df;
+
+      catalog[0].average[Nave].Nmeasure++;
+      Nmeas ++;
+      CHECK_REALLOCATE (catalog[0].measure, Measure, NMEAS, Nmeas, 100);
+    }
+
+    Nave ++;
+    if (Nave >= NAVE) {
+      NAVE += 100;
+      REALLOCATE (catalog[0].average, Average, NAVE);
+      REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*Nsec);
+    }
+  }
+  catalog[0].Naverage = Nave;
+  catalog[0].Nmeasure = Nmeas;
+  catalog[0].Nsecf_mem = Nave*Nsec;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load_subpix.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load_subpix.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/load_subpix.c	(revision 22322)
@@ -0,0 +1,51 @@
+# include "addstar.h"
+
+typedef struct {
+  double Amp;
+  double Phase;
+  double dM;
+} SubPixFix;
+
+static int Nsubpix;
+static SubPixFix *Subpix;
+
+void load_subpix () {
+
+  int i;
+  FILE *f;
+
+  Nsubpix = 40;
+  ALLOCATE (Subpix, SubPixFix, Nsubpix);
+
+  f = fopen (SubpixDatafile, "r");
+  if (f == NULL) Shutdown ("can't load subpix datafile %s", SubpixDatafile);
+
+  for (i = 0; i < Nsubpix; i++) {
+    fscanf (f, "%*s %*s %lf %lf %lf %*s\n", 
+	    &Subpix[i].Amp, &Subpix[i].Phase, &Subpix[i].dM);
+  }
+  fclose (f);
+
+}
+
+double get_subpix (double x, double y) {
+
+  int bin;
+  double dy, dM;
+
+  dy = y - (int)(y);
+  bin = 5 * (int)(x/100) + (int)(y/100);
+  dM = Subpix[bin].Amp*sin(2*3.14159*dy + Subpix[bin].Phase);
+  return (dM);
+}
+
+double scat_subpix (double x, double y) {
+
+  int bin;
+  double dy, dM;
+
+  dy = y - (int)(y);
+  bin = 5 * (int)(x/100) + (int)(y/100);
+  dM = Subpix[bin].dM;
+  return (dM);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/mkacc-2mass.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/mkacc-2mass.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/mkacc-2mass.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "addstar.h"
+# include "2mass.h"
+
+int main (int argc, char **argv) {
+
+  int i, N, Nrefcat;
+  Stars *refcat;
+  SkyRegion *regions, accregion;
+
+  ConfigInit (&argc, argv);
+
+  /* override any header PHOTCODE values */
+  thiscode = NULL;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    thiscode = GetPhotcodebyName (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: mkacc.2mass (file)\n");
+    exit (2);
+  }
+
+  NAMED_PHOTCODE (TM_J, "2MASS_J");
+  NAMED_PHOTCODE (TM_H, "2MASS_H");
+  NAMED_PHOTCODE (TM_K, "2MASS_K");
+  if (thiscode == NULL) Shutdown ("photcode not specified");
+  if (thiscode[0].code == TM_J) goto good_code;
+  if (thiscode[0].code == TM_H) goto good_code;
+  if (thiscode[0].code == TM_K) goto good_code;
+  Shutdown ("2MASS photcode not specified");
+
+good_code:
+  UserPatch.RAmin = 0;
+  UserPatch.RAmax = 360;
+  UserPatch.DECmin = -90;
+  UserPatch.DECmax = +90;
+
+  ALLOCATE (regions, SkyRegion, 1);
+  strcpy (regions[0].filename, argv[1]);
+  regions[0].RAmin = 0;
+  regions[0].RAmax = 360;
+  regions[0].DECmin = -90;
+  regions[0].DECmax = +90;
+
+  refcat = get2mass_AS_data (regions, &UserPatch, &Nrefcat);
+
+  /* find upper and lower file limits in RA and DEC */
+
+  strcpy (accregion.filename, regions[0].filename);
+  accregion.RAmin  = 360;
+  accregion.RAmax  =   0;
+  accregion.DECmin = +90;
+  accregion.DECmax = -90;
+
+  for (i = 0; i < Nrefcat; i++) {
+    accregion.RAmin  = MIN (refcat[i].R, accregion.RAmin);
+    accregion.RAmax  = MAX (refcat[i].R, accregion.RAmax);
+    accregion.DECmin = MIN (refcat[i].D, accregion.DECmin);
+    accregion.DECmax = MAX (refcat[i].D, accregion.DECmax);
+  }
+
+  fprintf (stderr, "%s %10.6f %10.6f  %10.6f %10.6f  %d\n", 
+	   accregion.filename, accregion.RAmin/15.0, accregion.RAmax/15.0, accregion.DECmin, accregion.DECmax, Nrefcat);
+
+  exit (0);
+}
+
+/* XXX update this function to create an additional accelerator file for each 2mass file
+   each file: one row per DEC band
+   each row: Rmin, Rmax, Dmin, Dmax, Nbyte(i)
+   where Nbyte(i) = byte for each Rmin + i*30 deg transition
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/opening_angle.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/opening_angle.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/opening_angle.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "addstar.h"
+
+double opening_angle (double x1, double y1, double x2, double y2, double x3, double y3) {
+
+  double dx1, dy1, dx2, dy2, ct, st, theta;
+
+  dx1 = x1 - x2;
+  dy1 = y1 - y2;
+  
+  dx2 = x3 - x2;
+  dy2 = y3 - y2;
+  
+  ct = (dx1*dx2 + dy1*dy2);
+  st = (dx1*dy2 - dx2*dy1);
+
+  theta = atan2 (st, ct);
+
+  return (theta);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/parse_time.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/parse_time.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/parse_time.c	(revision 22322)
@@ -0,0 +1,146 @@
+# include "addstar.h"
+
+int parse_time (Header *header) {
+
+  double jd;
+  int Ny, Nf, mode;
+  int Nsec, hour, min, sec, year, month, day;
+  char *py, *pm, *pd, *c;
+  char line[256];
+
+  /* we want to find JD or MJD to get Nsec (seconds since 01/01/1970) */
+
+  /* try JD first */
+  if (strcasecmp (JDKeyword, "NONE")) {
+    uppercase (JDKeyword);
+    if (!gfits_scan (header, JDKeyword, "%lf", 1, &jd)) {
+      fprintf (stderr, "ERROR: missing JD Keyword %s\n", JDKeyword);
+      exit (1);
+    }
+    Nsec = (jd - 2440587.5)*86400;
+    return (Nsec);
+  }
+
+  /* try MJD next */
+  if (strcasecmp (MJDKeyword, "NONE")) {
+    uppercase (MJDKeyword);
+    if (!gfits_scan (header, MJDKeyword, "%lf", 1, &jd)) {
+      fprintf (stderr, "ERROR: missing MJD Keyword %s\n", MJDKeyword);
+      exit (1);
+    }
+    Nsec = (jd - 40587.0)*86400;
+    return (Nsec);
+  }
+    
+  if (!strcasecmp (UTKeyword, "NONE")) {
+      fprintf (stderr, "ERROR: no valid Date/Time keywords\n");
+      exit (1);
+  }
+  if (!strcasecmp (DateKeyword, "NONE")) {
+      fprintf (stderr, "ERROR: no valid Date/Time keywords\n");
+      exit (1);
+  }
+  if (!strcasecmp (DateMode, "NONE")) {
+      fprintf (stderr, "ERROR: no valid Date/Time keywords\n");
+      exit (1);
+  }
+
+  /* get UT and DATE */
+  uppercase (UTKeyword);
+  if (!gfits_scan (header, UTKeyword, "%s", 1, line)) {
+      fprintf (stderr, "ERROR: missing UT Keyword %s\n", UTKeyword);
+      exit (1);
+    }
+  /* remove ':' characters */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  sscanf (line, "%d %d %d", &hour, &min, &sec);
+
+  /* parse mode line */
+  uppercase (DateMode);
+  for (Ny = 0, c = strchr (DateMode, 'Y'); c != (char *) NULL; c = strchr (c + 1, 'Y'), Ny++);
+  if ((Ny != 2) && (Ny != 4)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  py = strchr (DateMode, 'Y');
+  pm = strchr (DateMode, 'M');
+  pd = strchr (DateMode, 'D');
+  if ((py == (char *) NULL) || (pm == (char *) NULL) || (pd == (char *) NULL)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  if ((py > pm) && (py < pd)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  if ((py > pd) && (py < pm)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  mode = 0;
+  if ((py < pm) && (pm < pd)) { mode = 1; }  /* yyyy-mm-dd */
+  if ((py < pm) && (pm > pd)) { mode = 2; }  /* yyyy-dd-mm */
+  if ((py > pm) && (pm < pd)) { mode = 3; }  /* mm-dd-yyyy */
+  if ((py > pm) && (pm > pd)) { mode = 4; }  /* dd-mm-yyyy */
+  if (!mode) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+
+  /* parse date entry */
+  uppercase (DateKeyword);
+  if (!gfits_scan (header, DateKeyword, "%s",  1, line)) {
+    fprintf (stderr, "ERROR: missing DATE Keyword %s\n", DateKeyword);
+    exit (1);
+  }
+  /* remove possible separators: ':', '/' '.', '-' */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  for (c = strchr (line, 0x2f); c != (char *) NULL; c = strchr (line, 0x2f)) { *c = ' '; }
+  for (c = strchr (line, 0x2e); c != (char *) NULL; c = strchr (line, 0x2e)) { *c = ' '; }
+  for (c = strchr (line, 0x2d); c != (char *) NULL; c = strchr (line, 0x2d)) { *c = ' '; }
+
+  Nf = 0;
+  switch (mode) {
+  case 1:
+    Nf = sscanf (line, "%d %d %d", &year, &month, &day);
+    break;
+  case 2:
+    Nf = sscanf (line, "%d %d %d", &year, &day, &month);
+    break;
+  case 3:
+    Nf = sscanf (line, "%d %d %d", &month, &day, &year);
+    break;
+  case 4:
+    Nf = sscanf (line, "%d %d %d", &day, &month, &year);
+    break;
+  }
+  if (Nf != 3) {
+    fprintf (stderr, "error in date entry (%s) or DATE-MODE format (%s)\n", line, DateMode);
+    exit (1);
+  }
+
+  if (year > 1000) {
+    if (Ny == 2) {
+      fprintf (stderr, "warning: mode line claims 2 digit year, but 4 digit year found\n");
+    }
+  } else {
+    if (Ny == 4) {
+      fprintf (stderr, "warning: mode line claims 4 digit year, but 2 digit year found\n");
+    }
+    if (year < 50) year += 100;
+    year += 1900;
+  }    
+
+  /* this should probably use localtime */
+
+  /* convert yy.mm.dd hh.mm.ss to Nsec since 1970 (jd = 2440587.5) */
+  /* note that in this section, tm_mon has range 1-12, unlike for gmtime () */
+  jd = day - 32075 + (int)(1461*(year + 4800 + (int)(((month)-14)/12))/4)
+    + (int)(367*((month) - 2 - (int)(((month) - 14)/12)*12)/12)
+    - (int)(3*(int)((year + 4900 + (int)(((month) - 14)/12))/100)/4) - 0.5;
+  /* jd is the julian day of the whole day only not the time */
+  Nsec = (jd - 2440587.5)*86400 + 3600.0*hour + min*60.0 + sec;
+  
+  return (Nsec);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/replace_match.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/replace_match.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/replace_match.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "addstar.h"
+
+int replace_match (Average *average, Measure *measure, Stars *star) {
+
+  int i;
+
+  /* search for entry and replace values M, dM, R, D */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    if (measure[i].photcode != star[0].code) continue;
+    measure[i].dR = 3600.0*(average[0].R - star[0].R);
+    measure[i].dD = 3600.0*(average[0].D - star[0].D);
+    measure[i].M  = star[0].M;
+    measure[i].dM = star[0].dM;
+    star[0].found = average[0].measureOffset + i;
+    return (TRUE);
+  }
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/resort_catalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/resort_catalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/resort_catalog.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "addstar.h"
+
+void resort_catalog (Catalog *catalog) {
+
+  int *next_meas;
+  int Naves, Nmeas;
+  double dtime;
+  struct timeval start, stop;
+
+  if (catalog[0].sorted == TRUE) return;
+
+  gettimeofday (&start, NULL);
+
+  /* internal counters */
+  Nmeas = catalog[0].Nmeasure;
+  Naves = catalog[0].Naverage;
+  
+  /* set up pointers for linked list of measure, missing */
+  next_meas = build_measure_links (catalog[0].average, Naves, catalog[0].measure, Nmeas);
+
+  catalog[0].sorted = TRUE;
+  catalog[0].measure = sort_measure (catalog[0].average, Naves, catalog[0].measure, Nmeas, next_meas);
+
+  gettimeofday (&stop, NULL);
+  dtime = DTIME (stop, start);
+  fprintf (stderr, "  match time %9.4f sec for %7d measures, %6d average\n", dtime, Nmeas, Naves);
+
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/sedstar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/sedstar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/sedstar.c	(revision 22322)
@@ -0,0 +1,85 @@
+# include "sedstar.h"
+
+int main (int argc, char **argv) {
+
+  char *root, *ext, tmp;
+  int i, Nbytes;
+  SkyList *skylist;
+  SkyTable *sky;
+  AddstarClientOptions options;
+  Catalog incatalog, outcatalog;
+  SEDtable *sedtable;
+
+  // need to construct these options with args_load2mass...
+  options = ConfigInit (&argc, argv);
+  options = args_sedstar (argc, argv, options);
+
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // select regions of interest
+  skylist = SkyListByPatch (sky, -1, &UserPatch);
+
+  // load the SED data table
+  sedtable = SEDtableLoad (argv[1]);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    incatalog.filename = skylist[0].filename[i];
+    incatalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&incatalog, skylist[0].regions[i], VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", incatalog.filename);
+      exit (2);
+    }
+
+    // Naves_disk == 0 implies an empty catalog file
+    if ((incatalog.Naves_disk == 0) && options.only_match) {
+      if (VERBOSE) fprintf (stderr, "skipping empty region\n");
+      dvo_catalog_unlock (&incatalog);
+      dvo_catalog_free (&incatalog);
+      continue;
+    }
+
+    // create output catalog filename
+    root = strstr (incatalog.filename, CATDIR);
+    if (root == NULL) Shutdown ("error with input catalog name");
+    ext = incatalog.filename + strlen(CATDIR);
+    while (*ext == '/') ext++;
+    Nbytes = snprintf (&tmp, 0, "%s/%s", argv[2], ext);
+    ALLOCATE (outcatalog.filename, char, Nbytes + 1);
+    snprintf (outcatalog.filename, Nbytes + 1, "%s/%s", argv[2], ext);
+
+    outcatalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    outcatalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    outcatalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    outcatalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&outcatalog, skylist[0].regions[i], VERBOSE, "w")) {
+      Shutdown ("ERROR: failure to open/create catalog file %s\n", outcatalog.filename);
+    }
+
+    SEDfitCatalog (&outcatalog, &incatalog, sedtable);
+    
+    dvo_catalog_save (&outcatalog, VERBOSE);
+    dvo_catalog_unlock (&outcatalog);
+    dvo_catalog_free (&outcatalog);
+
+    dvo_catalog_unlock (&incatalog);
+    dvo_catalog_free (&incatalog);
+    // XXX free filename or not?
+  }
+  exit (0);
+}  
+
+/**  sedstar: 
+
+* load in the SED data table
+* load in the catalog file (by region)
+* fit stars in the catalog file
+* load output catalog file?
+* construct output catalog file (optional)
+* save output catalog file
+
+**/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/sky_tessalation.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/sky_tessalation.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/sky_tessalation.c	(revision 22322)
@@ -0,0 +1,676 @@
+# include "skycells.h"
+# include "assert.h"
+# define iSWAP(X,Y) {int tmp=(X); (X) = (Y); (Y) = tmp;}
+# define DVO_IMAGE_NAME_LEN 128
+
+// we use a static refcoords structure to avoid multiple alloc / init steps
+static Coords *refcoords = NULL;
+
+int sky_tessalation (FITS_DB *db, int level, int Nmax, int mode, double scale) {
+
+  sky_tessalation_init (scale);
+
+  if (mode == SQUARES) {
+    sky_tessalation_squares (db, level, Nmax);
+    return TRUE;
+  }
+
+  if (mode == TRIANGLES) {
+    sky_tessalation_triangles (db, level, Nmax);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+int sky_tessalation_triangles (FITS_DB *db, int level, int Nmax) {
+
+  int i, j, Ndigit, Ntriangles, Nbase, Ntotal, Ltop, Nout, Nimages;
+  double Ntop, fLtop;
+  SkyTriangle *base, *tri, *new;
+  Image *image;
+  char format[16];
+
+  // generate the initial base set
+  base = sky_base_triangles (&Nbase);
+
+  sky_base_rotation (base, Nbase);
+
+  // how many triangles total for this level?
+  Ntotal = Nbase*pow(4.0, level);
+  Ndigit = (int)(log10(Ntotal)) + 1 ;
+  snprintf (format, 16, "skytri.%%0%dd", Ndigit);
+
+  // to what depth do we need to go to have only Nmax foreach subcell?
+  Ntop = Ntotal / Nmax;
+  if (Ntop > Nbase) {
+    fLtop = log10(Ntop / Nbase) / log10(4.0);
+    if (fLtop > (int)(fLtop)) {
+      Ltop = fLtop + 1;
+    } else {
+      Ltop = fLtop;
+    }
+  } else {
+    Ltop = 0;
+  }
+
+  // subdivide the base set to Ltop level
+  for (i = 0; i < Ltop; i++) {
+    new = sky_divide_triangles (base, &Nbase);
+    free (base);
+    base = new;
+  }
+
+  // for each base triangle, subdivide the rest of the way and save
+  Nout = 0;
+  for (i = 0; i < Nbase; i++) {
+    ALLOCATE (tri, SkyTriangle, 1);
+    tri[0] = base[i];
+    Ntriangles = 1;
+    for (j = Ltop; j < level; j++) {
+      new = sky_divide_triangles (tri, &Ntriangles);
+      free (tri);
+      tri = new;
+    }
+
+    // convert the SkyTriangles to Image
+    ALLOCATE (image, Image, Ntriangles);
+    for (j = 0; j < Ntriangles; j++) {
+      sky_triangle_to_image (&image[j], &tri[j]);
+      snprintf (image[j].name, DVO_IMAGE_NAME_LEN, format, Nout);
+      Nout++;
+    }  
+    Nimages = Ntriangles;
+
+    /* add the new images and save */
+    dvo_image_addrows (db, image, Nimages);
+    SetProtect (TRUE);
+    dvo_image_update (db, VERBOSE);
+    SetProtect (FALSE);
+    dvo_image_clear_vtable (db);
+
+    free (image);
+    free (tri);
+  }
+  return (TRUE);
+}
+
+int sky_tessalation_squares (FITS_DB *db, int level, int Nmax) {
+
+  int i, j, Nname, Ndigit, Ntriangles, Nbase, Nimage, Ntotal, Ntop, Ltop, Nsubset, Nx, Ny;
+  double fLtop;
+  SkyTriangle *base, *tri, *new;
+  SkyRectangle *rectangle, *subset;
+  Image *image;
+  char format[16];
+
+  Nx = NX_SUB;
+  Ny = NY_SUB;
+
+  // generate the initial base set
+  base = sky_base_triangles (&Nbase);
+
+  sky_base_rotation (base, Nbase);
+
+  // how many total cells for this level (multiply by subdivisions, if used)?
+  Ntotal = Nbase*pow(4.0, level);
+  Ndigit = (int)(log10(Ntotal)) + 1 ;
+  snprintf (format, 16, "skycell.%%0%dd", Ndigit);
+
+  // to what depth do we need to go to have only Nmax foreach subcell?
+  Ntop = Ntotal / (Nmax*Nx*Ny) ;
+  if (Ntop > Nbase) {
+    fLtop = log10(Ntotal / (double)(Ntop * Nbase)) / log10(4.0);
+    if (fLtop > (int)(fLtop)) {
+      Ltop = fLtop + 1;
+    } else {
+      Ltop = fLtop;
+    }
+  } else {
+    Ltop = 0;
+  }
+
+  // subdivide the base set to Ltop level
+  for (i = 0; i < Ltop; i++) {
+    new = sky_divide_triangles (base, &Nbase);
+    free (base);
+    base = new;
+  }
+
+  // for each base triangle, subdivide the rest of the way and save
+  Nname = 0;
+  for (i = 0; i < Nbase; i++) {
+    ALLOCATE (tri, SkyTriangle, 1);
+    tri[0] = base[i];
+    Ntriangles = 1;
+    for (j = Ltop; j < level; j++) {
+      new = sky_divide_triangles (tri, &Ntriangles);
+      free (tri);
+      tri = new;
+    }
+
+    // convert the SkyTriangles to SkyRectangles
+    ALLOCATE (rectangle, SkyRectangle, Ntriangles);
+    for (j = 0; j < Ntriangles; j++) {
+      sky_triangle_to_rectangle (&rectangle[j], &tri[j]);
+    }  
+
+    // drop the appropriate subset
+    ALLOCATE (subset, SkyRectangle, Ntriangles);
+    for (j = Nsubset = 0; j < Ntriangles; j++) {
+      if (!strcmp(rectangle[j].coords.ctype, "DROP")) continue;
+      memcpy (&subset[Nsubset], &rectangle[j], sizeof(SkyRectangle));
+      snprintf (subset[Nsubset].name, DVO_IMAGE_NAME_LEN, format, Nname);
+      Nname++;
+      Nsubset++;
+    }  
+    free (rectangle);
+
+    // subdivide each image (Nx x Ny subcells)
+    Nimage = Nx*Ny*Nsubset;
+    ALLOCATE (image, Image, Nimage);
+    for (j = 0; j < Nsubset; j++) {
+      // convert the SkyRectangles to Images for output
+      sky_subdivide_image (&image[j*Nx*Ny], &subset[j], Nx, Ny);
+    }
+
+    /* add the new images and save */
+    dvo_image_addrows (db, image, Nimage);
+    SetProtect (TRUE);
+    dvo_image_update (db, VERBOSE);
+    SetProtect (FALSE);
+    dvo_image_clear_vtable (db);
+
+    free (subset);
+    free (image);
+    free (tri);
+  }
+  return (TRUE);
+}
+
+// an allocated image is supplied, we fill in the values
+int sky_triangle_to_image (Image *image, SkyTriangle *triangle) {
+
+  int i, NX, NY;
+  double xv[3], yv[3];	      // coordinates of the vertex in the reference projection 
+  double scale;
+  double Xmin, Xmax, Ymin, Ymax;
+
+  // calculate the triangle coordinates in r,d
+  sky_triangle_coords (triangle);
+
+  // we will project to the triangle center position
+  refcoords[0].crval1 = triangle[0].r;
+  refcoords[0].crval2 = triangle[0].d;
+
+  // project the vertices to this projection, find bounds
+  Xmin = Xmax = Ymin = Ymax = 0.0; // 0,0 is center of triangle
+  for (i = 0; i < 3; i++) {
+    RD_to_XY (&xv[i], &yv[i], triangle[0].rv[i], triangle[0].dv[i], refcoords);
+    Xmin = MIN (xv[i], Xmin);
+    Xmax = MAX (xv[i], Xmax);
+    Ymin = MIN (yv[i], Ymin);
+    Ymax = MAX (yv[i], Ymax);
+  }
+
+  // set NX, NY to the roughly full-width box (centered at 0,0)
+  NX = Xmax - Xmin;
+  NY = Ymax - Ymin;
+
+  memset (image, 0, sizeof(Image));
+  image[0].coords = *refcoords;
+  image[0].coords.pc1_1 = image[0].coords.pc2_2 = 1.0;
+  image[0].coords.pc1_2 = image[0].coords.pc2_1 = 0.0;
+
+  // We cannot use the correction below if we want to set cdelt1,2 to our desired pixel scale
+  // use this test to raise an error (60000 x 60000 is a very large image...)
+  strcpy (image[0].coords.ctype, "TRI--TAN");
+  scale = 0;
+  for (i = 0; i < 3; i++) {
+    scale = MAX (abs(xv[i]), scale);
+    scale = MAX (abs(yv[i]), scale);
+  }
+  if (scale > 32000) {
+    scale /= 30000.0;
+    NX /= scale;
+    NY /= scale;
+    image[0].coords.cdelt1 *= scale;
+    image[0].coords.cdelt2 *= scale;
+    for (i = 0; i < 3; i++) {
+      xv[i] /= scale;
+      yv[i] /= scale;
+    }
+  }
+  image[0].NX = NX;
+  image[0].NY = NY;
+
+  image[0].code = 1; // this needs to be set more sensibly
+
+  image[0].Mx   = xv[0];  image[0].My   = yv[0];
+  image[0].Mxxx = xv[1];  image[0].Mxyy = yv[1];
+  image[0].Mxxy = xv[2];  image[0].Myyy = yv[2];
+
+  return (TRUE);
+}
+
+// an allocated image is supplied, we fill in the values
+// we are only keeping ~half of the images
+int sky_triangle_to_rectangle (SkyRectangle *rectangle, SkyTriangle *triangle) {
+
+  int i, parity, peak, b1, b2, NX, NY, right;
+  double xv[3], yv[3];	      // coordinates of the vertex in the reference projection 
+  double xo, yo, xc, yc, xcr, ycr, angle;
+  double dB, dP, s1, s2, r1, r2, dr, dx, dy;
+  double angle_b1, angle_b2, slope;
+
+  // calculate the triangle coordinates in r,d
+  sky_triangle_coords (triangle);
+
+  // we will project to the triangle center position
+  refcoords[0].crval1 = triangle[0].r;
+  refcoords[0].crval2 = triangle[0].d;
+
+  // find the size, rotation, and parity of the image
+  // project the vertices and find the image parity
+  parity = 1;
+  for (i = 0; i < 3; i++) {
+    RD_to_XY (&xv[i], &yv[i], triangle[0].rv[i], triangle[0].dv[i], refcoords);
+    parity *= SIGN(yv[i]);
+  }
+
+  // choose the peak vertex
+  peak = -1;
+  for (i = 0; (peak == -1) && (i < 3); i++) {
+    if (parity == SIGN(yv[i])) {
+      peak = i;
+    }
+  }
+  assert (peak != -1);
+
+  // angle is from the center to the peak corner
+  angle = atan2(parity*xv[peak], parity*yv[peak]); // note that this is x/y not y/x (and in radians)
+
+  // find the base and height
+  b1 = (peak + 1) % 3;
+  b2 = (peak + 2) % 3;
+
+  // angle is from the center to the peak corner
+  angle_b1 = DEG_RAD*atan(yv[b1] / xv[b1]);
+  angle_b2 = DEG_RAD*atan(yv[b2] / xv[b2]);
+
+  // if one of the base-center angles is very small, the parity is marginal.  Use additional
+  // information to choose the parity.  note that both angle_b1 and angle_b2 cannot be close to
+  // zero.
+  if (fabs(angle_b1) < 10.0) {
+    right = (xv[b1] > 0);     // pointing left or right?
+    slope = (xv[peak] -  xv[b2]) / (yv[peak] - yv[b2]);
+    if ( right && (slope >= 0.0)) parity = +1;
+    if ( right && (slope <  0.0)) parity = -1;
+    if (!right && (slope <= 0.0)) parity = +1;
+    if (!right && (slope >  0.0)) parity = -1;
+    if (parity > 0) {
+      if (yv[peak] < yv[b2]) iSWAP(peak, b2); // require peak to be top (bottom) point
+    } else {
+      if (yv[peak] > yv[b2]) iSWAP(peak, b2); // require peak to be top (bottom) point
+    }
+    angle = atan2(parity*xv[peak], parity*yv[peak]); // note that this is x/y not y/x (and in radians)
+  }
+  if (fabs(angle_b2) < 10.0) {
+    right = (xv[b2] > 0);     // pointing left or right?
+    slope = (xv[peak] - xv[b1]) / (yv[peak] - yv[b1]); // tilt of opposite line
+    if ( right && (slope >= 0.0)) parity = +1;
+    if ( right && (slope <  0.0)) parity = -1;
+    if (!right && (slope <= 0.0)) parity = +1;
+    if (!right && (slope >  0.0)) parity = -1;
+    if (parity > 0) {
+      if (yv[peak] < yv[b1]) iSWAP(peak, b1); // require peak to be top (bottom) point
+    } else {
+      if (yv[peak] > yv[b1]) iSWAP(peak, b1); // require peak to be top (bottom) point
+    }
+    angle = atan2(parity*xv[peak], parity*yv[peak]); // note that this is x/y not y/x (and in radians)
+  }
+
+  // xo, yo is the center of the baseline
+  xo = 0.5*(xv[b2] + xv[b1]);
+  yo = 0.5*(yv[b2] + yv[b1]);
+
+  // find the max perpendicular distance from the peak
+
+  // dB[b1] == dB[b2] (since xo,yo is the midpoint of [b1] to [b2]
+  dB = hypot(xo      -xv[b1], yo      -yv[b1]);
+  dP = hypot(xv[peak]-xo,     yv[peak] -yo);
+	     
+  // XXX we could just choose the point based on s1 vs s2...
+  s1 = hypot(xv[peak]-xv[b1], yv[peak]-yv[b1]);
+  s2 = hypot(xv[peak]-xv[b2], yv[peak]-yv[b2]);
+
+  r1 = (SQ(s1) - SQ(dB) - SQ(dP)) / (2*dP);
+  r2 = (SQ(s2) - SQ(dB) - SQ(dP)) / (2*dP);
+
+  // dr >= 0
+  dr = MAX (r1, r2);
+
+  dx = -parity*dr*sin(angle);
+  dy = -parity*dr*cos(angle);
+
+  xo += dx;
+  yo += dy;
+
+  // xc, yc is the true image center
+  xc = 0.5*(xv[peak] + xo);
+  yc = 0.5*(yv[peak] + yo);
+
+  // NX,NY are the size of the circumscribed square, expanded by PADDING
+  NX = hypot((xv[b2]-xv[b1]),(yv[b2]-yv[b1])) * (1 + PADDING);
+  NY = hypot((xv[peak]-xo),(yv[peak]-yo)) * (1 + PADDING);
+
+  memset (rectangle, 0, sizeof(SkyRectangle));
+  rectangle[0].coords = *refcoords;
+
+  if (FIX_NS) {
+    rectangle[0].coords.pc1_1 = +1.0;
+    rectangle[0].coords.pc1_2 = +0.0;
+    rectangle[0].coords.pc2_1 = -0.0;
+    rectangle[0].coords.pc2_2 = +1.0;
+    xcr = xc*cos(angle) - yc*sin(angle); 
+    ycr = yc*cos(angle) + xc*sin(angle); 
+  } else {
+    rectangle[0].coords.pc1_1 = +cos(angle);
+    rectangle[0].coords.pc1_2 = +sin(angle);
+    rectangle[0].coords.pc2_1 = -sin(angle);
+    rectangle[0].coords.pc2_2 = +cos(angle);
+    xcr = xc;
+    ycr = yc;
+  }
+  
+  // crpix1,crpix2 is the projection center
+  rectangle[0].coords.crpix1 = 0.5*NX - xcr;
+  rectangle[0].coords.crpix2 = 0.5*NY - ycr;
+
+  // only keep one of the parity rectangles
+  if (((triangle[0].d >= 0) && (parity == +1)) || ((triangle[0].d < 0) && (parity == -1))) {
+    strcpy (rectangle[0].coords.ctype, "DEC--TAN");
+  } else {
+    strcpy (rectangle[0].coords.ctype, "DROP");
+  }
+
+  rectangle[0].NX = NX;
+  rectangle[0].NY = NY;
+  rectangle[0].code = 1; // this needs to be set more sensibly
+
+  return (TRUE);
+}
+
+// an allocated image set is supplied, we fill in the values
+int sky_subdivide_image (Image *output, SkyRectangle *input, int Nx, int Ny) {
+
+  int i, j, N, NX, NY, Ndigit;
+  char format[24];
+
+  NX = input[0].NX/(double)Nx + 0.5;
+  NY = input[0].NY/(double)Ny + 0.5;
+
+  // image[0].NX,NY are unsigned short: abort is we overflow
+  if ((NX > 0xffff) || (NY > 0xffff)) {
+    fprintf (stderr, "error: NX,NY too big for DVO limits; modify pixel scale\n");
+    fprintf (stderr, "NX: %d, NY: %d\n", NX, NY);
+    exit (1);
+  }
+
+  Ndigit = (int)(log10(Nx*Ny)) + 1 ;
+  snprintf (format, 24, "%s.%%0%dd", input[0].name, Ndigit);
+
+  N = 0;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+
+      memset (&output[N], 0, sizeof(Image));
+      memcpy (&output[N].coords, &input[0].coords, sizeof(Coords));
+
+      snprintf (output[N].name, DVO_IMAGE_NAME_LEN, format, N);
+      output[N].NX = NX;
+      output[N].NY = NY;
+      output[N].code = input[0].code;
+
+      output[N].coords.crpix1 = input[0].coords.crpix1 - i*NX;
+      output[N].coords.crpix2 = input[0].coords.crpix2 - j*NY;
+      N++;
+    }
+  }
+  return (TRUE);
+}
+
+int sky_triangle_coords (SkyTriangle *triangle) {
+
+  int i;
+  double r;
+
+  // calculate the triangle center
+  triangle[0].center.x = (triangle[0].vertex[0].x + triangle[0].vertex[1].x + triangle[0].vertex[2].x)/3.0;
+  triangle[0].center.y = (triangle[0].vertex[0].y + triangle[0].vertex[1].y + triangle[0].vertex[2].y)/3.0;
+  triangle[0].center.z = (triangle[0].vertex[0].z + triangle[0].vertex[1].z + triangle[0].vertex[2].z)/3.0;
+
+  // renormalize
+  r = 1.0 / sqrt (SQ(triangle[0].center.x) + SQ(triangle[0].center.y) + SQ(triangle[0].center.z));
+
+  triangle[0].center.x *= r;
+  triangle[0].center.y *= r;
+  triangle[0].center.z *= r;
+
+  triangle[0].d = DEG_RAD * asin(triangle[0].center.z);
+  triangle[0].r = DEG_RAD * atan2(triangle[0].center.y, triangle[0].center.x);
+
+  for (i = 0; i < 3; i++) {
+    triangle[0].dv[i] = DEG_RAD * asin(triangle[0].vertex[i].z);
+    triangle[0].rv[i] = DEG_RAD * atan2(triangle[0].vertex[i].y, triangle[0].vertex[i].x);
+  }
+
+  return TRUE;
+}
+
+// take a list of triangles from one level and return a list of triangles in the next level
+// we are doing basic edge division, always yielding 4x as many new triangles as old;
+SkyTriangle *sky_divide_triangles (SkyTriangle *in, int *ntriangles) {
+
+  int i, j, Ntriangles, Nt;
+  SkyTriangle *out;
+
+  Ntriangles = *ntriangles * 4;
+  ALLOCATE (out, SkyTriangle, Ntriangles);
+
+  Nt = 0;
+  for (i = 0; i < *ntriangles; i++) {
+    for (j = 0; j < 3; j++) {
+      out[4*i + j].vertex[0] = in[i].vertex[j];
+      out[4*i + j].vertex[1] = sky_divide_edge (in[i].vertex[j], in[i].vertex[(j+1)%3]);
+    }
+    for (j = 0; j < 3; j++) {
+      out[4*i + j].vertex[2] = out[4*i + (j+2)%3].vertex[1];
+    }
+    for (j = 0; j < 3; j++) {
+      out[4*i + 3].vertex[j] = out[4*i + j].vertex[1];
+    }
+    Nt += 4;
+  }
+  *ntriangles = Nt;
+  return (out);
+}
+
+// take a list of triangles from one level and return a list of triangles in the next level
+// we are doing basic edge division, always yielding 4x as many new triangles as old;
+Point sky_divide_edge (Point v1, Point v2) {
+
+  double r;
+  Point out;
+
+  out.x = v1.x + v2.x;
+  out.y = v1.y + v2.y;
+  out.z = v1.z + v2.z;
+
+  r = 1.0 / sqrt (SQ(out.x) + SQ(out.y) + SQ(out.z));
+
+  out.x *= r;
+  out.y *= r;
+  out.z *= r;
+
+  return (out);
+}
+
+# define THETA RAD_DEG*26.565
+# define D_PSI RAD_DEG*360.0/5.0
+
+SkyTriangle *sky_base_triangles (int *ntriangles) {
+
+  int i;
+  double ctht, stht, psi;
+  SkyTriangle *tri;
+
+  // generate 0-level triangles
+  ALLOCATE (tri, SkyTriangle, 20);
+
+  for (i = 0; i < 20; i++) {
+    memset (&tri[i], 0, sizeof(SkyTriangle));
+  }
+
+  ctht = cos(THETA);
+  stht = sin(THETA);
+
+  for (i = 0; i < 5; i++) {
+    tri[i].vertex[0].x = +0;
+    tri[i].vertex[0].y = +0;
+    tri[i].vertex[0].z = +1;
+
+    psi = (i + 0.0)*D_PSI;
+    tri[i].vertex[1].x = +ctht*cos(psi);
+    tri[i].vertex[1].y = +ctht*sin(psi);
+    tri[i].vertex[1].z = +stht;
+    
+    psi = (i + 1.0)*D_PSI;
+    tri[i].vertex[2].x = +ctht*cos(psi);
+    tri[i].vertex[2].y = +ctht*sin(psi);
+    tri[i].vertex[2].z = +stht;
+  }    
+  
+  for (i = 5; i < 10; i++) {
+    psi = (i + 0.0)*D_PSI;
+    tri[i].vertex[0].x = +ctht*cos(psi);
+    tri[i].vertex[0].y = +ctht*sin(psi);
+    tri[i].vertex[0].z = +stht;
+    
+    psi = (i + 0.5)*D_PSI;
+    tri[i].vertex[1].x = +ctht*cos(psi);
+    tri[i].vertex[1].y = +ctht*sin(psi);
+    tri[i].vertex[1].z = -stht;
+
+    psi = (i + 1.0)*D_PSI;
+    tri[i].vertex[2].x = +ctht*cos(psi);
+    tri[i].vertex[2].y = +ctht*sin(psi);
+    tri[i].vertex[2].z = +stht;
+  }    
+  
+  for (i = 10; i < 15; i++) {
+    psi = (i + 0.5)*D_PSI;
+    tri[i].vertex[0].x = +ctht*cos(psi);
+    tri[i].vertex[0].y = +ctht*sin(psi);
+    tri[i].vertex[0].z = -stht;
+
+    psi = (i + 1.0)*D_PSI;
+    tri[i].vertex[1].x = +ctht*cos(psi);
+    tri[i].vertex[1].y = +ctht*sin(psi);
+    tri[i].vertex[1].z = +stht;
+    
+    psi = (i + 1.5)*D_PSI;
+    tri[i].vertex[2].x = +ctht*cos(psi);
+    tri[i].vertex[2].y = +ctht*sin(psi);
+    tri[i].vertex[2].z = -stht;
+  }    
+  
+  for (i = 15; i < 20; i++) {
+    psi = (i + 0.5)*D_PSI;
+    tri[i].vertex[1].x = +ctht*cos(psi);
+    tri[i].vertex[1].y = +ctht*sin(psi);
+    tri[i].vertex[1].z = -stht;
+    
+    psi = (i + 1.5)*D_PSI;
+    tri[i].vertex[2].x = +ctht*cos(psi);
+    tri[i].vertex[2].y = +ctht*sin(psi);
+    tri[i].vertex[2].z = -stht;
+
+    tri[i].vertex[0].x = +0;
+    tri[i].vertex[0].y = +0;
+    tri[i].vertex[0].z = -1;
+  }    
+  
+  *ntriangles = 20;
+  return tri;
+}
+
+int sky_base_rotation (SkyTriangle *base, int Nbase) {
+
+  // apply the three euler angles (A, B, C)
+  // XXX for now, just apply A and B
+
+  int i, j, ix;
+  float rot[3][3], v[3];
+
+  rot[0][0] = +cos(EULER_A)*cos(EULER_B);
+  rot[1][0] = +sin(EULER_A)*cos(EULER_B);
+  rot[2][0] = +sin(EULER_B);
+
+  rot[0][1] = -sin(EULER_A);
+  rot[1][1] = +cos(EULER_A);
+  rot[2][1] = +0.0;
+
+  rot[0][2] = -cos(EULER_A)*sin(EULER_B);
+  rot[1][2] = -sin(EULER_A)*sin(EULER_B);
+  rot[2][2] = +cos(EULER_B);
+
+  for (i = 0; i < Nbase; i++) {
+    for (j = 0; j < 3; j++) {
+      for (ix = 0; ix < 3; ix++) {
+	v[ix] = 0.0;
+	v[ix] += base[i].vertex[j].x * rot[0][ix];
+	v[ix] += base[i].vertex[j].y * rot[1][ix];
+	v[ix] += base[i].vertex[j].z * rot[2][ix];
+      }
+      base[i].vertex[j].x = v[0];
+      base[i].vertex[j].y = v[1];
+      base[i].vertex[j].z = v[2];
+    }
+  }
+  return (TRUE);
+}
+
+int sky_tessalation_init (double scale) {
+
+  ALLOCATE (refcoords, Coords, 1);
+  refcoords[0].crval1 = refcoords[0].crval2 = 0.0;
+  refcoords[0].crpix1 = refcoords[0].crpix2 = 0.0;
+  refcoords[0].cdelt1 = refcoords[0].cdelt2 = scale / 3600;
+  refcoords[0].pc1_1 = refcoords[0].pc2_2 = 1.0;
+  refcoords[0].pc1_2 = refcoords[0].pc2_1 = 0.0;
+  refcoords[0].Npolyterms = 0;
+  memset (refcoords[0].polyterms, 0, 14*sizeof(float));
+  strcpy (refcoords[0].ctype, "RA---TAN");
+  return (TRUE);
+}
+
+// free the space used by the current vtable entries
+int dvo_image_clear_vtable (FITS_DB *db) {
+
+  int i;
+
+  // free memory used by the current vtable rows
+  for (i = 0; i < db[0].vtable.Nrow; i++) {
+    free (db[0].vtable.buffer[i]);
+  }
+  REALLOCATE (db[0].vtable.buffer, char *, 1);
+  REALLOCATE (db[0].vtable.row, int, 1);
+  db[0].vtable.Nrow   = 0;
+
+  // reset db[0].theader(NAXIS1) to match Image
+  gfits_modify (&db[0].theader, "NAXIS1", "%d", 1, sizeof(Image));
+  db[0].theader.Naxis[0] = sizeof(Image);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/skycells.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/skycells.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/skycells.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "skycells.h"
+
+int main (int argc, char **argv) {
+
+  int status, level;
+  FITS_DB db;
+
+  SetSignals ();
+  ConfigInit_skycells (&argc, argv);
+  args_skycells (argc, argv);
+  level = atoi (argv[1]);
+  
+  /*** update the image table ***/
+  /* setup image table format and lock */
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  status    = dvo_image_lock (&db, ImageCat, 3600.0, LCK_XCLD);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  /* load or create the image table */
+  if (db.dbstate == LCK_EMPTY) {
+    if (VERBOSE) fprintf (stderr, "can't find %s, creating a new one\n", ImageCat);
+    dvo_image_create (&db, 25.0);
+  } else {
+    if (!dvo_image_load (&db, VERBOSE, FALSE)) {
+      Shutdown ("can't read image catalog %s", db.filename);
+    }
+  }
+
+  // we have to put the database update calls deep down in the sky_tessalation code so we
+  // can write out the skycells in limited-sized chunks.
+  sky_tessalation (&db, level, NMAX, MODE, SCALE);
+
+  dvo_image_unlock (&db);
+  exit (0);
+}
+
+/*
+ * - we start with the image database loaded into memory (db)
+ * - we convert this to an empty vtable 
+ * - add groups of images to the vtable
+ * - we write out the vtable data
+ * - zero-out the saved entries, 
+ */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/update_coords.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/update_coords.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/addstar/src/update_coords.c	(revision 22322)
@@ -0,0 +1,53 @@
+# include "addstar.h"
+
+/* find the average and scatter for R and D - no outlier rejection */
+void update_coords (Average *average, Measure *measure, int *next) {
+
+  int i, m, Npt;
+  double R, D, r, d;
+  double r2, d2, dR2, dD2;
+
+  Npt = r = d = r2 = d2 = 0;
+
+  if (average[0].Nmeasure < 2) return;
+
+  /* find the average & sum-square (does not use reference coordinates) */
+  m = average[0].measureOffset;  /* first measurement of this star */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    if (measure[m].t == 0) {
+      m = next[m];
+      continue;
+    }
+    R = measure[m].dR;
+    D = measure[m].dD;
+    r += R;
+    d += D;
+    r2 += R*R;
+    d2 += D*D;
+    m = next[m];
+    Npt ++;
+  }
+  if (Npt < 1) return;
+
+  /* apply average offset */
+  r = r / Npt;  /* these are corrections in 1/100 arcsec to RA and DEC */
+  d = d / Npt;
+  average[0].R -= r / 3600.0;
+  average[0].D -= d / 3600.0;
+  m = average[0].measureOffset;  /* first measurement of this star */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    measure[m].dR -= r;
+    measure[m].dD -= d;
+    m = next[m];
+  }
+  
+  /* measure scatter, if possible */
+  if (Npt < 2) return;
+
+  dR2 = r2 / Npt - r*r;
+  dD2 = d2 / Npt - d*d;
+  average[0].Xp = sqrt (dD2 + dR2 / SQ(cos(d*RAD_DEG)));
+  /* Xp is scatter in position in hundredths of arcsec */
+
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/Makefile	(revision 22322)
@@ -0,0 +1,43 @@
+default: delstar
+help:
+	@echo "make options: addstar (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/delstar
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+INC	= 	$(HOME)/include
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+delstar: $(BIN)/delstar.$(ARCH)
+install: $(DESTBIN)/delstar
+
+DELSTAR = \
+$(SRC)/delstar.$(ARCH).o          \
+$(SRC)/SetSignals.$(ARCH).o       \
+$(SRC)/ConfigInit.$(ARCH).o 	  \
+$(SRC)/args.$(ARCH).o	          \
+$(SRC)/delete_imagename.$(ARCH).o \
+$(SRC)/delete_imagefile.$(ARCH).o \
+$(SRC)/delete_times.$(ARCH).o 	  \
+$(SRC)/gimages.$(ARCH).o   	  \
+$(SRC)/find_image_db.$(ARCH).o    \
+$(SRC)/find_matches.$(ARCH).o 	  \
+$(SRC)/parse_time.$(ARCH).o       \
+$(SRC)/check_permissions.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o          
+
+OLD = \
+$(SRC)/gregion_image.$(ARCH).o    \
+$(SRC)/gregion_patch.$(ARCH).o    \
+$(SRC)/RegionOps.$(ARCH).o   	  
+
+$(DELSTAR): $(INC)/delstar.h
+$(BIN)/delstar.$(ARCH) : $(DELSTAR)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,20 @@
+
+- delstar 1.6 : 
+  * converted to gfits APIs (forces libfits 1.6)
+  * converted to new DVO APIs (forces libdvo 1.3)
+  * removed unused code
+
+- delstar 1.5 : 2006.03.26
+  * dropped IMAGE_CATALOG from config
+  * convert to dvo_image_lock,unlock
+
+- delstar 1.4 : 2006.01.06
+  * major work to support SkyRegions
+  * cleaned up signed/unsigned inconsistencies
+
+2005.10.20 : delstar.1-3
+
+    This release keeps delstar in sync with addstar v1.3.  Support is
+    added for the different modes and formats, and for the new
+    internal data types (eg, mags, not millimags).
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/include/addstar.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/include/addstar.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/include/addstar.h	(revision 22322)
@@ -0,0 +1,13 @@
+# include <ohana.h>
+# include <dvo.h>
+
+/* global variables set in parameter file */
+char   ImageCat[256];
+char   ImageTemplate[256];
+char   CatTemplate[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+double NSIGMA;
+double ALPHA;
+int    VERBOSE;
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/include/delstar.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/include/delstar.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/include/delstar.h	(revision 22322)
@@ -0,0 +1,73 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+
+typedef struct {
+  Coords coords;
+  float *X, *Y;
+  int *N;
+  double RA[2], DEC[2];
+  double Area, density, spacing;
+} CatStats;
+
+/* global variables set in parameter file */
+char   ImageCat[256];
+char   ImageTemplate[256];
+char   CatTemplate[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+double NSIGMA;
+double ALPHA;
+int    VERBOSE;
+int    ORPHAN;
+int    MISSED;
+char   SKY_TABLE[256];
+int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+
+time_t    START;
+time_t    END;
+PhotCode *PHOTCODE;
+
+int    MODE;
+enum {MODE_IMAGENAME, MODE_IMAGEFILE, MODE_TIME, MODE_ORPHAN, MODE_MISSED};
+
+char DateKeyword[64], DateMode[64], UTKeyword[64], MJDKeyword[64], JDKeyword[64];
+
+/*** delstar prototypes ***/
+void       ConfigInit             PROTO((int *argc, char **argv));
+int        FindDecBand            PROTO((double dec, double *DEC0, double *DEC1));
+FILE      *GetDB                  PROTO((int *state));
+Image     *GetImages              PROTO((int *nimage));
+int        SetImages              PROTO((Image *new, int Nnew));
+void       SetProtect             PROTO((int mode));
+int        SetSignals             PROTO(());
+int        Shutdown               PROTO((char *format, ...));
+void       TrapSignal             PROTO((int sig));
+int        args                   PROTO((int *argc, char **argv));
+void       check_permissions      PROTO((char *basefile));
+void       delete_imagefile       PROTO((FITS_DB *db, char *filename));
+void       delete_imagename       PROTO((FITS_DB *db, char *name));
+void       delete_times           PROTO((FITS_DB *db));
+int        edge_check             PROTO((double *x1, double *y1, double *x2, double *y2));
+int       *find_images_data       PROTO((FITS_DB *db, Image *timage, int *nlist));
+int       *find_images_name       PROTO((FITS_DB *db, char *filename, int *nlist));
+int       *find_images_time       PROTO((FITS_DB *db, time_t start, time_t end, PhotCode *code, int *nlist));
+void       find_matches           PROTO((Catalog *catalog, int photcode, int start, int end));
+int        gcatalog               PROTO((Catalog *catalog));
+Image     *gimages                PROTO((char *filename));
+Image     *gtimes                 PROTO((int *NIMAGE));
+void       help                   PROTO(());
+int        load_image_db          PROTO((FITS_DB *db));
+void       lock_image_db          PROTO((FITS_DB *db, char *filename));
+void       match_images           PROTO((Catalog *catalog, Image *image, int Nimage));
+double     opening_angle          PROTO((double x1, double y1, double x2, double y2, double x3, double y3));
+int        parse_time             PROTO((Header *header));
+int        save_image_db          PROTO(());
+void       sort_lists             PROTO((float *X, float *Y, int *S, int N));
+void       unlock_image_db        PROTO((FITS_DB *db));
+void       usage                  PROTO(());
+int        wcatalog               PROTO((Catalog *catalog));
+
+void set_db (FITS_DB *in);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "delstar.h"
+
+void ConfigInit (int *argc, char **argv) {
+
+  char *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  ScanConfig (config, "NSIGMA",                 "%lf", 0, &NSIGMA);
+  ScanConfig (config, "ALPHA",                  "%lf", 0, &ALPHA);
+  ScanConfig (config, "GSCFILE",                "%s", 0, GSCFILE);
+  ScanConfig (config, "CATDIR",                 "%s", 0, CATDIR);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig (config, "PHOTCODE_FILE",         	"%s",  0, MasterPhotcodeFile);
+
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  ScanConfig (config, "DATE-KEYWORD",           "%s", 0, DateKeyword);
+  ScanConfig (config, "DATE-MODE",              "%s", 0, DateMode);
+  ScanConfig (config, "UT-KEYWORD",             "%s", 0, UTKeyword);
+  ScanConfig (config, "MJD-KEYWORD",            "%s", 0, MJDKeyword);
+  ScanConfig (config, "JD-KEYWORD",             "%s", 0, JDKeyword);
+
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = 2;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/SetSignals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/SetSignals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/SetSignals.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "delstar.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect these signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored (POSIX.1-1990) */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored (POSIX.1-1990) */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore (POSIX.1-1990) */
+    case SIGCONT:    /* continue - maintain this action (POSIX.1-1990) */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? (POSIX.1-1990) */
+    case SIGURG:     /* socket signal, ignore this (POSIX.1-2001) */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/Shutdown.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/Shutdown.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/Shutdown.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "delstar.h"
+
+static FITS_DB *db;
+
+void set_db (FITS_DB *in) {
+  db = in;
+}
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format));
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  SetProtect (TRUE);
+  gfits_db_close (db);
+  fprintf (stderr, "ERROR: delstar halted\n");
+  exit (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/args.c	(revision 22322)
@@ -0,0 +1,101 @@
+# include "delstar.h"
+
+void help () {
+
+  fprintf (stderr, "USAGE:\n");
+  fprintf (stderr, "  delstar (filename)\n");
+  fprintf (stderr, "  delstar -name (filename)\n");
+  fprintf (stderr, "  delstar -time (start) (stop/range)\n");
+  fprintf (stderr, "  delstar -orphan (region)\n");
+  fprintf (stderr, "  delstar -missed (region)\n\n");
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -v               : verbose mode\n");
+  fprintf (stderr, "  -photcode (code) : restrict by photcode\n");
+  fprintf (stderr, "\n"); 
+  exit (2);
+
+}
+
+void usage () {
+  fprintf (stderr, "USAGE: delstar (filename) / [optional mode] : -h for help\n");
+  exit (2);
+}
+
+int args (int *argc, char **argv) {
+  
+  int N;
+  double trange;
+  time_t tmp;
+
+  /* check for help request */
+  if (get_argument (*argc, argv, "-help") ||
+      get_argument (*argc, argv, "-h")) {
+    help ();
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (*argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  MODE = MODE_IMAGEFILE;
+  if ((N = get_argument (*argc, argv, "-name"))) {
+    if (MODE != MODE_IMAGEFILE) usage();
+    MODE = MODE_IMAGENAME;
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-orphan"))) {
+    if (MODE != MODE_IMAGEFILE) usage();
+    MODE = MODE_ORPHAN;
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-missed"))) {
+    if (MODE != MODE_IMAGEFILE) usage();
+    MODE = MODE_MISSED;
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-time"))) {
+    if (MODE != MODE_IMAGEFILE) usage();
+    MODE = MODE_TIME;
+    remove_argument (N, argc, argv);
+
+    if (!ohana_str_to_time (argv[N], &START)) usage ();
+    remove_argument (N, argc, argv);
+
+    /* interpret second value */
+    if (ohana_str_to_dtime (argv[N], &trange)) { 
+      if (trange < 0) {
+	END = START;
+	START = END + trange;
+      } else {
+	END = START + trange;
+      }
+      remove_argument (N, argc, argv);
+      goto goodtime;
+    }
+    if (ohana_str_to_time (argv[N], &END)) { 
+      if (START > END) {
+	tmp   = START;
+	START = END;
+	END   = START;
+      }
+      remove_argument (N, argc, argv);
+      goto goodtime;
+    }
+    usage ();
+  }
+goodtime:
+
+  /* restrict to a single photcode (not compatible with -image) */
+  PHOTCODE = NULL;
+  if ((N = get_argument (*argc, argv, "-photcode"))) {
+    remove_argument (N, argc, argv);
+    PHOTCODE = GetPhotcodebyName (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  if ((MODE == MODE_TIME) && (*argc != 1)) usage ();
+  if ((MODE != MODE_TIME) && (*argc != 2)) usage ();
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/check_permissions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/check_permissions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/check_permissions.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include "addstar.h"
+
+void check_permissions (char *basefile) {
+  
+  char *c, dir[256], filename[256];
+  struct stat filestat;
+  uid_t uid;
+  gid_t gid;
+  int status, cmode;
+
+  uid = getuid();
+  gid = getgid();
+
+  /* check permission to write to directory */
+  sprintf (filename, "%s", basefile);
+  c = strrchr (filename, '/');
+  if (c == (char *) NULL) {
+    strcpy (dir, ".");
+  } else {
+    *c = 0;
+    strcpy (dir, filename);
+  }
+  status = stat (dir, &filestat);
+  if (status == -1) {
+    fprintf (stderr, "directory %s does not exist, creating...\n", dir);
+    cmode = S_IRWXU | S_IRWXG | S_IRWXO;
+    status = mkdir (dir, cmode);
+    if (status == -1) {
+      fprintf (stderr, "ERROR: can't create %s\n", dir);
+      exit (1);
+    }
+  } 
+  status = stat (dir, &filestat);
+  if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRWXU)) ||
+      ((gid == filestat.st_gid) && (filestat.st_mode & S_IRWXG)) || 
+      (filestat.st_mode & S_IRWXO)) {
+  } else {
+    fprintf (stderr, "ERROR: can't write to %s\n", dir);
+    exit (1);
+  }
+  
+  /* check permission to write to file */
+  sprintf (filename, "%s", basefile);
+  status = stat (filename, &filestat);
+  if (status == 0) { /* file exists, are permissions OK? */
+    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR) && (filestat.st_mode & S_IWUSR)) ||
+	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP) && (filestat.st_mode & S_IWGRP)) || 
+	((filestat.st_mode & S_IROTH) && (filestat.st_mode & S_IWOTH))) {
+    } else {
+      fprintf (stderr, "ERROR: can't write to %s\n", filename);
+      exit (1);
+    }
+  }
+  
+  /* check permission to write to backup file */
+  sprintf (filename, "%s~", basefile);
+  status = stat (filename, &filestat);
+  if (status == 0) { /* file exists, are permissions OK? */
+    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR) && (filestat.st_mode & S_IWUSR)) ||
+	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP) && (filestat.st_mode & S_IWGRP)) || 
+	((filestat.st_mode & S_IROTH) && (filestat.st_mode & S_IWOTH))) {
+    } else {
+      fprintf (stderr, "ERROR: can't write to %s\n", filename);
+      exit (1);
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_imagefile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_imagefile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_imagefile.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "delstar.h"
+
+void delete_imagefile (FITS_DB *db, char *filename) {
+
+  int i, Nimlist;
+  int *imlist;
+  double trange;
+  time_t start, stop;
+  Image *image;
+  Catalog catalog;
+  SkyTable *sky;
+
+  /* load sky from correct table */
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+
+  /* load information about file - time/photcode */
+  image = gimages (filename);
+  
+  /* need to define method to get the mosaic (look up from table) */
+  if (VERBOSE) fprintf (stderr, "deleting %s\n", image[0].name);
+
+  for (i = 0; i < sky[0].Nregions; i++) {
+
+    if (VERBOSE) fprintf (stderr, "deleting from %s\n", sky[0].filename[i]);
+    catalog.filename = sky[0].filename[i];  /* don't free region before catalog! */
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, &sky[0].regions[i], VERBOSE, "a")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    if (!catalog.Naves_disk) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    /* pad exposure time by 1 sec to require a valid time */
+    /* trate is in 0.1 msec / row  - stop is the latest exposure end time */
+    trange = 1e-4*image[0].NY*image[0].trate + image[0].exptime + 1;  
+    start = image[0].tzero;
+    stop  = image[0].tzero + trange;
+    find_matches (&catalog, image[0].photcode, start, stop);
+
+    dvo_catalog_save (&catalog, VERBOSE);
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+
+  /* find and delete matching images */
+  // XXX EAM : load image data above, find mosaic?
+  imlist = find_images_data (db, image, &Nimlist);
+  if (!Nimlist) Shutdown ("image %s not found in db", filename);
+
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, imlist, Nimlist);
+  dvo_image_update (db, VERBOSE);
+  dvo_image_unlock (db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_imagename.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_imagename.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_imagename.c	(revision 22322)
@@ -0,0 +1,92 @@
+# include "delstar.h"
+
+void delete_imagename (FITS_DB *db, char *name) {
+
+  int i, j, k;
+  int Nimlist, Nimage, Noutimage;
+  int *imlist;
+  double trange;
+  time_t start, stop;
+  Image *image;
+  Image *outimage;
+  Catalog catalog;
+  SkyList *skylist;
+  SkyTable *sky;
+
+  /* load sky from correct table */
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  /* find image in db by name */
+  imlist = find_images_name (db, name, &Nimlist);
+  if (!Nimlist) Shutdown ("image %s not found in db", name);
+  
+  for (k = 0; k < Nimlist; k++) {
+
+    j = imlist[k];
+    if (VERBOSE) fprintf (stderr, "deleting %s\n", image[j].name);
+    
+    // XXX EAM : need to handle failure
+    FindMosaicForImage (image, Nimage, j);
+    skylist = SkyListByImage (sky, -1, &image[j]);
+
+    for (i = 0; i < skylist[0].Nregions; i++) {
+      if (VERBOSE) fprintf (stderr, "deleting from %s\n", skylist[0].filename[i]);
+      catalog.filename = skylist[0].filename[i];  /* don't free region before catalog! */
+      catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+      catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+
+      // an error exit status here is a significant error
+      if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "a")) {
+	fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+	exit (2);
+      }
+      if (!catalog.Naves_disk) {
+	dvo_catalog_unlock (&catalog);
+	dvo_catalog_free (&catalog);
+	continue;
+      }
+
+      /* trate is in 0.1 msec / row  - stop is the latest exposure end time */
+      /* pad exposure time by 1 sec to require a valid time */
+      trange = 1e-4*image[j].NY*image[j].trate + image[j].exptime + 1;  
+      start = image[j].tzero;
+      stop  = image[j].tzero + trange;
+      find_matches (&catalog, image[j].photcode, start, stop);
+
+      dvo_catalog_save_complete (&catalog, VERBOSE);
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+    }
+  }
+
+  /* delete the identified images */
+  ALLOCATE (outimage, Image, Nimage - Nimlist);
+  for (i = 0, k = 0; i < Nimage; i++) {
+      for (j = 0; j < Nimlist; j++) {
+	  if (imlist[j] == i) goto skip;
+      }
+      outimage[k] = image[i];
+      k++;
+  skip:
+      continue;
+  }
+  free (image);
+  Noutimage = Nimage - Nimlist;
+  
+  if (VERBOSE) fprintf (stderr, "removing %d images (leaving %d of %d)\n", Nimlist, Noutimage, Nimage);
+  // gfits_table_set_Image (&db[0].ftable, outimage, Noutimage);
+
+  gfits_modify (&db[0].theader, "NAXIS2", "%d", 1, Noutimage);
+  gfits_modify (&db[0].header, "NIMAGES", "%d", 1, Noutimage);
+  db[0].theader.Naxis[1] = Noutimage;
+  db[0].ftable.buffer = (char *) outimage;
+
+  dvo_image_save (db, VERBOSE);
+  dvo_image_unlock (db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_missed.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_missed.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_missed.c	(revision 22322)
@@ -0,0 +1,24 @@
+# include "delstar.h"
+
+/* drop all MISSED values for the given catalog */
+
+delete_missed (Catalog *catalog) {
+
+  int i;
+  int Nave, Nmeas, Nmiss;
+
+  Nave = catalog[0].Naverage;
+  Nmeas = catalog[0].Nmeasure;
+  Nmiss = catalog[0].Nmissing;
+  
+  if (VERBOSE) fprintf (stderr, "starting with Nave, Nmeas, Nmiss: %d %d %d\n", Nave, Nmeas, Nmiss);
+
+  /* set up references for missing to average */
+  for (i = 0; i < Nave; i++) {
+    catalog[0].average[i].Nn = 0;
+  }
+  REALLOCATE (catalog[0].missing, Missing, 1);
+  catalog[0].Nmissing = 0;
+  if (VERBOSE) fprintf (stderr, "  ending with Nave, Nmeas, Nmiss: %d %d %d\n", Nave, Nmeas, Nmiss);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_orphans.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_orphans.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_orphans.c	(revision 22322)
@@ -0,0 +1,258 @@
+# include "delstar.h"
+
+delete_orphans (char *name) {
+
+  int i, j, k, n, m, N, M, found;
+  int *N1, *N2,  *next, *next_miss, *ave_miss, last, last_miss;
+  int Nave, NAVE, Nmeas, NMEAS, Nmiss, NMISS, Nmatch;
+  int start, end, Nmeasfound, Nsecfilt;
+  unsigned int flags;
+  Measure *tmpmeasure;
+  Missing *tmpmissing;
+  Coords tcoords;
+  Catalog catalog;
+
+  /* find and load catalog file */
+  catalog.filename = name;
+  dvo_catalog_load (&catalog);
+  gcatstats (&catalog, &catstats);
+
+  /* find images overlapping catalog */
+  image = find_images_region (&catstats, &Nimage);
+  match_images (&catalog, image, Nimage);
+
+  /** allocate local arrays **/
+  Nave = catalog.Naverage;
+
+  Nmeas = catalog.Nmeasure;
+  ALLOCATE (next, int, Nmeas);
+  
+  Nmiss = catalog.Nmissing;
+  ALLOCATE (next_miss, int, Nmiss);
+  ALLOCATE (ave_miss, int, Nmiss);
+  
+  if (VERBOSE) fprintf (stderr, "starting with Nave, Nmeas, Nmiss: %d %d %d\n", Nave, Nmeas, Nmiss);
+
+  /* set up pointers for linked list of measure */
+  for (i = 0; i < Nmeas - 1; i++) {
+    next[i] = i+1;
+  }
+  next[i] = -1;
+  last = i;
+  /* set up pointers for linked list of missing */
+  for (i = 0; i < Nmiss - 1; i++) {
+    next_miss[i] = i+1;
+  }
+  next_miss[i] = -1;
+  last_miss = i;
+  /* set up references for missing to average */
+  for (i = 0; i < Nave; i++) {
+    for (j = 0; j < catalog.average[i].Nn; j++) {
+      ave_miss[catalog.average[i].missing + j] = i;
+    }
+  }
+  Nmeasfound = 0;
+  Nsecfilt = catalog.Nsecfilt;
+
+  /* fprintf (stderr, "fixing the measures...\n"); */
+  for (i = 0; (i < Nmeas); i++) {
+    if ((catalog.measure[i].t != 0) && (catalog.image[i] == -1)) { 
+      /* this star is an orphan */
+      Nmeasfound ++;
+      next[i] = -2; /* we delete this one */
+      /* fix the list links: connect the previous valid link to the next valid link */
+      for (j = i; (j >= 0) && (next[j] == -2); j--); /* find previous entry to fix link */
+      if (j >= 0) { /* if j < 0, there is no previous valid link, ignore this step */
+	if (next[j] != i) {
+	  fprintf (stderr, "error?  this link seems to have been lost\n");
+	  exit (1);
+	}
+	/* find next valid entry to fix link */
+	for (k = i; (k < Nmeas) && (next[k] == -2); k++);
+	if (k < Nmeas)
+	  next[j] = k;
+	else 
+	  next[j] = -1;  /* last link in list gets a -1 */
+      }
+      
+      /*** fix the corresponding average entry ***/
+      n = catalog.measure[i].averef;
+      if (catalog.average[n].Nm == 0) { /* this should never happen */
+	fprintf (stderr, "error? we deleted one too many objects?\n");
+	exit (1);
+      }
+      catalog.average[n].Nm --;
+      /* this was only entry in list: will be deleted below.  meanwhile, delete all missing entries*/
+      if ((catalog.average[n].Nm < 1) && (catalog.average[n].Nn > 0)) { 
+	m = catalog.average[n].missing;
+	for (j = 0; j < catalog.average[n].Nn; j++) {
+	  M = next_miss[m];
+	  next_miss[m] = -2;
+	  m = M;
+	}
+	m = catalog.average[n].missing;
+	/* fix the list links: connect the previous valid link to the next valid link */
+	for (j = m; (j >= 0) && (next_miss[j] == -2); j--); /* find previous entry to fix link */
+	if (j >= 0) { /* if j < 0, there is no previous valid link, ignore this step */
+	  if (next_miss[j] != m) {
+	    fprintf (stderr, "error?  this link seems to have been lost\n");
+	    exit (1);
+	  }
+	  /* find next valid entry to fix link */
+	  for (k = m; (k < Nmiss) && (next_miss[k] == -2); k++);
+	  if (k < Nmiss)
+	    next_miss[j] = k;
+	  else 
+	    next_miss[j] = -1;  /* last link in list gets a -1 */
+	}
+      }
+      /* this was first entry in list */
+      if ((catalog.average[n].offset == i) && (catalog.average[n].Nm > 0)) { 
+	m = catalog.average[n].offset;
+	/* find next valid entry -- notice lack of error checking... */
+	for (j = 0; (j < Nmeas) && (next[m+j] == -2); j++);
+	catalog.average[n].offset = m + j;
+      }
+
+    }
+  }
+  fprintf (stderr, "found %d meas to remove\n", Nmeasfound);
+
+  /* fprintf (stderr, "fixing the missing...\n"); */
+  /** find missing in time range of image **/
+  for (i = 0; (i < Nmiss); i++) {
+    if ((next_miss[i] != -2) && (catalog.missing[i].t >= start) && (catalog.missing[i].t <= end)) { 
+      /* this star is in this image */
+
+      next_miss[i] = -2; /* we delete this one */
+      /* fix the list links: connect the previous valid link to the next valid link */
+      for (j = i; (j >= 0) && (next_miss[j] == -2); j--); /* find previous entry to fix link */
+      if (j >= 0) { /* if j < 0, there is no previous valid link, ignore this step */
+	if (next_miss[j] != i) {
+	  fprintf (stderr, "error?  this link seems to have been lost\n");
+	  exit (1);
+	}
+	/* find next valid entry to fix link */
+	for (k = i; (k < Nmiss) && (next_miss[k] == -2); k++);
+	if (k < Nmiss)
+	  next_miss[j] = k;
+	else 
+	  next_miss[j] = -1;  /* last link in list gets a -1 */
+      }
+      
+      /* find the corresponding avearge entry 
+      found = FALSE;
+      for (n = 0; !found && (n < Nave); n++) {
+	if ((catalog.average[n].missing > 0) && (catalog.average[n].missing <= i) && (catalog.average[n].missing + catalog.average[n].Nn > i)) 
+	  found = TRUE;
+      }
+      n--; */
+      /*** fix the corresponding average entry ***/
+      n = ave_miss[i];
+      if (catalog.average[n].Nn == 0) { /* this should never happen */
+	fprintf (stderr, "error? we deleted one too many missing?\n");
+	exit (1);
+      }
+      catalog.average[n].Nn --;
+      /* this was first entry in list */
+      if ((catalog.average[n].missing == i) && (catalog.average[n].Nn > 0)) { 
+	m = catalog.average[n].missing;
+	for (j = 0; (j < Nmiss) && (next_miss[m+j] == -2); j++);
+	catalog.average[n].missing = m + j;
+      }
+
+    }
+  }
+
+  /* currently not worked out, but we will need to delete the references to blended image and cat stars */
+# if 0
+  /*** handle multiple stars */
+  /* this image star matches more than one catalog star */
+  if (stars[N].found > -1) {
+    catalog.measure[stars[N].found].flags |= ID_MEAS_BLEND_MEAS;
+    catalog.measure[Nmeas].flags |= ID_MEAS_BLEND_MEAS;
+  } 
+  if (stars[N].found == -2) { /* this image star matches a catalog star on a neighboring catalog */
+    catalog.measure[Nmeas].flags |= ID_MEAS_BLEND_MEAS_X;
+  } 
+  if (stars[N].found == -1) { /* this image star matches only this star */
+    stars[N].found = Nmeas;  /* save first match, in case coincidences are found */
+  }
+  /* this catalog star matches more than one image star */
+  if (catalog.found[n] > -1) {
+    catalog.measure[catalog.found[n]].flags |= ID_MEAS_BLEND_OBJ;
+    catalog.measure[Nmeas].flags |= ID_MEAS_BLEND_OBJ;
+  } else {
+    catalog.found[n] = Nmeas;
+  }
+# endif  
+
+  /* fprintf (stderr, "fixing the averages...\n"); */
+  /* fix Average list: delete entries with Nm == 0 */
+  for (i = j = 0; (i < Nave) && (j < Nave); i++, j++) {
+    for (; (j < Nave) && (catalog.average[j].Nm == 0); j++);
+    if ((i != j) && (j < Nave)) {
+      catalog.average[i] = catalog.average[j];
+      for (k = 0; k < catalog.Nsecfilt; k++) {
+	catalog.secfilt[i*Nsecfilt + k] = catalog.secfilt[j*Nsecfilt + k];
+      }
+    }
+    if (j == Nave) i--;
+  }
+  Nave = i;
+  REALLOCATE (catalog.average, Average, Nave);
+  REALLOCATE (catalog.secfilt, SecFilt, MAX (1, Nave*Nsecfilt));
+  
+  /* fprintf (stderr, "fixing the measure order...\n"); */
+  /* fix order of Measure (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmeasure, Measure, Nmeas);
+  for (i = 0; i < Nave; i++) {
+    n = catalog.average[i].offset;
+    catalog.average[i].offset = N;
+    for (k = 0; k < catalog.average[i].Nm; k++, N++) {
+      if ((n == -1) || (n == -2)) {
+	fprintf (stderr, "error: linked list is confused\n");
+	exit (1);
+      }
+      tmpmeasure[N] = catalog.measure[n]; 
+      tmpmeasure[N].averef = i;
+      n = next[n];
+    }
+  }
+  Nmeas = N;
+  free (catalog.measure);
+  catalog.measure = tmpmeasure;
+  REALLOCATE (catalog.measure, Measure, Nmeas);
+    
+  /* fprintf (stderr, "fixing the mising order...\n"); */
+  /* fix order of Missing (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmissing, Missing, Nmiss);
+  for (i = 0; i < Nave; i++) {
+    if (catalog.average[i].Nn > 0) {
+      n = catalog.average[i].missing;
+      catalog.average[i].missing = N;
+      for (k = 0; k < catalog.average[i].Nn; k++, N++) {
+	if ((n == -1) || (n == -2)) {
+	  fprintf (stderr, "error: linked list is confused\n");
+	  exit (1);
+	}
+	tmpmissing[N] = catalog.missing[n]; 
+	n = next_miss[n];
+      }
+    }
+  }
+  Nmiss = N;
+  free (catalog.missing);
+  catalog.missing = tmpmissing;
+  REALLOCATE (catalog.missing, Missing, Nmiss);
+
+  catalog.Naverage = Nave;
+  catalog.Nmeasure = Nmeas;
+  catalog.Nmissing = Nmiss;
+  catalog.Nsecf_mem = Nave*Nsecfilt;
+
+  if (VERBOSE) fprintf (stderr, "  ending with Nave, Nmeas, Nmiss: %d %d %d\n", Nave, Nmeas, Nmiss);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_times.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_times.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delete_times.c	(revision 22322)
@@ -0,0 +1,85 @@
+# include "delstar.h"
+
+void delete_times (FITS_DB *db) {
+
+  int i, j, k, n;
+  int Nimage, Nimlist, found, code;
+  int Nregions, NREGIONS;
+  int *imlist;
+  SkyList *skylist, *skyset;
+  SkyTable *sky;
+  Image *image;
+  Catalog catalog;
+
+  code = (PHOTCODE == NULL) ? -1 : PHOTCODE[0].code;
+
+  /* load sky from correct table */
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+
+  Nregions = 0;
+  NREGIONS = 10;
+  ALLOCATE (skylist, SkyList, 1);
+  ALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS);
+  skylist[0].ownElements = FALSE; // free these elements when freeing the list
+
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  /* find images for time range, delete each image */ 
+  imlist = find_images_time (db, START, END, PHOTCODE, &Nimlist);
+
+  /* find all overlapping regions */
+  for (n = 0; n < Nimlist; n++) {
+    j = imlist[n];
+    if (VERBOSE) fprintf (stderr, "finding regions for %s\n", image[j].name);
+
+    // XXX EAM : need to handle failure
+    FindMosaicForImage (image, Nimage, j);
+    skyset = SkyListByImage (sky, -1, &image[j]);
+
+    // tregion = gregion_image (&image[j], &Ntregions);
+    for (i = 0; i < skyset[0].Nregions; i++) {
+      found = FALSE;
+      for (k = 0; (k < skylist[0].Nregions) && !found; k++) {
+	found = !strcmp (skylist[0].regions[k][0].name, skyset[0].regions[i][0].name);
+      }
+      if (found) continue;
+      skylist[0].regions[Nregions] = skyset[0].regions[i];
+      Nregions ++;
+      CHECK_REALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS, Nregions, 10);
+    }
+    SkyListFree (skyset);
+  }
+
+  /* delete from all identified regions */
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    if (VERBOSE) fprintf (stderr, "deleting from %s\n", skylist[0].filename[i]);
+    catalog.filename = skylist[0].filename[i];  /* don't free region before catalog! */
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "a")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    if (!catalog.Naves_disk) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    find_matches (&catalog, code, START, END);
+    dvo_catalog_save (&catalog, VERBOSE);
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+
+  /* delete the identified images */
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, imlist, Nimlist);
+  dvo_image_update (db, VERBOSE);
+  dvo_image_unlock (db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delstar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delstar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/delstar.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "delstar.h"
+
+int main (int argc, char **argv) {
+
+  FITS_DB db;
+
+  int status;
+  SetSignals ();
+  ConfigInit (&argc, argv);
+  args (&argc, argv);
+
+  set_db (&db);
+  status = dvo_image_lock (&db, ImageCat, 60.0, LCK_XCLD); /* XCLD */
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+  if (db.dbstate == LCK_EMPTY) Shutdown ("ERROR: No images in catalog %s (1)", db.filename);
+
+  status = dvo_image_load (&db, VERBOSE, FALSE);
+  if (!status) Shutdown ("can't read image catalog %s", db.filename);
+
+  switch (MODE) {
+    case MODE_IMAGEFILE:
+      delete_imagefile (&db, argv[1]);
+      break;
+    case MODE_IMAGENAME:
+      delete_imagename (&db, argv[1]);
+      break;
+    case MODE_TIME:
+      delete_times (&db);
+      break;
+    case MODE_ORPHAN:
+      fprintf (stderr, "delete orphans not available\n");
+      // delete_orphans (argv[1]);
+      break;
+    case MODE_MISSED:
+      fprintf (stderr, "delete missed not available\n");
+      // delete_missed (argv[1]);
+      break;
+    default:
+      usage ();
+  }
+  exit (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/find_image_db.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/find_image_db.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/find_image_db.c	(revision 22322)
@@ -0,0 +1,94 @@
+# include "delstar.h"
+
+int *find_images_name (FITS_DB *db, char *filename, int *nlist) {
+
+  int i, Nimage, Nlist, NLIST;
+  int *list;
+  char *p, *name;
+  Image *image;
+
+  /* strip off all but the filename */
+  p = strrchr (filename, '/');
+  if (p == NULL) {
+    name = filename;
+  } else {
+    name = p + 1;
+  }
+
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  Nlist = 0;
+  NLIST = 100;
+  ALLOCATE (list, int, NLIST);
+
+  for (i = 0; i < Nimage; i++) {
+    if (strcmp (image[i].name, name)) continue;
+    list[Nlist] = i;
+    Nlist ++;
+    CHECK_REALLOCATE (list, int, NLIST, Nlist, 100);
+  }
+
+  *nlist = Nlist;
+  return (list);
+}
+
+/* find images in db by image data (time/photcode) */
+int *find_images_data (FITS_DB *db, Image *timage, int *nlist) {
+
+  int i, Nimage, Nlist, NLIST; 
+  int *list;
+  Image *image;
+  time_t start, stop;
+  int code;
+
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  start = timage[0].tzero - MAX(0.01*timage[0].trate*timage[0].NY, 1);
+  stop  = timage[0].tzero + MAX(1.01*timage[0].trate*timage[0].NY, 1);
+  code  = timage[0].photcode;
+
+  Nlist = 0;
+  NLIST = 100;
+  ALLOCATE (list, int, NLIST);
+
+  for (i = 0; i < Nimage; i++) {
+    if (image[i].tzero < start) continue;
+    if (image[i].tzero > stop) continue;
+    if (image[i].photcode != code) continue;
+    list[Nlist] = i;
+    Nlist ++;
+    CHECK_REALLOCATE (list, int, NLIST, Nlist, 100);
+  }
+
+  *nlist = Nlist;
+  return (list);
+}
+
+/* find images in db by image data (time/photcode) */
+int *find_images_time (FITS_DB *db, time_t start, time_t end, PhotCode *code, int *nlist) {
+
+  int i, Nimage, Nlist, NLIST;
+  int *list;
+  Image *image;
+
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  Nlist = 0;
+  NLIST = 100;
+  ALLOCATE (list, int, NLIST);
+
+  for (i = 0; i < Nimage; i++) {
+    if (image[i].tzero < START) continue;
+    if (image[i].tzero > END) continue;
+    if (code != NULL) {
+      if (image[i].photcode != code[0].code) continue;
+    }
+    list[Nlist] = i;
+    Nlist ++;
+    CHECK_REALLOCATE (list, int, NLIST, Nlist, 100);
+  }
+
+  *nlist = Nlist;
+  return (list);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/find_matches.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/find_matches.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/find_matches.c	(revision 22322)
@@ -0,0 +1,257 @@
+# include "delstar.h"
+
+void find_matches (Catalog *catalog, int photcode, int start, int end) {
+
+  int i, j, k, n, m, N, M, drop, averef;
+  int *next, *next_miss, *ave_miss, last, last_miss;
+  int Nave, Nmeas, NMEAS, Nmiss;
+  int Nmeasfound, Nsecfilt;
+  int this, prev;
+  Measure *tmpmeasure;
+  Missing *tmpmissing;
+
+  /** allocate local arrays **/
+  Nave = catalog[0].Naverage;
+
+  Nmeas = catalog[0].Nmeasure;
+  ALLOCATE (next, int, MAX(Nmeas,1));
+  
+  Nmiss = catalog[0].Nmissing;
+  ALLOCATE (next_miss, int, MAX(Nmiss,1));
+  ALLOCATE (ave_miss, int, MAX(Nmiss,1));
+  
+  if (VERBOSE) fprintf (stderr, "starting with Nave, Nmeas, Nmiss: %d %d %d\n", Nave, Nmeas, Nmiss);
+
+  /* set up pointers for linked list of measure */
+  for (i = 0; i < Nmeas - 1; i++) {
+    next[i] = i+1;
+  }
+  next[i] = -1;
+  last = i;
+  /* set up pointers for linked list of missing */
+  for (i = 0; i < Nmiss - 1; i++) {
+    next_miss[i] = i+1;
+  }
+  next_miss[i] = -1;
+  last_miss = i;
+  /* set up references for missing to average */
+  for (i = 0; i < Nave; i++) {
+    for (j = 0; j < catalog[0].average[i].Nmissing; j++) {
+      ave_miss[catalog[0].average[i].missingOffset + j] = i;
+    }
+  }
+
+  if (VERBOSE) fprintf (stderr, "deleting for range %d to %d, photcode %d\n", start, end, photcode);
+  Nmeasfound = 0;
+  Nsecfilt = catalog[0].Nsecfilt;
+
+  if (VERBOSE) fprintf (stderr, "fixing measure...\n"); 
+
+  /** find measure in time range **/
+  this = prev = -1;
+  for (i = 0; (i < Nmeas); i++) {
+    if (VERBOSE && !(i % 10000)) fprintf (stderr, ". ");
+    drop = TRUE;
+    drop &= (catalog[0].measure[i].t >= start);
+    drop &= (catalog[0].measure[i].t <= end);
+    drop &= ((photcode == -1) || (photcode == catalog[0].measure[i].photcode));
+    if (!drop) {
+      prev = i;
+      continue;
+    }
+    Nmeasfound ++;
+
+    /* this star is in this image */
+    this = next[i];
+    next[i] = -2; /* we delete this one */
+    if (prev != -1) { next[prev] = this; }
+
+# if (0) 
+    /* why is this section disabled? */
+    /* fix the list links: connect the previous valid link to the next valid link */
+    for (j = i; (j >= 0) && (next[j] == -2); j--); /* find previous entry to fix link */
+    if (j >= 0) { /* if j < 0, there is no previous valid link, ignore this step */
+      if (next[j] != i) {
+	fprintf (stderr, "error? (1)  this link seems to have been lost\n");
+	fprintf (stderr, "j: %d, next[j]: %d, i: %d\n", j, next[j], i);
+	exit (1);
+      }
+      /* find next valid entry to fix link */
+      for (k = i; (k < Nmeas) && (next[k] == -2); k++);
+      if (k < Nmeas)
+	next[j] = k;
+      else 
+	next[j] = -1;  /* last link in list gets a -1 */
+    }
+# endif      
+
+    /*** fix the corresponding average entry ***/
+    n = catalog[0].measure[i].averef;
+    if (catalog[0].average[n].Nmeasure == 0) { /* this should never happen */
+      fprintf (stderr, "error? we deleted one too many objects?\n");
+      exit (1);
+    }
+    catalog[0].average[n].Nmeasure --;
+    /* this was only entry in list: will be deleted below.  meanwhile, delete all missing entries*/
+    if ((catalog[0].average[n].Nmeasure < 1) && (catalog[0].average[n].Nmissing > 0)) { 
+      m = catalog[0].average[n].missingOffset;
+      for (j = 0; j < catalog[0].average[n].Nmissing; j++) {
+	M = next_miss[m];
+	next_miss[m] = -2;
+	m = M;
+      }
+      m = catalog[0].average[n].missingOffset;
+      /* fix the list links: connect the previous valid link to the next valid link */
+      for (j = m; (j >= 0) && (next_miss[j] == -2); j--); /* find previous entry to fix link */
+      if (j >= 0) { /* if j < 0, there is no previous valid link, ignore this step */
+	if (next_miss[j] != m) {
+	  fprintf (stderr, "error? (2) this link seems to have been lost\n");
+	  fprintf (stderr, "j: %d, next_miss[j]: %d, i: %d\n", j, next_miss[j], i);
+	  exit (1);
+	}
+	/* find next valid entry to fix link */
+	for (k = m; (k < Nmiss) && (next_miss[k] == -2); k++);
+	if (k < Nmiss)
+	  next_miss[j] = k;
+	else 
+	  next_miss[j] = -1;  /* last link in list gets a -1 */
+      }
+    }
+    /* this was first entry in list */
+    if ((catalog[0].average[n].measureOffset == i) && (catalog[0].average[n].Nmeasure > 0)) { 
+      m = catalog[0].average[n].measureOffset;
+      /* find next valid entry -- notice lack of error checking... */
+      for (j = 0; (j < Nmeas) && (next[m+j] == -2); j++);
+      if (catalog[0].measure[m+j].averef != n) {
+	fprintf (stderr, "error? measure.averef and average.measureOffset are mismatched\n");
+	exit (1);
+      }
+      catalog[0].average[n].measureOffset = m + j;
+    }
+  } 
+  fprintf (stderr, "found %d meas to remove\n", Nmeasfound);
+
+  if (VERBOSE) fprintf (stderr, "fixing missing..."); 
+  /** find missing in time range of image **/
+  for (i = 0; (i < Nmiss); i++) {
+    if (next_miss[i] == -2) continue;
+    if (catalog[0].missing[i].t < start) continue;
+    if (catalog[0].missing[i].t > end) continue;
+
+    next_miss[i] = -2; /* we delete this one */
+    /* fix the list links: connect the previous valid link to the next valid link */
+    for (j = i; (j >= 0) && (next_miss[j] == -2); j--); /* find previous entry to fix link */
+    if (j >= 0) { /* if j < 0, there is no previous valid link, ignore this step */
+      if (next_miss[j] != i) {
+	fprintf (stderr, "error? (3) this link seems to have been lost\n");
+	fprintf (stderr, "j: %d, next_miss[j]: %d, i: %d\n", j, next_miss[j], i);
+	exit (1);
+      }
+      /* find next valid entry to fix link */
+      for (k = i; (k < Nmiss) && (next_miss[k] == -2); k++);
+      if (k < Nmiss)
+	next_miss[j] = k;
+      else 
+	next_miss[j] = -1;  /* last link in list gets a -1 */
+    }
+      
+    /*** fix the corresponding average entry ***/
+    n = ave_miss[i];
+    if (catalog[0].average[n].Nmissing == 0) { /* this should never happen */
+      fprintf (stderr, "error? we deleted one too many missing?\n");
+      exit (1);
+    }
+    catalog[0].average[n].Nmissing --;
+    /* this was first entry in list */
+    if ((catalog[0].average[n].missingOffset == i) && (catalog[0].average[n].Nmissing > 0)) { 
+      m = catalog[0].average[n].missingOffset;
+      for (j = 0; (j < Nmiss) && (next_miss[m+j] == -2); j++);
+      catalog[0].average[n].missingOffset = m + j;
+    }
+  }
+
+  /* we should delete the references to blended image and cat stars ?? */
+  /* or drop since we are changing this concept ?? */
+
+  /* fix Average list: delete entries with Nm == 0 */
+  for (i = j = 0; (i < Nave) && (j < Nave); i++, j++) {
+    for (; (j < Nave) && (catalog[0].average[j].Nmeasure == 0); j++);
+    if ((i != j) && (j < Nave)) {
+      catalog[0].average[i] = catalog[0].average[j];
+      for (k = 0; k < catalog[0].Nsecfilt; k++) {
+	catalog[0].secfilt[i*Nsecfilt + k] = catalog[0].secfilt[j*Nsecfilt + k];
+      }
+    }    
+    if (j == Nave) i--;
+  }
+  Nave = i;
+  REALLOCATE (catalog[0].average, Average, Nave);
+  REALLOCATE (catalog[0].secfilt, SecFilt, MAX (1, Nave*Nsecfilt));
+
+  /* fix order of Measure (memory intensive, but fast) */
+  N = 0; 
+  NMEAS = Nmeas;
+  ALLOCATE (tmpmeasure, Measure, NMEAS);
+  for (i = 0; i < Nave; i++) {
+    n = catalog[0].average[i].measureOffset;
+    catalog[0].average[i].measureOffset = N;
+    averef = catalog[0].measure[n].averef;
+    for (k = 0; k < catalog[0].average[i].Nmeasure; k++, N++) {
+      if ((n == -1) || (n == -2)) {
+	fprintf (stderr, "error: linked list is confused\n");
+	exit (1);
+      }
+      // all measures for this object should have the same initial averef
+      if (catalog[0].measure[n].averef != averef) {
+	fprintf (stderr, "measure table is confused\n");
+	exit (1);
+      }
+      tmpmeasure[N] = catalog[0].measure[n]; 
+      tmpmeasure[N].averef = i;
+      n = next[n];
+      CHECK_REALLOCATE (tmpmeasure, Measure, NMEAS, N, 10000);
+    }
+  }
+  Nmeas = N;
+  free (catalog[0].measure);
+  catalog[0].measure = tmpmeasure;
+  REALLOCATE (catalog[0].measure, Measure, Nmeas);
+    
+  /* fprintf (stderr, "fixing the mising order...\n"); */
+  /* fix order of Missing (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmissing, Missing, Nmiss);
+  for (i = 0; i < Nave; i++) {
+    if (catalog[0].average[i].Nmissing > 0) {
+      n = catalog[0].average[i].missingOffset;
+      catalog[0].average[i].missingOffset = N;
+      for (k = 0; k < catalog[0].average[i].Nmissing; k++, N++) {
+	if ((n == -1) || (n == -2)) {
+	  fprintf (stderr, "error: linked list is confused\n");
+	  exit (1);
+	}
+	tmpmissing[N] = catalog[0].missing[n]; 
+	n = next_miss[n];
+      }
+    }
+  }
+  Nmiss = N;
+  free (catalog[0].missing);
+  catalog[0].missing = tmpmissing;
+  REALLOCATE (catalog[0].missing, Missing, Nmiss);
+
+  fprintf (stderr, "\n");
+
+  catalog[0].Naverage = Nave;
+  catalog[0].Nmeasure = Nmeas;
+  catalog[0].Nmissing = Nmiss;
+  catalog[0].Nsecf_mem = Nave*Nsecfilt;
+
+  if (VERBOSE) fprintf (stderr, "  ending with Nave, Nmeas, Nmiss: %d %d %d\n", Nave, Nmeas, Nmiss);
+
+  free (next);
+  free (next_miss);
+  free (ave_miss);
+  return;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/gimages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/gimages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/gimages.c	(revision 22322)
@@ -0,0 +1,94 @@
+# include "delstar.h"
+
+/* load information about image from image header
+ * this should be the same as addstar/gstars, but it is not...
+ */
+
+Image *gimages (char *filename) {
+ 
+  FILE *f;
+  Header header;
+  char photcode[64], *c;
+  double tmp;
+  Image *image;
+  int Nc, haveNx, haveNy;
+
+  ALLOCATE (image, Image, 1);
+  /* load header */
+  if (!gfits_read_header (filename, &header)) {
+    Shutdown ("ERROR: can't find image file %s", filename);
+  }
+
+  /* open file */
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    Shutdown ("ERROR: can't find data file %s", filename);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  /* add file name to image structure */
+  c = strrchr (filename, 0x2f);
+  if (c == (char *) NULL) {
+    strcpy (image[0].name, filename);
+  } else { 
+    strcpy (image[0].name, (c+1));
+  }
+
+  /* get astrometry information */
+  if (!GetCoords (&image[0].coords, &header)) {
+    Shutdown ("ERROR: no astrometric solution in header");
+  }
+  while (image[0].coords.crval1 < 0) image[0].coords.crval1 += 360.0;
+  while (image[0].coords.crval1 > 360.0) image[0].coords.crval1 -= 360.0;
+
+  /* CERROR in data file is in pixels, convert to 20*arcsec */
+  image[0].cerror = tmp * 50.0 * image[0].coords.cdelt1 * 3600.0;
+  while (image[0].coords.crval1 < 0) image[0].coords.crval1 += 360.0;
+  while (image[0].coords.crval1 > 360.0) image[0].coords.crval1 -= 360.0;
+ 
+  /* get other header info */
+  haveNx = gfits_scan (&header, "NAXIS1",   "%hd", 1, &image[0].NX); 
+  haveNy = gfits_scan (&header, "NAXIS2",   "%hd", 1, &image[0].NY);
+  if (!haveNx && !haveNy) {
+      haveNx = gfits_scan (&header, "IMNAXIS1",   "%hd", 1, &image[0].NX); 
+      haveNy = gfits_scan (&header, "IMNAXIS2",   "%hd", 1, &image[0].NY);
+  }      
+  if (!haveNx || !haveNy) {
+      Shutdown ("ERROR: missing image dimensions in header");
+  }
+
+  gfits_scan (&header, "PHOTCODE", "%s", 1, photcode);
+  Nc = GetPhotcodeCodebyName (photcode);
+  if (!Nc) {
+    Shutdown ("ERROR: photcode %s not found in photcode table", photcode);
+  }
+  image[0].photcode = Nc;
+
+  tmp = 0;
+  gfits_scan (&header, "FLIMIT",   "%lf", 1, &tmp);
+  image[0].detection_limit = tmp * 10.0;
+
+  tmp = 0;
+  gfits_scan (&header, "FSATUR",   "%lf", 1, &tmp);
+  image[0].saturation_limit = tmp * 10.0;
+
+  if (!gfits_scan (&header, "TZERO",   "%d",  1, &image[0].tzero)) {
+    image[0].tzero = parse_time (&header);
+  }
+
+  tmp = 0;
+  gfits_scan (&header, "TRATE",   "%lf", 1, &tmp);
+  image[0].trate = 10000 * tmp;
+
+  tmp = 0;
+  gfits_scan (&header, "AIRMASS", "%lf", 1, &tmp);
+  image[0].secz = tmp;
+
+  /* secz is in units milli-airmass */
+  image[0].Mcal = ALPHA*(image[0].secz - 1.000);
+  image[0].Xm   = NAN_S_SHORT;
+
+  free (header.buffer);
+ 
+  return (image);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/match_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/match_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/match_images.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "delstar.h"
+
+void match_images (Catalog *catalog, Image *image, int Nimage) {
+  
+  int j, k, found;
+  unsigned int *start, *stop;
+
+  /* this must be allocated so future free will not fail */
+  ALLOCATE (catalog[0].image, int, MAX (catalog[0].Nmeasure, 1));
+  if (catalog[0].Naverage == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (FALSE);
+  }
+
+  ALLOCATE (start, unsigned int, Nimage);
+  ALLOCATE (stop,  unsigned int, Nimage);
+  for (j = 0; j < Nimage; j++) {
+    start[j] = image[j].tzero - MAX(0.01*image[j].trate*image[j].NY, 1);
+    stop[j]  = image[j].tzero + MAX(1.01*image[j].trate*image[j].NY, 1);
+  }
+
+  for (j = 0; j < catalog[0].Nmeasure; j++) {
+    found = FALSE;
+    if (catalog[0].measure[j].t == 0) {
+      catalog[0].image[j] = -1;
+      found = TRUE;
+    }
+    for (k = 0; (k < Nimage) && !found; k++) {
+      if ((catalog[0].measure[j].t >= start[k]) && 
+	  (catalog[0].measure[j].t <= stop[k])) {
+	catalog[0].image[j] = k;
+	found = TRUE;
+      }
+    }
+    if (!found) {
+      catalog[0].image[j] = -1;
+      /* fprintf (stderr, "missing: %d %d\n", catalog[0].image[j], catalog[0].measure[j].t); */
+    }
+  }
+  free (start);
+  free (stop);
+}
+
+  /* this routine uses the time of each measurement to match the
+measurement with an image.  Since the measurement is only store to 1
+sec accuracy, which corresponds to roughly 30 rows at nominal speed,
+we can't tell exactly which image the star come from.  However, this
+doesn't matter, and in fact this helps a bit: a measurement from the
+top of one image is the same as from the bottom of the next.
+Therefore, we intentionally blur the edges of the images by 5%, which
+will help to tie together neighboring images... */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/parse_time.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/parse_time.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/delstar/src/parse_time.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "delstar.h"
+
+int parse_time (Header *header) {
+
+  double jd;
+  int Ny, Nf, mode;
+  int Nsec, hour, min, sec, year, month, day;
+  char *py, *pm, *pd, *c;
+  char line[256];
+
+  /* we want to find JD or MJD to get Nsec (seconds since 01/01/1970) */
+
+  /* try JD first */
+  if (strcasecmp (JDKeyword, "NONE")) {
+    uppercase (JDKeyword);
+    gfits_scan (header, JDKeyword, "%lf", 1, &jd);
+    Nsec = (jd - 2440587.5)*86400;
+    return (Nsec);
+  }
+
+  /* try MJD next */
+  if (strcasecmp (MJDKeyword, "NONE")) {
+    uppercase (MJDKeyword);
+    gfits_scan (header, MJDKeyword, "%lf", 1, &jd);
+    Nsec = (jd - 40587.0)*86400;
+    return (Nsec);
+  }
+    
+  /* get UT and DATE */
+  uppercase (UTKeyword);
+  gfits_scan (header, UTKeyword, "%s", 1, line);
+  /* remove ':' characters */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  sscanf (line, "%d %d %d", &hour, &min, &sec);
+
+  /* parse mode line */
+  uppercase (DateMode);
+  for (Ny = 0, c = strchr (DateMode, 'Y'); c != (char ) NULL; c = strchr (c + 1, 'Y'), Ny++);
+  if ((Ny != 2) && (Ny != 4)) {
+    Shutdown ("error in DATE-MODE format: %s", DateMode);
+  }
+  py = strchr (DateMode, 'Y');
+  pm = strchr (DateMode, 'M');
+  pd = strchr (DateMode, 'D');
+  if ((py == (char *) NULL) || (pm == (char *) NULL) || (pd == (char *) NULL)) {
+    Shutdown ("error in DATE-MODE format: %s", DateMode);
+  }
+  if ((py > pm) && (py < pd)) {
+    Shutdown ("error in DATE-MODE format: %s", DateMode);
+  }
+  if ((py > pd) && (py < pm)) {
+    Shutdown ("error in DATE-MODE format: %s", DateMode);
+  }
+  mode = 0;
+  if ((py < pm) && (pm < pd)) { mode = 1; }  /* yyyy-mm-dd */
+  if ((py < pm) && (pm > pd)) { mode = 2; }  /* yyyy-dd-mm */
+  if ((py > pm) && (pm < pd)) { mode = 3; }  /* mm-dd-yyyy */
+  if ((py > pm) && (pm > pd)) { mode = 4; }  /* dd-mm-yyyy */
+  if (!mode) {
+    Shutdown ("error in DATE-MODE format: %s", DateMode);
+  }
+
+  /* parse date entry */
+  uppercase (DateKeyword);
+  gfits_scan (header, DateKeyword, "%s",  1, line);
+  /* remove possible separators: ':', '/' '.', '-' */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  for (c = strchr (line, 0x2f); c != (char *) NULL; c = strchr (line, 0x2f)) { *c = ' '; }
+  for (c = strchr (line, 0x2e); c != (char *) NULL; c = strchr (line, 0x2e)) { *c = ' '; }
+  for (c = strchr (line, 0x2d); c != (char *) NULL; c = strchr (line, 0x2d)) { *c = ' '; }
+
+  Nf = 0;
+  switch (mode) {
+  case 1:
+    Nf = sscanf (line, "%d %d %d", &year, &month, &day);
+    break;
+  case 2:
+    Nf = sscanf (line, "%d %d %d", &year, &day, &month);
+    break;
+  case 3:
+    Nf = sscanf (line, "%d %d %d", &month, &day, &year);
+    break;
+  case 4:
+    Nf = sscanf (line, "%d %d %d", &day, &month, &year);
+    break;
+  }
+  if (Nf != 3) {
+    Shutdown ("error in date entry (%s) or DATE-MODE format (%s)", line, DateMode);
+  }
+
+  if (year > 1000) {
+    if (Ny == 2) {
+      fprintf (stderr, "warning: mode line claims 2 digit year, but 4 digit year found\n");
+    }
+  } else {
+    if (Ny == 4) {
+      fprintf (stderr, "warning: mode line claims 4 digit year, but 2 digit year found\n");
+    }
+    if (year < 50) year += 100;
+    year += 1900;
+  }    
+
+  /* convert yy.mm.dd hh.mm.ss to Nsec since 1970 (jd = 2440587.5) */
+  /* note that in this section, tm_mon has range 1-12, unlike for gmtime () */
+  jd = day - 32075 + (int)(1461*(year + 4800 + (int)(((month)-14)/12))/4)
+    + (int)(367*((month) - 2 - (int)(((month) - 14)/12)*12)/12)
+    - (int)(3*(int)((year + 4900 + (int)(((month) - 14)/12))/100)/4) - 0.5;
+  /* jd is the julian day of the whole day only not the time */
+  Nsec = (jd - 2440587.5)*86400 + 3600.0*hour + min*60.0 + sec;
+  
+  return (Nsec);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+bin
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/Makefile	(revision 22322)
@@ -0,0 +1,48 @@
+default: dvosplit
+help:
+	@echo "make options: dvosplit (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/dvosplit
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+dvosplit     : $(BIN)/dvosplit.$(ARCH)
+all: dvosplit
+
+DVOSPLIT = \
+$(SRC)/dvosplit.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/args.$(ARCH).o \
+$(SRC)/split_averages.$(ARCH).o \
+$(SRC)/split_measures.$(ARCH).o \
+$(SRC)/open_output_catalogs.$(ARCH).o
+
+$(DVOSPLIT)  : $(INC)/dvosplit.h
+
+$(BIN)/dvosplit.$(ARCH) : $(DVOSPLIT)
+
+INSTALL = dvosplit
+
+# dependancy rules for binary code #########################
+$(INSTALL): % : $(BIN)/%.$(ARCH)
+
+%.clean :
+	rm -f $(BIN)/$*.$(ARCH)
+
+%.install:
+	make $(DESTBIN)/$*
+
+install:
+	for i in $(INSTALL); do make $$i.install || exit; done
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/include/dvosplit.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/include/dvosplit.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/include/dvosplit.h	(revision 22322)
@@ -0,0 +1,42 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+# include <sys/time.h>
+# include <time.h>
+# include <zlib.h>
+
+/* solaris requires both of these instead of ip.h:
+   # include <sys/socket.h>
+   # include <netinet/in.h>
+*/
+
+/* linux is happy with this, not solaris */
+# include <netinet/ip.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <glob.h>
+
+typedef struct {
+  int *outref;
+  int *outcat;
+} AveLinks;
+
+int    VERBOSE;
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+
+SkyRegion UserPatch;  // used by MODE CAT
+
+int        main                   PROTO((int argc, char **argv));
+
+int        ConfigInit             PROTO((int *argc, char **argv));
+int        SetSignals             PROTO(());
+void       SetProtect             PROTO((int mode));
+void       TrapSignal             PROTO((int sig));
+int        Shutdown               PROTO((char *format, ...));
+int        args                   PROTO((int argc, char **argv));
+
+Catalog   *open_output_catalogs   PROTO((SkyList *outlist));
+AveLinks  *split_averages         PROTO((Catalog *incatalog, SkyList *outlist, Catalog *outcatalogs));
+int        split_measures         PROTO((Catalog *incatalog, SkyList *outlist, Catalog *outcatalogs, AveLinks *avelinks));
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "dvosplit.h"
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr);
+
+int ConfigInit (int *argc, char **argv) {
+
+  double ZERO_POINT;
+  char *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  GetConfig (config, "CATDIR",                 	"%s",  0, CATDIR);
+  GetConfig (config, "PHOTCODE_FILE",          	"%s",  0, MasterPhotcodeFile);
+
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+
+  /* default mode, format, if not specified */
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  GetConfig (config, "ZERO_PT",                "%lf", 0, &ZERO_POINT);
+  SetZeroPoint (ZERO_POINT);
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+  return (TRUE);
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/SetSignals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/SetSignals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/SetSignals.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "dvosplit.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect these signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored (POSIX.1-1990) */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored (POSIX.1-1990) */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore (POSIX.1-1990) */
+    case SIGCONT:    /* continue - maintain this action (POSIX.1-1990) */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? (POSIX.1-1990) */
+    case SIGURG:     /* socket signal, ignore this (POSIX.1-2001) */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/Shutdown.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/Shutdown.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/Shutdown.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "dvosplit.h"
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format) + 2);
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  fprintf (stderr, "ERROR: addstar halted\n");
+  exit (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/args.c	(revision 22322)
@@ -0,0 +1,87 @@
+# include "dvosplit.h"
+static void help (void);
+
+int args (int argc, char **argv) {
+  
+  int i, N, CONFIRM;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /*** check for command line options ***/
+
+  /*** provide additional data ***/ 
+  /* restrict to a portion of the sky? (REFCAT only) */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  CONFIRM = TRUE;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    CONFIRM = FALSE;
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc == 2) {
+    if (CONFIRM) {
+      fprintf (stderr, "you are splitting the entire sky in one pass\n");
+      fprintf (stderr, "this could be a time consuming operation.  type Ctrl-C within 5 seconds to cancel\n");
+      for (i = 5; i > 0; i--) {
+	fprintf (stderr, "%d.. ", i);
+	usleep (1000000);
+      }
+      fprintf (stderr, "\n");
+    }    
+    return (TRUE);
+  }
+
+  fprintf (stderr, "USAGE: dvosplit (newlevel) [-region (Rmin) (Rmax) (Dmin) (Dmax)]\n");
+  exit (2);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  dvosplit (newlevel)\n\n");
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -region ra ra dec dec 	  : migrate catalogs in specified region\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
+
+/** addstar modes:
+ 
+    addstar (image.smp)  - add cmp/smp image data to db
+    addstar -ref (file.dat) (photcode) 
+    addstar -cat (USNO/2MASS/GSC) -region (ra dec - ra dec)
+
+    -replace : ref/cat - replace existing match (photcode/time)
+    -match   : ref/cat - only add measures to existing averages
+
+    ref types: 
+    ASCII - RA,DEC,M,dM in a table
+
+    addstar 
+
+**/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/dvosplit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/dvosplit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/dvosplit.c	(revision 22322)
@@ -0,0 +1,88 @@
+# include "dvosplit.h"
+
+// dvosplit (outlevel) [-region Rmin Rmax Dmin Dmax]
+int main (int argc, char **argv) {
+
+  int i, j, OUT_DEPTH;
+  SkyTable *sky;
+  SkyList *skylist, *outlist;
+  Catalog incatalog, *outcatalogs;
+  AveLinks *avelinks;
+  char filename[256];
+
+  SetSignals ();
+  ConfigInit (&argc, argv);
+  args (argc, argv);
+
+  OUT_DEPTH = atoi (argv[1]);
+
+  // load the sky table for the existing database
+  sky = SkyTableLoadOptimal (CATDIR, NULL, NULL, SKY_DEPTH_HST, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+
+  // get the list of populated regions
+  skylist  = SkyListByPatch (sky, -1, &UserPatch);
+  
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    fprintf (stderr, "%s\n", skylist[0].regions[i][0].name);
+
+    // if (current level >  out level) skip: cannot currently merge catalogs
+    // if (current level == out level) skip: no action is needed
+    if (skylist[0].regions[i][0].depth >= OUT_DEPTH) continue;
+
+    // set the parameters which guide catalog open/load/create
+    incatalog.filename = skylist[0].filename[i];
+    incatalog.Nsecfilt = GetPhotcodeNsecfilt ();
+    incatalog.catflags = LOAD_NONE;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&incatalog, skylist[0].regions[i], VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", incatalog.filename);
+      exit (2);
+    }
+
+    // skip empty input catalogs
+    if (!incatalog.Naves_disk) {
+      dvo_catalog_unlock (&incatalog);
+      dvo_catalog_free (&incatalog);
+      continue;
+    }
+
+    // change sky.regions[i].depth for these regions
+    outlist = SkyListByPatch (sky, OUT_DEPTH, skylist[0].regions[i]);
+
+    outcatalogs = open_output_catalogs (outlist);
+
+    avelinks = split_averages (&incatalog, outlist, outcatalogs); 
+
+    split_measures (&incatalog, outlist, outcatalogs, avelinks); 
+
+    // XXX missing entries have to be reconstructed if they are desired
+    // split_missings (&incatalog, outlist, outcatalogs, avelinks); 
+
+    free (avelinks[0].outref);
+    free (avelinks[0].outcat);
+
+    dvo_catalog_unlock (&incatalog);
+
+    for (j = 0; j < outlist[0].Nregions; j++) {
+      dvo_catalog_unlock (&outcatalogs[j]);
+    }
+
+    // adjust depth
+    skylist[0].regions[i][0].table = FALSE;
+    for (j = 0; j < outlist[0].Nregions; j++) {
+      outlist[0].regions[j][0].table = TRUE;
+    }
+  }
+
+  // save sky table copy
+  sprintf (filename, "%s/SkyTable.fits", CATDIR);
+  check_file_access (filename, TRUE, VERBOSE);
+  if (!SkyTableSave (sky, filename)) {
+    fprintf (stderr, "ERROR: failed to save sky table for %s\n", CATDIR);
+    exit (1);
+  }
+
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/open_output_catalogs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/open_output_catalogs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/open_output_catalogs.c	(revision 22322)
@@ -0,0 +1,27 @@
+# include "dvosplit.h"
+
+Catalog *open_output_catalogs (SkyList *outlist) {
+
+  int i;
+  Catalog *outcatalogs;
+
+  ALLOCATE (outcatalogs, Catalog, outlist[0].Nregions);
+
+  // an error exit status here is a significant error
+  for (i = 0; i < outlist[0].Nregions; i++) {
+    
+    // set the parameters which guide catalog open/load/create
+    outcatalogs[i].filename  = outlist[0].filename[i];
+    outcatalogs[i].Nsecfilt  = GetPhotcodeNsecfilt ();
+    outcatalogs[i].catflags  = LOAD_NONE;
+    outcatalogs[i].catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    outcatalogs[i].catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+
+    if (!dvo_catalog_open (&outcatalogs[i], outlist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", outcatalogs[i].filename);
+      exit (2);
+    }
+  }
+  
+  return (outcatalogs);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/split_averages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/split_averages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/split_averages.c	(revision 22322)
@@ -0,0 +1,118 @@
+# include "dvosplit.h"
+# define NROWS 100000 /* ~10MB per block for measures */
+# define DNOUT 1000
+
+AveLinks *split_averages (Catalog *incatalog, SkyList *outlist, Catalog *outcatalogs) {
+
+  double inR, inD;
+  int n, block, ave, cat, averef, Nblocks, Ncat, Nout, Nsecfilt;
+  int *outref, *outcat, *outmem;
+  AveLinks *avelinks;
+
+  ALLOCATE (outref, int, incatalog[0].Naves_disk);
+  ALLOCATE (outcat, int, incatalog[0].Naves_disk);
+  ALLOCATE (outmem, int, outlist[0].Nregions);
+
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  // allocate enough space for these output buffers: use Nsecfilt + 1 incase the file
+  // contains primary photcodes, which will increase Nsecfilt by one.
+  for (cat = 0; cat < outlist[0].Nregions; cat++) {
+    outmem[cat] = DNOUT;
+    REALLOCATE (outcatalogs[cat].average, Average, outmem[cat]);
+    REALLOCATE (outcatalogs[cat].secfilt, SecFilt, outmem[cat]*(Nsecfilt + 1));
+  }
+
+  // split out the average & secfilt entries:
+  incatalog[0].catflags = LOAD_AVES | LOAD_SECF;
+  Nblocks = incatalog[0].Naves_disk / NROWS;
+  if (incatalog[0].Naves_disk % NROWS) Nblocks ++;
+  for (block = 0; block < Nblocks; block++) {
+
+    // read up to NROWS at a time
+    dvo_catalog_load_segment (incatalog, VERBOSE, block*NROWS, NROWS);
+    fprintf (stderr, "splitting %s (averages) .. %d of %d\n", incatalog[0].filename, block, Nblocks);
+
+    assert (block*NROWS == incatalog[0].Naves_off);
+
+    // distribute data to the output catalogs
+    for (ave = 0; ave < incatalog[0].Naverage; ave++) {
+      averef = ave + incatalog[0].Naves_off;
+	
+      inR = incatalog[0].average[ave].R;
+      inD = incatalog[0].average[ave].D;
+
+      // which of the outcatalogs contains this coordinate?
+
+      Ncat = -1;
+      for (cat = 0; cat < outlist[0].Nregions; cat++) {
+	if (inR < outlist[0].regions[cat][0].Rmin) continue;
+	if (inR > outlist[0].regions[cat][0].Rmax) continue;
+	if (inD < outlist[0].regions[cat][0].Dmin) continue;
+	if (inD > outlist[0].regions[cat][0].Dmax) continue;
+	Ncat = cat;
+	break;
+      }
+
+      if (Ncat == -1) {
+	fprintf (stderr, "WARNING: missed %d (%f, %f)\n", averef, inR, inD);
+	continue;
+      }
+
+      Nout = outcatalogs[Ncat].Naverage;
+      outref[averef] = Nout + outcatalogs[Ncat].Naves_off;
+      outcat[averef] = Ncat;
+
+      // assign the value to the next element of the output catalog
+      outcatalogs[Ncat].average[Nout] = incatalog[0].average[ave];
+      outcatalogs[Ncat].Naverage ++;
+
+      // update secfilt at the same time
+      for (n = 0; n < Nsecfilt; n++) {
+	outcatalogs[Ncat].secfilt[Nout*Nsecfilt + n] = incatalog[0].secfilt[ave*Nsecfilt + n];
+	outcatalogs[Ncat].Nsecf_mem++;
+      }
+
+      if (outcatalogs[Ncat].Naverage >= outmem[Ncat]) {
+	outmem[Ncat] += DNOUT;
+	REALLOCATE (outcatalogs[Ncat].average, Average, outmem[Ncat]);
+	REALLOCATE (outcatalogs[Ncat].secfilt, SecFilt, outmem[Ncat]*(Nsecfilt + 1));
+      }
+    }
+    dvo_catalog_free_data (incatalog);
+
+    // double check the values of Naverage, Nsecf_mem?
+
+    // write out the new values
+    for (cat = 0; cat < outlist[0].Nregions; cat++) {
+      outcatalogs[cat].catflags = LOAD_AVES | LOAD_SECF;
+
+      dvo_catalog_save (&outcatalogs[cat], VERBOSE);
+      // fprintf (stderr, "secfilt: %d %d %d %d\n", outcatalogs[cat].Nsecf_mem, outcatalogs[cat].Nsecf_disk, outcatalogs[cat].Nsecf_off, outcatalogs[cat].Naverage, outcatalogs[cat].Nsecfilt);
+
+      // advance the pointers and free the current data
+      // XXX these should be done within save segment:
+      outcatalogs[cat].Naves_disk += outcatalogs[cat].Naverage;
+      outcatalogs[cat].Naves_off  += outcatalogs[cat].Naverage;
+      outcatalogs[cat].Nsecf_disk += outcatalogs[cat].Nsecfilt * outcatalogs[cat].Naverage;
+      outcatalogs[cat].Nsecf_off  += outcatalogs[cat].Nsecfilt * outcatalogs[cat].Naverage;
+      outcatalogs[cat].Nsecfilt    = Nsecfilt;
+
+      outcatalogs[cat].Naverage    = 0;
+      outcatalogs[cat].Nsecf_mem   = 0;
+    }
+  }
+
+  for (cat = 0; cat < outlist[0].Nregions; cat++) {
+    dvo_catalog_free_data (&outcatalogs[cat]);
+  }
+
+  free (outmem);
+
+  ALLOCATE (avelinks, AveLinks, 1);
+  avelinks[0].outref = outref;
+  avelinks[0].outcat = outcat;
+
+
+  return (avelinks);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/split_measures.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/split_measures.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/split_measures.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include "dvosplit.h"
+# define NROWS 100000 /* ~10MB per row for measures */
+# define DNOUT 1000
+
+int split_measures (Catalog *incatalog, SkyList *outlist, Catalog *outcatalogs, AveLinks *avelinks) {
+
+  int block, meas, cat, Nblocks, Ncat, Nout, averef;
+  int *outref, *outcat, *outmem;
+
+  outref = avelinks->outref;
+  outcat = avelinks->outcat;
+  ALLOCATE (outmem, int, outlist[0].Nregions);
+
+  // allocate enough space for the output buffer
+  for (cat = 0; cat < outlist[0].Nregions; cat++) {
+    outmem[cat] = DNOUT;
+    REALLOCATE (outcatalogs[cat].measure, Measure, outmem[cat]);
+  }
+
+  // split out the measure entries:
+  incatalog[0].catflags = LOAD_MEAS;
+  Nblocks = incatalog[0].Nmeas_disk / NROWS;
+  if (incatalog[0].Nmeas_disk % NROWS) Nblocks ++;
+  for (block = 0; block < Nblocks; block++) {
+
+    // read up to NROWS at a time
+    dvo_catalog_load_segment (incatalog, VERBOSE, block*NROWS, NROWS);
+    fprintf (stderr, "splitting %s (measures) .. %d of %d\n", incatalog[0].filename, block, Nblocks);
+
+    assert (block*NROWS == incatalog[0].Nmeas_off);
+
+    for (meas = 0; meas < incatalog[0].Nmeasure; meas++) {
+
+      averef = incatalog[0].measure[meas].averef;
+      Ncat = outcat[averef];
+
+      Nout = outcatalogs[Ncat].Nmeasure;
+      outcatalogs[Ncat].measure[Nout] = incatalog[0].measure[meas];
+      outcatalogs[Ncat].measure[Nout].averef = outref[averef];
+      outcatalogs[Ncat].Nmeasure++;
+
+      if (outcatalogs[Ncat].Nmeasure >= outmem[Ncat]) {
+	outmem[Ncat] += DNOUT;
+	REALLOCATE (outcatalogs[Ncat].measure, Measure, outmem[Ncat]);
+      }
+    }
+    dvo_catalog_free_data (incatalog);
+
+    for (cat = 0; cat < outlist[0].Nregions; cat++) {
+      outcatalogs[cat].catflags = LOAD_MEAS;
+      dvo_catalog_save (&outcatalogs[cat], VERBOSE);
+
+      outcatalogs[cat].Nmeas_disk += outcatalogs[cat].Nmeasure;
+      outcatalogs[cat].Nmeas_off  += outcatalogs[cat].Nmeasure;
+      outcatalogs[cat].Nmeasure    = 0;
+
+    }
+  }
+
+  for (cat = 0; cat < outlist[0].Nregions; cat++) {
+    dvo_catalog_free_data (&outcatalogs[cat]);
+  }
+
+  free (outmem);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/split_missings.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/split_missings.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/dvosplit/src/split_missings.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "dvosplit.h"
+# define NROWS 100000 /* ~10MB per row for missings */
+
+int split_missings (Catalog *incatalog, SkyList *outlist, Catalog *outcatalogs, AveLinks *avelinks) {
+
+  int *outref, *outcat;
+
+  outref = avelinks->outref;
+  outcat = avelinks->outcat;
+
+  // allocate enough space for the output buffer
+  for (cat = 0; cat < outlist[0].Nregions; cat++) {
+    REALLOCATE (outcatalog[cat].missing, Missing, NROWS);
+  }
+
+  // split out the missing entries:
+  incatalog[0].catflags = LOAD_MISS;
+  Nblocks = incatalog[0].Nmiss_disk / NROWS;
+  if (incatalog[0].Nmiss_disk % NROWS) Nblocks ++;
+  for (block = 0; block < Nblocks; block++) {
+
+    // read up to NROWS at a time
+    dvo_catalog_load_segment (incatalog, VERBOSE, block*NROWS, NROWS);
+
+    for (miss = 0; miss < incatalog[0].Nmissing; miss++) {
+
+      averef = incatalog[0].missing[miss].averef;
+      Ncat = outcat[averef];
+
+      Nout = outcatalog[Ncat].Nmissing;
+      outcatalog[Ncat].missing[Nout] = incatalog[0].missing[miss];
+      outcatalog[Ncat].missing[Nout].averef = outref[averef];
+      outcatalog[Ncat].Nmissing++;
+    }
+
+    for (cat = 0; cat < outlist[0].Nregions; cat++) {
+      outcatalogs[cat].catflags = LOAD_MISS;
+      dvo_catalog_save_segment (&outcatalog[cat], VERBOSE);
+
+      outcatalog[cat].Nmiss_disk += outcatalog[cat].Nmissing;
+      outcatalog[cat].Nmiss_off  += outcatalog[cat].Nmissing;
+      outcatalog[cat].Nmissing    = 0;
+
+    }
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/Makefile	(revision 22322)
@@ -0,0 +1,37 @@
+default: elixir
+help:
+	@echo "make options: elixir (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/elixir
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+elixir: $(BIN)/elixir.$(ARCH)
+install: $(DESTBIN)/elixir
+
+ELIXIR = \
+$(SRC)/elixir.$(ARCH).o			$(SRC)/ConfigInit.$(ARCH).o		\
+$(SRC)/MachineOps.$(ARCH).o		$(SRC)/FifoOps.$(ARCH).o		\
+$(SRC)/QueueOps.$(ARCH).o		$(SRC)/CheckCluster.$(ARCH).o		\
+$(SRC)/CheckDepend.$(ARCH).o		$(SRC)/CheckEndingState.$(ARCH).o	\
+$(SRC)/CheckMachineStatus.$(ARCH).o	$(SRC)/CheckProcess.$(ARCH).o		\
+$(SRC)/InitProcess.$(ARCH).o		$(SRC)/StartMachine.$(ARCH).o		\
+$(SRC)/SockScan.$(ARCH).o		$(SRC)/LoadPending.$(ARCH).o		\
+$(SRC)/Photcodes.$(ARCH).o		$(SRC)/rconnect.$(ARCH).o		\
+$(SRC)/DefineProcesses.$(ARCH).o        $(SRC)/ProcessOps.$(ARCH).o		\
+$(SRC)/DumpStatus.$(ARCH).o		$(SRC)/CheckMessages.$(ARCH).o		\
+$(SRC)/MsgOps.$(ARCH).o			$(SRC)/LogOpen.$(ARCH).o		\
+$(SRC)/ConfigPID.$(ARCH).o		$(SRC)/RemoteOps.$(ARCH).o
+
+$(ELIXIR): $(INC)/elixir.h
+$(BIN)/elixir.$(ARCH): $(ELIXIR)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,24 @@
+
+- elixir-1-5 : 2006.08.23
+  * converted to gfits APIs (forces libfits 1.6)
+
+elixir-1-4:
+  changed printing formats for pointers to %p
+  using updated version of mkdirhier (libohana 1.7)
+
+elixir-1-3:
+  libohana / libfits were modified, forcing the 
+  addition of fitsio.h to controller.h
+
+elixir-1-2:
+ - changing setlockfile2 to fsetlockfile
+   (dropping use of fd version of APIs)
+   elixir should eventually be superceeded by sched/pcontrol,
+   but perhaps not at CFHT.
+
+elixir-1-1:
+ - minor cleanup of rconnect process
+ - ignore SIG_CHLD now 
+
+elixir-1-0:
+ - created tag for first elixir distribution
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/doc/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/doc/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/doc/README	(revision 22322)
@@ -0,0 +1,13 @@
+
+There are two parts to elixir which are a bit dangerous (and limit the
+possibilities).  
+
+First, the number of arguments which describe an object is limited to
+7.  This is limited in LoadPending.c and is due to the dumb way I
+parse these input lines.
+
+Second, in a similar way, the number of arguments to a command line
+argument (the 'process.arg' entries) is limited to 5, again for about
+the same reason.  this is in 'ParseLine' in ProcessOps.c
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/doc/notes.txt	(revision 22322)
@@ -0,0 +1,40 @@
+
+
+typedef struct {
+  char *buffer;
+  int   Nalloc;
+  int   Nmaxread;
+  int   Nextra;
+  int   Nlast;
+  int   Nbuffer;
+} Fifo;
+
+typedef struct {
+  int argc; char **argv; /* a list of words that define this object */
+  struct timeval start, accum, timer;
+  int   status;
+  char *logfile;
+  char *lastproc;
+} Object;
+
+typedef struct {
+  Object **object;
+  int    Nobject;
+  int    NOBJECT;
+} Queue;
+
+typedef struct {
+  char   *hostname;
+  int     rsock, wsock;
+  int     status; /* idle, busy, etc... */
+  struct  timeval start, accum, timer;
+  Fifo    fifo;
+  int     code;
+  Object *object;
+} Machine;
+
+
+currently, the transport is /usr/bin/rsh, defined in InitMachines.c 
+
+the shell on the remote machines is /bin/tcsh, defined by rconnect.c
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/include/elixir.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/include/elixir.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/include/elixir.h	(revision 22322)
@@ -0,0 +1,173 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# include <signal.h>
+# include <errno.h>
+
+# define CR 0x0D
+# define LF 0x0A
+
+# define IDLE     0x00
+# define BUSY     0x01
+# define DOWN     0x02
+# define DONE     0x04
+# define MESSAGE  0x08
+# define SUCCESS  0x10
+# define FAILURE  0x20
+# define JOBDONE  0x40
+# define WAITING  0x80
+# define ERROR    0x100
+# define CRASH    0x200
+# define TIMEOUT  0x400
+
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+/* represents a socketed connection to a remote host
+   which may or may not be currently running a process */
+
+typedef struct {
+  char *buffer;
+  int   Nalloc;
+  int   Nmaxread;
+  int   Nextra;
+  int   Nlast;
+  int   Nbuffer;
+} Fifo;
+
+typedef struct {
+  int argc; char **argv; /* a list of words that define this object */
+  struct timeval start, accum, timer;
+  int   status;
+  char *logfile;
+  char *lastproc;
+} Object;
+
+typedef struct {
+  Object **object;
+  int    Nobject;
+  int    NOBJECT;
+} Queue;
+
+typedef struct {
+  char   *hostname;
+  int     rsock, wsock, pid;
+  int     status; /* idle, busy, etc... */
+  struct  timeval start, accum, timer, quiet;
+  Fifo    fifo;
+  int     code;
+  Object *object;
+} Machine;
+
+typedef struct {
+  Machine **machine;
+  int     Nmachine;
+  int     NMACHINE;
+} Cluster;
+
+typedef struct {
+  char    *name;
+  Queue   *pending;
+  Queue   *success;
+  Queue   *failure;
+  Cluster *cluster;
+  int     argc;
+  char    **argv;
+} Process;
+
+/* we have one ProcessTimer for each existing machine
+   each ProcessTimer has one element for each process */
+typedef struct {
+  struct timeval *timer;
+  double       *timesum;
+  int          *Njobs;
+  int          *active;
+  Machine      *machine;
+  Process     **process;
+} ProcessTimers;
+
+int   VERBOSE;
+char  CONNECT[128];
+
+/* prototypes */
+
+Process **DefineProcesses (Process *global, int *nprocess, char *config);
+Process **GetProcessInfo (Process **gb, int *np, int *no);
+char 	 *BaseFilename (char *file);
+char 	 *BuildCode (char *line);
+char 	 *BuildName (char *line);
+char 	 *ConfigInit (int *argc, char **argv);
+Process  *ConfigProcess (char *config, char *procname);
+char 	 *ExpandEntry (char *entry, int argc, char **argv);
+Machine  *GetMachine (Cluster *cluster);
+Object   *GetObject (Queue *queue);
+char 	 *GetPhotcode (char *file);
+char 	 *GetPhotcodeExt (char *file);
+char 	 *GetPhotcodeMef (char *file);
+Machine  *GrabMachine ();
+Cluster  *InitCluster ();
+Process  *InitProcess (char *name, Queue *pending, Queue *failure, int (*mkargs)());
+Queue    *InitQueue ();
+FILE     *LogOpen (char *filename);
+char 	 *PathFilename (char *file);
+char 	 *RootFilename (char *file);
+int 	  CheckCluster (Cluster *cluster, Queue *success, Queue *failure, Queue *pending);
+int 	  CheckDepend (Object *object, int argc, char **argv, int *argd);
+int 	  CheckEndingState (Process *global, int Nobjects, int Dynamic);
+int 	  CheckMachineStatus (Machine *machine);
+int 	  CheckMessages ();
+int 	  CheckProcess (Process *process);
+void 	  CloseMachine (Machine *machine);
+void 	  ConfigPID (char *PIDFile);
+int 	  ConnectMachine (Machine *machine);
+void 	  DownMachine (Machine *machine);
+void 	  DumpFinished (Queue *queue, int Nstart, char *filename);
+void 	  DumpMachineStatus (FILE *f);
+int 	  DumpProcessTimes (char *filename);
+int 	  DumpStatus (char *filename);
+void 	  ElixirStop ();
+int 	  FlushFifo (Fifo *fifo);
+void 	  FreeArgs (int argc, char **argv, int *argd);
+void 	  FreeFifo (Fifo *fifo);
+int 	  GetDynamicState ();
+double    GetTimeout ();
+void 	  HaltElixir (char *pidfile);
+int 	  HalttoRestart (char *pidfile);
+void 	  IdleMachine (Machine *machine);
+int 	  InitFifo (Fifo *fifo, int Nalloc, int Nextra);
+void 	  InitMachines (char *config);
+int 	  InitMsgFile (char *file);
+void 	  InitProcessTimers (Process **process, int Nprocess);
+void 	  KillElixir (char *pidfile);
+int 	  LoadPID (char *file, pid_t *pid, char *username, char *machine);
+int 	  LoadPending (Process *global, char *inlist, int *state, int *dynamic);
+int 	  MakeArgs (Process *process, Object *object, int *cargc, char ***cargv, int **cargd);
+void 	  ParseLine (char *testline, int argc, char **argv, int *depend, char **outline);
+void 	  PushMachine (Machine *machine, Cluster *cluster);
+void 	  PushObject (Queue *queue, Object *object);
+void 	  PutMachine (Machine *machine, Cluster *cluster);
+void 	  PutObject (Queue *queue, Object *object);
+int 	  ReadMsg (char *fifo, char **message);
+int 	  ReadtoFifo (Fifo *fifo, int sock);
+void 	  RegisterTimeout (double value);
+void 	  RemovePID ();
+void 	  Restart (char **argv);
+void 	  RestartMachines ();
+void 	  SIG_DIE (int sig);
+void 	  SIG_MESSAGE (int sig);
+void 	  SIG_PIPE (int sig);
+void 	  SIG_RELOAD (int sig);
+void 	  SIG_STOP (int sig);
+int 	  SetExitTimer ();
+void 	  SetMessageFile (char *filename);
+int 	  ShiftFifo (Fifo *fifo);
+void 	  Shutdown (int status);
+int 	  SockScan (char *string, Fifo *fifo, int sock);
+int 	  StartMachine (Machine *machine, Object *object, int argc, char **argv);
+void 	  StartProcessTimer (Process *process, Machine *machine);
+void 	  StatusElixir (char *pidfile, char *msgfile);
+void 	  StopProcessTimer (Machine *machine);
+int 	  TestMachine (Machine *machine);
+int 	  WaitMsg (char *fifo, char **message, double maxdelay);
+int 	  WriteMsg (char *fifo, char *message);
+int 	  memstr (char *m1, char *m2, int n);
+int 	  rconnect (char *hostname, char *command, int *rsock, int *wsock);
+void GetConfig (char *config, char *field, char *format, int N, void *ptr);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckCluster.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckCluster.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckCluster.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "elixir.h"
+
+int CheckCluster (Cluster *cluster, Queue *success, Queue *failure, Queue *pending) {
+
+  int i, j, status;
+  Machine *machine, **tmachine;
+  Object *object;
+  FILE *logfile;
+
+  /* check current status of all machines in cluster, idle those finished */
+  for (i = 0; i < cluster[0].Nmachine; i++) {
+    
+    machine = cluster[0].machine[i];
+    status = CheckMachineStatus (machine);
+
+    object = machine[0].object;
+    logfile = LogOpen (object[0].logfile);
+
+    if (status & MESSAGE) {
+      fprintf (logfile, "%s @ %s:", object[0].argv[0], machine[0].hostname);
+      fwrite (&machine[0].fifo.buffer[machine[0].fifo.Nlast], 1, machine[0].fifo.Nbuffer - machine[0].fifo.Nlast, logfile);
+    }      
+
+    if (status & DOWN) {
+      fprintf (stderr, "%s @ %s is down\n", object[0].argv[0], machine[0].hostname);
+      fprintf (logfile, "%s @ %s is down\n", object[0].argv[0], machine[0].hostname);
+      object[0].timer.tv_sec = 0; /* reset timer */
+      StopProcessTimer (machine);
+      /* PutObject (failure, object); */
+      /* if machine crashes, retry object later */
+      PutObject (pending, object);
+      DownMachine (machine);
+      cluster[0].machine[i] = (Machine *) NULL;
+      goto escape;
+    }
+    if (status & DONE) {
+      object[0].timer.tv_sec = 0; /* reset timer */
+      StopProcessTimer (machine);
+      fprintf (logfile, "%s @ %s is done\n", object[0].argv[0], machine[0].hostname);
+      if (status & SUCCESS) {
+	PutObject (success, object);
+      } 
+      if (status & FAILURE) {
+	PutObject (failure, object);
+      } 
+      IdleMachine (machine);
+      cluster[0].machine[i] = (Machine *) NULL;
+      goto escape;
+    }
+    if (status & CRASH) {
+      object[0].timer.tv_sec = 0; /* reset timer */
+      StopProcessTimer (machine);
+      fprintf (logfile, "%s @ %s had process crash\n", object[0].argv[0], machine[0].hostname);
+      PutObject (failure, object);
+      IdleMachine (machine);
+      cluster[0].machine[i] = (Machine *) NULL;
+      goto escape;
+    }
+    if (status & ERROR) {
+      object[0].timer.tv_sec = 0; /* reset timer */
+      StopProcessTimer (machine);
+      fprintf (logfile, "%s @ %s has an odd status\n", object[0].argv[0], machine[0].hostname);
+      PutObject (failure, object);
+      DownMachine (machine);
+      cluster[0].machine[i] = (Machine *) NULL;
+      goto escape;
+    }
+    if (status & TIMEOUT) {
+      object[0].timer.tv_sec = 0; /* reset timer */
+      /* don't include TIMEOUT in timer stats StopProcessTimer (machine); */
+      fprintf (logfile, "%s @ %s timed out, retrying\n", object[0].argv[0], machine[0].hostname);
+      PutObject (pending, object);
+      IdleMachine (machine);
+      cluster[0].machine[i] = (Machine *) NULL;
+      goto escape;
+    }
+
+  escape:
+    if (logfile != stderr) fclose (logfile);
+  }
+
+  /* remove idle machines from this cluster's list */
+  ALLOCATE (tmachine, Machine *, cluster[0].NMACHINE);
+  for (j = i = 0; i < cluster[0].Nmachine; i++) {
+    if (cluster[0].machine[i] != (Machine *) NULL) {
+      tmachine[j] = cluster[0].machine[i];
+      j++;
+    }
+  }
+  free (cluster[0].machine);
+  cluster[0].machine = tmachine;
+  cluster[0].Nmachine = j;
+
+  return (TRUE);
+}
+
+
+/*
+
+  possible ending states:
+
+  DONE & SUCCESS = process finished normally & succeeded
+  DONE & FAILURE = process finished normally & failed
+
+  CRASH = process crashed
+  DOWN  = machine crashed
+  IDLE  = no process (should never show up here!)
+  ERROR = unexpected state!
+
+  whenever an object is done, in any of the possible states, the
+  arg dependency timer must be reset 
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckDepend.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckDepend.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckDepend.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "elixir.h"
+
+int CheckDepend (Object *object, int argc, char **argv, int *argd) {
+
+  int i;
+  struct stat filestat;
+  uid_t uid;
+  gid_t gid;
+  int status;
+  struct timeval now;
+  FILE *logfile;
+  double dtime;
+
+  uid = getuid();
+  gid = getgid();
+
+  for (i = 0; i < argc; i++) {
+    if (argd[i]) {
+      /* check permission to read file */
+      status = stat (argv[i], &filestat);
+      
+      /* continue if file exists and is accessible */
+      if (!status) {
+	if ((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR)) continue;
+	if ((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP)) continue;
+	if (filestat.st_mode & S_IROTH) continue;
+      }
+
+      /* if the file doesn't exist or is inaccessible, check the timer on this object */
+      if (object[0].timer.tv_sec == 0) {
+	gettimeofday (&object[0].timer, (void *) NULL);
+	return (0);
+      } 
+      
+      gettimeofday (&now, (void *) NULL);
+      dtime = DTIME (now, object[0].timer);
+      if (dtime > argd[i]) {
+	logfile = LogOpen (object[0].logfile);
+	fprintf (logfile, "timeout on %s: %f > %d\n", argv[i], dtime, argd[i]);
+	if (logfile != stderr) fclose (logfile);
+	object[0].status &= TIMEOUT;
+	return (2);
+      } else {
+	return (0);
+      }
+    }
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckEndingState.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckEndingState.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckEndingState.c	(revision 22322)
@@ -0,0 +1,80 @@
+# include "elixir.h"
+
+static int Nsuccess = 0;
+static int Nfailure = 0;
+static double timeout;
+struct timeval start;
+static int Reload = FALSE;
+
+int CheckEndingState (Process *global, int Nobjects, int Dynamic) {
+  
+  int Ndone;
+  struct timeval now;
+  double dtime;
+  
+  /* check the success and failure queues for newly added objects */
+  
+  if (global[0].success[0].Nobject > Nsuccess) {
+    DumpFinished (global[0].success, Nsuccess, global[0].argv[0]);
+    Nsuccess = global[0].success[0].Nobject;
+  }
+  if (global[0].failure[0].Nobject > Nfailure) {
+    DumpFinished (global[0].failure, Nfailure, global[0].argv[1]);
+    Nfailure = global[0].failure[0].Nobject;
+  }
+
+  if (Dynamic) return  (TRUE);
+  
+  Ndone = 0;
+  Ndone += global[0].success[0].Nobject;
+  Ndone += global[0].failure[0].Nobject;
+
+  if (Reload) {
+    gettimeofday (&now, (void *) NULL);
+    dtime = DTIME (now, start);
+    if (dtime > timeout) return (FALSE);
+  }
+  
+  if (Ndone == Nobjects)  
+    return (FALSE);
+  else
+    return (TRUE);
+}
+
+void DumpFinished (Queue *queue, int Nstart, char *filename) {
+  
+  int i, j;
+  FILE *f;
+  
+  f = fopen (filename, "a");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't output status of finished objects to %s\n", filename);
+    f = stderr;
+  }
+  
+  for (i = Nstart; i < queue[0].Nobject; i++) {
+    
+    for (j = 0; j < queue[0].object[i][0].argc; j++) {
+      fprintf (f, "%s ", queue[0].object[i][0].argv[j]);
+    }
+    fprintf (f, "%08x %s\n", queue[0].object[i][0].status, queue[0].object[i][0].lastproc);
+  }
+  
+  if (f != stderr) fclose (f);
+  
+}
+
+int SetExitTimer () {
+
+  timeout = GetTimeout ();
+  gettimeofday (&start, (void *) NULL);
+  Reload = TRUE;
+  return (TRUE);
+}
+
+/* 
+   
+   global.success = global[0].argv[0]
+   global.failure = global[0].argv[1]
+   
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckMachineStatus.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckMachineStatus.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckMachineStatus.c	(revision 22322)
@@ -0,0 +1,131 @@
+# include "elixir.h"
+# define MAXTIME 120
+
+static double timeout;
+
+void RegisterTimeout (double value) {
+ timeout = value;
+}
+
+double GetTimeout () {
+  return (timeout);
+}
+
+int CheckMachineStatus (Machine *machine) {
+
+  unsigned int status;
+  int Nread;
+  struct timeval now;
+  FILE *logfile;
+  float dtime;
+
+  status = machine[0].status;
+
+  if (status == IDLE) return (status);
+  if (status == DONE) return (status);
+  if (status == DOWN) return (status);
+
+  status &= ~MESSAGE; /* clear MESSAGE status */
+  
+  /* read from socket into fifo */
+  ShiftFifo (&machine[0].fifo);
+
+  Nread = ReadtoFifo (&machine[0].fifo, machine[0].rsock);
+  
+  /* evaluate data in message */
+  switch (Nread) {
+  case 0:
+    status = DOWN;
+    break;
+  case -1:
+    status |= BUSY;
+    gettimeofday (&now, (void *) NULL);
+    dtime = DTIME (now, machine[0].quiet);
+    if (dtime > timeout) {
+      logfile = LogOpen (machine[0].object[0].logfile);
+      fprintf (logfile, "%s: process is taking too long, giving up\n", machine[0].hostname);
+      if (logfile != stderr) fclose (logfile);
+      fprintf (stderr, "process on %s hung?  killing and retrying...\n", machine[0].hostname);
+      /* interrupt process which is running */
+      kill (machine[0].pid, SIGINT);
+      status = TIMEOUT;
+      /* status = (JOBDONE | FAILURE); */
+    }
+    break;
+  default:
+    status |= MESSAGE;
+    gettimeofday (&machine[0].quiet, (void *) NULL);
+    if (memstr (machine[0].fifo.buffer, "ERROR",        machine[0].fifo.Nbuffer))
+      status |= FAILURE;
+    if (memstr (machine[0].fifo.buffer, "SUCCESS",      machine[0].fifo.Nbuffer))
+      status |= SUCCESS;
+    if (memstr (machine[0].fifo.buffer, "PROCESS DONE", machine[0].fifo.Nbuffer))
+      status |= JOBDONE;
+    break;
+  }
+
+  /* evaluate status completion-type status signals */
+  switch (status & (JOBDONE | SUCCESS | FAILURE)) {
+  case FAILURE:
+  case SUCCESS:
+  case JOBDONE:
+    if (status & WAITING) break;
+    status |= WAITING;
+    gettimeofday (&machine[0].timer, (void *) NULL);
+    break;
+  case (JOBDONE | SUCCESS):
+  case (JOBDONE | FAILURE):
+    status |= DONE;
+    break;
+  case (FAILURE | SUCCESS):
+  case (JOBDONE | FAILURE | SUCCESS):
+    status |= ERROR;
+    break;
+  default:
+    break;
+  }
+
+  /* check for completion timeout */
+  if (status & WAITING) {
+    gettimeofday (&now, (void *) NULL);
+    if (DTIME (now, machine[0].timer) > MAXTIME) {
+      switch (status & (JOBDONE | SUCCESS | FAILURE)) {
+      case JOBDONE:
+	status |= CRASH;
+	break;
+      case SUCCESS:
+      case FAILURE:
+      default:
+	status |= ERROR;
+	break;
+      }
+    }
+  }
+
+  machine[0].object[0].status = status;
+  machine[0].status = status;
+
+  return (status);
+
+}
+
+
+/* machine status:
+
+   the machine can have several possible statuss, made up of specific bits
+   in the status variable:
+
+   IDLE 0x00 - no process running on host
+   DOWN 0x01 - no connection to host
+   BUSY 0x02 - no message on socket
+   DONE 0x04 - process completed
+
+   MESSAGE 0x08 - message on socket
+   FAILURE 0x10 - process reported 'failure'
+   SUCCESS 0x20 - process reported 'success'
+   JOBDONE 0x40 - process reported 'jobdone'
+   WAITING 0x80 - process reported 'jobdone'
+
+   ERROR   0x010 - unexpected return values
+   CRASH   0x020 - process crashed
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckMessages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckMessages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckMessages.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "elixir.h"
+
+static struct timeval then = {0,0};
+static char *MessageFile = (char *) NULL;
+
+void SetMessageFile (char *filename) {
+  MessageFile = filename;
+}
+
+int CheckMessages () {
+  
+  int status, Message;
+  struct timeval now;
+  char *message, *p, *p2;
+  char file[256], cmd[64];
+
+  /* we should only do this check every 200ms or so */
+  gettimeofday (&now, (void *) NULL);
+  if (DTIME (now, then) < 0.2) return (1);
+
+  if (MessageFile == (char *) NULL) {
+    fprintf (stderr, "not ready for messages\n");
+    return (FALSE);
+  }
+
+  status = WaitMsg (MessageFile, &message, 0.1);
+  then = now;
+  if (!status) return (FALSE);
+
+  Message = 0;
+
+  /* loop over all lines in message */
+  p = message;
+  while (strlen (p) > 0) {
+    p2 = strchr (p, '\n');
+    if (p2 == (char *) NULL) {
+      p2 = p + strlen (p) - 1;
+    } else {
+      *p2 = 0;
+    }
+
+    sscanf (p, "%s %s", cmd, file);
+
+    if (!strcasecmp (cmd, "ALIVE"))  {
+      WriteMsg (file, "BUSY");
+    }
+    if (!strcasecmp (cmd, "TIMES")) {
+      DumpProcessTimes (file);
+    }
+    if (!strcasecmp (cmd, "STATUS")) {
+      DumpStatus (file); 
+    }
+    if (!strcasecmp (cmd, "STOP"))   {
+      WriteMsg (file, "Elixir will end when processes are done");
+      ElixirStop ();
+    }
+    if (!strcasecmp (cmd, "ABORT"))  {
+      WriteMsg (file, "Elixir is exiting without finishing");
+      Shutdown (1);
+    }
+
+    p = p2 + 1;
+
+  }
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckProcess.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckProcess.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/CheckProcess.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "elixir.h"
+
+int CheckProcess (Process *process) {
+
+  Machine *machine;
+  int cargc, *cargd;
+  char **cargv;
+  Object *object;
+
+  CheckCluster (process[0].cluster, process[0].success, process[0].failure, process[0].pending);
+  if (drand48() > 0.5) return (FALSE);
+
+
+  if ((object = GetObject (process[0].pending)) == (Object *) NULL) return (FALSE);
+  object[0].lastproc = process[0].name;
+
+  /* can't create arguments, some dependencies might not be ready */
+  if (!MakeArgs (process, object, &cargc, &cargv, &cargd)) {
+    FreeArgs (cargc, cargv, cargd);
+    PutObject (process[0].pending, object);
+    return (FALSE);
+  }
+
+  /* dependencies not ready */
+  switch (CheckDepend (object, cargc, cargv, cargd)) {
+  case 0:
+    FreeArgs (cargc, cargv, cargd);
+    PutObject (process[0].pending, object);
+    return (FALSE);
+  case 2:
+    FreeArgs (cargc, cargv, cargd);
+    PutObject (process[0].failure, object);
+    return (FALSE);
+  case 1:
+  default:
+    break;
+  }
+    
+  /* no machine ready */
+  if ((machine = GrabMachine ()) == (Machine *) NULL) {
+    /* need to free appropriate items from above */
+    FreeArgs (cargc, cargv, cargd);
+    PushObject (process[0].pending, object);
+    return (FALSE);
+  }
+
+  StartMachine (machine, object, cargc, cargv);
+  FreeArgs (cargc, cargv, cargd);
+  StartProcessTimer (process, machine);
+
+  PutMachine (machine, process[0].cluster);
+  return (TRUE);
+}
+
+/* objects that are not ready go to the bottom of the stack (PutObject)
+   objects that are ready, but have no machine free, go to top of stack (PushObject) 
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,193 @@
+# include "elixir.h"
+
+void SIG_STOP (int sig) {
+  fprintf (stderr, "trapped signal %d, exiting when jobs are done\n", sig);
+  ElixirStop ();
+}
+
+void SIG_MESSAGE (int sig) {
+  fprintf (stderr, "trapped signal %d, reading message\n", sig);
+  CheckMessages ();
+}
+
+void SIG_DIE (int sig) {
+  fprintf (stderr, "trapped signal %d, exiting\n", sig);
+  Shutdown (1);
+}
+
+void SIG_PIPE (int sig) {
+  fprintf (stderr, "pipe signal %d\n", sig);
+}
+
+char *ConfigInit (int *argc, char **argv) {
+
+  int N;
+  char *config, *file;
+
+  /* use default settings */
+  signal (SIGKILL,   SIG_DFL);    
+  signal (SIGCONT,   SIG_DFL);    
+  signal (SIGSTOP,   SIG_DFL);    
+  signal (SIGCHLD,   SIG_DFL);    
+
+  /* exit on these signals */
+  signal (SIGILL,    SIG_DIE);     
+  signal (SIGABRT,   SIG_DIE);    
+  signal (SIGFPE,    SIG_DIE);     
+  signal (SIGSEGV,   SIG_DIE);    
+  signal (SIGTERM,   SIG_DIE);    
+  signal (SIGBUS,    SIG_DIE);     
+  signal (SIGTRAP,   SIG_DIE);    
+  signal (SIGXCPU,   SIG_DIE);    
+  signal (SIGXFSZ,   SIG_DIE);    
+  signal (SIGIOT,    SIG_DIE);     
+  signal (SIGHUP,    SIG_IGN);
+  signal (SIGINT,    SIG_DIE);
+  signal (SIGQUIT,   SIG_IGN);
+  signal (SIGPIPE,   SIG_PIPE);    
+  signal (SIGALRM,   SIG_IGN);    
+  signal (SIGUSR1,   SIG_STOP);    
+  signal (SIGUSR2,   SIG_MESSAGE);    
+  signal (SIGTSTP,   SIG_IGN);    
+  signal (SIGTTIN,   SIG_IGN);    
+  signal (SIGTTOU,   SIG_IGN);    
+  signal (SIGPROF,   SIG_IGN);    
+  signal (SIGURG,    SIG_IGN);     
+  signal (SIGVTALRM, SIG_IGN);  
+  signal (SIGIO,     SIG_IGN);      
+
+  /* signals which are not always defined */
+# ifdef SIGPOLL
+  signal (SIGPOLL,   SIG_IGN);    
+# endif
+# ifdef SIGPWR
+  signal (SIGPWR, SIG_DIE);     /* power failure (Sys V) */
+# endif
+# ifdef SIGWINCH
+  signal (SIGWINCH, SIG_IGN);   /* window resized (4.3BSD) */
+# endif
+# ifdef SIGSYS
+  signal (SIGSYS,    SIG_DIE);     
+# endif
+# ifdef SIGEMT
+ signal (SIGEMT,     SIG_DIE); 
+# endif
+# ifdef SIGSTKFLT
+  signal (SIGSTKFLT, SIG_DIE);  
+# endif
+# ifdef SIGINFO
+ signal (SIGINFO,    SIG_DIE); 
+# endif
+# ifdef SIGCLD
+  signal (SIGCLD,    SIG_DFL);     
+# endif
+# ifdef SIGLOST
+  signal (SIGLOST,   SIG_IGN); 
+# endif
+# ifdef SIGUNUSED
+  signal (SIGUNUSED, SIG_IGN); 
+# endif
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  if (file == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't choose configuration file\n");
+    Shutdown (1);
+  }    
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    Shutdown (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+  free (file);
+
+  if ((N = get_argument (*argc, argv, "-restart"))) {
+    char pidfile[128];
+
+    remove_argument (N, argc, argv);
+    if (!ScanConfig (config, "global.pid", "%s",  0, pidfile)) {
+      fprintf (stderr, "pid file not defined: global.pid\n");
+      exit (1);
+    }
+    HalttoRestart (pidfile);
+  }
+
+  if ((*argc != 2) && (*argc != 1)) {
+    fprintf (stderr, "USAGE: elixir [list]\n");
+    fprintf (stderr, "       elixir [-kill]\n");
+    fprintf (stderr, "       elixir [-stop]\n");
+    fprintf (stderr, "       elixir [-status]\n");
+    Shutdown (1);
+  }
+  
+  /* special command-line options */
+  { 
+    char pidfile[128], msgfile[128];
+
+    if (!ScanConfig (config, "global.pid", "%s",  0, pidfile)) {
+      fprintf (stderr, "pid file not defined: global.pid\n");
+      exit (1);
+    }
+    
+    if (!ScanConfig (config, "global.msg", "%s",  0, msgfile)) {
+      fprintf (stderr, "msg file not defined: global.msg\n");
+      exit (1);
+    }
+    
+    if (!ScanConfig (config, "CONNECT", "%s",  0, CONNECT)) {
+      sprintf (CONNECT, "/usr/bin/rsh");
+    }
+    
+    if (get_argument (*argc, argv, "-kill"))   KillElixir (pidfile);
+    if (get_argument (*argc, argv, "-stop"))   HaltElixir (pidfile);
+    if (get_argument (*argc, argv, "-status")) StatusElixir (pidfile, msgfile);
+  }
+
+  return (config);
+
+}
+
+/*
+
+  SIGHUP             1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+  SIGINT             2        A      Interrupt from keyboard
+  SIGQUIT            3        C      Quit from keyboard
+  SIGILL             4        C      Illegal Instruction
+  SIGABRT            6        C      Abort signal from abort(3)
+  SIGFPE             8        C      Floating point exception
+  SIGKILL            9       AEF     Kill signal
+  SIGSEGV           11        C      Invalid memory reference
+  SIGPIPE           13        A      Broken pipe: write to pipe with no readers
+  SIGALRM           14        A      Timer signal from alarm(2)
+  SIGTERM           15        A      Termination signal
+  SIGUSR1        30,10,16     A      User-defined signal 1
+  SIGUSR2        31,12,17     A      User-defined signal 2
+  SIGCHLD        20,17,18     B      Child stopped or terminated
+  SIGCONT        19,18,25            Continue if stopped
+  SIGSTOP        17,19,23    DEF     Stop process
+  SIGTSTP        18,20,24     D      Stop typed at tty
+  SIGTTIN        21,21,26     D      tty input for background process
+  SIGTTOU        22,22,27     D      tty output for background process
+  SIGBUS         10,7,10      C      Bus error (bad memory access)
+  SIGPOLL                     A      Pollable event (Sys V). Synonym of SIGIO
+  SIGPROF        27,27,29     A      Profiling timer expired
+  SIGSYS         12,-,12      C      Bad argument to routine (SVID)
+  SIGTRAP           5         C      Trace/breakpoint trap
+  SIGURG         16,23,21     B      Urgent condition on socket (4.2 BSD)
+  SIGVTALRM      26,26,28     A      Virtual alarm clock (4.2 BSD)
+  SIGXCPU        24,24,30     C      CPU time limit exceeded (4.2 BSD)
+  SIGXFSZ        25,25,31     C      File size limit exceeded (4.2 BSD)
+  SIGIOT            6         C      IOT trap. A synonym for SIGABRT
+  SIGEMT          7,-,7
+  SIGSTKFLT       -,16,-      A      Stack fault on coprocessor
+  SIGIO          23,29,22     A      I/O now possible (4.2 BSD)
+  SIGCLD          -,-,18             A synonym for SIGCHLD
+  SIGPWR         29,30,19     A      Power failure (System V)
+  SIGINFO         29,-,-             A synonym for SIGPWR
+  SIGLOST         -,-,-       A      File lock lost
+  SIGWINCH       28,28,20     B      Window resize signal (4.3 BSD, Sun)
+  SIGUNUSED       -,31,-      A      Unused signal (will be SIGSYS)
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/ConfigPID.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/ConfigPID.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/ConfigPID.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "elixir.h"
+
+static char *PIDMaster = (char *) NULL;
+
+void ConfigPID (char *PIDFile) {
+
+  pid_t pid;
+  char *username, machine[256];
+  FILE *f;
+
+  f = fopen (PIDFile, "r");
+  if (f == (FILE *) NULL) { 
+
+    pid = getpid ();
+    username = getenv ("USER");
+    if (username == (char *) NULL) {
+      fprintf (stderr, "error getting username\n");
+      exit (2);
+    }
+    bzero (machine, 256);
+    if (gethostname (machine, 256)) {
+      fprintf (stderr, "error getting hostname\n");
+      exit (2);
+    }
+
+    f = fopen (PIDFile, "w");
+    if (f == (FILE *) NULL) { 
+      fprintf (stderr, "can't write to PID file %s\n", PIDFile);
+      exit (2);
+    }
+
+    fprintf (f, "PID:     %d\n", pid);
+    fprintf (f, "USER:    %-s\n", username);
+    fprintf (f, "MACHINE: %-s\n", machine);
+    fclose (f);
+
+    PIDMaster = PIDFile;
+    return; 
+  }
+
+  ALLOCATE (username, char, 256);
+  fscanf (f, "%*s %d", &pid);
+  fscanf (f, "%*s %s", username);
+  fscanf (f, "%*s %s", machine);
+  fclose (f);
+
+  fprintf (stderr, "elixir is apparently running:\n\n");
+  fprintf (stderr, "  machine: %s\n", machine);
+  fprintf (stderr, "  user: %s\n", username);
+  fprintf (stderr, "  PID: %d\n", pid);
+  fprintf (stderr, "  remove %s if elixir has died unexpectedly\n", PIDFile);
+  Shutdown (1);
+}
+
+void RemovePID () {
+  
+  if (PIDMaster == (char *) NULL) 
+    return;
+  
+  if (unlink (PIDMaster)) {
+    fprintf (stderr, "error deleting PID File %s\n", PIDMaster);
+  }   
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/DefineProcesses.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/DefineProcesses.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/DefineProcesses.c	(revision 22322)
@@ -0,0 +1,135 @@
+# include "elixir.h"
+
+Process **DefineProcesses (Process *global, int *nprocess, char *config) {
+
+  int i, j;
+  char procname[256], entry[256], field[256];
+  Process **process;
+  int Nprocess, NPROCESS;
+  double timeout;
+
+  global[0].success = InitQueue ();
+  global[0].failure = InitQueue ();
+  
+  ALLOCATE (global[0].argv, char *, 8);
+  ALLOCATE (global[0].argv[0], char, 256);
+  ALLOCATE (global[0].argv[1], char, 256);
+  ALLOCATE (global[0].argv[2], char, 256);
+  ALLOCATE (global[0].argv[3], char, 256);
+  ALLOCATE (global[0].argv[4], char, 256);
+  ALLOCATE (global[0].argv[5], char, 256);
+  ALLOCATE (global[0].argv[6], char, 256);
+  ALLOCATE (global[0].argv[7], char, 256);
+  
+  GetConfig (config, "global.Nargs",   "%d",  0, &global[0].argc);
+  GetConfig (config, "global.success", "%s",  0, global[0].argv[0]);
+  GetConfig (config, "global.failure", "%s",  0, global[0].argv[1]);
+  GetConfig (config, "global.pending", "%s",  0, global[0].argv[2]);
+  GetConfig (config, "global.logfile", "%s",  0, global[0].argv[3]);
+  GetConfig (config, "global.photcode",  "%s",  0, global[0].argv[4]);
+  GetConfig (config, "global.msg",     "%s",  0, global[0].argv[5]);
+  GetConfig (config, "global.end",     "%s",  0, global[0].argv[6]);
+  GetConfig (config, "global.pid",     "%s",  0, global[0].argv[7]);
+
+  ScanConfig (config, "global.timeout", "%lf", 0, &timeout);
+  if (timeout < 1) {
+    fprintf (stderr, "global.timeout is absurd: %f\n", timeout);
+    Shutdown (1);
+  }
+  if (timeout < 100) {
+    fprintf (stderr, "**** global.timeout is very short (%f) ****\n", timeout);
+  }
+  RegisterTimeout (timeout);
+  
+  Nprocess = 0;
+  NPROCESS = 5;
+  ALLOCATE (process, Process *, NPROCESS);
+
+  /* find all entries in config file labeled 'process' */
+  for (i = 1; ScanConfig (config, "process", "%s", i, procname); i++) {
+    process[Nprocess] = ConfigProcess (config, procname);
+    Nprocess ++;
+    if (Nprocess == NPROCESS) {
+      NPROCESS += 5;
+      REALLOCATE (process, Process *, NPROCESS);
+    }
+  }
+  if (Nprocess == 0) {
+    fprintf (stderr, "no processes defined in config file\n");
+    Shutdown (1);
+  }
+
+  /* make links between processes */
+  for (i = 0; i < Nprocess; i++) {
+    /* connect this process success queue */
+    sprintf (field, "%s.success", process[i][0].name);
+    GetConfig (config, field, "%s", 0, entry);
+    if (!strcasecmp (entry, "global")) {
+      process[i][0].success = global[0].success;
+      goto stage1;
+    }
+    for (j = 0; j < Nprocess; j++) {
+      if (!strcasecmp (entry, process[j][0].name)) {
+	if (i == j) {
+	  fprintf (stderr, "ERROR: can't connect a process to itself: %s\n", process[i][0].name);
+	  Shutdown (1);
+	}
+	process[i][0].success = process[j][0].pending;
+	goto stage1;
+      }
+    }
+    fprintf (stderr, "ERROR: can't connect process %s to target %s\n", process[i][0].name, entry);
+    Shutdown (1);
+    
+  stage1:
+    /* connect this process failure queue */
+    sprintf (field, "%s.failure", process[i][0].name);
+    GetConfig (config, field, "%s", 0, entry);
+    if (!strcasecmp (entry, "global")) {
+      process[i][0].failure = global[0].failure;
+      goto stage2;
+    }
+    for (j = 0; j < Nprocess; j++) {
+      if (!strcasecmp (entry, process[j][0].name)) {
+	if (i == j) {
+	  fprintf (stderr, "ERROR: can't connect a process to itself: %s\n", process[i][0].name);
+	  Shutdown (1);
+	}
+	process[i][0].failure = process[j][0].pending;
+	goto stage2;
+      }
+    }
+    fprintf (stderr, "ERROR: can't connect process %s to target %s\n", process[i][0].name, entry);
+    Shutdown (1);
+    
+  stage2:
+    continue;
+  }
+
+  /* link global process to first process */
+  for (j = 0; j < Nprocess; j++) {
+    if (!strcasecmp (global[0].argv[2], process[j][0].name)) {
+      global[0].pending = process[j][0].pending;
+      goto stage3;
+    }
+  }
+  fprintf (stderr, "ERROR: can't connect global process to target %s\n", global[0].argv[2]);
+  Shutdown (1);
+ 
+stage3:
+  *nprocess = Nprocess;
+  return (process);
+
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find required field %s\n", field);
+    Shutdown (1);
+  }
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/DumpStatus.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/DumpStatus.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/DumpStatus.c	(revision 22322)
@@ -0,0 +1,78 @@
+# include "elixir.h"
+
+int DumpStatus (char *filename) {
+
+  FILE *f;
+  int i, state, total, mode;
+  Process *global, **process;
+  int Nprocess, Nobject, Dynamic;
+
+  process = GetProcessInfo (&global, &Nprocess, &Nobject);
+  Dynamic = GetDynamicState ();
+
+  if (filename == (char *) NULL) {
+    system ("tput clear");
+    f = stderr;
+  } else {
+    /* lock file */
+    f = fsetlockfile (filename, 0.1, LCK_XCLD, &state);
+    if (f == NULL) return (2);
+    fseek (f, 0, SEEK_END);
+  }  
+  
+  fprintf (f, "processes status:\n");
+  fprintf (f, "            name   pending  success  failure  total\n");
+  fprintf (f, "----------------------------------------------------------------\n");
+  total = global[0].pending[0].Nobject + global[0].success[0].Nobject + global[0].failure[0].Nobject;
+  fprintf (f, "%16s    %6d   %6d   %6d   %4d\n", "global", 
+	   global[0].pending[0].Nobject, global[0].success[0].Nobject, global[0].failure[0].Nobject, total); 
+  for (i = 0; i < Nprocess; i++) {
+    fprintf (f, "%16s    %6d\n", process[i][0].name, process[i][0].pending[0].Nobject); 
+  }
+  fprintf (f, "\n");
+
+  DumpMachineStatus (f);
+
+  fprintf (f, "\n");
+  fprintf (f, "objects loaded so far: %d\n\n", Nobject);
+  if (Dynamic) {
+    fprintf (f, "accepting input from FIFO\n");
+  } else {
+    fprintf (f, "NOT accepting input from FIFO\n");
+  }    
+  
+  if (f != stderr) {
+    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+    chmod (filename, mode);
+    fclearlockfile (filename, f, LCK_XCLD, &state);
+  }
+  return (TRUE);
+}
+
+/*
+
+processes status:
+            name  pending  success  failure  total
+----------------------------------------------------------------
+          global        1        0        4      5
+           mkdir        1
+         flatten        0
+          dophot        0
+         imclean        0
+          gastro        0
+         addstar        0
+          rmfile        5
+
+machine status:
+          name  process  status
+----------------------------------------------------------------
+         kiawe  rmfile   1
+         kiawe  addstar  9
+          milo  addstar  1
+
+objects loaded so far: 13
+
+accepting input from FIFO
+
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/FifoOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/FifoOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/FifoOps.c	(revision 22322)
@@ -0,0 +1,96 @@
+# include "elixir.h"
+
+int InitFifo (Fifo *fifo, int Nalloc, int Nextra) {
+
+  if (Nextra >= Nalloc) {
+    fprintf (stderr, "absurd fifo definition\n");
+    return (FALSE);
+  }
+
+  fifo[0].Nalloc = Nalloc;
+  fifo[0].Nextra = Nextra;
+  fifo[0].Nmaxread = Nalloc - Nextra;
+  fifo[0].Nlast = 0;
+  fifo[0].Nbuffer = 0;
+
+  ALLOCATE (fifo[0].buffer, char, fifo[0].Nalloc);
+
+  return (TRUE);
+
+}
+
+int FlushFifo (Fifo *fifo) {
+
+  fifo[0].Nlast = 0;
+  fifo[0].Nbuffer = 0;
+
+  return (TRUE);
+
+}
+
+/* after a shift, we can always read 
+   fifo[0].Nmaxread 
+   bytes into 
+   &fifo[0].buffer[Nbuffer] 
+   which is the byte after then end of existing data */
+
+int ShiftFifo (Fifo *fifo) {
+
+  int Nextra, Nshift;
+
+  Nextra = fifo[0].Nextra;
+  Nshift = fifo[0].Nbuffer - fifo[0].Nextra;
+  if (Nshift <= 0) return (TRUE);
+
+  memcpy (fifo[0].buffer, &fifo[0].buffer[Nshift], Nextra);
+  fifo[0].Nbuffer = Nextra;
+  fifo[0].Nlast = Nextra;
+
+  return (TRUE);
+}
+
+/* like a standard read, ReadtoFifo returns Nbytes read,
+   -1 for sock busy, or 0 for sock closed */
+
+int ReadtoFifo (Fifo *fifo, int sock) {
+
+  int Nread;
+  int Nbuffer, Nmaxread;
+
+  if (sock == 0) {
+    fprintf (stderr, "error with socket?\n");
+    return (0);
+  }
+
+  fifo[0].Nlast = fifo[0].Nbuffer;
+
+  Nbuffer = fifo[0].Nbuffer;
+  Nmaxread = fifo[0].Nmaxread;
+  Nread = read (sock, &fifo[0].buffer[Nbuffer], Nmaxread);
+
+  if (Nread > 0) fifo[0].Nbuffer += Nread;
+
+  if (Nread == -1) {
+    /* check for possible errors.  anything other than EAGAIN
+       is bad and should indicate the connection is down */
+    switch (errno) {
+    case EAGAIN:
+    case EIO:
+      Nread = -1;
+      break;
+    default:
+      fprintf (stderr, "read error: %d\n", errno);
+      Nread = 0;
+      break;
+    }
+  }
+
+  return (Nread);
+}
+
+void FreeFifo (Fifo *fifo) {
+
+  if (fifo[0].buffer != (char *) NULL) {
+    free (fifo[0].buffer);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/InitProcess.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/InitProcess.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/InitProcess.c	(revision 22322)
@@ -0,0 +1,19 @@
+# include "elixir.h"
+
+Process *InitProcess (char *name, Queue *pending, Queue *failure, int (*mkargs)()) {
+
+  Process *process;
+
+  ALLOCATE (process, Process, 1);
+
+  process[0].success = InitQueue ();
+  process[0].failure = failure;
+  process[0].pending = pending;
+
+  process[0].name    = strcreate (name);
+  /* process[0].mkarg   = mkargs; */
+
+  process[0].cluster = InitCluster ();
+
+  return (process);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/LoadPending.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/LoadPending.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/LoadPending.c	(revision 22322)
@@ -0,0 +1,127 @@
+# include "elixir.h"
+
+static struct timeval then = {0.0, 0.0};
+
+/* LoadPending returns the number of objects loaded from inlist
+   
+   state can be: 
+   0 - success
+   1 - file locked
+   2 - file has EOF flag
+   3 - error in input 
+*/
+
+int LoadPending (Process *global, char *filename, int *state, int *dynamic) {
+
+  int i, status, depend, Nobjects;
+  int lockstate, mode;
+  FILE *f;
+  char name[7][256], line[1024];
+  struct timeval now;
+  Object *object;
+
+  *state = 0;
+  Nobjects = 0;
+
+  /* we should only do this check every 1sec or so */
+  gettimeofday (&now, (void *) NULL);
+  if (DTIME (now, then) < 1.0) return (Nobjects);
+  then = now;
+
+  /* if dynamic, we must lock the file first, but not if not dynamic */
+  if (*dynamic) {
+    
+    /* check lockfile - don't remove list if locked */
+    f = fsetlockfile (filename, 1.0, LCK_XCLD, &lockstate);
+    if (f == NULL) { 
+      *state = 1;
+      return (Nobjects);
+    }
+  } else {
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      *state = 1;
+      return (Nobjects);
+    }
+  }
+
+  /* read lines, parse each line */
+  while (1) {
+    if (scan_line (f, line) == EOF) goto escape;
+
+    switch (global[0].argc) {
+    case 1:
+      status = sscanf (line, "%s", name[0]);
+      break;
+    case 2:
+      status = sscanf (line, "%s%s", name[0], name[1]);
+      break;
+    case 3:
+      status = sscanf (line, "%s%s%s", name[0], name[1], name[2]);
+      break;
+    case 4:
+      status = sscanf (line, "%s%s%s%s", name[0], name[1], name[2], name[3]);
+      break;
+    case 5:
+      status = sscanf (line, "%s%s%s%s%s", name[0], name[1], name[2], name[3], name[4]);
+      break;
+    case 6:
+      status = sscanf (line, "%s%s%s%s%s%s", name[0], name[1], name[2], name[3], name[4], name[5]);
+      break;
+    case 7:
+      status = sscanf (line, "%s%s%s%s%s%s%s", name[0], name[1], name[2], name[3], name[4], name[5], name[6]);
+      break;
+    default:
+      fprintf (stderr, "ERROR: unexpected number of entries per line in input file\n");
+      *state = 3;
+      goto escape;
+    }
+
+    if (status == 0) continue;  /* an empty or blank line */
+
+    if (!strcasecmp (name[0], "EOF")) {
+      /* dynamic -> static */
+      *state = 2;
+      goto escape;
+    }
+
+    /* silently ignore lines with the wrong number of args */
+    if (status != global[0].argc) continue;
+
+    ALLOCATE (object, Object, 1);
+    object[0].argc = status;
+    ALLOCATE (object[0].argv, char *, status);
+    for (i = 0; i < status; i++) {
+      object[0].argv[i] = strcreate (name[i]);
+    }
+    
+    /* convert the global.logfile description to a specific logfile for this object */
+    ParseLine (global[0].argv[3], object[0].argc, object[0].argv, &depend, &object[0].logfile);
+    object[0].status = 0;
+    object[0].timer.tv_sec = 0;
+    object[0].lastproc = (char *) NULL;
+    PutObject (global[0].pending, object);
+    Nobjects ++;
+  }
+
+  escape:
+  if (*dynamic) {
+    if (truncate (filename, 0)) {
+      fprintf (stderr, "failed to clear source %s (errno %d)\n", filename, errno);
+      Shutdown (1);
+    }
+
+    /* clean up fifo - set mode to 666, unlock */
+    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+    chmod (filename, mode);
+    fclearlockfile (filename, f, LCK_XCLD, &lockstate);
+  } else {
+    fclose (f);
+  }
+
+  if ((*state == 2) && *dynamic) {
+    *dynamic = FALSE;
+  }
+  return (Nobjects);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/LogOpen.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/LogOpen.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/LogOpen.c	(revision 22322)
@@ -0,0 +1,24 @@
+# include "elixir.h"
+
+FILE *LogOpen (char *filename) {
+
+  FILE *f;
+  char *path;
+
+  f = fopen (filename, "a");
+
+  /* probably don't have needed directory */
+  if ((f == (FILE *) NULL) && (errno == ENOENT)) {
+    path = pathname (filename);
+    if (!mkdirhier (path, S_IRWXU | S_IRWXG | S_IRWXO)) {
+      f = fopen (filename, "a");
+    }
+  }
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "LogOpen could not open the log file %s, errno: %d\n", filename, errno);
+    f = stderr;
+  }
+
+  return (f);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/MachineOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/MachineOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/MachineOps.c	(revision 22322)
@@ -0,0 +1,484 @@
+# include "elixir.h"
+
+/* this is in libohana -> should be in ohana.h */
+char *getcwd_cfht (char *path, int size);
+
+static Cluster *fullpool = (Cluster *) NULL;
+static Cluster *idlepool = (Cluster *) NULL;
+static Cluster *downpool = (Cluster *) NULL;
+static char *ConfigFilename;
+
+/* put machine on top of cluster */
+void PushMachine (Machine *machine, Cluster *cluster) {
+
+  int i;
+
+  if (cluster[0].Nmachine == cluster[0].NMACHINE) {
+    cluster[0].NMACHINE += 5;
+    REALLOCATE (cluster[0].machine, Machine *, cluster[0].NMACHINE);
+  }
+  for (i = cluster[0].Nmachine; i > 0; i--) {
+    cluster[0].machine[i] = cluster[0].machine[i-1];
+  }
+  cluster[0].machine[0] = machine;
+  cluster[0].Nmachine ++;
+}
+
+/* put machine on bottom of cluster */
+void PutMachine (Machine *machine, Cluster *cluster) {
+
+  if (cluster[0].Nmachine == cluster[0].NMACHINE) {
+    cluster[0].NMACHINE += 5;
+    REALLOCATE (cluster[0].machine, Machine *, cluster[0].NMACHINE);
+  }
+  cluster[0].machine[cluster[0].Nmachine] = machine;
+  cluster[0].Nmachine ++;
+}
+
+/* Get a machine from top of cluster */
+Machine *GetMachine (Cluster *cluster) {
+
+  int i;
+  Machine *machine;
+
+  if (cluster[0].Nmachine == 0) return ((Machine *) NULL);
+
+  machine = cluster[0].machine[0];
+  cluster[0].Nmachine --;
+  for (i = 0; i < cluster[0].Nmachine; i++) {
+    cluster[0].machine[i] = cluster[0].machine[i+1];
+  }
+  return (machine);
+}
+
+void IdleMachine (Machine *machine) {
+
+  machine[0].status = IDLE;
+  machine[0].object = (Object *) NULL;
+  PutMachine (machine, idlepool);
+ 
+}
+
+void DownMachine (Machine *machine) {
+
+  machine[0].status = DOWN;
+  machine[0].object = (Object *) NULL;
+  gettimeofday (&machine[0].start, (void *) NULL);
+  gettimeofday (&machine[0].timer, (void *) NULL);
+  machine[0].timer.tv_sec += 5;
+  PutMachine (machine, downpool);
+ 
+}
+
+# define NRETRIES 50
+int TestMachine (Machine *machine) {
+
+  int i, status;
+  char buffer[64];
+
+  FlushFifo (&machine[0].fifo);
+
+  if ((machine[0].wsock == 0) || (machine[0].rsock == 0)) return (FALSE);
+
+  /* writes are non-blocking.  check for EPIPE in case pipe is closed */
+  sprintf (buffer, "echo CONNECTION TEST\n");
+  status = write (machine[0].wsock, buffer, strlen(buffer));
+  if ((status == -1) && (errno == EPIPE)) {
+    fprintf (stderr, "socket unexpectedly closed in test\n");
+    CloseMachine (machine);
+    return (FALSE);
+  }
+
+  status = -1;
+  for (i = 0; (i < NRETRIES) && (status == -1); i++) {
+    status = SockScan ("CONNECTION TEST", &machine[0].fifo, machine[0].rsock);
+    if (status == 0) {
+      fprintf (stderr, "socket unexpectedly closed in test\n");
+      CloseMachine (machine);
+      return (FALSE);
+    }
+  }
+  if (i == NRETRIES) {
+    fprintf (stderr, "no response from machine, shutting it down\n");
+    CloseMachine (machine);
+    return (FALSE);
+  }
+  FlushFifo (&machine[0].fifo);
+  return (TRUE);
+}
+
+Machine *GrabMachine () {
+
+  Machine *machine;
+
+  machine = GetMachine (idlepool);
+  if (machine == (Machine *) NULL) return (machine);
+
+  if (!TestMachine (machine)) {
+    DownMachine (machine);
+    return ((Machine *) NULL);
+  }
+  return (machine);
+}
+
+Cluster *InitCluster () {
+
+  Cluster *cluster;
+
+  ALLOCATE (cluster, Cluster, 1);
+  cluster[0].NMACHINE = 5;
+  cluster[0].Nmachine = 0;
+  ALLOCATE (cluster[0].machine, Machine *, cluster[0].NMACHINE);
+  
+  return (cluster);
+
+}
+
+void InitMachines (char *config) {
+
+  int i, Nm, fd, Nout, status;
+  char name[256];
+  Machine *machine;
+  char *cwd;
+
+  fullpool = InitCluster ();
+  idlepool = InitCluster ();
+  downpool = InitCluster ();
+
+  /* processes run on these machines need to have access to the
+     exact config file used by elixir.  Therefore, we write the 
+     config file to a unique filename in this directory and pass 
+     that name to the processes which start machines.  it is 
+     crucial that the cwd be visible on the other machines, and have
+     the same path name.  when we exit (Shutdown(n)), we will delete
+     this file (but not before!). */
+  
+  /* get cwd and create config file here */
+  if ((cwd = getcwd_cfht (NULL, 1024)) == NULL) {
+    fprintf (stderr, "error getting cwd\n");
+    Shutdown (1);
+  }
+  sprintf (name, "%s/elixir.XXXXXX", cwd);
+  if ((fd = mkstemp (name)) == -1) {
+    fprintf (stderr, "can't store current config in cwd\n");
+    Shutdown (1);
+  }
+  Nout = write (fd, config, strlen (config));
+  if (Nout != strlen (config)) {
+    fprintf (stderr, "can't store current config in cwd\n");
+    Shutdown (1);
+  }
+  status = close (fd);
+  if (status == -1) {
+    fprintf (stderr, "can't store current config in cwd\n");
+    Shutdown (1);
+  }
+  ConfigFilename = strcreate (name);
+
+  /* create connection to each of the machines */
+  Nm = 0;
+  for (i = 1; ScanConfig (config, "MACHINE", "%s", i, name); i++) {
+    ALLOCATE (machine, Machine, 1);
+    machine[0].hostname = strcreate (name);
+    InitFifo (&machine[0].fifo, 0x4000, 0x1000);
+    if (ConnectMachine (machine)) {
+      IdleMachine (machine);
+    } else {
+      DownMachine (machine);
+    }
+    PutMachine (machine, fullpool);
+    Nm ++;
+  }
+  if (Nm == 0) {
+    fprintf (stderr, "no available machines, exiting\n");
+    Shutdown (1);
+  }
+      
+}
+
+int ConnectMachine (Machine *machine) {
+
+  int rsock, wsock, pid;
+  char line[256];
+
+  pid = rconnect (machine[0].hostname, CONNECT, &rsock, &wsock);
+  if (pid) {
+    machine[0].rsock = rsock;
+    machine[0].wsock = wsock;
+    machine[0].pid   = pid;
+    /* we can set up the shell here */
+    sprintf (line, "setenv PTOLEMY %s\n", ConfigFilename);
+    write (wsock, line, strlen (line));
+    sprintf (line, "umask 002\n");
+    write (wsock, line, strlen (line));
+    return (TRUE);
+  } else {
+    fprintf (stderr, "can't make connection to %s, skipping for now\n", machine[0].hostname);
+    machine[0].rsock = 0;
+    machine[0].wsock = 0;
+    machine[0].pid   = 0;
+    return (FALSE);
+  }
+}
+
+/* machine which are claimed as down need to be restarted.  
+   first, check that they really are down, then restart as needed */
+void RestartMachines () {
+  
+  int i;
+  Machine *machine;
+  double dtime;
+  struct timeval now;
+
+  for (i = 0; i < downpool[0].Nmachine; i++) {
+    machine = GetMachine (downpool);
+    if (machine == (Machine *) NULL) return;
+
+    /* we only try to reconnect if timer is expired */
+    gettimeofday (&now, (void *) NULL);
+    dtime = DTIME (machine[0].timer, now);
+    if (dtime > 0) {
+      PutMachine (machine, downpool);
+      continue;
+    }
+
+    fprintf (stderr, "restarting machine %s\n", machine[0].hostname);
+    if (TestMachine (machine)) {
+      /* machine is still alive, return to idlepool */
+      fprintf (stderr, "%s is alive\n", machine[0].hostname);
+      IdleMachine (machine);
+      continue;
+    }
+
+    if (ConnectMachine (machine)) {
+      fprintf (stderr, "connection to %s successfully restarted\n", machine[0].hostname);
+      IdleMachine (machine);
+    } else {
+      /* advance dtime so restarts happen later and later */
+      dtime = DTIME (machine[0].timer, machine[0].start);
+      dtime = dtime * 2;
+      dtime = MIN (600, dtime);
+      gettimeofday (&machine[0].start, (void *) NULL);
+      gettimeofday (&machine[0].timer, (void *) NULL);
+      machine[0].timer.tv_sec += dtime;
+      PutMachine (machine, downpool);
+    }
+  }
+
+}
+
+void Shutdown (int status) {
+
+  int i;
+
+  if (unlink (ConfigFilename) == -1) {
+    fprintf (stderr, "trouble deleting config file: %s\n", ConfigFilename);
+  }
+
+  RemovePID ();
+
+  if (fullpool == (Cluster *) NULL) exit (status);
+    
+  for (i = 0; i < fullpool[0].Nmachine; i++) {
+    CloseMachine (fullpool[0].machine[i]);
+  }
+
+  exit (status);
+
+}
+
+void Restart (char **argv) {
+
+  int i;
+
+  if (unlink (ConfigFilename) == -1) {
+    fprintf (stderr, "trouble deleting config file: %s\n", ConfigFilename);
+  }
+
+  RemovePID ();
+
+  if (fullpool == (Cluster *) NULL) execvp (argv[0], argv);
+    
+  for (i = 0; i < fullpool[0].Nmachine; i++) {
+    CloseMachine (fullpool[0].machine[i]);
+  }
+
+  execvp (argv[0], argv);
+}
+
+
+int InitMsgFile (char *file) {
+  
+  char line[256];
+  int status;
+  struct stat filestat;
+
+  if (stat (file, &filestat) == -1) return (TRUE);
+  sprintf (line, "mv -f %s %s~", file, file);
+  status = system (line);
+  return (status);
+}
+
+void CloseMachine (Machine *machine) {
+
+  char buffer[128];
+
+  sprintf (buffer, "exit\n");
+  write (machine[0].wsock, buffer, strlen(buffer));
+  close (machine[0].wsock);
+  close (machine[0].rsock);
+  fprintf (stderr, "shutdown machine %s, pid %d\n", machine[0].hostname, machine[0].pid); 
+
+}
+
+void DumpMachineStatus (FILE *f) {
+  
+  int i;
+
+  if (fullpool == (Cluster *) NULL) return;
+
+  fprintf (f, "machine status:\n");
+  fprintf (f, "          name  process  status\n");
+  fprintf (f, "----------------------------------------------------------------\n");
+  for (i = 0; i < fullpool[0].Nmachine; i++) {
+    if (fullpool[0].machine[i][0].object == (Object *) NULL) {
+      fprintf (f, "%14s %8s      %2d\n", 
+	       fullpool[0].machine[i][0].hostname,
+	       "(none)",
+	       fullpool[0].machine[i][0].status);
+      
+    } else {      
+      fprintf (f, "%14s %8s      %2d\n", 
+	       fullpool[0].machine[i][0].hostname,
+	       fullpool[0].machine[i][0].object[0].lastproc,
+	       fullpool[0].machine[i][0].status);
+    }
+
+  }
+
+}
+
+static int Nprocs, Nmachine;
+static ProcessTimers *proctimers;
+
+/* this must be called after machines have been loaded, registered */
+void InitProcessTimers (Process **process, int Nprocess) {
+  int i, j;
+
+  Nmachine = fullpool[0].Nmachine;
+  Nprocs = Nprocess;
+  ALLOCATE (proctimers, ProcessTimers, Nmachine);
+
+  for (i = 0; i < Nmachine; i++) {
+    ALLOCATE (proctimers[i].timer,   struct timeval, Nprocs);
+    ALLOCATE (proctimers[i].timesum, double,         Nprocs);
+    ALLOCATE (proctimers[i].Njobs,   int,            Nprocs);
+    ALLOCATE (proctimers[i].active,  int,            Nprocs);
+    ALLOCATE (proctimers[i].process, Process *,      Nprocs);
+    
+    for (j = 0; j < Nprocess; j++) {
+      proctimers[i].timer[j].tv_sec = 0;
+      proctimers[i].timer[j].tv_usec = 0;
+      proctimers[i].timesum[j] = 0;
+      proctimers[i].Njobs[j] = 0;
+      proctimers[i].active[j] = FALSE;
+      proctimers[i].process[j] = process[j];
+    }
+
+    proctimers[i].machine = fullpool[0].machine[i];
+
+  }
+  
+}
+
+void StartProcessTimer (Process *process, Machine *machine) {
+  
+  int i, j;
+
+  /* identify the machine */
+  for (i = 0; i < Nmachine; i++) {
+    if (proctimers[i].machine == machine) {
+      for (j = 0; j < Nprocs; j++) {
+	if (proctimers[i].process[j] == process) {
+	  gettimeofday (&proctimers[i].timer[j], (void *) NULL);
+	  proctimers[i].active[j] = TRUE;
+	  return;
+	}
+      }
+    }
+  }
+
+  fprintf (stderr, "can't find this machine, process combination!\n");
+  fprintf (stderr, "machine: %p process: %p\n", machine, process);
+  return;
+}
+
+void StopProcessTimer (Machine *machine) {
+  
+  int i, j;
+  struct timeval stop;
+  double dtime;
+
+  /* identify the machine */
+  for (i = 0; i < Nmachine; i++) {
+    if (proctimers[i].machine == machine) {
+      for (j = 0; j < Nprocs; j++) {
+	if (proctimers[i].active[j]) {
+ 	  gettimeofday (&stop, (void *) NULL);
+	  dtime = DTIME (stop, proctimers[i].timer[j]);
+	  proctimers[i].timesum[j] += dtime;
+	  proctimers[i].active[j] = FALSE;
+	  proctimers[i].Njobs[j] ++;
+	  return;
+	}
+      }
+    }
+  }
+
+  fprintf (stderr, "can't find an active process for this machine!\n");
+  fprintf (stderr, "machine: %p\n", machine);
+
+}
+
+int DumpProcessTimes (char *filename) {
+  
+  int i, j, state, mode;
+  FILE *f;
+  double dt;
+
+  if (filename == (char *) NULL) {
+    system ("tput clear");
+    f = stderr;
+  } else {
+    /* check lockfile */
+    f = fsetlockfile (filename, 0.1, LCK_XCLD, &state);
+    if (f == NULL) return (2);
+    fseek (f, 0, SEEK_END);
+  }  
+
+  fprintf (f, "               .");
+  for (j = 0; j < Nprocs; j++) {
+    fprintf (f, "   %-14s", proctimers[0].process[j][0].name);
+  }
+  fprintf (f, "\n");
+  for (i = 0; i < Nmachine; i++) {
+    fprintf (f, "%16s ", proctimers[i].machine[0].hostname);
+    for (j = 0; j < Nprocs; j++) {
+      if (proctimers[i].Njobs[j] > 0) {
+	dt = proctimers[i].timesum[j] / proctimers[i].Njobs[j];
+      } else {
+	dt = 0;
+      }
+      fprintf (f, "%7.3f %-6d   ", dt, proctimers[i].Njobs[j]);
+    }
+    fprintf (f, "\n");
+  }
+
+  if (f != stderr) {
+    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+    chmod (filename, mode);
+    fclearlockfile (filename, f, LCK_XCLD, &state);
+  }
+  return (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/MsgOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/MsgOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/MsgOps.c	(revision 22322)
@@ -0,0 +1,114 @@
+# include "elixir.h"
+
+int WaitMsg (char *fifo, char **message, double maxdelay) {
+
+  int status, Nsleep;
+  struct timeval now, then;
+  double dtime;
+  struct stat filestats;
+
+  /* maxdelay is the longest we will wait if the file is locked.  
+     if the file doesn't exist, or is empty, skip it */
+  /* limit our reads to only 10 tries, waiting for a little while in between */
+  Nsleep = 100000 * maxdelay;
+
+  status = stat (fifo, &filestats);
+  if (status == -1) return (0);
+  if (filestats.st_size == 0) return (0);
+
+  gettimeofday (&then, (void *) NULL);
+  while (TRUE) {
+    status = ReadMsg (fifo, message);
+    switch (status) {
+    case 1:
+      return (1);
+    default:
+      gettimeofday (&now, (void *) NULL);
+      dtime = DTIME (now, then);
+      if (dtime > maxdelay) return (0);
+    }
+    usleep (Nsleep);
+  }
+}
+
+int ReadMsg (char *fifo, char **message) {
+
+  int nbytes, Nbytes, NBYTES;
+  char *buffer;
+  int state, mode;
+  FILE *f;
+
+  /* check lockfile */
+  f = fsetlockfile (fifo, 0.1, LCK_XCLD, &state);
+  if (f == NULL) return (2);
+  
+  /* if file is empty, return 0 */
+  if (state == LCK_EMPTY) {
+    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+    chmod (fifo, mode);
+    fclearlockfile (fifo, f, LCK_XCLD, &state);
+    return (0);
+  }  
+  
+  /* read data from file */
+  Nbytes = 0;
+  NBYTES = 0x1000;
+  ALLOCATE (buffer, char, NBYTES);
+  while (TRUE) {
+    nbytes = fread (&buffer[Nbytes], 1, 0x1000, f);
+    if (nbytes < 0) { 
+      fprintf (stderr, "error in ReadMsg -- got -1 bytes\n");
+      exit (0);
+    }
+    if (nbytes == 0) break;
+    Nbytes += nbytes;
+    NBYTES += 0x1000;
+    REALLOCATE (buffer, char, NBYTES);
+  }
+  buffer[Nbytes] = 0;
+  
+  /* file will remain until unlocked and fclosed below, 
+     but cannot be written to because it is locked */
+  if (truncate (fifo, 0)) {
+    fprintf (stderr, "failed to clear fifo file %s (errno: %d)\n", fifo, errno);
+    Shutdown (1);
+  }
+  
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (fifo, mode);
+  fclearlockfile (fifo, f, LCK_XCLD, &state);
+  
+  if (Nbytes == 0) {
+    free (buffer);
+    return (0);
+  }
+
+  *message = buffer;
+  return (1);
+
+}
+
+int WriteMsg (char *fifo, char *message) {
+
+  int state, mode;
+  FILE *f;
+
+  /* check lockfile */
+  f = fsetlockfile (fifo, 0.1, LCK_XCLD, &state);
+  if (f == NULL) return (2);
+
+  /* write message to end of file */
+  fseek (f, 0, SEEK_END);
+  fprintf (f, "%s\n", message);
+
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (fifo, mode);
+  fclearlockfile (fifo, f, LCK_XCLD, &state);
+  return (1);
+}
+
+/*  possible return states:
+    0 - no message (fifo file empty or non-existent) 
+    1 - message received
+    2 - busy (fifo file locked)
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/Photcodes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/Photcodes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/Photcodes.c	(revision 22322)
@@ -0,0 +1,256 @@
+# include "elixir.h"
+
+char *GetPhotcode (char *file) {
+
+  char *code;
+  Header header;
+  char detector[80], filter[80];
+  int i, imageid;
+
+  /* read in image header, open image data region */
+  if (!gfits_read_header (file, &header)) {
+    fprintf (stderr, "ERROR: can't find image file %s (1)\n", file);
+    exit(0);
+  }
+
+  /** WARNING: this should use the abstracted keyword names **/
+  gfits_scan (&header, "DETECTOR", "%s", 1, detector);
+  gfits_scan (&header, "FILTER",   "%s", 1, filter);
+  gfits_scan (&header, "IMAGEID",  "%d", 1, &imageid);
+
+  gfits_free_header (&header);
+
+  for (i = 0; i < strlen(detector); i++) { detector[i] = toupper (detector[i]); }
+  for (i = 0; i < strlen(filter); i++) { filter[i] = toupper (filter[i]); }
+
+  ALLOCATE (code, char, 256);
+  sprintf (code, "%s.%s.%02d", detector, filter, imageid);
+  return (code);
+
+}
+
+/** WARNING: this is pretty weak **/
+char *GetPhotcodeMef (char *file) {
+
+  char *code;
+  Header header;
+  char detector[80], filter[80];
+  int i, imageid;
+  char *filename, *p;
+
+  /* file contains file and extend number */
+  ALLOCATE (filename, char, strlen(file) + 1);
+  
+  p = file;
+  while ((p = strchr (p, ',')) != (char *) NULL) { *p = ' '; }
+  sscanf (file, "%s %d", filename, &imageid);
+
+  /* read in image header, open image data region */
+  if (!gfits_read_header (filename, &header)) {
+    fprintf (stderr, "ERROR: can't find image file %s (1)\n", file);
+    exit(0);
+  }
+  free (filename);
+
+  gfits_scan (&header, "DETECTOR", "%s", 1, detector);
+  gfits_scan (&header, "FILTER",   "%s", 1, filter);
+  gfits_free_header (&header);
+
+  for (i = 0; i < strlen(detector); i++) { detector[i] = toupper (detector[i]); }
+  for (i = 0; i < strlen(filter); i++) { filter[i] = toupper (filter[i]); }
+
+  ALLOCATE (code, char, 256);  sprintf (code, "%s.%s.%02d", detector, filter, imageid);
+  return (code);
+
+}
+
+char *BuildCode (char *line) {
+
+  char *code, *p;
+  Header header;
+  char fullname[256], detector[80], filter[80];
+  char name[256], path[256], ccd[80], mode[80];
+  int i;
+
+  p = line;
+  while ((p = strchr (p, ',')) != (char *) NULL) { *p = ' '; }
+  sscanf (line, "%s %s %s %s", path, name, ccd, mode);
+
+  if (!strcasecmp (mode, "MEF")) {
+    sprintf (fullname, "%s/%s.fits", path, name);
+  } else {
+    sprintf (fullname, "%s/%s/%s%s.fits", path, name, name, ccd);
+  }
+
+  if (!gfits_read_header (fullname, &header)) {
+    fprintf (stderr, "ERROR: can't find image file %s (1)\n", fullname);
+    exit(0);
+  }
+  gfits_scan (&header, "DETECTOR", "%s", 1, detector);
+  gfits_scan (&header, "FILTER",   "%s", 1, filter);
+  gfits_free_header (&header);
+  
+  for (i = 0; i < strlen(detector); i++) { detector[i] = toupper (detector[i]); }
+  for (i = 0; i < strlen(filter); i++) { filter[i] = toupper (filter[i]); }
+
+  ALLOCATE (code, char, 256);
+  sprintf (code, "%s.%s.%s", detector, filter, ccd);
+  return (code);
+
+}
+
+char *BuildName (char *line) {
+
+  char *fullname;
+  char name[256], path[256], ccd[80], mode[80];
+  char *p;
+
+  ALLOCATE (fullname, char, 256);
+
+  p = line;
+  while ((p = strchr (p, ',')) != (char *) NULL) { *p = ' '; }
+  sscanf (line, "%s %s %s %s", path, name, ccd, mode);
+
+  if (!strcasecmp (mode, "MEF")) {
+    sprintf (fullname, "%s/%s.fits", path, name);
+  } else {
+    sprintf (fullname, "%s/%s/%s%s.fits", path, name, name, ccd);
+  }
+
+  return (fullname);
+
+}
+
+char *GetPhotcodeExt (char *file) {
+
+  char *code;
+  Header header;
+  char detector[80], filter[80];
+  int i, imageid, extend;
+  char *filename, *p;
+
+  /* file contains file and extend number */
+  ALLOCATE (filename, char, strlen(file) + 1);
+  
+  p = file;
+  while ((p = strchr (p, ',')) != (char *) NULL) { *p = ' '; }
+  sscanf (file, "%s %d", filename, &extend);
+
+  /* read in image header, open image data region */
+  if (!gfits_read_Xheader (filename, &header, extend)) {
+    fprintf (stderr, "ERROR: can't find image file %s (1)\n", file);
+    exit(0);
+  }
+  free (filename);
+
+  gfits_scan (&header, "DETECTOR", "%s", 1, detector);
+  gfits_scan (&header, "FILTER",   "%s", 1, filter);
+  gfits_scan (&header, "IMAGEID",  "%d", 1, &imageid);
+  gfits_free_header (&header);
+
+  for (i = 0; i < strlen(detector); i++) { detector[i] = toupper (detector[i]); }
+  for (i = 0; i < strlen(filter); i++) { filter[i] = toupper (filter[i]); }
+
+  ALLOCATE (code, char, 256);
+  sprintf (code, "%s.%s.%02d", detector, filter, imageid);
+  return (code);
+
+}
+
+/* given path/filename.ext return filename */
+char *RootFilename (char *file) {
+
+  int Nbyte;
+  char *root, *p1, *p2;
+
+  p1 = strrchr (file, '/');
+  if (p1 == (char *) NULL) p1 = file;
+
+  p2 = strrchr (file, '.');
+  if (p2 == (char *) NULL) p2 = p1 + strlen(p1);
+  Nbyte = p2-p1;
+
+  ALLOCATE (root, char, Nbyte + 1);
+  strncpy (root, p1, Nbyte);
+  root[Nbyte] = 0;
+  
+  return (root);
+
+}  
+
+/* given: path/filename
+   return: path or ./ if none */
+char *PathFilename (char *file) {
+
+  int Nbyte;
+  char *path, *p1;
+
+  p1 = strrchr (file, '/');
+  if (p1 == (char *) NULL) {
+    ALLOCATE (p1, char, 2);
+    strcpy (p1, ".");
+    return (p1);
+  }
+
+  Nbyte = p1-file;
+  ALLOCATE (path, char, Nbyte + 1);
+  strncpy (path, file, Nbyte);
+  path[Nbyte] = 0;
+  
+  return (path);
+
+}  
+
+/* given: A/B/D/filename
+   return: D or ./ if none 
+   if /filename, return '/' 
+   if filename, return ./
+*/
+char *BaseFilename (char *file) {
+
+  int Nbyte;
+  char *path, *p1, *p2;
+
+  p1 = strrchr (file, '/');
+  if (p1 == (char *) NULL) {
+    ALLOCATE (p1, char, 2);
+    strcpy (p1, ".");
+    return (p1);
+  }
+  if (p1 == file) {
+    ALLOCATE (p1, char, 2);
+    strcpy (p1, "/");
+    return (p1);
+  }
+
+  p2 = p1 - 1;
+  while ((p2 > file) && (*p2 != '/')) p2--;
+  if (p2 != file) {
+    p2 ++;
+  }
+
+  Nbyte = (p1 - p2);
+  ALLOCATE (path, char, Nbyte + 1);
+  strncpy (path, p2, Nbyte);
+  path[Nbyte] = 0;
+  
+  return (path);
+
+}  
+
+
+/*
+
+There are two methods to implement the Photcode determination for MEF files. 
+The difference is in whether the IMAGEID is used to refer to the ccd, or the EXTNUM.
+Originally, EXTNUM was used, which meant we needed to determine the IMAGEID for a given EXTNUM.
+The old implementation of PhotCodeMef returned the photcode based on the input MEF filename
+and the EXTNUM.  This is now maintained as PhotCodeExt, but not used in elixir.
+
+As of 13/2/00, we've converted to only using the IMAGEID.  this means that PhotCodeMef returns 
+the photcode, based on the MEF filename and the IMAGEID.  To go along with this change, the 
+function flatten.mef operates on the given IMAGEID not EXTNUM as well.                             
+
+*/
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/ProcessOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/ProcessOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/ProcessOps.c	(revision 22322)
@@ -0,0 +1,240 @@
+# include "elixir.h"
+
+/* take entry and return char with filled out value,
+   perform recursively */
+
+char *ExpandEntry (char *entry, int argc, char **argv) {
+
+  char *p1, *p2, *p3;
+  int Nbyte, N;
+  char *function, *operand, *value;
+  int Nout, Ncpy;
+
+  if (entry[0] == '!') {
+    p1 = strchr (entry, '!'); p1++;
+    p2 = strchr (entry, '(');
+    p3 = strchr (entry, ')');
+    if ((p2 == (char *) NULL) || (p3 == (char *) NULL)) {
+      fprintf (stderr, "syntax error in process defs\n");
+      Shutdown (1);
+    }
+    Nbyte = p2 - p1;
+    function = strncreate (p1, Nbyte);
+
+    p2 ++;
+    Nbyte = p3 - p2;
+    operand = strncreate (p2, Nbyte);
+    value = ExpandEntry (operand, argc, argv); 
+    free (operand);
+    operand = value; 
+    
+    value = (char *) NULL;
+    if (!strcasecmp (function, "buildname")) {
+      value = BuildName (operand);
+    }
+    if (!strcasecmp (function, "buildcode")) {
+      value = BuildCode (operand);
+    }
+    if (!strcasecmp (function, "photcode")) {
+      value = GetPhotcode (operand);
+    }
+    if (!strcasecmp (function, "photcodemef")) {
+      value = GetPhotcodeMef (operand);
+    }
+    if (!strcasecmp (function, "root")) {
+      value = RootFilename (operand);
+    }
+    if (!strcasecmp (function, "path")) {
+      value = PathFilename (operand);
+    }
+    if (!strcasecmp (function, "base")) {
+      value = BaseFilename (operand);
+    }
+    if (value == (char *) NULL) {
+      fprintf (stderr, "unknown process command %s\n", function);
+      Shutdown (1);
+    }
+    free (function); 
+    free (operand); 
+  } else {
+    Nbyte = strlen (entry) + 50;
+    ALLOCATE (value, char, Nbyte);
+
+    Nout = 0;
+    p1 = entry;
+    while ((p2 = strchr (p1, '&')) != (char *) NULL) {
+      Ncpy = p2 - p1;
+      if (Nout + Ncpy >= Nbyte - 1) {
+	Nbyte = Nout + Ncpy + 50;
+	REALLOCATE (value, char, Nbyte);
+      }    
+      strncpy (&value[Nout], p1, Ncpy);
+      Nout += Ncpy;
+      p2 ++;
+      N = strtod (p2, &p1);
+      if ((N > argc - 1) || (N < 0)) {
+	fprintf (stderr, "ERROR: command expects too many object arguments\n");
+	Shutdown (1);
+      }
+      Ncpy = strlen (argv[N]);
+      if (Nout + Ncpy >= Nbyte - 1) {
+	Nbyte = Nout + Ncpy + 50;
+	REALLOCATE (value, char, Nbyte);
+      }    
+      strcpy (&value[Nout], argv[N]);
+      Nout += Ncpy;
+    }
+    Ncpy = strlen (p1);
+    if (Nout + Ncpy >= Nbyte - 1) {
+      Nbyte = Nout + Ncpy + 50;
+      REALLOCATE (value, char, Nbyte);
+    }    
+    strncpy (&value[Nout], p1, Ncpy);
+    Nout += Ncpy;
+    value[Nout] = 0;
+  }
+
+  return (value);
+
+}
+
+Process *ConfigProcess (char *config, char *procname) {
+  
+  int j, NPAR;
+  char argname[256], argline[256];
+  Process *process;
+
+  NPAR = 10;
+  ALLOCATE (process, Process, 1);
+  ALLOCATE (process[0].argv, char *, NPAR);
+
+  process[0].name = strcreate (procname);
+
+  /* find all lines of form procname.arg - the argument lines */
+  sprintf (argname, "%s.arg", procname);
+  for (j = 0; ScanConfig (config, argname, "%s", j+1, argline); j++) {
+    process[0].argv[j] = strcreate (argline);
+    if (j == NPAR) {
+      NPAR += 10;
+      REALLOCATE (process[0].argv, char *, NPAR);
+    }
+  }
+  process[0].argc = j;
+  REALLOCATE (process[0].argv, char *, MAX (j,1));
+  if (j == 0) {
+    fprintf (stderr, "ERROR: process %s has no arguments defined\n", procname);
+    Shutdown (1);
+  }
+    
+  process[0].pending = InitQueue ();
+  process[0].failure = (Queue *) NULL;
+  process[0].success = (Queue *) NULL;
+  process[0].cluster = InitCluster ();
+
+  return (process);
+  
+}
+
+/* need a return value */
+int MakeArgs (Process *process, Object *object, int *cargc, char ***cargv, int **cargd) {
+
+  int i;
+  int Cargc, *Cargd;
+  char **Cargv;
+
+  Cargc = process[0].argc;
+  ALLOCATE (Cargv, char *, Cargc);
+  ALLOCATE (Cargd, int, Cargc);
+  
+  /* convert par to entry */
+  for (i = 0; i < Cargc; i++) {
+    ParseLine (process[0].argv[i], object[0].argc, object[0].argv, &Cargd[i], &Cargv[i]);
+  }
+  *cargc = Cargc;
+  *cargv = Cargv;
+  *cargd = Cargd;
+
+  return (TRUE);
+
+}
+
+/* 'process' carries around a list of strings which MakeArgs uses to create the command line
+   arguments and the dependencies (cargc, cargv, cargd) */
+
+void FreeArgs (int argc, char **argv, int *argd) {
+
+  int i;
+
+  for (i = 0; i < argc; i++) {
+    free (argv[i]);
+  }
+
+  free (argv);
+  free (argd);
+
+}
+
+
+void ParseLine (char *testline, int argc, char **argv, int *depend, char **outline) {
+
+  int i, k;
+  char format[256], *line, *value;
+  int NTERMS, Nterms;
+  char **terms;
+  
+
+  NTERMS = 8;
+  ALLOCATE (terms, char *, NTERMS);
+  for (i = 0; i < NTERMS; i++) {
+    ALLOCATE (terms[i], char, 256);
+  }
+
+  Nterms = sscanf (testline, "%d%s%s%s%s%s%s%s%s%s", depend, format, 
+		   terms[0], terms[1], terms[2], terms[3], terms[4], terms[5], terms[6], terms[7]);
+  Nterms -= 2;
+      
+  for (k = 0; k < Nterms; k++) {
+    value = ExpandEntry (terms[k], argc, argv);
+    strcpy (terms[k], value);
+    free (value);
+  }
+      
+  ALLOCATE (line, char, 256);
+  switch (Nterms) {
+  case 0:
+    sprintf (line, format); 
+    break;
+  case 1:
+    sprintf (line, format, terms[0]); 
+    break;
+  case 2:
+    sprintf (line, format, terms[0], terms[1]); 
+    break;
+  case 3:
+    sprintf (line, format, terms[0], terms[1], terms[2]); 
+    break;
+  case 4:
+    sprintf (line, format, terms[0], terms[1], terms[2], terms[3]); 
+    break;
+  case 5:
+    sprintf (line, format, terms[0], terms[1], terms[2], terms[3], terms[4]); 
+    break;
+  case 6:
+    sprintf (line, format, terms[0], terms[1], terms[2], terms[3], terms[4], terms[5]); 
+    break;
+  case 7:
+    sprintf (line, format, terms[0], terms[1], terms[2], terms[3], terms[4], terms[5], terms[6]); 
+    break;
+  case 8:
+    sprintf (line, format, terms[0], terms[1], terms[2], terms[3], terms[4], terms[5], terms[6], terms[7]); 
+    break;
+  default: 
+    fprintf (stderr, "too many terms for command line argument (lim = 5)\n");
+    Shutdown (1);
+  }
+  *outline = line;
+
+  for (i = 0; i < NTERMS; i++) { free (terms[i]); }
+  free (terms);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/QueueOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/QueueOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/QueueOps.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "elixir.h"
+
+/* get object from top of stack */
+Object *GetObject (Queue *queue) {
+
+  int i;
+  Object *object;
+  
+  if (queue[0].Nobject == 0) return ((Object *) NULL);
+
+  object = queue[0].object[0];
+  queue[0].Nobject --;
+  for (i = 0; i < queue[0].Nobject; i++) {
+    queue[0].object[i] = queue[0].object[i+1];
+  }
+  return (object);
+}
+
+/* put object on bottom of stack */
+void PutObject (Queue *queue, Object *object) {
+
+  if (queue[0].Nobject == queue[0].NOBJECT) {
+    queue[0].NOBJECT += 100;
+    REALLOCATE (queue[0].object, Object *, queue[0].NOBJECT);
+  }
+  queue[0].object[queue[0].Nobject] = object;
+  queue[0].Nobject ++;
+
+}
+
+/* push object on top of stack */
+void PushObject (Queue *queue, Object *object) {
+
+  int i;
+
+  if (queue[0].Nobject == queue[0].NOBJECT) {
+    queue[0].NOBJECT += 100;
+    REALLOCATE (queue[0].object, Object *, queue[0].NOBJECT);
+  }
+  for (i = queue[0].Nobject; i > 0; i--) {
+    queue[0].object[i] = queue[0].object[i-1];
+  }
+  queue[0].object[0] = object;
+  queue[0].Nobject ++;
+
+}
+
+/* allocate queue, setup with default values, allocate data */
+Queue *InitQueue () {
+
+  Queue *queue;
+
+  ALLOCATE (queue, Queue, 1);
+
+  queue[0].Nobject = 0;
+  queue[0].NOBJECT = 50;
+  ALLOCATE (queue[0].object, Object *, queue[0].NOBJECT);
+  return (queue);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/RemoteOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/RemoteOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/RemoteOps.c	(revision 22322)
@@ -0,0 +1,189 @@
+# include "elixir.h"
+
+void HaltElixir (char *pidfile) {
+  
+  pid_t pid;
+  char username[256], machine[256];
+  char line[512];
+  int i, wsock, rsock;
+  struct stat filestat;
+
+  if (!LoadPID (pidfile, &pid, username, machine)) {
+    fprintf (stderr, "elixir is not running\n");
+    exit (0);
+  }
+
+  /* set signal to remote machine */
+  if (!rconnect (machine, CONNECT, &rsock, &wsock)) {
+    fprintf (stderr, "can't make connection to machine %s to kill process\n", machine);
+    exit (1);
+  }
+  sprintf (line, "kill -USR1 %d\n", pid);
+  write (wsock, line, strlen (line));
+
+  for (i = 0; i < 100; i++) {
+    if (stat (pidfile, &filestat) == -1) exit (0);
+    usleep (100000);
+  }
+  fprintf (stderr, "elixir is still running\n");
+  exit (2);
+}
+
+void KillElixir (char *pidfile) {
+  
+  pid_t pid;
+  char username[256], machine[256];
+  char line[512];
+  int i, wsock, rsock;
+  struct stat filestat;
+
+  if (!LoadPID (pidfile, &pid, username, machine)) {
+    fprintf (stderr, "elixir is not running\n");
+    exit (0);
+  }
+
+  /* send signal to remote machine */
+  if (!rconnect (machine, CONNECT, &rsock, &wsock)) {
+    fprintf (stderr, "can't make connection to machine %s to kill process\n", machine);
+    exit (1);
+  }
+  sprintf (line, "kill -TERM %d\n", pid);
+  write (wsock, line, strlen (line));
+
+  for (i = 0; i < 100; i++) {
+    if (stat (pidfile, &filestat) == -1) exit (0);
+    usleep (100000);
+  }
+  fprintf (stderr, "elixir is still running\n");
+  exit (2);
+}
+
+void StatusElixir (char *pidfile, char *msgfile) {
+  
+  pid_t pid;
+  char username[256], machine[256], response[256], message[512];
+  char *answer;
+  char line[512];
+  int i, done, status, wsock, rsock;
+  struct stat filestat;
+  struct timeval now, then;
+
+  if (!LoadPID (pidfile, &pid, username, machine)) {
+    fprintf (stderr, "elixir is not running\n");
+    exit (0);
+  }
+
+  sprintf (response, "%s.XXXXXX", msgfile);
+  mkstemp (response);
+  sprintf (message, "STATUS %s", response);
+  if (VERBOSE) fprintf (stderr, "sending message: %s\n", message);
+  WriteMsg (msgfile, message);
+
+  /* send signal to remote machine */
+  if (!rconnect (machine, CONNECT, &rsock, &wsock)) {
+    fprintf (stderr, "can't make connection to machine %s to signal process\n", machine);
+    exit (1);
+  }
+  sprintf (line, "kill -USR2 %d\n", pid);
+  write (wsock, line, strlen (line));
+
+  /* wait (2 sec) for file to exist, then try to read it */
+  for (i = 0; ((status = stat (response, &filestat)) == -1) && (i < 20); i++) {
+    if (VERBOSE) fprintf (stderr, "waiting for response: %d\n", status);
+    usleep (100000);
+  }
+  if (i >= 20) {
+    fprintf (stderr, "no response\n");
+    exit (2);
+  }
+
+  done = FALSE;
+  gettimeofday (&then, (void *) NULL);
+  if (VERBOSE) fprintf (stderr, "reading message: %s\n", message);
+  while (!done) {
+    status = ReadMsg (response, &answer);
+    switch (status) {
+    case 1:
+      done = TRUE;
+      break;
+    default:
+      gettimeofday (&now, (void *) NULL);
+      if (DTIME (now, then) > 5.0) {
+	fprintf (stderr, "no response from elixir\n");
+	exit (2);
+      }
+    }
+    usleep (10000);
+  }
+  fprintf (stderr, "%s\n", answer);
+  gettimeofday (&now, (void *) NULL);
+  if (VERBOSE) fprintf (stderr, "response in %f\n", DTIME (now, then));
+
+  unlink (response);
+  exit (0);
+}
+
+int HalttoRestart (char *pidfile) {
+  
+  pid_t pid;
+  char username[256], machine[256];
+  char line[512];
+  int i, wsock, rsock;
+  struct stat filestat;
+
+  if (!LoadPID (pidfile, &pid, username, machine)) {
+    fprintf (stderr, "previous elixir not running\n");
+    return (TRUE);
+  }
+  /* check username matches: can only send signals to own process */
+
+  /* make connection to remote machine */
+  if (!rconnect (machine, CONNECT, &rsock, &wsock)) {
+    fprintf (stderr, "can't make connection to machine %s to kill process\n", machine);
+    exit (1);
+  }
+	     
+  /* send TERM signal */
+  sprintf (line, "kill -TERM %d\n", pid);
+  write (wsock, line, strlen (line));
+
+  /* wait for cleanup to finish */
+  for (i = 0; i < 100; i++) {
+    if (stat (pidfile, &filestat) == -1) {
+      fprintf (stderr, "previous elixir halted\n");
+      goto success;
+    }
+    usleep (100000);
+  }
+
+  /* kill meanly */
+  sprintf (line, "kill -KILL %d", pid);
+  write (wsock, line, strlen (line));
+  unlink (pidfile);
+
+  /* test if process is still alive? */
+ success:
+  sprintf (line, "exit\n");
+  write (wsock, line, strlen(line));
+  close (wsock);
+  close (rsock);
+  return (TRUE);
+  
+}
+
+int LoadPID (char *file, pid_t *pid, char *username, char *machine) {
+
+  FILE *f;
+
+  f = fopen (file, "r");
+  if (f == (FILE *) NULL) { 
+    return (FALSE);
+  }
+
+  fscanf (f, "%*s %d", pid);
+  fscanf (f, "%*s %s", username);
+  fscanf (f, "%*s %s", machine);
+  fclose (f);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/SockScan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/SockScan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/SockScan.c	(revision 22322)
@@ -0,0 +1,53 @@
+# include "elixir.h"
+# include <errno.h>
+# define MAXTIME 10
+
+int SockScan (char *string, Fifo *fifo, int sock) {
+  
+  int i, done, status;
+
+  done = 0;
+  for (i = 0; (i < MAXTIME) && !done; i++) {
+    ShiftFifo (fifo);
+    status = ReadtoFifo (fifo, sock);
+    switch (status) {
+    case 0:
+      break;
+    case -1:
+      usleep (1000);
+      break;
+    default:
+      done = memstr (fifo[0].buffer, string, fifo[0].Nbuffer);
+      break;
+    }
+  }
+  return (status);
+}
+
+/* returns (offset to m2) + 1, or 0 if failure */ 
+int memstr (char *m1, char *m2, int n) {
+
+  int i, N;
+
+  N = strlen (m2);
+  for (i = 0; (i < n - N + 1) && memcmp (m1, m2, N); i++, m1++);
+  if (memcmp (m1, m2, N)) {
+    return (0);
+  }
+  else {
+    return (i+1);
+  }
+
+}
+ 
+/* SockScan reads from the given socket looking for the given string
+   SockScan stores data read from socket in the fifo buffer to ensure 
+   it catches the given string.  a single invocation of SockScan will 
+   wait for up to 10 msec for data to come down the pipe before giving up.
+   SockScan can only search for one string in the output stream.
+   */
+
+
+/* need a way of signalling that the socket has closed...
+   maybe change the sock entry to 0?
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/StartMachine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/StartMachine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/StartMachine.c	(revision 22322)
@@ -0,0 +1,35 @@
+# include "elixir.h"
+
+int StartMachine (Machine *machine, Object *object, int argc, char **argv) {
+
+  int i, Nbyte, Nout;
+  char command[1024];
+  FILE *logfile;
+
+  logfile = LogOpen (object[0].logfile);
+  machine[0].object = object;
+
+  Nbyte = Nout = 0;
+  for (i = 0; i < argc; i++) {
+    Nout = sprintf (&command[Nbyte], "%s ", argv[i]);
+    Nbyte += Nout;
+  }
+    
+  fprintf (logfile, "%s @ %s: %s\n", object[0].argv[0], machine[0].hostname, command);
+  if (logfile != stderr) {
+    fflush (logfile);
+    fclose (logfile);
+  }
+
+  Nout = sprintf (&command[Nbyte], "\n echo PROCESS DONE\n");
+  write (machine[0].wsock, command, strlen(command));
+
+  gettimeofday (&machine[0].object[0].start, (void *) NULL);
+  gettimeofday (&machine[0].quiet, (void *) NULL);
+
+  machine[0].status = BUSY;
+  return (TRUE);
+}
+
+/* machine[0].quiet times since last message received from process. 
+   if process is too quiet for too long, we assume it died */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/elixir.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/elixir.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/elixir.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include "elixir.h"
+
+static int Dynamic;
+static int Reload;
+
+static int      Nobjects;
+static int      Nprocess;
+static Process **process;
+static Process   global;
+
+int main (int argc, char **argv) {
+  
+  int i, Nnew, state;
+  char *config;
+  int nloop;
+  char **targv;
+
+  /* save complete arglist incase we reload */
+  ALLOCATE (targv, char *, argc + 1);
+  for (i = 0; i < argc; i++) {
+    targv[i] = strcreate (argv[i]);
+  }
+  targv[i] = (char *) NULL;
+
+  config = ConfigInit (&argc, argv);
+
+  process = DefineProcesses (&global, &Nprocess, config);
+
+  /* PID filename stored in global.argv[7] */
+  ConfigPID (global.argv[7]);
+
+  InitMsgFile (global.argv[0]); /* success */
+  InitMsgFile (global.argv[1]); /* failure */
+  InitMsgFile (global.argv[5]); /* message */
+  InitMsgFile (global.argv[6]); /* end stat */
+  InitMachines (config);
+  free (config);
+
+  SetMessageFile (global.argv[5]);
+
+  InitProcessTimers (process, Nprocess);
+
+  Nobjects = 0;
+  Reload = FALSE;
+  Dynamic = TRUE;
+  nloop = 0;
+
+  /* any remaining entry is a file to load in static mode */
+  if (argc == 2) {
+    Dynamic = FALSE;
+    Nobjects = LoadPending (&global, argv[1], &state, &Dynamic);
+    if (state) {
+      fprintf (stderr, "error with input file %s (state %d)\n", argv[1], state);
+      Shutdown (1);
+    }
+  }
+
+  while (CheckEndingState (&global, Nobjects, Dynamic)) {
+
+    if (Dynamic) { 
+      Nnew = LoadPending (&global, global.argv[4], &state, &Dynamic);
+      Nobjects += Nnew;
+    }
+
+    for (i = 0; i < Nprocess; i++) {
+      CheckProcess (process[i]);
+    }
+
+    for (i = Nprocess - 1; i >= 0; i--) {
+      CheckProcess (process[i]);
+    }
+
+    CheckMessages ();
+    /* CheckMessages (&global, process, Nprocess, &Dynamic, Nobjects); */
+    RestartMachines ();
+
+    usleep (1000000);
+    if (nloop > 3000) {
+      fprintf (stderr, ".");
+      nloop = 0;
+    }
+    nloop ++;
+  }
+
+  DumpProcessTimes (global.argv[6]);
+ 
+  /* DumpStatus (global.argv[6], &global, process, Nprocess, Dynamic, Nobjects);  */
+  DumpStatus (global.argv[6]);
+
+  WriteMsg (global.argv[6], "DONE");
+
+  if (Reload) Restart (targv);
+
+  Shutdown (0);
+  exit (0);
+}
+
+void SIG_RELOAD (int sig) {
+  fprintf (stderr, "got signal RELOAD\n");
+  SetExitTimer ();
+  Dynamic = FALSE;
+  Reload = TRUE;
+}
+
+void ElixirStop () {
+  Dynamic = FALSE;
+}
+
+int GetDynamicState () {
+  return (Dynamic);
+}
+
+Process **GetProcessInfo (Process **gb, int *np, int *no) {
+  *np = Nprocess;
+  *no = Nobjects;
+  *gb = &global;
+  return (process);
+}
+
+/* create 'reload' mode:
+   0 - Reload = TRUE;
+   1 - Dynamic = FALSE;
+   2 - start timer (in CheckEndingState.c)
+   3 - if timer expires, set exit state
+   4 - if (Reload) exec (argc, arg)
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/rconnect.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/rconnect.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/src/rconnect.c	(revision 22322)
@@ -0,0 +1,100 @@
+# include "elixir.h"
+
+/* rconnect opens a remote shell on hostname and returns two file descriptors:
+   one for read, one for write */
+
+int rconnect (char *hostname, char *command, int *rsock, int *wsock) {
+
+  int i, rfd[2], wfd[2], status;
+  pid_t pid;
+  char buffer[0x4000];
+  char *file;
+  Fifo fifo;
+
+  InitFifo (&fifo, 0x4000, 0x1000);
+
+  status = pipe (rfd);
+  if (status < 0) {
+    perror ("pipe");
+    return (FALSE);
+  }
+  status = pipe (wfd);
+  if (status < 0) {
+    perror ("pipe");
+    return (FALSE);
+  }
+
+  file = filebasename (command);
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    fprintf (stderr, "starting remote connection to %s...", hostname);
+    /* close the excess sockets */
+    close (wfd[1]);
+    close (rfd[0]);
+    dup2 (wfd[0], STDIN_FILENO);
+    dup2 (rfd[1], STDOUT_FILENO);
+    dup2 (rfd[1], STDERR_FILENO);
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+    fprintf (stderr, "child is spawned\n");
+
+    status = execl (command, file, hostname, "/bin/csh", NULL); 
+    fprintf (stderr, "error starting remote shell process\n");
+    Shutdown (1);
+  }
+  *wsock = wfd[1];
+  *rsock = rfd[0];
+  close (wfd[0]);
+  close (rfd[1]);
+   
+  fcntl (*rsock, F_SETFL, O_NONBLOCK);
+  fcntl (*wsock, F_SETFL, O_NONBLOCK);
+
+  sprintf (buffer, "echo PTOLEMY STARTED\n");
+  status = write (*wsock, buffer, strlen(buffer));
+  if ((status == -1) && (errno == EPIPE)) {
+    fprintf (stderr, "socket closed unexpectedly\n");
+    close (*wsock);
+    close (*rsock);
+    return (FALSE);
+  }
+
+  /* try to get evidence connection is alive - wait upto a few seconds */
+  status = -1;
+  for (i = 0; (i < 300) && (status == -1); i++) {
+    fcntl (*rsock, F_SETFL, O_NONBLOCK);
+    status = SockScan ("PTOLEMY STARTED", &fifo, *rsock);
+    if (!(i % 30)) fprintf (stderr, ".");
+    if (status == 0) {
+      fprintf (stderr, "socket closed unexpectedly\n");
+      close (*wsock);
+      close (*rsock);
+      return (FALSE);
+    }
+  }
+  if (status == -1) {
+    fprintf (stderr, "timeout while connecting\n");
+    close (*wsock);
+    close (*rsock);
+    return (FALSE);
+  }
+  fprintf (stderr, "Connected\n");
+
+  /* the onintr command works with csh/tcsh type shells to
+     prevent trapping of SIGTERM, used to kill hung jobs.
+     for bash/sh type shells, you can use SIGQUIT instead
+     without setting onintr */
+  sprintf (buffer, "onintr\n");
+  write (*wsock, buffer, strlen(buffer));
+  if ((status == -1) && (errno == EPIPE)) {
+    fprintf (stderr, "socket closed unexpectedly\n");
+    close (*wsock);
+    close (*rsock);
+    return (FALSE);
+  }
+
+  FreeFifo (&fifo);
+  return (pid);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/README	(revision 22322)
@@ -0,0 +1,149 @@
+
+This directory includes a very simple elixir script, test.pro, to test
+that elixir is working correctly.  
+
+To run it, create an input list with one element per line.  The
+elements can be any word; an example is provided in test.list.
+
+Make sure the machines listed above represent valid machine names in
+your cluster.  Also, be certain that you can rsh to these machines
+without a password.
+
+Run the test by exectuting:
+
+elixir -c test.pro test.list
+
+the result of this test will be several files in your home directory:
+
+elixir-test.log
+elixir-test.end
+elixir-test.success
+
+elixir-test.log:
+
+The first file, elixir-test.log, shows the output from all of the
+processing steps.  In this test example, the log file is defined to be
+a single file, but it is also possible to use the &0 style
+command-line arguments to create a different file for each input line.
+All output from each of the processes goes into this file in the order
+received by the elixir program.  The resulting lines may look a bit
+confusing sometime as a result.  In addition, elixir adds some extra
+information to aid in debugging.  Here are a few example lines:
+
+foo @ kiawe: ls foo ; echo SUCCESS 
+bar @ milo: ls bar ; echo SUCCESS 
+foo @ kiawe:ls: foo: No such file or directory
+SUCCESS
+PROCESS DONE
+foo @ kiawe is done
+bar @ milo:ls: bar: No such file or directory
+SUCCESS
+PROCESS DONE
+bar @ milo is done
+
+Every output line from the processes are prepended with:
+
+(&0) @ (machine): 
+
+In this example, I used the two machines, kiawe & milo.  The first two
+lines show the first two commands being executed: 
+
+ls foo ; echo SUCCESS
+
+The next three lines show the output from the process executing on foo:
+
+ls: foo: No such file or directory 
+SUCCESS
+PROCESS DONE
+
+The 'ls' command did not find a file 'foo' in the home directory -
+note that all commands, since they are run remotely, execute in the
+user's home directory.  The word "SUCCESS" comes from the 'echo
+SUCCESS' portion of the command.  Currently, elixir uses the words
+SUCCESS, ERROR, and PROCESS DONE for process monitoring.  Ideally, the
+program called by elixir should check its output state and echo either
+SUCCESS or ERROR as appropriate.  In this test example, we simply
+force all processes to claim they succeeded with the 'echo SUCCESS'
+command.  The PROCESS DONE is added by elixir when it creates the
+command.  This lets elixir detect programs which crash and thereby
+fail to send either SUCCESS or ERROR.
+
+Side Note: this SUCCESS / ERROR flow control construct will soon be
+replaced by tests of the actual process exit status.  This will make
+elixir able to more cleanly support unix commnands.  
+
+The 6th line in the example above is the output from elixir noting
+that the process for 'foo' ended.  The next few lines show the output
+for the process on 'bar'.
+
+elixir-test.end:
+
+This file is created at the end of the elixir run and provides
+information about the run.  This same status information can be
+retrieved from elixir while it is running by using the 'status'
+program.  Here is the output from the above example:
+
+##################################################################
+               .   test1            test2            test3         
+           kiawe   0.120 3          5.056 2          0.130 1        
+            milo   0.110 1          5.062 2          0.109 3        
+processes status:
+            name   pending  success  failure  total
+----------------------------------------------------------------
+          global         0        4        0      4
+           test1         0
+           test2         0
+           test3         0
+
+machine status:
+          name  process  status
+----------------------------------------------------------------
+         kiawe   (none)       0
+          milo   (none)       0
+
+objects loaded so far: 4
+
+NOT accepting input from FIFO
+DONE
+##################################################################
+
+The first section shows a grid with processes across the top and
+machines in the left column.  The entries for each pair of machine &
+process are the average execution time and the number of instances of
+the given process which was run on the given machine.  So, in this
+example, the process 'test1' (which is the 'ls') was run on 'kiawe' 3
+times for an average of 0.120 sec and on 'milo' for an average of
+0.110 sec.  
+
+The second section shows the state of the different queues when the
+elixir process ended.  The left hand column shows the possible queues
+(global, test1, test2, test3).  For all nodes except the global node,
+the success and failure queues are always linked to a queue of another
+node.  Jobs which land in the success or failure queue of a node are
+instantly moved to the pending queue of another job.  Therefore, we
+only show the pending queue for the nodes except for 'global'.  Each
+number represents the number of jobs waiting in the given queue.
+(note: the global.pending queue is identical to one of the other
+pending queues and therefore represents a duplication).  The 'total'
+entry is the sum of jobs in each of the global queues.  Note that this
+table does not show the processes which are actually being executed,
+as these are considered to be 'in' a node, not on the queue of a node.
+They are represented in the next section.  
+
+The third section lists the status of all machines available.  The
+first column is the machine name; the second is the name of the
+process being executed, or '(none)' if the machine is idle; the third
+is a numerical status indication (which is generally ignored).  
+
+The next line shows the number of objects loaded by elixir.  Since
+elixir has the capability of loading new objects dynamically, this
+value on this line may increment as time goes by.  The next line shows
+whether elixir is accepting input during runtime or not.
+
+elixir-test.success:
+
+This file lists the processes which elixir successfully finished.  (An
+equivalent file, elixir-test.failure, would include jobs which did not
+succeed).  Entries are added to this file as the jobs complete.  In
+this way, it can be used to monitor the process of an elixir run.  
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/save.source
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/save.source	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/save.source	(revision 22322)
@@ -0,0 +1,4 @@
+msg1
+msg2
+msg3
+msg4
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/test.end
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/test.end	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/test.end	(revision 22322)
@@ -0,0 +1,21 @@
+               .   test1            test2            test3         
+       localhost   1.565 2          5.180 2          1.030 2        
+       localhost   1.575 2          5.180 2          1.030 2        
+processes status:
+            name   pending  success  failure  total
+----------------------------------------------------------------
+          global         0        4        0      4
+           test1         0
+           test2         0
+           test3         0
+
+machine status:
+          name  process  status
+----------------------------------------------------------------
+     localhost   (none)       0
+     localhost   (none)       0
+
+objects loaded so far: 4
+
+NOT accepting input from FIFO
+DONE
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/test.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/test.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/test.pro	(revision 22322)
@@ -0,0 +1,54 @@
+# Elixir test config file
+
+CONNECT         /usr/bin/ssh
+MACHINE		localhost
+MACHINE		localhost
+elixir		test
+
+# FIFO files
+global.success	$elixir.success
+global.failure	$elixir.failure
+global.source	$elixir.source
+global.msg	$elixir.msg
+global.end	$elixir.end
+global.pid	$elixir.pid
+
+# process definition
+global.Nargs	1
+global.logfile  0 %s $elixir.log
+global.pending	test1
+global.timeout	100
+
+# test1: ls (will ls &0 user home dir)
+process	test1 
+test1.arg 0 ls
+test1.arg 0 %s  &0
+test1.arg 0 ;
+test1.arg 0 echo
+test1.arg 0 SUCCESS
+test1.success test2
+test1.failure global
+
+# test2 sleep 5
+process	test2 
+test2.arg 0 sleep
+test2.arg 0 5
+test2.arg 0 ;
+test2.arg 0 echo
+test2.arg 0 SUCCESS
+test2.success test3
+test2.failure global
+
+# test3 echo &0
+process	test3 
+test3.arg 0 echo 
+test3.arg 0 %s  &0
+test3.arg 0 ;
+test3.arg 0 echo
+test3.arg 0 SUCCESS
+test3.success global
+test3.failure global
+
+# this is a very simple elixir script to test that elixir is working
+# correctly.  See the README file for instructions.
+ 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/test.success
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/test.success	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/elixir/test/test.success	(revision 22322)
@@ -0,0 +1,4 @@
+msg1 0000005d test3
+msg2 0000005d test3
+msg3 0000005d test3
+msg4 0000005d test3
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/Makefile	(revision 22322)
@@ -0,0 +1,33 @@
+default: uniphot
+help:
+	@echo "make options: uniphot (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/fixcat
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+uniphot: $(BIN)/uniphot.$(ARCH)
+install: $(DESTBIN)/uniphot
+
+FIXCAT = \
+$(SRC)/fixcat.$(ARCH).o 	$(SRC)/gcatalog.$(ARCH).o   \
+$(SRC)/coordops.$(ARCH).o	$(SRC)/sorts.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o 	$(SRC)/config.$(ARCH).o     \
+$(SRC)/Fread.$(ARCH).o		$(SRC)/check_lockfile.$(ARCH).o \
+$(SRC)/find_images.$(ARCH).o	$(SRC)/match_images.$(ARCH).o	\
+$(SRC)/gregions.$(ARCH).o	$(SRC)/aregion.$(ARCH).o	\
+$(SRC)/find_funnymags.$(ARCH).o	$(SRC)/gcatstats.$(ARCH).o\
+$(SRC)/find_matches.$(ARCH).o	$(SRC)/check_permissions.$(ARCH).o
+
+$(UNIPHOT): $(INC)/uniphot.h
+$(BIN)/uniphot.$(ARCH): $(UNIPHOT)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/doc/example.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/doc/example.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/doc/example.txt	(revision 22322)
@@ -0,0 +1,13 @@
+
+rats: foreach f ( */*.cpt )
+foreach? echo $f
+foreach? echo $f >> bright.log
+foreach? (markstar -v $f >> bright.dat) >>& bright.log
+foreach? end
+
+
+
+ echo $f
+ echo $f >> bright.log
+ (markstar -v $f >> bright.dat) >>& bright.log
+ end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/include/markstar.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/include/markstar.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/include/markstar.h	(revision 22322)
@@ -0,0 +1,42 @@
+# include <ohana.h>
+# include <dvo.h>
+
+int    VERBOSE;
+int    RESET;
+int    FORCE_RUN;
+
+/* global variables set in parameter file */
+char   CATDIR[256];
+char   GSCDIR[256];
+char   ImageCat[256];
+char   GSCFILE[256];
+
+double RADIUS;
+double TRAIL_WIDTH;
+int    NBINS;
+int    NPTSINLINE;
+double MIN_DENSITY;
+double NSIGMA;
+
+double BRIGHT_HALO_MAG;
+double BRIGHT_HALO_SLOPE;
+double BRIGHT_XTRAIL_WIDTH;
+double BRIGHT_XTRAIL_MAG;
+double BRIGHT_XTRAIL_SLOPE;
+double BRIGHT_YTRAIL_WIDTH;
+double BRIGHT_YTRAIL_MAG;
+double BRIGHT_YTRAIL_SLOPE;
+
+double GHOST_MAG;
+double GHOST_RADIUS;
+double OPTICAL_AXIS1;
+double OPTICAL_AXIS2;
+
+typedef struct {
+  Coords coords;
+  double *X, *Y;
+  int *N;
+  double RA[2], DEC[2];
+  double Area, density, spacing;
+} CatStats;
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "markstar.h"
+
+ConfigInit (int argc, char **argv) {
+
+  char *config, *file;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (&argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  /* used in other pipeline functions */
+  ScanConfig (config, "CATDIR",          "%s",  0, CATDIR);
+  ScanConfig (config, "GSCDIR",          "%s",  0, GSCDIR);
+  ScanConfig (config, "GSCFILE",         "%s",  0, GSCFILE);
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  /* unique to markstar */
+  ScanConfig (config, "SEARCH_RADIUS",   "%lf", 0, &RADIUS);
+  ScanConfig (config, "TRAIL_WIDTH",     "%lf", 0, &TRAIL_WIDTH);
+  ScanConfig (config, "NANGLE_BINS",     "%d",  0, &NBINS);
+  ScanConfig (config, "NPTSINLINE",      "%d",  0, &NPTSINLINE);
+  ScanConfig (config, "MIN_DENSITY",     "%lf", 0, &MIN_DENSITY);
+  ScanConfig (config, "SPACE_SIGMA",     "%lf", 0, &NSIGMA); 
+
+  ScanConfig (config, "BRIGHT_HALO_MAG",     "%lf", 0, &BRIGHT_HALO_MAG);
+  ScanConfig (config, "BRIGHT_HALO_SLOPE",   "%lf", 0, &BRIGHT_HALO_SLOPE);
+  ScanConfig (config, "BRIGHT_XTRAIL_WIDTH", "%lf", 0, &BRIGHT_XTRAIL_WIDTH);
+  ScanConfig (config, "BRIGHT_XTRAIL_MAG",   "%lf", 0, &BRIGHT_XTRAIL_MAG);
+  ScanConfig (config, "BRIGHT_XTRAIL_SLOPE", "%lf", 0, &BRIGHT_XTRAIL_SLOPE);
+  ScanConfig (config, "BRIGHT_YTRAIL_WIDTH", "%lf", 0, &BRIGHT_YTRAIL_WIDTH);
+  ScanConfig (config, "BRIGHT_YTRAIL_MAG",   "%lf", 0, &BRIGHT_YTRAIL_MAG);
+  ScanConfig (config, "BRIGHT_YTRAIL_SLOPE", "%lf", 0, &BRIGHT_YTRAIL_SLOPE);
+
+  ScanConfig (config, "GHOST_MAG",       "%lf", 0, &GHOST_MAG);
+  ScanConfig (config, "GHOST_RADIUS",    "%lf", 0, &GHOST_RADIUS);
+  ScanConfig (config, "OPTICAL_AXIS1",   "%lf", 0, &OPTICAL_AXIS1);
+  ScanConfig (config, "OPTICAL_AXIS2",   "%lf", 0, &OPTICAL_AXIS2);
+ 
+  free (config);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/aregion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/aregion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/aregion.c	(revision 22322)
@@ -0,0 +1,118 @@
+# include "markstar.h"
+
+double BigDecBounds[] = {0.0, 7.5, 15.0, 22.5, 30.0, 37.5, 45.0, 
+			 52.5, 60.0, 67.5, 75.0, 82.5, 90.0,
+			 0.0, -7.5, -15.0, -22.5, -30.0, -37.5, -45.0, 
+			 -52.5, -60.0, -67.5, -75.0, -82.5, -90.0};
+char *DecSections[] = {"N0000", "N0730", "N1500", "N2230", "N3000", "N3730", "N4500", 
+		       "N5230", "N6000", "N6730", "N7500", "N8230", "weirdness", 
+		       "S0000", "S0730", "S1500", "S2230", "S3000", "S3730", "S4500", 
+		       "S5230", "S6000", "S6730", "S7500", "S8230", "weirdness"};
+
+char *Dec2Sections[] = {"n0000", "n0730", "n1500", "n2230", "n3000", "n3730", "n4500", 
+			"n5230", "n6000", "n6730", "n7500", "n8230", "weirdness", 
+			"s0000", "s0730", "s1500", "s2230", "s3000", "s3730", "s4500", 
+			"s5230", "s6000", "s6730", "s7500", "s8230", "weirdness"};
+
+char *disk[] = {"disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", 
+		"disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "weirdness", 
+		"disk 1", "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", 
+		"disk 2", "disk 2", "disk 2", "disk 2", "disk 2", "weirdness"};
+
+int NBigRASections [] = {48, 47, 45, 43, 40, 36, 32, 28, 21, 15, 9, 3, 3, 48, 47, 45, 43, 40, 36, 32, 28, 21, 15, 9, 3, 3};
+
+int NDecLines[] = {593, 584, 551, 530, 522, 465, 406, 362, 280, 198, 123, 24, 
+                   0, 597, 578, 574, 577, 534, 499, 442, 376, 294, 212, 144, 48};
+
+/* find region file which contains ra, dec */
+aregion (region, f, ra, dec) 
+GSCRegion region[];
+FILE *f;
+double ra, dec;
+{
+  
+  
+  char buffer[28800], temp[50], file[50];
+  double dr, dd;
+  double RA0, RA1, DEC0, DEC1;
+  int i, NBigDec, NLINES, done, nregion;
+  
+  while (ra < 0) { ra += 360.0; }
+  while (ra >= 360.0) { ra -= 360.0; }
+    
+  if (dec >= 86.25) {
+    sprintf (file, "%s/n8230/pole.cpt\0", GSCDIR);
+    region[0].DEC[0] = 86.25;
+    region[0].DEC[1] = 93.75;
+    region[0].RA[0] = -180.0;
+    region[0].RA[1] =  540.0;
+    strcpy (region[0].filename, file);
+    return;
+  }
+    
+  NBigDec = -1;
+  for (i = 0; i < 12; i++) {
+# ifdef DEBUG
+    fprintf (stderr, "%d %f %f %f\n", i, dec, BigDecBounds[i], BigDecBounds[i+1]);
+# endif
+    if ((dec >= BigDecBounds[i]) && (dec < BigDecBounds[i+1])) {
+      NBigDec = i;
+      break;
+    }
+  }
+  if (NBigDec < 0) {
+    for (i = 13; i < 24; i++) {
+# ifdef DEBUG
+      fprintf (stderr, "%d %f %f %f\n", i, dec, BigDecBounds[i], BigDecBounds[i+1]);
+# endif
+      if ((dec < BigDecBounds[i]) && (dec >= BigDecBounds[i+1])) {
+	NBigDec = i;
+	break;
+      }
+    }
+  }
+  if (NBigDec < 0) {
+    fprintf (stderr, "dec out of range: %f\n", dec);
+  }
+    
+  NLINES = 0;
+  for (i = 0; i < NBigDec; i++) {
+    NLINES += NDecLines[i];
+  }
+  fseek (f, 5*2880 + 48*NLINES, SEEK_SET);
+      
+  done = FALSE;
+  Fread (buffer, 1, 48*NDecLines[NBigDec], f, "char");
+  for (i = 0; !done && (i < NDecLines[NBigDec]); i++) {
+    strncpy (temp, &buffer[i*48], 48);
+    temp[49] = 0;
+    hstgsc_hms_to_deg (&RA0, &RA1, &DEC0, &DEC1, &temp[7]);
+    if (RA1 < RA0) RA1 += 360.0;
+# ifdef DEBUG
+    fprintf (stderr, "%f %f %f  %f %f %f  %s\n", DEC0, dec, DEC1, RA0, ra, RA1, temp);
+# endif
+    if ((dec >= 0) && (dec >= DEC0) && (dec < DEC1) && (ra >= RA0) && (ra < RA1)) {
+      done = TRUE;
+    }
+    if ((dec < 0) && (dec < DEC0) && (dec >= DEC1) && (ra >= RA0) && (ra < RA1)) {
+      done = TRUE;
+    }
+  }
+  if (!done) {
+    fprintf (stderr, "error in search: %f %f\n", ra, dec);
+    exit (0);
+  }
+  temp[5] = 0;
+  sprintf (file, "%s/%s/%s.cpt\0", GSCDIR, Dec2Sections[NBigDec],&temp[1]);
+  if (DEC0 < DEC1) {
+    region[0].DEC[0] = DEC0;
+    region[0].DEC[1] = DEC1;
+  } else {
+    region[0].DEC[0] = DEC1;
+    region[0].DEC[1] = DEC0;
+  }     
+  region[0].RA[0] = RA0;
+  region[0].RA[1] = RA1;
+  strcpy (region[0].filename, file);
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/check_lockfile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/check_lockfile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/check_lockfile.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "markstar.h"
+
+check_lockfile ()
+{
+  
+  FILE *f;
+  char filename[128];
+  struct stat filestat;
+
+  sprintf (filename, "%s/lock\0", CATDIR);
+  if (stat (filename, &filestat) != -1) {
+    fprintf (stderr, "ERROR: catalog %s is locked, try again later\n", CATDIR);
+    exit (0);
+  }
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't set lock file %s\n", filename);
+    exit (0);
+  }
+  fclose (f);
+
+}
+
+clear_lockfile ()
+{
+  
+  char filename[128], line[256];
+  struct stat filestat;
+
+  sprintf (filename, "%s/lock\0", CATDIR);
+  if (stat (filename, &filestat) != -1) {
+    sprintf (line, "rm %s\0", filename);
+    system (line);
+  } else {
+    fprintf (stderr, "can't remove lockfile, why not?\n");
+  }
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/check_permissions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/check_permissions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/check_permissions.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "markstar.h"
+
+check_permissions (char *basefile) {
+  
+  FILE *f;
+  char *c, dir[256], filename[256];
+  struct stat filestat;
+  uid_t fuid, uid;
+  gid_t fgid, gid;
+  int status;
+
+  uid = getuid();
+  gid = getgid();
+
+  /* check permission to write to directory */
+  sprintf (filename, "%s\0", basefile);
+  c = strrchr (filename, '/');
+  if (c == (char *) NULL) {
+    strcpy (dir, ".");
+  } else {
+    *c = 0;
+    strcpy (dir, filename);
+  }
+  status = stat (dir, &filestat);
+  if (status == -1) {
+    fprintf (stderr, "ERROR: can't write to %s\n", dir);
+    exit (0);
+  } 
+  if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRWXU)) ||
+      ((gid == filestat.st_gid) && (filestat.st_mode & S_IRWXG)) || 
+      (filestat.st_mode & S_IRWXO)) {
+  } else {
+    fprintf (stderr, "ERROR: can't write to %s\n", dir);
+    exit (0);
+  }
+  
+  /* check permission to write to file */
+  sprintf (filename, "%s\0", basefile);
+  status = stat (filename, &filestat);
+  if (status == 0) { /* file exists, are permissions OK? */
+    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR) && (filestat.st_mode & S_IWUSR)) ||
+	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP) && (filestat.st_mode & S_IWGRP)) || 
+	((filestat.st_mode & S_IROTH) && (filestat.st_mode & S_IWOTH))) {
+    } else {
+      fprintf (stderr, "ERROR: can't write to %s\n", filename);
+      exit (0);
+    }
+  }
+  
+  /* check permission to write to backup file */
+  sprintf (filename, "%s~\0", basefile);
+  status = stat (filename, &filestat);
+  if (status == 0) { /* file exists, are permissions OK? */
+    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR) && (filestat.st_mode & S_IWUSR)) ||
+	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP) && (filestat.st_mode & S_IWGRP)) || 
+	((filestat.st_mode & S_IROTH) && (filestat.st_mode & S_IWOTH))) {
+    } else {
+      fprintf (stderr, "ERROR: can't write to %s\n", filename);
+      exit (0);
+    }
+  }
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/config.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/config.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/config.c	(revision 22322)
@@ -0,0 +1,110 @@
+# include <ohana.h>
+# define D_NBYTES 4096
+
+char *LoadConfigFile (filename) 
+char *filename; 
+{
+  
+  FILE *f;
+  int i, done, Nbytes, NBYTES, size;
+  char *config;
+  
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    fprintf (stderr, "couldn't find %s\n", filename);
+    return ((char *) NULL);
+  }
+ 
+  NBYTES = D_NBYTES;
+  ALLOCATE (config, char, NBYTES);
+ 
+  size = 0;
+  done = FALSE;
+  for (i = 0; !done; i++) {
+    Nbytes = Fread (&config[i*D_NBYTES], sizeof(char), D_NBYTES, f, "char");
+    size += Nbytes;
+    if (Nbytes < D_NBYTES) 
+      done = TRUE;
+    else {
+      NBYTES += D_NBYTES;
+      REALLOCATE (config, char, NBYTES);
+    }
+  }
+  
+  config[size] = '\n';
+  config[size+1] = 0;
+  REALLOCATE (config, char, size + 2);
+ 
+  fclose (f);
+
+  return (config);
+}
+
+int ScanConfig /* we expect one more field: the pointer to the value requested */
+# ifndef ANSI
+(config, field, mode, va_alist) 
+ char *config; char *field, *mode; va_dcl
+# else
+(char       config[],
+ char       field[],
+ char       mode[],...)
+# endif
+{
+ 
+  int i, j;
+  char *p, *p2, tmp[256];
+  va_list argp;
+  double value;
+  
+# ifndef ANSI
+  va_start (argp);
+# else
+  va_start (argp, N);
+# endif
+  
+  /* find the correct line with field */
+  p2 = config;
+  for (i = 0; ; i++) {
+    if (!strncmp (field, p2, strlen(field))) {
+      p = p2 + strlen (field);
+      break;
+    }
+    else {
+      p2 = strchr (p2, '\n');
+      if (p2 == (char *) NULL) {
+	fprintf (stderr, "entry %s not found in config file\n", field);
+	return (FALSE);
+      }
+      else p2++;
+    }
+  }
+  if (!strcmp (mode, "%s")) {
+    p2 = strchr (p, '\n');
+    if (p2 == (char *) NULL)
+      p2 = config + strlen(config);
+    bcopy (p, tmp, (p2-p));
+    tmp[(p2-p)] = 0;
+    stripwhite (tmp);
+    p2 = va_arg (argp, char *);
+    strcpy (p2, tmp);
+  }
+  else {
+ 
+    /* try to get a numerical value from the field */
+    value = strtod (p, &p2);
+    if ((*p2 == 'd') || (*p2 == 'D')) 
+      value *= pow (10.0, atof (p2 + 1));
+    
+    if (!strcmp (mode, "%d"))  *va_arg (argp, int *)       = value;
+    if (!strcmp (mode, "%u"))  *va_arg (argp, unsigned *)  = value;
+    if (!strcmp (mode, "%ld")) *va_arg (argp, long *)      = value;
+    if (!strcmp (mode, "%hd")) *va_arg (argp, short *)     = value;
+    if (!strcmp (mode, "%f"))  *va_arg (argp, float *)     = value;
+    if (!strcmp (mode, "%lf")) *va_arg (argp, double *)    = value;
+    
+  }
+ 
+  va_end (argp);
+  return (TRUE);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_bright_stars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_bright_stars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_bright_stars.c	(revision 22322)
@@ -0,0 +1,142 @@
+# include "markstar.h"
+
+find_bright_stars (catalog, catstats)
+Catalog catalog[];
+CatStats catstats[];
+{
+
+  int i, j, n, m, first_j, Nave, Ngsc;
+  Catalog GSCdata;
+  double MinRA, MinDEC, MaxRA, MaxDEC, RaCenter, DecCenter;
+  Coords *tcoords;
+  double *X1, *Y1, *X2, *Y2;
+  int *N1, *N2;
+  char *mark;
+  double dX, dY, dR, MaxDist, MaxDist1, MaxRadius, radius, radius2;
+
+  load_gsc_data (&GSCdata, catstats);
+
+  Nave = catalog[0].Naverage;
+  Ngsc = GSCdata.Naverage;
+    
+  /* in the function below, it is better to have the catalog with
+     more stars associated with index 2 (j) */ 
+  X2 = catstats[0].X;
+  Y2 = catstats[0].Y;
+  N2 = catstats[0].N;
+  ALLOCATE (X1, double, Ngsc);
+  ALLOCATE (Y1, double, Ngsc);
+  ALLOCATE (N1, int, Ngsc);
+  ALLOCATE (mark, char, Nave);
+  bzero (mark, Nave);
+
+  tcoords = &catstats[0].coords;
+  for (i = 0; i < Ngsc; i++) {
+    RD_to_XY (&X1[i], &Y1[i], GSCdata.average[i].R, GSCdata.average[i].D, tcoords);
+    N1[i] = i;
+  }
+  if (Ngsc > 1) sort_coords_index (X1, Y1, N1, Ngsc);
+  
+  /* first find stellar halos */
+  /* max radius (mag = -1) */
+  /** the j index moves quickly and is better associated with the catalog with more stars */
+  MaxRadius = BRIGHT_HALO_SLOPE * (-1.0 - BRIGHT_HALO_MAG); 
+  /** find catalog stars near GSC stars **/
+  for (i = j = 0; (i < Ngsc) && (j < Nave); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -2*MaxRadius) {
+      i++;
+      continue;
+    }
+    if (dX >= 2*MaxRadius) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    radius = MAX (BRIGHT_HALO_SLOPE * (0.001*GSCdata.average[N1[i]].M - BRIGHT_HALO_MAG), 0.0);
+    if (radius == 0) {
+      i++; 
+      continue;
+    }
+    radius2 = radius*radius;
+    for (; (dX > -2*radius) && (j < Nave); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      dR = dX*dX + dY*dY;
+      if (dR < radius2) {  /* new measurement of this star */
+	mark[j] = TRUE;
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  
+  /* next find y spikes */
+  /** find catalog stars near GSC stars **/
+  for (i = j = 0; (i < Ngsc) && (j < Nave); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -BRIGHT_YTRAIL_WIDTH) {
+      i++;
+      continue;
+    }
+    if (dX >= BRIGHT_YTRAIL_WIDTH) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    MaxDist = MAX (BRIGHT_YTRAIL_SLOPE * (0.001*GSCdata.average[N1[i]].M - BRIGHT_YTRAIL_MAG), 0.0);
+    for (; (dX > -BRIGHT_YTRAIL_WIDTH) && (j < Nave); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      if ((fabs(dX) < BRIGHT_YTRAIL_WIDTH) && (fabs(dY) < MaxDist)) {  /* star on spike */
+	mark[j] = TRUE;
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  
+  /* next find x spikes */
+  /** find catalog stars near GSC stars **/
+  MaxDist = MAX (BRIGHT_XTRAIL_SLOPE * (-1.0 - BRIGHT_XTRAIL_MAG), 0.0);
+  for (i = j = 0; (i < Ngsc) && (j < Nave); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -MaxDist) {
+      i++;
+      continue;
+    }
+    if (dX >= MaxDist) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    MaxDist1 = MAX (BRIGHT_XTRAIL_SLOPE * (0.001*GSCdata.average[N1[i]].M - BRIGHT_XTRAIL_MAG), 0.0);
+    for (; (dX > -MaxDist1) && (j < Nave); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      if ((fabs(dY) < BRIGHT_XTRAIL_WIDTH) && (fabs(dX) < MaxDist1)) {  /* star on spike */
+	mark[j] = TRUE;
+      }
+    }
+    j = first_j;
+    i++;
+  }
+
+  /* done with search, mark selected stars */
+  for (i = 0; i < Nave; i++) {
+    if (mark[i]) {
+      catalog[0].average[N2[i]].code = ID_BLEED;
+    }
+  } 
+  
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (mark);
+  free (GSCdata.average);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_funnymags.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_funnymags.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_funnymags.c	(revision 22322)
@@ -0,0 +1,27 @@
+# include "markstar.h"
+
+find_funnymags (catalog, images, Nimage)
+Catalog catalog[];
+Image images[];
+int Nimage;
+{
+
+  int i;
+  int *bad;
+
+  ALLOCATE (bad, int, Nimage);
+  bzero (bad, Nimage*sizeof(int));
+
+  for (i = 0; i < catalog[0].Nmeasure; i++) {
+    if (catalog[0].measure[i].M < 9000) {
+      bad[catalog[0].image[i]] ++;
+    }
+  }
+
+  for (i = 0; i < Nimage; i++) {
+    if (bad[i] > 0) {
+      fprintf (stdout, "%s %d %d\n", images[i].name, images[i].tzero, bad[i]);
+    }
+  }
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_ghosts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_ghosts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_ghosts.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include "markstar.h"
+
+find_ghosts (catalog, catstats, filename, image, Nimage)
+Catalog catalog[];
+CatStats catstats[];
+char *filename;
+Image image[];
+int Nimage;
+{
+
+  int i, j, first_j, Nave, Ngsc, Nregions;
+  Catalog GSCdata, Ghostdata;
+  double MinRA, MinDEC, MaxRA, MaxDEC, RaCenter, DecCenter;
+  Coords *tcoords;
+  float *X1, *Y1, *X2, *Y2;
+  int *N1, *N2, *match;
+  char *mark;
+  double dX, dY, dR, RADIUS2, BRIGHT_RADIUS, radius, X, Y, R, D;
+  Image timage;
+  GSCRegion *region, *gregions2();
+
+  for (i = 0; i < Nimage; i++) {
+    
+    /* coords structure for ghost image */
+    timage = image[i];
+    timage.coords.cdelt1 = -1*image[i].coords.cdelt1;
+    timage.coords.cdelt2 = -1*image[i].coords.cdelt2;
+    timage.coords.crpix1 = 2*OPTICAL_AXIS1 - image[i].coords.crpix1;
+    timage.coords.crpix2 = 2*OPTICAL_AXIS2 - image[i].coords.crpix2;
+
+    region = gregions2 (&timage, &Nregions);
+    
+    /* find ghost stars */
+    load_gsc_data_ghost (&GSCdata, region, Nregions, &timage);
+
+    /* refect ghost stars to find locations of ghosts */
+    ALLOCATE (Ghostdata.average, Average, MAX (GSCdata.Naverage, 1));
+    for (j = 0; j < GSCdata.Naverage; j++) {
+      RD_to_XY (&X, &Y, GSCdata.average[j].R, GSCdata.average[j].D, &timage);
+      fXY_to_RD (&Ghostdata.average[j].R, &Ghostdata.average[j].D, X, Y, &image[i]);
+      Ghostdata.average[j].M = GSCdata.average[j].M;
+    }    
+    Ghostdata.Naverage = GSCdata.Naverage;
+
+    /* match ghosts and image stars, mark ghosts */
+    find_matches (catalog, catstats, &Ghostdata, i);
+
+    free (Ghostdata.average);
+    free (GSCdata.average);
+  }
+
+}
+
+/* 
+
+   just to be clear on some of the terms:
+
+   the "ghost" is the fuzzy patch of light on an image caused by
+     a reflected star
+
+   the "ghost star" is the star in the sky which causes a ghost
+
+   the "ghost image" is the image location on the sky 
+     where ghost stars may come from.
+
+   the "image stars" are observed stars at the location of the
+     ghost - these are detections of the ghost
+
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_group.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_group.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_group.c	(revision 22322)
@@ -0,0 +1,69 @@
+# include "markstar.h"
+
+find_group (catstats, mark, Npts, i, ANGLE)
+     CatStats catstats[];
+     double *ANGLE;
+     int i, Npts;
+     char *mark;
+{
+ 
+  float *R, *D;
+  int j, N, bin, Ndegbin;
+  unsigned short int *A;
+  double Ra, De, dR, dD, angle, limit;
+
+   /* assign some parameter values */
+  
+  R = catstats[0].X;
+  D = catstats[0].Y;
+  limit = 10.0*sqrt((double)(M_PI*RADIUS*RADIUS*catstats[0].density)/(double)(NBINS));
+  Ndegbin = NBINS / 180.0;
+  ALLOCATE (A, unsigned short int, NBINS)
+  bzero (A, NBINS*sizeof(short));
+
+  /* look for points concentrated in an angle bin */
+  if (mark[i]) return (FALSE);
+  Ra = R[i];
+  De = D[i];
+  N = 0;
+  /* points east */
+  for (j = i + 1; (j < Npts) && (R[j] - Ra < RADIUS); j++) {
+    if (mark[j]) continue;
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      angle = atan2 (dD,dR);
+      if (!finite(angle)) 
+	continue;
+      if (angle < 0) angle += M_PI;
+      bin = 1 + angle*DEG_RAD*Ndegbin;
+      A[bin] ++;
+      if (A[bin] > limit) {
+	*ANGLE = (bin - 1.0) / (DEG_RAD*Ndegbin);
+	return (TRUE);
+      }
+      N ++;
+    }
+  }
+  /* points west */
+  for (j = i - 1; (j >= 0) && (Ra - R[j] < RADIUS); j--) {
+    if (mark[j]) continue;
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      angle = atan2 (dD,dR);
+      if (!finite(angle)) 
+	continue;
+      if (angle < 0) angle += M_PI;
+      bin = 1 + angle*DEG_RAD*Ndegbin;
+      A[bin] ++;	
+      if (A[bin] > limit) {
+	*ANGLE = (bin - 1.0) / (DEG_RAD*Ndegbin);
+	return (TRUE);
+      }
+      N ++;
+    }
+  }
+  return (FALSE);
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_images.c	(revision 22322)
@@ -0,0 +1,261 @@
+# include "markstar.h"
+# define OLDSTYLE 1
+# if (OLDSTYLE)
+double opening_angle ();
+# endif
+
+Image *find_images (catstats, Nimages)
+CatStats catstats[];
+int *Nimages;
+{
+  
+  Header header;
+  Image *timage, *image;
+  int i, j, k, found, nimage, Nimage, NIMAGE, NTIMAGE, Nloop, Nlast;
+  int n, Nim, status;
+  FILE *f;
+  double Xc[6], Yc[6], Xi[6], Yi[6], r, d, x, y, dx, dy;
+  Coords *tcoords;
+
+  /* we make positional comparisons in the projection of catalog */
+  tcoords = &catstats[0].coords;
+  /* define catalog corners */
+  Xc[0] = catstats[0].RA[0]; Yc[0] = catstats[0].DEC[0];
+  Xc[1] = catstats[0].RA[1]; Yc[1] = catstats[0].DEC[0];
+  Xc[2] = catstats[0].RA[1]; Yc[2] = catstats[0].DEC[1];
+  Xc[3] = catstats[0].RA[0]; Yc[3] = catstats[0].DEC[1];
+  Xc[4] = catstats[0].RA[0]; Yc[4] = catstats[0].DEC[0];
+  Xc[5] = catstats[0].RA[1]; Yc[5] = catstats[0].DEC[1];
+  for (j = 0; j < 6; j++) {
+    r = Xc[j]; d = Yc[j];
+    RD_to_XY (&Xc[j], &Yc[j], r, d, tcoords);
+  }
+  /* find Y positions of RA center */
+  r = 0.5*(catstats[0].RA[0] + catstats[0].RA[1]);
+  d = catstats[0].DEC[0];
+  RD_to_XY (&x, &y, r, d, tcoords);
+  Yc[0] = MIN (y, Yc[0]);
+  Yc[1] = MIN (y, Yc[1]);
+  Yc[4] = MIN (y, Yc[4]);
+  /* find Y positions of RA center */
+  r = 0.5*(catstats[0].RA[0] + catstats[0].RA[1]);
+  d = catstats[0].DEC[1];
+  RD_to_XY (&x, &y, r, d, tcoords);
+  Yc[2] = MAX (y, Yc[2]);
+  Yc[3] = MAX (y, Yc[3]);
+  Yc[5] = MAX (y, Yc[5]);
+
+  dx = 0.02*(Xc[2] - Xc[0]);
+  dy = 0.02*(Yc[2] - Yc[0]);
+  Xc[0] -= dx; Yc[0] -= dy;
+  Xc[1] += dx; Yc[1] -= dy;
+  Xc[2] += dx; Yc[2] += dy;
+  Xc[3] -= dx; Yc[3] += dy;
+  Xc[4] -= dx; Yc[4] -= dy;
+  Xc[5] -= dx; Yc[5] -= dy;
+
+  /* check if image datafile exists, get header, number of images */
+  if (!gfits_read_header (ImageCat, &header)) {
+    fprintf (stderr, "ERROR: No images in catalog %s (1)\n", ImageCat);
+    exit (0);
+  }
+  Nimage = 0;
+  gfits_scan (&header, "NIMAGES", "%d", 1, &Nimage);
+  if (Nimage == 0) {
+    fprintf (stderr, "ERROR: No images in catalog %s (1)\n", ImageCat);
+    exit (0);
+  }
+
+  /* get ready to read data on images */ 
+  f = fopen (ImageCat, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: trouble opening Image catalog: %s (2)\n", ImageCat);
+    exit (0);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  /* set up buffers for images, temporary storage */
+  NTIMAGE = 100;
+  ALLOCATE (timage, Image, NTIMAGE);
+  NIMAGE = 100;
+  ALLOCATE (image, Image, NIMAGE);
+  nimage = 0;
+  Nloop = Nimage / NTIMAGE + 1;
+  Nlast = Nimage % NTIMAGE;
+  
+  /* read in images in groups of NTIMAGE (100) */
+  for (n = 0; n < Nloop; n++) {
+    Nim = (n == Nloop - 1) ? Nlast : NTIMAGE;
+    status = Fread (timage, sizeof(Image), Nim, f, "image");
+    if (status != Nim) {
+      fprintf (stderr, "ERROR: couldn't read images from image catalog: %s\n", ImageCat);
+      exit (0);
+    }
+    /* test each image in block */
+    for (i = 0; i < Nim; i++) {
+      /* define image corners */
+      Xi[0] = 0;            Yi[0] = 0;
+      Xi[1] = timage[i].NX; Yi[1] = 0;
+      Xi[2] = timage[i].NX; Yi[2] = timage[i].NY;
+      Xi[3] = 0;            Yi[3] = timage[i].NY;
+      Xi[4] = 0;            Yi[4] = 0;
+      Xi[5] = timage[i].NX; Yi[5] = timage[i].NY;
+      found = FALSE;
+      /* transform to tcoords */
+      if (catstats[0].DEC[1] > 86.25) { /* pole */
+	for (j = 0; j < 6; j++) {
+	  XY_to_RD (&r, &d, Xi[j], Yi[j], &timage[i].coords);
+	  if (d > catstats[0].DEC[0] - 0.5) found = TRUE;
+	}
+      } else {
+	for (j = 0; j < 6; j++) {
+	  XY_to_RD (&r, &d, Xi[j], Yi[j], &timage[i].coords);
+	  RD_to_XY (&Xi[j], &Yi[j], r, d, tcoords);
+	}
+	/* check if edges cross */
+	for (j = 0; (j < 5) && !found; j++) {
+	  for (k = 0; (k < 5) && !found; k++) {
+	    found |= edge_check (&Xi[j], &Yi[j], &Xc[k], &Yc[k]);
+	  }
+	}
+      }
+      if (found) {
+	image[nimage] = timage[i]; 
+	image[nimage].code = 0;
+	nimage ++;
+	if (nimage == NIMAGE) {
+	  NIMAGE += 100;
+	  REALLOCATE (image, Image, NIMAGE);
+	}
+      }
+    }
+  }
+      
+  if (VERBOSE) { 
+    for (i = 0; i < nimage; i++) {
+      XY_to_RD (&r, &d, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+      fprintf (stderr, "associated images: %d %8.4f %8.4f %10d %6d  %5.3f %6.3f %6.3f\n", 
+	       i, r, d, image[i].tzero, image[i].nstar, 0.001*image[i].secz, 
+	       0.001*image[i].Mcal, 0.001*image[i].dMcal);
+    }
+  }
+
+  REALLOCATE (image, Image, MAX (nimage, 1));
+  free (timage);
+  *Nimages = nimage;
+  fclose (f);
+  return (image);
+}
+
+# if (OLDSTYLE)
+int edge_check (x1, y1, x2, y2)
+double *x1, *y1, *x2, *y2;
+{
+
+  double theta1, theta2;
+  double Theta1, Theta2;
+
+  theta1 = opening_angle (x1[0], y1[0], x2[0], y2[0], x1[1], y1[1]); 
+  theta2 = opening_angle (x1[0], y1[0], x2[0], y2[0], x2[1], y2[1]); 
+
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  Theta1 = theta1;
+  Theta2 = theta2;
+  theta1 = opening_angle (x2[0], y2[0], x1[1], y1[1], x2[1], y2[1]); 
+  theta2 = opening_angle (x2[0], y2[0], x1[1], y1[1], x1[0], y1[0]); 
+  
+ 
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+
+double opening_angle (x1, y1, x2, y2, x3, y3)
+double x1, y1, x2, y2, x3, y3;
+{
+
+  double dx1, dy1, dx2, dy2, ct, st, theta;
+
+  dx1 = x1 - x2;
+  dy1 = y1 - y2;
+  
+  dx2 = x3 - x2;
+  dy2 = y3 - y2;
+  
+  ct = (dx1*dx2 + dy1*dy2);
+  st = (dx1*dy2 - dx2*dy1);
+
+  theta = atan2 (st, ct);
+
+  return (theta);
+
+}
+#else 
+
+int edge_check (x1, y1, x2, y2)
+double *x1, *y1, *x2, *y2;
+{
+
+  double dot;
+  double dx1, dx2, dy1, dy2, dx3, dy3, x, y;
+  double cross, L1, L3, d1, d2;
+
+  dx1 = x1[1] - x1[0]; dy1 = y1[1] - y1[0];
+  dx2 = x2[1] - x2[0]; dy2 = y2[1] - y2[0];
+
+  cross = dx2*dy1 - dx1*dy2;
+  
+  if (cross == 0) {  /* lines are parallel, are they inline? */
+    dx3 = x2[1] - x1[0]; dy2 = y2[1] - y1[0];
+    L1 = hypot (dx1,dy1);
+    L3 = hypot (dx3,dy3);
+
+    dot = fabs (dx1*dx3 + dy1*dy3) / (L1*L3);
+    if (dot == 1.0) { /* lines are inline, do they overlap? */
+      d1 = (x1[1]-x2[1])*(x1[1]-x2[0]) + (y1[1]-y2[1])*(y1[1]-y2[0]);
+      d2 = (x1[0]-x2[1])*(x1[0]-x2[0]) + (y1[0]-y2[1])*(y1[0]-y2[0]);
+      if (d1*d2 < 0) { /* lines overlap */
+	return (TRUE);
+      } else {
+	return (FALSE);
+      }
+    } else {
+      return (FALSE);
+    }
+  }
+
+  x = (dx1*dx2*(y2[0] - y1[0]) + (x1[0]*dy1*dx2 - x2[0]*dy2*dx1)) / cross;
+
+  if (dx1 != 0) {
+    y = y1[0] + (x - x1[0])*dy1/dx1;
+  } else {
+    y = y2[0] + (x - x2[0])*dy2/dx2;
+  }
+
+  d1 = (x - x1[0])*(x - x1[1]) + (y - y1[0])*(y - y1[1]);
+  d2 = (x - x2[0])*(x - x2[1]) + (y - y2[0])*(y - y2[1]);
+  if ((d1 > 0) || (d2 > 0)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+      
+
+#endif
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_line.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_line.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_line.c	(revision 22322)
@@ -0,0 +1,161 @@
+# include "markstar.h"
+
+find_line (catstats, mark, Npts, i, M, B, Angle)
+     CatStats catstats[];
+     double *M, *B, Angle;
+     int i, Npts;
+     char *mark;
+{
+  
+  float *R, *D;
+  int j, N;
+  double X, Y, X2, Y2, XY, m, b, det;
+  double dR, dD, Ra, De, angle;
+  char Flipped;
+  
+  R = catstats[0].X;
+  D = catstats[0].Y;
+
+  /* fit a line to points near line */
+  Ra = R[i];
+  De = D[i];
+  X = Ra;
+  Y = De;
+  X2 = Ra*Ra;
+  Y2 = De*De;
+  XY = Ra*De;
+  N = 1;
+  /* points to the east */
+  for (j = i + 1; (j < Npts) && (R[j] - Ra < RADIUS); j++) {
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      angle = atan2 (dD,dR);
+      if (!finite(angle)) continue;
+      if (angle < 0) angle += M_PI;
+      if (fabs(angle - Angle) < RAD_DEG) {
+	X += R[j];
+	Y += D[j];
+	X2 += R[j]*R[j];
+	Y2 += D[j]*D[j];
+	N ++;
+	XY += R[j]*D[j];
+      }
+    }
+  }
+  /* points to the west */
+  for (j = i - 1; !mark[i] && (j >= 0) && (Ra - R[j] < RADIUS); j--) {
+    if (mark[j]) continue;
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      angle = atan2 (dD,dR);
+      if (!finite(angle)) continue;
+      if (angle < 0) angle += M_PI;
+      if (fabs(angle - Angle) < RAD_DEG) {
+	X += R[j];
+	Y += D[j];
+	X2 += R[j]*R[j];
+	Y2 += D[j]*D[j];
+	N ++;
+	XY += R[j]*D[j];
+      }
+    }
+  }
+  /* determine coeffs */
+  Flipped = 0;
+  det = 1.0 / (X2*N - X*X);
+  m = det * (XY*N - X*Y);
+  b = det * (X2*Y - XY*X);
+  if (fabs(m) > 1.1) { /* use a line of R = m*D + b instead */
+    /* fprintf (stderr, "high slope object: %f %f  -> ", m, b); */
+    det = 1.0 / (Y2*N - Y*Y);
+    m = det * (XY*N - X*Y);
+    b = det * (Y2*X - XY*Y);
+    Flipped = 1;
+    /* fprintf (stderr, "%f %f\n", m, b); */
+  }
+
+  *M = m;
+  *B = b;
+  return (Flipped);
+
+}
+
+
+find_better_line (catstats, mark, Npts, i, M, B, axis)
+     CatStats catstats[];
+     double *M, *B;
+     int i, Npts, axis;
+     char *mark;
+{
+  
+  float *R, *D;
+  int j, N;
+  double X, Y, X2, Y2, XY, m, b, det;
+  double dR, dD, Ra, De, delta;
+  
+  R = catstats[0].X;
+  D = catstats[0].Y;
+
+  /* fit a line to points near line */
+  Ra = R[i];
+  De = D[i];
+  X = Y = X2 = Y2 = XY = N = 0;
+  m = *M;  b = *B;
+
+  /* points to the east */
+  for (j = i; (j < Npts) && (R[j] - Ra < RADIUS); j++) {
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      if (axis == 1) 
+	delta = R[j] - m*D[j] - b;
+      else
+	delta = D[j] - m*R[j] - b;
+      if (fabs(delta) < 2*TRAIL_WIDTH) {
+	X += R[j];
+	Y += D[j];
+	X2 += R[j]*R[j];
+	Y2 += D[j]*D[j];
+	N ++;
+	XY += R[j]*D[j];
+      }
+    }
+  }
+  /* points to the west */
+  for (j = i - 1; (j >= 0) && (Ra - R[j] < RADIUS); j--) {
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      if (axis == 1) 
+	delta = R[j] - m*D[j] - b;
+      else
+	delta = D[j] - m*R[j] - b;
+      if (fabs(delta) < 2*TRAIL_WIDTH) {
+	X += R[j];
+	Y += D[j];
+	X2 += R[j]*R[j];
+	Y2 += D[j]*D[j];
+	N ++;
+	XY += R[j]*D[j];
+      }
+    }
+  }
+  /* determine coeffs */
+  if (axis == 0) {
+    det = 1.0 / (X2*N - X*X);
+    m = det * (XY*N - X*Y);
+    b = det * (X2*Y - XY*X);
+  } else {
+    det = 1.0 / (Y2*N - Y*Y);
+    m = det * (XY*N - X*Y);
+    b = det * (Y2*X - XY*Y);
+  }
+
+  /* fprintf (stderr, "%f %f %d\n", m, b, N); */
+
+  *M = m;
+  *B = b;
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_matches.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_matches.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_matches.c	(revision 22322)
@@ -0,0 +1,87 @@
+# include "markstar.h"
+
+find_matches (catalog, catstats, Ghostdata, Nimage)
+CatStats catstats[];
+Catalog catalog[], Ghostdata[];
+int Nimage;
+{
+
+  int i, j, k, n, m, N, M, first_j;
+  double X, Y, RADIUS, RADIUS2;
+  double *X1, *Y1, *X2, *Y2;
+  double dX, dY, dR;
+  int *N1, *N2;
+  int Nstar, Nghost, Ng;
+  unsigned int flags;
+  Coords *tcoords;
+
+  Nstar = catalog[0].Naverage;
+  Nghost = Ghostdata[0].Naverage;
+
+  if (Nghost < 1) return (0);
+
+  /* it is better to have the catalog with fewer stars
+     assigned to the X1 set */
+  X2 = catstats[0].X;
+  Y2 = catstats[0].Y;
+  N2 = catstats[0].N;
+
+  ALLOCATE (X1, double, Nghost);
+  ALLOCATE (Y1, double, Nghost);
+  ALLOCATE (N1, int, Nghost);
+
+  /* project ghosts to the frame of the catalog */
+  tcoords = &catstats[0].coords;
+  for (i = 0; i < Nghost; i++) {
+    RD_to_XY (&X1[i], &Y1[i], Ghostdata[0].average[i].R, Ghostdata[0].average[i].D, tcoords);
+    N1[i] = i;
+  }
+  if (Nghost > 1) sort_coords_index (X1, Y1, N1, Nghost);
+
+  /* choose a radius for matches */
+  RADIUS2 = GHOST_RADIUS*GHOST_RADIUS;
+
+  /** find matched stars **/
+  for (i = j = 0; (i < Nghost) && (j < Nstar); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -2*GHOST_RADIUS) {
+      i++;
+      continue;
+    }
+    if (dX >= 2*GHOST_RADIUS) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    for (; (dX > -2*GHOST_RADIUS) && (j < Nstar); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      dR = dX*dX + dY*dY;
+      if (dR < RADIUS2) {  
+	/* this object may be a ghost star, 
+	   but only mark those measurements on the correct image */
+	M = N2[j];
+	m = catalog[0].average[M].offset;
+	Ng = 0;
+	for (k = 0; k < catalog[0].average[M].Nm; k++) {
+	  if (catalog[0].image[m+k] == Nimage) {
+	    catalog[0].measure[m+k].average |= GHOST_DATA;
+	    Ng ++;
+	  }
+	}
+	/* all measurements are ghosts */
+	if (catalog[0].average[M].Nm == Ng) {
+	  catalog[0].average[M].code = ID_GHOST;
+	}
+      }
+    }
+    j = first_j;
+    i++;
+  }
+
+  free (X1);
+  free (Y1);
+  free (N1);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_trails.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_trails.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/find_trails.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "markstar.h"
+
+find_trails (catalog, catstats)
+Catalog catalog[];
+CatStats catstats[];
+{
+
+  int i, j, N, Nave, axis, marked;
+  double density, spacing, Area, Angle, m, b, RaCenter, DecCenter;
+  double MinRA, MaxRA, MinDEC, MaxDEC;
+  float *X1, *Y1;
+  char *mark;
+  int *N1;
+  Coords tcoords;
+  
+  Nave = catalog[0].Naverage;
+  N1 = catstats[0].N;
+
+  ALLOCATE (mark, char, Nave);
+  bzero (mark, Nave);
+
+  for (i = 0; i < Nave; i++) {
+    /* already marked, ignore */
+    if ((mark[i]) || 
+	(catalog[0].average[N1[i]].code == ID_BLEED) || 
+	(catalog[0].average[N1[i]].code == ID_GHOST))
+      continue;
+    /* a good star, ignore */
+    if ((catalog[0].average[N1[i]].Nm > 3) && 
+	(catalog[0].average[N1[i]].Nm > 4*catalog[0].average[N1[i]].Nn)) {
+      continue;
+    }
+    if (find_group (catstats, mark, Nave, i, &Angle)) {
+      /* this point has an excess nearby concentration, find the line */
+      axis = find_line (catstats, mark, Nave, i, &m, &b, Angle);
+      find_better_line (catstats, mark, Nave, i, &m, &b, axis);
+      mark_trail (catstats, mark, Nave, i, m, b, axis, catalog);
+    }
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/fixcat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/fixcat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/fixcat.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "markstar.h"
+
+main (argc, argv)
+int argc;
+char **argv;
+{
+
+  FILE *f;
+  int i, Nimages;
+  Image *image, *find_images();
+  Catalog catalog;
+  CatStats catstats;
+  struct timeval now, then;  
+  
+  fprintf (stderr, "this function is not well-defined: review and recode\n");
+  exit (1);
+
+  gettimeofday (&then, (void *) NULL);
+  ConfigInit (argc, argv);
+
+  VERBOSE = FALSE;
+  if ((i = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (i, &argc, argv);
+  }
+  if (argc < 2) {
+    fprintf (stderr, "ERROR: Usage: fixcat (catalog)\n");
+    exit (0);
+  }
+
+  /* replace with addstar/gcatalog as example */
+  gcatalog (argv[1], &catalog);
+
+  gcatstats (&catalog, &catstats);
+
+  image = find_images (&catstats, &Nimages);
+
+  match_images (&catalog, image, Nimages);
+    
+  find_funnymags (&catalog, image, Nimages);  
+
+  if (VERBOSE) {
+    gettimeofday (&now, (void *) NULL);
+    fprintf (stderr, "%s: elapsed time = %.2f sec\n", argv[1], 
+	     (now.tv_sec - then.tv_sec) + 1e-6*(now.tv_usec - then.tv_usec));
+  }
+  clear_lockfile (); 
+  fprintf (stderr, "SUCCESS\n");
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/gcatalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/gcatalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/gcatalog.c	(revision 22322)
@@ -0,0 +1,108 @@
+# include "markstar.h"
+
+gcatalog (catname, catalog)
+char *catname;
+Catalog catalog[];
+{
+  
+  char filename[128], line[64];
+  int Nitems, nitems;
+  int i, Nmeas, Nmiss, done;
+  FILE *f;
+  struct tm *local;
+  struct timeval now;
+  unsigned int NotTrail;
+  unsigned short NotBad;
+
+  sprintf (filename, "%s/%s\0", CATDIR, catname);
+
+  /* read catalog header */
+  if (!gfits_read_header (filename, &catalog[0].header)) {
+    fprintf (stderr, "ERROR: file doesn't exist %s\n", filename);
+    exit (0);
+  }
+
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't open catalog file: %s\n", filename);
+    exit (0);
+  }
+  fseek (f, catalog[0].header.size, SEEK_SET); 
+
+  /** find number of stars, measurements **/
+  catalog[0].Nmissing = catalog[0].Naverage = catalog[0].Nmeasure = 0;
+  gfits_scan (&catalog[0].header, "NSTARS", "%d", 1, &catalog[0].Naverage);
+  gfits_scan (&catalog[0].header, "NMEAS", "%d", 1, &catalog[0].Nmeasure);
+  gfits_scan (&catalog[0].header, "NMISS", "%d", 1, &catalog[0].Nmissing);
+
+  ALLOCATE (catalog[0].average, Average, MAX (catalog[0].Naverage, 1));
+  ALLOCATE (catalog[0].measure, Measure, MAX (catalog[0].Nmeasure, 1));
+  ALLOCATE (catalog[0].missing, Missing, MAX (catalog[0].Nmissing, 1));
+  if (catalog[0].Naverage == 0) {
+    /* this is a valid ending state: no stars */
+    if (VERBOSE) fprintf (stderr, "SUCCESS: no stars yet in catalog %s\n", filename);
+    fclose (f);
+    clear_lockfile ();
+    exit (0);
+  }
+
+  /* read average values */
+  Nitems = catalog[0].Naverage;
+  nitems = Fread (catalog[0].average, sizeof(Average), Nitems, f, "average");
+  if (nitems != Nitems) {
+    fprintf (stderr, "ERROR: failed to read data from catalog file %s (1)\n", filename);
+    fclose (f);
+    exit (0);
+  }
+  
+  /* read measurements */
+  Nitems = catalog[0].Nmeasure;
+  nitems = Fread (catalog[0].measure, sizeof(Measure), Nitems, f, "measure");
+  if (nitems != Nitems) {
+    fprintf (stderr, "ERROR: failed to read data from catalog file %s (2)\n", filename);
+    fclose (f);
+    exit (0);
+  }
+  
+  /* read missing */
+  Nitems = catalog[0].Nmissing;
+  nitems = Fread (catalog[0].missing, sizeof(Missing), Nitems, f, "missing");
+  if (nitems != Nitems) {
+    fprintf (stderr, "ERROR: failed to read data from catalog file %s (3)\n", filename);
+    fclose (f);
+    exit (0);
+  }
+  
+  if (VERBOSE) fprintf (stderr, "read %d stars from catalog file %s (%d measurements, %d missing)\n", 
+	   catalog[0].Naverage, filename, catalog[0].Nmeasure, catalog[0].Nmissing);
+
+  for (i = Nmeas = Nmiss = 0; i < catalog[0].Naverage; i++) {
+    Nmeas += catalog[0].average[i].Nm; 
+    Nmiss += catalog[0].average[i].Nn; 
+  }
+  if ((Nmeas != catalog[0].Nmeasure) || (Nmiss != catalog[0].Nmissing)) {
+    fprintf (stderr, "ERROR: data in catalog %s is corrupt, sums don't check\n");
+    fprintf (stderr, "ERROR: Nmeas: %d, %d\n", Nmeas, catalog[0].Nmeasure);
+    fprintf (stderr, "ERROR: Nmiss: %d, %d\n", Nmiss, catalog[0].Nmissing);
+    exit (0);
+  }
+
+  if (RESET) {
+    NotBad = (0xffff ^ 0xc003);
+    for (i = 0; i < catalog[0].Naverage; i++) {
+      if ((catalog[0].average[i].code & ID_MOVING) &&
+	  (catalog[0].average[i].code & ID_BAD_OBJECT) &&
+	  (catalog[0].average[i].code & 0x0003)) {
+	/* this will set the correct bit for each to 0 */ 
+	catalog[0].average[i].code &= NotBad;
+      }
+    }
+    for (i = 0; i < catalog[0].Nmeasure; i++) {
+      catalog[0].measure[i].average &= ~PART_OF_TRAIL;
+    }
+  }
+
+
+  fclose (f);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/gcatstats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/gcatstats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/gcatstats.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "markstar.h"
+
+gcatstats (catalog, catstats)
+Catalog catalog[];
+CatStats catstats[];
+{
+
+  int i;
+  double RaCenter, DecCenter;
+  double MinRA, MaxRA, MinDEC, MaxDEC;
+  double *X1, *Y1;
+  int *N1;
+  Coords tcoords;
+  
+  gfits_scan (&catalog[0].header, "RA0", "%lf", 1, &MinRA);
+  gfits_scan (&catalog[0].header, "RA1", "%lf", 1, &MaxRA);
+  gfits_scan (&catalog[0].header, "DEC0", "%lf", 1, &MinDEC);
+  gfits_scan (&catalog[0].header, "DEC1", "%lf", 1, &MaxDEC);
+
+  /* double check on region RA and DEC ranges */
+  DecCenter = 0.5*(MinDEC + MaxDEC);
+  RaCenter = 0.5*(MinRA + MaxRA);
+  if (MaxDEC > 86.25) {  /* we are on the pole */
+    DecCenter = 90.0;
+    RaCenter = 0.0;
+  }
+
+  catstats[0].RA[0] = MinRA;
+  catstats[0].RA[1] = MaxRA;
+  catstats[0].DEC[0] = MinDEC;
+  catstats[0].DEC[1] = MaxDEC;
+  catstats[0].Area = (MaxDEC - MinDEC)*(MaxRA - MinRA) / cos (RAD_DEG*DecCenter);
+  if (MaxDEC > 86.25) {  /* we are on the pole */
+    catstats[0].Area = 44.2;
+  }
+  catstats[0].density = catalog[0].Naverage / (3600*3600*catstats[0].Area);
+  catstats[0].spacing = 1.0 / (NSIGMA * catstats[0].density * 2*TRAIL_WIDTH);
+  fprintf (stderr, "Area, density, spacing: %f %f %f\n", catstats[0].Area, 
+	   catstats[0].density, catstats[0].spacing);
+  /* number of stars per square arcsec */
+
+  /** allocate local arrays **/
+  ALLOCATE (catstats[0].X, double, catalog[0].Naverage);
+  ALLOCATE (catstats[0].Y, double, catalog[0].Naverage);
+  ALLOCATE (catstats[0].N, int,   catalog[0].Naverage);
+
+  /* project onto rectilinear grid with 1 arcsec pixels, sort by X */
+  /* reference for coords is center of field  */
+  catstats[0].coords.crval1 = RaCenter;
+  catstats[0].coords.crval2 = DecCenter;
+  catstats[0].coords.crpix1 = catstats[0].coords.crpix2 = 0.0;
+  catstats[0].coords.cdelt1 = catstats[0].coords.cdelt2 = 1.0 / 3600.0;
+  catstats[0].coords.pc1_1 = catstats[0].coords.pc2_2 = 1.0;
+  catstats[0].coords.pc1_2 = catstats[0].coords.pc2_1 = 0.0;
+  strcpy (catstats[0].coords.ctype, "RA---TAN");
+
+  X1 = catstats[0].X;
+  Y1 = catstats[0].Y;
+  for (i = 0; i < catalog[0].Naverage; i++, X1++, Y1++) {
+    RD_to_XY (X1, Y1, catalog[0].average[i].R, catalog[0].average[i].D, &catstats[0].coords);
+    catstats[0].N[i] = i;
+  }
+  if (catalog[0].Naverage > 1) sort_coords_index (catstats[0].X, catstats[0].Y, catstats[0].N, catalog[0].Naverage);
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/gregions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/gregions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/gregions.c	(revision 22322)
@@ -0,0 +1,134 @@
+# include "markstar.h"
+
+GSCRegion *gregions1 (catstats, Nregions)
+CatStats catstats[];
+int *Nregions;
+{
+  
+  GSCRegion *region;
+  int i, j, k, x, y, done, nregion, nregion2, NREGION;
+  double ra, dec, dx, dy, Xo[4], Yo[4];
+  FILE *f;
+
+  f = fopen (GSCFILE, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: could not open GSC region file %s\n", GSCFILE);
+    exit (0);
+  }
+  nregion = 0;
+  NREGION = 10;
+  ALLOCATE (region, GSCRegion, NREGION);
+
+  if (catstats[0].DEC[0] == 86.25) { /* pole region */
+    dec = 86.0;
+    for (ra = 0.1; ra < 370; ra+= 30.0) {
+      aregion (&region[nregion], f, ra, dec);
+      done = FALSE;
+      for (j = 0; (j < nregion - 1) && !done; j++) {
+	if (!strcmp (region[nregion].filename, region[j].filename)) {
+	  nregion --;
+	  done = TRUE;
+	}
+      }
+      nregion ++;
+      if (nregion == NREGION) {
+	NREGION += 10;
+	REALLOCATE (region, GSCRegion, NREGION);
+      }
+    }
+  } else {
+    for (x = 0; x < 2; x++) {
+      for (y = 0; y < 2; y++) {
+	for (dx = -0.1; dx <= 0.1; dx += 0.2) {
+	  for (dy = -0.1; dy <= 0.1; dy += 0.2) {
+	    ra  = catstats[0].RA[x] + dx;
+	    dec = catstats[0].DEC[y] + dy;
+	    aregion (&region[nregion], f, ra, dec);
+	    done = FALSE;
+	    for (j = 0; (j < nregion) && !done; j++) {
+	      if (!strcmp (region[nregion].filename, region[j].filename)) {
+		done = TRUE;
+	      }
+	    }
+	    if (!done) {
+	      nregion ++;
+	    } 
+	    if (nregion == NREGION) {
+	      NREGION += 10;
+	      REALLOCATE (region, GSCRegion, NREGION);
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  if (VERBOSE) {
+    fprintf (stderr, "using %d regions\n", nregion);
+    for (i = 0; i < nregion; i++) {
+      fprintf (stderr, "region %d: %f %f  %f %f\n", i, region[i].RA[0], region[i].RA[1], region[i].DEC[0], region[i].DEC[1]);
+    } 
+  }
+
+  REALLOCATE (region, GSCRegion, MAX (nregion, 1));
+  *Nregions = nregion;
+  
+  fclose (f);
+  return (region);
+  
+}
+
+GSCRegion *gregions2 (image, Nregions)
+Image *image;
+int *Nregions;
+{
+  
+  GSCRegion *region;
+  FILE *f;
+  double x, y;
+  double dr, dd, dec, ra;
+  int i, j, done, nregion, NREGION;
+  
+  f = fopen (GSCFILE, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't find GSC region file %s\n", GSCFILE);
+    exit (0);
+  }
+  
+  /* find regions at image corners */
+  NREGION = 10;
+  ALLOCATE (region, GSCRegion, NREGION);
+  nregion = 0;
+
+  /* look for new regions on grid across image */ 
+  for (x = 0.0; x <= 1.0; x+=0.25) {
+    for (y = 0.0; y <= 1.0; y+=0.25) {
+      XY_to_RD (&ra, &dec, image[0].NX*(1.1*x - 0.05), image[0].NY*(1.1*y - 0.05), &image[0].coords);
+      aregion (&region[nregion], f, ra, dec);
+      done = FALSE;
+      for (j = 0; (j < nregion) && !done; j++) {
+	if (!strcmp (region[nregion].filename, region[j].filename)) {
+	  nregion --;
+	  done = TRUE;
+	}
+      }
+      nregion ++;
+      if (nregion == NREGION) {
+	NREGION += 10;
+	REALLOCATE (region, GSCRegion, NREGION);
+      }
+    }
+  }
+
+  if (VERBOSE) {
+    fprintf (stderr, "found %d region files:\n", nregion);
+    for (i = 0; i < nregion; i++) {
+      fprintf (stderr, "  %d %s\n", i, region[i].filename);
+    }
+  }
+  *Nregions = nregion;
+  
+  fclose (f);
+  return (region);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/load_gsc_data.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/load_gsc_data.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/load_gsc_data.c	(revision 22322)
@@ -0,0 +1,61 @@
+# include "markstar.h"
+# define NBYTES 160000
+# define BYTES_STAR 23
+# define BLOCK 1000
+# define DNSTARS 1000
+
+load_gsc_data (catalog, catstats)
+Catalog catalog[];
+CatStats catstats[];
+{
+
+  /* load data from the GSC files */
+  char filename[128];
+  char *tbuffer;
+  int nstar, NSTARS;
+  int i, j, Nbytes, nbytes, Nregions;
+  double R, D, M, MagLimit;
+  FILE *f;
+  GSCRegion *region, *gregions1(), *gregions2();
+  
+  region = gregions1 (catstats, &Nregions);
+
+  MagLimit = MAX (MAX (BRIGHT_HALO_MAG, BRIGHT_XTRAIL_MAG), BRIGHT_YTRAIL_MAG); 
+  nstar = 0;
+  NSTARS = DNSTARS;
+  ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+  ALLOCATE (catalog[0].average, Average, NSTARS);
+  Nbytes = BLOCK*BYTES_STAR;
+  
+  for (j = 0; j < Nregions; j++) {
+    f = fopen (region[j].filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "GSC file for region %s missing\n", region[j].filename);
+      exit (0);
+    }
+    
+    while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+      for (i = 0; i < nbytes / BYTES_STAR; i++) {
+	dparse (&M, 3, &tbuffer[i*BYTES_STAR]);
+	if (M > MagLimit) continue;
+	dparse (&R, 1, &tbuffer[i*BYTES_STAR]);
+	dparse (&D, 2, &tbuffer[i*BYTES_STAR]);
+	catalog[0].average[nstar].R = R;
+	catalog[0].average[nstar].D = D;
+	catalog[0].average[nstar].M = M * 1000.0;
+	nstar++;
+	if (nstar == NSTARS - 1) {
+	  NSTARS += DNSTARS;
+	  REALLOCATE (catalog[0].average, Average, NSTARS);
+	}
+      }
+    }
+    fclose (f);
+  }
+
+  free (tbuffer);
+  REALLOCATE (catalog[0].average, Average, nstar);
+  catalog[0].Naverage = nstar;
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/load_gsc_data_ghost.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/load_gsc_data_ghost.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/load_gsc_data_ghost.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "markstar.h"
+# define NBYTES 160000
+# define BYTES_STAR 23
+# define BLOCK 1000
+# define DNSTARS 1000
+
+/* this routine is basically identical to load_gsc_data, but
+   it is not limited to a single region file, and it merges the 
+   results */
+
+load_gsc_data_ghost (catalog, region, Nregion, image)
+Catalog catalog[];
+GSCRegion *region;
+int Nregion;
+Image image[];
+{
+
+  /* load data from the GSC files */
+  char filename[128];
+  char *tbuffer;
+  int nstar, NSTARS;
+  int i, j, Nbytes, nbytes;
+  double R, D, M, X, Y;
+  FILE *f;
+  Coords *tcoords;
+  int MinX, MinY, MaxX, MaxY;
+  
+  nstar = 0;
+  NSTARS = DNSTARS;
+  ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+  ALLOCATE (catalog[0].average, Average, NSTARS);
+  tcoords = &image[0].coords;
+  MinX = 0;
+  MinY = 0;
+  MaxX = image[0].NX;
+  MaxY = image[0].NY;
+  
+  for (j = 0; j < Nregion; j++) {
+    
+    f = fopen (region[j].filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "no GSC file for region %s (2)\n", region[j].filename);
+      exit (0);
+    }
+    
+    Nbytes = BLOCK*BYTES_STAR;
+    while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+      for (i = 0; i < nbytes / BYTES_STAR; i++) {
+	dparse (&M, 3, &tbuffer[i*BYTES_STAR]);
+	if (M > GHOST_MAG) continue;
+	dparse (&R, 1, &tbuffer[i*BYTES_STAR]);
+	dparse (&D, 2, &tbuffer[i*BYTES_STAR]);
+	RD_to_XY (&X, &Y, R, D, tcoords);
+	if ((X < MinX) || (X > MaxX) || (Y < MinY) || (Y > MaxY)) continue;
+	catalog[0].average[nstar].R = R;
+	catalog[0].average[nstar].D = D;
+	catalog[0].average[nstar].M = M * 1000.0;
+	nstar++;
+	if (nstar == NSTARS - 1) {
+	  NSTARS += DNSTARS;
+	  REALLOCATE (catalog[0].average, Average, NSTARS);
+	}
+      }
+    }
+    fclose (f);
+
+  }
+
+  free (tbuffer);
+  REALLOCATE (catalog[0].average, Average, MAX (nstar, 1));
+  catalog[0].Naverage = nstar;
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/mark_trail.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/mark_trail.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/mark_trail.c	(revision 22322)
@@ -0,0 +1,272 @@
+# include "markstar.h"
+int *make_common_list ();
+int *check_common_list ();
+
+mark_trail (catstats, mark, Nave, i, m, b, axis, catalog)
+     CatStats catstats[];
+     char *mark;
+     int i, Nave, axis;
+     double m, b;
+     Catalog catalog[];
+{
+
+  double *R, *D;
+  int j, k, jj, kk, N, NPTS, marked, start, end, Nm;
+  int *good, *seq, *N1;
+  double *dist, *dist2;
+  double d2, di, Di, dD, n, Dist, scale, norm;
+  double spacing;
+  int *list, Nlist;
+  int M, Nmeas, thisimage;
+
+  R = catstats[0].X;
+  D = catstats[0].Y;
+  N1 = catstats[0].N;
+  spacing = catstats[0].spacing;
+
+  NPTS = 200;
+  ALLOCATE (good, int, NPTS);
+  ALLOCATE (dist, double, NPTS);
+  ALLOCATE (dist2, double, NPTS);
+  ALLOCATE (seq, int, NPTS);
+
+  /* Find all points which lie near line */
+  /* save the entry number and distance along line */
+  N = 0;
+  scale = sqrt (1.0 + m*m);
+
+  if (axis == 1) {
+    for (j = 0; j < Nave; j++) {
+      norm = scale * fabs(R[j] - m*D[j] - b);
+      if (!mark[j] && (norm < TRAIL_WIDTH)) {
+	good[N] = j;
+	dist[N] = scale * (D[j] - D[i] + m*(R[j] - R[i]));
+	seq[N] = N;
+	N++;
+	if (N == NPTS - 1) {
+	  NPTS += 200;
+	  REALLOCATE (good, int, NPTS);
+	  REALLOCATE (dist, double, NPTS);
+	  REALLOCATE (dist2, double, NPTS);
+	  REALLOCATE (seq, int, NPTS);
+	}
+      }
+    }
+  } else {
+    for (j = 0; j < Nave; j++) {
+      norm = scale * fabs(D[j] - m*R[j] - b);
+      if (!mark[j] && (norm < TRAIL_WIDTH)) {
+	good[N] = j;
+	dist[N] = scale * (R[j] - R[i] + m*(D[j] - D[i]));
+	seq[N] = N;
+	N++;
+	if (N == NPTS - 1) {
+	  NPTS += 200;
+	  REALLOCATE (good, int, NPTS);
+	  REALLOCATE (dist, double, NPTS);
+	  REALLOCATE (dist2, double, NPTS);
+	  REALLOCATE (seq, int, NPTS);
+	}
+      }
+    }
+  }
+  
+  if (N < NPTSINLINE) 
+    return (0);
+
+  sort_seq (dist, seq, N);
+  
+  start = -1; end = -1;
+  for (j = 0; j < N-1; j++) {
+    /* if we have part of a line, and next point is in the line, check for common images */
+    if ((start != -1) && (fabs(dist[j] - dist[j+1]) < spacing)) {
+      list = check_common_list (list, N1[good[seq[j+1]]], &Nlist, catalog);
+      if (Nlist == 0) { /* if no common images, dump list and continue */
+	end = j + 1;
+	if (end - start > NPTSINLINE) {
+	  if (axis == 0)
+	    fprintf (stderr, "marking line %f %f  %d pts  %d  %d %d, %f %f\n", m, b, N, end-start, start, end, dist[start], dist[end-1]);
+	  else
+	    fprintf (stderr, "marking line %f %f  %d pts  %d  %d %d, %f %f\n", 1.0/m, -1.0*b/m, N, end-start, start, end, dist[start], dist[end-1]);
+	  for (k = start; k < end; k++) {
+	    M = N1[good[seq[k]]];
+	    mark[good[seq[k]]] = TRUE;
+	    /* we need to mark measurements from all images in common on the line */
+	    Nm = 0;
+	    for (jj = 0; jj < catalog[0].average[M].Nm; jj++) {
+	      Nmeas = catalog[0].average[M].offset + jj;
+	      thisimage = catalog[0].image[Nmeas];
+	      for (kk = 0; kk < Nlist; kk++) {
+		if (thisimage == list[kk]) {
+		  catalog[0].measure[Nmeas].average |= PART_OF_TRAIL;
+		  Nm ++;
+		}
+	      }
+	    }
+	    /* if there is only 1 measurement, mark object as bad */
+	    if (catalog[0].average[M].Nm == Nm) {
+	      catalog[0].average[M].code = ID_TRAIL;
+	    }
+	    catalog[0].average[M].code = ID_TRAIL;
+	  }
+	}
+	start = -1;
+	end = -1;
+      }
+    }
+    /* if we haven't yet found a line segment, check for the beginning */
+    if ((start < 0) && (fabs(dist[j] - dist[j+1]) < spacing)) {
+      start = j;
+      list = make_common_list (N1[good[seq[j]]], N1[good[seq[j+1]]], &Nlist, catalog);
+      if (Nlist == 0) { /* if no common images, move on */
+	start = -1;
+	free (list);
+      }
+    }
+    /* if we have a complete line, check for validity.  if it has enough members,
+	 mark them and continue searching for lines */
+    if ((start != -1) && ((fabs(dist[j] - dist[j+1]) >= spacing) || (j == N-2))) {
+      end = j + 1;
+      if (end - start > NPTSINLINE) {
+	if (axis == 0)
+	  fprintf (stderr, "marking line %f %f  %d pts  %d  %d %d, %f %f\n", m, b, N, end-start, start, end, dist[start], dist[end-1]);
+	else
+	  fprintf (stderr, "marking line %f %f  %d pts  %d  %d %d, %f %f\n", 1.0/m, -1.0*b/m, N, end-start, start, end, dist[start], dist[end-1]);
+	for (k = start; k < end; k++) {
+	  M = N1[good[seq[k]]];
+	  mark[good[seq[k]]] = TRUE;
+	  if (catalog[0].average[M].code == ID_BLEED) continue;
+	  /* we need to mark measurements from all images in common on the line */
+	  Nm = 0;
+	  for (jj = 0; jj < catalog[0].average[M].Nm; jj++) {
+	    Nmeas = catalog[0].average[M].offset + jj;
+	    thisimage = catalog[0].image[Nmeas];
+	    for (kk = 0; kk < Nlist; kk++) {
+	      if (thisimage == list[kk]) {
+		catalog[0].measure[Nmeas].average |= PART_OF_TRAIL;
+		Nm ++;
+	      }
+	    }
+	  }
+	  /* if there is only 1 measurement, mark object as bad */
+	  if (catalog[0].average[M].Nm == Nm) {
+	    catalog[0].average[M].code = ID_TRAIL;
+	  }
+	  catalog[0].average[M].code = ID_TRAIL;
+	}
+      }
+      free (list);
+      start = -1;
+      end = -1;
+    }
+  }
+}
+
+
+/* I is Average seq number for star 1, J for star 2 */
+/* make a list of images in common between two measurements */
+int *make_common_list (I, J, Nlist, catalog)
+int I, J, *Nlist;
+Catalog catalog[];
+{
+
+  int i, j, N1, N2, nlist, NLIST;
+  int *list;
+  int k, already;
+
+  NLIST = 50;
+  ALLOCATE (list, int, NLIST);
+  nlist = 0;
+
+  for (i = 0; i < catalog[0].average[I].Nm; i++) {
+    N1 = catalog[0].average[I].offset + i;
+    if (catalog[0].image[N1] == -1)
+      continue; /* not a real measurement */
+    for (j = 0; j < catalog[0].average[J].Nm; j++) {
+      N2 = catalog[0].average[J].offset + j;
+      if (catalog[0].image[N2] == -1)
+	continue; /* not a real measurement */
+      if (catalog[0].image[N1] == catalog[0].image[N2]) {
+	already = FALSE; 
+	for (k = 0; !already && (k < nlist); k++) {
+	  if (catalog[0].image[N1] == list[k]) { 
+	    already = TRUE;
+	  }
+	}
+	if (!already) {
+	  list[nlist] = catalog[0].image[N1];
+	  nlist ++;
+	  if (nlist == NLIST - 1) {
+	    NLIST += 50;
+	    REALLOCATE (list, int, NLIST);
+	  }
+	}
+      }
+    }
+  }
+  
+  REALLOCATE (list, int, MAX(nlist, 1));
+  *Nlist = nlist;
+  return (list);
+
+}
+
+
+
+/* J is Average seq number for star */
+
+int *check_common_list (inlist, J, Nlist, catalog)
+int *inlist, J, *Nlist;
+Catalog catalog[];
+{
+
+  int i, j, N2, Ninlist;
+  int *list, NLIST, nlist;
+  int already, found, k;
+
+  NLIST = 50;
+  ALLOCATE (list, int, NLIST);
+  nlist = 0;
+  Ninlist = *Nlist;
+
+  for (j = 0; j < catalog[0].average[J].Nm; j++) {
+    N2 = catalog[0].average[J].offset + j;
+    found = FALSE;
+    for (i = 0; !found && (i < Ninlist); i++) {
+      if (catalog[0].image[N2] == -1)
+	continue; /* not a real measurement */
+      if (inlist[i] == catalog[0].image[N2]) {
+	found = TRUE;
+	already = FALSE; 
+	for (k = 0; !already && (k < nlist); k++) {
+	  if (inlist[i] == list[k]) {
+	    already = TRUE;
+	  }
+	}
+	if (!already) {
+	  list[nlist] = inlist[i];
+	  nlist ++;
+	  if (nlist == NLIST - 1) {
+	    NLIST += 50;
+	    REALLOCATE (list, int, NLIST);
+	  }
+	}
+      }
+    }
+  }
+  
+  /* if there are no common images, return the input list */
+  if (nlist != 0) {
+    free (inlist);
+    REALLOCATE (list, int, MAX(nlist, 1));
+    *Nlist = nlist;
+    return (list);
+  } else {
+    return (inlist);
+  }
+
+}
+
+
+/* measurements associated with image number -1 are not 
+   measurements we made, but are added, like USNO */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/markstar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/markstar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/markstar.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "markstar.h"
+
+main (argc, argv)
+int argc;
+char **argv;
+{
+
+  FILE *f;
+  int i, Nstars, Nimage, Nregions, Nmissed;
+  Image *image, *find_images();
+  Catalog catalog;
+  CatStats catstats;
+  struct timeval now, then;  
+  
+  gettimeofday (&then, (void *) NULL);
+  ConfigInit (argc, argv);
+
+  VERBOSE = FALSE;
+  if ((i = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (i, &argc, argv);
+  }
+  FORCE_RUN = FALSE;
+  if ((i = get_argument (argc, argv, "-f"))) {
+    FORCE_RUN = TRUE;
+    remove_argument (i, &argc, argv);
+  }
+  RESET = FALSE;
+  if ((i = get_argument (argc, argv, "-reset"))) {
+    RESET = TRUE;
+    remove_argument (i, &argc, argv);
+  }
+  if (argc < 2) {
+    fprintf (stderr, "ERROR: Usage: markstar (catalog)\n");
+    exit (0);
+  }
+
+  /* if lockfile exists, program will complain and quit */
+  check_lockfile (); 
+  check_permissions (argv[1]);
+
+  gcatalog (argv[1], &catalog);
+
+  gcatstats (&catalog, &catstats);
+
+  image = find_images (&catstats, &Nimage);
+
+  match_images (&catalog, image, Nimage);
+    
+  find_bright_stars (&catalog, &catstats); 
+
+  find_ghosts (&catalog, &catstats, argv[1], image, Nimage);  
+
+  find_trails (&catalog, &catstats);  
+
+  wcatalog (argv[1], &catalog);
+
+  if (VERBOSE) {
+    gettimeofday (&now, (void *) NULL);
+    fprintf (stderr, "%s: elapsed time = %.2f sec\n", argv[1], 
+	     (now.tv_sec - then.tv_sec) + 1e-6*(now.tv_usec - then.tv_usec));
+  }
+  clear_lockfile (); 
+  fprintf (stderr, "SUCCESS\n");
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/match_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/match_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/match_images.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "markstar.h"
+
+match_images (catalog, image, Nimage)
+Catalog catalog[];
+Image   image[];
+int Nimage;
+{
+  
+  int j, k, found;
+  unsigned int *start, *stop;
+
+  /* this must be allocated so future free will not fail */
+  ALLOCATE (catalog[0].image, int, MAX (catalog[0].Nmeasure, 1));
+  if (catalog[0].Naverage == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (FALSE);
+  }
+
+  ALLOCATE (start, unsigned int, Nimage);
+  ALLOCATE (stop,  unsigned int, Nimage);
+  for (j = 0; j < Nimage; j++) {
+    start[j] = image[j].tzero - MAX(0.01*image[j].trate*image[j].NY, 1);
+    stop[j]  = image[j].tzero + MAX(1.01*image[j].trate*image[j].NY, 1);
+  }
+
+  for (j = 0; j < catalog[0].Nmeasure; j++) {
+    found = FALSE;
+    if (catalog[0].measure[j].t == 0) {
+      catalog[0].image[j] = -1;
+      found = TRUE;
+    }
+    for (k = 0; (k < Nimage) && !found; k++) {
+      if ((catalog[0].measure[j].t >= start[k]) && 
+	  (catalog[0].measure[j].t <= stop[k])) {
+	catalog[0].image[j] = k;
+	found = TRUE;
+      }
+    }
+    if (!found) {
+      fprintf (stderr, "ERROR: can't find source image for this measurement: %d\n",
+	       catalog[0].measure[j].t);
+      exit (0);
+    }
+  }
+  free (start);
+  free (stop);
+}
+
+  /* this routine uses the time of each measurement to match the
+measurement with an image.  Since the measurement is only store to 1
+sec accuracy, which corresponds to roughly 30 rows at nominal speed,
+we can't tell exactly which image the star come from.  However, this
+doesn't matter, and in fact this helps a bit: a measurement from the
+top of one image is the same as from the bottom of the next.
+Therefore, we intentionally blur the edges of the images by 5%, which
+will help to tie together neighboring images... */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/sorts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/sorts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/sorts.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "markstar.h"
+
+void sort_seq (double *X, int *S, int N) {
+
+# define SWAPFUNC(A,B){ \
+  double dtmp = X[A]; X[A] = X[B]; X[B] = dtmp; \
+  int    itmp = Y[A]; Y[A] = Y[B]; Y[B] = itmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/wcatalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/wcatalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/fixcat/src/wcatalog.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "markstar.h"
+
+wcatalog (catname, catalog)
+char *catname;
+Catalog catalog[];
+{
+  
+  int i, Nitems, nitems, status, mode;
+  char filename[128], line[256];
+  FILE *f;
+  struct stat filestat;
+
+  sprintf (filename, "%s/%s\0", CATDIR, catname);
+
+  status = stat (filename, &filestat);
+  if (status == 0) { /* file exists, make backup copy */
+    sprintf (line, "mv %s %s~\0", filename, filename);
+    status = system (line);
+    if (status) {
+      fprintf (stderr, "ERROR: unable to create %s~, exiting\n", filename);
+      exit (0);
+    }
+  }
+
+  if (catalog[0].Naverage == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (FALSE);
+  }
+  
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't create new catalog file: %s\n", filename);
+    exit (0);
+  }
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (filename, mode);
+  
+  gfits_modify (&catalog[0].header, "NSTARS", "%d", 1, catalog[0].Naverage);
+  gfits_modify (&catalog[0].header, "NMEAS", "%d", 1, catalog[0].Nmeasure);
+  gfits_modify (&catalog[0].header, "NMISS", "%d", 1, catalog[0].Nmissing);
+
+  gfits_modify (&catalog[0].header, "MARKSTAR", "%t", 1, TRUE);
+
+  nitems = Fwrite (catalog[0].header.buffer, 1, catalog[0].header.size, f, "char");
+  if (nitems != catalog[0].header.size) {
+    fprintf (stderr, "ERROR: failed to write header\n");
+    exit (0);
+  }
+
+  Nitems = catalog[0].Naverage;
+  nitems = Fwrite (catalog[0].average, sizeof(Average), Nitems, f, "average");
+  if (nitems != Nitems) {
+    fprintf (stderr, "ERROR: failed to write catalog file aves %s\n", filename);
+    exit (0);
+  }
+  
+  Nitems = catalog[0].Nmeasure;
+  nitems = Fwrite (catalog[0].measure, sizeof(Measure), Nitems, f, "measure");
+  if (nitems != Nitems) {
+    fprintf (stderr, "ERROR: failed to write catalog file meas %s\n", filename);
+    exit (0);
+  }
+
+  Nitems = catalog[0].Nmissing;
+  nitems = Fwrite (catalog[0].missing, sizeof(Missing), Nitems, f, "missing");
+  if (nitems != Nitems) {
+    fprintf (stderr, "ERROR: failed to write catalog file miss %s\n", filename);
+    exit (0);
+  }
+
+  fclose (f);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/Makefile	(revision 22322)
@@ -0,0 +1,44 @@
+default: gastro
+help:
+	@echo "make options: gastro (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/gastro
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+gastro: $(BIN)/gastro.$(ARCH)
+install: $(DESTBIN)/gastro
+
+GASTRO	= \
+$(SRC)/gastro.$(ARCH).o \
+$(SRC)/gstars.$(ARCH).o \
+$(SRC)/gargs.$(ARCH).o \
+$(SRC)/gcenter.$(ARCH).o \
+$(SRC)/granges.$(ARCH).o \
+$(SRC)/gfit.$(ARCH).o \
+$(SRC)/rotate.$(ARCH).o \
+$(SRC)/gproject.$(ARCH).o \
+$(SRC)/gheader.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/gfitpoly.$(ARCH).o \
+$(SRC)/get_region_coords.$(ARCH).o \
+$(SRC)/greference.$(ARCH).o \
+$(SRC)/getusno.$(ARCH).o \
+$(SRC)/getgsc.$(ARCH).o \
+$(SRC)/getptolemy.$(ARCH).o \
+$(SRC)/plotstuff.$(ARCH).o \
+$(SRC)/misc.$(ARCH).o
+
+$(GASTRO): $(INC)/gastro.h
+$(BIN)/gastro.$(ARCH): $(GASTRO)
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,13 @@
+
+ gastro-1-4:
+  * converted to gfits APIs (forces libfits 1.6)
+  * converted to new DVO APIs (forces libdvo 1.3)
+  * removed unused code
+  * better error-checking for ConfigInit
+
+ gastro-1-3:
+    minor fixes to use updates to average.d
+
+ gastro-1-2:
+    added support for dvo table modes / formats (libdvo v1.0)
+    minor changes to sync with libfits modes (libfits v1.4)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/include/gastro.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/include/gastro.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/include/gastro.h	(revision 22322)
@@ -0,0 +1,115 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <kapa.h>
+
+double DEFAULT_RADIUS;
+double MINIMUM_RADIUS;
+double MAX_ERROR, MAX_NONLINEAR;
+double MIN_PRECISE;
+double CCD_PC1_1;
+double CCD_PC2_2;
+double CCD_PC1_2;
+double CCD_PC2_1;
+double NFIELD;
+double SEARCH_RADIUS;
+double MMIN;
+double ROT_ZERO;
+double dROT;
+double RA_OFFSET, DEC_OFFSET;
+double POLE_RA, POLE_DEC;
+int POLAR_ALIGNMENT;
+int NROT;
+int VERBOSE;
+int LONEOS_COORDS;
+int CATDUMP;
+int MATCHDUMP;
+int NOMATCHDUMP;
+int NEWPHOTCODE;
+int MIN_MATCHES;
+char *PHOTCODE;
+int FLIPX, FLIPY;
+int NPOLYTERMS;
+char CDROM[256];
+char CATDIR[256];
+char CATMODE[16];    /* raw, mef, split, mysql */
+char CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char REFCAT[256];
+char HEADER[256];
+int PLOTSTUFF;
+int MAGLIMS;
+int NMAX_STARS;
+char PhotCodeFile[256];
+
+int    FORCE;
+double F_RA;
+double F_DEC;
+
+char GSCFILE[256], GSCDIR[256], LONEOS_REGION_FILE[256];
+double ASEC_PIX;
+char ROUGH_ASTROMETRY[64];
+
+/* simple structure to carry around data on an array of stars */
+typedef struct {
+  double X;
+  double Y;
+  double mag;
+} SStars;
+
+typedef struct {
+  Coords coords;
+  float *X, *Y;
+  int *N;
+  double RA[2], DEC[2];
+  double Area, density, spacing;
+} CatStats;
+
+typedef struct {
+  double R, D;
+  double r, b;
+} USNOdata;
+
+typedef struct {
+  int *match;
+  float *X, *Y;
+  int *N;
+} USNOstats;
+
+/*  this seems to be a problem: is not included from math.h with the -ansi flag */
+extern double hypot PROTO((double, double));
+
+SStars   *getptolemy          PROTO((CatStats *catstats, int *NSTARS));
+SStars   *getgsc              PROTO((CatStats *catstats, int *NSTARS));
+USNOdata *getusno	      PROTO((USNOstats *usnostats, CatStats *catstats, int *Nusno));
+SStars   *gstars	      PROTO((char *file, int *NSTARS, Coords *coords, int *NX, int *NY, double *dNdM));
+void 	  ConfigInit	      PROTO((int *argc, char **argv));
+void 	  DonePlotting	      PROTO((Graphdata *graphmode, int N));
+void 	  PlotReset	      PROTO((int N));
+void 	  PlotVector	      PROTO((int Npts, float *vect, int mode, int N));
+void 	  PrepPlotting	      PROTO((int Npts, Graphdata *graphmode, int N));
+void 	  XDead		      PROTO(());
+void   	  alter_header        PROTO((char *, char **, int, double, double, double, double, double, double, double, double, int));
+void 	  area_of_region      PROTO((CatStats *region));
+void 	  define_region	      PROTO((CatStats *catstats, Coords *coords, int NX, int NY));
+void   	  find_shift          PROTO((SStars *, SStars *, int, int, double, double, int *, double *, double *, double *));
+void 	  gargs		      PROTO((int *argc, char **argv, Coords *coords));
+int 	  gaussj	      PROTO((double **a, int n, double **b, int m));
+int 	  gcenter	      PROTO((SStars *stars1in, SStars *stars2, int N1, int N2, Coords *coords, int NX, int NY, double *dR));
+int 	  get_region_coords   PROTO((double *ra, double *dec, int rnumber, char *side));
+int 	  gfit		      PROTO((SStars *stars1, SStars *stars2, int N1, int N2, Coords *coords, int NX, int NY, double *Radius, double *DR, int *Nmatch, int mode));
+void 	  gfitpoly	      PROTO((SStars *stars1, SStars *stars2, int N1, int N2, Coords *coords, double *Radius, double *DR, int *Nmatch));
+void 	  gheader	      PROTO((char *file, Coords coords, double dR, int Nmatch));
+void 	  gproject	      PROTO((SStars *catalog, SStars **stars, int Ncat, int *Nstars, Coords *coords, int NX, int NY, double dNdM, int N1));
+void 	  granges	      PROTO((SStars *stars1, SStars *stars2, int N1, int N2, int NPIX, double *gx, double *gy, double *gx0, double *gy0));
+int 	  greference	      PROTO((SStars **cat, int *Ncat, Coords *coords, int NX, int NY));
+void 	  hh_hms	      PROTO((double hh, int *hr, int *mn, double *sc));
+void 	  hms_format	      PROTO((char *line, double value));
+int       line_fit            PROTO((SStars *, SStars *, int, int, double, double, double, double, double *, double *, double *, double *, double *, double *, double *, double *));
+int 	  mk_polyterm	      PROTO((int n, int m, int norder));
+int 	  mk_vector	      PROTO((int n, int m, int norder));
+int 	  open_graph	      PROTO((int N));
+void   	  precess             PROTO((double *, double *, double, double));
+void   	  ranges              PROTO((SStars *, SStars *, int, int, double *, double *, double *, double *, double, double));
+void 	  rotate	      PROTO((SStars *stars, int Nstars, double angle, int Xo, int Yo));
+void 	  sort_stars	      PROTO((SStars *stars, int N));
+void      sort_lists          PROTO((double *X, double *Y, int *S, int N));
+void   	  stats               PROTO((char *, double *, double *, double *, double *, double));
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "gastro.h"
+
+void ConfigInit (int *argc, char **argv) {
+  
+  char *config, *file;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  ScanConfig (config, "OFFSET_RADIUS",     "%lf", 0, &SEARCH_RADIUS);   // max allowed offset in gcenter
+  ScanConfig (config, "MIN_MATCHES",       "%d",  0, &MIN_MATCHES);     // min allowed fitted stars 
+  ScanConfig (config, "DEFAULT_RADIUS",    "%lf", 0, &DEFAULT_RADIUS);  // starting radius for matched fit
+  ScanConfig (config, "MINIMUM_RADIUS",    "%lf", 0, &MINIMUM_RADIUS);  // min allowed radius for matched fit
+  ScanConfig (config, "MAX_ERROR",         "%lf", 0, &MAX_ERROR);       // max allowed error for valid solution
+  ScanConfig (config, "MAX_NONLINEAR",     "%lf", 0, &MAX_NONLINEAR);   // max allowed shear |(11*22 - 12*21) / (L1*L2)|
+  ScanConfig (config, "CCD_PC1_1",         "%lf", 0, &CCD_PC1_1);       // guess if WCS is missing
+  ScanConfig (config, "CCD_PC2_2",         "%lf", 0, &CCD_PC2_2);       // guess if WCS is missing
+  ScanConfig (config, "CCD_PC1_2",         "%lf", 0, &CCD_PC1_2);       // guess if WCS is missing
+  ScanConfig (config, "CCD_PC2_1",         "%lf", 0, &CCD_PC2_1);       // guess if WCS is missing
+  ScanConfig (config, "ASEC_PIX",          "%lf", 0, &ASEC_PIX);        // guess if WCS is missing
+  ScanConfig (config, "NFIELD",            "%lf", 0, &NFIELD);          // search region in field units
+  ScanConfig (config, "NPOLYTERMS",        "%d",  0, &NPOLYTERMS);      // fit order
+  ScanConfig (config, "ROT_ZERO",          "%lf", 0, &ROT_ZERO);        // rotation search region
+  ScanConfig (config, "dROT",              "%lf", 0, &dROT);            // rotation search region
+  ScanConfig (config, "NROT",              "%d",  0, &NROT);            // rotation search region
+  ScanConfig (config, "POLAR_ALIGNMENT",   "%d",  0, &POLAR_ALIGNMENT); // apply polar alignment correction
+  ScanConfig (config, "POLAR_AXIS_RA",     "%lf", 0, &POLE_RA);         // true coords of pole (should be HA, not RA)
+  ScanConfig (config, "POLAR_AXIS_DEC",    "%lf", 0, &POLE_DEC);        // true coords of pole
+  ScanConfig (config, "RA_OFFSET",         "%lf", 0, &RA_OFFSET);       // ?? not well defined (should be euler angle)
+  ScanConfig (config, "DEC_OFFSET",        "%lf", 0, &DEC_OFFSET);      // ?? not well defined (should be euler angle)
+  ScanConfig (config, "LONEOS_REGIONS",    "%s",  0, LONEOS_REGION_FILE); // table of LONEOS regions to fix guess astrometry
+
+  ScanConfig (config, "GSCFILE",           "%s",  0, GSCFILE);          // location of sky table
+  ScanConfig (config, "GSCDIR",            "%s",  0, GSCDIR);           // location of HST GSC data 
+  ScanConfig (config, "USNO_CDROM",        "%s",  0, CDROM);            // location of USNO A data (USNO_A_DIR in gastro2)
+  ScanConfig (config, "ASTRO_REFCAT",      "%s",  0, REFCAT);           // which astrometry catalog to use
+  ScanConfig (config, "CATDIR",            "%s",  0, CATDIR);           // location of ptolemy-format ref data
+  ScanConfig (config, "ROUGH_ASTROMETRY",  "%s",  0, ROUGH_ASTROMETRY); // where to get initial guess (header, config)
+
+  if (strcasecmp (ROUGH_ASTROMETRY, "header") && 
+      strcasecmp (ROUGH_ASTROMETRY, "config")) {
+    fprintf (stderr, "ROUGH_ASTROMETRY must be one of: header, config\n");
+    exit (0);
+  }
+  free (config);
+  free (file);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gargs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gargs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gargs.c	(revision 22322)
@@ -0,0 +1,125 @@
+# include "gastro.h"
+# define NARGS 2  /* minimum is:  gastro catalog */
+
+void ahelp () {
+
+  fprintf (stderr, "gastro -- astrometry for LONEOS\n");
+
+  fprintf (stderr, "  USAGE: gastro pixscale filename");
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -v (verbose mode)\n");
+  fprintf (stderr, "  -dump (dump catalog stars, don't complete astrometry)\n");
+  fprintf (stderr, "  -mdmp (dump matched catalog stars)\n");
+  fprintf (stderr, "\n"); 
+  exit (0);
+
+}
+
+void gargs (int *argc, char **argv, Coords *coords) {
+  
+  int N;
+
+  if (get_argument (*argc, argv, "-help") ||
+      get_argument (*argc, argv, "-h")) {
+    ahelp ();
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (*argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  LONEOS_COORDS = FALSE;
+  if ((N = get_argument (*argc, argv, "-loneos"))) {
+    LONEOS_COORDS = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  NEWPHOTCODE = FALSE;
+  if ((N = get_argument (*argc, argv, "-p"))) {
+    NEWPHOTCODE = TRUE;
+    remove_argument (N, argc, argv);
+    PHOTCODE = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  PLOTSTUFF = FALSE;
+  if ((N = get_argument (*argc, argv, "-plot"))) {
+    PLOTSTUFF = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  MAGLIMS = TRUE;
+  if ((N = get_argument (*argc, argv, "-maglims"))) {
+    MAGLIMS = FALSE;
+    remove_argument (N, argc, argv);
+  }
+
+  NMAX_STARS = 300;
+  if ((N = get_argument (*argc, argv, "-nstars"))) {
+    remove_argument (N, argc, argv);
+    NMAX_STARS = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  HEADER[0] = 0;
+  if ((N = get_argument (*argc, argv, "-header"))) {
+    remove_argument (N, argc, argv);
+    strcpy (HEADER, argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  HEADER[0] = 0;
+  if ((N = get_argument (*argc, argv, "-head"))) {
+    remove_argument (N, argc, argv);
+    strcpy (HEADER, argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  FLIPX = FALSE;
+  if ((N = get_argument (*argc, argv, "-fx"))) {
+    FLIPX = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  FLIPY = FALSE;
+  if ((N = get_argument (*argc, argv, "-fy"))) {
+    FLIPY = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  FORCE = FALSE;
+  if ((N = get_argument (*argc, argv, "-coords"))) {
+    FORCE = TRUE;
+    remove_argument (N, argc, argv);
+    F_RA = atof (argv[N]);
+    remove_argument (N, argc, argv);
+    F_DEC = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  CATDUMP = FALSE;
+  if ((N = get_argument (*argc, argv, "-dump"))) {
+    CATDUMP = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  MATCHDUMP = FALSE;
+  if ((N = get_argument (*argc, argv, "-mdmp"))) {
+    MATCHDUMP = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  NOMATCHDUMP = FALSE;
+  if ((N = get_argument (*argc, argv, "-cdmp"))) {
+    NOMATCHDUMP = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  if (*argc != NARGS) {
+    fprintf (stderr, "USAGE: gastro filename\n");
+    exit (0);
+  }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gastro.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gastro.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gastro.c	(revision 22322)
@@ -0,0 +1,85 @@
+# include "gastro.h"
+# define ABORT \
+{ Nmatch = 1; dR = 0; \
+  gheader (argv[1], coords, dR, Nmatch); \
+  exit (0); }
+
+int main (int argc, char **argv) {
+
+  int N1, N2, Ncat, NX, NY, Nmatch, Nminterms;
+  SStars *catalog, *stars1, *stars2;
+  struct timeval now, then;  
+  Coords coords;
+  double dNdM, dR, Radius;
+  
+  gettimeofday (&then, (void *) NULL);
+
+  ConfigInit (&argc, argv);
+  gargs (&argc, argv, &coords); 
+
+  /* load stars from image (*.cmp file) */
+  N1 = NMAX_STARS;  /* we only want a small number of stars */
+  stars1 = gstars (argv[1], &N1, &coords, &NX, &NY, &dNdM);
+
+  /* load stars from reference catalogs */
+  greference (&catalog, &Ncat, &coords, NX, NY);
+
+  /* get rough alignment with reference stars */
+  gproject (catalog, &stars2, Ncat, &N2, &coords, NX, NY, dNdM, N1);
+  gcenter (stars1, stars2, N1, N2, &coords, NX, NY, &Radius);
+  free (stars2);
+
+  /* reload reference, get good astrometry */
+  if (!greference (&catalog, &Ncat, &coords, NX, NY)) ABORT;
+  /* NFIELD = 1.2; */
+  gproject (catalog, &stars2, Ncat, &N2, &coords, NX, NY, dNdM, N1);
+  if (!gfit (stars1, stars2, N1, N2, &coords, NX, NY, &Radius, &dR, &Nmatch, 1)) ABORT;
+  free (stars2);
+ 
+  gproject (catalog, &stars2, Ncat, &N2, &coords, NX, NY, dNdM, N1);
+  gfitpoly (stars1, stars2, N1, N2, &coords, &Radius, &dR, &Nmatch);
+
+  if (dR > MAX_ERROR) {
+    fprintf (stderr, "ERROR: bad solution! %f %f (%d stars)\n", dR, (dR / sqrt(1.0*Nmatch)), Nmatch);
+    ABORT;
+  }
+  fprintf (stderr, "good solution: %f %f (%d stars)\n", dR, (dR / sqrt(1.0*Nmatch)), Nmatch);
+
+  Nminterms = MIN_MATCHES;
+  switch (NPOLYTERMS) {
+  case 0:
+  case 1:
+    /* we don't really allow zero order fits */
+    Nminterms = MIN_MATCHES;
+    break;
+  case 2:
+    Nminterms = 20;
+    break;
+  case 3:
+    Nminterms = 40;
+    break;
+  }
+    
+  if (Nmatch <= Nminterms) { 
+    fprintf (stderr, "ERROR: too few stars for reliable solution, only %d\n", Nmatch);
+    ABORT;
+  }
+
+  gheader (argv[1], coords, dR, Nmatch);
+
+  if (VERBOSE) {
+    gettimeofday (&now, (void *) NULL);
+    fprintf (stderr, "%s: elapsed time = %.2f sec\n", argv[1], 
+	     (now.tv_sec - then.tv_sec) + 1e-6*(now.tv_usec - then.tv_usec));
+  }
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
+
+/*
+  free (stars2);
+
+  DEFAULT_RADIUS = DEFAULT_RADIUS / 4;
+  gproject (catalog, &stars2, Ncat, &N2, &coords, NX, NY, dNdM, N1);
+  if (!gfit (stars1, stars2, N1, N2, &coords, NX, NY, &Radius, &dR, &Nmatch, 0)) ABORT;
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gcenter.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gcenter.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gcenter.c	(revision 22322)
@@ -0,0 +1,255 @@
+# include "gastro.h"
+# define NSIGMA 2.0
+
+/* stars1.X,Y and stars2.X,Y are in image pixels 
+   stars2 (ref catalog) is approx, based on guess for scale */
+
+int gcenter (SStars *stars1in, SStars *stars2, int N1, int N2, Coords *coords, int NX, int NY, double *dR) {
+
+  double mean, sigma, gx, gy, gx0, gy0, n, SearchRadius;
+  int NPIX, minN, Nmin, Nmin0;
+  int i, j, k;
+  double *N, *DX, *DY, *D2, *tX1, *tY1, *tX2, *tY2;
+  double rot, Rot, Smin, Smin0, s, Fmin, Fmin0, f;
+  double Xmin, Xmin0, Ymin, Ymin0;
+  double RA, DEC, RAo, DECo;
+  double dX, dY, refX, refY;
+  double cs, sn;
+  double dR1, dD1, dR2, dD2, d1, d2;
+  double *sx1, *sy1, *sx2, *sy2;
+  SStars *stars1;  
+  char c;
+  Graphdata graphdata;
+  float *xvect, *yvect;
+  int Nvect, NVECT;
+  
+  SearchRadius = NX * SEARCH_RADIUS;
+  xvect = yvect = NULL;
+  Nvect = 0;
+
+  if (PLOTSTUFF) {
+    NVECT = N1*N2;
+    ALLOCATE (xvect, float, NVECT);
+    ALLOCATE (yvect, float, NVECT);
+    graphdata.xmin = 0;
+    graphdata.xmax = 2000;
+    graphdata.ymin = 0;
+    graphdata.ymax =  4000;
+    graphdata.style = 2;
+    graphdata.ptype = 2;
+    graphdata.ltype = 0;
+    graphdata.etype = 0;
+    graphdata.color = 0;
+    graphdata.lweight = 0;
+    graphdata.size = 0.5;
+  }
+ 
+  /*  NPIX = MAX (300, sqrt (20*(N1*N2) / MIN (N1, N2))); */
+  /* NPIX = 10 * sqrt (N2); */
+  NPIX = 300;
+  mean = 2 * N1*N2 / (NPIX*NPIX);
+  sigma = sqrt (mean);
+  minN =  MAX (6, mean + NSIGMA*sigma);
+  fprintf (stderr, "N1: %d, N2: %d, minN: %d\n", N1, N2, minN);
+
+  ALLOCATE (N,    double, NPIX*NPIX);
+  ALLOCATE (DX,   double, NPIX*NPIX);
+  ALLOCATE (DY,   double, NPIX*NPIX);
+  ALLOCATE (D2,   double, NPIX*NPIX);
+
+  ALLOCATE (stars1, SStars, N1);
+  for (i = 0; i < N1; i++) {
+    stars1[i] = stars1in[i];
+  }
+  
+  if (PLOTSTUFF) {
+    for (i = 0; i < N1; i++) {
+      xvect[Nvect] = stars1in[i].X;
+      yvect[Nvect] = stars1in[i].Y;
+      Nvect ++;
+    }
+  }
+  if (PLOTSTUFF) {
+    PlotReset (0);
+    PrepPlotting (Nvect, &graphdata, 0);
+    PlotVector (Nvect, xvect, 0, 0);
+    PlotVector (Nvect, yvect, 1, 0);
+    DonePlotting (&graphdata, 0);
+    usleep (300000);
+    fprintf (stderr, "plotting %d points\n", Nvect);
+    fprintf (stderr, "type return to continue");
+    fscanf (stdin, "%c", &c);
+    Nvect = 0;
+  }
+
+  Xmin = Ymin = 0;
+  Fmin = Smin = 1000000.0;
+  rot = Rot = ROT_ZERO-dROT*NROT;
+  rotate (stars1, N1, Rot, (int)coords[0].crpix1, (int)coords[0].crpix2); 
+
+  graphdata.xmin = -200;
+  graphdata.xmax =  200;
+  graphdata.ymin = -200;
+  graphdata.ymax =  200;
+
+  ALLOCATE (tX1, double, N1);
+  ALLOCATE (tY1, double, N1);
+  ALLOCATE (tX2, double, N2);
+  ALLOCATE (tY2, double, N2);
+  for (i = 0; i < N2; i++) {
+    tX2[i] = stars2[i].X;
+    tY2[i] = stars2[i].Y;
+  }
+  for (Rot = ROT_ZERO-dROT*NROT; Rot <= ROT_ZERO+dROT*NROT; Rot += dROT) {
+    granges (stars1, stars2, N1, N2, NPIX, &gx, &gy, &gx0, &gy0);
+    bzero (N,   NPIX*NPIX*sizeof(double));
+    bzero (DX,  NPIX*NPIX*sizeof(double));
+    bzero (DY,  NPIX*NPIX*sizeof(double));
+    bzero (D2,  NPIX*NPIX*sizeof(double));
+    for (i = 0; i < N1; i++) {
+      tX1[i] = stars1[i].X;
+      tY1[i] = stars1[i].Y;
+    }
+    sx1 = tX1; sy1 = tY1;
+    for (i = 0; i < N1; i++, sx1++, sy1++) {
+      sx2 = tX2; sy2 = tY2;
+      for (j = 0; j < N2; j++, sx2++, sy2++) {
+	dX = *sx1 - *sx2;
+	dY = *sy1 - *sy2;
+	if (hypot (dX, dY) > SearchRadius) continue;
+	if (PLOTSTUFF) {
+	  xvect[Nvect] = stars1[i].X - stars2[j].X;
+	  yvect[Nvect] = stars1[i].Y - stars2[j].Y;
+	  Nvect ++;
+	}
+	k = NPIX*(int)(gx*dX+gx0) + (int)(gy*dY+gy0);
+	N[k]   += 1.0;
+	DX[k]  += dX;
+	DY[k]  += dY;
+	D2[k]  += dX*dX + dY*dY;
+      }
+    }
+
+    Fmin0 = 1000.0;
+    Smin0 = Nmin0 = Xmin0 = Ymin0 = 0;
+    for (k = 0; k < NPIX*NPIX; k++) {
+      n = N[k]; /* 1*/ 
+      if (n < minN)
+	continue;
+      s = D2[k] - (SQ(DX[k]) + SQ(DY[k])) / n; 
+      f = s / (pow(n,4.0)); /* = 12 ops */
+      
+      if (f < Fmin0) { 
+	Fmin0 = f;
+	Smin0 = s;
+	Nmin0 = n;
+	Xmin0 = DX[k] / n;
+	Ymin0 = DY[k] / n;
+      }
+    }
+    if (VERBOSE) fprintf (stderr, "best offset: %7.1f %7.1f at %.1f deg  (%f %f %d)\n", Xmin0, Ymin0, Rot, Fmin0, sqrt(Smin0), Nmin0);
+    if (Fmin0 < Fmin) {
+      Fmin = Fmin0;
+      Smin = Smin0;
+      Nmin = Nmin0;
+      Xmin = Xmin0;
+      Ymin = Ymin0;
+      rot  = Rot;
+    }
+    if (PLOTSTUFF) {
+      PlotReset (0);
+      PrepPlotting (Nvect, &graphdata, 0);
+      PlotVector (Nvect, xvect, 0, 0);
+      PlotVector (Nvect, yvect, 1, 0);
+      DonePlotting (&graphdata, 0);
+      usleep (300000);
+      fprintf (stderr, "plotting %d points\n", Nvect);
+      fprintf (stderr, "type return to continue");
+      fscanf (stdin, "%c", &c);
+      Nvect = 0;
+    }
+    
+    rotate (stars1, N1, dROT, (int)coords[0].crpix1, (int)coords[0].crpix2);
+  }
+  rotate (stars1, N1, -ROT_ZERO-dROT*NROT, (int)coords[0].crpix1, (int)coords[0].crpix2);
+
+  free (N);
+  free (DX);
+  free (DY);
+  free (D2);
+
+  /* dx = dy = 10 pix */
+  refX = coords[0].crpix1 - Xmin;
+  refY = coords[0].crpix2 - Ymin;
+  XY_to_RD (&RA, &DEC, refX, refY, coords);
+  XY_to_RD (&RAo, &DECo, (refX + 10), refY, coords);
+  dR1 = (RAo - RA)*cos(DEC*RAD_DEG);
+  dD1 = (DECo - DEC);
+  XY_to_RD (&RAo, &DECo, refX, (refY + 10), coords);
+  dR2 = (RAo - RA)*cos(DEC*RAD_DEG);
+  dD2 = (DECo - DEC);
+  d1 = coords[0].cdelt1;  d2 = coords[0].cdelt2;
+  cs = cos(RAD_DEG*rot);  sn = sin(RAD_DEG*rot);
+
+  coords[0].pc1_1 =  cs*dR1 / (10*d1) + sn*dR2 / (10*d1);    coords[0].pc1_2 = cs*dR2 / (10*d1) - sn*dR1 / (10*d1);
+  coords[0].pc2_1 =  cs*dD1 / (10*d2) + sn*dD2 / (10*d2);    coords[0].pc2_2 = cs*dD2 / (10*d2) - sn*dD1 / (10*d2);
+  coords[0].crval1 = RA;
+  coords[0].crval2 = DEC;
+
+  /* diameter of 1 pixel box */
+  *dR = 1*sqrt(Smin);
+
+  fprintf (stderr, "%f x %f, %f\n", 1/gx, 1/gy, *dR);
+  if (VERBOSE) fprintf (stderr, "using: %7.1f %7.1f at %.1f deg\n", Xmin, Ymin, rot);
+
+# if (0)
+  if (VERBOSE) {
+    fprintf (stderr, "%s\n", coords[0].ctype);
+    fprintf (stderr, "%f %f\n", coords[0].crval1, coords[0].crval2);
+    fprintf (stderr, "%f %f\n", coords[0].crpix1, coords[0].crpix2);
+    fprintf (stderr, "%f %f\n", coords[0].pc1_1, coords[0].pc1_2);
+    fprintf (stderr, "%f %f\n", coords[0].pc2_1, coords[0].pc2_2);
+    fprintf (stderr, "%f %f\n", coords[0].cdelt1, coords[0].cdelt2);
+  }
+# endif
+
+  DEFAULT_RADIUS = MAX ((5*NX / NPIX), DEFAULT_RADIUS);
+  return (TRUE);
+}
+
+  /* we now have Xmin, Ymin, rot, get coords in unrotate stars1 frame of correct crref 
+  RAo = coords[0].crval1;
+  DECo = coords[0].crval2;
+
+  cs = cos(RAD_DEG*rot);  sn = sin(RAD_DEG*rot);
+  refX = coords[0].crpix1;
+  refY = coords[0].crpix2;
+
+  coords[0].crpix1 =  Xmin*cs + Ymin*sn + refX;
+  coords[0].crpix2 = -Xmin*sn + Ymin*cs + refY;
+
+  xx = coords[0].pc1_1; xy = coords[0].pc1_2; 
+  yx = coords[0].pc2_1; yy = coords[0].pc2_2; 
+  coords[0].pc1_1 =  cs*xx + sn*xy;    coords[0].pc1_2 = cs*xy - sn*xx;
+  coords[0].pc2_1 =  cs*yx + sn*yy;    coords[0].pc2_2 = cs*yy - sn*yx;
+
+  XY_to_RD (&RA, &DEC, refX, refY, coords);
+  if (fabs(RAo - RA) > 90) {
+    RA = (RA > 180.0) ? (RA - 180) : (RA + 180);
+    DEC = (DEC > 0.0) ? (180.0 - DEC) : (-180.0 - DEC);
+  }
+  coords[0].crval1 = RA;
+  coords[0].crval2 = DEC;
+  coords[0].crpix1 = refX;
+  coords[0].crpix2 = refY;
+
+  Rot = RAo - RA;
+  cs = cos(RAD_DEG*rot);  sn = sin(RAD_DEG*rot);
+  xx = coords[0].pc1_1; xy = coords[0].pc1_2; 
+  yx = coords[0].pc2_1; yy = coords[0].pc2_2; 
+  coords[0].pc1_1 =  cs*xx + sn*xy;    coords[0].pc1_2 = cs*xy - sn*xx;
+  coords[0].pc2_1 =  cs*yx + sn*yy;    coords[0].pc2_2 = cs*yy - sn*yx;
+
+  
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/get_region_coords.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/get_region_coords.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/get_region_coords.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "gastro.h"
+# define NBYTE_LINE 53
+# define NLINES 100
+
+int get_region_coords (double *ra, double *dec, int rnumber, char *side) {
+  
+  FILE *f;
+  int i, j, done, found, Nbytes, Nline, num, NBYTES;
+  char *buffer;
+  double R, D;
+  
+  f = fopen (LONEOS_REGION_FILE, "r");
+  if (f == NULL) {
+    fprintf (stderr, "couldn't find region map %s\n", LONEOS_REGION_FILE);
+    return (FALSE);
+  }
+ 
+  NBYTES = NBYTE_LINE * NLINES;
+  ALLOCATE (buffer, char, NBYTES);
+ 
+  found = done = FALSE;
+  for (i = 0; !done && !found; i++) {
+    Nbytes = fread (buffer, sizeof(char), NBYTES, f);
+    if (Nbytes < 1) done = TRUE;
+    Nline = Nbytes / NBYTE_LINE;
+    for (j = 0; !found && (j < Nline); j++) {
+      num = atof (&buffer[j*NBYTE_LINE]);
+      if (num == rnumber) {
+	found = TRUE;
+	sscanf (&buffer[j*NBYTE_LINE], "%*d %lf %lf", &R, &D);
+	fwrite (&buffer[j*NBYTE_LINE], 1, 106, stderr);
+	fprintf (stderr, "\n\n%f %f\n", R, D);
+	if (!strncasecmp (side, "east", 4)) {
+	  R += 0.026 / cos (D);  
+	  /* if the word says "east", we need to offset by 1 chip width,
+	     R and D are in radians here, so 0.026 is 1.5 deg in radians */
+	}
+	R *= (180.0 / M_PI);
+	D *= (180.0 / M_PI);
+      }
+    }
+  }
+
+  free (buffer);
+  fclose (f);
+
+  if (!found) {
+    fprintf (stderr, "error: can't find desired region number %d\n", rnumber);
+    *ra = *dec = 0;
+    return (FALSE);
+  }
+
+  *ra = R;
+  *dec = D;
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/getgsc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/getgsc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/getgsc.c	(revision 22322)
@@ -0,0 +1,86 @@
+# include "gastro.h"
+# define BYTES_STAR 23
+# define BLOCK 1000
+
+SStars *rd_gsc (char *filename, int *Nstars);
+
+SStars *getgsc (CatStats *catstats, int *NSTARS) {
+  
+  int i, j, k, Ns, Ngsc, Nstars; 
+  SStars *gsc;
+  SStars *stars;
+  SkyList *skylist;
+  SkyTable *sky;
+  SkyRegion patch;
+
+  patch.Rmin = catstats[0].RA[0];
+  patch.Rmax = catstats[0].RA[1];
+  patch.Dmin = catstats[0].DEC[0];
+  patch.Dmax = catstats[0].DEC[1];
+
+  /* load regions from GSC table, restrict to patch */
+  sky = SkyTableFromGSC (GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  SkyTableSetFilenames (sky, GSCDIR, "cpt");
+  skylist = SkyListByPatch (sky, -1, &patch);
+  
+  Nstars = 0;
+  ALLOCATE (stars, SStars, 1);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    gsc = rd_gsc (skylist[0].filename[i], &Ngsc);
+
+    Ns = Nstars;
+    Nstars += Ngsc;
+
+    REALLOCATE (stars, SStars, MAX (1, Nstars));
+    for (k = Ns, j = 0; j < Ngsc; k++, j++) {
+      stars[k].X   = gsc[j].X;
+      stars[k].Y   = gsc[j].Y;
+      stars[k].mag = gsc[j].mag;
+    }      
+    free (gsc);
+  }
+  SkyTableFree (sky);
+
+  *NSTARS = Nstars;
+  return (stars);
+}  
+
+SStars *rd_gsc (char *filename, int *Nstars) {
+  
+  SStars *stars;
+  int i, NSTAR, nstar, Nbytes, nbytes;
+  char *buffer;
+  FILE *f;
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't find catalog file %s\n", filename);
+    exit (1);
+  }
+  
+  nstar = 0;
+  NSTAR = 1000;
+  ALLOCATE (stars, SStars, NSTAR);
+
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR));
+  Nbytes = BLOCK*BYTES_STAR;
+
+  while ((nbytes = fread (buffer, 1, Nbytes, f)) > 0) {
+    for (i = 0; i < nbytes / BYTES_STAR; i++) {
+      dparse (&stars[nstar].X, 1, &buffer[i*BYTES_STAR]);
+      dparse (&stars[nstar].Y, 2, &buffer[i*BYTES_STAR]);
+      dparse (&stars[nstar].mag, 3, &buffer[i*BYTES_STAR]);
+      nstar++;
+      if (nstar == NSTAR) {
+	NSTAR += 1000;
+	REALLOCATE (stars, SStars, NSTAR);
+      }
+    }
+  }
+
+  free (buffer);
+
+  *Nstars = nstar;
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/getptolemy.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/getptolemy.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/getptolemy.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "gastro.h"
+
+SStars *getptolemy (CatStats *catstats, int *NSTARS) {
+  
+  int i, j, k, Ns, Nstars; 
+  Catalog catalog;
+  SStars *stars;
+  SkyList *skylist;
+  SkyTable *sky;
+  SkyRegion patch;
+
+  patch.Rmin = catstats[0].RA[0];
+  patch.Rmax = catstats[0].RA[1];
+  patch.Dmin = catstats[0].DEC[0];
+  patch.Dmax = catstats[0].DEC[1];
+
+  Nstars = 0;
+  ALLOCATE (stars, SStars, 1);
+
+  /* load regions from GSC table, restrict to patch */
+  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  skylist = SkyListByPatch (sky, -1, &patch);
+  
+  for (i = 0; i <skylist[0].Nregions; i++) {
+    // set the parameters which guide catalog open/load/create
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS;
+    catalog.Nsecfilt  = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    // Naves_disk == 0 implies an empty catalog file
+    // for only_match, skip empty catalogs
+    if (!catalog.Naves_disk) {
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    Ns = Nstars;
+    Nstars += catalog.Naverage;
+
+    REALLOCATE (stars, SStars, MAX (1, Nstars));
+    for (k = Ns, j = 0; j < catalog.Naverage; k++, j++) {
+      stars[k].X = catalog.average[j].R;
+      stars[k].Y = catalog.average[j].D;
+      stars[k].mag = catalog.measure[catalog.average[j].measureOffset].M;
+    }      
+    dvo_catalog_free (&catalog);
+  }
+
+  if (VERBOSE) fprintf (stderr, "%d stars from PTOLEMY\n", Nstars);
+  *NSTARS = Nstars;
+  return (stars);
+}  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/getusno.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/getusno.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/getusno.c	(revision 22322)
@@ -0,0 +1,131 @@
+# include "gastro.h"
+# define NZONE 24
+
+int SPDzone[] = {
+  0, 75, 450, 375, 1500, 1650, 300, 1425, 1725, 525, 1275, 225, 
+  675, 150, 600, 1575, 750, 975, 900, 1050, 1125, 1200, 825, 1350};
+
+int USNOdisk[] = {
+  1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10};
+
+USNOdata *getusno (USNOstats *usnostats, CatStats *catstats, int *Nusno) {
+
+  long int offset;
+  int i, bin, first, last, nitems, Nitems, Nbins;
+  float hours[100];
+  int start[100], number[100], *buffer, *buf;
+  char filename[128], c;
+  FILE *f;
+  double DEC1;
+  int iDEC0, iDEC1, iRA0, iRA1;
+  int spd, spd_start, spd_end, disk;
+  int NUSNO, nusno;
+  USNOdata *usno;
+
+  iRA0 = catstats[0].RA[0] * 360000.0;
+  iRA1 = catstats[0].RA[1] * 360000.0;
+  iDEC0 = (catstats[0].DEC[0] + 90.0) * 360000.0;
+  iDEC1 = (catstats[0].DEC[1] + 90.0) * 360000.0;
+  
+  spd_start = (int)(    (catstats[0].DEC[0] + 90) / 7.5) * 75.0;
+  DEC1 = (catstats[0].DEC[1] + 90) / 7.5;
+  if (DEC1 > (int)(DEC1)) {
+    spd_end =   (int)(1 + (catstats[0].DEC[1] + 90) / 7.5) * 75.0;
+  } else {
+    spd_end =   (int)(0 + (catstats[0].DEC[1] + 90) / 7.5) * 75.0;
+  }
+
+  NUSNO = 5000;
+  ALLOCATE (usno, USNOdata, NUSNO);
+  nusno = 0;
+
+  for (spd = spd_start; spd < spd_end; spd += 75) {
+    disk = -1;
+    for (i = 0; i < NZONE; i++) {
+      if (spd == SPDzone[i]) 
+	disk = USNOdisk[i];
+    }
+    if (disk < 0) {
+      fprintf (stderr, "ERROR: can't find cdrom for spd %d\n",  spd);
+      exit (0);
+    }
+    
+    /* load accelerator file */
+    sprintf (filename, "%s/zone%04d.acc", CDROM, spd); 
+    fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "can't open file %s, is cdrom %d in drive?\n", filename, disk);
+      fprintf (stderr, "press return when ready to continue: ");
+      fscanf (stdin, "%c", &c);
+      fprintf (stderr, "trying again...\n");
+      f = fopen (filename, "r");
+      if (f == (FILE *) NULL) {
+	fprintf (stderr, "still can't open file %s, is cdrom %d in drive?\n", filename, disk);
+	exit (0);  
+      }
+    }
+    for (i = 0; fscanf (f, "%f %d %d", &hours[i], &start[i], &number[i]) != EOF; i++);
+    Nbins = i;
+    fclose (f);
+    
+    first = catstats[0].RA[0] / 3.75;
+    if ((catstats[0].RA[1] / 3.75) == (int) (catstats[0].RA[1] / 3.75)) 
+      last  = catstats[0].RA[1] / 3.75;
+    else 
+      last  = 1 + catstats[0].RA[1] / 3.75;
+
+    if ((first > Nbins) || (last > Nbins)) {
+      fprintf (stderr, "RA out of range\n");
+      exit (0);
+    }
+    
+    /* open data file */
+    sprintf (filename, "%s/zone%04d.cat", CDROM, spd);
+    fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "can't open file %s\n", filename);
+      exit (0);
+    }
+    /* advance file pointer to first slice */
+    offset = 3*sizeof(int)*(start[first] - 1);
+    fseek (f, offset, SEEK_SET);
+    /* on each loop, load data from an RA slice of the catalog */
+    for (bin = first; bin < last; bin++) {
+      Nitems = 3*number[bin];
+      ALLOCATE (buffer, int, Nitems);
+      nitems = Fread (buffer, sizeof(int), Nitems, f, "int");
+      if (nitems != Nitems) {
+	fprintf (stderr, "error reading data from file %s (%d, %d, %d)\n", 
+		 filename, start[bin], number[bin], nitems);
+	exit (0);
+      }
+      buf = buffer;
+      /* print out data from slice within RA and DEC range */
+      for (i = 0; i < number[bin]; i++, buf+=3) {
+	if ((buf[0] > iRA0) && (buf[0] < iRA1) &&
+	    (buf[1] > iDEC0) && (buf[1] < iDEC1)) {
+	  usno[nusno].R = buf[0]/360000.0;
+	  usno[nusno].D = buf[1]/360000.0 - 90.0;
+	  usno[nusno].r = 0.1*(buf[2] - 1000*((int)(buf[2]/1000)));
+	  usno[nusno].b = 0.1*((int)(buf[2] - 1000000*((int)(buf[2]/1000000))) / 1000);
+	  nusno ++;
+	  if (nusno == NUSNO - 1) {
+	    NUSNO += 5000;
+	    REALLOCATE (usno, USNOdata, NUSNO);
+	  }	  
+	}
+      }
+      free (buffer);
+    }
+    fclose (f);
+  }
+
+  REALLOCATE (usno, USNOdata, MAX (nusno, 1));
+  *Nusno = nusno;
+  return (usno);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gfit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gfit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gfit.c	(revision 22322)
@@ -0,0 +1,460 @@
+# include "gastro.h"
+
+/* stars1.X,Y and stars2.X,Y are both in image pixels. 
+   The conversion terms (X_O, X_X, etc) make linear 
+   corrections in pixel coordinates */
+
+int gfit (SStars *stars1, SStars *stars2, int N1, int N2, Coords *coords, int NX, int NY, double *Radius, double *DR, int *Nmatch, int mode) {
+  
+  int i, j, iteration, Niter, last, halt, first_j, *Nextra, extras;
+  int *tmpN1, *tmpN2;
+  double X_O, X_X, X_Y, dX;
+  double Y_O, Y_X, Y_Y, dY;
+  double x, y, x2, y2, xy, N, R, wt;
+  double X, Y, Xx, Yy, Xy, Yx;
+  double Dx, Dy, DD, d2X, d2Y;
+  double RA, DEC, radius, radius2, fratio;
+  double *tmpX1, *tmpX2, *tmpY1, *tmpY2;
+  double tX1, tX2, tY1, tY2;
+  double dS;
+
+  char c;
+  Graphdata graphdata;
+  float *xvect, *yvect;
+  float *xvect2, *yvect2;
+  int Nvect, NVECT;
+  int Nvect2, NVECT2;
+  
+  Nvect = NVECT = Nvect2 = NVECT2 = 0;
+  xvect = yvect = xvect2 = yvect2 = NULL;
+
+  if (PLOTSTUFF) {
+    NVECT2 = MAX(N1, N2);
+    NVECT = MAX(N1, N2);
+    ALLOCATE (xvect, float, NVECT);
+    ALLOCATE (yvect, float, NVECT);
+    ALLOCATE (xvect2, float, NVECT2);
+    ALLOCATE (yvect2, float, NVECT2);
+  }
+    
+
+  /* allocate space for star coords */
+  ALLOCATE (tmpX1, double, N1);
+  ALLOCATE (tmpY1, double, N1);
+  ALLOCATE (tmpN1, int, N1);
+  
+  ALLOCATE (tmpX2, double, N2);
+  ALLOCATE (tmpY2, double, N2);
+  ALLOCATE (tmpN2, int, N2);
+  ALLOCATE (Nextra, int, N2);
+  bzero (Nextra, sizeof(int) * N2);
+
+  dX = dY = N = 0;
+
+  /* assign and sort list */
+  for (i = 0; i < N1; i++) {
+    tmpX1[i] = stars1[i].X;
+    tmpY1[i] = stars1[i].Y;
+    tmpN1[i] = i;
+  }
+  if (N1 > 1) sort_coords_index (tmpX1, tmpY1, tmpN1, N1);
+
+   
+  /* choose iteration ranges */
+  fratio = 1.41421;
+  extras = halt = last = FALSE;
+  radius = *Radius;
+  Niter = 2 + log (radius/MINIMUM_RADIUS) / log (fratio);
+  
+  /* initial values for fit coeffs */
+  X_X = 1; X_Y = 0; X_O = 0;
+  Y_X = 0; Y_Y = 1; Y_O = 0;
+  
+  for (iteration = 0; iteration < Niter; iteration ++) {
+
+    if (iteration >= Niter - 1) { /* next loop is the last one */
+      radius *= fratio;
+      last = TRUE;
+    }
+
+    /* setup and define */
+    radius2 = radius*radius;
+    dX = dY = d2X = d2Y = x = y = x2 = y2 = xy = X = Y = Xx = Xy = Yx = Yy = N = R = 0;
+    for (i = 0; i < N2; i++) {
+      tmpX2[i] = (X_O) + (X_X)*stars2[i].X + (X_Y)*stars2[i].Y;
+      tmpY2[i] = (Y_O) + (Y_X)*stars2[i].X + (Y_Y)*stars2[i].Y;
+      tmpN2[i] = i;
+    }
+    
+    if (PLOTSTUFF) {
+      for (i = 0; i < N1; i++) {
+	xvect2[i] = tmpX1[i];
+	yvect2[i] = tmpY1[i];
+      }
+      Nvect2 = N1;
+
+      graphdata.xmin = 0;
+      graphdata.xmax = 2000;
+      graphdata.ymin = 4000;
+      graphdata.ymax =  0;
+      graphdata.style = 2;
+      graphdata.ptype = 3;
+      graphdata.ltype = 0;
+      graphdata.etype = 0;
+      graphdata.color = 0;
+      graphdata.lweight = 0;
+      graphdata.size = 1.5;
+    
+      PlotReset (1);
+      PrepPlotting (Nvect2, &graphdata, 1);
+      PlotVector (Nvect2, xvect2, 0, 1);
+      PlotVector (Nvect2, yvect2, 1, 1);
+      DonePlotting (&graphdata, 1);
+      Nvect2 = 0;
+
+      for (i = 0; i < N2; i++) {
+	xvect2[i] = tmpX2[i];
+	yvect2[i] = tmpY2[i];
+      }
+      Nvect2 = N2;
+
+      graphdata.ptype = 1;
+
+      PrepPlotting (Nvect2, &graphdata, 1);
+      PlotVector (Nvect2, xvect2, 0, 1);
+      PlotVector (Nvect2, yvect2, 1, 1);
+      DonePlotting (&graphdata, 1);
+      Nvect2 = 0;
+    }
+
+    if (N2 > 1) sort_coords_index (tmpX2, tmpY2, tmpN2, N2);
+    
+    /* find matched stars */
+    for (i = j = 0; (i < N1) && (j < N2); ) {  
+      tX1 = tmpX1[i];
+      tX2 = tmpX2[j];
+      Dx = tX1 - tX2;
+      if (Dx <= -2.0*radius) {
+	i++;
+	continue;
+      }
+      if (Dx >= 2.0*radius) {
+	j++;
+	continue;
+      }
+
+      /**** possible improvement: find only the closest match 
+	    for stars that have more than one (save DD and i for each
+            cat star j */
+      /* in the right range */
+      first_j = j;
+      for (; (Dx > -2.0*radius) && (j < N2); j++) {
+	tY1 = tmpY1[i];
+	tX2 = tmpX2[j];
+	tY2 = tmpY2[j];
+	Dx = tX1 - tX2;
+	Dy = tY1 - tY2;
+	DD = Dx*Dx + Dy*Dy;
+	/* stars matched */
+	if (DD < radius2) {
+	  if (PLOTSTUFF) {
+	    xvect[Nvect] = Dx;
+	    yvect[Nvect] = tmpY1[i];
+	    Nvect ++;
+	    if (Nvect == NVECT) {
+	      NVECT += 100;
+	      REALLOCATE (xvect, float, NVECT);
+	      REALLOCATE (yvect, float, NVECT);
+	    }
+	    xvect2[Nvect2] = tmpX1[i];
+	    yvect2[Nvect2] = tmpY1[i];
+	    Nvect2 ++;
+	    if (Nvect2 == NVECT2 - 1) {
+	      NVECT2 += 100;
+	      REALLOCATE (xvect2, float, NVECT2);
+	    REALLOCATE (yvect2, float, NVECT2);
+	    }
+	  }
+	  /* wt = sqrt(sqrt(DD)); */
+	  wt = DD + 1;
+	  dX += Dx;
+	  dY += Dy;
+	  d2X += Dx*Dx;
+	  d2Y += Dy*Dy;
+	  x  += tX2/wt;
+	  y  += tY2/wt;
+	  x2 += tX2*tX2/wt;
+	  y2 += tY2*tY2/wt;
+	  xy += tX2*tY2/wt;
+	  X  += tX1/wt;
+	  Y  += tY1/wt;
+	  Xx += tX1*tX2/wt;
+	  Xy += tX1*tY2/wt;
+	  Yx += tY1*tX2/wt;
+	  Yy += tY1*tY2/wt;
+	  N  += 1.0;
+	  R  += 1.0/wt;
+	  if (last && MATCHDUMP && mode) {
+	    XY_to_RD (&RA, &DEC, stars2[tmpN2[j]].X, stars2[tmpN2[j]].Y, coords);
+	    fprintf (stdout, "%f %f %f %f %f\n", RA, DEC, stars2[tmpN2[j]].X, stars2[tmpN2[j]].Y, stars2[tmpN2[j]].mag);
+	  } 
+	}
+      }
+      j = first_j;
+      i++;
+    }
+    
+    if (PLOTSTUFF) {
+      
+      graphdata.xmin = 0;
+      graphdata.xmax = 2000;
+      graphdata.ymin = 4000;
+      graphdata.ymax =  0;
+      graphdata.style = 2;
+      graphdata.ptype = 2;
+      graphdata.ltype = 0;
+      graphdata.etype = 0;
+      graphdata.color = 0;
+      graphdata.lweight = 0;
+      graphdata.size = 1.5;
+
+      PrepPlotting (Nvect2, &graphdata, 1);
+      PlotVector (Nvect2, xvect2, 0, 1);
+      PlotVector (Nvect2, yvect2, 1, 1);
+      DonePlotting (&graphdata, 1);
+
+      graphdata.xmin = -150;
+      graphdata.xmax = 150;
+      graphdata.ymin = 0;
+      graphdata.ymax =  4000;
+      graphdata.style = 2;
+      graphdata.ptype = 2;
+      graphdata.ltype = 0;
+      graphdata.etype = 0;
+      graphdata.color = 0;
+      graphdata.lweight = 0;
+      graphdata.size = 1.5;
+    
+      PlotReset (0);
+      PrepPlotting (Nvect, &graphdata, 0);
+      PlotVector (Nvect, xvect, 0, 0);
+      PlotVector (Nvect, yvect, 1, 0);
+      DonePlotting (&graphdata, 0);
+      usleep (300000);
+      fprintf (stderr, "plotting %d points\n", Nvect);
+      fprintf (stderr, "type return to continue");
+      fscanf (stdin, "%c", &c);
+      Nvect = 0;
+    }
+
+    /* 
+    if (extras == 1) {
+      extras = 2;
+      halt = last = FALSE;
+      iteration -= 2;
+      radius *= fratio*fratio;
+    }
+    */
+
+    if (MATCHDUMP && mode && last) exit (0);
+    
+    if (NOMATCHDUMP && mode && last) {
+      for (i = 0; i < N2; i++) {
+	XY_to_RD (&RA, &DEC, stars2[i].X, stars2[i].Y, coords);
+	fprintf (stdout, "%f %f %f %f %f\n", RA, DEC, stars2[i].X, stars2[i].Y, stars2[i].mag);
+      }
+      exit (0);
+    }
+    
+    /* calculate the fit parameters */
+    if (!last) { 
+      double XX1, XY1, YX1, YY1, XO1, YO1;
+      double XX0, XY0, YX0, YY0, XO0, YO0;
+      double **matrix, **vector;
+      int NR, NC;
+      
+      if (VERBOSE) fprintf (stderr, "radius: %f, No. of matched stars: %d\n", radius, (int) N);
+      
+      if (N < 3) {
+	fprintf (stderr, "ERROR: too few stars\n");
+	X_O = X_X = X_Y = Y_O = Y_X = Y_Y = 0;
+	return (FALSE);
+      }
+      
+      NR = 3; NC = 2;
+      ALLOCATE (matrix, double *, NR);
+      ALLOCATE (vector, double *, NR);
+      for (i = 0; i < NR; i++) {
+	ALLOCATE (matrix[i], double, NR);
+	ALLOCATE (vector[i], double, NC);
+	bzero (vector[i], NC*sizeof(double));
+	bzero (matrix[i], NR*sizeof(double));
+      }
+
+      matrix[0][0] = R;
+      matrix[0][1] = matrix[1][0] = x;
+      matrix[0][2] = matrix[2][0] = y;
+      matrix[1][2] = matrix[2][1] = xy;
+      matrix[1][1] = x2;
+      matrix[2][2] = y2;
+
+      vector[0][0] = X;
+      vector[1][0] = Xx;
+      vector[2][0] = Xy;
+
+      vector[0][1] = Y;
+      vector[1][1] = Yx;
+      vector[2][1] = Yy;
+
+      dgaussjordan (matrix, vector, NR, NC); 
+      
+      /* 
+      Sx2 = x2 - x*x/N;
+      Sy2 = y2 - y*y/N;
+      Sxy = xy - x*y/N;
+      SXx = Xx - X*x/N;
+      SXy = Xy - X*y/N;
+      SYx = Yx - Y*x/N;
+      SYy = Yy - Y*y/N;
+      */
+
+      XX0 = X_X; XY0 = X_Y; XO0 = X_O;
+      YX0 = Y_X; YY0 = Y_Y; YO0 = Y_O;
+      
+      /* fit parameters relative to rotated frame */
+      /* 
+      XX1 = (SXx*Sy2 - SXy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+      XY1 = (SXy*Sx2 - SXx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+      XO1 = X/N - (XX1)*x/N - (XY1)*y/N;
+      
+      YX1 = (SYx*Sy2 - SYy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+      YY1 = (SYy*Sx2 - SYx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+      YO1 = Y/N - (YX1)*x/N - (YY1)*y/N;
+      */
+
+      XO1 = vector[0][0];
+      XX1 = vector[1][0];
+      XY1 = vector[2][0];
+			
+      YO1 = vector[0][1];
+      YX1 = vector[1][1];
+      YY1 = vector[2][1];
+
+      fprintf (stderr, "%f %f %f\n", vector[0][0], vector[1][0], vector[2][0]);
+      fprintf (stderr, "%f %f %f\n", vector[0][1], vector[1][1], vector[2][1]);
+
+      /* fit parameters relative to original frame */
+      X_X = XX1*XX0 + XY1*YX0;
+      X_Y = XX1*XY0 + XY1*YY0;
+      X_O = XX1*XO0 + XY1*YO0 + XO1;
+      
+      Y_X = YX1*XX0 + YY1*YX0;
+      Y_Y = YX1*XY0 + YY1*YY0;
+      Y_O = YX1*XO0 + YY1*YO0 + YO1;
+      
+    }
+    dX = sqrt(d2X/N - dX*dX/(N*N));  /* scatter in pixels in the X direction */
+    dY = sqrt(d2Y/N - dY*dY/(N*N));  /* scatter in pixels in the Y direction */
+    dS = hypot (dX, dY) / sqrt(N);
+    if (VERBOSE) {
+      fprintf (stderr, "scatter in pixels: %5.2f x %5.2f -- %5.2f %d %d %d %d\n", dX, dY, dS, halt, last, extras, iteration);
+    }
+# if (0)
+    if (!halt && !extras) {
+      if (iteration > 0) {
+	dSS = (Sprev - dS) / Sprev;
+      } else {
+	dSS = 1.0;
+      }
+      if (dSS < 0.05) { /* no fractional improvement, stop at last value */
+	radius = Rprev * fratio;
+	halt = TRUE;
+	/* recalculate fit parameters on first, get scatter on second, exit on third */
+      }
+      Rprev = radius;
+      Sprev = dS;
+    }
+# endif
+    radius /= fratio;
+  }
+  radius *= fratio;
+  
+  /* convert X_X, etc to coords */ 
+  { 
+    double X0, Y0, S1, S2, p11, p21, p12, p22;
+    double delt, A, B, C, D, dRot;
+    
+    *Nmatch = N;
+    
+    delt = 1.0 / (X_X*Y_Y - X_Y*Y_X);
+    X = (coords[0].crpix1 - X_O);
+    Y = (coords[0].crpix2 - Y_O);
+    X0 = delt * (X*Y_Y - Y*X_Y);
+    Y0 = delt * (Y*X_X - X*Y_X);
+    XY_to_RD (&RA, &DEC, X0, Y0, coords);
+    
+    S1 = coords[0].cdelt1;
+    S2 = coords[0].cdelt2;
+    p11 = coords[0].pc1_1;    p12 = coords[0].pc1_2;
+    p21 = coords[0].pc2_1;    p22 = coords[0].pc2_2;
+    
+    A =  S1*p11*Y_Y - S2*p12*Y_X;   B = S2*p12*X_X - S1*p11*X_Y;
+    C =  S1*p21*Y_Y - S2*p22*Y_X;   D = S2*p22*X_X - S2*p21*X_Y;
+    
+    coords[0].cdelt1 = sqrt (A*A + C*C);
+    coords[0].cdelt2 = sqrt (B*B + D*D);
+    
+    coords[0].pc1_1 = A / coords[0].cdelt1; 
+    coords[0].pc1_2 = B / coords[0].cdelt2; 
+    coords[0].pc2_1 = C / coords[0].cdelt1; 
+    coords[0].pc2_2 = D / coords[0].cdelt2; 
+    
+    coords[0].cdelt1 = coords[0].cdelt1 * delt;
+    coords[0].cdelt2 = coords[0].cdelt2 * delt;
+    if ((fabs(coords[0].cdelt1) > 2.0*ASEC_PIX/3600.0) || 
+	(fabs(coords[0].cdelt1) < 0.5*ASEC_PIX/3600.0) ||
+	(fabs(coords[0].cdelt2) > 2.0*ASEC_PIX/3600.0) || 
+	(fabs(coords[0].cdelt2) < 0.5*ASEC_PIX/3600.0)) {
+      fprintf (stderr, "ERROR: absurd solution\n");
+      return (FALSE);
+    }
+    
+    dRot = coords[0].crval1 - RA ;
+    coords[0].crval1 = RA;
+    coords[0].crval2 = DEC;
+    
+    A = cos (dRot*RAD_DEG);
+    B = sin (dRot*RAD_DEG);
+
+    p11 = coords[0].pc1_1;    p12 = coords[0].pc1_2;
+    p21 = coords[0].pc2_1;    p22 = coords[0].pc2_2;
+    
+    coords[0].pc1_1 = p11*A - p12*B;
+    coords[0].pc1_2 = p11*B + p12*A;
+    coords[0].pc2_1 = p21*A - p22*B;
+    coords[0].pc2_2 = p21*B + p22*A;
+
+  }
+
+  while (coords[0].crval1 < 0) coords[0].crval1 += 360.0;
+  while (coords[0].crval1 > 360.0) coords[0].crval1 -= 360.0;
+
+  *DR = sqrt (SQ(dX*coords[0].cdelt1*3600.0) + SQ(dY*coords[0].cdelt1*3600.0));
+  *Radius = radius;
+
+  if ((mode == 1) && VERBOSE) {
+    fprintf (stderr, "linear astrometric solution:\n");
+    fprintf (stderr, "mode: %s\n", coords[0].ctype);
+    fprintf (stderr, "ref value: %f %f\n", coords[0].crval1, coords[0].crval2);
+    fprintf (stderr, "ref pixel: %f %f\n", coords[0].crpix1, coords[0].crpix2);
+    fprintf (stderr, "ra  terms: %f %f\n", coords[0].pc1_1, coords[0].pc1_2);
+    fprintf (stderr, "dec terms: %f %f\n", coords[0].pc2_1, coords[0].pc2_2);
+    fprintf (stderr, "plt scale: %f %f\n", coords[0].cdelt1, coords[0].cdelt2);
+    fprintf (stderr, "accuracy:  %f %f\n", *DR, *DR / sqrt ((double)(*Nmatch)));
+  }
+
+  return (TRUE);
+
+  
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gfitpoly.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gfitpoly.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gfitpoly.c	(revision 22322)
@@ -0,0 +1,389 @@
+# include "gastro.h"
+
+void gfitpoly (SStars *stars1, SStars *stars2, int N1, int N2, Coords *coords, double *Radius, double *DR, int *Nmatch) {
+  
+  int i, j, m, n, M, N;
+  int first_j, last;
+  int *tmpN1, *tmpN2;
+  int NORDER, NTERM, NPARS, NPOWR;
+  double **sum, **xsum, **ysum;
+  double **matrix, **vector;
+  double Dx, Dy, DD, dX, dY, d2X, d2Y;
+  double radius, radius2;
+  double *tmpX1, *tmpX2, *tmpY1, *tmpY2;
+  double tX1, tX2, tY1, tY2;
+  double xterm, yterm, term, max;
+
+  NORDER = NPOLYTERMS;
+  NPOWR = NORDER + 1;
+  NTERM = 2*NORDER + 1;
+  NPARS = (NORDER + 1)*(NORDER + 2) / 2;
+  if (NPOLYTERMS < 2) {
+    coords[0].Npolyterms = 0;
+    {
+      double nominal_det, measure_det, min_det, max_det, d1, d2, diffangle, tmp;
+      
+      nominal_det = CCD_PC1_1 * CCD_PC2_2 - CCD_PC1_2 * CCD_PC2_1;
+      min_det = nominal_det / 1.05;
+      max_det = nominal_det * 1.05;
+      if (min_det > max_det) {
+	tmp = max_det; max_det = min_det; min_det = tmp;
+      }
+      
+      measure_det = coords[0].pc1_1*coords[0].pc2_2 - coords[0].pc1_2*coords[0].pc2_1;
+      if ((measure_det > max_det) || (measure_det < min_det)) {
+	fprintf (stderr, "absurd solution, not cartesian\n");
+	*DR = 1e9;
+      }
+      d1 = hypot (coords[0].pc1_2, coords[0].pc1_1);
+      d2 = hypot (coords[0].pc2_2, coords[0].pc2_1);
+      diffangle = fabs (coords[0].pc2_1*coords[0].pc1_1 + coords[0].pc1_2*coords[0].pc2_2) / (d1*d2);
+      if (diffangle > MAX_NONLINEAR) {
+	fprintf (stderr, "absurd solution, not cartesian\n");
+	*DR = 1e9;
+      }
+    }
+    return;
+  }
+
+  fprintf (stderr, "\nattempting higher order fit\n");
+
+  /* allocate space for star coords */
+  ALLOCATE (tmpX1, double, N1);
+  ALLOCATE (tmpY1, double, N1);
+  ALLOCATE (tmpN1, int, N1);
+
+  ALLOCATE (tmpX2, double, N2);
+  ALLOCATE (tmpY2, double, N2);
+  ALLOCATE (tmpN2, int, N2);
+
+  /* assign and sort list */
+  for (i = 0; i < N1; i++) {
+    tmpX1[i] = stars1[i].X;
+    tmpY1[i] = stars1[i].Y;
+    tmpN1[i] = i;
+  }
+  if (N1 > 1) sort_coords_index (tmpX1, tmpY1, tmpN1, N1);
+  for (i = 0; i < N2; i++) {
+    tmpX2[i] = stars2[i].X;
+    tmpY2[i] = stars2[i].Y;
+    tmpN2[i] = i;
+  }
+  if (N2 > 1) sort_coords_index (tmpX2, tmpY2, tmpN2, N2);
+  
+  /* choose iteration ranges */
+  radius = MINIMUM_RADIUS;
+  radius2 = radius*radius;
+
+  /* allocate arrays for fit solution */
+  ALLOCATE (sum, double *, NTERM);
+  ALLOCATE (xsum, double *, NTERM);
+  ALLOCATE (ysum, double *, NTERM);
+  for (i = 0; i < NTERM; i++) {
+    ALLOCATE (sum[i], double, NTERM);
+    bzero (sum[i], NTERM*sizeof(double));
+    ALLOCATE (xsum[i], double, NTERM);
+    bzero (xsum[i], NTERM*sizeof(double));
+    ALLOCATE (ysum[i], double, NTERM);
+    bzero (ysum[i], NTERM*sizeof(double));
+  }
+  ALLOCATE (matrix, double *, NPARS);
+  ALLOCATE (vector, double *, NPARS);
+  for (i = 0; i < NPARS; i++) {
+    ALLOCATE (matrix[i], double, NPARS);
+    ALLOCATE (vector[i], double, 2);
+    bzero (vector[i], 2*sizeof(double));
+    bzero (matrix[i], NPARS*sizeof(double));
+  }
+  
+  /* do the following loop twice.  
+     on first pass, find the coeffs.
+     on next pass, find just the residuals */
+
+  dX = dY = d2X = d2Y = N = 0;
+  for (last = 0; last < 2; last++) {
+    if (last) {
+      /* assign values based on determined coeffs */
+      for (i = 0; i < N1; i++) {
+	tmpX1[i] = tmpY1[i] = 0;
+	yterm = 1;
+	for (m = 0; m < NPOWR; m++) { 
+	  xterm = 1;
+	  for (n = 0; n < NPOWR - m; n++) {
+	    tmpX1[i] += xterm*yterm*xsum[n][m];
+	    tmpY1[i] += xterm*yterm*ysum[n][m];
+	    xterm *= stars1[i].X;
+	  }	
+	  yterm *= stars1[i].Y;
+	}
+	tmpN1[i] = i;
+      }
+      if (N1 > 1) sort_coords_index (tmpX1, tmpY1, tmpN1, N1);
+      dX = dY = d2X = d2Y = N = 0;
+    }
+    /* find matched stars */
+    for (i = j = 0; (i < N1) && (j < N2); ) {  
+      tX1 = tmpX1[i];
+      tX2 = tmpX2[j];
+      Dx = tX1 - tX2;
+      if (Dx <= -2.0*radius) {
+	i++;
+	continue;
+      }
+      if (Dx >= 2.0*radius) {
+	j++;
+	continue;
+      }
+      /* in the right range */
+      first_j = j;
+      for (; (Dx > -2.0*radius) && (j < N2); j++) {
+	tY1 = tmpY1[i];
+	tX2 = tmpX2[j];
+	tY2 = tmpY2[j];
+	Dx = tX1 - tX2;
+	Dy = tY1 - tY2;
+	DD = Dx*Dx + Dy*Dy;
+	/* stars matched */
+	if (DD < radius2) {
+	  if (last) {  /* calculate residuals */
+	    dX += Dx;
+	    dY += Dy;
+	    d2X += Dx*Dx;
+	    d2Y += Dy*Dy;
+	    N  += 1.0; 
+	  } else {    /* accumulate data for coeffs */
+	    xterm = 1;
+	    for (n = 0; n < NTERM; n++) {
+	      yterm = 1;
+	      for (m = 0; m < NTERM; m++) {
+		term = xterm*yterm;
+		if (n+m < NTERM) {
+		  sum[n][m] += term;
+		}
+		if (n+m < NPOWR) {
+		  xsum[n][m] += tX2*term;
+		  ysum[n][m] += tY2*term;
+		}
+		yterm *= tY1;
+	      }
+	      xterm *= tX1;
+	    }
+	  }
+	}
+      }
+      j = first_j;
+      i++;
+    }
+    
+    if (!last) { /* calculate polyterm coeffs */
+      fprintf (stderr, "matched %.0f stars for polyterms\n", sum[0][0]);
+      i = 0;
+      for (m = 0; m < NPOWR; m++) {
+	for (n = 0; n < NPOWR - m; n++, i++) {
+	  vector[i][0] = xsum[n][m];
+	  vector[i][1] = ysum[n][m];
+	}	
+      }
+      j = 0;
+      for (M = 0; M < NPOWR; M++) {
+	for (N = 0; N < NPOWR - M; N++, j++) {
+	  i = 0;
+	  for (m = 0; m < NPOWR; m++) {
+	    for (n = 0; n < NPOWR - m; n++, i++) {
+	      matrix[i][j] = sum[n+N][m+M];
+	    }	
+	  }
+	}
+      }       
+      max = 0.0;
+      for (i = 0; i < NPARS; i++) {
+	for (j = 0; j < NPARS; j++) {
+	  max = MAX (max, fabs(matrix[i][j]));
+	}
+	max = MAX (max, fabs(vector[i][0]));
+	max = MAX (max, fabs(vector[i][1]));
+      }
+      for (i = 0; i < NPARS; i++) {
+	for (j = 0; j < NPARS; j++) {
+	  matrix[i][j] /= max;
+	}
+	vector[i][0] /= max;
+	vector[i][1] /= max;
+      }
+      /* svd (matrix, NPARS, vector, 2);  */
+      dgaussjordan (matrix, vector, NPARS, 2); 
+      i = 0;
+      for (m = 0; m < NPOWR; m++) {
+	for (n = 0; n < NPOWR - m; n++, i++) {
+	  xsum[n][m] = vector[i][0];
+	  ysum[n][m] = vector[i][1];
+	}	
+      }
+      i = 0;
+      for (m = 0; m < NPOWR; m++) {
+	for (n = 0; n < NPOWR - m; n++, i++) {
+	  fprintf (stderr, "RA x^%dy^%d: %10.4g    DEC x^%dy^%d: %10.4g \n", 
+		   n, m, vector[i][0], n, m, vector[i][1]);
+	}	
+      }
+    } else {
+      fprintf (stderr, "%d stars matched for residuals\n", N);
+      
+      dX = sqrt(d2X/N - dX*dX/(N*N));  /* scatter in pixels in the X direction */
+      dY = sqrt(d2Y/N - dY*dY/(N*N));  /* scatter in pixels in the Y direction */
+      if (VERBOSE) fprintf (stderr, "scatter in pixels: %5.2f x %5.2f -- %5.2f\n", dX, dY, hypot(dX,dY) / sqrt(N));
+      *DR = sqrt (SQ(dX*coords[0].cdelt1*3600.0) + SQ(dY*coords[0].cdelt1*3600.0));
+      *Nmatch = N;
+
+    }
+  } 
+
+  /* convert new terms to adjustments in coords and to polyterms */
+  {
+    double S1, S2, p11, p12, p21, p22;
+    double a0, a1, a2, b0, b1, b2, det;
+    double X, Y;
+    int Np, Nv;
+    
+    S1 = coords[0].cdelt1;
+    S2 = coords[0].cdelt2;
+    p11 = coords[0].pc1_1;    p12 = coords[0].pc1_2;
+    p21 = coords[0].pc2_1;    p22 = coords[0].pc2_2;
+    
+    /* get the correct vector entries for the linear terms */
+    N = mk_vector (0, 0, NORDER);
+    a0 = vector[N][0];  b0 = vector[N][1];
+    N = mk_vector (1, 0, NORDER);
+    a1 = vector[N][0];  b1 = vector[N][1];
+    N = mk_vector (0, 1, NORDER);
+    a2 = vector[N][0];  b2 = vector[N][1];
+
+    det = 1.0 / (a1*b2 - a2*b1);
+
+    coords[0].pc1_1 = p11*a1 + p12*b1*(S2/S1);
+    coords[0].pc2_1 = p21*a1 + p22*b1*(S2/S1);
+    
+    coords[0].pc1_2 = p12*b2 + p11*a2*(S1/S2);
+    coords[0].pc2_2 = p22*b2 + p21*a2*(S1/S2);
+    
+    X = (coords[0].crpix1 - a0);
+    Y = (coords[0].crpix2 - b0);
+    coords[0].crpix1 = det*(X*b2 - Y*a2);
+    coords[0].crpix2 = det*(Y*a1 - X*b1);
+
+    coords[0].Npolyterms = NORDER;
+    strcpy (coords[0].ctype, "DEC--PLY");
+
+    /* generate higher order terms from vector */
+
+    for (i = 0; i < NORDER + 1; i++) {
+      for (j = 0; j < (NORDER - i + 1); j++) {
+	if (i + j < 2) continue;
+	Np = mk_polyterm (i, j, NORDER);
+	Nv = mk_vector (i, j, NORDER);
+	coords[0].polyterms[Np][0] = det*(vector[Nv][0]*b2  - vector[Nv][1]*a2);  /* x2 y0 */
+	coords[0].polyterms[Np][1] = det*(vector[Nv][1]*a1  - vector[Nv][0]*b1);  /* x2 y0 */
+      }
+    }
+  }
+
+  while (coords[0].crval1 < 0) coords[0].crval1 += 360.0;
+  while (coords[0].crval1 > 360.0) coords[0].crval1 -= 360.0;
+
+  {
+    double nominal_det, measure_det, min_det, max_det, tmp;
+    
+    nominal_det = CCD_PC1_1 * CCD_PC2_2 - CCD_PC1_2 * CCD_PC2_1;
+    min_det = nominal_det / 1.05;
+    max_det = nominal_det * 1.05;
+    if (min_det > max_det) {
+      tmp = max_det; max_det = min_det; min_det = tmp;
+    }
+    
+    measure_det = coords[0].pc1_1*coords[0].pc2_2 - coords[0].pc1_2*coords[0].pc2_1;
+    if ((measure_det > max_det) || (measure_det < min_det)) {
+      fprintf (stderr, "absurd solution, not cartesian: %f (%f - %f)\n", measure_det, max_det, min_det);
+      *DR = 1e9;
+    }
+  }
+}
+
+int mk_polyterm (int n, int m, int norder) {
+  
+  int i, nt, N;
+  
+  N = 0;
+  nt = n + m;
+  for (i = 2; i < nt; i++) {
+    N += i + 1;
+  }
+  N += m;
+  return (N);
+}
+
+int mk_vector (int n, int m, int norder) {
+  
+  int i, N;
+  
+  N = 0;
+  for (i = 0; i < m; i++) {
+    N += (norder - i + 1);
+  }
+  N += n;
+  return (N);
+}
+
+
+
+/**********************
+
+  vector vs polyterms:
+
+  the variable 'vector' is similar to, but not exactly like polyterms. 
+  vector[i][j] provides coeffs for all of the x,y terms, 
+    polyterms[i][j] only provides coeffs for the terms or order > 2.
+
+  vector[i][j] and polyterms[i][j] also use a slightly different order:
+
+  vector[i][j] runs in this order:
+
+                   n     m  norder = 3
+  vector[0][0] * x^0 * y^0
+  vector[1][0] * x^1 * y^0
+  vector[2][0] * x^2 * y^0
+  vector[3][0] * x^3 * y^0
+  vector[4][0] * x^0 * y^1
+  vector[5][0] * x^1 * y^1
+  vector[6][0] * x^2 * y^1
+  vector[7][0] * x^0 * y^2
+  vector[8][0] * x^1 * y^2
+  vector[9][0] * x^0 * y^3
+
+  to generate the vector entry from n, m, norder:
+  N = 0;
+  for (i = 0; i < m; i++) {
+    N += (norder - i + 1);
+  }
+  N += n;
+
+  polyterms[i][j] runs in this order:
+
+                      n     m
+  polyterms[0][0] * x^2 * y^0 = vector[2][0]
+  polyterms[1][0] * x^1 * y^1 = vector[5][0]
+  polyterms[2][0] * x^0 * y^2 = vector[7][0]
+  polyterms[3][0] * x^3 * y^0 = vector[3][0]
+  polyterms[4][0] * x^2 * y^1 = vector[6][0]
+  polyterms[5][0] * x^1 * y^2 = vector[8][0]
+  polyterms[6][0] * x^0 * y^3 = vector[9][0]
+  
+  to generate the polyterms entry from n, m, norder:
+
+  N = 0;
+  nt = n + m;
+  for (i = 2; i < nt; i++) {
+    N += i + 1;
+  }
+  N += m;
+
+  */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gheader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gheader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gheader.c	(revision 22322)
@@ -0,0 +1,127 @@
+# include "gastro.h"
+
+void gheader (char *file, Coords coords, double dR, int Nmatch) {
+
+  Header header;
+  FILE *f, *g;
+  int i, oldsize, nbytes, status;
+  char line[1024];
+
+  if (!gfits_read_header (file, &header)) {
+    fprintf (stderr, "ERROR: can't find image file %s (3)\n", file);
+    exit(0);
+  }
+  oldsize = header.size;
+
+  /* validating the photcode name should be the job of DVO/addstar */
+  /* here we are only writing the selected photcode name to the header */
+  if (NEWPHOTCODE) {
+    gfits_modify (&header, "PHOTCODE", "%s", 1, PHOTCODE);
+  }    
+  
+  if (Nmatch < 2) {
+    gfits_modify (&header, "NASTRO", "%d", 1, 0);
+    gfits_modify (&header, "NASTRO", "%C", 1, "number of stars used for astrometry");
+    goto skipstuff;
+  }
+  
+  gfits_modify (&header, "NASTRO", "%d", 1, Nmatch);
+  gfits_modify (&header, "NASTRO", "%C", 1, "number of stars used for astrometry");
+
+  /*** use PutCoords to update header ***/
+  if (coords.Npolyterms > 1) {
+    gfits_modify (&header, "CTYPE1",   "%s",  1, "RA---PLY");
+    gfits_modify (&header, "CTYPE2",   "%s",  1, "DEC--PLY");
+  } else {
+    gfits_modify (&header, "CTYPE1",   "%s",  1, "RA---TAN");
+    gfits_modify (&header, "CTYPE2",   "%s",  1, "DEC--TAN");
+  }    
+  gfits_modify (&header, "CDELT1",   "%le", 1, coords.cdelt1); 
+  gfits_modify (&header, "CDELT2",   "%le", 1, coords.cdelt2);
+  gfits_modify (&header, "CRVAL1",   "%lf", 1, coords.crval1);
+  gfits_modify (&header, "CRVAL2",   "%lf", 1, coords.crval2);  
+  gfits_modify (&header, "CRPIX1",   "%lf", 1, coords.crpix1);
+  gfits_modify (&header, "CRPIX2",   "%lf", 1, coords.crpix2);
+  gfits_modify (&header, "PC001001", "%le", 1, coords.pc1_1);
+  gfits_modify (&header, "PC001002", "%le", 1, coords.pc1_2);
+  gfits_modify (&header, "PC002001", "%le", 1, coords.pc2_1);
+  gfits_modify (&header, "PC002002", "%le", 1, coords.pc2_2);
+  gfits_modify (&header, "NPLYTERM", "%d", 1, coords.Npolyterms);
+  if (coords.Npolyterms > 1) {
+    /* RA Terms */
+    gfits_modify (&header, "PCA1X2Y0", "%le", 1, coords.polyterms[0][0]);   /* polyterms[0]); */
+    gfits_modify (&header, "PCA1X1Y1", "%le", 1, coords.polyterms[1][0]);   /* polyterms[1]); */
+    gfits_modify (&header, "PCA1X0Y2", "%le", 1, coords.polyterms[2][0]);   /* polyterms[2]); */
+
+    if (coords.Npolyterms > 2) {
+      gfits_modify (&header, "PCA1X3Y0", "%le", 1, coords.polyterms[3][0]);   /* polyterms[3]); */
+      gfits_modify (&header, "PCA1X2Y1", "%le", 1, coords.polyterms[4][0]);   /* polyterms[4]); */
+      gfits_modify (&header, "PCA1X1Y2", "%le", 1, coords.polyterms[5][0]);   /* polyterms[5]); */
+      gfits_modify (&header, "PCA1X0Y3", "%le", 1, coords.polyterms[6][0]);   /* polyterms[6]); */
+    }
+    /* Dec Terms */
+    gfits_modify (&header, "PCA2X2Y0", "%le", 1, coords.polyterms[0][1]);   /* polyterms[7]); */
+    gfits_modify (&header, "PCA2X1Y1", "%le", 1, coords.polyterms[1][1]);   /* polyterms[8]); */
+    gfits_modify (&header, "PCA2X0Y2", "%le", 1, coords.polyterms[2][1]);   /* polyterms[9]); */
+
+    if (coords.Npolyterms > 2) {
+      gfits_modify (&header, "PCA2X3Y0", "%le", 1, coords.polyterms[3][1]);   /* polyterms[10]); */
+      gfits_modify (&header, "PCA2X2Y1", "%le", 1, coords.polyterms[4][1]);   /* polyterms[11]); */
+      gfits_modify (&header, "PCA2X1Y2", "%le", 1, coords.polyterms[5][1]);   /* polyterms[12]); */
+      gfits_modify (&header, "PCA2X0Y3", "%le", 1, coords.polyterms[6][1]);   /* polyterms[13]); */
+    }
+  }
+  gfits_modify (&header, "CERROR", "%lf", 1, dR);
+  gfits_modify (&header, "CERROR", "%C", 1, "scatter in astrometry soln (arcsec)");
+  gfits_modify (&header, "CPRECISE", "%lf", 1, dR / sqrt(1.0*Nmatch));
+  gfits_modify (&header, "CPRECISE", "%C", 1, "precision of astrometry soln (arcsec)");
+  gfits_modify (&header, "EQUINOX", "%lf", 1, 2000.0);
+  /* we force equinox to be 2000.0 for all images */
+
+skipstuff:
+  if (header.size > oldsize) {
+    if (VERBOSE) fprintf (stderr, "header expanded, creating new copy\n");
+    sprintf (line, "mv %s %s~", file, file);
+    status = system (line);
+    if (status) {
+      fprintf (stderr, "ERROR: unable to create %s~, exiting\n", file);
+      exit (0);
+    }
+    sprintf (line, "%s~", file);
+    f = fopen (line, "r");
+    g = fopen (file, "w");
+    if (f == NULL) {
+      fprintf (stderr, "ERROR: can't find image file %s (4)\n", line);
+      exit(0);
+    }
+    if (g == NULL) {
+      fprintf (stderr, "ERROR: can't open output image file %s (4)\n", file);
+      exit(0);
+    }
+    nbytes = fwrite (header.buffer, 1, header.size, g);
+    fseek (f, oldsize, SEEK_SET);
+    for (i = 0; (nbytes = fread (header.buffer, 1, header.size, f)) > 0; i++) {
+      if (nbytes != fwrite (header.buffer, 1, nbytes, g)) {
+	fprintf (stderr, "ERROR: failure writing output data file\n");
+	exit (0);
+      }
+    }
+    fclose (f);
+    fclose (g);
+  } else {
+    f = fopen (file, "r+");
+    if (f == NULL) {
+      fprintf (stderr, "ERROR: can't find image file %s (4)\n", file);
+      exit(0);
+    }
+    
+    fseek (f, 0, SEEK_SET);
+    nbytes = fwrite (header.buffer, 1, header.size, f);
+    
+    fclose (f);
+  }
+  free (header.buffer);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gproject.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gproject.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gproject.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include "gastro.h"
+# define MMIN 2
+# define dM 0.5
+# define NMBIN 64
+
+void gproject (SStars *catalog, SStars **stars, int Ncat, int *Nstars, Coords *coords, int NX, int NY, double dNdM, int N1) {
+  
+  double mbin[NMBIN], ratio;
+  int i, j, NSTARS, nstar, nstar2;
+  double X, Y, m0, Mmin, Mmax;
+  int XMIN, XMAX, YMIN, YMAX;
+  SStars *tstars, *t2stars;
+
+  NSTARS = Ncat;
+  ALLOCATE (tstars, SStars, NSTARS);
+
+  XMIN = 0.5*(1.0 - NFIELD)*NX;
+  XMAX = 0.5*(1.0 + NFIELD)*NX;
+  YMIN = 0.5*(1.0 - NFIELD)*NY;
+  YMAX = 0.5*(1.0 + NFIELD)*NY;
+  
+  /* project to local coords, select stars within region */
+  for (nstar = i = 0; i < Ncat; i++) {
+    RD_to_XY (&X, &Y, catalog[i].X, catalog[i].Y, coords);
+    if ((X > XMIN) && (X < XMAX) && (Y > YMIN) && (Y < YMAX)) {
+      tstars[nstar].X = X;
+      tstars[nstar].Y = Y;
+      tstars[nstar].mag = catalog[i].mag;
+      nstar++;
+      if (CATDUMP) {
+	fprintf (stdout, "%f %f %f %f %f\n", catalog[i].X, catalog[i].Y, X, Y, catalog[i].mag);
+      }
+    }
+  }
+  if (CATDUMP) {
+    exit (0);
+  }
+
+  REALLOCATE (tstars, SStars, nstar);
+  if (VERBOSE) fprintf (stderr, "%d total reference stars\n", nstar);
+  
+  /* find appropriate magnitude range */ 
+  ratio = NFIELD * NFIELD;
+  m0 = 0;
+  bzero (mbin, NMBIN * sizeof (double));
+  for (i = 0; i < nstar; i++) {
+    if (tstars[i].mag < MMIN) {
+      fprintf (stderr, "%d %f %f %f\n", i, tstars[i].X, tstars[i].Y, tstars[i].mag);
+    }
+    j = (tstars[i].mag - MMIN) / dM;
+    j = MIN (MAX (j, 0), (NMBIN - 1));
+    mbin[j] ++;
+  }
+  for (i = 0; i < NMBIN; i++) {
+    if (ratio * dNdM < mbin[i] / dM) {
+      m0 = (i - 1) * dM + MMIN;
+      break;
+    }
+  }
+  
+  Mmin = m0 - 1.0;
+  Mmax = m0 + MAX (N1/dNdM, 1) + 1.0;
+  if (m0 == 0) {
+    Mmin = 0;
+    Mmax = 32;
+  }
+  if (VERBOSE) fprintf (stderr, "choosing magnitude range for reference stars: ");
+  if (VERBOSE) fprintf (stderr, " %5.2f to %5.2f mags\n", Mmin, Mmax);
+
+
+  ALLOCATE (t2stars, SStars, nstar);
+  if (MAGLIMS) {
+    /* make cut on mags */
+    for (nstar2 = i = 0; i < nstar; i++) {
+      if ((tstars[i].mag > Mmin) && (tstars[i].mag < Mmax)) { 
+	t2stars[nstar2] = tstars[i];
+	nstar2 ++;
+      }
+    }
+  } else {
+    bcopy (tstars, t2stars, nstar*sizeof(SStars));
+    nstar2 = nstar;
+  }
+    
+
+  free (tstars);
+  *stars = t2stars;
+  *Nstars = nstar2;
+  if (VERBOSE) fprintf (stderr, "%d reference stars in mag range\n\n", nstar2);
+
+}
+
+
+/* in this routine, 
+   catalog[i].X,Y are RA,DEC in dec degrees
+   stars[i].X.Y   are projected coords in approx pixels on image
+   */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/granges.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/granges.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/granges.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "gastro.h"
+
+void granges (SStars *stars1, SStars *stars2, int N1, int N2, int NPIX, double *gx, double *gy, double *gx0, double *gy0) {
+
+  int i;
+  double maxX1, minX1, maxY1, minY1;
+  double maxX2, minX2, maxY2, minY2;
+  double Xzero, Xrange, Yzero, Yrange;
+
+  maxX1 = minX1 = stars1[0].X;
+  maxY1 = minY1 = stars1[0].Y;
+  for (i = 0; i < N1; i++) {
+    maxX1 = MAX (maxX1, stars1[i].X);    
+    minX1 = MIN (minX1, stars1[i].X);    
+    maxY1 = MAX (maxY1, stars1[i].Y);    
+    minY1 = MIN (minY1, stars1[i].Y);    
+  }
+
+  maxX2 = minX2 = stars2[0].X;
+  maxY2 = minY2 = stars2[0].Y;
+  for (i = 0; i < N2; i++) {
+    maxX2 = MAX (maxX2, (stars2[i].X));    
+    minX2 = MIN (minX2, (stars2[i].X));    
+    maxY2 = MAX (maxY2, (stars2[i].Y));    
+    minY2 = MIN (minY2, (stars2[i].Y));    
+  }
+
+  Xzero = minX1 - maxX2;
+  Yzero = minY1 - maxY2;
+  Xrange = ((maxX1 - minX1) + (maxX2 - minX2));
+  Yrange = ((maxY1 - minY1) + (maxY2 - minY2));
+  
+  *gx = (NPIX - 1.0) / Xrange;
+  *gy = (NPIX - 1.0) / Yrange;
+  *gx0  = (1.0 - NPIX)*Xzero/Xrange;
+  *gy0  = (1.0 - NPIX)*Yzero/Yrange;
+
+  fprintf (stderr, "gx, gy: %f %f\n", *gx, *gy);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/greference.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/greference.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/greference.c	(revision 22322)
@@ -0,0 +1,119 @@
+# include "gastro.h"
+
+# define USNO 1
+# define GSC  0
+
+int greference (SStars **cat, int *Ncat, Coords *coords, int NX, int NY) {
+
+  int  i;
+  CatStats catstats;
+
+  if (VERBOSE) fprintf (stderr, "\nloading astrometric reference data from %s\n", REFCAT); 
+
+  *Ncat = 0;
+
+  define_region (&catstats, coords, NX, NY);
+  ALLOCATE (*cat, SStars, 1);
+
+  /* get stars from the USNO catalog for the given region */
+  if (!strcmp (REFCAT, "USNO") || !strcmp (REFCAT, "BOTH")) {
+    while (catstats.RA[0] > 360.0) catstats.RA[0] -= 360.0;
+    while (catstats.RA[0] <   0.0) catstats.RA[0] += 360.0;
+    while (catstats.RA[1] > 360.0) catstats.RA[1] -= 360.0;
+    while (catstats.RA[1] <   0.0) catstats.RA[1] += 360.0;
+
+    /* if RA crosses 0,360 boundary, do 2 passes */
+    if (catstats.RA[0] > catstats.RA[1]) {
+      int Nusno1, Nusno2;
+      USNOdata *usno1, *usno2;
+      USNOstats usnostats;
+      CatStats substats;
+
+      substats = catstats;
+      substats.RA[0] = 0.0;
+      usno1 = getusno (&usnostats, &substats, &Nusno1);
+
+      substats = catstats;
+      substats.RA[1] = 360.0;
+      usno2 = getusno (&usnostats, &substats, &Nusno2);
+
+      REALLOCATE (*cat, SStars, MAX (Nusno1 + Nusno2, 1));
+      for (i = 0; i < Nusno1; i++) {
+	cat[0][i].X = usno1[i].R;
+	cat[0][i].Y = usno1[i].D;
+	cat[0][i].mag = fabs(usno1[i].r);
+      }      
+      for (i = Nusno1; i < Nusno2; i++) {
+	cat[0][i].X = usno2[i].R;
+	cat[0][i].Y = usno2[i].D;
+	cat[0][i].mag = fabs(usno2[i].r);
+      }      
+      *Ncat = Nusno1 + Nusno2;
+      free (usno1);
+      free (usno2);
+    } else {
+      int Nusno;
+      USNOdata *usno;
+      USNOstats usnostats;
+
+      usno = getusno (&usnostats, &catstats, &Nusno);
+
+      REALLOCATE (*cat, SStars, MAX (Nusno, 1));
+      for (i = 0; i < Nusno; i++) {
+	cat[0][i].X = usno[i].R;
+	cat[0][i].Y = usno[i].D;
+	cat[0][i].mag = fabs(usno[i].r);
+      }      
+      *Ncat = Nusno;
+      free (usno);
+    }
+    if (VERBOSE) fprintf (stderr, "%d stars from USNO 1.0\n", *Ncat);
+  }
+
+  if (!strcmp (REFCAT, "GSC") || !strcmp (REFCAT, "BOTH")) {
+    int j, Ngsc;
+    SStars *gsc;
+
+    gsc = getgsc (&catstats, &Ngsc);
+    REALLOCATE (*cat, SStars, MAX (Ngsc + *Ncat, 1));
+    for (j = *Ncat, i = 0; i < Ngsc; i++) {
+      cat[0][j] = gsc[i];
+    }
+    if (VERBOSE) fprintf (stderr, "%d stars from HST GSC\n", Ngsc);
+    *Ncat += Ngsc;
+  }
+
+  if (!strcmp (REFCAT, "PTOLEMY")) {
+    free (*cat);
+    *cat = getptolemy (&catstats, Ncat);
+    if (VERBOSE) fprintf (stderr, "%d stars from PTOLEMY\n", *Ncat);
+  }
+  return (TRUE);
+}
+
+void define_region (CatStats *catstats, Coords *coords, int NX, int NY) {
+   
+  int i, j;
+  double X, Y, R, D;
+
+  catstats[0].RA[0] = catstats[0].DEC[0] =  360.0;
+  catstats[0].RA[1] = catstats[0].DEC[1] = -360.0;
+
+  for (i = -1; i < 2; i++) {
+    for (j = -1; j < 2; j++) {
+      X = 0.5*(1.0 + i*NFIELD)*NX;
+      Y = 0.5*(1.0 + j*NFIELD)*NY;
+      XY_to_RD (&R, &D, X, Y, coords);
+      /* coords returns a region all in same phase 
+	 while (R < 0.0)    R += 360.0;
+	 while (R >= 360.0) R -= 360.0;
+      */
+      catstats[0].RA[0]  = MIN (catstats[0].RA[0], R);
+      catstats[0].RA[1]  = MAX (catstats[0].RA[1], R);
+      catstats[0].DEC[0] = MIN (catstats[0].DEC[0], D);
+      catstats[0].DEC[1] = MAX (catstats[0].DEC[1], D);
+    }
+  }
+  fprintf (stderr, "full region: %f - %f, %f - %f\n", 
+	   catstats[0].RA[0], catstats[0].RA[1], catstats[0].DEC[0], catstats[0].DEC[1]);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/gstars.c	(revision 22322)
@@ -0,0 +1,232 @@
+# include "gastro.h"
+# define dcos(a) (cos((double)((a)*(RAD_DEG))))
+# define dsin(a) (sin((double)((a)*(RAD_DEG))))
+
+/* by necesity hard wired */
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+# include <sys/time.h>
+# include <time.h>
+
+void sort_stars_mag (SStars *stars, int N) {
+
+# define SWAPFUNC(A,B){ SStars tmp; tmp = stars[A]; stars[A] = stars[B]; stars[B] = tmp; }
+# define COMPARE(A,B)(stars[A].mag < stars[B].mag)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+SStars *gstars (char *file, int *NSTARS, Coords *coords, int *NX, int *NY, double *dNdM) {
+
+  char line[64], side[64];
+  Header header, theader;
+  FILE *f;
+  int j, Ninstar, nstar, rnumber, N, Nstars, nbytes, Nbytes;
+  SStars *stars;
+  char *buffer;
+  double X, Y, T1, T2, T3, type, csign;
+  double PD, PR, DE, RE;
+  double ra, dec, dmag;
+
+  /* read in image header, open image data region */
+  if (!gfits_read_header (file, &header)) {
+    fprintf (stderr, "ERROR: can't find image file %s (1)\n", file);
+    exit(0);
+  }
+  /* get complete info from header */
+  gfits_scan (&header, "NAXIS1", "%d", 1, NX);
+  gfits_scan (&header, "NAXIS2", "%d", 1, NY);
+
+  /* attempt to get detailed astrometric information from header */
+  if (!strcasecmp (ROUGH_ASTROMETRY, "header")) {
+    if (!HEADER[0]) {
+      GetCoords (coords, &header);
+    } else {
+      gfits_read_header (HEADER, &theader);
+      GetCoords (coords, &theader);
+    }
+    if (!strcmp (coords[0].ctype, "NONE") || (coords[0].cdelt1 == 0) ||  (coords[0].cdelt2 == 0)) {
+      fprintf (stderr, "header coordinates incomplete, trying for rough coordinates\n");
+      strcpy (ROUGH_ASTROMETRY, "config");
+    } else {
+      if (FLIPX) {
+	coords[0].pc1_1 *= -1;
+	coords[0].crpix1 = *NX - coords[0].crpix1;
+      }
+      if (FLIPY) {
+	coords[0].pc2_2 *= -1;
+	coords[0].crpix2 = *NY - coords[0].crpix2;
+      }
+      ASEC_PIX = fabs (coords[0].cdelt1 * 3600.0);
+      csign = coords[0].cdelt1 / fabs (coords[0].cdelt1);
+      CCD_PC1_1 = coords[0].pc1_1 * csign;
+      CCD_PC2_1 = coords[0].pc2_1 * csign;
+      csign = coords[0].cdelt2 / fabs (coords[0].cdelt2);
+      CCD_PC1_2 = coords[0].pc1_2 * csign;
+      CCD_PC2_2 = coords[0].pc2_2 * csign;
+      if (!strcmp (&coords[0].ctype[4], "-PLY")) {
+	strcpy (coords[0].ctype, "DEC--TAN");
+      }
+    }
+  }
+  
+  /* get just RA & DEC from header, other terms from config file */
+  if (!strcasecmp (ROUGH_ASTROMETRY, "config")) {
+    /* default values for coords */
+    strcpy (coords[0].ctype, "RA---TAN");
+    coords[0].pc1_1 = CCD_PC1_1; coords[0].pc1_2 = CCD_PC1_2;
+    coords[0].pc2_1 = CCD_PC2_1; coords[0].pc2_2 = CCD_PC2_2;
+    coords[0].cdelt1 = coords[0].cdelt2 = ASEC_PIX / 3600.0;
+    coords[0].Npolyterms = 0;
+    coords[0].crpix1 = 0.5*(*NX);
+    coords[0].crpix2 = 0.5*(*NY);
+
+    /* get RA & DEC from header, unless FORCE is ste */
+    if (!FORCE) {
+      /* RA (in hours, not degrees) */
+      if (!gfits_scan (&header, "RA", "%s", 1, line)) {
+	fprintf (stderr, "ERROR: no astrometry in header\n");
+	exit (1);
+      }
+      ohana_dms_to_ddd (&coords[0].crval1, line);
+      coords[0].crval1 = coords[0].crval1 * 15.0;
+
+      /* DEC */
+      if (!gfits_scan (&header, "DEC", "%s", 1, line)) {
+	fprintf (stderr, "ERROR: no astrometry in header\n");
+	exit (1);
+      } 
+      ohana_dms_to_ddd (&coords[0].crval2, line);
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "coordinates from header: %9.4f %9.4f\n", coords[0].crval1, coords[0].crval2);
+
+  /* use RA & DEC from command line arguments */
+  if (FORCE) {
+    coords[0].crval1 = F_RA;
+    coords[0].crval2 = F_DEC;
+    if (VERBOSE) fprintf (stderr, " forcing coordinates to: %9.4f %9.4f\n", coords[0].crval1, coords[0].crval2);
+  }    
+
+  /* the following two sections are LONEOS derived and may not be needed elsewhere */
+  if (LONEOS_COORDS) {
+    gfits_scan (&header, "COMMENT", "%s", 1, line);
+    sscanf (line, "%*s%d%s", &rnumber, side);
+    if (get_region_coords (&ra, &dec, rnumber, side)) {
+      if (fabs(ra - coords[0].crval1) > 0.1) {
+	fprintf (stderr, "large offset from claimed position, using region coords %f %f -> %f %f (%d %s)\n", 
+		 coords[0].crval1, coords[0].crval2, ra, dec, rnumber, side);
+	coords[0].crval1 = ra;
+	coords[0].crval2 = dec;
+      }
+    }
+  }
+
+  /* at this point, we need to correct the crval1, crval2, and ROT_ZERO values
+     based on the pole axis angle and the ra, dec offsets */
+  if (POLAR_ALIGNMENT) {
+    X = coords[0].crval1;
+    Y = coords[0].crval2;
+    PD = POLE_DEC;  PR = POLE_RA;
+    DE = DEC_OFFSET; RE = RA_OFFSET;
+    
+    T1 = dcos(Y-DE) * dcos(X-RE) * dsin(PD) + dsin(Y-DE) * dcos(PD);
+    T2 = dcos(Y-DE) * dsin(X-RE);
+    T3 = dsin(Y-DE) * dsin(PD) - dcos(Y-DE) * dcos(X-RE) * dcos(PD);
+    
+    coords[0].crval1 = (DEG_RAD * atan2 (T2, T1)) + PR;
+    coords[0].crval2 = (DEG_RAD * asin (T3));
+    while (coords[0].crval1 < 0) coords[0].crval1 += 360.0;
+    while (coords[0].crval1 > 360.0) coords[0].crval1 -= 360.0;
+    
+    if (VERBOSE) fprintf (stderr, "  after polar alignment: %9.4f %9.4f\n", coords[0].crval1, coords[0].crval2);
+  }
+
+  Nstars = 0;
+  gfits_scan (&header, "NSTARS", "%d", 1, &Nstars);
+  if (Nstars == -1) {
+    fprintf (stderr, "ERROR: failed to find NSTARS\n");
+    exit (0);
+  }
+  ALLOCATE (stars, SStars, Nstars);
+  Nbytes = Nstars*BYTES_STAR;
+
+  /* re-open file for stars */
+  f = fopen (file, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't find image file %s (2)\n", file);
+    exit(0);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  N = nstar = 0;
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR));
+  
+  while ((nbytes = fread (buffer, 1, (BLOCK*BYTES_STAR), f)) != 0) {
+    Ninstar = nbytes / BYTES_STAR;
+    for (j = 0; j < Ninstar; j++, nstar++) {
+      dparse (&stars[N].X,   1, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Y,   2, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].mag, 3, &buffer[j*BYTES_STAR]);
+      dparse (&dmag, 4, &buffer[j*BYTES_STAR]);
+      dparse (&type,         5, &buffer[j*BYTES_STAR]);
+      if ((type == 4) || (type == 6) || (type == 5) || (type == 9)) continue;
+      if (dmag > 100) continue;
+      N++;
+    }
+  }
+  free (header.buffer);
+  free (buffer);
+  fclose (f);
+ 
+  if (nstar != Nstars) {
+    fprintf (stderr, "WARNING: only read %d of %d stars\n", nstar, Nstars);
+  }
+  if (N < 5) { 
+    fprintf (stderr, "ERROR: too few stars for reliable solution, only %d\n",
+	     N);
+    exit (0);
+  }
+
+  sort_stars_mag (stars, N);  /* sorting by magnitude */
+  Nstars = N;
+
+  if (VERBOSE) fprintf (stderr, "\nread %d stars from data file", Nstars);
+  if (*NSTARS < Nstars) {
+    REALLOCATE (stars, SStars, *NSTARS);
+    if (VERBOSE) fprintf (stderr, ", using %d\n", *NSTARS);
+  } else {
+    *NSTARS = Nstars;
+    if (VERBOSE) fprintf (stderr, "\n");
+  }
+
+  *dNdM = *NSTARS / (stars[(*NSTARS-1)].mag - stars[0].mag) ;
+  if (VERBOSE) fprintf (stderr, "brightest star in datafile: %f mag\n", stars[0].mag);
+  
+  return (stars);
+}
+
+# if (0)
+
+  /***  this is tailored for LONEOS ***/
+  Nccd = -1;
+  gfits_scan (&header, "NCCD", "%d", 1, &Nccd);
+  if (Nccd == -1) {  /* no ccd info in header, not loneos */
+    coords[0].crpix1 = 0.5*(*NX);
+    coords[0].crpix2 = 0.5*(*NY);
+  }
+  if (Nccd == 0) {  /* chip 0 (a) *** might be wrong *** */
+    coords[0].crpix1 = 0;
+    coords[0].crpix2 = 0.5*(*NY);
+  }
+  if (Nccd == 1) {  /* chip 1 (b) */
+    coords[0].crpix1 = (*NX);
+    coords[0].crpix2 = 0.5*(*NY);
+  }
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/misc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/misc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/misc.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "gastro.h"
+
+# define SIGN(X)  (((X) == 0) ? 0 : ((fabs((double)(X))) / (X)))
+
+void hh_hms (double hh, int *hr, int *mn, double *sc) {
+
+  int flag;
+
+  flag = SIGN(hh);
+  hh *= flag;
+  hh = 24.0*(hh/24.0 - (int)(hh/24.0));
+  *sc = 60.0*(60.0*hh - (int)(60.0*hh));
+  *mn = 60.0*(hh - (int)hh);
+  *hr = (int) hh;
+  *hr *= flag;
+
+}
+ 
+void hms_format (char *line, double value) {
+
+  int hr, mn;
+  double sc;
+
+  hh_hms (value, &hr, &mn, &sc);
+  hr = (int) value;
+  if (isnan (value))
+    sprintf (line, "xx:xx:xx.xx");
+  else {
+    if (value < 0) {
+      sprintf (line, "-%02d:%02d:%05.2f", abs(hr), mn, sc);
+    } else {
+      sprintf (line, "%02d:%02d:%05.2f", hr, mn, sc);
+    }
+  }      
+}
+
+void area_of_region (CatStats *region) {
+  
+  double area;
+
+  area = DEG_RAD*(region[0].RA[1] - region[0].RA[0])*(sin(region[0].DEC[1]*RAD_DEG) - sin(region[0].DEC[0]*RAD_DEG));
+  region[0].Area = area;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/plotstuff.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/plotstuff.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/plotstuff.c	(revision 22322)
@@ -0,0 +1,104 @@
+# include "gastro.h"
+# include <signal.h>
+
+static int Xgraph[5] = {0,0,0,0,0};
+static int active;
+
+void XDead () {
+  signal (SIGPIPE, XDead);
+  fprintf (stderr, "kapa is dead, must restart\n");
+  Xgraph[active] = -1;
+}
+
+int open_graph (int N) {
+
+  char name[100];
+  
+  active = N;
+
+  sprintf (name, "gastro [%d]", N);
+  Xgraph[N] = KapaOpen ("kapa", name);
+
+  if (Xgraph[N] < 0) {
+    fprintf (stderr, "error starting kapa\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+void DonePlotting (Graphdata *graphmode, int N) {
+
+  if (Xgraph[N] == 0) return;
+  KapaBox (Xgraph[N], graphmode);
+  return;
+}
+
+void PrepPlotting (int Npts, Graphdata *graphmode, int N) {
+
+  if (Xgraph[N] == 0) return;
+
+  active = N;
+  if (Npts < 1) return;
+
+  KapaPrepPlot (Xgraph[N], Npts, graphmode);
+}
+
+void PlotVector (int Npts, float *vect, int mode, int N) {
+
+  if (Npts < 1) return;
+  active = N;
+
+  switch (mode) {
+    case 0:
+      KapaPlotVector (Xgraph[N], Npts, vect, "x");
+      break;
+    case 1:
+      KapaPlotVector (Xgraph[N], Npts, vect, "y");
+      break;
+    default:
+      abort();
+  }
+}
+
+void PlotReset (int N) {
+
+  char buffer[128];
+  int i;
+
+  /* test Xgraph[N], flush junk from pipe */
+  signal (SIGPIPE, XDead);
+  fcntl (Xgraph[N], F_SETFL,  O_NONBLOCK); 
+  for (i = 0; (read (Xgraph[N], buffer, 64) > 0) && (i < 20); i++);
+  fcntl (Xgraph[N], F_SETFL, !O_NONBLOCK); 
+  
+  if (Xgraph[N] < 1) if (!open_graph(N)) return;
+  KapaClearSections (Xgraph[N]);
+}
+
+/* include these lines to plot a pair of vectors: 
+
+   typedef struct {
+   double xmin, xmax, ymin, ymax;
+   int style, ptype, ltype, etype, color;
+   double lweight, size;
+   } Graphdata;
+   Graphdata graphdata;
+   
+   graphdata.xmin = -200;
+   graphdata.xmax = 4200;
+   graphdata.ymin = -500;
+   graphdata.ymax = 500;
+   graphdata.style = 2;
+   graphdata.ptype = 2;
+   graphdata.ltype = 0;
+   graphdata.etype = 0;
+   graphdata.color = 0;
+   graphdata.lweight = 0;
+   graphdata.size = 0.5;
+   
+   PrepPlotting (N, &graphdata, n);
+   PlotVector (N, Y, 0, n);
+   PlotVector (N, dM, 1, n);
+   DonePlotting (&graphdata, n);
+   
+ */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/rotate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/rotate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro/src/rotate.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "gastro.h"
+
+void rotate (SStars *stars, int Nstars, double angle, int Xo, int Yo) {
+  
+  int i;
+  double dX, dY, DX, DY, CS, SN;
+  double theta, theta2;
+
+  if (angle == 0.0) {
+    return;
+  }
+
+  theta = (angle*RAD_DEG);
+
+  if (fabs (angle) < 10) {
+    theta2 = 0.5*theta*theta;
+    for (i = 0; i < Nstars; i++) {
+      dX = (stars[i].X - Xo);
+      dY = (stars[i].Y - Yo);
+      stars[i].X += -theta*dY - theta2*dX;
+      stars[i].Y +=  theta*dX - theta2*dY;
+    }
+  } else {
+
+    CS = cos (theta);
+    SN = sin (theta);
+
+    for (i = 0; i < Nstars; i++) {
+      dX = (stars[i].X - Xo);
+      dY = (stars[i].Y - Yo);
+
+      DX = dX * CS - dY * SN;
+      DY = dX * SN + dY * CS;
+
+      stars[i].X = DX + Xo;
+      stars[i].Y = DY + Yo;
+    }
+  }
+    
+}
+
+
+  /* rotate the list 'stars' by an angle ccw from x axis */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/Makefile	(revision 22322)
@@ -0,0 +1,67 @@
+default: gastro2
+help:
+	@echo "make options: gastro2 (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/gastro2
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+gastro2: $(BIN)/gastro2.$(ARCH)
+coordtest: $(BIN)/coordtest.$(ARCH)
+extr2mass: $(BIN)/extr2mass.$(ARCH)
+install: $(DESTBIN)/gastro2
+
+EXTR2MASS = \
+$(SRC)/extr2mass.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/gregions2.$(ARCH).o
+
+COORDTEST = \
+$(SRC)/coordtest.$(ARCH).o \
+$(SRC)/gpairs.$(ARCH).o \
+$(SRC)/polyfit.$(ARCH).o
+
+GASTRO2 = \
+$(SRC)/plots.$(ARCH).o \
+$(SRC)/gheader2.$(ARCH).o \
+$(SRC)/gfit2.$(ARCH).o \
+$(SRC)/gpairs.$(ARCH).o \
+$(SRC)/polyfit.$(ARCH).o \
+$(SRC)/plotstuff.$(ARCH).o \
+$(SRC)/rotate2.$(ARCH).o \
+$(SRC)/gcenter2.$(ARCH).o \
+$(SRC)/gproject2.$(ARCH).o \
+$(SRC)/grid.$(ARCH).o \
+$(SRC)/lumfunc.$(ARCH).o \
+$(SRC)/sort.$(ARCH).o \
+$(SRC)/misc.$(ARCH).o \
+$(SRC)/gargs.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/gastro2.$(ARCH).o \
+$(SRC)/gstars2.$(ARCH).o \
+$(SRC)/greference2.$(ARCH).o \
+$(SRC)/getusno.$(ARCH).o \
+$(SRC)/getusnob.$(ARCH).o \
+$(SRC)/getptolemy.$(ARCH).o \
+$(SRC)/getgsc.$(ARCH).o \
+$(SRC)/remove_clumps.$(ARCH).o \
+$(SRC)/rfits.$(ARCH).o \
+$(SRC)/rtext.$(ARCH).o
+
+$(GASTRO2): $(INC)/gastro2.h
+$(COORDTEST): $(INC)/gastro2.h
+$(EXTR2MASS): $(INC)/gastro2.h
+
+$(BIN)/gastro2.$(ARCH): $(GASTRO2)
+$(BIN)/coordtest.$(ARCH): $(COORDTEST)
+$(BIN)/extr2mass.$(ARCH): $(EXTR2MASS)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,20 @@
+
+ gastro-2-3 2006.10.04
+  * converted to gfits APIs (forces libfits 1.6)
+  * converted to new DVO APIs (forces libdvo 1.3)
+  * removed unused code
+  * better error checks in ConfigInit
+  * fixed luminosity function matching
+  * added ASCA mode
+  * added mode to measure ptolemy fill-factor
+  * cleaned up polynomial fitting / header conversions
+  * cleaned up fitting code
+  * added error test for two few / nan fits
+
+ gastro-2-2:
+    added USNO-B
+
+ gastro-2-1:
+    added support for dvo table modes / formats (libdvo v1.0)
+    minor changes to sync with libfits modes (libfits v1.4)
+    added support for both fits and text input data files
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/include/gastro2.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/include/gastro2.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/include/gastro2.h	(revision 22322)
@@ -0,0 +1,202 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <kapa.h>
+
+typedef struct {
+  double R, D;
+  double P, Q;
+  double X, Y;
+  double M, dM;
+  int type;
+} StarData;
+
+typedef struct {
+  double dNdM;
+  double Mo;
+  double Mmin;
+  double Mmax;
+  double Mz;
+} LumStats;
+
+typedef struct {
+  double angle;
+  double Xoff;
+  double Yoff;
+  double Chi;
+  double dR;
+  int    N;
+} Answer;
+
+typedef struct {
+  Header header;   /* cmp file header */
+  LumStats lum;
+  Coords coords;   /* current best guess for astrometry */
+  Answer answer;
+
+  double Area;
+  StarData *stars; /* array with all star data */
+  int N;           /* number of stars */
+} CmpCatalog;
+
+typedef struct {
+  LumStats lum;
+
+  double Area;
+  double Moff;
+  double R0, R1;
+  double D0, D1;
+  int N;           /* number of stars */
+  
+  StarData *stars; /* array with all star data */
+} RefCatalog;
+
+typedef struct {
+  double RA[2], DEC[2];
+  double Area;
+  char *name;
+} CatStats;
+
+typedef struct {
+  double R, D;
+  double r, b;
+} USNOdata;
+
+/* global variables, from ConfigInit or args */
+double DEFAULT_RADIUS;
+double MINIMUM_RADIUS;
+double MAX_ERROR, MAX_NONLINEAR;
+double MIN_PRECISE;
+double CCD_PC1_1;
+double CCD_PC2_2;
+double CCD_PC1_2;
+double CCD_PC2_1;
+double NFIELD;
+double SEARCH_RADIUS;
+double ROT_ZERO;
+double dROT;
+double RA_OFFSET, DEC_OFFSET;
+double POLE_RA, POLE_DEC;
+int POLAR_ALIGNMENT;
+int NROT;
+int VERBOSE;
+int LONEOS_COORDS;
+int CATDUMP;
+int MATCHDUMP;
+int NOMATCHDUMP;
+int NEWPHOTCODE;
+int MIN_MATCHES;
+char *PHOTCODE;
+int FLIPX, FLIPY;
+int NPOLYTERMS;
+char CATDIR[256];
+char CATMODE[16];    /* raw, mef, split, mysql */
+char CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char REFCAT[256];
+char HEADER[256];
+int PLOTSTUFF;
+int MAGLIMS;
+int NMAX_STARS;
+char PhotCodeFile[256];
+int GASTRO_MAX_NSTARS;
+int TEXTMODE;
+int PTOLEMY_FILL_FACTOR;
+int MAGMANUAL;
+double MAGLIM_MIN;
+double MAGLIM_MAX;
+int NGRID_PIX;
+
+int    ASCA;
+int    FORCE;
+double F_RA;
+double F_DEC;
+
+double ASEC_PIX;
+char ROUGH_ASTROMETRY[64];
+
+/* locations for reference data */
+char GSCFILE[256];
+char GSCDIR[256];
+char CATDIR[256];
+char USNO_A_DIR[256];
+char USNO_B_DIR[256];
+char TWO_MASS_DIR[256];
+char ASTROM_CATDIR[256];
+char LONEOS_REGION_FILE[256];
+
+StarData *rtext (FILE *f, int *nstars);
+StarData *rfits (FILE *f, int *nstars);
+
+StarData *remove_clumps (StarData *instars, int *nstars, int NX, int NY);
+void 	  ConfigInit (int *argc, char **argv);
+void 	  DonePlotting (Graphdata *graphmode, int N);
+void 	  PlotReset (int N);
+void 	  PlotVector (int Npts, float *vect, int mode, int N);
+void 	  PrepPlotting (int Npts, Graphdata *graphmode, int N);
+void 	  XDead ();
+void 	  add_to_regions (CatStats *area);
+void 	  ahelp ();
+void 	  area_of_region (CatStats *region);
+double    area_of_skyregion (SkyRegion *region);
+void 	  args (int *argc, char **argv, Coords *coords);
+void 	  define_region (CatStats *catstats, CmpCatalog *Target);
+int 	  dms_to_ddd (double *Value, char *string);
+void 	  dump_coords (CmpCatalog *Target);
+void 	  fill_lumfunc (StarData *stars, int N, float *lbin, float *bin, int *nb);
+int 	  find_dec_bands (CatStats *area);
+void 	  fit_add (double x1, double y1, double x2, double y2, double wt);
+int 	  fit_adjust (Coords *coords);
+void 	  fit_apply (double *x, double *y, double X, double Y);
+void 	  fit_eval ();
+void 	  fit_init (int order);
+void 	  fit_lum_bin (double *x, double *y, int N, double *C0, double *C1);
+void 	  fit_norm (); 
+double    fit_scat (StarData *st, StarData *sr, Coords *coords);
+int 	  gaussj (double **a, int n, double **b, int m);
+void 	  gcenter (CmpCatalog *Target, RefCatalog *Ref);
+int 	  get_luminosity_func (StarData *stars, int N, LumStats *lum);
+int 	  getptolemy (CatStats *catstats, RefCatalog *Ref);
+int 	  getusno (CatStats *catstats, RefCatalog *Ref);
+int 	  getusnob (CatStats *catstats, RefCatalog *Ref, double epoch);
+int       getgsc (CatStats *catstats, RefCatalog *Ref);
+
+void 	  gfit (CmpCatalog *Target, RefCatalog *Ref, int order);
+void 	  gheader (char *file, CmpCatalog *Target);
+void 	  gproject (CmpCatalog *Target, RefCatalog *Ref, RefCatalog *Subset);
+void 	  greference (CmpCatalog *Target, RefCatalog *Ref);
+void 	  grid (CmpCatalog *Target, RefCatalog *Subset, Answer *answer);
+int 	  gridbin (double dX, double dY);
+void 	  gridfree ();
+void 	  gridinit (double XMIN, double XMAX, double YMIN, double YMAX, int Nr, int Nt);
+void 	  gstars (char *filename, CmpCatalog *Target);
+void 	  hh_hms (double hh, int *hr, int *mn, double *sc);
+void 	  hms_format (char *line, double value);
+void 	  init_regions ();
+int 	  load_ra_blocks (int Ndec, CatStats *area);
+int 	  mk_polyterm (int n, int m, int norder);
+int 	  mk_vector (int n, int m, int norder);
+int 	  open_graph (int N);
+
+void 	  pair_add (int i1, int i2);
+void 	  pair_init ();
+int       pair_lists (int **index1, int **index2);
+
+int 	  parse_GSC_line (CatStats *tregion, char *line);
+int 	  plot_addpt_gridplot (double x, double y);
+void 	  plot_done_gridplot ();
+void 	  plot_fullfield (CmpCatalog *Target, RefCatalog *Ref);
+void 	  plot_fullfield_pairs (float *x, float *y, int n);
+void 	  plot_gridpts (double *pts, int Npts);
+void 	  plot_init_gridplot ();
+void 	  plot_lumfunc (CmpCatalog *Target, RefCatalog *Ref);
+void 	  plot_resid (StarData *st, StarData *sr, Coords *coords);
+void 	  plot_resid_init (int version, double xmax);
+void 	  plot_resid_plot (int version, float *xvect, float *yvect, int Nvect);
+
+void 	  rotate (RefCatalog *Subset, RefCatalog *Ref, double angle);
+void 	  set_catalog (char *catdir);
+void 	  sort (double *X, int N);
+void 	  sort_lists (double *X, double *Y, int *S, int N);
+void 	  sort_lum (double *R, double *X, double *Y, int N);
+void 	  sort_stars_X (StarData *stars, int N);
+void 	  sort_stars_mag (StarData *stars, int N);
+int 	  str_to_radec (double *ra, double *dec, char *str1, char *str2);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "gastro2.h"
+
+void ConfigInit (int *argc, char **argv) {
+  
+  char *config, *file;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  /* default values for config variables: used if key is missing from the config file */
+  strcpy (ROUGH_ASTROMETRY, "header");
+
+  ScanConfig (config, "CCD_PC1_1",         "%lf", 0, &CCD_PC1_1);       // guess if WCS is missing
+  ScanConfig (config, "CCD_PC2_2",         "%lf", 0, &CCD_PC2_2);       // guess if WCS is missing
+  ScanConfig (config, "CCD_PC1_2",         "%lf", 0, &CCD_PC1_2);       // guess if WCS is missing
+  ScanConfig (config, "CCD_PC2_1",         "%lf", 0, &CCD_PC2_1);       // guess if WCS is missing
+  ScanConfig (config, "ASEC_PIX",          "%lf", 0, &ASEC_PIX);        // guess if WCS is missing
+  ScanConfig (config, "NFIELD",            "%lf", 0, &NFIELD);          // search region *padding* in field units
+  ScanConfig (config, "NGRID_PIX",         "%d",  0, &NGRID_PIX);       // resolution of grid search (pixels)
+  ScanConfig (config, "NPOLYTERMS",        "%d",  0, &NPOLYTERMS);      // high-order fit terms (2 or 3)
+  ScanConfig (config, "ROT_ZERO",          "%lf", 0, &ROT_ZERO);        // rotation search region
+  ScanConfig (config, "dROT",              "%lf", 0, &dROT);            // rotation search region
+  ScanConfig (config, "NROT",              "%d",  0, &NROT);            // rotation search region
+  ScanConfig (config, "POLAR_ALIGNMENT",   "%d",  0, &POLAR_ALIGNMENT); // apply polar alignment correction
+  ScanConfig (config, "POLAR_AXIS_RA",     "%lf", 0, &POLE_RA);         // true coords of pole (should be HA, not RA)
+  ScanConfig (config, "POLAR_AXIS_DEC",    "%lf", 0, &POLE_DEC);        // true coords of pole
+  ScanConfig (config, "RA_OFFSET",         "%lf", 0, &RA_OFFSET);       // ?? not well defined (should be euler angle)
+  ScanConfig (config, "DEC_OFFSET",        "%lf", 0, &DEC_OFFSET);      // ?? not well defined (should be euler angle)
+
+  /* possible sources of astrometric reference data */
+  if (!ScanConfig (config, "USNO_A_DIR",             "%s",  0, USNO_A_DIR)) {  // location of USNO A data (USNO_CDROM in gastro)
+    ScanConfig (config, "USNO_CDROM",             "%s",  0, USNO_A_DIR);  // alternate location of USNO A data
+  }
+  ScanConfig (config, "USNO_B_DIR",        "%s",  0, USNO_B_DIR);       // location of USNO B ref data
+  ScanConfig (config, "GSCDIR",            "%s",  0, GSCDIR);           // location of HST GSC ref data 
+  ScanConfig (config, "2MASS_DIR",         "%s",  0, TWO_MASS_DIR);  	// location of 2MASS ref data 
+  ScanConfig (config, "ASTROM_CATDIR",     "%s",  0, ASTROM_CATDIR); 	// location of ptolemy-format ref data
+
+  ScanConfig (config, "GSCFILE",           "%s",  0, GSCFILE);          // location of sky table
+  ScanConfig (config, "ASTRO_REFCAT",      "%s",  0, REFCAT);           // which astrometry catalog to use
+  ScanConfig (config, "ROUGH_ASTROMETRY",  "%s",  0, ROUGH_ASTROMETRY); // where to get initial guess (header, config)
+  ScanConfig (config, "GASTRO_MAX_NSTARS", "%d",  0, &GASTRO_MAX_NSTARS); // max number of stars from image to fit
+  ScanConfig (config, "GASTRO_MAX_MAG_ERROR", "%lf", 0, &MAX_ERROR);    // S/N limit on image stars used in fit
+
+  ScanConfig (config, "PHOTCODE_FILE",     "%s",  0, PhotCodeFile);  // not used
+
+  if (NFIELD <= 0.0) {
+      fprintf (stderr, "NFIELD is not sensible: choose a non-zero number\n");
+      exit (1);
+  }
+
+  if (!GASTRO_MAX_NSTARS) GASTRO_MAX_NSTARS = 300;
+  if (!MAX_ERROR) MAX_ERROR = 0.2;
+  if (!NGRID_PIX) NGRID_PIX = 50.0;
+  if (!NFIELD) NFIELD = 0.1;
+  
+  if (strcasecmp (ROUGH_ASTROMETRY, "header") && 
+      strcasecmp (ROUGH_ASTROMETRY, "config")) {
+    fprintf (stderr, "ROUGH_ASTROMETRY must be one of: header, config\n");
+    exit (1);
+  }
+  free (config);
+  free (file);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/coordtest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/coordtest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/coordtest.c	(revision 22322)
@@ -0,0 +1,142 @@
+# include "gastro2.h"
+
+# define A00 +500.00
+# define A10 +4.00
+# define A01 +1.00
+# define A20 -0.001
+# define A11 +0.000
+# define A02 -0.001
+# define A30 +0.00002
+# define A21 +0.00001
+# define A12 -0.00001
+# define A03 -0.00002
+
+# define B00 400.00
+# define B10  -1.00
+# define B01  4.00
+# define B20 -0.001
+# define B11  0.000
+# define B02 -0.001
+# define B30 -0.00002
+# define B21 -0.00001
+# define B12 +0.00001
+# define B03 +0.00002
+
+int main (int argc, char **argv) {
+
+  /* generate a set of fake data and send to fitter */
+  int i, N, order;
+  double x, y, z;
+  double L[50000], M[50000], X[50000], Y[50000];
+  Coords coords;
+  FILE *f;
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: coordtest (order)\n");
+    exit (2);
+  }
+
+  order = atoi (argv[1]);
+  fit_init (order);
+  N = 0;
+
+  switch (order) {
+    case 1:
+      for (x = -10.0; x < 10.1; x += 0.1) {
+	for (y = -10.0; y < 10.1; y += 0.1) {
+      
+	  L[N] = A00 + A10*x + A01*y;
+	  M[N] = B00 + B10*x + B01*y;
+	  X[N] = x;
+	  Y[N] = y;
+	  fit_add (x, y, L[N], M[N], 1.0);
+	  N++;
+	}
+      }
+      break;
+    case 2:
+      for (x = -10.0; x < 10.1; x += 0.1) {
+	for (y = -10.0; y < 10.1; y += 0.1) {
+      
+	  L[N] = A00 + A10*x + A01*y + A20*x*x + A11*x*y + A02*y*y;
+	  M[N] = B00 + B10*x + B01*y + B20*x*x + B11*x*y + B02*y*y;
+	  X[N] = x;
+	  Y[N] = y;
+	  fit_add (x, y, L[N], M[N], 1.0);
+	  N++;
+	}
+      }
+      break;
+    case 3:
+      for (x = -10.0; x < 10.1; x += 0.1) {
+	for (y = -10.0; y < 10.1; y += 0.1) {
+      
+	  L[N] = A00 + A10*x + A01*y + A20*x*x + A11*x*y + A02*y*y + A30*x*x*x + A21*x*x*y + A12*x*y*y + A03*y*y*y;
+	  M[N] = B00 + B10*x + B01*y + B20*x*x + B11*x*y + B02*y*y + B30*x*x*x + B21*x*x*y + B12*x*y*y + B03*y*y*y;
+	  X[N] = x;
+	  Y[N] = y;
+	  fit_add (x, y, L[N], M[N], 1.0);
+	  N++;
+	}
+      }
+      break;
+  }
+
+  fit_eval ();
+
+  strcpy (coords.ctype, "RA---PLY");
+  coords.crval1 = 0.0;
+  coords.crval2 = 0.0;
+
+  fit_adjust (&coords);
+
+  fprintf (stderr, "CTYPE: %s\n", coords.ctype);
+  fprintf (stderr, "CRVAL: %f %f\n", coords.crval1, coords.crval2);
+  fprintf (stderr, "CRPIX: %f %f\n", coords.crpix1, coords.crpix2);
+  fprintf (stderr, "CDELT: %f %f\n", coords.cdelt1, coords.cdelt2);
+  fprintf (stderr, "PC1_j: %f %f\n", coords.pc1_1,  coords.pc1_2);
+  fprintf (stderr, "PC2_j: %f %f\n", coords.pc2_1,  coords.pc2_2);
+
+  fprintf (stderr, "Npolyterms: %d\n", coords.Npolyterms);
+  for (i = 0; i < 7; i++) {
+    fprintf (stderr, "%f %f\n", coords.polyterms[i][0], coords.polyterms[i][1]);
+  }
+
+  f = fopen ("test.dat", "w");
+  {
+    double Lo, Mo, dL, dL2, dM, dM2;
+    double Xo, Yo, dX, dX2, dY, dY2;
+    double Lx, Mx;
+
+    dL = dL2 = dM = dM2 = 0.0;
+    dX = dX2 = dY = dY2 = 0.0;
+    for (i = 0; i < N; i++) {
+      XY_to_RD (&Lo, &Mo, X[i], Y[i], &coords);
+      dL  += (L[i] - Lo);
+      dM  += (M[i] - Mo);
+      dL2 += SQ(L[i] - Lo);
+      dM2 += SQ(M[i] - Mo);
+
+      RD_to_XY (&Xo, &Yo, L[i], M[i], &coords);
+      dX  += (X[i] - Xo);
+      dY  += (Y[i] - Yo);
+      dX2 += SQ(X[i] - Xo);
+      dY2 += SQ(Y[i] - Yo);
+
+      // fit_apply (&Lx, &Mx, X[i], Y[i]);
+      // fprintf (stderr, "%f,%f -> %f,%f | %f,%f : %f, %f\n", X[i], Y[i], Lx, Mx, L[i], M[i], Lo, Mo);
+
+      fprintf (f, "%f %f : %f %f :: %f %f : %f %f\n",
+	       X[i], Y[i], L[i], M[i], Lo, Mo, Xo, Yo);
+    }
+    fclose (f);
+
+    fprintf (stderr, "dL: %f\n", sqrt(fabs(dL2/N - SQ(dL/N))));
+    fprintf (stderr, "dM: %f\n", sqrt(fabs(dM2/N - SQ(dM/N))));
+    fprintf (stderr, "dX: %f\n", sqrt(fabs(dX2/N - SQ(dX/N))));
+    fprintf (stderr, "dY: %f\n", sqrt(fabs(dY2/N - SQ(dY/N))));
+  }
+
+  exit (0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gargs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gargs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gargs.c	(revision 22322)
@@ -0,0 +1,156 @@
+# include "gastro2.h"
+# define NARGS 2  /* minimum is:  gastro catalog */
+
+void ahelp () {
+
+  fprintf (stderr, "gastro -- astrometry for LONEOS\n");
+
+  fprintf (stderr, "  USAGE: gastro pixscale filename");
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -v (verbose mode)\n");
+  fprintf (stderr, "  -dump (dump catalog stars, don't complete astrometry)\n");
+  fprintf (stderr, "  -mdmp (dump matched catalog stars)\n");
+  fprintf (stderr, "\n"); 
+  exit (0);
+
+}
+
+void args (int *argc, char **argv, Coords *coords) {
+  
+  int N;
+
+  if (get_argument (*argc, argv, "-help") ||
+      get_argument (*argc, argv, "-h")) {
+    ahelp ();
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (*argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  /* force read of image database with mismatched NSTARS & size */ 
+  TEXTMODE = FALSE;
+  if ((N = get_argument (*argc, argv, "-textmode"))) {
+    TEXTMODE = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  /* force read of image database with mismatched NSTARS & size */ 
+  PTOLEMY_FILL_FACTOR = FALSE;
+  if ((N = get_argument (*argc, argv, "-ptolemy-fill-factor"))) {
+    PTOLEMY_FILL_FACTOR = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  PLOTSTUFF = FALSE;
+  if ((N = get_argument (*argc, argv, "-plot"))) {
+    PLOTSTUFF = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  MAGLIMS = TRUE;
+  if ((N = get_argument (*argc, argv, "-maglims"))) {
+    MAGLIMS = FALSE;
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-magrange"))) {
+    MAGLIMS = TRUE;
+    MAGMANUAL = TRUE;
+    remove_argument (N, argc, argv);
+    MAGLIM_MIN = atof (argv[N]);
+    remove_argument (N, argc, argv);
+    MAGLIM_MAX = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  NMAX_STARS = 300;
+  if ((N = get_argument (*argc, argv, "-nstars"))) {
+    remove_argument (N, argc, argv);
+    NMAX_STARS = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  HEADER[0] = 0;
+  if ((N = get_argument (*argc, argv, "-header"))) {
+    remove_argument (N, argc, argv);
+    strcpy (HEADER, argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  HEADER[0] = 0;
+  if ((N = get_argument (*argc, argv, "-head"))) {
+    remove_argument (N, argc, argv);
+    strcpy (HEADER, argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  FLIPX = FALSE;
+  if ((N = get_argument (*argc, argv, "-fx"))) {
+    FLIPX = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  FLIPY = FALSE;
+  if ((N = get_argument (*argc, argv, "-fy"))) {
+    FLIPY = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  FORCE = FALSE;
+  if ((N = get_argument (*argc, argv, "-coords"))) {
+    FORCE = TRUE;
+    remove_argument (N, argc, argv);
+    F_RA = atof (argv[N]);
+    remove_argument (N, argc, argv);
+    F_DEC = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /** XXX temporary trick to deal with the very wide-field ASCA images 
+      this alters the definition of the reference field boundaries */
+  ASCA = FALSE;
+  if ((N = get_argument (*argc, argv, "-asca"))) {
+    ASCA = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  CATDUMP = FALSE;
+  if ((N = get_argument (*argc, argv, "-dump"))) {
+    CATDUMP = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  MATCHDUMP = FALSE;
+  if ((N = get_argument (*argc, argv, "-mdmp"))) {
+    MATCHDUMP = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  NOMATCHDUMP = FALSE;
+  if ((N = get_argument (*argc, argv, "-cdmp"))) {
+    NOMATCHDUMP = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  // XXX these options are being ignored
+  NEWPHOTCODE = FALSE;
+  if ((N = get_argument (*argc, argv, "-p"))) {
+    NEWPHOTCODE = TRUE;
+    remove_argument (N, argc, argv);
+    PHOTCODE = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  LONEOS_COORDS = FALSE;
+  if ((N = get_argument (*argc, argv, "-loneos"))) {
+    LONEOS_COORDS = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  if (*argc != NARGS) {
+    fprintf (stderr, "USAGE: gastro filename\n");
+    exit (0);
+  }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gastro2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gastro2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gastro2.c	(revision 22322)
@@ -0,0 +1,99 @@
+# include "gastro2.h"
+
+int main (int argc, char **argv) {
+
+  int i;
+  RefCatalog Ref;
+  CmpCatalog Target;
+
+  /* start_timer (); */
+
+  ConfigInit (&argc, argv);
+  args (&argc, argv, &Target.coords); 
+
+  gstars (argv[1], &Target);
+  greference (&Target, &Ref);
+  gcenter (&Target, &Ref);
+
+  for (i = 0; i < 3; i++) {
+    gfit (&Target, &Ref, 1);
+    fprintf (stderr, "precision: %f\n", Target.answer.dR / sqrt(Target.answer.N));
+    if ((Target.answer.N < 2) || isnan(Target.answer.dR)) {
+	fprintf (stderr, "ERROR: bad fit\n");
+	exit (1);
+    }
+    if (VERBOSE) {
+      fprintf (stderr, "%s\n", Target.coords.ctype);
+      fprintf (stderr, "%f %f\n", Target.coords.crval1, Target.coords.crval2);
+      fprintf (stderr, "%f %f\n", Target.coords.crpix1, Target.coords.crpix2);
+      fprintf (stderr, "%f %f\n", Target.coords.pc1_1,  Target.coords.pc1_2);
+      fprintf (stderr, "%f %f\n", Target.coords.pc2_1,  Target.coords.pc2_2);
+      fprintf (stderr, "%f %f\n", Target.coords.cdelt1, Target.coords.cdelt2);
+    }
+
+  }
+
+  for (i = 0; i < 5; i++) {
+    gfit (&Target, &Ref, MIN (MAX (1, NPOLYTERMS), 3));
+    fprintf (stderr, "precision: %f\n", Target.answer.dR / sqrt(Target.answer.N));
+    if ((Target.answer.N < 2) || isnan(Target.answer.dR)) {
+	fprintf (stderr, "ERROR: bad fit\n");
+	exit (1);
+    }
+  }
+
+  if (VERBOSE) {
+    fprintf (stderr, "%s\n", Target.coords.ctype);
+    fprintf (stderr, "%f %f\n", Target.coords.crval1, Target.coords.crval2);
+    fprintf (stderr, "%f %f\n", Target.coords.crpix1, Target.coords.crpix2);
+    fprintf (stderr, "%f %f\n", Target.coords.pc1_1,  Target.coords.pc1_2);
+    fprintf (stderr, "%f %f\n", Target.coords.pc2_1,  Target.coords.pc2_2);
+    fprintf (stderr, "%f %f\n", Target.coords.cdelt1, Target.coords.cdelt2);
+  }
+
+  gheader (argv[1], &Target);
+
+  /* print_timer (); */
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
+
+  /* 
+     load config & args 
+
+     load stars & header from cmp file
+     (this should include filtering based on CONFIG data:
+     limit number of stars, limit types, etc) 
+
+     identify and load reference catalog(s)
+     - load all catalogs available?
+     - keep ra, dec, mag
+
+     project catalog to initial guess
+
+     find simple x, y offset in limited number of rotations
+   
+     reload reference catalog if dx, dy large 
+
+     project to new guess
+
+     fit on star-by-star basis
+     - include weighting by dmag
+     - downweight by Nmatch to each star
+     - iterate a few times?
+
+     project to new guess (why is this not part of the routines?)
+
+     try a higher order fit
+
+     adjust header, re-write file
+
+     TODO:
+
+     1) downweight points by Nmatch
+     2) better criterion for success
+     3) better criterion for failure
+     4) polyterms are broken
+
+  */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gcenter2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gcenter2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gcenter2.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include "gastro2.h"
+
+void gcenter (CmpCatalog *Target, RefCatalog *Ref) {
+
+  int i, N, Imin;
+  double angle, ChiMin;
+  Answer *answer;
+  RefCatalog Subset;
+
+  gproject (Target, Ref, &Subset);
+  if (PLOTSTUFF) plot_fullfield (Target, &Subset);
+
+  fprintf (stderr, "Target: %d, Ref: %d\n", Target[0].N, Subset.N);
+  dump_coords (Target);
+
+  ALLOCATE (answer, Answer, 2*NROT + 1);
+
+  N = 0;
+  for (angle = ROT_ZERO-dROT*NROT; angle <= ROT_ZERO+dROT*NROT; angle += dROT, N++) {
+
+    if (N == 2*NROT + 1) {
+      fprintf (stderr, "ERROR in logic: Nanswer > 2*NROT+1 (%d, %d)\n", N, 2*NROT+1);
+      exit (1);
+    }
+    answer[N].angle = angle;
+    grid (Target, &Subset, &answer[N]);
+  }
+
+  Imin = 0;
+  ChiMin = answer[0].Chi;
+  for (i = 0; i < N; i++) {
+    if (answer[i].Chi < ChiMin) {
+      Imin = i;
+      ChiMin = answer[i].Chi;
+    }
+  }
+      
+  fprintf (stderr, "best solution: angle: %6.1f, (%6.1f,%6.1f) - %10.8f for %d pairs\n", 
+	   answer[Imin].angle, answer[Imin].Xoff, answer[Imin].Yoff, answer[Imin].Chi, answer[Imin].N);
+
+  Target[0].answer = answer[Imin];
+
+  /* adjust original coordinates for new center */
+  { 
+
+    double cs, sn;
+    double pc11, pc12, pc21, pc22;
+    double Xo, Yo;
+
+    cs = cos(RAD_DEG*answer[Imin].angle);  sn = sin(RAD_DEG*answer[Imin].angle);
+    
+    pc11 = Target[0].coords.pc1_1;
+    pc12 = Target[0].coords.pc1_2;
+    pc21 = Target[0].coords.pc2_1;
+    pc22 = Target[0].coords.pc2_2;
+
+    Target[0].coords.pc1_1 =  pc11*cs - pc21*sn;
+    Target[0].coords.pc1_2 =  pc11*sn + pc12*cs;
+    Target[0].coords.pc2_1 =  pc21*cs - pc22*sn;
+    Target[0].coords.pc2_2 =  pc21*sn + pc22*cs;
+    
+    Xo = Target[0].coords.crpix1;
+    Yo = Target[0].coords.crpix2;
+    Target[0].coords.crpix1 = answer[Imin].Xoff + cs*Xo - sn*Yo;
+    Target[0].coords.crpix2 = answer[Imin].Yoff + sn*Xo + cs*Yo;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getgsc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getgsc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getgsc.c	(revision 22322)
@@ -0,0 +1,95 @@
+# include "gastro2.h"
+
+StarData *rd_gsc (char *filename, int *Nstars);
+
+int getgsc (CatStats *catstats, RefCatalog *Ref) {
+  
+  int i, j, k, Ns, Ngsc; 
+  StarData *gsc;
+  SkyList *skylist;
+  SkyTable *sky;
+  SkyRegion patch;
+
+  Ref[0].N = 0;
+  Ref[0].Area = 0;
+  ALLOCATE (Ref[0].stars, StarData, 1);
+
+  patch.Rmin = catstats[0].RA[0];
+  patch.Rmax = catstats[0].RA[1];
+  patch.Dmin = catstats[0].DEC[0];
+  patch.Dmax = catstats[0].DEC[1];
+
+  /* load regions from GSC table, restrict to patch */
+  sky = SkyTableFromGSC (GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  SkyTableSetFilenames (sky, GSCDIR, "cpt");
+  skylist = SkyListByPatch (sky, -1, &patch);
+  
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    gsc = rd_gsc (skylist[0].filename[i], &Ngsc);
+
+    Ns = Ref[0].N;
+    Ref[0].N += Ngsc;
+    Ref[0].Area += area_of_skyregion (skylist[0].regions[i]);
+
+    REALLOCATE (Ref[0].stars, StarData, MAX (1, Ref[0].N));
+    for (k = Ns, j = 0; j < Ngsc; k++, j++) {
+      Ref[0].stars[k].R = gsc[j].R;
+      Ref[0].stars[k].D = gsc[j].D;
+      Ref[0].stars[k].M = gsc[j].M;
+    }      
+    free (gsc);
+  }
+  SkyTableFree (sky);
+  
+  Ref[0].R0    = catstats[0].RA[0];
+  Ref[0].R1    = catstats[0].RA[1];
+  Ref[0].D0    = catstats[0].DEC[0];
+  Ref[0].D1    = catstats[0].DEC[1];
+
+  /* calculate luminosity function of stars */
+  get_luminosity_func (Ref[0].stars, Ref[0].N, &Ref[0].lum);
+
+  if (VERBOSE) fprintf (stderr, "%d stars from HST GSC\n", Ref[0].N);
+  return (TRUE);
+}  
+
+# define BYTES_STAR 23
+# define BLOCK 1000
+StarData *rd_gsc (char *filename, int *Nstars) {
+  
+  StarData *stars;
+  int i, NSTAR, nstar, Nbytes, nbytes;
+  char *buffer;
+  FILE *f;
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't find catalog file %s\n", filename);
+    exit (1);
+  }
+  
+  nstar = 0;
+  NSTAR = 1000;
+  ALLOCATE (stars, StarData, NSTAR);
+
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR));
+  Nbytes = BLOCK*BYTES_STAR;
+
+  while ((nbytes = fread (buffer, 1, Nbytes, f)) > 0) {
+    for (i = 0; i < nbytes / BYTES_STAR; i++) {
+      dparse (&stars[nstar].R, 1, &buffer[i*BYTES_STAR]);
+      dparse (&stars[nstar].D, 2, &buffer[i*BYTES_STAR]);
+      dparse (&stars[nstar].M, 3, &buffer[i*BYTES_STAR]);
+      nstar++;
+      if (nstar == NSTAR) {
+	NSTAR += 1000;
+	REALLOCATE (stars, StarData, NSTAR);
+      }
+    }
+  }
+
+  free (buffer);
+
+  *Nstars = nstar;
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getptolemy.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getptolemy.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getptolemy.c	(revision 22322)
@@ -0,0 +1,115 @@
+# include "gastro2.h"
+double catalog_area (Average *average, int Naverage, SkyRegion *region);
+
+int getptolemy (CatStats *catstats, RefCatalog *Ref) {
+  
+  int i, j, k, Ns; 
+  double FracArea;
+  Catalog catalog;
+  SkyList *skylist;
+  SkyTable *sky;
+  SkyRegion patch;
+
+  Ref[0].N = 0;
+  Ref[0].Area = 0;
+  ALLOCATE (Ref[0].stars, StarData, 1);
+
+  patch.Rmin = catstats[0].RA[0];
+  patch.Rmax = catstats[0].RA[1];
+  patch.Dmin = catstats[0].DEC[0];
+  patch.Dmax = catstats[0].DEC[1];
+
+  /* load regions from GSC table, restrict to patch */
+  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  skylist = SkyListByPatch (sky, -1, &patch);
+  
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    // set the parameters which guide catalog open/load/create
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS;
+    catalog.Nsecfilt  = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    // Naves_disk == 0 implies an empty catalog file
+    // for only_match, skip empty catalogs
+    if (!catalog.Naves_disk) {
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // this measurement adjusts a DVO database for partial coverage
+    // XXX the correction is bogus if the sky density of the catalog 
+    // is too low (<< 100 stars per field)
+    FracArea = 1.0;
+    if (PTOLEMY_FILL_FACTOR) {
+      FracArea = catalog_area (catalog.average, catalog.Naverage, skylist[0].regions[i]);
+    } 
+
+    Ns = Ref[0].N;
+    Ref[0].N += catalog.Naverage;
+    Ref[0].Area += FracArea * area_of_skyregion (skylist[0].regions[i]);
+
+    REALLOCATE (Ref[0].stars, StarData, MAX (1, Ref[0].N));
+    for (k = Ns, j = 0; j < catalog.Naverage; k++, j++) {
+      Ref[0].stars[k].R = catalog.average[j].R;
+      Ref[0].stars[k].D = catalog.average[j].D;
+      Ref[0].stars[k].M = catalog.measure[catalog.average[j].measureOffset].M;
+    }      
+    dvo_catalog_free (&catalog);
+  }
+  
+  Ref[0].R0 = catstats[0].RA[0];
+  Ref[0].R1 = catstats[0].RA[1];
+  Ref[0].D0 = catstats[0].DEC[0];
+  Ref[0].D1 = catstats[0].DEC[1];
+
+  /* calculate luminosity function of stars */
+  get_luminosity_func (Ref[0].stars, Ref[0].N, &Ref[0].lum);
+
+  if (VERBOSE) fprintf (stderr, "%d stars from PTOLEMY\n", Ref[0].N);
+  return (TRUE);
+}  
+
+double catalog_area (Average *average, int Naverage, SkyRegion *region) {
+
+  int i, xb, yb, Nb;
+  int bin[10][10];
+  double frac, Rmin, Rmax, Dmin, Dmax, dR, dD;
+
+  Rmin = region[0].Rmin;
+  Rmax = region[0].Rmax;
+  Dmin = region[0].Dmin;
+  Dmax = region[0].Dmax;
+  dR = Rmax - Rmin;
+  dD = Dmax - Dmin;
+
+  for (xb = 0; xb < 10; xb++) {
+    for (yb = 0; yb < 10; yb++) {
+      bin[xb][yb] = 0;
+    }
+  }
+
+  for (i = 0; i < Naverage; i++) {
+    xb = MAX (MIN (0, 10 * (average[i].R - Rmin) / dR), 9);
+    yb = MAX (MIN (0, 10 * (average[i].D - Dmin) / dD), 9);
+    bin[xb][yb] ++;
+  }
+
+  Nb = 0;
+  for (xb = 0; xb < 10; xb++) {
+    for (yb = 0; yb < 10; yb++) {
+      if (bin[xb][yb]) Nb ++;
+    }
+  }
+
+  frac = Nb / 100.0;
+
+  return (frac);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getusno.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getusno.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getusno.c	(revision 22322)
@@ -0,0 +1,138 @@
+# include "gastro2.h"
+# define NZONE 24
+
+int SPDzone[] = {
+  0, 75, 450, 375, 1500, 1650, 300, 1425, 1725, 525, 1275, 225, 
+  675, 150, 600, 1575, 750, 975, 900, 1050, 1125, 1200, 825, 1350};
+
+int USNOdisk[] = {
+  1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10};
+
+int getusno (CatStats *catstats, RefCatalog *Ref) {
+
+  long int offset;
+  int i, bin, first, last, nitems, Nitems, Nbins;
+  float hours[100];
+  int start[100], number[100], *buffer, *buf;
+  char filename[128];
+  FILE *f;
+  double DEC1;
+  int iDEC0, iDEC1, iRA0, iRA1;
+  int spd, spd_start, spd_end, disk;
+  int NUSNO, Nusno;
+  StarData *stars;
+
+  /* identify ra & dec range of interest */
+  iRA0 = catstats[0].RA[0] * 360000.0;
+  iRA1 = catstats[0].RA[1] * 360000.0;
+  iDEC0 = (catstats[0].DEC[0] + 90.0) * 360000.0;
+  iDEC1 = (catstats[0].DEC[1] + 90.0) * 360000.0;
+  
+  /* data is organized in south-pole distance zones */
+  spd_start = (int)((catstats[0].DEC[0] + 90) / 7.5) * 75.0;
+  DEC1 = (catstats[0].DEC[1] + 90) / 7.5;
+  if (DEC1 > (int)(DEC1)) {
+    spd_end =   (int)(1 + (catstats[0].DEC[1] + 90) / 7.5) * 75.0;
+  } else {
+    spd_end =   (int)(0 + (catstats[0].DEC[1] + 90) / 7.5) * 75.0;
+  }
+
+  Nusno = 0;
+  NUSNO = 5000;
+  ALLOCATE (stars, StarData, NUSNO);
+
+  for (spd = spd_start; spd < spd_end; spd += 75) {
+    disk = -1;
+    for (i = 0; i < NZONE; i++) {
+      if (spd == SPDzone[i]) 
+	disk = USNOdisk[i];
+    }
+    if (disk < 0) {
+      fprintf (stderr, "ERROR: can't find USNO zone for spd %d\n",  spd);
+      exit (0);
+    }
+    
+    /* load accelerator file */
+    sprintf (filename, "%s/zone%04d.acc", USNO_A_DIR, spd); 
+    fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open accelerator file %s\n", filename);
+      exit (1);  
+    }
+    for (i = 0; fscanf (f, "%f %d %d", &hours[i], &start[i], &number[i]) != EOF; i++);
+    Nbins = i;
+    fclose (f);
+    
+    first = catstats[0].RA[0] / 3.75;
+    if ((catstats[0].RA[1] / 3.75) == (int) (catstats[0].RA[1] / 3.75)) 
+      last  = catstats[0].RA[1] / 3.75;
+    else 
+      last  = 1 + catstats[0].RA[1] / 3.75;
+
+    if ((first > Nbins) || (last > Nbins)) {
+      fprintf (stderr, "ERROR: RA out of range\n");
+      exit (1);
+    }
+    
+    /* open data file */
+    sprintf (filename, "%s/zone%04d.cat", USNO_A_DIR, spd);
+    fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file %s\n", filename);
+      exit (1);
+    }
+    /* advance file pointer to first slice */
+    offset = 3*sizeof(int)*(start[first] - 1);
+    fseek (f, offset, SEEK_SET);
+    /* on each loop, load data from an RA slice of the catalog */
+    for (bin = first; bin < last; bin++) {
+      Nitems = 3*number[bin];
+      ALLOCATE (buffer, int, Nitems);
+      nitems = Fread (buffer, sizeof(int), Nitems, f, "int");
+      if (nitems != Nitems) {
+	fprintf (stderr, "ERROR: failure reading data from file %s\n", filename);
+	exit (1);
+      }
+      buf = buffer;
+      /* print out data from slice within RA and DEC range */
+      for (i = 0; i < number[bin]; i++, buf+=3) {
+	if ((buf[0] > iRA0) && (buf[0] < iRA1) &&
+	    (buf[1] > iDEC0) && (buf[1] < iDEC1)) {
+	  stars[Nusno].R = buf[0]/360000.0;
+	  stars[Nusno].D = buf[1]/360000.0 - 90.0;
+	  /* note that this is the RED mag */
+	  stars[Nusno].M = fabs (0.1*(buf[2] - 1000*((int)(buf[2]/1000))));
+	  /* b = 0.1*((int)(buf[2] - 1000000*((int)(buf[2]/1000000))) / 1000); */
+	  Nusno ++;
+	  if (Nusno == NUSNO) {
+	    NUSNO += 5000;
+	    REALLOCATE (stars, StarData, NUSNO);
+	  }	  
+	}
+      }
+      free (buffer);
+    }
+    fclose (f);
+  }
+
+  area_of_region (catstats);
+
+  REALLOCATE (stars, StarData, MAX (1, Nusno));
+
+  Ref[0].stars = stars;
+  Ref[0].N     = Nusno;
+  Ref[0].R0    = catstats[0].RA[0];
+  Ref[0].R1    = catstats[0].RA[1];
+  Ref[0].D0    = catstats[0].DEC[0];
+  Ref[0].D1    = catstats[0].DEC[1];
+  Ref[0].Area  = catstats[0].Area;
+
+  get_luminosity_func (Ref[0].stars, Ref[0].N, &Ref[0].lum);
+  
+  if (VERBOSE) fprintf (stderr, "%d stars from USNO 1.0\n", Nusno);
+  return (TRUE);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getusnob.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getusnob.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/getusnob.c	(revision 22322)
@@ -0,0 +1,163 @@
+# include "gastro2.h"
+# define NBYTE   4
+# define NELEM  20
+
+int getusnob (CatStats *catstats, RefCatalog *Ref, double epoch) {
+
+  long int offset;
+  int i, bin, first, last, nitems, Nitems, Nbins;
+  float hours[100];
+  int start[100], number[100], *buffer, *buf;
+  char filename[128];
+  FILE *f;
+  double DEC1;
+  double uR, uD;
+  float mB1, mB2, mR1, mR2, mB, mR;
+  int iDEC0, iDEC1, iRA0, iRA1;
+  int spd, spd_start, spd_end;
+  int NUSNO, Nusno, Nstars;
+  StarData *stars;
+
+  /* identify ra & dec range of interest */
+  iRA0 = catstats[0].RA[0] * 360000.0;
+  iRA1 = catstats[0].RA[1] * 360000.0;
+  iDEC0 = (catstats[0].DEC[0] + 90.0) * 360000.0;
+  iDEC1 = (catstats[0].DEC[1] + 90.0) * 360000.0;
+  /* note that DEC is in SPD, while both have units to 0.01 degrees */
+  
+  /* data is organized in south-pole distance zones, 1 deg per direction, 0.1 deg per file */
+  spd_start = (int)(10*(catstats[0].DEC[0] + 90));
+  DEC1 = 10*(catstats[0].DEC[1] + 90);
+  if (DEC1 > (int)(DEC1)) {
+    spd_end =   (int)(1 + 10*(catstats[0].DEC[1] + 90));
+  } else {
+    spd_end =   (int)(0 + 10*(catstats[0].DEC[1] + 90));
+  }
+
+  Nusno = 0;
+  NUSNO = 5000;
+  ALLOCATE (stars, StarData, NUSNO);
+
+  for (spd = spd_start; spd < spd_end; spd ++) {
+    
+    /* load accelerator file */
+    sprintf (filename, "%s/%03d/b%04d.acc", USNO_B_DIR, (int)(spd/10), spd); 
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open accelerator file %s\n", filename);
+      exit (1);  
+    }
+    for (i = 0; fscanf (f, "%f %d %d", &hours[i], &start[i], &number[i]) != EOF; i++);
+    Nbins = i;
+    fclose (f);
+    
+    first = catstats[0].RA[0] / 3.75;
+    if ((catstats[0].RA[1] / 3.75) == (int) (catstats[0].RA[1] / 3.75)) 
+      last  = catstats[0].RA[1] / 3.75;
+    else 
+      last  = 1 + catstats[0].RA[1] / 3.75;
+
+    if ((first > Nbins) || (last > Nbins)) {
+      fprintf (stderr, "ERROR: RA out of range\n");
+      exit (1);
+    }
+    
+    /* open data file */
+    sprintf (filename, "%s/%03d/b%04d.cat", USNO_B_DIR, (int)(spd/10), spd); 
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file %s\n", filename);
+      exit (1);
+    }
+
+    /** USNO-B consists of 20 x 4byte (int) records **/
+    /* advance file pointer to first slice */
+    offset = NELEM*NBYTE*(start[first] - 1);
+    fseek (f, offset, SEEK_SET);
+
+    /* sum the number of stars in data segment of interest */
+    Nstars = 0;
+    for (bin = first; bin < last; bin++) {
+      Nstars += number[bin];
+    }
+    Nitems = NELEM*Nstars;  /* number of integer blocks; need to use Fread for byte-swapping read */
+
+    /* allocate space for stars in segment */
+    ALLOCATE (buffer, int, Nitems);
+    // data has the WRONG byte order?
+    // nitems = Fread (buffer, sizeof(int), Nitems, f, "int");
+    nitems = fread (buffer, sizeof(int), Nitems, f);
+    if (nitems != Nitems) {
+      fprintf (stderr, "ERROR: failure reading data from file %s\n", filename);
+      exit (1);
+    }
+
+    buf = buffer;
+    /* print out data from slice within RA and DEC range */
+    for (i = 0; i < Nstars; i++, buf += NELEM) {
+      if (buf[0] < iRA0) continue;
+      if (buf[0] > iRA1) continue;
+      if (buf[1] < iDEC0) continue;
+      if (buf[1] > iDEC1) continue;
+      
+      bzero (&stars[Nusno], sizeof(StarData));
+      stars[Nusno].R = buf[0]/360000.0;
+      stars[Nusno].D = buf[1]/360000.0 - 90.0;
+      
+      uR = (buf[2] % 10000);
+      uR = (uR - 5000.0) * 0.002 / 3600.0;
+      uD = ((buf[2] / 10000) % 10000);
+      uD = (uD - 5000.0) * 0.002 / 3600.0;
+
+      /* 1st blue mag */
+      mB1 = 0.01 * (buf[5] % 10000);
+      /* 1st blue mag */
+      mB2 = 0.01 * (buf[6] % 10000);
+      /* 1st blue mag */
+      mR1 = 0.01 * (buf[7] % 10000);
+      /* 1st blue mag */
+      mR2 = 0.01 * (buf[8] % 10000);
+
+      if (mB1 && mB2) {
+	mB = 0.5*(mB1 + mB2);
+      } else {
+	mB = (mB1) ? mB1 : mB2;
+      }
+
+      if (mR1 && mR2) {
+	mR = 0.5*(mR1 + mR2);
+      } else {
+	mR = (mR1) ? mR1 : mR2;
+      }
+      
+      stars[Nusno].M = mB;
+      stars[Nusno].R += uR*(epoch - 2000.0);
+      stars[Nusno].D += uD*(epoch - 2000.0);
+      Nusno ++;
+      CHECK_REALLOCATE (stars, StarData, NUSNO, Nusno, 5000);
+    }
+    free (buffer);
+    fclose (f);
+  }
+
+  area_of_region (catstats);
+
+  REALLOCATE (stars, StarData, MAX (1, Nusno));
+
+  Ref[0].stars = stars;
+  Ref[0].N     = Nusno;
+  Ref[0].R0    = catstats[0].RA[0];
+  Ref[0].R1    = catstats[0].RA[1];
+  Ref[0].D0    = catstats[0].DEC[0];
+  Ref[0].D1    = catstats[0].DEC[1];
+  Ref[0].Area  = catstats[0].Area;
+
+  get_luminosity_func (Ref[0].stars, Ref[0].N, &Ref[0].lum);
+  
+  if (VERBOSE) fprintf (stderr, "%d stars from USNO 1.0\n", Nusno);
+  return (TRUE);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gfit2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gfit2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gfit2.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "gastro2.h"
+
+void gfit (CmpCatalog *Target, RefCatalog *Ref, int order) {
+
+  int i, j, j0;
+  int Npair, *idx1, *idx2;
+  double Radius, Radius2;
+  double dX, dY, dR;
+  RefCatalog Subset;
+  StarData *st, *sr;
+
+  /* XXX why is this hardwired here? */
+  NFIELD = 0.1;
+  gproject (Target, Ref, &Subset);
+  if (PLOTSTUFF) plot_fullfield (Target, &Subset);
+
+  if (Subset.N < 3) {
+    fprintf (stderr, "ERROR: solution off target\n");
+    exit (1);
+  }
+
+  /* need the stars sorted in X */
+  sort_stars_X (Target[0].stars, Target[0].N);
+  sort_stars_X (Subset.stars, Subset.N);
+
+  Radius = MAX (2.5 * Target[0].answer.dR, 0.5);
+  Radius2 = Radius*Radius;
+
+  st = Target[0].stars;
+  sr = Subset.stars;
+
+  /* find the matched pairs of stars within the radius */
+  pair_init ();
+  for (i = j = 0; (i < Target[0].N) && (j < Subset.N);) {
+    /* get in right X range */
+    dX = st[i].X - sr[j].X;
+    if (dX < -Radius) {
+      i++;
+      continue;
+    }
+    if (dX > Radius) {
+      j++;
+      continue;
+    }
+
+    /* check for pairs in this X range */
+    j0 = j;
+    for (; (dX > -Radius) && (j < Subset.N); j++) {
+    
+      dX = st[i].X - sr[j].X;
+      dY = st[i].Y - sr[j].Y;
+
+      dR = dX*dX + dY*dY;
+      if (dR > Radius2) {
+	j++;
+	continue;
+      }
+      pair_add (i, j);
+    }
+    j = j0;
+    i ++;
+  }
+  
+  Npair = pair_lists (&idx1, &idx2);
+  /* find fit for matched pairs */
+  fit_init (order);
+  for (i = 0; i < Npair; i++) {
+    fit_add (st[idx1[i]].X, st[idx1[i]].Y, sr[idx2[i]].P, sr[idx2[i]].Q, 1.0);
+  }
+  fit_eval ();
+
+  /* XXX this is weak: the fit_scat call requires the coords from the fit_adjust call */
+  Target[0].answer.N  = fit_adjust (&Target[0].coords);
+  Target[0].answer.dR = fit_scat (st, sr, &Target[0].coords);
+
+  if (PLOTSTUFF) fprintf (stderr, "ploting resid (2)\n");
+  plot_resid_init (0, (double) Target[0].header.Naxis[0]);
+  // XXX test: plotting fit_apply and RD_to_XY results plot_resid_init (1, (double) Target[0].header.Naxis[1]);
+  plot_resid_init (1, (double) Target[0].header.Naxis[0]);
+  if (PLOTSTUFF) plot_resid (st, sr, &Target[0].coords);
+  free (idx1);
+  free (idx2);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gheader2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gheader2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gheader2.c	(revision 22322)
@@ -0,0 +1,106 @@
+# include "gastro2.h"
+
+void gheader (char *file, CmpCatalog *Target) {
+
+  double dR;
+  Header header;
+  FILE *f, *g;
+  int i, oldsize, nbytes, status;
+  char line[1024];
+
+  if (!gfits_read_header (file, &header)) {
+    fprintf (stderr, "ERROR: can't find image file %s (3)\n", file);
+    exit(0);
+  }
+  oldsize = header.size;
+
+  /* check for insufficient number of stars */
+  switch (Target[0].coords.Npolyterms) {
+    case 0:
+    case 1:
+      if (Target[0].answer.N < 6) {
+	gfits_modify (&header, "NASTRO", "%d", 1, 0);
+	gfits_modify (&header, "NASTRO", "%C", 1, "number of stars used for astrometry");
+	goto skipstuff;
+      }
+      break;
+    case 2:
+      if (Target[0].answer.N < 12) {
+	gfits_modify (&header, "NASTRO", "%d", 1, 0);
+	gfits_modify (&header, "NASTRO", "%C", 1, "number of stars used for astrometry");
+	goto skipstuff;
+      }
+      break;
+    case 3:
+      if (Target[0].answer.N < 20) {
+	gfits_modify (&header, "NASTRO", "%d", 1, 0);
+	gfits_modify (&header, "NASTRO", "%C", 1, "number of stars used for astrometry");
+	goto skipstuff;
+      }
+      break;
+    default:
+      fprintf (stderr, "invalid order\n");
+      exit (2);
+  }
+  
+  gfits_modify (&header, "NASTRO", "%d", 1, Target[0].answer.N);
+  gfits_modify (&header, "NASTRO", "%C", 1, "number of stars used for astrometry");
+
+  /*** use PutCoords to update header ***/
+  PutCoords (&Target[0].coords, &header);
+
+  dR = fabs (Target[0].answer.dR*Target[0].coords.cdelt1*3600.0);
+  gfits_modify (&header, "CERROR", "%lf", 1, dR);
+  gfits_modify (&header, "CERROR", "%C", 1, "scatter in astrometry soln (arcsec)");
+  gfits_modify (&header, "CPRECISE", "%lf", 1, dR / sqrt(1.0*Target[0].answer.N));
+  gfits_modify (&header, "CPRECISE", "%C", 1, "precision of astrometry soln (arcsec)");
+  gfits_modify (&header, "EQUINOX", "%lf", 1, 2000.0);
+  /* we force equinox to be 2000.0 for all images */
+
+skipstuff:
+  if (header.size > oldsize) {
+    if (VERBOSE) fprintf (stderr, "header expanded, creating new copy\n");
+    sprintf (line, "mv %s %s~", file, file);
+    status = system (line);
+    if (status) {
+      fprintf (stderr, "ERROR: unable to create %s~, exiting\n", file);
+      exit (0);
+    }
+    sprintf (line, "%s~", file);
+    f = fopen (line, "r");
+    g = fopen (file, "w");
+    if (f == NULL) {
+      fprintf (stderr, "ERROR: can't find image file %s (4)\n", line);
+      exit(0);
+    }
+    if (g == NULL) {
+      fprintf (stderr, "ERROR: can't open output image file %s (4)\n", file);
+      exit(0);
+    }
+    nbytes = fwrite (header.buffer, 1, header.size, g);
+    fseek (f, oldsize, SEEK_SET);
+    for (i = 0; (nbytes = fread (header.buffer, 1, header.size, f)) > 0; i++) {
+      if (nbytes != fwrite (header.buffer, 1, nbytes, g)) {
+	fprintf (stderr, "ERROR: failure writing output data file\n");
+	exit (0);
+      }
+    }
+    fclose (f);
+    fclose (g);
+  } else {
+    f = fopen (file, "r+");
+    if (f == NULL) {
+      fprintf (stderr, "ERROR: can't find image file %s (4)\n", file);
+      exit(0);
+    }
+    
+    fseek (f, 0, SEEK_SET);
+    nbytes = fwrite (header.buffer, 1, header.size, f);
+    
+    fclose (f);
+  }
+  free (header.buffer);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gpairs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gpairs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gpairs.c	(revision 22322)
@@ -0,0 +1,35 @@
+# include "gastro2.h"
+
+static int NPAIR, Npair;
+static int *idx1, *idx2;
+
+void pair_init () {
+
+  Npair = 0;
+  NPAIR = 100;
+  ALLOCATE (idx1, int, NPAIR);
+  ALLOCATE (idx2, int, NPAIR);
+
+}
+
+void pair_add (int i1, int i2) {
+
+  idx1[Npair] = i1;
+  idx2[Npair] = i2;
+
+  Npair ++;
+
+  if (Npair == NPAIR) {
+    NPAIR += 100;
+    REALLOCATE (idx1, int, NPAIR);
+    REALLOCATE (idx2, int, NPAIR);
+  }
+
+}
+
+int pair_lists (int **index1, int **index2) {
+
+  *index1 = idx1;
+  *index2 = idx2;
+  return (Npair);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gproject2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gproject2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gproject2.c	(revision 22322)
@@ -0,0 +1,135 @@
+# include "gastro2.h"
+
+void gproject (CmpCatalog *Target, RefCatalog *Ref, RefCatalog *Subset) {
+
+  int i, N;
+  double X, Y, P, Q, M, Moff;
+  double XMIN, XMAX, YMIN, YMAX, MMIN, MMAX;
+  Coords TPtoSky, FPtoTP, *coords;
+  StarData *in, *out;
+
+  Subset[0] = Ref[0];
+
+  ALLOCATE (Subset[0].stars, StarData, MAX (1, Ref[0].N));
+
+  in     = Ref[0].stars;
+  out    = Subset[0].stars;
+
+  coords = &Target[0].coords;
+
+  /* create Tangent Plane to Sky transformation from input coords */
+  strcpy (TPtoSky.ctype, coords[0].ctype);
+  TPtoSky.crval1 = coords[0].crval1;
+  TPtoSky.crval2 = coords[0].crval2;
+
+  TPtoSky.cdelt1 = TPtoSky.cdelt2 = 1;
+  TPtoSky.crpix1 = TPtoSky.crpix2 = 0;
+  TPtoSky.pc1_1 = TPtoSky.pc2_2 = 1;
+  TPtoSky.pc1_2 = TPtoSky.pc2_1 = 0;
+  TPtoSky.Npolyterms = 0;
+  for (i = 0; i < 7; i++) {
+    TPtoSky.polyterms[i][0] = 0;
+    TPtoSky.polyterms[i][1] = 0;
+  }
+
+  /* create Focal Plane to Tangent Plane transformation from input coords */
+  strcpy (FPtoTP.ctype, "FP---PLY");
+  FPtoTP.crval1 = FPtoTP.crval2 = 0;
+
+  FPtoTP.cdelt1 = coords[0].cdelt1;
+  FPtoTP.cdelt2 = coords[0].cdelt2;
+  FPtoTP.crpix1 = coords[0].crpix1;
+  FPtoTP.crpix2 = coords[0].crpix2;
+  FPtoTP.pc1_1  = coords[0].pc1_1;
+  FPtoTP.pc1_2  = coords[0].pc1_2;
+  FPtoTP.pc2_1  = coords[0].pc2_1;
+  FPtoTP.pc2_2  = coords[0].pc2_2;
+
+  FPtoTP.Npolyterms = coords[0].Npolyterms;
+  for (i = 0; i < 7; i++) {
+    FPtoTP.polyterms[i][0] = coords[0].polyterms[i][0];
+    FPtoTP.polyterms[i][1] = coords[0].polyterms[i][1];
+  }
+
+  Moff = Ref[0].Moff;
+
+  XMIN = -0.5*NFIELD*Target[0].header.Naxis[0];
+  XMAX =  0.5*NFIELD*Target[0].header.Naxis[0] + Target[0].header.Naxis[0];
+  YMIN = -0.5*NFIELD*Target[0].header.Naxis[1];
+  YMAX =  0.5*NFIELD*Target[0].header.Naxis[1] + Target[0].header.Naxis[1];
+ 
+  /* need to allow some leeway? use a fixed +/- 0.5 mag for now */
+  MMIN = MMAX = 0;
+  if (MAGLIMS && !MAGMANUAL) {
+      MMAX = Target[0].lum.Mmax + 1.0;
+      MMIN = Target[0].lum.Mmin - 1.0;
+
+      if (MMAX < Ref[0].lum.Mmin + Moff) 
+	  fprintf (stderr, "warning: reference catalog probably too faint:  %5.3f < %5.3f\n", Target[0].lum.Mmax, Ref[0].lum.Mmin + Moff);
+
+      if (MMIN > Ref[0].lum.Mmax + Moff) 
+	  fprintf (stderr, "warning: reference catalog probably too bright: %5.3f > %5.3f\n", Target[0].lum.Mmin, Ref[0].lum.Mmax + Moff);
+  }
+  if (MAGMANUAL) {
+      MMIN = MAGLIM_MIN;
+      MMAX = MAGLIM_MAX;
+      Moff = 0;
+  }
+
+  if (VERBOSE) fprintf (stderr, "limited reference stars to mag range %f - %f\n", MMIN - Moff, MMAX - Moff);
+
+  for (N = i = 0; i < Ref[0].N; i++) {
+    RD_to_XY (&X, &Y, in[i].R, in[i].D, coords);
+    M = in[i].M + Moff;
+
+    if (X < XMIN) continue;
+    if (X > XMAX) continue;
+    if (Y < YMIN) continue;
+    if (Y > YMAX) continue;
+
+    if (MAGLIMS) {
+      if (M < MMIN) continue;
+      if (M > MMAX) continue;
+    }
+
+    /* get tangent-plane coordinates as well */
+    RD_to_XY (&P, &Q, in[i].R, in[i].D, &TPtoSky);
+
+    out[N] = in[i];
+    out[N].X = X;
+    out[N].Y = Y;
+    out[N].P = P;
+    out[N].Q = Q;
+    out[N].M = M;
+    N++;
+  }
+
+  if (N < 3) {
+    fprintf (stderr, "ERROR: too few reference stars accepted\n");
+    exit (1);
+  }
+    
+  Subset[0].N = N;
+  sort_stars_mag (Subset[0].stars, N);
+  if (GASTRO_MAX_NSTARS && (GASTRO_MAX_NSTARS < Subset[0].N)) {
+    Subset[0].N = GASTRO_MAX_NSTARS;
+    REALLOCATE (Subset[0].stars, StarData, Subset[0].N);
+  }
+  if (VERBOSE) fprintf (stderr, "using %d stars from ref catalog\n", Subset[0].N);
+
+  REALLOCATE (Subset[0].stars, StarData, MAX (1, Subset[0].N));
+
+  /* get tangent-plane coords for target as well */
+  for (i = 0; i < Target[0].N; i++) {
+    XY_to_RD (&P, &Q, Target[0].stars[i].X, Target[0].stars[i].Y, &FPtoTP);
+    Target[0].stars[i].P = P;
+    Target[0].stars[i].Q = Q;
+  }
+
+}
+
+/* in this function, we convert the Ra & Dec coords to the rough X, Y coords
+   we also convert the magnitudes to the approximate system with Ref[0].Moff 
+   also, reduce the domain to those within X, Y, M limits 
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/greference2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/greference2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/greference2.c	(revision 22322)
@@ -0,0 +1,131 @@
+# include "gastro2.h"
+
+void greference (CmpCatalog *Target, RefCatalog *Ref) {
+
+  CatStats catstats;
+
+  if (VERBOSE) fprintf (stderr, "loading astrometric reference data from %s\n", REFCAT); 
+
+  define_region (&catstats, Target);
+
+  Ref[0].N = 0;
+  /* get stars from the USNO A catalog for the given region */
+  if (!strcasecmp (REFCAT, "USNO")) {
+    getusno (&catstats, Ref);
+    /* calculate Ref[0].Moff from Target & Ref dMdN, Mo */
+  }
+
+  /* get stars from the USNO B catalog for the given region */
+  if (!strcasecmp (REFCAT, "USNOB")) {
+    getusnob (&catstats, Ref, 2000.0);
+    /* calculate Ref[0].Moff from Target & Ref dMdN, Mo */
+  }
+
+  /* get stars from the HST GSC catalog for the given region */
+  if (!strcasecmp (REFCAT, "GSC")) {
+    getgsc (&catstats, Ref);
+  }
+  
+  /* get stars from 2MASS for the given region -- add PHOTCODE check? */
+  if (!strcasecmp (REFCAT, "2MASS")) {
+    strcpy (CATDIR, TWO_MASS_DIR);
+    getptolemy (&catstats, Ref);
+  }
+  
+  /* get stars from the DVO CATDIR for the given region */
+  if (!strcasecmp (REFCAT, "PTOLEMY")) {
+    strcpy (CATDIR, ASTROM_CATDIR);
+    getptolemy (&catstats, Ref);
+  }
+  
+  if (Ref[0].N == 0) {
+    fprintf (stderr, "no ref objs: %s\n", REFCAT);
+    exit (1);
+  }
+
+  {
+    double Mref, Mtar, logRho;
+
+    /* what is the offset between the two lines at the average magnitude? */
+    Mref = 0.5*(Ref[0].lum.Mmin + Ref[0].lum.Mmax);
+    logRho = Mref * Ref[0].lum.dNdM + Ref[0].lum.Mo - log10(Ref[0].Area);
+    Mtar = (logRho + log10(Target[0].Area) - Target[0].lum.Mo) / Target[0].lum.dNdM;
+
+    Ref[0].lum.Mz = Mref;
+    Target[0].lum.Mz = Mtar;
+    Ref[0].Moff = Target[0].lum.Mz - Ref[0].lum.Mz;
+    fprintf (stderr, "mag offset: %f (Areas: %f vs %f; log(Rho): %f @ %f mags)\n", Ref[0].Moff, Target[0].Area, Ref[0].Area, logRho, Mref);
+  }
+
+  if (PLOTSTUFF) plot_lumfunc (Target, Ref);
+
+}
+
+/* return RA, DEC bounds of the reigon of interest */  
+void define_region (CatStats *catstats, CmpCatalog *Target) {
+   
+  int NX, NY, status;
+  double x, y, X, Y, R, D, dX, dY, Xo, Yo;
+
+  NX = Target[0].header.Naxis[0];
+  NY = Target[0].header.Naxis[1];
+
+  dX = NX + NFIELD*NX;
+  dY = NY + NFIELD*NY;
+
+  Xo = -0.5*NFIELD*NX;
+  Yo = -0.5*NFIELD*NY;
+
+  if (ASCA) {
+      XY_to_RD (&R, &D, 0.5*NX, 0.5*NY, &Target[0].coords);
+      catstats[0].RA[0]  = R - 90.0;
+      catstats[0].RA[1]  = R + 90.0;
+      catstats[0].DEC[0] = MAX (-90.0, D - 90.0);
+      catstats[0].DEC[1] = MIN (+90.0, D + 90.0);
+      if (VERBOSE) fprintf (stderr, "asca region: %f - %f, %f - %f\n", 
+			    catstats[0].RA[0], catstats[0].RA[1], catstats[0].DEC[0], catstats[0].DEC[1]);
+      return;
+  }
+
+  catstats[0].RA[0] =  360.0;
+  catstats[0].RA[1] =    0.0;
+  catstats[0].DEC[0] = +90.0;
+  catstats[0].DEC[1] = -90.0;
+
+  for (x = 0; x <= 1.0; x += 0.5) {
+    for (y = 0; y <= 1.0; y += 0.5) {
+
+      X = x*dX + Xo;
+      Y = y*dY + Yo;
+      status = XY_to_RD (&R, &D, X, Y, &Target[0].coords);
+      if (!status) continue;
+      if (isinf(R) || isnan(R)) continue;
+      if (isinf(D) || isnan(D)) continue;
+
+      catstats[0].RA[0]  = MIN (catstats[0].RA[0], R);
+      catstats[0].RA[1]  = MAX (catstats[0].RA[1], R);
+      catstats[0].DEC[0] = MIN (catstats[0].DEC[0], D);
+      catstats[0].DEC[1] = MAX (catstats[0].DEC[1], D);
+    }
+  }
+
+  /* is a pole in the image?  if so, include it... */
+  status = RD_to_XY (&X, &Y, 0.0, 90.0, &Target[0].coords);
+  if (status) {
+      if (fabs(X - NX*0.5) > (NFIELD + 0.5)*NX) goto not_north;
+      if (fabs(Y - NY*0.5) > (NFIELD + 0.5)*NY) goto not_north;
+      catstats[0].DEC[1] = 90.0;
+  }
+not_north:
+  
+  status = RD_to_XY (&X, &Y, 0.0, -90.0, &Target[0].coords);
+  if (status) {
+      if (fabs(X - NX*0.5) > (NFIELD + 0.5)*NX) goto not_south;
+      if (fabs(Y - NY*0.5) > (NFIELD + 0.5)*NY) goto not_south;
+      catstats[0].DEC[0] = -90.0;
+  }
+not_south:
+
+  if (VERBOSE) fprintf (stderr, "full region: %f - %f, %f - %f\n", 
+	   catstats[0].RA[0], catstats[0].RA[1], catstats[0].DEC[0], catstats[0].DEC[1]);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/grid.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/grid.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/grid.c	(revision 22322)
@@ -0,0 +1,219 @@
+# include "gastro2.h"
+
+/* this needs to be user-configured */
+static int NX, NY, Nbin;
+static double C0x, C1x, C0y, C1y;
+static double *N, *DX, *DY, *D2;
+
+void grid (CmpCatalog *Target, RefCatalog *Subset, Answer *answer) {
+
+  int i, j, n, Imin, Nmin, Nval;
+  double XMIN, XMAX, YMIN, YMAX;
+  double dX, dY;
+  double s, f, Fmin, Smin, *ntmp;
+
+  RefCatalog Ref;
+  StarData *st, *sr;
+
+  /* Ref is temporary in this function.  Free Ref.stars before exiting */
+  rotate (Subset, &Ref, answer[0].angle);
+
+  st = Target[0].stars;
+  sr = Ref.stars;
+  
+  /* NFIELD represents the search box; it is also the extra padding used 
+     to select the reference stars */
+  XMIN = -0.5*NFIELD*Target[0].header.Naxis[0];
+  XMAX = +0.5*NFIELD*Target[0].header.Naxis[0];
+  YMIN = -0.5*NFIELD*Target[0].header.Naxis[1];
+  YMAX = +0.5*NFIELD*Target[0].header.Naxis[1];
+
+  dX = dY = 0;
+  /* make two passes, with grids offset by 0.5 box for the second */
+  for (n = 0; n < 2; n++) {
+    gridinit (XMIN, XMAX, YMIN, YMAX, Ref.N, Target[0].N);
+    
+    if (PLOTSTUFF) plot_init_gridplot ();
+    /* fill in grid points */
+    for (i = 0; i < Target[0].N; i++) {
+      for (j = 0; j < Ref.N; j++) {
+	
+	dX = st[i].X - sr[j].X;
+	if (dX < XMIN) continue;
+	if (dX > XMAX) continue;
+	
+	dY = st[i].Y - sr[j].Y;
+	if (dY < YMIN) continue;
+	if (dY > YMAX) continue;
+	
+	gridbin (dX, dY);
+	if (PLOTSTUFF) plot_addpt_gridplot (dX, dY);
+      }
+    }
+
+    /* use sorted N list to define Nmin cut */
+    ALLOCATE (ntmp, double, Nbin);
+    bcopy (N, ntmp, Nbin*sizeof(double));
+    dsort (ntmp, Nbin);
+    for (i = 0; (ntmp[i] == 0) && (i < Nbin); i++);
+    Nval = MIN (Nbin - 1, (int)(0.75*(Nbin - i)) + i);
+    Nmin = ntmp[Nval];
+    free (ntmp);
+    
+    /* select 'best' grid point - is this statistic good enough? */
+    Fmin = 1e10;
+    Imin = -1;
+    for (i = 0; i < Nbin; i++) {
+      
+      if (N[i] < Nmin) continue;
+      
+      /* s is the variance, f is varience overweighted by number */
+      s = fabs ((D2[i]/N[i]) - SQ(DX[i]/N[i]) - SQ(DY[i]/N[i]));
+      f = s / SQ(SQ(N[i]));
+      
+      if (f < Fmin) {
+	Smin = s;
+	Fmin = f;
+	Imin = i;
+      }
+    }
+    if (Imin == -1) { 
+      fprintf (stderr, "ERROR: odd min value\n");
+      exit (1);
+    }
+    
+    if ((n == 0) || (Fmin < answer[0].Chi)) {
+      Smin = fabs ((D2[Imin]/N[Imin]) - SQ(DX[Imin]/N[Imin]) - SQ(DY[Imin]/N[Imin]));
+      answer[0].Xoff = DX[Imin] / N[Imin];
+      answer[0].Yoff = DY[Imin] / N[Imin];
+      answer[0].dR   = sqrt (Smin);
+      answer[0].Chi  = Fmin;
+      answer[0].N    = N[Imin];
+    }
+    
+    fprintf (stderr, "angle: %6.1f, (%6.1f,%6.1f) - %6.2f : %10.8f for %d pairs\n", 
+	     answer[0].angle, answer[0].Xoff, answer[0].Yoff, answer[0].dR, answer[0].Chi, answer[0].N);
+
+    if (PLOTSTUFF) plot_gridpts (N, Nbin);
+    if (PLOTSTUFF) plot_done_gridplot (dX, dY);
+
+    XMIN -= 0.5*NGRID_PIX;
+    XMAX -= 0.5*NGRID_PIX;
+    YMIN -= 0.5*NGRID_PIX;
+    YMAX -= 0.5*NGRID_PIX;
+    gridfree ();
+  }
+
+  free (Ref.stars);
+
+}
+
+void gridinit (double XMIN, double XMAX, double YMIN, double YMAX, int Nr, int Nt) {
+
+  NX = (XMAX - XMIN) / NGRID_PIX;
+  NY = (YMAX - YMIN) / NGRID_PIX;
+
+  C1x =          NX / (XMAX - XMIN); 
+  C0x = - XMIN * NX / (XMAX - XMIN);
+
+  C1y =          NY / (YMAX - YMIN); 
+  C0y = - YMIN * NY / (YMAX - YMIN);
+
+  Nbin = NX*NY;
+
+  ALLOCATE (N, double, Nbin);
+  ALLOCATE (DX, double, Nbin);
+  ALLOCATE (DY, double, Nbin);
+  ALLOCATE (D2, double, Nbin);
+
+  bzero (N,  Nbin*sizeof(double));
+  bzero (DX, Nbin*sizeof(double));
+  bzero (DY, Nbin*sizeof(double));
+  bzero (D2, Nbin*sizeof(double));
+
+}
+
+int gridbin (double dX, double dY) {
+
+  int bin, xbin, ybin;
+
+  xbin = (int) (C0x + dX * C1x);
+  ybin = (int) (C0y + dY * C1y);
+
+  bin =  xbin + NX * ybin;
+
+  if (bin < 0)     return (0);
+  if (bin >= Nbin) return (0);
+
+  N[bin]   += 1.0;
+  DX[bin]  += dX;
+  DY[bin]  += dY;
+  D2[bin]  += dX*dX + dY*dY;
+  
+  return (bin);
+
+}
+  
+void gridfree () {
+
+  free (N);
+  free (DX);
+  free (DY);
+  free (D2);
+
+}
+
+# if (0) 
+  /* use sorted N list to define Nmin cut */
+  ALLOCATE (ntmp, double, Nbin);
+  bcopy (N, ntmp, Nbin*sizeof(double));
+  dsort (ntmp, Nbin);
+  for (i = 0; (ntmp[i] == 0) && (i < Nbin); i++);
+  Nval = MIN (Nbin - 1, (int)(0.75*(Nbin - i)) + i);
+  Nmin = ntmp[Nval];
+  free (ntmp);
+
+  /* select 'best' grid point - is this statistic good enough? */
+  Fmin = 1e10;
+  Imin = -1;
+  for (i = 0; i < Nbin; i++) {
+
+    if (N[i] < Nmin) continue;
+
+    /* s is the variance, f is varience overweighted by number */
+    s = fabs ((D2[i]/N[i]) - SQ(DX[i]/N[i]) - SQ(DY[i]/N[i]));
+    f = s / SQ(SQ(N[i]));
+
+    if (f < Fmin) {
+      Smin = s;
+      Fmin = f;
+      Imin = i;
+    }
+  }
+  if (Imin == -1) { 
+    fprintf (stderr, "ERROR: odd min value\n");
+    exit (1);
+  }
+# endif
+
+# if (0)
+    /* find N sigma */
+    Ns = Ns2 = 0;
+    for (i = 0; i < Nbin; i++) {
+      Ns += N[i];
+      Ns2 += N[i]*N[i];
+    }
+    Ns = Ns / Nbin;
+    Ns2 = sqrt (Ns2 / Nbin - Ns*Ns);
+    fprintf (stderr, "N sigma: %f\n", Ns2);
+    
+    Imin = 0;
+    Fmin = N[0] / Ns2;
+    for (i = 0; i < Nbin; i++) {
+      f = N[i] / Ns2;
+      if (f > Fmin) {
+	Fmin = f;
+	Imin = i;
+      }
+    }
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gstars2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gstars2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/gstars2.c	(revision 22322)
@@ -0,0 +1,177 @@
+# include "gastro2.h"
+
+void gstars (char *filename, CmpCatalog *Target) {
+
+  int Nstars;
+  char line[80];
+  double det;
+  int NX, NY, Nskip, FoundAstrom, extend, naxis;
+  StarData *stars;
+  FILE *f;
+
+  /* open file for stars */
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open file to load stars\n");
+    exit (1);
+  }
+  if (!gfits_fread_header (f, &Target[0].header)) {
+    fprintf (stderr, "ERROR: can't read image header\n");
+    exit (1);
+  }
+  /* this line should not be needed */
+  fseek (f, Target[0].header.size, SEEK_SET); 
+
+  NX = Target[0].header.Naxis[0];
+  NY = Target[0].header.Naxis[1];
+
+  /* default values for coords */
+  strcpy (Target[0].coords.ctype, "RA---TAN");
+  Target[0].coords.pc1_1 = CCD_PC1_1; Target[0].coords.pc1_2 = CCD_PC1_2;
+  Target[0].coords.pc2_1 = CCD_PC2_1; Target[0].coords.pc2_2 = CCD_PC2_2;
+  Target[0].coords.cdelt1 = Target[0].coords.cdelt2 = ASEC_PIX / 3600.0;
+  Target[0].coords.Npolyterms = 0;
+  Target[0].coords.crpix1 = 0.5*NX;
+  Target[0].coords.crpix2 = 0.5*NY;
+  
+  /* attempt to get detailed astrometric information from header */
+  FoundAstrom = FALSE;
+  if (!strcasecmp (ROUGH_ASTROMETRY, "header")) {
+    if (!HEADER[0]) {
+      FoundAstrom = GetCoords (&Target[0].coords, &Target[0].header);
+    } else {
+      Header header;
+
+      if (!gfits_read_header (HEADER, &header)) {
+	fprintf (stderr, "ERROR: can't load external header\n");
+	exit (1);
+      }
+      FoundAstrom = GetCoords (&Target[0].coords, &header);
+      gfits_free_header (&header);
+    }
+    if (!FoundAstrom) {
+      fprintf (stderr, "header coordinates incomplete, trying for rough coordinates\n");
+      strcpy (ROUGH_ASTROMETRY, "config");
+    } else {
+      /* make optional adustments to header values (standard problems) */
+      if (FLIPX) {
+	Target[0].coords.pc1_1 *= -1;
+	Target[0].coords.crpix1 = NX - Target[0].coords.crpix1;
+      }
+      if (FLIPY) {
+	Target[0].coords.pc2_2 *= -1;
+	Target[0].coords.crpix2 = NY - Target[0].coords.crpix2;
+      }
+    }
+  }
+  
+  /*** abstract RA & DEC keywords, formats */
+  /* get just RA & DEC from header, other terms from config file */
+  if (!strcasecmp (ROUGH_ASTROMETRY, "config")) {
+    /* get RA & DEC from header */
+    FoundAstrom = TRUE;
+    FoundAstrom &= gfits_scan (&Target[0].header, "RA", "%s", 1, line);
+    ohana_dms_to_ddd (&Target[0].coords.crval1, line);
+    Target[0].coords.crval1 = Target[0].coords.crval1 * 15.0;
+    FoundAstrom &= gfits_scan (&Target[0].header, "DEC", "%s", 1, line);
+    ohana_dms_to_ddd (&Target[0].coords.crval2, line);
+  }
+
+  /* use RA & DEC from command line arguments */
+  if (FORCE) {
+    Target[0].coords.crval1 = F_RA;
+    Target[0].coords.crval2 = F_DEC;
+    if (VERBOSE) fprintf (stderr, " forcing coordinates to: %9.4f %9.4f\n", Target[0].coords.crval1, Target[0].coords.crval2);
+    FoundAstrom = TRUE;
+  }    
+
+  if (VERBOSE) fprintf (stderr, "using coordinates: %9.4f %9.4f\n", Target[0].coords.crval1, Target[0].coords.crval2);
+  if (!FoundAstrom) {
+    fprintf (stderr, "ERROR: can't get any valid coordinates, fix config file?\n");
+    exit (1);
+  }
+
+  /* at this point, we need to correct the crval1, crval2, and ROT_ZERO values
+     based on the pole axis angle and the ra, dec offsets */
+
+# define dcos(a) (cos((double)((a)*(RAD_DEG))))
+# define dsin(a) (sin((double)((a)*(RAD_DEG))))
+
+  if (POLAR_ALIGNMENT) {
+
+    double X, Y, PD, PR, DE, RE, T1, T2, T3;
+
+    X = Target[0].coords.crval1;
+    Y = Target[0].coords.crval2;
+    PD = POLE_DEC;   PR = POLE_RA;
+    DE = DEC_OFFSET; RE = RA_OFFSET;
+    
+    T1 = dcos(Y-DE) * dcos(X-RE) * dsin(PD) + dsin(Y-DE) * dcos(PD);
+    T2 = dcos(Y-DE) * dsin(X-RE);
+    T3 = dsin(Y-DE) * dsin(PD) - dcos(Y-DE) * dcos(X-RE) * dcos(PD);
+    
+    Target[0].coords.crval1 = (DEG_RAD * atan2 (T2, T1)) + PR;
+    Target[0].coords.crval2 = (DEG_RAD * asin (T3));
+    while (Target[0].coords.crval1 < 0) Target[0].coords.crval1 += 360.0;
+    while (Target[0].coords.crval1 > 360.0) Target[0].coords.crval1 -= 360.0;
+    
+    if (VERBOSE) fprintf (stderr, "  after polar alignment: %9.4f %9.4f\n", Target[0].coords.crval1, Target[0].coords.crval2);
+  }
+
+  /* get image area in deg^2 */
+  det = Target[0].coords.pc1_1 * Target[0].coords.pc2_2 - Target[0].coords.pc1_2 * Target[0].coords.pc2_1;
+  Target[0].Area = fabs (NX*NY*Target[0].coords.cdelt1*Target[0].coords.cdelt2*det);
+
+  /* read from FITS table or from text table */
+  /* Is NAXIS == 0 a better test?? */
+  extend = FALSE;
+  gfits_scan (&Target[0].header, "NAXIS",  "%t", 1, &naxis);
+  if ((naxis == 0) && !TEXTMODE) {
+    Nskip = gfits_data_size (&Target[0].header);
+    fseek (f, Nskip, SEEK_CUR); 
+    stars = rfits (f, &Nstars);
+  } else {
+    /* allocate space for stars */
+    if (!gfits_scan (&Target[0].header, "NSTARS", "%d", 1, &Nstars)) {
+      fprintf (stderr, "ERROR: failed to find NSTARS\n");
+      exit (1);
+    }
+    stars = rtext (f, &Nstars);
+  }
+  fclose (f);
+
+  stars = remove_clumps (stars, &Nstars, NX, NY);
+
+  sort_stars_mag (stars, Nstars);  /* sorting by magnitude */
+  Target[0].stars = stars;
+  Target[0].N = Nstars;
+
+  /* limit number of stars */
+  if (GASTRO_MAX_NSTARS && (GASTRO_MAX_NSTARS < Target[0].N)) {
+    Target[0].N = GASTRO_MAX_NSTARS;
+    REALLOCATE (Target[0].stars, StarData, Target[0].N);
+  }
+  if (VERBOSE) fprintf (stderr, "using %d stars from data file\n", Target[0].N);
+
+  /* calculate luminosity function of stars */
+  get_luminosity_func (stars, Target[0].N, &Target[0].lum);
+
+}
+
+/* 
+
+load cmp file FITS header 
+
+extract needed data from header:
+- NX, NY?
+- coords
+   
+load stellar photometry
+
+sort stars by mag
+
+filter & limit numbers
+
+find luminosity function slope, area?
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/lumfunc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/lumfunc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/lumfunc.c	(revision 22322)
@@ -0,0 +1,120 @@
+# include "gastro2.h"
+
+/* mag range -5 - 35, dmag = 0.25, Nbin = 160 */
+# define MMIN -5
+# define MMAX 35
+# define dM 0.5
+# define NMBIN 90
+
+int get_luminosity_func (StarData *stars, int N, LumStats *lum) {
+
+  int i, j, Nb, peaki, peakn;
+  double mbin[NMBIN];
+  double bin[NMBIN], lbin[NMBIN], rbin[NMBIN];
+  double C0, C1;
+
+  lum[0].dNdM = lum[0].Mo = 0;
+
+  bzero (mbin, NMBIN * sizeof (double));
+
+  /* sum histogram */
+  for (i = 0; i < N; i++) {
+    if (stars[i].M < MMIN) continue;
+    if (stars[i].M > MMAX) continue;
+
+    j = (stars[i].M - MMIN) / dM;
+    j = MIN (MAX (j, 0), (NMBIN - 1));
+    mbin[j] ++;
+  }
+
+  /* find peak bin */
+  peaki = 0;
+  peakn = mbin[0];
+  for (i = 0; i < NMBIN; i++) {
+    if (mbin[i] > peakn) {
+      peaki = i;
+      peakn = mbin[i];
+    }
+  }
+
+  /* select filled bins */
+  for (Nb = i = 0; i < peaki; i++) {
+    if (mbin[i] > 0) {
+      bin[Nb]  = i * dM + MMIN;
+      lbin[Nb] = log10 (mbin[i]);
+      Nb++;
+    }
+  }
+
+  /* find max & min mag bins */
+  lum[0].Mmin = bin[0];
+  lum[0].Mmax = bin[Nb-1];
+
+  if (Nb < 4) { return (FALSE); }
+  fit_lum_bin (bin, lbin, Nb, &C0, &C1);
+  
+  /* find residuals */
+  for (i = 0; i < Nb; i++) {
+    rbin[i] = C0 + C1*bin[i] - lbin[i];
+  }
+
+  /* keep inner 80% */
+  dsortthree (rbin, bin, lbin, Nb);
+  for (j = 0, i = 0.1*Nb; i < 0.9*Nb; i++, j++) {
+    bin[j]  = bin[i];
+    lbin[j] = lbin[i];
+  }    
+  Nb = j;
+
+  if (Nb < 4) { return (FALSE); }
+  fit_lum_bin (bin, lbin, Nb, &C0, &C1);
+
+  lum[0].dNdM = C1;
+  lum[0].Mo   = C0;
+  
+  if (VERBOSE) fprintf (stderr, "lum stats: dNdM = %f, Mo = %f, Mmin = %f, Mmax = %f\n", 
+	   lum[0].dNdM, lum[0].Mo, lum[0].Mmin, lum[0].Mmax);
+
+  return (TRUE);
+
+}
+
+
+void fit_lum_bin (double *x, double *y, int N, double *C0, double *C1) {
+
+  int i;
+  double **c, **b;
+
+  ALLOCATE (c, double *, 2);
+  ALLOCATE (b, double *, 2);
+  ALLOCATE (c[0], double, 2);
+  ALLOCATE (c[1], double, 2);
+  ALLOCATE (b[0], double, 1);
+  ALLOCATE (b[1], double, 1);
+
+  /* fit x, y to line */
+  c[0][0] = 0; c[0][1] = 0;
+  c[1][0] = 0; c[1][1] = 0;
+  b[0][0] = 0; b[1][0] = 0;
+
+  for (i = 0; i < N; i++) {
+    c[0][0] += 1;
+    c[0][1] += x[i];
+    c[1][0] += x[i];
+    c[1][1] += x[i]*x[i];
+    
+    b[0][0] += y[i];
+    b[1][0] += y[i]*x[i];
+  }
+  dgaussjordan (c, b, 2, 1);
+  *C0 = b[0][0];
+  *C1 = b[1][0];
+
+  free (b[0]);
+  free (b[1]);
+  free (c[0]);
+  free (c[1]);
+  free (c);
+  free (b);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/misc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/misc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/misc.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "gastro2.h"
+
+# define SIGN(X)  (((X) == 0) ? 0 : ((fabs((double)(X))) / (X)))
+
+void hh_hms (double hh, int *hr, int *mn, double *sc) {
+
+  int flag;
+
+  flag = SIGN(hh);
+  hh *= flag;
+  hh = 24.0*(hh/24.0 - (int)(hh/24.0));
+  *sc = 60.0*(60.0*hh - (int)(60.0*hh));
+  *mn = 60.0*(hh - (int)hh);
+  *hr = (int) hh;
+  *hr *= flag;
+
+}
+ 
+void hms_format (char *line, double value) {
+
+  int hr, mn;
+  double sc;
+
+  hh_hms (value, &hr, &mn, &sc);
+  hr = (int) value;
+  if (isnan (value))
+    sprintf (line, "xx:xx:xx.xx");
+  else {
+    if (value < 0) {
+      sprintf (line, "-%02d:%02d:%05.2f", abs(hr), mn, sc);
+    } else {
+      sprintf (line, "%02d:%02d:%05.2f", hr, mn, sc);
+    }
+  }      
+}
+
+void area_of_region (CatStats *region) {
+  
+  double area;
+
+  area = DEG_RAD*(region[0].RA[1] - region[0].RA[0])*(sin(region[0].DEC[1]*RAD_DEG) - sin(region[0].DEC[0]*RAD_DEG));
+  region[0].Area = area;
+}
+
+double area_of_skyregion (SkyRegion *region) {
+  
+  double area;
+
+  area = DEG_RAD*(region[0].Rmax - region[0].Rmin)*(sin(region[0].Dmax*RAD_DEG) - sin(region[0].Dmin*RAD_DEG));
+  return (area);
+}
+
+void dump_coords (CmpCatalog *Target) {
+  if (VERBOSE) {
+    fprintf (stderr, "%s\n", Target[0].coords.ctype);
+    fprintf (stderr, "%f %f\n", Target[0].coords.crval1, Target[0].coords.crval2);
+    fprintf (stderr, "%f %f\n", Target[0].coords.crpix1, Target[0].coords.crpix2);
+    fprintf (stderr, "%f %f\n", Target[0].coords.pc1_1,  Target[0].coords.pc1_2);
+    fprintf (stderr, "%f %f\n", Target[0].coords.pc2_1,  Target[0].coords.pc2_2);
+    fprintf (stderr, "%f %f\n", Target[0].coords.cdelt1, Target[0].coords.cdelt2);
+    fprintf (stderr, "\n");
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/plots.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/plots.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/plots.c	(revision 22322)
@@ -0,0 +1,440 @@
+# include "gastro2.h"
+
+int Nv, NV;
+float *xv, *yv;
+Graphdata gv;
+
+void plot_init_gridplot () {
+  
+  NV = 1000;
+  Nv = 0;
+
+  ALLOCATE (xv, float, NV);
+  ALLOCATE (yv, float, NV);
+
+  PlotReset (2);
+
+  gv.xmin = -400;
+  gv.xmax = +400;
+  gv.ymin = -400;
+  gv.ymax = +400;
+
+  gv.style = 2;
+  gv.ltype = 0;
+  gv.etype = 0;
+  gv.ebar  = 0;
+  gv.size = 0.3;
+  gv.lweight = 0;
+  gv.color = 0;
+  gv.ptype = 2;
+
+}
+
+int plot_addpt_gridplot (double x, double y) {
+
+  if (x < gv.xmin) return (0);
+  if (x > gv.xmax) return (0);
+  if (y < gv.ymin) return (0);
+  if (y > gv.ymax) return (0);
+
+  xv[Nv] = x;
+  yv[Nv] = y;
+  Nv ++;
+  
+  if (Nv == NV) {
+    NV += 1000;
+    REALLOCATE (xv, float, NV);
+    REALLOCATE (yv, float, NV);
+  }
+  return (1);
+}
+
+void plot_done_gridplot () {
+
+  char c;
+
+  /* send to Kapa */
+  PrepPlotting (Nv, &gv, 2);
+  PlotVector (Nv, xv, 0, 2);
+  PlotVector (Nv, yv, 1, 2);
+
+  DonePlotting (&gv, 2);
+  fprintf (stderr, "type return to continue");
+  fscanf (stdin, "%c", &c);
+  free (xv);
+  free (yv);
+}
+  
+static double Xm[2];
+static int Xg[2] = {0, 2};
+
+void plot_resid_init (int version, double xmax) {
+
+  if (version > 1) return;
+  if (version < 0) return;
+  
+  Xm[version] = xmax;
+}
+
+
+void plot_resid_plot (int version, float *xvect, float *yvect, int Nvect) {
+ 
+  int i;
+  char c;
+  float xmin, xmax, ymin, ymax;
+  Graphdata graphdata;
+  
+  xmin = xmax = xvect[0];
+  ymin = ymax = yvect[0];
+  for (i = 0; i < Nvect; i++) {
+    xmax = MAX (xvect[i], xmax);
+    xmin = MIN (xvect[i], xmin);
+    ymax = MAX (yvect[i], ymax);
+    ymin = MIN (yvect[i], ymin);
+  }
+    
+  PlotReset (Xg[version]);
+
+  graphdata.xmin = MIN (xmin,   0);
+  graphdata.xmax = MAX (xmax, Xm[version]);
+  graphdata.ymin = MIN (ymin, -10);
+  graphdata.ymax = MAX (ymax, +10);
+
+  graphdata.style = 2;
+  graphdata.ltype = 0;
+  graphdata.etype = 0;
+  graphdata.ebar  = 0;
+  graphdata.size = 0.5;
+  graphdata.lweight = 0;
+  graphdata.color = 0;
+  graphdata.ptype = 2;
+
+  /* send to Kapa */
+  PrepPlotting (Nvect, &graphdata, Xg[version]);
+  PlotVector (Nvect, xvect, 0, Xg[version]);
+  PlotVector (Nvect, yvect, 1, Xg[version]);
+
+  DonePlotting (&graphdata, Xg[version]);
+  fprintf (stderr, "type return to continue");
+  fscanf (stdin, "%c", &c);
+}
+
+void plot_gridpts (double *pts, int Npts) {
+
+  char c;
+  int i;
+  float *xvect, *yvect, ymax;
+  Graphdata graphdata;
+  
+  ymax = 0;
+  ALLOCATE (xvect, float, Npts);
+  ALLOCATE (yvect, float, Npts);
+  for (i = 0; i < Npts; i++) {
+    xvect[i] = i;
+    yvect[i] = pts[i];
+    ymax = MAX (yvect[i], ymax);
+  }
+    
+  PlotReset (0);
+
+  graphdata.xmin = -10;
+  graphdata.xmax = Npts + 10;
+  graphdata.ymin = -1;
+  graphdata.ymax = ymax + 1;
+
+  graphdata.style = 1;
+  graphdata.ltype = 0;
+  graphdata.etype = 0;
+  graphdata.ebar  = 0;
+  graphdata.size = 0.5;
+  graphdata.lweight = 0;
+  graphdata.color = 0;
+  graphdata.ptype = 0;
+
+  /* send to Kapa */
+  PrepPlotting (Npts, &graphdata, 0);
+  PlotVector (Npts, xvect, 0, 0);
+  PlotVector (Npts, yvect, 1, 0);
+  free (xvect);
+  free (yvect);
+
+  DonePlotting (&graphdata, 0);
+  fprintf (stderr, "type return to continue");
+  fscanf (stdin, "%c", &c);
+}
+
+static Graphdata gf;
+
+void plot_fullfield (CmpCatalog *Target, RefCatalog *Ref) {
+
+  int i, Nvect;
+  float *xvect, *yvect, *zvect, M, dM, mRefMin, mRefMax;
+  char c;
+  
+  dM = Target[0].lum.Mmax - Target[0].lum.Mmin;
+
+  PlotReset (1);
+
+  gf.xmin = -100;
+  gf.xmax = Target[0].header.Naxis[0] + 100;
+  gf.ymin = -100;
+  gf.ymax = Target[0].header.Naxis[1] + 100;
+
+  gf.style = 2;
+  gf.ltype = 0;
+  gf.etype = 0;
+  gf.ebar  = 0;
+  gf.size = -1;
+  gf.lweight = 0;
+
+  /* fill in vectors */
+  Nvect = Target[0].N;
+  ALLOCATE (xvect, float, Nvect);
+  ALLOCATE (yvect, float, Nvect);
+  ALLOCATE (zvect, float, Nvect);
+  for (i = 0; i < Nvect; i++) {
+    xvect[i] = Target[0].stars[i].X;
+    yvect[i] = Target[0].stars[i].Y;
+    M = (Target[0].lum.Mmax - Target[0].stars[i].M) / dM;
+    zvect[i] = MIN (1.0, MAX (0.01, M));
+  }
+
+  /* send to Kapa */
+  gf.ptype = 1;
+  gf.color = 0;
+  PrepPlotting (Nvect, &gf, 1);
+  PlotVector (Nvect, xvect, 0, 1);
+  PlotVector (Nvect, yvect, 1, 1);
+  PlotVector (Nvect, zvect, 1, 1);
+  free (xvect);
+  free (yvect);
+  free (zvect);
+
+  /* fill in vectors */
+  Nvect = Ref[0].N;
+  ALLOCATE (xvect, float, Nvect);
+  ALLOCATE (yvect, float, Nvect);
+  ALLOCATE (zvect, float, Nvect);
+
+  mRefMin = 30.0;
+  mRefMax = -5.0;
+  for (i = 0; i < Nvect; i++) {
+      mRefMin = MIN (mRefMin, Ref[0].stars[i].M);
+      mRefMax = MAX (mRefMax, Ref[0].stars[i].M);
+  }
+  dM = mRefMax - mRefMin;
+
+  for (i = 0; i < Nvect; i++) {
+    xvect[i] = Ref[0].stars[i].X;
+    yvect[i] = Ref[0].stars[i].Y;
+    M = (mRefMax - Ref[0].stars[i].M) / dM;
+    zvect[i] = MIN (1.0, MAX (0.01, M));
+  }
+
+  /* send to Kapa */
+  gf.color = 2;
+  gf.ptype = 2;
+  PrepPlotting (Nvect, &gf, 1);
+  PlotVector (Nvect, xvect, 0, 1);
+  PlotVector (Nvect, yvect, 1, 1);
+  PlotVector (Nvect, zvect, 2, 1);
+  free (xvect);
+  free (yvect);
+  free (zvect);
+
+  DonePlotting (&gf, 1);
+  fprintf (stderr, "type return to continue");
+  fscanf (stdin, "%c", &c);
+}
+
+void plot_fullfield_pairs (float *x, float *y, int n) {
+
+  char c;
+
+  /* send to Kapa */
+  gf.color = 6;
+  gf.ptype = 100;
+  gf.size =  1.0;
+  
+  PrepPlotting (n, &gf, 1);
+  PlotVector (n, x, 0, 1);
+  PlotVector (n, y, 1, 1);
+  fprintf (stderr, "type return to continue");
+  fscanf (stdin, "%c", &c);
+}  
+
+/* mag range -5 - 35, dmag = 0.25, Nbin = 160 */
+# define MMIN -5
+# define MMAX 35
+# define dM 0.5
+# define NMBIN 90
+
+void plot_lumfunc (CmpCatalog *Target, RefCatalog *Ref) {
+
+  int i, Nr, Nt;
+  float tbin[NMBIN], tval[NMBIN], rbin[NMBIN], rval[NMBIN];
+  double ymin, ymax;
+  char c;
+  Graphdata graphdata;
+
+  fill_lumfunc (Target[0].stars, Target[0].N, tval, tbin, &Nt);
+  fill_lumfunc (Ref[0].stars, Ref[0].N, rval, rbin, &Nr);
+
+  /* construct plots of log(stars / degree square) */
+  ymin = 5; ymax = -5;
+  for (i = 0; i < Nt; i++) {
+    tval[i] = tval[i] - log10 (Target[0].Area);
+    ymin = MIN (tval[i], ymin);
+    ymax = MAX (tval[i], ymax);
+  }
+
+  for (i = 0; i < Nr; i++) {
+    rval[i] = rval[i] - log10 (Ref[0].Area);
+    rbin[i] = rbin[i] + Ref[0].Moff;
+    ymin = MIN (rval[i], ymin);
+    ymax = MAX (rval[i], ymax);
+  }
+
+  graphdata.xmin = MMIN;
+  graphdata.xmax = MMAX;
+  graphdata.ymin = ymin - 0.1;
+  graphdata.ymax = ymax + 0.1;
+
+  graphdata.style = 1;
+  graphdata.ptype = 2;
+  graphdata.ltype = 0;
+  graphdata.etype = 0;
+  graphdata.ebar  = 0;
+  graphdata.color = 0;
+
+  graphdata.lweight = 0;
+  graphdata.size = 0.5;
+
+  PlotReset (0);
+
+  PrepPlotting (Nt, &graphdata, 0);
+  PlotVector (Nt, tbin, 0, 0);
+  PlotVector (Nt, tval, 1, 0);
+
+  graphdata.color = 2;
+  graphdata.style = 1;
+  graphdata.lweight = 0;
+  PrepPlotting (Nr, &graphdata, 0);
+  PlotVector (Nr, rbin, 0, 0);
+  PlotVector (Nr, rval, 1, 0);
+
+  /* plot truncated lum func */
+  for (i = 0; i < Nr; i++) {
+    if (rbin[i] < Target[0].lum.Mmin - 0.5) rval[i] = -1;
+    if (rbin[i] > Target[0].lum.Mmax + 0.5) rval[i] = -1;
+  }
+  graphdata.ltype = 1;
+  PrepPlotting (Nr, &graphdata, 0);
+  PlotVector (Nr, rbin, 0, 0);
+  PlotVector (Nr, rval, 1, 0);
+
+  DonePlotting (&graphdata, 0);
+  fprintf (stderr, "type return to continue");
+  fscanf (stdin, "%c", &c);
+
+}
+
+
+void plot_resid (StarData *st, StarData *sr, Coords *coords) {
+
+  int i;
+  double x, y, dx, dy;
+  float *xvect0, *yvect0, *xvect1, *yvect1, *xvect2, *yvect2;
+  int Npair, *idx1, *idx2;
+
+  Npair = pair_lists (&idx1, &idx2);
+  ALLOCATE (xvect0, float, Npair);
+  ALLOCATE (yvect0, float, Npair);
+  ALLOCATE (xvect1, float, Npair);
+  ALLOCATE (yvect1, float, Npair);
+
+  ALLOCATE (xvect2, float, 2*Npair);
+  ALLOCATE (yvect2, float, 2*Npair);
+  
+  for (i = 0; i < Npair; i++) {
+    RD_to_XY (&x, &y, sr[idx2[i]].R, sr[idx2[i]].D, coords);
+    
+    dx = st[idx1[i]].X - x;
+    dy = st[idx1[i]].Y - y;
+    
+    xvect0[i] = st[idx1[i]].X;
+    xvect1[i] = st[idx1[i]].Y;
+    yvect0[i] = dx;
+    yvect1[i] = dy;
+
+    xvect2[2*i+0] = st[idx1[i]].X;
+    yvect2[2*i+0] = st[idx1[i]].Y;
+    xvect2[2*i+1] = x;
+    yvect2[2*i+1] = y;
+  }
+
+  fprintf (stderr, "residuals using RD_to_XY\n");
+  plot_resid_plot (0, xvect0, yvect0, Npair);
+  // plot_resid_plot (1, xvect1, yvect1, Npair);
+  plot_fullfield_pairs (xvect2, yvect2, 2*Npair);
+
+  for (i = 0; i < Npair; i++) {
+    fit_apply (&x, &y, st[idx1[i]].X, st[idx1[i]].Y);
+    
+    dx = (x - sr[idx2[i]].P)/coords[0].cdelt1;
+    dy = (y - sr[idx2[i]].Q)/coords[0].cdelt2;
+    
+    xvect0[i] = st[idx1[i]].X;
+    xvect1[i] = st[idx1[i]].Y;
+    yvect0[i] = dx;
+    yvect1[i] = dy;
+
+    xvect2[2*i+0] = sr[idx2[i]].P;
+    yvect2[2*i+0] = sr[idx2[i]].Q;
+    xvect2[2*i+1] = x;
+    yvect2[2*i+1] = y;
+  }
+
+  fprintf (stderr, "residuals using fit_apply\n");
+  plot_resid_plot (1, xvect0, yvect0, Npair);
+  // plot_resid_plot (1, xvect1, yvect1, Npair);
+  // plot_fullfield_pairs (xvect2, yvect2, 2*Npair);
+
+  free (xvect2);
+  free (yvect2);
+  free (xvect0);
+  free (yvect0);
+  free (xvect1);
+  free (yvect1);
+
+}
+
+void fill_lumfunc (StarData *stars, int N, float *lbin, float *bin, int *nb) {
+
+  int i, j, Nb;
+  double mbin[NMBIN];
+
+  bzero (mbin, NMBIN * sizeof (double));
+
+  /* sum histogram */
+  for (i = 0; i < N; i++) {
+    if (stars[i].M < MMIN) continue;
+    if (stars[i].M > MMAX) continue;
+
+    j = (stars[i].M - MMIN) / dM;
+    j = MIN (MAX (j, 0), (NMBIN - 1));
+    mbin[j] ++;
+  }
+
+  /* select filled bins */
+  for (Nb = i = 0; i < NMBIN; i++) {
+    if (mbin[i] > 0) {
+      bin[Nb]  = i * dM + MMIN;
+      lbin[Nb] = log (mbin[i]) / log (10.0);
+      Nb++;
+    }
+  }
+
+  *nb = Nb;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/plotstuff.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/plotstuff.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/plotstuff.c	(revision 22322)
@@ -0,0 +1,104 @@
+# include "gastro2.h"
+# include <signal.h>
+
+static int Xgraph[5] = {0,0,0,0,0};
+static int active;
+
+void XDead () {
+  signal (SIGPIPE, XDead);
+  fprintf (stderr, "kapa is dead, must restart\n");
+  Xgraph[active] = -1;
+}
+
+int open_graph (int N) {
+
+  char name[100];
+  
+  active = N;
+
+  sprintf (name, "gastro [%d]", N);
+  Xgraph[N] = KapaOpen ("kapa", name);
+
+  if (Xgraph[N] < 0) {
+    fprintf (stderr, "error starting kapa\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+void DonePlotting (Graphdata *graphmode, int N) {
+
+  if (Xgraph[N] == 0) return;
+  KapaBox (Xgraph[N], graphmode);
+  return;
+}
+
+void PrepPlotting (int Npts, Graphdata *graphmode, int N) {
+
+  if (Xgraph[N] == 0) return;
+
+  active = N;
+  if (Npts < 1) return;
+
+  KapaPrepPlot (Xgraph[N], Npts, graphmode);
+}
+
+void PlotVector (int Npts, float *vect, int mode, int N) {
+
+  if (Npts < 1) return;
+  active = N;
+
+  switch (mode) {
+    case 0:
+      KapaPlotVector (Xgraph[N], Npts, vect, "x");
+      break;
+    case 1:
+      KapaPlotVector (Xgraph[N], Npts, vect, "y");
+      break;
+    default:
+      abort();
+  }
+}
+
+void PlotReset (int N) {
+
+  char buffer[128];
+  int i;
+
+  /* test Xgraph[N], flush junk from pipe */
+  signal (SIGPIPE, XDead);
+  fcntl (Xgraph[N], F_SETFL,  O_NONBLOCK); 
+  for (i = 0; (read (Xgraph[N], buffer, 64) > 0) && (i < 20); i++);
+  fcntl (Xgraph[N], F_SETFL, !O_NONBLOCK); 
+  
+  if (Xgraph[N] < 1) if (!open_graph(N)) return;
+  KapaClearSections (Xgraph[N]);
+}
+
+/* include these lines to plot a pair of vectors: 
+
+   typedef struct {
+   double xmin, xmax, ymin, ymax;
+   int style, ptype, ltype, etype, color;
+   double lweight, size;
+   } Graphdata;
+   Graphdata graphdata;
+   
+   graphdata.xmin = -200;
+   graphdata.xmax = 4200;
+   graphdata.ymin = -500;
+   graphdata.ymax = 500;
+   graphdata.style = 2;
+   graphdata.ptype = 2;
+   graphdata.ltype = 0;
+   graphdata.etype = 0;
+   graphdata.color = 0;
+   graphdata.lweight = 0;
+   graphdata.size = 0.5;
+   
+   PrepPlotting (N, &graphdata, n);
+   PlotVector (N, Y, 0, n);
+   PlotVector (N, dM, 1, n);
+   DonePlotting (&graphdata, n);
+   
+ */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/polyfit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/polyfit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/polyfit.c	(revision 22322)
@@ -0,0 +1,408 @@
+# include "gastro2.h"
+
+static int NTERM, NPOWR, NPARS, NORDER, Npts;
+static double **sum, **xsum, **ysum;
+static double **matrix, **vector;
+
+void fit_init (int order) {
+
+  int i;
+
+  NORDER = order;
+  NPOWR = NORDER + 1;
+  NTERM = 2*NORDER + 1;
+  NPARS = (NORDER + 1)*(NORDER + 2) / 2;
+  Npts  = 0;
+
+  /* allocate arrays for fit solution */
+  ALLOCATE (sum, double *, NTERM);
+  ALLOCATE (xsum, double *, NTERM);
+  ALLOCATE (ysum, double *, NTERM);
+  for (i = 0; i < NTERM; i++) {
+    ALLOCATE (sum[i], double, NTERM);
+    bzero (sum[i], NTERM*sizeof(double));
+    ALLOCATE (xsum[i], double, NTERM);
+    bzero (xsum[i], NTERM*sizeof(double));
+    ALLOCATE (ysum[i], double, NTERM);
+    bzero (ysum[i], NTERM*sizeof(double));
+  }
+  ALLOCATE (matrix, double *, NPARS);
+  ALLOCATE (vector, double *, NPARS);
+  for (i = 0; i < NPARS; i++) {
+    ALLOCATE (matrix[i], double, NPARS);
+    ALLOCATE (vector[i], double, 2);
+    bzero (vector[i], 2*sizeof(double));
+    bzero (matrix[i], NPARS*sizeof(double));
+  }
+}
+
+/* */
+void fit_add (double x1, double y1, double x2, double y2, double wt) {
+
+  int n, m;
+  double xterm, yterm, term;
+
+  xterm = 1;
+  for (n = 0; n < NTERM; n++) {
+    yterm = 1;
+    for (m = 0; m < NTERM; m++) {
+      term = xterm*yterm;
+      if (n+m < NTERM) {
+	sum[n][m] += term;
+      }
+      if (n+m < NPOWR) {
+	xsum[n][m] += x2*term;
+	ysum[n][m] += y2*term;
+      }
+      yterm *= y1;
+    }
+    xterm *= x1;
+  }
+  Npts ++;
+
+}
+
+void fit_eval () {
+
+  int i, j, n, m, M, N;
+  double max;
+
+  i = 0;
+  for (m = 0; m < NPOWR; m++) {
+    for (n = 0; n < NPOWR - m; n++, i++) {
+      vector[i][0] = xsum[n][m];
+      vector[i][1] = ysum[n][m];
+    }	
+  }
+  j = 0;
+  for (M = 0; M < NPOWR; M++) {
+    for (N = 0; N < NPOWR - M; N++, j++) {
+      i = 0;
+      for (m = 0; m < NPOWR; m++) {
+	for (n = 0; n < NPOWR - m; n++, i++) {
+	  matrix[i][j] = sum[n+N][m+M];
+	}	
+      }
+    }
+  }       
+  max = 0.0;
+  for (i = 0; i < NPARS; i++) {
+    for (j = 0; j < NPARS; j++) {
+      max = MAX (max, fabs(matrix[i][j]));
+    }
+    max = MAX (max, fabs(vector[i][0]));
+    max = MAX (max, fabs(vector[i][1]));
+  }
+  for (i = 0; i < NPARS; i++) {
+    for (j = 0; j < NPARS; j++) {
+      matrix[i][j] /= max;
+    }
+    vector[i][0] /= max;
+    vector[i][1] /= max;
+  }
+  dgaussjordan (matrix, vector, NPARS, 2); 
+  i = 0;
+  for (m = 0; m < NPOWR; m++) {
+    for (n = 0; n < NPOWR - m; n++, i++) {
+      xsum[n][m] = vector[i][0];
+      ysum[n][m] = vector[i][1];
+    }	
+  }
+  i = 0;
+  for (m = 0; m < NPOWR; m++) {
+    for (n = 0; n < NPOWR - m; n++, i++) {
+      if (VERBOSE) fprintf (stderr, "RA x^%dy^%d: %10.4g    DEC x^%dy^%d: %10.4g \n", n, m, vector[i][0], n, m, vector[i][1]);
+    }	
+  }
+}
+
+void fit_norm () { 
+
+  xsum[0][0] = 0;
+  xsum[1][0] = 1;
+  xsum[0][1] = 0;
+
+  ysum[0][0] = 0;
+  ysum[1][0] = 0;
+  ysum[0][1] = 1;
+}
+
+/* evaluate the fit at (X,Y) to yield (x,y) */
+void fit_apply (double *x, double *y, double X, double Y) {
+
+  int m, n;
+  double xterm, yterm;
+  double Xo, Yo;
+
+  Xo = Yo = 0;
+  yterm = 1;
+  for (m = 0; m < NPOWR; m++) { 
+    xterm = 1;
+    for (n = 0; n < NPOWR - m; n++) {
+      Xo += xterm*yterm*xsum[n][m];
+      Yo += xterm*yterm*ysum[n][m];
+      xterm *= X;
+    }	
+    yterm *= Y;
+  }
+  
+  *x = Xo;
+  *y = Yo;
+}
+
+/* evaluate the x-derivative of the fit at (X,Y) to yield (x,y) */
+void fit_apply_dx (double *x, double *y, double X, double Y) {
+
+  int m, n;
+  double xterm, yterm;
+  double Xo, Yo;
+
+  Xo = Yo = 0;
+  yterm = 1;
+  for (m = 0; m < NPOWR; m++) { 
+    xterm = 1;
+    for (n = 1; n < NPOWR - m; n++) {
+      Xo += n*xterm*yterm*xsum[n][m];
+      Yo += n*xterm*yterm*ysum[n][m];
+      xterm *= X;
+    }	
+    yterm *= Y;
+  }
+  
+  *x = Xo;
+  *y = Yo;
+}
+
+/* evaluate the y-derivative of the fit at (X,Y) to yield (x,y) */
+void fit_apply_dy (double *x, double *y, double X, double Y) {
+
+  int m, n;
+  double xterm, yterm;
+  double Xo, Yo;
+
+  Xo = Yo = 0;
+  yterm = 1;
+  for (m = 1; m < NPOWR; m++) { 
+    xterm = 1;
+    for (n = 0; n < NPOWR - m; n++) {
+      Xo += m*xterm*yterm*xsum[n][m];
+      Yo += m*xterm*yterm*ysum[n][m];
+      xterm *= X;
+    }	
+    yterm *= Y;
+  }
+  
+  *x = Xo;
+  *y = Yo;
+}
+
+/* measure the residual scatter in the fit */
+double fit_scat (StarData *st, StarData *sr, Coords *coords) {
+
+  int i;
+  int Npair, *idx1, *idx2;
+  double x, y, dx, dy, dX, dY, dX2, dY2, dR;
+  
+  Npair = pair_lists (&idx1, &idx2);
+
+  dX = dY = dX2 = dY2 = 0;
+  for (i = 0; i < Npair; i++) {
+
+    /* projection this direction includes the error introduced by
+       the interation on the nonlinear solution */
+    RD_to_XY (&x, &y, sr[idx2[i]].R, sr[idx2[i]].D, coords);
+    
+    dx = x - st[idx1[i]].X;
+    dy = y - st[idx1[i]].Y;
+    
+    dX += dx;
+    dY += dy;
+    dX2 += dx*dx;
+    dY2 += dy*dy;
+  }
+
+  /* scatter is measured on the tangent plane in degrees */
+  dX = dX / Npair;
+  dY = dY / Npair;
+  fprintf (stderr, "scatter: %f, %f\n", sqrt(dX2/Npair - dX*dX), sqrt(dY2/Npair - dY*dY));
+  fprintf (stderr, "Npts: %d\n", Npair);
+
+  dR = 0.5 * sqrt(fabs(dX2/Npair - dX*dX)) + 0.5 * sqrt (fabs(dY2/Npair - dY*dY));
+  return (dR);
+}
+
+/* convert fit terms to coords and polyterms */
+/**** what do we do with the value of ctype??? 
+      can we leave it alone? ****/
+int fit_adjust (Coords *coords) {
+
+  int i;
+  double a10, a01, a20, a11, a02, a30, a21, a12, a03;
+  double b10, b01, b20, b11, b02, b30, b21, b12, b03;
+  double Xo, Yo, det;
+  double **A, **B, Fx, Fy;
+    
+  /* start with the linear solution for Xo,Yo */
+  coords[0].cdelt1 = hypot (xsum[1][0], ysum[1][0]);
+  coords[0].cdelt2 = hypot (xsum[0][1], ysum[0][1]);
+  // coords[0].cdelt1 = coords[0].cdelt2 = 1.0;
+
+  det = 1.0 / (xsum[1][0]*ysum[0][1] - xsum[0][1]*ysum[1][0]);
+  Xo = det*(ysum[0][0]*xsum[0][1] - xsum[0][0]*ysum[0][1]);
+  Yo = det*(xsum[0][0]*ysum[1][0] - ysum[0][0]*xsum[1][0]);
+
+  coords[0].Npolyterms = NORDER;
+
+  if (coords[0].Npolyterms > 1) {
+    /* use the linear solution as a starting guess */
+    /* solve for L(Xo,Yo) = 0, M(Xo,Yo) = 0 */
+    /* this is the Newton-Raphson method - it needs the high order terms to be small */
+    ALLOCATE (A, double *, 2);
+    ALLOCATE (B, double *, 2);
+    ALLOCATE (A[0], double, 2);
+    ALLOCATE (A[1], double, 2);
+    ALLOCATE (B[0], double, 1);
+    ALLOCATE (B[1], double, 1);
+
+    for (i = 0; i < 10; i++) {
+      fit_apply (&Fx, &Fy, Xo, Yo);
+      fit_apply_dx (&A[0][0], &A[0][1], Xo, Yo);
+      fit_apply_dy (&A[1][0], &A[1][1], Xo, Yo);
+      B[0][0] = -Fx;
+      B[1][0] = -Fy;
+      dgaussjordan (A, B, 2, 1);
+      Xo += B[0][0]; 
+      Yo += B[1][0];
+    }
+    free (A[0]); free (B[0]);
+    free (A[1]); free (B[1]);
+    free (A); free (B);
+  }
+  coords[0].crpix1 = Xo;
+  coords[0].crpix2 = Yo;
+
+  switch (coords[0].Npolyterms) {
+    case 0:
+    case 1:
+      /* the linear solution can be analytically inverted */
+      coords[0].pc1_1 = xsum[1][0] / coords[0].cdelt1;
+      coords[0].pc1_2 = xsum[0][1] / coords[0].cdelt2;
+      coords[0].pc2_1 = ysum[1][0] / coords[0].cdelt1;
+      coords[0].pc2_2 = ysum[0][1] / coords[0].cdelt2;
+      for (i = 0; i < 7; i++) {
+	coords[0].polyterms[i][0] = coords[0].polyterms[i][1] = 0.0;
+      }
+      break;
+
+    case 2:
+      a10 = xsum[1][0] + 2.0*xsum[2][0]*Xo + xsum[1][1]*Yo;
+      a01 = xsum[0][1] + 2.0*xsum[0][2]*Yo + xsum[1][1]*Xo;
+      a20 = xsum[2][0];
+      a11 = xsum[1][1];
+      a02 = xsum[0][2];
+
+      b10 = ysum[1][0] + 2.0*ysum[2][0]*Xo + ysum[1][1]*Yo;
+      b01 = ysum[0][1] + 2.0*ysum[0][2]*Yo + ysum[1][1]*Xo;
+      b20 = ysum[2][0];
+      b11 = ysum[1][1];
+      b02 = ysum[0][2];
+
+      coords[0].pc1_1 = a10 / coords[0].cdelt1;
+      coords[0].pc1_2 = a01 / coords[0].cdelt2;
+      coords[0].pc2_1 = b10 / coords[0].cdelt1;
+      coords[0].pc2_2 = b01 / coords[0].cdelt2;
+
+      coords[0].polyterms[0][0] = a20 / SQ(coords[0].cdelt1);
+      coords[0].polyterms[1][0] = a11 / (coords[0].cdelt1*coords[0].cdelt2);
+      coords[0].polyterms[2][0] = a02 / SQ(coords[0].cdelt2);
+
+      coords[0].polyterms[0][1] = b20 / SQ(coords[0].cdelt1);
+      coords[0].polyterms[1][1] = b11 / (coords[0].cdelt1*coords[0].cdelt2);
+      coords[0].polyterms[2][1] = b02 / SQ(coords[0].cdelt2);
+      for (i = 3; i < 7; i++) {
+	coords[0].polyterms[i][0] = coords[0].polyterms[i][1] = 0.0;
+      }
+      break;
+      
+    case 3:
+      a10 = xsum[1][0] + 2*xsum[2][0]*Xo +   xsum[1][1]*Yo + 3*xsum[3][0]*Xo*Xo + 2*xsum[2][1]*Xo*Yo + xsum[1][2]*Yo*Yo;
+      a01 = xsum[0][1] + 2*xsum[0][2]*Yo +   xsum[1][1]*Xo + 3*xsum[0][3]*Yo*Yo + 2*xsum[1][2]*Xo*Yo + xsum[2][1]*Xo*Xo;
+      a20 = xsum[2][0] + 3*xsum[3][0]*Xo +   xsum[2][1]*Yo;
+      a11 = xsum[1][1] + 2*xsum[2][1]*Xo + 2*xsum[1][2]*Yo;
+      a02 = xsum[0][2] + 3*xsum[0][3]*Yo +   xsum[1][2]*Xo;
+      a30 = xsum[3][0];
+      a21 = xsum[2][1];
+      a12 = xsum[1][2];
+      a03 = xsum[0][3];
+
+      b10 = ysum[1][0] + 2*ysum[2][0]*Xo +   ysum[1][1]*Yo + 3*ysum[3][0]*Xo*Xo + 2*ysum[2][1]*Xo*Yo + ysum[1][2]*Yo*Yo;
+      b01 = ysum[0][1] + 2*ysum[0][2]*Yo +   ysum[1][1]*Xo + 3*ysum[0][3]*Yo*Yo + 2*ysum[1][2]*Xo*Yo + ysum[2][1]*Xo*Xo;
+      b20 = ysum[2][0] + 3*ysum[3][0]*Xo +   ysum[2][1]*Yo;
+      b11 = ysum[1][1] + 2*ysum[2][1]*Xo + 2*ysum[1][2]*Yo;
+      b02 = ysum[0][2] + 3*ysum[0][3]*Yo +   ysum[1][2]*Xo;
+      b30 = ysum[3][0];
+      b21 = ysum[2][1];
+      b12 = ysum[1][2];
+      b03 = ysum[0][3];
+
+      coords[0].pc1_1 = a10 / coords[0].cdelt1;
+      coords[0].pc1_2 = a01 / coords[0].cdelt2;
+      coords[0].pc2_1 = b10 / coords[0].cdelt1;
+      coords[0].pc2_2 = b01 / coords[0].cdelt2;
+
+      coords[0].polyterms[0][0] = a20 / SQ(coords[0].cdelt1);
+      coords[0].polyterms[1][0] = a11 / (coords[0].cdelt1*coords[0].cdelt2);
+      coords[0].polyterms[2][0] = a02 / SQ(coords[0].cdelt2);
+
+      coords[0].polyterms[3][0] = a30 / (SQ(coords[0].cdelt1)*coords[0].cdelt1);
+      coords[0].polyterms[4][0] = a21 / (SQ(coords[0].cdelt1)*coords[0].cdelt2);
+      coords[0].polyterms[5][0] = a12 / (SQ(coords[0].cdelt2)*coords[0].cdelt1);
+      coords[0].polyterms[6][0] = a03 / (SQ(coords[0].cdelt2)*coords[0].cdelt2);
+
+      coords[0].polyterms[0][1] = b20 / SQ(coords[0].cdelt1);
+      coords[0].polyterms[1][1] = b11 / (coords[0].cdelt1*coords[0].cdelt2);
+      coords[0].polyterms[2][1] = b02 / SQ(coords[0].cdelt2);
+
+      coords[0].polyterms[3][1] = b30 / (SQ(coords[0].cdelt1)*coords[0].cdelt1);
+      coords[0].polyterms[4][1] = b21 / (SQ(coords[0].cdelt1)*coords[0].cdelt2);
+      coords[0].polyterms[5][1] = b12 / (SQ(coords[0].cdelt2)*coords[0].cdelt1);
+      coords[0].polyterms[6][1] = b03 / (SQ(coords[0].cdelt2)*coords[0].cdelt2);
+      break;
+
+    default:
+      fprintf (stderr, "error: invalid order %d\n", coords[0].Npolyterms);
+      exit (2);
+  }
+
+  while (coords[0].crval1 < 0) coords[0].crval1 += 360.0;
+  while (coords[0].crval1 > 360.0) coords[0].crval1 -= 360.0;
+
+  /* test for valid solution */
+  return (Npts);
+}
+
+int mk_polyterm (int n, int m, int norder) {
+  
+  int i, nt, N;
+  
+  N = 0;
+  nt = n + m;
+  for (i = 2; i < nt; i++) {
+    N += i + 1;
+  }
+  N += m;
+  return (N);
+}
+
+int mk_vector (int n, int m, int norder) {
+  
+  int i, N;
+  
+  N = 0;
+  for (i = 0; i < m; i++) {
+    N += (norder - i + 1);
+  }
+  N += n;
+  return (N);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/remove_clumps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/remove_clumps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/remove_clumps.c	(revision 22322)
@@ -0,0 +1,94 @@
+# include "gastro2.h"
+
+StarData *remove_clumps (StarData *instars, int *nstars, int NX, int NY) {
+
+  int i, j, nx, ny, Npix, nn, x, y, pix, Nstars;
+  double s1, s2, mean, sigma, cutoff;
+  int *hist, *xcld;
+  int nxcld, Nxcld, Nout;
+  
+  StarData *outstars;
+
+  /* create histogram of pixels using coords */
+
+  Nstars = *nstars;
+
+  Npix = 100;
+  nx = (int) (NX / Npix) + 1;
+  ny = (int) (NY / Npix) + 1;
+  nn = nx * ny;
+
+  ALLOCATE (hist, int, nn);
+  ALLOCATE (xcld, int, nn);
+  bzero (hist, nn*sizeof(int));
+
+  for (i = 0; i < Nstars; i++) {
+    
+    x = (int) (instars[i].X / Npix);
+    y = (int) (instars[i].Y / Npix);
+    pix = x + nx * y;
+
+    if ((pix < 0) || (pix >= nn)) { 
+      fprintf (stderr, "! %f %f  %d %d  %d %d\n", instars[i].X, instars[i].Y, x, y, pix, nn);
+      continue;
+    }
+    
+    hist[pix] ++;
+
+  }
+
+  /* find stats on histogram */
+  s1 = s2 = 0;
+  for (i = 0; i < nn; i++) {
+    s1 += hist[i];
+    s2 += hist[i]*hist[i];
+  }
+  mean  = s1 / nn;
+  sigma = 2 + sqrt (s2 / nn - mean*mean);
+  cutoff = mean + 5*sigma;
+
+  /* identify clumps to exclude */
+  Nxcld = 0;
+  for (i = 0; i < nn; i++) {
+    if (hist[i] > cutoff) {
+      y = (int) (i / nx);
+      x = i - y*nx;
+      fprintf (stderr, "cut: %d  %d %d  %d\n", i, x, y, hist[i]);
+      xcld[Nxcld] = i;
+      Nxcld ++;
+    }
+  }
+
+  /* identify stars to exclude (type = -1) */
+  for (i = 0; i < Nxcld; i++) {
+
+    nxcld = 0;
+    for (j = 0; j < Nstars; j++) {
+      
+      x = (int) (instars[j].X / Npix);
+      y = (int) (instars[j].Y / Npix);
+      pix = x + nx * y;
+      if (pix != xcld[i]) continue;
+      nxcld ++;      
+      instars[j].type = -1;
+    }
+    fprintf (stderr, "exclude %d in clump %d\n", nxcld, i);
+  }
+
+  ALLOCATE (outstars, StarData, Nstars);
+  Nout = 0;
+
+  for (i = 0; i < Nstars; i++) {
+    if (instars[i].type == -1) continue;
+    outstars[Nout] = instars[i];
+    Nout ++;
+  } 
+
+  REALLOCATE (outstars, StarData, Nout);
+  *nstars = Nout;
+  fprintf (stderr, "keeping %d of %d stars\n", Nout, Nstars);
+  return (outstars);
+
+}
+  
+     
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/rfits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/rfits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/rfits.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "gastro2.h"
+
+StarData *rfits (FILE *f, int *nstars) {
+
+  int i, N, Nstars;
+  Header theader;
+  FTable table;
+  SMPData  *stars;
+  StarData *stardata;
+
+  /* init & load in table data */
+  table.header   = &theader;
+  if (!gfits_fread_ftable (f, &table, "SMPFILE")) goto escape;
+
+  stars = gfits_table_get_SMPData (&table, &Nstars, NULL);
+
+  ALLOCATE (stardata, StarData, Nstars);
+  for (i = N = 0; i < Nstars; i++) {
+    /* hardwired dophot exclusions should eventually be encapsulated elsewhere */
+    if (stars[i].dophot == 4) continue;
+    if (stars[i].dophot == 5) continue;
+    if (stars[i].dophot == 6) continue;
+    if (stars[i].dophot == 9) continue;
+    if ((MAX_ERROR > 0) && (stars[i].dM > MAX_ERROR)) continue;
+    stardata[N].X    = stars[i].X;
+    stardata[N].Y    = stars[i].Y;
+    stardata[N].M    = stars[i].M;
+    stardata[N].dM   = 1000*stars[i].dM;
+    stardata[N].type = stars[i].dophot;
+    stardata[N].R    = 0;
+    stardata[N].D    = 0;
+    N++;
+  }    
+  if (N < 5) { 
+    fprintf (stderr, "ERROR: too few stars for reliable solution, only %d\n", N);
+    exit (1);
+  }
+
+  REALLOCATE (stardata, StarData, MAX(N,1));
+  *nstars = N;
+  return (stardata);
+
+escape:
+  fprintf (stderr, "error reading file\n");
+  exit (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/rotate2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/rotate2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/rotate2.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "gastro2.h"
+
+void rotate (RefCatalog *Subset, RefCatalog *Ref, double angle) {
+  
+  int i;
+  double dX, dY, DX, DY, CS, SN;
+  double theta;
+  StarData *in, *out;
+
+  Ref[0] = Subset[0];
+
+  ALLOCATE (Ref[0].stars, StarData, MAX (1, Ref[0].N));
+  bcopy (Subset[0].stars, Ref[0].stars, Ref[0].N*sizeof(StarData));
+
+  if (angle == 0.0) return;
+
+  theta = (angle*RAD_DEG);
+  CS = cos (theta);
+  SN = sin (theta);
+
+  in  = Subset[0].stars;
+  out = Ref[0].stars;
+
+  for (i = 0; i < Ref[0].N; i++) {
+    dX = in[i].X;
+    dY = in[i].Y;
+    
+    DX = dX * CS - dY * SN;
+    DY = dX * SN + dY * CS;
+    
+    out[i].X = DX;
+    out[i].Y = DY;
+  }
+    
+}
+
+/* rotate the star list by an angle ccw from x axis */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/rtext.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/rtext.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/rtext.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include "gastro2.h"
+/* by necesity hard wired */
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+StarData *rtext (FILE *f, int *nstars) {
+
+  char *buffer;
+  int i, N, Nbytes, nbytes, Ninstar, Nstars, NSTARS;
+  double dmag, type;
+  StarData *stars;
+
+  NSTARS = *nstars;
+  ALLOCATE (stars, StarData, MAX (NSTARS, 1));
+  Nbytes = NSTARS*BYTES_STAR;
+
+  N = Nstars = 0;
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR));
+  
+  while ((nbytes = fread (buffer, 1, (BLOCK*BYTES_STAR), f)) != 0) {
+    Ninstar = nbytes / BYTES_STAR;
+    for (i = 0; i < Ninstar; i++, Nstars++) {
+      dparse (&stars[N].X, 1, &buffer[i*BYTES_STAR]);
+      dparse (&stars[N].Y, 2, &buffer[i*BYTES_STAR]);
+      dparse (&stars[N].M, 3, &buffer[i*BYTES_STAR]);
+      dparse (&dmag,       4, &buffer[i*BYTES_STAR]);
+      dparse (&type,       5, &buffer[i*BYTES_STAR]);
+
+      /* hardwired dophot exclusions should eventually be encapsulated elsewhere */
+      if ((type == 4) || (type == 6) || (type == 5) || (type == 9)) continue;
+      if ((MAX_ERROR > 0) && (dmag > 1000*MAX_ERROR)) continue;
+      stars[N].dM   = 0.001 * dmag;
+      stars[N].type = type;
+      N++;
+    }
+  }
+  free (buffer);
+ 
+  if (Nstars != NSTARS) {
+    fprintf (stderr, "WARNING: only read %d of %d stars\n", Nstars, NSTARS);
+  }
+  if (N < 5) { 
+    fprintf (stderr, "ERROR: too few stars for reliable solution, only %d\n", N);
+    exit (1);
+  }
+  *nstars = N;
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/sort.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/sort.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gastro2/src/sort.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "gastro2.h"
+
+void sort_stars_mag (StarData *stars, int N) {
+
+# define SWAPFUNC(A,B){ StarData tmp; tmp = stars[A]; stars[A] = stars[B]; stars[B] = tmp; }
+# define COMPARE(A,B)(stars[A].M < stars[B].M)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void sort_stars_X (StarData *stars, int N) {
+
+# define SWAPFUNC(A,B){ StarData tmp; tmp = stars[A]; stars[A] = stars[B]; stars[B] = tmp; }
+# define COMPARE(A,B)(stars[A].X < stars[B].X)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/Makefile	(revision 22322)
@@ -0,0 +1,29 @@
+default: gcompare
+help:
+	@echo "make options: gcompare (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/gcompare
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+MAN	=	$(HOME)/doc
+SRC	=	$(HOME)/src
+INC	=	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+gcompare: $(BIN)/gcompare.$(ARCH)
+install: $(DESTBIN)/gcompare
+
+GCOMPARE 	= \
+$(SRC)/input.$(ARCH).o 				$(SRC)/output.$(ARCH).o    		\
+$(SRC)/compare.$(ARCH).o 			$(SRC)/sort.$(ARCH).o      		\
+$(SRC)/count_neighbors.$(ARCH).o                $(SRC)/nextline.$(ARCH).o		\
+$(SRC)/nextword.$(ARCH).o			$(SRC)/gcompare.$(ARCH).o
+
+$(GCOMPARE): $(INC)/gcompare.h
+$(BIN)/gcompare.$(ARCH): $(GCOMPARE)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/doc/gcompare.1
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/doc/gcompare.1	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/doc/gcompare.1	(revision 22322)
@@ -0,0 +1,52 @@
+.ad l
+.nh
+.TH gcompare 1.0 "Sept 18, 1992" "Version 1.0"
+.SH NAME
+\fIgcompare:\fP  2-D intercomparison between 2 files
+
+.SH SYNOPSIS
+.B gcompare [-d -m -n1 -n2 -s1 N -s2 N -c X Y] file1 X1 Y1 file2 X2 Y2 radius
+
+.SH DESCRIPTION
+\fIgcompare\fP takes two files and compares the entries with one with
+the entries in the second.  Two columns (X and Y) which contain the
+coordinates are given.  Lines in the second file with coordinates
+within the given radius of the first file are selected.  Depending on
+the optional flags, any of the following are printed:  
+1) the coordinate difference for these matched lines,
+2) both matched lines (concatenated into a single line)
+3) \fIunmatched\fP lines from file 1 
+4) \fIunmatched\fP lines from file 2 
+
+.SS OPTIONS
+.TP
+.I "-d"
+Include in the output the coordinate differences for the two files
+.TP
+.I "-m"
+Include in the output the matched lines from the two files
+.TP
+.I "-n1"
+Include in the output the unmatched lines from file 1
+.TP
+.I "-n2"
+Include in the output the unmatched lines from file 2
+.TP
+.I "-s1 N"
+skip the first N lines of file 1
+.TP
+.I "-s2 N"
+skip the first N lines of file 2
+.TP
+.I "-c X Y"
+offset the coordinates of file 2 by (X,Y)
+.TP
+.I "-h, -help"
+print a short help summary
+
+
+.SH COPYRIGHT
+Copyright 1993 Eugene Magnier
+.PP                                                
+.SH AUTHOR
+Eugene Magnier.  MIT - CSR, UvA
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/include/gcompare.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/include/gcompare.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/include/gcompare.h	(revision 22322)
@@ -0,0 +1,38 @@
+# include <math.h>
+# include <ohana.h>
+
+typedef struct {
+  double  X, Y;
+  char   *line;
+  int     match;
+} value_type;
+
+typedef struct {
+  value_type *values;
+  char       *buffer;
+  int        Nvalues;
+} data_type;
+
+
+typedef struct {
+  char *line1;
+  char *line2;
+  double dX, dY;
+} match_type;
+
+/******* PROTOTYPES ***********/
+
+void               help              PROTO(());
+data_type          input             PROTO((char *, int, int, int));
+match_type        *compare           PROTO((data_type, data_type, int *, double, double, double, double));
+int                get_argument      PROTO((int, char **, char *));
+int                remove_argument   PROTO((int, int *, char **));
+void               data_sort         PROTO((data_type));
+void               output            PROTO((data_type, data_type, match_type *, int, int, int, int, int));
+char              *nextword          PROTO((char *));
+int                parse             PROTO((double *, int, char *));
+char              *nextline          PROTO((char *));
+int                scan_line         PROTO((FILE *, char *));
+
+extern double hypot PROTO((double, double));
+/*  this seems to be a problem: is not included from math.h with the -ansi flag */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/compare.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/compare.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/compare.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "gcompare.h"
+# define D_NMATCH 500;
+
+match_type *compare (data_type data1, data_type data2, int *Nmatches, double radius, double DX, double DY, double noauto) {
+
+  int i, j, first_j, Nmatch, NMATCH;
+  double dX, dY, dR;
+  match_type *match;
+
+  fprintf (stderr, "%f  %f\n", DX, DY);
+  Nmatch = 0;
+  NMATCH = D_NMATCH;
+  ALLOCATE (match, match_type, NMATCH);
+
+  for (i = j = 0;(i < data1.Nvalues) && (j < data2.Nvalues);) {
+    
+    dX = data1.values[i].X - data2.values[j].X - DX;
+
+    if (!(i % 100))
+      fprintf (stderr, ".");
+    
+    if (dX <= -radius)
+      i++;
+    if (dX >= radius)
+      j++;
+
+    if (fabs (dX) < radius) {
+      first_j = j;
+      for (j = first_j; (fabs (dX) < radius) && (j < data2.Nvalues); j++) {
+	dX = data1.values[i].X - data2.values[j].X - DX;
+	dY = data1.values[i].Y - data2.values[j].Y - DY;
+	dR = hypot (dX, dY);
+	if ((dR < radius) && ((noauto == 0) || (dR > noauto))) {
+	  match [Nmatch].line1 = data1.values[i].line;
+	  match [Nmatch].line2 = data2.values[j].line;
+	  match [Nmatch].dX = data1.values[i].X - data2.values[j].X - DX;
+	  match [Nmatch].dY = data1.values[i].Y - data2.values[j].Y - DY;
+	  data1.values[i].match = TRUE;
+	  data2.values[j].match = TRUE;
+	  Nmatch ++;
+	  if (Nmatch == NMATCH - 1) {
+	    NMATCH += D_NMATCH;
+	    REALLOCATE (match, match_type, NMATCH);
+	  }
+	}
+      }
+      j = first_j;
+      i++;
+    }
+
+  }
+  *Nmatches = Nmatch;
+  return (match);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/count_neighbors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/count_neighbors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/count_neighbors.c	(revision 22322)
@@ -0,0 +1,51 @@
+# include "gcompare.h"
+# define D_NMATCH 500;
+
+int count_neighbors (data, radius, DX, DY)
+data_type  data;
+double     radius;
+double     DX;
+double     DY;
+{
+
+  int i, j, first_j, Nfriends;
+  double dX, dY, dR;
+
+  fprintf (stderr, "%f  %f\n", DX, DY);
+
+  for (i = j = 0; (i < data.Nvalues) && (j < data.Nvalues);) {
+    
+    dX = data.values[i].X - data.values[j].X - DX;
+
+    if (!(i % 100))
+      fprintf (stderr, ".");
+    
+    Nfriends = 0;
+
+    if (dX <= -radius) {
+      fprintf (stdout, "%f %f %d\n", data.values[i].X, data.values[i].Y, Nfriends);
+      i++;
+    }
+    if (dX >= radius)
+      j++;
+
+    if (fabs (dX) < radius) {
+      first_j = j;
+      for (j = first_j; (fabs (dX) < radius) && (j < data.Nvalues); j++) {
+	if (i == j) continue;
+	dX = data.values[i].X - data.values[j].X - DX;
+	dY = data.values[i].Y - data.values[j].Y - DY;
+	dR = hypot (dX, dY);
+	if (dR < radius) {
+	  Nfriends ++;
+	}
+      }
+      j = first_j;
+      fprintf (stdout, "%f %f %d\n", data.values[i].X, data.values[i].Y, Nfriends);
+      i++;
+    }
+
+  }
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/find_matches.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/find_matches.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/find_matches.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "gcompare.h"
+
+table_type *find_matches (table, values, Nvalues, catalog, Ncatalog, noauto)
+table_type   table[];
+value_type   values[];
+int          Nvalues;
+catalog_type catalog[];
+int          Ncatalog;
+int          noauto;
+{
+
+  int i, j, start, done;
+  double delta_Dec, dRA, dDec, radius;
+
+  delta_Dec = (catalog[Ncatalog - 1].Dec - catalog[0].Dec);
+  for (i = 0; i < Nvalues; i++) {
+    if ((values[i].Dec > catalog[Ncatalog - 1].Dec) || (values[i].Dec < catalog[0].Dec))
+      continue;
+    start = Ncatalog * (values[i].Dec - catalog[0].Dec) / delta_Dec;
+    done = FALSE;
+    while (!done) {
+      if ((catalog[start].Dec > values[i].Dec - values[i].radius) && (start != 0)) {
+	start -= 100;
+	if (start < 0)
+	  start = 0;
+      }
+      else
+	done = TRUE;
+    }
+    for (j = start; ((j < Ncatalog) && 
+		     (catalog[j].Dec < values[i].Dec + values[i].radius)); j++) {
+      dRA = (values[i].RA - catalog[j].RA) / cos (DEG_RAD*values[i].Dec);
+      dDec = values[i].Dec - catalog[j].Dec;
+      
+      radius = hypot (dRA, dDec);
+      if ((radius <= values[i].radius) && (!noauto || (radius > 0))) {
+	ALLOCATE (table[i].match[table[i].Nmatches], char, NBYTES_LINE + 1);
+	bzero (table[i].match[table[i].Nmatches], NBYTES_LINE + 1);
+	strcpy (table[i].match[table[i].Nmatches], catalog[j].line);
+	table[i].Nmatches ++;
+	if (table[i].Nmatches > 500)
+	  fprintf (stderr, "too many objects! \n");
+      }
+    }
+  }
+
+  return (table);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/gcompare.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/gcompare.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/gcompare.c	(revision 22322)
@@ -0,0 +1,101 @@
+# include "gcompare.h"
+
+int main (int argc, char **argv) {
+  
+  /* USAGE:  gcompare file1 X1 Y1 file2 X2 Y2 radius */
+  
+  int Nskip1, Nskip2, Nmatches, N;
+  int X1, X2, Y1, Y2, deltas, match, nomatch1, nomatch2;
+  data_type data1, data2;
+  match_type *matches;
+  double radius, DX, DY, noauto;
+
+  DX = DY = Nskip1 = Nskip2 = 0;
+  noauto = match = nomatch1 = nomatch2 = deltas = FALSE;
+
+  if (remove_argument(get_argument (argc, argv, "-d"), &argc, argv))
+    deltas = TRUE;
+  if (remove_argument(get_argument (argc, argv, "-h"), &argc, argv))
+    help();
+  if (remove_argument(get_argument (argc, argv, "-help"), &argc, argv))
+    help();
+  if (remove_argument(get_argument (argc, argv, "-m"), &argc, argv))
+    match = TRUE;
+  if (remove_argument(get_argument (argc, argv, "-n1"), &argc, argv))
+    nomatch1 = TRUE;
+  if (remove_argument(get_argument (argc, argv, "-n2"), &argc, argv))
+    nomatch2 = TRUE;
+  if (!(match || deltas || nomatch1 || nomatch2)) {
+    fprintf (stderr, "no output mode, use at least one of -d, -m, -n1, or -n2\n");
+    exit (2);
+  }
+  if ((N = get_argument (argc, argv, "-na"))) {
+    remove_argument(N, &argc, argv);
+    noauto = atof(argv[N]);
+    remove_argument(N, &argc, argv);
+  }
+    
+  if ((N = get_argument (argc, argv, "-c")) && (N + 2 < argc)) {
+    DX = atof(argv[N + 1]);
+    DY = atof(argv[N + 2]);
+    remove_argument(N, &argc, argv);
+    remove_argument(N, &argc, argv);
+    remove_argument(N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-s1"))) {
+    Nskip1 = atof(argv[N + 1]);
+    remove_argument(N, &argc, argv);
+    remove_argument(N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-s2")) && (N + 1 < argc)) {
+    Nskip2 = atof(argv[N + 1]);
+    remove_argument(N, &argc, argv);
+    remove_argument(N, &argc, argv);
+  }
+  
+  if (argc != 8) {
+    fprintf (stderr, "gcompare [mode] file1 X1 Y1 file2 X2 Y2 radius\n");
+    exit (2);
+  }
+
+  X1 = atof (argv[2]);
+  Y1 = atof (argv[3]);
+  X2 = atof (argv[5]);
+  Y2 = atof (argv[6]);
+  radius = atof(argv[7]);
+  fprintf (stderr, "radius = %f %s\n", radius, argv[7]);
+
+  data1 = input (argv[1], X1, Y1, Nskip1);
+  fprintf (stderr, "list 1: %d values %d %d\n", data1.Nvalues, X1, Y1);
+  data2 = input (argv[4], X2, Y2, Nskip2);
+  fprintf (stderr, "list 2: %d values %d %d\n", data2.Nvalues, X2, Y2);
+
+  data_sort (data1); fprintf (stderr, "sorted 1\n");
+  data_sort (data2); fprintf (stderr, "sorted 2\n");
+
+  fprintf (stderr, "noauto: %e\n", noauto);
+  matches = compare (data1, data2, &Nmatches, radius, DX, DY, noauto);
+
+  output (data1, data2, matches, Nmatches, match, deltas, nomatch1, nomatch2);
+  exit (0);
+}
+
+
+
+void
+help() {
+
+  fprintf (stderr, "gcompare [mode] file1 X1 Y1 file2 X2 Y2 radius\n");
+  fprintf (stderr, " modes:\n");
+  fprintf (stderr, "  -d      return delta coords\n");
+  fprintf (stderr, "  -m      return matched lines\n");
+  fprintf (stderr, "  -n1     return unmatched lines, file 1\n");
+  fprintf (stderr, "  -n2     return unmatched lines, file 2\n");
+  fprintf (stderr, " additional options:\n");
+  fprintf (stderr, "  -c X Y    set offset (file1 - file2)\n");
+  fprintf (stderr, "  -s1 N     skip N lines from file 1\n");
+  fprintf (stderr, "  -s2 N     skip N lines from file 2\n");
+  fprintf (stderr, "  -na X     eliminate auto-matches (radius < X)\n");
+  exit (2);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/input.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/input.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/input.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "gcompare.h"
+# define D_NVALUES 1000
+# define D_NBYTES  10000
+
+data_type input (filename, X, Y, Nskip)
+char *filename;
+int   X;
+int   Y;
+int   Nskip;
+{
+  
+  data_type data;
+  int i, status, n, NVALUES, NBYTES, nbytes;
+  FILE *f;
+  char dummy_line[1000], *next;
+  
+  n = 0;
+
+  if (!strcmp (filename, "-")) {
+    f = stdin;
+  }
+  else {
+    f = fopen (filename, "r");
+    if (f == NULL) {
+      fprintf (stderr, "error opening file %s\n", filename);
+      exit (0);
+    }
+  }
+
+  for (i = 0; i < Nskip; i++) 
+    scan_line (f, dummy_line);
+ 
+  /* read in entire file */
+  NBYTES = D_NBYTES + 1;
+  ALLOCATE (data.buffer, char, NBYTES);
+  for (i = 0, nbytes = D_NBYTES; nbytes == D_NBYTES; i++) {
+    nbytes = fread (&data.buffer[i*D_NBYTES], 1, D_NBYTES, f);
+    NBYTES += D_NBYTES;
+    REALLOCATE (data.buffer, char, NBYTES);
+  }
+  NBYTES -= 2*D_NBYTES - nbytes;
+  data.buffer[NBYTES] = 0;
+  fprintf (stderr, "got %d bytes\n", NBYTES);
+
+  NVALUES = D_NVALUES;
+  ALLOCATE (data.values, value_type, NVALUES);
+  next = data.buffer;
+  for (i = 0; next != (char *) NULL; ) {
+    data.values[i].line  = next;
+    data.values[i].match = FALSE;
+    status  = dparse (&data.values[i].X, X, data.values[i].line);
+    status &= dparse (&data.values[i].Y, Y, data.values[i].line);
+    next = nextline (data.values[i].line);
+    if (status && (data.values[i].line[0] != '#')) {
+      i++;
+    }
+    if (i == NVALUES - 3) {
+      NVALUES += D_NVALUES;
+      REALLOCATE (data.values, value_type, NVALUES);
+    }
+  }
+  data.Nvalues = i;
+  return (data);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/neighbors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/neighbors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/neighbors.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "gcompare.h"
+
+void main (argc, argv)
+int    argc;
+char **argv;
+{
+  
+  /* USAGE:  gcompare file1 X1 Y1 file2 X2 Y2 radius */
+  
+  int i, Nskip1, Nskip2, Nmatches, N;
+  int X1, X2, Y1, Y2, deltas, match, nomatch1, nomatch2, noauto;
+  data_type data1, data2;
+  match_type *matches;
+  double radius, DX, DY;
+
+  DX = DY = Nskip1 = Nskip2 = 0;
+
+  if (remove_argument(get_argument (argc, argv, "-h"), &argc, argv))
+    help();
+  if (remove_argument(get_argument (argc, argv, "-help"), &argc, argv))
+    help();
+    
+  if ((N = get_argument (argc, argv, "-c")) && (N + 2 < argc)) {
+    DX = atof(argv[N + 1]);
+    DY = atof(argv[N + 2]);
+    remove_argument(N, &argc, argv);
+    remove_argument(N, &argc, argv);
+    remove_argument(N, &argc, argv);
+  }
+  if (N = get_argument (argc, argv, "-s1")) {
+    Nskip1 = atof(argv[N + 1]);
+    remove_argument(N, &argc, argv);
+    remove_argument(N, &argc, argv);
+  }
+  
+  if (argc != 5) {
+    fprintf (stderr, "neighbors [mode] file X Y radius\n");
+    exit (0);
+  }
+
+  X1 = atof (argv[2]);
+  Y1 = atof (argv[3]);
+  radius = atof(argv[4]);
+
+  data1 = input (argv[1], X1, Y1, Nskip1);
+  fprintf (stderr, "list 1: %d values %d %d\n", data1.Nvalues, X1, Y1);
+
+  data_sort (data1); fprintf (stderr, "sorted 1\n");
+
+  count_neighbors (data1, radius, DX, DY);
+
+}
+
+
+
+void
+help() {
+
+  fprintf (stderr, "gcompare [mode] file1 X1 Y1 file2 X2 Y2 radius\n");
+  fprintf (stderr, " modes:\n");
+  fprintf (stderr, "  -d      return delta coords\n");
+  fprintf (stderr, "  -m      return matched lines\n");
+  fprintf (stderr, "  -n1     return unmatched lines, file 1\n");
+  fprintf (stderr, "  -n2     return unmatched lines, file 2\n");
+  fprintf (stderr, " additional options:\n");
+  fprintf (stderr, "  -c X Y    set offset (file1 - file2)\n");
+  fprintf (stderr, "  -s1 N     skip N lines from file 1\n");
+  fprintf (stderr, "  -s2 N     skip N lines from file 2\n");
+  fprintf (stderr, "  -na X     eliminate auto-matches (radius < X)\n");
+  exit (0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/nextline.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/nextline.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/nextline.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "gcompare.h"
+
+char *nextline (line)
+char line[];
+{
+
+  char *ret;
+  
+  ret = strchr (line, '\n');
+  if (ret != NULL) { 
+    *ret = 0;
+    ret += 1;
+  }
+
+  return (ret);
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/nextword.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/nextword.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/nextword.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include "gcompare.h"
+
+char *nextword(string)
+char *string;
+{
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (; isspace (*string); string++);
+  for (; (*string != 0) && !isspace (*string); string++);
+  for (; isspace (*string); string++);
+  return (string);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/output.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/output.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/output.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "gcompare.h"
+
+void output (data1, data2, matches, Nmatches, match, deltas, nomatch1, nomatch2)
+data_type  data1;
+data_type  data2;
+match_type matches[];
+int        Nmatches;
+int        match;
+int        deltas; 
+int        nomatch1;
+int        nomatch2;
+{
+
+  int i, Nmatch1, Nmatch2;
+
+  fprintf (stderr, "This is a Gcompare output list\n");
+  fprintf (stderr, "There are %d matches\n\n", Nmatches);
+  Nmatch1 = Nmatch2 = 0.0;
+
+  if (match || deltas) {
+    for (i = 0; i < Nmatches; i++) {
+      if (deltas)
+	fprintf (stdout, "%15.9f  %15.9f  ", matches[i].dX, matches[i].dY);
+      if (match)
+	fprintf (stdout, "%s  %s", matches[i].line1, matches[i].line2);
+      fprintf (stdout, "\n");
+    }
+  }
+
+  if (match && nomatch1) 
+    fprintf (stdout, "\n non-matces from file 1:\n");
+  
+  if (nomatch1) {
+    for (i = 0; i < data1.Nvalues; i++) {
+      if (!data1.values[i].match) {
+	fprintf (stdout, "%s\n", data1.values[i].line);
+	Nmatch1 ++;
+      }
+    }
+    fprintf (stderr, "no matches in file 1: %d\n", Nmatch1);
+  }
+
+  if ((match || nomatch1) && nomatch2)
+    fprintf (stdout, "\n non-matces from file 2:\n");
+
+  if (nomatch2) {
+    for (i = 0; i < data2.Nvalues; i++) {
+      if (!data2.values[i].match) {
+	fprintf (stdout, "%s\n", data2.values[i].line);
+	Nmatch2++;
+      }
+    }
+    fprintf (stderr, "no matches in file 2: %d\n", Nmatch2);
+  }
+
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/sort.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/sort.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gcompare/src/sort.c	(revision 22322)
@@ -0,0 +1,13 @@
+# include "gcompare.h"
+
+void data_sort (data_type data) {
+
+# define SWAPFUNC(A,B){ value_type tmp; tmp = data.values[A]; data.values[A] = data.values[B]; data.values[B] = tmp; }
+# define COMPARE(A,B)(data.values[A].X < data.values[B].X)
+
+  OHANA_SORT (data.Nvalues, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+bin
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/Makefile	(revision 22322)
@@ -0,0 +1,74 @@
+default: all
+help:
+	@echo "make options: getstar (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/getstar
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+getstar: $(BIN)/getstar.$(ARCH)
+getstar.install: $(DESTBIN)/getstar
+
+dvoImageOverlaps: $(BIN)/dvoImageOverlaps.$(ARCH)
+dvoImageOverlaps.install: $(DESTBIN)/dvoImageOverlaps
+
+dvoImageExtract: $(BIN)/dvoImageExtract.$(ARCH)
+dvoImageExtract.install: $(DESTBIN)/dvoImageExtract
+
+all: getstar dvoImageOverlaps dvoImageExtract
+install: getstar.install dvoImageOverlaps.install dvoImageExtract.install
+
+GETSTAR = \
+$(SRC)/getstar.$(ARCH).o 	  \
+$(SRC)/args.$(ARCH).o		  \
+$(SRC)/ConfigInit.$(ARCH).o 	  \
+$(SRC)/Shutdown.$(ARCH).o	  \
+$(SRC)/SetSignals.$(ARCH).o	  \
+$(SRC)/select_by_region.$(ARCH).o \
+$(SRC)/write_catalog.$(ARCH).o    \
+$(SRC)/write_getstar_ps1_dev_0.$(ARCH).o \
+$(SRC)/write_getstar_ps1_dev_1.$(ARCH).o \
+$(SRC)/write_getstar_ps1_dev_2.$(ARCH).o 
+
+$(GETSTAR): $(INC)/getstar.h $(DVO_INCS)
+$(BIN)/getstar.$(ARCH): $(GETSTAR) $(DVO_LIBS)
+
+OVERLAPS = \
+$(SRC)/dvoImageOverlaps.$(ARCH).o 	 \
+$(SRC)/args_overlaps.$(ARCH).o		 \
+$(SRC)/ConfigInit_overlaps.$(ARCH).o 	 \
+$(SRC)/Shutdown.$(ARCH).o	         \
+$(SRC)/SetSignals.$(ARCH).o	         \
+$(SRC)/ReadImageFiles.$(ARCH).o          \
+$(SRC)/ReadImageHeader.$(ARCH).o         \
+$(SRC)/ListImageOverlaps.$(ARCH).o       \
+$(SRC)/GetFileMode.$(ARCH).o             \
+$(SRC)/edge_check.$(ARCH).o              \
+$(SRC)/opening_angle.$(ARCH).o           \
+$(SRC)/MatchImages.$(ARCH).o
+
+$(OVERLAPS): $(INC)/dvoImageOverlaps.h $(DVO_INCS)
+$(BIN)/dvoImageOverlaps.$(ARCH): $(OVERLAPS) $(DVO_LIBS)
+
+EXTRACT = \
+$(SRC)/dvoImageExtract.$(ARCH).o 	 \
+$(SRC)/ConfigInit_extract.$(ARCH).o 	 \
+$(SRC)/args_extract.$(ARCH).o		 \
+$(SRC)/Shutdown.$(ARCH).o	         \
+$(SRC)/SetSignals.$(ARCH).o	         \
+$(SRC)/WriteImages.$(ARCH).o             \
+$(SRC)/WriteImageFITS.$(ARCH).o          \
+$(SRC)/SelectImages.$(ARCH).o       
+
+$(EXTRACT): $(INC)/dvoImageExtract.h $(DVO_INCS)
+$(BIN)/dvoImageExtract.$(ARCH): $(EXTRACT) $(DVO_LIBS)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,8 @@
+ getstar-1-2
+  * added maximum magnitude selection
+
+ getstar-1-1
+  * converted to gfits APIs (forces libfits 1.6)
+
+ getstar-1-0
+    basic program, not really tested (not sure why this is tagged)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/doc/database.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/doc/database.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/doc/database.txt	(revision 22322)
@@ -0,0 +1,91 @@
+
+\begin{verbatim}
+
+typedef struct {
+  float R;                    /* RA  in decimal degrees */
+  float D;                    /* DEC in decimal degrees */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned short int Nm;      /* number of measurements */
+  unsigned short int Xp, Xm;  /* chisq values in tenths */
+  unsigned int offset;        /* offset to first Measure-ment */
+} Average;                    /* = 20 bytes / average */
+
+typedef struct {
+  char dR, dD;                /* tenths of arcsec (-12.7 to +12.7 valid range) */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned char dM;           /* thousandths of mag (0.000 -- 0.255 valid range) */
+  float t;                    /* time in seconds (what is reference?) */
+  unsigned int average;       /* reference to corresponding Average entry, 
+				 upper byte of value contains flags.
+				 limit of 16,777,215 stars (Naverage) 
+				 in a file (=0xFFFFFF).
+				 flags = average & 0x1000000 */
+} Measure;                    /* = 13 bytes / measure */
+
+# define BLEND_IMAGE   0X01000000
+# define BLEND_CATALOG 0X02000000
+# define UPPER_LIMIT   0X04000000
+# define CALIBRATED    0X08000000
+
+\end{verbatim}
+
+The above two structures define the entries in the photometry
+database.  The database consists of a large number of files
+representing a small patch on the sky (roughly 1.5 degree$^2$ in most
+places).  These files are organized into directories representing
+bands of Declination.  A reference file determines the coordinate
+boundaries for each of the files so that a given point on the sky can
+unambiguously be associated with a specific file in a specific
+directory.  The sky coordinates for each file is the same as those
+used by the HST Guide Star catalog, except for the region around the
+North celestial pole, for which all stars are included in a single
+file.  
+
+Within a given file, the data are stored in a binary format, with an
+ASCII FITS-like header.  The header is examply in the format of a
+normal FITS header, but with the exception that all files have a fixed
+number of header blocks (for now 3 blocks = 8640 bytes).  This is done
+to speed loading the header and finding the beginning of the binary
+data.  The number of 3 blocks seems quite generous, as currently only
+a few FITS keywords have been defined for each file, basically
+keywords to define the number of stars and the total number of
+measurements stored in the file, as well as values to define the RA
+and DEC range of the file.  
+
+The first section of data following the header blocks consists of
+average measurements for each uniquely observed star.  Each star
+occupies 20 bytes, the size of the Average structure defined above.
+The Average structure contains the average Ra, Dec, and Magnitude for
+the star, as well as the number of measurements, and \chisq\ values
+for the magnitude and position.  Finally, there is a 32 bit integer
+which defines the offset to the first measurement for this star.  This
+offset is defined as the number of Measure records from the start of
+the Measure structure.
+
+The second section of data, following the Average data contains all
+measurements for each star listed in Average.  Each measurement
+occupies 13 bytes, the size of the Measure structure.  This structure
+contains the difference of this position from the average RA and DEC,
+and the instrumental magnitude of this measurement (in the units
+defined by the fstat program, which give m = -2.5*log(cts) + Mo, where
+Mo is currently 24.5 [10/15]).  There is also the magnitude error for
+this measurement, the time of the measurement (in seconds relative to
+a to-be-determined zero point), and a reference to the entry in the
+Average structure so we can relate a given measurement with a given star.
+This last entry also includes a byte of flags, of which only 4 have
+currently been defined.  This means the Average offset can only be as
+large as  16,777,215 (0xffffff), limiting the possible number of stars
+allowed in a given file.  This does not seem like a long term problem,
+though:  aside from the fact that this number is very large and we
+only expect in the vicinity of 20,000 stars per file, the file can
+easily be divided into pieces at a later date if needed.  this last
+step is trivial, consisting of splitting the data up into smaller
+RA,DEC regions, and updating the reference catalog.  With the above
+definitions for the Average and Measure structures, we will typically
+expect 20,000 * 15 measurements per year and 20,000 average entries in
+a given file.  This implies a file size of about 4.3 MB at the end of
+a year.  It is also possible that we will choose to split the files up
+in the future if the number of measurements makes their size
+unweildy.  4.3 MB is sufficiently small that this is not a problem,
+but after only 5 years, the files will be over 21 MB each, getting to
+be fairly significant.  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/doc/description.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/doc/description.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/doc/description.txt	(revision 22322)
@@ -0,0 +1,94 @@
+
+ADDSTAR:
+
+This program takes a photometry file, with astrometry, provided by
+earlier routines and incorporates the stellar measurements into the
+photometry database.  This routine does NOT try to calibrate or check
+the photometry, nor does it try to improve the coordinate
+determinations or calculate chisquares for position or photometry -
+those tasks are performed by other routines.
+
+Addstar starts by loading in the photometry file and converting the
+pixel coordinates to sky coordinates using the astrometry information
+in the header of the file.  It also gets the detection limits and
+stores the image astrometry information.
+
+Next, addstar determines which of all the previous images overlaps
+with this image and also which of the catalog regions overlaps with
+this image.
+
+Addstar considers each catalog region in turn, and compares the
+positions of stars in the catalog with stars in the image.  If a
+catalog star is detected, a new measurement is added to the catalog.
+If a catalog star is not detected, but is in the field of view of this
+image, the detection limit for this image is added to the list of
+measurements as an upper limit.  If the image contains a star which is
+not already included in the catalog, the star is added to the catalog,
+and all previous images are checked to see if they included this
+location.  If so, upper limits are added to the list of measurements.
+
+To deal with blended images, we have taken the philosophy that all
+measurements compatible with the coordinates of a given star should be
+included in the list of measurements.  This assumes that programs or people
+downstream will figure out which is the "correct" measurement.  To
+this end, if a catalog star is consistent with multiple measurements,
+they are all added to the catalog measurement and the measurements are
+given a flag to say that there was blending in the catalog (ie, it is
+probably the catalog star which is a blend).  If multiple catalog
+stars are consistent with the same image star, that measurement is
+added to each catalog star, and given a flag to say that there was
+blending on the image.
+
+After all catalogs have been checked, updated, and written to disk,
+the image is added to the database of images.
+
+The format for a catalog region file is as follows:
+
+-- 
+Header
+--
+Averages
+--
+Measurements
+--
+
+The Header follows the format of a FITS header but always stored with
+3 blocks to speed up read time: 2880 x 3 bytes.  Two important
+keywords in the header: NSTARS & NMEAS
+
+All average values are stored consecutively in a single block.  They
+are written as an array with NSTARS elements of the Average structure
+defined below.  The individual measurements follow the averages as a
+block.  To find a measurement for a given star, you need to get the
+value of the offset for the star.  this is the element number for the
+first measurement of this star, and are followed by next measurements
+for this star, until the value Nm is reached.
+
+Here are the structures being used for the average and measurement
+values:
+
+typedef struct {
+  float R;                    /* RA  in decimal degrees */
+  float D;                    /* DEC in decimal degrees */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned short int Nm;      /* number of measurements */
+  unsigned short int Xp, Xm;  /* chisq values in tenths */
+  unsigned int offset;        /* offset to first measurement */
+} Average;                    /* = 20 bytes / average */
+
+typedef struct {
+  char dR, dD;                /* tenths of arcsec (-12.7 to +12.7 valid range) */
+  short int M;                /* thousandths of mag (-32.000 to 32.000 valid range) */
+  unsigned char dM;           /* thousandths of mag (0.000 -- 0.255 valid range) */
+  float t;                    /* time in seconds (what is reference?) */
+  unsigned int average;       
+  /* reference to corresponding average entry, upper byte stores flags:
+   limit of 16,777,215 stars (Naverage) in a file (=0xFFFFFF) 
+   flags: average & 0x1000000 */
+} Measure;                    /* = 13 bytes / measure */
+
+/* flags for measurements: */
+# define BLEND_IMAGE   0X01000000
+# define BLEND_CATALOG 0X02000000
+# define UPPER_LIMIT   0X04000000
+# define CALIBRATED    0X08000000
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/etc/phottemp.cat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/etc/phottemp.cat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/etc/phottemp.cat	(revision 22322)
@@ -0,0 +1,296 @@
+SIMPLE  =                    F  / ASTROCAM PHOTOMETRY FILE                    \
+BITPIX  =                   16 /                                              \
+NAXIS   =                    2  / NUMBER OF AXES                              \
+NAXIS1  =                 1106  / NUMBER OF COLUMNS                           \
+NAXIS2  =                 1024  / NUMBER OF ROWS                              \
+BSCALE  =             1.000000 /                                              \
+BZERO   =             0.000000 /                                              \
+DATE    = '19/12/94'            / UT Date of file creation (DD/MM/YY)         \
+ORIGIN  = 'MDM Observatory'     / Michigan-Dartmouth-MIT                      \
+LATITUDE=              31.9500  / Latitude (degrees N)                        \
+LONGITUD=            -111.6150  / Longitude (degrees E)                       \
+OBSERVER= 'Metzger '            / Name of observer                            \
+TELESCOP= '1.3m McGraw-Hill'    / Telescope used for observation              \
+INSTRUME= 'Charlotte Direct'    / Instrument used for observation             \
+DETECTOR= 'Charlotte/Tek 1024^2 CCD'  / Detector used for observation         \
+FRAME   =                   32  / Frame number of observation                 \
+CCDPICNO=                   32  / Frame number of observation                 \
+OBJECT  = 'ocl0327 '            / Name of object                              \
+IMAGETYP= 'OBJECT  '            / Type of observation                         \
+EXPTIME =              250.000  / Integration time (seconds)                  \
+DARKTIME=              250.067  / Dark current time (seconds)                 \
+DATE-OBS= '19/12/94'            / UT Date of observation (DD/MM/YY)           \
+UT      = ' 05:25:44.00'        / Universal time (UTC) at exposure start      \
+JD      =       2449705.726204                                                \
+RA      =           50.700      / Right Ascension                             \
+DEC     =           89.000      / Declination                                 \
+DIRECTN =                 -1    / Moving South                                \
+EQUINOX =             1950.000  / Equinox of RA and DEC                       \
+HA      = ' 02:14:06.72'        / Hour angle at start                         \
+ST      = ' 03:49:36.85'        / Sidereal time at start                      \
+ZD      = ' 36:26:07.17'        / Zenith distance (degrees)                   \
+AIRMASS =                1.243  / Airmass at start                            \
+FILTER  = 'I KP    '            / Filter description                          \
+GAIN    =                3.350  / Nominal gain (e-/ADU)                       \
+SECPIX1 =                0.508  / Arcseconds per pixel in fast dir            \
+SECPIX2 =                0.508  / Arcseconds per pixel in slow dir            \
+CCDBIN1 =                    1  / On-chip column binning (fast dir)           \
+CCDBIN2 =                    1  / On-chip row binning (slow dir)              \
+GPROBE  = '  5000.00   7000.00'  / Guide probe X Y                            \
+DATASEC = '[51:1074,1:1024]'    / Image area of frame                         \
+CCDSEC  = '[1:1074,1:1024]'     / Image area relative to full chip            \
+BIASSEC = '[1080:1106,1:1023]'  / Overscan area of frame                      \
+UNSIGN  =                    T /                                              \
+NSTARS  =                    0  / NUMBER OF stars                             \
+CTYPE1  = 'RA---SIN          ' /                                               
+CTYPE2  = 'DEC--SIN          ' /                                               
+CDELT1  =             0.000733 /                                               
+CDELT2  =             0.000733 /                                               
+CRVAL1  =            50.191418 /                                               
+CRVAL2  =            88.998663 /                                               
+CRPIX1  =          1028.644173 /                                               
+CRPIX2  =           503.129830 /                                               
+PC001001=             0.999906 /                                               
+PC001002=             0.012081 /                                               
+PC002001=            -0.011936 /                                               
+PC002002=             0.999950 /                                               
+END                                                                           \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+ 246.5  352.1 13.000 020 1 3.2
+ 246.5  352.1 14.100 020 1 3.2
+ 279.4  328.9 13.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 436.6  413.5 14.300 020 1 3.2
+   9.3  856.5 13.000 020 1 3.2
+   9.3  856.2 13.600 020 1 3.2
+ 319.7  697.9 12.000 020 1 3.2
+ 319.7  697.7 12.100 020 1 3.2
+ 244.3    9.5 14.100 020 1 3.2
+ 284.7  334.4 14.000 020 1 3.2
+ 127.1  122.8 14.200 020 1 3.2
+ 140.2  791.1 13.700 020 1 3.2
+ 140.2  790.8 14.400 020 1 3.2
+ 131.7  636.3 13.800 020 1 3.2
+ 423.8  700.6 13.800 020 1 3.2
+ 423.7  700.7 12.500 020 1 3.2
+ 266.2  290.7 13.800 020 1 3.2
+ 266.0  290.7 13.100 020 1 3.2
+ 570.7  721.6 13.600 020 1 3.2
+ 570.7  721.6 13.100 020 1 3.2
+ 548.7  673.0 13.700 020 1 3.2
+ 264.2  219.7 12.800 020 1 3.2
+ 264.0  219.7 12.100 020 1 3.2
+ 161.9  797.4 11.900 020 1 3.2
+ 162.0  797.2 12.200 020 1 3.2
+ 505.8  851.8 13.700 020 1 3.2
+  88.2  419.0 11.400 020 1 3.2
+  88.2  419.0 11.800 020 1 3.2
+ 426.9  613.2  9.400 020 1 3.2
+ 427.4  613.5  7.600 020 1 3.2
+ 326.1  595.2 12.100 020 1 3.2
+ 326.2  595.1 12.600 020 1 3.2
+ 444.4  845.2 14.200 020 1 3.2
+ 254.1  916.8 14.500 020 1 3.2
+ 502.6  882.4 14.200 020 1 3.2
+ 128.3  275.7 12.300 020 1 3.2
+ 128.3  275.7 12.900 020 1 3.2
+ 230.9  554.8 13.300 020 1 3.2
+ 230.9  554.7 14.200 020 1 3.2
+  47.5  685.2 13.500 020 1 3.2
+  47.5  684.9 14.200 020 1 3.2
+ 382.6  658.7 14.000 020 1 3.2
+ 182.2  330.3 14.200 020 1 3.2
+ 245.1  548.3 12.600 020 1 3.2
+ 245.3  548.0 13.200 020 1 3.2
+  50.3  849.0 13.700 020 1 3.2
+  50.5  848.8 14.300 020 1 3.2
+  17.9  772.7 14.200 020 1 3.2
+ 499.8  712.8 13.500 020 1 3.2
+ 499.8  712.6 14.400 020 1 3.2
+ 294.5  349.4 14.000 020 1 3.2
+ 497.7  747.8 11.000 020 1 3.2
+ 497.5  747.8 10.900 020 1 3.2
+ 109.7  323.5 14.000 020 1 3.2
+  59.5  917.6 14.300 020 1 3.2
+  59.4  917.8 13.700 020 1 3.2
+  82.2  880.5 13.800 020 1 3.2
+ 145.1  341.2 14.500 020 1 3.2
+ 452.7  592.4 14.400 020 1 3.2
+ 623.4  820.0 13.700 020 1 3.2
+ 609.7  829.3 13.500 020 1 3.2
+ 481.8  938.3 14.400 020 1 3.2
+ 617.0  792.7 13.500 020 1 3.2
+ 183.2  539.7 14.400 020 1 3.2
+ 133.9  679.6 14.400 020 1 3.2
+ 423.7  667.8  6.500 020 1 3.2
+ 434.7  111.9 13.200 020 1 3.2
+ 434.5  111.9 13.200 020 1 3.2
+ 434.6  111.9 14.200 020 1 3.2
+1222.3  684.2 13.900 020 1 3.2
+1077.6  282.5 14.000 020 1 3.2
+1077.8  282.5 13.400 020 1 3.2
+ 720.8  562.1 14.100 020 1 3.2
+ 721.1  561.9 14.400 020 1 3.2
+1182.1  944.4 12.100 020 1 3.2
+1181.9  944.4 11.800 020 1 3.2
+1181.9  944.4 12.300 020 1 3.2
+ 761.2  423.1 14.300 020 1 3.2
+ 761.0  423.1 14.100 020 1 3.2
+ 926.7  436.8 13.400 020 1 3.2
+ 926.6  436.8 14.200 020 1 3.2
+1209.1   45.0 13.200 020 1 3.2
+1209.2   43.6 12.800 020 1 3.2
+1135.2  653.1  8.100 020 1 3.2
+1135.6  653.4  8.200 020 1 3.2
+1123.5  735.8 12.000 020 1 3.2
+1123.6  735.8 11.800 020 1 3.2
+1123.7  735.7 11.600 020 1 3.2
+ 730.7   28.6 12.100 020 1 3.2
+ 730.7   28.6 12.200 020 1 3.2
+ 783.1  267.5 14.200 020 1 3.2
+ 783.2  267.5 13.400 020 1 3.2
+ 876.2  462.7 13.800 020 1 3.2
+ 876.4  462.7 14.200 020 1 3.2
+ 917.2  399.9 13.700 020 1 3.2
+ 917.3  399.9 13.200 020 1 3.2
+1227.5  218.4 10.400 020 1 3.2
+1227.7  218.4 10.500 020 1 3.2
+1022.7  146.0 13.300 020 1 3.2
+1022.5  146.0 14.100 020 1 3.2
+1324.7   72.3 12.900 020 1 3.2
+1324.6   72.3 13.200 020 1 3.2
+ 743.2  379.4 14.300 020 1 3.2
+ 742.9  379.4 13.900 020 1 3.2
+1255.9  544.0 12.600 020 1 3.2
+1256.0  543.9 12.300 020 1 3.2
+ 848.9  251.1 13.200 020 1 3.2
+ 849.0  251.1 12.700 020 1 3.2
+ 495.6  147.4 11.400 020 1 3.2
+ 495.6  147.4 11.300 020 1 3.2
+ 495.5  147.4 11.000 020 1 3.2
+1016.4  735.5 14.200 020 1 3.2
+1016.3  735.7 13.900 020 1 3.2
+ 795.2  675.9 14.400 020 1 3.2
+ 887.2  968.8 10.800 020 1 3.2
+ 626.8  726.9 14.400 020 1 3.2
+ 718.6  800.1  9.700 020 1 3.2
+ 675.4  301.6 14.400 020 1 3.2
+ 801.6  436.8 14.200 020 1 3.2
+1066.4  870.0 13.700 020 1 3.2
+1066.5  869.9 13.800 020 1 3.2
+ 644.5  662.9  9.900 020 1 3.2
+ 639.1  760.4 13.000 020 1 3.2
+ 822.7  663.1 12.800 020 1 3.2
+ 822.7  663.1 13.100 020 1 3.2
+ 677.2  716.2 12.900 020 1 3.2
+ 765.6  641.5 13.200 020 1 3.2
+ 765.7  641.4 13.500 020 1 3.2
+ 998.3  425.8 14.400 020 1 3.2
+ 713.7  716.2 13.800 020 1 3.2
+ 814.7  887.6  2.000 020 1 3.2
+ 808.7  884.1  8.200 020 1 3.2
+1812.3  259.3 14.500 020 1 3.2
+1655.9  655.4 12.200 020 1 3.2
+1655.9  655.4 12.100 020 1 3.2
+1577.2  839.6 11.500 020 1 3.2
+1577.2  839.6 11.400 020 1 3.2
+1643.7  597.0 14.100 020 1 3.2
+1552.8  251.1 10.100 020 1 3.2
+1552.8  251.1 10.300 020 1 3.2
+1410.0  999.9 13.900 020 1 3.2
+1335.6  687.0 13.000 020 1 3.2
+1335.8  686.8 12.700 020 1 3.2
+1471.4  262.0 14.200 020 1 3.2
+1479.6  608.3 14.200 020 1 3.2
+1478.1  211.5 12.000 020 1 3.2
+1478.3  211.5 12.000 020 1 3.2
+1552.5   46.4 14.500 020 1 3.2
+2042.4  548.3 10.800 020 1 3.2
+2042.6  548.3 11.000 020 1 3.2
+1448.0  915.7 14.100 020 1 3.2
+1462.3  989.2 13.000 020 1 3.2
+1462.3  989.0 12.700 020 1 3.2
+1316.9  787.3 14.100 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1677.2  106.4 13.100 020 1 3.2
+1677.0  106.4 13.300 020 1 3.2
+1324.2  429.9  9.200 020 1 3.2
+1324.2  429.9  9.000 020 1 3.2
+1462.8  214.3 13.700 020 1 3.2
+1462.8  214.3 14.200 020 1 3.2
+1405.4   95.5 11.700 020 1 3.2
+1405.2   95.5 11.600 020 1 3.2
+1604.8   90.0 11.200 020 1 3.2
+1604.8   90.0 11.300 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1459.8  473.6 13.400 020 1 3.2
+1459.7  473.6 13.800 020 1 3.2
+1438.7   77.8 12.200 020 1 3.2
+1438.6   77.8 12.200 020 1 3.2
+1355.0  114.6 13.000 020 1 3.2
+1354.9  114.6 13.200 020 1 3.2
+1941.4  940.7 13.000 020 1 3.2
+1941.6  940.7 12.900 020 1 3.2
+1994.0  800.8 13.900 020 1 3.2
+1993.8  800.8 14.000 020 1 3.2
+2024.4  907.0 13.700 020 1 3.2
+2024.4  907.0 13.800 020 1 3.2
+2035.7  881.6 13.900 020 1 3.2
+2035.9  881.6 13.800 020 1 3.2
+1994.0  924.2 12.100 020 1 3.2
+1994.0  924.2 12.000 020 1 3.2
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/etc/template.cat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/etc/template.cat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/etc/template.cat	(revision 22322)
@@ -0,0 +1,296 @@
+SIMPLE  =                    F  / ASTROCAM PHOTOMETRY FILE                    \
+BITPIX  =                   16 /                                              \
+NAXIS   =                    2  / NUMBER OF AXES                              \
+NAXIS1  =                 1106  / NUMBER OF COLUMNS                           \
+NAXIS2  =                 1024  / NUMBER OF ROWS                              \
+BSCALE  =             1.000000 /                                              \
+BZERO   =             0.000000 /                                              \
+DATE    = '19/12/94'            / UT Date of file creation (DD/MM/YY)         \
+ORIGIN  = 'MDM Observatory'     / Michigan-Dartmouth-MIT                      \
+LATITUDE=              31.9500  / Latitude (degrees N)                        \
+LONGITUD=            -111.6150  / Longitude (degrees E)                       \
+OBSERVER= 'Metzger '            / Name of observer                            \
+TELESCOP= '1.3m McGraw-Hill'    / Telescope used for observation              \
+INSTRUME= 'Charlotte Direct'    / Instrument used for observation             \
+DETECTOR= 'Charlotte/Tek 1024^2 CCD'  / Detector used for observation         \
+FRAME   =                   32  / Frame number of observation                 \
+CCDPICNO=                   32  / Frame number of observation                 \
+OBJECT  = 'ocl0327 '            / Name of object                              \
+IMAGETYP= 'OBJECT  '            / Type of observation                         \
+EXPTIME =              250.000  / Integration time (seconds)                  \
+DARKTIME=              250.067  / Dark current time (seconds)                 \
+DATE-OBS= '19/12/94'            / UT Date of observation (DD/MM/YY)           \
+UT      = ' 05:25:44.00'        / Universal time (UTC) at exposure start      \
+JD      =       2449705.726204                                                \
+RA      =           50.700      / Right Ascension                             \
+DEC     =           89.000      / Declination                                 \
+DIRECTN =                 -1    / Moving South                                \
+EQUINOX =             1950.000  / Equinox of RA and DEC                       \
+HA      = ' 02:14:06.72'        / Hour angle at start                         \
+ST      = ' 03:49:36.85'        / Sidereal time at start                      \
+ZD      = ' 36:26:07.17'        / Zenith distance (degrees)                   \
+AIRMASS =                1.243  / Airmass at start                            \
+FILTER  = 'I KP    '            / Filter description                          \
+GAIN    =                3.350  / Nominal gain (e-/ADU)                       \
+SECPIX1 =                0.508  / Arcseconds per pixel in fast dir            \
+SECPIX2 =                0.508  / Arcseconds per pixel in slow dir            \
+CCDBIN1 =                    1  / On-chip column binning (fast dir)           \
+CCDBIN2 =                    1  / On-chip row binning (slow dir)              \
+GPROBE  = '  5000.00   7000.00'  / Guide probe X Y                            \
+DATASEC = '[51:1074,1:1024]'    / Image area of frame                         \
+CCDSEC  = '[1:1074,1:1024]'     / Image area relative to full chip            \
+BIASSEC = '[1080:1106,1:1023]'  / Overscan area of frame                      \
+UNSIGN  =                    T /                                              \
+NSTARS  =                    0  / NUMBER OF stars                             \
+CTYPE1  = 'RA---SIN          ' /                                               
+CTYPE2  = 'DEC--SIN          ' /                                               
+CDELT1  =             0.000733 /                                               
+CDELT2  =             0.000733 /                                               
+CRVAL1  =            50.191418 /                                               
+CRVAL2  =            88.998663 /                                               
+CRPIX1  =          1028.644173 /                                               
+CRPIX2  =           503.129830 /                                               
+PC001001=             0.999906 /                                               
+PC001002=             0.012081 /                                               
+PC002001=            -0.011936 /                                               
+PC002002=             0.999950 /                                               
+END                                                                           \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+ 246.5  352.1 13.000 020 1 3.2
+ 246.5  352.1 14.100 020 1 3.2
+ 279.4  328.9 13.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 436.6  413.5 14.300 020 1 3.2
+   9.3  856.5 13.000 020 1 3.2
+   9.3  856.2 13.600 020 1 3.2
+ 319.7  697.9 12.000 020 1 3.2
+ 319.7  697.7 12.100 020 1 3.2
+ 244.3    9.5 14.100 020 1 3.2
+ 284.7  334.4 14.000 020 1 3.2
+ 127.1  122.8 14.200 020 1 3.2
+ 140.2  791.1 13.700 020 1 3.2
+ 140.2  790.8 14.400 020 1 3.2
+ 131.7  636.3 13.800 020 1 3.2
+ 423.8  700.6 13.800 020 1 3.2
+ 423.7  700.7 12.500 020 1 3.2
+ 266.2  290.7 13.800 020 1 3.2
+ 266.0  290.7 13.100 020 1 3.2
+ 570.7  721.6 13.600 020 1 3.2
+ 570.7  721.6 13.100 020 1 3.2
+ 548.7  673.0 13.700 020 1 3.2
+ 264.2  219.7 12.800 020 1 3.2
+ 264.0  219.7 12.100 020 1 3.2
+ 161.9  797.4 11.900 020 1 3.2
+ 162.0  797.2 12.200 020 1 3.2
+ 505.8  851.8 13.700 020 1 3.2
+  88.2  419.0 11.400 020 1 3.2
+  88.2  419.0 11.800 020 1 3.2
+ 426.9  613.2  9.400 020 1 3.2
+ 427.4  613.5  7.600 020 1 3.2
+ 326.1  595.2 12.100 020 1 3.2
+ 326.2  595.1 12.600 020 1 3.2
+ 444.4  845.2 14.200 020 1 3.2
+ 254.1  916.8 14.500 020 1 3.2
+ 502.6  882.4 14.200 020 1 3.2
+ 128.3  275.7 12.300 020 1 3.2
+ 128.3  275.7 12.900 020 1 3.2
+ 230.9  554.8 13.300 020 1 3.2
+ 230.9  554.7 14.200 020 1 3.2
+  47.5  685.2 13.500 020 1 3.2
+  47.5  684.9 14.200 020 1 3.2
+ 382.6  658.7 14.000 020 1 3.2
+ 182.2  330.3 14.200 020 1 3.2
+ 245.1  548.3 12.600 020 1 3.2
+ 245.3  548.0 13.200 020 1 3.2
+  50.3  849.0 13.700 020 1 3.2
+  50.5  848.8 14.300 020 1 3.2
+  17.9  772.7 14.200 020 1 3.2
+ 499.8  712.8 13.500 020 1 3.2
+ 499.8  712.6 14.400 020 1 3.2
+ 294.5  349.4 14.000 020 1 3.2
+ 497.7  747.8 11.000 020 1 3.2
+ 497.5  747.8 10.900 020 1 3.2
+ 109.7  323.5 14.000 020 1 3.2
+  59.5  917.6 14.300 020 1 3.2
+  59.4  917.8 13.700 020 1 3.2
+  82.2  880.5 13.800 020 1 3.2
+ 145.1  341.2 14.500 020 1 3.2
+ 452.7  592.4 14.400 020 1 3.2
+ 623.4  820.0 13.700 020 1 3.2
+ 609.7  829.3 13.500 020 1 3.2
+ 481.8  938.3 14.400 020 1 3.2
+ 617.0  792.7 13.500 020 1 3.2
+ 183.2  539.7 14.400 020 1 3.2
+ 133.9  679.6 14.400 020 1 3.2
+ 423.7  667.8  6.500 020 1 3.2
+ 434.7  111.9 13.200 020 1 3.2
+ 434.5  111.9 13.200 020 1 3.2
+ 434.6  111.9 14.200 020 1 3.2
+1222.3  684.2 13.900 020 1 3.2
+1077.6  282.5 14.000 020 1 3.2
+1077.8  282.5 13.400 020 1 3.2
+ 720.8  562.1 14.100 020 1 3.2
+ 721.1  561.9 14.400 020 1 3.2
+1182.1  944.4 12.100 020 1 3.2
+1181.9  944.4 11.800 020 1 3.2
+1181.9  944.4 12.300 020 1 3.2
+ 761.2  423.1 14.300 020 1 3.2
+ 761.0  423.1 14.100 020 1 3.2
+ 926.7  436.8 13.400 020 1 3.2
+ 926.6  436.8 14.200 020 1 3.2
+1209.1   45.0 13.200 020 1 3.2
+1209.2   43.6 12.800 020 1 3.2
+1135.2  653.1  8.100 020 1 3.2
+1135.6  653.4  8.200 020 1 3.2
+1123.5  735.8 12.000 020 1 3.2
+1123.6  735.8 11.800 020 1 3.2
+1123.7  735.7 11.600 020 1 3.2
+ 730.7   28.6 12.100 020 1 3.2
+ 730.7   28.6 12.200 020 1 3.2
+ 783.1  267.5 14.200 020 1 3.2
+ 783.2  267.5 13.400 020 1 3.2
+ 876.2  462.7 13.800 020 1 3.2
+ 876.4  462.7 14.200 020 1 3.2
+ 917.2  399.9 13.700 020 1 3.2
+ 917.3  399.9 13.200 020 1 3.2
+1227.5  218.4 10.400 020 1 3.2
+1227.7  218.4 10.500 020 1 3.2
+1022.7  146.0 13.300 020 1 3.2
+1022.5  146.0 14.100 020 1 3.2
+1324.7   72.3 12.900 020 1 3.2
+1324.6   72.3 13.200 020 1 3.2
+ 743.2  379.4 14.300 020 1 3.2
+ 742.9  379.4 13.900 020 1 3.2
+1255.9  544.0 12.600 020 1 3.2
+1256.0  543.9 12.300 020 1 3.2
+ 848.9  251.1 13.200 020 1 3.2
+ 849.0  251.1 12.700 020 1 3.2
+ 495.6  147.4 11.400 020 1 3.2
+ 495.6  147.4 11.300 020 1 3.2
+ 495.5  147.4 11.000 020 1 3.2
+1016.4  735.5 14.200 020 1 3.2
+1016.3  735.7 13.900 020 1 3.2
+ 795.2  675.9 14.400 020 1 3.2
+ 887.2  968.8 10.800 020 1 3.2
+ 626.8  726.9 14.400 020 1 3.2
+ 718.6  800.1  9.700 020 1 3.2
+ 675.4  301.6 14.400 020 1 3.2
+ 801.6  436.8 14.200 020 1 3.2
+1066.4  870.0 13.700 020 1 3.2
+1066.5  869.9 13.800 020 1 3.2
+ 644.5  662.9  9.900 020 1 3.2
+ 639.1  760.4 13.000 020 1 3.2
+ 822.7  663.1 12.800 020 1 3.2
+ 822.7  663.1 13.100 020 1 3.2
+ 677.2  716.2 12.900 020 1 3.2
+ 765.6  641.5 13.200 020 1 3.2
+ 765.7  641.4 13.500 020 1 3.2
+ 998.3  425.8 14.400 020 1 3.2
+ 713.7  716.2 13.800 020 1 3.2
+ 814.7  887.6  2.000 020 1 3.2
+ 808.7  884.1  8.200 020 1 3.2
+1812.3  259.3 14.500 020 1 3.2
+1655.9  655.4 12.200 020 1 3.2
+1655.9  655.4 12.100 020 1 3.2
+1577.2  839.6 11.500 020 1 3.2
+1577.2  839.6 11.400 020 1 3.2
+1643.7  597.0 14.100 020 1 3.2
+1552.8  251.1 10.100 020 1 3.2
+1552.8  251.1 10.300 020 1 3.2
+1410.0  999.9 13.900 020 1 3.2
+1335.6  687.0 13.000 020 1 3.2
+1335.8  686.8 12.700 020 1 3.2
+1471.4  262.0 14.200 020 1 3.2
+1479.6  608.3 14.200 020 1 3.2
+1478.1  211.5 12.000 020 1 3.2
+1478.3  211.5 12.000 020 1 3.2
+1552.5   46.4 14.500 020 1 3.2
+2042.4  548.3 10.800 020 1 3.2
+2042.6  548.3 11.000 020 1 3.2
+1448.0  915.7 14.100 020 1 3.2
+1462.3  989.2 13.000 020 1 3.2
+1462.3  989.0 12.700 020 1 3.2
+1316.9  787.3 14.100 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1677.2  106.4 13.100 020 1 3.2
+1677.0  106.4 13.300 020 1 3.2
+1324.2  429.9  9.200 020 1 3.2
+1324.2  429.9  9.000 020 1 3.2
+1462.8  214.3 13.700 020 1 3.2
+1462.8  214.3 14.200 020 1 3.2
+1405.4   95.5 11.700 020 1 3.2
+1405.2   95.5 11.600 020 1 3.2
+1604.8   90.0 11.200 020 1 3.2
+1604.8   90.0 11.300 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1459.8  473.6 13.400 020 1 3.2
+1459.7  473.6 13.800 020 1 3.2
+1438.7   77.8 12.200 020 1 3.2
+1438.6   77.8 12.200 020 1 3.2
+1355.0  114.6 13.000 020 1 3.2
+1354.9  114.6 13.200 020 1 3.2
+1941.4  940.7 13.000 020 1 3.2
+1941.6  940.7 12.900 020 1 3.2
+1994.0  800.8 13.900 020 1 3.2
+1993.8  800.8 14.000 020 1 3.2
+2024.4  907.0 13.700 020 1 3.2
+2024.4  907.0 13.800 020 1 3.2
+2035.7  881.6 13.900 020 1 3.2
+2035.9  881.6 13.800 020 1 3.2
+1994.0  924.2 12.100 020 1 3.2
+1994.0  924.2 12.000 020 1 3.2
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/include/dvoImageExtract.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/include/dvoImageExtract.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/include/dvoImageExtract.h	(revision 22322)
@@ -0,0 +1,33 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+# include <glob.h>
+
+enum {NONE, SIMPLE_CMP, SIMPLE_CMF, SIMPLE_MEF, MOSAIC_CMP, MOSAIC_CMF, MOSAIC_MEF, MOSAIC_PHU};
+
+int       VERBOSE;
+
+char OUTPUT[256];
+char GSCFILE[256];
+char CATDIR[256];
+char CATMODE[16];    /* raw, mef, split, mysql */
+char CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char SKY_TABLE[256];
+int  SKY_DEPTH;
+char   ImageCat[256];
+char *OUTFILE;
+Coords *MOSAIC;         // carries the mosaic into ReadImageHeader
+
+int  args_extract    	 PROTO((int argc, char **argv));
+int  ConfigInit_extract  PROTO((int *argc, char **argv));
+int  Shutdown         	 PROTO((char *format, ...));
+void TrapSignal       	 PROTO((int sig));
+void SetProtect       	 PROTO((int mode));
+int  SetSignals       	 PROTO(());
+
+int GetFileMode (Header *header);
+int edge_check (double *x1, double *y1, double *x2, double *y2);
+
+int  WriteImageFITS (FILE *f, Image *image);
+int  WriteImages (char *filename, Image *images, int Nimages, int *matches, int Nmatches);
+int *SelectImages (char *filename, Image *dbImages, int NdbImages, int *Nmatch);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/include/dvoImageOverlaps.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/include/dvoImageOverlaps.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/include/dvoImageOverlaps.h	(revision 22322)
@@ -0,0 +1,38 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+# include <glob.h>
+
+enum {NONE, SIMPLE_CMP, SIMPLE_CMF, SIMPLE_MEF, MOSAIC_CMP, MOSAIC_CMF, MOSAIC_MEF, MOSAIC_PHU};
+
+int       VERBOSE;
+
+char OUTPUT[256];
+char GSCFILE[256];
+char CATDIR[256];
+char CATMODE[16];    /* raw, mef, split, mysql */
+char CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char SKY_TABLE[256];
+int  SKY_DEPTH;
+char   ImageCat[256];
+Coords *MOSAIC;         // carries the mosaic into ReadImageHeader
+
+int WITH_PHU;
+int SOLO_PHU;
+int ACCEPT_ASTROM;
+
+int  args_overlaps    	 PROTO((int argc, char **argv));
+int  ConfigInit_overlaps PROTO((int *argc, char **argv));
+int  Shutdown         PROTO((char *format, ...));
+void TrapSignal       PROTO((int sig));
+void SetProtect       PROTO((int mode));
+int  SetSignals       PROTO(());
+
+Image *ReadImageFiles (char *filename, int *Nimages);
+int ReadImageHeader (Header *header, Image *image);
+int *MatchImage (Image *dbImages, int NdbImages, Image *image, int *Nmatch);
+int ListImageOverlaps (Image *dbImages, Image *image, int *matches, int Nmatches);
+
+int GetFileMode (Header *header);
+int edge_check (double *x1, double *y1, double *x2, double *y2);
+double opening_angle (double x1, double y1, double x2, double y2, double x3, double y3);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/include/getstar.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/include/getstar.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/include/getstar.h	(revision 22322)
@@ -0,0 +1,49 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+
+enum {
+  BY_NOTHING,
+  BY_REGION,
+  BY_RADIUS,
+  BY_CATALOG,
+  BY_IMAGE,
+  BY_IMMATCH,
+  BY_IMLIST,
+};
+
+int       VERBOSE;
+int       MODE;
+SkyRegion REGION;
+char     *IMAGENAME;
+
+char OUTPUT[256];
+char OUTFORMAT[256];
+char GSCFILE[256];
+char CATDIR[256];
+char CATMODE[16];    /* raw, mef, split, mysql */
+char CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char SKY_TABLE[256];
+int  SKY_DEPTH;
+
+int  MagLimitUse;
+float MagLimitValue;
+PhotCode *photcode;
+
+int  args             	     PROTO((int argc, char **argv));
+int  ConfigInit       	     PROTO((int *argc, char **argv));
+int  Shutdown         	     PROTO((char *format, ...));
+int  load_pt_catalog  	     PROTO((Catalog *catalog, SkyRegion *region));
+int  select_by_region 	     PROTO((Catalog *output, Catalog *catalog, SkyRegion *region, int start, int end));
+void set_db           	     PROTO((FITS_DB *in));
+void wcatalog         	     PROTO((char *filename, Catalog *catalog));
+void mkcatalog        	     PROTO((Catalog *catalog));
+void init_catalog     	     PROTO((Catalog *catalog));
+void TrapSignal       	     PROTO((int sig));
+void SetProtect       	     PROTO((int mode));
+int  SetSignals       	     PROTO(());
+int  gcatalog         	     PROTO((Catalog *catalog));
+int  write_catalog           PROTO((Catalog *catalog));
+int  write_getstar_PS1_DEV_0 PROTO((Catalog *catalog));
+int  write_getstar_PS1_DEV_1 PROTO((Catalog *catalog));
+int  write_getstar_PS1_DEV_2 PROTO((Catalog *catalog));
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "getstar.h"
+
+int ConfigInit (int *argc, char **argv) {
+
+  char *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  ScanConfig (config, "GSCFILE",                "%s", 0, GSCFILE);
+  ScanConfig (config, "CATDIR",                 "%s", 0, CATDIR);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig (config, "PHOTCODE_FILE",         	"%s",  0, MasterPhotcodeFile);
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = 2;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+  SetZeroPoint (25.0);
+
+  free (config);
+  free (file);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ConfigInit_extract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ConfigInit_extract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ConfigInit_extract.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "dvoImageExtract.h"
+
+int ConfigInit_extract (int *argc, char **argv) {
+
+  char *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  ScanConfig (config, "GSCFILE",                "%s", 0, GSCFILE);
+  ScanConfig (config, "CATDIR",                 "%s", 0, CATDIR);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig (config, "PHOTCODE_FILE",         	"%s",  0, MasterPhotcodeFile);
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = 2;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ConfigInit_overlaps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ConfigInit_overlaps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ConfigInit_overlaps.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "dvoImageOverlaps.h"
+
+int ConfigInit_overlaps (int *argc, char **argv) {
+
+  char *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  ScanConfig (config, "GSCFILE",                "%s", 0, GSCFILE);
+  ScanConfig (config, "CATDIR",                 "%s", 0, CATDIR);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig (config, "PHOTCODE_FILE",         	"%s",  0, MasterPhotcodeFile);
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = 2;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/GetFileMode.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/GetFileMode.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/GetFileMode.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "dvoImageOverlaps.h"
+
+// examine the PHU of this file and determine the file mode
+int GetFileMode (Header *header) {
+
+  char ctype[80];
+  int Naxis;
+  int simple, extend, haveNaxis, haveCTYPE;
+
+  gfits_scan (header, "SIMPLE", "%t", 1, &simple);
+  haveNaxis = gfits_scan (header, "NAXIS",  "%d", 1, &Naxis);
+  haveCTYPE = gfits_scan (header, "CTYPE1", "%s", 1, ctype);
+
+  gfits_scan (header, "EXTEND", "%t", 1, &extend);
+    
+  if ((Naxis == 2) || !simple) {
+    if (!strcmp (&ctype[4], "-WRP")) {
+      return MOSAIC_CMP;
+    }
+    return SIMPLE_CMP;
+  }
+
+  if (!extend && strcmp (&ctype[4], "-DIS")) {
+    if (!strcmp (&ctype[4], "-WRP")) {
+      return MOSAIC_CMF;
+    }
+    return SIMPLE_CMF;
+  }
+
+  if (!extend && !strcmp (&ctype[4], "-DIS")) {
+    return MOSAIC_PHU;
+  }
+
+  if (extend && strcmp (&ctype[4], "-DIS")) {
+    return SIMPLE_MEF;
+  }
+
+  if (extend && !strcmp (&ctype[4], "-DIS")) {
+    return MOSAIC_MEF;
+  }
+
+  return (NONE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ListImageOverlaps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ListImageOverlaps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ListImageOverlaps.c	(revision 22322)
@@ -0,0 +1,14 @@
+# include "dvoImageOverlaps.h"
+
+/* given image, find catalog images which overlap it */
+int ListImageOverlaps (Image *dbImages, Image *image, int *matches, int Nmatches) {
+  
+  int i, N;
+
+  for (i = 0; i < Nmatches; i++) {
+    N = matches[i];
+    fprintf (stdout, "%s  :  %s\n", image[0].name, dbImages[N].name);
+  }
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/MatchImages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/MatchImages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/MatchImages.c	(revision 22322)
@@ -0,0 +1,153 @@
+# include "dvoImageOverlaps.h"
+# ifndef FLT_MAX
+# define FLT_MAX 1e32
+# endif
+
+/* given image, find catalog images which overlap it */
+int *MatchImage (Image *dbImages, int NdbImages, Image *image, int *Nmatch) {
+  
+  int i, j, N, addtolist, status;
+  int NMATCH, nmatch, *match;
+  Coords tcoords;
+  double r, d;
+  double Xi[4], Yi[4], Xo[4], Yo[4];  /* image and original corners */
+  double Xmin, Xmax, Ymin, Ymax;
+  double xmin, xmax, ymin, ymax;
+
+  *Nmatch = 0;
+
+  if (!WITH_PHU && !strcmp (&image[0].coords.ctype[4], "-DIS")) return NULL;
+  if ( SOLO_PHU &&  strcmp (&image[0].coords.ctype[4], "-DIS")) return NULL;
+
+  /* project onto rectilinear grid with 1 arcsec pixels */
+  /* we keep the original crpix1,2 and crref1,2 */
+  /* for mosaic astrometry, the grid should be w.r.t. the tangent-plane, not chip coords */
+  if (!strcmp (&image[0].coords.ctype[4], "-WRP")) {
+    // if the input image is a mosaic, we need to register that mosaic for the calculations below
+    if (MOSAIC == NULL) {
+      fprintf (stderr, "no mosaic for WRP image (use -mosaic)\n");
+      return NULL;
+    }
+    RegisterMosaic (MOSAIC);
+
+    tcoords = MOSAIC[0];
+    tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+    tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
+    tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+    tcoords.Npolyterms = 1;
+    strcpy (tcoords.ctype, "RA---TAN");
+    /* register so image->sky conversions below have correct mosaic */
+  } else {
+    tcoords = image[0].coords;
+    tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+    tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
+    tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+    strcpy (tcoords.ctype, "RA---TAN");
+  }
+
+  /* define original corners */
+  SetImageCorners (Xo, Yo, &image[0]);
+  
+  Ymin = Xmin = +FLT_MAX;
+  Ymax = Xmax = -FLT_MAX;
+  for (j = 0; j < 4; j++) {
+    /* XY-to_RD is two-level if ctype == WRP */
+    XY_to_RD (&r, &d, Xo[j], Yo[j], &image[0].coords);
+    RD_to_XY (&Xo[j], &Yo[j], r, d, &tcoords);
+    Xmin = MIN (Xmin, Xo[j]);
+    Xmax = MAX (Xmax, Xo[j]);
+    Ymin = MIN (Ymin, Yo[j]);
+    Ymax = MAX (Ymax, Yo[j]);
+  }
+
+  /* match represents the subset of overlapping images */
+  nmatch = 0;
+  NMATCH = 20;
+  ALLOCATE (match, int, NMATCH);
+
+  /* setup links for mosaic WRP and DIS entries */
+  BuildChipMatch (dbImages, NdbImages);
+
+  /* run through image table and search for overlaps
+     also define the vtable entries for the images we keep  */
+
+  for (i = 0; i < NdbImages; i++) {
+
+    if (!WITH_PHU && !strcmp (&dbImages[i].coords.ctype[4], "-DIS")) continue;
+    if ( SOLO_PHU &&  strcmp (&dbImages[i].coords.ctype[4], "-DIS")) continue;
+
+    /* if any of these images are WRP images, need to find matching DIS */
+    if (!FindMosaicForImage (dbImages, NdbImages, i)) continue;
+
+    /* define image corners */
+    SetImageCorners (Xi, Yi, &dbImages[i]);
+    // Xi[4] = Xi[0]; Yi[4] = Yi[0];
+
+    /* transform to tcoords, skip corners off image */
+    /*** XXX this will fail for very large images which extend beyond 180deg */
+    ymin = xmin = +FLT_MAX;
+    ymax = xmax = -FLT_MAX;
+    for (j = N = 0; j < 4; j++) {
+      status = XY_to_RD (&r, &d, Xi[j], Yi[j], &dbImages[i].coords);
+      if (!status) continue;
+      status = RD_to_XY (&Xi[N], &Yi[N], r, d, &tcoords);
+      if (!status) continue;
+      xmin = MIN (xmin, Xi[N]);
+      xmax = MAX (xmax, Xi[N]);
+      ymin = MIN (ymin, Yi[N]);
+      ymax = MAX (ymax, Yi[N]);
+      N++;
+    }
+
+    /* check if one corner of dbImages[i] is inside image[0] */
+    for (j = 0; j < N; j++) {
+      addtolist = TRUE;
+      addtolist &= (Xi[j] >= Xmin);
+      addtolist &= (Xi[j] <= Xmax);
+      addtolist &= (Yi[j] >= Ymin);
+      addtolist &= (Yi[j] <= Ymax);
+      if (addtolist) goto addtolist;
+    }
+
+    /* or else, check if one corner of image[0] is inside dbImages[i] */
+    for (j = 0; j < 4; j++) {
+      addtolist = TRUE;
+      addtolist &= (Xo[j] >= xmin);
+      addtolist &= (Xo[j] <= xmax);
+      addtolist &= (Yo[j] >= ymin);
+      addtolist &= (Yo[j] <= ymax);
+      if (addtolist) goto addtolist;
+    }
+
+    // no match, skip this dbImage
+    continue;
+
+  addtolist:
+    match[nmatch] = i;
+    nmatch ++;
+    if (nmatch == NMATCH) {
+      NMATCH += 20;
+      REALLOCATE (match, int, NMATCH);
+    }
+  }
+  
+  if (VERBOSE) fprintf (stderr, "found %d overlapping images\n", nmatch);
+
+  *Nmatch = nmatch;
+  return (match);
+}
+  
+void SetImageCorners (double *X, double *Y, Image *image) {
+
+  if (!strcmp(&image[0].coords.ctype[4], "-DIS")) {
+    X[0] = -0.5*image[0].NX; Y[0] = -0.5*image[0].NY;
+    X[1] = +0.5*image[0].NX; Y[1] = -0.5*image[0].NY;
+    X[2] = +0.5*image[0].NX; Y[2] = +0.5*image[0].NY;
+    X[3] = -0.5*image[0].NX; Y[3] = +0.5*image[0].NY;
+  } else {
+    X[0] = 0;           Y[0] = 0;
+    X[1] = image[0].NX; Y[1] = 0;
+    X[2] = image[0].NX; Y[2] = image[0].NY;
+    X[3] = 0;           Y[3] = image[0].NY;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ReadImageFiles.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ReadImageFiles.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ReadImageFiles.c	(revision 22322)
@@ -0,0 +1,227 @@
+# include "dvoImageOverlaps.h"
+# define DVO_IMAGE_NAME_LEN 128
+
+Image *ReadImageFiles (char *filename, int *Nimages) {
+
+  int i, j, N, Nfile, Nheader, NHEADER, Nimage, NIMAGE;
+  int Nskip, Nhead, Ndata, done, status, mode;
+  char **file, *name;
+  FILE *f;
+  glob_t globList;
+  char **exthead, **extdata, **exttype, tmpword[80];
+  int *extnum_head, *extnum_data, *extsize;
+  Header *header, **headers;
+  Image *image;
+
+  // parse the filename as a glob
+  globList.gl_offs = 0;
+  glob (filename, 0, NULL, &globList);
+
+  // if the glob does not match, save the literal word:
+  // otherwise save all glob matches
+  if (globList.gl_pathc == 0) {
+    Nfile = 1;
+    ALLOCATE (file, char *, Nfile);
+    file[0] = strcreate (filename);
+  } else {
+    Nfile = globList.gl_pathc;
+    ALLOCATE (file, char *, Nfile);
+    for (i = 0; i < Nfile; i++) {
+      file[i] = strcreate (globList.gl_pathv[i]);
+    }
+  }
+
+  // open the first file, read the PHU header
+  f = fopen (file[0], "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't read header for %s\n", file[0]);
+    exit (1);
+  }
+  ALLOCATE (header, Header, 1);
+  gfits_fread_header (f, header);
+
+  mode = GetFileMode (header);
+
+  /*** load data from a single PHU or a collection of PHU files ***/
+  if ((mode != SIMPLE_MEF) && (mode != MOSAIC_MEF)) {
+    Nimage = Nfile;
+    ALLOCATE (image, Image, Nimage);
+    for (i = N = 0; i < Nfile; i++) {
+      if (i > 0) {
+	f = fopen (file[i], "r");
+	if (f == NULL) {
+	  fprintf (stderr, "can't read header for %s, skipping\n", file[i]);
+	  continue;
+	}
+	gfits_fread_header (f, header);
+      }
+
+      if (!ReadImageHeader (header, &image[N])) {
+	fprintf (stderr, "skipping %s\n", file[i]);
+	continue;
+      }
+      
+      /* find image rootname */
+      name = filebasename (file[i]);
+      snprintf (image[N].name, DVO_IMAGE_NAME_LEN, name);
+      free (name);
+
+      fclose (f);
+      gfits_free_header (header);
+      N++;
+    }
+    *Nimages = N;
+    
+    if (N == 0) {
+      fprintf (stderr, "ERROR: no valid image data in %s, giving up\n", filename);
+      exit (1);
+    }
+    return image;
+  }
+    
+  /* we have a multi-chip image */
+
+  /* we need to examine the extensions to determine the headers and the data */
+  NHEADER = 10;
+  ALLOCATE (headers, Header *, NHEADER);
+
+  // the first header is already loaded
+  headers[0] = header;
+  Nskip = gfits_data_size (header);
+  fseek (f, Nskip, SEEK_CUR); 
+
+  // load all headers into memory
+  done = FALSE;
+  for (i = 1; !done; i++) {
+      ALLOCATE (headers[i], Header, 1);
+      status = gfits_fread_header (f, headers[i]);
+      if (!status) { 
+	  done = TRUE;
+      } else {
+	  Nskip = gfits_data_size (headers[i]);
+	  fseek (f, Nskip, SEEK_CUR); 
+      }
+      if (i == NHEADER - 1) {
+	  NHEADER += 10;
+	  REALLOCATE (headers, Header *, NHEADER);
+      }
+  }
+  Nheader = i - 1; /* we failed on the last loop */
+    
+  // space to store the images, indexes to the matching headers
+  Nimage = 0;
+  NIMAGE = Nheader;
+  ALLOCATE (image, Image, NIMAGE);
+  ALLOCATE (exthead, char *, NIMAGE);
+  ALLOCATE (extdata, char *, NIMAGE);
+  ALLOCATE (exttype, char *, NIMAGE);
+  ALLOCATE (extnum_head, int, NIMAGE);
+  ALLOCATE (extnum_data, int, NIMAGE);
+  ALLOCATE (extsize, int, Nheader);
+
+  if (mode == MOSAIC_MEF) {
+      exthead[Nimage] = strcreate ("PHU");
+      extdata[Nimage] = strcreate ("NONE");
+      extnum_data[Nimage] = -1;
+      extnum_head[Nimage] = 0;
+      Nimage ++;
+  }
+
+  // now examine the headers, count the table entries, find corresponding headers
+  for (i = 0; i < Nheader; i++) {
+      extsize[i] = headers[i][0].size + gfits_data_size (headers[i]);
+      gfits_scan (headers[i], "EXTTYPE", "%s", 1, tmpword);
+
+      if (!strcmp (tmpword, "SMPDATA")   ||  
+	  !strcmp (tmpword, "PS1_DEV_0") ||  
+	  !strcmp (tmpword, "PS1_DEV_1")) {
+
+	  exttype[Nimage] = strcreate (tmpword);
+	  gfits_scan (headers[i], "EXTNAME", "%s", 1, tmpword);
+	  extdata[Nimage] = strcreate (tmpword);
+	  gfits_scan (headers[i], "EXTHEAD", "%s", 1, tmpword);
+	  exthead[Nimage] = strcreate (tmpword);
+	  extnum_data[Nimage] = i;
+	  extnum_head[Nimage] = -1;
+	  // find the matching exthead entry
+	  for (j = 0; j < Nheader; j++) {
+	    if (gfits_scan (headers[j], "EXTNAME", "%s", 1, tmpword)) {
+	      if (!strcmp (tmpword, exthead[Nimage])) {
+		extnum_head[Nimage] = j;
+	      }
+	    }
+	  }
+	  // skip or crash on table with missing matching header?
+	  if (extnum_head[Nimage] == -1) {
+	      fprintf (stderr, "ERROR: can't read header for %s\n", file[0]);
+	      exit (1);
+	  }
+	  Nimage ++;
+      }
+  }
+
+  // some old format files did not write EXTTYPE.  they have a single table in the first
+  // extension matched to the header in the PHU
+  if (Nimage == 0) {
+      extsize[0] = headers[0][0].size + gfits_data_size (headers[0]);
+      extsize[1] = headers[1][0].size + gfits_data_size (headers[1]);
+      gfits_scan (headers[1], "EXTNAME", "%s", 1, tmpword);
+      if (!strcmp (tmpword, "SMPFILE")) {
+	  extdata[Nimage] = strcreate (tmpword);
+	  exttype[Nimage] = strcreate ("SMPDATA");
+	  exthead[Nimage] = strcreate ("PHU");
+	  extnum_head[Nimage] = 0;
+	  extnum_data[Nimage] = 1;
+	  Nimage = 1;
+      }
+  }
+  if (Nimage == 0) Shutdown ("no object data in file");
+    
+  if (VERBOSE) fprintf (stderr, "file %s has %d headers, including %d images\n", file[0], Nheader, Nimage);
+
+  /* find image rootname */
+  name = filebasename (file[0]);
+
+  // now run through the images, interpret the headers and read the stars
+  for (i = N = 0; i < Nimage; i++) {
+      Nhead = extnum_head[i];
+
+      // XXX do I need to advance the file pointer, or does ReadImageHeader do this?
+      if (VERBOSE) fprintf (stderr, "reading header for %s (%s)\n", exthead[i], extdata[i]);
+      if (!ReadImageHeader (headers[Nhead], &image[N])) {
+	  fprintf (stderr, "skipping %s\n", exthead[i]);
+	  continue;
+      }
+
+      // XXX use something to set the chip name? EXTNAME?
+      if (!strcmp(exthead[i], "PHU") && (Nimage == 1)) {
+	snprintf (image[N].name, DVO_IMAGE_NAME_LEN, "%s", name);
+      } else {
+	snprintf (image[N].name, DVO_IMAGE_NAME_LEN, "%s[%s]", name, exthead[i]);
+      }
+
+      // skip the table if there is not data segment (eg, mosaic WRP image)
+      if (!strcmp(extdata[i], "NONE")) {
+	  N++;
+	  continue;
+      }
+
+      // advance the pointer to the start of the corresponding table block
+      Ndata = extnum_data[i];
+      Nskip = 0;
+      for (j = 0; j < Ndata; j++) {
+	  Nskip += extsize[j];
+      }
+      fseek (f, Nskip, SEEK_SET); 
+      N++;
+  }
+  free (name);
+  *Nimages = N;
+
+  if (N == 0) {
+    fprintf (stderr, "ERROR: no valid image data in %s, giving up\n", filename);
+    exit (1);
+  }
+
+  return image;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ReadImageHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ReadImageHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/ReadImageHeader.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "dvoImageOverlaps.h"
+
+/* read an image header corresponding to a CMF / CMP data block */
+int ReadImageHeader (Header *header, Image *image) {
+
+  int Nastro, Nx, Ny, haveNx, haveNy;
+  double tmp;
+
+  /* get astrometry information */
+  if (!GetCoords (&image[0].coords, header)) {
+    fprintf (stderr, "no astrometric solution in header\n");
+    return (FALSE);
+  }
+  // XXX currently, image uses an unsigned short for NX,XY. this is rather restrictive
+  // and needs to be at least checked.
+  haveNx = gfits_scan (header, "NAXIS1",   "%d", 1, &Nx);
+  haveNy = gfits_scan (header, "NAXIS2",   "%d", 1, &Ny);
+
+  if (!haveNx && !haveNy) {
+      haveNx = gfits_scan (header, "IMNAXIS1",   "%d", 1, &Nx);
+      haveNy = gfits_scan (header, "IMNAXIS2",   "%d", 1, &Ny);
+  }
+
+  if (!haveNx || !haveNy) {
+      fprintf (stderr, "missing image dimensions in header\n");
+      return (FALSE);
+  }
+
+  if ((Nx < 0) || (Nx > 0xffff)) {
+    fprintf (stderr, "WARNING: NX, NY out of range : image boundary will be wrong\n");
+  }
+  image[0].NX = Nx;
+  image[0].NY = Ny;
+
+  // if (!gfits_scan (header, "TZERO",   "%d",  1, &image[0].tzero)) {
+  // image[0].tzero = parse_time (header);
+  // }
+
+  /* only load astrometry, NAXIS1,2, and time if this is a MOSAIC_PHU (ctype is ....-DIS) */
+  if (!strcmp (&image[0].coords.ctype[4], "-DIS")) {
+    MOSAIC = &image[0].coords;
+    return (TRUE);
+  }
+
+  /* require Nastro > 0 unless or ACCEPT_ASTROM */
+  Nastro = 0;
+  gfits_scan (header, "NASTRO", "%d", 1, &Nastro);
+  if ((Nastro == 0) && !ACCEPT_ASTROM) {
+    fprintf (stderr, "bad astrometric solution in header\n");
+    return (FALSE);
+  }
+  if (!strcmp (&image[0].coords.ctype[4], "-WRP")) {
+    if (MOSAIC == NULL) {
+      fprintf (stderr, "no mosaic for WRP image (use -mosaic)\n");
+      return (FALSE);
+    }
+    RegisterMosaic (MOSAIC);
+  } else {
+    /* force image to lie in 0-360 range */
+    while (image[0].coords.crval1 < 0) image[0].coords.crval1 += 360.0;
+    while (image[0].coords.crval1 > 360.0) image[0].coords.crval1 -= 360.0;
+  }
+
+  { 
+    double R, D;
+    /* sanity check on the image coordinates */
+    XY_to_RD (&R, &D, 0.5*Nx, 0.5*Ny, &image[0].coords);
+    if (!finite(R) || !finite(D)) {
+      fprintf (stderr, "corrupted header coordinates, skipping\n");
+      return (FALSE);
+    }
+  }
+    
+  /* CERROR in data file is in arcsec */
+  if (!gfits_scan (header, "CERROR",   "%lf", 1, &tmp)) tmp = 1.0;
+  image[0].cerror = tmp * 50.0;
+ 
+  /*** why are we no longer using APMIFIT?? ***/
+  tmp = 0;
+  /* gfits_scan (header, "APMIFIT",  "%lf", 1, &tmp); */
+  image[0].apmifit = tmp;
+
+  tmp = 0;
+  /* gfits_scan (header, "dAPMIFIT", "%lf", 1, &tmp); */
+  image[0].dapmifit = tmp;
+
+  tmp = 0;
+  gfits_scan (header, "FLIMIT",   "%lf", 1, &tmp);
+  image[0].detection_limit = tmp * 10.0;
+
+  tmp = 0;
+  gfits_scan (header, "FSATUR",   "%lf", 1, &tmp);
+  image[0].saturation_limit = tmp * 10.0;
+
+  tmp = 0;
+  gfits_scan (header, "FWHM_X",   "%lf", 1, &tmp);
+  image[0].fwhm_x = tmp * 25.0 * image[0].coords.cdelt1 * 3600.0;
+
+  tmp = 0;
+  gfits_scan (header, "FWHM_Y",   "%lf", 1, &tmp);
+  image[0].fwhm_y = tmp * 25.0 * image[0].coords.cdelt1 * 3600.0;
+
+  image[0].trate  = 0.0;
+  image[0].secz   = NAN;
+  image[0].ccdnum = 0;
+
+  /* secz is in units milli-airmass */
+  image[0].Mcal = 0.0;
+  image[0].Xm   = NAN_S_SHORT;
+  image[0].code = 0;
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/SelectImages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/SelectImages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/SelectImages.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "dvoImageExtract.h"
+
+/* given image, find catalog images which overlap it */
+int *SelectImages (char *filename, Image *images, int Nimages, int *Nmatch) {
+  
+  int i, Nchar;
+  int NMATCH, nmatch, *match;
+
+  /* matches here are only based on string comparisons */
+
+  /* match represents the subset of matched images */
+  nmatch = 0;
+  NMATCH = 20;
+  ALLOCATE (match, int, NMATCH);
+
+  /* setup links for mosaic WRP and DIS entries */
+  BuildChipMatch (images, Nimages);
+
+  Nchar = strlen (filename);
+
+  for (i = 0; i < Nimages; i++) {
+
+    if (strncmp (images[i].name, filename, Nchar)) continue;
+
+    match[nmatch] = i;
+    nmatch ++;
+    if (nmatch == NMATCH) {
+      NMATCH += 20;
+      REALLOCATE (match, int, NMATCH);
+    }
+  }  
+
+  if (VERBOSE) fprintf (stderr, "found %d matching images\n", nmatch);
+
+  *Nmatch = nmatch;
+  return (match);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/SetSignals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/SetSignals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/SetSignals.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "getstar.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect these signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored (POSIX.1-1990) */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored (POSIX.1-1990) */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore (POSIX.1-1990) */
+    case SIGCONT:    /* continue - maintain this action (POSIX.1-1990) */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? (POSIX.1-1990) */
+    case SIGURG:     /* socket signal, ignore this (POSIX.1-2001) */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/Shutdown.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/Shutdown.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/Shutdown.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "getstar.h"
+
+static FITS_DB *db;
+
+void set_db (FITS_DB *in) {
+  db = in;
+}
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format));
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  SetProtect (TRUE);
+  gfits_db_close (db);
+  fprintf (stderr, "ERROR: getstar halted\n");
+  exit (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/WriteImageFITS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/WriteImageFITS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/WriteImageFITS.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "dvoImageExtract.h"
+
+/* given image, find catalog images which overlap it.  this function actually creates an
+ * image-less file, filling in the header, but not the pixels.
+ */
+int WriteImageFITS (FILE *f, Image *image) {
+  
+  int Nstars;
+  Header header;
+  Header theader;
+  FTable table;
+
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  header.Naxes = 2;
+  if (image) {
+    header.Naxis[0] = image[0].NX;
+    header.Naxis[1] = image[0].NY;
+  }
+  gfits_create_header (&header);
+  gfits_modify (&header, "NAXIS", "%d", 1, 0);
+
+  if (image) {
+    PutCoords (&image[0].coords, &header);
+  }
+  /* do not create data matrix - the matrix is defined to be empty (NAXIS=0)
+     gfits_create_matrix (&header, &matrix);
+  */
+  gfits_fwrite_header  (f, &header);
+
+  // gfits_fwrite_matrix  (f, &matrix);
+  return (TRUE);
+
+  Nstars = 0;
+  if (image) Nstars = image[0].nstar;
+
+  table.header = &theader;
+  gfits_table_set_SMPData (&table, NULL, Nstars);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &table);
+
+  return TRUE;
+}
+
+// XXX this is a temporary hack to get skycell output working
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/WriteImages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/WriteImages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/WriteImages.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "dvoImageExtract.h"
+
+/* given image, find catalog images which overlap it */
+int WriteImages (char *filename, Image *images, int Nimages, int *matches, int Nmatches) {
+  
+  int i, N;
+  int nWRP, newWRP, isWRP;
+  FILE *f;
+
+  /* matches here are only based on string comparisons */
+
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "failed to open output file %s\n", filename);
+    exit (1);
+  }
+
+  nWRP = -1;
+  isWRP = FALSE;
+  for (i = 0; i < Nmatches; i++) {
+    if (!strcmp (&images[0].coords.ctype[4], "-WRP")) {
+      if (!FindMosaicForImage (images, Nimages, i)) {
+	fprintf (stderr, "failed to find matching mosaic\n");
+	exit (1);
+      }
+      if (isWRP) {
+	newWRP = GetRegisteredMosaic();
+	if (newWRP != nWRP) {
+	  fprintf (stderr, "only one mosaic allowed in an output file\n");
+	  exit (1);
+	}
+      } else {
+	nWRP = GetRegisteredMosaic();
+	isWRP = TRUE;
+      }
+    }      
+  }
+
+  if (isWRP) {
+    WriteImageFITS (f, &images[nWRP]);
+  } else {
+    // write a blank
+    if (Nmatches > 1) {
+      WriteImageFITS (f, NULL);
+    }
+  }
+
+  for (i = 0; i < Nmatches; i++) {
+    N = matches[i];
+    WriteImageFITS (f, &images[N]);
+  }  
+
+  fclose (f);
+  return TRUE;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/args.c	(revision 22322)
@@ -0,0 +1,159 @@
+# include "getstar.h"
+
+void help () {
+  fprintf (stderr, "USAGE: \n"
+	   "getstar -region ra dec ra dec\n"
+	   "getstar -radius ra dec radius\n"
+	   "getstar -catalog n0000/0000.cpt\n"
+	   "getstar -image name\n"
+	   "getstar -immatch partial-name\n\n"
+	   " options: \n"
+	   " -maglim (mag)    : maximum magnitude returned\n"
+	   " -format (format) : output formats (CATALOG, PS1_DEV_0, PS1_DEV_1, PS1_DEV_2)\n"
+	   " -photcode (code) : desired photcode for output magnitudes\n"
+	   " -o output        : defaults to stdout\n"
+	   " -v               : verbose mode\n"
+	   " -h / -help       : this list\n"
+    );
+  exit (2);
+}
+
+int args (int argc, char **argv) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /* configuration info */
+  ConfigInit (&argc, argv);
+
+  /* check for command line options */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  MagLimitUse = FALSE;
+  if ((N = get_argument (argc, argv, "-maglim"))) {
+    MagLimitUse = TRUE;
+    remove_argument (N, &argc, argv);
+    MagLimitValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* check for command line options */
+  strcpy (OUTPUT, "stdout");
+  if ((N = get_argument (argc, argv, "-o"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (OUTPUT, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* check for command line options */
+  strcpy (OUTFORMAT, "CATALOG");
+  if ((N = get_argument (argc, argv, "-format"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (OUTFORMAT, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  // in some cases, we need a photcode
+  photcode = GetPhotcodebyNsec (0); // default to first average photcode
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    photcode = GetPhotcodebyName (argv[N]);
+    if (photcode == NULL) {
+      fprintf (stderr, "photcode %s not found in photcode table\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  /* parse optional entries above. one of the options below is required */
+  MODE = BY_NOTHING;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    double R, D;
+    MODE = BY_REGION;
+    remove_argument (N, &argc, argv);
+    if (argc != 5) help();
+    ohana_str_to_radec (&R, &D, argv[N+0], argv[N+1]);
+    REGION.Rmin = R;
+    REGION.Dmin = D;
+    remove_argument (N, &argc, argv);
+    remove_argument (N, &argc, argv);
+
+    ohana_str_to_radec (&R, &D, argv[N+0], argv[N+1]);
+    REGION.Rmax = R;
+    REGION.Dmax = D;
+    remove_argument (N, &argc, argv);
+    remove_argument (N, &argc, argv);
+
+    // XXX we will have issues at 0,360 boundary...
+    // see code in dvo/pmeasure for fixes
+    while (REGION.Rmin > 360) REGION.Rmin -= 360.0;
+    while (REGION.Rmin <   0) REGION.Rmin += 360.0;
+    while (REGION.Rmax > 360) REGION.Rmax -= 360.0;
+    while (REGION.Rmax <   0) REGION.Rmax += 360.0;
+    if (REGION.Dmax < REGION.Dmin) {
+	SWAP (REGION.Dmax, REGION.Dmin);
+    }
+  }
+  if ((N = get_argument (argc, argv, "-radius"))) {
+    double R, D, radius;
+    fprintf (stderr, "-radius is not recommended\n");
+    MODE = BY_RADIUS;
+    remove_argument (N, &argc, argv);
+    if (argc != 4) help();
+    ohana_str_to_radec (&R, &D, argv[N+0], argv[N+1]);
+    radius = atof(argv[N+2]);
+    REGION.Rmin = R - radius / cos(D*RAD_DEG);
+    REGION.Dmin = D - radius;
+    REGION.Rmax = R + radius / cos(D*RAD_DEG);
+    REGION.Dmax = D + radius;
+    remove_argument (N, &argc, argv);
+    remove_argument (N, &argc, argv);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-catalog"))) {
+    fprintf (stderr, "-catalog is not implemented\n");
+    exit (2);
+    MODE = BY_CATALOG;
+    remove_argument (N, &argc, argv);
+    if (argc != 2) help();
+  }
+  if ((N = get_argument (argc, argv, "-image"))) {
+    MODE = BY_IMAGE;
+    remove_argument (N, &argc, argv);
+    IMAGENAME = strcreate (argv[N]);
+    if (argc != 2) help();
+  }
+  if ((N = get_argument (argc, argv, "-immatch"))) {
+    MODE = BY_IMMATCH;
+    remove_argument (N, &argc, argv);
+    IMAGENAME = strcreate (argv[N]);
+    if (argc != 2) help();
+  }
+  if (MODE == BY_NOTHING) help ();
+
+  return (TRUE);
+}
+
+
+/* USAGE
+
+getstar -region ra dec ra dec
+getstar -radius ra dec radius
+getstar -catalog n0000/0000.cpt
+getstar -image name [-smp | -smf]
+getstar -immatch partial-name [-smp | -smf]
+   
+* return measurements 
+* return average / secfilt table
+* return a single image (smf/smp format)
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/args_extract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/args_extract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/args_extract.c	(revision 22322)
@@ -0,0 +1,38 @@
+# include "dvoImageExtract.h"
+
+void help () {
+  fprintf (stderr, "USAGE: \n"
+	   "dvoExtractImages (imageID) [-o output]\n"
+    );
+  exit (2);
+}
+
+int args_extract (int argc, char **argv) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /* check for command line options */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* check for command line options */
+  OUTFILE = NULL;
+  if ((N = get_argument (argc, argv, "-o"))) {
+    remove_argument (N, &argc, argv);
+    OUTFILE = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) help();
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/args_overlaps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/args_overlaps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/args_overlaps.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "dvoImageOverlaps.h"
+
+void help () {
+  fprintf (stderr, "USAGE: \n"
+	   "dvoImageOverlaps (image)\n"
+    );
+  exit (2);
+}
+
+int args_overlaps (int argc, char **argv) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /* check for command line options */
+  WITH_PHU = FALSE;
+  if ((N = get_argument (argc, argv, "+phu"))) {
+    WITH_PHU = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  SOLO_PHU = FALSE;
+  if ((N = get_argument (argc, argv, "-phu"))) {
+    WITH_PHU = TRUE;
+    SOLO_PHU = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* check for command line options */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* accept bad header astrometry */
+  ACCEPT_ASTROM = FALSE;
+  if ((N = get_argument (argc, argv, "-accept"))) {
+    ACCEPT_ASTROM = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-accept-astrom"))) {
+    ACCEPT_ASTROM = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* provide a mosaic for distortion */
+  MOSAIC = NULL;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    Header header;
+    ALLOCATE (MOSAIC, Coords, 1);
+
+    remove_argument (N, &argc, argv);
+    if (!gfits_read_header (argv[N], &header)) {
+      fprintf (stderr, "ERROR: can't read header for mosaic %s\n", argv[N]);
+      exit (1);
+    }
+    if (!GetCoords (MOSAIC, &header)) {
+      fprintf (stderr, "ERROR: no astrometric solution in header\n");
+      exit (1);
+    }
+    if (strcmp(&MOSAIC[0].ctype[4], "-DIS")) {
+      fprintf (stderr, "ERROR: not a mosaic distortion header\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    gfits_free_header (&header);
+  }
+
+  if (argc != 2) help();
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/dvoImageExtract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/dvoImageExtract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/dvoImageExtract.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "dvoImageExtract.h"
+
+int main (int argc, char **argv) {
+
+  int Nimages, status;
+  int Nmatches, *matches;
+  Image *images;
+  FITS_DB db;
+
+  SetSignals ();
+  ConfigInit_extract (&argc, argv);
+  args_extract (argc, argv);
+
+  /*** update the image table ***/
+  /* setup image table format and lock */
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  status    = dvo_image_lock (&db, ImageCat, 3600.0, LCK_SOFT);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  /* load or create the image table */
+  if (db.dbstate == LCK_EMPTY) {
+    fprintf (stderr, "no images in database (%s)\n", ImageCat);
+    exit (1);
+  } else {
+    if (!dvo_image_load (&db, VERBOSE, FALSE)) {
+      Shutdown ("can't read image catalog %s", db.filename);
+    }
+  }
+  dvo_image_unlock (&db);
+
+  // convert database table to internal structure
+  images = gfits_table_get_Image (&db.ftable, &Nimages, &db.swapped);
+  
+  matches = SelectImages (argv[1], images, Nimages, &Nmatches);
+  WriteImages (OUTFILE, images, Nimages, matches, Nmatches);
+
+  exit (0);
+}
+
+/* This program extracts images headers from a DVO database and writes them 
+   in cmf format (headers + object tables).
+
+   If multiple images are selected, they must be from the same exposures (how?).
+
+   If images are selected with WRP astrometry, the associated DIS image header is first written to
+   the output file.
+ 
+   USAGE: dvoExtractImages (imageID[s]) -o output.cmf
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/dvoImageOverlaps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/dvoImageOverlaps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/dvoImageOverlaps.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "dvoImageOverlaps.h"
+
+int main (int argc, char **argv) {
+
+  int i, Nimages, NdbImages, status;
+  int Nmatches, *matches;
+  Image *images, *dbImages;
+  FITS_DB db;
+
+  SetSignals ();
+  ConfigInit_overlaps (&argc, argv);
+  args_overlaps (argc, argv);
+  
+  images = ReadImageFiles (argv[1], &Nimages); 
+
+  /*** update the image table ***/
+  /* setup image table format and lock */
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  status    = dvo_image_lock (&db, ImageCat, 3600.0, LCK_SOFT);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  /* load or create the image table */
+  if (db.dbstate == LCK_EMPTY) {
+    fprintf (stderr, "no images in database (%s)\n", ImageCat);
+    exit (1);
+  } else {
+    if (!dvo_image_load (&db, VERBOSE, FALSE)) {
+      Shutdown ("can't read image catalog %s", db.filename);
+    }
+  }
+  dvo_image_unlock (&db);
+
+  // convert database table to internal structure
+  dbImages = gfits_table_get_Image (&db.ftable, &NdbImages, &db.swapped);
+  
+  // for (i = 1; i < 2; i++) {
+  for (i = 0; i < Nimages; i++) {
+    matches = MatchImage (dbImages, NdbImages, &images[i], &Nmatches);
+    ListImageOverlaps (dbImages, &images[i], matches, Nmatches);
+  }
+
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/edge_check.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/edge_check.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/edge_check.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "dvoImageOverlaps.h"
+
+int edge_check (double *x1, double *y1, double *x2, double *y2) {
+
+  double theta1, theta2;
+  double Theta1, Theta2;
+
+  theta1 = opening_angle (x1[0], y1[0], x2[0], y2[0], x1[1], y1[1]); 
+  theta2 = opening_angle (x1[0], y1[0], x2[0], y2[0], x2[1], y2[1]); 
+
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  Theta1 = theta1;
+  Theta2 = theta2;
+  theta1 = opening_angle (x2[0], y2[0], x1[1], y1[1], x2[1], y2[1]); 
+  theta2 = opening_angle (x2[0], y2[0], x1[1], y1[1], x1[0], y1[0]); 
+  
+ 
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/getstar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/getstar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/getstar.c	(revision 22322)
@@ -0,0 +1,114 @@
+# include "getstar.h"
+
+int main (int argc, char **argv) {
+
+  int i;
+  SkyTable *sky;
+  SkyList *skylist;
+  Catalog catalog;
+  Catalog output;
+  FITS_DB db;
+
+  args (argc, argv);
+  set_db (&db);
+
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+
+  // create an output catalog with the desired name and format options
+  dvo_catalog_init (&output, TRUE);
+  output.filename  = OUTPUT;
+  output.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+  output.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+  output.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+  output.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+  // this has to be here because the 'open' inits the catalog (perhaps not ideal)
+  if (!strcmp (OUTFORMAT, "CATALOG")) {
+    unlink (OUTPUT);
+    dvo_catalog_open   (&output, NULL, VERBOSE, "w");
+  }
+
+  switch (MODE) {
+
+    case BY_REGION:
+    case BY_RADIUS:
+
+      /* load corresponding sky regions */
+      skylist = SkyListByPatch (sky, -1, &REGION);
+      for (i = 0; i < skylist[0].Nregions; i++) {
+	// set the parameters which guide catalog open/load/create
+	catalog.filename = skylist[0].filename[i];
+	catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+	catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+
+	// an error exit status here is a significant error
+	if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "r")) {
+	  fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+	  exit (2);
+	}
+	if (!catalog.Naves_disk) {
+	  dvo_catalog_unlock (&catalog);
+	  dvo_catalog_free (&catalog);
+	  continue;
+	}
+	dvo_catalog_unlock (&catalog);
+
+	/* skip empty catalogs */
+	select_by_region (&output, &catalog, &REGION, 0, 0);
+      }
+      break;
+
+    case BY_IMLIST:
+      /* load image list */
+    case BY_IMAGE:
+
+      # if (0)
+      /* load corresponding sky regions */
+      skylist = SkyListByImage (sky, -1, &image, &Nimage);
+      for (i = 0; i < skylist[0].Nregions; i++) {
+	// set the parameters which guide catalog open/load/create
+	catalog.filename = skylist[0].filename[i];
+	catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+	catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+
+	// an error exit status here is a significant error
+	if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "r")) {
+	  fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+	  exit (2);
+	}
+	/* skip empty catalogs */
+	if (!catalog.Naves_disk) continue;
+	stars = select_by_image (&catalog, &image, 0, 0, stars, &Nstars);
+      }
+      # endif
+      fprintf (stderr, "error: BY_IMAGE not implemented\n");
+      exit (1);
+      break;
+
+    case BY_CATALOG:
+      fprintf (stderr, "error: BY_CATALOG not implemented\n");
+      exit (1);
+      break;
+      
+    default:
+      fprintf (stderr, "error: invalid options\n");
+      exit (1);
+  }
+
+  if (!strcmp (OUTFORMAT, "CATALOG")) {
+    write_catalog (&output);
+  }
+  if (!strcmp (OUTFORMAT, "PS1_DEV_0")) {
+    write_getstar_PS1_DEV_0 (&output);
+  }
+  if (!strcmp (OUTFORMAT, "PS1_DEV_1")) {
+    write_getstar_PS1_DEV_1 (&output);
+  }
+  if (!strcmp (OUTFORMAT, "PS1_DEV_2")) {
+    write_getstar_PS1_DEV_2 (&output);
+  }
+
+  fprintf (stderr, "error: invalid output format %s\n", OUTFORMAT);
+  exit (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/opening_angle.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/opening_angle.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/opening_angle.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "dvoImageOverlaps.h"
+
+double opening_angle (double x1, double y1, double x2, double y2, double x3, double y3) {
+
+  double dx1, dy1, dx2, dy2, ct, st, theta;
+
+  dx1 = x1 - x2;
+  dy1 = y1 - y2;
+  
+  dx2 = x3 - x2;
+  dy2 = y3 - y2;
+  
+  ct = (dx1*dx2 + dy1*dy2);
+  st = (dx1*dy2 - dx2*dy1);
+
+  theta = atan2 (st, ct);
+
+  return (theta);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/parse_time.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/parse_time.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/parse_time.c	(revision 22322)
@@ -0,0 +1,146 @@
+# include "addstar.h"
+
+int parse_time (Header *header) {
+
+  double jd;
+  int Ny, Nf, mode;
+  int Nsec, hour, min, sec, year, month, day;
+  char *py, *pm, *pd, *c;
+  char line[256];
+
+  /* we want to find JD or MJD to get Nsec (seconds since 01/01/1970) */
+
+  /* try JD first */
+  if (strcasecmp (JDKeyword, "NONE")) {
+    uppercase (JDKeyword);
+    if (!gfits_scan (header, JDKeyword, "%lf", 1, &jd)) {
+      fprintf (stderr, "ERROR: missing JD Keyword %s\n", JDKeyword);
+      exit (1);
+    }
+    Nsec = (jd - 2440587.5)*86400;
+    return (Nsec);
+  }
+
+  /* try MJD next */
+  if (strcasecmp (MJDKeyword, "NONE")) {
+    uppercase (MJDKeyword);
+    if (!gfits_scan (header, MJDKeyword, "%lf", 1, &jd)) {
+      fprintf (stderr, "ERROR: missing MJD Keyword %s\n", MJDKeyword);
+      exit (1);
+    }
+    Nsec = (jd - 40587.0)*86400;
+    return (Nsec);
+  }
+    
+  if (!strcasecmp (UTKeyword, "NONE")) {
+      fprintf (stderr, "ERROR: no valid Date/Time keywords\n");
+      exit (1);
+  }
+  if (!strcasecmp (DateKeyword, "NONE")) {
+      fprintf (stderr, "ERROR: no valid Date/Time keywords\n");
+      exit (1);
+  }
+  if (!strcasecmp (DateMode, "NONE")) {
+      fprintf (stderr, "ERROR: no valid Date/Time keywords\n");
+      exit (1);
+  }
+
+  /* get UT and DATE */
+  uppercase (UTKeyword);
+  if (!gfits_scan (header, UTKeyword, "%s", 1, line)) {
+      fprintf (stderr, "ERROR: missing UT Keyword %s\n", UTKeyword);
+      exit (1);
+    }
+  /* remove ':' characters */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  sscanf (line, "%d %d %d", &hour, &min, &sec);
+
+  /* parse mode line */
+  uppercase (DateMode);
+  for (Ny = 0, c = strchr (DateMode, 'Y'); c != (char ) NULL; c = strchr (c + 1, 'Y'), Ny++);
+  if ((Ny != 2) && (Ny != 4)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  py = strchr (DateMode, 'Y');
+  pm = strchr (DateMode, 'M');
+  pd = strchr (DateMode, 'D');
+  if ((py == (char *) NULL) || (pm == (char *) NULL) || (pd == (char *) NULL)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  if ((py > pm) && (py < pd)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  if ((py > pd) && (py < pm)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  mode = 0;
+  if ((py < pm) && (pm < pd)) { mode = 1; }  /* yyyy-mm-dd */
+  if ((py < pm) && (pm > pd)) { mode = 2; }  /* yyyy-dd-mm */
+  if ((py > pm) && (pm < pd)) { mode = 3; }  /* mm-dd-yyyy */
+  if ((py > pm) && (pm > pd)) { mode = 4; }  /* dd-mm-yyyy */
+  if (!mode) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+
+  /* parse date entry */
+  uppercase (DateKeyword);
+  if (!gfits_scan (header, DateKeyword, "%s",  1, line)) {
+    fprintf (stderr, "ERROR: missing DATE Keyword %s\n", DateKeyword);
+    exit (1);
+  }
+  /* remove possible separators: ':', '/' '.', '-' */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  for (c = strchr (line, 0x2f); c != (char *) NULL; c = strchr (line, 0x2f)) { *c = ' '; }
+  for (c = strchr (line, 0x2e); c != (char *) NULL; c = strchr (line, 0x2e)) { *c = ' '; }
+  for (c = strchr (line, 0x2d); c != (char *) NULL; c = strchr (line, 0x2d)) { *c = ' '; }
+
+  Nf = 0;
+  switch (mode) {
+  case 1:
+    Nf = sscanf (line, "%d %d %d", &year, &month, &day);
+    break;
+  case 2:
+    Nf = sscanf (line, "%d %d %d", &year, &day, &month);
+    break;
+  case 3:
+    Nf = sscanf (line, "%d %d %d", &month, &day, &year);
+    break;
+  case 4:
+    Nf = sscanf (line, "%d %d %d", &day, &month, &year);
+    break;
+  }
+  if (Nf != 3) {
+    fprintf (stderr, "error in date entry (%s) or DATE-MODE format (%s)\n", line, DateMode);
+    exit (1);
+  }
+
+  if (year > 1000) {
+    if (Ny == 2) {
+      fprintf (stderr, "warning: mode line claims 2 digit year, but 4 digit year found\n");
+    }
+  } else {
+    if (Ny == 4) {
+      fprintf (stderr, "warning: mode line claims 4 digit year, but 2 digit year found\n");
+    }
+    if (year < 50) year += 100;
+    year += 1900;
+  }    
+
+  /* this should probably use localtime */
+
+  /* convert yy.mm.dd hh.mm.ss to Nsec since 1970 (jd = 2440587.5) */
+  /* note that in this section, tm_mon has range 1-12, unlike for gmtime () */
+  jd = day - 32075 + (int)(1461*(year + 4800 + (int)(((month)-14)/12))/4)
+    + (int)(367*((month) - 2 - (int)(((month) - 14)/12)*12)/12)
+    - (int)(3*(int)((year + 4900 + (int)(((month) - 14)/12))/100)/4) - 0.5;
+  /* jd is the julian day of the whole day only not the time */
+  Nsec = (jd - 2440587.5)*86400 + 3600.0*hour + min*60.0 + sec;
+  
+  return (Nsec);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/select_by_image.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/select_by_image.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/select_by_image.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "getstar.h"
+
+Stars *select_by_image (Catalog *catalog, Image *image, int start, int end, Stars *stars, int *Nstar) {
+
+  int i, n, N, NSTARS;
+  int photcode;
+
+  if (stars == (Stars *) NULL) {
+    N = 0;
+    NSTARS = 1000;
+    ALLOCATE (stars, Stars, NSTARS);
+  } else {
+    N = *Nstar;
+    NSTARS = N + 1000;
+    REALLOCATE (stars, Stars, NSTARS);
+  }    
+
+  /* identify selection criteria */
+  photcode = -1;
+  if ((start == 0) && (end == 0)) {
+    start = image[0].tzero;
+    end   = image[0].tzero + 1e-4*image[0].NY*image[0].trate;  /* trate is in 0.1 msec / row */
+    photcode = image[0].photcode;
+  }
+  if (VERBOSE) fprintf (stderr, "extracting for range %d to %d (photcode %s)\n", start, end, photcode);
+
+  for (i = 0; (i < catalog[0].Nmeasure); i++) {
+    if ((i % 10000) == 0) fprintf (stderr, ". ");
+    if ((catalog[0].measure[i].t >= start) && (catalog[0].measure[i].t <= end) && (photcode == catalog[0].measure[i].photcode)) { 
+      n = catalog[0].measure[i].averef;
+      stars[N].R      = catalog[0].average[n].R - catalog[0].measure[i].dR / 360000.0;
+      stars[N].D      = catalog[0].average[n].D - catalog[0].measure[i].dD / 360000.0;
+
+      stars[N].M      = 0.001*(catalog[0].measure[i].M - catalog[0].measure[i].dt);
+      stars[N].dM     = catalog[0].measure[i].dM;
+      stars[N].dophot = catalog[0].measure[i].dophot;  
+
+      stars[N].Mgal   = 0.001*(catalog[0].measure[i].Mgal - catalog[0].measure[i].dt);
+
+      stars[N].fx     = 0.01*catalog[0].measure[i].FWx;
+      stars[N].fy     = stars[N].fx * 0.01*catalog[0].measure[i].fwy;
+      stars[N].df     = (360.0/255.0)*catalog[0].measure[i].theta;
+      stars[N].found  = catalog[0].measure[i].flags;
+
+      N ++;
+      if (N == NSTARS) {
+	NSTARS += 1000;
+	REALLOCATE (stars, Stars, NSTARS);
+      }    
+
+    } 
+  }
+  fprintf (stderr, "found %d meas\n", N);
+  *Nstar = N;
+  return (stars);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/select_by_region.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/select_by_region.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/select_by_region.c	(revision 22322)
@@ -0,0 +1,98 @@
+# include "getstar.h"
+
+/* add selected catalog objects to the output catalog */
+int select_by_region (Catalog *output, Catalog *catalog, SkyRegion *region, int start, int end) {
+
+  int i, j, n, Nm, offset, m, Nsecfilt, code, Nsec;
+  int Nave, NAVE, Nmeas, NMEAS;
+  double R, D;
+  float mag;
+
+  Nsecfilt = output[0].Nsecfilt;
+
+  if (output == NULL) Shutdown ("output not defined");
+
+  /* identify selection criteria */
+  if (VERBOSE) fprintf (stderr, "extracting for time range %d to %d\n", start, end);
+  if (VERBOSE) fprintf (stderr, "extracting for region %f,%f to %f,%f\n", 
+			region[0].Rmin, region[0].Dmin, region[0].Rmax, region[0].Dmax);
+
+  if (output[0].header.buffer != NULL) {
+    gfits_modify (&output[0].header, "RA0",  "%lf", 1, region[0].Rmin);
+    gfits_modify (&output[0].header, "DEC0", "%lf", 1, region[0].Dmin);
+    gfits_modify (&output[0].header, "RA1",  "%lf", 1, region[0].Rmax);
+    gfits_modify (&output[0].header, "DEC1", "%lf", 1, region[0].Dmax);
+  }
+
+  code = photcode[0].code;
+  Nsec = GetPhotcodeNsec (code);
+
+  /* select the average objects in this region */
+  Nave = output[0].Naverage;
+  NAVE = output[0].Naverage + 1000;
+  REALLOCATE (output[0].average, Average, NAVE);
+  REALLOCATE (output[0].secfilt, SecFilt, NAVE*Nsecfilt);
+
+  Nmeas = output[0].Nmeasure;
+  NMEAS = output[0].Nmeasure + 1000;
+  REALLOCATE (output[0].measure, Measure, NMEAS);
+
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    n = catalog[0].measure[i].averef;
+
+    R = catalog[0].average[i].R;
+    D = catalog[0].average[i].D;
+    
+    if (R < region[0].Rmin) continue;
+    if (R > region[0].Rmax) continue;
+    if (D < region[0].Dmin) continue;
+    if (D > region[0].Dmax) continue;
+
+    if (MagLimitUse) {
+      mag = NAN;
+      if (Nsec != -1) {
+	mag = catalog[0].secfilt[i*Nsecfilt + Nsec].M;
+      } else {
+	offset = catalog[0].average[i].measureOffset;
+	for (m = 0; m < catalog[0].average[i].Nmeasure; m++) {
+	  if (catalog[0].measure[offset + m].photcode == code) {
+	    mag = PhotRel (&catalog[0].measure[offset + m], &catalog[0].average[i], &catalog[0].secfilt[i*Nsecfilt]);
+	    break;
+	  }
+	}
+      }
+      if (isnan(mag) || (mag > MagLimitValue)) continue;
+    }
+
+    output[0].average[Nave] = catalog[0].average[i];
+    output[0].average[Nave].measureOffset = Nmeas;
+    for (j = 0; j < Nsecfilt; j++) {
+      output[0].secfilt[Nsecfilt*Nave + j] = catalog[0].secfilt[Nsecfilt*i + j];
+    }
+
+    Nm = 0;
+    offset = catalog[0].average[i].measureOffset;
+
+    for (j = 0; j < catalog[0].average[i].Nmeasure; j++) {
+      output[0].measure[Nmeas] = catalog[0].measure[offset + j];
+      output[0].measure[Nmeas].averef = Nave;
+      Nmeas ++;
+      
+      CHECK_REALLOCATE (output[0].measure, Measure, NMEAS, Nmeas, 1000);
+    }      
+
+    Nave ++;
+    if (Nave == NAVE) {
+      NAVE += 1000;
+      REALLOCATE (output[0].average, Average, NAVE);
+      REALLOCATE (output[0].secfilt, SecFilt, NAVE*Nsecfilt);
+    }
+  }
+  output[0].Naverage = Nave;
+  output[0].Nmeasure = Nmeas;
+  output[0].Nsecf_mem = Nave*Nsecfilt;
+
+  fprintf (stderr, "output catalog has %d stars (%d measures, %d secfilt)\n",
+	   output[0].Naverage, output[0].Nmeasure, output[0].Nsecfilt);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_catalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_catalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_catalog.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include "getstar.h"
+
+int write_catalog (Catalog *catalog) {    
+
+  /* write out the selected stars */
+  // XXX need to set the catalog boundaries by hand? RA0-RA1, DEC0-DEC1
+  dvo_catalog_save   (catalog, VERBOSE);
+  dvo_catalog_unlock (catalog);
+  dvo_catalog_free   (catalog);
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_getstar_ps1_dev_0.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_getstar_ps1_dev_0.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_getstar_ps1_dev_0.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "getstar.h"
+
+// convert the average/secfilt values to getstar format 
+// PS1_DEV_0 has no errors or motions, only positions and magnitudes
+int write_getstar_PS1_DEV_0 (Catalog *catalog) {    
+
+  int i, m, offset;
+  int Nsec_c0, Nsec_c1, Nsec_c2, Nsecfilt;
+  int code_c0, code_c1, code_c2;
+  Average *average;
+  Measure *measure;
+  SecFilt *secfilt;
+  Getstar_PS1_DEV_0 *output;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  FILE *f;
+  int Noutput;
+
+  Noutput = catalog[0].Naverage;
+  ALLOCATE (output, Getstar_PS1_DEV_0, Noutput);
+
+  // photcode is a global
+  if (photcode == NULL) {
+    fprintf (stderr, "undefined photcode\n");
+    exit (2);
+  }
+
+  Nsec_c0 = GetPhotcodeNsec (photcode[0].code);
+  Nsec_c1 = GetPhotcodeNsec (photcode[0].c1);
+  Nsec_c2 = GetPhotcodeNsec (photcode[0].c2);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  code_c0 = photcode[0].code;
+  code_c1 = photcode[0].c1;
+  code_c2 = photcode[0].c2;
+  measure = catalog[0].measure;
+  average = catalog[0].average;
+  secfilt = catalog[0].secfilt;
+
+  // do we skip any of catalog entries? (probably not)
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    
+    output[i].R        = average[i].R;
+    output[i].D        = average[i].D;
+
+    output[i].code     = average[i].code;
+    output[i].photcode = code_c0;
+
+    // It is not necessary for the output magnitude or color terms to be average values.
+    // If they are, we grab them quickly & easily from the secfilt table.  If not, then we
+    // need to scan the list of measures to find the value of interest
+
+    // find primary magnitude
+    if (Nsec_c0 != -1) {
+      output[i].mag = secfilt[i*Nsecfilt + Nsec_c0].M;
+    } else {
+      output[i].mag = NAN;
+      offset = average[i].measureOffset;
+      for (m = 0; m < average[i].Nmeasure; m++) {
+        if (measure[offset + m].photcode == code_c0) {
+          output[i].mag = PhotRel (&measure[offset + m], &average[i], &secfilt[i*Nsecfilt]);
+	  break;
+        }
+      }
+    }
+
+    // find color term 1
+    if (Nsec_c1 != -1) {
+      output[i].c1 = secfilt[i*Nsecfilt + Nsec_c1].M;
+    } else {
+      output[i].c1 = NAN;
+      offset = average[i].measureOffset;
+      for (m = 0; m < average[i].Nmeasure; m++) {
+        if (measure[offset + m].photcode == code_c1) {
+          output[i].c1 = PhotRel (&measure[offset + m], &average[i], &secfilt[i*Nsecfilt]);
+	  break;
+        }
+      }
+    }
+
+    // find color term 2
+    if (Nsec_c2 != -1) {
+      output[i].c2 = secfilt[i*Nsecfilt + Nsec_c2].M;
+    } else {
+      output[i].c2 = NAN;
+      offset = average[i].measureOffset;
+      for (m = 0; m < average[i].Nmeasure; m++) {
+        if (measure[offset + m].photcode == code_c2) {
+          output[i].c2 = PhotRel (&measure[offset + m], &average[i], &secfilt[i*Nsecfilt]);
+	  break;
+        }
+      }
+    }
+  }
+
+  // open file for output
+  f = fopen (OUTPUT, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open output file %s\n", OUTPUT);
+    exit (1);
+  }
+
+  // create primary header
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+
+  ftable.header = &theader;
+  gfits_table_set_Getstar_PS1_DEV_0 (&ftable, output, Noutput);
+
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &ftable);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_getstar_ps1_dev_1.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_getstar_ps1_dev_1.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_getstar_ps1_dev_1.c	(revision 22322)
@@ -0,0 +1,125 @@
+# include "getstar.h"
+
+// convert the average/secfilt values to getstar format 
+// PS1_DEV_1 has no errors, only positions, motions and magnitudes
+int write_getstar_PS1_DEV_1 (Catalog *catalog) {    
+
+  int i, m, offset;
+  int Nsec_c0, Nsec_c1, Nsec_c2, Nsecfilt;
+  int code_c0, code_c1, code_c2;
+  Average *average;
+  Measure *measure;
+  SecFilt *secfilt;
+  Getstar_PS1_DEV_1 *output;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  FILE *f;
+  int Noutput;
+
+  Noutput = catalog[0].Naverage;
+  ALLOCATE (output, Getstar_PS1_DEV_1, Noutput);
+
+  // photcode is a global
+  if (photcode == NULL) {
+    fprintf (stderr, "undefined photcode\n");
+    exit (2);
+  }
+
+  Nsec_c0 = GetPhotcodeNsec (photcode[0].code);
+  Nsec_c1 = GetPhotcodeNsec (photcode[0].c1);
+  Nsec_c2 = GetPhotcodeNsec (photcode[0].c2);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  code_c0 = photcode[0].code;
+  code_c1 = photcode[0].c1;
+  code_c2 = photcode[0].c2;
+  measure = catalog[0].measure;
+  average = catalog[0].average;
+  secfilt = catalog[0].secfilt;
+
+  // do we skip any of catalog entries? (probably not)
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    
+    output[i].R        = average[i].R;
+    output[i].D        = average[i].D;
+    output[i].uR       = average[i].uR;
+    output[i].uD       = average[i].uD;
+    output[i].P        = average[i].P;
+
+    output[i].code     = average[i].code;
+    output[i].photcode = code_c0;
+
+    // It is not necessary for the output color terms to be average values.  If they are,
+    // we grab them quickly & easily from the secfilt table.  If not, then we need to scan
+    // the list of measures to find the value of interest
+
+    // find primary magnitude
+    if (Nsec_c0 != -1) {
+      output[i].mag = secfilt[i*Nsecfilt + Nsec_c0].M;
+    } else {
+      output[i].mag = NAN;
+      offset = average[i].measureOffset;
+      for (m = 0; m < average[i].Nmeasure; m++) {
+        if (measure[offset + m].photcode == code_c0) {
+          output[i].mag = PhotRel (&measure[offset + m], &average[i], &secfilt[i*Nsecfilt]);
+	  break;
+        }
+      }
+    }
+
+    // find color term 1
+    if (Nsec_c1 != -1) {
+      output[i].c1 = secfilt[i*Nsecfilt + Nsec_c1].M;
+    } else {
+      output[i].c1 = NAN;
+      offset = average[i].measureOffset;
+      for (m = 0; m < average[i].Nmeasure; m++) {
+        if (measure[offset + m].photcode == code_c1) {
+          output[i].c1 = PhotRel (&measure[offset + m], &average[i], &secfilt[i*Nsecfilt]);
+	  break;
+        }
+      }
+    }
+
+    // find color term 2
+    if (Nsec_c2 != -1) {
+      output[i].c2 = secfilt[i*Nsecfilt + Nsec_c2].M;
+    } else {
+      output[i].c2 = NAN;
+      offset = average[i].measureOffset;
+      for (m = 0; m < average[i].Nmeasure; m++) {
+        if (measure[offset + m].photcode == code_c2) {
+          output[i].c2 = PhotRel (&measure[offset + m], &average[i], &secfilt[i*Nsecfilt]);
+	  break;
+        }
+      }
+    }
+  }
+
+  // open file for output
+  f = fopen (OUTPUT, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open output file %s\n", OUTPUT);
+    exit (1);
+  }
+
+  // create primary header
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+
+  ftable.header = &theader;
+  gfits_table_set_Getstar_PS1_DEV_1 (&ftable, output, Noutput);
+
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &ftable);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_getstar_ps1_dev_2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_getstar_ps1_dev_2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/getstar/src/write_getstar_ps1_dev_2.c	(revision 22322)
@@ -0,0 +1,131 @@
+# include "getstar.h"
+
+// convert the average/secfilt values to getstar format 
+int write_getstar_PS1_DEV_2 (Catalog *catalog) {    
+
+  int i, m, offset;
+  int Nsec_c0, Nsec_c1, Nsec_c2, Nsecfilt;
+  int code_c0, code_c1, code_c2;
+  Average *average;
+  Measure *measure;
+  SecFilt *secfilt;
+  Getstar_PS1_DEV_2 *output;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  FILE *f;
+  int Noutput;
+
+  Noutput = catalog[0].Naverage;
+  ALLOCATE (output, Getstar_PS1_DEV_2, Noutput);
+
+  // photcode is a global
+  if (photcode == NULL) {
+    fprintf (stderr, "undefined photcode\n");
+    exit (2);
+  }
+
+  Nsec_c0 = GetPhotcodeNsec (photcode[0].code);
+  Nsec_c1 = GetPhotcodeNsec (photcode[0].c1);
+  Nsec_c2 = GetPhotcodeNsec (photcode[0].c2);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  code_c0 = photcode[0].code;
+  code_c1 = photcode[0].c1;
+  code_c2 = photcode[0].c2;
+  measure = catalog[0].measure;
+  average = catalog[0].average;
+  secfilt = catalog[0].secfilt;
+
+  // do we skip any of catalog entries? (probably not)
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    
+    output[i].R        = average[i].R;
+    output[i].D        = average[i].D;
+    output[i].dR       = average[i].dR;
+    output[i].dD       = average[i].dD;
+
+    output[i].uR       = average[i].uR;
+    output[i].uD       = average[i].uD;
+    output[i].duR      = average[i].duR;
+    output[i].duD      = average[i].duD;
+
+    output[i].P        = average[i].P;
+    output[i].dP       = average[i].dP;
+
+    output[i].code     = average[i].code;
+    output[i].photcode = code_c0;
+
+    // It is not necessary for the output color terms to be average values.  If they are,
+    // we grab them quickly & easily from the secfilt table.  If not, then we need to scan
+    // the list of measures to find the value of interest
+
+    // find primary magnitude
+    if (Nsec_c0 != -1) {
+      output[i].mag = secfilt[i*Nsecfilt + Nsec_c0].M;
+    } else {
+      output[i].mag = NAN;
+      offset = average[i].measureOffset;
+      for (m = 0; m < average[i].Nmeasure; m++) {
+        if (measure[offset + m].photcode == code_c0) {
+          output[i].mag = PhotRel (&measure[offset + m], &average[i], &secfilt[i*Nsecfilt]);
+	  break;
+        }
+      }
+    }
+
+    // find color term 1
+    if (Nsec_c1 != -1) {
+      output[i].c1 = secfilt[i*Nsecfilt + Nsec_c1].M;
+    } else {
+      output[i].c1 = NAN;
+      offset = average[i].measureOffset;
+      for (m = 0; m < average[i].Nmeasure; m++) {
+        if (measure[offset + m].photcode == code_c1) {
+          output[i].c1 = PhotRel (&measure[offset + m], &average[i], &secfilt[i*Nsecfilt]);
+	  break;
+        }
+      }
+    }
+
+    // find color term 2
+    if (Nsec_c2 != -1) {
+      output[i].c2 = secfilt[i*Nsecfilt + Nsec_c2].M;
+    } else {
+      output[i].c2 = NAN;
+      offset = average[i].measureOffset;
+      for (m = 0; m < average[i].Nmeasure; m++) {
+        if (measure[offset + m].photcode == code_c2) {
+          output[i].c2 = PhotRel (&measure[offset + m], &average[i], &secfilt[i*Nsecfilt]);
+	  break;
+        }
+      }
+    }
+  }
+
+  // open file for output
+  f = fopen (OUTPUT, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open output file %s\n", OUTPUT);
+    exit (1);
+  }
+
+  // create primary header
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+
+  ftable.header = &theader;
+  gfits_table_set_Getstar_PS1_DEV_2 (&ftable, output, Noutput);
+
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &ftable);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/Makefile	(revision 22322)
@@ -0,0 +1,46 @@
+default: gophot
+help:
+	@echo "make options: gophot (default)"
+
+include ../../Makefile.System
+HOME    =       $(ROOT)/src/gophot
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+gophot: $(BIN)/gophot.$(ARCH)
+install: $(DESTBIN)/gophot
+
+GOPHOT = \
+$(SRC)/gophot.$(ARCH).o 	$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/message.$(ARCH).o	$(SRC)/dophot.$(ARCH).o \
+$(SRC)/makenoise.$(ARCH).o      $(SRC)/findsky.$(ARCH).o \
+$(SRC)/makemask.$(ARCH).o       $(SRC)/paravg.$(ARCH).o \
+$(SRC)/objout.$(ARCH).o         $(SRC)/addstar.$(ARCH).o \
+$(SRC)/ellipse.$(ARCH).o        $(SRC)/improve.$(ARCH).o \
+$(SRC)/fillerup.$(ARCH).o       $(SRC)/parinterp.$(ARCH).o \
+$(SRC)/guess.$(ARCH).o          $(SRC)/offpic.$(ARCH).o \
+$(SRC)/addlims.$(ARCH).o        $(SRC)/parupd.$(ARCH).o \
+$(SRC)/toofaint.$(ARCH).o       $(SRC)/isearch.$(ARCH).o \
+$(SRC)/shape.$(ARCH).o          $(SRC)/galaxy.$(ARCH).o \
+$(SRC)/transmask.$(ARCH).o      $(SRC)/cosmic.$(ARCH).o \
+$(SRC)/impaper2.$(ARCH).o       \
+$(SRC)/oblit.$(ARCH).o          $(SRC)/toobright.$(ARCH).o \
+$(SRC)/Chisq.$(ARCH).o          $(SRC)/oblims.$(ARCH).o \
+$(SRC)/ludcmp.$(ARCH).o         $(SRC)/lubksb.$(ARCH).o \
+$(SRC)/pseud2d.$(ARCH).o        $(SRC)/twofit.$(ARCH).o \
+$(SRC)/skyfun_plane.$(ARCH).o   $(SRC)/pseud4d.$(ARCH).o \
+$(SRC)/large_features.$(ARCH).o $(SRC)/set_thresholds.$(ARCH).o \
+$(SRC)/outline.$(ARCH).o        $(SRC)/delete_ellipse.$(ARCH).o \
+$(SRC)/feature_fluxes.$(ARCH).o $(SRC)/mediansky.$(ARCH).o
+
+$(GOPHOT): $(INC)/gophot.h $(INC)/prototypes.h
+$(BIN)/gophot.$(ARCH): $(GOPHOT)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,4 @@
+
+- gophot-1-2 : 2006.08.23
+  * converted to gfits APIs (forces libfits 1.6)
+  * cleaned up Makefile, -Wall compilation errors
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/doc/Notes
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/doc/Notes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/doc/Notes	(revision 22322)
@@ -0,0 +1,150 @@
+
+2005.10.20 : gophot v1.1
+
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+Old Notes
+
+Converting dophot to C: (I use dophot.f and dophot.c to refer to the
+fortran and C versions of dophot).
+
+some rules and goals for the first pass:
+
+1) I am maintaining the fortran dophot variable names as much as
+   possible, at least for now. 
+
+2) I am avoiding making proceedural changes / method changes, but I am
+willing to make minor modifications of things like looping strategies
+and if-statement order to clean the code a bit.
+
+3) I have placed all of the common-block variables in a single include
+file, gophot.h, with some attempt to define the names.  I've also placed the
+image data and noise matricies in gophot.h, along with nfast, nslow,
+so these are not passed endlessly back and forth among functions.
+
+4) My (sort-of) working fortran f2c dophot used an external loop,
+getfits.c which enclosed dophot.f and performed the image loading,
+big matrix allocation stuff.  I have renamed that gophot.c and cleaned
+it somewhat.  
+
+5) The biggest structural change so far is the infamous tuneup.f.  I
+have replaced it with my own ConfigInit function, based on my ohana
+config functions.  These have a much cleaner syntax for reading
+configuration values.  I have also placed the ConfigInit function much
+earlier, in gophot.c at the very beginning.  this means that config
+values can be overridden by image header values, if the fits_scan
+lines are coded.  currently, itop is read from the header (SATVALUE).
+
+For the moment, I am only making those changes needed to get a working
+dophot version, not a complete version.  Thus, I am skipping:
+warmstart, autothresh, median sky, hubble sky. 
+
+some function with significant adjustments, other than getfits.c and
+tuneup.f:
+
+message.c: actually this didn't exist in dophot.f - I have created a
+function mprint, which conditionally prints a message based on the
+verbosity level and an associated threshold.  the verbosity is
+registered with set_verbosity (level) and is stored static in
+message.c (not visible to the rest of dophot.c).  the prototype is:
+mprint (int level, char format, ...), with a format and argument
+syntax identical to printf.  all messages go to stderr, for now.
+
+makenoise.c: I am using pointers to do the loops. this should speed
+life alot.
+
+findsky.c: a new addition of mine to determine skyvalue from median of
+image.  this is implemented in the most recent dophot.f, but only
+since March 2000.
+
+There are some typical coding structures in the fortran that I am
+changing to make more legible:
+
+1) a = a + 1, a = a + b --> I'm using C inc args: a++, a+=b
+
+2) nested logic:
+
+{ /* some block */
+
+ code, code, code;
+
+ if (condition) then 
+
+   big block;
+
+ endif
+
+} /* end block */  
+
+there are many of these structures which conditionally perform a large
+operation, and otherwise do nothing or almost nothing.  I'm inverting
+these like this:
+
+{ /* some block */
+
+ code, code, code;
+
+ if (!condition) continue
+
+ big block;
+
+} /* end block */  
+
+data hard.soft.mat 
+read m1 4 m2 15
+set dm = m1 - m2
+lim -17 -7 -1 1; clear; box; plot m1 dm
+
+I have been making some significant changes to the dophot strategy for
+finding stars.  The goal is to handle saturated stars more
+effectively.  The strategy should not really change for faint stars.
+There are a few problems with the old strategy.  
+
+First, we were using an image array and a noise array.  To inhibit
+finding fake stars in the wings of bright stars, the noise array was
+enhanced when the stars were subtracted.  But for each loop, we were
+fitting and re-fitting each star several times.  As a result, we were
+adding, subtracting, adding, and subtracting the noise, which was of
+the order Nstar^2, many times.  Round off errors eventually made the
+noise array useless in the vicinity of bright stars.  Second, bright
+stars likely have many bad pixels, either low from sagging counts at
+or near saturation or high from bleeding.  Since these all have quite
+high flux, they highly discrepant points drive the fits
+significantly.  Finally, the initial guess location used the peak of
+the flux in a box around the trigger pixel.  This was problematic for
+bright stars because bleeding and saturation meant the peak was
+frequently far from the centroid.  I am addressing each of these
+problems separately.
+
+First, for the fits, we are now softening the errors by the residual
+from the initial guess fit.  This is a fitting method called 'robust
+fitting', and it allows significantly discrepant points to be part of
+the data without dominating or driving the fit to funny places.  This
+means the saturated stars are less biased by the few pixels in the
+core which are saturated and discrepant from the fit for the other
+pixels.
+
+Second, for the noise problem, I have made several related changes.
+First, I am now converting the image from DN to electrons (and
+re-scaling all relevant parameters to match).  This means the Poisson
+noise in the original image is now simply sqrt(Ncts).  I am putting
+only the square of the read noise in the noise array.  When I subtract
+a star, I move the counts from the image array to the noise array.
+Thus, the noise per pixel is always (Icts + Ncts) [where Icts = image
+array counts and Ncts = noise array counts] before or after the star
+subtraction.  I now have two tests for bump significance:  1) the
+signal-to-noise must be above a threshold: 
+
+sq(sum[image-sky]) > sum[noise + image] * ratio
+
+and the image counts must be significant:
+
+sub[image-sky] > sum[noise - readnoise^2] * ratio
+
+Finally, the initial guess will use a centroid in fillerup if the flux
+is high enough.
+
+
+
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/include/fit.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/include/fit.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/include/fit.h	(revision 22322)
@@ -0,0 +1,27 @@
+
+# ifndef ALLOCATE
+# define ALLOCATE(X,T,S)  \
+  X=(T *)malloc((unsigned) (MAX(((S)*sizeof(T)),1)));\
+  if(X==NULL) \
+    { \
+      fprintf(stderr,"failed to malloc X\n");\
+        exit (10);\
+    } 
+# define REALLOCATE(X,T,S) \
+  X=(T *)realloc(X,(unsigned) (MAX(((S)*sizeof(T)),1))); \
+  if(X==NULL) \
+    { \
+       fprintf(stderr,"failed to realloc X\n"); \
+       exit (10); \
+    }
+# endif /* ALLOCATE */
+
+float mrq2dinit (int *, int *, float *, float *, int, float *, int, float (funcs)(int *, int *, float *, float *)); 
+float mrq2dmin (int *, int *, float *, float *, int, float *, int, float (funcs)(int *, int *, float *, float *)); 
+float **mrq2dcovar (int);
+
+# define NPARS 8
+# define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+# define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+# define SQ(X)    (double) (((double)(X))*((double)(X)))
+# define SWAP(X,Y) {double tmp=(X); (X) = (Y); (Y) = tmp;}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/include/gophot.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/include/gophot.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/include/gophot.h	(revision 22322)
@@ -0,0 +1,129 @@
+/* includes */
+
+# include <ohana.h>
+# include <gfitsio.h>
+
+typedef char bool;
+
+# define TRUE (1)
+# define FALSE (0)
+# define SIGN(X)  (((X) == 0) ? 0 : ((fabs((double)(X))) / (X)))
+# define ROUND(X) ((int) ((X) + 0.5*SIGN(X)))
+# define SQ(X)    (double) (((double)(X))*((double)(X)))
+# define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+# define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+# define SWAP(X,Y) {double tmp=(X); (X) = (Y); (Y) = tmp;}
+
+/* constants */
+
+# define NRMAX 1024 /* max Ny, deprecated */
+# define NCMAX 1024 /* max Nx, deprecated */
+
+# define CHIPAR 0.9 /* used by chisq.f */
+# define NSMAX 100000 /* max number of stars */
+# define NPMAX 8    /* max number of parameters */
+# define NPAR  8    /* max number of parameters used */
+# define NPSKY 8    /* max sky fit parameters */
+# define NSKYFIT 3  /* max sky fit parameters used */
+# define NFF   20   /* max number of files / flags */
+# define NAPPLE 5   /* number of aper data somethings? */
+# define NAPMAX 30  /* number of correction apertures in file? */
+# define NFIT0  2   /* N par in fit 0 */
+# define NFIT1  4   /* N par in fit 1 */
+# define NFIT2  7   /* N par in fit 2 */
+# define NFIT3  8   /* N par in fit 3 */
+
+# define MAXFIL 5000 /* max size of subraster vector */
+# define NMASK 17    /* max mask size */
+# define MAGIC HUGE_VAL  /* sentinel for bad pixels */
+/* # define MAGICSET 2e30 old value for sentinel */
+
+# define ADD +1
+# define SUB -1
+
+/* global variables */
+
+/* int   lverb;     * verbosity - no longer global */
+float chipar;       /* unknown chisq scale factor */
+float ufactor;      /* star scaling factor */
+
+/* float b[2*NPMAX];   * two-star fit array */
+/* float fb[2*NPMAX];  * two-star fit error array */
+
+bool test7; 
+bool needit;        /* deprecated? */
+
+int xs[MAXFIL], ys[MAXFIL], nrect[3];        /* subraster vectors */
+float zs[MAXFIL], dzs[MAXFIL];   /* subraster vectors */
+float ts[MAXFIL];
+
+float starmask[NMASK][NMASK];
+float parms[NPMAX];
+
+/* float a[NPMAX], fa[NPMAX], c[NPMAX][NPMAX];  fit param arrays */
+float chiimp, apertime, filltime, addtime;   /* deprecated? */
+
+float sum0, sum1, sum2, maxval, xmax, ymax, xmax2, ymax2;  /* crude star statistics */
+int   npt;
+
+float chi[5]; /* almost deprecated, but still in galaxy & shape */
+
+/* tuneup parameters */
+
+enum {NONE1, PGAUSS};
+enum {NONE2, PLANE, HUBBLE, MEDIAN};
+enum {NONE3, COMPLETE, INCOMPLETE, INTERNAL, OLDSTYLE};
+
+char flags[NFF];
+char files[64][NFF];
+bool fixpos;
+
+float  skyguess, tmin, tmax, tfac;
+float  fac, xpnd, ctpersat, widobl, cmax;
+float  stograt, discrim, sig[4], arect[3];
+float  chicrit, xtra, crit7, snlim, bumpcrit, sn2cos;
+float  enuff4, enuff7;
+float  eperdn, rnoise;
+float  acc[NPMAX], parlim[NPMAX], ava[NPMAX];
+float  beta4, beta64;
+float  pixthresh;
+float  apmagmaxerr;
+float  nphsub, nphob, apmax, apskymin, apskymax, aperrmax;
+
+int irect[3], krect[3], ibot, itop, nit, grect[3];
+int icrit, ixby2, iyby2;
+int n0left, n0right, nthpix, nbadleft, nbadright, nbadtop, nbadbot;
+int jhxwid, jhywid, mprec, napertures;
+      
+/* image data */
+float *big, *noise;
+int nfast, nslow;   /* NAXIS1, NAXIS2 of image */
+
+/* star data */
+float starpar[NSMAX][NPMAX];
+float galpar[NSMAX][NPMAX];
+float shadow[NSMAX][NPMAX];
+float shaderr[NSMAX][NPMAX];
+float apple[NSMAX][NAPPLE];
+int   imtype[NSMAX];
+int   nstot;
+float thresh;
+float probgal[NSMAX]; /* deprecated */
+float rchisq[NSMAX]; /* deprecated */
+bool  fixxy;
+
+int nregion;
+float region[100][8];
+
+/* sky data */
+float skypar[NPSKY];
+
+/* image data */
+Header header;
+Matrix matrix;
+
+float (*onestar)(int, int, float *, float *);
+float (*twostar)(int, int, float *, float *);
+float (*skyfun)(int, int, float *, float *);
+
+# include "prototypes.h"
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/include/prototypes.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/include/prototypes.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/include/prototypes.h	(revision 22322)
@@ -0,0 +1,46 @@
+float pseud2d (int ix, int iy, float *a, float *fa);
+float pseud4d (int ix, int iy, float *a, float *fa);
+float skyfun_plane (int ix, int iy, float *a, float *fa);
+float chisq (float (function)(int, int, float *, float *), int *, int *, float *, float *, int, float *, float*, int, float *, float *, int);
+int ConfigInit (int *argc, char **argv);
+int addlims (float *star, int *jrect);
+int addstar (float *instar, int iadd, int type);
+char *SelectConfigFile (int *argc, char **argv, char *progname);
+char *LoadConfigFile (char *filename);
+char *ScanConfig (char *config, char *field, char *mode, int Nentry, ...);
+char *expandline (char *line, char *config);
+bool cosmic (float *star);
+int dophot ();
+int ellipse (float, float, float, float *, float *, float *, float *);
+int errupd (float *, int);
+int fillerup (int, int, int);
+float findsky ();
+bool galaxy (float *, float *, float *);
+float guess1 (float *, float *, int, int);
+float guess2 (float *, float *, int *, int *);
+float guess3 (float *, float *, int *, int *);
+int impaper2 (int);
+int improve (int);
+int isearch ();
+void lubksb (float **, int, int *, float *);
+void ludcmp (float **, int, int *, float *);
+int makemask ();
+int makenoise ();
+int mprint (int, char *, ...);
+int set_verbosity (int);
+int completeout ();
+int oblims (float *, int *);
+bool oblit (float *);
+bool oblit (float *);
+int paravg ();
+float parinterp (float, float, float *);
+int parupd (float *, float *, int, int);
+int shape ();
+int sort (float *, int);
+bool toobright (float *);
+bool toofaint (float *, float *);
+bool transmask (int, int, float);
+float twofit (float *, float *, float *, float *);
+int variparplane ();
+bool offpic (float *, int, int, float *, float *);
+float newguess (float *, float *, int, int);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/Chisq.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/Chisq.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/Chisq.c	(revision 22322)
@@ -0,0 +1,202 @@
+# include "gophot.h"
+
+float chisq (function, ix, iy, z, dz, npts, pars, dpars, npars, Acc, Parlim, niter) 
+     float (function)(int, int, float *, float *);
+     int *ix;
+     int *iy;
+     float *z, *dz;
+     float *pars, *dpars, *Acc, *Parlim;
+     int npts, npars, niter;
+{
+
+  float **covmatr, **tmpmatr;
+  float *v, *tmpvec, *fpars;
+  int *indx;
+  bool conv, marq, limit, islimit;
+  int i, j, k, jj, kk;
+  float ifact, fact, chinew, chiold, f, dz1, fakk, d, tmpval, value;
+  float perdeg, save;
+
+  if (npars >= NPMAX) return (MAGIC);
+
+  ALLOCATE (v, float, npars);
+  ALLOCATE (indx, int, npars);
+  ALLOCATE (fpars, float, NPMAX);
+  ALLOCATE (tmpvec, float, npars);
+  ALLOCATE (covmatr, float *, npars);
+  ALLOCATE (tmpmatr, float *, npars);
+  for (i = 0; i < npars; i++) {
+    ALLOCATE (covmatr[i], float, npars);
+    ALLOCATE (tmpmatr[i], float, npars + 1);
+  }
+
+  conv = FALSE;
+  limit = FALSE;
+
+  /* check if parameters exceeds limits at the start. */
+  for (j = 0; j < npars; j++) {
+    if (Parlim[j] < 0.0) {
+      limit = fabs(pars[j]) > fabs(Parlim[j]);
+      if (limit) mprint (4, "self-deception has occured: initial limits\n");
+    }
+  }
+
+  /*
+  for (j = 0; j < npars; j++) {
+    mprint (3, "%d %f %f\n", j, pars[j], fpars[j]);
+  }
+  */
+
+  ifact = 0;
+
+  for (i = 0; (i < niter) && !conv && !limit; i++) {
+    chinew = 0.0;
+    for (j = 0; j < npars; j++) {
+      for (kk = 0; kk < npars + 1; kk++) {
+	tmpmatr[j][kk] = 0;
+      }
+    }
+
+    for (j = 0; j < npts; j++) {
+      f = function (ix[j], iy[j], pars, fpars) - z[j];
+      /* fprintf (stderr, "%d  %d %d  %f %f %f\n", j, ix[j], iy[j], f, z[j], dz[j]); */
+      dz1 = 1.0 / dz[j];
+      chinew += SQ(f)*dz1;
+      for (kk = 0; kk < npars; kk++) {
+	if (fabs(fpars[kk]) > 1e-12) {
+	  fakk = fpars[kk]*dz1;
+	  tmpmatr[kk][npars] += fakk*f;
+	  for (jj = 0; jj <= kk; jj++) {
+	    if (fabs(fpars[kk]) > 1e-12) tmpmatr[kk][jj] += fakk*fpars[jj];	
+	  }
+	}
+      }
+    } 
+
+    chiold = chinew;
+    marq = FALSE;
+    for (k = 1; (k <= 10) && !marq && !limit; k++) {
+      conv = (k == 1);
+      fact = (k == 1) ? 0.0 : pow (2.0, ifact);
+      for (j = 0; j < npars; j++) {
+	for (jj = 0; jj < j; jj++) {
+	  covmatr[j][jj] = tmpmatr[j][jj];
+	  covmatr[jj][j] = tmpmatr[j][jj];
+	}
+	covmatr[j][j] = (1+fact)*tmpmatr[j][j];
+	v[j] = tmpmatr[j][npars];
+      }
+      ludcmp (covmatr, npars, indx, &d);
+
+      /* if d = 0, the matrix was singular; no convergence. */
+      if (d == 0) {
+	mprint (4, "singular matrix!\n");
+	/* need to free arrays */
+	return (MAGIC);
+      }
+      lubksb (covmatr, npars, indx, v);
+
+      /* 
+	 check if change in parameters exceeds limits.  if Parlim(j) > 0, then
+	 consider fractional changes.  if Parlim(j) < 0, consider absolute
+	 changes.  if Parlim(j) = 0, ignore this test.
+      */
+      for (j = 0; j < npars; j++) {
+	pars[j] -= chipar*v[j];
+	if (Parlim[j] > 0.0) {
+	  tmpval = fabs (v[j]/pars[j]);
+	  islimit = (tmpval > Parlim[j]);
+	  limit = limit || islimit;
+	  if (islimit) mprint (4, "self-deception has occured: frac limits: %d  %f %f %f\n", j, pars[j], v[j], Parlim[j]);
+	}
+	if (Parlim[j] < 0.0) {
+	  islimit = (fabs (pars[j]) > fabs (Parlim[j]));
+	  limit = limit || islimit;
+	  if (islimit) mprint (4, "self-deception has occured: abs limits: %d  %f %f %f\n", j, pars[j], v[j], Parlim[j]);
+	}
+	/* check convergence */
+	if (Acc[j] > 0) {
+	  tmpval = fabs (v[j]/pars[j]);
+	  conv = conv && (tmpval <= Acc[j]);
+	} else {
+	  conv = conv && (fabs (v[j]) <= fabs (Acc[j]));
+	}
+      }
+
+      if (conv) {
+	marq = TRUE;
+      } else {
+	if (!limit) {
+	  chinew = 0.0;
+	  for (j = 0; j < npts; j++) {
+	    f = function(ix[j], iy[j], pars, (float *) NULL) - z[j];
+	    chinew += SQ(f)/dz[j];
+	  }
+	  if (k == 2) ifact --;
+	  if (chinew < 1.0001*chiold) {
+	    marq = TRUE;
+	  } else {
+	    if (k == 2) ifact += 2;
+	    if (k > 2)  ifact ++;
+	    if (ifact > 10) goto escape;
+	    for (j = 0; j < npars; j++) pars[j] += chipar*v[j];
+	  }
+	}
+      }
+
+      mprint (4, "%d, %d, ", i, k);
+      for (kk = 0; kk < npars; kk++) mprint (4, "%f ", pars[kk]);
+      mprint (4, "  %f\n", chinew);
+
+    }
+  }
+  escape:
+      
+  if (!limit) {
+    for (j = 0; j < npars; j++) {
+      for (i = 0; i < npars; i++) tmpvec[i] = 0;
+      tmpvec[j] = 1;
+      lubksb (covmatr, npars, indx, tmpvec);
+      for (i = 0; i < npars; i++) tmpmatr[i][j] = tmpvec[i];
+    }
+  }
+      
+  if (conv && !limit) {
+    perdeg = sqrt (chinew / MAX (npts - npars, 1));
+    for (i = 0; i < npars; i++) {
+      if (tmpmatr[i][i] > 0) {
+	save = sqrt (tmpmatr[i][i]);
+      } else {
+	mprint (4, "trouble: negative autovariance for tmpmatr[%d][%d] = %f\n", i, i, tmpmatr[i][i]);
+	save = 1e10;
+      }
+      for (j = 0; j < npars; j++) {
+	covmatr[i][j] = tmpmatr[i][j]/save;
+	covmatr[j][i] = tmpmatr[j][i]/save;
+      }
+      covmatr[i][i] = save*perdeg;
+    }
+    value = chiold;
+  } else {
+    value = MAGIC;
+  }
+  
+  for (i = 0; i < npars; i++) dpars[i] = SQ(covmatr[i][i]);
+
+  for (kk = 0; kk < npars; kk++) mprint (4, "%f ", pars[kk]);
+  mprint (4, "     %f\n", chiold);
+  
+  for (i = 0; i < npars; i++) {
+    free (covmatr[i]);
+    free (tmpmatr[i]);
+  }
+  free (v);
+  free (indx);
+  free (fpars);
+  free (tmpvec);
+  free (covmatr);
+  free (tmpmatr);
+
+  return (value);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,265 @@
+# include "gophot.h"
+
+# define TestConfig(A,B,C,D,E) { if (!ScanConfig (A,B,C,D,E)) { fprintf (stderr, B); exit (1); }}
+
+int ConfigInit (int *argc, char **argv) {
+
+  char *config, *file;
+  float fwhm, ar, tilt;
+  char line[128];
+  int level;
+  float gmajwid, gxwid, gywid, fwhmx, fwhmy;
+  float scalefb, fbmin, scaleab, abmin, scalemb, ambmin;
+
+  /*** load configuration info ***/
+  file = argv[3];
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    exit (0);
+  }
+
+  set_verbosity (0);  /* initializes function data */
+
+  /* flags -- OK for now, not really used much */
+  flags[1] =  PGAUSS;  /* psf type */
+  flags[2] =  NONE2;   /* sky type */
+  flags[3] =  NONE3;   /* objtype_out */
+  flags[4] =  FALSE;   /* output shadow file? */
+  flags[5] =  FALSE;   /* image out? */
+  flags[6] =  FALSE;   /* warmstart? */
+  flags[7] =  FALSE;   /* load input shadow file? */
+  flags[8] =  FALSE;   /* objtype_in */
+  flags[10] = FALSE;   /* aperture correction file? */
+
+  onestar = pseud2d;
+  twostar = pseud4d;
+  skyfun  = skyfun_plane;
+
+  /* files:
+     files[2] - image_out 
+     files[3] - objects_in      - UNUSED
+     files[5] - shadowfile_out  - UNUSED
+     files[7] - shadowfile_in   - UNUSED
+     files[6] - logfile         - UNUSED
+     files[8] - apcorrfile      - UNUSED
+  */
+  
+  /** legacy - delete? */
+  n0left = 0;
+  n0right = 0;
+  nthpix = 0;
+
+  TestConfig (config, "FWHM",                   "%f", 0,    &fwhm);               /* Approx FWHM of objects (pixels) along major axis. */
+  TestConfig (config, "AXIS_RATIO",             "%f", 0,    &ar);                 /* For star objects.  AR=b/a; b=minor axis. */
+  TestConfig (config, "TILT",                   "%f", 0,    &tilt);               /* Angle of major axis in degrees; +x=0; +y=90. */
+  TestConfig (config, "SKY",                    "%f", 0,    &skyguess);           /* Approximate mean sky value in data numbers. */
+
+  /* convert to internal values */
+  fwhm *= 1.2; /* is this really necessary? */
+  tilt = tilt/57.29578;
+  gmajwid = SQ(fwhm/2.3548);
+  gxwid = gmajwid*(SQ(cos(tilt)) + SQ(ar*sin(tilt)));
+  gywid = gmajwid*(SQ(ar*cos(tilt)) + SQ(sin(tilt)));
+  fwhmx = 2.3548*sqrt(gxwid);
+  fwhmy = 2.3548*sqrt(gywid);
+  mprint (1, "fwhm x & y: %f, %f\n", fwhmx, fwhmy);
+
+  /* no longer use ava[0-3] */
+  ava[4] = gxwid;
+  ava[6] = gywid;
+  ava[5] = 0.01/sqrt(gxwid*gywid);
+  
+  TestConfig (config, "NFITBOX_X",              "%d", 0,    &irect[1]);           /* Size of fit box in the x-direction. */
+  TestConfig (config, "NFITBOX_Y",              "%d", 0,    &irect[2]);           /* Size of fit box in the y-direction. */
+  TestConfig (config, "MASKBOX_X",              "%d", 0,    &ixby2);              /* Size of mask box size in x. */
+  TestConfig (config, "MASKBOX_Y",              "%d", 0,    &iyby2);              /* Size of mask box size in y. */
+  TestConfig (config, "APBOX_X",                "%f", 0,    &arect[1]);           /* Size of aperture photometry box in x. */
+  TestConfig (config, "APBOX_Y",                "%f", 0,    &arect[2]);           /* Size of aperture photometry box in y. */
+  TestConfig (config, "NGALBOX_X",              "%d", 0,    &grect[1]);           /* Size of fit box in the x-direction. */
+  TestConfig (config, "NGALBOX_Y",              "%d", 0,    &grect[2]);           /* Size of fit box in the y-direction. */
+
+  TestConfig (config, "IBOTTOM",                "%d", 0,    &ibot);               /* Lowest allowed data value in data numbers. */
+  TestConfig (config, "ITOP",                   "%d", 0,    &itop);               /* Level where Saturation begins. */
+  TestConfig (config, "THRESHMIN",              "%f", 0,    &tmin);               /* Sigmas above sky for min threshold */
+  TestConfig (config, "THRESHMAX",              "%f", 0,    &tmax);               /* Value of maximum threshold. */
+  TestConfig (config, "THRESHDEC",              "%f", 0,    &tfac);               /* Threshold decrement in powers-of-2. */
+  TestConfig (config, "EPERDN",                 "%f", 0,    &eperdn);             /* Electrons per data number. */
+  TestConfig (config, "RDNOISE",                "%f", 0,    &rnoise);             /* Readout noise in electrons. */
+  rnoise = SQ (rnoise);  /* we will store SQ(rnoise) since this is always needed */
+
+  /* I don't like AUTOSCALE because it is not dynamic - 
+     it sets the values once here for the run, so FWHM better be right */
+  TestConfig (config, "AUTOSCALE",              "%s", 0,    line);                /* Auto-scaling of sizes by FWHM. */
+  if (!strncasecmp (line, "y", 1)) {
+    ScanConfig (config, "SCALEFITBOX",          "%f", 0,    &scalefb);          /* Size of fit box in units of FWHM. */
+    ScanConfig (config, "FITBOXMIN",            "%f", 0,    &fbmin);            /* Smallest allowed fit box size. */
+    ScanConfig (config, "SCALEAPBOX",           "%f", 0,    &scaleab);          /* Size of aperture phot box in units of FWHM. */
+    ScanConfig (config, "APBOXMIN",             "%f", 0,    &abmin);            /* Smallest allowed aperture phot box size. */
+    ScanConfig (config, "SCALEMASKBOX",         "%f", 0,    &scalemb);          /* Size of mask box in units of FWHM. */
+    ScanConfig (config, "AMASKBOXMIN",          "%f", 0,    &ambmin);           /* Smallest allowed mask box size. */
+    irect[1] = MAX (fwhmx*scalefb, fbmin);
+    irect[2] = MAX (fwhmy*scalefb, fbmin);
+    arect[1] = MAX (fwhmx*scaleab, abmin);
+    arect[2] = MAX (fwhmy*scaleab, abmin);
+    ixby2 = MAX(fwhmx*scalemb, ambmin);
+    ixby2 = MAX(fwhmy*scalemb, ambmin);
+  }
+  /* force boxes to have odd sizes */
+  if (((int)arect[1]) % 2 == 0) arect[1]++;
+  if (((int)arect[2]) % 2 == 0) arect[2]++;
+  if (irect[1] % 2 == 0) irect[1]++;
+  if (irect[2] % 2 == 0) irect[2]++;
+  if (ixby2 % 2 == 0) ixby2 ++;
+  if (iyby2 % 2 == 0) iyby2 ++;
+  ixby2 = (ixby2 - 1)/2;
+  iyby2 = (iyby2 - 1)/2;
+
+  fixpos = FALSE;
+  TestConfig (config, "FIXPOS", "%s", 0, line);                 /* Fix star positions? */
+  if (!strncasecmp (line, "y", 1)) fixpos = TRUE;
+
+  if (ScanConfig (config, "IMAGE_OUT", "%s", 0, files[2])) flags[5] = TRUE;    /* Output image name. */
+
+  TestConfig (config, "OBJTYPE_OUT", "%s", 0, &line);                /* Output format: (COMPLETE, INCOMPLETE, INTERNAL) */
+  if (!strcasecmp (line, "complete"))   flags[3] = COMPLETE;
+  if (!strcasecmp (line, "incomplete")) flags[3] = INCOMPLETE;
+  if (!strcasecmp (line, "internal"))   flags[3] = INTERNAL;
+  if (!strcasecmp (line, "oldstyle"))   flags[3] = OLDSTYLE;
+  if (flags[3] == NONE3) {
+    fprintf (stderr, "invalid OBJTYPE_OUT: %s\n", line);
+    exit (1);
+  }
+  
+  TestConfig (config, "LOGVERBOSITY",           "%d", 0,    &level);               /* Verbosity of log file; (0-4). */
+  set_verbosity (level);
+  TestConfig (config, "RESIDNOISE",             "%f", 0,    &fac);                 /* Fraction of noise to ADD to noise file. */
+  TestConfig (config, "FOOTPRINT_NOISE",        "%f", 0,    &xpnd);                /* Expand stars in noise file by this amount. */
+  TestConfig (config, "NPHSUB",                 "%f", 0,    &nphsub);              /* Limiting surface brightness for subtractions. */
+  TestConfig (config, "NPHOB",                  "%f", 0,    &nphob);               /* Limiting surface brightness for obliterations. */
+  TestConfig (config, "ICRIT",                  "%d", 0,    &icrit);               /* Obliterate if # of pixels > ITOP exceeds this. */
+  TestConfig (config, "CENTINTMAX",             "%f", 0,    &cmax);                /* Obliterate if central intensity exceeds this. */
+  TestConfig (config, "CTPERSAT",               "%f", 0,    &ctpersat);            /* Assumed intensity for saturated pixels. */
+
+  TestConfig (config, "STARGALKNOB",            "%f", 0,    &stograt);             /* Star/galaxy discriminator: bigger number, more stars */
+  TestConfig (config, "STARCOSKNOB",            "%f", 0,    &discrim);             /* Object/cosmic-ray discriminator: bigger number, more cosmics */
+  TestConfig (config, "SNLIM7",                 "%f", 0,    &crit7);               /* Minimum S/N for 7-parameter fit. */
+  crit7 = SQ(crit7);
+  TestConfig (config, "SNLIM",                  "%f", 0,    &snlim);               /* Minimum S/N for a pixel to be in fit subraster. */
+  TestConfig (config, "SNLIMMASK",              "%f", 0,    &bumpcrit);            /* Minimum S/N through mask to identify an object. */
+  TestConfig (config, "SNLIMCOS",               "%f", 0,    &sn2cos);              /* Minimum S/N to be called a cosmic ray. */
+  sn2cos = SQ(sn2cos);
+  TestConfig (config, "NBADLEFT",               "%d", 0,    &nbadleft);            /* Ignore pixels closer to the left edge than this. */
+  TestConfig (config, "NBADRIGHT",              "%d", 0,    &nbadright);           /* Ignore pixels closer to the right edge than this. */
+  TestConfig (config, "NBADTOP",                "%d", 0,    &nbadtop);             /* Ignore pixels closer to the top edge than this. */
+  TestConfig (config, "NBADBOT",                "%d", 0,    &nbadbot);             /* Ignore pixels closer to the bottom edge than this. */
+
+  TestConfig (config, "SKYTYPE",                "%s", 0,    line);                 /* SKY type: (PLANE, HUBBLE, MEDIAN) */
+  if (!strcasecmp (line, "plane")) flags[2] =  PLANE;
+  /* if (!strcasecmp (line, "hubble")) flags[2] = HUBBLE; */
+  /* if (!strcasecmp (line, "median")) flags[2] = MEDIAN; */
+  if (flags[2] == NONE2) {
+    fprintf (stderr, "invalid SKYTYPE: %s\n", line);
+    exit (1);
+  }
+
+  TestConfig (config, "NFITITER",               "%d", 0,    &nit);                 /* Maximum number of iterations. */
+  TestConfig (config, "NFITBOXFIRST_X",         "%d", 0,    &krect[1]);            /* Size of fit box in x for first pass. */
+  TestConfig (config, "NFITBOXFIRST_Y",         "%d", 0,    &krect[2]);            /* Size of fit box in y for first pass. */
+  TestConfig (config, "CHI2MINBIG",             "%f", 0,    &chicrit);             /* Critical CHI-squared for a large object. */
+  TestConfig (config, "XTRA",                   "%f", 0,    &xtra);                /* We need more S/N if some pixels are missing. */
+  TestConfig (config, "SIGMA1",                 "%f", 0,    &sig[1]);              /* Max. frac. scatter in sigma_x for stars. */
+  TestConfig (config, "SIGMA2",                 "%f", 0,    &sig[2]);              /* Max. scatter in xy cross term for stars. */
+  TestConfig (config, "SIGMA3",                 "%f", 0,    &sig[3]);              /* Max. frac. scatter in sigma_y for stars. */
+  TestConfig (config, "ENUFF4",                 "%f", 0,    &enuff4);              /* Fraction of pixels needed for 4-param fit. */
+  TestConfig (config, "ENUFF7",                 "%f", 0,    &enuff7);              /* Fraction of pixels needed for 7-param fit. */
+  TestConfig (config, "COSOBLSIZE",             "%f", 0,    &widobl);              /* Size of obliteration box for a cosmic ray. */
+  TestConfig (config, "APMAG_MAXERR",           "%f", 0,    &apmagmaxerr);         /* Max anticipated error for aperture phot report. */
+  TestConfig (config, "PIXTHRESH",              "%f", 0,    &pixthresh);           /* Trigger on pixels higher than noise*PIXTHRESH. */
+  TestConfig (config, "BETA4",                  "%f", 0,    &beta4);               /* R**4 coefficient modifier. */
+  TestConfig (config, "BETA6",                  "%f", 0,    &beta64);              /* R**6 coefficient modifier. */
+  beta4 = 1.0;
+  beta64 = 1.0;
+
+  TestConfig (config, "RELACC1",                "%f", 0,    &acc[0]);              /* Convergence criterion for sky. */
+  TestConfig (config, "RELACC2",                "%f", 0,    &acc[1]);              /* Convergence criterion for for central intensity. */
+  TestConfig (config, "RELACC3",                "%f", 0,    &acc[2]);              /* Convergence criterion for x-position. */
+  TestConfig (config, "RELACC4",                "%f", 0,    &acc[3]);              /* Convergence criterion for y-position. */
+  TestConfig (config, "RELACC5",                "%f", 0,    &acc[4]);              /* Convergence criterion for sigma-x. */
+  TestConfig (config, "RELACC6",                "%f", 0,    &acc[5]);              /* Convergence criterion for sigma-xy. */
+  TestConfig (config, "RELACC7",                "%f", 0,    &acc[6]);              /* Convergence criterion for sigma-y. */
+  TestConfig (config, "PARLIM1",                "%f", 0,    &parlim[0]);           /* Allowed change for sky value. */
+  TestConfig (config, "PARLIM2",                "%f", 0,    &parlim[1]);           /* Allowed change for central intensity. */
+  TestConfig (config, "PARLIM3",                "%f", 0,    &parlim[2]);           /* Allowed change for x-position. */
+  TestConfig (config, "PARLIM4",                "%f", 0,    &parlim[3]);           /* Allowed change for y-position. */
+  TestConfig (config, "PARLIM5",                "%f", 0,    &parlim[4]);           /* Allowed change for sigma-x. */
+  TestConfig (config, "PARLIM6",                "%f", 0,    &parlim[5]);           /* Allowed change for sigma-xy. */
+  TestConfig (config, "PARLIM7",                "%f", 0,    &parlim[6]);           /* Allowed change for sigma-y. */
+
+  /* other initial values for parameters */
+  needit = TRUE;
+  fixxy = FALSE;
+  test7 = FALSE;
+  ufactor = 100;
+  chipar = 0.9;
+
+  free (config);
+  /* free (file); */
+
+  return 1;
+}
+
+
+# if (0) /* things used by MEDIAN sky, disabled */
+  /* only if 'median' */
+  ScanConfig (config, "JHXWID",                 "%f", 0,    &jhxwid);                 /* X Half-size of median box (.le. 0 -> autoscale) */
+  ScanConfig (config, "JHYWID",                 "%f", 0,    &jhywid);                 /* Y (same as above) */
+  ScanConfig (config, "MPREC",                  "%f", 0,    &mprec);                 /* Median precision in DN (use .le. 0 for autocalc) */
+  ScanConfig (config, "NTHPIX",                 "%f", 0,    &nthpix);                 /* Frequency of sky updates in pixels for 1st pass */
+
+  /* log is now going to stderr, up to user to redirect */
+  ScanConfig (config, "LOGFILE",                "%s", 0,    &A);                 /* Log file name.  TERM for screen. */
+  ScanConfig (config, "IMAGE_IN",               "%s", 0,    &A);                 /* Input image name.  */
+  ScanConfig (config, "OBJECTS_OUT",            "%s", 0,    &A);                 /* Output object list file name. */
+  ScanConfig (config, "PARAMS_DEFAULT",         "%s", 0,    &A);                 /* Default parameters file name. */
+  ScanConfig (config, "PARAMS_OUT",             "%s", 0,    &A);                 /* Output parameters file name. */
+  ScanConfig (config, "SHADOWFILE_OUT",         "%s", 0,    &A);                 /* Output shadow file name. */
+  ScanConfig (config, "SHADOWFILE_IN",          "%s", 0,    &A);                 /* Input shadow file name. */
+  
+  ScanConfig (config, "ABSLIM1",                "%f", 0,    &A);                 /* Allowed range for sky value. */
+  ScanConfig (config, "ABSLIM2",                "%f", 0,    &A);                 /* Allowed range for central intensity. */
+  ScanConfig (config, "ABSLIM3",                "%f", 0,    &A);                 /* Allowed range for x-position. */
+  ScanConfig (config, "ABSLIM4",                "%f", 0,    &A);                 /* Allowed range for y-position. */
+  ScanConfig (config, "ABSLIM5",                "%f", 0,    &A);                 /* Allowed range for sigma-x. */
+  ScanConfig (config, "ABSLIM6",                "%f", 0,    &A);                 /* Allowed range for sigma-xy. */
+  ScanConfig (config, "ABSLIM7",                "%f", 0,    &A);                 /* Allowed range for sigma-y. */
+  ScanConfig (config, "ABSLIM8",                "%f", 0,    &A);                 /* Allowed range for sigma-y. */
+  
+  ScanConfig (config, "NPARAM",                 "%f", 0,    &A);                 /* Maximum number of PSF fit parameters. */
+  ScanConfig (config, "NFITMAG",                "%f", 0,    &A);                 /* No. of PSF parameters to get magnitudes. */
+  ScanConfig (config, "NFITSHAPE",              "%f", 0,    &A);                 /* No. of PSF parameters to get shape and mags. */
+  
+  ScanConfig (config, "MAXSTARS",               "%f", 0,    &A);                 /* Ignore pixels closer to the bottom edge than this. */
+  ScanConfig (config, "PSFTYPE",                "%s", 0,    &A);                 /* PSF type: (PGAUSS) */
+# endif
+
+# if (0) /* autothresh eliminated: we are using threshmin = Nsigma above sky always */
+  Autothresh = FALSE;
+  ScanConfig (config, "AUTOTHRESH",             "%s", 0,    line);                 /* Auto-scaling of thresholds. */
+  if (!strncasecmp (line, "y")) Autothresh = TRUE;
+  /* only if 'autothresh' */
+  ScanConfig (config, "SIGMAIBOTTOM",           "%f", 0,    &sigbot);                 /* Level of IBOTTOM below sky in units of noise. */
+  ScanConfig (config, "SIGMATHRESHMIN",         "%f", 0,    &sigthresh);                 /* Level of THRESHMIN above sky in units of noise. */
+# endif
+
+# if (0) /* warmstart code */  
+  status = ScanConfig (config, "OBJECTS_IN",             "%s", 0,    &A);                 /* Input object list file name. */
+  if (status) {
+    flags[6] = TRUE;
+    ScanConfig (config, "OBJTYPE_IN",             "%s", 0,    &A);                 /* Input format: (COMPLETE, INTERNAL) */
+  }
+# endif
+
+# if (0) /* aperture correction file - there are several other parameters, see tuneup.f */ 
+    ScanConfig (config, "APCORRFILE",             "%s", 0,    &A);                 /* Aperture correction file name. */
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/addlims.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/addlims.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/addlims.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "gophot.h"
+
+addlims (float *star, int *jrect) {
+
+  float temp, fudgex, fudgey;
+	
+  if (star[1] > 0) {
+    if (beta4 < 0.1) {
+      temp = star[1]/nphsub - 1.0;
+    } else {
+      temp = pow (6*star[1]/nphsub, 0.33333);
+    }
+    if (star[4] > 0) {
+      fudgex = sqrt(temp*star[4]*2);
+    } else {
+      fudgex = 1.5*irect[1]/2;
+    }
+    if (star[6] > 0) {
+      fudgey = sqrt(temp*star[6]*2);
+    } else {
+      fudgey = 1.5*irect[2]/2;
+    }
+  } else {
+    fudgex = 1.5*irect[1]/2;
+    fudgey = 1.5*irect[2]/2;
+  }
+  /* don't extrapolate beyond 2*fit box */
+  fudgex = MIN (irect[1], fudgex);
+  fudgey = MIN (irect[2], fudgex);
+  jrect[1] = star[2] - fudgex;
+  jrect[2] = star[2] + fudgex;
+  jrect[3] = star[3] - fudgey;
+  jrect[4] = star[3] + fudgey;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/addstar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/addstar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/addstar.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "gophot.h"
+
+addstar (float *instar, int iadd, int type) {
+
+  float star0[NPMAX], star1[NPMAX], *bigval, *noiseval;
+  float sky, bfactor, bsky, cfactor, csky, val;
+  int ix, iy, jrect[5];
+  int i, j, ihi, ilo, jhi, jlo, ixin, iyin;
+	
+  if (type == 12) beta4 = 0.01;
+
+  needit = FALSE;
+	
+  sky = guess2 (star0, instar, &ixin, &iyin);
+  sky = guess2 (star1, instar, &ixin, &iyin) / ufactor;
+	
+  addlims (instar, jrect);
+  star1[4] = star0[4]*SQ(xpnd);
+  star1[5] = star0[5]/SQ(xpnd);			
+  star1[6] = star0[6]*SQ(xpnd);
+	
+  bfactor = ufactor*iadd;
+  bsky = iadd*(0.5 - sky);  /* why the 0.5? */
+  cfactor = fac*ufactor;
+  csky = 0.5 - fac*sky;
+
+  ilo = MAX (jrect[1], 0);
+  ihi = MIN (jrect[2], nfast-1);
+  jlo = MAX (jrect[3], 0);
+  jhi = MIN (jrect[4], nslow-1);
+	
+  for (j = jlo; j <= jhi; j++) {
+    iy = j - iyin;
+    bigval = &big[ilo + j*nfast];
+    noiseval = &noise[ilo + j*nfast];
+    for (i = ilo; i <= ihi; i++, bigval++, noiseval++) {
+      if (!finite (*noiseval)) continue;
+      ix = i - ixin;
+      /* shouldn't bfactor multiply both onestar and bsky? */
+      val = ufactor*(onestar (ix, iy, star0, (float *) NULL) - sky);
+      *bigval += iadd*val;
+
+      val = fac*ufactor*fabs(onestar (ix, iy, star1, (float *) NULL) - sky);
+      *noiseval -= iadd*val;
+
+      if (*noiseval <= -1000) {
+	mprint (2, "i,j,noise = %d, %d, %f, %f %f %f %d, negative noise! obliterating\n", i, j, *noiseval, val, *bigval, cfactor, iadd);
+	/*
+	*bigval = MAGIC;
+	*noiseval = MAGIC;
+	*/
+      }
+    }
+  }
+  needit = TRUE;
+  beta4 = 1.0;
+}
+
+/* star0 is the nominial star region, star1 is the expanded are for the noise array */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/cosmic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/cosmic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/cosmic.c	(revision 22322)
@@ -0,0 +1,84 @@
+# include "gophot.h"
+
+bool cosmic (float *star) {
+
+  float dummy[NPMAX];
+  float obs, maxob, sky, chistar, chicos, pred, temp;
+  float sn2, tnoise, *Bval, *Nval;
+  int ix, iy, I, J, ii, jj, npix, imax, jmax;
+  bool pointy;
+
+  pointy = FALSE;
+
+  sky = guess2 (dummy, star, &ix, &iy);
+  temp = big[ix + iy*nfast] / ufactor - dummy[0];
+  if (finite (temp)) dummy[1] = temp;
+  maxob = -HUGE_VAL;
+  chistar = 0;
+  chicos  = 0;
+  npix = 0;
+  
+  for (J = -1; J <= 1; J++) {
+    jj = iy + J;
+    if (jj < 0) continue;
+    if (jj > nslow - 1) continue;
+    
+    Bval = &big[ix - 1 + jj*nfast];
+    Nval = &noise[ix - 1 + jj*nfast];
+    
+    for (I = -1; I <= 1; I++, Bval++, Nval++) {
+      ii = ix + I;
+      if (ii < 0) continue;
+      if (ii > nfast - 1) continue;
+      if (!finite (*Nval)) continue;
+      npix ++;
+      pred = ufactor*onestar (I, J, dummy, (float *) NULL);
+      obs = *Bval;
+      temp = 1.0 / (*Nval + *Bval);
+      chistar += SQ (obs - pred) * temp;
+      sn2 = SQ (obs - sky) * temp;
+      chicos += sn2;
+      if ((obs > maxob) && (sn2 >= sn2cos)) {
+	imax = ii;
+	jmax = jj;
+	maxob = obs;
+	tnoise = temp;
+      }
+    }
+  }
+
+  /* this is meant to test if the object is mostly a single pixel event.
+     doesn't do a good job of distinguishing a single pixel event from a
+     generally poor fit to the top of the star. */
+  if ((npix >= 7) && (maxob > -HUGE_VAL)) {
+    chicos -= SQ (maxob - sky) * tnoise;
+    pointy = (chicos/chistar < discrim);
+    mprint (3, "location %d, %d,  chi-star & chi-cosmic = %f, %f\n", ix, iy, chistar, chicos);
+  }
+
+  if (pointy) {
+    mprint (2, "cosmic ray intensity, x, y: %f %d %d\n", maxob, imax, jmax);
+    star[0] = sky;
+    star[1] = maxob;
+    star[2] = imax;
+    star[3] = jmax;
+    star[4] = widobl;
+    star[5] = -1;
+    star[6] = widobl;
+  }
+  return (pointy);
+}
+
+/* We are basically comparing if the object is more like a stellar
+   object, or if it is compatible with a single high point over a
+   general sky-like region.  
+
+   we have a danger here that a wide object will boost Io
+   significantly, which in turn makes the fit to the object
+   particularly poor in the center areas.  This in turn makes the
+   object seem comparable to the sky, and therefore a cosmic.  Since
+   we really only care if the inner 9 pixels are comparable to the
+   star shape, vaguely, we can fudge this by temporarily setting Io to
+   the central pixel value.  We don't bother if that pixel is bad (NaN).
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/delete_ellipse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/delete_ellipse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/delete_ellipse.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "gophot.h"
+
+float delete_ellipse (float *par, float sky) {
+
+  int i, j, npix;
+  float theta, phi;
+  float xp, yp, x, y, dx, dy, Dx, Dy;
+  float Chi, dv, Dv, R2, F2, r1, r2;
+  float flux, mean;
+
+  npix = flux = 0;
+
+  dx = MAX (par[2], par[3]) + 2;
+  dy = MAX (par[2], par[3]) + 2;
+
+  for (j = par[1] - dy; j < par[1] + dy; j++) {
+    if (j < 0) continue;
+    if (j >= nslow) continue;
+    for (i = par[0] - dx; i < par[0] + dx; i++) {
+      if (i < 0) continue;
+      if (i >= nfast) continue;
+    
+      Dx = i - par[0];
+      Dy = j - par[1];
+      phi = atan2 (Dy, Dx) - RAD_DEG * par[4];
+      theta = atan2 (par[2]*sin(phi), par[3]*cos(phi));
+      
+      /* this is the point on the ellipse at the same angle as ref point */
+      xp = par[2] * cos (theta);
+      yp = par[3] * sin (theta);
+    
+      x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG);
+      y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG);
+      
+      r1 = hypot (Dx, Dy);
+      r2 = hypot (x, y);
+      
+      if (r1 < r2) {
+	flux += big[i + nfast*j] - sky;
+	npix ++;
+      }
+
+    }
+  }
+  mean = flux / npix;
+  fprintf (stderr, "flux: %f, mag: %f, mean: %f\n", flux, -2.5*log10(flux), flux / npix);
+
+  for (j = par[1] - dy; j < par[1] + dy; j++) {
+    if (j < 0) continue;
+    if (j >= nslow) continue;
+    for (i = par[0] - dx; i < par[0] + dx; i++) {
+      if (i < 0) continue;
+      if (i >= nfast) continue;
+    
+      Dx = i - par[0];
+      Dy = j - par[1];
+      phi = atan2 (Dy, Dx) - RAD_DEG * par[4];
+      theta = atan2 (par[2]*sin(phi), par[3]*cos(phi));
+      
+      /* this is the point on the ellipse at the same angle as ref point */
+      xp = par[2] * cos (theta);
+      yp = par[3] * sin (theta);
+    
+      x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG);
+      y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG);
+      
+      r1 = hypot (Dx, Dy);
+      r2 = hypot (x, y);
+      
+      if (r1 < r2) {
+	big[i + nfast*j] -= mean;
+      }
+      if (r1 < 1.2*r2) {
+	noise[i + nfast*j] += fac*(mean + sky);
+      }
+
+    }
+  }
+  fprintf (stderr, "flux: %f, mag: %f, mean: %f\n", flux, -2.5*log10(flux), flux / npix);
+  return (flux);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/dophot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/dophot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/dophot.c	(revision 22322)
@@ -0,0 +1,94 @@
+# include "gophot.h"
+float set_thresholds (float, float, float *, int *);
+void large_features (float, float);
+int fix_mediansky  (float);
+float get_mediansky (int, int);
+
+int dophot (void) {
+
+  bool first, lastround;
+  float factor, sky, dsky;
+  int nstar, Nit, i, j;
+  char c;
+  int xtest, ytest;
+  struct timeval now, then;  
+  
+  makenoise ();
+  make_mediansky ();
+  get_skystats (&sky, &dsky);
+  fix_mediansky (sky);
+
+  /*
+  gettimeofday (&then, (void *) NULL);
+  for (i = 0; i < nfast; i++) {
+    for (j = 0; j < nslow; j++) {
+      sky = get_mediansky (i, j);
+      big[j*nfast + i] -= sky;
+    }
+  }
+  gettimeofday (&now, (void *) NULL);
+  fprintf (stderr, "elapsed time = %.2f sec\n", 
+	   (now.tv_sec - then.tv_sec) + 1e-6*(now.tv_usec - then.tv_usec));
+
+  gfits_write_header ("test.sub", &header);
+  gfits_write_matrix ("test.sub", &matrix);
+  exit (0);
+  */
+
+  thresh = set_thresholds (sky, dsky, &factor, &Nit);
+
+  /* large_features (sky, dsky); */
+
+  first = TRUE;
+  lastround = FALSE;
+
+  for (i = 0; i < Nit + 1; i++) {
+    mprint (0, "starting loop at threshold level %f\n", thresh);
+	   
+    if (i == Nit) lastround = TRUE;
+
+    makemask ();
+
+    /* fix pos needs to be defined correctly */
+    if (fixpos && first) improve (FALSE);
+	   
+    nstar = isearch (first);
+    shape ();
+    paravg ();
+
+    improve (lastround);
+	   
+    mprint (1, " ending loop at threshold level %f\n", thresh); 
+    mprint (1, " number of new objects found on this threshold = %f\n", nstar); 
+    mprint (1, " total number of objects found so far = %d\n", nstot); 
+    
+    thresh /= factor;
+
+    first = FALSE;
+
+# if (0)
+    gfits_write_header ("test.sub", &header);
+    gfits_write_matrix ("test.sub", &matrix);
+
+    completeout (nstot);
+    fprintf (stderr, "type return to continue:  ");
+    fscanf (stdin, "%c", &c);
+# endif
+
+  }
+
+  feature_fluxes ();
+
+  if (lastround) {
+    if (flags[3] == COMPLETE) completeout (nstot);
+  }
+	   
+    /* 
+    gfits_write_header ("test.sub", &header);
+    gfits_write_matrix ("test.sub", &matrix);
+    completeout (nstot);
+    fprintf (stderr, "type return to continue:  ");
+    fscanf (stdin, "%c", &c);
+    */
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/ellipse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/ellipse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/ellipse.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include <math.h>
+# include <stdio.h>
+# define SQ(X)    (double) (((double)(X))*((double)(X)))
+
+int ellipse (float Sx, float Sxy, float Sy,
+	     float *area, float *amaj, float *amin, float *angle) {
+
+  float A1, A2, A3, R, root1, root2;
+
+  if (fabs(Sxy) >= 1.0 / sqrt(fabs(Sx*Sy))) {
+    *area = 0.0;
+    *amaj = 0.0;
+    *amin = 0.0;
+    *angle = 0.0;
+    /* this is a poor fit - not an ellipse but a hyperbola */
+    return (0);
+  }
+
+  A1 = 1/(2*Sx);
+  A2 = 1/(2*Sy);
+  A3 = Sxy;
+  
+  *angle = atan2(-A3, A2 - A1) / 2.0;
+  R = sqrt( SQ(A2 - A1) + SQ(A3));
+  root1 = (A1 + A2 + R);
+  root2 = (A1 + A2 - R);
+
+  *area = 2.0*M_PI/sqrt(root1*root2);
+  *amaj = 2.35482*sqrt(1.0/root2);
+  *amin = 2.35482*sqrt(1.0/root1);
+
+  return (1);
+
+}
+
+
+/* In this function, Sx & Sy represent sx^2, sy^2 
+
+   given an elliptical Gaussian of the form:
+
+   exp (-z); z = x^2 / 2 sx^2 + y^2 / 2 sy^2 + xy Sxy
+
+   Sa, Sb, and angle can be found by:
+
+   A1 = 1/(2 Sx)
+   A2 = 1/(2 Sy)
+   A3 = Sxy
+   
+   R = sqrt ((A2 - A1)^2 + A3^2)
+   root1 = (A1 + A2 + R)
+   root2 = (A1 + A2 - R)
+   
+   angle = atan2 (-A3, A2 - A1) / 2.0
+   Sa = sqrt (1.0/root2)
+   Sb = sqrt (1.0/root1)
+   
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/feature_fluxes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/feature_fluxes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/feature_fluxes.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "gophot.h"
+
+float feature_fluxes () {
+
+  int i, j, npix, n;
+  float theta, phi, cs, sn;
+  float xp, yp, x, y, dx, dy, Dx, Dy;
+  float r1, r2;
+  float flux, mean, sky;
+
+  for (n = 0; n < nregion; n++) {
+
+    npix = flux = 0;
+    
+    dx = MAX (region[n][4], region[n][5]) + 2;
+    dy = MAX (region[n][4], region[n][5]) + 2;
+    cs = cos (region[n][6] * RAD_DEG);
+    sn = sin (region[n][6] * RAD_DEG);
+    sky = region[n][0];      
+
+    for (j = region[n][3] - dy; j < region[n][3] + dy; j++) {
+      if (j < 0) continue;
+      if (j >= nslow) continue;
+      for (i = region[n][2] - dx; i < region[n][2] + dx; i++) {
+	if (i < 0) continue;
+	if (i >= nfast) continue;
+	
+	Dx = i - region[n][2];
+	Dy = j - region[n][3];
+	phi = atan2 (Dy, Dx) - RAD_DEG * region[n][6];
+	theta = atan2 (region[n][4]*sin(phi), region[n][5]*cos(phi));
+	
+	/* this is the point on the ellipse at the same angle as ref point */
+	xp = region[n][4] * cos (theta);
+	yp = region[n][5] * sin (theta);
+	
+	x = xp * cs - yp * sn;
+	y = xp * sn + yp * cs;
+	
+	r1 = hypot (Dx, Dy);
+	r2 = hypot (x, y);
+	
+	if (r1 < r2) {
+	  flux += big[i + nfast*j] - sky;
+	  npix ++;
+	}
+	
+      }
+    }
+    /* existing image has mean subtracted = flux*Npix */
+    flux += region[n][1];
+    mean = flux / npix;
+    fprintf (stderr, "sub flux: %f, mag: %f, mean: %f\n", flux, -2.5*log10(flux), flux / npix);
+    region[n][7] = flux;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/fillerup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/fillerup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/fillerup.c	(revision 22322)
@@ -0,0 +1,169 @@
+# include "gophot.h"
+
+fillerup (int xo, int yo, int findstats) {
+
+  float *bigval, *noiseval, ufactor2, snlim2;
+  int i, j, ilo, ihi, jlo, jhi, ix, iy, idist, nsky, maxnpt, outer;
+  int *x, *y;
+  float *z, *d;
+  float N, S, F, Sx, Sy, Sx2, Sy2, xv, yv;
+  float value, SNR, sky;
+
+  ufactor2 = SQ(ufactor);
+  snlim2 = SQ(snlim);
+  npt = 0;
+  nsky = 0;
+
+  ilo = MAX (xo - nrect[1]/2, 0);
+  ihi = MIN (xo + nrect[1]/2, nfast-1);
+  jlo = MAX (yo - nrect[2]/2, 0);
+  jhi = MIN (yo + nrect[2]/2, nslow-1);
+  
+  outer = MIN (nrect[1]/3, nrect[2]/3);
+  
+  for (j = jlo; j <= jhi; j++) {
+    bigval = &big[ilo + j*nfast];
+    noiseval = &noise[ilo + j*nfast];
+    for (i = ilo; i <= ihi; i++, bigval++, noiseval++) {
+      if (!finite (*bigval)) continue;
+      if (!finite (*noiseval)) continue;
+      if (SQ(*bigval) < (*bigval + *noiseval) * snlim2) continue;
+      if (*bigval < (*noiseval - rnoise)) continue;
+      ix = i - xo;
+      iy = j - yo;
+      xs[npt] = ix;
+      ys[npt] = iy;
+      zs[npt] = *bigval/ufactor;
+      dzs[npt] = (*bigval + *noiseval) / ufactor2;
+      idist = MAX (abs(ix), abs(iy));
+      if (idist > outer) {
+	ts[nsky] = zs[npt];
+	nsky ++;
+      }
+      npt ++;
+    }
+  }
+  
+  if (npt == 0) return (FALSE);
+  if (!findstats) return (TRUE);
+
+  if (nsky == 0) return (FALSE);
+
+  fsort (ts, nsky);
+  sky = ts[(int)(0.5*nsky)];
+  sum2 = sky;
+
+  z = zs;
+  d = dzs;
+  S = N = 0;
+  for (i = 0; i < npt; i++, z++, d++) {
+    if (*z < sky) continue;
+    N += *d;
+    S += *z - sky;
+  }
+
+  if (S < 0) return (FALSE);
+  SNR = S*S / N;
+  if (SNR < SQ(bumpcrit)) return (FALSE);
+
+  if (SNR > 49) {
+    x = xs;
+    y = ys;
+    z = zs;
+    d = dzs;
+    N = S = Sx = Sy = Sx2 = Sy2 = 0;
+    for (i = 0; i < npt; i++, x++, y++, z++, d++) {
+      idist = MAX (abs(*x), abs(*y));
+      if (idist > outer) continue;
+      if (*z < sky) continue;
+      value = *z - sky;
+      xv = *x * value;
+      yv = *y * value;
+      N += *d;
+      S += value;
+      Sx += xv;
+      Sx2 += *x * xv;
+      Sy += yv;
+      Sy2 += *y * yv;
+    }
+    xmax = Sx / S;
+    ymax = Sy / S;
+    xmax2 = fabs(Sx2 / S - xmax*xmax);
+    ymax2 = fabs(Sy2 / S - ymax*ymax);
+    maxval = S / sqrt (xmax2*ymax2);
+  } else {
+    z = zs;
+    maxval = *z;
+    maxnpt = 0;
+    for (i = 0; i < npt; i++, z++) {
+      if (*z > maxval) {
+	maxnpt = i;
+	maxval = *z;
+      }
+    }
+    xmax = xs[maxnpt];
+    ymax = ys[maxnpt];
+    xmax2 = ava[4];
+    ymax2 = ava[6];
+    maxval -= sum2; 
+ }
+
+  maxval *= ufactor;
+  sum2 *= ufactor;
+
+  if (!finite (maxval)) return (FALSE);
+  return (TRUE);
+  
+}
+
+/* adjust the errors by the deviation from the guess fit */
+filladjust (float *star) {
+
+  int i;
+  float f, df;
+
+  return (0);
+  if (star[1] < cmax/(ufactor*10)) return (0); 
+
+  for (i = 0; i < npt; i++) {
+
+    f = onestar ((int)xs[i], (int)ys[i], star, (float *) NULL);
+    
+    df = 10 * fabs(f - zs[i]);
+    dzs[i] += df/ufactor;
+  }
+
+}
+
+/* check if weighted center is in OK location 
+   weighted center is based on useable pixels, not
+   on entire box */
+centertest (float *star, int *x, int *y) {
+
+  int i;
+  float f, df, Sf, Sx, Sy, dx, dy;
+
+  Sf = Sx = Sy = 0;
+
+  for (i = 0; i < npt; i++) {
+
+    f = onestar ((int)xs[i], (int)ys[i], star, (float *) NULL);
+    
+    Sf += f;
+    Sx += f*xs[i];
+    Sy += f*ys[i];
+
+  }
+
+  dx = star[2] - Sx/Sf;
+  dy = star[3] - Sy/Sf;
+
+  if ((fabs(dx) > 0.25*nrect[1]) || (fabs(dy) > 0.25*nrect[2])) {
+    mprint (3, "center %f %f %f\n", dx, dy, Sf);
+    return (TRUE);
+  }
+
+  return (FALSE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/findsky.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/findsky.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/findsky.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "gophot.h"
+
+float findsky () {
+
+  int i, skip, nsky, bin;
+  int zvalue[0x10000], nvalue;
+  float median, *valB;
+  
+  skip = 100;
+  valB = big;
+
+  for (i = 0; i < nfast*nslow; i+=skip, valB+=skip) {
+    bin = *valB;
+    if ((bin >= 0) && (bin < 0x10000)) {
+      nvalue ++;
+      zvalue[bin] ++;
+    }
+  }
+  
+  nsky = 0;
+  for (i = 0; (i < 0x10000) && (nsky < nvalue / 2); i++) {
+    nsky += zvalue[i];
+  }
+  if (nsky < nvalue / 2) {
+    mprint (1, "weird situation: no median found\n");
+    exit (1);
+  }
+  median = i;
+  mprint (1, "median = %f\n", median);
+
+  return (median);
+}
+
+/* arguments OK */
+
+/* this median method assumes 64k data range */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/galaxy.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/galaxy.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/galaxy.c	(revision 22322)
@@ -0,0 +1,58 @@
+# include "gophot.h"
+
+/* this is kind of stupid:  this function takes two 
+   lists of the parameters for the object because 
+   star1 has x, y, relative to 0, */
+
+bool galaxy (float *star1, float *err, float *star2) {
+
+  float dummy[NPMAX], tot[4], temp, nsigma;
+  int i;
+  bool value;
+
+  value = FALSE;
+
+  for (i = 0; i < 4; i++) chi[i] = 0;
+	
+  parinterp (star2[2], star2[3], dummy);
+
+  /* if errors on sigma x or y are so large, call it a galaxy */
+  if (dummy[4] < 3*sqrt(err[4])) return (TRUE);
+  if (dummy[6] < 3*sqrt(err[6])) return (TRUE);
+
+  /* too weak a measurement to be called galaxy */
+  if (star1[4] < 2*sqrt(err[4])) return (FALSE);
+  if (star1[6] < 2*sqrt(err[6])) return (FALSE);
+  if (star1[1] < 2*sqrt(err[1])) return (FALSE); 
+
+  /*************************************************************
+   I'm rather concerned about 'parsm' where did it come from? 
+  parms[] is set in varipar_plane.c  */							
+  temp = SQ (sig[1]*dummy[4]);
+  tot[1] = MAX (parms[4], temp);
+  temp = SQ (sig[2])/(dummy[4]*dummy[6]);
+  tot[2] = MAX (parms[5], temp);
+  temp = SQ (sig[3]*dummy[6]);
+  tot[3] = MAX (parms[6], temp);
+  chi[1] = SQ (star1[4] - dummy[4]) / (tot[1] + err[4]);
+  chi[2] = SQ (star1[5] - dummy[5]) / (tot[2] + err[5]);
+  chi[3] = SQ (star1[6] - dummy[6]) / (tot[3] + err[6]);
+
+  if (star1[4] < dummy[4]) chi[1] = 0;
+  if (star1[6] < dummy[6]) chi[3] = 0;
+  chi[4] = chi[1] + chi[2] + chi[3];
+
+  mprint (2, "galaxy test, object at %f %f  %f\n", star2[2], star2[3], star2[1]);
+  mprint (2, "chisqs: %f %f %f %f\n", chi[1], chi[2], chi[3], chi[4]);
+
+  nsigma = MIN (sqrt(chi[4]), 1.0e8);
+  mprint (2, "nsigma = %f\n", nsigma);
+
+  value = chi[4] >= chicrit;
+
+  return (value);
+
+}
+
+/* this function uses C 0,N-1 for a[], fa[] */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/gophot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/gophot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/gophot.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "gophot.h"
+
+int main (int argc, char **argv) {
+
+  float tmp;
+
+  if (argc < 4) {
+    fprintf (stderr, "ERROR: usage: dophot imagename outfile paramfile\n");
+    exit (2);
+  }
+  ConfigInit (&argc, argv);
+
+  fprintf (stderr, "reading from %s, writing to %s, %s param file\n", argv[1], argv[2], argv[3]);
+
+  strcpy (files[4], argv[2]);
+
+  /* load image header and data */
+  if (!gfits_read_header (argv[1], &header)) {
+    fprintf (stderr, "ERROR: can't open FITS header %s\n", argv[1]);
+    exit (1);
+  }
+  if (!gfits_read_matrix (argv[1], &matrix)) {
+    fprintf (stderr, "ERROR: can't open FITS matrix %s\n", argv[1]);
+    exit (1);
+  }
+  /* convert to float, set up noise array and axes */
+  gfits_convert_format (&header, &matrix, -32, 1.0, 0.0, 0);
+  ALLOCATE (noise, float, matrix.size);
+  big = (float *) matrix.buffer;
+  nfast = matrix.Naxis[0];
+  nslow = matrix.Naxis[1];
+ 
+  /* override config values with header values */ 
+  if (gfits_scan (&header, "SATVALUE", "%f", 1, &tmp)) itop = tmp;
+  if (gfits_scan (&header, "GAIN", "%f", 1, &tmp)) eperdn = tmp;
+  if (gfits_scan (&header, "NEWGAIN", "%f", 1, &tmp)) eperdn = tmp;
+
+  /* we are scaling the image to electrons, must also scale parameters */
+  itop *= eperdn;
+  ibot *= eperdn;
+  tmax *= eperdn;
+  cmax *= eperdn;
+  ctpersat *= eperdn;
+
+  dophot (); 
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
+
+/* in the rest of the program:
+   NFAST = matrix.Naxis[0]  -- # of X-axis pixels 
+   NSLOW = matrix.Naxis[1]  -- # of Y-axis pixels 
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/guess.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/guess.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/guess.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "gophot.h"
+
+/* fill in values for star[NPMAX] based on rough stats and sky model */
+float guess1 (float *star, float *dummy, int ix, int iy) {
+
+  /* parinterp fills in star[4,5,6] from averages */
+  parinterp (ix, iy, star);
+
+  star[0] = sum2/ufactor;
+  star[1] = maxval/ufactor;
+  star[2] = xmax;
+  star[3] = ymax;
+
+  return (0.0);
+
+}
+
+/* fill in values for star[NPMAX] based on rough stats and sky model */
+/* use sigma_x, sigma_y from fillerup */
+float newguess (float *star, float *dummy, int ix, int iy) {
+
+  star[0] = sum2 / ufactor;
+  star[1] = maxval / ufactor;
+  star[2] = xmax;
+  star[3] = ymax;
+  star[4] = xmax2;
+  star[5] = 0;
+  star[6] = ymax2;
+  
+  return (0.0);
+}
+
+/* fill in values for star from instar, rescaling */
+float guess2 (float *star, float *instar, int *ix, int *iy) {
+
+  /* i'm concerned that the ix, iy values need to be passed back. */
+  float value;
+
+  *ix = (int) (instar[2] + 0.5);
+  *iy = (int) (instar[3] + 0.5);
+
+  star[0] = instar[0]/ufactor;
+  star[1] = instar[1]/ufactor;
+  star[2] = instar[2] - *ix;
+  star[3] = instar[3] - *iy;
+  star[4] = instar[4];
+  star[5] = instar[5];
+  star[6] = instar[6];
+
+  value = instar[0];				
+  return (value);
+}
+
+
+/* fill in values for star from instar, rescaling */
+float guess3 (float *star, float *instar, int *ix, int *iy) {
+
+  float value;
+
+  *ix = (int) (instar[2] + 0.5);
+  *iy = (int) (instar[3] + 0.5);
+
+  parinterp (instar[2], instar[3], star);
+
+  star[0] = instar[0]/ufactor;
+  star[1] = instar[1]/ufactor;
+  star[2] = instar[2] - *ix;
+  star[3] = instar[3] - *iy;
+
+  value = instar[0];				
+  return (value);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/impaper2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/impaper2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/impaper2.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "gophot.h"
+
+impaper2 (int k) {
+
+  int i, nsky, nsort;
+  float sky, sum, r, dr, zsort[MAXFIL];
+
+  /* find median sky outside inner radius */
+
+  dr = 0.5*(SQ (0.5*arect[1]) + SQ (0.5*arect[2]));
+  nsort = 0;
+  for (i = 0; i < npt; i++) {
+    r = SQ (xs[i]) + SQ (ys[i]);
+    if (r >= dr) {
+      zsort[nsort] = zs[i];
+      nsort ++;
+    }
+  }
+  
+  if (nsort < 1) return (0);
+
+  fsort (zsort, nsort);
+        
+  sky = 0;
+  nsky = 0;
+  for (i = 0.25*nsort; i < 0.75*nsort; i++) {
+    sky += zsort[i];
+    nsky ++;
+  }
+  sky /= nsky;
+
+  sum = 0;
+  for (i = 0; i < npt; i++) {
+    sum += (zs[i] - sky);
+  }
+        
+  apple[k][1] = sum*ufactor;
+
+  return (1);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/improve.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/improve.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/improve.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include "gophot.h"
+
+# define ADD +1
+# define SUB -1
+
+improve (int last) {
+
+  float err[NPMAX], star[NPMAX], sky, dx, dy, starchi;
+  int i, k, ix, iy, jmtype, niter, nfit;
+  bool skip, snok;
+
+  /* imtype may be 1-9 for regular objects, 101-109 for fixed objects */
+
+  for (i = 0; i < nstot; i++) {
+
+    /* set up flags for fixed objects */
+    fixxy = fixpos && (imtype[i] >= 100);
+    jmtype = fixxy ? imtype[i] - 100 : imtype[i];
+    /* inverse: imtype[i] = fixxy ? jmtype + 100 : jmtype; */
+
+    if (jmtype == 0) continue;
+    if (jmtype == 6) continue;
+    if (jmtype == 16) continue;
+    if (jmtype == 8) continue;
+    if (jmtype == 18) continue;
+
+    /* if (jmtype == 3) continue; */
+    if (jmtype == 2) continue;
+    if (jmtype == 12) continue;
+    if (jmtype == 10) continue;
+	   
+    /* add star back to frame, get sky, get subraster */
+    addstar (starpar[i], ADD, jmtype);
+    sky = guess3 (star, starpar[i], &ix, &iy);
+    mprint (3, " improving star %d at %d, %d\n", i, ix, iy);
+    nrect[1] = irect[1];
+    nrect[2] = irect[2];
+    fillerup (ix, iy, FALSE);  /* we don't use the center information here */
+
+    /* skip star if off picture, has negative flux, or if s/n too low for non-fixed objects */
+    skip = offpic (star, ix, iy, &dx, &dy);
+    if (star[1] <= 0) {
+      skip = TRUE;
+      starpar[i][1] = 0;
+    }
+    if (!skip && !fixxy) {
+      snok = transmask (ix,iy,sky);
+      skip = skip && !snok;
+    }
+    if (skip) {
+      jmtype = 6;
+      mprint (3, " deactivating star %d at %d, %d\n", i, ix, iy);
+      imtype[i] = fixxy ? jmtype + 100 : jmtype;
+      continue;
+    }
+
+    /* fixed objects only vary x,y */
+    if (fixxy) {
+      nfit = NFIT0;
+      niter = 2;
+    } else {
+      nfit = NFIT1;
+      niter = nit;
+    }
+
+    filladjust (star); 
+
+    /* fit for magnitudes -- 4 param fit (or 2 for fixed obj) (initial guesses in are the previous fits) */
+    starchi = chisq (onestar, xs, ys, zs, dzs, npt, star, err, nfit, acc, parlim, niter);
+
+    /* set non-converge objects to type 4, don't bother with other calcs */
+    if (!finite(starchi)) {
+      if (jmtype != 3) jmtype = 4;
+      for (k = 0; k < NPMAX; k++) galpar[i][k] = starpar[i][k];
+      for (k = 0; k < NPMAX; k++) shadow[i][k] = starpar[i][k];
+      apple[i][4] = MIN (1.086*sqrt(err[1])/star[1], 1);
+      addstar (starpar[i], SUB, jmtype);
+      imtype[i] = fixxy ? jmtype + 100 : jmtype;
+      continue;
+    }
+
+    /* things which get negative flux, turn in to type 6 and skip */
+    if (star[1] <= 0) {
+      starpar[i][1] = 0;
+      jmtype = 6;
+      mprint (3, " deactivating star %d at %d, %d\n", i, ix, iy);
+      imtype[i] = fixxy ? jmtype + 100 : jmtype;
+      continue;
+    }
+
+    /* this section applies to type 1 3 5 7 */
+    if ((jmtype != 2) && (jmtype != 10)) {
+      /* store the fitted values */
+      parupd (star, starpar[i], ix, iy);
+    }
+
+    if ((jmtype != 2) && (jmtype != 3) && toofaint(starpar[i], err)) jmtype = 7;
+	   
+    apple[i][4] = MIN (1, 1.086*sqrt(err[1])/star[1]);
+
+    if (last) {
+      nrect[1] = arect[1];
+      nrect[2] = arect[2];
+      fillerup (ix, iy, FALSE);
+      impaper2 (i);
+    }
+	      
+    addstar (starpar[i], SUB, jmtype);
+    imtype[i] = fixxy ? jmtype + 100 : jmtype;
+
+  }
+
+  return (0);
+
+}
+
+/* this function uses C 0,N-1 for a[], fa[] */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/isearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/isearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/isearch.c	(revision 22322)
@@ -0,0 +1,170 @@
+# include "gophot.h"
+
+int isearch (int first) {
+  
+  int jrect[3];
+  int i, j, nsprev, nfound, nnew, ngoodpts, ngoodmask, ngoodfit, nfit;
+  float dummy[2*NPMAX], star[2*NPMAX], err[NPMAX], pixt2, bestsky;
+  float tthresh, thresh2, highsky, highthresh, tmp;
+  float sky, Chi, dx, dy, rdnoise2;
+  float *Bval, *Nval;
+  bool iscosmic, isbright, isfaint, hole;
+  float get_mediansky ();
+  FILE *f;
+
+  bzero (dummy, NPMAX*sizeof(float));
+  
+  pixt2 = SQ(pixthresh);
+
+  /* use jrect as temp in isearch.c */
+  for (i = 1; i < 3; i++) {
+    jrect[i] = (first) ? krect[i] : irect[i]; 
+  }
+	
+  nsprev = nstot;
+  nnew = 0;
+  ngoodpts = 0;
+  ngoodmask = 0;
+  ngoodfit = 0;
+  thresh2 = SQ(thresh);
+  nfit = (first) ? NFIT2 : NFIT1;
+  /* on first pass or so, we use 7 par fit, not 4 par fit */
+
+  Bval = big;
+  Nval = noise;
+  for (i = 0; i < nslow; i++) {
+    for (j = 0; j < nfast; j++, Bval++, Nval++) {
+
+      /* occasionally update sky guess based on median sky image */
+      if (!(j % nthpix)) {
+	sky = get_mediansky (j, i); 
+	tthresh = sky + thresh;
+      }
+
+      imtype[nstot] = 1;
+
+      /* skip bad pixels */
+      if (*Bval < tthresh) continue;
+      if (!finite (*Nval)) continue;
+      if (*Bval < *Nval - rnoise) continue;
+
+      /* test for significant peak */
+      if (!transmask (j, i, sky)) continue;
+      nnew ++;
+
+      /* nrect is passed globally to fillerup or through - rather obscure */ 
+      nrect[1] = jrect[1];
+      nrect[2] = jrect[2];
+
+      /* fills the vectors xs, ys, zs, dzs */
+      if (!fillerup (j, i, TRUE)) continue;
+      if ((fabs(xmax) > 0.33*nrect[1]) || (fabs(ymax) > 0.33*nrect[2])) {
+	mprint (3, "centroid moved: %d %d  %f %f\n", j, i, xmax, ymax);
+      }
+
+      tmp = enuff4*jrect[1]*jrect[2];
+      if (npt < tmp) {
+	mprint (3, "skipping: npt = %d\n", npt);
+	continue;
+      }
+      ngoodpts ++;
+
+      /* if sky guess has changed significantly, retest for significant peak */
+      if (fabs(sum2 - sky) / maxval > 0.2) {
+	if (!transmask (j, i, sum2)) {
+	  mprint (3, "failed transmask on local sky, %f\n", sum2);
+	  continue;
+	}
+      }
+      ngoodmask ++;
+
+      /* fill in guess for this object */
+      if (first) {
+	newguess (star, dummy, j, i); 
+      } else {
+	guess1 (star, dummy, j, i);
+      }
+      filladjust (star); 
+
+      /* fit star using 4 parameterfit */
+      Chi = chisq (onestar, xs, ys, zs, dzs, npt, star, err, nfit, acc, parlim, nit);
+
+      /* if (finite(Chi) && ((err[2] > 0.1) || (err[3] > 0.1))) fprintf (stderr, "errors: %d %d  %f %f   %f %f   %f\n", j, i, err[2], err[3], err[4], err[6], Chi/npt); */
+      if (!finite(Chi)) {
+	mprint (3, "failed to converge:  no entry in starlist\n");
+	continue;
+      }
+      if ((fabs(star[2]) > 0.33*nrect[1]) || (fabs(star[3]) > 0.33*nrect[2])) {
+	mprint (3, "fit center moved: %d %d  %f %f\n", j, i, star[2], star[3]);
+	continue;
+      }
+      if (centertest (star, xs, ys, npt)) {
+	mprint (3, "star %d at %d %d moved\n", nstot, j, i);
+	continue;
+      }
+      if (offpic (star, j, i, &dx, &dy)) {
+	mprint (3, "fitted star off image\n");
+	continue;
+      }
+
+      /* save fit parameters */
+      parupd (star, starpar[nstot], j, i);
+      parupd (star, shadow[nstot], j, i);
+      rchisq[nstot] = Chi / npt;
+      apple[nstot][4] = MIN (1, 1.086*sqrt(err[1])/star[1]);
+      mprint (2, "star %d: %f, %f  peak: %f chisq: %f\n", nstot, starpar[nstot][2], starpar[nstot][3], maxval, rchisq[nstot]);
+      ngoodfit ++;
+
+      if (verybright (starpar[nstot])) {
+	imtype[nstot] = 10;
+	goto finish;
+      }	
+      if (cosmic (starpar[nstot])) {
+	imtype[nstot] = 8;
+	hole = oblit (starpar[nstot]);
+	goto finish_noadd;
+      }	
+      if (toofaint (starpar[nstot], err)) {
+	imtype[nstot] = 7;
+	mprint (3, "faint imtype %d\n", imtype[nstot]);
+	goto finish;
+      }
+
+    finish:
+      addstar (starpar[nstot], SUB, imtype[nstot]);
+
+    finish_noadd:
+      nstot ++;
+      if (nstot >= NSMAX) {
+	mprint (0, "too many stars!\n");
+	return (0);
+      }
+
+    }
+  }
+  nfound = nstot - nsprev;
+  mprint (0, "stars found %d, stars tested %d\n", nfound, nnew);
+  mprint (0, "ngoodpts: %d, ngoodfit: %d, ngoodmask: %d\n", ngoodpts, ngoodfit, ngoodmask);
+
+  return (nfound);
+}
+
+
+/* this function uses C 0,N-1 for a[], fa[] */
+
+      /* before star subtraction:
+
+       Nval contains just sq(RN) (e) 
+       Bval contains counts (e)
+       dBval = sqrt(Bval + Nval)
+
+       after star subtraction:
+       
+       Nval contains fit + sq(RN)
+       Bval contains (obs-fit)
+       dBval = sqrt(Bval + Nval) [Bval' + Nval' = Bval + Nval] 
+      */       
+
+      /* note that the coordinates used in the fit are relative to j, i
+	 and are adjusted back in parupd */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/large_features.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/large_features.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/large_features.c	(revision 22322)
@@ -0,0 +1,161 @@
+# include "gophot.h"
+
+/* these don't need to be 'static' unless we intend to call these 
+   functions outside of this file... */
+   
+static int *x;
+static int *y;
+static int Npts = 0;
+static int NPTS = 0;
+
+static int dx[] = {-1, 1, 0, 0};
+static int dy[] = {0, 0, -1, 1};
+static int Ntry = 4;
+
+static float *mediansky;
+static int Nx, Ny;
+static float fx, fy;
+
+float *copy_mediansky (int *, int *, float *, float *);
+int set_value (int, int, float);
+void large_features (float, float);
+void get_neighbors (int, int, float);
+float get_value (int, int);
+int outline (float, float, float, float, float, float, float *);
+float delete_ellipse (float *, float);
+
+void large_features (float sky, float dsky) {
+
+  int i, j, k;
+  float min, flux, dSky;
+  float Xo, Yo, dX, dY;
+  float Xmin, Xmax, Ymin, Ymax;
+  float fitpars[5];
+
+  nregion = 0;
+
+  /* use a copy to protect original */
+  mediansky = copy_mediansky (&Nx, &Ny, &fx, &fy);
+
+  dSky = sqrt(sky + rnoise);
+  dsky = MAX (dsky, dSky);
+
+  min = sky + 3*dsky;
+  fprintf (stderr, "sky %f, %f, %f\n", sky, dsky, min);
+  /* find pixels which stand above threshold */
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+      if (mediansky [i + j*Nx] > min) {
+	/* this is always the first point in the group */
+	/* init the storage arrays */
+	Npts = 0;
+	NPTS = 100;
+	ALLOCATE (x, int, NPTS);
+	ALLOCATE (y, int, NPTS);
+	x[Npts] = i;
+	y[Npts] = j;
+	Npts ++;
+	get_neighbors (i, j, min);
+	/* we now have a list x, y, Npts */
+	Xmin = Xmax = x[0];
+	Ymin = Ymax = y[0];
+	for (k = 0; k < Npts; k++) {
+	  Xmin = MIN (Xmin, x[k]);
+	  Ymin = MIN (Ymin, y[k]);
+	  Xmax = MAX (Xmax, x[k]);
+	  Ymax = MAX (Ymax, y[k]);
+	}
+	Xo = 0.5*(Xmax + 1 + Xmin);
+	Yo = 0.5*(Ymax + 1 + Ymin);
+	dX = (Xmax + 1 - Xmin);
+	dY = (Ymax + 1 - Ymin);
+	convert_coords (&Xo, &Yo, &dX, &dY);
+	fprintf (stderr, "large feature: %f %f  %f %f\n", Xo, Yo, dX, dY);
+	outline (Xo, Yo, 0.4*dX, 0.4*dY, sky + 7*dsky, 2*dsky, fitpars);
+	flux = delete_ellipse (fitpars, sky);
+	fprintf (stderr, " flux: %f  %f\n", flux, sky);
+	region[nregion][0] = sky;
+	region[nregion][1] = flux;
+	region[nregion][2] = fitpars[0];
+	region[nregion][3] = fitpars[1];
+	region[nregion][4] = fitpars[2];
+	region[nregion][5] = fitpars[3];
+	region[nregion][6] = fitpars[4];
+	region[nregion][7] = flux;
+	nregion ++;
+	if (nregion == 100) {
+	  fprintf (stderr, "too many regions!\n");
+	  exit (0);
+	}
+      }
+    }
+  }
+}
+
+void get_neighbors (int ix, int iy, float min) {
+
+  int i, Ix, Iy;
+
+  for (i = 0; i < Ntry; i++) {
+    
+    Ix = ix + dx[i];
+    Iy = iy + dy[i];
+
+    if (get_value(Ix, Iy) > min) {
+      
+      x[Npts] = Ix;
+      y[Npts] = Iy;
+      Npts ++;
+      if (Npts == NPTS) {
+	NPTS += 100;
+	REALLOCATE (x, int, NPTS);
+	REALLOCATE (y, int, NPTS);
+      } 
+      
+      set_value (Ix, Iy, 0);
+      
+      get_neighbors (Ix, Iy, min);
+      
+    }
+  }
+}
+
+float get_value (int i, int j) {
+
+  float value;
+
+  if (i < 0) return (0);
+  if (i >= Nx) return (0);
+
+  if (j < 0) return (0);
+  if (j >= Ny) return (0);
+
+  value = mediansky[i + Nx*j];
+
+  return (value);
+
+}
+
+int set_value (int i, int j, float value) {
+
+  if (i < 0) return (0);
+  if (i >= Nx) return (0);
+
+  if (j < 0) return (0);
+  if (j >= Ny) return (0);
+
+  mediansky[i + Nx*j] = value;;
+
+  return (1);
+
+}
+
+convert_coords (float *X, float *Y, float *dX, float *dY) {
+
+  *X /= fx;
+  *dX /= fx;
+  *Y /= fy;
+  *dY /= fy;
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/lubksb.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/lubksb.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/lubksb.c	(revision 22322)
@@ -0,0 +1,31 @@
+void lubksb (float **a, int n, int *indx, float *b) {
+
+  int i, j, ii, kk;
+  float sum;
+
+  ii = -1;
+  for (i = 0; i < n; i++) {
+    kk = indx[i];
+    sum = b[kk];
+    b[kk] = b[i];
+    if (ii != -1) {
+      for (j = ii; j < i; j++) {
+	sum -= a[i][j]*b[j];
+      }
+    } else {
+      if (sum != 0) ii = i;
+    }
+    b[i] = sum;
+  }
+
+  for (i = n-1; i >= 0; i--) {
+    sum=b[i];
+    if (i < n-1) {
+      for (j = i+1; j < n; j++) {
+	sum -= a[i][j]*b[j];
+      }
+    }
+    b[i] = sum/a[i][i];
+  }
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/ludcmp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/ludcmp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/ludcmp.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include <ohana.h>
+
+# define TINY 1.0e-20
+
+void ludcmp (float **a, int n, int *indx, float *D) {
+
+  float *vv;
+  int i, j, k, imax;
+  float aamax, sum, dum, d;
+
+  ALLOCATE (vv, float, n);
+
+  d = 1.0;
+  for (i = 0; i < n; i++) {
+    aamax = 0.0;
+    for (j = 0; j < n; j++) {
+      if (fabs (a[i][j]) > aamax) aamax = fabs (a[i][j]);
+    }
+    if (aamax == 0.0) {
+      *D = 0;
+      free (vv);
+      return;
+    }
+    vv[i] = 1.0/aamax;
+  }
+
+  for (j = 0; j < n; j++) {
+    for (i = 0; i < j; i++) {
+      sum = a[i][j];
+      for (k = 0; k < i; k++) {
+	sum -= a[i][k]*a[k][j];
+      }
+      a[i][j] = sum;
+    }
+    aamax = 0.0;
+    for (i = j; i < n; i++) {
+      sum = a[i][j];
+      for (k = 0; k < j; k++) {
+	sum -= a[i][k]*a[k][j];
+      }
+      a[i][j] = sum;
+      dum = vv[i]*fabs(sum);
+      if (dum >= aamax) {
+	imax = i;
+	aamax = dum;
+      }
+    }
+    if (j != imax) {
+      for (k = 0; k < n; k++) {
+	dum = a[imax][k];
+	a[imax][k] = a[j][k];
+	a[j][k] = dum;
+      }
+      d = -d;
+      vv[imax] = vv[j];
+    }
+    indx[j] = imax;
+    if (j != n-1) {
+      if (a[j][j] == 0.0) a[j][j] = TINY;
+      dum = 1.0 / a[j][j];
+      for (i = j+1; i < n; i++) {
+	a[i][j] = a[i][j]*dum;
+      }
+    }
+  }
+  if (a[n-1][n-1] == 0.0) a[n-1][n-1] = TINY;
+  free (vv);
+
+  *D = d;
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/makemask.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/makemask.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/makemask.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "gophot.h"
+
+makemask () {
+
+  int i, j, ii, jj;
+  float value;
+  float dumx, dumy, dummy, star[NPMAX];
+
+  dumx = 0;
+  dumy = 0;
+  dummy = parinterp (dumx, dumy, star);
+  
+  star[0] = 0;			
+  star[1] = 1;			
+  star[2] = 0;			
+  star[3] = 0;	
+  
+  for (j = 0, jj = -iyby2; j < 2*iyby2 + 1; j++, jj++) {
+    for (i = 0, ii = -ixby2; i < 2*ixby2 + 1; i++, ii++) {
+      starmask[i][j] = value = onestar (ii, jj, star, (float *) NULL);
+      mprint (2, "starmask[%d][%d]: %f\n", ii, jj, value);
+    }
+  }
+}
+
+/* this function uses C 0,N-1 for a[], fa[] */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/makenoise.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/makenoise.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/makenoise.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "gophot.h"
+
+makenoise () {
+
+  int i, j;
+  float *valN, *valB;
+
+  valN = noise;
+  valB = big;
+
+  for (i = 0; i < nslow; i++) {
+    if ((i < nbadbot) || (i > nslow - nbadtop - 1)) {
+      for (j = 0; j < nfast; j++, valN++, valB++) *valN = MAGIC;
+      continue;
+    }      
+    for (j = 0; j < nfast; j++, valN++, valB++) {
+      if (j < nbadleft) {
+	*valN = MAGIC;
+	continue;
+      }
+      if (j > nfast - nbadright - 1) {
+	*valN = MAGIC;
+	continue;
+      }
+      *valB *= eperdn;
+      if (*valB > itop) {
+	*valN = MAGIC;
+	continue;
+      }
+      if (*valB <= ibot) {
+	*valN = MAGIC;
+	continue;
+      }
+      *valN = rnoise;
+    }
+  }
+}
+
+/* arguments OK */
+
+
+/* NEW CHOICES FOR NOISE AND BIG:
+
+   'big' is converted to electrons - this is the Poisson component of the error
+   'noise' will contain just the SQ(read-noise) - this is added to when a star is subtracted
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/mediansky.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/mediansky.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/mediansky.c	(revision 22322)
@@ -0,0 +1,196 @@
+# include "gophot.h"
+
+static float *mediansky;
+static int Nx, Ny;
+static float fx, fy;
+
+int make_mediansky () {
+
+  /* we have an image of nfast x nslow pix.  
+     make a new image of size sqrt(nfast) x sqrt(nslow)
+     (2k x 4k -> 45 x 63)
+  */
+
+  float *temp;
+  int i, j, I0, I1, J0, J1, I, J, n;
+  int nx, ny;
+  float Mv, Nv, Mv2, value;
+
+  Nx = sqrt (nfast);
+  Ny = sqrt (nslow);
+
+  /* nthpix is used in isearch.c to update sky guess */
+  nthpix = Nx;
+
+  fx = (float) Nx / nfast;
+  fy = (float) Ny / nslow;
+
+  ALLOCATE (mediansky, float, Nx*Ny);
+
+  nx = 1 + 1/fx;
+  ny = 1 + 1/fy;
+
+  ALLOCATE (temp, float, 2*nx*ny);
+
+  Nv = Mv = Mv2 = 0.0;
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+      
+      I0 = i / fx;
+      J0 = j / fy;
+
+      I1 = (i + 1) / fx;
+      J1 = (j + 1) / fy;
+
+      n = 0;
+      temp[0] = 0;
+      
+      for (J = J0; J < J1; J++) {
+	if (J < nbadbot) continue;
+	if (J > nslow - nbadtop - 1) continue;
+	for (I = I0; I < I1; I++) {
+	  if (I < nbadleft) continue;
+	  if (I > nfast - nbadright - 1) continue;
+	  temp[n] = big[J*nfast + I];
+	  n++;
+	}
+      }
+
+      fsort (temp, n);
+      value = temp[(int)(0.5*n)];
+
+      if (n < 2) {
+	mediansky[j*Nx + i] = MAGIC;
+      } else {
+	mediansky[j*Nx + i] = value;
+      }
+    }
+  }
+  return (0);
+}
+
+/* take array which is the median sky, copy to a separate vector, 
+   sort, find median and sigma in 50% interval around median */
+
+get_skystats (float *sky, float *dsky) {
+
+  float *temp, sky2;
+  int i, Npix, Nsky;
+
+  Npix = Nx*Ny;
+
+  ALLOCATE (temp, float, Npix);
+
+  memcpy (temp, mediansky, Npix*sizeof(float));
+
+  fsort (temp, Npix);
+
+  *sky = temp[(int)(0.5*Npix)];
+
+  Nsky = 0;
+  sky2 = 0;
+  for (i = 0.25*Npix; i < 0.75*Npix; i++) {
+    if (!finite(temp[i])) continue;
+    sky2 += SQ(temp[i] - *sky);
+    Nsky ++;
+  }
+  
+  *dsky = sqrt (sky2/Nsky);
+
+  free (temp);
+
+  mprint (0, "median sky: %f %f\n", *sky, *dsky);
+
+  return (1);
+
+}
+
+fix_mediansky (float sky) {
+
+  int i;
+
+  for (i = 0; i < Nx*Ny; i++) {
+    if (!finite (mediansky[i])) {
+      mediansky[i] = sky;
+    }
+  }
+}
+
+/* this version is fast and has no edge problems. */
+
+# if (1) 
+float get_mediansky (int i, int j) {
+
+  float value;
+  int I, J;
+
+  I = i * fx;
+  J = j * fy;
+
+  value = mediansky[J*Nx + I];
+
+  return (value);
+
+}
+# endif
+
+/* this version is slow, but more accurate.
+   on the other hand, it needs to be fixed for
+   edge problems. */
+
+# if (0) 
+float get_mediansky (int i, int j) {
+
+  float Fx, Fy, Vo, Vm, Vx, Vy;
+  float value;
+  int I, J, dx, dy;
+
+  I = i * fx;
+  J = j * fy;
+
+  Vo = mediansky[J*Nx + I];
+
+  Fx = i * fx - I;
+  if (Fx < 0.5) {
+    dx = -1;
+  } else {
+    dx = +1;
+    Fx = 1.0 - Fx;
+  }
+  Vm = mediansky[J*Nx + I + dx];
+  Vx = (Vo*(0.5+Fx) + Vm*(0.5-Fx));
+
+  Fy = j * fy - J;
+  if (Fy < 0.5) {
+    dy = -1;
+  } else {
+    dy = +1;
+    Fy = 1.0 - Fy;
+  }
+  Vm = mediansky[J*Nx + I + dy*Nx];
+  Vy = (Vo*(0.5+Fy) + Vm*(0.5-Fy));
+
+  value = 0.5*(Vx + Vy);
+
+  return (value);
+
+}
+# endif
+
+float *copy_mediansky (int *nx, int *ny, float *Fx, float *Fy) {
+
+  float *temp;
+
+  ALLOCATE (temp, float, Nx*Ny);
+  memcpy (temp, mediansky, Nx*Ny*sizeof(float));
+  
+  *nx = Nx;
+  *ny = Ny;
+  *Fx = fx;
+  *Fy = fy;
+
+  return (temp);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/message.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/message.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/message.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include <stdio.h>
+# include <stdarg.h>
+
+static FILE *f;
+static int verbosity = 0;
+
+int mprint (int level, char *mode, ...) {
+
+  int status;
+  va_list argp;
+  
+  if (level > verbosity) return (0);
+
+  va_start (argp, mode);
+  
+  status = vfprintf (f, mode, argp);
+
+  va_end (argp);
+
+  return (status);
+
+}
+
+int set_verbosity (int level) {
+
+  f = stderr;
+  verbosity = level;
+  return 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/objout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/objout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/objout.c	(revision 22322)
@@ -0,0 +1,88 @@
+# include "gophot.h"
+# define NCHAR 104
+
+completeout () {
+
+  FILE *f;
+  int i, Nchar;
+  float gcorr, gmag;
+  float area, amajor, aminor, tilt, fmag, xc, yc, apmag, tmp;
+  char line[NCHAR];
+	
+  f = fopen (files[4], "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "error, can't save data in file %s\n", files[4]);
+    exit (1);
+  }
+
+  ellipse (ava[4], ava[5], ava[6], &area, &amajor, &aminor, &tilt);
+  fprintf (f, "# Average Star: %f %f %f\n", amajor, aminor, tilt);
+
+  for (i = 0; i < nregion; i++) {
+    
+    fmag = -2.5*log10 (region[i][1]);
+    gmag = -2.5*log10 (region[i][7]);
+    apmag = 99.999;
+
+    Nchar = snprintf (line, NCHAR, "%3d %8.2f %8.2f %8.3f %6.3f %9.2f %9.3f %9.3f %7.2f %8.3f %8.3f  %8.2f",
+		      20, region[i][2], region[i][3], fmag, 0.01, region[i][0], region[i][4], region[i][5], region[i][6], 
+		      gmag, apmag, 10.0);
+    fprintf (f, "%s\n", line);
+  }
+
+  for (i = 0; i < nstot; i++) {
+         
+    fmag = 99.999;
+    gmag = 99.999;
+    apmag = 99.999;
+
+    if (imtype[i] != 8) {
+      /* pure gaussian fit mags */
+      ellipse (starpar[i][4], starpar[i][5], starpar[i][6], &area, &amajor, &aminor, &tilt);
+      tmp = area*starpar[i][1]/eperdn;
+      if (tmp > 0.0) fmag = -2.5 * log10 (tmp);
+      /* galaxy-non-gauss fit mags */
+      ellipse (shadow[i][4], shadow[i][5], shadow[i][6], &area, &amajor, &aminor, &tilt);
+      gcorr = 1.0;
+      tmp = area*gcorr*shadow[i][1]/eperdn;
+      if (tmp > 0.0) gmag = -2.5 * log10 (tmp);
+      tilt = 57.29578 * tilt;
+    } else {
+      /* get correct orientation for oblit boxes */
+      if (starpar[i][5] != -1) fmag = -99.999;
+      if (starpar[i][4] >= starpar[i][6]) {
+	amajor = starpar[i][4];
+	aminor = starpar[i][6];
+	tilt = 0.0;
+      } else {
+	amajor = starpar[i][6];
+	aminor = starpar[i][4];
+	tilt = 90.0;
+      }
+    }
+         
+    /* what is the value of the center of a pixel? */
+    /* this assumes the pixel center is at 0,0, not 0.5, 0.5 */
+    xc = starpar[i][2];
+    yc = starpar[i][3];
+
+    if (apple[i][1] > 0.0) apmag = -2.5 * log10 (apple[i][1]/eperdn);
+         
+    Nchar = snprintf (line, NCHAR, "%3d %8.2f %8.2f %8.3f %6.3f %9.2f %9.3f %9.3f %7.2f %8.3f %8.3f  %8.2f",
+	     imtype[i], xc, yc, fmag, apple[i][4], shadow[i][0], amajor, aminor, tilt, 
+	     gmag, apmag, rchisq[i]);
+    if (Nchar != NCHAR - 1) {
+      mprint (1, "funny line %d\n", i);
+    }
+    fprintf (f, "%s\n", line);
+  }         
+
+  /*
+  1    37.51  1193.91  -15.246   .034    594.48     6.657     5.654   -7.06  -15.611  -15.822
+  */  
+  fclose (f);
+
+  return (0);
+}
+
+/* this function uses C 0,N-1 for a[], fa[] */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/oblims.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/oblims.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/oblims.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include "gophot.h"
+
+oblims (float *star, int *jrect) {
+
+  float temp, fudgex, fudgey;
+
+  if (star[1] > 0) {
+    /* think about this line: */
+    temp = pow ((6*star[1]/nphob), 0.33333333);
+    if (star[4] > 0) {
+      fudgex = sqrt(temp*star[4]*2);
+    } else {
+      fudgex = 10;
+    }
+    if (star[6] > 0) {
+      fudgey = sqrt(temp*star[6]*2);
+    } else {
+      fudgey = 10;
+    }
+  } else {
+    fudgex = 10;
+    fudgey = 10;
+  }
+
+  mprint (3, "fudge size: %f %f\n", fudgex, fudgey);
+  jrect[1] = star[2] - fudgex;
+  jrect[2] = star[2] + fudgex;
+  jrect[3] = star[3] - fudgey;
+  jrect[4] = star[3] + fudgey;
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/oblit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/oblit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/oblit.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "gophot.h"
+
+bool oblit (float *star) {
+
+  bool value;
+	
+  float wx, wy, dum, duma[NPMAX];
+  int ix, iy, ixhi, ixlo, iyhi, iylo, jx, jy;
+	
+  dum = guess2 (duma, star, &ix, &iy);
+  wx = star[4];				
+  wy = star[6];
+
+  ixhi = MIN ((int)(ix + 0.5*wx + 0.5), nfast-1);
+  ixlo = MAX ((int)(ix - 0.5*wx + 0.5), 0);
+  iyhi = MIN ((int)(iy + 0.5*wy + 0.5), nslow-1);
+  iylo = MAX ((int)(iy - 0.5*wy + 0.5), 0);
+
+  mprint (3, "obliterating following region : %d - %d, %d - %d\n", ixlo, ixhi, iylo, iyhi);
+  for (jy = iylo; jy <= iyhi; jy++) {
+    for (jx = ixlo; jx <= ixhi; jx++) {
+      big[jx+jy*nfast] = MAGIC;
+      noise[jx+jy*nfast] = MAGIC;
+    }
+  }
+
+  mprint (2, "obliteration: %d, %d (%f x %f)\n", ix, iy, wx, wy);
+
+  return (TRUE);
+
+}
+
+	
+	
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/offpic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/offpic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/offpic.c	(revision 22322)
@@ -0,0 +1,24 @@
+# include "gophot.h"
+
+bool offpic (float *star, int ix, int iy, float *dx, float *dy) {
+
+  bool nogood;
+  float x, y;
+
+  x = ix + star[2];
+  y = iy + star[3];
+  
+  *dx = 0;
+  if (x < 0) *dx = -x;
+  if (x > nfast) *dx = x - nfast;
+	
+  *dy = 0;
+  if (y < 0) *dy = -y;
+  if (y > nslow) *dy = y - nslow;
+	
+  nogood = (*dx != 0) || (*dy != 0);
+
+  if (!fixpos) nogood = nogood || (star[1] < 0);
+
+  return (nogood);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/outline.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/outline.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/outline.c	(revision 22322)
@@ -0,0 +1,188 @@
+# include "gophot.h"
+
+int Npts;
+float *xv, *yv, *zv;
+float par[5];
+float dpar[5];
+float outline_chi (float);
+
+/* fit pars[5] to ellipse at Xo, Yo, dX, dY */
+int outline (float Xo, float Yo, float dX, float dY, float Io, float dIo, float *pars) {
+  
+  int i, j, k, Nx, Ny, NPTS, BigChange, ABigChange;
+  float oChi, dchi, Chi, Chi_p, Chi_m, dp, tmp_par, nChi, tmp1, tmp2;
+  float curve, frac, value;
+
+  par[0] = Xo;
+  par[1] = Yo;
+  par[2] = dX;
+  par[3] = dY;
+  par[4] = 0.0;
+
+  dpar[0] = 10;
+  dpar[1] = 10;
+  dpar[2] = 10;
+  dpar[3] = 10;
+  dpar[4] = 10;
+
+  /* find all pixels within range Io-dIo : Io+dIo, in region about center guess */
+
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (xv, float, NPTS);
+  ALLOCATE (yv, float, NPTS);
+  ALLOCATE (zv, float, NPTS);
+  for (j = par[1]-2*par[3]; j < par[1]+2*par[3]; j++) {
+    if (j < 0) continue;
+    if (j >= nslow) continue;
+    for (i = par[0]-2*par[2]; i < par[0]+2*par[2]; i++) {
+      if (i < 0) continue;
+      if (i >= nfast) continue;
+      value = big[i + nfast*j];
+      if (fabs (value - Io) < dIo) {
+	xv[Npts] = i;
+	yv[Npts] = j;
+	zv[Npts] = value;
+	Npts ++;
+	if (Npts == NPTS) {
+	  NPTS += 1000;
+	  REALLOCATE (xv, float, NPTS);
+	  REALLOCATE (yv, float, NPTS);
+	  REALLOCATE (zv, float, NPTS);
+	}
+      }
+    }
+  }
+
+  if (Npts == 0) {
+    fprintf (stderr, "no valid points in box, try again\n");
+    free (xv);
+    free (yv);
+    free (zv);
+    return (FALSE);
+  }
+
+  Chi = outline_chi (Io);
+  for (j = 0; j < 15; j++) {
+    
+    oChi = Chi;
+    for (i = 0; i < 5; i++) {
+      /* find +Chi, -Chi for this par & adjust par as needed */
+
+      for (k = 0, BigChange = TRUE; (k < 3) && BigChange; k++) {
+	tmp_par = par[i];
+	par[i] = tmp_par + dpar[i];
+	Chi_p = outline_chi (Io);
+	par[i] = tmp_par - dpar[i];
+	Chi_m = outline_chi (Io);
+	
+	/* have we braketted a minimum? (curve < 0) */
+	curve = (Chi_p - Chi) * (Chi - Chi_m);
+	if (curve > 0) {
+	  dp = 2*dpar[i];
+	} else {
+	  dp = 0.5 * dpar[i] * (Chi_m - Chi_p) / (Chi_m + Chi_p - 2*Chi);
+	}      
+	if (Chi_m + Chi_p - 2*Chi == 0) dp = 0;
+	/* don't let extrapolation go too far */
+	if (fabs (dp) > 2*fabs(dpar[i])) { dp = SIGN(dp) * fabs (2*dpar[i]); }
+	
+	par[i] = tmp_par + dp;
+	Chi = outline_chi (Io);
+	
+	BigChange = FALSE;
+	if (Chi <= 1.001*oChi) {
+	  /* got better */
+	  dchi = (oChi - Chi) / oChi; 
+	  if ((dchi > 0.03) || (curve > 0)) BigChange = TRUE;
+	} else {
+	  par[i] = tmp_par;
+	  Chi = oChi;
+	  if (Chi_m < Chi) {
+	    Chi = Chi_m;
+	    par[i] = tmp_par - dpar[i];
+	  }	
+	  if (Chi_p < Chi) {
+	    Chi = Chi_p;
+	    par[i] = tmp_par + dpar[i];
+	  }	
+	}	
+	oChi = Chi;
+      }
+      if (!BigChange) dpar[i] *= 0.8;
+    }
+
+    mprint (0, "try: %d  %f   ", j, Chi);
+    for (i = 0; i < 5; i++) {
+      mprint (0, "%f ", par[i]);
+    }
+    mprint (0, "\n");
+    for (i = 0; i < 5; i++) {
+      mprint (2, "%f ", dpar[i]);
+    }
+    mprint (2, "\n");
+    dchi -= Chi;
+
+  }
+
+  free (xv);
+  free (yv);
+  free (zv);
+  
+  pars[0] = par[0];
+  pars[1] = par[1];
+  pars[2] = par[2];
+  pars[3] = par[3];
+  pars[4] = par[4];
+  return (TRUE);
+
+}
+
+/* par[0] = x
+   par[1] = y
+   par[2] = dx
+   par[3] = dy
+   par[4] = dxy
+   
+    xp = par[2] * cos (t);
+    yp = par[3] * sin (t);
+    
+    x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG) + par[0];
+    y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG) + par[1];
+
+*/
+
+float outline_chi (float Io) {
+
+  int i;
+  float theta, phi;
+  float xp, yp, x, y;
+  float Chi, dv, Dv, R2, F2;
+  float cs, sn;
+
+  Chi = 0;
+
+  cs = cos (par[4] * RAD_DEG);
+  sn = sin (par[4] * RAD_DEG);
+
+  for (i = 0; i < Npts; i++) {
+    
+    phi = atan2 (yv[i] - par[1], xv[i] - par[0]) - RAD_DEG * par[4];
+    theta = atan2 (par[2]*sin(phi), par[3]*cos(phi));
+
+    /* this is the point on the ellipse at the same angle as ref point */
+    xp = par[2] * cos (theta);
+    yp = par[3] * sin (theta);
+    
+    x = xp * cs - yp * sn + par[0];
+    y = xp * sn + yp * cs + par[1];
+
+    R2 = sqrt (SQ (x - xv[i]) + SQ (y - yv[i]));
+    Chi += R2;
+
+  }
+
+  Chi = Chi / Npts;
+  return (Chi);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/paravg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/paravg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/paravg.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "gophot.h"
+
+sortkey (int *index, float *key, int N) {
+
+# define SWAPFUNC(A,B){ \
+  int   itmp; itmp = index[A]; index[A] = index[B]; index[B] = itmp; \
+  float ftmp; ftmp = key[A]; key[A] = key[B]; key[B] = ftmp; \
+}
+# define COMPARE(A,B)(key[A] < key[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+paravg () {
+
+  float sum[NPMAX][2];
+  int i, j, n;
+  FILE *f;
+  char c;
+  float *ave4, *ave5, *ave6, *key;
+  int Ngood, NGOOD, *index, *idx;
+  int Nave;
+  float a4, a5, a6;
+
+  for (j = 0; j < NPAR; j++) {
+    sum[j][0] = 0;
+    sum[j][1] = 0;
+  }
+  
+  if (nstot < 1) return (0);
+
+  NGOOD = 100;
+  Ngood = 0;
+  ALLOCATE (index, int, NGOOD);
+
+  /* find good stars, store index */
+  for (i = 0; i < nstot; i++) {
+    if (starpar[i][1] < 100) continue;
+    if ((imtype[i] == 1) || (imtype[i] == 101)) {
+      index[Ngood] = i;
+      Ngood ++;
+      if (Ngood == NGOOD) {
+	NGOOD += 100;
+	REALLOCATE (index, int, NGOOD);
+      }
+    }
+  }
+  for (i = 0; i < Ngood; i++) {
+    fprintf (stderr, "S: %d %d %d  %f %f   %f %f\n", i, index[i], imtype[index[i]], starpar[index[i]][1], shaderr[index[i]][1], starpar[index[i]][4], shadow[index[i]][4]);
+  }
+
+  /* find good stars, store index */
+  if (Ngood < 10) { /* accept type 2 as well... */
+    for (i = 0; i < nstot; i++) {
+      if ((imtype[i] == 2) || (imtype[i] == 102)) {
+	index[Ngood] = i;
+	Ngood ++;
+	if (Ngood == NGOOD) {
+	  NGOOD += 100;
+	  REALLOCATE (index, int, NGOOD);
+	}
+      }
+    }
+  }
+
+  if (Ngood > 10) { /* don't change if not enough 'stars' */
+    
+    ALLOCATE (ave4, float, Ngood);
+    ALLOCATE (ave5, float, Ngood);
+    ALLOCATE (ave6, float, Ngood);
+    ALLOCATE (key,  float, Ngood);
+    ALLOCATE (idx,  int, Ngood);
+    
+    for (i = 0; i < Ngood; i++) {
+      idx[i] = i;
+      key[i] = shadow[index[i]][4];
+      ave4[i] = shadow[index[i]][4];
+      ave5[i] = shadow[index[i]][5];
+      ave6[i] = shadow[index[i]][6];
+    }
+    sortkey (idx, key, Ngood);
+
+    a4 = a5 = a6 = Nave = 0;
+    for (i = 0.4*Ngood; i < 0.6*Ngood; i++) {
+      a4 += ave4[idx[i]];
+      a5 += ave5[idx[i]];
+      a6 += ave6[idx[i]];
+      Nave ++;
+    }
+    ava[4] = a4/Nave;
+    ava[5] = a5/Nave;
+    ava[6] = a6/Nave;
+
+    free (key);
+    free (idx);
+    free (ave4);
+    free (ave5);
+    free (ave6);
+  }
+  free (index);
+  
+
+  mprint (1, "%d stars used to find parameter averages\n", Ngood);
+  mprint (1, "average values so far of shape parameters for stars: \n");
+  for (j = 4; j < NPAR; j++) mprint (1, "%f ", ava[j]);
+  mprint (1, "\n");
+
+}
+/* this function uses C 0,N-1 for a[], fa[] */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/parinterp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/parinterp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/parinterp.c	(revision 22322)
@@ -0,0 +1,10 @@
+# include "gophot.h"
+
+float parinterp (float x, float y, float *star) {
+
+  star[4] = ava[4];
+  star[5] = ava[5];
+  star[6] = ava[6];
+  
+  return (star[0]);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/parupd.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/parupd.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/parupd.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "gophot.h"
+
+parupd (float *instar, float *outstar, int ix, int iy) {
+
+	outstar[0] = instar[0]*ufactor;
+	outstar[1] = instar[1]*ufactor;
+	outstar[2] = instar[2] + ix;
+	outstar[3] = instar[3] + iy;
+	outstar[4] = instar[4];
+	outstar[5] = instar[5];
+	outstar[6] = instar[6];
+}
+
+/* a legacy function 
+twoupd (float *instar, float *outstar, int ix, int iy) {
+
+	outstar[0] = instar[0];
+	outstar[1] = instar[1];
+	outstar[2] = instar[2] + ix;
+	outstar[3] = instar[3] + iy;
+	outstar[4] = instar[4];
+	outstar[5] = instar[5] + ix;
+	outstar[6] = instar[6] + iy;
+}
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/pseud2d.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/pseud2d.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/pseud2d.c	(revision 22322)
@@ -0,0 +1,84 @@
+# include "gophot.h"
+
+# define HALF 0.5
+# define THIRD 0.333
+# define EXPMIN -15.0
+
+float pseud2d (int ix, int iy, float *a, float *fa) {
+
+  double x, y, t5, t6, t7, T;
+  double denom, dt, pexp, R;
+  double Q;
+  float value;
+  int i;
+
+  x = ix - a[2];
+  y = iy - a[3];
+       
+  t6 = a[5]*y;
+  t5 = x/a[4];
+  t7 = y/a[6];
+
+  T = 0.5*((t5 + 2.0*t6)*x + t7*y);
+  
+  if (!finite (a[1])) fprintf (stderr, "bad star!\n");
+
+  if (T >= 0) { 
+    denom = 1.0 + T*(beta4 + HALF*beta64*T*(1.0 + THIRD*T));
+    pexp = 1.0 / denom; 
+    dt = beta4 + beta64*T*(1.0 + HALF*T);
+    if (!finite (pexp)) {
+      fprintf (stderr, "error in pseud2d: %f %f %f %d %d\n", T, denom, pexp, ix, iy); 
+      denom = 1.0 + T + HALF*beta4*T*T + THIRD*beta64*T*T*T;
+      pexp = 1.0 / denom;
+      if (!finite (pexp)) { 
+	fprintf (stderr, "error in pseud2d: %f %f %f %d %d\n", T, denom, pexp, ix, iy);
+	fprintf (stderr, "%f %f   %f %f %f\n", x, y, t5, t6, t7); 
+	fprintf (stderr, "%f %f %f\n", T, T*HALF*beta4*T, T*HALF*beta4*T*THIRD*beta64*T);
+	for (i = 0; i < 7; i++) fprintf (stderr, "%d %f\n", i, a[i]);
+	fprintf (stderr, "nstot: %d\n", nstot);
+	fprintf (stderr, "invalid value!\n");
+	exit (1);
+      } 
+    }
+   } else {
+    denom = MAX (T, EXPMIN);
+    denom = fabs (denom);
+    pexp = exp (denom);
+    denom = 1.0;
+    dt = 1.0;
+  }
+
+  value = a[1]*pexp;
+
+  if (fa != (float *) NULL) {
+    R = value*dt/denom;
+    fa[1] = pexp;
+    fa[2] = R*(t5 + t6);
+    fa[3] = R*(a[5]*x + t7);
+    fa[4] = R*t5*t5*0.5;
+    fa[6] = R*t7*t7*0.5;
+    
+    fa[5] = -R*x*y;
+    fa[0] = 1.0;
+  }
+
+  value += + a[0];
+
+  if (!finite(value)) {
+    fprintf (stderr, "error in pseud2d: %f %f %f %f\n", T, denom, pexp, a[1]);
+    exit (1);
+  }
+
+  return (value);
+
+}
+
+/* this function uses C 0,N-1 for a[], fa[] */
+
+/* a[0] - sky
+   a[1] - Io
+   a[2], a[3] - X, Y
+   a[4], a[5], a[6] - shape
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/pseud4d.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/pseud4d.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/pseud4d.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "gophot.h"
+
+# define HALF 0.5
+# define THIRD 0.33333333333333
+# define EXPMIN -15
+
+float pseud4d (int ix, int iy, float *a, float *fa) {
+
+  int i, ioff;
+  float x, y;
+  float denom, T, dt, pexp[2];
+  float a2, a5, a6, a7, t5, t6, t7, value;
+	
+  a5 = 1.0/a[7];
+  a7 = 1.0/a[9];
+  a6 = a[8];
+  
+  for (i = 0; i < 2; i++) {
+
+    ioff = 3*i;
+    x = ix - a[2 + ioff];
+    y = iy - a[3 + ioff];
+       
+    t5 = a5*x;
+    t6 = a6*y;
+    t7 = a7*y;
+    T = HALF*((t5 + 2*t6)*x + t7*y);
+	   
+    if (T > 0) {
+      denom = 1.0 + T*(1.0 + HALF*beta4*T*(1.0 + THIRD*beta64*T));
+      pexp[i] = 1./denom;
+    } else {
+      denom = MAX (T, EXPMIN);
+      denom = fabs (denom);
+      pexp[i] = exp (denom);
+      denom = 1.0;
+    }
+
+    a2 = exp(a[1 + ioff]);
+    value = a2*pexp[i];
+    pexp[i] = value;
+    
+    if (fa != (float *) NULL) {
+      if (T > 0) {
+	dt = 1.0 + beta4*T*(1.0 + HALF*beta64*T);
+      } else {
+      	dt = 1.0;
+      }
+      fa[1 + ioff] = value;
+      T = pexp[i]*dt/denom;
+      fa[2 + ioff] = (a5*x + a6*y)*T;
+      fa[3 + ioff] = (a6*x + a7*y)*T;
+      
+      fa[0] = 1;
+    }      
+  }
+
+  value = pexp[0] + pexp[1] + a[0];
+
+  return (value);
+
+}
+
+/* this function uses C 0,N-1 for a[], fa[] */
+
+/* a[0] - sky
+   a[1] - I1
+   a[2], a[3] - X1, Y1
+   a[4] - I2
+   a[5], a[6] - X2, Y2
+   a[7], a[8], a[9] - shape
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/set_thresholds.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/set_thresholds.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/set_thresholds.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include "gophot.h"
+
+float set_thresholds (float sky, float dSky, float *factor, int *Nit) {
+
+  float dsky, tmp;
+
+  /* tmin starts with Nsigma above sky */
+
+  dsky = sqrt(sky + rnoise);
+  fprintf (stderr, "dsky: %f %f %f %f\n", rnoise, sky, dsky, dSky);
+  dsky = MAX (dsky, dSky);
+  tmin = tmin * dsky;
+
+  mprint (0, "gain is %f, median sky is %f, min threshold is %f\n",
+	  eperdn, sky, tmin);
+
+  /* set starting threshold */
+  tmp = pow (2.0, tfac);
+  tmax = 0.5*itop;
+  *Nit  = log (tmax / tmin) / log (tmp);
+  *factor = pow ((tmax / tmin), (1.0 / *Nit));
+
+  return (tmax);
+
+}
+
+/* we are going to evenly divide the range from 
+   sky + tmin*dsky to 0.5*itop in slices with ratios 
+   close to tfac */
+
+/* images with very messy backgrounds should not be pushed to the 
+   absolute minimum.  We let the sky sigma be the max of the 
+   formal and the measured sigmas */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/shape.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/shape.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/shape.c	(revision 22322)
@@ -0,0 +1,267 @@
+# include "gophot.h"
+
+# define ZERO_SHADOW {shadow[i][0] = 0; \
+	              shadow[i][1] = 0; \
+	              shadow[i][4] = 1; \
+                      shadow[i][5] = 0; \
+                      shadow[i][6] = 1; }
+
+/* try 7 par fit for each object, decide on object types */
+shape () {
+
+  bool notnuff, gotfaint, verybig, offp, converge;
+
+  /* watch twofpar - i think we only need 2 entries, for storage in this loop */
+  float star[NPMAX], err[NPMAX], star1[NPMAX], star2[NPMAX];
+  float sky, galchi, dx, dy, starchi;
+  int i, j, n, k, ix, iy, jmtype, nsprev;
+
+  test7 = TRUE;
+  nsprev = nstot;
+
+  for (i = 0; i < nsprev; i++) {
+    mprint (3, " determining shape for object no. %d\n", i);
+
+    gotfaint = FALSE;
+    fixxy = imtype[i] >= 100;
+    jmtype = (fixxy) ? imtype[i] - 100 : imtype[i];
+	   
+    switch (jmtype) {
+    case 7:
+      mprint (3, "too faint, skipping object %d at %f, %f\n", i, starpar[i][2], starpar[i][3]);
+      continue;
+      break;
+    case 4:
+    case 9:
+      mprint (3, "nonconverge, skipping object %d at %f, %f\n", i, starpar[i][2], starpar[i][3]);
+      continue;
+      break;
+    case 8:
+    case 18:
+    case 6:
+    case 16:
+      mprint (3, "poor obj, skipping object %d at %f, %f\n", i, starpar[i][2], starpar[i][3]);
+      ZERO_SHADOW;
+      continue;
+      break;
+    }
+
+    if (shadow[i][0] == 0) {
+      sky = guess2 (star, starpar[i], &ix, &iy);
+    } else {
+      sky = guess2 (star, shadow[i], &ix, &iy);
+    }
+
+    addstar (starpar[i], ADD, jmtype);
+
+    nrect[1] = irect[1];
+    nrect[2] = irect[2];
+    fillerup (ix, iy, FALSE);
+    notnuff = (npt < enuff7*irect[1]*irect[2]);
+
+    if (notnuff) {
+      mprint (3, "obj %d, npts %d, %d & %d - skipping star: not enough pixels for 7-parm fit \n", i, npt, ix, iy);
+      if (jmtype != 2) jmtype = 5;
+      if (jmtype == 12) beta4 = 0.01;
+      addstar (starpar[i], SUB, jmtype);
+      beta4 = 1.0;
+      imtype[i] = fixxy ? jmtype + 100 : jmtype;
+      continue;
+    }
+
+    if (fixxy) {
+      gotfaint = !transmask (ix, iy, sky);
+      if (gotfaint) { 
+	if (jmtype != 3) jmtype = 7;
+	mprint (3, "obj %d, npts %d, %d & %d - skipping star: too faint for 7 par fit\n", i, npt, ix, iy);
+	addstar (starpar[i], SUB, jmtype);
+	imtype[i] = fixxy ? jmtype + 100 : jmtype;
+	continue;
+      }
+    }
+
+    filladjust (star); 
+    mprint (2, "obj %d, npts %d, %d & %d\n", i, npt, ix, iy);
+
+    /* fit 'extended' to star (7 parameter fit) */
+    galchi = chisq (onestar, xs, ys, zs, dzs, npt, star, err, NFIT2, acc, parlim, nit);
+    /*
+    if (finite(galchi) && ((err[4] > 0.25) || (err[6] > 0.25))) {
+      fprintf (stderr, "7 errors: %d %d  %f %f   %f %f   %f\n", ix, iy, err[2], err[3], err[4], err[6], galchi/npt);
+      fprintf (stderr, "                 %f %f   %f %f\n", star[2], star[3], star[4], star[6]);
+    }
+    */
+    if (fabs(star[5]) >= 1.0 / sqrt(fabs(star[4]*star[6]))) {
+      mprint (3, "hyperbola!  problem with %d at %f %f\n", i, starpar[i][2], starpar[i][3]);
+      /* just a warning... */
+    }
+    if ((star[4] > SQ(0.25*nrect[1])) || (star[6] > SQ(0.25*nrect[1]))) {
+     mprint (3, "warning - dubious fit for %d at %f %f\n", i, starpar[i][2], starpar[i][3]);
+      /* just a warning... */
+    } 
+    if (!finite (galchi)) {
+      /* shadow, starpar keep 4-par fit */
+      mprint (3, "non-converge object %d at %f %f\n", i, starpar[i][2], starpar[i][3]);
+      if (jmtype != 3) jmtype = 9;
+      mprint (3, "obj %d, npts %d, %d & %d .... failed to converge\n", i, npt, ix, iy);
+      addstar (starpar[i], SUB, jmtype);
+      imtype[i] = fixxy ? jmtype + 100 : jmtype;
+      continue;
+    }
+    
+    /* is new fit position reasonable? */
+    if (centertest (star, xs, ys, npt) || (fabs(star[2]) > 0.33*nrect[1]) || (fabs(star[3]) > 0.33*nrect[2])) {
+      mprint (3, "fit center moved 2: %f %f  %f %f\n", starpar[i][2], starpar[i][3], star[2], star[3]);
+      jmtype = 16;
+      /* shadow, starpar keep 4-par fit */
+      addstar (starpar[i], SUB, jmtype);
+      imtype[i] = fixxy ? jmtype + 100 : jmtype;
+      continue;
+    }
+      
+    /* calculate error here for type 2, 10. redone in improve for 1, 3, 5, 7 */
+    apple[i][4] = MIN (1, 1.086*sqrt(err[1])/star[1]);
+    rchisq[i] = galchi / npt;
+
+    /* save 7-par fit in shadow */
+    parupd (star, shadow[i], ix, iy);
+    for (j = 0; j < NPAR; j++) shaderr[i][j] = err[j];
+	
+    /* allow type 10 to go back to 1 and vice-versa */
+    if ((jmtype == 10) && !verybright (starpar[i])) jmtype = 1;
+    if (verybright (starpar[i])) jmtype = 10;
+
+    if (jmtype == 10) {
+      mprint (3, " very bright, keep 7 par fit only\n");
+
+      if ((thresh < tmax / 4) && (thresh > tmax/16)) {
+	/* test if type 10 can be safely split in two */
+	starchi = twofit (star, starpar[i], star1, star2);
+	/* has to be a really significant improvement */
+	if (finite(starchi) && (starchi/galchi < 10*stograt) && (starchi < 10*npt)) {
+	  mprint (3, " result-> a split star: gal-chi: %f, star-chi: %f\n", galchi, starchi);
+	  jmtype = 3;
+	  parupd (star1, shadow[i], ix, iy);
+	  parupd (star1, starpar[i], ix, iy);
+	  addstar (starpar[i], SUB, jmtype);
+	  imtype[nstot] = 3;
+	  parupd (star2, starpar[nstot], ix, iy);
+	  parupd (star2, shadow[nstot], ix, iy);	
+	  addstar (starpar[nstot], SUB, jmtype);
+	  nstot ++;
+	  continue;
+	}
+      }
+      parupd (star, starpar[i], ix, iy);
+
+      if ((thresh < tmax / 4) && (thresh > tmax/16)) {
+
+	float err2[NPMAX], galchi2;
+
+	beta4 = 0.01;
+	for (n = 0; n < NPMAX; n++) star2[n] = star[n];
+	galchi2 = chisq (onestar, xs, ys, zs, dzs, npt, star2, err2, NFIT2, acc, parlim, nit);
+	if (1.2*galchi2 < galchi) {
+	  jmtype = 12;
+	  parupd (star2, starpar[i], ix, iy);
+	  mprint (3, "extended: %f vs %f  %f %f  %f -   %d\n", galchi/npt, galchi2/npt, starpar[i][2], starpar[i][3], star[1], jmtype);
+	}
+	beta4 = 1.0;
+      }
+
+      addstar (starpar[i], SUB, jmtype);
+      imtype[i] = fixxy ? jmtype + 100 : jmtype;
+      continue;
+    }
+      
+    verybig = galaxy (star, shaderr[i], starpar[i]);
+    if (jmtype == 3) verybig = verybig && (chi[4] > xtra);
+
+    /* shouldn't this test come earlier? */
+    converge = finite(galchi);
+    offp = offpic (star, ix, iy, &dx, &dy);
+    verybig = verybig && !offp && converge;;
+
+    if (!verybig || fixxy) {
+      if (jmtype != 3) jmtype = 1;
+      if (!converge) {
+	if (jmtype != 3) jmtype = 9;
+	mprint (3, "obj %d, npts %d, %d & %d .... failed to converge\n", i, npt, ix, iy);
+	addstar (starpar[i], SUB, jmtype);
+	imtype[i] = fixxy ? jmtype + 100 : jmtype;
+	continue;
+      }
+      if (offp) {
+	jmtype = 9;
+	imtype[i] = fixxy ? jmtype + 100 : jmtype;
+	mprint (3, "obj %d, npts %d, %d & %d .... fit center outside fit subraster\n", i, npt, ix, iy);
+      }
+      addstar (starpar[i], SUB, jmtype);
+      imtype[i] = fixxy ? jmtype + 100 : jmtype;
+      continue;
+    }
+	   
+    mprint (3, "obj %d, npts %d, %d & %d .... is very big....\n", i, npt, ix, iy);
+    mprint (3, ".... testing galaxy vs. double-star \n");
+    
+    starchi = twofit (star, starpar[i], star1, star2);
+
+    if (finite(starchi) && (starchi/galchi < stograt)) {
+      mprint (3, " result-> a split star: gal-chi: %f, star-chi: %f\n", galchi, starchi);
+      jmtype = 3;
+      parupd (star1, shadow[i], ix, iy);
+      parupd (star1, starpar[i], ix, iy);
+      addstar (starpar[i], SUB, jmtype);
+      imtype[nstot] = 3;
+      parupd (star2, starpar[nstot], ix, iy);
+      parupd (star2, shadow[nstot], ix, iy);	
+      addstar (starpar[nstot], SUB, jmtype);
+      nstot ++;
+    } else {
+      mprint (3, " result-> a galaxy: gal-chi: %f, star-chi: %f\n", galchi, starchi);
+      jmtype = 2;
+      parupd (star, starpar[i], ix, iy);
+      if (star[1]*star[4]*star[6]*ufactor > 20000) {
+	float err2[NPMAX], galchi2;
+
+	beta4 = 0.01;
+	for (n = 0; n < NPMAX; n++) star2[n] = star[n];
+	galchi2 = chisq (onestar, xs, ys, zs, dzs, npt, star2, err2, NFIT2, acc, parlim, nit);
+	if (1.2*galchi2 < galchi) {
+	  jmtype = 12;
+	  parupd (star2, starpar[i], ix, iy);
+	}
+	/* fprintf (stderr, "extended: %f vs %f  %f %f  %f -   %d\n", galchi/npt, galchi2/npt, starpar[i][2], starpar[i][3], star[1], jmtype);
+	 */
+	beta4 = 1.0;
+      }
+      addstar (starpar[i], SUB, jmtype);
+    }
+    imtype[i] = fixxy ? jmtype + 100 : jmtype;
+  }
+  test7 = FALSE;
+}
+
+
+
+/*****************
+
+  This routine seems really poorly written.  there is too much obfuscation.
+
+  we just need to do a few things:
+
+  1) do we try this object with 7-par fit? (if no, continue)
+
+  2) fit the object with 7-par fit
+
+  3) did it succeed? (if no, set some types)
+
+  4) should it be a type 10? (ie, very bright)
+
+  5) should it be a type 2? (ie, significantly extended)
+
+  6) should if be a type 3? (ie, split in two)
+
+  7) should it be a type 12? (ie, hubble fit)
+
+******************/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/skyfun_plane.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/skyfun_plane.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/skyfun_plane.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "gophot.h"
+
+# define HALF 0.5
+
+float skyfun_plane (int ix, int iy, float *a, float *fa) {
+	
+  int i;
+  float value;
+
+  fa[0] = 1.0;
+  fa[1] = HALF*(ix - HALF*nfast)/(HALF*nfast);
+  fa[2] = HALF*(iy - HALF*nslow)/(HALF*nslow);
+
+  value = 0;
+  for (i = 0; i < 1; i++) {
+    value += a[i]*fa[i];
+  }
+
+  return (value);
+}
+
+/* this function uses C 0,N-1 for a[], fa[] */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/toobright.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/toobright.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/toobright.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "gophot.h"
+
+bool toobright (float *star) {
+
+  bool value;
+  bool flag;
+  int jrect[5];
+  int nsat, nbad, ix, iy, ixhi, ixlo, iyhi, iylo, jx, jy;
+  float dum, duma[NPMAX];
+
+  value = FALSE;
+	
+  if (star[1] < itop/4) return (FALSE);
+
+  nsat = 0;
+  nbad = 0;
+  dum = guess2 (duma, star, &ix, &iy);
+  ixhi = MIN ((int)(ix + krect[1]/2 + 0.5), nfast - 1);
+  ixlo = MAX ((int)(ix - krect[1]/2 + 0.5), 0);
+  iyhi = MIN ((int)(iy + krect[2]/2 + 0.5), nslow - 1);
+  iylo = MAX ((int)(iy - krect[2]/2 + 0.5), 0);
+
+  for (jy = iylo; jy <= iyhi; jy++) {
+    for (jx = ixlo; jx <= ixhi; jx++) {
+      if (!finite (noise[jx+jy*nfast])) nbad ++;
+      if (big[jx+jy*nfast] >= itop) nsat ++;
+    }
+  }
+
+  value = (star[1] > cmax) || (nsat >= icrit) || (nbad >= icrit);
+
+  mprint (3, "object at: %d, %d - %d:  %d %d %f %f %f\n", ix,iy, value, nsat, nbad, star[1], big[ix+iy*nfast], noise[ix+iy*nfast]);
+
+  if (value) {
+    if (nsat >= icrit)  star[1] = ctpersat*nsat;
+    
+    oblims(star,jrect);
+    
+    star[4] = jrect[2] - jrect[1];
+    star[5] = nsat;
+    star[6] = jrect[4] - jrect[3];
+    
+    mprint (0, "%d, %d funny pixels in object at %d, %d (%f peak)\n", nsat, nbad, ix, iy, star[1]);
+  }
+
+  return (value);
+}
+
+bool verybright (float *star) {
+
+  float flux;
+
+  /* rough scaling to compare fits with different scale */
+
+  flux = star[1]*sqrt((star[4]*star[6])/(ava[4]*ava[6]));;
+
+  if (flux > cmax) {
+    return (TRUE);
+  } else {
+    return (FALSE);
+  }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/toofaint.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/toofaint.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/toofaint.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "gophot.h"
+
+bool toofaint (float *star, float *err) {
+
+  bool value;
+  float cmin, sig2;
+
+  cmin = 1.0;
+  value = (star[1] < cmin);
+
+  if (err[1] > 0) {
+    sig2 = SQ(star[1]/ufactor) / err[1];
+    value = value || (sig2 < crit7);
+    mprint (3, "sig2 = %f (%f %f %f)\n", sig2, star[1], ufactor, err[1]);
+    mprint (3, "value, crit7: %d, %f\n", value, crit7);
+  }
+
+  return (value);
+
+}
+
+/* this function uses C 0,N-1 for a[], fa[] */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/transmask.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/transmask.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/transmask.c	(revision 22322)
@@ -0,0 +1,94 @@
+# include "gophot.h"
+
+bool transmask (int ix, int iy, float sky) {
+
+  bool value;
+  int i, j, ii, jj;
+  float tmp, tmp0, tmp1, tmp2, ratio, hump2;
+  float tmpB, tmpN, df;
+  float *Bval, *Nval, cval, tmp3;
+
+  value = FALSE;
+
+  if (test7) 
+    hump2 = crit7;
+  else
+    hump2 = SQ (bumpcrit);
+
+  tmp0 = tmp1 = tmp2 = 0;
+  tmpB = tmpN = tmp3 = 0;
+
+  cval = big[ix + iy*nfast];
+
+  jj = iy - iyby2;
+  for (j = 0; j < 2*iyby2 + 1; j++, jj++) {
+    if (jj < 0) continue;
+    if (jj > nslow - 1) continue;
+    
+    ii = ix - ixby2;
+    Bval = &big[ii + jj*nfast];
+    Nval = &noise[ii + jj*nfast];
+    for (i = 0; i < 2*ixby2 + 1; i++, ii++, Bval++, Nval++) {
+      if (ii < 0) continue;
+      if (ii > nfast - 1) continue;
+      if (!finite (*Nval)) continue;
+
+# if (1)
+      tmp0 ++;
+      tmp = starmask[i][j] / (*Nval + *Bval);
+      tmp1 += tmp*starmask[i][j];
+      tmp2 += tmp*(*Bval-sky);
+      
+# else
+
+      tmp0 ++;
+      tmp1 += (*Nval + *Bval);
+      tmp2 += (*Bval - sky);
+
+      df = (cval - *Bval);
+      tmp3 +=  df * fabs(df) / (*Nval + *Bval);
+
+# endif
+
+      tmpB += (*Bval - sky);
+      tmpN += (*Nval - rnoise);
+    }
+  }
+
+  if (tmp0 == 0) return (FALSE);
+
+  if (tmpB < tmpN) return (FALSE);  
+
+  if (tmp2 > 0) {
+    ratio = tmp2*tmp2/tmp1;
+
+# if (1)
+    if (ratio > hump2) {
+      value = TRUE;
+      mprint (3, " trigger 1 on new object: %d, %d,  %f %f   %f %f %f\n", ix, iy, ratio, tmp3, sky, tmp2, tmp1);
+      if (ratio < 1.1*hump2) mprint (3, "marginal: (s/n)**2 through mask = %f\n", ratio);
+    }
+# else
+    if ((ratio > hump2) && (tmp3 > 0.25*ratio)) {
+      value = TRUE;
+      mprint (3, " trigger 1 on new object: %d, %d,  %f %f   %f %f %f\n", ix, iy, ratio, tmp3, sky, tmp2, tmp1);
+      if (ratio < 1.1*hump2) mprint (3, "marginal: (s/n)**2 through mask = %f\n", ratio);
+    }
+# endif
+
+  }
+
+  if (value) mprint (3, " trigger 2 on new object: %d, %d\n", ix, iy);
+
+  return (value);
+
+}
+
+
+  /* t1 = 1/N
+     t2 = d/N
+     rat = N d^2 / N^2 = d^2 / N
+  */
+
+/* does not check if bump is significant in region, 
+   only if mask region is significant over sky */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/twofit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/twofit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/gophot/src/twofit.c	(revision 22322)
@@ -0,0 +1,127 @@
+# include "gophot.h"
+
+/****************************************
+ there are big problems here with the values of b and probably 
+ other things.  fix please! */
+
+/* we are fitting two stars at the location of the input star */
+/* starraw has the real X,Y coords, star is relative to centroid */
+
+float twofit (float *star, float *starraw, float *star1, float *star2) {
+
+  int i;
+  float b[2*NPMAX], err[2*NPMAX], bacc[2*NPMAX], bparlim[2*NPMAX], tstar[NPMAX];
+  bool badfit, conv;
+  float a5, a7, angle, root, root1, root2, dx2, dy2, dx, dy, value;
+  float garea, sarea, dx74, dy74, dot, fac1, fac2, dxmax, dymax;
+
+  badfit = FALSE;
+  conv = TRUE;
+
+  /* fills in values 1, 5, 6, 7, 8 */
+  parinterp (starraw[2], starraw[3], tstar);
+
+  /* this all seems way too obtuse. */
+  a5 = 1.0/star[4];
+  a7 = 1.0/star[6];
+  angle = atan2 (-2*star[5], a7 - a5)/2.0;
+  root = sqrt (SQ(a5 - a7) + 4*SQ(star[5]));
+  root1 = (a5 + a7 + root)/2.0;
+  root2 = root1 - root;
+  dx2 = star[4] - tstar[4];
+  dy2 = star[6] - tstar[6];
+  dx = sqrt (MAX (dx2, 0.0)) / 2.0;
+  dy = sqrt (MAX (dy2, 0.0)) / 2.0;
+  badfit = (MAX (dx, dy) == 0);
+  /* we can use these since dx & dy are always >= 0 */
+  dx = (cos(angle) < 0) ? -dx : dx;
+  dy = (sin(angle) < 0) ? -dy : dy;
+  garea = 1.0 / sqrt (fabs (root1*root2));
+  sarea = sqrt (fabs (tstar[4]*tstar[6]));
+  dx74 = starraw[2] - star[2];	
+  dy74 = starraw[3] - star[3];		
+  dot = dx74*dx + dy74*dy;
+  if (dot > 0) {
+    fac1 = 0.6666666;
+    fac2 = 1.3333333;
+  } else {
+    fac1 = 1.3333333;
+    fac2 = 0.6666666;
+  }
+	   
+  /* 
+     b[0] - sky
+     b[1] - I1
+     b[2], b[3] - X1, Y1
+     b[4] - I2
+     b[5], b[6] - X2, Y2
+     b[7], b[8], b[9] - shape 
+  */
+
+  b[0] = star[0];
+  b[1] = (star[1]*garea*fac2 / (sarea*2));
+  b[2] = star[2] - dx*fac1;
+  b[3] = star[3] - dy*fac1;
+  b[4] = (star[1]*garea*fac1 / (sarea*2));		
+  b[5] = star[2] + dx*fac2;
+  b[6] = star[3] + dy*fac2;
+
+  /* for reasons unclear, the fit is done in ln(Io), not Io */
+  b[1] = log(b[1]);
+  b[4] = log(b[4]); 
+
+  b[7] = tstar[4];
+  b[8] = tstar[5];
+  b[9] = tstar[6];
+
+  bacc[0] = acc[0];
+  bacc[1] = bacc[4] = acc[1];
+  bacc[2] = bacc[5] = acc[2];
+  bacc[3] = bacc[6] = acc[3];
+
+  bparlim[0] = parlim[0];
+  bparlim[1] = bparlim[4] = parlim[1];
+  bparlim[2] = bparlim[5] = parlim[2];
+  bparlim[3] = bparlim[6] = parlim[3];
+
+  dxmax = MAX (fabs(b[2]), fabs(b[5]));
+  dymax = MAX (fabs(b[3]), fabs(b[6]));
+  badfit = badfit || (dxmax > irect[1]/2.0);
+  badfit = badfit || (dymax > irect[2]/2.0);
+
+  if (!badfit) {
+    value = chisq (twostar, xs, ys, zs, dzs, npt, b, err, NFIT2, bacc, bparlim, 2*nit);
+    conv = finite (value);
+  }
+	
+  dxmax = MAX (fabs(b[2]), fabs(b[5]));
+  dymax = MAX (fabs(b[3]), fabs(b[6]));
+  badfit = badfit || (dxmax > 0.4*irect[1]);
+  badfit = badfit || (dymax > 0.4*irect[2]);
+
+  if (conv) {
+    if (b[1] < b[4]) {
+      SWAP (b[1], b[4]);
+      SWAP (b[2], b[5]);
+      SWAP (b[3], b[6]);
+    }
+    
+    star1[0] = star2[0] = b[0];
+    star1[4] = star2[4] = b[7];
+    star1[5] = star2[5] = b[8];
+    star1[6] = star2[6] = b[9];
+    star1[1] = exp(b[1]);
+    star1[2] = b[2];
+    star1[3] = b[3];
+    star2[1] = exp(b[4]);
+    star2[2] = b[5];
+    star2[3] = b[6];
+  }
+
+  badfit = badfit || centertest (star1, xs, ys);
+  badfit = badfit || centertest (star2, xs, ys);
+  if (!conv || badfit) value = MAGIC;
+
+  return (value);
+}
+/* this function uses C 0,N-1 for a[], fa[] */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/Makefile	(revision 22322)
@@ -0,0 +1,32 @@
+default: imclean
+help:
+	@echo "make options: imclean (default)"
+
+include ../../Makefile.System
+HOME    =       $(ROOT)/src/imclean
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+imclean: $(BIN)/imclean.$(ARCH)
+install: $(DESTBIN)/imclean
+
+IMCLEAN = \
+$(SRC)/imclean.$(ARCH).o	$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/args.$(ARCH).o		$(SRC)/sort_stars.$(ARCH).o \
+$(SRC)/AdjustHeader.$(ARCH).o	$(SRC)/find_group.$(ARCH).o \
+$(SRC)/find_trails.$(ARCH).o	$(SRC)/find_line.$(ARCH).o \
+$(SRC)/wstars.$(ARCH).o	        $(SRC)/LoadStarsDophot.$(ARCH).o \
+$(SRC)/LoadStarsSex.$(ARCH).o	$(SRC)/LoadStarsChad.$(ARCH).o	\
+$(SRC)/wfits.$(ARCH).o
+
+$(IMCLEAN): $(INC)/imclean.h
+$(BIN)/imclean.$(ARCH): $(IMCLEAN)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,12 @@
+
+- imclean-1-3 : 2006.08.23
+  * converted to gfits APIs (forces libfits 1.6)
+
+2005.10.20: imclean-1-2
+	minor changes to fix -Wall errors
+	fixed exit status
+	synched with libohana (v1.5) / libfits (v1.4) changes
+
+2005.07.06: imclean-1-1
+	some changes to use the autocoded version of the SMPData structure (was Stars)
+	support for both fits and text output formats
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/doc/VERSIONS
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/doc/VERSIONS	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/doc/VERSIONS	(revision 22322)
@@ -0,0 +1,10 @@
+
+tag names used by imclean:
+
+AG         : Comment
+imclean-1-0 : first version under CVS
+
+USES
+libfits-1-0
+libohana-1-0
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/include/imclean.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/include/imclean.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/include/imclean.h	(revision 22322)
@@ -0,0 +1,50 @@
+# include <ohana.h>
+# include <dvo.h>
+
+enum {DOPHOT, CHAD, SEXTRACT};
+
+int    MODE;
+int    FITS_OUTPUT;
+int    VERBOSE;
+int    RESET;
+int    FORCE_RUN;
+int    PROVIDE_ASTROM;
+int    NEWPHOTCODE;
+
+/* global variables set in parameter file */
+char   *PHOTCODE;
+char   PhotCodeFile[256];
+char   AstromFile[256];
+
+double DEFAULT_ERROR;
+double RADIUS;
+double TRAIL_WIDTH;
+int    NBINS;
+int    NPTSINLINE;
+double MIN_DENSITY;
+double NSIGMA;
+
+double RA, DEC, ZERO_POINT, MIN_SN_FSTAT;
+int CHAR_LINE, TYPE_FIELD, AP_FIELD, PSF_FIELD, HEADER_COORDS;
+
+int FIX_KEYWORD;
+char **KEYWORD, **KEYVALU, **KEYFMT;
+
+SMPData *LoadStarsDophot (char *filename, int *nstars, Header *header);
+SMPData *LoadStarsChad (char *filename, int *nstars, Header *header);
+SMPData *LoadStarsSex (char *filename, int *nstars, Header *header);
+
+void ConfigInit (int *argc, char **argv);
+void AdjustHeader (Header *header);
+void sort_stars (SMPData *X, int N);
+void find_trails (SMPData *stars, int Nstars);
+int find_group (SMPData *stars, char *mark, int Npts, int i, double *ANGLE);
+void wstars (char *filename, SMPData *stars, int Nstars, Header *header);
+
+void fix_total (SMPData *stars, int Nstars, Header *header);
+int find_line (SMPData *stars, char *mark, int Npts, int i, double *M, double *B, double Angle);
+void find_better_line (SMPData *stars, char *mark, int Npts, int i, double *M, double *B, int axis);
+
+void help ();
+void args (int argc, char **argv);
+void wfits (char *filename, SMPData *stars, int Nstars, Header *header);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/AdjustHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/AdjustHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/AdjustHeader.c	(revision 22322)
@@ -0,0 +1,101 @@
+# include "imclean.h"
+
+void AdjustHeader (Header *header) {
+
+  int i;
+  double value;
+  char line[256];
+
+  if (FIX_KEYWORD) {
+    for (i = 0; i < FIX_KEYWORD; i++) {
+      if (!strcmp (KEYFMT[i], "%f")) {
+	value = atof (KEYVALU[i]);
+	gfits_modify (header, KEYWORD[i], "%le", 1, value);
+      } else {
+	gfits_modify (header, KEYWORD[i], "%s", 1, KEYVALU[i]);
+      }
+    }
+  }
+
+  /* validating the photcode name should be the job of DVO/addstar */
+  /* here we are only writing the selected photcode name to the header */
+  if (NEWPHOTCODE) {
+    gfits_modify (header, "PHOTCODE", "%s", 1, PHOTCODE);
+  }    
+
+  if (!HEADER_COORDS) {
+    int rh, rm, dd, dm;
+    float rs, ds;
+    RA = RA / 15.0;
+    rh = RA; /* rh is int */
+    rm = 60.0 * (RA - rh);
+    rs = 3600 * (RA - rh - rm / 60.0);
+    sprintf (line, "%02d:%02d:%05.2f", rh, rm, rs);
+    gfits_modify (header, "RA", "%s", 1, line);
+    dd = DEC;
+    dm = 60.0 * (DEC - dd);
+    ds = 3600 * (DEC - dd - dm / 60.0);
+    sprintf (line, "%02d:%02d:%05.2f", dd, dm, ds);
+    gfits_modify (header, "DEC", "%s", 1, line);
+  }
+ 
+  if (PROVIDE_ASTROM) {
+
+    Header astrom_header;
+    Coords coords;
+
+    if (!gfits_read_header (AstromFile, &astrom_header)) {
+      fprintf (stderr, "ERROR: can't get astrometry from %s\n", AstromFile);
+      exit (1);
+    }
+    if (!GetCoords (&coords, &astrom_header)) {
+      fprintf (stderr, "ERROR: no astrometric solution in header\n");
+      exit (1);
+    }
+    /*** use PutCoords to update header ***/
+    if (coords.Npolyterms > 1) {
+      gfits_modify (header, "CTYPE1",   "%s",  1, "RA---PLY");
+      gfits_modify (header, "CTYPE2",   "%s",  1, "DEC--PLY");
+    } else {
+      gfits_modify (header, "CTYPE1",   "%s",  1, "RA---TAN");
+      gfits_modify (header, "CTYPE2",   "%s",  1, "DEC--TAN");
+    }    
+    gfits_modify (header, "NASTRO",   "%d", 1, 1); 
+
+    gfits_modify (header, "CDELT1",   "%le", 1, coords.cdelt1); 
+    gfits_modify (header, "CDELT2",   "%le", 1, coords.cdelt2);
+    gfits_modify (header, "CRVAL1",   "%lf", 1, coords.crval1);
+    gfits_modify (header, "CRVAL2",   "%lf", 1, coords.crval2);  
+    gfits_modify (header, "CRPIX1",   "%lf", 1, coords.crpix1);
+    gfits_modify (header, "CRPIX2",   "%lf", 1, coords.crpix2);
+    gfits_modify (header, "PC001001", "%le", 1, coords.pc1_1);
+    gfits_modify (header, "PC001002", "%le", 1, coords.pc1_2);
+    gfits_modify (header, "PC002001", "%le", 1, coords.pc2_1);
+    gfits_modify (header, "PC002002", "%le", 1, coords.pc2_2);
+    gfits_modify (header, "NPLYTERM", "%d", 1, coords.Npolyterms);
+    if (coords.Npolyterms > 1) {
+      /* RA Terms */
+      gfits_modify (header, "PCA1X2Y0", "%le", 1, coords.polyterms[0][0]);   /* polyterms[0]); */
+      gfits_modify (header, "PCA1X1Y1", "%le", 1, coords.polyterms[1][0]);   /* polyterms[1]); */
+      gfits_modify (header, "PCA1X0Y2", "%le", 1, coords.polyterms[2][0]);   /* polyterms[2]); */
+      
+      if (coords.Npolyterms > 2) {
+	gfits_modify (header, "PCA1X3Y0", "%le", 1, coords.polyterms[3][0]);   /* polyterms[3]); */
+	gfits_modify (header, "PCA1X2Y1", "%le", 1, coords.polyterms[4][0]);   /* polyterms[4]); */
+	gfits_modify (header, "PCA1X1Y2", "%le", 1, coords.polyterms[5][0]);   /* polyterms[5]); */
+	gfits_modify (header, "PCA1X0Y3", "%le", 1, coords.polyterms[6][0]);   /* polyterms[6]); */
+      }
+      /* Dec Terms */
+      gfits_modify (header, "PCA2X2Y0", "%le", 1, coords.polyterms[0][1]);   /* polyterms[7]); */
+      gfits_modify (header, "PCA2X1Y1", "%le", 1, coords.polyterms[1][1]);   /* polyterms[8]); */
+      gfits_modify (header, "PCA2X0Y2", "%le", 1, coords.polyterms[2][1]);   /* polyterms[9]); */
+      
+      if (coords.Npolyterms > 2) {
+	gfits_modify (header, "PCA2X3Y0", "%le", 1, coords.polyterms[3][1]);   /* polyterms[10]); */
+	gfits_modify (header, "PCA2X2Y1", "%le", 1, coords.polyterms[4][1]);   /* polyterms[11]); */
+	gfits_modify (header, "PCA2X1Y2", "%le", 1, coords.polyterms[5][1]);   /* polyterms[12]); */
+	gfits_modify (header, "PCA2X0Y3", "%le", 1, coords.polyterms[6][1]);   /* polyterms[13]); */
+      }
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "imclean.h"
+
+void ConfigInit (int *argc, char **argv) {
+  
+  char *config, *file;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  ScanConfig (config, "ZERO_PT",           "%lf", 0, &ZERO_POINT);
+  ScanConfig (config, "MIN_SN_FSTAT",      "%lf", 0, &MIN_SN_FSTAT);
+  ScanConfig (config, "DEFAULT_ERROR_FSTAT", "%lf", 0, &DEFAULT_ERROR);
+  ScanConfig (config, "DOPHOT_CHAR_LINE",  "%d", 0, &CHAR_LINE);
+  ScanConfig (config, "DOPHOT_TYPE_FIELD", "%d", 0, &TYPE_FIELD);
+  ScanConfig (config, "DOPHOTF_FIELD",  "%d", 0, &PSF_FIELD);
+  ScanConfig (config, "DOPHOT_AP_FIELD",   "%d", 0, &AP_FIELD);
+  ScanConfig (config, "PHOTCODE_FILE",     "%s", 0, PhotCodeFile);
+
+  /* unique to markstar */
+  ScanConfig (config, "SEARCH_RADIUS",   "%lf", 0, &RADIUS);
+  ScanConfig (config, "TRAIL_WIDTH",     "%lf", 0, &TRAIL_WIDTH);
+  ScanConfig (config, "NANGLE_BINS",     "%d",  0, &NBINS);
+  ScanConfig (config, "NPTSINLINE",      "%d",  0, &NPTSINLINE);
+  ScanConfig (config, "MIN_DENSITY",     "%lf", 0, &MIN_DENSITY);
+  ScanConfig (config, "SPACE_SIGMA",     "%lf", 0, &NSIGMA); 
+
+  free (config);
+  free (file);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/LoadStarsChad.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/LoadStarsChad.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/LoadStarsChad.c	(revision 22322)
@@ -0,0 +1,133 @@
+# include "imclean.h"
+# define NBLOCK 100
+# define HIST_BINS 150
+
+SMPData *LoadStarsChad (char *filename, int *nstars, Header *header) {
+
+  FILE *f;
+  SMPData *stars;
+  int NSTARS, Nstars, i, N;
+  int status;
+  double x, y, m, sky, lsky;
+  char *buffer;
+  int Mhist[HIST_BINS], Shist[HIST_BINS], bin, sum;
+  double FWHMx, FWHMy, angle, flux;
+  int satfound, done;
+  double saturate, complete;
+  char line[256];
+
+  ALLOCATE (buffer, char, CHAR_LINE*NBLOCK);
+
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't find object file %s\n", filename);
+    exit (1);
+  }
+
+  /* zero things that will sum */
+  for (i = 0; i < HIST_BINS; i++) { Mhist[i] = Shist[i] = 0; }
+  
+  N = 0;
+  Nstars = 0;
+  NSTARS = 500;
+  ALLOCATE (stars, SMPData, NSTARS);
+
+  /* for now assume file 'header' is fixed-format */
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  if (strncasecmp (line, "#seeing", 7)) {
+    fprintf (stderr, "error in header, skipping\n");
+    exit (1);
+  }
+  sscanf (line, "%*s %lf", &FWHMx);
+  FWHMy = FWHMx;
+  angle = 0;
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+
+  /* read in data from obj file */
+  /* data is not fixed format for lines, read each line one-at-a-time */
+  for (i = 0; (status = fscanf (f, "%lf %lf %*f %*f %lf %lf %*f", &x, &y, &sky, &flux)) != EOF; i++) {
+
+    if (status != 4) {
+      fprintf (stderr, "format error in file %s, line %d\n", filename, i);
+      continue;
+    }
+
+    if (flux <= 0) continue;
+    m = -2.5*log10 (flux);
+
+    if (sky < 1.0) {
+      lsky = 0.0;
+    } else {
+      lsky = log10(sky);
+    }
+
+    bin = MAX (0.0, MIN (HIST_BINS, 10.0 * (m + 15.0)));  /* stick in 0.1 mag bins */
+    Mhist[bin] ++;
+
+    m = MIN (50.0, m);
+    m = MAX (-24.0, m);
+
+    stars[Nstars].fx = 0;
+    stars[Nstars].fy = 0;
+    stars[Nstars].df = 0;
+    stars[Nstars].Mgal = 50.0;;
+    stars[Nstars].Map = 50.0;
+    stars[Nstars].X = x;
+    stars[Nstars].Y = y;
+    stars[Nstars].M = m;
+    stars[Nstars].dM = 0.01;
+    stars[Nstars].dophot = 1;
+    stars[Nstars].sky = lsky;
+    Nstars++;
+    if (Nstars == NSTARS - 1) {
+      NSTARS += 500;
+      REALLOCATE (stars, SMPData, NSTARS);
+    }
+  }
+
+  /* look at histogram, find saturation and completion limits */
+  sum = 0;
+  for (i = 0; i < HIST_BINS; i++) {
+    sum += Mhist[i];
+    Shist[i] = sum;
+  }
+  satfound = done = FALSE;
+  saturate = complete = 0.0;
+  for (i = 0; (i < HIST_BINS) && !done; i++) {
+    if ((!satfound) && (Mhist[i] > 0)) {
+      saturate = 0.1*(i-1) - 15.0;
+      satfound = TRUE;
+    }
+    if (Shist[i] > 0.9*Shist[HIST_BINS - 1]) {
+      complete = 0.1*i - 15.0;
+      done = TRUE;
+    }
+  }
+  
+  gfits_modify (header, "ZERO_PT", "%lf", 1, ZERO_POINT);
+  gfits_modify (header, "FWHM_X", "%lf", 1, FWHMx);
+  gfits_modify (header, "FWHM_Y", "%lf", 1, FWHMy);
+  gfits_modify (header, "ANGLE", "%lf", 1, angle);
+  gfits_modify (header, "FSATUR", "%lf", 1, (saturate + ZERO_POINT));
+  gfits_modify (header, "FLIMIT", "%lf", 1, (complete + ZERO_POINT));
+  gfits_modify (header, "NSTARS", "%d", 1, N);
+  for (i = 1; i <= 9; i++) {
+    sprintf (line, "TDOPHOT%1d", i);
+    gfits_modify (header, line, "%d", 1, 0);
+  }
+
+  *nstars = Nstars;
+  return (stars);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/LoadStarsDophot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/LoadStarsDophot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/LoadStarsDophot.c	(revision 22322)
@@ -0,0 +1,176 @@
+# include "imclean.h"
+# define NBLOCK 100
+# define HIST_BINS 300 
+/* the mag histogram has range 0.0 to 30.0 */
+/* m = 0.1*Mhist[i] */
+
+SMPData *LoadStarsDophot (char *filename, int *nstars, Header *header) {
+
+  FILE *f;
+  SMPData *stars;
+  int NSTARS, Nstars, i, Nline, N;
+  int type, status;
+  double x, y, m, dm, sky, lsky, fx, fy, df, Mgal, Map;
+  char *buffer;
+  int Mhist[HIST_BINS], Shist[HIST_BINS], n[20], bin, sum;
+  double FWHMx, FWHMy, angle;
+  int satfound, done;
+  double saturate, complete;
+  char line[256];
+  int MedHist[2002], NMedHist;
+  double SMedHist, dMed;
+
+  N = NMedHist = 0;
+  bzero (MedHist, 2002*sizeof(int));
+
+  ALLOCATE (buffer, char, CHAR_LINE*NBLOCK);
+
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't find object file %s\n", filename);
+    exit (1);
+  }
+
+  /* zero things that will sum */
+  for (i = 0; i < HIST_BINS; i++) { Mhist[i] = Shist[i] = 0; }
+  for (i = 1; i <= 9; i++) { n[i] = 0; }
+
+  Nstars = 0;
+  NSTARS = 500;
+  ALLOCATE (stars, SMPData, NSTARS);
+
+  /* read average values from first line */
+  scan_line (f, line);
+  sscanf (line, "%*s %*s %*s %lf %lf %lf", &FWHMx, &FWHMy, &angle);
+
+  /* read in data from obj file */
+  while ((Nline = fread (buffer, CHAR_LINE, NBLOCK, f)) > 0) {
+    for (i = 0; i < Nline; i++) {
+      /* we are now using all entries on the *.obj line */
+      status = sscanf (&buffer[i*CHAR_LINE], "%d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", 
+		       &type, &x, &y, &m, &dm, &sky, &fx, &fy, &df, &Mgal, &Map);
+      if (status != 11) {
+	fprintf (stderr, "format error in file %s, line %d\n", filename, i);
+	continue;
+      }
+
+      /* dophot magnitudes can range from 99.999 to -99.999 
+	 realistic numbers are between -20 and 0
+	 outside, we should set the value to 50.0 to force saturation */
+      
+      n[type] ++;
+      if (type == 6) continue;
+      if (type == 9) continue;
+      if (type == 8) continue;
+      if (type == 16) continue;
+      
+      if (m > 0) continue;                /* skip stars which totally fail on fit */
+      if (dm == 0.0) dm = DEFAULT_ERROR;  /* stars with poor errors, get 25.5% errors */
+      dm = MIN (0.999, MAX (0.0, dm));    /* truncate dm to fit in range 0 - 999 on output */
+
+      /* need to accumulate the median histogram thingy */
+      dMed = Map - m;
+      if ((fabs(m) < 90) && (fabs(Map) < 90) && (fabs(dMed) < 1)) {
+	bin = 1000 * (dMed + 1);
+	MedHist[bin] ++;
+	NMedHist ++;
+      }
+      
+      /* dophot provides values which are -2.5*log(counts) */
+      m    = ((m    > -25) && (m    < 0)) ? m + ZERO_POINT    : 50.0;
+      Mgal = ((Mgal > -25) && (Mgal < 0)) ? Mgal + ZERO_POINT : 50.0;
+      Map  = ((Map  > -25) && (Map  < 0)) ? Map + ZERO_POINT  : 50.0;
+
+      if (sky < 1.0) {
+	lsky = 0.0;
+      } else {
+	lsky = log10(sky);
+      }
+
+      if (MIN_SN_FSTAT*dm > 1.0) continue;  /* skip stars with errors too large */
+          
+      switch (type) {
+      case 1:
+      case 4:
+      case 7:
+	bin = MAX (0, MIN (HIST_BINS - 1, 10.0 * m));  /* stick in 0.1 mag bins */
+	Mhist[bin] ++;
+      default:
+	if (df < 0.0) df += 360.0;
+	stars[Nstars].X      = x;
+	stars[Nstars].Y      = y;
+	stars[Nstars].M      = m;
+	stars[Nstars].Mgal   = Mgal;
+	stars[Nstars].Map    = Map;
+	stars[Nstars].dM     = dm;
+	stars[Nstars].dophot = type;
+	stars[Nstars].sky    = lsky;
+	stars[Nstars].fx     = fx;
+	stars[Nstars].fy     = fy;
+	stars[Nstars].df     = df;
+	Nstars++;
+	if (Nstars == NSTARS - 1) {
+	  NSTARS += 500;
+	  REALLOCATE (stars, SMPData, NSTARS);
+	}
+      }
+    }
+
+  }    
+
+  /* look at histogram, find saturation and completion limits */
+  sum = 0;
+  for (i = 0; i < HIST_BINS; i++) {
+    sum += Mhist[i];
+    Shist[i] = sum;
+  }
+  satfound = done = FALSE;
+  saturate = complete = 0.0;
+  for (i = 0; (i < HIST_BINS) && !done; i++) {
+    if ((!satfound) && (Mhist[i] > 0)) {
+      saturate = 0.1*(i-1);
+      satfound = TRUE;
+    }
+    if (Shist[i] > 0.9*Shist[HIST_BINS - 1]) {
+      complete = 0.1*i;
+      done = TRUE;
+    }
+  }
+  
+  SMedHist = 0;
+  for (i = 0; (i < 2002) && (SMedHist < NMedHist / 2); i++) {
+    SMedHist += MedHist[i];
+  }
+  if (i == 2002) {
+    fprintf (stderr, "error finding (Ap - Fit) median\n");
+    SMedHist = 0;
+  } else {
+    SMedHist = 0.001*i - 1;
+    fprintf (stderr, "(Ap - Fit) median = %f\n", SMedHist);
+  }
+
+  for (i = 0; i < Nstars; i++) {
+    stars[i].Mgal += SMedHist;
+    stars[i].M += SMedHist;
+  }    
+
+  gfits_modify (header, "ZERO_PT", "%lf", 1, ZERO_POINT);
+  gfits_modify (header, "FWHM_X", "%lf", 1, FWHMx);
+  gfits_modify (header, "FWHM_Y", "%lf", 1, FWHMy);
+  gfits_modify (header, "ANGLE", "%lf", 1, angle);
+  gfits_modify (header, "FSATUR", "%lf", 1, saturate);
+  gfits_modify (header, "FLIMIT", "%lf", 1, complete);
+  gfits_modify (header, "APMIFIT", "%lf", 1, SMedHist);
+  gfits_modify (header, "NSTARS", "%d", 1, N);
+  for (i = 1; i <= 9; i++) {
+    sprintf (line, "TDOPHOT%1d", i);
+    gfits_modify (header, line, "%d", 1, n[i]);
+  }
+
+  *nstars = Nstars;
+  return (stars);
+
+}
+
+/* this function should load the stars and immediately convert them to
+   have the ZERO_PT zero point */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/LoadStarsSex.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/LoadStarsSex.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/LoadStarsSex.c	(revision 22322)
@@ -0,0 +1,142 @@
+# include "imclean.h"
+# define NBLOCK 100
+# define HIST_BINS 300 
+
+/* good for sextractor */
+
+SMPData *LoadStarsSex (char *filename, int *nstars, Header *header) {
+
+  FILE *f;
+  SMPData *stars;
+  int NSTARS, Nstars, i, Nline, N;
+  int type, status;
+  double x, y, m, dm, sky, lsky, ftype;
+  char *buffer;
+  int Mhist[HIST_BINS], Shist[HIST_BINS], n[10], bin, sum, flags;
+  double A, A2, S2, FWHMx, FWHMy, angle, Mgal, Map;
+  int gotFWHM, satfound, done;
+  double saturate, complete;
+
+  CHAR_LINE = 105;
+  TYPE_FIELD = 0;
+  
+  ALLOCATE (buffer, char, CHAR_LINE*NBLOCK);
+
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't find object file %s\n", filename);
+    exit (1);
+  }
+
+  /* zero things that will sum */
+  N = A = A2 = S2 = 0;
+  gotFWHM = FALSE;
+  for (i = 0; i < HIST_BINS; i++) { Mhist[i] = Shist[i] = 0; }
+  for (i = 1; i <= 9; i++) { n[i] = 0; }
+
+  Nstars = 0;
+  NSTARS = 500;
+  ALLOCATE (stars, SMPData, NSTARS);
+
+  /* read in data from obj file */
+  while ((Nline = fread (buffer, CHAR_LINE, NBLOCK, f)) > 0) {
+    for (i = 0; i < Nline; i++) {
+      status = sscanf (&buffer[i*CHAR_LINE + TYPE_FIELD], "%lf %lf %lf %lf %lf   %lf %lf %lf %lf %lf  %lf %d", 
+		       &ftype, &x, &y, &m, &dm, &sky, &FWHMx, &FWHMy, &angle, &Mgal, &Map, &flags);
+      if (status != 12) {
+	fprintf (stderr, "ERROR: format error in file %s, line %d\n", filename, i);
+	continue;
+      } 
+
+      if (flags > 7) continue;
+      /* if (m > 0) continue; skip stars which totally fail on fit */
+      if (dm == 0.0) dm = DEFAULT_ERROR;  
+
+      /* sextract can provide values which are -2.5*log(counts) */
+      m    += ZERO_POINT;
+      Mgal += ZERO_POINT;
+      Map  += ZERO_POINT;
+      m    = MIN (32.767, MAX (-32.767, m));
+      Mgal = MIN (32.767, MAX (-32.767, Mgal));
+      Map  = MIN (32.767, MAX (-32.767, Map));
+
+      if (sky < 1) {
+	lsky = 0.0;
+      } else {
+	lsky = log10(sky);
+      }
+
+      /* type = MAX (0, MIN (9, 5*log10(ftype) + 10)); */
+      switch (flags) {
+      case 4:
+      case 5:
+      case 6:
+      case 7:
+	type = 10;
+	break;
+      case 1:
+      case 2:
+      case 3:
+	type = 3;
+	break;
+      default:
+	type = 1;
+      }
+
+      if (MIN_SN_FSTAT*dm > 1.0) continue;
+      
+      bin = MAX (0, MIN (HIST_BINS - 1, 10.0 * m));  /* stick in 0.1 mag bins */
+      Mhist[bin] ++;
+      
+      stars[Nstars].X = x;
+      stars[Nstars].Y = y;
+      stars[Nstars].M = m;
+      stars[Nstars].dM = dm;
+      stars[Nstars].dophot = type;
+      stars[Nstars].sky = lsky;
+      
+      stars[Nstars].fx = FWHMx;
+      stars[Nstars].fy = FWHMx * (FWHMy/FWHMx);
+      stars[Nstars].df = angle;
+      stars[Nstars].Mgal = Mgal;
+      stars[Nstars].Map = Map;
+
+      Nstars++;
+      if (Nstars == NSTARS - 1) {
+	NSTARS += 500;
+	REALLOCATE (stars, SMPData, NSTARS);
+      }
+    }
+
+  }    
+
+  sum = 0;
+  for (i = 0; i < HIST_BINS; i++) {
+    sum += Mhist[i];
+    Shist[i] = sum;
+  }
+  satfound = done = FALSE;
+  saturate = complete = 0.0;
+  for (i = 0; (i < HIST_BINS) && !done; i++) {
+    if ((!satfound) && (Mhist[i] > 0)) {
+      saturate = 0.1*(i-1);
+      satfound = TRUE;
+    }
+    if (Shist[i] > 0.9*Shist[HIST_BINS - 1]) {
+      complete = 0.1*i;
+      done = TRUE;
+    }
+  }
+  
+  gfits_modify (header, "ZERO_PT", "%lf", 1, ZERO_POINT);
+  gfits_modify (header, "FWHM_X", "%lf", 1, FWHMx);
+  gfits_modify (header, "FWHM_Y", "%lf", 1, FWHMy);
+  gfits_modify (header, "ANGLE", "%lf", 1, angle);
+  gfits_modify (header, "FSATUR", "%lf", 1, saturate);
+  gfits_modify (header, "FLIMIT", "%lf", 1, complete);
+  gfits_modify (header, "NSTARS", "%d", 1, N);
+
+  *nstars = Nstars;
+  return (stars);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/args.c	(revision 22322)
@@ -0,0 +1,87 @@
+# include "imclean.h"
+# define NARGS 2  /* minimum is: addstar (filename) */
+
+void help () {
+
+  fprintf (stderr, "USAGE: imclean (file.fits) (file.obj) (file.cmp)\n");
+  exit (2);
+
+}
+
+void args (int argc, char **argv) {
+  
+  int i, N;
+
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  FITS_OUTPUT = FALSE;
+  if ((N = get_argument (argc, argv, "-fits"))) {
+    FITS_OUTPUT = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  HEADER_COORDS = TRUE;
+  if ((N = get_argument (argc, argv, "-coords"))) {
+    remove_argument (N, &argc, argv);
+    RA = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    DEC = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    HEADER_COORDS = FALSE;
+  }
+
+  NEWPHOTCODE = FALSE;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    NEWPHOTCODE = TRUE;
+    remove_argument (N, &argc, argv);
+    PHOTCODE = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  PROVIDE_ASTROM = FALSE;
+  if ((N = get_argument (argc, argv, "-astrom"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (AstromFile, argv[N]);
+    remove_argument (N, &argc, argv);
+    PROVIDE_ASTROM = TRUE;
+  }
+
+  MODE = DOPHOT;
+  if ((N = get_argument (argc, argv, "-chad"))) {
+    MODE = CHAD;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-sex"))) {
+    MODE = SEXTRACT;
+    remove_argument (N, &argc, argv);
+  }
+
+  ALLOCATE (KEYWORD, char *, 64);
+  ALLOCATE (KEYVALU, char *, 64);
+  ALLOCATE (KEYFMT, char *, 64);
+  FIX_KEYWORD = 0;
+  while ((N = get_argument (argc, argv, "-key"))) {
+    i = FIX_KEYWORD;
+    remove_argument (N, &argc, argv);
+    KEYWORD[i] = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    KEYFMT[i] = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    KEYVALU[i] = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    FIX_KEYWORD ++;
+    if (FIX_KEYWORD == 64) break;
+  }
+
+
+  if (argc != 4) help ();
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/find_group.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/find_group.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/find_group.c	(revision 22322)
@@ -0,0 +1,61 @@
+# include "imclean.h"
+
+int find_group (SMPData *stars, char *mark, int Npts, int i, double *ANGLE) {
+ 
+  int j, N, bin, Ndegbin;
+  unsigned short int *A;
+  double Xo, Yo, dX, dY, angle, limit;
+
+   /* assign some parameter values */
+  if (mark[i]) return (FALSE);
+
+  Ndegbin = NBINS / 180.0;
+  ALLOCATE (A, unsigned short int, NBINS + 1);
+  bzero (A, (NBINS+1)*sizeof(short));
+
+  /* look for points concentrated in an angle bin */
+  Xo = stars[i].X;
+  Yo = stars[i].Y;
+  N = 0;
+  /* points east */
+  for (j = i + 1; (j < Npts) && ((dX = stars[j].X - Xo) < RADIUS); j++) {
+    dY = stars[j].Y - Yo;
+    if (fabs(dY) < RADIUS) {
+      angle = atan2 (dY,dX);
+      if (!finite(angle)) continue;  /* only NaN if dD = dR = 0 */
+      if (angle < 0) angle += M_PI;
+      bin = angle*DEG_RAD*Ndegbin;
+      A[bin] ++;
+      N ++;
+    }
+  }
+  /* points west */
+  for (j = i - 1; (j >= 0) && ((dX = Xo - stars[j].X) < RADIUS); j--) {
+    if (mark[j]) continue;
+    dY = stars[j].Y - Yo;
+    if (fabs(dY) < RADIUS) {
+      angle = atan2 (dY,dX);
+      if (!finite(angle)) continue;
+      if (angle < 0) angle += M_PI;
+      bin = angle*DEG_RAD*Ndegbin;
+      A[bin] ++;	
+      N ++;
+    }
+  }
+  if (N < 5) {
+    free (A);
+    return (FALSE);
+  }
+  limit = MAX (5, NSIGMA*sqrt((double)(N)/(double)(NBINS)));
+  for (j = 0; j < NBINS; j++) {
+    if (A[j] > limit) {
+      *ANGLE = j / (DEG_RAD*Ndegbin);
+      fprintf (stderr, "group: %f (%f %f)\n", *ANGLE, Xo, Yo);
+      free (A);
+      return (TRUE);
+    }
+  }
+  free (A);
+  return (FALSE);
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/find_line.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/find_line.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/find_line.c	(revision 22322)
@@ -0,0 +1,164 @@
+# include "imclean.h"
+
+int find_line (SMPData *stars, char *mark, int Npts, int i, double *M, double *B, double Angle) {
+  
+  int j, N;
+  double X, Y, X2, Y2, XY, m, b, det;
+  double dX, dY, Xo, Yo, angle;
+  char Flipped;
+  
+  Xo = stars[i].X;
+  Yo = stars[i].Y;
+  X = Xo;
+  Y = Yo;
+  X2 = Xo*Xo;
+  Y2 = Yo*Yo;
+  XY = Xo*Yo;
+  N = 1;
+  /* points to the right */
+  for (j = i + 1; (j < Npts) && ((dX = stars[j].X - Xo) < RADIUS); j++) {
+    if (mark[j]) continue;
+    dY = stars[j].Y - Yo;
+    if (fabs(dY) < RADIUS) {
+      angle = atan2 (dY,dX);
+      if (!finite(angle)) continue;
+      if (angle < 0) angle += M_PI;
+      if (fabs(angle - Angle) < 2*RAD_DEG) {
+	X += stars[j].X;
+	Y += stars[j].Y;
+	X2 += stars[j].X*stars[j].X;
+	Y2 += stars[j].Y*stars[j].Y;
+	N ++;
+	XY += stars[j].X*stars[j].Y;
+      }
+    }
+  }
+  /* points to the left */
+  for (j = i - 1; (j >= 0) && ((dX = Xo - stars[j].X) < RADIUS); j--) {
+    if (mark[j]) continue;
+    dY = stars[j].Y - Yo;
+    if (fabs(dY) < RADIUS) {
+      angle = atan2 (dY,dX);
+      if (!finite(angle)) continue;
+      if (angle < 0) angle += M_PI;
+      if (fabs(angle - Angle) < 2*RAD_DEG) {
+	X += stars[j].X;
+	Y += stars[j].Y;
+	X2 += stars[j].X*stars[j].X;
+	Y2 += stars[j].Y*stars[j].Y;
+	N ++;
+	XY += stars[j].X*stars[j].Y;
+      }
+    }
+  }
+  /* determine coeffs */
+  Flipped = 0;
+  det = 1.0 / (X2*N - X*X);
+  m = det * (XY*N - X*Y);
+  b = det * (X2*Y - XY*X);
+  if (fabs(m) > 1.1) { /* use a line of R = m*D + b instead */
+    /* fprintf (stderr, "high slope object: %f %f  -> ", m, b); */
+    det = 1.0 / (Y2*N - Y*Y);
+    m = det * (XY*N - X*Y);
+    b = det * (Y2*X - XY*Y);
+    Flipped = 1;
+    /* fprintf (stderr, "%f %f\n", m, b); */
+  }
+
+  *M = m;
+  *B = b;
+  return (Flipped);
+
+}
+
+
+void find_better_line (SMPData *stars, char *mark, int Npts, int i, double *M, double *B, int axis) {
+  
+  int j, N;
+  double X, Y, X2, Y2, XY, m, b, det, dist;
+  double delta, *path;
+  int NLINE, *line;
+
+  NLINE = 50;
+  ALLOCATE (line, int, NLINE);
+  ALLOCATE (path, double, NLINE);
+  
+  /* fit a line to points near line */
+  X = Y = X2 = Y2 = XY = N = 0;
+  m = *M;  b = *B;
+  for (j = 0; (j < Npts); j++) {
+    if (axis == 1) 
+      delta = stars[j].X - m*stars[j].Y - b;
+    else
+      delta = stars[j].Y - m*stars[j].X - b;
+    if (fabs(delta) < 2*TRAIL_WIDTH) {
+      X += stars[j].X;
+      Y += stars[j].Y;
+      X2 += stars[j].X*stars[j].X;
+      Y2 += stars[j].Y*stars[j].Y;
+      XY += stars[j].X*stars[j].Y;
+      line[N] = j;
+      path[N] = hypot (stars[j].X - stars[line[0]].X, stars[j].Y - stars[line[0]].Y);
+      N ++;
+      if (N == NLINE - 1) {
+	NLINE += 50;
+	REALLOCATE (line, int, NLINE);
+	REALLOCATE (path, double, NLINE);
+      }
+    }
+  }
+
+  if (N < NPTSINLINE) {
+    free (line);
+    return;
+  }
+
+  for (i = 0; i < N - NPTSINLINE + 1; i++) {
+    j = i + NPTSINLINE - 1;
+    dist = fabs (path[j] - path[i]);
+    if ((j - i) / dist < MIN_DENSITY) continue;
+    for (; (j < N) && (((j - i) / dist) > MIN_DENSITY); j++) {
+      dist = fabs (path[j] - path[i]);
+    }
+    if ((j == N) && (((j - i) / dist) > MIN_DENSITY)) j++;
+    j--;
+    for (; i < j; i++) {
+      mark[line[i]] = TRUE;
+      stars[line[i]].dophot = 0;
+    }
+    i--;
+  }
+  free (line);
+  free (path);
+
+  /* determine coeffs */
+  if (axis == 0) {
+    det = 1.0 / (X2*N - X*X);
+    m = det * (XY*N - X*Y);
+    b = det * (X2*Y - XY*X);
+  } else {
+    det = 1.0 / (Y2*N - Y*Y);
+    m = det * (XY*N - X*Y);
+    b = det * (Y2*X - XY*Y);
+  }
+
+  fprintf (stderr, "%f %f %d\n", m, b, N);
+
+  *M = m;
+  *B = b;
+
+}
+
+void fix_total (SMPData *stars, int Nstars, Header *header) {
+  
+  int Ngood, i;
+
+  Ngood = 0;
+  for (i = 0; i < Nstars; i++) {
+    if (stars[i].dophot != 0) Ngood ++;
+  }
+
+  gfits_modify (header, "NSTARS", "%d", 1, Ngood);
+  
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/find_trails.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/find_trails.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/find_trails.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "imclean.h"
+
+void find_trails (SMPData *stars, int Nstars) {
+
+  int i;
+  char *mark;
+  double Angle, axis, m, b;
+  
+  ALLOCATE (mark, char, Nstars);
+  bzero (mark, Nstars);
+  
+  for (i = 0; i < Nstars; i++) {
+    /* already marked, ignore */
+    if (mark[i]) continue;
+    if (find_group (stars, mark, Nstars, i, &Angle)) {
+      /* this point has an excess nearby concentration, find the line */
+      axis = find_line (stars, mark, Nstars, i, &m, &b, Angle);
+      find_better_line (stars, mark, Nstars, i, &m, &b, axis);
+      /* mark_trail (stars, mark, Nstars, i, m, b, axis); */
+    }
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/imclean.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/imclean.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/imclean.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "imclean.h"
+
+int main (int argc, char **argv) {
+
+  Header header;
+  SMPData *stars;
+  int Nstars;
+
+  ConfigInit (&argc, argv);
+
+  args (argc, argv);
+
+  /* load in FITS header from image */
+  if (!gfits_read_header (argv[1], &header)) {
+    fprintf (stderr, "ERROR: can't find image file %s\n", argv[1]);
+    exit (1);
+  }
+
+  AdjustHeader (&header);
+  
+  switch (MODE) {
+  case DOPHOT:
+    stars = LoadStarsDophot (argv[2], &Nstars, &header);
+    break;
+  case CHAD:
+    stars = LoadStarsChad (argv[2], &Nstars, &header);
+    break;
+  case SEXTRACT:
+    stars = LoadStarsSex (argv[2], &Nstars, &header);
+    break;
+  default: 
+    fprintf (stderr, "unknown mode: %d\n", MODE);
+    exit (1);
+  }
+
+  sort_stars (stars, Nstars);
+  find_trails (stars, Nstars);  
+  fix_total (stars, Nstars, &header);
+
+  if (FITS_OUTPUT) {
+    wfits (argv[3], stars, Nstars, &header); 
+  } else {
+    wstars (argv[3], stars, Nstars, &header); 
+  }
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+
+}
+
+/* based on fstat and markstar:
+
+   0) load config data, global parameters 
+   1) load header
+   2) load data from *.obj file
+   3) eliminate bad star types: 6, 8
+   4) identify trails
+   5) get statistics on remaining stars
+
+*/
+
+/* 
+imclean (file.fits) (file.obj) (file.cmp) 
+
+ [-p photcode]
+ [-chad]
+ [-sex]
+ [-coords RA DEC] 
+ [-astrom file]
+ [-v]
+ [-key name %f value]
+ [-key name %s value]
+ (maximum of 64 keywords can be changed)
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/sort_stars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/sort_stars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/sort_stars.c	(revision 22322)
@@ -0,0 +1,13 @@
+# include "imclean.h"
+
+void sort_stars (SMPData *stars, int N) {
+
+# define SWAPFUNC(A,B){ SMPData tmp; tmp = stars[A]; stars[A] = stars[B]; stars[B] = tmp; }
+# define COMPARE(A,B)(stars[A].X < stars[B].X)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/star_stats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/star_stats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/star_stats.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include "imclean.h"
+
+star_stats (Header *header, SMPData *stars, int Nstars) {
+
+  int i;
+  
+  /* zero things that will sum */
+  N = A = A2 = S2 = 0;
+  gotFWHM = FALSE;
+
+  for (i = 0; i < HIST_BINS; i++) { Mhist[i] = Shist[i] = 0; }
+  for (i = 1; i <= 9; i++) { n[i] = 0; }
+
+  for (i = 0; i < Nstars; i++) {
+    n[type] ++;
+    switch (type) {
+    case 6:  /* just throw these ones out */
+    case 8:
+    case 9:
+      break;
+    case 1:
+      sscanf (&buffer[i*CHAR_LINE + AP_FIELD], "%lf", &ap);
+      if (ap < 99) {
+	apmifit = ap - m;
+	A += apmifit / SQ(df);
+	A2 += SQ(apmifit) / SQ(df);
+	S2 += 1.0 / SQ(df);
+      } 
+      if (!gotFWHM) {
+	sscanf (&buffer[i*CHAR_LINE + PSF_FIELD], "%lf %lf %lf ", &FWHMx, &FWHMy, &angle);
+	gotFWHM = TRUE;
+      }
+    case 4:
+    case 7:
+      bin = MAX (0.0, MIN (HIST_BINS, 10.0 * (m + 15.0)));  /* stick in 0.1 mag bins */
+      Mhist[bin] ++;
+    case 2:
+    case 3:
+    case 5:
+      fprintf (g, "%6.1f %6.1f %6.3f %03d %1d %3.1f\n", x, y, m+ZERO_POINT, (int)(1000*df), type, lsky);
+      N ++; 
+    }
+  }
+
+  Ap = A / S2;
+  Ap2 = sqrt(A2 / (S2) - Ap*Ap);
+  sum = 0.0;
+  for (i = 0; i < HIST_BINS; i++) {
+    sum += Mhist[i];
+    Shist[i] = sum;
+  }
+  satfound = done = FALSE;
+  for (i = 0; (i < HIST_BINS) && !done; i++) {
+    if ((!satfound) && (Mhist[i] > 0)) {
+      saturate = 0.1*(i-1) - 15.0;
+      satfound = TRUE;
+    }
+    if (Shist[i] > 0.9*Shist[HIST_BINS - 1]) {
+      complete = 0.1*i - 15.0;
+      done = TRUE;
+    }
+  }
+  
+  gfits_modify (&header, "ZERO_PT", "%lf", 1, ZERO_POINT);
+  gfits_modify (&header, "FWHM_X", "%lf", 1, FWHMx);
+  gfits_modify (&header, "FWHM_Y", "%lf", 1, FWHMy);
+  gfits_modify (&header, "ANGLE", "%lf", 1, angle);
+  gfits_modify (&header, "APMIFIT", "%lf", 1, Ap);
+  gfits_modify (&header, "dAPMIFIT", "%lf", 1, Ap2);
+  gfits_modify (&header, "FSATUR", "%lf", 1, (saturate + ZERO_POINT));
+  gfits_modify (&header, "FLIMIT", "%lf", 1, (complete + ZERO_POINT));
+  gfits_modify (&header, "NSTARS", "%d", 1, N);
+  for (i = 1; i <= 9; i++) {
+    sprintf (line, "TDOPHOT%1d\0", i);
+    gfits_modify (&header, line, "%d", 1, n[i]);
+  }
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/wfits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/wfits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/wfits.c	(revision 22322)
@@ -0,0 +1,27 @@
+# include "imclean.h"
+
+void wfits (char *filename, SMPData *stars, int Nstars, Header *header) {
+
+  Matrix matrix;
+  Header theader;
+  FTable table;
+
+  header[0].extend = TRUE;
+  header[0].Naxes = 0;
+  gfits_modify (header, "NAXIS",   "%d", 1, 0);
+  gfits_modify (header, "EXTEND",  "%t", 1, TRUE);
+  gfits_modify (header, "NEXTEND", "%d", 1, 1);
+
+  /* add in some keywords to specify the datatype & software version? */
+
+  /* create (empty) data matrix */
+  gfits_create_matrix (header, &matrix);
+    
+  table.header = &theader;
+  gfits_table_set_SMPData (&table, stars, Nstars);
+
+  gfits_write_header  (filename, header);
+  gfits_write_matrix  (filename, &matrix);
+  gfits_write_Theader (filename, &theader);
+  gfits_write_table   (filename, &table);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/wstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/wstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imclean/src/wstars.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "imclean.h"
+# define NCHAR 66 /* 65 char EXCLUDING return */
+
+void wstars (char *filename, SMPData *stars, int Nstars, Header *header) {
+  
+  int i, Nchar;
+  FILE *g;
+  char line[NCHAR + 3];
+
+  g = fopen (filename, "w");
+  if (g == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't create output file %s\n", filename);
+    exit (1);
+  }
+
+  fwrite (header[0].buffer, 1, header[0].size, g);
+
+  for (i = 0; i < Nstars; i++) {
+    if (stars[i].dophot == 0) continue;
+    Nchar = snprintf (line, NCHAR, "%6.1f %6.1f %6.3f %03d %2d %3.1f %6.3f %6.3f %6.2f %6.2f %5.1f", 
+		      stars[i].X, stars[i].Y, stars[i].M, 
+		      (int)(1000*stars[i].dM), stars[i].dophot, stars[i].sky, 
+		      stars[i].Mgal, stars[i].Map, stars[i].fx, stars[i].fy, stars[i].df);
+    
+    /* this is just a little funny.  NCHAR (in) includes the trailing NULL, Nchar (out) excludes it */
+    if (Nchar != NCHAR - 1) {
+      fprintf (stderr, "funny line %d (%d)\n%s\n", i, Nchar, line);
+    } else {
+      fprintf (g, "%s\n", line);
+    }
+  }
+
+  fclose (g);
+
+}
+
+/*
+
+  63.6 2869.5 17.568 157 17.568 17.568 25.01 25.00 360.0 7 2.9
+
+  63.6 2869.5 17.568 157 7 2.9
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/Makefile	(revision 22322)
@@ -0,0 +1,193 @@
+default: all
+help:
+	@echo "make options: all imregister etc (default)"
+
+include ../../Makefile.System
+HOME    =       $(ROOT)/src/imregister
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+BASEDIR =	$(HOME)/base
+DETREND =	$(HOME)/detrend
+PHOTREG =	$(HOME)/photreg
+IMREG   =	$(HOME)/imreg
+IMPHOT  =	$(HOME)/imphot
+SPREG   =	$(HOME)/spreg
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+# programs which should be installed
+INSTALL = \
+imregister   imstatreg    \
+imsort       imsearch     \
+showiminfo   convertimreg \
+detregister  detsearch    \
+photsearch   photreg      \
+imregtable   imphotsearch \
+cameraconfig filtnames    \
+photcode     photcode-table
+
+# program under development, not installed
+DEVEL = spregister spsearch imphotmerge
+
+IMPHOTOBJ = \
+$(IMPHOT)/args.$(ARCH).o \
+$(IMPHOT)/db_load.$(ARCH).o \
+$(IMPHOT)/dumpfits.$(ARCH).o \
+$(IMPHOT)/modify.$(ARCH).o \
+$(IMPHOT)/output.$(ARCH).o \
+$(IMPHOT)/rfits.$(ARCH).o \
+$(IMPHOT)/rtext.$(ARCH).o \
+$(IMPHOT)/subset.$(ARCH).o
+
+$(SRC)/imphotsearch.$(ARCH).o \
+$(SRC)/imphotmerge.$(ARCH).o \
+$(IMPHOTOBJ): $(INC)/imphot.h $(INC)/imregister.h
+
+IMOBJ  = \
+$(IMREG)/delete.$(ARCH).o \
+$(IMREG)/iminfo.$(ARCH).o \
+$(IMREG)/load_probes.$(ARCH).o  \
+$(IMREG)/match.$(ARCH).o  \
+$(IMREG)/modify.$(ARCH).o \
+$(IMREG)/newimages.$(ARCH).o \
+$(IMREG)/output.$(ARCH).o \
+$(IMREG)/unique.$(ARCH).o \
+$(IMREG)/cadc.$(ARCH).o   \
+$(IMREG)/mosaics.$(ARCH).o   \
+$(IMREG)/ConfigPID.$(ARCH).o   \
+$(IMREG)/SetSignals.$(ARCH).o   \
+$(IMREG)/rconnect.$(ARCH).o   \
+$(IMREG)/FifoOps.$(ARCH).o   \
+$(IMREG)/SockScan.$(ARCH).o   \
+$(IMREG)/submit.$(ARCH).o \
+$(IMREG)/imregclient.$(ARCH).o
+
+$(SRC)/imsearch.$(ARCH).o \
+$(SRC)/imregister.$(ARCH).o \
+$(SRC)/imstatreg.$(ARCH).o \
+$(SRC)/showiminfo.$(ARCH).o \
+$(SRC)/imregtable.$(ARCH).o \
+$(SRC)/convertimreg.$(ARCH).o \
+$(IMREG)/args.imsearch.$(ARCH).o \
+$(IMREG)/args.imregister.$(ARCH).o \
+$(IMOBJ): $(INC)/imreg.h $(INC)/imregister.h
+
+SPOBJ  = \
+$(SPREG)/delete.$(ARCH).o   \
+$(SPREG)/match.$(ARCH).o    \
+$(SPREG)/modify.$(ARCH).o   \
+$(SPREG)/output.$(ARCH).o   \
+$(SPREG)/unique.$(ARCH).o   \
+$(SPREG)/spinfo.$(ARCH).o   \
+$(SPREG)/showinfo.$(ARCH).o \
+$(SPREG)/ConfigInit.$(ARCH).o
+
+$(SRC)/spregister.$(ARCH).o \
+$(SPREG)/args.spregister.$(ARCH).o \
+$(SRC)/spsearch.$(ARCH).o \
+$(SPREG)/args.spsearch.$(ARCH).o \
+$(SPOBJ): $(INC)/spreg.h $(INC)/imregister.h
+
+PHOTOBJ  = \
+$(PHOTREG)/delete.$(ARCH).o  \
+$(PHOTREG)/match.$(ARCH).o   \
+$(PHOTREG)/convert.$(ARCH).o \
+$(PHOTREG)/output.$(ARCH).o  \
+$(PHOTREG)/getImageData.$(ARCH).o
+
+$(SRC)/photreg.$(ARCH).o \
+$(SRC)/photsearch.$(ARCH).o \
+$(PHOTREG)/args.photreg.$(ARCH).o \
+$(PHOTREG)/args.photsearch.$(ARCH).o \
+$(PHOTOBJ): $(INC)/photreg.h $(INC)/imregister.h
+
+DETOBJ  = \
+$(DETREND)/criteria.$(ARCH).o  \
+$(DETREND)/delete.$(ARCH).o    \
+$(DETREND)/entry.$(ARCH).o     \
+$(DETREND)/image.$(ARCH).o     \
+$(DETREND)/imdef.$(ARCH).o     \
+$(DETREND)/match.$(ARCH).o     \
+$(DETREND)/modify.$(ARCH).o    \
+$(DETREND)/mosaic.$(ARCH).o    \
+$(DETREND)/output.$(ARCH).o    \
+$(DETREND)/recipe.$(ARCH).o    \
+$(DETREND)/select.$(ARCH).o    \
+$(DETREND)/unique.$(ARCH).o    \
+$(DETREND)/altpath.$(ARCH).o   \
+$(DETREND)/db_names.$(ARCH).o   \
+$(DETREND)/usage.$(ARCH).o
+
+$(SRC)/detregister.$(ARCH).o  \
+$(SRC)/detsearch.$(ARCH).o \
+$(DETREND)/args.detregister.$(ARCH).o \
+$(DETREND)/args.detsearch.$(ARCH).o \
+$(DETOBJ): $(INC)/detrend.h $(INC)/imregister.h
+
+BASEOBJ = \
+$(BASEDIR)/ConfigCamera.$(ARCH).o \
+$(BASEDIR)/ConfigFilter.$(ARCH).o \
+$(BASEDIR)/ConfigInit.$(ARCH).o   \
+$(BASEDIR)/WriteFIFO.$(ARCH).o    \
+$(BASEDIR)/get_fwhm.$(ARCH).o     \
+$(BASEDIR)/misc.$(ARCH).o         \
+$(BASEDIR)/version.$(ARCH).o      \
+$(BASEDIR)/parse_time.$(ARCH).o   \
+$(BASEDIR)/fits_scan_nchar.$(ARCH).o
+
+$(SRC)/photcode-table.$(ARCH).o \
+$(SRC)/photcode.$(ARCH).o \
+$(SRC)/cameraconfig.$(ARCH).o \
+$(SRC)/filtnames.$(ARCH).o \
+$(BASEOBJ): $(INC)/imregister.h
+
+# specific programs:
+$(BIN)/imsearch.$(ARCH)     : $(SRC)/imsearch.$(ARCH).o     $(IMREG)/args.imsearch.$(ARCH).o      $(IMOBJ)   $(BASEOBJ)
+$(BIN)/imregister.$(ARCH)   : $(SRC)/imregister.$(ARCH).o   $(IMREG)/args.imregister.$(ARCH).o    $(IMOBJ)   $(BASEOBJ)
+$(BIN)/imstatreg.$(ARCH)    : $(SRC)/imstatreg.$(ARCH).o    $(IMREG)/args.imregister.$(ARCH).o    $(IMOBJ)   $(BASEOBJ)
+$(BIN)/showiminfo.$(ARCH)   : $(SRC)/showiminfo.$(ARCH).o   $(IMREG)/args.imregister.$(ARCH).o    $(IMOBJ)   $(BASEOBJ)
+$(BIN)/imregtable.$(ARCH)   : $(SRC)/imregtable.$(ARCH).o   $(IMREG)/args.imregister.$(ARCH).o    $(IMOBJ)   $(BASEOBJ)
+$(BIN)/convertimreg.$(ARCH) : $(SRC)/convertimreg.$(ARCH).o $(IMREG)/args.imregister.$(ARCH).o    $(IMOBJ)   $(BASEOBJ)
+$(BIN)/detregister.$(ARCH)  : $(SRC)/detregister.$(ARCH).o  $(DETREND)/args.detregister.$(ARCH).o $(DETOBJ)  $(BASEOBJ)
+$(BIN)/detsearch.$(ARCH)    : $(SRC)/detsearch.$(ARCH).o    $(DETREND)/args.detsearch.$(ARCH).o   $(DETOBJ)  $(BASEOBJ)
+$(BIN)/photreg.$(ARCH)      : $(SRC)/photreg.$(ARCH).o      $(PHOTREG)/args.photreg.$(ARCH).o     $(PHOTOBJ) $(BASEOBJ)
+$(BIN)/photsearch.$(ARCH)   : $(SRC)/photsearch.$(ARCH).o   $(PHOTREG)/args.photsearch.$(ARCH).o  $(PHOTOBJ) $(BASEOBJ)
+$(BIN)/spregister.$(ARCH)   : $(SRC)/spregister.$(ARCH).o   $(SPREG)/args.spregister.$(ARCH).o    $(SPOBJ)   $(BASEOBJ)
+$(BIN)/spsearch.$(ARCH)     : $(SRC)/spsearch.$(ARCH).o     $(SPREG)/args.spsearch.$(ARCH).o      $(SPOBJ)   $(BASEOBJ)
+$(BIN)/imphotsearch.$(ARCH) : $(SRC)/imphotsearch.$(ARCH).o $(IMPHOTOBJ) $(BASEOBJ)
+$(BIN)/imphotmerge.$(ARCH)  : $(SRC)/imphotmerge.$(ARCH).o  $(IMPHOTOBJ) $(BASEOBJ)
+$(BIN)/cameraconfig.$(ARCH) : $(SRC)/cameraconfig.$(ARCH).o $(BASEOBJ)
+$(BIN)/filtnames.$(ARCH)    : $(SRC)/filtnames.$(ARCH).o    $(BASEOBJ)
+$(BIN)/photcode.$(ARCH)     : $(SRC)/photcode.$(ARCH).o     $(BASEOBJ)
+$(BIN)/photcode-table.$(ARCH) : $(SRC)/photcode-table.$(ARCH).o
+
+$(BIN)/imsort.$(ARCH): $(BIN)/imregister.$(ARCH)
+	rm -f  $(BIN)/imsort.$(ARCH)
+	cp $(BIN)/imregister.$(ARCH) $(BIN)/imsort.$(ARCH)
+
+all: $(INSTALL) $(DEVEL)
+
+$(INSTALL) $(DEVEL) : % : $(BIN)/%.$(ARCH)
+
+# utilities #################################################
+
+%.clean :
+	rm -f $(BIN)/$*.$(ARCH)
+
+%.rebuild :
+	rm -f $(BIN)/$*.$(ARCH)
+	make $(DESTBIN)/$*
+
+%.install:
+	make $(DESTBIN)/$*
+
+install:
+	for i in $(INSTALL); do make $$i.install || exit; done
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/ConfigCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/ConfigCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/ConfigCamera.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "imregister.h"
+
+void ConfigCamera () { 
+
+  int i;
+  char *config, ID[64], field[128], line[128];
+
+  /* load camera config file */
+  config = LoadConfigFile (CameraConfig);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find camera config file %s\n", CameraConfig);
+    exit (1);
+  }
+  
+  /* load data from config file */
+  ScanConfig (config, "NCCD", "%d", 1, &Nccd);
+  ALLOCATE (ccds, char *, Nccd);
+  ALLOCATE (ccdn, char *, Nccd);
+
+  for (i = 0; i < Nccd; i++) {
+    sprintf (field, "CCD.%d", i);
+    ScanConfig (config, field, "%s", 1, line);
+    sscanf (line, "%s", ID);
+    ccds[i] = strcreate (ID);
+  }
+
+  for (i = 0; i < Nccd; i++) {
+    sprintf (ID, "%02d", i);
+    ccdn[i] = strcreate (ID);
+  }
+}
+
+int MatchCCDNameHeader (Header *header) {
+
+  int i;
+  char ID[64];
+
+  ID[0] = 0;
+  
+  gfits_scan (header, CCDnumKeyword,  "%s", 1, ID);
+  if (!ID[0]) { 
+    fprintf (stderr, "warning, ccd id not found in header\n");
+    return (-1);
+  }
+  
+  /* compare as number if ID is a complete number (ie, 00 equiv to 0, but 00b not equiv to 0b */
+  for (i = 0; i < Nccd; i++) {
+    if (strnumcmp (ccds[i], ID)) {
+      return (i);
+    }
+  }
+  
+  fprintf (stderr, "warning: ccd %s not found in camera config file\n", ID);
+  return (-1);
+}
+
+int MatchCCDName (char *ID) {
+
+  int i;
+
+  /* compare as number if ID is a complete number (ie, 00 equiv to 0, but 00b not equiv to 0b */
+  for (i = 0; i < Nccd; i++) {
+    if (strnumcmp (ccds[i], ID)) {
+      return (i);
+    }
+  }
+  
+  fprintf (stderr, "warning: ccd %s not found in camera config file\n", ID);
+  return (-1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/ConfigFilter.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/ConfigFilter.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/ConfigFilter.c	(revision 22322)
@@ -0,0 +1,94 @@
+# include "imregister.h"
+
+void ConfigFilter () {
+
+  int i, N, code, NFILT, Nfilt, Nfield;
+  char *c, line[256], name[64];
+  FILE *f;
+
+  /* open filter list file */
+  f = fopen (FilterList, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "error reading photcodes\n");
+    exit (1);
+  }
+
+  /* allocate dataspace needed */
+  NFILT = 100;
+  Nfilt = 0;
+  ALLOCATE (filternum,  int,    NFILT);
+  ALLOCATE (filtername, char *, NFILT);
+  for (i = Nfilt; i < NFILT; i++) {
+    ALLOCATE (filtername[i], char, 64);
+  }
+
+  while (scan_line (f, line) != EOF) {
+    for (c = line; isspace (*c); c++);
+    if (*c == '#') continue;
+    Nfield = sscanf (c, "%d %s", &code, name);
+    if (Nfield != 2) { continue; }
+
+    filternum[Nfilt] = code;
+    strcpy (filtername[Nfilt], name);
+    Nfilt ++;
+
+    if (Nfilt == NFILT - 1) {
+      NFILT += 100;
+      REALLOCATE (filternum,  int,    NFILT);
+      REALLOCATE (filtername, char *, NFILT);
+      for (i = Nfilt; i < NFILT; i++) {
+	REALLOCATE (filtername[i], char, 64);
+      }
+    }
+  }
+  fclose (f);
+
+  /* make filter hash table (using first available entries) */
+  ALLOCATE (filterhash, char *, Nfilt);
+  for (i = 0; i < Nfilt; i++) {
+    filterhash[i] = (char *) NULL;
+  }
+
+  for (i = 0; i < Nfilt; i++) {
+    N = filternum[i];
+    if (filterhash[N] != (char *) NULL) continue;
+    filterhash[N] = filtername[i];
+  }
+
+  NFILTER = Nfilt;
+  /* we now have NFILTER set, and filternum & filtername arrays filled */
+
+}  
+
+/* convert filter string to fixed filter names (convert all spaces to .) */
+int MatchFilterList (char *line) {
+
+  char *p;
+  int i, blank;
+
+  /* convert spaces to . */
+  blank = FALSE;
+  p = line;
+  for (i = 0; i < strlen (line); i++, p++) {
+    *p = line[i];
+    if (OHANA_WHITESPACE(line[i])) { 
+      *p = '.';
+      if (blank) p--;
+      if (!blank) blank = TRUE;
+    } else {
+      blank = FALSE;
+    }
+  }
+  *p = 0;
+
+  /* find defined filter name */
+  for (i = 0; i < NFILTER; i++) {
+    if (!strcasecmp (line, filtername[i])) {
+      /* careful: line[80] */
+      strcpy (line, filterhash[filternum[i]]);
+      return (TRUE);
+    }
+  }      
+  fprintf (stderr, "unknown filter %s\n", line);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/ConfigInit.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "imregister.h"
+
+int success;
+
+void ConfigInit (int *argc, char **argv) {
+
+  int i, NDB;
+  char *config, *file, ElixirBase[80], catdir[256];
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+
+  success = TRUE;
+
+  WarnConfig (config, "REGISTRATION_DATABASE",       "%s", 0, ImageDB);
+  WarnConfig (config, "DETREND_DATABASE",            "%s", 0, DetrendDB);
+  WarnConfig (config, "PHOT_DATABASE",               "%s", 0, PhotDB);
+  WarnConfig (config, "TRANS_DATABASE",              "%s", 0, TransDB);
+
+  WarnConfig (config, "CATDIR",                      "%s", 0, catdir);
+  WarnConfig (config, "PHOTCODE_FILE",               "%s", 0, MasterPhotcodeFile);
+  sprintf (ImPhotDB, "%s/Images.dat", catdir);
+
+  /* small text databases: filters, camera defs */ 
+  WarnConfig (config, "TEMPERATURE_LOG",             "%s", 0, TempLogFile);
+  WarnConfig (config, "FILTER_LIST",                 "%s", 0, FilterList);
+  WarnConfig (config, "CAMERA_CONFIG",               "%s", 0, CameraConfig);
+  WarnConfig (config, "DETREND_RECIPES",             "%s", 0, RecipeFile);
+						   
+  /* pixel scale for FWHM */ 
+  WarnConfig (config, "ASEC_PIX",                    "%lf", 0, &ARCSEC_PIXEL);
+
+  /* keyword abstractions for parse_time */	   
+  WarnConfig (config, "DATE-KEYWORD",                "%s", 0, DateKeyword);
+  WarnConfig (config, "DATE-MODE",                   "%s", 0, DateMode);
+  WarnConfig (config, "UT-KEYWORD",                  "%s", 0, UTKeyword);
+  WarnConfig (config, "MJD-KEYWORD",                 "%s", 0, MJDKeyword);
+  WarnConfig (config, "JD-KEYWORD",                  "%s", 0, JDKeyword);
+						   
+  /* keyword abstractions for iminfo */		   
+  WarnConfig (config, "EXPTIME-KEYWORD",             "%s", 0, ExptimeKeyword);
+  WarnConfig (config, "IMAGETYPE-KEYWORD",           "%s", 0, ImagetypeKeyword);
+  WarnConfig (config, "CCDNUM-KEYWORD",              "%s", 0, CCDnumKeyword);
+  WarnConfig (config, "FILTER-KEYWORD",              "%s", 0, FilterKeyword);
+  WarnConfig (config, "AIRMASS-KEYWORD",             "%s", 0, AirmassKeyword);
+  WarnConfig (config, "FOCUS-KEYWORD",               "%s", 0, FocusKeyword);
+  WarnConfig (config, "ROTATION-KEYWORD",            "%s", 0, RotationKeyword);
+  WarnConfig (config, "DETTEMP-KEYWORD",             "%s", 0, DettempKeyword);
+  WarnConfig (config, "TELDATA1-KEYWORD",            "%s", 0, Teldata1Keyword);
+  WarnConfig (config, "TELDATA2-KEYWORD",            "%s", 0, Teldata2Keyword);
+  WarnConfig (config, "TELDATA3-KEYWORD",            "%s", 0, Teldata3Keyword);
+  WarnConfig (config, "CAMERA-KEYWORD",              "%s", 0, CameraKeyword);
+
+  ScanConfig (config, "CAMERA",                      "%s", 0, Camera);
+  ScanConfig (config, "SEEING_REF_CCD",              "%s", 0, SeeingREFCCD);
+
+  /* optional values */
+  ScanConfig (config, "RA-DDD-KEYWORD",              "%s", 0, RADecDegKeyword);
+  ScanConfig (config, "DEC-DDD-KEYWORD",             "%s", 0, DECDecDegKeyword);
+  ScanConfig (config, "RA-HMS-KEYWORD",              "%s", 0, RASexigKeyword);
+  ScanConfig (config, "DEC-DMS-KEYWORD",             "%s", 0, DECSexigKeyword);
+
+  if (!RADecDegKeyword[0] & !DECDecDegKeyword[0] && !RASexigKeyword[0] && !DECSexigKeyword[0]) {
+    fprintf (stderr, "missing astrometry configuration information\n");
+    success = FALSE;
+  }
+						   
+  WarnConfig (config, "imstats",                     "%s", 0, ElixirBase);
+  sprintf (ImstatFifo, "%s.photcode", ElixirBase);   
+  WarnConfig (config, "ptolemy",                     "%s", 0, ElixirBase);
+  sprintf (PtolemyFifo, "%s.photcode", ElixirBase);
+
+  if (!ScanConfig (config, "CONNECT", "%s",  0, CONNECT)) {
+    sprintf (CONNECT, "/usr/bin/rsh");
+  }
+
+  /* load Detrend Alt Databases paths */
+  NDB = 10;
+  NDetrendAltDB = 0;
+  ALLOCATE (DetrendAltDB, char *, NDB);
+  ALLOCATE (DetrendAltDB[NDetrendAltDB], char, 256);
+  for (i = 1; ScanConfig (config, "DETREND_ALT_DB", "%s", i, DetrendAltDB[NDetrendAltDB]); i++) {
+    NDetrendAltDB ++;
+    if (NDetrendAltDB == NDB) {
+      NDB += 10;
+      REALLOCATE (DetrendAltDB, char *, NDB);
+    }
+    ALLOCATE (DetrendAltDB[NDetrendAltDB], char, 256);
+  }
+  free (DetrendAltDB[NDetrendAltDB]);
+
+  if (! success) {
+    fprintf (stderr, "ERROR: problem with elixir configuration\n");
+    exit (1);
+  }
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", catdir);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+
+}
+
+void WarnConfig (char *config, char *key, char * mode, int N, void *var) {
+  if (!ScanConfig (config, key, mode, N, var)) {
+    fprintf (stderr, "missing config variable %s\n", key);
+    success = FALSE;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/WriteFIFO.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/WriteFIFO.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/WriteFIFO.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "imregister.h"
+
+int WriteFIFO (char *filename, char *line) {
+
+  int state, mode;
+  FILE *f;
+
+  f = fsetlockfile (filename, 20.0, LCK_XCLD, &state);
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't lock fifo %s\n", filename);
+    return (FALSE);
+  }
+
+  fseek (f, 0, SEEK_END);
+  fprintf (f, "%s\n", line);
+
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (filename, mode);
+  fclearlockfile (filename, f, LCK_XCLD, &state);
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/convert.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/convert.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/convert.c	(revision 22322)
@@ -0,0 +1,2 @@
+# include "imregister.h"
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/fits_scan_nchar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/fits_scan_nchar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/fits_scan_nchar.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include "imregister.h"
+
+/* scan and give a warning for missing entries */
+void warn_scan (Header *header, char *field, char *format, int N, void *var) {
+  if (!gfits_scan (header, field, format, N, var)) {
+    fprintf (stderr, "WARNING: %s not found in header\n", field);
+  }
+}
+
+/* scan from header into a string of fixed length */
+int gfits_scan_nchar (Header *header, int size, char *field, int N,...) {
+
+  char tmpstr[160], *outstr; 
+  va_list argp;
+  int status;
+  
+  va_start (argp, N);
+  outstr = va_arg (argp, char *);
+  va_end (argp);
+
+  status = gfits_scan (header, field, "%s", N, tmpstr); 
+  strncpy (outstr, tmpstr, size - 1);
+  outstr[size-1] = 0; 
+
+  return (status);
+} 
+     
+void warn_scan_nchar (Header *header, int size, char *field, int N, void *var) {
+  if (!gfits_scan_nchar (header, size, field, N, var)) {
+    fprintf (stderr, "missing %s not found in header\n", field);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/misc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/misc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/misc.c	(revision 22322)
@@ -0,0 +1,133 @@
+# include "imregister.h"
+
+static double tz = 0.0;
+void set_timezone (double dt) {
+  tz = dt;
+}
+
+/* return values:
+   0 - no trange arguments
+   1 - arguments ok
+   2 - arguments bad
+*/
+int get_trange_arguments (int *argc, char **argv, time_t **Tstart, time_t **Tstop, int *ntimes) {
+
+  int Na, N, Ntimes;
+  double trange;
+  time_t tmp, *tstart, *tstop;
+
+  /* allocate space for returned lists */
+  Ntimes = 10;
+  ALLOCATE (tstart, time_t, Ntimes);
+  ALLOCATE (tstop,  time_t, Ntimes);
+
+  for (N = 0; ; N++) {
+
+    /* find next -trange arg */
+    Na = get_argument (*argc, argv, "-trange");
+    if (Na == 0) {
+      *ntimes = N;
+      *Tstart = tstart;
+      *Tstop  = tstop;
+      return (TRUE);
+    }
+    remove_argument (Na, argc, argv);
+    
+    /* tstart */
+    if (!ohana_str_to_time (argv[Na], &tstart[N])) { 
+      return (FALSE);
+    }
+
+    /* interpret second value */
+    remove_argument (Na, argc, argv);
+    if (ohana_str_to_dtime (argv[Na], &trange)) { 
+      if (trange < 0) {
+	tstop[N]  = tstart[N];
+	tstart[N] = tstop[N] + trange;
+      } else {
+	tstop[N]  = tstart[N] + trange;
+      }
+      remove_argument (Na, argc, argv);
+      goto goodvalue;
+    }
+    if (ohana_str_to_time (argv[Na], &tstop[N])) { 
+      if (tstart[N] > tstop[N]) {
+	tmp     = tstart[N];
+	tstart[N] = tstop[N];
+	tstop[N]  = tmp;
+      }
+      remove_argument (Na, argc, argv);
+      goto goodvalue;
+    }
+    return (FALSE); /* syntax error in 2nd value */
+
+  goodvalue:
+    if (N == Ntimes - 1) {
+      Ntimes += 10;
+      REALLOCATE (tstart, time_t, Ntimes);
+      REALLOCATE (tstop,  time_t, Ntimes);
+    }
+  }
+}
+  
+int get_filter_arguments (int *argc, char **argv, int **Filt, int *Nfilt) {
+
+  int i, N, Na, NF;
+  int *filt;
+
+  /* allocate space for returned lists */
+  NF = 10;
+  ALLOCATE (filt, int, NF);
+
+  for (N = 0; ; N++) {
+
+    /* find next -trange arg */
+    Na = get_argument (*argc, argv, "-filter");
+    if (Na == 0) {
+      *Nfilt = N;
+      *Filt = filt;
+      return (TRUE);
+    }
+    if (Na > *argc - 2) return (FALSE); /* -filter F */
+    remove_argument (Na, argc, argv);
+    
+    for (i = 0; i < strlen (argv[Na]); i++) { if (isspace (argv[Na][i])) argv[Na][i] = '.'; }
+    for (i = 0; i < NFILTER; i++) {
+      if (!strcasecmp (argv[Na], filtername[i])) {
+	filt[N] = filternum[i];
+      }
+    }
+    if (filt[N] == FILTER_NONE) return (FALSE);
+    remove_argument (Na, argc, argv);
+
+    if (N == NF - 1) {
+      NF += 10;
+      REALLOCATE (filt, int, NF);
+    }
+  }
+}
+  
+   
+/* replaces WHITESPACE blocks with single . */
+void clean_spaces (char *line) {
+
+  char *out, *in;
+
+  if (line == NULL) return;
+
+  out = in = line;
+  while (*in) {
+    if (OHANA_WHITESPACE(*in)) { 
+      *out = '.';
+      out ++;
+      in ++;
+      while (*in && OHANA_WHITESPACE(*in)) in++;
+    } else {
+      *out = *in;
+      out ++;
+      in ++;
+    }
+  }
+  *out = 0;
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/parse_time.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/parse_time.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/parse_time.c	(revision 22322)
@@ -0,0 +1,120 @@
+# include "imregister.h"
+
+int parse_time (Header *header) {
+
+  double jd;
+  int Ny, Nf, mode;
+  int Nsec, hour, min, sec, year, month, day;
+  char *py, *pm, *pd, *c;
+  char line[256];
+
+  /* we want to find JD or MJD to get Nsec (seconds since 01/01/1970) */
+
+  /* try JD first */
+  if (strcasecmp (JDKeyword, "NONE")) {
+    uppercase (JDKeyword);
+    warn_scan (header, JDKeyword, "%lf", 1, &jd);
+    Nsec = ohana_jd_to_sec (jd);
+    return (Nsec);
+  }
+
+  /* try MJD next */
+  if (strcasecmp (MJDKeyword, "NONE")) {
+    uppercase (MJDKeyword);
+    warn_scan (header, MJDKeyword, "%lf", 1, &jd);
+    Nsec = ohana_mjd_to_sec (jd);
+    return (Nsec);
+  }
+    
+  /* get UT and DATE */
+  uppercase (UTKeyword);
+  warn_scan (header, UTKeyword, "%s", 1, line);
+  /* remove ':' characters */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  sscanf (line, "%d %d %d", &hour, &min, &sec);
+
+  /* parse mode line */
+  uppercase (DateMode);
+  for (Ny = 0, c = strchr (DateMode, 'Y'); c != (char ) NULL; c = strchr (c + 1, 'Y'), Ny++);
+  if ((Ny != 2) && (Ny != 4)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (0);
+  }
+  py = strchr (DateMode, 'Y');
+  pm = strchr (DateMode, 'M');
+  pd = strchr (DateMode, 'D');
+  if ((py == (char *) NULL) || (pm == (char *) NULL) || (pd == (char *) NULL)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (0);
+  }
+  if ((py > pm) && (py < pd)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (0);
+  }
+  if ((py > pd) && (py < pm)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (0);
+  }
+  mode = 0;
+  if ((py < pm) && (pm < pd)) { mode = 1; }  /* yyyy-mm-dd */
+  if ((py < pm) && (pm > pd)) { mode = 2; }  /* yyyy-dd-mm */
+  if ((py > pm) && (pm < pd)) { mode = 3; }  /* mm-dd-yyyy */
+  if ((py > pm) && (pm > pd)) { mode = 4; }  /* dd-mm-yyyy */
+  if (!mode) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (0);
+  }
+
+  /* parse date entry */
+  uppercase (DateKeyword);
+  warn_scan (header, DateKeyword, "%s",  1, line);
+  /* remove possible separators: ':', '/' '.', '-' */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  for (c = strchr (line, 0x2f); c != (char *) NULL; c = strchr (line, 0x2f)) { *c = ' '; }
+  for (c = strchr (line, 0x2e); c != (char *) NULL; c = strchr (line, 0x2e)) { *c = ' '; }
+  for (c = strchr (line, 0x2d); c != (char *) NULL; c = strchr (line, 0x2d)) { *c = ' '; }
+
+  Nf = 0;
+  switch (mode) {
+  case 1:
+    Nf = sscanf (line, "%d %d %d", &year, &month, &day);
+    break;
+  case 2:
+    Nf = sscanf (line, "%d %d %d", &year, &day, &month);
+    break;
+  case 3:
+    Nf = sscanf (line, "%d %d %d", &month, &day, &year);
+    break;
+  case 4:
+    Nf = sscanf (line, "%d %d %d", &day, &month, &year);
+    break;
+  }
+  if (Nf != 3) {
+    fprintf (stderr, "error in date entry (%s) or DATE-MODE format (%s)\n", line, DateMode);
+    exit (0);
+  }
+
+  if (year > 1000) {
+    if (Ny == 2) {
+      fprintf (stderr, "warning: mode line claims 2 digit year, but 4 digit year found\n");
+    }
+  } else {
+    if (Ny == 4) {
+      fprintf (stderr, "warning: mode line claims 4 digit year, but 2 digit year found\n");
+    }
+    if (year < 50) year += 100;
+    year += 1900;
+  }    
+
+  /* convert yy.mm.dd hh.mm.ss to Nsec since 1970 (jd = 2440587.5) */
+  /* note that in this section, tm_mon has range 1-12, unlike for gmtime () */
+  jd = day - 32075 + (int)(1461*(year + 4800 + (int)(((month)-14)/12))/4)
+    + (int)(367*((month) - 2 - (int)(((month) - 14)/12)*12)/12)
+    - (int)(3*(int)((year + 4900 + (int)(((month) - 14)/12))/100)/4) - 0.5;
+  /* jd is the julian day of the whole day only not the time */
+  Nsec = (jd - 2440587.5)*86400 + 3600.0*hour + min*60.0 + sec;
+  
+  return (Nsec);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/base/version.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "imregister.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+void get_version (int argc, char **argv, char *version) {
+
+  if (!get_argument (argc, argv, "-version")) return;
+
+  fprintf (stderr, "%s\n", version);
+  fprintf (stderr, "%s\n", name);
+
+  fprintf (stderr, "ohana: %s\n", ohana_version());
+  fprintf (stderr, "fits:  %s\n", gfits_version());
+  exit (2);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/altpath.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/altpath.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/altpath.c	(revision 22322)
@@ -0,0 +1,155 @@
+# include "imregister.h"
+# include "detrend.h"
+
+int SetAltpath (FITS_DB *db, DetReg *image, int Nimage, Match *match, int Nmatch) {
+  
+  int i, j, n, found, status, Nlist;
+  int *list, *current;
+  char *dBPath, infile[256], outfile[256], line[1024];
+  struct stat statbuf;
+
+  ALLOCATE (current, int, Nimage);
+  ALLOCATE (list, int, Nimage);
+  Nlist = 0;
+
+  dBPath = get_dBPath ();
+
+  /* set altpath for 'add' images */
+  if (output.Altpath == ADD) {
+    /* find images to add */
+    for (i = 0; i < Nmatch; i++) {
+      if (image[match[i].image].altpath == FALSE) {
+	list[Nlist] = match[i].image;
+	Nlist ++;
+      }
+    }
+    /* copy the masters to the altpath locations */
+    for (j = 0; j < Nlist; j++) {
+      i = list[j];
+      for (n = 0; n < NDetrendAltDB; n++) {
+	sprintf (infile, "%s/%s", dBPath, image[i].filename);
+	sprintf (outfile, "%s/%s", DetrendAltDB[n], image[i].filename);
+	status = ckpathname (outfile);
+	if (!status) {
+	  fprintf (stderr, "warning: can't make outfile directory for %s\n", outfile);
+	  continue;
+	}
+	sprintf (line, "cp %s %s", infile, outfile);
+	fprintf (stderr, "%s\n", line);
+	status = system (line);
+	if (status) {
+	  fprintf (stderr, "warning: can't make outfile directory for %s\n", outfile);
+	  continue;
+	}
+      }
+      image[i].altpath = TRUE;
+    }
+
+    /** we may later want to pull this out and put it elsewhere **/
+    gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, list, Nlist);
+    for (i = 0; i < Nmatch; i++) {
+      gfits_convert_DetReg ((DetReg *) db[0].vtable.buffer[i], sizeof (DetReg), 1);
+    }
+    gfits_db_update (db);
+    gfits_db_close (db);
+    gfits_db_free (db);
+
+    fprintf (stderr, "SUCCESS\n");
+    exit (0);
+  }
+
+  /* unset altpath for 'delete' images */
+  if (output.Altpath == DELETE) {
+    /* find images to delete */
+    for (i = 0; i < Nmatch; i++) {
+      if (image[match[i].image].altpath == TRUE) {
+	list[Nlist] = match[i].image;
+	Nlist ++;
+      }
+    }
+    /* remove the copies from the altpath locations */
+    for (j = 0; j < Nlist; j++) {
+      i = list[j];
+      image[i].altpath = FALSE;
+      for (n = 0; n < NDetrendAltDB; n++) {
+	sprintf (outfile, "%s/%s", DetrendAltDB[n], image[i].filename);
+	sprintf (line, "rm %s", outfile);
+	fprintf (stderr, "%s\n", line);
+	status = system (line);
+	if (status) {
+	  fprintf (stderr, "warning: can't delete %s\n", outfile);
+	}
+      }
+    }
+
+    /** we may later want to pull this out and put it elsewhere **/
+    gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, list, Nlist);
+    for (i = 0; i < Nmatch; i++) {
+      gfits_convert_DetReg ((DetReg *) db[0].vtable.buffer[i], sizeof (DetReg), 1);
+    }
+    gfits_db_update (db);
+    gfits_db_close (db);
+    gfits_db_free (db);
+
+    fprintf (stderr, "SUCCESS\n");
+    exit (0);
+  }
+
+  /* check for existence in altpath, set flag as appropriate */
+  if (output.Altpath == UPDATE) {
+    for (j = 0; j < Nmatch; j++) {
+      i = match[j].image;
+      list[Nlist] = i;
+      Nlist ++;
+      found = TRUE;
+      for (n = 0; found && (n < NDetrendAltDB); n++) {
+	sprintf (outfile, "%s/%s", DetrendAltDB[n], image[i].filename);
+	status = stat (outfile, &statbuf);
+	fprintf (stderr, "checking for %s, status is %d\n", outfile, status);
+	if (status == -1) found = FALSE;
+      }
+      image[i].altpath = found;
+    }
+
+    /** we may later want to pull this out and put it elsewhere **/
+    gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, list, Nlist);
+    for (i = 0; i < Nmatch; i++) {
+      gfits_convert_DetReg ((DetReg *) db[0].vtable.buffer[i], sizeof (DetReg), 1);
+    }
+    gfits_db_update (db);
+    gfits_db_close (db);
+    gfits_db_free (db);
+
+    fprintf (stderr, "SUCCESS\n");
+    exit (0);
+  }
+
+  fprintf (stderr, "unknown altpath mode: %d\n", output.Altpath);
+  return (TRUE);
+}
+
+
+int ckpathname (char *newpath) {
+  
+  int status;
+  char *path;
+  struct stat statbuf;
+  
+  path = pathname (newpath);
+  
+  status = stat (path, &statbuf);
+  if (status == -1) {
+    if (errno == ENOENT) {
+      if (mkdirhier (path, S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
+	fprintf (stderr, "ERROR: can't create path %s\n", path);
+	return (FALSE);
+      }
+    } else {
+      fprintf (stderr, "ERROR: problem with path %s\n", path);
+      return (FALSE);
+    }
+  }
+  free (path);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/args.detregister.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/args.detregister.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/args.detregister.c	(revision 22322)
@@ -0,0 +1,121 @@
+# include "imregister.h" 
+# include "detrend.h"
+
+/* criteria struct is global */
+int regargs (int argc, char **argv, Descriptor *descriptor) {
+
+  int N, i;
+
+  ConfigInit (&argc, argv);
+  ConfigCamera ();
+  ConfigFilter ();
+
+  output.Modify = TRUE;
+
+  /* these command line arguments will override image header info */
+  /* define time range */
+  descriptor[0].TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &descriptor[0].tstart)) { 
+      fprintf (stderr, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &descriptor[0].tstop)) { 
+      fprintf (stderr, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    descriptor[0].TimeSelect = TRUE;
+  }
+
+  /* set optional label */
+  descriptor[0].label = strcreate ("detrend");
+  if ((N = get_argument (argc, argv, "-label"))) {
+    remove_argument (N, &argc, argv);
+    descriptor[0].label = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+ 
+  /* set optional label */
+  descriptor[0].imageID = NULL;
+  if ((N = get_argument (argc, argv, "-ID"))) {
+    remove_argument (N, &argc, argv);
+    descriptor[0].imageID = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+ 
+  /* set optional order */
+  descriptor[0].order = 0;
+  if ((N = get_argument (argc, argv, "-order"))) {
+    remove_argument (N, &argc, argv);
+    descriptor[0].order = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+ 
+  /* define image type */
+  descriptor[0].type = T_UNDEF;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    descriptor[0].type = get_image_type (argv[N]);
+    if (descriptor[0].type == T_UNDEF) {
+      fprintf (stderr, "ERROR: invalid image type %s\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+  }
+ 
+  /* define ccd number */
+  descriptor[0].CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    descriptor[0].CCD = -1;
+    for (i = 0; (i < Nccd) && (descriptor[0].CCD == -1); i++) {
+      if (strnumcmp (ccds[i], argv[N])) {
+	descriptor[0].CCD = i;
+      }
+    }
+    if (descriptor[0].CCD == -1) {
+      fprintf (stderr, "ERROR: ccd %s choice out not found in camera config\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    descriptor[0].CCDSelect = TRUE;
+  }
+ 
+  /* define exposure time */
+  descriptor[0].Exptime = 0.0;
+  descriptor[0].ExptimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-exptime"))) {
+    remove_argument (N, &argc, argv);
+    descriptor[0].Exptime = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    descriptor[0].ExptimeSelect = TRUE;
+  }
+ 
+  /* define filter */
+  descriptor[0].filter = FILTER_NONE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    for (i = 0; (i < NFILTER) && (descriptor[0].filter == FILTER_NONE); i++) {
+      if (!strcasecmp (argv[N], filtername[i])) {
+	descriptor[0].filter = filternum[i];
+      }
+    }      
+    if (descriptor[0].filter == FILTER_NONE) {
+      fprintf (stderr, "ERROR: invalid filter %s\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  /*** this program will only register SPLIT images ***/
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: detregister (filename) [config ops] -time start stop] [-type type] [-ccd N] [-filter name]\n");
+    fprintf (stderr, "ERROR - detregister not run\n");
+    exit (1);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/args.detsearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/args.detsearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/args.detsearch.c	(revision 22322)
@@ -0,0 +1,384 @@
+# include "imregister.h"
+# include "detrend.h"
+
+/********* decipher all of the possible command-line options ***********/
+int args (int argc, char **argv) {
+
+  int i, N;
+  int MosaicSelect, ImageSelect, bad, Recipe, Ncrit;
+  char *ImageFile, *ImageMode, *ImageExtend;
+  Criteria base, *crit;
+  int *filt;
+  time_t *tstart, *tstop;
+
+  if ((N = get_argument (argc, argv, "-h"))) { usage (); }
+  if ((N = get_argument (argc, argv, "--help"))) { usage (); }
+  bzero (&base, sizeof (Criteria));
+
+  /* check config & command-line args */
+  ConfigInit (&argc, argv);
+  ConfigCamera ();
+  ConfigFilter ();
+
+  ImageFile = ImageMode = ImageExtend = NULL;
+
+  /* mosaic-based selection */
+  MosaicSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    ImageFile = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    MosaicSelect = TRUE;
+    /* check for incompatible arguments: -exptime, -filter, -time, -ccd */
+    bad = get_argument (argc, argv, "-exptime");
+    bad &= get_argument (argc, argv, "-image");
+    bad &= get_argument (argc, argv, "-filter");
+    bad &= get_argument (argc, argv, "-trange");
+    bad &= get_argument (argc, argv, "-time");
+    bad &= get_argument (argc, argv, "-ccd");
+    if (bad) { 
+      fprintf (stderr, "ERROR: syntax error: conflict with -mosaic\n");
+      exit (1);
+    }
+  }
+
+  /* image-based selection */
+  ImageSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-image"))) {
+    remove_argument (N, &argc, argv);
+    ImageFile = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImageExtend = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImageMode = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImageSelect = TRUE;
+    /* check for incompatible arguments: -exptime, -filter, -time, -ccd */
+    bad = get_argument (argc, argv, "-exptime");
+    bad &= get_argument (argc, argv, "-filter");
+    bad &= get_argument (argc, argv, "-trange");
+    bad &= get_argument (argc, argv, "-time");
+    bad &= get_argument (argc, argv, "-ccd");
+    if (bad) { 
+      fprintf (stderr, "ERROR: syntax error: conflict with -image\n");
+      exit (1);
+    }
+  }
+
+  /* define image type */
+  base.TypeSelect = FALSE;
+  base.Type = T_UNDEF;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    base.Type = get_image_type (argv[N]);
+    if (base.Type == T_UNDEF) {
+      fprintf (stderr, "ERROR: invalid image type %s\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    base.TypeSelect = TRUE;
+    if (base.Type == T_ANY) base.TypeSelect = FALSE;
+  }
+ 
+  /* image mode (mef, splt, etc) */
+  base.ModeSelect = FALSE;
+  base.Mode = M_UNDEF;
+  if ((N = get_argument (argc, argv, "-mode"))) {
+    remove_argument (N, &argc, argv);
+    base.Mode = get_image_mode (argv[N]);
+    if (base.Mode == M_UNDEF) {
+      fprintf (stderr, "ERROR: invalid image mode %s\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    base.ModeSelect = TRUE;
+  }
+ 
+  /* define time / ranges */
+  tstart = tstop = NULL;
+  base.TimeSelect = base.tstart = base.tstop = 0;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    bad  = get_argument (argc, argv, "-trange");
+    bad &= ohana_str_to_time (argv[N], &base.tstart);
+    if (bad) {
+      fprintf (stderr, "ERROR: syntax error in -time\n");
+      exit (1);
+    }
+    base.tstop = base.tstart;  /* no need for dtime > 0 ? */
+    remove_argument (N, &argc, argv);
+    base.TimeSelect = 1;
+  } else {
+    if (!get_trange_arguments (&argc, argv, &tstart, &tstop, &base.TimeSelect)) {
+      fprintf (stderr, "ERROR: syntax error in -trange\n");
+      exit (1);
+    }
+  }
+
+  /* define filters */
+  base.FilterSelect = FALSE;
+  if (!get_filter_arguments (&argc, argv, &filt, &base.FilterSelect)) {
+    fprintf (stderr, "ERROR: syntax error in -filter\n");
+    exit (1);
+  }
+
+  /* define ccd number */
+  base.CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    base.CCD = -1;
+    remove_argument (N, &argc, argv);
+    for (i = 0; (i < Nccd) && (base.CCD == -1); i++) {
+      if (strnumcmp (ccds[i], argv[N])) {
+	base.CCD = i;
+      }
+    }
+    if (base.CCD == -1) {
+      fprintf (stderr, "ERROR: ccd %s choice out not found in camera config\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    base.CCDSelect = TRUE;
+  }
+ 
+  /* define exposure time */
+  base.ExptimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-exptime"))) {
+    remove_argument (N, &argc, argv);
+    base.Exptime = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    base.ExptimeSelect = TRUE;
+  }
+ 
+  /* varients on the standard selection */
+  base.EntrySelect = FALSE;
+  if ((N = get_argument (argc, argv, "-entry"))) {
+    remove_argument (N, &argc, argv);
+    base.Entry = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    base.EntrySelect = TRUE;
+  }
+  base.MatchNumber = -1;
+  if ((N = get_argument (argc, argv, "-match"))) {
+    remove_argument (N, &argc, argv);
+    base.MatchNumber = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  base.LabelSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-label"))) {
+    remove_argument (N, &argc, argv);
+    base.Label = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    base.LabelSelect = TRUE;
+  }
+  base.NameSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    base.Name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    base.NameSelect = TRUE;
+  }
+
+  /* these options affect the output mode */
+  output.Close = FALSE;
+  if ((N = get_argument (argc, argv, "-close"))) {
+    if (!base.TimeSelect && !ImageSelect && !MosaicSelect) {       
+      fprintf (stderr, "ERROR: syntax error in -close requires one of: -time -trange -image -mosaic\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    output.Close = TRUE;
+  }
+  output.TimeMode = START;
+  if ((N = get_argument (argc, argv, "-tstop"))) {
+    remove_argument (N, &argc, argv);
+    output.TimeMode = STOP;
+  }
+  if ((N = get_argument (argc, argv, "-treg"))) {
+    remove_argument (N, &argc, argv);
+    output.TimeMode = REG;
+  }
+  output.ElixirSmart = FALSE;
+  if ((N = get_argument (argc, argv, "-ve"))) {
+    remove_argument (N, &argc, argv);
+    output.ElixirSmart = TRUE;
+  }
+  output.verbose = TRUE;
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    remove_argument (N, &argc, argv);
+    output.verbose = FALSE;
+  }
+  output.table = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-fits"))) {
+    remove_argument (N, &argc, argv);
+    output.table = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  output.bintable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-binfits"))) {
+    remove_argument (N, &argc, argv);
+    output.bintable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  output.Criteria = FALSE;
+  if ((N = get_argument (argc, argv, "-criteria"))) {
+    output.Criteria = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  output.Chipname = FALSE;
+  if ((N = get_argument (argc, argv, "-chipname"))) {
+    output.Chipname = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* these options modify behavior */
+  output.Select = FALSE;
+  if ((N = get_argument (argc, argv, "-select"))) {
+    remove_argument (N, &argc, argv);
+    output.Select = TRUE;
+  }
+ 
+  /* these options modify behavior */
+  Recipe = FALSE;
+  if ((N = get_argument (argc, argv, "-recipe"))) {
+    remove_argument (N, &argc, argv);
+    Recipe = TRUE;
+    if (base.Type != T_UNDEF) {
+      fprintf (stderr, "-recipe and -type cannot be combined\n");
+      exit (1);
+    }
+  }
+ 
+  /* options which alter the database */
+  output.Delete = FALSE;
+  if ((N = get_argument (argc, argv, "-del"))) {
+    remove_argument (N, &argc, argv);
+    output.Delete = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-delete"))) {
+    remove_argument (N, &argc, argv);
+    output.Delete = TRUE;
+  }
+ 
+  output.Altpath = NONE;
+  if ((N = get_argument (argc, argv, "-altpath"))) {
+    if (output.Delete) {
+      fprintf (stderr, "can't combine -delete and -altpath flags\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    if (!strcasecmp (argv[N], "add")) output.Altpath = ADD;
+    if (!strcasecmp (argv[N], "delete")) output.Altpath = DELETE;
+    if (!strcasecmp (argv[N], "update")) output.Altpath = UPDATE;
+    if (!output.Altpath) { 
+      fprintf (stderr, "invalid -altpath option\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  output.Modify = FALSE;
+  if ((N = get_argument (argc, argv, "-modify"))) {
+    if (output.Delete) {
+      fprintf (stderr, "can't combine -delete and -modify flags\n");
+      exit (1);
+    }
+    if (output.Altpath) {
+      fprintf (stderr, "can't combine -altpath and -modify flags\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    output.Modify = TRUE;
+    output.ModifyEntry = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    output.ModifyValue = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (!strcasecmp (output.ModifyEntry, "label")) goto valid_entry;
+    if (!strcasecmp (output.ModifyEntry, "order")) goto valid_entry;
+    if (!strcasecmp (output.ModifyEntry, "mode"))  goto valid_entry;
+    if (!strcasecmp (output.ModifyEntry, "tstop")) goto valid_entry;
+    if (!strcasecmp (output.ModifyEntry, "tstart")) goto valid_entry;
+    fprintf (stderr, "invalid entry for -modify %s\n", output.ModifyEntry);
+    exit (1);
+  valid_entry:
+    if (!strcasecmp (output.ModifyEntry, "tstart") || !strcasecmp (output.ModifyEntry, "tstop")) {
+      if (!ohana_str_to_time (output.ModifyValue, &output.TimeValue)) { 
+	fprintf (stderr, "ERROR: invalid time %s\n", output.ModifyValue);
+	exit (1);
+      }
+    }
+  }
+
+  /* consistency checks */
+  if ((base.Type == T_DARK) || (base.Type == T_BIAS) || (base.Type == T_MASK)) {
+    if (base.FilterSelect) {
+      fprintf (stderr, "ERROR: filter invalid with type %s\n", get_type_name(base.Type));
+      exit (1);
+    }
+  }
+  /*
+  if ((base.Type != T_DARK) && base.ExptimeSelect) {
+    fprintf (stderr, "ERROR: exptime invalid with type %s\n", get_type_name(base.Type));
+    exit (1);
+  }
+  */
+  if (output.Select && !base.TimeSelect) {
+    fprintf (stderr, "ERROR: selection missing time\n");
+    exit (1);
+  }
+
+  /* arguments have all been read */
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: detsearch [config ops] [-mosaic name] [-image name Nx Mode] [-time start] [-type type] [-ccd N] [-filter name]\n");
+    exit (1);
+  }
+
+  /* set up criteria based on image, mosaic, or base (all mutually exclusive) */
+  crit = NULL;
+  if (ImageSelect)  
+    crit = ImageCriteria (base, ImageFile, ImageExtend, ImageMode, &Ncrit);
+  if (MosaicSelect) 
+    crit = MosaicCriteria (base, ImageFile, &Ncrit);
+  if (!MosaicSelect && !ImageSelect)
+    crit = ExpandBase (base, &Ncrit, tstart, tstop, filt);
+
+  if (Recipe) {
+    crit = ExpandRecipe (crit, &Ncrit);
+  }
+
+  Ncriteria = Ncrit;
+  criteria = crit;
+
+  return (TRUE);
+}
+
+Criteria *ExpandBase (Criteria base, int *ncrit, time_t *tstart, time_t *tstop, int *filt) {
+
+  Criteria *crit;
+  int i, j, Nc, Ncrit, Ntimes, Nfilt; 
+
+  /* expand multiple -filter, -trange options */
+  Nfilt  = base.FilterSelect;
+  Ntimes = base.TimeSelect;
+  if (Ntimes == 0) Ntimes = 1;
+  if (Nfilt  == 0) Nfilt  = 1;
+
+  Ncrit = Nfilt*Ntimes;
+  ALLOCATE (crit, Criteria, Ncrit);
+
+  Nc = 0;
+  for (i = 0; i < Nfilt; i++) {
+    for (j = 0; j < Ntimes; j++) {
+      crit[Nc] = base;
+      if (tstart != NULL) {
+	crit[Nc].tstart       = tstart[j];
+	crit[Nc].tstop        = tstop[j];
+      } 
+      if (base.FilterSelect) {
+	crit[Nc].Filter       = filt[i];
+      } 
+      Nc ++;
+    }
+  }
+  *ncrit = Ncrit;
+  return (crit);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/criteria.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/criteria.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/criteria.c	(revision 22322)
@@ -0,0 +1,219 @@
+# include "imregister.h"
+# include "detrend.h"
+
+Match CheckCriteria (DetReg *image) {
+
+  Match match;
+  int i, close, crit;
+
+  match.state = MATCH_NONE;
+  match.crit  = 0;
+  match.image = -1;
+
+  crit = 0;
+  close = FALSE;
+  for (i = 0; (i < Ncriteria) && (match.state == MATCH_NONE); i++) {
+
+    if (criteria[i].CCDSelect) {
+      if (image[0].mode == M_MEF) goto valid_ccd;
+      if (image[0].mode == M_MODES) goto valid_ccd;
+      if (image[0].ccd  == criteria[i].CCD) goto valid_ccd;
+      continue;
+    }
+  valid_ccd:
+
+    if (criteria[i].TypeSelect   && (image[0].type            != criteria[i].Type))   continue;
+    if (criteria[i].ModeSelect   && (image[0].mode            != criteria[i].Mode))   continue;
+    if (criteria[i].FilterSelect && (image[0].filter          != criteria[i].Filter)) continue;
+
+    if (criteria[i].EntrySelect  && (image[0].Nentry          != criteria[i].Entry))  continue;
+    if (criteria[i].LabelSelect  && (strcasecmp (image[0].label, criteria[i].Label))) continue;
+    if (criteria[i].NameSelect   && (strstr (image[0].filename, criteria[i].Name) == (char *) NULL)) continue;
+
+    /* looking for the best 'close' match: minimum |dt| */
+    if (criteria[i].TimeSelect   && (image[0].tstart           > criteria[i].tstop))  {
+      close = TRUE;
+      crit  = i;
+      continue;
+    } 
+    if (criteria[i].TimeSelect   && (image[0].tstop            < criteria[i].tstart)) {
+      close = TRUE;
+      crit  = i;
+      continue;
+    } 
+    match.crit  = i;
+    match.state = MATCH_PERFECT;
+  }
+
+  if ((match.state == MATCH_NONE) && close) {
+    match.crit  = crit;
+    match.state = MATCH_CLOSE;
+  }    
+  return (match);
+
+}
+
+/* 
+   the image only needs to match one criterion
+   close only counts for TimeSelect
+   multiple criteria can exist for Range, CCD, Filter
+*/
+
+Match *ExptimeCriteria (DetReg *image, int Nimage, Match *match, int *nmatch) {
+
+  int i, j, Nmatch, Nnew, NNEW, entry;
+  unsigned long dtime;
+  float Chi, ChiMin;
+  Match *new;
+
+  Nmatch = *nmatch;
+
+  Nnew = 0;
+  NNEW = 100;
+  ALLOCATE (new, Match, NNEW);
+
+  for (i = 0; i < Ncriteria; i++) {
+
+    /* find min Chi value */
+    ChiMin = HUGE;
+    for (j = 0; j < Nmatch; j++) {
+      if (match[j].crit != i) continue;
+      if (!criteria[i].ExptimeSelect) {
+	new[Nnew] = match[j];
+	Nnew ++;
+	if (Nnew == NNEW) {
+	  NNEW += 100;
+	  REALLOCATE (new, Match, NNEW);
+	}
+	continue;
+      }
+
+      entry = match[j].image;
+
+      dtime = 0;
+      if (criteria[i].TimeSelect) {
+	if (criteria[i].tstart > image[entry].tstop) 
+	  dtime = criteria[i].tstart - image[entry].tstop;
+	if (criteria[i].tstop < image[entry].tstart) 
+	  dtime = image[entry].tstart - criteria[i].tstop;
+      }
+      Chi = dtime / 864.0 + abs (criteria[i].Exptime - image[entry].exptime);
+      ChiMin = MIN (Chi, ChiMin);
+    }
+
+    /* select entries with Chi <= ChiMin */
+    for (j = 0; j < Nmatch; j++) {
+      if (match[j].crit != i) continue;
+      if (!criteria[i].ExptimeSelect) continue;
+      entry = match[j].image;
+      
+      dtime = 0;
+      if (criteria[i].TimeSelect) {
+	if (criteria[i].tstart > image[entry].tstop) 
+	  dtime = criteria[i].tstart - image[entry].tstop;
+	if (criteria[i].tstop < image[entry].tstart) 
+	  dtime = image[entry].tstart - criteria[i].tstop;
+      }
+      Chi = dtime / 864.0 + abs (criteria[i].Exptime - image[entry].exptime);
+      if (Chi <= ChiMin) {
+	new[Nnew] = match[j];
+	Nnew ++;
+	if (Nnew == NNEW) {
+	  NNEW += 100;
+	  REALLOCATE (new, Match, NNEW);
+	}
+      }
+    }
+  }
+
+  free (match);
+  *nmatch = Nnew;
+  return (new);
+}
+
+Match *CloseCriteria (DetReg *image, int Nimage, Match *match, int *nmatch) {
+
+  int i, j, Nmatch, Nnew, NNEW, Ngood, entry;
+  unsigned long dmin, dtime;
+  Match *new;
+
+  Nmatch = *nmatch;
+
+  Nnew = 0;
+  NNEW = 100;
+  ALLOCATE (new, Match, NNEW);
+
+  for (i = 0; i < Ncriteria; i++) {
+
+    Ngood = 0;
+
+    /* first include only perfect matches */
+    for (j = 0; j < Nmatch; j++) {
+      if (match[j].crit  != i) continue;
+      if (match[j].state != MATCH_PERFECT) continue;
+
+      Ngood ++;
+      new[Nnew] = match[j];
+      Nnew ++;
+      if (Nnew == NNEW) {
+	NNEW += 100;
+	REALLOCATE (new, Match, NNEW);
+      }	
+    } 
+
+    if (Ngood) continue;
+    if (!output.Close) continue;
+    if (!criteria[i].TimeSelect) continue;
+
+    /* find closest time */
+    dmin = 0xffffffff;
+    for (j = 0; j < Nmatch; j++) {
+      if (match[j].crit  != i) continue;
+      entry = match[j].image;
+      dtime = (image[entry].tstop < criteria[i].tstart) ?  criteria[i].tstart - image[entry].tstop : image[entry].tstart - criteria[i].tstop;
+      dmin = MIN (dmin, dtime);
+    }
+
+    /* select images with dtime == dmin */
+    for (j = 0; j < Nmatch; j++) {
+      if (match[j].crit  != i) continue;
+      entry = match[j].image;
+      dtime = (image[entry].tstop < criteria[i].tstart) ?  criteria[i].tstart - image[entry].tstop : image[entry].tstart - criteria[i].tstop;
+      if (dtime <= dmin) {
+	new[Nnew] = match[j];
+	Nnew ++;
+	if (Nnew == NNEW) {
+	  NNEW += 100;
+	  REALLOCATE (new, Match, NNEW);
+	}	
+      }
+    }
+  }
+
+  free (match);
+  *nmatch = Nnew;
+  return (new);
+}
+
+
+
+
+
+/* CheckCriteria returns two pieces of information:
+   match state (MATCH_NONE, MATCH_CLOSE, MATCH_PERFECT)
+   match entry (i)
+*/
+   
+/* match[] contains all potentially valid matches.
+   if they are darks, there will be several with similar values for detdata[i].tstop
+   we want to minimize:
+   (tzero - tstop) and abs (ExpTime - detdata[i].exptime) 
+
+   Chi = (tzero - tstop) / 864 + abs (ExpTime - detdata[i].exptime)
+
+   if (tzero - tstop) = 2 days, this term contributes 200.  
+*/
+
+
+/* CloseCriteria returns all images which are either a perfect match
+   or which are close and have the same minimum time distance */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/db_names.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/db_names.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/db_names.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "imregister.h"
+# include "detrend.h"
+
+static char *dBFile  = NULL;
+static char *dBPath  = NULL;
+static char *dBTrash = NULL;
+
+/*** these are functions to handle the special detrend.db file/trash names ****/
+
+char *set_dBFile () {
+
+  struct stat statbuf;
+  int status;
+
+  dBPath = DetrendDB;
+
+  /* define dBFile based on config data */
+  ALLOCATE (dBFile, char, strlen (dBPath) + 15);
+  sprintf (dBFile, "%s/detrend.db", dBPath);;
+  ALLOCATE (dBTrash, char, strlen (dBPath) + 15);
+  sprintf (dBTrash, "%s/trash", dBPath);
+
+  /* check on directories */
+  status = stat (dBPath, &statbuf);
+  if (output.Modify) {
+    if (status == -1) {
+      if (mkdirhier (dBPath, S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
+	fprintf (stderr, "ERROR: can't find or create path %s\n", dBPath);
+	exit (1);
+      }
+    }
+  }
+  if (output.Delete) {
+    status = stat (dBTrash, &statbuf);
+    if (status == -1) {
+      if (mkdirhier (dBTrash, S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
+	fprintf (stderr, "ERROR: detrend dB trash not found %s\n", dBTrash);
+	exit (1);
+      }
+    }
+  }
+  return (dBFile);
+}
+
+char *get_dBPath () {
+
+  return (dBPath);
+
+}
+
+int delete_image (DetReg *item) {
+  
+  int status;
+  char line[256];
+
+  if (output.verbose) fprintf (stderr, "deleting %s\n", item[0].filename);
+  sprintf (line, "mv -f %s/%s %s", dBPath, item[0].filename, dBTrash);
+  status = system (line);
+  if (status) fprintf (stderr, "trouble moving %s to trash (%s)\n", item[0].filename, dBTrash);
+
+  if (status) 
+    return (FALSE);
+  else 
+    return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/delete.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/delete.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/delete.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "imregister.h"
+# include "detrend.h"
+
+void DeleteSubset (FITS_DB *db, DetReg *image, int Nimage, Match *match, int Nmatch) {
+
+  int i, j;
+  int *keep, Ndel, Nsubset;
+  DetReg *subset;
+
+  ALLOCATE (keep, int, MAX (Nimage, 1));
+  for (i = 0; i < Nimage; i++) keep[i] = TRUE;
+  fprintf (stderr, "total of %d detrend images\n", Nimage);
+
+  Ndel = 0;
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j].image;
+    if (i == -1) continue;
+    keep[i] = FALSE;
+    Ndel ++;
+    delete_image (&image[i]);
+  }
+  fprintf (stderr, "delete %d images\n", Ndel);
+  if (Ndel == 0) { 
+    fprintf (stderr, "SUCCESS\n");
+    gfits_db_close (db);
+    exit (0);
+  }
+
+  /* create new data list */
+  Nsubset = Nimage - Ndel;
+  ALLOCATE (subset, DetReg, MAX (1, Nsubset));
+  fprintf (stderr, "keeping %d images\n", Nsubset);
+  for (j = i = 0; i < Nimage; i++) {
+    if (!keep[i]) continue;
+    subset[j] = image[i];
+    j ++;
+  }
+  free (keep);
+  free (image);
+
+  /** we may later want to pull this out and put it elsewhere **/
+  /** free db[0].theader, db[0].table.buffer? **/
+  gfits_table_set_DetReg (&db[0].ftable, subset, Nsubset);
+  gfits_db_save (db);
+  gfits_db_close (db);
+  gfits_db_free (db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/entry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/entry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/entry.c	(revision 22322)
@@ -0,0 +1,112 @@
+# include "imregister.h"
+# include "detrend.h"
+
+DetReg DefineEntry (Descriptor descriptor) {
+  
+  struct timeval now;
+  DetReg newdata;
+
+  gettimeofday (&now, (void *) NULL);
+  
+  /* now we have all of the data (except filename), set the new entry */
+  newdata.tstart    = descriptor.tstart;
+  newdata.tstop     = descriptor.tstop;
+  newdata.treg      = now.tv_sec;
+  newdata.type      = descriptor.type;
+  newdata.filter    = descriptor.filter;
+  newdata.ccd       = descriptor.CCD;
+  newdata.exptime   = descriptor.Exptime;
+  newdata.Norder    = descriptor.order;
+  newdata.mode      = descriptor.mode;
+  newdata.altpath   = FALSE;
+  bzero (newdata.dummy, 58);
+  snprintf (newdata.label, 64, "%s", descriptor.label);
+  
+  return (newdata);
+}
+
+/* we are generating a name based on type, etc.  determine valid path */
+int SaveEntry (char *input, DetReg *newdata, char *ID) {
+
+  int status;
+  int found, Ntry, filestate;
+  char path[256], fullpath[256], filename[256], rootname[256], fullname[256], line[512];
+  char *dBPath;
+  char *filter_basename;
+  struct stat statbuf;
+  FILE *f;
+
+  filter_basename = filterhash[newdata[0].filter];
+  dBPath = get_dBPath ();
+
+  if (newdata[0].type == T_FLAT) {
+    sprintf (path, "%s/%s", get_type_name(newdata[0].type), filter_basename);
+  } else {
+    sprintf (path, "%s", get_type_name(newdata[0].type));
+  }    
+  sprintf (fullpath, "%s/%s", dBPath, path);
+
+  status = stat (fullpath, &statbuf);
+  if (status == -1) {
+    if (errno == ENOENT) {
+      if (mkdirhier (fullpath, S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
+	fprintf (stderr, "ERROR: can't create path %s\n", fullpath);
+	exit (1);
+      }
+    } else {
+      fprintf (stderr, "ERROR: problem with path %s\n", fullpath);
+      exit (1);
+    }
+  }
+
+  /* base filename constructed from detrend type information */
+  switch (newdata[0].type) {
+  case T_DARK:
+  case T_BIAS:
+  case T_MASK:
+    sprintf (rootname, "%s.%s.%d.%02d", ID, get_type_name(newdata[0].type), (int)newdata[0].exptime, newdata[0].ccd);
+    break;
+  default:
+    sprintf (rootname, "%s.%s.%s.%02d", ID, get_type_name(newdata[0].type), filter_basename, newdata[0].ccd);
+  }
+
+  /* find & lock first file of the format name that does not exist */
+  found = FALSE;
+  for (Ntry = 0; !found && (Ntry < 100); Ntry++) {
+    sprintf (filename, "%s/%s.%02d.fits", path, rootname, Ntry);
+    sprintf (fullname, "%s/%s", dBPath, filename);
+    f = fsetlockfile (fullname, 2.0, LCK_XCLD, &filestate);
+    /* if there is an error, it may just mean file is locked. */
+    if (filestate == LCK_EMPTY) {
+	found = TRUE;
+	newdata[0].Nentry = Ntry;
+    } 
+  }
+  if (!found) { 
+    fprintf (stderr, "ERROR: no available target files for %s/%s?\n", path, rootname);
+    exit (1);
+  }
+  strcpy (newdata[0].filename, filename);
+    
+  if (DEBUG) {
+    fprintf (stderr, "path: %s\n", path);
+    fprintf (stderr, "fullpath: %s\n", fullpath);
+    fprintf (stderr, "filename: %s\n", filename);
+    fprintf (stderr, "dBPath: %s\n", dBPath);
+    fprintf (stderr, "line: %s\n", line);
+  }
+
+  /* copy the file to the new name, add entry to database */
+  /* we need some error checking here - complain if filename > 255 */
+  /* the copy replaces the locked file, lock remains valid */
+
+  sprintf (line, "/bin/cp -f %s %s", input, fullname);
+  status = system (line);
+  if (status) {
+    fprintf (stderr, "ERROR: failure in image copy %s to %s\n", input, fullname);
+    exit (1);
+  }
+  fclearlockfile (fullname, f, LCK_XCLD, &filestate);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/image.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/image.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/image.c	(revision 22322)
@@ -0,0 +1,90 @@
+# include "imregister.h"
+# include "detrend.h"
+
+Criteria *ImageCriteria (Criteria base, char *filename, char *ImageExtend, char *ImageMode, int *ncrit) {
+
+  int i, Ncrit;
+  Header header;
+  Criteria *crit;
+  char line[80];
+
+  Ncrit = 1;
+  ALLOCATE (crit, Criteria, Ncrit);
+
+  /* load options from the image header */
+  if (!gfits_read_header (filename, &header)) {
+    if (output.verbose) fprintf (stderr, "ERROR: trouble reading image header\n");
+    exit (1);
+  }
+
+  /* get Time from header */
+  base.tstop = base.tstart = parse_time (&header);
+  base.TimeSelect = TRUE;
+
+  /* get the CCD */
+  if (!strcasecmp (ImageMode, "SPLIT")) {
+    ALLOCATE (ImageExtend, char, 80);
+    if (!gfits_scan (&header, CCDnumKeyword, "%s", 1, ImageExtend)) {
+      fprintf (stderr, "ERROR: failure to read %s from header\n", CCDnumKeyword);
+      exit (1);
+    }
+  }
+  /* lookup CCD ID from camera config */
+  base.CCD = -1;
+  for (i = 0; (i < Nccd) && (base.CCD == -1); i++) {
+    if (strnumcmp (ccds[i], ImageExtend)) {
+      base.CCD = i;
+    }
+  }
+  if (base.CCD == -1) {
+    fprintf (stderr, "warning: ccd id %s not found\n", ImageExtend);
+    base.CCD = 0;
+  }
+
+  /* get filter from header */
+  if (!gfits_scan (&header, FilterKeyword, "%s", 1, line)) {
+    fprintf (stderr, "ERROR: trouble reading FILTER from header\n");
+    exit (1);
+  }
+  /* filter names in database have . for space */
+  base.Filter = FILTER_NONE;
+  for (i = 0; i < strlen (line); i++) { if (isspace (line[i])) line[i] = '.'; }
+  for (i = 0; (i < NFILTER) && (base.Filter == FILTER_NONE); i++) {
+    if (!strcasecmp (line, filtername[i])) {
+      base.Filter = filternum[i];
+    }
+  }      
+  if (base.Filter == FILTER_NONE) {
+    fprintf (stderr, "ERROR: invalid filter %s\n", line);
+    exit (1);
+  }
+
+  /* get exptime from header */
+  if (!gfits_scan (&header, ExptimeKeyword, "%f", 1, &base.Exptime)) {
+    fprintf (stderr, "ERROR: trouble reading EXPTIME from header\n");
+    exit (1);
+  }
+
+  /* define selections implicit in ImageSelect */
+  if (base.Type != T_MODES) {
+    base.CCDSelect = TRUE;
+  }
+  if ((base.Type != T_DARK) && (base.Type != T_BIAS) && (base.Type != T_MASK)) {
+    base.FilterSelect = TRUE;
+  } else {
+    base.FilterSelect = FALSE;
+  }      
+  if (base.Type == T_DARK) {
+    base.ExptimeSelect = TRUE;
+  } else {
+    base.ExptimeSelect = FALSE;
+  }      
+
+  /* controvesial... */
+  output.Select = TRUE;
+
+  *ncrit = Ncrit;
+  crit[0] = base;
+  return (crit);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/imdef.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/imdef.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/imdef.c	(revision 22322)
@@ -0,0 +1,149 @@
+# include "imregister.h"
+# include "detrend.h"
+
+int DefineImage (char *filename, Descriptor *descriptor) {
+
+  int i, Extend, Nextend;
+  char line[512];
+  Header header;
+
+  /* load remaining options from the image header */
+  if (!gfits_read_header (filename, &header)) {
+    fprintf (stderr, "ERROR: trouble reading image header\n");
+    exit (1);
+  }
+
+  /* first decide on image TYPE */
+  if (descriptor[0].type == T_UNDEF) {
+    if (!gfits_scan (&header, ImagetypeKeyword, "%s", 1, line)) {
+      fprintf (stderr, "ERROR: failure to read %s from header\n", ImagetypeKeyword);
+      exit (1);
+    }
+    descriptor[0].type = get_image_type (line);
+    if (descriptor[0].type == T_UNDEF) {
+      fprintf (stderr, "ERROR: invalid image type %s\n", line);
+      exit (1);
+    }
+  }    
+  /* conflicts */
+  if ((descriptor[0].type == T_DARK) && (descriptor[0].filter != FILTER_NONE)) {
+    fprintf (stderr, "ERROR: dark can't have a filter\n");
+    exit (1);
+  }
+  if ((descriptor[0].type == T_BIAS) && (descriptor[0].filter != FILTER_NONE)) {
+    fprintf (stderr, "ERROR: bias can't have a filter\n");
+    exit (1);
+  }
+  if ((descriptor[0].type == T_MASK) && (descriptor[0].filter != FILTER_NONE)) {
+    fprintf (stderr, "ERROR: mask can't have a filter\n");
+    exit (1);
+  }
+
+  /* identify MODE (MEF / SPLIT)  */
+  descriptor[0].mode = M_SPLIT;
+  Extend = FALSE;
+  gfits_scan (&header, "EXTEND", "%t", 1, &Extend);
+  if (Extend) {
+    descriptor[0].mode      = M_MEF;
+    descriptor[0].CCD       = Nccd;
+    descriptor[0].CCDSelect = TRUE;
+    gfits_scan (&header, "NEXTEND",  "%d", 1, &Nextend);
+    if (Nextend != Nccd) { 
+      fprintf (stderr, "warning: NEXTEND != Nccd (%d, %d)\n", Nextend, Nccd);
+    }      
+  }
+
+  /* modes: a special case */
+  if (descriptor[0].type == T_MODES) {
+    descriptor[0].mode      = M_MODES;
+    descriptor[0].CCD       = Nccd;
+    descriptor[0].CCDSelect = TRUE;
+  }
+
+  /* now identify CCD number */
+  if (!descriptor[0].CCDSelect) {
+    char ID[64];
+
+    descriptor[0].CCD = -1;
+    if (!gfits_scan (&header, CCDnumKeyword, "%s", 1, ID)) {
+      fprintf (stderr, "ERROR: failure to read %s from header\n", CCDnumKeyword);
+      exit (1);
+    }
+    for (i = 0; (i < Nccd) && (descriptor[0].CCD == -1); i++) {
+      if (strnumcmp (ID, ccds[i])) {
+	descriptor[0].CCD = i;
+      }
+    }
+    if (descriptor[0].CCD == -1) {
+      fprintf (stderr, "warning: ccd id not found\n");
+      descriptor[0].CCD = 0;
+    }
+    descriptor[0].CCDSelect = TRUE;
+  }
+
+  /* now get time range */
+  if (!descriptor[0].TimeSelect) {
+    if (!gfits_scan (&header, "TVSTART",   "%s", 1, line)) {
+      fprintf (stderr, "ERROR: missing start time\n");
+      exit (1);
+    }
+    if (!ohana_str_to_time (line, &descriptor[0].tstart)) { 
+      fprintf (stderr, "ERROR: invalid time %s\n", line);
+      exit (1);
+    }
+    
+    if (!gfits_scan (&header, "TVSTOP",   "%s", 1, line)) {
+      fprintf (stderr, "ERROR: missing stop time\n");
+      exit (1);
+    }
+    if (!ohana_str_to_time (line, &descriptor[0].tstop)) { 
+      fprintf (stderr, "ERROR: invalid time %s\n", line);
+      exit (1);
+    }
+  }
+
+  /* select the filter */
+  if (descriptor[0].type == T_DARK) goto skip_filter;
+  if (descriptor[0].type == T_BIAS) goto skip_filter;
+  if (descriptor[0].type == T_MASK) goto skip_filter;
+  if (descriptor[0].filter != FILTER_NONE) goto skip_filter;
+  if (!gfits_scan (&header, FilterKeyword, "%s", 1, line)) {
+    fprintf (stderr, "ERROR: failure to read %s from header\n", FilterKeyword);
+    exit (1);
+  }
+  for (i = 0; (i < NFILTER) && (descriptor[0].filter == FILTER_NONE); i++) {
+    if (!strcasecmp (line, filtername[i])) {
+      descriptor[0].filter = filternum[i];
+    }
+  }      
+  if (descriptor[0].filter == FILTER_NONE) {
+    fprintf (stderr, "ERROR: invalid filter %s\n", line);
+    exit (1);
+  }
+skip_filter:
+
+  /* set the exposure time, if needed */
+  if (descriptor[0].ExptimeSelect && (descriptor[0].type != T_DARK)) {
+    fprintf (stderr, "exposure time is not allowed for type %s\n", get_type_name(descriptor[0].type));
+    exit (1);
+  }
+  if (!descriptor[0].ExptimeSelect && (descriptor[0].type == T_DARK)) {
+    if (!gfits_scan (&header, ExptimeKeyword,  "%f", 1, &descriptor[0].Exptime)) {
+      fprintf (stderr, "ERROR: failure to read %s from header\n", ExptimeKeyword);
+      exit (1);
+    }
+  }
+
+  /* set the image ID (if not supplied) based on the camera header */
+  if (descriptor[0].imageID == NULL) {
+    if (gfits_scan (&header, "CRUNID",   "%s", 1, line)) {
+      descriptor[0].imageID = strcreate (line);
+    }
+  }
+  if (descriptor[0].imageID == NULL) {
+    descriptor[0].imageID = strcreate ("test");
+  }  
+  
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/match.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/match.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/match.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include "imregister.h"
+# include "detrend.h"
+
+Match *MatchCriteria (DetReg *image, int Nimage, int *nmatch) {
+
+  int i;
+  int Nmatch, NMATCH;
+  Match result, *match;
+
+  /* find entries that matches criteria */
+  Nmatch = 0;
+  NMATCH = 100;
+  ALLOCATE (match, Match, NMATCH);
+
+  /* find set of images that matches criteria */
+  for (i = 0; i < Nimage; i++) {
+    result = CheckCriteria (&image[i]);
+    if (result.state == MATCH_NONE) continue;
+
+    result.image = i;
+    match[Nmatch] = result;
+    Nmatch ++;
+    if (Nmatch == NMATCH) {
+      NMATCH += 100;
+      REALLOCATE (match, Match, NMATCH);
+    }
+  }
+
+  *nmatch = Nmatch;
+  return (match);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/modify.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/modify.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/modify.c	(revision 22322)
@@ -0,0 +1,58 @@
+# include "imregister.h"
+# include "detrend.h"
+
+int ModifySubset (FITS_DB *db, DetReg *image, int Nimage, Match *match, int Nmatch) {
+  
+  int i, j, value;
+  int *list;
+  
+  value = M_UNDEF;
+
+  ALLOCATE (list, int, Nimage);
+  if (!strcasecmp (output.ModifyEntry, "mode")) {
+    value = get_image_mode (output.ModifyValue);
+    if (value == M_UNDEF) {
+      fprintf (stderr, "ERROR: invalid image mode %s\n", output.ModifyValue);
+      exit (1);
+    }
+  }
+  if (!strcasecmp (output.ModifyEntry, "order")) {
+    value = atoi (output.ModifyValue);
+  }
+
+  /* list matched images */
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j].image;
+    list[j] = i;
+    
+    if (!strcasecmp (output.ModifyEntry, "label")) {
+      snprintf (image[i].label, 64, "%s", output.ModifyValue);
+    }
+    if (!strcasecmp (output.ModifyEntry, "order")) {
+      image[i].Norder = value;
+    }
+    if (!strcasecmp (output.ModifyEntry, "mode")) {
+      image[i].mode   = value;
+    }
+    if (!strcasecmp (output.ModifyEntry, "tstart")) {
+      image[i].tstart = output.TimeValue;
+    }
+    if (!strcasecmp (output.ModifyEntry, "tstop")) {
+      image[i].tstop  = output.TimeValue;
+    }
+  }
+
+  /** we may later want to pull this out and put it elsewhere **/
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, list, Nmatch);
+  for (i = 0; i < Nmatch; i++) {
+    gfits_convert_DetReg ((DetReg *) db[0].vtable.buffer[i], sizeof (DetReg), 1);
+  }
+  gfits_db_update (db);
+  gfits_db_close (db);
+  gfits_db_free (db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/mosaic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/mosaic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/mosaic.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "imregister.h"
+# include "detrend.h"
+
+Criteria *MosaicCriteria (Criteria base, char *filename, int *ncrit) {
+
+  int i, Ncrit;
+  char line[80];
+  Header header;
+  Criteria *crit;
+
+  Ncrit = 1;
+  ALLOCATE (crit, Criteria, Ncrit);
+
+  /* load options from the image header */
+  if (!gfits_read_header (filename, &header)) {
+    if (output.verbose) fprintf (stderr, "ERROR: trouble reading image header\n");
+    exit (1);
+  }
+
+  /* get Time from header */
+  base.tstop = base.tstart = parse_time (&header);
+  base.TimeSelect = TRUE;
+
+  /* get filter from header */
+  if (!gfits_scan (&header, FilterKeyword, "%s", 1, line)) {
+    fprintf (stderr, "ERROR: trouble reading FILTER from header\n");
+    exit (1);
+  }
+  for (i = 0; i < strlen (line); i++) { if (isspace (line[i])) line[i] = '.'; }
+  for (i = 0; (i < NFILTER) && (base.Filter == FILTER_NONE); i++) {
+    if (!strcasecmp (line, filtername[i])) {
+      base.Filter = filternum[i];
+    }
+  }      
+  if (base.Filter == FILTER_NONE) {
+    fprintf (stderr, "ERROR: invalid filter %s\n", line);
+    exit (1);
+  }
+
+  /* get exptime from header */
+  if (!gfits_scan (&header, ExptimeKeyword, "%f", 1, &base.Exptime)) {
+    fprintf (stderr, "ERROR: trouble reading EXPTIME from header\n");
+    exit (1);
+  }
+  
+  /* other settings implied by -mosaic */
+  base.CCDSelect = FALSE;
+  if ((base.Type != T_DARK) && (base.Type != T_BIAS) && (base.Type != T_MASK)) {
+    base.FilterSelect = TRUE;
+  } else {
+    base.FilterSelect = FALSE;
+  }      
+  if (base.Type == T_DARK) {
+    base.ExptimeSelect = TRUE;
+  } else {
+    base.ExptimeSelect = FALSE;
+  }      
+  output.Select = TRUE;
+
+  *ncrit = Ncrit;
+  crit[0] = base;
+  return (crit);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/output.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/output.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/output.c	(revision 22322)
@@ -0,0 +1,289 @@
+# include "imregister.h"
+# include "detrend.h"
+
+char *RandomPath (char *dBPath);
+extern double drand48();
+
+int OutputSubset (DetReg *image, int Nimage, Match *match, int Nmatch) {
+
+  if (output.table != (char *) NULL) {
+    DumpFitsTable (output.table, image, match, Nmatch);
+  } 
+  if (output.bintable != (char *) NULL) {
+    DumpFitsBintable (output.bintable, image, match, Nmatch);
+  } 
+  PrintSubset (image, match, Nmatch);
+  if (output.verbose) fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
+
+/* write out complete binary FITS table in format of db */
+int DumpFitsBintable (char *filename, DetReg *image, Match *match, int Nmatch) {
+
+  int i, j;
+  FILE *f;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  DetReg *subset;
+
+  ALLOCATE (subset, DetReg, MAX (1, Nmatch));
+  for (i = 0; i < Nmatch; i++){
+    j = match[i].image;
+    memcpy (&subset[i], &image[j], sizeof (DetReg));
+  }
+
+  /* open file for output */
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open output file %s\n", filename);
+    exit (1);
+  }
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+
+  ftable.header = &theader;
+  gfits_table_set_DetReg (&ftable, subset, Nmatch);
+
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &ftable);
+  fclose (f);
+  exit (0);
+}
+
+int DumpFitsTable (char *filename, DetReg *detdata, Match *match, int Nmatch) {
+  
+  Header header, theader;
+  Matrix matrix;
+  FTable table;
+  DetReg *newdata;
+  FILE *f;
+  char *startstr, *stopstr, *regstr, *line, key[33], ccdinfo[16];
+  char *filtstr, *typestr, *modestr, *ccdstr, *datestr, *p;
+  int i;
+  time_t tsecond;
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+  
+  /* create table header */
+  gfits_create_table_header (&theader, "TABLE", "MASTER_DETREND");
+      
+  /* add current date/time to header */
+  ohana_str_to_time ("now", &tsecond);
+  datestr = ohana_sec_to_date (tsecond);
+  gfits_modify (&header,  "DATE", "%s", 1, datestr);
+  gfits_modify (&theader, "DATE", "%s", 1, datestr);
+  free (datestr);
+
+  /* define table layout */
+  gfits_define_table_column (&theader, "A32",  "KEY",        "unique identifier",         "");
+  gfits_define_table_column (&theader, "A20",  "START_TIME", "start time of measurement", "yyyy/mm/dd,hh:mm:ss");
+  gfits_define_table_column (&theader, "A20",  "STOP_TIME",  "stop time of measurement",  "yyyy/mm/dd,hh:mm:ss");
+  gfits_define_table_column (&theader, "A20",  "REG_TIME",   "time of registration",      "yyyy/mm/dd,hh:mm:ss");
+  gfits_define_table_column (&theader, "F7.1", "EXPTIME",    "exposure time",             "seconds"); 
+  gfits_define_table_column (&theader, "A10",  "IMAGETYP",   "detrend type",              "");
+  gfits_define_table_column (&theader, "A10",  "FILTER",     "filter name",               "");
+  gfits_define_table_column (&theader, "A7",   "CCDINFO",    "ccd information",                  "");
+  gfits_define_table_column (&theader, "A7",   "MODE",       "data format mode",                  "");
+  gfits_define_table_column (&theader, "I3",   "VERSION",    "image version number",      "");
+  gfits_define_table_column (&theader, "I3",   "ORDER",      "selection order",           "");
+  gfits_define_table_column (&theader, "A64",  "LABEL",      "data label",                "");
+  gfits_define_table_column (&theader, "A256", "PATH",       "filename in db",            "");
+  
+  /* define TNULL, TNVAL values */
+  gfits_modify (&theader, "TNULL1",  "%s", 1, "NULL");  /* KEY        */
+  gfits_modify (&theader, "TNULL2",  "%s", 1, "NULL");  /* START_TIME */
+  gfits_modify (&theader, "TNULL3",  "%s", 1, "NULL");  /* STOP_TIME  */
+  gfits_modify (&theader, "TNULL4",  "%s", 1, "NULL");  /* REG_TIME   */
+  gfits_modify (&theader, "TNULL5",  "%s", 1, "NaN");   /* EXPTIME    */
+  gfits_modify (&theader, "TNULL6",  "%s", 1, "NULL");  /* IMAGETYP   */
+  gfits_modify (&theader, "TNULL7",  "%s", 1, "NULL");  /* FILTER     */
+  gfits_modify (&theader, "TNULL8",  "%s", 1, "NULL");  /* CCDINFO    */
+  gfits_modify (&theader, "TNULL9",  "%s", 1, "NULL");  /* MODE       */
+  gfits_modify (&theader, "TNULL10", "%s", 1, "-100");  /* VERSION    */
+  gfits_modify (&theader, "TNULL11", "%s", 1, "-100");  /* ORDER      */
+  gfits_modify (&theader, "TNULL12", "%s", 1, "NULL");  /* LABEL      */
+  gfits_modify (&theader, "TNULL13", "%s", 1, "NULL");  /* PATH       */
+
+  gfits_modify (&theader, "TNVAL1",  "%s", 1, "NA");    /* KEY        */
+  gfits_modify (&theader, "TNVAL2",  "%s", 1, "NA");    /* START_TIME */
+  gfits_modify (&theader, "TNVAL3",  "%s", 1, "NA");    /* STOP_TIME  */
+  gfits_modify (&theader, "TNVAL4",  "%s", 1, "NA");    /* REG_TIME   */
+  gfits_modify (&theader, "TNVAL5",  "%s", 1, "Inf");   /* EXPTIME    */
+  gfits_modify (&theader, "TNVAL6",  "%s", 1, "NA");    /* IMAGETYP   */
+  gfits_modify (&theader, "TNVAL7",  "%s", 1, "NA");    /* FILTER     */
+  gfits_modify (&theader, "TNVAL8",  "%s", 1, "NA");    /* CCDINFO    */
+  gfits_modify (&theader, "TNVAL9",  "%s", 1, "NA");    /* MODE       */
+  gfits_modify (&theader, "TNVAL10", "%s", 1, "-200");  /* VERSION    */
+  gfits_modify (&theader, "TNVAL11", "%s", 1, "-200");  /* ORDER      */
+  gfits_modify (&theader, "TNVAL12", "%s", 1, "NA");    /* LABEL      */
+  gfits_modify (&theader, "TNVAL13", "%s", 1, "NA");    /* PATH       */
+
+  /* create table, add data values */
+  gfits_create_table (&theader, &table);
+  
+  /* add data to table */
+  for (i = 0; i < Nmatch; i++) {
+    newdata = &detdata[match[i].image];
+
+    /* key = 02Bk02.flat.V.00.00 */
+    p = strrchr (newdata[0].filename, '/');
+    if (p == (char *) NULL) {
+      p = newdata[0].filename;
+    } else {
+      p ++;
+    }
+    bzero (key, 33);
+    strncpy (key, p, 32);
+    if ((p = strrchr (key, '.')) != (char *) NULL) *p = 0;
+
+    startstr = ohana_sec_to_date (newdata[0].tstart);
+    stopstr  = ohana_sec_to_date (newdata[0].tstop);
+    regstr   = ohana_sec_to_date (newdata[0].treg);
+    typestr  = get_type_name(newdata[0].type);
+    modestr  = get_mode_name(newdata[0].mode);
+    filtstr  = filterhash[newdata[0].filter];
+
+    if (newdata[0].mode == M_SPLIT) {
+      ccdstr   = ccds[newdata[0].ccd];
+    } else {
+      sprintf (ccdinfo, "%-3d", newdata[0].ccd);
+      ccdstr   = ccdinfo;
+    }
+
+    line = gfits_table_print (&table, key, startstr, stopstr, regstr, 
+			     newdata[0].exptime, typestr, filtstr, ccdstr, modestr,
+			     newdata[0].Nentry, newdata[0].Norder, 
+			     newdata[0].label, newdata[0].filename);
+
+    gfits_add_rows (&table, line, 1, strlen(line));
+    free (line);
+    free (startstr);
+    free (stopstr);
+    free (regstr);
+  }
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "Failure writing fits table\n");
+    return (FALSE);
+  }
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &table);
+  fclose (f);
+  exit (0);
+}
+
+/* Select, TimeMode are global */
+int PrintSubset (DetReg *detdata, Match *match, int Nmatch) {
+  
+  char *dBPath, *Path, *typestr, *filtstr, filename[128];
+  char *timestr, *modestr, *ccdstr, ccdinfo[16], ccdformat[16];
+  int i, j, Nc;
+  struct timeval now;
+  long A;
+
+  gettimeofday (&now, NULL);
+  A = now.tv_usec + now.tv_sec;
+  srand48 (A);
+
+  Nc = strlen (ccds[0]);
+  sprintf (ccdformat, "%%%dd", Nc);
+  dBPath = get_dBPath ();
+
+  /* list matched images */
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j].image;
+    switch (output.TimeMode) {
+    case START:
+      timestr = ohana_sec_to_date (detdata[i].tstart);
+      break;
+    case STOP:
+      timestr = ohana_sec_to_date (detdata[i].tstop);
+      break;
+    case REG:
+      timestr = ohana_sec_to_date (detdata[i].treg);
+      break;
+    default:
+      if (output.verbose) fprintf (stderr, "ERROR: bad TimeMode\n");
+      exit (1);
+    }
+
+    typestr = get_type_name(detdata[i].type);
+    modestr = get_mode_name(detdata[i].mode);
+    filtstr = filterhash[MIN (MAX (detdata[i].filter, 0), NFILTER - 1)];
+
+    if ((detdata[i].mode == M_SPLIT) && (detdata[i].ccd >= -1) && (detdata[i].ccd < Nccd)) {
+      ccdstr   = ccds[detdata[i].ccd];
+    } else {
+      sprintf (ccdinfo, ccdformat, detdata[i].ccd);
+      ccdstr   = ccdinfo;
+    }
+
+    /* output mode (Select vs List) */
+    if (output.Chipname && criteria[0].CCDSelect && detdata[i].mode == M_MEF) {
+      snprintf (filename, 128, "%s[%s]", detdata[i].filename, ccds[criteria[0].CCD]);
+    } else {
+      strcpy (filename, detdata[i].filename);
+    }
+
+    if (output.Select) {
+      if (detdata[i].altpath) {
+	Path = RandomPath (dBPath);
+      } else {
+	Path = dBPath;
+      }
+      fprintf (stdout, "%s/%s\n", Path, filename);
+    } else {
+      fprintf (stdout, "%-40s = %19s %7s %6s %6s %s %2d %2d %6.1f  %20s  %1d\n", 
+	       filename, timestr, modestr, typestr, filtstr, ccdstr,
+	       detdata[i].Nentry, detdata[i].Norder, detdata[i].exptime, detdata[i].label, detdata[i].altpath);
+    }
+    free (timestr);
+  }  
+  return (TRUE);
+}
+
+char *RandomPath (char *dBPath) {
+  
+  int N;
+
+  N = (NDetrendAltDB + 0.99)*drand48();
+  if (N == NDetrendAltDB) return (dBPath);
+  return (DetrendAltDB[N]);
+}
+
+int PrintCriteria () {
+
+  int i;
+  char *start, *stop;
+
+  for (i = 0; i < Ncriteria; i++) {
+    start = ohana_sec_to_date (criteria[i].tstart);
+    stop  = ohana_sec_to_date (criteria[i].tstop);
+    
+    fprintf (stdout, "%19s %19s %7s %6s %s %6.1f\n", 
+	     start, stop, get_type_name(criteria[i].Type), filterhash[criteria[i].Filter], 
+	     ccds[criteria[i].CCD], criteria[i].Exptime);
+
+    free (start);
+    free (stop);
+  }
+
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/recipe.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/recipe.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/recipe.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "imregister.h"
+# include "detrend.h"
+
+Criteria *ExpandRecipe (Criteria *base, int *Ncrit) {
+
+  int i, N, Ns, NS;
+  Criteria *crit;
+  int Nbase;
+  
+  Nbase = *Ncrit;
+  NS = Ns = 0;
+  ALLOCATE (crit, Criteria, 1);
+
+  for (N = 0; N < Nbase; N++) {
+
+    RecipeType = LoadRecipe (filterhash[base[N].Filter], &Nrecipe);
+
+    NS += Nrecipe;
+    REALLOCATE (crit, Criteria, NS);
+    
+    for (i = 0; i < Nrecipe; i++) {
+      crit[Ns] = base[N];
+      crit[Ns].TypeSelect = TRUE;
+      crit[Ns].Type = get_image_type (RecipeType[i]);
+      if (crit[Ns].Type == T_UNDEF) { 
+	fprintf (stderr, "ERROR: invalid image type %s\n", RecipeType[i]);
+	exit (1);
+      }
+      if (!strcasecmp (RecipeType[i], "bias") ||
+	  !strcasecmp (RecipeType[i], "dark") ||
+	  !strcasecmp (RecipeType[i], "mask")) {
+	crit[Ns].FilterSelect = FALSE;
+      }
+      if (!strcasecmp (RecipeType[i], "flat") ||
+	  !strcasecmp (RecipeType[i], "scatter") ||
+	  !strcasecmp (RecipeType[i], "fringe") ||
+	  !strcasecmp (RecipeType[i], "frpts") ||
+	  !strcasecmp (RecipeType[i], "modes")) {
+	crit[Ns].FilterSelect = TRUE;
+      }
+      if (!strcasecmp (RecipeType[i], "modes")) {
+	crit[Ns].CCDSelect = FALSE;
+      }
+      /*      
+      if (!strcasecmp (RecipeType[i], "dark")) {
+      crit[Ns].ExptimeSelect = TRUE;
+      } 
+      */
+      Ns ++;
+    }
+  }
+
+  *Ncrit = Ns;
+  return (crit);
+}
+
+char **LoadRecipe (char *filter, int *nrecipe) {
+  
+  char **RecipeType;
+  int Nrecipe, NRECIPE;
+
+  int Nfield, found;
+  char *c, *p1, line[256], list[256], Filter[64];
+  FILE *f;
+  
+  /* open filter list file */
+  f = fopen (RecipeFile, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "error reading recipe %s\n", RecipeFile);
+    exit (1);
+  }
+
+  /* allocate dataspace needed */
+  NRECIPE = 10;
+  Nrecipe = 0;
+  ALLOCATE (RecipeType, char *, NRECIPE);
+
+  /* load data from file */
+  found = FALSE;
+  while (!found && (scan_line (f, line) != EOF)) {
+    for (c = line; isspace (*c); c++);
+    if (*c == '#') continue;
+    Nfield = sscanf (c, "%s %s", Filter, list);
+    if (Nfield != 2) { continue; }
+
+    if (!strcasecmp (filter, Filter)) found = TRUE;
+  }
+  fclose (f);  
+
+  if (!found) {
+    fprintf (stderr, "can't find filter %s\n", filter);
+    exit (1);
+  }
+
+  /* list contains word,word,word - parse these words to RecipeType */
+  p1 = list;
+  while ((c = strchr (p1, ',')) != (char *) NULL) {
+    *c = 0;
+    RecipeType[Nrecipe] = strcreate (p1);  
+    p1 = c + 1;
+    Nrecipe ++;
+    if (Nrecipe == NRECIPE) {
+      NRECIPE += 10;
+      REALLOCATE (RecipeType, char *, NRECIPE);
+    }
+  }
+  if (*p1) {
+    RecipeType[Nrecipe] = strcreate (p1);  
+    Nrecipe ++;
+  }    
+  *nrecipe = Nrecipe;
+  return (RecipeType);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/select.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/select.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/select.c	(revision 22322)
@@ -0,0 +1,90 @@
+# include "imregister.h"
+# include "detrend.h"
+
+Match SelectEntry (DetReg *image, int Nimage, Match *list, int Nlist, Criteria *crit) {
+
+  /* force a single selection */
+
+  /* at this point, all external criteria for the given detrend images
+     are equivalently good (ie, date range, filters, type, etc).  the 
+     selection here is based on other options: 
+     1) Norder         - user assigned value to force selection
+     2) tstart         - start valid time
+     3) Nentry         - version number
+     4) MatchNumber    - method to force selection 
+  */
+
+  int i, j, entry, order, matchnum;
+  int nmatch, Nmatch;
+  unsigned int tstart;
+  Match *match, *tmatch, answer;
+
+  Nmatch = Nlist;
+  match = list;
+
+  /* select all images with the same, maximum Norder */
+  nmatch = 0;
+  ALLOCATE (tmatch, Match, Nmatch);
+  order = image[match[0].image].Norder;
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j].image;
+    order = MAX (order, image[i].Norder);
+  }
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j].image;
+    if (order <= image[i].Norder) {
+      tmatch[nmatch] = match[j];
+      nmatch ++;
+    }
+  }
+  match = tmatch;
+  Nmatch = nmatch;
+
+  /* select all images with the same, maximum tstart */
+  nmatch = 0;
+  ALLOCATE (tmatch, Match, Nmatch);
+  tstart = image[match[0].image].tstart;
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j].image;
+    tstart = MAX (tstart, image[i].tstart);
+  }
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j].image;
+    if (tstart <= image[i].tstart) {
+      tmatch[nmatch] = match[j];
+      nmatch ++;
+    }
+  }
+  free (match);
+  match = tmatch;
+  Nmatch = nmatch;
+
+  /* select all images with the same, maximum Nentry */
+  nmatch = 0;
+  ALLOCATE (tmatch, Match, Nmatch);
+  entry = image[match[0].image].Nentry;
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j].image;
+    entry = MAX (entry, image[i].Nentry);
+  }
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j].image;
+    if (entry <= image[i].Nentry) {
+      tmatch[nmatch] = match[j];
+      nmatch ++;
+    }
+  }
+  free (match);
+  match = tmatch;
+  Nmatch = nmatch;
+
+  /* if there are multiple matches, select MatchNumber entry 
+     0-Nmatch-1 , or -1 -> -Nmatch (saturate at ends) */
+  matchnum = crit[match[0].crit].MatchNumber;
+  if (matchnum < 0) matchnum += Nmatch;
+  matchnum = MAX (0, MIN (Nmatch - 1, matchnum));
+
+  answer = match[matchnum];
+  free (match);
+  return (answer);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/unique.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/unique.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/detrend/unique.c	(revision 22322)
@@ -0,0 +1,123 @@
+# include "imregister.h"
+# include "detrend.h"
+
+/* define a list of best images which have unique properties */ 
+Match *UniqueSubset (DetReg *image, int Nimage, Match *match, int *nmatch) {
+
+  int Nsubset, Nunique, Ncrit, NCRIT;
+  int i, j, N, Nmatch, found;
+  Criteria *crit;
+  Match *subset, *unique, *local;
+
+  Nmatch = *nmatch;
+  ALLOCATE (local, Match, Nmatch);
+
+  /* create a set of complete criteria derived from the images */
+
+  Ncrit = 0;
+  NCRIT = 10;
+  ALLOCATE (crit, Criteria, NCRIT);
+  bzero (crit, (NCRIT - Ncrit)*sizeof(Criteria));
+
+  for (i = 0; i < Nmatch; i++) {
+    
+    local[i] = match[i];
+    N = match[i].image;
+    if (N == -1) continue;
+
+    found = FALSE;
+    for (j = 0; (j < Ncrit) && !found; j++) {
+      if (!cmp_crit (&crit[j], &image[N])) continue;
+      found = TRUE;
+      local[i].crit  = j;
+    }      
+    if (found) continue;
+    local[i].crit  = Ncrit;
+    set_crit (&crit[Ncrit], &image[N]);
+    Ncrit ++;
+    if (Ncrit == NCRIT) {
+      NCRIT += 10;
+      REALLOCATE (crit, Criteria, NCRIT);
+      bzero (&crit[Ncrit], (NCRIT - Ncrit)*sizeof(Criteria));
+    }
+  }
+      
+  /* we now have a set of criteria (crit, Ncrit) which describe the matched images
+     and a set of local matches (local) which link images to crit */
+
+  ALLOCATE (subset, Match, Nmatch);
+  ALLOCATE (unique, Match, Nmatch);
+
+  Nunique = 0;
+  for (i = 0; i < Ncrit; i++) {
+
+    /* extract the subset of local matches which are associated with crit[i] */
+    Nsubset= 0;
+    for (j = 0; j < Nmatch; j++) {
+      if (local[j].crit  != i) continue;
+      subset[Nsubset] = local[j];
+      Nsubset ++;
+    }
+
+    /* subset, Nsubset has a unique list of matched images */
+    unique[Nunique] = SelectEntry (image, Nimage, subset, Nsubset, crit);
+    Nunique ++;
+  }
+  free (subset);
+  free (local);
+  free (match);
+
+  match = unique;
+  *nmatch = Nunique;
+
+  return (match);
+}
+
+
+/* 
+   return list of all images that have the same value of:
+   criteria = tstart, tstop, filter, ccd, type, exptime
+*/
+
+
+int cmp_crit (Criteria *crit, DetReg *image) {
+
+  /* image and criteria need to match on:
+     tstart, tstop, filter, ccd, type, exptime
+  */
+  
+  if (crit[0].Filter  != image[0].filter ) return (FALSE);
+  if (crit[0].CCD     != image[0].ccd    ) return (FALSE);
+  if (crit[0].Type    != image[0].type   ) return (FALSE);
+  /* if (crit[0].Exptime != image[0].exptime) return (FALSE); */
+
+  if (crit[0].tstart  > image[0].tstop) return (FALSE);
+  if (crit[0].tstop   < image[0].tstart) return (FALSE);
+
+  /* we have overlapping time ranges.  set crit[0] to have the 
+     maximum of these time ranges */
+
+  crit[0].tstop  = MIN (crit[0].tstop,  image[0].tstop);
+  crit[0].tstart = MAX (crit[0].tstart, image[0].tstart);
+
+  return (TRUE);
+
+}
+
+int set_crit (Criteria *crit, DetReg *image) {
+
+  /* image and criteria need to match on:
+     tstart, tstop, filter, ccd, type, exptime
+  */
+
+  crit[0].tstart  = image[0].tstart;
+  crit[0].tstop   = image[0].tstop;  
+  crit[0].Filter  = image[0].filter; 
+  crit[0].CCD     = image[0].ccd;    
+  crit[0].Type    = image[0].type;   
+  crit[0].Exptime = image[0].exptime;
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,34 @@
+
+- imregister-1-5 : 2006.08.23
+  * converted to gfits APIs (forces libfits 1.6)
+
+- imregister-1-4:
+  * dropped IMAGE_CATALOG from config
+  * fixed imageID if not in header
+  * changed mkdirhier to updated version (libohana 1.7)
+
+- imregister-1-3:
+  * modifications to use the new DVO load functions (libdvo-1-0)
+  * minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+- imregister-1-2:
+  * substantial changes to use autocoded tables
+  * reorgainzation of db I/F code to use fits_db functions
+
+- imregister-1-1:
+  * I made a lot of very minor changes to cleanup the FITS database
+  * interactions.  These should not have any impact on behavior.
+
+- imregister-1-0:
+  * I've added the newest versions of imregister to the CVS tree with the
+  * base tag imregister-1-0 (should have used imregister-3-0? still
+  * learning about revision control).  
+
+comment from before importing to CVS:
+  imregister-3.0 represents a completely new organization of these
+  programs, with the twin goals of making the FITS table handling more
+  cleanly encapsulated and of easing the addition of a mysql database
+  engine in place of the FITS tables.  As programs from imregister-1.0
+  are adapted to match this structure, they will be moved into the
+  imregister-3.0 directory.  
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/Compatibility
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/Compatibility	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/Compatibility	(revision 22322)
@@ -0,0 +1,58 @@
+
+transearch-1.0: 
+  v1: ok
+  v2: ok
+
+transearch-2.0: 
+  v1: not compatible, exits gracefully
+  v2: ok
+
+transreg-1.0:
+  v1: ok
+  v2: not compatible, exits gracefully
+
+transreg-1.0:
+  v1: not compatible, exits gracefully
+  v2: ok
+
+photsearch-1.0:
+  v1: ok
+  v2: ok
+
+photsearch-2.0:
+  v1: not compatible, exits gracefully
+  v2: ok
+
+photreg-1.0:
+  v1: ok
+  v2: ok [creates v1]
+
+photreg-2.0:
+  v1: not compatible, exits gracefully
+  v2: ok
+
+imsearch-1.0:
+  v1, v2: ok
+  v3: not compatible, exits gracefully
+
+imregister-1.0:
+  v1, v2: ok
+  v3: not compatible ****
+
+imregister-2.0:
+  v1, v2: ok
+  v3: not compatible, exits gracefully
+
+imsort-1.0:
+  v1, v2: ok
+  v3: not compatible ****
+
+imsort-2.0:
+  v1, v2: ok
+  v3: not compatible ***
+
+imregtable-3.0:
+  v1, v2: not compatibe, exits gracefully
+  v3: ok
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/autocode.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/autocode.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/autocode.txt	(revision 22322)
@@ -0,0 +1,20 @@
+
+I have been working to convert these functions to use libautocode to
+generate the table I/O APIs automatically.  A single table defines the
+layout of the table as well as the C structure.  A perl script uses a
+template to generate code to perform the byte swaping, to construct
+the table and add data or extract data from the table.
+
+I discovered that 'photreg' was mis-coded in Fread: it was not
+swapping the entry for refcode.  I have put in a 'rawshort' method to
+handle this for now, but it is fragile: it will only work on
+big-endian machines.  The tables need to be updated and the entry
+returned to short.  This can be done with the existing convert
+operations.
+
+as of 2005/03/30, I have converted the spsearch, photsearch functions,
+but I have not completely tested them.
+
+I am having some errors in the table creation: some lines are dropping
+the leading ' in the wrong place.  
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/dbtools.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/dbtools.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/dbtools.txt	(revision 22322)
@@ -0,0 +1,41 @@
+
+Elixir uses several databases to store a variety of information.
+One of these databases is the photometry database, with several
+interface tools (status, nrphot, addstar, etc).  The other databases
+are manipulated with the tools found in this directory.  In addition,
+tools used to abstract the camera description, photcodes, and filters
+are included here.
+
+imregister   : add images to the image registration database (imreg.db)
+
+imsearch     : find images in imreg.db
+
+imsort       : add images to imreg.db and pass to elixir systems
+
+imstatreg    : update stats to imreg.db (fwhm, flux, bias)
+
+detregister  : add images to the detrend database
+
+detsearch    : find images in the detrend database
+
+photreg      : add photometry datapoints to zeropoint db (phot.db)
+
+photsearch   : search phot.db
+
+transreg     : add transparency points to database (trans.db)
+
+transsearch  : search trans.db
+
+photcode     : find photcode for given image
+
+filtnames    : filter name lookups
+
+cameraconfig : get configuration information for camera
+
+As in all Elixir programs, a reference to a specific CCD in a MEF
+image can be done with (file) (ccd) MEF, where the value of 'ccd' is
+one of the valid extension names, associated in the header with the
+keyword defined by CCDNUM-KEYWORD.  In addition, searches (imsearch,
+detsearch) may be restricted to specific ccds with the -ccd flag,
+which also takes the extension name ID.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/det.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/det.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/det.txt	(revision 22322)
@@ -0,0 +1,46 @@
+
+detregister:
+-time")) {
+-label")) {
+-ID")) {
+-order")) {
+-type")) {
+-ccd")) {
+-exptime")) {
+-filter")) {
+
+detsearch:
+--help		: ok
+-ccd		: ok
+-close		: ok
+-exptime	: ok
+-filter		: ok
+-h		: ok
+-image		: ok
+-label		: ok
+-mosaic		: ok
+-recipe		: ok
+-select		: ok
+-time		: ok
+-trange		: ok
+-treg		: ok
+-tstop		: ok
+-type		: ok
+-ve		: ok
+-quiet		: ok
+
+-match		: ?
+-entry		: ok
+
+-fits		: ok
+-binfits	: ok
+-del		: ok
+-delete		: ok
+-modify		: ok
+
+--help : clean up usage statement
+
+option behavior: 
+
+-close implies -time or -trange
+-select returns best for all detrend base selection
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/detsearch.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/detsearch.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/detsearch.txt	(revision 22322)
@@ -0,0 +1,74 @@
+
+detregister / detsearch MEF support:
+
+issue of database image format:
+
+filename			type	CCD  mode
+modes/2001A.modes.R.12.00.fits  modes   12   modes
+flat/R/02Bk06.flat.R.00.00.fits flat    00   split
+flat/R/03Ak01.flat.R.00.fits    flat    12   mef
+
+detsearch / detregister concepts:
+
+selection rules:
+
+ match all simple filters 
+
+ -image foo.fits[ccd 08] matches (split 08) or (mef NCCD)
+ 
+ if CCD is defined, return foo.fits[ext] if foo.fits is MEF
+
+simple filters:
+
+ -ccd (N)                       : match ccd
+ -time yyyy/mm/dd,HH:MM:SS      : match time
+ -trange (start) (stop)         : match time range
+ -entry (value)                 : match entry (entry == version number)
+ -label (word)                  : match this label
+ -filter (name)                 : match filter
+ -exptime (value)               : match exposure time
+
+complex rules:
+ -image (filename) (ccd) (mode) : determine filters from image
+
+ -mosaic (filename)		: determine filters from image
+
+ -recipe			: determine filters from image
+
+ -type (type)                   : match type, limit valid filters
+
+ -match (value)                 : only list the Nth matched entry ???
+
+ -close                         : allow non-perfect matches
+
+ -select                        : select 'best' match
+
+display rules:
+ -tstop                         : display end of valid time range
+ -treg                          : display time of image registration
+ -ve                            : Elixir verbose (SUCCESS / ERROR) 
+ -quiet                         : Elixir quiet (no SUCCESS / ERROR) 
+
+
+behaviour rules:
+ -modify (entry) (value)        : change entry for matched images to value
+  [possible -modify entries: label, order, tstart, tstop]
+
+ -del    : delete the matched entries
+ -delete : delete the matched entries
+
+
+-image sets:  -filter, -ccd, -time, -exptime
+-mosaic sets: -filter, -time, -exptime, (-ccds?)
+-recipe sets -type, for each type, sets -
+
+possible values for -type:  mask, bias, dark, flat, fringe, scatter, modes
+
+valid filters for type selections:
+  mask:    -ccd, -label, -entry, -time || -trange
+  bias:    -ccd, -label, -entry, -time || -trange
+  dark:    -ccd, -label, -entry, -time || -trange, -exptime
+  flat:    -ccd, -label, -entry, -time || -trange, -filter
+  fringe:  -ccd, -label, -entry, -time || -trange, -filter
+  scatter: -ccd, -label, -entry, -time || -trange, -filter
+  modes:   -ccd, -label, -entry, -time || -trange, -filter
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/imreg.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/imreg.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/imreg.txt	(revision 22322)
@@ -0,0 +1,25 @@
+
+command-line options from imregister:
+-split	     - OK
+-noreg	     - OK
+
+command-line options from imsearch:
+-type	     - ok
+-etime	     - ok
+-mode	     - ok
+-ccd	     - ok
+-filter	     - ok
+-name	     - ok
+
+-treg	     -ok
+-seq	     -ok
+-pt	     -ok
+-table	     -ok
+-bintable    -ok
+-del	     -ok
+-delete	     -ok
+-newpath     -ok
+-mef2split   -ok 
+-split2mef   -ok
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/imregister.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/imregister.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/imregister.txt	(revision 22322)
@@ -0,0 +1,63 @@
+
+This directory contains several routines which are related to
+maintaining a small, elixir-internal databases about the images which
+have been obtained.
+
+This database can be used to find the location on disk of the images
+which we have analysed, or to display various statistics about images,
+or to find types of images which have been obtained.  
+
+The basic program is 'imregister', which places the basic information
+about an image in the database, as determined from the header.  The
+program is invoked with: {\tt imregister (filename) [-split]}.  The
+optional flag is used to tell the program to distinguish the
+individual (SPLIT) frames of a mosaic CCD from an individual CCD which
+should be treated as an isolated image.  
+
+The second program is 'imsort', which does the same task as
+'imregister', but it also sends a trigger to the IMSTAT elixir and if
+needed to the PTOLEMY elixir.  For this program, the -split flag makes
+a difference in how these images are treated in the elixir processes.
+The basic point is that a SINGLE image /fullpath/filename.fits
+produces analysis files of the form /newpath/filename.ext while a
+SPLIT image will have the form /fullpath/word/wordNN.fits and output
+files of the form /newpath/word/wordNN.ext.  
+
+May 11, 2000
+
+I have been working to clean up some of the details of the Elixir
+system.  There are three big issues I have been attempting to address.
+First, I have wanted to create a single set of Elixirs for both MEF
+and SPLIT (and even SINGLE) images.  I also want to minimize the
+number of programs are required to know something special about our
+system, our camera, or our method of naming files.  Finally, I have
+been trying to automate the selection of the appropriate detrend data.
+These are connected problems, in many ways.
+
+Here, in order, are the programs (and scripts) needed to run the
+Elixir system for which these issues are relevant:
+
+elixir.fork:  this program is launched by the camera to introduce a
+	      new image to the system.  It must understand the
+	      difference between SPLIT and MEF, and also the naming
+	      convention.
+
+imsort:	      this program adds the image to the database and passes a
+	      trigger to the elixirs ptolemy and imstats.  imsort
+	      needs to distinguish SPLIT, MEF, and SINGLE images, and
+	      it must know the naming convention.  
+
+elixir (ptolemy): this script is now independent of the mode and
+		  naming issues
+
+elixir (imstats): this script is now independent of the mode and
+		  naming issues
+
+flatten:	  this script needs to distinguish SPLIT and MEF
+		  images (it is told the mode by the input line), but
+		  it does not know the naming convention.
+
+imstats:	  this script needs to distinguish SPLIT and MEF
+		  images (it is told the mode by the input line), but
+		  it does not know the naming convention.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/imtable.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/imtable.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/imtable.txt	(revision 22322)
@@ -0,0 +1,22 @@
+
+Image Table: run @ 6 AM in ert
+
+- for given time period:
+
+* are all images in database? (compare archive db & elixir db)
+* have all images passed imstats?
+
+- complete images should be extracted into a table, sent to CADC
+- missing images should be registered or a warning sent on failure
+  (relink on /data/kapu/elixir/cfh12k)
+
+- nostat images should be sent to imstats
+  (launch imstats to process)
+ 
+* imsearch output format CADC: 
+
+  fits_define_table_column (&theader, "A64",   "FILE",       "filename in db",        "",                              1.0, 0.0);
+  fits_define_table_column (&theader, "F7.1",  "SKY",        "background level",     "counts / pixel",                 1.0, 0.0); 
+  fits_define_table_column (&theader, "F6.1",  "BIAS",       "bias level",           "counts / pixel",                 1.0, 0.0); 
+  fits_define_table_column (&theader, "F5.2",  "FWHM",       "image quality",        "pixels",                         1.0, 0.0); 
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/notes.2.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/notes.2.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/notes.2.txt	(revision 22322)
@@ -0,0 +1,63 @@
+
+problem:  imstatreg is too slow because of network I/O
+
+option 1: add mysql db to imstats system
+
+option 2: create client / server pair for imregister / imstatreg
+
+option 3: create buffer file for network I/O, local daemon for update
+
+1: best soln but will take a while
+2: ok option, but significant programming effort (worse than 1)
+3: easy programming, acceptable
+
+for imstatreg update, we need the only following data:
+
+obstime (unsigned int)
+ccd     (char or int)
+fwhm
+bias
+sky
+ra
+dec
+
+imstatreg -client:
+
+ read image info
+ load temp db
+ append data
+ close
+
+imstatreg -daemon:
+
+ load temp db
+ load main db
+ match images
+ update db
+ close db
+ empty temp db
+
+------------------------------------------------------
+
+detsearch needs optional alternative detrend.db paths
+
+- add column ALTPATH : true / false
+- add option -altpath :
+  - select images
+  - select ALTPATH images
+  - find subset 'add'
+  - find subset 'remove'
+  - 'remove': unlink files & unset ALTPATH
+  - 'add'   : copy files & set ALTPATH
+  
+
+if (ADD)    : all images in match are 'add', no images are 'delete'
+if (DELETE) : all images in match are 'delete', no images are 'add'
+if (UPDATE) : ??
+
+-----------------------------------------------------
+
+photreg / photsearch 
+
+- need to define the target photometry system in the output table.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/notes.old.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/notes.old.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/notes.old.txt	(revision 22322)
@@ -0,0 +1,12 @@
+
+there are some issues with the imregister database that should get
+addressed eventually.  currently, I am loading and saving the entire
+file each time I make a change.  this seems like a bad plan.  when we
+get a few hundred nights of images in there, we will have several
+megabytes to load, change, and save each time.  two things could
+change.  first, imregister (imsort) could only add a record at the end
+of the file.  second, the imstatreg function could find the right
+image and write over the few needed bytes.
+
+should I uses mmap to do the I/O?
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/notes.txt	(revision 22322)
@@ -0,0 +1,101 @@
+
+imsearch & CADC distribution:
+
+i need to create raw image tables for the CADC for only those images
+which have been processed (-proc t) and for which a distribution table
+has not been created.
+
+imsearch -modify help
+imsearch -modify path /oldpath /newpath
+imsearch -modify mode [mef/split] 
+imsearch -modify dist [t/f]
+
+history:
+
+v1 : < 2002.09 
+
+ - FITS table interface had errors, incorrect TFORM values
+ - imreg db used pseudo FITS table
+
+v2 : 2002.09 
+
+ - FITS table interface repaired
+ - imreg db uses pseudo FITS table
+ - unwieldy files which contain many functions (esp detsearch /
+   imsearch)
+
+v3 : 2002.11
+
+ - FITS table interface repaired
+ - imreg db converted to read FITS tables
+ - use of vtable interface introduced
+ - db interactions abstracted to allow for SQL
+ - reorganization of files
+
+v3: functions & dependancies
+
+function             : external : globals               : structs
+
+WriteFIFO            : ohana    :		        :
+LoadCameraConfig     : ohana    : ccd data, keywords	:
+MatchCCDName         : ohana    : ccd data, keywords	:
+ConfigCamera         : ohana    : ccd data, keywords	:
+LoadFilterList       : ohana    : filt data, keywords	:
+MatchFilterList      : ohana    : filt data             :
+ConfigInit           : ohana    : keywords  
+get_fwhm	     : ohana
+load_probes	     : ohana
+parse_time	     : ohana    : keywords
+get_trange_arguments : ohana
+make_backup	     : ohana
+uppercase	     : 
+check_unixtime	     : UNUSED
+sort (various)	     : 
+
+(why aren't these in libohana?)
+dms_to_ddd
+str_to_radec
+chk_time
+str_to_time
+str_to_dtime
+sec_to_jd
+jd_to_sec
+date_to_sec
+Fseek - allow for timeout...
+
+problems:
+
+ imregister2.c: args
+ imsort2.c: args, SubmitImages
+ imstatreg2.c: args
+ photcode.c: ConfigSystem (ConfigCamera ConfigFilters)
+ photreg2.c: make_backup
+ photsearch2.c: DumpFitsTable
+ showiminfo.c: args
+ transearch2: LoadFilters, DumpFitsTable
+ transreg2: make_backup
+
+main programs:
+
+cameraconfig
+convertimreg
+filtnames
+imphotsearch
+imregister2
+imsearch2
+imsort2
+imstatreg2
+photcode
+photreg2
+photsearch2
+showiminfo
+transearch
+transreg
+
+
+dependency types:
+
+external libraries
+local library
+group 
+program-specific functions
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/photdb.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/photdb.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/photdb.dat	(revision 22322)
@@ -0,0 +1,349 @@
+u 2003/02/10,13:00:46  25.3434 25.2800  0.0000    1   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/02/10,13:27:24  25.3435 25.2800  0.0000    1   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/02/10,13:33:10  25.3496 25.2800  0.0000    1   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/02/10,13:38:47  25.3583 25.2800  0.0000    1   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/02/10,13:56:21  25.3455 25.2800  0.0000    1   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/02/24,12:36:34  25.2875 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/02/24,12:37:43  25.2855 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/02/09,13:01:29  25.3035 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/02/09,13:05:05  25.3034 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/02/09,13:06:14  25.3004 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/02/24,12:39:01  25.3197 25.2800  0.0000    1   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+g 2003/02/23,10:10:59  26.4664 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/02/23,10:11:59  26.4654 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/02/23,10:13:00  26.4604 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/02/24,12:00:51  26.4463 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/02/24,12:01:43  26.4393 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/02/24,12:02:35  26.4343 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/02/09,12:47:05  26.4692 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/02/09,12:47:48  26.4642 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/02/09,12:48:40  26.4624 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/02/23,10:17:28  25.9736 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/23,10:20:12  25.9708 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/23,10:20:55  25.9648 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/08,13:30:08  25.9878 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/08,13:30:51  25.9862 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/08,13:31:43  25.9816 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/24,12:07:20  25.9939 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/24,12:10:39  25.9897 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/24,12:09:47  25.9722 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/09,12:26:03  25.9581 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/09,12:26:55  25.9529 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/09,12:27:38  25.9617 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/12,15:18:51  26.0073 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/02/23,10:25:32  25.7384 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/23,10:28:07  25.7415 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/23,10:28:59  25.7465 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/08,13:18:37  25.7317 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/08,13:19:29  25.7307 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/08,13:20:12  25.7289 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/24,12:15:24  25.7579 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/24,12:18:51  25.7489 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/09,13:22:04  25.7434 25.7430  0.0000    3   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/09,13:23:39  25.7397 25.7430  0.0000    3   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/24,12:18:00  25.7399 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/09,13:22:56  25.7318 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/02/23,10:33:44  24.7826 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/02/23,10:36:54  24.7967 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/02/24,12:26:47  24.7562 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/02/09,13:12:43  24.7879 24.8000  0.0000    3   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/02/09,13:13:26  24.7888 24.8000  0.0000    3   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/02/24,12:23:19  24.7178 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/02/24,12:25:46  24.7117 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/02/09,13:11:51  24.7789 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+g 2003/03/24,15:07:46  26.4566 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/03/30,13:12:34  26.4501 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+i 2003/03/25,15:12:48  25.7633 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+r 2003/03/24,15:10:56  25.9917 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/03/25,15:21:27  26.0231 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+u 2003/03/22,15:11:13  25.4630 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/03/23,15:12:14  25.3056 25.2800  0.0000    1   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+z 2003/03/24,14:59:16  24.8271 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/03/30,13:20:55  24.7152 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+g 2003/04/25,09:06:28  26.4030 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/04/26,12:38:26  25.9774 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/04/26,13:14:35  25.9455 25.9780  0.0046    6   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/04/25,08:55:23  25.9287 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/04/27,08:44:44  25.9646 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/04/26,10:44:32  25.9647 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/04/26,13:05:05  26.0053 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/04/27,08:47:28  25.9676 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/04/26,13:06:05  25.9697 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/04/27,09:57:10  25.7930 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/04/25,08:58:07  25.7034 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/04/25,09:00:43  24.7533 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+u 2003/06/09,14:36:14  25.2311 25.2800  0.0071    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/05/30,14:21:41  25.2892 25.2800  0.0150    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+r 2003/06/08,14:43:35  25.9302 25.9780  0.0000    3   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/06/02,07:20:29  25.7291 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/04,06:40:27  25.7566 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/05,06:21:27  25.5921 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/06,05:58:59  25.7441 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/08,14:48:20  25.6812 25.7430  0.0000    3   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/06/02,07:23:22  24.8465 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/09,14:42:25  24.7583 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+u 2003/06/24,14:36:40  25.2756 25.2800  0.0361    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/06/29,14:36:40  25.2756 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/07/01,13:04:48  25.2674 25.2800  0.0365    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/07/05,14:51:04  25.2739 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/06/27,14:43:26  25.2580 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/06/30,14:44:00  25.1926 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+g 2003/06/23,10:19:29  26.4170 26.4600  0.0112    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/24,10:28:33  26.4245 26.4600  0.0086    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/26,11:56:32  26.4183 26.4600  0.0130    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/27,10:17:02  26.4285 26.4600  0.0113    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/29,09:53:25  26.4350 26.4600  0.0274    6   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/28,12:25:29  26.4477 26.4600  0.0251    6   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/01,09:52:16  26.4206 26.4600  0.0130    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/04,10:25:06  26.4018 26.4600  0.0143    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/05,10:25:14  26.4114 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/22,14:27:01  26.4249 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/29,14:33:38  26.4104 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/01,13:01:55  26.4169 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/24,14:39:24  26.4019 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/30,14:40:59  26.3187 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/06/22,12:56:00  25.9895 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/23,10:13:35  25.9652 25.9780  0.0047    6   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/24,10:19:55  25.9644 25.9780  0.0033    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/26,12:02:09  25.9610 25.9780  0.0073    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/27,10:19:46  25.9708 25.9780  0.0008    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/29,09:56:18  25.9528 25.9780  0.0089    6   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/28,12:28:22  25.9663 25.9780  0.0058    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/01,09:55:00  25.9672 25.9780  0.0073    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/04,10:29:42  25.9544 25.9780  0.0103    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/05,10:16:36  25.9552 25.9780  0.0067    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/05,14:48:46  25.9320 25.9780  0.0000    3   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/22,14:29:45  25.9493 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/24,14:42:17  25.9354 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/27,14:49:29  25.9371 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/29,14:45:27  25.9172 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/30,14:52:30  25.9221 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/01,13:07:49  25.9294 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/07,07:42:14  25.8036 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/06/22,12:58:53  25.7312 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/24,10:25:49  25.7261 25.7430  0.0095    4   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/26,11:59:16  25.7120 25.7430  0.0099    4   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/27,10:22:39  25.7250 25.7430  0.0103    4   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/29,09:59:02  25.7016 25.7430  0.0193    6   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/28,12:30:57  25.7047 25.7430  0.0152    6   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/07/04,10:33:53  25.7089 25.7430  0.0085    4   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/07/05,10:21:04  25.7078 25.7430  0.0080    4   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/07/01,09:57:53  25.7179 25.7430  0.0000    3   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/22,14:22:42  25.7170 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/24,14:45:01  25.7069 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/27,14:46:36  25.7100 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/29,14:39:33  25.6881 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/30,14:46:45  25.6658 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/06/23,10:16:27  24.8045 24.8000  0.0426    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/24,10:22:47  24.8213 24.8000  0.0363    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/27,10:25:23  24.8190 24.8000  0.0415    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/29,10:01:55  24.7694 24.8000  0.0431    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/28,12:33:41  24.7714 24.8000  0.0408    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/01,10:00:28  24.8012 24.8000  0.0394    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/04,10:31:43  24.7981 24.8000  0.0419    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/05,10:23:05  24.7617 24.8000  0.0311    4   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/24,14:33:30  24.7644 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/27,14:52:13  24.7629 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/29,14:42:34  24.7233 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/30,14:49:37  24.7408 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+g 2003/05/25,08:16:48  26.4408 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/05/25,14:02:24  26.4275 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/05/25,14:03:59  26.4430 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/05/26,14:50:55  26.4043 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/05/26,07:54:11  26.4084 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/05/25,08:18:23  26.3957 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/05/27,10:26:41  25.9994 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/27,10:27:41  25.9876 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/25,14:06:51  25.9703 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/27,14:38:23  25.9785 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/26,14:47:54  25.9553 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/25,14:08:26  25.9845 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/27,14:40:16  25.9925 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/26,07:58:30  25.9474 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/25,08:23:08  25.9621 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/27,06:04:10  25.9786 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/27,10:29:16  25.9440 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/05/27,06:06:54  25.7535 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/27,10:31:52  25.7376 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/25,14:11:19  25.7157 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/27,14:43:26  25.7314 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/26,14:45:01  25.7264 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/25,14:12:54  25.7356 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/27,14:44:52  25.7485 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/26,08:02:41  25.7276 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/25,08:27:35  25.6939 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/27,06:08:29  25.6955 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/27,10:33:27  25.6919 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/05/25,08:30:28  24.8014 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/05/25,14:15:38  24.7352 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/05/25,14:17:13  24.7520 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/05/25,14:39:50  24.7505 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/05/25,14:42:25  24.7538 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/05/25,14:44:52  24.7559 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/05/25,14:50:55  24.7465 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/05/26,08:08:00  24.6889 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/05/26,08:09:44  24.7414 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/05/25,08:32:03  24.7488 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+u 2003/07/22,13:40:22  25.3017 25.2800  0.0209    6   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/07/31,10:28:33  25.2284 25.2800  0.0319    7   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/01,11:32:47  25.2627 25.2800  0.0124    5   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/07/22,11:26:35  25.2747 25.2800  0.0072    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+g 2003/07/30,12:30:05  26.3704 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:31:32  26.3815 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:32:24  26.4011 26.4600  0.0175    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:33:15  26.3942 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:34:07  26.3715 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:34:59  26.3837 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:36:43  26.3226 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:38:26  26.3463 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:39:18  26.3810 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:12:46  26.4141 26.4600  0.0199    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:14:38  26.2761 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:44:18  26.4026 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:45:10  26.3946 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:46:10  26.4046 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:47:02  26.3942 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:47:54  26.4032 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:48:46  26.4307 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:49:37  26.4329 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/01,14:30:46  26.4302 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:35:51  26.3759 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:37:35  26.3472 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:13:46  26.3669 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:40:19  26.4117 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:41:19  26.4284 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:42:20  26.4288 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:43:11  26.3882 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:44:21  26.4019 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:45:21  26.4233 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:46:22  26.3867 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:47:13  26.4394 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:48:23  26.4528 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,12:49:23  26.4335 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:15:30  26.2898 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:16:22  26.2841 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:28:01  26.3029 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:32:12  26.2736 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:33:12  26.3506 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:34:22  26.3716 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:35:22  26.3566 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:36:14  26.3866 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:37:23  26.3626 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:38:23  26.3926 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:39:24  26.3606 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:40:16  26.3566 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:41:25  26.3863 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:42:25  26.3973 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:50:29  26.4281 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,14:51:21  26.4151 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/22,11:20:06  26.4140 26.4600  0.0058    6   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:36:02  26.3875 26.4600  0.0082    6   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:36:54  26.3962 26.4600  0.0061    6   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:38:47  26.3939 26.4600  0.0048    6   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:39:38  26.3879 26.4600  0.0071    6   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/01,11:24:17  26.4161 26.4600  0.0192    7   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/22,13:42:40  26.4298 26.4600  0.0089    5   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:23:57  26.3996 26.4600  0.0087    5   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:35:02  26.3865 26.4600  0.0082    5   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:37:55  26.3802 26.4600  0.0061    5   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:40:30  26.3766 26.4600  0.0064    5   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:41:22  26.3746 26.4600  0.0090    5   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:42:31  26.3733 26.4600  0.0090    5   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,10:43:23  26.3783 26.4600  0.0086    5   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/07/30,12:28:04  25.9364 25.9780  0.0140    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/01,14:28:45  25.9363 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/01,11:28:27  25.9590 25.9780  0.0195    7   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/22,11:22:16  25.9475 25.9780  0.0000    3   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/22,13:38:03  25.9685 25.9780  0.0152    5   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/31,10:30:43  25.9386 25.9780  0.0138    5   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/08/01,14:35:13  25.7086 25.7430  0.0000    3   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/01,11:30:28  25.7165 25.7430  0.0193    5   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/07/22,11:17:57  25.7288 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/07/22,13:44:49  25.7200 25.7430  0.0187    5   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/07/31,10:32:52  25.6899 25.7430  0.0179    5   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/08/01,14:37:06  24.7903 24.8000  0.0000    3   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/22,11:24:17  24.7831 24.8000  0.0416    7   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/22,13:46:50  24.7811 24.8000  0.0401    7   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/31,10:26:15  24.7419 24.8000  0.0384    7   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/21,06:16:07  24.8628 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+u 2003/08/21,09:56:35  25.3331 25.2800  0.0357    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/22,06:16:24  25.4472 25.2800  0.0382    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/22,09:13:32  25.3878 25.2800  0.0050    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/25,12:39:10  25.4175 25.2800  0.0310    5   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/28,10:14:35  25.3980 25.2800  0.0296    5   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/29,08:14:38  25.3912 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/29,08:15:38  25.3885 25.2800  0.0373    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/30,09:59:02  25.3895 25.2800  0.0318    5   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/31,07:15:10  25.4075 25.2800  0.0273    5   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/09/04,08:31:03  25.3510 25.2800  0.0058    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/25,05:52:39  25.3050 25.2800  0.0000    1   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/30,07:13:00  25.4598 25.2800  0.0000    1   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+g 2003/08/27,15:09:21  26.5096 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/09/03,15:00:34  26.5288 26.4600  0.0205    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/23,14:57:24  26.5457 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/30,14:25:00  26.5669 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/09/04,15:11:05  26.4714 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/28,07:13:09  26.5061 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/21,10:07:58  26.4956 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/22,06:24:46  26.5343 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/25,12:36:51  26.5242 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/28,10:18:54  26.5111 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/29,08:22:24  26.5010 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/30,09:56:44  26.5113 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/31,07:19:37  26.5286 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/09/04,08:28:53  26.4813 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/23,07:09:15  26.5158 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/25,05:50:21  26.4298 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/30,07:10:33  26.5390 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/09/03,05:49:29  26.4413 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/08/23,14:50:55  26.0223 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/30,14:27:10  26.0383 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/09/03,15:02:35  26.0080 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/09/04,15:13:14  25.9757 25.9780  0.0000    3   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/30,09:52:24  25.9954 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/21,09:59:36  25.9575 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/22,06:18:25  26.0393 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/22,09:15:50  26.0112 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/25,12:34:50  26.0245 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/29,08:17:57  25.9944 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/31,07:21:47  26.0173 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/23,07:05:13  26.0000 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/30,07:06:23  26.0434 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/09/03,05:45:10  25.9892 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/09/03,15:04:45  25.7557 25.7430  0.0000    3   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/23,14:52:56  25.7570 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/30,14:29:02  25.7823 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/09/04,15:06:20  25.7414 25.7430  0.0000    3   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/28,07:18:20  25.7614 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/22,06:20:35  25.7676 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/22,09:17:42  25.7562 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/30,09:50:24  25.7790 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/21,10:02:21  25.7279 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/25,12:41:36  25.7803 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/28,10:20:55  25.7747 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/29,08:24:25  25.7721 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/31,07:13:00  25.7795 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/09/04,08:33:30  25.7536 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/23,07:07:23  25.7556 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/30,07:08:32  25.7745 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/09/03,05:47:19  25.7456 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/08/27,15:18:00  24.7928 24.8000  0.0000    3   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/28,15:15:24  24.8363 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/09/03,15:09:21  24.8027 24.8000  0.0193    4   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/23,14:59:42  24.7762 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/30,14:33:38  24.8063 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/09/04,15:15:15  24.7638 24.8000  0.0355    5   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/28,07:21:12  24.8333 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/21,10:05:13  24.7669 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/22,09:11:13  24.7917 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/25,12:43:46  24.7967 24.8000  0.0000    3   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/28,10:22:56  24.8075 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/29,08:20:06  24.7902 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/30,09:54:25  24.8141 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/31,07:17:28  24.8038 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/09/04,08:35:31  24.7788 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/22,06:22:36  24.8321 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/23,07:03:04  24.7013 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/25,05:44:00  24.7611 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/30,07:04:13  24.8370 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/09/03,05:43:00  24.7850 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/photdb.src
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/photdb.src	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/photdb.src	(revision 22322)
@@ -0,0 +1,349 @@
+photreg -photcode i -trange 2003/02/08,13:18:37 1s -zp 25.7317 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/08,13:19:29 1s -zp 25.7307 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/08,13:20:12 1s -zp 25.7289 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/08,13:30:08 1s -zp 25.9878 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/08,13:30:51 1s -zp 25.9862 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/08,13:31:43 1s -zp 25.9816 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/09,12:26:03 1s -zp 25.9581 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/09,12:26:55 1s -zp 25.9529 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/09,12:27:38 1s -zp 25.9617 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/02/09,12:47:05 1s -zp 26.4692 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/02/09,12:47:48 1s -zp 26.4642 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/02/09,12:48:40 1s -zp 26.4624 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/09,13:01:29 1s -zp 25.3035 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/09,13:05:05 1s -zp 25.3034 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/09,13:06:14 1s -zp 25.3004 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/02/09,13:11:51 1s -zp 24.7789 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/02/09,13:12:43 1s -zp 24.7879 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/02/09,13:13:26 1s -zp 24.7888 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/09,13:22:04 1s -zp 25.7434 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/09,13:22:56 1s -zp 25.7318 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/09,13:23:39 1s -zp 25.7397 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/10,13:00:46 1s -zp 25.3434 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/10,13:27:24 1s -zp 25.3435 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/10,13:33:10 1s -zp 25.3496 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/10,13:38:47 1s -zp 25.3583 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/10,13:56:21 1s -zp 25.3455 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/12,15:18:51 1s -zp 26.0073 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/02/23,10:10:59 1s -zp 26.4664 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/02/23,10:11:59 1s -zp 26.4654 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/02/23,10:13:00 1s -zp 26.4604 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/23,10:17:28 1s -zp 25.9736 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/23,10:20:12 1s -zp 25.9708 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/23,10:20:55 1s -zp 25.9648 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/23,10:25:32 1s -zp 25.7384 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/23,10:28:07 1s -zp 25.7415 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/23,10:28:59 1s -zp 25.7465 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/02/23,10:33:44 1s -zp 24.7826 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/02/23,10:36:54 1s -zp 24.7967 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/02/24,12:00:51 1s -zp 26.4463 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/02/24,12:01:43 1s -zp 26.4393 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/02/24,12:02:35 1s -zp 26.4343 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/24,12:07:20 1s -zp 25.9939 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/24,12:09:47 1s -zp 25.9722 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/02/24,12:10:39 1s -zp 25.9897 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/24,12:15:24 1s -zp 25.7579 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/24,12:18:00 1s -zp 25.7399 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/02/24,12:18:51 1s -zp 25.7489 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/02/24,12:23:19 1s -zp 24.7178 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/02/24,12:25:46 1s -zp 24.7117 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/02/24,12:26:47 1s -zp 24.7562 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/24,12:36:34 1s -zp 25.2875 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/24,12:37:43 1s -zp 25.2855 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/02/24,12:39:01 1s -zp 25.3197 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/03/22,15:11:13 1s -zp 25.4630 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/03/23,15:12:14 1s -zp 25.3056 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/03/24,14:59:16 1s -zp 24.8271 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/03/24,15:07:46 1s -zp 26.4566 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/03/24,15:10:56 1s -zp 25.9917 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/03/25,15:12:48 1s -zp 25.7633 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/03/25,15:21:27 1s -zp 26.0231 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/03/30,13:12:34 1s -zp 26.4501 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/03/30,13:20:55 1s -zp 24.7152 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/04/25,08:55:23 1s -zp 25.9287 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/04/25,08:58:07 1s -zp 25.7034 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/04/25,09:00:43 1s -zp 24.7533 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/04/25,09:06:28 1s -zp 26.4030 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/04/26,10:44:32 1s -zp 25.9647 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/04/26,12:38:26 1s -zp 25.9774 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/04/26,13:05:05 1s -zp 26.0053 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/04/26,13:06:05 1s -zp 25.9697 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/04/26,13:14:35 1s -zp 25.9455 -dzp 0.0046 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/04/27,08:44:44 1s -zp 25.9646 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/04/27,08:47:28 1s -zp 25.9676 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/04/27,09:57:10 1s -zp 25.7930 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/05/25,08:16:48 1s -zp 26.4408 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/05/25,08:18:23 1s -zp 26.3957 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/25,08:23:08 1s -zp 25.9621 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/25,08:27:35 1s -zp 25.6939 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/05/25,08:30:28 1s -zp 24.8014 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/05/25,08:32:03 1s -zp 24.7488 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/05/25,14:02:24 1s -zp 26.4275 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/05/25,14:03:59 1s -zp 26.4430 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/25,14:06:51 1s -zp 25.9703 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/25,14:08:26 1s -zp 25.9845 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/25,14:11:19 1s -zp 25.7157 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/25,14:12:54 1s -zp 25.7356 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/05/25,14:15:38 1s -zp 24.7352 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/05/25,14:17:13 1s -zp 24.7520 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/05/25,14:39:50 1s -zp 24.7505 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/05/25,14:42:25 1s -zp 24.7538 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/05/25,14:44:52 1s -zp 24.7559 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/05/25,14:50:55 1s -zp 24.7465 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/05/26,07:54:11 1s -zp 26.4084 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/26,07:58:30 1s -zp 25.9474 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/26,08:02:41 1s -zp 25.7276 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/05/26,08:08:00 1s -zp 24.6889 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/05/26,08:09:44 1s -zp 24.7414 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/26,14:45:01 1s -zp 25.7264 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/26,14:47:54 1s -zp 25.9553 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/05/26,14:50:55 1s -zp 26.4043 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/27,06:04:10 1s -zp 25.9786 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/27,06:06:54 1s -zp 25.7535 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/27,06:08:29 1s -zp 25.6955 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/27,10:26:41 1s -zp 25.9994 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/27,10:27:41 1s -zp 25.9876 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/27,10:29:16 1s -zp 25.9440 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/27,10:31:52 1s -zp 25.7376 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/27,10:33:27 1s -zp 25.6919 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/27,14:38:23 1s -zp 25.9785 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/05/27,14:40:16 1s -zp 25.9925 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/27,14:43:26 1s -zp 25.7314 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/05/27,14:44:52 1s -zp 25.7485 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/05/30,14:21:41 1s -zp 25.2892 -dzp 0.0150 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/02,07:20:29 1s -zp 25.7291 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/02,07:23:22 1s -zp 24.8465 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/04,06:40:27 1s -zp 25.7566 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/05,06:21:27 1s -zp 25.5921 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/06,05:58:59 1s -zp 25.7441 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/08,14:43:35 1s -zp 25.9302 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/08,14:48:20 1s -zp 25.6812 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/06/09,14:36:14 1s -zp 25.2311 -dzp 0.0071 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/09,14:42:25 1s -zp 24.7583 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/22,12:56:00 1s -zp 25.9895 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/22,12:58:53 1s -zp 25.7312 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/22,14:22:42 1s -zp 25.7170 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/06/22,14:27:01 1s -zp 26.4249 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/22,14:29:45 1s -zp 25.9493 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/23,10:13:35 1s -zp 25.9652 -dzp 0.0047 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/23,10:16:27 1s -zp 24.8045 -dzp 0.0426 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/06/23,10:19:29 1s -zp 26.4170 -dzp 0.0112 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/24,10:19:55 1s -zp 25.9644 -dzp 0.0033 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/24,10:22:47 1s -zp 24.8213 -dzp 0.0363 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/24,10:25:49 1s -zp 25.7261 -dzp 0.0095 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/06/24,10:28:33 1s -zp 26.4245 -dzp 0.0086 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/24,14:33:30 1s -zp 24.7644 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/06/24,14:36:40 1s -zp 25.2756 -dzp 0.0361 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/06/24,14:39:24 1s -zp 26.4019 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/24,14:42:17 1s -zp 25.9354 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/24,14:45:01 1s -zp 25.7069 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/06/26,11:56:32 1s -zp 26.4183 -dzp 0.0130 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/26,11:59:16 1s -zp 25.7120 -dzp 0.0099 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/26,12:02:09 1s -zp 25.9610 -dzp 0.0073 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/06/27,10:17:02 1s -zp 26.4285 -dzp 0.0113 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/27,10:19:46 1s -zp 25.9708 -dzp 0.0008 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/27,10:22:39 1s -zp 25.7250 -dzp 0.0103 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/27,10:25:23 1s -zp 24.8190 -dzp 0.0415 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/06/27,14:43:26 1s -zp 25.2580 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/27,14:46:36 1s -zp 25.7100 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/27,14:49:29 1s -zp 25.9371 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/27,14:52:13 1s -zp 24.7629 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/06/28,12:25:29 1s -zp 26.4477 -dzp 0.0251 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/28,12:28:22 1s -zp 25.9663 -dzp 0.0058 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/28,12:30:57 1s -zp 25.7047 -dzp 0.0152 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/28,12:33:41 1s -zp 24.7714 -dzp 0.0408 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/06/29,09:53:25 1s -zp 26.4350 -dzp 0.0274 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/29,09:56:18 1s -zp 25.9528 -dzp 0.0089 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/29,09:59:02 1s -zp 25.7016 -dzp 0.0193 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/29,10:01:55 1s -zp 24.7694 -dzp 0.0431 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/06/29,14:33:38 1s -zp 26.4104 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/06/29,14:36:40 1s -zp 25.2756 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/29,14:39:33 1s -zp 25.6881 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/29,14:42:34 1s -zp 24.7233 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/29,14:45:27 1s -zp 25.9172 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/06/30,14:40:59 1s -zp 26.3187 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/06/30,14:44:00 1s -zp 25.1926 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/06/30,14:46:45 1s -zp 25.6658 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/06/30,14:49:37 1s -zp 24.7408 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/06/30,14:52:30 1s -zp 25.9221 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/01,09:52:16 1s -zp 26.4206 -dzp 0.0130 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/07/01,09:55:00 1s -zp 25.9672 -dzp 0.0073 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/07/01,09:57:53 1s -zp 25.7179 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/07/01,10:00:28 1s -zp 24.8012 -dzp 0.0394 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/01,13:01:55 1s -zp 26.4169 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/07/01,13:04:48 1s -zp 25.2674 -dzp 0.0365 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/07/01,13:07:49 1s -zp 25.9294 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/04,10:25:06 1s -zp 26.4018 -dzp 0.0143 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/07/04,10:29:42 1s -zp 25.9544 -dzp 0.0103 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/07/04,10:31:43 1s -zp 24.7981 -dzp 0.0419 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/07/04,10:33:53 1s -zp 25.7089 -dzp 0.0085 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/07/05,10:16:36 1s -zp 25.9552 -dzp 0.0067 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/07/05,10:21:04 1s -zp 25.7078 -dzp 0.0080 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/07/05,10:23:05 1s -zp 24.7617 -dzp 0.0311 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/05,10:25:14 1s -zp 26.4114 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/07/05,14:48:46 1s -zp 25.9320 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/07/05,14:51:04 1s -zp 25.2739 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/07/07,07:42:14 1s -zp 25.8036 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/07/21,06:16:07 1s -zp 24.8628 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/07/22,11:17:57 1s -zp 25.7288 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/22,11:20:06 1s -zp 26.4140 -dzp 0.0058 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/07/22,11:22:16 1s -zp 25.9475 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/07/22,11:24:17 1s -zp 24.7831 -dzp 0.0416 -Nmeas 7 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/07/22,11:26:35 1s -zp 25.2747 -dzp 0.0072 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/07/22,13:38:03 1s -zp 25.9685 -dzp 0.0152 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/07/22,13:40:22 1s -zp 25.3017 -dzp 0.0209 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/22,13:42:40 1s -zp 26.4298 -dzp 0.0089 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/07/22,13:44:49 1s -zp 25.7200 -dzp 0.0187 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/07/22,13:46:50 1s -zp 24.7811 -dzp 0.0401 -Nmeas 7 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/07/30,12:28:04 1s -zp 25.9364 -dzp 0.0140 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:30:05 1s -zp 26.3704 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:31:32 1s -zp 26.3815 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:32:24 1s -zp 26.4011 -dzp 0.0175 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:33:15 1s -zp 26.3942 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:34:07 1s -zp 26.3715 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:34:59 1s -zp 26.3837 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:35:51 1s -zp 26.3759 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:36:43 1s -zp 26.3226 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:37:35 1s -zp 26.3472 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:38:26 1s -zp 26.3463 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:39:18 1s -zp 26.3810 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:40:19 1s -zp 26.4117 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:41:19 1s -zp 26.4284 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:42:20 1s -zp 26.4288 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:43:11 1s -zp 26.3882 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:44:21 1s -zp 26.4019 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:45:21 1s -zp 26.4233 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:46:22 1s -zp 26.3867 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:47:13 1s -zp 26.4394 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:48:23 1s -zp 26.4528 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,12:49:23 1s -zp 26.4335 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:12:46 1s -zp 26.4141 -dzp 0.0199 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:13:46 1s -zp 26.3669 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:14:38 1s -zp 26.2761 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:15:30 1s -zp 26.2898 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:16:22 1s -zp 26.2841 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:28:01 1s -zp 26.3029 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:32:12 1s -zp 26.2736 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:33:12 1s -zp 26.3506 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:34:22 1s -zp 26.3716 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:35:22 1s -zp 26.3566 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:36:14 1s -zp 26.3866 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:37:23 1s -zp 26.3626 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:38:23 1s -zp 26.3926 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:39:24 1s -zp 26.3606 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:40:16 1s -zp 26.3566 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:41:25 1s -zp 26.3863 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:42:25 1s -zp 26.3973 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:44:18 1s -zp 26.4026 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:45:10 1s -zp 26.3946 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:46:10 1s -zp 26.4046 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:47:02 1s -zp 26.3942 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:47:54 1s -zp 26.4032 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:48:46 1s -zp 26.4307 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:49:37 1s -zp 26.4329 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:50:29 1s -zp 26.4281 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/30,14:51:21 1s -zp 26.4151 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:23:57 1s -zp 26.3996 -dzp 0.0087 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/07/31,10:26:15 1s -zp 24.7419 -dzp 0.0384 -Nmeas 7 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/07/31,10:28:33 1s -zp 25.2284 -dzp 0.0319 -Nmeas 7 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/07/31,10:30:43 1s -zp 25.9386 -dzp 0.0138 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/07/31,10:32:52 1s -zp 25.6899 -dzp 0.0179 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:35:02 1s -zp 26.3865 -dzp 0.0082 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:36:02 1s -zp 26.3875 -dzp 0.0082 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:36:54 1s -zp 26.3962 -dzp 0.0061 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:37:55 1s -zp 26.3802 -dzp 0.0061 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:38:47 1s -zp 26.3939 -dzp 0.0048 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:39:38 1s -zp 26.3879 -dzp 0.0071 -Nmeas 6 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:40:30 1s -zp 26.3766 -dzp 0.0064 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:41:22 1s -zp 26.3746 -dzp 0.0090 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:42:31 1s -zp 26.3733 -dzp 0.0090 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/07/31,10:43:23 1s -zp 26.3783 -dzp 0.0086 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/01,11:24:17 1s -zp 26.4161 -dzp 0.0192 -Nmeas 7 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/01,11:28:27 1s -zp 25.9590 -dzp 0.0195 -Nmeas 7 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/01,11:30:28 1s -zp 25.7165 -dzp 0.0193 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/01,11:32:47 1s -zp 25.2627 -dzp 0.0124 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/01,14:28:45 1s -zp 25.9363 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/01,14:30:46 1s -zp 26.4302 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/01,14:35:13 1s -zp 25.7086 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/01,14:37:06 1s -zp 24.7903 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/21,09:56:35 1s -zp 25.3331 -dzp 0.0357 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/21,09:59:36 1s -zp 25.9575 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/21,10:02:21 1s -zp 25.7279 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/21,10:05:13 1s -zp 24.7669 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/21,10:07:58 1s -zp 26.4956 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/22,06:16:24 1s -zp 25.4472 -dzp 0.0382 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/22,06:18:25 1s -zp 26.0393 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/22,06:20:35 1s -zp 25.7676 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/22,06:22:36 1s -zp 24.8321 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/22,06:24:46 1s -zp 26.5343 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/22,09:11:13 1s -zp 24.7917 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/22,09:13:32 1s -zp 25.3878 -dzp 0.0050 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/22,09:15:50 1s -zp 26.0112 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/22,09:17:42 1s -zp 25.7562 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/23,07:03:04 1s -zp 24.7013 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/23,07:05:13 1s -zp 26.0000 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/23,07:07:23 1s -zp 25.7556 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/23,07:09:15 1s -zp 26.5158 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/23,14:50:55 1s -zp 26.0223 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/23,14:52:56 1s -zp 25.7570 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/23,14:57:24 1s -zp 26.5457 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/23,14:59:42 1s -zp 24.7762 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/25,05:44:00 1s -zp 24.7611 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/25,05:50:21 1s -zp 26.4298 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/25,05:52:39 1s -zp 25.3050 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/25,12:34:50 1s -zp 26.0245 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/25,12:36:51 1s -zp 26.5242 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/25,12:39:10 1s -zp 25.4175 -dzp 0.0310 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/25,12:41:36 1s -zp 25.7803 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/25,12:43:46 1s -zp 24.7967 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/27,15:09:21 1s -zp 26.5096 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/27,15:18:00 1s -zp 24.7928 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/28,07:13:09 1s -zp 26.5061 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/28,07:18:20 1s -zp 25.7614 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/28,07:21:12 1s -zp 24.8333 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/28,10:14:35 1s -zp 25.3980 -dzp 0.0296 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/28,10:18:54 1s -zp 26.5111 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/28,10:20:55 1s -zp 25.7747 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/28,10:22:56 1s -zp 24.8075 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/28,15:15:24 1s -zp 24.8363 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/29,08:14:38 1s -zp 25.3912 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/29,08:15:38 1s -zp 25.3885 -dzp 0.0373 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/29,08:17:57 1s -zp 25.9944 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/29,08:20:06 1s -zp 24.7902 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/29,08:22:24 1s -zp 26.5010 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/29,08:24:25 1s -zp 25.7721 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/30,07:04:13 1s -zp 24.8370 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/30,07:06:23 1s -zp 26.0434 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/30,07:08:32 1s -zp 25.7745 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/30,07:10:33 1s -zp 26.5390 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/30,07:13:00 1s -zp 25.4598 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/30,09:50:24 1s -zp 25.7790 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/30,09:52:24 1s -zp 25.9954 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/30,09:54:25 1s -zp 24.8141 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/30,09:56:44 1s -zp 26.5113 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/30,09:59:02 1s -zp 25.3895 -dzp 0.0318 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/30,14:25:00 1s -zp 26.5669 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/30,14:27:10 1s -zp 26.0383 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/30,14:29:02 1s -zp 25.7823 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/30,14:33:38 1s -zp 24.8063 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/08/31,07:13:00 1s -zp 25.7795 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/08/31,07:15:10 1s -zp 25.4075 -dzp 0.0273 -Nmeas 5 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/08/31,07:17:28 1s -zp 24.8038 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/08/31,07:19:37 1s -zp 26.5286 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/08/31,07:21:47 1s -zp 26.0173 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/09/03,05:43:00 1s -zp 24.7850 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/09/03,05:45:10 1s -zp 25.9892 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/09/03,05:47:19 1s -zp 25.7456 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/09/03,05:49:29 1s -zp 26.4413 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/09/03,15:00:34 1s -zp 26.5288 -dzp 0.0205 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/09/03,15:02:35 1s -zp 26.0080 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/09/03,15:04:45 1s -zp 25.7557 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/09/03,15:09:21 1s -zp 24.8027 -dzp 0.0193 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/09/04,08:28:53 1s -zp 26.4813 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode u -trange 2003/09/04,08:31:03 1s -zp 25.3510 -dzp 0.0058 -Nmeas 4 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/09/04,08:33:30 1s -zp 25.7536 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/09/04,08:35:31 1s -zp 24.7788 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -photcode i -trange 2003/09/04,15:06:20 1s -zp 25.7414 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode g -trange 2003/09/04,15:11:05 1s -zp 26.4714 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode r -trange 2003/09/04,15:13:14 1s -zp 25.9757 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -photcode z -trange 2003/09/04,15:15:15 1s -zp 24.7638 -dzp 0.0355 -Nmeas 5 -Ntime 1 -label elixir
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/spdb.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/spdb.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/spdb.txt	(revision 22322)
@@ -0,0 +1,78 @@
+
+plan - use imregister / imsearch framework for spectra.db
+
+relevant database usages:
+
+use this for spectra.db ref ?
+  average.code or average.Xg
+
+
+spectra in spectra.db will have RA,DEC: join between tables using position
+
+example queries:
+
+spsearch 
+
+/* structure for Spectra Registration Database */
+typedef struct {
+  char filename[64];
+  char pathname[128];
+  char telescope[32];
+  char instrument[32];
+ 
+  char objname[64];
+
+  char mode (MEF?)
+  
+  float ra, dec;
+  float exptime;
+  float airmass;
+
+  /* spectral range representation */
+  float W0, W1, dW; 
+
+  /* spectral range start, end; average value for nm / pixel */
+
+  float sky;
+  float bias;
+  float fwhm;
+
+  float telfocus;
+  float xprobe, yprobe, zprobe;
+  float dettemp;
+  float teltemp[4];
+  float rotangle;
+
+  unsigned long int obstime;
+  unsigned long int regtime;
+} RegImage;  /* 360 bytes / image */
+
+
+outstanding questions:
+
+- convert spectral file to standard format on db insertion?
+  - uniform keywords for wavelength calibration?
+  - need header WCS
+  - single FITS file
+  - how to store discontiguous spectra? (MEF?)
+
+
+-------------------------------------------
+
+spectra examples in hand:
+
+Keck I / HiRes : 
+     - single IRAF FITS spectra
+     - BANDIDn gives def of slice (spectrum, sigma, background, raw)
+     - WATn_001 gives units (Angstroms)     
+     - wavelength = (x-CRPIX1+1) * CD1_1 + CRVAL1
+     * how do we know the orientation?
+
+Keck I / ESI : 
+     - MEF spectra (yso/keck)
+       - spload-mef converts to a single 
+
+-----
+
+database interactions vs autocode FITS tables
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/spdb2.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/spdb2.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/spdb2.txt	(revision 22322)
@@ -0,0 +1,58 @@
+
+metadata to extract:
+
+typedef struct {
+  char filename[64];
+  char pathname[128];
+  char instrument[32];
+  char telescope[32];
+  char objname[64];
+  char extname[64];
+
+  float ra, dec;
+  float exptime;
+  float airmass;
+
+  float W0, W1, dW;
+  int Nspec;				/* number of spectra associated with entry */
+
+  char mode;			/* PHU, MEF, EXT */
+  char state;			/* image state: raw, flx, etc */
+  char flag;			/* image status flags */
+  char junk[9];
+
+  unsigned long int obstime;
+  unsigned long int regtime;
+} Spectrum;
+
+
+FITS Keywords for metadata
+
+case 1:
+NAXIS  = 1
+NAXIS1 = Nw
+
+case 2:
+NAXIS  = 2
+NAXIS1 = Nw
+NAXIS2 = 1
+
+case 3:
+NAXIS  = 2
+NAXIS1 = 1
+NAXIS2 = Nw
+
+case 4:
+NAXIS  = 2
+NAXIS1 = Nw
+NAXIS2 = n   (flux, dflux, etc)
+
+case 5:
+NAXIS  = 3
+NAXIS1 = Nw
+NAXIS2 = 1
+NAXIS3 = n   (flux, dflux, etc)
+
+0         1         2         3         4         5         6         7         8
+012345678901234567890123456789012345678901234567890123456789012345678901234567890
+/data/elixir2/datdir/megacam/03Am02/taurus/L1551/VLT/spectra/2002.04.01
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/transdb.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/transdb.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/transdb.dat	(revision 22322)
@@ -0,0 +1,159 @@
+u 2003/02/24,00:00:00  25.2980 25.2800  0.0157    7   3   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+g 2003/02/23,00:00:00  26.4640 26.4600  0.0026    3   3   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/02/24,00:00:00  26.4400 26.4600  0.0049    6   3   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/02/23,00:00:00  25.9700 25.9780  0.0037    3   3   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/02/24,00:00:00  25.9850 25.9780  0.0094    5   3   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/02/23,00:00:00  25.7420 25.7430  0.0033    3   3   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/02/24,00:00:00  25.7490 25.7430  0.0073    6   3   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/02/23,00:00:00  24.7900 24.8000  0.0070    2   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/02/24,00:00:00  24.7290 24.8000  0.0197    4   3   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+u 2003/03/22,00:00:00  25.4630 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/03/23,00:00:00  25.3060 25.2800  0.0000    1   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+g 2003/03/24,00:00:00  26.4570 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/03/30,00:00:00  26.4500 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/03/24,00:00:00  25.9920 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/03/25,00:00:00  26.0230 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/03/25,00:00:00  25.7630 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/03/24,00:00:00  24.8270 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/03/30,00:00:00  24.7150 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+g 2003/04/25,00:00:00  26.4030 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/04/25,00:00:00  25.9290 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/04/26,00:00:00  25.9730 25.9780  0.0195   11   5   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/04/27,00:00:00  25.9080 25.9780  0.0816    5   3   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/04/25,00:00:00  25.7030 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/04/25,00:00:00  24.7530 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+u 2003/05/30,00:00:00  25.2890 25.2800  0.0000    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/06/09,00:00:00  25.2310 25.2800  0.0000    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+r 2003/06/08,00:00:00  25.9300 25.9780  0.0000    3   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/06/02,00:00:00  25.7290 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/04,00:00:00  25.7570 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/05,00:00:00  25.5920 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/06,00:00:00  25.7440 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/08,00:00:00  25.6810 25.7430  0.0000    3   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/06/02,00:00:00  24.8460 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/09,00:00:00  24.7580 24.8000  0.0000    1   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+u 2003/06/24,00:00:00  25.2760 25.2800  0.0000    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/06/27,00:00:00  25.2580 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/06/29,00:00:00  25.2760 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/06/30,00:00:00  25.1930 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/07/01,00:00:00  25.2670 25.2800  0.0000    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/07/05,00:00:00  25.2740 25.2800  0.0000    3   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+g 2003/06/22,00:00:00  26.4250 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/23,00:00:00  26.4170 26.4600  0.0000    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/24,00:00:00  26.4130 26.4600  0.0113    5   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/26,00:00:00  26.4180 26.4600  0.0000    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/27,00:00:00  26.4280 26.4600  0.0000    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/28,00:00:00  26.4480 26.4600  0.0000    6   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/29,00:00:00  26.4230 26.4600  0.0123    8   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/06/30,00:00:00  26.3190 26.4600  0.0000    1   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/01,00:00:00  26.4190 26.4600  0.0019    6   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/04,00:00:00  26.4020 26.4600  0.0000    4   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/05,00:00:00  26.4110 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/06/22,00:00:00  25.9690 25.9780  0.0201    4   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/23,00:00:00  25.9650 25.9780  0.0000    6   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/24,00:00:00  25.9500 25.9780  0.0145    6   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/26,00:00:00  25.9610 25.9780  0.0000    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/27,00:00:00  25.9540 25.9780  0.0169    6   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/28,00:00:00  25.9660 25.9780  0.0000    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/29,00:00:00  25.9350 25.9780  0.0178    8   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/06/30,00:00:00  25.9220 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/01,00:00:00  25.9480 25.9780  0.0189    6   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/04,00:00:00  25.9540 25.9780  0.0000    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/05,00:00:00  25.9440 25.9780  0.0116    7   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/07,00:00:00  25.8040 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/06/22,00:00:00  25.7240 25.7430  0.0071    4   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/24,00:00:00  25.7160 25.7430  0.0096    6   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/26,00:00:00  25.7120 25.7430  0.0000    4   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/27,00:00:00  25.7180 25.7430  0.0075    6   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/28,00:00:00  25.7050 25.7430  0.0000    6   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/29,00:00:00  25.6950 25.7430  0.0067    8   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/06/30,00:00:00  25.6660 25.7430  0.0000    2   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/07/01,00:00:00  25.7180 25.7430  0.0000    3   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/07/04,00:00:00  25.7090 25.7430  0.0000    4   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/07/05,00:00:00  25.7080 25.7430  0.0000    4   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/06/23,00:00:00  24.8050 24.8000  0.0000    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/24,00:00:00  24.7930 24.8000  0.0284    8   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/27,00:00:00  24.7910 24.8000  0.0280    8   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/28,00:00:00  24.7710 24.8000  0.0000    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/29,00:00:00  24.7460 24.8000  0.0231    8   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/06/30,00:00:00  24.7410 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/01,00:00:00  24.8010 24.8000  0.0000    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/04,00:00:00  24.7980 24.8000  0.0000    6   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/05,00:00:00  24.7620 24.8000  0.0000    4   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+g 2003/05/25,00:00:00  26.4270 26.4600  0.0189    5   4   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/05/26,00:00:00  26.4060 26.4600  0.0021    2   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/05/25,00:00:00  25.9720 25.9780  0.0093    4   3   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/26,00:00:00  25.9510 25.9780  0.0040    3   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/05/27,00:00:00  25.9800 25.9780  0.0178    7   6   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/05/25,00:00:00  25.7150 25.7430  0.0170    3   3   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/26,00:00:00  25.7270 25.7430  0.0006    3   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/05/27,00:00:00  25.7260 25.7430  0.0242    6   6   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/05/25,00:00:00  24.7560 24.8000  0.0183    9   8   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/05/26,00:00:00  24.7150 24.8000  0.0262    3   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+u 2003/07/22,00:00:00  25.2880 25.2800  0.0135   10   2   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/07/31,00:00:00  25.2280 25.2800  0.0000    7   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/01,00:00:00  25.2630 25.2800  0.0000    5   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+g 2003/07/22,00:00:00  26.4220 26.4600  0.0079   11   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/30,00:00:00  26.3810 26.4600  0.0431   78  47   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/07/31,00:00:00  26.3850 26.4600  0.0086   59  11   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/01,00:00:00  26.4230 26.4600  0.0070    9   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/07/22,00:00:00  25.9580 25.9780  0.0105    8   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/30,00:00:00  25.9360 25.9780  0.0000    4   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/07/31,00:00:00  25.9390 25.9780  0.0000    5   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/01,00:00:00  25.9480 25.9780  0.0113    9   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/07/22,00:00:00  25.7240 25.7430  0.0044    6   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/07/31,00:00:00  25.6900 25.7430  0.0000    5   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/01,00:00:00  25.7130 25.7430  0.0039    8   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/07/21,00:00:00  24.8630 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/22,00:00:00  24.7820 24.8000  0.0010   14   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/07/31,00:00:00  24.7420 24.8000  0.0000    7   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/01,00:00:00  24.7900 24.8000  0.0000    3   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+u 2003/08/21,00:00:00  25.3330 25.2800  0.0000    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/22,00:00:00  25.4170 25.2800  0.0297    8   2   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/25,00:00:00  25.3610 25.2800  0.0563    6   2   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/28,00:00:00  25.3980 25.2800  0.0000    5   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/29,00:00:00  25.3900 25.2800  0.0013    7   2   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/30,00:00:00  25.4250 25.2800  0.0351    6   2   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/08/31,00:00:00  25.4070 25.2800  0.0000    5   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+u 2003/09/04,00:00:00  25.3510 25.2800  0.0000    4   1   0.2410 -0.3500  unknown  u_SDSS g_SDSS elixir
+g 2003/08/21,00:00:00  26.4960 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/22,00:00:00  26.5340 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/23,00:00:00  26.5310 26.4600  0.0150    2   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/25,00:00:00  26.4770 26.4600  0.0472    3   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/27,00:00:00  26.5100 26.4600  0.0000    3   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/28,00:00:00  26.5090 26.4600  0.0025    4   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/29,00:00:00  26.5010 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/30,00:00:00  26.5390 26.4600  0.0227    5   3   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/08/31,00:00:00  26.5290 26.4600  0.0000    2   1   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/09/03,00:00:00  26.4850 26.4600  0.0437    5   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+g 2003/09/04,00:00:00  26.4760 26.4600  0.0049    4   2   0.1480 -0.1500  unknown  g_SDSS r_SDSS elixir
+r 2003/08/21,00:00:00  25.9570 25.9780  0.0000    2   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/22,00:00:00  26.0250 25.9780  0.0141    2   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/23,00:00:00  26.0110 25.9780  0.0112    2   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/25,00:00:00  26.0240 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/29,00:00:00  25.9940 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/30,00:00:00  26.0260 25.9780  0.0215    3   3   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/08/31,00:00:00  26.0170 25.9780  0.0000    1   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/09/03,00:00:00  25.9990 25.9780  0.0094    2   2   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+r 2003/09/04,00:00:00  25.9760 25.9780  0.0000    3   1   0.0000 -0.1000  unknown  g_SDSS r_SDSS elixir
+i 2003/08/21,00:00:00  25.7280 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/22,00:00:00  25.7620 25.7430  0.0057    4   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/23,00:00:00  25.7560 25.7430  0.0007    2   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/25,00:00:00  25.7800 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/28,00:00:00  25.7680 25.7430  0.0067    2   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/29,00:00:00  25.7720 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/30,00:00:00  25.7790 25.7430  0.0032    4   3   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/08/31,00:00:00  25.7790 25.7430  0.0000    1   1   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/09/03,00:00:00  25.7510 25.7430  0.0050    4   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+i 2003/09/04,00:00:00  25.7470 25.7430  0.0061    4   2   0.0830 -0.0400  unknown  r_SDSS i_SDSS elixir
+z 2003/08/21,00:00:00  24.7670 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/22,00:00:00  24.8120 24.8000  0.0202    3   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/23,00:00:00  24.7390 24.8000  0.0374    2   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/25,00:00:00  24.7790 24.8000  0.0178    4   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/27,00:00:00  24.7930 24.8000  0.0000    3   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/28,00:00:00  24.8260 24.8000  0.0129    5   3   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/29,00:00:00  24.7900 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/30,00:00:00  24.8190 24.8000  0.0130    4   3   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/08/31,00:00:00  24.8040 24.8000  0.0000    2   1   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/09/03,00:00:00  24.7940 24.8000  0.0089    5   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
+z 2003/09/04,00:00:00  24.7710 24.8000  0.0075    7   2   0.0500 -0.0300  unknown  g_SDSS z_SDSS elixir
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/transdb.src
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/transdb.src	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/doc/transdb.src	(revision 22322)
@@ -0,0 +1,159 @@
+photreg -trans -photcode z -trange 2003/02/23,00:00:00 1s -zp 24.7900 -dzp 0.0070 -Nmeas 2 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/02/23,00:00:00 1s -zp 25.7420 -dzp 0.0033 -Nmeas 3 -Ntime 3 -label elixir
+photreg -trans -photcode r -trange 2003/02/23,00:00:00 1s -zp 25.9700 -dzp 0.0037 -Nmeas 3 -Ntime 3 -label elixir
+photreg -trans -photcode g -trange 2003/02/23,00:00:00 1s -zp 26.4640 -dzp 0.0026 -Nmeas 3 -Ntime 3 -label elixir
+photreg -trans -photcode z -trange 2003/02/24,00:00:00 1s -zp 24.7290 -dzp 0.0197 -Nmeas 4 -Ntime 3 -label elixir
+photreg -trans -photcode u -trange 2003/02/24,00:00:00 1s -zp 25.2980 -dzp 0.0157 -Nmeas 7 -Ntime 3 -label elixir
+photreg -trans -photcode i -trange 2003/02/24,00:00:00 1s -zp 25.7490 -dzp 0.0073 -Nmeas 6 -Ntime 3 -label elixir
+photreg -trans -photcode r -trange 2003/02/24,00:00:00 1s -zp 25.9850 -dzp 0.0094 -Nmeas 5 -Ntime 3 -label elixir
+photreg -trans -photcode g -trange 2003/02/24,00:00:00 1s -zp 26.4400 -dzp 0.0049 -Nmeas 6 -Ntime 3 -label elixir
+photreg -trans -photcode u -trange 2003/03/22,00:00:00 1s -zp 25.4630 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode u -trange 2003/03/23,00:00:00 1s -zp 25.3060 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/03/24,00:00:00 1s -zp 24.8270 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/03/24,00:00:00 1s -zp 25.9920 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/03/24,00:00:00 1s -zp 26.4570 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/03/25,00:00:00 1s -zp 25.7630 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/03/25,00:00:00 1s -zp 26.0230 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/03/30,00:00:00 1s -zp 24.7150 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/03/30,00:00:00 1s -zp 26.4500 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/04/25,00:00:00 1s -zp 24.7530 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/04/25,00:00:00 1s -zp 25.7030 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/04/25,00:00:00 1s -zp 25.9290 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/04/25,00:00:00 1s -zp 26.4030 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/04/26,00:00:00 1s -zp 25.9730 -dzp 0.0195 -Nmeas 11 -Ntime 5 -label elixir
+photreg -trans -photcode r -trange 2003/04/27,00:00:00 1s -zp 25.9080 -dzp 0.0816 -Nmeas 5 -Ntime 3 -label elixir
+photreg -trans -photcode z -trange 2003/05/25,00:00:00 1s -zp 24.7560 -dzp 0.0183 -Nmeas 9 -Ntime 8 -label elixir
+photreg -trans -photcode i -trange 2003/05/25,00:00:00 1s -zp 25.7150 -dzp 0.0170 -Nmeas 3 -Ntime 3 -label elixir
+photreg -trans -photcode r -trange 2003/05/25,00:00:00 1s -zp 25.9720 -dzp 0.0093 -Nmeas 4 -Ntime 3 -label elixir
+photreg -trans -photcode g -trange 2003/05/25,00:00:00 1s -zp 26.4270 -dzp 0.0189 -Nmeas 5 -Ntime 4 -label elixir
+photreg -trans -photcode z -trange 2003/05/26,00:00:00 1s -zp 24.7150 -dzp 0.0262 -Nmeas 3 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/05/26,00:00:00 1s -zp 25.7270 -dzp 0.0006 -Nmeas 3 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/05/26,00:00:00 1s -zp 25.9510 -dzp 0.0040 -Nmeas 3 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/05/26,00:00:00 1s -zp 26.4060 -dzp 0.0021 -Nmeas 2 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/05/27,00:00:00 1s -zp 25.7260 -dzp 0.0242 -Nmeas 6 -Ntime 6 -label elixir
+photreg -trans -photcode r -trange 2003/05/27,00:00:00 1s -zp 25.9800 -dzp 0.0178 -Nmeas 7 -Ntime 6 -label elixir
+photreg -trans -photcode u -trange 2003/05/30,00:00:00 1s -zp 25.2890 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/06/02,00:00:00 1s -zp 24.8460 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/02,00:00:00 1s -zp 25.7290 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/04,00:00:00 1s -zp 25.7570 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/05,00:00:00 1s -zp 25.5920 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/06,00:00:00 1s -zp 25.7440 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/08,00:00:00 1s -zp 25.6810 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/06/08,00:00:00 1s -zp 25.9300 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/06/09,00:00:00 1s -zp 24.7580 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode u -trange 2003/06/09,00:00:00 1s -zp 25.2310 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/22,00:00:00 1s -zp 25.7240 -dzp 0.0071 -Nmeas 4 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/06/22,00:00:00 1s -zp 25.9690 -dzp 0.0201 -Nmeas 4 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/06/22,00:00:00 1s -zp 26.4250 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/06/23,00:00:00 1s -zp 24.8050 -dzp 0.0000 -Nmeas 6 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/06/23,00:00:00 1s -zp 25.9650 -dzp 0.0000 -Nmeas 6 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/06/23,00:00:00 1s -zp 26.4170 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/06/24,00:00:00 1s -zp 24.7930 -dzp 0.0284 -Nmeas 8 -Ntime 2 -label elixir
+photreg -trans -photcode u -trange 2003/06/24,00:00:00 1s -zp 25.2760 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/24,00:00:00 1s -zp 25.7160 -dzp 0.0096 -Nmeas 6 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/06/24,00:00:00 1s -zp 25.9500 -dzp 0.0145 -Nmeas 6 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/06/24,00:00:00 1s -zp 26.4130 -dzp 0.0113 -Nmeas 5 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/06/26,00:00:00 1s -zp 25.7120 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/06/26,00:00:00 1s -zp 25.9610 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/06/26,00:00:00 1s -zp 26.4180 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/06/27,00:00:00 1s -zp 24.7910 -dzp 0.0280 -Nmeas 8 -Ntime 2 -label elixir
+photreg -trans -photcode u -trange 2003/06/27,00:00:00 1s -zp 25.2580 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/27,00:00:00 1s -zp 25.7180 -dzp 0.0075 -Nmeas 6 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/06/27,00:00:00 1s -zp 25.9540 -dzp 0.0169 -Nmeas 6 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/06/27,00:00:00 1s -zp 26.4280 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/06/28,00:00:00 1s -zp 24.7710 -dzp 0.0000 -Nmeas 6 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/28,00:00:00 1s -zp 25.7050 -dzp 0.0000 -Nmeas 6 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/06/28,00:00:00 1s -zp 25.9660 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/06/28,00:00:00 1s -zp 26.4480 -dzp 0.0000 -Nmeas 6 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/06/29,00:00:00 1s -zp 24.7460 -dzp 0.0231 -Nmeas 8 -Ntime 2 -label elixir
+photreg -trans -photcode u -trange 2003/06/29,00:00:00 1s -zp 25.2760 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/29,00:00:00 1s -zp 25.6950 -dzp 0.0067 -Nmeas 8 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/06/29,00:00:00 1s -zp 25.9350 -dzp 0.0178 -Nmeas 8 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/06/29,00:00:00 1s -zp 26.4230 -dzp 0.0123 -Nmeas 8 -Ntime 2 -label elixir
+photreg -trans -photcode z -trange 2003/06/30,00:00:00 1s -zp 24.7410 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode u -trange 2003/06/30,00:00:00 1s -zp 25.1930 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/06/30,00:00:00 1s -zp 25.6660 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/06/30,00:00:00 1s -zp 25.9220 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/06/30,00:00:00 1s -zp 26.3190 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/07/01,00:00:00 1s -zp 24.8010 -dzp 0.0000 -Nmeas 6 -Ntime 1 -label elixir
+photreg -trans -photcode u -trange 2003/07/01,00:00:00 1s -zp 25.2670 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/07/01,00:00:00 1s -zp 25.7180 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/07/01,00:00:00 1s -zp 25.9480 -dzp 0.0189 -Nmeas 6 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/07/01,00:00:00 1s -zp 26.4190 -dzp 0.0019 -Nmeas 6 -Ntime 2 -label elixir
+photreg -trans -photcode z -trange 2003/07/04,00:00:00 1s -zp 24.7980 -dzp 0.0000 -Nmeas 6 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/07/04,00:00:00 1s -zp 25.7090 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/07/04,00:00:00 1s -zp 25.9540 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/07/04,00:00:00 1s -zp 26.4020 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/07/05,00:00:00 1s -zp 24.7620 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode u -trange 2003/07/05,00:00:00 1s -zp 25.2740 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/07/05,00:00:00 1s -zp 25.7080 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/07/05,00:00:00 1s -zp 25.9440 -dzp 0.0116 -Nmeas 7 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/07/05,00:00:00 1s -zp 26.4110 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/07/07,00:00:00 1s -zp 25.8040 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/07/21,00:00:00 1s -zp 24.8630 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/07/22,00:00:00 1s -zp 24.7820 -dzp 0.0010 -Nmeas 14 -Ntime 2 -label elixir
+photreg -trans -photcode u -trange 2003/07/22,00:00:00 1s -zp 25.2880 -dzp 0.0135 -Nmeas 10 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/07/22,00:00:00 1s -zp 25.7240 -dzp 0.0044 -Nmeas 6 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/07/22,00:00:00 1s -zp 25.9580 -dzp 0.0105 -Nmeas 8 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/07/22,00:00:00 1s -zp 26.4220 -dzp 0.0079 -Nmeas 11 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/07/30,00:00:00 1s -zp 25.9360 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/07/30,00:00:00 1s -zp 26.3810 -dzp 0.0431 -Nmeas 78 -Ntime 47 -label elixir
+photreg -trans -photcode z -trange 2003/07/31,00:00:00 1s -zp 24.7420 -dzp 0.0000 -Nmeas 7 -Ntime 1 -label elixir
+photreg -trans -photcode u -trange 2003/07/31,00:00:00 1s -zp 25.2280 -dzp 0.0000 -Nmeas 7 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/07/31,00:00:00 1s -zp 25.6900 -dzp 0.0000 -Nmeas 5 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/07/31,00:00:00 1s -zp 25.9390 -dzp 0.0000 -Nmeas 5 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/07/31,00:00:00 1s -zp 26.3850 -dzp 0.0086 -Nmeas 59 -Ntime 11 -label elixir
+photreg -trans -photcode z -trange 2003/08/01,00:00:00 1s -zp 24.7900 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode u -trange 2003/08/01,00:00:00 1s -zp 25.2630 -dzp 0.0000 -Nmeas 5 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/08/01,00:00:00 1s -zp 25.7130 -dzp 0.0039 -Nmeas 8 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/08/01,00:00:00 1s -zp 25.9480 -dzp 0.0113 -Nmeas 9 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/08/01,00:00:00 1s -zp 26.4230 -dzp 0.0070 -Nmeas 9 -Ntime 2 -label elixir
+photreg -trans -photcode z -trange 2003/08/21,00:00:00 1s -zp 24.7670 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode u -trange 2003/08/21,00:00:00 1s -zp 25.3330 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/08/21,00:00:00 1s -zp 25.7280 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/08/21,00:00:00 1s -zp 25.9570 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/08/21,00:00:00 1s -zp 26.4960 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/08/22,00:00:00 1s -zp 24.8120 -dzp 0.0202 -Nmeas 3 -Ntime 2 -label elixir
+photreg -trans -photcode u -trange 2003/08/22,00:00:00 1s -zp 25.4170 -dzp 0.0297 -Nmeas 8 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/08/22,00:00:00 1s -zp 25.7620 -dzp 0.0057 -Nmeas 4 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/08/22,00:00:00 1s -zp 26.0250 -dzp 0.0141 -Nmeas 2 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/08/22,00:00:00 1s -zp 26.5340 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/08/23,00:00:00 1s -zp 24.7390 -dzp 0.0374 -Nmeas 2 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/08/23,00:00:00 1s -zp 25.7560 -dzp 0.0007 -Nmeas 2 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/08/23,00:00:00 1s -zp 26.0110 -dzp 0.0112 -Nmeas 2 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/08/23,00:00:00 1s -zp 26.5310 -dzp 0.0150 -Nmeas 2 -Ntime 2 -label elixir
+photreg -trans -photcode z -trange 2003/08/25,00:00:00 1s -zp 24.7790 -dzp 0.0178 -Nmeas 4 -Ntime 2 -label elixir
+photreg -trans -photcode u -trange 2003/08/25,00:00:00 1s -zp 25.3610 -dzp 0.0563 -Nmeas 6 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/08/25,00:00:00 1s -zp 25.7800 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/08/25,00:00:00 1s -zp 26.0240 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/08/25,00:00:00 1s -zp 26.4770 -dzp 0.0472 -Nmeas 3 -Ntime 2 -label elixir
+photreg -trans -photcode z -trange 2003/08/27,00:00:00 1s -zp 24.7930 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/08/27,00:00:00 1s -zp 26.5100 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/08/28,00:00:00 1s -zp 24.8260 -dzp 0.0129 -Nmeas 5 -Ntime 3 -label elixir
+photreg -trans -photcode u -trange 2003/08/28,00:00:00 1s -zp 25.3980 -dzp 0.0000 -Nmeas 5 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/08/28,00:00:00 1s -zp 25.7680 -dzp 0.0067 -Nmeas 2 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/08/28,00:00:00 1s -zp 26.5090 -dzp 0.0025 -Nmeas 4 -Ntime 2 -label elixir
+photreg -trans -photcode z -trange 2003/08/29,00:00:00 1s -zp 24.7900 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode u -trange 2003/08/29,00:00:00 1s -zp 25.3900 -dzp 0.0013 -Nmeas 7 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/08/29,00:00:00 1s -zp 25.7720 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/08/29,00:00:00 1s -zp 25.9940 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/08/29,00:00:00 1s -zp 26.5010 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/08/30,00:00:00 1s -zp 24.8190 -dzp 0.0130 -Nmeas 4 -Ntime 3 -label elixir
+photreg -trans -photcode u -trange 2003/08/30,00:00:00 1s -zp 25.4250 -dzp 0.0351 -Nmeas 6 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/08/30,00:00:00 1s -zp 25.7790 -dzp 0.0032 -Nmeas 4 -Ntime 3 -label elixir
+photreg -trans -photcode r -trange 2003/08/30,00:00:00 1s -zp 26.0260 -dzp 0.0215 -Nmeas 3 -Ntime 3 -label elixir
+photreg -trans -photcode g -trange 2003/08/30,00:00:00 1s -zp 26.5390 -dzp 0.0227 -Nmeas 5 -Ntime 3 -label elixir
+photreg -trans -photcode z -trange 2003/08/31,00:00:00 1s -zp 24.8040 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode u -trange 2003/08/31,00:00:00 1s -zp 25.4070 -dzp 0.0000 -Nmeas 5 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/08/31,00:00:00 1s -zp 25.7790 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode r -trange 2003/08/31,00:00:00 1s -zp 26.0170 -dzp 0.0000 -Nmeas 1 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/08/31,00:00:00 1s -zp 26.5290 -dzp 0.0000 -Nmeas 2 -Ntime 1 -label elixir
+photreg -trans -photcode z -trange 2003/09/03,00:00:00 1s -zp 24.7940 -dzp 0.0089 -Nmeas 5 -Ntime 2 -label elixir
+photreg -trans -photcode i -trange 2003/09/03,00:00:00 1s -zp 25.7510 -dzp 0.0050 -Nmeas 4 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/09/03,00:00:00 1s -zp 25.9990 -dzp 0.0094 -Nmeas 2 -Ntime 2 -label elixir
+photreg -trans -photcode g -trange 2003/09/03,00:00:00 1s -zp 26.4850 -dzp 0.0437 -Nmeas 5 -Ntime 2 -label elixir
+photreg -trans -photcode z -trange 2003/09/04,00:00:00 1s -zp 24.7710 -dzp 0.0075 -Nmeas 7 -Ntime 2 -label elixir
+photreg -trans -photcode u -trange 2003/09/04,00:00:00 1s -zp 25.3510 -dzp 0.0000 -Nmeas 4 -Ntime 1 -label elixir
+photreg -trans -photcode i -trange 2003/09/04,00:00:00 1s -zp 25.7470 -dzp 0.0061 -Nmeas 4 -Ntime 2 -label elixir
+photreg -trans -photcode r -trange 2003/09/04,00:00:00 1s -zp 25.9760 -dzp 0.0000 -Nmeas 3 -Ntime 1 -label elixir
+photreg -trans -photcode g -trange 2003/09/04,00:00:00 1s -zp 26.4760 -dzp 0.0049 -Nmeas 4 -Ntime 2 -label elixir
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/ConfigInit.c	(revision 22322)
@@ -0,0 +1,118 @@
+# include "imregister.h"
+# include "imphot.h"
+
+int success;
+
+void ConfigInitImphot (int *argc, char **argv) {
+
+  int i, NDB;
+  char *config, *file, ElixirBase[80], catdir[256];
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+
+  success = TRUE;
+
+  WarnConfig (config, "CATDIR",                      "%s", 0, catdir);
+  WarnConfig (config, "PHOTCODE_FILE",         	     "%s", 0, MasterPhotcodeFile);
+  sprintf (ImPhotDB, "%s/Images.dat", catdir);
+
+  /* small text databases: filters, camera defs */ 
+  WarnConfig (config, "TEMPERATURE_LOG",             "%s", 0, TempLogFile);
+  WarnConfig (config, "FILTER_LIST",                 "%s", 0, FilterList);
+  WarnConfig (config, "CAMERA_CONFIG",               "%s", 0, CameraConfig);
+  WarnConfig (config, "DETREND_RECIPES",             "%s", 0, RecipeFile);
+						   
+  /* pixel scale for FWHM */ 
+  WarnConfig (config, "ASEC_PIX",                    "%lf", 0, &ARCSEC_PIXEL);
+
+  /* keyword abstractions for parse_time */	   
+  WarnConfig (config, "DATE-KEYWORD",                "%s", 0, DateKeyword);
+  WarnConfig (config, "DATE-MODE",                   "%s", 0, DateMode);
+  WarnConfig (config, "UT-KEYWORD",                  "%s", 0, UTKeyword);
+  WarnConfig (config, "MJD-KEYWORD",                 "%s", 0, MJDKeyword);
+  WarnConfig (config, "JD-KEYWORD",                  "%s", 0, JDKeyword);
+						   
+  /* keyword abstractions for iminfo */		   
+  WarnConfig (config, "EXPTIME-KEYWORD",             "%s", 0, ExptimeKeyword);
+  WarnConfig (config, "IMAGETYPE-KEYWORD",           "%s", 0, ImagetypeKeyword);
+  WarnConfig (config, "CCDNUM-KEYWORD",              "%s", 0, CCDnumKeyword);
+  WarnConfig (config, "FILTER-KEYWORD",              "%s", 0, FilterKeyword);
+  WarnConfig (config, "AIRMASS-KEYWORD",             "%s", 0, AirmassKeyword);
+  WarnConfig (config, "FOCUS-KEYWORD",               "%s", 0, FocusKeyword);
+  WarnConfig (config, "ROTATION-KEYWORD",            "%s", 0, RotationKeyword);
+  WarnConfig (config, "DETTEMP-KEYWORD",             "%s", 0, DettempKeyword);
+  WarnConfig (config, "TELDATA1-KEYWORD",            "%s", 0, Teldata1Keyword);
+  WarnConfig (config, "TELDATA2-KEYWORD",            "%s", 0, Teldata2Keyword);
+  WarnConfig (config, "TELDATA3-KEYWORD",            "%s", 0, Teldata3Keyword);
+  WarnConfig (config, "CAMERA-KEYWORD",              "%s", 0, CameraKeyword);
+
+  ScanConfig (config, "CAMERA",                      "%s", 0, Camera);
+  ScanConfig (config, "SEEING_REF_CCD",              "%s", 0, SeeingREFCCD);
+
+  /* optional values */
+  ScanConfig (config, "RA-DDD-KEYWORD",              "%s", 0, RADecDegKeyword);
+  ScanConfig (config, "DEC-DDD-KEYWORD",             "%s", 0, DECDecDegKeyword);
+  ScanConfig (config, "RA-HMS-KEYWORD",              "%s", 0, RASexigKeyword);
+  ScanConfig (config, "DEC-DMS-KEYWORD",             "%s", 0, DECSexigKeyword);
+
+  if (!RADecDegKeyword[0] & !DECDecDegKeyword[0] && !RASexigKeyword[0] && !DECSexigKeyword[0]) {
+    fprintf (stderr, "missing astrometry configuration information\n");
+    success = FALSE;
+  }
+						   
+  WarnConfig (config, "imstats",                     "%s", 0, ElixirBase);
+  sprintf (ImstatFifo, "%s.photcode", ElixirBase);   
+  WarnConfig (config, "ptolemy",                     "%s", 0, ElixirBase);
+  sprintf (PtolemyFifo, "%s.photcode", ElixirBase);
+
+  if (!ScanConfig (config, "CONNECT", "%s",  0, CONNECT)) {
+    sprintf (CONNECT, "/usr/bin/rsh");
+  }
+
+  /* load Detrend Alt Databases paths */
+  NDB = 10;
+  NDetrendAltDB = 0;
+  ALLOCATE (DetrendAltDB, char *, NDB);
+  ALLOCATE (DetrendAltDB[NDetrendAltDB], char, 256);
+  for (i = 1; ScanConfig (config, "DETREND_ALT_DB", "%s", i, DetrendAltDB[NDetrendAltDB]); i++) {
+    NDetrendAltDB ++;
+    if (NDetrendAltDB == NDB) {
+      NDB += 10;
+      REALLOCATE (DetrendAltDB, char *, NDB);
+    }
+    ALLOCATE (DetrendAltDB[NDetrendAltDB], char, 256);
+  }
+  free (DetrendAltDB[NDetrendAltDB]);
+
+  if (! success) {
+    fprintf (stderr, "ERROR: problem with elixir configuration\n");
+    exit (1);
+  }
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+
+}
+
+void WarnConfig (char *config, char *key, char * mode, int N, void *var) {
+  if (!ScanConfig (config, key, mode, N, var)) {
+    fprintf (stderr, "missing config variable %s\n", key);
+    success = FALSE;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/args.c	(revision 22322)
@@ -0,0 +1,92 @@
+# include "imregister.h" 
+# include "imphot.h"
+
+/* criteria struct is global */
+int args (int argc, char **argv) {
+
+  int N;
+
+  ConfigInit (&argc, argv); /* load elixir config data */
+
+  /* interpret command-line arguments */
+  if (!get_trange_arguments (&argc, argv, &criteria.tstart, &criteria.tstop, &criteria.Ntimes)) {
+    fprintf (stderr, "ERROR: syntax error\n");
+    exit (1);
+  }
+
+  /* select by image photcode */
+  criteria.PhotcodeSelect = FALSE;
+  criteria.photcode = 0;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    if (!(criteria.photcode = GetPhotcodeCodebyName (argv[N]))) {
+      fprintf (stderr, "ERROR: photcode not found in photcode table\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    criteria.PhotcodeSelect = TRUE;
+  }
+
+  /* string in image name */
+  criteria.NameSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.NameSelect = TRUE;
+  }
+
+  /* string in image name */
+  criteria.CodeSelect = FALSE;
+  criteria.Code = 0;
+  if ((N = get_argument (argc, argv, "-code"))) {
+    criteria.CodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    criteria.Code = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  options.table = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-fits"))) {
+    remove_argument (N, &argc, argv);
+    options.table = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  options.bintable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-binfits"))) {
+    remove_argument (N, &argc, argv);
+    options.bintable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* desired action */
+  options.modify = FALSE;
+  options.ModifyValue = options.ModifyEntry = NULL;
+  if ((N = get_argument (argc, argv, "-flag"))) {
+    remove_argument (N, &argc, argv);
+    options.ModifyEntry = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    options.ModifyValue = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    options.modify = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  FORCE_READ = FALSE;
+  if ((N = get_argument (argc, argv, "-force"))) {
+    remove_argument (N, &argc, argv);
+    FORCE_READ = TRUE;
+  }
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: imphotsearch [config ops] [-trange start stop/delta] [-photcode code] [-fits out.fits]\n");
+    exit (1);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/db_load.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/db_load.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/db_load.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "imregister.h"
+# include "imphot.h"
+
+enum {FITS, TEXT};
+
+int db_load (FITS_DB *db) {
+
+  int Nx, Ny, Naxis;
+  int mode, status;
+
+  /* database name must be set first */
+  if (db == NULL) {
+    fprintf (stderr, "db handle is not set\n");
+    return (FALSE);
+  }
+
+  /* init & load in FITS table data - return FALSE on error */
+  if (!gfits_fread_header (db[0].f, &db[0].header)) {
+    fprintf (stderr, "can't read primary header\n"); 
+    return (FALSE);
+  }
+
+  gfits_scan (&db[0].header, "NAXIS",  "%d", 1, &Naxis);
+  gfits_scan (&db[0].header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (&db[0].header, "NAXIS2", "%d", 1, &Ny);
+  
+  mode = FITS;
+  if ((Naxis == 2) && (Nx == 1106) && (Ny == 1024)) {
+    mode = TEXT;
+  }
+  
+  /* how do we decide if it is text or fits? must examine header */
+  if (FITS) {
+    status = rfits (db);
+  } else {
+    status = rtext (db);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/dumpfits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/dumpfits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/dumpfits.c	(revision 22322)
@@ -0,0 +1,141 @@
+# include "imregister.h"
+# include "imphot.h"
+
+/* write out complete binary FITS table in format of db */
+int DumpFitsBintable (char *filename, Image *image, int *match, int Nmatch) {
+
+  int i, j;
+  FILE *f;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  Image *subset;
+
+  ALLOCATE (subset, Image, MAX (1, Nmatch));
+  for (i = 0; i < Nmatch; i++){
+    j = match[i];
+    memcpy (&subset[i], &image[j], sizeof (Image));
+  }
+
+  /* open file for output */
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open output file %s\n", filename);
+    exit (1);
+  }
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+
+  ftable.header = &theader;
+  gfits_table_set_Image (&ftable, subset, Nmatch);
+
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &ftable);
+  fclose (f);
+  exit (0);
+}
+
+int DumpFitsTable (char *filename, Image *image, int *match, int Nmatch) {
+  
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  Image *subset;
+  FILE *f;
+  char *startstr, *filtstr, *datestr, *line;
+  int i;
+  double zp, dzp, ra, dec, airmass, sky;
+  time_t tsecond;
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+  
+  /* the ASCII table is always a little harder than the binary:
+   * we need to build the data line a bit carefully 
+   */
+
+  /* create table header */
+  gfits_create_table_header (&theader, "TABLE", "ZPTS");
+
+  /* add current date/time to header */
+  ohana_str_to_time ("now", &tsecond);
+  datestr = ohana_sec_to_date (tsecond);
+  gfits_modify (&header,  "DATE", "%s", 1, datestr);
+  gfits_modify (&theader, "DATE", "%s", 1, datestr);
+  
+  /* define table layout */
+  gfits_define_table_column (&theader, "A20",   "START_TIME", "start time of measurement", "yyyy/mm/dd,hh:mm:ss");
+  gfits_define_table_column (&theader, "A10",   "FILTER",     "filter and camera name",    "");
+  gfits_define_table_column (&theader, "F8.4",  "ZP_OBS",     "measured zero point",       "mag");
+  gfits_define_table_column (&theader, "F7.4",  "ZP_ERR",     "error on zero point",       "mag");
+  gfits_define_table_column (&theader, "F11.6", "RA",         "RA (J2000)",                "dec. degrees");
+  gfits_define_table_column (&theader, "F11.6", "DEC",        "DEC (J2000)",               "dec. degrees");
+  gfits_define_table_column (&theader, "F7.3",  "C_AIRMASS",  "airmass coeff",             "mag per airmass"); 
+  gfits_define_table_column (&theader, "F7.1",  "SKY",        "median sky flux",           "counts");
+  gfits_define_table_column (&theader, "I6",    "NSTAR",      "Number of stars in image",  "stars");
+
+  /* define TNULL, TNVAL values */
+  gfits_modify (&theader, "TNULL1",  "%s", 1, "NULL"); /* START_TIME */
+  gfits_modify (&theader, "TNULL2",  "%s", 1, "NULL"); /* FILTER     */
+  gfits_modify (&theader, "TNULL3",  "%s", 1, "NaN");  /* ZP_OBS     */
+  gfits_modify (&theader, "TNULL4",  "%s", 1, "NaN");  /* ZP_ERR     */
+  gfits_modify (&theader, "TNULL5",  "%s", 1, "NaN");  /* RA         */
+  gfits_modify (&theader, "TNULL6",  "%s", 1, "NaN");  /* DEC        */
+  gfits_modify (&theader, "TNULL7",  "%s", 1, "NaN");  /* C_AIRMASS  */
+  gfits_modify (&theader, "TNULL8",  "%s", 1, "NaN");  /* SKY        */
+  gfits_modify (&theader, "TNULL9",  "%s", 1,  "-1");  /* NSTAR      */
+
+  gfits_modify (&theader, "TNVAL1",  "%s", 1, "NA");   /* START_TIME */
+  gfits_modify (&theader, "TNVAL2",  "%s", 1, "NA");   /* FILTER     */
+  gfits_modify (&theader, "TNVAL3",  "%s", 1, "Inf");  /* ZP_OBS     */
+  gfits_modify (&theader, "TNVAL4",  "%s", 1, "Inf");  /* ZP_ERR     */
+  gfits_modify (&theader, "TNVAL5",  "%s", 1, "Inf");  /* RA         */
+  gfits_modify (&theader, "TNVAL6",  "%s", 1, "Inf");  /* DEC        */
+  gfits_modify (&theader, "TNVAL7",  "%s", 1, "Inf");  /* C_AIRMASS  */
+  gfits_modify (&theader, "TNVAL8",  "%s", 1, "Inf");  /* SKY        */
+  gfits_modify (&theader, "TNVAL9",  "%s", 1,  "-2");  /* NSTAR      */
+
+  /* add data to table */
+  for (i = 0; i < Nmatch; i++) {
+    subset   = &image[match[i]];
+    startstr = ohana_sec_to_date (subset[0].tzero);
+    filtstr  = GetPhotcodeNamebyCode (subset[0].photcode);
+    zp       = subset[0].Mcal;
+    dzp      = subset[0].dMcal;
+    XY_to_RD (&ra, &dec, 0.0, 0.0, &subset[0].coords);
+    airmass  = subset[0].secz;
+    sky      = subset[0].Myyyy + 0x8000;
+
+    /* we should get an error here if we don't construct this line correctly */
+    line = gfits_table_print (&ftable, startstr, filtstr, zp, dzp, ra, dec, airmass, sky, subset[0].nstar);
+    gfits_add_rows (&ftable, line, 1, strlen(line));
+    free (line);
+    free (startstr);
+  }
+
+  /* write data to output file */
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "Failure writing fits table\n");
+    exit (1);
+  }
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &ftable);
+  fclose (f);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/modify.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/modify.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/modify.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "imregister.h"
+# include "imphot.h"
+
+void ModifySubset (FITS_DB *db, Image *image, int Nimage, int *match, int Nmatch) {
+
+  int i, j;
+
+  /* modify the selected entries */
+  for (j = 0; j < Nmatch; j++) {
+
+    i = match[j];
+
+    if (options.modify) {
+      if (!strcasecmp (options.ModifyEntry, "and")) {
+	image[i].code &= atoi (options.ModifyValue);
+      }
+      if (!strcasecmp (options.ModifyEntry, "or")) {
+	image[i].code |= atoi (options.ModifyValue);
+      }
+      if (!strcasecmp (options.ModifyEntry, "xor")) {
+	image[i].code ^= atoi (options.ModifyValue);
+      }
+      if (!strcasecmp (options.ModifyEntry, "=")) {
+	image[i].code = atoi (options.ModifyValue);
+      }
+    }
+  }
+
+  /** we may later want to pull this out and put it elsewhere **/
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, match, Nmatch);
+  for (i = 0; i < Nmatch; i++) {
+    gfits_convert_Image ((Image *) db[0].vtable.buffer[i], sizeof (Image), 1);
+  }
+  gfits_db_update (db);
+  gfits_db_close (db);
+  gfits_db_free (db);
+
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/output.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/output.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/output.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "imregister.h"
+# include "imphot.h"
+
+int output (Image *image, int *match, int Nmatch) {
+
+  int status;
+
+  /* output the selected entries */
+  if (options.table != (char *) NULL) {
+    status = DumpFitsTable (options.table, image, match, Nmatch);
+    return (TRUE);
+  } 
+
+  /* output the selected entries */
+  if (options.bintable != (char *) NULL) {
+    status = DumpFitsBintable (options.bintable, image, match, Nmatch);
+    return (TRUE);
+  } 
+
+  PrintSubset (image, match, Nmatch);
+  return (TRUE);
+}
+
+int PrintSubset (Image *image, int *match, int Nmatch) {
+
+  int i, j;
+  char *timestr, *photstr;
+  static char PhotError[] = "unknown";
+
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j];
+      
+    /* convert UNIX time to Elixir-style date string */
+    timestr = ohana_sec_to_date (image[i].tzero);
+      
+    /* convert photcode to filter name */
+    photstr = GetPhotcodeNamebyCode (image[i].photcode);
+    if (photstr == (char *) NULL) photstr = PhotError;
+      
+    fprintf (stdout, "%s %s %s  %7.4f %7.4f  %7.4f %5d %02x\n", image[i].name, photstr, timestr, 
+	     image[i].Mcal, image[i].dMcal, image[i].secz, image[i].nstar, image[i].code); 
+    free (timestr);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/rfits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/rfits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/rfits.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "imregister.h"
+
+/* load the rest of the db table into memory (first extension only) */
+int rfits (FITS_DB *db) {
+
+  /* database name must be set first */
+  if (db == NULL) {
+    fprintf (stderr, "db handle is not set\n");
+    return (FALSE);
+  }
+  if (!gfits_fread_matrix (db[0].f, &db[0].matrix, &db[0].header)) {
+    fprintf (stderr, "can't read primary matrix");
+    return (FALSE);
+  }
+  if (!gfits_fread_header (db[0].f, &db[0].theader)) {
+    fprintf (stderr, "can't read table header");
+    return (FALSE);
+  }
+  if (!gfits_fread_ftable_data (db[0].f, &db[0].ftable)) {
+    fprintf (stderr, "can't read table data");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/rtext.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/rtext.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/rtext.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "imregister.h"
+# include "imphot.h"
+
+int rtext (FITS_DB *db) {
+
+  int Nimage, size, nimage;
+  struct stat filestatus;
+  Image *image;
+
+  /* check that file size makes sense */
+  Nimage = 0;
+  gfits_scan (&db[0].header, "NIMAGES", "%d", 1, &Nimage);
+  if (stat (db[0].filename, &filestatus) == -1) {
+    if (VERBOSE) fprintf (stderr, "ERROR: failed to get status of image catalog\n");
+    exit (1);
+  }
+  size = Nimage*sizeof(Image) + db[0].header.size;
+  if (size != filestatus.st_size) {
+    int Ndata;
+
+    Ndata = (filestatus.st_size - db[0].header.size) / sizeof (Image);
+    if (VERBOSE) fprintf (stderr, "ERROR: image catalog has inconsistent size\n");
+    if (VERBOSE) fprintf (stderr, "header: %d, data: %d\n", Nimage, Ndata);
+    if (!FORCE_READ) exit (1);
+    Nimage = Ndata;
+  } 
+
+  /* create a dummy set of table information */
+  /* (original table has NAXIS = 2, change to 0) */
+  gfits_modify (&db[0].header, "NAXIS", "%d", 1, 0);
+  gfits_create_matrix (&db[0].header, &db[0].matrix);
+  gfits_table_mkheader_Image (&db[0].theader);
+  db[0].ftable.header = &db[0].theader;
+
+  /* alloc, read images */
+  ALLOCATE (image, Image, MAX (Nimage, 1));
+  nimage = fread (image, sizeof(Image), Nimage, db[0].f);
+  if (nimage != Nimage) {
+    if (VERBOSE) fprintf (stderr, "ERROR: problem loading image catalog\n");
+    exit (1);
+  } 
+  db[0].ftable.buffer = (char *) image;
+  gfits_modify (&db[0].theader, "NAXIS2", "%d", 1, Nimage);
+  db[0].theader.Naxis[1] = Nimage;
+  db[0].ftable.size = gfits_data_size (&db[0].theader);
+  
+  return (TRUE);
+}
+
+/* the old Image.dat files used a fake FITS header defining a finite data block
+   this function reads in the 
+*/
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/subset.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/subset.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imphot/subset.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "imregister.h"
+# include "imphot.h"
+
+int *subset (Image *image, int Nimage, int *nsubset) {
+
+  int i, j, status;
+  int Nsubset, NameSelectLength;
+  int *index;
+
+  NameSelectLength = 0;
+
+  /* allocate space for reference lists */
+  Nsubset = 0;
+  ALLOCATE (index, int, Nimage);
+  
+  if (criteria.NameSelect) {
+    NameSelectLength = strlen(criteria.Name);
+  }
+
+  for (i = 0; i < Nimage; i++) {
+    for (j = 0, status = FALSE; !status && (j < criteria.Ntimes); j++) {
+      status = (image[i].tzero >= criteria.tstart[j]) && (image[i].tzero <= criteria.tstop[j]);
+    }
+    if (!status && criteria.Ntimes) continue;
+    if (criteria.PhotcodeSelect && (image[i].photcode != criteria.photcode)) continue;
+    if (criteria.CodeSelect     && (image[i].code != criteria.Code)) continue;
+    if (criteria.NameSelect     && strncasecmp (image[i].name, criteria.Name, NameSelectLength)) continue;
+
+    index[Nsubset] = i;
+    Nsubset ++;
+  }
+  *nsubset = Nsubset;
+  return (index);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/ConfigPID.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/ConfigPID.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/ConfigPID.c	(revision 22322)
@@ -0,0 +1,86 @@
+# include "imregister.h"
+# include "imreg.h"
+
+static char *PIDMaster = (char *) NULL;
+
+int ConfigPID (char *pidfile) {
+
+  pid_t pid;
+  char *username, machine[256];
+  FILE *f;
+
+  ALLOCATE (username, char, 256);
+  if (!LoadPID (pidfile, &pid, username, machine)) {
+    /* no PID file, make new one */
+
+    pid = getpid ();
+
+    free (username);
+    username = getenv ("USER");
+    if (username == (char *) NULL) {
+      fprintf (stderr, "error getting username\n");
+      exit (2);
+    }
+    bzero (machine, 256);
+    if (gethostname (machine, 256)) {
+      fprintf (stderr, "error getting hostname\n");
+      exit (2);
+    }
+
+    f = fopen (pidfile, "w");
+    if (f == (FILE *) NULL) { 
+      fprintf (stderr, "can't write to PID file %s\n", pidfile);
+      exit (2);
+    }
+
+    fprintf (f, "PID:     %d\n", pid);
+    fprintf (f, "USER:    %-s\n", username);
+    fprintf (f, "MACHINE: %-s\n", machine);
+    fclose (f);
+
+    PIDMaster = pidfile;
+    return (TRUE); 
+  }
+
+  /* PID file exists, warn & exit */
+  fprintf (stderr, "elixir is apparently running:\n\n");
+  fprintf (stderr, "  machine: %s\n", machine);
+  fprintf (stderr, "  user: %s\n", username);
+  fprintf (stderr, "  PID: %d\n", pid);
+  fprintf (stderr, "  remove %s if elixir has died unexpectedly\n", pidfile);
+  Shutdown (1);
+  return (FALSE); 
+}
+
+void RemovePID () {
+  
+  if (PIDMaster == (char *) NULL) 
+    return;
+  
+  if (unlink (PIDMaster)) {
+    fprintf (stderr, "error deleting PID File %s\n", PIDMaster);
+  }   
+}
+
+int LoadPID (char *file, pid_t *pid, char *username, char *machine) {
+
+  FILE *f;
+
+  f = fopen (file, "r");
+  if (f == (FILE *) NULL) { 
+    return (FALSE);
+  }
+
+  fscanf (f, "%*s %d", pid);
+  fscanf (f, "%*s %s", username);
+  fscanf (f, "%*s %s", machine);
+  fclose (f);
+
+  return (TRUE);
+}
+
+int Shutdown (int status) {
+
+  RemovePID ();
+  exit (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/FifoOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/FifoOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/FifoOps.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include "imregister.h"
+# include "imreg.h"
+
+int InitFifo (Fifo *fifo, int Nalloc, int Nextra) {
+
+  if (Nextra >= Nalloc) {
+    fprintf (stderr, "absurd fifo definition\n");
+    return (FALSE);
+  }
+
+  fifo[0].Nalloc = Nalloc;
+  fifo[0].Nextra = Nextra;
+  fifo[0].Nmaxread = Nalloc - Nextra;
+  fifo[0].Nlast = 0;
+  fifo[0].Nbuffer = 0;
+
+  ALLOCATE (fifo[0].buffer, char, fifo[0].Nalloc);
+
+  return (TRUE);
+
+}
+
+int FlushFifo (Fifo *fifo) {
+
+  fifo[0].Nlast = 0;
+  fifo[0].Nbuffer = 0;
+
+  return (TRUE);
+
+}
+
+/* after a shift, we can always read 
+   fifo[0].Nmaxread 
+   bytes into 
+   &fifo[0].buffer[Nbuffer] 
+   which is the byte after then end of existing data */
+
+int ShiftFifo (Fifo *fifo) {
+
+  int Nextra, Nshift;
+
+  Nextra = fifo[0].Nextra;
+  Nshift = fifo[0].Nbuffer - fifo[0].Nextra;
+  if (Nshift <= 0) return (TRUE);
+
+  memcpy (fifo[0].buffer, &fifo[0].buffer[Nshift], Nextra);
+  fifo[0].Nbuffer = Nextra;
+  fifo[0].Nlast = Nextra;
+
+  return (TRUE);
+}
+
+/* like a standard read, ReadtoFifo returns Nbytes read,
+   -1 for sock busy, or 0 for sock closed */
+
+int ReadtoFifo (Fifo *fifo, int sock) {
+
+  int Nread;
+  int Nbuffer, Nmaxread;
+
+  if (sock == 0) {
+    fprintf (stderr, "error with socket?\n");
+    return (0);
+  }
+
+  fifo[0].Nlast = fifo[0].Nbuffer;
+
+  Nbuffer = fifo[0].Nbuffer;
+  Nmaxread = fifo[0].Nmaxread;
+  Nread = read (sock, &fifo[0].buffer[Nbuffer], Nmaxread);
+
+  if (Nread > 0) fifo[0].Nbuffer += Nread;
+
+  if (Nread == -1) {
+    /* check for possible errors.  anything other than EAGAIN
+       is bad and should indicate the connection is down */
+    switch (errno) {
+    case EAGAIN:
+    case EIO:
+      Nread = -1;
+      break;
+    default:
+      fprintf (stderr, "read error: %d\n", errno);
+      Nread = 0;
+      break;
+    }
+  }
+
+  return (Nread);
+}
+
+void FreeFifo (Fifo *fifo) {
+
+  if (fifo[0].buffer != (char *) NULL) {
+    free (fifo[0].buffer);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/SetSignals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/SetSignals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/SetSignals.c	(revision 22322)
@@ -0,0 +1,178 @@
+# include "imregister.h"
+# include "imreg.h"
+
+void SIG_DIE (int sig) {
+  fprintf (stderr, "trapped signal %d, exiting\n", sig);
+  Shutdown (1);
+}
+
+void SIG_PIPE (int sig) {
+  fprintf (stderr, "pipe signal: %d\n", sig);
+}
+
+void SetSignals () {
+
+  /* use default settings */
+  signal (SIGKILL,   SIG_DFL);    
+  signal (SIGCONT,   SIG_DFL);    
+  signal (SIGSTOP,   SIG_DFL);    
+  signal (SIGCHLD,   SIG_DFL);
+
+  /* exit on these signals */
+  signal (SIGILL,    SIG_DIE);     
+  signal (SIGABRT,   SIG_DIE);    
+  signal (SIGFPE,    SIG_DIE);     
+  signal (SIGSEGV,   SIG_DIE);    
+  signal (SIGTERM,   SIG_DIE);    
+  signal (SIGBUS,    SIG_DIE);     
+  signal (SIGTRAP,   SIG_DIE);    
+  signal (SIGXCPU,   SIG_DIE);    
+  signal (SIGXFSZ,   SIG_DIE);    
+  signal (SIGIOT,    SIG_DIE);     
+  signal (SIGINT,    SIG_DIE);
+
+  signal (SIGPIPE,   SIG_PIPE);    
+
+  /* ignore these signals */
+  signal (SIGHUP,    SIG_IGN);
+  signal (SIGQUIT,   SIG_IGN);
+  signal (SIGALRM,   SIG_IGN);    
+  signal (SIGUSR1,   SIG_IGN);    
+  signal (SIGUSR2,   SIG_IGN);    
+  signal (SIGTSTP,   SIG_IGN);    
+  signal (SIGTTIN,   SIG_IGN);    
+  signal (SIGTTOU,   SIG_IGN);    
+  signal (SIGPROF,   SIG_IGN);    
+  signal (SIGURG,    SIG_IGN);     
+  signal (SIGVTALRM, SIG_IGN);  
+  signal (SIGIO,     SIG_IGN);      
+
+  /* signals which are not always defined */
+# ifdef SIGPOLL
+  signal (SIGPOLL,   SIG_IGN);    
+# endif
+# ifdef SIGPWR
+  signal (SIGPWR, SIG_DIE);     /* power failure (Sys V) */
+# endif
+# ifdef SIGWINCH
+  signal (SIGWINCH, SIG_IGN);   /* window resized (4.3BSD) */
+# endif
+# ifdef SIGUNUSED
+  signal (SIGUNUSED, SIG_DFL);  
+# endif
+# ifdef SIGSYS
+  signal (SIGSYS,    SIG_DIE);     
+# endif
+# ifdef SIGEMT
+ signal (SIGEMT,    SIG_DIE); 
+# endif
+# ifdef SIGSTKFLT
+  signal (SIGSTKFLT, SIG_DIE);  
+# endif
+# ifdef SIGINFO
+ signal (SIGINFO,   SIG_DIE); 
+# endif
+# ifdef SIGCLD
+  signal (SIGCLD,    SIG_DFL);     
+# endif
+# ifdef SIGLOST
+  signal (SIGLOST,   SIG_IGN); 
+# endif
+# ifdef SIGUNUSED
+  signal (SIGUNUSED,   SIG_IGN); 
+# endif
+
+}
+
+void KillProcess (char *pidfile) {
+  
+  pid_t pid;
+  char username[256], machine[256];
+  char line[512];
+  int i, wsock, rsock;
+  struct stat filestat;
+
+  if (!LoadPID (pidfile, &pid, username, machine)) {
+    fprintf (stderr, "imstatreg is not running\n");
+    exit (0);
+  }
+
+  /* send signal to remote machine */
+  if (!rconnect (machine, CONNECT, &rsock, &wsock)) {
+    fprintf (stderr, "can't make connection to machine %s to kill process %d\n", machine, pid);
+    exit (1);
+  }
+  sprintf (line, "kill -TERM %d\n", pid);
+  write (wsock, line, strlen (line));
+
+  for (i = 0; i < 300; i++) {
+    if (stat (pidfile, &filestat) == -1) exit (0);
+    usleep (100000);
+  }
+  fprintf (stderr, "imstatreg is still running\n");
+  exit (2);
+}
+
+void StatusProcess (char *pidfile) {
+  
+  pid_t pid;
+  char username[256], machine[256];
+
+  if (!LoadPID (pidfile, &pid, username, machine)) {
+    fprintf (stderr, "imstatreg is not running\n");
+    exit (2);
+  }
+
+  /* PID file exists, warn & exit */
+  fprintf (stderr, "imstatreg is apparently running:\n\n");
+  fprintf (stderr, "  machine: %s\n", machine);
+  fprintf (stderr, "  user: %s\n", username);
+  fprintf (stderr, "  PID: %d\n", pid);
+  fprintf (stderr, "  remove %s if imstatreg has died unexpectedly\n", pidfile);
+
+  exit (0);
+}
+
+/*
+
+  SIGHUP             1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+  SIGINT             2        A      Interrupt from keyboard
+  SIGQUIT            3        C      Quit from keyboard
+  SIGILL             4        C      Illegal Instruction
+  SIGABRT            6        C      Abort signal from abort(3)
+  SIGFPE             8        C      Floating point exception
+  SIGKILL            9       AEF     Kill signal
+  SIGSEGV           11        C      Invalid memory reference
+  SIGPIPE           13        A      Broken pipe: write to pipe with no readers
+  SIGALRM           14        A      Timer signal from alarm(2)
+  SIGTERM           15        A      Termination signal
+  SIGUSR1        30,10,16     A      User-defined signal 1
+  SIGUSR2        31,12,17     A      User-defined signal 2
+  SIGCHLD        20,17,18     B      Child stopped or terminated
+  SIGCONT        19,18,25            Continue if stopped
+  SIGSTOP        17,19,23    DEF     Stop process
+  SIGTSTP        18,20,24     D      Stop typed at tty
+  SIGTTIN        21,21,26     D      tty input for background process
+  SIGTTOU        22,22,27     D      tty output for background process
+  SIGBUS         10,7,10      C      Bus error (bad memory access)
+  SIGPOLL                     A      Pollable event (Sys V). Synonym of SIGIO
+  SIGPROF        27,27,29     A      Profiling timer expired
+  SIGSYS         12,-,12      C      Bad argument to routine (SVID)
+  SIGTRAP           5         C      Trace/breakpoint trap
+  SIGURG         16,23,21     B      Urgent condition on socket (4.2 BSD)
+  SIGVTALRM      26,26,28     A      Virtual alarm clock (4.2 BSD)
+  SIGXCPU        24,24,30     C      CPU time limit exceeded (4.2 BSD)
+  SIGXFSZ        25,25,31     C      File size limit exceeded (4.2 BSD)
+  SIGIOT            6         C      IOT trap. A synonym for SIGABRT
+  SIGEMT          7,-,7
+  SIGSTKFLT       -,16,-      A      Stack fault on coprocessor
+  SIGIO          23,29,22     A      I/O now possible (4.2 BSD)
+  SIGCLD          -,-,18             A synonym for SIGCHLD
+  SIGPWR         29,30,19     A      Power failure (System V)
+  SIGINFO         29,-,-             A synonym for SIGPWR
+  SIGLOST         -,-,-       A      File lock lost
+  SIGWINCH       28,28,20     B      Window resize signal (4.3 BSD, Sun)
+  SIGUNUSED       -,31,-      A      Unused signal (will be SIGSYS)
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/SockScan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/SockScan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/SockScan.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "imregister.h"
+# include "imreg.h"
+# include <errno.h>
+# define MAXTIME 10
+
+int SockScan (char *string, Fifo *fifo, int sock) {
+  
+  int i, done, status;
+
+  done = 0;
+  for (i = 0; (i < MAXTIME) && !done; i++) {
+    ShiftFifo (fifo);
+    status = ReadtoFifo (fifo, sock);
+    switch (status) {
+    case 0:
+      break;
+    case -1:
+      usleep (1000);
+      break;
+    default:
+      done = memstr (fifo[0].buffer, string, fifo[0].Nbuffer);
+      break;
+    }
+  }
+  return (status);
+}
+
+/* returns (offset to m2) + 1, or 0 if failure */ 
+int memstr (char *m1, char *m2, int n) {
+
+  int i, N;
+
+  N = strlen (m2);
+  for (i = 0; (i < n - N + 1) && memcmp (m1, m2, N); i++, m1++);
+  if (memcmp (m1, m2, N)) {
+    return (0);
+  }
+  else {
+    return (i+1);
+  }
+
+}
+ 
+/* SockScan reads from the given socket looking for the given string
+   SockScan stores data read from socket in the fifo buffer to ensure 
+   it catches the given string.  a single invocation of SockScan will 
+   wait for up to 10 msec for data to come down the pipe before giving up.
+   SockScan can only search for one string in the output stream.
+   */
+
+
+/* need a way of signalling that the socket has closed...
+   maybe change the sock entry to 0?
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/args.imregister.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/args.imregister.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/args.imregister.c	(revision 22322)
@@ -0,0 +1,109 @@
+# include "imregister.h"
+# include "imreg.h"
+
+int args (int argc, char **argv) {
+
+  int N;
+
+  ConfigInit (&argc, argv); /* load elixir config data */
+  ConfigCamera ();          /* load camera information */
+  ConfigFilter ();          /* load filter information */
+
+  SingleIsSplit = FALSE;
+  if ((N = get_argument (argc, argv, "-split"))) {
+    remove_argument (N, &argc, argv);
+    SingleIsSplit = TRUE;
+  }
+
+  NoReg = FALSE;
+  if ((N = get_argument (argc, argv, "-noreg"))) {
+    remove_argument (N, &argc, argv);
+    NoReg = TRUE;
+  }
+
+  output.verbose = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    output.verbose = TRUE;
+  }
+
+  NeedType = FALSE;
+  if ((N = get_argument (argc, argv, "-needtype"))) {
+    remove_argument (N, &argc, argv);
+    NeedType = TRUE;
+  }
+
+  /* all imregister programs are implicitly modifying the db */
+  output.modify = TRUE;
+  IMSORT = FALSE;
+
+  if (strstr (argv[0], "imregister") != (char *) NULL) {
+    if (argc != 2) {
+      fprintf (stderr, "ERROR: Usage: imregister (filename) [-split] [-noreg]\n");
+      exit (1);
+    }
+    return (TRUE);
+  }
+  if (strstr (argv[0], "showiminfo") != (char *) NULL) {
+    if (argc != 2) {
+      fprintf (stderr, "ERROR: Usage: showiminfo (filename) [-split] [-noreg]\n");
+      exit (1);
+    }
+    return (TRUE);
+  }
+  if (strstr (argv[0], "imsort") != (char *) NULL) {
+    if (argc != 2) {
+      fprintf (stderr, "ERROR: Usage: imsort filename(s) [-split] [-noreg]\n");
+      exit (1);
+    }
+    IMSORT = TRUE;
+    return (TRUE);
+  }
+  if (strstr (argv[0], "imstatreg") != (char *) NULL) {
+
+    char *path, *file;
+
+    CLIENT = TRUE;
+
+    /* set up name to lockfile */
+    path = pathname (ImageDB);
+    file = filebasename (ImageDB);
+    ALLOCATE (PIDFILE, char, strlen (path) + strlen (file) + 10);
+    sprintf (PIDFILE, "%s/.%s.pid", path, file);
+
+    /* create db.log and db.bfr */
+    sprintf (TempDB, "%s.bfr", ImageDB);
+    sprintf (LogFile, "%s.log", ImageDB);
+    
+    /* check for daemon mode */
+    if ((N = get_argument (argc, argv, "-daemon"))) {
+      remove_argument (N, &argc, argv);
+      CLIENT = FALSE;
+
+      /* special daemon options */
+      if (get_argument (argc, argv, "-kill"))   KillProcess (PIDFILE);
+      if (get_argument (argc, argv, "-status")) StatusProcess (PIDFILE);
+
+      LOOP_DELAY = 60;
+      if ((N = get_argument (argc, argv, "-delay"))) {
+	remove_argument (N, &argc, argv);
+	LOOP_DELAY = atof(argv[N]);
+	remove_argument (N, &argc, argv);
+      }	
+
+      if (argc != 1) {
+	fprintf (stderr, "ERROR: Usage: imstatreg -daemon [-delay N] [-status] [-kill]\n");
+	exit (1);
+      }
+      return (TRUE);
+    }
+
+    if (argc != 4) {
+      fprintf (stderr, "ERROR: Usage: imstatreg (fits) (stats) (sdat) [-split] [-noreg]\n");
+      fprintf (stderr, "       or:    imstatreg -daemon\n");
+      exit (1);
+    }
+    return (TRUE);
+  }
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/args.imsearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/args.imsearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/args.imsearch.c	(revision 22322)
@@ -0,0 +1,294 @@
+# include "imregister.h" 
+# include "imreg.h"
+
+/* criteria struct is global */
+int args (int argc, char **argv) {
+
+  double dt;
+  int N, i;
+
+  ConfigInit (&argc, argv); /* load elixir config data */
+  ConfigCamera ();          /* load camera information */
+  ConfigFilter ();          /* load filter information */
+
+  /* set timezone (set static in misc.c) */
+  if ((N = get_argument (argc, argv, "-tz"))) {
+    remove_argument (N, &argc, argv);
+    dt = atof (argv[N]);
+    set_timezone (dt);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* define time range */
+  criteria.Ntimes = 0;
+  if (!get_trange_arguments (&argc, argv, &criteria.tstart, &criteria.tstop, &criteria.Ntimes)) {
+    fprintf (stderr, "ERROR: syntax error\n");
+    exit (1);
+  }
+
+  /* image type (dark, flat, bias, etc) */
+  criteria.TypeSelect = FALSE;
+  criteria.Type = T_UNDEF;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Type = get_image_type (argv[N]);
+    if (criteria.Type == T_UNDEF) {
+      fprintf (stderr, "ERROR: invalid image type %s\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    criteria.TypeSelect = TRUE;
+  }
+ 
+  /* image mode (split, mef, single) */
+  criteria.ModeSelect = FALSE;
+  criteria.Mode = M_UNDEF;
+  if ((N = get_argument (argc, argv, "-mode"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Mode = get_image_mode (argv[N]);
+    if (criteria.Mode == M_UNDEF) {
+      fprintf (stderr, "ERROR: invalid image mode %s\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    criteria.ModeSelect = TRUE;
+  }
+
+  /* exposure time */
+  criteria.ExptimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-etime"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Exptime = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.ExptimeSelect = TRUE;
+  }
+ 
+  /* ccd number */
+  criteria.CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    criteria.CCD = -1;
+    for (i = 0; (i < Nccd) && (criteria.CCD == -1); i++) {
+      if (strnumcmp (ccds[i], argv[N])) {
+	criteria.CCD = i;
+      }
+    }
+    if (criteria.CCD == -1) {
+      fprintf (stderr, "ERROR: ccd %s choice not found in camera config\n", argv[N]);
+      exit (1);
+    }
+
+    remove_argument (N, &argc, argv);
+    criteria.CCDSelect = TRUE;
+  }
+  /* select CCD seq number */
+  if ((N = get_argument (argc, argv, "-ccdn"))) {
+    remove_argument (N, &argc, argv);
+    criteria.CCD = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.CCDSelect = TRUE;
+  }
+ 
+  /* filter name */
+  criteria.FilterSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Filter = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.FilterSelect = TRUE;
+    if (!strcasecmp (criteria.Filter, "X")) {
+      criteria.FilterSelect = FALSE;
+    }
+  }
+
+  /* string in image name */
+  criteria.NameSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.NameSelect = TRUE;
+  }
+
+  /* processed state */
+  criteria.ProcSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-proc"))) {
+    criteria.ProcSelect = TRUE;
+    remove_argument (N, &argc, argv);
+
+    criteria.Proc = -1;
+    if (!strcasecmp (argv[N], "t")) criteria.Proc = TRUE;
+    if (!strcasecmp (argv[N], "f")) criteria.Proc = FALSE;
+    remove_argument (N, &argc, argv);
+    if (criteria.Proc == -1) {
+      fprintf (stderr, "ERROR: -proc (t/f)\n");
+      exit (1);
+    }
+  }
+
+  criteria.DistSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-dist"))) {
+    criteria.DistSelect = TRUE;
+    remove_argument (N, &argc, argv);
+
+    criteria.Dist = -1;
+    if (!strcasecmp (argv[N], "t")) criteria.Dist = TRUE;
+    if (!strcasecmp (argv[N], "f")) criteria.Dist = FALSE;
+    remove_argument (N, &argc, argv);
+    if (criteria.Dist == -1) {
+      fprintf (stderr, "ERROR: -dist (t/f)\n");
+      exit (1);
+    }
+  }
+
+  /*** command-line options which modify the output list */
+  if ((N = get_argument (argc, argv, "-treg"))) {
+    remove_argument (N, &argc, argv);
+    SetOutputMode ("RegTimeMode");
+  }
+  if ((N = get_argument (argc, argv, "-seq"))) {
+    remove_argument (N, &argc, argv);
+    SetOutputMode ("CCDSeq");
+  }
+  if ((N = get_argument (argc, argv, "-pt"))) {
+    remove_argument (N, &argc, argv);
+    SetOutputMode ("PTstyle");
+  }
+  output.table = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-table"))) {
+    remove_argument (N, &argc, argv);
+    output.table = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  output.cadctable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-cadctable"))) {
+    remove_argument (N, &argc, argv);
+    output.cadctable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  output.bintable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-bintable"))) {
+    remove_argument (N, &argc, argv);
+    output.bintable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /*** command-line options which modify behavior (delete, modify, newpath, mef2split split2mef */
+  output.delete = FALSE;
+  output.modify = FALSE;
+  output.modify_path = FALSE; 
+  output.modify_dist = FALSE;
+  output.mef2split = FALSE; 
+  output.split2mef = FALSE;
+  output.unique = FALSE;
+
+  if ((N = get_argument (argc, argv, "-delete"))) {
+    remove_argument (N, &argc, argv);
+    output.delete = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "-unique"))) {
+    remove_argument (N, &argc, argv);
+    output.unique = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "-modify"))) {
+    if (output.delete) { 
+      fprintf (stderr, "can't specify more than one modifier at a time\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    output.modify = TRUE;
+    
+    /* modify part of the path */
+    if (!strcasecmp (argv[N], "path")) {
+      output.modify_path = TRUE;
+      remove_argument (N, &argc, argv);
+      output.oldpath = strcreate (argv[N]);
+      remove_argument (N, &argc, argv);
+      output.newpath = strcreate (argv[N]);
+      remove_argument (N, &argc, argv);
+      goto valid_modify;
+    }
+
+    /* modify the image mode (MEF <-> SPLIT) */
+    if (!strcasecmp (argv[N], "mode")) {
+      remove_argument (N, &argc, argv);
+      if (!strcasecmp (argv[N], "mef")) {
+	output.mef2split = TRUE;
+	remove_argument (N, &argc, argv);
+	goto valid_modify;
+      }
+      if (!strcasecmp (argv[N], "split")) {
+	output.split2mef = TRUE;
+	remove_argument (N, &argc, argv);
+	goto valid_modify;
+      }
+    }
+
+    /* modify distributed status */
+    if (!strcasecmp (argv[N], "dist")) {
+      output.modify_dist = TRUE;
+      remove_argument (N, &argc, argv);
+      if (!strcasecmp (argv[N], "t")) {
+	output.dist = TRUE;
+	remove_argument (N, &argc, argv);
+	goto valid_modify;
+      }
+      if (!strcasecmp (argv[N], "f")) {
+	output.dist = FALSE;
+	remove_argument (N, &argc, argv);
+	goto valid_modify;
+      }
+    }
+
+    /* modify the type */
+    if (!strcasecmp (argv[N], "type")) {
+      output.modify_type = TRUE;
+      remove_argument (N, &argc, argv);
+      output.type = get_image_type (argv[N]);
+      if (output.type == T_UNDEF) {
+	fprintf (stderr, "ERROR: invalid image type %s\n", argv[N]);
+	exit (1);
+      }
+      remove_argument (N, &argc, argv);
+      goto valid_modify;
+    }
+
+    /* modify the filter */
+    if (!strcasecmp (argv[N], "filter")) {
+      output.modify_filter = TRUE;
+      remove_argument (N, &argc, argv);
+      output.filter = strcreate (argv[N]);
+      remove_argument (N, &argc, argv);
+      goto valid_modify;
+    }
+
+    if (!strcasecmp (argv[N], "help")) {
+      fprintf (stderr, "-modify option: \n");
+      fprintf (stderr, "  -modify path (oldpath) (newpath)\n"); 
+      fprintf (stderr, "  -modify mode (mef | split)\n"); 
+      fprintf (stderr, "  -modify dist (t | f)\n"); 
+      fprintf (stderr, "  -modify type (flat, etc)\n"); 
+      fprintf (stderr, "  -modify filter (name)\n\n"); 
+      fprintf (stderr, "  mode mef : convert mef to split\n");
+      fprintf (stderr, "  mode split : convert split to mef\n");
+      exit (2);
+    }
+
+    fprintf (stderr, "invalid -modify option, try -modify help\n");
+    exit (1);
+  }
+ valid_modify:
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: imsearch [config ops] \n");
+    fprintf (stderr, "  [-type type] [-mode mode] [-trange start range] [-ccd N]\n");
+    fprintf (stderr, "  [-etime exptime] [-filter name] [-name string] [-proc t/f]\n");
+    fprintf (stderr, "  [-treg] [-seq] [-pt] [-table] [-cadctable] [-bintable]\n");
+    fprintf (stderr, "  [-delete] [-modify (options)]\n");
+    exit (1);
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/cadc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/cadc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/cadc.c	(revision 22322)
@@ -0,0 +1,303 @@
+# include "imregister.h"
+# include "imreg.h"
+
+static int REFCCD;
+
+void DumpCADCTable (char *filename, RegImage *image, int *match, int Nmatch) {
+  
+  int i, Obsid, Nobsid, Nsubset, ref;
+  char *datestr, *line, hdrname[99];
+  time_t tsecond;
+  Header header, theader;
+  Matrix matrix;
+  FTable table;
+  RegImage *row;
+  int *index, *entry, *obsid;
+  MosaicLayout *layout;
+  int *subset;
+  double left, right, center, outer, top, bottom;
+  double iqx, iqy, iqr, iqf, iqo;
+
+  /* set REFCCD for use in GetREFIQ */
+  REFCCD = MatchCCDName (SeeingREFCCD);
+  if (REFCCD == -1) { 
+    fprintf (stderr, "ERROR: can't get reference ccd\n");
+    exit (1);
+  }
+
+  /* assign relevant mosaic layout structure */
+  layout = (MosaicLayout *) NULL;
+  if (!strcasecmp (Camera, "CFH12K"))  layout = CreateCFH12K ();
+  if (!strcasecmp (Camera, "MegaCam")) layout = CreateMegaCam ();
+  if (!strcasecmp (Camera, "MegaNorth")) layout = CreateMegaCam ();
+  if (layout == (MosaicLayout *) NULL) {
+    fprintf (stderr, "ERROR: invalid camera for CADC Table\n");
+    exit (1);
+  }
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+  
+  /* create table header */
+  gfits_create_table_header (&theader, "TABLE", "CADC_RAW_IMAGES");
+      
+  /* add current date/time to header */
+  ohana_str_to_time ("now", &tsecond);
+  datestr = ohana_sec_to_date (tsecond);
+  gfits_modify (&header,  "DATE", "%s", 1, datestr);
+  gfits_modify (&theader, "DATE", "%s", 1, datestr);
+
+  /* define table layout */
+  gfits_define_table_column (&theader, "A99",   "FILENAME",       "filename in db",                  "");
+  gfits_define_table_column (&theader, "A99",   "HDR_FILENAME",   "image header filename",           "");
+  gfits_define_table_column (&theader, "I10",   "OBSID",          "image ID number",                 "");
+  gfits_define_table_column (&theader, "F5.2",  "OBS_IQ_REFCCD",  "image quality on reference chip", "arcsec");
+  gfits_define_table_column (&theader, "F5.2",  "OBS_IQ_CENTER",  "image quality center region",     "arcsec");
+  gfits_define_table_column (&theader, "F5.2",  "OBS_IQ_R_RATIO", "IQ ratio (outer / center)",       "");
+  gfits_define_table_column (&theader, "F5.2",  "OBS_IQ_X_RATIO", "IQ ratio (left / right)",         "");
+  gfits_define_table_column (&theader, "F5.2",  "OBS_IQ_Y_RATIO", "IQ ratio (top / bottom)",         "");
+  gfits_define_table_column (&theader, "F9.3",  "OBS_BG_VAL",     "background level",                "counts / pixel");
+
+  /* define TNULL, TNVAL values */
+  gfits_modify (&theader, "TNULL1",  "%s", 1, "NULL");  /* FILENAME       */
+  gfits_modify (&theader, "TNULL2",  "%s", 1, "NULL");  /* HDR_FILENAME   */
+  gfits_modify (&theader, "TNULL3",  "%s", 1, "0");     /* OBSID          */
+  gfits_modify (&theader, "TNULL4",  "%s", 1, "0.00");  /* OBS_IQ_REFCCD  */
+  gfits_modify (&theader, "TNULL5",  "%s", 1, "0.00");  /* OBS_IQ_CENTER  */
+  gfits_modify (&theader, "TNULL6",  "%s", 1, "0.00");  /* OBS_IQ_R_RATIO */
+  gfits_modify (&theader, "TNULL7",  "%s", 1, "0.00");  /* OBS_IQ_X_RATIO */
+  gfits_modify (&theader, "TNULL8",  "%s", 1, "0.00");  /* OBS_IQ_Y_RATIO */
+  gfits_modify (&theader, "TNULL9",  "%s", 1, "0.00");  /* OBS_BG_VAL     */
+
+  gfits_modify (&theader, "TNVAL1",  "%s", 1, "NA");    /* FILENAME     */
+  gfits_modify (&theader, "TNVAL2",  "%s", 1, "NA");    /* HDR_FILENAME */
+  gfits_modify (&theader, "TNVAL3",  "%s", 1, "-1");    /* OBSID        */
+  gfits_modify (&theader, "TNVAL4",  "%s", 1, "-1.00"); /* OBS_IQ_REFCCD  */
+  gfits_modify (&theader, "TNVAL5",  "%s", 1, "-1.00"); /* OBS_IQ_CENTER  */
+  gfits_modify (&theader, "TNVAL6",  "%s", 1, "-1.00"); /* OBS_IQ_R_RATIO */
+  gfits_modify (&theader, "TNVAL7",  "%s", 1, "-1.00"); /* OBS_IQ_X_RATIO */
+  gfits_modify (&theader, "TNVAL8",  "%s", 1, "-1.00"); /* OBS_IQ_Y_RATIO */
+  gfits_modify (&theader, "TNVAL9",  "%s", 1, "-1.00"); /* OBS_BG_VAL   */
+
+  /* create table, add data values */
+  gfits_create_table (&theader, &table);
+  
+  /* prepare indicies to handle table data */
+  GetObsIDIndex (image, match, Nmatch, &index, &entry);
+  obsid = GetUniqueObsID (image, index, entry, Nmatch, &Nobsid);
+
+  for (i = 0; i < Nobsid; i++) {
+    ref = GetREFCCD (image, index, entry, Nmatch, obsid[i]);
+    row = &image[ref];
+    Obsid = index[obsid[i]];
+    sprintf (hdrname, "%s.hdr", row[0].filename);
+
+    subset = GetObsIDSubset (image, obsid[i], index, entry, Nmatch, &Nsubset);
+    center = MosaicIQStats (image, subset, Nsubset, &layout[0].center);
+    outer  = MosaicIQStats (image, subset, Nsubset, &layout[0].outer );
+    top    = MosaicIQStats (image, subset, Nsubset, &layout[0].top   );
+    bottom = MosaicIQStats (image, subset, Nsubset, &layout[0].bottom);
+    left   = MosaicIQStats (image, subset, Nsubset, &layout[0].left  );
+    right  = MosaicIQStats (image, subset, Nsubset, &layout[0].right );
+    free (subset);
+
+    iqx = (left   == 0) ? 0 : (right / left);
+    iqy = (bottom == 0) ? 0 : (top / bottom);
+    iqr = (center == 0) ? 0 : (outer / center);
+    iqf = center * ARCSEC_PIXEL;
+    iqo = row[0].fwhm*ARCSEC_PIXEL;
+    line = gfits_table_print (&table, row[0].filename, hdrname, Obsid, iqo, iqf, iqr, iqx, iqy, row[0].sky);
+
+    gfits_add_rows (&table, line, 1, strlen(line));
+    free (line);
+  }
+  free (obsid);
+  free (index);
+  free (entry);
+
+  gfits_write_header  (filename, &header);
+  gfits_write_matrix  (filename, &matrix);
+  gfits_write_Theader (filename, &theader);
+  gfits_write_table   (filename, &table);
+  exit (0);
+}
+
+int *GetObsIDSubset (RegImage *image, int start, int *index, int *entry, int Nindex, int *Nsubset) {
+
+  int i, N, NSUBSET;
+  int *subset;
+
+  /* create output index */
+  N = 0;
+  NSUBSET = 64;
+  ALLOCATE (subset, int, NSUBSET);
+
+  /* find unique sequences */
+  for (i = start; (i < Nindex) && (index[i] == index[start]); i++) {
+    subset[N] = entry[i];
+    N++;
+    if (N == NSUBSET) {
+      NSUBSET += 64;
+      REALLOCATE (subset, int, NSUBSET);
+    }
+  }
+
+  *Nsubset = N;
+  return (subset);
+}
+
+/* return an index list of unique obs id entries at start of sequence */
+int *GetUniqueObsID (RegImage *image, int *index, int *entry, int Nindex, int *Nmatch) {
+  
+  int i, j, N, NMATCH;
+  int *match;
+
+  /* create output index */
+  N = 0;
+  NMATCH = 1000;
+  ALLOCATE (match, int, NMATCH);
+
+  /* find unique sequences */
+  for (i = 0; i < Nindex; ) {
+    for (j = i + 1; (j < Nindex) && (index[i] == index[j]); j++);
+
+    /* add unique entry to output list */
+    match[N] = i;
+    N ++;
+    if (N == NMATCH) {
+      NMATCH += 1000;
+      REALLOCATE (match, int, NMATCH);
+    }
+
+    /* j always points to the next entry */
+    i = j;
+  }
+  *Nmatch = N;
+  return (match);
+}
+
+/* return seeing for ccd == REFCCD */
+int GetREFCCD (RegImage *image, int *index, int *entry, int Nindex, int start) {
+  
+  int i, N, NMATCH;
+  int *match;
+
+  /* create output index */
+  N = 0;
+  NMATCH = 1000;
+  ALLOCATE (match, int, NMATCH);
+
+  /* find unique sequences */
+  for (i = start; (i < Nindex) && (index[i] == index[start]); i++) {
+    if (image[entry[i]].ccd == REFCCD) return (entry[i]);
+  }
+  return (start);
+}
+
+void GetObsIDIndex (RegImage *image, int *match, int Nmatch, int **Index, int **Entry) {
+
+  int i;
+  int *index, *entry;
+
+  /* index = OBSID */
+  ALLOCATE (index, int, Nmatch);
+  ALLOCATE (entry, int, Nmatch);
+  for (i = 0; i < Nmatch; i++) {
+    index[i] = atoi (image[match[i]].filename);
+    if (index[i] < 400000) fprintf (stderr, "warning: derived obsid < 400000\n");
+    entry[i] = match[i];
+  }
+  isortpair (index, entry, Nmatch);
+  *Index = index;
+  *Entry = entry;
+}
+
+/* match is a list of image entries with the same obsid */
+double MosaicIQStats (RegImage *image, int *match, int Nmatch, MosaicRegion *region) {
+
+  int i, j, N, Nccd;
+  double *list, value;
+
+  Nccd = region[0].Nccd;
+  ALLOCATE (list, double, Nccd);
+
+  N = 0;
+  for (i = 0; i < Nccd; i++) {
+    for (j = 0; j < Nmatch; j++) {
+      if (image[match[j]].ccd == region[0].ccd[i]) {
+	list[N] = image[match[j]].fwhm;
+	N++;
+	break;
+      }
+    }
+  }
+
+  value = SigmaClipList (list, N);
+  free (list);
+  return (value);
+}
+
+double SigmaClipList (double *list, int N) {
+
+  int i, n;
+  double median, sigma3, m1, m2;
+
+  if (N == 0) return (0.0);
+  if (N == 1) return (list[0]);
+  
+  dsort (list, N);
+  median = list[(int)(0.5*N)];
+  
+  m1 = m2 = 0;
+  for (i = 0; i < N; i++) { m1 += list[i]; m2 += SQ(list[i]); }
+  sigma3 = 3*sqrt (m2/N - m1*m1/N/N);
+  
+  m1 = n = 0;
+  for (i = 0; i < N; i++) { 
+    if (abs(list[i] - median) > sigma3) continue;
+    m1 += list[i];
+    n ++;
+  }
+
+  m2 = m1 / n;
+  return (m2);
+}
+  
+
+/* the CADC table is special: we need to report specfic IQ stats 
+   which represent the variations in focus across the mosaic. 
+   this representation is explicitly dependent on the mosaic,
+   and does not make sense for other camera types 
+   CADC table option cannot be combined with CCD filtering options
+   CADC table forces -unique, needed to make calculation
+*/
+
+/* derived CADC parameters:
+   OBS_IQ_CENTER - sigma-clip mean value of center region
+   OBS_IQ_X      - ratio of left to right regions
+   OBS_IQ_Y      - ratio of top to bottom regions
+   OBS_IQ_R      - ratio of center to edge regions
+*/
+
+/* cfh12k:
+
+   00 01 02 03 04 05
+   06 07 08 09 10 11
+
+   center: 02,03,08,09
+   outer:  00,01,04,05,06,07,10,11
+   top;    00-05
+   bottom: 06-11
+   right:  00,01,02,06,07,08
+   left:   03,04,05,09,10,11
+
+   for each region, calculate median, sigma, reject >3sigma, calculate mean
+*/
+
+/* we are guaranteed a unique set of filename / ccd values */
+/* index = OBSID = atoi (filename) */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/delete.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/delete.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/delete.c	(revision 22322)
@@ -0,0 +1,51 @@
+# include "imregister.h"
+# include "imreg.h"
+
+void DeleteSubset (FITS_DB *db, RegImage *image, int Nimage, int *match, int Nmatch) {
+
+  int i, j;
+  int *keep, Ndel, Nsubset;
+  RegImage *subset;
+
+  ALLOCATE (keep, int, MAX (Nimage, 1));
+  for (i = 0; i < Nimage; i++) keep[i] = TRUE;
+  fprintf (stderr, "total of %d images\n", Nimage);
+
+  Ndel = 0;
+  for (i = 0; i < Nmatch; i++) {
+    j = match[i];
+    if (j == -1) continue;
+    keep[j] = FALSE;
+    Ndel ++;
+  }
+  fprintf (stderr, "delete %d images\n", Ndel);
+  if (Ndel == 0) { 
+    fprintf (stderr, "SUCCESS\n");
+    gfits_db_close (db);
+    exit (0);
+  }
+
+  /* create new data list */
+  Nsubset = Nimage - Ndel;
+  ALLOCATE (subset, RegImage, MAX (1, Nsubset));
+  fprintf (stderr, "keeping %d images\n", Nsubset);
+  for (j = i = 0; i < Nimage; i++) {
+    if (!keep[i]) continue;
+    subset[j] = image[i];
+    j++;
+  }
+
+  free (keep);
+  free (image);
+
+  /** we may later want to pull this out and put it elsewhere **/
+  /** free db[0].theader, db[0].table.buffer? **/
+  gfits_table_set_RegImage (&db[0].ftable, subset, Nsubset);
+  gfits_db_save (db);
+  gfits_db_close (db);
+  gfits_db_free (db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/iminfo.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/iminfo.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/iminfo.c	(revision 22322)
@@ -0,0 +1,165 @@
+# include "imregister.h"
+# include "imreg.h"
+
+char *getcwd_cfht ();
+
+RegImage *iminfo (char *filename) {
+
+  RegImage *image;
+  Header header;
+  int extend;
+  int Naxes, Nextend, Nseq;
+  char Imagetype[80], line[80];
+  struct timeval now;
+  char *name, *cwd, *tempname;
+  double tmp;
+
+  ALLOCATE (image, RegImage, 1);
+  bzero (image, sizeof (RegImage));
+
+  /* load in FITS header from image */
+  if (!gfits_read_header (filename, &header)) {
+    fprintf (stderr, "ERROR: can't find image file %s\n", filename);
+    exit (1);
+  }
+
+  /* find the important header keyword values */
+  image[0].obstime = parse_time (&header);
+
+  gettimeofday (&now, (void *) NULL);
+  image[0].regtime = now.tv_sec;
+
+  image[0].sky = image[0].fwhm = image[0].bias = 0;
+  
+  /* default for a single CCD frame */
+  Naxes = 2; 
+  extend = FALSE;
+  image[0].mode     = M_SINGLE;
+  image[0].flag     = 0;
+  image[0].ccd      = 0;
+  image[0].seq      = 0;
+  image[0].seqtime  = 0;
+
+  /* determine data layout (SINGLE, SPLIT, MEF, CUBE, SLICE) */
+  gfits_scan (&header, "EXTEND",  "%t", 1, &extend);
+  gfits_scan (&header, "NAXIS",  "%d", 1, &Naxes);
+  if (extend) { /* MEF file */
+    gfits_scan (&header, "NEXTEND",  "%d", 1, &Nextend);
+    image[0].mode = M_MEF;
+    image[0].ccd  = Nextend;
+  } 
+  /* need to distinguish MEF, CUBE, and MEF-CUBE */
+  if (Naxes == 3) { /* data cube */
+    gfits_scan (&header, "NAXIS3",  "%d", 1, &Nseq);
+    if (image[0].mode == M_MEF) {
+      fprintf (stderr, "MEF-CUBE not ready\n");
+      exit (1);
+    }
+    image[0].mode = M_CUBE;
+    image[0].seq  = Nseq;
+    /* abstract this name somewhere ? */
+    gfits_scan (&header, "SEQTIME", "%f", 1, &image[0].seqtime);
+  }
+  if (SingleIsSplit && (image[0].mode == M_SINGLE)) {
+    image[0].mode = M_SPLIT;
+    image[0].ccd  = MatchCCDNameHeader (&header);
+  }
+  /* is there a better way to id a 'split' image? */
+
+  /* extract other relevant data from header */
+  gfits_scan (&header, ImagetypeKeyword,  "%s", 1, &Imagetype);
+
+  /* grab the image type : if not defined, set to 'none' */
+  image[0].type = get_image_type (Imagetype);
+  if (NeedType && (image[0].type == T_UNDEF)) {
+    fprintf (stderr, "ERROR: skipping unknown image type\n");
+    exit (1);
+  } else {
+    image[0].type = T_NONE;
+  }
+
+  /* grab interesting info from header */
+  gfits_scan (&header, ExptimeKeyword,    "%f", 1, &image[0].exptime);
+  gfits_scan (&header, AirmassKeyword,    "%f", 1, &image[0].airmass);
+  gfits_scan (&header, FocusKeyword,      "%f", 1, &image[0].telfocus);
+  gfits_scan (&header, Teldata1Keyword,   "%f", 1, &image[0].xprobe);
+  gfits_scan (&header, Teldata2Keyword,   "%f", 1, &image[0].yprobe);
+  gfits_scan (&header, Teldata3Keyword,   "%f", 1, &image[0].zprobe);
+  gfits_scan (&header, DettempKeyword,    "%f", 1, &image[0].dettemp);
+  gfits_scan (&header, RotationKeyword,   "%f", 1, &image[0].rotangle);
+
+  /* force strings to fit in available space (32 bytes) */
+  gfits_scan (&header, CameraKeyword,     "%s", 1, line);
+  strncpy (image[0].instrument, line, 31);
+  image[0].instrument[31] = 0;
+
+  gfits_scan (&header, FilterKeyword, "%s", 1, line);
+  MatchFilterList (line);
+  strncpy (image[0].filter, line, 31);
+  image[0].filter[31] = 0;
+
+  /* header has RA & DEC in decimal degrees */
+  if (RADecDegKeyword[0] & DECDecDegKeyword[0]) {
+    gfits_scan (&header, RADecDegKeyword,  "%f", 1, &image[0].ra);
+    gfits_scan (&header, DECDecDegKeyword, "%f", 1, &image[0].dec);
+  } else {
+    if (RASexigKeyword[0] & DECSexigKeyword[0]) {
+      gfits_scan (&header, RASexigKeyword,  "%s", 1, line);
+      ohana_dms_to_ddd (&tmp, line);
+      image[0].ra = 15*tmp;
+      gfits_scan (&header, DECSexigKeyword, "%s", 1, &line);
+      ohana_dms_to_ddd (&tmp, line);
+      image[0].dec = tmp;
+    }
+  }
+
+  /* this segment is strongly CFHT dependent.  also it is somewhat
+     poor: the file must have the probes in the right order.
+     load_probes checks the data validity, but has no gurantee that
+     the right range has been loaded */
+
+  /* to change the selected probes, change:
+     1) the probes[] entries below
+     2) the entries in the program 'gettemps' */
+
+  /* the probes have to agree with the entries in the program 'gettemps' */
+  {
+    double pvalues[4];
+    static int probes[] = {2, 36, 43, 45};
+    if (load_probes (TempLogFile, image[0].obstime, probes, pvalues, 4)) {
+	image[0].teltemp_0 = pvalues[0];
+	image[0].teltemp_1 = pvalues[1];
+	image[0].teltemp_2 = pvalues[2];
+	image[0].teltemp_3 = pvalues[3];
+    } else {
+      fprintf (stderr, "failure to get probe data\n");
+      image[0].teltemp_0 = 200.0;
+      image[0].teltemp_1 = 200.0;
+      image[0].teltemp_2 = 200.0;
+      image[0].teltemp_3 = 200.0;
+    }
+  }
+  /* this should be a call to an external function... */
+  
+  /* extract the file name and the path */
+  name = filebasename (filename);
+  strcpy (image[0].filename, name);
+  name = pathname (filename);
+  if (name[0] != '/') {
+    cwd = getcwd_cfht (NULL, 1024);
+    ALLOCATE (tempname, char, strlen (cwd) + strlen (name) + 2);
+    if (!strcmp (name, ".")) {
+      sprintf (tempname, "%s", cwd);
+    } else {
+      sprintf (tempname, "%s/%s", cwd, name);
+    }      
+    free (name);
+    free (cwd);
+    name = tempname;
+  }    
+  strcpy (image[0].pathname, name);
+
+  return (image);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/imregclient.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/imregclient.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/imregclient.c	(revision 22322)
@@ -0,0 +1,93 @@
+# include "imregister.h"
+# include "imreg.h"
+
+int imregclient (char *fitsfile, char *statfile, char *datfile) {
+
+  int i, Nentry, Nslice, status, tmpint;
+  FILE *f;
+  RegImage *image;
+  FITS_DB db;
+
+  Nentry = 0;
+  image = iminfo (fitsfile);
+  
+  /* if images is MEF or SPLIT/SINGLE, load stats file */
+  /* get stats file (has sky, bias, etc) */
+  switch (image[0].mode) {
+  case M_MEF:
+  case M_SPLIT:
+    f = fopen (statfile, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file.stats\n");
+      exit (1);
+    }
+    fscanf (f, "%f %f", &image[0].sky, &image[0].bias);
+    fclose (f);
+    image[0].fwhm = get_fwhm (datfile);
+    Nentry = 1;
+    break;
+  case M_SINGLE:
+    f = fopen (statfile, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file.stats\n");
+      exit (1);
+    }
+    status = fscanf (f, "%d %f %f", &tmpint, &image[0].sky, &image[0].fwhm);
+    image[0].ccd = tmpint;
+    fclose (f);
+    Nentry = 1;
+    break;
+  case M_CUBE:
+    f = fopen (statfile, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file.stats\n");
+      exit (1);
+    }
+    status = 3;
+    Nslice = image[0].seq;
+    Nentry = Nslice + 1;
+    REALLOCATE (image, RegImage, Nentry);
+    for (i = 0; i < Nentry; i++) {
+      image[i] = image[0];
+      status = fscanf (f, "%d %f %f", &tmpint, &image[i].sky, &image[i].fwhm);
+      image[i].seq = tmpint;
+      if (image[i].seq == Nslice) continue;
+      image[i].obstime += image[0].seqtime*image[i].seq;
+    }
+    fclose (f);
+    Nentry = i;
+    break;
+  }
+  if (NoReg) dump_data (image, Nentry);
+
+  db.lockstate = LCK_HARD;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  if (!gfits_db_lock (&db, TempDB)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  if (db.dbstate == LCK_EMPTY) {
+    gfits_db_create (&db);
+    gfits_table_set_RegImage (&db.ftable, NULL, 0);
+  } else {  
+    if (!gfits_db_load (&db)) {
+      fprintf (stderr, "ERROR: failure to load db\n");
+      gfits_db_close (&db);
+      exit (1);
+    }
+  }
+
+  gfits_convert_RegImage (image, sizeof (RegImage), Nentry);
+  gfits_table_to_vtable (&db.ftable, &db.vtable, 0, 0);
+  gfits_vadd_rows (&db.vtable, (char *) image, Nentry, sizeof(RegImage));
+
+  gfits_db_update (&db);
+  gfits_db_close (&db);
+  gfits_db_free (&db);
+
+  fprintf (stderr, "SUCCESS: wrote temp image data\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/match.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/match.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/match.c	(revision 22322)
@@ -0,0 +1,78 @@
+# include "imregister.h"
+# include "imreg.h"
+
+int *match_criteria (RegImage *image, int Nimage, int *Nmatch) {
+
+  int i, j, Nname;
+  int N, NMATCH;
+  int *match;
+  int reject;
+
+  /* create selection index */
+  N = 0;
+  NMATCH = 1000;
+  ALLOCATE (match, int, NMATCH);
+
+  Nname = 0;
+  if (criteria.NameSelect) Nname = strlen (criteria.Name);
+  
+  /* find entries that matches criteria */
+  for (i = 0; i < Nimage; i++) {
+    for (j = 0, reject = TRUE; reject && (j < criteria.Ntimes); j++) {
+      reject = (image[i].obstime + image[i].exptime < criteria.tstart[j]) || (image[i].obstime > criteria.tstop[j]);
+    }
+    if (criteria.Ntimes && reject) continue;
+    if (criteria.FilterSelect  && (strcasecmp (image[i].filter, criteria.Filter))) continue;
+    if (criteria.ModeSelect    && (image[i].mode != criteria.Mode)) continue;
+    if (criteria.CCDSelect     && (image[i].ccd != criteria.CCD)) continue;
+    if (criteria.TypeSelect    && (image[i].type != criteria.Type)) continue;
+    if (criteria.ExptimeSelect && (fabs (image[i].exptime - criteria.Exptime) > 5.0)) continue;
+    if (criteria.NameSelect    && (strncasecmp (image[i].filename, criteria.Name, Nname))) continue;
+    if (criteria.ProcSelect    && (criteria.Proc ^ (image[i].bias != 0.0))) continue;
+    if (criteria.DistSelect    && (criteria.Dist ^ (image[i].flag && IMREG_DIST))) continue;
+
+    match[N] = i;
+    N ++;
+    if (N == NMATCH) {
+      NMATCH += 1000;
+      REALLOCATE (match, int, NMATCH);
+    }
+  }
+  *Nmatch = N;
+  return (match);
+}
+
+int *match_images (RegImage *image, int Nimage, RegImage *subset, int Nsubset, int *Nmatch) {
+  
+  int i, j, N, Nfound;
+  int *match;
+
+  /* find matching images - very inefficient : sort by obstime, find those first? */
+  ALLOCATE (match, int, Nsubset);
+  for (j = 0; j < Nsubset; j++) {
+    match[j] = -1;
+    for (i = 0; (match[j] == -1) && (i < Nimage); i++) {
+      if (image[i].obstime > subset[j].obstime + 1) continue;
+      if (image[i].obstime < subset[j].obstime - 1) continue;
+      if (image[i].ccd != subset[j].ccd) continue;
+      match[j] = i;
+    }
+  }
+  
+  Nfound = 0;
+  for (i = 0; i < Nsubset; i++) {
+    /* set the new values for this image */
+    N = match[i];
+    if (N == -1) continue;
+    image[N].fwhm = subset[i].fwhm; 
+    image[N].bias = subset[i].bias; 
+    image[N].sky  = subset[i].sky; 
+    image[N].ra   = subset[i].ra; 
+    image[N].dec  = subset[i].dec; 
+    /* if the image is MEF, these were not correctly assigned by imsort.
+       this step uses the values from the split ccd image */
+    Nfound ++;
+  }
+  *Nmatch = Nfound;
+  return (match);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/modify.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/modify.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/modify.c	(revision 22322)
@@ -0,0 +1,98 @@
+# include "imregister.h"
+# include "imreg.h"
+
+void ModifySubset (FITS_DB *db, RegImage *image, int Nimage, int *match, int Nmatch) {
+
+  int i, j, Nold;
+  char *tmppath;
+  char *ext, *root, *path, dist;
+
+  Nold = 0;
+  tmppath = NULL;
+
+  /* create some necessary variables */
+  if (output.modify_path) { 
+    Nold = strlen (output.oldpath);
+    ALLOCATE (tmppath, char, 128);
+  }
+  if (output.modify_dist) {
+    dist = (output.dist) ? 0xff : ~IMREG_DIST;
+  }
+
+  /* modify the selected entries */
+  for (j = 0; j < Nmatch; j++) {
+
+    i = match[j];
+
+    if (output.modify_path) {
+      if (!strncmp (image[i].pathname, output.oldpath, Nold)) {
+	strcpy (tmppath, &image[i].pathname[Nold]);
+	snprintf (image[i].pathname, 128, "%s%s", output.newpath, tmppath);
+      }
+    }
+
+    if (output.mef2split) {
+      if (image[i].mode == M_MEF) {
+	root = filerootname (image[i].filename);
+	ext = fileextname (image[i].filename);
+	path = strcreate (image[i].pathname);
+
+	snprintf (image[i].pathname, 128, "%s/%s", path, root);
+	snprintf (image[i].filename, 64,  "%s%02d.%s", root, image[i].ccd, ext);
+	image[i].mode = M_SPLIT;
+	free (root);
+	free (ext);
+	free (path);
+      }
+    }
+
+    if (output.split2mef) {
+      if (image[i].mode == M_SPLIT) {
+	ext  = fileextname (image[i].filename);
+	root = filebasename (image[i].pathname);
+	path = pathname (image[i].pathname);
+
+	snprintf (image[i].pathname, 128, "%s", path);
+	snprintf (image[i].filename, 64,  "%s.%s", root, ext);
+	image[i].mode = M_MEF;
+	free (root);
+	free (ext);
+	free (path);
+      }
+    }
+
+    if (output.modify_dist) {
+      if (output.dist)  image[i].flag |=  IMREG_DIST;
+      if (!output.dist) image[i].flag &= ~IMREG_DIST;
+    }
+
+    if (output.modify_filter) {
+      strncpy (image[i].filter, output.filter, 31);
+    }
+
+    if (output.modify_type) {
+      image[i].type = output.type;
+    }
+
+  }
+
+  /** we may later want to pull this out and put it elsewhere **/
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, match, Nmatch);
+  for (i = 0; i < Nmatch; i++) {
+    gfits_convert_RegImage ((RegImage *) db[0].vtable.buffer[i], sizeof (RegImage), 1);
+  }
+  gfits_db_update (db);
+  gfits_db_close (db);
+  gfits_db_free (db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+
+}
+
+
+/* 
+   MEF                      SPLIT
+   /path/filename.fits <--> /path/filename/filenameNN.fits
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/mosaics.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/mosaics.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/mosaics.c	(revision 22322)
@@ -0,0 +1,163 @@
+# include "imregister.h"
+# include "imreg.h"
+
+MosaicLayout *CreateCFH12K () {
+
+  MosaicLayout *layout;
+
+  ALLOCATE (layout, MosaicLayout, 1);
+
+  layout[0].center.Nccd = 4;
+  ALLOCATE (layout[0].center.ccd, int, layout[0].center.Nccd);
+  layout[0].center.ccd[0] = 2;
+  layout[0].center.ccd[1] = 3;
+  layout[0].center.ccd[2] = 8;
+  layout[0].center.ccd[3] = 9;
+
+  layout[0].outer.Nccd = 8;
+  ALLOCATE (layout[0].outer.ccd, int, layout[0].outer.Nccd);
+  layout[0].outer.ccd[0] = 0;
+  layout[0].outer.ccd[1] = 1;
+  layout[0].outer.ccd[2] = 4;
+  layout[0].outer.ccd[3] = 5;
+  layout[0].outer.ccd[4] = 6;
+  layout[0].outer.ccd[5] = 7;
+  layout[0].outer.ccd[6] = 10;
+  layout[0].outer.ccd[7] = 11;
+
+  layout[0].top.Nccd = 6;
+  ALLOCATE (layout[0].top.ccd, int, layout[0].top.Nccd);
+  layout[0].top.ccd[0] = 0;
+  layout[0].top.ccd[1] = 1;
+  layout[0].top.ccd[2] = 2;
+  layout[0].top.ccd[3] = 3;
+  layout[0].top.ccd[4] = 4;
+  layout[0].top.ccd[5] = 5;
+
+  layout[0].bottom.Nccd = 6;
+  ALLOCATE (layout[0].bottom.ccd, int, layout[0].bottom.Nccd);
+  layout[0].bottom.ccd[0] = 6;
+  layout[0].bottom.ccd[1] = 7;
+  layout[0].bottom.ccd[2] = 8;
+  layout[0].bottom.ccd[3] = 9;
+  layout[0].bottom.ccd[4] = 10;
+  layout[0].bottom.ccd[5] = 11;
+
+  layout[0].left.Nccd = 6;
+  ALLOCATE (layout[0].left.ccd, int, layout[0].left.Nccd);
+  layout[0].left.ccd[0] = 0;
+  layout[0].left.ccd[1] = 1;
+  layout[0].left.ccd[2] = 2;
+  layout[0].left.ccd[3] = 6;
+  layout[0].left.ccd[4] = 7;
+  layout[0].left.ccd[5] = 8;
+
+  layout[0].right.Nccd = 6;
+  ALLOCATE (layout[0].right.ccd, int, layout[0].right.Nccd);
+  layout[0].right.ccd[0] = 3;
+  layout[0].right.ccd[1] = 4;
+  layout[0].right.ccd[2] = 5;
+  layout[0].right.ccd[3] = 9;
+  layout[0].right.ccd[4] = 10;
+  layout[0].right.ccd[5] = 11;
+
+  return (layout);
+}
+
+MosaicLayout *CreateMegaCam () {
+
+  MosaicLayout *layout;
+
+  ALLOCATE (layout, MosaicLayout, 1);
+
+  layout[0].center.Nccd = 10;
+  ALLOCATE (layout[0].center.ccd, int, layout[0].center.Nccd);
+  layout[0].center.ccd[0] = 11;
+  layout[0].center.ccd[1] = 12;
+  layout[0].center.ccd[2] = 13;
+  layout[0].center.ccd[3] = 14;
+  layout[0].center.ccd[4] = 15;
+  layout[0].center.ccd[5] = 20;
+  layout[0].center.ccd[6] = 21;
+  layout[0].center.ccd[7] = 22;
+  layout[0].center.ccd[8] = 23;
+  layout[0].center.ccd[9] = 24;
+
+  layout[0].outer.Nccd = 26;
+  ALLOCATE (layout[0].outer.ccd, int, layout[0].outer.Nccd);
+  layout[0].outer.ccd[0] = 0;
+  layout[0].outer.ccd[1] = 1;
+  layout[0].outer.ccd[2] = 2;
+  layout[0].outer.ccd[3] = 3;
+  layout[0].outer.ccd[4] = 4;
+  layout[0].outer.ccd[5] = 5;
+  layout[0].outer.ccd[6] = 6;
+  layout[0].outer.ccd[7] = 7;
+  layout[0].outer.ccd[8] = 8;
+  layout[0].outer.ccd[9] = 9;
+  layout[0].outer.ccd[10] = 10;
+  layout[0].outer.ccd[11] = 16;
+  layout[0].outer.ccd[12] = 17;
+  layout[0].outer.ccd[13] = 18;
+  layout[0].outer.ccd[14] = 19;
+  layout[0].outer.ccd[15] = 25;
+  layout[0].outer.ccd[16] = 26;
+  layout[0].outer.ccd[17] = 27;
+  layout[0].outer.ccd[18] = 28;
+  layout[0].outer.ccd[19] = 29;
+  layout[0].outer.ccd[20] = 30;
+  layout[0].outer.ccd[21] = 31;
+  layout[0].outer.ccd[22] = 32;
+  layout[0].outer.ccd[23] = 33;
+  layout[0].outer.ccd[24] = 34;
+  layout[0].outer.ccd[25] = 35;
+
+  layout[0].top.Nccd = 9;
+  ALLOCATE (layout[0].top.ccd, int, layout[0].top.Nccd);
+  layout[0].top.ccd[0] = 0;
+  layout[0].top.ccd[1] = 1;
+  layout[0].top.ccd[2] = 2;
+  layout[0].top.ccd[3] = 3;
+  layout[0].top.ccd[4] = 4;
+  layout[0].top.ccd[5] = 5;
+  layout[0].top.ccd[6] = 6;
+  layout[0].top.ccd[7] = 7;
+  layout[0].top.ccd[8] = 8;
+
+  layout[0].bottom.Nccd = 9;
+  ALLOCATE (layout[0].bottom.ccd, int, layout[0].bottom.Nccd);
+  layout[0].bottom.ccd[0] = 27;
+  layout[0].bottom.ccd[1] = 28;
+  layout[0].bottom.ccd[2] = 29;
+  layout[0].bottom.ccd[3] = 30;
+  layout[0].bottom.ccd[4] = 31;
+  layout[0].bottom.ccd[5] = 32;
+  layout[0].bottom.ccd[6] = 33;
+  layout[0].bottom.ccd[7] = 34;
+  layout[0].bottom.ccd[8] = 35;
+
+  layout[0].left.Nccd = 8;
+  ALLOCATE (layout[0].left.ccd, int, layout[0].left.Nccd);
+  layout[0].left.ccd[0] = 0;
+  layout[0].left.ccd[1] = 1;
+  layout[0].left.ccd[2] = 9;
+  layout[0].left.ccd[3] = 10;
+  layout[0].left.ccd[4] = 18;
+  layout[0].left.ccd[5] = 19;
+  layout[0].left.ccd[6] = 27;
+  layout[0].left.ccd[7] = 28;
+
+  layout[0].right.Nccd = 8;
+  ALLOCATE (layout[0].right.ccd, int, layout[0].right.Nccd);
+  layout[0].right.ccd[0] = 7;
+  layout[0].right.ccd[1] = 8;
+  layout[0].right.ccd[2] = 16;
+  layout[0].right.ccd[3] = 17;
+  layout[0].right.ccd[4] = 25;
+  layout[0].right.ccd[5] = 26;
+  layout[0].right.ccd[6] = 34;
+  layout[0].right.ccd[7] = 35;
+
+  return (layout);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/newimages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/newimages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/newimages.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "imregister.h"
+# include "imreg.h"
+
+RegImage *newimages (RegImage *image, int *Nimage) {
+
+  int i, Nnew;
+  RegImage *regimage;
+
+  Nnew = 0;
+  regimage = NULL;
+
+  /* identify new entries based on mode */
+  switch (image[0].mode) {
+  case M_CUBE:
+    Nnew = image[0].seq + 1;
+    ALLOCATE (regimage, RegImage, Nnew);
+    regimage[0] = image[0];
+    for (i = 1; i < Nnew; i++) {
+      regimage[i] = image[0]; 
+      regimage[i].seq = i - 1;
+      regimage[i].mode = M_SLICE;
+      regimage[i].obstime += image[0].seqtime*regimage[i].seq;
+    }
+    break;
+  case M_MEF:
+    Nnew = image[0].ccd;
+    ALLOCATE (regimage, RegImage, Nnew);
+    for (i = 0; i < Nnew; i++) {
+      regimage[i] = image[0]; 
+      regimage[i].ccd = i;
+    }
+    break;
+  case M_SPLIT:
+  case M_SINGLE:
+    Nnew = 1;
+    regimage = &image[0];
+    break;
+  }
+  *Nimage = Nnew;
+  return (regimage);
+}
+
+
+/* meaning of image fields for different data modes:
+
+   mode      ccd                    seq
+   SINGLE    0                      0
+   SPLIT     ccd seq number         0
+   MEF       Nccd                   0
+   CUBE      ccd seq number         Nseq
+   SLICE     ccd seq number         seq number
+   CUBE-MEF  Nccd                   Nseq
+   SLICE-MEF Nccd                   seq number
+
+*/
+
+/*
+ we need to consider modifying this to separately ID MEF/SPLIT/SINGLE and CUBE/SLICE 
+ for the moment, we are keeping this the same as the old style
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/output.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/output.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/output.c	(revision 22322)
@@ -0,0 +1,251 @@
+# include "imregister.h"
+# include "imreg.h"
+
+static int RegTimeMode = FALSE;
+static int CCDSeq = FALSE;
+static int PTstyle = FALSE;
+
+void SetOutputMode (char *mode) {
+
+  if (!strcmp (mode, "RegTimeMode")) {
+    RegTimeMode = TRUE;
+    return;
+  }
+  if (!strcmp (mode, "CCDSeq")) {
+    CCDSeq = TRUE;
+    return;
+  }
+  if (!strcmp (mode, "PTstyle")) {
+    PTstyle = TRUE;
+    return;
+  }
+
+}
+
+/* given a subset list, write out the selected images, if desired */
+void OutputSubset (RegImage *image, int Nimage, int *match, int Nmatch) {
+
+  if (output.table != (char *) NULL) {
+    DumpFitsTable (output.table, image, match, Nmatch);
+  } 
+
+  if (output.bintable != (char *) NULL) {
+    DumpFitsBintable (output.bintable, image, match, Nmatch);
+  } 
+
+  if (output.cadctable != (char *) NULL) {
+    DumpCADCTable (output.cadctable, image, match, Nmatch);
+  } 
+
+  PrintSubset (image, match, Nmatch);
+  if (output.verbose) fprintf (stderr, "SUCCESS\n");
+
+  exit (0);
+}
+
+/* write out complete binary FITS table in format of db */
+void DumpFitsBintable (char *filename, RegImage *image, int *match, int Nmatch) {
+
+  int i, j;
+  FILE *f;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  RegImage *subset;
+
+  /* extract subset list to single array */
+  ALLOCATE (subset, RegImage, MAX (1, Nmatch));
+  for (i = 0; i < Nmatch; i++){
+    j = match[i];
+    memcpy (&subset[i], &image[j], sizeof (RegImage));
+  }
+
+  /* open file for output */
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open output file %s\n", filename);
+    exit (1);
+  }
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+
+  ftable.header = &theader;
+  gfits_table_set_RegImage (&ftable, subset, Nmatch);
+
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &ftable);
+  fclose (f);
+  exit (0);
+}
+
+void DumpFitsTable (char *filename, RegImage *image, int *match, int Nmatch) {
+  
+  int i;
+  char *obsstr, *regstr, *line, dummy[64];
+  char *modestr, *typestr, *ccdstr, *datestr;
+  time_t tsecond;
+  Header header, theader;
+  Matrix matrix;
+  FTable table;
+  RegImage *subset;
+
+  bzero (dummy, 64);
+  memset (dummy, ' ', 63);
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+  
+  /* create table header */
+  gfits_create_table_header (&theader, "TABLE", "IMAGE_DATABASE");
+      
+  /* add current date/time to header */
+  ohana_str_to_time ("now", &tsecond);
+  datestr = ohana_sec_to_date (tsecond);
+  gfits_modify (&header,  "DATE", "%s", 1, datestr);
+  gfits_modify (&theader, "DATE", "%s", 1, datestr);
+
+  /* define table layout */
+  gfits_define_table_column (&theader, "A64",   "FILE",       "filename in db",        "");
+  gfits_define_table_column (&theader, "A128",  "PATH",       "fullpath in db",        "");
+  gfits_define_table_column (&theader, "A32",   "FILTER",     "filter name",           "");
+  gfits_define_table_column (&theader, "A32",   "INSTRUMENT", "instrument",            "");
+  gfits_define_table_column (&theader, "A6",    "CCD",        "ccd identifier",        "");
+  gfits_define_table_column (&theader, "A6",    "MODE",       "mef/split/etc",         "");
+  gfits_define_table_column (&theader, "A8",    "TYPE",       "object/flat/bias/etc",  "");
+  gfits_define_table_column (&theader, "A25",   "JUNK",       "space for expansion",   "");
+  gfits_define_table_column (&theader, "F6.1",  "EXPTIME",    "exposure time",        "seconds");
+  gfits_define_table_column (&theader, "F5.3",  "AIRMASS",    "airmass",              "");
+  gfits_define_table_column (&theader, "F7.1",  "SKY",        "background level",     "counts / pixel");
+  gfits_define_table_column (&theader, "F6.1",  "BIAS",       "bias level",           "counts / pixel");
+  gfits_define_table_column (&theader, "F5.2",  "FWHM",       "image quality",        "pixels");
+  gfits_define_table_column (&theader, "F5.1",  "TELFOCUS",   "telescope focus",      "microns");
+  gfits_define_table_column (&theader, "F5.1",  "XPROBE",     "bonnette probe x pos", "microns");
+  gfits_define_table_column (&theader, "F5.1",  "YPROBE",     "bonnette probe y pos", "microns");
+  gfits_define_table_column (&theader, "F5.1",  "ZPROBE",     "bonnette focus",       "microns");
+  gfits_define_table_column (&theader, "F5.1",  "DETTEMP",    "detector temperature", "deg celcius");
+  gfits_define_table_column (&theader, "F5.1",  "TELTEMP0",   "other temperature",    "deg celcius");
+  gfits_define_table_column (&theader, "F5.1",  "TELTEMP1",   "other temperature",    "deg celcius");
+  gfits_define_table_column (&theader, "F5.1",  "TELTEMP2",   "other temperature",    "deg celcius");
+  gfits_define_table_column (&theader, "F5.1",  "TELTEMP3",   "other temperature",    "deg celcius");
+  gfits_define_table_column (&theader, "F5.1",  "ROTANGLE",   "camear rotation angle", "degrees");
+  gfits_define_table_column (&theader, "F10.6", "RA",         "image ra",              "degrees");
+  gfits_define_table_column (&theader, "F10.6", "DEC",        "image dec",             "degrees");
+  gfits_define_table_column (&theader, "A20",   "OBS_TIME",   "time of measurement",   "seconds since Jan 1, 1970 UT");
+  gfits_define_table_column (&theader, "A20",   "REG_TIME",   "time of registration",  "seconds since Jan 1, 1970 UT");
+
+  /* create table, add data values */
+  gfits_create_table (&theader, &table);
+  
+  /* add data to table */
+  for (i = 0; i < Nmatch; i++) {
+    subset = &image[match[i]];
+    obsstr   = ohana_sec_to_date (subset[0].obstime);
+    regstr   = ohana_sec_to_date (subset[0].regtime);
+    typestr  = get_type_name(subset[0].type);
+    modestr  = get_mode_name(subset[0].mode);
+    ccdstr   = ccds[(int)subset[0].ccd];
+
+    line = gfits_table_print (&table, subset[0].pathname, subset[0].filename, 
+			     subset[0].filter, subset[0].instrument, ccdstr,
+			     modestr, typestr, dummy, 
+			     subset[0].exptime, subset[0].airmass, 
+			     subset[0].sky, subset[0].bias, subset[0].fwhm, 
+			     subset[0].telfocus, subset[0].xprobe, subset[0].yprobe, subset[0].zprobe, 
+			     subset[0].dettemp, 
+			     subset[0].teltemp_0, subset[0].teltemp_1, subset[0].teltemp_2, subset[0].teltemp_3,
+			     subset[0].rotangle, 
+			     subset[0].ra, subset[0].dec, 
+			     obsstr, regstr);
+
+    gfits_add_rows (&table, line, 1, strlen(line));
+    free (line);
+    free (obsstr);
+    free (regstr);
+  }
+
+  gfits_write_header  (filename, &header);
+  gfits_write_matrix  (filename, &matrix);
+  gfits_write_Theader (filename, &theader);
+  gfits_write_table   (filename, &table);
+  exit (0);
+}
+
+/* Select, TimeMode are global */
+int PrintSubset (RegImage *image, int *match, int Nmatch) {
+  
+  char ccdstr[64];  
+  int i, j;
+  char *modestr, *typestr, *timestr, *root, *path;
+
+  /* print the selected entries */
+  for (j = 0; j < Nmatch; j++) {
+    
+    i = match[j];
+
+    modestr = get_mode_name (image[i].mode);
+    typestr = get_type_name (image[i].type);
+    timestr = RegTimeMode ? ohana_sec_to_date (image[i].regtime) : ohana_sec_to_date (image[i].obstime);
+
+    if (CCDSeq) {
+      sprintf (ccdstr, "%02d", image[i].ccd);
+    } else {
+      if ((image[i].ccd < 0) || (image[i].ccd >= Nccd)) {
+	sprintf (ccdstr, "%02d", image[i].ccd);
+      } else {
+      sprintf (ccdstr, "%s", ccds[(int)image[i].ccd]);
+      }
+    }      
+
+    if (PTstyle) {
+      root = filerootname (image[i].filename);
+
+      /* do i want ccdstr? add a dot? 654321o.ccd00.ext */
+      if (image[i].mode == M_MEF) {
+	fprintf (stdout, "%s/%s %s/%s%02d %s %s\n", image[i].pathname, image[i].filename, root, root, image[i].ccd, ccdstr, modestr);
+      }
+
+      if (image[i].mode == M_SPLIT) {
+	path = basename (image[i].pathname);
+	fprintf (stdout, "%s/%s %s/%s %s %s\n", image[i].pathname, image[i].filename, path, root, ccdstr, "SPLIT");
+      }
+
+      if ((image[i].mode == M_SINGLE) || (image[i].mode == M_CUBE)) {
+	fprintf (stdout, "%s/%s %s 0 %s\n", image[i].pathname, image[i].filename, root, modestr);
+      }
+    } else {
+      /* this is somewhat poor: I have predefined a subset of value, and I can't guarantee that 'filter' has no spaces */
+      fprintf (stdout, "%5d %6s %6s %s  ", i, typestr, modestr, ccdstr);
+      fprintf (stdout, "%s %s  ", image[i].pathname, image[i].filename);
+      fprintf (stdout, "%s %f %s %f %f %f\n", image[i].filter, image[i].exptime, 
+	       timestr, image[i].fwhm, image[i].bias, image[i].sky);
+    }
+
+    free (timestr);
+  }
+  return (TRUE);
+}
+
+int dump_data (RegImage *image, int Nimage) {
+
+  int i;
+
+  for (i = 0; i < Nimage; i++) {
+    fprintf (stdout, "%s %f %f %f %s\n", image[i].filename, image[i].fwhm, image[i].sky, image[i].bias, image[i].filter);
+  }
+
+  fprintf (stdout, "SUCCESS\n");
+  exit (0);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/rconnect.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/rconnect.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/rconnect.c	(revision 22322)
@@ -0,0 +1,100 @@
+# include "imregister.h"
+# include "imreg.h"
+
+/* rconnect opens a remote shell on hostname and returns two file descriptors:
+   one for read, one for write */
+
+int rconnect (char *hostname, char *command, int *rsock, int *wsock) {
+
+  int i, rfd[2], wfd[2], status;
+  pid_t pid;
+  char buffer[0x4000];
+  char *file;
+  Fifo fifo;
+
+  InitFifo (&fifo, 0x4000, 0x1000);
+
+  status = pipe (rfd);
+  if (status < 0) {
+    perror ("pipe");
+    return (FALSE);
+  }
+  status = pipe (wfd);
+  if (status < 0) {
+    perror ("pipe");
+    return (FALSE);
+  }
+
+  file = filebasename (command);
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    fprintf (stderr, "starting remote connection to %s...", hostname);
+    /* close the excess sockets */
+    close (wfd[1]);
+    close (rfd[0]);
+    dup2 (wfd[0], STDIN_FILENO);
+    dup2 (rfd[1], STDOUT_FILENO);
+    dup2 (rfd[1], STDERR_FILENO);
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    status = execl (command, file, hostname, "/bin/csh", NULL); 
+    fprintf (stderr, "error starting remote shell process\n");
+    Shutdown (1);
+  }
+  *wsock = wfd[1];
+  *rsock = rfd[0];
+  close (wfd[0]);
+  close (rfd[1]);
+  
+  fcntl (*rsock, F_SETFL, O_NONBLOCK);
+  fcntl (*wsock, F_SETFL, O_NONBLOCK);
+
+  sprintf (buffer, "echo PTOLEMY STARTED\n");
+  status = write (*wsock, buffer, strlen(buffer));
+  if ((status == -1) && (errno == EPIPE)) {
+    fprintf (stderr, "socket closed unexpectedly\n");
+    close (*wsock);
+    close (*rsock);
+    return (FALSE);
+  }
+
+  /* try to get evidence connection is alive - wait upto a few seconds */
+  status = -1;
+  for (i = 0; (i < 300) && (status == -1); i++) {
+    fcntl (*rsock, F_SETFL, O_NONBLOCK);
+    status = SockScan ("PTOLEMY STARTED", &fifo, *rsock);
+    if (!(i % 30)) fprintf (stderr, ".");
+    if (status == 0) {
+      fprintf (stderr, "socket closed unexpectedly\n");
+      close (*wsock);
+      close (*rsock);
+      return (FALSE);
+    }
+  }
+  if (i == -1) {
+    fprintf (stderr, "timeout while connecting\n");
+    close (*wsock);
+    close (*rsock);
+    return (FALSE);
+  }
+  fprintf (stderr, "Connected\n");
+
+  /* the onintr command works with csh/tcsh type shells to
+     prevent trapping of SIGTERM, used to kill hung jobs.
+     for bash/sh type shells, you can use SIGQUIT instead
+     without setting onintr */
+  sprintf (buffer, "onintr\n");
+  write (*wsock, buffer, strlen(buffer));
+  if ((status == -1) && (errno == EPIPE)) {
+    fprintf (stderr, "socket closed unexpectedly\n");
+    close (*wsock);
+    close (*rsock);
+    return (FALSE);
+  }
+
+  FreeFifo (&fifo);
+  return (pid);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/submit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/submit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/submit.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "imregister.h"
+# include "imreg.h"
+
+int SubmitImages (RegImage *image) {
+
+  int i;
+  char line[1024], *root, *path;
+
+  /* send these images to FIFOs for imstat and ptolemy */ 
+
+  if (image[0].mode == M_MEF) {
+    for (i = 0; i < image[0].ccd; i++) {
+      root = filerootname (image[0].filename);
+      if (i < Nccd) {
+	sprintf (line, "%s/%s %s/%s%s %s %s", image[0].pathname, image[0].filename, root, root, ccdn[i], ccds[i], "MEF");
+      } else {
+	sprintf (line, "%s/%s %s/%sxx none %s", image[0].pathname, image[0].filename, root, root, "MEF");
+      }
+      if (!WriteFIFO (ImstatFifo, line)) return (FALSE);
+      if (image[0].type == T_OBJECT) {
+	if (!WriteFIFO (PtolemyFifo, line)) return (FALSE);
+      }
+    }
+  } 
+
+  if (image[0].mode == M_SINGLE) {
+    root = filerootname (image[0].filename);
+    sprintf (line, "%s/%s %s %02d %s", image[0].pathname, image[0].filename, root, 0, "SINGLE");
+    if (!WriteFIFO (ImstatFifo, line)) return (FALSE);
+    if (image[0].type == T_OBJECT) {
+      if (!WriteFIFO (PtolemyFifo, line)) return (FALSE);
+    }
+  } 
+
+  if (image[0].mode == M_CUBE) {
+    root = filerootname (image[0].filename);
+    sprintf (line, "%s/%s %s %02d %s", image[0].pathname, image[0].filename, root, 0, "CUBE");
+    if (!WriteFIFO (ImstatFifo, line)) return (FALSE);
+    if (image[0].type == T_OBJECT) {
+      if (!WriteFIFO (PtolemyFifo, line)) return (FALSE);
+    }
+  } 
+
+  if (image[0].mode == M_SPLIT) {
+    path = basename (image[0].pathname);
+    root = filerootname (image[0].filename);
+    sprintf (line, "%s/%s %s/%s %02d %s", image[0].pathname, image[0].filename, path, root, image[0].ccd, "SPLIT");
+    if (!WriteFIFO (ImstatFifo, line)) return (FALSE);
+    if (image[0].type == T_OBJECT) {
+      if (!WriteFIFO (PtolemyFifo, line)) return (FALSE);
+    }
+  }    
+
+  return (TRUE);
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/unique.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/unique.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/imreg/unique.c	(revision 22322)
@@ -0,0 +1,79 @@
+# include "imregister.h"
+# include "imreg.h"
+
+void sortstr (char **S, int *X, int N) {
+
+# define SWAPFUNC(A,B){ \
+  char *tmp = S[A]; S[A] = S[B]; S[B] = tmp; \
+  int  itmp = X[A]; X[A] = X[B]; X[B] = itmp; \
+}
+# define COMPARE(A,B)(strcmp(S[A], S[B]) < 0)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+/* input is a subset index of image list, output is a new subset */
+int *unique_entries (RegImage *image, int Nimage, int *subset, int *Nmatch) {
+
+  int i, j, k, m, Nsubset;
+  int N, NMATCH;
+  int *match, *entry;
+  char idxline[128];
+  char **index;
+
+  if (!output.unique) return (subset);
+
+  /* create output index */
+  N = 0;
+  NMATCH = 1000;
+  ALLOCATE (match, int, NMATCH);
+
+  Nsubset = *Nmatch;
+
+  /* index = filename.ccd */
+  ALLOCATE (index, char *, Nsubset);
+  ALLOCATE (entry, int, Nsubset);
+  for (i = 0; i < Nsubset; i++) {
+    sprintf (idxline, "%s.%02d", image[subset[i]].filename, image[subset[i]].ccd);
+    index[i] = strcreate (idxline);
+    entry[i] = subset[i];
+  }
+  sortstr (index, entry, Nsubset);
+
+  /* find unique sequences */
+  for (i = 0; i < Nsubset; ) {
+    for (j = i + 1; (j < Nsubset) && (!strcmp (index[i], index[j])); j++);
+
+    /* find first entry with bias != 0, else entry i */
+    m = i;
+    for (k = i; k < j; k++) {
+      if (image[entry[k]].bias != 0) {
+	m = k;
+	break;
+      }
+    }
+    
+    /* add unique entry to output list */
+    match[N] = entry[m];
+    N ++;
+    if (N == NMATCH) {
+      NMATCH += 1000;
+      REALLOCATE (match, int, NMATCH);
+    }
+
+    /* j always points to the next entry */
+    i = j;
+  }
+
+  for (i = 0; i < Nsubset; i++) free (index[i]);
+  free (index);
+  free (entry);
+  free (subset);
+
+  *Nmatch = N;
+  return (match);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/detrend.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/detrend.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/detrend.h	(revision 22322)
@@ -0,0 +1,111 @@
+
+typedef struct {
+  int state; /* none, close, perfect */
+  int crit;  /* which criterion? */
+  int image; /* which detrend image? */
+} Match;
+
+typedef struct {
+  int TimeSelect;    time_t tstart, tstop;
+  char *label; 
+  char *imageID;
+  int order;
+  int type;
+  char mode;
+  int CCDSelect;     int CCD;
+  int ExptimeSelect; float Exptime;
+  int filter;
+} Descriptor;
+
+/* MosaicSelect & ImageSelect define these values */
+typedef struct {
+  int ModeSelect;    int Mode;
+  int TypeSelect;    int Type;
+  int CCDSelect;     int CCD;
+  int FilterSelect;  int Filter;
+  int EntrySelect;   int Entry;
+  int LabelSelect;   char *Label;
+  int NameSelect;    char *Name;
+  int ExptimeSelect; float Exptime;
+  int TimeSelect;    time_t tstart, tstop;
+  int MatchNumber;
+} Criteria;
+
+int Ncriteria;
+Criteria *criteria;
+
+struct {
+  int Select;
+  int Delete;
+  int Modify;
+  int Altpath;
+  int ElixirSmart;
+  int TimeMode;
+  int Recipe;
+  int Close;
+  int Criteria;
+  int Chipname;
+
+  char *ModifyEntry, *ModifyValue;
+  time_t TimeValue;
+  
+  int verbose;
+  char *table;
+  char *bintable;
+} output;
+
+/* altpath values */
+enum {NONE, ADD, DELETE, UPDATE};
+enum {START, STOP, REG};
+enum {UNLOCK, LOCK, IGNORE};
+enum {MATCH_NONE, MATCH_CLOSE, MATCH_PERFECT};
+
+# define DEBUG 0
+
+char **RecipeType;
+int Nrecipe;
+
+int SingleIsSplit;
+int NoReg;
+
+int regargs (int argc, char **argv, Descriptor *descriptor);
+int args (int argc, char **argv);
+int DefineImage (char *filename, Descriptor *descriptor);
+DetReg DefineEntry (Descriptor descriptor);
+
+char *set_dBFile ();
+char *get_dBPath ();
+int delete_image (DetReg *image);
+
+int SaveEntry (char *input, DetReg *newdata, char *ID);
+
+char **LoadRecipe (char *filter, int *nrecipe);
+
+Match *MatchCriteria (DetReg *image, int Nimage, int *nmatch);
+Match *UniqueSubset (DetReg *image, int Nimage, Match *match, int *nmatch);
+Match *ExptimeCriteria (DetReg *image, int Nimage, Match *match, int *nmatch);
+Match *CloseCriteria (DetReg *image, int Nimage, Match *match, int *nmatch);
+
+Match CheckCriteria (DetReg *image);
+
+int OutputSubset (DetReg *image, int Nimage, Match *match, int Nmatch);
+int DumpFitsTable (char *filename, DetReg *detdata, Match *match, int Nmatch);
+int PrintSubset (DetReg *detdata, Match *match, int Nmatch);
+Match SelectEntry (DetReg *image, int Nimage, Match *list, int Nlist, Criteria *crit);
+
+int usage ();
+Criteria *MosaicCriteria (Criteria base, char *filename, int *ncrit);
+Criteria *ImageCriteria (Criteria base, char *filename, char *ImageExtend, char *ImageMode, int *ncrit);
+Criteria *ExpandBase (Criteria base, int *ncrit, time_t *tstart, time_t *tstop, int *filt);
+Criteria *ExpandRecipe (Criteria *base, int *Ncrit);
+
+int escape (int mode, char *message);
+int DumpFitsBintable (char *filename, DetReg *image, Match *match, int Nmatch);
+int cmp_crit (Criteria *crit, DetReg *image);
+int set_crit (Criteria *crit, DetReg *image);
+int ckpathname (char *newpath);
+int PrintCriteria ();
+
+int SetAltpath (FITS_DB *db, DetReg *image, int Nimage, Match *match, int Nmatch);
+int ModifySubset (FITS_DB *db, DetReg *image, int Nimage, Match *match, int Nmatch);
+void DeleteSubset (FITS_DB *db, DetReg *image, int Nimage, Match *match, int Nmatch);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/imphot.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/imphot.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/imphot.h	(revision 22322)
@@ -0,0 +1,29 @@
+
+struct {
+  int PhotcodeSelect; int photcode;
+  int NameSelect;     char *Name;
+  int Ntimes;         time_t *tstart, *tstop;
+  int CodeSelect;     int Code;
+} criteria;
+
+struct {
+  int   modify;
+  char *ModifyValue;
+  char *ModifyEntry;
+  char *table;
+  char *bintable;
+} options;
+
+int VERBOSE;
+int FORCE_READ;
+
+int db_load (FITS_DB *db);
+int args (int argc, char **argv);
+int DumpFitsBintable (char *filename, Image *image, int *match, int Nmatch);
+int DumpFitsTable (char *filename, Image *image, int *match, int Nmatch);
+void ModifySubset (FITS_DB *db, Image *image, int Nimage, int *match, int Nmatch);
+int PrintSubset (Image *image, int *match, int Nmatch);
+int output (Image *image, int *match, int Nmatch);
+int rfits (FITS_DB *db);
+int rtext (FITS_DB *db);
+int *subset (Image *image, int Nimage, int *nsubset);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/imreg.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/imreg.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/imreg.h	(revision 22322)
@@ -0,0 +1,130 @@
+# include <signal.h>
+
+typedef struct {
+  char *buffer;
+  int   Nalloc;
+  int   Nmaxread;
+  int   Nextra;
+  int   Nlast;
+  int   Nbuffer;
+} Fifo;
+
+struct {
+  int ModeSelect;    int Mode;
+  int TypeSelect;    int Type;
+  int CCDSelect;     int CCD;
+  int FilterSelect;  char *Filter;
+  int EntrySelect;   int Entry;
+  int LabelSelect;   char *Label;
+  int ExptimeSelect; float Exptime;
+  int TimeSelect;    unsigned long Time;
+  int NameSelect;    char *Name;
+  int ProcSelect;    int Proc;
+  int DistSelect;    int Dist;
+  int Ntimes;        time_t *tstart, *tstop;
+  int MatchNumber;
+  int Close;
+} criteria;
+
+struct {
+  int delete;
+  int modify;
+
+  int modify_path;
+  char *oldpath, *newpath;
+
+  int unique;
+  int mef2split, split2mef;
+  int modify_dist, dist;
+  int modify_filter, modify_type, type;
+  char *filter;
+
+  int HST;
+  int verbose;
+  char *table;
+  char *bintable;
+  char *cadctable;
+} output;
+
+typedef struct {
+  int *ccd;
+  int Nccd;
+  char name[64];
+} MosaicRegion;
+
+typedef struct {
+  MosaicRegion center;
+  MosaicRegion outer;
+  MosaicRegion top;
+  MosaicRegion bottom;
+  MosaicRegion left;
+  MosaicRegion right;
+} MosaicLayout;
+
+enum {UNLOCK, LOCK, IGNORE};
+
+int SingleIsSplit;
+int NeedType;
+int NoReg;
+int IMSORT;
+int CLIENT;
+char *PIDFILE;
+int LOOP_DELAY;
+
+int args (int argc, char **argv);
+
+void DeleteSubset (FITS_DB *db, RegImage *image, int Nimage, int *match, int Nmatch);
+RegImage *iminfo (char *filename);
+
+int *match_criteria (RegImage *image, int Nimage, int *Nmatch);
+int *match_images (RegImage *image, int Nimage, RegImage *subset, int Nsubset, int *Nmatch);
+
+RegImage *newimages (RegImage *image, int *Nimage);
+
+void ModifySubset (FITS_DB *db, RegImage *image, int Nimage, int *match, int Nmatch);
+void SetOutputMode (char *mode);
+void OutputSubset (RegImage *image, int Nimage, int *match, int Nmatch);
+void DumpFitsBintable (char *filename, RegImage *image, int *match, int Nmatch);
+void DumpFitsTable (char *filename, RegImage *image, int *match, int Nmatch);
+int PrintSubset (RegImage *image, int *match, int Nmatch);
+int dump_data (RegImage *image, int Nimage);
+int SubmitImages (RegImage *image);
+int load_probes (char *filename, unsigned long tzero, int *wantprobe, double *values, int Nprobe);
+int define_table (Header *header, Matrix *matrix, Header *theader, FTable *table);
+void set_timezone (double dt);
+
+void DumpCADCTable (char *filename, RegImage *image, int *match, int Nmatch);
+int *GetObsIDSubset (RegImage *image, int start, int *index, int *entry, int Nindex, int *Nsubset);
+int *GetUniqueObsID (RegImage *image, int *index, int *entry, int Nindex, int *Nmatch);
+void GetObsIDIndex (RegImage *image, int *match, int Nmatch, int **Index, int **Entry);
+double SigmaClipList (double *list, int N);
+double MosaicIQStats (RegImage *image, int *match, int Nmatch, MosaicRegion *region);
+MosaicLayout *CreateCFH12K ();
+MosaicLayout *CreateMegaCam ();
+int GetREFCCD (RegImage *image, int *index, int *entry, int Nindex, int start);
+
+int imregclient (char *fitsfile, char *statfile, char *datfile);
+void SIG_DIE (int sig);
+void SIG_PIPE (int sig);
+void SetSignals ();
+void KillProcess (char *pidfile);
+void StatusProcess (char *pidfile);
+int close_lock_db ();
+int ConfigPID (char *pidfile);
+int print_db_status (char *message);
+int escape (int mode, char *message);
+
+int LoadPID (char *file, pid_t *pid, char *username, char *machine);
+int Shutdown (int status);
+void RemovePID ();
+int rconnect (char *hostname, char *command, int *rsock, int *wsock);
+
+int InitFifo (Fifo *fifo, int Nalloc, int Nextra);
+int FlushFifo (Fifo *fifo);
+int ShiftFifo (Fifo *fifo);
+int ReadtoFifo (Fifo *fifo, int sock);
+void FreeFifo (Fifo *fifo);
+int SockScan (char *string, Fifo *fifo, int sock);
+int memstr (char *m1, char *m2, int n);
+
+int *unique_entries (RegImage *image, int Nimage, int *subset, int *Nmatch);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/imregister.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/imregister.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/imregister.h	(revision 22322)
@@ -0,0 +1,100 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <errno.h>
+# include <time.h>
+# include <ctype.h>
+# include <stdlib.h>
+
+/* config variables from very old versions - remove?
+char ImageTemplate[256];
+char DBServer[256];
+*/
+
+char ImstatFifo[256];
+char PtolemyFifo[256];
+
+char ImageDB[256];
+char DetrendDB[256];
+char PhotDB[256];
+char TransDB[256];
+char ImPhotDB[256];
+char TempDB[256];
+char LogFile[256];
+char CONNECT[64];
+
+int  NDetrendAltDB;
+char **DetrendAltDB;
+
+char PhotCodeFile[256];
+char TempLogFile[256];
+char FilterList[256];
+char CameraConfig[256];
+char RecipeFile[256];
+
+char DateKeyword[64];
+char DateMode[64];
+char UTKeyword[64];
+char MJDKeyword[64];
+char JDKeyword[64];
+char ExptimeKeyword[16];
+char ImagetypeKeyword[16];
+char CCDnumKeyword[16];
+char FilterKeyword[16];
+char AirmassKeyword[16];
+char FocusKeyword[16];
+char RotationKeyword[16];
+char DettempKeyword[16];
+char Teldata1Keyword[16];
+char Teldata2Keyword[16];
+char Teldata3Keyword[16];
+char RADecDegKeyword[16];
+char DECDecDegKeyword[16];
+char RASexigKeyword[16];
+char DECSexigKeyword[16];
+char CameraKeyword[16];
+char Camera[64];
+char SeeingREFCCD[64];
+double ARCSEC_PIXEL;
+
+/* global vars used by camera info */
+char **ccds, **ccdn;
+int  Nccd;
+
+/* global vars used by filter abstraction */
+# define FILTER_ANY  -1 
+# define FILTER_NONE 0
+int NFILTER;
+char **filtername;
+char **filterhash;
+int   *filternum;
+
+int get_trange_arguments (int *argc, char **argv, time_t **Tstart, time_t **Tstop, int *ntimes);
+int get_filter_arguments (int *argc, char **argv, int **Filt, int *Nfilt);
+
+int parse_time (Header *header);
+void sort (float *value, int N);
+void dsort (double *value, int N);
+void sortstr (char **S, int *X, int N);
+void sortpair (int *X, int *Y, int N);
+
+void ConfigCamera ();
+int MatchCCDNameHeader (Header *header);
+int MatchCCDName (char *ID);
+void ConfigFilter ();
+int MatchFilterList (char *line);
+void ConfigInit (int *argc, char **argv);
+void WarnConfig (char *config, char *key, char * mode, int N, void *var);
+int WriteFIFO (char *filename, char *line);
+double get_fwhm (char *filename);
+
+void get_version (int argc, char **argv, char *version);
+int set_db (char *filename);
+
+void warn_scan (Header *header, char *field, char *format, int N, void *var);
+int gfits_scan_nchar (Header *header, int size, char *field, int N,...);
+void warn_scan_nchar (Header *header, int size, char *field, int N, void *var);
+
+void clean_spaces (char *line);
+
+/* where is this defined ?? */
+char *basename (char *);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/mosaics.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/mosaics.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/mosaics.h	(revision 22322)
@@ -0,0 +1,63 @@
+
+MosaicLayout *CreateCFH12K () {
+
+  MosaicLayout *layout;
+
+  ALLOCATE (layout, MosaicLayout, 1);
+
+  layout[0].center.Nccd = 4;
+  ALLOCATE (layout[0].center.ccd, int, layout[0].center.Nccd);
+  layout[0].center.ccd[0] = 2;
+  layout[0].center.ccd[1] = 3;
+  layout[0].center.ccd[2] = 8;
+  layout[0].center.ccd[3] = 9;
+
+  layout[0].outer.Nccd = 8;
+  ALLOCATE (layout[0].outer.ccd, int, layout[0].outer.Nccd);
+  layout[0].outer.ccd[0] = 0;
+  layout[0].outer.ccd[1] = 1;
+  layout[0].outer.ccd[2] = 4;
+  layout[0].outer.ccd[3] = 5;
+  layout[0].outer.ccd[4] = 6;
+  layout[0].outer.ccd[5] = 7;
+  layout[0].outer.ccd[6] = 10;
+  layout[0].outer.ccd[7] = 11;
+
+  layout[0].top.Nccd = 6;
+  ALLOCATE (layout[0].top.ccd, int, layout[0].top.Nccd);
+  layout[0].top.ccd[0] = 0;
+  layout[0].top.ccd[1] = 1;
+  layout[0].top.ccd[2] = 2;
+  layout[0].top.ccd[3] = 3;
+  layout[0].top.ccd[4] = 4;
+  layout[0].top.ccd[5] = 5;
+
+  layout[0].bottom.Nccd = 6;
+  ALLOCATE (layout[0].bottom.ccd, int, layout[0].bottom.Nccd);
+  layout[0].bottom.ccd[0] = 6;
+  layout[0].bottom.ccd[1] = 7;
+  layout[0].bottom.ccd[2] = 8;
+  layout[0].bottom.ccd[3] = 9;
+  layout[0].bottom.ccd[4] = 10;
+  layout[0].bottom.ccd[5] = 11;
+
+  layout[0].left.Nccd = 6;
+  ALLOCATE (layout[0].left.ccd, int, layout[0].left.Nccd);
+  layout[0].left.ccd[0] = 0;
+  layout[0].left.ccd[1] = 1;
+  layout[0].left.ccd[2] = 2;
+  layout[0].left.ccd[3] = 6;
+  layout[0].left.ccd[4] = 7;
+  layout[0].left.ccd[5] = 8;
+
+  layout[0].right.Nccd = 6;
+  ALLOCATE (layout[0].right.ccd, int, layout[0].right.Nccd);
+  layout[0].right.ccd[0] = 3;
+  layout[0].right.ccd[1] = 4;
+  layout[0].right.ccd[2] = 5;
+  layout[0].right.ccd[3] = 9;
+  layout[0].right.ccd[4] = 10;
+  layout[0].right.ccd[5] = 11;
+
+  return (layout);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/photreg.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/photreg.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/photreg.h	(revision 22322)
@@ -0,0 +1,45 @@
+# include <signal.h>
+
+struct {
+  int Ntimes;         time_t *tstart, *tstop;
+  int PhotCodeSelect; int photcode;
+  int LabelSelect;    char *Label;
+} criteria;
+
+struct {
+  int delete;
+  int modify;
+
+  int HST;
+  int verbose;
+  int convert;
+  char *table;
+  char *bintable;
+  char *cadctable;
+
+  int equiv;
+  int offset;
+  char *db;
+  int photcodenames;
+} output;
+
+enum {UNLOCK, LOCK, IGNORE};
+
+int args (int argc, char **argv);
+int regargs (int argc, char **argv, PhotPars *);
+
+void DeleteSubset (FITS_DB *db, PhotPars *photpars, int Nphotpars, int *match, int Nmatch);
+int *match_criteria (PhotPars *photpars, int Nphotpars, int *Nmatch);
+
+void OutputSubset (PhotPars *photpars, int Nphotpars, int *match, int Nmatch);
+
+void DumpFitsBintable (char *filename, PhotPars *image, int *match, int Nmatch);
+void DumpFitsTable (char *filename, PhotPars *image, int *match, int Nmatch);
+int PrintSubset (PhotPars *image, int *match, int Nmatch);
+
+void set_timezone (double dt);
+void getImageData (char *Image, char *ImageCCD, char *ImageMode);
+int escape (int mode, char *message);
+
+PhotPars *PhotParsOld_to_PhotPars (PhotParsOld *input, int Nphotpars);
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/spreg.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/spreg.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/include/spreg.h	(revision 22322)
@@ -0,0 +1,105 @@
+# include <signal.h>
+
+enum {
+  SPMODE_UKN,				/* unknown mode */
+  SPMODE_PHU,				/* one spectrum, primary header unit */
+  SPMODE_MEF,				/* spectrum group, extensions */
+  SPMODE_EXT,				/* one of a spectrum group, extension */
+  SPMODE_N
+};
+
+enum {
+  SPSTATE_UKN,				/* unknown state */
+  SPSTATE_RAW,				/* raw image counts */
+  SPSTATE_FLT,				/* flattened image counts */
+  SPSTATE_CLN,				/* clean extraction */
+  SPSTATE_WAV,				/* wavelength calibrated */
+  SPSTATE_FLX,				/* flux calibrated (implies WAV) */
+  SPSTATE_N
+};
+
+struct {
+  int Ntimes;           time_t *tstart, *tstop;
+  int ModeSelect;       int Mode;
+  int StateSelect;      int State;
+  int ExptimeSelect;    float Exptime;
+  int FilenameSelect;   char *Filename;
+  int ObjectSelect;     char *Object;
+  int TelescopeSelect;  char *Telescope;
+  int InstrumentSelect; char *Instrument;
+  int MatchNumber;
+} criteria;
+
+struct {
+  int delete;
+  int modify;
+
+  int unique;
+  int modify_path;
+  char *oldpath, *newpath;
+
+  int modify_mode, mode;
+  int modify_state, state;
+
+  int HST;
+  int verbose;
+  char *table;
+  char *bintable;
+} output;
+
+typedef struct {
+  int *ccd;
+  int Nccd;
+  char name[64];
+} MosaicRegion;
+
+typedef struct {
+  MosaicRegion center;
+  MosaicRegion outer;
+  MosaicRegion top;
+  MosaicRegion bottom;
+  MosaicRegion left;
+  MosaicRegion right;
+} MosaicLayout;
+
+enum {UNLOCK, LOCK, IGNORE};
+
+int SingleIsSplit;
+int NeedType;
+int NoReg;
+int IMSORT;
+int CLIENT;
+char *PIDFILE;
+int LOOP_DELAY;
+int DUMP;
+
+char SpectrumDB[64];
+char ObjectKeyword[64];
+char TelescopeKeyword[64];
+
+
+int args (int argc, char **argv);
+int *match_criteria (Spectrum *spectrum, int Nspectrum, int *Nmatch);
+int *unique_entries (Spectrum *spectrum, int Nspectrum, int *subset, int *Nmatch);
+
+void ModifySubset (FITS_DB *db, Spectrum *spectrum, int Nspectrum, int *match, int Nmatch);
+void DeleteSubset (FITS_DB *db, Spectrum *spectrum, int Nspectrum, int *match, int Nmatch);
+void OutputSubset (Spectrum *spectrum, int Nspectrum, int *match, int Nmatch);
+
+void SetOutputMode (char *mode);
+void DumpFitsBintable (char *filename, Spectrum *spectrum, int *match, int Nmatch);
+void DumpFitsTable (char *filename, Spectrum *spectrum, int *match, int Nmatch);
+int PrintSubset (Spectrum *spectrum, int *match, int Nmatch);
+int dump_data (Spectrum *spectrum, int Nspectrum);
+
+Spectrum *spinfo (char *filename);
+int *match_spectrums (Spectrum *subset, int Nsubset, int *Nmatch);
+int SubmitSpectrums (Spectrum *spectrum);
+void set_timezone (double dt);
+
+void showinfo (Spectrum *spec);
+int escape (int mode, char *message);
+int set_spectra (Spectrum *new, int Nnew);
+Spectrum *get_spectra (int *N);
+
+void ConfigInitSpec (int *argc, char **argv);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/args.photreg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/args.photreg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/args.photreg.c	(revision 22322)
@@ -0,0 +1,137 @@
+# include "imregister.h"
+# include "photreg.h"
+
+void usage ();
+
+int regargs (int argc, char **argv, PhotPars *newdata) {
+
+  int *list, Nlist;
+  int N, Ntimes;
+  time_t *tstart, *tstop;
+  PhotCode *photcode, *depcode;
+
+  ConfigInit (&argc, argv);
+
+  photcode = NULL;
+  output.modify = TRUE;
+  bzero (newdata, sizeof(PhotPars));
+
+  if (get_argument (argc, argv, "-h")) usage ();
+  if (get_argument (argc, argv, "--help")) usage ();
+
+  /* set the required database */
+  output.db = strcreate ("phot");
+  if ((N = get_argument (argc, argv, "-trans"))) {
+    remove_argument (N, &argc, argv);
+    output.db = strcreate ("trans");
+  }
+
+  /*** optional fields ***/
+  strcpy (newdata[0].label, "elixir");
+  if ((N = get_argument (argc, argv, "-label"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (newdata[0].label, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  newdata[0].Nmeas = 1;
+  if ((N = get_argument (argc, argv, "-Nmeas"))) {
+    remove_argument (N, &argc, argv);
+    newdata[0].Nmeas = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  newdata[0].Ntime = 1;
+  if ((N = get_argument (argc, argv, "-Ntime"))) {
+    remove_argument (N, &argc, argv);
+    newdata[0].Ntime = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  output.offset = FALSE;
+  if ((N = get_argument (argc, argv, "-offset"))) {
+    remove_argument (N, &argc, argv);
+    output.offset = TRUE;
+  }
+
+  /**** required arguments ****/
+  if (!get_trange_arguments (&argc, argv, &tstart, &tstop, &Ntimes)) usage ();
+  if (Ntimes != 1) usage ();
+  newdata[0].tstart = tstart[0];
+  newdata[0].tstop = tstop[0];
+
+  if (!get_argument (argc, argv, "-zp")) goto required;
+  if (!get_argument (argc, argv, "-dzp")) goto required;
+  if (!get_argument (argc, argv, "-photcode")) goto required;
+
+  /* observed zero point */
+  if ((N = get_argument (argc, argv, "-zp"))) {
+    remove_argument (N, &argc, argv);
+    newdata[0].ZP = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  } 
+
+  /* error on observed zero point */
+  if ((N = get_argument (argc, argv, "-dzp"))) {
+    remove_argument (N, &argc, argv);
+    newdata[0].dZP = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* photcode system */
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    if ((photcode = GetPhotcodebyName (argv[N])) == NULL) {
+      fprintf (stderr, "ERROR: photcode not found in photcode table\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  /* remainding newdata values can be set once photcode is known 
+     be careful about consistency for offset definition:
+     dM == ZPi - ZPo  (ZPo = nominal zero point, ZPi = specific observed zero point)
+     ie, clouds = -dM (ZPi < ZPo)
+  */
+  
+  /* find first (!) dep photcode which is equivalent to this code */
+  list = GetPhotcodeEquivList (photcode[0].code, &Nlist);
+  depcode = GetPhotcodebyCode (list[0]);
+
+  newdata[0].photcode = photcode[0].code;
+  newdata[0].refcode  = photcode[0].equiv;
+  newdata[0].X        = photcode[0].X[0];
+  newdata[0].c1       = photcode[0].c1;
+  newdata[0].c2       = photcode[0].c2;
+  
+  newdata[0].K        = depcode[0].K;
+  newdata[0].ZPo      = 0.001*photcode[0].C + 0.001*depcode[0].C;
+  if (output.offset) newdata[0].ZP += newdata[0].ZPo;
+  return (TRUE);
+
+required:
+  fprintf (stderr, "missing required fields\n");
+  usage ();
+  return (FALSE);
+}
+
+/* differences between phot.db & trans.db:
+
+   Ntime : photreg = 1 : transreg = N
+   dBFile: phot.db     : trans.db
+   ASCII EXTNAME: IMAGE_ZPTS : SUMMARY_ZPTS
+   BINARY EXTNAME: ZERO_POINTS_3.0 : TRANS_POINTS_3.0
+   
+   photreg -photcode B -zp 26.1 -dzp 0.02 -date 2003/1/1,10:00:00 -Nmeas 5 -Ntime 1 -db trans
+   photreg -photcode B -zp 26.1 -dzp 0.02 -date 2003/1/1,10:00:00 -Nmeas 5 -Ntime 1 -db trans
+
+   photsearch -db trans
+   photsearch -db phot
+   
+*/
+
+void usage () {  
+  fprintf (stderr, "USAGE: photreg (-zp zp) (-dzp dzp) (-trange start range|end) (-photcode code)\n");
+  fprintf (stderr, "       [-label label] [-offset] [-Nmeas N] [-Ntime N] [-db trans]\n");
+  exit (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/args.photsearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/args.photsearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/args.photsearch.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include "imregister.h"
+# include "photreg.h"
+
+int args (int argc, char **argv) {
+
+  int N, equiv;
+
+  ConfigInit (&argc, argv);
+  ConfigCamera ();
+  ConfigFilter ();
+
+  criteria.Ntimes = 0;
+  if (!get_trange_arguments (&argc, argv, &criteria.tstart, &criteria.tstop, &criteria.Ntimes)) {
+    fprintf (stderr, "ERROR: syntax error\n");
+    exit (1);
+  }
+
+  criteria.PhotCodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    if ((criteria.photcode = GetPhotcodeCodebyName (argv[N])) == 0) {
+      fprintf (stderr, "ERROR: photcode not found in table\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    criteria.PhotCodeSelect = TRUE;
+  }
+
+  criteria.LabelSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-label"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Label = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.LabelSelect = TRUE;
+  }
+
+  output.delete = FALSE;
+  if ((N = get_argument (argc, argv, "-delete"))) {
+    remove_argument (N, &argc, argv);
+    output.delete = TRUE;
+  }
+
+  output.equiv = FALSE;
+  if ((N = get_argument (argc, argv, "-equiv"))) {
+    remove_argument (N, &argc, argv);
+    output.equiv = TRUE;
+  }
+
+  output.offset = FALSE;
+  if ((N = get_argument (argc, argv, "-offset"))) {
+    remove_argument (N, &argc, argv);
+    output.offset = TRUE;
+  }
+
+  output.table = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-table"))) {
+    remove_argument (N, &argc, argv);
+    output.table = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  output.bintable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-bintable"))) {
+    remove_argument (N, &argc, argv);
+    output.bintable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  output.verbose = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    output.verbose = TRUE;
+  }
+
+  output.photcodenames = TRUE;
+  if ((N = get_argument (argc, argv, "-photcodes"))) {
+    remove_argument (N, &argc, argv);
+    output.photcodenames = FALSE;
+  }
+
+  output.convert = FALSE;
+  if ((N = get_argument (argc, argv, "-convert"))) {
+    remove_argument (N, &argc, argv);
+    output.convert = TRUE;
+    if (output.delete || output.modify) {
+      fprintf (stderr, "can't change old format table\n");
+      exit (1);
+    }
+  }
+
+  if ((N = get_argument (argc, argv, "-image"))) {
+    remove_argument (N, &argc, argv);
+    if (argc < N + 3) {
+      fprintf (stderr, "missing arguments to -image\n");
+      exit (1);
+    }
+    getImageData (argv[N], argv[N+1], argv[N+2]);
+    remove_argument (N, &argc, argv);
+    remove_argument (N, &argc, argv);
+    remove_argument (N, &argc, argv);
+  }
+
+  output.db = strcreate ("phot");
+  if ((N = get_argument (argc, argv, "-trans"))) {
+    remove_argument (N, &argc, argv);
+    output.db = strcreate ("trans");
+  }
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: photsearch [config ops] [-v] [-version]\n");
+    fprintf (stderr, "       [-trange start stop/range] [-photcode code]\n");
+    fprintf (stderr, "       [-label label] [-delete]\n");
+    fprintf (stderr, "       [-table table.fits] [-bintable bintable.fits]\n");
+    exit (1);
+  }
+ 
+  /* set up photcode information */
+  if (criteria.PhotCodeSelect && output.equiv) {
+    if (!(equiv = GetPhotcodeEquivCodebyCode (criteria.photcode))) {
+      fprintf (stderr, "ERROR: photcode not found in photcode table\n");
+      exit (1);
+    }
+    criteria.photcode = equiv;
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/convert.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/convert.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/convert.c	(revision 22322)
@@ -0,0 +1,27 @@
+# include "imregister.h"
+# include "photreg.h"
+
+PhotPars *PhotParsOld_to_PhotPars (PhotParsOld *input, int Nphotpars) {
+
+  int i;
+  PhotPars *output;
+
+  ALLOCATE (output, PhotPars, Nphotpars);
+
+  for (i = 0; i < Nphotpars; i++) {
+    output[i].ZP       = input[i].ZP;
+    output[i].ZPo      = input[i].ZPo;
+    output[i].dZP      = input[i].dZP;
+    output[i].K        = input[i].K;
+    output[i].X        = input[i].X;
+    output[i].tstart   = input[i].tstart;
+    output[i].tstop    = input[i].tstop;
+    output[i].c1       = input[i].c1;
+    output[i].c2       = input[i].c2;
+    output[i].photcode = input[i].photcode;
+    strcpy (output[i].label, input[i].label);
+    output[i].Nmeas = 0;
+    output[i].Ntime = 0;
+  }
+  return (output);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/delete.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/delete.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/delete.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "imregister.h"
+# include "photreg.h"
+
+void DeleteSubset (FITS_DB *db, PhotPars *photdata, int Nphotdata, int *match, int Nmatch) {
+
+  int i, j;
+  int *keep, Ndel, Nsubset;
+  PhotPars *subset;
+
+  ALLOCATE (keep, int, MAX (Nphotdata, 1));
+  for (i = 0; i < Nphotdata; i++) keep[i] = TRUE;
+  fprintf (stderr, "total of %d photdata\n", Nphotdata);
+
+  Ndel = 0;
+  for (i = 0; i < Nmatch; i++) {
+    j = match[i];
+    if (j == -1) continue;
+    keep[j] = FALSE;
+    Ndel ++;
+  }
+  fprintf (stderr, "delete %d photdata\n", Ndel);
+
+  if (Ndel == 0) { 
+    fprintf (stderr, "SUCCESS\n");
+    gfits_db_close (db);
+    gfits_db_free (db);
+    exit (0);
+  }
+
+  /* create new data list */
+  Nsubset = Nphotdata - Ndel;
+  ALLOCATE (subset, PhotPars, MAX (1, Nsubset));
+  fprintf (stderr, "keeping %d photdata\n", Nsubset);
+  for (j = i = 0; i < Nphotdata; i++) {
+    if (!keep[i]) continue;
+    subset[j] = photdata[i];
+    j++;
+  }
+
+  gfits_table_set_PhotPars (&db[0].ftable, subset, Nsubset);
+  gfits_db_save (db);
+  gfits_db_close (db);
+  gfits_db_free (db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/getImageData.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/getImageData.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/getImageData.c	(revision 22322)
@@ -0,0 +1,69 @@
+# include "imregister.h"
+# include "photreg.h"
+
+void getImageData (char *Image, char *ImageCCD, char *ImageMode) {
+
+  Header header;
+  char detector[64], filter[64], photcode[64];
+  int i, ccd, Nfilter;
+  
+  /* extract time & photcode from header */
+
+  /* load options from the image header */
+  if (!gfits_read_header (Image, &header)) {
+    if (output.verbose) fprintf (stderr, "ERR: trouble reading image header\n");
+    exit (1);
+  }
+
+  /* get time from image header */
+  ALLOCATE (criteria.tstart, time_t, 1);
+  ALLOCATE (criteria.tstart, time_t, 1);
+  criteria.tstart[0] = parse_time (&header);
+  criteria.tstop[0] = criteria.tstart[0] + 1;
+  criteria.Ntimes = 1;
+
+  /** determine photcode from header **/
+  /* get camera */
+  gfits_scan (&header, CameraKeyword, "%s", 1, detector);
+  for (i = 0; i < strlen(detector); i++) { detector[i] = toupper (detector[i]); }
+  for (i = 0; i < strlen(detector); i++) { if (isspace (detector[i])) detector[i] = '.'; }
+    
+  /* get filter */
+  Nfilter = FILTER_NONE;
+  gfits_scan (&header, FilterKeyword,   "%s", 1, filter);
+  for (i = 0; i < strlen (filter); i++) { if (isspace (filter[i])) filter[i] = '.'; }
+  for (i = 0; (i < NFILTER) && (Nfilter == FILTER_NONE); i++) {
+    if (!strcasecmp (filter, filtername[i])) {
+      Nfilter = filternum[i];
+    }
+  }      
+  if (Nfilter == FILTER_NONE) {
+    fprintf (stderr, "ERR: invalid filter %s\n", filter);
+    exit (1);
+  }
+  strcpy (filter, filtername[Nfilter]);
+
+  /* get ccd number */
+  if (!strcasecmp (ImageCCD, "phu")) {
+    ccd = 0;
+  } else {
+   if (!strcasecmp (ImageMode, "mef")) {
+     ccd = MatchCCDName (ImageCCD);
+   } else {
+     ccd = -1;
+     gfits_scan (&header, CCDnumKeyword,  "%d", 1, &ccd);
+   }  
+   if (ccd == -1) {
+     fprintf (stderr, "ERR: invalid ccd %s %s\n", ImageCCD, ImageMode);
+     exit (1);
+   }
+  }
+
+  sprintf (photcode, "%s.%s.%02d", detector, filter, ccd);
+  if (!(criteria.photcode = GetPhotcodeCodebyName (photcode))) {
+    fprintf (stderr, "ERR: photcode not found table\n");
+    exit (1);
+  }
+  criteria.PhotCodeSelect = TRUE;
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/match.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/match.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/match.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "imregister.h"
+# include "photreg.h"
+
+int *match_criteria (PhotPars *photdata, int Nphotdata, int *Nmatch) {
+
+  int i, j;
+  int N, NMATCH;
+  int *match;
+  int reject;
+
+  /* create selection index */
+  N = 0;
+  NMATCH = 1000;
+  ALLOCATE (match, int, NMATCH);
+
+  /* find entries that matches criteria */
+  for (i = 0; i < Nphotdata; i++) {
+    for (j = 0, reject = TRUE; reject && (j < criteria.Ntimes); j++) {
+      reject = (photdata[i].tstop < criteria.tstart[j]) || (photdata[i].tstart > criteria.tstop[j]);
+    }
+    if (criteria.Ntimes && reject) continue;
+    if (criteria.PhotCodeSelect && (photdata[i].photcode != criteria.photcode)) continue;
+    if (criteria.LabelSelect && strcmp (photdata[i].label, criteria.Label)) continue;
+
+    match[N] = i;
+    N ++;
+    if (N == NMATCH) {
+      NMATCH += 1000;
+      REALLOCATE (match, int, NMATCH);
+    }
+  }
+  *Nmatch = N;
+  return (match);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/output.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/output.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/photreg/output.c	(revision 22322)
@@ -0,0 +1,240 @@
+# include "imregister.h"
+# include "photreg.h"
+
+static char PhotError[] = "unknown";
+static char PhotNA[] = "unknown";
+
+/* given a subset list, write out the selected images, if desired */
+void OutputSubset (PhotPars *photdata, int Nphotdata, int *match, int Nmatch) {
+
+  if (output.table != (char *) NULL) {
+    DumpFitsTable (output.table, photdata, match, Nmatch);
+  } 
+
+  if (output.bintable != (char *) NULL) {
+    DumpFitsBintable (output.bintable, photdata, match, Nmatch);
+  } 
+
+  PrintSubset (photdata, match, Nmatch);
+  if (output.verbose) fprintf (stderr, "SUCCESS\n");
+
+  exit (0);
+}
+
+/* write out complete binary FITS table in format of db */
+void DumpFitsBintable (char *filename, PhotPars *photdata, int *match, int Nmatch) {
+
+  int i, j;
+  FILE *f;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  PhotPars *subset;
+
+  /* extract subset of input data */
+  ALLOCATE (subset, PhotPars, MAX (1, Nmatch));
+  for (i = 0; i < Nmatch; i++){
+    j = match[i];
+    memcpy (&subset[i], &photdata[j], sizeof (PhotPars));
+  }
+
+  /* open file for output */
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open output file %s\n", filename);
+    exit (1);
+  }
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+
+  ftable.header = &theader;
+  gfits_table_set_PhotPars (&ftable, subset, Nmatch);
+
+  /* EXTNAME is set to ZERO_POINTS_3.0 by default */
+  if (!strcmp (output.db, "trans")) {
+    gfits_modify (&theader, "EXTNAME", "%s", 1, "TRANS_POINTS_3.0");
+  }
+
+  gfits_write_header  (filename, &header);
+  gfits_write_matrix  (filename, &matrix);
+  gfits_write_Theader (filename, &theader);
+  gfits_write_table   (filename, &ftable);
+  fclose (f);
+  exit (0);
+}
+
+/** add ASCII table to autocode? **/
+void DumpFitsTable (char *filename, PhotPars *photdata, int *index, int Nkeep) {
+  
+  Header header, theader;
+  Matrix matrix;
+  FTable table;
+  PhotPars *newdata;
+  FILE *f;
+  char *startstr, *stopstr, *datestr, *line;
+  char *c1, *c2, *code, *photsys, *extname;
+  int i;
+  time_t tsecond;
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+  
+  /* create table header */
+  if (!strcmp (output.db, "phot")) {
+    extname = strcreate ("IMAGE_ZPTS");
+  } else {
+    extname = strcreate ("SUMMARY_ZPTS");
+  }
+  gfits_create_table_header (&theader, "TABLE", extname);
+    
+  /* add current date/time to header */
+  ohana_str_to_time ("now", &tsecond);
+  datestr = ohana_sec_to_date ((unsigned int) tsecond);
+  gfits_modify (&header,  "DATE", "%s", 1, datestr);
+  gfits_modify (&theader, "DATE", "%s", 1, datestr);
+  free (datestr);
+     
+  /* define table layout */
+  gfits_define_table_column (&theader, "F8.4", "ZP_OBS",       "measured zero point",       "mag");
+  gfits_define_table_column (&theader, "F8.4", "ZP_REF",       "nominal zero point",        "mag");
+  gfits_define_table_column (&theader, "F7.4", "ZP_ERR",       "error on zero point",       "mag");
+  gfits_define_table_column (&theader, "F7.3", "C_AIRMASS",    "airmass coeff",             "mag per airmass"); 
+  gfits_define_table_column (&theader, "F6.3", "C_COLOR",      "color coeff",               "mag per mag");
+  gfits_define_table_column (&theader, "A20",  "START_TIME",   "start time of measurement", "yyyy/mm/dd,hh:mm:ss");
+  gfits_define_table_column (&theader, "A20",  "STOP_TIME",    "stop time of measurement",  "yyyy/mm/dd,hh:mm:ss");
+  gfits_define_table_column (&theader, "A12",  "C1_NAME",      "filter 1 for color",        "");
+  gfits_define_table_column (&theader, "A12",  "C2_NAME",      "filter 2 for color",        "");
+  gfits_define_table_column (&theader, "I6",   "NSTARS",       "number of stars used",      "");
+  gfits_define_table_column (&theader, "I6",   "NTIMES",       "number of unique images",   "");
+  gfits_define_table_column (&theader, "A12",  "INT_PHOT_SYS", "internal photom system",    "");
+  gfits_define_table_column (&theader, "A12",  "REF_PHOT_SYS", "reference photom system",   "");
+  gfits_define_table_column (&theader, "A70",  "LABEL",        "data label",                "");
+  
+  /* define TNULL, TNVAL values */
+  gfits_modify (&theader, "TNULL1",  "%s", 1, "NaN");   /* ZP_OBS     */
+  gfits_modify (&theader, "TNULL2",  "%s", 1, "NaN");   /* ZP_REF     */
+  gfits_modify (&theader, "TNULL3",  "%s", 1, "NaN");   /* ZP_ERR     */
+  gfits_modify (&theader, "TNULL4",  "%s", 1, "NaN");   /* C_AIRMASS  */
+  gfits_modify (&theader, "TNULL5",  "%s", 1, "NaN");   /* C_COLOR    */
+  gfits_modify (&theader, "TNULL6",  "%s", 1, "NULL");  /* START_TIME */
+  gfits_modify (&theader, "TNULL7",  "%s", 1, "NULL");  /* STOP_TIME  */
+  gfits_modify (&theader, "TNULL8",  "%s", 1, "NULL");  /* C1_NAME    */
+  gfits_modify (&theader, "TNULL9",  "%s", 1, "NULL");  /* C2_NAME    */
+  gfits_modify (&theader, "TNULL10", "%s", 1, "NULL");  /* NSTARS     */
+  gfits_modify (&theader, "TNULL11", "%s", 1, "NULL");  /* NTIMES     */
+  gfits_modify (&theader, "TNULL12", "%s", 1, "NULL");  /* INT_PHOT_SYS */
+  gfits_modify (&theader, "TNULL13", "%s", 1, "NULL");  /* REF_PHOT_SYS */
+  gfits_modify (&theader, "TNULL14", "%s", 1, "NULL");  /* LABEL      */
+
+  gfits_modify (&theader, "TNVAL1",  "%s", 1, "Inf");   /* ZP_OBS       */
+  gfits_modify (&theader, "TNVAL2",  "%s", 1, "Inf");   /* ZP_REF       */
+  gfits_modify (&theader, "TNVAL3",  "%s", 1, "Inf");   /* ZP_ERR       */
+  gfits_modify (&theader, "TNVAL4",  "%s", 1, "Inf");   /* C_AIRMASS    */
+  gfits_modify (&theader, "TNVAL5",  "%s", 1, "Inf");   /* C_COLOR      */
+  gfits_modify (&theader, "TNVAL6",  "%s", 1, "NA");    /* START_TIME   */
+  gfits_modify (&theader, "TNVAL7",  "%s", 1, "NA");    /* STOP_TIME    */
+  gfits_modify (&theader, "TNVAL8",  "%s", 1, "NA");    /* C1_NAME      */
+  gfits_modify (&theader, "TNVAL9",  "%s", 1, "NA");    /* C2_NAME      */
+  gfits_modify (&theader, "TNVAL10", "%s", 1, "NA");    /* NSTARS       */
+  gfits_modify (&theader, "TNVAL11", "%s", 1, "NA");    /* NTIMES       */
+  gfits_modify (&theader, "TNVAL12", "%s", 1, "NA");    /* INT_PHOT_SYS */
+  gfits_modify (&theader, "TNVAL13", "%s", 1, "NA");    /* REF_PHOT_SYS */
+  gfits_modify (&theader, "TNVAL14", "%s", 1, "NA");    /* LABEL        */
+
+  /* create table, add data values */
+  gfits_create_table (&theader, &table);
+  
+  /* add data to table */
+  for (i = 0; i < Nkeep; i++) {
+    newdata = &photdata[index[i]];
+    startstr = ohana_sec_to_date (newdata[0].tstart);
+    stopstr = ohana_sec_to_date (newdata[0].tstop);
+    code    = GetPhotcodeNamebyCode (newdata[0].photcode);
+    photsys = GetPhotcodeNamebyCode (newdata[0].refcode);
+    c1      = GetPhotcodeNamebyCode (newdata[0].c1);
+    c2      = GetPhotcodeNamebyCode (newdata[0].c2);
+    if (code    == (char *) NULL) code    = PhotNA;
+    if (photsys == (char *) NULL) photsys = PhotNA;
+    if (c1      == (char *) NULL) c1      = PhotNA;
+    if (c2      == (char *) NULL) c2      = PhotNA;
+
+    line = gfits_table_print (&table, newdata[0].ZP, newdata[0].ZPo, newdata[0].dZP, 
+	      newdata[0].K, newdata[0].X, startstr, stopstr,
+	      c1, c2, newdata[0].Nmeas, newdata[0].Ntime, code, photsys, newdata[0].label);
+    if (!gfits_add_rows (&table, line, 1, strlen(line))) {
+      fprintf (stderr, "error writing dataline");
+      exit (1);
+    }
+
+    free (line);
+    free (startstr);
+    free (stopstr);
+  }
+ 
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "Failure writing fits table\n");
+    exit (1);
+  }
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &table);
+  fclose (f);
+  exit (0);
+}
+
+/* Select, TimeMode are global */
+int PrintSubset (PhotPars *photdata, int *match, int Nmatch) {
+  
+  int i, j;
+  char *photstr, *timestr, *c1, *c2, *refcode;
+  
+  /* print the selected entries */
+  for (j = 0; j < Nmatch; j++) {
+    
+    i = match[j];
+    
+    /* convert UNIX time to Elixir-style date string */
+    timestr = ohana_sec_to_date (photdata[i].tstart);
+    
+    if (output.photcodenames) {
+      /* convert photcode to filter name */
+      photstr = GetPhotcodeNamebyCode (photdata[i].photcode);
+      refcode = GetPhotcodeNamebyCode (photdata[i].refcode);
+      c1      = GetPhotcodeNamebyCode (photdata[i].c1);
+      c2      = GetPhotcodeNamebyCode (photdata[i].c2);
+
+      if (photstr == NULL) photstr = PhotError;
+      if (refcode == NULL) refcode = PhotError;
+      if (c1      == NULL) c1      = PhotError;
+      if (c2      == NULL) c2      = PhotError;
+      
+      fprintf (stdout, "%s %s  %7.4f %7.4f %7.4f  %3d %3d  %7.4f %7.4f  %s  %s %s %s\n", 
+	       photstr, timestr, photdata[i].ZP, photdata[i].ZPo, photdata[i].dZP, photdata[i].Nmeas, photdata[i].Ntime, 
+	       photdata[i].X, photdata[i].K, refcode, c1, c2, photdata[i].label); 
+    } else {
+      fprintf (stdout, "%4d %s  %7.4f %7.4f %7.4f  %3d %3d  %7.4f %7.4f  %4d %4d %4d  %s\n", 
+	       photdata[i].photcode, timestr, photdata[i].ZP, photdata[i].ZPo, photdata[i].dZP, photdata[i].Nmeas, 
+	       photdata[i].Ntime, photdata[i].X, photdata[i].K, 
+	       photdata[i].refcode, photdata[i].c1, photdata[i].c2, photdata[i].label); 
+    }
+      
+    free (timestr);
+  }
+  return (TRUE);
+}
+
+/**** the pre-autocode implementation of this program failed to byte-swap refcode!
+      any tables from before the new implementation have to be treated carefully
+****/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/ConfigInit.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "imregister.h"
+# include "spreg.h"
+
+int success;
+
+void ConfigInitSpec (int *argc, char **argv) {
+
+  char *config, *file;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+
+  success = TRUE;
+
+  WarnConfig (config, "SPECTRUM_DATABASE",       "%s", 0, SpectrumDB);
+
+  /* keyword abstractions for parse_time */	   
+  WarnConfig (config, "DATE-KEYWORD",                "%s", 0, DateKeyword);
+  WarnConfig (config, "DATE-MODE",                   "%s", 0, DateMode);
+  WarnConfig (config, "UT-KEYWORD",                  "%s", 0, UTKeyword);
+  WarnConfig (config, "MJD-KEYWORD",                 "%s", 0, MJDKeyword);
+  WarnConfig (config, "JD-KEYWORD",                  "%s", 0, JDKeyword);
+						   
+  /* keyword abstractions for spinfo */		   
+  WarnConfig (config, "EXPTIME-KEYWORD",             "%s", 0, ExptimeKeyword);
+  WarnConfig (config, "AIRMASS-KEYWORD",             "%s", 0, AirmassKeyword);
+  WarnConfig (config, "CAMERA-KEYWORD",              "%s", 0, CameraKeyword);
+  WarnConfig (config, "OBJECT-KEYWORD",              "%s", 0, ObjectKeyword);
+  WarnConfig (config, "TELESCOPE-KEYWORD",           "%s", 0, TelescopeKeyword);
+
+  /* semi-optional values */
+  ScanConfig (config, "RA-DDD-KEYWORD",              "%s", 0, RADecDegKeyword);
+  ScanConfig (config, "DEC-DDD-KEYWORD",             "%s", 0, DECDecDegKeyword);
+  ScanConfig (config, "RA-HMS-KEYWORD",              "%s", 0, RASexigKeyword);
+  ScanConfig (config, "DEC-DMS-KEYWORD",             "%s", 0, DECSexigKeyword);
+  if (!RADecDegKeyword[0] & !DECDecDegKeyword[0] && !RASexigKeyword[0] && !DECSexigKeyword[0]) {
+    fprintf (stderr, "missing astrometry configuration information\n");
+    success = FALSE;
+  }
+
+  if (! success) {
+    fprintf (stderr, "ERROR: problem with elixir configuration\n");
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/args.spregister.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/args.spregister.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/args.spregister.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "imregister.h"
+# include "spreg.h"
+
+int args (int argc, char **argv) {
+
+  int N;
+
+  ConfigInitSpec (&argc, argv); /* load elixir config data */
+
+  NoReg = FALSE;
+  if ((N = get_argument (argc, argv, "-noreg"))) {
+    remove_argument (N, &argc, argv);
+    NoReg = TRUE;
+  }
+
+  DUMP = FALSE;
+  if ((N = get_argument (argc, argv, "-dump"))) {
+    remove_argument (N, &argc, argv);
+    DUMP = TRUE;
+  }
+
+  output.verbose = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    output.verbose = TRUE;
+  }
+
+  NeedType = FALSE;
+  if ((N = get_argument (argc, argv, "-needtype"))) {
+    remove_argument (N, &argc, argv);
+    NeedType = TRUE;
+  }
+
+  /* all imregister programs are implicitly modifying the db */
+  output.modify = TRUE;
+  IMSORT = FALSE;
+
+  if (argc != 2) {
+    fprintf (stderr, "ERROR: Usage: spregister (filename) [-noreg]\n");
+    exit (1);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/args.spsearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/args.spsearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/args.spsearch.c	(revision 22322)
@@ -0,0 +1,176 @@
+# include "imregister.h" 
+# include "spreg.h"
+
+/* criteria struct is global */
+int args (int argc, char **argv) {
+
+  double dt;
+  int N;
+
+  ConfigInitSpec (&argc, argv); /* load elixir config data */
+
+  /* set timezone (set static in misc.c) */
+  if ((N = get_argument (argc, argv, "-tz"))) {
+    remove_argument (N, &argc, argv);
+    dt = atof (argv[N]);
+    set_timezone (dt);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* define time range */
+  criteria.Ntimes = 0;
+  if (!get_trange_arguments (&argc, argv, &criteria.tstart, &criteria.tstop, &criteria.Ntimes)) {
+    fprintf (stderr, "ERROR: syntax error\n");
+    exit (1);
+  }
+
+  /* exposure time */
+  criteria.ExptimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-etime"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Exptime = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.ExptimeSelect = TRUE;
+  }
+ 
+  /* image type (dark, flat, bias, etc) */
+  criteria.StateSelect = FALSE;
+
+ 
+  /* image mode (split, mef, single) */
+  criteria.ModeSelect = FALSE;
+  /*
+  criteria.Mode = M_UNDEF;
+  if ((N = get_argument (argc, argv, "-mode"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Mode = get_image_mode (argv[N]);
+    if (criteria.Mode == M_UNDEF) {
+      fprintf (stderr, "ERROR: invalid image mode %s\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    criteria.ModeSelect = TRUE;
+  }
+  */
+
+  /* string in image name */
+  criteria.FilenameSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filename"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Filename = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.FilenameSelect = TRUE;
+  }
+
+  /* string in image name */
+  criteria.ObjectSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-object"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Object = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.ObjectSelect = TRUE;
+  }
+
+  /* string in image name */
+  criteria.TelescopeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-tel"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Telescope = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.TelescopeSelect = TRUE;
+  }
+
+  /* string in image name */
+  criteria.InstrumentSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-inst"))) {
+    remove_argument (N, &argc, argv);
+    criteria.Instrument = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    criteria.InstrumentSelect = TRUE;
+  }
+
+  /*** command-line options which modify the output list */
+  if ((N = get_argument (argc, argv, "-treg"))) {
+    remove_argument (N, &argc, argv);
+    SetOutputMode ("RegTimeMode");
+  }
+  output.table = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-table"))) {
+    remove_argument (N, &argc, argv);
+    output.table = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  output.bintable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-bintable"))) {
+    remove_argument (N, &argc, argv);
+    output.bintable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /*** command-line options which modify behavior (delete, modify, newpath, mef2split split2mef */
+  output.delete = FALSE;
+  output.modify = FALSE;
+  output.modify_path = FALSE; 
+  output.unique = FALSE;
+
+  if ((N = get_argument (argc, argv, "-delete"))) {
+    remove_argument (N, &argc, argv);
+    output.delete = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "-unique"))) {
+    remove_argument (N, &argc, argv);
+    output.unique = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "-modify"))) {
+    if (output.delete) { 
+      fprintf (stderr, "can't specify more than one modifier at a time\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    output.modify = TRUE;
+    
+    /* modify part of the path */
+    if (!strcasecmp (argv[N], "path")) {
+      output.modify_path = TRUE;
+      remove_argument (N, &argc, argv);
+      output.oldpath = strcreate (argv[N]);
+      remove_argument (N, &argc, argv);
+      output.newpath = strcreate (argv[N]);
+      remove_argument (N, &argc, argv);
+      goto valid_modify;
+    }
+
+    /* modify distributed status */
+    if (!strcasecmp (argv[N], "mode")) {
+      output.modify_mode = TRUE;
+      remove_argument (N, &argc, argv);
+      output.mode = atoi(argv[N]);
+      remove_argument (N, &argc, argv);
+      goto valid_modify;
+    }
+
+    if (!strcasecmp (argv[N], "help")) {
+      fprintf (stderr, "-modify option: \n");
+      fprintf (stderr, "  -modify path (oldpath) (newpath)\n"); 
+      fprintf (stderr, "  -modify mode (mef | split)\n"); 
+      exit (2);
+    }
+
+    fprintf (stderr, "invalid -modify option, try -modify help\n");
+    exit (1);
+  }
+ valid_modify:
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: imsearch [config ops] \n");
+    fprintf (stderr, "  [-type type] [-mode mode] [-trange start range] [-ccd N]\n");
+    fprintf (stderr, "  [-etime exptime] [-filter name] [-name string] [-proc t/f]\n");
+    fprintf (stderr, "  [-treg] [-seq] [-pt] [-table] [-cadctable] [-bintable]\n");
+    fprintf (stderr, "  [-delete] [-modify (options)]\n");
+    exit (1);
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/delete.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/delete.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/delete.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "imregister.h"
+# include "spreg.h"
+
+void DeleteSubset (FITS_DB *db, Spectrum *spectrum, int Nspectrum, int *match, int Nmatch) {
+
+  int i, j;
+  int *keep, Nbad, Nsubset;
+  Spectrum *subset;
+
+  ALLOCATE (keep, int, MAX (Nspectrum, 1));
+  for (i = 0; i < Nspectrum; i++) keep[i] = TRUE;
+  fprintf (stderr, "total of %d spectra\n", Nspectrum);
+
+  Nbad = 0;
+  for (i = 0; i < Nmatch; i++) {
+    j = match[i];
+    if (j == -1) continue;
+    keep[j] = FALSE;
+    Nbad ++;
+  }
+  fprintf (stderr, "delete %d spectra\n", Nbad);
+  if (Nbad == 0) { 
+    fprintf (stderr, "SUCCESS\n");
+    gfits_db_close (db);
+    exit (0);
+  }
+
+  Nsubset = Nspectrum - Nbad;
+  ALLOCATE (subset, Spectrum, MAX (1, Nsubset));
+  fprintf (stderr, "keeping %d spectra\n", Nsubset);
+  for (j = i = 0; i < Nspectrum; i++) {
+    if (!keep[i]) continue;
+    subset[j] = spectrum[i];
+    j++;
+  }
+  free (keep);
+  free (spectrum);
+
+  /** we may later want to pull this out and put it elsewhere **/
+  /** free db[0].theader, db[0].table.buffer? **/
+  gfits_table_set_Spectrum (&db[0].ftable, subset, Nsubset);
+  gfits_db_save (db);
+  gfits_db_close (db);
+  gfits_db_free (db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/match.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/match.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/match.c	(revision 22322)
@@ -0,0 +1,87 @@
+# include "imregister.h"
+# include "spreg.h"
+
+int *match_criteria (Spectrum *spectrum, int Nspectrum, int *Nmatch) {
+
+  int i, j;
+  int N, NMATCH;
+  int *match;
+  int reject;
+  int Nfilename, Nobject, Ntelescope, Ninstrument;
+
+  /* create selection index */
+  N = 0;
+  NMATCH = 1000;
+  ALLOCATE (match, int, NMATCH);
+
+  Nfilename = Nobject = Ntelescope = Ninstrument = 0;
+
+  if (criteria.FilenameSelect)   Nfilename   = strlen (criteria.Filename);
+  if (criteria.ObjectSelect)     Nobject     = strlen (criteria.Object);
+  if (criteria.TelescopeSelect)  Ntelescope  = strlen (criteria.Telescope);
+  if (criteria.InstrumentSelect) Ninstrument = strlen (criteria.Instrument);
+
+  /* find entries that matches criteria */
+  for (i = 0; i < Nspectrum; i++) {
+    for (j = 0, reject = TRUE; reject && (j < criteria.Ntimes); j++) {
+      reject = (spectrum[i].obstime + spectrum[i].exptime < criteria.tstart[j]) || (spectrum[i].obstime > criteria.tstop[j]);
+    }
+    if (criteria.Ntimes && reject) continue;
+    if (criteria.ModeSelect    && (spectrum[i].mode  != criteria.Mode)) continue;
+    if (criteria.StateSelect   && (spectrum[i].state != criteria.State)) continue;
+
+    if (criteria.ExptimeSelect && (fabs (spectrum[i].exptime - criteria.Exptime) > 5.0)) continue;
+
+    if (criteria.FilenameSelect   && (strncasecmp (spectrum[i].filename, criteria.Filename, Nfilename))) continue;
+    if (criteria.ObjectSelect     && (strncasecmp (spectrum[i].objname, criteria.Object, Nobject))) continue;
+    if (criteria.TelescopeSelect  && (strncasecmp (spectrum[i].telescope, criteria.Telescope, Ntelescope))) continue;
+    if (criteria.InstrumentSelect && (strncasecmp (spectrum[i].instrument, criteria.Instrument, Ninstrument))) continue;
+
+    match[N] = i;
+    N ++;
+    if (N == NMATCH) {
+      NMATCH += 1000;
+      REALLOCATE (match, int, NMATCH);
+    }
+  }
+  *Nmatch = N;
+  return (match);
+}
+
+# if (0)
+
+int *match_spectra (Spectrum *subset, int Nsubset, int *Nmatch) {
+  
+  int i, j, N, Nspectrum, Nfound;
+  int *match;
+  Spectrum *spectrum;
+
+  spectrum = get_spectra (&Nspectrum);
+
+  /* find matching spectra - very inefficient : sort by obstime, find those first? */
+  ALLOCATE (match, int, Nsubset);
+  for (j = 0; j < Nsubset; j++) {
+    match[j] = -1;
+    for (i = 0; (match[j] == -1) && (i < Nspectrum); i++) {
+      if (spectrum[i].obstime > subset[j].obstime + 1) continue;
+      if (spectrum[i].obstime < subset[j].obstime - 1) continue;
+      match[j] = i;
+    }
+  }
+  
+  Nfound = 0;
+  for (i = 0; i < Nsubset; i++) {
+    /* set the new values for this spectrum */
+    N = match[i];
+    if (N == -1) continue;
+    spectrum[N].ra   = subset[i].ra; 
+    spectrum[N].dec  = subset[i].dec; 
+    /* if the spectrum is MEF, these were not correctly assigned by imsort.
+       this step uses the values from the split ccd spectrum */
+    Nfound ++;
+  }
+  *Nmatch = Nfound;
+  return (match);
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/modify.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/modify.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/modify.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "imregister.h"
+# include "spreg.h"
+
+void ModifySubset (FITS_DB *db, Spectrum *spectrum, int Nspectrum, int *match, int Nmatch) {
+
+  int i, j, Nold;
+  char *tmppath;
+
+  Nold = 0;
+  tmppath = NULL;
+
+  /* create some necessary variables */
+  if (output.modify_path) { 
+    Nold = strlen (output.oldpath);
+    ALLOCATE (tmppath, char, 128);
+  }
+
+  /* modify the selected entries */
+  for (j = 0; j < Nmatch; j++) {
+
+    i = match[j];
+
+    if (output.modify_path) {
+      if (!strncmp (spectrum[i].pathname, output.oldpath, Nold)) {
+	strcpy (tmppath, &spectrum[i].pathname[Nold]);
+	snprintf (spectrum[i].pathname, 128, "%s%s", output.newpath, tmppath);
+      }
+    }
+
+    if (output.modify_mode) {
+      spectrum[i].mode = output.mode;
+    }
+
+    if (output.modify_state) {
+      spectrum[i].state = output.state;
+    }
+  }
+
+  /** we may later want to pull this out and put it elsewhere **/
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, match, Nmatch);
+  for (i = 0; i < Nmatch; i++) {
+    gfits_convert_Spectrum ((Spectrum *) db[0].vtable.buffer[i], sizeof (Spectrum), 1);
+  }
+  gfits_db_update (db);
+  gfits_db_close (db);
+  gfits_db_free (db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/output.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/output.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/output.c	(revision 22322)
@@ -0,0 +1,174 @@
+# include "imregister.h"
+# include "spreg.h"
+
+static int RegTimeMode = FALSE;
+
+void SetOutputMode (char *mode) {
+
+  if (!strcmp (mode, "RegTimeMode")) {
+    RegTimeMode = TRUE;
+    return;
+  }
+  return;
+}
+
+/* given a subset list, write out the selected spectra, if desired */
+void OutputSubset (Spectrum *spectrum, int Nspectra, int *match, int Nmatch) {
+
+  if (output.table != (char *) NULL) {
+    DumpFitsTable (output.table, spectrum, match, Nmatch);
+  } 
+
+  if (output.bintable != (char *) NULL) {
+    DumpFitsBintable (output.bintable, spectrum, match, Nmatch);
+  } 
+
+  PrintSubset (spectrum, match, Nmatch);
+  exit (0);
+}
+
+/* write out complete binary FITS table in format of db */
+void DumpFitsBintable (char *filename, Spectrum *spectrum, int *match, int Nmatch) {
+
+  int i, j;
+  FILE *f;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  Spectrum *subset;
+
+  /* extract subset list to single array */
+  ALLOCATE (subset, Spectrum, MAX (1, Nmatch));
+  for (i = 0; i < Nmatch; i++){
+    j = match[i];
+    memcpy (&subset[i], &spectrum[j], sizeof (Spectrum));
+  }
+
+  /* open file for output */
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open output file %s\n", filename);
+    exit (1);
+  }
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+
+  ftable.header = &theader;
+  gfits_table_set_Spectrum (&ftable, subset, Nmatch);
+
+  gfits_fwrite_header   (f, &header);
+  gfits_fwrite_matrix   (f, &matrix);
+  gfits_fwrite_Theader  (f, &theader);
+  gfits_fwrite_table   (f, &ftable);
+  fclose (f);
+  exit (0);
+}
+
+/* write out an ASCII table */
+void DumpFitsTable (char *filename, Spectrum *spectrum, int *match, int Nmatch) {
+  
+  int i;
+  char *obsstr, *regstr, *line, *datestr;
+  time_t tsecond;
+  FILE *f;
+  Header header, theader;
+  Matrix matrix;
+  FTable ftable;
+  Spectrum *subset;
+
+  /* create primary header */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 1);
+  
+  /* the ASCII table is always a little harder than the binary:
+   * we need to build the data line a bit carefully 
+   */
+
+  /* create an empty table which we will fill in by hand */
+  ftable.header = &theader;
+  gfits_table_set_SpectrumASCII (&ftable, NULL, 0);
+  
+  /* add data to table */
+  for (i = 0; i < Nmatch; i++) {
+    subset = &spectrum[match[i]];
+    obsstr = ohana_sec_to_date (subset[0].obstime);
+    regstr = ohana_sec_to_date (subset[0].regtime);
+
+    /* we should get an error here if we don't construct this line correctly */
+    line = gfits_table_print (&ftable, subset[0].filename, subset[0].pathname, subset[0].instrument, 
+			     subset[0].telescope, subset[0].objname, subset[0].extname, 
+			     subset[0].ra, subset[0].dec, subset[0].exptime, subset[0].airmass, 
+			     subset[0].Ws, subset[0].We, subset[0].dW, 
+			     subset[0].Nspec, obsstr, regstr,
+			     subset[0].mode, subset[0].state, subset[0].flag);
+
+    gfits_add_rows (&ftable, line, 1, strlen(line));
+    free (line);
+    free (obsstr);
+    free (regstr);
+  }
+
+  /* add current date/time to header */
+  ohana_str_to_time ("now", &tsecond);
+  datestr = ohana_sec_to_date (tsecond);
+  gfits_modify (&header,  "DATE", "%s", 1, datestr);
+  gfits_modify (&theader, "DATE", "%s", 1, datestr);
+
+  /* open file for output */
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open output file %s\n", filename);
+    exit (1);
+  }
+
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &ftable);
+  fclose (f);
+
+  exit (0);
+}
+
+/* Select, TimeMode are global */
+int PrintSubset (Spectrum *spectrum, int *match, int Nmatch) {
+  
+  int i, j;
+  char *timestr;
+
+  /* print the selected entries */
+  for (j = 0; j < Nmatch; j++) {
+    i = match[j];
+    
+    timestr = RegTimeMode ? ohana_sec_to_date (spectrum[i].regtime) : ohana_sec_to_date (spectrum[i].obstime);
+
+    /* predefined subset of values */
+    fprintf (stdout, "%5d %20s %s %s %s %s ", i, timestr, spectrum[i].pathname, spectrum[i].filename, spectrum[i].objname, spectrum[i].telescope);
+    fprintf (stdout, "%5.1f %5.3f %.1f %.1f %.2f\n", spectrum[i].exptime, spectrum[i].airmass, spectrum[i].Ws, spectrum[i].We, spectrum[i].dW);
+
+    free (timestr);
+  }
+  return (TRUE);
+}
+
+int dump_data (Spectrum *spectrum, int Nspectrum) {
+
+  int i;
+
+  for (i = 0; i < Nspectrum; i++) {
+    fprintf (stdout, "%s %s %f %f %f %f\n", spectrum[i].filename, spectrum[i].objname, spectrum[i].exptime, spectrum[i].airmass, spectrum[i].Ws, spectrum[i].We);
+  }
+
+  fprintf (stdout, "SUCCESS\n");
+  exit (0);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/showinfo.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/showinfo.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/showinfo.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "imregister.h"
+# include "spreg.h"
+
+void showinfo (Spectrum *spec) {
+
+  char *obstime, *regtime;
+
+  fprintf (stderr, "\n");
+  fprintf (stderr, "filename: %s\n",   spec[0].filename);
+  fprintf (stderr, "pathname: %s\n",   spec[0].pathname);
+  fprintf (stderr, "extname:  %s\n\n",   spec[0].extname);
+
+  fprintf (stderr, "instrument: %s\n\n", spec[0].instrument);
+  fprintf (stderr, "telescope: %s\n\n", spec[0].telescope);
+
+
+  fprintf (stderr, "mode: %d, state: %d, flag: %x\n\n", spec[0].mode, spec[0].state, spec[0].flag);
+
+  fprintf (stderr, "exptime: %f, airmass: %f\n", spec[0].exptime, spec[0].airmass);
+  fprintf (stderr, "ra: %f, dec: %f\n", spec[0].ra, spec[0].dec);
+  fprintf (stderr, "objname: %s\n\n", spec[0].objname);
+
+  fprintf (stderr, "Ws: %f, We: %f, dW: %f\n", spec[0].Ws, spec[0].We, spec[0].dW);
+  fprintf (stderr, "Nspec: %d\n", spec[0].Nspec);
+
+  obstime = ohana_sec_to_date (spec[0].obstime);
+  regtime = ohana_sec_to_date (spec[0].regtime);
+
+  fprintf (stderr, "obstime: %s\n", obstime);
+  fprintf (stderr, "regtime: %s\n", regtime);
+
+  exit (0);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/spinfo.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/spinfo.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/spinfo.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "imregister.h"
+# include "spreg.h"
+
+Spectrum *spinfo (char *filename) {
+
+  Spectrum *spectrum;
+  Header header;
+  char line[80];
+  struct timeval now;
+  char *name, *cwd, *tempname;
+  double tmp;
+
+  ALLOCATE (spectrum, Spectrum, 1);
+  bzero (spectrum, sizeof (Spectrum));
+
+  /* load in FITS header from image */
+  if (!gfits_read_header (filename, &header)) {
+    fprintf (stderr, "ERROR: can't find image file %s\n", filename);
+    exit (1);
+  }
+
+  /* get filename */
+  name = filebasename (filename);
+  snprintf (spectrum[0].filename, 32, "%s", name);
+  free (name);
+
+  /* get pathname (add cwd if not absolute) */
+  name = pathname (filename);
+  if (name[0] != '/') {
+    cwd = getcwd (NULL, 64 - strlen(name) - 2);
+    if (cwd == (char *) NULL) {
+      cwd = strcreate ("longpath");
+      /* pathname is limited to 64 chars.  if it is toolong, 
+	 we put in the word 'longpath'.  no other useful solution. */
+    }
+    ALLOCATE (tempname, char, strlen (cwd) + strlen (name) + 2);
+    if (!strcmp (name, ".")) {
+      sprintf (tempname, "%s", cwd);
+    } else {
+      sprintf (tempname, "%s/%s", cwd, name);
+    }      
+    free (name);
+    free (cwd);
+    name = tempname;
+  }    
+  snprintf (spectrum[0].pathname, 64, "%s", name);
+  free (name);
+
+  /* find the important header keyword values */
+  spectrum[0].obstime = parse_time (&header);
+
+  gettimeofday (&now, (void *) NULL);
+  spectrum[0].regtime = now.tv_sec;
+
+  warn_scan_nchar (&header, 16, ObjectKeyword,    1, spectrum[0].objname);
+  warn_scan_nchar (&header, 16, CameraKeyword,    1, spectrum[0].instrument);
+  warn_scan_nchar (&header, 16, TelescopeKeyword, 1, spectrum[0].telescope);
+
+  clean_spaces (spectrum[0].objname);
+  clean_spaces (spectrum[0].instrument);
+  clean_spaces (spectrum[0].telescope);
+
+  warn_scan (&header, ExptimeKeyword,    "%f", 1, &spectrum[0].exptime);
+  warn_scan (&header, AirmassKeyword,    "%f", 1, &spectrum[0].airmass);
+
+  /* grab coordinates from header */
+  if (RADecDegKeyword[0] & DECDecDegKeyword[0]) {
+    /* expect RA & DEC in decimal degrees */
+    warn_scan (&header, RADecDegKeyword,  "%f", 1, &spectrum[0].ra);
+    warn_scan (&header, DECDecDegKeyword, "%f", 1, &spectrum[0].dec);
+  } else {
+    if (RASexigKeyword[0] & DECSexigKeyword[0]) {
+      /* expect RA & DEC in hh:mm:ss, dd:mm:ss */
+      warn_scan (&header, RASexigKeyword,  "%s", 1, line);
+      ohana_dms_to_ddd (&tmp, line);
+      spectrum[0].ra = 15*tmp;
+      warn_scan (&header, DECSexigKeyword, "%s", 1, &line);
+      ohana_dms_to_ddd (&tmp, line);
+      spectrum[0].dec = tmp;
+    }
+  }
+
+  /* for now we will not try to determine the mode, state, or 
+     wavelength ranges from the header.  set these with -modify
+     or on the command line on insert.  later on, we can add the logic */
+  
+  spectrum[0].mode  = SPMODE_UKN;
+  spectrum[0].state = SPSTATE_UKN;
+  spectrum[0].Nspec = 1;
+  spectrum[0].flag  = 0;
+  
+  { 
+    int Nx;
+    float crpix, crval, cd11;
+
+    warn_scan (&header, "CRPIX1", "%f", 1, &crpix);
+    warn_scan (&header, "CRVAL1", "%f", 1, &crval);
+    if (!gfits_scan (&header, "CDELT1", "%f", 1, &cd11))
+      warn_scan (&header, "CD1_1", "%f", 1, &cd11);
+    
+    gfits_scan (&header, "NAXIS1", "%d", 1, &Nx);
+
+    spectrum[0].Ws = (1  - crpix)*cd11 + crval;
+    spectrum[0].We = (Nx - crpix)*cd11 + crval;
+    spectrum[0].dW = cd11;
+  }
+  
+  strcpy (spectrum[0].extname, "PHU");
+  
+  return (spectrum);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/unique.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/unique.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/spreg/unique.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "imregister.h"
+# include "spreg.h"
+
+void sortstr (char **S, int *X, int N) {
+
+# define SWAPFUNC(A,B){ \
+  char *tmp = S[A]; S[A] = S[B]; S[B] = tmp; \
+  int  itmp = X[A]; X[A] = X[B]; X[B] = itmp; \
+}
+# define COMPARE(A,B)(strcmp(S[A], S[B]) < 0)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+/* input is a subset index of spectrum list, output is a new subset */
+int *unique_entries (Spectrum *spectrum, int Nspectrum, int *subset, int *Nmatch) {
+
+  int i, j, m, Nsubset;
+  int N, NMATCH;
+  int *match, *entry;
+  char idxline[128];
+  char **index;
+
+  if (!output.unique) return (subset);
+
+  /* create output index */
+  N = 0;
+  NMATCH = 1000;
+  ALLOCATE (match, int, NMATCH);
+
+  Nsubset = *Nmatch;
+
+  /* index = filename */
+  ALLOCATE (index, char *, Nsubset);
+  ALLOCATE (entry, int, Nsubset);
+  for (i = 0; i < Nsubset; i++) {
+    sprintf (idxline, "%s", spectrum[subset[i]].filename);
+    index[i] = strcreate (idxline);
+    entry[i] = subset[i];
+  }
+  sortstr (index, entry, Nsubset);
+
+  /* find unique sequences */
+  for (i = 0; i < Nsubset; ) {
+    for (j = i + 1; (j < Nsubset) && (!strcmp (index[i], index[j])); j++);
+    m = i;
+    
+    /* add unique entry to output list */
+    match[N] = entry[m];
+    N ++;
+    if (N == NMATCH) {
+      NMATCH += 1000;
+      REALLOCATE (match, int, NMATCH);
+    }
+
+    /* j always points to the next entry */
+    i = j;
+  }
+
+  for (i = 0; i < Nsubset; i++) free (index[i]);
+  free (index);
+  free (entry);
+  free (subset);
+
+  *Nmatch = N;
+  return (match);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/cameraconfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/cameraconfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/cameraconfig.c	(revision 22322)
@@ -0,0 +1,262 @@
+# include "imregister.h"
+static char *version = "cameraconfig $Revision: 1.3 $";
+
+void usage ();
+
+int main (int argc, char **argv) {
+
+  int i, Nccd, Nx, Ny, mosaic_x, mosaic_y, N, use_biassec;
+  int dx, dy;
+  char *config, *file;
+  char CameraConfig[256];
+  char field[64], line[256], keyword[64];
+  char ID[64], *IDsel;
+  double x, y, Xo, Yo, theta;
+  int Choice, GetID, SEQ, GetN, Nsel;
+  int NCCD, AXES, CCDS, CCDN, XOFF, YOFF, XFLIP, YFLIP, XO, YO, THETA;
+  int AXIS0, AXIS1, MOSAIC_X, MOSAIC_Y, DATASEC, BIASSEC, USE_BIASSEC;
+  char datasec[64], biassec[64];
+
+  bzero (ID, 64);
+  get_version (argc, argv, version);
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (&argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (!ScanConfig (config, "CAMERA_CONFIG", "%s", 0, CameraConfig)) {
+    fprintf (stderr, "ERROR: can't find CAMERA_CONFIG in configuration file\n");
+    exit (1);
+  }
+  free (config);
+  free (file);
+
+  /* load camera config file */
+  config = LoadConfigFile (CameraConfig);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find camera config file %s\n", CameraConfig);
+    exit (1);
+  }
+
+  if (argc == 1) { 
+    fprintf (stderr, "USAGE: cameraconfig [-options]\n");
+    fprintf (stderr, "using %s for config information\n", CameraConfig);
+    exit (1);
+  }
+
+  /* command line options */
+  if ((N = get_argument (argc, argv, "-h"))) usage ();
+
+  /* command line options */
+  NCCD = FALSE;
+  if ((N = get_argument (argc, argv, "-Nccd"))) {
+    remove_argument (N, &argc, argv);
+    NCCD = TRUE;
+  }
+
+  AXES = FALSE;
+  if ((N = get_argument (argc, argv, "-axes"))) {
+    remove_argument (N, &argc, argv);
+    AXES = TRUE;
+  }
+
+  AXIS0 = FALSE;
+  if ((N = get_argument (argc, argv, "-axis0"))) {
+    remove_argument (N, &argc, argv);
+    AXIS0 = TRUE;
+  }
+
+  AXIS1 = FALSE;
+  if ((N = get_argument (argc, argv, "-axis1"))) {
+    remove_argument (N, &argc, argv);
+    AXIS1 = TRUE;
+  }
+
+  MOSAIC_X = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaicx"))) {
+    remove_argument (N, &argc, argv);
+    MOSAIC_X = TRUE;
+  }
+  MOSAIC_Y = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaicy"))) {
+    remove_argument (N, &argc, argv);
+    MOSAIC_Y = TRUE;
+  }
+
+  CCDS = FALSE;
+  if ((N = get_argument (argc, argv, "-ccds"))) {
+    remove_argument (N, &argc, argv);
+    CCDS = TRUE;
+  }
+
+  CCDN = FALSE;
+  if ((N = get_argument (argc, argv, "-ccdn"))) {
+    remove_argument (N, &argc, argv);
+    CCDN = TRUE;
+  }
+
+  SEQ = FALSE;
+  if ((N = get_argument (argc, argv, "-seq"))) {
+    remove_argument (N, &argc, argv);
+    SEQ = TRUE;
+  }
+
+  XOFF = FALSE;
+  if ((N = get_argument (argc, argv, "-xoff"))) {
+    remove_argument (N, &argc, argv);
+    XOFF = TRUE;
+  }
+
+  YOFF = FALSE;
+  if ((N = get_argument (argc, argv, "-yoff"))) {
+    remove_argument (N, &argc, argv);
+    YOFF = TRUE;
+  }
+
+  XO = FALSE;
+  if ((N = get_argument (argc, argv, "-Xo"))) {
+    remove_argument (N, &argc, argv);
+    XO = TRUE;
+  }
+
+  YO = FALSE;
+  if ((N = get_argument (argc, argv, "-Yo"))) {
+    remove_argument (N, &argc, argv);
+    YO = TRUE;
+  }
+
+  THETA = FALSE;
+  if ((N = get_argument (argc, argv, "-theta"))) {
+    remove_argument (N, &argc, argv);
+    THETA = TRUE;
+  }
+
+  XFLIP = FALSE;
+  if ((N = get_argument (argc, argv, "-xflip"))) {
+    remove_argument (N, &argc, argv);
+    XFLIP = TRUE;
+  }
+
+  YFLIP = FALSE;
+  if ((N = get_argument (argc, argv, "-yflip"))) {
+    remove_argument (N, &argc, argv);
+    YFLIP = TRUE;
+  }
+
+  DATASEC = FALSE;
+  if ((N = get_argument (argc, argv, "-datasec"))) {
+    remove_argument (N, &argc, argv);
+    DATASEC = TRUE;
+  }
+  BIASSEC = FALSE;
+  if ((N = get_argument (argc, argv, "-biassec"))) {
+    remove_argument (N, &argc, argv);
+    BIASSEC = TRUE;
+  }
+  USE_BIASSEC = FALSE;
+  if ((N = get_argument (argc, argv, "-usebiassec"))) {
+    remove_argument (N, &argc, argv);
+    USE_BIASSEC = TRUE;
+  }
+
+  GetID = FALSE;
+  Nsel = 0;
+  if ((N = get_argument (argc, argv, "-ID"))) {
+    GetID = TRUE;
+    remove_argument (N, &argc, argv);
+    Nsel = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  GetN = FALSE;
+  IDsel = NULL;
+  if ((N = get_argument (argc, argv, "-N"))) {
+    GetN = TRUE;
+    remove_argument (N, &argc, argv);
+    IDsel = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) { 
+    fprintf (stderr, "USAGE: cameraconfig [-options]\n");
+    exit (1);
+  }
+
+  /* load data from config file */
+  ScanConfig (config, "NCCD", "%d", 1, &Nccd);
+  ScanConfig (config, "NAXIS1", "%d", 1, &Nx);
+  ScanConfig (config, "NAXIS2", "%d", 1, &Ny);
+  ScanConfig (config, "MOSAIC_X", "%d", 1, &mosaic_x);
+  ScanConfig (config, "MOSAIC_Y", "%d", 1, &mosaic_y);
+  ScanConfig (config, "USE_BIASSEC", "%d", 1, &use_biassec);
+  
+  if (NCCD) fprintf (stdout, "%d\n", Nccd);
+  if (AXES) fprintf (stdout, "%d %d\n", Nx, Ny);
+  if (AXIS0) fprintf (stdout, "%d\n", Nx);
+  if (AXIS1) fprintf (stdout, "%d\n", Ny);
+  if (MOSAIC_X) fprintf (stdout, "%d\n", mosaic_x);
+  if (MOSAIC_Y) fprintf (stdout, "%d\n", mosaic_y);
+  if (USE_BIASSEC) fprintf (stdout, "%d\n", use_biassec);
+  
+  ScanConfig (config, "CHIPID_KEYWORD", "%s", 1, keyword);
+  
+  Choice = SEQ || CCDS || CCDN || XOFF || YOFF || XFLIP || YFLIP || DATASEC || BIASSEC || XO || YO || THETA;
+
+  for (i = 0; i < Nccd; i++) {
+    sprintf (field, "CCD.%d", i);
+    ScanConfig (config, field, "%s", 1, line);
+    sscanf (line, "%s %lf %lf %d %d %s %s %lf %lf %lf", 
+	    ID, &x, &y, &dx, &dy, datasec, biassec, &Xo, &Yo, &theta);
+
+    if (GetN  && strnumcmp (IDsel, ID)) fprintf (stdout, "%d\n", i);
+    if (GetID && (Nsel == i)) fprintf (stdout, "%s\n", ID);
+    if (SEQ)     fprintf (stdout, "%d ", i);
+    if (CCDS)    fprintf (stdout, "%s ", ID);
+    if (CCDN)    fprintf (stdout, "%02d", i);
+    if (XOFF)    fprintf (stdout, "%f ", x);
+    if (YOFF)    fprintf (stdout, "%f ", y);
+    if (XFLIP)   fprintf (stdout, "%d ", dx);
+    if (YFLIP)   fprintf (stdout, "%d ", dy);
+    if (DATASEC) fprintf (stdout, "%s ", datasec);
+    if (BIASSEC) fprintf (stdout, "%s ", biassec);
+
+    if (XO)      fprintf (stdout, "%7.1f ", Xo);
+    if (YO)      fprintf (stdout, "%7.1f ", Yo);
+    if (THETA)   fprintf (stdout, "%7.3f ", theta);
+
+    if (Choice) fprintf (stdout, "\n");
+
+  }
+
+  exit (0);
+}
+
+void usage () {
+
+  fprintf (stderr, "cameraconfig [option] : lookup camera parameters\n");
+  fprintf (stderr, "   -Nccd         : number of CCDs\n");
+  fprintf (stderr, "   -axes         : x & y dimensions\n");
+  fprintf (stderr, "   -axis0        : x dimension (CCD)\n");
+  fprintf (stderr, "   -axis1        : y dimension (CCD)\n");
+  fprintf (stderr, "   -mosaicx      : x dimension (mosaic)\n");
+  fprintf (stderr, "   -mosaicy      : y dimension (mosaic)\n");
+  fprintf (stderr, "   -usebiassec   : use header BIASSEC\n");
+
+  fprintf (stderr, "   -seq          : chip sequence number\n");
+  fprintf (stderr, "   -ccds         : chip extension ID\n");
+  fprintf (stderr, "   -xoff         : x offset in mosaic\n");
+  fprintf (stderr, "   -yoff         : y offset in mosaic\n");
+  fprintf (stderr, "   -xflip        : x flip in mosaic\n");
+  fprintf (stderr, "   -yflip        : y flip in mosaic\n");
+  fprintf (stderr, "   -datasec      : DATASEC value\n");
+  fprintf (stderr, "   -biassec      : BIASSEC value\n");
+
+  fprintf (stderr, "   -ID [N]       : return ID for seq\n");
+  fprintf (stderr, "   -N [ID]       : return seq for ID\n");
+  exit (2);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/convertimreg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/convertimreg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/convertimreg.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "imregister.h"
+# include "imreg.h"
+static char *version = "convertimreg $Revision: 3.4 $";
+
+int main (int argc, char **argv) {
+ 
+  FILE *f;
+  Header header;
+  RegImage *pimage;
+  int i, Nimage, status, lockstate, dbstate;
+  int *match;
+  
+  get_version (argc, argv, version);
+  ConfigInit (&argc, argv);
+  ConfigCamera ();
+  ConfigFilter ();
+  
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: convertregdb (input) (output)\n");
+    fprintf (stderr, "       convert old pseudo-FITS table imreg db to FITS table\n");
+    exit (1);
+  }
+  
+  /* load old-style pseudo-FITS table db */
+  lockstate = LCK_SOFT;
+
+  /* lock database (soft) */
+  f = fsetlockfile (argv[1], 300.0, lockstate, &dbstate);
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't open db\n");
+    exit (1);
+  }
+  Fseek (f, 0, SEEK_SET);
+
+  /* load in database header */
+  if (!gfits_load_header (f, &header)) {
+    fprintf (stderr, "ERROR: trouble reading database header\n");
+    fclearlockfile (argv[1], f, lockstate, &dbstate);
+    exit (1);
+  }
+
+  /* load existing data from database */
+  gfits_scan (&header, "NIMAGES", "%d", 1, &Nimage);
+  ALLOCATE (pimage, RegImage, Nimage);
+  status = fread (pimage, sizeof(RegImage), Nimage, f);
+  if (status != Nimage) {
+    fprintf (stderr, "ERROR: header and data in dB don't match (%d vs %d)\n", Nimage, status);
+    fclearlockfile (argv[1], f, lockstate, &dbstate);
+    exit (1);
+  }
+  fclearlockfile (argv[1], f, lockstate, &dbstate);
+  gfits_convert_RegImage (pimage, sizeof (RegImage), Nimage);
+
+  /* create complete subset */
+  ALLOCATE (match, int, Nimage);
+  for (i = 0; i < Nimage; i++) match[i] = i;
+
+  DumpFitsBintable (argv[2], pimage, match, Nimage);
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/detregister.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/detregister.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/detregister.c	(revision 22322)
@@ -0,0 +1,95 @@
+# include "imregister.h"
+# include "detrend.h"
+static char *version = "detregister $Revision: 3.5 $";
+
+int main (int argc, char **argv) {
+ 
+  char *dBFile;
+  DetReg newdata;
+  Descriptor descriptor;
+  FITS_DB db;
+  
+  get_version (argc, argv, version);
+  regargs (argc, argv, &descriptor);
+  DefineImage (argv[1], &descriptor);
+
+  newdata = DefineEntry (descriptor);
+  SaveEntry (argv[1], &newdata, descriptor.imageID);
+
+  db.lockstate = LCK_HARD;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  dBFile = set_dBFile ();
+  if (!gfits_db_lock (&db, dBFile)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  if (db.dbstate == LCK_EMPTY) {
+    gfits_db_create (&db);    
+    gfits_table_set_DetReg (&db.ftable, NULL, 0);
+  } else {  
+    if (!gfits_db_load (&db)) {
+      fprintf (stderr, "ERROR: failure to load db\n");
+      gfits_db_close (&db);
+      exit (1);
+    }
+  }
+
+  gfits_convert_DetReg (&newdata, sizeof (DetReg), 1);
+  gfits_table_to_vtable (&db.ftable, &db.vtable, 0, 0);
+  gfits_vadd_rows (&db.vtable, (char *) &newdata, 1, sizeof(DetReg));
+
+  gfits_db_update (&db);
+  gfits_db_close (&db);
+  gfits_db_free (&db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
+
+/* detrend files created by mkdetrend have names of the following form:
+   
+   TYPE.CCD.FILTER.CRUN.NUMBER.fits 
+   
+   ie:
+   
+   bias.02.0.01Ak3.0.fits
+   
+   When they are registered, the NUMBER component needs to be
+   updated to match the next available number.
+   
+   This is a bit tricky.  We can easily identify 
+   TYPE, CCD, FILTER, and extrapolate to the NUMBER, but 
+   unambiguously identifying the CRUN is harder.  We could make it a 
+   required keyword on the command line, or insert it in the
+   image header and request it if it doesn't exist?
+
+    required fields:
+
+    tstart
+    tstop
+    type
+    ccd
+
+    filename (automatic)
+    treg (automatic)
+
+    case type 
+      flat:
+      ring:
+      scat:
+        filter is required
+      default:
+        filter is none
+
+    case type
+      dark:
+        exptime is required
+      bias:
+        exptime is 0.0
+      default:
+        exptime is NaN
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/detsearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/detsearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/detsearch.c	(revision 22322)
@@ -0,0 +1,91 @@
+# include "imregister.h"
+# include "detrend.h"
+static char *version = "detsearch $Revision: 3.8 $";
+
+int main (int argc, char **argv) {
+ 
+  int Nmatch, Ndetrend;
+  char *dBFile;
+  FITS_DB db;
+  Match *match;
+  DetReg *detrend;
+
+  /* args searches the argument list and generates an array of criteria */
+  get_version (argc, argv, version);
+  args (argc, argv);
+
+  if (output.Criteria) PrintCriteria ();
+	
+  db.lockstate = (output.Modify || output.Delete) ? LCK_HARD : LCK_SOFT;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  dBFile = set_dBFile ();
+  if (!gfits_db_lock (&db, dBFile)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  
+  if (!gfits_db_load (&db)) {
+    fprintf (stderr, "ERROR: failure to load db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+
+  if (!output.Modify && !output.Delete && !output.Altpath) gfits_db_close (&db);
+  detrend = gfits_table_get_DetReg (&db.ftable, &Ndetrend, &db.swapped);
+  
+  match = MatchCriteria (detrend, Ndetrend, &Nmatch);          /* match basic criteria */
+  match = ExptimeCriteria (detrend, Ndetrend, match, &Nmatch); /* reduce matches based on Exptime */
+  match = CloseCriteria (detrend, Ndetrend, match, &Nmatch);   /* reduce matches based on closeness */
+
+  if (output.Select) {
+    match = UniqueSubset (detrend, Ndetrend, match, &Nmatch);
+    if (Nmatch == 0) {
+      if (output.verbose) fprintf (stderr, "ERROR: can't find any valid detrend files (%s %s %d)\n", get_type_name(criteria[0].Type), filtername[criteria[0].Filter], criteria[0].CCD);
+      gfits_db_close (&db);
+      exit (1);
+    }
+  }
+
+  if (output.Altpath) SetAltpath (&db, detrend, Ndetrend, match, Nmatch);
+  if (output.Modify) ModifySubset (&db, detrend, Ndetrend, match, Nmatch);
+  if (output.Delete) DeleteSubset (&db, detrend, Ndetrend, match, Nmatch);
+
+  OutputSubset (detrend, Ndetrend, match, Nmatch);
+  exit (0);
+}
+
+/*
+
+selection options:
+
+CCDSelect
+TypeSelect
+TimeSelect
+FilterSelect
+ExptimeSelect
+EntrySelect
+LabelSelect
+
+
+ functional modes:
+
+   1) list - list all images that apply to a restrictive set of criteria
+   
+   2) select - choose the reference image(s)
+
+   3) delete - delete a set of images
+
+   4) modify - alter fields for a set of images
+
+   
+   methods to define criteria
+
+   - command line  [-time -type -ccd -filter -exptime]
+   - image  -> define time, filter, ccd
+   - mosaic -> define time, filter, exptime
+   - recipe -> defile type list based on filter
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/filtnames.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/filtnames.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/filtnames.c	(revision 22322)
@@ -0,0 +1,168 @@
+# include "imregister.h"
+static char *version = "filtnames $Revision: 1.2 $";
+
+typedef struct {
+  int code, calibrated;
+  char name[64], ref[64], c1[64], c2[64];
+} FiltCode;
+
+int main (int argc, char **argv) {
+
+  int i, code, Ncode, Nmatch, Nfield, N, Select, CalibrationData;
+  int Nfiltcode, NFILTCODE;
+  char *config, *file, *target;
+  char FilterList[256];
+  char name[64], ref[64], c1[64], c2[64], line[256], *c;
+  FILE *f;
+  FiltCode *filtcode;
+
+  get_version (argc, argv, version);
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (&argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (!ScanConfig (config, "FILTER_LIST", "%s", 0, FilterList)) {
+    fprintf (stderr, "ERROR: can't find FILTER_LIST in configuration file\n");
+    exit (1);
+  }
+  free (config);
+  free (file);
+
+  /* image type (dark, flat, bias, etc) */
+  Select = TRUE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    Select = FALSE;
+  }
+ 
+  /* image type (dark, flat, bias, etc) */
+  CalibrationData = FALSE;
+  if ((N = get_argument (argc, argv, "-cal"))) {
+    remove_argument (N, &argc, argv);
+    CalibrationData = TRUE;
+  }
+ 
+  if (argc != 2) { 
+    fprintf (stderr, "USAGE: filtnames (filtername)\n");
+    fprintf (stderr, "       filtnames list : list all unique filters\n");
+    fprintf (stderr, "       [-all] : list all names for given filter(s)\n");
+    fprintf (stderr, "       [-cal] : print calibration data for filter(s)\n");
+    exit (1);
+  }
+
+  target = argv[1];
+  for (i = 0; i < strlen (target); i++) { if (isspace (target[i])) target[i] = '.'; }
+
+  /* open filter list file */
+  f = fopen (FilterList, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "error reading photcodes\n");
+    exit (1);
+  }
+
+  /* load filter list & codes */
+  NFILTCODE = 100;
+  Nfiltcode = 0;
+  ALLOCATE (filtcode, FiltCode, NFILTCODE);
+
+  while (scan_line (f, line) != EOF) {
+    for (c = line; isspace (*c); c++);
+    if (*c == '#') continue;
+    if (*c == 0) continue;
+    Nfield = sscanf (c, "%d %s %s %s %*s %s", &code, name, ref, c1, c2);
+    if ((Nfield != 2) && (Nfield != 5)) {
+      fprintf (stderr, "error reading line: %s\n", c);
+      exit (1);
+    }
+
+    filtcode[Nfiltcode].code = code;
+    filtcode[Nfiltcode].calibrated = FALSE;
+    strcpy (filtcode[Nfiltcode].name, name);
+    filtcode[Nfiltcode].ref[0] = 0;
+    filtcode[Nfiltcode].c1[0] = 0;
+    filtcode[Nfiltcode].c2[0] = 0;
+    if (Nfield == 5) {
+      filtcode[Nfiltcode].calibrated = TRUE;
+      strcpy (filtcode[Nfiltcode].ref, ref);
+      strcpy (filtcode[Nfiltcode].c1, c1);
+      strcpy (filtcode[Nfiltcode].c2, c2);
+    }
+    Nfiltcode ++;
+
+    if (Nfiltcode == NFILTCODE - 1) {
+      NFILTCODE += 100;
+      REALLOCATE (filtcode, FiltCode, NFILTCODE);
+    }
+  }
+
+  /* special target: list -- list all filters */
+  if (!strcasecmp (target, "list")) {
+    
+    int *uniq, Nuniq, j, found;
+    ALLOCATE (uniq, int, Nfiltcode);
+
+    /* identify unique filter codes */
+    Nuniq = 0;
+    for (i = 0; i < Nfiltcode; i++) {
+      found = FALSE;
+      for (j = 0; !found && (j < Nuniq); j++) {
+	if (filtcode[i].code == uniq[j]) found = TRUE;
+      }
+      if (found) continue;
+      uniq[Nuniq] = filtcode[i].code;
+      Nuniq ++;
+    }
+
+    /* list the entries of the unique codes.  skip code == 0 (none) */
+    for (i = 0; i < Nuniq; i++) {
+      if (uniq[i] == 0) continue;
+      for (j = 0; j < Nfiltcode; j++) {
+	if (uniq[i] != filtcode[j].code) continue;
+	if (CalibrationData) {
+	  fprintf (stdout, "%s %d %s %s %s\n", filtcode[j].name, filtcode[j].calibrated, filtcode[j].ref, filtcode[j].c1, filtcode[j].c2);
+	} else {
+	  fprintf (stdout, "%s\n", filtcode[j].name);
+	}
+	if (Select) break;
+      }
+    }
+    exit (0);
+  }
+
+  /* find given name in filter list (case insensitive) */
+  Ncode = 0;
+  for (i = 0; i < Nfiltcode; i++) {
+    if (strcasecmp (target, filtcode[i].name)) continue;
+    Ncode = filtcode[i].code;
+    break;
+  }
+  if (!Ncode) {
+    fprintf (stderr, "no filter match found\n");
+    exit (1);
+  }
+
+  /* find first entry with this code */
+  Nmatch = 0;
+  for (i = 0; i < Nfiltcode; i++) {
+    if (Ncode != filtcode[i].code) continue;
+    if (CalibrationData) {
+      fprintf (stdout, "%s %d %s %s %s\n", filtcode[i].name, filtcode[i].calibrated, filtcode[i].ref, filtcode[i].c1, filtcode[i].c2);
+    } else {
+      fprintf (stdout, "%s\n", filtcode[i].name);
+    }
+    Nmatch ++;
+    if (Select) break;
+  }
+
+  if (!Nmatch) {
+    fprintf (stderr, "no filter code error: code mis-match\n");
+    exit (1);
+  }
+
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imphotmerge.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imphotmerge.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imphotmerge.c	(revision 22322)
@@ -0,0 +1,177 @@
+# include "imregister.h"
+
+/*** this needs to be written using dvo_image functions !!! ***/
+
+Image *LoadImageTable (FILE *f, Header *header, int *nimage);
+static char *version = "imphotcopy $Revision: 1.6 $";
+
+int main (int argc, char **argv) {
+ 
+  Header header, theader;
+  Image *image, *input;
+  FILE *f, *g;
+  int i, j, status, N;
+  char *dBFile;
+  int Nimage, Ninput, *index, Ntimes;
+  time_t *tstart, *tstop;
+  int VERBOSE, PHOTCODE, dbstate;
+  int PhotCodeSelect, Nin;
+  char *FitsOutput, *NameSelect;
+  int NameSelectLength;
+
+  get_version (argc, argv, version);
+  ConfigInit (&argc, argv);
+
+  /* interpret command-line arguments */
+  if (!get_trange_arguments (&argc, argv, &tstart, &tstop, &Ntimes)) {
+    fprintf (stderr, "ERROR: syntax error\n");
+    exit (1);
+  }
+
+  /* select by image photcode */
+  PhotCodeSelect = FALSE;
+  PHOTCODE = 0;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    if (!(PHOTCODE = GetPhotcodeCodebyName (argv[N]))) {
+      fprintf (stderr, "ERROR: photcode not found in photcode table\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    PhotCodeSelect = TRUE;
+  }
+
+  /* string in image name */
+  NameSelect = (char *) NULL;
+  NameSelectLength = 0;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    NameSelect = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    NameSelectLength = strlen (NameSelect);
+  }
+
+  FitsOutput = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-fits"))) {
+    remove_argument (N, &argc, argv);
+    FitsOutput = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: imphotmerge (input) (target) [config ops] [-trange start stop/delta] [-photcode code]\n");
+    exit (1);
+  }
+ 
+  /* load image database - still a non-FITS file */
+  dBFile = argv[2];
+
+  f = fsetlockfile (dBFile, 120.0, LCK_HARD, &dbstate);
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't set lock on %s, state is %d\n", dBFile, dbstate);
+    exit (1);
+  }
+  image = LoadImageTable (f, &header, &Nimage);
+
+  /* load input table */
+  g = fopen (argv[1], "r");
+  if (g == (FILE *) NULL) {
+    fprintf (stderr, "error opening input data file\n");
+    exit (1);
+  }
+  input = LoadImageTable (g, &theader, &Ninput);
+  fclose (g);
+
+  /* allocate space for reference lists */
+  Nin = 0;
+  ALLOCATE (index, int, Ninput);
+  
+  /* these filters are applied to input NOT image */
+  for (i = 0; i < Ninput; i++) {
+    for (j = 0, status = FALSE; !status && (j < Ntimes); j++) {
+      status = (input[i].tzero >= tstart[j]) && (input[i].tzero <= tstop[j]);
+    }
+    if (!status && Ntimes) continue;
+    if (PhotCodeSelect && (input[i].photcode != PHOTCODE)) continue;
+    if ((NameSelect != (char *) NULL) && (strncasecmp (input[i].name, NameSelect, NameSelectLength))) continue;
+
+    index[Nin] = i;
+    Nin ++;
+  }
+
+  /* add new images to images */
+  REALLOCATE (image, Image, Nimage + Nin);
+  for (j = 0, i = Nimage; i < Nimage + Nin; i++, j++) {
+    image[i] = input[index[j]];
+  }
+  Nimage += Nin;
+  gfits_modify (&header, "NIMAGES", "%d", 1, Nimage);
+
+  /* position to begining of file to write header */
+  fseek (f, 0, SEEK_SET);
+  status = Fwrite (header.buffer, 1, header.size, f, "char");
+  if (status != header.size) {
+    fprintf (stderr, "ERROR: failed writing data to image header\n");
+    exit (0);
+  }
+
+  /* position to end of file for new image data */
+  fseek (f, header.size, SEEK_SET);
+  status = Fwrite (image, sizeof(Image), Nimage, f, "image");
+  if (status != Nimage) {
+    fprintf (stderr, "ERROR: failed writing data to image catalog\n");
+    exit (0);
+  }
+
+  fclearlockfile (dBFile, f, LCK_HARD, &dbstate);
+
+  if (VERBOSE) fprintf (stderr, "SUCCESS\n");
+  exit (0);
+
+}
+
+Image *LoadImageTable (FILE *f, Header *header, int *nimage) {
+
+  int Nimage, Ndata, Nread, size;
+  struct stat filestatus;
+  Image *image;
+
+  /* read header */
+  if (!gfits_fread_header (f, header)) {
+    fprintf (stderr, "ERROR: can't read image catalog\n");
+    exit (1);
+  }
+
+  /* check that file size makes sense */
+  Nimage = 0;
+  gfits_scan (header, "NIMAGES", "%d", 1, &Nimage);
+  if (fstat (fileno(f), &filestatus) == -1) {
+    fprintf (stderr, "ERROR: failed to get status of image catalog\n");
+    exit (1);
+  }
+  size = Nimage*sizeof(Image) + header[0].size;
+  if (size != filestatus.st_size) {
+    Ndata = (filestatus.st_size - header[0].size) / sizeof (Image);
+    fprintf (stderr, "ERROR: image catalog has inconsistent size\n");
+    fprintf (stderr, "header: %d, data: %d\n", Nimage, Ndata);
+    Nimage = Ndata;
+  } 
+
+  /* alloc, read images */
+  ALLOCATE (image, Image, MAX (Nimage, 1));
+  Nread = Fread (image, sizeof(Image), Nimage, f, "image");
+  if (Nread != Nimage) {
+    fprintf (stderr, "ERROR: problem loading image catalog\n");
+    exit (1);
+  } 
+
+  *nimage = Nimage;
+  return (image);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imphotsearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imphotsearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imphotsearch.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "imregister.h"
+# include "imphot.h"
+
+static char *version = "imphotsearch $Revision: 1.10 $";
+
+int main (int argc, char **argv) {
+ 
+  int Nmatch, Nimage;
+  int *match;
+  Image *image;
+  FITS_DB db;
+
+  get_version (argc, argv, version);
+  args (argc, argv);
+ 
+  db.lockstate = (options.modify) ? LCK_HARD : LCK_SOFT;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  /* don't create a new image table if not available */
+  if (!gfits_db_lock (&db, ImPhotDB)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  /* we use a varient of gfits_db_load since the file may be in text format */
+  if (!dvo_image_load (&db, VERBOSE, FORCE_READ)) {
+    fprintf (stderr, "ERROR: failure to load db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  if (!options.modify) gfits_db_close (&db);
+
+  image = gfits_table_get_Image (&db.ftable, &Nimage, &db.swapped);
+
+  match = subset (image, Nimage, &Nmatch);
+  if (options.modify) ModifySubset (&db, image, Nimage, match, Nmatch);
+
+  output (image, match, Nmatch);
+
+  if (VERBOSE) fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imregister.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imregister.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imregister.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "imregister.h"
+# include "imreg.h"
+static char *version = "imregister $Revision: 3.8 $";
+
+int main (int argc, char **argv) {
+ 
+  int Nregimage;
+  RegImage *image, *regimage;
+  FITS_DB db;
+
+  get_version (argc, argv, version);
+  args (argc, argv);
+
+  image = iminfo (argv[1]);
+  regimage = newimages (image, &Nregimage);
+
+  if (NoReg) goto skip_reg;
+
+  db.lockstate = LCK_HARD;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  if (!gfits_db_lock (&db, ImageDB)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  if (db.dbstate == LCK_EMPTY) {
+    gfits_db_create (&db);
+    gfits_table_set_RegImage (&db.ftable, NULL, 0);
+  } else {  
+    if (!gfits_db_load (&db)) {
+      fprintf (stderr, "ERROR: failure to load db\n");
+      gfits_db_close (&db);
+      exit (1);
+    }
+  }
+
+  gfits_convert_RegImage (regimage, sizeof (RegImage), Nregimage);
+  gfits_table_to_vtable (&db.ftable, &db.vtable, 0, 0);
+  gfits_vadd_rows (&db.vtable, (char *) regimage, Nregimage, sizeof(RegImage));
+
+  gfits_db_update (&db);
+  gfits_db_close (&db);
+  gfits_db_free (&db);
+
+skip_reg:
+  if (IMSORT) SubmitImages (image);
+  fprintf (stderr, "SUCCESS: registered %s\n", argv[1]);
+  exit (0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imregtable.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imregtable.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imregtable.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "imregister.h"
+# include "imreg.h"
+static char *version = "imregtable $Revision: 3.8 $";
+
+int main (int argc, char **argv) {
+ 
+  int Nimage;
+  char *infile;
+  RegImage *image;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+  FITS_DB db;
+
+  get_version (argc, argv, version);
+  ConfigInit (&argc, argv);
+  ConfigCamera ();
+  ConfigFilter ();
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: imregtable (table)\n");
+    exit (1);
+  }
+
+  /* load in table data */
+  infile = argv[1];
+
+  /* need to error check these */
+  ftable.header = &theader;
+  gfits_read_header  (infile, &header);
+  gfits_read_matrix  (infile, &matrix);
+  gfits_read_ftable  (infile, &ftable, "IMAGE_DATABASE");
+
+  image = gfits_table_get_RegImage (&ftable, &Nimage, NULL);
+
+  /* load database table */
+  db.lockstate = LCK_HARD;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  if (!gfits_db_lock (&db, ImageDB)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  if (db.dbstate == LCK_EMPTY) {
+    gfits_db_create (&db);
+    gfits_table_set_RegImage (&db.ftable, NULL, 0);
+  } else {  
+    if (!gfits_db_load (&db)) {
+      fprintf (stderr, "ERROR: failure to load db\n");
+      gfits_db_close (&db);
+      exit (1);
+    }
+  }
+
+  gfits_convert_RegImage (image, sizeof (RegImage), Nimage);
+  gfits_table_to_vtable (&db.ftable, &db.vtable, 0, 0);
+  gfits_vadd_rows (&db.vtable, (char *) image, Nimage, sizeof(RegImage));
+
+  gfits_db_update (&db);
+  gfits_db_close (&db);
+  gfits_db_free (&db);
+
+  fprintf (stderr, "SUCCESS: registered %s\n", argv[1]);
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imsearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imsearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imsearch.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "imregister.h"
+# include "imreg.h"
+static char *version = "imsearch $Revision: 3.7 $";
+
+int main (int argc, char **argv) {
+ 
+  int Nmatch, Nimage, *match;
+  RegImage *image;
+  FITS_DB db;
+
+  get_version (argc, argv, version);
+  args (argc, argv);
+
+  db.lockstate = (output.modify || output.delete) ? LCK_HARD : LCK_SOFT;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  if (!gfits_db_lock (&db, ImageDB)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  if (!gfits_db_load (&db)) {
+    fprintf (stderr, "ERROR: failure to load db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+
+  if (!output.modify && !output.delete) gfits_db_close (&db);
+
+  image = gfits_table_get_RegImage (&db.ftable, &Nimage, &db.swapped);
+  
+  match = match_criteria (image, Nimage, &Nmatch);
+  match = unique_entries (image, Nimage, match, &Nmatch);
+
+  if (output.modify) ModifySubset (&db, image, Nimage, match, Nmatch);
+  if (output.delete) DeleteSubset (&db, image, Nimage, match, Nmatch);
+
+  OutputSubset (image, Nimage, match, Nmatch);
+  exit (0);
+}
+
+/* 
+
+FITS table version:
+
+   load_db - open, read in database, store as RegImage structure
+   match   - return index 'match' to matched images
+   modify  - change value of selected images
+             write out subset of rows
+   delete  - remove selected images from image structure
+             write out entire table
+   output  - write out subset in various formats
+
+   get_images returns pointer to complete image structure
+
+
+SQL version:
+
+   load_db - set up connection
+   match   - convert criteria to SQL where clause and do:
+             'select from images [where clause]'
+             images structure is filled with result from query
+   modify  - change value of subset selection (identical)
+             update selected rows
+   delete  - user where clause and do 
+             'delete from images where clause'
+   output  - (identical)
+
+   get_images returns pointer to image subset from query
+   match[i] = i
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imstatreg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imstatreg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/imstatreg.c	(revision 22322)
@@ -0,0 +1,116 @@
+# include "imregister.h"
+# include "imreg.h"
+static char *version = "imstatreg $Revision: 3.16 $";
+
+int main (int argc, char **argv) {
+ 
+  int i, child;
+  int *match, Nmatch, Nimage, Nsubset;
+  FILE *f;
+  RegImage *image, *subset;
+  FITS_DB image_db, temp_db;
+
+  get_version (argc, argv, version);
+  args (argc, argv);
+  SetSignals ();
+
+  if (CLIENT) imregclient (argv[1], argv[2], argv[3]);
+
+  /* fork in background */
+  child = fork ();
+  if (child == -1) {
+    fprintf (stderr, "error forking imstatreg -daemon \n");
+    exit (1);
+  } 
+  if (child !=  0) {
+    fprintf (stderr, "starting imstatreg, logging to %s\n", LogFile);
+    exit (0);
+  }
+
+  /* child process, check for previous process */
+  ConfigPID (PIDFILE);
+
+  /* redirect stderr, stdout to logfile */
+  f = freopen (LogFile, "a", stdout);
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open log file %s, writing to stderr\n", LogFile);
+  } else {
+    /* an error here will be missed, but is unlikely since we have access in the above test */
+    f = freopen (LogFile, "a", stderr);
+  }
+
+  image_db.lockstate = LCK_HARD;
+  image_db.timeout   = 300.0;
+  gfits_db_init (&image_db);
+
+  temp_db.lockstate = LCK_HARD;
+  temp_db.timeout   = 300.0;
+  gfits_db_init (&temp_db);
+
+  /* start loop */
+  while (1) {
+
+    /* check / load / delete temporary database */
+    if (!gfits_db_lock (&temp_db, TempDB)) {
+      gfits_db_close (&temp_db);
+      fprintf (stderr, "error locking temp db (path missing? access permission?)\n");
+      exit (1);
+    }
+    if (temp_db.dbstate == LCK_EMPTY) {
+      fprintf (stderr, "temporary database empty\n");
+      gfits_db_close (&temp_db);
+      goto next;
+    } 
+    if (!gfits_db_load (&temp_db)) {
+      fprintf (stderr, "error reading temp db\n");
+      exit (1);
+    }
+    subset = gfits_table_get_RegImage (&temp_db.ftable, &Nsubset, &temp_db.swapped);
+    fprintf (stderr, "temporary database read\n");
+
+    /* delete, unlock existing database */
+    truncate (TempDB, 0);
+    gfits_db_close (&temp_db);
+    fprintf (stderr, "temporary database closed\n");
+ 
+    /* check / load main database */
+    if (!gfits_db_lock (&image_db, ImageDB)) {
+      gfits_db_close (&image_db);
+      fprintf (stderr, "error locking image db (path missing? access permission?)\n");
+      exit (1);
+    }
+    if (temp_db.dbstate == LCK_EMPTY) {
+      fprintf (stderr, "main database empty\n");
+      gfits_db_close (&image_db);
+      gfits_db_free (&temp_db);
+      goto next;
+      /* this is a type of error: we read entries from the
+	 temp db, but there were no entries to match in the main db
+	 we will just drop the temp db data */
+    }
+    image = gfits_table_get_RegImage (&image_db.ftable, &Nimage, &image_db.swapped);
+    fprintf (stderr, "main database read\n");
+
+    /* match temp image with main images */
+    match = match_images (image, Nimage, subset, Nsubset, &Nmatch);
+    if (Nmatch != Nsubset) fprintf (stderr, "WARNING: some images missed\n");
+
+    /* update entries in main db */
+    gfits_vtable_from_ftable (&image_db.ftable, &image_db.vtable, match, Nmatch);
+    for (i = 0; i < Nmatch; i++) {
+      gfits_convert_RegImage ((RegImage *) image_db.vtable.buffer[i], sizeof (RegImage), 1);
+    }
+    gfits_db_update (&image_db);
+    gfits_db_close (&image_db);
+
+    gfits_db_free (&image_db);
+    gfits_db_free (&temp_db);
+    if (match != NULL) free (match);
+    if (image != NULL) free (image);
+      
+  next:
+    fflush (stderr);
+    fflush (stdout);
+    sleep (LOOP_DELAY);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photcode-table.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photcode-table.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photcode-table.c	(revision 22322)
@@ -0,0 +1,102 @@
+# include "imregister.h"
+enum {NONE, IMPORT, EXPORT};
+
+char CatdirPhotcodeFile[256];
+char MasterPhotcodeFile[256];
+
+int args (int argc, char **argv);
+int ConfigInitLocal (int *argc, char **argv);
+void GetConfig (char *config, char *field, char *format, int N, void *ptr);
+void usage ();
+
+int main (int argc, char **argv) {
+
+  int mode;
+
+  ConfigInitLocal (&argc, argv);
+  mode = args (argc, argv);
+    
+  if (mode == IMPORT) {
+    LoadPhotcodesText (MasterPhotcodeFile);
+    SavePhotcodesFITS (CatdirPhotcodeFile);
+    exit (0);
+  }
+
+  if (mode == EXPORT) {
+    LoadPhotcodesFITS (CatdirPhotcodeFile);
+    SavePhotcodesText (MasterPhotcodeFile);
+    exit (0);
+  }
+
+  usage ();
+  exit (1);
+}
+
+int args (int argc, char **argv) {
+
+  int N, mode;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help")) usage ();
+  if (get_argument (argc, argv, "-h")) usage ();
+
+  mode = NONE;
+  if ((N = get_argument (argc, argv, "-import"))) {
+    mode = IMPORT;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-export"))) {
+    mode = EXPORT;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) usage ();
+
+  strcpy (MasterPhotcodeFile, argv[1]);
+  return (mode);
+}
+
+int ConfigInitLocal (int *argc, char **argv) {
+
+  char *config, *file;
+  char CATDIR[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  // if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  GetConfig (config, "CATDIR",                 	"%s",  0, CATDIR);
+  // GetConfig (config, "PHOTCODE_FILE",          	"%s",  0, MasterPhotcodeFile);
+  
+  // set the CATDIR version based on CATDIR
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+
+  free (config);
+  free (file);
+  return TRUE;
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
+
+void usage () {
+
+  fprintf (stderr, "USAGE: photcode-table -export (textfile) [-D CATDIR catdir]\n");
+  fprintf (stderr, "USAGE: photcode-table -import (textfile) [-D CATDIR catdir]\n");
+  exit (2);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photcode.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photcode.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photcode.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "imregister.h"
+static char *version = "photcode $Revision: 3.4 $";
+
+int main (int argc, char **argv) {
+
+  Header header;
+  char detector[80], filter[80], *ID;
+  int i, ccd, VERBOSE, N, Nfilter;
+
+  get_version (argc, argv, version);
+  ConfigInit (&argc, argv);
+  ConfigCamera ();
+  ConfigFilter ();
+
+  VERBOSE = TRUE;
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = FALSE;
+  }
+
+  if (argc != 4) { 
+    fprintf (stderr, "USAGE: photcode (file.fits) (ccd) (mode)\n");
+    exit (1);
+  }
+
+  /* read in image header */
+  if (!gfits_read_header (argv[1], &header)) {
+    if (VERBOSE) fprintf (stderr, "ERROR: can't find image file %s (1)\n", argv[1]);
+    exit (1);
+  }
+
+  gfits_scan (&header, CameraKeyword, "%s", 1, detector);
+  for (i = 0; i < strlen(detector); i++) { detector[i] = toupper (detector[i]); }
+  for (i = 0; i < strlen(detector); i++) { if (isspace (detector[i])) detector[i] = '.'; }
+
+  gfits_scan (&header, FilterKeyword,   "%s", 1, filter);
+  for (i = 0; i < strlen (filter); i++) { if (isspace (filter[i])) filter[i] = '.'; }
+  Nfilter = FILTER_NONE;
+  for (i = 0; (i < NFILTER) && (Nfilter == FILTER_NONE); i++) {
+    if (!strcasecmp (filter, filtername[i])) {
+      Nfilter = filternum[i];
+    }
+  }      
+  if (Nfilter == FILTER_NONE) {
+    fprintf (stderr, "ERROR: invalid filter %s\n", filter);
+    exit (1);
+  }
+  strcpy (filter, filterhash[Nfilter]);
+
+  if (!strcasecmp (argv[3], "mef")) {
+    ID = strcreate (argv[2]);
+  } else {
+    ALLOCATE (ID, char, 80);
+    gfits_scan (&header, CCDnumKeyword,  "%s", 1, ID);
+  }
+  ccd = -1;
+  for (i = 0; (i < Nccd) && (ccd == -1); i++) {
+    if (strnumcmp (ccds[i], ID)) {
+      ccd = i;
+    }
+  }
+  if (ccd == -1) {
+    fprintf (stderr, "warning: ccd %d not found in camera config file\n", ccd);
+    ccd = 0;
+  }
+
+  fprintf (stdout, "%s.%s.%02d\n", detector, filter, ccd);
+
+  exit (0);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photreg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photreg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photreg.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "imregister.h"
+# include "photreg.h"
+static char *version = "photreg $Revision: 1.9 $";
+
+int main (int argc, char **argv) {
+ 
+  char *filename;
+  PhotPars newdata;
+  FITS_DB db;
+  
+  get_version (argc, argv, version);
+  regargs (argc, argv, &newdata);
+
+  db.lockstate = LCK_HARD;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  filename = PhotDB;
+  if (!strcmp (output.db, "trans")) filename = TransDB;
+
+  if (!gfits_db_lock (&db, filename)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  if (db.dbstate == LCK_EMPTY) {
+    gfits_db_create (&db);
+    gfits_table_set_PhotPars (&db.ftable, NULL, 0);
+    /* EXTNAME is set to ZERO_POINTS_3.0 by default */
+    if (!strcmp (output.db, "trans")) {
+      gfits_modify (&db.theader, "EXTNAME", "%s", 1, "TRANS_POINTS_3.0");
+    }
+  } else {  
+    if (!gfits_db_load (&db)) {
+      fprintf (stderr, "ERROR: failure to load db\n");
+      gfits_db_close (&db);
+      exit (1);
+    }
+  }
+
+  /** we may later want to pull this out and put it elsewhere **/
+  gfits_convert_PhotPars (&newdata, sizeof (PhotPars), 1);
+  gfits_table_to_vtable (&db.ftable, &db.vtable, 0, 0);
+  gfits_vadd_rows (&db.vtable, (char *) &newdata, 1, sizeof(PhotPars));
+
+  gfits_db_update (&db);
+  gfits_db_close (&db);
+  gfits_db_free (&db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
+
+  /* data values are set in args by matching flags */
+
+/*** stick the header, table, theader, ftable creation in a single API? ***/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photsearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photsearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/photsearch.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "imregister.h"
+# include "photreg.h"
+static char *version = "photsearch $Revision: 1.9 $";
+
+int main (int argc, char **argv) {
+
+  char *filename;
+  int Nmatch, Nphotpars;
+  int *match;
+  FITS_DB db;
+  PhotPars *photpars;
+  PhotParsOld *photpars_old;
+
+  get_version (argc, argv, version);
+  args (argc, argv);
+
+  db.lockstate = (output.modify || output.delete) ? LCK_HARD : LCK_SOFT;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  if (!strcmp (output.db, "phot")) {
+    filename = PhotDB;
+  } else {
+    filename = TransDB;
+  }
+
+  if (!gfits_db_lock (&db, filename)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  
+  if (!gfits_db_load (&db)) {
+    fprintf (stderr, "ERROR: failure to load db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+
+  if (!output.modify && !output.delete) gfits_db_close (&db);
+
+  /* add test to EXTNAME? */
+  if (output.convert) {
+    photpars_old = gfits_table_get_PhotParsOld (&db.ftable, &Nphotpars, &db.swapped);
+    photpars = PhotParsOld_to_PhotPars (photpars_old, Nphotpars);
+  } else {
+    photpars = gfits_table_get_PhotPars (&db.ftable, &Nphotpars, &db.swapped);
+  }
+
+  match = match_criteria (photpars, Nphotpars, &Nmatch);
+
+  if (output.delete) DeleteSubset (&db, photpars, Nphotpars, match, Nmatch);
+
+  OutputSubset (photpars, Nphotpars, match, Nmatch);
+  exit (0);
+}
+
+  /* db selection is set in args, based on -trans */
+
+/* valid EXTNAME values:
+
+   phot, !output.convert, bintable: "ZERO_POINTS_3.0"
+   phot, !output.convert, table:    "IMAGE_ZPTS"
+
+   trans, !output.convert, bintable: "TRANS_POINTS_3.0"
+   trans, !output.convert, table:    "SUMMARY_ZPTS"
+
+   phot, !output.convert, bintable: "ZERO_POINTS"
+   phot, !output.convert, table:    "IMAGE_ZPTS"
+
+   trans, !output.convert, bintable: "TRANS_POINTS"
+   trans, !output.convert, table:    "SUMMARY_ZPTS"
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/showiminfo.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/showiminfo.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/showiminfo.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "imregister.h"
+# include "imreg.h"
+static char *version = "showiminfo $Revision: 3.2 $";
+
+int main (int argc, char **argv) {
+ 
+  char *obstime, *regtime;
+  RegImage *image, im;
+
+  get_version (argc, argv, version);
+  args (argc, argv);
+  image = iminfo (argv[1]);
+  im = image[0];
+  
+  fprintf (stderr, "\n");
+  fprintf (stderr, "filename: %s\n",   im.filename);
+  fprintf (stderr, "pathname: %s\n",   im.pathname);
+  fprintf (stderr, "filter: %s\n",     im.filter);
+  fprintf (stderr, "instrument: %s\n\n", im.instrument);
+
+  fprintf (stderr, "ccd: %d, mode: %d, type: %d\n\n", im.ccd, im.mode, im.type);
+
+  fprintf (stderr, "exptime: %f, airmass: %f, telfocus: %f\n", im.exptime, im.airmass, im.telfocus);
+  fprintf (stderr, "xprobe: %f, yprobe: %f, zprobe: %f\n", im.xprobe, im.yprobe, im.zprobe);
+  fprintf (stderr, "dettemp: %f, temp0: %f temp1: %f, temp2: %f, temp3: %f\n\n", 
+	   im.dettemp, im.teltemp_0, im.teltemp_1, im.teltemp_2, im.teltemp_3);
+
+  fprintf (stderr, "ra: %f, dec: %f, rotangle: %f\n", im.ra, im.dec, im.rotangle);
+
+  obstime = ohana_sec_to_date (im.obstime);
+  regtime = ohana_sec_to_date (im.regtime);
+
+  fprintf (stderr, "obstime: %s\n", obstime);
+  fprintf (stderr, "regtime: %s\n", regtime);
+
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/spregister.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/spregister.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/spregister.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "imregister.h"
+# include "spreg.h"
+static char *version = "spregister $Revision: 1.9 $";
+
+int main (int argc, char **argv) {
+ 
+  Spectrum *spectrum;
+  FITS_DB db;
+
+  get_version (argc, argv, version);
+  args (argc, argv);
+
+  spectrum = spinfo (argv[1]);
+
+  if (DUMP) showinfo (spectrum);
+
+  if (NoReg) exit (0);
+
+  db.lockstate = LCK_HARD;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  if (!gfits_db_lock (&db, SpectrumDB)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  if (db.dbstate == LCK_EMPTY) {
+    gfits_db_create (&db);
+    gfits_table_set_Spectrum (&db.ftable, NULL, 0);
+  } else {  
+    if (!gfits_db_load (&db)) {
+      fprintf (stderr, "ERROR: failure to load db\n");
+      gfits_db_close (&db);
+      exit (1);
+    }
+  }
+
+  gfits_convert_Spectrum (spectrum, sizeof (Spectrum), 1);
+  gfits_table_to_vtable (&db.ftable, &db.vtable, 0, 0);
+  gfits_vadd_rows (&db.vtable, (char *) spectrum, 1, sizeof(Spectrum));
+
+  gfits_db_update (&db);
+  gfits_db_close (&db);
+  gfits_db_free (&db);
+
+  fprintf (stderr, "SUCCESS\n");
+  exit (0);
+}
+
+
+/* notes:
+   SpectrumDB set in args:ConfigInit by config variable SPECTRUM_DB
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/spsearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/spsearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/imregister/src/spsearch.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "imregister.h"
+# include "spreg.h"
+static char *version = "spsearch $Revision: 1.8 $";
+
+int main (int argc, char **argv) {
+ 
+  int Nmatch, Nspectrum, *match;
+  Spectrum *spectrum;
+  FITS_DB db;
+
+  get_version (argc, argv, version);
+  args (argc, argv);
+
+  db.lockstate = (output.modify || output.delete) ? LCK_HARD : LCK_SOFT;
+  db.timeout   = 300.0;
+  gfits_db_init (&db);
+
+  if (!gfits_db_lock (&db, SpectrumDB)) {
+    fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+  if (!gfits_db_load (&db)) {
+    fprintf (stderr, "ERROR: failure to load db\n");
+    gfits_db_close (&db);
+    exit (1);
+  }
+
+  if (!output.modify && !output.delete) gfits_db_close (&db);
+
+  spectrum = gfits_table_get_Spectrum (&db.ftable, &Nspectrum, &db.swapped);
+  
+  match = match_criteria (spectrum, Nspectrum, &Nmatch);
+  match = unique_entries (spectrum, Nspectrum, match, &Nmatch);
+
+  if (output.modify) ModifySubset (&db, spectrum, Nspectrum, match, Nmatch);
+  if (output.delete) DeleteSubset (&db, spectrum, Nspectrum, match, Nmatch);
+
+  OutputSubset (spectrum, Nspectrum, match, Nmatch);
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/Makefile	(revision 22322)
@@ -0,0 +1,76 @@
+default: kapa
+help:
+	@echo "make options: kapa (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/kapa
+LIB	=	$(HOME)/lib
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+EDIR	=	$(HOME)/event
+GDIR	=	$(HOME)/graph
+SDIR	=	$(HOME)/setup
+INC	=	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+LDDEPS  = $(DESTLIB)/libkapa.a $(DESTLIB)/libdvo.a $(DESTLIB)/libFITS.a $(DESTLIB)/libohana.a
+INDEPS  = $(DESTINC)/kapa.h $(DESTINC)/dvo.h $(DESTINC)/gfitsio.h $(DESTINC)/ohana.h
+
+kapa: $(BIN)/kapa.$(ARCH)
+install: $(DESTBIN)/kapa
+
+# local source / object files ########################
+EOBJ = \
+$(EDIR)/CheckPipe.$(ARCH).o               $(EDIR)/Stop.$(ARCH).o               \
+$(EDIR)/EventLoop.$(ARCH).o         	  $(EDIR)/Reconfig.$(ARCH).o           \
+$(EDIR)/SetFont.$(ARCH).o           	  $(EDIR)/PositionPicture.$(ARCH).o\
+$(EDIR)/InterpretKeys.$(ARCH).o     	  $(EDIR)/Refresh.$(ARCH).o		\
+$(EDIR)/SetSection.$(ARCH).o		  $(EDIR)/FlushDisplay.$(ARCH).o	\
+$(EDIR)/Resize.$(ARCH).o
+
+GOBJ = \
+$(GDIR)/DrawObjects.$(ARCH).o             $(GDIR)/DrawFrame.$(ARCH).o       	\
+$(GDIR)/DrawText.$(ARCH).o		  $(GDIR)/LoadObject.$(ARCH).o		\
+$(GDIR)/ErasePlot.$(ARCH).o		  $(GDIR)/LoadBox.$(ARCH).o		\
+$(GDIR)/SetLimits.$(ARCH).o               $(GDIR)/PSObjects.$(ARCH).o		\
+$(GDIR)/DrawLabels.$(ARCH).o              $(GDIR)/LoadLabels.$(ARCH).o		\
+$(GDIR)/cursor.$(ARCH).o 		  $(GDIR)/PSFrame.$(ARCH).o		\
+$(GDIR)/LoadPtext.$(ARCH).o		  $(GDIR)/DrawPtext.$(ARCH).o		\
+$(GDIR)/PSLabels.$(ARCH).o		  $(GDIR)/PSPtext.$(ARCH).o		\
+$(GDIR)/PPMit.$(ARCH).o	          \
+
+BDRAW = \
+$(GDIR)/bDrawIt.$(ARCH).o	  	  $(GDIR)/bDrawFrame.$(ARCH).o          \
+$(GDIR)/bDrawLabels.$(ARCH).o             $(GDIR)/bDrawObjects.$(ARCH).o        \
+$(GDIR)/PNGit.$(ARCH).o \
+
+SOBJ = \
+$(SDIR)/CheckColors.$(ARCH).o             $(SDIR)/MakeGC.$(ARCH).o             \
+$(SDIR)/CheckDisplayName.$(ARCH).o  	  $(SDIR)/MapWindow.$(ARCH).o          \
+$(SDIR)/CheckFontName.$(ARCH).o     	  $(SDIR)/NameWindow.$(ARCH).o         \
+$(SDIR)/CheckGeometry.$(ARCH).o     	  $(SDIR)/OpenDisplay.$(ARCH).o        \
+$(SDIR)/CloseDisplay.$(ARCH).o      	  $(SDIR)/QuitX.$(ARCH).o              \
+$(SDIR)/CreateWindow.$(ARCH).o      	  $(SDIR)/SetNormalHints.$(ARCH).o     \
+$(SDIR)/DefineLayout.$(ARCH).o      	  $(SDIR)/SetUpDisplay.$(ARCH).o       \
+$(SDIR)/SetUpWindow.$(ARCH).o        	  $(SDIR)/args.$(ARCH).o               \
+$(SDIR)/GetColor.$(ARCH).o          	  $(SDIR)/SetWMHints.$(ARCH).o         \
+$(SDIR)/LoadFont.$(ARCH).o          	  $(SDIR)/TopWindow.$(ARCH).o          \
+$(SDIR)/Ximage.$(ARCH).o             \
+$(SDIR)/MakeCursor.$(ARCH).o
+
+OBJ  =  $(EOBJ) $(GOBJ) $(SOBJ) $(BDRAW)
+
+# dependancy rules for include files ########################
+$(OBJ): $(INC)/Ximage.h \
+	$(INC)/constants.h \
+	$(INC)/structures.h \
+	$(INC)/prototypes.h
+
+$(OBJ): $(INDEPS) $(LDDEPS)
+
+$(BIN)/kapa.$(ARCH): $(OBJ)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,31 @@
+
+- kapa 1.6 : 2006.08.23
+  * some PNG fixes
+  * converted to gfits APIs (forces libfits 1.6)
+  * removed items moved to libkapa
+
+- kapa 1.5
+  * fixed residual label text
+  * fixed box -axis,-label,-ticks bug (was ignoring user input) 
+  * dropped unused axis.label (is replaced by label.text)
+  * fixed bug in event loop which missed certain mask types
+  * fixed bDrawLabel error
+  * changed comms to libkapa functions
+
+- kapa 1.4
+  * major work to allow noX operation
+  * added bDraw functions
+  * integrated with libkapa
+
+kapa-1-3: released 2005.10.20
+  changed cursor interaction (returns name for keys)
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+kapa-1-2: released 2005.08.15
+  cleaned up complaints from -Wall -Werror.  mostly defining 
+  prototypes, removing unused variables, pushing Graphcolors
+    into the MakeColormap function 
+
+kapa-1-1:
+  fixed up the event loop to reduce excess cpu spinning.
+  cleaned up the color structure
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/VERSIONS
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/VERSIONS	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/VERSIONS	(revision 22322)
@@ -0,0 +1,5 @@
+
+tag names used by kapa:
+
+TAG         : Comment
+kapa-1-0    : first version under CVS
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/input
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/input	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/input	(revision 22322)
@@ -0,0 +1,87 @@
+
+macro colortest
+  lim 0 1 0 1; clear; box
+
+  style -x 2 -pt 0 -sz 3.0
+  dot -c black     0.1 0.9
+  dot -c white     0.2 0.9
+  dot -c red       0.3 0.9
+  dot -c pink      0.4 0.9
+  dot -c orange    0.5 0.9
+  dot -c yellow    0.1 0.8
+  dot -c wheat     0.2 0.8
+  dot -c gold      0.3 0.8
+  dot -c green     0.4 0.8
+  dot -c darkgreen 0.5 0.8
+  dot -c blue      0.1 0.7
+  dot -c skyblue   0.2 0.7
+  dot -c indigo    0.3 0.7
+  dot -c violet    0.4 0.7
+  dot -c grey10    0.5 0.7
+  dot -c grey20    0.1 0.6
+  dot -c grey30    0.2 0.6
+  dot -c grey40    0.3 0.6
+  dot -c grey50    0.4 0.6
+  dot -c grey60    0.5 0.6
+  dot -c grey70    0.1 0.5
+  dot -c grey80    0.2 0.5
+  dot -c grey90    0.3 0.5
+end
+
+macro colortest2
+  lim 0 1 0 1; clear; box
+
+  style -x 2 -pt 7 -sz 3.0
+  dot -c black     0.1 0.9
+  dot -c white     0.2 0.9
+  dot -c red       0.3 0.9
+  dot -c pink      0.4 0.9
+  dot -c orange    0.5 0.9
+  dot -c yellow    0.1 0.8
+  dot -c wheat     0.2 0.8
+  dot -c gold      0.3 0.8
+  dot -c green     0.4 0.8
+  dot -c darkgreen 0.5 0.8
+  dot -c blue      0.1 0.7
+  dot -c skyblue   0.2 0.7
+  dot -c indigo    0.3 0.7
+  dot -c violet    0.4 0.7
+  dot -c grey10    0.5 0.7
+  dot -c grey20    0.1 0.6
+  dot -c grey30    0.2 0.6
+  dot -c grey40    0.3 0.6
+  dot -c grey50    0.4 0.6
+  dot -c grey60    0.5 0.6
+  dot -c grey70    0.1 0.5
+  dot -c grey80    0.2 0.5
+  dot -c grey90    0.3 0.5
+end
+
+macro pltest
+  lim 0 1 0 1; clear; box
+
+  style -x 2 -pt 7 -sz 3.0 -lw 2
+  dot -c black     0.1 0.9
+  dot -c white     0.2 0.9
+  dot -c red       0.3 0.9
+  dot -c pink      0.4 0.9
+  dot -c orange    0.5 0.9
+  dot -c yellow    0.1 0.8
+  dot -c wheat     0.2 0.8
+  dot -c gold      0.3 0.8
+  dot -c green     0.4 0.8
+  dot -c darkgreen 0.5 0.8
+  dot -c blue      0.1 0.7
+  dot -c skyblue   0.2 0.7
+  dot -c indigo    0.3 0.7
+  dot -c violet    0.4 0.7
+  dot -c grey10    0.5 0.7
+  dot -c grey20    0.1 0.6
+  dot -c grey30    0.2 0.6
+  dot -c grey40    0.3 0.6
+  dot -c grey50    0.4 0.6
+  dot -c grey60    0.5 0.6
+  dot -c grey70    0.1 0.5
+  dot -c grey80    0.2 0.5
+  dot -c grey90    0.3 0.5
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/doc/notes.txt	(revision 22322)
@@ -0,0 +1,36 @@
+
+2005.11.27
+
+	I am improving the functionality of kapa so that it does not
+	need to have an X server to build PNG / PPM / PS plots.
+	Before, I generated the PNG / PPM plots from the screen image
+	by using an X function to examine the color of each pixel and
+	generate the corresponding PNG/PPM image.  This implied that
+	an X server was required even if the window was not being
+	looked at.  The PS version of the plots were not quite so
+	difficult: the PS code is generated from the plot when the PS
+	output file is constructed.  I decided to implement the
+	equivalent concept for the PNG/PPM images. 
+
+	I have build a set of drawing functions which are used by the
+	PNG function (PPM not yet implemented) to create the PNG image
+	buffer from the graphic objects stored by Kapa.  This
+	subsystem is called bDraw.  Most of the drawing functions
+	correspond to just the primitive point, line, and circle
+	functions.  I have implemented bDrawPoint, bDrawLine,
+	bDrawCircle, and demonstrated the complete concept.  At this
+	point, only a few features are missing:
+
+	- line weight.  the bDraw functions do not respect the value
+          of the line weight (which is available to them).  All lines
+          and circles are drawn as line weight 1.
+
+	- line type.  no line type data is used. (Kapa probably over
+          respects it: it should not apply the line type to the point
+          plotting)
+
+	- rectangles and triangles.  several of the point-types are
+          constructed by calling bDrawRect, bDrawTri, neither of which
+          are implemented at this time.
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/CheckPipe.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/CheckPipe.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/CheckPipe.c	(revision 22322)
@@ -0,0 +1,133 @@
+# include "Ximage.h"
+# define STRCONST(A) ((int)(0x1000000*A[0] + 0x10000*A[1] + 0x100*A[2] + 0x1*A[3]))
+
+int CheckPipe () {
+
+  int status;
+  char buffer[32];
+
+  /***** read (4 byte) message word from socket ****/
+  status = read (sock, buffer, 4);
+  buffer[4] = 0;
+  switch (status) {
+  case -1:                          /* no input from pipe: continue */
+    return (TRUE);
+    break;
+
+  case 0:
+    fprintf (stderr, "pipe has died!\n");
+    return (FALSE);
+    break;
+
+  case 4:
+    break;
+
+  default:
+    fprintf (stderr, "weird signal: too many or few bytes!  %d\n", status);
+    return (TRUE);
+    break;
+  }
+  
+  /***** handle different messages ****/
+  if (!strcmp (buffer, "QUIT")) return (FALSE);
+  
+  if (!strcmp (buffer, "PSIT")) {
+    status = PSit ();
+    write (sock, "DONE", 4);
+    return (status);
+  }
+  
+  if (!strcmp (buffer, "PNGF")) {
+    status = PNGit ();
+    write (sock, "DONE", 4);
+    return (status);
+  }
+  
+  if (!strcmp (buffer, "PPMF")) {
+    status = PPMit ();
+    write (sock, "DONE", 4);
+    return (status);
+  }
+  
+  if (!strcmp (buffer, "RSIZ")) {
+    status = Resize ();
+    return (status);
+  }
+
+  if (!strcmp (buffer, "PLOT")) {
+    status = LoadObject ();
+    return (status);
+  }
+  
+  if (!strcmp (buffer, "LIMS")) {
+    KiiSendMessage (sock, "%8.1f %8.1f ", 
+		    section[TheSection].axis[0].dfx, 
+		    section[TheSection].axis[1].dfy);
+    return (TRUE);
+  }
+  
+  if (!strcmp (buffer, "SLIM")) {
+    status = SetLimits ();
+    return (TRUE);
+  }
+  
+  if (!strcmp (buffer, "LABL")) {
+    status = LoadLabels ();
+    return (TRUE);
+  }
+  
+  if (!strcmp (buffer, "LSEC")) {
+    status = ListSection ();
+    return (TRUE);
+  }
+  
+  if (!strcmp (buffer, "DSEC")) {
+    status = SetSection (FALSE);
+    return (TRUE);
+  }
+  
+  if (!strcmp (buffer, "SSEC")) {
+    status = SetSection (TRUE);
+    return (TRUE);
+  }
+  
+  if (!strcmp (buffer, "PTXT")) {
+    status = LoadPtext ();
+    return (TRUE);
+  }
+  
+  if (!strcmp (buffer, "FONT")) {
+    status = SetFont ();
+    return (TRUE);
+  }
+  
+  if (!strcmp (buffer, "CURS")) {
+    cursor ();
+    return (TRUE);
+  }
+  
+  /* Erase Section */
+  if (!strcmp (buffer, "ERAS")) {
+    status = ErasePlot (TRUE);
+    return (status);
+  }
+  
+  /* Don't Erase Section */
+  if (!strcmp (buffer, "ERSS")) {
+    status = ErasePlot (FALSE);
+    return (status);
+  }
+  
+  if (!strcmp (buffer, "DBOX")) {
+    status = LoadBox ();
+    return (status);
+  }
+  
+  fprintf (stderr, "unknown signal %s\n", buffer);
+
+  return (TRUE);
+
+}
+
+
+    
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/EventLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/EventLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/EventLoop.c	(revision 22322)
@@ -0,0 +1,139 @@
+# include "Ximage.h"
+
+/* # define IgnoreMask (long) (ButtonPressMask | ClientMessage | ButtonReleaseMask | KeyPressMask | PointerMotionMask) */
+
+/* list events being selected below, all other masks are ignored */ 
+# define IgnoreMask (long) (~(StructureNotifyMask | SubstructureNotifyMask | ExposureMask))
+
+int LastEvent (Display *display, int type, XEvent *event) {
+
+  int found;
+
+  found = FALSE;
+  while (XCheckTypedEvent (display, type, event)) {
+    found = TRUE;
+  }
+  return (found);
+}
+
+int EventLoop () {
+  
+  XEvent          event;
+  int             status;
+  Display        *display;
+  
+  if (USE_XWINDOW) Refresh (1);
+
+  status = TRUE;
+  while (status) {
+    
+    if (!CheckPipe ()) return (FALSE);
+    
+    if (!USE_XWINDOW) {
+      usleep (50000);
+      continue;
+    }
+
+    if (XEventsQueued (graphic.display, QueuedAfterFlush) < 1) {
+	/* fprintf (stderr, "."); */
+      usleep (50000);
+      continue;
+    }
+
+    display = graphic.display;
+
+    /* grab the last entry for these events */
+    if (LastEvent (display, ConfigureNotify, &event)) Reconfig (&event);
+    if (LastEvent (display, CirculateNotify, &event)) Reconfig (&event);
+    if (LastEvent (display, Expose,          &event)) Refresh (1);
+
+    if (LastEvent (display, MappingNotify,   &event)) XRefreshKeyboardMapping ((XMappingEvent *) &event);
+
+    /* drop and ignore the following StructureNotifyMask events */
+    LastEvent (display, GravityNotify, &event);
+    LastEvent (display, ReparentNotify, &event);
+    LastEvent (display, MapNotify, &event);
+    LastEvent (display, UnmapNotify, &event);
+
+    /* remove those events we will ignore */
+    while (XCheckMaskEvent (display, IgnoreMask, &event)) continue;
+
+    /* events to remove which have no mask component */
+    while (XCheckTypedEvent (display, MappingNotify, &event)) continue;
+    while (XCheckTypedEvent (display, ClientMessage, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionClear, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionNotify, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionRequest, &event)) continue;
+  }
+  return (status);
+}
+
+# if (0)
+
+/* all masks from X.h for reference: */
+
+#define NoEventMask                     0L
+#define KeyPressMask                    (1L<<0)
+#define KeyReleaseMask                  (1L<<1)
+#define ButtonPressMask                 (1L<<2)
+#define ButtonReleaseMask               (1L<<3)
+#define EnterWindowMask                 (1L<<4)
+#define LeaveWindowMask                 (1L<<5)
+#define PointerMotionMask               (1L<<6)
+#define PointerMotionHintMask           (1L<<7)
+#define Button1MotionMask               (1L<<8)
+#define Button2MotionMask               (1L<<9)
+#define Button3MotionMask               (1L<<10)
+#define Button4MotionMask               (1L<<11)
+#define Button5MotionMask               (1L<<12)
+#define ButtonMotionMask                (1L<<13)
+#define KeymapStateMask                 (1L<<14)
+#define ExposureMask                    (1L<<15)
+#define VisibilityChangeMask            (1L<<16)
+#define StructureNotifyMask             (1L<<17)
+#define ResizeRedirectMask              (1L<<18)
+#define SubstructureNotifyMask          (1L<<19)
+#define SubstructureRedirectMask        (1L<<20)
+#define FocusChangeMask                 (1L<<21)
+#define PropertyChangeMask              (1L<<22)
+#define ColormapChangeMask              (1L<<23)
+#define OwnerGrabButtonMask             (1L<<24)
+
+/* all events from X.h for reference: */
+
+#define KeyPress                2
+#define KeyRelease              3
+#define ButtonPress             4
+#define ButtonRelease           5
+#define MotionNotify            6
+#define EnterNotify             7
+#define LeaveNotify             8
+#define FocusIn                 9
+#define FocusOut                10
+#define KeymapNotify            11
+#define Expose                  12
+#define GraphicsExpose          13
+#define NoExpose                14
+#define VisibilityNotify        15
+#define CreateNotify            16
+#define DestroyNotify           17
+#define UnmapNotify             18
+#define MapNotify               19
+#define MapRequest              20
+#define ReparentNotify          21
+#define ConfigureNotify         22
+#define ConfigureRequest        23
+#define GravityNotify           24
+#define ResizeRequest           25
+#define CirculateNotify         26
+#define CirculateRequest        27
+#define PropertyNotify          28
+#define SelectionClear          29
+#define SelectionRequest        30
+#define SelectionNotify         31
+#define ColormapNotify          32
+#define ClientMessage           33
+#define MappingNotify           34
+#define LASTEvent               35      /* must be bigger than any event # */
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/FlushDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/FlushDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/FlushDisplay.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "Ximage.h"
+
+static struct timeval reftime; 
+static char reftimeset = FALSE;
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+void FlushDisplay () {
+
+  struct timeval now;
+  int flush;
+  double dtime;
+
+  if (!USE_XWINDOW) return;
+
+  flush = FALSE;
+  if (!reftimeset) {
+    flush = TRUE;
+    gettimeofday (&reftime, NULL);
+  } 
+
+  gettimeofday (&now, NULL);
+  dtime = DTIME (now, reftime);
+
+  if (dtime > 0.1) {
+    flush = TRUE;
+  }
+
+  if (flush) {
+    XFlush (graphic.display);
+    reftime = now;
+  }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/InterpretKeys.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/InterpretKeys.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/InterpretKeys.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage.h"
+
+int InterpretKeys (XEvent *event) {
+
+  int    status;
+  KeySym keysym;
+  char            string[10];
+  XComposeStatus  composestatus;
+
+  XLookupString ((XKeyEvent *)event, string, 9, &keysym, &composestatus);
+
+  switch (keysym) {
+
+  default:
+    status = TRUE;
+  }
+
+  return (status);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/InterpretPresses.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/InterpretPresses.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/InterpretPresses.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "Ximage.h"
+
+int 
+InterpretPresses (graphic, layout, event)
+Graphic        graphic[];
+Layout         layout[];
+XButtonEvent   event[];
+{
+
+  int             status, done, this_button, x, y, old_cursor;
+  KeySym          keysym;
+  Button         *button;
+
+  status = TRUE;
+  this_button = event[0].button;
+  
+  if ((event[0].type == ButtonPress) && InPicture (event, &layout[0].picture)) {
+    Reorient (graphic, layout, event);
+  }
+
+  if ((event[0].type == ButtonPress) && InPicture (event, &layout[0].cmapbar)) {
+    DragColorbar (graphic, layout, event);
+  }
+
+  /* if on an exisiting button, Invert, wait for release, then go (or not) */
+  if ((button = CheckButtons (event, layout)) != (Button *) NULL) {
+    InvertButton (graphic, button); 
+    done = FALSE;
+    while (!done) { /* wait for release of this button */
+      XNextEvent (graphic[0].display, (XEvent *) event);
+      if ((event[0].type == ButtonRelease) && (event[0].button == this_button)) {
+	done = TRUE;
+      }
+    }
+    DrawButton (graphic, button);
+    if (InButton (event, button)) {
+      switch (event[0].button) {
+      case 1:
+	status = button[0].function_1(graphic, layout);
+	break;
+      case 2:
+	status = button[0].function_2(graphic, layout);
+	break;
+      case 3:
+	status = button[0].function_3(graphic, layout);
+	break;
+      }
+    }
+    else {
+      return (status);
+    }
+  }
+
+  return (status);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/PositionPicture.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/PositionPicture.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/PositionPicture.c	(revision 22322)
@@ -0,0 +1,95 @@
+# include "Ximage.h"
+
+void PositionPicture (Layout *layout) {
+
+  int fontsize, bump, Nc;
+  double PADx, PADy, Dx, Dy;
+  double PXm, PXp, PYm, PYp;
+  double X0, Y0, dX, dY;
+  char string[64], *fontname;
+
+  fontname = GetRotFont (&fontsize);
+
+  /* window has outer bounding box with margins Wx, Wy 
+     section commands subdivide the internal box (Dx,Dy)
+   */
+
+  PADx = MAX (graphic.dx / 20.0, fontsize);
+  PADy = MAX (graphic.dy / 20.0, fontsize);
+  Dx = graphic.dx - 2*PADx;
+  Dy = graphic.dy - 2*PADy;
+
+  /* each graph has a padding PXm, PXp, PYm, PYp */
+  PXm = (layout[0].axis[1].islabel) ? 4*fontsize : 0;
+  PXp = (layout[0].axis[3].islabel) ? 4*fontsize : 0;
+  PYm = (layout[0].axis[0].islabel) ? 4*fontsize : 0;
+  PYp = (layout[0].axis[2].islabel) ? 4*fontsize : 0;
+ 
+  X0 = PADx + PXm + (Dx * layout[0].x);
+  Y0 = PADy + PYm + (Dy * layout[0].y);
+  dX = (Dx * layout[0].dx) - PXp - PXm;
+  dY = (Dy * layout[0].dy) - PYp - PYm;
+
+  /* define locations of coordinate axes */
+  layout[0].axis[0].fx  = X0;
+  layout[0].axis[0].fy  = graphic.dy - Y0;
+  layout[0].axis[0].dfx = dX;
+  layout[0].axis[0].dfy = 0;
+  
+  layout[0].axis[1].fx  = X0;
+  layout[0].axis[1].fy  = graphic.dy - Y0;
+  layout[0].axis[1].dfx = 0;
+  layout[0].axis[1].dfy = -dY;
+
+  layout[0].axis[2].fx  = X0;
+  layout[0].axis[2].fy  = graphic.dy - Y0 - dY;
+  layout[0].axis[2].dfx = dX;
+  layout[0].axis[2].dfy = 0;
+
+  layout[0].axis[3].fx  = X0 + dX;
+  layout[0].axis[3].fy  = graphic.dy - Y0;
+  layout[0].axis[3].dfx = 0;
+  layout[0].axis[3].dfy = -dY;
+
+  PADx = 0.8*fontsize + 2;
+  PADy = 3.0*fontsize + 4;
+
+  /* define locations of axis labels */
+  layout[0].label[LABELX0].x = layout[0].axis[0].fx + 0.5*layout[0].axis[0].dfx;
+  bump = (layout[0].axis[0].islabel) ? PADy : PADx;
+  layout[0].label[LABELX0].y = layout[0].axis[0].fy + bump;
+
+  layout[0].label[LABELX1].x = layout[0].axis[2].fx + 0.5*layout[0].axis[2].dfx;
+  bump = (layout[0].axis[2].islabel) ? PADy : PADx;
+  layout[0].label[LABELX1].y = layout[0].axis[2].fy - bump;
+
+  sprintf (string, "%4g", layout[0].axis[1].min);
+  Nc = strlen (string);
+  sprintf (string, "%4g", layout[0].axis[1].max);
+  Nc = MAX (Nc, strlen (string));
+
+  layout[0].label[LABELY0].y = layout[0].axis[1].fy + 0.5*layout[0].axis[1].dfy;
+  bump = (layout[0].axis[1].islabel) ? (0.8*Nc*fontsize + 1) : PADx;
+  layout[0].label[LABELY0].x = layout[0].axis[1].fx - bump;
+
+  sprintf (string, "%4g", layout[0].axis[1].min);
+  Nc = strlen (string);
+  sprintf (string, "%4g", layout[0].axis[1].max);
+  Nc = MAX (Nc, strlen (string));
+
+  layout[0].label[LABELY1].y = layout[0].axis[3].fy + 0.5*layout[0].axis[3].dfy;
+  bump = (layout[0].axis[3].islabel) ? (0.8*Nc*fontsize + 1) : PADx;
+  layout[0].label[LABELY1].x = layout[0].axis[3].fx + bump;
+  
+  /* these are wrong and have to be adjusted to sit in the corners */
+
+  layout[0].label[LABELUL].x = layout[0].axis[2].fx - PADx;
+  layout[0].label[LABELUL].y = layout[0].axis[2].fy - PADx;
+  layout[0].label[LABELUR].x = layout[0].axis[2].fx + layout[0].axis[2].dfx + PADx;
+  layout[0].label[LABELUR].y = layout[0].axis[2].fy - PADx;
+  layout[0].label[LABELLL].x = layout[0].axis[0].fx - PADx;
+  layout[0].label[LABELLL].y = layout[0].axis[0].fy + PADx;
+  layout[0].label[LABELLR].x = layout[0].axis[0].fx + layout[0].axis[0].dfx + PADx;
+  layout[0].label[LABELLR].y = layout[0].axis[0].fy + PADx;
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Reconfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Reconfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Reconfig.c	(revision 22322)
@@ -0,0 +1,24 @@
+# include "Ximage.h"
+
+int Reconfig (XEvent *event) {
+
+  int i;
+
+  if ((graphic.dx == event[0].xconfigure.width) &&
+      (graphic.dy == event[0].xconfigure.height)) 
+    return (TRUE);
+
+  graphic.dx = MAX(event[0].xconfigure.width,  MIN_WIDTH); 
+  graphic.dy = MAX(event[0].xconfigure.height, MIN_HEIGHT);
+
+  for (i = 0; i < Nsection; i++) {
+    PositionPicture (&section[i]);
+  }
+
+  XClearWindow (graphic.display, graphic.window);
+
+  Refresh (1);
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Refresh.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Refresh.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Refresh.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "Ximage.h"
+
+void Refresh (int mode) {
+
+  int i;
+  
+  if (!USE_XWINDOW) return;
+  if (HAVE_BACKING) return;
+
+  /* XClearWindow   (graphic.display, graphic.window); */
+  XSetForeground (graphic.display, graphic.gc, graphic.back);
+  XFillRectangle (graphic.display, graphic.window, graphic.gc, 0, 0, graphic.dx, graphic.dy);
+  XSetForeground (graphic.display, graphic.gc, graphic.fore);
+  
+  for (i = 0; i < Nsection; i++) {
+    DrawFrame   (&section[i]);
+    DrawObjects (&section[i]);
+    DrawLabels  (&section[i]);
+    DrawPtext   (&section[i]);
+  }
+
+  FlushDisplay ();
+ 
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Resize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Resize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Resize.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "Ximage.h"
+
+int Resize () {
+ 
+  int i;
+  unsigned int NX, NY;
+
+  KiiScanMessage (sock, "%d %d", &NX, &NY);
+
+  if ((graphic.dx == NX) && (graphic.dy == NY)) 
+    return (TRUE);
+
+  graphic.dx = MAX(NX, 50); 
+  graphic.dy = MAX(NY, 50); 
+
+  if (USE_XWINDOW) XResizeWindow (graphic.display, graphic.window, NX, NY);
+
+  for (i = 0; i < Nsection; i++) {
+    PositionPicture (&section[i]);
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic.display, graphic.window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/SetFont.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/SetFont.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/SetFont.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "Ximage.h"
+
+int SetFont () {
+  
+  char buffer[256], name[64];
+  int status, size;
+  
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+  status = read (sock, buffer, 16); 
+  buffer[16] = 0; 
+  sscanf (buffer, "%s", name);
+  
+  status = read (sock, buffer, 16); 
+  buffer[16] = 0; 
+  sscanf (buffer, "%d", &size);
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+  
+  SetRotFont (name, size);
+  
+  return (TRUE);
+  
+}
+
+/*
+  layout[0].X = 0;
+  layout[0].Y = 0;
+  layout[0].expand = 1;
+  */
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/SetSection.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/SetSection.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/SetSection.c	(revision 22322)
@@ -0,0 +1,114 @@
+# include "Ximage.h"
+
+int SetSection (int SwitchSection) {
+  
+  int i, NewSection, ThisSection, MoveSection;
+  char name[128];
+  double x, y, dx, dy;
+
+  if (SwitchSection) {
+    KiiScanMessage (sock, "%s", name);
+  } else {
+    KiiScanMessage (sock, "%s %lf %lf %lf %lf", name, &x, &y, &dx, &dy);
+  }    
+
+  NewSection = FALSE;
+  MoveSection = FALSE;
+  ThisSection = -1;
+  for (i = 0; i < Nsection; i++) {
+    if (!strcmp (name, section[i].name)) {
+      ThisSection = i;
+      break;
+    }
+  }
+
+  if (ThisSection == -1) {
+    if (SwitchSection) {
+      fprintf (stderr, "section %s not found\n", name);
+      return (TRUE);
+    }
+    NewSection = TRUE;
+    Nsection ++;
+    REALLOCATE (section, Layout, Nsection);
+    ThisSection = Nsection - 1;
+    strcpy (section[ThisSection].name, name);
+    section[ThisSection].Nobjects = 0;
+    section[ThisSection].Nptext = 0;
+    ALLOCATE (section[ThisSection].objects, Gobjects, 1);  /* allocate so later free will not crash! */
+    section[ThisSection].objects[0].x   = section[ThisSection].objects[0].y   = section[ThisSection].objects[0].z = (float *) NULL;
+    section[ThisSection].objects[0].dxm = section[ThisSection].objects[0].dxp = (float *) NULL;
+    section[ThisSection].objects[0].dym = section[ThisSection].objects[0].dyp = (float *) NULL;
+    ALLOCATE (section[ThisSection].ptext, Label, 1);       /* allocate so later free will not crash! */
+    for (i = 0; i < 4; i++) {
+      section[ThisSection].axis[i].min = 0;
+      section[ThisSection].axis[i].max = 1;
+      section[ThisSection].axis[i].isaxis = FALSE;
+      section[ThisSection].axis[i].areticks = FALSE;
+      section[ThisSection].axis[i].islabel = FALSE;
+    }    
+    for (i = 0; i < 8; i++) {
+      strcpy (section[ThisSection].label[i].text, "");
+    }
+  }
+
+  if (!SwitchSection) {
+    if (!NewSection && (section[ThisSection].x != x)) MoveSection = TRUE;
+    section[ThisSection].x = x;
+    if (!NewSection && (section[ThisSection].y != y)) MoveSection = TRUE;
+    section[ThisSection].y = y;
+    if (!NewSection && (section[ThisSection].dx != dx)) MoveSection = TRUE;
+    section[ThisSection].dx = dx;
+    if (!NewSection && (section[ThisSection].dy != dy)) MoveSection = TRUE;
+    section[ThisSection].dy = dy;
+
+    if (NewSection || MoveSection) {
+      PositionPicture (&section[ThisSection]);
+    }
+
+    if (MoveSection) {
+      XClearWindow (graphic.display, graphic.window);
+      Refresh (1);
+    } 
+    if (NewSection) {
+    }      
+  }
+
+  TheSection = ThisSection;
+
+  return (TRUE);
+  
+}
+
+int ListSection () {
+  
+  int i, ThisSection;
+  char name[128];
+
+  KiiScanMessage (sock, "%s", name);
+
+  if (!strcmp (name, "*")) {
+    for (i = 0; i < Nsection; i++) {
+      fprintf (stderr, "%s: %6.3f %6.3f %6.3f %6.3f\n", 
+	       section[i].name, section[i].x, section[i].y, section[i].dx, section[i].dy);
+    }
+    return (TRUE);
+  }
+
+  ThisSection = -1;
+  for (i = 0; i < Nsection; i++) {
+    if (!strcmp (name, section[i].name)) {
+      ThisSection = i;
+      break;
+    }
+  }
+  if (ThisSection == -1) {
+    fprintf (stderr, "section %s not found\n", name);
+    return (TRUE);
+  }
+
+  fprintf (stderr, "%s: %6.3f %6.3f %6.3f %6.3f\n", 
+	   section[ThisSection].name, 
+	   section[ThisSection].x, section[ThisSection].y, 
+	   section[ThisSection].dx, section[ThisSection].dy);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Stop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Stop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/event/Stop.c	(revision 22322)
@@ -0,0 +1,8 @@
+# include "Ximage.h"
+
+int Stop () {
+  return (FALSE);
+}
+
+/******  this function looks stupid, but it has to be like this:
+  it is called as a pointer to function **/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawFrame.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawFrame.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawFrame.c	(revision 22322)
@@ -0,0 +1,161 @@
+# include "Ximage.h"
+# define DrawLine(X,Y,DX,DY) (XDrawLine (graphic.display, graphic.window, graphic.gc, (int)(X), (int)(Y), (int)(X+DX), (int)(Y+DY)))
+  
+void DrawFrame (Layout *layout) {
+  
+  int i, fx, fy, dfx, dfy, P, IsLabel;
+  double range, major, minor, first, next;
+
+  DrawRotTextInit (graphic.display, graphic.window, graphic.gc, graphic.fore, graphic.back);
+
+  /* each axis is drawn independently, but ticks and labels are placed according to
+     perpendicular distance. */
+  for (i = 0; i < 4; i++) {
+    fx = layout[0].axis[i].fx;
+    fy = layout[0].axis[i].fy;
+    dfx = layout[0].axis[i].dfx;
+    dfy = layout[0].axis[i].dfy;
+    P = hypot ((double)layout[0].axis[(i+1)%2].dfx, (double)layout[0].axis[(i+1)%2].dfy);
+
+    if (layout[0].axis[i].isaxis) {
+      DrawLine (fx, fy, dfx, dfy);
+    }
+    
+    if (layout[0].axis[i].areticks) {
+      range = layout[0].axis[i].max - layout[0].axis[i].min;
+      AxisTickScale (&layout[0].axis[i], &major, &minor);
+
+      first = minor*((int)(layout[0].axis[i].min/minor));
+      if ((range > 0) && (first < layout[0].axis[i].min)) {
+	first += minor;
+      }
+      if ((range < 0) && (first > layout[0].axis[i].min)) {
+	first -= minor;
+      }
+      for (next = first; ((range > 0) && (next <= layout[0].axis[i].max)) || ((range < 0) && (next >= layout[0].axis[i].max));) {
+	IsLabel = FALSE;
+	if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || 
+	    (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)) ||
+	    (fabs ((int)((next - 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)))
+	  IsLabel = layout[0].axis[i].islabel;
+	DrawTick (fx, fy, dfx, dfy, P, layout[0].axis[i].min, layout[0].axis[i].max, next, IsLabel, i);
+	if (range > 0) 
+	  next += minor;
+	else 
+	  next -= minor;
+      }
+    }
+  }
+}
+
+void DrawTick (int fx, int fy, int dfx, int dfy, 
+	       int P, double min, double max, 
+	       double value, int mode, int naxis) {
+  
+  int x, y, dx, dy, pos, dir, fontsize;
+  double size, n;
+  char string[64], *fontname;
+
+  if (mode) { 
+    size = MAX (0.02, 7.0 / P); 
+  } else {
+    size = MAX (0.01, 4.0 / P); 
+  }
+  
+  n = P / sqrt ((double)(dfx*dfx + dfy*dfy));
+  x = fx + (value-min)*dfx/(max - min);
+  y = fy + (value-min)*dfy/(max - min);
+
+  dir = +1;
+  if ((naxis == 0) || (naxis == 1)) dir *= -1;
+  dx = dir*size*dfy*n;	
+  dy = dir*size*dfx*n;
+  
+  DrawLine (x, y, dx, dy);
+
+# ifdef IN_AND_OUT_TICKS
+# define GAP 0.03
+# else
+# define GAP 0.01
+# endif
+
+  if (mode == 1) {
+    fontname = GetRotFont (&fontsize);
+    pos = 0;
+    
+    /* temporarily assume rectilinear axes */
+    if (naxis == 0) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 1; }
+    if (naxis == 2) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 7; }
+
+    if (naxis == 1) { dy = 0; dx = -(0.8*fontsize + 1); pos = 3; }
+    if (naxis == 3) { dy = 0; dx = +(0.8*fontsize + 1); pos = 5; }
+
+    x = fx + (value-min)*dfx/(max - min) + dx;
+    y = fy + (value-min)*dfy/(max - min) + dy;
+    if (fabs(value/(max-min)) < 0.001) { value = 0.0; }
+    sprintf (string, "%4g", value);
+    DrawRotText (x, y, string, pos, 0.0);
+  }
+  
+}
+
+
+void AxisTickScale (Axis *axis, double *major, double *minor) {
+
+  double range, lrange, factor, mantis, fmantis, power;
+
+  range = axis[0].max - axis[0].min;
+
+  lrange = log10(MAX(fabs(range), 1e-30));
+  mantis = modf (lrange, &factor);
+  if (mantis < 0.0) {
+    mantis += 1.0;
+    factor -= 1.0;
+  }
+  
+  power = pow(10.0, factor);
+  fmantis = pow(10.0, mantis);
+  *major = 0.5 * power;
+  *minor = 0.1 * power;
+  
+  if ((fmantis >= 1.0) && (fmantis <  1.999)) {
+    *major = 0.5 * power;
+    *minor = 0.1 * power;
+    if (axis[0].areticks == 1) {
+      *major = 1.0 * power;
+      *minor = 0.2 * power;
+    }	  
+  }
+  if ((fmantis >= 1.999) && (fmantis <  3.999)) {
+    *major = 1.0 * power;
+    *minor = 0.2 * power;
+    if (axis[0].areticks == 1) {
+      *major = 2.0 * power;
+      *minor = 0.5 * power;
+    }	  
+  }
+  if ((fmantis >= 3.999) && (fmantis <  5.999)) {
+    *major = 1.0 * power;
+    *minor = 0.5 * power;
+    if (axis[0].areticks == 1) {
+      *major = 2.0 * power;
+      *minor = 0.5 * power;
+    }	  
+  }
+  if ((fmantis >= 5.999) && (fmantis <   7.999)) {
+    *major = 2.0 * power;
+    *minor = 0.5 * power;
+    if (axis[0].areticks == 1) {
+      *major = 2.5 * power;
+      *minor = 0.5 * power;
+    }	  
+  }
+  if ((fmantis >= 7.999) && (fmantis <  10.000)) {
+    *major = 2.5 * power;
+    *minor = 0.5 * power;
+    if (axis[0].areticks == 1) {
+      *major = 5.0 * power;
+      *minor = 1.0 * power;
+    }	  
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawLabels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawLabels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawLabels.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "Ximage.h"
+  
+void DrawLabels (Layout *layout) {
+  
+  int i, pos, x, y, size;
+  double angle;
+  char *fontname;
+
+  pos = 0;
+  fontname = GetRotFont (&size);
+  XSetForeground (graphic.display, graphic.gc, graphic.fore);
+  DrawRotTextInit (graphic.display, graphic.window, graphic.gc, graphic.fore, graphic.back);
+
+  /* each label is drawn independently */
+  for (i = 0; i < 8; i++) {
+    if (strcmp (layout[0].label[i].text, "")) {
+      angle = 0;
+      switch (i) {
+      case 0: pos = 7; break;
+      case 1: pos = 1; angle = -90; break;
+      case 2: pos = 1; break;
+      case 3: pos = 1; angle =  90; break;
+      case 4: pos = 2; break;
+      case 5: pos = 0; break;
+      case 6: pos = 8; break;
+      case 7: pos = 6; break;
+      }	
+      x = layout[0].label[i].x;
+      y = layout[0].label[i].y;
+      SetRotFont (layout[0].label[i].font, layout[0].label[i].size);
+      DrawRotText (x, y, layout[0].label[i].text, pos, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /*
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|       |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+          
+ 6____7___8 
+  |       | 
+  |       | 
+ 3|   4   |5
+  |       |
+  |       |
+  ---------
+  0   1   2
+
+  */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawObjects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawObjects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawObjects.c	(revision 22322)
@@ -0,0 +1,619 @@
+# include "Ximage.h"
+# define DrawLine(X1,Y1,X2,Y2) (XDrawLine (graphic.display, graphic.window, graphic.gc, (int)(X1+0.5), (int)(Y1+0.5), (int)(X2+0.5), (int)(Y2+0.5)))
+# define DrawRectangle(X1,Y1,X2,Y2) (XDrawRectangle (graphic.display, graphic.window, graphic.gc, (int)(X1+0.5), (int)(Y1+0.5), (int)(X2+0.5), (int)(Y2+0.5)))
+# define FillRectangle(X1,Y1,X2,Y2) (XFillRectangle (graphic.display, graphic.window, graphic.gc, (int)(X1+0.5), (int)(Y1+0.5), (int)(X2+0.5), (int)(Y2+0.5)))
+# define DrawCircle(X,Y,R) (XDrawArc (graphic.display, graphic.window, graphic.gc, (int)(X-R+0.5), (int)(Y-R+0.5), abs(2*R+0.5), abs(2*R+0.5), 0, 23040))
+
+# define CONNECT 0
+# define HISTOGRAM 1
+# define POINTS 2
+
+void ClipLine (double x0, double y0, double x1, double y1, double X0, double Y0, double X1, double Y1);
+
+/* draw all objects for this Layout */
+int DrawObjects (Layout *layout) {
+  
+  int i;
+  
+  for (i = 0; i < layout[0].Nobjects; i++) {
+    if (DEBUG) fprintf (stderr, "object: %d\n", i);
+    if (DEBUG) fprintf (stderr, "Npts: %d\n", layout[0].objects[i].Npts);
+    DrawObjectN (layout, &layout[0].objects[i]);
+  }    
+  return (TRUE);
+}
+
+/* Draw a specific object in the layout */
+int DrawObjectN (Layout *layout, Gobjects *object) {
+  
+  static char dash[2] = {5,10};
+  static char dot[2] = {3,3};
+  int lweight;
+  
+  lweight = MAX (0, MIN (10, object[0].lweight));
+
+  /* set line type */
+  switch (object[0].ltype) {
+  case 0:
+    XSetLineAttributes (graphic.display, graphic.gc, lweight, LineSolid, CapNotLast, JoinMiter);
+    break;
+  case 1:
+    XSetDashes (graphic.display, graphic.gc, 100, dash, 2);
+    XSetLineAttributes (graphic.display, graphic.gc, lweight, LineOnOffDash, CapNotLast, JoinMiter);
+    break;
+  case 2:
+    XSetDashes (graphic.display, graphic.gc, 10, dot, 2);
+    XSetLineAttributes (graphic.display, graphic.gc, lweight, LineOnOffDash, CapNotLast, JoinMiter);
+    break;
+  default:
+    XSetLineAttributes (graphic.display, graphic.gc, lweight, LineSolid, CapNotLast, JoinMiter);
+    break;
+  }
+
+  XSetForeground (graphic.display, graphic.gc, graphic.color[object[0].color]);
+
+  switch (object[0].style) {
+  case CONNECT: 
+    DrawConnect (layout, object);
+    break;
+  case HISTOGRAM:
+    DrawHistogram (layout, object);
+    break;
+  case POINTS:
+    DrawPoints (layout, object);
+    break;
+  }
+    
+  if (object[0].etype & 0x01) {
+    DrawYErrors (layout, object);
+  }
+  if (object[0].etype & 0x02) {
+    DrawXErrors (layout, object);
+  }
+
+  XSetLineAttributes (graphic.display, graphic.gc, 0, LineSolid, CapNotLast, JoinMiter);
+  XSetForeground (graphic.display, graphic.gc, graphic.fore);
+  return (TRUE);
+}
+
+/******/
+void DrawConnect (Layout *layout, Gobjects *object) {
+  
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1;
+  double X0, X1, Y0, Y1;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = layout[0].axis[0].fx;
+  X1 = layout[0].axis[0].fx + layout[0].axis[0].dfx;
+  Y0 = layout[0].axis[1].fy;
+  Y1 = layout[0].axis[1].fy + layout[0].axis[1].dfy;
+
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    
+    ClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
+    /* DrawLine (sx0, sy0, sx1, sy1); */
+    sx0 = sx1; sy0 = sy1;
+  }
+  
+}
+
+void ClipLine (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0) {
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x0,y0 if outside box */
+  if ((x0 < X0) && (x1 >= X0)) {
+    y0 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X0;
+  }
+  if ((x0 > X1) && (x1 <= X1)) {
+    y0 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X1;
+  }
+  if ((y0 < Y0) && (y1 >= Y0)) {
+    x0 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y0;
+  }
+  if ((y0 > Y1) && (y1 <= Y1)) {
+    x0 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y1;
+  }
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x1,y1 if outside box */
+  if ((x1 < X0) && (x0 >= X0)) {
+    y1 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X0;
+  }
+  if ((x1 > X1) && (x0 <= X1)) {
+    y1 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X1;
+  }
+  if ((y1 < Y0) && (y0 >= Y0)) {
+    x1 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y0;
+  }
+  if ((y1 > Y1) && (y0 <= Y1)) {
+    x1 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y1;
+  }
+  DrawLine (x0, y0, x1, y1);
+}
+  
+
+/******/
+void DrawHistogram (Layout *layout, Gobjects *object) {
+
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sxa;
+  double X0, X1, Y0, Y1;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = layout[0].axis[0].fx;
+  X1 = layout[0].axis[0].fx + layout[0].axis[0].dfx;
+  Y0 = layout[0].axis[1].fy;
+  Y1 = layout[0].axis[1].fy + layout[0].axis[1].dfy;
+
+  /* find the first valid datapoint */
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  sx0 = MIN (MAX (sx0, X0), X1);
+  sy0 = MAX (MIN (sy0, Y0), Y1);
+  
+  /* continue with rest of points */
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    sx1 = MIN (MAX (sx1, X0), X1);
+    sy1 = MAX (MIN (sy1, Y0), Y1);
+    sxa = 0.5*(sx0 + sx1);
+    DrawLine (sx0, sy0, sxa, sy0);
+    DrawLine (sxa, sy0, sxa, sy1);
+    DrawLine (sxa, sy1, sx1, sy1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+/******/
+void DrawPoints (Layout *layout, Gobjects *object) {
+
+  int i;
+  float *x, *y, *z;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx, sy, d, sx1, sy1, sx2, sy2;
+  
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  /**** points are scaled by object.z ***/
+  if (object[0].size < 0) {
+    d = 0.5 * (graphic.dx + graphic.dy) * 0.01;
+    x = object[0].x; y = object[0].y; z = object[0].z;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx - d*z[i], sy - d*z[i], 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {	/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx - d*z[i], sy - d*z[i], 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy, sx + d*z[i] + 1, sy);
+	    DrawLine (sx, sy - d*z[i], sx, sy + d*z[i] + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {	/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d*z[i] + 1, sy - d*z[i] - 1, sx - d*z[i], sy + d*z[i]);
+	    DrawLine (sx - d*z[i], sy - d*z[i], sx + d*z[i] + 1, sy + d*z[i] + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      XPoint points[4];
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    points[0].x = sx - d*z[i];  points[0].y = sy + 0.58*d*z[i];  
+	    points[1].x = sx + d*z[i];  points[1].y = sy + 0.58*d*z[i];  
+	    points[2].x = sx;           points[2].y = sy - 1.15*d*z[i];  
+	    points[3].x = sx - d*z[i];  points[3].y = sy + 0.58*d*z[i];  
+	    XFillPolygon (graphic.display, graphic.window, graphic.gc, points, 4, Convex, CoordModeOrigin);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy + 0.58*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx + d*z[i], sy + 0.58*d*z[i], sx,          sy - 1.15*d*z[i]);
+	    DrawLine (sx,          sy - 1.15*d*z[i], sx - d*z[i], sy + 0.58*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx,          sy - 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	/* connect a pair of points */
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	if (!(finite(x[i+1]) && finite(y[i+1]))) continue;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  } 
+  /**** points are not scaled by object.z ***/
+  else {
+    d = object[0].size * 0.5 * (graphic.dx + graphic.dy) * 0.003;
+    x = object[0].x; y = object[0].y;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx - d, sy - d, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {		/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+   	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx - d, sy - d, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy, sx + d + 1, sy);
+	    DrawLine (sx, sy - d, sx, sy + d + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {		/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    /* XDrawLine is a little funny, this is needed to fix endpost errors */
+	    DrawLine (sx + d + 1, sy - d - 1, sx - d, sy + d);
+	    DrawLine (sx - d, sy - d, sx + d + 1, sy + d + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      XPoint points[4];
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    points[0].x = sx - d;  points[0].y = sy + 0.58*d;  
+	    points[1].x = sx + d;  points[1].y = sy + 0.58*d;  
+	    points[2].x = sx;      points[2].y = sy - 1.15*d;  
+	    points[3].x = sx - d;  points[3].y = sy + 0.58*d;  
+	    XFillPolygon (graphic.display, graphic.window, graphic.gc, points, 4, Convex, CoordModeOrigin);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy + 0.58*d, sx + d, sy + 0.58*d);
+	    DrawLine (sx + d, sy + 0.58*d, sx,     sy - 1.15*d);
+	    DrawLine (sx,     sy - 1.15*d, sx - d, sy + 0.58*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d, sy + 0.58*d);
+	    DrawLine (sx, sy, sx + d, sy + 0.58*d);
+	    DrawLine (sx, sy, sx,     sy - 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  }
+}
+    
+/******/
+void DrawXErrors (Layout *layout, Gobjects *object) {
+  
+  int i, bar;
+  float *x, *y, *dxm, *dxp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dxp = object[0].dxp; dxm = object[0].dxm; 
+  bar = object[0].ebar; sz = object[0].size*layout[0].axis[1].dfy*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] + dxp[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] + dxp[i])*myi + y[i]*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy))) 
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxm[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] - dxm[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] - dxm[i])*myi + y[i]*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+  }
+}
+    
+/******/
+void DrawYErrors (Layout *layout, Gobjects *object) {
+
+  int i, bar;
+  float *x, *y, *dym, *dyp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dyp = object[0].dyp; dym = object[0].dym; 
+  bar = object[0].ebar; sz = object[0].size*layout[0].axis[0].dfx*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dyp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = sx0 + dyp[i]*mxj;
+    sy1 = sy0 + dyp[i]*myj;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dym[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] - dym[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] - dym[i])*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawPtext.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawPtext.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawPtext.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "Ximage.h"
+  
+void DrawPtext (Layout *layout) {
+  
+  int i, x, y, size;
+  double angle;
+  char *fontname;
+
+  fontname = GetRotFont (&size);
+  XSetForeground (graphic.display, graphic.gc, graphic.fore);
+  DrawRotTextInit (graphic.display, graphic.window, graphic.gc, graphic.fore, graphic.back);
+
+  for (i = 0; i < layout[0].Nptext; i++) {
+    if (strcmp (layout[0].ptext[i].text, "")) {
+      angle = layout[0].ptext[i].angle;
+      x = layout[0].ptext[i].x;
+      y = layout[0].ptext[i].y;
+      SetRotFont (layout[0].ptext[i].font, layout[0].ptext[i].size);
+      DrawRotText (x, y, layout[0].ptext[i].text, 5, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /* pos values
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|   8   |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+  */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawText.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawText.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/DrawText.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "Ximage.h"
+  
+/* Draw text centered at x,y */
+
+void DrawText (int x, int y, char *string, int pos) {
+
+  char *c;
+  int dx, dy;
+
+  for (c = string; (*c == ' ') || (*c == '\t'); c++);
+  if (*c == 0) return;
+  
+  dx  = XTextWidth (graphic.font, c, strlen(c));
+  dy  = graphic.font[0].ascent;
+  switch (pos) {
+  case 0: XDrawString (graphic.display, graphic.window, graphic.gc, x - dx,   y + dy,   c, strlen(c)); break;
+  case 1: XDrawString (graphic.display, graphic.window, graphic.gc, x - dx/2, y + dy,   c, strlen(c)); break;
+  case 2: XDrawString (graphic.display, graphic.window, graphic.gc, x,        y + dy,   c, strlen(c)); break;
+  case 3: XDrawString (graphic.display, graphic.window, graphic.gc, x - dx,   y + dy/2, c, strlen(c)); break;
+  case 4: XDrawString (graphic.display, graphic.window, graphic.gc, x - dx/2, y + dy/2, c, strlen(c)); break;
+  case 5: XDrawString (graphic.display, graphic.window, graphic.gc, x,        y + dy/2, c, strlen(c)); break;
+  case 6: XDrawString (graphic.display, graphic.window, graphic.gc, x - dx,   y,        c, strlen(c)); break;
+  case 7: XDrawString (graphic.display, graphic.window, graphic.gc, x - dx/2, y,        c, strlen(c)); break;
+  case 8: XDrawString (graphic.display, graphic.window, graphic.gc, x,        y,        c, strlen(c)); break;
+  }
+}
+
+/*
+            
+ 6____7___8
+  |       | 
+  |       | 
+ 3|   4   |5
+  |       |
+  |       |
+  ---------
+ 0    1   2
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/ErasePlot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/ErasePlot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/ErasePlot.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "Ximage.h"
+# define TESTFREE(a) { if ((a) != (float *) NULL) free (a);}
+
+int ErasePlot (int ClearSection) {
+  
+  int i, j;
+
+  for (j = 0; j < Nsection; j++) {
+
+    /* free data objects, then re-alloc those needed */
+    for (i = 0; i < section[j].Nobjects; i++) {
+      TESTFREE (section[j].objects[i].x);
+      TESTFREE (section[j].objects[i].y);
+      TESTFREE (section[j].objects[i].z);
+      TESTFREE (section[j].objects[i].dxm);
+      TESTFREE (section[j].objects[i].dxp);
+      TESTFREE (section[j].objects[i].dym);
+      TESTFREE (section[j].objects[i].dyp);
+    }
+    
+    /* reset axes and labels */
+    for (i = 0; i < 4; i++) {
+      section[j].axis[i].isaxis = FALSE;
+      section[j].axis[i].islabel = FALSE;
+      section[j].axis[i].areticks = FALSE;
+    }
+    for (i = 0; i < 8; i++) {
+      strcpy (section[j].label[i].text, "");
+    }
+    
+    /* free objects and text entries, or re-zero */
+    if (ClearSection && (j != 0)) {
+      free (section[j].objects);
+      free (section[j].ptext);
+    } else {
+      section[j].Nobjects = 0;
+      REALLOCATE (section[j].objects, Gobjects, 1);
+      section[j].Nptext = 0;
+      REALLOCATE (section[j].ptext, Label, 1);
+    }
+  }
+
+  if (ClearSection) {
+    Nsection = 1;
+    TheSection = 0;
+    REALLOCATE (section, Layout, Nsection);
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic.display, graphic.window);
+  Refresh (1);
+
+  return (TRUE);
+  
+}
+
+
+/** note that 'section' is the global variable
+    of type 'Layout' which carries data and info 
+    about each graph section 
+**/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadBox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadBox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadBox.c	(revision 22322)
@@ -0,0 +1,79 @@
+# include "Ximage.h"
+
+int LoadBox () {
+  
+  int i, status;
+  char Axis[16], Labels[16], Ticks[16];
+  Layout *layout;
+  
+  layout = &section[TheSection];
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf", 
+		  &layout[0].axis[0].min, &layout[0].axis[0].max, 
+		  &layout[0].axis[1].min, &layout[0].axis[1].max);
+
+  layout[0].axis[3].min = layout[0].axis[1].min;
+  layout[0].axis[3].max = layout[0].axis[1].max;
+  layout[0].axis[2].min = layout[0].axis[0].min;
+  layout[0].axis[2].max = layout[0].axis[0].max;
+  
+  KiiScanMessage (sock, "%s %s %s", Axis, Labels, Ticks);
+
+  for (i = 0; i < 4; i++) {
+    switch (Axis[i]) {
+    case '0':
+      layout[0].axis[i].isaxis = FALSE;
+      break;
+    case '1':
+      layout[0].axis[i].isaxis = TRUE;
+      break;
+    case '2':
+      layout[0].axis[i].isaxis = TRUE;
+      break;
+    }
+    switch (Ticks[i]) {
+    case '0':
+      layout[0].axis[i].areticks = FALSE;
+      break;
+    case '1':
+      layout[0].axis[i].areticks = TRUE;
+      break;
+    case '2':
+      layout[0].axis[i].areticks = 2;
+      break;
+    }
+    switch (Labels[i]) {
+    case '0':
+      layout[0].axis[i].islabel = FALSE;
+      break;
+    case '1':
+      layout[0].axis[i].islabel = TRUE;
+      break;
+    case '2':
+      layout[0].axis[i].islabel = (i < 2);
+      break;
+    }
+  }
+
+  if (USE_XWINDOW) DrawFrame (layout);
+  status = TRUE;
+  if (status) {
+    for (i = 0; i < Nsection; i++) {
+      PositionPicture (&section[i]);
+    }
+    if (USE_XWINDOW) XClearWindow (graphic.display, graphic.window);
+    Refresh (1);
+  } else {
+    FlushDisplay ();
+  } 
+
+  return (TRUE);
+  
+}
+
+/*
+  layout[0].X = 0;
+  layout[0].Y = 0;
+  layout[0].expand = 1;
+*/
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadLabels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadLabels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadLabels.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "Ximage.h"
+
+int LoadLabels () {
+  
+  char *c, *label;
+  int mode, size, Nbytes;
+  Layout *layout;
+  
+  layout = &section[TheSection];
+  
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+  
+  KiiScanMessage (sock, "%d", &mode);
+  label = KiiRecvData (sock);
+
+  bzero (layout[0].label[mode].text, LABEL_MAXLEN);
+
+  Nbytes = MIN (strlen(label), LABEL_MAXLEN - 1);
+  strncpy (layout[0].label[mode].text, label, Nbytes);
+  label[Nbytes] = 0;
+
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+  
+  c = GetRotFont (&size);
+  layout[0].label[mode].size = size;
+  strcpy (layout[0].label[mode].font, c);
+  if (USE_XWINDOW) DrawLabels (layout);
+  
+  FlushDisplay ();
+  
+  return (TRUE);
+  
+}
+
+/*
+  layout[0].X = 0;
+  layout[0].Y = 0;
+  layout[0].expand = 1;
+  */
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadObject.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadObject.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadObject.c	(revision 22322)
@@ -0,0 +1,185 @@
+# include "Ximage.h"
+# include <errno.h>
+
+int LoadObject () {
+  
+  int N;
+  Layout *layout;
+
+  layout = &section[TheSection];
+  
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+  
+  N = layout[0].Nobjects;
+  layout[0].Nobjects ++;
+  REALLOCATE (layout[0].objects, Gobjects, layout[0].Nobjects);
+  layout[0].objects[N].x = layout[0].objects[N].y = layout[0].objects[N].z = (float *) NULL;
+  layout[0].objects[N].dxm = layout[0].objects[N].dxp = (float *) NULL;
+  layout[0].objects[N].dym = layout[0].objects[N].dyp = (float *) NULL;
+  
+  KiiScanMessage (sock, "%d %d %d %d %d %d %d %lf %lf",
+		  &layout[0].objects[N].Npts, &layout[0].objects[N].style, 
+		  &layout[0].objects[N].ptype, &layout[0].objects[N].ltype, 
+		  &layout[0].objects[N].etype, &layout[0].objects[N].ebar, 
+		  &layout[0].objects[N].color, 
+		  &layout[0].objects[N].lweight, &layout[0].objects[N].size);
+  
+  if (DEBUG) fprintf (stderr, "%d %d %d %d %d %d %d %lf %lf\n",
+		      layout[0].objects[N].Npts, layout[0].objects[N].style, 
+		      layout[0].objects[N].ptype, layout[0].objects[N].ltype, 
+		      layout[0].objects[N].etype, layout[0].objects[N].ebar, 
+		      layout[0].objects[N].color, 
+		      layout[0].objects[N].lweight, layout[0].objects[N].size);
+  
+  /* force valid ranges */
+  if ((layout[0].objects[N].color > KapaColormapSize()) || (layout[0].objects[N].color < 0))
+    layout[0].objects[N].color = 0;
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf",
+		  &layout[0].objects[N].x0, &layout[0].objects[N].x1, 
+		  &layout[0].objects[N].y0, &layout[0].objects[N].y1);
+  
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+  
+  if (!LoadVectorData (layout, N, "x")) {
+    FreeObjectData (&layout[0].objects[N]);
+    layout[0].Nobjects --;
+    REALLOCATE (layout[0].objects, Gobjects, MAX (1, layout[0].Nobjects));
+  }
+    
+  if (!LoadVectorData (layout, N, "y")) {
+    FreeObjectData (&layout[0].objects[N]);
+    layout[0].Nobjects --;
+    REALLOCATE (layout[0].objects, Gobjects, MAX (1, layout[0].Nobjects));
+  }
+  if (layout[0].objects[N].size < 0.0) {
+    if (!LoadVectorData (layout, N, "z")) {
+      FreeObjectData (&layout[0].objects[N]);
+      layout[0].Nobjects --;
+      REALLOCATE (layout[0].objects, Gobjects, MAX (1, layout[0].Nobjects));
+    }
+  }
+  if (layout[0].objects[N].etype & 0x01) {
+    if (!LoadVectorData (layout, N, "dym")) {
+      FreeObjectData (&layout[0].objects[N]);
+      layout[0].Nobjects --;
+      REALLOCATE (layout[0].objects, Gobjects, MAX (1, layout[0].Nobjects));
+    }
+    if (!LoadVectorData (layout, N, "dyp")) {
+      FreeObjectData (&layout[0].objects[N]);
+      layout[0].Nobjects --;
+      REALLOCATE (layout[0].objects, Gobjects, MAX (1, layout[0].Nobjects));
+    }
+  }
+  if (layout[0].objects[N].etype & 0x02) {
+    if (!LoadVectorData (layout, N, "dxm")) {
+      FreeObjectData (&layout[0].objects[N]);
+      layout[0].Nobjects --;
+      REALLOCATE (layout[0].objects, Gobjects, MAX (1, layout[0].Nobjects));
+    }
+    if (!LoadVectorData (layout, N, "dxp")) {
+      FreeObjectData (&layout[0].objects[N]);
+      layout[0].Nobjects --;
+      REALLOCATE (layout[0].objects, Gobjects, MAX (1, layout[0].Nobjects));
+    }
+  }
+
+  if (DEBUG) fprintf (stderr, "loaded %d objects, using object %d\n", layout[0].objects[N].Npts, N);
+
+  if (USE_XWINDOW) DrawObjectN (layout, &layout[0].objects[layout[0].Nobjects-1]);
+  FlushDisplay ();
+
+  return (TRUE);
+  
+}
+
+/* load data for the named component */
+int LoadVectorData (Layout *layout, int N, char *type) {
+  
+  int Npts, Ninpts, status, Ntry;
+  int bytes_left;
+  char *buff;
+
+  buff = NULL;
+  Npts = layout[0].objects[N].Npts;
+  status = 1;
+  if (!strcmp (type, "x")) {
+    ALLOCATE (layout[0].objects[N].x, float, MAX (1, Npts));
+    buff = (char *) layout[0].objects[N].x;
+  }
+  if (!strcmp (type, "y")) {
+    ALLOCATE (layout[0].objects[N].y, float, MAX (1, Npts));
+    buff = (char *) layout[0].objects[N].y;
+  }
+  if (!strcmp (type, "z")) {
+    ALLOCATE (layout[0].objects[N].z, float, MAX (1, Npts));
+    buff = (char *) layout[0].objects[N].z;
+  }
+  if (!strcmp (type, "dxm")) {
+    ALLOCATE (layout[0].objects[N].dxm, float, MAX (1, Npts));
+    buff = (char *) layout[0].objects[N].dxm;
+  }
+  if (!strcmp (type, "dxp")) {
+    ALLOCATE (layout[0].objects[N].dxp, float, MAX (1, Npts));
+    buff = (char *) layout[0].objects[N].dxp;
+  }
+  if (!strcmp (type, "dym")) {
+    ALLOCATE (layout[0].objects[N].dym, float, MAX (1, Npts));
+    buff = (char *) layout[0].objects[N].dym;
+  }
+  if (!strcmp (type, "dyp")) {
+    ALLOCATE (layout[0].objects[N].dyp, float, MAX (1, Npts));
+    buff = (char *) layout[0].objects[N].dyp;
+  }
+
+  bytes_left = Npts*sizeof (float);
+
+  Ntry = 0;
+  if (DEBUG) fprintf (stderr, "starting vector load\n");
+  Ninpts = 0;
+  while (bytes_left > 0) {
+    status = read (sock, buff, bytes_left);
+    if (DEBUG) fprintf (stderr, "status: %d, %d\n", status, bytes_left);
+    if (status == 0) {  /* No more pipe */
+      fprintf (stderr, "error: pipe closed\n");
+      return (FALSE);
+    }
+    if (status != -1) { /* pipe has data */
+      Ninpts += status;
+      bytes_left -= status;
+      buff = (char *)(buff + status);
+      Ntry = 0;
+      continue;
+    }
+    if (errno == EAGAIN) {
+      Ntry ++;
+      if (Ntry > 100) {
+	fprintf (stderr, "kapa communication error\n");
+	return (FALSE);
+      }
+      usleep (10000);
+      continue;
+    }
+    perror ("kapa load");
+  }
+  if (Ninpts != Npts*sizeof(float)) {  
+    fprintf (stderr, "error: expected %d bytes, but got only %d\n", Ninpts, (unsigned int)(Npts*sizeof(float)));
+    return (FALSE);
+  }
+  if (DEBUG) fprintf (stderr, "done vector load\n");
+  return (TRUE);
+
+}
+
+void FreeObjectData (Gobjects *object) {
+
+  if (object[0].x != (float *) NULL) free (object[0].x);
+  if (object[0].y != (float *) NULL) free (object[0].y);
+  if (object[0].z != (float *) NULL) free (object[0].z);
+
+  if (object[0].dxm != (float *) NULL) free (object[0].dxm);
+  if (object[0].dxp != (float *) NULL) free (object[0].dxp);
+  if (object[0].dym != (float *) NULL) free (object[0].dym);
+  if (object[0].dyp != (float *) NULL) free (object[0].dyp);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadPtext.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadPtext.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/LoadPtext.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "Ximage.h"
+
+int LoadPtext () {
+  
+  char *string;
+  int N, size;
+  double tX, tY, tT, L;
+  Layout *layout;
+  
+  layout = &section[TheSection];
+  
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+  
+  layout[0].Nptext = MAX (layout[0].Nptext, 0);
+  N = layout[0].Nptext;
+  layout[0].Nptext++;
+  REALLOCATE (layout[0].ptext, Label, layout[0].Nptext);
+
+  KiiScanMessage (sock, "%lf %lf %lf", &tX, &tY, &tT);
+
+  L = layout[0].axis[0].dfx;
+  layout[0].ptext[N].x = L * (tX - layout[0].axis[0].min) / (layout[0].axis[0].max - layout[0].axis[0].min) + layout[0].axis[0].fx;
+
+  L = layout[0].axis[1].dfy;
+  layout[0].ptext[N].y = L * (tY - layout[0].axis[1].min) / (layout[0].axis[1].max - layout[0].axis[1].min) + layout[0].axis[1].fy;
+
+  layout[0].ptext[N].angle = tT;
+
+  bzero (layout[0].ptext[N].text, LABEL_MAXLEN);
+
+  string = KiiRecvData (sock);
+  strcpy (layout[0].ptext[N].text, string);
+  free (string);
+
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+  
+  string = GetRotFont (&size);
+  layout[0].ptext[N].size = size;
+  strcpy (layout[0].ptext[N].font, string);
+
+  if (USE_XWINDOW) DrawPtext (layout);
+  
+  FlushDisplay ();
+  
+  return (TRUE);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PNGit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PNGit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PNGit.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "Ximage.h"
+
+int PNGit () {
+
+  FILE *f;
+  png_structp png_ptr;
+  png_infop info_ptr;
+  png_color *palette;
+  int Npalette;
+  char filename[1024];
+  int Nbytes, status;
+
+  bDrawBuffer *buffer;
+
+  /* expect a line telling the number of bytes and a filename */
+  status = read (sock, filename, 16);
+  filename[16] = 0;
+  sscanf (filename, "%*s %d", &Nbytes);
+  status = read (sock, filename, Nbytes);
+  filename[status] = 0; /* make the string easy to parse */
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open output file %s\n", "Xgraph.png");
+    return (TRUE);  /* true because otherwise it quits kapa! */
+  }
+
+  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, (png_voidp) NULL, (png_voidp) NULL, (png_voidp) NULL);
+  if (!png_ptr) {
+    fprintf (stderr, "can't get png structure\n");
+    fclose (f);
+    return (TRUE);
+  }
+
+  info_ptr = png_create_info_struct (png_ptr);
+  if (!info_ptr) {
+    fprintf (stderr, "can't get png info structure\n");
+    png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+    fclose (f);
+    return (TRUE);
+  }
+
+  if (setjmp (png_ptr[0].jmpbuf)) {
+    fprintf (stderr, "can't get png return\n");
+    png_destroy_write_struct (&png_ptr, &info_ptr);
+    fclose (f);
+    return (TRUE);
+  }
+
+  png_init_io (png_ptr, f);
+
+  /* see docs for write-row-callback to provide progress info */
+
+  png_set_IHDR (png_ptr, info_ptr, graphic.dx, graphic.dy, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 
+
+  /* png_set_IHDR (png_ptr, info_ptr, graphic.dx, graphic.dy, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); */
+
+  palette = KapaPNGPalette (&Npalette);
+  png_set_PLTE (png_ptr, info_ptr, palette, Npalette); 
+ 
+  png_write_info (png_ptr, info_ptr);
+
+  buffer = bDrawIt ();
+
+  png_write_image (png_ptr, buffer[0].pixels);
+  png_write_end (png_ptr, info_ptr);
+  png_destroy_write_struct (&png_ptr, &info_ptr);
+  
+  bDrawBufferFree (buffer);
+  free (palette);
+  fclose (f);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PPMit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PPMit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PPMit.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "Ximage.h"
+
+int PPMit () {
+
+  FILE *f;
+  char *line, filename[1024];
+  int Nbytes, status, dx, dy, i, j, Npalette, color;
+  png_color *palette;
+
+  bDrawBuffer *buffer;
+
+  /* expect a line telling the number of bytes and a filename */
+  status = read (sock, filename, 16);
+  filename[16] = 0;
+  sscanf (filename, "%*s %d", &Nbytes);
+  status = read (sock, filename, Nbytes);
+  filename[status] = 0; /* make the string easy to parse */
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open output file %s\n", filename);
+    return (TRUE);  /* true because otherwise it quits kapa! */
+  }
+  
+  palette = KapaPNGPalette (&Npalette);
+
+  dx = graphic.dx;
+  dy = graphic.dy;
+  
+  fprintf (f, "P6\n");
+  fprintf (f, "%d %d\n", dx, dy);
+  fprintf (f, "255\n");
+
+  buffer = bDrawIt ();
+
+  ALLOCATE (line, char, 3*dx);
+
+  for (i = 0; i < dy; i++) {
+    for (j = 0; j < dx; j++) {
+      color = buffer[0].pixels[i][j];
+      line[3*j + 0] = palette[color].red;
+      line[3*j + 1] = palette[color].green;
+      line[3*j + 2] = palette[color].blue;
+    }
+    fwrite (line, 3, dx, f);
+  }
+  fclose (f);
+  
+  bDrawBufferFree (buffer);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSFrame.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSFrame.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSFrame.c	(revision 22322)
@@ -0,0 +1,99 @@
+# include "Ximage.h"
+# define DrawLine(X1,Y1,DX,DY) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f L\n", X1, Y1, X1+DX, Y1+DY))
+
+int PSFrame (Layout *layout, FILE *f) {
+  
+  int i, P, IsLabel, fontsize;
+  double fx, fy, dfx, dfy, range, major, minor, first, next;
+  char *fontname;
+
+  /* each axis is drawn independently */
+  fontname = GetRotFont (&fontsize);
+  fprintf (f, "1 setlinewidth\n");
+  for (i = 0; i < 4; i++) {
+    fx = layout[0].axis[i].fx;
+    fy = graphic.dy - layout[0].axis[i].fy;
+    dfx = layout[0].axis[i].dfx;
+    dfy = -layout[0].axis[i].dfy;
+    P = hypot ((double)layout[0].axis[(i+1)%2].dfx, (double)layout[0].axis[(i+1)%2].dfy);
+
+    if (layout[0].axis[i].isaxis) { DrawLine (fx, fy, dfx, dfy); }
+    
+    if (layout[0].axis[i].areticks) {
+      range = layout[0].axis[i].max - layout[0].axis[i].min;
+      AxisTickScale (&layout[0].axis[i], &major, &minor);
+
+      first = minor*((int)(layout[0].axis[i].min/minor));
+      if ((range > 0) && (first < layout[0].axis[i].min)) {
+	first += minor;
+      }
+      if ((range < 0) && (first > layout[0].axis[i].min)) {
+	first -= minor;
+      }
+      for (next = first; ((range > 0) && (next <= layout[0].axis[i].max)) || ((range < 0) && (next >= layout[0].axis[i].max));) {
+	IsLabel = FALSE;
+	if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || 
+	    (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)) ||
+	    (fabs ((int)((next - 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)))
+	  IsLabel = layout[0].axis[i].islabel;
+	PSTick (f, fx, fy, dfx, dfy, P, layout[0].axis[i].min, layout[0].axis[i].max, next, IsLabel, i);
+	if (range > 0) 
+	  next += minor;
+	else 
+	  next -= minor;
+      }
+    }
+  }
+  return (TRUE);
+}
+
+void PSTick (FILE *f, double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int mode, int naxis) {
+  
+  int pos, dir, fontsize;
+  double size, n, x, y, dx, dy;
+  char string[64], *fontname;
+
+  pos = size = 0;
+
+  if (mode == 1) { size = MAX (0.02, 7.0 / P); }
+  if (mode == 0) { size = MAX (0.01, 4.0 / P); }
+  
+  n = P / sqrt ((double)(dfx*dfx + dfy*dfy));
+  x = fx + (value-min)*dfx/(max - min);
+  y = fy + (value-min)*dfy/(max - min);
+
+  dir = -1;
+  if ((naxis == 0) || (naxis == 1)) dir *= -1;
+  dx = dir*size*dfy*n;	
+  dy = dir*size*dfx*n;
+  
+  DrawLine (x, y, dx, dy);
+
+  if (mode == 1) {
+    fontname = GetRotFont (&fontsize);
+
+    /* temporarily assume rectilinear axes */
+    if (naxis == 0) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 1; }
+    if (naxis == 2) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 7; }
+
+    if (naxis == 1) { dy = 0; dx = -(0.8*fontsize + 1); pos = 3; }
+    if (naxis == 3) { dy = 0; dx = +(0.8*fontsize + 1); pos = 5; }
+
+    x = fx + (value-min)*dfx/(max - min) + dx;
+    y = fy + (value-min)*dfy/(max - min) + dy;
+    if (fabs(value) < 1e-14) { value = 0.0; }
+    sprintf (string, "%g", value);
+    PSRotText (f, (int)x, (int)y, string, pos, 0.0);
+  }
+}
+
+  /* 
+  dx = size*dfy*n;	
+  dy = size*dfx*n;
+  DrawLine (x, y, dx, dy);
+  
+  dx = -size*dfy*n;	
+  dy = -size*dfx*n;
+  DrawLine (x, y, dx, dy);
+  */
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSLabels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSLabels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSLabels.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "Ximage.h"
+  
+void PSLabels (Layout *layout, FILE *f) {
+  
+  int i, pos, x, y, size;
+  double angle;
+  char *fontname;
+
+  pos = 0;
+  fontname = GetRotFont (&size);
+  for (i = 0; i < 8; i++) {
+    if (strcmp (layout[0].label[i].text, "")) {
+      angle = 0;
+      switch (i) {
+      case 0: pos = 7; break;
+      case 1: pos = 1; angle = -90; break;
+      case 2: pos = 1; break;
+      case 3: pos = 1; angle =  90; break;
+      case 4: pos = 2; break;
+      case 5: pos = 0; break;
+      case 6: pos = 8; break;
+      case 7: pos = 6; break;
+      }	
+      x = layout[0].label[i].x;
+      y = graphic.dy - layout[0].label[i].y;
+      SetRotFont (layout[0].label[i].font, layout[0].label[i].size); 
+      PSRotText (f, x, y, layout[0].label[i].text, pos, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /*
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|       |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+  */
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSObjects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSObjects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSObjects.c	(revision 22322)
@@ -0,0 +1,685 @@
+# include "Ximage.h"
+# define XOFFSET 60
+# define YOFFSET 60
+
+void ClipLinePS (double, double, double, double, double, double, double, double, FILE *);
+
+static char *name = "$Name: not supported by cvs2svn $";
+
+int PSit () {
+
+  int i, pageMode, scaleMode;
+  double scale;
+  FILE *f;
+  char filename[1024], pagename[1024], *version;
+
+  /* expect a line telling the number of bytes and a filename */
+  KiiScanMessage (sock, "%s %s %d %d", filename, pagename, &scaleMode, &pageMode);
+
+  if (pageMode == KAPA_PS_NEWPAGE) {
+    f = fopen (filename, "a+");
+  } else {
+    f = fopen (filename, "w");
+  }
+  if (f == NULL) {
+    fprintf (stderr, "can't open output file %s\n", filename);
+    return (TRUE);  /* true because otherwise it quits kapa! */
+  }
+
+  /* two scaling options: expand to fit page / keep absolute size */ 
+  if (scaleMode) {
+    scale = MIN (fabs(500.0 / graphic.dx), fabs (700.0 / graphic.dy));
+  } else {
+    scale = 72.0 / 96.0; /* ratio of screen pixels to points */
+  }
+
+  switch (pageMode) {
+    case KAPA_PS_NEWPLOT:
+      fprintf (f, "%%!PS-Adobe-2.0 EPSF-2.0\n");
+      fprintf (f, "%%%%Title: %s\n", filename);
+      version = strip_version (name);
+      fprintf (f, "%%%%Creator: Kapa (%s)\n", version);
+      free (version);
+      fprintf (f, "%%%%BoundingBox: %d %d %.0f %.0f\n", 
+	       XOFFSET, YOFFSET, XOFFSET + scale*graphic.dx, YOFFSET + scale*graphic.dy);
+      fprintf (f, "%%%%Pages: 1\n");
+      fprintf (f, "%%%%DocumentFonts:\n");
+      fprintf (f, "%%%%EndComments\n");
+      fprintf (f, "%%%%EndProlog\n");
+      fprintf (f, "%%%%Page: %s\n\n", pagename);
+      break;
+
+    case KAPA_PS_NEWPAGE:
+      fprintf (f, "%%%%Page: %s\n\n", pagename);
+      break;
+
+    case KAPA_PS_RAWPAGE:
+      break;
+  } 
+  fprintf (f, "gsave %% encloses picture\n");
+  fprintf (f, "%% local abbreviations\n");
+  fprintf (f, "/Times-Roman findfont 14 scalefont setfont\n");
+  fprintf (f, "/T {moveto show stroke} def\n");
+  fprintf (f, "/B { newpath moveto dup 0 exch rlineto exch dup 0 rlineto exch -1 mul\n");
+  fprintf (f, " 0 exch rlineto -1 mul 0 rlineto closepath stroke } def\n");
+  fprintf (f, "/F { newpath moveto dup 0 exch rlineto exch dup 0 rlineto exch -1 mul\n");
+  fprintf (f, " 0 exch rlineto -1 mul 0 rlineto closepath fill stroke } def\n");
+  fprintf (f, "/C {0 360 arc stroke} def\n");
+  fprintf (f, "/L {newpath moveto lineto stroke} def\n\n");
+  fprintf (f, "/TF {newpath moveto lineto lineto fill stroke} def\n\n");
+
+  if (pageMode != KAPA_PS_RAWPAGE) {
+    fprintf (f, " %d %d translate\n", XOFFSET, YOFFSET);
+    fprintf (f, "  %f  %f scale\n", scale, scale);
+  }
+
+  for (i = 0; i < Nsection; i++) {
+    PSFrame (&section[i], f); 
+    PSObjects (&section[i], f);
+    PSLabels (&section[i], f);
+    PSPtext (&section[i], f);
+  }
+  
+  fprintf (f, "grestore %% end of picture\n");
+
+  if (pageMode != KAPA_PS_RAWPAGE) fprintf (f, "showpage\n");
+
+  fclose (f);
+  return (TRUE);
+
+}
+
+# define DrawLine(X1,Y1,X2,Y2) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f L\n", X1, graphic.dy - Y1, X2, graphic.dy - Y2))
+# define DrawCircle(X1,Y1,R) (fprintf (f, " %6.2f %6.2f %6.2f C\n", (X1), (graphic.dy - Y1), (R)))
+# define DrawRectangle(X,Y,dX,dY) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f B\n", (dX), (dY), (X-0.5*dX), (graphic.dy-Y-0.5*dY)))
+# define FillRectangle(X,Y,dX,dY) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f F\n", (dX), (dY), (X-0.5*dX), (graphic.dy-Y-0.5*dY)))
+# define FillTriangle(X1,Y1,X2,Y2, X3, Y3) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f TF\n", (X1), (graphic.dy-Y1), (X2), (graphic.dy-Y2), (X3), (graphic.dy-Y3)))
+# define CONNECT 0
+# define HISTOGRAM 1
+# define POINTS 2
+
+int PSObjects (Layout *layout, FILE *f) {
+  
+  int i;
+  double lweight;
+  static char dash[] = "5";
+  static char dot[] = "3";
+  
+  for (i = 0; i < layout[0].Nobjects; i++) {
+    switch (layout[0].objects[i].ltype) {
+    case 0:
+      break;
+    case 1:
+      fprintf (f, "[%s] 0 setdash\n", dash);
+      break;
+    case 2:
+      fprintf (f, "[%s] 0 setdash\n", dot);
+      break;
+    default:
+      break;
+    }
+    
+    lweight = MAX (0, MIN (10, layout[0].objects[i].lweight));
+    fprintf (f, "%.1f setlinewidth\n", lweight);
+    fprintf (f, "%s setrgbcolor\n", KapaColorRGBString(layout[0].objects[i].color));
+
+    switch (layout[0].objects[i].style) {
+    case CONNECT: 
+      PSConnect (layout, &layout[0].objects[i], f);
+      break;
+    case HISTOGRAM:
+      PSHistogram (layout, &layout[0].objects[i], f);
+      break;
+    case POINTS:
+      PSPoints (layout, &layout[0].objects[i], f);
+      break;
+    }
+
+    if (layout[0].objects[i].etype & 0x01) {
+      PSYErrors (layout, &layout[0].objects[i], f);
+    }
+    if (layout[0].objects[i].etype & 0x02) {
+      PSXErrors (layout, &layout[0].objects[i], f);
+    }
+    fprintf (f, "[] 0 setdash\n");
+    fprintf (f, "0.00 0.00 0.00 setrgbcolor\n");
+  }
+  return (TRUE);
+}
+
+/*******/
+void PSConnect (Layout *layout, Gobjects *object, FILE *f) {
+  
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1;
+  double X0, X1, Y0, Y1;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = layout[0].axis[0].fx;
+  X1 = layout[0].axis[0].fx + layout[0].axis[0].dfx;
+  Y0 = layout[0].axis[1].fy;
+  Y1 = layout[0].axis[1].fy + layout[0].axis[1].dfy;
+
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    ClipLinePS (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1, f);
+    /* DrawLine (sx0, sy0, sx1, sy1); */
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+void ClipLinePS (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0, FILE *f) {
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x0,y0 if outside box */
+  if ((x0 < X0) && (x1 >= X0)) {
+    y0 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X0;
+  }
+  if ((x0 > X1) && (x1 <= X1)) {
+    y0 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X1;
+  }
+  if ((y0 < Y0) && (y1 >= Y0)) {
+    x0 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y0;
+  }
+  if ((y0 > Y1) && (y1 <= Y1)) {
+    x0 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y1;
+  }
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x1,y1 if outside box */
+  if ((x1 < X0) && (x0 >= X0)) {
+    y1 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X0;
+  }
+  if ((x1 > X1) && (x0 <= X1)) {
+    y1 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X1;
+  }
+  if ((y1 < Y0) && (y0 >= Y0)) {
+    x1 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y0;
+  }
+  if ((y1 > Y1) && (y0 <= Y1)) {
+    x1 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y1;
+  }
+  DrawLine (x0, y0, x1, y1);
+}
+
+/*******/
+void PSHistogram (Layout *layout, Gobjects *object, FILE *f) {
+
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sxa;
+  double X0, X1, Y0, Y1;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = layout[0].axis[0].fx;
+  X1 = layout[0].axis[0].fx + layout[0].axis[0].dfx;
+  Y0 = layout[0].axis[1].fy;
+  Y1 = layout[0].axis[1].fy + layout[0].axis[1].dfy;
+
+  /* find the first valid datapoint */
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  sx0 = MIN (MAX (sx0, X0), X1);
+  sy0 = MAX (MIN (sy0, Y0), Y1);
+  
+  /* continue with rest of points */
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    sx1 = MIN (MAX (sx1, X0), X1);
+    sy1 = MAX (MIN (sy1, Y0), Y1);
+    sxa = 0.5*(sx0 + sx1);
+    DrawLine (sx0, sy0, sxa, sy0);
+    DrawLine (sxa, sy0, sxa, sy1);
+    DrawLine (sxa, sy1, sx1, sy1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+/*******/
+void PSPoints (Layout *layout, Gobjects *object, FILE *f) {
+ 
+  int i;
+  float *x, *y, *z;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx, sy, d, sx1, sy1, sx2, sy2;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);  
+ 
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);  
+
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  /* below here, code is identical with DrawObjects */
+  /**** points are scaled by object.z ***/
+  if (object[0].size < 0) {
+    d = 0.5 * (graphic.dx + graphic.dy) * 0.01;
+    x = object[0].x; y = object[0].y; z = object[0].z;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {	/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy, sx + d*z[i], sy);
+	    DrawLine (sx, sy - d*z[i], sx, sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {	/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d*z[i], sy - d*z[i], sx - d*z[i], sy + d*z[i]);
+	    DrawLine (sx - d*z[i], sy - d*z[i], sx + d*z[i], sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    FillTriangle (sx - d*z[i], sy - 0.58*d*z[i], sx + d*z[i], sy - 0.58*d*z[i], sx, sy + 1.15*d*z[i]);
+	    /*
+	    DrawLine (sx - d*z[i], sy + 0.58*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx + d*z[i], sy + 0.58*d*z[i], sx,          sy - 1.15*d*z[i]);
+	    DrawLine (sx,          sy - 1.15*d*z[i], sx - d*z[i], sy + 0.58*d*z[i]);
+	    */
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy + 0.58*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx + d*z[i], sy + 0.58*d*z[i], sx,          sy - 1.15*d*z[i]);
+	    DrawLine (sx,          sy - 1.15*d*z[i], sx - d*z[i], sy + 0.58*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx,          sy - 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	/* connect a pair of points */
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  } else {
+    d = object[0].size * 0.5 * (graphic.dx + graphic.dy) * 0.003;
+    x = object[0].x; y = object[0].y;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {		/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+   	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy, sx + d, sy);
+	    DrawLine (sx, sy - d, sx, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {		/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d, sy - d, sx - d, sy + d);
+	    DrawLine (sx - d, sy - d, sx + d, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    /*
+	    DrawLine (sx - d, sy + 0.58*d, sx + d, sy + 0.58*d);
+	    DrawLine (sx + d, sy + 0.58*d, sx,     sy - 1.15*d);
+	    DrawLine (sx,     sy - 1.15*d, sx - d, sy + 0.58*d);
+	    */
+	    FillTriangle (sx - d, sy - 0.58*d, sx + d, sy - 0.58*d, sx, sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy - 0.58*d, sx + d, sy - 0.58*d);
+	    DrawLine (sx + d, sy - 0.58*d, sx,     sy + 1.15*d);
+	    DrawLine (sx,     sy + 1.15*d, sx - d, sy - 0.58*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx + d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx,     sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  }
+}
+    
+/*******/
+void PSXErrors (Layout *layout, Gobjects *object, FILE *f) {
+  
+  int i, bar;
+  float *x, *y, *dxm, *dxp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dxp = object[0].dxp; dxm = object[0].dxm; 
+  bar = object[0].ebar; sz = object[0].size*layout[0].axis[1].dfy*0.03;
+   
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] + dxp[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] + dxp[i])*myi + y[i]*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxm[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] - dxm[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] - dxm[i])*myi + y[i]*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+  }
+}
+
+    
+/*******/
+void PSYErrors (Layout *layout, Gobjects *object, FILE *f) {
+  
+  int i, bar;
+  float *x, *y, *dym, *dyp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+  
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dyp = object[0].dyp; dym = object[0].dym; 
+  bar = object[0].ebar; sz = object[0].size*layout[0].axis[0].dfx*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dyp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] + dyp[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] + dyp[i])*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dym[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] - dym[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] - dym[i])*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSPtext.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSPtext.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/PSPtext.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include "Ximage.h"
+  
+void PSPtext (Layout *layout, FILE *f) {
+
+  int i, x, y, size;
+  double angle;
+  char *fontname;
+
+  fontname = GetRotFont (&size);
+  for (i = 0; i < layout[0].Nptext; i++) {
+    if (strcmp (layout[0].ptext[i].text, "")) {
+      angle = layout[0].ptext[i].angle;
+      x = layout[0].ptext[i].x;
+      y = graphic.dy - layout[0].ptext[i].y;
+      SetRotFont (layout[0].ptext[i].font, layout[0].ptext[i].size);
+      PSRotText (f, x, y, layout[0].ptext[i].text, 5, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /* pos values
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|   8   |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+  */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/SetLimits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/SetLimits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/SetLimits.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "Ximage.h"
+
+int SetLimits () {
+  
+  int i;
+  double xmin, xmax, ymin, ymax;
+  Layout *layout;
+
+  layout = &section[TheSection];
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf", &xmin, &xmax, &ymin, &ymax);
+
+  layout[0].axis[2].min = layout[0].axis[0].min = xmin;
+  layout[0].axis[2].max = layout[0].axis[0].max = xmax;
+  layout[0].axis[3].min = layout[0].axis[1].min = ymin; 
+  layout[0].axis[3].max = layout[0].axis[1].max = ymax;
+  
+  for (i = 0; i < layout[0].Nobjects; i++) {
+    layout[0].objects[i].x0 = xmin;
+    layout[0].objects[i].x1 = xmax;
+    layout[0].objects[i].y0 = ymin;
+    layout[0].objects[i].y1 = ymax;
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic.display, graphic.window);
+  Refresh (1);
+
+  return (TRUE);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawFrame.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawFrame.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawFrame.c	(revision 22322)
@@ -0,0 +1,89 @@
+# include "Ximage.h"
+
+int bDrawFrame (Layout *layout) {
+  
+  int i, P, IsLabel, fontsize;
+  double fx, fy, dfx, dfy, range, major, minor, first, next;
+  char *fontname;
+
+  /* each axis is drawn independently */
+  fontname = GetRotFont (&fontsize);
+  for (i = 0; i < 4; i++) {
+    fx  = layout[0].axis[i].fx;
+    fy  = layout[0].axis[i].fy;
+    dfx = layout[0].axis[i].dfx;
+    dfy = layout[0].axis[i].dfy;
+    P = hypot ((double)layout[0].axis[(i+1)%2].dfx, (double)layout[0].axis[(i+1)%2].dfy);
+
+    if (layout[0].axis[i].isaxis) { 
+      bDrawLine (fx, fy, fx+dfx, fy+dfy); 
+    }
+    
+    if (layout[0].axis[i].areticks) {
+      range = layout[0].axis[i].max - layout[0].axis[i].min;
+
+      AxisTickScale (&layout[0].axis[i], &major, &minor);
+
+      first = minor*((int)(layout[0].axis[i].min/minor));
+      if ((range > 0) && (first < layout[0].axis[i].min)) {
+	first += minor;
+      }
+      if ((range < 0) && (first > layout[0].axis[i].min)) {
+	first -= minor;
+      }
+      for (next = first; ((range > 0) && (next <= layout[0].axis[i].max)) || ((range < 0) && (next >= layout[0].axis[i].max));) {
+	IsLabel = FALSE;
+	if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || 
+	    (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)) ||
+	    (fabs ((int)((next - 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)))
+	  IsLabel = layout[0].axis[i].islabel;
+	bDrawTick (fx, fy, dfx, dfy, P, layout[0].axis[i].min, layout[0].axis[i].max, next, IsLabel, i);
+	if (range > 0) 
+	  next += minor;
+	else 
+	  next -= minor;
+      }
+    }
+  }
+  return (TRUE);
+}
+
+void bDrawTick (double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int mode, int naxis) {
+  
+  int pos, dir, fontsize;
+  double size, n, x, y, dx, dy;
+  char string[64], *fontname;
+
+  pos = size = 0;
+
+  if (mode == 1) { size = MAX (0.02, 7.0 / P); }
+  if (mode == 0) { size = MAX (0.01, 4.0 / P); }
+  
+  n = P / sqrt ((double)(dfx*dfx + dfy*dfy));
+  x = fx + (value-min)*dfx/(max - min);
+  y = fy + (value-min)*dfy/(max - min);
+
+  dir = +1;
+  if ((naxis == 0) || (naxis == 1)) dir *= -1;
+  dx = dir*size*dfy*n;	
+  dy = dir*size*dfx*n;
+  
+  bDrawLine (x, y, x+dx, y+dy);
+
+  if (mode == 1) {
+    fontname = GetRotFont (&fontsize);
+
+    /* temporarily assume rectilinear axes */
+    if (naxis == 0) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 1; }
+    if (naxis == 2) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 7; }
+
+    if (naxis == 1) { dy = 0; dx = -(0.8*fontsize + 1); pos = 3; }
+    if (naxis == 3) { dy = 0; dx = +(0.8*fontsize + 1); pos = 5; }
+
+    x = fx + (value-min)*dfx/(max - min) + dx;
+    y = fy + (value-min)*dfy/(max - min) + dy;
+    if (fabs(value) < 1e-14) { value = 0.0; }
+    sprintf (string, "%g", value);
+    bDrawRotText ((int)x, (int)y, string, pos, 0.0);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawIt.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawIt.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawIt.c	(revision 22322)
@@ -0,0 +1,24 @@
+# include "Ximage.h"
+
+bDrawBuffer *bDrawIt () {
+
+  int i;
+  bDrawBuffer *buffer;
+  bDrawColor black;
+
+  black = KapaColorByName ("black");
+
+  buffer = bDrawBufferCreate (graphic.dx, graphic.dy);
+  bDrawSetBuffer (buffer);
+  bDrawSetStyle (black, 0, 0);
+  
+  for (i = 0; i < Nsection; i++) {
+    bDrawFrame (&section[i]); 
+    bDrawObjects (&section[i]);
+    bDrawLabels (&section[i]);
+    bDrawPtext (&section[i]);
+  }
+  
+  return (buffer);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawLabels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawLabels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawLabels.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "Ximage.h"
+  
+void bDrawLabels (Layout *layout) {
+  
+  int i, pos, x, y, size;
+  double angle;
+  char *fontname;
+
+  pos = 0;
+  fontname = GetRotFont (&size);
+  for (i = 0; i < 8; i++) {
+    if (strcmp (layout[0].label[i].text, "")) {
+      angle = 0;
+      switch (i) {
+      case 0: pos = 7; break;
+      case 1: pos = 1; angle = -90; break;
+      case 2: pos = 1; break;
+      case 3: pos = 1; angle =  90; break;
+      case 4: pos = 2; break;
+      case 5: pos = 0; break;
+      case 6: pos = 8; break;
+      case 7: pos = 6; break;
+      }	
+      x = layout[0].label[i].x;
+      y = layout[0].label[i].y;
+      SetRotFont (layout[0].label[i].font, layout[0].label[i].size); 
+      bDrawRotText (x, y, layout[0].label[i].text, pos, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+void bDrawPtext (Layout *layout) {
+
+  int i, x, y, size;
+  double angle;
+  char *fontname;
+
+  fontname = GetRotFont (&size);
+  for (i = 0; i < layout[0].Nptext; i++) {
+    if (strcmp (layout[0].ptext[i].text, "")) {
+      angle = layout[0].ptext[i].angle;
+      x = layout[0].ptext[i].x;
+      y = layout[0].ptext[i].y;
+      SetRotFont (layout[0].ptext[i].font, layout[0].ptext[i].size);
+      bDrawRotText (x, y, layout[0].ptext[i].text, 5, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+/*
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|       |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+*/
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawObjects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawObjects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/bDrawObjects.c	(revision 22322)
@@ -0,0 +1,570 @@
+# include "Ximage.h"
+
+# define DrawLine(X1,Y1,X2,Y2) (bDrawLine ((X1), (Y1), (X2), (Y2)))
+# define DrawCircle(X1,Y1,R) (bDrawCircle ((X1), (Y1), (R)))
+# define DrawRectangle(X,Y,dX,dY) (bDrawRectOpen ((X-0.5*dX), (Y-0.5*dY), (X+0.5*dX), (Y+0.5*dY)))
+# define FillRectangle(X,Y,dX,dY) (bDrawRectFill ((X-0.5*dX), (Y-0.5*dY), (X+0.5*dX), (Y+0.5*dY)))
+# define FillTriangle(X1,Y1,X2,Y2,X3,Y3) (bDrawTriFill ((X1), (Y1), (X2), (Y2), (X3), (Y3)))
+# define OpenTriangle(X1,Y1,X2,Y2,X3,Y3) (bDrawTriOpen ((X1), (Y1), (X2), (Y2), (X3), (Y3)))
+# define CONNECT 0
+# define HISTOGRAM 1
+# define POINTS 2
+
+int bDrawObjects (Layout *layout) {
+  
+  int i;
+  int type;
+  int weight;
+  bDrawColor color;
+  bDrawColor black;
+  
+  black = KapaColorByName ("black");
+  for (i = 0; i < layout[0].Nobjects; i++) {
+
+    weight = MAX (0, MIN (10, layout[0].objects[i].lweight));
+    type = layout[0].objects[i].ltype;    
+    color = layout[0].objects[i].color;
+    bDrawSetStyle (color, weight, type);
+
+    switch (layout[0].objects[i].style) {
+    case CONNECT: 
+      bDrawConnect (layout, &layout[0].objects[i]);
+      break;
+    case HISTOGRAM:
+      bDrawHistogram (layout, &layout[0].objects[i]);
+      break;
+    case POINTS:
+      bDrawPoints (layout, &layout[0].objects[i]);
+      break;
+    }
+
+    if (layout[0].objects[i].etype & 0x01) {
+      bDrawYErrors (layout, &layout[0].objects[i]);
+    }
+    if (layout[0].objects[i].etype & 0x02) {
+      bDrawXErrors (layout, &layout[0].objects[i]);
+    }
+  }
+  bDrawSetStyle (black, 0, 0);
+  return (TRUE);
+}
+
+void bDrawConnect (Layout *layout, Gobjects *object) {
+  
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1;
+  double X0, X1, Y0, Y1;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = layout[0].axis[0].fx;
+  X1 = layout[0].axis[0].fx + layout[0].axis[0].dfx;
+  Y0 = layout[0].axis[1].fy;
+  Y1 = layout[0].axis[1].fy + layout[0].axis[1].dfy;
+
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    bDrawClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+void bDrawClipLine (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0) {
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x0,y0 if outside box */
+  if ((x0 < X0) && (x1 >= X0)) {
+    y0 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X0;
+  }
+  if ((x0 > X1) && (x1 <= X1)) {
+    y0 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X1;
+  }
+  if ((y0 < Y0) && (y1 >= Y0)) {
+    x0 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y0;
+  }
+  if ((y0 > Y1) && (y1 <= Y1)) {
+    x0 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y1;
+  }
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x1,y1 if outside box */
+  if ((x1 < X0) && (x0 >= X0)) {
+    y1 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X0;
+  }
+  if ((x1 > X1) && (x0 <= X1)) {
+    y1 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X1;
+  }
+  if ((y1 < Y0) && (y0 >= Y0)) {
+    x1 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y0;
+  }
+  if ((y1 > Y1) && (y0 <= Y1)) {
+    x1 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y1;
+  }
+  DrawLine (x0, y0, x1, y1);
+}
+
+/*******/
+void bDrawHistogram (Layout *layout, Gobjects *object) {
+
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sxa;
+  double X0, X1, Y0, Y1;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = layout[0].axis[0].fx;
+  X1 = layout[0].axis[0].fx + layout[0].axis[0].dfx;
+  Y0 = layout[0].axis[1].fy;
+  Y1 = layout[0].axis[1].fy + layout[0].axis[1].dfy;
+
+  /* find the first valid datapoint */
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  sx0 = MIN (MAX (sx0, X0), X1);
+  sy0 = MAX (MIN (sy0, Y0), Y1);
+  
+  /* continue with rest of points */
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    sx1 = MIN (MAX (sx1, X0), X1);
+    sy1 = MAX (MIN (sy1, Y0), Y1);
+    sxa = 0.5*(sx0 + sx1);
+    DrawLine (sx0, sy0, sxa, sy0);
+    DrawLine (sxa, sy0, sxa, sy1);
+    DrawLine (sxa, sy1, sx1, sy1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+/*******/
+void bDrawPoints (Layout *layout, Gobjects *object) {
+ 
+  int i;
+  float *x, *y, *z;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx, sy, d, sx1, sy1, sx2, sy2;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);  
+ 
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);  
+
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  /* below here, code is identical with DrawObjects */
+  /**** points are scaled by object.z ***/
+  if (object[0].size < 0) {
+    d = 0.5 * (graphic.dx + graphic.dy) * 0.01;
+    x = object[0].x; y = object[0].y; z = object[0].z;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {	/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy, sx + d*z[i], sy);
+	    DrawLine (sx, sy - d*z[i], sx, sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {	/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d*z[i], sy - d*z[i], sx - d*z[i], sy + d*z[i]);
+	    DrawLine (sx - d*z[i], sy - d*z[i], sx + d*z[i], sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    FillTriangle (sx - d*z[i], sy - 0.58*d*z[i], sx + d*z[i], sy - 0.58*d*z[i], sx, sy + 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    OpenTriangle (sx - d*z[i], sy + 0.58*d*z[i], sx, sy - 1.15*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx,          sy - 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	/* connect a pair of points */
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  } else {
+    d = object[0].size * 0.5 * (graphic.dx + graphic.dy) * 0.003;
+    x = object[0].x; y = object[0].y;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {		/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+   	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy, sx + d, sy);
+	    DrawLine (sx, sy - d, sx, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {		/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d, sy - d, sx - d, sy + d);
+	    DrawLine (sx - d, sy - d, sx + d, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    FillTriangle (sx - d, sy - 0.58*d, sx + d, sy - 0.58*d, sx, sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    OpenTriangle (sx - d, sy + 0.58*d, sx + d, sy + 0.58*d, sx, sy - 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx + d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx,     sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > layout[0].axis[0].fx) && (sx < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	    (sy < layout[0].axis[1].fy) && (sy > layout[0].axis[1].fy + layout[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  }
+}
+    
+/*******/
+void bDrawXErrors (Layout *layout, Gobjects *object) {
+  
+  int i, bar;
+  float *x, *y, *dxm, *dxp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dxp = object[0].dxp; dxm = object[0].dxm; 
+  bar = object[0].ebar; sz = object[0].size*layout[0].axis[1].dfy*0.03;
+   
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] + dxp[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] + dxp[i])*myi + y[i]*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxm[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] - dxm[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] - dxm[i])*myi + y[i]*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+  }
+}
+    
+/*******/
+void bDrawYErrors (Layout *layout, Gobjects *object) {
+  
+  int i, bar;
+  float *x, *y, *dym, *dyp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+  
+  mxi = layout[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = layout[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = layout[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = layout[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  layout[0].axis[0].fx - object[0].x0*layout[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*layout[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*layout[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  layout[0].axis[1].fy - object[0].y0*layout[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dyp = object[0].dyp; dym = object[0].dym; 
+  bar = object[0].ebar; sz = object[0].size*layout[0].axis[0].dfx*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dyp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] + dyp[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] + dyp[i])*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dym[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] - dym[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] - dym[i])*myj + by;
+    if (((sx0 > layout[0].axis[0].fx) && (sx0 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy0 < layout[0].axis[1].fy) && (sy0 > layout[0].axis[1].fy + layout[0].axis[1].dfy)) ||
+	((sx1 > layout[0].axis[0].fx) && (sx1 < layout[0].axis[0].fx + layout[0].axis[0].dfx) &&
+	 (sy1 < layout[0].axis[1].fy) && (sy1 > layout[0].axis[1].fy + layout[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/cursor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/cursor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/graph/cursor.c	(revision 22322)
@@ -0,0 +1,112 @@
+# include "Ximage.h"
+
+/* # define IgnoreMask (long) (ClientMessage | ButtonReleaseMask | PointerMotionMask) */
+# define IgnoreMask (long) (~(StructureNotifyMask | SubstructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask))
+
+int LastEvent (Display *display, int type, XEvent *event);
+
+int cursor () {
+
+  Display        *display;
+  XEvent          event;
+  KeySym          keysym;
+  int             status, value;
+  XComposeStatus  composestatus;
+  char            string[10], line[40], *name;
+  double          x, y;
+  char            buffer[10];
+  Layout         *layout;
+  XButtonEvent   *mouse_event;
+
+  layout = &section[TheSection];
+
+  while (1) {
+    
+    /* check the pipe for messages */
+    status = read (sock, buffer, 4);
+    buffer[4] = 0;
+    
+    if (status == -1) goto events;
+    if (status ==  0) return (FALSE);
+    if (status ==  4) {
+      if (!strcmp (buffer, "NCUR")) return (TRUE);
+      goto events;
+    }
+    fprintf (stderr, "weird signal: too many or few bytes!  %d\n", status);
+    return (TRUE);
+
+  events:
+    if (XEventsQueued (graphic.display, QueuedAfterFlush) < 1) {
+      usleep (50000);
+      continue;
+    }
+
+    /* check for x events */
+    display = graphic.display;
+
+    /* grab the last entry for these events */
+    if (LastEvent (display, ConfigureNotify, &event)) Reconfig (&event);
+    if (LastEvent (display, CirculateNotify, &event)) Reconfig (&event);
+    if (LastEvent (display, Expose,          &event)) Refresh (1);
+
+    if (LastEvent (display, MappingNotify,   &event)) XRefreshKeyboardMapping ((XMappingEvent *) &event);
+	
+    /* drop and ignore the following StructureNotifyMask events */
+    LastEvent (display, GravityNotify, &event);
+    LastEvent (display, ReparentNotify, &event);
+    LastEvent (display, MapNotify, &event);
+    LastEvent (display, UnmapNotify, &event);
+
+    /* handle button presses */
+    if (LastEvent (display, ButtonPress,     &event)) {
+      mouse_event = (XButtonEvent *) &event;
+      sprintf (string, "Button%d", mouse_event[0].button);
+      x = (mouse_event[0].x - layout[0].axis[0].fx)*(layout[0].axis[0].max - layout[0].axis[0].min)/layout[0].axis[0].dfx + layout[0].axis[0].min;
+      y = (mouse_event[0].y - layout[0].axis[1].fy)*(layout[0].axis[1].max - layout[0].axis[1].min)/layout[0].axis[1].dfy + layout[0].axis[1].min;
+      snprintf (line, 40, "%12s %12.6f %12.6f ", string, x, y);
+      write (sock, line, 40);
+    }
+
+    /* handle key presses */
+    if (LastEvent (display, KeyPress,        &event)) {
+      value = XLookupString ((XKeyEvent *) &event, string, 9, &keysym, &composestatus);
+      name = XKeysymToString (keysym);
+
+      // ignore unmapped keys
+      if (name == NULL) continue;
+
+      x = (event.xkey.x - layout[0].axis[0].fx)*(layout[0].axis[0].max - layout[0].axis[0].min)/layout[0].axis[0].dfx + layout[0].axis[0].min;
+      y = (event.xkey.y - layout[0].axis[1].fy)*(layout[0].axis[1].max - layout[0].axis[1].min)/layout[0].axis[1].dfy + layout[0].axis[1].min;
+
+      // skip the following keys: 
+      if (!strcmp (name, "Shift_L")) continue;
+      if (!strcmp (name, "Shift_R")) continue;
+      if (!strcmp (name, "Control_L")) continue;
+      if (!strcmp (name, "Control_R")) continue;
+      if (!strcmp (name, "Alt_L")) continue;
+      if (!strcmp (name, "Alt_R")) continue;
+      if (!strcmp (name, "Super_L")) continue;
+      if (!strcmp (name, "Super_R")) continue;
+      if (!strcmp (name, "Caps_Lock")) continue;
+      if (!strcmp (name, "Pause")) continue;
+      if (!strcmp (name, "Continue")) continue;
+      if (!strcmp (name, "Num_Lock")) continue;
+      if (!strcmp (name, "Scroll_Lock")) continue;
+      if (!strcmp (name, "Print")) continue;
+      if (!strcmp (name, "(null)")) continue;
+
+      snprintf (line, 40, "%12s %12.6f %12.6f ", name, x, y);
+      write (sock, line, 40);
+    }
+
+    /* remove those events we will ignore */
+    while (XCheckMaskEvent (display, IgnoreMask, &event)) continue;
+
+    /* events to remove which have no mask component */
+    while (XCheckTypedEvent (display, ClientMessage, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionClear, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionNotify, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionRequest, &event)) continue;
+  }
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/Ximage.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/Ximage.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/Ximage.h	(revision 22322)
@@ -0,0 +1,7 @@
+# include <Xohana.h>
+# include <dvo.h>
+# include <kapa.h>
+# include "constants.h"
+# include "structures.h"
+# include "prototypes.h"
+# include "globals.h"
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/alphabet.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/alphabet.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/alphabet.h	(revision 22322)
@@ -0,0 +1,53 @@
+# include "../rotfont/times8.h"
+# include "../rotfont/times12.h"
+# include "../rotfont/times14.h"
+# include "../rotfont/times18.h"
+# include "../rotfont/times24.h"
+
+# include "../rotfont/courier8.h"
+# include "../rotfont/courier12.h"
+# include "../rotfont/courier14.h"
+# include "../rotfont/courier18.h"
+# include "../rotfont/courier24.h"
+
+# include "../rotfont/helvetica8.h"
+# include "../rotfont/helvetica12.h"
+# include "../rotfont/helvetica14.h"
+# include "../rotfont/helvetica18.h"
+# include "../rotfont/helvetica24.h"
+
+# include "../rotfont/symbol8.h"
+# include "../rotfont/symbol12.h"
+# include "../rotfont/symbol14.h"
+# include "../rotfont/symbol18.h"
+# include "../rotfont/symbol24.h"
+
+# define DEFFONT 1
+static FontSet HardwiredFonts[] = {
+  {times8font,  "times", 8},
+  {times12font, "times", 12},
+  {times14font, "times", 14},
+  {times18font, "times", 18},
+  {times24font, "times", 24},
+  
+  {courier8font,  "courier", 8},
+  {courier12font, "courier", 12},
+  {courier14font, "courier", 14},
+  {courier18font, "courier", 18},
+  {courier24font, "courier", 24},
+  
+  {helvetica8font,  "helvetica", 8},
+  {helvetica12font, "helvetica", 12},
+  {helvetica14font, "helvetica", 14},
+  {helvetica18font, "helvetica", 18},
+  {helvetica24font, "helvetica", 24},
+  
+  {symbol8font,  "symbol", 8},
+  {symbol12font, "symbol", 12},
+  {symbol14font, "symbol", 14},
+  {symbol18font, "symbol", 18},
+  {symbol24font, "symbol", 24}
+};
+
+/* put these as static in RotFont.c with accessor functions */
+# define NROT 256
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/buttons.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/buttons.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/buttons.h	(revision 22322)
@@ -0,0 +1,120 @@
+#define PS_width 25
+#define PS_height 25
+static char PS_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x81, 0x3f, 0x00,
+   0x82, 0x67, 0x60, 0x00, 0x02, 0x26, 0x40, 0x00, 0x02, 0x24, 0x40, 0x00,
+   0x02, 0x24, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00,
+   0x02, 0x24, 0x00, 0x00, 0x02, 0x64, 0x00, 0x00, 0x02, 0xc6, 0x00, 0x00,
+   0x82, 0x07, 0x07, 0x00, 0xfe, 0x01, 0x0c, 0x00, 0x02, 0x00, 0x18, 0x00,
+   0x02, 0x00, 0x20, 0x00, 0x02, 0x00, 0x60, 0x00, 0x02, 0x00, 0x40, 0x00,
+   0x02, 0x00, 0xc0, 0x00, 0x02, 0x10, 0x80, 0x00, 0x02, 0x10, 0xc0, 0x00,
+   0x02, 0x60, 0x60, 0x00, 0x02, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00}; 
+#define grey_width 25
+#define grey_height 25
+static char grey_bits[] = {
+   0x90, 0x40, 0xf7, 0x01, 0x04, 0xaa, 0xad, 0x01, 0x68, 0xd5, 0xfe, 0x01,
+   0x82, 0x28, 0xdb, 0x01, 0x10, 0xd2, 0x75, 0x01, 0x40, 0xad, 0xee, 0x01,
+   0xac, 0x50, 0xbf, 0x01, 0x00, 0xaa, 0xf9, 0x01, 0x52, 0xd5, 0xd6, 0x01,
+   0x80, 0x28, 0x6f, 0x01, 0x28, 0xd2, 0xfd, 0x01, 0x44, 0xad, 0xba, 0x01,
+   0x90, 0x50, 0xf7, 0x01, 0x02, 0xaa, 0xcd, 0x01, 0x68, 0xd5, 0x7e, 0x01,
+   0x80, 0x28, 0xfb, 0x01, 0x14, 0xd2, 0xb5, 0x01, 0x40, 0xad, 0xee, 0x01,
+   0xaa, 0x50, 0xdf, 0x01, 0x00, 0xaa, 0x79, 0x01, 0x50, 0xd5, 0xf6, 0x01,
+   0x84, 0x28, 0xaf, 0x01, 0x28, 0xd2, 0xfd, 0x01, 0x40, 0xad, 0xda, 0x01,
+   0x90, 0x50, 0x77, 0x01};
+#define rainbow_width 25
+#define rainbow_height 25
+static char rainbow_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x80, 0xff, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x0c, 0x7f, 0x00,
+   0x00, 0xe3, 0x80, 0x01, 0xc0, 0x9c, 0xff, 0x00, 0x20, 0x62, 0x00, 0x01,
+   0x90, 0x99, 0xff, 0x00, 0x48, 0x76, 0x00, 0x01, 0x24, 0x09, 0x00, 0x00,
+   0x94, 0x06, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
+   0xa5, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+   0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+   0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+   0x15, 0x00, 0x00, 0x00};
+#define recenter_width 25
+#define recenter_height 25
+static char recenter_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00,
+   0x80, 0xef, 0x03, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0x30, 0x00, 0x18, 0x00,
+   0x10, 0x00, 0x10, 0x00, 0x18, 0x00, 0x30, 0x00, 0x08, 0x00, 0x20, 0x00,
+   0x0c, 0x00, 0x60, 0x00, 0x0c, 0x38, 0x60, 0x00, 0x0c, 0x7c, 0x60, 0x00,
+   0x04, 0x7c, 0x40, 0x00, 0x0c, 0x7c, 0x60, 0x00, 0x0c, 0x38, 0x60, 0x00,
+   0x0c, 0x00, 0x60, 0x00, 0x08, 0x00, 0x20, 0x00, 0x18, 0x00, 0x30, 0x00,
+   0x10, 0x00, 0x10, 0x00, 0x30, 0x00, 0x18, 0x00, 0xe0, 0x00, 0x0e, 0x00,
+   0x80, 0xef, 0x03, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define puns_width 25
+#define puns_height 25
+static char puns_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0xd8, 0x01,
+   0x00, 0x0c, 0x37, 0x00, 0xf8, 0xc9, 0x6f, 0x00, 0x0c, 0xde, 0xf0, 0x00,
+   0x06, 0x5b, 0x1f, 0x00, 0x7b, 0xd9, 0x0c, 0x00, 0xc9, 0x70, 0x06, 0x00,
+   0x81, 0x71, 0x03, 0x00, 0x78, 0xb9, 0x39, 0x00, 0xc8, 0xdb, 0x0f, 0x00,
+   0x84, 0xf7, 0x7f, 0x00, 0x04, 0xff, 0xc3, 0x00, 0xe6, 0xfe, 0x8e, 0x00,
+   0x9a, 0x7d, 0x0b, 0x00, 0x08, 0xbc, 0x01, 0x00, 0x08, 0xfc, 0x3f, 0x00,
+   0x0c, 0xff, 0x78, 0x00, 0x84, 0x39, 0x60, 0x00, 0x40, 0x38, 0x40, 0x00,
+   0x60, 0x38, 0x40, 0x00, 0x20, 0x38, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
+   0x00, 0xd6, 0x00, 0x00};
+#define red_width 25
+#define red_height 25
+static char red_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xfc, 0x03, 0x00,
+   0x20, 0x07, 0x06, 0x00, 0xe0, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x08, 0x00,
+   0x20, 0x00, 0x08, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x00, 0x08, 0x00,
+   0x20, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x06, 0x00, 0x20, 0x80, 0x01, 0x00,
+   0x20, 0xe0, 0x00, 0x00, 0x20, 0x7e, 0x00, 0x00, 0xe0, 0xc3, 0x00, 0x00,
+   0x20, 0xc0, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x20, 0x80, 0x01, 0x00,
+   0x20, 0x00, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00, 0x20, 0x00, 0x03, 0x00,
+   0x20, 0x00, 0x06, 0x00, 0x20, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x18, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define green_width 25
+#define green_height 25
+static char green_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00,
+   0x00, 0x06, 0x04, 0x00, 0x80, 0x03, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x80, 0x0f, 0x00, 0x40, 0xc0, 0x30, 0x00,
+   0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x08, 0x00,
+   0x00, 0x03, 0x08, 0x00, 0x00, 0x7e, 0x0e, 0x00, 0x00, 0xc0, 0x03, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define blue_width 25
+#define blue_height 25
+static char blue_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xfe, 0x00, 0x00,
+   0xc0, 0x83, 0x07, 0x00, 0xc0, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x04, 0x00, 0x40, 0x00, 0x02, 0x00,
+   0x40, 0x80, 0x07, 0x00, 0x40, 0xe0, 0x0c, 0x00, 0xc0, 0x3f, 0x08, 0x00,
+   0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x06, 0x00, 0x40, 0x00, 0x02, 0x00,
+   0xc0, 0x80, 0x03, 0x00, 0x40, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define yellow_width 25
+#define yellow_height 25
+static char yellow_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00,
+   0x80, 0x01, 0x0c, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x02, 0x02, 0x00,
+   0x00, 0x84, 0x01, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define hms_width 25
+#define hms_height 25
+static char hms_bits[] = {
+   0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+   0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0xf0, 0x00, 0x1e, 0xef, 0x89, 0x00,
+   0x22, 0x11, 0x09, 0x00, 0x22, 0x11, 0x71, 0x00, 0x22, 0x11, 0x81, 0x00,
+   0x22, 0x11, 0x89, 0x00, 0x22, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x1e, 0xc7, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x80, 0x00,
+   0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0x80, 0x00,
+   0x40, 0x80, 0x80, 0x00, 0x5c, 0xb8, 0xb8, 0x00, 0x62, 0xc4, 0xc4, 0x00,
+   0x42, 0x84, 0x84, 0x00, 0x42, 0x84, 0x84, 0x00, 0x62, 0xc4, 0xc4, 0x00,
+   0x5c, 0xb9, 0xb8, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/constants.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/constants.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/constants.h	(revision 22322)
@@ -0,0 +1,45 @@
+/* hardwired values for some window parameters */
+
+# define EVENT_MASK (long) \
+(ButtonPressMask \
+ | ClientMessage \
+ | ButtonReleaseMask \
+ | KeyPressMask \
+ | ExposureMask \
+ | StructureNotifyMask \
+ | PointerMotionMask)
+
+# define DEFAULT_CURSOR XC_crosshair
+# define BORDER_WIDTH 2
+# define MIN_WIDTH 250
+# define MIN_HEIGHT 250
+# define LABEL_MAXLEN 128
+
+/* label names */
+# define LABELX0 0
+# define LABELY0 1
+# define LABELX1 2
+# define LABELY1 3
+# define LABELUL 4
+# define LABELUR 5
+# define LABELLL 6
+# define LABELLR 7
+
+/* EVENT_MASK consists of:
+
+ExposureMask        : Expose
+StructureNotifyMask : CirculateNotify | 
+                      ConfigureNotify | 
+                      DestroyNotify   | 
+		      GravityNotify   | 
+		      MapNotify       |
+		      ReparentNotify  |
+		      UnmapNotify
+ButtonPressMask     : ButtonPress
+ButtonReleaseMask   : ButtonRelease
+KeyPressMask        : KeyPress
+PointerMotionMask   : MotionNotify
+(always)            : ClientMessage 
+(always)            : MappingNotify
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/globals.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/globals.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/globals.h	(revision 22322)
@@ -0,0 +1,21 @@
+
+int HAVE_BACKING;
+int DEBUG;
+int USE_XWINDOW;
+int MAP_WINDOW;
+int FOREGROUND;
+char *NAME_WINDOW;
+
+/* file descriptor for socket connection to mana */
+int sock; 
+
+/* each layout / section defines one of the active portions of the graphics window
+   there may be an arbitrary number of sections, and each section may fill an arbitrary
+   rectangle in the X window.  The startup configuration assumes 1 section filling the 
+   entire X window. */
+
+Layout *section;
+int    Nsection, TheSection;
+
+/* graphic defines the basic details of the X window */
+Graphic graphic;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/arrow.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/arrow.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/arrow.h	(revision 22322)
@@ -0,0 +1,4 @@
+#define arrow_width 6
+#define arrow_height 9
+static char arrow_bits[] = {
+   0x00, 0x02, 0x06, 0x0e, 0x1e, 0x0e, 0x06, 0x02, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/back.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/back.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/back.h	(revision 22322)
@@ -0,0 +1,6 @@
+#define back_width 16
+#define back_height 16
+static char back_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x3c, 0x00, 0x3f, 0xc0, 0x3f,
+   0xf0, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xf0, 0x3f, 0xc0, 0x3f, 0x00, 0x3f,
+   0x00, 0x3c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/down.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/down.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/down.h	(revision 22322)
@@ -0,0 +1,4 @@
+#define down_width 9
+#define down_height 6
+static char down_bits[] = {
+   0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/fore.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/fore.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/fore.h	(revision 22322)
@@ -0,0 +1,6 @@
+#define fore_width 16
+#define fore_height 16
+static char fore_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x3c, 0x00, 0xfc, 0x00, 0xfc, 0x03,
+   0xfc, 0x0f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x0f, 0xfc, 0x03, 0xfc, 0x00,
+   0x3c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/icon.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/icon.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/icon.h	(revision 22322)
@@ -0,0 +1,13 @@
+#define icon_width 29
+#define icon_height 29
+static unsigned char icon_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/stop.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/stop.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/icons/stop.h	(revision 22322)
@@ -0,0 +1,6 @@
+#define stop_width 16
+#define stop_height 16
+static char stop_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
+   0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
+   0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/prototypes.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/prototypes.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/prototypes.h	(revision 22322)
@@ -0,0 +1,132 @@
+Button       *CheckButtons        PROTO((XButtonEvent *, Layout *));
+void          CheckColors         PROTO((int *, char **));
+void          CheckDisplayName    PROTO((int *, char **, char *));
+void          CheckFontName       PROTO((int *, char **, char *));
+void          CheckGeometry       PROTO((int *, char **));
+int           CheckPipe           PROTO(());
+void          CreateWindow        PROTO((Window, int, long));
+void          DefineLayout        PROTO((int, char **));
+void          DrawConnect         PROTO((Layout *, Gobjects *));
+void          DrawFrame           PROTO((Layout *));
+void          DrawHistogram       PROTO((Layout *, Gobjects *));
+void          DrawLabels          PROTO((Layout *));
+int           DrawObjectN         PROTO((Layout *, Gobjects *));
+int           DrawObjects         PROTO((Layout *));
+void          DrawPoints          PROTO((Layout *, Gobjects *));
+void          DrawXErrors         PROTO((Layout *, Gobjects *));
+void          DrawYErrors         PROTO((Layout *, Gobjects *));
+int           ErasePlot           PROTO(());
+unsigned long GetColor            PROTO((Display *, char *, Colormap, unsigned long));
+int           GetColormapSize     PROTO(());
+char         *GetRGBString        PROTO((int N));
+int           InButton            PROTO((XButtonEvent *, Button *));
+int           InPicture           PROTO((XButtonEvent *, Picture *));
+int           ListSection         PROTO(());
+int           LoadBox             PROTO(());
+void          LoadFont            PROTO((int *, char **, char *));
+int           LoadLabels          PROTO(());
+int           LoadObject          PROTO(());
+int           LoadPtext           PROTO(());
+int           LoadVectorData      PROTO((Layout *, int, char *));
+void          MakeColormap        PROTO((int, char **));
+void          MakeCursor          PROTO((unsigned int));
+void          MapWindow           PROTO(());
+void          NameWindow          PROTO((char *));
+Display      *OpenDisplay         PROTO((char *, int *));
+int           PNGit               PROTO(());
+int           PPMit               PROTO(());
+void          PSConnect           PROTO((Layout *, Gobjects *, FILE *));
+int           PSFrame             PROTO((Layout *, FILE *));
+void          PSHistogram         PROTO((Layout *, Gobjects *, FILE *));
+void          PSLabels            PROTO((Layout *, FILE *));
+int           PSObjects           PROTO((Layout *, FILE *));
+void          PSPoints            PROTO((Layout *, Gobjects *, FILE *));
+void          PSXErrors           PROTO((Layout *, Gobjects *, FILE *));
+void          PSYErrors           PROTO((Layout *, Gobjects *, FILE *));
+int           PSit                PROTO(());
+void          PositionPicture     PROTO((Layout *));
+void          QuitX               PROTO((Display *, char *, char *));
+int           Reconfig            PROTO((XEvent *));
+void          Refresh             PROTO((int));
+void          Remap               PROTO((Layout *, Matrix  *));
+int           Resize              PROTO(());
+int           SetFont             PROTO(());
+int           SetLimits           PROTO(());
+void          SetNormalHints      PROTO(());
+int           SetSection          PROTO((int SwitchSection));
+void          SetUpDisplay        PROTO((int *, char **));
+void          SetUpWindow         PROTO((int *, char **));
+void          SetWMHints          PROTO((Icon *));
+void          TopWindow           PROTO((Icon *));
+int           cursor              PROTO(());
+int           get_argument        PROTO((int, char **, char *));
+void          hh_hms              PROTO((char *, double, double, char));
+int           remove_argument     PROTO((int, int *, char **));
+
+void MakeGC ();
+
+int EventLoop ();
+void CloseDisplay ();
+int args (int *argc, char **argv);
+
+void DrawPtext (Layout *layout);
+void FlushDisplay ();
+void AxisTickScale (Axis *axis, double *major, double *minor);
+void DrawFrame (Layout *layout);
+void DrawTick (int fx, int fy, int dfx, int dfy, int P, double min, double max, double value, int mode, int naxis);
+void FreeObjectData (Gobjects *object);
+void PSPtext (Layout *layout, FILE *f);
+void PSTick (FILE *f, double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int mode, int naxis);
+
+/* kapa bDraw Functions */
+int bDrawObjects (Layout *layout);
+void bDrawConnect (Layout *layout, Gobjects *object);
+void bDrawClipLine (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0);
+void bDrawHistogram (Layout *layout, Gobjects *object);
+void bDrawPoints (Layout *layout, Gobjects *object);
+void bDrawXErrors (Layout *layout, Gobjects *object);
+void bDrawYErrors (Layout *layout, Gobjects *object);
+
+int bDrawFrame (Layout *layout);
+void bDrawTick (double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int mode, int naxis);
+void bDrawLabels (Layout *layout);
+void bDrawPtext (Layout *layout);
+
+bDrawBuffer *bDrawIt ();
+
+/******************* XGRAPH **
+void          StatusBox           PROTO((Graphic *, Layout *));
+void          ResetColorbar       PROTO((Graphic *, Layout *, double, double));
+void          Reorient            PROTO((Graphic *, Layout *, XButtonEvent *));
+void          PaintOverlay        PROTO((Graphic *, Layout *, int));
+void          MakeGC              PROTO((Graphic *));
+void          InvertButton        PROTO((Graphic *, Button *));
+void          FlashButton         PROTO((Graphic *, Button *));
+void          DrawButton          PROTO((Graphic *, Button *));
+void          DrawBitmap          PROTO((Graphic *, int, int, int, int, char *, int));
+void          DragColorbar        PROTO((Graphic *, Layout *, XButtonEvent *));
+void          CrossHairs          PROTO((Graphic *, Layout *));
+void          CreateZoom          PROTO((Layout *, Graphic *, double, double));
+void          CreatePicture       PROTO((Layout *, Graphic *));
+int           rainbow             PROTO((Graphic *, Layout *));
+int           puns                PROTO((Graphic *, Layout *));
+int           greycolors          PROTO((Graphic *, Layout *));
+int           UpdatePointer       PROTO((Graphic *, Layout *, XMotionEvent *));
+int           ToggleDEG           PROTO((Graphic *, Layout *));
+int           Stop                PROTO((Graphic *, Layout *));
+int           SaveOverlay         PROTO((Graphic *, Layout *));
+int           Rescale             PROTO((Graphic *, Layout *));
+int           RecenterRescale     PROTO((Graphic *, Layout *));
+int           Recenter            PROTO((Graphic *, Layout *));
+int           Overlay3            PROTO((Graphic *, Layout *));
+int           Overlay2            PROTO((Graphic *, Layout *));
+int           Overlay1            PROTO((Graphic *, Layout *));
+int           Overlay0            PROTO((Graphic *, Layout *));
+int           NewPicture          PROTO((Graphic *, Layout *));
+int           LoadOverlay         PROTO((Graphic *, Layout *));
+int           InterpretPresses    PROTO((Graphic *, Layout *, XButtonEvent *));
+int           InterpretKeys       PROTO((Graphic *, Layout *, XEvent *));
+int           EventLoop           PROTO((Graphic *, Layout *));
+int           EraseOverlay        PROTO((Graphic *, Layout *));
+int           CSaveOverlay        PROTO((Graphic *, Layout *));
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/structures.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/structures.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/include/structures.h	(revision 22322)
@@ -0,0 +1,120 @@
+
+/**************** Graphic carries X info around ****************/
+typedef struct {
+  Display       *display;
+  int            screen;
+  int            depth;
+  Window         window;
+  GC             gc;
+  XFontStruct   *font;
+  Cursor         cursor;
+  int            x,  y;
+  int            dx, dy;
+  Colormap       colormap;
+  unsigned long  fore;
+  unsigned long  back;
+  unsigned long  *color;
+} Graphic;
+
+/**************** X related "widget" structures ****************/
+typedef struct {
+  int      x, y, dx, dy;      /* position and size */
+  int      text;              /* does this have a picture or text? */
+  int      width, height;
+  char    *bitmap;          /* picture on button */
+  int    (*function_1) ();  /* mouse_button 1 function */
+  int    (*function_2) ();  /* mouse_button 2 function */
+  int    (*function_3) ();  /* mouse_button 3 function */
+} Button;
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  char    *label;          /* label on TextLine */
+  char     text[1024];     /* words of TextLine */
+  char     old_text[1024]; /* words of TextLine */
+  int      outline;        /* draw an outline?  */ 
+  int      cursor;         /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextLine;
+
+typedef char STRING[1024];
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  STRING  *text;           /* words of TextLine */
+  int      Nlines;
+  int      outline;        /* draw an outline?  */ 
+  int      cursor_line;    /* cursor line */
+  int      cursor_x;       /* location of cursor (if selected) */
+  int      cursor_y;       /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextBox;
+
+/**************** general structures ****************/
+typedef struct {
+  Pixmap pixmap;
+  int    width;
+  int    height;
+  unsigned char *bits;
+} Icon;
+
+typedef struct {
+  int      dx, dy, x, y;
+  XImage  *pix;
+  char    *data;
+} Picture;
+
+typedef struct {
+  char type[10];
+  double x, y;
+  double dx, dy;
+  double angle;
+  char  *text;
+} Object;
+
+typedef struct {
+  int x, y;
+  int dx, dy;
+  double angle;
+  int  size;
+  char font[64]; 
+  char text[LABEL_MAXLEN];
+} Label;
+
+typedef struct {
+  int Nobjects;
+  unsigned long color;
+  Object *objects;
+} Overlay;
+  
+typedef struct {
+  float *x, *y, *z, *dxp, *dxm, *dyp, *dym;
+  int Npts;
+  int style, ptype, ltype, color, etype, ebar;
+  double lweight, size;
+  double x0, x1, y0, y1;  /* limits for this object */
+} Gobjects;
+
+typedef struct {
+  double min, max;
+  char isaxis, areticks, islabel, islog;
+  double fx, dfx, fy, dfy;  /* axis location on graphic */
+} Axis;
+
+/******** Here we define the Layout struct specific to this program  *******/
+typedef struct {
+  Axis      axis[4];    /* coordinate axes */
+  Label     label[8];   /* fixed axis labels */
+
+  Gobjects *objects;    /* graphic objects */    
+  int      Nobjects;    
+
+  Label    *ptext;      /* placed text labels */
+  int      Nptext;      
+
+  double   x, dx;      /* location of plot on window ( 0 - 1, 0 - 1 ) */
+  double   y, dy; 
+
+  char     name[LABEL_MAXLEN];  /* name of the section */
+
+} Layout;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckColors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckColors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckColors.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "Ximage.h"
+
+/************** CheckColors *************/
+void CheckColors (int *argc, char **argv) {
+
+  char *temp_name;
+  int N;
+
+  graphic.fore = BlackPixel (graphic.display, graphic.screen);
+  temp_name = XGetDefault (graphic.display, argv[0], "Foreground");
+  if ((N = get_argument (*argc, argv, "-fg"))) {
+    if (N + 1 < *argc) {
+      temp_name = argv[N+1];
+    } else {
+      fprintf (stderr, "error: usage is -fg color\n");
+      exit (0);
+    }
+  }
+  if (temp_name != (char *) NULL) 
+    graphic.fore = GetColor (graphic.display, temp_name, graphic.colormap, graphic.fore);
+
+  graphic.back = WhitePixel (graphic.display, graphic.screen);
+  temp_name = XGetDefault (graphic.display, argv[0], "Background");
+  if ((N = get_argument (*argc, argv, "-bg"))) {
+    if (N + 1 < *argc) {
+      temp_name = argv[N+1];
+    } else {
+      fprintf (stderr, "error: usage is -bg color\n");
+      exit (0);
+    }
+  }  
+  if (temp_name != (char *)NULL) 
+    graphic.back = GetColor (graphic.display, temp_name, graphic.colormap, graphic.back);
+ 
+}
+
+  /* here we define the values for foreground and background
+     if -fg, or -bg exist, or if Foreground or Background are set in .Xdefaults, 
+     use those.  foreground defaults to black, background defaults to white. */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckDisplayName.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckDisplayName.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckDisplayName.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "Ximage.h"
+
+/************** CheckDisplayName *************/
+void CheckDisplayName (int *argc, char **argv, char *display_name) {
+
+  int N;
+
+  display_name[0] = 0;
+  if ((N = get_argument (*argc, argv, "-d"))) {
+    if (N + 1 < *argc) {
+      strcpy (display_name, argv[N + 1]);
+      remove_argument(N, argc, argv);
+      remove_argument(N, argc, argv);
+    } else {
+      fprintf (stderr, "error: usage is [-display/-d] DisplayName\n");
+      exit (0);
+    }
+  }
+
+  if ((N = get_argument (*argc, argv, "-display"))) {
+    if (N + 1 < *argc) {
+      strcpy (display_name, argv[N + 1]);
+      remove_argument(N, argc, argv);
+      remove_argument(N, argc, argv);
+    } else {
+      fprintf (stderr, "error: usage is [-display/-d] DisplayName\n");
+      exit (0);
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckFontName.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckFontName.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckFontName.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "Ximage.h"
+
+/************** CheckFontName *************/
+void CheckFontName (int *argc, char **argv, char *fontname) {
+
+  int N;
+
+  if ((N = get_argument (*argc, argv, "-font"))) {
+    if (N + 1 < *argc) {
+      strcpy (fontname, argv[N + 1]);
+      remove_argument(N, argc, argv);
+      remove_argument(N, argc, argv);
+    } else {
+      fprintf (stderr, "error: usage is -font fontname\n");
+      exit (0);
+    }
+  }   
+  if ((N = get_argument (*argc, argv, "-fn"))) {
+    if (N + 1 < *argc) {
+      strcpy (fontname, argv[N + 1]);
+      remove_argument(N, argc, argv);
+      remove_argument(N, argc, argv);
+    } else {
+      fprintf (stderr, "error: usage is -fn fontname\n");
+      exit (0);
+    }
+  }   
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckGeometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckGeometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CheckGeometry.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "Ximage.h"
+
+/************** CheckGeometry *************/
+void CheckGeometry (int *argc, char **argv) {
+
+  int status, x, y, N;
+  unsigned int dx, dy;
+  int X, Y, dX, dY;
+  char *temp_name;
+  
+  temp_name = XGetDefault (graphic.display, argv[0], "geometry");
+  if ((N = get_argument (*argc, argv, "-geom"))) {
+    if (N + 1 < *argc) {
+      temp_name = argv[N+1];
+    } else {
+      fprintf (stderr, "error: usage is -geom DisplayName\n");
+      exit (0);
+    }
+  }
+
+  X = 10;
+  Y = 10;
+  dX = 512;
+  dY = 512;
+  if (temp_name != (char *)NULL) {  
+    status = XParseGeometry (temp_name, &x, &y, &dx, &dy);
+    if (status & XValue) X = x;
+    if (status & YValue) Y = y;
+    if (status & WidthValue) {
+      dX = dx;
+    }
+    if (status & HeightValue) {
+      dY = dy;
+    }
+    if (status & XNegative) X  = DisplayWidth  (graphic.display, graphic.screen) - dX + X;
+    if (status & YNegative) Y  = DisplayHeight (graphic.display, graphic.screen) - dY + Y;
+  }
+
+  graphic.x = X;
+  graphic.y = Y;
+  graphic.dx = dX;
+  graphic.dy = dY; 
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CloseDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CloseDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CloseDisplay.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include "Ximage.h"
+
+/************** CloseDisplay *************/
+void CloseDisplay () {
+  XFreeFont (graphic.display, graphic.font); 
+  XFreeGC (graphic.display, graphic.gc);
+  XDestroySubwindows (graphic.display, graphic.window);
+  XDestroyWindow (graphic.display, graphic.window);
+  XFlush (graphic.display);
+  XCloseDisplay (graphic.display);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CreateWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CreateWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/CreateWindow.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "Ximage.h"
+
+/************** CreateWindow *************/
+void CreateWindow (Window parent, int border, long events) {
+
+  XSetWindowAttributes attributes;
+  unsigned long attribute_mask;
+  Visual *visual = CopyFromParent;
+
+  HAVE_BACKING = (DoesBackingStore (ScreenOfDisplay(graphic.display, graphic.screen)) == Always);
+
+  if (HAVE_BACKING) {
+    attributes.backing_store = Always;
+    attribute_mask = CWBackingStore | CWBackPixel | CWBorderPixel | CWEventMask;
+  } else {
+    attribute_mask = CWBackPixel | CWBorderPixel | CWEventMask;
+  }
+
+  attributes.background_pixel = graphic.back;
+  attributes.border_pixel     = graphic.fore;
+  attributes.event_mask       = events;
+
+  graphic.window = XCreateWindow (graphic.display, parent, 
+				     graphic.x, graphic.y, 
+				     graphic.dx, graphic.dy, 
+				     border, CopyFromParent,
+				     InputOutput, visual, 
+				     attribute_mask, &attributes);
+
+  if (graphic.window == (Window) None)
+    QuitX (graphic.display, "", "error: could not open window");
+
+  XSelectInput (graphic.display, graphic.window, events);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/DefineLayout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/DefineLayout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/DefineLayout.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "Ximage.h"
+# define PAD 40
+
+/* general initialization for things not specific to X */
+void DefineLayout (int argc, char **argv) {
+
+  int i;
+  
+  /** initiate connection with server **/
+  if (!FOREGROUND) {	
+    if (argc < 2) {
+      fprintf (stderr, "socket path not specified\n");
+      exit (0);
+    }
+    sock = KiiWait (argv[1]);
+  }
+
+  InitRotFonts ();
+
+  /** set up defaults for display region **/
+  /* default startup uses one graphic region, filling the window */
+  
+  Nsection = 1;
+  ALLOCATE (section, Layout, Nsection);
+  TheSection = 0;
+
+  /* set up axis positions */
+  for (i = 0; i < 4; i++) {
+    section[0].axis[i].min = 0.0;
+    section[0].axis[i].max = 1.0;
+    section[0].axis[i].isaxis = FALSE;
+    section[0].axis[i].areticks = FALSE;
+    section[0].axis[i].islabel = FALSE;
+  }    
+  for (i = 0; i < 8; i++) {
+    strcpy (section[0].label[i].text, "");
+  }
+  section[0].x  = 0;
+  section[0].dx = 1; 
+  section[0].y  = 0; 
+  section[0].dy = 1; 
+  strcpy (section[0].name, "default");
+
+  PositionPicture (&section[0]);
+
+  section[0].Nobjects = 0;
+  section[0].Nptext = 0;
+
+  ALLOCATE (section[0].objects, Gobjects, 1);  /* allocate so later free will not crash! */
+  section[0].objects[0].x   = section[0].objects[0].y   = section[0].objects[0].z = (float *) NULL;
+  section[0].objects[0].dxm = section[0].objects[0].dxp = (float *) NULL;
+  section[0].objects[0].dym = section[0].objects[0].dyp = (float *) NULL;
+  
+  ALLOCATE (section[0].ptext, Label, 1);       /* allocate so later free will not crash! */
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/GetColor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/GetColor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/GetColor.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "Ximage.h"
+
+/************** GetColor *************/
+unsigned long 
+GetColor (display, name, colormap, default_color)
+Display  *display;
+char      name[];
+Colormap  colormap;
+unsigned long default_color;
+{
+
+  unsigned long color;
+  XColor rgbcolor, hardwarecolor;
+  int status;
+
+  color = default_color;
+  status = XLookupColor (display, colormap, name, &rgbcolor, &hardwarecolor);
+  if (status) {
+    status = XAllocColor (display, colormap, &hardwarecolor);
+    if (status) {
+      color = hardwarecolor.pixel;
+    }
+  }
+  return (color);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/LoadFont.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/LoadFont.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/LoadFont.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include "Ximage.h"
+
+/************** LoadFont *************/
+void LoadFont (int *argc, char **argv, char *default_name) {
+
+  char name[400];
+  char *temp_name;
+
+  temp_name = XGetDefault (graphic.display, argv[0], "Font");
+  if (temp_name == (char *) NULL) 
+    strcpy (name, default_name);
+  else 
+    strcpy (name, temp_name);
+
+  CheckFontName (argc, argv, name);
+
+  graphic.font = XLoadQueryFont (graphic.display, name);
+  if (graphic.font == (XFontStruct *) NULL) {
+    fprintf (stderr, "Could not load fond %s, using %s\n", name, default_name);
+    graphic.font = XLoadQueryFont (graphic.display, default_name);
+    if (graphic.font == (XFontStruct *) NULL) {
+      graphic.font = XLoadQueryFont (graphic.display, "fixed");
+      fprintf (stderr, "Could not load fond %s, using %s\n", default_name, "fixed");
+    }
+  }
+
+  if (graphic.font != (XFontStruct *) NULL) 
+    XSetFont (graphic.display, graphic.gc, graphic.font[0].fid);
+  else
+    QuitX (graphic.display, "Error: could not load font", name);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/MakeCursor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/MakeCursor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/MakeCursor.c	(revision 22322)
@@ -0,0 +1,9 @@
+# include "Ximage.h"
+
+/************** MakeCursor *************/
+void MakeCursor (unsigned int which_cursor) {
+
+  graphic.cursor = XCreateFontCursor (graphic.display, (unsigned) which_cursor);
+
+  if (graphic.cursor != (Cursor) None) XDefineCursor (graphic.display, graphic.window, graphic.cursor);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/MakeGC.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/MakeGC.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/MakeGC.c	(revision 22322)
@@ -0,0 +1,14 @@
+# include "Ximage.h"
+
+/************** MakeGC *************/
+void MakeGC () {
+
+  XGCValues gcvalues;
+
+  gcvalues.foreground = graphic.fore;
+  gcvalues.background = graphic.back;
+  graphic.gc = XCreateGC (graphic.display, graphic.window, 
+			     GCForeground | GCBackground, &gcvalues);
+  if (graphic.gc == 0)
+    QuitX (graphic.display, "Error in creating a Graphics Context", "");
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/MapWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/MapWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/MapWindow.c	(revision 22322)
@@ -0,0 +1,10 @@
+# include "Ximage.h"
+
+/************** MapWindow *************/
+void MapWindow () {
+
+  XMapRaised (graphic.display, graphic.window);
+  XMapSubwindows (graphic.display, graphic.window);
+  FlushDisplay ();
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/NameWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/NameWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/NameWindow.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include "Ximage.h"
+
+/************** NameWindow *************/
+void NameWindow (char *Name) {
+
+  char       *name;
+  char       *class_name;
+  char       *class_type;
+  XClassHint *classhints;
+
+  name = strrchr (Name, '/');
+  if (name != NULL) 
+    name ++;
+  else 
+    name = Name;
+
+  class_type = class_name = name;
+  classhints = XAllocClassHint ();
+
+  if (classhints != (XClassHint *) NULL)  {
+    classhints[0].res_name = class_name;
+    classhints[0].res_class = class_type;
+    XSetClassHint (graphic.display, graphic.window, classhints);
+    XFree (classhints);
+  }
+  
+  XStoreName (graphic.display, graphic.window, name);
+  XSetIconName (graphic.display, graphic.window, name);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/OpenDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/OpenDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/OpenDisplay.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "Ximage.h"
+
+/************** OpenDisplay *************/
+Display *OpenDisplay (char *display_name, int *screen) {
+
+  Display *display;
+  
+  display = XOpenDisplay (display_name);
+  if (display != (Display *) NULL) *screen = DefaultScreen (display);
+  if (display == (Display *) NULL) {
+    fprintf (stderr, "Error could not open X display to %s\n", XDisplayName (display_name));
+    exit (0);
+  }
+  
+  return (display);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/QuitX.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/QuitX.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/QuitX.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include "Ximage.h"
+
+/************** QuitX *************/
+void QuitX (Display *display, char *error_message, char *error_file) {
+
+  fprintf (stderr, "Error: %s%s\n", error_message, error_file);
+  XCloseDisplay (display);
+  exit (0);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetNormalHints.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetNormalHints.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetNormalHints.c	(revision 22322)
@@ -0,0 +1,27 @@
+# include "Ximage.h"
+
+/************** SetNormalHints  *************/
+void SetNormalHints () {
+
+  XSizeHints *sizehints;
+
+  sizehints = XAllocSizeHints ();
+
+  if (sizehints != (XSizeHints *) NULL)  {
+    sizehints[0].x = graphic.x;
+    sizehints[0].y = graphic.x;
+    sizehints[0].width = graphic.dx;
+    sizehints[0].height = graphic.dy;
+    sizehints[0].min_width = MIN_WIDTH;
+    sizehints[0].min_height = MIN_HEIGHT;    
+    sizehints[0].flags = USPosition | USSize | PMinSize;
+
+    sizehints[0].base_width = graphic.dx;
+    sizehints[0].base_height = graphic.dy;
+    sizehints[0].flags |= PBaseSize;
+    
+    XSetWMNormalHints (graphic.display, graphic.window, sizehints);
+    XFree (sizehints);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetUpDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetUpDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetUpDisplay.c	(revision 22322)
@@ -0,0 +1,16 @@
+# include "Ximage.h"
+
+/************** SetUpDisplay *************/
+void SetUpDisplay (int *argc, char **argv) {
+
+  char display_name[120];
+
+  CheckDisplayName (argc, argv, display_name);
+
+  graphic.display  = OpenDisplay     (display_name,   &graphic.screen);
+  graphic.colormap = DefaultColormap (graphic.display, graphic.screen);
+  graphic.depth    = DefaultDepth    (graphic.display, graphic.screen);
+  CheckColors (argc, argv);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetUpWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetUpWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetUpWindow.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "Ximage.h"
+# include "icons/icon.h"
+
+/************** SetUpWindow *************/
+void SetUpWindow (int *argc, char **argv) {
+
+  Icon icon;
+  char *name;
+  int Ncolors;
+
+  icon.width = icon_width;
+  icon.height = icon_height;
+  icon.bits = icon_bits;
+
+  CheckGeometry (argc, argv);
+  TopWindow (&icon);
+  LoadFont (argc, argv, "fixed"); 
+
+  FlushDisplay ();
+  XSetWindowBackground (graphic.display, graphic.window, graphic.back);
+
+  SetNormalHints ();
+  SetWMHints (&icon);
+
+  if (NAME_WINDOW == NULL) {
+    NameWindow ("Kapa");
+  } else {
+    ALLOCATE (name, char, strlen(NAME_WINDOW) + 10);
+    sprintf (name, "Kapa %s", NAME_WINDOW);
+    NameWindow (name);
+    free (name);
+  }
+
+  graphic.color = KapaX11colors (graphic.display, graphic.colormap, graphic.fore, &Ncolors);
+  if (MAP_WINDOW) MapWindow (graphic);
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetWMHints.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetWMHints.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/SetWMHints.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "Ximage.h"
+
+/************** SetWMHints  *************/
+void SetWMHints (Icon *icon) {
+
+  XWMHints *wmhints;
+
+  wmhints = XAllocWMHints ();
+  if (wmhints != (XWMHints *) NULL) {
+    wmhints[0].initial_state = NormalState;
+    wmhints[0].input = True;
+    if (icon[0].pixmap != (Pixmap) None) {
+      wmhints[0].icon_pixmap = icon[0].pixmap;
+      wmhints[0].icon_mask = icon[0].pixmap;
+      wmhints[0].flags = StateHint | InputHint | IconPixmapHint | IconMaskHint;
+    } else {
+      wmhints[0].flags = StateHint | InputHint;
+    }
+    
+    XSetWMHints (graphic.display, graphic.window, wmhints);
+    XFree (wmhints);
+
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/TopWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/TopWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/TopWindow.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage.h"
+
+/************** TopWindow *************/
+void TopWindow (Icon *icon) {
+
+  Window rootwindow;
+
+  rootwindow = RootWindow (graphic.display, graphic.screen);
+
+  CreateWindow (rootwindow, BORDER_WIDTH, EVENT_MASK);
+  MakeGC ();
+
+  icon[0].pixmap = XCreateBitmapFromData (graphic.display, graphic.window, (char *) icon[0].bits, icon[0].width, icon[0].height);
+
+  MakeCursor (DEFAULT_CURSOR);
+  XFreeCursor (graphic.display, graphic.cursor);
+  FlushDisplay ();
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/Ximage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/Ximage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/Ximage.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "Ximage.h"
+
+int main (int argc, char **argv) {
+  
+  /* this is a lame place to put this default */
+  graphic.x = 10;
+  graphic.y = 10;
+  graphic.dx = 512;
+  graphic.dy = 512; 
+
+  args (&argc, argv);
+
+  if (USE_XWINDOW) SetUpDisplay (&argc, argv); /* skip if Xless */
+  if (USE_XWINDOW) SetUpWindow (&argc, argv);  /* skip if Xless */
+
+  DefineLayout (argc, argv); 
+  EventLoop ();
+
+  if (USE_XWINDOW) CloseDisplay ();
+  exit (0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa/setup/args.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "Ximage.h"
+
+int args (int *argc, char **argv) {
+
+  int N;
+
+  MAP_WINDOW = TRUE;
+  if ((N = get_argument (*argc, argv, "-nomap"))) {
+    remove_argument(N, argc, argv);
+    MAP_WINDOW = FALSE;
+  }
+
+  NAME_WINDOW = NULL;
+  if ((N = get_argument (*argc, argv, "-name"))) {
+    remove_argument(N, argc, argv);
+    NAME_WINDOW = strcreate (argv[N]);
+    remove_argument(N, argc, argv);
+  }
+
+  USE_XWINDOW = TRUE;
+  if ((N = get_argument (*argc, argv, "-noX"))) {
+    remove_argument(N, argc, argv);
+    USE_XWINDOW = FALSE;
+  }
+
+  if ((N = get_argument (*argc, argv, "-debug"))) {
+    remove_argument(N, argc, argv);
+    DEBUG = TRUE;
+  } else {
+    DEBUG = FALSE;
+  }
+
+  FOREGROUND = FALSE;
+  if ((N = get_argument (*argc, argv, "-fg"))) {
+    remove_argument(N, argc, argv);
+    FOREGROUND = TRUE;
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+bin
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/Makefile	(revision 22322)
@@ -0,0 +1,94 @@
+default: kapa
+help:
+	@echo "make options: kapa (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/kapa2
+LIB	=	$(HOME)/lib
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+INC	=	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+LDDEPS  = $(DESTLIB)/libkapa.a $(DESTLIB)/libdvo.a $(DESTLIB)/libFITS.a $(DESTLIB)/libohana.a
+INDEPS  = $(DESTINC)/kapa.h $(DESTINC)/dvo.h $(DESTINC)/gfitsio.h $(DESTINC)/ohana.h
+
+kapa: $(BIN)/kapa.$(ARCH)
+install: $(DESTBIN)/kapa
+
+KAPA = \
+$(SRC)/kapa.$(ARCH).o                     $(SRC)/args.$(ARCH).o               \
+$(SRC)/SetUpGraphic.$(ARCH).o             $(SRC)/NameWindow.$(ARCH).o         \
+$(SRC)/CheckColors.$(ARCH).o              $(SRC)/MakeGC.$(ARCH).o             \
+$(SRC)/CheckDisplayName.$(ARCH).o  	  $(SRC)/MapWindow.$(ARCH).o          \
+$(SRC)/CheckGeometry.$(ARCH).o     	  $(SRC)/OpenDisplay.$(ARCH).o        \
+$(SRC)/CloseDisplay.$(ARCH).o      	  $(SRC)/QuitX.$(ARCH).o              \
+$(SRC)/CreateWindow.$(ARCH).o      	  $(SRC)/SetNormalHints.$(ARCH).o     \
+$(SRC)/GetColor.$(ARCH).o          	  $(SRC)/SetWMHints.$(ARCH).o         \
+$(SRC)/LoadFont.$(ARCH).o          	  $(SRC)/TopWindow.$(ARCH).o          \
+$(SRC)/MakeCursor.$(ARCH).o		  $(SRC)/FlushDisplay.$(ARCH).o       \
+$(SRC)/LoadObject.$(ARCH).o		  $(SRC)/LoadFrame.$(ARCH).o	      \
+$(SRC)/LoadTextlines.$(ARCH).o		  $(SRC)/LoadLabels.$(ARCH).o         \
+$(SRC)/DrawObjects.$(ARCH).o              $(SRC)/DrawFrame.$(ARCH).o          \
+$(SRC)/DrawTextlines.$(ARCH).o		  $(SRC)/DrawLabels.$(ARCH).o         \
+$(SRC)/PSObjects.$(ARCH).o		  $(SRC)/PSFrame.$(ARCH).o            \
+$(SRC)/PSLabels.$(ARCH).o		  $(SRC)/PSTextlines.$(ARCH).o        \
+$(SRC)/bDrawObjects.$(ARCH).o	  	  $(SRC)/bDrawFrame.$(ARCH).o         \
+$(SRC)/bDrawLabels.$(ARCH).o              $(SRC)/bDrawIt.$(ARCH).o            \
+$(SRC)/PNGit.$(ARCH).o                    $(SRC)/PPMit.$(ARCH).o	      \
+$(SRC)/PSit.$(ARCH).o                     $(SRC)/CrossHairs.$(ARCH).o         \
+$(SRC)/CheckPipe.$(ARCH).o                $(SRC)/EventLoop.$(ARCH).o          \
+$(SRC)/Reconfig.$(ARCH).o                 $(SRC)/Refresh.$(ARCH).o            \
+$(SRC)/Layout.$(ARCH).o		  	  $(SRC)/Sections.$(ARCH).o	      \
+$(SRC)/Graphs.$(ARCH).o                   $(SRC)/SetGraphSize.$(ARCH).o       \
+$(SRC)/Resize.$(ARCH).o                   $(SRC)/ErasePlots.$(ARCH).o         \
+$(SRC)/EraseImage.$(ARCH).o         	  $(SRC)/SetToolbox.$(ARCH).o         \
+$(SRC)/EraseCurrentPlot.$(ARCH).o         $(SRC)/EraseSections.$(ARCH).o      \
+$(SRC)/SetSection.$(ARCH).o		  $(SRC)/DefineSection.$(ARCH).o      \
+$(SRC)/SetLimits.$(ARCH).o                $(SRC)/SetFont.$(ARCH).o            \
+$(SRC)/Center.$(ARCH).o                   $(SRC)/Remap.$(ARCH).o              \
+$(SRC)/Remap8.$(ARCH).o                   $(SRC)/Remap16.$(ARCH).o            \
+$(SRC)/Remap24.$(ARCH).o                  $(SRC)/Remap32.$(ARCH).o            \
+$(SRC)/LoadPicture.$(ARCH).o              $(SRC)/Image.$(ARCH).o              \
+$(SRC)/SetImageSize.$(ARCH).o             $(SRC)/GetPixelCount.$(ARCH).o      \
+$(SRC)/StatusBox.$(ARCH).o                $(SRC)/UpdateStatusBox.$(ARCH).o    \
+$(SRC)/DrawButton.$(ARCH).o               $(SRC)/DrawBitmap.$(ARCH).o         \
+$(SRC)/hh_hms.$(ARCH).o	                  $(SRC)/CreateZoom.$(ARCH).o         \
+$(SRC)/CreatePicture.$(ARCH).o            $(SRC)/CreateColorbar.$(ARCH).o     \
+$(SRC)/MakeColormap.$(ARCH).o		  $(SRC)/SetColormap.$(ARCH).o        \
+$(SRC)/PaintOverlay.$(ARCH).o		  $(SRC)/SetGraphData.$(ARCH).o       \
+$(SRC)/LoadOverlay.$(ARCH).o		  $(SRC)/EraseOverlay.$(ARCH).o       \
+$(SRC)/SaveOverlay.$(ARCH).o              $(SRC)/SetImageData.$(ARCH).o       \
+$(SRC)/CSaveOverlay.$(ARCH).o		  $(SRC)/EraseOverlay.$(ARCH).o       \
+$(SRC)/CheckVisual.$(ARCH).o              $(SRC)/CursorOps.$(ARCH).o          \
+$(SRC)/InterpretKeys.$(ARCH).o            $(SRC)/InterpretPresses.$(ARCH).o   \
+$(SRC)/DragColorbar.$(ARCH).o             $(SRC)/Reorient.$(ARCH).o           \
+$(SRC)/InButton.$(ARCH).o                 $(SRC)/InPicture.$(ARCH).o          \
+$(SRC)/CheckButtons.$(ARCH).o             $(SRC)/InvertButton.$(ARCH).o       \
+$(SRC)/UpdatePointer.$(ARCH).o            $(SRC)/JPEGit24.$(ARCH).o           \
+$(SRC)/bDrawOverlay.$(ARCH).o             $(SRC)/ButtonFunctions.$(ARCH).o    \
+$(SRC)/PSimage.$(ARCH).o                  $(SRC)/PSPixmap.$(ARCH).o           \
+$(SRC)/PSOverlay.$(ARCH).o                $(SRC)/SetChannel.$(ARCH).o         \
+$(SRC)/SetColorScale.$(ARCH).o            $(SRC)/ColorCube.$(ARCH).o          \
+$(SRC)/ColorHistogram.$(ARCH).o           $(SRC)/CreateWide.$(ARCH).o
+
+#$(SRC)/CreateZoom8.$(ARCH).o              $(SRC)/CreateZoom16.$(ARCH).o       \
+#$(SRC)/CreateZoom24.$(ARCH).o             $(SRC)/CreateZoom32.$(ARCH).o       \
+
+OBJ  =  $(KAPA)
+
+# dependancy rules for include files ########################
+$(OBJ): $(INC)/Ximage.h \
+	$(INC)/constants.h \
+	$(INC)/structures.h \
+	$(INC)/prototypes.h
+
+$(OBJ): $(INDEPS) $(LDDEPS)
+
+$(BIN)/kapa.$(ARCH): $(OBJ)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/doc/UpgradeConcepts.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/doc/UpgradeConcepts.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/doc/UpgradeConcepts.txt	(revision 22322)
@@ -0,0 +1,54 @@
+
+2007.05.08
+
+  User Commands:
+  * Add a section (position, type, name)
+  * Add a section element (add graph, add image)
+  * Del a section (by name)
+  * Del a section element (del graph, del image)
+  * Resize window (resizes each section)
+  * Set active section
+  * Get active section
+
+2007.05.06
+
+I am examining plans to upgrade the Kapa / Kii programs.  I have several ideas on this topic:
+
+* Completely merge the code base.  There are many common features, and
+  the merge will be easy.  The biggest difference between them from a
+  programming point of view is that Kapa uses global variables for the
+  layout and graphic (and other minor data), while Kii passes around
+  the Graphic / Layout elements.
+
+  The more object-oriented way to do this is to have private static
+  data for these concepts and to provide accessor functions to operate
+  on or retreive these pointers.  
+
+* Allow an arbitrary number of the graph / image widgets.  The image
+  widget should be considered a single item, which includes the main
+  image window, the zoom box, the status box, the buttons, etc.  The
+  positioning of the multiple graph / image elements in a single
+  window could be equivalent to the 'section' command for the graph
+  sub-boxes.  An image or a graph could be place at any X,Y,dX,dY
+  position with X,Y having a range of 0-1.  In addition, a graph
+  could be optionally tied to a specific image, with the location
+  determined by the image window location (ie, outer box of the image
+  window matched to bounding box of the graph).
+
+* Event parsing would need to go through the list of graph / image
+  elements to determine if the corresond to any of them.  
+
+* Drawing should be done in the order: images first, graphs second.
+  The sequence for the images and / or graphs should be adjustable.
+
+* create an equivalent to the zoom box which is a wide full-view of
+  the current image.
+
+* add the image flipping; this may be easiest using multiple versions
+  of the code with the correct sign flips...
+
+* carry the image data as a full 32 bit buffer
+
+* (optionally?) listed on an IP socket?
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/Ximage.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/Ximage.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/Ximage.h	(revision 22322)
@@ -0,0 +1,15 @@
+# include <X11/Xatom.h>
+# include <X11/Xlib.h>
+# include <X11/Xresource.h>
+# include <X11/Xutil.h>
+# include <X11/cursorfont.h>
+# include <X11/keysym.h>
+# include <X11/keysymdef.h>
+
+# include <ohana.h>
+# include <dvo.h>
+# include <kapa.h>
+# include "constants.h"
+# include "structures.h"
+# include "prototypes.h"
+# include "globals.h"
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/alphabet.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/alphabet.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/alphabet.h	(revision 22322)
@@ -0,0 +1,53 @@
+# include "../rotfont/times8.h"
+# include "../rotfont/times12.h"
+# include "../rotfont/times14.h"
+# include "../rotfont/times18.h"
+# include "../rotfont/times24.h"
+
+# include "../rotfont/courier8.h"
+# include "../rotfont/courier12.h"
+# include "../rotfont/courier14.h"
+# include "../rotfont/courier18.h"
+# include "../rotfont/courier24.h"
+
+# include "../rotfont/helvetica8.h"
+# include "../rotfont/helvetica12.h"
+# include "../rotfont/helvetica14.h"
+# include "../rotfont/helvetica18.h"
+# include "../rotfont/helvetica24.h"
+
+# include "../rotfont/symbol8.h"
+# include "../rotfont/symbol12.h"
+# include "../rotfont/symbol14.h"
+# include "../rotfont/symbol18.h"
+# include "../rotfont/symbol24.h"
+
+# define DEFFONT 1
+static FontSet HardwiredFonts[] = {
+  {times8font,  "times", 8},
+  {times12font, "times", 12},
+  {times14font, "times", 14},
+  {times18font, "times", 18},
+  {times24font, "times", 24},
+  
+  {courier8font,  "courier", 8},
+  {courier12font, "courier", 12},
+  {courier14font, "courier", 14},
+  {courier18font, "courier", 18},
+  {courier24font, "courier", 24},
+  
+  {helvetica8font,  "helvetica", 8},
+  {helvetica12font, "helvetica", 12},
+  {helvetica14font, "helvetica", 14},
+  {helvetica18font, "helvetica", 18},
+  {helvetica24font, "helvetica", 24},
+  
+  {symbol8font,  "symbol", 8},
+  {symbol12font, "symbol", 12},
+  {symbol14font, "symbol", 14},
+  {symbol18font, "symbol", 18},
+  {symbol24font, "symbol", 24}
+};
+
+/* put these as static in RotFont.c with accessor functions */
+# define NROT 256
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/buttons.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/buttons.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/buttons.h	(revision 22322)
@@ -0,0 +1,144 @@
+#define PS_width 25
+#define PS_height 25
+static char PS_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x81, 0x3f, 0x00,
+   0x82, 0x67, 0x60, 0x00, 0x02, 0x26, 0x40, 0x00, 0x02, 0x24, 0x40, 0x00,
+   0x02, 0x24, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00,
+   0x02, 0x24, 0x00, 0x00, 0x02, 0x64, 0x00, 0x00, 0x02, 0xc6, 0x00, 0x00,
+   0x82, 0x07, 0x07, 0x00, 0xfe, 0x01, 0x0c, 0x00, 0x02, 0x00, 0x18, 0x00,
+   0x02, 0x00, 0x20, 0x00, 0x02, 0x00, 0x60, 0x00, 0x02, 0x00, 0x40, 0x00,
+   0x02, 0x00, 0xc0, 0x00, 0x02, 0x10, 0x80, 0x00, 0x02, 0x10, 0xc0, 0x00,
+   0x02, 0x60, 0x60, 0x00, 0x02, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00}; 
+#define grey_width 25
+#define grey_height 25
+static char grey_bits[] = {
+   0x90, 0x40, 0xf7, 0x01, 0x04, 0xaa, 0xad, 0x01, 0x68, 0xd5, 0xfe, 0x01,
+   0x82, 0x28, 0xdb, 0x01, 0x10, 0xd2, 0x75, 0x01, 0x40, 0xad, 0xee, 0x01,
+   0xac, 0x50, 0xbf, 0x01, 0x00, 0xaa, 0xf9, 0x01, 0x52, 0xd5, 0xd6, 0x01,
+   0x80, 0x28, 0x6f, 0x01, 0x28, 0xd2, 0xfd, 0x01, 0x44, 0xad, 0xba, 0x01,
+   0x90, 0x50, 0xf7, 0x01, 0x02, 0xaa, 0xcd, 0x01, 0x68, 0xd5, 0x7e, 0x01,
+   0x80, 0x28, 0xfb, 0x01, 0x14, 0xd2, 0xb5, 0x01, 0x40, 0xad, 0xee, 0x01,
+   0xaa, 0x50, 0xdf, 0x01, 0x00, 0xaa, 0x79, 0x01, 0x50, 0xd5, 0xf6, 0x01,
+   0x84, 0x28, 0xaf, 0x01, 0x28, 0xd2, 0xfd, 0x01, 0x40, 0xad, 0xda, 0x01,
+   0x90, 0x50, 0x77, 0x01};
+#define rainbow_width 25
+#define rainbow_height 25
+static char rainbow_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x80, 0xff, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x0c, 0x7f, 0x00,
+   0x00, 0xe3, 0x80, 0x01, 0xc0, 0x9c, 0xff, 0x00, 0x20, 0x62, 0x00, 0x01,
+   0x90, 0x99, 0xff, 0x00, 0x48, 0x76, 0x00, 0x01, 0x24, 0x09, 0x00, 0x00,
+   0x94, 0x06, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
+   0xa5, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+   0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+   0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+   0x15, 0x00, 0x00, 0x00};
+#define recenter_width 25
+#define recenter_height 25
+static char recenter_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00,
+   0x80, 0xef, 0x03, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0x30, 0x00, 0x18, 0x00,
+   0x10, 0x00, 0x10, 0x00, 0x18, 0x00, 0x30, 0x00, 0x08, 0x00, 0x20, 0x00,
+   0x0c, 0x00, 0x60, 0x00, 0x0c, 0x38, 0x60, 0x00, 0x0c, 0x7c, 0x60, 0x00,
+   0x04, 0x7c, 0x40, 0x00, 0x0c, 0x7c, 0x60, 0x00, 0x0c, 0x38, 0x60, 0x00,
+   0x0c, 0x00, 0x60, 0x00, 0x08, 0x00, 0x20, 0x00, 0x18, 0x00, 0x30, 0x00,
+   0x10, 0x00, 0x10, 0x00, 0x30, 0x00, 0x18, 0x00, 0xe0, 0x00, 0x0e, 0x00,
+   0x80, 0xef, 0x03, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define heat_width 25
+#define heat_height 25
+static char heat_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0xd8, 0x01,
+   0x00, 0x0c, 0x37, 0x00, 0xf8, 0xc9, 0x6f, 0x00, 0x0c, 0xde, 0xf0, 0x00,
+   0x06, 0x5b, 0x1f, 0x00, 0x7b, 0xd9, 0x0c, 0x00, 0xc9, 0x70, 0x06, 0x00,
+   0x81, 0x71, 0x03, 0x00, 0x78, 0xb9, 0x39, 0x00, 0xc8, 0xdb, 0x0f, 0x00,
+   0x84, 0xf7, 0x7f, 0x00, 0x04, 0xff, 0xc3, 0x00, 0xe6, 0xfe, 0x8e, 0x00,
+   0x9a, 0x7d, 0x0b, 0x00, 0x08, 0xbc, 0x01, 0x00, 0x08, 0xfc, 0x3f, 0x00,
+   0x0c, 0xff, 0x78, 0x00, 0x84, 0x39, 0x60, 0x00, 0x40, 0x38, 0x40, 0x00,
+   0x60, 0x38, 0x40, 0x00, 0x20, 0x38, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
+   0x00, 0xd6, 0x00, 0x00};
+#define red_width 25
+#define red_height 25
+static char red_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xfc, 0x03, 0x00,
+   0x20, 0x07, 0x06, 0x00, 0xe0, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x08, 0x00,
+   0x20, 0x00, 0x08, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x00, 0x08, 0x00,
+   0x20, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x06, 0x00, 0x20, 0x80, 0x01, 0x00,
+   0x20, 0xe0, 0x00, 0x00, 0x20, 0x7e, 0x00, 0x00, 0xe0, 0xc3, 0x00, 0x00,
+   0x20, 0xc0, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x20, 0x80, 0x01, 0x00,
+   0x20, 0x00, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00, 0x20, 0x00, 0x03, 0x00,
+   0x20, 0x00, 0x06, 0x00, 0x20, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x18, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define green_width 25
+#define green_height 25
+static char green_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00,
+   0x00, 0x06, 0x04, 0x00, 0x80, 0x03, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x80, 0x0f, 0x00, 0x40, 0xc0, 0x30, 0x00,
+   0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x08, 0x00,
+   0x00, 0x03, 0x08, 0x00, 0x00, 0x7e, 0x0e, 0x00, 0x00, 0xc0, 0x03, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define blue_width 25
+#define blue_height 25
+static char blue_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xfe, 0x00, 0x00,
+   0xc0, 0x83, 0x07, 0x00, 0xc0, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x04, 0x00, 0x40, 0x00, 0x02, 0x00,
+   0x40, 0x80, 0x07, 0x00, 0x40, 0xe0, 0x0c, 0x00, 0xc0, 0x3f, 0x08, 0x00,
+   0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x06, 0x00, 0x40, 0x00, 0x02, 0x00,
+   0xc0, 0x80, 0x03, 0x00, 0x40, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define yellow_width 25
+#define yellow_height 25
+static char yellow_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00,
+   0x80, 0x01, 0x0c, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x02, 0x02, 0x00,
+   0x00, 0x84, 0x01, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define hms_width 25
+#define hms_height 25
+static char hms_bits[] = {
+   0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+   0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0xf0, 0x00, 0x1e, 0xef, 0x89, 0x00,
+   0x22, 0x11, 0x09, 0x00, 0x22, 0x11, 0x71, 0x00, 0x22, 0x11, 0x81, 0x00,
+   0x22, 0x11, 0x89, 0x00, 0x22, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x1e, 0xc7, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x80, 0x00,
+   0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0x80, 0x00,
+   0x40, 0x80, 0x80, 0x00, 0x5c, 0xb8, 0xb8, 0x00, 0x62, 0xc4, 0xc4, 0x00,
+   0x42, 0x84, 0x84, 0x00, 0x42, 0x84, 0x84, 0x00, 0x62, 0xc4, 0xc4, 0x00,
+   0x5c, 0xb9, 0xb8, 0x00};
+#define flipx_width 25
+#define flipx_height 25
+static char flipx_bits[] = {
+   0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x80, 0x10, 0x02, 0x00,
+   0x40, 0x00, 0x04, 0x00, 0x20, 0x10, 0x08, 0x00, 0x10, 0x10, 0x10, 0x00,
+   0x08, 0x00, 0x20, 0x00, 0x04, 0x10, 0x40, 0x00, 0x02, 0x10, 0x80, 0x00,
+   0x01, 0x00, 0x00, 0x01, 0x02, 0x10, 0x80, 0x00, 0x04, 0x10, 0x40, 0x00,
+   0x08, 0x00, 0x20, 0x00, 0x10, 0x10, 0x10, 0x00, 0x20, 0x10, 0x08, 0x00,
+   0x40, 0x00, 0x04, 0x00, 0x80, 0x10, 0x02, 0x00, 0x00, 0x11, 0x01, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+   0x00, 0x10, 0x00, 0x00 };
+#define flipy_width 25
+#define flipy_height 25
+static char flipy_bits[] = {
+   0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
+   0x00, 0x82, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x00, 0x02, 0x00,
+   0x40, 0x00, 0x04, 0x00, 0x20, 0x00, 0x08, 0x00, 0x10, 0x00, 0x10, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0xb7, 0x6d, 0xdb, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x20, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x04, 0x00, 0x80, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00,
+   0x00, 0x82, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
+   0x00, 0x10, 0x00, 0x00 };
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/constants.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/constants.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/constants.h	(revision 22322)
@@ -0,0 +1,93 @@
+/* hardwired values for some window parameters */
+
+# define EVENT_MASK (long) \
+(ButtonPressMask \
+ | ClientMessage \
+ | ButtonReleaseMask \
+ | KeyPressMask \
+ | ExposureMask \
+ | StructureNotifyMask \
+ | PointerMotionMask)
+
+# define NCHANNELS 3
+# define NPIXELS_DYNAMIC 128
+
+// XXX for the moment, this is set to match the values in SetColorScale3D_CC
+# define NPIXELS_STATIC 4600
+
+# define PAD1  3
+# define PAD2  5
+# define TEXTPAD 25
+# define COLORPAD 10
+# define TEXT_Y 15
+# define ZOOM_X 152
+# define ZOOM_Y 152
+# define NOVERLAYS 4
+# define BUTTON_WIDTH 28
+# define BUTTON_HEIGHT 28
+
+# define DEFAULT_CURSOR XC_crosshair
+# define BORDER_WIDTH 2
+# define MIN_WIDTH 50
+# define MIN_HEIGHT 50
+# define LABEL_MAXLEN 128
+
+typedef enum {
+    KAPA_SCALE_1D,
+    KAPA_SCALE_3D_RUFF,
+    KAPA_SCALE_3D_FULL
+} KapaColorScaleMode;
+
+/* label names */
+typedef enum {
+  LABELX0,
+  LABELY0,
+  LABELX1,
+  LABELY1,
+  LABELUL,
+  LABELUR,
+  LABELLL,
+  LABELLR
+} KapaLabelMode;
+
+typedef enum {
+  KAPA_CHANNEL_RED,
+  KAPA_CHANNEL_GREEN,
+  KAPA_CHANNEL_BLUE,
+} KapaChannels;
+
+// use an enum to identify the 3 dimensions:
+typedef enum {
+  CC_X,
+  CC_Y,
+  CC_Z,
+} CCDimen;
+
+/* EVENT_MASK consists of:
+
+ExposureMask        : Expose
+StructureNotifyMask : CirculateNotify | 
+                      ConfigureNotify | 
+                      DestroyNotify   | 
+		      GravityNotify   | 
+		      MapNotify       |
+		      ReparentNotify  |
+		      UnmapNotify
+ButtonPressMask     : ButtonPress
+ButtonReleaseMask   : ButtonRelease
+KeyPressMask        : KeyPress
+PointerMotionMask   : MotionNotify
+(always)            : ClientMessage 
+(always)            : MappingNotify
+
+*/
+
+# define NOVERLAYS 4
+/* number of overlays is defined here.
+   the number is also crucial in the following files:
+   PositionPictures.c
+   MakeColormap.c
+   prototypes.h
+*/
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/globals.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/globals.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/globals.h	(revision 22322)
@@ -0,0 +1,28 @@
+
+int   ACTIVE_CURSOR;
+int   HAVE_BACKING;
+int   DEBUG;
+int   USE_XWINDOW;
+int   MAP_WINDOW;
+int   FOREGROUND;
+char *NAME_WINDOW;
+
+/* these should be absorbed into KapaImageWidget 
+int OVERLAY[NOVERLAYS];
+int MOVE_POINTER;
+int DECIMAL_DEG;
+*/
+
+/* file descriptor for socket connection to mana */
+// int sock; 
+
+/* each layout / section defines one of the active portions of the graphics window
+   there may be an arbitrary number of sections, and each section may fill an arbitrary
+   rectangle in the X window.  The startup configuration assumes 1 section filling the 
+   entire X window. */
+
+// Layout *section;
+// int    Nsection, TheSection;
+
+/* graphic defines the basic details of the X window */
+// Graphic graphic;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/icons.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/icons.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/icons.h	(revision 22322)
@@ -0,0 +1,43 @@
+
+#define icon_width 29
+#define icon_height 29
+static unsigned char icon_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+/* *** icons for buttons, not used ***
+#define arrow_width 6
+#define arrow_height 9
+static char arrow_bits[] = {
+   0x00, 0x02, 0x06, 0x0e, 0x1e, 0x0e, 0x06, 0x02, 0x00};
+#define back_width 16
+#define back_height 16
+static char back_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x3c, 0x00, 0x3f, 0xc0, 0x3f,
+   0xf0, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xf0, 0x3f, 0xc0, 0x3f, 0x00, 0x3f,
+   0x00, 0x3c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00};
+#define down_width 9
+#define down_height 6
+static char down_bits[] = {
+   0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00};
+#define fore_width 16
+#define fore_height 16
+static char fore_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x3c, 0x00, 0xfc, 0x00, 0xfc, 0x03,
+   0xfc, 0x0f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x0f, 0xfc, 0x03, 0xfc, 0x00,
+   0x3c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00};
+#define stop_width 16
+#define stop_height 16
+static char stop_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
+   0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
+   0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/kii.proto.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/kii.proto.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/kii.proto.h	(revision 22322)
@@ -0,0 +1,124 @@
+
+/* top-level program */
+int           args                PROTO((int *argc, char **argv));
+void          SetUpDisplay        PROTO((Graphic *, int *, char **));
+void          SetUpWindow         PROTO((Graphic *, int *, char **));
+void          DefineLayout        PROTO((Layout *, Graphic *, int, char **));
+int           EventLoop           PROTO((Graphic *, Layout *));
+void          CloseDisplay        PROTO((Graphic *));
+
+/* SetUpDisplay */
+void          CheckDisplayName    PROTO((int *, char **, char *));
+Display      *OpenDisplay         PROTO((char *, int *));
+void	      CheckVisual	  PROTO((Graphic *graphic, int *argc, char **argv));
+void          CheckColors         PROTO((Graphic *, int *, char **));
+
+/* SetUpWindow */
+void          CheckGeometry       PROTO((int *, char **, Graphic *));
+void          TopWindow           PROTO((Graphic *, Icon *));
+void           CreateWindow       PROTO((Graphic *, Window, int, long));
+void           MakeGC             PROTO((Graphic *));
+void           MakeCursor         PROTO((Graphic *, unsigned int));
+void          LoadFont            PROTO((Graphic *, int *, char **, char *));
+void           CheckFontName      PROTO((int *, char **, char *));
+void          SetNormalHints      PROTO((Graphic *));
+void          SetWMHints          PROTO((Graphic *, Icon *));
+void          NameWindow          PROTO((Graphic *, char *));
+void          MapWindow           PROTO((Graphic *));
+
+/* DefineLayout - fns below may be called if layout is changed? */
+void          PositionPictures    PROTO((Layout *, Graphic *));
+
+void          MakeColormap        PROTO((Graphic *, Layout *, int, char **));
+int	      SetColormap	  PROTO((Graphic *graphic, Layout *layout, char *name));
+void          CreateColorbar      PROTO((Layout *, Graphic *));
+void          CreatePicture       PROTO((Layout *, Graphic *));
+
+/* EventLoop */
+int           CheckPipe           PROTO((Graphic *, Layout *));
+int           Reconfig            PROTO((Graphic *, Layout *, XEvent *));
+void          Refresh             PROTO((Graphic *, Layout *, int));
+int           UpdatePointer       PROTO((Graphic *, Layout *, XMotionEvent *));
+int           InterpretKeys       PROTO((Graphic *, Layout *, XEvent *));
+int           InterpretPresses    PROTO((Graphic *, Layout *, XButtonEvent *));
+
+/* CheckPipe */
+int           NewPicture          PROTO((Graphic *, Layout *));
+int           EraseOverlay        PROTO((Graphic *, Layout *));
+int           LoadOverlay         PROTO((Graphic *, Layout *));
+int	      LoadTickmarks	  PROTO((Graphic *graphic, Layout *layout));
+int           SaveOverlay         PROTO((Graphic *, Layout *));
+int           CSaveOverlay        PROTO((Graphic *, Layout *));
+int	      PSit		  PROTO((Graphic *graphic, Layout *layout, int Raw));
+int	      JPEGit		  PROTO((Graphic *graphic, Layout *layout));
+int	      JPEGit24		  PROTO((Graphic *graphic, Layout *layout));
+int	      Resize		  PROTO((Graphic *graphic, Layout *layout));
+int	      Center		  PROTO((Graphic *graphic, Layout *layout));
+
+/* imagezoom functions */
+void          CreateZoom          PROTO((Layout *, Graphic *, double, double));
+void	      CreateZoom8	  PROTO((Layout *layout, Graphic *graphic, double x, double y));
+void	      CreateZoom16	  PROTO((Layout *layout, Graphic *graphic, double x, double y));
+void	      CreateZoom24	  PROTO((Layout *layout, Graphic *graphic, double x, double y));
+void	      CreateZoom32	  PROTO((Layout *layout, Graphic *graphic, double x, double y));
+void	      UpdateStatusBox	  PROTO((Graphic *graphic, Layout *layout, double x, double y, double z, int mode));
+void          CrossHairs          PROTO((Graphic *, Layout *));
+
+/* X image drawing functions */
+void          Reorient            PROTO((Graphic *, Layout *, double, double, int));
+void          ReorientOnButton    PROTO((Graphic *, Layout *, XButtonEvent *));
+int	      Recenter		  PROTO((Graphic *graphic, Layout *layout));
+void          PaintOverlay        PROTO((Graphic *, Layout *, int));
+void	      PaintTickmarks	  PROTO((Graphic *graphic, Layout *layout));
+void          Remap               PROTO((Graphic *, Layout *, Matrix  *));
+void	      Remap8		  PROTO((Graphic *graphic, Layout *layout, Matrix *matrix));
+void	      Remap16		  PROTO((Graphic *graphic, Layout *layout, Matrix *matrix));
+void	      Remap24		  PROTO((Graphic *graphic, Layout *layout, Matrix *matrix));
+void	      Remap32		  PROTO((Graphic *graphic, Layout *layout, Matrix *matrix));
+
+/* PS image drawing functions */
+void	      ConvertPixmap8	  PROTO((Layout *layout, FILE *f));
+void	      ConvertPixmap16	  PROTO((Layout *layout, FILE *f));
+void	      ConvertPixmap24	  PROTO((Layout *layout, FILE *f));
+void	      ConvertPixmap32	  PROTO((Layout *layout, FILE *f));
+void	      DrawOverlay	  PROTO((Graphic *graphic, Layout *layout, int N, FILE *f, int extra));
+
+/* JPEG image drawing functions */
+void	      bDrawOverlay	  PROTO((Layout *layout, int N));
+
+/***** Prototypes for image ***************/
+void          StatusBox           PROTO((Graphic *, Layout *));
+
+/***** Prototypes for action ***************/
+int           InPicture           PROTO((XButtonEvent *, Picture *));
+int           Stop                PROTO((Graphic *, Layout *));
+void          DragColorbar        PROTO((Graphic *, Layout *, XButtonEvent *));
+void          ResetColorbar       PROTO((Graphic *, Layout *, double, double));
+
+/***** Prototypes for button ***************/
+Button       *CheckButtons        PROTO((XButtonEvent *, Layout *));
+void          DrawButton          PROTO((Graphic *, Button *));
+void          FlashButton         PROTO((Graphic *, Button *));
+int           InButton            PROTO((XButtonEvent *, Button *));
+void          InvertButton        PROTO((Graphic *, Button *));
+int           greycolors          PROTO((Graphic *, Layout *));
+int           rainbow             PROTO((Graphic *, Layout *));
+int           puns                PROTO((Graphic *, Layout *));
+int           Recenter            PROTO((Graphic *, Layout *));
+int           RecenterRescale     PROTO((Graphic *, Layout *));
+int           Rescale             PROTO((Graphic *, Layout *));
+int           ToggleDEG           PROTO((Graphic *, Layout *));
+int           Overlay0            PROTO((Graphic *, Layout *));
+int           Overlay1            PROTO((Graphic *, Layout *));
+int           Overlay2            PROTO((Graphic *, Layout *));
+int           Overlay3            PROTO((Graphic *, Layout *));
+
+/***** Prototypes for xtools ***************/
+void	      Screen_to_Image     PROTO((double *x1, double *y1, double x2, double y2, Layout *layout));
+void	      Image_to_Screen	  PROTO((double *x1, double *y1, double x2, double y2, Layout *layout));
+unsigned long GetColor            PROTO((Display *, char *, Colormap, unsigned long));
+void          QuitX               PROTO((Display *, char *, char *));
+void          hh_hms              PROTO((char *, double, double, char));
+void          DrawBitmap          PROTO((Graphic *, int, int, int, int, char *, int));
+int	      cursor		  PROTO((Graphic *graphic, Layout *layout));
+void	      FlushDisplay	  PROTO((Display *display));
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/kii.struct.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/kii.struct.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/kii.struct.h	(revision 22322)
@@ -0,0 +1,129 @@
+
+/**************** Graphic carries X info around ****************/
+typedef struct {
+  Display       *display;
+  int            screen;
+  int            depth;
+  Window         window;
+  Visual        *visual;
+  int            visualclass;
+  GC             gc;
+  XFontStruct   *font;
+  Cursor         cursor;
+  int            x,  y;
+  int            dx, dy;
+  Colormap       colormap;
+  unsigned long  Npixels, pixels[256];
+  int            Nbits;
+  /*
+  unsigned long  fore;
+  unsigned long  back;
+  */
+  unsigned long  black, white;
+} Graphic;
+
+/**************** X related "widget" structures ****************/
+typedef struct {
+  int      x, y, dx, dy;      /* position and size */
+  int      text;              /* does this have a picture or text? */
+  int      width, height;
+  char    *bitmap;          /* picture on button */
+  int    (*function_1) ();  /* mouse_button 1 function */
+  int    (*function_2) ();  /* mouse_button 2 function */
+  int    (*function_3) ();  /* mouse_button 3 function */
+} Button;
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  char    *label;          /* label on TextLine */
+  char     text[1024];     /* words of TextLine */
+  char     old_text[1024]; /* words of TextLine */
+  int      outline;        /* draw an outline?  */ 
+  int      cursor;         /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextLine;
+
+typedef char STRING[1024];
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  STRING  *text;           /* words of TextLine */
+  int      Nlines;
+  int      outline;        /* draw an outline?  */ 
+  int      cursor_line;    /* cursor line */
+  int      cursor_x;       /* location of cursor (if selected) */
+  int      cursor_y;       /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextBox;
+
+/**************** general structures ****************/
+typedef struct {
+  Pixmap pixmap;
+  int    width;
+  int    height;
+  char  *bits;
+} Icon;
+
+typedef struct {
+  int      dx, dy, x, y;
+  XImage  *pix;
+  char *data;
+} Picture;
+
+typedef struct {
+  char type[10];
+  double x, y;
+  double dx, dy;
+  double angle;
+  char  *text;
+} Object;
+
+typedef struct {
+  int Nobjects;
+  unsigned long color;
+  Object *objects;
+} Overlay;
+  
+/******** Here we define the Layout struct specific to this program  *******/
+typedef struct {
+  /* objects on the mana window */
+  Picture  picture;
+  Picture  cmapbar;
+  Picture  zoom;
+  Button   PS_button;
+  Button   recenter_button;
+  Button   hms_button; /* toggle HMS/DECIMAL mode (DECIMAL_DEG macro) */
+  Button   grey_button;
+  Button   rainbow_button;
+  Button   puns_button;
+  Button   overlay_button[NOVERLAYS];
+  Overlay  overlay[NOVERLAYS];
+  Overlay  tickmarks;
+  int      text_x, text_y;
+
+  /* file descriptor for socket connection to mana */
+  int Ximage; 
+
+  /* data mana needs */
+  Matrix   matrix;         /* data for picture */
+  double   X, Y;           /* image pixel at screen center */
+  int      expand;         /* zoomscale */
+  double   zero, range;    /* zero, range for picture to cmap */
+  double   max, min;       /* zero, range for data to z-value */
+  double   start, slope;   /* zero, range for cmap to pixels */
+  double   x, y, z;        /* last pointer coords */
+  Coords   coords;
+  char     file[1024];     /* name of file */
+  char     buffer_name[1024];  /* name of buffer */
+
+  /* fundamental pieces */
+  unsigned long  black;
+  unsigned long  white;
+  XColor   cmap[256];
+  int      Npixels;
+
+} Layout;
+
+/* this routine is independent of the number of overlays */
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/prototypes.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/prototypes.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/prototypes.h	(revision 22322)
@@ -0,0 +1,254 @@
+
+/* top-level program */
+int	      args		  PROTO((int *argc, char **argv));
+void          SetUpGraphic        PROTO((int *argc, char **argv));
+void          DefineLayout        PROTO((int, char **));
+int	      EventLoop		  PROTO(());
+void	      CloseDisplay	  PROTO(());
+
+/* SetUpGraphic */
+char         *CheckDisplayName    PROTO((int *argc, char **argv));
+Display      *OpenDisplay         PROTO((char *name, int *screen));
+void          CheckColors         PROTO((Graphic *graphic, int *argc, char **argv));
+
+/* SetUpWindow */
+void          CheckGeometry       PROTO((Graphic *graphic, int *argc, char **argv));
+void          TopWindow           PROTO((Graphic *graphic, Icon *icon));
+void          CreateWindow        PROTO((Graphic *graphic, Window parent, int border, long events));
+void	      MakeGC	    	  PROTO((Graphic *graphic));
+void          MakeCursor          PROTO((Graphic *graphic, unsigned int cursor));
+void          LoadFont            PROTO((Graphic *graphic, int *argc, char **argv, char *default_name));
+void          SetNormalHints      PROTO((Graphic *graphic));
+void          SetWMHints          PROTO((Graphic *graphic, Icon *icon));
+void          NameWindow          PROTO((Graphic *graphic, char *name));
+void          MapWindow           PROTO((Graphic *graphic));
+void          CheckVisual         PROTO((Graphic *graphic, int *argc, char **argv));
+
+/* X drawing utilities */
+void	      DrawFrame		  PROTO((KapaGraphWidget *graph));
+int           DrawObjects         PROTO((KapaGraphWidget *graph));
+void          DrawLabels          PROTO((KapaGraphWidget *graph));
+void          EraseLabels         PROTO((KapaGraphWidget *graph));
+void          DrawLabelsRaw       PROTO((Graphic *graphic, KapaGraphWidget *graph, int color));
+void	      DrawTextlines	  PROTO((KapaGraphWidget *graph));
+void          DrawConnect         PROTO((KapaGraphWidget *graph, Gobjects *objects));
+void          DrawHistogram       PROTO((KapaGraphWidget *graph, Gobjects *objects));
+int           DrawObjectN         PROTO((KapaGraphWidget *graph, Gobjects *objects));
+void          DrawPoints          PROTO((KapaGraphWidget *graph, Gobjects *objects));
+void          DrawXErrors         PROTO((KapaGraphWidget *graph, Gobjects *objects));
+void          DrawYErrors         PROTO((KapaGraphWidget *graph, Gobjects *objects));
+void	      DrawTick		  PROTO((int fx, int fy, int dfx, int dfy, int P, double min, double max, double value, int IsLabel, int IsMajor, int naxis));
+void	      AxisTickScale	  PROTO((Axis *axis, double *major, double *minor));
+
+/* EventLoop */
+int           PScommand           PROTO((int sock));
+int           CheckPipe           PROTO(());
+int           Reconfig            PROTO((XEvent *event));
+void          Refresh             PROTO(());
+
+/* CheckPipe */
+int           PNGit               PROTO((int sock));
+int           PPMit               PROTO((int sock));
+int           LoadFrame           PROTO((int sock));
+int           LoadObject          PROTO((int sock));
+int           LoadLabels          PROTO((int sock));
+int           LoadTextlines       PROTO((int sock));
+int           Resize              PROTO((int sock));
+int           GetLimits           PROTO((int sock));
+int           SetLimits           PROTO((int sock));
+int           SetSection          PROTO((int sock));
+int           ListSection         PROTO((int sock));
+int           MoveSection         PROTO((int sock));
+int           DefineSection       PROTO((int sock));
+int           SetFont             PROTO((int sock));
+int           EraseCurrentPlot    PROTO((void));
+int           ErasePlots          PROTO((void));
+int           EraseSections       PROTO((void));
+int           EraseImage          PROTO((void));
+int           SetGraphData        PROTO((int sock));
+int           GetGraphData        PROTO((int sock));
+int           SetImageData        PROTO((int sock));
+int           GetImageData        PROTO((int sock));
+int           SetImageCoords      PROTO((int sock));
+int           GetImageCoords      PROTO((int sock));
+int           GetImageRange       PROTO((int sock));
+int           SetChannel          PROTO((int sock));
+int           SetColormapFromPipe PROTO((int sock));
+
+int           LoadVectorData      PROTO((int sock, KapaGraphWidget *graph, int N, char *type));
+
+/* Section Utilities */
+Section      *InitSection	  PROTO(());
+void          FreeSection	  PROTO((Section *section));
+void          FreeSections	  PROTO(());
+Section      *AddSection	  PROTO((char *name, float x, float y, float dx, float dy));
+int           DelSection	  PROTO((char *name));
+int           GetSectionByName	  PROTO((char *name));
+int           GetNumberOfSections PROTO(());
+Section      *GetSectionByNumber  PROTO((int N));
+Section      *GetActiveSection	  PROTO(());
+int           SetActiveSectionByNumber PROTO((int N));
+int           ListSection         PROTO((int sock));
+void          SetSectionSizes     PROTO((Section *section));
+
+KapaGraphWidget *InitGraph        PROTO(());
+void          DrawGraph           PROTO((KapaGraphWidget *graph));
+void          SetGraphSize        PROTO((Section *section));
+void          FreeGraph           PROTO((KapaGraphWidget *graph));
+
+void          InitLayout          PROTO((int argc, char **argv));
+
+/* PS drawing utilities */
+int           PSit                PROTO((char *filename, char *pagename, int scaleMode, int pageMode));
+int           PSFrame             PROTO((KapaGraphWidget *graph, FILE *f));
+int           PSObjects           PROTO((KapaGraphWidget *graph, FILE *f));
+void          PSLabels            PROTO((KapaGraphWidget *graph, FILE *f));
+void	      PSTextlines	  PROTO((KapaGraphWidget *graph, FILE *f));
+void          PSConnect           PROTO((KapaGraphWidget *graph, Gobjects *objects, FILE *f));
+void          PSHistogram         PROTO((KapaGraphWidget *graph, Gobjects *objects, FILE *f));
+void          PSPoints            PROTO((KapaGraphWidget *graph, Gobjects *objects, FILE *f));
+void          PSXErrors           PROTO((KapaGraphWidget *graph, Gobjects *objects, FILE *f));
+void          PSYErrors           PROTO((KapaGraphWidget *graph, Gobjects *objects, FILE *f));
+void	      PSTick		  PROTO((FILE *f, double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int IsLabel, int IsMajor, int naxis));
+void          ClipLinePS          PROTO((double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0, FILE *f));
+int           PSimage    	  PROTO((KapaImageWidget *image, FILE *f));
+void 	      PSOverlay  	  PROTO((KapaImageWidget *image, int N, FILE *f, int extra));
+void 	      PSPixmap8  	  PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
+void 	      PSPixmap16 	  PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
+void 	      PSPixmap24 	  PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
+void 	      PSPixmap32 	  PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
+
+/* kapa bDraw Functions */
+int	      bDrawFrame	  PROTO((KapaGraphWidget *graph));
+int	      bDrawObjects	  PROTO((KapaGraphWidget *graph));
+void	      bDrawLabels	  PROTO((KapaGraphWidget *graph));
+void	      bDrawTextlines	  PROTO((KapaGraphWidget *graph));
+void	      bDrawConnect	  PROTO((KapaGraphWidget *graph, Gobjects *object));
+void	      bDrawHistogram	  PROTO((KapaGraphWidget *graph, Gobjects *object));
+void	      bDrawPoints	  PROTO((KapaGraphWidget *graph, Gobjects *object));
+void	      bDrawXErrors	  PROTO((KapaGraphWidget *graph, Gobjects *object));
+void	      bDrawYErrors	  PROTO((KapaGraphWidget *graph, Gobjects *object));
+void	      bDrawTick		  PROTO((double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int IsLabel, int IsMajor, int naxis));
+void	      bDrawClipLine	  PROTO((double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0));
+bDrawBuffer  *bDrawIt		  PROTO(());
+void          bDrawGraph          PROTO((KapaGraphWidget *graph));
+
+/* misc support */
+int           LastEvent           PROTO((Display *display, int type, XEvent *event));
+void	      FreeObjectData	  PROTO((Gobjects *object));
+void	      FlushDisplay	  PROTO(());
+unsigned long GetColor            PROTO((Display *display, char *name, Colormap colormap, unsigned long default_color));
+void          QuitX               PROTO((Display *display, char *message));
+Graphic      *GetGraphic          PROTO(());
+int           GetPixelCount       PROTO((int sock));
+
+int           Center              PROTO(());
+void          SetColorScale       PROTO((Graphic *graphic, KapaImageWidget *image));
+void          SetColorScale1D     PROTO((Graphic *graphic, KapaImageWidget *image));
+int           SetColorScale3D     PROTO((Graphic *graphic, KapaImageWidget *image));
+void          Remap               PROTO((Graphic *graphic, KapaImageWidget *image));
+void          Remap8              PROTO((Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix));
+void          Remap16             PROTO((Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix));
+void          Remap24             PROTO((Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix));
+void          Remap32             PROTO((Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix));
+int           LoadPicture         PROTO((int sock));
+
+KapaImageWidget *InitImageWidget  PROTO(());
+int           InitImageChannel    PROTO((KapaImageChannel *channel));
+void          FreeImage           PROTO((KapaImageWidget *image));
+void          SetImageSize        PROTO((Section *section));
+
+void          InitButtonSize      PROTO((Button *button, int width, int height, char *bitmap));
+void          InitButtonFunc      PROTO((Button *button, int (*function)()));
+void          DrawImage           PROTO((KapaImageWidget *image));
+void          DrawButton          PROTO((Graphic *graphic, Button *button));
+void          DrawBitmap          PROTO((Graphic *graphic, int x, int y, int dx, int dy, char *bitmap, int mode));
+void          CrossHairs          PROTO((Graphic *graphic, Picture *image));
+void          hh_hms              PROTO((char *line, double ra, double dec, char sep));
+
+int           SetColormap         PROTO((char *name));
+void          MakeColormap        PROTO((int argc, char **argv));
+
+void          PaintOverlay        PROTO((Graphic *graphic, KapaImageWidget *image, int N));
+void          PaintTickmarks      PROTO((Graphic *graphic, KapaImageWidget *image));
+
+void          Picture_to_Image    PROTO((double *x1, double *y1, double x2, double y2, Picture *picture));
+void          Screen_to_Image     PROTO((double *x1, double *y1, double x2, double y2, Picture *picture));
+void          Image_to_Picture    PROTO((double *x1, double *y1, double x2, double y2, Picture *picture));
+void          Image_to_Screen     PROTO((double *x1, double *y1, double x2, double y2, Picture *picture));
+void          Picture_Lower 	  PROTO((int *i_start, int *j_start, Matrix *matrix, Picture *picture));
+void          Picture_Upper 	  PROTO((int *i_end, int *j_end, Matrix *matrix, Picture *picture));
+
+void          DragColorbar        PROTO((Graphic *graphic, KapaImageWidget *image, XButtonEvent *mouse_event));
+void          ResetColorbar       PROTO((Graphic *graphic, double start, double slope));
+void          ReorientOnButton    PROTO((Graphic *graphic, KapaImageWidget *image, XButtonEvent *mouse_event));
+void          Reorient            PROTO((Graphic *graphic, KapaImageWidget *image, double X, double Y, int mode));
+
+Button       *CheckButtons        PROTO((XButtonEvent *event, KapaImageWidget *image));
+int           InButton            PROTO((XButtonEvent *event, Button *button));
+int           InPicture           PROTO((XButtonEvent *event, Picture *picture));
+
+/* Button Functions */
+int           greycolors	  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      heat		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      rainbow		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Recenter		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Rescale		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      RecenterRescale	  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      ToggleDEG		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      FlipImageX	  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      FlipImageY	  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Overlay0		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Overlay1		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Overlay2		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Overlay3		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      PSfunction	  PROTO((Graphic *graphic, KapaImageWidget *image));
+
+/* misc functions */
+void 	      SetToolbox   (int sock);
+int  	      EraseOverlay (int sock);
+int  	      LoadOverlay  (int sock);
+int  	      SaveOverlay  (int sock);
+int  	      CSaveOverlay (int sock);
+int  	      JPEGit24     (int sock);
+
+int  	      UpdatePointer (Graphic *graphic, XMotionEvent *event);
+int  	      InterpretPresses (Graphic *graphic, XButtonEvent *event);
+int  	      InterpretKeys (Graphic *graphic, XKeyEvent *event);
+void 	      InitPipe (char *namedSocket);
+void 	      EraseGraph (KapaGraphWidget *graph);
+void 	      StatusBox (Graphic *graphic, KapaImageWidget *image);
+
+void 	      CreatePicture (KapaImageWidget *image, Graphic *graphic);
+void 	      CreateColorbar (KapaImageWidget *image, Graphic *graphic);
+void 	      CreateZoom (Graphic *graphic, KapaImageWidget *image);
+void 	      CreateWide (Graphic *graphic, KapaImageWidget *image);
+void 	      UpdateStatusBox (Graphic *graphic, KapaImageWidget *image, double x, double y, double z, int mode);
+
+// void 	      CreateZoom8  (KapaImageWidget *image, Graphic *graphic, double x, double y);
+// void 	      CreateZoom16 (KapaImageWidget *image, Graphic *graphic, double x, double y);
+// void 	      CreateZoom24 (KapaImageWidget *image, Graphic *graphic, double x, double y);
+// void 	      CreateZoom32 (KapaImageWidget *image, Graphic *graphic, double x, double y);
+
+int  	      GetActiveSocket ();
+void 	      InvertButton (Graphic *graphic, Button *button);
+void 	      bDrawOverlay (KapaImageWidget *image, int N);
+
+/* color cube tools */
+CCNode *CCNodeAlloc ();
+CCNode *CCFindChild (CCNode *node, float x, float y, float z);
+int CCSplitNode (CCNode *node);
+int CCSplitNodeIterate (CCNode *node, int current, int max);
+void CCNodeFree (CCNode *node);
+CCNode *CCFindBottom (CCNode *top, float x, float y, float z);
+int CCNodeExtractCounts (CCNode *node, float **values, int *nvalues, int *NVALUES);
+int CCNodeDivideLimit (CCNode *node, float minValue);
+int CCNodeInitCounts (CCNode *node, float value);
+
+/* 3D color histogram */
+void ColorHistogram (KapaImageWidget *image, CCNode *cube);
+void CCNodeSetColorPixels (KapaImageWidget *image, CCNode *cube);
+int CCNodeSetColorMap (CCNode *node, XColor *cmap, int Npixels, int *current);
+
+int SetColorScale3D_CC (Graphic *graphic, KapaImageWidget *image);
+int SetColorCubeHistogram ();
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/structures.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/structures.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/include/structures.h	(revision 22322)
@@ -0,0 +1,213 @@
+
+/*** 3C color cube histogram tree thingy ***/
+typedef struct CCNode {
+  // for the moment, this structure is specific to the color-histogram analysis.  if
+  // this were a void *pointer and we defined some additional apis, we could use this
+  // structure to track any 3D space
+  int count;    
+  int pixel;
+  char bottom;
+  float min[3]; // min value for each of the 3 dimensions for this node
+  float mid[3]; // mid-point for each of the 3 dimensions for this node
+  float max[3]; // max value for each of the 3 dimensions for this node
+  struct CCNode *sub[2][2][2];
+} CCNode;
+
+/**************** Graphic carries X info around ****************/
+typedef struct {
+  Display       *display;     // X display pointer
+  int            screen;      // X screen number
+  int            depth;
+  Window         window;
+  Visual        *visual;
+  int            visualclass; // is visual dynamic? (XXX change name?)
+  int            Nbits;	      // pixel depth in bits (8, 16, 24, 32)
+  GC             gc;
+  XFontStruct   *font;
+  Cursor         cursor;
+  int            x,  y;
+  unsigned int   dx, dy;
+  CCNode        *cube;
+  XColor        *cmap;
+  Colormap       colormap;
+  unsigned long *color;      // graph plotting colors
+  int            Ncolors;
+
+  unsigned long *pixels;      // image pixel colors
+  int Npixels;		      // number of pixels
+
+  int            ColorScaleMode; // single colormap for all images??
+  int nRed;
+  int nBlue;
+  int nGreen;
+
+  unsigned long  fore;	      // basic foreground color 
+  unsigned long  back;	      // basic background color
+
+  unsigned long  overlay_color[NOVERLAYS]; // image plotting colors 
+} Graphic;
+
+/**************** X related "widget" structures ****************/
+typedef struct {
+  int      x, y, dx, dy;      /* position and size */
+  int      width, height;     /* size of the bitmap */
+  char    *bitmap;            /* picture on button */
+  int    (*function_1) ();    /* mouse_button 1 function */
+  int    (*function_2) ();    /* mouse_button 2 function */
+  int    (*function_3) ();    /* mouse_button 3 function */
+} Button;
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  char    *label;          /* label on TextLine */
+  char     text[1024];     /* words of TextLine */
+  char     old_text[1024]; /* words of TextLine */
+  int      outline;        /* draw an outline?  */ 
+  int      cursor;         /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextLine;
+
+typedef char STRING[1024];
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  STRING  *text;           /* words of TextLine */
+  int      Nlines;
+  int      outline;        /* draw an outline?  */ 
+  int      cursor_line;    /* cursor line */
+  int      cursor_x;       /* location of cursor (if selected) */
+  int      cursor_y;       /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextBox;
+
+/**************** general structures ****************/
+typedef struct {
+  Pixmap pixmap;
+  int    width;
+  int    height;
+  unsigned char *bits;
+} Icon;
+
+typedef struct {
+  int      x, y;	      // location of picture in graphic
+  int      dx, dy;	      // size of picture
+  int      DX, DY;	      // size of displayed picture (must be updated with new images...)
+  int      expand;	      // zoomscale
+  double   X,  Y;	      // center of image in picture
+  char     flipx, flipy;      // parity (0 = +; 1 = -)
+  XImage  *pix;
+  char    *data;
+} Picture;
+
+// objects associated with a KiiImage
+// XXX rename thi
+typedef struct {
+  char type[10];
+  double x, y;
+  double dx, dy;
+  double angle;
+  char  *text;
+} Object;
+
+typedef struct {
+  int x, y;
+  int dx, dy;
+  double angle;
+  int  size;
+  char font[64]; 
+  char text[LABEL_MAXLEN];
+} Label;
+
+typedef struct {
+  int active;
+  int Nobjects;
+  unsigned long color;
+  KiiOverlay *objects;
+} Overlay;
+  
+typedef struct {
+  float *x, *y, *z, *dxp, *dxm, *dyp, *dym;
+  int Npts;
+  int style, ptype, ltype, color, etype, ebar;
+  double lweight, size;
+  double x0, x1, y0, y1;  /* limits for this object */
+} Gobjects;
+
+typedef struct {
+  double min, max;
+  char isaxis, areticks, islabel, islog;
+  double fx, dfx, fy, dfy;  /* axis location on graphic */
+  double lweight;
+  int color;
+} Axis;
+
+// a single graph in the display window
+typedef struct {
+  Axis      axis[4];    /* coordinate axes */
+  Label     label[8];   /* fixed axis labels */
+  Graphdata data;       /* current graph data */
+
+  Gobjects *objects;    /* graphic objects */    
+  int      Nobjects;    
+
+  Label    *textline;      /* placed text labels */
+  int      Ntextline;      
+} KapaGraphWidget;
+
+typedef struct {
+  // data associated with this image element
+  Matrix   matrix;         /* data for picture */
+  double   zero, range;    /* zero, range for picture to cmap */
+  double   max, min;       /* zero, range for data to z-value */
+  double   start, slope;   /* zero, range for cmap to pixels */
+  Coords   coords;
+  char     file[1024];     /* name of file */
+  char     name[1024];     /* name of buffer */
+} KapaImageChannel;
+
+// a single image in the display window
+typedef struct {
+  // picture components of the image element
+  Picture  picture;	      // the primary view of the image
+  Picture  zoom;	      // the zoom window
+  Picture  wide;	      // the wide-view window
+  Picture  cmapbar;	      // the colormap bar
+
+  // the control buttons
+  Button   PS_button;
+  Button   recenter_button;
+  Button   hms_button; /* toggle HMS/DECIMAL mode (DECIMAL_DEG macro) */
+  Button   grey_button;
+  Button   rainbow_button;
+  Button   heat_button;
+  Button   overlay_button[NOVERLAYS];
+  Button   flipx_button;
+  Button   flipy_button;
+
+  // location of the status box
+  int      text_x, text_y;
+  int      text_dx, text_dy, text_dyo;
+  int      location;	      // position of the zoom/status widgets (0 = none, 1-4 = bottom,left,top,right)
+  int      MovePointer;
+  int      DecimalDegrees;
+
+  // double   X, Y;           /* image pixel at screen center */
+  // double   x, y, z;        /* last pointer coords */
+
+  Overlay  overlay[NOVERLAYS];
+  Overlay  tickmarks;
+
+  unsigned short   *pixmap;   // lookup table for image pixel value to pixel index
+  int      nPixels;
+ 
+  KapaImageChannel *image;
+  KapaImageChannel channel[NCHANNELS];
+} KapaImageWidget;
+
+typedef struct {
+  KapaGraphWidget *graph;
+  KapaImageWidget *image;
+  float  x,  y;
+  float dx, dy;
+  char *name;
+} Section;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ButtonFunctions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ButtonFunctions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ButtonFunctions.c	(revision 22322)
@@ -0,0 +1,144 @@
+# include "Ximage.h"
+
+int PSfunction (Graphic *graphic, KapaImageWidget *image) {
+
+  int status;
+
+  status = PSit ("kapa.ps", "default", TRUE, KAPA_PS_NEWPLOT);
+  return (status);
+}
+
+int greycolors (Graphic *graphic, KapaImageWidget *image) {
+  SetColormap ("greyscale");
+  CreateColorbar (image, graphic);
+  SetColorScale (graphic, image);
+  Remap (graphic, image);
+  CreateZoom (graphic, image); 
+  CreateWide (graphic, image); 
+  Refresh ();
+  return (TRUE);
+}
+
+int heat (Graphic *graphic, KapaImageWidget *image) {
+  SetColormap ("heat");
+  CreateColorbar (image, graphic);
+  SetColorScale (graphic, image);
+  Remap (graphic, image);
+  CreateZoom (graphic, image); 
+  CreateWide (graphic, image); 
+  Refresh ();
+  return (TRUE);
+}
+
+int rainbow (Graphic *graphic, KapaImageWidget *image) {
+  SetColormap ("rainbow");
+  CreateColorbar (image, graphic);
+  SetColorScale (graphic, image);
+  Remap (graphic, image);
+  CreateZoom (graphic, image); 
+  CreateWide (graphic, image); 
+  Refresh ();
+  return (TRUE);
+}
+
+int Recenter (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].picture.X = 0.5*image[0].image[0].matrix.Naxis[0];
+  image[0].picture.Y = 0.5*image[0].image[0].matrix.Naxis[1];
+ 
+  Remap (graphic, image);
+  Refresh ();
+  return (TRUE);
+
+}
+
+int Rescale (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].picture.expand = 1;
+  Remap (graphic, image);
+  Refresh ();
+  return (TRUE);
+
+}
+
+int RecenterRescale (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].picture.X = 0.5*image[0].image[0].matrix.Naxis[0];
+  image[0].picture.Y = 0.5*image[0].image[0].matrix.Naxis[1];
+  image[0].picture.expand = 1;
+ 
+  Remap (graphic, image);
+  Refresh ();
+  return (TRUE);
+
+}
+
+int FlipImageX (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].picture.flipx = !image[0].picture.flipx;
+  image[0].zoom.flipx 	 = !image[0].zoom.flipx;
+  image[0].wide.flipx 	 = !image[0].wide.flipx;
+ 
+  Remap (graphic, image);
+  CreateWide (graphic, image);
+  Refresh ();
+  return (TRUE);
+}
+
+int FlipImageY (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].picture.flipy = !image[0].picture.flipy;
+  image[0].zoom.flipy 	 = !image[0].zoom.flipy;
+  image[0].wide.flipy 	 = !image[0].wide.flipy;
+ 
+  Remap (graphic, image);
+  CreateWide (graphic, image);
+  Refresh ();
+  return (TRUE);
+}
+
+int ToggleDEG (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].DecimalDegrees = image[0].DecimalDegrees ^ TRUE;
+  StatusBox (graphic, image);
+  FlushDisplay (graphic[0].display);
+  return (TRUE);
+
+}
+
+/*********** overlay_button functions ************/
+int Overlay0 (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].overlay[0].active = image[0].overlay[0].active ^ TRUE;
+  Refresh ();
+  return (TRUE);
+
+}
+
+int Overlay1 (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].overlay[1].active = image[0].overlay[1].active ^ TRUE;
+  Refresh ();
+  return (TRUE);
+
+}
+
+int Overlay2 (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].overlay[2].active = image[0].overlay[2].active ^ TRUE;
+  Refresh ();
+  return (TRUE);
+
+}
+
+int Overlay3 (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].overlay[3].active = image[0].overlay[3].active ^ TRUE;
+  Refresh ();
+  return (TRUE);
+
+}
+
+/* this routine is NOT independent of the number of overlays */
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CSaveOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CSaveOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CSaveOverlay.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include "Ximage.h"
+
+int CSaveOverlay (int sock) {
+
+  char filename[256], *type;
+  int i, N;
+  double ra, dec, ra1, dec1, x1, y1, dra, ddec;
+  FILE *f;
+  Section *section;
+  KapaImageWidget *image;
+
+  KiiScanMessage (sock, "%*s %d %s", &N, filename);
+  
+  section = GetActiveSection();
+  image   = section->image;
+  if (image == NULL) return (TRUE);
+
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "could not open %s\n", filename);
+    return (TRUE);
+  }
+
+  for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+    if (image[0].overlay[N].objects[i].type == KII_OVERLAY_LINE) {
+      XY_to_RD (&ra, &dec, image[0].overlay[N].objects[i].x, image[0].overlay[N].objects[i].y, &image[0].image[0].coords);
+      x1 = image[0].overlay[N].objects[i].x + image[0].overlay[N].objects[i].dx;
+      y1 = image[0].overlay[N].objects[i].y + image[0].overlay[N].objects[i].dy;
+      XY_to_RD (&ra1, &dec1, x1, y1, &image[0].image[0].coords);
+      dra = (ra1 - ra);
+      ddec = (dec1 - dec);
+    } else {
+      XY_to_RD (&ra, &dec, image[0].overlay[N].objects[i].x, image[0].overlay[N].objects[i].y, &image[0].image[0].coords);
+      x1 = image[0].overlay[N].objects[i].x;
+      y1 = image[0].overlay[N].objects[i].y + image[0].overlay[N].objects[i].dy;
+      XY_to_RD (&ra1, &dec1, x1, y1, &image[0].image[0].coords);
+      ddec = fabs (dec1 - dec);
+      x1 = image[0].overlay[N].objects[i].x + image[0].overlay[N].objects[i].dx;
+      y1 = image[0].overlay[N].objects[i].y;
+      XY_to_RD (&ra1, &dec1, x1, y1, &image[0].image[0].coords);
+      dra = cos (dec*RAD_DEG) * fabs (ra1 - ra);
+    }
+    type = KiiOverlayTypeByNumber (image[0].overlay[N].objects[i].type);
+    if (type == NULL) continue;
+    fprintf (f, "%s %lf %lf %lf %lf\n", type, ra, dec, dra, ddec);
+   }
+  fclose (f);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Center.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Center.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Center.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include "Ximage.h"
+
+int Center (int sock) {
+
+  int zoom;
+  double X, Y;
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  KiiScanMessage (sock, "%lf %lf %d", &X,  &Y, &zoom);
+
+  graphic = GetGraphic();
+  section = GetActiveSection();
+  image = section->image;
+  if (image == NULL) return (TRUE);
+
+  image[0].picture.X = X;
+  image[0].picture.Y = Y;
+  if ((zoom != 0) && (zoom != -1)) {
+    image[0].picture.expand = zoom;
+  }
+
+  if (USE_XWINDOW) {
+    Remap (graphic, image);
+    Refresh ();
+    XFlush (graphic[0].display);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckButtons.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckButtons.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckButtons.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "Ximage.h"
+
+/******** Here we test the Buttons specific to this program  *******/
+Button *CheckButtons (XButtonEvent  *event, KapaImageWidget *image) {
+
+  int i;
+  Button *button;
+  button = (Button *) NULL;
+
+  if (InButton (event, &image[0].recenter_button)) 
+    button = &image[0].recenter_button;
+
+  if (InButton (event, &image[0].grey_button)) 
+    button = &image[0].grey_button;
+
+  if (InButton (event, &image[0].rainbow_button)) 
+    button = &image[0].rainbow_button;
+
+  if (InButton (event, &image[0].heat_button)) 
+    button = &image[0].heat_button;
+
+  if (InButton (event, &image[0].PS_button)) 
+    button = &image[0].PS_button;
+
+  if (InButton (event, &image[0].hms_button)) 
+    button = &image[0].hms_button;
+
+  if (InButton (event, &image[0].flipx_button)) 
+    button = &image[0].flipx_button;
+
+  if (InButton (event, &image[0].flipy_button)) 
+    button = &image[0].flipy_button;
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (InButton (event, &image[0].overlay_button[i])) 
+      button = &image[0].overlay_button[i];
+  }
+
+  return (button);
+
+}
+
+/* To define a button, you must:
+
+   0) add the button to the Image structure in structures.h
+   1) place the info about the button in PositionPicture.c
+   2) (make any bitmaps needed and put them in buttons.h
+   3) place an entry in CheckButtons.c
+   4) Add the button to Refresh.c
+   5) create the button's function
+   6) add it to the Makefile
+   7) add it to the prototypes
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckColors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckColors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckColors.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "Ximage.h"
+
+/************** CheckColors *************/
+void CheckColors (Graphic *graphic, int *argc, char **argv) {
+
+  char *temp_name;
+  int N;
+
+  graphic->fore = BlackPixel (graphic->display, graphic->screen);
+  temp_name = XGetDefault (graphic->display, argv[0], "Foreground");
+  if ((N = get_argument (*argc, argv, "-fg"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is -fg color\n");
+      exit (0);
+    }
+    remove_argument (N, argc, argv);
+    temp_name = argv[N];
+    remove_argument (N, argc, argv);
+  } 
+  if (temp_name != NULL) {
+    graphic->fore = GetColor (graphic->display, temp_name, graphic->colormap, graphic->fore);
+  }
+
+  graphic->back = WhitePixel (graphic->display, graphic->screen);
+  temp_name = XGetDefault (graphic->display, argv[0], "Background");
+  if ((N = get_argument (*argc, argv, "-bg"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is -bg color\n");
+      exit (0);
+    }
+    remove_argument (N, argc, argv);
+    temp_name = argv[N];
+    remove_argument (N, argc, argv);
+  } 
+  if (temp_name != NULL) {
+    graphic->back = GetColor (graphic->display, temp_name, graphic->colormap, graphic->back);
+  }
+  return;
+}
+
+  /* here we define the values for foreground and background
+     if -fg, or -bg exist, or if Foreground or Background are set in .Xdefaults, 
+     use those.  foreground defaults to black, background defaults to white. */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckDisplayName.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckDisplayName.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckDisplayName.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include "Ximage.h"
+
+/************** CheckDisplayName *************/
+char *CheckDisplayName (int *argc, char **argv) {
+
+  char *name = NULL;
+  int N;
+
+  if ((N = get_argument (*argc, argv, "-d"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is [-display/-d] DisplayName\n");
+      exit (0);
+    }
+    remove_argument(N, argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument(N, argc, argv);
+    return (name);
+  }
+
+  if ((N = get_argument (*argc, argv, "-display"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is [-display/-d] DisplayName\n");
+      exit (0);
+    }
+    remove_argument(N, argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument(N, argc, argv);
+    return (name);
+  }
+  return (NULL);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckGeometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckGeometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckGeometry.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "Ximage.h"
+
+/************** CheckGeometry *************/
+void CheckGeometry (Graphic *graphic, int *argc, char **argv) {
+
+  int status, N;
+  char *temp_name;
+  
+  temp_name = XGetDefault (graphic->display, argv[0], "geometry");
+  if ((N = get_argument (*argc, argv, "-geom"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is -geom DisplayName\n");
+      exit (0);
+    }
+    remove_argument (N, argc, argv);
+    temp_name = argv[N];
+    remove_argument (N, argc, argv);
+  }
+  if (temp_name == NULL) return;
+
+  status = XParseGeometry (temp_name, &graphic->x, &graphic->y, &graphic->dx, &graphic->dy);
+  if (status & XNegative) graphic->x += DisplayWidth  (graphic->display, graphic->screen) - graphic->dx;
+  if (status & YNegative) graphic->y += DisplayHeight (graphic->display, graphic->screen) - graphic->dy;
+
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckPipe.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckPipe.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckPipe.c	(revision 22322)
@@ -0,0 +1,315 @@
+# include "Ximage.h"
+# define STRCONST(A) ((int)(0x1000000*A[0] + 0x10000*A[1] + 0x100*A[2] + 0x1*A[3]))
+
+static KapaSockAddress Address;
+static int InitSocket = -1;
+static int sock = -1;
+
+// we can supply a port here, with only small changes
+void InitPipe (char *namedSocket) {
+
+  if (namedSocket == NULL) {
+    InitSocket = KapaServerInit (&Address);
+  } else {
+    sock = KapaWaitNamedSocket (namedSocket);
+  }
+  return;
+}
+
+int GetActiveSocket () {
+  return (sock);
+}
+
+// after we have processed the command, we unblock the socket
+# define FINISHED(A) { fcntl (sock, F_SETFL, O_NONBLOCK); return (A); }
+
+int CheckPipe () {
+
+  int status;
+  char word[5];
+
+  // check if we have a valid connection. if not, see if we can get one
+  if (sock == -1) {
+    sock = KapaServerWait (InitSocket, &Address);
+    if (sock == -1) return (TRUE);
+    close (InitSocket); /* stop listening for new connections */
+    fcntl (sock, F_SETFL, O_NONBLOCK);  
+  }
+
+  /***** read (4 byte) message word from socket ****/
+  status = read (sock, word, 4);
+  word[4] = 0;
+  switch (status) {
+  case -1:                          /* no input from pipe: continue */
+    return (TRUE);
+    break;
+
+  case 0:
+    fprintf (stderr, "pipe has died!\n");
+    return (FALSE);
+    break;
+
+  case 4:
+    break;
+
+  default:
+    fprintf (stderr, "weird signal: too many or few bytes!  %d\n", status);
+    return (TRUE);
+    break;
+  }
+  
+  /* once we get a command, we block to ensure we get complete messages */
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+
+  /***** handle different messages ****/
+  if (ACTIVE_CURSOR) {
+    if (strcmp (word, "NCUR")) {
+      fprintf (stderr, "wrong end message %s\n", word);
+      KiiSendCommand (sock, 4, "DONE");
+      FINISHED (TRUE);
+    }
+    ACTIVE_CURSOR = FALSE;
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  } 
+
+  if (!strcmp (word, "QUIT")) FINISHED (FALSE);
+  
+  if (!strcmp (word, "CURS")) {
+    ACTIVE_CURSOR = TRUE;
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "PSIT")) {
+    status = PScommand (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  if (!strcmp (word, "PNGF")) {
+    status = PNGit (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  if (!strcmp (word, "PPMF")) {
+    status = PPMit (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  if (!strcmp (word, "DBOX")) {
+    status = LoadFrame (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  if (!strcmp (word, "PLOT")) {
+    status = LoadObject (sock);
+    // LoadObject sends its own handshake
+    // KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  if (!strcmp (word, "LABL")) {
+    status = LoadLabels (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "PTXT")) {
+    status = LoadTextlines (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "RSIZ")) {
+    status = Resize (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "GLIM")) {
+    GetLimits (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "SLIM")) {
+    status = SetLimits (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "GSTY")) {
+    GetGraphData (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "SSTY")) {
+    status = SetGraphData (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "SIMC")) {
+    SetImageCoords (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "GIMC")) {
+    GetImageCoords (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "GIMR")) {
+    GetImageRange (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "GIMD")) {
+    GetImageData (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "SIMD")) {
+    status = SetImageData (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "TOOL")) {
+    SetToolbox (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "SSEC")) {
+    status = SetSection (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "LSEC")) {
+    status = ListSection (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "DSEC")) {
+    status = DefineSection (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "MSEC")) {
+    status = MoveSection (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "FONT")) {
+    status = SetFont (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  /* Erase Section */
+  if (!strcmp (word, "ERSC")) {
+    status = EraseCurrentPlot ();
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  /* Erase Plots */
+  if (!strcmp (word, "ERSP")) {
+    status = ErasePlots ();
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  /* Erase Sections */
+  if (!strcmp (word, "ERSS")) {
+    status = EraseSections ();
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  /* Erase Image */
+  if (!strcmp (word, "ERSI")) {
+    status = EraseImage ();
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  /* Erase Overlay for this section */
+  if (!strcmp (word, "ERSO")) {
+    status = EraseOverlay (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "READ")) {
+    status = LoadPicture (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "LOAD")) {
+    status = LoadOverlay (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "CHAN")) {
+    status = SetChannel (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "CMAP")) {
+    status = SetColormapFromPipe (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "SAVE")) {
+    status = SaveOverlay (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "CSVE")) {
+    status = CSaveOverlay (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "JPEG")) {
+    status = JPEGit24 (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "CENT")) {
+    status = Center (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "NPIX")) {
+    GetPixelCount (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+
+  fprintf (stderr, "unknown signal %s\n", word);
+  KiiSendCommand (sock, 4, "DONE");
+  FINISHED (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckVisual.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckVisual.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CheckVisual.c	(revision 22322)
@@ -0,0 +1,196 @@
+# include "Ximage.h"
+
+/* DirectColor doesn't seem to work, even though it is available:
+   I cannot use XAllocColorCells to get pixels under DirectColor
+*/
+
+/* static int try_visual[] = {5, 3, 1, 4, 2, 0}; */
+
+void CheckVisual (Graphic *graphic, int *argc, char **argv) {
+
+  int i, Nfound, N;
+  int isColor, isDefault, isDynamic;
+  XVisualInfo *visual_list, visual_temp;
+  unsigned long planes[3];
+  XPixmapFormatValues *pixmaps;
+  int Npixmaps, Npixels;
+
+  if (DEBUG) {
+    fprintf (stderr, "DirectColor: %d\n", DirectColor);
+    fprintf (stderr, "PseudoColor: %d\n", PseudoColor);
+    fprintf (stderr, "TrueColor:   %d\n", TrueColor);
+    fprintf (stderr, "GrayScale:   %d\n", GrayScale);
+    fprintf (stderr, "StaticColor: %d\n", StaticColor);
+    fprintf (stderr, "StaticGray:  %d\n", StaticGray);
+  }
+
+  visual_temp.screen = graphic[0].screen;
+  
+  /* find available visuals */
+  visual_list = XGetVisualInfo (graphic[0].display, VisualScreenMask, &visual_temp, &Nfound);
+  if (Nfound == 0) {
+    fprintf (stderr, "error finding useful visual\n");
+    exit (0);
+  }
+  
+  /* check default visual first */ 
+  for (i = 0; (i < Nfound) && (graphic[0].visual != visual_list[i].visual); i++);
+  if (i == Nfound) {
+    fprintf (stderr, "default visual not found??\n");
+    exit (0);
+  }
+
+  // set these based on selected visual
+  isColor = isDefault = isDynamic = FALSE;
+
+  // attempt to select the most desirable type of visual: Default as PseudoColor (XXX is it still true?)
+  if (DEBUG) fprintf (stderr, "default visual class is %d\n", visual_list[i].class);
+  if (visual_list[i].class == PseudoColor) {
+    isColor = isDefault = isDynamic = TRUE;
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = TRUE;
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: PseudoColor (XXX is it still true?)
+  for (i = 0; (i < Nfound) && (visual_list[i].class != PseudoColor); i++);
+  if (i != Nfound) {
+    isColor = isDynamic = TRUE;
+    if (DEBUG) fprintf (stderr, "selected visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = TRUE;
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: GrayScale (XXX is it still true?)
+  for (i = 0; (i < Nfound) && (visual_list[i].class != GrayScale); i++);
+  if (i != Nfound) {
+    isDynamic = TRUE;
+    if (DEBUG) fprintf (stderr, "selected visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = TRUE;
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: TrueColor (XXX is it still true?)
+  for (i = 0; (i < Nfound) && (visual_list[i].class != TrueColor); i++);
+  if (i != Nfound) {
+    isColor = TRUE;
+    if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = FALSE;
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: TrueColor (XXX is it still true?)
+  for (i = 0; (i < Nfound) && (visual_list[i].class != StaticColor); i++);
+  if (i != Nfound) {
+    if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = FALSE;
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: TrueColor (XXX is it still true?)
+  for (i = 0; (i < Nfound) && (visual_list[i].class != StaticGray); i++);
+  if (i != Nfound) {
+    if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = FALSE;
+    goto test_pixels;
+  }
+
+  test_pixels:
+
+  /* need to make a colormap if 
+     1) the selected visual is not the default 
+     2) there are not enough colors available */
+
+  /* NEED TO ADD A COUPLE LINES DEFINING THE VARIOUS HARD COLORS (BLACK, WHITE, AND THE OVERLAYS) */
+
+  // allow user to force a private colormap
+  if ((N = get_argument (*argc, argv, "-private"))) {
+    remove_argument(N, argc, argv);
+    isDefault = FALSE;
+  } 
+
+  // allocate a private colormap, if needed
+  if (!isDefault) {
+    if (DEBUG) fprintf (stderr, "allocated private colormap\n");
+    graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, AllocNone);
+  }
+
+  if (isDynamic) {
+    Npixels = NPIXELS_DYNAMIC;
+    if ((N = get_argument (*argc, argv, "-ncolors"))) {
+      remove_argument(N, argc, argv);
+      Npixels = atoi (argv[N]);
+      remove_argument(N, argc, argv);
+    } 
+
+    /* allocate color cells */
+    ALLOCATE (graphic[0].pixels, unsigned long, Npixels);
+    ALLOCATE (graphic[0].cmap,   XColor,        Npixels);
+    for (graphic[0].Npixels = Npixels; graphic[0].Npixels >= 16; graphic[0].Npixels -= 4) {
+      if (DEBUG) fprintf (stderr, "trying %d colors\n", (int) graphic[0].Npixels);
+      if (XAllocColorCells (graphic[0].display, graphic[0].colormap, FALSE, planes, 1, graphic[0].pixels, graphic[0].Npixels)) {
+	break;
+      }
+    }
+
+    /* insufficient cells, can we make a private colormap? */
+    if (graphic[0].Npixels < 16) {
+      if (!isDefault) {
+	fprintf (stderr, "can't allocate enough cells in private colormap\n");
+	exit (0);
+      }
+      if (DEBUG) fprintf (stderr, "can't allocate enough cells, using private colormap\n");
+      graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, AllocNone);
+
+      for (graphic[0].Npixels = NPIXELS_DYNAMIC; graphic[0].Npixels >= 16; graphic[0].Npixels -= 4) {
+	if (DEBUG) fprintf (stderr, "trying %d colors\n", (int) graphic[0].Npixels);
+	if (XAllocColorCells (graphic[0].display, graphic[0].colormap, FALSE, planes, 1, graphic[0].pixels, graphic[0].Npixels)) {
+	  break;
+	}
+      }
+
+      if ((N = get_argument (*argc, argv, "-colorcount"))) {
+	fprintf (stderr, "kapa can grab %d colors\n", graphic[0].Npixels);
+	exit (0);
+      } 
+      if (graphic[0].Npixels < 16) {
+	fprintf (stderr, "can't even allocate enough cells in private colormap\n");
+	exit (0);
+      }
+    }
+  } else {
+    Npixels = NPIXELS_STATIC;
+    if ((N = get_argument (*argc, argv, "-ncolors"))) {
+      remove_argument(N, argc, argv);
+      Npixels = atoi (argv[N]);
+      remove_argument(N, argc, argv);
+    } 
+
+    // XXX allocate the unsigned long *pixels here and above
+    ALLOCATE (graphic[0].pixels, unsigned long, Npixels);
+    ALLOCATE (graphic[0].cmap,   XColor,        Npixels);
+    graphic[0].Npixels = Npixels;
+  }
+
+  if ((N = get_argument (*argc, argv, "-colorcount"))) {
+    fprintf (stderr, "kii can grab %d colors\n", graphic[0].Npixels);
+    exit (0);
+  } 
+  
+  pixmaps = XListPixmapFormats (graphic[0].display, &Npixmaps);
+  for (i = 0; i < Npixmaps; i++) {
+    if (pixmaps[i].depth == graphic[0].depth) {
+      graphic[0].Nbits = pixmaps[i].bits_per_pixel;
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CloseDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CloseDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CloseDisplay.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "Ximage.h"
+
+/************** CloseDisplay *************/
+void CloseDisplay () {
+
+  Graphic *graphic;
+
+  if (!USE_XWINDOW) return;
+
+  graphic = GetGraphic();
+  XFreeFont (graphic->display, graphic->font); 
+  XFreeGC (graphic->display, graphic->gc);
+  XDestroySubwindows (graphic->display, graphic->window);
+  XDestroyWindow (graphic->display, graphic->window);
+  XFlush (graphic->display);
+  XCloseDisplay (graphic->display);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ColorCube.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ColorCube.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ColorCube.c	(revision 22322)
@@ -0,0 +1,225 @@
+# include "Ximage.h"
+
+/* these functions are used to generate an appropriately-sampled color map for a given image.
+   we assume that only a limited number of colors can be allocated (start with, eg, 1024, grow to
+   64k or so).  the RGB image is constructed from the 32bit images, each with their own zero and
+   scale.  We want to allocate set of color cells which sample the actual colors available in the
+   image data.  We generate a 3D histogram of the actual data values.  The histogram is generated
+   using a k-d tree. */
+
+// the k-d tree is built of a bunch of nodes, each of which represents a cubic region in the 3D
+// color space.  each node may be occupied, or may point to a set of 8 children, subdividing the
+// cube into equal cubes.
+
+// NOTE : nothing in this file specifies a relationship between x,y,z and red,blue,green
+
+// set of operations needed:
+
+// allocate empty, bottom-level node
+CCNode *CCNodeAlloc () {
+
+  CCNode *node;
+  int ix, iy, iz;
+
+  ALLOCATE (node, CCNode, 1);
+  node->count = 0;
+  node->pixel = 0;
+  node->bottom = TRUE;
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	node->sub[ix][iy][iz] = NULL;
+      }
+    }
+  }
+  return node;
+}
+
+// given a coordinate and a containing node, find the corresponding child node (if any)
+CCNode *CCFindChild (CCNode *node, float x, float y, float z) {
+
+  int ix, iy, iz;
+
+  assert (x >= node->min[CC_X]);
+  assert (y >= node->min[CC_Y]);
+  assert (z >= node->min[CC_Z]);
+
+  assert (x <= node->max[CC_X]);
+  assert (y <= node->max[CC_Y]);
+  assert (z <= node->max[CC_Z]);
+
+  if (node->bottom) return NULL; // already at the bottom
+
+  ix = (x < node->mid[CC_X]) ? 0 : 1;
+  iy = (y < node->mid[CC_Y]) ? 0 : 1;
+  iz = (z < node->mid[CC_Z]) ? 0 : 1;
+  
+  return node->sub[ix][iy][iz];
+}
+
+// given a coordinate and a containing node, find the corresponding child node (if any)
+int CCSplitNode (CCNode *node) {
+
+  CCNode *child;
+  int ix, iy, iz;
+
+  // trying to split an already split node
+  if (!node->bottom) return FALSE;
+
+  node->bottom = FALSE;
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	child = CCNodeAlloc();
+	child->min[CC_X] = (ix == 0) ? node->min[CC_X] : node->mid[CC_X];
+	child->min[CC_Y] = (iy == 0) ? node->min[CC_Y] : node->mid[CC_Y];
+	child->min[CC_Z] = (iz == 0) ? node->min[CC_Z] : node->mid[CC_Z];
+	child->max[CC_X] = (ix == 0) ? node->mid[CC_X] : node->max[CC_X];
+	child->max[CC_Y] = (iy == 0) ? node->mid[CC_Y] : node->max[CC_Y];
+	child->max[CC_Z] = (iz == 0) ? node->mid[CC_Z] : node->max[CC_Z];
+
+	child->mid[CC_X] = 0.5*(child->min[CC_X] + child->max[CC_X]);
+	child->mid[CC_Y] = 0.5*(child->min[CC_Y] + child->max[CC_Y]);
+	child->mid[CC_Z] = 0.5*(child->min[CC_Z] + child->max[CC_Z]);
+
+	node->sub[ix][iy][iz] = child;
+      }
+    }
+  }
+  return TRUE;
+}
+
+int CCSplitNodeIterate (CCNode *node, int current, int max) {
+
+  int ix, iy, iz;
+
+  if (current == max) return TRUE;
+  current ++;
+
+  CCSplitNode (node);
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCSplitNodeIterate (node->sub[ix][iy][iz], current, max);
+      }
+    }
+  }
+  return TRUE;
+}
+
+// allocate empty, bottom-level node
+void CCNodeFree (CCNode *node) {
+
+  int ix, iy, iz;
+
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	if (node->sub[ix][iy][iz]) CCNodeFree (node->sub[ix][iy][iz]);
+      }
+    }
+  }
+  free (node);
+  return;
+}
+
+// given a coordinate and a containing node, find the corresponding child node (if any)
+CCNode *CCFindBottom (CCNode *top, float x, float y, float z) {
+
+  CCNode *node;
+  CCNode *next;
+
+  assert (x >= top->min[CC_X]);
+  assert (y >= top->min[CC_Y]);
+  assert (z >= top->min[CC_Z]);
+
+  assert (x <= top->max[CC_X]);
+  assert (y <= top->max[CC_Y]);
+  assert (z <= top->max[CC_Z]);
+
+  node = top;
+  next = CCFindChild (node, x, y, z);
+  while (next != NULL) {
+    node = next;
+    next = CCFindChild (node, x, y, z);
+  }
+
+  return node;
+}
+
+// iterate over the CCNode tree to generate a single, 1d vector of all data values
+// XXX this function is specific to the color histogram concept
+int CCNodeExtractCounts (CCNode *node, float **values, int *nvalues, int *NVALUES) {
+
+  int ix, iy, iz;
+
+  if (*values == NULL) {
+    nvalues[0] = 0;
+    NVALUES[0] = 32;
+    ALLOCATE (*values, float, NVALUES[0]);
+  }
+
+  if (node->bottom) {
+    values[0][nvalues[0]] = node->count;
+    nvalues[0] ++;
+    if (nvalues[0] >= NVALUES[0]) {
+      NVALUES[0] += 32;
+      REALLOCATE (*values, float, NVALUES[0]);
+    }
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeExtractCounts (node->sub[ix][iy][iz], values, nvalues, NVALUES);
+      }
+    }
+  }
+  return TRUE;
+}
+
+// iterate over the CCNode tree and subdivide any bottom level nodes with value > minValue
+// XXX this function is specific to the color histogram concept
+int CCNodeDivideLimit (CCNode *node, float minValue) {
+
+  int ix, iy, iz;
+
+  if (node->bottom) {
+    if (node->count > minValue) {
+      CCSplitNode (node);
+    }
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeDivideLimit (node->sub[ix][iy][iz], minValue);
+      }
+    }
+  }
+  return TRUE;
+}
+
+// iterate over the CCNode tree and subdivide any bottom level nodes with value > minValue
+// XXX this function is specific to the color histogram concept
+int CCNodeInitCounts (CCNode *node, float value) {
+
+  int ix, iy, iz;
+
+  if (node->bottom) {
+    node->count = value;
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeInitCounts (node->sub[ix][iy][iz], value);
+      }
+    }
+  }
+  return TRUE;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ColorHistogram.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ColorHistogram.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ColorHistogram.c	(revision 22322)
@@ -0,0 +1,188 @@
+# include "Ximage.h"
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+void ColorHistogram (KapaImageWidget *image, CCNode *cube) {
+
+  int i, DX, DY, nPixels;
+  float *rData, *bData, *gData, rValue, gValue, bValue;
+  float redSlope, blueSlope, greenSlope;
+  float redStart, blueStart, greenStart;
+  CCNode *node;
+
+  // Input images are scaled to 0.0 - 1.0 floating point range.  These values are accumulated in the
+  // 3d histogram.  Use the histogram to allocate colors or subdivide the top-populated histogram
+  // cells and re-evaluated.   
+
+  // set start & slope for red (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_RED].range != 0.0) {
+    redSlope = 1.0 / image[0].channel[KAPA_CHANNEL_RED].range;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero / image[0].channel[KAPA_CHANNEL_RED].range;
+  } else {
+    redSlope = 1.0;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero;
+  }
+  // set start & slope for blue (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_BLUE].range != 0.0) {
+    blueSlope = 1.0 / image[0].channel[KAPA_CHANNEL_BLUE].range;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero / image[0].channel[KAPA_CHANNEL_BLUE].range;
+  } else {
+    blueSlope = 1.0;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero;
+  }
+  // set start & slope for green (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_GREEN].range != 0.0) {
+    greenSlope = 1.0 / image[0].channel[KAPA_CHANNEL_GREEN].range;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero / image[0].channel[KAPA_CHANNEL_GREEN].range;
+  } else {
+    greenSlope = 1.0;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero;
+  }
+
+  DX = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1];
+  // XXX check on equal size for all three channels
+
+  nPixels = DX*DY;
+
+  // loop over pixels, convert data values to range values (0.0 - 1.0), increment 3D histogram cell
+  rData = (float *) image[0].channel[KAPA_CHANNEL_RED].matrix.buffer;
+  bData = (float *) image[0].channel[KAPA_CHANNEL_BLUE].matrix.buffer;
+  gData = (float *) image[0].channel[KAPA_CHANNEL_GREEN].matrix.buffer;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < nPixels; i++, rData++, bData++, gData++) {
+    rValue = *rData * redSlope   - redStart;
+    if (rValue < 0.0) rValue = 0.0;
+    if (rValue > 1.0) rValue = 1.0;
+    if (isnan(rValue)) rValue = 0.0;
+    if (isinf(rValue)) rValue = 1.0;
+
+    bValue = *bData * blueSlope  - blueStart;
+    if (bValue < 0.0) bValue = 0.0;
+    if (bValue > 1.0) bValue = 1.0;
+    if (isnan(bValue)) bValue = 0.0;
+    if (isinf(bValue)) bValue = 1.0;
+
+    gValue = *gData * greenSlope - greenStart;
+    if (gValue < 0.0) gValue = 0.0;
+    if (gValue > 1.0) gValue = 1.0;
+    if (isnan(gValue)) gValue = 0.0;
+    if (isinf(gValue)) gValue = 1.0;
+
+    // XXX at the moment, we are saturating before supplying to this function
+    // NOTE : x,y,z = red,green,blue
+    node = CCFindBottom (cube, rValue, gValue, bValue);
+    node->count ++;
+  }
+}
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+void CCNodeSetColorPixels (KapaImageWidget *image, CCNode *cube) {
+
+  int i, DX, DY, nPixels;
+  float *rData, *bData, *gData, rValue, gValue, bValue;
+  unsigned short *oData;
+  float redSlope, blueSlope, greenSlope;
+  float redStart, blueStart, greenStart;
+  CCNode *node;
+
+  // Input images are scaled to 0.0 - 1.0 floating point range.  These values are accumulated in the
+  // 3d histogram.  Use the histogram to allocate colors or subdivide the top-populated histogram
+  // cells and re-evaluated.   
+
+  // set start & slope for red (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_RED].range != 0.0) {
+    redSlope = 1.0 / image[0].channel[KAPA_CHANNEL_RED].range;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero / image[0].channel[KAPA_CHANNEL_RED].range;
+  } else {
+    redSlope = 1.0;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero;
+  }
+  // set start & slope for blue (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_BLUE].range != 0.0) {
+    blueSlope = 1.0 / image[0].channel[KAPA_CHANNEL_BLUE].range;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero / image[0].channel[KAPA_CHANNEL_BLUE].range;
+  } else {
+    blueSlope = 1.0;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero;
+  }
+  // set start & slope for green (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_GREEN].range != 0.0) {
+    greenSlope = 1.0 / image[0].channel[KAPA_CHANNEL_GREEN].range;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero / image[0].channel[KAPA_CHANNEL_GREEN].range;
+  } else {
+    greenSlope = 1.0;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero;
+  }
+
+  DX = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1];
+  // XXX check on equal size for all three channels
+
+  nPixels = DX*DY;
+  if (image[0].nPixels != nPixels) {
+    REALLOCATE (image[0].pixmap, unsigned short, nPixels);
+    image[0].nPixels = nPixels;
+  }
+
+  // loop over pixels, convert data values to range values (0.0 - 1.0), increment 3D histogram cell
+  rData = (float *) image[0].channel[KAPA_CHANNEL_RED].matrix.buffer;
+  bData = (float *) image[0].channel[KAPA_CHANNEL_BLUE].matrix.buffer;
+  gData = (float *) image[0].channel[KAPA_CHANNEL_GREEN].matrix.buffer;
+
+  oData = image[0].pixmap;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < nPixels; i++, rData++, bData++, gData++, oData++) {
+    rValue = *rData * redSlope   - redStart;
+    if (rValue < 0.0) rValue = 0.0;
+    if (rValue > 1.0) rValue = 1.0;
+    if (isnan(rValue)) rValue = 0.0;
+    if (isinf(rValue)) rValue = 1.0;
+
+    bValue = *bData * blueSlope  - blueStart;
+    if (bValue < 0.0) bValue = 0.0;
+    if (bValue > 1.0) bValue = 1.0;
+    if (isnan(bValue)) bValue = 0.0;
+    if (isinf(bValue)) bValue = 1.0;
+
+    gValue = *gData * greenSlope - greenStart;
+    if (gValue < 0.0) gValue = 0.0;
+    if (gValue > 1.0) gValue = 1.0;
+    if (isnan(gValue)) gValue = 0.0;
+    if (isinf(gValue)) gValue = 1.0;
+
+    // XXX at the moment, we are saturating before supplying to this function
+    // NOTE : x,y,z = red,green,blue 
+    node = CCFindBottom (cube, rValue, gValue, bValue);
+    *oData = node->pixel;
+  }
+}
+
+// iterate over the CCNode tree and subdivide any bottom level nodes with value > minValue
+// XXX this function is specific to the color histogram concept
+// NOTE : x,y,z = red,green,blue 
+int CCNodeSetColorMap (CCNode *node, XColor *cmap, int Npixels, int *current) {
+
+  int ix, iy, iz;
+
+  if (node->bottom) {
+    cmap[current[0]].red   = node->mid[CC_X]*0xffff;
+    cmap[current[0]].green = node->mid[CC_Y]*0xffff;
+    cmap[current[0]].blue  = node->mid[CC_Z]*0xffff;
+    cmap[current[0]].flags = DoRed | DoGreen | DoBlue;
+    node->pixel = current[0];
+    current[0] ++;
+    assert (current[0] <= Npixels);
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeSetColorMap (node->sub[ix][iy][iz], cmap, Npixels, current);
+      }
+    }
+  }
+  return TRUE;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateColorbar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateColorbar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateColorbar.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "Ximage.h"
+
+void CreateColorbar (KapaImageWidget *image, Graphic *graphic) {
+
+  int i, j, dx, dy, extra, start;
+  unsigned long  pixvalue;
+  unsigned int  *out24;
+  unsigned char *out8;
+
+  dx = image[0].cmapbar.dx;
+  dy = image[0].cmapbar.dy;
+
+  /* create the cmap scale */
+  switch (graphic[0].Nbits) {
+  case 8:
+    REALLOCATE (image[0].cmapbar.data, char, dx*dy);
+    out8 = (unsigned char *) image[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = graphic[0].cmap[(int)(i*graphic[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	out8[j*dx + i] = pixvalue;
+      }
+    }
+    image[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].cmapbar.data, dx, dy, 8, 0);
+    break;
+
+  case 16:
+    REALLOCATE (image[0].cmapbar.data, char, 2*dy*dx);
+    out8 = (unsigned char *) image[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = graphic[0].cmap[(int)(i*graphic[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	start = 2*j*dx + 2*i;
+	out8[start + 0] = 0x0000ff & pixvalue;
+	out8[start + 1] = 0x0000ff & (pixvalue >> 8);
+      }
+    }
+    image[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].cmapbar.data, dx, dy, 16, 0);
+    break;
+
+  case 24:
+    extra = 4 - (dx * 3) % 4;
+    REALLOCATE (image[0].cmapbar.data, char, dy*(3*dx + extra));
+    out8 = (unsigned char *) image[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = graphic[0].cmap[(int)(i*graphic[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	start = j*(3*dx+extra) + 3*i;
+	out8[start + 0] = 0x0000ff & pixvalue;
+	out8[start + 1] = 0x0000ff & (pixvalue >> 8);
+	out8[start + 2] = 0x0000ff & (pixvalue >> 16);
+      }
+    }
+    image[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].cmapbar.data, dx, dy, 32, 0);
+    break;
+
+  case 32:
+    REALLOCATE (image[0].cmapbar.data, char, 4*dx*dy);
+    out24 = (unsigned int *) image[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = graphic[0].cmap[(int)(i*graphic[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	out24[j*dx + i] = pixvalue;
+      }
+    }
+    image[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					 image[0].cmapbar.data, dx, dy, 32, 0);
+    break;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreatePicture.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreatePicture.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreatePicture.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "Ximage.h"
+
+void CreatePicture (KapaImageWidget *image, Graphic *graphic) {
+
+  int i, j, extra;
+  unsigned char *c;
+  unsigned int *l;
+  unsigned int start, start1, start2, start3;
+
+  start = graphic[0].back;
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    REALLOCATE (image[0].wide.data,    char, image[0].wide.dx*image[0].wide.dy);
+    REALLOCATE (image[0].zoom.data,    char, image[0].zoom.dx*image[0].zoom.dy);
+    REALLOCATE (image[0].picture.data, char, image[0].picture.dx*image[0].picture.dy);
+    c = (unsigned char *) image[0].picture.data;
+    for (i = 0; i < (image[0].picture.dx*image[0].picture.dy); i++, c++)
+      *c = start;
+    image[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].picture.data, image[0].picture.dx, image[0].picture.dy, 8, 0);
+    break;
+
+  case 16:
+    REALLOCATE (image[0].wide.data,    char, 2*image[0].wide.dx*image[0].wide.dy);
+    REALLOCATE (image[0].zoom.data,    char, 2*image[0].zoom.dx*image[0].zoom.dy);
+    REALLOCATE (image[0].picture.data, char, 2*image[0].picture.dy*image[0].picture.dx);
+    c = (unsigned char *) image[0].picture.data;
+    start1 = 0x0000ff & (start);
+    start2 = 0x0000ff & (start >> 8);
+    for (i = 0; i < image[0].picture.dy; i++) {
+      for (j = 0; j < image[0].picture.dx; j++, c+=2) {
+	c[0] = start1;
+	c[1] = start2;
+      }
+    }
+    image[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].picture.data, image[0].picture.dx, image[0].picture.dy, 16, 0);
+    break;
+
+  case 24:
+    extra = 4 - (image[0].picture.dx * 3) % 4;
+    REALLOCATE (image[0].wide.data, char, image[0].wide.dy*(3*image[0].wide.dx+extra));
+    REALLOCATE (image[0].zoom.data, char, image[0].zoom.dy*(3*image[0].zoom.dx+extra));
+    REALLOCATE (image[0].picture.data, char, image[0].picture.dy*(3*image[0].picture.dx+extra));
+    c = (unsigned char *) image[0].picture.data;
+    start1 = 0x0000ff & (start);
+    start2 = 0x0000ff & (start >> 8);
+    start3 = 0x0000ff & (start >> 16);
+    for (i = 0; i < image[0].picture.dy; i++) {
+      for (j = 0; j < image[0].picture.dx; j++, c+=3) {
+	c[0] = start1;
+	c[1] = start2;
+	c[2] = start3;
+      }
+      c+=extra;
+    }
+    image[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].picture.data, image[0].picture.dx, image[0].picture.dy, 24, 0);
+    break;
+
+  case 32:
+    REALLOCATE (image[0].wide.data,    char, 4*image[0].wide.dx*image[0].wide.dy);
+    REALLOCATE (image[0].zoom.data,    char, 4*image[0].zoom.dx*image[0].zoom.dy);
+    REALLOCATE (image[0].picture.data, char, 4*image[0].picture.dx*image[0].picture.dy);
+    l = (unsigned int *) image[0].picture.data;
+    for (i = 0; i < (image[0].picture.dx*image[0].picture.dy); i++, l++)
+      *l = start;
+    image[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].picture.data, image[0].picture.dx, image[0].picture.dy, 32, 0);
+    break;
+  }
+    
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateWide.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateWide.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateWide.c	(revision 22322)
@@ -0,0 +1,19 @@
+# include "Ximage.h"
+
+void CreateWide (Graphic *graphic, KapaImageWidget *image) {
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    Remap8  (graphic, image, &image->wide, &image->image->matrix);
+    break;
+  case 16:
+    Remap16 (graphic, image, &image->wide, &image->image->matrix);
+    break;
+  case 24:
+    Remap24 (graphic, image, &image->wide, &image->image->matrix);
+    break;
+  case 32:
+    Remap32 (graphic, image, &image->wide, &image->image->matrix);
+    break;
+  }    
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateWindow.c	(revision 22322)
@@ -0,0 +1,35 @@
+# include "Ximage.h"
+
+/************** CreateWindow *************/
+void CreateWindow (Graphic *graphic, Window parent, int border, long events) {
+
+  XSetWindowAttributes attributes;
+  unsigned long attribute_mask;
+  Visual *visual = CopyFromParent;
+
+  HAVE_BACKING = (DoesBackingStore (ScreenOfDisplay(graphic->display, graphic->screen)) == Always);
+  HAVE_BACKING = FALSE;
+
+  if (HAVE_BACKING) {
+    attributes.backing_store = Always;
+    attribute_mask = CWBackingStore | CWBackPixel | CWBorderPixel | CWEventMask;
+  } else {
+    attribute_mask = CWBackPixel | CWBorderPixel | CWEventMask;
+  }
+
+  attributes.background_pixel = graphic->back;
+  attributes.border_pixel     = graphic->fore;
+  attributes.event_mask       = events;
+
+  graphic->window = XCreateWindow (graphic->display, parent, 
+				     graphic->x, graphic->y, 
+				     graphic->dx, graphic->dy, 
+				     border, CopyFromParent,
+				     InputOutput, visual, 
+				     attribute_mask, &attributes);
+
+  if (graphic->window == (Window) None)
+    QuitX (graphic->display, "error: could not open window");
+
+  XSelectInput (graphic->display, graphic->window, events);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage.h"
+
+void CreateZoom (Graphic *graphic, KapaImageWidget *image) {
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    Remap8  (graphic, image, &image->zoom, &image->image->matrix);
+    break;
+  case 16:
+    Remap16 (graphic, image, &image->zoom, &image->image->matrix);
+    break;
+  case 24:
+    Remap24 (graphic, image, &image->zoom, &image->image->matrix);
+    break;
+  case 32:
+    Remap32 (graphic, image, &image->zoom, &image->image->matrix);
+    break;
+  }    
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom16.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom16.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom16.c	(revision 22322)
@@ -0,0 +1,144 @@
+# include "Ximage.h"
+
+void CreateZoom16 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
+
+# if (0)
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY, pixelN;
+  double expand, zoomscale, Rx, Ry;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2, *data;
+  float *imdata, *in_pix,  *in_pix2;
+  unsigned char pixel1[256], pixel2[256];
+  unsigned char pixvalue1, pixvalue2;
+  unsigned long back;
+  unsigned char back1, back2;
+
+  dx = image[0].zoom.dx;
+  dy = image[0].zoom.dy;
+
+  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (image[0].zoom.data, char, 2*dy*dx);
+    bzero (image[0].zoom.data, image[0].zoom.dx*image[0].zoom.dy);
+    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 16, 0);
+    return;
+  }
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel1[i] = 0x0000ff &  graphic[0].cmap[i].pixel;
+    pixel2[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 8);
+  }
+  back = graphic[0].back;
+  back1 = 0x0000ff & back;
+  back2 = 0x0000ff & (back >> 8);
+  // define the color transform parameters
+  MaxValue = graphic[0].Npixels - 1;
+  if (image[0].image[0].range != 0.0) {
+    slope = graphic[0].Npixels / image[0].image[0].range;
+    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
+  } else {
+    slope = 1.0;
+    start = image[0].image[0].zero;
+  }
+
+  zoomscale = MAX (5, image[0].expand + 5);
+  expand = 1 / zoomscale;
+  expand_out = (int) zoomscale;
+  expand_in  = 1;
+
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
+  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
+
+  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  data = out_pix = (unsigned char *) image[0].zoom.data;
+  imdata  = (float *) image[0].image[0].matrix.buffer;
+  in_pix  = &imdata[DX*(int)MAX(Ry,0) + (int)MAX(Rx,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = back;
+    return;
+  } 
+  
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++, out_pix+=2) {
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+    }
+  }
+
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[2*j*dx];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) { 
+      out_pix2 = out_pix + 2*jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2+=2) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+      }
+    }
+    out_pix += 2*i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=2) {
+	pixelN = PixelLookup(*in_pix2);
+	out_pix[0] = pixel1[pixelN];
+	out_pix[1] = pixel2[pixelN];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 2*expand_out) { 
+	pixelN = PixelLookup(*in_pix2);
+	pixvalue1 = pixel1[pixelN];
+	pixvalue2 = pixel2[pixelN];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=2*(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2+=2) {
+	    out_pix2[0] = pixvalue1; 
+	    out_pix2[1] = pixvalue2; 
+	  }
+	}
+      }
+    }
+    out_pix -= 2*dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + 2*jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2+=2) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+      }
+    }
+  } 
+  
+  out_pix = &data[2*j_end*dx];
+  /**** fill in top area ****/
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix+=2) { 
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+    }
+  }
+
+  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 16, 0);
+  
+# endif
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom24.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom24.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom24.c	(revision 22322)
@@ -0,0 +1,178 @@
+# include "Ximage.h"
+
+void CreateZoom24 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
+
+# if (0)
+  int i, j, ii, jj, extra;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY, pixelN;
+  double expand, zoomscale, Rx, Ry;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2, *data;
+  float *imdata, *in_pix, *in_pix2;
+  unsigned char pixel1[256], pixel2[256], pixel3[256];
+  unsigned char pixvalue1, pixvalue2, pixvalue3;
+  unsigned long back;
+  unsigned char back1, back2, back3;
+
+  dx = image[0].zoom.dx;
+  dy = image[0].zoom.dy;
+  extra = 4 - (dx * 3) % 4;
+
+  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (image[0].zoom.data, char, dy*(3*dx+extra));
+    bzero (image[0].zoom.data, image[0].zoom.dx*image[0].zoom.dy);
+    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
+    return;
+  }
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel1[i] = 0x0000ff &  graphic[0].cmap[i].pixel;
+    pixel2[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 8);
+    pixel3[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 16);
+  }
+  back = graphic[0].back;
+  back1 = 0x0000ff & back;
+  back2 = 0x0000ff & (back >> 8);
+  back3 = 0x0000ff & (back >> 16);
+
+  // define the color transform parameters
+  MaxValue = graphic[0].Npixels - 1;
+  if (image[0].image[0].range != 0.0) {
+    slope = graphic[0].Npixels / image[0].image[0].range;
+    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
+  } else {
+    slope = 1.0;
+    start = image[0].image[0].zero;
+  }
+
+  zoomscale = MAX (5, image[0].expand + 5);
+  expand = 1 / zoomscale;
+  expand_out = (int) zoomscale;
+  expand_in  = 1;
+
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
+  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
+
+  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  data = out_pix = (unsigned char *) image[0].zoom.data;
+  imdata  = (float *) image[0].image[0].matrix.buffer;
+  in_pix  = &imdata[DX*(int)MAX(Ry,0) + (int)MAX(Rx,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = back;
+    return;
+  } 
+  
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) {
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+      out_pix[2] = back3;
+    }
+    out_pix += extra;
+  }
+
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[j*(3*dx+extra)];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) { 
+      out_pix2 = out_pix + jj*(3*dx + extra);
+      for (i = 0; i < i_start; i++, out_pix2+=3) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+	out_pix2[2] = back3;
+      }
+    }
+    out_pix += 3*i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=3) {
+	pixelN = PixelLookup(*in_pix2);
+	out_pix[0] = pixel1[pixelN];
+	out_pix[1] = pixel2[pixelN];
+	out_pix[2] = pixel3[pixelN];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 3*expand_out) { 
+	pixelN   = PixelLookup(*in_pix2);
+	pixvalue1 = pixel1[pixelN];
+	pixvalue2 = pixel2[pixelN];
+	pixvalue3 = pixel3[pixelN];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=3*(dx-expand_out)+extra) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2+=3) {
+	    out_pix2[0] = pixvalue1; 
+	    out_pix2[1] = pixvalue2; 
+	    out_pix2[2] = pixvalue3; 
+	  }
+	}
+      }
+    }
+    out_pix -= 3*dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*(3*dx+extra);
+      for (i = i_end; i < dx; i++, out_pix2+=3) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+	out_pix2[2] = back3;
+      }
+    }
+  } 
+  
+  /*
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= (expand_out - (j_end - j_start) % expand_out);
+  */
+
+  out_pix = &data[j_end*(3*dx+extra)];
+  /**** fill in top area ****/
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) { 
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+      out_pix[2] = back3;
+    }
+    out_pix+=extra;
+  }
+
+  /*
+  out_pix = (unsigned char *) image[0].zoom.data;
+  for (i = 0; i < dx*dy; i++) {
+    out_pix[3*i + 0] = back1;
+    out_pix[3*i + 1] = back2;
+    out_pix[3*i + 2] = back3;
+  }
+  for (j = 0; j < dy/2; j++) {
+    for (i = 0; i < dx; i++) {
+      out_pix[j*(extra + 3*dx) + 3*i + 0] = pixel1[10];
+      out_pix[j*(extra + 3*dx) + 3*i + 1] = pixel2[10];
+      out_pix[j*(extra + 3*dx) + 3*i + 2] = pixel3[10];
+    }
+
+  }
+  */
+  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, 24, ZPixmap, 0, 
+					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
+  
+# endif
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom32.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom32.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom32.c	(revision 22322)
@@ -0,0 +1,138 @@
+# include "Ximage.h"
+
+void CreateZoom32 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  double expand, Ix, Iy;
+  int expand_in, expand_out;
+  unsigned int *out_pix, *out_pix2;
+  unsigned short *in_pix, *in_pix2;
+  unsigned long *pixel, pixvalue;
+  unsigned long back;
+
+  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (image[0].zoom.data, char, 4*image[0].zoom.dx*image[0].zoom.dy);
+    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
+    return;
+  }
+
+  ALLOCATE (pixel, unsigned long, graphic[0].Npixels);
+
+  // local array for pixel values
+  for (i = 0; i < graphic[0].Npixels; i++) { 
+    pixel[i] = graphic[0].cmap[i].pixel;
+  }
+  back = graphic[0].back;
+
+  // set up expansions
+  assert ((image[0].zoom.expand >= 1) || (image[0].zoom.expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (image[0].zoom.expand > 0) {
+    expand = 1 / (1.0*image[0].zoom.expand);
+    expand_out = image[0].zoom.expand;
+    expand_in  = 1;
+  }
+  if (image[0].zoom.expand < 0) {
+    expand = fabs((double)image[0].zoom.expand);
+    expand_out = 1;
+    expand_in  = -image[0].zoom.expand;
+  }
+
+  // define the image sizes
+  dx = image[0].zoom.dx;
+  dy = image[0].zoom.dy;
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+  
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, &image[0].image[0].matrix, &image[0].zoom);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, &image[0].image[0].matrix, &image[0].zoom);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, &image[0].zoom);
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned int *) image[0].zoom.data;
+  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  // XXX can we just drop this?
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = back;
+    return;
+  } 
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) {
+    *out_pix = back;
+  }
+
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	*out_pix = pixel[*in_pix2];
+      }
+    } else {
+      // equiv to : out_pix += (i_end - i_start) + [expand_out - (i_end - i_start) % expand_out]
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixvalue = pixel[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue; 
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += (dx - i_end);
+    assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
+  } 
+  assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= (expand_out - (j_end - j_start) % expand_out);
+  
+  assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
+
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned int *)image[0].zoom.data < dx*dy); j++, out_pix++) { 
+    *out_pix = back;
+  }
+  assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
+
+  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
+  
+  free (pixel);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom8.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom8.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CreateZoom8.c	(revision 22322)
@@ -0,0 +1,130 @@
+# include "Ximage.h"
+
+void CreateZoom8 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
+
+# if (0)
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY, pixelN;
+  double expand, zoomscale, Rx, Ry;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2;
+  float *imdata, *in_pix, *in_pix2;
+  unsigned long pixel[256], pixvalue;
+  unsigned long back;
+
+  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (image[0].zoom.data, char, image[0].zoom.dx*image[0].zoom.dy);
+    bzero (image[0].zoom.data, image[0].zoom.dx*image[0].zoom.dy);
+    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 8, 0);
+    return;
+  }
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel[i] = graphic[0].cmap[i].pixel;
+  }
+  back = graphic[0].back;
+
+  // define the color transform parameters
+  MaxValue = graphic[0].Npixels - 1;
+  if (image[0].image[0].range != 0.0) {
+    slope = graphic[0].Npixels / image[0].image[0].range;
+    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
+  } else {
+    slope = 1.0;
+    start = image[0].image[0].zero;
+  }
+
+  zoomscale = MAX (5, image[0].expand + 5);
+  expand = 1 / zoomscale;
+  expand_out = (int) zoomscale;
+  expand_in  = 1;
+
+  dx = image[0].zoom.dx;
+  dy = image[0].zoom.dy;
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
+  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
+
+  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned char *) image[0].zoom.data;
+  imdata  = (float *) image[0].image[0].matrix.buffer;
+  in_pix  = &imdata[DX*(int)MAX(Ry,0) + (int)MAX(Rx,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = back;
+    return;
+  } 
+  
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) 
+    *out_pix = back;
+  
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	pixelN = PixelLookup(*in_pix2);
+	*out_pix = pixel[pixelN];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixelN   = PixelLookup(*in_pix2);
+	pixvalue = pixel[pixelN];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue;
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += (dx - i_end);
+    
+  } 
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned char *) image[0].zoom.data < dx*dy); j++, out_pix++) { 
+    *out_pix = back;
+  }
+
+  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 8, 0);
+  
+# endif
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CrossHairs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CrossHairs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CrossHairs.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include "Ximage.h"
+
+void CrossHairs (Graphic *graphic, Picture *image) {
+
+  int x0, x1, x5, x7;
+  int y0, y2, y4, y5;
+  double zoomscale;
+
+  // is this totally wrong??
+  zoomscale = image[0].expand;
+  x0 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale - 2.5) + image[0].x;
+  x5 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale - 0.5) + image[0].x;
+  x7 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale + 0.5) + image[0].x;
+  x1 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale + 2.5) + image[0].x;
+						    	 
+  y4 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale - 2.5) + image[0].y;
+  y2 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale - 0.5) + image[0].y;
+  y0 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale + 0.5) + image[0].y;
+  y5 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale + 2.5) + image[0].y;
+
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+  
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y0, x5, y0);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5, y0, x5, y5);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y5, x7, y0);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y0, x1, y0);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y2, x5, y2);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5, y2, x5, y4);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y4, x7, y2);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y2, x1, y2);
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y0+1, x5-1, y0+1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5-1, y0+1, x5-1, y5);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y5, x7+1, y0+1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y0+1, x1, y0+1);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y2-1, x5-1, y2-1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5-1, y2-1, x5-1, y4);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y4, x7+1, y2-1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y2-1, x1, y2-1);
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CursorOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CursorOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/CursorOps.c	(revision 22322)
@@ -0,0 +1,121 @@
+# include "Ximage.h"
+
+// input coordinates are relative to the picture bounding box
+void Picture_to_Image (double *x1, double *y1, double x2, double y2, Picture *picture) {
+
+  double expand, dx, dy;
+
+  expand = 1.0;
+  if (picture[0].expand > 0) {
+    expand = 1 / (1.0*picture[0].expand);
+  } else {
+    expand = fabs((double)picture[0].expand);
+  }
+  
+  // pixel coordinates in picture frame
+  dx = expand*(x2 - 0.5*picture[0].dx);
+  dy = expand*(y2 - 0.5*picture[0].dy);
+
+  // picture[0].X,Y is the image coordinate in the center of the picture
+  *x1 = picture[0].flipx ? picture[0].X - dx : picture[0].X + dx;
+  *y1 = picture[0].flipy ? picture[0].Y - dy : picture[0].Y + dy;
+}
+
+// input coordinates are relative to the X window
+void Screen_to_Image (double *x1, double *y1, double x2, double y2, Picture *picture) {
+
+  double xp, yp;
+
+  // pixel coordinates in picture frame
+  xp = x2 - picture[0].x;
+  yp = y2 - picture[0].y;
+
+  Picture_to_Image (x1, y1, xp, yp, picture);
+}
+
+void Image_to_Picture (double *x1, double *y1, double x2, double y2, Picture *picture) {
+
+  double expand, dx, dy;
+
+  /* notice that here, expand is the reciprocal of the expand above */
+  expand = 1.0;
+  if (picture[0].expand > 0) {
+    expand = picture[0].expand;
+  } else {
+    expand = 1 / fabs((double)picture[0].expand);
+  }
+  
+  // pixel coordinates in picture frame
+  dx = picture[0].flipx ? picture[0].X - x2 : x2 - picture[0].X;
+  dy = picture[0].flipy ? picture[0].Y - y2 : y2 - picture[0].Y;
+    
+  *x1 = expand*dx + 0.5*picture[0].dx;
+  *y1 = expand*dy + 0.5*picture[0].dy;
+}
+
+void Image_to_Screen (double *x1, double *y1, double x2, double y2, Picture *picture) {
+
+  double xp, yp;
+
+  Image_to_Picture (&xp, &yp, x2, y2, picture);
+
+  // pixel coordinates in screen frame
+  *x1 = xp + picture[0].x;
+  *y1 = yp + picture[0].y;
+}
+
+// input coordinates are relative to the picture bounding box
+void Picture_Lower (int *i_start, int *j_start, Matrix *matrix, Picture *picture) {
+
+  double Ix, Iy, Sx, Sy;
+
+  // Ix, Iy are the image coordinates of the specified screen pixel
+  Picture_to_Image (&Ix, &Iy, 0.0, 0.0, picture);
+
+  // Ix, Iy are now limited to valid image coordinates
+  Ix = MIN (MAX (Ix, 0), matrix[0].Naxis[0]);
+  Iy = MIN (MAX (Iy, 0), matrix[0].Naxis[1]);
+
+  // round up (down) to nearest pixel boundary
+  if (Ix > (int)(Ix)) {
+    Ix = picture[0].flipx ? (int)(Ix) : (int)(Ix) + 1;
+  }
+  if (Iy > (int)(Iy)) {
+    Iy = picture[0].flipy ? (int)(Iy) : (int)(Iy) + 1;
+  }
+
+  // Sx, Sy are the screen coordinates of the Ix,Iy pixel
+  Image_to_Picture (&Sx, &Sy, Ix, Iy, picture);
+
+  // i_start, j_start are now limited to valid screen coordinates
+  *i_start = MIN (MAX (Sx, 0), picture[0].dx);
+  *j_start = MIN (MAX (Sy, 0), picture[0].dy);
+}
+  
+// input coordinates are relative to the picture bounding box
+void Picture_Upper (int *i_end, int *j_end, Matrix *matrix, Picture *picture) {
+
+  double Ix, Iy, Sx, Sy;
+
+  // Ix, Iy are the image coordinates of the specified screen pixel
+  Picture_to_Image (&Ix, &Iy, picture[0].dx, picture[0].dy, picture);
+
+  // Ix, Iy are now limited to valid image coordinates
+  Ix = MIN (MAX (Ix, 0), matrix[0].Naxis[0]);
+  Iy = MIN (MAX (Iy, 0), matrix[0].Naxis[1]);
+
+  // round down (up) to nearest pixel boundary
+  if (Ix > (int)(Ix)) {
+    Ix = picture[0].flipx ? (int)(Ix) + 1 : (int)(Ix);
+  }
+  if (Iy > (int)(Iy)) {
+    Iy = picture[0].flipy ? (int)(Iy) + 1: (int)(Iy);
+  }
+
+  // Sx, Sy are the screen coordinates of the Ix,Iy pixel
+  Image_to_Picture (&Sx, &Sy, Ix, Iy, picture);
+
+  // i_start, j_start are now limited to valid screen coordinates
+  *i_end = MIN (MAX (Sx, 0), picture[0].dx);
+  *j_end = MIN (MAX (Sy, 0), picture[0].dy);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DefineSection.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DefineSection.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DefineSection.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "Ximage.h"
+
+// define the section, but do not create a graph or image 
+int DefineSection (int sock) {
+  
+  int N, MoveSection;
+  char name[128];
+  double x, y, dx, dy;
+  Section *section;
+
+  KiiScanMessage (sock, "%s %lf %lf %lf %lf", name, &x, &y, &dx, &dy);
+
+  MoveSection = FALSE;
+
+  N = GetSectionByName (name);
+  if (N < 0) {
+    section = AddSection (name, x, y, dx, dy);
+    MoveSection = TRUE;
+  } else {
+    section = GetSectionByNumber (N);
+    SetActiveSectionByNumber (N);
+
+    if (section[0].x != x)   MoveSection = TRUE;
+    if (section[0].y != y)   MoveSection = TRUE;
+    if (section[0].dx != dx) MoveSection = TRUE;
+    if (section[0].dy != dy) MoveSection = TRUE;
+
+    section[0].x = x;
+    section[0].y = y;
+    section[0].dx = dx;
+    section[0].dy = dy;
+  }
+
+  if (MoveSection) {
+    SetSectionSizes (section);
+    Refresh (1);
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DragColorbar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DragColorbar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DragColorbar.c	(revision 22322)
@@ -0,0 +1,106 @@
+# include "Ximage.h"
+/* future expansion: warp mouse to the right spot for the current settings */
+
+void DragColorbar (Graphic *graphic, KapaImageWidget *image, XButtonEvent *mouse_event) {
+
+  double          frac_x, oldfrac_x, frac_y, oldfrac_y, start, slope;
+  int             X, Y;
+  XEvent          event;
+  XMotionEvent   *move;
+  int             xstatus, Npix, center;
+
+  if (!graphic[0].visualclass) return;
+
+  X = mouse_event[0].x;
+  Y = mouse_event[0].y;
+  Npix = graphic[0].Npixels;
+  slope = image[0].image[0].slope;
+  start = image[0].image[0].start;
+  oldfrac_x = oldfrac_y = 10;
+  
+  while (1) {
+    frac_x = X / (1.0*graphic[0].dx);
+    frac_x = MAX (0, frac_x);
+    frac_x = MIN (1, frac_x);
+    frac_y = Y / (1.0*graphic[0].dy);
+    frac_y = MAX (0, frac_y);
+    frac_y = MIN (1, frac_y);
+    switch (mouse_event[0].button) {
+    case 1:
+      center = frac_x*Npix;
+      slope = Npix / (1 + 4*frac_y*frac_y*Npix);
+      start = Npix/2.0 - center*slope;
+      break;
+    case 2:
+      start = 0;
+      slope = 1;
+      break;
+    case 3:
+      center = (Npix/2.0 - start) / slope;
+      slope = Npix / (1 + 4*frac_x*frac_x*Npix);
+      start = Npix/2.0 - center*slope;
+      break;
+    }
+
+    if ((frac_x != oldfrac_x) || (frac_y != oldfrac_y)) {
+      ResetColorbar (graphic, start, slope);
+    }
+    oldfrac_x = frac_x;
+    oldfrac_y = frac_y;
+      
+    if ((xstatus = XCheckMaskEvent (graphic[0].display, EVENT_MASK, &event))) {
+      
+      switch (event.type)  {
+	
+      case MotionNotify:
+	if ((xstatus = XPending (graphic[0].display)) < 2) {
+	  move = (XMotionEvent *) &event;
+	  X = move[0].x;
+	  Y = move[0].y;
+	}
+	break;
+	
+      case ButtonPress:
+      case ButtonRelease:
+	image[0].image[0].start = start;	
+	image[0].image[0].slope = slope;
+	return;
+	break;
+	
+      }
+    }
+  }
+  
+}
+
+void ResetColorbar (Graphic *graphic, double start, double slope) {
+
+  int i, j;
+  XColor cmap[256];
+
+  if (!graphic[0].visualclass) return;
+
+  for (i = 0; i < graphic[0].Npixels; i++) {
+    cmap[i] = graphic[0].cmap[i];
+    j = start + i * slope;
+    if (j < 0) {
+      cmap[i].red = graphic[0].cmap[0].red;
+      cmap[i].blue = graphic[0].cmap[0].blue;
+      cmap[i].green = graphic[0].cmap[0].green;
+    }
+    else {
+      if (j >= graphic[0].Npixels) {
+	cmap[i].red = graphic[0].cmap[graphic[0].Npixels-1].red;
+	cmap[i].blue = graphic[0].cmap[graphic[0].Npixels-1].blue;
+	cmap[i].green = graphic[0].cmap[graphic[0].Npixels-1].green;
+      }
+      else {
+	cmap[i].red = graphic[0].cmap[j].red;
+	cmap[i].blue = graphic[0].cmap[j].blue;
+	cmap[i].green = graphic[0].cmap[j].green;
+      }
+    }
+  }
+
+  XStoreColors(graphic[0].display, graphic[0].colormap, cmap, graphic[0].Npixels);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawBitmap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawBitmap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawBitmap.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "Ximage.h"
+
+void DrawBitmap (Graphic *graphic, int x, int y, int dx, int dy, char *bitmap, int mode) {
+
+  int i, j, byte_line, byte, bit, flag;
+  unsigned long int fore, back;
+
+  fore = graphic[0].fore;
+  back = graphic[0].back;
+
+  if (mode == 0) {
+    fore = graphic[0].fore;
+    back = graphic[0].back;
+    graphic[0].fore = back;
+    graphic[0].back = back;
+  }
+    
+  if (mode == 2) {
+    fore = graphic[0].fore;
+    back = graphic[0].back;
+    graphic[0].fore = back;
+    graphic[0].back = fore;
+  }
+    
+  
+  byte_line = (int) ((dx + 7) / 8);
+  for (i = 0; i < dy; i++) {
+    for (j = 0; j < dx; j++) {
+      byte = byte_line * i + (j / 8);
+      bit = j % 8;
+      flag = 0x01 & (bitmap[byte] >> bit);
+      if (flag)
+	XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+      else 
+	XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+      XDrawPoint (graphic[0].display, graphic[0].window, 
+		  graphic[0].gc, x + j, y + i);
+    }
+  }
+  if (mode == 0) {
+    graphic[0].fore = fore;
+    graphic[0].back = back;
+  }
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawButton.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawButton.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawButton.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "Ximage.h"
+
+void DrawButton (Graphic *graphic, Button *button) {
+  
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+  XFillRectangle (graphic[0].display, 
+		  graphic[0].window,
+		  graphic[0].gc,
+		  button[0].x,  button[0].y,
+		  button[0].dx, button[0].dy);
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+  XDrawRectangle (graphic[0].display, 
+		  graphic[0].window,
+		  graphic[0].gc,
+		  button[0].x,  button[0].y,
+		  button[0].dx, button[0].dy);
+
+  DrawBitmap (graphic, 
+	      button[0].x + (button[0].dx - button[0].width) / 2 + 1, 
+	      button[0].y + (button[0].dy - button[0].height) / 2 + 1, 
+	      button[0].width, button[0].height, 
+	      button[0].bitmap, 1);
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+}
+
+# if (0)
+  if (button[0].text) {
+    dX = XTextWidth (graphic[0].font, button[0].bitmap, strlen(button[0].bitmap));
+    y = button[0].y + (button[0].dy + graphic[0].font[0].ascent)/2;
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 button[0].x + (button[0].dx - dX) / 2, y,
+		 button[0].bitmap, strlen(button[0].bitmap));
+  } else {
+    DrawBitmap (graphic, 
+		button[0].x + (button[0].dx - button[0].width) / 2 + 1, 
+		button[0].y + (button[0].dy - button[0].height) / 2 + 1, 
+		button[0].width, button[0].height, 
+		button[0].bitmap, 1);
+  }
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawFrame.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawFrame.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawFrame.c	(revision 22322)
@@ -0,0 +1,184 @@
+# include "Ximage.h"
+# define MIN_RANGE 1e-10
+# define DrawLine(X,Y,DX,DY) (XDrawLine (graphic->display, graphic->window, graphic->gc, (int)(X), (int)(Y), (int)(X+DX), (int)(Y+DY)))
+  
+static Graphic *graphic;
+
+void DrawFrame (KapaGraphWidget *graph) {
+  
+  int i, fx, fy, dfx, dfy, P, IsLabel, IsMajor;
+  double range, major, minor, first, next, lweight;
+
+  graphic = GetGraphic();
+
+  /* each axis is drawn independently, but ticks and labels are placed according to
+     perpendicular distance. */
+  for (i = 0; i < 4; i++) {
+    fx = graph[0].axis[i].fx;
+    fy = graph[0].axis[i].fy;
+    dfx = graph[0].axis[i].dfx;
+    dfy = graph[0].axis[i].dfy;
+    P = hypot ((double)graph[0].axis[(i+1)%2].dfx, (double)graph[0].axis[(i+1)%2].dfy);
+
+    lweight = MAX (0, MIN (10, graph[0].axis[i].lweight));
+    XSetLineAttributes (graphic->display, graphic->gc, lweight, LineSolid, CapNotLast, JoinMiter);
+    
+    graph[0].axis[i].color = MAX (0, MIN (15, graph[0].axis[i].color));
+
+    XSetForeground (graphic->display, graphic->gc, graphic->color[graph[0].axis[i].color]);
+    DrawRotTextInit (graphic->display, graphic->window, graphic->gc, graphic->color[graph[0].axis[i].color], graphic->back);
+
+    if (graph[0].axis[i].isaxis) {
+      DrawLine (fx, fy, dfx, dfy);
+    }
+    
+    if (graph[0].axis[i].areticks) {
+      if (isnan(graph[0].axis[i].min) || isinf(graph[0].axis[i].min)) continue;
+      if (isnan(graph[0].axis[i].max) || isinf(graph[0].axis[i].max)) continue;
+
+      range = graph[0].axis[i].max - graph[0].axis[i].min;
+      if (fabs(range) < MIN_RANGE) continue;
+
+      if (fabs(range) < MIN_RANGE) {
+	range = (range < 0) ? -MIN_RANGE : +MIN_RANGE;
+      }
+
+      AxisTickScale (&graph[0].axis[i], &major, &minor);
+
+      first = minor*((int)(graph[0].axis[i].min/minor));
+      if ((range > 0) && (first < graph[0].axis[i].min)) {
+	first += minor;
+      }
+      if ((range < 0) && (first > graph[0].axis[i].min)) {
+	first -= minor;
+      }
+      for (next = first; ((range > 0) && (next <= graph[0].axis[i].max)) || ((range < 0) && (next >= graph[0].axis[i].max));) {
+	IsMajor = FALSE;
+	IsMajor |= (fabs((int)(next/major) - (next/major)) < 0.5*(minor/major));
+	IsMajor |= (fabs((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major));
+	IsMajor |= (fabs ((int)((next - 0.5*minor)/major) - (next/major)) < 0.5*(minor/major));
+	IsLabel = (IsMajor && graph[0].axis[i].islabel);
+	DrawTick (fx, fy, dfx, dfy, P, graph[0].axis[i].min, graph[0].axis[i].max, next, IsLabel, IsMajor, i);
+	if (range > 0) 
+	  next += minor;
+	else 
+	  next -= minor;
+      }
+    }
+  }
+}
+
+void DrawTick (int fx, int fy, int dfx, int dfy, 
+	       int P, double min, double max, 
+	       double value, int IsLabel, int IsMajor, int naxis) {
+  
+  int x, y, dx, dy, pos, dir, fontsize;
+  double size, n;
+  char string[64], *fontname;
+
+  if (IsMajor) { 
+    size = MAX (0.02, 7.0 / P); 
+  } else {
+    size = MAX (0.01, 4.0 / P); 
+  }
+  
+  n = P / sqrt ((double)(dfx*dfx + dfy*dfy));
+  x = fx + (value-min)*dfx/(max - min);
+  y = fy + (value-min)*dfy/(max - min);
+
+  dir = +1;
+  if ((naxis == 0) || (naxis == 1)) dir *= -1;
+  dx = dir*size*dfy*n;	
+  dy = dir*size*dfx*n;
+  
+  DrawLine (x, y, dx, dy);
+
+# ifdef IN_AND_OUT_TICKS
+# define GAP 0.03
+# else
+# define GAP 0.01
+# endif
+
+  if (IsLabel) {
+    fontname = GetRotFont (&fontsize);
+    pos = 0;
+    
+    /* temporarily assume rectilinear axes */
+    if (naxis == 0) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 1; }
+    if (naxis == 2) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 7; }
+
+    if (naxis == 1) { dy = 0; dx = -(0.8*fontsize + 1); pos = 3; }
+    if (naxis == 3) { dy = 0; dx = +(0.8*fontsize + 1); pos = 5; }
+
+    x = fx + (value-min)*dfx/(max - min) + dx;
+    y = fy + (value-min)*dfy/(max - min) + dy;
+    if (fabs(value/(max-min)) < 0.001) { value = 0.0; }
+    sprintf (string, "%4g", value);
+    DrawRotText (x, y, string, pos, 0.0);
+  }
+  
+}
+
+
+void AxisTickScale (Axis *axis, double *major, double *minor) {
+
+  double range, lrange, factor, mantis, fmantis, power;
+
+  range = axis[0].max - axis[0].min;
+  if (fabs(range) < MIN_RANGE) {
+    range = (range < 0) ? -MIN_RANGE : +MIN_RANGE;
+  }
+
+  lrange = log10(MAX(fabs(range), MIN_RANGE));
+  mantis = modf (lrange, &factor);
+  if (mantis < 0.0) {
+    mantis += 1.0;
+    factor -= 1.0;
+  }
+  
+  power = MAX(pow(10.0, factor), MIN_RANGE);
+  fmantis = pow(10.0, mantis);
+  *major = MAX(0.5 * power, MIN_RANGE);
+  *minor = MAX(0.1 * power, MIN_RANGE);
+  
+  if ((fmantis >= 1.0) && (fmantis <  1.999)) {
+    *major = 0.5 * power;
+    *minor = 0.1 * power;
+    if (axis[0].areticks == 1) {
+      *major = 1.0 * power;
+      *minor = 0.2 * power;
+    }	  
+  }
+  if ((fmantis >= 1.999) && (fmantis <  3.999)) {
+    *major = 1.0 * power;
+    *minor = 0.2 * power;
+    if (axis[0].areticks == 1) {
+      *major = 2.0 * power;
+      *minor = 0.5 * power;
+    }	  
+  }
+  if ((fmantis >= 3.999) && (fmantis <  5.999)) {
+    *major = 1.0 * power;
+    *minor = 0.5 * power;
+    if (axis[0].areticks == 1) {
+      *major = 2.0 * power;
+      *minor = 0.5 * power;
+    }	  
+  }
+  if ((fmantis >= 5.999) && (fmantis <   7.999)) {
+    *major = 2.0 * power;
+    *minor = 0.5 * power;
+    if (axis[0].areticks == 1) {
+      *major = 2.5 * power;
+      *minor = 0.5 * power;
+    }	  
+  }
+  if ((fmantis >= 7.999) && (fmantis <  10.000)) {
+    *major = 2.5 * power;
+    *minor = 0.5 * power;
+    if (axis[0].areticks == 1) {
+      *major = 5.0 * power;
+      *minor = 1.0 * power;
+    }	  
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawLabels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawLabels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawLabels.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "Ximage.h"
+  
+void EraseLabels (KapaGraphWidget *graph) {
+  
+  Graphic *graphic;
+  graphic = GetGraphic();
+
+  DrawLabelsRaw (graphic, graph, graphic->back);
+}
+
+void DrawLabels (KapaGraphWidget *graph) {
+  
+  Graphic *graphic;
+  graphic = GetGraphic();
+
+  DrawLabelsRaw (graphic, graph, graphic->fore);
+}
+
+void DrawLabelsRaw (Graphic *graphic, KapaGraphWidget *graph, int color) {
+
+  int i, pos, x, y, size;
+  double angle;
+  char *fontname;
+
+  pos = 0;
+  fontname = GetRotFont (&size);
+  XSetForeground (graphic->display, graphic->gc, color);
+  DrawRotTextInit (graphic->display, graphic->window, graphic->gc, color, graphic->back);
+
+  /* each label is drawn independently */
+  for (i = 0; i < 8; i++) {
+    if (strcmp (graph[0].label[i].text, "")) {
+      angle = 0;
+      switch (i) {
+      case 0: pos = 7; break;
+      case 1: pos = 1; angle = -90; break;
+      case 2: pos = 1; break;
+      case 3: pos = 1; angle =  90; break;
+      case 4: pos = 2; break;
+      case 5: pos = 0; break;
+      case 6: pos = 8; break;
+      case 7: pos = 6; break;
+      }	
+      x = graph[0].label[i].x;
+      y = graph[0].label[i].y;
+      SetRotFont (graph[0].label[i].font, graph[0].label[i].size);
+      DrawRotText (x, y, graph[0].label[i].text, pos, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /*
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|       |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+          
+ 6____7___8 
+  |       | 
+  |       | 
+ 3|   4   |5
+  |       |
+  |       |
+  ---------
+  0   1   2
+
+  */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawObjects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawObjects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawObjects.c	(revision 22322)
@@ -0,0 +1,624 @@
+# include "Ximage.h"
+# define DrawLine(X1,Y1,X2,Y2) (XDrawLine (graphic->display, graphic->window, graphic->gc, (int)(X1+0.5), (int)(Y1+0.5), (int)(X2+0.5), (int)(Y2+0.5)))
+# define DrawRectangle(X1,Y1,X2,Y2) (XDrawRectangle (graphic->display, graphic->window, graphic->gc, (int)(X1+0.5), (int)(Y1+0.5), (int)(X2+0.5), (int)(Y2+0.5)))
+# define FillRectangle(X1,Y1,X2,Y2) (XFillRectangle (graphic->display, graphic->window, graphic->gc, (int)(X1+0.5), (int)(Y1+0.5), (int)(X2+0.5), (int)(Y2+0.5)))
+# define DrawCircle(X,Y,R) (XDrawArc (graphic->display, graphic->window, graphic->gc, (int)(X-R+0.5), (int)(Y-R+0.5), abs(2*R+0.5), abs(2*R+0.5), 0, 23040))
+
+# define CONNECT 0
+# define HISTOGRAM 1
+# define POINTS 2
+
+static Graphic *graphic;
+
+void ClipLine (double x0, double y0, double x1, double y1, double X0, double Y0, double X1, double Y1);
+
+/* draw all objects for this Graph */
+int DrawObjects (KapaGraphWidget *graph) {
+  
+  int i;
+  
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    if (DEBUG) fprintf (stderr, "object: %d\n", i);
+    if (DEBUG) fprintf (stderr, "Npts: %d\n", graph[0].objects[i].Npts);
+    DrawObjectN (graph, &graph[0].objects[i]);
+  }    
+  return (TRUE);
+}
+
+/* Draw a specific object in the graph */
+int DrawObjectN (KapaGraphWidget *graph, Gobjects *object) {
+  
+  static char dash[2] = {5,10};
+  static char dot[2] = {3,3};
+  int lweight;
+  
+  // this function calls all of the supporting Draw... functions below
+  graphic = GetGraphic();
+
+  lweight = MAX (0, MIN (10, object[0].lweight));
+
+  /* set line type */
+  switch (object[0].ltype) {
+  case 0:
+    XSetLineAttributes (graphic->display, graphic->gc, lweight, LineSolid, CapNotLast, JoinMiter);
+    break;
+  case 1:
+    XSetDashes (graphic->display, graphic->gc, 100, dash, 2);
+    XSetLineAttributes (graphic->display, graphic->gc, lweight, LineOnOffDash, CapNotLast, JoinMiter);
+    break;
+  case 2:
+    XSetDashes (graphic->display, graphic->gc, 10, dot, 2);
+    XSetLineAttributes (graphic->display, graphic->gc, lweight, LineOnOffDash, CapNotLast, JoinMiter);
+    break;
+  default:
+    XSetLineAttributes (graphic->display, graphic->gc, lweight, LineSolid, CapNotLast, JoinMiter);
+    break;
+  }
+
+  XSetForeground (graphic->display, graphic->gc, graphic->color[object[0].color]);
+
+  switch (object[0].style) {
+  case CONNECT: 
+    DrawConnect (graph, object);
+    break;
+  case HISTOGRAM:
+    DrawHistogram (graph, object);
+    break;
+  case POINTS:
+    DrawPoints (graph, object);
+    break;
+  }
+    
+  if (object[0].etype & 0x01) {
+    DrawYErrors (graph, object);
+  }
+  if (object[0].etype & 0x02) {
+    DrawXErrors (graph, object);
+  }
+
+  XSetLineAttributes (graphic->display, graphic->gc, 0, LineSolid, CapNotLast, JoinMiter);
+  XSetForeground (graphic->display, graphic->gc, graphic->fore);
+  return (TRUE);
+}
+
+/******/
+void DrawConnect (KapaGraphWidget *graph, Gobjects *object) {
+  
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    
+    ClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
+    /* DrawLine (sx0, sy0, sx1, sy1); */
+    sx0 = sx1; sy0 = sy1;
+  }
+  
+}
+
+void ClipLine (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0) {
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x0,y0 if outside box */
+  if ((x0 < X0) && (x1 >= X0)) {
+    y0 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X0;
+  }
+  if ((x0 > X1) && (x1 <= X1)) {
+    y0 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X1;
+  }
+  if ((y0 < Y0) && (y1 >= Y0)) {
+    x0 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y0;
+  }
+  if ((y0 > Y1) && (y1 <= Y1)) {
+    x0 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y1;
+  }
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x1,y1 if outside box */
+  if ((x1 < X0) && (x0 >= X0)) {
+    y1 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X0;
+  }
+  if ((x1 > X1) && (x0 <= X1)) {
+    y1 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X1;
+  }
+  if ((y1 < Y0) && (y0 >= Y0)) {
+    x1 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y0;
+  }
+  if ((y1 > Y1) && (y0 <= Y1)) {
+    x1 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y1;
+  }
+  DrawLine (x0, y0, x1, y1);
+}
+  
+
+/******/
+void DrawHistogram (KapaGraphWidget *graph, Gobjects *object) {
+
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sxa;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  /* find the first valid datapoint */
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  sx0 = MIN (MAX (sx0, X0), X1);
+  sy0 = MAX (MIN (sy0, Y0), Y1);
+  
+  /* continue with rest of points */
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    sx1 = MIN (MAX (sx1, X0), X1);
+    sy1 = MAX (MIN (sy1, Y0), Y1);
+    sxa = 0.5*(sx0 + sx1);
+    DrawLine (sx0, sy0, sxa, sy0);
+    DrawLine (sxa, sy0, sxa, sy1);
+    DrawLine (sxa, sy1, sx1, sy1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+/******/
+void DrawPoints (KapaGraphWidget *graph, Gobjects *object) {
+
+  int i;
+  float *x, *y, *z;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx, sy, d, sx1, sy1, sx2, sy2;
+  
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  /**** points are scaled by object.z ***/
+  if (object[0].size < 0) {
+    d = 0.5 * (graphic->dx + graphic->dy) * 0.01;
+    x = object[0].x; y = object[0].y; z = object[0].z;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx - d*z[i], sy - d*z[i], 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {	/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx - d*z[i], sy - d*z[i], 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy, sx + d*z[i] + 1, sy);
+	    DrawLine (sx, sy - d*z[i], sx, sy + d*z[i] + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {	/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d*z[i] + 1, sy - d*z[i] - 1, sx - d*z[i], sy + d*z[i]);
+	    DrawLine (sx - d*z[i], sy - d*z[i], sx + d*z[i] + 1, sy + d*z[i] + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      XPoint points[4];
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    points[0].x = sx - d*z[i];  points[0].y = sy + 0.58*d*z[i];  
+	    points[1].x = sx + d*z[i];  points[1].y = sy + 0.58*d*z[i];  
+	    points[2].x = sx;           points[2].y = sy - 1.15*d*z[i];  
+	    points[3].x = sx - d*z[i];  points[3].y = sy + 0.58*d*z[i];  
+	    XFillPolygon (graphic->display, graphic->window, graphic->gc, points, 4, Convex, CoordModeOrigin);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy + 0.58*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx + d*z[i], sy + 0.58*d*z[i], sx,          sy - 1.15*d*z[i]);
+	    DrawLine (sx,          sy - 1.15*d*z[i], sx - d*z[i], sy + 0.58*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx,          sy - 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	/* connect a pair of points */
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	if (!(finite(x[i+1]) && finite(y[i+1]))) continue;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  } 
+  /**** points are not scaled by object.z ***/
+  else {
+    d = object[0].size * 0.5 * (graphic->dx + graphic->dy) * 0.003;
+    x = object[0].x; y = object[0].y;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx - d, sy - d, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {		/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+   	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx - d, sy - d, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy, sx + d + 1, sy);
+	    DrawLine (sx, sy - d, sx, sy + d + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {		/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    /* XDrawLine is a little funny, this is needed to fix endpost errors */
+	    DrawLine (sx + d + 1, sy - d - 1, sx - d, sy + d);
+	    DrawLine (sx - d, sy - d, sx + d + 1, sy + d + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      XPoint points[4];
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    points[0].x = sx - d;  points[0].y = sy + 0.58*d;  
+	    points[1].x = sx + d;  points[1].y = sy + 0.58*d;  
+	    points[2].x = sx;      points[2].y = sy - 1.15*d;  
+	    points[3].x = sx - d;  points[3].y = sy + 0.58*d;  
+	    XFillPolygon (graphic->display, graphic->window, graphic->gc, points, 4, Convex, CoordModeOrigin);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy + 0.58*d, sx + d, sy + 0.58*d);
+	    DrawLine (sx + d, sy + 0.58*d, sx,     sy - 1.15*d);
+	    DrawLine (sx,     sy - 1.15*d, sx - d, sy + 0.58*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d, sy + 0.58*d);
+	    DrawLine (sx, sy, sx + d, sy + 0.58*d);
+	    DrawLine (sx, sy, sx,     sy - 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  }
+}
+    
+/******/
+void DrawXErrors (KapaGraphWidget *graph, Gobjects *object) {
+  
+  int i, bar;
+  float *x, *y, *dxm, *dxp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dxp = object[0].dxp; dxm = object[0].dxm; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[1].dfy*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] + dxp[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] + dxp[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy))) 
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxm[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] - dxm[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] - dxm[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+  }
+}
+    
+/******/
+void DrawYErrors (KapaGraphWidget *graph, Gobjects *object) {
+
+  int i, bar;
+  float *x, *y, *dym, *dyp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dyp = object[0].dyp; dym = object[0].dym; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[0].dfx*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dyp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = sx0 + dyp[i]*mxj;
+    sy1 = sy0 + dyp[i]*myj;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dym[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] - dym[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] - dym[i])*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawTextlines.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawTextlines.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/DrawTextlines.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "Ximage.h"
+  
+void DrawTextlines (KapaGraphWidget *graph) {
+  
+  int i, x, y, size;
+  double angle;
+  char *fontname;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  fontname = GetRotFont (&size);
+  XSetForeground (graphic->display, graphic->gc, graphic->fore);
+  DrawRotTextInit (graphic->display, graphic->window, graphic->gc, graphic->fore, graphic->back);
+
+  for (i = 0; i < graph[0].Ntextline; i++) {
+    if (strcmp (graph[0].textline[i].text, "")) {
+      angle = graph[0].textline[i].angle;
+      x = graph[0].textline[i].x;
+      y = graph[0].textline[i].y;
+      SetRotFont (graph[0].textline[i].font, graph[0].textline[i].size);
+      DrawRotText (x, y, graph[0].textline[i].text, 5, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /* pos values
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|   8   |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+  */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseCurrentPlot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseCurrentPlot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseCurrentPlot.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage.h"
+
+// erase just the current plot
+int EraseCurrentPlot () {
+  
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->graph == NULL) return (TRUE);
+
+  EraseGraph (section->graph);
+  
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseImage.c	(revision 22322)
@@ -0,0 +1,19 @@
+# include "Ximage.h"
+
+int EraseImage () {
+
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic();
+  section = GetActiveSection();
+  image = section->image;
+  if (image == NULL) return (TRUE);
+
+  FreeImage (image);
+  section->image = NULL;
+
+  if (USE_XWINDOW) Refresh ();
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseOverlay.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include "Ximage.h"
+
+int EraseOverlay (int sock) {
+
+  int i, N;
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic();
+  section = GetActiveSection();
+  image = section->image;
+  if (image == NULL) return (TRUE);
+
+  KiiScanCommand (sock, 16, "%*s %d", &N);
+
+  if (N > NOVERLAYS) {
+    REALLOCATE (image[0].tickmarks.objects, KiiOverlay, 1);
+    image[0].tickmarks.Nobjects = 0;
+  } else {
+    for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+      if (image[0].overlay[N].objects[i].type == KII_OVERLAY_TEXT) {
+	free (image[0].overlay[N].objects[i].text);
+      }
+    }
+    REALLOCATE (image[0].overlay[N].objects, KiiOverlay, 1);
+    image[0].overlay[N].Nobjects = 0;
+    image[0].overlay[N].active = FALSE;
+  }
+
+  if (USE_XWINDOW) Refresh ();
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ErasePlots.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ErasePlots.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/ErasePlots.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "Ximage.h"
+
+// erase all plots, keep the sections
+int ErasePlots () {
+  
+  int i, Nsection;
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+  
+  // reset the sizes for all sections
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+      section = GetSectionByNumber (i);
+      EraseGraph (section->graph);
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseSections.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseSections.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EraseSections.c	(revision 22322)
@@ -0,0 +1,16 @@
+# include "Ximage.h"
+
+int EraseSections () {
+  
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+  
+  FreeSections ();
+  AddSection ("default", 0.0, 0.0, 1.0, 1.0);
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EventLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EventLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/EventLoop.c	(revision 22322)
@@ -0,0 +1,141 @@
+# include "Ximage.h"
+
+/* list events being selected below, all other masks are ignored */ 
+# define IgnoreMask (long) (~(StructureNotifyMask | SubstructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | PointerMotionMask))
+
+int LastEvent (Display *display, int type, XEvent *event) {
+
+  int found;
+
+  found = FALSE;
+  while (XCheckTypedEvent (display, type, event)) {
+    found = TRUE;
+  }
+  return (found);
+}
+
+int EventLoop () {
+  
+  int      status;
+  XEvent   event;
+  Display *display;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+  display = graphic->display;
+  
+  if (USE_XWINDOW) Refresh (1);
+
+  status = TRUE;
+  while (status) {
+    
+    if (!CheckPipe ()) return (FALSE);
+    
+    if (!USE_XWINDOW) {
+      usleep (50000);
+      continue;
+    }
+
+    if (XEventsQueued (display, QueuedAfterFlush) < 1) {
+      /* fprintf (stderr, "."); */
+      usleep (50000);
+      continue;
+    }
+
+    /* grab the last entry for these events */
+    if (LastEvent (display, ConfigureNotify, &event)) Reconfig (&event);
+    if (LastEvent (display, CirculateNotify, &event)) Reconfig (&event);
+    if (LastEvent (display, Expose,          &event)) Refresh (1);
+    if (LastEvent (display, MappingNotify,   &event)) XRefreshKeyboardMapping ((XMappingEvent *) &event);
+    if (LastEvent (display, MotionNotify,    &event)) UpdatePointer (graphic, (XMotionEvent *) &event);
+    if (LastEvent (display, ButtonPress,     &event)) InterpretPresses (graphic, (XButtonEvent *) &event);
+    if (LastEvent (display, KeyPress,        &event)) InterpretKeys (graphic, (XKeyEvent *) &event);
+
+    /* drop and ignore the following StructureNotifyMask events */
+    LastEvent (display, GravityNotify, &event);
+    LastEvent (display, ReparentNotify, &event);
+    LastEvent (display, MapNotify, &event);
+    LastEvent (display, UnmapNotify, &event);
+
+    /* remove those events we will ignore */
+    while (XCheckMaskEvent (display, IgnoreMask, &event)) continue;
+
+    /* events to remove which have no mask component */
+    while (XCheckTypedEvent (display, MappingNotify, &event)) continue;
+    while (XCheckTypedEvent (display, ClientMessage, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionClear, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionNotify, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionRequest, &event)) continue;
+  }
+  return (status);
+}
+
+# if (0)
+
+/* all masks from X.h for reference: */
+
+#define NoEventMask                     0L
+#define KeyPressMask                    (1L<<0)
+#define KeyReleaseMask                  (1L<<1)
+#define ButtonPressMask                 (1L<<2)
+#define ButtonReleaseMask               (1L<<3)
+#define EnterWindowMask                 (1L<<4)
+#define LeaveWindowMask                 (1L<<5)
+#define PointerMotionMask               (1L<<6)
+#define PointerMotionHintMask           (1L<<7)
+#define Button1MotionMask               (1L<<8)
+#define Button2MotionMask               (1L<<9)
+#define Button3MotionMask               (1L<<10)
+#define Button4MotionMask               (1L<<11)
+#define Button5MotionMask               (1L<<12)
+#define ButtonMotionMask                (1L<<13)
+#define KeymapStateMask                 (1L<<14)
+#define ExposureMask                    (1L<<15)
+#define VisibilityChangeMask            (1L<<16)
+#define StructureNotifyMask             (1L<<17)
+#define ResizeRedirectMask              (1L<<18)
+#define SubstructureNotifyMask          (1L<<19)
+#define SubstructureRedirectMask        (1L<<20)
+#define FocusChangeMask                 (1L<<21)
+#define PropertyChangeMask              (1L<<22)
+#define ColormapChangeMask              (1L<<23)
+#define OwnerGrabButtonMask             (1L<<24)
+
+/* all events from X.h for reference: */
+
+#define KeyPress                2
+#define KeyRelease              3
+#define ButtonPress             4
+#define ButtonRelease           5
+#define MotionNotify            6
+#define EnterNotify             7
+#define LeaveNotify             8
+#define FocusIn                 9
+#define FocusOut                10
+#define KeymapNotify            11
+#define Expose                  12
+#define GraphicsExpose          13
+#define NoExpose                14
+#define VisibilityNotify        15
+#define CreateNotify            16
+#define DestroyNotify           17
+#define UnmapNotify             18
+#define MapNotify               19
+#define MapRequest              20
+#define ReparentNotify          21
+#define ConfigureNotify         22
+#define ConfigureRequest        23
+#define GravityNotify           24
+#define ResizeRequest           25
+#define CirculateNotify         26
+#define CirculateRequest        27
+#define PropertyNotify          28
+#define SelectionClear          29
+#define SelectionRequest        30
+#define SelectionNotify         31
+#define ColormapNotify          32
+#define ClientMessage           33
+#define MappingNotify           34
+#define LASTEvent               35      /* must be bigger than any event # */
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/FlushDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/FlushDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/FlushDisplay.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "Ximage.h"
+
+static struct timeval reftime; 
+static char reftimeset = FALSE;
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+void FlushDisplay () {
+
+  struct timeval now;
+  int flush;
+  double dtime;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  if (!USE_XWINDOW) return;
+
+  flush = FALSE;
+  if (!reftimeset) {
+    flush = TRUE;
+    gettimeofday (&reftime, NULL);
+  } 
+
+  gettimeofday (&now, NULL);
+  dtime = DTIME (now, reftime);
+
+  if (dtime > 0.1) {
+    flush = TRUE;
+  }
+
+  if (flush) {
+    XFlush (graphic->display);
+    reftime = now;
+  }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/GetColor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/GetColor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/GetColor.c	(revision 22322)
@@ -0,0 +1,16 @@
+# include "Ximage.h"
+
+/************** GetColor *************/
+unsigned long GetColor (Display *display, char *name, Colormap colormap, unsigned long default_color) {
+
+  int status;
+  XColor rgbcolor, hardwarecolor;
+
+  status = XLookupColor (display, colormap, name, &rgbcolor, &hardwarecolor);
+  if (!status) return (default_color);
+
+  status = XAllocColor (display, colormap, &hardwarecolor);
+  if (!status) return (default_color);
+
+  return (hardwarecolor.pixel);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/GetPixelCount.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/GetPixelCount.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/GetPixelCount.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include "Ximage.h"
+
+int GetPixelCount (int sock) {
+  
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  KiiSendMessage (sock, "NPIX: %8d", graphic->Npixels);
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Graphs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Graphs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Graphs.c	(revision 22322)
@@ -0,0 +1,120 @@
+# include "Ximage.h"
+
+/* initialize graph data */
+KapaGraphWidget *InitGraph () {
+
+  int i;
+  KapaGraphWidget *graph;
+
+  ALLOCATE (graph, KapaGraphWidget, 1);
+
+  /* set up axis positions */
+  for (i = 0; i < 4; i++) {
+    graph[0].axis[i].min = 0.0;
+    graph[0].axis[i].max = 1.0;
+    graph[0].axis[i].isaxis = FALSE;
+    graph[0].axis[i].areticks = FALSE;
+    graph[0].axis[i].islabel = FALSE;
+    graph[0].axis[i].lweight = 0.0;
+    graph[0].axis[i].color = 0;
+  }    
+  for (i = 0; i < 8; i++) {
+    strcpy (graph[0].label[i].text, "");
+  }
+
+  graph[0].data.xmin = 0.0;
+  graph[0].data.xmax = 1.0;
+  graph[0].data.ymin = 0.0;
+  graph[0].data.ymax = 1.0;
+
+  graph[0].data.style 	= 2; 		// points
+  graph[0].data.ptype 	= 2;		// + for points
+  graph[0].data.ltype 	= 0;		// solid line
+  graph[0].data.etype 	= 0;		// no error bars
+  graph[0].data.ebar  	= 0;		// no cross bar
+  graph[0].data.color 	= 0;		// black
+  graph[0].data.lweight = 0.5;		// line weight of 0.5
+  graph[0].data.size    = 1.0;		// point size of 1.0
+
+  graph[0].Nobjects = 0;
+  graph[0].Ntextline = 0;
+
+  ALLOCATE (graph[0].objects, Gobjects, 1);  /* allocate so later free will not crash! */
+  ALLOCATE (graph[0].textline, Label, 1);    /* allocate so later free will not crash! */
+
+  graph[0].objects[0].x   = graph[0].objects[0].y   = graph[0].objects[0].z = NULL;
+  graph[0].objects[0].dxm = graph[0].objects[0].dxp = NULL;
+  graph[0].objects[0].dym = graph[0].objects[0].dyp = NULL;
+
+  return (graph);
+}
+
+void DrawGraph (KapaGraphWidget *graph) {
+  if (graph == NULL) return;
+  DrawFrame    (graph);
+  DrawObjects  (graph);
+  DrawLabels   (graph);
+  DrawTextlines(graph);
+}
+
+/* remove objects */
+void EraseGraph (KapaGraphWidget *graph) {
+
+  int i;
+
+  if (graph == NULL) return;
+
+  /* free data objects, then re-alloc those needed */
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    FREE (graph[0].objects[i].x);
+    FREE (graph[0].objects[i].y);
+    FREE (graph[0].objects[i].z);
+    FREE (graph[0].objects[i].dxm);
+    FREE (graph[0].objects[i].dxp);
+    FREE (graph[0].objects[i].dym);
+    FREE (graph[0].objects[i].dyp);
+  }
+    
+  /* reset axes and labels */
+  for (i = 0; i < 4; i++) {
+    graph[0].axis[i].isaxis = FALSE;
+    graph[0].axis[i].islabel = FALSE;
+    graph[0].axis[i].areticks = FALSE;
+  }
+  for (i = 0; i < 8; i++) {
+    strcpy (graph[0].label[i].text, "");
+  }
+    
+  graph[0].Nobjects = 0;
+  graph[0].Ntextline = 0;
+  REALLOCATE (graph[0].objects, Gobjects, 1);
+  REALLOCATE (graph[0].textline, Label, 1);
+
+  graph[0].objects[0].x   = graph[0].objects[0].y   = graph[0].objects[0].z = NULL;
+  graph[0].objects[0].dxm = graph[0].objects[0].dxp = NULL;
+  graph[0].objects[0].dym = graph[0].objects[0].dyp = NULL;
+}
+
+/* remove objects */
+void FreeGraph (KapaGraphWidget *graph) {
+
+  int i;
+
+  if (graph == NULL) return;
+
+  /* free data objects, then re-alloc those needed */
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    FREE (graph[0].objects[i].x);
+    FREE (graph[0].objects[i].y);
+    FREE (graph[0].objects[i].z);
+    FREE (graph[0].objects[i].dxm);
+    FREE (graph[0].objects[i].dxp);
+    FREE (graph[0].objects[i].dym);
+    FREE (graph[0].objects[i].dyp);
+  }
+    
+  FREE (graph[0].objects);
+  FREE (graph[0].textline);
+  free (graph);
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Image.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Image.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Image.c	(revision 22322)
@@ -0,0 +1,214 @@
+# include "Ximage.h"
+# include "buttons.h"
+
+int InitImageChannel (KapaImageChannel *channel) {
+
+  /** set up a bunch of default things **/
+  channel->zero = 0;
+  channel->range = 1;
+  channel->start = 0;
+  channel->slope = 1;
+
+  channel->coords.Npolyterms = 0;
+  channel->matrix.size = 0; /* a flag to show there is no data in the matrix */
+  ALLOCATE (channel->matrix.buffer, char, 1);  /* allocate so later free will not crash! */
+
+  return (TRUE);
+}
+
+/* initialization for things not specific to X */
+KapaImageWidget *InitImageWidget () {
+
+  int i;
+  KapaImageWidget *image;
+  Graphic *graphic;
+
+  graphic = GetGraphic ();
+
+  ALLOCATE (image, KapaImageWidget, 1);
+  memset (image, 0, sizeof(KapaImageWidget));
+
+  for (i = 0; i < NCHANNELS; i++) {
+    InitImageChannel (&image[0].channel[i]);
+  }
+  image[0].image = &image[0].channel[0];
+
+  image[0].nPixels = 0;
+  ALLOCATE (image[0].pixmap, unsigned short, 1);  /* allocate so later free will not crash! */
+
+  // XXXX this has been moved to graphic, which may be wrong
+  // image[0].ColorScaleMode = KAPA_SCALE_1D;
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    image[0].overlay[i].Nobjects = 0;
+    ALLOCATE (image[0].overlay[i].objects, KiiOverlay, 1);  /* allocate so later free will not crash! */
+    image[0].overlay[i].active = FALSE;
+    image[0].overlay[i].color = graphic[0].overlay_color[i];
+  }
+
+  // set the center and expansion for the pictures:
+  image[0].picture.X      = 0.0;
+  image[0].picture.Y      = 0.0;
+  image[0].picture.expand = 1;
+  image[0].picture.flipx  = FALSE;
+  image[0].picture.flipy  = FALSE;
+
+  image[0].zoom.X      	  = 0.0;
+  image[0].zoom.Y      	  = 0.0;
+  image[0].zoom.expand 	  = +5;
+  image[0].zoom.flipx  	  = FALSE;
+  image[0].zoom.flipy  	  = FALSE;
+
+  image[0].wide.X      	  = 0.0;
+  image[0].wide.Y      	  = 0.0;
+  image[0].wide.expand 	  = -5;
+  image[0].wide.flipx  	  = FALSE;
+  image[0].wide.flipy  	  = FALSE;
+
+  image[0].location = 4;
+
+  image[0].MovePointer = TRUE;
+  image[0].DecimalDegrees  = TRUE;
+  ALLOCATE (image[0].picture.data, char, 1);   /* allocate so later free will not crash! */
+  ALLOCATE (image[0].cmapbar.data, char, 1);   /* allocate so later free will not crash! */
+  ALLOCATE (image[0].zoom.data, char, 1);      /* allocate so later free will not crash! */
+  ALLOCATE (image[0].wide.data, char, 1);      /* allocate so later free will not crash! */
+
+  InitButtonSize (&image[0].PS_button, PS_width, PS_height, PS_bits);
+  InitButtonFunc (&image[0].PS_button, PSfunction);
+
+  InitButtonSize (&image[0].grey_button, grey_width, grey_height, grey_bits);
+  InitButtonFunc (&image[0].grey_button, greycolors);
+
+  InitButtonSize (&image[0].rainbow_button, rainbow_width, rainbow_height, rainbow_bits);
+  InitButtonFunc (&image[0].rainbow_button, rainbow);
+
+  InitButtonSize (&image[0].heat_button, heat_width, heat_height, heat_bits);
+  InitButtonFunc (&image[0].heat_button, heat);
+
+  InitButtonSize (&image[0].recenter_button, recenter_width, recenter_height, recenter_bits);
+  image[0].recenter_button.function_1 = Recenter;
+  image[0].recenter_button.function_2 = RecenterRescale;
+  image[0].recenter_button.function_3 = Rescale;
+
+  InitButtonSize (&image[0].overlay_button[0], red_width, red_height, red_bits);
+  InitButtonFunc (&image[0].overlay_button[0], Overlay0);
+
+  InitButtonSize (&image[0].overlay_button[1], green_width, green_height, green_bits);
+  InitButtonFunc (&image[0].overlay_button[1], Overlay1);
+
+  InitButtonSize (&image[0].overlay_button[2], blue_width, blue_height, blue_bits);
+  InitButtonFunc (&image[0].overlay_button[2], Overlay2);
+
+  InitButtonSize (&image[0].overlay_button[3], yellow_width, yellow_height, yellow_bits);
+  InitButtonFunc (&image[0].overlay_button[3], Overlay3);
+
+  InitButtonSize (&image[0].hms_button, hms_width, hms_height, hms_bits);
+  InitButtonFunc (&image[0].hms_button, ToggleDEG);
+
+  InitButtonSize (&image[0].flipx_button, flipx_width, flipx_height, flipx_bits);
+  InitButtonFunc (&image[0].flipx_button, FlipImageX);
+
+  InitButtonSize (&image[0].flipy_button, flipy_width, flipy_height, flipy_bits);
+  InitButtonFunc (&image[0].flipy_button, FlipImageY);
+
+  return (image);
+}
+
+void InitButtonSize (Button *button, int width, int height, char *bitmap) {
+  button->dx = BUTTON_WIDTH;
+  button->dy = BUTTON_HEIGHT;
+  button->width = width;
+  button->height = height;
+  button->bitmap = bitmap;
+}
+
+void InitButtonFunc (Button *button, int (*function)()) {
+  button->function_1 = function;
+  button->function_2 = function;
+  button->function_3 = function;
+}
+
+void DrawImage (KapaImageWidget *image) {
+
+  int i;
+  Graphic *graphic;
+
+  if (image == NULL) return;
+
+  graphic = GetGraphic ();
+
+  XSetForeground (graphic[0].display,  graphic[0].gc, graphic[0].fore);
+  XDrawRectangle (graphic[0].display,  graphic[0].window, graphic[0].gc, 
+		  image[0].picture.x,  image[0].picture.y, 
+		  image[0].picture.dx+1, image[0].picture.dy+1);
+  
+  XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	     image[0].picture.pix, 0, 0, 
+	     image[0].picture.x + 1, image[0].picture.y + 1, 
+	     image[0].picture.dx, image[0].picture.dy);
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (image[0].overlay[i].active) {
+      PaintOverlay (graphic, image, i);
+    }
+  }
+
+  if (image[0].location) {
+    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	       image[0].cmapbar.pix, 0, 0, 
+	       image[0].cmapbar.x, image[0].cmapbar.y, 
+	       image[0].cmapbar.dx, image[0].cmapbar.dy);
+
+    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	       image[0].wide.pix, 0, 0, 
+	       image[0].wide.x, image[0].wide.y, 
+	       image[0].wide.dx, image[0].wide.dy);
+
+    CrossHairs (graphic, &image[0].zoom);
+  
+    /* erase everything below zoom box, then draw */
+    /*
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		    image[0].text_x, image[0].text_x, image[0].text_dx, image[0].text_dy); 
+    */    
+    DrawButton (graphic, &image[0].PS_button);
+    DrawButton (graphic, &image[0].recenter_button);
+    DrawButton (graphic, &image[0].grey_button);
+    DrawButton (graphic, &image[0].rainbow_button);
+    DrawButton (graphic, &image[0].heat_button);
+    DrawButton (graphic, &image[0].hms_button);
+
+    DrawButton (graphic, &image[0].flipx_button);
+    DrawButton (graphic, &image[0].flipy_button);
+
+    for (i = 0; i < NOVERLAYS; i++) {
+      DrawButton (graphic, &image[0].overlay_button[i]);
+    }
+    StatusBox (graphic, image);
+  }
+
+  FlushDisplay (graphic[0].display);
+}
+
+void FreeImage (KapaImageWidget *image) {
+
+  int i;
+
+  if (image == NULL) return;
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    free (image[0].overlay[i].objects);
+  }
+  for (i = 0; i < NCHANNELS; i++) {
+    free (image[0].channel[i].matrix.buffer);
+  }
+  free (image[0].pixmap);
+  free (image[0].picture.data);
+  free (image[0].cmapbar.data);
+  free (image[0].zoom.data);
+  free (image[0].wide.data);
+
+  free (image);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InButton.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InButton.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InButton.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "Ximage.h"
+
+int InButton (XButtonEvent *button_event, Button *button) {
+
+  int answer;
+  int x, y;
+
+  x = button_event[0].x;
+  y = button_event[0].y;
+  
+  answer = ((x >= button[0].x) && (x <= button[0].x + button[0].dx) &&
+	    (y >= button[0].y) && (y <= button[0].y + button[0].dy));
+
+  return (answer);
+
+}
+
+/*** make this a macro? ***/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InPicture.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InPicture.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InPicture.c	(revision 22322)
@@ -0,0 +1,16 @@
+# include "Ximage.h"
+
+int InPicture (XButtonEvent *button_event, Picture *picture) {
+
+  int answer;
+  int x, y;
+
+  x = button_event[0].x;
+  y = button_event[0].y;
+  
+  answer = ((x >= picture[0].x) && (x <= picture[0].x + picture[0].dx) &&
+	    (y >= picture[0].y) && (y <= picture[0].y + picture[0].dy));
+
+  return (answer);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InterpretKeys.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InterpretKeys.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InterpretKeys.c	(revision 22322)
@@ -0,0 +1,183 @@
+# include "Ximage.h"
+
+int InterpretKeys (Graphic *graphic, XKeyEvent *event) {
+
+  float           *imdata;
+  double 	   X, Y, Z, R, D, offset;
+  int    	   sock, DX, DY, modstate;
+  char   	  *name, string[16];
+  KeySym           keysym;
+  XComposeStatus   composestatus;
+  Section         *section;
+  KapaImageWidget *image;
+  KapaGraphWidget *graph;
+
+  // XXX select the window element which contains the event
+  section = GetActiveSection();
+  image   = section->image;
+  graph   = section->graph;
+
+  XLookupString (event, string, 9, &keysym, &composestatus);
+  modstate = event[0].state;
+
+  // return graph coords by default, image if graph does not exist
+  // XXX allow user to choose graph or image coords
+  if (ACTIVE_CURSOR) {
+
+    sock = GetActiveSocket ();
+    if (sock == -1) goto skip_cursor;
+
+    name = XKeysymToString (keysym);
+
+    // skip the following keys: 
+    if (name == NULL) goto skip_cursor;
+    if (!strcmp (name, "Shift_L")) goto skip_cursor;
+    if (!strcmp (name, "Shift_R")) goto skip_cursor;
+    if (!strcmp (name, "Control_L")) goto skip_cursor;
+    if (!strcmp (name, "Control_R")) goto skip_cursor;
+    if (!strcmp (name, "Alt_L")) goto skip_cursor;
+    if (!strcmp (name, "Alt_R")) goto skip_cursor;
+    if (!strcmp (name, "Super_L")) goto skip_cursor;
+    if (!strcmp (name, "Super_R")) goto skip_cursor;
+    if (!strcmp (name, "Caps_Lock")) goto skip_cursor;
+    if (!strcmp (name, "Pause")) goto skip_cursor;
+    if (!strcmp (name, "Continue")) goto skip_cursor;
+    if (!strcmp (name, "Num_Lock")) goto skip_cursor;
+    if (!strcmp (name, "Scroll_Lock")) goto skip_cursor;
+    if (!strcmp (name, "Print")) goto skip_cursor;
+    if (!strcmp (name, "(null)")) goto skip_cursor;
+
+    Z = -1;
+
+    if (graph) {
+      X = (event[0].x - graph[0].axis[0].fx)*(graph[0].axis[0].max - graph[0].axis[0].min)/graph[0].axis[0].dfx + graph[0].axis[0].min;
+      Y = (event[0].y - graph[0].axis[1].fy)*(graph[0].axis[1].max - graph[0].axis[1].min)/graph[0].axis[1].dfy + graph[0].axis[1].min;
+      XY_to_RD (&R, &D, X, Y, &graph[0].data.coords);
+    } 
+    if (image && !graph) {
+      if (event[0].x < image[0].picture.x) goto skip_cursor;
+      if (event[0].y < image[0].picture.y) goto skip_cursor;
+      if (event[0].x > image[0].picture.x + image[0].picture.dx) goto skip_cursor;
+      if (event[0].y > image[0].picture.y + image[0].picture.dy) goto skip_cursor;
+      Screen_to_Image (&X, &Y, (double)event[0].x, (double)event[0].y, &image[0].picture);
+      XY_to_RD (&R, &D, X, Y, &image[0].image[0].coords);
+
+      DX = image[0].image[0].matrix.Naxis[0];
+      DY = image[0].image[0].matrix.Naxis[1];
+
+      if (X < 0) goto off_image;
+      if (Y < 0) goto off_image;
+      if (X >= DX) goto off_image;
+      if (Y >= DY) goto off_image;
+      imdata = (float *) image[0].image[0].matrix.buffer;
+      Z      = imdata[DX*(int)(Y) + (int)(X)];
+    }
+  off_image:
+    KiiSendMessage (sock, "%12s %12.6f %12.6f %12.6f %12.6f %12.6f", name, X, Y, Z, R, D);
+  }
+
+skip_cursor:
+  if (image == NULL) return (TRUE);
+
+  // offset is in image pixels: 
+  // 0.5 image pixels is 1 screen pixel for expand == +2
+  // 2.0 image pixels is 1 screen pixel for expand == -2
+  if (image[0].picture.expand == 0) image[0].picture.expand = 1;
+  offset = (image[0].picture.expand > 0) ? 1.0 / image[0].picture.expand : -image[0].picture.expand;
+  if (modstate & ControlMask) offset *= 100;
+
+  switch (keysym) {
+
+    case XK_F1:
+      image[0].image = &image[0].channel[0];
+      SetColorScale (graphic, image);
+      Remap (graphic, image);
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+
+    case XK_F2:
+      image[0].image = &image[0].channel[1];
+      SetColorScale (graphic, image);
+      Remap (graphic, image);
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+
+    case XK_F3:
+      image[0].image = &image[0].channel[2];
+      SetColorScale (graphic, image);
+      Remap (graphic, image);
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+
+    case XK_KP_Home:
+    case XK_Home:
+      image[0].picture.expand = 1;
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+    case XK_KP_End:
+    case XK_End:
+      image[0].picture.expand = 1;
+      Reorient (graphic, image, 0.5*image[0].image[0].matrix.Naxis[0], 0.5*image[0].image[0].matrix.Naxis[1], 0);
+      break;
+    case XK_KP_Enter:
+    case XK_KP_Begin:
+    case XK_Return:
+      Screen_to_Image (&X, &Y, event[0].x + 0.5, event[0].y + 0.5, &image[0].picture);
+      Reorient (graphic, image, X, Y, 0);
+      break;
+    case XK_Prior:
+    case XK_KP_Prior:
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, +1);
+      break;
+    case XK_Next:
+    case XK_KP_Next:
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, -1);
+      break;
+    case XK_Up:
+    case XK_KP_Up:
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y + offset, 0);
+      break;
+    case XK_Down:
+    case XK_KP_Down:
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y - offset, 0);
+      break;
+    case XK_Left:
+    case XK_KP_Left:
+      Reorient (graphic, image, image[0].picture.X + offset, image[0].picture.Y, 0);
+      break;
+    case XK_Right:
+    case XK_KP_Right:
+      Reorient (graphic, image, image[0].picture.X - offset, image[0].picture.Y, 0);
+      break;
+
+    case XK_KP_Add:
+      if (modstate & ControlMask) {
+	  image[0].image[0].zero -= 0.05*image[0].image[0].range;
+	  image[0].image[0].range *= 1.1;
+      } else {
+	  image[0].image[0].zero += 0.1*image[0].image[0].range;
+      }
+      SetColorScale (graphic, image);
+      Remap (graphic, image);
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+    case XK_KP_Subtract:
+      if (modstate & ControlMask) {
+	  image[0].image[0].zero += 0.05*image[0].image[0].range;
+	  image[0].image[0].range *= 0.90;
+      } else {
+	  image[0].image[0].zero -= 0.1*image[0].image[0].range;
+      }
+      SetColorScale (graphic, image);
+      Remap (graphic, image);
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+
+    case XK_Tab:
+      image[0].MovePointer = image[0].MovePointer ^ TRUE;
+      if (image[0].MovePointer) UpdatePointer (graphic, (XMotionEvent *)event);
+      break;
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InterpretPresses.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InterpretPresses.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InterpretPresses.c	(revision 22322)
@@ -0,0 +1,118 @@
+# include "Ximage.h"
+
+int InterpretPresses (Graphic *graphic, XButtonEvent *event) {
+
+  int              sock, DX, DY, status, done, this_button;
+  char             name[16];
+  double           X, Y, Z, R, D;
+  float           *imdata;
+  Button          *button;
+  Section         *section;
+  KapaImageWidget *image;
+  KapaGraphWidget *graph;
+
+  // XXX select the window element which contains the event
+  section = GetActiveSection();
+  image   = section->image;
+  graph   = section->graph;
+
+  // return graph coords by default, image if graph does not exist
+  // XXX allow user to choose graph or image coords
+  if (ACTIVE_CURSOR) {
+    sock = GetActiveSocket ();
+    if (sock == -1) goto skip_cursor;
+
+    sprintf (name, "Button%d", event[0].button);
+    if (graph) {
+      X = (event[0].x - graph[0].axis[0].fx)*(graph[0].axis[0].max - graph[0].axis[0].min)/graph[0].axis[0].dfx + graph[0].axis[0].min;
+      Y = (event[0].y - graph[0].axis[1].fy)*(graph[0].axis[1].max - graph[0].axis[1].min)/graph[0].axis[1].dfy + graph[0].axis[1].min;
+      XY_to_RD (&R, &D, X, Y, &graph[0].data.coords);
+    } 
+    if (image && !graph) {
+      if (event[0].x < image[0].picture.x) goto skip_cursor;
+      if (event[0].y < image[0].picture.y) goto skip_cursor;
+      if (event[0].x > image[0].picture.x + image[0].picture.dx) goto skip_cursor;
+      if (event[0].y > image[0].picture.y + image[0].picture.dy) goto skip_cursor;
+      Screen_to_Image (&X, &Y, event[0].x + 0.5, event[0].y + 0.5, &image[0].picture);
+
+      XY_to_RD (&R, &D, X, Y, &image[0].image[0].coords);
+
+      DX = image[0].image[0].matrix.Naxis[0];
+      DY = image[0].image[0].matrix.Naxis[1];
+
+      if (X < 0) goto off_image;
+      if (Y < 0) goto off_image;
+      if (X >= DX) goto off_image;
+      if (Y >= DY) goto off_image;
+      imdata = (float *) image[0].image[0].matrix.buffer;
+      Z      = imdata[DX*(int)(Y) + (int)(X)];
+    }
+  off_image:
+    KiiSendMessage (sock, "%12s %12.6f %12.6f %12.6f %12.6f %12.6f", name, X, Y, Z, R, D);
+  }
+
+skip_cursor:
+  status = TRUE;
+  this_button = event[0].button;
+
+  // XXX add graph buttons here
+  if (image == NULL) return (TRUE);
+  
+  if ((event[0].type == ButtonPress) && InPicture (event, &image[0].picture)) {
+    ReorientOnButton (graphic, image, event);
+  }
+
+  if ((event[0].type == ButtonPress) && InPicture (event, &image[0].cmapbar)) {
+    DragColorbar (graphic, image, event);
+  }
+
+  /* if on an exisiting button, Invert, wait for release, then go (or not) */
+  if ((button = CheckButtons (event, image)) != (Button *) NULL) {
+    InvertButton (graphic, button); 
+    done = FALSE;
+    while (!done) { /* wait for release of this button */
+      XNextEvent (graphic[0].display, (XEvent *) event);
+      if ((event[0].type == ButtonRelease) && (event[0].button == this_button)) {
+	done = TRUE;
+      }
+    }
+    DrawButton (graphic, button);
+    if (InButton (event, button)) {
+      switch (event[0].button) {
+      case 1:
+	status = button[0].function_1(graphic, image);
+	break;
+      case 2:
+	status = button[0].function_2(graphic, image);
+	break;
+      case 3:
+	status = button[0].function_3(graphic, image);
+	break;
+      }
+    } else {
+      return (status);
+    }
+  }
+  return (status);
+}
+
+void ReorientOnButton (Graphic *graphic, KapaImageWidget *image, XButtonEvent *mouse_event) {
+
+  double X, Y;
+
+  Screen_to_Image (&X, &Y, mouse_event[0].x + 0.5, mouse_event[0].y + 0.5, &image[0].picture);
+
+  switch (mouse_event[0].button) {
+    case 1:
+      Reorient (graphic, image, X, Y, 0);
+      break;
+    case 2:
+      Reorient (graphic, image, X, Y, -1);
+      break;
+    case 3:
+      Reorient (graphic, image, X, Y, +1);
+      break;
+    default:
+      break;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InvertButton.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InvertButton.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/InvertButton.c	(revision 22322)
@@ -0,0 +1,19 @@
+# include "Ximage.h"
+
+void InvertButton (Graphic *graphic, Button *button) {
+
+  unsigned long fore, back;
+
+  fore =  graphic[0].fore;
+  back =  graphic[0].back;
+
+  graphic[0].fore = back;
+  graphic[0].back = fore;
+
+  DrawButton (graphic, button);
+  FlushDisplay (graphic[0].display);
+  
+  graphic[0].fore = fore;
+  graphic[0].back = back;
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/JPEGit24.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/JPEGit24.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/JPEGit24.c	(revision 22322)
@@ -0,0 +1,216 @@
+# include "Ximage.h"
+# include "jpeglib.h"
+
+# define WHITE_R 255
+# define WHITE_G 255
+# define WHITE_B 255
+
+// XXX this currently writes out the jpeg for just the active image
+int JPEGit24 (int sock) {
+
+  struct jpeg_compress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
+  JSAMPLE *image_buffer;	/* Points to data for current line */
+  JSAMPLE *line_buffer;	        /* Points to data for current line */
+  Section *section;
+  Graphic *graphic;
+  KapaImageWidget *image;
+
+  int ii, i, j;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  int quality;
+  int expand_in, expand_out;
+  double expand, Ix, Iy;
+  unsigned char *out_pix;
+  unsigned short *in_pix, *in_pix_ref;
+  unsigned char *pixel1, *pixel2, *pixel3;
+  char filename[1024];
+  FILE *f;
+
+  /* expect a line telling the number of bytes and a filename */
+  KiiScanMessage (sock, "%s", filename);
+
+  graphic = GetGraphic();
+  section = GetActiveSection();
+  image   = section->image;
+  if (image == NULL) return (TRUE);
+
+  /***** JPEG init calls */
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_compress (&cinfo);
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "Kii: failed to open %s for output\n", filename);
+    return (TRUE);
+  }
+  jpeg_stdio_dest(&cinfo, f);
+  
+  quality = 75;
+  cinfo.image_width = image[0].picture.dx; 	/* image width and height, in pixels */
+  cinfo.image_height = image[0].picture.dy;
+# ifdef GREYSCALE
+  cinfo.input_components = 1;		        /* # of color components per pixel */
+  cinfo.in_color_space = JCS_GRAYSCALE; 	/* colorspace of input image */
+# else 
+  cinfo.input_components = 3;		        
+  cinfo.in_color_space = JCS_RGB; 	
+# endif
+  jpeg_set_defaults (&cinfo);
+  jpeg_set_quality (&cinfo, quality, TRUE       /* limit to baseline-JPEG values */);
+  jpeg_start_compress (&cinfo, TRUE);
+
+  ALLOCATE (pixel1, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixel2, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixel3, unsigned char, graphic[0].Npixels);
+
+  /** cmap[i].pixel must be defined even if X is not used **/
+  for (i = 0; i < graphic[0].Npixels; i++) { /* set up pixel array */
+    pixel1[i] = graphic[0].cmap[i].red >> 8;
+    pixel2[i] = graphic[0].cmap[i].green >> 8;
+    pixel3[i] = graphic[0].cmap[i].blue >> 8;
+  }
+
+  assert ((image[0].picture.expand >= 1) || (image[0].picture.expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (image[0].picture.expand > 0) {
+    expand = 1 / (1.0*image[0].picture.expand);
+    expand_out = image[0].picture.expand;
+    expand_in  = 1;
+  }
+  if (image[0].picture.expand < 0) {
+    expand = fabs((double)image[0].picture.expand);
+    expand_out = 1;
+    expand_in  = -image[0].picture.expand;
+  }
+
+  dx = image[0].picture.dx;
+  dy = image[0].picture.dy;
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, &image[0].image[0].matrix, &image[0].picture);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, &image[0].image[0].matrix, &image[0].picture);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, &image[0].picture);
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  /* output line buffer */
+  ALLOCATE (image_buffer, JSAMPLE, 3*dx*dy);
+  ALLOCATE (line_buffer, JSAMPLE, 3*dx);
+
+  in_pix_ref  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  out_pix = line_buffer;
+  for (i = 0; i < dx; i++, out_pix+=3) {
+    out_pix[0] = WHITE_R;
+    out_pix[1] = WHITE_G;
+    out_pix[2] = WHITE_B;
+  }
+  for (j = 0; j < j_start; j++) {
+    memcpy (&image_buffer[j*3*dx], line_buffer, 3*dx);
+  }
+  
+  /*** fill in the image data region ***/
+  for (j = j_start; j < j_end; j+= expand_out, in_pix_ref += expand_in*DX) {
+    
+    /* create one output image line */
+    in_pix = in_pix_ref;
+    out_pix = line_buffer;
+
+    /**** fill in area to the left of the picture ****/
+    for (i = 0; i < i_start; i++, out_pix+=3) {
+      out_pix[0] = WHITE_R;
+      out_pix[1] = WHITE_G;
+      out_pix[2] = WHITE_B;
+    }
+    
+    /*** fill in the picture region ***/
+    for (i = i_start; i < i_end; i+=expand_out, in_pix+=expand_in) {
+      for (ii = 0; ii < expand_out; ii++, out_pix+=3) {
+	out_pix[0] = pixel1[*in_pix];
+	out_pix[1] = pixel2[*in_pix];
+	out_pix[2] = pixel3[*in_pix];
+      }
+    }
+    
+    /**** fill in area to the right of the picture ****/
+    for (i = i_end; i < dx; i++, out_pix+=3) {
+      out_pix[0] = WHITE_R;
+      out_pix[1] = WHITE_G;
+      out_pix[2] = WHITE_B;
+    }
+
+    /* write out the image line expand_out times */
+    for (i = 0; i < expand_out; i++) {
+      memcpy (&image_buffer[(j + i)*3*dx], line_buffer, 3*dx);
+    }
+  }
+
+  /**** fill in top area ****/
+  out_pix = line_buffer;
+  for (i = 0; i < dx; i++, out_pix+=3) { 
+    out_pix[0] = WHITE_R;
+    out_pix[1] = WHITE_G;
+    out_pix[2] = WHITE_B;
+  }
+  for (j = j_end; j < dy; j++) {
+    memcpy (&image_buffer[j*3*dx], line_buffer, 3*dx);
+  }
+
+
+  /* I need to write the overlay objects on the jpeg image.
+     if i can write / overwrite data in jpeg buffer, then do it here,
+     otherwise i need to create a temporary image buffer, then write the 
+     scanlines to that buffer */
+
+  {
+    int Npalette;
+    png_color *palette;
+    bDrawColor white, color;
+    bDrawBuffer *buffer;
+
+    palette = KapaPNGPalette (&Npalette);
+
+    buffer = bDrawBufferCreate (dx, dy);
+    bDrawSetBuffer (buffer);
+    for (i = 0; i < NOVERLAYS; i++) {
+      if (image[i].overlay[i].active) bDrawOverlay (image, i);
+    }
+
+    white = KapaColorByName ("white");
+    for (j = 0; j < dy; j++) {
+      for (i = 0; i < dx; i++) {
+	color = buffer[0].pixels[j][i];
+	if (color == white) continue;
+	image_buffer[j*3*dx + 3*i + 0] = palette[color].red;
+	image_buffer[j*3*dx + 3*i + 1] = palette[color].green;
+	image_buffer[j*3*dx + 3*i + 2] = palette[color].blue;
+      }
+    }
+    bDrawBufferFree (buffer);
+  }
+
+  for (i = 0; i < dy; i++) {
+    row_pointer[0] = &image_buffer[i*3*dx];
+    (void) jpeg_write_scanlines (&cinfo, row_pointer, 1);
+  }
+
+  jpeg_finish_compress (&cinfo);
+  fclose (f);
+  jpeg_destroy_compress (&cinfo);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Layout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Layout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Layout.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "Ximage.h"
+static char default_colormap[] = "grayscale";
+
+void InitLayout (int argc, char **argv) {
+
+  int N;
+  Section *section;
+  char *namedSocket = NULL;
+  
+  namedSocket = NULL;
+  if ((N = get_argument (argc, argv, "-socket"))) { 
+    remove_argument (N, &argc, argv);
+    namedSocket = argv[N];
+    remove_argument (N, &argc, argv);
+  }
+
+  // if we specify a named socket, wait until we are contacted
+  // otherwise, open an INET sock and check occasionally for requests
+  InitPipe (namedSocket);
+
+  ACTIVE_CURSOR = FALSE;
+
+  /* get the display colors */
+  if (USE_XWINDOW) {
+    MakeColormap (argc, argv);
+  } else {
+    char *colormap;
+    colormap = default_colormap;
+    if ((N = get_argument (argc, argv, "-cm"))) {
+      remove_argument (N, &argc, argv);
+      colormap = argv[N];
+    }
+    SetColormap (colormap);
+  }
+
+  /* move this out of here... */
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: kapa\n");
+    exit (0);
+  }
+
+  InitRotFonts ();
+
+  /* create basic section, empty of image or graph */
+  section = AddSection ("default", 0.0, 0.0, 1.0, 1.0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadFont.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadFont.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadFont.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "Ximage.h"
+
+/************** LoadFont *************/
+void LoadFont (Graphic *graphic, int *argc, char **argv, char *default_name) {
+
+  int N;
+  char *name;
+
+  name = XGetDefault (graphic->display, argv[0], "Font");
+  if (name == NULL) name = default_name;
+
+  /* check for command-line options */
+  if ((N = get_argument (*argc, argv, "-font"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is -font fontname\n");
+      exit (0);
+    }
+    remove_argument(N, argc, argv);
+    name = argv[N];
+    remove_argument(N, argc, argv);
+  }   
+  if ((N = get_argument (*argc, argv, "-fn"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is -fn fontname\n");
+      exit (0);
+    }
+    remove_argument(N, argc, argv);
+    name = argv[N];
+    remove_argument(N, argc, argv);
+  } 
+
+  graphic->font = XLoadQueryFont (graphic->display, name);
+  if ((graphic->font == NULL) && (name != default_name)) {
+    fprintf (stderr, "Could not load requested font %s, trying %s\n", name, default_name);
+    graphic->font = XLoadQueryFont (graphic->display, default_name);
+  }
+  if (graphic->font == NULL) {
+    fprintf (stderr, "Could not load font %s, using %s\n", default_name, "fixed");
+    graphic->font = XLoadQueryFont (graphic->display, "fixed");
+  }
+  if (graphic->font == NULL) {
+    QuitX (graphic->display, "Error: could not load font");
+  }
+
+  XSetFont (graphic->display, graphic->gc, graphic->font[0].fid);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadFrame.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadFrame.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadFrame.c	(revision 22322)
@@ -0,0 +1,91 @@
+# include "Ximage.h"
+
+int LoadFrame (int sock) {
+  
+  int i, color;
+  char Axis[16], Labels[16], Ticks[16];
+  double lweight;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf", 
+		  &graph[0].axis[0].min, &graph[0].axis[0].max, 
+		  &graph[0].axis[1].min, &graph[0].axis[1].max);
+
+  graph[0].axis[3].min = graph[0].axis[1].min;
+  graph[0].axis[3].max = graph[0].axis[1].max;
+  graph[0].axis[2].min = graph[0].axis[0].min;
+  graph[0].axis[2].max = graph[0].axis[0].max;
+  
+  KiiScanMessage (sock, "%lf %d", &lweight, &color);
+  color = MAX (0, MIN (15, color));
+
+  KiiScanMessage (sock, "%s %s %s", Axis, Labels, Ticks);
+
+  for (i = 0; i < 4; i++) {
+    graph[0].axis[i].lweight = lweight;
+    graph[0].axis[i].color = color;
+
+    switch (Axis[i]) {
+    case '0':
+      graph[0].axis[i].isaxis = FALSE;
+      break;
+    case '1':
+      graph[0].axis[i].isaxis = TRUE;
+      break;
+    case '2':
+      graph[0].axis[i].isaxis = TRUE;
+      break;
+    }
+    switch (Ticks[i]) {
+    case '0':
+      graph[0].axis[i].areticks = FALSE;
+      break;
+    case '1':
+      graph[0].axis[i].areticks = TRUE;
+      break;
+    case '2':
+      graph[0].axis[i].areticks = 2;
+      break;
+    }
+    switch (Labels[i]) {
+    case '0':
+      graph[0].axis[i].islabel = FALSE;
+      break;
+    case '1':
+      graph[0].axis[i].islabel = TRUE;
+      break;
+    case '2':
+      graph[0].axis[i].islabel = (i < 2);
+      break;
+    }
+  }
+
+  SetSectionSizes (section);
+  if (USE_XWINDOW) DrawFrame (graph);
+  FlushDisplay ();
+
+  /* XXX why did I do this??? */
+# if (0)
+  status = TRUE;
+  if (status) {
+    for (i = 0; i < Nsection; i++) {
+      PositionPicture (&section[i]);
+    }
+    if (USE_XWINDOW) XClearWindow (graphic.display, graphic.window);
+    Refresh (1);
+  } else {
+    FlushDisplay ();
+  } 
+# endif
+
+  return (TRUE);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadLabels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadLabels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadLabels.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "Ximage.h"
+
+int LoadLabels (int sock) {
+  
+  char *c, *label;
+  int mode, size, Nbytes;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+  
+  KiiScanMessage (sock, "%d", &mode);
+  label = KiiRecvData (sock);
+
+  if (USE_XWINDOW) EraseLabels (graph);
+  bzero (graph[0].label[mode].text, LABEL_MAXLEN);
+
+  Nbytes = MIN (strlen(label), LABEL_MAXLEN - 1);
+  strncpy (graph[0].label[mode].text, label, Nbytes);
+  label[Nbytes] = 0;
+  
+  c = GetRotFont (&size);
+  graph[0].label[mode].size = size;
+  strcpy (graph[0].label[mode].font, c);
+  if (USE_XWINDOW) DrawLabels (graph);
+  
+  FlushDisplay ();
+  
+  return (TRUE);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadObject.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadObject.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadObject.c	(revision 22322)
@@ -0,0 +1,231 @@
+# include "Ximage.h"
+# include <errno.h>
+# define DEBUG 0
+
+int LoadObject (int sock) {
+  
+  int N;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+  
+  N = graph[0].Nobjects;
+  graph[0].Nobjects ++;
+  REALLOCATE (graph[0].objects, Gobjects, graph[0].Nobjects);
+  graph[0].objects[N].x = graph[0].objects[N].y = graph[0].objects[N].z = (float *) NULL;
+  graph[0].objects[N].dxm = graph[0].objects[N].dxp = (float *) NULL;
+  graph[0].objects[N].dym = graph[0].objects[N].dyp = (float *) NULL;
+  
+  KiiScanMessage (sock, "%d %d %d %d %d %d %d %lf %lf",
+		  &graph[0].objects[N].Npts, &graph[0].objects[N].style, 
+		  &graph[0].objects[N].ptype, &graph[0].objects[N].ltype, 
+		  &graph[0].objects[N].etype, &graph[0].objects[N].ebar, 
+		  &graph[0].objects[N].color, 
+		  &graph[0].objects[N].lweight, &graph[0].objects[N].size);
+  
+  if (DEBUG) fprintf (stderr, "%d %d %d %d %d %d %d %lf %lf\n",
+		      graph[0].objects[N].Npts, graph[0].objects[N].style, 
+		      graph[0].objects[N].ptype, graph[0].objects[N].ltype, 
+		      graph[0].objects[N].etype, graph[0].objects[N].ebar, 
+		      graph[0].objects[N].color, 
+		      graph[0].objects[N].lweight, graph[0].objects[N].size);
+  
+  /* force valid ranges */
+  if ((graph[0].objects[N].color > KapaColormapSize()) || (graph[0].objects[N].color < 0))
+    graph[0].objects[N].color = 0;
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf",
+		  &graph[0].objects[N].x0, &graph[0].objects[N].x1, 
+		  &graph[0].objects[N].y0, &graph[0].objects[N].y1);
+
+  // acknowledge receipt of the metadata
+  KiiSendCommand (sock, 4, "DONE"); 
+  
+  // XXX Currently, I require these in a special order.  The data includes a message defining the
+  // object type.  This could be made more flexible by using the information (though we still need
+  // to know how many items will be sent)
+
+  if (!LoadVectorData (sock, graph, N, "x")) {
+    FreeObjectData (&graph[0].objects[N]);
+    graph[0].Nobjects --;
+    REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+  }
+    
+  if (!LoadVectorData (sock, graph, N, "y")) {
+    FreeObjectData (&graph[0].objects[N]);
+    graph[0].Nobjects --;
+    REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+  }
+  if (graph[0].objects[N].size < 0.0) {
+    if (!LoadVectorData (sock, graph, N, "z")) {
+      FreeObjectData (&graph[0].objects[N]);
+      graph[0].Nobjects --;
+      REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+    }
+  }
+  if (graph[0].objects[N].etype & 0x01) {
+    if (!LoadVectorData (sock, graph, N, "dym")) {
+      FreeObjectData (&graph[0].objects[N]);
+      graph[0].Nobjects --;
+      REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+    }
+    if (!LoadVectorData (sock, graph, N, "dyp")) {
+      FreeObjectData (&graph[0].objects[N]);
+      graph[0].Nobjects --;
+      REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+    }
+  }
+  if (graph[0].objects[N].etype & 0x02) {
+    if (!LoadVectorData (sock, graph, N, "dxm")) {
+      FreeObjectData (&graph[0].objects[N]);
+      graph[0].Nobjects --;
+      REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+    }
+    if (!LoadVectorData (sock, graph, N, "dxp")) {
+      FreeObjectData (&graph[0].objects[N]);
+      graph[0].Nobjects --;
+      REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+    }
+  }
+
+  if (DEBUG) fprintf (stderr, "loaded %d objects, using object %d\n", graph[0].objects[N].Npts, N);
+
+  if (USE_XWINDOW) DrawObjectN (graph, &graph[0].objects[graph[0].Nobjects-1]);
+  FlushDisplay ();
+
+  return (TRUE);
+  
+}
+
+int LoadVectorData (int sock, KapaGraphWidget *graph, int N, char *type) {
+  
+  int i, Npts, Ninpts, status, Ntry;
+  int bytes_left;
+  char *byte, *buffer, type_send[16], tmp;
+  int Npts_send, Nbytes_send, swap_client, swap_host;
+
+  buffer = NULL;
+  Npts = graph[0].objects[N].Npts;
+
+  KiiWaitAnswer (sock, "PLOB");
+  KiiScanMessage (sock, "%s %d %d %d", type_send, &Npts_send, &Nbytes_send, &swap_client);
+  if (strcmp (type, type_send)) {
+    fprintf (stderr, "Kapa Communication error: unexpected data type %s vs %s\n", type_send, type);
+  }
+  if (Npts_send != Npts) {
+    fprintf (stderr, "Kapa Communication error: unexpected number of points %d vs %d\n", Npts_send, Npts);
+  }
+  if (Nbytes_send != Npts_send*sizeof(float)) {
+    fprintf (stderr, "Kapa Communication error: unexpected data size %d vs %d\n", Nbytes_send, Npts_send*sizeof(float));
+  }
+
+  status = 1;
+  if (!strcmp (type, "x")) {
+    ALLOCATE (graph[0].objects[N].x, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].x;
+  }
+  if (!strcmp (type, "y")) {
+    ALLOCATE (graph[0].objects[N].y, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].y;
+  }
+  if (!strcmp (type, "z")) {
+    ALLOCATE (graph[0].objects[N].z, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].z;
+  }
+  if (!strcmp (type, "dxm")) {
+    ALLOCATE (graph[0].objects[N].dxm, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].dxm;
+  }
+  if (!strcmp (type, "dxp")) {
+    ALLOCATE (graph[0].objects[N].dxp, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].dxp;
+  }
+  if (!strcmp (type, "dym")) {
+    ALLOCATE (graph[0].objects[N].dym, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].dym;
+  }
+  if (!strcmp (type, "dyp")) {
+    ALLOCATE (graph[0].objects[N].dyp, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].dyp;
+  }
+
+  bytes_left = Npts*sizeof (float);
+
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+
+  // read the vector data as raw binary in client machine byte order (floats)
+  Ntry = 0;
+  if (DEBUG) fprintf (stderr, "starting vector load\n");
+  Ninpts = 0;
+  byte = buffer;
+  while (bytes_left > 0) {
+    status = read (sock, byte, bytes_left);
+    if (DEBUG) fprintf (stderr, "status: %d, %d\n", status, bytes_left);
+    if (status == 0) {  /* No more pipe */
+      fprintf (stderr, "error: pipe closed\n");
+      return (FALSE);
+    }
+    if (status != -1) { /* pipe has data */
+      Ninpts += status;
+      bytes_left -= status;
+      byte = (char *)(byte + status);
+      Ntry = 0;
+      continue;
+    }
+    if (errno == EAGAIN) {
+      Ntry ++;
+      if (Ntry > 100) {
+	fprintf (stderr, "kapa communication error\n");
+	return (FALSE);
+      }
+      usleep (10000);
+      continue;
+    }
+    perror ("kapa load");
+  }
+
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+  KiiSendCommand (sock, 4, "DONE"); 
+
+# ifdef BYTE_SWAP
+  swap_host = 1;
+# else 
+  swap_host = 0;
+# endif  
+
+  // if host and client have opposite swap parities, word swap the incoming data
+  // SWAP_WORD is strangely defined... it takes the number of the start byte in a
+  // buffer called 'byte'
+  if ((swap_host && !swap_client) || (!swap_host && swap_client)) {
+    byte = buffer;
+    for (i = 0; i < Nbytes_send; i+=4) {
+      SWAP_WORD (i);
+    }
+  }
+
+  if (Ninpts != Npts*sizeof(float)) {  
+    fprintf (stderr, "error: expected %d bytes, but got only %d\n", Ninpts, (unsigned int)(Npts*sizeof(float)));
+    return (FALSE);
+  }
+  if (DEBUG) fprintf (stderr, "done vector load\n");
+  return (TRUE);
+
+}
+
+void FreeObjectData (Gobjects *object) {
+
+  if (object[0].x != (float *) NULL) free (object[0].x);
+  if (object[0].y != (float *) NULL) free (object[0].y);
+  if (object[0].z != (float *) NULL) free (object[0].z);
+
+  if (object[0].dxm != (float *) NULL) free (object[0].dxm);
+  if (object[0].dxp != (float *) NULL) free (object[0].dxp);
+  if (object[0].dym != (float *) NULL) free (object[0].dym);
+  if (object[0].dyp != (float *) NULL) free (object[0].dyp);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadOverlay.c	(revision 22322)
@@ -0,0 +1,128 @@
+# include "Ximage.h"
+
+int LoadOverlay (int sock) {
+  
+  int i, j, Ntotal, Nbytes, Nread, Nfound, Ntext, Nobjects, overnum;
+  int Noverlay, Ntextdata;
+  char *textdata, *buffer, *p, *q;
+  Section *section;
+  KapaImageWidget *image;
+  Graphic *graphic;
+  KiiOverlayBase *overlay;
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  KiiScanMessage (sock, "%d %d %d %d", &overnum, &Noverlay, &Ntext, &Ntextdata);
+
+  // XXX need to validate overnum 
+  if ((overnum < 0) || (overnum >= NOVERLAYS)) overnum = 0;
+
+  // read the overlay data as binary 
+  Ntotal = 0;
+  Nbytes = Noverlay*sizeof(KiiOverlayBase);
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+  ALLOCATE (overlay, KiiOverlayBase, Noverlay);
+  buffer = (char *) overlay;
+  while (Nbytes > 0) { 
+    Nread = read (sock, &buffer[Ntotal], Nbytes);
+    // fprintf (stderr, "read: %d of %d remaining, %d so far, %d expected\n", Nread, Nbytes, Ntotal, Noverlay*sizeof(KiiOverlayBase));
+    if (Nread == 0) {  /* No more pipe */
+      fprintf (stderr, "error: pipe closed\n");
+      free (overlay);
+      fcntl (sock, F_SETFL, !O_NONBLOCK);  
+      return (FALSE);
+    }
+    if (Nread != -1) { /* pipe has data */
+      Nbytes -= Nread;
+      Ntotal += Nread;
+    }
+  }
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+  KiiSendCommand (sock,  4, "DONE");
+
+  // read the textdata as binary
+  Ntotal = 0;
+  Nbytes = Ntextdata;
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+  ALLOCATE (textdata, char, Ntextdata);
+  while (Nbytes > 0) { 
+    Nread = read (sock, &textdata[Ntotal], Nbytes);
+    if (Nread == 0) {  /* No more pipe */
+      fprintf (stderr, "error: pipe closed\n");
+      free (textdata);
+      free (overlay);
+      fcntl (sock, F_SETFL, !O_NONBLOCK);  
+      return (FALSE);
+    }
+    if (Nread != -1) { /* pipe has data */
+      Nbytes -= Nread;
+      Ntotal += Nread;
+    }
+  }
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+  KiiSendCommand (sock,  4, "DONE");
+
+  // add new overlay objects to existing data
+  Nobjects = image[0].overlay[overnum].Nobjects + Noverlay;
+  REALLOCATE (image[0].overlay[overnum].objects, KiiOverlay, Nobjects);
+
+  j = image[0].overlay[overnum].Nobjects;
+  for (i = 0; i < Noverlay; i++, j++) {
+    image[0].overlay[overnum].objects[j].x     = overlay[i].x;
+    image[0].overlay[overnum].objects[j].y     = overlay[i].y;
+    image[0].overlay[overnum].objects[j].dx    = overlay[i].dx;
+    image[0].overlay[overnum].objects[j].dy    = overlay[i].dy;
+    image[0].overlay[overnum].objects[j].angle = overlay[i].angle;
+    image[0].overlay[overnum].objects[j].type  = overlay[i].type;
+    image[0].overlay[overnum].objects[j].text  = NULL;
+  }
+
+  // parse the text data : text lines are separated by '\n', one per text entry
+  p = textdata;
+  Nfound = 0;
+  for (i = 0; i < Noverlay; i++) {
+    if (overlay[i].type != KII_OVERLAY_TEXT) continue;
+    if (Nfound >= Ntext) {
+      fprintf (stderr, "inconsistent number of text lines\n");
+      break;
+    }
+    if (! *p) {
+      fprintf (stderr, "inconsistent number of text lines\n");
+      break;
+    }
+    q = strchr (p, '\n');
+    if (q == NULL) {
+      fprintf (stderr, "inconsistent text line\n");
+      break;
+    }
+    j = image[0].overlay[overnum].Nobjects + i;
+    image[0].overlay[overnum].objects[j].text = strncreate (p, q-p);
+    p = q + 1;
+    Nfound ++;
+  }
+  if (Nfound != Ntext) {
+    fprintf (stderr, "read %d text lines, expected %d\n", Nfound, Ntext);
+  }
+
+  free (textdata);
+  free (overlay);
+
+  image[0].overlay[overnum].Nobjects = Nobjects;
+  image[0].overlay[overnum].active = TRUE;
+
+  if (USE_XWINDOW) {
+    for (i = 0; i < NOVERLAYS; i++) {
+      if (image[0].overlay[i].active) {
+	PaintOverlay (graphic, image, i);
+      }
+    }
+    XFlush (graphic[0].display);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadPicture.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadPicture.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadPicture.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include "Ximage.h"
+
+int LoadPicture (int sock) {
+
+  Header header;
+  char *buff;
+  int status, bytes_left;
+  Section *section;
+  KapaImageWidget *image;
+  Graphic *graphic;
+  double Xoffset, Yoffset, wx;
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+  
+  KiiSendMessage (sock, "%d", graphic->Npixels);
+
+  // when we load a new picture, use the same orientation as the old picture
+  Xoffset = 0.0;
+  Yoffset = 0.0;
+  if (image[0].image[0].matrix.size) {
+    Xoffset = image[0].picture.X - 0.5*image[0].image[0].matrix.Naxis[0];
+    Yoffset = image[0].picture.Y - 0.5*image[0].image[0].matrix.Naxis[1];
+  }
+
+  gfits_init_header (&header);
+  header.Naxes = 2;
+  KiiScanMessage (sock, "%d %d %d %d %lf %lf", &header.Naxis[0], &header.Naxis[1]);
+  KiiScanMessage (sock, "%d %d %d %d %lf %lf", &header.bitpix, &header.unsign, &header.bzero, &header.bscale);
+  KiiScanMessage (sock, "%lf %lf %s %s",  &image[0].image[0].zero, &image[0].image[0].range, image[0].image[0].name, image[0].image[0].file);
+  KiiScanMessage (sock, "%lf %lf %d", &image[0].image[0].min,  &image[0].image[0].max, &header.size);
+  KiiScanMessage (sock, "%lf %f %f %f %f", &image[0].image[0].coords.crval1, &image[0].image[0].coords.crpix1, &image[0].image[0].coords.cdelt1, &image[0].image[0].coords.pc1_1, &image[0].image[0].coords.pc1_2);
+  KiiScanMessage (sock, "%lf %f %f %f %f", &image[0].image[0].coords.crval2, &image[0].image[0].coords.crpix2, &image[0].image[0].coords.cdelt2, &image[0].image[0].coords.pc2_1, &image[0].image[0].coords.pc2_2);
+  KiiScanMessage (sock, "%s", image[0].image[0].coords.ctype);
+
+  gfits_free_matrix (&image[0].image[0].matrix);
+  gfits_create_matrix (&header, &image[0].image[0].matrix);
+
+  // reference point for image is the center pixel
+  image[0].picture.X = 0.5*header.Naxis[0] + Xoffset;
+  image[0].picture.Y = 0.5*header.Naxis[1] + Yoffset;
+
+  // choose expand for wide to guarantee we fit:
+  wx = MAX ((header.Naxis[0] / (float) image[0].wide.dx), (header.Naxis[1] / (float) image[0].wide.dy));
+  if (wx > 1.0) {
+    image[0].wide.expand = -wx;
+  } else {
+    image[0].wide.expand = 1.0 / wx;
+  }    
+  image[0].wide.X = 0.5*header.Naxis[0];
+  image[0].wide.Y = 0.5*header.Naxis[1];
+
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+
+  status = 1;
+  buff = image[0].image[0].matrix.buffer;
+  bytes_left = header.size;
+  image[0].image[0].matrix.size = 0;
+  while (bytes_left > 0) {
+    status = read (sock, buff, bytes_left);
+    if (status == 0) {  /* No more pipe */
+      fprintf (stderr, "error: pipe closed\n");
+      return (FALSE);
+    }
+    if (status != -1) { /* pipe has data */
+      image[0].image[0].matrix.size += status;
+      bytes_left -= status;
+      buff = (char *)(buff + status);
+    }
+  }
+
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+
+  if (DEBUG) fprintf (stderr, "read %d bytes\n", image[0].image[0].matrix.size);
+  /* it it not obvious this condition should kill kii, but ... */
+  if (image[0].image[0].matrix.size != header.size) {  
+    fprintf (stderr, "error: expected %d bytes, but got only %d\n", header.size, image[0].image[0].matrix.size);
+    return (FALSE);
+  }
+
+  if (!USE_XWINDOW) return (TRUE);
+
+  SetColorScale (graphic, image);
+  Remap (graphic, image);
+  CreateWide (graphic, image);
+  if (DEBUG) fprintf (stderr, "remapped image\n");
+  Refresh ();
+  if (DEBUG) fprintf (stderr, "refreshed\n");
+  XFlush (graphic[0].display);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadTextlines.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadTextlines.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadTextlines.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include "Ximage.h"
+
+int LoadTextlines (int sock) {
+  
+  char *string;
+  int N, size;
+  double tX, tY, tT, L;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  graph = section->graph;
+    if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+
+  graph[0].Ntextline = MAX (graph[0].Ntextline, 0);
+  N = graph[0].Ntextline;
+  graph[0].Ntextline++;
+  REALLOCATE (graph[0].textline, Label, graph[0].Ntextline);
+
+  KiiScanMessage (sock, "%lf %lf %lf", &tX, &tY, &tT);
+
+  L = graph[0].axis[0].dfx;
+  graph[0].textline[N].x = L * (tX - graph[0].axis[0].min) / (graph[0].axis[0].max - graph[0].axis[0].min) + graph[0].axis[0].fx;
+
+  L = graph[0].axis[1].dfy;
+  graph[0].textline[N].y = L * (tY - graph[0].axis[1].min) / (graph[0].axis[1].max - graph[0].axis[1].min) + graph[0].axis[1].fy;
+
+  graph[0].textline[N].angle = tT;
+
+  bzero (graph[0].textline[N].text, LABEL_MAXLEN);
+
+  string = KiiRecvData (sock);
+  strcpy (graph[0].textline[N].text, string);
+  free (string);
+  
+  string = GetRotFont (&size);
+  graph[0].textline[N].size = size;
+  strcpy (graph[0].textline[N].font, string);
+
+  if (USE_XWINDOW) DrawTextlines (graph);
+  
+  FlushDisplay ();
+  
+  return (TRUE);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadTickmarks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadTickmarks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/LoadTickmarks.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "Ximage.h"
+
+int LoadTickmarks (int sock) {
+  
+  char line[129], type[16];
+  double x, y, dx, dy;
+  int status, NOBJECTS, Nobjects, done;
+  Section *section;
+  KapaImageWidget *image;
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image   = section->image;
+
+  Nobjects = image[0].tickmarks.Nobjects;
+  NOBJECTS = Nobjects + 100;
+  REALLOCATE (image[0].tickmarks.objects, KiiOverlay, NOBJECTS);
+  
+  done = FALSE;
+  while (!done) {
+    status = read (sock, line, 128); 
+    line[128] = 0; 
+
+    if (!strncmp (line, "DONE", 4)) {
+      done = TRUE;
+      break;
+    }
+    
+    sscanf (line, "%s %lf %lf %lf %lf\n", type, &x, &y, &dx, &dy);
+    
+    if (strcmp (type, "TEXT") && strcmp (type, "LINE") && strcmp (type, "BOX") && strcmp (type, "CIRCLE")) {  /* skip */
+      fprintf (stderr, "don't know %s, skipping\n", type);
+      continue;
+    }
+    
+    strcpy (image[0].tickmarks.objects[Nobjects].type, type);
+    image[0].tickmarks.objects[Nobjects].x = x;
+    image[0].tickmarks.objects[Nobjects].y = y;
+    image[0].tickmarks.objects[Nobjects].dx = dx;
+    image[0].tickmarks.objects[Nobjects].dy = dy;
+    
+    if (!strcmp (type, "TEXT")) { /* dx = Nchar, dy = angle (not yet used) */
+      status = read (sock, line, 128); 
+      line[128] = 0; 
+      ALLOCATE (image[0].tickmarks.objects[Nobjects].text, char, (int) dx + 1);
+      strncpy (image[0].tickmarks.objects[Nobjects].text, line, (int) dx);
+      image[0].tickmarks.objects[Nobjects].text[(int)dx] = 0;
+    }      
+    
+    Nobjects++;
+    if (Nobjects >= NOBJECTS) {
+      NOBJECTS = Nobjects + 100;
+      REALLOCATE (image[0].tickmarks.objects, KiiOverlay, NOBJECTS);
+    }
+
+  }
+
+  REALLOCATE (image[0].tickmarks.objects, KiiOverlay, MAX(Nobjects, 1));
+  image[0].tickmarks.Nobjects = Nobjects;
+
+  if (USE_XWINDOW) Refresh ();
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MakeColormap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MakeColormap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MakeColormap.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "Ximage.h"
+
+static char default_cmap[] = "grayscale";
+
+void MakeColormap (int argc, char **argv) {
+
+  int i, N, status;
+  char *temp_name;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  /* hardwired colors - white, black -- for drawing text and overlay graphics */
+  temp_name = XGetDefault (graphic[0].display, argv[0], "ROverlay");
+  graphic[0].overlay_color[0] = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "red"    : temp_name), graphic[0].colormap, graphic[0].fore);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "GOverlay");
+  graphic[0].overlay_color[1] = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "green"  : temp_name), graphic[0].colormap, graphic[0].fore);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "BOverlay");
+  graphic[0].overlay_color[2] = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "blue"   : temp_name), graphic[0].colormap, graphic[0].fore);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "YOverlay");
+  graphic[0].overlay_color[3] = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "yellow" : temp_name), graphic[0].colormap, graphic[0].fore);
+
+  for (i = 0; i < graphic[0].Npixels; i++) {
+    graphic[0].cmap[i].pixel = graphic[0].pixels[i];
+  }
+
+  /* decide on a color map */
+  temp_name = XGetDefault (graphic[0].display, argv[0], "Colormap");
+  if ((N = get_argument (argc, argv, "-cm"))) {
+    if (N + 1 < argc) {
+      temp_name = argv[N+1];
+    } else {
+      fprintf (stderr, "error: usage is -cm ColormapName\n");
+      exit (1);
+    }
+  }
+  if (temp_name == (char *) NULL) temp_name = default_cmap;
+
+  status = SetColormap (temp_name);
+  if (!status) {
+    fprintf (stderr, "invalid colormap, using default %s\n", default_cmap);
+    temp_name = default_cmap;
+    status = SetColormap (temp_name);
+    if (!status) {
+      fprintf (stderr, "problem with default colormap\n");
+      exit (1);
+    }
+  }
+}
+
+/* this routine is NOT independent of the number of overlays */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MakeCursor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MakeCursor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MakeCursor.c	(revision 22322)
@@ -0,0 +1,9 @@
+# include "Ximage.h"
+
+/************** MakeCursor *************/
+void MakeCursor (Graphic *graphic, unsigned int cursor) {
+
+  graphic->cursor = XCreateFontCursor (graphic->display, (unsigned) cursor);
+
+  if (graphic->cursor != (Cursor) None) XDefineCursor (graphic->display, graphic->window, graphic->cursor);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MakeGC.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MakeGC.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MakeGC.c	(revision 22322)
@@ -0,0 +1,14 @@
+# include "Ximage.h"
+
+/************** MakeGC *************/
+void MakeGC (Graphic *graphic) {
+
+  XGCValues gcvalues;
+
+  gcvalues.foreground = graphic->fore;
+  gcvalues.background = graphic->back;
+  graphic->gc = XCreateGC (graphic->display, graphic->window, 
+			     GCForeground | GCBackground, &gcvalues);
+  if (graphic->gc == 0)
+    QuitX (graphic->display, "Error in creating a Graphics Context");
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MapWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MapWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/MapWindow.c	(revision 22322)
@@ -0,0 +1,9 @@
+# include "Ximage.h"
+
+/************** MapWindow *************/
+void MapWindow (Graphic *graphic) {
+
+  XMapRaised (graphic->display, graphic->window);
+  XMapSubwindows (graphic->display, graphic->window);
+  FlushDisplay (graphic);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/NameWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/NameWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/NameWindow.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "Ximage.h"
+
+/************** NameWindow *************/
+void NameWindow (Graphic *graphic, char *Name) {
+
+  char       *name;
+  char       *class_name;
+  char       *class_type;
+  XClassHint *classhints;
+
+  name = strrchr (Name, '/');
+  if (name != NULL) 
+    name ++;
+  else 
+    name = Name;
+
+  class_type = class_name = name;
+  classhints = XAllocClassHint ();
+
+  if (classhints != (XClassHint *) NULL)  {
+    classhints[0].res_name = class_name;
+    classhints[0].res_class = class_type;
+    XSetClassHint (graphic->display, graphic->window, classhints);
+    XFree (classhints);
+  }
+  
+  XStoreName (graphic->display, graphic->window, name);
+  XSetIconName (graphic->display, graphic->window, name);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/OpenDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/OpenDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/OpenDisplay.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "Ximage.h"
+
+/************** OpenDisplay *************/
+Display *OpenDisplay (char *name, int *screen) {
+
+  Display *display;
+  
+  display = XOpenDisplay (name);
+  if (display == NULL) {
+    fprintf (stderr, "Error could not open X display to %s\n", XDisplayName (name));
+    exit (0);
+  }
+  *screen = DefaultScreen (display);
+  return (display);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PNGit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PNGit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PNGit.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include "Ximage.h"
+
+int PNGit (int sock) {
+
+  FILE *f;
+  png_structp png_ptr;
+  png_infop info_ptr;
+  png_color *palette;
+  int Npalette;
+  char filename[1024];
+  int Nbytes, status;
+  bDrawBuffer *buffer;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  /* expect a line telling the number of bytes and a filename */
+  status = read (sock, filename, 16);
+  filename[16] = 0;
+  sscanf (filename, "%*s %d", &Nbytes);
+  status = read (sock, filename, Nbytes);
+  filename[status] = 0; /* make the string easy to parse */
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open output file %s\n", "Xgraph.png");
+    return (TRUE);  /* true because otherwise it quits kapa! */
+  }
+
+  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, (png_voidp) NULL, (png_voidp) NULL, (png_voidp) NULL);
+  if (!png_ptr) {
+    fprintf (stderr, "can't get png structure\n");
+    fclose (f);
+    return (TRUE);
+  }
+
+  info_ptr = png_create_info_struct (png_ptr);
+  if (!info_ptr) {
+    fprintf (stderr, "can't get png info structure\n");
+    png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+    fclose (f);
+    return (TRUE);
+  }
+
+  if (setjmp (png_ptr[0].jmpbuf)) {
+    fprintf (stderr, "can't get png return\n");
+    png_destroy_write_struct (&png_ptr, &info_ptr);
+    fclose (f);
+    return (TRUE);
+  }
+
+  png_init_io (png_ptr, f);
+
+  /* see docs for write-row-callback to provide progress info */
+
+  png_set_IHDR (png_ptr, info_ptr, graphic->dx, graphic->dy, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 
+
+  /* png_set_IHDR (png_ptr, info_ptr, graphic->dx, graphic->dy, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); */
+
+  palette = KapaPNGPalette (&Npalette);
+  png_set_PLTE (png_ptr, info_ptr, palette, Npalette); 
+ 
+  png_write_info (png_ptr, info_ptr);
+
+  buffer = bDrawIt ();
+
+  png_write_image (png_ptr, buffer[0].pixels);
+  png_write_end (png_ptr, info_ptr);
+  png_destroy_write_struct (&png_ptr, &info_ptr);
+  
+  bDrawBufferFree (buffer);
+  free (palette);
+  fclose (f);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PPMit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PPMit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PPMit.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "Ximage.h"
+
+int PPMit (int sock) {
+
+  FILE *f;
+  char *line, filename[1024];
+  int Nbytes, status, dx, dy, i, j, Npalette, color;
+  png_color *palette;
+  bDrawBuffer *buffer;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  /* expect a line telling the number of bytes and a filename */
+  status = read (sock, filename, 16);
+  filename[16] = 0;
+  sscanf (filename, "%*s %d", &Nbytes);
+  status = read (sock, filename, Nbytes);
+  filename[status] = 0; /* make the string easy to parse */
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open output file %s\n", filename);
+    return (TRUE);  /* true because otherwise it quits kapa! */
+  }
+  
+  palette = KapaPNGPalette (&Npalette);
+
+  dx = graphic->dx;
+  dy = graphic->dy;
+  
+  fprintf (f, "P6\n");
+  fprintf (f, "%d %d\n", dx, dy);
+  fprintf (f, "255\n");
+
+  buffer = bDrawIt ();
+
+  ALLOCATE (line, char, 3*dx);
+
+  for (i = 0; i < dy; i++) {
+    for (j = 0; j < dx; j++) {
+      color = buffer[0].pixels[i][j];
+      line[3*j + 0] = palette[color].red;
+      line[3*j + 1] = palette[color].green;
+      line[3*j + 2] = palette[color].blue;
+    }
+    fwrite (line, 3, dx, f);
+  }
+  fclose (f);
+  
+  bDrawBufferFree (buffer);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSFrame.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSFrame.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSFrame.c	(revision 22322)
@@ -0,0 +1,108 @@
+# include "Ximage.h"
+# define DrawLine(X1,Y1,DX,DY) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f L\n", X1, Y1, X1+DX, Y1+DY))
+
+int PSFrame (KapaGraphWidget *graph, FILE *f) {
+  
+  int i, P, IsLabel, IsMajor, fontsize;
+  double fx, fy, dfx, dfy, range, major, minor, first, next;
+  char *fontname;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  /* each axis is drawn independently */
+  fontname = GetRotFont (&fontsize);
+  fprintf (f, "1 setlinewidth\n");
+  for (i = 0; i < 4; i++) {
+    fx = graph[0].axis[i].fx;
+    fy = graphic->dy - graph[0].axis[i].fy;
+    dfx = graph[0].axis[i].dfx;
+    dfy = -graph[0].axis[i].dfy;
+    P = hypot ((double)graph[0].axis[(i+1)%2].dfx, (double)graph[0].axis[(i+1)%2].dfy);
+
+    if (graph[0].axis[i].isaxis) { DrawLine (fx, fy, dfx, dfy); }
+    
+    if (graph[0].axis[i].areticks) {
+      if (isnan(graph[0].axis[i].min) || isinf(graph[0].axis[i].min)) continue;
+      if (isnan(graph[0].axis[i].max) || isinf(graph[0].axis[i].max)) continue;
+
+      range = graph[0].axis[i].max - graph[0].axis[i].min;
+      AxisTickScale (&graph[0].axis[i], &major, &minor);
+
+      first = minor*((int)(graph[0].axis[i].min/minor));
+      if ((range > 0) && (first < graph[0].axis[i].min)) {
+	first += minor;
+      }
+      if ((range < 0) && (first > graph[0].axis[i].min)) {
+	first -= minor;
+      }
+      for (next = first; ((range > 0) && (next <= graph[0].axis[i].max)) || ((range < 0) && (next >= graph[0].axis[i].max));) {
+	IsMajor = FALSE;
+	IsMajor |= (fabs((int)(next/major) - (next/major)) < 0.5*(minor/major));
+	IsMajor |= (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major));
+	IsMajor |= (fabs ((int)((next - 0.5*minor)/major) - (next/major)) < 0.5*(minor/major));
+	IsLabel = (IsMajor && graph[0].axis[i].islabel);
+	PSTick (f, fx, fy, dfx, dfy, P, graph[0].axis[i].min, graph[0].axis[i].max, next, IsLabel, IsMajor, i);
+	if (range > 0) 
+	  next += minor;
+	else 
+	  next -= minor;
+      }
+    }
+  }
+  return (TRUE);
+}
+
+void PSTick (FILE *f, double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int IsLabel, int IsMajor, int naxis) {
+  
+  int pos, dir, fontsize;
+  double size, n, x, y, dx, dy;
+  char string[64], *fontname;
+
+  pos = size = 0;
+
+  if (IsMajor) { 
+    size = MAX (0.02, 7.0 / P); 
+  } else {
+    size = MAX (0.01, 4.0 / P); 
+  }
+  
+  n = P / sqrt ((double)(dfx*dfx + dfy*dfy));
+  x = fx + (value-min)*dfx/(max - min);
+  y = fy + (value-min)*dfy/(max - min);
+
+  dir = -1;
+  if ((naxis == 0) || (naxis == 1)) dir *= -1;
+  dx = dir*size*dfy*n;	
+  dy = dir*size*dfx*n;
+  
+  DrawLine (x, y, dx, dy);
+
+  if (IsLabel) {
+    fontname = GetRotFont (&fontsize);
+
+    /* temporarily assume rectilinear axes */
+    if (naxis == 0) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 1; }
+    if (naxis == 2) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 7; }
+
+    if (naxis == 1) { dy = 0; dx = -(0.8*fontsize + 1); pos = 3; }
+    if (naxis == 3) { dy = 0; dx = +(0.8*fontsize + 1); pos = 5; }
+
+    x = fx + (value-min)*dfx/(max - min) + dx;
+    y = fy + (value-min)*dfy/(max - min) + dy;
+    if (fabs(value) < 1e-14) { value = 0.0; }
+    sprintf (string, "%g", value);
+    PSRotText (f, (int)x, (int)y, string, pos, 0.0);
+  }
+}
+
+  /* 
+  dx = size*dfy*n;	
+  dy = size*dfx*n;
+  DrawLine (x, y, dx, dy);
+  
+  dx = -size*dfy*n;	
+  dy = -size*dfx*n;
+  DrawLine (x, y, dx, dy);
+  */
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSLabels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSLabels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSLabels.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include "Ximage.h"
+  
+void PSLabels (KapaGraphWidget *graph, FILE *f) {
+  
+  int i, pos, x, y, size;
+  double angle;
+  char *fontname;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  pos = 0;
+  fontname = GetRotFont (&size);
+  for (i = 0; i < 8; i++) {
+    if (strcmp (graph[0].label[i].text, "")) {
+      angle = 0;
+      switch (i) {
+      case 0: pos = 7; break;
+      case 1: pos = 1; angle = -90; break;
+      case 2: pos = 1; break;
+      case 3: pos = 1; angle =  90; break;
+      case 4: pos = 2; break;
+      case 5: pos = 0; break;
+      case 6: pos = 8; break;
+      case 7: pos = 6; break;
+      }	
+      x = graph[0].label[i].x;
+      y = graphic->dy - graph[0].label[i].y;
+      SetRotFont (graph[0].label[i].font, graph[0].label[i].size); 
+      PSRotText (f, x, y, graph[0].label[i].text, pos, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /*
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|       |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+  */
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSObjects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSObjects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSObjects.c	(revision 22322)
@@ -0,0 +1,600 @@
+# include "Ximage.h"
+
+# define DrawLine(X1,Y1,X2,Y2) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f L\n", X1, graphic->dy - Y1, X2, graphic->dy - Y2))
+# define DrawCircle(X1,Y1,R) (fprintf (f, " %6.2f %6.2f %6.2f C\n", (X1), (graphic->dy - Y1), (R)))
+# define DrawRectangle(X,Y,dX,dY) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f B\n", (dX), (dY), (X-0.5*dX), (graphic->dy-Y-0.5*dY)))
+# define FillRectangle(X,Y,dX,dY) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f F\n", (dX), (dY), (X-0.5*dX), (graphic->dy-Y-0.5*dY)))
+# define FillTriangle(X1,Y1,X2,Y2, X3, Y3) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f TF\n", (X1), (graphic->dy-Y1), (X2), (graphic->dy-Y2), (X3), (graphic->dy-Y3)))
+# define CONNECT 0
+# define HISTOGRAM 1
+# define POINTS 2
+
+static Graphic *graphic;
+
+int PSObjects (KapaGraphWidget *graph, FILE *f) {
+  
+  int i;
+  double lweight;
+  static char dash[] = "5";
+  static char dot[] = "3";
+  
+  graphic = GetGraphic();
+
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    switch (graph[0].objects[i].ltype) {
+    case 0:
+      break;
+    case 1:
+      fprintf (f, "[%s] 0 setdash\n", dash);
+      break;
+    case 2:
+      fprintf (f, "[%s] 0 setdash\n", dot);
+      break;
+    default:
+      break;
+    }
+    
+    lweight = MAX (0, MIN (10, graph[0].objects[i].lweight));
+    fprintf (f, "%.1f setlinewidth\n", lweight);
+    fprintf (f, "%s setrgbcolor\n", KapaColorRGBString(graph[0].objects[i].color));
+
+    switch (graph[0].objects[i].style) {
+    case CONNECT: 
+      PSConnect (graph, &graph[0].objects[i], f);
+      break;
+    case HISTOGRAM:
+      PSHistogram (graph, &graph[0].objects[i], f);
+      break;
+    case POINTS:
+      PSPoints (graph, &graph[0].objects[i], f);
+      break;
+    }
+
+    if (graph[0].objects[i].etype & 0x01) {
+      PSYErrors (graph, &graph[0].objects[i], f);
+    }
+    if (graph[0].objects[i].etype & 0x02) {
+      PSXErrors (graph, &graph[0].objects[i], f);
+    }
+    fprintf (f, "[] 0 setdash\n");
+    fprintf (f, "0.00 0.00 0.00 setrgbcolor\n");
+  }
+  return (TRUE);
+}
+
+/*******/
+void PSConnect (KapaGraphWidget *graph, Gobjects *object, FILE *f) {
+  
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    ClipLinePS (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1, f);
+    /* DrawLine (sx0, sy0, sx1, sy1); */
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+void ClipLinePS (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0, FILE *f) {
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x0,y0 if outside box */
+  if ((x0 < X0) && (x1 >= X0)) {
+    y0 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X0;
+  }
+  if ((x0 > X1) && (x1 <= X1)) {
+    y0 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X1;
+  }
+  if ((y0 < Y0) && (y1 >= Y0)) {
+    x0 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y0;
+  }
+  if ((y0 > Y1) && (y1 <= Y1)) {
+    x0 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y1;
+  }
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x1,y1 if outside box */
+  if ((x1 < X0) && (x0 >= X0)) {
+    y1 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X0;
+  }
+  if ((x1 > X1) && (x0 <= X1)) {
+    y1 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X1;
+  }
+  if ((y1 < Y0) && (y0 >= Y0)) {
+    x1 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y0;
+  }
+  if ((y1 > Y1) && (y0 <= Y1)) {
+    x1 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y1;
+  }
+  DrawLine (x0, y0, x1, y1);
+}
+
+/*******/
+void PSHistogram (KapaGraphWidget *graph, Gobjects *object, FILE *f) {
+
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sxa;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  /* find the first valid datapoint */
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  sx0 = MIN (MAX (sx0, X0), X1);
+  sy0 = MAX (MIN (sy0, Y0), Y1);
+  
+  /* continue with rest of points */
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    sx1 = MIN (MAX (sx1, X0), X1);
+    sy1 = MAX (MIN (sy1, Y0), Y1);
+    sxa = 0.5*(sx0 + sx1);
+    DrawLine (sx0, sy0, sxa, sy0);
+    DrawLine (sxa, sy0, sxa, sy1);
+    DrawLine (sxa, sy1, sx1, sy1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+/*******/
+void PSPoints (KapaGraphWidget *graph, Gobjects *object, FILE *f) {
+ 
+  int i;
+  float *x, *y, *z;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx, sy, d, sx1, sy1, sx2, sy2;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);  
+ 
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);  
+
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  /* below here, code is identical with DrawObjects */
+  /**** points are scaled by object.z ***/
+  if (object[0].size < 0) {
+    d = 0.5 * (graphic->dx + graphic->dy) * 0.01;
+    x = object[0].x; y = object[0].y; z = object[0].z;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {	/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy, sx + d*z[i], sy);
+	    DrawLine (sx, sy - d*z[i], sx, sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {	/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d*z[i], sy - d*z[i], sx - d*z[i], sy + d*z[i]);
+	    DrawLine (sx - d*z[i], sy - d*z[i], sx + d*z[i], sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillTriangle (sx - d*z[i], sy - 0.58*d*z[i], sx + d*z[i], sy - 0.58*d*z[i], sx, sy + 1.15*d*z[i]);
+	    /*
+	    DrawLine (sx - d*z[i], sy + 0.58*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx + d*z[i], sy + 0.58*d*z[i], sx,          sy - 1.15*d*z[i]);
+	    DrawLine (sx,          sy - 1.15*d*z[i], sx - d*z[i], sy + 0.58*d*z[i]);
+	    */
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy + 0.58*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx + d*z[i], sy + 0.58*d*z[i], sx,          sy - 1.15*d*z[i]);
+	    DrawLine (sx,          sy - 1.15*d*z[i], sx - d*z[i], sy + 0.58*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx,          sy - 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	/* connect a pair of points */
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  } else {
+    d = object[0].size * 0.5 * (graphic->dx + graphic->dy) * 0.003;
+    x = object[0].x; y = object[0].y;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {		/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+   	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy, sx + d, sy);
+	    DrawLine (sx, sy - d, sx, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {		/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d, sy - d, sx - d, sy + d);
+	    DrawLine (sx - d, sy - d, sx + d, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    /*
+	    DrawLine (sx - d, sy + 0.58*d, sx + d, sy + 0.58*d);
+	    DrawLine (sx + d, sy + 0.58*d, sx,     sy - 1.15*d);
+	    DrawLine (sx,     sy - 1.15*d, sx - d, sy + 0.58*d);
+	    */
+	    FillTriangle (sx - d, sy - 0.58*d, sx + d, sy - 0.58*d, sx, sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy - 0.58*d, sx + d, sy - 0.58*d);
+	    DrawLine (sx + d, sy - 0.58*d, sx,     sy + 1.15*d);
+	    DrawLine (sx,     sy + 1.15*d, sx - d, sy - 0.58*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx + d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx,     sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  }
+}
+    
+/*******/
+void PSXErrors (KapaGraphWidget *graph, Gobjects *object, FILE *f) {
+  
+  int i, bar;
+  float *x, *y, *dxm, *dxp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dxp = object[0].dxp; dxm = object[0].dxm; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[1].dfy*0.03;
+   
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] + dxp[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] + dxp[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxm[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] - dxm[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] - dxm[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+  }
+}
+
+    
+/*******/
+void PSYErrors (KapaGraphWidget *graph, Gobjects *object, FILE *f) {
+  
+  int i, bar;
+  float *x, *y, *dym, *dyp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+  
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dyp = object[0].dyp; dym = object[0].dym; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[0].dfx*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dyp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] + dyp[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] + dyp[i])*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dym[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] - dym[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] - dym[i])*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSOverlay.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "Ximage.h"
+
+void PSOverlay (KapaImageWidget *image, int N, FILE *f, int extra) {
+
+  int i;
+  double X, Y, dX, dY;
+  int Xmin, Ymin, Xmax, Ymax;
+  double expand, X0, Y0;
+ 
+  expand = 1.0;
+  if (image[0].picture.expand > 0) {
+    expand = 1 / (1.0*image[0].picture.expand);
+  }
+  if (image[0].picture.expand < 0) {
+    expand = fabs((double)image[0].picture.expand);
+  }
+
+  Image_to_Screen (&X0, &Y0, 0.0, 0.0, &image[0].picture);
+  X0 -= image[0].picture.x;
+  Y0 -= image[0].picture.y;
+
+  Xmin = 0;
+  Ymin = 0;
+  Xmax = image[0].picture.dx;
+  Ymax = image[0].picture.dy;
+
+  for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+    X  = (image[0].overlay[N].objects[i].x)/expand + X0;
+    Y =  Ymax - (image[0].overlay[N].objects[i].y)/expand - Y0;
+    dX = (image[0].overlay[N].objects[i].dx)/expand;
+    dY = (image[0].overlay[N].objects[i].dy)/expand;
+    
+    switch (image[0].overlay[N].objects[i].type) {
+      case KII_OVERLAY_LINE:
+	if (((X < Xmin) && (X + dX < Xmin)) || ((X > Xmax) && (X + dX > Xmax)) ||
+	    ((Y < Ymin) && (Y + dY < Ymin)) || ((Y > Ymax) && (Y + dY > Ymax))) {
+	  break;
+	}
+	fprintf (f, " %6.1f %6.1f %6.1f %6.1f L\n", X + extra, Y + extra, (X+dX + extra), (Y-dY + extra));
+	break;
+      case KII_OVERLAY_TEXT:
+	if (((X < Xmin) && (X + dX < Xmin)) || ((X > Xmax) && (X + dX > Xmax)) ||
+	    ((Y < Ymin) && (Y + dY < Ymin)) || ((Y > Ymax) && (Y + dY > Ymax))) {
+	  break;
+	}
+	fprintf (f, "(%s) %6.1f %6.1f T\n", image[0].overlay[N].objects[i].text, X + extra, Y + extra); 
+	break;
+      case KII_OVERLAY_BOX:
+	if (((X - 0.5*dX < Xmin) && (X + 0.5*dX < Xmin)) || ((X - 0.5*dX > Xmax) && (X + 0.5*dX > Xmax)) ||
+	    ((Y - 0.5*dY < Ymin) && (Y + 0.5*dY < Ymin)) || ((Y - 0.5*dY > Ymax) && (Y + 0.5*dY > Ymax))) {
+	  break;
+	}
+	fprintf (f, " %6.1f %6.1f %6.1f %6.1f B\n", (dX + 2*extra), (dY + 2*extra), (X - 0.5*dX - extra), (Y - 0.5*dY - extra));
+	break;
+      case KII_OVERLAY_CIRCLE:
+	if (((X - dX < Xmin) && (X + dX < Xmin)) || ((X - dX > Xmax) && (X + dX > Xmax)) ||
+	    ((Y - dY < Ymin) && (Y + dY < Ymin)) || ((Y - dY > Ymax) && (Y + dY > Ymax))) {
+	  break;
+	}
+	// XXX add in the rotated ellipse version (rotate in PS space)
+	fprintf (f, " %6.1f %6.1f %6.1f C\n", X, Y, fabs(dX + extra));
+	break;
+      default:
+	fprintf (stderr, "skipping unknown object\n");
+	break;
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSPixmap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSPixmap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSPixmap.c	(revision 22322)
@@ -0,0 +1,185 @@
+# include "Ximage.h"
+
+// XXX this stuff has been broken by the conversion to the pixmap stuff
+
+void PSPixmap8 (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned char *buff;
+  unsigned long back;
+
+  Nchar = 255.0;
+  Npix = graphic[0].Npixels;
+  frac = Nchar / Npix;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned char *)image[0].picture.data + image[0].picture.dx*(image[0].picture.dy - 1);
+  slope = image[0].image[0].slope;
+  start = image[0].image[0].start;
+  back  = graphic[0].back;
+
+  for (i = 0; i < image[0].picture.dy; i++) {
+    for (k = 0; k < image[0].picture.dx; k++, buff++) {
+      if (*buff == back) 
+	val = Nchar;
+      else {
+	for (m = 0; (graphic[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*image[0].picture.dx;
+  }
+  return;
+}
+
+void PSPixmap16 (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned short *buff;
+  unsigned long back;
+
+  Nchar = 255.0;
+  Npix = graphic[0].Npixels;
+  frac = Nchar / Npix;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned short *)image[0].picture.data + image[0].picture.dx*(image[0].picture.dy - 1);
+  slope = image[0].image[0].slope;
+  start = image[0].image[0].start;
+  back  = graphic[0].back;
+
+  for (i = 0; i < image[0].picture.dy; i++) {
+    for (k = 0; k < image[0].picture.dx; k++, buff++) {
+      if (*buff == back) 
+	val = Nchar;
+      else {
+	for (m = 0; (graphic[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*image[0].picture.dx;
+  }
+  return;
+}
+
+void PSPixmap24 (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, k, m, dx, dy, val, extra;
+  unsigned char *buff;
+  unsigned long color, byte;
+  double Nchar, Npix, start, slope, frac;
+  unsigned long back;
+
+  Nchar = 255.0;
+  Npix = graphic[0].Npixels;
+  frac = Nchar / Npix;
+  dx = image[0].picture.dx;
+  dy = image[0].picture.dy;
+  extra = 4 - (dx * 3) % 4;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned char *)&image[0].picture.data[(dy - 1)*(3*dx + extra)];
+  slope = image[0].image[0].slope;
+  start = image[0].image[0].start;
+  back  = graphic[0].back;
+
+  for (i = 0; i < dy; i++) {
+    for (k = 0; k < dx; k++, buff+=3) {
+      if (*buff == back) {
+	val = Nchar;
+      } else {
+	color = 0;
+	byte = buff[2];
+	color = (byte << 16);
+	byte = buff[1];
+	color |= (byte << 8);
+	byte = buff[0];
+	color |= byte;
+	for (m = 0; (graphic[0].cmap[m].pixel != color) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*3*dx + extra;
+  }
+  return;
+}
+
+void PSPixmap32 (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned int *buff;
+  unsigned long back;
+
+  Nchar = 255.0;
+  Npix = graphic[0].Npixels;
+  frac = Nchar / Npix;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned int *)image[0].picture.data + image[0].picture.dx*(image[0].picture.dy - 1);
+  slope = image[0].image[0].slope;
+  start = image[0].image[0].start;
+  back  = graphic[0].back;
+
+  for (i = 0; i < image[0].picture.dy; i++) {
+    for (k = 0; k < image[0].picture.dx; k++, buff++) {
+      if (*buff == back) 
+	val = Nchar;
+      else {
+	for (m = 0; (graphic[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*image[0].picture.dx;
+  }
+  return;
+}
+
+# if (0)
+// XXX needs work!
+void PSPixmap32_RGB (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned int *buff;
+  unsigned long back;
+  unsigned char 
+
+  ALLOCATE (pixelR, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixelG, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixelB, unsigned char, graphic[0].Npixels);
+
+  /** cmap[i].pixel must be defined even if X is not used **/
+  for (i = 0; i < graphic[0].Npixels; i++) { /* set up pixel array */
+    pixelR[i] = graphic[0].cmap[i].red >> 8;
+    pixelG[i] = graphic[0].cmap[i].green >> 8;
+    pixelB[i] = graphic[0].cmap[i].blue >> 8;
+  }
+
+  for (i = 0; i < image[0].picture.dy; i++) {
+    for (k = 0; k < image[0].picture.dx; k++, buff++) {
+      if (*buff == back) 
+	val = Nchar;
+      else {
+	for (m = 0; (graphic[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*image[0].picture.dx;
+  }
+  return;
+}
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSTextlines.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSTextlines.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSTextlines.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "Ximage.h"
+  
+void PSTextlines (KapaGraphWidget *graph, FILE *f) {
+
+  int i, x, y, size;
+  double angle;
+  char *fontname;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  fontname = GetRotFont (&size);
+  for (i = 0; i < graph[0].Ntextline; i++) {
+    if (strcmp (graph[0].textline[i].text, "")) {
+      angle = graph[0].textline[i].angle;
+      x = graph[0].textline[i].x;
+      y = graphic->dy - graph[0].textline[i].y;
+      SetRotFont (graph[0].textline[i].font, graph[0].textline[i].size);
+      PSRotText (f, x, y, graph[0].textline[i].text, 5, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /* pos values
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|   8   |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+  */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSimage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSimage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSimage.c	(revision 22322)
@@ -0,0 +1,61 @@
+# include "Ximage.h"
+
+int PSimage (KapaImageWidget *image, FILE *f) {
+  
+  int i;
+  Graphic *graphic;
+
+  if (!USE_XWINDOW) {
+    fprintf (stderr, "PSimage not working yet for no X mode\n");
+    return (TRUE);
+  }
+
+  graphic = GetGraphic();
+
+  fprintf (f, " newpath 0 0 moveto %d 0 lineto %d %d lineto 0 %d lineto closepath clip\n\n", 
+	   image[0].picture.dx, image[0].picture.dx, image[0].picture.dy, image[0].picture.dy);
+  fprintf (f, "gsave %% encloses image\n");
+  fprintf (f, "%d %d 8\n", image[0].picture.dx, image[0].picture.dy);
+  fprintf (f, "[1 0 0 1 0 0]\n");
+  fprintf (f, "{currentfile %d string readhexstring pop} image\n\n", image[0].picture.dx);
+
+  /******** First we draw the picture itself ********/
+  /* in !USE_XWINDOW, we'll have to change this to use the JPEG function */
+  switch (graphic[0].Nbits) {
+  case 8:
+    PSPixmap8 (graphic, image, f);
+    break;
+  case 16:
+    PSPixmap16 (graphic, image, f);
+    break;
+  case 24:
+    PSPixmap24 (graphic, image, f);
+    break;
+  case 32:
+    PSPixmap32 (graphic, image, f);
+    break;
+  }
+
+  fprintf (f, "grestore %% end of image\n");
+  fprintf (f, "stroke\n");
+
+  // should we have a 'gsave / grestore' container here?
+  fprintf (f, "%% plot overlay objects\n");
+  fprintf (f, "1 setgray\n");
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (image[0].overlay[i].active) {
+      fprintf (f, "%% overlay %d\n", i);
+      PSOverlay (image, i, f, 0);
+    }
+  }
+  fprintf (f, "0 setgray\n");
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (image[0].overlay[i].active) {
+      fprintf (f, "%% overlay %d\n", i);
+      PSOverlay (image, i, f, 1);
+    }
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PSit.c	(revision 22322)
@@ -0,0 +1,107 @@
+# include "Ximage.h"
+# define XOFFSET 60
+# define YOFFSET 60
+
+static char *name = "$Name: not supported by cvs2svn $";
+static Graphic *graphic;
+
+int PScommand (int sock) {
+
+  int status, scaleMode, pageMode;
+  char filename[1024], pagename[1024];
+
+  /* expect a line telling the number of bytes and a filename */
+  KiiScanMessage (sock, "%s %s %d %d", filename, pagename, &scaleMode, &pageMode);
+  status = PSit (filename, pagename, scaleMode, pageMode);
+  return (status);
+}
+
+int PSit (char *filename, char *pagename, int scaleMode, int pageMode) {
+
+  int i, Nsection;
+  double scale;
+  FILE *f;
+  char *version;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  if (pageMode == KAPA_PS_NEWPAGE) {
+    f = fopen (filename, "a+");
+  } else {
+    f = fopen (filename, "w");
+  }
+  if (f == NULL) {
+    fprintf (stderr, "can't open output file %s\n", filename);
+    return (TRUE);  /* true because otherwise it quits kapa! */
+  }
+
+  /* two scaling options: expand to fit page / keep absolute size */ 
+  if (scaleMode) {
+    scale = MIN (fabs(500.0 / graphic->dx), fabs (700.0 / graphic->dy));
+  } else {
+    scale = 72.0 / 96.0; /* ratio of screen pixels to points */
+  }
+
+  switch (pageMode) {
+    case KAPA_PS_NEWPLOT:
+      fprintf (f, "%%!PS-Adobe-2.0 EPSF-2.0\n");
+      fprintf (f, "%%%%Title: %s\n", filename);
+      version = strip_version (name);
+      fprintf (f, "%%%%Creator: Kapa (%s)\n", version);
+      free (version);
+      fprintf (f, "%%%%BoundingBox: %d %d %.0f %.0f\n", 
+	       XOFFSET, YOFFSET, XOFFSET + scale*graphic->dx, YOFFSET + scale*graphic->dy);
+      fprintf (f, "%%%%Pages: 1\n");
+      fprintf (f, "%%%%DocumentFonts:\n");
+      fprintf (f, "%%%%EndComments\n");
+      fprintf (f, "%%%%EndProlog\n");
+      fprintf (f, "%%%%Page: %s\n\n", pagename);
+      break;
+
+    case KAPA_PS_NEWPAGE:
+      fprintf (f, "%%%%Page: %s\n\n", pagename);
+      break;
+
+    case KAPA_PS_RAWPAGE:
+      break;
+  } 
+  fprintf (f, "gsave %% encloses picture\n");
+  fprintf (f, "%% local abbreviations\n");
+  fprintf (f, "/Times-Roman findfont 14 scalefont setfont\n");
+  fprintf (f, "/T {moveto show stroke} def\n");
+  fprintf (f, "/B { newpath moveto dup 0 exch rlineto exch dup 0 rlineto exch -1 mul\n");
+  fprintf (f, " 0 exch rlineto -1 mul 0 rlineto closepath stroke } def\n");
+  fprintf (f, "/F { newpath moveto dup 0 exch rlineto exch dup 0 rlineto exch -1 mul\n");
+  fprintf (f, " 0 exch rlineto -1 mul 0 rlineto closepath fill stroke } def\n");
+  fprintf (f, "/C {0 360 arc stroke} def\n");
+  fprintf (f, "/L {newpath moveto lineto stroke} def\n\n");
+  fprintf (f, "/TF {newpath moveto lineto lineto fill stroke} def\n\n");
+
+  if (pageMode != KAPA_PS_RAWPAGE) {
+    fprintf (f, " %d %d translate\n", XOFFSET, YOFFSET);
+    fprintf (f, "  %f  %f scale\n", scale, scale);
+  }
+
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+    section = GetSectionByNumber (i);
+    if (section->graph) {
+      PSFrame (section->graph, f); 
+      PSObjects (section->graph, f);
+      PSLabels (section->graph, f);
+      PSTextlines (section->graph, f);
+    }
+    if (section->image) {
+      PSimage (section->image, f);
+    }
+  }
+  
+  fprintf (f, "grestore %% end of picture\n");
+
+  if (pageMode != KAPA_PS_RAWPAGE) fprintf (f, "showpage\n");
+
+  fclose (f);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PaintOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PaintOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PaintOverlay.c	(revision 22322)
@@ -0,0 +1,84 @@
+# include "Ximage.h"
+
+void PaintOverlay (Graphic *graphic, KapaImageWidget *image, int N) {
+
+  int i;
+  int dX, dY, dx, dy;
+  int x, y, Xmin, Ymin, Xmax, Ymax, Xrange, Yrange;
+  double t, expand, X, Y;
+ 
+  XSetForeground (graphic[0].display, graphic[0].gc, image[0].overlay[N].color);
+  
+  expand = 1.0;
+  if (image[0].picture.expand > 0) {
+    expand = image[0].picture.expand;
+  }
+  if (image[0].picture.expand < 0) {
+    expand = 1.0 / fabs((double)image[0].picture.expand);
+  }
+
+  Xmin = image[0].picture.x;
+  Ymin = image[0].picture.y;
+  Xmax = image[0].picture.x + image[0].picture.dx;
+  Ymax = image[0].picture.y + image[0].picture.dy;
+  Xrange = image[0].picture.dx;
+  Yrange = image[0].picture.dy;
+
+  for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+    Image_to_Screen (&X, &Y, image[0].overlay[N].objects[i].x, image[0].overlay[N].objects[i].y, &image[0].picture);
+    dX = image[0].overlay[N].objects[i].dx * expand;
+    dY = image[0].overlay[N].objects[i].dy * expand;
+    if (image[0].picture.flipx) dX *= -1;
+    if (image[0].picture.flipy) dY *= -1;
+
+    if (X + dX < Xmin) continue;
+    if (X - dX > Xmax) continue;
+    if (Y + dY < Ymin) continue; 
+    if (Y - dY > Ymax);
+
+    /* for a LINE, (x, y) is the start, (dx, dy) is the distance to end
+       for a CIRCLE (x, y) is the center, (dx, dy) is the radius 
+       for a BOX (x, y) is the center, (dx, dy) is the width */
+
+    switch (image[0].overlay[N].objects[i].type) {
+      case KII_OVERLAY_LINE:
+	// the angle makes no sense for a line...
+	XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, (X+dX), (Y+dY));
+	break;
+      case KII_OVERLAY_TEXT:
+	// XXX currently we ignore the rectangle angle
+	XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, image[0].overlay[N].objects[i].text, strlen(image[0].overlay[N].objects[i].text));
+	break;
+      case KII_OVERLAY_BOX:
+	dx = MAX (abs(dX),2) / 2;
+	dy = MAX (abs(dY),2) / 2;
+	// XXX currently we ignore the rectangle angle
+	XDrawRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, (X - dx), (Y - dy), 2*dx, 2*dy);
+	break;
+      case KII_OVERLAY_CIRCLE:
+	dx = MAX (abs(dX),2);
+	dy = MAX (abs(dY),2);
+	if (image[0].overlay[N].objects[i].angle == 0.0) {
+	  XDrawArc (graphic[0].display, graphic[0].window, graphic[0].gc, (X - dx), (Y - dy), 2*dx, 2*dy, 0, 23040);
+	} else {
+	  // very stupid rotate ellipse drawing:
+	  double angle = image[0].overlay[N].objects[i].angle * RAD_DEG;
+	  double cs = cos(angle);
+	  double sn = sin(angle);
+	  // XXX dt should be based on the size of the ellipse...
+	  for (t = 0; t < 2*M_PI; t+=0.05) {
+	    x = X + dx*cos(t)*cs + dy*sin(t)*sn;
+	    y = Y - dx*cos(t)*sn + dy*sin(t)*cs;
+	    XDrawPoint (graphic[0].display, graphic[0].window, graphic[0].gc, x, y);
+	  }
+	}
+	break;
+      default:
+	fprintf (stderr, "skipping unknown object\n");
+	break;
+    }
+  }
+  
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PaintTickmarks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PaintTickmarks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/PaintTickmarks.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "Ximage.h"
+
+void PaintTickmarks (Graphic *graphic, KapaImageWidget *image) {
+
+  int i;
+  int X, Y, dX, dY;
+  int Xmin, Ymin, Xmax, Ymax, Xrange, Yrange;
+ 
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+
+  Xmin = image[0].picture.x;
+  Ymin = image[0].picture.y;
+  Xmax = image[0].picture.x + image[0].picture.dx;
+  Ymax = image[0].picture.y + image[0].picture.dy;
+  Xrange = image[0].picture.dx;
+  Yrange = image[0].picture.dy;
+
+  for (i = 0; i < image[0].tickmarks.Nobjects; i++) {
+    X  = image[0].tickmarks.objects[i].x * Xrange + Xmin;
+    Y  = image[0].tickmarks.objects[i].y * Yrange + Ymin;
+    dX = image[0].tickmarks.objects[i].dx * Xrange;
+    dY = image[0].tickmarks.objects[i].dy * Yrange;
+
+    switch (image[0].overlay[0].objects[i].type) {
+      case KII_OVERLAY_LINE:
+	XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, (X+dX), (Y+dY));
+	break;
+      case KII_OVERLAY_TEXT:
+	if (image[0].tickmarks.objects[i].dy == 0) {
+	    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+	    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y-11, 6*strlen(image[0].tickmarks.objects[i].text), 11); 
+	    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+	    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, image[0].tickmarks.objects[i].text, strlen(image[0].tickmarks.objects[i].text));
+	}
+	if (image[0].tickmarks.objects[i].dy == 90) {
+	    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+	    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y-6*strlen(image[0].tickmarks.objects[i].text), 11, 6*strlen(image[0].tickmarks.objects[i].text)); 
+	    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+	    /* XDrawRotString (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, image[0].tickmarks.objects[i].text, strlen(image[0].tickmarks.objects[i].text)); */
+	}
+	break;
+      case KII_OVERLAY_BOX:
+	XDrawRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, (int)(X - 0.5*dX), (int)(Y - 0.5*dY), abs(dX), abs(dY));
+	break;
+      case KII_OVERLAY_CIRCLE:
+	XDrawArc (graphic[0].display, graphic[0].window, graphic[0].gc, X - dX, Y - dY, abs(2*dX), abs(2*dY), 0, 23040);
+	break;
+      default:
+	fprintf (stderr, "skipping unknown object\n");
+	break;
+    }
+  }
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/QuitX.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/QuitX.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/QuitX.c	(revision 22322)
@@ -0,0 +1,9 @@
+# include "Ximage.h"
+
+/************** QuitX *************/
+void QuitX (Display *display, char *error_message) {
+
+  fprintf (stderr, "Error: %s\n", error_message);
+  XCloseDisplay (display);
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Reconfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Reconfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Reconfig.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "Ximage.h"
+
+// XXX merge this code with Resize
+int Reconfig (XEvent *event) {
+
+  int i, Nsection, NX, NY;
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  // XXX keep this min limit (or modify for !USE_XWINDOW)?
+  NX = MAX(event[0].xconfigure.width, MIN_WIDTH); 
+  NY = MAX(event[0].xconfigure.height, MIN_HEIGHT); 
+
+  // if the new size is the same as the old size, do nothing.
+  if ((graphic->dx == NX) && (graphic->dy == NY)) return (TRUE);
+
+  // set the new window size
+  graphic->dx = NX; 
+  graphic->dy = NY; 
+
+  // reset the sizes for all sections
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+      section = GetSectionByNumber (i);
+      SetSectionSizes (section);
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Refresh.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Refresh.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Refresh.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "Ximage.h"
+
+void Refresh () {
+
+  int i, Nsection;
+  Graphic *graphic;
+  Section *section;
+
+  if (!USE_XWINDOW) return;
+  // if (HAVE_BACKING) return;
+
+  graphic = GetGraphic();
+  
+  /* XClearWindow   (graphic.display, graphic.window); */
+  XSetForeground (graphic->display, graphic->gc, graphic->back);
+  XFillRectangle (graphic->display, graphic->window, graphic->gc, 0, 0, graphic->dx, graphic->dy);
+  XSetForeground (graphic->display, graphic->gc, graphic->fore);
+  
+  // reset the sizes for all sections
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+      section = GetSectionByNumber (i);
+      DrawImage (section->image);
+      DrawGraph (section->graph);
+  }
+
+  FlushDisplay ();
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap.c	(revision 22322)
@@ -0,0 +1,21 @@
+# include "Ximage.h"
+
+void Remap (Graphic *graphic, KapaImageWidget *image) {
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    Remap8  (graphic, image, &image->picture, &image->image->matrix);
+    break;
+  case 16:
+    Remap16 (graphic, image, &image->picture, &image->image->matrix);
+    break;
+  case 24:
+    Remap24 (graphic, image, &image->picture, &image->image->matrix);
+    break;
+  case 32:
+    Remap32 (graphic, image, &image->picture, &image->image->matrix);
+    break;
+  }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap16.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap16.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap16.c	(revision 22322)
@@ -0,0 +1,136 @@
+# include "Ximage.h"
+
+# define MY_SWAP_BYTE(W) { \
+  char tmp, *X; \
+  X = (char *) &W; \
+  tmp = X[0]; X[0] = X[1]; X[1] = tmp; }
+
+void Remap16 (Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;
+  int dx, dy, DX, DY;
+  double expand, Ix, Iy;
+  int expand_in, expand_out;
+  unsigned short *out_pix, *out_pix2, *data;
+  unsigned short *in_pix, *in_pix2;
+  unsigned short *pixel, pixvalue;
+  unsigned short back;
+  int swap_client, swap_server, swap_bytes;
+
+  ALLOCATE (pixel, unsigned short, graphic[0].Npixels);
+
+# ifdef BYTE_SWAP
+  swap_client = 1;
+# else 
+  swap_client = 0;
+# endif  
+
+  swap_server = ImageByteOrder (graphic[0].display);
+  swap_bytes = !(swap_client ^ swap_server);
+
+  // local array for pixel values (is this working??)
+  for (i = 0; i < graphic[0].Npixels; i++) { /* set up pixel array */
+    pixel[i] = 0xffff &  graphic[0].cmap[i].pixel;
+    if (swap_bytes) MY_SWAP_BYTE (pixel[i]);
+  }
+  back = 0xffff & graphic[0].back;
+  if (swap_bytes) MY_SWAP_BYTE (back);
+  // XXX not certain this is the correct solution...
+
+  // set up expansions
+  assert ((picture[0].expand >= 1) || (picture[0].expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (picture[0].expand > 0) {
+    expand = 1 / (1.0*picture[0].expand);
+    expand_out = picture[0].expand;
+    expand_in  = 1;
+  }
+  if (picture[0].expand < 0) {
+    expand = fabs((double)picture[0].expand);
+    expand_out = 1;
+    expand_in  = -picture[0].expand;
+  }
+
+  // define the image boundaries
+  dx = picture[0].dx;
+  dy = picture[0].dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, matrix, picture);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, matrix, picture);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  data = out_pix = (unsigned short *) picture[0].data;
+  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) {
+    *out_pix = back;
+  }
+  
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[j*dx];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out) && (j + jj < dy); jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	*out_pix = pixel[*in_pix2];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixvalue = pixel[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; (jj < expand_out) && (j + jj < dy); jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue; 
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; (jj < expand_out) && (j + jj < dy); jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+  } 
+  
+  /**** fill in top area ****/
+  out_pix = &data[j_end*dx];
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix++) { 
+      *out_pix = back;
+    }
+  }
+
+  picture[0].pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					picture[0].data, picture[0].dx, picture[0].dy, 16, 0);
+
+  free (pixel);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap24.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap24.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap24.c	(revision 22322)
@@ -0,0 +1,150 @@
+# include "Ximage.h"
+
+void Remap24 (Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback, extra;
+  int dx, dy, DX, DY;
+  double expand, Ix, Iy;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2, *data;
+  unsigned short *in_pix, *in_pix2;
+  unsigned char *pixel1, *pixel2, *pixel3;
+  unsigned char pixvalue1, pixvalue2, pixvalue3;
+  unsigned char back1, back2, back3;
+
+  ALLOCATE (pixel1, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixel2, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixel3, unsigned char, graphic[0].Npixels);
+
+  // local arrays for pixel values
+  for (i = 0; i < graphic[0].Npixels; i++) { /* set up pixel array */
+    pixel1[i] = 0x0000ff &  graphic[0].cmap[i].pixel;
+    pixel2[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 8);
+    pixel3[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 16);
+  }
+  back1 = 0x0000ff & (graphic[0].back >>  0);
+  back2 = 0x0000ff & (graphic[0].back >>  8);
+  back3 = 0x0000ff & (graphic[0].back >> 16);
+
+  // set up expansions
+  assert ((picture[0].expand >= 1) || (picture[0].expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (picture[0].expand == 0) /* set up expansions */
+    picture[0].expand = 1;
+  if (picture[0].expand > 0) {
+    expand = 1 / (1.0*picture[0].expand);
+    expand_out = picture[0].expand;
+    expand_in  = 1;
+  }
+  if (picture[0].expand < 0) {
+    expand = fabs((double)picture[0].expand);
+    expand_out = 1;
+    expand_in  = -picture[0].expand;
+  }
+
+  dx = picture[0].dx;
+  dy = picture[0].dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  // each row is padded to a 4-byte word
+  extra = 4 - (dx * 3) % 4;
+
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, matrix, picture);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, matrix, picture);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+  
+  data = out_pix = (unsigned char *) picture[0].data;
+  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) {
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+      out_pix[2] = back3;
+    }
+    out_pix += extra;
+  }
+  
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[j*(3*dx+extra)];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out) && (j + jj < dy); jj++) { 
+      out_pix2 = out_pix + jj*(3*dx + extra);
+      for (i = 0; i < i_start; i++, out_pix2+=3) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+	out_pix2[2] = back3;
+      }
+    }
+    out_pix += 3*i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=3) {
+	out_pix[0] = pixel1[*in_pix2];
+	out_pix[1] = pixel2[*in_pix2];
+	out_pix[2] = pixel3[*in_pix2];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 3*expand_out) { 
+	pixvalue1 = pixel1[*in_pix2];
+	pixvalue2 = pixel2[*in_pix2];
+	pixvalue3 = pixel3[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; (jj < expand_out) && (j + jj < dy); jj++, out_pix2+=3*(dx-expand_out)+extra) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2+=3) {
+	    out_pix2[0] = pixvalue1; 
+	    out_pix2[1] = pixvalue2; 
+	    out_pix2[2] = pixvalue3; 
+	  }
+	}
+      }
+    }
+    out_pix -= 3*dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; (jj < expand_out) && (j + jj < dy); jj++) {
+      //    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*(3*dx+extra);
+      for (i = i_end; i < dx; i++, out_pix2+=3) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+	out_pix2[2] = back3;
+      }
+    }
+  } 
+  
+  /**** fill in top area ****/
+  out_pix = &data[j_end*(3*dx+extra)];
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) { 
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+      out_pix[2] = back3;
+    }
+    out_pix+=extra;
+  }
+
+  picture[0].pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				 picture[0].data, picture[0].dx, picture[0].dy, 32, 0);
+
+  free (pixel1);
+  free (pixel2);
+  free (pixel3);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap32.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap32.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap32.c	(revision 22322)
@@ -0,0 +1,161 @@
+# include "Ximage.h"
+
+# define MY_SWAP_INT(A,B) { int tmp; tmp = A; A = B; B = tmp; }
+
+# define MY_SWAP_WORD(W) { \
+  char tmp, *X; \
+  X = (char *) &W; \
+  tmp = X[0]; X[0] = X[3]; X[3] = tmp; \
+  tmp = X[1]; X[1] = X[2]; X[2] = tmp; }
+
+void Remap32 (Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback, inDX, inDY;
+  int dx, dy, DX, DY;
+  double expand, Ix, Iy;
+  int expand_in, expand_out;
+  unsigned int *out_pix, *out_pix2;
+  unsigned short *in_pix, *in_pix2;
+  unsigned long *pixel, pixvalue;
+  unsigned long back;
+  int swap_client, swap_server, swap_bytes;
+
+  ALLOCATE (pixel, unsigned long, graphic[0].Npixels);
+
+# ifdef BYTE_SWAP
+  swap_client = 1;
+# else 
+  swap_client = 0;
+# endif  
+
+  swap_server = ImageByteOrder (graphic[0].display);
+  swap_bytes = !(swap_client ^ swap_server);
+
+  // local array for pixel values
+  for (i = 0; i < graphic[0].Npixels; i++) { 
+    pixel[i] = graphic[0].cmap[i].pixel;
+    if (swap_bytes) MY_SWAP_WORD(pixel[i]);
+  }
+  back = graphic[0].back;
+  if (swap_bytes) MY_SWAP_WORD(back);
+
+  // set up expansions
+  assert ((picture[0].expand >= 1) || (picture[0].expand <= -1));
+  expand = expand_in = expand_out = 1.0;
+  if (picture[0].expand > 0) {
+    expand = 1 / (1.0*picture[0].expand);
+    expand_out = picture[0].expand;
+    expand_in  = 1;
+  }
+  if (picture[0].expand < 0) {
+    expand = fabs((double)picture[0].expand);
+    expand_out = 1;
+    expand_in  = -picture[0].expand;
+  }
+
+  // define the image sizes
+  dx = picture[0].dx;
+  dy = picture[0].dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, matrix, picture);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, matrix, picture);
+
+  if (i_end < i_start) MY_SWAP_INT (i_start, i_end);
+  if (j_end < j_start) MY_SWAP_INT (j_start, j_end);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+  Ix = MIN (MAX (0, Ix), DX - 1);
+  Iy = MIN (MAX (0, Iy), DY - 1);
+  // XXX not completely consistent with the i_start, i_end range...
+
+  // we need to offset because i_start points to the bottom edge of Ix
+  // if (picture[0].flipx) Ix -= 1.0;
+  // if (picture[0].flipy) Iy -= 1.0;
+
+  inDX = picture[0].flipx ? -1 : +1;
+  inDY = picture[0].flipy ? -1 : +1;
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned int *) picture[0].data;
+  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) {
+    *out_pix = back;
+  }
+  
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += inDY*expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out) && (j + jj < dy); jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2 += inDX*expand_in, out_pix++) {
+	*out_pix = pixel[*in_pix2];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2 += inDX, out_pix+= expand_out) { 
+	pixvalue = pixel[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; (jj < expand_out) && (j + jj < dy); jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue;
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    // assert (in_pix2 - image[0].pixmap <= DX*DY);
+    // assert (in_pix2 - image[0].pixmap >= 0);
+    // assert (in_pix - image[0].pixmap <= DX*DY);
+    // assert (in_pix - image[0].pixmap >= 0);
+
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; (jj < expand_out) && (j + jj < dy); jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += (dx - i_end);
+    // assert (out_pix - (unsigned int *)picture[0].data <= dx*dy);
+    // assert (out_pix - (unsigned int *)picture[0].data >= 0);
+  } 
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  
+  // assert (out_pix - (unsigned int *)picture[0].data <= dx*dy);
+  // assert (out_pix - (unsigned int *)picture[0].data >= 0);
+
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned int *)picture[0].data < dx*dy); j++, out_pix++) { 
+    *out_pix = back;
+  }
+
+  picture[0].pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					picture[0].data, picture[0].dx, picture[0].dy, 32, 0);
+
+  free (pixel);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap8.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap8.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Remap8.c	(revision 22322)
@@ -0,0 +1,119 @@
+# include "Ximage.h"
+
+void Remap8 (Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;
+  int dx, dy, DX, DY;
+  double expand, Ix, Iy;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2;
+  unsigned short *in_pix,  *in_pix2;
+  unsigned char *pixel, pixvalue;
+  unsigned char back;
+
+  ALLOCATE (pixel, unsigned char, graphic[0].Npixels);
+
+  // local array for pixel values
+  for (i = 0; i < graphic[0].Npixels; i++) {
+    pixel[i] = 0xff & graphic[0].cmap[i].pixel;
+  }
+  back = 0xff & graphic[0].back;
+
+  // set up expansions
+  assert ((picture[0].expand >= 1) || (picture[0].expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (picture[0].expand == 0) /* set up expansions */
+    picture[0].expand = 1;
+  if (picture[0].expand > 0) {
+    expand = 1 / (1.0*picture[0].expand);
+    expand_out = picture[0].expand;
+    expand_in  = 1;
+  }
+  if (picture[0].expand < 0) {
+    expand = fabs((double)picture[0].expand);
+    expand_out = 1;
+    expand_in  = -picture[0].expand;
+  }
+
+  // define the image boundaries
+  dx = picture[0].dx;
+  dy = picture[0].dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, matrix, picture);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, matrix, picture);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned char *) picture[0].data;
+  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) {
+    *out_pix = back;
+  }
+  
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out) && (j + jj < dy); jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	*out_pix = pixel[*in_pix2];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixvalue = pixel[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; (jj < expand_out) & (j + jj < dy); jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue;
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; (jj < expand_out) && (j + jj < dy); jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += (dx - i_end);
+    
+  } 
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned char *)picture[0].data < dx*dy); j++, out_pix++) { 
+    *out_pix = back;
+  }
+  picture[0].pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				 picture[0].data, picture[0].dx, picture[0].dy, 8, 0);
+  free (pixel);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Reorient.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Reorient.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Reorient.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "Ximage.h"
+
+void Reorient (Graphic *graphic, KapaImageWidget *image, double X, double Y, int mode) {
+
+  Picture *picture;
+
+  picture = &image[0].picture;
+
+  if (picture[0].expand == 0) picture[0].expand = 1;
+
+  if ((picture[0].X == X) && (picture[0].Y == Y) && (mode == 0)) {
+    Refresh ();
+    XFlush (graphic[0].display);
+    return;
+  }
+
+  switch (mode) {
+  case 0:
+    if ((picture[0].X != X) || (picture[0].Y != Y)) {
+      picture[0].X = X;
+      picture[0].Y = Y;
+    }
+    break;
+  case -1: 
+    picture[0].expand--;
+    if ((picture[0].expand == 0) || (picture[0].expand == -1)) picture[0].expand = -2;
+    picture[0].X = X;
+    picture[0].Y = Y;
+    break;
+  case +1:
+    picture[0].expand++;
+    if ((picture[0].expand == 0) || (picture[0].expand == -1)) picture[0].expand = 1;
+    picture[0].X = X;
+    picture[0].Y = Y;
+    break;
+  }
+
+  Remap (graphic, image);
+  Refresh ();
+ 
+  XFlush (graphic[0].display);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Rescale.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Rescale.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Rescale.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "Ximage.h"
+
+void Rescale (Graphic *graphic, KapaImageWidget *image, Matrix *matrix) {
+
+  int i, j, DX, DY, value;
+  float *iData;
+  unsigned short *oData;
+  float slope;
+  float start;
+  unsigned short MaxValue;
+
+  // define the color transform parameters
+  MaxValue = graphic[0].Npixels - 1;
+  if (image[0].image[0].range != 0.0) {
+    slope = graphic[0].Npixels / image[0].image[0].range;
+    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
+  } else {
+    slope = 1.0;
+    start = image[0].image[0].zero;
+  }
+
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  oData = graphic[0].pixmap;
+  iData = (float *) matrix[0].buffer;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < DX; i++) {
+    for (j = 0; j < DY; j++, iData++, oData++) {
+      value = slope * iData - start;
+      if (value < 0) value = 0;
+      if (value > MaxValue) value = MaxValue;
+      *oData = value;
+    }
+  }
+}
+
+/** in order to call this function, graphic[0].pixmap must be allocated to match the
+ * current matrix (current channel) 
+ */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Resize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Resize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Resize.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "Ximage.h"
+
+// XXX Should there be a base command + KiiMessage command?
+int Resize (int sock) {
+ 
+  int i, Nsection;
+  unsigned int NX, NY;
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  KiiScanMessage (sock, "%d %d", &NX, &NY);
+
+  // XXX keep this min limit (or modify for !USE_XWINDOW)?
+  NX = MAX(NX, MIN_WIDTH); 
+  NY = MAX(NY, MIN_HEIGHT); 
+
+  // if the new size is the same as the old size, do nothing.
+  if ((graphic->dx == NX) && (graphic->dy == NY)) return (TRUE);
+
+  // set the new window size
+  graphic->dx = NX; 
+  graphic->dy = NY; 
+
+  if (USE_XWINDOW) XResizeWindow (graphic->display, graphic->window, NX, NY);
+
+  // reset the sizes for all sections
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+      section = GetSectionByNumber (i);
+      SetSectionSizes (section);
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SaveOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SaveOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SaveOverlay.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "Ximage.h"
+
+int SaveOverlay (int sock) {
+
+  int i, N;
+  char filename[256], *type;
+  FILE *f;
+  Section *section;
+  KapaImageWidget *image;
+
+  section = GetActiveSection();
+  image   = section->image;
+  if (image == NULL) return (TRUE);
+
+  KiiScanMessage (sock, "%*s %d %s", &N, filename);
+  
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "could not open %s\n", filename);
+    return (TRUE);
+  }
+
+  for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+    type = KiiOverlayTypeByNumber (image[0].overlay[N].objects[i].type);
+    if (type == NULL) continue;
+    fprintf (f, "%s %lf %lf %lf %lf\n", 
+	     type,
+	     image[0].overlay[N].objects[i].x,
+	     image[0].overlay[N].objects[i].y,
+	     image[0].overlay[N].objects[i].dx,
+	     image[0].overlay[N].objects[i].dy);
+  }
+  fclose (f);
+  return (TRUE);
+}
+
+/* this is asymmetric with LoadOverlay.c.  In that case, the client reads the file and sends
+ * the overlay objects to kapa.  In this case, kapa writes the file directly... 
+ */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Sections.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Sections.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/Sections.c	(revision 22322)
@@ -0,0 +1,218 @@
+# include "Ximage.h"
+
+static int  ActiveSection  = 0;
+static int       Nsections = 0;
+static Section **sections = NULL;
+
+Section *InitSection () {
+
+  Section *section;
+  ALLOCATE (section, Section, 1);
+
+  section[0].graph = NULL;
+  section[0].image = NULL;
+  section[0].x = 0;
+  section[0].y = 0;
+  section[0].dx = 0;
+  section[0].dy = 0;
+
+  section[0].name = NULL;
+  return (section);
+}
+
+void FreeSection (Section *section) {
+
+  if (section == NULL) return;
+  FreeGraph (section[0].graph);
+  FreeImage (section[0].image);
+  FREE (section[0].name);
+  free (section);
+}
+
+void FreeSections () {
+
+  int i;
+
+  for (i = 0; i < Nsections; i++) {
+    FreeSection (sections[i]);
+  }
+  free (sections);
+  Nsections = 0;
+  sections = NULL;
+}
+
+Section *AddSection (char *name, float x, float y, float dx, float dy) {
+
+  int N;
+
+  N = Nsections;
+
+  if (sections == NULL) {
+    Nsections = 1;
+    ALLOCATE (sections, Section *, Nsections);
+  } else {
+    Nsections ++;
+    REALLOCATE (sections, Section *, MAX (1, Nsections));
+  }
+  sections[N] = InitSection();
+  sections[N][0].name = strcreate (name);
+  sections[N][0].x = x;
+  sections[N][0].y = y;
+  sections[N][0].dx = dx;
+  sections[N][0].dy = dy;
+  ActiveSection = N;
+  return (sections[N]);
+}
+
+int DelSection (char *name) {
+
+  int i, j;
+
+  for (i = 0; i < Nsections; i++) {
+    if (strcmp (name, sections[i][0].name)) continue;
+    FreeSection (sections[i]);
+    for (j = i; j < Nsections - 1; j++) {
+      sections[j] = sections[j+1];
+    }
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+int GetSectionByName (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nsections; i++) {
+    if (strcmp (name, sections[i][0].name)) continue;
+    return (i);
+  }
+  return (-1);
+}
+
+int GetNumberOfSections () {
+  return (Nsections);
+}
+
+Section *GetSectionByNumber (int N) {
+  if (N >= Nsections) return NULL;
+  if (N < 0) return NULL;
+  return sections[N];
+}
+
+Section *GetActiveSection () {
+  int N;
+  N = ActiveSection;
+  return (sections[N]);
+}
+
+int SetActiveSectionByNumber (int N) {
+  if (N >= Nsections) return FALSE;
+  if (N < 0) return FALSE;
+  ActiveSection = N;
+  return TRUE;
+}
+
+int ListSection (int sock) {
+  
+  int i, ThisSection;
+  char name[128];
+
+  KiiScanMessage (sock, "%s", name);
+
+  if (!strcmp (name, "*")) {
+    for (i = 0; i < Nsections; i++) {
+      fprintf (stderr, "%s: %6.3f %6.3f %6.3f %6.3f\n", 
+	       sections[i][0].name, sections[i][0].x, sections[i][0].y, sections[i][0].dx, sections[i][0].dy);
+    }
+    return (TRUE);
+  }
+
+  ThisSection = -1;
+  for (i = 0; i < Nsections; i++) {
+    if (!strcmp (name, sections[i][0].name)) {
+      ThisSection = i;
+      break;
+    }
+  }
+  if (ThisSection == -1) {
+    fprintf (stderr, "section %s not found\n", name);
+    return (TRUE);
+  }
+
+  fprintf (stderr, "%s: %6.3f %6.3f %6.3f %6.3f\n", 
+	   sections[ThisSection][0].name, 
+	   sections[ThisSection][0].x,  sections[ThisSection][0].y, 
+	   sections[ThisSection][0].dx, sections[ThisSection][0].dy);
+  return (TRUE);
+}
+
+void SetSectionSizes (Section *section) {
+
+    SetGraphSize (section);
+    SetImageSize (section);
+    return;
+}
+
+// return TRUE even for nonsense cases to avoid quitting kapa
+int MoveSection (int sock) {
+
+  int i, N;
+  char name[128];
+  char direction[16];
+  Section *tmpSection = NULL;
+
+  KiiScanMessage (sock, "%s %s", name, direction);
+  
+  N = GetSectionByName (name);
+  if (N < 0) {
+    fprintf (stderr, "section %s not found\n", name);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (direction, "up")) {
+      if (N < 0) return (TRUE);
+      if (N > Nsections - 2) return (TRUE);
+      tmpSection = sections[N];
+      sections[N] = sections[N+1];
+      sections[N+1] = tmpSection;
+      Refresh (1);
+      return (TRUE);
+  }
+
+  if (!strcasecmp (direction, "down")) {
+      if (N < 1) return (TRUE);
+      if (N > Nsections - 1) return (TRUE);
+      tmpSection = sections[N];
+      sections[N] = sections[N-1];
+      sections[N-1] = tmpSection;
+      Refresh (1);
+      return (TRUE);
+  }
+
+  if (!strcasecmp (direction, "top")) {
+      if (N < 0) return (TRUE);
+      if (N > Nsections - 2) return (TRUE);
+      tmpSection = sections[N];
+      for (i = N; i < Nsections - 1; i++) {
+	sections[i] = sections[i+1];
+      }
+      sections[i] = tmpSection;
+      Refresh (1);
+      return (TRUE);
+  }
+
+  if (!strcasecmp (direction, "bottom")) {
+      if (N < 1) return (TRUE);
+      if (N > Nsections - 1) return (TRUE);
+      tmpSection = sections[N];
+      for (i = N; i >= 1; i--) {
+	sections[i] = sections[i-1];
+      }
+      sections[i] = tmpSection;
+      Refresh (1);
+      return (TRUE);
+  }
+  fprintf (stderr, "unknown direction %s for MoveSection\n", direction);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetChannel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetChannel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetChannel.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "Ximage.h"
+
+int SetChannel (int sock) {
+  
+  int Nchannel;
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  KiiScanMessage (sock, "%d", &Nchannel);
+
+  if (Nchannel <  0) return (TRUE);
+  if (Nchannel >= NCHANNELS) return (TRUE);
+  
+  image[0].image = &image[0].channel[Nchannel];
+  SetColorScale (graphic, image);
+
+  if (!USE_XWINDOW) return (TRUE);
+
+  Remap (graphic, image);
+  if (DEBUG) fprintf (stderr, "remapped image\n");
+  Refresh ();
+  if (DEBUG) fprintf (stderr, "refreshed\n");
+  XFlush (graphic[0].display);
+
+  return (TRUE);
+}
+
+int SetColormapFromPipe (int sock) {
+  
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+  char colormap[256];
+  int status;
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  KiiScanMessage (sock, "%s", colormap);
+
+  status = SetColormap (colormap);
+  if (!status) fprintf (stderr, "unknown colormap %s\n", colormap);
+
+  SetColorScale (graphic, image);
+  if (!USE_XWINDOW) return (TRUE);
+
+  Remap (graphic, image);
+  Refresh ();
+  XFlush (graphic[0].display);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetColorScale.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetColorScale.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetColorScale.c	(revision 22322)
@@ -0,0 +1,243 @@
+# include "Ximage.h"
+
+void SetColorScale (Graphic *graphic, KapaImageWidget *image) {
+
+  switch (graphic[0].ColorScaleMode) {
+    case KAPA_SCALE_1D:
+      SetColorScale1D (graphic, image);
+      break;
+    case KAPA_SCALE_3D_RUFF:
+      // fall-back on 1D colors (ie, if images are mis-matched)
+      if (!SetColorScale3D (graphic, image)) {
+	graphic[0].ColorScaleMode = KAPA_SCALE_1D;
+	SetColorScale1D (graphic, image);
+      }
+      break;
+    case KAPA_SCALE_3D_FULL:
+      // fall-back on 1D colors (ie, if images are mis-matched)
+      if (!SetColorScale3D_CC (graphic, image)) {
+	graphic[0].ColorScaleMode = KAPA_SCALE_1D;
+	SetColorScale1D (graphic, image);
+      }
+      break;
+    default:
+      fprintf (stderr, "programming error in kapa: unknown color scale mode\n");
+      return;
+  }
+  return;
+}
+
+void SetColorScale1D (Graphic *graphic, KapaImageWidget *image) {
+  int i, DX, DY, value, nPixels;
+  float *iData;
+  unsigned short *oData;
+  float slope;
+  float start;
+  unsigned short MaxValue;
+  Matrix *matrix;
+
+  // define the color transform parameters
+  MaxValue = graphic[0].Npixels - 1;
+  if (image[0].image[0].range != 0.0) {
+    slope = graphic[0].Npixels / image[0].image[0].range;
+    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
+  } else {
+    slope = 1.0;
+    start = image[0].image[0].zero;
+  }
+
+  matrix = &image->image->matrix;
+
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  nPixels = DX*DY;
+  if (image[0].nPixels != nPixels) {
+    REALLOCATE (image[0].pixmap, unsigned short, nPixels);
+    image[0].nPixels = nPixels;
+  }
+
+  oData = image[0].pixmap;
+  iData = (float *) matrix[0].buffer;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < nPixels; i++, iData++, oData++) {
+    value = *iData * slope - start;
+    if (value < 0) value = 0;
+    if (value > MaxValue) value = MaxValue;
+    *oData = value;
+  }
+}
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+// XXX this uses a crude, uniform-spacing cube
+int SetColorScale3D (Graphic *graphic, KapaImageWidget *image) {
+
+  int i, DX, DY, nPixels, rValue, gValue, bValue;
+  float *rData, *bData, *gData;
+  unsigned short *oData;
+  float redSlope, blueSlope, greenSlope;
+  float redStart, blueStart, greenStart;
+
+  DX = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[1];
+
+  if (DX != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1]) return FALSE;
+  if (DX != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[1]) return FALSE;
+
+  // define the color transform parameters
+  unsigned short maxRed = graphic[0].nRed - 1;
+  unsigned short maxBlue = graphic[0].nBlue - 1;
+  unsigned short maxGreen = graphic[0].nGreen - 1;
+
+  // set start & slope for red (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_RED].range != 0.0) {
+    redSlope = graphic[0].nRed / image[0].channel[KAPA_CHANNEL_RED].range;
+    redStart = graphic[0].nRed * image[0].channel[KAPA_CHANNEL_RED].zero / image[0].channel[KAPA_CHANNEL_RED].range;
+  } else {
+    redSlope = 1.0;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero;
+  }
+  // set start & slope for blue (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_BLUE].range != 0.0) {
+    blueSlope = graphic[0].nBlue / image[0].channel[KAPA_CHANNEL_BLUE].range;
+    blueStart = graphic[0].nBlue * image[0].channel[KAPA_CHANNEL_BLUE].zero / image[0].channel[KAPA_CHANNEL_BLUE].range;
+  } else {
+    blueSlope = 1.0;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero;
+  }
+  // set start & slope for green (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_GREEN].range != 0.0) {
+    greenSlope = graphic[0].nGreen / image[0].channel[KAPA_CHANNEL_GREEN].range;
+    greenStart = graphic[0].nGreen * image[0].channel[KAPA_CHANNEL_GREEN].zero / image[0].channel[KAPA_CHANNEL_GREEN].range;
+  } else {
+    greenSlope = 1.0;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero;
+  }
+
+  nPixels = DX*DY;
+  if (image[0].nPixels != nPixels) {
+    REALLOCATE (image[0].pixmap, unsigned short, nPixels);
+    image[0].nPixels = nPixels;
+  }
+
+  oData = image[0].pixmap;
+  rData = (float *) image[0].channel[KAPA_CHANNEL_RED].matrix.buffer;
+  bData = (float *) image[0].channel[KAPA_CHANNEL_BLUE].matrix.buffer;
+  gData = (float *) image[0].channel[KAPA_CHANNEL_GREEN].matrix.buffer;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < nPixels; i++, rData++, bData++, gData++, oData++) {
+    rValue = *rData * redSlope   - redStart;
+    if (rValue < 0) rValue = 0;
+    if (rValue > maxRed) rValue = maxRed;
+
+    bValue = *bData * blueSlope  - blueStart;
+    if (bValue < 0) bValue = 0;
+    if (bValue > maxBlue) bValue = maxBlue;
+
+    gValue = *gData * greenSlope - greenStart;
+    if (gValue < 0) gValue = 0;
+    if (gValue > maxGreen) gValue = maxGreen;
+
+    *oData = gValue + bValue * (maxGreen + 1) + rValue * (maxGreen + 1) * (maxBlue + 1);
+  }
+  return TRUE;
+}
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+int SetColorCubeHistogram () {
+
+  int i, DX, DY, Nvalues, NVALUES, Npixels, Nmin;
+  float *values;
+  CCNode *cube;
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) return (FALSE);
+  image = section->image;
+
+  DX = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[1];
+  if (DX != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1]) return FALSE;
+  if (DX != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[1]) return FALSE;
+
+  // create the top-level cube
+  if (graphic[0].cube != NULL) {
+    CCNodeFree (graphic[0].cube);
+  }
+  graphic[0].cube = CCNodeAlloc();
+
+  cube = graphic[0].cube;
+  cube->min[CC_X] = cube->min[CC_Y] = cube->min[CC_Z] = 0.0;
+  cube->max[CC_X] = cube->max[CC_Y] = cube->max[CC_Z] = 1.0;
+  cube->mid[CC_X] = cube->mid[CC_Y] = cube->mid[CC_Z] = 0.5;
+
+  // subdivide cube into first, uniform division (4x4x4)
+  CCSplitNodeIterate (cube, 0, 2);
+
+  // need to allocate at least 64 + NPASS*7*NSPLIT pixels (overestimate if 64 < NSPLIT)
+# define NPASS 5
+# define NSPLIT 128
+
+  for (i = 0; i < NPASS; i++) {
+    // generate histogram
+    ColorHistogram (image, cube);
+
+    // extract histogram values
+    values  = NULL;
+    Nvalues = 0;
+    NVALUES = 0;
+    CCNodeExtractCounts (cube, &values, &Nvalues, &NVALUES);
+  
+    // select top N cells
+    fsort (values, Nvalues);
+
+    // split nodes with more than value[n]
+    Nmin = MAX (0, Nvalues - NSPLIT);
+    CCNodeDivideLimit (cube, values[Nmin]);
+    free (values);
+
+    CCNodeInitCounts (cube, 0.0);
+  }
+
+  // generate histogram
+  ColorHistogram (image, cube);
+  
+  // convert histogram 3D values to cmap colors
+  Npixels = 0;
+  CCNodeSetColorMap (cube, graphic[0].cmap, graphic[0].Npixels, &Npixels);
+
+  // store the colors
+  if (USE_XWINDOW) {
+    if (graphic[0].visualclass) {
+      XStoreColors(graphic[0].display, graphic[0].colormap, graphic[0].cmap, Npixels);
+    } else {
+      for (i = 0; i < Npixels; i++) {
+	if (XAllocColor (graphic[0].display, graphic[0].colormap, &graphic[0].cmap[i]) == 0) {
+	  fprintf (stderr, "error on %d\n", i);
+	}
+      }
+    }
+  }
+  return TRUE;
+}
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+int SetColorScale3D_CC (Graphic *graphic, KapaImageWidget *image) {
+
+  CCNode *cube;
+
+  // create the top-level cube
+  cube = graphic[0].cube;
+  CCNodeSetColorPixels (image, cube);
+  return TRUE;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetColormap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetColormap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetColormap.c	(revision 22322)
@@ -0,0 +1,159 @@
+# include "Ximage.h"
+
+# define SETVALUE(VAR,VALUE,MINVAL,MAXVAL) { \
+  float tmp = (VALUE); \
+  if (tmp < MINVAL) { \
+    VAR = MINVAL; \
+  } else if (tmp > MAXVAL) { \
+    VAR = MAXVAL; \
+  } else { \
+    VAR = tmp; \
+  } }   
+
+int SetColormap (char *name) {
+
+  int i, red, blue, green;
+  float scale, blueRef, redRef, greenRef;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  // the "fullcolor" colormap is uniquely defined for each image;
+  // defer to the 'SetColorScale' step
+  if (!strcasecmp (name, "fullcolor")) {
+    graphic[0].ColorScaleMode = KAPA_SCALE_3D_FULL;
+    if (SetColorCubeHistogram ()) return TRUE;
+  }
+
+  // very simple color model: evenly spaced cube 
+  if (!strcasecmp (name, "ruffcolor")) {
+      graphic[0].nRed  = pow (graphic[0].Npixels, 0.333);
+      graphic[0].nBlue = pow (graphic[0].Npixels, 0.333);
+      graphic[0].nGreen = graphic[0].Npixels / (graphic[0].nRed * graphic[0].nBlue);
+
+      // red,green,blue are values in range 0x0000 to 0xffff
+      float redScale = 0xffff / (graphic[0].nRed - 1);
+      float blueScale = 0xffff / (graphic[0].nBlue - 1);
+      float greenScale = 0xffff / (graphic[0].nGreen - 1);
+
+      i = 0;
+      for (red = 0; red < graphic[0].nRed; red++) {  
+	  for (blue = 0; blue < graphic[0].nBlue; blue++) {  
+	      for (green = 0; green < graphic[0].nGreen; green++, i++) {  
+		  SETVALUE (graphic[0].cmap[i].red,   red*redScale, 0, 0xffff);
+		  SETVALUE (graphic[0].cmap[i].blue,  blue*blueScale, 0, 0xffff);
+		  SETVALUE (graphic[0].cmap[i].green, green*greenScale, 0, 0xffff);
+		  graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+	      }
+	  }
+      }
+
+      // all other modes are 1D: set the flag:
+      graphic[0].ColorScaleMode = KAPA_SCALE_3D_RUFF;
+      goto store_colors;
+  }
+
+  // all other modes are 1D: set the flag:
+  graphic[0].ColorScaleMode = KAPA_SCALE_1D;
+
+  // red,green,blue are values in range 0x0000 to 0xffff
+  scale = 0xffff / (graphic[0].Npixels - 1);
+
+  /* greyscale */
+  if ((!strcasecmp (name, "grayscale")) || (!strcasecmp (name, "greyscale"))) {
+    for (i = 0; i < graphic[0].Npixels; i++) {  
+      SETVALUE (graphic[0].cmap[i].red,   0xffff - i*scale, 0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].green, 0xffff - i*scale, 0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].blue,  0xffff - i*scale, 0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  /* -grayscale */
+  if ((!strcasecmp (name, "-grayscale")) || (!strcasecmp (name, "-greyscale"))) {
+    for (i = 0; i < graphic[0].Npixels; i++) {  
+      SETVALUE (graphic[0].cmap[i].red,   i*scale, 0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].green, i*scale, 0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].blue,  i*scale, 0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  /* heat */
+  if (!strcasecmp (name, "Heat")) {
+    greenRef = 0.25*graphic[0].Npixels*scale*2.0;
+    blueRef  = 0.50*graphic[0].Npixels*scale*2.0;
+    for (i = 0; i < (int)(0.25*graphic[0].Npixels); i++) {  
+      SETVALUE (graphic[0].cmap[i].red,   2*i*scale, 0, 0xffff);
+      graphic[0].cmap[i].green = 0;
+      graphic[0].cmap[i].blue  = 0;
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = 0.25*graphic[0].Npixels; i < 0.50*graphic[0].Npixels; i++) {  
+      SETVALUE (graphic[0].cmap[i].red,   2*i*scale,            0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].green, 2*i*scale - greenRef, 0, 0xffff);
+      graphic[0].cmap[i].blue = 0;
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.50*graphic[0].Npixels); i < (int)(0.75*graphic[0].Npixels); i++) {  
+      graphic[0].cmap[i].red = 0xffff;
+      SETVALUE (graphic[0].cmap[i].green, 2*i*scale - greenRef, 0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].blue,  2*i*scale - blueRef,  0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.75*graphic[0].Npixels); i < graphic[0].Npixels; i++) {  
+      graphic[0].cmap[i].red   = 0xffff;
+      graphic[0].cmap[i].green = 0xffff;
+      SETVALUE (graphic[0].cmap[i].blue,  2*i*scale - blueRef,  0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  /* rainbow */
+  if (!strcasecmp (name, "Rainbow")) {
+    redRef   = 0.25*graphic[0].Npixels*scale*4.0;
+    greenRef = 0.50*graphic[0].Npixels*scale*4.0;
+    blueRef  = 0.50*graphic[0].Npixels*scale*4.0;
+    for (i = 0; i < (int)(0.25*graphic[0].Npixels); i++) {  
+      graphic[0].cmap[i].red   = 0;
+      graphic[0].cmap[i].green = 0;
+      SETVALUE (graphic[0].cmap[i].blue,  4*i*scale,           0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.25*graphic[0].Npixels); i < (int)(0.50*graphic[0].Npixels); i++) {  
+      SETVALUE (graphic[0].cmap[i].red,   4*i*scale - redRef,  0, 0xffff);
+      graphic[0].cmap[i].green = 0;
+      SETVALUE (graphic[0].cmap[i].blue,  blueRef - 4*i*scale, 0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.50*graphic[0].Npixels); i < (int)(0.75*graphic[0].Npixels); i++) {  
+      graphic[0].cmap[i].red  = 0xffff;
+      SETVALUE (graphic[0].cmap[i].green,  4*i*scale - greenRef, 0, 0xffff);
+      graphic[0].cmap[i].blue = 0;
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    blueRef  = 0.75*graphic[0].Npixels*scale*4.0;
+    for (i = (int)(0.75*graphic[0].Npixels); i < graphic[0].Npixels; i++) {  
+      graphic[0].cmap[i].red   = 0xffff;
+      graphic[0].cmap[i].green = 0xffff;
+      SETVALUE (graphic[0].cmap[i].blue,  4*i*scale - blueRef, 0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  return (FALSE);
+
+ store_colors:
+  if (!USE_XWINDOW) return (TRUE);
+  if (graphic[0].visualclass) {
+    XStoreColors(graphic[0].display, graphic[0].colormap, graphic[0].cmap, graphic[0].Npixels);
+  } else {
+    for (i = 0; i < graphic[0].Npixels; i++) {
+      if (XAllocColor (graphic[0].display, graphic[0].colormap, &graphic[0].cmap[i]) == 0) {
+	fprintf (stderr, "error on %d\n", i);
+      }
+    }
+  }
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetFont.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetFont.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetFont.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "Ximage.h"
+
+int SetFont (int sock) {
+  
+  char name[64];
+  int size;
+  
+  KiiScanCommand (sock, 16, "%s", name);
+  KiiScanCommand (sock, 16, "%d", &size);
+
+  SetRotFont (name, size);
+  
+  return (TRUE);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetGraphData.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetGraphData.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetGraphData.c	(revision 22322)
@@ -0,0 +1,119 @@
+# include "Ximage.h"
+
+int SetGraphData (int sock) {
+  
+  int i;
+  double xmin, xmax, ymin, ymax;
+  Graphic *graphic;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+  
+  // get graph data from client 
+  KiiScanMessage (sock, "%d %d %d %d %d %d %lf %lf", 
+		  &graph[0].data.style, 
+		  &graph[0].data.ptype, 
+		  &graph[0].data.ltype, 
+		  &graph[0].data.etype, 
+		  &graph[0].data.ebar, 
+		  &graph[0].data.color, 
+		  &graph[0].data.lweight, 
+		  &graph[0].data.size);
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf", 
+		  &graph[0].data.xmin, 
+		  &graph[0].data.xmax, 
+		  &graph[0].data.ymin, 
+		  &graph[0].data.ymax);
+
+  KiiScanMessage (sock, "%f %f %f %f", 
+		  &graph[0].data.coords.pc1_1, &graph[0].data.coords.pc2_2,
+		  &graph[0].data.coords.pc1_2, &graph[0].data.coords.pc2_1);
+
+  KiiScanMessage (sock, "%d %d %s", 
+		  &graph[0].data.flipeast, &graph[0].data.flipnorth,
+		  graph[0].data.coords.ctype);
+
+  KiiScanMessage (sock, "%lf %lf %f %f %f %f", 
+		  &graph[0].data.coords.crval1,
+		  &graph[0].data.coords.crval2,
+		  &graph[0].data.coords.crpix1,
+		  &graph[0].data.coords.crpix2,
+		  &graph[0].data.coords.cdelt1,
+		  &graph[0].data.coords.cdelt2);
+
+
+
+  xmin = graph[0].data.xmin;
+  xmax = graph[0].data.xmax;
+  ymin = graph[0].data.ymin;
+  ymax = graph[0].data.ymax;
+
+  // XXX there are now two things which track the graph limits
+  // make sure these are kept in sync (or drop one!)
+  graph[0].axis[2].min = graph[0].axis[0].min = xmin;
+  graph[0].axis[2].max = graph[0].axis[0].max = xmax;
+  graph[0].axis[3].min = graph[0].axis[1].min = ymin; 
+  graph[0].axis[3].max = graph[0].axis[1].max = ymax;
+  
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    graph[0].objects[i].x0 = xmin;
+    graph[0].objects[i].x1 = xmax;
+    graph[0].objects[i].y0 = ymin;
+    graph[0].objects[i].y1 = ymax;
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);  
+}
+
+int GetGraphData (int sock) {
+  
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+
+  KiiSendMessage (sock, "%8d %d %d %d %d %d %f %f", 
+		  graph[0].data.style, 
+		  graph[0].data.ptype, graph[0].data.ltype, 
+		  graph[0].data.etype, graph[0].data.ebar, graph[0].data.color, 
+		  graph[0].data.lweight, graph[0].data.size);
+
+  KiiSendMessage (sock, "%g %g %g %g", 
+		  graph[0].data.xmin, graph[0].data.xmax, 
+		  graph[0].data.ymin, graph[0].data.ymax);
+
+  KiiSendMessage (sock, "%g %g %g %g", 
+		  graph[0].data.coords.pc1_1, graph[0].data.coords.pc2_2,
+		  graph[0].data.coords.pc1_2, graph[0].data.coords.pc2_1);
+
+  KiiSendMessage (sock, "%d %d %s", 
+		  graph[0].data.flipeast, graph[0].data.flipnorth,
+		  graph[0].data.coords.ctype);
+
+  KiiSendMessage (sock, "%g %g %g %g %g %g", 
+		  graph[0].data.coords.crval1,
+		  graph[0].data.coords.crval2,
+		  graph[0].data.coords.crpix1,
+		  graph[0].data.coords.crpix2,
+		  graph[0].data.coords.cdelt1,
+		  graph[0].data.coords.cdelt2);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetGraphSize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetGraphSize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetGraphSize.c	(revision 22322)
@@ -0,0 +1,128 @@
+# include "Ximage.h"
+
+/* Set the dimensions of the specific graph based on the current window size.  The graph
+   is placed within the window at the fractional position defined by the section */
+
+void SetGraphSize (Section *section) {
+
+  int fontsize, bump, Nc;
+  int textpad, textdY, WdY; 
+  double PADx, PADy, Dx, Dy;
+  double PXm, PXp, PYm, PYp;
+  double X0, Y0, dX, dY;
+  char string[64], *fontname;
+  KapaGraphWidget *graph;
+  Graphic *graphic;
+
+  if (section == NULL) return;
+  graph = section->graph;
+  if (graph == NULL) return;
+
+  graphic = GetGraphic ();
+  fontname = GetRotFont (&fontsize);
+
+  PADx = MAX (graphic->dx / 20.0, fontsize);
+  PADy = MAX (graphic->dy / 20.0, fontsize);
+  Dx = graphic->dx - 2*PADx;
+  Dy = graphic->dy - 2*PADy;
+
+  /* each graph has a padding PXm, PXp, PYm, PYp */
+  PXm = (graph[0].axis[1].islabel) ? 4*fontsize : 0;
+  PXp = (graph[0].axis[3].islabel) ? 4*fontsize : 0;
+  PYm = (graph[0].axis[0].islabel) ? 4*fontsize : 0;
+  PYp = (graph[0].axis[2].islabel) ? 4*fontsize : 0;
+ 
+  /* basic size of the graph in Xwindow coordinates */
+  X0 = PADx + PXm + (Dx * section[0].x);
+  Y0 = PADy + PYm + (Dy * section[0].y);
+  dX = (Dx * section[0].dx) - PXp - PXm;
+  dY = (Dy * section[0].dy) - PYp - PYm;
+
+  // if we are tied to an image, make mods as needed
+  if (section->image) {
+    textpad = USE_XWINDOW ? graphic[0].font[0].ascent : 10;
+    textdY = 6*textpad + 7*PAD1;
+    WdY = MAX (ZOOM_Y, textdY + 2*BUTTON_HEIGHT + PAD1);
+
+    switch (section->image->location) {
+      case 1:
+	Y0 = graphic[0].dy * section[0].y + 2*PAD1 + WdY + 2; // tied to image in Y
+	dY = graphic[0].dy * section[0].dy - 5*PAD1 - WdY - COLORPAD + 1;
+	break;
+      case 3:
+	dY = graphic[0].dy * section[0].dy - 5*PAD1 - WdY - COLORPAD - PADy - PYm;
+	break;
+      case 2:
+	X0 = graphic[0].dx * section[0].x  + 2*PAD1 + ZOOM_X;
+	dX = graphic[0].dx * section[0].dx - 3*PAD1 - ZOOM_X;
+	break;
+      case 4:
+	dX = graphic[0].dx * section[0].dx - 3*PAD1 - ZOOM_X - PADx - PXm;
+	break;
+    }
+  }
+
+  /* define locations of coordinate axes */
+  graph[0].axis[0].fx  = X0;
+  graph[0].axis[0].fy  = graphic->dy - Y0;
+  graph[0].axis[0].dfx = dX;
+  graph[0].axis[0].dfy = 0;
+  
+  graph[0].axis[1].fx  = X0;
+  graph[0].axis[1].fy  = graphic->dy - Y0;
+  graph[0].axis[1].dfx = 0;
+  graph[0].axis[1].dfy = -dY;
+
+  graph[0].axis[2].fx  = X0;
+  graph[0].axis[2].fy  = graphic->dy - Y0 - dY;
+  graph[0].axis[2].dfx = dX;
+  graph[0].axis[2].dfy = 0;
+
+  graph[0].axis[3].fx  = X0 + dX;
+  graph[0].axis[3].fy  = graphic->dy - Y0;
+  graph[0].axis[3].dfx = 0;
+  graph[0].axis[3].dfy = -dY;
+
+  PADx = 0.8*fontsize + 2;
+  PADy = 3.0*fontsize + 4;
+
+  /* define locations of axis labels */
+  graph[0].label[LABELX0].x = graph[0].axis[0].fx + 0.5*graph[0].axis[0].dfx;
+  bump = (graph[0].axis[0].islabel) ? PADy : PADx;
+  graph[0].label[LABELX0].y = graph[0].axis[0].fy + bump;
+
+  graph[0].label[LABELX1].x = graph[0].axis[2].fx + 0.5*graph[0].axis[2].dfx;
+  bump = (graph[0].axis[2].islabel) ? PADy : PADx;
+  graph[0].label[LABELX1].y = graph[0].axis[2].fy - bump;
+
+  /* check for the max size of the axis label */
+  sprintf (string, "%4g", graph[0].axis[1].min);
+  Nc = strlen (string);
+  sprintf (string, "%4g", graph[0].axis[1].max);
+  Nc = MAX (Nc, strlen (string));
+
+  graph[0].label[LABELY0].y = graph[0].axis[1].fy + 0.5*graph[0].axis[1].dfy;
+  bump = (graph[0].axis[1].islabel) ? (0.8*Nc*fontsize + 1) : PADx;
+  graph[0].label[LABELY0].x = graph[0].axis[1].fx - bump;
+
+  sprintf (string, "%4g", graph[0].axis[3].min);
+  Nc = strlen (string);
+  sprintf (string, "%4g", graph[0].axis[3].max);
+  Nc = MAX (Nc, strlen (string));
+
+  graph[0].label[LABELY1].y = graph[0].axis[3].fy + 0.5*graph[0].axis[3].dfy;
+  bump = (graph[0].axis[3].islabel) ? (0.8*Nc*fontsize + 1) : PADx;
+  graph[0].label[LABELY1].x = graph[0].axis[3].fx + bump;
+  
+  /* these are wrong and have to be adjusted to sit in the corners */
+
+  graph[0].label[LABELUL].x = graph[0].axis[2].fx - PADx;
+  graph[0].label[LABELUL].y = graph[0].axis[2].fy - PADx;
+  graph[0].label[LABELUR].x = graph[0].axis[2].fx + graph[0].axis[2].dfx + PADx;
+  graph[0].label[LABELUR].y = graph[0].axis[2].fy - PADx;
+  graph[0].label[LABELLL].x = graph[0].axis[0].fx - PADx;
+  graph[0].label[LABELLL].y = graph[0].axis[0].fy + PADx;
+  graph[0].label[LABELLR].x = graph[0].axis[0].fx + graph[0].axis[0].dfx + PADx;
+  graph[0].label[LABELLR].y = graph[0].axis[0].fy + PADx;
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetGraphStyle.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetGraphStyle.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetGraphStyle.c	(revision 22322)
@@ -0,0 +1,84 @@
+# include "Ximage.h"
+
+int SetGraphStyle (int sock) {
+  
+  int i;
+  double xmin, xmax, ymin, ymax;
+  Graphic *graphic;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+  
+  // get graph style from client 
+  KiiScanMessage (sock, "%d %d %d %d %d %d %lf %lf", 
+		  &graph[0].style.style, 
+		  &graph[0].style.ptype, 
+		  &graph[0].style.ltype, 
+		  &graph[0].style.etype, 
+		  &graph[0].style.ebar, 
+		  &graph[0].style.color, 
+		  &graph[0].style.lweight, 
+		  &graph[0].style.size);
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf", 
+		  &graph[0].style.xmin, 
+		  &graph[0].style.xmax, 
+		  &graph[0].style.ymin, 
+		  &graph[0].style.ymax);
+
+  xmin = graph[0].style.xmin;
+  xmax = graph[0].style.xmax;
+  ymin = graph[0].style.ymin;
+  ymax = graph[0].style.ymax;
+
+  // XXX there are now two things which track the graph limits
+  // make sure these are kept in sync (or drop one!)
+  graph[0].axis[2].min = graph[0].axis[0].min = xmin;
+  graph[0].axis[2].max = graph[0].axis[0].max = xmax;
+  graph[0].axis[3].min = graph[0].axis[1].min = ymin; 
+  graph[0].axis[3].max = graph[0].axis[1].max = ymax;
+  
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    graph[0].objects[i].x0 = xmin;
+    graph[0].objects[i].x1 = xmax;
+    graph[0].objects[i].y0 = ymin;
+    graph[0].objects[i].y1 = ymax;
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);  
+}
+
+int GetGraphStyle (int sock) {
+  
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+
+  KiiSendMessage (sock, "%8d %d %d %d %d %d %f %f", 
+		  graph[0].style.style, 
+		  graph[0].style.ptype, graph[0].style.ltype, 
+		  graph[0].style.etype, graph[0].style.ebar, graph[0].style.color, 
+		  graph[0].style.lweight, graph[0].style.size);
+  KiiSendMessage (sock, "%g %g %g %g", 
+		  graph[0].style.xmin, graph[0].style.xmax, 
+		  graph[0].style.ymin, graph[0].style.ymax);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetImageData.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetImageData.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetImageData.c	(revision 22322)
@@ -0,0 +1,134 @@
+# include "Ximage.h"
+
+int SetImageData (int sock) {
+  
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+  
+  // get image data from client 
+  KiiScanMessage (sock, "%lf %lf %s", 
+		  &image[0].image[0].zero, 
+		  &image[0].image[0].range, 
+		  image[0].image[0].name, 
+		  image[0].image[0].file);
+
+  // XXX when we go to 32bit, this should remap the image
+  // if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  // Refresh (1);
+
+  return (TRUE);  
+}
+
+int GetImageData (int sock) {
+  
+  Section *section;
+  KapaImageWidget *image;
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  KiiSendMessage (sock, "%g %g %s %s", 
+		  image[0].image[0].zero,
+		  image[0].image[0].range,
+		  image[0].image[0].name,
+		  image[0].image[0].file);
+
+  return (TRUE);
+}
+
+int SetImageCoords (int sock) {
+  
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+  
+  KiiScanMessage (sock, "%f %f %f %f", 
+		  &image[0].image[0].coords.pc1_1, &image[0].image[0].coords.pc2_2,
+		  &image[0].image[0].coords.pc1_2, &image[0].image[0].coords.pc2_1);
+
+  KiiScanMessage (sock, "%s", image[0].image[0].coords.ctype);
+
+  KiiScanMessage (sock, "%lf %lf %f %f %f %f", 
+		  &image[0].image[0].coords.crval1,
+		  &image[0].image[0].coords.crval2,
+		  &image[0].image[0].coords.crpix1,
+		  &image[0].image[0].coords.crpix2,
+		  &image[0].image[0].coords.cdelt1,
+		  &image[0].image[0].coords.cdelt2);
+
+  return (TRUE);  
+}
+
+int GetImageCoords (int sock) {
+  
+  Section *section;
+  KapaImageWidget *image;
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  KiiSendMessage (sock, "%g %g %g %g", 
+		  image[0].image[0].coords.pc1_1, image[0].image[0].coords.pc2_2,
+		  image[0].image[0].coords.pc1_2, image[0].image[0].coords.pc2_1);
+
+  KiiSendMessage (sock, "%s", image[0].image[0].coords.ctype);
+
+  KiiSendMessage (sock, "%g %g %g %g %g %g", 
+		  image[0].image[0].coords.crval1,
+		  image[0].image[0].coords.crval2,
+		  image[0].image[0].coords.crpix1,
+		  image[0].image[0].coords.crpix2,
+		  image[0].image[0].coords.cdelt1,
+		  image[0].image[0].coords.cdelt2);
+
+  return (TRUE);
+}
+
+int GetImageRange (int sock) {
+  
+  Section *section;
+  KapaImageWidget *image;
+  double Xmin, Xmax, Ymin, Ymax;
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  Picture_to_Image (&Xmin, &Ymin, 0.0, 0.0, &image[0].picture);
+  Picture_to_Image (&Xmax, &Ymax, image[0].picture.dx, image[0].picture.dy, &image[0].picture);
+
+  KiiSendMessage (sock, "%g %g %g %g", Xmin, Xmax, Ymin, Ymax);
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetImageSize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetImageSize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetImageSize.c	(revision 22322)
@@ -0,0 +1,380 @@
+# include "Ximage.h"
+
+/* Set the dimensions of the specific image based on the current window size.  The image
+   is placed within the window at the fractional position defined by the section */
+
+// XXX currently only set to fill the window; does not adjust for section range
+void SetImageSize (Section *section) {
+
+  int Xs, Ys, dX, dY;
+  int textpad, textdY, WdY; 
+  KapaImageWidget *image;
+  KapaGraphWidget *graph;
+  Graphic *graphic;
+
+  if (section == NULL) return;
+  image = section->image;
+  if (image == NULL) return;
+  graph = section->graph;
+
+  graphic = GetGraphic ();
+
+  /* the image is placed within the graphic window in region specified by section */
+  Xs = graphic[0].dx * section[0].x;
+  Ys = graphic[0].dy * (1 - section[0].y - section[0].dy);
+  dX = graphic[0].dx * section[0].dx;
+  dY = graphic[0].dy * section[0].dy;
+
+  textpad = USE_XWINDOW ? graphic[0].font[0].ascent : 10;
+  textdY = 6*textpad + 7*PAD1;
+  WdY = MAX (ZOOM_Y, textdY + 2*BUTTON_HEIGHT + PAD1);
+
+  switch (image[0].location) {
+
+    case 0: // no zoom / status / wide
+      if (section->graph) {
+	  image[0].picture.x  = graph[0].axis[0].fx;
+	  image[0].picture.y  = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+	  image[0].picture.dx = MAX(graph[0].axis[0].dfx + 1, 1);
+	  image[0].picture.dy = MAX(fabs(graph[0].axis[1].dfy) - 1, 1);
+      } else {
+	  image[0].picture.x  = Xs + PAD1;
+	  image[0].picture.y  = Ys + PAD1;
+	  image[0].picture.dx = dX - 2*PAD1; 
+	  image[0].picture.dy = dY - 2*PAD1;
+      }
+      if (USE_XWINDOW) CreatePicture (image, graphic);
+      Remap (graphic, image);
+      return;
+
+    case 1: // zoom / status / wide on bottom
+
+      if (section->graph) {
+	  image[0].picture.x = graph[0].axis[0].fx;
+	  image[0].picture.y = Ys + 2*PAD1 + COLORPAD;
+	  image[0].picture.dx = graph[0].axis[0].dfx;
+	  image[0].picture.dy = dY - 5*PAD1 - WdY - COLORPAD;
+      } else {
+	  image[0].picture.x  = Xs + PAD1;
+	  image[0].picture.y  = Ys + 2*PAD1 + COLORPAD;
+	  image[0].picture.dx = dX - 2*PAD1; 
+	  image[0].picture.dy = dY - 5*PAD1 - WdY - COLORPAD;
+      }
+
+      image[0].cmapbar.dx = dX - 2*PAD1; 
+      image[0].cmapbar.dy = COLORPAD;
+      image[0].cmapbar.x = Xs + PAD1;
+      image[0].cmapbar.y = Ys + PAD1;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].zoom.dx = ZOOM_X; 
+      image[0].zoom.dy = ZOOM_Y;
+      image[0].zoom.x = Xs + PAD1;
+      image[0].zoom.y = image[0].picture.y + image[0].picture.dy + PAD1;
+
+      /** everything below is tied in x-dir to the zoom box **/
+      image[0].text_x = image[0].zoom.x + image[0].zoom.dx + PAD1;
+      image[0].text_y = image[0].zoom.y;
+      image[0].text_dx = ZOOM_X;
+      image[0].text_dy = 6*textpad + 7*PAD1;
+      image[0].text_dyo = 3*textpad + 4*PAD1;
+
+      image[0].overlay_button[0].x = image[0].text_x;
+      image[0].overlay_button[0].y = image[0].text_y + image[0].text_dy + PAD1;
+   
+      image[0].overlay_button[1].x = image[0].overlay_button[0].x + image[0].overlay_button[0].dx + PAD1;
+      image[0].overlay_button[1].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[2].x = image[0].overlay_button[1].x + image[0].overlay_button[1].dx + PAD1;
+      image[0].overlay_button[2].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[3].x = image[0].overlay_button[2].x + image[0].overlay_button[2].dx + PAD1;
+      image[0].overlay_button[3].y = image[0].overlay_button[0].y;
+
+      image[0].hms_button.x = image[0].overlay_button[3].x + image[0].overlay_button[3].dx + PAD1;
+      image[0].hms_button.y = image[0].overlay_button[0].y;
+
+      image[0].PS_button.x = image[0].text_x;
+      image[0].PS_button.y = image[0].overlay_button[0].y + BUTTON_HEIGHT + PAD1;
+
+      /** everything below is tied to the PS_button in y-dir + the neighbor in x-dir **/
+      image[0].grey_button.x = image[0].PS_button.x + image[0].PS_button.dx + PAD1;
+      image[0].grey_button.y = image[0].PS_button.y;
+
+      image[0].rainbow_button.x = image[0].grey_button.x + image[0].grey_button.dx + PAD1;
+      image[0].rainbow_button.y = image[0].PS_button.y;
+
+      image[0].heat_button.x = image[0].rainbow_button.x + image[0].rainbow_button.dx + PAD1;
+      image[0].heat_button.y = image[0].PS_button.y;
+
+      image[0].recenter_button.x = image[0].heat_button.x + image[0].heat_button.dx + PAD1;
+      image[0].recenter_button.y = image[0].PS_button.y;
+
+      // add just below
+      image[0].flipx_button.x = image[0].recenter_button.x + image[0].recenter_button.dx + PAD1;
+      image[0].flipx_button.y = image[0].recenter_button.y;
+
+      image[0].flipy_button.x = image[0].hms_button.x + image[0].hms_button.dx + PAD1;
+      image[0].flipy_button.y = image[0].hms_button.y;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].wide.dx = ZOOM_X; 
+      image[0].wide.dy = ZOOM_Y;
+      image[0].wide.x = image[0].flipx_button.x + image[0].flipx_button.dx + PAD1;
+      image[0].wide.y = image[0].zoom.y;
+      break;
+
+    case 3: // zoom / status / wide on top
+
+      if (section->graph) {
+	image[0].picture.x = graph[0].axis[0].fx;
+	image[0].picture.y = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+	image[0].picture.dx = MAX(graph[0].axis[0].dfx, 1);
+	image[0].picture.dy = MAX(fabs(graph[0].axis[1].dfy) - 1, 1);
+      } else {
+	image[0].picture.dx = dX - 2*PAD1; 
+	image[0].picture.dy = dY - 5*PAD1 - WdY - COLORPAD;
+	image[0].picture.x = Xs + PAD1;
+	image[0].picture.y = Ys + 4*PAD1 + COLORPAD + WdY;
+      }
+
+      image[0].cmapbar.dx = dX - 2*PAD1; 
+      image[0].cmapbar.dy = COLORPAD;
+      image[0].cmapbar.x = Xs + PAD1;
+      image[0].cmapbar.y = Ys + PAD1;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].zoom.dx = ZOOM_X; 
+      image[0].zoom.dy = ZOOM_Y;
+      image[0].zoom.x = Xs + PAD1;
+      image[0].zoom.y = Ys + 2*PAD1 + COLORPAD;
+
+      /** everything below is tied in x-dir to the zoom box **/
+      image[0].text_x = image[0].zoom.x + image[0].zoom.dx + PAD1;
+      image[0].text_y = image[0].zoom.y;
+      image[0].text_dx = ZOOM_X;
+      image[0].text_dy = 6*textpad + 7*PAD1;
+      image[0].text_dyo = 3*textpad + 4*PAD1;
+
+      image[0].overlay_button[0].x = image[0].text_x;
+      image[0].overlay_button[0].y = image[0].text_y + image[0].text_dy + PAD1;
+   
+      image[0].overlay_button[1].x = image[0].overlay_button[0].x + image[0].overlay_button[0].dx + PAD1;
+      image[0].overlay_button[1].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[2].x = image[0].overlay_button[1].x + image[0].overlay_button[1].dx + PAD1;
+      image[0].overlay_button[2].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[3].x = image[0].overlay_button[2].x + image[0].overlay_button[2].dx + PAD1;
+      image[0].overlay_button[3].y = image[0].overlay_button[0].y;
+
+      image[0].hms_button.x = image[0].overlay_button[3].x + image[0].overlay_button[3].dx + PAD1;
+      image[0].hms_button.y = image[0].overlay_button[0].y;
+
+      image[0].PS_button.x = image[0].text_x;
+      image[0].PS_button.y = image[0].overlay_button[0].y + BUTTON_HEIGHT + PAD1;
+
+      /** everything below is tied to the PS_button in y-dir + the neighbor in x-dir **/
+      image[0].grey_button.x = image[0].PS_button.x + image[0].PS_button.dx + PAD1;
+      image[0].grey_button.y = image[0].PS_button.y;
+
+      image[0].rainbow_button.x = image[0].grey_button.x + image[0].grey_button.dx + PAD1;
+      image[0].rainbow_button.y = image[0].PS_button.y;
+
+      image[0].heat_button.x = image[0].rainbow_button.x + image[0].rainbow_button.dx + PAD1;
+      image[0].heat_button.y = image[0].PS_button.y;
+
+      image[0].recenter_button.x = image[0].heat_button.x + image[0].heat_button.dx + PAD1;
+      image[0].recenter_button.y = image[0].PS_button.y;
+
+      // add just below
+      image[0].flipx_button.x = image[0].recenter_button.x + image[0].recenter_button.dx + PAD1;
+      image[0].flipx_button.y = image[0].recenter_button.y;
+
+      image[0].flipy_button.x = image[0].hms_button.x + image[0].hms_button.dx + PAD1;
+      image[0].flipy_button.y = image[0].hms_button.y;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].wide.dx = ZOOM_X; 
+      image[0].wide.dy = ZOOM_Y;
+      image[0].wide.x = image[0].flipx_button.x + image[0].flipx_button.dx + PAD1;
+      image[0].wide.y = image[0].zoom.y;
+      break;
+
+    case 2: // zoom / status / wide on left
+
+      if (section->graph) {
+	image[0].picture.x = Xs + 2*PAD1 + ZOOM_X;
+	image[0].picture.y = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+	image[0].picture.dx = dX - 3*PAD1 - ZOOM_X; 
+	image[0].picture.dy = MAX(fabs(graph[0].axis[1].dfy) - 1, 1);
+      } else {
+	image[0].picture.dx = dX - 3*PAD1 - ZOOM_X; 
+	image[0].picture.dy = dY - 3*PAD1 - COLORPAD;
+	image[0].picture.x = Xs + 2*PAD1 + ZOOM_X;
+	image[0].picture.y = Ys + 2*PAD1 + COLORPAD;
+      }
+
+      image[0].cmapbar.dx = dX - 2*PAD1; 
+      image[0].cmapbar.dy = COLORPAD;
+      image[0].cmapbar.x = Xs + PAD1;
+      image[0].cmapbar.y = Ys + PAD1;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].zoom.dx = ZOOM_X; 
+      image[0].zoom.dy = ZOOM_Y;
+      image[0].zoom.x = Xs + PAD1;
+      image[0].zoom.y = image[0].picture.y;
+
+      /** everything below is tied in x-dir to the zoom box **/
+      image[0].text_x = image[0].zoom.x;
+      image[0].text_y = image[0].zoom.y + image[0].zoom.dy + PAD1;
+      image[0].text_dx = ZOOM_X;
+      image[0].text_dy = 6*textpad + 7*PAD1;
+      image[0].text_dyo = 3*textpad + 4*PAD1;
+
+      image[0].overlay_button[0].x = image[0].text_x;
+      image[0].overlay_button[0].y = image[0].text_y + image[0].text_dy + PAD1;
+   
+      image[0].overlay_button[1].x = image[0].overlay_button[0].x + image[0].overlay_button[0].dx + PAD1;
+      image[0].overlay_button[1].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[2].x = image[0].overlay_button[1].x + image[0].overlay_button[1].dx + PAD1;
+      image[0].overlay_button[2].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[3].x = image[0].overlay_button[2].x + image[0].overlay_button[2].dx + PAD1;
+      image[0].overlay_button[3].y = image[0].overlay_button[0].y;
+
+      image[0].hms_button.x = image[0].overlay_button[3].x + image[0].overlay_button[3].dx + PAD1;
+      image[0].hms_button.y = image[0].overlay_button[0].y;
+
+      image[0].PS_button.x = image[0].zoom.x;
+      image[0].PS_button.y = image[0].overlay_button[0].y + BUTTON_HEIGHT + PAD1;
+
+      /** everything below is tied to the PS_button in y-dir + the neighbor in x-dir **/
+      image[0].grey_button.x = image[0].PS_button.x + image[0].PS_button.dx + PAD1;
+      image[0].grey_button.y = image[0].PS_button.y;
+
+      image[0].rainbow_button.x = image[0].grey_button.x + image[0].grey_button.dx + PAD1;
+      image[0].rainbow_button.y = image[0].PS_button.y;
+
+      image[0].heat_button.x = image[0].rainbow_button.x + image[0].rainbow_button.dx + PAD1;
+      image[0].heat_button.y = image[0].PS_button.y;
+
+      image[0].recenter_button.x = image[0].heat_button.x + image[0].heat_button.dx + PAD1;
+      image[0].recenter_button.y = image[0].PS_button.y;
+
+      // add just below
+      image[0].flipx_button.x = image[0].PS_button.x;
+      image[0].flipx_button.y = image[0].PS_button.y + BUTTON_HEIGHT + PAD1;
+
+      image[0].flipy_button.x = image[0].flipx_button.x + image[0].flipx_button.dx + PAD1;
+      image[0].flipy_button.y = image[0].flipx_button.y;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].wide.dx = ZOOM_X; 
+      image[0].wide.dy = ZOOM_Y;
+      image[0].wide.x = image[0].flipx_button.x;
+      image[0].wide.y = image[0].flipx_button.y + BUTTON_HEIGHT + PAD1;
+      break;
+
+    case 4:  // zoom / status / wide on right
+
+      if (section->graph) {
+	image[0].picture.x = graph[0].axis[0].fx;
+	image[0].picture.y = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+	image[0].picture.dx = dX - 3*PAD1 - ZOOM_X - graph[0].axis[0].fx; 
+	image[0].picture.dy = MAX(fabs(graph[0].axis[1].dfy) - 1, 1);
+      } else {
+	image[0].picture.dx = dX - 3*PAD1 - ZOOM_X; 
+	image[0].picture.dy = dY - 3*PAD1 - COLORPAD;
+	image[0].picture.x = Xs + PAD1;
+	image[0].picture.y = Ys + 2*PAD1 + COLORPAD;
+      }
+
+      image[0].cmapbar.dx = dX - 2*PAD1; 
+      image[0].cmapbar.dy = COLORPAD;
+      image[0].cmapbar.x = Xs + PAD1;
+      image[0].cmapbar.y = Ys + PAD1;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].zoom.dx = ZOOM_X; 
+      image[0].zoom.dy = ZOOM_Y;
+      image[0].zoom.x = image[0].picture.x + image[0].picture.dx + PAD1;
+      image[0].zoom.y = image[0].picture.y;
+
+      /** everything below is tied in x-dir to the zoom box **/
+      image[0].text_x = image[0].zoom.x;
+      image[0].text_y = image[0].zoom.y + image[0].zoom.dy + PAD1;
+      image[0].text_dx = ZOOM_X;
+      image[0].text_dy = 6*textpad + 7*PAD1;
+      image[0].text_dyo = 3*textpad + 4*PAD1;
+
+      image[0].overlay_button[0].x = image[0].text_x;
+      image[0].overlay_button[0].y = image[0].text_y + image[0].text_dy + PAD1;
+   
+      image[0].overlay_button[1].x = image[0].overlay_button[0].x + image[0].overlay_button[0].dx + PAD1;
+      image[0].overlay_button[1].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[2].x = image[0].overlay_button[1].x + image[0].overlay_button[1].dx + PAD1;
+      image[0].overlay_button[2].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[3].x = image[0].overlay_button[2].x + image[0].overlay_button[2].dx + PAD1;
+      image[0].overlay_button[3].y = image[0].overlay_button[0].y;
+
+      image[0].hms_button.x = image[0].overlay_button[3].x + image[0].overlay_button[3].dx + PAD1;
+      image[0].hms_button.y = image[0].overlay_button[0].y;
+
+      image[0].PS_button.x = image[0].zoom.x;
+      image[0].PS_button.y = image[0].overlay_button[0].y + BUTTON_HEIGHT + PAD1;
+
+      /** everything below is tied to the PS_button in y-dir + the neighbor in x-dir **/
+      image[0].grey_button.x = image[0].PS_button.x + image[0].PS_button.dx + PAD1;
+      image[0].grey_button.y = image[0].PS_button.y;
+
+      image[0].rainbow_button.x = image[0].grey_button.x + image[0].grey_button.dx + PAD1;
+      image[0].rainbow_button.y = image[0].PS_button.y;
+
+      image[0].heat_button.x = image[0].rainbow_button.x + image[0].rainbow_button.dx + PAD1;
+      image[0].heat_button.y = image[0].PS_button.y;
+
+      image[0].recenter_button.x = image[0].heat_button.x + image[0].heat_button.dx + PAD1;
+      image[0].recenter_button.y = image[0].PS_button.y;
+
+      // add just below
+      image[0].flipx_button.x = image[0].zoom.x;
+      image[0].flipx_button.y = image[0].PS_button.y + BUTTON_HEIGHT + PAD1;
+
+      image[0].flipy_button.x = image[0].flipx_button.x + image[0].flipx_button.dx + PAD1;
+      image[0].flipy_button.y = image[0].flipx_button.y;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].wide.dx = ZOOM_X; 
+      image[0].wide.dy = ZOOM_Y;
+      image[0].wide.x = image[0].flipx_button.x;
+      image[0].wide.y = image[0].flipx_button.y + BUTTON_HEIGHT + PAD1;
+      break;
+
+    default:
+      abort ();
+      break;
+  }
+
+  if (USE_XWINDOW) {
+    CreatePicture (image, graphic);
+    CreateColorbar (image, graphic);
+    CreateZoom (graphic, image); 
+    CreateWide (graphic, image); 
+  }
+  Remap (graphic, image);
+
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetLimits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetLimits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetLimits.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "Ximage.h"
+
+int SetLimits (int sock) {
+  
+  int i;
+  double xmin, xmax, ymin, ymax;
+  Graphic *graphic;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf", &xmin, &xmax, &ymin, &ymax);
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+  
+  graph[0].axis[2].min = graph[0].axis[0].min = xmin;
+  graph[0].axis[2].max = graph[0].axis[0].max = xmax;
+  graph[0].axis[3].min = graph[0].axis[1].min = ymin; 
+  graph[0].axis[3].max = graph[0].axis[1].max = ymax;
+  
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    graph[0].objects[i].x0 = xmin;
+    graph[0].objects[i].x1 = xmax;
+    graph[0].objects[i].y0 = ymin;
+    graph[0].objects[i].y1 = ymax;
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);  
+}
+
+int GetLimits (int sock) {
+  
+  double dX, dY;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  graph = section->graph;
+
+  if (graph == NULL) {
+    dX = 0.0;
+    dY = 0.0;
+  } else {
+    dX = graph[0].axis[0].dfx;
+    dY = graph[0].axis[1].dfy;
+  }
+
+  KiiSendMessage (sock, "%8.1f %8.1f ", dX, dY);
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetNormalHints.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetNormalHints.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetNormalHints.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "Ximage.h"
+
+/************** SetNormalHints  *************/
+void SetNormalHints (Graphic *graphic) {
+
+  XSizeHints *sizehints;
+
+  sizehints = XAllocSizeHints ();
+  if (sizehints == NULL) return;
+
+  sizehints[0].x = graphic->x;
+  sizehints[0].y = graphic->x;
+  sizehints[0].width = graphic->dx;
+  sizehints[0].height = graphic->dy;
+  sizehints[0].min_width = MIN_WIDTH;
+  sizehints[0].min_height = MIN_HEIGHT;    
+
+  // XXX : can we drop the position flag 
+  // sizehints[0].flags = USPosition | USSize | PMinSize;
+  sizehints[0].flags = USSize | PMinSize;
+
+  sizehints[0].base_width = graphic->dx;
+  sizehints[0].base_height = graphic->dy;
+  sizehints[0].flags |= PBaseSize;
+    
+  XSetWMNormalHints (graphic->display, graphic->window, sizehints);
+  XFree (sizehints);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetSection.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetSection.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetSection.c	(revision 22322)
@@ -0,0 +1,19 @@
+# include "Ximage.h"
+
+// set active section
+int SetSection (int sock) {
+  
+  int N;
+  char name[128];
+
+  KiiScanMessage (sock, "%s", name);
+  
+  N = GetSectionByName (name);
+  if (N < 0) {
+    fprintf (stderr, "section %s not found\n", name);
+    return (TRUE);
+  }
+
+  SetActiveSectionByNumber (N);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetToolbox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetToolbox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetToolbox.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include "Ximage.h"
+
+// set the position of the image toolbox
+void SetToolbox (int sock) {
+
+  int location;
+  Section *section;
+  Graphic *graphic;
+
+  KiiScanMessage (sock, "%d", &location);
+  if ((location < 0) || (location > 4)) {
+    fprintf (stderr, "invalid toolbox location %d\n", location);
+    return;
+  }
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) { 
+    section->image = InitImageWidget ();
+  }
+  section->image->location = location;
+  SetSectionSizes (section);
+
+  if (!USE_XWINDOW) return;
+
+  Remap (graphic, section->image);
+  if (DEBUG) fprintf (stderr, "remapped image\n");
+  Refresh ();
+  if (DEBUG) fprintf (stderr, "refreshed\n");
+  XFlush (graphic->display);
+
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetUpGraphic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetUpGraphic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetUpGraphic.c	(revision 22322)
@@ -0,0 +1,69 @@
+# include "Ximage.h"
+# include "icons.h"
+
+static Graphic *graphic = NULL;
+
+/************** SetUpDisplay *************/
+void SetUpGraphic (int *argc, char **argv) {
+
+  Icon icon;
+  char *name;
+  int Ncolors;
+
+  ALLOCATE (graphic, Graphic, 1);
+  graphic->x  = 10;
+  graphic->y  = 10;
+  graphic->dx = 512;
+  graphic->dy = 512; 
+
+  if (!USE_XWINDOW) {
+    ALLOCATE (graphic[0].pixels, unsigned long, NPIXELS_STATIC);
+    ALLOCATE (graphic[0].cmap,   XColor,        NPIXELS_STATIC);
+    graphic[0].Npixels = NPIXELS_STATIC;
+    graphic[0].cube = NULL;
+    return;
+  }
+
+  name = CheckDisplayName (argc, argv);
+
+  graphic->display  = OpenDisplay     (name,    &graphic->screen);
+  graphic->colormap = DefaultColormap (graphic->display, graphic->screen);
+  graphic->depth    = DefaultDepth    (graphic->display, graphic->screen);
+  graphic->visual   = DefaultVisual   (graphic->display, graphic->screen);
+  if (name != NULL) free (name);
+
+  CheckVisual (graphic, argc, argv);
+  CheckColors (graphic, argc, argv);
+
+  icon.width = icon_width;
+  icon.height = icon_height;
+  icon.bits = icon_bits;
+
+  CheckGeometry (graphic, argc, argv);
+  TopWindow (graphic, &icon);
+  LoadFont (graphic, argc, argv, "fixed"); 
+
+  // FlushDisplay (graphic);
+  XSetWindowBackground (graphic->display, graphic->window, graphic->back);
+
+  SetNormalHints (graphic);
+  SetWMHints (graphic, &icon);
+
+  if (NAME_WINDOW == NULL) {
+    NameWindow (graphic, "Kapa");
+  } else {
+    ALLOCATE (name, char, strlen(NAME_WINDOW) + 10);
+    sprintf (name, "Kapa %s", NAME_WINDOW);
+    NameWindow (graphic, name);
+    free (name);
+  }
+
+  graphic->color = KapaX11colors (graphic->display, graphic->colormap, graphic->fore, &Ncolors);
+  if (MAP_WINDOW) MapWindow (graphic);
+  return;
+}
+
+Graphic *GetGraphic () {
+  return graphic;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetWMHints.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetWMHints.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/SetWMHints.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "Ximage.h"
+
+/************** SetWMHints  *************/
+void SetWMHints (Graphic *graphic, Icon *icon) {
+
+  XWMHints *wmhints;
+
+  wmhints = XAllocWMHints ();
+  if (wmhints == NULL) return;
+
+  wmhints[0].initial_state = NormalState;
+  wmhints[0].input = True;
+  if (icon[0].pixmap != (Pixmap) None) {
+    wmhints[0].icon_pixmap = icon[0].pixmap;
+    wmhints[0].icon_mask = icon[0].pixmap;
+    wmhints[0].flags = StateHint | InputHint | IconPixmapHint | IconMaskHint;
+  } else {
+    wmhints[0].flags = StateHint | InputHint;
+  }
+    
+  XSetWMHints (graphic->display, graphic->window, wmhints);
+  XFree (wmhints);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/StatusBox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/StatusBox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/StatusBox.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "Ximage.h"
+
+void StatusBox (Graphic *graphic, KapaImageWidget *image) {
+
+  double  x, y, z;
+
+  z = -1;
+
+  if (image[0].MovePointer) {
+    x = 0.5*image[0].image[0].matrix.Naxis[0];
+    y = 0.5*image[0].image[0].matrix.Naxis[1];
+    // z = -1;
+    image[0].zoom.X = x;
+    image[0].zoom.Y = y;
+    // image[0].z = z;
+  } else {
+    x = image[0].zoom.X;
+    y = image[0].zoom.Y;
+    // z = image[0].z;
+  }
+  
+  UpdateStatusBox (graphic, image, x, y, z, 1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/TopWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/TopWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/TopWindow.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "Ximage.h"
+
+/************** TopWindow *************/
+void TopWindow (Graphic *graphic, Icon *icon) {
+
+  Window rootwindow;
+
+  rootwindow = RootWindow (graphic->display, graphic->screen);
+
+  CreateWindow (graphic, rootwindow, BORDER_WIDTH, EVENT_MASK);
+  MakeGC (graphic);
+
+  icon[0].pixmap = XCreateBitmapFromData (graphic->display, graphic->window, (char *) icon[0].bits, icon[0].width, icon[0].height);
+
+  MakeCursor (graphic, DEFAULT_CURSOR);
+  XFreeCursor (graphic->display, graphic->cursor);
+  // FlushDisplay (graphic);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/UpdatePointer.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/UpdatePointer.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/UpdatePointer.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "Ximage.h"
+
+int UpdatePointer (Graphic *graphic, XMotionEvent *event) {
+
+  int textpad;
+  double  x, y, z;
+  float *data;
+  char line[100];
+  Section *section;
+  KapaImageWidget *image;
+
+  // XXX select the window element which contains the event
+  section = GetActiveSection();
+  image   = section->image;
+  if (image == NULL) return (TRUE);
+  if (!image[0].location) return (TRUE);
+
+  if (image[0].MovePointer && InPicture ((XButtonEvent *)event, &image[0].picture)) {
+
+    data = (float *) image[0].image[0].matrix.buffer;
+    Screen_to_Image (&x, &y, event[0].x + 0.5, event[0].y + 0.5, &image[0].picture);
+
+    z = -1;
+    if (x < 0) goto skip;
+    if (x >= image[0].image[0].matrix.Naxis[0]) goto skip;
+    if (y < 0) goto skip;
+    if (y >= image[0].image[0].matrix.Naxis[1]) goto skip;
+    z = data[(int)(y)*image[0].image[0].matrix.Naxis[0] + (int)(x)];
+
+  skip:
+    image[0].zoom.X = x;
+    image[0].zoom.Y = y;
+    
+    UpdateStatusBox (graphic, image, x, y, z, 0);
+    XFlush (graphic[0].display);
+      
+    CreateZoom (graphic, image);  
+    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	       image[0].zoom.pix, 0, 0, 
+	       image[0].zoom.x, image[0].zoom.y, 
+	       image[0].zoom.dx, image[0].zoom.dy);
+    CrossHairs (graphic, &image[0].zoom);
+    XFlush (graphic[0].display);
+  }
+  
+  if (InPicture ((XButtonEvent *)event, &image[0].cmapbar)) {
+    z = image[0].image[0].zero  + image[0].image[0].range * (event[0].x - image[0].cmapbar.x) / image[0].cmapbar.dx;
+    textpad = graphic[0].font[0].ascent;
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+                  image[0].text_x + 1, image[0].text_y + 1, ZOOM_X - 2, textpad + PAD1 + 1);
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+    bzero (line, 100);
+    sprintf (line, "%22.3f", z);
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+                 image[0].text_x + PAD1, image[0].text_y + textpad + PAD1, 
+		 line, strlen(line));
+    
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/UpdateStatusBox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/UpdateStatusBox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/UpdateStatusBox.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "Ximage.h"
+
+void UpdateStatusBox (Graphic *graphic, KapaImageWidget *image, double x, double y, double z, int mode) {
+
+  int textpad;
+  double ra, dec; 
+  char line[100];
+
+  XY_to_RD (&ra, &dec, x, y, &image[0].image[0].coords);
+
+  textpad = graphic[0].font[0].ascent;
+
+  if (mode) {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+		    image[0].text_x, image[0].text_y, image[0].text_dx, image[0].text_dy);  
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+    XDrawRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+		    image[0].text_x, image[0].text_y, image[0].text_dx, image[0].text_dy);
+  
+    bzero (line, 100);
+    sprintf (line, "(%d x %d) @ %d                                         ", 
+	     image[0].picture.dx, image[0].picture.dy, image[0].picture.expand); 
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		 image[0].text_x + PAD1, image[0].text_y + 4*textpad + 4*PAD1, line, 25);
+    
+    bzero (line, 100);
+    sprintf (line, "%-25s", image[0].image[0].file); 
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		 image[0].text_x + PAD1, image[0].text_y + 5*textpad + 5*PAD1, line, strlen(line));
+    
+    bzero (line, 100);
+    sprintf (line, "(%s)                                          ", image[0].image[0].name); 
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		 image[0].text_x + PAD1, image[0].text_y + 6*textpad + 6*PAD1, line, 25);
+    
+  } else {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+    // XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].overlay_color[1]);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+		    image[0].text_x+1, image[0].text_y+1, image[0].text_dx-1, image[0].text_dyo-2);  
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+  }
+  bzero (line, 100);
+  sprintf (line, "%22.3f", z);
+  XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	       image[0].text_x + PAD1, image[0].text_y + textpad + PAD1, line, strlen(line));
+  
+  bzero (line, 100);
+  sprintf (line, "%10.1f %10.1f", x, y);
+  XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	       image[0].text_x + PAD1, image[0].text_y + 2*textpad + 2*PAD1, line, strlen(line));
+  
+  bzero (line, 100);
+  if (image[0].DecimalDegrees) {
+    sprintf (line, "%10.6f %10.6f", ra, dec); 
+  } else {
+    hh_hms (line, ra, dec, ':');
+  }
+  XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	       image[0].text_x + PAD1, image[0].text_y + 3*textpad + 3*PAD1, line, strlen(line));
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/args.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "Ximage.h"
+
+int args (int *argc, char **argv) {
+
+  int N;
+
+  MAP_WINDOW = TRUE;
+  if ((N = get_argument (*argc, argv, "-nomap"))) {
+    remove_argument(N, argc, argv);
+    MAP_WINDOW = FALSE;
+  }
+
+  NAME_WINDOW = NULL;
+  if ((N = get_argument (*argc, argv, "-name"))) {
+    remove_argument(N, argc, argv);
+    NAME_WINDOW = strcreate (argv[N]);
+    remove_argument(N, argc, argv);
+  }
+
+  USE_XWINDOW = TRUE;
+  if ((N = get_argument (*argc, argv, "-noX"))) {
+    remove_argument(N, argc, argv);
+    USE_XWINDOW = FALSE;
+  }
+
+  if ((N = get_argument (*argc, argv, "-debug"))) {
+    remove_argument(N, argc, argv);
+    DEBUG = TRUE;
+  } else {
+    DEBUG = FALSE;
+  }
+
+  FOREGROUND = FALSE;
+  if ((N = get_argument (*argc, argv, "-fg"))) {
+    remove_argument(N, argc, argv);
+    FOREGROUND = TRUE;
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawFrame.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawFrame.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawFrame.c	(revision 22322)
@@ -0,0 +1,95 @@
+# include "Ximage.h"
+
+int bDrawFrame (KapaGraphWidget *graph) {
+  
+  int i, P, IsLabel, IsMajor, fontsize;
+  double fx, fy, dfx, dfy, range, major, minor, first, next;
+  char *fontname;
+
+  /* each axis is drawn independently */
+  fontname = GetRotFont (&fontsize);
+  for (i = 0; i < 4; i++) {
+    fx  = graph[0].axis[i].fx;
+    fy  = graph[0].axis[i].fy;
+    dfx = graph[0].axis[i].dfx;
+    dfy = graph[0].axis[i].dfy;
+    P = hypot ((double)graph[0].axis[(i+1)%2].dfx, (double)graph[0].axis[(i+1)%2].dfy);
+
+    if (graph[0].axis[i].isaxis) { 
+      bDrawLine (fx, fy, fx+dfx, fy+dfy); 
+    }
+    
+    if (graph[0].axis[i].areticks) {
+      if (isnan(graph[0].axis[i].min) || isinf(graph[0].axis[i].min)) continue;
+      if (isnan(graph[0].axis[i].max) || isinf(graph[0].axis[i].max)) continue;
+
+      range = graph[0].axis[i].max - graph[0].axis[i].min;
+
+      AxisTickScale (&graph[0].axis[i], &major, &minor);
+
+      first = minor*((int)(graph[0].axis[i].min/minor));
+      if ((range > 0) && (first < graph[0].axis[i].min)) {
+	first += minor;
+      }
+      if ((range < 0) && (first > graph[0].axis[i].min)) {
+	first -= minor;
+      }
+      for (next = first; ((range > 0) && (next <= graph[0].axis[i].max)) || ((range < 0) && (next >= graph[0].axis[i].max));) {
+	IsMajor = FALSE;
+	IsMajor |= (fabs((int)(next/major) - (next/major)) < 0.5*(minor/major));
+	IsMajor |= (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major));
+	IsMajor |= (fabs ((int)((next - 0.5*minor)/major) - (next/major)) < 0.5*(minor/major));
+	IsLabel = (IsMajor && graph[0].axis[i].islabel);
+	bDrawTick (fx, fy, dfx, dfy, P, graph[0].axis[i].min, graph[0].axis[i].max, next, IsLabel, IsMajor, i);
+	if (range > 0) 
+	  next += minor;
+	else 
+	  next -= minor;
+      }
+    }
+  }
+  return (TRUE);
+}
+
+void bDrawTick (double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int IsLabel, int IsMajor, int naxis) {
+  
+  int pos, dir, fontsize;
+  double size, n, x, y, dx, dy;
+  char string[64], *fontname;
+
+  pos = size = 0;
+
+  if (IsMajor) { 
+    size = MAX (0.02, 7.0 / P); 
+  } else {
+    size = MAX (0.01, 4.0 / P); 
+  }
+  
+  n = P / sqrt ((double)(dfx*dfx + dfy*dfy));
+  x = fx + (value-min)*dfx/(max - min);
+  y = fy + (value-min)*dfy/(max - min);
+
+  dir = +1;
+  if ((naxis == 0) || (naxis == 1)) dir *= -1;
+  dx = dir*size*dfy*n;	
+  dy = dir*size*dfx*n;
+  
+  bDrawLine (x, y, x+dx, y+dy);
+
+  if (IsLabel) {
+    fontname = GetRotFont (&fontsize);
+
+    /* temporarily assume rectilinear axes */
+    if (naxis == 0) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 1; }
+    if (naxis == 2) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 7; }
+
+    if (naxis == 1) { dy = 0; dx = -(0.8*fontsize + 1); pos = 3; }
+    if (naxis == 3) { dy = 0; dx = +(0.8*fontsize + 1); pos = 5; }
+
+    x = fx + (value-min)*dfx/(max - min) + dx;
+    y = fy + (value-min)*dfy/(max - min) + dy;
+    if (fabs(value) < 1e-14) { value = 0.0; }
+    sprintf (string, "%g", value);
+    bDrawRotText ((int)x, (int)y, string, pos, 0.0);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawIt.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawIt.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawIt.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "Ximage.h"
+
+bDrawBuffer *bDrawIt () {
+
+  int i, Nsection;
+  bDrawBuffer *buffer;
+  bDrawColor black;
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  black = KapaColorByName ("black");
+
+  buffer = bDrawBufferCreate (graphic->dx, graphic->dy);
+  bDrawSetBuffer (buffer);
+  bDrawSetStyle (black, 0, 0);
+  
+  // reset the sizes for all sections
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+      section = GetSectionByNumber (i);
+      bDrawGraph (section->graph);
+      // bDrawImage (section->image);
+  }
+
+  return (buffer);
+}
+
+void bDrawGraph (KapaGraphWidget *graph) {
+  if (graph == NULL) return;
+  bDrawFrame (graph); 
+  bDrawObjects (graph);
+  bDrawLabels (graph);
+  bDrawTextlines (graph);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawLabels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawLabels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawLabels.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "Ximage.h"
+  
+void bDrawLabels (KapaGraphWidget *graph) {
+  
+  int i, pos, x, y, size;
+  double angle;
+  char *fontname;
+
+  pos = 0;
+  fontname = GetRotFont (&size);
+  for (i = 0; i < 8; i++) {
+    if (strcmp (graph[0].label[i].text, "")) {
+      angle = 0;
+      switch (i) {
+      case 0: pos = 7; break;
+      case 1: pos = 1; angle = -90; break;
+      case 2: pos = 1; break;
+      case 3: pos = 1; angle =  90; break;
+      case 4: pos = 2; break;
+      case 5: pos = 0; break;
+      case 6: pos = 8; break;
+      case 7: pos = 6; break;
+      }	
+      x = graph[0].label[i].x;
+      y = graph[0].label[i].y;
+      SetRotFont (graph[0].label[i].font, graph[0].label[i].size); 
+      bDrawRotText (x, y, graph[0].label[i].text, pos, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+void bDrawTextlines (KapaGraphWidget *graph) {
+
+  int i, x, y, size;
+  double angle;
+  char *fontname;
+
+  fontname = GetRotFont (&size);
+  for (i = 0; i < graph[0].Ntextline; i++) {
+    if (strcmp (graph[0].textline[i].text, "")) {
+      angle = graph[0].textline[i].angle;
+      x = graph[0].textline[i].x;
+      y = graph[0].textline[i].y;
+      SetRotFont (graph[0].textline[i].font, graph[0].textline[i].size);
+      bDrawRotText (x, y, graph[0].textline[i].text, 5, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+/*
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|       |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+*/
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawObjects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawObjects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawObjects.c	(revision 22322)
@@ -0,0 +1,574 @@
+# include "Ximage.h"
+
+# define DrawLine(X1,Y1,X2,Y2) (bDrawLine ((X1), (Y1), (X2), (Y2)))
+# define DrawCircle(X1,Y1,R) (bDrawCircle ((X1), (Y1), (R)))
+# define DrawRectangle(X,Y,dX,dY) (bDrawRectOpen ((X-0.5*dX), (Y-0.5*dY), (X+0.5*dX), (Y+0.5*dY)))
+# define FillRectangle(X,Y,dX,dY) (bDrawRectFill ((X-0.5*dX), (Y-0.5*dY), (X+0.5*dX), (Y+0.5*dY)))
+# define FillTriangle(X1,Y1,X2,Y2,X3,Y3) (bDrawTriFill ((X1), (Y1), (X2), (Y2), (X3), (Y3)))
+# define OpenTriangle(X1,Y1,X2,Y2,X3,Y3) (bDrawTriOpen ((X1), (Y1), (X2), (Y2), (X3), (Y3)))
+# define CONNECT 0
+# define HISTOGRAM 1
+# define POINTS 2
+
+static Graphic *graphic;
+
+int bDrawObjects (KapaGraphWidget *graph) {
+  
+  int i;
+  int type;
+  int weight;
+  bDrawColor color;
+  bDrawColor black;
+  
+  graphic = GetGraphic();
+
+  black = KapaColorByName ("black");
+  for (i = 0; i < graph[0].Nobjects; i++) {
+
+    weight = MAX (0, MIN (10, graph[0].objects[i].lweight));
+    type = graph[0].objects[i].ltype;    
+    color = graph[0].objects[i].color;
+    bDrawSetStyle (color, weight, type);
+
+    switch (graph[0].objects[i].style) {
+    case CONNECT: 
+      bDrawConnect (graph, &graph[0].objects[i]);
+      break;
+    case HISTOGRAM:
+      bDrawHistogram (graph, &graph[0].objects[i]);
+      break;
+    case POINTS:
+      bDrawPoints (graph, &graph[0].objects[i]);
+      break;
+    }
+
+    if (graph[0].objects[i].etype & 0x01) {
+      bDrawYErrors (graph, &graph[0].objects[i]);
+    }
+    if (graph[0].objects[i].etype & 0x02) {
+      bDrawXErrors (graph, &graph[0].objects[i]);
+    }
+  }
+  bDrawSetStyle (black, 0, 0);
+  return (TRUE);
+}
+
+void bDrawConnect (KapaGraphWidget *graph, Gobjects *object) {
+  
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    bDrawClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+void bDrawClipLine (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0) {
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x0,y0 if outside box */
+  if ((x0 < X0) && (x1 >= X0)) {
+    y0 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X0;
+  }
+  if ((x0 > X1) && (x1 <= X1)) {
+    y0 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X1;
+  }
+  if ((y0 < Y0) && (y1 >= Y0)) {
+    x0 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y0;
+  }
+  if ((y0 > Y1) && (y1 <= Y1)) {
+    x0 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y1;
+  }
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x1,y1 if outside box */
+  if ((x1 < X0) && (x0 >= X0)) {
+    y1 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X0;
+  }
+  if ((x1 > X1) && (x0 <= X1)) {
+    y1 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X1;
+  }
+  if ((y1 < Y0) && (y0 >= Y0)) {
+    x1 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y0;
+  }
+  if ((y1 > Y1) && (y0 <= Y1)) {
+    x1 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y1;
+  }
+  DrawLine (x0, y0, x1, y1);
+}
+
+/*******/
+void bDrawHistogram (KapaGraphWidget *graph, Gobjects *object) {
+
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sxa;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  /* find the first valid datapoint */
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  sx0 = MIN (MAX (sx0, X0), X1);
+  sy0 = MAX (MIN (sy0, Y0), Y1);
+  
+  /* continue with rest of points */
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    sx1 = MIN (MAX (sx1, X0), X1);
+    sy1 = MAX (MIN (sy1, Y0), Y1);
+    sxa = 0.5*(sx0 + sx1);
+    DrawLine (sx0, sy0, sxa, sy0);
+    DrawLine (sxa, sy0, sxa, sy1);
+    DrawLine (sxa, sy1, sx1, sy1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+/*******/
+void bDrawPoints (KapaGraphWidget *graph, Gobjects *object) {
+ 
+  int i;
+  float *x, *y, *z;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx, sy, d, sx1, sy1, sx2, sy2;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);  
+ 
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);  
+
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  /* below here, code is identical with DrawObjects */
+  /**** points are scaled by object.z ***/
+  if (object[0].size < 0) {
+    d = 0.5 * (graphic->dx + graphic->dy) * 0.01;
+    x = object[0].x; y = object[0].y; z = object[0].z;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {	/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy, sx + d*z[i], sy);
+	    DrawLine (sx, sy - d*z[i], sx, sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {	/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d*z[i], sy - d*z[i], sx - d*z[i], sy + d*z[i]);
+	    DrawLine (sx - d*z[i], sy - d*z[i], sx + d*z[i], sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillTriangle (sx - d*z[i], sy - 0.58*d*z[i], sx + d*z[i], sy - 0.58*d*z[i], sx, sy + 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    OpenTriangle (sx - d*z[i], sy + 0.58*d*z[i], sx, sy - 1.15*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx,          sy - 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	/* connect a pair of points */
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  } else {
+    d = object[0].size * 0.5 * (graphic->dx + graphic->dy) * 0.003;
+    x = object[0].x; y = object[0].y;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {		/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+   	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy, sx + d, sy);
+	    DrawLine (sx, sy - d, sx, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {		/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d, sy - d, sx - d, sy + d);
+	    DrawLine (sx - d, sy - d, sx + d, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillTriangle (sx - d, sy - 0.58*d, sx + d, sy - 0.58*d, sx, sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    OpenTriangle (sx - d, sy + 0.58*d, sx + d, sy + 0.58*d, sx, sy - 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx + d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx,     sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  }
+}
+    
+/*******/
+void bDrawXErrors (KapaGraphWidget *graph, Gobjects *object) {
+  
+  int i, bar;
+  float *x, *y, *dxm, *dxp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dxp = object[0].dxp; dxm = object[0].dxm; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[1].dfy*0.03;
+   
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] + dxp[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] + dxp[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxm[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] - dxm[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] - dxm[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+  }
+}
+    
+/*******/
+void bDrawYErrors (KapaGraphWidget *graph, Gobjects *object) {
+  
+  int i, bar;
+  float *x, *y, *dym, *dyp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+  
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dyp = object[0].dyp; dym = object[0].dym; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[0].dfx*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dyp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] + dyp[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] + dyp[i])*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dym[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] - dym[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] - dym[i])*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/bDrawOverlay.c	(revision 22322)
@@ -0,0 +1,101 @@
+# include "Ximage.h"
+# define INFRONT 4
+
+static char name[4][16] = {"red", "green", "blue", "yellow"};
+
+void bDrawOverlay (KapaImageWidget *image, int N) {
+
+  int i;
+  int dX, dY, dx, dy;
+  int X, Y, Xmin, Ymin, Xmax, Ymax, Xrange, Yrange;
+  double expand, X0, Y0;
+  bDrawColor color;
+ 
+  /* translate color to bDrawColors : image[0].overlay[N].color */
+  color = KapaColorByName (name[N]);
+  bDrawSetStyle (color, 0, 0);
+  
+  expand = 1.0;
+  if (image[0].picture.expand > 0) {
+    expand = 1 / (1.0*image[0].picture.expand);
+  }
+  if (image[0].picture.expand < 0) {
+    expand = fabs((double)image[0].picture.expand);
+  }
+
+  Image_to_Screen (&X0, &Y0, 0.0, 0.0, &image[0].picture);
+  X0 -= image[0].picture.x;
+  Y0 -= image[0].picture.y;
+
+  Xmin = 0;
+  Ymin = 0;
+  Xmax = image[0].picture.dx;
+  Ymax = image[0].picture.dy;
+  Xrange = image[0].picture.dx;
+  Yrange = image[0].picture.dy;
+
+  for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+    if (N == INFRONT) {
+      X  = image[0].overlay[N].objects[i].x * Xrange;
+      Y  = image[0].overlay[N].objects[i].y * Yrange;
+      dX = image[0].overlay[N].objects[i].dx * Xrange;
+      dY = image[0].overlay[N].objects[i].dy * Yrange;
+    } else {
+      X  = image[0].overlay[N].objects[i].x/expand + X0;
+      Y =  image[0].overlay[N].objects[i].y/expand + Y0;
+      dX = image[0].overlay[N].objects[i].dx/expand;
+      dY = image[0].overlay[N].objects[i].dy/expand;
+    }
+    if ((X + dX < Xmin) || (X - dX > Xmax) ||
+	(Y + dY < Ymin) || (Y - dY > Ymax)) {
+      continue;
+    }
+
+    /* for a LINE, (x, y) is the start, (dx, dy) is the distance to end
+       for a CIRCLE (x, y) is the center, (dx, dy) is the radius 
+       for a BOX (x, y) is the center, (dx, dy) is the width */
+
+    switch (image[0].overlay[N].objects[i].type) {
+      case KII_OVERLAY_LINE:
+	bDrawLine (X, Y, (X+dX), (Y+dY));
+	break;
+      case KII_OVERLAY_TEXT:
+	bDrawRotText (X, Y, image[0].overlay[N].objects[i].text, 8, 0.0);
+	break;
+      case KII_OVERLAY_BOX:
+	dx = MAX (abs(dX),2) / 2;
+	dy = MAX (abs(dY),2) / 2;
+	bDrawRectOpen ((X-dx), (Y-dy), (X+dx), (Y+dy));
+	// bDrawRectOpen ((X-dx), (Y-dy), (X), (Y));
+	break;
+      case KII_OVERLAY_CIRCLE:
+	dx = MAX (abs(dX),2);
+	dy = MAX (abs(dY),2);
+	if (image[0].overlay[N].objects[i].angle == 0.0) {
+	  bDrawArc (X, Y, dx, dy, 0, 360);
+	} else {
+	  // very stupid rotate ellipse drawing:
+	  double x, y, t;
+	  double angle = image[0].overlay[N].objects[i].angle * RAD_DEG;
+	  double cs = cos(angle);
+	  double sn = sin(angle);
+	  // XXX dt should be based on the size of the ellipse...
+	  for (t = 0; t < 2*M_PI; t+=0.05) {
+	    x = X + dx*cos(t)*cs + dy*sin(t)*sn;
+	    y = Y - dx*cos(t)*sn + dy*sin(t)*cs;
+	    bDrawPoint (x, y);
+	  }
+	}
+	break;
+      default:
+	fprintf (stderr, "skipping unknown object\n");
+	break;
+    }
+  }
+  
+  /* translate color to bDrawColors : image[0].overlay[N].color */
+  bDrawSetStyle (color, 0, 0);
+}
+
+/* this routine is independent of the number of overlays */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/hh_hms.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/hh_hms.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/hh_hms.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "Ximage.h"
+
+void hh_hms (char *line, double ra, double dec, char sep) {
+
+  int h, m, flag;
+  double s;
+  
+  ra /= 15.0;  /* convert from degrees to hours */
+  flag = SIGN(ra);
+  ra *= flag;
+  h = ra;
+  m = 60.000001*(ra - h);
+  s = 3600*(ra - h - m / 60.0);
+  if (flag > 0)
+    sprintf (line, " %02d%c%02d%c%04.1f  ", h, sep, m, sep, s);
+  else
+    sprintf (line, "-%02d%c%02d%c%04.1f  ", h, sep, m, sep, s);
+  
+  flag = SIGN(dec);
+  dec *= flag;
+  h = dec;
+  m = 60.000001*(dec - h);
+  s = 3600*(dec - h - m / 60.0);
+  if (flag > 0)
+    sprintf (&line[13], " %02d%c%02d%c%04.1f", h, sep, m, sep, s);
+  else
+    sprintf (&line[13], "-%02d%c%02d%c%04.1f", h, sep, m, sep, s);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/kapa.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/kapa.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/src/kapa.c	(revision 22322)
@@ -0,0 +1,14 @@
+# include "Ximage.h"
+
+int main (int argc, char **argv) {
+  
+  args (&argc, argv);
+
+  SetUpGraphic (&argc, argv);
+
+  InitLayout (argc, argv);
+  EventLoop ();
+
+  CloseDisplay ();
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/test/input
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/test/input	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/test/input	(revision 22322)
@@ -0,0 +1,38 @@
+
+macro test1
+  mcreate a 512 512
+  set x = xramp(a)
+  tv x 0 512
+end
+
+macro go
+  mcreate a 512 512
+  set x = xramp(a)
+  set y = yramp(a)
+  set z = sqrt((x-256)^2 + (y-256)^2)
+  tvchannel 1
+  tv x 0 512
+  tvchannel 2
+  tv y 0 512
+  tvchannel 3
+  tv z 0 512
+end
+
+macro test
+  rd r dub.red.fits
+  rd g dub.green.fits
+  rd b dub.blue.fits
+  set R = r*(r >= 0) + (r + 256)*(r < 0)
+  set G = g*(g >= 0) + (g + 256)*(g < 0)
+  set B = b*(b >= 0) + (b + 256)*(b < 0)
+  tvchannel 1
+  tv R 0 256
+  tvchannel 2
+  tv G 0 256
+  tvchannel 3
+  tv B 0 256
+
+  tvcolor ruffcolor
+  sleep 1
+  tvcolor fullcolor
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/CheckTextLines.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/CheckTextLines.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/CheckTextLines.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "Ximage.h"
+
+/******** Here we test the TextLines specific to this program  ****
+TextLine *
+CheckTextLines (graphic, event, layout)
+Graphic         graphic[];
+XEvent          event[];
+Layout          layout[];
+{
+
+  TextLine *textline;
+  textline = (TextLine *) NULL;
+
+  if (InTextLine (graphic, event, &layout[0].zero))
+    textline = &layout[0].zero;
+  if (InTextLine (graphic, event, &layout[0].range))
+    textline = &layout[0].range;
+  if (InTextLine (graphic, event, &layout[0].effects))
+    textline = &layout[0].effects;
+  if (InTextLine (graphic, event, &layout[0].command))
+    textline = &layout[0].command;
+
+  return (textline);
+
+}
+
+
+***/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DownArrow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DownArrow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DownArrow.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int 
+DownArrow (button_event, height, width, SB_x, SB_y)
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_y > (height - SB_y)) && 
+	    (m_x > (width - SB_y)));
+    return (answer);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DrawCursor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DrawCursor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DrawCursor.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+
+
+DrawCursor (graphic, textline)    
+Graphic       graphic[];
+TextLine      textline[];
+{
+
+  int dx, label_width, DX, text_width, i;
+
+  label_width  = XTextWidth (graphic[0].font, 
+			     textline[0].label,
+			     strlen(textline[0].label));
+  
+  text_width  = XTextWidth (graphic[0].font, 
+			    textline[0].text,
+			    strlen(textline[0].text));
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+
+  if (label_width + text_width + 2*XBorder > textline[0].dx) {
+    DX = textline[0].dx - label_width - 2*XBorder - arrow_width;
+    for (i = 1; (i < strlen (textline[0].text)) && (text_width > DX); i++) {
+      text_width  = XTextWidth (graphic[0].font, 
+				&textline[0].text[i],
+				strlen(&textline[0].text[i]));
+    }
+    dx  = XTextWidth (graphic[0].font, &textline[0].text[i - 1], 
+		      textline[0].cursor - i + 1) + arrow_width;
+  }
+  else 
+    dx  = XTextWidth (graphic[0].font, textline[0].text, textline[0].cursor);
+
+  XDrawLine (graphic[0].display, 
+	     graphic[0].window, 
+	     graphic[0].gc, 
+	     textline[0].x + label_width + XBorder + dx, 
+	     textline[0].y + YBorder,
+	     textline[0].x + label_width + XBorder + dx, 
+	     textline[0].y + textline[0].dy - YBorder);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DrawTextBox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DrawTextBox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DrawTextBox.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "Ximage.h"
+# define XBorder 10
+# define YBorder 2
+# define arrow_width 6
+# define arrow_height 9
+static char arrow_bits[] = {
+  0x00, 0x02, 0x06, 0x0e, 0x1e, 0x0e, 0x06, 0x02, 0x00};
+#define down_width 9
+#define down_height 6
+static char down_bits[] = {
+   0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00};
+
+/******************* DrawTextBox ****************/
+DrawTextBox (graphic, textbox)
+Graphic      graphic[];
+TextBox      textbox[];
+{
+
+  int X, Y, y, dy, Nlines, i;
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+  XFillRectangle (graphic[0].display, 
+		  graphic[0].window,
+		  graphic[0].gc,
+		  textbox[0].x  + 1,  textbox[0].y + 1,
+		  XBorder - 2, textbox[0].dy - 2);
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  if (textbox[0].outline) {
+    XDrawRectangle (graphic[0].display, 
+		    graphic[0].window,
+		    graphic[0].gc,
+		    textbox[0].x,  textbox[0].y,
+		    textbox[0].dx, textbox[0].dy);
+  }
+
+  dy = graphic[0].font[0].ascent + graphic[0].font[0].descent;
+  Nlines = (textbox[0].dy - 2*YBorder) / dy;
+
+  for (i = 0; (i < Nlines) && (i < textbox[0].Nlines); i++) {
+    y = textbox[0].y + graphic[0].font[0].ascent + dy*i + YBorder;
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 textbox[0].x + XBorder, y,
+		 textbox[0].text[i], 
+		 strlen(textbox[0].text[i]));
+  }
+
+  if (Nlines < textbox[0].Nlines) {
+    DrawBitmap (graphic, 
+		textbox[0].x + textbox[0].dx - down_width - 2,
+		textbox[0].y + textbox[0].dy - down_height - 2,
+		down_width, down_height, down_bits, 1);
+  }
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  if (textbox[0].cursor_line != -1) {
+    Y = textbox[0].y + dy*textbox[0].cursor_line + YBorder + (dy - arrow_height)/2;
+    X = textbox[0].x + (XBorder - arrow_width) / 2;
+    DrawBitmap (graphic, X, Y, arrow_width, arrow_height, arrow_bits, 1);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DrawTextLine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DrawTextLine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/DrawTextLine.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+DrawTextLine (graphic, textline)
+Graphic  graphic[];
+TextLine textline[];
+{
+
+  int y;
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  if (textline[0].outline) {
+    XDrawRectangle (graphic[0].display, 
+		    graphic[0].window,
+		    graphic[0].gc,
+		    textline[0].x,  textline[0].y,
+		    textline[0].dx, textline[0].dy);
+  }
+
+  y = textline[0].y + (textline[0].dy + graphic[0].font[0].ascent) / 2;
+  XDrawString (graphic[0].display, 
+	       graphic[0].window, 
+	       graphic[0].gc, 
+	       textline[0].x + XBorder, y,
+	       textline[0].label, 
+	       strlen(textline[0].label));
+  RedrawString (graphic, textline);
+
+  if (textline[0].cursor != -1)
+    DrawCursor (graphic, textline);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/InTextBox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/InTextBox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/InTextBox.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "Ximage.h"
+# define XBorder 10
+# define YBorder 2
+# define arrow_width 6
+# define arrow_height 9
+static char arrow_bits[] = {
+  0x00, 0x02, 0x06, 0x0e, 0x1e, 0x0e, 0x06, 0x02, 0x00};
+#define down_width 9
+#define down_height 6
+static char down_bits[] = {
+   0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00};
+
+/******************* InTextBox ****************/
+int
+InTextBox (graphic, event, textbox)
+Graphic          graphic[];
+XButtonEvent    *event;
+TextBox          textbox[];
+{
+
+  int answer;
+  int i, x, y, Y, X, dy;
+  int minX, maxX, minY, maxY;
+  
+  x = event[0].x;
+  y = event[0].y;
+  dy = graphic[0].font[0].ascent + graphic[0].font[0].descent;
+  
+  minX = textbox[0].x;
+  maxX = textbox[0].x + textbox[0].dx;
+  minY = textbox[0].y;
+  maxY = textbox[0].y + textbox[0].dy;
+  if (dy*textbox[0].Nlines + 2*YBorder < textbox[0].dy)
+    maxY = textbox[0].y + dy*textbox[0].Nlines + 2*YBorder;
+
+  answer = ((x >= minX) && (x <= maxX) && (y >= minY) && (y <= maxY));
+  
+  /* find the cursor position, erase the old arrow and draw a new arrow */
+  if (answer) {
+    if (textbox[0].cursor_line != -1) {
+      Y = textbox[0].y + dy*textbox[0].cursor_line + YBorder + (dy - arrow_height)/2;
+      X = textbox[0].x + (XBorder - arrow_width) / 2;
+      DrawBitmap (graphic, X, Y, arrow_width, arrow_height, arrow_bits, 0);
+    }
+    textbox[0].cursor_line = (y - textbox[0].y - YBorder)/dy;
+    X = textbox[0].x + (XBorder - arrow_width) / 2;
+    Y = textbox[0].y + dy*textbox[0].cursor_line + YBorder + (dy - arrow_height)/2;
+    DrawBitmap (graphic, X, Y, arrow_width, arrow_height, arrow_bits, 1);
+    textbox[0].cursor_x = x;
+    textbox[0].cursor_y = y;
+    XFlush (graphic[0].display);
+  }
+  
+  return (answer);
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/InTextLine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/InTextLine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/InTextLine.c	(revision 22322)
@@ -0,0 +1,58 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+int
+InTextLine (graphic, event, textline)
+Graphic          graphic[];
+XButtonEvent    *event;
+TextLine         textline[];
+{
+
+  int answer, done;
+  int i, x, dx, y, text_width, label_width;
+  int minX, maxX, minY, maxY;
+  char testline[1000];
+
+  x = event[0].x;
+  y = event[0].y;
+  
+  label_width  = XTextWidth (graphic[0].font, 
+			     textline[0].label,
+			     strlen(textline[0].label));
+  
+  minX = textline[0].x + XBorder + label_width;
+  maxX = textline[0].x + textline[0].dx;
+  minY = textline[0].y;
+  maxY = textline[0].y + textline[0].dy;
+
+  answer = ((x >= minX) && (x <= maxX) && (y >= minY) && (y <= maxY));
+
+  /* find the cursor position and draw a vertical bar */
+  if (answer) {
+    text_width  = XTextWidth (graphic[0].font, 
+			      textline[0].text,
+			      strlen(textline[0].text));
+    
+    if (x >= minX + text_width) {
+      textline[0].cursor = strlen (textline[0].text);
+    }
+    else {
+      for (i = 1, done = FALSE; (i <= strlen (textline[0].text)) && !done; i++) {
+	dx  = XTextWidth (graphic[0].font, 
+			  textline[0].text, i);
+	if (x < minX + dx) {
+	  textline[0].cursor = i - 1;
+	  done = TRUE;
+	}
+      }
+    }
+    DrawCursor (graphic, textline);
+  }    
+  XFlush (graphic[0].display);
+  
+  return (answer);
+  
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/LeftArrow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/LeftArrow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/LeftArrow.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+LeftArrow (button_event, height, width, SB_x, SB_y) 
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_x < SB_x) && (m_y < SB_x));
+  return (answer);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/RedrawString.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/RedrawString.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/RedrawString.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+
+#define arrow_width 6
+#define arrow_height 9
+RedrawString (graphic, textline)
+Graphic       graphic[];
+TextLine      textline[];
+{
+
+  int label_width, text_width, y, i, DX;
+  static char arrow_bits[] = {
+    0x00, 0x10, 0x18, 0x1c, 0x1e, 0x1c, 0x18, 0x10, 0x00};
+  
+  label_width  = XTextWidth (graphic[0].font, 
+			     textline[0].label,
+			     strlen(textline[0].label));
+  
+  text_width  = XTextWidth (graphic[0].font, 
+			    textline[0].text,
+			    strlen(textline[0].text));
+
+  if (label_width + text_width + 2*XBorder > textline[0].dx) {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		    textline[0].x + XBorder + label_width, 
+		    textline[0].y + YBorder, 
+		    textline[0].dx - XBorder - label_width, 
+		    textline[0].dy - 2*YBorder + 1);
+    
+    DX = textline[0].dx - label_width - 2*XBorder - arrow_width;
+    for (i = 1; (i < strlen (textline[0].text)) && (text_width > DX); i++) {
+      text_width  = XTextWidth (graphic[0].font, 
+				&textline[0].text[i],
+				strlen(&textline[0].text[i]));
+    }
+
+    y = textline[0].y + (textline[0].dy + graphic[0].font[0].ascent) / 2;
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+    DrawBitmap (graphic, 
+		textline[0].x + XBorder + label_width,
+		textline[0].y + YBorder + (textline[0].dy - arrow_height)/2,
+		arrow_width, arrow_height, arrow_bits, 1);
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 textline[0].x + XBorder + label_width + arrow_width, y,
+		 &textline[0].text[i - 1], 
+		 strlen(&textline[0].text[i - 1]));
+    XFlush (graphic[0].display);
+  }
+  else {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		    textline[0].x + XBorder + label_width, 
+		    textline[0].y + YBorder, 
+		    textline[0].dx - XBorder - label_width, 
+		    textline[0].dy - 2*YBorder + 1);
+    
+    y = textline[0].y + (textline[0].dy + graphic[0].font[0].ascent) / 2;
+    
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 textline[0].x + XBorder + label_width, y,
+		 textline[0].text, 
+		 strlen(textline[0].text));
+  }
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/RightArrow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/RightArrow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/RightArrow.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+RightArrow (button_event, height, width, SB_x, SB_y) 
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_x < SB_x) && 
+	    (m_y > (height - SB_x - SB_y)) && 
+	    (m_y < (height - SB_y)));
+  return (answer);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/TextLineEntry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/TextLineEntry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/TextLineEntry.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+int 
+TextLineEntry (graphic, textline, keyevent)
+Graphic        graphic[];
+TextLine       textline[];
+XKeyEvent     *keyevent;
+{
+
+  int i, status, N;
+  char dummy[3];
+  XComposeStatus compose_status;
+  KeySym keysym;
+
+  status = TRUE;
+  strcpy (textline[0].old_text, textline[0].text);
+  bzero (&textline[0].text[strlen(textline[0].text)], 
+	 1024 - strlen(textline[0].text));
+  i = textline[0].cursor;
+
+
+  N = XLookupString (keyevent, dummy, 1, &keysym, &compose_status);
+  switch (keysym) {
+  case XK_BackSpace:
+  case XK_Delete:
+    if (textline[0].cursor > 0) {
+      bcopy (&textline[0].text[i], &textline[0].text[i - 1], 
+	     strlen(&textline[0].text[i]) + 1);
+      RedrawString (graphic, textline);
+      textline[0].cursor --;
+      DrawCursor (graphic, textline);
+    }
+    break;
+  case XK_Left:
+    RedrawString (graphic, textline);
+    textline[0].cursor --;
+    if (textline[0].cursor < 0)
+      textline[0].cursor = 0;
+    DrawCursor (graphic, textline);
+    break;
+  case XK_Right:
+    RedrawString (graphic, textline);
+    textline[0].cursor ++;
+    if (textline[0].cursor > strlen (textline[0].text))
+      textline[0].cursor = strlen (textline[0].text);
+    DrawCursor (graphic, textline);
+    break;
+  case XK_Return:
+    RedrawString (graphic, textline);
+    textline[0].cursor = -1;
+    status = FALSE;
+    break;
+  case XK_d:
+  case XK_D: 
+    if (dummy[0] == 4) { /* a control-d was typed! */
+      if (textline[0].cursor < strlen(textline[0].text)) {
+	bcopy (&textline[0].text[i + 1], &textline[0].text[i], 
+	       strlen(&textline[0].text[i]));
+	RedrawString (graphic, textline);
+	DrawCursor (graphic, textline);
+      }
+      break;  /* WARNING: this case MUST come before default.
+		 anything between this and default will be executed
+		 if no cntl-d is typed! */
+    }
+  default:
+    if (N != 0) {
+      bcopy (&textline[0].text[i], &textline[0].text[i + 1], 
+	     strlen(&textline[0].text[i]));
+      textline[0].text[i] = dummy[0];
+      RedrawString (graphic, textline);
+      textline[0].cursor ++;
+      DrawCursor (graphic, textline);
+    }
+  }
+  return (status);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/UpArrow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/UpArrow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/UpArrow.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+UpArrow (button_event, height, width, SB_x, SB_y) 
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_y > (height - SB_y)) && 
+	    (m_x > SB_x) && 
+	    (m_x < (SB_x + SB_y)));
+  return (answer);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/xScrollBar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/xScrollBar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/xScrollBar.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int 
+xScrollBar (button_event, height, width, SB_x, SB_y, f)
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+double *f;
+{
+
+  int answer, m_x, m_y;
+  double f1, f2;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_x < SB_x) && 
+	    (m_y > SB_x) && 
+	    (m_y < (height - SB_x - SB_y)));
+  f1 = (double)(m_y - SB_x);
+  f2 = (double)(height - 2*SB_x - SB_y);
+  *f = f1 / f2;
+  return (answer);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/yScrollBar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/yScrollBar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kapa2/textbox/yScrollBar.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+yScrollBar (button_event, height, width, SB_x, SB_y, f)
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+double *f;
+{
+
+  int answer, m_x, m_y;
+  double f1, f2;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_y > (height - SB_y)) && 
+	    (m_x > (SB_x + SB_y)) && 
+	    (m_x < (width - SB_y)));
+  f1 = (double)(m_x - SB_x - SB_y);
+  f2 = (double)(width - 2*SB_y - SB_x);
+  *f = f1 / f2;
+  return (answer);
+
+}
+
+	
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/Makefile	(revision 22322)
@@ -0,0 +1,96 @@
+default: kii
+help:
+	@echo "make options: kii (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/kii
+LIB	=	$(HOME)/lib
+BIN	=	$(HOME)/bin
+BDIR	=	$(HOME)/button
+CDIR	=	$(HOME)/colorbar
+EDIR	=	$(HOME)/event
+MDIR	=	$(HOME)/misc
+ODIR	=	$(HOME)/overlay
+PDIR	=	$(HOME)/picture
+SDIR	=	$(HOME)/setup
+ZDIR	=	$(HOME)/zoom
+INC	=	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+kii: $(BIN)/kii.$(ARCH)
+install: $(DESTBIN)/kii
+
+# local source / object files ########################
+BOBJ = \
+$(BDIR)/ButtonFunctions.$(ARCH).o         $(BDIR)/InButton.$(ARCH).o           \
+$(BDIR)/CheckButtons.$(ARCH).o     	  $(BDIR)/InPicture.$(ARCH).o          \
+$(BDIR)/DrawButton.$(ARCH).o       	  $(BDIR)/InvertButton.$(ARCH).o       \
+$(BDIR)/FlashButton.$(ARCH).o      	  $(BDIR)/PSit.$(ARCH).o		\
+$(BDIR)/JPEGit.$(ARCH).o		  $(BDIR)/ConvertPixmap.$(ARCH).o
+
+COBJ = \
+$(CDIR)/CreateColorbar.$(ARCH).o          $(CDIR)/SetColormap.$(ARCH).o        \
+$(CDIR)/DragColorbar.$(ARCH).o
+
+EOBJ = \
+$(EDIR)/CheckPipe.$(ARCH).o               $(EDIR)/Stop.$(ARCH).o               \
+$(EDIR)/EventLoop.$(ARCH).o         	  $(EDIR)/Reconfig.$(ARCH).o           \
+$(EDIR)/InterpretKeys.$(ARCH).o     	  $(EDIR)/Refresh.$(ARCH).o            \
+$(EDIR)/InterpretPresses.$(ARCH).o  	  $(EDIR)/FlushDisplay.$(ARCH).o
+
+MOBJ = \
+$(MDIR)/hh_hms.$(ARCH).o		  
+
+OOBJ = \
+$(ODIR)/EraseOverlay.$(ARCH).o            $(ODIR)/PaintOverlay.$(ARCH).o       \
+$(ODIR)/LoadOverlay.$(ARCH).o   	  $(ODIR)/SaveOverlay.$(ARCH).o        \
+$(ODIR)/DrawOverlay.$(ARCH).o		  $(ODIR)/CSaveOverlay.$(ARCH).o       \
+$(ODIR)/PaintTickmarks.$(ARCH).o          $(ODIR)/LoadTickmarks.$(ARCH).o      \
+$(ODIR)/bDrawOverlay.$(ARCH).o
+
+POBJ = \
+$(PDIR)/CreatePicture.$(ARCH).o           $(PDIR)/Reorient.$(ARCH).o           \
+$(PDIR)/NewPicture.$(ARCH).o        	  $(PDIR)/Center.$(ARCH).o             \
+$(PDIR)/PositionPictures.$(ARCH).o  	  $(PDIR)/StatusBox.$(ARCH).o          \
+$(PDIR)/CursorOps.$(ARCH).o 		  $(PDIR)/Resize.$(ARCH).o 		\
+$(PDIR)/Remap8.$(ARCH).o 		  $(PDIR)/Remap24.$(ARCH).o 		\
+$(PDIR)/Remap.$(ARCH).o             	  $(PDIR)/Remap32.$(ARCH).o		\
+$(PDIR)/Remap16.$(ARCH).o		  $(PDIR)/JPEGit24.$(ARCH).o            \
+$(PDIR)/cursor.$(ARCH).o
+
+SOBJ = \
+$(SDIR)/CheckColors.$(ARCH).o             $(SDIR)/MakeGC.$(ARCH).o             \
+$(SDIR)/CheckDisplayName.$(ARCH).o  	  $(SDIR)/MapWindow.$(ARCH).o          \
+$(SDIR)/CheckFontName.$(ARCH).o     	  $(SDIR)/NameWindow.$(ARCH).o         \
+$(SDIR)/CheckGeometry.$(ARCH).o     	  $(SDIR)/OpenDisplay.$(ARCH).o        \
+$(SDIR)/CloseDisplay.$(ARCH).o      	  $(SDIR)/QuitX.$(ARCH).o              \
+$(SDIR)/CreateWindow.$(ARCH).o      	  $(SDIR)/SetNormalHints.$(ARCH).o     \
+$(SDIR)/DefineLayout.$(ARCH).o      	  $(SDIR)/SetUpDisplay.$(ARCH).o       \
+$(SDIR)/DrawBitmap.$(ARCH).o        	  $(SDIR)/SetUpWindow.$(ARCH).o        \
+$(SDIR)/GetColor.$(ARCH).o          	  $(SDIR)/SetWMHints.$(ARCH).o         \
+$(SDIR)/LoadFont.$(ARCH).o          	  $(SDIR)/TopWindow.$(ARCH).o          \
+$(SDIR)/MakeColormap.$(ARCH).o      	  $(SDIR)/Ximage.$(ARCH).o             \
+$(SDIR)/MakeCursor.$(ARCH).o              $(SDIR)/CheckVisual.$(ARCH).o	       \
+$(SDIR)/args.$(ARCH).o
+
+ZOBJ = \
+$(ZDIR)/UpdatePointer.$(ARCH).o      \
+$(ZDIR)/CrossHairs.$(ARCH).o		  $(ZDIR)/UpdateStatusBox.$(ARCH).o	\
+$(ZDIR)/CreateZoom.$(ARCH).o              $(ZDIR)/CreateZoom32.$(ARCH).o        \
+$(ZDIR)/CreateZoom8.$(ARCH).o             $(ZDIR)/CreateZoom24.$(ARCH).o	\
+$(ZDIR)/CreateZoom16.$(ARCH).o
+
+OBJ  =  $(BOBJ) $(COBJ) $(EOBJ) $(MOBJ) $(OOBJ) $(POBJ) $(SOBJ) $(ZOBJ)
+
+# dependancy rules for include files ########################
+$(OBJ): $(INC)/Ximage.h \
+	$(INC)/constants.h \
+	$(INC)/structures.h \
+	$(INC)/prototypes.h
+
+$(BIN)/kii.$(ARCH): $(OBJ)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/ButtonFunctions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/ButtonFunctions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/ButtonFunctions.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include "Ximage.h"
+
+static char *GREY = "greyscale";
+static char *PUNS = "Puns";
+static char *RAINBOW = "Rainbow";
+
+int greycolors (Graphic *graphic, Layout *layout) {
+  char *name;
+  name = GREY;
+  SetColormap (graphic, layout, name);
+  CreateColorbar (layout, graphic);
+  Remap (graphic, layout, &layout[0].matrix);
+  CreateZoom (layout, graphic, 0, 0); 
+  Refresh (graphic, layout, 0);
+  XFlush (graphic[0].display);
+  return (TRUE);
+}
+
+int puns (Graphic *graphic, Layout *layout) {
+  char *name;
+  name = PUNS;
+  SetColormap (graphic, layout, name);
+  CreateColorbar (layout, graphic);
+  Remap (graphic, layout, &layout[0].matrix);
+  CreateZoom (layout, graphic, 0, 0); 
+  Refresh (graphic, layout, 0);
+  XFlush (graphic[0].display);
+  return (TRUE);
+}
+
+int rainbow (Graphic *graphic, Layout *layout) {
+  char *name;
+  name = RAINBOW;
+  SetColormap (graphic, layout, name);
+  CreateColorbar (layout, graphic);
+  Remap (graphic, layout, &layout[0].matrix);
+  CreateZoom (layout, graphic, 0, 0); 
+  Refresh (graphic, layout, 0);
+  XFlush (graphic[0].display);
+  return (TRUE);
+}
+
+int Recenter (Graphic *graphic, Layout *layout) {
+
+  layout[0].X = 0;
+  layout[0].Y = 0;
+ 
+  Remap (graphic, layout, &layout[0].matrix);
+  Refresh (graphic, layout, 0);
+  FlushDisplay (graphic[0].display);
+  return (TRUE);
+
+}
+
+int Rescale (Graphic *graphic, Layout *layout) {
+
+  layout[0].expand = 1;
+  Remap (graphic, layout, &layout[0].matrix);
+  Refresh (graphic, layout, 0);
+  FlushDisplay (graphic[0].display);
+  return (TRUE);
+
+}
+
+int RecenterRescale (Graphic *graphic, Layout *layout) {
+
+  layout[0].X = 0;
+  layout[0].Y = 0;
+  layout[0].expand = 1;
+ 
+  Remap (graphic, layout, &layout[0].matrix);
+  Refresh (graphic, layout, 0);
+  FlushDisplay (graphic[0].display);
+  return (TRUE);
+
+}
+
+int ToggleDEG (Graphic *graphic, Layout *layout) {
+
+  DECIMAL_DEG = DECIMAL_DEG ^ TRUE;
+  StatusBox (graphic, layout);
+  FlushDisplay (graphic[0].display);
+  return (TRUE);
+
+}
+
+/*********** overlay_button functions ************/
+int Overlay0 (Graphic *graphic, Layout *layout) {
+
+  OVERLAY[0] = OVERLAY[0] ^ TRUE;
+  Refresh (graphic, layout, 0);
+  FlushDisplay (graphic[0].display);
+  return (TRUE);
+
+}
+
+int Overlay1 (Graphic *graphic, Layout *layout) {
+
+  OVERLAY[1] = OVERLAY[1] ^ TRUE;
+  Refresh (graphic, layout, 0);
+  FlushDisplay (graphic[0].display);
+  return (TRUE);
+
+}
+
+int Overlay2 (Graphic *graphic, Layout *layout) {
+
+  OVERLAY[2] = OVERLAY[2] ^ TRUE;
+  Refresh (graphic, layout, 0);
+  FlushDisplay (graphic[0].display);
+  return (TRUE);
+
+}
+
+int Overlay3 (Graphic *graphic, Layout *layout) {
+
+  OVERLAY[3] = OVERLAY[3] ^ TRUE;
+  Refresh (graphic, layout, 0);
+  FlushDisplay (graphic[0].display);
+  return (TRUE);
+
+}
+
+/* this routine is NOT independent of the number of overlays */
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/CheckButtons.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/CheckButtons.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/CheckButtons.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "Ximage.h"
+# define PAD_X 10
+# define PAD_Y 10
+# define NPLANES 1
+# define NPIXELS 255
+
+/******** Here we test the Buttons specific to this program  *******/
+Button *
+CheckButtons (event, layout)
+XButtonEvent  event[];
+Layout        layout[];
+{
+
+  int i;
+  Button *button;
+  button = (Button *) NULL;
+
+  if (InButton (event, &layout[0].recenter_button)) 
+    button = &layout[0].recenter_button;
+
+  if (InButton (event, &layout[0].grey_button)) 
+    button = &layout[0].grey_button;
+
+  if (InButton (event, &layout[0].rainbow_button)) 
+    button = &layout[0].rainbow_button;
+
+  if (InButton (event, &layout[0].puns_button)) 
+    button = &layout[0].puns_button;
+
+  if (InButton (event, &layout[0].PS_button)) 
+    button = &layout[0].PS_button;
+
+  if (InButton (event, &layout[0].hms_button)) 
+    button = &layout[0].hms_button;
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (InButton (event, &layout[0].overlay_button[i])) 
+      button = &layout[0].overlay_button[i];
+  }
+
+  return (button);
+
+}
+
+
+/* To define a button, you must:
+
+   0) add the button to the Layout structure in structures.h
+   1) place the info about the button in PositionPicture.c
+   2) (make any bitmaps needed and put them in buttons.h
+   3) place an entry in CheckButtons.c
+   4) Add the button to Refresh.c
+   5) create the button's function
+   6) add it to the Makefile
+   7) add it to the prototypes
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/ConvertPixmap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/ConvertPixmap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/ConvertPixmap.c	(revision 22322)
@@ -0,0 +1,262 @@
+# include "Ximage.h"
+
+void ConvertPixmap8 (Layout *layout, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned char *buff;
+
+  Nchar = 255.0;
+  Npix = layout[0].Npixels;
+  frac = Nchar / Npix;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned char *)layout[0].picture.data + layout[0].picture.dx*(layout[0].picture.dy - 1);
+  slope = layout[0].slope;
+  start = layout[0].start;
+
+  for (i = 0; i < layout[0].picture.dy; i++) {
+    for (k = 0; k < layout[0].picture.dx; k++, buff++) {
+      if (*buff == layout[0].white) 
+	val = Nchar;
+      else {
+	for (m = 0; (layout[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*layout[0].picture.dx;
+  }
+  return;
+}
+
+void ConvertPixmap16 (Layout *layout, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned short *buff;
+
+  Nchar = 255.0;
+  Npix = layout[0].Npixels;
+  frac = Nchar / Npix;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned short *)layout[0].picture.data + layout[0].picture.dx*(layout[0].picture.dy - 1);
+  slope = layout[0].slope;
+  start = layout[0].start;
+
+  for (i = 0; i < layout[0].picture.dy; i++) {
+    for (k = 0; k < layout[0].picture.dx; k++, buff++) {
+      if (*buff == layout[0].white) 
+	val = Nchar;
+      else {
+	for (m = 0; (layout[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*layout[0].picture.dx;
+  }
+  return;
+}
+
+void ConvertPixmap24 (Layout *layout, FILE *f) {
+
+  int i, k, m, dx, dy, val, extra;
+  unsigned char *buff;
+  unsigned long color, byte;
+  double Nchar, Npix, start, slope, frac;
+
+  Nchar = 255.0;
+  Npix = layout[0].Npixels;
+  frac = Nchar / Npix;
+  dx = layout[0].picture.dx;
+  dy = layout[0].picture.dy;
+  extra = 4 - (dx * 3) % 4;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned char *)&layout[0].picture.data[(dy - 1)*(3*dx + extra)];
+  slope = layout[0].slope;
+  start = layout[0].start;
+
+  for (i = 0; i < dy; i++) {
+    for (k = 0; k < dx; k++, buff+=3) {
+      color = 0;
+      byte = buff[2];
+      color = (byte << 16);
+      byte = buff[1];
+      color |= (byte << 8);
+      byte = buff[0];
+      color |= byte;
+      for (m = 0; (layout[0].cmap[m].pixel != color) && (m < Npix); m++);
+      val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*3*dx + extra;
+  }
+  return;
+}
+
+void ConvertPixmap32 (Layout *layout, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned int *buff;
+
+  Nchar = 255.0;
+  Npix = layout[0].Npixels;
+  frac = Nchar / Npix;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned int *)layout[0].picture.data + layout[0].picture.dx*(layout[0].picture.dy - 1);
+  slope = layout[0].slope;
+  start = layout[0].start;
+
+  for (i = 0; i < layout[0].picture.dy; i++) {
+    for (k = 0; k < layout[0].picture.dx; k++, buff++) {
+      if (*buff == layout[0].white) 
+	val = Nchar;
+      else {
+	for (m = 0; (layout[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*layout[0].picture.dx;
+  }
+  return;
+}
+
+# if (0)
+void ConvertPixmap (Layout *layout, File *f) {
+  
+  /* set up expansions */
+  expand = expand_in = expand_out = 1.0;
+  if (layout[0].expand == 0)
+    layout[0].expand = 1;
+  if (layout[0].expand > 0) {
+    expand = 1 / (1.0*layout[0].expand);
+    expand_out = layout[0].expand;
+    expand_in  = 1;
+  }
+  if (layout[0].expand < 0) {
+    expand = fabs((double)layout[0].expand);
+    expand_out = 1;
+    expand_in  = -layout[0].expand;
+  }
+
+  dx = layout[0].picture.dx;
+  dy = layout[0].picture.dy;
+  DX = layout[0].matrix.Naxis[0];
+  DY = layout[0].matrix.Naxis[1];
+
+  /* X,Y are the image coordinates of the first image pixel */
+  X = MAX(0.5*(DX - dx*expand) - layout[0].X, 0);
+  if ((int)X != X) 
+    X = (int) X + 1;
+  else 
+    X = (int) X;
+  Y = MAX(0.5*(DY - dy*expand) - layout[0].Y, 0);
+  if ((int)Y != Y) 
+    Y = (int) Y + 1;
+  else 
+    Y = (int) Y;
+
+  /* Rx,Ry are the screen coordinates of the first image pixel */
+  Rx = (X + layout[0].X - 0.5*DX)/expand + 0.5*dx;
+  Ry = (Y + layout[0].Y - 0.5*DY)/expand + 0.5*dy;
+
+  i_start = MIN (MAX (Rx, 0), dx - expand_out + 1);
+  j_start = MIN (MAX (Ry, 0), dy - expand_out + 1);
+  
+  if (layout[0].expand > 0) {
+    i_end = MAX (MIN (i_start + ((int)(expand*(dx-i_start)))/expand, expand_out*(DX-X) + Rx), 0);
+    j_end = MAX (MIN (j_start + ((int)(expand*(dy-j_start)))/expand, expand_out*(DY-Y) + Ry), 0);
+  } else {
+    i_end = MAX (MIN (dx, (DX-X)/expand + Rx), 0);
+    j_end = MAX (MIN (dy, (DY-Y)/expand + Ry), 0);
+  }    
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  /* output line buffer */
+  ALLOCATE (line_buffer, char, dx);
+
+  in_pix_ref  = (unsigned char *) (layout[0].matrix.buffer) + DX*(int)MAX(Y,0) + (int)MAX(X,0);
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  Nchar = 255.0;
+  Npix = layout[0].Npixels;
+  frac = Nchar / Npix;
+
+  /**** fill in bottom area ****/
+  out_pix = line_buffer;
+  for (i = 0; i < dx; i++, out_pix++) {
+    *out_pix = WHITE;
+  }
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++) {
+      fprintf (f, "%02x", val);
+      if ((Npt % 40) == 0) {
+	fprintf (f, "\n");
+      }
+    }
+  }
+  
+  /*** fill in the image data region ***/
+  for (j = j_start; j < j_end; j+= expand_out, in_pix_ref += expand_in*DX) {
+    
+    /* create one output image line */
+    in_pix = in_pix_ref;
+    out_pix = line_buffer;
+
+    /**** fill in area to the left of the picture ****/
+    for (i = 0; i < i_start; i++, out_pix+=3) {
+      *out_pix = WHITE;
+    }
+    
+    /*** fill in the picture region ***/
+    for (i = i_start; i < i_end; i+=expand_out, in_pix+=expand_in) {
+      for (ii = 0; ii < expand_out; ii++, out_pix++) {
+	*out_pix = *in_pix*frac;
+      }
+    }
+    
+    /**** fill in area to the right of the picture ****/
+    for (i = i_end; i < dx; i++, out_pix++) {
+      *out_pix = WHITE;
+    }
+
+    /* write out the image line expand_out times */
+    for (j = 0; j < expand_out; j++) {
+      for (i = 0; i < dx; i++) {
+	fprintf (f, "%02x", val);
+	if ((Npt % 40) == 0) {
+	  fprintf (f, "\n");
+	}
+      }
+    }
+  }
+
+  /**** fill in top area ****/
+  out_pix = line_buffer;
+  for (i = 0; i < dx; i++, out_pix++) { 
+    *out_pix = WHITE;
+  }
+  for (j = j_end; j < dy; j++) {
+    for (i = 0; i < dx; i++) {
+      fprintf (f, "%02x", val);
+      if ((Npt % 40) == 0) {
+	fprintf (f, "\n");
+      }
+    }
+  }
+}
+# endif
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/DrawButton.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/DrawButton.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/DrawButton.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "Ximage.h"
+
+void
+DrawButton (graphic, button)
+Graphic graphic[];
+Button button[];
+{
+  
+  int y, dX;
+  
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+  XFillRectangle (graphic[0].display, 
+		  graphic[0].window,
+		  graphic[0].gc,
+		  button[0].x,  button[0].y,
+		  button[0].dx, button[0].dy);
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  XDrawRectangle (graphic[0].display, 
+		  graphic[0].window,
+		  graphic[0].gc,
+		  button[0].x,  button[0].y,
+		  button[0].dx, button[0].dy);
+
+  if (button[0].text) {
+    dX = XTextWidth (graphic[0].font, button[0].bitmap, strlen(button[0].bitmap));
+    y = button[0].y + (button[0].dy + graphic[0].font[0].ascent)/2;
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 button[0].x + (button[0].dx - dX) / 2, y,
+		 button[0].bitmap, strlen(button[0].bitmap));
+  }
+  else {
+    DrawBitmap (graphic, 
+		button[0].x + (button[0].dx - button[0].width) / 2 + 1, 
+		button[0].y + (button[0].dy - button[0].height) / 2 + 1, 
+		button[0].width, button[0].height, 
+		button[0].bitmap, 1);
+  }
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/FlashButton.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/FlashButton.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/FlashButton.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "Ximage.h"
+
+void FlashButton (Graphic *graphic, Button *button) {
+
+  unsigned long black, white;
+
+  black =  graphic[0].black;
+  white =  graphic[0].white;
+  graphic[0].black = white;
+  graphic[0].white = black;
+
+  DrawButton (graphic, button);
+  XFlush (graphic[0].display);
+/*  usleep (20000);  removed - not a standard C sleep command, apparently! */
+  
+  graphic[0].black = black;
+  graphic[0].white = white;
+  DrawButton (graphic, button);
+  XFlush (graphic[0].display);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/InButton.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/InButton.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/InButton.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "Ximage.h"
+
+int
+InButton (button_event, button)
+XButtonEvent *button_event;
+Button button[];
+{
+
+  int answer;
+  int x, y;
+
+  x = button_event[0].x;
+  y = button_event[0].y;
+  
+  answer = ((x >= button[0].x) && (x <= button[0].x + button[0].dx) &&
+	    (y >= button[0].y) && (y <= button[0].y + button[0].dy));
+
+  return (answer);
+
+}
+
+/*** make this a macro? ***/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/InPicture.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/InPicture.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/InPicture.c	(revision 22322)
@@ -0,0 +1,21 @@
+# include "Ximage.h"
+
+int
+InPicture (button_event, picture)
+XButtonEvent *button_event;
+Picture picture[];
+{
+
+  int answer;
+  int x, y;
+
+  x = button_event[0].x;
+  y = button_event[0].y;
+  
+  answer = ((x >= picture[0].x) && (x <= picture[0].x + picture[0].dx) &&
+	    (y >= picture[0].y) && (y <= picture[0].y + picture[0].dy));
+
+  return (answer);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/InvertButton.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/InvertButton.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/InvertButton.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage.h"
+
+void InvertButton (Graphic *graphic, Button *button) {
+
+  unsigned long black, white;
+
+  black =  graphic[0].black;
+  white =  graphic[0].white;
+
+  graphic[0].black = white;
+  graphic[0].white = black;
+
+  DrawButton (graphic, button);
+  FlushDisplay (graphic[0].display);
+  
+  graphic[0].black = black;
+  graphic[0].white = white;
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/JPEGit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/JPEGit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/JPEGit.c	(revision 22322)
@@ -0,0 +1,153 @@
+# include "Ximage.h"
+# include "jpeglib.h"
+
+int JPEGit (Graphic *graphic, Layout *layout) {
+  
+  struct jpeg_compress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
+  JSAMPLE *image_buffer;	/* Points to data for current line */
+
+  int j, k, m;
+  unsigned char *buff;
+  double Nchar, Npix, start, slope, frac;
+  int quality;
+  unsigned long pcolor;
+
+  char filename[1024];
+  int Nbytes, status;
+
+  XImage *tmpimage;
+
+  FILE *f;
+  
+  /* expect a line telling the number of bytes and a filename */
+  status = read (layout[0].Ximage, filename, 16);
+  filename[16] = 0;
+  sscanf (filename, "%*s %d", &Nbytes);
+  status = read (layout[0].Ximage, filename, Nbytes);
+  filename[status] = 0; /* make the string easy to parse */
+
+  /* 
+  switch (graphic[0].Nbits) {
+  case 8:
+    break;
+  case 16:
+    fprintf (stderr, "converter to 16 bit not yet implemented\n");
+    return (TRUE);
+    break;
+  case 24:
+    fprintf (stderr, "converter to 24 bit not yet implemented\n");
+    return (TRUE);
+    break;
+  case 32:
+    fprintf (stderr, "converter to 32 bit not yet implemented\n");
+    return (TRUE);
+    break;
+  }
+  */
+
+  /* set up the error handler , initialize the JPEG compression object. */
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_compress (&cinfo);
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "failed to open %s for output\n", filename);
+    return (TRUE);
+  }
+  jpeg_stdio_dest(&cinfo, f);
+  
+  quality = 75;
+  cinfo.image_width = layout[0].picture.dx; 	/* image width and height, in pixels */
+  cinfo.image_height = layout[0].picture.dy;
+# ifdef GREYSCALE
+  cinfo.input_components = 1;		        /* # of color components per pixel */
+  cinfo.in_color_space = JCS_GRAYSCALE; 	/* colorspace of input image */
+# else 
+  cinfo.input_components = 3;		        
+  cinfo.in_color_space = JCS_RGB; 	
+# endif
+  jpeg_set_defaults (&cinfo);
+  jpeg_set_quality (&cinfo, quality, TRUE       /* limit to baseline-JPEG values */);
+  jpeg_start_compress (&cinfo, TRUE);
+
+  ALLOCATE (image_buffer, JSAMPLE, 3*layout[0].picture.dx);
+  buff = (unsigned char *)layout[0].picture.data;
+  slope = layout[0].slope;
+  start = layout[0].start;
+  Nchar = 255.0;                                /* range of output values */
+  Npix = layout[0].Npixels;
+  frac = Nchar / Npix;
+  
+  {
+    XWindowAttributes WindowAttributes;
+    Pixmap tmp_pixmap;
+    Window tmp_window;
+    
+    XGetWindowAttributes (graphic[0].display, graphic[0].window, &WindowAttributes);
+    
+    tmp_pixmap = XCreatePixmap (graphic[0].display, graphic[0].window, graphic[0].dx, graphic[0].dy, WindowAttributes.depth);
+
+    tmp_window = graphic[0].window;
+    graphic[0].window = tmp_pixmap;
+
+    Refresh (graphic, layout, 0);
+
+    tmpimage = XGetImage (graphic[0].display, graphic[0].window, layout[0].picture.x+1, layout[0].picture.y+1, layout[0].picture.dx, layout[0].picture.dy, 0xffffffff, ZPixmap);
+
+    for (j = 0; (j < layout[0].picture.dy) && (cinfo.next_scanline < cinfo.image_height); j++) {
+      for (k = 0; k < layout[0].picture.dx; k++) {
+	pcolor = XGetPixel (tmpimage, k, j);
+	if (pcolor == layout[0].white) { 
+	  image_buffer[3*k+0] = Nchar;
+	  image_buffer[3*k+1] = Nchar;
+	  image_buffer[3*k+2] = Nchar;
+	  continue;
+	}
+	if (pcolor == layout[0].overlay[0].color) { 
+	  image_buffer[3*k+0] = Nchar;
+	  image_buffer[3*k+1] = 0;
+	  image_buffer[3*k+2] = 0;
+	  continue;
+	}
+	if (pcolor == layout[0].overlay[1].color) { 
+	  image_buffer[3*k+0] = 0;
+	  image_buffer[3*k+1] = Nchar;
+	  image_buffer[3*k+2] = 0;
+	  continue;
+	}
+	if (pcolor == layout[0].overlay[2].color) { 
+	  image_buffer[3*k+0] = 0;
+	  image_buffer[3*k+1] = 0;
+	  image_buffer[3*k+2] = Nchar;
+	  continue;
+	}
+	if (pcolor == layout[0].overlay[3].color) { 
+	  image_buffer[3*k+0] = Nchar;
+	  image_buffer[3*k+1] = Nchar;
+	  image_buffer[3*k+2] = 0;
+	  continue;
+	}
+	for (m = 0; (layout[0].cmap[m].pixel != pcolor) && (m < Npix); m++);
+	/* val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix); */
+	image_buffer[3*k+0] = layout[0].cmap[m].red / 256;
+	image_buffer[3*k+1] = layout[0].cmap[m].green / 256;
+	image_buffer[3*k+2] = layout[0].cmap[m].blue / 256;
+      }
+      row_pointer[0] = image_buffer;
+      (void) jpeg_write_scanlines (&cinfo, row_pointer, 1);
+    }
+    graphic[0].window = tmp_window;
+    XFreePixmap (graphic[0].display, tmp_pixmap);
+  }
+
+  XDestroyImage (tmpimage);
+
+  jpeg_finish_compress (&cinfo);
+  fclose (f);
+  jpeg_destroy_compress (&cinfo);
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/PSit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/PSit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/button/PSit.c	(revision 22322)
@@ -0,0 +1,131 @@
+# include "Ximage.h"
+# define PADDING 10
+# define XOFFSET 60
+# define YOFFSET 60
+
+static char *name = "$Name: not supported by cvs2svn $";
+
+int PSit (Graphic *graphic, Layout *layout, int Raw) {
+  
+  FILE *f;
+  int i, pageMode, scaleMode;
+  double scale;
+  char filename[1024], pagename[1024], *version;
+
+  /* expect a line telling the number of bytes and a filename */
+  KiiScanMessage (layout[0].Ximage, "%s %s %d %d", filename, pagename, &scaleMode, &pageMode);
+
+  if (pageMode == KAPA_PS_NEWPAGE) {
+    f = fopen (filename, "a+");
+  } else {
+    f = fopen (filename, "w");
+  }
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "failed to open %s for output\n", filename);
+    return (TRUE);
+  }
+  
+  if (!USE_XWINDOW) {
+    fprintf (stderr, "PS not working yet\n");
+    return (TRUE);
+  }
+
+  if (scaleMode) {
+    scale = MIN ((500.0/(double)layout[0].picture.dx), (700.0/(double)layout[0].picture.dy));
+  } else {
+    scale = 72.0 / 96.0; /* ratio of screen pixels to points */
+  }
+
+  switch (pageMode) {
+    case KAPA_PS_NEWPLOT:
+      fprintf (f, "%%!PS-Adobe-2.0 EPSF-2.0\n");
+      fprintf (f, "%%%%Title: %s\n", filename);
+      version = strip_version (name);
+      fprintf (f, "%%%%Creator: Ki'i (%s)\n", version);
+      free (version);
+      fprintf (f, "%%%%BoundingBox: %d %d %.0f %.0f\n", 
+	       XOFFSET - PADDING, YOFFSET - PADDING, 
+	       scale*layout[0].picture.dx + PADDING + XOFFSET, 
+	       scale*layout[0].picture.dy + PADDING + YOFFSET);
+      fprintf (f, "%%%%Pages: 1\n");
+      fprintf (f, "%%%%DocumentFonts:\n");
+      fprintf (f, "%%%%EndComments\n");
+      fprintf (f, "%%%%EndProlog\n");
+      fprintf (f, "%%%%Page: %s\n\n", pagename);
+      break;
+
+    case KAPA_PS_NEWPAGE:
+      fprintf (f, "%%%%Page: %s\n\n", pagename);
+      break;
+
+    case KAPA_PS_RAWPAGE:
+      break;
+  } 
+
+  fprintf (f, "gsave %% encloses picture\n");
+  fprintf (f, "%% local abbreviations\n");
+  fprintf (f, "/Times-Roman findfont 18 scalefont setfont\n");
+  fprintf (f, "/T {moveto show stroke} def\n");
+  fprintf (f, "/B { newpath moveto dup 0 exch rlineto exch dup 0 rlineto exch -1 mul\n");
+  fprintf (f, " 0 exch rlineto -1 mul 0 rlineto closepath stroke } def\n");
+  fprintf (f, "/C {0 360 arc stroke} def\n");
+  fprintf (f, "/L {newpath moveto lineto stroke} def\n\n");
+
+  if (pageMode != KAPA_PS_RAWPAGE) {
+    fprintf (f, " %d %d translate\n", XOFFSET, YOFFSET);
+    fprintf (f, "  %f  %f scale\n", scale, scale);
+  }
+
+  fprintf (f, " newpath 0 0 moveto %d 0 lineto %d %d lineto 0 %d lineto closepath clip\n\n", 
+	   layout[0].picture.dx, layout[0].picture.dx, layout[0].picture.dy, layout[0].picture.dy);
+  fprintf (f, "gsave %% encloses image\n");
+  fprintf (f, "%d %d 8\n", layout[0].picture.dx, layout[0].picture.dy);
+  fprintf (f, "[1 0 0 1 0 0]\n");
+  fprintf (f, "{currentfile %d string readhexstring pop} image\n\n", layout[0].picture.dx);
+
+  /******** First we draw the picture itself ********/
+  /* in !USE_XWINDOW, we'll have to change this to use the JPEG function */
+  switch (graphic[0].Nbits) {
+  case 8:
+    ConvertPixmap8 (layout, f);
+    break;
+  case 16:
+    ConvertPixmap16 (layout, f);
+    break;
+  case 24:
+    ConvertPixmap24 (layout, f);
+    break;
+  case 32:
+    ConvertPixmap32 (layout, f);
+    break;
+  }
+
+  fprintf (f, "grestore %% end of image\n");
+  fprintf (f, "stroke\n");
+  fprintf (f, "%% plot overlay objects\n");
+  fprintf (f, "1 setgray\n");
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (OVERLAY[i]) {
+      fprintf (f, "%% overlay %d\n", i);
+      DrawOverlay (graphic, layout, i, f, 0);
+    }
+  }
+  fprintf (f, "0 setgray\n");
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (OVERLAY[i]) {
+      fprintf (f, "%% overlay %d\n", i);
+      DrawOverlay (graphic, layout, i, f, 1);
+    }
+  }
+  
+  fprintf (f, "grestore %% end of picture\n");
+
+  if (pageMode != KAPA_PS_RAWPAGE) fprintf (f, "showpage\n");
+
+  fclose (f);
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/colorbar/CreateColorbar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/colorbar/CreateColorbar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/colorbar/CreateColorbar.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "Ximage.h"
+
+void CreateColorbar (Layout *layout, Graphic *graphic) {
+
+  int i, j, dx, dy, extra, start;
+  unsigned long  pixvalue;
+  unsigned int  *out24;
+  unsigned char *out8;
+
+  dx = layout[0].cmapbar.dx;
+  dy = layout[0].cmapbar.dy;
+
+  /* create the cmap scale */
+  switch (graphic[0].Nbits) {
+  case 8:
+    REALLOCATE (layout[0].cmapbar.data, char, dx*dy);
+    out8 = (unsigned char *) layout[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = layout[0].cmap[(int)(i*layout[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	out8[j*dx + i] = pixvalue;
+      }
+    }
+    layout[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  layout[0].cmapbar.data, dx, dy, 8, 0);
+    break;
+
+  case 16:
+    REALLOCATE (layout[0].cmapbar.data, char, 2*dy*dx);
+    out8 = (unsigned char *) layout[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = layout[0].cmap[(int)(i*layout[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	start = 2*j*dx + 2*i;
+	out8[start + 0] = 0x0000ff & pixvalue;
+	out8[start + 1] = 0x0000ff & (pixvalue >> 8);
+      }
+    }
+    layout[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  layout[0].cmapbar.data, dx, dy, 16, 0);
+    break;
+
+  case 24:
+    extra = 4 - (dx * 3) % 4;
+    REALLOCATE (layout[0].cmapbar.data, char, dy*(3*dx + extra));
+    out8 = (unsigned char *) layout[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = layout[0].cmap[(int)(i*layout[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	start = j*(3*dx+extra) + 3*i;
+	out8[start + 0] = 0x0000ff & pixvalue;
+	out8[start + 1] = 0x0000ff & (pixvalue >> 8);
+	out8[start + 2] = 0x0000ff & (pixvalue >> 16);
+      }
+    }
+    layout[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  layout[0].cmapbar.data, dx, dy, 32, 0);
+    break;
+
+  case 32:
+    REALLOCATE (layout[0].cmapbar.data, char, 4*dx*dy);
+    out24 = (unsigned int *) layout[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = layout[0].cmap[(int)(i*layout[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	out24[j*dx + i] = pixvalue;
+      }
+    }
+    layout[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  layout[0].cmapbar.data, dx, dy, 32, 0);
+    break;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/colorbar/DragColorbar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/colorbar/DragColorbar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/colorbar/DragColorbar.c	(revision 22322)
@@ -0,0 +1,107 @@
+# include "Ximage.h"
+/* future expansion: warp mouse to the right spot for the current settings */
+
+void DragColorbar (Graphic *graphic, Layout *layout, XButtonEvent *mouse_event) {
+
+  double          frac_x, oldfrac_x, frac_y, oldfrac_y, start, slope;
+  int             X, Y;
+  XEvent          event;
+  XMotionEvent   *move;
+  int             xstatus, Npix, center;
+
+  if (!graphic[0].visualclass) return;
+
+  X = mouse_event[0].x;
+  Y = mouse_event[0].y;
+  Npix = layout[0].Npixels;
+  slope = layout[0].slope;
+  start = layout[0].start;
+  oldfrac_x = oldfrac_y = 10;
+  
+  while (1) {
+    frac_x = X / (1.0*graphic[0].dx);
+    frac_x = MAX (0, frac_x);
+    frac_x = MIN (1, frac_x);
+    frac_y = Y / (1.0*graphic[0].dy);
+    frac_y = MAX (0, frac_y);
+    frac_y = MIN (1, frac_y);
+    switch (mouse_event[0].button) {
+    case 1:
+      center = frac_x*Npix;
+      slope = Npix / (1 + 4*frac_y*frac_y*Npix);
+      start = Npix/2.0 - center*slope;
+      break;
+    case 2:
+      start = 0;
+      slope = 1;
+      break;
+    case 3:
+      center = (Npix/2.0 - start) / slope;
+      slope = Npix / (1 + 4*frac_x*frac_x*Npix);
+      start = Npix/2.0 - center*slope;
+      break;
+    }
+
+    if ((frac_x != oldfrac_x) || (frac_y != oldfrac_y)) {
+      ResetColorbar (graphic, layout, start, slope);
+    }
+    oldfrac_x = frac_x;
+    oldfrac_y = frac_y;
+      
+    if ((xstatus = XCheckMaskEvent (graphic[0].display, EVENT_MASK, &event))) {
+      
+      switch (event.type)  {
+	
+      case MotionNotify:
+	if ((xstatus = XPending (graphic[0].display)) < 2) {
+	  move = (XMotionEvent *) &event;
+	  X = move[0].x;
+	  Y = move[0].y;
+	}
+	break;
+	
+      case ButtonPress:
+      case ButtonRelease:
+	layout[0].start = start;	
+	layout[0].slope = slope;
+	return;
+	break;
+	
+      }
+    }
+  }
+  
+}
+
+void ResetColorbar (Graphic *graphic, Layout *layout, double start, double slope) {
+
+  int i, j;
+  XColor cmap[256];
+
+  if (!graphic[0].visualclass) return;
+
+  for (i = 0; i < layout[0].Npixels; i++) {
+    cmap[i] = layout[0].cmap[i];
+    j = start + i * slope;
+    if (j < 0) {
+      cmap[i].red = layout[0].cmap[0].red;
+      cmap[i].blue = layout[0].cmap[0].blue;
+      cmap[i].green = layout[0].cmap[0].green;
+    }
+    else {
+      if (j >= layout[0].Npixels) {
+	cmap[i].red = layout[0].cmap[layout[0].Npixels-1].red;
+	cmap[i].blue = layout[0].cmap[layout[0].Npixels-1].blue;
+	cmap[i].green = layout[0].cmap[layout[0].Npixels-1].green;
+      }
+      else {
+	cmap[i].red = layout[0].cmap[j].red;
+	cmap[i].blue = layout[0].cmap[j].blue;
+	cmap[i].green = layout[0].cmap[j].green;
+      }
+    }
+  }
+
+  XStoreColors(graphic[0].display, graphic[0].colormap, cmap, layout[0].Npixels);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/colorbar/SetColormap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/colorbar/SetColormap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/colorbar/SetColormap.c	(revision 22322)
@@ -0,0 +1,101 @@
+# include "Ximage.h"
+
+/*** this function uses only private colormaps.  it should try the standard colormap
+     first to get better control over the smooth greyscales ***/
+
+int SetColormap (Graphic *graphic, Layout *layout, char *name) {
+
+  int i;
+
+  /* greyscale */
+  if ((!strcmp (name, "grayscale")) || (!strcmp (name, "greyscale"))) {
+    for (i = 0; i < layout[0].Npixels; i++) {  
+      layout[0].cmap[i].red = 256*(255 - 255*i/layout[0].Npixels);
+      layout[0].cmap[i].green = 256*(255 - 255*i/layout[0].Npixels);
+      layout[0].cmap[i].blue = 256*(255 - 255*i/layout[0].Npixels);
+      layout[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  /* heat */
+  if (!strcmp (name, "Puns")) {
+    for (i = 0; i < (int)(0.25*layout[0].Npixels); i++) {  
+      layout[0].cmap[i].green = 0;
+      layout[0].cmap[i].blue = 0;
+      layout[0].cmap[i].red = MIN (256*255, 256*255*2*i/(1.0*layout[0].Npixels));  
+      layout[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.25*layout[0].Npixels); i < (int)(0.50*layout[0].Npixels); i++) {  
+      layout[0].cmap[i].red = MIN (256*255, 256*255*2*i/(1.0*layout[0].Npixels));
+      layout[0].cmap[i].green = MIN (256*255, 256*255*2*(i/(1.0*layout[0].Npixels) - 0.25));
+      layout[0].cmap[i].blue = 0;
+      layout[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.50*layout[0].Npixels); i < (int)(0.75*layout[0].Npixels); i++) {  
+      layout[0].cmap[i].red = 256*255;
+      layout[0].cmap[i].green = MIN (256*255, 256*255*2*(i/(1.0*layout[0].Npixels) - 0.25));
+      layout[0].cmap[i].blue = MIN (256*255, 256*255*2*(i/(1.0*layout[0].Npixels) - 0.50));
+      layout[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.75*layout[0].Npixels); i < layout[0].Npixels; i++) {  
+      layout[0].cmap[i].red = 256*255;
+      layout[0].cmap[i].green = 256*255;
+      layout[0].cmap[i].blue = MIN (256*255, 256*255*2*(i/(1.0*layout[0].Npixels) - 0.50));
+      layout[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  /* -grayscale */
+  if ((!strcmp (name, "-grayscale")) || (!strcmp (name, "-greyscale"))) {
+    for (i = 0; i < layout[0].Npixels; i++) {  
+      layout[0].cmap[i].red = 256*(255*i/layout[0].Npixels);
+      layout[0].cmap[i].green = 256*(255*i/layout[0].Npixels);
+      layout[0].cmap[i].blue = 256*(255*i/layout[0].Npixels);
+      layout[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  /* rainbow */
+  if (!strcmp (name, "Rainbow")) {
+    for (i = 0; i < (int)(0.25*layout[0].Npixels); i++) {  
+      layout[0].cmap[i].red = 0;
+      layout[0].cmap[i].green = 0;
+      layout[0].cmap[i].blue = 256*(255*4*(i/(1.0*layout[0].Npixels)));  
+      layout[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.25*layout[0].Npixels); i < (int)(0.50*layout[0].Npixels); i++) {  
+      layout[0].cmap[i].red = 256*(255*4*((i/(1.0*layout[0].Npixels)) - 0.25));
+      layout[0].cmap[i].green = 0;
+      layout[0].cmap[i].blue = 256*(255*4*(0.50 - (i/(1.0*layout[0].Npixels))));
+      layout[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.50*layout[0].Npixels); i < (int)(0.75*layout[0].Npixels); i++) {  
+      layout[0].cmap[i].red = 256*255;
+      layout[0].cmap[i].green = 256*(255*4*((i/(1.0*layout[0].Npixels)) - 0.50));
+      layout[0].cmap[i].blue = 0;
+      layout[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.75*layout[0].Npixels); i < layout[0].Npixels; i++) {  
+      layout[0].cmap[i].red = 256*255;
+      layout[0].cmap[i].green = 256*255;
+      layout[0].cmap[i].blue = 256*(255*4*((i/(1.0*layout[0].Npixels)) - 0.75));
+      layout[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  return (FALSE);
+
+ store_colors:
+  if (!USE_XWINDOW) return (TRUE);
+  if (graphic[0].visualclass) {
+    XStoreColors(graphic[0].display, graphic[0].colormap, layout[0].cmap, layout[0].Npixels);
+  } else {
+    for (i = 0; i < layout[0].Npixels; i++) {
+      if (XAllocColor (graphic[0].display, graphic[0].colormap, &layout[0].cmap[i]) == 0) {
+	fprintf (stderr, "error on %d\n", i);
+      }
+    }
+  }
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,34 @@
+
+- kii 1.6 : 2006.08.23
+  * converted to gfits APIs (forces libfits 1.6)
+  * added keyboard events
+  * removed items moved to libkapa
+  * added test scripts
+
+- kii 1.5
+  * fixed bug in event loop which missed certain mask types
+  * added arrows and up/down keys to window events
+  * added ReorientOnButton / changed Reorient
+  * fixed errors wrt bDraw / fonts
+
+- kii 1.4
+  * major work to allow noX operation
+  * added bDraw functions
+  * integrated with libkapa
+
+kii-1-3: released 2005.10.20
+  - changed cursor interaction (returns name for keys)
+  - minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+kii-1-2:
+  - cleaned up the event loop to reduce CPU spinning
+
+kii-1-1: 
+  - cleaned up the update / refresh process to correctly handle
+    uncovered portions.
+  - cleaned up the event loop to handle slow traffic better
+  - cleanup up the cursor event loop in the same way
+  - fixed crash on non-printing cursor keys
+
+kii-1-0:
+  imported to CVS 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/doc/VERSIONS
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/doc/VERSIONS	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/doc/VERSIONS	(revision 22322)
@@ -0,0 +1,9 @@
+
+tag names used by kapa:
+
+TAG         : Comment
+kii-1-0     : first version under CVS
+
+USES
+libfits-1-0
+libohana-1-0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/doc/notes.txt	(revision 22322)
@@ -0,0 +1,29 @@
+
+2005.12.05
+
+  I have been making some improvements to Kapa / Kii, and am
+  planning on further advances.  Here are some of the features
+  I'd like to add:
+
+  - library function calls (see libfuncs.txt)
+
+  - named plotting styles (line, histogram, points)
+  - add y-based histogram 
+  - move point-to-point to a style
+  - explore standard colormaps to get better greyscale
+  - merge kapa and kii common code?
+  - kapa / kii device Number in title bar
+
+  - complete kapa plotting features in kii 
+  - axis labels on kii image
+  - kapa (and kii) axis labels, frame in color
+  - image-plotting in kapa
+
+  - three selectable kii image buffers
+  - true-color kii representation 
+  
+  - color PS for kii (currently only B&W)
+  - refresh picture after new colormap
+  - DVO regions corresponding to Kapa 'sections' on any window
+
+  - push argv/argc entries all into args, push options into globals.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/CheckPipe.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/CheckPipe.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/CheckPipe.c	(revision 22322)
@@ -0,0 +1,140 @@
+# include "Ximage.h"
+# define STRCONST(A) ((int)(0x1000000*A[0] + 0x10000*A[1] + 0x100*A[2] + 0x1*A[3]))
+# define DEBUG 0
+
+static int ActiveCursor = FALSE;
+
+int CheckPipe (Graphic *graphic, Layout *layout) {
+
+  int status;
+  char buffer[20];
+
+  /* attempt to read 4 bytes from stdin */
+  bzero (buffer, 20);
+  status = read (layout[0].Ximage, buffer, 4);
+  buffer[4] = 0;
+
+  switch (status) {
+  case -1:
+    /* no input from pipe: continue */
+    return (TRUE);
+    break;
+
+  case 0:
+    fprintf (stderr, "pipe has died!\n");
+    return (FALSE);
+    break;
+
+  case 4:
+    if (DEBUG) fprintf (stderr, "got signal %s from pipe\n", buffer);
+    break;
+
+  default:
+    fprintf (stderr, "weird signal: too many or few bytes!  %d\n", status);
+    return (TRUE);
+    break;
+  }
+  
+  /* In Active Cursor mode, we can only receive the NCUR signal */
+  if (ActiveCursor) {
+    if (!strcmp (buffer, "NCUR")) {
+      ActiveCursor = FALSE;
+      return (2);
+    }
+    fprintf (stderr, "unknown signal (AC) %s\n", buffer);
+    return (TRUE);
+  }
+
+  /* If Active Cursor mode is off, we can receive other signal */
+  if (!strcmp (buffer, "QUIT")) {
+    if (DEBUG) fprintf (stderr, "Goodbye!\n");
+    return (FALSE);
+  }
+  
+  if (!strcmp (buffer, "READ")) {
+    status = NewPicture (graphic, layout);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "CURS")) {
+    ActiveCursor = TRUE;
+    status = cursor (graphic, layout);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "NCUR")) {
+    fprintf (stderr, "error: not in active cursor mode\n");
+    return (TRUE);
+  }
+
+  if (!strcmp (buffer, "ERAS")) {
+    status = EraseOverlay (graphic, layout);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "LOAD")) {
+    status = LoadOverlay (graphic, layout);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "TICK")) {
+    status = LoadTickmarks (graphic, layout);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "SAVE")) {
+    status = SaveOverlay (graphic, layout);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "PSIT")) {
+    /** this one needs work : ConvertPixmapN fails for !USE_XWINDOW */
+    status = PSit (graphic, layout, 0);
+    write (layout[0].Ximage, "DONE", 4);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "PSRW")) {
+    /** this one needs work : ConvertPixmapN fails for !USE_XWINDOW */
+    status = PSit (graphic, layout, 1);
+    write (layout[0].Ximage, "DONE", 4);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "JPEG")) {
+    status = JPEGit24 (graphic, layout);
+    write (layout[0].Ximage, "DONE", 4);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "RSIZ")) {
+    status = Resize (graphic, layout);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "CSVE")) {
+    status = CSaveOverlay (graphic, layout);
+    return (status);
+  }
+
+  if (!strcmp (buffer, "CENT")) {
+    status = Center (graphic, layout);
+    return (status);
+  }
+
+  /* is this no longer used ?
+  if (!strcmp (buffer, "NPIX")) {
+    sprintf (buffer, "NPIX:   %8d", layout[0].Npixels);
+    write (layout[0].Ximage, buffer, 16);
+    return (TRUE);
+  }
+  */
+
+  fprintf (stderr, "unknown signal %s\n", buffer);
+
+  return (TRUE);
+
+}
+
+
+    
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/EventLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/EventLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/EventLoop.c	(revision 22322)
@@ -0,0 +1,80 @@
+# include "Ximage.h"
+
+/* # define IgnoreMask (long) (ClientMessage | ButtonReleaseMask) */
+# define IgnoreMask (long) (~(StructureNotifyMask | SubstructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | PointerMotionMask))
+
+int LastEvent (Display *display, int type, XEvent *event) {
+
+  int found;
+
+  found = FALSE;
+  while (XCheckTypedEvent (display, type, event)) {
+    found = TRUE;
+  }
+  return (found);
+}
+
+int EventLoop (Graphic *graphic, Layout *layout) {
+  
+  XEvent          event;
+  int             status;
+  Display        *display;
+  
+  if (USE_XWINDOW) Refresh (graphic, layout, 1);
+
+  status = TRUE;
+  while (status) {
+    
+    if (!CheckPipe (graphic, layout))
+      return (FALSE);
+    
+    if (!USE_XWINDOW) {
+      usleep (50000);
+      continue;
+    }
+
+    if (XEventsQueued (graphic[0].display, QueuedAfterFlush) < 1) {
+      usleep (10000);
+      continue;
+    }
+
+    display = graphic[0].display;
+
+    /* grab the last entry for these events */
+    if (LastEvent (display, ConfigureNotify, &event)) Reconfig (graphic, layout, &event);
+    if (LastEvent (display, CirculateNotify, &event)) Reconfig (graphic, layout, &event);
+    if (LastEvent (display, Expose,          &event)) Refresh (graphic, layout, 1);
+    if (LastEvent (display, MotionNotify,    &event)) UpdatePointer (graphic, layout, (XMotionEvent *) &event);
+    if (LastEvent (display, ButtonPress,     &event)) InterpretPresses (graphic, layout, (XButtonEvent *) &event);
+    if (LastEvent (display, KeyPress,        &event)) InterpretKeys (graphic, layout, &event);
+
+    if (LastEvent (display, MappingNotify,   &event)) XRefreshKeyboardMapping ((XMappingEvent *) &event);
+
+    /* drop and ignore the following StructureNotifyMask events */
+    LastEvent (display, GravityNotify, &event);
+    LastEvent (display, ReparentNotify, &event);
+    LastEvent (display, MapNotify, &event);
+    LastEvent (display, UnmapNotify, &event);
+
+    /* remove those events we will ignore */
+    while (XCheckMaskEvent (display, IgnoreMask, &event)) continue;
+
+    /* events to remove which have no mask component */
+    while (XCheckTypedEvent (display, MappingNotify, &event)) continue;
+    while (XCheckTypedEvent (display, ClientMessage, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionClear, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionNotify, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionRequest, &event)) continue;
+  }
+  return (status);
+}
+
+# if (0)
+/* WARNING: this is defined because the HP UX installation i have does not
+seem to know this structure, though it should.  Clearly a need for autoconf.. */
+struct Ktimeval {
+  unsigned long tv_sec;         /* seconds */
+  long          tv_usec;        /* and microseconds */
+};
+# endif
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/FlushDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/FlushDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/FlushDisplay.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include "Ximage.h"
+
+static struct timeval reftime; 
+static char reftimeset = FALSE;
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+void FlushDisplay (Display *display) {
+
+  struct timeval now;
+  int flush;
+  double dtime;
+
+  flush = FALSE;
+  if (!reftimeset) {
+    flush = TRUE;
+    gettimeofday (&reftime, NULL);
+  } 
+
+  gettimeofday (&now, NULL);
+  dtime = DTIME (now, reftime);
+
+  if (dtime > 0.1) {
+    flush = TRUE;
+  }
+
+  if (flush) {
+    XFlush (display);
+    reftime = now;
+  }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/InterpretKeys.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/InterpretKeys.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/InterpretKeys.c	(revision 22322)
@@ -0,0 +1,82 @@
+# include "Ximage.h"
+
+int InterpretKeys (Graphic *graphic, Layout *layout, XEvent *event) {
+
+  double X, Y, offset;
+  int    modstate;
+  char   string[10];
+  KeySym keysym;
+  XComposeStatus  composestatus;
+  XKeyEvent *keyevent;
+
+  keyevent = (XKeyEvent *) event;
+  XLookupString (keyevent, string, 9, &keysym, &composestatus);
+  modstate = keyevent[0].state;
+
+  // offset is in image pixels: 
+  // 0.5 image pixels is 1 screen pixel for expand == +2
+  // 2.0 image pixels is 1 screen pixel for expand == -2
+  if (layout[0].expand == 0) layout[0].expand = 1;
+  offset = (layout[0].expand > 0) ? 1.0 / layout[0].expand : -layout[0].expand;
+  if (modstate & ControlMask) offset *= 100;
+
+  switch (keysym) {
+
+    case XK_KP_Home:
+    case XK_Home:
+      layout[0].expand = 1;
+      Reorient (graphic, layout, layout[0].X, layout[0].Y, 0);
+      break;
+    case XK_KP_End:
+    case XK_End:
+      layout[0].expand = 1;
+      Reorient (graphic, layout, 0, 0, 0);
+      break;
+    case XK_KP_Enter:
+    case XK_KP_Begin:
+    case XK_Return:
+      Screen_to_Image (&X, &Y, (double)keyevent[0].x, (double)keyevent[0].y, layout);
+      X = 0.5*layout[0].matrix.Naxis[0] - X;
+      Y = 0.5*layout[0].matrix.Naxis[1] - Y;
+      Reorient (graphic, layout, X, Y, 0);
+      break;
+    case XK_Prior:
+    case XK_KP_Prior:
+      Reorient (graphic, layout, layout[0].X, layout[0].Y, +1);
+      break;
+    case XK_Next:
+    case XK_KP_Next:
+      Reorient (graphic, layout, layout[0].X, layout[0].Y, -1);
+      break;
+    case XK_Up:
+    case XK_KP_Up:
+      Reorient (graphic, layout, layout[0].X, layout[0].Y + offset, 0);
+      break;
+    case XK_Down:
+    case XK_KP_Down:
+      Reorient (graphic, layout, layout[0].X, layout[0].Y - offset, 0);
+      break;
+    case XK_Left:
+    case XK_KP_Left:
+      Reorient (graphic, layout, layout[0].X + offset, layout[0].Y, 0);
+      break;
+    case XK_Right:
+    case XK_KP_Right:
+      Reorient (graphic, layout, layout[0].X - offset, layout[0].Y, 0);
+      break;
+
+    case XK_Tab:
+      MOVE_POINTER = MOVE_POINTER ^ TRUE;
+      if (MOVE_POINTER) UpdatePointer (graphic, layout, (XMotionEvent *)event);
+      break;
+  }
+
+  return (TRUE);
+}
+
+/* other keys we might want to use:
+
+  
+
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/InterpretPresses.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/InterpretPresses.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/InterpretPresses.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "Ximage.h"
+
+int InterpretPresses (Graphic *graphic, Layout *layout, XButtonEvent *event) {
+
+  int             status, done, this_button;
+  Button         *button;
+
+  status = TRUE;
+  this_button = event[0].button;
+  
+  if ((event[0].type == ButtonPress) && InPicture (event, &layout[0].picture)) {
+    ReorientOnButton (graphic, layout, event);
+  }
+
+  if ((event[0].type == ButtonPress) && InPicture (event, &layout[0].cmapbar)) {
+    DragColorbar (graphic, layout, event);
+  }
+
+  /* if on an exisiting button, Invert, wait for release, then go (or not) */
+  if ((button = CheckButtons (event, layout)) != (Button *) NULL) {
+    InvertButton (graphic, button); 
+    done = FALSE;
+    while (!done) { /* wait for release of this button */
+      XNextEvent (graphic[0].display, (XEvent *) event);
+      if ((event[0].type == ButtonRelease) && (event[0].button == this_button)) {
+	done = TRUE;
+      }
+    }
+    DrawButton (graphic, button);
+    if (InButton (event, button)) {
+      switch (event[0].button) {
+      case 1:
+	status = button[0].function_1(graphic, layout);
+	break;
+      case 2:
+	status = button[0].function_2(graphic, layout);
+	break;
+      case 3:
+	status = button[0].function_3(graphic, layout);
+	break;
+      }
+    }
+    else {
+      return (status);
+    }
+  }
+
+  return (status);
+
+}
+
+void ReorientOnButton (Graphic *graphic, Layout *layout, XButtonEvent *mouse_event) {
+
+  double X, Y;
+
+  Screen_to_Image (&X, &Y, (double)mouse_event[0].x, (double)mouse_event[0].y, layout);
+  X = 0.5*layout[0].matrix.Naxis[0] - X;
+  Y = 0.5*layout[0].matrix.Naxis[1] - Y;
+
+  switch (mouse_event[0].button) {
+    case 1:
+      Reorient (graphic, layout, X, Y, 0);
+      break;
+    case 2:
+      Reorient (graphic, layout, X, Y, -1);
+      break;
+    case 3:
+      Reorient (graphic, layout, X, Y, +1);
+      break;
+    default:
+      break;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/NextEvent.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/NextEvent.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/NextEvent.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage.h"
+
+/************** NextEvent *************/
+void
+NextEvent (graphic, event, keysym)
+Graphic graphic[];
+XEvent *event;
+KeySym *keysym;
+{
+  int status;
+
+  status = FALSE;
+
+  while (!status) {
+    XNextEvent (graphic[0].display, event);
+    status = ParseEvent (graphic, event, keysym);
+  }
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/ParseEvent.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/ParseEvent.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/ParseEvent.c	(revision 22322)
@@ -0,0 +1,61 @@
+# include "Ximage.h"
+
+/************** ParseEvent *************/
+int
+ParseEvent (graphic, event, keysym)
+Graphic graphic[];
+XEvent *event;
+KeySym *keysym;
+{
+
+  XComposeStatus composestatus;
+  char string[10];
+  int status;
+ 
+  status = FALSE;
+  switch (event[0].type) {
+  case ButtonPress:
+    status = TRUE;
+    break;
+  case ButtonRelease:
+    status = TRUE;
+    break;
+  case ClientMessage:
+    status = TRUE;
+    break;
+  case MotionNotify:
+    status = TRUE;
+    break;
+  case PropertyNotify:
+    status = TRUE;
+    break;
+  case SelectionClear:
+    status = TRUE;
+    break;
+  case SelectionNotify:
+    status = TRUE;
+    break;
+  case SelectionRequest:
+    status = TRUE;
+    break;
+  case ConfigureNotify:
+    if ((graphic[0].dx != event[0].xconfigure.width) || 
+	(graphic[0].dy != event[0].xconfigure.height))
+      status = TRUE;
+    break;
+  case Expose:
+    status = TRUE;
+    if (event[0].xexpose.count != 0)
+      status = FALSE;
+    break;
+  case KeyPress:
+    status = TRUE;
+    XLookupString ((XKeyEvent *)event, string, 9, keysym, &composestatus);
+    break;
+  case MappingNotify:
+    XRefreshKeyboardMapping ((XMappingEvent *)event);
+    break;
+  }
+
+  return (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/Reconfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/Reconfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/Reconfig.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage.h"
+# define MAX_STRING 100
+
+int Reconfig (Graphic *graphic, Layout *layout, XEvent *event) {
+  
+  if ((graphic[0].dx == event[0].xconfigure.width) &&
+      (graphic[0].dy == event[0].xconfigure.height)) 
+    return (TRUE);
+
+  graphic[0].dx = MAX(event[0].xconfigure.width,  MIN_WIDTH); 
+  graphic[0].dy = MAX(event[0].xconfigure.height, MIN_HEIGHT);
+
+  XClearWindow (graphic[0].display, graphic[0].window);
+  PositionPictures (layout, graphic);
+  CreateColorbar (layout, graphic);
+  CreatePicture (layout, graphic);
+  Remap (graphic, layout, &layout[0].matrix); 
+  Refresh (graphic, layout, 1);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/Refresh.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/Refresh.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/Refresh.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "Ximage.h"
+# define NPOINTS 10
+
+void Refresh (Graphic *graphic, Layout *layout, int mode) {
+
+  int i;
+
+  XSetForeground (graphic[0].display, graphic[0].gc, layout[0].black);
+  XDrawRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		  layout[0].picture.x, layout[0].picture.y, 
+		  layout[0].picture.dx + 1, layout[0].picture.dy + 1);
+  
+  XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	     layout[0].picture.pix, 0, 0, 
+	     layout[0].picture.x + 1, layout[0].picture.y + 1, 
+	     layout[0].picture.dx, layout[0].picture.dy);
+
+  XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	     layout[0].cmapbar.pix, 0, 0, 
+	     layout[0].cmapbar.x, layout[0].cmapbar.y, 
+	     layout[0].cmapbar.dx, layout[0].cmapbar.dy);
+
+  CrossHairs (graphic, layout);
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (OVERLAY[i])
+      PaintOverlay (graphic, layout, i);
+  }
+  PaintTickmarks (graphic, layout);
+
+  if (mode == 1) {
+    /* erase everything below zoom box, then draw */
+    XSetForeground (graphic[0].display, graphic[0].gc, layout[0].white);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		    layout[0].text_x, layout[0].text_x, layout[0].zoom.dx, graphic[0].dy); 
+    
+    DrawButton (graphic, &layout[0].PS_button);
+    DrawButton (graphic, &layout[0].recenter_button);
+    DrawButton (graphic, &layout[0].grey_button);
+    DrawButton (graphic, &layout[0].rainbow_button);
+    DrawButton (graphic, &layout[0].puns_button);
+    DrawButton (graphic, &layout[0].hms_button);
+
+    for (i = 0; i < NOVERLAYS; i++) {
+      DrawButton (graphic, &layout[0].overlay_button[i]);
+    }
+  }
+
+  StatusBox (graphic, layout);
+
+  FlushDisplay (graphic[0].display);
+}
+
+/* this routine is independent of the number of overlays */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/Stop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/Stop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/event/Stop.c	(revision 22322)
@@ -0,0 +1,19 @@
+# include "Ximage.h"
+# define XBorder 10
+# define YBorder 2
+
+int
+Stop (graphic, layout)
+Graphic   graphic[];
+Layout    layout[];
+{
+  return (FALSE);
+}
+
+
+
+
+
+
+/******  this function looks stupid, but it has to be like this:
+  it is called as a pointer to function **/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/Ximage.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/Ximage.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/Ximage.h	(revision 22322)
@@ -0,0 +1,8 @@
+# include <Xohana.h>
+# include <dvo.h>
+# include <kapa.h>
+# include "constants.h"
+# include "structures.h"
+# include "prototypes.h"
+
+/* dvo.h is used only for Coords */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/buttons.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/buttons.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/buttons.h	(revision 22322)
@@ -0,0 +1,120 @@
+#define PS_width 25
+#define PS_height 25
+static char PS_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x81, 0x3f, 0x00,
+   0x82, 0x67, 0x60, 0x00, 0x02, 0x26, 0x40, 0x00, 0x02, 0x24, 0x40, 0x00,
+   0x02, 0x24, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00,
+   0x02, 0x24, 0x00, 0x00, 0x02, 0x64, 0x00, 0x00, 0x02, 0xc6, 0x00, 0x00,
+   0x82, 0x07, 0x07, 0x00, 0xfe, 0x01, 0x0c, 0x00, 0x02, 0x00, 0x18, 0x00,
+   0x02, 0x00, 0x20, 0x00, 0x02, 0x00, 0x60, 0x00, 0x02, 0x00, 0x40, 0x00,
+   0x02, 0x00, 0xc0, 0x00, 0x02, 0x10, 0x80, 0x00, 0x02, 0x10, 0xc0, 0x00,
+   0x02, 0x60, 0x60, 0x00, 0x02, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00}; 
+#define grey_width 25
+#define grey_height 25
+static char grey_bits[] = {
+   0x90, 0x40, 0xf7, 0x01, 0x04, 0xaa, 0xad, 0x01, 0x68, 0xd5, 0xfe, 0x01,
+   0x82, 0x28, 0xdb, 0x01, 0x10, 0xd2, 0x75, 0x01, 0x40, 0xad, 0xee, 0x01,
+   0xac, 0x50, 0xbf, 0x01, 0x00, 0xaa, 0xf9, 0x01, 0x52, 0xd5, 0xd6, 0x01,
+   0x80, 0x28, 0x6f, 0x01, 0x28, 0xd2, 0xfd, 0x01, 0x44, 0xad, 0xba, 0x01,
+   0x90, 0x50, 0xf7, 0x01, 0x02, 0xaa, 0xcd, 0x01, 0x68, 0xd5, 0x7e, 0x01,
+   0x80, 0x28, 0xfb, 0x01, 0x14, 0xd2, 0xb5, 0x01, 0x40, 0xad, 0xee, 0x01,
+   0xaa, 0x50, 0xdf, 0x01, 0x00, 0xaa, 0x79, 0x01, 0x50, 0xd5, 0xf6, 0x01,
+   0x84, 0x28, 0xaf, 0x01, 0x28, 0xd2, 0xfd, 0x01, 0x40, 0xad, 0xda, 0x01,
+   0x90, 0x50, 0x77, 0x01};
+#define rainbow_width 25
+#define rainbow_height 25
+static char rainbow_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x80, 0xff, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x0c, 0x7f, 0x00,
+   0x00, 0xe3, 0x80, 0x01, 0xc0, 0x9c, 0xff, 0x00, 0x20, 0x62, 0x00, 0x01,
+   0x90, 0x99, 0xff, 0x00, 0x48, 0x76, 0x00, 0x01, 0x24, 0x09, 0x00, 0x00,
+   0x94, 0x06, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
+   0xa5, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+   0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+   0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+   0x15, 0x00, 0x00, 0x00};
+#define recenter_width 25
+#define recenter_height 25
+static char recenter_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00,
+   0x80, 0xef, 0x03, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0x30, 0x00, 0x18, 0x00,
+   0x10, 0x00, 0x10, 0x00, 0x18, 0x00, 0x30, 0x00, 0x08, 0x00, 0x20, 0x00,
+   0x0c, 0x00, 0x60, 0x00, 0x0c, 0x38, 0x60, 0x00, 0x0c, 0x7c, 0x60, 0x00,
+   0x04, 0x7c, 0x40, 0x00, 0x0c, 0x7c, 0x60, 0x00, 0x0c, 0x38, 0x60, 0x00,
+   0x0c, 0x00, 0x60, 0x00, 0x08, 0x00, 0x20, 0x00, 0x18, 0x00, 0x30, 0x00,
+   0x10, 0x00, 0x10, 0x00, 0x30, 0x00, 0x18, 0x00, 0xe0, 0x00, 0x0e, 0x00,
+   0x80, 0xef, 0x03, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define puns_width 25
+#define puns_height 25
+static char puns_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0xd8, 0x01,
+   0x00, 0x0c, 0x37, 0x00, 0xf8, 0xc9, 0x6f, 0x00, 0x0c, 0xde, 0xf0, 0x00,
+   0x06, 0x5b, 0x1f, 0x00, 0x7b, 0xd9, 0x0c, 0x00, 0xc9, 0x70, 0x06, 0x00,
+   0x81, 0x71, 0x03, 0x00, 0x78, 0xb9, 0x39, 0x00, 0xc8, 0xdb, 0x0f, 0x00,
+   0x84, 0xf7, 0x7f, 0x00, 0x04, 0xff, 0xc3, 0x00, 0xe6, 0xfe, 0x8e, 0x00,
+   0x9a, 0x7d, 0x0b, 0x00, 0x08, 0xbc, 0x01, 0x00, 0x08, 0xfc, 0x3f, 0x00,
+   0x0c, 0xff, 0x78, 0x00, 0x84, 0x39, 0x60, 0x00, 0x40, 0x38, 0x40, 0x00,
+   0x60, 0x38, 0x40, 0x00, 0x20, 0x38, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
+   0x00, 0xd6, 0x00, 0x00};
+#define red_width 25
+#define red_height 25
+static char red_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xfc, 0x03, 0x00,
+   0x20, 0x07, 0x06, 0x00, 0xe0, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x08, 0x00,
+   0x20, 0x00, 0x08, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x00, 0x08, 0x00,
+   0x20, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x06, 0x00, 0x20, 0x80, 0x01, 0x00,
+   0x20, 0xe0, 0x00, 0x00, 0x20, 0x7e, 0x00, 0x00, 0xe0, 0xc3, 0x00, 0x00,
+   0x20, 0xc0, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x20, 0x80, 0x01, 0x00,
+   0x20, 0x00, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00, 0x20, 0x00, 0x03, 0x00,
+   0x20, 0x00, 0x06, 0x00, 0x20, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x18, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define green_width 25
+#define green_height 25
+static char green_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00,
+   0x00, 0x06, 0x04, 0x00, 0x80, 0x03, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x80, 0x0f, 0x00, 0x40, 0xc0, 0x30, 0x00,
+   0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x08, 0x00,
+   0x00, 0x03, 0x08, 0x00, 0x00, 0x7e, 0x0e, 0x00, 0x00, 0xc0, 0x03, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define blue_width 25
+#define blue_height 25
+static char blue_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xfe, 0x00, 0x00,
+   0xc0, 0x83, 0x07, 0x00, 0xc0, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x04, 0x00, 0x40, 0x00, 0x02, 0x00,
+   0x40, 0x80, 0x07, 0x00, 0x40, 0xe0, 0x0c, 0x00, 0xc0, 0x3f, 0x08, 0x00,
+   0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x06, 0x00, 0x40, 0x00, 0x02, 0x00,
+   0xc0, 0x80, 0x03, 0x00, 0x40, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define yellow_width 25
+#define yellow_height 25
+static char yellow_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00,
+   0x80, 0x01, 0x0c, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x02, 0x02, 0x00,
+   0x00, 0x84, 0x01, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define hms_width 25
+#define hms_height 25
+static char hms_bits[] = {
+   0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+   0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0xf0, 0x00, 0x1e, 0xef, 0x89, 0x00,
+   0x22, 0x11, 0x09, 0x00, 0x22, 0x11, 0x71, 0x00, 0x22, 0x11, 0x81, 0x00,
+   0x22, 0x11, 0x89, 0x00, 0x22, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x1e, 0xc7, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x80, 0x00,
+   0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0x80, 0x00,
+   0x40, 0x80, 0x80, 0x00, 0x5c, 0xb8, 0xb8, 0x00, 0x62, 0xc4, 0xc4, 0x00,
+   0x42, 0x84, 0x84, 0x00, 0x42, 0x84, 0x84, 0x00, 0x62, 0xc4, 0xc4, 0x00,
+   0x5c, 0xb9, 0xb8, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/constants.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/constants.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/constants.h	(revision 22322)
@@ -0,0 +1,58 @@
+# define PAD1  5
+# define PAD2  3
+# define TEXTPAD 25
+# define COLORPAD 10
+# define TEXT_Y 15
+# define ZOOM_X 170
+# define ZOOM_Y 170
+# define NOVERLAYS 4
+/* number of overlays is defined here.
+   the number is also crucial in the following files:
+   PositionPictures.c
+   MakeColormap.c
+   prototypes.h
+*/
+
+# define DEFAULT_CURSOR XC_crosshair
+# define BORDER_WIDTH 2
+# define MIN_WIDTH 10
+# define MIN_HEIGHT 20
+# define AssignText(X,T) allocate (X, char, strlen (T)); strcpy (X, T);
+# define AssignData(X,T,N) allocate (X, char, N); bcopy (T, X, N);
+
+int OVERLAY[NOVERLAYS];
+int MOVE_POINTER;
+int DECIMAL_DEG;
+int DEBUG;
+int FOREGROUND;
+int USE_XWINDOW;
+int MAP_WINDOW;
+char *NAME_WINDOW;
+
+# define EVENT_MASK (long) \
+(ButtonPressMask \
+ | ClientMessage \
+ | ButtonReleaseMask \
+ | KeyPressMask \
+ | ExposureMask \
+ | StructureNotifyMask \
+ | PointerMotionMask)
+
+/* EVENT_MASK consists of:
+
+ExposureMask        : Expose
+StructureNotifyMask : CirculateNotify | 
+                      ConfigureNotify | 
+                      DestroyNotify   | 
+		      GravityNotify   | 
+		      MapNotify       |
+		      ReparentNotify  |
+		      UnmapNotify
+ButtonPressMask     : ButtonPress
+ButtonReleaseMask   : ButtonRelease
+KeyPressMask        : KeyPress
+PointerMotionMask   : MotionNotify
+(always)            : ClientMessage 
+(always)            : MappingNotify
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/arrow.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/arrow.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/arrow.h	(revision 22322)
@@ -0,0 +1,4 @@
+#define arrow_width 6
+#define arrow_height 9
+static char arrow_bits[] = {
+   0x00, 0x02, 0x06, 0x0e, 0x1e, 0x0e, 0x06, 0x02, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/back.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/back.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/back.h	(revision 22322)
@@ -0,0 +1,6 @@
+#define back_width 16
+#define back_height 16
+static char back_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x3c, 0x00, 0x3f, 0xc0, 0x3f,
+   0xf0, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xf0, 0x3f, 0xc0, 0x3f, 0x00, 0x3f,
+   0x00, 0x3c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/down.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/down.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/down.h	(revision 22322)
@@ -0,0 +1,4 @@
+#define down_width 9
+#define down_height 6
+static char down_bits[] = {
+   0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/fore.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/fore.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/fore.h	(revision 22322)
@@ -0,0 +1,6 @@
+#define fore_width 16
+#define fore_height 16
+static char fore_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x3c, 0x00, 0xfc, 0x00, 0xfc, 0x03,
+   0xfc, 0x0f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x0f, 0xfc, 0x03, 0xfc, 0x00,
+   0x3c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/icon.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/icon.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/icon.h	(revision 22322)
@@ -0,0 +1,13 @@
+#define icon_width 30
+#define icon_height 30
+static char icon_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00,
+   0x00, 0xfe, 0x0f, 0x00, 0x00, 0xff, 0x1f, 0x00, 0xc0, 0x0f, 0x7e, 0x00,
+   0xe0, 0x01, 0xf0, 0x00, 0xe0, 0xff, 0xff, 0x00, 0xf0, 0xff, 0xff, 0x01,
+   0xb8, 0xff, 0xbf, 0x03, 0xb8, 0x03, 0xb8, 0x03, 0xb8, 0xe3, 0xb8, 0x03,
+   0x9c, 0xf3, 0x39, 0x07, 0x9c, 0xfb, 0x3b, 0x07, 0x9c, 0xfb, 0x3b, 0x07,
+   0x9c, 0xfb, 0x3b, 0x07, 0x9c, 0xf3, 0x39, 0x07, 0xb8, 0xe3, 0xb8, 0x03,
+   0xb8, 0x03, 0xb8, 0x03, 0xb8, 0xff, 0xbf, 0x03, 0xf0, 0xff, 0xff, 0x01,
+   0xe0, 0xff, 0xff, 0x00, 0xe0, 0x01, 0xf0, 0x00, 0xc0, 0x0f, 0x7e, 0x00,
+   0x00, 0xff, 0x1f, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0x00, 0xf0, 0x01, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/stop.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/stop.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/icons/stop.h	(revision 22322)
@@ -0,0 +1,6 @@
+#define stop_width 16
+#define stop_height 16
+static char stop_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
+   0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
+   0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/prototypes.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/prototypes.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/prototypes.h	(revision 22322)
@@ -0,0 +1,116 @@
+
+int args (int *argc, char **argv);
+
+void bDrawOverlay (Layout *layout, int N);
+
+int SetColormap (Graphic *graphic, Layout *layout, char *name);
+void FlushDisplay (Display *display);
+int Recenter (Graphic *graphic, Layout *layout);
+void DrawOverlay (Graphic *graphic, Layout *layout, int N, FILE *f, int extra);
+int cursor (Graphic *graphic, Layout *layout);
+int PSit (Graphic *graphic, Layout *layout, int Raw);
+int JPEGit (Graphic *graphic, Layout *layout);
+int JPEGit24 (Graphic *graphic, Layout *layout);
+int Resize (Graphic *graphic, Layout *layout);
+int Center (Graphic *graphic, Layout *layout);
+int LoadTickmarks (Graphic *graphic, Layout *layout);
+void PaintTickmarks (Graphic *graphic, Layout *layout);
+void Screen_to_Image (double *x1, double *y1, double x2, double y2, Layout *layout);
+void Image_to_Screen (double *x1, double *y1, double x2, double y2, Layout *layout);
+void UpdateStatusBox (Graphic *graphic, Layout *layout, double x, double y, double z, int mode);
+void Remap8 (Graphic *graphic, Layout *layout, Matrix *matrix);
+void Remap16 (Graphic *graphic, Layout *layout, Matrix *matrix);
+void Remap24 (Graphic *graphic, Layout *layout, Matrix *matrix);
+void Remap32 (Graphic *graphic, Layout *layout, Matrix *matrix);
+void CheckVisual (Graphic *graphic, int *argc, char **argv);
+void CreateZoom8 (Layout *layout, Graphic *graphic, double x, double y);
+void CreateZoom16 (Layout *layout, Graphic *graphic, double x, double y);
+void CreateZoom24 (Layout *layout, Graphic *graphic, double x, double y);
+void CreateZoom32 (Layout *layout, Graphic *graphic, double x, double y);
+
+void ConvertPixmap8  (Layout *layout, FILE *f);
+void ConvertPixmap16 (Layout *layout, FILE *f);
+void ConvertPixmap24 (Layout *layout, FILE *f);
+void ConvertPixmap32 (Layout *layout, FILE *f);
+
+
+/***** Prototypes for image ***************/
+
+void          PositionPictures    PROTO((Layout *, Graphic *));
+void          CreateColorbar      PROTO((Layout *, Graphic *));
+void          CreatePicture       PROTO((Layout *, Graphic *));
+void          CreateZoom          PROTO((Layout *, Graphic *, double, double));
+void          Remap               PROTO((Graphic *, Layout *, Matrix  *));
+int           NewPicture          PROTO((Graphic *, Layout *));
+void          Reorient            PROTO((Graphic *, Layout *, double, double, int));
+void          ReorientOnButton    PROTO((Graphic *, Layout *, XButtonEvent *));
+int           UpdatePointer       PROTO((Graphic *, Layout *, XMotionEvent *));
+void          CrossHairs          PROTO((Graphic *, Layout *));
+int           LoadOverlay         PROTO((Graphic *, Layout *));
+int           SaveOverlay         PROTO((Graphic *, Layout *));
+int           CSaveOverlay         PROTO((Graphic *, Layout *));
+int           EraseOverlay        PROTO((Graphic *, Layout *));
+void          StatusBox           PROTO((Graphic *, Layout *));
+void          PaintOverlay        PROTO((Graphic *, Layout *, int));
+
+/***** Prototypes for action ***************/
+
+int           CheckPipe           PROTO((Graphic *, Layout *));
+void          DefineLayout        PROTO((Layout *, Graphic *, int, char **));
+int           EventLoop           PROTO((Graphic *, Layout *));
+int           InPicture           PROTO((XButtonEvent *, Picture *));
+int           InterpretKeys       PROTO((Graphic *, Layout *, XEvent *));
+int           InterpretPresses    PROTO((Graphic *, Layout *, XButtonEvent *));
+int           Reconfig            PROTO((Graphic *, Layout *, XEvent *));
+void          Refresh             PROTO((Graphic *, Layout *, int));
+int           Stop                PROTO((Graphic *, Layout *));
+int           PSit                PROTO((Graphic *, Layout *, int));
+int           get_argument        PROTO((int, char **, char *));
+int           remove_argument     PROTO((int, int *, char **));
+void          DragColorbar        PROTO((Graphic *, Layout *, XButtonEvent *));
+void          ResetColorbar       PROTO((Graphic *, Layout *, double, double));
+void          MakeColormap        PROTO((Graphic *, Layout *, int, char **));
+
+/***** Prototypes for button ***************/
+
+Button       *CheckButtons        PROTO((XButtonEvent *, Layout *));
+void          DrawButton          PROTO((Graphic *, Button *));
+void          FlashButton         PROTO((Graphic *, Button *));
+int           InButton            PROTO((XButtonEvent *, Button *));
+void          InvertButton        PROTO((Graphic *, Button *));
+int           greycolors          PROTO((Graphic *, Layout *));
+int           rainbow             PROTO((Graphic *, Layout *));
+int           puns                PROTO((Graphic *, Layout *));
+int           Recenter            PROTO((Graphic *, Layout *));
+int           RecenterRescale     PROTO((Graphic *, Layout *));
+int           Rescale             PROTO((Graphic *, Layout *));
+int           ToggleDEG           PROTO((Graphic *, Layout *));
+int           Overlay0            PROTO((Graphic *, Layout *));
+int           Overlay1            PROTO((Graphic *, Layout *));
+int           Overlay2            PROTO((Graphic *, Layout *));
+int           Overlay3            PROTO((Graphic *, Layout *));
+
+/***** Prototypes for xtools ***************/
+
+void          CheckColors         PROTO((Graphic *, int *, char **));
+void          CheckDisplayName    PROTO((int *, char **, char *));
+void          CheckFontName       PROTO((int *, char **, char *));
+void          CheckGeometry       PROTO((int *, char **, Graphic *));
+void          CloseDisplay        PROTO((Graphic *));
+void          CreateWindow        PROTO((Graphic *, Window, int, long));
+void          DrawBitmap          PROTO((Graphic *, int, int, int, int, char *, int));
+unsigned long GetColor            PROTO((Display *, char *, Colormap, unsigned long));
+void          LoadFont            PROTO((Graphic *, int *, char **, char *));
+void          MakeCursor          PROTO((Graphic *, unsigned int));
+void          MakeGC              PROTO((Graphic *));
+void          MapWindow           PROTO((Graphic *));
+void          NameWindow          PROTO((Graphic *, char *));
+Display      *OpenDisplay         PROTO((char *, int *));
+void          QuitX               PROTO((Display *, char *, char *));
+void          SetNormalHints      PROTO((Graphic *));
+void          SetUpDisplay        PROTO((Graphic *, int *, char **));
+void          SetUpWindow         PROTO((Graphic *, int *, char **));
+void          SetWMHints          PROTO((Graphic *, Icon *));
+void          TopWindow           PROTO((Graphic *, Icon *));
+
+void          hh_hms              PROTO((char *, double, double, char));
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/structures.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/structures.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/include/structures.h	(revision 22322)
@@ -0,0 +1,128 @@
+/**************** Graphic carries X info around ****************/
+typedef struct {
+  Display       *display;
+  int            screen;
+  int            depth;
+  Window         window;
+  Visual        *visual;
+  int            visualclass;
+  GC             gc;
+  XFontStruct   *font;
+  Cursor         cursor;
+  int            x,  y;
+  int            dx, dy;
+  Colormap       colormap;
+  unsigned long  Npixels, pixels[256];
+  int            Nbits;
+  /*
+  unsigned long  fore;
+  unsigned long  back;
+  */
+  unsigned long  black, white;
+} Graphic;
+
+/**************** X related "widget" structures ****************/
+typedef struct {
+  int      x, y, dx, dy;      /* position and size */
+  int      text;              /* does this have a picture or text? */
+  int      width, height;
+  char    *bitmap;          /* picture on button */
+  int    (*function_1) ();  /* mouse_button 1 function */
+  int    (*function_2) ();  /* mouse_button 2 function */
+  int    (*function_3) ();  /* mouse_button 3 function */
+} Button;
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  char    *label;          /* label on TextLine */
+  char     text[1024];     /* words of TextLine */
+  char     old_text[1024]; /* words of TextLine */
+  int      outline;        /* draw an outline?  */ 
+  int      cursor;         /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextLine;
+
+typedef char STRING[1024];
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  STRING  *text;           /* words of TextLine */
+  int      Nlines;
+  int      outline;        /* draw an outline?  */ 
+  int      cursor_line;    /* cursor line */
+  int      cursor_x;       /* location of cursor (if selected) */
+  int      cursor_y;       /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextBox;
+
+/**************** general structures ****************/
+typedef struct {
+  Pixmap pixmap;
+  int    width;
+  int    height;
+  char  *bits;
+} Icon;
+
+typedef struct {
+  int      dx, dy, x, y;
+  XImage  *pix;
+  char *data;
+} Picture;
+
+typedef struct {
+  char type[10];
+  double x, y;
+  double dx, dy;
+  double angle;
+  char  *text;
+} Object;
+
+typedef struct {
+  int Nobjects;
+  unsigned long color;
+  Object *objects;
+} Overlay;
+  
+/******** Here we define the Layout struct specific to this program  *******/
+typedef struct {
+  /* objects on the mana window */
+  Picture  picture;
+  Picture  cmapbar;
+  Picture  zoom;
+  Button   PS_button;
+  Button   recenter_button;
+  Button   hms_button; /* toggle HMS/DECIMAL mode (DECIMAL_DEG macro) */
+  Button   grey_button;
+  Button   rainbow_button;
+  Button   puns_button;
+  Button   overlay_button[NOVERLAYS];
+  Overlay  overlay[NOVERLAYS];
+  Overlay  tickmarks;
+  int      text_x, text_y;
+
+  /* file descriptor for socket connection to mana */
+  int Ximage; 
+
+  /* data mana needs */
+  Matrix   matrix;         /* data for picture */
+  double   X, Y;           /* image pixel at screen center */
+  int      expand;         /* zoomscale */
+  double   zero, range;    /* zero, range for picture to cmap */
+  double   max, min;       /* zero, range for data to z-value */
+  double   start, slope;   /* zero, range for cmap to pixels */
+  double   x, y, z;        /* last pointer coords */
+  Coords   coords;
+  char     file[1024];     /* name of file */
+  char     buffer_name[1024];  /* name of buffer */
+
+  /* fundamental pieces */
+  unsigned long  black;
+  unsigned long  white;
+  XColor   cmap[256];
+  int      Npixels;
+
+} Layout;
+
+/* this routine is independent of the number of overlays */
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/misc/hh_hms.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/misc/hh_hms.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/misc/hh_hms.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "Ximage.h"
+
+void hh_hms (char *line, double ra, double dec, char sep) {
+
+  int h, m, flag;
+  double s;
+  
+  ra /= 15.0;  /* convert from degrees to hours */
+  flag = SIGN(ra);
+  ra *= flag;
+  h = ra;
+  m = 60.000001*(ra - h);
+  s = 3600*(ra - h - m / 60.0);
+  if (flag > 0)
+    sprintf (line, " %02d%c%02d%c%04.1f  ", h, sep, m, sep, s);
+  else
+    sprintf (line, "-%02d%c%02d%c%04.1f  ", h, sep, m, sep, s);
+  
+  flag = SIGN(dec);
+  dec *= flag;
+  h = dec;
+  m = 60.000001*(dec - h);
+  s = 3600*(dec - h - m / 60.0);
+  if (flag > 0)
+    sprintf (&line[13], " %02d%c%02d%c%04.1f", h, sep, m, sep, s);
+  else
+    sprintf (&line[13], "-%02d%c%02d%c%04.1f", h, sep, m, sep, s);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/CSaveOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/CSaveOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/CSaveOverlay.c	(revision 22322)
@@ -0,0 +1,51 @@
+# include "Ximage.h"
+
+/*************/
+int CSaveOverlay (Graphic *graphic, Layout *layout) {
+
+  char buffer[256], filename[256];
+  int i, status, Nbytes, N;
+  double ra, dec, ra1, dec1, x1, y1, dra, ddec;
+  FILE *f;
+
+  status = read (layout[0].Ximage, buffer, 16); 
+  buffer[16] = 0; 
+  sscanf (buffer, "%*s %d", &Nbytes); 
+  status = read (layout[0].Ximage, buffer, Nbytes); 
+  buffer[Nbytes] = 0; 
+  sscanf (buffer, "%*s %d %s", &N, filename);
+  
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "could not open %s\n", filename);
+    return (TRUE);
+  }
+
+  for (i = 0; i < layout[0].overlay[N].Nobjects; i++) {
+    if (!strcmp (layout[0].overlay[N].objects[i].type, "LINE")) {
+      XY_to_RD (&ra, &dec, layout[0].overlay[N].objects[i].x, layout[0].overlay[N].objects[i].y, &layout[0].coords);
+      x1 = layout[0].overlay[N].objects[i].x + layout[0].overlay[N].objects[i].dx;
+      y1 = layout[0].overlay[N].objects[i].y + layout[0].overlay[N].objects[i].dy;
+      XY_to_RD (&ra1, &dec1, x1, y1, &layout[0].coords);
+      dra = (ra1 - ra);
+      ddec = (dec1 - dec);
+    } else {
+      XY_to_RD (&ra, &dec, layout[0].overlay[N].objects[i].x, layout[0].overlay[N].objects[i].y, &layout[0].coords);
+      x1 = layout[0].overlay[N].objects[i].x;
+      y1 = layout[0].overlay[N].objects[i].y + layout[0].overlay[N].objects[i].dy;
+      XY_to_RD (&ra1, &dec1, x1, y1, &layout[0].coords);
+      ddec = fabs (dec1 - dec);
+      x1 = layout[0].overlay[N].objects[i].x + layout[0].overlay[N].objects[i].dx;
+      y1 = layout[0].overlay[N].objects[i].y;
+      XY_to_RD (&ra1, &dec1, x1, y1, &layout[0].coords);
+      dra = cos (dec*RAD_DEG) * fabs (ra1 - ra);
+    }
+    fprintf (f, "%s %lf %lf %lf %lf\n", layout[0].overlay[N].objects[i].type, ra, dec, dra, ddec);
+   }
+  fclose (f);
+  return (TRUE);
+}
+
+
+/* this routine is independent of the number of overlays */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/DrawOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/DrawOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/DrawOverlay.c	(revision 22322)
@@ -0,0 +1,82 @@
+# include "Ximage.h"
+# define INFRONT 3
+
+void DrawOverlay (Graphic *graphic, Layout *layout, int N, FILE *f, int extra) {
+
+  int i;
+  double X, Y, dX, dY;
+  int Xmin, Ymin, Xmax, Ymax;
+  double expand, X0, Y0;
+ 
+  expand = 1.0;
+  if (layout[0].expand > 0) {
+    expand = 1 / (1.0*layout[0].expand);
+  }
+  if (layout[0].expand < 0) {
+    expand = fabs((double)layout[0].expand);
+  }
+  /*  
+  X0 =  (layout[0].X + 1 - 0.5*layout[0].matrix.Naxis[0])/expand + 0.5*layout[0].picture.dx;
+  Y0 =  (layout[0].Y + 1 - 0.5*layout[0].matrix.Naxis[1])/expand + 0.5*layout[0].picture.dy;
+  */
+  Image_to_Screen (&X0, &Y0, 0.0, 0.0, layout);
+  X0 -= layout[0].picture.x;
+  Y0 -= layout[0].picture.y;
+
+  Xmin = 0;
+  Ymin = 0;
+  Xmax = layout[0].picture.dx;
+  Ymax = layout[0].picture.dy;
+
+  for (i = 0; i < layout[0].overlay[N].Nobjects; i++) {
+    if (N == INFRONT) {
+      X  = layout[0].overlay[N].objects[i].x * Xmax;
+      Y  = layout[0].overlay[N].objects[i].y * Ymax;
+      dX = layout[0].overlay[N].objects[i].dx * Xmax;
+      dY = layout[0].overlay[N].objects[i].dy * Ymax;
+    } else {
+      X  = (layout[0].overlay[N].objects[i].x)/expand + X0;
+      Y =  Ymax - (layout[0].overlay[N].objects[i].y)/expand - Y0;
+      dX = (layout[0].overlay[N].objects[i].dx)/expand;
+      dY = (layout[0].overlay[N].objects[i].dy)/expand;
+    }
+    
+    if (!strcmp (layout[0].overlay[N].objects[i].type, "LINE")) {
+      if (((X < Xmin) && (X + dX < Xmin)) || ((X > Xmax) && (X + dX > Xmax)) ||
+	  ((Y < Ymin) && (Y + dY < Ymin)) || ((Y > Ymax) && (Y + dY > Ymax))) {
+	continue;
+      }
+      fprintf (f, " %6.1f %6.1f %6.1f %6.1f L\n", X + extra, Y + extra, (X+dX + extra), (Y-dY + extra));
+      continue;
+    }
+    if (!strcmp (layout[0].overlay[N].objects[i].type, "TEXT")) {
+      if (((X < Xmin) && (X + dX < Xmin)) || ((X > Xmax) && (X + dX > Xmax)) ||
+	  ((Y < Ymin) && (Y + dY < Ymin)) || ((Y > Ymax) && (Y + dY > Ymax))) {
+	continue;
+      }
+      fprintf (f, "(%s) %6.1f %6.1f T\n", layout[0].overlay[N].objects[i].text, X + extra, Y + extra); 
+      continue;
+    }
+    if (!strcmp (layout[0].overlay[N].objects[i].type, "BOX")) {
+      if (((X - 0.5*dX < Xmin) && (X + 0.5*dX < Xmin)) || ((X - 0.5*dX > Xmax) && (X + 0.5*dX > Xmax)) ||
+	  ((Y - 0.5*dY < Ymin) && (Y + 0.5*dY < Ymin)) || ((Y - 0.5*dY > Ymax) && (Y + 0.5*dY > Ymax))) {
+	continue;
+      }
+      fprintf (f, " %6.1f %6.1f %6.1f %6.1f B\n", (dX + 2*extra), (dY + 2*extra), (X - 0.5*dX - extra), (Y - 0.5*dY - extra));
+      continue;
+    }
+    if (!strcmp (layout[0].overlay[N].objects[i].type, "CIRCLE")) {
+      if (((X - dX < Xmin) && (X + dX < Xmin)) || ((X - dX > Xmax) && (X + dX > Xmax)) ||
+	  ((Y - dY < Ymin) && (Y + dY < Ymin)) || ((Y - dY > Ymax) && (Y + dY > Ymax))) {
+	continue;
+      }
+      fprintf (f, " %6.1f %6.1f %6.1f C\n", X, Y, fabs(dX + extra));
+      continue;
+    }
+    fprintf (stderr, "don't know %s, skipping\n", layout[0].overlay[N].objects[i].type);
+  }
+  
+}
+
+/* this routine is independent of the number of overlays */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/EraseOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/EraseOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/EraseOverlay.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "Ximage.h"
+
+int EraseOverlay (Graphic *graphic, Layout *layout) {
+
+  char buffer[256];
+  int i, status, N;
+
+  fcntl (layout[0].Ximage, F_SETFL, !O_NONBLOCK);  
+  status = read (layout[0].Ximage, buffer, 16); 
+  buffer[16] = 0; 
+  sscanf (buffer, "%*s %d", &N); 
+  if (N > NOVERLAYS) {
+    REALLOCATE (layout[0].tickmarks.objects, Object, 1);
+    layout[0].tickmarks.Nobjects = 0;
+  } else {
+    for (i = 0; i < layout[0].overlay[N].Nobjects; i++) {
+      if (!strcmp (layout[0].overlay[N].objects[i].type, "TEXT")) {
+	free (layout[0].overlay[N].objects[i].text);
+      }
+    }
+    REALLOCATE (layout[0].overlay[N].objects, Object, 1);
+    layout[0].overlay[N].Nobjects = 0;
+    OVERLAY[N] = FALSE;
+  }
+
+  if (USE_XWINDOW) Refresh (graphic, layout, 0);
+
+  fcntl (layout[0].Ximage, F_SETFL, O_NONBLOCK);  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/LoadOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/LoadOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/LoadOverlay.c	(revision 22322)
@@ -0,0 +1,104 @@
+# include "Ximage.h"
+
+int LoadOverlay (graphic, layout)
+     Graphic graphic[];
+     Layout  layout[];
+{
+  
+  char line[17], *buffer, *buff, type[16], string[128];
+  double x, y, dx, dy;
+  int Nin, bytes, status;
+  int i, N, NOBJECTS, Nobjects, Nstart, done;
+
+  ALLOCATE (buffer, char, 65536);  /* space for 512 lines of 128 bytes */
+  bzero (buffer, 65536);
+  done = FALSE;
+
+  fcntl (layout[0].Ximage, F_SETFL, !O_NONBLOCK);  
+
+  status = read (layout[0].Ximage, line, 16); 
+  line[16] = 0; 
+  sscanf (line, "%*s %d", &N); 
+  Nstart = layout[0].overlay[N].Nobjects;
+
+  while (!done) {
+    status = read (layout[0].Ximage, line, 16); 
+    line[16] = 0; 
+    
+    if (!strncmp (line, "DONE", 4)) {
+      done = TRUE;
+      break;
+    }
+
+    if (!strncmp (line, "NLINES", 6)) {
+      sscanf (line, "%*s %d", &Nin);
+      buff = buffer;
+      bytes = 128*Nin;
+      fcntl (layout[0].Ximage, F_SETFL, O_NONBLOCK);  
+      while (bytes > 0) { 
+	status = read (layout[0].Ximage, buff, bytes);
+	if (status == 0) {  /* No more pipe */
+	  fprintf (stderr, "error: pipe closed\n");
+	  free (buffer);
+	  fcntl (layout[0].Ximage, F_SETFL, O_NONBLOCK);  
+	  return (FALSE);
+	}
+	if (status != -1) { /* pipe has data */
+	  bytes -= status;
+	  buff = (char *)(buff + status);
+	}
+      }
+      fcntl (layout[0].Ximage, F_SETFL, !O_NONBLOCK);  
+      /* parse buffer data */
+      NOBJECTS = layout[0].overlay[N].Nobjects + Nin;
+      Nobjects = layout[0].overlay[N].Nobjects;
+      REALLOCATE (layout[0].overlay[N].objects, Object, NOBJECTS);
+      for (i = 0; i < Nin; i++) {
+	sscanf (&buffer[i*128], "%s %lf %lf %lf %lf\n", type, &x, &y, &dx, &dy);
+
+	if (strcasecmp (type, "TEXT") && strcasecmp (type, "LINE") && strcasecmp (type, "BOX") && strcasecmp (type, "CIRCLE")) {  /* skip */
+	  fprintf (stderr, "don't know %s, skipping\n", type);
+	  continue;
+	}
+
+	if (!strcasecmp (type, "TEXT")) { /* end of objects */
+	  sscanf (&buffer[i*128], "%s %lf %lf %s\n", type, &x, &y, string);
+	}
+	
+	strcpy (layout[0].overlay[N].objects[Nobjects].type, type);
+	layout[0].overlay[N].objects[Nobjects].x = x;
+	layout[0].overlay[N].objects[Nobjects].y = y;
+	if (!strcmp (type, "TEXT")) {
+	  layout[0].overlay[N].objects[Nobjects].dx = 0;
+	  layout[0].overlay[N].objects[Nobjects].dy = 0;
+	  layout[0].overlay[N].objects[Nobjects].text = strcreate (string);
+	} else {
+	  layout[0].overlay[N].objects[Nobjects].dx = dx;
+	  layout[0].overlay[N].objects[Nobjects].dy = dy;
+	}
+	Nobjects++;
+      }
+      REALLOCATE (layout[0].overlay[N].objects, Object, MAX(Nobjects, 1));
+      layout[0].overlay[N].Nobjects = Nobjects;
+    }
+  }
+
+  free (buffer);
+  REALLOCATE (layout[0].overlay[N].objects, Object, MAX (1, layout[0].overlay[N].Nobjects));
+  OVERLAY[N] = TRUE;
+  /*  Refresh (graphic, layout, 0); */
+  if (USE_XWINDOW) {
+    for (i = 0; i < NOVERLAYS; i++) {
+      if (OVERLAY[i])
+	PaintOverlay (graphic, layout, i);
+    }
+    PaintTickmarks (graphic, layout);
+    XFlush (graphic[0].display);
+  }
+  fcntl (layout[0].Ximage, F_SETFL, O_NONBLOCK);  
+  return (TRUE);
+
+}
+
+
+/* this routine is independent of the number of overlays */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/LoadTickmarks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/LoadTickmarks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/LoadTickmarks.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "Ximage.h"
+
+int LoadTickmarks (Graphic *graphic, Layout *layout) {
+  
+  char line[129], type[16];
+  double x, y, dx, dy;
+  int status, NOBJECTS, Nobjects, done;
+
+  fcntl (layout[0].Ximage, F_SETFL, !O_NONBLOCK);  
+
+  Nobjects = layout[0].tickmarks.Nobjects;
+  NOBJECTS = Nobjects + 100;
+  REALLOCATE (layout[0].tickmarks.objects, Object, NOBJECTS);
+  
+  done = FALSE;
+  while (!done) {
+    status = read (layout[0].Ximage, line, 128); 
+    line[128] = 0; 
+
+    if (!strncmp (line, "DONE", 4)) {
+      done = TRUE;
+      break;
+    }
+    
+    sscanf (line, "%s %lf %lf %lf %lf\n", type, &x, &y, &dx, &dy);
+    
+    if (strcmp (type, "TEXT") && strcmp (type, "LINE") && strcmp (type, "BOX") && strcmp (type, "CIRCLE")) {  /* skip */
+      fprintf (stderr, "don't know %s, skipping\n", type);
+      continue;
+    }
+    
+    strcpy (layout[0].tickmarks.objects[Nobjects].type, type);
+    layout[0].tickmarks.objects[Nobjects].x = x;
+    layout[0].tickmarks.objects[Nobjects].y = y;
+    layout[0].tickmarks.objects[Nobjects].dx = dx;
+    layout[0].tickmarks.objects[Nobjects].dy = dy;
+    
+    if (!strcmp (type, "TEXT")) { /* dx = Nchar, dy = angle (not yet used) */
+      status = read (layout[0].Ximage, line, 128); 
+      line[128] = 0; 
+      ALLOCATE (layout[0].tickmarks.objects[Nobjects].text, char, (int) dx + 1);
+      strncpy (layout[0].tickmarks.objects[Nobjects].text, line, (int) dx);
+      layout[0].tickmarks.objects[Nobjects].text[(int)dx] = 0;
+    }      
+    
+    Nobjects++;
+    if (Nobjects >= NOBJECTS) {
+      NOBJECTS = Nobjects + 100;
+      REALLOCATE (layout[0].tickmarks.objects, Object, NOBJECTS);
+    }
+
+  }
+
+  REALLOCATE (layout[0].tickmarks.objects, Object, MAX(Nobjects, 1));
+  layout[0].tickmarks.Nobjects = Nobjects;
+
+  if (USE_XWINDOW) Refresh (graphic, layout, 0);
+
+  fcntl (layout[0].Ximage, F_SETFL, O_NONBLOCK);  
+  return (TRUE);
+  
+}
+
+
+/* this routine is independent of the number of overlays */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/PaintOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/PaintOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/PaintOverlay.c	(revision 22322)
@@ -0,0 +1,85 @@
+# include "Ximage.h"
+# define INFRONT 4
+
+void PaintOverlay (Graphic *graphic, Layout *layout, int N) {
+
+  int i;
+  int dX, dY, dx, dy;
+  int X, Y, Xmin, Ymin, Xmax, Ymax, Xrange, Yrange;
+  double expand, X0, Y0;
+ 
+  XSetForeground (graphic[0].display, graphic[0].gc, layout[0].overlay[N].color);
+  
+  expand = 1.0;
+  if (layout[0].expand > 0) {
+    expand = 1 / (1.0*layout[0].expand);
+  }
+  if (layout[0].expand < 0) {
+    expand = fabs((double)layout[0].expand);
+  }
+  /* 
+  X = 0.5*layout[0].matrix.Naxis[0] - expand*((int)(0.5*layout[0].picture.dx + 0.5) - 0.0) - layout[0].X;
+  Y = 0.5*layout[0].matrix.Naxis[1] - expand*((int)(0.5*layout[0].picture.dy + 0.5) - 0.0) - layout[0].Y;  
+  X0 =  (layout[0].X + 1 - (int)(0.5*layout[0].matrix.Naxis[0] + 0.5) - X + (int)X)/expand + layout[0].picture.x + 0.5*layout[0].picture.dx;
+  Y0 =  (layout[0].Y + 1 - (int)(0.5*layout[0].matrix.Naxis[1] + 0.5) - Y + (int)Y)/expand + layout[0].picture.y + 0.5*layout[0].picture.dy;
+  */
+
+  Image_to_Screen (&X0, &Y0, 0.0, 0.0, layout);
+
+  Xmin = layout[0].picture.x;
+  Ymin = layout[0].picture.y;
+  Xmax = layout[0].picture.x + layout[0].picture.dx;
+  Ymax = layout[0].picture.y + layout[0].picture.dy;
+  Xrange = layout[0].picture.dx;
+  Yrange = layout[0].picture.dy;
+
+  for (i = 0; i < layout[0].overlay[N].Nobjects; i++) {
+    if (N == INFRONT) {
+      X  = layout[0].overlay[N].objects[i].x * Xrange + Xmin;
+      Y  = layout[0].overlay[N].objects[i].y * Yrange + Ymin;
+      dX = layout[0].overlay[N].objects[i].dx * Xrange;
+      dY = layout[0].overlay[N].objects[i].dy * Yrange;
+    } else {
+      X  = layout[0].overlay[N].objects[i].x/expand + X0;
+      Y =  layout[0].overlay[N].objects[i].y/expand + Y0;
+      dX = layout[0].overlay[N].objects[i].dx/expand;
+      dY = layout[0].overlay[N].objects[i].dy/expand;
+    }
+    if ((X + dX < Xmin) || (X - dX > Xmax) ||
+	(Y + dY < Ymin) || (Y - dY > Ymax)) {
+      continue;
+    }
+
+    /* for a LINE, (x, y) is the start, (dx, dy) is the distance to end
+       for a CIRCLE (x, y) is the center, (dx, dy) is the radius 
+       for a BOX (x, y) is the center, (dx, dy) is the width */
+
+    if (!strcasecmp (layout[0].overlay[N].objects[i].type, "LINE")) {
+      XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, (X+dX), (Y+dY));
+      continue;
+    }
+    if (!strcasecmp (layout[0].overlay[N].objects[i].type, "TEXT")) {
+      XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, layout[0].overlay[N].objects[i].text, strlen(layout[0].overlay[N].objects[i].text));
+      continue;
+    }
+    if (!strcasecmp (layout[0].overlay[N].objects[i].type, "BOX")) {
+      dx = MAX (abs(dX),2) / 2;
+      dy = MAX (abs(dY),2) / 2;
+      XDrawRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, (X - dx), (Y - dy), 2*dx, 2*dy);
+       continue;
+    }
+    if (!strcasecmp (layout[0].overlay[N].objects[i].type, "CIRCLE")) {
+      dx = MAX (abs(dX),2);
+      dy = MAX (abs(dY),2);
+      XDrawArc (graphic[0].display, graphic[0].window, graphic[0].gc, (X - dx), (Y - dy), 2*dx, 2*dy, 0, 23040);
+      continue;
+    }
+    fprintf (stderr, "don't know %s, skipping (PO)\n", layout[0].overlay[N].objects[i].type);
+  }
+  
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+
+}
+
+/* this routine is independent of the number of overlays */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/PaintTickmarks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/PaintTickmarks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/PaintTickmarks.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "Ximage.h"
+# define INFRONT 3
+/* # include "rotletters.h" */
+
+void PaintTickmarks (Graphic *graphic, Layout *layout) {
+
+  int i;
+  int X, Y, dX, dY;
+  int Xmin, Ymin, Xmax, Ymax, Xrange, Yrange;
+ 
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+
+  Xmin = layout[0].picture.x;
+  Ymin = layout[0].picture.y;
+  Xmax = layout[0].picture.x + layout[0].picture.dx;
+  Ymax = layout[0].picture.y + layout[0].picture.dy;
+  Xrange = layout[0].picture.dx;
+  Yrange = layout[0].picture.dy;
+
+  for (i = 0; i < layout[0].tickmarks.Nobjects; i++) {
+    X  = layout[0].tickmarks.objects[i].x * Xrange + Xmin;
+    Y  = layout[0].tickmarks.objects[i].y * Yrange + Ymin;
+    dX = layout[0].tickmarks.objects[i].dx * Xrange;
+    dY = layout[0].tickmarks.objects[i].dy * Yrange;
+
+    if (!strcmp (layout[0].tickmarks.objects[i].type, "LINE")) {
+      XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, (X+dX), (Y+dY));
+      continue;
+    }
+    if (!strcmp (layout[0].tickmarks.objects[i].type, "TEXT")) {
+      if (layout[0].tickmarks.objects[i].dy == 0) {
+	XSetForeground (graphic[0].display, graphic[0].gc, layout[0].white);
+	XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y-11, 6*strlen(layout[0].tickmarks.objects[i].text), 11); 
+	XSetForeground (graphic[0].display, graphic[0].gc, layout[0].black);
+	XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, layout[0].tickmarks.objects[i].text, strlen(layout[0].tickmarks.objects[i].text));
+      }
+      if (layout[0].tickmarks.objects[i].dy == 90) {
+	XSetForeground (graphic[0].display, graphic[0].gc, layout[0].white);
+	XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y-6*strlen(layout[0].tickmarks.objects[i].text), 11, 6*strlen(layout[0].tickmarks.objects[i].text)); 
+	XSetForeground (graphic[0].display, graphic[0].gc, layout[0].black);
+	/* XDrawRotString (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, layout[0].tickmarks.objects[i].text, strlen(layout[0].tickmarks.objects[i].text)); */
+      }
+      continue;
+    }
+    if (!strcmp (layout[0].tickmarks.objects[i].type, "BOX")) {
+      XDrawRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, (int)(X - 0.5*dX), (int)(Y - 0.5*dY), abs(dX), abs(dY));
+       continue;
+    }
+    if (!strcmp (layout[0].tickmarks.objects[i].type, "CIRCLE")) {
+      XDrawArc (graphic[0].display, graphic[0].window, graphic[0].gc, X - dX, Y - dY, abs(2*dX), abs(2*dY), 0, 23040);
+      continue;
+    }
+    fprintf (stderr, "don't know %s, skipping (PO)\n", layout[0].tickmarks.objects[i].type);
+  }
+  
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+
+}
+
+/* this routine is independent of the number of overlays */
+
+
+/*
+XDrawRotString (dpy, win, gc, X, Y, text, len)
+     
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/RotFont.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/RotFont.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/RotFont.c	(revision 22322)
@@ -0,0 +1,124 @@
+# include "Ximage.h"
+# include "alphabet.h"
+  
+static int Nrotfonts;
+static FontSet *RotFonts;
+
+static char currentname[64];
+static int  currentsize;
+static double currentscale;
+static RotFont *currentfont;
+
+void InitRotFonts () {
+
+  int i, Nhardwired;
+
+  Nhardwired = sizeof (HardwiredFonts) / sizeof (FontSet);
+  
+  Nrotfonts = Nhardwired;
+  ALLOCATE (RotFonts, FontSet, Nrotfonts);
+  
+  for (i = 0; i < Nhardwired; i++) {
+    RotFonts[i] = HardwiredFonts[i];
+  }
+
+  currentfont = RotFonts[DEFFONT].font;
+  currentscale = 1.0;
+  strcpy (currentname, RotFonts[DEFFONT].name);
+  currentsize = RotFonts[DEFFONT].size;
+}
+
+int SetRotFont (char *name, int size) {
+  
+  int i, nsize, msize, bsize, bigger, dsize, match, good;
+
+  bigger = good = match = -1;
+  dsize = 10000;
+  bsize = 10000;
+  for (i = 0; i < Nrotfonts; i++) {
+    if (!strcasecmp (RotFonts[i].name, name)) {
+      good = i;
+      nsize = abs (RotFonts[i].size - size);
+      if (nsize < dsize) {
+	match = i;
+	dsize = nsize;
+      }
+      msize = RotFonts[i].size - size;
+      if ((msize < bsize) && (msize >= 0)) {
+	bigger = i;
+	bsize = msize;
+      }
+    }
+  }
+  
+  if ((match == -1) && (good != -1)) match = good;
+  if (bigger != -1) match = bigger;
+  if (match != -1) {
+    currentfont = RotFonts[match].font;
+    currentscale = (double) size / RotFonts[match].size;
+    currentsize = size;
+    strcpy (currentname, name);
+    return (TRUE);
+  } else {
+    fprintf (stderr, "no matching font\n");
+    return (FALSE);
+  }
+
+}
+  
+char *GetRotFont (int *size) {
+
+  *size = currentsize;
+  return (currentname);
+
+}
+
+RotFont *GetRotFontData (double *scale) {
+  *scale = currentscale;
+  return (currentfont);
+}
+
+int RotStrlen (char *c) {
+
+  int i, N, dX, code;
+  double scale; 
+  
+  scale = currentscale;
+
+  /* find string length */
+  dX = 0;
+
+  code = FALSE;
+  for (i = 0; i < strlen (c); i++) {
+    N = (int)(c[i]);
+    /* skip non-printing characters */
+    if ((N < 0) || (N >= NROT)) continue;
+
+    /* check for special characters */
+    if (!code) {
+      if (N == 94) { /* super-script */
+	scale *= 0.8;
+	continue;
+      }
+      if (N == 95) { /* sub-script */
+	scale *= 0.8;
+	continue;
+      }
+      if (N == 124) { /* normal-script */
+	scale = currentscale;
+	continue;
+      }
+      if (N == 92) { /* backslash */
+	code = TRUE;
+	continue;
+      } 
+      if (N == 38) { /* font-code */
+	i++;
+	continue;
+      }
+    }
+    code = FALSE;
+    dX += scale*currentfont[N].dx + 1;
+  }
+  return (dX);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/SaveOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/SaveOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/SaveOverlay.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "Ximage.h"
+
+/*************/
+int 
+SaveOverlay (graphic, layout)
+     Graphic graphic[];
+     Layout  layout[];
+{
+
+  char buffer[256], filename[256];
+  int i, status, Nbytes, N;
+  FILE *f;
+
+  status = read (layout[0].Ximage, buffer, 16); 
+  buffer[16] = 0; 
+  sscanf (buffer, "%*s %d", &Nbytes); 
+  status = read (layout[0].Ximage, buffer, Nbytes); 
+  buffer[Nbytes] = 0; 
+  sscanf (buffer, "%*s %d %s", &N, filename);
+  
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "could not open %s\n", filename);
+    return (TRUE);
+  }
+
+  for (i = 0; i < layout[0].overlay[N].Nobjects; i++) {
+    fprintf (f, "%s %lf %lf %lf %lf\n", 
+	     layout[0].overlay[N].objects[i].type,
+	     layout[0].overlay[N].objects[i].x,
+	     layout[0].overlay[N].objects[i].y,
+	     layout[0].overlay[N].objects[i].dx,
+	     layout[0].overlay[N].objects[i].dy);
+  }
+  fclose (f);
+  return (TRUE);
+}
+
+
+/* this routine is independent of the number of overlays */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/bDrawOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/bDrawOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/overlay/bDrawOverlay.c	(revision 22322)
@@ -0,0 +1,93 @@
+# include "Ximage.h"
+# define INFRONT 4
+
+static char name[4][16] = {"red", "green", "blue", "yellow"};
+
+void bDrawOverlay (Layout *layout, int N) {
+
+  int i;
+  int dX, dY, dx, dy;
+  int X, Y, Xmin, Ymin, Xmax, Ymax, Xrange, Yrange;
+  double expand, X0, Y0;
+  bDrawColor color;
+ 
+  /* translate color to bDrawColors : layout[0].overlay[N].color */
+  color = KapaColorByName (name[N]);
+  bDrawSetStyle (color, 0, 0);
+  
+  expand = 1.0;
+  if (layout[0].expand > 0) {
+    expand = 1 / (1.0*layout[0].expand);
+  }
+  if (layout[0].expand < 0) {
+    expand = fabs((double)layout[0].expand);
+  }
+  /* 
+  X = 0.5*layout[0].matrix.Naxis[0] - expand*((int)(0.5*layout[0].picture.dx + 0.5) - 0.0) - layout[0].X;
+  Y = 0.5*layout[0].matrix.Naxis[1] - expand*((int)(0.5*layout[0].picture.dy + 0.5) - 0.0) - layout[0].Y;  
+  X0 =  (layout[0].X + 1 - (int)(0.5*layout[0].matrix.Naxis[0] + 0.5) - X + (int)X)/expand + layout[0].picture.x + 0.5*layout[0].picture.dx;
+  Y0 =  (layout[0].Y + 1 - (int)(0.5*layout[0].matrix.Naxis[1] + 0.5) - Y + (int)Y)/expand + layout[0].picture.y + 0.5*layout[0].picture.dy;
+  */
+
+  Image_to_Screen (&X0, &Y0, 0.0, 0.0, layout);
+  X0 -= layout[0].picture.x;
+  Y0 -= layout[0].picture.y;
+
+  Xmin = 0;
+  Ymin = 0;
+  Xmax = layout[0].picture.dx;
+  Ymax = layout[0].picture.dy;
+  Xrange = layout[0].picture.dx;
+  Yrange = layout[0].picture.dy;
+
+  for (i = 0; i < layout[0].overlay[N].Nobjects; i++) {
+    if (N == INFRONT) {
+      X  = layout[0].overlay[N].objects[i].x * Xrange;
+      Y  = layout[0].overlay[N].objects[i].y * Yrange;
+      dX = layout[0].overlay[N].objects[i].dx * Xrange;
+      dY = layout[0].overlay[N].objects[i].dy * Yrange;
+    } else {
+      X  = layout[0].overlay[N].objects[i].x/expand + X0;
+      Y =  layout[0].overlay[N].objects[i].y/expand + Y0;
+      dX = layout[0].overlay[N].objects[i].dx/expand;
+      dY = layout[0].overlay[N].objects[i].dy/expand;
+    }
+    if ((X + dX < Xmin) || (X - dX > Xmax) ||
+	(Y + dY < Ymin) || (Y - dY > Ymax)) {
+      continue;
+    }
+
+    /* for a LINE, (x, y) is the start, (dx, dy) is the distance to end
+       for a CIRCLE (x, y) is the center, (dx, dy) is the radius 
+       for a BOX (x, y) is the center, (dx, dy) is the width */
+
+    if (!strcasecmp (layout[0].overlay[N].objects[i].type, "LINE")) {
+      bDrawLine (X, Y, (X+dX), (Y+dY));
+      continue;
+    }
+    if (!strcasecmp (layout[0].overlay[N].objects[i].type, "TEXT")) {
+      bDrawRotText (X, Y, layout[0].overlay[N].objects[i].text, 8, 0.0);
+      continue;
+    }
+    if (!strcasecmp (layout[0].overlay[N].objects[i].type, "BOX")) {
+      dx = MAX (abs(dX),2) / 2;
+      dy = MAX (abs(dY),2) / 2;
+      bDrawRectOpen ((X-dx), (Y-dy), (X+dx), (Y+dy));
+      // bDrawRectOpen ((X-dx), (Y-dy), (X), (Y));
+       continue;
+    }
+    if (!strcasecmp (layout[0].overlay[N].objects[i].type, "CIRCLE")) {
+      dx = MAX (abs(dX),2);
+      dy = MAX (abs(dY),2);
+      bDrawArc (X, Y, dx, dy, 0, 360);
+      continue;
+    }
+    fprintf (stderr, "don't know %s, skipping (PO)\n", layout[0].overlay[N].objects[i].type);
+  }
+  
+  /* translate color to bDrawColors : layout[0].overlay[N].color */
+  bDrawSetStyle (color, 0, 0);
+}
+
+/* this routine is independent of the number of overlays */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Center.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Center.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Center.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include "Ximage.h"
+
+int Center (Graphic *graphic, Layout *layout) {
+
+  char buffer[1024];
+  int status, zoom, Nbytes;
+  double X, Y;
+
+  fcntl (layout[0].Ximage, F_SETFL, !O_NONBLOCK);  
+  status = read (layout[0].Ximage, buffer, 16); 
+  buffer[16] = 0; 
+  sscanf (buffer, "%*s %d", &Nbytes); 
+  status = read (layout[0].Ximage, buffer, Nbytes); 
+  buffer[status] = 0; /* make the string easy to parse */ 
+  sscanf (buffer, "%lf %lf %d", &X,  &Y, &zoom);
+
+  layout[0].X = 0.5*layout[0].matrix.Naxis[0] - X;
+  layout[0].Y = 0.5*layout[0].matrix.Naxis[1] - Y;
+  if ((zoom != 0) && (zoom != -1)) {
+    layout[0].expand = zoom;
+  }
+
+  if (USE_XWINDOW) {
+    Remap (graphic, layout, &layout[0].matrix);
+    Refresh (graphic, layout, 0);
+    XFlush (graphic[0].display);
+  }
+
+  fcntl (layout[0].Ximage, F_SETFL, O_NONBLOCK);  
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/CreatePicture.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/CreatePicture.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/CreatePicture.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include "Ximage.h"
+
+void CreatePicture (Layout *layout, Graphic *graphic) {
+
+  int i, j, extra;
+  unsigned char *c;
+  unsigned int *l;
+  unsigned int start, start1, start2, start3;
+
+  start = layout[0].white;
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    REALLOCATE (layout[0].picture.data, char, layout[0].picture.dx*layout[0].picture.dy);
+    c = (unsigned char *) layout[0].picture.data;
+    for (i = 0; i < (layout[0].picture.dx*layout[0].picture.dy); i++, c++)
+      *c = start;
+    layout[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  layout[0].picture.data, layout[0].picture.dx, layout[0].picture.dy, 8, 0);
+    break;
+
+  case 16:
+    REALLOCATE (layout[0].picture.data, char, 2*layout[0].picture.dy*layout[0].picture.dx);
+    c = (unsigned char *) layout[0].picture.data;
+    start1 = 0x0000ff & (start);
+    start2 = 0x0000ff & (start >> 8);
+    for (i = 0; i < layout[0].picture.dy; i++) {
+      for (j = 0; j < layout[0].picture.dx; j++, c+=2) {
+	c[0] = start1;
+	c[1] = start2;
+      }
+    }
+    layout[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  layout[0].picture.data, layout[0].picture.dx, layout[0].picture.dy, 16, 0);
+    break;
+
+  case 24:
+    extra = 4 - (layout[0].picture.dx * 3) % 4;
+    REALLOCATE (layout[0].picture.data, char, layout[0].picture.dy*(3*layout[0].picture.dx+extra));
+    c = (unsigned char *) layout[0].picture.data;
+    start1 = 0x0000ff & (start);
+    start2 = 0x0000ff & (start >> 8);
+    start3 = 0x0000ff & (start >> 16);
+    for (i = 0; i < layout[0].picture.dy; i++) {
+      for (j = 0; j < layout[0].picture.dx; j++, c+=3) {
+	c[0] = start1;
+	c[1] = start2;
+	c[2] = start3;
+      }
+      c+=extra;
+    }
+    layout[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  layout[0].picture.data, layout[0].picture.dx, layout[0].picture.dy, 24, 0);
+    break;
+
+  case 32:
+    ALLOCATE (layout[0].picture.data, char, (4*layout[0].picture.dx*layout[0].picture.dy + 32));
+    memset (layout[0].picture.data, 0xbd, 4*layout[0].picture.dx*layout[0].picture.dy + 30);
+    l = (unsigned int *) layout[0].picture.data;
+    for (i = 0; i < (layout[0].picture.dx*layout[0].picture.dy); i++, l++)
+      *l = start;
+    layout[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  layout[0].picture.data, layout[0].picture.dx, layout[0].picture.dy, 32, 0);
+    break;
+  }
+    
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/CursorOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/CursorOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/CursorOps.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "Ximage.h"
+
+void Screen_to_Image (double *x1, double *y1, double x2, double y2, Layout *layout) {
+
+  double expand;
+
+  expand = 1.0;
+  if (layout[0].expand > 0) {
+    expand = 1 / (1.0*layout[0].expand);
+  }
+  if (layout[0].expand < 0) {
+    expand = fabs((double)layout[0].expand);
+  }
+  
+  *x1 = expand*(x2 - layout[0].picture.x - 0.5*layout[0].picture.dx) + 0.5*layout[0].matrix.Naxis[0] - layout[0].X;
+  *y1 = expand*(y2 - layout[0].picture.y - 0.5*layout[0].picture.dy) + 0.5*layout[0].matrix.Naxis[1] - layout[0].Y;
+  
+}
+
+void Image_to_Screen (double *x1, double *y1, double x2, double y2, Layout *layout) {
+
+  double expand;
+
+  /* notice that here, expand is the reciprocal of the expand above */
+  expand = 1.0;
+  if (layout[0].expand > 0) {
+    expand = abs(layout[0].expand);
+  }
+  if (layout[0].expand < 0) {
+    expand = 1 / ((double)abs(layout[0].expand));
+  }
+  
+  *x1 = (x2 - 0.5*layout[0].matrix.Naxis[0] + layout[0].X) * expand + layout[0].picture.x + 0.5*layout[0].picture.dx;
+  *y1 = (y2 - 0.5*layout[0].matrix.Naxis[1] + layout[0].Y) * expand + layout[0].picture.y + 0.5*layout[0].picture.dy;
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/JPEGit24.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/JPEGit24.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/JPEGit24.c	(revision 22322)
@@ -0,0 +1,225 @@
+# include "Ximage.h"
+# include "jpeglib.h"
+
+# define WHITE_R 255
+# define WHITE_G 255
+# define WHITE_B 255
+
+int JPEGit24 (Graphic *graphic, Layout *layout) {
+
+  struct jpeg_compress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
+  JSAMPLE *image_buffer;	/* Points to data for current line */
+  JSAMPLE *line_buffer;	        /* Points to data for current line */
+
+  int ii, i, j;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  int status, Nbytes, quality;
+  int expand_in, expand_out;
+  double expand, Rx, Ry, X, Y;
+  unsigned char *out_pix, *in_pix, *in_pix_ref;
+  unsigned char pixel1[256], pixel2[256], pixel3[256];
+  char filename[1024];
+  FILE *f;
+
+  /* expect a line telling the number of bytes and a filename */
+  status = read (layout[0].Ximage, filename, 16);
+  filename[16] = 0;
+  sscanf (filename, "%*s %d", &Nbytes);
+  status = read (layout[0].Ximage, filename, Nbytes);
+  filename[status] = 0; /* make the string easy to parse */
+
+  /***** JPEG init calls */
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_compress (&cinfo);
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "Kii: failed to open %s for output\n", filename);
+    return (TRUE);
+  }
+  jpeg_stdio_dest(&cinfo, f);
+  
+  quality = 75;
+  cinfo.image_width = layout[0].picture.dx; 	/* image width and height, in pixels */
+  cinfo.image_height = layout[0].picture.dy;
+# ifdef GREYSCALE
+  cinfo.input_components = 1;		        /* # of color components per pixel */
+  cinfo.in_color_space = JCS_GRAYSCALE; 	/* colorspace of input image */
+# else 
+  cinfo.input_components = 3;		        
+  cinfo.in_color_space = JCS_RGB; 	
+# endif
+  jpeg_set_defaults (&cinfo);
+  jpeg_set_quality (&cinfo, quality, TRUE       /* limit to baseline-JPEG values */);
+  jpeg_start_compress (&cinfo, TRUE);
+
+  /** cmap[i].pixel must be defined even if X is not used **/
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel1[i] = layout[0].cmap[i].red >> 8;
+    pixel2[i] = layout[0].cmap[i].green >> 8;
+    pixel3[i] = layout[0].cmap[i].blue >> 8;
+  }
+
+  expand = expand_in = expand_out = 1.0;
+  if (layout[0].expand == 0) /* set up expansions */
+    layout[0].expand = 1;
+  if (layout[0].expand > 0) {
+    expand = 1 / (1.0*layout[0].expand);
+    expand_out = layout[0].expand;
+    expand_in  = 1;
+  }
+  if (layout[0].expand < 0) {
+    expand = fabs((double)layout[0].expand);
+    expand_out = 1;
+    expand_in  = -layout[0].expand;
+  }
+
+  dx = layout[0].picture.dx;
+  dy = layout[0].picture.dy;
+  DX = layout[0].matrix.Naxis[0];
+  DY = layout[0].matrix.Naxis[1];
+
+  /* X,Y are the image coordinates of the first image pixel */
+  X = MAX(0.5*(DX - dx*expand) - layout[0].X, 0);
+  if ((int)X != X) 
+    X = (int) X + 1;
+  else 
+    X = (int) X;
+  Y = MAX(0.5*(DY - dy*expand) - layout[0].Y, 0);
+  if ((int)Y != Y) 
+    Y = (int) Y + 1;
+  else 
+    Y = (int) Y;
+
+  /* Rx,Ry are the screen coordinates of the first image pixel */
+  Rx = (X + layout[0].X - 0.5*DX)/expand + 0.5*dx;
+  Ry = (Y + layout[0].Y - 0.5*DY)/expand + 0.5*dy;
+
+  i_start = MIN (MAX (Rx, 0), dx - expand_out + 1);
+  j_start = MIN (MAX (Ry, 0), dy - expand_out + 1);
+  
+  if (layout[0].expand > 0) {
+    i_end = MAX (MIN (i_start + ((int)(expand*(dx-i_start)))/expand, expand_out*(DX-X) + Rx), 0);
+    j_end = MAX (MIN (j_start + ((int)(expand*(dy-j_start)))/expand, expand_out*(DY-Y) + Ry), 0);
+  } else {
+    i_end = MAX (MIN (dx, (DX-X)/expand + Rx), 0);
+    j_end = MAX (MIN (dy, (DY-Y)/expand + Ry), 0);
+  }    
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  /* output line buffer */
+  ALLOCATE (image_buffer, JSAMPLE, 3*dx*dy);
+  ALLOCATE (line_buffer, JSAMPLE, 3*dx);
+
+  in_pix_ref  = (unsigned char *) (layout[0].matrix.buffer) + DX*(int)MAX(Y,0) + (int)MAX(X,0);
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  out_pix = line_buffer;
+  for (i = 0; i < dx; i++, out_pix+=3) {
+    out_pix[0] = WHITE_R;
+    out_pix[1] = WHITE_G;
+    out_pix[2] = WHITE_B;
+  }
+  for (j = 0; j < j_start; j++) {
+    memcpy (&image_buffer[j*3*dx], line_buffer, 3*dx);
+  }
+  
+  /*** fill in the image data region ***/
+  for (j = j_start; j < j_end; j+= expand_out, in_pix_ref += expand_in*DX) {
+    
+    /* create one output image line */
+    in_pix = in_pix_ref;
+    out_pix = line_buffer;
+
+    /**** fill in area to the left of the picture ****/
+    for (i = 0; i < i_start; i++, out_pix+=3) {
+      out_pix[0] = WHITE_R;
+      out_pix[1] = WHITE_G;
+      out_pix[2] = WHITE_B;
+    }
+    
+    /*** fill in the picture region ***/
+    for (i = i_start; i < i_end; i+=expand_out, in_pix+=expand_in) {
+      for (ii = 0; ii < expand_out; ii++, out_pix+=3) {
+	out_pix[0] = pixel1[*in_pix];
+	out_pix[1] = pixel2[*in_pix];
+	out_pix[2] = pixel3[*in_pix];
+      }
+    }
+    
+    /**** fill in area to the right of the picture ****/
+    for (i = i_end; i < dx; i++, out_pix+=3) {
+      out_pix[0] = WHITE_R;
+      out_pix[1] = WHITE_G;
+      out_pix[2] = WHITE_B;
+    }
+
+    /* write out the image line expand_out times */
+    for (i = 0; i < expand_out; i++) {
+      memcpy (&image_buffer[(j + i)*3*dx], line_buffer, 3*dx);
+    }
+  }
+
+  /**** fill in top area ****/
+  out_pix = line_buffer;
+  for (i = 0; i < dx; i++, out_pix+=3) { 
+    out_pix[0] = WHITE_R;
+    out_pix[1] = WHITE_G;
+    out_pix[2] = WHITE_B;
+  }
+  for (j = j_end; j < dy; j++) {
+    memcpy (&image_buffer[j*3*dx], line_buffer, 3*dx);
+  }
+
+
+  /* I need to write the overlay objects on the jpeg image.
+     if i can write / overwrite data in jpeg buffer, then do it here,
+     otherwise i need to create a temporary image buffer, then write the 
+     scanlines to that buffer */
+
+  {
+    int Npalette;
+    png_color *palette;
+    bDrawColor white, color;
+    bDrawBuffer *buffer;
+
+    palette = KapaPNGPalette (&Npalette);
+
+    buffer = bDrawBufferCreate (dx, dy);
+    bDrawSetBuffer (buffer);
+    for (i = 0; i < NOVERLAYS; i++) {
+      if (OVERLAY[i]) bDrawOverlay (layout, i);
+    }
+
+    white = KapaColorByName ("white");
+    for (j = 0; j < dy; j++) {
+      for (i = 0; i < dx; i++) {
+	color = buffer[0].pixels[j][i];
+	if (color == white) continue;
+	image_buffer[j*3*dx + 3*i + 0] = palette[color].red;
+	image_buffer[j*3*dx + 3*i + 1] = palette[color].green;
+	image_buffer[j*3*dx + 3*i + 2] = palette[color].blue;
+      }
+    }
+    bDrawBufferFree (buffer);
+  }
+
+  for (i = 0; i < dy; i++) {
+    row_pointer[0] = &image_buffer[i*3*dx];
+    (void) jpeg_write_scanlines (&cinfo, row_pointer, 1);
+  }
+
+  jpeg_finish_compress (&cinfo);
+  fclose (f);
+  jpeg_destroy_compress (&cinfo);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/NewPicture.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/NewPicture.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/NewPicture.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "Ximage.h"
+
+int NewPicture (Graphic *graphic, Layout *layout) {
+
+  Header header;
+  char *buff;
+  int status, bytes_left, socket;
+ 
+  socket = layout[0].Ximage;
+
+  KiiSendMessage (socket, "%d", layout[0].Npixels);
+
+  fcntl (socket, F_SETFL, !O_NONBLOCK);  
+
+  header.Naxes = 2;
+
+  KiiScanMessage (socket, "%d %d %d %d %lf %lf", &header.Naxis[0], &header.Naxis[1]);
+  KiiScanMessage (socket, "%d %d %d %d %lf %lf", &header.bitpix, &header.unsign, &header.bzero, &header.bscale);
+  KiiScanMessage (socket, "%lf %lf",  &layout[0].zero, &layout[0].range);
+  KiiScanMessage (socket, "%lf %lf %d", &layout[0].min,  &layout[0].max, &header.size);
+  KiiScanMessage (socket, "%lf %f %f %f %f", &layout[0].coords.crval1, &layout[0].coords.crpix1, &layout[0].coords.cdelt1, &layout[0].coords.pc1_1, &layout[0].coords.pc1_2);
+  KiiScanMessage (socket, "%lf %f %f %f %f", &layout[0].coords.crval2, &layout[0].coords.crpix2, &layout[0].coords.cdelt2, &layout[0].coords.pc2_1, &layout[0].coords.pc2_2);
+	  
+  KiiScanMessage (socket, "%s %s %s", layout[0].coords.ctype, layout[0].file, layout[0].buffer_name);
+
+  gfits_free_matrix (&layout[0].matrix);
+  gfits_create_matrix (&header, &layout[0].matrix);
+
+  fcntl (socket, F_SETFL, O_NONBLOCK);  
+
+  status = 1;
+  buff = layout[0].matrix.buffer;
+  bytes_left = header.size;
+  layout[0].matrix.size = 0;
+  while (bytes_left > 0) {
+    status = read (socket, buff, bytes_left);
+    if (status == 0) {  /* No more pipe */
+      fprintf (stderr, "error: pipe closed\n");
+      return (FALSE);
+    }
+    if (status != -1) { /* pipe has data */
+      layout[0].matrix.size += status;
+      bytes_left -= status;
+      buff = (char *)(buff + status);
+    }
+  }
+
+  if (DEBUG) fprintf (stderr, "read %d bytes\n", layout[0].matrix.size);
+  /* it it not obvious this condition should kill kii, but ... */
+  if (layout[0].matrix.size != header.size) {  
+    fprintf (stderr, "error: expected %d bytes, but got only %d\n", header.size, layout[0].matrix.size);
+    return (FALSE);
+  }
+
+  if (!USE_XWINDOW) return (TRUE);
+
+  Remap (graphic, layout, &layout[0].matrix);
+  if (DEBUG) fprintf (stderr, "remapped image\n");
+  Refresh (graphic, layout, 0);
+  if (DEBUG) fprintf (stderr, "refreshed\n");
+  XFlush (graphic[0].display);
+
+  return (TRUE);
+
+}
+
+/*
+   layout[0].X = 0;
+   layout[0].Y = 0;
+   layout[0].expand = 1;
+*/
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/PositionPictures.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/PositionPictures.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/PositionPictures.c	(revision 22322)
@@ -0,0 +1,152 @@
+# include "Ximage.h" 
+# include "buttons.h"
+# define BUTTON_WIDTH 28
+# define BUTTON_HEIGHT 28
+
+void PositionPictures (Layout *layout, Graphic *graphic) {
+
+  layout[0].cmapbar.dx = graphic[0].dx - 2*PAD1; 
+  layout[0].cmapbar.dy = COLORPAD;
+  layout[0].cmapbar.x = PAD1;
+  layout[0].cmapbar.y = PAD1;
+
+  layout[0].zoom.dx = ZOOM_X; 
+  layout[0].zoom.dy = ZOOM_Y;
+  layout[0].zoom.x = graphic[0].dx - PAD1 - ZOOM_X;
+  layout[0].zoom.y = layout[0].cmapbar.y + layout[0].cmapbar.dy + PAD2;
+
+  layout[0].picture.dx = layout[0].zoom.x - 2*PAD1 - 25; 
+  layout[0].picture.dy = graphic[0].dy - 2*PAD1 - PAD2 - COLORPAD - 25;
+  layout[0].picture.x = PAD1 + 25;
+  layout[0].picture.y = PAD1 + PAD2 + COLORPAD;
+
+  /** everything below is tied in x-dir to the zoom box **/
+  layout[0].text_x = layout[0].zoom.x;
+  layout[0].text_y = layout[0].zoom.y + layout[0].zoom.dy + PAD2;
+
+  /*  layout[0].PS_button.y = graphic[0].dy - BUTTON_HEIGHT - PAD1; */
+  layout[0].PS_button.x = layout[0].zoom.x + 5;
+  layout[0].PS_button.y = 2*ZOOM_Y;
+  layout[0].PS_button.dx = BUTTON_WIDTH;
+  layout[0].PS_button.dy = BUTTON_HEIGHT;
+  layout[0].PS_button.text = FALSE;
+  layout[0].PS_button.width = PS_width;
+  layout[0].PS_button.height = PS_height;
+  layout[0].PS_button.bitmap = PS_bits;
+  layout[0].PS_button.function_1 = PSit;
+  layout[0].PS_button.function_2 = PSit;
+  layout[0].PS_button.function_3 = PSit;
+
+  /** everything below is tied to the PS_button in y-dir **/
+  layout[0].grey_button.x = layout[0].PS_button.x + layout[0].PS_button.dx + PAD1;
+  layout[0].grey_button.y = layout[0].PS_button.y;
+  layout[0].grey_button.dx = BUTTON_WIDTH;
+  layout[0].grey_button.dy = BUTTON_HEIGHT;
+  layout[0].grey_button.text = FALSE;
+  layout[0].grey_button.width = grey_width;
+  layout[0].grey_button.height = grey_height;
+  layout[0].grey_button.bitmap = grey_bits;
+  layout[0].grey_button.function_1 = greycolors;
+  layout[0].grey_button.function_2 = greycolors;
+  layout[0].grey_button.function_3 = greycolors;
+
+  layout[0].rainbow_button.x = layout[0].grey_button.x + layout[0].grey_button.dx + PAD1;
+  layout[0].rainbow_button.y = layout[0].PS_button.y;
+  layout[0].rainbow_button.dx = BUTTON_WIDTH;
+  layout[0].rainbow_button.dy = BUTTON_HEIGHT;
+  layout[0].rainbow_button.text = FALSE;
+  layout[0].rainbow_button.width = rainbow_width;
+  layout[0].rainbow_button.height = rainbow_height;
+  layout[0].rainbow_button.bitmap = rainbow_bits;
+  layout[0].rainbow_button.function_1 = rainbow;
+  layout[0].rainbow_button.function_2 = rainbow;
+  layout[0].rainbow_button.function_3 = rainbow;
+
+  layout[0].puns_button.x = layout[0].rainbow_button.x + layout[0].rainbow_button.dx + PAD1;
+  layout[0].puns_button.y = layout[0].PS_button.y;
+  layout[0].puns_button.dx = BUTTON_WIDTH;
+  layout[0].puns_button.dy = BUTTON_HEIGHT;
+  layout[0].puns_button.text = FALSE;
+  layout[0].puns_button.width = puns_width;
+  layout[0].puns_button.height = puns_height;
+  layout[0].puns_button.bitmap = puns_bits;
+  layout[0].puns_button.function_1 = puns;
+  layout[0].puns_button.function_2 = puns;
+  layout[0].puns_button.function_3 = puns;
+
+  layout[0].recenter_button.x = layout[0].puns_button.x + layout[0].puns_button.dx + PAD1;
+  layout[0].recenter_button.y = layout[0].PS_button.y;
+  layout[0].recenter_button.dx = BUTTON_WIDTH;
+  layout[0].recenter_button.dy = BUTTON_HEIGHT;
+  layout[0].recenter_button.text = FALSE;
+  layout[0].recenter_button.width = recenter_width;
+  layout[0].recenter_button.height = recenter_height;
+  layout[0].recenter_button.bitmap = recenter_bits;
+  layout[0].recenter_button.function_1 = Recenter;
+  layout[0].recenter_button.function_2 = RecenterRescale;
+  layout[0].recenter_button.function_3 = Rescale;
+
+  layout[0].overlay_button[0].x = layout[0].zoom.x + 5;
+  layout[0].overlay_button[0].y = layout[0].PS_button.y - BUTTON_HEIGHT - PAD1;
+  layout[0].overlay_button[0].dx = BUTTON_WIDTH;
+  layout[0].overlay_button[0].dy = BUTTON_HEIGHT;
+  layout[0].overlay_button[0].text = FALSE;
+  layout[0].overlay_button[0].width = red_width;
+  layout[0].overlay_button[0].height = red_height;
+  layout[0].overlay_button[0].bitmap = red_bits;
+  layout[0].overlay_button[0].function_1 = Overlay0;
+  layout[0].overlay_button[0].function_2 = Overlay0;
+  layout[0].overlay_button[0].function_3 = Overlay0;
+		   	   
+  layout[0].overlay_button[1].x = layout[0].overlay_button[0].x + layout[0].overlay_button[0].dx + PAD1;
+  layout[0].overlay_button[1].y = layout[0].PS_button.y - BUTTON_HEIGHT - PAD1;
+  layout[0].overlay_button[1].dx = BUTTON_WIDTH;
+  layout[0].overlay_button[1].dy = BUTTON_HEIGHT;
+  layout[0].overlay_button[1].text = FALSE;
+  layout[0].overlay_button[1].width = green_width;
+  layout[0].overlay_button[1].height = green_height;
+  layout[0].overlay_button[1].bitmap = green_bits;
+  layout[0].overlay_button[1].function_1 = Overlay1;
+  layout[0].overlay_button[1].function_2 = Overlay1;
+  layout[0].overlay_button[1].function_3 = Overlay1;
+
+  layout[0].overlay_button[2].x = layout[0].overlay_button[1].x + layout[0].overlay_button[1].dx + PAD1;
+  layout[0].overlay_button[2].y = layout[0].PS_button.y - BUTTON_HEIGHT - PAD1;
+  layout[0].overlay_button[2].dx = BUTTON_WIDTH;
+  layout[0].overlay_button[2].dy = BUTTON_HEIGHT;
+  layout[0].overlay_button[2].text = FALSE;
+  layout[0].overlay_button[2].width = blue_width;
+  layout[0].overlay_button[2].height = blue_height;
+  layout[0].overlay_button[2].bitmap = blue_bits;
+  layout[0].overlay_button[2].function_1 = Overlay2;
+  layout[0].overlay_button[2].function_2 = Overlay2;
+  layout[0].overlay_button[2].function_3 = Overlay2;
+		   	   
+  layout[0].overlay_button[3].x = layout[0].overlay_button[2].x + layout[0].overlay_button[2].dx + PAD1;
+  layout[0].overlay_button[3].y = layout[0].PS_button.y - BUTTON_HEIGHT - PAD1;
+  layout[0].overlay_button[3].dx = BUTTON_WIDTH;
+  layout[0].overlay_button[3].dy = BUTTON_HEIGHT;
+  layout[0].overlay_button[3].text = FALSE;
+  layout[0].overlay_button[3].width = yellow_width;
+  layout[0].overlay_button[3].height = yellow_height;
+  layout[0].overlay_button[3].bitmap = yellow_bits;
+  layout[0].overlay_button[3].function_1 = Overlay3;
+  layout[0].overlay_button[3].function_2 = Overlay3;
+  layout[0].overlay_button[3].function_3 = Overlay3;
+
+  layout[0].hms_button.x = layout[0].overlay_button[3].x + layout[0].overlay_button[3].dx + PAD1;
+  layout[0].hms_button.y = layout[0].PS_button.y - BUTTON_HEIGHT - PAD1;
+  layout[0].hms_button.dx = BUTTON_WIDTH;
+  layout[0].hms_button.dy = BUTTON_HEIGHT;
+  layout[0].hms_button.text = FALSE;
+  layout[0].hms_button.width = hms_width;
+  layout[0].hms_button.height = hms_height;
+  layout[0].hms_button.bitmap = hms_bits;
+  layout[0].hms_button.function_1 = ToggleDEG;
+  layout[0].hms_button.function_2 = ToggleDEG;
+  layout[0].hms_button.function_3 = ToggleDEG;
+
+}
+
+
+/* this routine is dependent on the number of Overlays */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap.c	(revision 22322)
@@ -0,0 +1,21 @@
+# include "Ximage.h"
+
+void Remap (Graphic *graphic, Layout *layout, Matrix *matrix) {
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    Remap8 (graphic, layout, matrix);
+    break;
+  case 16:
+    Remap16 (graphic, layout, matrix);
+    break;
+  case 24:
+    Remap24 (graphic, layout, matrix);
+    break;
+  case 32:
+    Remap32 (graphic, layout, matrix);
+    break;
+  }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap16.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap16.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap16.c	(revision 22322)
@@ -0,0 +1,153 @@
+# include "Ximage.h"
+# define FRAC(a) ((a) - (int)(a))
+
+void Remap16 (Graphic *graphic, Layout *layout, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  double expand, Rx, Ry, X, Y;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2, *data;
+  unsigned char *in_pix,  *in_pix2;
+  unsigned char pixel1[256], pixel2[256], pixel3[256];
+  unsigned char pixvalue1, pixvalue2;
+  unsigned long white;
+  unsigned char white1, white2;
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel1[i] = 0x0000ff & layout[0].cmap[i].pixel;
+    pixel2[i] = 0x0000ff & (layout[0].cmap[i].pixel >> 8);
+    pixel3[i] = 0x0000ff & (layout[0].cmap[i].pixel >> 16);
+  }
+  white = layout[0].white;
+  white1 = 0x00ff & white;
+  white2 = 0x00ff & (white >> 8);
+
+  expand = expand_in = expand_out = 1.0;
+  if (layout[0].expand == 0) /* set up expansions */
+    layout[0].expand = 1;
+  if (layout[0].expand > 0) {
+    expand = 1 / (1.0*layout[0].expand);
+    expand_out = layout[0].expand;
+    expand_in  = 1;
+  }
+  if (layout[0].expand < 0) {
+    expand = fabs((double)layout[0].expand);
+    expand_out = 1;
+    expand_in  = -layout[0].expand;
+  }
+
+  dx = layout[0].picture.dx;
+  dy = layout[0].picture.dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  /* X,Y are the image coordinates of the first image pixel */
+  X = MAX(0.5*(DX - dx*expand) - layout[0].X, 0);
+  if ((int)X != X) 
+    X = (int) X + 1;
+  else 
+    X = (int) X;
+  Y = MAX(0.5*(DY - dy*expand) - layout[0].Y, 0);
+  if ((int)Y != Y) 
+    Y = (int) Y + 1;
+  else 
+    Y = (int) Y;
+
+  /* Rx,Ry are the screen coordinates of the first image pixel */
+  Rx = (X + layout[0].X - 0.5*DX)/expand + 0.5*dx;
+  Ry = (Y + layout[0].Y - 0.5*DY)/expand + 0.5*dy;
+
+  i_start = MIN (MAX (Rx, 0), dx - expand_out + 1);
+  j_start = MIN (MAX (Ry, 0), dy - expand_out + 1);
+  
+  if (layout[0].expand > 0) {
+    i_end = MAX (MIN (i_start + ((int)(expand*(dx-i_start)))/expand, expand_out*(DX-X) + Rx), 0);
+    j_end = MAX (MIN (j_start + ((int)(expand*(dy-j_start)))/expand, expand_out*(DY-Y) + Ry), 0);
+  } else {
+    i_end = MAX (MIN (dx, (DX-X)/expand + Rx), 0);
+    j_end = MAX (MIN (dy, (DY-Y)/expand + Ry), 0);
+  }    
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  data = out_pix = (unsigned char *) layout[0].picture.data;
+  in_pix  = (unsigned char *) (matrix[0].buffer) + DX*(int)MAX(Y,0) + (int)MAX(X,0);
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++, out_pix+=2) {
+      out_pix[0] = white1;
+      out_pix[1] = white2;
+    }
+  }
+  
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[2*j*dx];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
+      out_pix2 = out_pix + 2*jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2+=2) {
+	out_pix2[0] = white1;
+	out_pix2[1] = white2;
+      }
+    }
+    out_pix += 2*i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=2) {
+	out_pix[0] = pixel1[*in_pix2];
+	out_pix[1] = pixel2[*in_pix2];
+      }
+    }
+    else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 2*expand_out) { 
+	pixvalue1 = pixel1[*in_pix2];
+	pixvalue2 = pixel2[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=2*(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2+=2) {
+	    out_pix2[0] = pixvalue1; 
+	    out_pix2[1] = pixvalue2; 
+	  }
+	}
+      }
+    }
+    out_pix -= 2*dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + 2*jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2+=2) {
+	out_pix2[0] = white1;
+	out_pix2[1] = white2;
+      }
+    }
+  } 
+  
+  /*
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  */
+  
+  out_pix = &data[2*j_end*dx];
+  /**** fill in top area ****/
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix+=2) { 
+      out_pix[0] = white1;
+      out_pix[1] = white2;
+    }
+  }
+
+  layout[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					layout[0].picture.data, layout[0].picture.dx, layout[0].picture.dy, 16, 0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap24.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap24.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap24.c	(revision 22322)
@@ -0,0 +1,164 @@
+# include "Ximage.h"
+# define FRAC(a) ((a) - (int)(a))
+
+void Remap24 (Graphic *graphic, Layout *layout, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback, extra;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  double expand, Rx, Ry, X, Y;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2, *data;
+  unsigned char *in_pix,  *in_pix2;
+  unsigned char pixel1[256], pixel2[256], pixel3[256];
+  unsigned char pixvalue1, pixvalue2, pixvalue3;
+  unsigned long white;
+  unsigned char white1, white2, white3;
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel1[i] = 0x0000ff & layout[0].cmap[i].pixel;
+    pixel2[i] = 0x0000ff & (layout[0].cmap[i].pixel >> 8);
+    pixel3[i] = 0x0000ff & (layout[0].cmap[i].pixel >> 16);
+  }
+  white = layout[0].white;
+  white1 = 0x0000ff & white;
+  white2 = 0x0000ff & (white >> 8);
+  white3 = 0x0000ff & (white >> 16);
+
+  expand = expand_in = expand_out = 1.0;
+  if (layout[0].expand == 0) /* set up expansions */
+    layout[0].expand = 1;
+  if (layout[0].expand > 0) {
+    expand = 1 / (1.0*layout[0].expand);
+    expand_out = layout[0].expand;
+    expand_in  = 1;
+  }
+  if (layout[0].expand < 0) {
+    expand = fabs((double)layout[0].expand);
+    expand_out = 1;
+    expand_in  = -layout[0].expand;
+  }
+
+  dx = layout[0].picture.dx;
+  dy = layout[0].picture.dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+  extra = 4 - (dx * 3) % 4;
+
+  /* X,Y are the image coordinates of the first image pixel */
+  X = MAX(0.5*(DX - dx*expand) - layout[0].X, 0);
+  if ((int)X != X) 
+    X = (int) X + 1;
+  else 
+    X = (int) X;
+  Y = MAX(0.5*(DY - dy*expand) - layout[0].Y, 0);
+  if ((int)Y != Y) 
+    Y = (int) Y + 1;
+  else 
+    Y = (int) Y;
+
+  /* Rx,Ry are the screen coordinates of the first image pixel */
+  Rx = (X + layout[0].X - 0.5*DX)/expand + 0.5*dx;
+  Ry = (Y + layout[0].Y - 0.5*DY)/expand + 0.5*dy;
+
+  i_start = MIN (MAX (Rx, 0), dx - expand_out + 1);
+  j_start = MIN (MAX (Ry, 0), dy - expand_out + 1);
+  
+  if (layout[0].expand > 0) {
+    i_end = MAX (MIN (i_start + ((int)(expand*(dx-i_start)))/expand, expand_out*(DX-X) + Rx), 0);
+    j_end = MAX (MIN (j_start + ((int)(expand*(dy-j_start)))/expand, expand_out*(DY-Y) + Ry), 0);
+  } else {
+    i_end = MAX (MIN (dx, (DX-X)/expand + Rx), 0);
+    j_end = MAX (MIN (dy, (DY-Y)/expand + Ry), 0);
+  }    
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  data = out_pix = (unsigned char *) layout[0].picture.data;
+  in_pix  = (unsigned char *) (matrix[0].buffer) + DX*(int)MAX(Y,0) + (int)MAX(X,0);
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) {
+      out_pix[0] = white1;
+      out_pix[1] = white2;
+      out_pix[2] = white3;
+    }
+    out_pix += extra;
+  }
+  
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[j*(3*dx+extra)];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
+      out_pix2 = out_pix + jj*(3*dx + extra);
+      for (i = 0; i < i_start; i++, out_pix2+=3) {
+	out_pix2[0] = white1;
+	out_pix2[1] = white2;
+	out_pix2[2] = white3;
+      }
+    }
+    out_pix += 3*i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=3) {
+	out_pix[0] = pixel1[*in_pix2];
+	out_pix[1] = pixel2[*in_pix2];
+	out_pix[2] = pixel3[*in_pix2];
+      }
+    }
+    else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 3*expand_out) { 
+	pixvalue1 = pixel1[*in_pix2];
+	pixvalue2 = pixel2[*in_pix2];
+	pixvalue3 = pixel3[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=3*(dx-expand_out)+extra) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2+=3) {
+	    out_pix2[0] = pixvalue1; 
+	    out_pix2[1] = pixvalue2; 
+	    out_pix2[2] = pixvalue3; 
+	  }
+	}
+      }
+    }
+    out_pix -= 3*dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*(3*dx+extra);
+      for (i = i_end; i < dx; i++, out_pix2+=3) {
+	out_pix2[0] = white1;
+	out_pix2[1] = white2;
+	out_pix2[2] = white3;
+      }
+    }
+  } 
+  
+  /*
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  */
+  
+  out_pix = &data[j_end*(3*dx+extra)];
+  /**** fill in top area ****/
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) { 
+      out_pix[0] = white1;
+      out_pix[1] = white2;
+      out_pix[2] = white3;
+    }
+    out_pix+=extra;
+  }
+
+  layout[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					layout[0].picture.data, layout[0].picture.dx, layout[0].picture.dy, 32, 0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap32.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap32.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap32.c	(revision 22322)
@@ -0,0 +1,132 @@
+# include "Ximage.h"
+# define FRAC(a) ((a) - (int)(a))
+
+void Remap32 (Graphic *graphic, Layout *layout, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  double expand, Rx, Ry, X, Y;
+  int expand_in, expand_out;
+  unsigned int *out_pix, *out_pix2;
+  unsigned char *in_pix,  *in_pix2;
+  unsigned long pixel[256], pixvalue;
+  unsigned long white;
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel[i] = layout[0].cmap[i].pixel;
+  }
+  white = layout[0].white;
+
+  expand = expand_in = expand_out = 1.0;
+  if (layout[0].expand == 0) /* set up expansions */
+    layout[0].expand = 1;
+  if (layout[0].expand > 0) {
+    expand = 1 / (1.0*layout[0].expand);
+    expand_out = layout[0].expand;
+    expand_in  = 1;
+  }
+  if (layout[0].expand < 0) {
+    expand = fabs((double)layout[0].expand);
+    expand_out = 1;
+    expand_in  = -layout[0].expand;
+  }
+
+  dx = layout[0].picture.dx;
+  dy = layout[0].picture.dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+  /* X,Y are the image coordinates of the first image pixel */
+  X = MAX(0.5*(DX - dx*expand) - layout[0].X, 0);
+  if ((int)X != X) 
+    X = (int) X + 1;
+  else 
+    X = (int) X;
+  Y = MAX(0.5*(DY - dy*expand) - layout[0].Y, 0);
+  if ((int)Y != Y) 
+    Y = (int) Y + 1;
+  else 
+    Y = (int) Y;
+
+  /* Rx,Ry are the screen coordinates of the first image pixel */
+  Rx = (X + layout[0].X - 0.5*DX)/expand + 0.5*dx;
+  Ry = (Y + layout[0].Y - 0.5*DY)/expand + 0.5*dy;
+
+  i_start = MIN (MAX (Rx, 0), dx - expand_out + 1);
+  j_start = MIN (MAX (Ry, 0), dy - expand_out + 1);
+  
+  if (layout[0].expand > 0) {
+    i_end = MAX (MIN (i_start + ((int)(expand*(dx-i_start)))/expand, expand_out*(DX-X) + Rx), 0);
+    j_end = MAX (MIN (j_start + ((int)(expand*(dy-j_start)))/expand, expand_out*(DY-Y) + Ry), 0);
+  } else {
+    i_end = MAX (MIN (dx, (DX-X)/expand + Rx), 0);
+    j_end = MAX (MIN (dy, (DY-Y)/expand + Ry), 0);
+  }    
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned int *) layout[0].picture.data;
+  in_pix  = (unsigned char *) (matrix[0].buffer) + DX*(int)MAX(Y,0) + (int)MAX(X,0);
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) 
+    *out_pix = white;
+  
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = white;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	*out_pix = pixel[*in_pix2];
+      }
+      /* *out_pix = *(pixel + *in_pix2);  */
+    }
+    else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixvalue = pixel[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue;
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = white;
+      }
+    }
+    out_pix += (dx - i_end);
+    
+  } 
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned int *)layout[0].picture.data < dx*dy); j++, out_pix++) { 
+    *out_pix = white;
+  }
+  layout[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					layout[0].picture.data, layout[0].picture.dx, layout[0].picture.dy, 32, 0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap8.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap8.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Remap8.c	(revision 22322)
@@ -0,0 +1,132 @@
+# include "Ximage.h"
+# define FRAC(a) ((a) - (int)(a))
+
+void Remap8 (Graphic *graphic, Layout *layout, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  double expand, Rx, Ry, X, Y;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2;
+  unsigned char *in_pix,  *in_pix2;
+  unsigned long pixel[256], pixvalue;
+  unsigned long white;
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel[i] = layout[0].cmap[i].pixel;
+  }
+  white = layout[0].white;
+
+  expand = expand_in = expand_out = 1.0;
+  if (layout[0].expand == 0) /* set up expansions */
+    layout[0].expand = 1;
+  if (layout[0].expand > 0) {
+    expand = 1 / (1.0*layout[0].expand);
+    expand_out = layout[0].expand;
+    expand_in  = 1;
+  }
+  if (layout[0].expand < 0) {
+    expand = fabs((double)layout[0].expand);
+    expand_out = 1;
+    expand_in  = -layout[0].expand;
+  }
+
+  dx = layout[0].picture.dx;
+  dy = layout[0].picture.dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+  /* X,Y are the image coordinates of the first image pixel */
+  X = MAX(0.5*(DX - dx*expand) - layout[0].X, 0);
+  if ((int)X != X) 
+    X = (int) X + 1;
+  else 
+    X = (int) X;
+  Y = MAX(0.5*(DY - dy*expand) - layout[0].Y, 0);
+  if ((int)Y != Y) 
+    Y = (int) Y + 1;
+  else 
+    Y = (int) Y;
+
+  /* Rx,Ry are the screen coordinates of the first image pixel */
+  Rx = (X + layout[0].X - 0.5*DX)/expand + 0.5*dx;
+  Ry = (Y + layout[0].Y - 0.5*DY)/expand + 0.5*dy;
+
+  i_start = MIN (MAX (Rx, 0), dx - expand_out + 1);
+  j_start = MIN (MAX (Ry, 0), dy - expand_out + 1);
+  
+  if (layout[0].expand > 0) {
+    i_end = MAX (MIN (i_start + ((int)(expand*(dx-i_start)))/expand, expand_out*(DX-X) + Rx), 0);
+    j_end = MAX (MIN (j_start + ((int)(expand*(dy-j_start)))/expand, expand_out*(DY-Y) + Ry), 0);
+  } else {
+    i_end = MAX (MIN (dx, (DX-X)/expand + Rx), 0);
+    j_end = MAX (MIN (dy, (DY-Y)/expand + Ry), 0);
+  }    
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned char *) layout[0].picture.data;
+  in_pix  = (unsigned char *) matrix[0].buffer + DX*(int)MAX(Y,0) + (int)MAX(X,0);
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) 
+    *out_pix = white;
+  
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = white;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	*out_pix = pixel[*in_pix2];
+      }
+      /* *out_pix = *(pixel + *in_pix2);  */
+    }
+    else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixvalue = pixel[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue;
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = white;
+      }
+    }
+    out_pix += (dx - i_end);
+    
+  } 
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned char *)layout[0].picture.data < dx*dy); j++, out_pix++) { 
+    *out_pix = white;
+  }
+  layout[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					layout[0].picture.data, layout[0].picture.dx, layout[0].picture.dy, 8, 0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Reorient.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Reorient.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Reorient.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "Ximage.h"
+
+void Reorient (Graphic *graphic, Layout *layout, double X, double Y, int mode) {
+
+  if (layout[0].expand == 0)
+    layout[0].expand = 1;
+
+  switch (mode) {
+  case 0:
+    if ((layout[0].X != X) || (layout[0].Y != Y)) {
+      layout[0].X = X;
+      layout[0].Y = Y;
+    }
+    break;
+  case -1: 
+    layout[0].expand--;
+    if ((layout[0].expand == 0) || (layout[0].expand == -1))
+      layout[0].expand = -2;
+    if (layout[0].expand < -30) {
+      fprintf (stderr, "can't get smaller!\n");
+      layout[0].expand = -30;
+      return;
+    }
+    layout[0].X = X;
+    layout[0].Y = Y;
+    break;
+  case +1:
+    layout[0].expand++;
+    if ((layout[0].expand == 0) || (layout[0].expand == -1))
+      layout[0].expand = 1;
+    if (layout[0].expand > 30) {
+      fprintf (stderr, "can't get bigger!\n");
+      layout[0].expand = 30;
+      return;
+    }
+    layout[0].X = X;
+    layout[0].Y = Y;
+    break;
+  }
+
+  Remap (graphic, layout, &layout[0].matrix);
+  Refresh (graphic, layout, 0);
+ 
+  XFlush (graphic[0].display);
+}
+
+# if (0) /* the "warp" function seems to be a problem on SUNs for some reason */
+  XQueryPointer (graphic[0].display, graphic[0].window, &dummy1, &dummy1, &dummy2, &dummy2, &x, &y, &dummy3); 
+  if ((x > layout[0].picture.x) && (x < layout[0].picture.x + layout[0].picture.dx) &&
+      (y > layout[0].picture.y) && (y < layout[0].picture.y + layout[0].picture.dy) &&
+      ((mouse_event[0].button == 2) || (mouse_event[0].button == 3))) {
+    XWarpPointer (graphic[0].display, None, graphic[0].window, 0, 0, 0, 0, 
+		  layout[0].picture.x + 0.5*layout[0].picture.dx,
+		  layout[0].picture.y + 0.5*layout[0].picture.dy);
+  }
+# endif
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Rescale.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Rescale.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Rescale.c	(revision 22322)
@@ -0,0 +1,16 @@
+# include "Ximage.h"
+
+void
+Rescale (graphic, layout)
+Graphic graphic[];
+Layout  layout[];
+{
+
+  fprintf (stdout, "enter new zero, range: ");
+  fscanf (stdin, "%lf%lf", &layout[0].zero, &layout[0].range);
+
+  Remap (graphic, layout, &layout[0].matrix);
+  Refresh (graphic, layout, 0);
+  
+  XFlush (graphic[0].display);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Resize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Resize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/Resize.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include "Ximage.h"
+
+int Resize (Graphic *graphic, Layout *layout) {
+ 
+  unsigned int NX, NY;
+
+  KiiScanMessage (layout[0].Ximage, "%d %d", &NX, &NY);
+
+  NX += ZOOM_X + 3*PAD1 + 25;
+  NY += 2*PAD1 + PAD2 + COLORPAD + 25;
+
+  if ((graphic[0].dx == NX) && (graphic[0].dy == NY)) 
+    return (TRUE);
+
+  graphic[0].dx = MAX(NX, 50); 
+  graphic[0].dy = MAX(NY, 50); 
+
+  if (USE_XWINDOW) XResizeWindow (graphic[0].display, graphic[0].window, NX, NY);
+
+  PositionPictures (layout, graphic);
+
+  if (USE_XWINDOW) {
+    CreateColorbar (layout, graphic);
+    CreatePicture (layout, graphic);
+    Remap (graphic, layout, &layout[0].matrix); 
+    XClearWindow (graphic[0].display, graphic[0].window);
+    Refresh (graphic, layout, 1);
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/StatusBox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/StatusBox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/StatusBox.c	(revision 22322)
@@ -0,0 +1,27 @@
+# include "Ximage.h"
+
+void
+StatusBox (graphic, layout)
+Graphic        graphic[];
+Layout         layout[];
+{
+
+  double  x, y, z;
+
+  if (MOVE_POINTER) {
+    x = 0.5*layout[0].matrix.Naxis[0];
+    y = 0.5*layout[0].matrix.Naxis[1];
+    z = -1;
+    layout[0].x = x;
+    layout[0].y = y;
+    layout[0].z = z;
+  }
+  else {
+    x = layout[0].x;
+    y = layout[0].y;
+    z = layout[0].z;
+  }
+  
+  UpdateStatusBox (graphic, layout, x, y, z, 1);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/TestPicture.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/TestPicture.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/TestPicture.c	(revision 22322)
@@ -0,0 +1,59 @@
+# include "Ximage.h"
+
+int 
+TestPicture (graphic, layout)
+     Graphic graphic[];
+     Layout  layout[];
+{
+
+  Header header;
+  char buffer[1024], *buff;
+  int status, bytes_left, Nbytes, i, j;
+  double frac;
+
+  gfits_free_matrix (&layout[0].matrix);
+
+  header.Naxes = 2;
+  header.Naxis[0] = 512;
+  header.Naxis[1] = 512;
+  header.bitpix = 8;
+  header.unsign = 0;
+  header.bzero = 0;
+  header.bscale = 1;
+
+  layout[0].zero = 0;
+  layout[0].range = layout[0].Npixels;
+  layout[0].min = 0; 
+  layout[0].max = layout[0].Npixels;
+  
+  gfits_create_matrix (&header, &layout[0].matrix);
+
+  frac = layout[0].Npixels / (512.0*512.0);
+  buff = layout[0].matrix.buffer;
+  for (i = 0; i < 512; i++) {
+    for (j = 0; j < 512; j++, buff++) {
+      *buff = (int)(i*j*frac);
+    }
+  }
+
+/*
+  Remap (layout, &layout[0].matrix);
+  layout[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					layout[0].picture.data, 
+					layout[0].picture.dx, layout[0].picture.dy, 8, 0);
+
+  (DEBUG) && fprintf (stderr, "remapped image\n", layout[0].matrix.size);
+  Refresh (graphic, layout);
+  
+  (DEBUG) && fprintf (stderr, "refreshed\n", layout[0].matrix.size);
+  XFlush (graphic[0].display);
+*/
+}
+
+
+/*
+   layout[0].X = 0;
+   layout[0].Y = 0;
+   layout[0].expand = 1;
+*/
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/cursor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/cursor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/picture/cursor.c	(revision 22322)
@@ -0,0 +1,82 @@
+# include "Ximage.h"
+
+# define IgnoreMask (long) (~(StructureNotifyMask | SubstructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | PointerMotionMask))
+
+int LastEvent (Display *display, int type, XEvent *event);
+
+int cursor (Graphic *graphic, Layout *layout) {
+
+  Display        *display;
+  XEvent          event;
+  KeySym          keysym;
+  int             status;
+  XComposeStatus  composestatus;
+  char            string[10], line[40], *name;
+  double          x, y;
+  
+  while (1) {
+    
+    status = CheckPipe (graphic, layout);
+    if (status == 0) return (FALSE);
+    if (status == 2) return (TRUE);
+    if (status == 1) goto events;
+
+  events:
+    if (XEventsQueued (graphic[0].display, QueuedAfterFlush) < 1) {
+      usleep (10000);
+      continue;
+    }
+
+    display = graphic[0].display;
+
+    /* only do the last entry for these events */
+    /* this ignores the return status from these functions */
+    if (LastEvent (display, ConfigureNotify, &event)) Reconfig (graphic, layout, &event);
+    if (LastEvent (display, Expose,          &event)) Refresh (graphic, layout, 1);
+    if (LastEvent (display, MappingNotify,   &event)) XRefreshKeyboardMapping ((XMappingEvent *) &event);
+    if (LastEvent (display, MotionNotify,    &event)) UpdatePointer (graphic, layout, (XMotionEvent *) &event);
+    if (LastEvent (display, ButtonPress,     &event)) InterpretPresses (graphic, layout, (XButtonEvent *) &event);
+    if (LastEvent (display, KeyPress,        &event)) {
+      status = XLookupString ((XKeyEvent *) &event, string, 9, &keysym, &composestatus);
+
+      if (event.xkey.x < layout[0].picture.x) continue;
+      if (event.xkey.x > layout[0].picture.x + layout[0].picture.dx) continue;
+      if (event.xkey.y < layout[0].picture.y) continue;
+      if (event.xkey.y > layout[0].picture.y + layout[0].picture.dy) continue;
+	
+      Screen_to_Image (&x, &y, (double)event.xkey.x, (double)event.xkey.y, layout);
+      name = XKeysymToString (keysym);
+
+      // skip the following keys: 
+      if (!strcmp (name, "Shift_L")) continue;
+      if (!strcmp (name, "Shift_R")) continue;
+      if (!strcmp (name, "Control_L")) continue;
+      if (!strcmp (name, "Control_R")) continue;
+      if (!strcmp (name, "Alt_L")) continue;
+      if (!strcmp (name, "Alt_R")) continue;
+      if (!strcmp (name, "Super_L")) continue;
+      if (!strcmp (name, "Super_R")) continue;
+      if (!strcmp (name, "Caps_Lock")) continue;
+      if (!strcmp (name, "Pause")) continue;
+      if (!strcmp (name, "Continue")) continue;
+      if (!strcmp (name, "Num_Lock")) continue;
+      if (!strcmp (name, "Scroll_Lock")) continue;
+      if (!strcmp (name, "Print")) continue;
+      if (!strcmp (name, "(null)")) continue;
+
+      snprintf (line, 40, "%12s %12.6f %12.6f ", name, x, y);
+      write (layout[0].Ximage, line, 40);
+    }
+
+    /* remove those events we will ignore */
+    while (XCheckMaskEvent (display, IgnoreMask, &event)) continue;
+
+    /* events to remove which have no mask component */
+    while (XCheckTypedEvent (display, MappingNotify, &event)) continue;
+    while (XCheckTypedEvent (display, ClientMessage, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionClear, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionNotify, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionRequest, &event)) continue;
+  }
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/DownArrow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/DownArrow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/DownArrow.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int 
+DownArrow (button_event, height, width, SB_x, SB_y)
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_y > (height - SB_y)) && 
+	    (m_x > (width - SB_y)));
+    return (answer);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/LeftArrow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/LeftArrow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/LeftArrow.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+LeftArrow (button_event, height, width, SB_x, SB_y) 
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_x < SB_x) && (m_y < SB_x));
+  return (answer);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/RightArrow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/RightArrow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/RightArrow.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+RightArrow (button_event, height, width, SB_x, SB_y) 
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_x < SB_x) && 
+	    (m_y > (height - SB_x - SB_y)) && 
+	    (m_y < (height - SB_y)));
+  return (answer);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/ScrollBars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/ScrollBars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/ScrollBars.c	(revision 22322)
@@ -0,0 +1,38 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+void
+ScrollBars (display, window, gc, height, width, SB_x, SB_y, f_x, f_y)
+Display *display;
+Window window;
+GC gc;
+int width, height, SB_x, SB_y;
+double f_x, f_y;
+{
+  
+  int x, y;
+
+  y = height - SB_y;
+  XDrawLine (display, window, gc, 0, y, width, y);
+  XDrawLine (display, window, gc, SB_x, 0, SB_x, height);
+  XDrawLine (display, window, gc, 0, SB_x, SB_x, SB_x);
+  x = width - SB_y;
+  y = height - SB_y;
+  XDrawLine (display, window, gc, x, y, x, height);
+  y = height - SB_x - SB_y;
+  XDrawLine (display, window, gc, 0, y, SB_x, y);
+  x = SB_y + SB_x;
+  y = height - SB_y;
+  XDrawLine (display, window, gc, x, y, x, height);
+  x = (width - 2*SB_y - SB_x) * f_x + SB_x + SB_y;
+  y = (height - 2*SB_x - SB_y) * f_y + SB_x;
+  XDrawLine (display, window, gc, x, height - SB_y, x, height);
+  XDrawLine (display, window, gc, x + 1, height - SB_y, x + 1, height);
+  XDrawLine (display, window, gc, 0, y, SB_x, y);
+  XDrawLine (display, window, gc, 0, y+1, SB_x, y+1);
+  y = height - SB_y;
+  XFillRectangle (display, window, gc, 0, y, SB_x, SB_y);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/UpArrow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/UpArrow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/UpArrow.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+UpArrow (button_event, height, width, SB_x, SB_y) 
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_y > (height - SB_y)) && 
+	    (m_x > SB_x) && 
+	    (m_x < (SB_x + SB_y)));
+  return (answer);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/xScrollBar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/xScrollBar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/xScrollBar.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int 
+xScrollBar (button_event, height, width, SB_x, SB_y, f)
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+double *f;
+{
+
+  int answer, m_x, m_y;
+  double f1, f2;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_x < SB_x) && 
+	    (m_y > SB_x) && 
+	    (m_y < (height - SB_x - SB_y)));
+  f1 = (double)(m_y - SB_x);
+  f2 = (double)(height - 2*SB_x - SB_y);
+  *f = f1 / f2;
+  return (answer);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/yScrollBar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/yScrollBar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/scrollbar/yScrollBar.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+yScrollBar (button_event, height, width, SB_x, SB_y, f)
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+double *f;
+{
+
+  int answer, m_x, m_y;
+  double f1, f2;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_y > (height - SB_y)) && 
+	    (m_x > (SB_x + SB_y)) && 
+	    (m_x < (width - SB_y)));
+  f1 = (double)(m_x - SB_x - SB_y);
+  f2 = (double)(width - 2*SB_y - SB_x);
+  *f = f1 / f2;
+  return (answer);
+
+}
+
+	
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckColors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckColors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckColors.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "Ximage.h"
+
+/************** CheckColors *************/
+void CheckColors (Graphic *graphic, int *argc, char **argv) {
+
+  char *temp_name;
+  int N;
+
+  /* here we define the values for "black" (blackground) and "white" (whiteground)
+     if -fg, or -bg exist, or if Foreground or Background are set in .Xdefaults, 
+     use those.  blackground defaults to Black, whiteground defaults to White. */
+
+  graphic[0].black = BlackPixel (graphic[0].display, graphic[0].screen);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "Foreground");
+  if ((N = get_argument (*argc, argv, "-fg"))) {
+    if (N + 1 < *argc) {
+      temp_name = argv[N+1];
+    } else {
+      fprintf (stderr, "error: usage is -fg color\n");
+      exit (0);
+    }
+  }
+  if (temp_name != (char *)NULL) 
+    graphic[0].black = GetColor (graphic[0].display, temp_name, graphic[0].colormap, graphic[0].black);
+
+  graphic[0].white = WhitePixel (graphic[0].display, graphic[0].screen);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "Background");
+  if ((N = get_argument (*argc, argv, "-bg"))) {
+    if (N + 1 < *argc) {
+      temp_name = argv[N+1];
+    } else {
+      fprintf (stderr, "error: usage is -bg color\n");
+      exit (0);
+    }
+  }  
+  if (temp_name != (char *)NULL) 
+    graphic[0].white = GetColor (graphic[0].display, temp_name, graphic[0].colormap, graphic[0].white);
+ 
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckDisplayName.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckDisplayName.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckDisplayName.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "Ximage.h"
+
+/************** CheckDisplayName *************/
+void CheckDisplayName (int *argc, char **argv, char *display_name) {
+
+  int N;
+
+  display_name[0] = 0;
+  if ((N = get_argument (*argc, argv, "-d"))) {
+    if (N + 1 < *argc) {
+      strcpy (display_name, argv[N + 1]);
+      remove_argument(N, argc, argv);
+      remove_argument(N, argc, argv);
+    } else {
+      fprintf (stderr, "error: usage is [-display/-d] DisplayName\n");
+      exit (0);
+    }
+  }
+  if ((N = get_argument (*argc, argv, "-display"))) {
+    if (N + 1 < *argc) {
+      strcpy (display_name, argv[N + 1]);
+      remove_argument(N, argc, argv);
+      remove_argument(N, argc, argv);
+    } else {
+      fprintf (stderr, "error: usage is [-display/-d] DisplayName\n");
+      exit (0);
+    }
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckFontName.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckFontName.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckFontName.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "Ximage.h"
+
+/************** CheckFontName *************/
+void CheckFontName (int *argc, char **argv, char *fontname) {
+
+  int N;
+
+  if ((N = get_argument (*argc, argv, "-font"))) {
+    if (N + 1 < *argc) {
+      strcpy (fontname, argv[N + 1]);
+      remove_argument(N, argc, argv);
+      remove_argument(N, argc, argv);
+    } else {
+      fprintf (stderr, "error: usage is -font fontname\n");
+      exit (0);
+    }
+  }   
+  if ((N = get_argument (*argc, argv, "-fn"))) {
+    if (N + 1 < *argc) {
+      strcpy (fontname, argv[N + 1]);
+      remove_argument(N, argc, argv);
+      remove_argument(N, argc, argv);
+    } else {
+      fprintf (stderr, "error: usage is -fn fontname\n");
+      exit (0);
+    }
+  }   
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckGeometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckGeometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckGeometry.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "Ximage.h"
+
+/************** CheckGeometry *************/
+void CheckGeometry (int *argc, char **argv, Graphic *graphic) {
+
+  int status, x, y, N;
+  unsigned int dx, dy;
+  int X, Y, dX, dY;
+  char *temp_name;
+  
+  temp_name = XGetDefault (graphic[0].display, argv[0], "geometry");
+  if ((N = get_argument (*argc, argv, "-geom"))) {
+    if (N + 1 < *argc) {
+      temp_name = argv[N+1];
+    }
+    else {
+      fprintf (stderr, "error: usage is -geom DisplayName\n");
+      exit (0);
+    }
+  }
+  X = 10;
+  Y = 10;
+  dX = 512 + 2*PAD1 + PAD1 + ZOOM_X;
+  dY = 512 + 2*PAD1 + PAD2 + COLORPAD;
+  if (temp_name != (char *)NULL) {  
+    status = XParseGeometry (temp_name, &x, &y, &dx, &dy);
+    if (status & XValue)
+      X = x;
+    if (status & YValue)
+      Y = y;
+    if (status & WidthValue) {
+      dX = dx + 2*PAD1 + PAD1 + ZOOM_X;
+      dX = MAX (dX, MIN_WIDTH);
+    }
+    if (status & HeightValue) {
+      dY = dy + 2*PAD1 + PAD2 + COLORPAD + 42;
+      dY = MAX (dY, MIN_HEIGHT);
+    }
+    if (status & XNegative)
+      X  = DisplayWidth  (graphic[0].display, graphic[0].screen) - dX + X;
+    if (status & YNegative)
+      Y  = DisplayHeight (graphic[0].display, graphic[0].screen) - dY + Y;
+  }
+
+  graphic[0].x = X;
+  graphic[0].y = Y;
+  graphic[0].dx = dX;
+  graphic[0].dy = dY; 
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckVisual.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckVisual.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CheckVisual.c	(revision 22322)
@@ -0,0 +1,196 @@
+# include "Ximage.h"
+# define NPIXELS 64
+
+/* DirectColor doesn't seem to work, even though it is available:
+   I cannot use XAllocColorCells to get pixels under DirectColor
+*/
+
+/* static int try_visual[] = {5, 3, 1, 4, 2, 0}; */
+
+void CheckVisual (Graphic *graphic, int *argc, char **argv) {
+
+  int i, Nfound, N;
+  int col, def, dyn;
+  XVisualInfo *visual_list, visual_temp;
+  unsigned long planes[3];
+  XPixmapFormatValues *pixmaps;
+  int Npixmaps;
+
+  if (DEBUG) {
+    fprintf (stderr, "DirectColor: %d\n", DirectColor);
+    fprintf (stderr, "PseudoColor: %d\n", PseudoColor);
+    fprintf (stderr, "TrueColor: %d\n", TrueColor);
+    fprintf (stderr, "GrayScale: %d\n", GrayScale);
+    fprintf (stderr, "StaticColor: %d\n", StaticColor);
+    fprintf (stderr, "StaticGray: %d\n", StaticGray);
+  }
+
+  visual_temp.screen = graphic[0].screen;
+  
+  /* find available visuals */
+  visual_list = XGetVisualInfo (graphic[0].display, VisualScreenMask, &visual_temp, &Nfound);
+  if (Nfound == 0) {
+    fprintf (stderr, "error finding useful visual\n");
+    exit (0);
+  }
+  
+  /* check default visual first */ 
+  for (i = 0; (i < Nfound) && (graphic[0].visual != visual_list[i].visual); i++);
+  if (i == Nfound) {
+    fprintf (stderr, "default visual not found??\n");
+    exit (0);
+  }
+
+  col = def = dyn = FALSE;
+  if (DEBUG) fprintf (stderr, "default visual class is %d\n", visual_list[i].class);
+  switch (visual_list[i].class) {
+  case PseudoColor:
+    col = def = dyn = TRUE;
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = TRUE;
+    goto test_pixels;
+    break;
+  default:
+    break;
+  }
+
+  for (i = 0; i < Nfound; i++) {
+    switch (visual_list[i].class) {
+    case PseudoColor:
+      if (DEBUG) fprintf (stderr, "selected visual class is %d\n", visual_list[i].class);
+      col = dyn = TRUE;
+      if (graphic[0].visual == visual_list[i].visual) {
+	def = TRUE;
+      } else {
+	graphic[0].visual = visual_list[i].visual;
+      }
+      graphic[0].visualclass = TRUE;
+      goto test_pixels;
+      break;
+    default:
+      break;
+    }
+  }
+
+  for (i = 0; i < Nfound; i++) {
+    switch (visual_list[i].class) {
+    case GrayScale:
+      if (DEBUG) fprintf (stderr, "selected visual class is %d\n", visual_list[i].class);
+      dyn = TRUE;
+      if (graphic[0].visual == visual_list[i].visual) {
+	def = TRUE;
+      } else {
+	graphic[0].visual = visual_list[i].visual;
+      }
+      graphic[0].visualclass = TRUE;
+      goto test_pixels;
+      break;
+    default:
+      break;
+    }
+  }
+
+  for (i = 0; (i < Nfound) && (visual_list[i].class != TrueColor); i++);
+  if (i != Nfound) {
+    col = TRUE;
+    if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+    if (graphic[0].visual == visual_list[i].visual) {
+      def = TRUE;
+    } else {
+      graphic[0].visual = visual_list[i].visual;
+    }
+    graphic[0].visualclass = FALSE;
+    goto test_pixels;
+  }
+
+  for (i = 0; (i < Nfound) && (visual_list[i].class != StaticColor); i++);
+  if (i != Nfound) {
+    if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+    if (graphic[0].visual == visual_list[i].visual) {
+      def = TRUE;
+    } else {
+      graphic[0].visual = visual_list[i].visual;
+    }
+    graphic[0].visualclass = FALSE;
+    goto test_pixels;
+  }
+
+  for (i = 0; (i < Nfound) && (visual_list[i].class != StaticGray); i++);
+  if (i != Nfound) {
+    if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+    if (graphic[0].visual == visual_list[i].visual) {
+      def = TRUE;
+    } else {
+      graphic[0].visual = visual_list[i].visual;
+    }
+    graphic[0].visualclass = FALSE;
+    goto test_pixels;
+  }
+
+  test_pixels:
+
+  /* need to make a colormap if 
+     1) the selected visual is not the default 
+     2) there are not enough colors available */
+
+  /* NEED TO ADD A COUPLE LINES DEFINING THE VARIOUS HARD COLORS (BLACK, WHITE, AND THE OVERLAYS) */
+
+  if ((N = get_argument (*argc, argv, "-private"))) {
+    remove_argument(N, argc, argv);
+    def = FALSE;
+  } 
+
+  if (!def) {
+    if (DEBUG) fprintf (stderr, "allocated private colormap\n");
+    graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, AllocNone);
+  }
+
+  if (dyn) {
+    /* allocate color cells  */
+    for (graphic[0].Npixels = NPIXELS;
+	 ((graphic[0].Npixels >= 16) && 
+	  !XAllocColorCells (graphic[0].display, graphic[0].colormap, FALSE, planes, 1, graphic[0].pixels, graphic[0].Npixels));
+	 graphic[0].Npixels -= 4) {
+      if (DEBUG) fprintf (stderr, "trying %d colors\n", (int) graphic[0].Npixels);
+    }
+
+
+    /* make private colormap  */
+    if (graphic[0].Npixels < 16) {
+      fprintf (stderr, "can't allocate enough cells, using private colormap\n");
+      graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, AllocNone);
+      for (graphic[0].Npixels = NPIXELS;
+	   ((graphic[0].Npixels >= 16) && 
+	    !XAllocColorCells (graphic[0].display, graphic[0].colormap, FALSE, planes, 1, graphic[0].pixels, graphic[0].Npixels));
+	   graphic[0].Npixels -= 4);
+
+      if ((N = get_argument (*argc, argv, "-colorcount"))) {
+	fprintf (stderr, "kii can grab %d colors\n", (int) graphic[0].Npixels);
+	exit (0);
+      } 
+      if (graphic[0].Npixels < 16) {
+	fprintf (stderr, "can't even allocate enough cells in private colormap\n");
+	exit (0);
+      }
+    }
+  }
+
+  if (!dyn) {
+    graphic[0].Npixels = 256;
+    fprintf (stderr, "can't use dynamic colors, color scrollbar inactive\n");
+    fprintf (stderr, " this can be avoided by using a dynamic visual class\n");
+    fprintf (stderr, " (see Kii help page for details)\n");
+  }
+
+  if ((N = get_argument (*argc, argv, "-colorcount"))) {
+    fprintf (stderr, "kii can grab %d colors\n", (int) graphic[0].Npixels);
+    exit (0);
+  } 
+  
+  pixmaps = XListPixmapFormats (graphic[0].display, &Npixmaps);
+  for (i = 0; i < Npixmaps; i++) {
+    if (pixmaps[i].depth == graphic[0].depth) {
+      graphic[0].Nbits = pixmaps[i].bits_per_pixel;
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CloseDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CloseDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CloseDisplay.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "Ximage.h"
+
+/************** CloseDisplay *************/
+void CloseDisplay (Graphic *graphic) {
+
+  XFlush (graphic[0].display);
+  XFreeFont (graphic[0].display, graphic[0].font); 
+  XFreeGC (graphic[0].display, graphic[0].gc);
+  XDestroySubwindows (graphic[0].display, graphic[0].window);
+  XDestroyWindow (graphic[0].display, graphic[0].window);
+  XFlush (graphic[0].display);
+  XCloseDisplay (graphic[0].display);
+  return;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CreateWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CreateWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/CreateWindow.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include "Ximage.h"
+
+/************** CreateWindow *************/
+void
+CreateWindow (graphic, parent, border, events)
+Graphic       graphic[];
+Window        parent;
+int           border;
+long          events;
+{
+
+  XSetWindowAttributes attributes;
+  unsigned long attribute_mask;
+
+  attributes.background_pixel = graphic[0].white;
+  attributes.border_pixel     = graphic[0].black;
+  attributes.event_mask       = events;
+  attributes.colormap         = graphic[0].colormap;
+  attribute_mask              = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap;
+
+  graphic[0].window = XCreateWindow (graphic[0].display, parent, 
+				     graphic[0].x, graphic[0].y, 
+				     graphic[0].dx, graphic[0].dy, 
+				     border, graphic[0].depth,
+				     InputOutput, graphic[0].visual, 
+				     attribute_mask, &attributes);
+
+  if (graphic[0].window == (Window) None)
+    QuitX (graphic[0].display, "", "error: could not open window");
+
+  XSelectInput (graphic[0].display, graphic[0].window, events);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/DefineLayout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/DefineLayout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/DefineLayout.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "Ximage.h"
+
+static char default_colormap[] = "grayscale";
+
+void DefineLayout (Layout *layout, Graphic *graphic, int argc, char **argv) {
+
+  int i, N;
+  char *colormap;
+
+  /** initiate connection with mana **/
+  if (!FOREGROUND) {
+    if (argc < 2) {
+      fprintf (stderr, "socket path not specified\n");
+      exit (0);
+    }
+    layout[0].Ximage = KiiWait (argv[1]);
+  }
+
+  PositionPictures (layout, graphic);
+  
+  /* get the display colors */
+  if (USE_XWINDOW) {
+    MakeColormap (graphic, layout, argc, argv);
+  } else {
+    colormap = default_colormap;
+    if ((N = get_argument (argc, argv, "-cm"))) {
+      remove_argument (N, &argc, argv);
+      colormap = argv[N];
+    }
+    layout[0].Npixels = 256;
+    SetColormap (graphic, layout, colormap);
+  }
+
+  /** set up a bunch of default things **/
+  layout[0].X = layout[0].Y = 0;
+  layout[0].expand = 1;
+  layout[0].zero = 0;
+  layout[0].range = 1;
+  layout[0].coords.Npolyterms = 0;
+  for (i = 0; i < NOVERLAYS; i++) {
+    layout[0].overlay[i].Nobjects = 0;
+    ALLOCATE (layout[0].overlay[i].objects, Object, 1);  /* allocate so later free will not crash! */
+    OVERLAY[i] = FALSE;
+  }
+  layout[0].matrix.size = 0; /* a flag to show there is no data in the matrix */
+
+  MOVE_POINTER = TRUE;
+  DECIMAL_DEG  = TRUE;
+  ALLOCATE (layout[0].matrix.buffer, char, 1);  /* allocate so later free will not crash! */
+  ALLOCATE (layout[0].picture.data, char, 1);   /* allocate so later free will not crash! */
+  ALLOCATE (layout[0].cmapbar.data, char, 1);   /* allocate so later free will not crash! */
+  ALLOCATE (layout[0].zoom.data, char, 1);      /* allocate so later free will not crash! */
+
+  if (USE_XWINDOW) {
+    CreatePicture (layout, graphic);
+    CreateColorbar (layout, graphic);
+    CreateZoom (layout, graphic, 0, 0); 
+  }
+}
+
+/* this routine is independent of the number of overlays */
+
+  /* cmap = the mapping of value to color in kii
+     colormap = the set of pixel -> color representation in X */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/DrawBitmap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/DrawBitmap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/DrawBitmap.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "Ximage.h"
+
+void
+DrawBitmap (graphic, x, y, dx, dy, bitmap, mode)
+Graphic     graphic[];
+int         x, y, dx, dy;
+char        bitmap[];
+int         mode;
+{
+
+  int i, j, byte_line, byte, bit, flag;
+  unsigned long int black, white;
+
+  black = graphic[0].black;
+  white = graphic[0].white;
+
+  if (mode == 0) {
+    black = graphic[0].black;
+    white = graphic[0].white;
+    graphic[0].black = white;
+    graphic[0].white = white;
+  }
+    
+  if (mode == 2) {
+    black = graphic[0].black;
+    white = graphic[0].white;
+    graphic[0].black = white;
+    graphic[0].white = black;
+  }
+    
+  
+  byte_line = (int) ((dx + 7) / 8);
+  for (i = 0; i < dy; i++) {
+    for (j = 0; j < dx; j++) {
+      byte = byte_line * i + (j / 8);
+      bit = j % 8;
+      flag = 0x01 & (bitmap[byte] >> bit);
+      if (flag)
+	XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+      else 
+	XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+      XDrawPoint (graphic[0].display, graphic[0].window, 
+		  graphic[0].gc, x + j, y + i);
+    }
+  }
+  if (mode == 0) {
+    graphic[0].black = black;
+    graphic[0].white = white;
+  }
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/GetColor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/GetColor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/GetColor.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "Ximage.h"
+
+/************** GetColor *************/
+unsigned long 
+GetColor (display, name, colormap, default_color)
+Display  *display;
+char      name[];
+Colormap  colormap;
+unsigned long default_color;
+{
+
+  unsigned long color;
+  XColor rgbcolor, hardwarecolor;
+  int status;
+
+  color = default_color;
+  status = XLookupColor (display, colormap, name, &rgbcolor, &hardwarecolor);
+  if (status) {
+    status = XAllocColor (display, colormap, &hardwarecolor);
+    if (status) {
+      color = hardwarecolor.pixel;
+    }
+  }
+  return (color);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/LoadFont.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/LoadFont.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/LoadFont.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include "Ximage.h"
+
+/************** LoadFont *************/
+void
+LoadFont (graphic, argc, argv, default_name)
+Graphic   graphic[];
+int      *argc;
+char     *argv[];
+char      default_name[];
+{
+
+  char name[400];
+
+  strcpy (name, default_name);
+  CheckFontName (argc, argv, name);
+  graphic[0].font = XLoadQueryFont (graphic[0].display, name);
+  if (graphic[0].font == (XFontStruct *) NULL) {
+    fprintf (stderr, "Could not load fond %s, using %s\n", name, default_name);
+    graphic[0].font = XLoadQueryFont (graphic[0].display, default_name);
+    if (graphic[0].font == (XFontStruct *) NULL) {
+      graphic[0].font = XLoadQueryFont (graphic[0].display, "fixed");
+      fprintf (stderr, "Could not load fond %s, using %s\n", 
+	       default_name, "fixed");
+    }
+  }
+  if (graphic[0].font != (XFontStruct *) NULL) 
+    XSetFont (graphic[0].display, graphic[0].gc, graphic[0].font[0].fid);
+  else
+    QuitX (graphic[0].display, "Error: could not load font", name);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MakeColormap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MakeColormap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MakeColormap.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "Ximage.h"
+# define NPIXELS 64
+
+static char default_cmap[] = "grayscale";
+
+void MakeColormap (Graphic *graphic, Layout *layout, int argc, char **argv) {
+
+  int i, N, status;
+  char *temp_name;
+
+  /* hardwired colors - white, black -- for drawing text and overlay graphics */
+  layout[0].white = graphic[0].white;
+  layout[0].black = graphic[0].black;
+  layout[0].start = 0;
+  layout[0].slope = 1;
+  temp_name = XGetDefault (graphic[0].display, argv[0], "ROverlay");
+  layout[0].overlay[0].color = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "red"    : temp_name), graphic[0].colormap, graphic[0].black);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "GOverlay");
+  layout[0].overlay[1].color = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "green"  : temp_name), graphic[0].colormap, graphic[0].black);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "BOverlay");
+  layout[0].overlay[2].color = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "blue"   : temp_name), graphic[0].colormap, graphic[0].black);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "YOverlay");
+  layout[0].overlay[3].color = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "yellow" : temp_name), graphic[0].colormap, graphic[0].black);
+
+  layout[0].Npixels = graphic[0].Npixels;
+  for (i = 0; i < layout[0].Npixels; i++) 
+    layout[0].cmap[i].pixel = graphic[0].pixels[i];
+
+  /* decide on a color map */
+  temp_name = XGetDefault (graphic[0].display, argv[0], "Colormap");
+  if ((N = get_argument (argc, argv, "-cm"))) {
+    if (N + 1 < argc) {
+      temp_name = argv[N+1];
+    } else {
+      fprintf (stderr, "error: usage is -cm ColormapName\n");
+      exit (1);
+    }
+  }
+  if (temp_name == (char *) NULL) temp_name = default_cmap;
+
+  status = SetColormap (graphic, layout, temp_name);
+  if (!status) {
+    fprintf (stderr, "invalid colormap, using default %s\n", default_cmap);
+    temp_name = default_cmap;
+    status = SetColormap (graphic, layout, temp_name);
+    if (!status) {
+      fprintf (stderr, "problem with default colormap\n");
+      exit (1);
+    }
+  }
+}
+
+/* this routine is NOT independent of the number of overlays */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MakeCursor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MakeCursor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MakeCursor.c	(revision 22322)
@@ -0,0 +1,16 @@
+# include "Ximage.h"
+
+/************** MakeCursor *************/
+void
+MakeCursor  (graphic, which_cursor)
+Graphic      graphic[];
+unsigned int which_cursor;
+{
+
+  graphic[0].cursor = XCreateFontCursor (graphic[0].display, 
+					 (unsigned) which_cursor);
+  if (graphic[0].cursor != (Cursor) None)
+    XDefineCursor (graphic[0].display, graphic[0].window, graphic[0].cursor);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MakeGC.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MakeGC.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MakeGC.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "Ximage.h"
+
+/************** MakeGC *************/
+void MakeGC (Graphic *graphic) {
+
+  XGCValues gcvalues;
+
+  gcvalues.foreground = graphic[0].black;
+  gcvalues.background = graphic[0].white;
+  graphic[0].gc = XCreateGC (graphic[0].display, graphic[0].window, 
+			     GCForeground | GCBackground, &gcvalues);
+  if (graphic[0].gc == 0)
+    QuitX (graphic[0].display, "Error in creating a Graphics Context", "");
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MapWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MapWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/MapWindow.c	(revision 22322)
@@ -0,0 +1,13 @@
+# include "Ximage.h"
+
+/************** MapWindow *************/
+void
+MapWindow (graphic)
+Graphic    graphic[];
+{
+
+  XMapRaised (graphic[0].display, graphic[0].window);
+  XMapSubwindows (graphic[0].display, graphic[0].window);
+  XFlush (graphic[0].display);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/NameWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/NameWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/NameWindow.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "Ximage.h"
+
+/************** NameWindow *************/
+void NameWindow (Graphic *graphic, char *name) {
+
+  char       *class_name;
+  char       *class_type;
+  XClassHint *classhints;
+
+  class_type = class_name = name;
+
+  classhints = XAllocClassHint ();
+
+  if (classhints != (XClassHint *) NULL)  {
+    classhints[0].res_name = class_name;
+    classhints[0].res_class = class_type;
+    XSetClassHint (graphic[0].display, graphic[0].window, classhints);
+    XFree (classhints);
+  }
+  
+  XStoreName (graphic[0].display, graphic[0].window, name);
+  XSetIconName (graphic[0].display, graphic[0].window, name);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/OpenDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/OpenDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/OpenDisplay.c	(revision 22322)
@@ -0,0 +1,19 @@
+# include "Ximage.h"
+
+/************** OpenDisplay *************/
+Display *OpenDisplay (char *display_name, int *screen) {
+
+  Display *display;
+  
+  display = XOpenDisplay (display_name);
+  if (display != (Display *) NULL)
+    *screen = DefaultScreen (display);
+  if (display == (Display *) NULL) {
+    fprintf (stderr, "Error could not open X display to %s\n", 
+	     XDisplayName (display_name));
+    exit (0);
+  }
+
+  return (display);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/QuitX.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/QuitX.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/QuitX.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "Ximage.h"
+
+/************** QuitX *************/
+void
+QuitX (display, error_message, error_file)
+Display *display;
+char error_message[], error_file[];
+{
+
+  fprintf (stderr, "Error: %s%s\n", error_message, error_file);
+  XCloseDisplay (display);
+  exit (0);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetNormalHints.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetNormalHints.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetNormalHints.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "Ximage.h"
+
+/************** SetNormalHints  *************/
+void
+SetNormalHints (graphic)
+Graphic         graphic[];
+{
+
+  XSizeHints *sizehints;
+
+  sizehints = XAllocSizeHints ();
+
+  if (sizehints != (XSizeHints *) NULL)  {
+    sizehints[0].x = graphic[0].x;
+    sizehints[0].y = graphic[0].x;
+    sizehints[0].width = graphic[0].dx;
+    sizehints[0].height = graphic[0].dy;
+    sizehints[0].min_width = MIN_WIDTH;
+    sizehints[0].min_height = MIN_HEIGHT;    
+    sizehints[0].flags = USPosition | USSize | PMinSize;
+
+    sizehints[0].base_width = graphic[0].dx;
+    sizehints[0].base_height = graphic[0].dy;
+    sizehints[0].flags |= PBaseSize;
+    
+    XSetWMNormalHints (graphic[0].display, graphic[0].window, sizehints);
+    XFree (sizehints);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetUpDisplay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetUpDisplay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetUpDisplay.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "Ximage.h"
+
+/************** SetUpDisplay *************/
+void SetUpDisplay (Graphic *graphic, int *argc, char **argv) {
+
+  char display_name[120];
+
+  CheckDisplayName (argc, argv, display_name); 
+
+  /* set default values */
+  graphic[0].display  = OpenDisplay (display_name, &graphic[0].screen);
+  graphic[0].colormap = DefaultColormap (graphic[0].display, graphic[0].screen);
+  graphic[0].depth    = DefaultDepth    (graphic[0].display, graphic[0].screen);
+  graphic[0].visual   = DefaultVisual   (graphic[0].display, graphic[0].screen);
+
+  /* get the basic colors */
+  CheckVisual (graphic, argc, argv);
+  CheckColors (graphic, argc, argv);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetUpWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetUpWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetUpWindow.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "Ximage.h"
+# include "icons/icon.h"
+
+/************** SetUpWindow *************/
+void SetUpWindow (Graphic *graphic, int *argc, char **argv) {
+
+  Icon icon;
+  char *name;
+
+  icon.width = icon_width;
+  icon.height = icon_height;
+  icon.bits = icon_bits;
+
+  CheckGeometry (argc, argv, graphic);
+  TopWindow (graphic, &icon);
+  LoadFont (graphic, argc, argv, "fixed"); 
+
+  XFlush (graphic[0].display);
+  XSetWindowBackground (graphic[0].display, graphic[0].window, graphic[0].white);
+
+  SetNormalHints (graphic);
+  SetWMHints (graphic, &icon);
+
+  if (NAME_WINDOW == NULL) {
+    NameWindow (graphic, "Ki'i");
+  } else {
+    ALLOCATE (name, char, strlen(NAME_WINDOW) + 10);
+    sprintf (name, "Ki'i %s", NAME_WINDOW);
+    NameWindow (graphic, name);
+    free (name);
+  }
+
+  if (MAP_WINDOW) MapWindow (graphic);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetWMHints.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetWMHints.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/SetWMHints.c	(revision 22322)
@@ -0,0 +1,27 @@
+# include "Ximage.h"
+
+/************** SetWMHints  *************/
+void
+SetWMHints (graphic, icon) 
+Graphic     graphic[];
+Icon        icon[];
+{
+  XWMHints *wmhints;
+
+  wmhints = XAllocWMHints ();
+  if (wmhints != (XWMHints *)NULL) {
+    wmhints[0].initial_state = NormalState;
+    wmhints[0].input = True;
+    if (icon[0].pixmap != (Pixmap) None) {
+      wmhints[0].icon_pixmap = icon[0].pixmap;
+      wmhints[0].icon_mask = icon[0].pixmap;
+      wmhints[0].flags = StateHint | InputHint | IconPixmapHint | IconMaskHint;
+    }
+    else 
+      wmhints[0].flags = StateHint | InputHint;
+    
+    XSetWMHints (graphic[0].display, graphic[0].window, wmhints);
+    XFree (wmhints);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/TopWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/TopWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/TopWindow.c	(revision 22322)
@@ -0,0 +1,21 @@
+# include "Ximage.h"
+
+/************** TopWindow *************/
+void TopWindow (Graphic *graphic, Icon *icon) {
+
+  Window rootwindow;
+
+  rootwindow = RootWindow (graphic[0].display, graphic[0].screen);
+
+  CreateWindow (graphic, rootwindow, BORDER_WIDTH, EVENT_MASK);
+  MakeGC (graphic);
+  icon[0].pixmap = XCreateBitmapFromData (graphic[0].display,
+					graphic[0].window, 
+					icon[0].bits, 
+					icon[0].width, 
+					icon[0].height);
+  MakeCursor (graphic, DEFAULT_CURSOR);
+  XFreeCursor (graphic[0].display, graphic[0].cursor);
+  XFlush (graphic[0].display);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/Ximage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/Ximage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/Ximage.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "Ximage.h"
+
+int main (int argc, char **argv) {
+
+  Graphic graphic;
+  Layout  layout;
+  
+  args (&argc, argv);
+
+  bzero (&graphic, sizeof(Graphic));
+  bzero (&layout, sizeof(Layout));
+
+  if (USE_XWINDOW) SetUpDisplay (&graphic, &argc, argv);
+  if (USE_XWINDOW) {
+    SetUpWindow (&graphic, &argc, argv);
+  } else {
+    graphic.x = 10;
+    graphic.y = 10;
+    graphic.dx = 512 + 2*PAD1 + PAD1 + ZOOM_X;
+    graphic.dy = 512 + 2*PAD1 + PAD2 + COLORPAD;
+  }
+
+  DefineLayout (&layout, &graphic, argc, argv);
+  EventLoop (&graphic, &layout);
+
+  if (USE_XWINDOW) CloseDisplay (&graphic);
+  exit (0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/setup/args.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "Ximage.h"
+
+int args (int *argc, char **argv) {
+
+  int N;
+
+  MAP_WINDOW = TRUE;
+  if ((N = get_argument (*argc, argv, "-nomap"))) {
+    remove_argument(N, argc, argv);
+    MAP_WINDOW = FALSE;
+  }
+
+  NAME_WINDOW = NULL;
+  if ((N = get_argument (*argc, argv, "-name"))) {
+    remove_argument(N, argc, argv);
+    NAME_WINDOW = strcreate (argv[N]);
+    remove_argument(N, argc, argv);
+  }
+
+  USE_XWINDOW = TRUE;
+  if ((N = get_argument (*argc, argv, "-noX"))) {
+    remove_argument(N, argc, argv);
+    USE_XWINDOW = FALSE;
+  }
+
+  DEBUG = FALSE;
+  if ((N = get_argument (*argc, argv, "-debug"))) {
+    remove_argument(N, argc, argv);
+    DEBUG = TRUE;
+  }
+
+  FOREGROUND = FALSE;
+  if ((N = get_argument (*argc, argv, "-fg"))) {
+    remove_argument(N, argc, argv);
+    FOREGROUND = TRUE;
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/test/jpeg.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/test/jpeg.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/test/jpeg.sh	(revision 22322)
@@ -0,0 +1,17 @@
+
+macro kii-test
+  mcreate z 512 512
+  set a = xramp (z)
+  tv a 0 512
+
+  load red sample.reg
+
+  create x 64 512 64
+  vload red x x -type circle -size 5
+  vload blue x x -type box -size 3
+  vload yellow x x -type line -size 10
+
+  jpeg -name test.png
+  ps -name test.ps
+end
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/test/regions.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/test/regions.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/test/regions.sh	(revision 22322)
@@ -0,0 +1,32 @@
+
+$KII = kii -noX
+
+macro go
+ mcreate a 512 512
+ set b = yramp (a)
+ tv b 0 512
+ create x 0 512 64
+ set y = 1.1*x
+ vload red x x
+ vload red x y
+ set y = x + 10
+ vload blue -type line x y
+ set y = x + 20
+ vload green -type circle x y -size 3.0
+ set y = x + 30
+ vload yellow -type box x y -size 5.0
+
+ for i 1 6
+  vload red -type box x y -size $i
+  vload blue -type circle x y -size $i
+ end
+
+ jpeg -name test.jpg
+
+ resize 512 512
+ center 256 256 1
+ jpeg -name test2.jpg
+
+ center 256 256 3
+ jpeg -name test3.jpg
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/CheckTextLines.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/CheckTextLines.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/CheckTextLines.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "Ximage.h"
+
+/******** Here we test the TextLines specific to this program  ****
+TextLine *
+CheckTextLines (graphic, event, layout)
+Graphic         graphic[];
+XEvent          event[];
+Layout          layout[];
+{
+
+  TextLine *textline;
+  textline = (TextLine *) NULL;
+
+  if (InTextLine (graphic, event, &layout[0].zero))
+    textline = &layout[0].zero;
+  if (InTextLine (graphic, event, &layout[0].range))
+    textline = &layout[0].range;
+  if (InTextLine (graphic, event, &layout[0].effects))
+    textline = &layout[0].effects;
+  if (InTextLine (graphic, event, &layout[0].command))
+    textline = &layout[0].command;
+
+  return (textline);
+
+}
+
+
+***/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/DrawCursor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/DrawCursor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/DrawCursor.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+
+
+DrawCursor (graphic, textline)    
+Graphic       graphic[];
+TextLine      textline[];
+{
+
+  int dx, label_width, DX, text_width, i;
+
+  label_width  = XTextWidth (graphic[0].font, 
+			     textline[0].label,
+			     strlen(textline[0].label));
+  
+  text_width  = XTextWidth (graphic[0].font, 
+			    textline[0].text,
+			    strlen(textline[0].text));
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+
+  if (label_width + text_width + 2*XBorder > textline[0].dx) {
+    DX = textline[0].dx - label_width - 2*XBorder - arrow_width;
+    for (i = 1; (i < strlen (textline[0].text)) && (text_width > DX); i++) {
+      text_width  = XTextWidth (graphic[0].font, 
+				&textline[0].text[i],
+				strlen(&textline[0].text[i]));
+    }
+    dx  = XTextWidth (graphic[0].font, &textline[0].text[i - 1], 
+		      textline[0].cursor - i + 1) + arrow_width;
+  }
+  else 
+    dx  = XTextWidth (graphic[0].font, textline[0].text, textline[0].cursor);
+
+  XDrawLine (graphic[0].display, 
+	     graphic[0].window, 
+	     graphic[0].gc, 
+	     textline[0].x + label_width + XBorder + dx, 
+	     textline[0].y + YBorder,
+	     textline[0].x + label_width + XBorder + dx, 
+	     textline[0].y + textline[0].dy - YBorder);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/DrawTextBox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/DrawTextBox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/DrawTextBox.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "Ximage.h"
+# define XBorder 10
+# define YBorder 2
+# define arrow_width 6
+# define arrow_height 9
+static char arrow_bits[] = {
+  0x00, 0x02, 0x06, 0x0e, 0x1e, 0x0e, 0x06, 0x02, 0x00};
+#define down_width 9
+#define down_height 6
+static char down_bits[] = {
+   0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00};
+
+/******************* DrawTextBox ****************/
+DrawTextBox (graphic, textbox)
+Graphic      graphic[];
+TextBox      textbox[];
+{
+
+  int X, Y, y, dy, Nlines, i;
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+  XFillRectangle (graphic[0].display, 
+		  graphic[0].window,
+		  graphic[0].gc,
+		  textbox[0].x  + 1,  textbox[0].y + 1,
+		  XBorder - 2, textbox[0].dy - 2);
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  if (textbox[0].outline) {
+    XDrawRectangle (graphic[0].display, 
+		    graphic[0].window,
+		    graphic[0].gc,
+		    textbox[0].x,  textbox[0].y,
+		    textbox[0].dx, textbox[0].dy);
+  }
+
+  dy = graphic[0].font[0].ascent + graphic[0].font[0].descent;
+  Nlines = (textbox[0].dy - 2*YBorder) / dy;
+
+  for (i = 0; (i < Nlines) && (i < textbox[0].Nlines); i++) {
+    y = textbox[0].y + graphic[0].font[0].ascent + dy*i + YBorder;
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 textbox[0].x + XBorder, y,
+		 textbox[0].text[i], 
+		 strlen(textbox[0].text[i]));
+  }
+
+  if (Nlines < textbox[0].Nlines) {
+    DrawBitmap (graphic, 
+		textbox[0].x + textbox[0].dx - down_width - 2,
+		textbox[0].y + textbox[0].dy - down_height - 2,
+		down_width, down_height, down_bits, 1);
+  }
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  if (textbox[0].cursor_line != -1) {
+    Y = textbox[0].y + dy*textbox[0].cursor_line + YBorder + (dy - arrow_height)/2;
+    X = textbox[0].x + (XBorder - arrow_width) / 2;
+    DrawBitmap (graphic, X, Y, arrow_width, arrow_height, arrow_bits, 1);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/DrawTextLine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/DrawTextLine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/DrawTextLine.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+DrawTextLine (graphic, textline)
+Graphic  graphic[];
+TextLine textline[];
+{
+
+  int y;
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  if (textline[0].outline) {
+    XDrawRectangle (graphic[0].display, 
+		    graphic[0].window,
+		    graphic[0].gc,
+		    textline[0].x,  textline[0].y,
+		    textline[0].dx, textline[0].dy);
+  }
+
+  y = textline[0].y + (textline[0].dy + graphic[0].font[0].ascent) / 2;
+  XDrawString (graphic[0].display, 
+	       graphic[0].window, 
+	       graphic[0].gc, 
+	       textline[0].x + XBorder, y,
+	       textline[0].label, 
+	       strlen(textline[0].label));
+  RedrawString (graphic, textline);
+
+  if (textline[0].cursor != -1)
+    DrawCursor (graphic, textline);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/InTextBox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/InTextBox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/InTextBox.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "Ximage.h"
+# define XBorder 10
+# define YBorder 2
+# define arrow_width 6
+# define arrow_height 9
+static char arrow_bits[] = {
+  0x00, 0x02, 0x06, 0x0e, 0x1e, 0x0e, 0x06, 0x02, 0x00};
+#define down_width 9
+#define down_height 6
+static char down_bits[] = {
+   0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00};
+
+/******************* InTextBox ****************/
+int
+InTextBox (graphic, event, textbox)
+Graphic          graphic[];
+XButtonEvent    *event;
+TextBox          textbox[];
+{
+
+  int answer;
+  int i, x, y, Y, X, dy;
+  int minX, maxX, minY, maxY;
+  
+  x = event[0].x;
+  y = event[0].y;
+  dy = graphic[0].font[0].ascent + graphic[0].font[0].descent;
+  
+  minX = textbox[0].x;
+  maxX = textbox[0].x + textbox[0].dx;
+  minY = textbox[0].y;
+  maxY = textbox[0].y + textbox[0].dy;
+  if (dy*textbox[0].Nlines + 2*YBorder < textbox[0].dy)
+    maxY = textbox[0].y + dy*textbox[0].Nlines + 2*YBorder;
+
+  answer = ((x >= minX) && (x <= maxX) && (y >= minY) && (y <= maxY));
+  
+  /* find the cursor position, erase the old arrow and draw a new arrow */
+  if (answer) {
+    if (textbox[0].cursor_line != -1) {
+      Y = textbox[0].y + dy*textbox[0].cursor_line + YBorder + (dy - arrow_height)/2;
+      X = textbox[0].x + (XBorder - arrow_width) / 2;
+      DrawBitmap (graphic, X, Y, arrow_width, arrow_height, arrow_bits, 0);
+    }
+    textbox[0].cursor_line = (y - textbox[0].y - YBorder)/dy;
+    X = textbox[0].x + (XBorder - arrow_width) / 2;
+    Y = textbox[0].y + dy*textbox[0].cursor_line + YBorder + (dy - arrow_height)/2;
+    DrawBitmap (graphic, X, Y, arrow_width, arrow_height, arrow_bits, 1);
+    textbox[0].cursor_x = x;
+    textbox[0].cursor_y = y;
+    XFlush (graphic[0].display);
+  }
+  
+  return (answer);
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/InTextLine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/InTextLine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/InTextLine.c	(revision 22322)
@@ -0,0 +1,58 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+int
+InTextLine (graphic, event, textline)
+Graphic          graphic[];
+XButtonEvent    *event;
+TextLine         textline[];
+{
+
+  int answer, done;
+  int i, x, dx, y, text_width, label_width;
+  int minX, maxX, minY, maxY;
+  char testline[1000];
+
+  x = event[0].x;
+  y = event[0].y;
+  
+  label_width  = XTextWidth (graphic[0].font, 
+			     textline[0].label,
+			     strlen(textline[0].label));
+  
+  minX = textline[0].x + XBorder + label_width;
+  maxX = textline[0].x + textline[0].dx;
+  minY = textline[0].y;
+  maxY = textline[0].y + textline[0].dy;
+
+  answer = ((x >= minX) && (x <= maxX) && (y >= minY) && (y <= maxY));
+
+  /* find the cursor position and draw a vertical bar */
+  if (answer) {
+    text_width  = XTextWidth (graphic[0].font, 
+			      textline[0].text,
+			      strlen(textline[0].text));
+    
+    if (x >= minX + text_width) {
+      textline[0].cursor = strlen (textline[0].text);
+    }
+    else {
+      for (i = 1, done = FALSE; (i <= strlen (textline[0].text)) && !done; i++) {
+	dx  = XTextWidth (graphic[0].font, 
+			  textline[0].text, i);
+	if (x < minX + dx) {
+	  textline[0].cursor = i - 1;
+	  done = TRUE;
+	}
+      }
+    }
+    DrawCursor (graphic, textline);
+  }    
+  XFlush (graphic[0].display);
+  
+  return (answer);
+  
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/RedrawString.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/RedrawString.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/RedrawString.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+
+#define arrow_width 6
+#define arrow_height 9
+RedrawString (graphic, textline)
+Graphic       graphic[];
+TextLine      textline[];
+{
+
+  int label_width, text_width, y, i, DX;
+  static char arrow_bits[] = {
+    0x00, 0x10, 0x18, 0x1c, 0x1e, 0x1c, 0x18, 0x10, 0x00};
+  
+  label_width  = XTextWidth (graphic[0].font, 
+			     textline[0].label,
+			     strlen(textline[0].label));
+  
+  text_width  = XTextWidth (graphic[0].font, 
+			    textline[0].text,
+			    strlen(textline[0].text));
+
+  if (label_width + text_width + 2*XBorder > textline[0].dx) {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		    textline[0].x + XBorder + label_width, 
+		    textline[0].y + YBorder, 
+		    textline[0].dx - XBorder - label_width, 
+		    textline[0].dy - 2*YBorder + 1);
+    
+    DX = textline[0].dx - label_width - 2*XBorder - arrow_width;
+    for (i = 1; (i < strlen (textline[0].text)) && (text_width > DX); i++) {
+      text_width  = XTextWidth (graphic[0].font, 
+				&textline[0].text[i],
+				strlen(&textline[0].text[i]));
+    }
+
+    y = textline[0].y + (textline[0].dy + graphic[0].font[0].ascent) / 2;
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+    DrawBitmap (graphic, 
+		textline[0].x + XBorder + label_width,
+		textline[0].y + YBorder + (textline[0].dy - arrow_height)/2,
+		arrow_width, arrow_height, arrow_bits, 1);
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 textline[0].x + XBorder + label_width + arrow_width, y,
+		 &textline[0].text[i - 1], 
+		 strlen(&textline[0].text[i - 1]));
+    XFlush (graphic[0].display);
+  }
+  else {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		    textline[0].x + XBorder + label_width, 
+		    textline[0].y + YBorder, 
+		    textline[0].dx - XBorder - label_width, 
+		    textline[0].dy - 2*YBorder + 1);
+    
+    y = textline[0].y + (textline[0].dy + graphic[0].font[0].ascent) / 2;
+    
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 textline[0].x + XBorder + label_width, y,
+		 textline[0].text, 
+		 strlen(textline[0].text));
+  }
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/TextLineEntry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/TextLineEntry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/textline/TextLineEntry.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+int 
+TextLineEntry (graphic, textline, keyevent)
+Graphic        graphic[];
+TextLine       textline[];
+XKeyEvent     *keyevent;
+{
+
+  int i, status, N;
+  char dummy[3];
+  XComposeStatus compose_status;
+  KeySym keysym;
+
+  status = TRUE;
+  strcpy (textline[0].old_text, textline[0].text);
+  bzero (&textline[0].text[strlen(textline[0].text)], 
+	 1024 - strlen(textline[0].text));
+  i = textline[0].cursor;
+
+
+  N = XLookupString (keyevent, dummy, 1, &keysym, &compose_status);
+  switch (keysym) {
+  case XK_BackSpace:
+  case XK_Delete:
+    if (textline[0].cursor > 0) {
+      bcopy (&textline[0].text[i], &textline[0].text[i - 1], 
+	     strlen(&textline[0].text[i]) + 1);
+      RedrawString (graphic, textline);
+      textline[0].cursor --;
+      DrawCursor (graphic, textline);
+    }
+    break;
+  case XK_Left:
+    RedrawString (graphic, textline);
+    textline[0].cursor --;
+    if (textline[0].cursor < 0)
+      textline[0].cursor = 0;
+    DrawCursor (graphic, textline);
+    break;
+  case XK_Right:
+    RedrawString (graphic, textline);
+    textline[0].cursor ++;
+    if (textline[0].cursor > strlen (textline[0].text))
+      textline[0].cursor = strlen (textline[0].text);
+    DrawCursor (graphic, textline);
+    break;
+  case XK_Return:
+    RedrawString (graphic, textline);
+    textline[0].cursor = -1;
+    status = FALSE;
+    break;
+  case XK_d:
+  case XK_D: 
+    if (dummy[0] == 4) { /* a control-d was typed! */
+      if (textline[0].cursor < strlen(textline[0].text)) {
+	bcopy (&textline[0].text[i + 1], &textline[0].text[i], 
+	       strlen(&textline[0].text[i]));
+	RedrawString (graphic, textline);
+	DrawCursor (graphic, textline);
+      }
+      break;  /* WARNING: this case MUST come before default.
+		 anything between this and default will be executed
+		 if no cntl-d is typed! */
+    }
+  default:
+    if (N != 0) {
+      bcopy (&textline[0].text[i], &textline[0].text[i + 1], 
+	     strlen(&textline[0].text[i]));
+      textline[0].text[i] = dummy[0];
+      RedrawString (graphic, textline);
+      textline[0].cursor ++;
+      DrawCursor (graphic, textline);
+    }
+  }
+  return (status);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "Ximage.h"
+# define XPIX(x,Rx,S) (x*S + Rx)
+# define ZPIX(value,Rz1,Rz2) (value*Rz1 - Rz2)
+# define FRAC(a) ((a) - (int)(a))
+
+void CreateZoom (Layout *layout, Graphic *graphic, double x, double y) {
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    CreateZoom8 (layout, graphic, x, y);
+    break;
+
+  case 16:
+    CreateZoom16 (layout, graphic, x, y);
+    break;
+
+  case 24:
+    CreateZoom24 (layout, graphic, x, y);
+    break;
+
+  case 32:
+    CreateZoom32 (layout, graphic, x, y);
+    break;
+  }    
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom.new
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom.new	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom.new	(revision 22322)
@@ -0,0 +1,112 @@
+# include "Ximage.h"
+# define XPIX(x,Rx,S) (x*S + Rx)
+
+void
+CreateZoom (layout, graphic, x, y)
+Layout layout[];
+Graphic graphic[];
+double x, y;
+{
+
+  int i, j, ii, jj, X, Y;
+  int zvalue;
+  double value, expand, zero, range, zoomscale;
+  double X0, Y0, Rx, Ry, Rz1, Rz2;
+  int expand_int, dx, dy, DX, DY, Npixels, yfac;
+  Visual *visual = CopyFromParent;
+  unsigned long pixel;
+
+
+  /* create a test pattern image to put in layout[0].picture */
+  if (layout[0].matrix.size == 0) {
+    REALLOCATE (layout[0].zoom.data, char, layout[0].zoom.dx*layout[0].zoom.dy);
+    bzero (layout[0].zoom.data, layout[0].zoom.dx*layout[0].zoom.dy);
+  }
+  else {
+    for (i = 0; i < 256; i++) 
+      pixel[i] = layout[0].cmap[i].pixel;
+    white = layout[0].white;
+    
+    zoomscale = MAX (5, layout[0].expand + 5);
+    expand = 1 / zoomscale;
+    expand_out = (int) zoomscale;
+    expand_in  = 1;
+    
+    dx = layout[0].zoom.dx;
+    dy = layout[0].zoom.dy;
+    DX = layout[0].matrix.Naxis[0];
+    DY = layout[0].matrix.Naxis[1];
+    Rx = DX/2 - dx*expand/2.0 - X0;
+    Ry = DY/2 - dy*expand/2.0 - Y0;  
+    
+    /* reassign as much for speed as clarity here */
+    X0 = 0.5*layout[0].matrix.Naxis[0] - x;
+    Y0 = 0.5*layout[0].matrix.Naxis[1] - y;
+    dx = layout[0].picture.dx;
+    dy = layout[0].picture.dy;
+    DX = matrix[0].Naxis[0];
+    DY = matrix[0].Naxis[1];
+    Rx = DX/2 - dx*expand/2.0 - X0;
+    Ry = DY/2 - dy*expand/2.0 - Y0;
+    
+    i_start = MAX (-Rx / expand, 0);
+    j_start = MAX (-Ry / expand, 0);
+    i_end   = MIN ((DX - Rx)/expand, dx - expand_out + 1);
+    j_end   = MIN ((DY - Ry)/expand, dy - expand_out + 1);
+    
+    in_pix = (short *) matrix[0].buffer;
+    out_pix = layout[0].zoom.data;
+    for (j = 0; j < j_start; j++) {
+      for (i = 0; i < dx; i++, out_pix++) {
+	*out_pix = white;
+      }
+    }
+    
+  for (j = j_start; j < j_end; j+= expand_out) {
+    Y = XPIX (j, Ry, expand);
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix = layout[0].zoom.data + (j+jj)*dx;  /* fencepost errors? */
+      for (i = 0; i < i_start; i++, out_pix++) {
+	*out_pix = white;
+      }
+    }
+    out_pix = layout[0].zoom.data + j*dx + i_start;  
+    X = XPIX (i_start, Rx, expand);
+    in_pix2 = in_pix + DX*Y + X;
+    for (i = i_start; i < i_end; i+= expand_out, in_pix2+= expand_in) { 
+      zvalue = 0x003f & *in_pix2;
+      for (ii = 0; ii < expand_out; ii++, out_pix++) {
+	for (jj = 0; jj < expand_out; jj++) {
+	  *(out_pix + jj*dx) = *(pixel + zvalue);
+	}
+      }
+    }
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix = layout[0].zoom.data + (j+jj)*dx + i_end;  /* fencepost errors? */
+      for (i = i_end; i < dx; i++, out_pix++) {
+	*out_pix = white;
+      }
+    }
+  }
+  for (j = j_end; j < dy; j++) {
+    out_pix = layout[0].picture.data + j*dx;
+    for (i = 0; i < dx; i++, out_pix++) {
+      *out_pix = white;
+    }
+  }
+}
+	
+  /*** here, if expand is < 1 (fractional expansion), we sparsely sample the 
+    original image, instead of box averaging - do we like this?  well, 
+    it speeds things up alot! ***/
+
+
+
+
+  layout[0].zoom.pix = XCreateImage (graphic[0].display, visual, graphic[0].depth, ZPixmap, 0, 
+					layout[0].zoom.data, 
+					layout[0].zoom.dx, layout[0].zoom.dy, 8, 0);
+  
+}
+
+ 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom16.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom16.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom16.c	(revision 22322)
@@ -0,0 +1,134 @@
+# include "Ximage.h"
+# define XPIX(x,Rx,S) (x*S + Rx)
+# define ZPIX(value,Rz1,Rz2) (value*Rz1 - Rz2)
+# define FRAC(a) ((a) - (int)(a))
+
+void CreateZoom16 (Layout *layout, Graphic *graphic, double x, double y) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  double expand, zoomscale, Rx, Ry;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2, *data;
+  unsigned char *in_pix,  *in_pix2;
+  unsigned char pixel1[256], pixel2[256];
+  unsigned char pixvalue1, pixvalue2;
+  unsigned long white;
+  unsigned char white1, white2;
+
+  dx = layout[0].zoom.dx;
+  dy = layout[0].zoom.dy;
+
+  if (layout[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (layout[0].zoom.data, char, 2*dy*dx);
+    bzero (layout[0].zoom.data, layout[0].zoom.dx*layout[0].zoom.dy);
+    layout[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       layout[0].zoom.data, layout[0].zoom.dx, layout[0].zoom.dy, 16, 0);
+    return;
+  }
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel1[i] = 0x0000ff & layout[0].cmap[i].pixel;
+    pixel2[i] = 0x0000ff & (layout[0].cmap[i].pixel >> 8);
+  }
+  white = layout[0].white;
+  white1 = 0x0000ff & white;
+  white2 = 0x0000ff & (white >> 8);
+
+  zoomscale = MAX (5, layout[0].expand + 5);
+  expand = 1 / zoomscale;
+  expand_out = (int) zoomscale;
+  expand_in  = 1;
+
+  DX = layout[0].matrix.Naxis[0];
+  DY = layout[0].matrix.Naxis[1];
+  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
+  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
+
+  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  data = out_pix = (unsigned char *) layout[0].zoom.data;
+  in_pix  = (unsigned char *) (layout[0].matrix.buffer) + DX*(int)MAX(Ry,0) + (int)MAX(Rx,0);
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = white;
+    return;
+  } 
+  
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++, out_pix+=2) {
+      out_pix[0] = white1;
+      out_pix[1] = white2;
+    }
+  }
+
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[2*j*dx];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) { 
+      out_pix2 = out_pix + 2*jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2+=2) {
+	out_pix2[0] = white1;
+	out_pix2[1] = white2;
+      }
+    }
+    out_pix += 2*i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=2) {
+	out_pix[0] = pixel1[*in_pix2];
+	out_pix[1] = pixel2[*in_pix2];
+      }
+    }
+    else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 2*expand_out) { 
+	pixvalue1 = pixel1[*in_pix2];
+	pixvalue2 = pixel2[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=2*(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2+=2) {
+	    out_pix2[0] = pixvalue1; 
+	    out_pix2[1] = pixvalue2; 
+	  }
+	}
+      }
+    }
+    out_pix -= 2*dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + 2*jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2+=2) {
+	out_pix2[0] = white1;
+	out_pix2[1] = white2;
+      }
+    }
+  } 
+  
+  out_pix = &data[2*j_end*dx];
+  /**** fill in top area ****/
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix+=2) { 
+      out_pix[0] = white1;
+      out_pix[1] = white2;
+    }
+  }
+
+  layout[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, 16, ZPixmap, 0, 
+					layout[0].zoom.data, layout[0].zoom.dx, layout[0].zoom.dy, 32, 0);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom24.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom24.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom24.c	(revision 22322)
@@ -0,0 +1,167 @@
+# include "Ximage.h"
+# define XPIX(x,Rx,S) (x*S + Rx)
+# define ZPIX(value,Rz1,Rz2) (value*Rz1 - Rz2)
+# define FRAC(a) ((a) - (int)(a))
+
+void CreateZoom24 (Layout *layout, Graphic *graphic, double x, double y) {
+
+  int i, j, ii, jj, extra;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  double expand, zoomscale, Rx, Ry;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2, *data;
+  unsigned char *in_pix,  *in_pix2;
+  unsigned char pixel1[256], pixel2[256], pixel3[256];
+  unsigned char pixvalue1, pixvalue2, pixvalue3;
+  unsigned long white;
+  unsigned char white1, white2, white3;
+
+  dx = layout[0].zoom.dx;
+  dy = layout[0].zoom.dy;
+  extra = 4 - (dx * 3) % 4;
+
+  if (layout[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (layout[0].zoom.data, char, dy*(3*dx+extra));
+    bzero (layout[0].zoom.data, layout[0].zoom.dx*layout[0].zoom.dy);
+    layout[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       layout[0].zoom.data, layout[0].zoom.dx, layout[0].zoom.dy, 32, 0);
+    return;
+  }
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel1[i] = 0x0000ff & layout[0].cmap[i].pixel;
+    pixel2[i] = 0x0000ff & (layout[0].cmap[i].pixel >> 8);
+    pixel3[i] = 0x0000ff & (layout[0].cmap[i].pixel >> 16);
+  }
+  white = layout[0].white;
+  white1 = 0x0000ff & white;
+  white2 = 0x0000ff & (white >> 8);
+  white3 = 0x0000ff & (white >> 16);
+
+  zoomscale = MAX (5, layout[0].expand + 5);
+  expand = 1 / zoomscale;
+  expand_out = (int) zoomscale;
+  expand_in  = 1;
+
+  DX = layout[0].matrix.Naxis[0];
+  DY = layout[0].matrix.Naxis[1];
+  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
+  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
+
+  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  data = out_pix = (unsigned char *) layout[0].zoom.data;
+  in_pix  = (unsigned char *) (layout[0].matrix.buffer) + DX*(int)MAX(Ry,0) + (int)MAX(Rx,0);
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = white;
+    return;
+  } 
+  
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) {
+      out_pix[0] = white1;
+      out_pix[1] = white2;
+      out_pix[2] = white3;
+    }
+    out_pix += extra;
+  }
+
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[j*(3*dx+extra)];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) { 
+      out_pix2 = out_pix + jj*(3*dx + extra);
+      for (i = 0; i < i_start; i++, out_pix2+=3) {
+	out_pix2[0] = white1;
+	out_pix2[1] = white2;
+	out_pix2[2] = white3;
+      }
+    }
+    out_pix += 3*i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=3) {
+	out_pix[0] = pixel1[*in_pix2];
+	out_pix[1] = pixel2[*in_pix2];
+	out_pix[2] = pixel3[*in_pix2];
+      }
+    }
+    else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 3*expand_out) { 
+	pixvalue1 = pixel1[*in_pix2];
+	pixvalue2 = pixel2[*in_pix2];
+	pixvalue3 = pixel3[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=3*(dx-expand_out)+extra) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2+=3) {
+	    out_pix2[0] = pixvalue1; 
+	    out_pix2[1] = pixvalue2; 
+	    out_pix2[2] = pixvalue3; 
+	  }
+	}
+      }
+    }
+    out_pix -= 3*dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*(3*dx+extra);
+      for (i = i_end; i < dx; i++, out_pix2+=3) {
+	out_pix2[0] = white1;
+	out_pix2[1] = white2;
+	out_pix2[2] = white3;
+      }
+    }
+  } 
+  
+  /*
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= (expand_out - (j_end - j_start) % expand_out);
+  */
+
+  out_pix = &data[j_end*(3*dx+extra)];
+  /**** fill in top area ****/
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) { 
+      out_pix[0] = white1;
+      out_pix[1] = white2;
+      out_pix[2] = white3;
+    }
+    out_pix+=extra;
+  }
+
+  /*
+  out_pix = (unsigned char *) layout[0].zoom.data;
+  for (i = 0; i < dx*dy; i++) {
+    out_pix[3*i + 0] = white1;
+    out_pix[3*i + 1] = white2;
+    out_pix[3*i + 2] = white3;
+  }
+  for (j = 0; j < dy/2; j++) {
+    for (i = 0; i < dx; i++) {
+      out_pix[j*(extra + 3*dx) + 3*i + 0] = pixel1[10];
+      out_pix[j*(extra + 3*dx) + 3*i + 1] = pixel2[10];
+      out_pix[j*(extra + 3*dx) + 3*i + 2] = pixel3[10];
+    }
+
+  }
+  */
+  layout[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, 24, ZPixmap, 0, 
+					layout[0].zoom.data, layout[0].zoom.dx, layout[0].zoom.dy, 32, 0);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom32.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom32.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom32.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "Ximage.h"
+# define XPIX(x,Rx,S) (x*S + Rx)
+# define ZPIX(value,Rz1,Rz2) (value*Rz1 - Rz2)
+# define FRAC(a) ((a) - (int)(a))
+
+void CreateZoom32 (Layout *layout, Graphic *graphic, double x, double y) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  double expand, zoomscale, Rx, Ry;
+  int expand_in, expand_out;
+  unsigned int *out_pix, *out_pix2, *data;
+  unsigned char  *in_pix,  *in_pix2;
+  unsigned long pixel[256], pixvalue;
+  unsigned long white;
+
+  if (layout[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (layout[0].zoom.data, char, 4*layout[0].zoom.dx*layout[0].zoom.dy);
+    layout[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       layout[0].zoom.data, layout[0].zoom.dx, layout[0].zoom.dy, 32, 0);
+    return;
+  }
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel[i] = layout[0].cmap[i].pixel;
+  }
+  white = layout[0].white;
+
+  zoomscale = MAX (5, layout[0].expand + 5);
+  expand = 1 / zoomscale;
+  expand_out = (int) zoomscale;
+  expand_in  = 1;
+
+  dx = layout[0].zoom.dx;
+  dy = layout[0].zoom.dy;
+  DX = layout[0].matrix.Naxis[0];
+  DY = layout[0].matrix.Naxis[1];
+  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
+  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
+
+  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned int *) layout[0].zoom.data;
+  data = (unsigned int *) layout[0].zoom.data;
+  in_pix  = (unsigned char *) (layout[0].matrix.buffer) + DX*(int)MAX(Ry,0) + (int)MAX(Rx,0);
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = white;
+    return;
+  } 
+  
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) {
+    out_pix[0] = white;
+  }
+
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	out_pix[0] = white;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	out_pix[0] = pixel[*in_pix2];
+      }
+    }
+    else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixvalue = *(pixel + *in_pix2);
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    out_pix2[0] = pixvalue; 
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	out_pix2[0] = white;
+      }
+    }
+    out_pix += (dx - i_end);
+  } 
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= (expand_out - (j_end - j_start) % expand_out);
+  
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned int *)layout[0].zoom.data < dx*dy); j++, out_pix++) { 
+    out_pix[0] = white;
+  }
+
+  out_pix = (unsigned int *)layout[0].zoom.data;
+  layout[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					layout[0].zoom.data, layout[0].zoom.dx, layout[0].zoom.dy, 32, 0);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom8.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom8.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CreateZoom8.c	(revision 22322)
@@ -0,0 +1,140 @@
+# include "Ximage.h"
+# define XPIX(x,Rx,S) (x*S + Rx)
+# define ZPIX(value,Rz1,Rz2) (value*Rz1 - Rz2)
+# define FRAC(a) ((a) - (int)(a))
+
+void CreateZoom8 (Layout *layout, Graphic *graphic, double x, double y) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  double expand, zoomscale, Rx, Ry;
+  int expand_in, expand_out;
+  char  *out_pix, *out_pix2;
+  char  *in_pix,  *in_pix2;
+  unsigned long pixel[256], pixvalue;
+  unsigned long white;
+
+  if (layout[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (layout[0].zoom.data, char, layout[0].zoom.dx*layout[0].zoom.dy);
+    bzero (layout[0].zoom.data, layout[0].zoom.dx*layout[0].zoom.dy);
+    layout[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       layout[0].zoom.data, layout[0].zoom.dx, layout[0].zoom.dy, 8, 0);
+    return;
+  }
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel[i] = layout[0].cmap[i].pixel;
+  }
+  white = layout[0].white;
+
+  zoomscale = MAX (5, layout[0].expand + 5);
+  expand = 1 / zoomscale;
+  expand_out = (int) zoomscale;
+  expand_in  = 1;
+
+  /*
+  dx = layout[0].zoom.dx;
+  dy = layout[0].zoom.dy;
+  DX = layout[0].matrix.Naxis[0];
+  DY = layout[0].matrix.Naxis[1];
+  Rx = 0.5*(DX - (dx - 1)*expand) - x;
+  Ry = 0.5*(DY - (dy - 1)*expand) - y;
+  data coords of 0,0 point in pic array 
+  X = 0.5*layout[0].matrix.Naxis[0] - expand*((int)(0.5*layout[0].zoom.dx + 0.5) - 0.0) - layout[0].X;
+  Y = 0.5*layout[0].matrix.Naxis[1] - expand*((int)(0.5*layout[0].zoom.dy + 0.5) - 0.0) - layout[0].Y;  
+  i_start = MIN (MAX (-Rx / expand, 0), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, 0), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = layout[0].zoom.data;
+  in_pix  = (char *) (layout[0].matrix.buffer) + DX*(int)MAX(Y,0) + (int)MAX(X,0);
+
+  */
+
+  dx = layout[0].zoom.dx;
+  dy = layout[0].zoom.dy;
+  DX = layout[0].matrix.Naxis[0];
+  DY = layout[0].matrix.Naxis[1];
+  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
+  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
+
+  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (char *) layout[0].zoom.data;
+  in_pix  = (char *) (layout[0].matrix.buffer) + DX*(int)MAX(Ry,0) + (int)MAX(Rx,0);
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = white;
+    return;
+  } 
+  
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) 
+    *out_pix = white;
+  
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = white;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++)
+	*out_pix = *(pixel + *in_pix2); 
+    }
+    else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixvalue = *(pixel + *in_pix2);
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue;
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = white;
+      }
+    }
+    out_pix += (dx - i_end);
+    
+  } 
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - layout[0].zoom.data < dx*dy); j++, out_pix++) { 
+    *out_pix = white;
+  }
+
+  layout[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					layout[0].zoom.data, layout[0].zoom.dx, layout[0].zoom.dy, 8, 0);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CrossHairs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CrossHairs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/CrossHairs.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "Ximage.h"
+
+void CrossHairs (Graphic *graphic, Layout *layout) {
+
+  int x0, x1, x5, x7;
+  int y0, y2, y4, y5;
+  double zoomscale;
+
+  zoomscale = MAX (5, layout[0].expand + 5);
+  x0 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale - 2.5) + layout[0].zoom.x;
+  x5 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale - 0.5) + layout[0].zoom.x;
+  x7 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale + 0.5) + layout[0].zoom.x;
+  x1 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale + 2.5) + layout[0].zoom.x;
+						    	 
+  y4 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale - 2.5) + layout[0].zoom.y;
+  y2 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale - 0.5) + layout[0].zoom.y;
+  y0 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale + 0.5) + layout[0].zoom.y;
+  y5 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale + 2.5) + layout[0].zoom.y;
+
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y0, x5, y0);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5, y0, x5, y5);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y5, x7, y0);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y0, x1, y0);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y2, x5, y2);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5, y2, x5, y4);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y4, x7, y2);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y2, x1, y2);
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y0+1, x5-1, y0+1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5-1, y0+1, x5-1, y5);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y5, x7+1, y0+1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y0+1, x1, y0+1);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y2-1, x5-1, y2-1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5-1, y2-1, x5-1, y4);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y4, x7+1, y2-1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y2-1, x1, y2-1);
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/UpdatePointer.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/UpdatePointer.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/UpdatePointer.c	(revision 22322)
@@ -0,0 +1,58 @@
+# include "Ximage.h"
+# define FRAC(a) ((a) - (int)(a))
+
+int UpdatePointer (Graphic *graphic, Layout *layout, XMotionEvent *event) {
+
+  int textpad;
+  double  x, y, z;
+  char line[100];
+
+  if (MOVE_POINTER && InPicture ((XButtonEvent *)event, &layout[0].picture)) {
+
+    Screen_to_Image (&x, &y, (double)event[0].x, (double)event[0].y, layout);
+
+    if ((x >= 0) && (x < layout[0].matrix.Naxis[0]) && (y >= 0) && (y < layout[0].matrix.Naxis[1])) {
+      /*
+      X = event[0].x - layout[0].picture.x;
+      Y = event[0].y - layout[0].picture.y;
+      pix = MIN (MAX (X + Y*layout[0].picture.dx, 0), layout[0].picture.dx*layout[0].picture.dy - 1);
+      z = layout[0].range * layout[0].picture.data[pix] / layout[0].Npixels + layout[0].zero;
+      */
+      z = -1;
+    }
+    else
+      z = -1;
+    layout[0].x = x;
+    layout[0].y = y;
+    layout[0].z = z;
+    
+    UpdateStatusBox (graphic, layout, x, y, z, 0);
+    XFlush (graphic[0].display);
+      
+    CreateZoom (layout, graphic, x, y);  
+    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	       layout[0].zoom.pix, 0, 0, 
+	       layout[0].zoom.x, layout[0].zoom.y, 
+	       layout[0].zoom.dx, layout[0].zoom.dy);
+    CrossHairs (graphic, layout);
+    XFlush (graphic[0].display);
+    
+  }
+  
+  if (InPicture ((XButtonEvent *)event, &layout[0].cmapbar)) {
+    z = layout[0].zero  + layout[0].range * (event[0].x - layout[0].cmapbar.x) / layout[0].cmapbar.dx;
+    textpad = graphic[0].font[0].ascent;
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+                  layout[0].text_x + 1, layout[0].text_y + 1, ZOOM_X - 2, textpad + PAD1 + 1);
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+    bzero (line, 100);
+    sprintf (line, "%25.2f", z);
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+                 layout[0].text_x + PAD1, layout[0].text_y + textpad + PAD1, 
+		 line, strlen(line));
+    
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/UpdateStatusBox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/UpdateStatusBox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/kii/zoom/UpdateStatusBox.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "Ximage.h"
+
+void UpdateStatusBox (Graphic *graphic, Layout *layout, double x, double y, double z, int mode) {
+
+  int textpad;
+  double ra, dec; 
+  char line[100];
+
+  XY_to_RD (&ra, &dec, x, y, &layout[0].coords);
+
+  textpad = graphic[0].font[0].ascent;
+
+  if (mode) {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+		    layout[0].text_x, layout[0].text_y, ZOOM_X, 6*textpad+7*PAD1);  
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+    XDrawRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+		    layout[0].text_x, layout[0].text_y, ZOOM_X, 6*textpad+7*PAD1);
+  
+    bzero (line, 100);
+    sprintf (line, "(%d x %d) @ %d                                         ", 
+	     layout[0].picture.dx, layout[0].picture.dy, layout[0].expand); 
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		 layout[0].text_x + PAD1, layout[0].text_y + 4*textpad + 4*PAD1, line, 25);
+    
+    bzero (line, 100);
+    sprintf (line, "%-25s", layout[0].file); 
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		 layout[0].text_x + PAD1, layout[0].text_y + 5*textpad + 5*PAD1, line, strlen(line));
+    
+    bzero (line, 100);
+    sprintf (line, "(%s)                                          ", layout[0].buffer_name); 
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		 layout[0].text_x + PAD1, layout[0].text_y + 6*textpad + 6*PAD1, line, 25);
+    
+  } else {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+		    layout[0].text_x + 1, layout[0].text_y + 1, ZOOM_X - 2, 3*textpad+3*PAD1 +1);  
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  }
+  bzero (line, 100);
+  sprintf (line, "%25.3f", z);
+  XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	       layout[0].text_x + PAD1, layout[0].text_y + textpad + PAD1, line, strlen(line));
+  
+  bzero (line, 100);
+  sprintf (line, "%12.1f %12.1f", x, y);
+  XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	       layout[0].text_x + PAD1, layout[0].text_y + 2*textpad + 2*PAD1, line, strlen(line));
+  
+  bzero (line, 100);
+  if (DECIMAL_DEG) {
+    sprintf (line, "%12.6f %12.6f", ra, dec); 
+  } else {
+    hh_hms (line, ra, dec, ':');
+  }
+  XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	       layout[0].text_x + PAD1, layout[0].text_y + 3*textpad + 3*PAD1, line, strlen(line));
+  
+ 
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+include
+lib
+src
+tex
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/Makefile	(revision 22322)
@@ -0,0 +1,76 @@
+default: autocode
+help:
+	@echo "make options: install autocode latex objects"
+
+include ../../Makefile.System
+HOME	=	$(ROOT)/src/libautocode
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	= 	$(HOME)/doc
+DEF	=	$(HOME)/def
+TEX	=	$(HOME)/tex
+INC	=	$(HOME)/include
+ASRC    =       $(SRC)
+AINC    =       $(INC)
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+latex:    $(LIBTEX)
+objects:  $(AOBJS)
+install:  $(DESTINC)/autocode.h $(DESTLIB)/libautocode.a $(DESTLIB)/libautocode.$(DLLTYPE)
+autocode: $(INC)/autocode.h $(LIB)/libautocode.$(ARCH).a $(LIB)/libautocode.$(ARCH).$(DLLTYPE)
+
+# this library is not normally installed: the objects in the
+# file included below are rolled into libdvo 
+include Makefile.Targets
+
+LIBTEX = \
+$(TEX)/coords.tex \
+$(TEX)/average.tex \
+$(TEX)/secfilt.tex \
+$(TEX)/measure.tex \
+$(TEX)/missing.tex \
+$(TEX)/photcode.tex \
+$(TEX)/image.tex 
+
+$(LIB)/libautocode.$(ARCH).a: $(AOBJS)
+$(LIB)/libautocode.$(ARCH).$(DLLTYPE): $(AOBJS)
+
+$(DESTLIB)/libautocode.a:  $(LIB)/libautocode.$(ARCH).a
+$(DESTLIB)/libautocode.$(DLLTYPE): $(LIB)/libautocode.$(ARCH).$(DLLTYPE)
+$(DESTINC)/autocode.h : $(INC)/autocode.h
+
+$(AOBJS): $(DEF)/autocode.c $(DESTINC)/autocode.h
+
+$(INC)/autocode.h: $(AINCS) $(DEF)/autocode.h $(DEF)/common.h
+	cat $(DEF)/common.h >  $(INC)/autocode.h
+	cat $(AINCS)        >> $(INC)/autocode.h
+	@echo "# endif"     >> $(INC)/autocode.h
+	@echo done with autocode.h
+
+$(INC)/%.h: $(DEF)/%.d $(DEF)/autocode.h $(DEF)/common.h
+	@if [ ! -d $(INC) ]; then mkdir -p $(INC); fi
+	./generate $< $(DEF)/autocode.h $@
+
+$(SRC)/%.c: $(DEF)/%.d $(DEF)/autocode.c $(INC)/%.h
+	@if [ ! -d $(SRC) ]; then mkdir -p $(SRC); fi
+	./generate $< $(DEF)/autocode.c $@
+
+$(TEX)/%.tex: $(DEF)/%.d $(DEF)/autocode.tex
+	@if [ ! -d $(TEX) ]; then mkdir -p $(TEX); fi
+	./generate $< $(DEF)/autocode.tex $@
+
+clean:	cleandef
+cleandef:
+	rm -f `find src -name "*.c"`
+	rm -f `find include -name "*.h"`
+# 	this is an unusual target!  it deletes .c and .h files!
+
+.PRECIOUS: $(SRC)/%.c
+.PRECIOUS: $(LIB)/%.$(ARCH).a
+.PRECIOUS: $(LIB)/%.$(ARCH).$(DLLTYPE)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/Makefile.Targets
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/Makefile.Targets	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/Makefile.Targets	(revision 22322)
@@ -0,0 +1,108 @@
+
+AOBJS = \
+$(ASRC)/coords.$(ARCH).o \
+$(ASRC)/average.$(ARCH).o \
+$(ASRC)/average-loneos.$(ARCH).o \
+$(ASRC)/average-elixir.$(ARCH).o \
+$(ASRC)/average-panstarrs-dev-0.$(ARCH).o \
+$(ASRC)/average-panstarrs-dev-1.$(ARCH).o \
+$(ASRC)/average-ps1-dev-1.$(ARCH).o \
+$(ASRC)/average-ps1-dev-2.$(ARCH).o \
+$(ASRC)/secfilt.$(ARCH).o \
+$(ASRC)/secfilt-loneos.$(ARCH).o \
+$(ASRC)/secfilt-elixir.$(ARCH).o \
+$(ASRC)/secfilt-panstarrs-dev-0.$(ARCH).o \
+$(ASRC)/secfilt-panstarrs-dev-1.$(ARCH).o \
+$(ASRC)/secfilt-ps1-dev-1.$(ARCH).o \
+$(ASRC)/secfilt-ps1-dev-2.$(ARCH).o \
+$(ASRC)/measure.$(ARCH).o \
+$(ASRC)/measure-loneos.$(ARCH).o \
+$(ASRC)/measure-elixir.$(ARCH).o \
+$(ASRC)/measure-panstarrs-dev-0.$(ARCH).o \
+$(ASRC)/measure-panstarrs-dev-1.$(ARCH).o \
+$(ASRC)/measure-ps1-dev-1.$(ARCH).o \
+$(ASRC)/measure-ps1-dev-2.$(ARCH).o \
+$(ASRC)/missing.$(ARCH).o \
+$(ASRC)/photcode.$(ARCH).o \
+$(ASRC)/photcode-elixir.$(ARCH).o \
+$(ASRC)/photcode-ps1-dev-1.$(ARCH).o \
+$(ASRC)/photcode-ps1-dev-2.$(ARCH).o \
+$(ASRC)/photcode-ps1-dev-3.$(ARCH).o \
+$(ASRC)/image.$(ARCH).o \
+$(ASRC)/image-loneos.$(ARCH).o \
+$(ASRC)/image-elixir.$(ARCH).o \
+$(ASRC)/image-panstarrs-dev-0.$(ARCH).o \
+$(ASRC)/image-panstarrs-dev-1.$(ARCH).o \
+$(ASRC)/image-ps1-dev-1.$(ARCH).o \
+$(ASRC)/image-ps1-dev-2.$(ARCH).o \
+$(ASRC)/image-ps1-dev-3.$(ARCH).o \
+$(ASRC)/regimage.$(ARCH).o \
+$(ASRC)/detreg.$(ARCH).o \
+$(ASRC)/photreg.$(ARCH).o \
+$(ASRC)/photreg-old.$(ARCH).o \
+$(ASRC)/ps1_dev_0.$(ARCH).o \
+$(ASRC)/ps1_dev_1.$(ARCH).o \
+$(ASRC)/smpdata.$(ARCH).o \
+$(ASRC)/getstar-ps1-dev-0.$(ARCH).o \
+$(ASRC)/getstar-ps1-dev-1.$(ARCH).o \
+$(ASRC)/getstar-ps1-dev-2.$(ARCH).o \
+$(ASRC)/spectrum.$(ARCH).o \
+$(ASRC)/spectrum-ascii.$(ARCH).o \
+$(ASRC)/Stars.$(ARCH).o \
+$(ASRC)/GSCRegion.$(ARCH).o \
+$(ASRC)/AddstarClientOptions.$(ARCH).o \
+$(ASRC)/SkyRegion.$(ARCH).o
+
+AINCS = \
+$(AINC)/coords.h \
+$(AINC)/average.h \
+$(AINC)/average-loneos.h \
+$(AINC)/average-elixir.h \
+$(AINC)/average-panstarrs-dev-0.h \
+$(AINC)/average-panstarrs-dev-1.h \
+$(AINC)/average-ps1-dev-1.h \
+$(AINC)/average-ps1-dev-2.h \
+$(AINC)/secfilt.h \
+$(AINC)/secfilt-loneos.h \
+$(AINC)/secfilt-elixir.h \
+$(AINC)/secfilt-panstarrs-dev-0.h \
+$(AINC)/secfilt-panstarrs-dev-1.h \
+$(AINC)/secfilt-ps1-dev-1.h \
+$(AINC)/secfilt-ps1-dev-2.h \
+$(AINC)/measure.h \
+$(AINC)/measure-loneos.h \
+$(AINC)/measure-elixir.h \
+$(AINC)/measure-panstarrs-dev-0.h \
+$(AINC)/measure-panstarrs-dev-1.h \
+$(AINC)/measure-ps1-dev-1.h \
+$(AINC)/measure-ps1-dev-2.h \
+$(AINC)/missing.h \
+$(AINC)/photcode.h \
+$(AINC)/photcode-elixir.h \
+$(AINC)/photcode-ps1-dev-1.h \
+$(AINC)/photcode-ps1-dev-2.h \
+$(AINC)/photcode-ps1-dev-3.h \
+$(AINC)/image.h \
+$(AINC)/image-loneos.h \
+$(AINC)/image-elixir.h \
+$(AINC)/image-panstarrs-dev-0.h \
+$(AINC)/image-panstarrs-dev-1.h \
+$(AINC)/image-ps1-dev-1.h \
+$(AINC)/image-ps1-dev-2.h \
+$(AINC)/image-ps1-dev-3.h \
+$(AINC)/regimage.h \
+$(AINC)/detreg.h \
+$(AINC)/photreg.h \
+$(AINC)/photreg-old.h \
+$(AINC)/ps1_dev_0.h \
+$(AINC)/ps1_dev_1.h \
+$(AINC)/getstar-ps1-dev-0.h \
+$(AINC)/getstar-ps1-dev-1.h \
+$(AINC)/getstar-ps1-dev-2.h \
+$(AINC)/smpdata.h \
+$(AINC)/spectrum.h \
+$(AINC)/spectrum-ascii.h \
+$(AINC)/Stars.h \
+$(AINC)/GSCRegion.h \
+$(AINC)/AddstarClientOptions.h \
+$(AINC)/SkyRegion.h
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/AddstarClientOptions.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/AddstarClientOptions.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/AddstarClientOptions.d	(revision 22322)
@@ -0,0 +1,23 @@
+STRUCT  AddstarClientOptions
+EXTNAME OPTIONS
+TYPE    BINTABLE
+SIZE    80
+
+FIELD     Nsigma,           NSIGMA,               double,         match radius in terms of astrometric error 
+FIELD     radius,           RADIUS,		  double,         match radius in arcsec (default)
+FIELD  	  mode,             MODE,                 int,            data source mode
+FIELD  	  filelist,         FILELIST,             int,            if true file is a list of input files
+FIELD  	  existing_regions, EXISTING_REGIONS,     int,            use only existing regions
+FIELD  	  only_match,       ONLY_MATCH,           int,            only update matched stars
+FIELD  	  skip_missed,      SKIP_MISSED,          int,            .
+FIELD  	  replace,          REPLACE,              int,            .
+FIELD  	  closest,          CLOSEST,              int,            .
+FIELD  	  nosort,           NOSORT,               int,            .
+FIELD  	  update,           UPDATE,               int,            .
+FIELD  	  only_images,      ONLY_IMAGES,          int,            .
+FIELD  	  calibrate,        CALIBRATE,            int,            .
+FIELD  	  quality_airmass,  HQ_AIRMASS,           int,            use high-quality airmass?
+FIELD  	  mosaic,           MOSAIC,               int,            does this image require a mosaic coordinate system?
+FIELD  	  photcode,         PHOTCODE,             int,     	  photocode of input data
+FIELD  	  timeref,          TIMEREF,              e_time,  	  time/date of input data (REFLIST only?)
+FIELD  	  imageID,          IMAGE_ID,             unsigned int,   reference to image
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/GSCRegion.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/GSCRegion.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/GSCRegion.d	(revision 22322)
@@ -0,0 +1,8 @@
+STRUCT  GSCRegion
+EXTNAME OPTIONS
+TYPE    BINTABLE
+SIZE    288
+
+FIELD   filename,   FILENAME,   char[256],   catalog filename
+FIELD   DEC,        DEC,        double[2],   Dec boundaries
+FIELD   RA,         DEC,        double[2],   RA boundaries
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/SkyRegion.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/SkyRegion.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/SkyRegion.d	(revision 22322)
@@ -0,0 +1,17 @@
+STRUCT  SkyRegion
+EXTNAME SKY_REGION
+TYPE    BINTABLE
+SIZE    56
+
+FIELD   Rmin,	   R_MIN,	   float, 
+FIELD   Rmax,	   R_MAX,	   float, 
+FIELD   Dmin,	   D_MIN,	   float, 
+FIELD   Dmax,	   D_MAX,	   float, 
+FIELD   childS,	   CHILD_S,	   int,           sequence number in full table of first child
+FIELD   childE,	   CHILD_E,	   int,           sequence number in full table of last child + 1
+FIELD   parent,	   PARENT,	   int,           sequence number in full table of parent
+FIELD   index,     INDEX,          int,           sequence number in full table of this entry
+FIELD   depth,	   DEPTH,	   char,          depth of this entry
+FIELD   child,	   CHILD,	   char,          does this entry have children?
+FIELD   table,	   TABLE,	   char,          does this entry have a table?
+FIELD   name,      NAME,           char[21],      name / filename
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/Stars.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/Stars.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/Stars.d	(revision 22322)
@@ -0,0 +1,59 @@
+STRUCT  Stars
+EXTNAME STARS
+TYPE    BINTABLE
+SIZE    248
+
+FIELD     X,                X,          double,    x coordinate on image,	     pixels
+FIELD     Y,                Y,          double,    y coordinate on image,	     pixels
+FIELD     dX,               dX,         double,    x coordinate error,	             pixels
+FIELD     dY,               dY,         double,    y coordinate error,  	     pixels
+FIELD     R,                R,          double,    ra coordinate on sky,	     decimal degrees
+FIELD     D,                D,          double,    dec coordinate on sky,	     decimal degrees
+FIELD     dR,               dR,         double,    ra error,			     arcsec
+FIELD     dD,               dD,         double,    dec error,			     arcsec
+FIELD     uR,               U_RA,       double,    proper motion in RA,		     milliarcsec/year
+FIELD     uD,               U_DEC,      double,    proper motion in DEC,	     milliarcsec/year
+FIELD     duR,              U_RA_ERR,   double,    p-m error in RA,		     milliarcsec/year
+FIELD     duD,              U_DEC_ERR,  double,    p-m error in DEC,		     milliarcsec/year
+FIELD     P,                PAR,        double,    parallax,			     milliarcsec
+FIELD     dP,               PAR_ERR,    double,    parallax error,		     milliarcsec
+FIELD     M,                M,          double,    instrumental mag
+FIELD     dM,               DM,         double,    error on mag
+FIELD     dMcal,            DMCAL,      double,    systematic error on mag
+FIELD     sky,              SKY,        double,    local sky counts
+FIELD     dsky,             dSKY,       double,    local sky error counts
+FIELD     fx,               FX,         double,    object FWHM x-dir,		     pixels?
+FIELD     fy,               FY,         double,    object FWHM y-dir,		     pixels?
+FIELD     df,               DF,         double,    object position angle,	     degrees
+# FIELD     Mgal,             MGAL,       double,    alternative (galaxy) magnitude
+FIELD     Map,              MAP,        double,    alternative (aperture) magnitude
+FIELD     Mpeak,            MPEAK,      double,    alternative (peak) magnitude
+FIELD     detID,            ID,         int,       detection identifier
+FIELD     imageID,          IMAGE_ID,   int,       image identifier
+FIELD     found,            FOUND,      int,       found in database catalog?
+FIELD     t,                T,          e_time,    date/time of exposure (UNIX)
+FIELD     dt,               EXPTIME,    float,     exposure time,                    2.5*log(exptime)
+FIELD     psfChisq,         PSF_CHISQ,  float
+FIELD     crNsigma,         CR_NSIGMA,  float
+FIELD     extNsigma,        EXT_NSIGMA, float
+FIELD     psfQual,          PSF_QUAL,   float
+FIELD     Mcal,             MCAL,       float,     image cal mag,	             mag
+FIELD     airmass,          AIRMASS,    float,     (airmass - 1),		     airmass
+FIELD     az,     	    AZ,         float,     azimuth
+FIELD     code,             CODE,       short
+FIELD     nFrames,          N_FRAMES,   short
+FIELD     flags,            FLAGS,      short
+FIELD     dophot,           DOPHOT,     char,      dophot type code
+FIELD     dummy,            DUMMY,      char
+
+# XXX I'm going to need azimuth (or load from image header?)
+
+# double:   24 * 8 : 192
+# int/float:10 * 4 :  40
+# short:     2 * 2 :   4
+# char:      4 * 1 :   4
+
+# this structure is only used internally and for interprocess communication (addstar)
+# dR, uR, etc should be better defined at the pole...
+# define down the types to floats where reasonable (all but R,D)?
+# R,D should be in ICRS, J2000, epoch 2000 : precess as needed, apply p-m as needed
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/autocode.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/autocode.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/autocode.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include "autocode.h"
+
+int gfits_convert_$STRUCT ($STRUCT *data, int size, int nitems) {
+
+  int i;
+  unsigned char *byte, tmp;
+
+  if (size != $SIZE) { 
+    fprintf (stderr, "ERROR: mismatch in data types $STRUCT: %d vs %d\n",
+	     size, $SIZE);
+    exit (1);
+  }
+
+  /* provide initial values to avoid compiler warnings for non-BYTE_SWAP arch */
+  i = tmp = 0;
+  byte = NULL;
+
+# ifdef BYTE_SWAP
+  byte = (unsigned char *) data;
+  for (i = 0; i < nitems; i++, byte += $SIZE) {
+    /** BYTE SWAP **/
+  }
+# endif  
+
+  return (TRUE);
+} 
+
+/*** add test of EXTNAME and header-defined columns? ***/
+/* return internal structure representation */
+$STRUCT *gfits_table_get_$STRUCT (FTable *ftable, int *Ndata, int *swapped) {
+
+  $STRUCT *data;
+
+  *Ndata = ftable[0].header[0].Naxis[1];
+  data = ($STRUCT *) ftable[0].buffer;
+  if ((swapped == NULL) || (*swapped == FALSE)) {
+    gfits_convert_$STRUCT (data, sizeof ($STRUCT), *Ndata);
+    gfits_table_scale_data (ftable);
+    if (swapped != NULL) *swapped = TRUE;
+  }
+  return (data);
+}
+
+int gfits_table_set_$STRUCT (FTable *ftable, $STRUCT *data, int Ndata) {
+
+  Header *header;
+
+  header = ftable[0].header;
+
+  /* create table header */
+  gfits_create_table_header (header, "$TYPE", "$EXTNAME");
+
+  /* define table layout */
+  /** TABLE DEFINITION **/
+
+  /* create table */
+  gfits_create_table (header, ftable);
+
+  /* add data values */
+  gfits_table_scale_data (ftable);
+  gfits_convert_$STRUCT (data, sizeof ($STRUCT), Ndata);
+  gfits_add_rows (ftable, (char *) data, Ndata, sizeof ($STRUCT));
+
+  return (TRUE);
+}
+
+int gfits_table_mkheader_$STRUCT (Header *header) {
+
+  /* create table header */
+  gfits_create_table_header (header, "$TYPE", "$EXTNAME");
+
+  /* define table layout */
+  /** TABLE DEFINITION **/
+
+  return (TRUE);
+}
+
+int Send_$STRUCT (int device, $STRUCT *data, int Ndata, int copy) {
+
+  int Nbytes;
+  $STRUCT *tmpdata;
+
+  Nbytes = Ndata * sizeof ($STRUCT);
+
+  if (copy) {
+    ALLOCATE (tmpdata, $STRUCT, Ndata);
+    memcpy (tmpdata, data, Nbytes);
+  } else {
+    tmpdata = data;
+  }
+
+  gfits_convert_$STRUCT (tmpdata, sizeof ($STRUCT), Ndata);
+
+  SendCommand (device, 16, "NVALUE: %6d", Ndata);
+  SendCommand (device, 16, "NBYTES: %6d", Nbytes);
+  write (device, tmpdata, Nbytes);
+  
+  /* perform handshaking? */
+
+  return (TRUE);
+}
+
+int Recv_$STRUCT (int device, $STRUCT **data, int *Ndata) {
+
+  int ndata;
+  IOBuffer message;
+  $STRUCT *tmpdata;
+
+  ExpectCommand (device, 16, 1.0, &message);
+  sscanf (message.buffer, "%*s %d", &ndata);
+  FreeIOBuffer (&message);
+  
+  /* what is reasonable for timeout? */
+  ExpectMessage (device, 1.0, &message);
+  
+  tmpdata = ($STRUCT *) message.buffer;
+  gfits_convert_$STRUCT (tmpdata, sizeof ($STRUCT), ndata);
+
+  /* double-check data length? */
+  /* perform handshaking? */
+
+  *Ndata = ndata;
+  *data = tmpdata;
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/autocode.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/autocode.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/autocode.h	(revision 22322)
@@ -0,0 +1,9 @@
+
+/** STRUCT DEFINITION **/
+
+$STRUCT *gfits_table_get_$STRUCT (FTable *table, int *Ndata, int *swapped);
+int      gfits_table_set_$STRUCT (FTable *ftable, $STRUCT *data, int Ndata);
+int      gfits_table_mkheader_$STRUCT (Header *header);
+int      gfits_convert_$STRUCT ($STRUCT *data, int size, int nitems);
+int Send_$STRUCT (int device, $STRUCT *data, int Ndata, int copy);
+int Recv_$STRUCT (int device, $STRUCT **data, int *Ndata);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/autocode.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/autocode.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/autocode.tex	(revision 22322)
@@ -0,0 +1,13 @@
+
+\begin{table}[h]
+\begin{center}
+\caption{\label{$STRUCT} $DESCRIPTION (EXTNAME: {\tt $STRUCT})}
+\begin{tabular}{|l|l|l|l|}
+\hline
+name        & type           & description                 & units \\
+\hline
+%%% LATEX TABLE DEFINITION 
+\hline
+\end{tabular}
+\end{center}
+\end{table}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-elixir.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-elixir.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-elixir.d	(revision 22322)
@@ -0,0 +1,19 @@
+STRUCT  Average_Elixir
+EXTNAME DVO_AVERAGE_ELIXIR
+TYPE    BINTABLE
+SIZE    32
+
+# elements of data structure / FITS table
+
+FIELD R,              RA,         float,            RA,                	       	  decimal degrees 
+FIELD D,              DEC,        float,            DEC,               	       	  decimal degrees 
+FIELD M,              MAG,        short,            primary mag,       	       	  millimag
+FIELD Nm,             NMEAS,      unsigned short,   number of measures
+FIELD Nn,             NMISS,      unsigned short,   number of missings
+FIELD Xp,             SIGMA_POS,  short, 	    position scatter,   	  1/100 arcsec
+FIELD Xm,             CHISQ_MAG,  short, 	    chisq for primary mag,        [100*log(value)]  ?
+FIELD code,           code,       unsigned short,   ID code (star; ghost; etc)
+FIELD offset,         offset,     int,     	    offset to first measurement
+FIELD missing,        missing,    int,     	    offset to first missing obs
+FIELD dM,             MAG_ERR,    short,            error on primary mag,         millimag
+FIELD Xg,             Xg,         short,            best chisq value
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-loneos.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-loneos.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-loneos.d	(revision 22322)
@@ -0,0 +1,17 @@
+STRUCT  Average_Loneos
+EXTNAME DVO_AVERAGE_LONEOS
+TYPE    BINTABLE
+SIZE    28
+
+# elements of data structure / FITS table
+
+FIELD R,              RA,         float,            RA,                	       	  decimal degrees 
+FIELD D,              DEC,        float,            DEC,               	       	  decimal degrees 
+FIELD M,              MAG,        short,            primary mag,       	       	  millimag
+FIELD Nm,             NMEAS,      unsigned short,   number of measures
+FIELD Nn,             NMISS,      unsigned short,   number of missings
+FIELD Xp,             SIGMA_POS,  short, 	    position scatter,   	  1/100 arcsec
+FIELD Xm,             CHISQ_MAG,  short, 	    chisq for primary mag,        [1000*value]  ?
+FIELD code,           code,       unsigned short,   ID code (star; ghost; etc)
+FIELD offset,         offset,     int,     	    offset to first measurement
+FIELD missing,        missing,    int,     	    offset to first missing obs
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-panstarrs-dev-0.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-panstarrs-dev-0.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-panstarrs-dev-0.d	(revision 22322)
@@ -0,0 +1,40 @@
+STRUCT       Average_Panstarrs_DEV_0
+EXTNAME      DVO_AVERAGE_PANSTARRS_DEV_0
+TYPE         BINTABLE
+SIZE         72
+DESCRIPTION  DVO Average Object Table
+
+# elements of data structure / FITS table
+
+FIELD R,              RA,         double,           RA,                	       	  decimal degrees 
+FIELD D,              DEC,        double,           DEC,               	       	  decimal degrees 
+FIELD dR,             RA_ERR,     float,            RA error                      arcsec
+FIELD dD,             DEC_ERR,    float,            DEC error                     arcsec
+
+FIELD uR,             U_RA,       float,            RA*cos(D) proper-motion,      arcsec/year
+FIELD uD,             U_DEC,      float,            DEC proper-motion,            arcsec/year
+FIELD duR,            V_RA_ERR,   float,            RA*cos(D) p-m error,          arcsec/year
+FIELD duD,            V_DEC_ERR,  float,            DEC p-m error,                arcsec/year
+FIELD P,              PAR,        float,            parallax,			  arcsec
+FIELD dP,             PAR_ERR,    float,            parallax error,               arcsec
+
+FIELD Xp,             SIGMA_POS,  short, 	    position scatter,   	  1/100 arcsec
+FIELD Nm,             NMEAS,      unsigned short,   number of measures
+FIELD Nn,             NMISS,      unsigned short,   number of missings
+FIELD code,           code,       unsigned short,   ID code (star; ghost; etc)
+FIELD offset,         offset,     int,     	    offset to first measurement
+FIELD missing,        missing,    int,     	    offset to first missing obs
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints for backwards compatibility?
+FIELD objID,          OBJ_ID,     unsigned int,   ID upper bytes
+FIELD catID,          CAT_ID,     unsigned int,   ID lower bytes
+
+# this structure should only be used for internal representations
+# the average-FORMAT structures should be used for external representations
+# note that the average magnitudes are stored in the 'secfilt' table (change this name??)
+# the index for the secfilt table is just Nsecfilt times the index for the average table.
+
+# the DVO object IDs are generated internally and are not equivalent to the PSPS object IDs
+# probably need to add position chisq
+
+# XXX include the number of measurements used to determine the positional information?
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-panstarrs-dev-1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-panstarrs-dev-1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-panstarrs-dev-1.d	(revision 22322)
@@ -0,0 +1,40 @@
+STRUCT       Average_Panstarrs_DEV_1
+EXTNAME      DVO_AVERAGE_PANSTARRS_DEV_1
+TYPE         BINTABLE
+SIZE         72
+DESCRIPTION  DVO Average Object Table
+
+# elements of data structure / FITS table
+
+FIELD R,              RA,         double,           RA,                	       	  decimal degrees 
+FIELD D,              DEC,        double,           DEC,               	       	  decimal degrees 
+FIELD dR,             RA_ERR,     float,            RA error                      arcsec
+FIELD dD,             DEC_ERR,    float,            DEC error                     arcsec
+
+FIELD uR,             U_RA,       float,            RA*cos(D) proper-motion,      arcsec/year
+FIELD uD,             U_DEC,      float,            DEC proper-motion,            arcsec/year
+FIELD duR,            V_RA_ERR,   float,            RA*cos(D) p-m error,          arcsec/year
+FIELD duD,            V_DEC_ERR,  float,            DEC p-m error,                arcsec/year
+FIELD P,              PAR,        float,            parallax,			  arcsec
+FIELD dP,             PAR_ERR,    float,            parallax error,               arcsec
+
+FIELD Xp,             SIGMA_POS,  short, 	    position scatter,   	  1/100 arcsec
+FIELD Nm,             NMEAS,      unsigned short,   number of measures
+FIELD Nn,             NMISS,      unsigned short,   number of missings
+FIELD code,           code,       unsigned short,   ID code (star; ghost; etc)
+FIELD offset,         offset,     int,     	    offset to first measurement
+FIELD missing,        missing,    int,     	    offset to first missing obs
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints for backwards compatibility?
+FIELD objID,          OBJ_ID,     unsigned int,   ID upper bytes
+FIELD catID,          CAT_ID,     unsigned int,   ID lower bytes
+
+# this structure should only be used for internal representations
+# the average-FORMAT structures should be used for external representations
+# note that the average magnitudes are stored in the 'secfilt' table (change this name??)
+# the index for the secfilt table is just Nsecfilt times the index for the average table.
+
+# the DVO object IDs are generated internally and are not equivalent to the PSPS object IDs
+# probably need to add position chisq
+
+# XXX include the number of measurements used to determine the positional information?
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-ps1-dev-1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-ps1-dev-1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-ps1-dev-1.d	(revision 22322)
@@ -0,0 +1,40 @@
+STRUCT       Average_PS1_DEV_1
+EXTNAME      DVO_AVERAGE_PS1_DEV_1
+TYPE         BINTABLE
+SIZE         72
+DESCRIPTION  DVO Average Object Table
+
+# elements of data structure / FITS table
+
+FIELD R,              RA,         double,           RA,                	       	  decimal degrees 
+FIELD D,              DEC,        double,           DEC,               	       	  decimal degrees 
+FIELD dR,             RA_ERR,     float,            RA error                      arcsec
+FIELD dD,             DEC_ERR,    float,            DEC error                     arcsec
+
+FIELD uR,             U_RA,       float,            RA*cos(D) proper-motion,      arcsec/year
+FIELD uD,             U_DEC,      float,            DEC proper-motion,            arcsec/year
+FIELD duR,            V_RA_ERR,   float,            RA*cos(D) p-m error,          arcsec/year
+FIELD duD,            V_DEC_ERR,  float,            DEC p-m error,                arcsec/year
+FIELD P,              PAR,        float,            parallax,			  arcsec
+FIELD dP,             PAR_ERR,    float,            parallax error,               arcsec
+
+FIELD Xp,             SIGMA_POS,  short, 	    position scatter,   	  1/100 arcsec
+FIELD Nm,             NMEAS,      unsigned short,   number of measures
+FIELD Nn,             NMISS,      unsigned short,   number of missings
+FIELD code,           code,       unsigned short,   ID code (star; ghost; etc)
+FIELD offset,         offset,     int,     	    offset to first measurement
+FIELD missing,        missing,    int,     	    offset to first missing obs
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints for backwards compatibility?
+FIELD objID,          OBJ_ID,     unsigned int,   ID upper bytes
+FIELD catID,          CAT_ID,     unsigned int,   ID lower bytes
+
+# this structure should only be used for internal representations
+# the average-FORMAT structures should be used for external representations
+# note that the average magnitudes are stored in the 'secfilt' table (change this name??)
+# the index for the secfilt table is just Nsecfilt times the index for the average table.
+
+# the DVO object IDs are generated internally and are not equivalent to the PSPS object IDs
+# probably need to add position chisq
+
+# XXX include the number of measurements used to determine the positional information?
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-ps1-dev-2.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-ps1-dev-2.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average-ps1-dev-2.d	(revision 22322)
@@ -0,0 +1,52 @@
+STRUCT       Average_PS1_DEV_2
+EXTNAME      DVO_AVERAGE_PS1_DEV_2
+TYPE         BINTABLE
+SIZE         80
+DESCRIPTION  DVO Average Object Table
+
+# elements of data structure / FITS table
+
+FIELD R,              RA,         double,           RA,                	       	  decimal degrees 
+FIELD D,              DEC,        double,           DEC,               	       	  decimal degrees 
+FIELD dR,             RA_ERR,     float,            RA error                      arcsec
+FIELD dD,             DEC_ERR,    float,            DEC error                     arcsec
+
+FIELD uR,             U_RA,       float,            RA*cos(D) proper-motion,      arcsec/year
+FIELD uD,             U_DEC,      float,            DEC proper-motion,            arcsec/year
+FIELD duR,            V_RA_ERR,   float,            RA*cos(D) p-m error,          arcsec/year
+FIELD duD,            V_DEC_ERR,  float,            DEC p-m error,                arcsec/year
+FIELD P,              PAR,        float,            parallax,			  arcsec
+FIELD dP,             PAR_ERR,    float,            parallax error,               arcsec
+
+FIELD Xp,             SIGMA_POS,   short, 	    position scatter,   	  1/100 arcsec
+FIELD Nmeasure,       NMEASURE,    unsigned short,  number of psf measurements
+FIELD Nmissing,       NMISSING,    unsigned short,  number of missings
+FIELD Nextend,        NEXTEND,     unsigned short,  number of extended measurements
+FIELD measureOffset,  OFF_MEASURE, int,     	    offset to first psf measurement
+FIELD missingOffset,  OFF_MISSING, int,     	    offset to first missing obs
+FIELD extendOffset,   OFF_EXTEND,  int,     	    offset to first extended measurement
+
+FIELD code,           code,       unsigned short,   ID code (star; ghost; etc)
+FIELD dummy,          DUMMY,      char[2],          padding
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints
+# for C89 compatibility.  The objID is constructed based on the
+# position of first instatiation.  this is actually quite expensive
+# because we need to include the uniqueness test to construct this,
+# which requires a select for each new object.  Therefore, I will use
+# a table based ID (table ID + object ID), and we will have to
+# re-number the object IDs if we change the table density, OR treat
+# all subdivisions as entries which are from a foreign table.
+
+FIELD objID,          OBJ_ID,    unsigned int,   unique ID for object in table
+FIELD catID,          CAT_ID,    unsigned int,   unique ID for table in which object was first realized
+
+# this structure should only be used for internal representations
+# the average-FORMAT structures should be used for external representations
+# note that the average magnitudes are stored in the 'secfilt' table (change this name??)
+# the index for the secfilt table is just Nsecfilt times the index for the average table.
+
+# the DVO object IDs are generated internally and are not equivalent to the PSPS object IDs
+# probably need to add position chisq
+
+# XXX include the number of measurements used to determine the positional information?
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/average.d	(revision 22322)
@@ -0,0 +1,52 @@
+STRUCT       Average
+EXTNAME      DVO_AVERAGE
+TYPE         BINTABLE
+SIZE         80
+DESCRIPTION  DVO Average Object Table
+
+# elements of data structure / FITS table
+
+FIELD R,              RA,         double,           RA,                	       	  decimal degrees 
+FIELD D,              DEC,        double,           DEC,               	       	  decimal degrees 
+FIELD dR,             RA_ERR,     float,            RA error                      arcsec
+FIELD dD,             DEC_ERR,    float,            DEC error                     arcsec
+
+FIELD uR,             U_RA,       float,            RA*cos(D) proper-motion,      arcsec/year
+FIELD uD,             U_DEC,      float,            DEC proper-motion,            arcsec/year
+FIELD duR,            V_RA_ERR,   float,            RA*cos(D) p-m error,          arcsec/year
+FIELD duD,            V_DEC_ERR,  float,            DEC p-m error,                arcsec/year
+FIELD P,              PAR,        float,            parallax,			  arcsec
+FIELD dP,             PAR_ERR,    float,            parallax error,               arcsec
+
+FIELD Xp,             SIGMA_POS,   short, 	    position scatter,   	  1/100 arcsec
+FIELD Nmeasure,       NMEASURE,    unsigned short,  number of psf measurements
+FIELD Nmissing,       NMISSING,    unsigned short,  number of missings
+FIELD Nextend,        NEXTEND,     unsigned short,  number of extended measurements
+FIELD measureOffset,  OFF_MEASURE, int,     	    offset to first psf measurement
+FIELD missingOffset,  OFF_MISSING, int,     	    offset to first missing obs
+FIELD extendOffset,   OFF_EXTEND,  int,     	    offset to first extended measurement
+
+FIELD code,           code,       unsigned short,   ID code (star; ghost; etc)
+FIELD dummy,          DUMMY,      char[2],          padding
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints
+# for C89 compatibility.  The objID is constructed based on the
+# position of first instatiation.  this is actually quite expensive
+# because we need to include the uniqueness test to construct this,
+# which requires a select for each new object.  Therefore, I will use
+# a table based ID (table ID + object ID), and we will have to
+# re-number the object IDs if we change the table density, OR treat
+# all subdivisions as entries which are from a foreign table.
+
+FIELD objID,          OBJ_ID,    unsigned int,   unique ID for object in table
+FIELD catID,          CAT_ID,    unsigned int,   unique ID for table in which object was first realized
+
+# this structure should only be used for internal representations
+# the average-FORMAT structures should be used for external representations
+# note that the average magnitudes are stored in the 'secfilt' table (change this name??)
+# the index for the secfilt table is just Nsecfilt times the index for the average table.
+
+# the DVO object IDs are generated internally and are not equivalent to the PSPS object IDs
+# probably need to add position chisq
+
+# XXX include the number of measurements used to determine the positional information?
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/common.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/common.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/common.h	(revision 22322)
@@ -0,0 +1,56 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+# ifndef AUTOCODE_H
+# define AUTOCODE_H
+/* matched endif is added by Makefile */
+
+# ifndef TRUE
+# define TRUE (1)
+# define FALSE (0)
+# endif
+
+# define SWAP_BYTE(X) \
+  tmp = byte[X+0]; byte[X+0] = byte[X+1]; byte[X+1] = tmp;
+# define SWAP_WORD(X) \
+  tmp = byte[X+0]; byte[X+0] = byte[X+3]; byte[X+3] = tmp; \
+  tmp = byte[X+1]; byte[X+1] = byte[X+2]; byte[X+2] = tmp;
+# define SWAP_DBLE(X) \
+  tmp = byte[X+0]; byte[X+0] = byte[X+7]; byte[X+7] = tmp; \
+  tmp = byte[X+1]; byte[X+1] = byte[X+6]; byte[X+6] = tmp; \
+  tmp = byte[X+2]; byte[X+2] = byte[X+5]; byte[X+5] = tmp; \
+  tmp = byte[X+3]; byte[X+3] = byte[X+4]; byte[X+4] = tmp;
+
+# ifdef linux
+# define BYTE_SWAP
+# endif
+
+# ifdef darwin_x86
+# define BYTE_SWAP
+# endif
+
+# ifdef sid
+# define BYTE_SWAP
+# endif
+
+# ifdef dec
+# define BYTE_SWAP
+# endif
+
+# define e_time unsigned int
+# define e_void long long
+# define rawshort short
+
+/*** rawshort is used to handle the broken pre-autocode photreg tables
+     fix the tables and remove this
+***/
+
+/*** this file uses data types which must have fixed sizes regardless 
+     of the platform.  It originally used the basic C primitives: 
+       float, double, int, short int, unsigned long int, etc.
+     this breaks under 64 bit (and probably on other systems).
+     I should define internal data types which should be set by the 
+     use of # define statements if needed.  I will cheat for now and use
+     the time_t to replace unsigned long int in this file 
+***/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/coords.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/coords.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/coords.d	(revision 22322)
@@ -0,0 +1,20 @@
+STRUCT       Coords
+EXTNAME      COORDS
+TYPE         BINTABLE
+SIZE         120
+DESCRIPTION  DVO Coordinate Transformation Table
+
+# elements of the Coords structure
+FIELD  crval1,           CRVAL1,               double,   	 coordinate at reference pixel
+FIELD  crval2,           CRVAL2,               double,  	 coordinate at reference pixel
+FIELD  crpix1,           CRPIX1,               float,   	 coordinate of reference pixel
+FIELD  crpix2,           CRPIX2,               float,   	 coordinate of reference pixel
+FIELD  cdelt1,           CDELT1,               float,   	 degrees per pixel
+FIELD  cdelt2,           CDELT2,               float,    	 degrees per pixel
+FIELD  pc1_1,            PC1_1,                float,    	 rotation matrix
+FIELD  pc1_2,            PC1_2,                float,    	 rotation matrix
+FIELD  pc2_1,            PC2_1,                float,    	 rotation matrix
+FIELD  pc2_2,            PC2_2,                float,    	 rotation matrix
+FIELD  polyterms,        POLYTERMS,            float[7][2],	 higher order warping terms
+FIELD  ctype,            CTYPE,                char[15],      coordinate type
+FIELD  Npolyterms,       NPOLYTERMS,           char,     	 order of polynomial
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/detreg.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/detreg.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/detreg.d	(revision 22322)
@@ -0,0 +1,21 @@
+STRUCT  DetReg
+EXTNAME DETREND_DATABASE
+TYPE    BINTABLE
+SIZE    416
+
+# elements of data structure / FITS table
+
+FIELD tstart,         START_TIME, e_time,    start time of measurement, seconds since 1970 Jan 01 UT
+FIELD tstop,          STOP_TIME,  e_time,    stop time of measurement,  seconds since Jan 1, 1970 UT
+FIELD treg,           REG_TIME,   e_time,    time of registration,      seconds since Jan 1, 1970 UT
+FIELD exptime,        EXPTIME,    float,     exposure time,             seconds
+FIELD type,           IMAGETYP,   int,       detrend type number
+FIELD filter,         FILTER,     int,       filter number
+FIELD ccd,            CCDNUM,     int,       ccd number
+FIELD Nentry,         VERSION,    int,       image version number
+FIELD Norder,         ORDER,      int,       selection order
+FIELD mode,           MODE,       char,      image mode
+FIELD altpath,        ALTPATH,    char,      available on alt db paths, true for data on alt db paths
+FIELD dummy,          RESERVED,   char[58],  space for additions,       for future expansion
+FIELD label,          LABEL,      char[64],  data label
+FIELD filename,       PATH,       char[256], filename in db
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/extend-ps1-dev-2.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/extend-ps1-dev-2.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/extend-ps1-dev-2.d	(revision 22322)
@@ -0,0 +1,58 @@
+STRUCT       Extend_PS1_DEV_2
+EXTNAME      DVO_EXTEND_PS1_DEV_2
+TYPE         BINTABLE
+SIZE         160
+DESCRIPTION  DVO Detection Extended Source Parameters Table 
+
+FIELD dR,             D_RA,         	  float,          RA offset,                	  arcsec
+FIELD dD,             D_DEC,        	  float,          DEC offset,               	  arcsec
+FIELD M,              MAG,          	  float,          catalog mag,       	       	  mag
+FIELD dM,             MAG_ERR,      	  float,          mag error,                    mag
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints for backwards compatibility?
+# I don't actually have these indexed, but this lets us match back to the measurement
+FIELD detID,          DET_ID,       	  unsigned int,   detection ID
+FIELD imageID,        IMAGE_ID,     	  unsigned int,   reference to image
+FIELD photcode,       PHOTCODE,     	  unsigned short, photcode
+FIELD padding,        PADDING,     	  unsigned short, padding
+
+FIELD FWx,            EXT_MAJOR,    	  float,          object fwhm major axis
+FIELD FWy,            EXT_MINOR,    	  float,          object fwhm minor axis
+FIELD theta,          EXT_THETA,    	  float,          angle wrt ccd X dir
+
+FIELD petroMag,       PETRO_MAG,    	  float,          petrosian mag
+FIELD petroMagErr,    PETRO_MAG_ERR,	  float,          petrosian mag
+FIELD petroRad,       PETRO_RADIUS,       float,          petrosian mag
+FIELD petroRadErr,    PETRO_RADIUS_ERR,   float,          petrosian mag
+
+FIELD kronMag,        KRON_MAG,    	  float,          kron mag
+FIELD kronMagErr,     KRON_MAG_ERR,	  float,          kron mag
+FIELD kronRad,        KRON_RADIUS,     	  float,          kron mag
+FIELD kronRadErr,     KRON_RADIUS_ERR, 	  float,          kron mag
+
+FIELD isophotMag,     ISOPHOT_MAG,    	  float,          isophot mag
+FIELD isophotMagErr,  ISOPHOT_MAG_ERR,	  float,          isophot mag
+FIELD isophotRad,     ISOPHOT_RADIUS,     float,          isophot mag
+FIELD isophotRadErr,  ISOPHOT_RADIUS_ERR, float,          isophot mag
+
+FIELD fluxR0,         FLUX_VAL_R_00,      float,          flux in annulus 0
+FIELD fluxR1,         FLUX_VAL_R_01,      float,          flux in annulus 1
+FIELD fluxR2,         FLUX_VAL_R_02,      float,          flux in annulus 2
+FIELD fluxR3,         FLUX_VAL_R_03,      float,          flux in annulus 3
+FIELD fluxR4,         FLUX_VAL_R_04,      float,          flux in annulus 4
+FIELD fluxR5,         FLUX_VAL_R_05,      float,          flux in annulus 5
+
+FIELD fluxR0err,      FLUX_ERR_R_00,      float,          flux error in annulus 0
+FIELD fluxR1err,      FLUX_ERR_R_01,      float,          flux error in annulus 1
+FIELD fluxR2err,      FLUX_ERR_R_02,      float,          flux error in annulus 2
+FIELD fluxR3err,      FLUX_ERR_R_03,      float,          flux error in annulus 3
+FIELD fluxR4err,      FLUX_ERR_R_04,      float,          flux error in annulus 4
+FIELD fluxR5err,      FLUX_ERR_R_05,      float,          flux error in annulus 5
+
+FIELD fluxR0var,      FLUX_VAR_R_00,      float,          flux var in annulus 0
+FIELD fluxR1var,      FLUX_VAR_R_01,      float,          flux var in annulus 1
+FIELD fluxR2var,      FLUX_VAR_R_02,      float,          flux var in annulus 2
+FIELD fluxR3var,      FLUX_VAR_R_03,      float,          flux var in annulus 3
+FIELD fluxR4var,      FLUX_VAR_R_04,      float,          flux var in annulus 4
+FIELD fluxR5var,      FLUX_VAR_R_05,      float,          flux var in annulus 5
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/extmodel-ps1-dev-2.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/extmodel-ps1-dev-2.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/extmodel-ps1-dev-2.d	(revision 22322)
@@ -0,0 +1,22 @@
+STRUCT       ExtModel_PS1_DEV_2
+EXTNAME      DVO_EXTMODEL_PS1_DEV_2
+TYPE         BINTABLE
+SIZE         104
+DESCRIPTION  DVO Detection Extended Model Fits
+
+FIELD dR,             D_RA,         	  float,          RA offset,                	  arcsec
+FIELD dD,             D_DEC,        	  float,          DEC offset,               	  arcsec
+FIELD M,              MAG,          	  float,          catalog mag,       	       	  mag
+FIELD dM,             MAG_ERR,      	  float,          mag error,                    mag
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints for backwards compatibility?
+# I don't actually have these indexed, but this lets us match back to the measurement
+FIELD detID,          DET_ID,       	  unsigned int,   detection ID
+FIELD imageID,        IMAGE_ID,     	  unsigned int,   reference to image
+FIELD photcode,       PHOTCODE,     	  unsigned short, photcode
+
+FIELD FWx,            EXT_MAJOR,    	  float,          object fwhm major axis
+FIELD FWy,            EXT_MINOR,    	  float,          object fwhm minor axis
+FIELD theta,          EXT_THETA,    	  float,          angle wrt ccd X dir
+
+## XXX other model parameter values here
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/getstar-ps1-dev-0.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/getstar-ps1-dev-0.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/getstar-ps1-dev-0.d	(revision 22322)
@@ -0,0 +1,17 @@
+STRUCT       Getstar_PS1_DEV_0
+EXTNAME      GETSTAR_PS1_DEV_0
+TYPE         BINTABLE
+SIZE         32
+DESCRIPTION  Getstar output file
+
+# elements of data structure / FITS table
+
+# average R,D are epoch & equinox J2000.0
+FIELD R,              RA,         double,           RA,                	       	  decimal degrees 
+FIELD D,              DEC,        double,           DEC,               	       	  decimal degrees 
+FIELD mag,            MAG,        float,            average magnitude in requested photcode
+FIELD c1,             MAG_C1,     float,            average magnitude in color term 1
+FIELD c2,             MAG_C2,     float,            average magnitude in color term 2
+
+FIELD photcode,       PHOTCODE,   unsigned short,   photcode for this mag
+FIELD code,           CODE,       unsigned short,   ID code (star; ghost; etc)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/getstar-ps1-dev-1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/getstar-ps1-dev-1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/getstar-ps1-dev-1.d	(revision 22322)
@@ -0,0 +1,22 @@
+STRUCT       Getstar_PS1_DEV_1
+EXTNAME      GETSTAR_PS1_DEV_1
+TYPE         BINTABLE
+SIZE         48
+DESCRIPTION  Getstar output file
+
+# elements of data structure / FITS table
+
+# average R,D are epoch & equinox J2000.0
+FIELD R,              RA,         double,           RA,                	       	  decimal degrees 
+FIELD D,              DEC,        double,           DEC,               	       	  decimal degrees 
+FIELD uR,             U_RA,       float,            RA*cos(D) proper-motion,      arcsec/year
+FIELD uD,             U_DEC,      float,            DEC proper-motion,            arcsec/year
+FIELD P,              PAR,        float,            parallax,			  arcsec
+
+FIELD mag,            MAG,        float,            average magnitude in requested photcode
+FIELD c1,             MAG_C1,     float,            average magnitude in color term 1
+FIELD c2,             MAG_C2,     float,            average magnitude in color term 2
+
+FIELD photcode,       PHOTCODE,   unsigned short,   photcode for this mag
+FIELD code,           CODE,       unsigned short,   ID code (star; ghost; etc)
+FIELD dummy,          DUMMY,      char[4],          padding
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/getstar-ps1-dev-2.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/getstar-ps1-dev-2.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/getstar-ps1-dev-2.d	(revision 22322)
@@ -0,0 +1,28 @@
+STRUCT       Getstar_PS1_DEV_2
+EXTNAME      GETSTAR_PS1_DEV_2
+TYPE         BINTABLE
+SIZE         64
+DESCRIPTION  Getstar output file
+
+# elements of data structure / FITS table
+
+# average R,D are epoch & equinox J2000.0
+FIELD R,              RA,         double,           RA,                	       	  decimal degrees 
+FIELD D,              DEC,        double,           DEC,               	       	  decimal degrees 
+FIELD dR,             RA_ERR,     float,            RA error                      arcsec
+FIELD dD,             DEC_ERR,    float,            DEC error                     arcsec
+
+FIELD uR,             U_RA,       float,            RA*cos(D) proper-motion,      arcsec/year
+FIELD uD,             U_DEC,      float,            DEC proper-motion,            arcsec/year
+FIELD duR,            V_RA_ERR,   float,            RA*cos(D) p-m error,          arcsec/year
+FIELD duD,            V_DEC_ERR,  float,            DEC p-m error,                arcsec/year
+
+FIELD P,              PAR,        float,            parallax,			  arcsec
+FIELD dP,             PAR_ERR,    float,            parallax error,               arcsec
+
+FIELD mag,            MAG,        float,            average magnitude in requested photcode
+FIELD c1,             MAG_C1,     float,            average magnitude in color term 1
+FIELD c2,             MAG_C2,     float,            average magnitude in color term 2
+
+FIELD photcode,       PHOTCODE,   unsigned short,   photcode for this mag
+FIELD code,           CODE,       unsigned short,   ID code (star; ghost; etc)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ascii.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ascii.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ascii.d	(revision 22322)
@@ -0,0 +1,18 @@
+# name of structure type
+STRUCT  ImageASCII
+EXTNAME IMAGE
+TYPE    TABLE
+SIZE    328
+
+# elements of data structure / FITS table
+FIELD obstime,    START_TIME, char[20],    start time of measurement, yyyy/mm/dd,hh:mm:ss
+FIELD filter,  	  FILTER,     char[10],    filter and camera name    
+FIELD ZP,      	  ZP_OBS,     float[8.4],  measured zero point,       mag
+FIELD dZP,     	  ZP_ERR,     float[7.4],  error on zero point,       mag
+FIELD ra,      	  RA,         float[11.6], RA (J2000),                dec. degrees
+FIELD dec,     	  DEC,        float[11.6], DEC (J2000),               dec. degrees
+FIELD airmass, 	  C_AIRMASS,  float[7.3],  airmass coeff,             mag per airmass 
+FIELD sky,     	  SKY,        float[7.1],  median sky flux,           counts
+FIELD Nstar,   	  NSTAR,      int[6],      Number of stars in image,  stars
+
+## XXX is this used?
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-elixir.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-elixir.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-elixir.d	(revision 22322)
@@ -0,0 +1,59 @@
+STRUCT  Image_Elixir
+EXTNAME DVO_IMAGE_ELIXIR
+TYPE 	BINTABLE
+SIZE 	240
+
+# elements of the image structure
+
+SUBSTRUCT coords,           COORDS,               Coords,        astrometric data
+SUBFIELD  crval1,           CRVAL1,               double,   	 coordinate at reference pixel
+SUBFIELD  crval2,           CRVAL2,               double,  	 coordinate at reference pixel
+SUBFIELD  crpix1,           CRPIX1,               float,   	 coordinate of reference pixel
+SUBFIELD  crpix2,           CRPIX2,               float,   	 coordinate of reference pixel
+SUBFIELD  cdelt1,           CDELT1,               float,   	 degrees per pixel
+SUBFIELD  cdelt2,           CDELT2,               float,    	 degrees per pixel
+SUBFIELD  pc1_1,            PC1_1,                float,    	 rotation matrix
+SUBFIELD  pc1_2,            PC1_2,                float,    	 rotation matrix
+SUBFIELD  pc2_1,            PC2_1,                float,    	 rotation matrix
+SUBFIELD  pc2_2,            PC2_2,                float,    	 rotation matrix
+SUBFIELD  polyterms,        POLYTERMS,            float[7][2],	 higher order warping terms
+SUBFIELD  ctype,            CTYPE,                char[15],      coordinate type
+SUBFIELD  Npolyterms,       NPOLYTERMS,           char,     	 order of polynomial
+
+FIELD 	  tzero,            TZERO,                e_time,         readout time (row 0)
+FIELD 	  nstar,            NSTAR,                unsigned int,   number of stars on image
+FIELD 	  secz,             SECZ,                 short,      	  airmass,                   milliairmass
+FIELD 	  NX,               NX,                   short,      	  image width
+FIELD 	  NY,               NY,                   short,      	  image height
+FIELD 	  apmifit,          APMIFIT,              short,      	  aperture correction,       millimag
+FIELD 	  dapmifit,         DAPMIFIT,             short,      	  apmifit error,             millimag
+FIELD 	  source,           SOURCE,               short,      	  identifier for CCD,
+FIELD 	  Mcal,             MCAL,                 short,      	  calibration mag,           millimag
+FIELD 	  dMcal,            DMCAL,                short,      	  error on Mcal,             millimag
+FIELD 	  Xm,               XM,                   short,      	  image chisq,               10*log(value)
+FIELD 	  name,             NAME,                 char[32],       name of original image 
+FIELD 	  detection_limit,  DETECTION_LIMIT,      unsigned char,  detection limit,           10*mag
+FIELD 	  saturation_limit, SATURATION_LIMIT,     unsigned char,  saturation limit,          10*mag
+FIELD 	  cerror,           CERROR,               unsigned char,  astrometric error,         50*arcsec
+FIELD 	  fwhm_x,           FWHM_X,               unsigned char,  PSF x width,               25*arcsec
+FIELD 	  fwhm_y,           FWHM_Y,               unsigned char,  PSF y width,               25*arcsec
+FIELD 	  trate,            TRATE,                unsigned char,  scan rate,                 100 usec/pixel
+FIELD 	  exptime,          EXPTIME,              float,          exposure time,             seconds
+FIELD 	  code,             CODE,                 char,           image quality flag
+FIELD 	  ccdnum,           CCDNUM,               unsigned char,  CCD ID number
+FIELD 	  dummy,            DUMMY,                char[20],       unused
+FIELD 	  order,            ORDER,                short,      	  Mrel 2D polynomical order 
+FIELD 	  Mx,               MX,                   short,      	  Mrel polyterm
+FIELD 	  My,               MY,                   short,      	  Mrel polyterm
+FIELD 	  Mxx,              MXX,                  short,      	  Mrel polyterm
+FIELD 	  Mxy,              MXY,                  short,      	  Mrel polyterm
+FIELD 	  Myy,              MYY,                  short,      	  Mrel polyterm
+FIELD 	  Mxxx,             MXXX,                 short,      	  Mrel polyterm
+FIELD 	  Mxxy,             MXXY,                 short,      	  Mrel polyterm
+FIELD 	  Mxyy,             MXYY,                 short,      	  Mrel polyterm
+FIELD 	  Myyy,             MYYY,                 short,      	  Mrel polyterm
+FIELD 	  Mxxxx,            MXXXX,                short,      	  Mrel polyterm
+FIELD 	  Mxxxy,            MXXXY,                short,      	  Mrel polyterm
+FIELD 	  Mxxyy,            MXXYY,                short,      	  Mrel polyterm
+FIELD 	  Mxyyy,            MXYYY,                short,      	  Mrel polyterm
+FIELD 	  Myyyy,            MYYYY,                short,      	  Mrel polyterm
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-loneos.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-loneos.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-loneos.d	(revision 22322)
@@ -0,0 +1,59 @@
+STRUCT  Image_Loneos
+EXTNAME DVO_IMAGE_LONEOS
+TYPE 	BINTABLE
+SIZE 	240
+
+# elements of the image structure
+
+SUBSTRUCT coords,           COORDS,               Coords,        astrometric data
+SUBFIELD  crval1,           CRVAL1,               double,   	 coordinate at reference pixel
+SUBFIELD  crval2,           CRVAL2,               double,  	 coordinate at reference pixel
+SUBFIELD  crpix1,           CRPIX1,               float,   	 coordinate of reference pixel
+SUBFIELD  crpix2,           CRPIX2,               float,   	 coordinate of reference pixel
+SUBFIELD  cdelt1,           CDELT1,               float,   	 degrees per pixel
+SUBFIELD  cdelt2,           CDELT2,               float,    	 degrees per pixel
+SUBFIELD  pc1_1,            PC1_1,                float,    	 rotation matrix
+SUBFIELD  pc1_2,            PC1_2,                float,    	 rotation matrix
+SUBFIELD  pc2_1,            PC2_1,                float,    	 rotation matrix
+SUBFIELD  pc2_2,            PC2_2,                float,    	 rotation matrix
+SUBFIELD  polyterms,        POLYTERMS,            float[7][2],	 higher order warping terms
+SUBFIELD  ctype,            CTYPE,                char[15],      coordinate type
+SUBFIELD  Npolyterms,       NPOLYTERMS,           char,     	 order of polynomial
+
+FIELD 	  tzero,            TZERO,                e_time,         readout time (row 0)
+FIELD 	  nstar,            NSTAR,                unsigned int,   number of stars on image
+FIELD 	  secz,             SECZ,                 short,      	  airmass,                   milliairmass
+FIELD 	  NX,               NX,                   short,      	  image width
+FIELD 	  NY,               NY,                   short,      	  image height
+FIELD 	  apmifit,          APMIFIT,              short,      	  aperture correction,       millimag
+FIELD 	  dapmifit,         DAPMIFIT,             short,      	  apmifit error,             millimag
+FIELD 	  source,           SOURCE,               short,      	  identifier for CCD,
+FIELD 	  Mcal,             MCAL,                 short,      	  calibration mag,           millimag
+FIELD 	  dMcal,            DMCAL,                short,      	  error on Mcal,             millimag
+FIELD 	  Xm,               XM,                   short,      	  image chisq,               10*log(value)
+FIELD 	  name,             NAME,                 char[32],       name of original image 
+FIELD 	  detection_limit,  DETECTION_LIMIT,      unsigned char,  detection limit,           10*mag
+FIELD 	  saturation_limit, SATURATION_LIMIT,     unsigned char,  saturation limit,          10*mag
+FIELD 	  cerror,           CERROR,               unsigned char,  astrometric error,         50*arcsec
+FIELD 	  fwhm_x,           FWHM_X,               unsigned char,  PSF x width,               25*arcsec
+FIELD 	  fwhm_y,           FWHM_Y,               unsigned char,  PSF y width,               25*arcsec
+FIELD 	  trate,            TRATE,                unsigned char,  scan rate,                 100 usec/pixel
+FIELD 	  exptime,          EXPTIME,              float,          exposure time,             seconds
+FIELD 	  code,             CODE,                 char,           image quality flag
+FIELD 	  ccdnum,           CCDNUM,               unsigned char,  CCD ID number
+FIELD 	  dummy,            DUMMY,                char[20],       unused
+FIELD 	  order,            ORDER,                short,      	  Mrel 2D polynomical order 
+FIELD 	  Mx,               MX,                   short,      	  Mrel polyterm
+FIELD 	  My,               MY,                   short,      	  Mrel polyterm
+FIELD 	  Mxx,              MXX,                  short,      	  Mrel polyterm
+FIELD 	  Mxy,              MXY,                  short,      	  Mrel polyterm
+FIELD 	  Myy,              MYY,                  short,      	  Mrel polyterm
+FIELD 	  Mxxx,             MXXX,                 short,      	  Mrel polyterm
+FIELD 	  Mxxy,             MXXY,                 short,      	  Mrel polyterm
+FIELD 	  Mxyy,             MXYY,                 short,      	  Mrel polyterm
+FIELD 	  Myyy,             MYYY,                 short,      	  Mrel polyterm
+FIELD 	  Mxxxx,            MXXXX,                short,      	  Mrel polyterm
+FIELD 	  Mxxxy,            MXXXY,                short,      	  Mrel polyterm
+FIELD 	  Mxxyy,            MXXYY,                short,      	  Mrel polyterm
+FIELD 	  Mxyyy,            MXYYY,                short,      	  Mrel polyterm
+FIELD 	  Myyyy,            MYYYY,                short,      	  Mrel polyterm
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-panstarrs-dev-0.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-panstarrs-dev-0.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-panstarrs-dev-0.d	(revision 22322)
@@ -0,0 +1,70 @@
+STRUCT       Image_Panstarrs_DEV_0
+EXTNAME      DVO_IMAGE_PANSTARRS_DEV_0
+TYPE         BINTABLE
+SIZE         256
+DESCRIPTION  DVO Image Table 
+
+# elements of the image structure
+
+SUBSTRUCT coords,           COORDS,               Coords,        astrometric data
+SUBFIELD  crval1,           CRVAL1,               double,   	 coordinate at reference pixel
+SUBFIELD  crval2,           CRVAL2,               double,  	 coordinate at reference pixel
+SUBFIELD  crpix1,           CRPIX1,               float,   	 coordinate of reference pixel
+SUBFIELD  crpix2,           CRPIX2,               float,   	 coordinate of reference pixel
+SUBFIELD  cdelt1,           CDELT1,               float,   	 degrees per pixel
+SUBFIELD  cdelt2,           CDELT2,               float,    	 degrees per pixel
+SUBFIELD  pc1_1,            PC1_1,                float,    	 rotation matrix
+SUBFIELD  pc1_2,            PC1_2,                float,    	 rotation matrix
+SUBFIELD  pc2_1,            PC2_1,                float,    	 rotation matrix
+SUBFIELD  pc2_2,            PC2_2,                float,    	 rotation matrix
+SUBFIELD  polyterms,        POLYTERMS,            float[7][2],	 higher order warping terms
+SUBFIELD  ctype,            CTYPE,                char[15],      coordinate type
+SUBFIELD  Npolyterms,       NPOLYTERMS,           char,     	 order of polynomial
+# 120 bytes
+
+FIELD 	  tzero,            TZERO,                e_time,         readout time (row 0)
+FIELD 	  nstar,            NSTAR,                unsigned int,   number of stars on image
+FIELD 	  secz,             SECZ,                 float,      	  airmass,                   mag
+FIELD 	  NX,               NX,                   short,      	  image width
+FIELD 	  NY,               NY,                   short,      	  image height
+FIELD 	  apmifit,          APMIFIT,              float,      	  aperture correction,       mag
+FIELD 	  dapmifit,         DAPMIFIT,             float,      	  apmifit error,             mag
+FIELD 	  Mcal,             MCAL,                 float,      	  calibration mag,           mag
+FIELD 	  dMcal,            DMCAL,                float,      	  error on Mcal,             mag
+FIELD 	  Xm,               XM,                   short,      	  image chisq,               10*log(value)
+FIELD 	  photcode,         PHOTCODE,             short,      	  identifier for CCD,
+FIELD 	  exptime,          EXPTIME,              float,          exposure time,             seconds
+FIELD     sidtime,          ST,			  float,          sidereal time of exposure
+FIELD     latitude,         LAT,		  float,          observatory latitude,      degrees
+# 40 bytes
+
+FIELD 	  name,             NAME,                 char[32],       name of original image 
+FIELD 	  detection_limit,  DETECTION_LIMIT,      unsigned char,  detection limit,           10*mag
+FIELD 	  saturation_limit, SATURATION_LIMIT,     unsigned char,  saturation limit,          10*mag
+FIELD 	  cerror,           CERROR,               unsigned char,  astrometric error,         50*arcsec
+FIELD 	  fwhm_x,           FWHM_X,               unsigned char,  PSF x width,               25*arcsec
+FIELD 	  fwhm_y,           FWHM_Y,               unsigned char,  PSF y width,               25*arcsec
+FIELD 	  trate,            TRATE,                unsigned char,  scan rate,                 100 usec/pixel
+FIELD 	  code,             CODE,                 char,           image quality flag
+FIELD 	  ccdnum,           CCDNUM,               unsigned char,  CCD ID number
+FIELD 	  imageID_hi,       IMAGE_ID_HI,          unsigned int,   ID upper bytes
+FIELD 	  imageID_lo,       IMAGE_ID_LO,          unsigned int,   ID lower bytes
+# 48 bytes 
+
+FIELD 	  dummy,            DUMMY,                char[10],       unused
+FIELD 	  order,            ORDER,                short,      	  Mrel 2D polynomical order 
+FIELD 	  Mx,               MX,                   short,      	  Mrel polyterm
+FIELD 	  My,               MY,                   short,      	  Mrel polyterm
+FIELD 	  Mxx,              MXX,                  short,      	  Mrel polyterm
+FIELD 	  Mxy,              MXY,                  short,      	  Mrel polyterm
+FIELD 	  Myy,              MYY,                  short,      	  Mrel polyterm
+FIELD 	  Mxxx,             MXXX,                 short,      	  Mrel polyterm
+FIELD 	  Mxxy,             MXXY,                 short,      	  Mrel polyterm
+FIELD 	  Mxyy,             MXYY,                 short,      	  Mrel polyterm
+FIELD 	  Myyy,             MYYY,                 short,      	  Mrel polyterm
+FIELD 	  Mxxxx,            MXXXX,                short,      	  Mrel polyterm
+FIELD 	  Mxxxy,            MXXXY,                short,      	  Mrel polyterm
+FIELD 	  Mxxyy,            MXXYY,                short,      	  Mrel polyterm
+FIELD 	  Mxyyy,            MXYYY,                short,      	  Mrel polyterm
+FIELD 	  Myyyy,            MYYYY,                short,      	  Mrel polyterm
+# 40 bytes
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-panstarrs-dev-1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-panstarrs-dev-1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-panstarrs-dev-1.d	(revision 22322)
@@ -0,0 +1,70 @@
+STRUCT       Image_Panstarrs_DEV_1
+EXTNAME      DVO_IMAGE_PANSTARRS_DEV_1
+TYPE         BINTABLE
+SIZE         288
+DESCRIPTION  DVO Image Table 
+
+# elements of the image structure
+
+SUBSTRUCT coords,           COORDS,               Coords,        astrometric data
+SUBFIELD  crval1,           CRVAL1,               double,   	 coordinate at reference pixel
+SUBFIELD  crval2,           CRVAL2,               double,  	 coordinate at reference pixel
+SUBFIELD  crpix1,           CRPIX1,               float,   	 coordinate of reference pixel
+SUBFIELD  crpix2,           CRPIX2,               float,   	 coordinate of reference pixel
+SUBFIELD  cdelt1,           CDELT1,               float,   	 degrees per pixel
+SUBFIELD  cdelt2,           CDELT2,               float,    	 degrees per pixel
+SUBFIELD  pc1_1,            PC1_1,                float,    	 rotation matrix
+SUBFIELD  pc1_2,            PC1_2,                float,    	 rotation matrix
+SUBFIELD  pc2_1,            PC2_1,                float,    	 rotation matrix
+SUBFIELD  pc2_2,            PC2_2,                float,    	 rotation matrix
+SUBFIELD  polyterms,        POLYTERMS,            float[7][2],	 higher order warping terms
+SUBFIELD  ctype,            CTYPE,                char[15],      coordinate type
+SUBFIELD  Npolyterms,       NPOLYTERMS,           char,     	 order of polynomial
+# 120 bytes
+
+FIELD 	  tzero,            TZERO,                e_time,         readout time (row 0)
+FIELD 	  nstar,            NSTAR,                unsigned int,   number of stars on image
+FIELD 	  secz,             SECZ,                 float,      	  airmass,                   mag
+FIELD 	  NX,               NX,                   short,      	  image width
+FIELD 	  NY,               NY,                   short,      	  image height
+FIELD 	  apmifit,          APMIFIT,              float,      	  aperture correction,       mag
+FIELD 	  dapmifit,         DAPMIFIT,             float,      	  apmifit error,             mag
+FIELD 	  Mcal,             MCAL,                 float,      	  calibration mag,           mag
+FIELD 	  dMcal,            DMCAL,                float,      	  error on Mcal,             mag
+FIELD 	  Xm,               XM,                   short,      	  image chisq,               10*log(value)
+FIELD 	  photcode,         PHOTCODE,             short,      	  identifier for CCD,
+FIELD 	  exptime,          EXPTIME,              float,          exposure time,             seconds
+FIELD     sidtime,          ST,			  float,          sidereal time of exposure
+FIELD     latitude,         LAT,		  float,          observatory latitude,      degrees
+# 40 bytes
+
+FIELD 	  name,             NAME,                 char[64],       name of original image 
+FIELD 	  detection_limit,  DETECTION_LIMIT,      unsigned char,  detection limit,           10*mag
+FIELD 	  saturation_limit, SATURATION_LIMIT,     unsigned char,  saturation limit,          10*mag
+FIELD 	  cerror,           CERROR,               unsigned char,  astrometric error,         50*arcsec
+FIELD 	  fwhm_x,           FWHM_X,               unsigned char,  PSF x width,               25*arcsec
+FIELD 	  fwhm_y,           FWHM_Y,               unsigned char,  PSF y width,               25*arcsec
+FIELD 	  trate,            TRATE,                unsigned char,  scan rate,                 100 usec/pixel
+FIELD 	  code,             CODE,                 char,           image quality flag
+FIELD 	  ccdnum,           CCDNUM,               unsigned char,  CCD ID number
+FIELD 	  imageID_hi,       IMAGE_ID_HI,          unsigned int,   ID upper bytes
+FIELD 	  imageID_lo,       IMAGE_ID_LO,          unsigned int,   ID lower bytes
+# 48 bytes 
+
+FIELD 	  dummy,            DUMMY,                char[10],       unused
+FIELD 	  order,            ORDER,                short,      	  Mrel 2D polynomical order 
+FIELD 	  Mx,               MX,                   short,      	  Mrel polyterm
+FIELD 	  My,               MY,                   short,      	  Mrel polyterm
+FIELD 	  Mxx,              MXX,                  short,      	  Mrel polyterm
+FIELD 	  Mxy,              MXY,                  short,      	  Mrel polyterm
+FIELD 	  Myy,              MYY,                  short,      	  Mrel polyterm
+FIELD 	  Mxxx,             MXXX,                 short,      	  Mrel polyterm
+FIELD 	  Mxxy,             MXXY,                 short,      	  Mrel polyterm
+FIELD 	  Mxyy,             MXYY,                 short,      	  Mrel polyterm
+FIELD 	  Myyy,             MYYY,                 short,      	  Mrel polyterm
+FIELD 	  Mxxxx,            MXXXX,                short,      	  Mrel polyterm
+FIELD 	  Mxxxy,            MXXXY,                short,      	  Mrel polyterm
+FIELD 	  Mxxyy,            MXXYY,                short,      	  Mrel polyterm
+FIELD 	  Mxyyy,            MXYYY,                short,      	  Mrel polyterm
+FIELD 	  Myyyy,            MYYYY,                short,      	  Mrel polyterm
+# 40 bytes
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ps1-dev-1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ps1-dev-1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ps1-dev-1.d	(revision 22322)
@@ -0,0 +1,69 @@
+STRUCT       Image_PS1_DEV_1
+EXTNAME      DVO_IMAGE_PS1_DEV_1
+TYPE         BINTABLE
+SIZE         288
+DESCRIPTION  DVO Image Table 
+
+# elements of the image structure
+
+SUBSTRUCT coords,           COORDS,               Coords,        astrometric data
+SUBFIELD  crval1,           CRVAL1,               double,   	 coordinate at reference pixel
+SUBFIELD  crval2,           CRVAL2,               double,  	 coordinate at reference pixel
+SUBFIELD  crpix1,           CRPIX1,               float,   	 coordinate of reference pixel
+SUBFIELD  crpix2,           CRPIX2,               float,   	 coordinate of reference pixel
+SUBFIELD  cdelt1,           CDELT1,               float,   	 degrees per pixel
+SUBFIELD  cdelt2,           CDELT2,               float,    	 degrees per pixel
+SUBFIELD  pc1_1,            PC1_1,                float,    	 rotation matrix
+SUBFIELD  pc1_2,            PC1_2,                float,    	 rotation matrix
+SUBFIELD  pc2_1,            PC2_1,                float,    	 rotation matrix
+SUBFIELD  pc2_2,            PC2_2,                float,    	 rotation matrix
+SUBFIELD  polyterms,        POLYTERMS,            float[7][2],	 higher order warping terms
+SUBFIELD  ctype,            CTYPE,                char[15],      coordinate type
+SUBFIELD  Npolyterms,       NPOLYTERMS,           char,     	 order of polynomial
+# 120 bytes
+
+FIELD 	  tzero,            TZERO,                e_time,         readout time (row 0)
+FIELD 	  nstar,            NSTAR,                unsigned int,   number of stars on image
+FIELD 	  secz,             SECZ,                 float,      	  airmass,                   mag
+FIELD 	  NX,               NX,                   short,      	  image width
+FIELD 	  NY,               NY,                   short,      	  image height
+FIELD 	  apmifit,          APMIFIT,              float,      	  aperture correction,       mag
+FIELD 	  dapmifit,         DAPMIFIT,             float,      	  apmifit error,             mag
+FIELD 	  Mcal,             MCAL,                 float,      	  calibration mag,           mag
+FIELD 	  dMcal,            DMCAL,                float,      	  error on Mcal,             mag
+FIELD 	  Xm,               XM,                   short,      	  image chisq,               10*log(value)
+FIELD 	  photcode,         PHOTCODE,             short,      	  identifier for CCD,
+FIELD 	  exptime,          EXPTIME,              float,          exposure time,             seconds
+FIELD     sidtime,          ST,			  float,          sidereal time of exposure
+FIELD     latitude,         LAT,		  float,          observatory latitude,      degrees
+# 40 bytes
+
+FIELD 	  name,             NAME,                 char[64],       name of original image 
+FIELD 	  detection_limit,  DETECTION_LIMIT,      unsigned char,  detection limit,           10*mag
+FIELD 	  saturation_limit, SATURATION_LIMIT,     unsigned char,  saturation limit,          10*mag
+FIELD 	  cerror,           CERROR,               unsigned char,  astrometric error,         50*arcsec
+FIELD 	  fwhm_x,           FWHM_X,               unsigned char,  PSF x width,               25*arcsec
+FIELD 	  fwhm_y,           FWHM_Y,               unsigned char,  PSF y width,               25*arcsec
+FIELD 	  trate,            TRATE,                unsigned char,  scan rate,                 100 usec/pixel
+FIELD 	  code,             CODE,                 char,           image quality flag
+FIELD 	  ccdnum,           CCDNUM,               unsigned char,  CCD ID number
+FIELD 	  imageID,          IMAGE_ID,             unsigned int,   image ID
+# 44 bytes 
+
+FIELD 	  dummy,            DUMMY,                char[14],       unused
+FIELD 	  order,            ORDER,                short,      	  Mrel 2D polynomical order 
+FIELD 	  Mx,               MX,                   short,      	  Mrel polyterm
+FIELD 	  My,               MY,                   short,      	  Mrel polyterm
+FIELD 	  Mxx,              MXX,                  short,      	  Mrel polyterm
+FIELD 	  Mxy,              MXY,                  short,      	  Mrel polyterm
+FIELD 	  Myy,              MYY,                  short,      	  Mrel polyterm
+FIELD 	  Mxxx,             MXXX,                 short,      	  Mrel polyterm
+FIELD 	  Mxxy,             MXXY,                 short,      	  Mrel polyterm
+FIELD 	  Mxyy,             MXYY,                 short,      	  Mrel polyterm
+FIELD 	  Myyy,             MYYY,                 short,      	  Mrel polyterm
+FIELD 	  Mxxxx,            MXXXX,                short,      	  Mrel polyterm
+FIELD 	  Mxxxy,            MXXXY,                short,      	  Mrel polyterm
+FIELD 	  Mxxyy,            MXXYY,                short,      	  Mrel polyterm
+FIELD 	  Mxyyy,            MXYYY,                short,      	  Mrel polyterm
+FIELD 	  Myyyy,            MYYYY,                short,      	  Mrel polyterm
+# 40 bytes
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ps1-dev-2.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ps1-dev-2.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ps1-dev-2.d	(revision 22322)
@@ -0,0 +1,70 @@
+STRUCT       Image_PS1_DEV_2
+EXTNAME      DVO_IMAGE_PS1_DEV_2
+TYPE         BINTABLE
+SIZE         280
+DESCRIPTION  DVO Image Table 
+
+# elements of the image structure
+
+SUBSTRUCT coords,           COORDS,               Coords,        astrometric data
+SUBFIELD  crval1,           CRVAL1,               double,   	 coordinate at reference pixel
+SUBFIELD  crval2,           CRVAL2,               double,  	 coordinate at reference pixel
+SUBFIELD  crpix1,           CRPIX1,               float,   	 coordinate of reference pixel
+SUBFIELD  crpix2,           CRPIX2,               float,   	 coordinate of reference pixel
+SUBFIELD  cdelt1,           CDELT1,               float,   	 degrees per pixel
+SUBFIELD  cdelt2,           CDELT2,               float,    	 degrees per pixel
+SUBFIELD  pc1_1,            PC1_1,                float,    	 rotation matrix
+SUBFIELD  pc1_2,            PC1_2,                float,    	 rotation matrix
+SUBFIELD  pc2_1,            PC2_1,                float,    	 rotation matrix
+SUBFIELD  pc2_2,            PC2_2,                float,    	 rotation matrix
+SUBFIELD  polyterms,        POLYTERMS,            float[7][2],	 higher order warping terms
+SUBFIELD  ctype,            CTYPE,                char[15],      coordinate type
+SUBFIELD  Npolyterms,       NPOLYTERMS,           char,     	 order of polynomial
+# 120 bytes
+
+FIELD 	  tzero,            TZERO,                e_time,         readout time (row 0)
+FIELD 	  nstar,            NSTAR,                unsigned int,   number of stars on image
+FIELD 	  secz,             SECZ,                 float,      	  airmass,                   mag
+FIELD 	  NX,               NX,                   unsigned short, image width
+FIELD 	  NY,               NY,                   unsigned short, image height
+FIELD 	  apmifit,          APMIFIT,              float,      	  aperture correction,       mag
+FIELD 	  dapmifit,         DAPMIFIT,             float,      	  apmifit error,             mag
+FIELD 	  Mcal,             MCAL,                 float,      	  calibration mag,           mag
+FIELD 	  dMcal,            DMCAL,                float,      	  error on Mcal,             mag
+FIELD 	  Xm,               XM,                   short,      	  image chisq,               10*log(value)
+FIELD 	  photcode,         PHOTCODE,             short,      	  identifier for CCD,
+FIELD 	  exptime,          EXPTIME,              float,          exposure time,             seconds
+FIELD     sidtime,          ST,			  float,          sidereal time of exposure
+FIELD     latitude,         LAT,		  float,          observatory latitude,      degrees
+# 40 bytes
+
+FIELD 	  name,             NAME,                 char[64],       name of original image 
+FIELD 	  detection_limit,  DETECTION_LIMIT,      unsigned char,  detection limit,           10*mag
+FIELD 	  saturation_limit, SATURATION_LIMIT,     unsigned char,  saturation limit,          10*mag
+FIELD 	  cerror,           CERROR,               unsigned char,  astrometric error,         50*arcsec
+FIELD 	  fwhm_x,           FWHM_X,               unsigned char,  PSF x width,               25*arcsec
+FIELD 	  fwhm_y,           FWHM_Y,               unsigned char,  PSF y width,               25*arcsec
+FIELD 	  trate,            TRATE,                unsigned char,  scan rate,                 100 usec/pixel
+FIELD 	  code,             CODE,                 char,           image quality flag
+FIELD 	  ccdnum,           CCDNUM,               unsigned char,  CCD ID number
+FIELD 	  imageID,          IMAGE_ID,             unsigned int,   internal image ID
+FIELD 	  externID,         EXTERN_ID,            unsigned int,   external image ID
+FIELD 	  sourceID,         SOURCE_ID,            unsigned short, analysis source ID
+# 48 bytes 
+
+FIELD 	  order,            ORDER,                short,      	  Mrel 2D polynomical order 
+FIELD 	  Mx,               MX,                   short,      	  Mrel polyterm
+FIELD 	  My,               MY,                   short,      	  Mrel polyterm
+FIELD 	  Mxx,              MXX,                  short,      	  Mrel polyterm
+FIELD 	  Mxy,              MXY,                  short,      	  Mrel polyterm
+FIELD 	  Myy,              MYY,                  short,      	  Mrel polyterm
+FIELD 	  Mxxx,             MXXX,                 short,      	  Mrel polyterm
+FIELD 	  Mxxy,             MXXY,                 short,      	  Mrel polyterm
+FIELD 	  Mxyy,             MXYY,                 short,      	  Mrel polyterm
+FIELD 	  Myyy,             MYYY,                 short,      	  Mrel polyterm
+FIELD 	  Mxxxx,            MXXXX,                short,      	  Mrel polyterm
+FIELD 	  Mxxxy,            MXXXY,                short,      	  Mrel polyterm
+FIELD 	  Mxxyy,            MXXYY,                short,      	  Mrel polyterm
+FIELD 	  Mxyyy,            MXYYY,                short,      	  Mrel polyterm
+FIELD 	  Myyyy,            MYYYY,                short,      	  Mrel polyterm
+# 40 bytes
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ps1-dev-3.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ps1-dev-3.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image-ps1-dev-3.d	(revision 22322)
@@ -0,0 +1,70 @@
+STRUCT       Image_PS1_DEV_3
+EXTNAME      DVO_IMAGE_PS1_DEV_3
+TYPE         BINTABLE
+SIZE         344
+DESCRIPTION  DVO Image Table 
+
+# elements of the image structure
+
+SUBSTRUCT coords,           COORDS,               Coords,        astrometric data
+SUBFIELD  crval1,           CRVAL1,               double,   	 coordinate at reference pixel
+SUBFIELD  crval2,           CRVAL2,               double,  	 coordinate at reference pixel
+SUBFIELD  crpix1,           CRPIX1,               float,   	 coordinate of reference pixel
+SUBFIELD  crpix2,           CRPIX2,               float,   	 coordinate of reference pixel
+SUBFIELD  cdelt1,           CDELT1,               float,   	 degrees per pixel
+SUBFIELD  cdelt2,           CDELT2,               float,    	 degrees per pixel
+SUBFIELD  pc1_1,            PC1_1,                float,    	 rotation matrix
+SUBFIELD  pc1_2,            PC1_2,                float,    	 rotation matrix
+SUBFIELD  pc2_1,            PC2_1,                float,    	 rotation matrix
+SUBFIELD  pc2_2,            PC2_2,                float,    	 rotation matrix
+SUBFIELD  polyterms,        POLYTERMS,            float[7][2],	 higher order warping terms
+SUBFIELD  ctype,            CTYPE,                char[15],      coordinate type
+SUBFIELD  Npolyterms,       NPOLYTERMS,           char,     	 order of polynomial
+# 120 bytes
+
+FIELD 	  tzero,            TZERO,                e_time,         readout time (row 0)
+FIELD 	  nstar,            NSTAR,                unsigned int,   number of stars on image
+FIELD 	  secz,             SECZ,                 float,      	  airmass,                   mag
+FIELD 	  NX,               NX,                   unsigned short, image width
+FIELD 	  NY,               NY,                   unsigned short, image height
+FIELD 	  apmifit,          APMIFIT,              float,      	  aperture correction,       mag
+FIELD 	  dapmifit,         DAPMIFIT,             float,      	  apmifit error,             mag
+FIELD 	  Mcal,             MCAL,                 float,      	  calibration mag,           mag
+FIELD 	  dMcal,            DMCAL,                float,      	  error on Mcal,             mag
+FIELD 	  Xm,               XM,                   short,      	  image chisq,               10*log(value)
+FIELD 	  photcode,         PHOTCODE,             short,      	  identifier for CCD,
+FIELD 	  exptime,          EXPTIME,              float,          exposure time,             seconds
+FIELD     sidtime,          ST,			  float,          sidereal time of exposure
+FIELD     latitude,         LAT,		  float,          observatory latitude,      degrees
+# 40 bytes
+
+FIELD 	  name,             NAME,                 char[128],      name of original image 
+FIELD 	  detection_limit,  DETECTION_LIMIT,      unsigned char,  detection limit,           10*mag
+FIELD 	  saturation_limit, SATURATION_LIMIT,     unsigned char,  saturation limit,          10*mag
+FIELD 	  cerror,           CERROR,               unsigned char,  astrometric error,         50*arcsec
+FIELD 	  fwhm_x,           FWHM_X,               unsigned char,  PSF x width,               25*arcsec
+FIELD 	  fwhm_y,           FWHM_Y,               unsigned char,  PSF y width,               25*arcsec
+FIELD 	  trate,            TRATE,                unsigned char,  scan rate,                 100 usec/pixel
+FIELD 	  code,             CODE,                 char,           image quality flag
+FIELD 	  ccdnum,           CCDNUM,               unsigned char,  CCD ID number
+FIELD 	  imageID,          IMAGE_ID,             unsigned int,   internal image ID
+FIELD 	  externID,         EXTERN_ID,            unsigned int,   external image ID
+FIELD 	  sourceID,         SOURCE_ID,            unsigned short, analysis source ID
+# 48 bytes 
+
+FIELD 	  order,            ORDER,                short,      	  Mrel 2D polynomical order 
+FIELD 	  Mx,               MX,                   short,      	  Mrel polyterm
+FIELD 	  My,               MY,                   short,      	  Mrel polyterm
+FIELD 	  Mxx,              MXX,                  short,      	  Mrel polyterm
+FIELD 	  Mxy,              MXY,                  short,      	  Mrel polyterm
+FIELD 	  Myy,              MYY,                  short,      	  Mrel polyterm
+FIELD 	  Mxxx,             MXXX,                 short,      	  Mrel polyterm
+FIELD 	  Mxxy,             MXXY,                 short,      	  Mrel polyterm
+FIELD 	  Mxyy,             MXYY,                 short,      	  Mrel polyterm
+FIELD 	  Myyy,             MYYY,                 short,      	  Mrel polyterm
+FIELD 	  Mxxxx,            MXXXX,                short,      	  Mrel polyterm
+FIELD 	  Mxxxy,            MXXXY,                short,      	  Mrel polyterm
+FIELD 	  Mxxyy,            MXXYY,                short,      	  Mrel polyterm
+FIELD 	  Mxyyy,            MXYYY,                short,      	  Mrel polyterm
+FIELD 	  Myyyy,            MYYYY,                short,      	  Mrel polyterm
+# 40 bytes
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/image.d	(revision 22322)
@@ -0,0 +1,71 @@
+STRUCT       Image
+EXTNAME      DVO_IMAGE
+TYPE         BINTABLE
+SIZE         344
+DESCRIPTION  DVO Image Table 
+
+# elements of the image structure
+
+SUBSTRUCT coords,           COORDS,               Coords,        astrometric data
+SUBFIELD  crval1,           CRVAL1,               double,   	 coordinate at reference pixel
+SUBFIELD  crval2,           CRVAL2,               double,  	 coordinate at reference pixel
+SUBFIELD  crpix1,           CRPIX1,               float,   	 coordinate of reference pixel
+SUBFIELD  crpix2,           CRPIX2,               float,   	 coordinate of reference pixel
+SUBFIELD  cdelt1,           CDELT1,               float,   	 degrees per pixel
+SUBFIELD  cdelt2,           CDELT2,               float,    	 degrees per pixel
+SUBFIELD  pc1_1,            PC1_1,                float,    	 rotation matrix
+SUBFIELD  pc1_2,            PC1_2,                float,    	 rotation matrix
+SUBFIELD  pc2_1,            PC2_1,                float,    	 rotation matrix
+SUBFIELD  pc2_2,            PC2_2,                float,    	 rotation matrix
+SUBFIELD  polyterms,        POLYTERMS,            float[7][2],	 higher order warping terms
+SUBFIELD  ctype,            CTYPE,                char[15],      coordinate type
+SUBFIELD  Npolyterms,       NPOLYTERMS,           char,     	 order of polynomial
+# 120 bytes
+
+# change this to a double?
+FIELD 	  tzero,            TZERO,                e_time,         readout time (row 0)
+FIELD 	  nstar,            NSTAR,                unsigned int,   number of stars on image
+FIELD 	  secz,             SECZ,                 float,      	  airmass,                   mag
+FIELD 	  NX,               NX,                   unsigned short, image width
+FIELD 	  NY,               NY,                   unsigned short, image height
+FIELD 	  apmifit,          APMIFIT,              float,      	  aperture correction,       mag
+FIELD 	  dapmifit,         DAPMIFIT,             float,      	  apmifit error,             mag
+FIELD 	  Mcal,             MCAL,                 float,      	  calibration mag,           mag
+FIELD 	  dMcal,            DMCAL,                float,      	  error on Mcal,             mag
+FIELD 	  Xm,               XM,                   short,      	  image chisq,               10*log(value)
+FIELD 	  photcode,         PHOTCODE,             short,      	  identifier for CCD,
+FIELD 	  exptime,          EXPTIME,              float,          exposure time,             seconds
+FIELD     sidtime,          ST,			  float,          sidereal time of exposure
+FIELD     latitude,         LAT,		  float,          observatory latitude,      degrees
+# 40 bytes
+
+FIELD 	  name,             NAME,                 char[128],      name of original image 
+FIELD 	  detection_limit,  DETECTION_LIMIT,      unsigned char,  detection limit,           10*mag
+FIELD 	  saturation_limit, SATURATION_LIMIT,     unsigned char,  saturation limit,          10*mag
+FIELD 	  cerror,           CERROR,               unsigned char,  astrometric error,         50*arcsec
+FIELD 	  fwhm_x,           FWHM_X,               unsigned char,  PSF x width,               25*arcsec
+FIELD 	  fwhm_y,           FWHM_Y,               unsigned char,  PSF y width,               25*arcsec
+FIELD 	  trate,            TRATE,                unsigned char,  scan rate,                 100 usec/pixel
+FIELD 	  code,             CODE,                 char,           image quality flag
+FIELD 	  ccdnum,           CCDNUM,               unsigned char,  CCD ID number
+FIELD 	  imageID,          IMAGE_ID,             unsigned int,   internal image ID
+FIELD 	  externID,         EXTERN_ID,            unsigned int,   external image ID
+FIELD 	  sourceID,         SOURCE_ID,            unsigned short, analysis source ID
+# 48 bytes 
+
+FIELD 	  order,            ORDER,                short,      	  Mrel 2D polynomical order 
+FIELD 	  Mx,               MX,                   short,      	  Mrel polyterm
+FIELD 	  My,               MY,                   short,      	  Mrel polyterm
+FIELD 	  Mxx,              MXX,                  short,      	  Mrel polyterm
+FIELD 	  Mxy,              MXY,                  short,      	  Mrel polyterm
+FIELD 	  Myy,              MYY,                  short,      	  Mrel polyterm
+FIELD 	  Mxxx,             MXXX,                 short,      	  Mrel polyterm
+FIELD 	  Mxxy,             MXXY,                 short,      	  Mrel polyterm
+FIELD 	  Mxyy,             MXYY,                 short,      	  Mrel polyterm
+FIELD 	  Myyy,             MYYY,                 short,      	  Mrel polyterm
+FIELD 	  Mxxxx,            MXXXX,                short,      	  Mrel polyterm
+FIELD 	  Mxxxy,            MXXXY,                short,      	  Mrel polyterm
+FIELD 	  Mxxyy,            MXXYY,                short,      	  Mrel polyterm
+FIELD 	  Mxyyy,            MXYYY,                short,      	  Mrel polyterm
+FIELD 	  Myyyy,            MYYYY,                short,      	  Mrel polyterm
+# 40 bytes
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-elixir.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-elixir.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-elixir.d	(revision 22322)
@@ -0,0 +1,23 @@
+STRUCT  Measure_Elixir
+EXTNAME DVO_MEASURE_ELIXIR
+TYPE    BINTABLE
+SIZE    32
+
+# elements of data structure / FITS table
+
+FIELD dR,             D_RA,       short,          RA offset,                	  1/100 arcsec
+FIELD dD,             D_DEC,      short,          DEC offset,               	  1/100 arcsec
+FIELD M,              MAG,        short,          catalog mag,       	       	  millimag
+FIELD Mcal,           Mcal,       short,          image cal mag,	          millimag
+FIELD Mgal,           Mgal,       short,          galaxy mag,			  millimag
+FIELD airmass,        airmass,    short,          (airmass - 1),		  milliairmass
+FIELD FWx,            FWx,        short,          object fwhm major axis,         1/100 of arcsec 
+FIELD dM,             dM,         unsigned char,  mag error,                      millimag
+FIELD fwy,            fwy,        unsigned char,  object fwhm minor/major ratio
+FIELD theta,          theta,      unsigned char,  angle wrt ccd X dir,            (0xff/360) deg
+FIELD dophot,         dophot,     char,           dophot type
+FIELD source,         source,     unsigned short, photcode
+FIELD t,              t,          unsigned int,   time in seconds (UNIX)
+FIELD averef,         averef,     unsigned int,   reference to average entry      
+FIELD dt,             dt,         short,          exposure time,                  2500*log(exptime)
+FIELD flags,          flags,      unsigned short, flags for various uses  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-loneos.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-loneos.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-loneos.d	(revision 22322)
@@ -0,0 +1,16 @@
+STRUCT  Measure_Loneos
+EXTNAME DVO_MEASURE_LONEOS
+TYPE    BINTABLE
+SIZE    20
+
+# elements of data structure / FITS table
+
+FIELD dR,             D_RA,       short,          RA offset,                	  1/100 arcsec
+FIELD dD,             D_DEC,      short,          DEC offset,               	  1/100 arcsec
+FIELD M,              MAG,        short,          catalog mag,       	       	  millimag
+FIELD Mcal,           Mcal,       short,          image cal mag,	          millimag
+FIELD dM,             dM,         unsigned char,  mag error,                      millimag
+FIELD dophot,         dophot,     char,           dophot type
+FIELD source,         source,     unsigned short, photcode
+FIELD t,              t,          unsigned int,   time in seconds (UNIX)
+FIELD averef,         averef,     unsigned int,   reference to average entry      
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-panstarrs-dev-0.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-panstarrs-dev-0.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-panstarrs-dev-0.d	(revision 22322)
@@ -0,0 +1,54 @@
+STRUCT       Measure_Panstarrs_DEV_0
+EXTNAME      DVO_MEASURE_PANSTARRS_DEV_0
+TYPE         BINTABLE
+SIZE         96
+DESCRIPTION  DVO Detection Measurement Table 
+
+FIELD dR,             D_RA,         float,          RA offset,                	  arcsec
+FIELD dD,             D_DEC,        float,          DEC offset,               	  arcsec
+FIELD M,              MAG,          float,          catalog mag,       	       	  mag
+FIELD Mcal,           M_CAL,        float,          image cal mag,	          mag
+FIELD Mgal,           M_GAL,        float,          galaxy mag,			  mag
+FIELD dM,             MAG_ERR,      float,          mag error,                    mag
+FIELD dt,             M_TIME,       float,          exposure time,                2.5*log(exptime)
+
+# note that with airmass = 1.0 / cos(90 - alt), we have full alt/az representation
+FIELD airmass,        AIRMASS,      float,          (airmass - 1),		  airmass
+FIELD az,             AZ,           float,          telescope azimuth
+
+# new field elements needed for Pan-STARRS:
+FIELD Xccd,           X_CCD,        float,          X coord on chip,               pixels
+FIELD Yccd,           Y_CCD,        float,          Y coord on chip,               pixels
+
+# could these be packed into fewer bits?
+FIELD Sky,            SKY_FLUX,     float,          local estimate of sky flux,    counts/sec
+FIELD dSky,           SKY_FLUX_ERR, float,          local estimate of sky flux,    counts/sec
+
+FIELD t,              TIME,         unsigned int,   time in seconds (UNIX)
+FIELD averef,         AVE_REF,      unsigned int,   reference to average entry      
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints for backwards compatibility?
+FIELD detID_hi,       DET_ID_HI,    unsigned int,   ID upper bytes
+FIELD detID_lo,       DET_ID_LO,    unsigned int,   ID lower bytes
+
+FIELD imageID_hi,     IMAGE_ID_HI,  unsigned int,   reference to image
+FIELD imageID_lo,     IMAGE_ID_LO,  unsigned int,   reference to image
+
+FIELD FWx,            FWHM_MAJOR,   short,          object fwhm major axis,       1/100 of arcsec 
+FIELD FWy,            FWHM_MINOR,   short,          object fwhm minor axis,       1/100 of arcsec 
+FIELD theta,          PSF_THETA,    short,          angle wrt ccd X dir,          (0xffff/360) deg
+FIELD photcode,       PHOTCODE,     unsigned short, photcode
+
+FIELD flags,          FLAGS,        unsigned short, flags for various uses  
+
+FIELD dXccd,          X_CCD_ERR,    short,          X coord error on chip,         pixels
+FIELD dYccd,          Y_CCD_ERR,    short,          Y coord error on chip,         pixels
+
+# do we need more resolution than a short? should this be a log?
+FIELD qPSF,           PSF_QF,       short,          psf coverage/quality factor
+
+FIELD dophot,         DOPHOT,       char,           dophot type
+FIELD stargal,        STAR_GAL,     char,           star-galaxy separator
+
+# we need extra bytes for padding purposes...
+FIELD dummy,          DUMMY,        char[2],        padding
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-panstarrs-dev-1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-panstarrs-dev-1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-panstarrs-dev-1.d	(revision 22322)
@@ -0,0 +1,54 @@
+STRUCT       Measure_Panstarrs_DEV_1
+EXTNAME      DVO_MEASURE_PANSTARRS_DEV_1
+TYPE         BINTABLE
+SIZE         96
+DESCRIPTION  DVO Detection Measurement Table 
+
+FIELD dR,             D_RA,         float,          RA offset,                	  arcsec
+FIELD dD,             D_DEC,        float,          DEC offset,               	  arcsec
+FIELD M,              MAG,          float,          catalog mag,       	       	  mag
+FIELD Mcal,           M_CAL,        float,          image cal mag,	          mag
+FIELD Mgal,           M_GAL,        float,          galaxy mag,			  mag
+FIELD dM,             MAG_ERR,      float,          mag error,                    mag
+FIELD dt,             M_TIME,       float,          exposure time,                2.5*log(exptime)
+
+# note that with airmass = 1.0 / cos(90 - alt), we have full alt/az representation
+FIELD airmass,        AIRMASS,      float,          (airmass - 1),		  airmass
+FIELD az,             AZ,           float,          telescope azimuth
+
+# new field elements needed for Pan-STARRS:
+FIELD Xccd,           X_CCD,        float,          X coord on chip,               pixels
+FIELD Yccd,           Y_CCD,        float,          Y coord on chip,               pixels
+
+# could these be packed into fewer bits?
+FIELD Sky,            SKY_FLUX,     float,          local estimate of sky flux,    counts/sec
+FIELD dSky,           SKY_FLUX_ERR, float,          local estimate of sky flux,    counts/sec
+
+FIELD t,              TIME,         unsigned int,   time in seconds (UNIX)
+FIELD averef,         AVE_REF,      unsigned int,   reference to average entry      
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints for backwards compatibility?
+FIELD detID_hi,       DET_ID_HI,    unsigned int,   ID upper bytes
+FIELD detID_lo,       DET_ID_LO,    unsigned int,   ID lower bytes
+
+FIELD imageID_hi,     IMAGE_ID_HI,  unsigned int,   reference to image
+FIELD imageID_lo,     IMAGE_ID_LO,  unsigned int,   reference to image
+
+FIELD FWx,            FWHM_MAJOR,   short,          object fwhm major axis,       1/100 of arcsec 
+FIELD FWy,            FWHM_MINOR,   short,          object fwhm minor axis,       1/100 of arcsec 
+FIELD theta,          PSF_THETA,    short,          angle wrt ccd X dir,          (0xffff/360) deg
+FIELD photcode,       PHOTCODE,     unsigned short, photcode
+
+FIELD flags,          FLAGS,        unsigned short, flags for various uses  
+
+FIELD dXccd,          X_CCD_ERR,    short,          X coord error on chip,         pixels
+FIELD dYccd,          Y_CCD_ERR,    short,          Y coord error on chip,         pixels
+
+# do we need more resolution than a short? should this be a log?
+FIELD qPSF,           PSF_QF,       short,          psf coverage/quality factor
+
+FIELD dophot,         DOPHOT,       char,           dophot type
+FIELD stargal,        STAR_GAL,     char,           star-galaxy separator
+
+# we need extra bytes for padding purposes...
+FIELD dummy,          DUMMY,        char[2],        padding
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-ps1-dev-1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-ps1-dev-1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-ps1-dev-1.d	(revision 22322)
@@ -0,0 +1,56 @@
+STRUCT       Measure_PS1_DEV_1
+EXTNAME      DVO_MEASURE_PS1_DEV_1
+TYPE         BINTABLE
+SIZE         104
+DESCRIPTION  DVO Detection Measurement Table 
+
+FIELD dR,             D_RA,         float,          RA offset,                	  arcsec
+FIELD dD,             D_DEC,        float,          DEC offset,               	  arcsec
+FIELD M,              MAG,          float,          catalog mag,       	       	  mag
+FIELD Mcal,           M_CAL,        float,          image cal mag,	          mag
+FIELD Mgal,           M_GAL,        float,          galaxy mag,			  mag
+FIELD dM,             MAG_ERR,      float,          mag error,                    mag
+FIELD dt,             M_TIME,       float,          exposure time,                2.5*log(exptime)
+
+# note that with airmass = 1.0 / cos(90 - alt), we have full alt/az representation
+FIELD airmass,        AIRMASS,      float,          (airmass - 1),		  airmass
+FIELD az,             AZ,           float,          telescope azimuth
+
+# new field elements needed for Pan-STARRS:
+FIELD Xccd,           X_CCD,        float,          X coord on chip,               pixels
+FIELD Yccd,           Y_CCD,        float,          Y coord on chip,               pixels
+
+# could these be packed into fewer bits?
+FIELD Sky,            SKY_FLUX,     float,          local estimate of sky flux,    counts/sec
+FIELD dSky,           SKY_FLUX_ERR, float,          local estimate of sky flux,    counts/sec
+
+FIELD t,              TIME,         unsigned int,   time in seconds (UNIX)
+FIELD averef,         AVE_REF,      unsigned int,   reference to average entry      
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints for backwards compatibility?
+FIELD detID,          DET_ID,       unsigned int,   detection ID
+FIELD imageID,        IMAGE_ID,     unsigned int,   reference to image
+
+# do we need more resolution than a short? should this be a log?
+FIELD qPSF,           PSF_QF,       float,          psf coverage/quality factor
+FIELD psfChisq,       PSF_CHISQ,    float,          psf coverage/quality factor
+FIELD crNsigma,       CR_NSIGMA,    float,          psf coverage/quality factor
+FIELD extNsigma,      EXT_NSIGMA,   float,          psf coverage/quality factor
+
+FIELD FWx,            FWHM_MAJOR,   short,          object fwhm major axis,       1/100 of arcsec 
+FIELD FWy,            FWHM_MINOR,   short,          object fwhm minor axis,       1/100 of arcsec 
+FIELD theta,          PSF_THETA,    short,          angle wrt ccd X dir,          (0xffff/360) deg
+FIELD photcode,       PHOTCODE,     unsigned short, photcode
+
+FIELD dXccd,          X_CCD_ERR,    short,          X coord error on chip,         pixels
+FIELD dYccd,          Y_CCD_ERR,    short,          Y coord error on chip,         pixels
+
+FIELD dbFlags,        DB_FLAGS,     unsigned short, flags for various uses  
+FIELD photFlags,      PHOT_FLAGS,   unsigned short, flags supplied by photometry program
+
+FIELD stargal,        STAR_GAL,     char,           star-galaxy separator
+
+# absorb these into photFlags?
+FIELD dophot,         DOPHOT,       char,           dophot type
+
+FIELD dummy,          DUMMY,        char[2],        padding
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-ps1-dev-2.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-ps1-dev-2.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure-ps1-dev-2.d	(revision 22322)
@@ -0,0 +1,57 @@
+STRUCT       Measure_PS1_DEV_2
+EXTNAME      DVO_MEASURE_PS1_DEV_2
+TYPE         BINTABLE
+SIZE         112
+DESCRIPTION  DVO Detection Measurement Table 
+
+FIELD dR,             D_RA,         float,          RA offset,                	  arcsec
+FIELD dD,             D_DEC,        float,          DEC offset,               	  arcsec
+FIELD M,              MAG,          float,          catalog mag,       	       	  mag
+FIELD Mcal,           M_CAL,        float,          image cal mag,	          mag
+FIELD Map,            M_APER,       float,          aperture mag,		  mag
+FIELD dM,             MAG_ERR,      float,          mag error,                    mag
+FIELD dMcal,          MAG_CAL_ERR,  float,          systematic calibration error, mag
+FIELD dt,             M_TIME,       float,          exposure time,                2.5*log(exptime)
+
+# note that with airmass = 1.0 / cos(90 - alt), we have full alt/az representation
+FIELD airmass,        AIRMASS,      float,          (airmass - 1),		  airmass
+FIELD az,             AZ,           float,          telescope azimuth
+
+# new field elements needed for Pan-STARRS:
+FIELD Xccd,           X_CCD,        float,          X coord on chip,               pixels
+FIELD Yccd,           Y_CCD,        float,          Y coord on chip,               pixels
+
+# could these be packed into fewer bits?
+FIELD Sky,            SKY_FLUX,     float,          local estimate of sky flux,    counts/sec
+FIELD dSky,           SKY_FLUX_ERR, float,          local estimate of sky flux,    counts/sec
+
+FIELD t,              TIME,         unsigned int,   time in seconds (UNIX)
+FIELD averef,         AVE_REF,      unsigned int,   reference to average entry      
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints for backwards compatibility?
+FIELD detID,          DET_ID,       unsigned int,   detection ID
+FIELD imageID,        IMAGE_ID,     unsigned int,   reference to DVO image ID
+
+# do we need more resolution than a short? should this be a log?
+FIELD qPSF,           PSF_QF,       float,          psf coverage/quality factor
+FIELD psfChisq,       PSF_CHISQ,    float,          psf fit chisq
+FIELD crNsigma,       CR_NSIGMA,    float,          Nsigma deviation towards CR
+FIELD extNsigma,      EXT_NSIGMA,   float,          Nsigma deviation towards EXT
+
+FIELD FWx,            FWHM_MAJOR,   short,          object fwhm major axis,       1/100 of arcsec 
+FIELD FWy,            FWHM_MINOR,   short,          object fwhm minor axis,       1/100 of arcsec 
+FIELD theta,          PSF_THETA,    short,          angle wrt ccd X dir,          (0xffff/360) deg
+FIELD photcode,       PHOTCODE,     unsigned short, photcode
+
+FIELD dXccd,          X_CCD_ERR,    short,          X coord error on chip,         pixels
+FIELD dYccd,          Y_CCD_ERR,    short,          Y coord error on chip,         pixels
+
+FIELD dbFlags,        DB_FLAGS,     unsigned short, flags for various uses  
+FIELD photFlags,      PHOT_FLAGS,   unsigned short, flags supplied by photometry program
+
+FIELD stargal,        STAR_GAL,     char,           star-galaxy separator
+
+# absorb these into photFlags?
+FIELD dophot,         DOPHOT,       char,           dophot type
+
+FIELD dummy,          DUMMY,        char[6],        padding
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/measure.d	(revision 22322)
@@ -0,0 +1,58 @@
+STRUCT       Measure
+EXTNAME      DVO_MEASURE
+TYPE         BINTABLE
+SIZE         112
+DESCRIPTION  DVO Detection Measurement Table 
+
+FIELD dR,             D_RA,         float,          RA offset,                	  arcsec
+FIELD dD,             D_DEC,        float,          DEC offset,               	  arcsec
+FIELD M,              MAG,          float,          catalog mag,       	       	  mag
+FIELD Mcal,           M_CAL,        float,          image cal mag,	          mag
+FIELD Map,            M_APER,       float,          aperture mag,		  mag
+FIELD dM,             MAG_ERR,      float,          mag error,                    mag
+FIELD dMcal,          MAG_CAL_ERR,  float,          systematic calibration error, mag
+FIELD dt,             M_TIME,       float,          exposure time,                2.5*log(exptime)
+
+# note that with airmass = 1.0 / cos(90 - alt), we have full alt/az representation
+FIELD airmass,        AIRMASS,      float,          (airmass - 1),		  airmass
+FIELD az,             AZ,           float,          telescope azimuth
+
+# new field elements needed for Pan-STARRS:
+FIELD Xccd,           X_CCD,        float,          X coord on chip,               pixels
+FIELD Yccd,           Y_CCD,        float,          Y coord on chip,               pixels
+
+# could these be packed into fewer bits?
+FIELD Sky,            SKY_FLUX,     float,          local estimate of sky flux,    counts/sec
+FIELD dSky,           SKY_FLUX_ERR, float,          local estimate of sky flux,    counts/sec
+
+FIELD t,              TIME,         unsigned int,   time in seconds (UNIX)
+FIELD averef,         AVE_REF,      unsigned int,   reference to average entry      
+
+# Pan-STARRS uses a 64-bit detection ID.  keep this in two 32 bit ints for backwards compatibility?
+FIELD detID,          DET_ID,       unsigned int,   detection ID
+FIELD imageID,        IMAGE_ID,     unsigned int,   reference to DVO image ID
+
+# do we need more resolution than a short? should this be a log?
+FIELD qPSF,           PSF_QF,       float,          psf coverage/quality factor
+FIELD psfChisq,       PSF_CHISQ,    float,          psf fit chisq
+FIELD crNsigma,       CR_NSIGMA,    float,          Nsigma deviation towards CR
+FIELD extNsigma,      EXT_NSIGMA,   float,          Nsigma deviation towards EXT
+
+FIELD FWx,            FWHM_MAJOR,   short,          object fwhm major axis,       1/100 of arcsec 
+FIELD FWy,            FWHM_MINOR,   short,          object fwhm minor axis,       1/100 of arcsec 
+FIELD theta,          PSF_THETA,    short,          angle wrt ccd X dir,          (0xffff/360) deg
+FIELD photcode,       PHOTCODE,     unsigned short, photcode
+
+# convert this to error in arcsec on load?
+FIELD dXccd,          X_CCD_ERR,    short,          X coord error on chip,         pixels
+FIELD dYccd,          Y_CCD_ERR,    short,          Y coord error on chip,         pixels
+
+FIELD dbFlags,        DB_FLAGS,     unsigned short, flags for various uses  
+FIELD photFlags,      PHOT_FLAGS,   unsigned short, flags supplied by photometry program
+
+FIELD stargal,        STAR_GAL,     char,           star-galaxy separator
+
+# absorb these into photFlags?
+FIELD dophot,         DOPHOT,       char,           dophot type
+
+FIELD dummy,          DUMMY,        char[6],        padding
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/missing.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/missing.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/missing.d	(revision 22322)
@@ -0,0 +1,8 @@
+STRUCT       Missing
+EXTNAME      DVO_MISSING
+TYPE         BINTABLE
+SIZE         4
+DESCRIPTION  DVO Non-Detection Table 
+
+# elements of data structure / FITS table
+FIELD t,               TIME,        unsigned int,    time in seconds
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-elixir.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-elixir.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-elixir.d	(revision 22322)
@@ -0,0 +1,20 @@
+STRUCT       PhotCode_Elixir
+EXTNAME      DVO_PHOTCODE_ELIXIR
+TYPE         BINTABLE
+SIZE         80
+DESCRIPTION  DVO Photcode Description Table 
+
+# elements of data structure / FITS table
+FIELD  code,          CODE,           unsigned short, code number (stored in Measure.source) 
+FIELD  name,          NAME,           char[32],       name for filter combination 
+FIELD  type,          TYPE,           char,           PRI/SEC/DEP/REF 
+FIELD  dummy,         DUMMY,          char[3],        padding
+FIELD  C,             C_LAM,          short,          primary phot calibration terms (millimags) 
+FIELD  dC,            C_LAM_ERR,      short,          primary phot calibration terms (millimags) 
+FIELD  dX,            X_ERR,          short,          primary phot calibration terms (millimags) 
+FIELD  K,             K,              float,          secondary phot calibration terms (millimags) 
+FIELD  c1,            C1,             int,            color is average.M[c1] - average.M[c2] 
+FIELD  c2,            C2,             int,            color is average.M[c1] - average.M[c2] 
+FIELD  equiv,         EQUIV,          int,            this dependent filter is equivalent to equiv PRI/SEC
+FIELD  Nc,            NC,             int,            number of color terms 
+FIELD  X,             X,              float[4],       color terms $X[0]*mc + X[1]*mc^2 + X[2]*mc^3$, etc 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-ps1-dev-1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-ps1-dev-1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-ps1-dev-1.d	(revision 22322)
@@ -0,0 +1,23 @@
+STRUCT       PhotCode_PS1_DEV_1
+EXTNAME      DVO_PHOTCODE_PS1_DEV_1
+TYPE         BINTABLE
+SIZE         104
+DESCRIPTION  DVO Photcode Description Table 
+
+# elements of data structure / FITS table
+FIELD  code,         	  CODE,          	 unsigned short, code number (stored in Measure.source) 
+FIELD  name,         	  NAME,          	 char[32],       name for filter combination 
+FIELD  type,         	  TYPE,          	 char,           PRI/SEC/DEP/REF 
+FIELD  dummy,        	  DUMMY,         	 char[3],        padding
+FIELD  C,            	  C_LAM,         	 short,          primary phot calibration terms (millimags) 
+FIELD  dC,           	  C_LAM_ERR,     	 short,          primary phot calibration terms (millimags) 
+FIELD  dX,           	  X_ERR,         	 short,          primary phot calibration terms (millimags) 
+FIELD  K,            	  K,             	 float,          secondary phot calibration terms (millimags) 
+FIELD  c1,           	  C1,            	 int,            color is average.M[c1] - average.M[c2] 
+FIELD  c2,           	  C2,            	 int,            color is average.M[c1] - average.M[c2] 
+FIELD  equiv,        	  EQUIV,         	 int,            this dependent filter is equivalent to equiv PRI/SEC
+FIELD  Nc,           	  NC,            	 int,            number of color terms 
+FIELD  X,            	  X,             	 float[4],       color terms $X[0]*mc + X[1]*mc^2 + X[2]*mc^3$, etc 
+FIELD  astromErrMagScale, ASTROM_ERR_MAG_SCALE,  float,          astrometric error / mag error scale
+FIELD  photomErrSys,   	  PHOTOM_ERR_SYS,  	 float,          systematic photometric error
+FIELD  padding,           PADDING,               float[4],       placeholder
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-ps1-dev-2.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-ps1-dev-2.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-ps1-dev-2.d	(revision 22322)
@@ -0,0 +1,29 @@
+STRUCT       PhotCode_PS1_DEV_2
+EXTNAME      DVO_PHOTCODE_PS1_DEV_2
+TYPE         BINTABLE
+SIZE         96
+DESCRIPTION  DVO Photcode Description Table 
+
+# elements of data structure / FITS table
+FIELD  code,         	  CODE,          	 unsigned short, code number (stored in Measure.source) 
+FIELD  name,         	  NAME,          	 char[32],       name for filter combination 
+FIELD  type,         	  TYPE,          	 char,           PRI/SEC/DEP/REF 
+FIELD  dummy,        	  DUMMY,         	 char[3],        padding
+FIELD  C,            	  C_LAM,         	 short,          primary phot calibration terms (millimags) 
+FIELD  dC,           	  C_LAM_ERR,     	 short,          primary phot calibration terms (millimags) 
+FIELD  dX,           	  X_ERR,         	 short,          primary phot calibration terms (millimags) 
+FIELD  K,            	  K,             	 float,          secondary phot calibration terms (millimags) 
+FIELD  c1,           	  C1,            	 int,            color is average.M[c1] - average.M[c2] 
+FIELD  c2,           	  C2,            	 int,            color is average.M[c1] - average.M[c2] 
+FIELD  equiv,        	  EQUIV,         	 int,            this dependent filter is equivalent to equiv PRI/SEC
+FIELD  Nc,           	  NC,            	 int,            number of color terms 
+FIELD  X,            	  X,             	 float[4],       color terms $X[0]*mc + X[1]*mc^2 + X[2]*mc^3$, etc 
+FIELD  astromErrSys,      ASTROM_ERR_SYS,  	 float,          systematic astrometry error (arcsec)
+FIELD  astromErrScale,    ASTROM_ERR_SCALE,  	 float,          astrometric error scale
+FIELD  astromErrMagScale, ASTROM_ERR_MAG_SCALE,  float,          astrometric error / mag error scale
+FIELD  photomErrSys,   	  PHOTOM_ERR_SYS,  	 float,          systematic photometric error
+
+#   dR_total^2 =  dR_sys^2 + AS * dR_obs^2 + MS * dM_obs^2
+#   dR_sys : systematicAstrometryError
+#   AS     : astrometryErrorScale
+#   MS     : astrometryErrorMagScale
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-ps1-dev-3.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-ps1-dev-3.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode-ps1-dev-3.d	(revision 22322)
@@ -0,0 +1,33 @@
+STRUCT       PhotCode_PS1_DEV_3
+EXTNAME      DVO_PHOTCODE_PS1_DEV_3
+TYPE         BINTABLE
+SIZE         104
+DESCRIPTION  DVO Photcode Description Table 
+
+# elements of data structure / FITS table
+FIELD  code,         	  CODE,          	 unsigned short, code number (stored in Measure.source) 
+FIELD  name,         	  NAME,          	 char[32],       name for filter combination 
+FIELD  type,         	  TYPE,          	 char,           PRI/SEC/DEP/REF 
+FIELD  dummy,        	  DUMMY,         	 char[3],        padding
+FIELD  C,            	  C_LAM,         	 short,          primary phot calibration terms (millimags) 
+FIELD  dC,           	  C_LAM_ERR,     	 short,          primary phot calibration terms (millimags) 
+FIELD  dX,           	  X_ERR,         	 short,          primary phot calibration terms (millimags) 
+FIELD  K,            	  K,             	 float,          secondary phot calibration terms (millimags) 
+FIELD  c1,           	  C1,            	 int,            color is average.M[c1] - average.M[c2] 
+FIELD  c2,           	  C2,            	 int,            color is average.M[c1] - average.M[c2] 
+FIELD  equiv,        	  EQUIV,         	 int,            this dependent filter is equivalent to equiv PRI/SEC
+FIELD  Nc,           	  NC,            	 int,            number of color terms 
+FIELD  X,            	  X,             	 float[4],       color terms $X[0]*mc + X[1]*mc^2 + X[2]*mc^3$, etc 
+FIELD  astromErrSys,      ASTROM_ERR_SYS,  	 float,          systematic astrometry error (arcsec)
+FIELD  astromErrScale,    ASTROM_ERR_SCALE,  	 float,          astrometric error scale
+FIELD  astromErrMagScale, ASTROM_ERR_MAG_SCALE,  float,          astrometric error / mag error scale
+FIELD  astromPoorMask,    ASTROM_POOR_MASK,      short,          detections matching this mask should only be used in emergencies
+FIELD  astromBadMask,     ASTROM_BAD_MASK,       short,          detections matching this mask should not be used
+FIELD  photomErrSys,   	  PHOTOM_ERR_SYS,  	 float,          systematic photometric error
+FIELD  photomPoorMask, 	  PHOTOM_POOR_MASK,  	 short,          detections matching this mask should only be used in emergencies
+FIELD  photomBadMask,  	  PHOTOM_BAD_MASK,  	 short,          detections matching this mask should not be used
+
+#   dR_total^2 =  dR_sys^2 + AS * dR_obs^2 + MS * dM_obs^2
+#   dR_sys : systematicAstrometryError
+#   AS     : astrometryErrorScale
+#   MS     : astrometryErrorMagScale
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photcode.d	(revision 22322)
@@ -0,0 +1,33 @@
+STRUCT       PhotCode
+EXTNAME      DVO_PHOTCODE_RAW
+TYPE         BINTABLE
+SIZE         104
+DESCRIPTION  DVO Photcode Description Table 
+
+# elements of data structure / FITS table
+FIELD  code,         	  CODE,          	 unsigned short, code number (stored in Measure.source) 
+FIELD  name,         	  NAME,          	 char[32],       name for filter combination 
+FIELD  type,         	  TYPE,          	 char,           PRI/SEC/DEP/REF 
+FIELD  dummy,        	  DUMMY,         	 char[3],        padding
+FIELD  C,            	  C_LAM,         	 short,          primary phot calibration terms (millimags) 
+FIELD  dC,           	  C_LAM_ERR,     	 short,          primary phot calibration terms (millimags) 
+FIELD  dX,           	  X_ERR,         	 short,          primary phot calibration terms (millimags) 
+FIELD  K,            	  K,             	 float,          secondary phot calibration terms (millimags) 
+FIELD  c1,           	  C1,            	 int,            color is average.M[c1] - average.M[c2] 
+FIELD  c2,           	  C2,            	 int,            color is average.M[c1] - average.M[c2] 
+FIELD  equiv,        	  EQUIV,         	 int,            this dependent filter is equivalent to equiv PRI/SEC
+FIELD  Nc,           	  NC,            	 int,            number of color terms 
+FIELD  X,            	  X,             	 float[4],       color terms $X[0]*mc + X[1]*mc^2 + X[2]*mc^3$, etc 
+FIELD  astromErrSys,      ASTROM_ERR_SYS,  	 float,          systematic astrometry error (arcsec)
+FIELD  astromErrScale,    ASTROM_ERR_SCALE,  	 float,          astrometric error scale
+FIELD  astromErrMagScale, ASTROM_ERR_MAG_SCALE,  float,          astrometric error / mag error scale
+FIELD  astromPoorMask,    ASTROM_POOR_MASK,      short,          detections matching this mask should only be used in emergencies
+FIELD  astromBadMask,     ASTROM_BAD_MASK,       short,          detections matching this mask should not be used
+FIELD  photomErrSys,   	  PHOTOM_ERR_SYS,  	 float,          systematic photometric error
+FIELD  photomPoorMask, 	  PHOTOM_POOR_MASK,  	 short,          detections matching this mask should only be used in emergencies
+FIELD  photomBadMask,  	  PHOTOM_BAD_MASK,  	 short,          detections matching this mask should not be used
+
+#   dR_total^2 =  dR_sys^2 + AS * dR_obs^2 + MS * dM_obs^2
+#   dR_sys : systematicAstrometryError
+#   AS     : astrometryErrorScale
+#   MS     : astrometryErrorMagScale
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photreg-old.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photreg-old.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photreg-old.d	(revision 22322)
@@ -0,0 +1,18 @@
+STRUCT PhotParsOld
+EXTNAME ZERO_POINTS
+TYPE BINTABLE
+SIZE 100
+
+# elements of data structure / FITS table
+
+FIELD ZP,    	  ZP_OBS,     float,       measured zero point,       mag
+FIELD ZPo,   	  ZP_REF,     float, 	   nominal zero point,        mag
+FIELD dZP,   	  ZP_ERR,     float, 	   error on zero point,       mag
+FIELD K,     	  C_AIRMASS,  float, 	   airmass coeff,             mag per airmass
+FIELD X,     	  C_COLOR,    float, 	   color coeff,               mag per mag
+FIELD tstart,	  START_TIME, e_time,      start time of measurement, seconds since 1 Jan 1970 UT
+FIELD tstop, 	  STOP_TIME,  e_time,      stop time of measurement,  seconds since 1 Jan 1970 UT
+FIELD c1,    	  C1_CODE,    short, 	   code 1 for color,          photcode
+FIELD c2,    	  C2_CODE,    short, 	   code 2 for color,          photcode
+FIELD photcode,   PHOTCODE,   short, 	   photcode,                  photcode
+FIELD label,      LABEL,      char[66],    data label
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photreg.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photreg.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/photreg.d	(revision 22322)
@@ -0,0 +1,21 @@
+STRUCT PhotPars
+EXTNAME ZERO_POINTS_3.0
+TYPE BINTABLE
+SIZE 108
+
+# elements of data structure / FITS table
+
+FIELD ZP,    	  ZP_OBS,     float,       measured zero point,       mag
+FIELD ZPo,   	  ZP_REF,     float, 	   nominal zero point,        mag
+FIELD dZP,   	  ZP_ERR,     float, 	   error on zero point,       mag
+FIELD K,     	  C_AIRMASS,  float, 	   airmass coeff,             mag per airmass
+FIELD X,     	  C_COLOR,    float, 	   color coeff,               mag per mag
+FIELD tstart,	  START_TIME, e_time,      start time of measurement, seconds since 1 Jan 1970 UT
+FIELD tstop, 	  STOP_TIME,  e_time,      stop time of measurement,  seconds since 1 Jan 1970 UT
+FIELD c1,    	  C1_CODE,    short, 	   code 1 for color,          photcode
+FIELD c2,    	  C2_CODE,    short, 	   code 2 for color,          photcode
+FIELD photcode,   PHOTCODE,   short, 	   photcode,                  photcode
+FIELD label,      LABEL,      char[64],    data label
+FIELD refcode,    REFCODE,    rawshort,	   photcode,                  photcode
+FIELD Ntime,      N_TIME,     int, 	   number of times
+FIELD Nmeas,      N_MEAS,     int, 	   number of measurements
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/ps1_dev_0.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/ps1_dev_0.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/ps1_dev_0.d	(revision 22322)
@@ -0,0 +1,24 @@
+# name of structure type
+STRUCT  PS1_DEV_0
+EXTNAME PS1_DEV_0
+TYPE    BINTABLE
+SIZE    64
+
+# elements of data structure / FITS table
+FIELD detID,    IPP_IDET,      	  unsigned int, detection ID
+FIELD X,      	X_PSF,    	  float,    x coord,              pixels
+FIELD Y,      	Y_PSF,    	  float,    y coord,              pixels
+FIELD dX,      	X_PSF_SIG,    	  float,    x coord error,        pixels
+FIELD dY,      	Y_PSF_SIG,    	  float,    y coord error,        pixels
+FIELD M,      	PSF_INST_MAG,     float,    inst mags,            mags
+FIELD dM,     	PSF_INST_MAG_SIG, float,    inst mag error,       mags
+FIELD Mpeak,    PEAK_FLUX_AS_MAG, float,    inst mag error,       mags
+FIELD sky,    	SKY,              float,    sky flux,             cnts/sec
+FIELD dSky,    	SKY_SIG,          float,    sky flux errorf       cnts/sec
+FIELD psfChisq, PSF_CHISQ,        float,    psf fit chisq
+FIELD fx,     	PSF_WIDTH_X,      float,    semi-major,           pixels
+FIELD fy,     	PSF_WIDTH_Y,      float,    semi-minor,           pixels
+FIELD df,     	PSF_THETA,        float,    ellipse angle,        degrees
+FIELD psfQual, 	PSF_QF,           float,    quality factor
+FIELD nFrames, 	N_FRAMES,         short,    images overlapping peak
+FIELD dummy,  	DUMMY,            short,    padding
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/ps1_dev_1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/ps1_dev_1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/ps1_dev_1.d	(revision 22322)
@@ -0,0 +1,26 @@
+# name of structure type
+STRUCT  PS1_DEV_1
+EXTNAME PS1_DEV_1
+TYPE    BINTABLE
+SIZE    72
+
+# elements of data structure / FITS table
+FIELD detID,     IPP_IDET,     	   unsigned int, detection ID
+FIELD X,      	 X_PSF,    	   float,    x coord,              pixels
+FIELD Y,      	 Y_PSF,    	   float,    y coord,              pixels
+FIELD dX,      	 X_PSF_SIG,    	   float,    x coord error,        pixels
+FIELD dY,      	 Y_PSF_SIG,    	   float,    y coord error,        pixels
+FIELD M,      	 PSF_INST_MAG,     float,    inst mags,            mags
+FIELD dM,     	 PSF_INST_MAG_SIG, float,    inst mag error,       mags
+FIELD Mpeak,     PEAK_FLUX_AS_MAG, float,    inst mag error,       mags
+FIELD sky,    	 SKY,              float,    sky flux,             cnts/sec
+FIELD dSky,    	 SKY_SIG,          float,    sky flux errorf       cnts/sec
+FIELD psfChisq,  PSF_CHISQ,        float,    psf fit chisq
+FIELD crNsigma,  CR_NSIGMA,        float,    Nsigma deviations from PSF to CF
+FIELD extNsigma, EXT_NSIGMA,       float,    Nsigma deviations from PSF to EXT
+FIELD fx,     	 PSF_WIDTH_X,      float,    semi-major,           pixels
+FIELD fy,     	 PSF_WIDTH_Y,      float,    semi-minor,           pixels
+FIELD df,     	 PSF_THETA,        float,    ellipse angle,        degrees
+FIELD psfQual, 	 PSF_QF,           float,    quality factor
+FIELD nFrames, 	 N_FRAMES,         short,    images overlapping peak
+FIELD flags,  	 FLAGS,            short,    padding
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/regimage.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/regimage.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/regimage.d	(revision 22322)
@@ -0,0 +1,40 @@
+STRUCT  RegImage
+EXTNAME IMAGE_DATABASE
+TYPE    BINTABLE
+SIZE    360
+
+# elements of data structure / FITS table
+
+FIELD filename,         FILE,       char[64],     filename in db
+FIELD pathname,     	PATH,       char[128],    fullpath in db
+FIELD filter,       	FILTER,     char[32],     filter name
+FIELD instrument,   	INSTRUMENT, char[32],     instrument
+FIELD ccd,	        CCD,        char,   	  ccd identifier
+FIELD mode,	        MODE,       char,   	  mef/split/etc
+FIELD type,	        TYPE,       char,   	  object/flat/bias/etc
+FIELD flag,	        FLAG,       char,   	  data flags
+FIELD seqtime,          SEQTIME,    float,        exposure time per slice,   seconds
+FIELD seq,	        SEQ,        char,   	  sequence number
+FIELD junk,	        JUNK,       char[19],     space for expansion
+FIELD exptime,	        EXPTIME,    float,  	  exposure time,             seconds
+FIELD airmass,	        AIRMASS,    float,  	  airmass
+FIELD sky,	        SKY,        float,  	  background level,          counts / pixel
+FIELD bias,	        BIAS,       float,  	  bias level,                counts / pixel
+FIELD fwhm,	        FWHM,       float,  	  image quality,             pixels
+FIELD telfocus,	        TELFOCUS,   float,  	  telescope focus,           microns
+FIELD xprobe,	        XPROBE,     float,  	  bonnette probe x pos,      microns
+FIELD yprobe,	        YPROBE,     float,  	  bonnette probe y pos,      microns
+FIELD zprobe,	        ZPROBE,     float,  	  bonnette focus,            microns
+FIELD dettemp,	        DETTEMP,    float,  	  detector temperature,      deg celcius
+FIELD teltemp_0,       	TELTEMP0,   float,  	  other temperature,         deg celcius
+FIELD teltemp_1,       	TELTEMP1,   float,  	  other temperature,         deg celcius
+FIELD teltemp_2,       	TELTEMP2,   float,  	  other temperature,         deg celcius
+FIELD teltemp_3,       	TELTEMP3,   float,  	  other temperature,         deg celcius
+FIELD rotangle,	        ROTANGLE,   float,  	  camera rotation angle,     degrees
+FIELD ra,	        RA,         float,  	  image ra,                  degrees
+FIELD dec,	        DEC,        float,  	  image dec,                 degrees
+FIELD obstime,	        OBS_TIME,   e_time, 	  time of measurement,       seconds since 01 Jan 1970 UT
+FIELD regtime,	        REG_TIME,   e_time, 	  time of registration,      seconds since 01 Jan 1970 UT
+
+# take care of the memory padding boundaries when using 'junk' for new elements
+# which are not of type char!
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-elixir.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-elixir.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-elixir.d	(revision 22322)
@@ -0,0 +1,9 @@
+STRUCT  SecFilt_Elixir
+EXTNAME DVO_SECFILT_ELIXIR
+TYPE    BINTABLE
+SIZE    6
+
+# elements of data structure / FITS table
+FIELD  M,  MAG,      short,                other mags,       millimags
+FIELD  Xm, MAG_CHI,  short,                chisq on mag
+FIELD  dM, MAG_ERR,  short,                scatter on mag
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-loneos.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-loneos.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-loneos.d	(revision 22322)
@@ -0,0 +1,8 @@
+STRUCT  SecFilt_Loneos
+EXTNAME DVO_SECFILT_LONEOS
+TYPE    BINTABLE
+SIZE    4
+
+# elements of data structure / FITS table
+FIELD  M,  MAG,      short,                other mags,       millimags
+FIELD  Xm, MAG_CHI,  short,                chisq on mag
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-panstarrs-dev-0.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-panstarrs-dev-0.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-panstarrs-dev-0.d	(revision 22322)
@@ -0,0 +1,13 @@
+STRUCT       SecFilt_Panstarrs_DEV_0
+EXTNAME      DVO_SECFILT_PANSTARRS_DEV_0
+TYPE         BINTABLE
+SIZE         16
+DESCRIPTION  DVO SecFilt : Secondary Filter Data 
+
+# elements of data structure / FITS table
+FIELD  M,     MAG,      float,                other mags,       mags
+FIELD  dM,    MAG_ERR,  float,                scatter on mag,   mags
+FIELD  Xm,    MAG_CHI,  short,                chisq on mag,     [100*log(value)]
+FIELD  Ncode, NCODE,    short,                number of detections in band
+FIELD  Nused, NUSED,    short,                number of detections used in average
+FIELD  dummy, JUNK,     short,                place holder
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-panstarrs-dev-1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-panstarrs-dev-1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-panstarrs-dev-1.d	(revision 22322)
@@ -0,0 +1,13 @@
+STRUCT       SecFilt_Panstarrs_DEV_1
+EXTNAME      DVO_SECFILT_PANSTARRS_DEV_1
+TYPE         BINTABLE
+SIZE         16
+DESCRIPTION  DVO SecFilt : Secondary Filter Data 
+
+# elements of data structure / FITS table
+FIELD  M,     MAG,      float,                other mags,       mags
+FIELD  dM,    MAG_ERR,  float,                scatter on mag,   mags
+FIELD  Xm,    MAG_CHI,  short,                chisq on mag,     [100*log(value)]
+FIELD  Ncode, NCODE,    short,                number of detections in band
+FIELD  Nused, NUSED,    short,                number of detections used in average
+FIELD  dummy, JUNK,     short,                place holder
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-ps1-dev-1.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-ps1-dev-1.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-ps1-dev-1.d	(revision 22322)
@@ -0,0 +1,13 @@
+STRUCT       SecFilt_PS1_DEV_1
+EXTNAME      DVO_SECFILT_PS1_DEV_1
+TYPE         BINTABLE
+SIZE         16
+DESCRIPTION  DVO SecFilt : Secondary Filter Data 
+
+# elements of data structure / FITS table
+FIELD  M,     MAG,      float,                other mags,       mags
+FIELD  dM,    MAG_ERR,  float,                scatter on mag,   mags
+FIELD  Xm,    MAG_CHI,  short,                chisq on mag,     [100*log(value)]
+FIELD  Ncode, NCODE,    short,                number of detections in band
+FIELD  Nused, NUSED,    short,                number of detections used in average
+FIELD  dummy, JUNK,     short,                place holder
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-ps1-dev-2.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-ps1-dev-2.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt-ps1-dev-2.d	(revision 22322)
@@ -0,0 +1,13 @@
+STRUCT       SecFilt_PS1_DEV_2
+EXTNAME      DVO_SECFILT_PS1_DEV_2
+TYPE         BINTABLE
+SIZE         16
+DESCRIPTION  DVO SecFilt : Secondary Filter Data 
+
+# elements of data structure / FITS table
+FIELD  M,     MAG,      float,                other mags,       mags
+FIELD  dM,    MAG_ERR,  float,                scatter on mag,   mags
+FIELD  Xm,    MAG_CHI,  short,                chisq on mag,     [100*log(value)]
+FIELD  Ncode, NCODE,    short,                number of detections in band
+FIELD  Nused, NUSED,    short,                number of detections used in average
+FIELD  dummy, JUNK,     short,                place holder
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/secfilt.d	(revision 22322)
@@ -0,0 +1,13 @@
+STRUCT       SecFilt
+EXTNAME      DVO_SECFILT
+TYPE         BINTABLE
+SIZE         16
+DESCRIPTION  DVO SecFilt : Secondary Filter Data 
+
+# elements of data structure / FITS table
+FIELD  M,     MAG,      float,                other mags,       mags
+FIELD  dM,    MAG_ERR,  float,                scatter on mag,   mags
+FIELD  Xm,    MAG_CHI,  short,                chisq on mag,     [100*log(value)]
+FIELD  Ncode, NCODE,    short,                number of detections in band
+FIELD  Nused, NUSED,    short,                number of detections used in average
+FIELD  dummy, JUNK,     short,                place holder
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/smpdata.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/smpdata.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/smpdata.d	(revision 22322)
@@ -0,0 +1,19 @@
+# name of structure type
+STRUCT  SMPData
+EXTNAME SMPFILE
+TYPE    BINTABLE
+SIZE    44
+
+# elements of data structure / FITS table
+FIELD X,      X_PIX,      float,    x coord,              pixels
+FIELD Y,      Y_PIX,      float,    y coord,              pixels
+FIELD M,      MAG_RAW,    float,    inst mags,            mags
+FIELD dM,     MAG_ERR,    float,    inst mag error,       mags
+FIELD Mgal,   MAG_GAL,    float,    galaxy mag,           mags
+FIELD Map,    MAG_AP,     float,    aperture mag,         mags
+FIELD sky,    LOG_SKY,    float,    log-10 of sky,        cnts/sec
+FIELD fx,     FWHM_X,     float,    semi-major,           pixels
+FIELD fy,     FWHM_Y,     float,    semi-minor,           pixels
+FIELD df,     THETA,      float,    ellipse angle,        degrees
+FIELD dophot, DOPHOT,     char,     dophot type,          none
+FIELD dummy,  DUMMY,      char[3],  padding,              none
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/spectrum-ascii.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/spectrum-ascii.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/spectrum-ascii.d	(revision 22322)
@@ -0,0 +1,29 @@
+# name of structure type
+STRUCT  SpectrumASCII
+EXTNAME SPECTRUM
+TYPE    TABLE
+SIZE    262
+
+# elements of data structure / FITS table
+FIELD filename,   FILENAME,   char[32],    filename in db,        
+FIELD pathname,   PATHNAME,   char[64],    fullpath in db,        
+FIELD instrument, INSTRUMENT, char[16],    instrument,            
+FIELD telescope,  TELESCOPE,  char[16],    telescope,             
+FIELD objname,    OBJNAME,    char[16],    object name,           
+FIELD extname,    EXTNAME,    char[16],    extname in file,       
+
+FIELD ra,         RA,         float[10.6], ra,                    degrees
+FIELD dec,        DEC,        float[10.6], dec,                   degrees
+FIELD exptime,    EXPTIME,    float[6.1],  exposure time,         seconds
+FIELD airmass,    AIRMASS,    float[5.3],  airmass,               none
+FIELD Ws,         Ws,         float[7.2],  spectral range start,  Angstrom
+FIELD We,         We,         float[7.2],  spectral range end,    Angstrom
+FIELD dW,         dW,         float[7.2],  spectral resolution,   Angstrom / pix
+	       			      
+FIELD Nspec,      NSPECTRA,   int[3],      number of spectra,     none
+FIELD obstime,    OBS_TIME,   char[20],    time of measurement,   yyyy/mm/dd
+FIELD regtime,    REG_TIME,   char[20],    time of registration,  yyyy/mm/dd
+	       			      
+FIELD mode,       MODE,       int[2],      phu/mef/ext,           
+FIELD state,      STATE,      int[2],      raw/wav/flx/etc,       
+FIELD flag,       FLAG,       int[3],      status flags,          
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/spectrum.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/spectrum.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/def/spectrum.d	(revision 22322)
@@ -0,0 +1,31 @@
+STRUCT  Spectrum
+EXTNAME SPECTRUM_DATABASE
+TYPE    BINTABLE
+SIZE    216
+
+# elements of data structure / FITS table
+	       			      
+FIELD ra,         RA,         float,     ra,                    degrees
+FIELD dec,        DEC,        float,     dec,                   degrees
+FIELD exptime,    EXPTIME,    float,     exposure time,         seconds
+FIELD airmass,    AIRMASS,    float,     airmass,               
+FIELD Ws,         Ws,         float,     spectral range start,  Angstrom
+FIELD We,         We,         float,     spectral range end,    Angstrom
+FIELD dW,         dW,         float,     spectral resolution,   Angstrom / pix
+	       			      
+FIELD Nspec,      NSPECTRA,   int,       number of spectra,     
+FIELD obstime,    OBS_TIME,   int,       time of measurement,   seconds since 1 Jan 1970 UT
+FIELD regtime,    REG_TIME,   int,       time of registration,  seconds since 1 Jan 1970 UT
+	       			      
+FIELD mode,       MODE,       char,      phu/mef/ext           
+FIELD state,      STATE,      char,      raw/wav/flx/etc       
+FIELD flag,       FLAG,       char,      status flags
+FIELD extra,      EXTRA,      char[13],  room for expansion
+
+FIELD pathname,   PATHNAME,   char[64],  fullpath in db
+FIELD filename,   FILENAME,   char[32],  filename in db
+FIELD extname,    EXTNAME,    char[16],  extname in file,       
+
+FIELD instrument, INSTRUMENT, char[16],  instrument,            
+FIELD telescope,  TELESCOPE,  char[16],  telescope,             
+FIELD objname,    OBJNAME,    char[16],  object name,           
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,44 @@
+
+- libautocode 1.5
+  * converted to gfits APIs (forces libfits 1.6)
+  * fixed comma errors in average tables
+
+- libautocode 1.4
+  * added average-pmtest.d
+  * added autocode.tex, tex interpretation
+  * added proper-motion, etc to Stars.d
+  * added proper-motion, etc to average.d
+  * DESCRIPTION element for tex tables
+
+- libautocode 1.3
+  * added SkyRegion.d
+  * added AddstarClientOptions
+  * fixed e_void ptr in generate
+  * cleaned up signed/unsigned inconsistencies
+
+libautocode-1-2
+  added support for Comm functions (Send/Recv structure)
+  changed internal precision to doubles (RA/DEC) and floats (MAG)
+  dropped all internal millimag representations
+  made panstarrs format higher precisions than elixir
+  added the following new structures to support multiple data formats:
+     average-elixir
+     image-ascii
+     image-elixir
+     image-loneos
+     image-panstarrs
+     measure-elixir
+     measure-panstarrs
+     secfilt-elixir
+     secfilt-loneos
+     secfilt-panstarrs
+  added these structures for addstar client/server comms
+     AddstarClientOptions
+     GSCRegion
+     Stars
+
+libautocode-1-1
+  added the measure, average entries to autocode tables
+
+libautocode-1-0
+  import to CVS
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/doc/autocode.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/doc/autocode.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/doc/autocode.txt	(revision 22322)
@@ -0,0 +1,71 @@
+
+This directory contains a collection of autocoded FITS I/O routines
+used to define FITS Table DB functions.  
+
+The autocoder program is a perl script 'generate'.  This scripts takes
+as input a schema file and a template source code file, and produces
+an output file from these two inputs.  A single schema file is meant
+to be associated with a single data concept which is being coded.  For
+example, in the case of the FITS DB tables, a single schema file
+defines a single FITS table, or equivalently, a single C structure
+representing a row in that FITS table.
+
+Schema File
+
+The schema file currently consists of two types of information.
+
+First, there are simple keyword / name pairs which the autocoder
+simply applies as a direct replacement anywhere in the template.  For
+example, the keyword NAME replaces any instance of "$NAME" in the
+template with the corresponding value.  
+
+Second, there is a special type of keyword: FIELD.  The FIELD entry
+defines an entry in the data structure.  The FIELD entry is followed
+by the following comma-separated pieces of information:
+
+- the element name
+- the element data type
+- the element description
+- the element physical unit
+
+Template File
+
+The template file is a source code in whatever language is desired.
+Within the template, the defined keywords may be used as desired,
+wherever needed.  In addition, there are special directives which
+invoke additional special behavior.  All special directives have the
+form of comments within their target language.  They are replaced with
+a block of code in that target language, normally one which iterates
+over the FIELD elements of the schema file.  As such, the directive
+appropriate for one language should not be used within code for a
+different language.  Here is a list of existing special directives:
+
+/** STRUCT DEFINITION **/
+
+This tells the autocoder to create a structure definition for the
+schema.  The structure definition creates a structure with elements
+based on the FIELD entries, with the name $NAME (NOTE: this is perhaps
+making too much of an assumption.  we could require the template to
+provide the wrapper: "typedef struct { } $NAME;" and have the
+autocoder only insert the field lines).  
+
+/** TABLE DEFINITION **/
+
+This tells the autocoder to output the lines which add a column to an
+exiting empty table.  The assumption is that the template contains
+code to initialize a table.  It then invokes the table definition
+code, which defines each of the appropriate columns.  This is perhaps
+followed by some code which finalizes the table definition.  (Note:
+the code output by the autocoder in this block is not very flexible:
+it the current example, it explicitly uses the Elixir FITS Table
+functions, and refers to some data elements in the template code.  The
+latter could possibly be more abstracted with the keyword / value
+pairs, but in any case, the autocoded lines will have to be
+constructed somewhat by hand for a specific API set.
+
+/** BYTE SWAP **/
+
+This tells the autocoder to output code defining the byte swaps
+appropriat to the data structure.  Again, the resulting code depends a
+bit on the intended use and API set.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/doc/notes.txt	(revision 22322)
@@ -0,0 +1,138 @@
+
+FITS I/O APIs
+
+  - I/O to a binary FITS table
+  - one extension per file?
+  - no locking
+  - always read/write to/from matched structure
+  - user needs to supply idiosyncratic conversions
+
+  - basic APIs:
+    - fits_read_datatable_NAME
+    - fits_write_datatable_NAME
+    - fits_IO_convert_NAME
+    - fits_fread_datatable_NAME
+    - fits_fwrite_datatable_NAME
+
+FITS db APIs
+
+Basic Rules:
+
+  - All db files are Binary FITS tables
+  - There is only one extension per file
+  - The PHU contains basic information like status & date
+  - There is no data array for the PHU (NAXIS = 0)
+
+typedef struct {
+  FILE *f;
+  char *filename;
+  Header header;
+  FTable ftable;
+  int Nrows;
+  int Ncols;
+  int Nbyte;
+  int lockstate;
+  int lockreq;
+  NAME *data;
+} NAME_DB;
+
+NAME_DB *NAME_DB_Init (NAME_DB *db);
+
+  - 
+
+NAME_DB *NAME_DB_Load (char *filename, int lockreq);
+
+  - lock the database file
+  - load PHU
+  - load the header
+  - validate the EXTNAME
+  - validate the header
+  - load the data into ftable
+  - convert to memory format
+
+  * if SOFT & database file does not exist (file is empty), 
+    - return structure w/ 0 entries
+
+  * if HARD & database file does not exist (file is empty), 
+    - open, lock, return structure w/ 0 entries
+
+  * if database file cannot be locked
+    - return an error (NULL)
+
+  * if header does not validate: 
+    - unlock file
+    - return an error (NULL)
+
+NAME_DB *NAME_DB_Loadset (char *filename, int lockreq, int start, int Nrows);
+
+  - lock the database file
+  - load PHU
+  - load the header
+  - validate the EXTNAME
+  - validate the header
+  - load the data into vtable
+  - convert to memory format
+
+  * if database file does not exist (file is empty), 
+    - open, lock, return structure w/ 0 entries
+  * if requested entries don't exist
+    - unlock file
+    - return an error (NULL)
+
+NAME_DB *NAME_DB_Save ();
+
+  - if Nrows = 0, delete the file
+
+  - write PHU
+  - write header
+  - write ftable
+
+  - if vtable: 
+    - update 
+  - if ftable: 
+    - update 
+
+NAME_DB *NAME_DB_Saveset ();
+
+  - if Nrows = 0, delete the file
+
+  - write PHU
+  - write header
+  - write ftable
+
+  - if vtable: 
+    - update 
+  - if ftable: 
+    - update 
+
+NAME_DB *NAME_DB_Free ();
+
+  - unlock file
+
+
+---
+
+DVO tables
+
+I am converting all DVO tables to have an internal representation of
+the table with the necessary precision (ie, double for RA & DEC, etc),
+and a set of external representations in which the precision and
+available columns are truncated to fit a specific representation.  the
+autocode tables then include the internal name (ie, measure.d) and the
+external versions (ie, measure-elixir.d, etc).  
+
+To pursue this, I have modifed the definion of measure.d to replace,
+eg, dR with dR_PS.  I am now compiling all of ohana and looking for
+places where the new name is needed in the code, stopping the
+compilation.  (In the process, I am being forced to clean up the
+relphot - at least - coding problems: unused variables, undeclared
+functions, etc).  After I have made the changes, I will search for all
+instances of dR_PS and return them to their original.
+
+The only things to watch for in this process are units for the
+values.  In the older code, the value of measure.dR was interpreted as
+1/100th of an arcsec.  Everywhere this was the interpretation must be
+changed to make the value represent 1 arcsec.  This will also be an
+issue for the internal magnitude representations, which all use
+millimags...
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/generate
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/generate	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libautocode/generate	(revision 22322)
@@ -0,0 +1,384 @@
+#!/usr/bin/env perl
+
+$VERBOSE = 0;
+if ($ARGV[0] eq "-v") { $VERBOSE = 1; shift @ARGV; }
+if (@ARGV != 3) { die "generate (schema) (template) (output)\n"; }
+
+$schema   = $ARGV[0];
+$template = $ARGV[1];
+$output   = $ARGV[2];
+
+&parse_schema;
+&parse_template;
+exit 0;
+
+sub parse_schema {
+    open (FILE, "$schema");
+    @list = <FILE>;
+    close (FILE);
+
+    $NAME    = "";
+    $FILE    = "";
+    $Nfield  = 0;
+    $TIMEOUT = 0;
+
+    &init_key ("DESCRIPTION");
+    &init_key ("TIMEOUT");
+    &init_key ("EXTNAME");
+    &init_key ("STRUCT");
+    &init_key ("NAME");
+    &init_key ("FILE");
+    &init_key ("SIZE");
+    &init_key ("TYPE");
+
+    foreach $line (@list) {
+	chop $line;
+	($key, $value) = split (" ", $line, 2);
+	
+	# strip white space from the following
+	if ($key eq "TYPE")    { ($value) = $value =~ m|\s*(\S+)\s*|; }
+	if ($key eq "SIZE")    { ($value) = $value =~ m|\s*(\S+)\s*|; }
+	if ($key eq "STRUCT")  { ($value) = $value =~ m|\s*(\S+)\s*|; }
+	if ($key eq "EXTNAME") { ($value) = $value =~ m|\s*(\S+)\s*|; }
+
+	&set_keypair ($key, $value);
+
+	# these are used internally (not just a replacement)
+	if ($key eq "TYPE")    { $TYPE = $value;   }
+	if ($key eq "SIZE")    { $SIZE = $value;   }
+	if ($key eq "STRUCT")  { $STRUCT = $value; }
+
+	# not a simple key/value entry
+ 	if (($key eq "FIELD") || ($key eq "SUBSTRUCT") || ($key eq "SUBFIELD")) {
+	    ($element, $field, $format, $comment, $unit) = split (/,\s+/, $value, 5);
+	    if ($VERBOSE) { printf "%-20s %-20s %-15s %-35s %-10s\n", $element, $field, $format, $comment, $unit; }
+	    push @element, $element;
+	    push @field,   $field;
+	    push @format,  $format;
+	    push @comment, $comment;
+	    push @unit,    $unit;
+	    push @mode,    $key;
+	}
+    }
+    $Nexpect = 0;
+    if ($TYPE eq "BINTABLE") { $Nexpect = &count_bintablesize; }
+    if ($TYPE eq "TABLE")    { $Nexpect = &count_tablesize;    }
+    if (! $Nexpect) { die "missing valid TYPE\n"; }
+    if ($Nexpect != $SIZE) { die "size mismatch: $Nexpect vs $SIZE\n"; }
+}
+
+sub parse_template {
+    open (FILE, $template);
+    @list = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$output");
+
+    foreach $line (@list) {
+	
+	&check_keypairs;
+
+	print FILE $line;
+
+	# fill in structure
+	if ($line =~ m|/\*\* STRUCT DEFINITION \*\*/|) {
+	    &write_structure;
+	}
+
+	# fill in latex table description
+	if ($line =~ m|%%% LATEX TABLE DEFINITION|) {
+	    &write_latex;
+	}
+
+	# fill in structure
+	if ($line =~ m|/\*\* TABLE DEFINITION \*\*/|) {
+	    if ($TYPE eq "BINTABLE") { &write_bintabledefs; }
+	    if ($TYPE eq "TABLE")    { &write_tabledefs; }
+	}
+
+	# fill in swaps
+	if ($line =~ m|/\*\* BYTE SWAP \*\*/|) {
+	    &write_byteswaps;
+	}
+    }
+    close (FILE);
+}
+
+sub write_bintabledefs {
+
+    for ($i = 0; $i < @field; $i++) {
+	# skip SUBSTRUCT type of entries:
+	if ($mode[$i] eq "SUBSTRUCT") { next; }
+
+	($type, $Np) = &get_type_array ($format[$i]);
+	
+	# rawshort is a short without byteswapping
+
+	$pt1 = 0;
+	if ($type eq "char")          { $pt1 = "A"; }
+	if ($type eq "byte")   	      { $pt1 = "B"; }
+	if ($type eq "unsigned char") { $pt1 = "B"; }
+	if ($type eq "rawshort")      { $pt1 = "I"; }
+	if ($type eq "short")  	      { $pt1 = "I"; }
+	if ($type eq "unsigned short"){ $pt1 = "I"; }
+	if ($type eq "int")    	      { $pt1 = "J"; }
+	if ($type eq "unsigned int")  { $pt1 = "J"; }
+	if ($type eq "e_time") 	      { $pt1 = "J"; }
+	if ($type eq "float")  	      { $pt1 = "E"; }
+	if ($type eq "double") 	      { $pt1 = "D"; }
+
+	if ($type eq "e_void") 	      { $pt1 = "B"; $Np = 8*$Np; }
+	# e_void is a 64 bit pointer, cast to size_t.  its value is not loaded
+	# from the table.
+
+	if (!$pt1) { die "unknown type $type"; }
+
+	if ($Np == 1) {
+	    $pt2 = $pt1;
+	} else {
+	    $pt2 = sprintf "%d%s", $Np, $pt1;
+	}
+
+	printf FILE "  gfits_define_bintable_column (header, ";
+	printf FILE "%-8s",               "\"$pt2\", ";
+	printf FILE "%-20s",              "\"$field[$i]\", ";
+	printf FILE "%-35s",              "\"$comment[$i]\", ";
+	printf FILE "%-20s 1.0, 0.0);\n", "\"$unit[$i]\", ";
+    }
+
+}
+
+sub write_latex {
+
+    for ($i = 0; $i < @field; $i++) {
+	# skip SUBSTRUCT type of entries:
+	if ($mode[$i] eq "SUBSTRUCT") { next; }
+
+	($type, $Np) = &get_type_array ($format[$i]);
+
+	if ($type eq "e_time") 	      { $type = "unsigned int"; }
+
+	# print STDOUT "$field[$i] .. $type[$i] .. $comment[$i] .. $unit[$i]\n";
+
+        printf FILE "%-20s & ",       "\\code{$field[$i]} ";
+	printf FILE "%-20s & ",       "$type";
+	printf FILE "%-35s & ",       "$comment[$i]";
+	printf FILE "%-20s \\\\ \n",  "$unit[$i]";
+    }
+}
+
+sub get_type_array {
+    
+    my ($format) = $_[0];
+    my ($type);
+    my ($Np, $N1, $N2, $N3);
+
+    $type = $N1 = $N2 = $N3 = $Np = 0;
+    if (!$type) {
+	($type, $N1, $N2, $N3) = $format =~ m|^(.w+)\[(\d+)\]\[(\d+)\]\[(\d+)\]|;
+	$Np = $N1*$N2*$N3;
+    }
+    if (!$type) {
+	($type, $N1, $N2)      = $format =~ m|^(.+)\[(\d+)\]\[(\d+)\]|;
+	$Np = $N1*$N2;
+    } 
+    if (!$type) {
+	($type, $N1)           = $format =~ m|^(.+)\[(\d+)\]|;
+	$Np = $N1;
+    }
+    if (!$type) { 
+	$type                  = $format; 
+	$Np = 1;
+    }
+    # print "type: $type, Np: $Np\n";
+    if ($Np == 0) { die "syntax error in format/array\n"; }
+    return ($type, $Np);
+}
+
+sub write_tabledefs {
+
+    for ($i = 0; $i < @field; $i++) {
+	# skip SUBSTRUCT type of entries:
+	if ($mode[$i] eq "SUBSTRUCT") { next; }
+
+	($type, $N1, $N2)      = $format[$i] =~ m|^(\w+)\[(\d+)\]\[(\d+)\]|;
+	if ($N2) { die "ASCII table cannot have multi-valued column"; }
+
+	($type, $length) = $format[$i] =~ m|^(\w+)\[([\d\.]+)\]|;
+	if (!$type && !$length) { die "format must be specified for ASCII table"; }
+
+	$pt1 = 0;
+	if ($type eq "char")   	      { $pt1 = sprintf "A%s", $length; }
+	if ($type eq "byte")   	      { $pt1 = sprintf "I%s", $length; }
+	if ($type eq "unsigned char") { $pt1 = sprintf "I%s", $length; }
+	if ($type eq "rawshort")      { $pt1 = sprintf "I%s", $length; }
+	if ($type eq "short")  	      { $pt1 = sprintf "I%s", $length; }
+	if ($type eq "unsigned short"){ $pt1 = sprintf "I%s", $length; }
+	if ($type eq "int")    	      { $pt1 = sprintf "I%s", $length; }
+	if ($type eq "unsigned int")  { $pt1 = sprintf "I%s", $length; }
+	if ($type eq "e_time") 	      { $pt1 = sprintf "I%s", $length; }
+	if ($type eq "float")  	      { $pt1 = sprintf "F%s", $length; }
+	if ($type eq "double") 	      { $pt1 = sprintf "F%s", $length; }
+
+	if ($type eq "e_void") 	      { $pt1 = sprintf "I%s", $length; }
+
+	if (!$pt1) { die "unknown type $type"; }
+
+	printf FILE "  gfits_define_table_column (header, ";
+	printf FILE "%-8s",      "\"$pt1\", ";
+	printf FILE "%-20s",     "\"$field[$i]\", ";
+	printf FILE "%-35s",     "\"$comment[$i]\", ";
+	printf FILE "%-20s);\n", "\"$unit[$i]\"";
+    }
+
+}
+
+sub write_structure {
+    print FILE "typedef struct {\n";
+    for ($i = 0; $i < @element; $i++) {
+	# skip SUBFIELD entries
+	if ($mode[$i] eq "SUBFIELD") { next; }
+
+	# here we only want to match the pattern [1][2][3]..[N]
+	($type, $array) = $format[$i] =~ m|^(\w+)(\[.*\])|;
+	# print "type: $type, array: $array\n";
+	if (!$type && !$array) { $type = $format[$i]; }
+
+	if ($array && (($TYPE eq "BINTABLE") || ($type eq "char"))) {
+	    $pt2 = sprintf "%s%s;", $element[$i], $array;
+	} else {
+	    $pt2 = sprintf "%s;", $element[$i];
+	}
+
+	if ($unit[$i] eq "") {
+	    $pt3 = sprintf "// %s", $comment[$i];
+	} else {
+	    $pt3 = sprintf "// %s (%s)", $comment[$i], $unit[$i];
+	}
+	printf FILE "  %-16s %-21s %s\n", $type, $pt2, $pt3;
+    }    
+    print FILE "} $STRUCT;\n";
+}
+
+# this does not work with ASCII tables, 
+# but should not be needed for ASCII tables!
+sub write_byteswaps {
+    if ($TYPE eq "TABLE") {
+	printf FILE "/*** no byteswaps for ASCII tables ***/\n";
+	return;
+    }
+    $N = 0;
+    for ($i = 0; $i < @field; $i++) {
+	# skip SUBSTRUCT type of entries:
+	if ($mode[$i] eq "SUBSTRUCT") { next; }
+
+	($type, $Np) = &get_type_array ($format[$i]);
+
+	# rawshort is a patch for old photreg tables: provide a fix for the tables
+	# some photreg tables were not byteswapped for certain columns
+
+	$n = 0;
+	if ($type eq "char") 	      { $N +=   $Np; next; }
+	if ($type eq "byte") 	      { $N +=   $Np; next; }
+	if ($type eq "unsigned char") { $N +=   $Np; next; }
+	if ($type eq "rawshort")      { $N += 2*$Np; next; }
+	if ($type eq "short")  	      { $T = "BYTE"; $n = 2; }
+	if ($type eq "unsigned short"){ $T = "BYTE"; $n = 2; }
+	if ($type eq "int")    	      { $T = "WORD"; $n = 4; }
+	if ($type eq "unsigned int")  { $T = "WORD"; $n = 4; }
+	if ($type eq "e_time") 	      { $T = "WORD"; $n = 4; }
+	if ($type eq "e_void") 	      { $T = "DBLE"; $n = 8; }
+	if ($type eq "float")  	      { $T = "WORD"; $n = 4; }
+	if ($type eq "double") 	      { $T = "DBLE"; $n = 8; }
+	if (!$n) { die "unknown type $type"; }
+	for ($j = 0; $j < $Np; $j++) {
+	    printf FILE "    SWAP_%s (%d); // %s\n", $T, $N, $field[$i];
+	    $N += $n;
+	}
+    }
+}
+
+sub count_bintablesize {
+
+    $Nbytes = 0;
+    for ($i = 0; $i < @field; $i++) {
+	# skip SUBSTRUCT type of entries:
+	if ($mode[$i] eq "SUBSTRUCT") { next; }
+
+	# add [\d\.] to match ascii-type formats
+	($type, $Np) = &get_type_array ($format[$i]);
+
+	$valid = 0;
+	if ($type eq "char")   	       { $Nbytes += 1*$Np; $valid = 1; }
+	if ($type eq "byte")   	       { $Nbytes += 1*$Np; $valid = 1; }
+	if ($type eq "unsigned char")  { $Nbytes += 1*$Np; $valid = 1; }
+	if ($type eq "rawshort")       { $Nbytes += 2*$Np; $valid = 1; }
+	if ($type eq "short")  	       { $Nbytes += 2*$Np; $valid = 1; }
+	if ($type eq "unsigned short") { $Nbytes += 2*$Np; $valid = 1; }
+	if ($type eq "int")    	       { $Nbytes += 4*$Np; $valid = 1; }
+	if ($type eq "unsigned int")   { $Nbytes += 4*$Np; $valid = 1; }
+	if ($type eq "e_time") 	       { $Nbytes += 4*$Np; $valid = 1; }
+	if ($type eq "e_void") 	       { $Nbytes += 8*$Np; $valid = 1; }
+	if ($type eq "float")  	       { $Nbytes += 4*$Np; $valid = 1; }
+	if ($type eq "double") 	       { $Nbytes += 8*$Np; $valid = 1; }
+	if (!$valid) { die "unknown type $type"; }
+    }
+    return ($Nbytes);
+}
+
+sub count_tablesize {
+
+    $Nbytes = 0;
+    for ($i = 0; $i < @field; $i++) {
+	# skip SUBSTRUCT type of entries:
+	if ($mode[$i] eq "SUBSTRUCT") { next; }
+
+	($fpt) = $format[$i] =~ m|^\w+\[([\d\.]+)\]|;
+	$length = int($fpt);
+	# print "$format[$i], $length, $fpt\n";
+	if ($length == 0) { die "ASCII table format requires field size"; }
+	$Nbytes += $length;
+    }
+    return ($Nbytes);
+}
+
+sub init_key {
+    my ($key)   = $_[0];
+
+    push @key, $key;
+    push @value, "";
+}
+
+sub set_keypair {
+    my ($i);
+    my ($key)   = $_[0];
+    my ($value) = $_[1];
+
+    for ($i = 0; $i < @key; $i++) {
+	if ($key eq $key[$i]) {
+	    if ($value[$i] ne "") { die "key is multiply defined\n"; }
+	    $value[$i] = $value;
+	    return;
+	}
+    }
+}
+
+sub check_keypairs {
+    my ($i);
+    for ($i = 0; $i < @key; $i++) {
+	# if ($VERBOSE) { print "$key[$i]  -- $value[$i]\n"; }
+	if ($line =~ m|\$$key[$i]|) {
+	    if ($value[$i] eq "") { die "missing value for required key $key[$i]\n"; }
+	    $line =~ s|\$$key[$i]|$value[$i]|g;
+	}
+    }
+}
+
+# we need to find the structure size, including padding 
+# i'm not sure I know the answer to this: it is probably 
+# the total number of bytes rounded up to the largest 
+# data item in the structure (ie, 8 for a double, etc)
+# if we have the size, then we can double check the structure
+# against the expectation at runtime.  for the moment,
+# calculate by hand and add to def.d file 
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+lib
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/Makefile	(revision 22322)
@@ -0,0 +1,117 @@
+default: install
+help:
+	@echo "make options: install libdvo clean dist"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/libdvo
+AUTO	=	$(ROOT)/src/libautocode
+LIB	= 	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	=	$(HOME)/include
+ASRC	=	$(AUTO)/src
+AINC	=	$(AUTO)/include
+ADEF	=	$(AUTO)/def
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS) -lFITS -lohana
+
+default: install
+install: $(DESTLIB)/libdvo.a $(DESTLIB)/libdvo.$(DLLTYPE)
+libdvo: $(LIB)/libdvo.$(ARCH).a $(LIB)/libdvo.$(ARCH).$(DLLTYPE)
+
+DEFS = \
+$(DESTINC)/loneos_defs.h \
+$(DESTINC)/elixir_defs.h \
+$(DESTINC)/panstarrs_dev_0_defs.h \
+$(DESTINC)/panstarrs_dev_1_defs.h \
+$(DESTINC)/ps1_dev_1_defs.h \
+$(DESTINC)/ps1_dev_2_defs.h \
+$(DESTINC)/ps1_dev_3_defs.h
+
+INCS = $(DEFS) $(DESTINC)/dvo.h $(DESTINC)/autocode.h
+
+OBJS = \
+$(SRC)/version.$(ARCH).o	 \
+$(SRC)/coordops.$(ARCH).o	 \
+$(SRC)/dvosorts.$(ARCH).o	 \
+$(SRC)/dvo_photcode_ops.$(ARCH).o \
+$(SRC)/LoadPhotcodes.$(ARCH).o   \
+$(SRC)/LoadPhotcodesText.$(ARCH).o   \
+$(SRC)/LoadPhotcodesFITS.$(ARCH).o   \
+$(SRC)/SavePhotcodesText.$(ARCH).o   \
+$(SRC)/SavePhotcodesFITS.$(ARCH).o   \
+$(SRC)/imreg_datatypes.$(ARCH).o \
+$(SRC)/mosaic_astrom.$(ARCH).o   \
+$(SRC)/fits_db.$(ARCH).o	 \
+$(SRC)/photfits.$(ARCH).o        \
+$(SRC)/dvo_image.$(ARCH).o       \
+$(SRC)/dvo_image_raw.$(ARCH).o   \
+$(SRC)/dvo_catalog.$(ARCH).o     \
+$(SRC)/dvo_catalog_raw.$(ARCH).o       \
+$(SRC)/dvo_catalog_mef.$(ARCH).o       \
+$(SRC)/dvo_catalog_split.$(ARCH).o     \
+$(SRC)/dvo_catalog_create.$(ARCH).o    \
+$(SRC)/dvo_catalog_chipcoords.$(ARCH).o \
+$(SRC)/dvo_convert.$(ARCH).o           \
+$(SRC)/dvo_convert_elixir.$(ARCH).o    \
+$(SRC)/dvo_convert_loneos.$(ARCH).o    \
+$(SRC)/dvo_convert_panstarrs_DEV_0.$(ARCH).o \
+$(SRC)/dvo_convert_panstarrs_DEV_1.$(ARCH).o \
+$(SRC)/dvo_convert_PS1_DEV_1.$(ARCH).o \
+$(SRC)/dvo_convert_PS1_DEV_2.$(ARCH).o \
+$(SRC)/dvo_convert_PS1_DEV_3.$(ARCH).o \
+$(SRC)/skyregion_io.$(ARCH).o    \
+$(SRC)/skyregion_gsc.$(ARCH).o    \
+$(SRC)/skyregion_ops.$(ARCH).o
+
+# $(SRC)/dvo_convert_panstarrs.$(ARCH).o \
+# $(SRC)/dvo_convert_pmtest.$(ARCH).o \
+
+include ../libautocode/Makefile.Targets
+
+$(OBJS): $(INCS)
+
+$(LIB)/libdvo.$(ARCH).a:  $(AOBJS) $(OBJS)
+$(LIB)/libdvo.$(ARCH).$(DLLTYPE): $(AOBJS) $(OBJS)
+
+$(DESTLIB)/libdvo.a:  $(LIB)/libdvo.$(ARCH).a
+$(DESTLIB)/libdvo.$(DLLTYPE): $(LIB)/libdvo.$(ARCH).$(DLLTYPE)
+
+# dependency rules for the autocode files
+
+$(AOBJS): $(ADEF)/autocode.c $(DESTINC)/autocode.h
+
+$(DESTINC)/autocode.h: $(AINC)/autocode.h
+	@if [ ! -d $(DESTINC) ]; then mkdir -p $(DESTINC); fi
+	rm -f $@
+	cp $< $@
+
+$(AINC)/autocode.h: $(AINCS) $(ADEF)/autocode.h $(ADEF)/common.h
+	@echo "make $@ from $^"
+	cd $(AUTO) && make $@
+
+# generic rule for AINCS
+$(AINC)/%.h : $(ADEF)/%.d $(ADEF)/autocode.h $(ADEF)/common.h
+	@echo "make $@ from $^"
+	cd $(AUTO) && make $@
+
+# generic rule for ASRC
+$(ASRC)/%.c : $(ADEF)/%.d $(ADEF)/autocode.c $(AINC)/%.h
+	@echo "make $@ from $^"
+	cd $(AUTO) && make $@
+
+# generic rule for AOBJS
+$(ASRC)/%.$(ARCH).o : $(ASRC)/%.c
+	@echo "make $@ from $^"
+	cd $(AUTO) && make $@
+
+tabletest: install
+	gcc -L$(LIBDIR) -I$(INCDIR) -o tabletest $(SRC)/tabletest.c -ldvo -lFITS -lohana -lm
+
+clean: cleandef
+cleandef:
+	cd $(AUTO) && make clean
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,20 @@
+
+- libdvo 1.3 : 2006.08.23
+  * converted to gfits APIs (forces libfits 1.6)
+  * unified DVO APIs
+  * fixed DVO API naming (dvo_catalog..., dvo_image...)
+  * fixes to polynomial conversions in coords.c
+  * less restrictive ctypes for polynomials (coords.c)
+  * fixes to dvo locking strategy
+
+- libdvo 1.2
+  * added PMTEST format
+  * added dvo_image_lock,unlock functions
+  * fixes to the skyregion support
+  * dropped _PS from average.R,D,M
+  * fixed bug in coord interpretation of CROTA2
+  * fixed reallocation logic in skyregions
+
+- libdvo 1.1
+  * added skyregion support functions and structures
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-catalogs.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-catalogs.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-catalogs.txt	(revision 22322)
@@ -0,0 +1,70 @@
+
+// APIs related to the DVO catalogs:
+
+load_catalog (Catalog *catalog, int VERBOSE);
+
+dvo_catalog_lock (Catalog *catalog, int lockmode);
+// lockmode: SOFT, XCLD, HARD
+
+dvo_catalog_unlock (Catalog *catalog);
+
+dvo_catalog_load (Catalog *catalog, int VERBOSE, int mode);
+// format: INTERNAL, LONEOS, ELIXIR, PANSTARRS, PMTEST, etc.
+// layout: RAW, MEF, SPLIT
+// mode:   READ, WRITE
+
+// returns
+//   DVO_CAT_OPEN_FAIL:  failure to lock catalog
+//   DVO_CAT_OPEN_OK:    success: file is locked and opened
+//   DVO_CAT_OPEN_EMPTY: success: file is locked but empty
+
+ the catalog is opened and the data for each of the elements (average,
+ measure, etc) is loaded if requested.  Whether or not the data is
+ loaded, the values of catalog.Naves_disk,Naves_off,Naverage are set
+ to match the segment of data loaded.  this API always loads either
+ the entire data set, or none.  If none, the offset value is set to
+ point at the end of the table.
+
+dvo_catalog_save (Catalog *catalog, int VERBOSE);
+// format: INTERNAL, LONEOS, ELIXIR, PANSTARRS, PMTEST, etc.
+// layout: RAW, MEF, SPLIT
+// mode:   READ, WRITE
+
+dvo_catalog_init (Catalog *catalog);
+
+dvo_catalog_create (Catalog *catalog);
+
+dvo_catalog_check (Catalog *catalog, int Nsecfilt, int extend);
+// check an existing catalog against the identified Nsecfilt value
+// if (Nsecfilt == catalog[0].Nsecfilt) OK
+// if (Nsecfilt <  catalog[0].Nsecfilt) ERROR
+// if (Nsecfilt >  catalog[0].Nsecfilt) && extend) extend
+// if (Nsecfilt >  catalog[0].Nsecfilt) && !extend) ERROR
+
+
+several possible cases:
+
+- open and read an existing catalog / create if missing
+- open and read an existing catalog / error if missing (can be ignored)
+
+dvo_catalog_open (Catalog *catalog, int mode); 
+
+dvo_catalog_load_segment_average (Catalog *catalog, int start, int Nrows)
+
+ this function reads in the requested number of rows from the catalog
+ for the Average and SecFilt tables only.  It is possible within the
+ DVO framework to examine the average values in slices.  
+
+dvo_catalog_load_segment_measure (Catalog *catalog, int start, int Nrows)
+
+ this function reads in the requested number of rows from the catalog
+ for the Measure table only.  It is not guaranteed to be possible to
+ examine the measure values in slices if reference is needed to their
+ average values.
+
+dvo_catalog_load_segment_missing (Catalog *catalog, int start, int Nrows)
+
+ this function reads in the requested number of rows from the catalog
+ for the Missing table only.  It is not guaranteed to be possible to
+ examine the missing values in slices if reference is needed to their
+ average values.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-flags.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-flags.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-flags.txt	(revision 22322)
@@ -0,0 +1,39 @@
+
+There are several types of quality flags used within DVO.  There are
+flags associated with the measurements, the average objects, and the
+images.  
+
+measure->photFlags : these flags are supplied by the photometry
+		     analysis program which produced the data.  They
+		     may be directly defined by the program, as in the
+		     case of psphot, or they may be translations of
+		     detection metadata supplied by the data source
+		     (eg, 2MASS or SDSS).  These flags are read only:
+		     the are set by addstar, and not modified by any
+		     of the DVO database manipulation programs.  
+
+measure->dbFlags : these flags are used within DVO to note the ways in
+		   which the detetion is determined to be special by
+		   the database analysis programs.  These flags may be
+		   set by addstar on ingest, or by relphot, relastro,
+		   or equivalent programs.  Here are the possible
+		   values for dbFlags:
+
+NOTE: update these names and use an enum:		   
+# define ID_MEAS_NOCAL        0x0001 : detection ignored for this analysis (photcode, time range) -- internal only
+# define ID_MEAS_POOR_PHOTOM  0x0002 : detection is photometry outlier
+# define ID_MEAS_SKIP_PHOTOM  0x0004 : detection was ignored for photometry measurement
+# define ID_MEAS_AREA         0x0008 : detection near image edge
+# define ID_MEAS_POOR_ASTROM  0x0010 : detection is astrometry outlier
+# define ID_MEAS_SKIP_ASTROM  0x0020 : detection was ignored for astrometry measurement
+# define ID_MEAS_UNDEF_1      0x0040 : unused
+# define ID_MEAS_UNDEF_2      0x0080 : unused
+# define BLEND_IMAGE          0x0100 : detection is within radius of multiple objects
+# define BLEND_CATALOG        0x0200 : multiple detections within radius of object
+# define ID_MEAS_UNDEF_3      0x0400 : unused
+# define ID_MEAS_UNDEF_4      0x0800 : unused
+# define BLEND_IMAGE_NEIGHBOR 0x1000 : detection is within radius of multiple objects across catalogs
+# define ID_ARTIFACT          0x2000 : detection is thought to be non-astronomical
+# define ID_MEAS_UNDEF_5      0x4000 : unused
+# define ID_MEAS_UNDEF_6      0x8000 : unused
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-images.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-images.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-images.txt	(revision 22322)
@@ -0,0 +1,7 @@
+
+the dvo image table is not extremely efficient.  it is unsorted, and
+spatial searches consist of complete scans of the table.
+
+a spatial index could consist of five cardinal points for each square
+image (4 corners and the center) or a central point and max radius.
+table would consist each would be
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-int-nan.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-int-nan.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-int-nan.txt	(revision 22322)
@@ -0,0 +1,14 @@
+
+we have a variety of data fields which use char, short, int or long as
+a data type.  we need a value which can mean NAN in that context.
+these are defined in dvo.h as elements of the enum DVO_INT_NAN.  
+
+2007.11.07 : I am going through the code base and replacing the
+        deprecated values NO_ERR and NO_MAG with their equivalent
+        DVO_INT_NAN names.  At the same time, where ever the internal
+        value is a float or double, these must be converted to real
+        NAN values.  These may once have used char, short, or int
+        internally, and were not updated correctly.  
+
+	* watch out for Xm : PhotXm returns -1, not NAN for invalid entry,
+	  secfilt.Xm is short (use NAN_S_SHORT?)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-structures.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-structures.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/dvo-structures.txt	(revision 22322)
@@ -0,0 +1,178 @@
+
+2008.01.31
+
+  For testing the quality of the extended source measurements, I need
+  to include them somehow within DVO.  My plan is to have a set of
+  extended source tables linked to the average tables, parallel to the
+  measure tables.  One issue is: how many rows in this table?  I think
+  I need to have room for all fields measured by psphot  Another
+  question: do I use a fixed layout I/O file, or do I use the header
+  to read only the columns of interest.  I can do the latter, and it
+  is only a little slower.
+
+2007.02.22
+
+I have several DVO improvements to implement.  I need to plan them a
+bit carefully.  Here are my thoughts on these updates.
+
+- add chip X,Y to measure table
+
+  this is a fairly simple addition.  In addstar/find_matches, I just need
+  to assign the X,Y value from the incoming star data.  For loading
+  old catalogs which don't include X,Y in the table, I need to
+  calculate the X,Y position based on the R,D after finding the
+  matching image.  I have code to do this calculation currently in
+  dvo/photometry.c for looking up X,Y on the fly.  once this is moved
+  into the load functions, it will not be needed in photometry.c
+
+- remove PRI/SEC and only use secfilt table for mags
+
+  this is not a very difficult change, conceptually, but it may be a
+  fair amount of work.  all of the functions which currently switch on
+  the case of PRI/SEC just need to look in the secfilt location.  the
+  value of Nsec needs to increase by 1.  the load from tables which
+  used PRI/SEC need to move the PRI values to the correct location in
+  secfilt
+
+  eventually, rename 'secfilt' to a better name choice
+
+- add image, average, measure IDs
+
+  for PS1, the image and measure need to be generated by the external
+  software.  they would just be part of the input stream.  for the
+  case were the IDs are not supplied, DVO needs to generate them in a
+  unique way.  I probably need to understand / define that mechanism
+  before tackling this problem.
+
+  one point: I need to keep the old averef index.  how do this index
+  and the objID work together?
+
+- link measure to image
+
+  temporariliy use an index like averef?
+
+- move photcode table to catdir (also zero points)
+
+  should be fairly easy: just like the SkyTable, look in the catdir
+  first, then generate from the default text table if it is missing.
+
+- color term as a function of mosaic position
+ 
+- mextract field,field,field 
+
+  this should not be a very difficult job.  just add a loop to the
+  avextract / mextract functions.
+
+- mextract field(s) where condition
+
+  this is a bit trickier, and could make use of the code in the dvo
+  math functions
+
+- dvo select from mysql
+
+  not very difficult: need to define the commands to select the
+  database and set up a connection, then parse the select line into
+  the appropriate format.  just need to as the db for the type of the
+  fields that are requested: must be numerical.
+
+  select field,field,field from table where condition
+
+- change vectors to doubles
+
+  probably not a terrible job.  this will depend on how the union is
+  defined.  I don't want this to explode the size of an image!
+
+---------
+
+I now have the ability to load and save DVO databases in old formats,
+with automatic conversion to the current 'internal' representations
+for the average, measure, and image tables.  an important concern with
+this process is ensuring that we track the old layout names and avoid
+breaking existing databases.  Here is a list of old databases, some of
+which may not have been written with the right names, or with
+inaccurate names.  these can be fixed by setting the FORMAT and MODE
+header keywords with the 'fits_insert' command:
+
+- LONEOS : old-style cmp files (ie, RAW), with the 'LONEOS' format.
+- CFHT Elixir databases: cmp files, ELIXIR format
+- Brandon's Taurus db: incorrect 'PANSTARRS' format, should be called
+  'PSTEST1'.  drop support for this eventually? have brandon migrate to
+  the new panstarrs formats?
+
+I am going to use the following naming convention for future db table
+updates:
+
+- PANSTARRS.DEV.0, PANSTARRS.DEV.1, etc
+- PANSTARRS.PS1.0, PANSTARRS.PS1.1, etc
+- PANSTARRS.PS4.0, PANSTARRS.PS4.1, etc
+
+The next version of the DVO format needs several major changes:
+
+  - unique IDs in the measure and average tables.
+  - reference to the source image in the measure table.
+  - chip X,Y coordinates and errors in the measure table
+
+  - remove primary average magnitude from average.d and only use
+    the secfilt.d table for the average magnitudes
+
+These are fairly substantial modifications.  Can I do these changes on
+an appropriate timescale to get Dave Monet working with relastro?  Is
+it necessary to do these all at once?
+
+------
+
+DVO has undergone at least two bit-incompatible versions of the data
+structures to date: the 'loneos' version and the 'elixir' version.  
+
+the 'loneos' version was used for the loneos database currently still
+in my dataspace.
+
+the 'elixir' version, which extended some of the structures and added
+the Secfilt concept, was added at CFHT and is used for all of the
+existing CFHT elixir databases.
+
+For DVO-2 development, I will need to make a few modifications to
+those tables.  It will be useful to allow versions of DVO which
+interact with these different versions correctly.  At one level, this
+simply means changing the structures in the included file.  More
+difficult is that certain functions use elements of the structures
+from one version which are not available in the other versions.  
+
+I am going to explore this process briefly to get DVO-2 started,
+particularly so I can solve the astrometric problems which are
+currently limited by the use of floats to represent RA & DEC.  
+
+I am going to make the selection of the structures depend on #define
+values in the header files.  Currently, everybody which works with the
+dvo files includes loneos.h.  This should be broken out to a loneos.h,
+elixir.h, panstarrs.h, etc, all independently included from within
+dvo.h, which will be the include file called within the user-level
+programs.
+
+Translations
+
+/* average data as stored in the LONEOS database -- must be translated on load */
+
+Loneos:Measure -> Elixir:Measure
+/* OLD LONEOS VERSION.  NOT BYTE COMPATIBLE WITH NEW VERSION 
+   (can be loaded with translation, losing the exptime,
+   and limited the number of Naverage to 0xffffff - in load_catalog) */
+
+------
+
+the DVO structure Catalog contains the data from a single sky patch, including:
+    average, measure, missing, and secfilt.  
+
+the catalog may be saved in one of 4 modes (catalog.catmode):
+    raw   : the old Elixir style which is a header + 4 binary tables
+    mef   : catalog is a single file with header and 4 fits tables
+    split : catalog is 5 files, one with the header and one for each table
+    mysql : catalog is 4 (or 5?) mysql tables
+
+    in split mode, the filename is the file of the descriptive header
+    and the averages, the measure, missing, and secfilt data are
+    stored in additional files referenced in the header and named
+    based on the filename (replacing extension .cpa with .cpb, .cpc,
+    .cpd, for example)
+
+    
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/fits_db.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/fits_db.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/fits_db.txt	(revision 22322)
@@ -0,0 +1,5 @@
+
+FITS_DB represents a FITS table file, including the primary header,
+any existing matrix, the table header, and the table data as either an
+ftable (real) or vtable (virtual) FITS table.  
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/doc/notes.txt	(revision 22322)
@@ -0,0 +1,55 @@
+
+Adding a new dvo catalog format.  Assume the new format name is 'foo'.
+
+1) create the autocode definition files for average, measure, secfilt, image
+   these files must be a subset of the internal versions of these same
+   tables.  if you intend to add fields which don't exist in the
+   internal tables, you must update the internal tables as well.  the
+   naming convention is: average-foo.d, etc.
+
+2) add the new definition files to libautocode/Makefile.Targets (both
+   .o and .h lists).
+
+3) set the STRUCT and EXTNAME for these definitions files to have a
+   unique value. the naming convention for EXTNAME is DVO_AVERAGE_FOO
+   and for STRUCT is AverageFoo (add version info with underscores and
+   uppercase, eg: AveragePanstarrs_DEV_0, AveragePanstarrs_PS1_2).
+
+4) create a DVOTableFormat entry for the new format
+   (libdvo/include/dvo.h).  the naming convetion is DVO_FORMAT_FOO.
+
+5) add an entry for the new format in dvo_catalog_catformat
+ (libdvo/src/dvo_catalog.c).  The name should be "FOO".
+
+6) add entry for structure size in dvo_catalog_load_raw
+   (libdvo/src/dvo_catalog_raw.c), since this is not available from
+   the header.
+
+7) add entries in ReadRawAverage, WriteRawAverage, ReadRawMeasure,
+   WriteRawMeasure, ReadRawSecFilt, WriteRawSecFilt.  Make sure to use
+   the new STRUCT names. the conversion function gfit_convert_Foo will
+   be automatically generated.
+
+8) add entry in WriteRawAverage, making sure to use the new STRUCT
+   name. the conversion function gfit_convert_Foo will be
+   automatically generated.  
+
+9) create a new conversion file dvo_convert_foo.c and define the
+   internal to Foo conversions.
+
+* Note some esoteric format issues:  
+
+  - LONEOS and ELIXIR lack chip coordinates, and must apply an
+    on-the-fly conversion, implemented in dvo_catalog_chipcoords. 
+
+  - LONEOS and ELIXIR use the old primary/secondary photcode concept,
+    and must have one photcode dataset extracted from the average
+    table on read and resupplied on write.  this happens in
+    dvo_catalog_save_mef, dvo_catalog_save_raw,
+    dvo_catalog_save_split, dvo_catalog_update_split
+
+  - LONEOS and ELIXIR have old versions where the header does not
+    specify the type explicitly.  the functions dvo_catalog_load_raw
+    and dvo_image_load_raw identify these types based on special
+    keywords.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/dvo.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/dvo.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/dvo.h	(revision 22322)
@@ -0,0 +1,449 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# include <autocode.h>
+
+# ifndef DVO_H
+# define DVO_H
+
+/* DVO table modes */
+typedef enum {DVO_MODE_UNDEF, DVO_MODE_RAW, DVO_MODE_MEF, DVO_MODE_SPLIT, DVO_MODE_MYSQL} DVOTableMode;
+
+/* DVO table formats */
+typedef enum {DVO_FORMAT_UNDEF, 
+	      DVO_FORMAT_INTERNAL, 
+	      DVO_FORMAT_ELIXIR, 
+	      DVO_FORMAT_LONEOS, 
+	      DVO_FORMAT_PANSTARRS_DEV_0,
+	      DVO_FORMAT_PANSTARRS_DEV_1,
+	      DVO_FORMAT_PS1_DEV_1,
+	      DVO_FORMAT_PS1_DEV_2,
+	      DVO_FORMAT_PS1_DEV_3
+} DVOTableFormat;
+
+/* image data modes in RegImage */
+typedef enum {T_UNDEF = -1, T_NONE, T_OBJECT, T_DARK, T_BIAS, T_FLAT, T_MASK, T_FRINGE, T_SCATTER, T_MODES, T_FRINGEPTS, T_ANY, N_TYPE} ElixirDetrendTypes;
+typedef enum {M_UNDEF = -1, M_NONE, M_MEF, M_SPLIT, M_SINGLE, M_CUBE, M_SLICE, M_MODES, N_MODE} ElixirDetrendModes;
+
+typedef enum {DVO_CAT_OPEN_FAIL, DVO_CAT_OPEN_OK, DVO_CAT_OPEN_EMPTY} DVOCatalogOpenModes;
+
+typedef enum {
+  PROJ_NONE, // undefined
+  PROJ_ZEA, // zenithal
+  PROJ_ZPL, // zenithal
+  PROJ_ARC, // zenithal
+  PROJ_STG, // zenithal
+  PROJ_SIN, // zenithal
+  PROJ_TAN, // zenithal
+  PROJ_TNX, // zenithal
+  PROJ_DIS, // zenithal (TAN + polyterms)
+  PROJ_LIN, // cartesian
+  PROJ_PLY, // cartesian
+  PROJ_WRP, // cartesian
+  PROJ_AIT, // pseudocyl
+  PROJ_GLS, // pseudocyl
+  PROJ_PAR, // pseudocyl
+} OhanaProjection;
+
+typedef enum {
+  PROJ_MODE_NONE,
+  PROJ_MODE_CARTESIAN,
+  PROJ_MODE_ZENITHAL,
+  PROJ_MODE_PSEUDOCYL,
+} OhanaProjectionMode;
+
+// these are used as NAN for types of int values
+typedef enum {
+    NAN_S_CHAR  = 0x7f,
+    NAN_U_CHAR  = 0xff,   // was NO_ERR
+    NAN_S_SHORT = 0x7fff, // was NO_MAG
+    NAN_U_SHORT = 0xffff, 
+    NAN_S_INT   = 0x7fffffff,
+    NAN_U_INT   = 0xffffffff,
+} DVO_INT_NAN;
+
+# ifndef NAN
+# ifndef BYTE_SWAP
+#  define __nan_bytes           { 0x7f, 0xc0, 0, 0 }
+# else
+#  define __nan_bytes           { 0, 0, 0xc0, 0x7f }
+# endif
+static union { unsigned char __c[4]; float __d; } __nan_union
+    __attribute_used__ = { __nan_bytes };
+# define NAN    (__nan_union.__d)
+# endif
+
+/* RegImage.flag values */
+# define IMREG_DIST  0x01 /* image distributed, only imregister-3.0 */
+
+/* catalog values to be loaded */
+# define LOAD_NONE 	0x00
+# define LOAD_AVES 	0x01
+# define LOAD_MEAS 	0x02
+# define LOAD_MISS 	0x04
+# define LOAD_SECF 	0x08 
+# define LOAD_MEAS_META 0x10
+
+/* photometry code types */
+// # define PHOT_PRI 0x01
+# define PHOT_SEC 0x02
+# define PHOT_DEP 0x03
+# define PHOT_REF 0x04
+# define PHOT_ALT 0x05  /* never stored, only for look-ups */
+# define PHOT_MAG 0x06  /* generic magnitude; never stored */
+
+/* Image.code values.  these are codes to note bad images */
+# define ID_IMAGE_NEW   0x0000  /* no nrphot attempted */
+# define ID_IMAGE_NOCAL 0x0001  /* used within nrphot to mean "don't apply fit" */
+# define ID_IMAGE_POOR  0x0002  /* relphot says image is bad */
+# define ID_IMAGE_SKIP  0x0004  /* external information image is bad */
+# define ID_IMAGE_FEW   0x0008  /* currently too few measurements for good value */
+
+/* Measure.flags values */
+// XXX replace the # defines with typedef enum
+# define ID_MEAS_NOCAL        0x0001  /* detection ignored for this analysis (photcode, time range) -- internal only */
+# define ID_MEAS_POOR_PHOTOM  0x0002  /* detection is photometry outlier					     */	  
+# define ID_MEAS_SKIP_PHOTOM  0x0004  /* detection was ignored for photometry measurement			     */	  
+# define ID_MEAS_AREA         0x0008  /* detection near image edge						     */
+# define ID_MEAS_POOR_ASTROM  0x0010  /* detection is astrometry outlier					     */	  
+# define ID_MEAS_SKIP_ASTROM  0x0020  /* detection was ignored for astrometry measurement			     */	  
+# define ID_MEAS_UNDEF_1      0x0040  /* unused									     */
+# define ID_MEAS_UNDEF_2      0x0080  /* unused									     */
+# define ID_MEAS_BLEND_MEAS   0x0100  /* detection is within radius of multiple objects				     */
+# define ID_MEAS_BLEND_OBJ    0x0200  /* multiple detections within radius of object				     */
+# define ID_MEAS_UNDEF_3      0x0400  /* unused									     */
+# define ID_MEAS_UNDEF_4      0x0800  /* unused									     */
+# define ID_MEAS_BLEND_MEAS_X 0x1000  /* detection is within radius of multiple objects across catalogs		     */
+# define ID_MEAS_ARTIFACT     0x2000  /* detection is thought to be non-astronomical				     */
+# define ID_MEAS_UNDEF_5      0x4000  /* unused									     */
+# define ID_MEAS_UNDEF_6      0x8000  /* unused									     */
+
+// XXX we used these names previously in markstar: replace with ID_MEAS_ARTIFACT
+// # define ID_MEAS_TRAIL        0x2000
+// # define ID_MEAS_GHOST        0x4000
+
+/* some subtle distinctions between the blend flags:
+   BLEND_IMAGE: the star on an image is matched with more 
+   than one star in the catalog (image has worse seeing than catalog)
+   BLEND_CATALOG: the star in the catalog is matched with more 
+   than one star on the image (image has better seeing than catalog)
+   CALIBRATED: relative photometry has been performed on this measurement
+   BLEND_IMAGE_NEIGHBOR: the star on an image is matched with more 
+   than one star in the catalog, but not in the same catalog file.
+*/
+
+/* Average.code values */
+# define ID_STAR_FEW   0x0001 /* used within relphot: skip star */
+# define ID_STAR_POOR  0x0002 /* used within relphot: skip star */
+# define ID_PROPER     0x0400 /* star with large proper motion */
+# define ID_TRANSIENT  0x1000 /* is this mutually exclusive with USNO?  */
+# define ID_VARIABLE   0x2000 /* not currently set? */
+# define ID_ASTEROID   0x2000 /* identified with an asteroid */
+# define ID_BAD_OBJECT 0x4000 /* if all measurements are bad, set this bit */
+# define ID_MOVING     0x8000
+# define ID_ROCK       0xa000 /* 0x8000 + 0x2000 */
+# define ID_GHOST      0xc001 /* 0x8000 + 0x4000 + 0x0001 */
+# define ID_TRAIL      0xc002 /* 0x8000 + 0x4000 + 0x0002 */
+# define ID_BLEED      0xc003 /* 0x8000 + 0x4000 + 0x0003 */ 
+# define ID_COSMIC     0xc004 /* 0x8000 + 0x4000 + 0x0004 */ 
+
+/*** general dvo structures (internal use only / not IO) ***/
+
+/* FITS DB structure */
+typedef struct {
+  FILE  *f;
+  char  *filename;
+  int    dbstate;
+  int    lockstate;
+  double timeout;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  VTable vtable;
+  int    mode;          /* what data storage mode is used for disk file? */
+  int    format;        /* what data format is used for disk file? */
+  int    virtual;       /* is table in ftable or vtable? */
+  int    swapped;       /* is table in internal byte-order? */
+} FITS_DB;
+
+/* the basic HST GSC layout corresponds to a depth of 3 */
+# define SKY_DEPTH_HST 3
+
+/* SkyRegion : better implementation than GSCRegion */
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion *regions;
+} SkyTable;
+
+typedef struct {
+  int Nregions;
+  int ownElements; 				  /* does this list own filename, regions? */
+  char **filename;
+  SkyRegion **regions;
+} SkyList;
+
+# if (0)
+/* structure for data on a catalog region */
+typedef struct {
+  char filename[256];
+  double DEC[2], RA[2];
+} GSCRegion;
+# endif
+
+typedef struct {
+  int Ncode;					  // number of photcodes
+  int Nsecfilt;					  // number of average magnitudes
+  int hashcode[0x10000];		  // index from photcode value to sequence
+  int hashNsec[0x10000];		  // index from photcode value to Nsec seq
+  int codeNsec[0x10000];		  // index from Nsec seq to photcode value
+  PhotCode *code;
+} PhotCodeData;
+
+/* a catalog contains this data */
+typedef struct Catalog {
+  char *filename;			/* catalog file */
+  FILE *f;  				/* file descriptor */
+  Header  header;
+
+  /* data in the catalog file */
+  Average *average;
+  Measure *measure; 
+  Missing *missing; 
+  SecFilt *secfilt;
+
+  int Nsecfilt;  /* number of secfilt entries for each average entry */
+  int Naverage,   Nmeasure,   Nmissing,   Nsecf_mem;  /* current number of each component in memory */
+  int Naves_disk, Nmeas_disk, Nmiss_disk, Nsecf_disk; /* current number of each component on disk */
+  int Naves_off,  Nmeas_off,  Nmiss_off,  Nsecf_off;  /* index of first loaded data value */
+
+  /* note the different counting for Nsecfilt:
+     number of secfilt rows on disk is: Nave_disk * Nsecfilt
+     number of secfilt rows in mem  is: Naverage * Nsecfilt
+     *** that is just silly, and bad: convert to using Nsec_mem, Nsec_disk, Nsec_off.
+     *** unless we always require the secfilt and average entries to be loaded sychronously.
+   */
+
+  /* pointers to split data files */
+  struct Catalog *measure_catalog;		/* measure catalog data (split) */
+  struct Catalog *missing_catalog;		/* missing catalog data (split) */
+  struct Catalog *secfilt_catalog;		/* secfilt catalog data (split) */
+
+  unsigned int objID;
+  unsigned int catID;
+
+  /* extra catalog information */
+  int lockmode;
+  int catmode;				/* storage mode (raw, mef, split, mysql) */
+  int catformat;			/* storage format (elixir, panstarrs, etc) */
+  int catflags;				/* choices to be loaded */
+  int sorted;				/* is measure table average-sorted? */
+  
+  /* pointers for data manipulation */
+  int *found;
+  int *image;
+  int *mosaic;
+  float *X;
+  float *Y;
+
+} Catalog;
+
+/*** prototypes ***/
+
+/* in gfits_db.c */
+int   gfits_db_init                PROTO((FITS_DB *db));
+int   gfits_db_create              PROTO((FITS_DB *db));
+int   gfits_db_lock                PROTO((FITS_DB *db, char *filename));
+int   gfits_db_load                PROTO((FITS_DB *db));
+int   gfits_db_load_segment        PROTO((FITS_DB *db, int start, int Nrows));
+int   gfits_db_save                PROTO((FITS_DB *db));
+int   gfits_db_update              PROTO((FITS_DB *db));
+int   gfits_db_close               PROTO((FITS_DB *db));
+int   gfits_db_free                PROTO((FITS_DB *db));
+
+/* in coords.c, using libautocode/def/coords.d */
+int  XY_to_LM (double *L, double *M, double x,  double y,   Coords *coords);
+int  LM_to_XY (double *x,  double *y,   double L, double M, Coords *coords);
+int  RD_to_LM (double *L, double *M, double ra,  double dec,   Coords *coords);
+int  LM_to_RD (double *ra, double *dec,   double L, double M, Coords *coords);
+int  XY_to_RD (double *ra, double *dec, double x,  double y,   Coords *coords);
+int  RD_to_XY (double *x,  double *y,   double ra, double dec, Coords *coords);
+int  fXY_to_RD (float *ra, float *dec, double x,  double y,   Coords *coords);
+int  fRD_to_XY (float *x,  float *y,   double ra, double dec, Coords *coords);
+int  GetCoords (Coords *coords, Header *header);
+int  PutCoords (Coords *coords, Header *header);
+void RegisterMosaic (Coords *coords);
+void coords_precess (double *ra, double *dec, double in_epoch, double out_epoch);
+OhanaProjection GetProjection (char *ctype);
+int SetProjection (char *ctype, OhanaProjection proj);
+OhanaProjectionMode GetProjectionMode (OhanaProjection proj);
+
+char *libdvo_version ();
+
+int isRegisteredMosaic ();
+int GetRegisteredMosaic ();
+int GetMosaicCoords (Coords *coords);
+int FindMosaicForImage (Image *images, int Nimages, int entry);
+int FindMosaicForImage_TableSearch (Image *images, int Nimages, int entry);
+int FindMosaicForImage_MatchSearch (Image *images, int Nimages, int entry);
+int BuildChipMatch (Image *images, int Nimages);
+void SetImageCorners (double *X, double *Y, Image *image);
+
+short int putMi (double value);
+double getMi (short int value);
+void returnMcal (Image *image, double *c);
+void assignMcal (Image *image, double *c, int order);
+double applyMcal (Image *image, double x, double y);
+double findscatter (double *X, double *Y, double *M, double *dM, int N, double *c, int order);
+
+PhotCode *GetPhotcodebyName (char *name);
+PhotCode *GetPhotcodeEquivbyName (char *name);
+PhotCode *GetPhotcodebyCode (int code);
+PhotCode *GetPhotcodebyNsec (int Nsec);
+PhotCode *GetPhotcodeEquivbyCode (int code);
+char     *GetPhotcodeNamebyCode (int code);
+
+float PhotInst (Measure *measure);
+float PhotCat (Measure *measure);
+float PhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+float PhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+float PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+float PhotXm (PhotCode *code, Average *average, SecFilt *secfilt);
+float PhotdM (PhotCode *code, Average *average, SecFilt *secfilt);
+
+float PhotColorForCode (Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+int PhotColor (Average *average, SecFilt *secfilt, Measure *measure, int c1, int c2, double *color);
+
+PhotCodeData *GetPhotcodeTable ();
+
+int LoadPhotcodes (char *catdir_file, char *master_file);
+int LoadPhotcodesText (char *filename);
+int LoadPhotcodesFITS (char *filename);
+int SavePhotcodesText (char *filename);
+int SavePhotcodesFITS (char *filename);
+
+void PrintPhotcodeNamebyCode (FILE *f, char *format, int code);
+
+int GetPhotcodeCodebyName (char *name);
+int GetPhotcodeEquivCodebyName (char *name);
+int GetPhotcodeEquivCodebyCode (int code);
+int GetPhotcodeNsec (int code);
+int GetPhotcodeNsecfilt ();
+void SetZeroPoint (double ZP);
+double GetZeroPoint ();
+int *GetPhotcodeEquivList (int code, int *nlist);
+void ParseColorTerms (char *terms, float *X, int *N);
+
+int get_image_type (char *name);
+char *get_type_name (int type);
+int get_image_mode (char *name);
+char *get_mode_name (int mode);
+
+/* these functions refer to the DVO structures defined in the includes above
+ *  they are being replaced with autocode entries (drop when totally autocoded)
+ */
+
+int   Fread (void *ptr, int size, int nitems, FILE *f, char *type);
+int   Fwrite (void *ptr, int size, int nitems, FILE *f, char *type);
+int   ByteSwap (char *ptr, int size, int nitems, char *type);
+int   ConvertStruct (char *buffer, int size, int Nbytes, char *type);
+
+/** dvo_catalog APIs */
+void dvo_catalog_init (Catalog *catalog, int complete);
+void dvo_catalog_create (SkyRegion *region, Catalog *catalog);
+void dvo_catalog_free (Catalog *catalog);
+void dvo_catalog_free_data (Catalog *catalog);
+int dvo_catalog_check (Catalog *catalog, int Nsecfilt, int extend);
+int dvo_catalog_lock (Catalog *catalog, int lockmode);
+int dvo_catalog_unlock (Catalog *catalog);
+int dvo_catalog_load (Catalog *catalog, int VERBOSE);
+int dvo_catalog_open (Catalog *catalog, SkyRegion *region, int VERBOSE, char *iomode);
+int dvo_catalog_save (Catalog *catalog, char VERBOSE);
+int dvo_catalog_save_complete (Catalog *catalog, char VERBOSE);
+int dvo_catalog_update (Catalog *catalog, char VERBOSE);
+int dvo_catalog_catformat (char *catformat);
+int dvo_catalog_catmode (char *catmode);
+void dvo_catalog_test (Catalog *catalog, int halt);
+
+/* catmode-specific APIs */
+int dvo_catalog_load_raw (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_raw (Catalog *catalog, char VERBOSE);
+int dvo_catalog_load_mef (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_mef (Catalog *catalog, char VERBOSE);
+int dvo_catalog_load_split (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_split (Catalog *catalog, char VERBOSE);
+int dvo_catalog_update_split (Catalog *catalog, char VERBOSE);
+int dvo_catalog_save_split_complete (Catalog *catalog, char VERBOSE);
+
+int dvo_catalog_load_segment (Catalog *catalog, int VERBOSE, int start, int Nrows);
+int dvo_catalog_load_segment_split (Catalog *catalog, int VERBOSE, int start, int Nrows);
+
+/*** conversion functions / I/O conversions ***/
+Average *ReadRawAverage (FILE *f, int Naverage, int format, SecFilt **primary);
+Measure *ReadRawMeasure (FILE *f, int Nmeasure, int format);
+SecFilt *ReadRawSecFilt (FILE *f, int Nsecfilt, int format);
+int WriteRawAverage (FILE *f, Average *average, int Naverage, int format, SecFilt *primary);
+int WriteRawMeasure (FILE *f, Measure *measure, int Nmeasure, int format);
+int WriteRawSecFilt (FILE *f, SecFilt *secfilt, int Nsecfilt, int format);
+
+Average *FtableToAverage (FTable *ftable, int *Naverage, int *format, SecFilt **primary);
+Measure *FtableToMeasure (FTable *ftable, int *Nmeasure, int *format);
+SecFilt *FtableToSecFilt (FTable *ftable, int *Nsecfilt, int *format);
+int FtableToImage (FTable *ftable, Header *theader, int *format);
+
+int AverageToFtable (FTable *ftable, Average *average, int Naverage, int format, SecFilt *primary);
+int MeasureToFtable (FTable *ftable, Measure *measure, int Nmeasure, int format);
+int SecFiltToFtable (FTable *ftable, SecFilt *secfilt, int Nsecfilt, int format);
+int ImageToFtable (FTable *ftable, Header *theader, int format);
+int ImageToVtable (VTable *vtable, Header *theader, int format);
+
+# include "loneos_defs.h"
+# include "elixir_defs.h"
+# include "panstarrs_dev_0_defs.h"
+# include "panstarrs_dev_1_defs.h"
+# include "ps1_dev_1_defs.h"
+# include "ps1_dev_2_defs.h"
+# include "ps1_dev_3_defs.h"
+
+/*** DVO image db I/O Functions ***/
+int dvo_image_lock (FITS_DB *db, char *filename, double timeout, int lockstate);
+int dvo_image_unlock (FITS_DB *db);
+int dvo_image_load (FITS_DB *db, int VERBOSE, int FORCE_READ);
+int dvo_image_save (FITS_DB *db, int VERBOSE);
+int dvo_image_update (FITS_DB *db, int VERBOSE);
+int dvo_image_load_raw (FITS_DB *db, int VERBOSE, int FORCE_READ);
+int dvo_image_update_raw (FITS_DB *db, int VERBOSE);
+int dvo_image_save_raw (FITS_DB *db, int VERBOSE);
+int dvo_image_addrows (FITS_DB *db, Image *new, int Nnew);
+void dvo_image_create (FITS_DB *db, double ZeroPoint);
+
+/* skyregion APIs */
+int        SkyTableSave        	   PROTO((SkyTable *table, char *filename));
+SkyTable  *SkyTableLoad        	   PROTO((char *filename, int VERBOSE));
+SkyTable  *SkyTableFromGSC     	   PROTO((char *filename, int depth, int VERBOSE));
+SkyTable  *SkyTableLoadOptimal 	   PROTO((char *catdir, char *SKYFILE, char *GSCFILE, int depth, int VERBOSE));
+int        SkyTableSetDepth    	   PROTO((SkyTable *sky, int depth));
+SkyList   *SkyRegionByPoint    	   PROTO((SkyTable *table, int depth, double ra, double dec));
+SkyList   *SkyListByPoint      	   PROTO((SkyTable *table, double ra, double dec));
+SkyList   *SkyListByRadius     	   PROTO((SkyTable *table, int depth, double RA, double DEC, double radius));
+SkyList   *SkyListByPatch      	   PROTO((SkyTable *table, int depth, SkyRegion *patch));
+SkyList   *SkyListByName      	   PROTO((SkyTable *table, char *name));
+SkyList   *SkyListByImage      	   PROTO((SkyTable *table, int depth, Image *image));
+SkyList   *SkyListByBounds     	   PROTO((SkyTable *table, int depth, double Rmin, double Rmax, double Dmin, double Dmax));
+SkyList   *SkyListChildrenByBounds PROTO((SkyTable *table, int No, int depth, double Rmin, double Rmax, double Dmin, double Dmax));
+int        SkyListMerge     	   PROTO((SkyList **outlist, SkyList *newlist));
+int        SkyListFree             PROTO((SkyList *list));
+int        SkyTableFree            PROTO((SkyTable *table));
+int        SkyListSetFilenames     PROTO((SkyList *list, char *path, char *ext));
+int        SkyTableSetFilenames    PROTO((SkyTable *sky, char *path, char *ext));
+
+/* dvo-specific sorting functions */
+void sortave (Average *ave, int N);
+void sort_image_subset (Image *image, int *subset, int N);
+void sort_coords_index (double *X, double *Y, int *S, int N);
+void sort_stars_ra (Stars *stars, int N);
+void sort_regions (SkyRegion *region, int N);
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/dvo.update.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/dvo.update.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/dvo.update.h	(revision 22322)
@@ -0,0 +1,399 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# include <autocode.h>
+
+# ifndef DVO_H
+# define DVO_H
+
+/*
+  # define ELIXIR 1
+  # define PANSTARRS 0
+  # define LONEOS 0
+*/
+
+/*** named data values (convert all to enums?) ***/
+
+/* DVO table modes */
+enum {DVO_MODE_UNDEF, DVO_MODE_RAW, DVO_MODE_MEF, DVO_MODE_SPLIT, DVO_MODE_MYSQL} DVOTableMode;
+
+/* DVO table formats */
+enum {DVO_FORMAT_UNDEF, DVO_FORMAT_INTERNAL, DVO_FORMAT_ELIXIR, DVO_FORMAT_LONEOS, DVO_FORMAT_PANSTARRS, DVO_FORMAT_PMTEST} DVOTableFormat;
+
+/* image data modes in RegImage */
+enum {T_UNDEF = -1, T_NONE, T_OBJECT, T_DARK, T_BIAS, T_FLAT, T_MASK, T_FRINGE, T_SCATTER, T_MODES, T_FRINGEPTS, T_ANY, N_TYPE};
+enum {M_UNDEF = -1, M_NONE, M_MEF, M_SPLIT, M_SINGLE, M_CUBE, M_SLICE, M_MODES, N_MODE};
+
+typedef enum {
+    PROJ_NONE, // undefined
+    PROJ_ZEA, // zenithal
+    PROJ_ZPL, // zenithal
+    PROJ_ARC, // zenithal
+    PROJ_STG, // zenithal
+    PROJ_SIN, // zenithal
+    PROJ_TAN, // zenithal
+    PROJ_DIS, // zenithal (TAN + polyterms)
+    PROJ_LIN, // cartesian
+    PROJ_PLY, // cartesian
+    PROJ_WRP, // cartesian
+    PROJ_AIT, // pseudocyl
+    PROJ_GLS, // pseudocyl
+    PROJ_PAR, // pseudocyl
+} OhanaProjections;
+
+typedef enum {
+  PROJ_MODE_NONE,
+  PROJ_MODE_CARTESIAN,
+  PROJ_MODE_ZENITHAL,
+  PROJ_MODE_PSEUDOCYL,
+} OhanaProjectionModes;
+
+/* RegImage.flag values */
+# define IMREG_DIST  0x01 /* image distributed, only imregister-3.0 */
+
+/* catalog values to be loaded */
+# define LOAD_AVES 	0x01
+# define LOAD_MEAS 	0x02
+# define LOAD_MISS 	0x04
+# define LOAD_SECF 	0x08 
+# define LOAD_MEAS_META 0x10
+
+/* invalid mag value */
+# define NO_MAG    0x7fff
+# define NO_ERR    0xff
+
+/* photometry code types */
+# define PHOT_PRI 0x01
+# define PHOT_SEC 0x02
+# define PHOT_DEP 0x03
+# define PHOT_REF 0x04
+# define PHOT_ALT 0x05  /* never stored, only for look-ups */
+
+/* Image.code values.  these are codes to note bad images */
+# define ID_IMAGE_NEW   0x0000  /* no nrphot attempted */
+# define ID_IMAGE_NOCAL 0x0001  /* used within nrphot to mean "don't apply fit" */
+# define ID_IMAGE_POOR  0x0002  /* relphot says image is bad */
+# define ID_IMAGE_SKIP  0x0004  /* external information image is bad */
+# define ID_IMAGE_FEW   0x0008  /* currently too few measurements for good value */
+
+/* Measure.flags values */
+# define ID_MEAS_NOCAL        0x0001
+# define ID_MEAS_POOR         0x0002
+# define ID_MEAS_SKIP         0x0004
+# define ID_MEAS_AREA         0x0008
+# define BLEND_IMAGE          0x0100 
+# define BLEND_CATALOG        0x0200
+# define BLEND_IMAGE_NEIGHBOR 0x1000
+# define ID_MEAS_TRAIL        0x2000
+# define ID_MEAS_GHOST        0x4000
+
+/* some subtle distinctions between the blend flags:
+   BLEND_IMAGE: the star on an image is matched with more 
+   than one star in the catalog (image has worse seeing than catalog)
+   BLEND_CATALOG: the star in the catalog is matched with more 
+   than one star on the image (image has better seeing than catalog)
+   CALIBRATED: relative photometry has been performed on this measurement
+   BLEND_IMAGE_NEIGHBOR: the star on an image is matched with more 
+   than one star in the catalog, but not in the same catalog file.
+*/
+
+/* Average.code values */
+# define ID_STAR_FEW   0x0001 /* used within relphot: skip star */
+# define ID_STAR_POOR  0x0002 /* used within relphot: skip star */
+# define ID_PROPER     0x0400 /* star with large proper motion */
+# define ID_TRANSIENT  0x1000 /* is this mutually exclusive with USNO?  */
+# define ID_VARIABLE   0x2000 /* not currently set? */
+# define ID_ASTEROID   0x2000 /* identified with an asteroid */
+# define ID_BAD_OBJECT 0x4000 /* if all measurements are bad, set this bit */
+# define ID_MOVING     0x8000
+# define ID_ROCK       0xa000 /* 0x8000 + 0x2000 */
+# define ID_GHOST      0xc001 /* 0x8000 + 0x4000 + 0x0001 */
+# define ID_TRAIL      0xc002 /* 0x8000 + 0x4000 + 0x0002 */
+# define ID_BLEED      0xc003 /* 0x8000 + 0x4000 + 0x0003 */ 
+# define ID_COSMIC     0xc004 /* 0x8000 + 0x4000 + 0x0004 */ 
+
+/*** general dvo structures (internal use only / not IO) ***/
+
+/* FITS DB structure */
+typedef struct {
+  FILE  *f;
+  char  *filename;
+  int    dbstate;
+  int    lockstate;
+  double timeout;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  VTable vtable;
+  int    mode;          /* what data storage mode is used for disk file? */
+  int    format;        /* what data format is used for disk file? */
+  int    virtual;       /* is table in ftable or vtable? */
+  int    swapped;       /* is table in internal byte-order? */
+} FITS_DB;
+
+/* the basic HST GSC layout corresponds to a depth of 3 */
+# define SKY_DEPTH_HST 3
+
+/* SkyRegion : better implementation than GSCRegion */
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion *regions;
+} SkyTable;
+
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion **regions;
+} SkyList;
+
+# if (0)
+/* structure for data on a catalog region */
+typedef struct {
+  char filename[256];
+  double DEC[2], RA[2];
+} GSCRegion;
+# endif
+
+typedef struct {
+  int Ncode;
+  int Nsecfilt;
+  int hashcode[0x10000];
+  int hashNsec[0x10000];
+  PhotCode *code;
+} PhotCodeData;
+
+/* a catalog contains this data */
+typedef struct Catalog {
+  char *filename;			/* catalog file */
+  FILE *f;  				/* file descriptor */
+  Header  header;
+
+  /* data in the catalog file */
+  Average *average;
+  Measure *measure; 
+  Missing *missing; 
+  SecFilt *secfilt;
+  int Naverage, Nmeasure, Nmissing, Nsecfilt;   /* current number of each component */
+  int Nave_disk, Nmeas_disk, Nmiss_disk;        /* number of component on disk */
+  int Nmeas_off;			        /* dist seq of first loaded data value */
+  /* note the different counting for Nsecfilt */
+
+  /* pointers to split data files */
+  struct Catalog *measure_catalog;		/* measure catalog data (split) */
+  struct Catalog *missing_catalog;		/* missing catalog data (split) */
+  struct Catalog *secfilt_catalog;		/* secfilt catalog data (split) */
+
+  /* extra catalog information */
+  int lockmode;
+  int catmode;				/* storage mode (raw, mef, split, mysql) */
+  int catformat;			/* storage format (elixir, panstarrs, etc) */
+  int catflags;				/* choices to be loaded */
+  int sorted;				/* is measure table average-sorted? */
+  
+  /* pointers for data manipulation */
+  int *found;
+  int *image;
+  int *mosaic;
+  float *X;
+  float *Y;
+
+} Catalog;
+
+/*** prototypes ***/
+
+/* in gfits_db.c */
+int   gfits_db_init                PROTO((FITS_DB *db));
+int   gfits_db_create              PROTO((FITS_DB *db));
+int   gfits_db_lock                PROTO((FITS_DB *db, char *filename));
+int   gfits_db_load                PROTO((FITS_DB *db));
+int   gfits_db_load_segment        PROTO((FITS_DB *db, int start, int Nrows));
+int   gfits_db_save                PROTO((FITS_DB *db));
+int   gfits_db_update              PROTO((FITS_DB *db));
+int   gfits_db_close               PROTO((FITS_DB *db));
+int   gfits_db_free                PROTO((FITS_DB *db));
+
+/* in coords.c, using libautocode/def/coords.d */
+int  XY_to_RD (double *ra, double *dec, double x,  double y,   Coords *coords);
+int  RD_to_XY (double *x,  double *y,   double ra, double dec, Coords *coords);
+int  fXY_to_RD (float *ra, float *dec, double x,  double y,   Coords *coords);
+int  fRD_to_XY (float *x,  float *y,   double ra, double dec, Coords *coords);
+int  GetCoords (Coords *coords, Header *header);
+int  PutCoords (Coords *coords, Header *header);
+void RegisterMosaic (Coords *coords);
+void coords_precess (double *ra, double *dec, double in_epoch, double out_epoch);
+
+int FindMosaicForImage (Image *images, int Nimages, int entry);
+int FindMosaicForImage_TableSearch (Image *images, int Nimages, int entry);
+int FindMosaicForImage_MatchSearch (Image *images, int Nimages, int entry);
+int BuildChipMatch (Image *images, int Nimages);
+void SetImageCorners (double *X, double *Y, Image *image);
+
+short int putMi (double value);
+double getMi (short int value);
+void returnMcal (Image *image, double *c);
+void assignMcal (Image *image, double *c, int order);
+double applyMcal (Image *image, double x, double y);
+double findscatter (double *X, double *Y, double *M, double *dM, int N, double *c, int order);
+
+PhotCode *GetPhotcodebyName (char *name);
+PhotCode *GetPhotcodeEquivbyName (char *name);
+PhotCode *GetPhotcodebyCode (int code);
+PhotCode *GetPhotcodebyNsec (int Nsec);
+PhotCode *GetPhotcodeEquivbyCode (int code);
+char     *GetPhotcodeNamebyCode (int code);
+
+float PhotInst (Measure *measure);
+float PhotCat (Measure *measure);
+float PhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+float PhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+float PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+float PhotXm (PhotCode *code, Average *average, SecFilt *secfilt);
+float PhotdM (PhotCode *code, Average *average, SecFilt *secfilt);
+
+# if (0)
+short iPhotInst (Measure *measure);
+short iPhotAbs (Measure *measure);
+short iPhotCat (Measure *measure);
+short iPhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+short iPhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+short iPhotCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+short iPhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+short iPhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+short iPhotXm (PhotCode *code, Average *average, SecFilt *secfilt);
+short iPhotdM (PhotCode *code, Average *average, SecFilt *secfilt);
+# endif
+
+float iPhotColor (Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+int PhotColor (Average *average, SecFilt *secfilt, Measure *measure, int c1, int c2, double *color);
+
+int LoadPhotcodes (char *filename);
+int GetPhotcodeCodebyName (char *name);
+int GetPhotcodeEquivCodebyName (char *name);
+int GetPhotcodeEquivCodebyCode (int code);
+int GetPhotcodeNsec (int code);
+int GetPhotcodeNsecfilt ();
+void SetZeroPoint (double ZP);
+int *GetPhotcodeEquivList (int code, int *nlist);
+void ParseColorTerms (char *terms, float *X, int *N);
+
+int get_image_type (char *name);
+char *get_type_name (int type);
+int get_image_mode (char *name);
+char *get_mode_name (int mode);
+
+/* these functions refer to the DVO structures defined in the includes above
+ *  they are being replaced with autocode entries (drop when totally autocoded)
+ */
+
+int   Fread (void *ptr, int size, int nitems, FILE *f, char *type);
+int   Fwrite (void *ptr, int size, int nitems, FILE *f, char *type);
+int   ByteSwap (char *ptr, int size, int nitems, char *type);
+int   ConvertStruct (char *buffer, int size, int Nbytes, char *type);
+
+/** dvo_catalog APIs */
+void dvo_catalog_init (Catalog *catalog, int complete);
+void dvo_catalog_create (SkyRegion *region, Catalog *catalog);
+void dvo_catalog_free (Catalog *catalog);
+int dvo_catalog_check (Catalog *catalog, int Nsecfilt, int extend);
+int dvo_catalog_lock (Catalog *catalog, int lockmode);
+int dvo_catalog_unlock (Catalog *catalog);
+int dvo_catalog_load (Catalog *catalog, int VERBOSE);
+int dvo_catalog_open (Catalog *catalog, SkyRegion *region, int VERBOSE, char *iomode);
+int dvo_catalog_save (Catalog *catalog, char VERBOSE);
+int dvo_catalog_update (Catalog *catalog, char VERBOSE);
+int dvo_catalog_catformat (char *catformat);
+int dvo_catalog_catmode (char *catmode);
+void dvo_catalog_test (Catalog *catalog, int halt);
+
+/* catmode-specific APIs */
+int dvo_catalog_load_raw (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_raw (Catalog *catalog, char VERBOSE);
+int dvo_catalog_load_mef (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_mef (Catalog *catalog, char VERBOSE);
+int dvo_catalog_load_split (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_split (Catalog *catalog, char VERBOSE);
+int dvo_catalog_update_split (Catalog *catalog, char VERBOSE);
+
+/*** conversion functions / I/O conversions ***/
+Average *ReadRawAverage (FILE *f, int Naverage, int format);
+Measure *ReadRawMeasure (FILE *f, int Nmeasure, int format);
+SecFilt *ReadRawSecFilt (FILE *f, int Nsecfilt, int format);
+int WriteRawAverage (FILE *f, Average *average, int Naverage, int format);
+int WriteRawMeasure (FILE *f, Measure *measure, int Nmeasure, int format);
+int WriteRawSecFilt (FILE *f, SecFilt *secfilt, int Nsecfilt, int format);
+
+Average *FtableToAverage (FTable *ftable, int *Naverage, int *format);
+Average *AverageLoneosToInternal (AverageLoneos *in, int Nvalues);
+Average *AverageElixirToInternal (AverageElixir *in, int Nvalues);
+Average *AveragePanstarrsToInternal (AveragePanstarrs *in, int Nvalues);
+Average *AveragePMtestToInternal (AveragePMtest *in, int Nvalues);
+AverageLoneos *AverageInternalToLoneos (Average *in, int Nvalues);
+AverageElixir *AverageInternalToElixir (Average *in, int Nvalues);
+AveragePanstarrs *AverageInternalToPanstarrs (Average *in, int Nvalues);
+AveragePMtest *AverageInternalToPMtest (Average *in, int Nvalues);
+
+Measure *FtableToMeasure (FTable *ftable, int *Nmeasure, int *format);
+Measure *MeasureLoneosToInternal (MeasureLoneos *in, int Nvalues);
+Measure *MeasureElixirToInternal (MeasureElixir *in, int Nvalues);
+Measure *MeasurePanstarrsToInternal (MeasurePanstarrs *in, int Nvalues);
+MeasureLoneos *MeasureInternalToLoneos (Measure *in, int Nvalues);
+MeasureElixir *MeasureInternalToElixir (Measure *in, int Nvalues);
+MeasurePanstarrs *MeasureInternalToPanstarrs (Measure *in, int Nvalues);
+
+SecFilt *FtableToSecFilt (FTable *ftable, int *Nsecfilt, int *format);
+SecFilt *SecFiltLoneosToInternal (SecFiltLoneos *in, int Nvalues);
+SecFilt *SecFiltElixirToInternal (SecFiltElixir *in, int Nvalues);
+SecFilt *SecFiltPanstarrsToInternal (SecFiltPanstarrs *in, int Nvalues);
+SecFiltLoneos *SecFiltInternalToLoneos (SecFilt *in, int Nvalues);
+SecFiltElixir *SecFiltInternalToElixir (SecFilt *in, int Nvalues);
+SecFiltPanstarrs *SecFiltInternalToPanstarrs (SecFilt *in, int Nvalues);
+
+int AverageToFtable (FTable *ftable, Average *average, int Naverage, int format);
+int MeasureToFtable (FTable *ftable, Measure *measure, int Nmeasure, int format);
+int SecFiltToFtable (FTable *ftable, SecFilt *secfilt, int Nsecfilt, int format);
+
+/*** DVO image db I/O Functions ***/
+int dvo_image_lock (FITS_DB *db, char *filename, double timeout, int lockstate);
+int dvo_image_unlock (FITS_DB *db);
+int dvo_image_load (FITS_DB *db, int VERBOSE, int FORCE_READ);
+int dvo_image_save (FITS_DB *db, int VERBOSE);
+int dvo_image_update (FITS_DB *db, int VERBOSE);
+int dvo_image_load_raw (FITS_DB *db, int VERBOSE, int FORCE_READ);
+int dvo_image_update_raw (FITS_DB *db, int VERBOSE);
+int dvo_image_save_raw (FITS_DB *db, int VERBOSE);
+int dvo_image_addrows (FITS_DB *db, Image *new, int Nnew);
+void dvo_image_create (FITS_DB *db, double ZeroPoint);
+
+int FtableToImage (FTable *ftable, Header *theader, int *format);
+int ImageToFtable (FTable *ftable, Header *theader, int format);
+int ImageToVtable (VTable *vtable, Header *theader, int format);
+Image *ImageElixirToInternal (ImageElixir *in, int Nvalues);
+ImageElixir *ImageInternalToElixir (Image *in, int Nvalues);
+Image *ImageLoneosToInternal (ImageLoneos *in, int Nvalues);
+ImageLoneos *ImageInternalToLoneos (Image *in, int Nvalues);
+Image *ImagePanstarrsToInternal (ImagePanstarrs *in, int Nvalues);
+ImagePanstarrs *ImageInternalToPanstarrs (Image *in, int Nvalues);
+
+/* skyregion APIs */
+int        SkyTableSave        	   PROTO((SkyTable *table, char *filename));
+SkyTable  *SkyTableLoad        	   PROTO((char *filename, int VERBOSE));
+SkyTable  *SkyTableFromGSC     	   PROTO((char *filename, int depth, int VERBOSE));
+SkyTable  *SkyTableLoadOptimal 	   PROTO((char *catdir, char *SKYFILE, char *GSCFILE, int depth, int VERBOSE));
+int        SkyTableSetDepth    	   PROTO((SkyTable *sky, int depth));
+SkyList   *SkyRegionByPoint    	   PROTO((SkyTable *table, int depth, double ra, double dec));
+SkyList   *SkyListByPoint      	   PROTO((SkyTable *table, double ra, double dec));
+SkyList   *SkyListByRadius     	   PROTO((SkyTable *table, int depth, double RA, double DEC, double radius));
+SkyList   *SkyListByPatch      	   PROTO((SkyTable *table, int depth, SkyRegion *patch));
+SkyList   *SkyListByName      	   PROTO((SkyTable *table, char *name));
+SkyList   *SkyListByImage      	   PROTO((SkyTable *table, int depth, Image *image));
+SkyList   *SkyListByBounds     	   PROTO((SkyTable *table, int depth, double Rmin, double Rmax, double Dmin, double Dmax));
+SkyList   *SkyListChildrenByBounds PROTO((SkyTable *table, int No, int depth, double Rmin, double Rmax, double Dmin, double Dmax));
+int        SkyListFree             PROTO((SkyList *list, int ELEMENTS));
+int        SkyTableFree            PROTO((SkyTable *table));
+int        SkyListSetFilenames     PROTO((SkyList *list, char *path, char *ext));
+int        SkyTableSetFilenames    PROTO((SkyTable *sky, char *path, char *ext));
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/elixir_defs.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/elixir_defs.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/elixir_defs.h	(revision 22322)
@@ -0,0 +1,11 @@
+Image 		       	*Image_Elixir_ToInternal (Image_Elixir *in, int Nvalues);
+Image_Elixir 	       	*ImageInternalTo_Elixir (Image *in, int Nvalues);
+Average 	       	*Average_Elixir_ToInternal (Average_Elixir *in, int Nvalues, SecFilt **primary);
+Average_Elixir 	        *AverageInternalTo_Elixir (Average *in, int Nvalues, SecFilt *primary);
+Measure 	       	*Measure_Elixir_ToInternal (Measure_Elixir *in, int Nvalues);
+Measure_Elixir 	        *MeasureInternalTo_Elixir (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_Elixir_ToInternal (SecFilt_Elixir *in, int Nvalues);
+SecFilt_Elixir 	       	*SecFiltInternalTo_Elixir (SecFilt *in, int Nvalues);
+
+PhotCode *PhotCode_Elixir_To_Internal (PhotCode_Elixir *in, int Nvalues);
+PhotCode_Elixir *PhotCode_Internal_To_Elixir (PhotCode *in, int Nvalues);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/loneos_defs.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/loneos_defs.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/loneos_defs.h	(revision 22322)
@@ -0,0 +1,8 @@
+Image 		       	*Image_Loneos_ToInternal (Image_Loneos *in, int Nvalues);
+Image_Loneos 	       	*ImageInternalTo_Loneos (Image *in, int Nvalues);
+Average                	*Average_Loneos_ToInternal (Average_Loneos *in, int Nvalues, SecFilt **primary);
+Average_Loneos 	        *AverageInternalTo_Loneos (Average *in, int Nvalues, SecFilt *primary);
+Measure                	*Measure_Loneos_ToInternal (Measure_Loneos *in, int Nvalues);
+Measure_Loneos 	        *MeasureInternalTo_Loneos (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_Loneos_ToInternal (SecFilt_Loneos *in, int Nvalues);
+SecFilt_Loneos 	       	*SecFiltInternalTo_Loneos (SecFilt *in, int Nvalues);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/panstarrs_dev_0_defs.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/panstarrs_dev_0_defs.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/panstarrs_dev_0_defs.h	(revision 22322)
@@ -0,0 +1,8 @@
+Image 		       	*Image_Panstarrs_DEV_0_ToInternal (Image_Panstarrs_DEV_0 *in, int Nvalues);
+Image_Panstarrs_DEV_0  	*ImageInternalTo_Panstarrs_DEV_0 (Image *in, int Nvalues);
+Average 	       	*Average_Panstarrs_DEV_0_ToInternal (Average_Panstarrs_DEV_0 *in, int Nvalues, SecFilt **primary);
+Average_Panstarrs_DEV_0 *AverageInternalTo_Panstarrs_DEV_0 (Average *in, int Nvalues, SecFilt *primary);
+Measure 	       	*Measure_Panstarrs_DEV_0_ToInternal (Measure_Panstarrs_DEV_0 *in, int Nvalues);
+Measure_Panstarrs_DEV_0 *MeasureInternalTo_Panstarrs_DEV_0 (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_Panstarrs_DEV_0_ToInternal (SecFilt_Panstarrs_DEV_0 *in, int Nvalues);
+SecFilt_Panstarrs_DEV_0 *SecFiltInternalTo_Panstarrs_DEV_0 (SecFilt *in, int Nvalues);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/panstarrs_dev_1_defs.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/panstarrs_dev_1_defs.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/panstarrs_dev_1_defs.h	(revision 22322)
@@ -0,0 +1,8 @@
+Image 		       	*Image_Panstarrs_DEV_1_ToInternal (Image_Panstarrs_DEV_1 *in, int Nvalues);
+Image_Panstarrs_DEV_1  	*ImageInternalTo_Panstarrs_DEV_1 (Image *in, int Nvalues);
+Average 	       	*Average_Panstarrs_DEV_1_ToInternal (Average_Panstarrs_DEV_1 *in, int Nvalues, SecFilt **primary);
+Average_Panstarrs_DEV_1 *AverageInternalTo_Panstarrs_DEV_1 (Average *in, int Nvalues, SecFilt *primary);
+Measure 	       	*Measure_Panstarrs_DEV_1_ToInternal (Measure_Panstarrs_DEV_1 *in, int Nvalues);
+Measure_Panstarrs_DEV_1 *MeasureInternalTo_Panstarrs_DEV_1 (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_Panstarrs_DEV_1_ToInternal (SecFilt_Panstarrs_DEV_1 *in, int Nvalues);
+SecFilt_Panstarrs_DEV_1 *SecFiltInternalTo_Panstarrs_DEV_1 (SecFilt *in, int Nvalues);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/ps1_dev_1_defs.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/ps1_dev_1_defs.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/ps1_dev_1_defs.h	(revision 22322)
@@ -0,0 +1,11 @@
+Image 		       	*Image_PS1_DEV_1_ToInternal (Image_PS1_DEV_1 *in, int Nvalues);
+Image_PS1_DEV_1  	*ImageInternalTo_PS1_DEV_1 (Image *in, int Nvalues);
+Average 	       	*Average_PS1_DEV_1_ToInternal (Average_PS1_DEV_1 *in, int Nvalues, SecFilt **primary);
+Average_PS1_DEV_1       *AverageInternalTo_PS1_DEV_1 (Average *in, int Nvalues, SecFilt *primary);
+Measure 	       	*Measure_PS1_DEV_1_ToInternal (Measure_PS1_DEV_1 *in, int Nvalues);
+Measure_PS1_DEV_1       *MeasureInternalTo_PS1_DEV_1 (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_PS1_DEV_1_ToInternal (SecFilt_PS1_DEV_1 *in, int Nvalues);
+SecFilt_PS1_DEV_1       *SecFiltInternalTo_PS1_DEV_1 (SecFilt *in, int Nvalues);
+
+PhotCode *PhotCode_PS1_DEV_1_To_Internal (PhotCode_PS1_DEV_1 *in, int Nvalues);
+PhotCode_PS1_DEV_1 *PhotCode_Internal_To_PS1_DEV_1 (PhotCode *in, int Nvalues);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/ps1_dev_2_defs.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/ps1_dev_2_defs.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/ps1_dev_2_defs.h	(revision 22322)
@@ -0,0 +1,11 @@
+Image 		       	*Image_PS1_DEV_2_ToInternal (Image_PS1_DEV_2 *in, int Nvalues);
+Image_PS1_DEV_2  	*ImageInternalTo_PS1_DEV_2 (Image *in, int Nvalues);
+Average 	       	*Average_PS1_DEV_2_ToInternal (Average_PS1_DEV_2 *in, int Nvalues, SecFilt **primary);
+Average_PS1_DEV_2       *AverageInternalTo_PS1_DEV_2 (Average *in, int Nvalues, SecFilt *primary);
+Measure 	       	*Measure_PS1_DEV_2_ToInternal (Measure_PS1_DEV_2 *in, int Nvalues);
+Measure_PS1_DEV_2       *MeasureInternalTo_PS1_DEV_2 (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_PS1_DEV_2_ToInternal (SecFilt_PS1_DEV_2 *in, int Nvalues);
+SecFilt_PS1_DEV_2       *SecFiltInternalTo_PS1_DEV_2 (SecFilt *in, int Nvalues);
+
+PhotCode *PhotCode_PS1_DEV_2_To_Internal (PhotCode_PS1_DEV_2 *in, int Nvalues);
+PhotCode_PS1_DEV_2 *PhotCode_Internal_To_PS1_DEV_2 (PhotCode *in, int Nvalues);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/ps1_dev_3_defs.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/ps1_dev_3_defs.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/include/ps1_dev_3_defs.h	(revision 22322)
@@ -0,0 +1,2 @@
+PhotCode *PhotCode_PS1_DEV_3_To_Internal (PhotCode_PS1_DEV_3 *in, int Nvalues);
+PhotCode_PS1_DEV_3 *PhotCode_Internal_To_PS1_DEV_3 (PhotCode *in, int Nvalues);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/lib/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/lib/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/lib/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.darwin.dylib
+*.darwin_x86.dylib
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/LoadPhotcodes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/LoadPhotcodes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/LoadPhotcodes.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include <dvo.h>
+
+int LoadPhotcodes (char *catdir_file, char *master_file) {
+
+  /* first try to load the photcodes from the specified CATDIR location */
+  if (LoadPhotcodesFITS (catdir_file)) return TRUE;
+  
+  /* next try to load the photcodes from the master text photcode file */
+  /* automatically (or on demand?) save the text file to the FITS version */
+  if (LoadPhotcodesText (master_file)) { 
+      if (!check_file_access (catdir_file, TRUE, TRUE)) return TRUE;
+      SavePhotcodesFITS (catdir_file);
+      return TRUE;
+  }
+
+  return FALSE;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/LoadPhotcodesFITS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/LoadPhotcodesFITS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/LoadPhotcodesFITS.c	(revision 22322)
@@ -0,0 +1,135 @@
+# include <dvo.h>
+
+/* this is a read-only function to load the FITS photcode file into the internal photcode table */
+/* locking is used to avoid collisions with programs trying to update the photcodes values */
+/* XXX better distinction between NOT FOUND and FAILURE */
+int LoadPhotcodesFITS (char *filename) {
+
+  PhotCodeData *table = NULL;
+  PhotCode *photcode;
+  FITS_DB db;
+  char extname[80];
+
+  int i, code, Ncode, Nsec, Nsecfilt;
+
+  /* XXX choose more sensible lock timeouts! */
+  db.lockstate = LCK_SOFT;
+  db.timeout   = 10.0;
+  gfits_db_init (&db);
+
+  /* does this mean the db is empty, non-existent, or has access errors? */
+  // XXX we need better error handling here
+  if (!gfits_db_lock (&db, filename)) {
+    // fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    return FALSE;
+  }
+  if (db.dbstate == LCK_EMPTY) {
+    // fprintf (stderr, "ERROR: db is empty\n");
+    gfits_db_close (&db);
+    return FALSE;
+  } 
+  if (!gfits_db_load (&db)) {
+    // fprintf (stderr, "ERROR: failure to load db\n");
+    gfits_db_close (&db);
+    return FALSE;
+  }
+  gfits_db_close (&db);
+
+  /* convert FITS format data to internal format (just byteswaps) */
+  gfits_scan (&db.theader, "EXTNAME", "%s", 1, extname);
+
+  if (!strcmp (extname, "DVO_PHOTCODE") || !strcmp (extname, "DVO_PHOTCODE_ELIXIR")) {
+    PhotCode_Elixir *photcode_elixir = gfits_table_get_PhotCode_Elixir (&db.ftable, &Ncode, &db.swapped);
+    photcode = PhotCode_Elixir_To_Internal (photcode_elixir, Ncode);
+    free (photcode_elixir);
+  } 
+
+  if (!strcmp (extname, "DVO_PHOTCODE_PS1_DEV_1")) {
+    PhotCode_PS1_DEV_1 *photcode_ps1_dev_1 = gfits_table_get_PhotCode_PS1_DEV_1 (&db.ftable, &Ncode, &db.swapped);
+    photcode = PhotCode_PS1_DEV_1_To_Internal (photcode_ps1_dev_1, Ncode);
+    free (photcode_ps1_dev_1);
+  } 
+
+  if (!strcmp (extname, "DVO_PHOTCODE_PS1_DEV_2")) {
+    PhotCode_PS1_DEV_2 *photcode_ps1_dev_2 = gfits_table_get_PhotCode_PS1_DEV_2 (&db.ftable, &Ncode, &db.swapped);
+    photcode = PhotCode_PS1_DEV_2_To_Internal (photcode_ps1_dev_2, Ncode);
+    free (photcode_ps1_dev_2);
+  } 
+
+  if (!strcmp (extname, "DVO_PHOTCODE_PS1_DEV_3")) {
+    PhotCode_PS1_DEV_3 *photcode_ps1_dev_3 = gfits_table_get_PhotCode_PS1_DEV_3 (&db.ftable, &Ncode, &db.swapped);
+    photcode = PhotCode_PS1_DEV_3_To_Internal (photcode_ps1_dev_3, Ncode);
+    free (photcode_ps1_dev_3);
+  } 
+
+  table = GetPhotcodeTable ();
+  if (table[0].code != NULL) free (table[0].code);
+  /* we are using a 16-bit int for the photcodes, so these indexes can be fixed-length */
+  /* XXX if we need to go with a larger photcode, we'll need to use a sequenced index and a
+     binary search to get to a given value (0x100000000 ints would take quite a few
+     bytes...) */
+  for (i = 0; i < 0x10000; i++) {
+    table[0].hashcode[i] = -1;
+    table[0].hashNsec[i] = -1;
+    table[0].codeNsec[i] = -1;
+  }
+
+  /* set up photcode indexes (see dvo_photcode_ops.c) */
+  Nsecfilt = 0;
+  for (i = 0; i < Ncode; i++) {
+    if (photcode[i].type == PHOT_ALT) continue; /* no hashcode for ALT codes */
+    if (table[0].hashcode[photcode[i].code] != -1) {
+      fprintf (stderr, "duplicate photcodes in file\n");
+      code = table[0].hashcode[photcode[i].code];
+      fprintf (stderr, "conflict between %s (%d) and %s (%d)\n",
+	       photcode[i].name, photcode[i].code, photcode[code].name, photcode[code].code);
+      free (photcode);
+      return FALSE;
+    }
+    table[0].hashcode[photcode[i].code] = i;
+    if (photcode[i].type == PHOT_SEC) {
+      table[0].hashNsec[photcode[i].code] = Nsecfilt;
+      table[0].codeNsec[Nsecfilt] = photcode[i].code;
+      Nsecfilt ++;
+    }
+  }
+
+  // validity check for references
+  // photcode.equiv of 0 means "undefined"
+  for (i = 0; i < Ncode; i++) {
+    if (photcode[i].type == PHOT_DEP) {
+      if (photcode[i].equiv == 0) continue;
+      Nsec = table[0].hashcode[photcode[i].equiv];
+      if ((Nsec >= Ncode) || (Nsec < 0)) {
+	fprintf (stderr, "reference for dependent photcode is not in photcodes\n");
+	free (photcode);
+	return FALSE;
+      }
+      if (photcode[Nsec].type != PHOT_SEC) {
+	fprintf (stderr, "reference for dependent photcode is not an average photcode\n");
+	free (photcode);
+	return FALSE;
+      }
+    }
+    if (photcode[i].type == PHOT_ALT) {
+      Nsec = table[0].hashcode[photcode[i].code];
+      if ((Nsec >= Ncode) || (Nsec < 0)) {
+	fprintf (stderr, "reference for alternate photcode is not in photcodes\n");
+	free (photcode);
+	return FALSE;
+      }
+      if (photcode[Nsec].type != PHOT_SEC) {
+	fprintf (stderr, "reference for alternate photcode is not an average photcode\n");
+	free (photcode);
+	return FALSE;
+      }
+    }
+  }
+
+  table[0].code     = photcode;
+  table[0].Ncode    = Ncode;
+  table[0].Nsecfilt = Nsecfilt;
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/LoadPhotcodesText.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/LoadPhotcodesText.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/LoadPhotcodesText.c	(revision 22322)
@@ -0,0 +1,275 @@
+# include <dvo.h>
+
+static int PhotcodeNumberOrName (char *word) {
+
+  int value;
+  char *endpt;
+
+  if (!strcmp (word, "-")) return (0);
+
+  value = strtol (word, &endpt, 10);
+  if (endpt != word + strlen(word)) {
+    value = GetPhotcodeCodebyName (word);
+  }
+  return (value);
+}
+
+/* load the text photcode table */
+int LoadPhotcodesText (char *filename) {
+  
+  /* XXX fix these */
+  PhotCodeData *table = NULL;
+  PhotCode *photcode;
+
+  FILE *f;
+  int i, Nsecfilt, Nsec, Ncode, NPHOTCODE, Nfield;
+  int code;
+  char *c;
+  char line[256], **c1_names, **c2_names, **eq_names;
+  char name[32], type[32], Zero[32], Airmass[32], Offset[32];
+  char C1[32], C2[32], Slope[32], Color[32], Primary[32];
+  char astromErrSys[32], astromErrScale[32], astromErrMagScale[32], photomErrSys[32];
+  char astromPoorMask[32], astromBadMask[32], photomPoorMask[32], photomBadMask[32];
+
+  table = GetPhotcodeTable ();
+  if (table[0].code != NULL) free (table[0].code);
+  /* we are using a 16-bit int for the photcodes, so these indexes can be fixed-length */
+  /* XXX if we need to go with a larger photcode, we'll need to use a sequenced index and a
+     binary search to get to a given value (0x100000000 ints would take quite a few
+     bytes...) */
+  for (i = 0; i < 0x10000; i++) {
+    table[0].hashcode[i] = -1;
+    table[0].hashNsec[i] = -1;
+    table[0].codeNsec[i] = -1;
+  }
+
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    table[0].Ncode    = 0;
+    table[0].Nsecfilt = 0;
+    table[0].code     = (PhotCode *) NULL;
+    return (FALSE);
+  }
+
+  Ncode = 0;
+  NPHOTCODE = 10;
+  ALLOCATE (photcode, PhotCode, NPHOTCODE);
+  ALLOCATE (eq_names, char *,   NPHOTCODE);
+  ALLOCATE (c1_names, char *,   NPHOTCODE);
+  ALLOCATE (c2_names, char *,   NPHOTCODE);
+
+  while (scan_line (f, line) != EOF) {
+    for (c = line; isspace (*c); c++);
+    if (*c == '#') continue;
+    Nfield = sscanf (c, "%d %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s", 
+		     &code, name, type, Zero, Airmass, Offset, C1, C2, Slope, Color, Primary, astromErrSys, astromErrScale, astromErrMagScale, photomErrSys, astromPoorMask, astromBadMask, photomPoorMask, photomBadMask);
+
+    switch (Nfield) {
+      case 11: 
+	// minimum number of fields : original elixir layout
+	strcpy (astromErrSys,      "0.0");
+	strcpy (astromErrScale,    "0.0");
+	strcpy (astromErrMagScale, "0.0");
+	strcpy (photomErrSys,      "0.0");
+	strcpy (astromPoorMask,    "0");
+	strcpy (astromBadMask,     "0");
+	strcpy (photomPoorMask,    "0");
+	strcpy (photomBadMask,     "0");
+	break;
+      case 14: 
+	// allow only astrom elements
+	strcpy (photomErrSys,      "0.0");
+	strcpy (astromPoorMask,    "0");
+	strcpy (astromBadMask,     "0");
+	strcpy (photomPoorMask,    "0");
+	strcpy (photomBadMask,     "0");
+	break;
+      case 15: 
+	strcpy (astromPoorMask,    "0");
+	strcpy (astromBadMask,     "0");
+	strcpy (photomPoorMask,    "0");
+	strcpy (photomBadMask,     "0");
+	break;
+      case 17: 
+	strcpy (photomPoorMask,    "0");
+	strcpy (photomBadMask,     "0");
+	break;
+      case 19: 
+	// all fields defined
+	break;
+      default:
+	// skip unknown layouts
+	continue;
+    }
+
+    if (!code) {
+	fprintf (stderr, "photcode values may not be 0: fix %s\n", name);
+	return (FALSE);
+    }
+
+    photcode[Ncode].type = 0;
+    photcode[Ncode].code = code;
+    memset (photcode[Ncode].name, 0, 32);
+    strcpy (photcode[Ncode].name, name);
+
+    if (!strncasecmp (type, "pri", 3)) {
+      photcode[Ncode].type  = PHOT_SEC;
+    }
+    if (!strncasecmp (type, "sec", 3)) {
+      photcode[Ncode].type  = PHOT_SEC;
+    }
+    if (!strncasecmp (type, "dep", 3)) {
+      photcode[Ncode].type  = PHOT_DEP;
+    }
+    if (!strncasecmp (type, "ref", 3)) {
+      photcode[Ncode].type  = PHOT_REF;
+    }
+    if (!strncasecmp (type, "alt", 3)) {
+      /* alt photcodes are a little different: they have the SAME photcode as an existing
+	 pri/sec photcode, but define an alternate transformation for that code */
+      photcode[Ncode].type  = PHOT_ALT;
+    }
+
+    photcode[Ncode].astromErrSys      = atof (astromErrSys);
+    photcode[Ncode].astromErrScale    = atof (astromErrScale);
+    photcode[Ncode].astromErrMagScale = atof (astromErrMagScale);
+    photcode[Ncode].photomErrSys      = atof (photomErrSys);
+
+    photcode[Ncode].astromPoorMask    = strtol (astromPoorMask, NULL, 0);
+    photcode[Ncode].astromBadMask     = strtol (astromBadMask, NULL, 0);
+    photcode[Ncode].photomPoorMask    = strtol (photomPoorMask, NULL, 0);
+    photcode[Ncode].photomBadMask     = strtol (photomBadMask, NULL, 0);
+
+    switch (photcode[Ncode].type) {
+      case PHOT_SEC:
+      case PHOT_ALT:
+      case PHOT_DEP:
+	photcode[Ncode].C     = 1000*atof (Zero);
+	photcode[Ncode].K     = atof (Airmass);
+	photcode[Ncode].dC    = 1000*atof (Offset);
+	photcode[Ncode].dX    = 1000*atof (Color);
+	c1_names[Ncode]       = strcreate (C1);
+	c2_names[Ncode]       = strcreate (C2);
+	eq_names[Ncode]       = strcreate (Primary);
+	ParseColorTerms (Slope, photcode[Ncode].X, &photcode[Ncode].Nc);
+	break;
+
+      case PHOT_REF:
+	photcode[Ncode].C     = 0;
+	photcode[Ncode].K     = 0;
+	photcode[Ncode].dC    = 0;
+	photcode[Ncode].dX    = 0;
+	c1_names[Ncode]       = strcreate ("0");
+	c2_names[Ncode]       = strcreate ("0");
+	eq_names[Ncode]       = strcreate (Primary);
+	photcode[Ncode].X[0]  = 0;
+	photcode[Ncode].Nc    = 0;
+	break;
+
+      default:
+	fprintf (stderr, "error: invalid photcode type\n");
+	exit (2);
+    }      
+
+    if (!photcode[Ncode].type) {
+      fprintf (stderr, "error in Photfile: unknown type %s\n", type);
+    }
+
+    Ncode++;
+    if (Ncode == NPHOTCODE) {
+      NPHOTCODE += 10;
+      REALLOCATE (photcode, PhotCode, NPHOTCODE);
+      REALLOCATE (eq_names, char *,   NPHOTCODE);
+      REALLOCATE (c1_names, char *,   NPHOTCODE);
+      REALLOCATE (c2_names, char *,   NPHOTCODE);
+    }
+  }
+  fclose (f);
+  
+  // convert the named references (c1, c2, equiv) to code values
+  for (i = 0; i < Ncode; i++) {
+    photcode[i].c1    = PhotcodeNumberOrName (c1_names[i]);
+    photcode[i].c2    = PhotcodeNumberOrName (c2_names[i]);
+    photcode[i].equiv = PhotcodeNumberOrName (eq_names[i]);
+    free (c1_names[i]);
+    free (c2_names[i]);
+    free (eq_names[i]);
+  }
+  free (c1_names);
+  free (c2_names);
+  free (eq_names);
+
+  /* set up photcode indexes (see dvo_photcode_ops.c) */
+  Nsecfilt = 0;
+  for (i = 0; i < Ncode; i++) {
+    if (photcode[i].type == PHOT_ALT) continue; /* no hashcode for ALT codes */
+    if (table[0].hashcode[photcode[i].code] != -1) {
+      fprintf (stderr, "duplicate photcodes in file\n");
+      code = table[0].hashcode[photcode[i].code];
+      fprintf (stderr, "conflict between %s (%d) and %s (%d)\n",
+	       photcode[i].name, photcode[i].code, photcode[code].name, photcode[code].code);
+      free (photcode);
+      return (FALSE);
+    }
+    table[0].hashcode[photcode[i].code] = i;
+    if (photcode[i].type == PHOT_SEC) {
+      table[0].hashNsec[photcode[i].code] = Nsecfilt;
+      table[0].codeNsec[Nsecfilt] = photcode[i].code;
+      Nsecfilt ++;
+    }
+  }
+
+  // validity check for references
+  // photcode.equiv of 0 means "undefined"
+  for (i = 0; i < Ncode; i++) {
+    if (photcode[i].type == PHOT_DEP) {
+      if (photcode[i].equiv == 0) continue;
+      Nsec = table[0].hashcode[photcode[i].equiv];
+      if ((Nsec >= Ncode) || (Nsec < 0)) {
+	fprintf (stderr, "reference for dependent photcode is not a valid photcode\n");
+	free (photcode);
+	return (FALSE);
+      }
+      if (photcode[Nsec].type != PHOT_SEC) {
+	fprintf (stderr, "reference for dependent photcode is not an average photcode\n");
+	free (photcode);
+	return (FALSE);
+      }
+    }
+    if (photcode[i].type == PHOT_ALT) {
+      Nsec = table[0].hashcode[photcode[i].code];
+      if ((Nsec >= Ncode) || (Nsec < 0)) {
+	fprintf (stderr, "reference for alternate photcode is not in photcodes\n");
+	free (photcode);
+	return (FALSE);
+      }
+      if (photcode[Nsec].type != PHOT_SEC) {
+	fprintf (stderr, "reference for alternate photcode is not an average photcode\n");
+	free (photcode);
+	return (FALSE);
+      }
+    }
+  }
+  table[0].code     = photcode;
+  table[0].Ncode    = Ncode;
+  table[0].Nsecfilt = Nsecfilt;
+
+  return (TRUE);
+}
+
+# define NCTERMS 4
+void ParseColorTerms (char *terms, float *X, int *N) {
+
+  int i;
+  char *p;
+
+  p = terms;
+
+  for (i = 0; (p != NULL) && (i < NCTERMS); i++) {
+    X[i] = atof (p);
+    p = strchr (p, ',');
+    if (p == (char *) NULL) continue;
+    p ++;
+  }
+  *N = i;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/SavePhotcodesFITS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/SavePhotcodesFITS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/SavePhotcodesFITS.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include <dvo.h>
+
+/* this function saves the FITS photcode file into the internal photcode table */
+/* locking is used to avoid collisions with programs trying to update the photcodes values */
+/* XXX better distinction between NOT FOUND and FAILURE */
+int SavePhotcodesFITS (char *filename) {
+
+  PhotCodeData *table = NULL;
+  FITS_DB db;
+
+  table = GetPhotcodeTable ();
+  if (table == NULL) {
+    fprintf (stderr, "ERROR: no internal photcode table is defined\n");
+    return FALSE;
+  }
+
+  /* XXX choose more sensible lock timeouts! */
+  db.lockstate = LCK_XCLD;
+  db.timeout   = 10.0;
+  gfits_db_init (&db);
+
+  /* does this mean the db is empty, non-existent, or has access errors? */
+  if (!gfits_db_lock (&db, filename)) {
+    fprintf (stderr, "ERROR: failure to lock db, cannot save photcode table to %s\n", filename);
+    gfits_db_close (&db);
+    return FALSE;
+  }
+
+  // for the moment, we simply support the latest photcode format for output
+  // XXX update this as needed as new formats are defined
+  PhotCode_PS1_DEV_3 *photcode_output = PhotCode_Internal_To_PS1_DEV_3 (table[0].code, table[0].Ncode);
+
+  /* convert FITS format data to internal format (byteswaps & EXTNAME) */
+  gfits_db_create (&db);
+  gfits_table_set_PhotCode_PS1_DEV_3 (&db.ftable, photcode_output, table[0].Ncode);
+  gfits_db_save (&db);
+  gfits_db_close (&db);
+
+  free (photcode_output);
+
+  return TRUE;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/SavePhotcodesText.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/SavePhotcodesText.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/SavePhotcodesText.c	(revision 22322)
@@ -0,0 +1,111 @@
+# include <dvo.h>
+# define SCALE 0.001
+
+static char *PHOT_SEC_NAME = "sec";
+static char *PHOT_ALT_NAME = "alt";
+static char *PHOT_REF_NAME = "ref";
+static char *PHOT_DEP_NAME = "dep";
+
+/* this function saves the FITS photcode file into the internal photcode table */
+/* locking is used to avoid collisions with programs trying to update the photcodes values */
+/* XXX better distinction between NOT FOUND and FAILURE */
+int SavePhotcodesText (char *filename) {
+
+  PhotCodeData *table = NULL;
+  struct stat filestat;
+  char *type;
+  int i, j, status;
+  FILE *f;
+
+  table = GetPhotcodeTable ();
+  if (table == NULL) {
+    fprintf (stderr, "ERROR: no internal photcode table is defined\n");
+    return FALSE;
+  }
+
+  /* check if file exists */
+  status = stat (filename, &filestat);
+  if (status == -1) {
+    if (errno != ENOENT) {
+      fprintf (stderr, "ERROR: problem accessing output path for%s\n", filename);
+      return FALSE;
+    }
+  } else {
+    make_backup (filename);
+  } 
+
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: problem creating photcode file %s\n", filename);
+    return FALSE;
+  }
+
+  fprintf (f, "#                                           airmass      color                         astrometry  mag    photom  astrom mask    photom mask\n");
+  fprintf (f, "# code  name                type    zero  slope offset c1    c2   slope   zero  equiv  sys scale   scale  sys     poor   bad     poor   bad\n");
+  //          "  1     g                    sec   0.000  0.000 0.000  1003  1004 0.0160     0  1051   0.000 0.000 0.000  0.000   0xffff 0xffff  0xffff 0xffff
+
+  for (i = 0; i < table[0].Ncode; i++) {
+    switch (table[0].code[i].type) {
+      case PHOT_SEC:
+	type = PHOT_SEC_NAME;
+	break;
+      case PHOT_ALT:
+	type = PHOT_ALT_NAME;
+	break;
+      case PHOT_REF:
+	type = PHOT_REF_NAME;
+	break;
+      case PHOT_DEP:
+	type = PHOT_DEP_NAME;
+	break;
+      default:
+	fprintf (stderr, "ERROR: problem with photcode type for %s\n", GetPhotcodeNamebyCode(table[0].code[i].code));
+	return FALSE;
+    }
+
+    fprintf (f, "  %-5d %-18s  %4s  %6.3f %6.3f %5.3f ",
+	     table[0].code[i].code,
+	     GetPhotcodeNamebyCode (table[0].code[i].code),
+	     type,
+	     table[0].code[i].C*SCALE, 
+	     table[0].code[i].K*SCALE, 
+	     table[0].code[i].dC*SCALE);
+
+    PrintPhotcodeNamebyCode (f, "%5d ", table[0].code[i].c1);
+    PrintPhotcodeNamebyCode (f, "%5d ", table[0].code[i].c2);
+
+    for (j = 0; j < table[0].code[i].Nc - 1; j++) {
+      fprintf (f, " %6.4f,", table[0].code[i].X[j]);
+    }
+    fprintf (f, "%6.4f %5d ", table[0].code[i].X[j], table[0].code[i].dX);
+    PrintPhotcodeNamebyCode (f, "%5d ", table[0].code[i].equiv);
+
+    fprintf (f, "  %5.3f %5.3f %5.3f  %5.3f", 
+	     table[0].code[i].astromErrSys, 
+	     table[0].code[i].astromErrScale, 
+	     table[0].code[i].astromErrMagScale, 
+	     table[0].code[i].photomErrSys); 
+
+    fprintf (f, "   0x%04x 0x%04x  0x%04x 0x%04x", 
+	     table[0].code[i].astromPoorMask, 
+	     table[0].code[i].astromBadMask, 
+	     table[0].code[i].photomPoorMask, 
+	     table[0].code[i].photomBadMask); 
+
+    fprintf (f, "\n");
+  }
+  fclose (f);
+  return TRUE;
+}
+
+void PrintPhotcodeNamebyCode (FILE *f, char *format, int code) {
+
+  char *name;
+  
+  name = GetPhotcodeNamebyCode (code);
+  if (name == NULL) {
+    fprintf (f, "    - ");
+  } else {
+    fprintf (f, format, code);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/coordops.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/coordops.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/coordops.c	(revision 22322)
@@ -0,0 +1,758 @@
+# include <dvo.h>
+
+static Coords mosaic;
+static int gotMosaic = FALSE;
+
+void RegisterMosaic (Coords *coords) {
+  mosaic = *coords;
+  gotMosaic = TRUE;
+}
+
+int isRegisteredMosaic () {
+  return (gotMosaic);
+}
+
+int GetMosaicCoords (Coords *coords) {
+  *coords = mosaic;
+  return (TRUE);
+}
+
+int XY_to_LM (double *L, double *M, double x, double y, Coords *coords) {
+
+  double X, Y;
+
+  /** convert pixel coordinates to cartesian system **/
+  X = coords[0].cdelt1*(x - coords[0].crpix1);
+  Y = coords[0].cdelt2*(y - coords[0].crpix2);
+
+  *L = (X*coords[0].pc1_1 + Y*coords[0].pc1_2);
+  *M = (X*coords[0].pc2_1 + Y*coords[0].pc2_2);
+
+  /** extra polynomial terms **/
+  if (coords[0].Npolyterms > 1) {
+    *L += X*X*coords[0].polyterms[0][0] + X*Y*coords[0].polyterms[1][0] + Y*Y*coords[0].polyterms[2][0];
+    *M += X*X*coords[0].polyterms[0][1] + X*Y*coords[0].polyterms[1][1] + Y*Y*coords[0].polyterms[2][1];
+  }
+  if (coords[0].Npolyterms > 2) {
+    *L += X*X*X*coords[0].polyterms[3][0] + X*X*Y*coords[0].polyterms[4][0] + X*Y*Y*coords[0].polyterms[5][0] + Y*Y*Y*coords[0].polyterms[6][0];
+    *M += X*X*X*coords[0].polyterms[3][1] + X*X*Y*coords[0].polyterms[4][1] + X*Y*Y*coords[0].polyterms[5][1] + Y*Y*Y*coords[0].polyterms[6][1];
+  }
+
+  return (TRUE);
+}
+
+int LM_to_RD (double *ra, double *dec, double L, double M, Coords *coords) {
+
+  OhanaProjection proj;
+  OhanaProjectionMode mode;
+  double R, T, Z, Z2, sphi, cphi, stht, ctht;
+  double alpha, delta, salp, calp, sdel, sdp, cdp;
+
+  proj = GetProjection (coords[0].ctype);
+  mode = GetProjectionMode (proj);
+  if (proj == PROJ_NONE) return (FALSE);
+  if (proj == PROJ_MODE_NONE) return (FALSE);
+
+  stht = ctht = 1;
+
+  /** Locally Cartesian Projections **/
+  if (mode == PROJ_MODE_CARTESIAN) {
+    *ra  = L + coords[0].crval1;
+    *dec = M + coords[0].crval2;
+
+    /* mosaic astrometry : WRP is chip astrometry; apply mosaic (DIS) term */
+    if (proj == PROJ_WRP) {
+      if (!gotMosaic) return (FALSE);
+      XY_to_RD (ra, dec, *ra, *dec, &mosaic);
+    }
+    return (TRUE);
+  }
+  
+  /** Zenithal Projections **/
+  if (mode == PROJ_MODE_ZENITHAL) {
+    R = hypot (L,M);
+    if ((L == 0) && (M == 0)) {
+      sphi = 0;
+      cphi = 1;
+    } else {
+      sphi =  L / R;
+      cphi = -M / R;
+    }
+
+    switch (proj) {
+      case PROJ_TAN:
+      case PROJ_TNX:
+      case PROJ_DIS:
+	// R = cot (theta) = cos(theta) / sin(theta)
+	if (R == 0) {
+	  stht = 1.0;
+	  ctht = 0.0;
+	} else {
+	  T = DEG_RAD / R;
+	  stht =   T / sqrt ( 1.0 + T*T);
+	  ctht = 1.0 / sqrt ( 1.0 + T*T);
+	}
+	break;
+      case PROJ_STG:
+	// R = (180/pi) [2 cos (theta)  / 1 + sin (theta)]
+	stht = (4 - RAD_DEG*R) / (4 + RAD_DEG*R);
+	ctht = sqrt (1 - stht*stht);
+	break;
+      case PROJ_SIN:
+	// R = (180/pi) cos (theta)
+	ctht = RAD_DEG * R;
+	stht = sqrt (1 - ctht*ctht);
+	break;
+      case PROJ_ARC:
+	// R = 90 - theta (degrees)
+	ctht = sin (RAD_DEG * R);
+	stht = cos (RAD_DEG * R);
+	break;
+      case PROJ_ZEA:
+      case PROJ_ZPL:
+	if (R > 2*DEG_RAD) {
+	  *ra = L;
+	  *dec = M;
+	  return (FALSE);
+	}
+	stht = 1 - 0.5*SQ(R*RAD_DEG);
+	ctht = sqrt (1 - stht*stht);
+	break;
+      default:
+	return (FALSE);
+    }
+    sdp  = sin(RAD_DEG*coords[0].crval2);
+    cdp  = cos(RAD_DEG*coords[0].crval2);
+    
+    sdel = stht*sdp - ctht*cphi*cdp;
+    salp = ctht*sphi;
+    calp = stht*cdp + ctht*cphi*sdp;
+    alpha = atan2 (salp, calp);
+    delta = asin (sdel);
+    
+    *ra  = DEG_RAD*alpha + coords[0].crval1;
+    *dec = DEG_RAD*delta;
+
+    /* rationalize ra range 0 - 360.0 */
+    while (*ra < 0.0)   *ra += 360.0;
+    while (*ra > 360.0) *ra -= 360.0;
+
+    return (TRUE);
+  }
+  
+  /**** Other Conventional Projections ****/
+  if (mode == PROJ_MODE_PSEUDOCYL) {
+    switch (proj) {
+      case PROJ_AIT:
+      Z2 = (1.0 - SQ(RAD_DEG*0.25*L) - SQ(RAD_DEG*0.5*M));
+      if (Z2 < 0) return (FALSE);
+      Z = sqrt (Z2);
+      alpha = 2.0 * DEG_RAD * atan2 (RAD_DEG*0.5*Z*L, 2.0*Z2 - 1.0);
+      delta = DEG_RAD * asin (RAD_DEG*M*Z);
+      break;
+      
+      case PROJ_GLS:
+	/* L,M in degrees, alpha,delta in degrees */
+	alpha = L / cos (RAD_DEG * M);
+	delta = M;
+	break;
+      case PROJ_PAR:
+	/* L,M in degrees, alpha,delta in degrees */
+	alpha = L / (1.0 - SQ(2.0*M/180));
+	delta = 3 * DEG_RAD * asin (M/180.0);
+	break;
+      default:
+	return (FALSE);
+    }
+    *ra  = alpha + coords[0].crval1;
+    *dec = delta + coords[0].crval2;
+
+    /* rationalize ra range 0 - 360.0 */
+    while (*ra < 0.0)   *ra += 360.0;
+    while (*ra > 360.0) *ra -= 360.0;
+
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+int RD_to_LM (double *L, double *M, double ra, double dec, Coords *coords) {
+
+  double phi, theta;
+  double Lo, Mo;
+  double sphi, cphi, stht, ctht;
+  double salp, calp, sdel, cdel, sdp, cdp;
+  double P, A, Rc;
+  OhanaProjection proj;
+  OhanaProjectionMode mode;
+
+  *L = *M = 0;
+
+  proj = GetProjection (coords[0].ctype);
+  mode = GetProjectionMode (proj);
+  if (proj == PROJ_NONE) return (FALSE);
+  if (proj == PROJ_MODE_NONE) return (FALSE);
+
+  /**** Locally Cartesian Projections ****/
+  if (mode == PROJ_MODE_CARTESIAN) {
+    if (proj == PROJ_WRP) {
+      if (!gotMosaic) return (FALSE);
+      RD_to_XY (&Lo, &Mo, ra, dec, &mosaic);
+      *L = (Lo - coords[0].crval1);
+      *M = (Mo - coords[0].crval2);
+      return (TRUE);
+    }
+    *L = (ra  - coords[0].crval1);
+    *M = (dec - coords[0].crval2);
+    return (TRUE);
+  }
+  
+  /**** Zenithal Projections ****/
+  if (mode == PROJ_MODE_ZENITHAL) {
+    sdp  = sin(RAD_DEG*coords[0].crval2);
+    cdp  = cos(RAD_DEG*coords[0].crval2);
+    salp = sin(RAD_DEG*(ra - coords[0].crval1));
+    calp = cos(RAD_DEG*(ra - coords[0].crval1));
+    sdel = sin(RAD_DEG*dec);
+    cdel = cos(RAD_DEG*dec);
+
+    stht = sdel*sdp + cdel*cdp*calp;    /* sin(theta) */
+    sphi = cdel*salp;                   /* = cos(theta)*sin(phi) */
+    cphi = cdel*sdp*calp - sdel*cdp;    /* = cos(theta)*cos(phi) */
+
+    // L = +R sin(phi)
+    // M = -R cos(phi)
+
+    switch (proj) {
+      case PROJ_TAN:
+      case PROJ_TNX:
+      case PROJ_DIS:
+	// R = cot (theta) = cos(theta) / sin(theta)
+	Rc = hypot(sphi, cphi);
+	*L = (stht == 0) ? 180.0 * sphi / Rc :  +DEG_RAD * sphi / stht;
+	*M = (stht == 0) ? 180.0 * cphi / Rc :  -DEG_RAD * cphi / stht;
+	return (stht > 0);
+      case PROJ_STG:
+	// R = 2 cos(theta) / [1 + sin(theta)]
+	Rc = DEG_RAD * 2 / (1 + stht);
+	*L = +Rc * sphi;
+	*M = -Rc * cphi;
+	return (stht > 0);
+      case PROJ_SIN:
+	// R = cos(theta)
+	*L = +DEG_RAD * sphi;
+	*M = -DEG_RAD * cphi;
+	return (stht > 0);
+      case PROJ_ARC:
+	// R = 90 - theta
+	ctht = hypot(sphi, cphi);
+	theta = atan2 (stht, ctht);
+	Rc = 90 - DEG_RAD * theta;
+	*L = (ctht == 0.0) ? 0.0 : +Rc * sphi / ctht ;
+	*M = (ctht == 0.0) ? 0.0 : -Rc * cphi / ctht ;
+	return (TRUE);
+      case PROJ_ZEA:
+      case PROJ_ZPL:
+	// R = 90 - theta
+	Rc = DEG_RAD * M_SQRT2 / sqrt (1 + stht);
+	*L =  Rc * sphi;
+	*M = -Rc * cphi;
+	return (stht > 0);
+      default:
+	return (FALSE);
+    }
+    return (FALSE);
+  }
+
+  /**** Other Standard Projections ****/
+  if (mode == PROJ_MODE_PSEUDOCYL) {
+    switch (proj) {
+      case PROJ_AIT:
+	phi = RAD_DEG*(ra - coords[0].crval1);
+	theta = RAD_DEG*(dec - coords[0].crval2);
+	P = 1.0 + cos (theta) * cos (0.5*phi);
+	if (P == 0.0) {
+	  *L =  0.0;
+	  *M =  0.0;
+	  return (TRUE);
+	} 
+	A =  DEG_RAD * sqrt (2.0 / P);
+	*L =  2.0 * A * cos (theta) * sin (0.5*phi);
+	*M =  A * sin (theta);
+	return (TRUE);
+      case PROJ_GLS:
+	phi = ra - coords[0].crval1;
+	theta = dec - coords[0].crval2;
+	*L = phi * cos(RAD_DEG * theta);
+	*M = theta;
+	return (TRUE);
+      case PROJ_PAR:
+	phi = ra - coords[0].crval1;
+	theta = dec - coords[0].crval2;
+	*L = phi * (2.0*cos(2*RAD_DEG*theta/3.0) - 1);
+	*M = 180.0 * sin (RAD_DEG*theta/3.0);
+	return (TRUE);
+      default:
+	return (FALSE);
+    }
+    return (FALSE);
+  }
+  return (FALSE);
+}
+
+int LM_to_XY (double *x, double *y, double L, double M, Coords *coords) {
+
+  int i;
+  double determ;
+  double X, Y, Lo, Mo, dL, dM;
+
+  *x = 0;
+  *y = 0;
+
+  /* convert L,M to X,Y */
+  determ = 1.0 / (coords[0].pc1_1*coords[0].pc2_2 - coords[0].pc1_2*coords[0].pc2_1);
+  X = determ * (coords[0].pc2_2*L - coords[0].pc1_2*M);
+  Y = determ * (coords[0].pc1_1*M - coords[0].pc2_1*L);
+
+  /** extra polynomial terms **/
+  if (coords[0].Npolyterms > 1) {
+    for (i = 0; i < 10; i++) {
+      Lo = (X*coords[0].pc1_1 + Y*coords[0].pc1_2);
+      Mo = (X*coords[0].pc2_1 + Y*coords[0].pc2_2);
+      if (coords[0].Npolyterms > 1) {
+	Lo += X*X*coords[0].polyterms[0][0] + X*Y*coords[0].polyterms[1][0] + Y*Y*coords[0].polyterms[2][0];
+	Mo += X*X*coords[0].polyterms[0][1] + X*Y*coords[0].polyterms[1][1] + Y*Y*coords[0].polyterms[2][1];
+      }
+      if (coords[0].Npolyterms > 2) {
+	Lo += X*X*X*coords[0].polyterms[3][0] + X*X*Y*coords[0].polyterms[4][0] + X*Y*Y*coords[0].polyterms[5][0] + Y*Y*Y*coords[0].polyterms[6][0];
+	Mo += X*X*X*coords[0].polyterms[3][1] + X*X*Y*coords[0].polyterms[4][1] + X*Y*Y*coords[0].polyterms[5][1] + Y*Y*Y*coords[0].polyterms[6][1];
+      }
+      dL = (L - Lo);
+      dM = (M - Mo);
+
+      X += determ * (coords[0].pc2_2*dL - coords[0].pc1_2*dM);
+      Y += determ * (coords[0].pc1_1*dM - coords[0].pc2_1*dM);
+    }
+  }
+  /* check for correct size (iterate?) */
+
+  *x = X / coords[0].cdelt1 + coords[0].crpix1;
+  *y = Y / coords[0].cdelt2 + coords[0].crpix2;
+
+  return (TRUE);
+}
+
+int XY_to_RD (double *ra, double *dec, double x, double y, Coords *coords) {
+
+  double L, M;
+  int status;
+
+  status = XY_to_LM (&L, &M, x, y, coords);
+  if (!status) return FALSE;
+
+  status = LM_to_RD (ra, dec, L, M, coords);
+  return (status);
+}
+
+int RD_to_XY (double *x, double *y, double ra, double dec, Coords *coords) {
+
+  double L, M;
+  int status;
+
+  status = RD_to_LM (&L, &M, ra, dec, coords);
+
+  if (finite(L) && finite(M)) {
+      LM_to_XY (x, y, L, M, coords);
+  } else {
+      *x = L;
+      *y = M;
+  }
+  return (status);
+}
+
+int fRD_to_XY (float *x, float *y, double ra, double dec, Coords *coords) {
+
+  int status;
+  double tmpx, tmpy;
+
+  status = RD_to_XY (&tmpx, &tmpy, ra, dec, coords);
+  *x = tmpx;
+  *y = tmpy;
+  
+  return (status);
+
+}
+
+int fXY_to_RD (float *ra, float *dec, double x, double y, Coords *coords) {
+
+  int status;
+  double tmpr, tmpd;
+
+  status = XY_to_RD (&tmpr, &tmpd, x, y, coords);
+  *ra = tmpr;
+  *dec = tmpd;
+  
+  return (status);
+
+}
+
+int GetCoords (Coords *coords, Header *header) {
+  
+  int status, itmp, Polynomial, Polyterm;
+  double Lambda, rotate, scale;
+  double equinox;
+  char *ctype;
+  
+  rotate = 0.0;
+  coords[0].crval1 = coords[0].crpix1 = coords[0].cdelt1 = 0.0;
+  coords[0].crval2 = coords[0].crpix2 = coords[0].cdelt2 = 0.0;
+  coords[0].pc1_1 = coords[0].pc2_2 = 1.0;
+  coords[0].pc2_1 = coords[0].pc1_2 = 0.0;
+  coords[0].Npolyterms = 1;
+  strcpy (coords[0].ctype, "NONE");
+  
+  status = FALSE; 
+  if (gfits_scan (header, "CTYPE2", "%s", 1, coords[0].ctype)) {
+    status  = gfits_scan (header, "CRVAL1", "%lf", 1, &coords[0].crval1);
+    status &= gfits_scan (header, "CRPIX1", "%f", 1, &coords[0].crpix1);
+    status &= gfits_scan (header, "CRVAL2", "%lf", 1, &coords[0].crval2);  
+    status &= gfits_scan (header, "CRPIX2", "%f", 1, &coords[0].crpix2);
+
+    if (gfits_scan (header, "CDELT1", "%f", 1, &coords[0].cdelt1)) {
+      status &= gfits_scan (header, "CDELT2", "%f", 1, &coords[0].cdelt2);
+      if (gfits_scan (header, "CROTA2", "%lf", 1, &rotate)) {
+	Lambda = coords[0].cdelt2 / coords[0].cdelt1;
+	coords[0].pc1_1 =  cos(rotate*RAD_DEG);
+	coords[0].pc1_2 = -sin(rotate*RAD_DEG) * Lambda;
+	coords[0].pc2_1 =  sin(rotate*RAD_DEG) / Lambda;
+	coords[0].pc2_2 =  cos(rotate*RAD_DEG);
+      }
+      if (gfits_scan (header, "PC001001", "%f", 1, &coords[0].pc1_1)) {
+	status &= gfits_scan (header, "PC001002", "%f", 1, &coords[0].pc1_2);
+	status &= gfits_scan (header, "PC002001", "%f", 1, &coords[0].pc2_1);
+	status &= gfits_scan (header, "PC002002", "%f", 1, &coords[0].pc2_2);
+      }
+
+      /* set NPLYTERM based on header.  if NPLYTERM is missing, it should have a 
+	 value of 0, unless the projection type is one of PLY, DIS, WRP, in which
+	 case it should be set to 3 */
+      ctype = &coords[0].ctype[4];
+      Polynomial = !strcmp (ctype, "-PLY") || !strcmp (ctype, "-DIS") || !strcmp (ctype, "-WRP");
+      Polyterm = gfits_scan (header, "NPLYTERM", "%d", 1, &itmp);
+
+      coords[0].Npolyterms = 0;
+      if (Polynomial && !Polyterm) coords[0].Npolyterms = 1;
+      if (Polyterm) coords[0].Npolyterms = itmp;
+
+      switch (coords[0].Npolyterms) {
+	case 3:
+	  status &= gfits_scan (header, "PCA1X3Y0", "%f", 1, &coords[0].polyterms[3][0]);
+	  status &= gfits_scan (header, "PCA1X2Y1", "%f", 1, &coords[0].polyterms[4][0]);
+	  status &= gfits_scan (header, "PCA1X1Y2", "%f", 1, &coords[0].polyterms[5][0]);
+	  status &= gfits_scan (header, "PCA1X0Y3", "%f", 1, &coords[0].polyterms[6][0]);
+	  status &= gfits_scan (header, "PCA2X3Y0", "%f", 1, &coords[0].polyterms[3][1]);
+	  status &= gfits_scan (header, "PCA2X2Y1", "%f", 1, &coords[0].polyterms[4][1]);
+	  status &= gfits_scan (header, "PCA2X1Y2", "%f", 1, &coords[0].polyterms[5][1]);
+	  status &= gfits_scan (header, "PCA2X0Y3", "%f", 1, &coords[0].polyterms[6][1]);
+	case 2:
+	  status &= gfits_scan (header, "PCA1X2Y0", "%f", 1, &coords[0].polyterms[0][0]);
+	  status &= gfits_scan (header, "PCA1X1Y1", "%f", 1, &coords[0].polyterms[1][0]);
+	  status &= gfits_scan (header, "PCA1X0Y2", "%f", 1, &coords[0].polyterms[2][0]);
+	  status &= gfits_scan (header, "PCA2X2Y0", "%f", 1, &coords[0].polyterms[0][1]);
+	  status &= gfits_scan (header, "PCA2X1Y1", "%f", 1, &coords[0].polyterms[1][1]);
+	  status &= gfits_scan (header, "PCA2X0Y2", "%f", 1, &coords[0].polyterms[2][1]);
+	case 0:
+	case 1:
+	  break;
+      }
+    } else {
+      if (gfits_scan (header, "CD1_1", "%f", 1, &coords[0].pc1_1)) {
+	status &= gfits_scan (header, "CD1_2", "%f", 1, &coords[0].pc1_2);
+	status &= gfits_scan (header, "CD2_1", "%f", 1, &coords[0].pc2_1);
+	status &= gfits_scan (header, "CD2_2", "%f", 1, &coords[0].pc2_2);
+	/* renormalize */
+	scale = hypot (coords[0].pc1_1, coords[0].pc1_2);
+	coords[0].cdelt1 = coords[0].cdelt2 = scale;
+	coords[0].pc1_1 /= scale;
+	coords[0].pc1_2 /= scale;
+	coords[0].pc2_1 /= scale;
+	coords[0].pc2_2 /= scale;
+      } else {
+	status = FALSE;
+      }
+    }
+  } else {
+    /* some of my thesis data uses this simple linear model - convert on read? */
+    if (gfits_scan (header, "RA_O", "%lf", 1, &coords[0].crval1)) {
+      status  = gfits_scan (header, "RA_X", "%f", 1, &coords[0].pc1_1);
+      status &= gfits_scan (header, "RA_Y", "%f", 1, &coords[0].pc1_2);
+      status &= gfits_scan (header, "DEC_O", "%lf", 1, &coords[0].crval2);  
+      status &= gfits_scan (header, "DEC_X", "%f", 1, &coords[0].pc2_1);
+      status &= gfits_scan (header, "DEC_Y", "%f", 1, &coords[0].pc2_2);
+      coords[0].crpix1 = coords[0].crpix2 = 0.0;
+      coords[0].cdelt1 = coords[0].cdelt2 = 1.0;
+      strcpy (coords[0].ctype, "GENE");
+    }
+  }
+  if (status) {
+    if (!gfits_scan (header, "EQUINOX", "%lf", 1, &equinox)) {
+      if (!gfits_scan (header, "EPOCH", "%lf", 1, &equinox)) {
+	equinox = 2000.0;
+      }
+    }
+    if (fabs (equinox - 2000.0) > 0.1) {
+      coords_precess (&coords[0].crval1, &coords[0].crval2, equinox, 2000.0);
+    } 
+  }
+  if (!status) {
+    fprintf (stderr, "error getting all elements for coordinate mode %s\n", coords[0].ctype);
+    coords[0].crval1 = coords[0].crpix1 = coords[0].cdelt1 = 0.0;
+    coords[0].crval2 = coords[0].crpix2 = coords[0].cdelt2 = 0.0;
+    coords[0].pc1_1 = coords[0].pc2_2 = 1.0;
+    coords[0].pc2_1 = coords[0].pc1_2 = 0.0;
+    strcpy (coords[0].ctype, "NONE");
+  }
+  return (status);
+}
+
+int PutCoords (Coords *coords, Header *header) {
+  
+  int OldAIPS;
+  char csys[16], ctype[16];
+  double rotate, Lambda;
+
+  /* modifications to the ctype? */
+  OldAIPS = FALSE;
+  gfits_modify (header, "CTYPE2",   "%s",  1, coords[0].ctype);
+  if (!strcmp(coords[0].ctype, "MM")) {
+    gfits_modify (header, "CTYPE1",   "%s",  1, "LL");
+    OldAIPS = TRUE;
+  } else {
+    strcpy (csys, "NONE");
+    if (!strncmp (coords[0].ctype, "DEC-", 4)) strcpy (csys, "RA--");
+    if (!strncmp (coords[0].ctype, "GLAT", 4)) strcpy (csys, "GLON");
+    if (!strncmp (coords[0].ctype, "ELAT", 4)) strcpy (csys, "ELON");
+    if (!strncmp (coords[0].ctype, "HLAT", 4)) strcpy (csys, "HLON");
+    if (!strncmp (coords[0].ctype, "SLAT", 4)) strcpy (csys, "SLON");
+    if (!strcmp (csys, "NONE")) return (FALSE);
+    sprintf (ctype, "%s-%s", csys, &coords[0].ctype[5]);
+    gfits_modify (header, "CTYPE1",   "%s",  1, ctype);
+  }    
+
+  gfits_modify (header, "CDELT1",   "%le", 1, coords[0].cdelt1); 
+  gfits_modify (header, "CDELT2",   "%le", 1, coords[0].cdelt2);
+  gfits_modify (header, "CRVAL1",   "%lf", 1, coords[0].crval1);
+  gfits_modify (header, "CRVAL2",   "%lf", 1, coords[0].crval2);  
+  gfits_modify (header, "CRPIX1",   "%lf", 1, coords[0].crpix1);
+  gfits_modify (header, "CRPIX2",   "%lf", 1, coords[0].crpix2);
+
+  if (OldAIPS) {
+    Lambda = coords[0].cdelt2 / coords[0].cdelt1;
+    rotate = DEG_RAD*atan2 (coords[0].pc2_1*Lambda, coords[0].pc1_1);
+    gfits_modify (header, "CROTA1", "%f", 1, rotate);
+    gfits_modify (header, "CROTA2", "%f", 1, rotate);
+    return (TRUE);
+  } 
+
+  gfits_modify (header, "PC001001", "%le", 1, coords[0].pc1_1);
+  gfits_modify (header, "PC001002", "%le", 1, coords[0].pc1_2);
+  gfits_modify (header, "PC002001", "%le", 1, coords[0].pc2_1);
+  gfits_modify (header, "PC002002", "%le", 1, coords[0].pc2_2);
+  gfits_modify (header, "NPLYTERM", "%d",  1, coords[0].Npolyterms);
+
+  /* RA Terms */
+  if (coords[0].Npolyterms > 1) {
+    gfits_modify (header, "PCA1X2Y0", "%le", 1, coords[0].polyterms[0][0]);   /* polyterms[0]); */
+    gfits_modify (header, "PCA1X1Y1", "%le", 1, coords[0].polyterms[1][0]);   /* polyterms[1]); */
+    gfits_modify (header, "PCA1X0Y2", "%le", 1, coords[0].polyterms[2][0]);   /* polyterms[2]); */
+  }
+  if (coords[0].Npolyterms > 2) {
+    gfits_modify (header, "PCA1X3Y0", "%le", 1, coords[0].polyterms[3][0]);   /* polyterms[3]); */
+    gfits_modify (header, "PCA1X2Y1", "%le", 1, coords[0].polyterms[4][0]);   /* polyterms[4]); */
+    gfits_modify (header, "PCA1X1Y2", "%le", 1, coords[0].polyterms[5][0]);   /* polyterms[5]); */
+    gfits_modify (header, "PCA1X0Y3", "%le", 1, coords[0].polyterms[6][0]);   /* polyterms[6]); */
+  }
+
+  /* Dec Terms */
+  if (coords[0].Npolyterms > 1) {
+    gfits_modify (header, "PCA2X2Y0", "%le", 1, coords[0].polyterms[0][1]);   /* polyterms[7]); */
+    gfits_modify (header, "PCA2X1Y1", "%le", 1, coords[0].polyterms[1][1]);   /* polyterms[8]); */
+    gfits_modify (header, "PCA2X0Y2", "%le", 1, coords[0].polyterms[2][1]);   /* polyterms[9]); */
+  }
+  if (coords[0].Npolyterms > 2) {
+    gfits_modify (header, "PCA2X3Y0", "%le", 1, coords[0].polyterms[3][1]);   /* polyterms[10]); */
+    gfits_modify (header, "PCA2X2Y1", "%le", 1, coords[0].polyterms[4][1]);   /* polyterms[11]); */
+    gfits_modify (header, "PCA2X1Y2", "%le", 1, coords[0].polyterms[5][1]);   /* polyterms[12]); */
+    gfits_modify (header, "PCA2X0Y3", "%le", 1, coords[0].polyterms[6][1]);   /* polyterms[13]); */
+  }
+  return (TRUE);
+}
+
+void coords_precess (double *ra, double *dec, double in_epoch, double out_epoch) {
+
+  double T;
+  double A, D, RA, DEC, zeta, z, theta;
+  double SA, CA, SD, CD;
+  
+  T = (out_epoch - in_epoch) / 100.0;
+  
+  zeta  = RAD_DEG*(0.6406161*T + 0.0000839*T*T + 0.0000050*T*T*T);
+  theta = RAD_DEG*(0.5567530*T - 0.0001185*T*T - 0.0000116*T*T*T);
+  z     =          0.6406161*T + 0.0003041*T*T + 0.0000051*T*T*T;
+  
+  A = *ra;
+  D = *dec;
+  SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+  CD = sqrt (1 - SD*SD);
+  SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+  CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+  
+  DEC = DEG_RAD*asin(SD);
+  RA  = DEG_RAD*atan2(SA, CA) + z;
+  
+  if (RA < 0)
+    RA += 360;
+  
+  *ra = RA;
+  *dec = DEC; 
+}
+
+/* -PLY projection is an extrapolation of the -TAN projection. 
+   In addition to the usual linear terms of CRPIXi, PC00i00j, there are 
+   higher order polynomial terms (up to 3rd order):
+   Axis 1 terms:
+   PCA1X2Y0 = coords.polyterm[0][0] = x^2                                             
+   PCA1X1Y1 = coords.polyterm[1][0] = xy                                          
+   PCA1X0Y2 = coords.polyterm[2][0] = y^2                                             
+   PCA1X3Y0 = coords.polyterm[3][0] = x^3                                             
+   PCA1X2Y1 = coords.polyterm[4][0] = x^2 y                                             
+   PCA1X1Y2 = coords.polyterm[5][0] = x y^2                                             
+   PCA1X0Y3 = coords.polyterm[6][0] = y^2                                              
+   Axis 2 terms:
+   PCA2X2Y0 = coords.polyterm[0][1] = x^2                                               
+   PCA2X1Y1 = coords.polyterm[1][1] = xy                                                
+   PCA2X0Y2 = coords.polyterm[2][1] = y^2                                               
+   PCA2X3Y0 = coords.polyterm[3][1] = x^3                                               
+   PCA2X2Y1 = coords.polyterm[4][1] = x^2 y                                             
+   PCA2X1Y2 = coords.polyterm[5][1] = x y^2                                             
+   PCA2X0Y3 = coords.polyterm[6][1] = y^2                                               
+*/
+
+# if (0)
+
+  /** convert pixel coordinates to cartesian system **/
+  X = coords[0].cdelt1*(x - coords[0].crpix1);
+  Y = coords[0].cdelt2*(y - coords[0].crpix2);
+  if (Polynomi) {
+    if (coords[0].Npolyterms > 2) {
+      X += coords[0].cdelt1*(x*x*coords[0].polyterms[0][0] + x*y*coords[0].polyterms[1][0] + y*y*coords[0].polyterms[2][0]);
+      Y += coords[0].cdelt2*(x*x*coords[0].polyterms[0][1] + x*y*coords[0].polyterms[1][1] + y*y*coords[0].polyterms[2][1]);
+    }
+    if (coords[0].Npolyterms > 2) {
+      X += coords[0].cdelt1*(x*x*x*coords[0].polyterms[3][0] + x*x*y*coords[0].polyterms[4][0] + x*y*y*coords[0].polyterms[5][0] + y*y*y*coords[0].polyterms[6][0]);
+      Y += coords[0].cdelt2*(x*x*x*coords[0].polyterms[3][1] + x*x*y*coords[0].polyterms[4][1] + x*y*y*coords[0].polyterms[5][1] + y*y*y*coords[0].polyterms[6][1]);
+    }
+  }
+
+  L = (X*coords[0].pc1_1 + Y*coords[0].pc1_2);
+  M = (X*coords[0].pc2_1 + Y*coords[0].pc2_2);
+/** this code is the old method used for higher order terms.  they
+    are essentially 6th order, with weird coupled terms.
+    I don't think any real data used these terms, but they should 
+    be re-calculated, I would think 
+**/
+
+# endif
+
+/*
+
+Projections and Transformations
+
+The Coords structure is used to represent the standard FITS header representations of the WCS terms.
+The FITS WCS encapsulates several concepts in one system: coordinate frame, projection, and
+transformations.  The ctype variable defines both the frame (ie, RA/DEC, GLON/GLAT, etc) and the
+projection (ie, AIT, SIN, TAN, etc).  The associated terms (PC00i00j) define transformations from
+the projection system to another linear coordinate system.  I have extended the basic WCS
+transformation terms to include higher-order polynomial terms.  The presence of higher-order terms
+is indicated in the header by the NPLYTERM keyword, with a value greater than 1 (a value of 0 or 1
+implies no higher-order coefficients).  The coefficients have keywords of the form PCAnXiYj where
+'n' is the axis corresponding to the dependent variable, 'i' is the order of the X component and 'j'
+is the order of the Y component.  A value of 2 indicates that the second-order coefficients are
+defined (PCAnX2Y0, PCAnX1Y1, PCAnX0Y2); A value of 3 indicates that the third-order coefficients are
+also defined (PCAnX3Y0, PCAnX2Y1, PCAnX1Y2, PCAnX0Y3).  Some headers in the past were generated
+without the NPLYTERM keyword.  a value of PLY, DIS, or WRP for the projection without a
+corresponding value for NPLYTERM implies that the value should be interpreted as '3'.
+
+*/
+
+  /* PLY is equiv to LIN with higher order terms
+     ZPL is equiv to ZEA with higher order terms
+     DIS is equiv to TAN with higher order terms
+     WRP is equiv to PLY, with implied mosaic */
+
+OhanaProjection GetProjection (char *ctype) {
+  if (!strcmp(&ctype[4], "-ZEA")) return PROJ_ZEA;
+  if (!strcmp(&ctype[4], "-ZPL")) return PROJ_ZPL;
+  if (!strcmp(&ctype[4], "-ARC")) return PROJ_ARC;
+  if (!strcmp(&ctype[4], "-STG")) return PROJ_STG;
+  if (!strcmp(&ctype[4], "-SIN")) return PROJ_SIN;
+  if (!strcmp(&ctype[0], "MM"))   return PROJ_SIN; // note ctype[0]
+  if (!strcmp(&ctype[4], "-TAN")) return PROJ_TAN;
+  if (!strcmp(&ctype[4], "-TNX")) return PROJ_TNX;
+  if (!strcmp(&ctype[4], "-DIS")) return PROJ_DIS;
+  if (!strcmp(&ctype[4], "-LIN")) return PROJ_LIN;
+  if (!strcmp(&ctype[0], "GENE")) return PROJ_LIN; // note ctype[0]
+  if (!strcmp(&ctype[4], "-PLY")) return PROJ_PLY;
+  if (!strcmp(&ctype[4], "-WRP")) return PROJ_WRP;
+  if (!strcmp(&ctype[4], "-AIT")) return PROJ_AIT;
+  if (!strcmp(&ctype[4], "-GLS")) return PROJ_GLS;
+  if (!strcmp(&ctype[4], "-PAR")) return PROJ_PAR;
+  return PROJ_NONE;
+}
+  
+int SetProjection (char *ctype, OhanaProjection proj) {
+  switch (proj) {
+    case PROJ_ZEA: strcpy(&ctype[4], "-ZEA"); return TRUE;
+    case PROJ_ZPL: strcpy(&ctype[4], "-ZPL"); return TRUE;
+    case PROJ_ARC: strcpy(&ctype[4], "-ARC"); return TRUE;
+    case PROJ_STG: strcpy(&ctype[4], "-STG"); return TRUE;
+    case PROJ_SIN: strcpy(&ctype[4], "-SIN"); return TRUE;
+    case PROJ_TAN: strcpy(&ctype[4], "-TAN"); return TRUE;
+    case PROJ_TNX: strcpy(&ctype[4], "-TNX"); return TRUE;
+    case PROJ_DIS: strcpy(&ctype[4], "-DIS"); return TRUE;
+    case PROJ_LIN: strcpy(&ctype[4], "-LIN"); return TRUE;
+    case PROJ_PLY: strcpy(&ctype[4], "-PLY"); return TRUE;
+    case PROJ_WRP: strcpy(&ctype[4], "-WRP"); return TRUE;
+    case PROJ_AIT: strcpy(&ctype[4], "-AIT"); return TRUE;
+    case PROJ_GLS: strcpy(&ctype[4], "-GLS"); return TRUE;
+    case PROJ_PAR: strcpy(&ctype[4], "-PAR"); return TRUE;
+    case PROJ_NONE: return FALSE;
+  }
+  return FALSE;
+}  
+
+OhanaProjectionMode GetProjectionMode (OhanaProjection proj) {
+  switch (proj) {
+    case PROJ_ZEA:
+    case PROJ_ZPL:
+    case PROJ_ARC:
+    case PROJ_STG:
+    case PROJ_SIN:
+    case PROJ_TAN:
+    case PROJ_TNX:
+    case PROJ_DIS:
+      return PROJ_MODE_ZENITHAL;
+    case PROJ_LIN: 
+    case PROJ_PLY: 
+    case PROJ_WRP: 
+      return PROJ_MODE_CARTESIAN;
+    case PROJ_AIT:
+    case PROJ_GLS:
+    case PROJ_PAR:
+      return PROJ_MODE_PSEUDOCYL;
+    default: 
+      return PROJ_MODE_NONE;
+  }
+  return PROJ_MODE_NONE;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog.c	(revision 22322)
@@ -0,0 +1,446 @@
+# include <dvo.h>
+# define DEBUG 1
+
+void dvo_catalog_test (Catalog *catalog, int halt) {
+
+  Catalog *subcat;
+
+  // fprintf (stderr, "catalog: Naverage = %d, average = %zx\n", catalog[0].Naverage, (size_t) catalog[0].average);
+  // fprintf (stderr, "catalog: Nmeasure = %d, measure = %zx\n", catalog[0].Nmeasure, (size_t) catalog[0].measure);
+  // fprintf (stderr, "catalog: Nmissing = %d, missing = %zx\n", catalog[0].Nmissing, (size_t) catalog[0].missing);
+  // fprintf (stderr, "catalog: Nsecfilt = %d, secfilt = %zx\n", catalog[0].Nsecfilt, (size_t) catalog[0].secfilt);
+
+  if (!catalog[0].measure || !catalog[0].secfilt) {
+    fprintf (stderr, "error: %s\n", catalog[0].filename);
+    if (halt) abort ();
+  }
+
+  // XXX test that things are correctly initialized
+  if (catalog[0].catmode != DVO_MODE_SPLIT) return;
+
+  subcat = catalog[0].measure_catalog;
+  if (subcat) {
+    if (subcat[0].measure_catalog || subcat[0].secfilt_catalog || subcat[0].missing_catalog) {
+      fprintf (stderr, "error in init\n");
+      abort ();
+    }
+  }
+  subcat = catalog[0].missing_catalog;
+  if (subcat) {
+    if (subcat[0].measure_catalog || subcat[0].secfilt_catalog || subcat[0].missing_catalog) {
+      fprintf (stderr, "error in init\n");
+      abort ();
+    }
+  }
+  subcat = catalog[0].secfilt_catalog;
+  if (subcat) {
+    if (subcat[0].measure_catalog || subcat[0].secfilt_catalog || subcat[0].missing_catalog) {
+      fprintf (stderr, "error in init\n");
+      abort ();
+    }
+  }
+}
+
+int dvo_catalog_catformat (char *catformat) {
+  
+  /* set the specified CATFORMAT */
+  if (!strcasecmp (catformat, "INTERNAL"))        return (DVO_FORMAT_INTERNAL);
+  if (!strcasecmp (catformat, "LONEOS"))    	  return (DVO_FORMAT_LONEOS);
+  if (!strcasecmp (catformat, "ELIXIR"))    	  return (DVO_FORMAT_ELIXIR);
+  if (!strcasecmp (catformat, "PANSTARRS_DEV_0")) return (DVO_FORMAT_PANSTARRS_DEV_0);
+  if (!strcasecmp (catformat, "PANSTARRS_DEV_1")) return (DVO_FORMAT_PANSTARRS_DEV_1);
+  if (!strcasecmp (catformat, "PS1_DEV_1"))       return (DVO_FORMAT_PS1_DEV_1);
+  if (!strcasecmp (catformat, "PS1_DEV_2"))       return (DVO_FORMAT_PS1_DEV_2);
+  return (DVO_FORMAT_UNDEF);
+}
+
+int dvo_catalog_catmode (char *catmode) {
+
+  /* set the specified CATMODE */
+  if (!strcasecmp (catmode, "RAW"))   return (DVO_MODE_RAW);
+  if (!strcasecmp (catmode, "MEF"))   return (DVO_MODE_MEF);
+  if (!strcasecmp (catmode, "SPLIT")) return (DVO_MODE_SPLIT);
+  return (DVO_MODE_UNDEF);
+}
+
+// init all data, or just catalog data
+void dvo_catalog_init (Catalog *catalog, int complete) {
+
+  // the following are used to guide open/create/load
+  if (complete) {
+    catalog[0].f = NULL;
+    catalog[0].filename = NULL;
+
+    catalog[0].lockmode = 0;
+    catalog[0].catmode  = 0;
+    catalog[0].catformat = 0;
+    catalog[0].catflags = 0;
+    catalog[0].sorted = 0;
+    catalog[0].Nsecfilt = 0;
+  }
+
+  gfits_init_header (&catalog[0].header);
+
+  // the following describe the catalog files on disk
+  catalog[0].average = NULL;
+  catalog[0].measure = NULL; 
+  catalog[0].missing = NULL; 
+  catalog[0].secfilt = NULL;
+  
+  catalog[0].Naverage = 0;
+  catalog[0].Nmeasure = 0;
+  catalog[0].Nmissing = 0;
+  catalog[0].Nsecf_mem = 0;
+
+  catalog[0].Naves_disk = 0;
+  catalog[0].Nmeas_disk = 0;
+  catalog[0].Nmiss_disk = 0;
+  catalog[0].Nsecf_disk = 0;
+
+  catalog[0].Naves_off  = 0;
+  catalog[0].Nmeas_off  = 0;
+  catalog[0].Nmiss_off  = 0;
+  catalog[0].Nsecf_off  = 0;
+
+  /* pointers to SPLIT data files */
+  catalog[0].measure_catalog = NULL;
+  catalog[0].missing_catalog = NULL;
+  catalog[0].secfilt_catalog = NULL;
+
+  /* pointers for data manipulation */
+  catalog[0].found = NULL;
+  catalog[0].image = NULL;
+  catalog[0].mosaic = NULL;
+  catalog[0].X = NULL;
+  catalog[0].Y = NULL;
+}
+
+/* possible exit status for lock_catalog: 
+   DVO_CAT_OPEN_FAIL - failure (including lock failure)
+   DVO_CAT_OPEN_OK - success
+   DVO_CAT_OPEN_EMPTY - empty file (file may be open or closed!) 
+*/
+int dvo_catalog_lock (Catalog *catalog, int lockmode) {
+
+  int dbstate;
+
+  /* record lockmode choice */
+  catalog[0].lockmode = lockmode;
+
+  /* set lock on database, create stream f */
+  // fprintf (stderr, "locking: %s\n", catalog[0].filename);
+  catalog[0].f = fsetlockfile (catalog[0].filename, 3600.0, catalog[0].lockmode, &dbstate);
+
+  if (dbstate == LCK_MISSING) return (DVO_CAT_OPEN_EMPTY);
+  if (dbstate == LCK_EMPTY)   return (DVO_CAT_OPEN_EMPTY);
+  if (catalog[0].f == NULL)   return (DVO_CAT_OPEN_FAIL);
+
+  fseek (catalog[0].f, 0, SEEK_SET);
+  return (DVO_CAT_OPEN_OK);
+}
+
+int dvo_catalog_unlock (Catalog *catalog) {
+
+  int fd, dbstate;
+  struct stat filestat;
+
+  if (catalog[0].f == (FILE *) NULL) return (2);
+  fflush (catalog[0].f);
+  // fprintf (stderr, "unlocking: %s\n", catalog[0].filename);
+
+  // attempt to unlink an empty file
+  fd = fileno (catalog[0].f);
+  if (!fstat (fd, &filestat)) {
+    if (filestat.st_size == 0) {
+      unlink (catalog[0].filename);
+    }
+  }
+
+  fclearlockfile (catalog[0].filename, catalog[0].f, catalog[0].lockmode, &dbstate);
+
+  if (catalog[0].catmode == DVO_MODE_SPLIT) {
+    if (catalog[0].measure_catalog != NULL) dvo_catalog_unlock (catalog[0].measure_catalog);
+    if (catalog[0].missing_catalog != NULL) dvo_catalog_unlock (catalog[0].missing_catalog);
+    if (catalog[0].secfilt_catalog != NULL) dvo_catalog_unlock (catalog[0].secfilt_catalog);
+  }
+  return (1);
+}
+
+// open an existing catalog file or or create a new one as needed (iomode == "w")
+// returns FALSE if a catalog could not be opened
+// returns TRUE if the catalog was empty
+// XXX allow stdout as destination (don't lock)
+enum {DVO_OPEN_NONE, DVO_OPEN_READ, DVO_OPEN_WRITE, DVO_OPEN_UPDATE};
+int dvo_catalog_open (Catalog *catalog, SkyRegion *region, int VERBOSE, char *iomode) {
+
+  int Nsecfilt, mode;
+
+  mode = DVO_OPEN_NONE;
+  if (!strcasecmp (iomode, "r")) mode = DVO_OPEN_READ;
+  if (!strcasecmp (iomode, "w")) mode = DVO_OPEN_WRITE;
+  if (!strcasecmp (iomode, "a")) mode = DVO_OPEN_UPDATE;
+  if (mode == DVO_OPEN_NONE) return (FALSE);
+
+  // save the expected Nsecfilt value for testing
+  Nsecfilt = catalog[0].Nsecfilt;
+
+  dvo_catalog_init (catalog, FALSE);
+
+  catalog[0].lockmode  = LCK_XCLD;
+  if (mode == DVO_OPEN_READ) catalog[0].lockmode  = LCK_SOFT;
+  
+  // XXX make a backup?  always?
+  if (!check_file_access (catalog[0].filename, TRUE, VERBOSE)) {
+    if (VERBOSE) fprintf (stderr, "no permission to access %s\n", catalog[0].filename);
+    return (FALSE);
+  }
+
+  if (VERBOSE) fprintf (stderr, "opening %s\n", catalog[0].filename);
+  
+  switch (dvo_catalog_lock (catalog, catalog[0].lockmode)) {
+  case DVO_CAT_OPEN_FAIL:
+    if (VERBOSE) fprintf (stderr, "can't lock file %s\n", catalog[0].filename);
+    return (FALSE);
+  case DVO_CAT_OPEN_OK:
+    if (!dvo_catalog_load (catalog, VERBOSE)) {
+      if (VERBOSE) fprintf (stderr, "failure loading catalog\n");
+      return (FALSE);
+    }
+    if (!dvo_catalog_check (catalog, Nsecfilt, TRUE)) {
+      if (VERBOSE) fprintf (stderr, "can't reduce number of secondary filters\n");
+      return (FALSE);
+    }
+    if (VERBOSE) fprintf (stderr, "loaded existing file %s\n", catalog[0].filename);
+    break;
+  case DVO_CAT_OPEN_EMPTY:
+    if ((mode == DVO_OPEN_READ) || (mode == DVO_OPEN_UPDATE)) return (TRUE);
+    catalog[0].Nsecfilt = Nsecfilt;
+    dvo_catalog_create (region, catalog); /* fills in new header info */
+    if (VERBOSE) fprintf (stderr, "creating new file %s\n", catalog[0].filename);
+    break;
+  }
+  return (TRUE);
+}
+
+// load an existing dvo_catalog currenly, the catmode information is carried per
+// catalog.  the user-set value of catmode will set the output mode for new
+// tables an old table will keep its mode.
+int dvo_catalog_load (Catalog *catalog, int VERBOSE) {
+  
+  int Naxis, split, status;
+  char measure[80];
+
+  dvo_catalog_init (catalog, FALSE);
+
+  // load the main catalog header, determine characteristics
+  if (!gfits_fread_header (catalog[0].f, &catalog[0].header)) {
+    if (VERBOSE) fprintf (stderr, "failure to read main catalog header\n");
+    return (FALSE);
+  }
+  // check if the table has been sorted or not 
+  status = gfits_scan (&catalog[0].header, "SORTED", "%t", 1, &catalog[0].sorted);
+  if (!status) catalog[0].sorted = TRUE;
+
+  // determine catmode
+  if (!gfits_scan (&catalog[0].header, "NAXIS", "%d", 1, &Naxis)) {
+    if (VERBOSE) fprintf (stderr, "can't determine catalog db mode\n");
+    return (FALSE);
+  }
+  catalog[0].catmode = DVO_MODE_MEF;
+  split = gfits_scan (&catalog[0].header, "MEASURE", "%s", 1, measure);
+  if (split) catalog[0].catmode = DVO_MODE_SPLIT;
+  if (Naxis == 2) catalog[0].catmode = DVO_MODE_RAW;
+  /* don't use Naxis == 2 and split mode! */
+
+  switch (catalog[0].catmode) {
+    case DVO_MODE_RAW:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_RAW)\n");
+      dvo_catalog_load_raw (catalog, VERBOSE);
+      break;
+    case DVO_MODE_MEF:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_MEF)\n");
+      dvo_catalog_load_mef (catalog, VERBOSE);
+      break;
+    case DVO_MODE_SPLIT:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_SPLIT)\n");
+      dvo_catalog_load_split (catalog, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "error getting catalog mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+// write out the data, unlink if empty?  'save' means: write out all data currently in
+// memory.  NOTE: this is currently not always possible: for non-SPLIT mode files, this
+// operation may require expanding the file size, which does not automatically happen
+int dvo_catalog_save (Catalog *catalog, char VERBOSE) {
+
+  // set the 'sorted' header keyword
+  gfits_modify (&catalog[0].header, "SORTED",  "%t", 1, catalog[0].sorted);
+
+  // XXX handle return status
+  switch (catalog[0].catmode) {
+    case DVO_MODE_RAW:
+      dvo_catalog_save_raw (catalog, VERBOSE);
+      break;
+    case DVO_MODE_MEF:
+      dvo_catalog_save_mef (catalog, VERBOSE);
+      break;
+    case DVO_MODE_SPLIT:
+      dvo_catalog_save_split (catalog, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "invalid catalog mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+// write out the data, assuming the in-memory data represents all data for the table.
+// NOTE: This operation may truncate the file and delete entries.
+int dvo_catalog_save_complete (Catalog *catalog, char VERBOSE) {
+
+  // set the 'sorted' header keyword
+  gfits_modify (&catalog[0].header, "SORTED",  "%t", 1, catalog[0].sorted);
+
+  // XXX handle return status
+  switch (catalog[0].catmode) {
+    case DVO_MODE_RAW:
+      dvo_catalog_save_raw (catalog, VERBOSE);
+      break;
+    case DVO_MODE_MEF:
+      dvo_catalog_save_mef (catalog, VERBOSE);
+      break;
+    case DVO_MODE_SPLIT:
+      dvo_catalog_save_split_complete (catalog, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "invalid catalog mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+// write out the in-memory data which extends beyond the data on disk.  NOTE: this is
+// currently only possible for the SPLIT mode.
+int dvo_catalog_update (Catalog *catalog, char VERBOSE) {
+
+  // set the 'sorted' header keyword
+  catalog[0].sorted = FALSE;
+  gfits_modify (&catalog[0].header, "SORTED",  "%t", 1, catalog[0].sorted);
+
+  /* update is only valid for catmode SPLIT */
+  switch (catalog[0].catmode) {
+    case DVO_MODE_RAW:
+      fprintf (stderr, "not allowed for RAW mode\n");
+      break;
+    case DVO_MODE_MEF:
+      fprintf (stderr, "not allowed for MEF mode\n");
+      break;
+    case DVO_MODE_SPLIT:
+      dvo_catalog_update_split (catalog, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "failure updating catalog\n");
+      fprintf (stderr, "invalid catalog mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+int dvo_catalog_check (Catalog *catalog, int Nsecfilt, int extend) {
+
+  int i, j, Nextra, in, out;
+  SecFilt *insec, *outsec;
+
+  if (Nsecfilt == 0) return (TRUE); // if Nsecfilt is not set, don't do this check
+  if (catalog[0].Nsecfilt == Nsecfilt) return (TRUE);
+  if (catalog[0].Nsecfilt > Nsecfilt) return (FALSE);
+  if ((catalog[0].Nsecfilt < Nsecfilt) && !extend) return (FALSE);
+
+  if ((catalog[0].Nsecfilt < Nsecfilt) && extend) {
+    Nextra = Nsecfilt - catalog[0].Nsecfilt;
+    insec = catalog[0].secfilt;
+    ALLOCATE (outsec, SecFilt, catalog[0].Naverage * Nsecfilt);
+    for (in = out = i = 0; i < catalog[0].Naverage; i++) {
+      for (j = 0; j < catalog[0].Nsecfilt; j++, in++, out++) {
+	outsec[out].M  = insec[in].M;
+	outsec[out].dM = insec[in].dM;
+	outsec[out].Xm = insec[in].Xm;
+      }
+      for (j = 0; j < Nextra; j++, out++) {
+	outsec[out].M  = NAN;
+	outsec[out].dM = NAN;
+	outsec[out].Xm = NAN_S_SHORT;
+      }
+    }
+    free (catalog[0].secfilt);
+    catalog[0].secfilt = outsec;
+    catalog[0].Nsecfilt = Nsecfilt;
+    catalog[0].Nsecf_mem = Nsecfilt * catalog[0].Naverage;
+  }
+  return (TRUE);
+}
+
+void dvo_catalog_free (Catalog *catalog) {
+  dvo_catalog_free_data (catalog);
+  gfits_free_header (&catalog[0].header);
+}
+
+void dvo_catalog_free_data (Catalog *catalog) {
+
+  /* free, initialize data structures */
+  if (catalog[0].average != NULL) {
+    free (catalog[0].average); 
+    catalog[0].Naverage = 0;
+    catalog[0].average = NULL;
+  }
+  if (catalog[0].measure != NULL) {
+    free (catalog[0].measure); 
+    catalog[0].Nmeasure = 0;
+    catalog[0].measure = NULL;
+  }
+  if (catalog[0].missing != NULL) {
+    free (catalog[0].missing); 
+    catalog[0].Nmissing = 0;
+    catalog[0].missing = NULL;
+  }
+  if (catalog[0].secfilt != NULL) {
+    free (catalog[0].secfilt); 
+    catalog[0].Nsecf_mem = 0;
+    catalog[0].secfilt = NULL;
+  }
+}
+
+/*
+  mode   : items to read (LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF)
+  format : what table structure on disk (INTERNAL, LONEOS, etc, )
+  style  : raw, mef, split, mysql
+*/
+
+int dvo_catalog_load_segment (Catalog *catalog, int VERBOSE, int start, int Nrows) {
+
+  switch (catalog[0].catmode) {
+    case DVO_MODE_RAW:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_RAW)\n");
+      fprintf (stderr, "cannot do this in raw mode\n");
+      // dvo_catalog_load_segment_raw (catalog, VERBOSE, start, Nrows);
+      break;
+    case DVO_MODE_MEF:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_MEF)\n");
+      fprintf (stderr, "cannot do this in mef mode\n");
+      // dvo_catalog_load_segment_mef (catalog, VERBOSE, start, Nrows);
+      break;
+    case DVO_MODE_SPLIT:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_SPLIT)\n");
+      dvo_catalog_load_segment_split (catalog, VERBOSE, start, Nrows);
+      break;
+    default:
+      fprintf (stderr, "error getting catalog mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_chipcoords.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_chipcoords.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_chipcoords.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include <dvo.h>
+
+int dvo_match_image (Image *image, int Nimage, unsigned int T, short int S) {
+
+  int N, Nlo, Nhi, N1, N2;
+
+  /* bracket first value of interest */
+  Nlo = 0; Nhi = Nimage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[N].tzero < T) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  N1 = Nlo;
+
+  /* bracket last value of interest */
+  Nlo = 0; Nhi = Nimage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[N].tzero > T) {
+      Nhi = N;
+    } else {
+      Nlo = N - 1;
+    }
+  }
+  N2 = Nhi;
+
+  for (N = N1; N < N2; N++) {
+    if ((image[N].tzero == T) && (image[N].photcode == S)) {
+      return (N);
+    }
+  }
+  return (-1);
+}
+
+// if necessary, determine chip coordinates for all measures
+int dvo_catalog_chipcoords (Catalog *catalog, Image *image, int Nimage) {
+
+  int i, j, m, N;
+  double ra, dec, x, y;
+  Average *average;
+  Measure *measure;
+
+  if (catalog[0].catformat == DVO_FORMAT_LONEOS) goto do_convert;  // special conversion for LONEOS
+  if (catalog[0].catformat == DVO_FORMAT_ELIXIR) goto do_convert;  // special conversion for ELIXIR
+  return TRUE;
+
+do_convert:
+  average = catalog[0].average;
+  measure = catalog[0].measure;
+  
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    m = average[i].measureOffset;
+    for (j = 0; j < average[i].Nmeasure; j++, m++) {
+      ra  = average[i].R - measure[m].dR / 3600.0;
+      dec = average[i].D - measure[m].dD / 3600.0;
+      N = dvo_match_image (image, Nimage, measure[m].t, measure[m].photcode);
+      if (N == -1) continue;
+      RD_to_XY (&x, &y, ra, dec, &image[N].coords);
+      measure[m].Xccd = x;
+      measure[m].Yccd = y;
+    }
+  }
+  return TRUE;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_create.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_create.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_create.c	(revision 22322)
@@ -0,0 +1,139 @@
+# include <dvo.h>
+# define DEBUG 0
+
+// create a new dvo catalog file (if split, lock extra files as well?)
+// catalog internals which must be set:
+// catalog[0].filename
+// catalog[0].catmode
+// catalog[0].catformat (used by dvo_catalog_save)
+// catalog[0].lockmode
+void dvo_catalog_create (SkyRegion *region, Catalog *catalog) {
+
+  int length;
+  char *path, *root, *file, *line;
+  time_t now;
+
+  if (DEBUG) fprintf (stderr, "new catalog file: %s\n", catalog[0].filename);
+
+  // init the data values, not the internals listed above
+  dvo_catalog_init (catalog, FALSE);
+
+  /* for RAW mode, make header a fake image */
+  if (catalog[0].catmode == DVO_MODE_RAW) {
+    catalog[0].header.bitpix   = 16;
+    catalog[0].header.Naxes    = 2;
+    catalog[0].header.Naxis[0] = 1;
+    catalog[0].header.Naxis[1] = 1;
+  }
+  gfits_create_header (&catalog[0].header);
+  
+  if (catalog[0].catmode == DVO_MODE_SPLIT) {
+    path = pathname (catalog[0].filename);
+    root = filerootname (catalog[0].filename);
+    length = strlen(path) + strlen(root) + 6;
+
+    /* define measure catalog file */
+    ALLOCATE (catalog[0].measure_catalog, Catalog, 1);
+    dvo_catalog_init (catalog[0].measure_catalog, TRUE);
+
+    /* create basic data for measure catalog file */
+    gfits_create_header (&catalog[0].measure_catalog[0].header);
+    ALLOCATE (catalog[0].measure_catalog[0].filename, char, length);
+    sprintf (catalog[0].measure_catalog[0].filename, "%s/%s.cpm", path, root);
+    file = filebasename (catalog[0].measure_catalog[0].filename);
+    gfits_modify (&catalog[0].header, "MEASURE", "%s", 1, file);
+    free (file);
+
+    /* define missing catalog file */
+    ALLOCATE (catalog[0].missing_catalog, Catalog, 1);
+    dvo_catalog_init (catalog[0].missing_catalog, TRUE);
+
+    /* create basic data for missing catalog file */
+    gfits_create_header (&catalog[0].missing_catalog[0].header);
+    ALLOCATE (catalog[0].missing_catalog[0].filename, char, length);
+    sprintf (catalog[0].missing_catalog[0].filename, "%s/%s.cpn", path, root);
+    file = filebasename (catalog[0].missing_catalog[0].filename);
+    gfits_modify (&catalog[0].header, "MISSING", "%s", 1, file);
+    free (file);
+
+    /* define secfilt catalog file */
+    ALLOCATE (catalog[0].secfilt_catalog, Catalog, 1);
+    dvo_catalog_init (catalog[0].secfilt_catalog, TRUE);
+
+    /* create basic data for secfilt catalog file */
+    gfits_create_header (&catalog[0].secfilt_catalog[0].header);
+    ALLOCATE (catalog[0].secfilt_catalog[0].filename, char, length);
+    sprintf (catalog[0].secfilt_catalog[0].filename, "%s/%s.cps", path, root);
+    file = filebasename (catalog[0].secfilt_catalog[0].filename);
+    gfits_modify (&catalog[0].header, "SECFILT", "%s", 1, file);
+    free (file);
+    free (path);
+    free (root);
+
+    // lock the additional split files
+    // XXX clear residual locks if we fail
+    if (dvo_catalog_lock (catalog[0].measure_catalog, catalog[0].lockmode) != DVO_CAT_OPEN_EMPTY) {
+      fprintf (stderr, "error with file lock\n");
+      exit (2);
+    }
+    if (dvo_catalog_lock (catalog[0].missing_catalog, catalog[0].lockmode) != DVO_CAT_OPEN_EMPTY) {
+      fprintf (stderr, "error with file lock\n");
+      exit (2);
+    }
+    if (dvo_catalog_lock (catalog[0].secfilt_catalog, catalog[0].lockmode) != DVO_CAT_OPEN_EMPTY) {
+      fprintf (stderr, "error with file lock\n");
+      exit (2);
+    }
+
+  }    
+
+  /* write RA,DEC range in header */
+  if (region) {
+    gfits_modify (&catalog[0].header, "RA0",  "%lf", 1, region[0].Rmin);
+    gfits_modify (&catalog[0].header, "DEC0", "%lf", 1, region[0].Dmin);
+    gfits_modify (&catalog[0].header, "RA1",  "%lf", 1, region[0].Rmax);
+    gfits_modify (&catalog[0].header, "DEC1", "%lf", 1, region[0].Dmax);
+  }
+
+  /* write creation date in header */
+  ohana_str_to_time ("now", &now);
+  line = ohana_sec_to_date (now);
+  gfits_modify (&catalog[0].header, "DATE", "%s", 1, line);
+  free (line);
+
+  /* dummy allocation so realloc will succeed */
+  ALLOCATE (catalog[0].average, Average, 1);
+  ALLOCATE (catalog[0].measure, Measure, 1);
+  ALLOCATE (catalog[0].missing, Missing, 1);
+  ALLOCATE (catalog[0].secfilt, SecFilt, 1);
+
+  /* setup secondary filters to match photcodes:
+   * Nsecfilt is number of filters.  Number of entries in array is
+   * Nsecfilt * Naverage.  At this point, N entries == 0
+   */
+}
+  
+int dvo_catalog_set_range (Catalog *catalog) {
+
+  int i;
+  double Rmin, Rmax, Dmin, Dmax;
+
+  /* determine RA,DEC range */
+  Rmin = 360.0;
+  Rmax =   0.0;
+  Dmin = +90.0;
+  Dmax = -90.0;
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    Rmin = MIN (Rmin, catalog[0].average[i].R);
+    Rmax = MAX (Rmax, catalog[0].average[i].R);
+    Dmin = MIN (Dmin, catalog[0].average[i].D);
+    Dmax = MAX (Dmax, catalog[0].average[i].D);
+  }
+
+  /* write RA,DEC range in header */
+  gfits_modify (&catalog[0].header, "RA0",  "%lf", 1, Rmin);
+  gfits_modify (&catalog[0].header, "DEC0", "%lf", 1, Dmin);
+  gfits_modify (&catalog[0].header, "RA1",  "%lf", 1, Rmax);
+  gfits_modify (&catalog[0].header, "DEC1", "%lf", 1, Dmax);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_mef.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_mef.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_mef.c	(revision 22322)
@@ -0,0 +1,333 @@
+# include <dvo.h>
+
+int dvo_catalog_load_mef (Catalog *catalog, int VERBOSE) {
+  
+  int Nitems, Nbytes, Nexpect, Naverage, Nmeasure, Nmissing, Nsecfilt;
+  FILE *f;
+
+  Header header;
+  Matrix matrix;
+  FTable ftable;
+  SecFilt *primary;
+
+  f = catalog[0].f;
+  ftable.header = &header;
+
+  /* move pointer past header -- must be already read (load_catalog) */
+  fseek (f, catalog[0].header.size, SEEK_SET);
+
+  /* matrix should be empty */
+  if (!gfits_fread_matrix (f, &matrix, &catalog[0].header)) {
+    if (VERBOSE) fprintf (stderr, "can't read primary matrix");
+    return (FALSE);
+  }
+  /* get the components from the header */
+  if (!gfits_scan (&catalog[0].header, "NSTARS",   "%d", 1, &Naverage)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMEAS",    "%d", 1, &Nmeasure)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMISS",    "%d", 1, &Nmissing)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NSECFILT", "%d", 1, &Nsecfilt)) Nsecfilt = 0;
+
+  /* save the current number so we can do partial updates */
+  catalog[0].Naves_disk = Naverage;
+  catalog[0].Nmeas_disk = Nmeasure;
+  catalog[0].Nmiss_disk = Nmissing;
+  catalog[0].Nsecf_disk = Naverage * Nsecfilt;
+
+  /**  Nsecfilt is unusual: it does not list the number of data items in the table
+       instead, the number of items is Nsecfilt * Naverage.  **/
+  catalog[0].Nsecfilt = Nsecfilt;
+
+  /* default values, but we will assign these a valid value before we exit (even if empty) */
+  catalog[0].average = NULL;
+  catalog[0].measure = NULL;
+  catalog[0].missing = NULL;
+  catalog[0].secfilt = NULL;
+
+  /* validate table mode */
+
+  /* read Average table header */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read table average header");
+    return (FALSE);
+  }
+  /* read Average table data (or skip) */
+  if (catalog[0].catflags & LOAD_AVES) {
+    if (!gfits_fread_ftable_data (f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table average data");
+      return (FALSE);
+    }
+    /* old versions of DVO stored one of the average magnitudes in Average. we save this if needed */
+    catalog[0].average = FtableToAverage (&ftable, &Naverage, &catalog[0].catformat, &primary);
+    if (Naverage != catalog[0].Naves_disk) {
+      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%d vs %d)\n", Naverage, catalog[0].Naves_disk);
+    }
+    catalog[0].Naverage = catalog[0].Naves_disk;
+    catalog[0].Naves_off = 0;
+  } else {
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    ALLOCATE (catalog[0].average, Average, 1);
+    catalog[0].Naverage = 0;
+    catalog[0].Naves_off = catalog[0].Naves_disk;
+  }
+  gfits_free_header (&header);
+  /** free the ftable or not? data is being used still..? **/
+
+  /* read Measure table header */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read table measure header");
+    return (FALSE);
+  }
+  /* read Measure table data */
+  if (catalog[0].catflags & LOAD_MEAS) {
+    if (!gfits_fread_ftable_data (f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure data");
+      return (FALSE);
+    }
+    catalog[0].measure = FtableToMeasure (&ftable, &catalog[0].Nmeasure, &catalog[0].catformat);
+    if (Nmeasure != catalog[0].Nmeas_disk) {
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%d vs %d)\n", Nmeasure, catalog[0].Nmeas_disk);
+    }
+    catalog[0].Nmeasure = catalog[0].Nmeas_disk;
+    catalog[0].Nmeas_off = 0;
+  } else {
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    ALLOCATE (catalog[0].measure, Measure, 1);
+    catalog[0].Nmeasure = 0;
+    catalog[0].Nmeas_off = catalog[0].Nmeas_disk;
+  }
+
+  /* read Missing table header */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read table missing header");
+    return (FALSE);
+  }
+  /* read Missing table data */
+  if (catalog[0].catflags & LOAD_MISS) {
+    if (!gfits_fread_ftable_data (f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table missing data");
+      return (FALSE);
+    }
+    /* no conversions currently defined */
+    catalog[0].missing = gfits_table_get_Missing (&ftable, &catalog[0].Nmissing, NULL);
+    if (Nmissing != catalog[0].Nmiss_disk) {
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%d vs %d)\n", Nmissing, catalog[0].Nmiss_disk);
+    }
+    catalog[0].Nmissing = catalog[0].Nmiss_disk;
+    catalog[0].Nmiss_off = 0;
+  } else {
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    ALLOCATE (catalog[0].missing, Missing, 1);
+    catalog[0].Nmissing = 0;
+    catalog[0].Nmiss_off = catalog[0].Nmiss_disk;
+  }
+
+  /* read secfilt table header */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read table secfilt header");
+    return (FALSE);
+  }
+  /* read secfilt table data */
+  if (catalog[0].catflags & LOAD_SECF) {
+    if (!gfits_fread_ftable_data (f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table secfilt data");
+      return (FALSE);
+    }
+
+    /* how many entries do we expect from the secfilt table? */
+    Nexpect = catalog[0].Nsecfilt * catalog[0].Naverage;
+    catalog[0].secfilt = FtableToSecFilt (&ftable, &Nitems, &catalog[0].catformat);
+    if (Nexpect != Nitems) {
+      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers (%d vs %d)\n", Nexpect, Nitems);
+    }
+
+    /* if primary is defined, we were supplied with one additional average magnitude from Average
+       we need to interleave these magnitudes with the secfilt entries just loaded */
+    if (primary != NULL) {
+      int Ntmpfilt, Ntotal, i, j;
+      SecFilt *tmpfilt;
+      tmpfilt  = catalog[0].secfilt;
+      Ntmpfilt = catalog[0].Nsecfilt;
+      Nsecfilt = catalog[0].Nsecfilt + 1;
+      Ntotal = Nsecfilt * catalog[0].Naves_disk;
+      ALLOCATE (catalog[0].secfilt, SecFilt, Ntotal);
+      for (i = 0; i < catalog[0].Naves_disk; i++) {
+	catalog[0].secfilt[i*Nsecfilt + 0] = primary[i];
+	for (j = 0; j < Ntmpfilt; j++) {
+	  catalog[0].secfilt[i*Nsecfilt + j + 1] = tmpfilt[i*Ntmpfilt + j];
+	}
+      }		
+      catalog[0].Nsecfilt = Nsecfilt;
+      catalog[0].Nsecf_disk = Ntotal;
+      free (primary);
+    } 
+    catalog[0].Nsecf_mem = catalog[0].Nsecf_disk;
+    catalog[0].Nsecf_off = 0;
+  } else {
+    /* no real need to skip the data array here... */
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    if (primary != NULL) {
+      free (primary);
+      catalog[0].Nsecfilt ++;
+      catalog[0].Nsecf_disk =  catalog[0].Nsecfilt * catalog[0].Naves_disk;
+    }
+    ALLOCATE (catalog[0].secfilt, SecFilt, 1);
+    catalog[0].Nsecf_mem = 0;
+    catalog[0].Nsecf_off = catalog[0].Nsecf_disk;
+  }
+
+  return (TRUE);
+}
+
+/* XXX need to decompose the primary and secfilt entries for Elixir and Loneos */
+/* save_catalog_mef writes a complete new file from scratch */
+int dvo_catalog_save_mef (Catalog *catalog, char VERBOSE) {
+
+  int Nitems;
+  FILE *f;
+  Matrix matrix;
+  Header header;
+  FTable ftable;
+  SecFilt *primary;
+  SecFilt *secfilt;
+  int i, j, Nsecfilt, Nallfilt, Ntotal;
+
+  if (catalog[0].Naverage == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (TRUE);
+  }
+
+  /* for the appropriate types, pull out the first secfilt and pass to AverageToFtable as primary */
+  if ((catalog[0].catformat == DVO_FORMAT_ELIXIR) || // special case for ELIXIR 
+      (catalog[0].catformat == DVO_FORMAT_LONEOS)) { // special case for LONEOS
+    Nallfilt = catalog[0].Nsecfilt;
+    Nsecfilt = catalog[0].Nsecfilt - 1;
+    Ntotal = Nsecfilt * catalog[0].Naverage;
+    ALLOCATE (primary, SecFilt, catalog[0].Naverage);
+    ALLOCATE (secfilt, SecFilt, Ntotal);
+
+    for (i = 0; i < catalog[0].Naverage; i++) {
+      primary[i] = catalog[0].secfilt[i*Nallfilt + 0];
+      for (j = 0; j < Nsecfilt; j++) {
+	secfilt[i*Nsecfilt + j] = catalog[0].secfilt[i*Nallfilt + j + 1];
+      }
+    }		
+  } else {
+    primary = NULL;
+    secfilt = catalog[0].secfilt;
+    Nsecfilt = catalog[0].Nsecfilt;
+  }
+
+  /* make sure header is consistent with data */
+  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, catalog[0].Naverage);
+  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, catalog[0].Nmeasure);
+  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, catalog[0].Nmissing);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
+  gfits_modify (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
+
+  f = catalog[0].f;
+  /* rewind file pointers and truncate */
+  fseek (f, 0, SEEK_SET);
+  ftruncate (fileno (catalog[0].f), 0);
+  ftable.header = &header;
+
+  /* write table PHU header */
+  if (!gfits_fwrite_header  (catalog[0].f, &catalog[0].header)) {
+    fprintf (stderr, "can't write primary header");
+    goto failure;
+  }
+
+  /* this is probably a NOP, do I have to keep it in? */
+  gfits_create_matrix (&catalog[0].header, &matrix);
+  if (!gfits_fwrite_matrix  (catalog[0].f, &matrix)) {
+    fprintf (stderr, "can't write primary matrix");
+    goto failure;
+  }
+  gfits_free_matrix (&matrix);
+
+  /* write out Average table (convert to FITS table format) */
+  AverageToFtable (&ftable, catalog[0].average, catalog[0].Naverage, catalog[0].catformat, primary);
+  if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
+    fprintf (stderr, "can't write table header");
+    goto failure;
+  }
+  if (!gfits_fwrite_table (catalog[0].f, &ftable)) {
+    fprintf (stderr, "can't write table data");
+    goto failure;
+  }
+  gfits_free_table (&ftable);
+  gfits_free_header (&header);
+
+  /* write out Measure table (convert to FITS table format) */
+  MeasureToFtable (&ftable, catalog[0].measure, catalog[0].Nmeasure, catalog[0].catformat);
+  if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
+    fprintf (stderr, "can't write table header");
+    goto failure;
+  }
+  if (!gfits_fwrite_table (catalog[0].f, &ftable)) {
+    fprintf (stderr, "can't write table data");
+    goto failure;
+  }
+  gfits_free_table (&ftable);
+  gfits_free_header (&header);
+
+  /* write out Missing table (convert to FITS table format) */
+  gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing);
+  if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
+    fprintf (stderr, "can't write table header");
+    goto failure;
+  }
+  if (!gfits_fwrite_table (catalog[0].f, &ftable)) {
+    fprintf (stderr, "can't write table data");
+    goto failure;
+  }
+  gfits_free_table (&ftable);
+  gfits_free_header (&header);
+
+  /* write out SecFilt table (convert to FITS table format) */
+  Nitems = catalog[0].Naverage * Nsecfilt;
+  SecFiltToFtable (&ftable, secfilt, Nitems, catalog[0].catformat);
+  if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
+    fprintf (stderr, "can't write table header");
+    goto failure;
+  }
+  if (!gfits_fwrite_table (catalog[0].f, &ftable)) {
+    fprintf (stderr, "can't write table data");
+    goto failure;
+  }
+  gfits_free_table (&ftable);
+  gfits_free_header (&header);
+
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (TRUE);
+
+failure:
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (FALSE);
+}
+
+/*
+  catalog data is:
+  header (bitpix == 8)
+  matrix (empty)
+  average header
+  average table
+  measure header
+  measure table
+  missing header
+  missing table
+  secfilt header
+  secfilt table
+*/
+   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_raw.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_raw.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_raw.c	(revision 22322)
@@ -0,0 +1,602 @@
+# include <dvo.h>
+
+/* read data from raw-style catalog file; set data format values based on file */
+
+int dvo_catalog_load_raw (Catalog *catalog, int VERBOSE) {
+  
+  int Nitems, nitems;
+  int i, Nmeas, Nmiss, size;
+  int NewMeasure, Nskip;
+  int AverageSize, MeasureSize, MissingSize, SecFiltSize;
+  FILE *f;
+  struct stat filestatus;
+  char format[80], telescope[80];
+  SecFilt *primary;
+
+  f = catalog[0].f;
+
+  /* move pointer past header -- must be already read (load_catalog) */
+  fseek (f, catalog[0].header.size, SEEK_SET);
+
+  /* get the components from the header */
+  catalog[0].Naverage = catalog[0].Nmeasure = catalog[0].Nmissing = catalog[0].Nsecfilt = 0;
+  if (!gfits_scan (&catalog[0].header, "NSTARS",   "%d", 1, &catalog[0].Naverage)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMEAS",    "%d", 1, &catalog[0].Nmeasure)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMISS",    "%d", 1, &catalog[0].Nmissing)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NSECFILT", "%d", 1, &catalog[0].Nsecfilt)) catalog[0].Nsecfilt = 0;
+
+  /* determine catalog format */
+  catalog[0].catformat = DVO_FORMAT_UNDEF;
+  if (gfits_scan (&catalog[0].header, "FORMAT",  "%s", 1, format)) {
+    catalog[0].catformat = dvo_catalog_catformat (format);
+    if (catalog[0].catformat != DVO_FORMAT_UNDEF) goto got_format;
+  }
+  /* special cases: old versions of the DB tables which were poorly identified */
+  if (gfits_scan (&catalog[0].header, "NEWMEAS",  "%t", 1, &NewMeasure)) {
+    catalog[0].catformat = DVO_FORMAT_ELIXIR; // special case for ELIXIR
+    goto got_format;
+  }
+  if (gfits_scan (&catalog[0].header, "TELESCOP",  "%s", 1, telescope)) {
+    if (!strncmp (telescope, "LONEOS", strlen("LONEOS"))) {
+      catalog[0].catformat = DVO_FORMAT_LONEOS; // special case for LONEOS
+      goto got_format;
+    }
+    if (!strncmp (telescope, "1.3m McGraw-Hill", strlen("1.3m McGraw-Hill"))) {
+      catalog[0].catformat = DVO_FORMAT_ELIXIR; // special case for ELIXIR
+      goto got_format;
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "cannot determine catalog format\n");
+  return (FALSE);
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+  case DVO_FORMAT_##NAME: { \
+    AverageSize = sizeof(Average_##TYPE); \
+    MeasureSize = sizeof(Measure_##TYPE); \
+    SecFiltSize = sizeof(SecFilt_##TYPE); \
+    break; }
+
+got_format:
+  /* determine datatype sizes */
+  switch (catalog[0].catformat) {
+    case DVO_FORMAT_INTERNAL: {
+      AverageSize = sizeof(Average);
+      MeasureSize = sizeof(Measure);
+      SecFiltSize = sizeof(SecFilt);
+      break;
+    }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "programming error in phot_catalog_raw\n");
+      exit (2);
+  }
+# undef FORMAT_CASE
+
+  MissingSize = sizeof (Missing);
+
+  /* predicted file size - for double checking data validity */
+  size = catalog[0].header.size;
+  size += AverageSize * catalog[0].Naverage;
+  size += MeasureSize * catalog[0].Nmeasure;
+  size += MissingSize * catalog[0].Nmissing;
+  size += SecFiltSize * catalog[0].Nsecfilt * catalog[0].Naverage;
+
+  /* check that file size makes sense */
+  if (stat (catalog[0].filename, &filestatus) == -1) {
+    if (VERBOSE) fprintf (stderr, "failed to get status of catalog\n");
+    return (FALSE);
+  }
+  if (size > filestatus.st_size) {
+    if (VERBOSE) {
+      fprintf (stderr, "star catalog has inconsistent size\n");
+      fprintf (stderr, "average: %d = %d bytes\n", catalog[0].Naverage, catalog[0].Naverage*AverageSize);
+      fprintf (stderr, "measure: %d = %d bytes\n", catalog[0].Nmeasure, catalog[0].Nmeasure*MeasureSize);
+      fprintf (stderr, "missing: %d = %d bytes\n", catalog[0].Nmissing, catalog[0].Nmissing*MissingSize);
+      fprintf (stderr, "secfilt: %d = %d bytes\n", catalog[0].Nsecfilt, catalog[0].Nsecfilt*SecFiltSize*catalog[0].Naverage);
+      fprintf (stderr, "expect: %d, found: %d\n", size, (int)filestatus.st_size);
+    }
+    return (FALSE);
+  } 
+  if (size < filestatus.st_size) {
+    if (VERBOSE) fprintf (stderr, "warning: file larger than expected\n");
+  } 
+
+  if (catalog[0].Naverage == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars yet in catalog %s\n", catalog[0].filename);
+    return (TRUE);
+  }
+
+  /* read and convert the averages (use a macro to clean this up?) */
+  /* old versions of DVO stored one of the average magnitudes in Average. we save this if needed */
+  if (catalog[0].catflags & LOAD_AVES) {
+    catalog[0].average = ReadRawAverage (catalog[0].f, catalog[0].Naverage, catalog[0].catformat, &primary);
+  } else {
+    /* skip over averages */
+    Nskip = catalog[0].Naverage * AverageSize;
+    fseek (f, Nskip, SEEK_CUR); 
+  }    
+  
+  /* read and convert the measures (use a macro to clean this up?) */
+  if (catalog[0].catflags & LOAD_MEAS) {
+    catalog[0].measure = ReadRawMeasure (catalog[0].f, catalog[0].Nmeasure, catalog[0].catformat);
+  } else {
+    /* skip over measures */
+    Nskip = catalog[0].Nmeasure * MeasureSize;
+    fseek (f, Nskip, SEEK_CUR); 
+  }    
+
+  /* read and convert missing */
+  if (catalog[0].catflags & LOAD_MISS) {
+    ALLOCATE (catalog[0].missing, Missing, MAX (catalog[0].Nmissing, 1));
+    Nitems = catalog[0].Nmissing;
+    nitems = fread (catalog[0].missing, MissingSize, Nitems, f);
+    if (nitems != Nitems) {
+      if (VERBOSE) fprintf (stderr, "failed to read missing from catalog file %s (%d vs %d)\n", catalog[0].filename, nitems, Nitems);
+      return (FALSE);
+    }
+    gfits_convert_Missing (catalog[0].missing, MissingSize, Nitems);
+  } else {
+    /* skip over missings */
+    Nskip = catalog[0].Nmissing * MissingSize;
+    fseek (f, Nskip, SEEK_CUR); 
+  }
+  
+  /* read and convert secfilt */
+  if (catalog[0].catflags & LOAD_SECF) {
+    Nitems = catalog[0].Naverage * catalog[0].Nsecfilt;
+    catalog[0].secfilt = ReadRawSecFilt (catalog[0].f, Nitems, catalog[0].catformat);
+
+    /* if primary is defined, we were supplied with one additional average magnitude from Average
+       we need to interleave these magnitudes with the secfilt entries just loaded */
+    if (primary != NULL) {
+      int Ntmpfilt, Nsecfilt, Ntotal, i, j;
+      SecFilt *tmpfilt;
+      tmpfilt  = catalog[0].secfilt;
+      Ntmpfilt = catalog[0].Nsecfilt;
+      Nsecfilt = catalog[0].Nsecfilt + 1;
+      Ntotal = Nsecfilt * catalog[0].Naverage;
+      ALLOCATE (catalog[0].secfilt, SecFilt, Ntotal);
+      for (i = 0; i < catalog[0].Naverage; i++) {
+	catalog[0].secfilt[i*Nsecfilt + 0] = primary[i];
+	for (j = 0; j < Ntmpfilt; j++) {
+	  catalog[0].secfilt[i*Nsecfilt + j + 1] = tmpfilt[i*Ntmpfilt + j];
+	}
+      }		
+      catalog[0].Nsecfilt = Nsecfilt;
+      catalog[0].Nsecf_mem = Ntotal;
+      free (primary);
+    } 
+
+  } else {
+    /* skip over secfilts */
+    Nskip = catalog[0].Nsecfilt * catalog[0].Naverage * SecFiltSize;
+    fseek (f, Nskip, SEEK_CUR); 
+    if (primary != NULL) free (primary);
+  }
+
+  if (VERBOSE) fprintf (stderr, "read %d stars from catalog file %s (%d measurements, %d missing, %d secondary filters)\n", 
+			catalog[0].Naverage, catalog[0].filename, catalog[0].Nmeasure, catalog[0].Nmissing, catalog[0].Nsecfilt);
+
+  /* check data integrity */
+  if (catalog[0].catflags & LOAD_AVES) {
+    for (i = Nmeas = Nmiss = 0; i < catalog[0].Naverage; i++) {
+      Nmeas += catalog[0].average[i].Nmeasure; 
+      Nmiss += catalog[0].average[i].Nmissing; 
+    }
+    if ((Nmeas != catalog[0].Nmeasure) || (Nmiss != catalog[0].Nmissing)) {
+      if (VERBOSE) {
+	fprintf (stderr, "****** data in catalog %s is corrupt, sums don't check\n", catalog[0].filename);
+	fprintf (stderr, "****** Nmeas: %d, %d\n", Nmeas, catalog[0].Nmeasure);
+	fprintf (stderr, "****** Nmiss: %d, %d\n", Nmiss, catalog[0].Nmissing);
+      }
+      return (FALSE);
+    }
+  }
+
+  /* save the current number so we can do partial updates */
+  catalog[0].Naves_disk = catalog[0].Naverage;
+  catalog[0].Nmeas_disk = catalog[0].Nmeasure;
+  catalog[0].Nmiss_disk = catalog[0].Nmissing;
+
+  return (TRUE);
+}
+
+int dvo_catalog_save_raw (Catalog *catalog, char VERBOSE) {
+
+  int Nitems, nitems;
+  FILE *f;
+  SecFilt *primary;
+  SecFilt *secfilt;
+  int i, j, Nsecfilt, Nallfilt, Ntotal;
+
+  if (catalog[0].Naverage == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (TRUE);
+  }
+
+  /* for the appropriate types, pull out the first secfilt and pass to WriteRawAverage as primary */
+  if ((catalog[0].catformat == DVO_FORMAT_ELIXIR) || // special case for ELIXIR
+      (catalog[0].catformat == DVO_FORMAT_LONEOS)) { // special case for LONEOS
+    Nallfilt = catalog[0].Nsecfilt;
+    Nsecfilt = catalog[0].Nsecfilt - 1;
+    Ntotal = Nsecfilt * catalog[0].Naverage;
+    ALLOCATE (primary, SecFilt, catalog[0].Naverage);
+    ALLOCATE (secfilt, SecFilt, Ntotal);
+
+    for (i = 0; i < catalog[0].Naverage; i++) {
+      primary[i] = catalog[0].secfilt[i*Nallfilt + 0];
+      for (j = 0; j < Nsecfilt; j++) {
+	secfilt[i*Nsecfilt + j] = catalog[0].secfilt[i*Nallfilt + j + 1];
+      }
+    }		
+  } else {
+    primary = NULL;
+    secfilt = catalog[0].secfilt;
+    Nsecfilt = catalog[0].Nsecfilt;
+  }
+
+  /* make sure header is consistent with data */
+  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, catalog[0].Naverage);
+  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, catalog[0].Nmeasure);
+  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, catalog[0].Nmissing);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, catalog[0].Nsecfilt);
+
+  /* specify the appropriate data format */
+  if (catalog[0].catformat == DVO_FORMAT_INTERNAL)  	  gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "INTERNAL");
+  if (catalog[0].catformat == DVO_FORMAT_LONEOS)    	  gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "LONEOS");
+  if (catalog[0].catformat == DVO_FORMAT_ELIXIR)    	  gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "ELIXIR");
+  if (catalog[0].catformat == DVO_FORMAT_PANSTARRS_DEV_0) gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "PANSTARRS_DEV_0");
+  if (catalog[0].catformat == DVO_FORMAT_PANSTARRS_DEV_1) gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "PANSTARRS_DEV_1");
+  if (catalog[0].catformat == DVO_FORMAT_PS1_DEV_1)       gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "PS1_DEV_1");
+  if (catalog[0].catformat == DVO_FORMAT_PS1_DEV_2)       gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "PS1_DEV_2");
+
+  /* rewind file pointers and truncate file */
+  f = catalog[0].f;
+  fseek (f, 0, SEEK_SET);
+  ftruncate (fileno (catalog[0].f), 0);
+
+  /* write header data (use gfits_write_header?) */
+  nitems = fwrite (catalog[0].header.buffer, 1, catalog[0].header.size, f);
+  if (nitems != catalog[0].header.size) {
+    if (VERBOSE) fprintf (stderr, "failed to write header\n");
+    goto failure;
+  }
+
+  /* write averages and measures */
+  WriteRawAverage (f, catalog[0].average, catalog[0].Naverage, catalog[0].catformat, primary);
+  WriteRawMeasure (f, catalog[0].measure, catalog[0].Nmeasure, catalog[0].catformat);
+
+  /* write missing data */
+  Nitems = catalog[0].Nmissing;
+  gfits_convert_Missing (catalog[0].missing, sizeof(Missing), Nitems);
+  nitems = fwrite (catalog[0].missing, sizeof(Missing), Nitems, f);
+  if (nitems != Nitems) {
+    if (VERBOSE) fprintf (stderr, "failed to write catalog file missing %s\n", catalog[0].filename);
+    goto failure;
+  }
+
+  Nitems = catalog[0].Naverage * catalog[0].Nsecfilt;
+  WriteRawSecFilt (f, catalog[0].secfilt, Nitems, catalog[0].catformat);
+
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (TRUE);
+
+failure:
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (FALSE);
+}
+
+/*** 
+     some warnings:
+     - need to be wary of exit condition errors
+***/
+
+/** Average / Raw Table conversions **/
+
+/* in the Elixir and Loneos cases, we are supplied with an average magnitude in Average
+   in these cases, save these values in primary; otherwise set primary to NULL */
+
+Average *ReadRawAverage (FILE *f, int Naverage, int format, SecFilt **primary) {
+
+  Average *average;
+
+  *primary = NULL;
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      Average_##TYPE *tmpAverage; \
+      ALLOCATE (tmpAverage, Average_##TYPE, MAX (Naverage, 1)); \
+      nitems = fread (tmpAverage, sizeof(Average_##TYPE), Naverage, f); \
+      if (nitems != Naverage) { \
+	fprintf (stderr, "failed to read averages (%d vs %d)\n", nitems, Naverage); \
+	return (NULL); \
+      } \
+      gfits_convert_Average_##TYPE (tmpAverage, sizeof(Average_##TYPE), Naverage); \
+      average = Average_##TYPE##_ToInternal (tmpAverage, Naverage, primary); \
+      free (tmpAverage); \
+      break; } \
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      ALLOCATE (average, Average, MAX (Naverage, 1));
+      nitems = fread (average, sizeof(Average), Naverage, f);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to read averages (%d vs %d)\n", nitems, Naverage);
+	return (NULL);
+      }
+      gfits_convert_Average (average, sizeof(Average), Naverage);
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error reading measures\n");
+      return (NULL);
+  }
+# undef FORMAT_CASE
+
+  return (average);
+}
+
+/* accepts and converts internal average formats and outputs 
+   raw data in the specified format */
+int WriteRawAverage (FILE *f, Average *average, int Naverage, int format, SecFilt *primary) {
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      Average_##TYPE *tmpAverage; \
+      tmpAverage = AverageInternalTo_##TYPE (average, Naverage, primary); \
+      gfits_convert_Average_##TYPE (tmpAverage, sizeof(Average_##TYPE), Naverage); \
+      nitems = fwrite (tmpAverage, sizeof(Average_##TYPE), Naverage, f); \
+      free (tmpAverage); \
+      if (nitems != Naverage) { \
+	fprintf (stderr, "failed to write averages (%d vs %d)\n", nitems, Naverage); \
+	return (FALSE); \
+      } \
+      break; }
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      gfits_convert_Average (average, sizeof(Average), Naverage);
+      nitems = fwrite (average, sizeof(Average), Naverage, f);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to write averages (%d vs %d)\n", nitems, Naverage);
+	return (FALSE);
+      }
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error writing averages\n");
+      return (FALSE);
+  }
+# undef FORMAT_CASE
+
+  return (TRUE);
+}
+
+/** Average / Raw Table conversions **/
+
+Measure *ReadRawMeasure (FILE *f, int Nmeasure, int format) {
+
+  Measure *measure;
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      Measure_##TYPE *tmpMeasure; \
+      ALLOCATE (tmpMeasure, Measure_##TYPE, MAX (Nmeasure, 1)); \
+      nitems = fread (tmpMeasure, sizeof(Measure_##TYPE), Nmeasure, f); \
+      if (nitems != Nmeasure) { \
+	fprintf (stderr, "failed to read measures (%d vs %d)\n", nitems, Nmeasure); \
+	return (NULL); \
+      } \
+      gfits_convert_Measure_##TYPE (tmpMeasure, sizeof(Measure_##TYPE), Nmeasure); \
+      measure = Measure_##TYPE##_ToInternal (tmpMeasure, Nmeasure); \
+      free (tmpMeasure); \
+      break; }
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      ALLOCATE (measure, Measure, MAX (Nmeasure, 1));
+      nitems = fread (measure, sizeof(Measure), Nmeasure, f);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to read measures (%d vs %d)\n", nitems, Nmeasure);
+	return (NULL);
+      }
+      gfits_convert_Measure (measure, sizeof(Measure), Nmeasure);
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error reading measures\n");
+      return (NULL);
+  }
+# undef FORMAT_CASE
+
+  return (measure);
+}
+
+/* accepts and converts internal measure formats and outputs 
+   raw data in the specified format */
+int WriteRawMeasure (FILE *f, Measure *measure, int Nmeasure, int format) {
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      Measure_##TYPE *tmpMeasure; \
+      tmpMeasure = MeasureInternalTo_##TYPE (measure, Nmeasure); \
+      gfits_convert_Measure_##TYPE (tmpMeasure, sizeof(Measure_##TYPE), Nmeasure); \
+      nitems = fwrite (tmpMeasure, sizeof(Measure_##TYPE), Nmeasure, f); \
+      free (tmpMeasure); \
+      if (nitems != Nmeasure) { \
+	fprintf (stderr, "failed to write measures (%d vs %d)\n", nitems, Nmeasure); \
+	return (FALSE); \
+      } \
+      break; }
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      gfits_convert_Measure (measure, sizeof(Measure), Nmeasure);
+      nitems = fwrite (measure, sizeof(Measure), Nmeasure, f);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to write measures (%d vs %d)\n", nitems, Nmeasure);
+	return (FALSE);
+      }
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error writing measures\n");
+      return (FALSE);
+  }
+# undef FORMAT_CASE
+
+  return (TRUE);
+}
+
+/** SecFilt / Raw Table conversions **/
+
+SecFilt *ReadRawSecFilt (FILE *f, int Nsecfilt, int format) {
+
+  SecFilt *secfilt;
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      SecFilt_##TYPE *tmpSecFilt; \
+      ALLOCATE (tmpSecFilt, SecFilt_##TYPE, MAX (Nsecfilt, 1)); \
+      nitems = fread (tmpSecFilt, sizeof(SecFilt_##TYPE), Nsecfilt, f); \
+      if (nitems != Nsecfilt) { \
+	fprintf (stderr, "failed to read secfilts (%d vs %d)\n", nitems, Nsecfilt); \
+	return (NULL); \
+      } \
+      gfits_convert_SecFilt_##TYPE (tmpSecFilt, sizeof(SecFilt_##TYPE), Nsecfilt); \
+      secfilt = SecFilt_##TYPE##_ToInternal (tmpSecFilt, Nsecfilt); \
+      free (tmpSecFilt); \
+      break; }
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      ALLOCATE (secfilt, SecFilt, MAX (Nsecfilt, 1));
+      nitems = fread (secfilt, sizeof(SecFilt), Nsecfilt, f);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to read secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (NULL);
+      }
+      gfits_convert_SecFilt (secfilt, sizeof(SecFilt), Nsecfilt);
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error reading measures\n");
+      return (NULL);
+  }
+# undef FORMAT_CASE
+
+  return (secfilt);
+}
+
+/* accepts and converts internal secfilt formats and outputs 
+   raw data in the specified format */
+int WriteRawSecFilt (FILE *f, SecFilt *secfilt, int Nsecfilt, int format) {
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      SecFilt_##TYPE *tmpSecFilt; \
+      tmpSecFilt = SecFiltInternalTo_##TYPE (secfilt, Nsecfilt); \
+      gfits_convert_SecFilt_##TYPE (tmpSecFilt, sizeof(SecFilt_##TYPE), Nsecfilt); \
+      nitems = fwrite (tmpSecFilt, sizeof(SecFilt_##TYPE), Nsecfilt, f); \
+      free (tmpSecFilt); \
+      if (nitems != Nsecfilt) { \
+	fprintf (stderr, "failed to write secfilts (%d vs %d)\n", nitems, Nsecfilt); \
+	return (FALSE); \
+      } \
+      break; }
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      gfits_convert_SecFilt (secfilt, sizeof(SecFilt), Nsecfilt);
+      nitems = fwrite (secfilt, sizeof(SecFilt), Nsecfilt, f);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to write secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (FALSE);
+      }
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error writing secfilts\n");
+      return (FALSE);
+  }
+# undef FORMAT_CASE
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_split.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_split.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_catalog_split.c	(revision 22322)
@@ -0,0 +1,1049 @@
+# include <dvo.h>
+
+// return options: 
+// * error (cannot lock, open, read, etc)
+// * empty (file is not found)
+// * ok
+
+int dvo_catalog_secfilt_to_primary (Catalog *catalog, SecFilt **myPrimary, SecFilt **mySecfilt, int *myNsecfilt) {
+
+  int i, j, Nallfilt, Nsecfilt, Ntotal;
+
+  SecFilt *primary;
+  SecFilt *secfilt;
+
+  if (catalog[0].secfilt == NULL) {       
+    fprintf (stderr, "missing secfilt, cannot build output averages (dvo_catalog_split.c)\n");
+    exit (1);
+  }
+  secfilt = catalog[0].secfilt;
+
+  // XXX this translation only works if we have loaded / created a matched average/secfilt set
+  assert (catalog[0].Nsecf_mem == catalog[0].Nsecfilt*catalog[0].Naverage);
+
+  Nallfilt = catalog[0].Nsecfilt;
+  Nsecfilt = catalog[0].Nsecfilt - 1;
+  Ntotal = Nsecfilt * catalog[0].Naverage;
+  ALLOCATE (primary, SecFilt, catalog[0].Naverage);
+  ALLOCATE (secfilt, SecFilt, Ntotal);
+
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    primary[i] = secfilt[i*Nallfilt + 0];
+    for (j = 0; j < Nsecfilt; j++) {
+      secfilt[i*Nsecfilt + j] = catalog[0].secfilt[i*Nallfilt + j + 1];
+    }
+  }		
+  catalog[0].Nsecfilt --;
+  catalog[0].Nsecf_mem = catalog[0].Naverage*catalog[0].Nsecfilt;
+
+  *myPrimary = primary;
+  *mySecfilt = secfilt;
+  *myNsecfilt = Nsecfilt;
+  return (TRUE);
+}
+
+int dvo_catalog_primary_to_secfilt (Catalog *catalog, SecFilt *primary, int Naves) {
+
+  int Ntmpfilt, Nsecfilt, Ntotal, i, j;
+  SecFilt *tmpfilt;
+
+  tmpfilt  = catalog[0].secfilt;
+  Ntmpfilt = catalog[0].Nsecfilt;
+
+  // we do NOT modify Nsecf_disk; this operation only modifies in in-memory values
+
+  catalog[0].Nsecfilt ++;
+  Nsecfilt = catalog[0].Nsecfilt;
+  Ntotal = Nsecfilt * Naves;
+
+  catalog[0].Nsecf_mem = Ntotal;
+
+  ALLOCATE (catalog[0].secfilt, SecFilt, Ntotal);
+  for (i = 0; i < Naves; i++) {
+    catalog[0].secfilt[i*Nsecfilt + 0] = primary[i];
+    for (j = 0; j < Ntmpfilt; j++) {
+      catalog[0].secfilt[i*Nsecfilt + j + 1] = tmpfilt[i*Ntmpfilt + j];
+    }
+  }		
+  free (tmpfilt);
+  free (primary);
+  return (TRUE);
+}
+
+int dvo_catalog_save_subcat (Catalog *catalog, FTable *ftable, int start, int Nrows, int Ndisk, int Ntotal) {
+
+  Matrix matrix;
+
+  /* rewind file pointers and truncate (file is still open) */
+  fseek (catalog->f, 0, SEEK_SET);
+
+  // write PHU header
+  if (!gfits_fwrite_header  (catalog->f, &catalog->header)) {
+    fprintf (stderr, "can't write primary header");
+    return (FALSE);
+  }
+
+  // write the PHU matrix; this is probably a NOP, do I have to keep it in?
+  gfits_create_matrix (&catalog->header, &matrix);
+  if (!gfits_fwrite_matrix  (catalog->f, &matrix)) {
+    fprintf (stderr, "can't write primary matrix");
+    gfits_free_matrix (&matrix);
+    return (FALSE);
+  }
+  gfits_free_matrix (&matrix);
+
+  // write the table data
+  if (!gfits_fwrite_ftable_range (catalog->f, ftable, start, Nrows, Ndisk, Ntotal)) {
+    fprintf (stderr, "can't write table data");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int dvo_catalog_open_subcat (Catalog *catalog, Catalog **Subcat, Header *header, char *name, int VERBOSE) {
+
+  int Nskip, status;
+  char *path, string[80];
+  Catalog *subcat;
+
+  /* in split mode, we need to init & open the corresponding measure file (even if we do not read
+   * any data in at this stage) */
+  ALLOCATE (subcat, Catalog, 1);
+  dvo_catalog_init (subcat, TRUE);
+
+  *Subcat = subcat;
+
+  /* needed to find the split files below */
+  path = pathname (catalog[0].filename);
+
+  /* get split filename from main header (paths relative to cpt file) */
+  if (!gfits_scan (&catalog[0].header, name,  "%s", 1, string)) {
+    free (path);
+    return (DVO_CAT_OPEN_FAIL);
+  }
+  ALLOCATE (subcat[0].filename, char, strlen(path) + strlen(string) + 2);
+  sprintf (subcat[0].filename, "%s/%s", path, string);
+  free (path);
+
+  /* lock & open catalog file */
+  status = dvo_catalog_lock (subcat, catalog[0].lockmode);
+  if (status != DVO_CAT_OPEN_OK) {
+    if (VERBOSE) {
+      if (status == DVO_CAT_OPEN_EMPTY) {
+	fprintf (stderr, "%s (%s) is empty\n", name, subcat[0].filename);
+      } else {
+	fprintf (stderr, "failure to lock %s (%s)\n", name, subcat[0].filename);
+      }
+    }
+    return (status);
+  }
+
+  /* read PHU */
+  if (!gfits_load_header (subcat[0].f, &subcat[0].header)) {
+    if (VERBOSE) fprintf (stderr, "error reading %s header: %s\n", name, subcat[0].filename);
+    return (DVO_CAT_OPEN_FAIL);
+  }
+  Nskip = gfits_data_size (&subcat[0].header);
+  fseek (subcat[0].f, Nskip, SEEK_CUR);
+
+  /* read Measure table header */
+  if (!gfits_fread_header (subcat[0].f, header)) {
+    if (VERBOSE) fprintf (stderr, "can't read %s PHU header\n", name);
+    gfits_free_header (&subcat[0].header);
+    return (DVO_CAT_OPEN_FAIL);
+  }
+  return (DVO_CAT_OPEN_OK);
+}
+
+int dvo_catalog_load_split (Catalog *catalog, int VERBOSE) {
+
+  int Nbytes, Nitems, Naverage, Nmeasure, Nmissing, Nsecfilt, status;
+  Header header;
+  FTable ftable;
+  SecFilt *primary;
+
+  /* ftable header storage for below */
+  ftable.header = &header;
+  ftable.buffer = NULL;
+  header.buffer = NULL;
+  primary = NULL;
+
+  /* get the components from the header - these duplicate information in the split files (NAXIS2) */
+  if (!gfits_scan (&catalog[0].header, "NSTARS",   "%d", 1, &Naverage)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMEAS",    "%d", 1, &Nmeasure)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMISS",    "%d", 1, &Nmissing)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NSECFILT", "%d", 1, &Nsecfilt)) Nsecfilt = 0;
+
+  /* save the current number so we can do partial updates */
+  catalog[0].Naves_disk = Naverage;
+  catalog[0].Nmeas_disk = Nmeasure;
+  catalog[0].Nmiss_disk = Nmissing;
+  catalog[0].Nsecf_disk = Naverage * Nsecfilt;
+
+  /**  Nsecfilt is unusual: it does not list the number of data items in the table
+       instead, the number of items is Nsecfilt * Naverage.  **/
+  catalog[0].Nsecfilt  = Nsecfilt;
+
+  /* default values, but we will assign these a valid value before we exit (even if empty) */
+  catalog[0].average = NULL;
+  catalog[0].measure = NULL;
+  catalog[0].missing = NULL;
+  catalog[0].secfilt = NULL;
+
+  /*** Average Table ***/
+  if (catalog[0].catflags & LOAD_AVES) {
+    /* move pointer past header -- must be already read (load_catalog) */
+    Nbytes = catalog[0].header.size + gfits_data_size (&catalog[0].header);
+    fseek (catalog[0].f, Nbytes, SEEK_SET);
+    /* read Average table header */
+    if (!gfits_fread_header (catalog[0].f, &header)) { 
+      if (VERBOSE) fprintf (stderr, "can't read table average header");
+      return (FALSE);
+    }
+    /* read Average table data : format is irrelevant here */
+    if (!gfits_fread_ftable_data (catalog[0].f, &ftable)) { 
+      if (VERBOSE) fprintf (stderr, "can't read table average data");
+      return (FALSE);
+    }
+    /* convert the saved version of the table to the internal version.  Old versions of DVO stored
+     * one of the average magnitudes in Average.  We save this in case it is needed below.  NOTE:
+     * primary is only used if we read in the secfilt table, otherwise it should be freed */
+    catalog[0].average = FtableToAverage (&ftable, &Naverage, &catalog[0].catformat, &primary);
+    if (Naverage != catalog[0].Naves_disk) {
+      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%d vs %d)\n", Naverage, catalog[0].Naves_disk);
+    }
+    gfits_free_header (&header);
+    catalog[0].Naverage = catalog[0].Naves_disk;
+    catalog[0].Naves_off = 0;
+  } else {
+    ALLOCATE (catalog[0].average, Average, 1);
+    catalog[0].Naverage = 0;
+    catalog[0].Naves_off = catalog[0].Naves_disk;
+  }
+
+  /*** Measure Table ***/
+  status = dvo_catalog_open_subcat (catalog, &catalog[0].measure_catalog, ftable.header, "MEASURE", VERBOSE);
+  if (status == DVO_CAT_OPEN_FAIL) {
+    return (FALSE);
+  }
+  if ((status == DVO_CAT_OPEN_EMPTY) && (catalog[0].Nmeas_disk > 0)) {
+    return (FALSE);
+  }
+  if ((status != DVO_CAT_OPEN_EMPTY) && (catalog[0].catflags & LOAD_MEAS)) {
+    // XXX this allows an empty Measure catalog with non-empty Average catalog : is that OK?
+    /* read Measure table data */
+    if (!gfits_fread_ftable_data (catalog[0].measure_catalog[0].f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure data\n");
+      return (FALSE);
+    }
+    /* convert data format to internal : returns number of row read in Nmeasure */
+    catalog[0].measure = FtableToMeasure (&ftable, &Nmeasure, &catalog[0].catformat);
+    if (Nmeasure != catalog[0].Nmeas_disk) {
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%d vs %d)\n", Nmeasure, catalog[0].Nmeas_disk);
+    }
+    catalog[0].Nmeasure = catalog[0].Nmeas_disk;
+    catalog[0].Nmeas_off = 0;
+  } else {
+    // XXX is it necessary to generate a template header here?
+    gfits_create_header (&catalog[0].measure_catalog[0].header);
+    ALLOCATE (catalog[0].measure, Measure, 1);
+    catalog[0].Nmeasure = 0;
+    catalog[0].Nmeas_off = catalog[0].Nmeas_disk;
+  }
+  gfits_free_header (&header);
+
+  /*** Missing Table ***/
+  status = dvo_catalog_open_subcat (catalog, &catalog[0].missing_catalog, ftable.header, "MISSING", VERBOSE);
+  if (status == DVO_CAT_OPEN_FAIL) {
+    return (FALSE);
+  }
+  if ((status == DVO_CAT_OPEN_EMPTY) && (catalog[0].Nmiss_disk > 0)) {
+    return (FALSE);
+  }
+  if ((status != DVO_CAT_OPEN_EMPTY) && (catalog[0].catflags & LOAD_MISS)) {
+    /* read Missing table data */
+    if (!gfits_fread_ftable_data (catalog[0].missing_catalog[0].f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table missing data\n");
+      return (FALSE);
+    }
+    /* no conversions currently defined : this just does the byte swap */
+    catalog[0].missing = gfits_table_get_Missing (&ftable, &Nmissing, NULL);
+    if (Nmissing != catalog[0].Nmiss_disk) {
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%d vs %d)\n", Nmissing, catalog[0].Nmiss_disk);
+    }
+    catalog[0].Nmissing = catalog[0].Nmiss_disk;
+    catalog[0].Nmiss_off = 0;
+  } else {
+    // XXX is it necessary to generate a template header here?
+    gfits_create_header (&catalog[0].missing_catalog[0].header);
+    ALLOCATE (catalog[0].missing, Missing, 1);
+    catalog[0].Nmissing = 0;
+    catalog[0].Nmiss_off = catalog[0].Nmiss_disk;
+  }
+  gfits_free_header (ftable.header);
+
+  /*** Secfilt Table ***/
+  status = dvo_catalog_open_subcat (catalog, &catalog[0].secfilt_catalog, ftable.header, "SECFILT", VERBOSE);
+  if (status == DVO_CAT_OPEN_FAIL) {
+    return (FALSE);
+  }
+  if ((status == DVO_CAT_OPEN_EMPTY) && (catalog[0].Nsecf_disk > 0)) {
+    return (FALSE);
+  }
+  if ((status != DVO_CAT_OPEN_EMPTY) && (catalog[0].catflags & LOAD_SECF)) {
+    /* read secfilt table data */
+    if (!gfits_fread_ftable_data (catalog[0].secfilt_catalog[0].f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table secfilt data\n");
+      return (FALSE);
+    }
+    catalog[0].secfilt = FtableToSecFilt (&ftable, &Nitems, &catalog[0].catformat);
+    if (Nitems != catalog[0].Nsecf_disk) {
+      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers (%d vs %d)\n", Nitems, catalog[0].Nsecf_disk);
+    }
+    catalog[0].Nsecf_mem = catalog[0].Nsecf_disk;
+    catalog[0].Nsecf_off = 0;
+
+    /* if primary is defined, we were supplied with one additional average magnitude from Average
+       we need to interleave these magnitudes with the secfilt entries just loaded */
+    if (primary != NULL) {
+      // this modifies catalog.Nsecf_mem,Nsecfilt
+      dvo_catalog_primary_to_secfilt (catalog, primary, catalog[0].Naves_disk);
+    } 
+  } else {
+    if (primary != NULL) {
+      free (primary);
+      catalog[0].Nsecfilt ++;
+    }
+    gfits_create_header (&catalog[0].secfilt_catalog[0].header);
+    ALLOCATE (catalog[0].secfilt, SecFilt, 1);
+    catalog[0].Nsecf_mem = 0;
+    catalog[0].Nsecf_off = catalog[0].Nsecf_disk;
+  }
+  gfits_free_header (ftable.header);
+
+  return (TRUE);
+}
+
+// I need to always read both average and secfilt at the same time to correctly manage the
+// primary secfilt values...
+int dvo_catalog_load_segment_split (Catalog *catalog, int VERBOSE, int start, int Nrows) {
+
+  int Nbytes, Naverage, Nexpect, Nitems, Nmeasure, Nmissing;
+  Header header;
+  FTable ftable;
+  SecFilt *primary;
+
+  /* ftable header storage for below */
+  ftable.header = &header;
+  ftable.buffer = NULL;
+  header.buffer = NULL;
+  primary = NULL;
+
+  /*** Average (& SecFilt) Table ***/
+  if (catalog[0].catflags & LOAD_AVES) {
+
+    /*** load the Average data ***/
+    /* move pointer past header and matrix -- must be already read (load_catalog) */
+    Nbytes = catalog[0].header.size + gfits_data_size (&catalog[0].header);
+    fseek (catalog[0].f, Nbytes, SEEK_SET);
+    /* read Average table header */
+    if (!gfits_fread_header (catalog[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read table average header");
+      return (FALSE);
+    }
+    /* read Average table data : format is irrelevant here */
+    if (!gfits_fread_ftable_range (catalog[0].f, &ftable, start, Nrows)) {
+      if (VERBOSE) fprintf (stderr, "can't read table average data");
+      return (FALSE);
+    }
+
+    /* convert the saved version of the table to the internal version.  Old versions of DVO stored
+     * one of the average magnitudes in Average.  We save this in case it is needed below.  NOTE:
+     * primary is only used if we read in the secfilt table, otherwise it should be freed */
+    catalog[0].average = FtableToAverage (&ftable, &Naverage, &catalog[0].catformat, &primary);
+    if (Naverage != Nrows) {
+      // XXX this condition denotes the eof has been reached; not an error or a warning
+      // fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%d vs %d)\n", Naverage, Nrows);
+    }
+    gfits_free_header (&header);
+    catalog[0].Naverage = Naverage;
+    catalog[0].Naves_off = start;
+
+    /*** load the secfilt data ***/
+    Catalog *subcat = catalog[0].secfilt_catalog;
+
+    /* move pointer past header -- must be already read (load_catalog) */
+    Nbytes = subcat[0].header.size + gfits_data_size (&subcat[0].header);
+    fseek (subcat[0].f, Nbytes, SEEK_SET);
+
+    /* read Secfilt table header */
+    if (!gfits_fread_header (subcat[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure header");
+      return (FALSE);
+    }
+    /* read Secfilt table data : format is irrelevant here */
+    if (!gfits_fread_ftable_range (subcat[0].f, &ftable, start*catalog[0].Nsecfilt, catalog[0].Naverage*catalog[0].Nsecfilt)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure data");
+      return (FALSE);
+    }
+
+    Nexpect = catalog[0].Naverage * catalog[0].Nsecfilt;
+    catalog[0].secfilt = FtableToSecFilt (&ftable, &Nitems, &catalog[0].catformat);
+    if (Nitems != Nexpect) {
+      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers (%d vs %d)\n", Nitems, Nexpect);
+    }
+    catalog[0].Nsecf_mem = catalog[0].Naverage * catalog[0].Nsecfilt;
+    catalog[0].Nsecf_off = start               * catalog[0].Nsecfilt;
+
+    /* if primary is defined, we were supplied with one additional average magnitude from Average
+       we need to interleave these magnitudes with the secfilt entries just loaded */
+    if (primary != NULL) {
+      dvo_catalog_primary_to_secfilt (catalog, primary, Nrows);
+    } 
+    gfits_free_header (&header);
+  }
+
+  // XXX check the open status of the catalog
+  if (catalog[0].catflags & LOAD_MEAS) {
+
+    Catalog *subcat = catalog[0].measure_catalog;
+
+    /* move pointer past header -- must be already read (load_catalog) */
+    Nbytes = subcat[0].header.size + gfits_data_size (&subcat[0].header);
+    fseek (subcat[0].f, Nbytes, SEEK_SET);
+
+    /* read Measure table header */
+    if (!gfits_fread_header (subcat[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure header");
+      return (FALSE);
+    }
+    /* read Measure table data : format is irrelevant here */
+    if (!gfits_fread_ftable_range (subcat[0].f, &ftable, start, Nrows)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure data");
+      return (FALSE);
+    }
+
+    /* convert data format to internal : returns number of row read in Nmeasure */
+    catalog[0].measure = FtableToMeasure (&ftable, &Nmeasure, &catalog[0].catformat);
+    if (Nmeasure != Nrows) {
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%d vs %d)\n", Nmeasure, Nrows);
+    }
+    gfits_free_header (&header);
+    catalog[0].Nmeasure = Nmeasure;
+    catalog[0].Nmeas_off = start;
+  }
+
+  // XXX check the open status of the catalog?
+  if (catalog[0].catflags & LOAD_MISS) {
+
+    Catalog *subcat = catalog[0].missing_catalog;
+
+    /* move pointer past header -- must be already read (load_catalog) */
+    Nbytes = subcat[0].header.size + gfits_data_size (&subcat[0].header);
+    fseek (subcat[0].f, Nbytes, SEEK_SET);
+
+    /* read Missing table header */
+    if (!gfits_fread_header (subcat[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read table missing header");
+      return (FALSE);
+    }
+    /* read Missing table data : format is irrelevant here */
+    if (!gfits_fread_ftable_range (subcat[0].f, &ftable, start, Nrows)) {
+      if (VERBOSE) fprintf (stderr, "can't read table missing data");
+      return (FALSE);
+    }
+
+    /* no conversions currently defined : this just does the byte swap */
+    catalog[0].missing = gfits_table_get_Missing (&ftable, &Nmissing, NULL);
+    if (Nmissing != Nrows) {
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%d vs %d)\n", Nmissing, Nrows);
+    }
+    gfits_free_header (&header);
+    catalog[0].Nmissing = Nmissing;
+    catalog[0].Nmiss_off = start;
+  }
+  return (TRUE);
+}
+
+/* save_catalog_split writes all data currently in memory to disk */
+int dvo_catalog_save_split (Catalog *catalog, char VERBOSE) {
+
+  int Nitems;
+  Header header;
+  FTable ftable;
+  SecFilt *primary, *secfilt;
+  int Nsecfilt;
+  int Naves_disk_new, Nmeas_disk_new, Nmiss_disk_new, Nsecf_disk_new;
+  int first, start, Nrows;
+
+  ftable.header = &header;
+  ftable.buffer = NULL;
+  header.buffer = NULL;
+  primary = NULL;
+  
+  // skip empty catalogs: it is illegal to have Measures without corresponding Averages
+  Naves_disk_new = MAX (catalog[0].Naves_disk, catalog[0].Naverage + catalog[0].Naves_off);
+  if (Naves_disk_new == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (TRUE);
+  }
+
+  // for the appropriate types, pull out the first secfilt and pass to AverageToFtable as primary
+  switch (catalog[0].catformat) {
+    case DVO_FORMAT_ELIXIR: // special case for ELIXIR
+    case DVO_FORMAT_LONEOS: // special case for LONEOS
+      dvo_catalog_secfilt_to_primary (catalog, &primary, &secfilt, &Nsecfilt);
+      break;
+    default:
+      primary = NULL;
+      secfilt = catalog[0].secfilt;
+      Nsecfilt = catalog[0].Nsecfilt;
+      break;
+  }
+
+  Nmeas_disk_new = MAX (catalog[0].Nmeas_disk, catalog[0].Nmeasure + catalog[0].Nmeas_off);
+  Nmiss_disk_new = MAX (catalog[0].Nmiss_disk, catalog[0].Nmissing + catalog[0].Nmiss_off);
+  Nsecf_disk_new = MAX (catalog[0].Nsecf_disk, catalog[0].Naverage*Nsecfilt + catalog[0].Nsecf_off);
+
+  /* make sure header is consistent with data */
+  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, Naves_disk_new);
+  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, Nmeas_disk_new);
+  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, Nmiss_disk_new);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
+  gfits_modify (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
+
+  /* in split mode, we can save only part of the data */ 
+
+  /*** Average Table ***/
+  if ((catalog[0].catflags & LOAD_AVES) && (catalog[0].average != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Naves_off; // first disk row to write
+    Nrows  = catalog[0].Naverage - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Naverage);
+    assert (catalog[0].Naves_disk >= catalog[0].Naves_off);
+
+    /* convert internal to external format */
+    if (!AverageToFtable (&ftable, &catalog[0].average[first], Nrows, catalog[0].catformat, primary)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    if (!dvo_catalog_save_subcat (catalog, &ftable, start, Nrows, catalog[0].Naves_disk, Naves_disk_new)) {
+      fprintf (stderr, "failure writing Average table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  } else {
+    // even if we do not save the average table, we need to keep the header in sync
+    /* rewind file pointers and truncate (file is still open) */
+    fseek (catalog[0].f, 0, SEEK_SET);
+
+    /* write table PHU header - always write this out */
+    /* XXX EAM : check if disk file size has changed */
+    if (!gfits_fwrite_header  (catalog[0].f, &catalog[0].header)) {
+      fprintf (stderr, "can't write primary header");
+      goto failure;
+    }
+  }
+
+  /*** Measure Table ***/
+  if ((catalog[0].catflags & LOAD_MEAS) && (catalog[0].measure != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Nmeas_off; // first disk row to write
+    Nrows  = catalog[0].Nmeasure - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Nmeasure);
+    assert (catalog[0].Nmeas_disk >= catalog[0].Nmeas_off);
+
+    // convert to external table format
+    if (!MeasureToFtable (&ftable, &catalog[0].measure[first], Nrows, catalog[0].catformat)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Measure table
+    if (!dvo_catalog_save_subcat (catalog[0].measure_catalog, &ftable, start, Nrows, catalog[0].Nmeas_disk, Nmeas_disk_new)) {
+      fprintf (stderr, "trouble writing Measure table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /*** Missing Table ***/
+  if ((catalog[0].catflags & LOAD_MISS) && (catalog[0].missing != NULL)) {
+
+    if (catalog[0].Nmiss_off != 0) {
+      fprintf (stderr, "inconsistency: Missing table cannot be written in segments\n");
+      goto failure;
+    }
+
+    // convert to external table format
+    if (!gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Missing table (must write out entire table)
+    if (!dvo_catalog_save_subcat (catalog[0].missing_catalog, &ftable, 0, catalog[0].Nmissing, catalog[0].Nmissing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble writing Missing Table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /*** Secfilt Table ***/
+  if ((catalog[0].catflags & LOAD_SECF) && (catalog[0].secfilt != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Nsecf_off; // first disk row to write
+    Nitems = catalog[0].Nsecf_mem;
+    Nrows  = Nitems - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= Nitems);
+    assert (catalog[0].Nsecf_disk >= catalog[0].Nsecf_off);
+    // XXX check these for consistency...
+
+    // convert to external table format
+    SecFiltToFtable (&ftable, &secfilt[first], Nrows, catalog[0].catformat);
+
+    // write out SecFilt table
+    if (!dvo_catalog_save_subcat (catalog[0].secfilt_catalog, &ftable, start, Nrows, catalog[0].Nsecf_disk, Nsecf_disk_new)) {
+      fprintf (stderr, "failure writing SecFilt table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (TRUE);
+
+failure:
+  /* free temp storage */
+  gfits_free_header (&header);
+  gfits_free_table (&ftable);
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (FALSE);
+}
+
+/* save_catalog_split writes all data currently in memory to disk, truncating if the disk file was larger */
+int dvo_catalog_save_split_complete (Catalog *catalog, char VERBOSE) {
+
+  int Nitems;
+  Header header;
+  FTable ftable;
+  SecFilt *primary, *secfilt;
+  int Nsecfilt;
+  int Naves_disk_new, Nmeas_disk_new, Nmiss_disk_new, Nsecf_disk_new;
+  int first, start, Nrows;
+
+  ftable.header = &header;
+  ftable.buffer = NULL;
+  header.buffer = NULL;
+  primary = NULL;
+  
+  // skip empty catalogs: it is illegal to have Measures without corresponding Averages
+  if (catalog[0].Naves_off > 0) {
+    fprintf (stderr, "ERROR: only partial catalog (Average) was loaded\n");
+    goto failure;
+  }
+  if (catalog[0].Nmeas_off > 0) {
+    fprintf (stderr, "ERROR: only partial catalog (Measure) was loaded\n");
+    goto failure;
+  }
+  if (catalog[0].Nmiss_off > 0) {
+    fprintf (stderr, "ERROR: only partial catalog (Missing) was loaded\n");
+    goto failure;
+  }
+  if (catalog[0].Nsecf_off > 0) {
+    fprintf (stderr, "ERROR: only partial catalog (Secfilt) was loaded\n");
+    goto failure;
+  }
+
+  if ((catalog[0].Naverage > 0) && (Naves_disk_new == 0)) {
+    Naves_disk_new = catalog[0].Naverage;
+    if (VERBOSE) fprintf (stderr, "resulting catalog is empty; delete it\n");
+    // unlink ();
+    return (TRUE);
+  }
+
+  Naves_disk_new = catalog[0].Naverage;
+
+  // for the appropriate types, pull out the first secfilt and pass to AverageToFtable as primary
+  switch (catalog[0].catformat) {
+    case DVO_FORMAT_ELIXIR: // special case for ELIXIR
+    case DVO_FORMAT_LONEOS: // special case for LONEOS
+      dvo_catalog_secfilt_to_primary (catalog, &primary, &secfilt, &Nsecfilt);
+      break;
+    default:
+      primary = NULL;
+      secfilt = catalog[0].secfilt;
+      Nsecfilt = catalog[0].Nsecfilt;
+      break;
+  }
+
+  Nmeas_disk_new = catalog[0].Nmeasure;
+  Nmiss_disk_new = catalog[0].Nmissing;
+  Nsecf_disk_new = catalog[0].Naverage*Nsecfilt;
+
+  /* make sure header is consistent with data */
+  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, Naves_disk_new);
+  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, Nmeas_disk_new);
+  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, Nmiss_disk_new);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
+  gfits_modify (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
+
+  /* in split mode, we can save only part of the data */ 
+
+  /*** Average Table ***/
+  if ((catalog[0].catflags & LOAD_AVES) && (catalog[0].average != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Naves_off; // first disk row to write
+    Nrows  = catalog[0].Naverage - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Naverage);
+    assert (catalog[0].Naves_disk >= catalog[0].Naves_off);
+
+    /* convert internal to external format */
+    if (!AverageToFtable (&ftable, &catalog[0].average[first], Nrows, catalog[0].catformat, primary)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    if (!dvo_catalog_save_subcat (catalog, &ftable, start, Nrows, catalog[0].Naves_disk, Naves_disk_new)) {
+      fprintf (stderr, "failure writing Average table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  } else {
+    // even if we do not save the average table, we need to keep the header in sync
+    /* rewind file pointers and truncate (file is still open) */
+    fseek (catalog[0].f, 0, SEEK_SET);
+
+    /* write table PHU header - always write this out */
+    /* XXX EAM : check if disk file size has changed */
+    if (!gfits_fwrite_header  (catalog[0].f, &catalog[0].header)) {
+      fprintf (stderr, "can't write primary header");
+      goto failure;
+    }
+  }
+
+  /*** Measure Table ***/
+  if ((catalog[0].catflags & LOAD_MEAS) && (catalog[0].measure != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Nmeas_off; // first disk row to write
+    Nrows  = catalog[0].Nmeasure - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Nmeasure);
+    assert (catalog[0].Nmeas_disk >= catalog[0].Nmeas_off);
+
+    // convert to external table format
+    if (!MeasureToFtable (&ftable, &catalog[0].measure[first], Nrows, catalog[0].catformat)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Measure table
+    if (!dvo_catalog_save_subcat (catalog[0].measure_catalog, &ftable, start, Nrows, catalog[0].Nmeas_disk, Nmeas_disk_new)) {
+      fprintf (stderr, "trouble writing Measure table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /*** Missing Table ***/
+  if ((catalog[0].catflags & LOAD_MISS) && (catalog[0].missing != NULL)) {
+
+    if (catalog[0].Nmiss_off != 0) {
+      fprintf (stderr, "inconsistency: Missing table cannot be written in segments\n");
+      goto failure;
+    }
+
+    // convert to external table format
+    if (!gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Missing table (must write out entire table)
+    if (!dvo_catalog_save_subcat (catalog[0].missing_catalog, &ftable, 0, catalog[0].Nmissing, catalog[0].Nmissing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble writing Missing Table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /*** Secfilt Table ***/
+  if ((catalog[0].catflags & LOAD_SECF) && (catalog[0].secfilt != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Nsecf_off; // first disk row to write
+    Nitems = catalog[0].Nsecf_mem;
+    Nrows  = Nitems - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= Nitems);
+    assert (catalog[0].Nsecf_disk >= catalog[0].Nsecf_off);
+    // XXX check these for consistency...
+
+    // convert to external table format
+    SecFiltToFtable (&ftable, &secfilt[first], Nrows, catalog[0].catformat);
+
+    // write out SecFilt table
+    if (!dvo_catalog_save_subcat (catalog[0].secfilt_catalog, &ftable, start, Nrows, catalog[0].Nsecf_disk, Nsecf_disk_new)) {
+      fprintf (stderr, "failure writing SecFilt table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (TRUE);
+
+failure:
+  /* free temp storage */
+  gfits_free_header (&header);
+  gfits_free_table (&ftable);
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (FALSE);
+}
+
+/* update_catalog_split only writes new lines to file. */
+int dvo_catalog_update_split (Catalog *catalog, char VERBOSE) {
+
+  Header header;
+  FTable ftable;
+  SecFilt *primary, *secfilt;
+  int Nsecfilt;
+  int Naves_disk_new, Nmeas_disk_new, Nmiss_disk_new, Nsecf_disk_new;
+  int first, start, Nrows;
+
+  ftable.header = &header;
+  ftable.buffer = NULL;
+  header.buffer = NULL;
+
+  // skip empty catalogs: it is illegal to have Measures without corresponding Averages
+  Naves_disk_new = MAX (catalog[0].Naves_disk, catalog[0].Naverage + catalog[0].Naves_off);
+  if (Naves_disk_new == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (TRUE);
+  }
+
+  // for the appropriate types, pull out the first secfilt and pass to AverageToFtable as primary 
+  switch (catalog[0].catformat) {
+    case DVO_FORMAT_ELIXIR: // special case for ELIXIR
+    case DVO_FORMAT_LONEOS: // special case for LONEOS
+      dvo_catalog_secfilt_to_primary (catalog, &primary, &secfilt, &Nsecfilt);
+      break;
+    default:
+      primary = NULL;
+      secfilt = catalog[0].secfilt;
+      Nsecfilt = catalog[0].Nsecfilt;
+      break;
+  }
+
+  Nmeas_disk_new = MAX (catalog[0].Nmeas_disk, catalog[0].Nmeasure + catalog[0].Nmeas_off);
+  Nmiss_disk_new = MAX (catalog[0].Nmiss_disk, catalog[0].Nmissing + catalog[0].Nmiss_off);
+  Nsecf_disk_new = MAX (catalog[0].Nsecf_disk, catalog[0].Naverage*Nsecfilt + catalog[0].Nsecf_off);
+
+  /* make sure header is consistent with data */
+  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, Naves_disk_new);
+  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, Nmeas_disk_new);
+  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, Nmiss_disk_new);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
+  gfits_modify (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
+
+  /* in split mode, we can save only part of the data */ 
+
+  /*** Average Table ***/
+  if (catalog[0].average != NULL) {
+
+    first  = catalog[0].Naves_disk - catalog[0].Naves_off; // first row to write (memory)
+    start  = catalog[0].Naves_disk;                        // first row to write (disk)
+    Nrows  = catalog[0].Naverage - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Naverage);
+    assert (catalog[0].Naves_disk >= catalog[0].Naves_off);
+
+    /* convert internal to external format */
+    if (!AverageToFtable (&ftable, &catalog[0].average[first], Nrows, catalog[0].catformat, primary)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    if (!dvo_catalog_save_subcat (catalog, &ftable, start, Nrows, catalog[0].Naves_disk, Naves_disk_new)) {
+      fprintf (stderr, "failure writing Average table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  } else {
+    // even if we do not save the average table, we need to keep the header in sync
+    /* rewind file pointers and truncate (file is still open) */
+    fseek (catalog[0].f, 0, SEEK_SET);
+
+    /* write table PHU header - always write this out */
+    /* XXX EAM : check if disk file size has changed */
+    if (!gfits_fwrite_header  (catalog[0].f, &catalog[0].header)) {
+      fprintf (stderr, "can't write primary header");
+      goto failure;
+    }
+  }
+
+  /*** Measure Table ***/
+  if (catalog[0].measure != NULL) {
+
+    first  = catalog[0].Nmeas_disk - catalog[0].Nmeas_off;  // first row in memory to write
+    start  = catalog[0].Nmeas_disk; // first disk row to write
+    Nrows  = catalog[0].Nmeasure - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Nmeasure);
+    assert (catalog[0].Nmeas_disk >= catalog[0].Nmeas_off);
+
+    // convert to external table format
+    if (!MeasureToFtable (&ftable, &catalog[0].measure[first], Nrows, catalog[0].catformat)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Measure table
+    if (!dvo_catalog_save_subcat (catalog[0].measure_catalog, &ftable, start, Nrows, catalog[0].Nmeas_disk, Nmeas_disk_new)) {
+      fprintf (stderr, "trouble writing Measure table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /* missing table CANNOT be written unsorted, thus it is always written 
+     out in full */
+
+  /*** Missing Table ***/
+
+  if (catalog[0].missing != NULL) {
+
+    if (catalog[0].Nmiss_off != 0) {
+      fprintf (stderr, "inconsistency: Missing table cannot be written in segments\n");
+      goto failure;
+    }
+
+    // convert to external table format
+    if (!gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Missing table (must write out entire table)
+    if (!dvo_catalog_save_subcat (catalog[0].missing_catalog, &ftable, 0, catalog[0].Nmissing, catalog[0].Nmissing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble writing Missing Table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /*** Secfilt Table ***/
+  if (catalog[0].secfilt != NULL) {
+
+    first  = catalog[0].Nsecf_disk - catalog[0].Nsecf_off;  // first row in memory to write
+    start  = catalog[0].Nsecf_disk; // first disk row to write
+    Nrows  = catalog[0].Nsecf_mem - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (catalog[0].Nsecf_disk >= catalog[0].Nsecf_off);
+
+    // convert to external table format
+    SecFiltToFtable (&ftable, &secfilt[first], Nrows, catalog[0].catformat);
+
+    // write out SecFilt table
+    if (!dvo_catalog_save_subcat (catalog[0].secfilt_catalog, &ftable, start, Nrows, catalog[0].Nsecf_disk, Nsecf_disk_new)) {
+      fprintf (stderr, "failure writing SecFilt table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+
+  return (TRUE);
+
+failure:
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (FALSE);
+}
+
+/* in split mode, extra files are linked to catalog->measure_catalog, etc.  Each
+   has a valid filename, f, header.  The primary catalog data pointers are set
+   to point at the corresponding entries from the elements on the chain. An
+   unloaded entry has a null pointer here.
+*/
+
+/* XXX EAM : this file needs work on the error exit conditions and memory leaks, esp under errors */
+
+/* XXX EAM : update is not efficient.  MeasureToFtable should only 
+   convert the new rows (Nmeas_disk to Nmeasure). the resulting
+   table represents the end rows of the ftable.  we need to define
+   the vtable based on the ftable, but with Ny = Nmeasure */  
+  
+
+
+    // * convert to an ftable
+    // * optionally write the PHU header/matrix
+    // * advance to the start of the output data block:
+    // ** Nx * catalog[0].Nmeas_off
+    // * write out the ftable data block
+    // * if Nmeas_off + Nmeasure >= Nmeas_disk, update padding
+    // ** start = Nmeas_off
+    // ** Nrows = Nmeasure
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert.c	(revision 22322)
@@ -0,0 +1,383 @@
+# include <dvo.h>
+
+/* The Ftable-TYPE conversion functions determine the format of table based on EXTNAME in header.
+   they convert the table to the internal format, and set 'format'.  
+
+   The TYPE-Ftable conversions functions create output tables in the format requested
+   by the 'format' function parameter.
+*/
+
+/** this file might be more readable if I use macros for the repetative
+    constructions below **/
+
+/*** Average / FTable conversion functions ***/
+
+Average *FtableToAverage (FTable *ftable, int *Naverage, int *format, SecFilt **primary) {
+
+  Average *average;
+  char extname[80];
+
+  /* in the Elixir and Loneos cases, we are supplied with an average magnitude in Average
+     in these cases, save these values in primary; otherwise set primary to NULL */
+  *primary = NULL;
+
+  /* convert to the internal format */
+  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
+    fprintf (stderr, "EXTNAME missing for average table\n");
+    return (FALSE);
+  }
+
+# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
+  if (!strcmp (extname, NAME)) { \
+    Average_##TYPE *tmpAverage; \
+    tmpAverage = gfits_table_get_Average_##TYPE (ftable, Naverage, NULL); \
+    average = Average_##TYPE##_ToInternal (tmpAverage, *Naverage, primary); \
+    free (tmpAverage); \
+    *format = DVO_FORMAT_##FORMAT; \
+    return (average); }
+
+  if (!strcmp (extname, "DVO_AVERAGE")) {
+    average = gfits_table_get_Average (ftable, Naverage, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (average);
+  }
+
+  CONVERT_FORMAT ("DVO_AVERAGE_ELIXIR", 	 ELIXIR, 	  Elixir);
+  CONVERT_FORMAT ("DVO_AVERAGE_LONEOS", 	 LONEOS, 	  Loneos);
+  CONVERT_FORMAT ("DVO_AVERAGE_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
+  CONVERT_FORMAT ("DVO_AVERAGE_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
+  CONVERT_FORMAT ("DVO_AVERAGE_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
+  CONVERT_FORMAT ("DVO_AVERAGE_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
+# undef CONVERT_FORMAT
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+
+  *Naverage = 0;
+  return (NULL);
+}
+
+int AverageToFtable (FTable *ftable, Average *average, int Naverage, int format, SecFilt *primary) {
+
+# define FORMAT_CASE(FORMAT, TYPE) \
+    case DVO_FORMAT_##FORMAT: { \
+      Average_##TYPE *tmpAverage; \
+      tmpAverage = AverageInternalTo_##TYPE (average, Naverage, primary); \
+      gfits_table_set_Average_##TYPE (ftable, tmpAverage, Naverage); \
+      free (tmpAverage); \
+      break; }
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_table_set_Average (ftable, average, Naverage);
+      break; }
+
+      FORMAT_CASE (ELIXIR, 	    Elixir);
+      FORMAT_CASE (LONEOS, 	    Loneos);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
+# undef FORMAT_CASE
+
+    default:
+      fprintf (stderr, "table format unknown (average)\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
+/*** Measure / FTable conversion functions ***/
+
+Measure *FtableToMeasure (FTable *ftable, int *Nmeasure, int *format) {
+
+  Measure *measure;
+  char extname[80];
+
+  /* convert to the internal format */
+  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
+    fprintf (stderr, "EXTNAME missing for measure table\n");
+    return (FALSE);
+  }
+
+# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
+  if (!strcmp (extname, NAME)) { \
+    Measure_##TYPE *tmpMeasure; \
+    tmpMeasure = gfits_table_get_Measure_##TYPE (ftable, Nmeasure, NULL); \
+    measure = Measure_##TYPE##_ToInternal (tmpMeasure, *Nmeasure); \
+    free (tmpMeasure); \
+    *format = DVO_FORMAT_##FORMAT; \
+    return (measure); }
+
+  if (!strcmp (extname, "DVO_MEASURE")) {
+    measure = gfits_table_get_Measure (ftable, Nmeasure, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (measure);
+  }
+
+  CONVERT_FORMAT ("DVO_MEASURE_ELIXIR", 	 ELIXIR, 	  Elixir);
+  CONVERT_FORMAT ("DVO_MEASURE_LONEOS", 	 LONEOS,          Loneos);
+  CONVERT_FORMAT ("DVO_MEASURE_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
+  CONVERT_FORMAT ("DVO_MEASURE_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
+  CONVERT_FORMAT ("DVO_MEASURE_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
+  CONVERT_FORMAT ("DVO_MEASURE_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
+# undef CONVERT_FORMAT
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+
+  *Nmeasure = 0;
+  return (NULL);
+}
+
+int MeasureToFtable (FTable *ftable, Measure *measure, int Nmeasure, int format) {
+
+# define FORMAT_CASE(FORMAT, TYPE) \
+    case DVO_FORMAT_##FORMAT: { \
+      Measure_##TYPE *tmpMeasure; \
+      tmpMeasure = MeasureInternalTo_##TYPE (measure, Nmeasure); \
+      gfits_table_set_Measure_##TYPE (ftable, tmpMeasure, Nmeasure); \
+      free (tmpMeasure); \
+      break; }
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_table_set_Measure (ftable, measure, Nmeasure);
+      break; }
+
+      FORMAT_CASE (ELIXIR, 	    Elixir);
+      FORMAT_CASE (LONEOS, 	    Loneos);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
+# undef FORMAT_CASE
+
+    default:
+      fprintf (stderr, "table format unknown (measure)\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
+/*** SecFilt / FTable conversion functions ***/
+
+SecFilt *FtableToSecFilt (FTable *ftable, int *Nsecfilt, int *format) {
+
+  SecFilt *secfilt;
+  char extname[80];
+
+  /* convert to the internal format */
+  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
+    fprintf (stderr, "EXTNAME missing for secfilt table\n");
+    return (FALSE);
+  }
+
+# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
+  if (!strcmp (extname, NAME)) { \
+    SecFilt_##TYPE *tmpSecFilt; \
+    tmpSecFilt = gfits_table_get_SecFilt_##TYPE (ftable, Nsecfilt, NULL); \
+    secfilt = SecFilt_##TYPE##_ToInternal (tmpSecFilt, *Nsecfilt); \
+    free (tmpSecFilt); \
+    *format = DVO_FORMAT_##FORMAT; \
+    return (secfilt); }
+
+  if (!strcmp (extname, "DVO_SECFILT")) {
+    secfilt = gfits_table_get_SecFilt (ftable, Nsecfilt, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (secfilt);
+  }
+
+  CONVERT_FORMAT ("DVO_SECFILT_ELIXIR", 	 ELIXIR, 	  Elixir);
+  CONVERT_FORMAT ("DVO_SECFILT_LONEOS", 	 LONEOS, 	  Loneos);
+  CONVERT_FORMAT ("DVO_SECFILT_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
+  CONVERT_FORMAT ("DVO_SECFILT_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
+  CONVERT_FORMAT ("DVO_SECFILT_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
+  CONVERT_FORMAT ("DVO_SECFILT_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
+# undef CONVERT_FORMAT
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+
+  *Nsecfilt = 0;
+  return (NULL);
+}
+
+int SecFiltToFtable (FTable *ftable, SecFilt *secfilt, int Nsecfilt, int format) {
+
+# define FORMAT_CASE(FORMAT, TYPE) \
+    case DVO_FORMAT_##FORMAT: { \
+      SecFilt_##TYPE *tmpSecFilt; \
+      tmpSecFilt = SecFiltInternalTo_##TYPE (secfilt, Nsecfilt); \
+      gfits_table_set_SecFilt_##TYPE (ftable, tmpSecFilt, Nsecfilt); \
+      free (tmpSecFilt); \
+      break; }
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_table_set_SecFilt (ftable, secfilt, Nsecfilt);
+      break; }
+
+      FORMAT_CASE (ELIXIR, 	    Elixir);
+      FORMAT_CASE (LONEOS, 	    Loneos);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
+# undef FORMAT_CASE
+
+    default:
+      fprintf (stderr, "table format unknown (secfilt)\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
+/*** Image Conversions ***/
+
+int FtableToImage (FTable *ftable, Header *theader, int *format) {
+
+  int Nimage;
+  char extname[80];
+
+  /* extname may be set from outside if the source is RAW not MEF */
+  if (*format == DVO_FORMAT_ELIXIR) {		  // special case for ELIXIR
+    Image_Elixir *tmpimage;
+    tmpimage = gfits_table_get_Image_Elixir (ftable, &Nimage, NULL);
+    ftable[0].buffer = (char *) Image_Elixir_ToInternal (tmpimage, Nimage);
+    free (tmpimage);
+    gfits_free_header (theader);
+    gfits_table_mkheader_Image (theader);
+    gfits_modify (theader, "NAXIS2", "%d", 1, Nimage);
+    theader[0].Naxis[1] = Nimage;
+    ftable[0].size = gfits_data_size (theader);
+    return (TRUE);
+  }
+
+  /* convert to the internal format */
+  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
+    fprintf (stderr, "EXTNAME missing for image table\n");
+    return (FALSE);
+  }
+
+# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
+  if (!strcmp (extname, NAME)) { \
+    Image_##TYPE *tmpimage; \
+    *format = DVO_FORMAT_##FORMAT; \
+    tmpimage = gfits_table_get_Image_##TYPE (ftable, &Nimage, NULL); \
+    ftable[0].buffer = (char *) Image_##TYPE##_ToInternal (tmpimage, Nimage); \
+    free (tmpimage); \
+    gfits_free_header (theader); \
+    gfits_table_mkheader_Image (theader); \
+    gfits_modify (theader, "NAXIS2", "%d", 1, Nimage); \
+    theader[0].Naxis[1] = Nimage; \
+    ftable[0].size = gfits_data_size (theader); \
+    return (TRUE); }
+
+  if (!strcmp (extname, "DVO_IMAGE")) {
+    Image *image;
+    image = gfits_table_get_Image (ftable, &Nimage, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (TRUE);
+  }
+
+  CONVERT_FORMAT ("DVO_IMAGE_ELIXIR", 	       ELIXIR,          Elixir);
+  CONVERT_FORMAT ("DVO_IMAGE_LONEOS", 	       LONEOS,          Loneos);
+  CONVERT_FORMAT ("DVO_IMAGE_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
+  CONVERT_FORMAT ("DVO_IMAGE_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
+  CONVERT_FORMAT ("DVO_IMAGE_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
+  CONVERT_FORMAT ("DVO_IMAGE_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
+  CONVERT_FORMAT ("DVO_IMAGE_PS1_DEV_3",       PS1_DEV_3,       PS1_DEV_3);
+# undef CONVERT_FORMAT
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+  return (FALSE);
+}
+
+int ImageToFtable (FTable *ftable, Header *theader, int format) {
+
+  int Nimage;
+
+  Nimage = theader[0].Naxis[1];
+
+# define FORMAT_CASE(FORMAT, TYPE) \
+    case DVO_FORMAT_##FORMAT: { \
+      Image_##TYPE *tmpImage; \
+      tmpImage = ImageInternalTo_##TYPE ((Image *) ftable[0].buffer, Nimage); \
+      free (ftable[0].buffer); \
+      gfits_table_set_Image_##TYPE (ftable, tmpImage, Nimage); \
+      free (tmpImage); \
+      break; }
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_convert_Image ((Image *) ftable[0].buffer, sizeof(Image), Nimage);
+      break; }
+
+      FORMAT_CASE (ELIXIR, 	    Elixir);
+      FORMAT_CASE (LONEOS, 	    Loneos);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
+      FORMAT_CASE (PS1_DEV_3,       PS1_DEV_3);
+# undef FORMAT_CASE
+
+    default:
+      fprintf (stderr, "table format unknown (image ftable)\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
+int ImageToVtable (VTable *vtable, Header *theader, int format) {
+
+  int i, Nrow, Nimage;
+
+  Nrow = vtable[0].Nrow;
+
+# define FORMAT_CASE(FORMAT, TYPE) \
+  case DVO_FORMAT_##FORMAT: { \
+      Image_##TYPE *tmpImage; \
+      /* convert table rows from internal to external format */ \
+      for (i = 0; i < Nrow; i++) { \
+	  tmpImage = ImageInternalTo_##TYPE ((Image *) vtable[0].buffer[i], 1); \
+	  gfits_convert_Image_##TYPE (tmpImage, sizeof(Image_##TYPE), 1); \
+	  free (vtable[0].buffer[i]); \
+	  vtable[0].buffer[i] = (char *) tmpImage; \
+      } \
+      /* convert header from old format to new format */ \
+      gfits_scan (theader, "NAXIS2", "%d", 1, &Nimage); \
+      gfits_free_header (theader); \
+      gfits_table_mkheader_Image_##TYPE (theader); \
+      gfits_modify (theader, "NAXIS2", "%d", 1, Nimage); \
+      theader[0].Naxis[1] = Nimage; \
+      vtable[0].size = gfits_data_size (theader); \
+      return (TRUE); }
+
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      for (i = 0; i < Nrow; i++) {
+	gfits_convert_Image ((Image *) vtable[0].buffer[i], sizeof(Image), 1);
+      }
+      return (TRUE); }
+
+      FORMAT_CASE (ELIXIR, 	    Elixir);
+      FORMAT_CASE (LONEOS, 	    Loneos);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
+      FORMAT_CASE (PS1_DEV_3,       PS1_DEV_3);
+# undef FORMAT_CASE
+
+    default:
+      break;
+  }
+  fprintf (stderr, "table format unknown (image vtable)\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_1.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_1.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_1.c	(revision 22322)
@@ -0,0 +1,366 @@
+# include <dvo.h>
+
+/* convert PS1_DEV_1 formats to internal formats */
+
+Measure *Measure_PS1_DEV_1_ToInternal (Measure_PS1_DEV_1 *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Map        = in[i].Mgal;
+    out[i].dM         = in[i].dM;
+    out[i].dt         = in[i].dt;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].detID      = in[i].detID;
+    out[i].imageID    = in[i].imageID;
+    out[i].qPSF       = in[i].qPSF;
+    out[i].psfChisq   = in[i].psfChisq;
+    out[i].crNsigma   = in[i].crNsigma;
+    out[i].extNsigma  = in[i].extNsigma;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].photcode   = in[i].photcode;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].dbFlags    = in[i].dbFlags;
+    out[i].photFlags  = in[i].photFlags;
+    out[i].stargal    = in[i].stargal;
+    out[i].dophot     = in[i].dophot;
+  }
+  return (out);
+}
+
+Measure_PS1_DEV_1 *MeasureInternalTo_PS1_DEV_1 (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_PS1_DEV_1 *out;
+
+  ALLOCATE (out, Measure_PS1_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Mgal       = in[i].Map;
+    out[i].dM         = in[i].dM;
+    out[i].dt         = in[i].dt;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].detID      = in[i].detID;
+    out[i].imageID    = in[i].imageID;
+    out[i].qPSF       = in[i].qPSF;
+    out[i].psfChisq   = in[i].psfChisq;
+    out[i].crNsigma   = in[i].crNsigma;
+    out[i].extNsigma  = in[i].extNsigma;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].photcode   = in[i].photcode;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].dbFlags    = in[i].dbFlags;
+    out[i].photFlags  = in[i].photFlags;
+    out[i].stargal    = in[i].stargal;
+    out[i].dophot     = in[i].dophot;
+  }
+  return (out);
+}
+
+// 'primary' is needed to conform with the API for Loneos and Elixir, but is not used
+Average *Average_PS1_DEV_1_ToInternal (Average_PS1_DEV_1 *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        	 = in[i].R;      
+    out[i].D        	 = in[i].D;      
+    out[i].Xp       	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nm;     
+    out[i].Nmissing      = in[i].Nn;     
+    out[i].code     	 = in[i].code;   
+    out[i].measureOffset = in[i].offset; 
+    out[i].missingOffset = in[i].missing;
+    out[i].dR       	 = in[i].dR;
+    out[i].dD       	 = in[i].dD;
+    out[i].uR       	 = in[i].uR;
+    out[i].uD       	 = in[i].uD;
+    out[i].duR      	 = in[i].duR;
+    out[i].duD      	 = in[i].duD;
+    out[i].P        	 = in[i].P;
+    out[i].dP       	 = in[i].dP;
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+  }
+  return (out);
+}
+
+// 'primary' is needed to conform with the API for Loneos and Elixir, but is not used
+Average_PS1_DEV_1 *AverageInternalTo_PS1_DEV_1 (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_PS1_DEV_1 *out;
+
+  ALLOCATE (out, Average_PS1_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        = in[i].R;      
+    out[i].D        = in[i].D;      
+    out[i].Xp       = in[i].Xp;     
+    out[i].Nm       = in[i].Nmeasure;     
+    out[i].Nn       = in[i].Nmissing;     
+    out[i].code     = in[i].code;   
+    out[i].offset   = in[i].measureOffset; 
+    out[i].missing  = in[i].missingOffset;
+    out[i].dR       = in[i].dR;
+    out[i].dD       = in[i].dD;
+    out[i].uR       = in[i].uR;
+    out[i].uD       = in[i].uD;
+    out[i].duR      = in[i].duR;
+    out[i].duD      = in[i].duD;
+    out[i].P        = in[i].P;
+    out[i].dP       = in[i].dP;
+    out[i].objID    = in[i].objID;
+    out[i].catID    = in[i].catID;
+  }
+  return (out);
+}
+
+SecFilt *SecFilt_PS1_DEV_1_ToInternal (SecFilt_PS1_DEV_1 *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+ }
+  return (out);
+}
+
+SecFilt_PS1_DEV_1 *SecFiltInternalTo_PS1_DEV_1 (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_PS1_DEV_1 *out;
+
+  ALLOCATE (out, SecFilt_PS1_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+  }
+  return (out);
+}
+
+Image *Image_PS1_DEV_1_ToInternal (Image_PS1_DEV_1 *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 63); // out[128], in[64]
+    out[i].name[63] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+/* convert internal images to panstarrs-format images */
+Image_PS1_DEV_1 *ImageInternalTo_PS1_DEV_1 (Image *in, int Nvalues) {
+
+  int i;
+  Image_PS1_DEV_1 *out;
+
+  ALLOCATE (out, Image_PS1_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 63); // out[128], in[64]
+    out[i].name[63] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+PhotCode_PS1_DEV_1 *PhotCode_Internal_To_PS1_DEV_1 (PhotCode *in, int Nvalues) {
+
+  int i;
+  PhotCode_PS1_DEV_1 *out;
+
+  ALLOCATE (out, PhotCode_PS1_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code = in[i].code;         
+    out[i].type = in[i].type;         
+    out[i].C  	= in[i].C;            
+    out[i].dC 	= in[i].dC;           
+    out[i].dX 	= in[i].dX;           
+    out[i].K  	= in[i].K;            
+    out[i].c1 	= in[i].c1;           
+    out[i].c2 	= in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].astromErrMagScale = out[i].astromErrMagScale;
+    out[i].photomErrSys      = out[i].photomErrSys;
+
+  }
+  return (out);
+}
+
+PhotCode *PhotCode_PS1_DEV_1_To_Internal (PhotCode_PS1_DEV_1 *in, int Nvalues) {
+
+  int i;
+  PhotCode *out;
+
+  ALLOCATE (out, PhotCode, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C  	 = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+
+    // not defined in PS1_DEV_1
+    out[i].astromErrSys      = 0.0;
+    out[i].astromErrScale    = 0.0;
+    out[i].astromErrMagScale = in[i].astromErrMagScale;
+    out[i].photomErrSys      = in[i].photomErrSys;
+
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].photomPoorMask      = 0;
+    out[i].photomBadMask       = 0;
+    out[i].astromPoorMask      = 0;
+    out[i].astromBadMask       = 0;
+  }
+  return (out);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_2.c	(revision 22322)
@@ -0,0 +1,374 @@
+# include <dvo.h>
+
+/* convert PS1_DEV_2 formats to internal formats */
+
+Measure *Measure_PS1_DEV_2_ToInternal (Measure_PS1_DEV_2 *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Map        = in[i].Map;
+    out[i].dM         = in[i].dM;
+    out[i].dMcal      = in[i].dMcal;
+    out[i].dt         = in[i].dt;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].detID      = in[i].detID;
+    out[i].imageID    = in[i].imageID;
+    out[i].qPSF       = in[i].qPSF;
+    out[i].psfChisq   = in[i].psfChisq;
+    out[i].crNsigma   = in[i].crNsigma;
+    out[i].extNsigma  = in[i].extNsigma;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].photcode   = in[i].photcode;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].dbFlags    = in[i].dbFlags;
+    out[i].photFlags  = in[i].photFlags;
+    out[i].stargal    = in[i].stargal;
+    out[i].dophot     = in[i].dophot;
+  }
+  return (out);
+}
+
+Measure_PS1_DEV_2 *MeasureInternalTo_PS1_DEV_2 (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_PS1_DEV_2 *out;
+
+  ALLOCATE (out, Measure_PS1_DEV_2, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Map        = in[i].Map;
+    out[i].dM         = in[i].dM;
+    out[i].dMcal      = in[i].dMcal;
+    out[i].dt         = in[i].dt;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].detID      = in[i].detID;
+    out[i].imageID    = in[i].imageID;
+    out[i].qPSF       = in[i].qPSF;
+    out[i].psfChisq   = in[i].psfChisq;
+    out[i].crNsigma   = in[i].crNsigma;
+    out[i].extNsigma  = in[i].extNsigma;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].photcode   = in[i].photcode;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].dbFlags    = in[i].dbFlags;
+    out[i].photFlags  = in[i].photFlags;
+    out[i].stargal    = in[i].stargal;
+    out[i].dophot     = in[i].dophot;
+  }
+  return (out);
+}
+
+// 'primary' is needed to conform with the API for Loneos and Elixir, but is not used
+Average *Average_PS1_DEV_2_ToInternal (Average_PS1_DEV_2 *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        	 = in[i].R;      
+    out[i].D        	 = in[i].D;      
+    out[i].dR       	 = in[i].dR;
+    out[i].dD       	 = in[i].dD;
+    out[i].uR       	 = in[i].uR;
+    out[i].uD       	 = in[i].uD;
+    out[i].duR      	 = in[i].duR;
+    out[i].duD      	 = in[i].duD;
+    out[i].P        	 = in[i].P;
+    out[i].dP       	 = in[i].dP;
+    out[i].Xp       	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nmeasure;     
+    out[i].Nmissing      = in[i].Nmissing;     
+    out[i].Nextend       = in[i].Nextend;     
+    out[i].code     	 = in[i].code;   
+    out[i].measureOffset = in[i].measureOffset; 
+    out[i].missingOffset = in[i].missingOffset;
+    out[i].extendOffset  = in[i].extendOffset;
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+  }
+  return (out);
+}
+
+// 'primary' is needed to conform with the API for Loneos and Elixir, but is not used
+Average_PS1_DEV_2 *AverageInternalTo_PS1_DEV_2 (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_PS1_DEV_2 *out;
+
+  ALLOCATE (out, Average_PS1_DEV_2, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        	 = in[i].R;      
+    out[i].D        	 = in[i].D;      
+    out[i].dR       	 = in[i].dR;
+    out[i].dD       	 = in[i].dD;
+    out[i].uR       	 = in[i].uR;
+    out[i].uD       	 = in[i].uD;
+    out[i].duR      	 = in[i].duR;
+    out[i].duD      	 = in[i].duD;
+    out[i].P        	 = in[i].P;
+    out[i].dP       	 = in[i].dP;
+    out[i].Xp       	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nmeasure;     
+    out[i].Nmissing      = in[i].Nmissing;     
+    out[i].Nextend       = in[i].Nextend;     
+    out[i].code     	 = in[i].code;   
+    out[i].measureOffset = in[i].measureOffset; 
+    out[i].missingOffset = in[i].missingOffset;
+    out[i].extendOffset  = in[i].extendOffset;
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+  }
+  return (out);
+}
+
+SecFilt *SecFilt_PS1_DEV_2_ToInternal (SecFilt_PS1_DEV_2 *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+ }
+  return (out);
+}
+
+SecFilt_PS1_DEV_2 *SecFiltInternalTo_PS1_DEV_2 (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_PS1_DEV_2 *out;
+
+  ALLOCATE (out, SecFilt_PS1_DEV_2, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+  }
+  return (out);
+}
+
+Image *Image_PS1_DEV_2_ToInternal (Image_PS1_DEV_2 *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 63); // out[128], int[64]
+    out[i].name[63] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID;
+    out[i].externID	    = in[i].externID;
+    out[i].sourceID	    = in[i].sourceID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+Image_PS1_DEV_2 *ImageInternalTo_PS1_DEV_2 (Image *in, int Nvalues) {
+
+  int i;
+  Image_PS1_DEV_2 *out;
+
+  ALLOCATE (out, Image_PS1_DEV_2, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 63); // in[128], out[64]
+    out[i].name[63] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID;
+    out[i].externID	    = in[i].externID;
+    out[i].sourceID	    = in[i].sourceID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+PhotCode_PS1_DEV_2 *PhotCode_Internal_To_PS1_DEV_2 (PhotCode *in, int Nvalues) {
+
+  int i;
+  PhotCode_PS1_DEV_2 *out;
+
+  ALLOCATE (out, PhotCode_PS1_DEV_2, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C     = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].astromErrSys      = in[i].astromErrSys;
+    out[i].astromErrScale    = in[i].astromErrScale;
+    out[i].astromErrMagScale = in[i].astromErrMagScale;
+    out[i].photomErrSys      = in[i].photomErrSys;
+  }
+  return (out);
+}
+
+PhotCode *PhotCode_PS1_DEV_2_To_Internal (PhotCode_PS1_DEV_2 *in, int Nvalues) {
+
+  int i;
+  PhotCode *out;
+
+  ALLOCATE (out, PhotCode, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C     = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].astromErrSys      = in[i].astromErrSys;
+    out[i].astromErrScale    = in[i].astromErrScale;
+    out[i].astromErrMagScale = in[i].astromErrMagScale;
+    out[i].photomErrSys      = in[i].photomErrSys;
+
+    out[i].photomPoorMask      = 0;
+    out[i].photomBadMask       = 0;
+    out[i].astromPoorMask      = 0;
+    out[i].astromBadMask       = 0;
+  }
+  return (out);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_3.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_3.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_3.c	(revision 22322)
@@ -0,0 +1,193 @@
+# include <dvo.h>
+
+/* convert PS1_DEV_3 formats to internal formats */
+
+// XXX EAM I am not yet ready to commit to a full PS1_DEV_3 release, but I am providing image and photcode conversion
+
+Image *Image_PS1_DEV_3_ToInternal (Image_PS1_DEV_3 *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 127); // out[128], in[128]
+    out[i].name[127] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID;
+    out[i].externID	    = in[i].externID;
+    out[i].sourceID	    = in[i].sourceID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+Image_PS1_DEV_3 *ImageInternalTo_PS1_DEV_3 (Image *in, int Nvalues) {
+
+  int i;
+  Image_PS1_DEV_3 *out;
+
+  ALLOCATE (out, Image_PS1_DEV_3, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 127); // out[128], in[128]
+    out[i].name[127] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID;
+    out[i].externID	    = in[i].externID;
+    out[i].sourceID	    = in[i].sourceID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+PhotCode_PS1_DEV_3 *PhotCode_Internal_To_PS1_DEV_3 (PhotCode *in, int Nvalues) {
+
+  int i;
+  PhotCode_PS1_DEV_3 *out;
+
+  ALLOCATE (out, PhotCode_PS1_DEV_3, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C     = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].astromErrSys      = in[i].astromErrSys;
+    out[i].astromErrScale    = in[i].astromErrScale;
+    out[i].astromErrMagScale = in[i].astromErrMagScale;
+    out[i].photomErrSys      = in[i].photomErrSys;
+
+    out[i].photomPoorMask      = in[i].photomPoorMask;
+    out[i].photomBadMask       = in[i].photomBadMask;
+    out[i].astromPoorMask      = in[i].astromPoorMask;
+    out[i].astromBadMask       = in[i].astromBadMask;
+  }
+  return (out);
+}
+
+PhotCode *PhotCode_PS1_DEV_3_To_Internal (PhotCode_PS1_DEV_3 *in, int Nvalues) {
+
+  int i;
+  PhotCode *out;
+
+  ALLOCATE (out, PhotCode, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C     = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].astromErrSys      = in[i].astromErrSys;
+    out[i].astromErrScale    = in[i].astromErrScale;
+    out[i].astromErrMagScale = in[i].astromErrMagScale;
+    out[i].photomErrSys      = in[i].photomErrSys;
+
+    out[i].photomPoorMask      = in[i].photomPoorMask;
+    out[i].photomBadMask       = in[i].photomBadMask;
+    out[i].astromPoorMask      = in[i].astromPoorMask;
+    out[i].astromBadMask       = in[i].astromBadMask;
+  }
+  return (out);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_elixir.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_elixir.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_elixir.c	(revision 22322)
@@ -0,0 +1,367 @@
+# include <dvo.h>
+
+/* convert elixir-format measures to internal measures */
+Measure *Measure_Elixir_ToInternal (Measure_Elixir *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR       = (in[i].dR      == NAN_S_SHORT) ? NAN : in[i].dR     * 0.01;
+    out[i].dD       = (in[i].dD      == NAN_S_SHORT) ? NAN : in[i].dD     * 0.01;
+    out[i].M        = (in[i].M       == NAN_S_SHORT) ? NAN : in[i].M      * 0.001;
+    out[i].dM       = (in[i].dM      == NAN_U_CHAR)  ? NAN : in[i].dM     * 0.001;
+    out[i].dt       = (in[i].dt      == NAN_S_SHORT) ? NAN : in[i].dt     * 0.001;
+    out[i].Mcal     = (in[i].Mcal    == NAN_S_SHORT) ? NAN : in[i].Mcal   * 0.001;
+
+    // 2008.02.26 : I've renamed Mgal to Map, and intend it to be used per aperture
+    // magnitudes.  Most uses of Mgal in the past were actually aperture (isophotal) mags
+    out[i].Map      = (in[i].Mgal    == NAN_S_SHORT) ? NAN : in[i].Mgal   * 0.001;
+    out[i].airmass  = (in[i].airmass == NAN_S_SHORT) ? NAN : in[i].airmass* 0.001;
+    out[i].FWx 	    = in[i].FWx;
+    out[i].FWy 	    = in[i].fwy * in[i].FWx * 0.01;
+    out[i].theta    = in[i].theta*(0x10000 / 0x100);
+    out[i].dophot   = in[i].dophot;
+    out[i].photcode = in[i].source;
+    out[i].t        = in[i].t;
+    out[i].averef   = in[i].averef;
+    out[i].dbFlags  = in[i].flags;
+    
+    /* these can be determined if needed / desired */
+    out[i].Xccd     = 0;
+    out[i].Yccd     = 0;
+    out[i].dXccd    = 0;
+    out[i].dYccd    = 0;
+
+    /* these do not have a corresponding value */
+    out[i].az        = 0;
+    out[i].stargal   = 0;
+    out[i].Sky       = 0;
+    out[i].dSky      = 0;
+    out[i].qPSF      = 0;
+    out[i].psfChisq  = 0;
+    out[i].crNsigma  = 0;
+    out[i].extNsigma = 0;
+    out[i].photFlags = 0;
+
+    /* XXX add these later */
+    out[i].detID = 0;
+    out[i].imageID = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to elixir-format measures */
+Measure_Elixir *MeasureInternalTo_Elixir (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_Elixir *out;
+
+  ALLOCATE (out, Measure_Elixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR       = isnan(in[i].dR     ) ? NAN_S_SHORT : in[i].dR      *  100.0;
+    out[i].dD       = isnan(in[i].dD     ) ? NAN_S_SHORT : in[i].dD      *  100.0;
+    out[i].M        = isnan(in[i].M      ) ? NAN_S_SHORT : in[i].M       * 1000.0;
+    out[i].dM       = isnan(in[i].dM     ) ? NAN_U_CHAR  : in[i].dM      * 1000.0;
+    out[i].dt       = isnan(in[i].dt     ) ? NAN_S_SHORT : in[i].dt      * 1000.0;
+    out[i].Mcal     = isnan(in[i].Mcal   ) ? NAN_S_SHORT : in[i].Mcal    * 1000.0;
+    out[i].Mgal     = isnan(in[i].Map    ) ? NAN_S_SHORT : in[i].Map     * 1000.0;
+    out[i].airmass  = isnan(in[i].airmass) ? NAN_S_SHORT : in[i].airmass * 1000.0;
+
+    if (out[i].M < 0) {
+	fprintf (stderr, ".");
+    }
+
+    out[i].FWx 	   = in[i].FWx;
+    out[i].fwy 	   = in[i].FWy * 100.0 / in[i].FWx;
+    out[i].theta   = in[i].theta*(0x100/ 0x10000);
+    out[i].dophot  = in[i].dophot;
+    out[i].source  = in[i].photcode;
+    out[i].t       = in[i].t;
+    out[i].averef  = in[i].averef;
+    out[i].flags   = in[i].dbFlags;
+  }
+  return (out);
+}
+
+/* convert elixir-format averages to internal averages */
+Average *Average_Elixir_ToInternal (Average_Elixir *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+  ALLOCATE (*primary, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R       	 = in[i].R;      
+    out[i].D       	 = in[i].D;      
+    out[i].Xp      	 = in[i].Xp;     
+    out[i].Nmeasure 	 = in[i].Nm;     
+    out[i].Nmissing 	 = in[i].Nn;     
+    out[i].code     	 = in[i].code;   
+    out[i].measureOffset = in[i].offset; 
+    out[i].missingOffset = in[i].missing;
+
+    /* these don't exist in Elixir */
+    out[i].dR      = 0;
+    out[i].dD      = 0;
+    out[i].uR      = 0;
+    out[i].uD      = 0;
+    out[i].duR     = 0;
+    out[i].duD     = 0;
+    out[i].P       = 0;
+    out[i].dP      = 0;
+
+    /* XXX add these later */
+    out[i].objID   = 0;
+    out[i].catID   = 0;
+
+    primary[0][i].M     = (in[i].M  == NAN_S_SHORT) ? NAN : in[i].M  * 0.001;      
+    primary[0][i].dM    = (in[i].dM == NAN_S_SHORT) ? NAN : in[i].dM * 0.001;      
+    primary[0][i].Xm    = in[i].Xm;     
+    primary[0][i].Ncode = 0;     
+    primary[0][i].Nused = 0;
+  }
+  return (out);
+}
+
+/* convert internal averages to elixir-format averages */
+Average_Elixir *AverageInternalTo_Elixir (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_Elixir *out;
+
+  ALLOCATE (out, Average_Elixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R       = in[i].R;      
+    out[i].D       = in[i].D;      
+    out[i].Xp      = in[i].Xp;     
+    out[i].Nm      = in[i].Nmeasure;     
+    out[i].Nn      = in[i].Nmissing;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].measureOffset; 
+    out[i].missing = in[i].missingOffset;
+
+    out[i].M       = isnan(primary[i].M)  ? NAN_S_SHORT : primary[i].M   * 1000.0;
+    out[i].dM      = isnan(primary[i].dM) ? NAN_S_SHORT : primary[i].dM  * 1000.0;
+    out[i].Xm      = primary[i].Xm;     
+  }
+  return (out);
+}
+
+/* convert elixir-format secfilts to internal secfilts */
+SecFilt *SecFilt_Elixir_ToInternal (SecFilt_Elixir *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = (in[i].M  == NAN_S_SHORT) ? NAN : in[i].M   * 0.001;
+    out[i].dM    = (in[i].dM == NAN_S_SHORT) ? NAN : in[i].dM  * 0.001;
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = 0;
+    out[i].Nused = 0;
+  }
+  return (out);
+}
+
+/* convert internal secfilts to elixir-format secfilts */
+SecFilt_Elixir *SecFiltInternalTo_Elixir (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_Elixir *out;
+
+  ALLOCATE (out, SecFilt_Elixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = isnan(in[i].M)  ? NAN_S_SHORT : in[i].M   * 1000.0;
+    out[i].dM    = isnan(in[i].dM) ? NAN_S_SHORT : in[i].dM  * 1000.0;
+    out[i].Xm    = in[i].Xm;     
+  }
+  return (out);
+}
+
+/* convert elixir-format images to internal images */
+Image *Image_Elixir_ToInternal (Image_Elixir *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 31); // in[32], out[128]
+    out[i].name[31] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].secz    	    = (in[i].secz     == NAN_S_SHORT) ? NAN : in[i].secz     * 0.001;
+    out[i].apmifit  	    = (in[i].apmifit  == NAN_S_SHORT) ? NAN : in[i].apmifit  * 0.001;
+    out[i].dapmifit  	    = (in[i].dapmifit == NAN_S_SHORT) ? NAN : in[i].dapmifit * 0.001;
+    out[i].Mcal   	    = (in[i].Mcal     == NAN_S_SHORT) ? NAN : in[i].Mcal     * 0.001;
+    out[i].dMcal   	    = (in[i].dMcal    == NAN_S_SHORT) ? NAN : in[i].dMcal    * 0.001;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].source;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+
+    /* XXX add these later */
+    out[i].imageID = 0;
+
+    // not available in ELIXIR
+    out[i].sidtime  	    = NAN;
+    out[i].latitude  	    = NAN;
+  }
+  return (out);
+}
+
+/* convert internal images to elixir-format images */
+Image_Elixir *ImageInternalTo_Elixir (Image *in, int Nvalues) {
+
+  int i;
+  Image_Elixir *out;
+
+  ALLOCATE (out, Image_Elixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[128]
+    out[i].name[31] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].secz    	    = isnan(in[i].secz    ) ? NAN_S_SHORT : in[i].secz     * 1000.0;
+    out[i].apmifit    	    = isnan(in[i].apmifit ) ? NAN_S_SHORT : in[i].apmifit  * 1000.0;
+    out[i].dapmifit    	    = isnan(in[i].dapmifit) ? NAN_S_SHORT : in[i].dapmifit * 1000.0;
+    out[i].Mcal    	    = isnan(in[i].Mcal    ) ? NAN_S_SHORT : in[i].Mcal     * 1000.0;
+    out[i].dMcal    	    = isnan(in[i].dMcal   ) ? NAN_S_SHORT : in[i].dMcal    * 1000.0;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].source   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+PhotCode_Elixir *PhotCode_Internal_To_Elixir (PhotCode *in, int Nvalues) {
+
+  int i;
+  PhotCode_Elixir *out;
+
+  ALLOCATE (out, PhotCode_Elixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C  	 = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+  }
+  return (out);
+}
+
+PhotCode *PhotCode_Elixir_To_Internal (PhotCode_Elixir *in, int Nvalues) {
+
+  int i;
+  PhotCode *out;
+
+  ALLOCATE (out, PhotCode, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C  	 = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    // not defined in Elixir:
+    out[i].astromErrSys      = 0.0;
+    out[i].astromErrScale    = 0.0;
+    out[i].astromErrMagScale = 0.0;
+    out[i].photomErrSys      = 0.0;
+
+    out[i].photomPoorMask      = 0;
+    out[i].photomBadMask       = 0;
+    out[i].astromPoorMask      = 0;
+    out[i].astromBadMask       = 0;
+  }
+  return (out);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_loneos.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_loneos.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_loneos.c	(revision 22322)
@@ -0,0 +1,292 @@
+# include <dvo.h>
+
+/* convert loneos-format measures to internal measures */
+Measure *Measure_Loneos_ToInternal (Measure_Loneos *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR        = (in[i].dR      == NAN_S_SHORT) ? NAN : in[i].dR     * 0.01;
+    out[i].dD        = (in[i].dD      == NAN_S_SHORT) ? NAN : in[i].dD     * 0.01;
+    out[i].M         = (in[i].M       == NAN_S_SHORT) ? NAN : in[i].M      * 0.001;
+    out[i].dM        = (in[i].dM      == NAN_U_CHAR)  ? NAN : in[i].dM     * 0.001;
+    out[i].Mcal      = (in[i].Mcal    == NAN_S_SHORT) ? NAN : in[i].Mcal   * 0.001;
+    out[i].dophot    = in[i].dophot;
+    out[i].photcode  = in[i].source;
+    out[i].t         = in[i].t;
+
+    /* flags and averef are now split */
+    out[i].averef    =  in[i].averef & 0x00ffffff;
+    out[i].dbFlags   = (in[i].averef & 0xff000000) >> 24;
+
+    /* these values don't exist in the Loneos format */
+
+    // 2008.02.26 : I've renamed Mgal to Map, and intend it to be used per aperture
+    // magnitudes.  Most uses of Mgal in the past were actually aperture (isophotal) mags
+    out[i].Map       = out[i].M;
+    out[i].dt        = 0;
+    out[i].airmass   = 0;
+    out[i].az        = 0;
+    out[i].FWx 	     = 0;
+    out[i].FWy 	     = 0;
+    out[i].theta     = 0;
+    out[i].stargal   = 0;
+    out[i].Sky       = 0;
+    out[i].dSky      = 0;
+    out[i].qPSF      = 0;
+    out[i].psfChisq  = 0;
+    out[i].crNsigma  = 0;
+    out[i].extNsigma = 0;
+    out[i].photFlags = 0;
+
+    /* these can be determined if needed / desired */
+    out[i].Xccd     = 0;
+    out[i].Yccd     = 0;
+    out[i].dXccd    = 0;
+    out[i].dYccd    = 0;
+
+    /* XXX add these later */
+    out[i].detID = 0;
+    out[i].imageID = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to loneos-format measures */
+Measure_Loneos *MeasureInternalTo_Loneos (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_Loneos *out;
+
+  ALLOCATE (out, Measure_Loneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR     = isnan(in[i].dR     ) ? NAN_S_SHORT : in[i].dR      *  100.0;
+    out[i].dD     = isnan(in[i].dD     ) ? NAN_S_SHORT : in[i].dD      *  100.0;
+    out[i].M      = isnan(in[i].M      ) ? NAN_S_SHORT : in[i].M       * 1000.0;
+    out[i].dM     = isnan(in[i].dM     ) ? NAN_U_CHAR  : in[i].dM      * 1000.0;
+    out[i].Mcal   = isnan(in[i].Mcal   ) ? NAN_S_SHORT : in[i].Mcal    * 1000.0;
+    out[i].dophot = in[i].dophot;
+    out[i].source = in[i].photcode;
+    out[i].t      = in[i].t;
+
+    /* flags and averef are merged in Loneos */
+    out[i].averef =  (in[i].averef & 0x00ffffff) | (in[i].dbFlags << 24);
+  }
+  return (out);
+}
+
+/* convert loneos-format averages to internal averages */
+Average *Average_Loneos_ToInternal (Average_Loneos *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+  ALLOCATE (*primary, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R       	 = in[i].R;      
+    out[i].D       	 = in[i].D;      
+    out[i].Xp      	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nm;     
+    out[i].Nmissing      = in[i].Nn;     
+    out[i].code    	 = in[i].code;   
+    out[i].measureOffset = in[i].offset; 
+    out[i].missingOffset = in[i].missing;
+
+    /* these don't exist in Loneos */
+    out[i].dR      = 0;
+    out[i].dD      = 0;
+    out[i].uR      = 0;
+    out[i].uD      = 0;
+    out[i].duR     = 0;
+    out[i].duD     = 0;
+    out[i].P       = 0;
+    out[i].dP      = 0;
+
+    /* XXX add these later */
+    out[i].objID   = 0;
+    out[i].catID   = 0;
+
+    primary[0][i].M  = (in[i].M  == NAN_S_SHORT) ? NAN : in[i].M  * 0.001;      
+    primary[0][i].Xm = in[i].Xm;     
+    primary[0][i].dM = NAN;
+    primary[0][i].Ncode = 0;     
+    primary[0][i].Nused = 0;
+  }
+  return (out);
+}
+
+/* convert internal averages to loneos-format averages */
+Average_Loneos *AverageInternalTo_Loneos (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_Loneos *out;
+
+  ALLOCATE (out, Average_Loneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R       = in[i].R;      
+    out[i].D       = in[i].D;      
+    out[i].Xp      = in[i].Xp;     
+    out[i].Nm      = in[i].Nmeasure;     
+    out[i].Nn      = in[i].Nmissing;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].measureOffset; 
+    out[i].missing = in[i].missingOffset;
+
+    out[i].M       = isnan(primary[i].M)  ? NAN_S_SHORT : primary[i].M   * 1000.0;
+    out[i].Xm      = primary[i].Xm;     
+  }
+  return (out);
+}
+
+/* convert loneos-format secfilts to internal secfilts */
+SecFilt *SecFilt_Loneos_ToInternal (SecFilt_Loneos *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = (in[i].M  == NAN_S_SHORT) ? NAN : in[i].M * 0.001;
+    out[i].Xm    = in[i].Xm;      
+    out[i].dM    = NAN;
+    out[i].Ncode = 0;
+    out[i].Nused = 0;
+  }
+  return (out);
+}
+
+/* convert internal secfilts to loneos-format secfilts */
+SecFilt_Loneos *SecFiltInternalTo_Loneos (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_Loneos *out;
+
+  ALLOCATE (out, SecFilt_Loneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M    = isnan(in[i].M)  ? NAN_S_SHORT : in[i].M * 1000.0;
+    out[i].Xm   = in[i].Xm;      
+  }
+  return (out);
+}
+
+/* convert loneos-format images to internal images */
+Image *Image_Loneos_ToInternal (Image_Loneos *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 31); // in[32], out[128]
+    out[i].name[31] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+
+    out[i].secz    	    = (in[i].secz     == NAN_S_SHORT) ? NAN : in[i].secz     * 0.001;
+    out[i].apmifit  	    = (in[i].apmifit  == NAN_S_SHORT) ? NAN : in[i].apmifit  * 0.001;
+    out[i].dapmifit  	    = (in[i].dapmifit == NAN_S_SHORT) ? NAN : in[i].dapmifit * 0.001;
+    out[i].Mcal   	    = (in[i].Mcal     == NAN_S_SHORT) ? NAN : in[i].Mcal     * 0.001;
+    out[i].dMcal   	    = (in[i].dMcal    == NAN_S_SHORT) ? NAN : in[i].dMcal    * 0.001;
+
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].source;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+
+    /* XXX add these later */
+    out[i].imageID = 0;
+  }
+  return (out);
+}
+
+/* convert internal images to loneos-format images */
+Image_Loneos *ImageInternalTo_Loneos (Image *in, int Nvalues) {
+
+  int i;
+  Image_Loneos *out;
+
+  ALLOCATE (out, Image_Loneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[128]
+    out[i].name[31] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+
+    out[i].secz    	    = isnan(in[i].secz    ) ? NAN_S_SHORT : in[i].secz     * 1000.0;
+    out[i].apmifit    	    = isnan(in[i].apmifit ) ? NAN_S_SHORT : in[i].apmifit  * 1000.0;
+    out[i].dapmifit    	    = isnan(in[i].dapmifit) ? NAN_S_SHORT : in[i].dapmifit * 1000.0;
+    out[i].Mcal    	    = isnan(in[i].Mcal    ) ? NAN_S_SHORT : in[i].Mcal     * 1000.0;
+    out[i].dMcal    	    = isnan(in[i].dMcal   ) ? NAN_S_SHORT : in[i].dMcal    * 1000.0;
+
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].source   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_0.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_0.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_0.c	(revision 22322)
@@ -0,0 +1,307 @@
+# include <dvo.h>
+
+/* convert panstarrs-format measures to internal measures */
+Measure *Measure_Panstarrs_DEV_0_ToInternal (Measure_Panstarrs_DEV_0 *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].dM         = in[i].dM;
+    out[i].Mcal       = in[i].Mcal;
+
+    // 2008.02.26 : I've renamed Mgal to Map, and intend it to be used per aperture
+    // magnitudes.  Most uses of Mgal in the past were actually aperture (isophotal) mags
+    out[i].Map        = in[i].Mgal;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].dt         = in[i].dt;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].dophot     = in[i].dophot;
+    out[i].photcode   = in[i].photcode;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].dbFlags    = in[i].flags;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].stargal    = in[i].stargal;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].qPSF       = (in[i].qPSF == NAN_S_SHORT) ? NAN : in[i].qPSF;
+    out[i].detID      = in[i].detID_lo;
+    out[i].imageID    = in[i].imageID_lo;
+
+    // these don't have a correspondence
+    out[i].psfChisq  = 0;
+    out[i].crNsigma  = 0;
+    out[i].extNsigma = 0;
+    out[i].photFlags = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to panstarrs-format measures */
+Measure_Panstarrs_DEV_0 *MeasureInternalTo_Panstarrs_DEV_0 (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_Panstarrs_DEV_0 *out;
+
+  ALLOCATE (out, Measure_Panstarrs_DEV_0, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].dM         = in[i].dM;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Mgal       = in[i].Map;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].dt         = in[i].dt;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].dophot     = in[i].dophot;
+    out[i].photcode   = in[i].photcode;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].flags      = in[i].dbFlags;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].stargal    = in[i].stargal;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].qPSF       = isnan(in[i].qPSF) ? NAN_S_SHORT : in[i].qPSF;
+    out[i].detID_hi   = 0;
+    out[i].detID_lo   = in[i].detID;
+    out[i].imageID_hi = 0;
+    out[i].imageID_lo = in[i].imageID;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format averages to internal averages */
+// 'primary is needed to conform with the API for Loneos and Elixir, but is not used
+Average *Average_Panstarrs_DEV_0_ToInternal (Average_Panstarrs_DEV_0 *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        	 = in[i].R;      
+    out[i].D        	 = in[i].D;      
+    out[i].Xp       	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nm;     
+    out[i].Nmissing      = in[i].Nn;     
+    out[i].code          = in[i].code;   
+    out[i].measureOffset = in[i].offset; 
+    out[i].missingOffset = in[i].missing;
+    out[i].dR       	 = in[i].dR;
+    out[i].dD       	 = in[i].dD;
+    out[i].uR       	 = in[i].uR;
+    out[i].uD       	 = in[i].uD;
+    out[i].duR      	 = in[i].duR;
+    out[i].duD      	 = in[i].duD;
+    out[i].P        	 = in[i].P;
+    out[i].dP       	 = in[i].dP;
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+  }
+  return (out);
+}
+
+/* convert internal averages to panstarrs-format averages */
+// 'primary is needed to conform with the API for Loneos and Elixir, but is not used
+Average_Panstarrs_DEV_0 *AverageInternalTo_Panstarrs_DEV_0 (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_Panstarrs_DEV_0 *out;
+
+  ALLOCATE (out, Average_Panstarrs_DEV_0, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        = in[i].R;      
+    out[i].D        = in[i].D;      
+    out[i].Xp       = in[i].Xp;     
+    out[i].Nm       = in[i].Nmeasure;     
+    out[i].Nn       = in[i].Nmissing;     
+    out[i].code     = in[i].code;   
+    out[i].offset   = in[i].measureOffset; 
+    out[i].missing  = in[i].missingOffset;
+    out[i].dR       = in[i].dR;
+    out[i].dD       = in[i].dD;
+    out[i].uR       = in[i].uR;
+    out[i].uD       = in[i].uD;
+    out[i].duR      = in[i].duR;
+    out[i].duD      = in[i].duD;
+    out[i].P        = in[i].P;
+    out[i].dP       = in[i].dP;
+    out[i].objID    = in[i].objID;
+    out[i].catID    = in[i].catID;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format secfilts to internal secfilts */
+SecFilt *SecFilt_Panstarrs_DEV_0_ToInternal (SecFilt_Panstarrs_DEV_0 *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+ }
+  return (out);
+}
+
+/* convert internal secfilts to panstarrs-format secfilts */
+SecFilt_Panstarrs_DEV_0 *SecFiltInternalTo_Panstarrs_DEV_0 (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_Panstarrs_DEV_0 *out;
+
+  ALLOCATE (out, SecFilt_Panstarrs_DEV_0, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format images to internal images */
+Image *Image_Panstarrs_DEV_0_ToInternal (Image_Panstarrs_DEV_0 *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 31); // out[128], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID_lo;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+/* convert internal images to panstarrs-format images */
+Image_Panstarrs_DEV_0 *ImageInternalTo_Panstarrs_DEV_0 (Image *in, int Nvalues) {
+
+  int i;
+  Image_Panstarrs_DEV_0 *out;
+
+  ALLOCATE (out, Image_Panstarrs_DEV_0, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[128]
+    out[i].name[31] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID_hi	    = 0;
+    out[i].imageID_lo	    = in[i].imageID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_1.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_1.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_1.c	(revision 22322)
@@ -0,0 +1,307 @@
+# include <dvo.h>
+
+/* convert panstarrs-format measures to internal measures */
+Measure *Measure_Panstarrs_DEV_1_ToInternal (Measure_Panstarrs_DEV_1 *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].dM         = in[i].dM;
+    out[i].Mcal       = in[i].Mcal;
+
+    // 2008.02.26 : I've renamed Mgal to Map, and intend it to be used per aperture
+    // magnitudes.  Most uses of Mgal in the past were actually aperture (isophotal) mags
+    out[i].Map        = in[i].Mgal;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].dt         = in[i].dt;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].dophot     = in[i].dophot;
+    out[i].photcode   = in[i].photcode;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].dbFlags    = in[i].flags;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].stargal    = in[i].stargal;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].qPSF       = (in[i].qPSF == NAN_S_SHORT) ? NAN : in[i].qPSF;
+    out[i].detID      = in[i].detID_lo;
+    out[i].imageID    = in[i].imageID_lo;
+
+    // these don't have a correspondence
+    out[i].psfChisq  = 0;
+    out[i].crNsigma  = 0;
+    out[i].extNsigma = 0;
+    out[i].photFlags = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to panstarrs-format measures */
+Measure_Panstarrs_DEV_1 *MeasureInternalTo_Panstarrs_DEV_1 (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_Panstarrs_DEV_1 *out;
+
+  ALLOCATE (out, Measure_Panstarrs_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].dM         = in[i].dM;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Mgal       = in[i].Map;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].dt         = in[i].dt;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].dophot     = in[i].dophot;
+    out[i].photcode   = in[i].photcode;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].flags      = in[i].dbFlags;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].stargal    = in[i].stargal;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].qPSF       = isnan(in[i].qPSF) ? NAN_S_SHORT : in[i].qPSF;
+    out[i].detID_hi   = 0;
+    out[i].detID_lo   = in[i].detID;
+    out[i].imageID_hi = 0;
+    out[i].imageID_lo = in[i].imageID;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format averages to internal averages */
+// 'primary is needed to conform with the API for Loneos and Elixir, but is not used
+Average *Average_Panstarrs_DEV_1_ToInternal (Average_Panstarrs_DEV_1 *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        	 = in[i].R;      
+    out[i].D        	 = in[i].D;      
+    out[i].Xp       	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nm;     
+    out[i].Nmissing      = in[i].Nn;     
+    out[i].code     	 = in[i].code;   
+    out[i].measureOffset = in[i].offset; 
+    out[i].missingOffset = in[i].missing;
+    out[i].dR       	 = in[i].dR;
+    out[i].dD       	 = in[i].dD;
+    out[i].uR       	 = in[i].uR;
+    out[i].uD       	 = in[i].uD;
+    out[i].duR      	 = in[i].duR;
+    out[i].duD      	 = in[i].duD;
+    out[i].P        	 = in[i].P;
+    out[i].dP       	 = in[i].dP;
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+  }
+  return (out);
+}
+
+/* convert internal averages to panstarrs-format averages */
+// 'primary is needed to conform with the API for Loneos and Elixir, but is not used
+Average_Panstarrs_DEV_1 *AverageInternalTo_Panstarrs_DEV_1 (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_Panstarrs_DEV_1 *out;
+
+  ALLOCATE (out, Average_Panstarrs_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        = in[i].R;      
+    out[i].D        = in[i].D;      
+    out[i].Xp       = in[i].Xp;     
+    out[i].Nm       = in[i].Nmeasure;     
+    out[i].Nn       = in[i].Nmissing;     
+    out[i].code     = in[i].code;   
+    out[i].offset   = in[i].measureOffset; 
+    out[i].missing  = in[i].missingOffset;
+    out[i].dR       = in[i].dR;
+    out[i].dD       = in[i].dD;
+    out[i].uR       = in[i].uR;
+    out[i].uD       = in[i].uD;
+    out[i].duR      = in[i].duR;
+    out[i].duD      = in[i].duD;
+    out[i].P        = in[i].P;
+    out[i].dP       = in[i].dP;
+    out[i].objID    = in[i].objID;
+    out[i].catID    = in[i].catID;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format secfilts to internal secfilts */
+SecFilt *SecFilt_Panstarrs_DEV_1_ToInternal (SecFilt_Panstarrs_DEV_1 *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+ }
+  return (out);
+}
+
+/* convert internal secfilts to panstarrs-format secfilts */
+SecFilt_Panstarrs_DEV_1 *SecFiltInternalTo_Panstarrs_DEV_1 (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_Panstarrs_DEV_1 *out;
+
+  ALLOCATE (out, SecFilt_Panstarrs_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format images to internal images */
+Image *Image_Panstarrs_DEV_1_ToInternal (Image_Panstarrs_DEV_1 *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 63); // in[64], out[128]
+    out[i].name[63] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID_lo;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+/* convert internal images to panstarrs-format images */
+Image_Panstarrs_DEV_1 *ImageInternalTo_Panstarrs_DEV_1 (Image *in, int Nvalues) {
+
+  int i;
+  Image_Panstarrs_DEV_1 *out;
+
+  ALLOCATE (out, Image_Panstarrs_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+
+    strncpy (out[i].name, in[i].name, 63); // in[128], out[64]
+    out[i].name[63] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID_hi	    = 0;
+    out[i].imageID_lo	    = in[i].imageID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_image.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_image.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_image.c	(revision 22322)
@@ -0,0 +1,200 @@
+# include <dvo.h>
+
+/* lock the image table */
+int dvo_image_lock (FITS_DB *db, char *filename, double timeout, int lockstate) {
+
+  /* lock the image catalog */
+  if (!check_file_access (filename, FALSE, TRUE)) return (FALSE);
+
+  db[0].lockstate = lockstate;
+  db[0].timeout   = timeout;
+  gfits_db_init (db);
+
+  if (!gfits_db_lock (db, filename)) {
+    fprintf (stderr, "can't lock image catalog\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int dvo_image_unlock (FITS_DB *db) {
+
+  mode_t mode;
+
+  gfits_db_close (db);
+
+  /* force permissions to 666 */
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (db[0].filename, mode);
+  return (TRUE);
+}
+
+/* determine db mode (RAW/MEF) and read the complete table */
+int dvo_image_load (FITS_DB *db, int VERBOSE, int FORCE_READ) {
+
+  int Naxis, Nread, status;
+  char buffer[241];
+
+  /* database name must be set first */
+  if (db == NULL) return (FALSE);
+  if (db[0].f == NULL) return (FALSE);
+
+  /* test for NAXIS == 2 in line 3 */
+  bzero (buffer, 241);
+  fseek (db[0].f, 0, SEEK_SET);
+  Nread = fread (buffer, 1, 240, db[0].f);
+  if (Nread != 240) {
+    if (VERBOSE) fprintf (stderr, "can't determine image db mode\n");
+    return (FALSE);
+  }
+  Nread = sscanf (&buffer[160], "NAXIS   = %d", &Naxis);
+  if (Nread != 1) {
+    if (VERBOSE) fprintf (stderr, "can't determine image db mode\n");
+    return (FALSE);
+  }
+  fseek (db[0].f, 0, SEEK_SET);
+
+  /* default values */
+  db[0].mode = DVO_MODE_MEF;
+  db[0].format = DVO_FORMAT_UNDEF;
+  if (Naxis == 2) db[0].mode = DVO_MODE_RAW; /* image table can only be RAW or MEF */
+
+  switch (db[0].mode) {
+    case DVO_MODE_MEF:
+      if (VERBOSE) fprintf (stderr, "reading images (mode DVO_MODE_MEF)\n");
+      status = gfits_db_load (db);
+      break;
+    case DVO_MODE_RAW:
+      if (VERBOSE) fprintf (stderr, "reading images (mode DVO_MODE_RAW)\n");
+      status = dvo_image_load_raw (db, VERBOSE, FORCE_READ);
+      break;
+    default:
+      fprintf (stderr, "error getting image mode\n");
+      exit (2);
+  }
+  if (!status) return (FALSE);
+  FtableToImage (&db[0].ftable, &db[0].theader, &db[0].format);
+  db[0].swapped = TRUE;  /* table has internal byte-order */
+  return (TRUE);
+}
+
+/* write the complete image table to disk in the requested mode */
+int dvo_image_save (FITS_DB *db, int VERBOSE) {
+
+  int status;
+
+  /* convert from internal to requested external format */
+  ImageToFtable (&db[0].ftable, &db[0].theader, db[0].format);
+  db[0].swapped = FALSE;
+
+  /* write data in appropriate mode */
+  switch (db[0].mode) {
+    case DVO_MODE_MEF:
+    case DVO_MODE_SPLIT:
+      status = gfits_db_save (db);
+      break;
+    case DVO_MODE_RAW:
+      status = dvo_image_save_raw (db, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "error writing image mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+int dvo_image_addrows (FITS_DB *db, Image *new, int Nnew) {
+
+  int Nimages;
+
+  /* adjust header */
+  Nimages = 0;
+  gfits_scan (&db[0].header, "NIMAGES", "%d", 1, &Nimages);
+  Nimages += Nnew;
+  gfits_modify (&db[0].header, "NIMAGES", "%d", 1, Nimages);
+
+  gfits_table_to_vtable (&db[0].ftable, &db[0].vtable, 0, 0);
+  gfits_vadd_rows (&db[0].vtable, (char *) new, Nnew, sizeof(Image));
+
+  /* check that primary header and table header agree */
+  if (Nimages != db[0].theader.Naxis[1]) {
+    fprintf (stderr, "header / table length mismatch!\n");
+  }
+  return (TRUE);
+}  
+
+/* write the vtable data to disk in the requested mode */
+int dvo_image_update (FITS_DB *db, int VERBOSE) {
+
+  int status;
+
+  /* convert from internal to requested external format */
+  /* theader is modified to match output format */
+  ImageToVtable (&db[0].vtable, &db[0].theader, db[0].format);
+  db[0].swapped = FALSE;
+
+  /* write data in appropriate mode */
+  switch (db[0].mode) {
+    case DVO_MODE_MEF:
+    case DVO_MODE_SPLIT:
+      status = gfits_db_update (db);
+      break;
+    case DVO_MODE_RAW:
+      status = dvo_image_update_raw (db, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "error writing image mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+void dvo_image_create (FITS_DB *db, double ZeroPoint) {
+
+  /* create new header */
+  gfits_init_header (&db[0].header);
+
+  /* check the mode */
+  switch (db[0].mode) {
+    case DVO_MODE_RAW:
+      /* make header a fake image */
+      db[0].header.bitpix   = 16;
+      db[0].header.Naxes    = 2;
+      db[0].header.Naxis[0] = 1;
+      db[0].header.Naxis[1] = 1;
+      gfits_create_header (&db[0].header);
+      break;
+    case DVO_MODE_MEF:
+    case DVO_MODE_SPLIT:
+      db[0].header.extend   = TRUE;
+      gfits_create_header (&db[0].header);
+      db[0].mode = DVO_MODE_MEF;
+      break;
+    default:
+      fprintf (stderr, "invalid output catalog mode\n");
+      exit (1);
+  }
+
+  /* check the format */
+  if (db[0].format == DVO_FORMAT_UNDEF) {
+    fprintf (stderr, "invalid output catalog format\n");
+    exit (1);
+  }
+
+  gfits_create_matrix (&db[0].header, &db[0].matrix);
+  gfits_table_set_Image (&db[0].ftable, NULL, 0);
+
+  gfits_modify (&db[0].header, "NIMAGES", "%d", 1, 0);
+  gfits_modify (&db[0].header, "ZERO_PT", "%lf", 1, ZeroPoint);
+
+  if (db[0].format == DVO_FORMAT_INTERNAL)  	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "INTERNAL");
+  if (db[0].format == DVO_FORMAT_LONEOS)    	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "LONEOS");
+  if (db[0].format == DVO_FORMAT_ELIXIR)    	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "ELIXIR");
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_0) gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PANSTARRS_DEV_0");
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_1) gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PANSTARRS_DEV_1");
+  if (db[0].format == DVO_FORMAT_PS1_DEV_1)       gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_DEV_1");
+  if (db[0].format == DVO_FORMAT_PS1_DEV_2)       gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_DEV_2");
+  if (db[0].format == DVO_FORMAT_PS1_DEV_3)       gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_DEV_3");
+  
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_image_raw.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_image_raw.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_image_raw.c	(revision 22322)
@@ -0,0 +1,157 @@
+# include <dvo.h>
+
+/* the old Image.dat files used a fake FITS header defining a finite data block
+ * this function reads in the header and data, and creates an ftable and theader to match
+ */
+
+int dvo_image_load_raw (FITS_DB *db, int VERBOSE, int FORCE_READ) {
+
+  int Nimage, Ndata, size, ImageSize, nbytes, Nbytes;
+  struct stat filestatus;
+  char format[80], telescope[80];
+
+  /* read fits header from file - return FALSE on error */
+  if (!gfits_fread_header (db[0].f, &db[0].header)) {
+    fprintf (stderr, "can't read primary header\n"); 
+    return (FALSE);
+  }
+
+  /* determine image table format */
+  db[0].format = DVO_FORMAT_UNDEF;
+  if (gfits_scan (&db[0].header, "FORMAT",  "%s", 1, format)) {
+    db[0].format = dvo_catalog_catformat (format);
+    if (db[0].format != DVO_FORMAT_UNDEF) goto got_format;
+  }
+  if (gfits_scan (&db[0].header, "TELESCOP",  "%s", 1, telescope)) {
+    if (!strncmp (telescope, "LONEOS", strlen("LONEOS"))) {
+      db[0].format = DVO_FORMAT_LONEOS; // special case for LONEOS
+      goto got_format;
+    }
+    if (!strncmp (telescope, "1.3m McGraw-Hill", strlen("1.3m McGraw-Hill"))) {
+      db[0].format = DVO_FORMAT_ELIXIR; // special case for ELIXIR
+      goto got_format;
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "cannot determine image table format\n");
+  return (FALSE);
+
+got_format:
+  /* find number of images */
+  Nimage = 0;
+  gfits_scan (&db[0].header, "NIMAGES", "%d", 1, &Nimage);
+  if (stat (db[0].filename, &filestatus) == -1) {
+    if (VERBOSE) fprintf (stderr, "ERROR: failed to get status of image catalog\n");
+    exit (1);
+  }
+
+  /* get datatype size */
+  ImageSize = 0;
+  if (db[0].format == DVO_FORMAT_INTERNAL)  	  ImageSize = sizeof(Image);
+  if (db[0].format == DVO_FORMAT_LONEOS)    	  ImageSize = sizeof(Image_Loneos);
+  if (db[0].format == DVO_FORMAT_ELIXIR)    	  ImageSize = sizeof(Image_Elixir);
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_0) ImageSize = sizeof(Image_Panstarrs_DEV_0);
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_1) ImageSize = sizeof(Image_Panstarrs_DEV_1);
+  if (db[0].format == DVO_FORMAT_PS1_DEV_1)       ImageSize = sizeof(Image_PS1_DEV_1);
+  if (db[0].format == DVO_FORMAT_PS1_DEV_2)       ImageSize = sizeof(Image_PS1_DEV_2);
+
+  /* check that filesize makes sense */
+  size = Nimage*ImageSize + db[0].header.size;
+  if (size != filestatus.st_size) {
+    Ndata = (filestatus.st_size - db[0].header.size) / ImageSize;
+    if (VERBOSE) fprintf (stderr, "ERROR: image catalog has inconsistent size\n");
+    if (VERBOSE) fprintf (stderr, "header: %d, data: %d\n", Nimage, Ndata);
+    if (!FORCE_READ) exit (1);
+    Nimage = Ndata;
+  } 
+
+  /* create a dummy ftable, theader set for this data */
+  /* (original table has NAXIS = 2, change to 0) */
+  /* gfits_modify (&db[0].header, "NAXIS", "%d", 1, 0); */
+  gfits_create_matrix (&db[0].header, &db[0].matrix);
+  db[0].ftable.header = &db[0].theader;
+
+  if (db[0].format == DVO_FORMAT_INTERNAL)  	  gfits_table_mkheader_Image (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_LONEOS)    	  gfits_table_mkheader_Image_Loneos (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_ELIXIR)    	  gfits_table_mkheader_Image_Elixir (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_0) gfits_table_mkheader_Image_Panstarrs_DEV_0 (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_1) gfits_table_mkheader_Image_Panstarrs_DEV_1 (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_PS1_DEV_1)       gfits_table_mkheader_Image_PS1_DEV_1 (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_PS1_DEV_2)       gfits_table_mkheader_Image_PS1_DEV_2 (&db[0].theader);
+    
+  /* read data from file */
+  Nbytes = ImageSize*Nimage;
+  ALLOCATE (db[0].ftable.buffer, char, Nbytes);
+  nbytes = fread (db[0].ftable.buffer, 1, Nbytes, db[0].f);
+  if (nbytes != Nbytes) {
+    if (VERBOSE) fprintf (stderr, "ERROR: problem loading image catalog\n");
+    exit (1);
+  } 
+
+  gfits_modify (&db[0].theader, "NAXIS2", "%d", 1, Nimage);
+  db[0].theader.Naxis[1] = Nimage;
+  db[0].ftable.size = gfits_data_size (&db[0].theader);
+  db[0].swapped = FALSE;  /* table does not have internal byte-order */
+  return (TRUE);
+}
+
+/* write out image db elements from vtable */
+int dvo_image_update_raw (FITS_DB *db, int VERBOSE) {
+
+  int i, Nx, Ny, Nrow, Nbytes;
+  int status, start, offset;
+  int *row;
+
+  if (VERBOSE) fprintf (stderr, "writing out %d images\n", db[0].vtable.Nrow);
+
+  /* position to start of file */
+  Fseek (db[0].f, 0, SEEK_SET);
+  
+  /* write out complete header (no check on disk size?) */
+  status = fwrite (db[0].header.buffer, 1, db[0].header.size, db[0].f);
+  if (status != db[0].header.size) {
+    fprintf (stderr, "ERROR: failed writing data to image header\n");
+    exit (1);
+  }
+
+  /** this code is identical to gfits_fwrite_vtable, except without padding */
+  Nrow = db[0].vtable.Nrow;
+  row = db[0].vtable.row;
+  gfits_scan (db[0].vtable.header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (db[0].vtable.header, "NAXIS2", "%d", 1, &Ny);
+
+  /* file pointer is at beginning of desired table data */
+  start = ftell (db[0].f);
+  
+  for (i = 0; i < Nrow; i++) {
+    offset = start + Nx*row[i];
+    fseek (db[0].f, offset, SEEK_SET);
+    Nbytes = fwrite (db[0].vtable.buffer[i], sizeof (char), Nx, db[0].f);
+    if (Nbytes != Nx) { return (FALSE); }
+  }
+  return (TRUE);
+}
+
+/* write out complete image db table from ftable */
+int dvo_image_save_raw (FITS_DB *db, int VERBOSE) {
+
+  int status, Nx, Ny, size, Nbytes;
+
+  if (VERBOSE) fprintf (stderr, "writing out %d images\n", db[0].theader.Naxis[1]);
+
+  /* position to start of file */
+  Fseek (db[0].f, 0, SEEK_SET);
+  
+  /* write out complete header (no check on disk size?) */
+  status = fwrite (db[0].header.buffer, 1, db[0].header.size, db[0].f);
+  if (status != db[0].header.size) {
+    fprintf (stderr, "ERROR: failed writing data to image header\n");
+    exit (1);
+  }
+
+  gfits_scan (db[0].ftable.header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (db[0].ftable.header, "NAXIS2", "%d", 1, &Ny);
+  size = Nx * Ny;
+  Nbytes = fwrite (db[0].ftable.buffer, sizeof(char), size, db[0].f);
+  if (Nbytes != size) return (FALSE);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_photcode_ops.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_photcode_ops.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvo_photcode_ops.c	(revision 22322)
@@ -0,0 +1,537 @@
+# include <dvo.h>
+
+/* we use a static variable to save the pre-loaded the photcodes.
+   photcode conversion functions refer to this table for ref values */
+
+/* We have three indexes:
+
+   table[0].hashcode provides the table photcode sequence for a photcode value
+   table[0].hashNsec provides the Nsec sequence for a photcode value
+   table[0].codeNsec provides the photcode value for an Nsec sequence
+
+   given photcode = table[0].code[i] and code = photcode[0].code
+
+   hashcode[code] = i
+   hashNsec[code] = Nsec or -1 if not a PRI/SEC code
+   codeNsec[Nsec] = code
+*/
+
+static PhotCodeData *photcodes = NULL;
+
+PhotCodeData *GetPhotcodeTable () {
+
+  /* allocate space to photcode table, free existing data */
+  if (photcodes == NULL) {
+    ALLOCATE (photcodes, PhotCodeData, 1);
+    photcodes[0].code = NULL;
+  }
+  return photcodes;
+}
+
+/* the static ZERO_POINT is used by programs as an approximate nominal */
+static double ZERO_POINT = 0.0;
+
+void SetZeroPoint (double ZP) {
+  ZERO_POINT = ZP;
+}
+
+double GetZeroPoint () {
+  return ZERO_POINT;
+}
+
+# define SCALE 0.001
+
+/********** photcode lookup functions **********/
+
+/* return photcode for given name */
+PhotCode *GetPhotcodebyName (char *name) {
+  
+  int i;
+
+  if (name == NULL) return (NULL);
+  
+  for (i = 0; i < photcodes[0].Ncode; i++) {
+    if (!strcmp (photcodes[0].code[i].name, name)) {
+      return (&photcodes[0].code[i]);
+    }
+  }
+  return (NULL);
+}
+/* return photcode.code for given name */
+int GetPhotcodeCodebyName (char *name) {
+  
+  int i;
+  
+  if (name == NULL) return (0);
+
+  for (i = 0; i < photcodes[0].Ncode; i++) {
+    if (!strcmp (photcodes[0].code[i].name, name)) {
+      return (photcodes[0].code[i].code);
+    }
+  }
+  return (0);
+}
+/* return equivalent photcode for given name */
+PhotCode *GetPhotcodeEquivbyName (char *name) {
+  
+  int i, equiv;
+  
+  if (name == NULL) return (NULL);
+
+  for (i = 0; i < photcodes[0].Ncode; i++) {
+    if (!strcmp (photcodes[0].code[i].name, name)) {
+      if (photcodes[0].code[i].equiv == 0) return (NULL);
+      equiv = photcodes[0].hashcode[photcodes[0].code[i].equiv];
+      if (equiv == -1) return (NULL);
+      return (&photcodes[0].code[equiv]);
+    }
+  }
+  return (NULL);
+}
+/* return equivalent photcode.code for given name */
+int GetPhotcodeEquivCodebyName (char *name) {
+  
+  int i, equiv;
+  
+  if (name == NULL) return (0);
+
+  for (i = 0; i < photcodes[0].Ncode; i++) {
+    if (!strcmp (photcodes[0].code[i].name, name)) {
+      if (photcodes[0].code[i].equiv == 0) return (0);
+      equiv = photcodes[0].hashcode[photcodes[0].code[i].equiv];
+      if (equiv == -1) return (0);
+      return (photcodes[0].code[equiv].code);
+    }
+  }
+  return (0);
+}
+
+/* return photcode for given code */
+PhotCode *GetPhotcodebyCode (int code) {
+  
+  int entry;
+  
+  if (code < 0) return (NULL);
+  if (code > 0x10000) return (NULL);
+
+  entry = photcodes[0].hashcode[code];
+  if (entry == -1) return (NULL);
+
+  return (&photcodes[0].code[entry]);
+}
+/* return photcode.code for given code */
+char *GetPhotcodeNamebyCode (int code) {
+  
+  int entry;
+  
+  if (code < 0) return (NULL);
+  if (code > 0x10000) return (NULL);
+
+  entry = photcodes[0].hashcode[code];
+  if (entry == -1) return (NULL);
+
+  return (photcodes[0].code[entry].name);
+}
+/* return equivalent photcode for given code */
+PhotCode *GetPhotcodeEquivbyCode (int code) {
+  
+  int entry, equiv;
+  
+  if (code < 0) return (NULL);
+  if (code > 0x10000) return (NULL);
+
+  entry = photcodes[0].hashcode[code];
+  if (entry == -1) return (NULL);
+
+  if (photcodes[0].code[entry].equiv == 0) return (NULL);
+  equiv = photcodes[0].hashcode[photcodes[0].code[entry].equiv];
+
+  if (equiv == -1) return (NULL);
+  return (&photcodes[0].code[equiv]);
+}
+/* return equivalent photcode.code for given code */
+int GetPhotcodeEquivCodebyCode (int code) {
+  
+  int entry;
+  
+  if (code < 0) return (0);
+  if (code > 0x10000) return (0);
+
+  entry = photcodes[0].hashcode[code];
+  if (entry == -1) return (0);
+  return (photcodes[0].code[entry].equiv);
+}
+
+int GetPhotcodeNsec (int code) {
+  
+  int Nsec;
+  
+  if (code < 0) return (-1);
+  if (code > 0x10000) return (-1);
+
+  Nsec = photcodes[0].hashNsec[code];
+  return (Nsec);
+}
+
+/* Nsec of 0 is PRI */
+PhotCode *GetPhotcodebyNsec (int Nsec) {
+  
+  int Ncode;
+  int Nseq;
+
+  if (Nsec > photcodes[0].Nsecfilt) return (NULL);
+  if (Nsec < 0) return (NULL);
+  
+  Ncode = photcodes[0].codeNsec[Nsec];
+  if (Ncode < 0) return (NULL);
+
+  Nseq = photcodes[0].hashcode[Ncode];
+  return (&photcodes[0].code[Nseq]);
+}
+
+int GetPhotcodeNsecfilt () {
+  return (photcodes[0].Nsecfilt);
+}
+
+/* ALLOCATE and return list of all photcodes
+   with photcode.equiv == code */
+int *GetPhotcodeEquivList (int code, int *nlist) {
+
+  int i, Nlist;
+  int *list;
+
+  ALLOCATE (list, int, MAX (1, photcodes[0].Ncode));
+  Nlist = 0;
+  for (i = 0; i < photcodes[0].Ncode; i++) {
+    if (photcodes[0].code[i].equiv != code) continue;
+    list[Nlist] = photcodes[0].code[i].code;
+    Nlist ++;
+  }
+  REALLOCATE (list, int, MAX (1, Nlist));
+
+  *nlist = Nlist;
+  return (list);
+}
+
+/******** photometry conversion functions *********/
+float PhotInst (Measure *measure) {
+
+  int Np;
+  float M;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    M = measure[0].M;
+    return (M);
+  }
+
+  M = measure[0].M - measure[0].dt - ZERO_POINT;
+	  
+  return (M);
+
+}
+
+float PhotCat (Measure *measure) {
+
+  int Np;
+  float Mcat;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mcat = measure[0].M;
+    return (Mcat);
+  }
+  code = &photcodes[0].code[Np];
+  Mcat = measure[0].M - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
+  
+  return (Mcat);
+}
+
+float PhotSys (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  int i, Np;
+  float Mcat, Mcol, Msys, mc, Mc;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Msys = measure[0].M;
+    return (Msys);
+  }
+  code = &photcodes[0].code[Np];
+  Mcat = measure[0].M - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
+
+  /* for DEP, color must be made of PRI/SEC */
+  mc = PhotColorForCode (average, secfilt, NULL, code);
+  if (isnan(mc)) return (Mcat);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;
+  }
+  Msys = Mcat + Mcol;
+  return (Msys);
+}
+
+float PhotRel (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  int i, Np;
+  float Mcat, Mcol, Mrel, mc, Mc;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mcat = measure[0].M;
+    return (Mcat);
+  }
+  code = &photcodes[0].code[Np];
+  Mrel = measure[0].M - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C - measure[0].Mcal;
+
+  /* for DEP, color must be made of PRI/SEC */
+  mc = PhotColorForCode (average, secfilt, NULL, code);
+  if (isnan(mc)) return (Mrel);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;    /* the 0.001 is needed for higher order terms to keep the units mag = mag^n */
+  }
+  Mrel += Mcol;
+  return (Mrel);
+}
+
+/* return calibrated magnitude from measure for given photcode */
+float PhotCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code) {
+
+  int i, Np; 
+  float Mcal, Mrel, Mcol, mc, Mc;
+
+  if (code == NULL) return NAN;
+
+  /* code must be the matching PRI/SEC code for this measurement or an equivalent ALT */
+  Np = photcodes[0].hashcode[thisone[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mrel = thisone[0].M;
+    return (Mrel);
+  }
+  if (code[0].code != photcodes[0].code[Np].equiv) return (NAN);
+
+  Mcal = PhotRel (thisone, average, secfilt) + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (isnan(mc)) return (Mcal);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;
+  }
+  Mcal += Mcol;
+  return (Mcal);
+}
+
+/* color term may not use DEP magnitude */
+float PhotColorForCode (Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code) {
+
+  int i, Ns1, Ns2, Ns;
+  float m1, m2, mc;
+  PhotCode *color;
+
+  m1 = m2 = NAN;
+
+  if (measure == NULL) {
+    Ns1 = photcodes[0].hashNsec[code[0].c1];
+    Ns2 = photcodes[0].hashNsec[code[0].c2];
+  
+    m1 = (Ns1 == -1) ? NAN : secfilt[Ns1].M;
+    m2 = (Ns2 == -1) ? NAN : secfilt[Ns2].M;
+    mc = (isnan(m1) || isnan(m2)) ? NAN : (m1 - m2);
+    return (mc);
+  }
+
+  /* find magnitude matching first color term */
+  color = GetPhotcodebyCode (code[0].c1);
+  if (color == NULL) return (NAN);
+  if (color[0].type == PHOT_REF) {
+    for (i = 0; (i < average[0].Nmeasure) && (isnan(m1)); i++) {
+      if (measure[i].photcode == color[0].code) {
+	m1 = measure[i].M;
+      }
+    }
+  } else {
+    Ns = photcodes[0].hashNsec[color[0].code];
+    m1 = (Ns == -1) ? NAN : secfilt[Ns].M;
+  }	
+
+  /* find magnitude matching second color term */
+  color = GetPhotcodebyCode (code[0].c2);
+  if (color == NULL) return (NAN);
+  if (color[0].type == PHOT_REF) {
+    for (i = 0; (i < average[0].Nmeasure) && (isnan(m2)); i++) {
+      if (measure[i].photcode == color[0].code) {
+	m2 = measure[i].M;
+      }
+    }
+  } else {
+    Ns = photcodes[0].hashNsec[color[0].code];
+    m2 = (Ns == -1) ? NAN : secfilt[Ns].M;
+  }	
+  mc = (isnan(m1) || isnan(m2)) ? NAN : (m1 - m2);
+  return (mc);
+}
+
+/* return calibrated magnitude from average/secfilt for given photcode */
+float PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+
+  int i, Ns;
+  float Mave, Mref, Mcol, mc;
+  double Mc;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mave = (Ns == -1) ? NAN : secfilt[Ns].M;
+  Mref = Mave + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (isnan(mc)) return (Mref);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;    /* the 0.001 is needed for higher order terms to keep the units mag = mag^n */
+  }
+  Mref += Mcol;
+  return (Mref);
+}
+
+/***/
+float PhotAve (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  float Mave;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mave = (Ns == -1) ? NAN : secfilt[Ns].M;
+  return (Mave);
+}
+
+float PhotdM (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  float dM;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  dM  = (Ns == -1) ? NAN : secfilt[Ns].dM;
+  return (dM);
+}
+
+// XXX return NAN or NAN_S_SHORT? (secfilt->Xm is short)
+float PhotXm (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  short Mi;
+  float Xm;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mi = (Ns == -1) ? NAN : secfilt[Ns].Xm;
+  Xm = (isnan(Mi)) ? -1.0 : pow (10.0, 0.01*Mi);
+  return (Xm);
+}
+
+/* given a photcode pair c1 & c2, return the color of this star (NaN if not found) */
+int PhotColor (Average *average, SecFilt *secfilt, Measure *measure, int c1, int c2, double *color) {
+
+  int i, Ns;
+  double M1, M2, dM;
+  PhotCode *code;
+  
+  code = GetPhotcodebyCode (c1);
+  if (code == NULL) return (FALSE);
+  if (code[0].type == PHOT_REF) {
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      if (measure[i].photcode == c1) {
+	M1 = measure[i].M;
+	goto filter1;
+      }
+    }	
+    return (FALSE);
+  } else {
+    Ns = photcodes[0].hashNsec[code[0].code];
+    M1 = (Ns == -1) ? NAN : secfilt[Ns].M;
+  }	
+
+filter1:
+  code = GetPhotcodebyCode (c2);
+  if (code == NULL) return (FALSE);
+  if (code[0].type == PHOT_REF) {
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      if (measure[i].photcode == c2) {
+	M2 = measure[i].M;
+	goto filter2;
+      }
+    }	
+    return (FALSE);
+  } else {
+    Ns = photcodes[0].hashNsec[code[0].code];
+    M2 = (Ns == -1) ? NAN : secfilt[Ns].M;
+  }	
+  
+filter2:
+
+  dM = M1 - M2;
+  *color = dM;
+  
+  return (TRUE);
+}
+
+/* photcode table should have the following format: 
+
+# code name     type  zero  airmass  offset  c1 c2  slope  <color>  primary
+1    B        pri   24.0  0.15     -       -  -   -      -        -
+2    B        pri   24.0  0.15     -       -  -   -      -        -
+3    B1       sec   22.5  0.18     0.15    1  2   0.10   0.50     1
+1000 USNO_B   ref   -     -        -       -  -   -      -        -
+
+*/
+
+
+/*
+  Nc1 = photcodes[0].code[Np].c1;
+  Ns1 = photcodes[0].hashNsec[Nc1];
+
+  Nc2 = photcodes[0].code[Np].c2;
+  Ns2 = photcodes[0].hashNsec[Nc2];
+
+  Xlam = photcodes[0].code[Np].X[0];
+  Klam = photcodes[0].code[Np].K;
+  
+  m1 = (Ns1 == -1) ? average[0].M : secfilt[Ns1].M;
+  m2 = (Ns2 == -1) ? average[0].M : secfilt[Ns2].M;
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvosorts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvosorts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/dvosorts.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include <dvo.h>
+
+/* several dvo-specific sorting functions used in a number of locations */
+
+/* values are ave[i].R, ave[i].D, ave[i].M */
+void sortave (Average *ave, int N) {
+
+# define SWAPFUNC(A,B){ Average tmp; tmp = ave[A]; ave[A] = ave[B]; ave[B] = tmp; }
+# define COMPARE(A,B)(ave[A].R < ave[B].R)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+/* sort subset by image[subset[i]].tzero */
+void sort_image_subset (Image *image, int *subset, int N) {
+
+# define SWAPFUNC(A,B){ int tmp; tmp = subset[A]; subset[A] = subset[B]; subset[B] = tmp; }
+# define COMPARE(A,B)(image[subset[A]].tzero < image[subset[B]].tzero)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+/* sort a coordinate pair (X,Y) and the associated index (S) */
+void sort_coords_index (double *X, double *Y, int *S, int N) {
+  
+# define SWAPFUNC(A,B){ double dtmp; int itmp; \
+  dtmp = X[A]; X[A] = X[B]; X[B] = dtmp; \
+  dtmp = Y[A]; Y[A] = Y[B]; Y[B] = dtmp; \
+  itmp = S[A]; S[A] = S[B]; S[B] = itmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void sort_stars_ra (Stars *stars, int N) {
+
+# define SWAPFUNC(A,B){ Stars tmp; tmp = stars[A]; stars[A] = stars[B]; stars[B] = tmp; }
+# define COMPARE(A,B)(stars[A].R < stars[B].R)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void sort_regions (SkyRegion *region, int N) {
+
+# define SWAPFUNC(A,B){ SkyRegion tmp; tmp = region[A]; region[A] = region[B]; region[B] = tmp; }
+# define COMPARE(A,B)(region[A].Dmin < region[B].Dmin)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/fits_db.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/fits_db.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/fits_db.c	(revision 22322)
@@ -0,0 +1,234 @@
+# include <dvo.h>
+
+/* init the db structure */
+int gfits_db_init (FITS_DB *db) {
+
+  db[0].f             = NULL;
+  db[0].filename      = NULL;
+  db[0].header.buffer = NULL;
+  db[0].matrix.buffer = NULL;
+  db[0].theader.buffer = NULL;
+  db[0].ftable.buffer = NULL;
+  db[0].ftable.header = &db[0].theader;
+
+  db[0].vtable.header = NULL;
+  db[0].vtable.buffer = NULL;
+  db[0].vtable.row    = NULL;
+  return (TRUE);
+}
+
+/* create an empty db */
+int gfits_db_create (FITS_DB *db) {
+  gfits_init_header (&db[0].header);    
+  db[0].header.extend = TRUE;
+  gfits_create_header (&db[0].header);
+  gfits_create_matrix (&db[0].header, &db[0].matrix);
+  gfits_print (&db[0].header, "NEXTEND", "%d", 1, 1);
+  db[0].ftable.header = &db[0].theader;
+  return (TRUE);
+}
+
+int gfits_db_lock (FITS_DB *db, char *filename) {
+  
+  /* database name must be set first */
+  if (filename == NULL) {
+    fprintf (stderr, "db file is not set\n");
+    return (FALSE);
+  }
+
+  /* database handle must be set first */
+  if (db == NULL) {
+    fprintf (stderr, "db handle is not set\n");
+    return (FALSE);
+  }
+
+  /* lock & open database */
+  db[0].f = fsetlockfile (filename, db[0].timeout, db[0].lockstate, &db[0].dbstate);
+  if (db[0].f == NULL) {
+    if (db[0].dbstate == LCK_MISSING) {
+      fprintf (stderr, "no data in db %s\n", filename);
+    } else {
+      fprintf (stderr, "cannot set lock on db file %s\n", filename);
+    }
+    return (FALSE);
+  }
+  
+  db[0].filename = strcreate (filename);
+  return (TRUE);
+}
+
+/* load the complete db table into memory - load first extension, do not validate EXTNAME */
+int gfits_db_load (FITS_DB *db) {
+
+  /* database name must be set first */
+  if (db == NULL) {
+    fprintf (stderr, "db handle is not set\n");
+    return (FALSE);
+  }
+
+  /* init & load in FITS table data - return FALSE on error */
+  if (!gfits_fread_header (db[0].f, &db[0].header)) {
+    fprintf (stderr, "can't read primary header\n"); 
+    gfits_db_free (db);
+    return (FALSE);
+  }
+  if (!gfits_fread_matrix (db[0].f, &db[0].matrix, &db[0].header)) {
+    fprintf (stderr, "can't read primary matrix");
+    gfits_db_free (db);
+    return (FALSE);
+  }
+  if (!gfits_fread_header (db[0].f, &db[0].theader)) {
+    fprintf (stderr, "can't read table header");
+    gfits_db_free (db);
+    return (FALSE);
+  }
+  if (!gfits_fread_ftable_data (db[0].f, &db[0].ftable)) {
+    fprintf (stderr, "can't read table data");
+    gfits_db_free (db);
+    return (FALSE);
+  }
+  db[0].swapped = FALSE;  /* table does not have internal byte-order */
+  return (TRUE);
+}
+
+/* load the data from Nrows from table starting at start; load header, etc if needed */
+int gfits_db_load_segment (FITS_DB *db, int start, int Nrows) {
+
+  int Nskip;
+
+  /* database name must be set first */
+  if (db == NULL) {
+    fprintf (stderr, "db handle is not set\n");
+    return (FALSE);
+  }
+
+  /* database name must be opened */
+  if (db[0].f == NULL) {
+    fprintf (stderr, "db is not opened\n");
+    return (FALSE);
+  }
+
+  /* position to start of file */
+  Fseek (db[0].f, 0, SEEK_SET);
+
+  /* load or skip header */
+  if (db[0].header.buffer == NULL) {
+    if (!gfits_fread_header (db[0].f, &db[0].header)) {
+      fprintf (stderr, "can't read primary header\n"); 
+      return (FALSE);
+    }
+  } else {
+    Nskip = db[0].header.size;
+    Fseek (db[0].f, Nskip, SEEK_CUR);
+  }
+
+  /* load or skip matrix */
+  if (db[0].matrix.buffer == NULL) {
+    if (!gfits_fread_matrix (db[0].f, &db[0].matrix, &db[0].header)) {
+      fprintf (stderr, "can't read primary matrix");
+      return (FALSE);
+    }
+  } else {
+    Nskip = gfits_data_size (&db[0].header);
+    Fseek (db[0].f, Nskip, SEEK_CUR);
+  }
+
+  /* load or skip table header */
+  if (db[0].theader.buffer == NULL) {
+    if (!gfits_fread_header (db[0].f, &db[0].theader)) {
+      fprintf (stderr, "can't read table header");
+      return (FALSE);
+    }
+  } else {
+    Nskip = db[0].header.size;
+    Fseek (db[0].f, Nskip, SEEK_CUR);
+  }
+
+  /* read table segment into vtable */
+  if (!gfits_fread_vtable_range (db[0].f, &db[0].vtable, start, Nrows)) {
+    fprintf (stderr, "can't read table data");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+/* write complete db file */
+int gfits_db_save (FITS_DB *db) {
+
+  /* write all data to file */
+  make_backup (db[0].filename);
+  Fseek (db[0].f, 0, SEEK_SET);
+
+  if (!gfits_fwrite_header  (db[0].f, &db[0].header)) {
+    fprintf (stderr, "can't write primary header");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_matrix  (db[0].f, &db[0].matrix)) {
+    fprintf (stderr, "can't write primary matrix");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_Theader (db[0].f, &db[0].theader)) {
+    fprintf (stderr, "can't write table header");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_table   (db[0].f, &db[0].ftable)) {
+    fprintf (stderr, "can't write table data");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+/* write vtable to db file (also appends rows to the end of the table) */
+int gfits_db_update (FITS_DB *db) {
+
+  /* this section is not valid if we have changed the size of header, matrix, theader */
+
+  /* write subset to file */
+  // make_backup (db[0].filename);  /*** drop this!!?? ***/
+  Fseek (db[0].f, 0, SEEK_SET);
+
+  /* do we revert to the old version if this fails? */
+  if (!gfits_fwrite_header   (db[0].f, &db[0].header))  {
+    fprintf (stderr, "can't update primary header");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_matrix   (db[0].f, &db[0].matrix))  {
+    fprintf (stderr, "can't update primary matrix");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_Theader  (db[0].f, &db[0].theader)) {
+    fprintf (stderr, "can't update table header");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_vtable   (db[0].f, &db[0].vtable))  {
+    fprintf (stderr, "can't update table data");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+/* free memory associated with db handle */
+int gfits_db_free (FITS_DB *db) {
+  gfits_free_header (&db[0].header);
+  gfits_free_matrix (&db[0].matrix);
+  gfits_free_header (&db[0].theader);
+  gfits_free_table  (&db[0].ftable);
+  gfits_free_vtable (&db[0].vtable);
+  if (db[0].filename != NULL) {
+    free (db[0].filename);
+    db[0].filename = NULL;
+  }
+  return (TRUE);
+}
+
+/* close the db files (close open file & unlock) */
+int gfits_db_close (FITS_DB *db) {
+  if (db[0].f == NULL) return (TRUE);
+  fclearlockfile (db[0].filename, db[0].f, db[0].lockstate, &db[0].dbstate);
+  db[0].f = NULL;
+  return (TRUE);
+}  
+
+/* for this to be atomic, need to unlink before we unlock */
+/* unlink file here if resulting file is empty? */
+/* if (db[0].Nrow == 0) && (lockstate != LCK_SOFT)) unlink (dBFile); */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/imreg_datatypes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/imreg_datatypes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/imreg_datatypes.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include <dvo.h>
+
+char typename[N_TYPE][32] = {"none", "object", "dark", "bias", "flat", "mask", "fringe", "scatter", "modes", "frpts", "any"};
+char typecode[N_TYPE]     = {'x',    'o',      'd',    'b',    'f',    'm',    'r',      's',       'M',     'F'};
+char modename[N_MODE][32] = {"none", "MEF", "SPLIT", "SINGLE", "CUBE", "SLICE", "MODES"};
+
+int get_image_type (char *name) {
+
+  int i;
+  
+  for (i = 0; i < N_TYPE; i++) {
+    if (!strncasecmp (name, typename[i], strlen(typename[i]))) {
+      return (i);
+    }
+  }
+
+  /* special cases */
+  if (!strncasecmp (name, "SKYFLAT", strlen("SKYFLAT"))) {
+    return (T_FLAT);
+  }
+
+  return (T_UNDEF);
+}
+
+char *get_type_name (int type) {
+
+  if (type < T_NONE) return ("undef");
+  if (type >= N_TYPE) return ("undef");
+  return (typename[type]);
+}
+
+int get_image_mode (char *name) {
+
+  int i;
+  
+  for (i = 0; i < N_MODE; i++) {
+    if (!strncasecmp (name, modename[i], strlen(modename[i]))) {
+      return (i);
+    }
+  }
+  return (M_UNDEF);
+}
+
+char *get_mode_name (int mode) {
+
+  if (mode < M_NONE) return ("undef");
+  if (mode >= N_MODE) return ("undef");
+  return (modename[mode]);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/mosaic_astrom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/mosaic_astrom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/mosaic_astrom.c	(revision 22322)
@@ -0,0 +1,131 @@
+# include <dvo.h>
+
+/* chip-match table: j = ChipMatch[i], images[j] is DIS for images[i] WRP */
+
+static int    iDIS = -1;      // DIS entry in ChipMatch[]
+static int    Ndis = 0;
+static int    *DISentry = NULL;
+static e_time *DIStzero = NULL;
+static int    *ChipMatch = NULL;
+
+/* what is the currently registered mosaic image? */
+int GetRegisteredMosaic () {
+  return (iDIS);
+}
+
+/* given an image array and a current entry of type WRP, find matching DIS & Register it */
+int FindMosaicForImage (Image *images, int Nimages, int entry) {
+
+  int status;
+
+  if (ChipMatch == NULL) {
+    status = FindMosaicForImage_TableSearch (images, Nimages, entry);
+    return (status);
+  }
+  status = FindMosaicForImage_MatchSearch (images, Nimages, entry);
+  return (status);
+}
+
+/* given an image array and a current entry of type WRP, find matching DIS & Register it */
+int FindMosaicForImage_TableSearch (Image *images, int Nimages, int entry) {
+
+  e_time tzero;
+  int i;
+
+  /* search backwards for image with same time and type DIS */
+
+  if (strcmp(&images[entry].coords.ctype[4], "-WRP")) {
+    /* not a wrp image, do nothing */
+    return (TRUE); /* error or not */
+  }
+
+  tzero = images[entry].tzero;
+  
+  for (i = entry - 1; i >= 0; i--) {
+    if (images[i].tzero != tzero) continue;
+    if (strcmp(&images[i].coords.ctype[4], "-DIS")) continue;
+
+    /* found a valid match */
+    RegisterMosaic (&images[i].coords);
+    iDIS = i;
+    return (TRUE);
+  }
+
+  for (i = entry + 1; i < Nimages; i++) {
+    if (images[i].tzero != tzero) continue;
+    if (strcmp(&images[i].coords.ctype[4], "-DIS")) continue;
+
+    /* found a valid match */
+    RegisterMosaic (&images[i].coords);
+    iDIS = i;
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+/* given an image array and a current entry of type WRP, find matching DIS & Register it */
+int FindMosaicForImage_MatchSearch (Image *images, int Nimages, int entry) {
+
+  int N;
+
+  if (strcmp(&images[entry].coords.ctype[4], "-WRP")) {
+    /* not a wrp image, do nothing */
+    return (TRUE); /* error or not */
+  }
+
+  if (ChipMatch == NULL) return (FALSE);
+
+  N = ChipMatch[entry];
+  if (N < 0) return (FALSE);
+  if (N >= Nimages) return (FALSE);
+
+  RegisterMosaic (&images[N].coords);
+  iDIS = N;
+  return (TRUE);
+}
+
+int BuildChipMatch (Image *images, int Nimages) {
+
+  int i, j, NDIS;
+
+  if (DISentry != NULL) free (DISentry);
+  if (DIStzero != NULL) free (DIStzero);
+  if (ChipMatch != NULL) free (ChipMatch);
+
+  /* find all DIS images, save tzero (& instrument) */
+  Ndis = 0;
+  NDIS = 100;
+  ALLOCATE (DISentry, int, NDIS);
+  ALLOCATE (DIStzero, e_time, NDIS);
+
+  for (i = 0; i < Nimages; i++) {
+    if (strcmp(&images[i].coords.ctype[4], "-DIS")) continue;
+    DISentry[Ndis] = i;
+    DIStzero[Ndis] = images[i].tzero;
+    Ndis ++;
+    if (Ndis >= NDIS) {
+      NDIS += 100;
+      REALLOCATE (DISentry, int, NDIS);
+      REALLOCATE (DIStzero, e_time, NDIS);
+    }
+  }
+
+  /* find all matched WRP images */
+  ALLOCATE (ChipMatch, int, Nimages);
+  for (i = 0; i < Nimages; i++) {
+    ChipMatch[i] = -1;
+    if (strcmp(&images[i].coords.ctype[4], "-WRP")) continue;
+    for (j = 0; j < Ndis; j++) {
+      if (DIStzero[j] != images[i].tzero) continue;
+      ChipMatch[i] = DISentry[j];
+      goto got_match;
+    }
+
+    fprintf (stderr, "WARNING: can't find matching mosaic \n");
+    ChipMatch[i] = -2;
+
+  got_match:
+    continue;
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/photfits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/photfits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/photfits.c	(revision 22322)
@@ -0,0 +1,258 @@
+# include <dvo.h>
+# define MAX_ORDER 3
+
+/**** XXXX warning: these functions have not been corrected to handle 
+      the change of Mcal from millimags to mags
+      I don't think most functions are using the Mcal polynomial terms in 
+      any case... ****/
+
+/* all terms of order > 0 are stored as 16bit floats 
+   the zero-order term is stored as a short int (-32k,+32k) */
+
+/* convert double to low-precision short int */
+short int putMi (double value) {
+  
+  double T;
+  unsigned int I;
+
+  if (value > 0) {
+    T = 1000 * log10 (value);
+    I = MAX (MIN (T, 16383), -16382) + 0x3fff;
+    return (I);
+  }
+  if (value < 0) {
+    T = -value;
+    T = 1000 * log10 (T);
+    I = MAX (MIN (T, 16383), -16382) + 0xbfff;
+    return (I);
+  }
+  if (value == 0) {
+    I = 0;
+    return (0);
+  }
+  return (NAN_S_SHORT);
+}
+
+/* convert low-precision short int to double */
+double getMi (short int value) {
+  
+  double T, V, sign;
+  short int I;
+
+  if (value == 0) {
+    return (0.0);
+  }
+  if (value & 0x8000) {
+    sign = -1;
+    I = value & 0x7fff;
+  } else {
+    sign = +1;
+    I = value;
+  }
+    
+  T = (I - 0x3fff) / 1000.0;
+  V = sign * pow (10.0, T);
+  return (V);
+}
+
+/* convert image parameters to c[i] coeffs */
+void returnMcal (Image *image, double *c) {
+
+  switch (image[0].order) {
+  case 0:
+    c[0] = image[0].Mcal;
+    return;
+  case 1:
+    c[0] = image[0].Mcal;
+    c[1] = getMi (image[0].Mx);
+    c[2] = getMi (image[0].My);
+    return;
+  case 2:
+    c[0] = image[0].Mcal;
+    c[1] = getMi (image[0].Mx);
+    c[2] = getMi (image[0].Mxx);
+    c[3] = getMi (image[0].My);
+    c[4] = getMi (image[0].Mxy);
+    c[5] = getMi (image[0].Myy);
+    return;
+  case 3:
+    c[0] = image[0].Mcal;
+    c[1] = getMi (image[0].Mx);
+    c[2] = getMi (image[0].Mxx);
+    c[3] = getMi (image[0].Mxxx);
+    c[4] = getMi (image[0].My);
+    c[5] = getMi (image[0].Mxy);
+    c[6] = getMi (image[0].Mxxy);
+    c[7] = getMi (image[0].Myy);
+    c[8] = getMi (image[0].Mxyy);
+    c[9] = getMi (image[0].Myyy);
+    return;
+  case 4:
+    c[0] = image[0].Mcal;
+    c[1] = getMi (image[0].Mx);
+    c[2] = getMi (image[0].Mxx);
+    c[3] = getMi (image[0].Mxxx);
+    c[4] = getMi (image[0].Mxxxx);
+    c[5] = getMi (image[0].My);
+    c[6] = getMi (image[0].Mxy);
+    c[7] = getMi (image[0].Mxxy);
+    c[8] = getMi (image[0].Mxxxy);
+    c[9] = getMi (image[0].Myy);
+    c[10] = getMi (image[0].Mxyy);
+    c[11] = getMi (image[0].Mxxyy);
+    c[12] = getMi (image[0].Myyy);
+    c[13] = getMi (image[0].Mxyyy);
+    c[14] = getMi (image[0].Myyyy);
+    return;
+  default:
+    c[0] = 0;
+    return;
+  }
+}
+
+void assignMcal (Image *image, double *c, int order) {
+
+  image[0].order = order;
+
+  switch (order) {
+  case 0:
+    image[0].Mcal = c[0];
+    return;
+  case 1:
+    image[0].Mcal = c[0];
+    image[0].Mx    = putMi(c[1]);
+    image[0].My    = putMi(c[2]);
+    return;
+  case 2:
+    image[0].Mcal = c[0];
+    image[0].Mx    = putMi(c[1]);
+    image[0].Mxx   = putMi(c[2]);
+    image[0].My    = putMi(c[3]);
+    image[0].Mxy   = putMi(c[4]);
+    image[0].Myy   = putMi(c[5]);
+    return;
+  case 3:
+    image[0].Mcal = c[0];
+    image[0].Mx    = putMi(c[1]);
+    image[0].Mxx   = putMi(c[2]);
+    image[0].Mxxx  = putMi(c[3]);
+    image[0].My    = putMi(c[4]);
+    image[0].Mxy   = putMi(c[5]);
+    image[0].Mxxy  = putMi(c[6]);
+    image[0].Myy   = putMi(c[7]);
+    image[0].Mxyy  = putMi(c[8]);
+    image[0].Myyy  = putMi(c[9]);
+    return;
+  case 4:
+    image[0].Mcal = c[0];
+    image[0].Mx    = putMi(c[1]);
+    image[0].Mxx   = putMi(c[2]);
+    image[0].Mxxx  = putMi(c[3]);
+    image[0].Mxxxx = putMi(c[4]);
+    image[0].My    = putMi(c[5]);
+    image[0].Mxy   = putMi(c[6]);
+    image[0].Mxxy  = putMi(c[7]);
+    image[0].Mxxxy = putMi(c[8]);
+    image[0].Myy   = putMi(c[9]);
+    image[0].Mxyy  = putMi(c[10]);
+    image[0].Mxxyy = putMi(c[11]);
+    image[0].Myyy  = putMi(c[12]);
+    image[0].Mxyyy = putMi(c[13]);
+    image[0].Myyyy = putMi(c[14]);
+    return;
+  default:
+    image[0].Mcal = 0.0;
+    image[0].order = 0;
+    return;
+  }
+}
+
+/* return value of image fit at x,y */
+double applyMcal (Image *image, double x, double y) {
+
+  double Mcal, c[15];
+
+  returnMcal (image, c);  /* convert image parameters to c[i] coeffs */
+  Mcal = 0.0;
+  switch (image[0].order) {
+  case 0:
+    Mcal = c[0];
+    break;
+  case 1:
+    Mcal = c[0] + x*c[1] + y*c[2];
+    break;
+  case 2:
+    Mcal = c[0] + x*(c[1] + x*c[2]) + y*(c[3] + x*c[4] + y*c[5]);
+    break;
+  case 3:
+    Mcal = c[0] + x*(c[1] + x*(c[2] + x*c[3])) + y*(c[4] + x*(c[5] + x*c[6]) + y*(c[7] + x*c[8] + y*c[9]));
+    break;
+  case 4:
+    Mcal = c[0] + x*(c[1] + x*(c[2] + x*(c[3] + x*c[4]))) + y*(c[5] + x*(c[6] + x*(c[7] + x*c[8])) + y*(c[9] + x*(c[10] + x*c[11]) + y*(c[12] + x*c[13] + y*c[14])));
+    break;
+  }
+  return (Mcal);
+}
+
+double findscatter (double *X, double *Y, double *M, double *dM, int N, double *c, int order) {
+  
+  int i;
+  double *x, *y, *m, *dm;
+  double S, S2, s, dS;
+
+  S2 = S = 0;
+  if (N < 2) {
+    return (0.0);
+  }
+  
+  x = X; y = Y; m = M; dm = dM;
+  switch (order) {
+  case 0:
+    for (i = 0; i < N; i++, m++, dm++) {
+      dS  = *m - c[0];
+      *dm = dS;
+      S  += dS;
+      S2 += dS*dS;
+    }
+    break;
+  case 1:
+    for (i = 0; i < N; i++, m++, x++, y++, dm++) {
+      s   = c[0] + *x*c[1] + *y*c[2];
+      dS  = *m - s;
+      *dm = dS;
+      S  += dS;
+      S2 += dS*dS;
+    }
+    break;
+  case 2:
+    for (i = 0; i < N; i++, m++, x++, y++, dm++) {
+      s   = c[0] + *x*(c[1] + *x*c[2]) + *y*(c[3] + *x*c[4] + *y*c[5]);
+      dS  = *m - s;
+      *dm = dS;
+      S  += dS;
+      S2 += dS*dS;
+    }
+    break;
+  case 3:
+    for (i = 0; i < N; i++, m++, x++, y++, dm++) {
+      s   = c[0] + *x*(c[1] + *x*(c[2] + *x*c[3])) + *y*(c[4] + *x*(c[5] + *x*c[6]) + *y*(c[7] + *x*c[8] + *y*c[9]));
+      dS  = *m - s;
+      *dm = dS;
+      S  += dS;
+      S2 += dS*dS;
+    }
+    break;
+  case 4:
+    for (i = 0; i < N; i++, m++, x++, y++, dm++) {
+      s   = c[0] + *x*(c[1] + *x*(c[2] + *x*(c[3] + *x*c[4]))) + *y*(c[5] + *x*(c[6] + *x*(c[7] + *x*c[8])) + *y*(c[9] + *x*(c[10] + *x*c[11]) + *y*(c[12] + *x*c[13] + *y*c[14])));
+      dS  = *m - s;
+      *dm = dS;
+      S  += dS;
+      S2 += dS*dS;
+    }
+    break;
+  }
+  S = S / N;
+  S2 = sqrt (S2 / N - S*S);
+  return (S2);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skydb.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skydb.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skydb.c	(revision 22322)
@@ -0,0 +1,400 @@
+
+typedef struct {
+  float r, d;
+} SkyCoord;
+
+/* each region is bounded on the sky by lines of constant RA & DEC.
+   the region represents a specified depth, and it may or may not have
+   children.  The start and end of the children in the array are childS and childE 
+   If the region at the given depth is populated with an object table, then 'object' is TRUE.
+   If the region at the given depth is populated with an image table, then 'image' is TRUE.
+*/   
+
+typedef struct {
+  char  name[16];
+  float Rmin, Rmax;
+  float Dmin, Dmax;
+  int   childS, childE;
+  char  depth, child;
+  char  object, image;
+} SkyRegion; /* 48 bytes */ 
+
+/* I have defined no accelerators other than the table hierarchy */
+
+/* find region which overlaps c at given depth (-1 : max depth) */
+SkyRegion *SkyFindPoint (SkyRegion *db, SkyCoord c, int depth) {
+  
+  Ns = 0;
+  Ne = 1;
+
+  while (1) {
+    No = -1;
+    for (i = Ns; (No == -1) && (i < Ne); i++) {
+      if (c.r < db[i].Rmin) continue;
+      if (c.r > db[i].Rmax) continue;
+      if (c.d < db[i].Dmin) continue;
+      if (c.d > db[i].Dmax) continue;
+      // got the needed region at this depth
+      No = i;
+    }
+    if (No == -1) return ((SkyRegion *) NULL);
+    if (depth == db[No].depth) return (&db[No]);
+    if ((depth > db[No].depth) && !db[No].child) return ((SkyRegion *) NULL);
+
+    /* need to check Ns, Ne, or guarantee valid range */
+    Ns = db[No].childS;
+    Ne = db[No].childE;
+
+    if ((depth > db[No].depth) && db[No].child) continue;
+    return (&db[No]);
+  }
+}
+
+/* find regions at all levels which overlap c */
+SkyRegion **SkyFindLevels (SkyRegion *db, SkyCoord c, int *Nregion) {
+
+  SkyRegion **region;
+  
+  Ns = 0;
+  Ne = 1;
+
+  N = 0;
+  NREGION = 10;
+  ALLOCATE (region, SkyRegion *, NREGION);
+
+  while (1) {
+    No = -1;
+    for (i = Ns; (No == -1) && (i < Ne); i++) {
+      if (c.r < db[i].Rmin) continue;
+      if (c.r > db[i].Rmax) continue;
+      if (c.d < db[i].Dmin) continue;
+      if (c.d > db[i].Dmax) continue;
+      // got the needed region at this depth
+      No = i;
+    }
+    if (No == -1) return ((SkyRegion *) NULL);
+
+    region[N] = &db[No];
+    N++;
+    if (N == NREGION) {
+      NREGION += 10;
+      REALLOCATE (region, SkyRegion *, NREGION);
+    }      
+
+    /* need to check Ns, Ne, or guarantee valid range */
+    if (db[No].child) {
+      Ns = db[No].childS;
+      Ne = db[No].childE;
+    } else {
+      *Nregion = N; 
+      return (region);
+    }
+  }
+}
+
+/* find regions contained within rectangular region  c1 - c2 */
+SkyRegion **SkyFindArea (SkyRegion *db, SkyCoord c1, SkyCoord c2, *nlist) {
+
+  int Nlist;
+  SkyRegion *list, *new, *sub;
+
+  while (c1.r > 360.0) c1.r -= 360.0;
+  while (c1.r <   0.0) c1.r += 360.0;
+  while (c2.r > 360.0) c2.r -= 360.0;
+  while (c2.r <   0.0) c2.r += 360.0;
+
+  /* check on c1.r > c2.r : cross boundary */
+  if (c1.r > c2.r) {
+    c0 = c2; c0.r = 360.0;
+    list1 = SkyFindArea (db, c1, c0, &Nlist1);
+    c0 = c1; c0.r = 0.0;
+    list2 = SkyFindArea (db, c0, c2, &Nlist2);
+    Nlist = Nlist1 + Nlist2;
+    ALLOCATE (list, SkyRegion *, Nlist);
+    memcpy (&list[0], list1, Nlist1*sizeof());
+    memcpy (&list[Nlist1], list2, Nlist2*sizeof());
+    free (list1);
+    free (list2);
+    *nlist = Nlist;
+    return (list);
+  }    
+
+  Nlist = 1;
+  ALLOCATE (list, SkyRegion *, 1);
+  list[0] = &db[0];
+  getchild = db[0].child;
+
+  while (getchild) {
+
+    getchild = FALSE;
+    Nnew = 0;
+    NNEW = Nlist + 100;
+    ALLOCATE (new, SkyRegion *, NNEW);
+
+    for (i = 0; i < Nlist; i++) {
+      if (list[i][0].child) {
+	sub = SkyFindAreaDB(db, list[i], c1, c2, &Nsub);
+	if (Nnew + Nsub == NNEW) {
+	  NNEW += 100;
+	  REALLOCATE (new, SkyRegion *, NNEW);
+	}
+	for (i = 0; i < Nsub; i++) {
+	  getchild |= sub[i][0].child;
+	  new[Nnew] = sub[i];
+	  Nnew++;
+	}
+	free (sub);
+      } else {
+	new[Nnew] = list[i];
+	Nnew ++;
+	if (Nnew == NNEW) {
+	  NNEW += 100;
+	  REALLOCATE (new, SkyRegion *, NNEW);
+	}
+      }
+    }
+
+    free (list);
+    list = new;
+    Nlist = Nnew;
+  }
+
+  return (list);
+  *nlist = Nlist;
+
+}
+
+/* ?? */
+SkyRegion *SkyFindAreaDB (SkyRegion *db, SkyRegion *ref, SkyCoord c1, SkyCoord c2, int *Nregion) {
+
+  SkyRegion **region;
+  
+  Ns = ref[0].childS;
+  Ne = ref[0].childE;
+
+  N = 0;
+  NREGION = 100;
+  ALLOCATE (region, SkyRegion *, NREGION);
+
+  /* c1 min, c2 max */
+
+  for (i = Ns; (No == -1) && (i < Ne); i++) {
+    if (c2.r < db[i].Rmin) continue;
+    if (c1.r > db[i].Rmax) continue;
+    if (c2.d < db[i].Dmin) continue;
+    if (c1.d > db[i].Dmax) continue;
+
+    region[N] = &db[i];
+    N++;
+    if (N == NREGION) {
+      NREGION += 100;
+      REALLOCATE (region, SkyRegion *, NREGION);
+    }      
+  }
+  *Nregion = N; 
+  return (region);
+}
+
+SkyRegion *SkyBuildTable (SkyRegion *seed, int Nseed, int level, int depth) {
+
+  /* given a table of Rmin, Dmin, Rmax, Dmax, name, at level 3, generate
+     all higher levels and 'depth' extra levels */
+
+  /* levels:
+     0 - fullsky.cpt
+     1 - n????.cpt / s????.cpt
+     2 - r????.cpt
+     3 - ????.cpt
+     4 - ????.??.cpt
+  */
+  
+  SkyRegion *db;
+
+  /* allocate at least 30 for levels 0 & 1 */
+  NREGION = 100;
+  ALLOCATE (db, SkyRegion, NREGION);
+
+  /* full sky */
+  strcpy (db[0].name, "fullsky.cpt");
+  db[0].Rmin =   0; db[0].Rmax = 360;
+  db[0].Dmin = -90; db[0].Dmax =  90;
+  db[0].depth = 0;
+  db[0].child = FALSE;
+  N = 1;
+
+  db[0].childS = N;
+  /* north dec bands */
+  for (dec = 0; dec < 90; dec += 7.5) {
+    sprintf (db[N].name, "n%04d.cpt", (int) 100*dec);
+    db[N].Rmin =   0; db[N].Rmax = 360;
+    db[N].Dmin = dec; db[N].Dmax = dec + 7.5;
+    db[N].depth = 1;
+    db[N].child = FALSE;
+    N++;
+  }
+  /* south dec bands */
+  for (dec = 0; dec > -90; dec -= 7.5) {
+    sprintf (db[N].name, "s%04d.cpt", (int) 100*dec);
+    db[N].Rmin =   0;       db[N].Rmax = 360;
+    db[N].Dmin = dec - 7.5; db[N].Dmax = dec;
+    db[N].depth = 1;
+    db[N].child = FALSE;
+    N++;
+  }
+  db[0].childE = N;
+
+  /* subdivide dec bands based on seed */
+  Rnumber = 0;
+  childS = db[0].childS;
+  childE = db[0].childE;
+  for (i = childS; i < childE; i++) {
+
+    Nnew = 0;
+    NNEW = 100;
+    ALLOCATE (new, SkyRegion, NNEW);
+
+    for (j = 0; j < Nseed; j++) {
+      if (seed[j].Rmin > db[i].Rmax) continue;
+      if (seed[j].Rmax < db[i].Rmin) continue;
+      if (seed[j].Dmin > db[i].Dmax) continue;
+      if (seed[j].Dmax < db[i].Dmin) continue;
+      
+      for (k = 0; k < Nnew; k++) {
+	if ((seed[j].Rmin == new[k].Rmin) && 
+	    (seed[j].Rmax == new[k].Rmax)) { 
+	  goto next_seed;
+	}
+	if ((seed[j].Rmin == new[k].Rmin) ^^ 
+	    (seed[j].Rmax != new[k].Rmax)) { 
+	  fprintf (stderr, "inconsistent blocks in seed file\n");
+	  return (NULL);
+	}
+      }	
+      new[Nnew].Rmin = seed[j].Rmin;
+      new[Nnew].Rmax = seed[j].Rmax;
+      new[Nnew].Dmin = db[i].Dmin;
+      new[Nnew].Dmax = db[i].Dmax;
+      new[Nnew].depth = 2;
+      new[Nnew].child = FALSE;
+      strncpy (root, db[i].name, 5); root[5] = 0;
+      sprintf (new[Nnew].name, "%s/r%04d.cpt", root, Rnumber);
+      Rnumber ++;      
+      Nnew ++;
+      if (Nnew == NNEW) {
+	NNEW += 100;
+	REALLOCATE (new, SkyRegion, NNEW);
+      }
+    next_seed:
+    }
+    /* update db list */
+    db[i].childS = N;
+    db[i].childE = N + Nnew;
+    NREGION = N + Nnew + 100;
+    REALLOCATE (db, SkyRegion, NREGION);
+    memcpy (&db[N], new, Nnew*sizeof(SkyRegion));
+    free (new);
+    N += Nnew;
+  }
+
+  /* subdivide ra strips based on seed */
+  childS = db[db[0].childS].childS;
+  childE = db[db[0].childE - 1].childE;
+  nextS = N;
+  for (i = childS; i < childE; i++) {
+
+    Nnew = 0;
+    NNEW = 100;
+    ALLOCATE (new, SkyRegion, NNEW);
+
+    for (j = 0; j < Nseed; j++) {
+      if (seed[j].Rmin > db[i].Rmax) continue;
+      if (seed[j].Rmax < db[i].Rmin) continue;
+      if (seed[j].Dmin > db[i].Dmax) continue;
+      if (seed[j].Dmax < db[i].Dmin) continue;
+      
+      for (k = 0; k < Nnew; k++) {
+	if ((seed[j].Dmin == new[k].Dmin) && 
+	    (seed[j].Dmax == new[k].Dmax)) { 
+	  goto next_seed;
+	}
+	if ((seed[j].Dmin == new[k].Dmin) ^^ 
+	    (seed[j].Dmax != new[k].Dmax)) { 
+	  fprintf (stderr, "inconsistent blocks in seed file\n");
+	  return (NULL);
+	}
+      }	
+      new[Nnew].Dmin = seed[j].Dmin;
+      new[Nnew].Dmax = seed[j].Dmax;
+      new[Nnew].Rmin = db[i].Rmin;
+      new[Nnew].Rmax = db[i].Rmax;
+      new[Nnew].depth = 3;
+      new[Nnew].child = FALSE;
+      strcpy (new[Nnew].name, seed[j].name);
+      Nnew ++;
+      if (Nnew == NNEW) {
+	NNEW += 100;
+	REALLOCATE (new, SkyRegion, NNEW);
+      }
+    next_seed:
+    }
+    
+    /* update db list */
+    db[i].childS = N;
+    db[i].childE = N + Nnew;
+    NREGION = N + Nnew + 100;
+    REALLOCATE (db, SkyRegion, NREGION);
+    memcpy (&db[N], new, Nnew*sizeof(SkyRegion));
+    free (new);
+    N += Nnew;
+  }
+  nextE = N;
+
+  /* subdivide entries once more */
+  childS = nextS;
+  childE = nextE;
+  for (i = childS; i < childE; i++) {
+    Rnumber = 0;
+    db[i].childS = N;
+    dDec = (db[i].Dmax - db[i].Dmin) / 5.0;
+    dRa  = (db[i].Rmax - db[i].Rmin) / 5.0;
+    for (nx = 0; nx < 5; nx++) {
+      for (ny = 0; ny < 5; ny++) {
+	db[N].Dmin = db[i].Dmin + dDec * (nx + 0);
+	db[N].Dmax = db[i].Dmax + dDec * (nx + 1);
+	db[N].Rmin = db[i].Rmin + dDec * (nx + 0);
+	db[N].Rmax = db[i].Rmax + dDec * (nx + 1);
+	db[N].depth = 3;
+	db[N].child = FALSE;
+	strncpy (root, db[i].name, 10); root[10] = 0;
+	sprintf (db[N].name, "%s.%02d.cpt", root, Rnumber);
+	Rnumber ++;
+	N ++;
+	if (N == NREGION) {
+	  NREGION += 100;
+	  REALLOCATE (new, SkyRegion, NREGION);
+	}
+      }
+    }
+  }
+
+}
+
+/* region is pointer to entry in db */
+SkyRegion *SkyExtend (SkyRegion *db, SkyRegion *region) {
+
+}
+
+/* region is pointer to entry in db */
+SkyRegion *SkyContract (SkyRegion *db, SkyRegion *region) {
+
+}
+
+SkyRegion *SkyFindGCircle (SkyRegion *db, SkyCoord c1, SkyCoord c2) {
+
+}
+
+
+/* is entire db in memory, or do we load from disk for each operation? 
+   10,000 at level 3, 100,000 @ 4 : 0.5MB, 5MB */
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skyregion_gsc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skyregion_gsc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skyregion_gsc.c	(revision 22322)
@@ -0,0 +1,564 @@
+# include "dvo.h"
+# define NDECBANDS 24
+# define NDIV 4
+# define DEBUG 0
+
+static int DecLines[] = {593, 584, 551, 530, 522, 465, 406, 362, 280, 198, 123, 25, 597, 578, 574, 577, 534, 499, 442, 376, 294, 212, 144, 48};
+static double DecMin[] = {0.0, +7.5, +15.0, +22.5, +30.0, +37.5, +45.0, +52.5, +60.0, +67.5, +75.0, +82.5, -7.5, -15.0, -22.5, -30.0, -37.5, -45.0, -52.5, -60.0, -67.5, -75.0, -82.5, -90.0};
+static double DecMax[] = {+7.5, +15.0, +22.5, +30.0, +37.5, +45.0, +52.5, +60.0, +67.5, +75.0, +82.5, +90.0, 0.0, -7.5, -15.0, -22.5, -30.0, -37.5, -45.0, -52.5, -60.0, -67.5, -75.0, -82.5};
+static char *DecNames[] = {"n0000", "n0730", "n1500", "n2230", "n3000", "n3730", "n4500", "n5230", "n6000", "n6730", "n7500", "n8230", "s0000", "s0730", "s1500", "s2230", "s3000", "s3730", "s4500", "s5230", "s6000", "s6730", "s7500", "s8230"};
+
+// L0 : full sky
+// L1 : Dec bands
+// L2 : RA segments
+// L3 : GSC regions
+// L4 : GSC subdivisions
+
+// a zone contains a set of L3 regions of the same size
+typedef struct {
+  int L1band;   // parent band
+  int L3start;  // start of zone in L1 band
+  int L3end;    // end of zone in L1 band (last + 1)
+  float dDec;   // height of L3 region in zone
+  float Rmin;
+  float Rmax;
+  float Dmin;
+  float Dmax;
+  int Nset;
+} SkyRegionZone;
+
+SkyTable *SkyRegionForDecBand (char *buffer, int Nregions, char *DecName, float Dmin, float Dmax);
+SkyRegionZone *SkyRegionFindZones (SkyTable *band, int *Nzones, int parent);
+void SkyTableL2fromZone (SkyTable *L2, SkyTable *L3, SkyTable *L4, SkyTable *band, SkyRegionZone *zone, int parent);
+void SkyTableL3fromL2 (SkyRegion *L2, SkyTable *L3, SkyTable *L4, SkyTable *band, int Ns, int Ne);
+void SkyTableL4fromL3 (SkyRegion *L3, SkyTable *L4);
+
+void SkyTableSort (SkyTable *table);
+void SkyTableAppend (SkyTable *old, SkyTable *new, int Nprev);
+int NsetForDecRange (float dDec);
+void SkyRegionPrint (SkyRegion *region);
+
+SkyTable *SkyTableFromGSC (char *filename, int depth, int VERBOSE) {
+
+  int i, j, skipLines, Nzones;
+
+  Header theader;
+  FTable ftable;
+  SkyTable *skytable;
+  SkyTable *band;
+  SkyTable L0, L1, L2, L3, L4;
+  SkyRegionZone *zones;
+
+  FILE *f;
+  
+  // ohana_memdump (0);
+
+  if (filename == NULL) {
+    if (VERBOSE) fprintf (stderr, "template GSC Region file not defined\n");
+    return (NULL);
+  }
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    if (VERBOSE) fprintf (stderr, "can't find GSC Region file %s\n", filename);
+    return (NULL);
+  }
+
+/* load in table data */
+  ftable.header = &theader;
+  if (!gfits_fread_ftable (f, &ftable, "REGIONS")) {
+    if (VERBOSE) fprintf (stderr, "can't read GSC Region table\n");
+    fclose (f);
+    return (NULL);
+  }
+
+/* generate the regions for each level independently. indices (parent,childE,childS) 
+   refer to the value within the independent group.  at the end, we combine and adjust
+   the indices */
+
+/* L0 : full sky */
+  L0.Nregions = 1;
+  ALLOCATE (L0.regions, SkyRegion, L0.Nregions);
+  L0.regions[0].Rmin 	=   0;
+  L0.regions[0].Rmax 	= 360;
+  L0.regions[0].Dmin 	= -90;
+  L0.regions[0].Dmax 	= +90;
+  L0.regions[0].index  	=  0;
+  L0.regions[0].depth  	=  0;
+  L0.regions[0].parent 	= -1;
+  L0.regions[0].child  	=  TRUE;
+  L0.regions[0].table  	= -1;
+  L0.regions[0].childS  =  0;
+  L0.regions[0].childE  =  NDECBANDS;
+  strcpy (L0.regions[0].name, "fullsky");
+  if (DEBUG) SkyRegionPrint (&L0.regions[0]);
+
+  /* allocate space for all levels */
+  L1.Nregions = L2.Nregions = L3.Nregions = L4.Nregions = 0;
+  ALLOCATE (L1.regions, SkyRegion, 1);
+  ALLOCATE (L2.regions, SkyRegion, 1);
+  ALLOCATE (L3.regions, SkyRegion, 1);
+  ALLOCATE (L4.regions, SkyRegion, 1);
+
+  // skipLines = 0;
+  // for (i = 0; i < 16; i++) skipLines += DecLines[i];
+  // for (i = 16; i < 17; i++) {
+
+  /* L1 : dec bands */
+  skipLines = 0;
+  for (i = 0; i < NDECBANDS; i++) {
+    L1.regions[i].Rmin     = 0;
+    L1.regions[i].Rmax     = 360;
+    L1.regions[i].Dmin     = DecMin[i];
+    L1.regions[i].Dmax     = DecMax[i];
+    L1.regions[i].index    = i;
+    L1.regions[i].depth    = 1;
+    L1.regions[i].parent   = 0;
+    L1.regions[i].child    = TRUE;
+    L1.regions[i].table    = -1;
+    strcpy (L1.regions[i].name, DecNames[i]);
+    if (DEBUG) SkyRegionPrint (&L1.regions[i]);
+
+    /* build the L2 regions for this L1 region (based on zones) */
+    L1.regions[i].childS   = L2.Nregions;
+
+    /* load all GSC Regions in this band */
+    band = SkyRegionForDecBand (&ftable.buffer[skipLines*48], DecLines[i], DecNames[i], L1.regions[i].Dmin, L1.regions[i].Dmax);
+    skipLines += DecLines[i];
+
+    /* sort the band regions by Rmin */
+    SkyTableSort (band);
+    
+    // free zones below
+    zones = SkyRegionFindZones (band, &Nzones, i);
+
+    /* subdivide each zone */
+    for (j = 0; j < Nzones; j++) {
+      if (DEBUG >= 1) fprintf (stderr, "zone: %d : %f - %f : %f - %f\n", j, zones[j].Rmin, zones[j].Rmax, zones[j].Dmin, zones[j].Dmax);
+      SkyTableL2fromZone (&L2, &L3, &L4, band, &zones[j], i);
+    }
+    free (zones);
+    SkyTableFree (band);
+
+    /* at end of loop, L2.Nregions has been updated to end of L2.regions for L1.region */
+    L1.regions[i].childE   = L2.Nregions;
+
+    L1.Nregions ++;
+    REALLOCATE (L1.regions, SkyRegion, L1.Nregions + 1);
+  }
+
+  /* merge L1 into L0 */
+  ALLOCATE (skytable, SkyTable, 1);
+  ALLOCATE (skytable[0].regions, SkyRegion, 1);
+  skytable[0].Nregions = 0;
+
+  SkyTableAppend (skytable, &L0, 0);
+  SkyTableAppend (skytable, &L1, skytable[0].Nregions - L0.Nregions);
+  SkyTableAppend (skytable, &L2, skytable[0].Nregions - L1.Nregions);
+  SkyTableAppend (skytable, &L3, skytable[0].Nregions - L2.Nregions);
+  SkyTableAppend (skytable, &L4, skytable[0].Nregions - L3.Nregions);
+
+  free (L0.regions);
+  free (L1.regions);
+  free (L2.regions);
+  free (L3.regions);
+  free (L4.regions);
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  /* fix the depth elements and create the filename array */
+  ALLOCATE (skytable[0].filename, char *, skytable[0].Nregions);
+  for (i = 0; i < skytable[0].Nregions; i++) {
+    skytable[0].filename[i] = NULL;
+    skytable[0].regions[i].table = (skytable[0].regions[i].depth == depth);
+  }
+
+  // ohana_memdump (0);
+  return (skytable);
+}
+
+// allocates band, band->regions
+SkyTable *SkyRegionForDecBand (char *buffer, int Nregions, char *DecName, float DminBand, float DmaxBand) {
+
+  int i;
+  double Rmin, Rmax, Dmin, Dmax;
+  char temp[80], name[80];
+  SkyTable *band;
+  SkyRegion *regions;
+
+  ALLOCATE (band, SkyTable, 1);
+  ALLOCATE (regions, SkyRegion, Nregions);
+  for (i = 0; i < Nregions; i++) {
+    strncpy (temp, &buffer[i*48], 48);
+    temp[49] = 0;
+    hstgsc_hms_to_deg (&Rmin, &Rmax, &Dmin, &Dmax, &temp[7]);
+    if (Dmax < Dmin) SWAP (Dmin, Dmax);
+
+    /* check that we are in correct Dec band */
+    if ((Dmax < DminBand) || (Dmin > DmaxBand)) {
+      fprintf (stderr, "error with raw table: table mis-match\n");
+      fprintf (stderr, "line: %s\n", temp);
+      fprintf (stderr, "region %d: %f - %f vs %f - %f\n", i, Dmin, Dmax, DminBand, DmaxBand);
+      exit (2);
+    }
+
+    /* regions near 0,360 boundary have Rmax == 0.0 */
+    if (Rmax < Rmin) Rmax += 360.0;
+
+    regions[i].Dmin = Dmin;
+    regions[i].Dmax = Dmax;
+    regions[i].Rmin = Rmin;
+    regions[i].Rmax = Rmax;
+
+    temp[5] = 0;
+    sprintf (name, "%s/%s", DecName, &temp[1]);
+    strcpy (regions[i].name, name);
+  }
+  band[0].filename = NULL;
+  band[0].regions = regions;
+  band[0].Nregions = Nregions;
+  return (band);
+}
+
+/* sort skytable by Rmin */
+void SkyTableSort (SkyTable *table) {
+
+  SkyRegion *regions;
+  char **filename = NULL;
+
+  regions = table[0].regions;
+  filename = table[0].filename;
+
+# define SWAPFUNC(A,B){ \
+  if (table[0].filename) { \
+    char     *tempfile = filename[A]; filename[A] = filename[B]; filename[B] = tempfile; \
+  } \
+  SkyRegion tempregion = regions[A]; regions[A] = regions[B]; regions[B] = tempregion; \
+}
+# define COMPARE(A,B)(regions[A].Rmin < regions[B].Rmin)
+
+  OHANA_SORT (table[0].Nregions, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+}
+
+/* given a complete set of regions in a Dec band, subdivide into zones of equal-sized regions.
+ * this function assumes the DEC band is sliced first by lines of constant RA, then subdivided
+ * with lines of constant DEC.  Groups ('zones') of these smaller boxes have the same size in
+ * delta-Dec.  We are trying to fine the boundaries of these zones.  The initial assumption
+ * breaks down for the pole region (86.25 < DEC < 90), where the entire range is divided into a
+ * single circular patch.  we need to treat it differently.
+ */
+
+// allocates zones
+SkyRegionZone *SkyRegionFindZones (SkyTable *band, int *Nzones, int parent) {
+  
+  float Dmin, Dmax, dDec, dDecZone;
+  int i, j, Nz, NZ, Nregions, poleRegion;
+  SkyRegion *regions;
+  SkyRegion tempregion;
+  SkyRegionZone *zones;
+  char basename[64];
+  
+  regions = band[0].regions;
+  Nregions = band[0].Nregions;
+
+  NZ = 10;
+  ALLOCATE (zones, SkyRegionZone, NZ);
+ 
+  /* the pole regions need to be separately handled.  skip in analysis below */
+  poleRegion = 0;
+
+  /* search for transitions in the region dDec, find the max Dec range */
+  Nz = -1;
+  dDecZone = -1;
+  Dmin = +90;
+  Dmax = -90;
+  for (i = 0; i < Nregions; i++) {
+    if ((regions[i].Dmin >= +86.00) || (regions[i].Dmax <= -86.00)) {
+      /* drop the pole region (re-generated below) */
+      poleRegion = SIGN(regions[i].Dmin);
+      Nregions --;
+      tempregion = regions[i];
+      for (j = i; j < Nregions; j++) {
+	regions[j] = regions[j+1];
+      }
+      i--;
+      continue;
+    }
+    /* is this region in the current zone? */
+    Dmin = MIN (regions[i].Dmin, Dmin);
+    Dmax = MAX (regions[i].Dmax, Dmax);
+    dDec = regions[i].Dmax - regions[i].Dmin;
+    if (fabs(dDec - zones[Nz].dDec) > 0.001) {
+      /* we've found the end of the current zone; set the ending info */
+      if (Nz >= 0) {
+	zones[Nz].L3end = i;
+	zones[Nz].Rmax = regions[i-1].Rmax;
+      }
+
+      /* go to the next zone */
+      Nz++;
+      CHECK_REALLOCATE (zones, SkyRegionZone, NZ, Nz, 10);
+
+      /* start info for the new zone */
+      dDecZone = dDec;
+      zones[Nz].Rmin = regions[i].Rmin;
+      zones[Nz].dDec = dDecZone;
+      zones[Nz].L3start = i;
+      zones[Nz].L1band = parent;
+      zones[Nz].Nset = NsetForDecRange (zones[Nz].dDec);
+    }
+  }
+  zones[Nz].L3end = i;
+  zones[Nz].Rmax = regions[i-1].Rmax;
+  Nz ++;
+
+  for (i = 0; i < Nz; i++) {
+    zones[i].Dmin = Dmin;
+    zones[i].Dmax = Dmax;
+  }
+  
+  /* the pole region is mis-allocated in the gsc table to L3.  elevate it 
+     to L2 (zone) and subdivide */
+  if (poleRegion) {
+    CHECK_REALLOCATE (zones, SkyRegionZone, NZ, Nz, 10);
+    /* pole region @ L2 */
+    zones[Nz].Rmin = 0.0;
+    zones[Nz].Rmax = 360.0;
+    if (poleRegion > 0) {
+      zones[Nz].Dmin = +86.25;
+      zones[Nz].Dmax = +90.00;
+      strcpy (basename, "n8230/pole");
+    } else {
+      zones[Nz].Dmin = -90.00;
+      zones[Nz].Dmax = -86.25;
+      strcpy (basename, "s8230/pole");
+    }
+    zones[Nz].dDec = 90.00 - 86.25;
+    zones[Nz].L3start = Nregions;
+    zones[Nz].L3end = Nregions + 5;
+    zones[Nz].L1band = parent;
+
+    band[0].Nregions += 4;
+    REALLOCATE (band[0].regions, SkyRegion, band[0].Nregions);
+    for (i = 0; i < 4; i++) {
+      band[0].regions[Nregions + i].Rmin = i * 90.0;
+      band[0].regions[Nregions + i].Rmax = i * 90.0 + 90.0;
+      if (poleRegion > 0) {
+	band[0].regions[Nregions + i].Dmin = +86.250;
+	band[0].regions[Nregions + i].Dmax = +88.125;
+      } else {
+	band[0].regions[Nregions + i].Dmin = -88.125;
+	band[0].regions[Nregions + i].Dmax = -86.250;
+      }
+      sprintf (band[0].regions[Nregions + i].name, "%s.%d", basename, i);
+    }
+    band[0].regions[Nregions + i].Rmin = 0.0;
+    band[0].regions[Nregions + i].Rmax = 360.0;
+    if (poleRegion > 0) {
+      band[0].regions[Nregions + i].Dmin = +88.125;
+      band[0].regions[Nregions + i].Dmax = +90.000;
+    } else {
+      band[0].regions[Nregions + i].Dmin = -90.000;
+      band[0].regions[Nregions + i].Dmax = -88.125;
+    }
+    sprintf (band[0].regions[Nregions + i].name, "%s.%d", basename, i);
+
+    zones[0].Nset = 6;
+    zones[1].Nset = 5;
+
+    Nz ++;
+  }
+
+  *Nzones = Nz;
+  return (zones);
+}
+
+int NsetForDecRange (float dDec) {
+
+  int Ndiv, Nset;
+  
+  /* max segment size is ~7.5 x 15 degrees */
+  Ndiv = (int) (0.5 + 7.5 / dDec);
+  Nset = 2*Ndiv*Ndiv;
+  return (Nset);
+}
+  
+// memory neutral
+void SkyTableL2fromZone (SkyTable *L2, SkyTable *L3, SkyTable *L4, SkyTable *band, SkyRegionZone *zone, int parent) {
+
+  int i, Nr, Ns, Ne;
+  char *p, name[80];
+
+  Nr = L2[0].Nregions;
+  REALLOCATE (L2[0].regions, SkyRegion, Nr + 1);
+  
+  /* divide this zone into L2 regions with Nset L3 regions each (fewer on ends) */
+  for (i = zone[0].L3start; i < zone[0].L3end; i += zone[0].Nset) {
+    Ns = i; 
+    Ne = MIN (i + zone[0].Nset, zone[0].L3end);
+
+    L2[0].regions[Nr].Rmin = band[0].regions[Ns].Rmin;
+    L2[0].regions[Nr].Rmax = band[0].regions[Ne - 1].Rmax;
+    L2[0].regions[Nr].Dmin = zone[0].Dmin;
+    L2[0].regions[Nr].Dmax = zone[0].Dmax;
+    
+    L2[0].regions[Nr].index    =  Nr;
+    L2[0].regions[Nr].depth    =  2;
+    L2[0].regions[Nr].table    =  -1;
+    L2[0].regions[Nr].parent   =  parent;
+    L2[0].regions[Nr].child    =  FALSE;
+    L2[0].regions[Nr].childS   =  0;
+    L2[0].regions[Nr].childE   =  0;
+
+    /* define names for L2 regions */
+    p = strchr (band[0].regions[Ns].name, '/');
+    if (p == NULL) {
+      fprintf (stderr, "programming error in SkyTableL2fromZone\n");
+      exit (2);
+    }
+    *p = 0;
+    sprintf (name, "%s/z%03d", band[0].regions[Ns].name, Nr);
+    *p = '/';
+    strcpy (L2[0].regions[Nr].name, name);
+    if (DEBUG >= 2) SkyRegionPrint (&L2[0].regions[Nr]);
+
+    /* childS and childE are set in SkyTableL3fromL2 */
+    SkyTableL3fromL2 (&L2[0].regions[Nr], L3, L4, band, Ns, Ne);
+
+    Nr++;
+    REALLOCATE (L2[0].regions, SkyRegion, Nr + 1);
+  }
+  L2[0].Nregions = Nr;
+}
+
+// memory neutral
+void SkyTableL3fromL2 (SkyRegion *L2, SkyTable *L3, SkyTable *L4, SkyTable *band, int Ns, int Ne) {
+
+  int i, Nr;
+
+  Nr = L3[0].Nregions;
+  L3[0].Nregions += Ne - Ns;
+  REALLOCATE (L3[0].regions, SkyRegion, L3[0].Nregions);
+
+  L2[0].child  = TRUE;
+  L2[0].childS = Nr;
+  L2[0].childE = L3[0].Nregions;
+
+  /* copy the band entries corresponding to this region in the L3 table */
+  for (i = Ns; i < Ne; i++) {
+
+    L3[0].regions[Nr] = band[0].regions[i];
+    
+    L3[0].regions[Nr].index    =  Nr;
+    L3[0].regions[Nr].depth    =  3;
+    L3[0].regions[Nr].table    =  -1;
+    L3[0].regions[Nr].parent   =  L2[0].index;
+    L3[0].regions[Nr].child    =  FALSE;
+    L3[0].regions[Nr].childS   =  0;
+    L3[0].regions[Nr].childE   =  0;
+
+    if (DEBUG >= 3) SkyRegionPrint (&L3[0].regions[Nr]);
+    /* name is set for the band in SkyRegionForDecBand */
+
+    /* childS and childE are set in SkyTableL3fromL3 */
+    SkyTableL4fromL3 (&L3[0].regions[Nr], L4);
+    Nr++;
+  }
+
+  return;
+}
+
+// memory neutral
+void SkyTableL4fromL3 (SkyRegion *L3, SkyTable *L4) {
+
+  int nx, ny, Nr, Nbox;
+  double Rmin, Dmin, dR, dD;
+  char name[80];
+
+  Nr = L4[0].Nregions;
+  L4[0].Nregions += NDIV*NDIV;
+  REALLOCATE (L4[0].regions, SkyRegion, L4[0].Nregions);
+
+  L3[0].child  = TRUE;
+  L3[0].childS = Nr;
+  L3[0].childE = L4[0].Nregions;
+
+  // XXX handle the pole regions just a bit differently...
+
+  /* subdivide L3 into NDIV boxes */
+  Rmin = L3[0].Rmin;
+  Dmin = L3[0].Dmin;
+  dR = (L3[0].Rmax - L3[0].Rmin) / NDIV;
+  dD = (L3[0].Dmax - L3[0].Dmin) / NDIV;
+
+  Nbox = 0;
+  for (ny = 0; ny < NDIV; ny ++) {
+    for (nx = 0; nx < NDIV; nx ++) {
+      L4[0].regions[Nr].Rmin     = Rmin  + (nx + 0)*dR;
+      L4[0].regions[Nr].Rmax     = Rmin  + (nx + 1)*dR;
+      L4[0].regions[Nr].Dmin     = Dmin + (ny + 0)*dD;
+      L4[0].regions[Nr].Dmax     = Dmin + (ny + 1)*dD;
+
+      L4[0].regions[Nr].index    =  Nr;
+      L4[0].regions[Nr].depth    =  4;
+      L4[0].regions[Nr].table    =  -1;
+      L4[0].regions[Nr].parent   =  L3[0].index;
+      L4[0].regions[Nr].child    =  FALSE;
+      L4[0].regions[Nr].childS   =  0;
+      L4[0].regions[Nr].childE   =  0;
+
+      sprintf (name, "%s.%02d", L3[0].name, Nbox);
+      strcpy (L4[0].regions[Nr].name, name);
+      if (DEBUG >= 4) SkyRegionPrint (&L4[0].regions[Nr]);
+
+      Nr ++;
+      Nbox ++;
+    }
+  }
+  return;
+}
+
+// memory neutral
+void SkyTableAppend (SkyTable *old, SkyTable *new, int Nprev) {
+
+  int i, Nold;
+
+  Nold = old[0].Nregions;
+
+  old[0].Nregions += new[0].Nregions;
+  REALLOCATE (old[0].regions, SkyRegion, old[0].Nregions);
+
+  for (i = Nprev; i < Nold; i++) {
+    old[0].regions[i].childS += Nold;
+    old[0].regions[i].childE += Nold;
+  }
+
+  for (i = 0; i < new[0].Nregions; i++) {
+    old[0].regions[i + Nold] = new[0].regions[i];
+    old[0].regions[i + Nold].parent += Nprev;
+  }
+  return;
+}
+
+// memory neutral
+void SkyRegionPrint (SkyRegion *region) {
+
+  int i;
+
+  fprintf (stderr, "L%d:", region[0].depth);
+  for (i = 0; i < region[0].depth; i++) {
+    fprintf (stderr, " ");
+  } 
+  fprintf (stderr, "%s : %f - %f : %f - %f\n", region[0].name, region[0].Rmin, region[0].Rmax, region[0].Dmin, region[0].Dmax);
+  return;
+}
+
+/***
+    notes and questions:
+    1) is the regions.index value used?
+
+
+***/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skyregion_io.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skyregion_io.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skyregion_io.c	(revision 22322)
@@ -0,0 +1,170 @@
+# include "dvo.h"
+
+SkyTable *SkyTableLoad (char *filename, int VERBOSE) {
+
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  SkyTable *skytable;
+  FILE *f;
+  int i;
+  
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    if (VERBOSE) fprintf (stderr, "can't find Sky Region file %s\n", filename);
+    return (NULL);
+  }
+
+  /* load in table data */
+  ftable.header = &theader;
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read Sky Region header\n");
+    fclose (f);
+    return (NULL);
+  }
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read Sky Region matrix\n");
+    gfits_free_header (&header);
+    fclose (f);
+    return (NULL);
+  }
+  if (!gfits_fread_ftable (f, &ftable, "SKY_REGION")) {
+    if (VERBOSE) fprintf (stderr, "can't read Sky Region table\n");
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    fclose (f);
+    return (NULL);
+  }
+
+  ALLOCATE (skytable, SkyTable, 1);
+  skytable[0].regions = gfits_table_get_SkyRegion (&ftable, &skytable[0].Nregions, NULL);
+  ALLOCATE (skytable[0].filename, char *, skytable[0].Nregions);
+  for (i = 0; i < skytable[0].Nregions; i++) {
+    skytable[0].filename[i] = NULL;
+  }
+  
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+
+  return (skytable);
+}
+
+int SkyTableSave (SkyTable *skytable, char *filename) {
+
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  FILE *f;
+
+  /* make phu header (no matrix needed) */
+  ftable.header = &theader;
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+
+  gfits_table_set_SkyRegion (&ftable, skytable[0].regions, skytable[0].Nregions);
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) { 
+    fprintf (stderr, "cannot open %s for output\n", filename);
+    return (FALSE);
+  }
+  
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table  (f, &ftable);
+  fclose (f);
+
+  return (TRUE);
+}
+
+SkyTable *SkyTableLoadOptimal (char *catdir, char *skyfile, char *gscfile, int depth, int verbose) {
+
+  char filename[256];
+  struct stat filestat;
+  SkyTable *sky;
+
+  /* first option: CATDIR/SkyTable.fits */
+  sprintf (filename, "%s/SkyTable.fits", catdir);
+  if (stat (filename, &filestat)) goto SKYFILE;
+  if (!check_file_access (filename, FALSE, verbose)) goto SKYFILE;
+  sky = SkyTableLoad (filename, verbose);
+  if (sky == NULL) {
+    fprintf (stderr, "error loading sky table\n");
+    exit (1);
+  }
+  return (sky);
+
+SKYFILE:
+  /* second option: SKYFILE */
+  if (skyfile == NULL) goto GSCFILE;
+  if (skyfile[0] != 0) goto GSCFILE;
+  if (stat (skyfile, &filestat)) goto GSCFILE;
+  if (!check_file_access (skyfile, FALSE, verbose)) goto GSCFILE;
+  sky = SkyTableLoad (skyfile, verbose);
+  if (sky == NULL) {
+    fprintf (stderr, "error loading sky table\n");
+    return (NULL);
+  }
+  /* set the depths to the default depth */
+  SkyTableSetDepth (sky, depth);
+
+  /* create CATDIR copy */
+  sprintf (filename, "%s/SkyTable.fits", catdir);
+  check_file_access (filename, FALSE, verbose);
+  if (!SkyTableSave (sky, filename)) return NULL;
+
+  gfits_convert_SkyRegion (sky[0].regions, sizeof (SkyTable), sky[0].Nregions);
+  return (sky);
+
+GSCFILE:
+
+  /* third option: GSCFILE */
+  sky = SkyTableFromGSC (gscfile, depth, verbose);
+  if (sky == NULL) {
+    fprintf (stderr, "error loading sky table\n");
+    return (NULL);
+  }
+
+  /* create CATDIR copy */
+  sprintf (filename, "%s/SkyTable.fits", catdir);
+  check_file_access (filename, FALSE, verbose);
+  if (!SkyTableSave (sky, filename)) return NULL;
+
+  gfits_convert_SkyRegion (sky[0].regions, sizeof (SkyRegion), sky[0].Nregions);
+  return (sky);
+}
+
+int SkyListSetFilenames (SkyList *list, char *path, char *ext) {
+
+  int i;
+  char line[256];
+
+  // this generates the names, be sure to free when not needed
+  for (i = 0; i < list[0].Nregions; i++) {
+    sprintf (line, "%s/%s.%s", path, list[0].regions[i][0].name, ext);
+    list[0].filename[i] = strcreate (line);
+  }
+
+  return (TRUE);
+}
+
+int SkyTableSetFilenames (SkyTable *sky, char *path, char *ext) {
+
+  int i;
+  char line[256];
+
+  // this generates the names, be sure to free when not needed
+  for (i = 0; i < sky[0].Nregions; i++) {
+    sprintf (line, "%s/%s.%s", path, sky[0].regions[i].name, ext);
+    sky[0].filename[i] = strcreate (line);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skyregion_ops.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skyregion_ops.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/skyregion_ops.c	(revision 22322)
@@ -0,0 +1,418 @@
+# include "dvo.h"
+
+/* each region is bounded on the sky by lines of constant RA & DEC.
+   the region represents a specified depth, and it may or may not have
+   children.  The start and end of the children in the array are childS and childE 
+   If the region at the given depth is populated with an object table, then 'object' is TRUE.
+   If the region at the given depth is populated with an image table, then 'image' is TRUE.
+   I have defined no accelerators other than the table hierarchy */
+
+/* find region which overlaps c at given depth (-1 : populated ) */
+SkyList *SkyRegionByPoint (SkyTable *table, int depth, double ra, double dec) {
+  
+  int i, Ns, Ne, No;
+  SkyRegion *region;
+  SkyList *list;
+
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions,  SkyRegion *, 1);
+  ALLOCATE (list[0].filename,  char *, 1);
+  list[0].Nregions = 0;
+  list[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  region = table[0].regions;
+
+  Ns = 0;
+  Ne = 1;
+
+  while (1) {
+    No = -1;
+    for (i = Ns; (i < Ne) && (i < table[0].Nregions); i++) {
+      if (ra  < region[i].Rmin) continue;
+      if (ra  > region[i].Rmax) continue;
+      if (dec < region[i].Dmin) continue;
+      if (dec > region[i].Dmax) continue;
+      No = i;
+      break;
+    }
+    if (No == -1) return (list);
+    if ((depth == -1) && (region[No].table)) break;
+    if (depth == region[No].depth) break;
+    if ((depth > region[No].depth) && !region[No].child) return (list);
+
+    /* need to check Ns, Ne, or guarantee valid range */
+    Ns = region[No].childS;
+    Ne = region[No].childE;
+  }
+
+  list[0].regions[0] = &region[No];
+  list[0].filename[0] = table[0].filename[No];
+  list[0].Nregions = 1;
+  return (list);
+}
+
+/* find regions at all levels which match name */
+/* XXX : need to add support for selected level / populated level */
+SkyList *SkyListByName (SkyTable *table, char *name) {
+
+  int i, Nchar, N, NREGIONS;
+  SkyList *list;
+  SkyRegion *region;
+  
+  N = 0;
+  NREGIONS = 10;
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions, SkyRegion *, NREGIONS);
+  ALLOCATE (list[0].filename, char *, NREGIONS);
+  list[0].Nregions = N;
+  list[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  region = table[0].regions;
+
+  Nchar = strlen (name);
+
+  for (i = 0; i < table[0].Nregions; i++) {
+    if (strncasecmp (region[i].name, name, Nchar)) continue;
+
+    list[0].regions[N] = &region[i];
+    list[0].filename[N] = table[0].filename[i];
+    N++;
+    if (N >= NREGIONS) {
+	NREGIONS += 10;
+	REALLOCATE (list[0].regions, SkyRegion *, NREGIONS);
+	REALLOCATE (list[0].filename, char *, NREGIONS);
+    }
+  }
+  list[0].Nregions = N;
+  return (list);
+}
+
+/* find regions at all levels which overlap c */
+SkyList *SkyListByPoint (SkyTable *table, double ra, double dec) {
+
+  int i, Ns, Ne, No, N, NREGIONS;
+  SkyList *list;
+  SkyRegion *region;
+  
+  N = 0;
+  NREGIONS = 10;
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions,  SkyRegion *, NREGIONS);
+  ALLOCATE (list[0].filename,  char *, NREGIONS);
+  list[0].Nregions = N;
+  list[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  region = table[0].regions;
+
+  Ns = 0;
+  Ne = 1;
+
+  while (1) {
+    No = -1;
+    for (i = Ns; (i < Ne) && (i < table[0].Nregions); i++) {
+      if (ra  < region[i].Rmin) continue;
+      if (ra  > region[i].Rmax) continue;
+      if (dec < region[i].Dmin) continue;
+      if (dec > region[i].Dmax) continue;
+      No = i;
+      break;
+    }
+    if (No == -1) return (list);
+
+    list[0].regions[N] = &region[No];
+    list[0].filename[N] = table[0].filename[No];
+    N++;
+    if (N >= NREGIONS) {
+	NREGIONS += 10;
+	REALLOCATE (list[0].regions, SkyRegion *, NREGIONS);
+	REALLOCATE (list[0].filename, char *, NREGIONS);
+    }
+    list[0].Nregions = N;
+
+    /* need to check Ns, Ne, or guarantee valid range */
+    if (region[No].child) {
+      Ns = region[No].childS;
+      Ne = region[No].childE;
+    } else {
+      return (list);
+    }
+  }
+}
+
+SkyList *SkyListByRadius (SkyTable *table, int depth, double RA, double DEC, double radius) {
+
+  double rad;
+  double Rmin, Rmax, Dmin, Dmax;
+  SkyList *list;
+
+  Dmin = DEC - radius;
+  Dmax = DEC + radius;
+
+  if ((Dmin <= -89) || (Dmax >= 89)) {
+    Rmin = 0;
+    Rmax = 360;
+  } else {
+    rad = MAX (radius / (cos(Dmin*RAD_DEG)), radius / (cos(Dmax*RAD_DEG)));
+    Rmin = RA - rad;
+    Rmax = RA + rad;
+  }
+
+  list = SkyListByBounds (table, depth, Rmin, Rmax, Dmin, Dmax);
+  return (list);
+}
+
+SkyList *SkyListByPatch (SkyTable *table, int depth, SkyRegion *patch) {
+
+  SkyList *list;
+
+  list = SkyListByBounds (table, depth, patch[0].Rmin, patch[0].Rmax, patch[0].Dmin, patch[0].Dmax);
+  return (list);
+}
+
+/* user must be careful about mosaic registration */
+SkyList *SkyListByImage (SkyTable *table, int depth, Image *image) {
+
+  int j;
+  SkyList *list;
+  double r, d, X[4], Y[4];
+  double Rmin, Rmax, Dmin, Dmax;
+  
+  // XXX EAM : image/mosaic MUST be registered (if WRP) 
+  SetImageCorners (X, Y, image);
+
+  Rmin = 360.0;
+  Rmax =   0.0;
+  Dmin = +90.0;
+  Dmax = -90.0;
+
+  /* does this work at 0,360 boundary? XY_to_RD must return 
+     coord offsets relative to CRVAL1,2 (ie, NOT renormalize) */
+  for (j = 0; j < 4; j++) {
+    /* XY_to_RD is two-level if ctype == WRP */
+    XY_to_RD (&r, &d, X[j], Y[j], &image[0].coords);
+    Rmin = MIN (Rmin, r);
+    Rmax = MAX (Rmax, r);
+    Dmin = MIN (Dmin, d);
+    Dmax = MAX (Dmax, d);
+  }
+
+  list = SkyListByBounds (table, depth, Rmin, Rmax, Dmin, Dmax);
+  return (list);
+}
+
+SkyList *SkyListByBounds (SkyTable *table, int depth, double Rmin, double Rmax, double Dmin, double Dmax) {
+
+  int i, j, Ns;
+  SkyList *list, *extra;
+
+  /* rationalize Rmin, Rmax */
+  while (Rmin <   0.0) Rmin += 360;
+  while (Rmin > 360.0) Rmin -= 360;
+  while (Rmax <   0.0) Rmax += 360;
+  while (Rmax > 360.0) Rmax -= 360;
+
+  /* handle 0,360 boundary requests */
+  /* this is probably wrong: I will get duplicates for all Dec bands... */
+  if (Rmin > Rmax) {
+    list = SkyListChildrenByBounds (table, -1, depth, 0.0, Rmax, Dmin, Dmax);
+
+    extra = SkyListChildrenByBounds (table, -1, depth, Rmin, 360.0, Dmin, Dmax);
+    REALLOCATE (list[0].regions, SkyRegion *, list[0].Nregions + extra[0].Nregions);
+    REALLOCATE (list[0].filename, char *, list[0].Nregions + extra[0].Nregions);
+    Ns = list[0].Nregions;
+    for (i = 0; i < extra[0].Nregions; i++) {
+      // search for pre-existing match
+      for (j = 0; j < list[0].Nregions; j++) {
+	if (list[0].regions[j] == extra[0].regions[i]) {
+	  goto skip;
+	}
+      }
+      list[0].regions[Ns] = extra[0].regions[i];
+      list[0].filename[Ns] = extra[0].filename[i];
+      Ns ++;
+    skip:
+      continue;
+    }
+    list[0].Nregions = Ns;
+    SkyListFree (extra);
+  } else {
+    list = SkyListChildrenByBounds (table, -1, depth, Rmin, Rmax, Dmin, Dmax);
+  }
+
+  return (list);
+}
+
+SkyList *SkyListChildrenByBounds (SkyTable *table, int No, int depth, double Rmin, double Rmax, double Dmin, double Dmax) {
+
+  int i, j, Ns, Ne, Nnew, NNEW;
+  int append;
+  SkyList *children;
+  SkyList *list;
+  SkyRegion *region;
+
+  Nnew = 0;
+  NNEW = 50;
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions, SkyRegion *, NNEW);
+  ALLOCATE (list[0].filename, char *, NNEW);
+  list[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  region = table[0].regions;
+
+  if (No == -1) {
+    Ns = 0;
+    Ne = 1;
+  } else {
+    Ns = region[No].childS;
+    Ne = region[No].childE;
+  }
+
+  for (i = Ns; (i < Ne) && (i < table[0].Nregions); i++) {
+    if (Rmax < region[i].Rmin) continue;
+    if (Rmin > region[i].Rmax) continue;
+    if (Dmax < region[i].Dmin) continue;
+    if (Dmin > region[i].Dmax) continue;
+
+    if ((depth > region[i].depth) && (region[i].child == FALSE)) continue;
+
+    append = FALSE;
+    append |= (depth > region[i].depth) && (region[i].child == TRUE);
+    append |= (depth ==             -1) && (region[i].table == FALSE);
+
+    if (append) {
+      /* append children to new */
+      children = SkyListChildrenByBounds (table, i, depth, Rmin, Rmax, Dmin, Dmax);
+      if (Nnew + children[0].Nregions >= NNEW) {
+	NNEW = Nnew + children[0].Nregions + 50;
+	REALLOCATE (list[0].regions, SkyRegion *, NNEW);
+	REALLOCATE (list[0].filename, char *, NNEW);
+      }
+      for (j = 0; j < children[0].Nregions; j++) {
+	list[0].regions[Nnew + j] = children[0].regions[j];
+	list[0].filename[Nnew + j] = children[0].filename[j];
+      }
+      Nnew += children[0].Nregions;
+      SkyListFree (children);
+    } else {
+      list[0].regions[Nnew] = &region[i];
+      list[0].filename[Nnew] = table[0].filename[i];
+      Nnew ++;
+      if (Nnew >= NNEW) {
+	  NNEW += 50;
+	  REALLOCATE (list[0].regions, SkyRegion *, NNEW);
+	  REALLOCATE (list[0].filename, char *, NNEW);
+      }
+    }
+  }
+
+  list[0].Nregions = Nnew;
+  return (list);
+}
+
+/* XXX EAM : this should go elsewhere (libdvo?) */
+void SetImageCorners (double *X, double *Y, Image *image) {
+
+  if (!strcmp(&image[0].coords.ctype[4], "-DIS")) {
+    X[0] = -0.5*image[0].NX; Y[0] = -0.5*image[0].NY;
+    X[1] = +0.5*image[0].NX; Y[1] = -0.5*image[0].NY;
+    X[2] = +0.5*image[0].NX; Y[2] = +0.5*image[0].NY;
+    X[3] = -0.5*image[0].NX; Y[3] = +0.5*image[0].NY;
+  } else {
+    X[0] = 0;           Y[0] = 0;
+    X[1] = image[0].NX; Y[1] = 0;
+    X[2] = image[0].NX; Y[2] = image[0].NY;
+    X[3] = 0;           Y[3] = image[0].NY;
+  }
+}
+
+int SkyTableSetDepth (SkyTable *sky, int depth) {
+
+  int i;
+
+  for (i = 0; i < sky[0].Nregions; i++) {
+    sky[0].regions[i].table = (sky[0].regions[i].depth == depth);
+  }
+  return (TRUE);
+}
+
+int SkyListFree (SkyList *list) {
+
+  int i;
+
+  /* XXX do we need to free the filename array as well? */
+  if (list == NULL) return (TRUE);
+  if (list[0].regions != NULL) {
+    if (list[0].ownElements) {
+      for (i = 0; i < list[0].Nregions; i++) {
+	if (list[0].filename[i] != NULL) {
+	  free (list[0].filename[i]);
+	}
+	free (list[0].regions[i]);
+      }
+    }
+    free (list[0].regions);
+  }
+  free (list);
+  list = NULL;
+  return (TRUE);
+}
+
+int SkyTableFree (SkyTable *table) {
+
+  int i;
+
+  if (table == NULL) return (TRUE);
+  if (table[0].filename != NULL) {
+    for (i = 0; i < table[0].Nregions; i++) {
+      if (table[0].filename[i] != NULL) {
+	free (table[0].filename[i]);
+      }
+    }
+    free (table[0].filename);
+  }
+  if (table[0].regions != NULL) {
+    free (table[0].regions);
+  }
+  free (table);
+  table = NULL;
+  return (TRUE);
+}
+
+int SkyListMerge (SkyList **outlist, SkyList *newlist) {
+
+    int i, j, skip, Nlist, Ntotal;
+    SkyList *list;
+    
+    if (*outlist == NULL) {
+	ALLOCATE (list, SkyList, 1);
+	ALLOCATE (list[0].regions, SkyRegion *, 1);
+	ALLOCATE (list[0].filename, char *, 1);
+	list[0].ownElements = FALSE; // this list is only holding a view to the elements
+	list[0].Nregions = 0;
+	*outlist = list;
+    } else {
+	list = *outlist;
+    }
+
+    Ntotal = list[0].Nregions + newlist[0].Nregions;
+    REALLOCATE (list[0].regions, SkyRegion *, Ntotal);
+    REALLOCATE (list[0].filename, char *, Ntotal);
+
+    Nlist = list[0].Nregions;
+    for (i = 0; i < newlist[0].Nregions; i++) {
+	/* check existing list to see if we already have this region */
+	skip = FALSE;
+	for (j = 0; j < list[0].Nregions; j++) {
+	    if (list[0].regions[j] == newlist[0].regions[i]) skip = TRUE;
+	}
+	if (skip) continue;
+	list[0].regions[Nlist] = newlist[0].regions[i];
+	list[0].filename[Nlist] = newlist[0].filename[i];
+	Nlist++;
+    }
+    list[0].Nregions = Nlist;
+    REALLOCATE (list[0].regions, SkyRegion *, Nlist);
+    REALLOCATE (list[0].filename, char *, Nlist);
+
+    return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/tabletest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/tabletest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/tabletest.c	(revision 22322)
@@ -0,0 +1,10 @@
+# include "dvo.h"
+
+int main (int argc, char **argv) {
+
+  SkyTable *table;
+
+  SkyTableFromGSC (argv[1], 2, TRUE);
+
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libdvo/src/version.c	(revision 22322)
@@ -0,0 +1,6 @@
+# include <dvo.h>
+static char *name = "$Name: not supported by cvs2svn $";
+
+char *libdvo_version () {
+  return (name);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/Makefile	(revision 22322)
@@ -0,0 +1,74 @@
+default: install
+help:
+	@echo "make options: install libfits man clean dist"
+
+include ../../Makefile.System
+HOME	=	$(ROOT)/src/libfits
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+HEAD	=	$(HOME)/header
+MATR	=	$(HOME)/matrix
+TABL	=	$(HOME)/table
+EXT	=	$(HOME)/extern
+INC	=	$(HOME)/include
+MAN	= 	$(HOME)/doc
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC -Wall -Werror
+FULL_CPPFLAGS = $(BASE_CPPFLAGS) -I$(EXT)
+FULL_LDFLAGS  = $(BASE_LDFLAGS) -lohana
+
+install: $(DESTLIB)/libFITS.a $(DESTLIB)/libFITS.$(DLLTYPE) $(DESTMAN)/fits.1
+libfits: $(LIB)/libFITS.$(ARCH).a $(LIB)/libFITS.$(ARCH).$(DLLTYPE)
+man: $(DESTMAN)/fits.1
+
+INCS = $(DESTINC)/gfitsio.h 
+
+HEADER_OBJ = \
+$(HEAD)/F_modify.$(ARCH).o			$(HEAD)/F_create_H.$(ARCH).o  \
+$(HEAD)/F_free_H.$(ARCH).o			$(HEAD)/F_H_field.$(ARCH).o   \
+$(HEAD)/F_read_H.$(ARCH).o			$(HEAD)/F_write_H.$(ARCH).o   \
+$(HEAD)/F_scan.$(ARCH).o			$(HEAD)/F_print.$(ARCH).o     \
+$(HEAD)/F_copy_H.$(ARCH).o			$(HEAD)/F_delete.$(ARCH).o    \
+$(HEAD)/F_read_XH.$(ARCH).o			$(HEAD)/F_init_H.$(ARCH).o    \
+$(HEAD)/F_convert_H.$(ARCH).o                   $(HEAD)/version.$(ARCH).o
+
+MATRIX_OBJ = \
+$(MATR)/F_add_M_value.$(ARCH).o 		$(MATR)/F_add_M.$(ARCH).o        \
+$(MATR)/F_create_M.$(ARCH).o 			$(MATR)/F_divide_M.$(ARCH).o     \
+$(MATR)/F_free_M.$(ARCH).o 			$(MATR)/F_copy_M.$(ARCH).o       \
+$(MATR)/F_write_M.$(ARCH).o 			$(MATR)/F_get_M_value.$(ARCH).o  \
+$(MATR)/F_multiply_M.$(ARCH).o 			$(MATR)/F_read_M.$(ARCH).o       \
+$(MATR)/F_insert_M.$(ARCH).o 			$(MATR)/F_set_M_value.$(ARCH).o  \
+$(MATR)/F_convert_format.$(ARCH).o              $(MATR)/F_read_segment.$(ARCH).o \
+$(MATR)/F_read_portion.$(ARCH).o		$(MATR)/F_load_M.$(ARCH).o	 \
+$(MATR)/F_matrix.$(ARCH).o                      $(MATR)/F_compress_M.$(ARCH).o   \
+$(MATR)/F_uncompress_data.$(ARCH).o
+
+TABLE_OBJ = \
+$(TABL)/F_create_T.$(ARCH).o 			$(TABL)/F_create_TH.$(ARCH).o    \
+$(TABL)/F_read_T.$(ARCH).o			$(TABL)/F_read_TH.$(ARCH).o      \
+$(TABL)/F_write_T.$(ARCH).o			$(TABL)/F_write_TH.$(ARCH).o     \
+$(TABL)/F_define_column.$(ARCH).o		$(TABL)/F_table_format.$(ARCH).o \
+$(TABL)/F_set_column.$(ARCH).o			$(TABL)/F_get_column.$(ARCH).o   \
+$(TABL)/F_table_row.$(ARCH).o			$(TABL)/F_free_T.$(ARCH).o       \
+$(TABL)/F_table_varlength.$(ARCH).o
+
+EXTERN_OBJ = \
+$(EXT)/fits_hcompress.$(ARCH).o \
+$(EXT)/fits_hdecompress.$(ARCH).o \
+$(EXT)/pliocomp.$(ARCH).o \
+$(EXT)/ricecomp.$(ARCH).o
+
+OBJS = $(HEADER_OBJ) $(MATRIX_OBJ) $(TABLE_OBJ) $(EXTERN_OBJ)
+
+extern: $(EXTERN_OBJ)
+
+$(OBJS): $(INCS)
+
+$(LIB)/libFITS.$(ARCH).a: $(OBJS)
+$(LIB)/libFITS.$(ARCH).$(DLLTYPE): $(OBJS)
+
+$(DESTLIB)/libFITS.a:  $(LIB)/libFITS.$(ARCH).a
+$(DESTLIB)/libFITS.$(DLLTYPE): $(LIB)/libFITS.$(ARCH).$(DLLTYPE)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,41 @@
+
+- libfits 1.6 : 2006.08.23
+  * in order to allow compliation with cfitsio programs, I have changed the 
+    entire namespace to 'gfits' from 'fits'  (actually, only fits_copy_header 
+    was conflicting, but it is better to have a sufficiently different namespace).  
+    All ohana programs need to migrate to the new version of libfits.
+  * set buffer to NULL in gfits_init_header 
+
+- libfits 1.5
+  * cleaned up signed/unsigned inconsistencies
+
+libfits-1-4:
+  added dependency on libohana
+  make libfits work with ohana_allocate
+  removed internal fits.h include file, 
+  replaced with the common fitsio.h file
+
+2005.10.07
+
+	I was having some memory collision problems, and attempting to
+	use the ohana_allocate functions reminded me that the libFITS
+	functions were not supported under ohana_allocate.  This was
+	unhelpful.  I bit the bullet and split libohana into libohana
+	(base functions only, including ohana_allocate) and libdvo
+	(functions based on the libautocode structures).  Doing this
+	allowed me to make libFITS depend on libohana (including
+	ohana_allocate).  BUT, this forced me to change all LDFLAGS
+	entries in ohana to swap -lohana -lFITS for -lFITS -lohana,
+	and to add include <fitsio.h> in some cases.
+
+libfits-1-3:
+ added some additional vtable support, fixed minor iterpretation errors
+ in fits_modify.  
+
+libfits-1-1: 
+
+  F_scan now skips leading ' before parsing value : some bad FITS
+  writers add ' to float entries!
+
+libfits-1-0:
+  imported to CVS
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/README	(revision 22322)
@@ -0,0 +1,33 @@
+
+Installing gene's FITS C libraries.
+
+These FITS libraries are part of a larger package, but the rest of the
+package is not needed to make the libraries.  To install it for a
+specific system, the Makefile expects the environment variable ARCH to
+be defined to something appropriate to your machine (ie, sol for
+solaris, or linux for linux machines.  The resulting .o files then
+have machine specific names (ie, F_read_M.sol.o), making it easy to
+recompile for multiple machines.  The compiled library also has
+a machine specific names (libFITS.sol.a), and the installed version is
+then placed with a generic name in a machine specific locations (ie
+lib/sol/libFITS.a).  
+
+
+Edit the Configure file match your current system.  As it stands, the Makefile
+expects the fits directory to be $(ROOT)/src/fits.  The XINC and XLIB
+variables are not actually used by the fits libraries, so you can
+igore them.  
+
+BYTE_SWAP: byte swapping can be forced by placing a -DBYTE_SWAP option
+on the CC line in Configure (see commented out line).  Byte swapping
+is also enabled automatically if ARCH = linux.  (If you have other
+byte swapped machines, you can edit include/fits.h to enable them
+automatically as well).
+
+define an environment variable ARCH and run "make" or "make install"
+to install the library.
+
+good luck
+
+gene
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/VERSIONS
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/VERSIONS	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/VERSIONS	(revision 22322)
@@ -0,0 +1,5 @@
+
+tag names used by libfits:
+
+TAG         : Comment
+libfits-1-0 : first version under CVS
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/fits.1
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/fits.1	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/fits.1	(revision 22322)
@@ -0,0 +1,256 @@
+.ad l
+.nh
+.TH FITS 1.0 "Sept 18, 1992" "Version 1.0"
+.SH NAME
+\fIFITS HEADER routines:\fP  fits_read_header, fits_write_header, 
+fits_header_field,  fits_scan, fits_print, fits_copy_header,
+fits_create_header, fits_free_header.
+
+\fIFITS MATRIX routines:\fP  fits_read_matrix, fits_write_matrix, 
+fits_get_matrix_value, fits_set_matrix_value, fits_create_matrix, 
+fits_free_matrix, fits_insert_array
+
+.SH SYNOPSIS
+.nf
+.ft B
+(compile with -lFITS)
+# include <fitsio.h>
+.LP
+\fI FITS Header Routines \fP
+.RS
+int   fits_read_header (filename, header)
+char      *filename;
+Header    *header;
+
+int   fits_write_header (filename, header)
+char      *filename;
+Header    *header;
+
+char *fits_header_field (header, field, N)
+Header    *header;
+char      *field;
+int        N;
+
+int   fits_scan   (header, field, mode, N, value)
+int   fits_print  (header, field, mode, N, value)
+int   fits_modify (header, field, mode, N, value)
+Header    *header;
+char      *field, *mode, *value;
+int        N;
+
+int   fits_create_header (header)
+Header    *header;
+
+int   fits_copy_header (header1, header2)
+Header    *header1;
+Header    *header2;
+
+int   fits_free_header (header)
+Header    *header;
+
+.RE
+.LP
+\fI FITS Matrix Routines \fP
+.RS 
+int   fits_read_matrix (filename, matrix)
+char      *filename;
+Matrix    *matrix;
+
+int   fits_write_matrix (filename, matrix)
+char      *filename;
+Matrix    *matrix;
+
+int   fits_get_matrix_value (matrix, x, y)
+Matrix    *matrix;
+int        x, y;
+
+void  fits_set_matrix_value (matrix, x, y, value)
+Matrix    *matrix;
+int        x, y;
+double     value;
+
+int   fits_create_matrix (header, matrix)
+Header    *header;
+Matrix    *matrix;
+
+int   fits_insert_array (matrix, array, x, y)
+Matrix    *matrix;
+Matrix    *array;
+int        x, y;
+
+int   fits_free_matrix (matrix)
+Matrix    *matrix;
+
+
+.SH DESCRIPTION
+.LP
+These functions allow for FITS I/O in a general C-like way.  There are
+several relavant structures defined in \fIfitsio.h\fP (see
+STRUCTURES below).  
+
+\fI FITS Header Routines \fP
+
+.RS 
+\fIfits_read_header\fP opens a file and reads the FITS header into
+a Header structure.  No error checking, except file existence is
+currently provided.  
+
+\fIfits_write_header\fP opens a file and write the Header structure
+into the file in FITS format.  It also puts a RETURN character (0x0a)
+into the N*80 bytes (the end of every header line).  This is not FITS
+standard, but it makes the header more readable.
+
+\fIfits_header_field\fP returns a pointer to the start of the Nth
+header line with the given keyword "field".  No error check is
+provided, except to note the lack of such a field (returns a NULL
+value).
+
+\fIfits_scan\fP, \fIfits_print\fP and \fIfits_modify\fP are modeled on
+\fIfscanf\fP and \fIfprintf\fP but act on a FITS header instead of a
+stream.  \fIfits_print\fP write a new entry, while \fIfits_modify\fP
+alters the value of an existing entry (wiping out the comment line).
+\fBdNote:\fP \fIfits_modify\fP will create a new entry, but \fIfits_print\fP 
+will \fInot\fP modify and existing entry!
+The following modes are supported:
+
+.RS
+.PD 0
+.TP
+%d 
+A signed decimal integer is expected.
+.TP
+%u 
+An unsigned decimal integer is expected.
+.TP
+%ld 
+A long decimal integer is expected.
+.TP
+%hd 
+A short decimal integer is expected.
+.TP
+%f 
+A float is expected.
+.TP 
+%lf 
+A double is expected.
+.TP
+%t 
+A boolean is expected.  The pointer should be of type \fIbool\fP.
+.TP
+%s 
+A string is expected.  The pointer should be of type \fIchar\fP.
+.TP
+%S A comment line string is expected.  The pointer should be of type \fIchar\fP.
+.RE
+
+\fIfits_create_header\fP creates a Header structure based on a partially filled 
+Header structure.  The mandatory structure values (simple, bitpix, Naxes, 
+Naxis[N], extend, unsign) must be set.  The buffer will be filled in 
+according to these values.
+
+\fIfits_copy_header\fP copies the header1 to header2;
+
+\fIfits_free_header\fP frees the allocated pointers associated with the 
+header.
+.RE
+
+\fI FITS Matrix Routines \fP
+
+.RS
+\fIfits_read_matrix\fP opens the given file and reads the matrix structure.
+There is, of course, no decent error checking.
+
+\fIfits_write_matrix\fP opens the given file and writes the matrix structure.
+The matrix is written to the end of the file, in the assumption that
+there is already a Header written to the file.  
+There is, of course, no decent error checking.
+
+\fIfits_get_matrix_value\fP returns the value of the pixel at the given (2-D) 
+coordinates.
+
+\fIfits_set_matrix_value\fP sets the value of the pixel at the given (2-D) 
+coordinates.
+
+\fIfits_create_matrix\fP creates a matrix which corresponds to the format
+described in the Header structure.
+
+\fIfits_copy_matrix\fP makes a duplicate copy of header1 in header2. 
+
+\fIfits_free_matrix\fP frees the allocated pointers associated with the
+Matrix.
+
+\fIfits_insert_array\fP set the values of the Matrix to those of the
+array (a small Matrix), using the x,y values as a relative offset.
+.RE
+
+.SH STRUCTURES
+.PP
+There are several important structures defined in \fIfitsio.h\fP
+which are described here.
+
+# define bool char
+
+# typedef struct {
+  bool                    simple;
+  bool                    unsign;
+  bool                    extend;
+  int                     bitpix;
+  int                     Naxes;
+  int                     Naxis[FT_MAX_NAXES];
+  int                     size;
+  double                  bzero, bscale;
+  char                   *buffer;
+ } Header;
+
+typedef struct {
+  int                     Naxes;
+  int                     Naxis[FT_MAX_NAXES];
+  int                     bitpix;
+  bool                    unsign;
+  double                  bzero, bscale;
+  char                   *buffer;
+  int                     size;
+ } Matrix;
+
+REMINDER: data = (byte value) * bscale + bzero
+
+\fIbool\fP is defined to be a char.
+
+\fIHeader\fP is a structure for holding all header information.  It
+consists of values of the basic header fields required for a FITS file
+(SIMPLE, EXTEND, BITPIX, NAXIS, NAXIS1, NAXIS2...) as well as a
+pointer to the header data itself (\fIbuffer\fP) and the number of
+bytes in the header buffer (\fIsize\fP).  
+
+\fIMatrix\fP is a structure for holding the image matrix data.  It
+consists of the \fINaxes\fP, and \fINaxis[10]\fP entries as well
+as the \fIbuffer\fP and the \fIsize\fP entries.
+
+
+.SH COPYRIGHT
+Copyright 1991 Eugene Magnier
+.PP                                                
+Permission to use, copy, modify, distribute, and sell this software
+and its documentation for any purpose is hereby granted without fee,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of MIT not be used in
+advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.  MIT and the authors make
+no representations about the suitability of this software for any
+purpose.  It is provided "as is" without express or implied warranty.
+.PP
+MIT and the authors disclaim all warranties with regard to this
+software, including all implied warranties of merchantability and
+fitness, in no event shall MIT or the authors be liable for any
+special, indirect or consequential damages or any damages whatsoever
+resulting from loss of use, data or profits, whether in an action of
+contract, negligence or other tortious action, arising out of or in
+connection with the use or performance of this software.
+.SH ACKNOWLEDGEMENTS
+Walter H.G. Lewin for inspiration
+.PP
+NASA for lots of money
+.PP
+.SH AUTHOR
+Eugene Magnier.  MIT - CSR
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/fits.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/fits.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/fits.tex	(revision 22322)
@@ -0,0 +1,62 @@
+\documentstyle{AA}
+
+\title{FITS I/O Routine}
+\author{gene magnier}
+
+\begin{document}
+\maketitle
+
+        This file documents the C routines I have created for easy
+manipulation of FITS files.  For now, I will assume familiarity with C
+and the FITS format definitions.  (Maybe I will expand this text
+later).  This set of routines consists of a number of subroutines,
+each of which is contained within its own file.  The subroutines fall into
+three classes: those which manipulte headers, those which manipulate
+data matrices, and those which manipulate tables.  Every subroutine
+has a name which starts with the word {\tt fits}, and includes the word
+{\tt header, matrix,} or {\tt table}.  The file names use the letters
+F, H, M, and T as abbreviations for these words.  Below, I list all of
+the subroutines, and following this I describe them and their use.   
+
+\begin{center}
+\begin{tabular}{ll}
+\hline
+routine name               &   file name \\
+\hline
+fits\_read\_header         &   F\_read\_H.c            \\
+fits\_write\_header        &   F\_write\_H.c           \\
+fits\_header\_field        &   F\_H\_field.c           \\
+fits\_scan                 &   F\_scan.c               \\
+fits\_print                &   F\_print.c              \\
+fits\_modify               &   F\_modify.c             \\
+fits\_delete               &   F\_delete.c             \\
+fits\_create\_header       &   F\_create\_H.c          \\
+fits\_copy\_header         &   F\_copy\_H.c            \\
+fits\_free\_header         &   F\_free\_H.c            \\
+\hline
+fits\_read\_matrix         &   F\_read\_M.c            \\
+fits\_write\_matrix        &   F\_write\_M.c           \\
+fits\_get\_matrix\_value   &   F\_get\_M\_value.c      \\
+fits\_set\_matrix\_value   &   F\_set\_M\_value.c      \\
+fits\_add\_matrix\_value   &   F\_add\_M\_value.c      \\
+fits\_create\_matrix       &   F\_create\_M.c          \\
+fits\_copy\_matrix         &   F\_copy\_M.c            \\
+fits\_free\_matrix         &   F\_free\_M.c            \\
+fits\_add\_matrix          &   F\_add\_M.c             \\
+fits\_multiply\_matrix     &   F\_multiply\_M.c        \\
+fits\_divide\_matrix       &   F\_divide\_M.c          \\
+fits\_insert\_matrix       &   F\_insert\_M.c          \\
+fits\_insert\_array        &   F\_insert\_array.c      \\
+\hline
+fits\_read\_Theader        &   F\_read\_TH.c           \\
+fits\_write\_Theader       &   F\_write\_TH.c          \\
+fits\_create\_Theader      &   F\_create\_TH.c         \\
+fits\_read\_table          &   F\_read\_T.c            \\
+fits\_get\_table\_column   &   F\_get\_T\_column.c     \\
+fits\_get\_table\_value    &   F\_get\_T\_value.c      \\
+\hline
+\end{tabular}
+\end{center}
+
+\end{document}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/fits.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/fits.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/fits.txt	(revision 22322)
@@ -0,0 +1,253 @@
+
+FITS(1.0)         MISC. REFERENCE MANUAL PAGES          FITS(1.0)
+
+NAME
+     _F_I_T_S _H_E_A_D_E_R _r_o_u_t_i_n_e_s:  fits_read_header, fits_write_header,
+     fits_header_field,  fits_scan, fits_print, fits_copy_header,
+     fits_create_header, fits_free_header.
+
+     _F_I_T_S _M_A_T_R_I_X _r_o_u_t_i_n_e_s:  fits_read_matrix, fits_write_matrix,
+     fits_get_matrix_value, fits_set_matrix_value,
+     fits_create_matrix, fits_free_matrix, fits_insert_array
+
+SYNOPSIS
+     (compile with -lFITS)
+     # include <fitsio.h>
+
+      _F_I_T_S _H_e_a_d_e_r _R_o_u_t_i_n_e_s
+          int   fits_read_header (filename, header)
+          char      *filename;
+          Header    *header;
+
+          int   fits_write_header (filename, header)
+          char      *filename;
+          Header    *header;
+
+          char *fits_header_field (header, field, N)
+          Header    *header;
+          char      *field;
+          int        N;
+
+          int   fits_scan   (header, field, mode, value, N)
+          int   fits_print  (header, field, mode, value, N)
+          int   fits_modify (header, field, mode, value, N)
+          Header    *header;
+          char      *field, *mode, *value;
+          int        N;
+
+          int   fits_create_header (header)
+          Header    *header;
+
+          int   fits_copy_header (header1, header2)
+          Header    *header1;
+          Header    *header2;
+
+          int   fits_free_header (header)
+          Header    *header;
+
+      _F_I_T_S _M_a_t_r_i_x _R_o_u_t_i_n_e_s
+          int   fits_read_matrix (filename, matrix)
+          char      *filename;
+          Matrix    *matrix;
+
+          int   fits_write_matrix (filename, matrix)
+
+Version 1.0        Last change: Sept 18, 1992                   1
+
+FITS(1.0)         MISC. REFERENCE MANUAL PAGES          FITS(1.0)
+
+          char      *filename;
+          Matrix    *matrix;
+
+          int   fits_get_matrix_value (matrix, x, y)
+          Matrix    *matrix;
+          int        x, y;
+
+          void  fits_set_matrix_value (matrix, x, y, value)
+          Matrix    *matrix;
+          int        x, y;
+          double     value;
+
+          int   fits_create_matrix (header, matrix)
+          Header    *header;
+          Matrix    *matrix;
+
+          int   fits_insert_array (matrix, array, x, y)
+          Matrix    *matrix;
+          Matrix    *array;
+          int        x, y;
+
+          int   fits_free_matrix (matrix)
+          Matrix    *matrix;
+
+DESCRIPTION
+     These functions allow for FITS I/O in a general C-like way.
+     There are several relavant structures defined in _f_i_t_s_i_o._h
+     (see STRUCTURES below).
+
+      _F_I_T_S _H_e_a_d_e_r _R_o_u_t_i_n_e_s
+
+          _f_i_t_s__r_e_a_d__h_e_a_d_e_r opens a file and reads the FITS header
+          into a Header structure.  No error checking, except
+          file existence is currently provided.
+
+          _f_i_t_s__w_r_i_t_e__h_e_a_d_e_r opens a file and write the Header
+          structure into the file in FITS format.  It also puts a
+          RETURN character (0x0a) into the N*80 bytes (the end of
+          every header line).  This is not FITS standard, but it
+          makes the header more readable.
+
+          _f_i_t_s__h_e_a_d_e_r__f_i_e_l_d returns a pointer to the start of the
+          Nth header line with the given keyword "field".  No
+          error check is provided, except to note the lack of
+          such a field (returns a NULL value).
+
+          _f_i_t_s__s_c_a_n, _f_i_t_s__p_r_i_n_t and _f_i_t_s__m_o_d_i_f_y are modeled on
+          _f_s_c_a_n_f and _f_p_r_i_n_t_f but act on a FITS header instead of
+          a stream.  _f_i_t_s__p_r_i_n_t write a new entry, while
+          _f_i_t_s__m_o_d_i_f_y alters the value of an existing entry
+
+Version 1.0        Last change: Sept 18, 1992                   2
+
+FITS(1.0)         MISC. REFERENCE MANUAL PAGES          FITS(1.0)
+
+          (wiping out the comment line).  The following modes are
+          supported:
+
+               %d   A signed decimal integer is expected.
+               %u   An unsigned decimal integer is expected.
+               %ld  A long decimal integer is expected.
+               %hd  A short decimal integer is expected.
+               %f   A float is expected.
+               %lf  A double is expected.
+               %t   A boolean is expected.  The pointer should be
+                    of type _b_o_o_l.
+               %s   A string is expected.  The pointer should be
+                    of type _c_h_a_r.
+                    should be of type _c_h_a_r.
+               %S A comment line string is expected.  The pointer
+
+          _f_i_t_s__c_r_e_a_t_e__h_e_a_d_e_r creates a Header structure based on
+          a partially filled Header structure.  The mandatory
+          structure values (simple, bitpix, Naxes, Naxis[N],
+          extend, unsign) must be set.  The buffer will be filled
+          in according to these values.
+
+          _f_i_t_s__c_o_p_y__h_e_a_d_e_r copies the header1 to header2;
+
+          _f_i_t_s__f_r_e_e__h_e_a_d_e_r frees the allocated pointers
+          associated with the header.
+
+      _F_I_T_S _M_a_t_r_i_x _R_o_u_t_i_n_e_s
+
+          _f_i_t_s__r_e_a_d__m_a_t_r_i_x opens the given file and reads the
+          matrix structure.  There is, of course, no decent error
+          checking.
+
+          _f_i_t_s__w_r_i_t_e__m_a_t_r_i_x opens the given file and writes the
+          matrix structure.  The matrix is written to the end of
+          the file, in the assumption that there is already a
+          Header written to the file. There is, of course, no
+          decent error checking.
+
+          _f_i_t_s__g_e_t__m_a_t_r_i_x__v_a_l_u_e returns the value of the pixel at
+          the given (2-D) coordinates.
+
+          _f_i_t_s__s_e_t__m_a_t_r_i_x__v_a_l_u_e sets the value of the pixel at
+          the given (2-D) coordinates.
+
+          _f_i_t_s__c_r_e_a_t_e__m_a_t_r_i_x creates a matrix which corresponds
+          to the format described in the Header structure.
+
+          _f_i_t_s__c_o_p_y__m_a_t_r_i_x makes a duplicate copy of header1 in
+          header2.
+
+          _f_i_t_s__f_r_e_e__m_a_t_r_i_x frees the allocated pointers
+
+Version 1.0        Last change: Sept 18, 1992                   3
+
+FITS(1.0)         MISC. REFERENCE MANUAL PAGES          FITS(1.0)
+
+          associated with the Matrix.
+
+          _f_i_t_s__i_n_s_e_r_t__a_r_r_a_y set the values of the Matrix to those
+          of the array (a small Matrix), using the x,y values as
+          a relative offset.
+
+STRUCTURES
+     There are several important structures defined in _f_i_t_s_i_o._h
+     which are described here.
+
+     # define bool char
+
+     # typedef struct {
+       bool                    simple;
+       bool                    unsign;
+       bool                    extend;
+       int                     bitpix;
+       int                     Naxes;
+       int                     Naxis[FT_MAX_NAXES];
+       int                     size;
+       double                  bzero, bscale;
+       char                   *buffer;
+      } Header;
+
+     typedef struct {
+       int                     Naxes;
+       int                     Naxis[FT_MAX_NAXES];
+       int                     bitpix;
+       bool                    unsign;
+       double                  bzero, bscale;
+       char                   *buffer;
+       int                     size;
+      } Matrix;
+
+     REMINDER: data = (byte value) * bscale + bzero
+
+     _b_o_o_l is defined to be a char.
+
+     _H_e_a_d_e_r is a structure for holding all header information.
+     It consists of values of the basic header fields required
+     for a FITS file (SIMPLE, EXTEND, BITPIX, NAXIS, NAXIS1,
+     NAXIS2...) as well as a pointer to the header data itself
+     (_b_u_f_f_e_r) and the number of bytes in the header buffer
+     (_s_i_z_e).
+
+     _M_a_t_r_i_x is a structure for holding the image matrix data.  It
+     consists of the _N_a_x_e_s, and _N_a_x_i_s[_1_0] entries as well as the
+     _b_u_f_f_e_r and the _s_i_z_e entries.
+
+COPYRIGHT
+     Copyright 1991 Eugene Magnier
+
+Version 1.0        Last change: Sept 18, 1992                   4
+
+FITS(1.0)         MISC. REFERENCE MANUAL PAGES          FITS(1.0)
+
+     Permission to use, copy, modify, distribute, and sell this
+     software and its documentation for any purpose is hereby
+     granted without fee, provided that the above copyright
+     notice appear in all copies and that both that copyright
+     notice and this permission notice appear in supporting
+     documentation, and that the name of MIT not be used in
+     advertising or publicity pertaining to distribution of the
+     software without specific, written prior permission.  MIT
+     and the authors make no representations about the
+     suitability of this software for any purpose.  It is
+     provided "as is" without express or implied warranty.
+     MIT and the authors disclaim all warranties with regard to
+     this software, including all implied warranties of
+     merchantability and fitness, in no event shall MIT or the
+     authors be liable for any special, indirect or consequential
+     damages or any damages whatsoever resulting from loss of
+     use, data or profits, whether in an action of contract,
+     negligence or other tortious action, arising out of or in
+     connection with the use or performance of this software.
+ACKNOWLEDGEMENTS
+     Walter H.G. Lewin for inspiration
+     NASA for lots of money
+AUTHOR
+     Eugene Magnier.  MIT - CSR
+
+Version 1.0        Last change: Sept 18, 1992                   5
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/notes-compress.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/notes-compress.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/notes-compress.txt	(revision 22322)
@@ -0,0 +1,54 @@
+
+TFORMn : rPt(e_max)
+r is 0, 1, or absent
+P is literal
+t is datatype character for binary tables
+e_max is the largest record in the column
+
+data column actually contains: 2 32bit values:
+Length + Offset
+
+THEAP : offset from start of data to start of heap
+(note that the heap starts at NAXIS1 x NAXIS2, not Nx2880 bytes). 
+
+total size of heap segment (gap + heap data) is PCOUNT
+
+I need to implement (at least) support for read of compressed images.
+It might be a good plan to upgrade the gfits API set, or at least
+identify needed work.
+
+* PCOUNT / GCOUNT : these are currently ignored.  I need to at least
+  include them in the calculation of the fits data segment size.  I
+  might need to use them in the interpretation of the table data.
+
+* implement variable-length columns in FITS table support.  
+
+* add the I/O support directly to gfits_read/write_matrix, or define
+  low-level APIs to support read/write of a compressed image?
+
+  * we should start with the low-level APIs and add in the higher
+    level ones as needed.
+
+* possible needed APIs & upgrades:
+
+  - gfits_image_is_compressed (Header *header) 
+    does the specified header correspond to a compressed image?	
+    what about the PHU which implies a compressed image in the next
+    HDU?
+
+  - Matrix *gfits_uncompress_image (Header *header, Table *table)
+    convert the specified table into an image, modifying the header in
+    place
+
+  - Table *gfits_compress_image (Header *header, Matrix *matrix, Compression *options)
+    compress the given image returning the table data
+
+  o include PCOUNT in data area size (gfits_matrix_size)
+
+  o read heap when reading table (gfits_fread_table_data)
+
+  - void *gfits_varlength_column_pointer (FTable *ftable, VarLengthColumn *column, int row);
+
+  o int gfits_varlength_column_define (FTable *ftable, VarLengthColumn *def, int column);
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/notes.txt	(revision 22322)
@@ -0,0 +1,133 @@
+
+2002/09/19
+
+I am making some substantial upgrades to libfits.  My primary goal for
+the moment is the improve the table handling, but I am also making
+some minor cleanups in other areas.  A major concern is that no
+changes should be made to existing functions that will alter their
+behavior and break other programs.  Below I list functions that change
+and note if they risk causing any problems.
+
+header:
+
+fits_init_header:   new function (SAFE)
+fits_create_header: changed fits_print to fits_modify (SAFE)
+fits_fread_header:  new function, to eventually replace fits_load_header (SAFE)
+fits_fwrite_header: new function, to eventually replace fits_save_header (SAFE)
+fits_write_header:  cleaned (f == NULL) (SAFE)
+
+matrix:
+
+fits_create_matrix: if NAXIS==0, create 0 size matrix, allocate min 1 byte
+ - this is different, but now follows correct fits standard.
+ - does not affect any existing programs: all assume NAXIS > 0
+fits_fread_matrix:  new function, to eventually replace fits_load_matrix (SAFE)
+fits_load_matrix:   cleanup declaration format (SAFE)
+fits_fwrite_matrix: new function (SAFE)
+fits_write_matrix:  file ops only, wrapper to fwrite (SAFE)
+fits_matrix_size:   new function (SAFE)
+
+table: no changes
+
+---
+
+fits_write_header:  open / truncate file, write header first
+fits_fwrite_header: 
+
+
+
+current table functions: (9/2002)
+
+fits_read_Theader   (char *filename, Header *theader);
+fits_load_Theader   (FILE *f, Header *theader)
+fits_read_table     (char *filename, Table *table);
+fits_table_column   (Table *table, char *field, char *mode, varg array);
+fits_create_Theader (Header *header, char *type);
+
+new functions:
+
+fits_init_header (Header *header);
+ - set all values to defaults: 
+   bitpix = 8, Naxes = 0, bscale = 1, bzero = 0, extend = FALSE, unsign = FALSE;
+   
+fits_create_table_header (Header *header, char *type);
+ - create an empty table header: TFIELDS = 0, NAXIS1 = NAXIS2 = 0
+
+fits_define_table_column (Header *header, char *format, char *type, char *comment, char *unit);
+ - add a column definition to header
+ - add appropriate keywords
+ - update TFIELDS
+ - update NAXIS1
+
+fits_define_bintable_column (Header *header, char *format, char *type, char *comment, char *unit, double scale, double zero);
+ - add a column definition to header
+ - add appropriate keywords
+ - update TFIELDS
+ - update NAXIS1
+
+fits_get_table_column ()
+
+fits_table_column (Table *table, char *field, char *mode, void *result);
+
+
+
+
+typedef struct {
+  int                     simple;
+  int                     unsign;
+  int                     extend;
+  int                     bitpix;
+  int                     Naxes;
+  int                     Naxis[FT_MAX_NAXES];
+  int                     size;
+  double                  bzero;
+  double                  bscale;
+  char                   *buffer;
+} Header;
+
+typedef struct {
+  int                     unsign;
+  int                     bitpix;
+  int                     Naxes;
+  int                     Naxis[FT_MAX_NAXES];
+  int                     size;
+  double                  bzero;
+  double                  bscale;
+  char                   *buffer;
+} Matrix;
+
+typedef struct {
+  int                     bitpix;
+  int                     Nfields;
+  int                     Naxes;
+  int                     Naxis[FT_MAX_NAXES];
+  int                     size;
+  char                   *buffer;
+  Header                  header;
+} Table;
+          
+typedef struct {
+  Header                 *header;
+  char                   *buffer;
+  int                     size;
+} FTable;
+
+typedef struct {
+  Header                 *header;
+  char                  **buffer;
+  int			  Nrow;
+  int                    *row;
+  int                     size;  /* total buffer size */
+  int			  pad;   /* bytes of padding at the end */
+} VTable;
+
+virtual table:
+
+a virtual table contains only part of the data on disk size is the
+total size of the table (disk + mem) buffer is a contiguous block of
+data in the table msize is the size of the memory block stored at
+buffer offset is the start of the memory block
+
+Nx is width of table,
+Ny is number of occupied rows
+size = Nx*Ny + pad;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/notes2.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/notes2.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/notes2.txt	(revision 22322)
@@ -0,0 +1,51 @@
+
+complete list of FITS table APIs:
+
+int fits_create_table (Header *header, FTable *table) 
+int fits_create_Theader (Header *header, char *type)  - create empty extension header 
+int fits_create_table_header (Header *header, char *type, char *extname) - create valid table header
+
+int fits_define_bintable_column (Header *header, char *format, char *label, char *comment, char *unit, double bscale, double bzero)
+int fits_define_table_column (Header *header, char *format, char *label, char *comment, char *unit) - 
+
+int fits_get_bintable_column (Header *header, FTable *table, char *label, void **data)
+int fits_get_table_column (Header *header, FTable *table, char *label, void **data)
+
+int fits_set_bintable_column (Header *header, FTable *table, char *label, void *data, int Nrow)
+int fits_set_table_column (Header *header, FTable *table, char *label, void *data, int Nrow)
+
+int fits_read_table (char *filename, Table *table)
+int fits_read_ftable (char *filename, FTable *table, char *extname)
+int fits_fread_ftable (FILE *f, FTable *table, char *extname)
+int fits_fread_vtable (FILE *f, VTable *table, char *extname, int Nrow, int *row)
+
+int fits_read_Theader (char *filename, Header *Theader)
+int fits_load_Theader (FILE *f, Header *Theader)
+int fits_fread_Theader (FILE *f, Header *Theader)
+
+int fits_bintable_format (char *format, char *type, int *Nval, int *Nbytes)
+int fits_table_format (char *format, char *type, int *Nval, int *Nbytes)
+
+  interpret table column format values.  
+  format - input column format value (TFORM), output C-style printing format
+  type   - char, int, float, double : use to case pointers
+  Nval   - number of entries 
+  Nbytes - bytes per entry
+
+int fits_table_to_vtable (FTable *ftable, VTable *vtable, int start, int Nkeep)
+
+int fits_add_rows (FTable *table, char *data, int Nrow, int Nbytes)
+int fits_vadd_rows (VTable *table, char *data, int Nrow, int Nbytes)
+int fits_delete_rows (FTable *table, int Nstart, int Nrow)
+
+int fits_write_table (char *filename, FTable *table)
+int fits_fwrite_table (FILE *f, FTable *table)
+int fits_fwrite_vtable (FILE *f, VTable *table)
+
+int fits_write_Theader (char *filename, Header *header)
+int fits_fwrite_Theader (FILE *f, Header *header)
+
+void fits_get_table_column (table, X, mode, values)  - REMOVED
+void fits_get_table_value (table, X, Y, mode, value) - REMOVED
+int fits_table_column (Table *table, char *field, char *mode,...) - REMOVED
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/sample.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/sample.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/sample.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# define TRUE 1
+
+main () {
+
+  char filename[80];
+  int i, Nrow;
+  Header header, theader1, theader2;
+  Matrix matrix;
+  FTable table1, table2;
+  float zpobs[1000];
+  float zpref[1000];
+  unsigned long int time[1000];
+  char tchar[1000];
+  
+  strcpy (filename, "test.fits");
+  Nrow = 20;
+  for (i = 0; i < Nrow; i++) {
+    zpobs[i] = i + 5;
+    zpref[i] = i + 15;
+    time[i] = i + 300000;
+    sprintf (&tchar[8*i], "%8d", i);
+  }
+
+  /* create primary header */
+  gfits_init_header (&header);    header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_print (&header, "NEXTEND", "%d", 1, 2);
+
+  /* define bintable header & layout */
+  gfits_create_table_header (&theader1, "BINTABLE", "ZERO_POINTS");
+
+  gfits_define_bintable_column (&theader1, "E", "ZP_OBS", "measured zero point", "mag", 1.0, 0.0);
+  gfits_define_bintable_column (&theader1, "E", "ZP_REF", "measured zero point", "mag", 1.0, 0.0);
+  gfits_define_bintable_column (&theader1, "J", "TIME",   "time of data", "seconds since Jan 1, 1970 UT", 1.0, 0.0);
+  gfits_define_bintable_column (&theader1, "8A", "TCHAR",   "time of data", "seconds since Jan 1, 1970 UT", 1.0, 0.0);
+
+  /* create table, add data values */
+  gfits_create_table (&theader1, &table1);
+
+  /* set table column based on array, extend NAXIS2 as needed/appropriate */
+  gfits_set_bintable_column (&theader1, &table1, "ZP_OBS", zpobs, Nrow);
+  gfits_set_bintable_column (&theader1, &table1, "ZP_REF", zpref, Nrow);
+  gfits_set_bintable_column (&theader1, &table1, "TIME",   time,  Nrow);
+  gfits_set_bintable_column (&theader1, &table1, "TCHAR",  tchar,  Nrow);
+
+  /* define ASCII table header */
+  gfits_create_table_header (&theader2, "TABLE", "ASCII_PTS");
+
+  gfits_define_table_column (&theader2, "F5.2", "ZP_OBS", "measured zero point", "mag");
+  gfits_define_table_column (&theader2, "F5.2", "ZP_REF", "measured zero point", "mag");
+  gfits_define_table_column (&theader2, "I8", "TIME",    "time of data", "seconds");
+  gfits_define_table_column (&theader2, "A8", "TCHAR",   "time of data", "YYYYMMDD");
+
+  /* create table, add data values */
+  gfits_create_table (&theader2, &table2);
+
+  gfits_set_table_column (&theader2, &table2, "ZP_OBS", zpobs, Nrow);
+  gfits_set_table_column (&theader2, &table2, "ZP_REF", zpref, Nrow);
+  gfits_set_table_column (&theader2, &table2, "TIME",   time,  Nrow);
+  gfits_set_table_column (&theader2, &table2, "TCHAR",  tchar, Nrow);
+
+  /* write header, matrix, table1, table2 */
+  gfits_write_header  (filename, &header);
+  gfits_write_matrix  (filename, &matrix);
+  gfits_write_Theader (filename, &theader1);
+  gfits_write_table   (filename, &table1);
+  gfits_write_Theader (filename, &theader2);
+  gfits_write_table   (filename, &table2);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/sample2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/sample2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/doc/sample2.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# include <stdio.h>
+# define TRUE 1
+
+main () {
+
+  char filename[80];
+  int i, j, Nrow;
+  Header header, theader1, theader2;
+  Matrix matrix;
+  FTable table1, table2;
+  float *zpobs, *zpref;
+  unsigned long int *time;
+  char *tchar;
+  
+  table1.header = &theader1;
+  table2.header = &theader2;
+
+  strcpy (filename, "test.fits");
+  gfits_read_header  (filename, &header);
+  gfits_read_matrix  (filename, &matrix);
+  gfits_read_ftable  (filename, &table1, "ZERO_POINTS");
+  gfits_read_ftable  (filename, &table2, "ASCII_PTS");
+  
+  /* set table column based on array, extend NAXIS2 as needed/appropriate */
+  gfits_get_bintable_column (&theader1, &table1, "ZP_OBS", &zpobs);
+  gfits_get_bintable_column (&theader1, &table1, "ZP_REF", &zpref);
+  gfits_get_bintable_column (&theader1, &table1, "TIME",   &time);
+  gfits_get_bintable_column (&theader1, &table1, "TCHAR",  &tchar);
+  gfits_scan (&theader1, "NAXIS2", "%d", 1, &Nrow);
+
+  for (i = 0; i < Nrow; i++) {
+    fprintf (stderr, "%d %f %f %d   ", i, zpobs[i], zpref[i], time[i]);
+    for (j = 0; j < 8; j++) { fprintf (stderr, "%c", tchar[i*8 + j]); }
+    fprintf (stderr, "\n");
+  }
+
+  /* set table column based on array, extend NAXIS2 as needed/appropriate */
+  gfits_get_table_column (&theader2, &table2, "ZP_OBS", &zpobs);
+  gfits_get_table_column (&theader2, &table2, "ZP_REF", &zpref);
+  gfits_get_table_column (&theader2, &table2, "TIME",   &time);
+  gfits_get_table_column (&theader2, &table2, "TCHAR",  &tchar);
+  gfits_scan (&theader2, "NAXIS2", "%d", 1, &Nrow);
+
+  for (i = 0; i < Nrow; i++) {
+    fprintf (stderr, "%d %f %f %d   ", i, zpobs[i], zpref[i], time[i]);
+    for (j = 0; j < 8; j++) { fprintf (stderr, "%c", tchar[i*8 + j]); }
+    fprintf (stderr, "\n");
+  }
+
+
+}      
+
+/*
+  gfits_set_table_column (&theader2, &table2, "ZP_OBS", zpobs, Nrow);
+  gfits_set_table_column (&theader2, &table2, "ZP_REF", zpref, Nrow);
+  gfits_set_table_column (&theader2, &table2, "TIME",   time,  Nrow);
+  gfits_set_table_column (&theader2, &table2, "TCHAR",  tchar, Nrow);
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/fits_hcompress.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/fits_hcompress.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/fits_hcompress.c	(revision 22322)
@@ -0,0 +1,1688 @@
+/*  #########################################################################
+These routines to apply the H-compress compression algorithm to a 2-D Fits
+image were written by R. White at the STScI and were obtained from the STScI at
+http://www.stsci.edu/software/hcompress.html
+
+This source file is a concatination of the following sources files in the
+original distribution 
+ htrans.c 
+ digitize.c 
+ encode.c 
+ qwrite.c 
+ doencode.c 
+ bit_output.c 
+ qtree_encode.c
+
+The following modifications have been made to the original code:
+
+  - commented out redundant "include" statements
+  - added the noutchar global variable 
+  - changed all the 'extern' declarations to 'static', since all the routines are in
+    the same source file
+  - changed the first parameter in encode (and in lower level routines from a file stream
+    to a char array
+  - modifid the encode routine to return the size of the compressed array of bytes
+  - changed calls to printf and perror to call the CFITSIO ffpmsg routine
+  - modified the mywrite routine, and lower level byte writing routines,  to copy 
+    the output bytes to a char array, instead of writing them to a file stream
+  - replace "exit" statements with "return" statements
+  - changed the function declarations to the more modern ANSI C style
+
+ ############################################################################  */
+ 
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+
+// EAM : dropping fitsio prototypes
+# define LONGLONG long long
+# define DATA_COMPRESSION_ERR 413  /* error in imcompress routines */
+# define DATA_DECOMPRESSION_ERR 414 /* error in imcompress routines */
+
+static long noutchar;
+static long noutmax;
+
+static int htrans(int a[],int nx,int ny);
+static void digitize(int a[], int nx, int ny, int scale);
+static int encode(char *outfile, long *nlen, int a[], int nx, int ny, int scale);
+static void shuffle(int a[], int n, int n2, int tmp[]);
+
+static int htrans64(LONGLONG a[],int nx,int ny);
+static void digitize64(LONGLONG a[], int nx, int ny, int scale);
+static int encode64(char *outfile, long *nlen, LONGLONG a[], int nx, int ny, int scale);
+static void shuffle64(LONGLONG a[], int n, int n2, LONGLONG tmp[]);
+
+static  void writeint(char *outfile, int a);
+static  void writelonglong(char *outfile, LONGLONG a);
+static  int qwrite(char *outfile, char *a, int n); 
+static  int doencode(char *outfile, int a[], int nx, int ny, unsigned char nbitplanes[3]);
+static  int doencode64(char *outfile, LONGLONG a[], int nx, int ny, unsigned char nbitplanes[3]);
+static int  mywrite(char *file, char buffer[], int n);
+
+static int qtree_encode(char *outfile, int a[], int n, int nqx, int nqy, int nbitplanes);
+static int qtree_encode64(char *outfile, LONGLONG a[], int n, int nqx, int nqy, int nbitplanes);
+static void start_outputing_bits();
+static void done_outputing_bits(char *outfile);
+static void output_nbits(char *outfile, int bits, int n);
+
+static void qtree_onebit(int a[], int n, int nx, int ny, unsigned char b[], int bit);
+static void qtree_onebit64(LONGLONG a[], int n, int nx, int ny, unsigned char b[], int bit);
+static void qtree_reduce(unsigned char a[], int n, int nx, int ny, unsigned char b[]);
+static int  bufcopy(unsigned char a[], int n, unsigned char buffer[], int *b, int bmax);
+static void write_bdirect(char *outfile, int a[], int n,int nqx, int nqy, unsigned char scratch[], int bit);
+static void write_bdirect64(char *outfile, LONGLONG a[], int n,int nqx, int nqy, unsigned char scratch[], int bit);
+
+#define output_nybble(outfile,c)	output_nbits(outfile,c,4)
+#define output_huffman(outfile,c)	output_nbits(outfile,code[c],ncode[c])
+
+/* ---------------------------------------------------------------------- */
+int fits_hcompress(int *a, int ny, int nx, int scale, char *output, 
+                  long *nbytes, int *status)
+{
+  /* 
+     compress the input image using the H-compress algorithm
+  
+   a  - input image array
+   nx - size of X axis of image
+   ny - size of Y axis of image
+   scale - quantization scale factor. Larger values results in more (lossy) compression
+           scale = 0 does lossless compression
+   output - pre-allocated array to hold the output compressed stream of bytes
+   nbyts  - input value = size of the output buffer;
+            returned value = size of the compressed byte stream, in bytes
+
+ NOTE: the nx and ny dimensions as defined within this code are reversed from
+ the usual FITS notation.  ny is the fastest varying dimension, which is
+ usually considered the X axis in the FITS image display
+
+  */
+
+  int stat;
+  
+  if (*status > 0) return(*status);
+
+  /* H-transform */
+  stat = htrans(a, nx, ny);
+  if (stat) {
+     *status = stat;
+     return(*status);
+  }
+
+  /* digitize */
+  digitize(a, nx, ny, scale);
+
+  /* encode and write to output array */
+
+  noutmax = *nbytes;  /* input value is the allocated size of the array */
+  *nbytes = 0;  /* reset */
+
+  stat = encode(output, nbytes, a, nx, ny, scale);
+
+  *status = stat;
+  return(*status);
+}
+/* ---------------------------------------------------------------------- */
+int fits_hcompress64(LONGLONG *a, int ny, int nx, int scale, char *output, 
+                  long *nbytes, int *status)
+{
+  /* 
+     compress the input image using the H-compress algorithm
+  
+   a  - input image array
+   nx - size of X axis of image
+   ny - size of Y axis of image
+   scale - quantization scale factor. Larger values results in more (lossy) compression
+           scale = 0 does lossless compression
+   output - pre-allocated array to hold the output compressed stream of bytes
+   nbyts  - size of the compressed byte stream, in bytes
+
+ NOTE: the nx and ny dimensions as defined within this code are reversed from
+ the usual FITS notation.  ny is the fastest varying dimension, which is
+ usually considered the X axis in the FITS image display
+
+  */
+
+  int stat;
+  
+  if (*status > 0) return(*status);
+
+  /* H-transform */
+  stat = htrans64(a, nx, ny);
+  if (stat) {
+     *status = stat;
+     return(*status);
+  }
+
+  /* digitize */
+  digitize64(a, nx, ny, scale);
+
+  /* encode and write to output array */
+  noutmax = *nbytes;  /* input value is the allocated size of the array */
+  *nbytes = 0;  /* reset */
+
+  stat = encode64(output, nbytes, a, nx, ny, scale);
+
+  *status = stat;
+  return(*status);
+}
+
+ 
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* htrans.c   H-transform of NX x NY integer image
+ *
+ * Programmer: R. White		Date: 11 May 1992
+ */
+
+/* ######################################################################### */
+static int htrans(int a[],int nx,int ny)
+{
+int nmax, log2n, h0, hx, hy, hc, nxtop, nytop, i, j, k;
+int oddx, oddy;
+int shift, mask, mask2, prnd, prnd2, nrnd2;
+int s10, s00;
+int *tmp;
+
+	/*
+	 * log2n is log2 of max(nx,ny) rounded up to next power of 2
+	 */
+	nmax = (nx>ny) ? nx : ny;
+	log2n = (int) (log((float) nmax)/log(2.0)+0.5);
+	if ( nmax > (1<<log2n) ) {
+		log2n += 1;
+	}
+	/*
+	 * get temporary storage for shuffling elements
+	 */
+	tmp = (int *) malloc(((nmax+1)/2)*sizeof(int));
+	if(tmp == (int *) NULL) {
+	  // EAM : dropping Pence CFITSIO dependencies
+	  // XXX : add an error message function to gfits
+	  fprintf (stderr, "htrans: insufficient memory\n");
+	  return(DATA_COMPRESSION_ERR);
+	}
+	/*
+	 * set up rounding and shifting masks
+	 */
+	shift = 0;
+	mask  = -2;
+	mask2 = mask << 1;
+	prnd  = 1;
+	prnd2 = prnd << 1;
+	nrnd2 = prnd2 - 1;
+	/*
+	 * do log2n reductions
+	 *
+	 * We're indexing a as a 2-D array with dimensions (nx,ny).
+	 */
+	nxtop = nx;
+	nytop = ny;
+	for (k = 0; k<log2n; k++) {
+		oddx = nxtop % 2;
+		oddy = nytop % 2;
+		for (i = 0; i<nxtop-oddx; i += 2) {
+			s00 = i*ny;				/* s00 is index of a[i,j]	*/
+			s10 = s00+ny;			/* s10 is index of a[i+1,j]	*/
+			for (j = 0; j<nytop-oddy; j += 2) {
+				/*
+				 * Divide h0,hx,hy,hc by 2 (1 the first time through).
+				 */
+				h0 = (a[s10+1] + a[s10] + a[s00+1] + a[s00]) >> shift;
+				hx = (a[s10+1] + a[s10] - a[s00+1] - a[s00]) >> shift;
+				hy = (a[s10+1] - a[s10] + a[s00+1] - a[s00]) >> shift;
+				hc = (a[s10+1] - a[s10] - a[s00+1] + a[s00]) >> shift;
+				/*
+				 * Throw away the 2 bottom bits of h0, bottom bit of hx,hy.
+				 * To get rounding to be same for positive and negative
+				 * numbers, nrnd2 = prnd2 - 1.
+				 */
+				a[s10+1] = hc;
+				a[s10  ] = ( (hx>=0) ? (hx+prnd)  :  hx        ) & mask ;
+				a[s00+1] = ( (hy>=0) ? (hy+prnd)  :  hy        ) & mask ;
+				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
+				s00 += 2;
+				s10 += 2;
+			}
+			if (oddy) {
+				/*
+				 * do last element in row if row length is odd
+				 * s00+1, s10+1 are off edge
+				 */
+				h0 = (a[s10] + a[s00]) << (1-shift);
+				hx = (a[s10] - a[s00]) << (1-shift);
+				a[s10  ] = ( (hx>=0) ? (hx+prnd)  :  hx        ) & mask ;
+				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
+				s00 += 1;
+				s10 += 1;
+			}
+		}
+		if (oddx) {
+			/*
+			 * do last row if column length is odd
+			 * s10, s10+1 are off edge
+			 */
+			s00 = i*ny;
+			for (j = 0; j<nytop-oddy; j += 2) {
+				h0 = (a[s00+1] + a[s00]) << (1-shift);
+				hy = (a[s00+1] - a[s00]) << (1-shift);
+				a[s00+1] = ( (hy>=0) ? (hy+prnd)  :  hy        ) & mask ;
+				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
+				s00 += 2;
+			}
+			if (oddy) {
+				/*
+				 * do corner element if both row and column lengths are odd
+				 * s00+1, s10, s10+1 are off edge
+				 */
+				h0 = a[s00] << (2-shift);
+				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
+			}
+		}
+		/*
+		 * now shuffle in each dimension to group coefficients by order
+		 */
+		for (i = 0; i<nxtop; i++) {
+			shuffle(&a[ny*i],nytop,1,tmp);
+		}
+		for (j = 0; j<nytop; j++) {
+			shuffle(&a[j],nxtop,ny,tmp);
+		}
+		/*
+		 * image size reduced by 2 (round up if odd)
+		 */
+		nxtop = (nxtop+1)>>1;
+		nytop = (nytop+1)>>1;
+		/*
+		 * divisor doubles after first reduction
+		 */
+		shift = 1;
+		/*
+		 * masks, rounding values double after each iteration
+		 */
+		mask  = mask2;
+		prnd  = prnd2;
+		mask2 = mask2 << 1;
+		prnd2 = prnd2 << 1;
+		nrnd2 = prnd2 - 1;
+	}
+	free(tmp);
+	return(0);
+}
+/* ######################################################################### */
+
+static int htrans64(LONGLONG a[],int nx,int ny)
+{
+int nmax, log2n, nxtop, nytop, i, j, k;
+int oddx, oddy;
+int shift;
+int s10, s00;
+LONGLONG h0, hx, hy, hc, prnd, prnd2, nrnd2, mask, mask2;
+LONGLONG *tmp;
+
+	/*
+	 * log2n is log2 of max(nx,ny) rounded up to next power of 2
+	 */
+	nmax = (nx>ny) ? nx : ny;
+	log2n = (int) (log((float) nmax)/log(2.0)+0.5);
+	if ( nmax > (1<<log2n) ) {
+		log2n += 1;
+	}
+	/*
+	 * get temporary storage for shuffling elements
+	 */
+	tmp = (LONGLONG *) malloc(((nmax+1)/2)*sizeof(LONGLONG));
+	if(tmp == (LONGLONG *) NULL) {
+	  // EAM : dropping Pence CFITSIO dependencies
+	  // XXX : add an error message function to gfits
+	  fprintf (stderr, "htrans: insufficient memory\n"); 
+	  return(DATA_COMPRESSION_ERR);
+	}
+	/*
+	 * set up rounding and shifting masks
+	 */
+	shift = 0;
+	mask  = (LONGLONG) -2;
+	mask2 = mask << 1;
+	prnd  = (LONGLONG) 1;
+	prnd2 = prnd << 1;
+	nrnd2 = prnd2 - 1;
+	/*
+	 * do log2n reductions
+	 *
+	 * We're indexing a as a 2-D array with dimensions (nx,ny).
+	 */
+	nxtop = nx;
+	nytop = ny;
+	for (k = 0; k<log2n; k++) {
+		oddx = nxtop % 2;
+		oddy = nytop % 2;
+		for (i = 0; i<nxtop-oddx; i += 2) {
+			s00 = i*ny;				/* s00 is index of a[i,j]	*/
+			s10 = s00+ny;			/* s10 is index of a[i+1,j]	*/
+			for (j = 0; j<nytop-oddy; j += 2) {
+				/*
+				 * Divide h0,hx,hy,hc by 2 (1 the first time through).
+				 */
+				h0 = (a[s10+1] + a[s10] + a[s00+1] + a[s00]) >> shift;
+				hx = (a[s10+1] + a[s10] - a[s00+1] - a[s00]) >> shift;
+				hy = (a[s10+1] - a[s10] + a[s00+1] - a[s00]) >> shift;
+				hc = (a[s10+1] - a[s10] - a[s00+1] + a[s00]) >> shift;
+				/*
+				 * Throw away the 2 bottom bits of h0, bottom bit of hx,hy.
+				 * To get rounding to be same for positive and negative
+				 * numbers, nrnd2 = prnd2 - 1.
+				 */
+				a[s10+1] = hc;
+				a[s10  ] = ( (hx>=0) ? (hx+prnd)  :  hx        ) & mask ;
+				a[s00+1] = ( (hy>=0) ? (hy+prnd)  :  hy        ) & mask ;
+				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
+				s00 += 2;
+				s10 += 2;
+			}
+			if (oddy) {
+				/*
+				 * do last element in row if row length is odd
+				 * s00+1, s10+1 are off edge
+				 */
+				h0 = (a[s10] + a[s00]) << (1-shift);
+				hx = (a[s10] - a[s00]) << (1-shift);
+				a[s10  ] = ( (hx>=0) ? (hx+prnd)  :  hx        ) & mask ;
+				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
+				s00 += 1;
+				s10 += 1;
+			}
+		}
+		if (oddx) {
+			/*
+			 * do last row if column length is odd
+			 * s10, s10+1 are off edge
+			 */
+			s00 = i*ny;
+			for (j = 0; j<nytop-oddy; j += 2) {
+				h0 = (a[s00+1] + a[s00]) << (1-shift);
+				hy = (a[s00+1] - a[s00]) << (1-shift);
+				a[s00+1] = ( (hy>=0) ? (hy+prnd)  :  hy        ) & mask ;
+				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
+				s00 += 2;
+			}
+			if (oddy) {
+				/*
+				 * do corner element if both row and column lengths are odd
+				 * s00+1, s10, s10+1 are off edge
+				 */
+				h0 = a[s00] << (2-shift);
+				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
+			}
+		}
+		/*
+		 * now shuffle in each dimension to group coefficients by order
+		 */
+		for (i = 0; i<nxtop; i++) {
+			shuffle64(&a[ny*i],nytop,1,tmp);
+		}
+		for (j = 0; j<nytop; j++) {
+			shuffle64(&a[j],nxtop,ny,tmp);
+		}
+		/*
+		 * image size reduced by 2 (round up if odd)
+		 */
+		nxtop = (nxtop+1)>>1;
+		nytop = (nytop+1)>>1;
+		/*
+		 * divisor doubles after first reduction
+		 */
+		shift = 1;
+		/*
+		 * masks, rounding values double after each iteration
+		 */
+		mask  = mask2;
+		prnd  = prnd2;
+		mask2 = mask2 << 1;
+		prnd2 = prnd2 << 1;
+		nrnd2 = prnd2 - 1;
+	}
+	free(tmp);
+	return(0);
+}
+
+/* ######################################################################### */
+static void
+shuffle(int a[], int n, int n2, int tmp[])
+{
+
+/* 
+int a[];	 array to shuffle					
+int n;		 number of elements to shuffle	
+int n2;		 second dimension					
+int tmp[];	 scratch storage					
+*/
+
+int i;
+int *p1, *p2, *pt;
+
+	/*
+	 * copy odd elements to tmp
+	 */
+	pt = tmp;
+	p1 = &a[n2];
+	for (i=1; i < n; i += 2) {
+		*pt = *p1;
+		pt += 1;
+		p1 += (n2+n2);
+	}
+	/*
+	 * compress even elements into first half of A
+	 */
+	p1 = &a[n2];
+	p2 = &a[n2+n2];
+	for (i=2; i<n; i += 2) {
+		*p1 = *p2;
+		p1 += n2;
+		p2 += (n2+n2);
+	}
+	/*
+	 * put odd elements into 2nd half
+	 */
+	pt = tmp;
+	for (i = 1; i<n; i += 2) {
+		*p1 = *pt;
+		p1 += n2;
+		pt += 1;
+	}
+}
+/* ######################################################################### */
+static void
+shuffle64(LONGLONG a[], int n, int n2, LONGLONG tmp[])
+{
+
+/* 
+LONGLONG a[];	 array to shuffle					
+int n;		 number of elements to shuffle	
+int n2;		 second dimension					
+LONGLONG tmp[];	 scratch storage					
+*/
+
+int i;
+LONGLONG *p1, *p2, *pt;
+
+	/*
+	 * copy odd elements to tmp
+	 */
+	pt = tmp;
+	p1 = &a[n2];
+	for (i=1; i < n; i += 2) {
+		*pt = *p1;
+		pt += 1;
+		p1 += (n2+n2);
+	}
+	/*
+	 * compress even elements into first half of A
+	 */
+	p1 = &a[n2];
+	p2 = &a[n2+n2];
+	for (i=2; i<n; i += 2) {
+		*p1 = *p2;
+		p1 += n2;
+		p2 += (n2+n2);
+	}
+	/*
+	 * put odd elements into 2nd half
+	 */
+	pt = tmp;
+	for (i = 1; i<n; i += 2) {
+		*p1 = *pt;
+		p1 += n2;
+		pt += 1;
+	}
+}
+/* ######################################################################### */
+/* ######################################################################### */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* digitize.c	digitize H-transform
+ *
+ * Programmer: R. White		Date: 11 March 1991
+ */
+
+/* ######################################################################### */
+static void  
+digitize(int a[], int nx, int ny, int scale)
+{
+int d, *p;
+
+	/*
+	 * round to multiple of scale
+	 */
+	if (scale <= 1) return;
+	d=(scale+1)/2-1;
+	for (p=a; p <= &a[nx*ny-1]; p++) *p = ((*p>0) ? (*p+d) : (*p-d))/scale;
+}
+
+/* ######################################################################### */
+static void  
+digitize64(LONGLONG a[], int nx, int ny, int scale)
+{
+LONGLONG d, *p, scale64;
+
+	/*
+	 * round to multiple of scale
+	 */
+	if (scale <= 1) return;
+	d=(scale+1)/2-1;
+	scale64 = scale;  /* use a 64-bit int for efficiency in the big loop */
+
+	for (p=a; p <= &a[nx*ny-1]; p++) *p = ((*p>0) ? (*p+d) : (*p-d))/scale64;
+}
+/* ######################################################################### */
+/* ######################################################################### */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* encode.c		encode H-transform and write to outfile
+ *
+ * Programmer: R. White		Date: 2 February 1994
+ */
+
+static char code_magic[2] = { (char)0xDD, (char)0x99 };
+
+
+/* ######################################################################### */
+static int encode(char *outfile, long *nlength, int a[], int nx, int ny, int scale)
+{
+
+/* FILE *outfile;  - change outfile to a char array */  
+/*
+  long * nlength    returned length (in bytes) of the encoded array)
+  int a[];								 input H-transform array (nx,ny)
+  int nx,ny;								 size of H-transform array	
+  int scale;								 scale factor for digitization
+*/
+int nel, nx2, ny2, i, j, k, q, vmax[3], nsign, bits_to_go;
+unsigned char nbitplanes[3];
+unsigned char *signbits;
+int stat = 0;
+
+        noutchar = 0;  /* initialize the number of compressed bytes that have been written */
+	nel = nx*ny;
+	/*
+	 * write magic value
+	 */
+	qwrite(outfile, code_magic, sizeof(code_magic));
+	writeint(outfile, nx);			/* size of image */
+	writeint(outfile, ny);
+	writeint(outfile, scale);		/* scale factor for digitization */
+	/*
+	 * write first value of A (sum of all pixels -- the only value
+	 * which does not compress well)
+	 */
+	writelonglong(outfile, (LONGLONG) a[0]);
+
+	a[0] = 0;
+	/*
+	 * allocate array for sign bits and save values, 8 per byte
+	 */
+	signbits = (unsigned char *) malloc((nel+7)/8);
+	if (signbits == (unsigned char *) NULL) {
+	  // EAM : dropping Pence CFITSIO dependencies
+	  // XXX : add an error message function to gfits
+	  fprintf(stderr, "encode: insufficient memory\n"); 
+	  return(DATA_COMPRESSION_ERR);
+	}
+	nsign = 0;
+	bits_to_go = 8;
+	signbits[0] = 0;
+	for (i=0; i<nel; i++) {
+		if (a[i] > 0) {
+			/*
+			 * positive element, put zero at end of buffer
+			 */
+			signbits[nsign] <<= 1;
+			bits_to_go -= 1;
+		} else if (a[i] < 0) {
+			/*
+			 * negative element, shift in a one
+			 */
+			signbits[nsign] <<= 1;
+			signbits[nsign] |= 1;
+			bits_to_go -= 1;
+			/*
+			 * replace a by absolute value
+			 */
+			a[i] = -a[i];
+		}
+		if (bits_to_go == 0) {
+			/*
+			 * filled up this byte, go to the next one
+			 */
+			bits_to_go = 8;
+			nsign += 1;
+			signbits[nsign] = 0;
+		}
+	}
+	if (bits_to_go != 8) {
+		/*
+		 * some bits in last element
+		 * move bits in last byte to bottom and increment nsign
+		 */
+		signbits[nsign] <<= bits_to_go;
+		nsign += 1;
+	}
+
+	/*
+	 * calculate number of bit planes for 3 quadrants
+	 *
+	 * quadrant 0=bottom left, 1=bottom right or top left, 2=top right, 
+	 */
+	for (q=0; q<3; q++) {
+		vmax[q] = 0;
+	}
+	/*
+	 * get maximum absolute value in each quadrant
+	 */
+	nx2 = (nx+1)/2;
+	ny2 = (ny+1)/2;
+	j=0;	/* column counter	*/
+	k=0;	/* row counter		*/
+	for (i=0; i<nel; i++) {
+		q = (j>=ny2) + (k>=nx2);
+		if (vmax[q] < a[i]) vmax[q] = a[i];
+		if (++j >= ny) {
+			j = 0;
+			k += 1;
+		}
+	}
+	/*
+	 * now calculate number of bits for each quadrant
+	 */
+	for (q = 0; q < 3; q++) {
+		nbitplanes[q] = (int) (log((float) (vmax[q]+1))/log(2.0)+0.5);
+		if ( (vmax[q]+1) > (1<<nbitplanes[q]) ) {
+			nbitplanes[q] += 1;
+		}
+	}
+	/*
+	 * write nbitplanes
+	 */
+	if (0 == qwrite(outfile, (char *) nbitplanes, sizeof(nbitplanes))) {
+	        *nlength = noutchar;
+
+		fprintf (stderr, "encode: output buffer too small\n");
+		return (DATA_COMPRESSION_ERR);
+        }
+	 
+	/*
+	 * write coded array
+	 */
+	stat = doencode(outfile, a, nx, ny, nbitplanes);
+	/*
+	 * write sign bits
+	 */
+
+	if (nsign > 0) {
+
+	   if ( 0 == qwrite(outfile, (char *) signbits, nsign)) {
+	        free(signbits);
+	        *nlength = noutchar;
+		fprintf (stderr, "encode: output buffer too small");
+		return(DATA_COMPRESSION_ERR);
+          }
+	} 
+	
+	free(signbits);
+	*nlength = noutchar;
+
+        if (noutchar >= noutmax) {
+	  fprintf (stderr, "encode: output buffer too small");
+	  return(DATA_COMPRESSION_ERR);
+        }  
+	
+	return(stat); 
+}
+/* ######################################################################### */
+static int encode64(char *outfile, long *nlength, LONGLONG a[], int nx, int ny, int scale)
+{
+
+/* FILE *outfile;  - change outfile to a char array */  
+/*
+  long * nlength    returned length (in bytes) of the encoded array)
+  LONGLONG a[];								 input H-transform array (nx,ny)
+  int nx,ny;								 size of H-transform array	
+  int scale;								 scale factor for digitization
+*/
+int nel, nx2, ny2, i, j, k, q, nsign, bits_to_go;
+LONGLONG vmax[3];
+unsigned char nbitplanes[3];
+unsigned char *signbits;
+int stat = 0;
+
+        noutchar = 0;  /* initialize the number of compressed bytes that have been written */
+	nel = nx*ny;
+	/*
+	 * write magic value
+	 */
+	qwrite(outfile, code_magic, sizeof(code_magic));
+	writeint(outfile, nx);				/* size of image	*/
+	writeint(outfile, ny);
+	writeint(outfile, scale);			/* scale factor for digitization */
+	/*
+	 * write first value of A (sum of all pixels -- the only value
+	 * which does not compress well)
+	 */
+
+	writelonglong(outfile, a[0]);
+	a[0] = 0;
+	/*
+	 * allocate array for sign bits and save values, 8 per byte
+	 */
+	signbits = (unsigned char *) malloc((nel+7)/8);
+	if (signbits == (unsigned char *) NULL) {
+	  fprintf (stderr, "encode64: insufficient memory");
+	  return(DATA_COMPRESSION_ERR);
+	}
+	nsign = 0;
+	bits_to_go = 8;
+	signbits[0] = 0;
+	for (i=0; i<nel; i++) {
+		if (a[i] > 0) {
+			/*
+			 * positive element, put zero at end of buffer
+			 */
+			signbits[nsign] <<= 1;
+			bits_to_go -= 1;
+		} else if (a[i] < 0) {
+			/*
+			 * negative element, shift in a one
+			 */
+			signbits[nsign] <<= 1;
+			signbits[nsign] |= 1;
+			bits_to_go -= 1;
+			/*
+			 * replace a by absolute value
+			 */
+			a[i] = -a[i];
+		}
+		if (bits_to_go == 0) {
+			/*
+			 * filled up this byte, go to the next one
+			 */
+			bits_to_go = 8;
+			nsign += 1;
+			signbits[nsign] = 0;
+		}
+	}
+	if (bits_to_go != 8) {
+		/*
+		 * some bits in last element
+		 * move bits in last byte to bottom and increment nsign
+		 */
+		signbits[nsign] <<= bits_to_go;
+		nsign += 1;
+	}
+	/*
+	 * calculate number of bit planes for 3 quadrants
+	 *
+	 * quadrant 0=bottom left, 1=bottom right or top left, 2=top right, 
+	 */
+	for (q=0; q<3; q++) {
+		vmax[q] = 0;
+	}
+	/*
+	 * get maximum absolute value in each quadrant
+	 */
+	nx2 = (nx+1)/2;
+	ny2 = (ny+1)/2;
+
+        i = 0;
+        for (k=0; k<ny; k++) {
+            for (j=0; j<nx; j++) {
+                q = (j>=ny2) + (k>=nx2);
+                vmax[q] |= a[i];
+                i++;
+            }
+        } 
+
+	/*
+	 * now calculate number of bits for each quadrant
+	 */
+	 
+        /* this is a more efficient way to do this, */
+ 
+ 
+        for (q = 0; q < 3; q++) {
+            for (nbitplanes[q] = 0; vmax[q]>0; vmax[q] = vmax[q]>>1, nbitplanes[q]++) ; 
+        }
+
+
+/*
+	for (q = 0; q < 3; q++) {
+		nbitplanes[q] = log((float) (vmax[q]+1))/log(2.0)+0.5;
+		if ( (vmax[q]+1) > (((LONGLONG) 1)<<nbitplanes[q]) ) {
+			nbitplanes[q] += 1;
+		}
+	}
+*/
+
+	/*
+	 * write nbitplanes
+	 */
+
+	if (0 == qwrite(outfile, (char *) nbitplanes, sizeof(nbitplanes))) {
+	        *nlength = noutchar;
+		fprintf (stderr, "encode: output buffer too small");
+		return(DATA_COMPRESSION_ERR);
+        }
+	 
+	/*
+	 * write coded array
+	 */
+	stat = doencode64(outfile, a, nx, ny, nbitplanes);
+	/*
+	 * write sign bits
+	 */
+
+	if (nsign > 0) {
+
+	   if ( 0 == qwrite(outfile, (char *) signbits, nsign)) {
+	        free(signbits);
+	        *nlength = noutchar;
+		fprintf (stderr, "encode: output buffer too small");
+		return(DATA_COMPRESSION_ERR);
+          }
+	} 
+
+	free(signbits);
+	
+	*nlength = noutchar;
+
+        if (noutchar >= noutmax) {
+		fprintf (stderr, "encode64: output buffer too small");
+		return(DATA_COMPRESSION_ERR);
+        }
+		
+	return(stat); 
+}
+/* ######################################################################### */
+/* ######################################################################### */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* qwrite.c	Write binary data
+ *
+ * Programmer: R. White		Date: 11 March 1991
+ */
+
+/* ######################################################################### */
+static void
+writeint(char *outfile, int a)
+{
+int i;
+unsigned char b[4];
+
+	/* Write integer A one byte at a time to outfile.
+	 *
+	 * This is portable from Vax to Sun since it eliminates the
+	 * need for byte-swapping.
+	 */
+	for (i=3; i>=0; i--) {
+		b[i] = a & 0x000000ff;
+		a >>= 8;
+	}
+	for (i=0; i<4; i++) qwrite(outfile, (char *) &b[i],1);
+}
+
+/* ######################################################################### */
+static void
+writelonglong(char *outfile, LONGLONG a)
+{
+int i;
+unsigned char b[8];
+
+	/* Write integer A one byte at a time to outfile.
+	 *
+	 * This is portable from Vax to Sun since it eliminates the
+	 * need for byte-swapping.
+	 */
+	for (i=7; i>=0; i--) {
+		b[i] = (unsigned char) (a & 0x000000ff);
+		a >>= 8;
+	}
+	for (i=0; i<8; i++) qwrite(outfile, (char *) &b[i],1);
+}
+
+/* ######################################################################### */
+static int qwrite(char *outfile, char *a, int n)
+{
+int nwrite;
+
+	nwrite = mywrite(outfile, a, n);	
+	return(nwrite);      /* added by WDP to prevent compiler warning */
+}
+
+/* ######################################################################### */
+static int
+mywrite(char *file, char buffer[], int n)
+{
+    /*
+     * write n bytes from buffer into file
+     * returns number of bytes read (=n) if successful, <=0 if not
+     */
+
+     if (noutchar + n > noutmax) return(0);  /* buffer overflow */
+     
+     memcpy(&file[noutchar], buffer, n);
+     noutchar += n;
+
+     return(n);
+}
+/* ######################################################################### */
+/* ######################################################################### */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* doencode.c	Encode 2-D array and write stream of characters on outfile
+ *
+ * This version assumes that A is positive.
+ *
+ * Programmer: R. White		Date: 7 May 1991
+ */
+
+/* ######################################################################### */
+static int
+doencode(char *outfile, int a[], int nx, int ny, unsigned char nbitplanes[3])
+{
+/* char *outfile;						 output data stream
+int a[];							 Array of values to encode			
+int nx,ny;							 Array dimensions [nx][ny]			
+unsigned char nbitplanes[3];		 Number of bit planes in quadrants	
+*/
+
+int nx2, ny2, stat = 0;
+
+	nx2 = (nx+1)/2;
+	ny2 = (ny+1)/2;
+	/*
+	 * Initialize bit output
+	 */
+	start_outputing_bits();
+	/*
+	 * write out the bit planes for each quadrant
+	 */
+	stat = qtree_encode(outfile, &a[0],          ny, nx2,  ny2,  nbitplanes[0]);
+
+        if (!stat)
+		stat = qtree_encode(outfile, &a[ny2],        ny, nx2,  ny/2, nbitplanes[1]);
+
+        if (!stat)
+		stat = qtree_encode(outfile, &a[ny*nx2],     ny, nx/2, ny2,  nbitplanes[1]);
+
+        if (!stat)
+		stat = qtree_encode(outfile, &a[ny*nx2+ny2], ny, nx/2, ny/2, nbitplanes[2]);
+	/*
+	 * Add zero as an EOF symbol
+	 */
+	output_nybble(outfile, 0);
+	done_outputing_bits(outfile);
+	
+	return(stat);
+}
+/* ######################################################################### */
+static int
+doencode64(char *outfile, LONGLONG a[], int nx, int ny, unsigned char nbitplanes[3])
+{
+/* char *outfile;						 output data stream
+LONGLONG a[];							 Array of values to encode			
+int nx,ny;							 Array dimensions [nx][ny]			
+unsigned char nbitplanes[3];		 Number of bit planes in quadrants	
+*/
+
+int nx2, ny2, stat = 0;
+
+	nx2 = (nx+1)/2;
+	ny2 = (ny+1)/2;
+	/*
+	 * Initialize bit output
+	 */
+	start_outputing_bits();
+	/*
+	 * write out the bit planes for each quadrant
+	 */
+	stat = qtree_encode64(outfile, &a[0],          ny, nx2,  ny2,  nbitplanes[0]);
+
+        if (!stat)
+		stat = qtree_encode64(outfile, &a[ny2],        ny, nx2,  ny/2, nbitplanes[1]);
+
+        if (!stat)
+		stat = qtree_encode64(outfile, &a[ny*nx2],     ny, nx/2, ny2,  nbitplanes[1]);
+
+        if (!stat)
+		stat = qtree_encode64(outfile, &a[ny*nx2+ny2], ny, nx/2, ny/2, nbitplanes[2]);
+	/*
+	 * Add zero as an EOF symbol
+	 */
+	output_nybble(outfile, 0);
+	done_outputing_bits(outfile);
+	
+	return(stat);
+}
+/* ######################################################################### */
+/* ######################################################################### */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* BIT OUTPUT ROUTINES */
+
+
+static LONGLONG bitcount;
+
+/* THE BIT BUFFER */
+
+static int buffer2;			/* Bits buffered for output	*/
+static int bits_to_go2;			/* Number of bits free in buffer */
+
+
+/* ######################################################################### */
+/* INITIALIZE FOR BIT OUTPUT */
+
+static void
+start_outputing_bits()
+{
+	buffer2 = 0;			/* Buffer is empty to start	*/
+	bits_to_go2 = 8;		/* with				*/
+	bitcount = 0;
+}
+
+/* ######################################################################### */
+/* OUTPUT N BITS (N must be <= 8) */
+
+static void
+output_nbits(char *outfile, int bits, int n)
+{
+	/*
+	 * insert bits at end of buffer
+	 */
+	buffer2 <<= n;
+	buffer2 |= ( bits & ((1<<n)-1) );
+	bits_to_go2 -= n;
+	if (bits_to_go2 <= 0) {
+		/*
+		 * buffer2 full, put out top 8 bits
+		 */
+/*		putc((buffer2>>(-bits_to_go2)) & 0xff,outfile); */
+
+	        outfile[noutchar] = ((buffer2>>(-bits_to_go2)) & 0xff);
+
+		if (noutchar < noutmax) noutchar++;
+		
+		bits_to_go2 += 8;
+	}
+	bitcount += n;
+}
+
+
+/* ######################################################################### */
+/* FLUSH OUT THE LAST BITS */
+
+static void
+done_outputing_bits(char *outfile)
+{
+	if(bits_to_go2 < 8) {
+/*		putc(buffer2<<bits_to_go2,outfile); */
+
+	        outfile[noutchar] = (buffer2<<bits_to_go2);
+		if (noutchar < noutmax) noutchar++;
+
+		/* count the garbage bits too */
+		bitcount += bits_to_go2;
+	}
+}
+/* ######################################################################### */
+/* ######################################################################### */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* qtree_encode.c	Encode values in quadrant of 2-D array using binary
+ *					quadtree coding for each bit plane.  Assumes array is
+ *					positive.
+ *
+ * Programmer: R. White		Date: 15 May 1991
+ */
+
+/*
+ * Huffman code values and number of bits in each code
+ */
+static int code[16] =
+	{
+	0x3e, 0x00, 0x01, 0x08, 0x02, 0x09, 0x1a, 0x1b,
+	0x03, 0x1c, 0x0a, 0x1d, 0x0b, 0x1e, 0x3f, 0x0c
+	};
+static int ncode[16] =
+	{
+	6,    3,    3,    4,    3,    4,    5,    5,
+	3,    5,    4,    5,    4,    5,    6,    4
+	};
+
+/*
+ * variables for bit output to buffer when Huffman coding
+ */
+static int bitbuffer, bits_to_go3;
+
+/*
+ * macros to write out 4-bit nybble, Huffman code for this value
+ */
+
+
+/* ######################################################################### */
+static int
+qtree_encode(char *outfile, int a[], int n, int nqx, int nqy, int nbitplanes)
+{
+
+/*
+int a[];
+int n;								 physical dimension of row in a		
+int nqx;							 length of row			
+int nqy;							 length of column (<=n)				
+int nbitplanes;						 number of bit planes to output	
+*/
+	
+int log2n, i, k, bit, b, bmax, nqmax, nqx2, nqy2, nx, ny;
+unsigned char *scratch, *buffer;
+
+	/*
+	 * log2n is log2 of max(nqx,nqy) rounded up to next power of 2
+	 */
+	nqmax = (nqx>nqy) ? nqx : nqy;
+	log2n = (int) (log((float) nqmax)/log(2.0)+0.5);
+	if (nqmax > (1<<log2n)) {
+		log2n += 1;
+	}
+	/*
+	 * initialize buffer point, max buffer size
+	 */
+	nqx2 = (nqx+1)/2;
+	nqy2 = (nqy+1)/2;
+	bmax = (nqx2*nqy2+1)/2;
+	/*
+	 * We're indexing A as a 2-D array with dimensions (nqx,nqy).
+	 * Scratch is 2-D with dimensions (nqx/2,nqy/2) rounded up.
+	 * Buffer is used to store string of codes for output.
+	 */
+	scratch = (unsigned char *) malloc(2*bmax);
+	buffer = (unsigned char *) malloc(bmax);
+	if ((scratch == (unsigned char *) NULL) ||
+		(buffer  == (unsigned char *) NULL)) {
+/*		fprintf(stderr, "qtree_encode: insufficient memory\n");
+		exit(-1);  */
+		
+		fprintf (stderr, "qtree_encode: insufficient memory");
+		return(DATA_COMPRESSION_ERR);
+	}
+	/*
+	 * now encode each bit plane, starting with the top
+	 */
+	for (bit=nbitplanes-1; bit >= 0; bit--) {
+		/*
+		 * initial bit buffer
+		 */
+		b = 0;
+		bitbuffer = 0;
+		bits_to_go3 = 0;
+		/*
+		 * on first pass copy A to scratch array
+		 */
+		qtree_onebit(a,n,nqx,nqy,scratch,bit);
+		nx = (nqx+1)>>1;
+		ny = (nqy+1)>>1;
+		/*
+		 * copy non-zero values to output buffer, which will be written
+		 * in reverse order
+		 */
+		if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) {
+			/*
+			 * quadtree is expanding data,
+			 * change warning code and just fill buffer with bit-map
+			 */
+			write_bdirect(outfile,a,n,nqx,nqy,scratch,bit);
+			goto bitplane_done;
+		}
+		/*
+		 * do log2n reductions
+		 */
+		for (k = 1; k<log2n; k++) {
+			qtree_reduce(scratch,ny,nx,ny,scratch);
+			nx = (nx+1)>>1;
+			ny = (ny+1)>>1;
+			if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) {
+				write_bdirect(outfile,a,n,nqx,nqy,scratch,bit);
+				goto bitplane_done;
+			}
+		}
+		/*
+		 * OK, we've got the code in buffer
+		 * Write quadtree warning code, then write buffer in reverse order
+		 */
+		output_nybble(outfile,0xF);
+		if (b==0) {
+			if (bits_to_go3>0) {
+				/*
+				 * put out the last few bits
+				 */
+				output_nbits(outfile, bitbuffer & ((1<<bits_to_go3)-1),
+					bits_to_go3);
+			} else {
+				/*
+				 * have to write a zero nybble if there are no 1's in array
+				 */
+				output_huffman(outfile,0);
+			}
+		} else {
+			if (bits_to_go3>0) {
+				/*
+				 * put out the last few bits
+				 */
+				output_nbits(outfile, bitbuffer & ((1<<bits_to_go3)-1),
+					bits_to_go3);
+			}
+			for (i=b-1; i>=0; i--) {
+				output_nbits(outfile,buffer[i],8);
+			}
+		}
+		bitplane_done: ;
+	}
+	free(buffer);
+	free(scratch);
+	return(0);
+}
+/* ######################################################################### */
+static int
+qtree_encode64(char *outfile, LONGLONG a[], int n, int nqx, int nqy, int nbitplanes)
+{
+
+/*
+LONGLONG a[];
+int n;								 physical dimension of row in a		
+int nqx;							 length of row			
+int nqy;							 length of column (<=n)				
+int nbitplanes;						 number of bit planes to output	
+*/
+	
+int log2n, i, k, bit, b, nqmax, nqx2, nqy2, nx, ny;
+int bmax;  /* this potentially needs to be made a 64-bit int to support large arrays */
+unsigned char *scratch, *buffer;
+
+	/*
+	 * log2n is log2 of max(nqx,nqy) rounded up to next power of 2
+	 */
+	nqmax = (nqx>nqy) ? nqx : nqy;
+	log2n = (int) (log((float) nqmax)/log(2.0)+0.5);
+	if (nqmax > (1<<log2n)) {
+		log2n += 1;
+	}
+	/*
+	 * initialize buffer point, max buffer size
+	 */
+	nqx2 = (nqx+1)/2;
+	nqy2 = (nqy+1)/2;
+	bmax = (( nqx2)* ( nqy2)+1)/2;
+	/*
+	 * We're indexing A as a 2-D array with dimensions (nqx,nqy).
+	 * Scratch is 2-D with dimensions (nqx/2,nqy/2) rounded up.
+	 * Buffer is used to store string of codes for output.
+	 */
+	scratch = (unsigned char *) malloc(2*bmax);
+	buffer = (unsigned char *) malloc(bmax);
+	if ((scratch == (unsigned char *) NULL) ||
+		(buffer  == (unsigned char *) NULL)) {
+/*		fprintf(stderr, "qtree_encode: insufficient memory\n");
+		exit(-1);  */
+		
+		fprintf (stderr, "qtree_encode64: insufficient memory");
+		return(DATA_COMPRESSION_ERR);
+	}
+	/*
+	 * now encode each bit plane, starting with the top
+	 */
+	for (bit=nbitplanes-1; bit >= 0; bit--) {
+		/*
+		 * initial bit buffer
+		 */
+		b = 0;
+		bitbuffer = 0;
+		bits_to_go3 = 0;
+		/*
+		 * on first pass copy A to scratch array
+		 */
+		qtree_onebit64(a,n,nqx,nqy,scratch,bit);
+		nx = (nqx+1)>>1;
+		ny = (nqy+1)>>1;
+		/*
+		 * copy non-zero values to output buffer, which will be written
+		 * in reverse order
+		 */
+		if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) {
+			/*
+			 * quadtree is expanding data,
+			 * change warning code and just fill buffer with bit-map
+			 */
+			write_bdirect64(outfile,a,n,nqx,nqy,scratch,bit);
+			goto bitplane_done;
+		}
+		/*
+		 * do log2n reductions
+		 */
+		for (k = 1; k<log2n; k++) {
+			qtree_reduce(scratch,ny,nx,ny,scratch);
+			nx = (nx+1)>>1;
+			ny = (ny+1)>>1;
+			if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) {
+				write_bdirect64(outfile,a,n,nqx,nqy,scratch,bit);
+				goto bitplane_done;
+			}
+		}
+		/*
+		 * OK, we've got the code in buffer
+		 * Write quadtree warning code, then write buffer in reverse order
+		 */
+		output_nybble(outfile,0xF);
+		if (b==0) {
+			if (bits_to_go3>0) {
+				/*
+				 * put out the last few bits
+				 */
+				output_nbits(outfile, bitbuffer & ((1<<bits_to_go3)-1),
+					bits_to_go3);
+			} else {
+				/*
+				 * have to write a zero nybble if there are no 1's in array
+				 */
+				output_huffman(outfile,0);
+			}
+		} else {
+			if (bits_to_go3>0) {
+				/*
+				 * put out the last few bits
+				 */
+				output_nbits(outfile, bitbuffer & ((1<<bits_to_go3)-1),
+					bits_to_go3);
+			}
+			for (i=b-1; i>=0; i--) {
+				output_nbits(outfile,buffer[i],8);
+			}
+		}
+		bitplane_done: ;
+	}
+	free(buffer);
+	free(scratch);
+	return(0);
+}
+
+/* ######################################################################### */
+/*
+ * copy non-zero codes from array to buffer
+ */
+static int
+bufcopy(unsigned char a[], int n, unsigned char buffer[], int *b, int bmax)
+{
+int i;
+
+	for (i = 0; i < n; i++) {
+		if (a[i] != 0) {
+			/*
+			 * add Huffman code for a[i] to buffer
+			 */
+			bitbuffer |= code[a[i]] << bits_to_go3;
+			bits_to_go3 += ncode[a[i]];
+			if (bits_to_go3 >= 8) {
+				buffer[*b] = bitbuffer & 0xFF;
+				*b += 1;
+				/*
+				 * return warning code if we fill buffer
+				 */
+				if (*b >= bmax) return(1);
+				bitbuffer >>= 8;
+				bits_to_go3 -= 8;
+			}
+		}
+	}
+	return(0);
+}
+
+/* ######################################################################### */
+/*
+ * Do first quadtree reduction step on bit BIT of array A.
+ * Results put into B.
+ * 
+ */
+static void
+qtree_onebit(int a[], int n, int nx, int ny, unsigned char b[], int bit)
+{
+int i, j, k;
+int b0, b1, b2, b3;
+int s10, s00;
+
+	/*
+	 * use selected bit to get amount to shift
+	 */
+	b0 = 1<<bit;
+	b1 = b0<<1;
+	b2 = b0<<2;
+	b3 = b0<<3;
+	k = 0;							/* k is index of b[i/2,j/2]	*/
+	for (i = 0; i<nx-1; i += 2) {
+		s00 = n*i;					/* s00 is index of a[i,j]	*/
+		s10 = s00+n;				/* s10 is index of a[i+1,j]	*/
+		for (j = 0; j<ny-1; j += 2) {
+			b[k] = ( ( a[s10+1]     & b0)
+				   | ((a[s10  ]<<1) & b1)
+				   | ((a[s00+1]<<2) & b2)
+				   | ((a[s00  ]<<3) & b3) ) >> bit;
+			k += 1;
+			s00 += 2;
+			s10 += 2;
+		}
+		if (j < ny) {
+			/*
+			 * row size is odd, do last element in row
+			 * s00+1,s10+1 are off edge
+			 */
+			b[k] = ( ((a[s10  ]<<1) & b1)
+				   | ((a[s00  ]<<3) & b3) ) >> bit;
+			k += 1;
+		}
+	}
+	if (i < nx) {
+		/*
+		 * column size is odd, do last row
+		 * s10,s10+1 are off edge
+		 */
+		s00 = n*i;
+		for (j = 0; j<ny-1; j += 2) {
+			b[k] = ( ((a[s00+1]<<2) & b2)
+				   | ((a[s00  ]<<3) & b3) ) >> bit;
+			k += 1;
+			s00 += 2;
+		}
+		if (j < ny) {
+			/*
+			 * both row and column size are odd, do corner element
+			 * s00+1, s10, s10+1 are off edge
+			 */
+			b[k] = ( ((a[s00  ]<<3) & b3) ) >> bit;
+			k += 1;
+		}
+	}
+}
+/* ######################################################################### */
+/*
+ * Do first quadtree reduction step on bit BIT of array A.
+ * Results put into B.
+ * 
+ */
+static void
+qtree_onebit64(LONGLONG a[], int n, int nx, int ny, unsigned char b[], int bit)
+{
+int i, j, k;
+LONGLONG b0, b1, b2, b3;
+int s10, s00;
+
+	/*
+	 * use selected bit to get amount to shift
+	 */
+	b0 = ((LONGLONG) 1)<<bit;
+	b1 = b0<<1;
+	b2 = b0<<2;
+	b3 = b0<<3;
+	k = 0;							/* k is index of b[i/2,j/2]	*/
+	for (i = 0; i<nx-1; i += 2) {
+		s00 = n*i;					/* s00 is index of a[i,j]	*/
+		s10 = s00+n;				/* s10 is index of a[i+1,j]	*/
+		for (j = 0; j<ny-1; j += 2) {
+			b[k] = (unsigned char) (( ( a[s10+1]     & b0)
+				   | ((a[s10  ]<<1) & b1)
+				   | ((a[s00+1]<<2) & b2)
+				   | ((a[s00  ]<<3) & b3) ) >> bit);
+			k += 1;
+			s00 += 2;
+			s10 += 2;
+		}
+		if (j < ny) {
+			/*
+			 * row size is odd, do last element in row
+			 * s00+1,s10+1 are off edge
+			 */
+			b[k] = (unsigned char) (( ((a[s10  ]<<1) & b1)
+				   | ((a[s00  ]<<3) & b3) ) >> bit);
+			k += 1;
+		}
+	}
+	if (i < nx) {
+		/*
+		 * column size is odd, do last row
+		 * s10,s10+1 are off edge
+		 */
+		s00 = n*i;
+		for (j = 0; j<ny-1; j += 2) {
+			b[k] = (unsigned char) (( ((a[s00+1]<<2) & b2)
+				   | ((a[s00  ]<<3) & b3) ) >> bit);
+			k += 1;
+			s00 += 2;
+		}
+		if (j < ny) {
+			/*
+			 * both row and column size are odd, do corner element
+			 * s00+1, s10, s10+1 are off edge
+			 */
+			b[k] = (unsigned char) (( ((a[s00  ]<<3) & b3) ) >> bit);
+			k += 1;
+		}
+	}
+}
+
+/* ######################################################################### */
+/*
+ * do one quadtree reduction step on array a
+ * results put into b (which may be the same as a)
+ */
+static void
+qtree_reduce(unsigned char a[], int n, int nx, int ny, unsigned char b[])
+{
+int i, j, k;
+int s10, s00;
+
+	k = 0;							/* k is index of b[i/2,j/2]	*/
+	for (i = 0; i<nx-1; i += 2) {
+		s00 = n*i;					/* s00 is index of a[i,j]	*/
+		s10 = s00+n;				/* s10 is index of a[i+1,j]	*/
+		for (j = 0; j<ny-1; j += 2) {
+			b[k] =	(a[s10+1] != 0)
+				| ( (a[s10  ] != 0) << 1)
+				| ( (a[s00+1] != 0) << 2)
+				| ( (a[s00  ] != 0) << 3);
+			k += 1;
+			s00 += 2;
+			s10 += 2;
+		}
+		if (j < ny) {
+			/*
+			 * row size is odd, do last element in row
+			 * s00+1,s10+1 are off edge
+			 */
+			b[k] =  ( (a[s10  ] != 0) << 1)
+				  | ( (a[s00  ] != 0) << 3);
+			k += 1;
+		}
+	}
+	if (i < nx) {
+		/*
+		 * column size is odd, do last row
+		 * s10,s10+1 are off edge
+		 */
+		s00 = n*i;
+		for (j = 0; j<ny-1; j += 2) {
+			b[k] =  ( (a[s00+1] != 0) << 2)
+				  | ( (a[s00  ] != 0) << 3);
+			k += 1;
+			s00 += 2;
+		}
+		if (j < ny) {
+			/*
+			 * both row and column size are odd, do corner element
+			 * s00+1, s10, s10+1 are off edge
+			 */
+			b[k] = ( (a[s00  ] != 0) << 3);
+			k += 1;
+		}
+	}
+}
+
+/* ######################################################################### */
+static void
+write_bdirect(char *outfile, int a[], int n,int nqx, int nqy, unsigned char scratch[], int bit)
+{
+int i;
+
+	/*
+	 * Write the direct bitmap warning code
+	 */
+	output_nybble(outfile,0x0);
+	/*
+	 * Copy A to scratch array (again!), packing 4 bits/nybble
+	 */
+	qtree_onebit(a,n,nqx,nqy,scratch,bit);
+	/*
+	 * write to outfile
+	 */
+	for (i = 0; i < ((nqx+1)/2) * ((nqy+1)/2); i++) {
+		output_nybble(outfile,scratch[i]);
+	}
+}
+/* ######################################################################### */
+static void
+write_bdirect64(char *outfile, LONGLONG a[], int n,int nqx, int nqy, unsigned char scratch[], int bit)
+{
+int i;
+
+	/*
+	 * Write the direct bitmap warning code
+	 */
+	output_nybble(outfile,0x0);
+	/*
+	 * Copy A to scratch array (again!), packing 4 bits/nybble
+	 */
+	qtree_onebit64(a,n,nqx,nqy,scratch,bit);
+	/*
+	 * write to outfile
+	 */
+	for (i = 0; i < ((nqx+1)/2) * ((nqy+1)/2); i++) {
+		output_nybble(outfile,scratch[i]);
+	}
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/fits_hdecompress.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/fits_hdecompress.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/fits_hdecompress.c	(revision 22322)
@@ -0,0 +1,1974 @@
+/*  #########################################################################
+These routines to apply the H-compress decompression algorithm to a 2-D Fits
+image were written by R. White at the STScI and were obtained from the STScI at
+http://www.stsci.edu/software/hcompress.html
+
+This source file is a concatination of the following sources files in the
+original distribution 
+  hinv.c 
+  hsmooth.c 
+  undigitize.c 
+  decode.c 
+  dodecode.c 
+  qtree_decode.c 
+  qread.c 
+  bit_input.c
+
+
+The following modifications have been made to the original code:
+
+  - commented out redundant "include" statements
+  - added the nextchar global variable 
+  - changed all the 'extern' declarations to 'static', since all the routines are in
+    the same source file
+  - changed the first parameter in decode (and in lower level routines from a file stream
+    to a char array
+  - modified the myread routine, and lower level byte reading routines,  to copy 
+    the input bytes to a char array, instead of reading them from a file stream
+  - changed the function declarations to the more modern ANSI C style
+  - changed calls to printf and perror to call the CFITSIO ffpmsg routine
+  - replace "exit" statements with "return" statements
+
+ ############################################################################  */
+ 
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+// EAM : dropping fitsio prototypes
+# define LONGLONG long long
+# define DATA_COMPRESSION_ERR 413  /* error in imcompress routines */
+# define DATA_DECOMPRESSION_ERR 414 /* error in imcompress routines */
+
+/* WDP added test to see if min and max are already defined */
+#ifndef min
+#define min(a,b)        (((a)<(b))?(a):(b))
+#endif
+#ifndef max
+#define max(a,b)        (((a)>(b))?(a):(b))
+#endif
+
+#define input_nybble(infile)	input_nbits(infile,4)
+
+static long nextchar;
+
+static int decode(unsigned char *infile, int *a, int *nx, int *ny, int *scale);
+static int decode64(unsigned char *infile, LONGLONG *a, int *nx, int *ny, int *scale);
+static int hinv(int a[], int nx, int ny, int smooth ,int scale);
+static int hinv64(LONGLONG a[], int nx, int ny, int smooth ,int scale);
+static void undigitize(int a[], int nx, int ny, int scale);
+static void undigitize64(LONGLONG a[], int nx, int ny, int scale);
+static void unshuffle(int a[], int n, int n2, int tmp[]);
+static void unshuffle64(LONGLONG a[], int n, int n2, LONGLONG tmp[]);
+static void hsmooth(int a[], int nxtop, int nytop, int ny, int scale);
+static void hsmooth64(LONGLONG a[], int nxtop, int nytop, int ny, int scale);
+static void qread(unsigned char *infile,char *a, int n);
+static int  readint(unsigned char *infile);
+static LONGLONG readlonglong(unsigned char *infile);
+static int dodecode(unsigned char *infile, int a[], int nx, int ny, unsigned char nbitplanes[3]);
+static int dodecode64(unsigned char *infile, LONGLONG a[], int nx, int ny, unsigned char nbitplanes[3]);
+static int qtree_decode(unsigned char *infile, int a[], int n, int nqx, int nqy, int nbitplanes);
+static int qtree_decode64(unsigned char *infile, LONGLONG a[], int n, int nqx, int nqy, int nbitplanes);
+static void start_inputing_bits();
+static int input_bit(unsigned char *infile);
+static int input_nbits(unsigned char *infile, int n);
+
+static void qtree_expand(unsigned char *infile, unsigned char a[], int nx, int ny, unsigned char b[]);
+static void qtree_bitins(unsigned char a[], int nx, int ny, int b[], int n, int bit);
+static void qtree_bitins64(unsigned char a[], int nx, int ny, LONGLONG b[], int n, int bit);
+static void qtree_copy(unsigned char a[], int nx, int ny, unsigned char b[], int n);
+static void read_bdirect(unsigned char *infile, int a[], int n, int nqx, int nqy, unsigned char scratch[], int bit);
+static void read_bdirect64(unsigned char *infile, LONGLONG a[], int n, int nqx, int nqy, unsigned char scratch[], int bit);
+static int  input_huffman(unsigned char *infile);
+static int  myread(unsigned char *file, char buffer[], int n);
+
+/* ---------------------------------------------------------------------- */
+int fits_hdecompress(unsigned char *input, int smooth, int *a, int *ny, int *nx, 
+                     int *scale, int *status)
+{
+  /* 
+     decompress the input byte stream using the H-compress algorithm
+  
+   input  - input array of compressed bytes
+   a - pre-allocated array to hold the output uncompressed image
+   nx - returned X axis size
+   ny - returned Y axis size
+
+ NOTE: the nx and ny dimensions as defined within this code are reversed from
+ the usual FITS notation.  ny is the fastest varying dimension, which is
+ usually considered the X axis in the FITS image display
+
+  */
+int stat;
+
+  if (*status > 0) return(*status);
+
+	/* decode the input array */
+
+	stat = decode(input, a, nx, ny, scale);
+        *status = stat;
+	if (stat) return(*status);
+	
+	/*
+	 * Un-Digitize
+	 */
+	undigitize(a, *nx, *ny, *scale);
+
+	/*
+	 * Inverse H-transform
+	 */
+	stat = hinv(a, *nx, *ny, smooth, *scale);
+        *status = stat;
+	
+  return(*status);
+}
+/* ---------------------------------------------------------------------- */
+int fits_hdecompress64(unsigned char *input, int smooth, LONGLONG *a, int *ny, int *nx, 
+                     int *scale, int *status)
+{
+  /* 
+     decompress the input byte stream using the H-compress algorithm
+  
+   input  - input array of compressed bytes
+   a - pre-allocated array to hold the output uncompressed image
+   nx - returned X axis size
+   ny - returned Y axis size
+
+ NOTE: the nx and ny dimensions as defined within this code are reversed from
+ the usual FITS notation.  ny is the fastest varying dimension, which is
+ usually considered the X axis in the FITS image display
+
+  */
+  int stat, *iarray, ii, nval;
+
+  if (*status > 0) return(*status);
+
+	/* decode the input array */
+
+	stat = decode64(input, a, nx, ny, scale);
+        *status = stat;
+	if (stat) return(*status);
+	
+	/*
+	 * Un-Digitize
+	 */
+	undigitize64(a, *nx, *ny, *scale);
+
+	/*
+	 * Inverse H-transform
+	 */
+	stat = hinv64(a, *nx, *ny, smooth, *scale);
+
+        *status = stat;
+	
+         /* pack the I*8 values back into an I*4 array */
+        iarray = (int *) a;
+	nval = (*nx) * (*ny);
+
+	for (ii = 0; ii < nval; ii++)
+	   iarray[ii] = (int) a[ii];	
+
+  return(*status);
+}
+
+/*  ############################################################################  */
+/*  ############################################################################  */
+
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* hinv.c   Inverse H-transform of NX x NY integer image
+ *
+ * Programmer: R. White		Date: 23 July 1993
+ */
+
+/*  ############################################################################  */
+static int 
+hinv(int a[], int nx, int ny, int smooth ,int scale)
+/*
+int smooth;    0 for no smoothing, else smooth during inversion 
+int scale;     used if smoothing is specified 
+*/
+{
+int nmax, log2n, i, j, k;
+int nxtop,nytop,nxf,nyf,c;
+int oddx,oddy;
+int shift, bit0, bit1, bit2, mask0, mask1, mask2,
+	prnd0, prnd1, prnd2, nrnd0, nrnd1, nrnd2, lowbit0, lowbit1;
+int h0, hx, hy, hc;
+int s10, s00;
+int *tmp;
+
+	/*
+	 * log2n is log2 of max(nx,ny) rounded up to next power of 2
+	 */
+	nmax = (nx>ny) ? nx : ny;
+	log2n = (int) (log((float) nmax)/log(2.0)+0.5);
+	if ( nmax > (1<<log2n) ) {
+		log2n += 1;
+	}
+	/*
+	 * get temporary storage for shuffling elements
+	 */  
+	tmp = (int *) malloc(((nmax+1)/2)*sizeof(int));
+	if (tmp == (int *) NULL) {
+	  fprintf (stderr, "hinv: insufficient memory");
+		return(DATA_DECOMPRESSION_ERR);
+	}
+	/*
+	 * set up masks, rounding parameters
+	 */
+	shift  = 1;
+	bit0   = 1 << (log2n - 1);
+	bit1   = bit0 << 1;
+	bit2   = bit0 << 2;
+	mask0  = -bit0;
+	mask1  = mask0 << 1;
+	mask2  = mask0 << 2;
+	prnd0  = bit0 >> 1;
+	prnd1  = bit1 >> 1;
+	prnd2  = bit2 >> 1;
+	nrnd0  = prnd0 - 1;
+	nrnd1  = prnd1 - 1;
+	nrnd2  = prnd2 - 1;
+	/*
+	 * round h0 to multiple of bit2
+	 */
+	a[0] = (a[0] + ((a[0] >= 0) ? prnd2 : nrnd2)) & mask2;
+	/*
+	 * do log2n expansions
+	 *
+	 * We're indexing a as a 2-D array with dimensions (nx,ny).
+	 */
+	nxtop = 1;
+	nytop = 1;
+	nxf = nx;
+	nyf = ny;
+	c = 1<<log2n;
+	for (k = log2n-1; k>=0; k--) {
+		/*
+		 * this somewhat cryptic code generates the sequence
+		 * ntop[k-1] = (ntop[k]+1)/2, where ntop[log2n] = n
+		 */
+		c = c>>1;
+		nxtop = nxtop<<1;
+		nytop = nytop<<1;
+		if (nxf <= c) { nxtop -= 1; } else { nxf -= c; }
+		if (nyf <= c) { nytop -= 1; } else { nyf -= c; }
+		/*
+		 * double shift and fix nrnd0 (because prnd0=0) on last pass
+		 */
+		if (k == 0) {
+			nrnd0 = 0;
+			shift = 2;
+		}
+		/*
+		 * unshuffle in each dimension to interleave coefficients
+		 */
+		for (i = 0; i<nxtop; i++) {
+			unshuffle(&a[ny*i],nytop,1,tmp);
+		}
+		for (j = 0; j<nytop; j++) {
+			unshuffle(&a[j],nxtop,ny,tmp);
+		}
+		/*
+		 * smooth by interpolating coefficients if SMOOTH != 0
+		 */
+		if (smooth) hsmooth(a,nxtop,nytop,ny,scale);
+		oddx = nxtop % 2;
+		oddy = nytop % 2;
+		for (i = 0; i<nxtop-oddx; i += 2) {
+			s00 = ny*i;				/* s00 is index of a[i,j]	*/
+			s10 = s00+ny;			/* s10 is index of a[i+1,j]	*/
+			for (j = 0; j<nytop-oddy; j += 2) {
+				h0 = a[s00  ];
+				hx = a[s10  ];
+				hy = a[s00+1];
+				hc = a[s10+1];
+				/*
+				 * round hx and hy to multiple of bit1, hc to multiple of bit0
+				 * h0 is already a multiple of bit2
+				 */
+				hx = (hx + ((hx >= 0) ? prnd1 : nrnd1)) & mask1;
+				hy = (hy + ((hy >= 0) ? prnd1 : nrnd1)) & mask1;
+				hc = (hc + ((hc >= 0) ? prnd0 : nrnd0)) & mask0;
+				/*
+				 * propagate bit0 of hc to hx,hy
+				 */
+				lowbit0 = hc & bit0;
+				hx = (hx >= 0) ? (hx - lowbit0) : (hx + lowbit0);
+				hy = (hy >= 0) ? (hy - lowbit0) : (hy + lowbit0);
+				/*
+				 * Propagate bits 0 and 1 of hc,hx,hy to h0.
+				 * This could be simplified if we assume h0>0, but then
+				 * the inversion would not be lossless for images with
+				 * negative pixels.
+				 */
+				lowbit1 = (hc ^ hx ^ hy) & bit1;
+				h0 = (h0 >= 0)
+					? (h0 + lowbit0 - lowbit1)
+					: (h0 + ((lowbit0 == 0) ? lowbit1 : (lowbit0-lowbit1)));
+				/*
+				 * Divide sums by 2 (4 last time)
+				 */
+				a[s10+1] = (h0 + hx + hy + hc) >> shift;
+				a[s10  ] = (h0 + hx - hy - hc) >> shift;
+				a[s00+1] = (h0 - hx + hy - hc) >> shift;
+				a[s00  ] = (h0 - hx - hy + hc) >> shift;
+				s00 += 2;
+				s10 += 2;
+			}
+			if (oddy) {
+				/*
+				 * do last element in row if row length is odd
+				 * s00+1, s10+1 are off edge
+				 */
+				h0 = a[s00  ];
+				hx = a[s10  ];
+				hx = ((hx >= 0) ? (hx+prnd1) : (hx+nrnd1)) & mask1;
+				lowbit1 = hx & bit1;
+				h0 = (h0 >= 0) ? (h0 - lowbit1) : (h0 + lowbit1);
+				a[s10  ] = (h0 + hx) >> shift;
+				a[s00  ] = (h0 - hx) >> shift;
+			}
+		}
+		if (oddx) {
+			/*
+			 * do last row if column length is odd
+			 * s10, s10+1 are off edge
+			 */
+			s00 = ny*i;
+			for (j = 0; j<nytop-oddy; j += 2) {
+				h0 = a[s00  ];
+				hy = a[s00+1];
+				hy = ((hy >= 0) ? (hy+prnd1) : (hy+nrnd1)) & mask1;
+				lowbit1 = hy & bit1;
+				h0 = (h0 >= 0) ? (h0 - lowbit1) : (h0 + lowbit1);
+				a[s00+1] = (h0 + hy) >> shift;
+				a[s00  ] = (h0 - hy) >> shift;
+				s00 += 2;
+			}
+			if (oddy) {
+				/*
+				 * do corner element if both row and column lengths are odd
+				 * s00+1, s10, s10+1 are off edge
+				 */
+				h0 = a[s00  ];
+				a[s00  ] = h0 >> shift;
+			}
+		}
+		/*
+		 * divide all the masks and rounding values by 2
+		 */
+		bit2 = bit1;
+		bit1 = bit0;
+		bit0 = bit0 >> 1;
+		mask1 = mask0;
+		mask0 = mask0 >> 1;
+		prnd1 = prnd0;
+		prnd0 = prnd0 >> 1;
+		nrnd1 = nrnd0;
+		nrnd0 = prnd0 - 1;
+	}
+	free(tmp);
+	return(0);
+}
+/*  ############################################################################  */
+static int 
+hinv64(LONGLONG a[], int nx, int ny, int smooth ,int scale)
+/*
+int smooth;    0 for no smoothing, else smooth during inversion 
+int scale;     used if smoothing is specified 
+*/
+{
+int nmax, log2n, i, j, k;
+int nxtop,nytop,nxf,nyf,c;
+int oddx,oddy;
+int shift;
+LONGLONG mask0, mask1, mask2, prnd0, prnd1, prnd2, bit0, bit1, bit2;
+LONGLONG  nrnd0, nrnd1, nrnd2, lowbit0, lowbit1;
+LONGLONG h0, hx, hy, hc;
+int s10, s00;
+LONGLONG *tmp;
+
+	/*
+	 * log2n is log2 of max(nx,ny) rounded up to next power of 2
+	 */
+	nmax = (nx>ny) ? nx : ny;
+	log2n = (int) (log((float) nmax)/log(2.0)+0.5);
+	if ( nmax > (1<<log2n) ) {
+		log2n += 1;
+	}
+	/*
+	 * get temporary storage for shuffling elements
+	 */  
+	tmp = (LONGLONG *) malloc(((nmax+1)/2)*sizeof(LONGLONG));
+	if (tmp == (LONGLONG *) NULL) {
+		fprintf (stderr, "hinv64: insufficient memory");
+		return(DATA_DECOMPRESSION_ERR);
+	}
+	/*
+	 * set up masks, rounding parameters
+	 */
+	shift  = 1;
+	bit0   = ((LONGLONG) 1) << (log2n - 1);
+	bit1   = bit0 << 1;
+	bit2   = bit0 << 2;
+	mask0  = -bit0;
+	mask1  = mask0 << 1;
+	mask2  = mask0 << 2;
+	prnd0  = bit0 >> 1;
+	prnd1  = bit1 >> 1;
+	prnd2  = bit2 >> 1;
+	nrnd0  = prnd0 - 1;
+	nrnd1  = prnd1 - 1;
+	nrnd2  = prnd2 - 1;
+	/*
+	 * round h0 to multiple of bit2
+	 */
+	a[0] = (a[0] + ((a[0] >= 0) ? prnd2 : nrnd2)) & mask2;
+	/*
+	 * do log2n expansions
+	 *
+	 * We're indexing a as a 2-D array with dimensions (nx,ny).
+	 */
+	nxtop = 1;
+	nytop = 1;
+	nxf = nx;
+	nyf = ny;
+	c = 1<<log2n;
+	for (k = log2n-1; k>=0; k--) {
+		/*
+		 * this somewhat cryptic code generates the sequence
+		 * ntop[k-1] = (ntop[k]+1)/2, where ntop[log2n] = n
+		 */
+		c = c>>1;
+		nxtop = nxtop<<1;
+		nytop = nytop<<1;
+		if (nxf <= c) { nxtop -= 1; } else { nxf -= c; }
+		if (nyf <= c) { nytop -= 1; } else { nyf -= c; }
+		/*
+		 * double shift and fix nrnd0 (because prnd0=0) on last pass
+		 */
+		if (k == 0) {
+			nrnd0 = 0;
+			shift = 2;
+		}
+		/*
+		 * unshuffle in each dimension to interleave coefficients
+		 */
+		for (i = 0; i<nxtop; i++) {
+			unshuffle64(&a[ny*i],nytop,1,tmp);
+		}
+		for (j = 0; j<nytop; j++) {
+			unshuffle64(&a[j],nxtop,ny,tmp);
+		}
+		/*
+		 * smooth by interpolating coefficients if SMOOTH != 0
+		 */
+		if (smooth) hsmooth64(a,nxtop,nytop,ny,scale);
+		oddx = nxtop % 2;
+		oddy = nytop % 2;
+		for (i = 0; i<nxtop-oddx; i += 2) {
+			s00 = ny*i;				/* s00 is index of a[i,j]	*/
+			s10 = s00+ny;			/* s10 is index of a[i+1,j]	*/
+			for (j = 0; j<nytop-oddy; j += 2) {
+				h0 = a[s00  ];
+				hx = a[s10  ];
+				hy = a[s00+1];
+				hc = a[s10+1];
+				/*
+				 * round hx and hy to multiple of bit1, hc to multiple of bit0
+				 * h0 is already a multiple of bit2
+				 */
+				hx = (hx + ((hx >= 0) ? prnd1 : nrnd1)) & mask1;
+				hy = (hy + ((hy >= 0) ? prnd1 : nrnd1)) & mask1;
+				hc = (hc + ((hc >= 0) ? prnd0 : nrnd0)) & mask0;
+				/*
+				 * propagate bit0 of hc to hx,hy
+				 */
+				lowbit0 = hc & bit0;
+				hx = (hx >= 0) ? (hx - lowbit0) : (hx + lowbit0);
+				hy = (hy >= 0) ? (hy - lowbit0) : (hy + lowbit0);
+				/*
+				 * Propagate bits 0 and 1 of hc,hx,hy to h0.
+				 * This could be simplified if we assume h0>0, but then
+				 * the inversion would not be lossless for images with
+				 * negative pixels.
+				 */
+				lowbit1 = (hc ^ hx ^ hy) & bit1;
+				h0 = (h0 >= 0)
+					? (h0 + lowbit0 - lowbit1)
+					: (h0 + ((lowbit0 == 0) ? lowbit1 : (lowbit0-lowbit1)));
+				/*
+				 * Divide sums by 2 (4 last time)
+				 */
+				a[s10+1] = (h0 + hx + hy + hc) >> shift;
+				a[s10  ] = (h0 + hx - hy - hc) >> shift;
+				a[s00+1] = (h0 - hx + hy - hc) >> shift;
+				a[s00  ] = (h0 - hx - hy + hc) >> shift;
+				s00 += 2;
+				s10 += 2;
+			}
+			if (oddy) {
+				/*
+				 * do last element in row if row length is odd
+				 * s00+1, s10+1 are off edge
+				 */
+				h0 = a[s00  ];
+				hx = a[s10  ];
+				hx = ((hx >= 0) ? (hx+prnd1) : (hx+nrnd1)) & mask1;
+				lowbit1 = hx & bit1;
+				h0 = (h0 >= 0) ? (h0 - lowbit1) : (h0 + lowbit1);
+				a[s10  ] = (h0 + hx) >> shift;
+				a[s00  ] = (h0 - hx) >> shift;
+			}
+		}
+		if (oddx) {
+			/*
+			 * do last row if column length is odd
+			 * s10, s10+1 are off edge
+			 */
+			s00 = ny*i;
+			for (j = 0; j<nytop-oddy; j += 2) {
+				h0 = a[s00  ];
+				hy = a[s00+1];
+				hy = ((hy >= 0) ? (hy+prnd1) : (hy+nrnd1)) & mask1;
+				lowbit1 = hy & bit1;
+				h0 = (h0 >= 0) ? (h0 - lowbit1) : (h0 + lowbit1);
+				a[s00+1] = (h0 + hy) >> shift;
+				a[s00  ] = (h0 - hy) >> shift;
+				s00 += 2;
+			}
+			if (oddy) {
+				/*
+				 * do corner element if both row and column lengths are odd
+				 * s00+1, s10, s10+1 are off edge
+				 */
+				h0 = a[s00  ];
+				a[s00  ] = h0 >> shift;
+			}
+		}
+		/*
+		 * divide all the masks and rounding values by 2
+		 */
+		bit2 = bit1;
+		bit1 = bit0;
+		bit0 = bit0 >> 1;
+		mask1 = mask0;
+		mask0 = mask0 >> 1;
+		prnd1 = prnd0;
+		prnd0 = prnd0 >> 1;
+		nrnd1 = nrnd0;
+		nrnd0 = prnd0 - 1;
+	}
+	free(tmp);
+	return(0);
+}
+
+/*  ############################################################################  */
+static void
+unshuffle(int a[], int n, int n2, int tmp[])
+/*
+int a[];	 array to shuffle					
+int n;		 number of elements to shuffle	
+int n2;		 second dimension					
+int tmp[];	 scratch storage					
+*/
+{
+int i;
+int nhalf;
+int *p1, *p2, *pt;
+ 
+	/*
+	 * copy 2nd half of array to tmp
+	 */
+	nhalf = (n+1)>>1;
+	pt = tmp;
+	p1 = &a[n2*nhalf];				/* pointer to a[i]			*/
+	for (i=nhalf; i<n; i++) {
+		*pt = *p1;
+		p1 += n2;
+		pt += 1;
+	}
+	/*
+	 * distribute 1st half of array to even elements
+	 */
+	p2 = &a[ n2*(nhalf-1) ];		/* pointer to a[i]			*/
+	p1 = &a[(n2*(nhalf-1))<<1];		/* pointer to a[2*i]		*/
+	for (i=nhalf-1; i >= 0; i--) {
+		*p1 = *p2;
+		p2 -= n2;
+		p1 -= (n2+n2);
+	}
+	/*
+	 * now distribute 2nd half of array (in tmp) to odd elements
+	 */
+	pt = tmp;
+	p1 = &a[n2];					/* pointer to a[i]			*/
+	for (i=1; i<n; i += 2) {
+		*p1 = *pt;
+		p1 += (n2+n2);
+		pt += 1;
+	}
+}
+/*  ############################################################################  */
+static void
+unshuffle64(LONGLONG a[], int n, int n2, LONGLONG tmp[])
+/*
+LONGLONG a[];	 array to shuffle					
+int n;		 number of elements to shuffle	
+int n2;		 second dimension					
+LONGLONG tmp[];	 scratch storage					
+*/
+{
+int i;
+int nhalf;
+LONGLONG *p1, *p2, *pt;
+ 
+	/*
+	 * copy 2nd half of array to tmp
+	 */
+	nhalf = (n+1)>>1;
+	pt = tmp;
+	p1 = &a[n2*nhalf];				/* pointer to a[i]			*/
+	for (i=nhalf; i<n; i++) {
+		*pt = *p1;
+		p1 += n2;
+		pt += 1;
+	}
+	/*
+	 * distribute 1st half of array to even elements
+	 */
+	p2 = &a[ n2*(nhalf-1) ];		/* pointer to a[i]			*/
+	p1 = &a[(n2*(nhalf-1))<<1];		/* pointer to a[2*i]		*/
+	for (i=nhalf-1; i >= 0; i--) {
+		*p1 = *p2;
+		p2 -= n2;
+		p1 -= (n2+n2);
+	}
+	/*
+	 * now distribute 2nd half of array (in tmp) to odd elements
+	 */
+	pt = tmp;
+	p1 = &a[n2];					/* pointer to a[i]			*/
+	for (i=1; i<n; i += 2) {
+		*p1 = *pt;
+		p1 += (n2+n2);
+		pt += 1;
+	}
+}
+
+/*  ############################################################################  */
+/*  ############################################################################  */
+
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* hsmooth.c	Smooth H-transform image by adjusting coefficients toward
+ *				interpolated values
+ *
+ * Programmer: R. White		Date: 13 April 1992
+ */
+
+/*  ############################################################################  */
+static void 
+hsmooth(int a[], int nxtop, int nytop, int ny, int scale)
+/*
+int a[];			 array of H-transform coefficients		
+int nxtop,nytop;	 size of coefficient block to use			
+int ny;				 actual 1st dimension of array			
+int scale;			 truncation scale factor that was used	
+*/
+{
+int i, j;
+int ny2, s10, s00, diff, dmax, dmin, s, smax;
+int hm, h0, hp, hmm, hpm, hmp, hpp, hx2, hy2;
+int m1,m2;
+
+	/*
+	 * Maximum change in coefficients is determined by scale factor.
+	 * Since we rounded during division (see digitize.c), the biggest
+	 * permitted change is scale/2.
+	 */
+	smax = (scale >> 1);
+	if (smax <= 0) return;
+	ny2 = ny << 1;
+	/*
+	 * We're indexing a as a 2-D array with dimensions (nxtop,ny) of which
+	 * only (nxtop,nytop) are used.  The coefficients on the edge of the
+	 * array are not adjusted (which is why the loops below start at 2
+	 * instead of 0 and end at nxtop-2 instead of nxtop.)
+	 */
+	/*
+	 * Adjust x difference hx
+	 */
+	for (i = 2; i<nxtop-2; i += 2) {
+		s00 = ny*i;				/* s00 is index of a[i,j]	*/
+		s10 = s00+ny;			/* s10 is index of a[i+1,j]	*/
+		for (j = 0; j<nytop; j += 2) {
+			/*
+			 * hp is h0 (mean value) in next x zone, hm is h0 in previous x zone
+			 */
+			hm = a[s00-ny2];
+			h0 = a[s00];
+			hp = a[s00+ny2];
+			/*
+			 * diff = 8 * hx slope that would match h0 in neighboring zones
+			 */
+			diff = hp-hm;
+			/*
+			 * monotonicity constraints on diff
+			 */
+			dmax = max( min( (hp-h0), (h0-hm) ), 0 ) << 2;
+			dmin = min( max( (hp-h0), (h0-hm) ), 0 ) << 2;
+			/*
+			 * if monotonicity would set slope = 0 then don't change hx.
+			 * note dmax>=0, dmin<=0.
+			 */
+			if (dmin < dmax) {
+				diff = max( min(diff, dmax), dmin);
+				/*
+				 * Compute change in slope limited to range +/- smax.
+				 * Careful with rounding negative numbers when using
+				 * shift for divide by 8.
+				 */
+				s = diff-(a[s10]<<3);
+				s = (s>=0) ? (s>>3) : ((s+7)>>3) ;
+				s = max( min(s, smax), -smax);
+				a[s10] = a[s10]+s;
+			}
+			s00 += 2;
+			s10 += 2;
+		}
+	}
+	/*
+	 * Adjust y difference hy
+	 */
+	for (i = 0; i<nxtop; i += 2) {
+		s00 = ny*i+2;
+		s10 = s00+ny;
+		for (j = 2; j<nytop-2; j += 2) {
+			hm = a[s00-2];
+			h0 = a[s00];
+			hp = a[s00+2];
+			diff = hp-hm;
+			dmax = max( min( (hp-h0), (h0-hm) ), 0 ) << 2;
+			dmin = min( max( (hp-h0), (h0-hm) ), 0 ) << 2;
+			if (dmin < dmax) {
+				diff = max( min(diff, dmax), dmin);
+				s = diff-(a[s00+1]<<3);
+				s = (s>=0) ? (s>>3) : ((s+7)>>3) ;
+				s = max( min(s, smax), -smax);
+				a[s00+1] = a[s00+1]+s;
+			}
+			s00 += 2;
+			s10 += 2;
+		}
+	}
+	/*
+	 * Adjust curvature difference hc
+	 */
+	for (i = 2; i<nxtop-2; i += 2) {
+		s00 = ny*i+2;
+		s10 = s00+ny;
+		for (j = 2; j<nytop-2; j += 2) {
+			/*
+			 * ------------------    y
+			 * | hmp |    | hpp |    |
+			 * ------------------    |
+			 * |     | h0 |     |    |
+			 * ------------------    -------x
+			 * | hmm |    | hpm |
+			 * ------------------
+			 */
+			hmm = a[s00-ny2-2];
+			hpm = a[s00+ny2-2];
+			hmp = a[s00-ny2+2];
+			hpp = a[s00+ny2+2];
+			h0  = a[s00];
+			/*
+			 * diff = 64 * hc value that would match h0 in neighboring zones
+			 */
+			diff = hpp + hmm - hmp - hpm;
+			/*
+			 * 2 times x,y slopes in this zone
+			 */
+			hx2 = a[s10  ]<<1;
+			hy2 = a[s00+1]<<1;
+			/*
+			 * monotonicity constraints on diff
+			 */
+			m1 = min(max(hpp-h0,0)-hx2-hy2, max(h0-hpm,0)+hx2-hy2);
+			m2 = min(max(h0-hmp,0)-hx2+hy2, max(hmm-h0,0)+hx2+hy2);
+			dmax = min(m1,m2) << 4;
+			m1 = max(min(hpp-h0,0)-hx2-hy2, min(h0-hpm,0)+hx2-hy2);
+			m2 = max(min(h0-hmp,0)-hx2+hy2, min(hmm-h0,0)+hx2+hy2);
+			dmin = max(m1,m2) << 4;
+			/*
+			 * if monotonicity would set slope = 0 then don't change hc.
+			 * note dmax>=0, dmin<=0.
+			 */
+			if (dmin < dmax) {
+				diff = max( min(diff, dmax), dmin);
+				/*
+				 * Compute change in slope limited to range +/- smax.
+				 * Careful with rounding negative numbers when using
+				 * shift for divide by 64.
+				 */
+				s = diff-(a[s10+1]<<6);
+				s = (s>=0) ? (s>>6) : ((s+63)>>6) ;
+				s = max( min(s, smax), -smax);
+				a[s10+1] = a[s10+1]+s;
+			}
+			s00 += 2;
+			s10 += 2;
+		}
+	}
+}
+/*  ############################################################################  */
+static void 
+hsmooth64(LONGLONG a[], int nxtop, int nytop, int ny, int scale)
+/*
+LONGLONG a[];			 array of H-transform coefficients		
+int nxtop,nytop;	 size of coefficient block to use			
+int ny;				 actual 1st dimension of array			
+int scale;			 truncation scale factor that was used	
+*/
+{
+int i, j;
+int ny2, s10, s00;
+LONGLONG hm, h0, hp, hmm, hpm, hmp, hpp, hx2, hy2, diff, dmax, dmin, s, smax, m1, m2;
+
+	/*
+	 * Maximum change in coefficients is determined by scale factor.
+	 * Since we rounded during division (see digitize.c), the biggest
+	 * permitted change is scale/2.
+	 */
+	smax = (scale >> 1);
+	if (smax <= 0) return;
+	ny2 = ny << 1;
+	/*
+	 * We're indexing a as a 2-D array with dimensions (nxtop,ny) of which
+	 * only (nxtop,nytop) are used.  The coefficients on the edge of the
+	 * array are not adjusted (which is why the loops below start at 2
+	 * instead of 0 and end at nxtop-2 instead of nxtop.)
+	 */
+	/*
+	 * Adjust x difference hx
+	 */
+	for (i = 2; i<nxtop-2; i += 2) {
+		s00 = ny*i;				/* s00 is index of a[i,j]	*/
+		s10 = s00+ny;			/* s10 is index of a[i+1,j]	*/
+		for (j = 0; j<nytop; j += 2) {
+			/*
+			 * hp is h0 (mean value) in next x zone, hm is h0 in previous x zone
+			 */
+			hm = a[s00-ny2];
+			h0 = a[s00];
+			hp = a[s00+ny2];
+			/*
+			 * diff = 8 * hx slope that would match h0 in neighboring zones
+			 */
+			diff = hp-hm;
+			/*
+			 * monotonicity constraints on diff
+			 */
+			dmax = max( min( (hp-h0), (h0-hm) ), 0 ) << 2;
+			dmin = min( max( (hp-h0), (h0-hm) ), 0 ) << 2;
+			/*
+			 * if monotonicity would set slope = 0 then don't change hx.
+			 * note dmax>=0, dmin<=0.
+			 */
+			if (dmin < dmax) {
+				diff = max( min(diff, dmax), dmin);
+				/*
+				 * Compute change in slope limited to range +/- smax.
+				 * Careful with rounding negative numbers when using
+				 * shift for divide by 8.
+				 */
+				s = diff-(a[s10]<<3);
+				s = (s>=0) ? (s>>3) : ((s+7)>>3) ;
+				s = max( min(s, smax), -smax);
+				a[s10] = a[s10]+s;
+			}
+			s00 += 2;
+			s10 += 2;
+		}
+	}
+	/*
+	 * Adjust y difference hy
+	 */
+	for (i = 0; i<nxtop; i += 2) {
+		s00 = ny*i+2;
+		s10 = s00+ny;
+		for (j = 2; j<nytop-2; j += 2) {
+			hm = a[s00-2];
+			h0 = a[s00];
+			hp = a[s00+2];
+			diff = hp-hm;
+			dmax = max( min( (hp-h0), (h0-hm) ), 0 ) << 2;
+			dmin = min( max( (hp-h0), (h0-hm) ), 0 ) << 2;
+			if (dmin < dmax) {
+				diff = max( min(diff, dmax), dmin);
+				s = diff-(a[s00+1]<<3);
+				s = (s>=0) ? (s>>3) : ((s+7)>>3) ;
+				s = max( min(s, smax), -smax);
+				a[s00+1] = a[s00+1]+s;
+			}
+			s00 += 2;
+			s10 += 2;
+		}
+	}
+	/*
+	 * Adjust curvature difference hc
+	 */
+	for (i = 2; i<nxtop-2; i += 2) {
+		s00 = ny*i+2;
+		s10 = s00+ny;
+		for (j = 2; j<nytop-2; j += 2) {
+			/*
+			 * ------------------    y
+			 * | hmp |    | hpp |    |
+			 * ------------------    |
+			 * |     | h0 |     |    |
+			 * ------------------    -------x
+			 * | hmm |    | hpm |
+			 * ------------------
+			 */
+			hmm = a[s00-ny2-2];
+			hpm = a[s00+ny2-2];
+			hmp = a[s00-ny2+2];
+			hpp = a[s00+ny2+2];
+			h0  = a[s00];
+			/*
+			 * diff = 64 * hc value that would match h0 in neighboring zones
+			 */
+			diff = hpp + hmm - hmp - hpm;
+			/*
+			 * 2 times x,y slopes in this zone
+			 */
+			hx2 = a[s10  ]<<1;
+			hy2 = a[s00+1]<<1;
+			/*
+			 * monotonicity constraints on diff
+			 */
+			m1 = min(max(hpp-h0,0)-hx2-hy2, max(h0-hpm,0)+hx2-hy2);
+			m2 = min(max(h0-hmp,0)-hx2+hy2, max(hmm-h0,0)+hx2+hy2);
+			dmax = min(m1,m2) << 4;
+			m1 = max(min(hpp-h0,0)-hx2-hy2, min(h0-hpm,0)+hx2-hy2);
+			m2 = max(min(h0-hmp,0)-hx2+hy2, min(hmm-h0,0)+hx2+hy2);
+			dmin = max(m1,m2) << 4;
+			/*
+			 * if monotonicity would set slope = 0 then don't change hc.
+			 * note dmax>=0, dmin<=0.
+			 */
+			if (dmin < dmax) {
+				diff = max( min(diff, dmax), dmin);
+				/*
+				 * Compute change in slope limited to range +/- smax.
+				 * Careful with rounding negative numbers when using
+				 * shift for divide by 64.
+				 */
+				s = diff-(a[s10+1]<<6);
+				s = (s>=0) ? (s>>6) : ((s+63)>>6) ;
+				s = max( min(s, smax), -smax);
+				a[s10+1] = a[s10+1]+s;
+			}
+			s00 += 2;
+			s10 += 2;
+		}
+	}
+}
+
+
+/*  ############################################################################  */
+/*  ############################################################################  */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* undigitize.c		undigitize H-transform
+ *
+ * Programmer: R. White		Date: 9 May 1991
+ */
+
+/*  ############################################################################  */
+static void
+undigitize(int a[], int nx, int ny, int scale)
+{
+int *p;
+
+	/*
+	 * multiply by scale
+	 */
+	if (scale <= 1) return;
+	for (p=a; p <= &a[nx*ny-1]; p++) *p = (*p)*scale;
+}
+/*  ############################################################################  */
+static void
+undigitize64(LONGLONG a[], int nx, int ny, int scale)
+{
+LONGLONG *p, scale64;
+
+	/*
+	 * multiply by scale
+	 */
+	if (scale <= 1) return;
+	scale64 = (LONGLONG) scale;   /* use a 64-bit int for efficiency in the big loop */
+	
+	for (p=a; p <= &a[nx*ny-1]; p++) *p = (*p)*scale64;
+}
+
+/*  ############################################################################  */
+/*  ############################################################################  */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* decode.c		read codes from infile and construct array
+ *
+ * Programmer: R. White		Date: 2 February 1994
+ */
+
+
+static char code_magic[2] = { (char)0xDD, (char)0x99 };
+
+/*  ############################################################################  */
+static int decode(unsigned char *infile, int *a, int *nx, int *ny, int *scale)
+/*
+char *infile;				 input file							
+int  *a;				 address of output array [nx][ny]		
+int  *nx,*ny;				 size of output array					
+int  *scale;				 scale factor for digitization		
+*/
+{
+LONGLONG sumall;
+int nel, stat;
+unsigned char nbitplanes[3];
+char tmagic[2];
+
+	/* initialize the byte read position to the beginning of the array */;
+	nextchar = 0;
+	
+	/*
+	 * File starts either with special 2-byte magic code or with
+	 * FITS keyword "SIMPLE  ="
+	 */
+	qread(infile, tmagic, sizeof(tmagic));
+	/*
+	 * check for correct magic code value
+	 */
+	if (memcmp(tmagic,code_magic,sizeof(code_magic)) != 0) {
+		fprintf (stderr, "bad file format");
+		return(DATA_DECOMPRESSION_ERR);
+	}
+	*nx =readint(infile);				/* x size of image			*/
+	*ny =readint(infile);				/* y size of image			*/
+	*scale=readint(infile);				/* scale factor for digitization	*/
+	
+	nel = (*nx) * (*ny);
+
+	/* sum of all pixels	*/
+	sumall=readlonglong(infile);
+	/* # bits in quadrants	*/
+
+	qread(infile, (char *) nbitplanes, sizeof(nbitplanes));
+
+	stat = dodecode(infile, a, *nx, *ny, nbitplanes);
+	/*
+	 * put sum of all pixels back into pixel 0
+	 */
+	a[0] = (int) sumall;
+	return(stat);
+}
+/*  ############################################################################  */
+static int decode64(unsigned char *infile, LONGLONG *a, int *nx, int *ny, int *scale)
+/*
+char *infile;				 input file							
+LONGLONG  *a;				 address of output array [nx][ny]		
+int  *nx,*ny;				 size of output array					
+int  *scale;				 scale factor for digitization		
+*/
+{
+int nel, stat;
+LONGLONG sumall;
+unsigned char nbitplanes[3];
+char tmagic[2];
+
+	/* initialize the byte read position to the beginning of the array */;
+	nextchar = 0;
+	
+	/*
+	 * File starts either with special 2-byte magic code or with
+	 * FITS keyword "SIMPLE  ="
+	 */
+	qread(infile, tmagic, sizeof(tmagic));
+	/*
+	 * check for correct magic code value
+	 */
+	if (memcmp(tmagic,code_magic,sizeof(code_magic)) != 0) {
+		fprintf (stderr, "bad file format");
+		return(DATA_DECOMPRESSION_ERR);
+	}
+	*nx =readint(infile);				/* x size of image			*/
+	*ny =readint(infile);				/* y size of image			*/
+	*scale=readint(infile);				/* scale factor for digitization	*/
+	
+	nel = (*nx) * (*ny);
+
+	/* sum of all pixels	*/
+	sumall=readlonglong(infile);
+	/* # bits in quadrants	*/
+
+	qread(infile, (char *) nbitplanes, sizeof(nbitplanes));
+
+	stat = dodecode64(infile, a, *nx, *ny, nbitplanes);
+	/*
+	 * put sum of all pixels back into pixel 0
+	 */
+	a[0] = sumall;
+
+	return(stat);
+}
+
+
+/*  ############################################################################  */
+/*  ############################################################################  */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* dodecode.c	Decode stream of characters on infile and return array
+ *
+ * This version encodes the different quadrants separately
+ *
+ * Programmer: R. White		Date: 9 May 1991
+ */
+
+/*  ############################################################################  */
+static int
+dodecode(unsigned char *infile, int a[], int nx, int ny, unsigned char nbitplanes[3])
+
+/* int a[];					 			
+   int nx,ny;					 Array dimensions are [nx][ny]		
+   unsigned char nbitplanes[3];		 Number of bit planes in quadrants
+*/
+{
+int i, nel, nx2, ny2, stat;
+
+	nel = nx*ny;
+	nx2 = (nx+1)/2;
+	ny2 = (ny+1)/2;
+
+	/*
+	 * initialize a to zero
+	 */
+	for (i=0; i<nel; i++) a[i] = 0;
+	/*
+	 * Initialize bit input
+	 */
+	start_inputing_bits();
+	/*
+	 * read bit planes for each quadrant
+	 */
+	stat = qtree_decode(infile, &a[0],          ny, nx2,  ny2,  nbitplanes[0]);
+        if (stat) return(stat);
+	
+	stat = qtree_decode(infile, &a[ny2],        ny, nx2,  ny/2, nbitplanes[1]);
+        if (stat) return(stat);
+	
+	stat = qtree_decode(infile, &a[ny*nx2],     ny, nx/2, ny2,  nbitplanes[1]);
+        if (stat) return(stat);
+	
+	stat = qtree_decode(infile, &a[ny*nx2+ny2], ny, nx/2, ny/2, nbitplanes[2]);
+        if (stat) return(stat);
+	
+	/*
+	 * make sure there is an EOF symbol (nybble=0) at end
+	 */
+	if (input_nybble(infile) != 0) {
+		fprintf (stderr, "dodecode: bad bit plane values");
+		return(DATA_DECOMPRESSION_ERR);
+	}
+	/*
+	 * now get the sign bits
+	 * Re-initialize bit input
+	 */
+	start_inputing_bits();
+	for (i=0; i<nel; i++) {
+		if (a[i] != 0) {
+			if (input_bit(infile) != 0) a[i] = -a[i];
+		}
+	}
+	return(0);
+}
+/*  ############################################################################  */
+static int
+dodecode64(unsigned char *infile, LONGLONG a[], int nx, int ny, unsigned char nbitplanes[3])
+
+/* LONGLONG a[];					 			
+   int nx,ny;					 Array dimensions are [nx][ny]		
+   unsigned char nbitplanes[3];		 Number of bit planes in quadrants
+*/
+{
+int i, nel, nx2, ny2, stat;
+
+	nel = nx*ny;
+	nx2 = (nx+1)/2;
+	ny2 = (ny+1)/2;
+
+	/*
+	 * initialize a to zero
+	 */
+	for (i=0; i<nel; i++) a[i] = 0;
+	/*
+	 * Initialize bit input
+	 */
+	start_inputing_bits();
+	/*
+	 * read bit planes for each quadrant
+	 */
+	stat = qtree_decode64(infile, &a[0],          ny, nx2,  ny2,  nbitplanes[0]);
+        if (stat) return(stat);
+	
+	stat = qtree_decode64(infile, &a[ny2],        ny, nx2,  ny/2, nbitplanes[1]);
+        if (stat) return(stat);
+	
+	stat = qtree_decode64(infile, &a[ny*nx2],     ny, nx/2, ny2,  nbitplanes[1]);
+        if (stat) return(stat);
+	
+	stat = qtree_decode64(infile, &a[ny*nx2+ny2], ny, nx/2, ny/2, nbitplanes[2]);
+        if (stat) return(stat);
+	
+	/*
+	 * make sure there is an EOF symbol (nybble=0) at end
+	 */
+	if (input_nybble(infile) != 0) {
+		fprintf (stderr, "dodecode64: bad bit plane values");
+		return(DATA_DECOMPRESSION_ERR);
+	}
+	/*
+	 * now get the sign bits
+	 * Re-initialize bit input
+	 */
+	start_inputing_bits();
+	for (i=0; i<nel; i++) {
+		if (a[i] != 0) {
+			if (input_bit(infile) != 0) a[i] = -a[i];
+		}
+	}
+	return(0);
+}
+
+/*  ############################################################################  */
+/*  ############################################################################  */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* qtree_decode.c	Read stream of codes from infile and construct bit planes
+ *					in quadrant of 2-D array using binary quadtree coding
+ *
+ * Programmer: R. White		Date: 7 May 1991
+ */
+
+/*  ############################################################################  */
+static int
+qtree_decode(unsigned char *infile, int a[], int n, int nqx, int nqy, int nbitplanes)
+
+/*
+char *infile;
+int a[];				 a is 2-D array with dimensions (n,n)	
+int n;					 length of full row in a				
+int nqx;				 partial length of row to decode		
+int nqy;				 partial length of column (<=n)		
+int nbitplanes;				 number of bitplanes to decode		
+*/
+{
+int log2n, k, bit, b, nqmax;
+int nx,ny,nfx,nfy,c;
+int nqx2, nqy2;
+unsigned char *scratch;
+
+	/*
+	 * log2n is log2 of max(nqx,nqy) rounded up to next power of 2
+	 */
+	nqmax = (nqx>nqy) ? nqx : nqy;
+	log2n = (int) (log((float) nqmax)/log(2.0)+0.5);
+	if (nqmax > (1<<log2n)) {
+		log2n += 1;
+	}
+	/*
+	 * allocate scratch array for working space
+	 */
+	nqx2=(nqx+1)/2;
+	nqy2=(nqy+1)/2;
+	scratch = (unsigned char *) malloc(nqx2*nqy2);
+	if (scratch == (unsigned char *) NULL) {
+		fprintf (stderr, "qtree_decode: insufficient memory");
+		return(DATA_DECOMPRESSION_ERR);
+	}
+	/*
+	 * now decode each bit plane, starting at the top
+	 * A is assumed to be initialized to zero
+	 */
+	for (bit = nbitplanes-1; bit >= 0; bit--) {
+		/*
+		 * Was bitplane was quadtree-coded or written directly?
+		 */
+		b = input_nybble(infile);
+
+		if(b == 0) {
+			/*
+			 * bit map was written directly
+			 */
+			read_bdirect(infile,a,n,nqx,nqy,scratch,bit);
+		} else if (b != 0xf) {
+			fprintf (stderr, "qtree_decode: bad format code");
+			return(DATA_DECOMPRESSION_ERR);
+		} else {
+			/*
+			 * bitmap was quadtree-coded, do log2n expansions
+			 *
+			 * read first code
+			 */
+			scratch[0] = input_huffman(infile);
+			/*
+			 * now do log2n expansions, reading codes from file as necessary
+			 */
+			nx = 1;
+			ny = 1;
+			nfx = nqx;
+			nfy = nqy;
+			c = 1<<log2n;
+			for (k = 1; k<log2n; k++) {
+				/*
+				 * this somewhat cryptic code generates the sequence
+				 * n[k-1] = (n[k]+1)/2 where n[log2n]=nqx or nqy
+				 */
+				c = c>>1;
+				nx = nx<<1;
+				ny = ny<<1;
+				if (nfx <= c) { nx -= 1; } else { nfx -= c; }
+				if (nfy <= c) { ny -= 1; } else { nfy -= c; }
+				qtree_expand(infile,scratch,nx,ny,scratch);
+			}
+			/*
+			 * now copy last set of 4-bit codes to bitplane bit of array a
+			 */
+			qtree_bitins(scratch,nqx,nqy,a,n,bit);
+		}
+	}
+	free(scratch);
+	return(0);
+}
+/*  ############################################################################  */
+static int
+qtree_decode64(unsigned char *infile, LONGLONG a[], int n, int nqx, int nqy, int nbitplanes)
+
+/*
+char *infile;
+LONGLONG a[];				 a is 2-D array with dimensions (n,n)	
+int n;					 length of full row in a				
+int nqx;				 partial length of row to decode		
+int nqy;				 partial length of column (<=n)		
+int nbitplanes;				 number of bitplanes to decode		
+*/
+{
+int log2n, k, bit, b, nqmax;
+int nx,ny,nfx,nfy,c;
+int nqx2, nqy2;
+unsigned char *scratch;
+
+	/*
+	 * log2n is log2 of max(nqx,nqy) rounded up to next power of 2
+	 */
+	nqmax = (nqx>nqy) ? nqx : nqy;
+	log2n = (int) (log((float) nqmax)/log(2.0)+0.5);
+	if (nqmax > (1<<log2n)) {
+		log2n += 1;
+	}
+	/*
+	 * allocate scratch array for working space
+	 */
+	nqx2=(nqx+1)/2;
+	nqy2=(nqy+1)/2;
+	scratch = (unsigned char *) malloc(nqx2*nqy2);
+	if (scratch == (unsigned char *) NULL) {
+		fprintf (stderr, "qtree_decode64: insufficient memory");
+		return(DATA_DECOMPRESSION_ERR);
+	}
+	/*
+	 * now decode each bit plane, starting at the top
+	 * A is assumed to be initialized to zero
+	 */
+	for (bit = nbitplanes-1; bit >= 0; bit--) {
+		/*
+		 * Was bitplane was quadtree-coded or written directly?
+		 */
+		b = input_nybble(infile);
+
+		if(b == 0) {
+			/*
+			 * bit map was written directly
+			 */
+			read_bdirect64(infile,a,n,nqx,nqy,scratch,bit);
+		} else if (b != 0xf) {
+			fprintf (stderr, "qtree_decode64: bad format code");
+			return(DATA_DECOMPRESSION_ERR);
+		} else {
+			/*
+			 * bitmap was quadtree-coded, do log2n expansions
+			 *
+			 * read first code
+			 */
+			scratch[0] = input_huffman(infile);
+			/*
+			 * now do log2n expansions, reading codes from file as necessary
+			 */
+			nx = 1;
+			ny = 1;
+			nfx = nqx;
+			nfy = nqy;
+			c = 1<<log2n;
+			for (k = 1; k<log2n; k++) {
+				/*
+				 * this somewhat cryptic code generates the sequence
+				 * n[k-1] = (n[k]+1)/2 where n[log2n]=nqx or nqy
+				 */
+				c = c>>1;
+				nx = nx<<1;
+				ny = ny<<1;
+				if (nfx <= c) { nx -= 1; } else { nfx -= c; }
+				if (nfy <= c) { ny -= 1; } else { nfy -= c; }
+				qtree_expand(infile,scratch,nx,ny,scratch);
+			}
+			/*
+			 * now copy last set of 4-bit codes to bitplane bit of array a
+			 */
+			qtree_bitins64(scratch,nqx,nqy,a,n,bit);
+		}
+	}
+	free(scratch);
+	return(0);
+}
+
+
+/*  ############################################################################  */
+/*
+ * do one quadtree expansion step on array a[(nqx+1)/2,(nqy+1)/2]
+ * results put into b[nqx,nqy] (which may be the same as a)
+ */
+static void
+qtree_expand(unsigned char *infile, unsigned char a[], int nx, int ny, unsigned char b[])
+{
+int i;
+
+	/*
+	 * first copy a to b, expanding each 4-bit value
+	 */
+	qtree_copy(a,nx,ny,b,ny);
+	/*
+	 * now read new 4-bit values into b for each non-zero element
+	 */
+	for (i = nx*ny-1; i >= 0; i--) {
+		if (b[i] != 0) b[i] = input_huffman(infile);
+	}
+}
+
+/*  ############################################################################  */
+/*
+ * copy 4-bit values from a[(nx+1)/2,(ny+1)/2] to b[nx,ny], expanding
+ * each value to 2x2 pixels
+ * a,b may be same array
+ */
+static void
+qtree_copy(unsigned char a[], int nx, int ny, unsigned char b[], int n)
+/*   int n;		declared y dimension of b */
+{
+int i, j, k, nx2, ny2;
+int s00, s10;
+
+	/*
+	 * first copy 4-bit values to b
+	 * start at end in case a,b are same array
+	 */
+	nx2 = (nx+1)/2;
+	ny2 = (ny+1)/2;
+	k = ny2*(nx2-1)+ny2-1;			/* k   is index of a[i,j]			*/
+	for (i = nx2-1; i >= 0; i--) {
+		s00 = 2*(n*i+ny2-1);		/* s00 is index of b[2*i,2*j]		*/
+		for (j = ny2-1; j >= 0; j--) {
+			b[s00] = a[k];
+			k -= 1;
+			s00 -= 2;
+		}
+	}
+	/*
+	 * now expand each 2x2 block
+	 */
+	for (i = 0; i<nx-1; i += 2) {
+		s00 = n*i;					/* s00 is index of b[i,j]	*/
+		s10 = s00+n;				/* s10 is index of b[i+1,j]	*/
+		for (j = 0; j<ny-1; j += 2) {
+			b[s10+1] =  b[s00]     & 1;
+			b[s10  ] = (b[s00]>>1) & 1;
+			b[s00+1] = (b[s00]>>2) & 1;
+			b[s00  ] = (b[s00]>>3) & 1;
+			s00 += 2;
+			s10 += 2;
+		}
+		if (j < ny) {
+			/*
+			 * row size is odd, do last element in row
+			 * s00+1, s10+1 are off edge
+			 */
+			b[s10  ] = (b[s00]>>1) & 1;
+			b[s00  ] = (b[s00]>>3) & 1;
+		}
+	}
+	if (i < nx) {
+		/*
+		 * column size is odd, do last row
+		 * s10, s10+1 are off edge
+		 */
+		s00 = n*i;
+		for (j = 0; j<ny-1; j += 2) {
+			b[s00+1] = (b[s00]>>2) & 1;
+			b[s00  ] = (b[s00]>>3) & 1;
+			s00 += 2;
+		}
+		if (j < ny) {
+			/*
+			 * both row and column size are odd, do corner element
+			 * s00+1, s10, s10+1 are off edge
+			 */
+			b[s00  ] = (b[s00]>>3) & 1;
+		}
+	}
+}
+
+/*  ############################################################################  */
+/*
+ * Copy 4-bit values from a[(nx+1)/2,(ny+1)/2] to b[nx,ny], expanding
+ * each value to 2x2 pixels and inserting into bitplane BIT of B.
+ * A,B may NOT be same array (it wouldn't make sense to be inserting
+ * bits into the same array anyway.)
+ */
+static void
+qtree_bitins(unsigned char a[], int nx, int ny, int b[], int n, int bit)
+/*
+   int n;		declared y dimension of b
+*/
+{
+int i, j, k;
+int s00, s10;
+
+	/*
+	 * expand each 2x2 block
+	 */
+	k = 0;							/* k   is index of a[i/2,j/2]	*/
+	for (i = 0; i<nx-1; i += 2) {
+		s00 = n*i;					/* s00 is index of b[i,j]		*/
+		s10 = s00+n;				/* s10 is index of b[i+1,j]		*/
+		for (j = 0; j<ny-1; j += 2) {
+			b[s10+1] |= ( a[k]     & 1) << bit;
+			b[s10  ] |= ((a[k]>>1) & 1) << bit;
+			b[s00+1] |= ((a[k]>>2) & 1) << bit;
+			b[s00  ] |= ((a[k]>>3) & 1) << bit;
+			s00 += 2;
+			s10 += 2;
+			k += 1;
+		}
+		if (j < ny) {
+			/*
+			 * row size is odd, do last element in row
+			 * s00+1, s10+1 are off edge
+			 */
+			b[s10  ] |= ((a[k]>>1) & 1) << bit;
+			b[s00  ] |= ((a[k]>>3) & 1) << bit;
+			k += 1;
+		}
+	}
+	if (i < nx) {
+		/*
+		 * column size is odd, do last row
+		 * s10, s10+1 are off edge
+		 */
+		s00 = n*i;
+		for (j = 0; j<ny-1; j += 2) {
+			b[s00+1] |= ((a[k]>>2) & 1) << bit;
+			b[s00  ] |= ((a[k]>>3) & 1) << bit;
+			s00 += 2;
+			k += 1;
+		}
+		if (j < ny) {
+			/*
+			 * both row and column size are odd, do corner element
+			 * s00+1, s10, s10+1 are off edge
+			 */
+			b[s00  ] |= ((a[k]>>3) & 1) << bit;
+			k += 1;
+		}
+	}
+}
+/*  ############################################################################  */
+/*
+ * Copy 4-bit values from a[(nx+1)/2,(ny+1)/2] to b[nx,ny], expanding
+ * each value to 2x2 pixels and inserting into bitplane BIT of B.
+ * A,B may NOT be same array (it wouldn't make sense to be inserting
+ * bits into the same array anyway.)
+ */
+static void
+qtree_bitins64(unsigned char a[], int nx, int ny, LONGLONG b[], int n, int bit)
+/*
+   int n;		declared y dimension of b
+*/
+{
+int i, j, k;
+int s00, s10;
+
+	/*
+	 * expand each 2x2 block
+	 */
+	k = 0;							/* k   is index of a[i/2,j/2]	*/
+	for (i = 0; i<nx-1; i += 2) {
+		s00 = n*i;					/* s00 is index of b[i,j]		*/
+		s10 = s00+n;				/* s10 is index of b[i+1,j]		*/
+		for (j = 0; j<ny-1; j += 2) {
+			b[s10+1] |= ((LONGLONG) ( a[k]     & 1)) << bit;
+			b[s10  ] |= ((((LONGLONG)a[k])>>1) & 1) << bit;
+			b[s00+1] |= ((((LONGLONG)a[k])>>2) & 1) << bit;
+			b[s00  ] |= ((((LONGLONG)a[k])>>3) & 1) << bit;
+			s00 += 2;
+			s10 += 2;
+			k += 1;
+		}
+		if (j < ny) {
+			/*
+			 * row size is odd, do last element in row
+			 * s00+1, s10+1 are off edge
+			 */
+			b[s10  ] |= ((((LONGLONG)a[k])>>1) & 1) << bit;
+			b[s00  ] |= ((((LONGLONG)a[k])>>3) & 1) << bit;
+			k += 1;
+		}
+	}
+	if (i < nx) {
+		/*
+		 * column size is odd, do last row
+		 * s10, s10+1 are off edge
+		 */
+		s00 = n*i;
+		for (j = 0; j<ny-1; j += 2) {
+			b[s00+1] |= ((((LONGLONG)a[k])>>2) & 1) << bit;
+			b[s00  ] |= ((((LONGLONG)a[k])>>3) & 1) << bit;
+			s00 += 2;
+			k += 1;
+		}
+		if (j < ny) {
+			/*
+			 * both row and column size are odd, do corner element
+			 * s00+1, s10, s10+1 are off edge
+			 */
+			b[s00  ] |= ((((LONGLONG)a[k])>>3) & 1) << bit;
+			k += 1;
+		}
+	}
+}
+
+/*  ############################################################################  */
+static void
+read_bdirect(unsigned char *infile, int a[], int n, int nqx, int nqy, unsigned char scratch[], int bit)
+{
+int i;
+
+	/*
+	 * read bit image packed 4 pixels/nybble
+	 */
+	for (i = 0; i < ((nqx+1)/2) * ((nqy+1)/2); i++) {
+		scratch[i] = input_nybble(infile);
+	}
+	/*
+	 * insert in bitplane BIT of image A
+	 */
+	qtree_bitins(scratch,nqx,nqy,a,n,bit);
+}
+/*  ############################################################################  */
+static void
+read_bdirect64(unsigned char *infile, LONGLONG a[], int n, int nqx, int nqy, unsigned char scratch[], int bit)
+{
+int i;
+
+	/*
+	 * read bit image packed 4 pixels/nybble
+	 */
+	for (i = 0; i < ((nqx+1)/2) * ((nqy+1)/2); i++) {
+		scratch[i] = input_nybble(infile);
+	}
+	/*
+	 * insert in bitplane BIT of image A
+	 */
+	qtree_bitins64(scratch,nqx,nqy,a,n,bit);
+}
+
+/*  ############################################################################  */
+/*
+ * Huffman decoding for fixed codes
+ *
+ * Coded values range from 0-15
+ *
+ * Huffman code values (hex):
+ *
+ *	3e, 00, 01, 08, 02, 09, 1a, 1b,
+ *	03, 1c, 0a, 1d, 0b, 1e, 3f, 0c
+ *
+ * and number of bits in each code:
+ *
+ *	6,  3,  3,  4,  3,  4,  5,  5,
+ *	3,  5,  4,  5,  4,  5,  6,  4
+ */
+static int input_huffman(unsigned char *infile)
+{
+int c;
+
+	/*
+	 * get first 3 bits to start
+	 */
+	c = input_nbits(infile,3);
+	if (c < 4) {
+		/*
+		 * this is all we need
+		 * return 1,2,4,8 for c=0,1,2,3
+		 */
+		return(1<<c);
+	}
+	/*
+	 * get the next bit
+	 */
+	c = input_bit(infile) | (c<<1);
+	if (c < 13) {
+		/*
+		 * OK, 4 bits is enough
+		 */
+		switch (c) {
+			case  8 : return(3);
+			case  9 : return(5);
+			case 10 : return(10);
+			case 11 : return(12);
+			case 12 : return(15);
+		}
+	}
+	/*
+	 * get yet another bit
+	 */
+	c = input_bit(infile) | (c<<1);
+	if (c < 31) {
+		/*
+		 * OK, 5 bits is enough
+		 */
+		switch (c) {
+			case 26 : return(6);
+			case 27 : return(7);
+			case 28 : return(9);
+			case 29 : return(11);
+			case 30 : return(13);
+		}
+	}
+	/*
+	 * need the 6th bit
+	 */
+	c = input_bit(infile) | (c<<1);
+	if (c == 62) {
+		return(0);
+	} else {
+		return(14);
+	}
+}
+
+/*  ############################################################################  */
+/*  ############################################################################  */
+/* Copyright (c) 1993 Association of Universities for Research 
+ * in Astronomy. All rights reserved. Produced under National   
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+/* qread.c	Read binary data
+ *
+ * Programmer: R. White		Date: 11 March 1991
+ */
+
+static int readint(unsigned char *infile)
+{
+int a,i;
+unsigned char b[4];
+
+	/* Read integer A one byte at a time from infile.
+	 *
+	 * This is portable from Vax to Sun since it eliminates the
+	 * need for byte-swapping.
+	 */
+	for (i=0; i<4; i++) qread(infile,(char *) &b[i],1);
+	a = b[0];
+	for (i=1; i<4; i++) a = (a<<8) + b[i];
+	return(a);
+}
+
+/*  ############################################################################  */
+static LONGLONG readlonglong(unsigned char *infile)
+{
+int i;
+LONGLONG a;
+unsigned char b[8];
+
+	/* Read integer A one byte at a time from infile.
+	 *
+	 * This is portable from Vax to Sun since it eliminates the
+	 * need for byte-swapping.
+	 */
+	for (i=0; i<8; i++) qread(infile,(char *) &b[i],1);
+	a = b[0];
+	for (i=1; i<8; i++) a = (a<<8) + b[i];
+	return(a);
+}
+
+/*  ############################################################################  */
+static void qread(unsigned char *infile,char *a, int n)
+{
+/*
+	if(myread(infile, a, n) != n) {
+		perror("qread");
+		exit(-1);
+
+	}
+*/
+	myread(infile, a, n);
+}
+
+/*  ############################################################################  */
+static int myread(unsigned char *file, char buffer[], int n)
+{
+    /*
+     * read n bytes from file into buffer
+     * returns number of bytes read (=n) if successful, <=0 if not
+     *
+     * this version is for VMS C: each read may return
+     * fewer than n bytes, so multiple reads may be needed
+     * to fill the buffer.
+     *
+     * I think this is unnecessary for Sun Unix, but it won't hurt
+     * either, so I'll leave it in.
+     */
+/*    int nread, total;  */
+
+    /* keep reading until we've read n bytes */
+/*
+    total = 0;
+    while ( (nread = fread(&buffer[total], 1, n-total, file)) > 0) {
+	total += nread;
+	if (total==n) return(total);
+    }
+*/
+
+    memcpy(buffer, &file[nextchar], n);
+    nextchar += n;
+    
+    return(n);
+}
+
+/*  ############################################################################  */
+/*  ############################################################################  */
+/* Copyright (c) 1993 Association of Universities for Research
+ * in Astronomy. All rights reserved. Produced under National
+ * Aeronautics and Space Administration Contract No. NAS5-26555.
+ */
+
+/* BIT INPUT ROUTINES */
+
+/* THE BIT BUFFER */
+
+static int buffer2;			/* Bits waiting to be input	*/
+static int bits_to_go;			/* Number of bits still in buffer */
+
+
+/* INITIALIZE BIT INPUT */
+
+/*  ############################################################################  */
+static void start_inputing_bits()
+{
+	/*
+	 * Buffer starts out with no bits in it
+	 */
+	bits_to_go = 0;
+}
+
+
+/*  ############################################################################  */
+/* INPUT A BIT */
+
+static int input_bit(unsigned char *infile)
+{
+	if (bits_to_go == 0) {			/* Read the next byte if no	*/
+		/* buffer2 = getc(infile); */	/* bits are left in buffer	*/
+
+		buffer2 = infile[nextchar];
+		nextchar++;
+		
+	/*	if (buffer2 == EOF) {  */
+			/*
+			 * end of file is an error for this application
+			 */
+/*
+			fprintf(stderr, "input_bit: unexpected end-of-file\n");
+			exit(-1);
+		}
+*/
+		bits_to_go = 8;
+	}
+	/*
+	 * Return the next bit
+	 */
+	bits_to_go -= 1;
+	return((buffer2>>bits_to_go) & 1);
+}
+
+
+/*  ############################################################################  */
+/* INPUT N BITS (N must be <= 8) */
+
+static int input_nbits(unsigned char *infile, int n)
+{
+int c;
+
+	if (bits_to_go < n) {
+		/*
+		 * need another byte's worth of bits
+		 */
+		buffer2 <<= 8;
+
+/*		c = getc(infile);  */
+
+		c = infile[nextchar];
+		nextchar++;
+		
+/*		if (c == EOF) { */
+			/*
+			 * end of file is an error for this application
+			 */
+/*
+			fprintf(stderr, "input_nbits: unexpected end-of-file\n");
+			exit(-1);
+		}
+*/
+		buffer2 |= c;
+		bits_to_go += 8;
+	}
+	/*
+	 * now pick off the first n bits
+	 */
+	bits_to_go -= n;
+	return( (buffer2>>bits_to_go) & ((1<<n)-1) );
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/pliocomp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/pliocomp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/pliocomp.c	(revision 22322)
@@ -0,0 +1,331 @@
+/* stdlib is needed for the abs function */
+#include <stdlib.h>
+/*
+   The following prototype code was provided by Doug Tody, NRAO, for
+   performing conversion between pixel arrays and line lists.  The
+   compression technique is used in IRAF.
+*/
+int pl_p2li (int *pxsrc, int xs, short *lldst, int npix);
+int pl_l2pi (short *ll_src, int xs, int *px_dst, int npix);
+
+
+/*
+ * PL_P2L -- Convert a pixel array to a line list.  The length of the list is
+ * returned as the function value.
+ *
+ * Translated from the SPP version using xc -f, f2c.  8Sep99 DCT.
+ */
+
+#ifndef min
+#define min(a,b)        (((a)<(b))?(a):(b))
+#endif
+#ifndef max
+#define max(a,b)        (((a)>(b))?(a):(b))
+#endif
+
+int pl_p2li (int *pxsrc, int xs, short *lldst, int npix)
+/* int *pxsrc;                      input pixel array */
+/* int xs;                          starting index in pxsrc (?) */
+/* short *lldst;                    encoded line list */
+/* int npix;                        number of pixels to convert */
+{
+    /* System generated locals */
+    int ret_val, i__1, i__2, i__3;
+
+    /* Local variables */
+    static int zero, v, x1, hi, ip, dv, xe, np, op, iz, nv, pv, nz;
+
+    /* Parameter adjustments */
+    --lldst;
+    --pxsrc;
+
+    /* Function Body */
+    if (! (npix <= 0)) {
+        goto L110;
+    }
+    ret_val = 0;
+    goto L100;
+L110:
+    lldst[3] = -100;
+    lldst[2] = 7;
+    lldst[1] = 0;
+    lldst[6] = 0;
+    lldst[7] = 0;
+    xe = xs + npix - 1;
+    op = 8;
+    zero = 0;
+/* Computing MAX */
+    i__1 = zero, i__2 = pxsrc[xs];
+    pv = max(i__1,i__2);
+    x1 = xs;
+    iz = xs;
+    hi = 1;
+    i__1 = xe;
+    for (ip = xs; ip <= i__1; ++ip) {
+        if (! (ip < xe)) {
+            goto L130;
+        }
+/* Computing MAX */
+        i__2 = zero, i__3 = pxsrc[ip + 1];
+        nv = max(i__2,i__3);
+        if (! (nv == pv)) {
+            goto L140;
+        }
+        goto L120;
+L140:
+        if (! (pv == 0)) {
+            goto L150;
+        }
+        pv = nv;
+        x1 = ip + 1;
+        goto L120;
+L150:
+        goto L131;
+L130:
+        if (! (pv == 0)) {
+            goto L160;
+        }
+        x1 = xe + 1;
+L160:
+L131:
+        np = ip - x1 + 1;
+        nz = x1 - iz;
+        if (! (pv > 0)) {
+            goto L170;
+        }
+        dv = pv - hi;
+        if (! (dv != 0)) {
+            goto L180;
+        }
+        hi = pv;
+        if (! (abs(dv) > 4095)) {
+            goto L190;
+        }
+        lldst[op] = (short) ((pv & 4095) + 4096);
+        ++op;
+        lldst[op] = (short) (pv / 4096);
+        ++op;
+        goto L191;
+L190:
+        if (! (dv < 0)) {
+            goto L200;
+        }
+        lldst[op] = (short) (-dv + 12288);
+        goto L201;
+L200:
+        lldst[op] = (short) (dv + 8192);
+L201:
+        ++op;
+        if (! (np == 1 && nz == 0)) {
+            goto L210;
+        }
+        v = lldst[op - 1];
+        lldst[op - 1] = (short) (v | 16384);
+        goto L91;
+L210:
+L191:
+L180:
+L170:
+        if (! (nz > 0)) {
+            goto L220;
+        }
+L230:
+        if (! (nz > 0)) {
+            goto L232;
+        }
+        lldst[op] = (short) min(4095,nz);
+        ++op;
+/* L231: */
+        nz += -4095;
+        goto L230;
+L232:
+        if (! (np == 1 && pv > 0)) {
+            goto L240;
+        }
+        lldst[op - 1] = (short) (lldst[op - 1] + 20481);
+        goto L91;
+L240:
+L220:
+L250:
+        if (! (np > 0)) {
+            goto L252;
+        }
+        lldst[op] = (short) (min(4095,np) + 16384);
+        ++op;
+/* L251: */
+        np += -4095;
+        goto L250;
+L252:
+L91:
+        x1 = ip + 1;
+        iz = x1;
+        pv = nv;
+L120:
+        ;
+    }
+/* L121: */
+    lldst[4] = (short) ((op - 1) % 32768);
+    lldst[5] = (short) ((op - 1) / 32768);
+    ret_val = op - 1;
+    goto L100;
+L100:
+    return ret_val;
+} /* plp2li_ */
+
+/*
+ * PL_L2PI -- Translate a PLIO line list into an integer pixel array.
+ * The number of pixels output (always npix) is returned as the function
+ * value.
+ *
+ * Translated from the SPP version using xc -f, f2c.  8Sep99 DCT.
+ */
+
+int pl_l2pi (short *ll_src, int xs, int *px_dst, int npix)
+/* short *ll_src;                   encoded line list */
+/* int xs;                          starting index in ll_src */
+/* int *px_dst;                    output pixel array */
+/* int npix;                       number of pixels to convert */
+{
+    /* System generated locals */
+    int ret_val, i__1, i__2;
+
+    /* Local variables */
+    static int data, sw0001, otop, i__, lllen, i1, i2, x1, x2, ip, xe, np,
+             op, pv, opcode, llfirt;
+    static int skipwd;
+
+    /* Parameter adjustments */
+    --px_dst;
+    --ll_src;
+
+    /* Function Body */
+    if (! (ll_src[3] > 0)) {
+        goto L110;
+    }
+    lllen = ll_src[3];
+    llfirt = 4;
+    goto L111;
+L110:
+    lllen = (ll_src[5] << 15) + ll_src[4];
+    llfirt = ll_src[2] + 1;
+L111:
+    if (! (npix <= 0 || lllen <= 0)) {
+        goto L120;
+    }
+    ret_val = 0;
+    goto L100;
+L120:
+    xe = xs + npix - 1;
+    skipwd = 0;
+    op = 1;
+    x1 = 1;
+    pv = 1;
+    i__1 = lllen;
+    for (ip = llfirt; ip <= i__1; ++ip) {
+        if (! skipwd) {
+            goto L140;
+        }
+        skipwd = 0;
+        goto L130;
+L140:
+        opcode = ll_src[ip] / 4096;
+        data = ll_src[ip] & 4095;
+        sw0001 = opcode;
+        goto L150;
+L160:
+        x2 = x1 + data - 1;
+        i1 = max(x1,xs);
+        i2 = min(x2,xe);
+        np = i2 - i1 + 1;
+        if (! (np > 0)) {
+            goto L170;
+        }
+        otop = op + np - 1;
+        if (! (opcode == 4)) {
+            goto L180;
+        }
+        i__2 = otop;
+        for (i__ = op; i__ <= i__2; ++i__) {
+            px_dst[i__] = pv;
+/* L190: */
+        }
+/* L191: */
+        goto L181;
+L180:
+        i__2 = otop;
+        for (i__ = op; i__ <= i__2; ++i__) {
+            px_dst[i__] = 0;
+/* L200: */
+        }
+/* L201: */
+        if (! (opcode == 5 && i2 == x2)) {
+            goto L210;
+        }
+        px_dst[otop] = pv;
+L210:
+L181:
+        op = otop + 1;
+L170:
+        x1 = x2 + 1;
+        goto L151;
+L220:
+        pv = (ll_src[ip + 1] << 12) + data;
+        skipwd = 1;
+        goto L151;
+L230:
+        pv += data;
+        goto L151;
+L240:
+        pv -= data;
+        goto L151;
+L250:
+        pv += data;
+        goto L91;
+L260:
+        pv -= data;
+L91:
+        if (! (x1 >= xs && x1 <= xe)) {
+            goto L270;
+        }
+        px_dst[op] = pv;
+        ++op;
+L270:
+        ++x1;
+        goto L151;
+L150:
+        ++sw0001;
+        if (sw0001 < 1 || sw0001 > 8) {
+            goto L151;
+        }
+        switch ((int)sw0001) {
+            case 1:  goto L160;
+            case 2:  goto L220;
+            case 3:  goto L230;
+            case 4:  goto L240;
+            case 5:  goto L160;
+            case 6:  goto L160;
+            case 7:  goto L250;
+            case 8:  goto L260;
+        }
+L151:
+        if (! (x1 > xe)) {
+            goto L280;
+        }
+        goto L131;
+L280:
+L130:
+        ;
+    }
+L131:
+    i__1 = npix;
+    for (i__ = op; i__ <= i__1; ++i__) {
+        px_dst[i__] = 0;
+/* L290: */
+    }
+/* L291: */
+    ret_val = npix;
+    goto L100;
+L100:
+    return ret_val;
+} /* pll2pi_ */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/ricecomp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/ricecomp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/ricecomp.c	(revision 22322)
@@ -0,0 +1,1372 @@
+
+// 2008.06.22 EAM : This code was taken from CFITSIO and included in Ohana for rice
+// decompression.  In order to include this .c file (or future upgrades), we include our own
+// version of ricecomp.h (not the one available in the CFITSIO tree) and include it instead of
+// fitsio2.h.  This file defines our own version of ffpmsg.  
+
+/*
+  The following code was written by Richard White at STScI and made
+  available for use in CFITSIO in July 1999.  These routines were
+  originally contained in 2 source files: rcomp.c and rdecomp.c,
+  and the 'include' file now called ricecomp.h was originally called buffer.h.
+*/
+
+/*----------------------------------------------------------*/
+/*                                                          */
+/*    START OF SOURCE FILE ORIGINALLY CALLED rcomp.c        */
+/*                                                          */
+/*----------------------------------------------------------*/
+/* @(#) rcomp.c 1.5 99/03/01 12:40:27 */
+/* rcomp.c	Compress image line using
+ *		(1) Difference of adjacent pixels
+ *		(2) Rice algorithm coding
+ *
+ * Returns number of bytes written to code buffer or
+ * -1 on failure
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef unsigned char Buffer_t;
+
+typedef struct {
+	int bitbuffer;		/* bit buffer					*/
+	int bits_to_go;		/* bits to go in buffer			*/
+	Buffer_t *start;	/* start of buffer				*/
+	Buffer_t *current;	/* current position in buffer	*/
+	Buffer_t *end;		/* end of buffer				*/
+} Buffer;
+
+#define putcbuf(c,mf) 	((*(mf->current)++ = c), 0)
+
+// #include "fitsio2.h"
+# include "ricecomp.h"
+
+static void start_outputing_bits(Buffer *buffer);
+static int done_outputing_bits(Buffer *buffer);
+static int output_nbits(Buffer *buffer, int bits, int n);
+
+/* this routine used to be called 'rcomp'  (WDP)  */
+/*---------------------------------------------------------------------------*/
+
+int fits_rcomp(int a[],		/* input array			*/
+	  int nx,		/* number of input pixels	*/
+	  unsigned char *c,	/* output buffer		*/
+	  int clen,		/* max length of output		*/
+	  int nblock)		/* coding block size		*/
+{
+Buffer bufmem, *buffer = &bufmem;
+int bsize, i, j, thisblock;
+int lastpix, nextpix, pdiff;
+int v, fs, fsmask, top, fsmax, fsbits, bbits;
+int lbitbuffer, lbits_to_go;
+unsigned int psum;
+double pixelsum, dpsum;
+unsigned int *diff;
+
+    /*
+     * Original size of each pixel (bsize, bytes) and coding block
+     * size (nblock, pixels)
+     * Could make bsize a parameter to allow more efficient
+     * compression of short & byte images.
+     */
+    bsize = 4;
+
+/*    nblock = 32; now an input parameter*/
+    /*
+     * From bsize derive:
+     * FSBITS = # bits required to store FS
+     * FSMAX = maximum value for FS
+     * BBITS = bits/pixel for direct coding
+     */
+
+/*
+    switch (bsize) {
+    case 1:
+	fsbits = 3;
+	fsmax = 6;
+	break;
+    case 2:
+	fsbits = 4;
+	fsmax = 14;
+	break;
+    case 4:
+	fsbits = 5;
+	fsmax = 25;
+	break;
+    default:
+        ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes");
+	return(-1);
+    }
+*/
+
+    /* move out of switch block, to tweak performance */
+    fsbits = 5;
+    fsmax = 25;
+    bbits = 1<<fsbits;
+
+    /*
+     * Set up buffer pointers
+     */
+    buffer->start = c;
+    buffer->current = c;
+    buffer->end = c+clen;
+    buffer->bits_to_go = 8;
+    /*
+     * array for differences mapped to non-negative values
+     */
+    diff = (unsigned int *) malloc(nblock*sizeof(unsigned int));
+    if (diff == (unsigned int *) NULL) {
+        ffpmsg("fits_rcomp: insufficient memory");
+	return(-1);
+    }
+    /*
+     * Code in blocks of nblock pixels
+     */
+    start_outputing_bits(buffer);
+
+    /* write out first int value to the first 4 bytes of the buffer */
+    if (output_nbits(buffer, a[0], 32) == EOF) {
+        ffpmsg("rice_encode: end of buffer");
+        free(diff);
+        return(-1);
+    }
+
+    lastpix = a[0];  /* the first difference will always be zero */
+
+    thisblock = nblock;
+    for (i=0; i<nx; i += nblock) {
+	/* last block may be shorter */
+	if (nx-i < nblock) thisblock = nx-i;
+	/*
+	 * Compute differences of adjacent pixels and map them to unsigned values.
+	 * Note that this may overflow the integer variables -- that's
+	 * OK, because we can recover when decompressing.  If we were
+	 * compressing shorts or bytes, would want to do this arithmetic
+	 * with short/byte working variables (though diff will still be
+	 * passed as an int.)
+	 *
+	 * compute sum of mapped pixel values at same time
+	 * use double precision for sum to allow 32-bit integer inputs
+	 */
+	pixelsum = 0.0;
+	for (j=0; j<thisblock; j++) {
+	    nextpix = a[i+j];
+	    pdiff = nextpix - lastpix;
+	    diff[j] = (unsigned int) ((pdiff<0) ? ~(pdiff<<1) : (pdiff<<1));
+	    pixelsum += diff[j];
+	    lastpix = nextpix;
+	}
+
+	/*
+	 * compute number of bits to split from sum
+	 */
+	dpsum = (pixelsum - (thisblock/2) - 1)/thisblock;
+	if (dpsum < 0) dpsum = 0.0;
+	psum = ((unsigned int) dpsum ) >> 1;
+	for (fs = 0; psum>0; fs++) psum >>= 1;
+
+	/*
+	 * write the codes
+	 * fsbits ID bits used to indicate split level
+	 */
+	if (fs >= fsmax) {
+	    /* Special high entropy case when FS >= fsmax
+	     * Just write pixel difference values directly, no Rice coding at all.
+	     */
+	    if (output_nbits(buffer, fsmax+1, fsbits) == EOF) {
+                ffpmsg("rice_encode: end of buffer");
+                free(diff);
+		return(-1);
+	    }
+	    for (j=0; j<thisblock; j++) {
+		if (output_nbits(buffer, diff[j], bbits) == EOF) {
+                    ffpmsg("rice_encode: end of buffer");
+                    free(diff);
+		    return(-1);
+		}
+	    }
+	} else if (fs == 0 && pixelsum == 0) {
+	    /*
+	     * special low entropy case when FS = 0 and pixelsum=0 (all
+	     * pixels in block are zero.)
+	     * Output a 0 and return
+	     */
+	    if (output_nbits(buffer, 0, fsbits) == EOF) {
+                ffpmsg("rice_encode: end of buffer");
+                free(diff);
+		return(-1);
+	    }
+	} else {
+	    /* normal case: not either very high or very low entropy */
+	    if (output_nbits(buffer, fs+1, fsbits) == EOF) {
+                ffpmsg("rice_encode: end of buffer");
+                free(diff);
+		return(-1);
+	    }
+	    fsmask = (1<<fs) - 1;
+	    /*
+	     * local copies of bit buffer to improve optimization
+	     */
+	    lbitbuffer = buffer->bitbuffer;
+	    lbits_to_go = buffer->bits_to_go;
+	    for (j=0; j<thisblock; j++) {
+		v = diff[j];
+		top = v >> fs;
+		/*
+		 * top is coded by top zeros + 1
+		 */
+		if (lbits_to_go >= top+1) {
+		    lbitbuffer <<= top+1;
+		    lbitbuffer |= 1;
+		    lbits_to_go -= top+1;
+		} else {
+		    lbitbuffer <<= lbits_to_go;
+		    putcbuf(lbitbuffer & 0xff,buffer);
+
+		    for (top -= lbits_to_go; top>=8; top -= 8) {
+			putcbuf(0, buffer);
+		    }
+		    lbitbuffer = 1;
+		    lbits_to_go = 7-top;
+		}
+		/*
+		 * bottom FS bits are written without coding
+		 * code is output_nbits, moved into this routine to reduce overheads
+		 * This code potentially breaks if FS>24, so I am limiting
+		 * FS to 24 by choice of FSMAX above.
+		 */
+		if (fs > 0) {
+		    lbitbuffer <<= fs;
+		    lbitbuffer |= v & fsmask;
+		    lbits_to_go -= fs;
+		    while (lbits_to_go <= 0) {
+			putcbuf((lbitbuffer>>(-lbits_to_go)) & 0xff,buffer);
+			lbits_to_go += 8;
+		    }
+		}
+	    }
+
+	    /* check if overflowed output buffer */
+	    if (buffer->current > buffer->end) {
+                 ffpmsg("rice_encode: end of buffer");
+                 free(diff);
+		 return(-1);
+	    }
+	    buffer->bitbuffer = lbitbuffer;
+	    buffer->bits_to_go = lbits_to_go;
+	}
+    }
+    done_outputing_bits(buffer);
+    free(diff);
+    /*
+     * return number of bytes used
+     */
+    return(buffer->current - buffer->start);
+}
+/*---------------------------------------------------------------------------*/
+
+int fits_rcomp_short(
+	  short a[],		/* input array			*/
+	  int nx,		/* number of input pixels	*/
+	  unsigned char *c,	/* output buffer		*/
+	  int clen,		/* max length of output		*/
+	  int nblock)		/* coding block size		*/
+{
+Buffer bufmem, *buffer = &bufmem;
+int bsize, i, j, thisblock;
+
+/* 
+NOTE: in principle, the following 2 variable could be declared as 'short'
+but in fact the code runs faster (on 32-bit Linux at least) as 'int'
+*/
+int lastpix, nextpix;
+/* int pdiff; */
+short pdiff; 
+int v, fs, fsmask, top, fsmax, fsbits, bbits;
+int lbitbuffer, lbits_to_go;
+/* unsigned int psum; */
+unsigned short psum;
+double pixelsum, dpsum;
+unsigned int *diff;
+
+    /*
+     * Original size of each pixel (bsize, bytes) and coding block
+     * size (nblock, pixels)
+     * Could make bsize a parameter to allow more efficient
+     * compression of short & byte images.
+     */
+    bsize = 2;
+
+/*    nblock = 32; now an input parameter */
+    /*
+     * From bsize derive:
+     * FSBITS = # bits required to store FS
+     * FSMAX = maximum value for FS
+     * BBITS = bits/pixel for direct coding
+     */
+
+/*
+    switch (bsize) {
+    case 1:
+	fsbits = 3;
+	fsmax = 6;
+	break;
+    case 2:
+	fsbits = 4;
+	fsmax = 14;
+	break;
+    case 4:
+	fsbits = 5;
+	fsmax = 25;
+	break;
+    default:
+        ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes");
+	return(-1);
+    }
+*/
+
+    /* move these out of switch block to further tweak performance */
+    fsbits = 4;
+    fsmax = 14;
+    bbits = 1<<fsbits;
+
+    /*
+     * Set up buffer pointers
+     */
+    buffer->start = c;
+    buffer->current = c;
+    buffer->end = c+clen;
+    buffer->bits_to_go = 8;
+    /*
+     * array for differences mapped to non-negative values
+     */
+    diff = (unsigned int *) malloc(nblock*sizeof(unsigned int));
+    if (diff == (unsigned int *) NULL) {
+        ffpmsg("fits_rcomp: insufficient memory");
+	return(-1);
+    }
+    /*
+     * Code in blocks of nblock pixels
+     */
+    start_outputing_bits(buffer);
+
+    /* write out first short value to the first 2 bytes of the buffer */
+    if (output_nbits(buffer, a[0], 16) == EOF) {
+        ffpmsg("rice_encode: end of buffer");
+        free(diff);
+        return(-1);
+    }
+
+    lastpix = a[0];  /* the first difference will always be zero */
+
+    thisblock = nblock;
+    for (i=0; i<nx; i += nblock) {
+	/* last block may be shorter */
+	if (nx-i < nblock) thisblock = nx-i;
+	/*
+	 * Compute differences of adjacent pixels and map them to unsigned values.
+	 * Note that this may overflow the integer variables -- that's
+	 * OK, because we can recover when decompressing.  If we were
+	 * compressing shorts or bytes, would want to do this arithmetic
+	 * with short/byte working variables (though diff will still be
+	 * passed as an int.)
+	 *
+	 * compute sum of mapped pixel values at same time
+	 * use double precision for sum to allow 32-bit integer inputs
+	 */
+	pixelsum = 0.0;
+	for (j=0; j<thisblock; j++) {
+	    nextpix = a[i+j];
+	    pdiff = nextpix - lastpix;
+	    diff[j] = (unsigned int) ((pdiff<0) ? ~(pdiff<<1) : (pdiff<<1));
+	    pixelsum += diff[j];
+	    lastpix = nextpix;
+	}
+	/*
+	 * compute number of bits to split from sum
+	 */
+	dpsum = (pixelsum - (thisblock/2) - 1)/thisblock;
+	if (dpsum < 0) dpsum = 0.0;
+/*	psum = ((unsigned int) dpsum ) >> 1; */
+	psum = ((unsigned short) dpsum ) >> 1;
+	for (fs = 0; psum>0; fs++) psum >>= 1;
+
+	/*
+	 * write the codes
+	 * fsbits ID bits used to indicate split level
+	 */
+	if (fs >= fsmax) {
+	    /* Special high entropy case when FS >= fsmax
+	     * Just write pixel difference values directly, no Rice coding at all.
+	     */
+	    if (output_nbits(buffer, fsmax+1, fsbits) == EOF) {
+                ffpmsg("rice_encode: end of buffer");
+                free(diff);
+		return(-1);
+	    }
+	    for (j=0; j<thisblock; j++) {
+		if (output_nbits(buffer, diff[j], bbits) == EOF) {
+                    ffpmsg("rice_encode: end of buffer");
+                    free(diff);
+		    return(-1);
+		}
+	    }
+	} else if (fs == 0 && pixelsum == 0) {
+	    /*
+	     * special low entropy case when FS = 0 and pixelsum=0 (all
+	     * pixels in block are zero.)
+	     * Output a 0 and return
+	     */
+	    if (output_nbits(buffer, 0, fsbits) == EOF) {
+                ffpmsg("rice_encode: end of buffer");
+                free(diff);
+		return(-1);
+	    }
+	} else {
+	    /* normal case: not either very high or very low entropy */
+	    if (output_nbits(buffer, fs+1, fsbits) == EOF) {
+                ffpmsg("rice_encode: end of buffer");
+                free(diff);
+		return(-1);
+	    }
+	    fsmask = (1<<fs) - 1;
+	    /*
+	     * local copies of bit buffer to improve optimization
+	     */
+	    lbitbuffer = buffer->bitbuffer;
+	    lbits_to_go = buffer->bits_to_go;
+	    for (j=0; j<thisblock; j++) {
+		v = diff[j];
+		top = v >> fs;
+		/*
+		 * top is coded by top zeros + 1
+		 */
+		if (lbits_to_go >= top+1) {
+		    lbitbuffer <<= top+1;
+		    lbitbuffer |= 1;
+		    lbits_to_go -= top+1;
+		} else {
+		    lbitbuffer <<= lbits_to_go;
+		    putcbuf(lbitbuffer & 0xff,buffer);
+		    for (top -= lbits_to_go; top>=8; top -= 8) {
+			putcbuf(0, buffer);
+		    }
+		    lbitbuffer = 1;
+		    lbits_to_go = 7-top;
+		}
+		/*
+		 * bottom FS bits are written without coding
+		 * code is output_nbits, moved into this routine to reduce overheads
+		 * This code potentially breaks if FS>24, so I am limiting
+		 * FS to 24 by choice of FSMAX above.
+		 */
+		if (fs > 0) {
+		    lbitbuffer <<= fs;
+		    lbitbuffer |= v & fsmask;
+		    lbits_to_go -= fs;
+		    while (lbits_to_go <= 0) {
+			putcbuf((lbitbuffer>>(-lbits_to_go)) & 0xff,buffer);
+			lbits_to_go += 8;
+		    }
+		}
+	    }
+	    /* check if overflowed output buffer */
+	    if (buffer->current > buffer->end) {
+                 ffpmsg("rice_encode: end of buffer");
+                 free(diff);
+		 return(-1);
+	    }
+	    buffer->bitbuffer = lbitbuffer;
+	    buffer->bits_to_go = lbits_to_go;
+	}
+    }
+    done_outputing_bits(buffer);
+    free(diff);
+    /*
+     * return number of bytes used
+     */
+    return(buffer->current - buffer->start);
+}
+/*---------------------------------------------------------------------------*/
+
+int fits_rcomp_byte(
+	  signed char a[],		/* input array			*/
+	  int nx,		/* number of input pixels	*/
+	  unsigned char *c,	/* output buffer		*/
+	  int clen,		/* max length of output		*/
+	  int nblock)		/* coding block size		*/
+{
+Buffer bufmem, *buffer = &bufmem;
+int bsize, i, j, thisblock;
+
+/* 
+NOTE: in principle, the following 2 variable could be declared as 'short'
+but in fact the code runs faster (on 32-bit Linux at least) as 'int'
+*/
+int lastpix, nextpix;
+/* int pdiff; */
+signed char pdiff; 
+int v, fs, fsmask, top, fsmax, fsbits, bbits;
+int lbitbuffer, lbits_to_go;
+/* unsigned int psum; */
+unsigned char psum;
+double pixelsum, dpsum;
+unsigned int *diff;
+
+    /*
+     * Original size of each pixel (bsize, bytes) and coding block
+     * size (nblock, pixels)
+     * Could make bsize a parameter to allow more efficient
+     * compression of short & byte images.
+     */
+    bsize = 1;
+
+/*    nblock = 32; now an input parameter */
+    /*
+     * From bsize derive:
+     * FSBITS = # bits required to store FS
+     * FSMAX = maximum value for FS
+     * BBITS = bits/pixel for direct coding
+     */
+
+/*
+    switch (bsize) {
+    case 1:
+	fsbits = 3;
+	fsmax = 6;
+	break;
+    case 2:
+	fsbits = 4;
+	fsmax = 14;
+	break;
+    case 4:
+	fsbits = 5;
+	fsmax = 25;
+	break;
+    default:
+        ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes");
+	return(-1);
+    }
+*/
+
+    /* move these out of switch block to further tweak performance */
+    fsbits = 3;
+    fsmax = 6;
+    bbits = 1<<fsbits;
+
+    /*
+     * Set up buffer pointers
+     */
+    buffer->start = c;
+    buffer->current = c;
+    buffer->end = c+clen;
+    buffer->bits_to_go = 8;
+    /*
+     * array for differences mapped to non-negative values
+     */
+    diff = (unsigned int *) malloc(nblock*sizeof(unsigned int));
+    if (diff == (unsigned int *) NULL) {
+        ffpmsg("fits_rcomp: insufficient memory");
+	return(-1);
+    }
+    /*
+     * Code in blocks of nblock pixels
+     */
+    start_outputing_bits(buffer);
+
+    /* write out first byte value to the first  byte of the buffer */
+    if (output_nbits(buffer, a[0], 8) == EOF) {
+        ffpmsg("rice_encode: end of buffer");
+        free(diff);
+        return(-1);
+    }
+
+    lastpix = a[0];  /* the first difference will always be zero */
+
+    thisblock = nblock;
+    for (i=0; i<nx; i += nblock) {
+	/* last block may be shorter */
+	if (nx-i < nblock) thisblock = nx-i;
+	/*
+	 * Compute differences of adjacent pixels and map them to unsigned values.
+	 * Note that this may overflow the integer variables -- that's
+	 * OK, because we can recover when decompressing.  If we were
+	 * compressing shorts or bytes, would want to do this arithmetic
+	 * with short/byte working variables (though diff will still be
+	 * passed as an int.)
+	 *
+	 * compute sum of mapped pixel values at same time
+	 * use double precision for sum to allow 32-bit integer inputs
+	 */
+	pixelsum = 0.0;
+	for (j=0; j<thisblock; j++) {
+	    nextpix = a[i+j];
+	    pdiff = nextpix - lastpix;
+	    diff[j] = (unsigned int) ((pdiff<0) ? ~(pdiff<<1) : (pdiff<<1));
+	    pixelsum += diff[j];
+	    lastpix = nextpix;
+	}
+	/*
+	 * compute number of bits to split from sum
+	 */
+	dpsum = (pixelsum - (thisblock/2) - 1)/thisblock;
+	if (dpsum < 0) dpsum = 0.0;
+/*	psum = ((unsigned int) dpsum ) >> 1; */
+	psum = ((unsigned char) dpsum ) >> 1;
+	for (fs = 0; psum>0; fs++) psum >>= 1;
+
+	/*
+	 * write the codes
+	 * fsbits ID bits used to indicate split level
+	 */
+	if (fs >= fsmax) {
+	    /* Special high entropy case when FS >= fsmax
+	     * Just write pixel difference values directly, no Rice coding at all.
+	     */
+	    if (output_nbits(buffer, fsmax+1, fsbits) == EOF) {
+                ffpmsg("rice_encode: end of buffer");
+                free(diff);
+		return(-1);
+	    }
+	    for (j=0; j<thisblock; j++) {
+		if (output_nbits(buffer, diff[j], bbits) == EOF) {
+                    ffpmsg("rice_encode: end of buffer");
+                    free(diff);
+		    return(-1);
+		}
+	    }
+	} else if (fs == 0 && pixelsum == 0) {
+	    /*
+	     * special low entropy case when FS = 0 and pixelsum=0 (all
+	     * pixels in block are zero.)
+	     * Output a 0 and return
+	     */
+	    if (output_nbits(buffer, 0, fsbits) == EOF) {
+                ffpmsg("rice_encode: end of buffer");
+                free(diff);
+		return(-1);
+	    }
+	} else {
+	    /* normal case: not either very high or very low entropy */
+	    if (output_nbits(buffer, fs+1, fsbits) == EOF) {
+                ffpmsg("rice_encode: end of buffer");
+                free(diff);
+		return(-1);
+	    }
+	    fsmask = (1<<fs) - 1;
+	    /*
+	     * local copies of bit buffer to improve optimization
+	     */
+	    lbitbuffer = buffer->bitbuffer;
+	    lbits_to_go = buffer->bits_to_go;
+	    for (j=0; j<thisblock; j++) {
+		v = diff[j];
+		top = v >> fs;
+		/*
+		 * top is coded by top zeros + 1
+		 */
+		if (lbits_to_go >= top+1) {
+		    lbitbuffer <<= top+1;
+		    lbitbuffer |= 1;
+		    lbits_to_go -= top+1;
+		} else {
+		    lbitbuffer <<= lbits_to_go;
+		    putcbuf(lbitbuffer & 0xff,buffer);
+		    for (top -= lbits_to_go; top>=8; top -= 8) {
+			putcbuf(0, buffer);
+		    }
+		    lbitbuffer = 1;
+		    lbits_to_go = 7-top;
+		}
+		/*
+		 * bottom FS bits are written without coding
+		 * code is output_nbits, moved into this routine to reduce overheads
+		 * This code potentially breaks if FS>24, so I am limiting
+		 * FS to 24 by choice of FSMAX above.
+		 */
+		if (fs > 0) {
+		    lbitbuffer <<= fs;
+		    lbitbuffer |= v & fsmask;
+		    lbits_to_go -= fs;
+		    while (lbits_to_go <= 0) {
+			putcbuf((lbitbuffer>>(-lbits_to_go)) & 0xff,buffer);
+			lbits_to_go += 8;
+		    }
+		}
+	    }
+	    /* check if overflowed output buffer */
+	    if (buffer->current > buffer->end) {
+                 ffpmsg("rice_encode: end of buffer");
+                 free(diff);
+		 return(-1);
+	    }
+	    buffer->bitbuffer = lbitbuffer;
+	    buffer->bits_to_go = lbits_to_go;
+	}
+    }
+    done_outputing_bits(buffer);
+    free(diff);
+    /*
+     * return number of bytes used
+     */
+    return(buffer->current - buffer->start);
+}
+/*---------------------------------------------------------------------------*/
+/* bit_output.c
+ *
+ * Bit output routines
+ * Procedures return zero on success, EOF on end-of-buffer
+ *
+ * Programmer: R. White     Date: 20 July 1998
+ */
+
+/* Initialize for bit output */
+
+static void start_outputing_bits(Buffer *buffer)
+{
+    /*
+     * Buffer is empty to start with
+     */
+    buffer->bitbuffer = 0;
+    buffer->bits_to_go = 8;
+}
+
+/*---------------------------------------------------------------------------*/
+/* Output N bits (N must be <= 32) */
+
+static int output_nbits(Buffer *buffer, int bits, int n)
+{
+/* local copies */
+int lbitbuffer;
+int lbits_to_go;
+    /* AND mask for the right-most n bits */
+    static unsigned int mask[33] = 
+         {0,
+	  0x1,       0x3,       0x7,       0xf,       0x1f,       0x3f,       0x7f,       0xff,
+	  0x1ff,     0x3ff,     0x7ff,     0xfff,     0x1fff,     0x3fff,     0x7fff,     0xffff,
+	  0x1ffff,   0x3ffff,   0x7ffff,   0xfffff,   0x1fffff,   0x3fffff,   0x7fffff,   0xffffff,
+	  0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff};
+
+    /*
+     * insert bits at end of bitbuffer
+     */
+    lbitbuffer = buffer->bitbuffer;
+    lbits_to_go = buffer->bits_to_go;
+    if (lbits_to_go+n > 32) {
+	/*
+	 * special case for large n: put out the top lbits_to_go bits first
+	 * note that 0 < lbits_to_go <= 8
+	 */
+	lbitbuffer <<= lbits_to_go;
+/*	lbitbuffer |= (bits>>(n-lbits_to_go)) & ((1<<lbits_to_go)-1); */
+	lbitbuffer |= (bits>>(n-lbits_to_go)) & *(mask+lbits_to_go);
+	putcbuf(lbitbuffer & 0xff,buffer);
+	n -= lbits_to_go;
+	lbits_to_go = 8;
+    }
+    lbitbuffer <<= n;
+/*    lbitbuffer |= ( bits & ((1<<n)-1) ); */
+    lbitbuffer |= ( bits & *(mask+n) );
+    lbits_to_go -= n;
+    while (lbits_to_go <= 0) {
+	/*
+	 * bitbuffer full, put out top 8 bits
+	 */
+	putcbuf((lbitbuffer>>(-lbits_to_go)) & 0xff,buffer);
+	lbits_to_go += 8;
+    }
+    buffer->bitbuffer = lbitbuffer;
+    buffer->bits_to_go = lbits_to_go;
+    return(0);
+}
+/*---------------------------------------------------------------------------*/
+/* Flush out the last bits */
+
+static int done_outputing_bits(Buffer *buffer)
+{
+    if(buffer->bits_to_go < 8) {
+	putcbuf(buffer->bitbuffer<<buffer->bits_to_go,buffer);
+	
+/*	if (putcbuf(buffer->bitbuffer<<buffer->bits_to_go,buffer) == EOF)
+	    return(EOF);
+*/
+    }
+    return(0);
+}
+/*---------------------------------------------------------------------------*/
+/*----------------------------------------------------------*/
+/*                                                          */
+/*    START OF SOURCE FILE ORIGINALLY CALLED rdecomp.c      */
+/*                                                          */
+/*----------------------------------------------------------*/
+
+/* @(#) rdecomp.c 1.4 99/03/01 12:38:41 */
+/* rdecomp.c	Decompress image line using
+ *		(1) Difference of adjacent pixels
+ *		(2) Rice algorithm coding
+ *
+ * Returns 0 on success or 1 on failure
+ */
+
+/*    moved these 'includes' to the beginning of the file (WDP)
+#include <stdio.h>
+#include <stdlib.h>
+*/
+
+/*---------------------------------------------------------------------------*/
+/* this routine used to be called 'rdecomp'  (WDP)  */
+
+int fits_rdecomp (unsigned char *c,		/* input buffer			*/
+	     int clen,			/* length of input		*/
+	     unsigned int array[],	/* output array			*/
+	     int nx,			/* number of output pixels	*/
+	     int nblock)		/* coding block size		*/
+{
+int bsize, i, k, imax;
+int nbits, nzero, fs;
+unsigned char *cend, bytevalue;
+unsigned int b, diff, lastpix;
+int fsmax, fsbits, bbits;
+static int *nonzero_count = (int *)NULL;
+
+   /*
+     * Original size of each pixel (bsize, bytes) and coding block
+     * size (nblock, pixels)
+     * Could make bsize a parameter to allow more efficient
+     * compression of short & byte images.
+     */
+    bsize = 4;
+
+/*    nblock = 32; now an input parameter */
+    /*
+     * From bsize derive:
+     * FSBITS = # bits required to store FS
+     * FSMAX = maximum value for FS
+     * BBITS = bits/pixel for direct coding
+     */
+
+/*
+    switch (bsize) {
+    case 1:
+	fsbits = 3;
+	fsmax = 6;
+	break;
+    case 2:
+	fsbits = 4;
+	fsmax = 14;
+	break;
+    case 4:
+	fsbits = 5;
+	fsmax = 25;
+	break;
+    default:
+        ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes");
+	return 1;
+    }
+*/
+
+    /* move out of switch block, to tweak performance */
+    fsbits = 5;
+    fsmax = 25;
+
+    bbits = 1<<fsbits;
+
+    if (nonzero_count == (int *) NULL) {
+	/*
+	 * nonzero_count is lookup table giving number of bits
+	 * in 8-bit values not including leading zeros
+	 */
+
+        /*  NOTE!!!  This memory never gets freed  */
+	nonzero_count = (int *) malloc(256*sizeof(int));
+	if (nonzero_count == (int *) NULL) {
+            ffpmsg("rdecomp: insufficient memory");
+	    return 1;
+	}
+	nzero = 8;
+	k = 128;
+	for (i=255; i>=0; ) {
+	    for ( ; i>=k; i--) nonzero_count[i] = nzero;
+	    k = k/2;
+	    nzero--;
+	}
+    }
+    /*
+     * Decode in blocks of nblock pixels
+     */
+
+    /* first 4 bytes of input buffer contain the value of the first */
+    /* 4 byte integer value, without any encoding */
+    
+    lastpix = 0;
+    bytevalue = c[0];
+    lastpix = lastpix | (bytevalue<<24);
+    bytevalue = c[1];
+    lastpix = lastpix | (bytevalue<<16);
+    bytevalue = c[2];
+    lastpix = lastpix | (bytevalue<<8);
+    bytevalue = c[3];
+    lastpix = lastpix | bytevalue;
+
+    c += 4;  
+    cend = c + clen - 4;
+
+    b = *c++;		    /* bit buffer			*/
+    nbits = 8;		    /* number of bits remaining in b	*/
+    for (i = 0; i<nx; ) {
+	/* get the FS value from first fsbits */
+	nbits -= fsbits;
+	while (nbits < 0) {
+	    b = (b<<8) | (*c++);
+	    nbits += 8;
+	}
+	fs = (b >> nbits) - 1;
+
+	b &= (1<<nbits)-1;
+	/* loop over the next block */
+	imax = i + nblock;
+	if (imax > nx) imax = nx;
+	if (fs<0) {
+	    /* low-entropy case, all zero differences */
+	    for ( ; i<imax; i++) array[i] = lastpix;
+	} else if (fs==fsmax) {
+	    /* high-entropy case, directly coded pixel values */
+	    for ( ; i<imax; i++) {
+		k = bbits - nbits;
+		diff = b<<k;
+		for (k -= 8; k >= 0; k -= 8) {
+		    b = *c++;
+		    diff |= b<<k;
+		}
+		if (nbits>0) {
+		    b = *c++;
+		    diff |= b>>(-k);
+		    b &= (1<<nbits)-1;
+		} else {
+		    b = 0;
+		}
+		/*
+		 * undo mapping and differencing
+		 * Note that some of these operations will overflow the
+		 * unsigned int arithmetic -- that's OK, it all works
+		 * out to give the right answers in the output file.
+		 */
+		if ((diff & 1) == 0) {
+		    diff = diff>>1;
+		} else {
+		    diff = ~(diff>>1);
+		}
+		array[i] = diff+lastpix;
+		lastpix = array[i];
+	    }
+	} else {
+	    /* normal case, Rice coding */
+	    for ( ; i<imax; i++) {
+		/* count number of leading zeros */
+		while (b == 0) {
+		    nbits += 8;
+		    b = *c++;
+		}
+		nzero = nbits - nonzero_count[b];
+		nbits -= nzero+1;
+		/* flip the leading one-bit */
+		b ^= 1<<nbits;
+		/* get the FS trailing bits */
+		nbits -= fs;
+		while (nbits < 0) {
+		    b = (b<<8) | (*c++);
+		    nbits += 8;
+		}
+		diff = (nzero<<fs) | (b>>nbits);
+		b &= (1<<nbits)-1;
+
+		/* undo mapping and differencing */
+		if ((diff & 1) == 0) {
+		    diff = diff>>1;
+		} else {
+		    diff = ~(diff>>1);
+		}
+		array[i] = diff+lastpix;
+		lastpix = array[i];
+	    }
+	}
+	if (c > cend) {
+            ffpmsg("decompression error: hit end of compressed byte stream");
+	    return 1;
+	}
+    }
+    if (c < cend) {
+        ffpmsg("decompression warning: unused bytes at end of compressed buffer");
+    }
+    return 0;
+}
+/*---------------------------------------------------------------------------*/
+/* this routine used to be called 'rdecomp'  (WDP)  */
+
+int fits_rdecomp_short (unsigned char *c,		/* input buffer			*/
+	     int clen,			/* length of input		*/
+	     unsigned short array[],  	/* output array			*/
+	     int nx,			/* number of output pixels	*/
+	     int nblock)		/* coding block size		*/
+{
+int i, imax;
+int bsize, k;
+int nbits, nzero, fs;
+unsigned char *cend, bytevalue;
+unsigned int b, diff, lastpix;
+int fsmax, fsbits, bbits;
+static int *nonzero_count = (int *)NULL;
+
+   /*
+     * Original size of each pixel (bsize, bytes) and coding block
+     * size (nblock, pixels)
+     * Could make bsize a parameter to allow more efficient
+     * compression of short & byte images.
+     */
+
+    bsize = 2;
+    
+/*    nblock = 32; now an input parameter */
+    /*
+     * From bsize derive:
+     * FSBITS = # bits required to store FS
+     * FSMAX = maximum value for FS
+     * BBITS = bits/pixel for direct coding
+     */
+
+/*
+    switch (bsize) {
+    case 1:
+	fsbits = 3;
+	fsmax = 6;
+	break;
+    case 2:
+	fsbits = 4;
+	fsmax = 14;
+	break;
+    case 4:
+	fsbits = 5;
+	fsmax = 25;
+	break;
+    default:
+        ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes");
+	return 1;
+    }
+*/
+
+    /* move out of switch block, to tweak performance */
+    fsbits = 4;
+    fsmax = 14;
+
+    bbits = 1<<fsbits;
+
+    if (nonzero_count == (int *) NULL) {
+	/*
+	 * nonzero_count is lookup table giving number of bits
+	 * in 8-bit values not including leading zeros
+	 */
+
+        /*  NOTE!!!  This memory never gets freed  */
+	nonzero_count = (int *) malloc(256*sizeof(int));
+	if (nonzero_count == (int *) NULL) {
+            ffpmsg("rdecomp: insufficient memory");
+	    return 1;
+	}
+	nzero = 8;
+	k = 128;
+	for (i=255; i>=0; ) {
+	    for ( ; i>=k; i--) nonzero_count[i] = nzero;
+	    k = k/2;
+	    nzero--;
+	}
+    }
+    /*
+     * Decode in blocks of nblock pixels
+     */
+
+    /* first 2 bytes of input buffer contain the value of the first */
+    /* 2 byte integer value, without any encoding */
+    
+    lastpix = 0;
+    bytevalue = c[0];
+    lastpix = lastpix | (bytevalue<<8);
+    bytevalue = c[1];
+    lastpix = lastpix | bytevalue;
+
+    c += 2;  
+    cend = c + clen - 2;
+
+    b = *c++;		    /* bit buffer			*/
+    nbits = 8;		    /* number of bits remaining in b	*/
+    for (i = 0; i<nx; ) {
+	/* get the FS value from first fsbits */
+	nbits -= fsbits;
+	while (nbits < 0) {
+	    b = (b<<8) | (*c++);
+	    nbits += 8;
+	}
+	fs = (b >> nbits) - 1;
+
+	b &= (1<<nbits)-1;
+	/* loop over the next block */
+	imax = i + nblock;
+	if (imax > nx) imax = nx;
+	if (fs<0) {
+	    /* low-entropy case, all zero differences */
+	    for ( ; i<imax; i++) array[i] = lastpix;
+	} else if (fs==fsmax) {
+	    /* high-entropy case, directly coded pixel values */
+	    for ( ; i<imax; i++) {
+		k = bbits - nbits;
+		diff = b<<k;
+		for (k -= 8; k >= 0; k -= 8) {
+		    b = *c++;
+		    diff |= b<<k;
+		}
+		if (nbits>0) {
+		    b = *c++;
+		    diff |= b>>(-k);
+		    b &= (1<<nbits)-1;
+		} else {
+		    b = 0;
+		}
+   
+		/*
+		 * undo mapping and differencing
+		 * Note that some of these operations will overflow the
+		 * unsigned int arithmetic -- that's OK, it all works
+		 * out to give the right answers in the output file.
+		 */
+		if ((diff & 1) == 0) {
+		    diff = diff>>1;
+		} else {
+		    diff = ~(diff>>1);
+		}
+		array[i] = diff+lastpix;
+		lastpix = array[i];
+	    }
+	} else {
+	    /* normal case, Rice coding */
+	    for ( ; i<imax; i++) {
+		/* count number of leading zeros */
+		while (b == 0) {
+		    nbits += 8;
+		    b = *c++;
+		}
+		nzero = nbits - nonzero_count[b];
+		nbits -= nzero+1;
+		/* flip the leading one-bit */
+		b ^= 1<<nbits;
+		/* get the FS trailing bits */
+		nbits -= fs;
+		while (nbits < 0) {
+		    b = (b<<8) | (*c++);
+		    nbits += 8;
+		}
+		diff = (nzero<<fs) | (b>>nbits);
+		b &= (1<<nbits)-1;
+
+		/* undo mapping and differencing */
+		if ((diff & 1) == 0) {
+		    diff = diff>>1;
+		} else {
+		    diff = ~(diff>>1);
+		}
+		array[i] = diff+lastpix;
+		lastpix = array[i];
+	    }
+	}
+	if (c > cend) {
+            ffpmsg("decompression error: hit end of compressed byte stream");
+	    return 1;
+	}
+    }
+    if (c < cend) {
+        ffpmsg("decompression warning: unused bytes at end of compressed buffer");
+    }
+    return 0;
+}
+/*---------------------------------------------------------------------------*/
+/* this routine used to be called 'rdecomp'  (WDP)  */
+
+int fits_rdecomp_byte (unsigned char *c,		/* input buffer			*/
+	     int clen,			/* length of input		*/
+	     unsigned char array[],  	/* output array			*/
+	     int nx,			/* number of output pixels	*/
+	     int nblock)		/* coding block size		*/
+{
+int i, imax;
+int bsize, k;
+int nbits, nzero, fs;
+unsigned char *cend;
+unsigned int b, diff, lastpix;
+int fsmax, fsbits, bbits;
+static int *nonzero_count = (int *)NULL;
+
+   /*
+     * Original size of each pixel (bsize, bytes) and coding block
+     * size (nblock, pixels)
+     * Could make bsize a parameter to allow more efficient
+     * compression of short & byte images.
+     */
+
+    bsize = 1;
+    
+/*    nblock = 32; now an input parameter */
+    /*
+     * From bsize derive:
+     * FSBITS = # bits required to store FS
+     * FSMAX = maximum value for FS
+     * BBITS = bits/pixel for direct coding
+     */
+
+/*
+    switch (bsize) {
+    case 1:
+	fsbits = 3;
+	fsmax = 6;
+	break;
+    case 2:
+	fsbits = 4;
+	fsmax = 14;
+	break;
+    case 4:
+	fsbits = 5;
+	fsmax = 25;
+	break;
+    default:
+        ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes");
+	return 1;
+    }
+*/
+
+    /* move out of switch block, to tweak performance */
+    fsbits = 3;
+    fsmax = 6;
+
+    bbits = 1<<fsbits;
+
+    if (nonzero_count == (int *) NULL) {
+	/*
+	 * nonzero_count is lookup table giving number of bits
+	 * in 8-bit values not including leading zeros
+	 */
+
+        /*  NOTE!!!  This memory never gets freed  */
+	nonzero_count = (int *) malloc(256*sizeof(int));
+	if (nonzero_count == (int *) NULL) {
+            ffpmsg("rdecomp: insufficient memory");
+	    return 1;
+	}
+	nzero = 8;
+	k = 128;
+	for (i=255; i>=0; ) {
+	    for ( ; i>=k; i--) nonzero_count[i] = nzero;
+	    k = k/2;
+	    nzero--;
+	}
+    }
+    /*
+     * Decode in blocks of nblock pixels
+     */
+
+    /* first byte of input buffer contain the value of the first */
+    /* byte integer value, without any encoding */
+    
+    lastpix = c[0];
+    c += 1;  
+    cend = c + clen - 1;
+
+    b = *c++;		    /* bit buffer			*/
+    nbits = 8;		    /* number of bits remaining in b	*/
+    for (i = 0; i<nx; ) {
+	/* get the FS value from first fsbits */
+	nbits -= fsbits;
+	while (nbits < 0) {
+	    b = (b<<8) | (*c++);
+	    nbits += 8;
+	}
+	fs = (b >> nbits) - 1;
+
+	b &= (1<<nbits)-1;
+	/* loop over the next block */
+	imax = i + nblock;
+	if (imax > nx) imax = nx;
+	if (fs<0) {
+	    /* low-entropy case, all zero differences */
+	    for ( ; i<imax; i++) array[i] = lastpix;
+	} else if (fs==fsmax) {
+	    /* high-entropy case, directly coded pixel values */
+	    for ( ; i<imax; i++) {
+		k = bbits - nbits;
+		diff = b<<k;
+		for (k -= 8; k >= 0; k -= 8) {
+		    b = *c++;
+		    diff |= b<<k;
+		}
+		if (nbits>0) {
+		    b = *c++;
+		    diff |= b>>(-k);
+		    b &= (1<<nbits)-1;
+		} else {
+		    b = 0;
+		}
+   
+		/*
+		 * undo mapping and differencing
+		 * Note that some of these operations will overflow the
+		 * unsigned int arithmetic -- that's OK, it all works
+		 * out to give the right answers in the output file.
+		 */
+		if ((diff & 1) == 0) {
+		    diff = diff>>1;
+		} else {
+		    diff = ~(diff>>1);
+		}
+		array[i] = diff+lastpix;
+		lastpix = array[i];
+	    }
+	} else {
+	    /* normal case, Rice coding */
+	    for ( ; i<imax; i++) {
+		/* count number of leading zeros */
+		while (b == 0) {
+		    nbits += 8;
+		    b = *c++;
+		}
+		nzero = nbits - nonzero_count[b];
+		nbits -= nzero+1;
+		/* flip the leading one-bit */
+		b ^= 1<<nbits;
+		/* get the FS trailing bits */
+		nbits -= fs;
+		while (nbits < 0) {
+		    b = (b<<8) | (*c++);
+		    nbits += 8;
+		}
+		diff = (nzero<<fs) | (b>>nbits);
+		b &= (1<<nbits)-1;
+
+		/* undo mapping and differencing */
+		if ((diff & 1) == 0) {
+		    diff = diff>>1;
+		} else {
+		    diff = ~(diff>>1);
+		}
+		array[i] = diff+lastpix;
+		lastpix = array[i];
+	    }
+	}
+	if (c > cend) {
+            ffpmsg("decompression error: hit end of compressed byte stream");
+	    return 1;
+	}
+    }
+    if (c < cend) {
+        ffpmsg("decompression warning: unused bytes at end of compressed buffer");
+    }
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/ricecomp.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/ricecomp.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/extern/ricecomp.h	(revision 22322)
@@ -0,0 +1,8 @@
+
+
+// 2008.06.22 EAM : This code was taken from CFITSIO and included in Ohana for rice
+// decompression.  In order to include this .c file (or future upgrades), we include our own
+// version of ricecomp.h (not the one available in the CFITSIO tree) and include it instead of
+// fitsio2.h.  This file defines our own version of ffpmsg.
+
+#define ffpmsg(MSG) fprintf(stderr, "%s\n", MSG)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_H_field.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_H_field.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_H_field.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits header field ****************************/
+char *gfits_header_field (Header *header, char *field, int N) {
+
+  char *buf;
+  int i, Nwant, Nfound;
+  char keyword[10];
+
+  /* create a blank-padded keyword with exactly 8 characters */
+  strcpy (keyword, field);
+  for (i = strlen(field); i < 8; i++) keyword[i] = ' ';
+  keyword[8] = 0;
+
+  buf = header[0].buffer;
+
+  if (N > 0) {
+    /* find the Nth entry */
+    Nfound = 0;
+    for (i = 0; i < header[0].size; i+= FT_LINE_LENGTH, buf += FT_LINE_LENGTH) {
+      if (!strncmp (keyword, buf, 8)) {
+	Nfound ++;
+	if (Nfound == N) return (buf);
+      }
+    }
+  }
+
+  if (N < 0) {
+    /* count the entries */
+    Nfound = 0;
+    for (i = 0; i < header[0].size; i+= FT_LINE_LENGTH, buf += FT_LINE_LENGTH) {
+      if (!strncmp (keyword, buf, 8)) {
+	Nfound ++;
+      }
+    }
+
+    Nwant = Nfound + N + 1;
+    buf = header[0].buffer;
+
+    /* find the Nwant entry */
+    Nfound = 0;
+    for (i = 0; i < header[0].size; i+= FT_LINE_LENGTH, buf += FT_LINE_LENGTH) {
+      if (!strncmp (keyword, buf, 8)) {
+	Nfound ++;
+	if (Nwant == Nfound) return (buf);
+      }
+    }
+
+  }
+
+  return ((char *) NULL);
+
+/* 
+
+   find the Nth entry of this keyword.
+   if N < 0, find the last - N - 1 word (ie, -1 = last)
+
+*/
+}
+
+/*********************** fits header field ****************************/
+char *gfits_header_lineno (Header *header, int N) {
+
+  char *buf;
+
+  if (N*80 >= header[0].size) return NULL;
+
+  buf = &header[0].buffer[N*80];
+
+  return (buf);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_convert_H.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_convert_H.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_convert_H.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+int gfits_primary_to_extended (Header *header, char *exttype, char *comment) {
+
+  int Ns, No;
+  char line[81];
+
+  // XXX check for valid exttype, comment, output string
+  snprintf (line, 81, "%-8s= '%-18s' / %-s ", "XTENSION", exttype, comment);
+  Ns = strlen (line);
+  No = 80 - Ns;
+  strncpy (header->buffer, line, Ns);
+  memset (&header->buffer[Ns], ' ', No);
+
+  return (TRUE);
+}
+
+// don't require the current to have SIMPLE
+int gfits_modify_extended (Header *header, char *exttype, char *comment) {
+
+  int Ns, No;
+  char line[81];
+
+  // XXX check for valid exttype, comment, output string
+  snprintf (line, 81, "%-8s= '%-18s' / %-s ", "XTENSION", exttype, comment);
+  Ns = strlen (line);
+  No = 80 - Ns;
+  strncpy (header->buffer, line, Ns);
+  memset (&header->buffer[Ns], ' ', No);
+
+  return (TRUE);
+}
+
+int gfits_extended_to_primary (Header *header, int simple, char *comment) {
+
+  int Ns, No;
+  char line[81];
+
+  // XXX check for valid exttype, comment, output string
+  if (simple) {
+    snprintf (line, 81, "%-8s= %-18s T / %-s ", "SIMPLE", " ", comment);
+  } else {
+    snprintf (line, 81, "%-8s= %-18s F / %-s ", "SIMPLE", " ", comment);
+  }
+  Ns = strlen (line);
+  No = 80 - Ns;
+  strncpy (header->buffer, line, Ns);
+  memset (&header->buffer[Ns], ' ', No);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_copy_H.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_copy_H.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_copy_H.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits copy header ***********************************/
+int gfits_copy_header (Header *in, Header *out) {
+
+  int i;
+
+  out[0].simple = in[0].simple;
+  out[0].unsign = in[0].unsign;
+  out[0].extend = in[0].extend;
+  out[0].bitpix = in[0].bitpix;
+
+  out[0].Naxes  = in[0].Naxes;
+  for (i = 0; i < FT_MAX_NAXES; i++) 
+    out[0].Naxis[i] = in[0].Naxis[i];
+
+  out[0].size   = in[0].size;
+
+  out[0].pcount = in[0].pcount;
+  out[0].gcount = in[0].gcount;
+  out[0].bzero  = in[0].bzero;
+  out[0].bscale = in[0].bscale;
+
+  if (out[0].buffer != NULL) free (out[0].buffer);
+  ALLOCATE (out[0].buffer, char, out[0].size);
+  
+  strncpy (out[0].buffer, in[0].buffer, out[0].size);
+
+  return (TRUE);
+}	
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_create_H.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_create_H.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_create_H.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# define NBYTES 2880
+
+/******************** fits create header ***********************************/
+int gfits_create_header (Header *header) {
+
+  int i;
+  char axis[10];
+  
+  header[0].size = NBYTES;
+
+  ALLOCATE (header[0].buffer, char, NBYTES);
+
+  for (i = 0; i < NBYTES; i++) 
+    header[0].buffer[i] = ' ';
+  strncpy (header[0].buffer, "END", 3);
+
+  gfits_modify (header, "SIMPLE", "%t", 1, header[0].simple);
+  gfits_modify (header, "BITPIX", "%d", 1, header[0].bitpix);
+  gfits_modify (header, "NAXIS",  "%d", 1, header[0].Naxes);
+				       
+  for (i = 0; i < header[0].Naxes; i++) {
+    snprintf (axis, 10, "NAXIS%d", i + 1);
+    gfits_modify (header,  axis, "%d", 1, header[0].Naxis[i]);
+  }
+
+  gfits_modify (header, "PCOUNT", "%d",  1, header[0].pcount);
+  gfits_modify (header, "GCOUNT", "%d",  1, header[0].gcount);
+  gfits_modify (header, "BSCALE", "%lf", 1, header[0].bscale);
+  gfits_modify (header, "BZERO",  "%lf", 1, header[0].bzero);
+  gfits_modify (header, "EXTEND", "%t",  1, header[0].extend);
+  return (TRUE);
+
+}	
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_delete.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_delete.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_delete.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits delete ****************************/
+int gfits_delete (Header *header, char *field, int N) {
+  
+  int i, Nbytes;
+  char *p1, *p2;
+  
+  p1 = gfits_header_field (header, field, N);
+  if (p1 == NULL) return (TRUE);
+
+  p2 = gfits_header_field (header, "END", 1);
+  if (p2 == NULL) return (FALSE); 
+
+  /* pull everything from p1 + FT_LINE_LENGTH to p2 + 79 back 80 chars */
+  Nbytes = (p2 - p1);
+  memmove (p1, p1 + FT_LINE_LENGTH, Nbytes);
+
+  for (i = 0; i < FT_LINE_LENGTH; i++) 
+    *(p2 + i) = ' ';
+
+  p2 = gfits_header_field (header, "END", 1);
+  if (header[0].size - (p2 - header[0].buffer + FT_LINE_LENGTH) > FT_RECORD_SIZE) {
+    header[0].size -= FT_RECORD_SIZE;
+    REALLOCATE (header[0].buffer, char, header[0].size);
+  }
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_free_H.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_free_H.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_free_H.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits free header ***********************************/
+void gfits_free_header (Header *header) {
+
+  if (header[0].buffer == (char *) NULL) return;
+  free (header[0].buffer);
+  header[0].buffer = (char *) NULL;
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_init_H.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_init_H.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_init_H.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include <ohana.h>
+# include <gfitsio.h>
+static int FT_UNSIGN_MODE = FALSE;
+
+/*********************** fits read header ***********************************/
+int gfits_init_header (Header *header) {
+
+  int i;
+
+  header[0].simple = TRUE;
+  header[0].extend = FALSE;
+  header[0].unsign = FALSE;
+  header[0].pcount = 0;
+  header[0].gcount = 1;
+  header[0].bscale = 1.0;
+  header[0].bzero  = 0.0;
+  header[0].bitpix = 8;
+  header[0].Naxes  = 0;
+  header[0].buffer = NULL;
+  for (i = 0; i < FT_MAX_NAXES; i++)
+    header[0].Naxis[i] = 0;
+
+  return (TRUE);
+}
+
+int gfits_set_unsign_mode (int mode) {
+  int oldmode;
+  
+  oldmode = FT_UNSIGN_MODE;
+  FT_UNSIGN_MODE = mode;
+  return (oldmode);
+}
+
+int gfits_get_unsign_mode () {
+  return (FT_UNSIGN_MODE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_insert_array.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_insert_array.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_insert_array.c	(revision 22322)
@@ -0,0 +1,24 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits insert array ***********************************/
+void gfits_insert_matrix (matrix, array, x, y) 
+Matrix *matrix, *array; 
+int x, y;
+{
+
+  /* there is no check here to match BITPIX, BZERO, or BSCALE */
+
+  int i, start_m, Nbytes_m, Nbytes;
+
+  Nbytes = array[0].Naxis[0]*abs(array[0].bitpix) / 8;
+  Nbytes_m = matrix[0].Naxis[0]*abs(matrix[0].bitpix) / 8;
+  start_m = y*Nbytes_m + x*abs(matrix[0].bitpix) / 8;
+
+  for (i = 0; i < array[0].Naxis[1]; i++) {
+    bcopy (&array[0].buffer[i*Nbytes], 
+	   &matrix[0].buffer[start_m + i*Nbytes_m], Nbytes);
+  }
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_modify.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_modify.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_modify.c	(revision 22322)
@@ -0,0 +1,187 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+char *gfits_keyword_start (char *line);
+char *gfits_keyword_end (char *line);
+void pad_ending (char *line, char value, int Nbyte);
+
+int gfits_modify (Header *header, char *field, char *mode, int N,...) {
+  
+  /* this function expects one more argument, the value to be written */
+  /* this function is extremely similar to gfits_print, except it allows for changing an existing field. */
+
+  char comment[82], string[82], data[82];
+  char *p, *qs, *qe;
+  va_list argp;
+  
+  va_start (argp, N);
+  bzero (data, 82);
+  bzero (string, 82);
+  bzero (comment, 82);
+
+  if (mode[0] != '%') {
+    fprintf (stderr, "gfits_print: weird mode:  %s\n", mode);
+    return (FALSE);
+  }
+
+  /* find location of desired entry */
+  p = gfits_header_field (header, field, N);
+  if (p == NULL)  {
+    /* new entry, find the END of the header */
+    p = gfits_header_field (header, "END", 1);
+    if (p == NULL) return (FALSE); 
+    
+    /* is there enough space for 1 more line? */
+    if (header[0].size - (p - (header[0].buffer)) < 2*FT_LINE_LENGTH) {
+      header[0].size += FT_RECORD_SIZE;
+      REALLOCATE (header[0].buffer, char, header[0].size);
+      p = gfits_header_field (header, "END", 1);
+      if (p == NULL) return (FALSE); 
+      memset (p + FT_LINE_LENGTH, ' ', FT_RECORD_SIZE);
+    }
+    
+    /* push END line back 1 */
+    memmove ((p + FT_LINE_LENGTH), p, FT_LINE_LENGTH);
+    memset (p, ' ', FT_LINE_LENGTH);
+  } else {
+    /* old entry, save the comment region (is this skipping a character for non-strings?) */
+    qe = gfits_keyword_end (p);
+    qe += 3;
+    qe = MIN (p + 80, qe);
+    strncpy (comment, qe, p + 80 - qe);
+  }
+  pad_ending (comment, ' ', 82);  /* comment must contain spaces to the end */
+
+  /* write the numeric modes */
+  if (!strcmp (mode, "%d"))  snprintf (string, 81, "%-8s= %20d / %-s ",     field, va_arg (argp, int),      comment);
+  if (!strcmp (mode, "%u"))  snprintf (string, 81, "%-8s= %20d / %-s ",     field, va_arg (argp, unsigned), comment);
+  if (!strcmp (mode, "%ld")) snprintf (string, 81, "%-8s= %20ld / %-s ",    field, va_arg (argp, long),     comment);
+  if (!strcmp (mode, "%hd")) snprintf (string, 81, "%-8s= %20d / %-s ",     field, va_arg (argp, int),      comment);
+  if (!strcmp (mode, "%f"))  snprintf (string, 81, "%-8s= %20.10f / %-s ",  field, va_arg (argp, double),   comment); 
+  if (!strcmp (mode, "%lf")) snprintf (string, 81, "%-8s= %20.10f / %-s ",  field, va_arg (argp, double),   comment); 
+  if (!strcmp (mode, "%e"))  snprintf (string, 81, "%-8s= %20.10E / %-s ",  field, va_arg (argp, double),   comment); 
+  if (!strcmp (mode, "%le")) snprintf (string, 81, "%-8s= %20.10E / %-s ",  field, va_arg (argp, double),   comment); 
+  if (!strcmp (mode, "%g"))  snprintf (string, 81, "%-8s= %20.10G / %-s ",  field, va_arg (argp, double),   comment); 
+  if (!strcmp (mode, "%lg")) snprintf (string, 81, "%-8s= %20.10G / %-s ",  field, va_arg (argp, double),   comment); 
+
+  /* write the boolean mode */
+  if (!strcmp (mode, "%t")) {
+    if (va_arg (argp, int)) 
+      snprintf (string, 81, "%-8s= %18s T / %-s ", field, " ", comment);
+    else
+      snprintf (string, 81, "%-8s= %18s F / %-s ", field, " ", comment);
+  }
+
+  /* string value.  Quotes must be at least 18 chars apart */
+  if (!strcmp (mode, "%s")) {
+    strncpy (data, va_arg (argp, char *), 68);
+    snprintf (string, 81, "%-8s= '%-18s' / %-s ", field, data, comment);
+  }
+
+  /* comment type of value.  */
+  if (!strcmp (mode, "%S")) {
+    strncpy (data, va_arg (argp, char *), 71);
+    snprintf (string, 81, "%-8s %-71s", field, data);
+  }  
+
+  /* just a comment associated with a value.  this is assumes a fixed format position for the comment */
+  if (!strcmp (mode, "%C")) { 
+    qe = gfits_keyword_end (p);
+    qs = gfits_keyword_start (p);
+
+    /* keep ' on ends, if there */
+    if (qe[0]  == 0x27) qe ++;
+    if (qs[-1] == 0x27) qs --;
+
+    strncpy (data, qs, MAX (MIN (qe - qs, 71), 0));
+    strncpy (comment, va_arg (argp, char *), 80);
+    pad_ending (comment, ' ', 82);  /* comment must contain spaces to the end */
+    snprintf (string, 81, "%-8s= %s / %-s", field, data, comment);
+    /* this will keep the original line, but truncate the comment */
+  }
+
+  strncpy (p, string, 80);
+  va_end (argp);
+  return (TRUE);
+
+}
+
+/* given a FITS card line, return pointer to the start of the data area */
+char *gfits_keyword_start (char *line) {
+
+  char *c;
+
+  /* find the end of the existing data region */
+  c = line + 8;
+  if (*c != '=') return (c);  /* no data in COMMENT field */
+  
+  c += 2;
+  /* advance pointer over WHITESPACE */
+  while ((*c == ' ') && (c < line + 80)) { c++; }
+  
+  /* skip one quote mark */
+  if ((*c == 0x27) && (c < line + 80)) { c++; }
+
+  return (c);
+}
+
+/* given a FITS card line, return pointer to the end of the data area */
+char *gfits_keyword_end (char *line) {
+
+  int done;
+  char *c1, *c2;
+
+  /* find the end of the existing data region */
+  c1 = line + 8;
+  if (*c1 != '=') return (c1);  /* no data in COMMENT field */
+  c1 += 2;
+
+  /* advance pointer over WHITESPACE */
+  while ((*c1 == ' ') && (c1 < line + 80)) { c1++; }
+  
+  if (c1[0] == 0x27) { /* entry a string, skip over 'fred' */
+    for (done = FALSE, c2 = c1 + 1; !done && (c2 < line + 80); c2++) {
+      if ((c2[0] == 0x27) && (c2[1] == 0x27)) {
+	c2 += 2;
+	continue;
+      }
+      if (c2[0] == 0x27) {
+	c1 = c2;
+	done = TRUE;
+      }
+    }
+    if (!done) { /* error in line: mismatched ' chars, return fixed position */
+      c1 = line + 30;
+    }
+  } else {
+    while ((*c1 != ' ') && (c1 < line + 80)) c1++;
+  }
+  return (c1);
+}
+
+/* according to the FITS guidelines, a string is supposed to start with a single quote
+   on the 11th character.  I'll be generous and allow the string to start at the 10th.
+   I read the string into tmp then strip off the leading single quote and spaces.
+   a string of "  '  fred is here  '  " should become "fred is here".
+   a string of "  fred is here  " should also become "fred is here".
+   the remaining problem:
+   "'O''HARA'" should be kept as O''HARA.  need to test for double single quotes
+   d -- /(0x2f: comment)  (*(p + i + 10) != 0x2f) &&  && (*(p + i + 10) != 0x27)
+ */
+
+/* fill 'line' with Nbyte space from first NULL to last byte with value */
+void pad_ending (char *line, char value, int Nbyte) {
+  
+  char *p;
+  int N;
+
+  line[Nbyte-1] = 0;
+  
+  p = line + strlen (line);
+  N = MAX (Nbyte - strlen (line) - 1, 0);
+  memset (p, value, N);
+}
+
+
+/* in this routine, the value field will expand as needed, forcing out the comment
+   the total line is limited by snprintf to only 80 chars + EOL */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_print.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_print.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_print.c	(revision 22322)
@@ -0,0 +1,100 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+int gfits_print (Header *header, char *field, char *mode, int N,...) {
+  
+  /* this function expects one more argument, the value to be written */
+
+  static char blank[] = " ";
+  char string[82], line[80];
+  char *p, a;
+  va_list argp;  
+
+  va_start (argp, N);
+  
+  if (mode[0] != '%') {
+    fprintf (stderr, "gfits_print: weird mode:  %s\n", mode);
+    return (FALSE);
+  }
+
+  /* this is supposed to create a new field, not modify an old one.  */
+  p = gfits_header_field (header, field, N);
+  if (p != NULL) return (FALSE);
+
+  /* find the END of the header */
+  p = gfits_header_field (header, "END", 1);
+  if (p == NULL) return (FALSE); 
+
+  /* is there enough space for 1 more line? */
+  if (header[0].size - (p - (header[0].buffer)) < 2*FT_LINE_LENGTH) {
+    header[0].size += FT_RECORD_SIZE;
+    REALLOCATE (header[0].buffer, char, header[0].size);
+    /* re-find the "END" marker, in case new memory block is used */
+    p = gfits_header_field (header, "END", 1);
+    if (p == NULL) return (FALSE); 
+    memset (p + FT_LINE_LENGTH, ' ', FT_RECORD_SIZE);
+  }
+
+  /* push END line back 1 */
+  memmove ((p + FT_LINE_LENGTH), p, FT_LINE_LENGTH);
+  memset (p, ' ', FT_LINE_LENGTH);
+
+  /* write the new FITS card, setting the comment to be blank */
+  /* in these lines, the value field will expand as needed, forcing out the comment
+     the total line is limited by snprintf to only 80 chars + EOL */
+
+  /* write the numeric modes */
+  if (!strcmp (mode, "%d"))  { snprintf (string, 81, "%-8s= %20d / %46s ",    field, va_arg (argp, int),      blank); }
+  if (!strcmp (mode, "%u"))  { snprintf (string, 81, "%-8s= %20d / %46s ",    field, va_arg (argp, unsigned), blank); }
+  if (!strcmp (mode, "%ld")) { snprintf (string, 81, "%-8s= %20ld / %46s ",   field, va_arg (argp, long),     blank); }
+  if (!strcmp (mode, "%hd")) { snprintf (string, 81, "%-8s= %20d / %46s ",    field, va_arg (argp, int),      blank); }
+  if (!strcmp (mode, "%f"))  { snprintf (string, 81, "%-8s= %20.10f / %46s ", field, va_arg (argp, double),   blank); }
+  if (!strcmp (mode, "%lf")) { snprintf (string, 81, "%-8s= %20.10f / %46s ", field, va_arg (argp, double),   blank); }
+  if (!strcmp (mode, "%e"))  { snprintf (string, 81, "%-8s= %20.10E / %46s ", field, va_arg (argp, double),   blank); }
+  if (!strcmp (mode, "%le")) { snprintf (string, 81, "%-8s= %20.10E / %46s ", field, va_arg (argp, double),   blank); }
+  if (!strcmp (mode, "%g"))  { snprintf (string, 81, "%-8s= %20.10G / %46s ", field, va_arg (argp, double),   blank); }
+  if (!strcmp (mode, "%lg")) { snprintf (string, 81, "%-8s= %20.10G / %46s ", field, va_arg (argp, double),   blank); }
+
+  /* write the boolean mode */
+  if (!strcmp (mode, "%t")) {
+    a = va_arg (argp, int);
+    if (a == 1)
+      snprintf (string, 81, "%-8s= %18s T / %46s ", field, blank, blank);
+    else
+      snprintf (string, 81, "%-8s= %18s F / %46s ", field, blank, blank);
+  }
+
+  /* string value.  Quotes must be at least 18 chars apart.  Longer lines will this should be fixed to allow arbitrary string lengths, up to 69 chars */
+  if (!strcmp (mode, "%s")) {
+    strcpy (line, va_arg (argp, char *));
+    line[68] = 0;
+    snprintf (string, 81, "%-8s= '%-18s' / %46s ", field, line, blank);
+  }
+
+  /* comment type of value.  */
+  if (!strcmp (mode, "%S")) {
+    /* we are forcing the entry to be <= 69 char long */
+    strcpy (line, va_arg (argp, char *));
+    line[71] = 0;
+    snprintf (string, 81, "%-8s %-71s", field, line);
+  }  
+
+  /*
+maximal string line:
+12345678901234567890123456789012345678901234567890123456789012345678901234567890 .
+KEYWORD = '01234567890123456789012345678901234567890123456789012345678901234567'
+
+maximal comment line:
+12345678901234567890123456789012345678901234567890123456789012345678901234567890 .
+KEYWORD  01234567890123456789012345678901234567890123456789012345678901234567890
+  */
+
+  /* can't write the comment in gfits_print - use gfits_modify */
+  if (!strcmp (mode, "%C")) return (FALSE);
+
+  strncpy (p, string, 80);
+  
+  va_end (argp);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_read_H.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_read_H.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_read_H.c	(revision 22322)
@@ -0,0 +1,99 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits read header ***********************************/
+int gfits_read_header (char *filename, Header *header) {
+  
+  FILE *f;
+  
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    header[0].buffer = NULL;
+    return (FALSE);
+  }
+  if (!gfits_load_header (f, header)) {
+    fclose (f);
+    return (FALSE);
+  }
+
+  fclose (f);
+  return (TRUE);
+}	
+
+int gfits_load_header (FILE *f, Header *header) {
+
+  int i, done, Nbytes, t1, t2;
+  char *p;
+
+  header[0].size = 0;
+  done = FALSE;
+  ALLOCATE (header[0].buffer, char, 1);
+
+  for (i = 0; !done; i++) {
+    REALLOCATE (header[0].buffer, char, (i + 1)*FT_RECORD_SIZE);
+    Nbytes = fread (&header[0].buffer[i*FT_RECORD_SIZE], 
+		    sizeof(char), FT_RECORD_SIZE, f);
+    if (Nbytes != FT_RECORD_SIZE) {
+      if (feof(f)) return (FALSE);
+      perror ("gfits_load_header : failed to read all data");
+      return (FALSE);
+    }
+    
+    header[0].size += Nbytes;
+
+    if (i == 0) { 
+      /* on first block, verify it is a FITS table: SIMPLE .. or XTENSION ... */
+      t1 = strncmp (header[0].buffer, "SIMPLE", 6);
+      t2 = strncmp (header[0].buffer, "XTENSION", 8);
+      if (t1 && t2) return (FALSE);
+    }
+
+    p = gfits_header_field (header, "END", 1);
+    if (p != NULL)
+      done = TRUE;
+  }
+
+  header[0].unsign = gfits_get_unsign_mode();
+  header[0].bscale = 1;
+  header[0].bzero  = 0;
+  for (i = 0; i < FT_MAX_NAXES; i++)
+    header[0].Naxis[i] = 0;
+
+  gfits_scan (header,  "SIMPLE", "%t",   1, &header[0].simple);
+  gfits_scan (header,  "BITPIX", "%d",   1, &header[0].bitpix);
+  gfits_scan (header,  "NAXIS",  "%d",   1, &header[0].Naxes);
+				                           
+  gfits_scan (header,  "EXTEND", "%t",   1, &header[0].extend);
+  gfits_scan (header,  "UNSIGN", "%t",   1, &header[0].unsign);
+  gfits_scan (header,  "BSCALE", "%lf",  1, &header[0].bscale);
+  gfits_scan (header,  "BZERO",  "%lf",  1, &header[0].bzero);
+				       
+  gfits_scan (header,  "NAXIS1", "%d", 1, &header[0].Naxis[0]);
+  gfits_scan (header,  "NAXIS2", "%d", 1, &header[0].Naxis[1]);
+  gfits_scan (header,  "NAXIS3", "%d", 1, &header[0].Naxis[2]);
+  gfits_scan (header,  "NAXIS4", "%d", 1, &header[0].Naxis[3]);
+  gfits_scan (header,  "NAXIS5", "%d", 1, &header[0].Naxis[4]);
+  gfits_scan (header,  "NAXIS6", "%d", 1, &header[0].Naxis[5]);
+  gfits_scan (header,  "NAXIS7", "%d", 1, &header[0].Naxis[6]);
+  gfits_scan (header,  "NAXIS8", "%d", 1, &header[0].Naxis[7]);
+  gfits_scan (header,  "NAXIS9", "%d", 1, &header[0].Naxis[8]);
+  gfits_scan (header, "NAXIS10", "%d", 1, &header[0].Naxis[9]);
+
+  if (!gfits_scan (header, "PCOUNT",  "%d", 1, &header[0].pcount)) {
+    header[0].pcount = 0;
+  }
+  if (!gfits_scan (header, "GCOUNT",  "%d", 1, &header[0].gcount)) {
+    header[0].gcount = 1;
+  }
+
+  return (TRUE);
+
+}
+
+int gfits_fread_header (FILE *f, Header *header) {
+
+  int status;
+  
+  status = gfits_load_header (f, header);
+  return (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_read_XH.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_read_XH.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_read_XH.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits read header ***********************************/
+int gfits_read_Xheader (char *filename, Header *header, int N) {
+
+  int status;
+  FILE *f;
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    header[0].buffer = NULL;
+    return (FALSE);
+  }
+
+  status = gfits_fread_Xheader (f, header, N);
+  if (!status) {
+    header[0].buffer = NULL;
+    fclose (f);
+    return (FALSE);
+  }
+
+  fclose (f);
+  return (TRUE);
+
+}
+
+/*********************** fits read header ***********************************/
+int gfits_fread_Xheader (FILE *f, Header *header, int N) {
+  
+  /* read header for extension number N */
+
+  int j, Nmatrix, Nskip;
+  Header theader;
+  
+  /* set f to beginning of file */
+  fseek (f, 0, SEEK_SET);
+
+  Nskip = 0;
+  for (j = -1; j < N; j++) {
+    /* load data for this header */
+    if (!gfits_load_header (f, &theader)) {
+      return (FALSE);
+    }
+
+    Nmatrix = gfits_data_size (&theader);
+
+    /* skip to next header */
+    fseek (f, Nmatrix, SEEK_CUR);
+    Nskip += (Nmatrix + theader.size);
+    gfits_free_header (&theader);
+  }
+  
+  if (!gfits_load_header (f, header)) {
+    return (FALSE);
+  }
+  Nskip += header[0].size;
+  return (Nskip);
+}	
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_scan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_scan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_scan.c	(revision 22322)
@@ -0,0 +1,95 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+char *gfits_keyword_start (char *line);
+char *gfits_keyword_end (char *line);
+
+int gfits_scan (Header *header, char *field, char *mode, int N,...) {
+
+  int status;
+  va_list argp;
+  
+  va_start (argp, N);
+  status = gfits_vscan (header, field, mode, N, argp);
+  va_end (argp);
+  return (status);
+}
+  
+int gfits_vscan (Header *header, char *field, char *mode, int N, va_list argp) {
+
+  char *p, *q, *s, tmp[81];
+  int Nchar;
+  double value;
+  
+  /* find the correct line with field */
+  p = gfits_header_field (header, field, N);
+  if (p == NULL) return (FALSE);
+
+  /* non-data entry (COMMENT, HISTORY) */
+  if (!strcmp (mode, "%S")) {
+    strncpy (va_arg (argp, char *), p + 8, FT_HISTORY_LENGTH);
+    return (TRUE);
+  }
+
+  /* all others require '=' in column 8 */
+  if (p[8] != '=') return (FALSE);
+
+  /* comment from data line */
+  if (!strcmp (mode, "%C")) {
+    q = gfits_keyword_end (p) + 3;
+    q = MIN (p + 80, q);
+    bzero (tmp, 81);
+    Nchar = MIN (80, p + 80 - q);
+    memcpy (tmp, q, Nchar);
+    stripwhite (tmp);
+    strcpy (va_arg (argp, char *), tmp);
+    return (TRUE);
+  }
+
+  /* extract data into char array (exclude containing ' chars) */
+  if (!strcmp (mode, "%s")) {
+    s = gfits_keyword_start (p); /* points at first char (not ') */
+    q = gfits_keyword_end (p); /* points at following space or ' */
+
+    Nchar = MAX (0, (q - s));
+    bzero (tmp, 81);
+    memcpy (tmp, s, Nchar);
+    stripwhite (tmp);
+    strcpy (va_arg (argp, char *), tmp);
+    return (TRUE);
+  }
+  
+  /* boolean data, requires int target */
+  if (!strcmp (mode, "%t")) {
+    s = gfits_keyword_start (p);
+    if (*s == 'T') {
+      *va_arg (argp, int *) = TRUE;
+      return (TRUE);
+    }
+    if (*s == 'F') {
+      *va_arg (argp, int *) = FALSE;   
+      return (TRUE);
+    }
+  }
+
+  /* remaining options are numerical data */
+  /* need to interpret 1.0d5 as 1.0e5 */
+  s = gfits_keyword_start (p); /* points at first char (not ') */
+  q = gfits_keyword_end (p); /* points at following space or ' */
+  value = strtod (s, &q);
+  if ((*q == 'd') || (*q == 'D')) 
+    value *= pow (10.0, atof (q + 1));
+
+  if (!strcmp (mode, "%d"))  { *va_arg (argp, int *)       = value; return (TRUE); }
+  if (!strcmp (mode, "%u"))  { *va_arg (argp, unsigned *)  = value; return (TRUE); }
+  if (!strcmp (mode, "%ld")) { *va_arg (argp, long *)      = value; return (TRUE); }
+  if (!strcmp (mode, "%hd")) { *va_arg (argp, short *)     = value; return (TRUE); }
+  if (!strcmp (mode, "%f"))  { *va_arg (argp, float *)     = value; return (TRUE); }
+  if (!strcmp (mode, "%lf")) { *va_arg (argp, double *)    = value; return (TRUE); }
+
+  /* no valid mode found */
+  return (FALSE);
+}
+
+/* if the variable argument stuff breaks on another system, look 
+at F_modify.c as it has the old code */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_write_H.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_write_H.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/F_write_H.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits write header ***********************************/
+int gfits_write_header (char *filename, Header *header) {
+
+  FILE *f;
+  int Nbytes, status;
+  
+  status = TRUE;
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) return (FALSE);
+
+  Nbytes = fwrite (header[0].buffer, sizeof(char), header[0].size, f);
+
+  if (Nbytes != header[0].size) {
+    status = FALSE;
+  }
+
+  fclose (f);
+
+  return (status);
+}	
+
+/*********************** fits write header ***********************************/
+int gfits_save_header (FILE *f, Header *header) {
+
+  int Nbytes, status;
+  
+  status = TRUE;
+
+  Nbytes = fwrite (header[0].buffer, sizeof(char), header[0].size, f);
+
+  if (Nbytes != header[0].size) {
+    status = FALSE;
+  }
+
+  return (status);
+}	
+
+
+int gfits_fwrite_header (FILE *f, Header *header) {
+
+  int status;
+  status = gfits_save_header (f, header);
+  return (status);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/header/version.c	(revision 22322)
@@ -0,0 +1,7 @@
+# include <ohana.h>
+# include <gfitsio.h>
+static char *name = "$Name: not supported by cvs2svn $";
+
+char *gfits_version () {
+  return (name);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/include/cfuncs.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/include/cfuncs.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/include/cfuncs.h	(revision 22322)
@@ -0,0 +1,60 @@
+/*** random C-functions ***/
+
+void
+  bcopy
+  (char *b1,
+   char *b2,
+   int   length);
+
+void
+  bzero
+  (char *b,
+   int   length);
+
+# ifndef HAS_ANSI_PROTOTYPES
+int
+   fprintf 
+   (FILE *stream,
+    char *format,...);
+
+int
+   fscanf 
+   (FILE *stream,
+    char *format,...);
+
+int 
+  fread
+  (char *ptr,
+   int size, 
+   int nitems,
+   FILE *stream);
+
+/*
+char *
+  malloc
+  (unsigned size);
+
+char *
+  realloc
+  (char *ptr,
+   unsigned size);
+*/
+
+int 
+  fwrite
+  (char *ptr,
+   int size, 
+   int nitems,
+   FILE *stream);
+
+int
+  fseek 
+  (FILE *f,
+   int offset,
+   int from);
+
+int
+  fclose
+  (FILE *f);
+# endif /* HAS_ANSI_PROTOTYPES */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/include/gfitsio.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/include/gfitsio.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/include/gfitsio.h	(revision 22322)
@@ -0,0 +1,213 @@
+/* FITS specific macros and structures */
+
+# include <assert.h>
+# include <errno.h>
+
+# ifndef GFITSIO
+# define GFITSIO
+
+/* also defined in libautocode/def/common.h */
+/* what about lin64 ?? - 'linux' might be defined automatically by linux */
+# ifndef BYTE_SWAP
+# ifdef linux
+# define BYTE_SWAP
+# endif
+
+# ifdef sid
+# define BYTE_SWAP
+# endif
+
+# ifdef darwin_x86
+# define BYTE_SWAP
+# endif
+
+# ifdef dec
+# define BYTE_SWAP
+# endif
+# endif /* BYTE_SWAP */
+
+# ifndef NEWLINE
+# define NEWLINE                 10  /* UNIX RETURN character */
+# endif /* NEWLINE */
+
+/********** FITS Constants *********/
+# define FT_TEXT_LENGTH          18  /* max length text header field */
+# define FT_MAX_NAXES            10  /* max number of axes */
+# define FT_FIELD_LENGTH          8  /* max length header field */   
+# define FT_COMMENT_LENGTH       47  /* max length comment field */
+# define FT_HISTORY_LENGTH       72  /* max length history / comment */
+# define FT_LINE_LENGTH          80  /* FITS header line length */
+# define FT_RECORD_SIZE        2880  /* FITS block size */
+
+/* this structure defines the buffer which contains a header
+   and contains the minimum required keywords */
+
+typedef struct {
+  int                     simple;
+  int                     unsign;
+  int                     extend;
+  int                     bitpix;
+  int                     Naxes;
+  int                     Naxis[FT_MAX_NAXES];
+  int                     size;
+  int                     pcount;
+  int                     gcount;
+  double                  bzero;
+  double                  bscale;
+  char                   *buffer;
+} Header;
+
+/* this structure defines the buffer which contains a matrix
+   and contains some relavant info */
+
+typedef struct {
+  int                     unsign;
+  int                     bitpix;
+  int                     Naxes;
+  int                     Naxis[FT_MAX_NAXES];
+  int                     size;
+  double                  bzero;
+  double                  bscale;
+  char                   *buffer;
+} Matrix;
+   
+/* the FTable represents a complete table on disk */          
+typedef struct {
+  Header                 *header;
+  char                   *buffer;
+  int                     size;
+} FTable;
+
+/* the VTable represents rows from a table on disk */          
+typedef struct {
+  Header                 *header;
+  char                  **buffer;
+  int                     Nrow;
+  int                    *row;
+  int                     size;  /* total buffer size */
+  int                     pad;   /* bytes of padding at the end */
+} VTable;
+
+typedef struct {
+  int  maxlen; 		      // max size of all table rows
+  int  nbytes;		      // number of bytes per column element
+  int  Nstart;		      // byte offset of this column
+  int  heap_start;	      // byte offset to start of HEAP
+  char format;		      // data format character (one of: XLABIJEDCM)
+} VarLengthColumn;
+
+# ifndef PROTO
+# define PROTO(A) A
+# endif
+
+char   *gfits_version                  PROTO((void));
+
+/******************************* Header functions *************/
+
+char   *gfits_header_field             PROTO((Header *header, char *field, int N)); 
+char   *gfits_header_lineno            PROTO((Header *header, int N)); 
+char   *gfits_keyword_end              PROTO((char *line));
+int     gfits_copy_header              PROTO((Header *in, Header *out)); 
+int     gfits_create_header            PROTO((Header *header)); 
+int     gfits_delete                   PROTO((Header *header, char *field, int N)); 
+int     gfits_fread_Xheader            PROTO((FILE *f, Header *header, int N));
+int     gfits_fread_header             PROTO((FILE *f, Header *header));
+void    gfits_free_header              PROTO((Header *header)); 
+int     gfits_fwrite_header            PROTO((FILE *f, Header *header)); 
+int     gfits_get_unsign_mode          PROTO((void));
+int     gfits_init_header              PROTO((Header *header));
+int     gfits_load_header              PROTO((FILE *f, Header *header));
+int     gfits_modify                   PROTO((Header *header, char *field, char *mode, int N,...)); 
+int     gfits_print                    PROTO((Header *header, char *field, char *mode, int N,...)); 
+int     gfits_read_Xheader             PROTO((char *filename, Header *header, int N));
+int     gfits_read_header              PROTO((char *filename, Header *header));
+int     gfits_save_header              PROTO((FILE *f, Header *header));
+int     gfits_scan                     PROTO((Header *header, char *field, char *mode, int N,...));
+int     gfits_set_unsign_mode          PROTO((int mode));
+int     gfits_stripwhite               PROTO((char *string));
+int     gfits_vscan                    PROTO((Header *header, char *field, char *mode, int N, va_list argp));
+int     gfits_write_header             PROTO((char *filename, Header *header)); 
+int     gfits_data_size                PROTO((Header *header));
+int     gfits_data_min_size            PROTO((Header *header));
+int 	gfits_extended_to_primary      PROTO((Header *header, int simple, char *comment));
+int 	gfits_primary_to_extended      PROTO((Header *header, char *exttype, char *comment));
+int 	gfits_modify_extended          PROTO((Header *header, char *exttype, char *comment));
+
+
+/******************************* Matrix functions *************/
+
+void   	gfits_add_matrix_value         PROTO((Matrix *matrix, int x, int y, double value)); 
+int    	gfits_convert_format           PROTO((Header *header, Matrix *matrix, int outBitpix, double outScale, double outZero, int outUnsign));
+int     gfits_copy_matrix              PROTO((Matrix *in, Matrix *out)); 
+int     gfits_create_matrix            PROTO((Header *header, Matrix *matrix)); 
+int    	gfits_divide_matrix            PROTO((Matrix *M1, Matrix *M2, Matrix *M3)); 
+int    	gfits_fread_matrix             PROTO((FILE *f, Matrix *matrix, Header *header));
+int    	gfits_fread_matrix_segment     PROTO((FILE *f, Matrix *matrix, Header *header, char *region));
+void    gfits_free_matrix              PROTO((Matrix *matrix)); 
+int     gfits_fwrite_matrix            PROTO((FILE *f, Matrix *matrix));      
+double  gfits_get_matrix_value         PROTO((Matrix *matrix, int x, int y)); 
+void   	gfits_insert_array             PROTO((Matrix *matrix, Matrix *array, int x, int y)); 
+int    	gfits_load_matrix              PROTO((FILE *f, Matrix *matrix, Header *header));
+int    	gfits_multiply_matrix          PROTO((Matrix *M1, Matrix *M2, Matrix *M3)); 
+int     gfits_read_matrix              PROTO((char *filename, Matrix *matrix));      
+int    	gfits_read_matrix_segment      PROTO((char *filename, Matrix *matrix, char *region));
+int     gfits_read_portion             PROTO((char *filename, Matrix *matrix, int Nskip, int Npix));
+void   	gfits_set_matrix_value         PROTO((Matrix *matrix, int x, int y, double value)); 
+int     gfits_write_matrix             PROTO((char *filename, Matrix *matrix)); 
+int     gfits_uncompress_image 	       PROTO((Header *header, Matrix *matrix, FTable *ftable));
+int     gfits_uncompress_data  	       PROTO((char *zdata, int Nzdata, char *cmptype, char **optname, char **optvalue, int Nopt, char *outdata, int *Nout, int out_pixsize));
+int     gfits_distribute_data  	       PROTO((Matrix *matrix, int bitpix, char *data, int Ndata, int *otile, int *ztile, float zscale, float zzero));
+int     gfits_byteswap_zdata   	       PROTO((char *zdata, int Nzdata, int bitpix));
+int     gfits_extension_is_compressed  PROTO((Header *header));
+int     gfits_tile_size                PROTO((Matrix *matrix, int *otile, int *ztile));
+int     gfits_uncompressed_data_pixsize PROTO((char *cmptype, int out_bitpix, char **optname, char **optvalue, int Noptions));
+int     gfits_vartable_heap_pixsize    PROTO((char format));
+
+/******************************* Table functions *************/
+
+char   *gfits_table_print              PROTO((FTable *ftable,...));
+int     gfits_add_rows                 PROTO((FTable *ftable, char *data, int Nrow, int Nbytes));
+int     gfits_bintable_format          PROTO((char *format, char *type, int *Nval, int *Nbytes));
+int     gfits_create_table             PROTO((Header *header, FTable *ftable));
+int     gfits_create_table_header      PROTO((Header *header, char *type, char *extname));
+int     gfits_define_bintable_column   PROTO((Header *header, char *format, char *label, char *comment, char *unit, double bscale, double bzero));
+int     gfits_define_table_column      PROTO((Header *header, char *format, char *label, char *comment, char *unit));
+int   	gfits_fread_ftable             PROTO((FILE *f, FTable *ftable, char *extname)); 
+int   	gfits_fread_ftable_data        PROTO((FILE *f, FTable *ftable));
+int   	gfits_fread_ftable_range       PROTO((FILE *f, FTable *ftable, int start, int Nrows));
+int   	gfits_fread_vtable             PROTO((FILE *f, VTable *vtable, char *extname, int Nrow, int *row));
+int   	gfits_fread_vtable_range       PROTO((FILE *f, VTable *vtable, int start, int Nrows));
+int     gfits_free_table               PROTO((FTable *ftable));
+int     gfits_free_vtable              PROTO((VTable *vtable));
+int     gfits_fwrite_table             PROTO((FILE *f, FTable *table));
+int     gfits_fwrite_vtable            PROTO((FILE *f, VTable *table));
+int     gfits_fwrite_ftable_range      PROTO((FILE *f, FTable *table, int start, int Nrows, int Ndisk, int Ntotal));
+int     gfits_get_bintable_column      PROTO((Header *header, FTable *table, char *label, void **data));
+int     gfits_get_bintable_column_type PROTO((Header *header, char *label, char *type, int *Nval));
+void   *gfits_get_bintable_column_data PROTO((Header *header, FTable *table, char *label, char *type, int *Nrow, int *Ncol));
+int     gfits_get_table_column         PROTO((Header *header, FTable *table, char *label, void **data));
+int     gfits_get_table_column_type    PROTO((Header *header, char *label, char *type));
+int     gfits_read_ftable              PROTO((char *filename, FTable *table, char *extname));
+int     gfits_read_table               PROTO((char *filename, FTable *ftable)); 
+int     gfits_set_bintable_column      PROTO((Header *header, FTable *table, char *label, void *data, int Nrow));
+int     gfits_set_table_column         PROTO((Header *header, FTable *table, char *label, void *data, int Nrow));
+int     gfits_table_column             PROTO((FTable *ftable, char *field, char *mode,...));
+int     gfits_table_format             PROTO((char *format, char *type, int *Nval, int *Nbytes));
+int     gfits_table_scale_data         PROTO((FTable *ftable));
+int     gfits_table_scale_storage      PROTO((FTable *ftable));
+int     gfits_table_to_vtable          PROTO((FTable *ftable, VTable *vtable, int start, int Nkeep));
+int     gfits_vadd_rows                PROTO((VTable *vtable, char *data, int Nrow, int Nbytes));
+int     gfits_vtable_from_ftable       PROTO((FTable *ftable, VTable *vtable, int *row, int Nrow));
+int     gfits_write_table              PROTO((char *filename, FTable *ftable)); 
+
+int     gfits_create_Theader           PROTO((Header *header, char *type));
+int     gfits_fread_Theader            PROTO((FILE *f, Header *header));
+int     gfits_fwrite_Theader           PROTO((FILE *f, Header *header));
+int     gfits_load_Theader             PROTO((FILE *f, Header *header));
+int     gfits_read_Theader             PROTO((char *filename, Header *header));     
+int     gfits_write_Theader            PROTO((char *filename, Header *header));
+
+int     gfits_varlength_column_define  PROTO((FTable *ftable, VarLengthColumn *def, int column));
+void   *gfits_varlength_column_pointer PROTO((FTable *ftable, VarLengthColumn *column, int row, int *length));
+
+#endif /* FITSIO */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/lib/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/lib/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/lib/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.darwin.dylib
+*.darwin_x86.dylib
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_add_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_add_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_add_M.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits insert array ***********************************/
+void gfits_add_matrix (matrix, array, x, y) 
+Matrix *matrix, *array; 
+int x, y;
+{
+
+  /* there is no check here to match BITPIX, BZERO, or BSCALE */
+
+  int i, j;
+  double V;
+
+  if ((x + array[0].Naxis[0] > matrix[0].Naxis[0]) ||
+      (y + array[0].Naxis[1] > matrix[0].Naxis[1])) {
+    fprintf (stderr, "can't add array here: (%d,%d) - (%d,%d) vs (%d,%d)\n",
+	     x, y, x + array[0].Naxis[0], y + array[0].Naxis[1],
+	     matrix[0].Naxis[0], matrix[0].Naxis[1]);
+    return;
+  }
+
+  for (i = 0; i < array[0].Naxis[0]; i++) {
+    for (j = 0; j < array[0].Naxis[1]; j++) {
+      V = gfits_get_matrix_value (array, i, j);
+      V += gfits_get_matrix_value (matrix, x + i, y + j);
+      gfits_set_matrix_value (matrix, x + i, y + j, V);
+    }
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_add_M_value.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_add_M_value.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_add_M_value.c	(revision 22322)
@@ -0,0 +1,19 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/***************** fits add matrix value ***********************************/
+void gfits_add_matrix_value (matrix, x, y, Value) 
+Matrix *matrix; 
+int x, y; 
+double Value;
+{
+
+  double value;
+
+  value = gfits_get_matrix_value (matrix, x, y);
+  value += Value;
+  gfits_set_matrix_value (matrix, x, y, value);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_compress_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_compress_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_compress_M.c	(revision 22322)
@@ -0,0 +1,527 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# include <zlib.h>
+
+# define ESCAPE { \
+  if (ztile != NULL) free (ztile); \
+  if (optname != NULL) { \
+    for (j = 0; j < Noptions; j++) { \
+      free (optname[j]); \
+      free (optvalue[j]); \
+    } \
+    free (optname); \
+    free (optvalue); \
+  } \
+  if (out != NULL) free (out); \
+  if (otile != NULL) free (otile); \
+  if (ntile != NULL) free (ntile); \
+  return (FALSE); }
+
+# define MOD_KEYWORD(ZNAME,NAME,TYPE,IN,OUT) { \
+    if (gfits_scan (header, ZNAME, TYPE, 1, IN)) { \
+      gfits_modify (header, NAME, TYPE, 1, OUT); \
+    } \
+    gfits_delete (header, ZNAME, 1); }
+
+# define MOD_KEYWORD_REQUIRED(ZNAME,NAME,TYPE,IN,OUT) { \
+  if (!gfits_scan (header, ZNAME, TYPE, 1, IN)) ESCAPE; \
+  gfits_delete (header, ZNAME, 1); \
+  gfits_modify (header, NAME, TYPE, 1, OUT); }
+
+int gfits_uncompress_image (Header *header, Matrix *matrix, FTable *ftable) {
+
+  int i, j, status, zimage, zcol, Nzrows, Nout, Nzdata, max_tile_size;
+  char cmptype[80];
+  char zaxis[10], naxis[10], key[10], word[81], exttype[81], checksum[81], datasum[81];
+  int Noptions, NOPTIONS;
+  VarLengthColumn zdef;
+  float zscale, zzero;
+
+  int zdata_pixsize, odata_pixsize, idata_pixsize;
+
+  int *ztile = NULL;
+  int *otile = NULL;
+  int *ntile = NULL;
+  char **optname = NULL;
+  char **optvalue = NULL;
+  char *out = NULL;
+  char *zdata = NULL;
+
+  Noptions = 0;
+
+  // is ZIMAGE present?
+  status = gfits_scan (ftable->header, "ZIMAGE", "%t", 1, &zimage);
+  if (!status || !zimage) ESCAPE;
+
+  // copy original header to output header
+  gfits_copy_header (ftable->header, header);
+
+  // delete ZIMAGE from output header
+  gfits_delete (header, "ZIMAGE", 1);
+
+  // extract compression-specific keywords, update header as needed.
+  if (!gfits_scan (header, "ZCMPTYPE", "%s", 1, cmptype)) ESCAPE;
+  gfits_delete (header, "ZCMPTYPE", 1);
+
+  MOD_KEYWORD_REQUIRED ("ZBITPIX", "BITPIX", "%d", &header->bitpix, header->bitpix);
+  MOD_KEYWORD_REQUIRED ("ZNAXIS",  "NAXIS",  "%d", &header->Naxes,  header->Naxes);
+
+  for (i = 0; i < header->Naxes; i++) {
+    snprintf (zaxis, 10, "ZNAXIS%d", i + 1);
+    snprintf (naxis, 10, "NAXIS%d", i + 1);
+    MOD_KEYWORD_REQUIRED (zaxis,  naxis,  "%d", &header->Naxis[i],  header->Naxis[i]);
+  }    
+
+  // set up the tile sizes : ztile is the default size of the tile in the Nth dimension
+  // the actual tile size may be smaller at the edge of a dimension.  if the ZTILEn
+  // entries are not found, default to [Nx,1,1,...]
+  ALLOCATE (ztile, int, header->Naxes);
+  if (!gfits_scan (header, "ZTILE1", "%d", 1, &ztile[0])) {
+    ztile[0] = header->Naxis[0];
+    for (i = 1; i < header->Naxes; i++) {
+      ztile[i] = 1;
+    }
+  } else {
+    gfits_delete (header, "ZTILE1", 1);
+    for (i = 1; i < header->Naxes; i++) {
+      snprintf (key, 10, "ZTILE%d", i + 1);
+      if (!gfits_scan (header, key, "%d", 1, &ztile[i])) ESCAPE;
+      gfits_delete (header, key, 1);
+    }
+  }
+
+  // search for algorithm-specific keywords. these are used to control compression options
+  // note the difference between the keywords (1 indexed) and the variables (0 indexed)
+  NOPTIONS = 10;
+  ALLOCATE (optname, char *, NOPTIONS);
+  ALLOCATE (optvalue, char *, NOPTIONS);
+  for (Noptions = 0; TRUE; Noptions++) {
+    snprintf (key, 10, "ZNAME%d", Noptions + 1);
+    if (!gfits_scan (header, key, "%s", 1, word)) break;
+    gfits_delete (header, key, 1);
+    optname[Noptions] = strcreate (word);
+
+    snprintf (key, 10, "ZVAL%d", Noptions + 1);
+    if (!gfits_scan (header, key, "%s", 1, word)) ESCAPE;
+    gfits_delete (header, key, 1);
+    optvalue[Noptions] = strcreate (word);
+
+    if (Noptions == NOPTIONS - 1) {
+      NOPTIONS += 10;
+      REALLOCATE (optname, char *, NOPTIONS);
+      REALLOCATE (optvalue, char *, NOPTIONS);
+    }
+  }
+
+  // XXX get ZMASKCMP 
+  int zsimple, have_zsimple;
+  int have_ztension;
+
+  have_zsimple  = gfits_scan (header, "ZSIMPLE", "%t", 1, &zsimple);
+  have_ztension = gfits_scan (header, "ZTENSION", "%s", 1, exttype);
+
+  // this is a very bogus case: we cannot have both keywords
+  if (have_zsimple && have_ztension) ESCAPE;
+
+  // if neither are present, we have an image that is not really following the standard:
+  // assume it is a PHU
+  if (!have_zsimple && !have_ztension) {
+    header->simple = TRUE;
+    gfits_extended_to_primary (header, header->simple, "Image data");
+
+    MOD_KEYWORD ("ZEXTEND",  "EXTEND",   "%t", &header->extend, header->extend);
+    MOD_KEYWORD ("ZBLOCKED", "BLOCKED",  "%t", &header->extend, header->extend);
+  }
+
+  // have_zsimple : image comes from a PHU
+  if (have_zsimple) {
+    header->simple = zsimple;
+    gfits_delete (header, "ZSIMPLE", 1);
+    gfits_extended_to_primary (header, header->simple, "Image data");
+
+    MOD_KEYWORD ("ZEXTEND",  "EXTEND",   "%t", &header->extend, header->extend);
+    MOD_KEYWORD ("ZBLOCKED", "BLOCKED",  "%t", &header->extend, header->extend);
+  } 
+
+  // have_ztension : image comes from an extension
+  if (have_ztension) {
+    gfits_delete (header, "ZTENSION", 1);
+    gfits_modify_extended (header, exttype, "Image extension");
+
+    // we may have an uncompressed PCOUNT / GCOUNT value, otherwise set to 0,1
+    if (gfits_scan (header, "ZPCOUNT", "%d", 1, &header->pcount)) {
+	gfits_delete (header, "ZPCOUNT", 1);
+	gfits_modify (header, "PCOUNT", "%d", 1, header->pcount);
+    } else {
+	header->pcount = 0;
+	gfits_modify (header, "PCOUNT", "%d", 1, header->pcount);
+    }
+    if (gfits_scan (header, "ZGCOUNT", "%d", 1, &header->gcount)) {
+	gfits_delete (header, "ZGCOUNT", 1);
+	gfits_modify (header, "GCOUNT", "%d", 1, header->gcount);
+    } else {
+	header->pcount = 1;
+	gfits_modify (header, "GCOUNT", "%d", 1, header->gcount);
+    }
+  } else {
+    header->pcount = 0;
+    header->gcount = 1;
+    gfits_modify (header, "PCOUNT", "%d", 1, header->pcount);
+    gfits_modify (header, "GCOUNT", "%d", 1, header->gcount);
+  }
+
+  MOD_KEYWORD ("ZHECKSUM", "CHECKSUM", "%s", checksum,        checksum);
+  MOD_KEYWORD ("ZDATASUM", "DATASUM",  "%s", datasum,         datasum);
+
+  zscale = 1;
+  gfits_scan (header, "ZSCALE", "%lf", 1, &zscale);
+
+  zzero = 0;
+  gfits_scan (header, "ZZERO", "%lf", 1, &zzero);
+
+  // find the COMPRESSED_DATA column (format should be 1PB, 1PI, 1PJ)
+  for (i = 1; TRUE; i++) {
+    snprintf (key, 10, "TTYPE%d", i);
+    if (!gfits_scan (ftable->header, key, "%s", 1, word)) ESCAPE;
+    if (!strcmp (word, "COMPRESSED_DATA")) break;
+  }
+  zcol = i;
+
+  if (!gfits_varlength_column_define (ftable, &zdef, zcol)) ESCAPE;
+  gfits_delete (header, "TFIELDS", 1);
+  snprintf (key, 10, "TTYPE%d", zcol);
+  gfits_delete (header, key, 1);
+  snprintf (key, 10, "TFORM%d", zcol);
+  gfits_delete (header, key, 1);
+
+  // create the output image
+  gfits_create_matrix (header, matrix);
+
+  // counters for tile numbers
+  ALLOCATE (otile, int, matrix->Naxes);
+  ALLOCATE (ntile, int, matrix->Naxes);
+  max_tile_size = 1;
+  for (i = 0; i < matrix->Naxes; i++) {
+    otile[i] = 0;
+    ntile[i] = (matrix->Naxis[i] % ztile[i]) ? (matrix->Naxis[i] / ztile[i] + 1) : (matrix->Naxis[i] / ztile[i]);
+    max_tile_size *= ztile[i];
+  }
+
+  // this takes place in three major steps:
+  // 1) read the table data : zdef.format tells the size of the varlength heap element
+  // 2) uncompress the data : pixel size depends on compression method
+  // 3) distribute data to the tiles : output pixel size depends on header.bitpix
+
+  // heapdata -> zdata -> odata -> idata
+
+  // heapdata.pixsize : zdef.format
+  // zdata.pixsize : depends on compression
+  // odata.pixsize : depends on compression
+  // idata.pixsize : header.bitpix
+
+  // size of an element in the vartable heap section
+  zdata_pixsize = gfits_vartable_heap_pixsize (zdef.format);
+
+  // size of a pixel in the final image
+  idata_pixsize = abs(header[0].bitpix) / 8;
+
+  // size of a pixel in the output from the decompression routine
+  odata_pixsize = gfits_uncompressed_data_pixsize (cmptype, header[0].bitpix, optname, optvalue, Noptions);
+  ALLOCATE (out, char, odata_pixsize*max_tile_size);
+
+  // uncompress the data
+  Nzrows = ftable->header->Naxis[1];
+  for (i = 0; i < Nzrows; i++) {
+
+    // expected output size for this tile
+    Nout = gfits_tile_size (matrix, otile, ztile);
+
+    zdata = gfits_varlength_column_pointer (ftable, &zdef, i, &Nzdata);
+    if (!zdata) return (FALSE);
+
+    // XXX not certain this is the correct place to do the swap (or if zdata_pixsize is
+    // the correct size to guide the swap)
+    if (!gfits_byteswap_zdata (zdata, Nzdata, zdata_pixsize)) return (FALSE);
+
+    // gfits_uncompress_data uncompresses from zdata to the temporary output buffer which must be allocated
+    if (!gfits_uncompress_data ((char *)zdata, Nzdata, cmptype, optname, optvalue, Noptions, out, &Nout, odata_pixsize)) return (FALSE);
+
+    // copy the uncompressed pixels into their correct locations 	    
+    if (!gfits_distribute_data (matrix, odata_pixsize, out, Nout, otile, ztile, zscale, zzero)) return (FALSE);
+
+    // update the tile counters, carrying to the next dimension if needed
+    for (j = 0; j < matrix->Naxes; j++) {
+      otile[j] ++;
+      if (otile[j] == ntile[j]) {
+	otile[j] = 0;
+      } else {
+	break;
+      }
+    }
+  }
+  return (TRUE);
+}
+
+// gfits_varlength returns a pointer to a chunk of Nzdata of data elements starting at zdata.
+
+// XXX need to have an API to send the user data
+
+// XXX need to byte-swap the table column; this needs to be worked out more clearly
+// in the APIs: do the table read / column extract functions swap or not?.  we have
+// put the byte-swapping in gifts_varlength_column_pointer, but this is fairly weak.
+
+// XXX inconsistency between data element size between compressed data, uncompressed data, 
+// and output pixel data.....
+
+// true sizes of this tile (in pixels)
+int gfits_tile_size (Matrix *matrix, int *otile, int *ztile) {
+
+  int i, Npixels, Ndimen;
+
+  Npixels = 1;
+  for (i = 0; i < matrix->Naxes; i++) {
+    Ndimen = MIN ((matrix->Naxis[i] - otile[i]*ztile[i]), ztile[i]);
+    Npixels *= Ndimen;
+  }
+  
+  return (Npixels);
+}
+
+// bitpix is the input data size/type
+int gfits_distribute_data (Matrix *matrix, int bitpix, char *data, int Ndata, int *otile, int *ztile, float zscale, float zzero) {
+
+  int i, j, start, offset, coord, Nline;
+  int *counter = NULL;
+  int *Ztile = NULL;
+
+  ALLOCATE (counter, int, matrix->Naxes);
+  ALLOCATE (Ztile, int, matrix->Naxes);
+
+  // counter for current row in tile to copy
+  // true sizes of this tile (in pixels)
+  for (i = 0; i < matrix->Naxes; i++) {
+    counter[i] = 0;
+    Ztile[i] = MIN ((matrix->Naxis[i] - otile[i]*ztile[i]), ztile[i]);
+  }
+
+  // number of lines in the tile (in pixels)
+  Nline = 1;
+  for (i = 1; i < matrix->Naxes; i++) {
+    Nline *= Ztile[i];
+  }
+
+  // double check reported size (needs the Isize)
+  assert (Ndata == Ztile[0]*Nline);
+
+  // set the starting point of the tile:
+  // start = otile[0]*ztile[0] + otile[1]*ztile[1]*Naxis[0] + otile[2]*ztile[2]*Naxis[0]*Naxis[1] + ...;
+  // start = otile[0]*ztile[0] + Naxis[0]*(otile[1]*ztile[1] + Naxis[1]*(otile[2]*ztile[2] + ...));
+  start = otile[matrix->Naxes-1]*ztile[matrix->Naxes-1];
+  for (i = matrix->Naxes - 2; i >= 0; i--) {
+    coord = otile[i]*ztile[i];
+    start = start*matrix->Naxis[i] + coord;
+  }
+  
+  // pixel offset in output array relative to tile start
+  offset = 0;
+
+  // we need to set up switches for all of the possible combinations:
+  // this macro is used at the inner switch to run the actual loop
+# define SCALE_AND_DIST(TYPE, SIZE) { \
+    TYPE *Optr = (TYPE *) &matrix->buffer[SIZE*(offset + start)]; \
+    for (j = 0; j < Ztile[0]; j++, Iptr++, Optr++) { \
+      *Optr = *Iptr * zscale + zzero; \
+    } }
+
+  // this macro sets up the outer switch and calls above macro with all output bitpix options
+# define SETUP_INSIZE(TYPE, SIZE) { \
+    TYPE *Iptr = (TYPE *) &data[i*SIZE*Ztile[0]]; \
+    switch (matrix->bitpix) { \
+      case 8: \
+	SCALE_AND_DIST (char, 1); \
+	break; \
+      case 16: \
+	SCALE_AND_DIST (short, 2); \
+	break; \
+      case 32: \
+	SCALE_AND_DIST (int, 4); \
+	break; \
+      case -32: \
+	SCALE_AND_DIST (float, 4); \
+	break; \
+      case -64: \
+	SCALE_AND_DIST (double, 8); \
+	break; \
+      default: \
+	abort(); \
+    } }
+
+  // loop over lines in the tile
+  for (i = 0; i < Nline; i++) {
+    switch (bitpix) {
+      case 1:
+	SETUP_INSIZE (char, 1);
+	break;
+      case 2: 
+	SETUP_INSIZE (short, 2);
+	break; 
+      case 4:
+	SETUP_INSIZE (int, 4);
+	break;
+      default:
+	abort();
+    }
+
+    // update the counters, carrying to the next dimension if needed
+    for (j = 1; j < matrix->Naxes; j++) {
+      counter[j] ++;
+      if (counter[j] == Ztile[j]) {
+	counter[j] = 0;
+      } else {
+	break;
+      }
+    }
+    if (j == matrix->Naxes) assert (i == Nline - 1); // we should be done here...
+
+    // Naxes = 3
+    // offset = counter[1]*matrix->Naxis[0] + counter[2]*matrix->Naxis[0]*matrix->Naxis[1] + 
+    // offset = matrix->Naxis[0]*(counter[1] + matrix->Naxis[1]*(counter[2] + matrix->Naxis[2]*...))
+
+    // determine the offset of the next line relative to the start position
+    offset = counter[matrix->Naxes - 1];
+    for (j = matrix->Naxes - 2; j >= 0; j--) {
+      offset = offset*matrix->Naxis[j] + counter[j];
+    }      
+  }
+
+  free (counter);
+  free (Ztile);
+  return (TRUE);
+}
+
+int gfits_byteswap_zdata (char *zdata, int Nzdata, int pixsize) {
+
+# ifdef BYTE_SWAP
+
+  int i;
+  char tmp;
+
+  switch (pixsize) {
+    case 1:
+      break;
+
+    case 2:
+      for (i = 0; i < 2*Nzdata; i+=2) {
+	tmp = zdata[i];
+	zdata[i] = zdata[i+1];
+	zdata[i+1] = tmp;
+      }
+      break;
+
+    case 4:
+      for (i = 0; i < 4*Nzdata; i+=4) {
+	tmp = zdata[i+1];
+	zdata[i+1] = zdata[i+2];
+	zdata[i+2] = tmp;
+	tmp = zdata[i];
+	zdata[i] = zdata[i+3];
+	zdata[i+3] = tmp;
+      }
+      break;
+
+    case 8:
+      for (i = 0; i < 8*Nzdata; i+=8) {
+	tmp = zdata[i+0];
+	zdata[i+0] = zdata[i+7];
+	zdata[i+7] = tmp;
+	tmp = zdata[i+1];
+	zdata[i+1] = zdata[i+6];
+	zdata[i+6] = tmp;
+	tmp = zdata[i+2];
+	zdata[i+2] = zdata[i+5];
+	zdata[i+5] = tmp;
+	tmp = zdata[i+3];
+	zdata[i+3] = zdata[i+4];
+	zdata[i+4] = tmp;
+      }
+      break;
+  }
+# endif
+  return (TRUE);
+}
+
+int gfits_extension_is_compressed (Header *header) {
+
+    int has_extension, has_extname, has_zimage, zimage;
+    char extname[80], extension[80];
+
+    has_extension = gfits_scan (header, "XTENSION", "%s", 1, extension);
+    has_extname = gfits_scan (header, "EXTNAME", "%s", 1, extname);
+    has_zimage  = gfits_scan (header, "ZIMAGE", "%t", 1, &zimage);
+
+    if (has_extension && !strcmp (extension, "IMAGE")) return (FALSE);
+    if (has_zimage && zimage) return (TRUE);
+    if (has_extname) {
+	if (!strcmp (extname, "COMPRESSED_IMAGE")) return (TRUE);
+    }
+
+    return (FALSE);
+}
+
+int gfits_compressed_is_primary (Header *header) {
+
+    int has_ztension, has_zimage;
+    int ztension, zimage;
+
+    has_zimage   = gfits_scan (header, "ZIMAGE",   "%t", 1, &zimage);
+    has_ztension = gfits_scan (header, "ZTENSION", "%t", 1, &ztension);
+
+    if (has_zimage && zimage) return (TRUE);
+
+    return (FALSE);
+}
+
+int gfits_uncompressed_data_pixsize (char *cmptype, int out_bitpix, char **optname, char **optvalue, int Noptions) {
+
+  int i, Nbyte;
+
+  if (!strcasecmp(cmptype, "GZIP_1")) {
+    return (4);
+  }
+  if (!strcasecmp(cmptype, "RICE_1")) {
+
+    // if BYTEPIX option is specified, use that for Nbyte
+    for (i = 0; i < Noptions; i++) {
+      if (!strcmp(optname[i], "BYTEPIX")) {
+	Nbyte = atoi (optvalue[i]);
+	return (Nbyte);
+      }
+    }
+
+    return (4);
+  }
+  if (!strcasecmp(cmptype, "PLIO_1")) {
+    return (4);
+  }
+  if (!strcasecmp(cmptype, "HCOMPRESS_1")) {
+    if (out_bitpix == 8)  return (4);
+    if (out_bitpix == 16) return (4);
+    return (8);
+  }
+  return (0);
+}
+
+int gfits_vartable_heap_pixsize (char format) {
+
+  if (format == 'B') {
+      return (1);
+  }
+  if (format == 'I') {
+      return (2);
+  }
+  if (format == 'J') {
+      return (4);
+  }
+  fprintf (stderr, "invalid size for compressed data: %c\n", format);  
+  abort ();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_convert_format.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_convert_format.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_convert_format.c	(revision 22322)
@@ -0,0 +1,392 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+# define CONVERTDOWN \
+  inMode  *in; \
+  outMode *out; \
+  out = (outMode *) matrix[0].buffer; \
+  in  = (inMode  *) matrix[0].buffer; \
+  for (i = 0; i < Npixels; i++, out++, in++) \
+    *out = *in*A + B; \
+  REALLOCATE (matrix[0].buffer, char, matrix[0].size); 
+
+# define CONVERTSAME \
+  inMode  *in; \
+  outMode *out; \
+  out = (outMode *) matrix[0].buffer; \
+  in  = (inMode  *) matrix[0].buffer; \
+  for (i = 0; i < Npixels; i++, out++, in++) \
+    *out = *in*A + B; 
+
+# define CONVERTUP \
+  inMode  *in; \
+  outMode *out; \
+  REALLOCATE (matrix[0].buffer, char, matrix[0].size); \
+  out = (outMode *)matrix[0].buffer + Npixels - 1; \
+  in  = (inMode  *)matrix[0].buffer + Npixels - 1; \
+  for (i = 0; i < Npixels; i++, out--, in--) \
+    *out = *in*A + B;
+
+/*********************** fits convert format ***********************************/
+int gfits_convert_format (Header *header, Matrix *matrix, int outBitpix, double outScale, double outZero, int outUnsign) {
+
+  unsigned long i, nbytes, Npixels;
+  int    inBitpix, inUnsign;
+  double inScale, inZero;
+  double A, B;
+
+  inBitpix         = header[0].bitpix;
+  inScale          = header[0].bscale;
+  inZero           = header[0].bzero;
+  inUnsign         = header[0].unsign;
+
+  if ((inBitpix == outBitpix) && (inScale == outScale) && 
+      (inZero == outZero) && (inUnsign == outUnsign))
+    return (TRUE);
+
+  matrix[0].bitpix = header[0].bitpix = outBitpix;
+  matrix[0].bscale = header[0].bscale = outScale;
+  matrix[0].bzero  = header[0].bzero  = outZero;
+  matrix[0].unsign = header[0].unsign = outUnsign;
+  matrix[0].size   = gfits_data_size (header);
+  gfits_modify (header, "BITPIX", "%d", 1, outBitpix);
+  gfits_modify (header, "BSCALE", "%lf", 1, outScale);
+  gfits_modify (header, "BZERO",  "%lf", 1, outZero);
+  gfits_modify (header, "UNSIGN", "%t", 1, outUnsign);
+
+  Npixels          = header[0].Naxis[0]*header[0].Naxis[1];
+  nbytes           = Npixels * (abs(outBitpix) / 8);
+
+  A = inScale / outScale;
+  B = (inZero - outZero) / outScale;
+
+  if ((!outUnsign) && (!inUnsign)) {  /** BLOCK 1 **/
+    switch (inBitpix) {
+    case 8: 
+      { typedef unsigned char inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTSAME; break; }
+	case 16:  { typedef short          outMode; CONVERTUP;   break; }
+	case -16: { typedef unsigned short outMode; CONVERTUP;   break; }
+	case 32:  { typedef int            outMode; CONVERTUP;   break; }
+	case -32: { typedef float          outMode; CONVERTUP;   break; }
+	case -64: { typedef double         outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case 16: 
+      { typedef short inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTDOWN; break; }
+	case 16:  { typedef short          outMode; CONVERTSAME; break; }
+	case -16: { typedef unsigned short outMode; CONVERTSAME; break; }
+	case 32:  { typedef int            outMode; CONVERTUP;   break; }
+	case -32: { typedef float          outMode; CONVERTUP;   break; }
+	case -64: { typedef double         outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case -16: 
+      { typedef unsigned short inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTDOWN; break; }
+	case 16:  { typedef short          outMode; CONVERTSAME; break; }
+	case -16: { typedef unsigned short outMode; CONVERTSAME; break; }
+	case 32:  { typedef int            outMode; CONVERTUP;   break; }
+	case -32: { typedef float          outMode; CONVERTUP;   break; }
+	case -64: { typedef double         outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case 32: 
+      { typedef int inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTDOWN; break; }
+	case 16:  { typedef short          outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short outMode; CONVERTDOWN; break; }
+	case 32:  { typedef int            outMode; CONVERTSAME; break; }
+	case -32: { typedef float          outMode; CONVERTSAME; break; }
+	case -64: { typedef double         outMode; CONVERTUP;   break; }
+	}
+      }
+      break;
+    case -32: 
+      { typedef float inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTDOWN; break; }
+	case 16:  { typedef short          outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short outMode; CONVERTDOWN; break; }
+	case 32:  { typedef int            outMode; CONVERTSAME; break; }
+	case -32: { typedef float          outMode; CONVERTSAME; break; }
+	case -64: { typedef double         outMode; CONVERTUP;   break; }
+	}
+      }
+      break;
+    case -64: 
+      { typedef double inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTDOWN; break; }
+	case 16:  { typedef short          outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short outMode; CONVERTDOWN; break; }
+	case 32:  { typedef int            outMode; CONVERTDOWN; break; }
+	case -32: { typedef float          outMode; CONVERTDOWN; break; }
+	case -64: { typedef double         outMode; CONVERTSAME; break; }
+	}
+      }
+    }
+  }
+  if ((outUnsign) && (!inUnsign)) {  /** BLOCK 3 **/
+    switch (inBitpix) {
+    case 8: 
+      { typedef unsigned char inMode;
+	switch (outBitpix) {
+	case 8:   { typedef          char   outMode; CONVERTSAME; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTUP;   break; }
+	case -16: { typedef unsigned short  outMode; CONVERTUP;   break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTUP;   break; }
+	case -32: { typedef          float  outMode; CONVERTUP;   break; }
+	case -64: { typedef          double outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case 16: 
+      { typedef short inMode;
+	switch (outBitpix) {
+	case 8:   { typedef          char   outMode; CONVERTDOWN; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTSAME; break; }
+	case -16: { typedef unsigned short  outMode; CONVERTSAME; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTUP;   break; }
+	case -32: { typedef          float  outMode; CONVERTUP;   break; }
+	case -64: { typedef          double outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case -16: 
+      { typedef unsigned short inMode;
+	switch (outBitpix) {
+	case 8:   { typedef          char   outMode; CONVERTDOWN; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTSAME; break; }
+	case -16: { typedef unsigned short  outMode; CONVERTSAME; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTUP;   break; }
+	case -32: { typedef          float  outMode; CONVERTUP;   break; }
+	case -64: { typedef          double outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case 32: 
+      { typedef int inMode;
+	switch (outBitpix) {
+	case 8:   { typedef          char   outMode; CONVERTDOWN; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTSAME; break; }
+	case -32: { typedef          float  outMode; CONVERTSAME; break; }
+	case -64: { typedef          double outMode; CONVERTUP;   break; }
+	}
+      }
+      break;
+    case -32: 
+      { typedef float inMode;
+	switch (outBitpix) {
+	case 8:   { typedef          char   outMode; CONVERTDOWN; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTSAME; break; }
+	case -32: { typedef          float  outMode; CONVERTSAME; break; }
+	case -64: { typedef          double outMode; CONVERTUP;   break; }
+	}
+      }
+      break;
+    case -64: 
+      { typedef double inMode;
+	switch (outBitpix) {
+	case 8:   { typedef          char   outMode; CONVERTDOWN; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTDOWN; break; }
+	case -32: { typedef          float  outMode; CONVERTDOWN; break; }
+	case -64: { typedef          double outMode; CONVERTSAME; break; }
+	}
+      }
+    }
+  }
+  if ((!outUnsign) && (inUnsign)) {  /** BLOCK 2 **/
+    switch (inBitpix) {
+    case 8: 
+      { typedef unsigned char inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTSAME; break; }
+	case 16:  { typedef short          outMode; CONVERTUP;   break; }
+	case -16: { typedef unsigned short outMode; CONVERTUP;   break; }
+	case 32:  { typedef int            outMode; CONVERTUP;   break; }
+	case -32: { typedef float          outMode; CONVERTUP;   break; }
+	case -64: { typedef double         outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case 16: 
+      { typedef unsigned short inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTDOWN; break; }
+	case 16:  { typedef short          outMode; CONVERTSAME; break; }
+	case -16: { typedef unsigned short outMode; CONVERTSAME; break; }
+	case 32:  { typedef int            outMode; CONVERTUP;   break; }
+	case -32: { typedef float          outMode; CONVERTUP;   break; }
+	case -64: { typedef double         outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case -16: 
+      { typedef unsigned short inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTDOWN; break; }
+	case 16:  { typedef short          outMode; CONVERTSAME; break; }
+	case -16: { typedef unsigned short outMode; CONVERTSAME; break; }
+	case 32:  { typedef int            outMode; CONVERTUP;   break; }
+	case -32: { typedef float          outMode; CONVERTUP;   break; }
+	case -64: { typedef double         outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case 32: 
+      { typedef unsigned int inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTDOWN; break; }
+	case 16:  { typedef short          outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short outMode; CONVERTDOWN; break; }
+	case 32:  { typedef int            outMode; CONVERTSAME; break; }
+	case -32: { typedef float          outMode; CONVERTSAME; break; }
+	case -64: { typedef double         outMode; CONVERTUP;   break; }
+	}
+      }
+      break;
+    case -32: 
+      { typedef float inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTDOWN; break; }
+	case 16:  { typedef short          outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short outMode; CONVERTDOWN; break; }
+	case 32:  { typedef int            outMode; CONVERTSAME; break; }
+	case -32: { typedef float          outMode; CONVERTSAME; break; }
+	case -64: { typedef double         outMode; CONVERTUP;   break; }
+	}
+      }
+      break;
+    case -64: 
+      { typedef double inMode;
+	switch (outBitpix) {
+	case 8:   { typedef unsigned char  outMode; CONVERTDOWN; break; }
+	case 16:  { typedef short          outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short outMode; CONVERTDOWN; break; }
+	case 32:  { typedef int            outMode; CONVERTDOWN; break; }
+	case -32: { typedef float          outMode; CONVERTDOWN; break; }
+	case -64: { typedef double         outMode; CONVERTSAME; break; }
+	}
+      }
+    }
+  }
+  if ((outUnsign) && (inUnsign)) {
+    switch (inBitpix) {
+    case 8: 
+      { typedef char inMode;
+	switch (outBitpix) {
+	case 8:   { typedef char            outMode; CONVERTSAME; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTUP;   break; }
+	case -16: { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTUP;   break; }
+	case -32: { typedef          float  outMode; CONVERTUP;   break; }
+	case -64: { typedef          double outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case 16: 
+      { typedef unsigned short inMode;
+	switch (outBitpix) {
+	case 8:   { typedef char            outMode; CONVERTDOWN; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTSAME; break; }
+	case -16: { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTUP;   break; }
+	case -32: { typedef          float  outMode; CONVERTUP;   break; }
+	case -64: { typedef          double outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case -16: 
+      { typedef unsigned short inMode;
+	switch (outBitpix) {
+	case 8:   { typedef char            outMode; CONVERTDOWN; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTSAME; break; }
+	case -16: { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTUP;   break; }
+	case -32: { typedef          float  outMode; CONVERTUP;   break; }
+	case -64: { typedef          double outMode; CONVERTUP;   break; }
+	}
+      }
+      break;  
+    case 32: 
+      { typedef unsigned int inMode;
+	switch (outBitpix) {
+	case 8:   { typedef char            outMode; CONVERTDOWN; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTSAME; break; }
+	case -32: { typedef          float  outMode; CONVERTSAME; break; }
+	case -64: { typedef          double outMode; CONVERTUP;   break; }
+	}				    
+      }					    
+      break;				    
+    case -32: 				    
+      { typedef float inMode;		    
+	switch (outBitpix) {		    
+	case 8:   { typedef char            outMode; CONVERTDOWN; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTSAME; break; }
+	case -32: { typedef          float  outMode; CONVERTSAME; break; }
+	case -64: { typedef          double outMode; CONVERTUP;   break; }
+	}				    
+      }					    
+      break;				    
+    case -64: 				    
+      { typedef double inMode;		    
+	switch (outBitpix) {		    
+	case 8:   { typedef char            outMode; CONVERTDOWN; break; }
+	case 16:  { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case -16: { typedef unsigned short  outMode; CONVERTDOWN; break; }
+	case 32:  { typedef unsigned int    outMode; CONVERTDOWN; break; }
+	case -32: { typedef          float  outMode; CONVERTDOWN; break; }
+	case -64: { typedef          double outMode; CONVERTSAME; break; }
+	}
+      }
+    }
+  }
+
+  /* zero the padding region */
+  {
+    char *out;
+    int Nextra;
+    
+    out = (char *)matrix[0].buffer;
+    Nextra = matrix[0].size - nbytes;
+    bzero (&out[nbytes], Nextra);
+  }
+
+  return (TRUE);
+}
+ 
+/* WARNING --- this is STILL machine dependant.  It will now work on all 
+   machines, as long as char, int, short, float, double have sizes of 
+   8, 16, 32, 32, 64 bits resp.  However, the floating pt types are NOT
+   universal (or FITS standard), and so can't exchange from machine to 
+   machine. */
+
+/* NOTICE -- This file looks rather strange.  It is set this way to make 
+   all of the possible inter-conversions without too much excess code. 
+   The macros CONVERTUP and CONVERTDOWN need to be distinct.
+   Going to smaller or equal BITPIX, it is safe to run the array forward 
+   and allocate after the conversion.
+   Going to larger BITPIX, you need to reallocate first and run backwards 
+   to avoid wiping out the pixels.
+   OK, to deal with in and out UNSIGN, I have made four if blocks that are
+   almost identical. this looks silly.  any suggestions? 
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_copy_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_copy_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_copy_M.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits copy header ***********************************/
+int gfits_copy_matrix (Matrix *matrix1, Matrix *matrix2) {
+
+  int i;
+
+  matrix2[0].unsign = matrix1[0].unsign;
+  matrix2[0].bitpix = matrix1[0].bitpix;
+  matrix2[0].size   = matrix1[0].size;
+  matrix2[0].bzero  = matrix1[0].bzero;
+  matrix2[0].bscale = matrix1[0].bscale;
+  matrix2[0].Naxes  = matrix1[0].Naxes;
+  for (i = 0; i < FT_MAX_NAXES; i++) 
+    matrix2[0].Naxis[i] = matrix1[0].Naxis[i];
+
+  if (matrix2[0].buffer != NULL) free (matrix2[0].buffer);
+  ALLOCATE (matrix2[0].buffer, char, matrix2[0].size);
+  
+  memcpy (matrix2[0].buffer, matrix1[0].buffer, matrix2[0].size);
+
+  return (TRUE);
+}	
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_create_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_create_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_create_M.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits create matrix *******************************/
+int gfits_create_matrix (header, matrix) 
+Header *header;
+Matrix *matrix;
+{
+
+  int i, Nbytes;
+
+  matrix[0].bitpix = header[0].bitpix;
+  matrix[0].unsign = header[0].unsign;
+  matrix[0].bscale = header[0].bscale;
+  matrix[0].bzero  = header[0].bzero;
+  matrix[0].Naxes  = header[0].Naxes;
+  for (i = 0; i < FT_MAX_NAXES; i++)
+    matrix[0].Naxis[i] = header[0].Naxis[i];
+
+  Nbytes = gfits_data_size (header);
+  ALLOCATE (matrix[0].buffer, char, MAX (Nbytes, 1));
+  bzero (matrix[0].buffer, Nbytes);
+  
+  matrix[0].size = Nbytes;
+  return (TRUE);
+
+}
+
+// XXX free buffer if non-null: need to double check for existing frees
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_divide_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_divide_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_divide_M.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/********************* fits divide matrix ***********************************/
+int gfits_divide_matrix (Matrix *M1, Matrix *M2, Matrix *M3) {
+
+  int i,j;
+  double value, val2;
+
+  if ((M1[0].Naxis[0] != M2[0].Naxis[0]) ||
+      (M1[0].Naxis[1] != M2[0].Naxis[1])) 
+    return (FALSE);
+
+  for (i = 0; i < M1[0].Naxis[0]; i++) {
+    for (j = 0; j < M1[0].Naxis[1]; j++) {
+      value = gfits_get_matrix_value (M1, i, j);
+      val2  = gfits_get_matrix_value (M2, i, j);
+      if (val2 != 0) 
+	value = value / val2;
+      else
+	value = 0.0;
+      gfits_set_matrix_value (M3, i, j, value);
+    }
+  }
+  
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_free_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_free_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_free_M.c	(revision 22322)
@@ -0,0 +1,13 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits free matrix ***********************************/
+void gfits_free_matrix (Matrix *matrix) {
+
+  if (matrix[0].buffer == (char *) NULL) return;
+  free (matrix[0].buffer);
+  matrix[0].buffer = (char *) NULL;
+  
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_get_M_value.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_get_M_value.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_get_M_value.c	(revision 22322)
@@ -0,0 +1,59 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/***************** fits get matrix value ***********************************/
+double gfits_get_matrix_value (Matrix *matrix, int x, int y) {
+
+  double value;
+  int pixel;
+
+  if ((x < 0) || (x >= matrix[0].Naxis[0]) || (y < 0) || (y >= matrix[0].Naxis[1]))
+    return (0.0);
+  pixel = matrix[0].Naxis[0]*y + x;
+  value = HUGE_VAL;
+
+  if (matrix[0].unsign) {
+    switch (matrix[0].bitpix) {
+    case 8:
+      value = *((unsigned char  *) matrix[0].buffer + pixel);
+      break;
+    case 16:
+      value = *((unsigned short *) matrix[0].buffer + pixel);
+      break;
+    case 32:
+      value = *((unsigned int   *) matrix[0].buffer + pixel);
+      break;
+    case -32:
+      value = *((float          *) matrix[0].buffer + pixel);
+      break;
+    case -64:
+      value = *((double         *) matrix[0].buffer + pixel);
+      break;
+    }
+  }
+  else {
+    switch (matrix[0].bitpix) {
+    case 8:
+      value = *((char  *) matrix[0].buffer + pixel);
+      break;
+    case 16:
+      value = *((short *) matrix[0].buffer + pixel);
+      break;
+    case 32:
+      value = *((int   *) matrix[0].buffer + pixel);
+      break;
+    case -32:
+      value = *((float          *) matrix[0].buffer + pixel);
+      break;
+    case -64:
+      value = *((double         *) matrix[0].buffer + pixel);
+      break;
+    }
+  }
+
+  value = matrix[0].bscale*value + matrix[0].bzero;
+  return (value);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_insert_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_insert_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_insert_M.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits insert array ***********************************/
+void gfits_insert_matrix (Matrix *matrix, Matrix *array, int x, int y) {
+
+  /* there is no check here to match BITPIX, BZERO, or BSCALE */
+
+  int i, start_m, Nbytes_m, Nbytes;
+
+  if ((x + array[0].Naxis[0] > matrix[0].Naxis[0]) ||
+      (y + array[0].Naxis[1] > matrix[0].Naxis[1])) {
+    fprintf (stderr, "can't insert array here: (%d,%d) - (%d,%d) vs (%d,%d)\n",
+	     x, y, x + array[0].Naxis[0], y + array[0].Naxis[1],
+	     matrix[0].Naxis[0], matrix[0].Naxis[1]);
+    return;
+  }
+
+  Nbytes = array[0].Naxis[0]*abs(array[0].bitpix) / 8;
+  Nbytes_m = matrix[0].Naxis[0]*abs(matrix[0].bitpix) / 8;
+  start_m = y*Nbytes_m + x*abs(matrix[0].bitpix) / 8;
+
+  for (i = 0; i < array[0].Naxis[1]; i++) {
+    memmove (&matrix[0].buffer[start_m + i*Nbytes_m], 
+	     &array[0].buffer[i*Nbytes], Nbytes);
+  }
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_load_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_load_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_load_M.c	(revision 22322)
@@ -0,0 +1,103 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+int gfits_fread_matrix (FILE *f, Matrix *matrix, Header *header) {
+  
+  int status;
+  
+  status = gfits_load_matrix (f, matrix, header);
+  return (status);
+}
+
+/*********************** fits read matrix ***********************************/
+int gfits_load_matrix (FILE *f, Matrix *matrix, Header *header) {
+
+  int i, nbytes, Nbytes;
+
+  matrix[0].bitpix = header[0].bitpix;
+  matrix[0].unsign = header[0].unsign;
+  matrix[0].bscale = header[0].bscale;
+  matrix[0].bzero  = header[0].bzero;
+  matrix[0].Naxes  = header[0].Naxes;
+  for (i = 0; i < FT_MAX_NAXES; i++)
+    matrix[0].Naxis[i] = header[0].Naxis[i];
+
+  Nbytes = gfits_data_size (header);
+  ALLOCATE (matrix[0].buffer, char, Nbytes);
+  matrix[0].size = Nbytes;
+
+  nbytes = fread (matrix[0].buffer, sizeof(char), Nbytes, f);
+  if (nbytes != Nbytes) {
+    perror ("FITS file is short in ##__func__");
+    if (nbytes != gfits_data_min_size (header)) {
+      fprintf (stderr, "error: fits read error in %s", __func__);
+      return (FALSE);
+    }
+    fprintf (stderr, "warning: file missing pad\n");
+  }
+
+# ifdef BYTE_SWAP  
+ {
+  int perpix;
+  unsigned char *byte0, *byte1, *byte2, *byte3, *byte4, *byte5, *byte6, *byte7, tmp;
+
+  perpix = abs(header[0].bitpix) / 8;
+  if (perpix > 1) {
+    byte0 = (unsigned char *) matrix[0].buffer;
+    byte1 = (unsigned char *) matrix[0].buffer + 1;
+    byte2 = (unsigned char *) matrix[0].buffer + 2;
+    byte3 = (unsigned char *) matrix[0].buffer + 3;
+    byte4 = (unsigned char *) matrix[0].buffer + 4;
+    byte5 = (unsigned char *) matrix[0].buffer + 5;
+    byte6 = (unsigned char *) matrix[0].buffer + 6;
+    byte7 = (unsigned char *) matrix[0].buffer + 7;
+    if (perpix == 2) {
+      for (i = 0; i < nbytes; i+=2, byte0 += 2, byte1 += 2) {
+	tmp = *byte0;
+	*byte0 = *byte1;
+	*byte1 = tmp;
+      }
+    }
+    if (perpix == 4) {
+      for (i = 0; i < nbytes; i+=4, byte0 += 4, byte1 += 4, byte2 += 4, byte3 += 4) {
+	tmp = *byte0;
+	*byte0 = *byte3;
+	*byte3 = tmp;
+	tmp = *byte1;
+	*byte1 = *byte2;
+	*byte2 = tmp;
+      }
+    }
+    if (perpix == 8) {
+      for (i = 0; i < nbytes; i+=8, byte0 += 8, byte1 += 8, byte2 += 8, byte3 += 8, byte4 += 8, byte5 += 8, byte6 += 8, byte7 += 8) {
+	tmp = *byte0;
+	*byte0 = *byte7;
+	*byte7 = tmp;
+	tmp = *byte1;
+	*byte1 = *byte6;
+	*byte6 = tmp;
+	tmp = *byte1;
+	*byte2 = *byte5;
+	*byte5 = tmp;
+	tmp = *byte1;
+	*byte3 = *byte4;
+	*byte4 = tmp;
+      }
+    }
+  }
+ }
+# endif
+
+  if (nbytes < Nbytes - 2880) {  /* this is a bad FITS error: image is not OK */
+    fprintf (stderr, "error reading in matrix data from FITS file\n");
+    return (FALSE);
+  }
+  if (nbytes != Nbytes) {  /* this is a FITS error, but often the image is OK */
+    fprintf (stderr, "incomplete block in FITS file: (%d, %d)\n", nbytes, Nbytes);
+    return (TRUE); 
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_matrix.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_matrix.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_matrix.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+int gfits_data_size (Header *header) {
+  
+  int i, Nrec, size;
+
+  if (header[0].Naxes == 0) return (0);
+
+  size = abs(header[0].bitpix / 8);
+
+  for (i = 0; i < header[0].Naxes; i++)
+    size *= header[0].Naxis[i];
+
+  // XXX do I multiply this times gcount?
+  size += header[0].pcount;
+
+  /* round up to next complete block */
+  if (size % FT_RECORD_SIZE) {
+    Nrec = 1 + (int) (size / FT_RECORD_SIZE);
+    size = FT_RECORD_SIZE * Nrec;
+  }
+
+  return (size);
+}
+
+int gfits_data_min_size (Header *header) {
+  
+  int i, size;
+
+  if (header[0].Naxes == 0) return (0);
+
+  size = abs(header[0].bitpix / 8);
+
+  for (i = 0; i < header[0].Naxes; i++)
+    size *= header[0].Naxis[i];
+
+  // XXX do I multiply this times gcount?
+  size += header[0].pcount;
+
+  return (size);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_multiply_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_multiply_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_multiply_M.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/******************* fits multiply matrix ***********************************/
+int gfits_multiply_matrix (Matrix *M1, Matrix *M2, Matrix *M3) {
+
+  int i,j;
+  double value;
+
+  if ((M1[0].Naxis[0] != M2[0].Naxis[0]) ||
+      (M1[0].Naxis[1] != M2[0].Naxis[1])) 
+    return (FALSE);
+
+  for (i = 0; i < M1[0].Naxis[0]; i++) {
+    for (j = 0; j < M1[0].Naxis[1]; j++) {
+      value = gfits_get_matrix_value (M1, i, j);
+      value *= gfits_get_matrix_value (M2, i, j);
+      gfits_set_matrix_value (M3, i, j, value);
+    }
+  }
+  
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_read_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_read_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_read_M.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits read matrix ***********************************/
+int gfits_read_matrix (char *filename, Matrix *matrix) {
+
+  FILE *f;
+  Header header;
+  int status;
+
+  status = gfits_read_header (filename, &header);
+  if (!status) {
+    fprintf (stderr, "error reading header of FITS file %s\n", filename);
+    return (FALSE);
+  }
+
+  f = fopen (filename, "r");
+  if (f == NULL) return (FALSE);
+
+  fseek (f, header.size, 0);
+  
+  status = gfits_load_matrix (f, matrix, &header);
+
+  fclose (f);
+
+  return (status);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_read_portion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_read_portion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_read_portion.c	(revision 22322)
@@ -0,0 +1,119 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# ifndef SEEK_CUR 
+# define SEEK_SET 0   
+# define SEEK_CUR 1   
+# define SEEK_END 2
+# endif
+
+/*********************** fits read matrix ***********************************/
+/** the result of this function is a matrix with dimension Npix * 1 **/
+int gfits_read_portion (char *filename, Matrix *matrix, int Nskip, int Npix) {
+
+  FILE *f;
+  Header header;
+  int status, nbytes, Nbytes, Nrec;
+
+  status = gfits_read_header (filename, &header);
+  if (!status) {
+    fprintf (stderr, "error reading header of FITS file %s\n", filename);
+    return (FALSE);
+  }
+
+  matrix[0].bitpix = header.bitpix;
+  matrix[0].unsign = header.unsign;
+  matrix[0].bscale = header.bscale;
+  matrix[0].bzero  = header.bzero;
+  matrix[0].Naxes  = 2;
+  matrix[0].Naxis[0] = Npix;
+  matrix[0].Naxis[1] = 1;
+
+  Nbytes = Npix  * abs (matrix[0].bitpix)/8;
+  Nskip  = Nskip * abs (matrix[0].bitpix)/8;
+
+  /* round up to next complete block */
+  if (Nbytes % FT_RECORD_SIZE) {
+    Nrec = 1 + (int) (Nbytes / FT_RECORD_SIZE);
+    Nbytes = FT_RECORD_SIZE * Nrec;
+  }
+  matrix[0].size = Nbytes;
+  ALLOCATE (matrix[0].buffer, char, Nbytes);
+
+  /* open file, read data, close file */
+  f = fopen (filename, "r");
+  if (f == NULL) return (FALSE);
+  fseek (f, header.size + Nskip, SEEK_SET);
+  nbytes = fread (matrix[0].buffer, sizeof(char), Nbytes, f);
+  if (nbytes != Nbytes) {
+    perror ("fits matrix read error");
+  }
+
+  fclose (f);
+
+# ifdef BYTE_SWAP  
+ {
+  int i, perpix;
+  unsigned char *byte0, *byte1, *byte2, *byte3, *byte4, *byte5, *byte6, *byte7, ctmp;
+
+  perpix = abs(header.bitpix) / 8;
+  if (perpix > 1) {
+    byte0 = (unsigned char *) matrix[0].buffer;
+    byte1 = (unsigned char *) matrix[0].buffer + 1;
+    byte2 = (unsigned char *) matrix[0].buffer + 2;
+    byte3 = (unsigned char *) matrix[0].buffer + 3;
+    byte4 = (unsigned char *) matrix[0].buffer + 4;
+    byte5 = (unsigned char *) matrix[0].buffer + 5;
+    byte6 = (unsigned char *) matrix[0].buffer + 6;
+    byte7 = (unsigned char *) matrix[0].buffer + 7;
+    if (perpix == 2) {
+      for (i = 0; i < nbytes; i+=2, byte0 += 2, byte1 += 2) {
+	ctmp = *byte0;
+	*byte0 = *byte1;
+	*byte1 = ctmp;
+      }
+    }
+    if (perpix == 4) {
+      for (i = 0; i < nbytes; i+=4, byte0 += 4, byte1 += 4, byte2 += 4, byte3 += 4) {
+	ctmp = *byte0;
+	*byte0 = *byte3;
+	*byte3 = ctmp;
+	ctmp = *byte1;
+	*byte1 = *byte2;
+	*byte2 = ctmp;
+      }
+    }
+    if (perpix == 8) {
+      for (i = 0; i < nbytes; i+=8, byte0 += 8, byte1 += 8, byte2 += 8, byte3 += 8, byte4 += 8, byte5 += 8, byte6 += 8, byte7 += 8) {
+	ctmp = *byte0;
+	*byte0 = *byte7;
+	*byte7 = ctmp;
+	ctmp = *byte1;
+	*byte1 = *byte6;
+	*byte6 = ctmp;
+	ctmp = *byte1;
+	*byte2 = *byte5;
+	*byte5 = ctmp;
+	ctmp = *byte1;
+	*byte3 = *byte4;
+	*byte4 = ctmp;
+      }
+    }
+  }
+ }
+# endif
+
+  gfits_free_header (&header);
+
+  if (nbytes < Nbytes - 2880) {  /* this is a bad FITS error: image is not OK */
+    fprintf (stderr, "error reading in matrix data from FITS file %s (%d < %d - 2880)\n", filename, nbytes, Nbytes);
+    return (FALSE);
+  }
+  if (nbytes != Nbytes) {  /* this is a FITS error, but often the image is OK */
+    fprintf (stderr, "incomplete block in %s: (%d, %d)\n", filename, nbytes, Nbytes);
+    return (TRUE); 
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_read_segment.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_read_segment.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_read_segment.c	(revision 22322)
@@ -0,0 +1,159 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits read matrix ***********************************/
+int gfits_read_matrix_segment (char *filename, Matrix *matrix, char *region) {
+
+  FILE *f;
+  Header header;
+  int status;
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    return (FALSE);
+  }
+
+  status = gfits_fread_header (f, &header);
+  if (!status) {
+    fclose (f);
+    fprintf (stderr, "error reading header of FITS file %s\n", filename);
+    return (FALSE);
+  }
+
+  status = gfits_fread_matrix_segment (f, matrix, &header, region);
+  if (!status) {
+    fclose (f);
+    fprintf (stderr, "error reading header of FITS file %s\n", filename);
+    return (FALSE);
+  }
+
+  fclose (f);
+  return (TRUE);
+}
+
+// read header before calling this function
+// file pointer should be at start of Matrix data segment
+int gfits_fread_matrix_segment (FILE *f, Matrix *matrix, Header *header, char *region) {
+
+  int status, i, nbytes, Nbytes, Nskip, NbytesData;
+  int wantaxis[FT_MAX_NAXES][2];
+  double tmp;
+
+  for (i = 0; i < FT_MAX_NAXES; i++) {
+    wantaxis[i][0] = wantaxis[i][1] = 0;
+  }
+
+  for (i = 0; i < header[0].Naxes; i++) {
+    status = dparse (&tmp, 2*i + 1, region);
+    if (!status || (tmp < 0))
+      wantaxis[i][0] = 0;
+    else 
+      wantaxis[i][0] = (tmp > header[0].Naxis[i] - 1) ? header[0].Naxis[i] - 1 : tmp;
+    status = dparse (&tmp, 2*i + 2, region);
+    if (!status || (tmp < 0))
+      wantaxis[i][1] = header[0].Naxis[i];
+    else 
+      wantaxis[i][1] = (tmp > header[0].Naxis[i]) ? header[0].Naxis[i] : tmp;
+  }
+
+  matrix[0].bitpix = header[0].bitpix;
+  matrix[0].unsign = header[0].unsign;
+  matrix[0].bscale = header[0].bscale;
+  matrix[0].bzero  = header[0].bzero;
+  matrix[0].Naxes  = header[0].Naxes;
+  for (i = 0; i < FT_MAX_NAXES; i++)
+    matrix[0].Naxis[i] = wantaxis[i][1] - wantaxis[i][0];
+
+  matrix[0].size = abs (matrix[0].bitpix) / 8;
+  for (i = 0; i < matrix[0].Naxes; i++)
+    matrix[0].size *= matrix[0].Naxis[i];
+
+  Nskip = wantaxis[2][0]*matrix[0].Naxis[0]*matrix[0].Naxis[1]*abs(matrix[0].bitpix)/8;
+  NbytesData = Nskip + matrix[0].size;
+
+  if (NbytesData % FT_RECORD_SIZE) 
+    Nbytes = FT_RECORD_SIZE * ((int) (NbytesData / FT_RECORD_SIZE) + 1) - Nskip;
+  else 
+    Nbytes = FT_RECORD_SIZE * ((int) (NbytesData / FT_RECORD_SIZE)) - Nskip;
+
+  ALLOCATE (matrix[0].buffer, char, MAX (Nbytes, 1));
+
+# ifndef SEEK_CUR 
+# define SEEK_SET 0   
+# define SEEK_CUR 1   
+# define SEEK_END 2
+# endif
+
+  /* currently only good for reading in full planes in 3-D */
+  fseek (f, Nskip, SEEK_CUR);
+  nbytes = fread (matrix[0].buffer, sizeof(char), Nbytes, f);
+  if (nbytes != Nbytes) {
+    perror ("fits matrix read error");
+  }
+
+  matrix[0].size = Nbytes;
+
+# ifdef BYTE_SWAP  
+ {
+  unsigned char *byte0, *byte1, *byte2, *byte3, *byte4, *byte5, *byte6, *byte7, ctmp;
+  int perpix;
+
+  perpix = abs(header[0].bitpix) / 8;
+  if (perpix > 1) {
+    byte0 = (unsigned char *) matrix[0].buffer;
+    byte1 = (unsigned char *) matrix[0].buffer + 1;
+    byte2 = (unsigned char *) matrix[0].buffer + 2;
+    byte3 = (unsigned char *) matrix[0].buffer + 3;
+    byte4 = (unsigned char *) matrix[0].buffer + 4;
+    byte5 = (unsigned char *) matrix[0].buffer + 5;
+    byte6 = (unsigned char *) matrix[0].buffer + 6;
+    byte7 = (unsigned char *) matrix[0].buffer + 7;
+    if (perpix == 2) {
+      for (i = 0; i < nbytes; i+=2, byte0 += 2, byte1 += 2) {
+	ctmp = *byte0;
+	*byte0 = *byte1;
+	*byte1 = ctmp;
+      }
+    }
+    if (perpix == 4) {
+      for (i = 0; i < nbytes; i+=4, byte0 += 4, byte1 += 4, byte2 += 4, byte3 += 4) {
+	ctmp = *byte0;
+	*byte0 = *byte3;
+	*byte3 = ctmp;
+	ctmp = *byte1;
+	*byte1 = *byte2;
+	*byte2 = ctmp;
+      }
+    }
+    if (perpix == 8) {
+      for (i = 0; i < nbytes; i+=8, byte0 += 8, byte1 += 8, byte2 += 8, byte3 += 8, byte4 += 8, byte5 += 8, byte6 += 8, byte7 += 8) {
+	ctmp = *byte0;
+	*byte0 = *byte7;
+	*byte7 = ctmp;
+	ctmp = *byte1;
+	*byte1 = *byte6;
+	*byte6 = ctmp;
+	ctmp = *byte1;
+	*byte2 = *byte5;
+	*byte5 = ctmp;
+	ctmp = *byte1;
+	*byte3 = *byte4;
+	*byte4 = ctmp;
+      }
+    }
+  }
+ }
+# endif
+
+  if (nbytes < Nbytes - 2880) {  /* this is a bad FITS error: image is not OK */
+    fprintf (stderr, "error reading in matrix data from FITS file (%d < %d - 2880)\n", nbytes, Nbytes);
+    return (FALSE);
+  }
+  if (nbytes != Nbytes) {  /* this is a FITS error, but often the image is OK */
+    fprintf (stderr, "incomplete block in FITS file: (%d, %d)\n", nbytes, Nbytes);
+    return (TRUE); 
+  }
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_set_M_value.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_set_M_value.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_set_M_value.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/****************** fits set matrix value ***********************************/
+void gfits_set_matrix_value (Matrix *matrix, int x, int y, double Value) {
+
+  int pixel;
+  double value;
+
+  pixel = matrix[0].Naxis[0]*y + x;
+  value = (Value - matrix[0].bzero) / matrix[0].bscale;
+
+  if (matrix[0].unsign) {
+    switch (matrix[0].bitpix) {
+    case 8:
+      *((char  *) matrix[0].buffer + pixel) = value;
+      break;
+    case 16:
+      *((short *) matrix[0].buffer + pixel) = value;
+      break;
+    case 32:
+      *((int   *) matrix[0].buffer + pixel) = value;
+      break;
+    case -32:
+      *((float          *) matrix[0].buffer + pixel) = value;
+      break;
+    case -64:
+      *((double         *) matrix[0].buffer + pixel) = value;
+      break;
+    }
+  }
+  else {
+    switch (matrix[0].bitpix) {
+    case 8:
+      *((unsigned char  *) matrix[0].buffer + pixel) = value;
+      break;
+    case 16:
+      *((unsigned short *) matrix[0].buffer + pixel) = value;
+      break;
+    case 32:
+      *((unsigned int   *) matrix[0].buffer + pixel) = value;
+      break;
+    case -32:
+      *((float          *) matrix[0].buffer + pixel) = value;
+      break;
+    case -64:
+      *((double         *) matrix[0].buffer + pixel) = value;
+      break;
+    }
+  }
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_uncompress_data.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_uncompress_data.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_uncompress_data.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# include <zlib.h>
+
+/* functions defined in ricecomp.c */
+int fits_rcomp(int a[], int nx,	unsigned char *c, int clen, int nblock);
+int fits_rdecomp (unsigned char *c, int clen, unsigned int array[], int nx, int nblock);
+int fits_rdecomp_short (unsigned char *c, int clen, unsigned short array[], int nx, int nblock);
+int fits_rdecomp_byte (unsigned char *c, int clen, unsigned char array[], int nx, int nblock);
+
+/* functions defined in fits_hcompress.c */
+# define LONGLONG long long
+int fits_hcompress(int *a, int ny, int nx, int scale, char *output, long *nbytes, int *status);
+int fits_hcompress64(LONGLONG *a, int ny, int nx, int scale, char *output, long *nbytes, int *status);
+
+/* functions defined in fits_hdeccompress.c */
+int fits_hdecompress(unsigned char *input, int smooth, int *a, int *ny, int *nx, int *scale, int *status);
+int fits_hdecompress64(unsigned char *input, int smooth, LONGLONG *a, int *ny, int *nx, int *scale, int *status);
+
+/* functions defined in pliocomp.c */
+int pl_p2li (int *pxsrc, int xs, short *lldst, int npix);
+int pl_l2pi (short *ll_src, int xs, int *px_dst, int npix);
+
+int gfits_uncompress_data (char *zdata, int Nzdata, char *cmptype, char **optname, char **optvalue, int Nopt, char *outdata, int *Nout, int out_pixsize) {
+
+  int status;
+  static int Ninsum = 0;
+  static int Noutsum = 0;
+
+  if (!strcasecmp(cmptype, "GZIP_1")) {
+    unsigned long tNout = *Nout * out_pixsize;
+    // uncompress does not require us to know the expected number of pixel; it tells us the number
+    status = uncompress ((Bytef *) outdata, &tNout, (Bytef *) zdata, Nzdata);
+    if (status != Z_OK) {
+      fprintf (stderr, "error in uncompress (GZIP)\n");
+      return (FALSE);
+    }
+    *Nout = tNout;
+    return (TRUE);
+  }
+
+  if (!strcasecmp(cmptype, "RICE_1")) {
+    int i, blocksize;
+    // look for the BLOCKSIZE
+    blocksize = 32;
+    for (i = 0; i < Nopt; i++) {
+      if (!strcmp(optname[i], "BLOCKSIZE")) {
+	blocksize = atoi (optvalue[i]);
+	if ((blocksize != 16) && (blocksize != 32)) {
+	  fprintf (stderr, "RICE blocksize is not valid: %d (%s = %s)\n", blocksize, optname[i], optvalue[i]);
+	  return (FALSE);
+	}
+      }
+    }
+
+    int Npix;
+    // Npix = *Nout * (out_pixsize / 4.0);
+    Npix = *Nout;
+
+    Ninsum += Nzdata;
+    Noutsum += *Nout;
+    // fprintf (stderr, "%d comp bytes; %d uncomp 'pixels', totals: %d %d\n", Nzdata, Npix, Ninsum, Noutsum);
+
+    switch (out_pixsize) {
+      case 4:
+	// rice decompression from the CFITSIO source tree : we need to tell it the expected number of pixels
+	// is also REQUIRES 4byte output, which is fairly stupid.
+	if (fits_rdecomp ((unsigned char *) zdata, Nzdata, (unsigned int *) outdata, Npix, blocksize)) {
+	  fprintf (stderr, "error in rice decompression\n");
+	  return (FALSE);
+	}
+	return (TRUE);
+
+      case 2:
+	if (fits_rdecomp_short ((unsigned char *) zdata, Nzdata, (unsigned short *) outdata, Npix, blocksize)) {
+	  fprintf (stderr, "error in rice decompression\n");
+	  return (FALSE);
+	}
+	return (TRUE);
+
+      case 1:
+	if (fits_rdecomp_byte ((unsigned char *) zdata, Nzdata, (unsigned char *) outdata, Npix, blocksize)) {
+	  fprintf (stderr, "error in rice decompression\n");
+	  return (FALSE);
+	}
+	return (TRUE);
+	
+      default:
+	fprintf (stderr, "invalid output pixel size %d\n", out_pixsize);
+	return (FALSE);
+    }
+    
+  }
+  
+  if (!strcasecmp(cmptype, "PLIO_1")) {
+    int Npix;
+    Npix = pl_l2pi ((short *) zdata, 1, (int *) outdata, *Nout);
+    if (Npix != *Nout) {
+      fprintf (stderr, "error in plio decompression\n");
+      return (FALSE);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp(cmptype, "HCOMPRESS_1")) {
+    int Nx, Ny, scale;
+    status = 0;
+    // call hdecompress without smoothing
+    fits_hdecompress ((unsigned char *) zdata, FALSE, (int *) outdata, &Nx, &Ny, &scale, &status);
+    if (status) {
+      fprintf (stderr, "error in hdecompress\n");
+      return (FALSE);
+    }
+    // fprintf (stderr, "decompression yields image %d x %d (scale: %d)\n", Nx, Ny, scale);
+    
+    if (Nx * Ny != *Nout) {
+      fprintf (stderr, "error in hdecompress: mismatched output size\n");
+      return (FALSE);
+    }
+    return (TRUE);
+  }
+
+  fprintf (stderr, "unknown compression %s\n", cmptype);
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_write_M.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_write_M.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/matrix/F_write_M.c	(revision 22322)
@@ -0,0 +1,152 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits write matrix ***********************************/
+int gfits_write_matrix (char *filename, Matrix *matrix) {
+
+  FILE *f;
+  int status;
+
+  f = fopen (filename, "a");
+  if (f == NULL) return (FALSE);
+  
+  fseek (f, 0, SEEK_END);  /* write matrix to end of file! */
+
+  status = gfits_fwrite_matrix (f, matrix);
+  fclose (f);
+  return (status);
+}
+
+/*********************** fits write matrix ***********************************/
+int gfits_fwrite_matrix (FILE *f, Matrix *matrix) {
+
+  int status, nbytes, Nbytes;
+
+  status = TRUE;
+
+  /* this is a bit dangerous: we are not realloc'ing the matrix.
+     if the size is wrong, it probably should simply pad the fwrite statement.
+     better policy should be defined (ie, matrix.size always correct blocking?)
+  */
+
+  if (matrix[0].size % FT_RECORD_SIZE) 
+    nbytes = FT_RECORD_SIZE * ((int) (matrix[0].size / FT_RECORD_SIZE) + 1);
+  else 
+    nbytes = FT_RECORD_SIZE * ((int) (matrix[0].size / FT_RECORD_SIZE));
+
+  /* this is a bit cumbersome: swap all words, write out file, then swap back... */
+# ifdef BYTE_SWAP  
+  { 
+    int i;
+    unsigned char *byte0, *byte1, *byte2, *byte3, *byte4, *byte5, *byte6, *byte7, tmp;
+    int perpix;
+
+    perpix = abs(matrix[0].bitpix) / 8;
+    if (perpix > 1) {
+      byte0 = (unsigned char *) matrix[0].buffer;
+      byte1 = (unsigned char *) matrix[0].buffer + 1;
+      byte2 = (unsigned char *) matrix[0].buffer + 2;
+      byte3 = (unsigned char *) matrix[0].buffer + 3;
+      byte4 = (unsigned char *) matrix[0].buffer + 4;
+      byte5 = (unsigned char *) matrix[0].buffer + 5;
+      byte6 = (unsigned char *) matrix[0].buffer + 6;
+      byte7 = (unsigned char *) matrix[0].buffer + 7;
+      if (perpix == 2) {
+	for (i = 0; i < nbytes; i+=2, byte0 += 2, byte1 += 2) {
+	  tmp = *byte0;
+	  *byte0 = *byte1;
+	  *byte1 = tmp;
+	}
+      }
+      if (perpix == 4) {
+	for (i = 0; i < nbytes; i+=4, byte0 += 4, byte1 += 4, byte2 += 4, byte3 += 4) {
+	  tmp = *byte0;
+	  *byte0 = *byte3;
+	  *byte3 = tmp;
+	  tmp = *byte1;
+	  *byte1 = *byte2;
+	  *byte2 = tmp;
+	}
+      }
+      if (perpix == 8) {
+	for (i = 0; i < nbytes; i+=8, byte0 += 8, byte1 += 8, byte2 += 8, byte3 += 8, byte4 += 8, byte5 += 8, byte6 += 8, byte7 += 8) {
+	  tmp = *byte0;
+	  *byte0 = *byte7;
+	  *byte7 = tmp;
+	  tmp = *byte1;
+	  *byte1 = *byte6;
+	  *byte6 = tmp;
+	  tmp = *byte1;
+	  *byte2 = *byte5;
+	  *byte5 = tmp;
+	  tmp = *byte1;
+	  *byte3 = *byte4;
+	  *byte4 = tmp;
+	}
+      }
+    }
+  }
+# endif
+
+  Nbytes = fwrite (matrix[0].buffer, sizeof(char), nbytes, f);
+  if (Nbytes != nbytes) {
+    status = FALSE;
+  }
+
+# ifdef BYTE_SWAP 
+  {
+    int i;
+    unsigned char *byte0, *byte1, *byte2, *byte3, *byte4, *byte5, *byte6, *byte7, tmp;
+    int perpix;
+
+    perpix = abs(matrix[0].bitpix) / 8;
+    if (perpix > 1) {
+      byte0 = (unsigned char *) matrix[0].buffer;
+      byte1 = (unsigned char *) matrix[0].buffer + 1;
+      byte2 = (unsigned char *) matrix[0].buffer + 2;
+      byte3 = (unsigned char *) matrix[0].buffer + 3;
+      byte4 = (unsigned char *) matrix[0].buffer + 4;
+      byte5 = (unsigned char *) matrix[0].buffer + 5;
+      byte6 = (unsigned char *) matrix[0].buffer + 6;
+      byte7 = (unsigned char *) matrix[0].buffer + 7;
+      if (perpix == 2) {
+	for (i = 0; i < nbytes; i+=2, byte0 += 2, byte1 += 2) {
+	  tmp = *byte0;
+	  *byte0 = *byte1;
+	  *byte1 = tmp;
+	}
+      }
+      if (perpix == 4) {
+	for (i = 0; i < nbytes; i+=4, byte0 += 4, byte1 += 4, byte2 += 4, byte3 += 4) {
+	  tmp = *byte0;
+	  *byte0 = *byte3;
+	  *byte3 = tmp;
+	  tmp = *byte1;
+	  *byte1 = *byte2;
+	  *byte2 = tmp;
+	}
+      }
+      if (perpix == 8) {
+	for (i = 0; i < nbytes; i+=8, byte0 += 8, byte1 += 8, byte2 += 8, byte3 += 8, byte4 += 8, byte5 += 8, byte6 += 8, byte7 += 8) {
+	  tmp = *byte0;
+	  *byte0 = *byte7;
+	  *byte7 = tmp;
+	  tmp = *byte1;
+	  *byte1 = *byte6;
+	  *byte6 = tmp;
+	  tmp = *byte1;
+	  *byte2 = *byte5;
+	  *byte5 = tmp;
+	  tmp = *byte1;
+	  *byte3 = *byte4;
+	  *byte4 = tmp;
+	}
+      }
+    }
+  }
+# endif
+
+  return (status);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_create_T.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_create_T.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_create_T.c	(revision 22322)
@@ -0,0 +1,51 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits create table *******************************/
+int gfits_create_table (Header *header, FTable *table) {
+
+  int Nbytes;
+  char type[80];
+
+  gfits_scan (header, "XTENSION", "%s", 1, type);
+
+  table[0].header = header;
+  
+  Nbytes = gfits_data_size (header);
+  ALLOCATE (table[0].buffer, char, MAX (Nbytes, 1));
+  if (!strcmp (type, "TABLE")) {
+    memset (table[0].buffer, ' ', Nbytes);
+  } else {
+    memset (table[0].buffer, 0, Nbytes);
+  }
+  table[0].size = Nbytes;
+  return (TRUE);
+
+}
+
+/* init table structure, allocate space for data array (min 1 byte for 0 length) */
+
+/*********************** fits create table *******************************/
+int gfits_create_vtable (Header *header, VTable *table, int Nrow) {
+
+  int i, Nx, Ny;
+  char type[80];
+
+  gfits_scan (header, "XTENSION", "%s", 1, type);
+
+  table[0].header = header;
+  Nx = table[0].header[0].Naxis[0];
+  Ny = table[0].header[0].Naxis[0];
+  table[0].size = gfits_data_size (header);
+  table[0].pad = table[0].size - Nx*Ny;
+ 
+  table[0].Nrow = Nrow;
+  ALLOCATE (table[0].buffer, char *, MAX (Nrow, 1));
+  ALLOCATE (table[0].row, int, MAX (Nrow, 1));
+  for (i = 0; i < Nrow; i++) {
+    ALLOCATE (table[0].buffer[i], char, Nx);
+  }
+  return (TRUE);
+}
+
+/* init table structure, allocate space for data array (min 1 byte for 0 length) */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_create_TH.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_create_TH.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_create_TH.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# define NBYTES 2880
+
+/* a basic table header (extension) is different from a primary header
+   in that it has the word XTENSION and a type for the first line,
+   instead of SIMPLE */
+
+/*********************** fits create Theader *********************************/
+int gfits_create_Theader (Header *header, char *type) {
+  
+  int i;
+  char axis[10];
+  
+  header[0].size = NBYTES;
+
+  ALLOCATE (header[0].buffer, char, NBYTES);
+  
+  for (i = 0; i < NBYTES; i++) 
+  header[0].buffer[i] = ' ';
+  strncpy (header[0].buffer, "END", 3);
+  
+  gfits_modify (header, "XTENSION", "%s", 1, type);
+  gfits_modify (header, "BITPIX",   "%d", 1, header[0].bitpix);
+  gfits_modify (header, "NAXIS",    "%d", 1, header[0].Naxes);
+  
+  for (i = 0; i < header[0].Naxes; i++) {
+    sprintf (axis, "NAXIS%d", i + 1);
+    gfits_modify (header,  axis, "%d", 1, header[0].Naxis[i], 1);
+  }
+  
+  gfits_modify (header, "PCOUNT", "%d", 1, header[0].pcount);
+  gfits_modify (header, "GCOUNT", "%d", 1, header[0].gcount);
+  if (!strcmp (type, "IMAGE")) {
+    gfits_modify (header, "BSCALE", "%lf", 1, header[0].bscale);
+    gfits_modify (header, "BZERO",  "%lf", 1, header[0].bzero);
+  }
+
+  return (TRUE);
+}	
+
+/*********************** fits create table header *********************************/
+int gfits_create_table_header (Header *header, char *type, char *extname) {
+  
+  int i, valid;
+
+  /* check valid table types */
+  valid = FALSE;
+  valid |= !strcmp (type, "TABLE");
+  valid |= !strcmp (type, "BINTABLE");
+  if (!valid) return (FALSE);
+
+  gfits_init_header (header);
+
+  header[0].simple = FALSE;
+  header[0].Naxes  = 2;
+  for (i = 0; i < FT_MAX_NAXES; i++)
+    header[0].Naxis[i] = 0;
+
+  gfits_create_Theader (header, type);
+
+  gfits_modify (header, "TFIELDS", "%d", 1, 0);
+  gfits_modify (header, "EXTNAME", "%s", 1, extname);
+  
+  return (TRUE);
+}	
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_define_column.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_define_column.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_define_column.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/***********************/
+int gfits_define_bintable_column (Header *header, char *format, char *label, char *comment, char *unit, double bscale, double bzero) {
+
+  int Nfields, Nbytes, Nval, Naxis1;
+  char type[16], field[16];
+  
+  if (!gfits_bintable_format (format, type, &Nval, &Nbytes)) return (FALSE);
+  
+  Nfields = 0;
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+  gfits_scan (header, "NAXIS1",  "%d", 1, &Naxis1);
+  Nfields ++;
+  Naxis1 += Nbytes*Nval;
+
+  sprintf (field, "TTYPE%d", Nfields);
+  gfits_modify (header, field, "%s", 1, label);
+  gfits_modify (header, field, "%C", 1, comment);
+  sprintf (field, "TUNIT%d", Nfields);
+  gfits_modify (header, field, "%s", 1, unit);
+  sprintf (field, "TFORM%d", Nfields);
+  gfits_modify (header, field, "%s", 1, format);
+  sprintf (field, "TSCAL%d", Nfields);
+  gfits_modify (header, field, "%lf", 1, bscale);
+  sprintf (field, "TZERO%d", Nfields);
+  gfits_modify (header, field, "%lf", 1, bzero);
+
+  /* update TFIELDS & NAXIS1 */
+  gfits_modify (header, "TFIELDS", "%d", 1, Nfields);
+  gfits_modify (header, "NAXIS1",  "%d", 1, Naxis1);
+  header[0].Naxis[0] = Naxis1;
+
+  return (TRUE);
+}
+
+/***********************/
+int gfits_define_table_column (Header *header, char *format, char *label, char *comment, char *unit) {
+
+  int Nstart, Nfields, Nbytes, Nval, Naxis1;
+  char type[16], field[16], cformat[16];
+
+  strcpy (cformat, format);
+  if (!gfits_table_format (cformat, type, &Nval, &Nbytes)) return (FALSE);
+
+  Nfields = 0;
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+  gfits_scan (header, "NAXIS1",  "%d", 1, &Naxis1);
+  Nstart = Naxis1 + 1;
+  Nfields ++;
+  Naxis1 += Nbytes*Nval;
+
+  sprintf (field, "TTYPE%d", Nfields);
+  gfits_modify (header, field, "%s", 1, label);
+  gfits_modify (header, field, "%C", 1, comment);
+  sprintf (field, "TUNIT%d", Nfields);
+  gfits_modify (header, field, "%s", 1, unit);
+  sprintf (field, "TFORM%d", Nfields);
+  gfits_modify (header, field, "%s", 1, format);
+
+  sprintf (field, "TBCOL%d", Nfields);
+  gfits_modify (header, field, "%d", 1, Nstart);
+
+  gfits_modify (header, "TFIELDS", "%d", 1, Nfields);
+  gfits_modify (header, "NAXIS1",  "%d", 1, Naxis1);
+  header[0].Naxis[0] = Naxis1;
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_free_T.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_free_T.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_free_T.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+int gfits_free_table (FTable *table) {
+  
+  if (table[0].buffer == (char *) NULL) return (TRUE);
+  free (table[0].buffer);
+  table[0].buffer = (char *) NULL;
+  return (TRUE);
+}
+
+int gfits_free_vtable (VTable *table) {
+  
+  int i;
+
+  if (table[0].buffer == (char **) NULL) return (TRUE);
+
+  for (i = 0; i < table[0].Nrow; i++) {
+    free (table[0].buffer[i]);
+  }
+  free (table[0].buffer);
+  free (table[0].row);
+
+  table[0].buffer = NULL;
+  table[0].row    = NULL;
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_get_T_column.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_get_T_column.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_get_T_column.c	(revision 22322)
@@ -0,0 +1,61 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits get table column *****************************/
+void gfits_get_table_column (table, X, mode, values) 
+Table *table; 
+int X; 
+char *mode, **values;
+{
+  
+  char Tform[100], field[100], tmp[100];
+  int  start, Nchar, i, N;
+  
+  int    *Ivalues;
+  long   *Lvalues;
+  short  *Svalues;
+  float  *Fvalues;
+  double *Dvalues;
+  char  **Cvalues;
+  
+  /* no error checking, all sorts of problems! */
+  /* should I count fields from 0 or from 1? */
+  
+  sprintf (field, "TBCOL%d\0", X);
+  gfits_scan (&table[0].header, field, "%d", &start, 1);
+  start --;
+  sprintf (field, "TFORM%d\0", X);
+  gfits_scan (&table[0].header, field, "%s", Tform, 1);
+  Nchar = atof (&Tform[1]);
+  
+  if (!strcmp (mode, "%d")) {
+    ALLOCATE (Ivalues, int, table[0].Naxis[1]);
+    for (i = 0; i < table[0].Naxis[1]; i++) {
+      strncpy (tmp, &table[0].buffer[i*table[0].Naxis[0] + start], Nchar);
+      tmp[Nchar] = 0;
+      Ivalues[i] = atof (tmp);
+    }
+    values[0] = (char *) Ivalues;
+  }
+  if (!strcmp (mode, "%lf")) {
+    ALLOCATE (Dvalues, double, table[0].Naxis[1]);
+    for (i = 0; i < table[0].Naxis[1]; i++) {
+      strncpy (tmp, &table[0].buffer[i*table[0].Naxis[0] + start], Nchar);
+      tmp[Nchar] = 0;
+      Dvalues[i] = atof (tmp);
+    }
+    values[0] = (char *) Dvalues;
+  }
+  if ((mode[0] == '%') && (mode[strlen(mode) - 1] == 'c')) {
+    mode[strlen(mode) - 1] = 0;
+    N = atof(&mode[1]);
+    ALLOCATE (Cvalues, char *, table[0].Naxis[1]);
+    for (i = 0; i < table[0].Naxis[1]; i++) {
+      ALLOCATE (Cvalues[i], char, N + 1);
+      strncpy (Cvalues[i], &table[0].buffer[i*table[0].Naxis[0] + start], N);
+      Cvalues[i][N] = 0;
+    }
+    values[0] = (char *) Cvalues;
+  }
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_get_T_value.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_get_T_value.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_get_T_value.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits get table value ********************************/
+  
+void gfits_get_table_value (table, X, Y, mode, value)
+Table *table; 
+int X, Y; 
+char *mode, *value;
+{
+  
+  char Tform[100], field[100];
+  int  start, Nchar, byte;
+  char tmp[1000];
+  
+  /* no error checking, all sorts of problems! */
+  
+  sprintf (field, "TBCOL%d\0", X);
+  gfits_scan (&table[0].header, field, "%d", &start, 1);
+  start --;
+  
+  sprintf (field, "TFORM%d\0", X);
+  gfits_scan (&table[0].header, field, "%s", Tform, 1);
+  Nchar = atof (&Tform[1]);
+  
+  byte = Y*table[0].Naxis[0] + start;
+  strncpy (tmp, &table[0].buffer[byte], Nchar);
+  tmp[Nchar] = 0;
+
+  if (!strcmp (mode, "%d"))  *(int      *) value = (int)      atof (tmp);
+  if (!strcmp (mode, "%u"))  *(unsigned *) value = (unsigned) atof (tmp);
+  if (!strcmp (mode, "%ld")) *(long     *) value = (long)     atof (tmp);
+  if (!strcmp (mode, "%hd")) *(short    *) value = (short)    atof (tmp);
+  if (!strcmp (mode, "%f"))  *(float    *) value = (float)    atof (tmp);
+  if (!strcmp (mode, "%lf")) *(double   *) value = (double)   atof (tmp);
+
+  if (!strcmp (mode, "%s")) 
+    strcpy (value, tmp);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_get_column.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_get_column.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_get_column.c	(revision 22322)
@@ -0,0 +1,372 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# define SWAP_BYTE { \
+  char tmp; \
+  tmp = Pin[0]; Pin[0] = Pin[1]; Pin[1] = tmp; }
+# define SWAP_WORD { \
+  char tmp; \
+  tmp = Pin[0]; Pin[0] = Pin[3]; Pin[3] = tmp; \
+  tmp = Pin[1]; Pin[1] = Pin[2]; Pin[2] = tmp; }
+# define SWAP_DBLE { \
+  char tmp; \
+  tmp = Pin[0]; Pin[0] = Pin[7]; Pin[7] = tmp; \
+  tmp = Pin[1]; Pin[1] = Pin[6]; Pin[6] = tmp; \
+  tmp = Pin[2]; Pin[2] = Pin[5]; Pin[5] = tmp; \
+  tmp = Pin[3]; Pin[3] = Pin[4]; Pin[4] = tmp; }
+
+void *gfits_get_bintable_column_data (Header *header, FTable *table, char *label, char *type, int *Nrow, int *Ncol) {
+
+  int i, N, Nfields, Nval, Nbytes, Nx, Ny, Nstart, Nv, Nb;
+  char tlabel[80], field[80], format[80], tmpline[16];
+  char *Pin, *Pout, *array;
+  double Bscale, Bzero;
+
+  if (label == (char *) NULL) return (NULL);
+  if (label[0] == 0) return (NULL);
+
+  /* find label in header */
+  tlabel[0] = 0;
+  if (!gfits_scan (header, "TFIELDS", "%d", 1, &Nfields)) return (NULL);
+  for (i = 1; strcmp (label, tlabel) && (i < Nfields + 1); i++) {
+    sprintf (field, "TTYPE%d", i);
+    gfits_scan (header, field, "%s", 1, tlabel);
+  }
+  if (strcmp (label, tlabel)) return (NULL);
+  N = i - 1;
+
+  Bscale = 1; 
+  Bzero  = 0;
+
+  /* interpret format */
+  sprintf (field, "TSCAL%d", N);
+  gfits_scan (header, field, "%lf", 1, &Bscale);
+  sprintf (field, "TZERO%d", N);
+  gfits_scan (header, field, "%lf", 1, &Bzero);
+  sprintf (field, "TFORM%d", N);
+  gfits_scan (header, field, "%s", 1, format);
+
+  if (!gfits_bintable_format (format, type, &Nval, &Nbytes)) return (NULL);
+  
+  /* check existing table dimensions */
+  gfits_scan (header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (header, "NAXIS2",  "%d", 1, &Ny);
+
+  /* scan columns to find insert point */
+  Nstart = 0;
+  for (i = 1; i < N; i++) {
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (header, field, "%s", 1, format);
+    gfits_bintable_format (format, tmpline, &Nv, &Nb);
+    Nstart += Nv*Nb;
+  }
+
+  /* extract bytes from table into array */
+  ALLOCATE (array, char, Nbytes*Nval*Ny);
+  Pin  = table[0].buffer + Nstart;
+  Pout = array;
+  for (i = 0; i < Ny; i++, Pin += Nx, Pout += Nval*Nbytes) {
+    memcpy (Pout, Pin, Nval*Nbytes);
+  }
+
+  /* convert data in-situ with correct type, byte swap and Bzero/Bscale */
+  Pin  = array;
+  Pout = array;
+  if (!strcmp (type, "char")) {
+    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
+      *(char *)Pout = *(char *)Pin*Bscale + Bzero;
+    }
+  }
+  if (!strcmp (type, "short")) {
+    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
+# ifdef BYTE_SWAP
+      SWAP_BYTE;
+# endif
+      *(short *)Pout = *(short *)Pin*Bscale + Bzero;
+    }  
+  }
+  if (!strcmp (type, "int")) {
+    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
+# ifdef BYTE_SWAP
+      SWAP_WORD;
+# endif
+      *(int *)Pout = *(int *)Pin*Bscale + Bzero;
+    }
+  }
+  if (!strcmp (type, "float")) {
+    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
+# ifdef BYTE_SWAP
+      SWAP_WORD;
+# endif
+      *(float *)Pout = *(float *)Pin*Bscale + Bzero;
+    }
+  }
+  if (!strcmp (type, "double")) {
+    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
+# ifdef BYTE_SWAP
+      SWAP_DBLE;
+# endif
+      *(double *)Pout = *(double *)Pin*Bscale + Bzero;
+    }
+  }
+
+  *Ncol = Nval;
+  *Nrow = Ny;
+  return (array);
+}
+
+/***********************/
+int gfits_get_bintable_column_type (Header *header, char *label, char *type, int *Nval) {
+
+  int i, N, Nfields, Nbytes;
+  char tlabel[80], field[80], format[80];
+
+  if (label == (char *) NULL) return (FALSE);
+  if (label[0] == 0) return (FALSE);
+
+  /* find label in header */
+  tlabel[0] = 0;
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+  for (i = 1; strcmp (label, tlabel) && (i < Nfields + 1); i++) {
+    sprintf (field, "TTYPE%d", i);
+    gfits_scan (header, field, "%s", 1, tlabel);
+  }
+  if (strcmp (label, tlabel)) return (FALSE);
+  N = i - 1;
+
+  sprintf (field, "TFORM%d", N);
+  gfits_scan (header, field, "%s", 1, format);
+
+  if (!gfits_bintable_format (format, type, Nval, &Nbytes)) return (FALSE);
+  return (TRUE);
+}
+
+/***********************/
+int gfits_get_bintable_column (Header *header, FTable *table, char *label, void **data) {
+
+  int i, N, Nfields, Nval, Nbytes, Nx, Ny, Nstart, Nv, Nb;
+  char tlabel[80], field[80], format[80], type[16], tmpline[16];
+  char *Pin, *Pout, *array;
+  double Bscale, Bzero;
+
+  if (label == (char *) NULL) return (FALSE);
+  if (label[0] == 0) return (FALSE);
+
+  /* find label in header */
+  tlabel[0] = 0;
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+  for (i = 1; strcmp (label, tlabel) && (i < Nfields + 1); i++) {
+    sprintf (field, "TTYPE%d", i);
+    gfits_scan (header, field, "%s", 1, tlabel);
+  }
+  if (strcmp (label, tlabel)) return (FALSE);
+  N = i - 1;
+
+  Bscale = 1; 
+  Bzero  = 0;
+
+  /* interpret format */
+  sprintf (field, "TSCAL%d", N);
+  gfits_scan (header, field, "%lf", 1, &Bscale);
+  sprintf (field, "TZERO%d", N);
+  gfits_scan (header, field, "%lf", 1, &Bzero);
+  sprintf (field, "TFORM%d", N);
+  gfits_scan (header, field, "%s", 1, format);
+
+  if (!gfits_bintable_format (format, type, &Nval, &Nbytes)) return (FALSE);
+  
+  /* check existing table dimensions */
+  gfits_scan (header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (header, "NAXIS2",  "%d", 1, &Ny);
+
+  /* scan columns to find insert point */
+  Nstart = 0;
+  for (i = 1; i < N; i++) {
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (header, field, "%s", 1, format);
+    gfits_bintable_format (format, tmpline, &Nv, &Nb);
+    Nstart += Nv*Nb;
+  }
+
+  /* extract bytes from table into temporary array */
+  ALLOCATE (array, char, Nbytes*Nval*Ny);
+  Pin  = table[0].buffer + Nstart;
+  Pout = array;
+  for (i = 0; i < Ny; i++, Pin += Nx, Pout += Nval*Nbytes) {
+    memcpy (Pout, Pin, Nval*Nbytes);
+  }
+
+  /* convert data in-situ with correct type, byte swap and Bzero/Bscale */
+  Pin  = array;
+  Pout = array;
+  if (!strcmp (type, "char")) {
+    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
+      *(char *)Pout = *(char *)Pin*Bscale + Bzero;
+    }
+  }
+  if (!strcmp (type, "short")) {
+    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
+# ifdef BYTE_SWAP
+      SWAP_BYTE;
+# endif
+      *(short *)Pout = *(short *)Pin*Bscale + Bzero;
+    }  
+  }
+  if (!strcmp (type, "int")) {
+    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
+# ifdef BYTE_SWAP
+      SWAP_WORD;
+# endif
+      *(int *)Pout = *(int *)Pin*Bscale + Bzero;
+    }
+  }
+  if (!strcmp (type, "float")) {
+    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
+# ifdef BYTE_SWAP
+      SWAP_WORD;
+# endif
+      *(float *)Pout = *(float *)Pin*Bscale + Bzero;
+    }
+  }
+  if (!strcmp (type, "double")) {
+    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
+# ifdef BYTE_SWAP
+      SWAP_DBLE;
+# endif
+      *(double *)Pout = *(double *)Pin*Bscale + Bzero;
+    }
+  }
+
+  *data = array;
+  return (TRUE);
+}
+
+/* 
+   valid BINTABLE column formats:
+   L - logical
+   X - bit
+   I - 16 bit int
+   J - 32 bit int
+   A - char
+   E - float 
+   D - double
+   B - unsigned bytes
+   C - complex float
+   M - complex double
+   P - var lenght array descrpt (64 bit)
+   
+   all can be preceeded by integer N which specified number
+   of entries in array
+   
+*/
+
+/***********************/
+int gfits_get_table_column_type (Header *header, char *label, char *type) {
+
+  int i, N, Nfields, Nval, Nbytes;
+  char tlabel[80], field[80], format[80];
+
+  if (label == (char *) NULL) return (FALSE);
+  if (label[0] == 0) return (FALSE);
+
+  /* find label in header */
+  tlabel[0] = 0;
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+  for (i = 1; strcmp (label, tlabel) && (i < Nfields + 1); i++) {
+    sprintf (field, "TTYPE%d", i);
+    gfits_scan (header, field, "%s", 1, tlabel);
+  }
+  if (strcmp (label, tlabel)) return (FALSE);
+  N = i - 1;
+
+  /* interpret format */
+  sprintf (field, "TFORM%d", N);
+  gfits_scan (header, field, "%s", 1, format);
+
+  if (!gfits_table_format (format, type, &Nval, &Nbytes)) return (FALSE);
+  return (TRUE);
+}
+
+/***********************/
+int gfits_get_table_column (Header *header, FTable *table, char *label, void **data) {
+
+  int i, N, Nfields, Nval, Nbytes, Nx, Ny, Nstart, Nv, Nb;
+  char tlabel[80], field[80], format[80], cformat[80], type[16], tmp[16];
+  char *array, *Pin, *Pout, *line;
+
+  if (label == (char *) NULL) return (FALSE);
+  if (label[0] == 0) return (FALSE);
+  array = NULL;
+
+  /* find label in header */
+  tlabel[0] = 0;
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+  for (i = 1; strcmp (label, tlabel) && (i < Nfields + 1); i++) {
+    sprintf (field, "TTYPE%d", i);
+    gfits_scan (header, field, "%s", 1, tlabel);
+  }
+  if (strcmp (label, tlabel)) return (FALSE);
+  N = i - 1;
+
+  /* interpret format */
+  sprintf (field, "TFORM%d", N);
+  gfits_scan (header, field, "%s", 1, format);
+
+  if (!gfits_table_format (format, type, &Nval, &Nbytes)) return (FALSE);
+  strcpy (cformat, format);
+  
+  /* check existing table dimensions */
+  gfits_scan (header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (header, "NAXIS2",  "%d", 1, &Ny);
+
+  /* scan columns to find insert point */
+  Nstart = 0;
+  for (i = 1; i < N; i++) {
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (header, field, "%s", 1, format);
+    gfits_table_format (format, tmp, &Nv, &Nb);
+    Nstart += Nv*Nb;
+  }
+
+  /* allocate temporary line, init pointers */
+  ALLOCATE (line, char, Nval+1);
+  bzero (line, Nval+1);
+  Pin  = table[0].buffer + Nstart;
+
+  /* allocate output array, copy/scan line as needed */
+  if (!strcmp (type, "char")) {
+    ALLOCATE (array, char, Ny*Nval);
+    Pout = array;
+    for (i = 0; i < Ny; i++, Pin+=Nx, Pout+=Nval*Nbytes) {
+      memcpy (Pout, Pin, Nval*Nbytes);
+    }
+  }
+  if (!strcmp (type, "int")) {
+    ALLOCATE (array, char, Ny*4);
+    Pout = array;
+    for (i = 0; i < Ny; i++, Pin+=Nx, Pout+=4) {
+      memcpy (line, Pin, Nval*Nbytes);
+      sscanf (line, "%d", (int *)Pout);
+    }
+  }
+  if (!strcmp (type, "float")) {
+    ALLOCATE (array, char, Ny*4);
+    Pout = array;
+    for (i = 0; i < Ny; i++, Pin+=Nx, Pout+=4) {
+      memcpy (line, Pin, Nval*Nbytes);
+      sscanf (line, "%f", (float *)Pout);
+    }
+  }
+  *data = array;
+  free (line);
+
+  return (TRUE);
+}
+
+/*
+  valid TABLE column formats:
+  FN.N  - floating point
+  INN   - integer
+  ANN   - NN char string
+  
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_read_T.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_read_T.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_read_T.c	(revision 22322)
@@ -0,0 +1,232 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits read table ***********************************/
+int gfits_read_ftable (char *filename, FTable *table, char *extname) {
+
+  int status;
+  FILE *f;
+
+  f = fopen (filename, "r");
+  if (f == NULL) return (FALSE);
+
+  status = gfits_fread_ftable (f, table, extname);
+  fclose (f);
+  return (status);
+}	
+
+/*********************** fits read table ***********************************/
+int gfits_fread_ftable (FILE *f, FTable *table, char *extname) {
+
+  int j, found, Nbytes;
+  Header *header;
+  char tname[80];
+
+  header = table[0].header;
+  fseek (f, 0, SEEK_SET);
+
+  found = FALSE;
+  for (j = -1; !found; j++) {
+    /* load data for this header */
+    if (!gfits_load_header (f, header)) return (FALSE);
+
+    /* check if this is the correct extension or not */
+    bzero (tname, 80);
+    gfits_scan (header, "EXTNAME", "%s", 1, tname);
+    if (!strcmp (tname, extname)) {
+      if (gfits_fread_ftable_data (f, table)) return (TRUE);
+      gfits_free_header (header);
+      return (FALSE);
+    }
+
+    /* skip to next header */
+    Nbytes = gfits_data_size (header);
+    fseek (f, Nbytes, SEEK_CUR);
+    gfits_free_header (header);
+  }
+  return (TRUE);
+}	
+
+/*********************** fits read ftable data ***********************************/
+int gfits_fread_ftable_data (FILE *f, FTable *table) {
+
+  int Nbytes, Nread;
+  char string[128];
+
+  /* find buffer size */
+  Nbytes = gfits_data_size (table[0].header);
+  ALLOCATE (table[0].buffer, char, Nbytes);
+
+  Nread = fread (table[0].buffer, sizeof (char), Nbytes, f);
+  if (Nread != Nbytes) {
+    snprintf (string, 128, "FITS file is short (%s)", __func__);
+    perror (string);
+    if (Nread < gfits_data_min_size (table[0].header)) {
+      fprintf (stderr, "error: fits read error in %s, read %d, need %d\n", __func__, Nread, gfits_data_min_size (table[0].header));
+      gfits_free_table  (table);
+      return (FALSE);
+    }
+    fprintf (stderr, "warning: file missing pad\n");
+  }
+  table[0].size = Nbytes;
+  return (TRUE);
+}	
+
+/*********************** fits read ftable data ***********************************/
+int gfits_fread_ftable_range (FILE *f, FTable *table, int start, int Nrows) {
+
+  int Nbytes, Nread, Nskip, Nx, Ny;
+
+  /* find disk table size */
+  Nx = table[0].header[0].Naxis[0];
+  Ny = table[0].header[0].Naxis[1];
+
+  // it is an error to ask for data starting out-of-bounds
+  if (start < 0) return (FALSE);
+  if (start >= Ny) return (FALSE);
+  
+  // if we request more data than is available, we will stop at the table end.
+  Nrows = MIN (Nrows, Ny - start);
+
+  Nskip = start * Nx;
+  Nbytes = Nrows * Nx;
+  ALLOCATE (table[0].buffer, char, MAX (Nbytes, 1));
+
+  fseek (f, Nskip, SEEK_CUR);
+
+  Nread = fread (table[0].buffer, sizeof (char), Nbytes, f);
+  if (Nread != Nbytes) {
+    if (ferror (f)) {
+      perror ("fits read error");
+    } else {
+      fprintf (stderr, "unexpected eof\n");
+    }      
+    free (table[0].buffer);
+    return (FALSE);
+  }
+
+  /* modify structure and header to match actual read rows Ny */
+  table[0].header[0].Naxis[1] = Nrows;
+  gfits_modify (table[0].header, "NAXIS2",  "%d", 1, Nrows);
+  table[0].size = gfits_data_size (table[0].header);
+
+  return (TRUE);
+}	
+
+/*********************** fits read ftable data ***********************************/
+int gfits_fread_vtable_range (FILE *f, VTable *table, int start, int Nrows) {
+
+  int i, Nbytes, Nread, Nskip, Nx, Ny;
+  char *buffer;
+
+  /* find buffer size */
+  Nx = table[0].header[0].Naxis[0];
+  Ny = table[0].header[0].Naxis[1];
+  table[0].size = gfits_data_size (table[0].header);
+  table[0].pad = table[0].size - Nx*Ny;
+
+  if (start < 0) return (FALSE);
+  if (start + Nrows >= Ny) return (FALSE);
+  
+  Nskip = start * Nx;
+  Nbytes = Nrows * Nx;
+  ALLOCATE (buffer, char, MAX (Nbytes, 1));
+
+  fseek (f, Nskip, SEEK_CUR);
+  Nread = fread (buffer, sizeof (char), Nbytes, f);
+  if (Nread != Nbytes) {
+    perror ("fits read error");
+    free (buffer);
+    return (FALSE);
+  }
+
+  ALLOCATE (table[0].row, int, MAX (Nrows, 1));
+  ALLOCATE (table[0].buffer, char *, MAX (Nrows, 1));
+  for (i = 0; i < Nrows; i++) {
+    ALLOCATE (table[0].buffer[i], char, MAX (Nx, 1));
+    memcpy (table[0].buffer[i], &buffer[i*Nx], Nx);
+    table[0].row[i] = start + i;
+  }
+  free (buffer);
+
+  return (TRUE);
+}	
+
+/*********************** fits read virtual table ***********************************/
+int gfits_fread_vtable (FILE *f, VTable *table, char *extname, int Nrow, int *row) {
+
+  int i, j, Nbytes, Nread;
+  int start, Nx, Ny, offset;
+  Header *header;
+  char tname[80];
+
+  header = table[0].header;
+  fseek (f, 0, SEEK_SET);
+
+  for (j = -1; TRUE; j++) {
+    /* load data for this header */
+    if (!gfits_load_header (f, header)) return (FALSE);
+
+    /* find buffer size */
+    Nbytes = gfits_data_size (header);
+
+    /* check if this is the correct extension or not */
+    bzero (tname, 80);
+    gfits_scan (header, "EXTNAME", "%s", 1, tname);
+    if (strcmp (tname, extname)) {
+      /* skip to next header */
+      fseek (f, Nbytes, SEEK_CUR);
+      gfits_free_header (header);
+      continue;
+    }
+
+    /* file pointer is at beginning of desired table data */
+    start = ftell (f);
+    gfits_scan (header, "NAXIS1", "%d", 1, &Nx);
+    gfits_scan (header, "NAXIS2", "%d", 1, &Ny);
+    for (i = 0; i < Nrow; i++) {
+      if (row[i] > Ny) { return (FALSE); }
+    }
+
+    ALLOCATE (table[0].buffer, char *, MAX (1, Nrow));
+    for (i = 0; i < Nrow; i++) {
+      ALLOCATE (table[0].buffer[i], char, MAX (1, Nx));
+      offset = start + Nx*row[i];
+      fseek (f, offset, SEEK_SET);
+      Nread = fread (table[0].buffer[i], sizeof (char), Nx, f);
+      if (Nread != Nx) { 
+	perror ("fits read error");
+	return (FALSE); 
+      }
+    }
+
+    table[0].Nrow  = Nrow;
+    ALLOCATE (table[0].row, int, MAX (1, Nrow));    
+    for (i = 0; i < Nrow; i++) table[0].row[i] = row[i];
+    table[0].size   = gfits_data_size (table[0].header);
+    table[0].pad    = table[0].size - Nx*Ny;
+    return (TRUE);
+  }
+}	
+
+int gfits_fread_header_extname (FILE *f, Header *header, char *extname) {
+
+  int Nbytes;
+  char current[80];
+
+  fseek (f, 0, SEEK_SET);
+  gfits_fread_header (f, header);
+
+  if (!strcasecmp (extname, "PHU")) return (TRUE);
+
+  Nbytes = gfits_data_size (header);
+  fseek (f, Nbytes, SEEK_CUR);
+
+  while (gfits_fread_header (f, header)) {
+    gfits_scan (header, "EXTNAME", "%s", 1, current);
+    if (!strcmp (current, extname)) return (TRUE);
+    Nbytes = gfits_data_size (header);
+    fseek (f, Nbytes, SEEK_CUR);
+  }
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_read_TH.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_read_TH.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_read_TH.c	(revision 22322)
@@ -0,0 +1,91 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits read Theader ***********************************/
+int gfits_read_Theader (char *filename, Header *Theader) {
+  
+  FILE *f;
+  Header header;
+  int status, Nbytes;
+  
+  status = gfits_read_header (filename, &header);
+  if (!status) {
+    fprintf (stderr, "error reading header of FITS file %s\n", filename);
+    return (FALSE);
+  }
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    Theader[0].buffer = NULL;
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+
+  Nbytes = gfits_data_size (&header);
+  fseek (f, Nbytes, SEEK_SET);
+  gfits_free_header (&header);
+
+  status = gfits_load_Theader (f, Theader);
+  fclose (f);
+  return (status);
+}	
+
+/* load table from STREAM positioned at beginning of table header */
+/*********************** fits load Theader ***********************************/
+int gfits_load_Theader (FILE *f, Header *Theader) {
+  
+  char *p;
+  int i, done, status, Nbytes;
+  
+  Theader[0].size = 0;
+  done = FALSE;
+  ALLOCATE (Theader[0].buffer, char, 1);
+
+  for (i = 0; !done; i++) {
+    REALLOCATE (Theader[0].buffer, char, (i + 1)*FT_RECORD_SIZE);
+    Nbytes = fread (&Theader[0].buffer[i*FT_RECORD_SIZE], 
+		    sizeof(char), FT_RECORD_SIZE, f);
+    if (Nbytes != FT_RECORD_SIZE) {
+      perror ("fits matrix read error");
+    }
+
+    Theader[0].size += Nbytes;
+    if (Nbytes != FT_RECORD_SIZE) 
+      done = TRUE;
+    p = gfits_header_field (Theader, "END", 1);
+    if (p != NULL)
+      done = TRUE;
+  }
+
+  Theader[0].bscale = 0;
+  Theader[0].bzero  = 0;
+  for (i = 0; i < FT_MAX_NAXES; i++)
+    Theader[0].Naxis[i] = 0;
+
+  status = TRUE;
+  status &= gfits_scan (Theader,  "BITPIX", "%d", 1, &Theader[0].bitpix);
+  status &= gfits_scan (Theader,  "NAXIS",  "%d", 1, &Theader[0].Naxes);
+  if (!status) return (FALSE);
+				                           
+  gfits_scan (Theader,  "NAXIS1", "%d", 1, &Theader[0].Naxis[0]);
+  gfits_scan (Theader,  "NAXIS2", "%d", 1, &Theader[0].Naxis[1]);
+  gfits_scan (Theader,  "NAXIS3", "%d", 1, &Theader[0].Naxis[2]);
+  gfits_scan (Theader,  "NAXIS4", "%d", 1, &Theader[0].Naxis[3]);
+  gfits_scan (Theader,  "NAXIS5", "%d", 1, &Theader[0].Naxis[4]);
+  gfits_scan (Theader,  "NAXIS6", "%d", 1, &Theader[0].Naxis[5]);
+  gfits_scan (Theader,  "NAXIS7", "%d", 1, &Theader[0].Naxis[6]);
+  gfits_scan (Theader,  "NAXIS8", "%d", 1, &Theader[0].Naxis[7]);
+  gfits_scan (Theader,  "NAXIS9", "%d", 1, &Theader[0].Naxis[8]);
+  gfits_scan (Theader, "NAXIS10", "%d", 1, &Theader[0].Naxis[9]);
+
+  return (TRUE);
+}	
+
+/*********************** fits fread Theader ***********************************/
+int gfits_fread_Theader (FILE *f, Header *Theader) {
+  
+  int status;
+  
+  status = gfits_load_Theader (f, Theader);
+  return (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_set_column.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_set_column.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_set_column.c	(revision 22322)
@@ -0,0 +1,251 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# define SWAP_BYTE { \
+  char tmp; \
+  tmp = Pout[0]; Pout[0] = Pout[1]; Pout[1] = tmp; }
+# define SWAP_WORD { \
+  char tmp; \
+  tmp = Pout[0]; Pout[0] = Pout[3]; Pout[3] = tmp; \
+  tmp = Pout[1]; Pout[1] = Pout[2]; Pout[2] = tmp; }
+# define SWAP_DBLE { \
+  char tmp; \
+  tmp = Pout[0]; Pout[0] = Pout[7]; Pout[7] = tmp; \
+  tmp = Pout[1]; Pout[1] = Pout[6]; Pout[6] = tmp; \
+  tmp = Pout[2]; Pout[2] = Pout[5]; Pout[5] = tmp; \
+  tmp = Pout[3]; Pout[3] = Pout[4]; Pout[4] = tmp; }
+
+/***********************/
+int gfits_set_bintable_column (Header *header, FTable *table, char *label, void *data, int Nrow) {
+
+  int i, N, Nfields, Nval, Nbytes, Nx, Ny, nbytes, Nstart, Nv, Nb;
+  char tlabel[80], field[80], format[80], type[16], tmpline[16];
+  char *Pin, *Pout, *array;
+  double Bscale, Bzero;
+
+  if (label == (char *) NULL) return (FALSE);
+  if (label[0] == 0) return (FALSE);
+
+  /* find label in header */
+  tlabel[0] = 0;
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+  for (i = 1; strcmp (label, tlabel) && (i < Nfields + 1); i++) {
+    sprintf (field, "TTYPE%d", i);
+    gfits_scan (header, field, "%s", 1, tlabel);
+  }
+  if (strcmp (label, tlabel)) return (FALSE);
+  N = i - 1;
+
+  /* interpret format */
+  sprintf (field, "TSCAL%d", N);
+  gfits_scan (header, field, "%lf", 1, &Bscale);
+  sprintf (field, "TZERO%d", N);
+  gfits_scan (header, field, "%lf", 1, &Bzero);
+  sprintf (field, "TFORM%d", N);
+  gfits_scan (header, field, "%s", 1, format);
+
+  if (!gfits_bintable_format (format, type, &Nval, &Nbytes)) return (FALSE);
+  
+  /* check existing table dimensions */
+  gfits_scan (header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (header, "NAXIS2",  "%d", 1, &Ny);
+  if (Ny == 0) { 
+    Ny = Nrow;
+    header[0].Naxis[1] = Ny;
+    gfits_modify (header, "NAXIS2",  "%d", 1, Ny);
+
+    nbytes = gfits_data_size (header);
+    REALLOCATE (table[0].buffer, char, MAX (nbytes, 1));
+    bzero (table[0].buffer, nbytes);
+    table[0].size = nbytes;
+  }
+  if (Ny != Nrow) return (FALSE);
+
+  /* scan columns to find insert point */
+  Nstart = 0;
+  for (i = 1; i < N; i++) {
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (header, field, "%s", 1, format);
+    gfits_bintable_format (format, tmpline, &Nv, &Nb);
+    Nstart += Nv*Nb;
+  }
+
+  /* make duplicate of data with correct type
+     byte swap and Bzero/Bscale */
+  ALLOCATE (array, char, Nbytes*Nval*Nrow);
+  Pin = data;
+  Pout = array;
+  if (!strcmp (type, "char")) {
+    for (i = 0; i < Nval*Nrow; i++, Pin+=Nbytes, Pout+=Nbytes) {
+      *(char *)Pout = (*(char *)Pin - Bzero) / Bscale;
+    }
+  }
+  if (!strcmp (type, "short")) {
+    for (i = 0; i < Nval*Nrow; i++, Pin+=Nbytes, Pout+=Nbytes) {
+      *(short *)Pout = (*(short *)Pin - Bzero) / Bscale;
+# ifdef BYTE_SWAP
+      SWAP_BYTE;
+# endif
+    }  
+  }
+  if (!strcmp (type, "int")) {
+    for (i = 0; i < Nval*Nrow; i++, Pin+=Nbytes, Pout+=Nbytes) {
+      *(int *)Pout = (*(int *)Pin - Bzero) / Bscale;
+# ifdef BYTE_SWAP
+      SWAP_WORD;
+# endif
+    }
+  }
+  if (!strcmp (type, "float")) {
+    for (i = 0; i < Nval*Nrow; i++, Pin+=Nbytes, Pout+=Nbytes) {
+      *(float *)Pout = (*(float *)Pin - Bzero) / Bscale;
+# ifdef BYTE_SWAP
+      SWAP_WORD;
+# endif
+    }
+  }
+  if (!strcmp (type, "double")) {
+    for (i = 0; i < Nval*Nrow; i++, Pin+=Nbytes, Pout+=Nbytes) {
+      *(double *)Pout = (*(double *)Pin - Bzero) / Bscale;
+# ifdef BYTE_SWAP
+      SWAP_DBLE;
+# endif
+    }
+  }
+
+  /* check array space */
+  if (Nx*Ny < Nx*(Nrow - 1) + Nstart + Nval*Nbytes) {
+    fprintf (stderr, "mismatch in array sizes\n");
+    return (FALSE);
+  }
+
+  /* insert bytes from array into appropriate section of buffer */
+  Pout = table[0].buffer + Nstart;
+  Pin  = array;
+  for (i = 0; i < Nrow; i++, Pout += Nx, Pin += Nval*Nbytes) {
+    memcpy (Pout, Pin, Nval*Nbytes);
+  }
+
+  free (array);
+  return (TRUE);
+}
+
+/* 
+   valid BINTABLE column formats:
+   L - logical
+   X - bit
+   I - 16 bit int
+   J - 32 bit int
+   A - char
+   E - float 
+   D - double
+   B - unsigned bytes
+   C - complex float
+   M - complex double
+   P - var lenght array descrpt (64 bit)
+   
+   all can be preceeded by integer N which specified number
+   of entries in array
+   
+*/
+
+
+/***********************/
+int gfits_set_table_column (Header *header, FTable *table, char *label, void *data, int Nrow) {
+
+  int i, N, Nfields, Nval, Nbytes, Nx, Ny, nbytes, Nstart, Nv, Nb;
+  char tlabel[80], field[80], format[80], cformat[80], type[16], tmp[16];
+  char *array, *Pin, *Pout, *line;
+
+  if (label == (char *) NULL) return (FALSE);
+  if (label[0] == 0) return (FALSE);
+
+  /* find label in header */
+  tlabel[0] = 0;
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+  for (i = 1; strcmp (label, tlabel) && (i < Nfields + 1); i++) {
+    sprintf (field, "TTYPE%d", i);
+    gfits_scan (header, field, "%s", 1, tlabel);
+  }
+  if (strcmp (label, tlabel)) return (FALSE);
+  N = i - 1;
+
+  /* interpret format */
+  sprintf (field, "TFORM%d", N);
+  gfits_scan (header, field, "%s", 1, format);
+
+  if (!gfits_table_format (format, type, &Nval, &Nbytes)) return (FALSE);
+  strcpy (cformat, format);
+  
+  /* check existing table dimensions */
+  gfits_scan (header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (header, "NAXIS2",  "%d", 1, &Ny);
+  if (Ny == 0) { 
+    Ny = Nrow;
+    header[0].Naxis[1] = Ny;
+    gfits_modify (header, "NAXIS2",  "%d", 1, Ny);
+
+    nbytes = gfits_data_size (header);
+    REALLOCATE (table[0].buffer, char, MAX (nbytes, 1));
+    bzero (table[0].buffer, nbytes);
+    table[0].size = nbytes;
+  }
+  if (Ny != Nrow) return (FALSE);
+
+  /* scan columns to find insert point */
+  Nstart = 0;
+  for (i = 1; i < N; i++) {
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (header, field, "%s", 1, format);
+    gfits_table_format (format, tmp, &Nv, &Nb);
+    Nstart += Nv*Nb;
+  }
+
+  /* print line with appropriate formatting */
+  ALLOCATE (array, char, Nbytes*Nval*Nrow);
+  ALLOCATE (line, char, Nval+1);
+  Pin = data;
+  Pout = array;
+  if (!strcmp (type, "char")) {
+    for (i = 0; i < Nval*Nrow; i++, Pin++, Pout++) {
+      *Pout = *Pin;
+    }
+  }
+  if (!strcmp (type, "int")) {
+    for (i = 0; i < Nrow; i++, Pin+=4, Pout+=Nval) {
+      snprintf (line, Nval + 1, cformat, *(int *)Pin);
+      memcpy (Pout, line, Nval);
+    }
+  }
+  if (!strcmp (type, "float")) {
+    for (i = 0; i < Nrow; i++, Pin+=4, Pout+=Nval) {
+      snprintf (line, Nval + 1, cformat, *(float *)Pin);
+      memcpy (Pout, line, Nval);
+    }
+  }
+
+  /* check array space */
+  if (Nx*Ny < Nx*(Nrow - 1) + Nstart + Nval*Nbytes) {
+    fprintf (stderr, "mismatch in array sizes\n");
+    return (FALSE);
+  }
+
+  /* insert bytes from array into appropriate section of buffer */
+  Pout = table[0].buffer + Nstart;
+  Pin  = array;
+  for (i = 0; i < Nrow; i++, Pout += Nx, Pin += Nval*Nbytes) {
+    memcpy (Pout, Pin, Nval*Nbytes);
+  }
+
+  free (line);
+  free (array);
+  return (TRUE);
+}
+
+/*
+  valid TABLE column formats:
+  FN.N  - floating point
+  INN   - integer
+  ANN   - NN char string
+  
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_column.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_column.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_column.c	(revision 22322)
@@ -0,0 +1,95 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits table column ****************************/
+int gfits_table_column (Table *table, char *field, char *mode,...) {
+/* we expect one more field: the pointer to the array we read in */
+
+  char string[80], this_field[80], form[80], temp[80];
+  int i, j, N, start, end, width, M;
+  va_list argp;
+  double **D;
+  float  **F;
+  char   ***C;
+  int    **I;
+  
+  va_start (argp, mode);
+
+  /* find the correct field */
+  for (i = 0; i < table[0].Nfields; i++) {
+    sprintf (string, "TTYPE%d\0", i+1);
+    gfits_scan (&table[0].header, string, "%s", 1, this_field);
+    if (!strcmp (field, this_field)) {
+      break;
+    }
+  }
+  
+  if (i == table[0].Nfields) {
+    fprintf (stderr, "Table field %s does not exist\n", field);
+    return (FALSE);
+  }
+
+  N = i + 1;
+
+  sprintf (string, "TBCOL%d\0", N);
+  gfits_scan (&table[0].header, string, "%d", 1, &start);
+  sprintf (string, "TFORM%d\0", N);
+  gfits_scan (&table[0].header, string, "%s", 1, form);
+  /* we could use some error checking from the FITS table form, but
+     it is not immediately crucial */
+
+  if (N == table[0].Nfields) { 
+    end = table[0].Naxis[0];
+  } else {
+    sprintf (string, "TBCOL%d\0", N+1);
+    gfits_scan (&table[0].header, string, "%d", 1, &end);
+  }
+  width = end - start;
+  
+  if (!strcmp (mode, "%d")) {
+    I = va_arg (argp, int **);
+    ALLOCATE ((*I), int, table[0].Naxis[1]);
+    M = 0;
+  }
+  if (!strcmp (mode, "%f"))  {
+    F = va_arg (argp, float **);
+    ALLOCATE ((*F), float, table[0].Naxis[1]);
+    M = 1;
+  }
+  if (!strcmp (mode, "%lf")) {
+    D = va_arg (argp, double **);
+    ALLOCATE ((*D), double, table[0].Naxis[1]);
+    M = 2;
+  }
+  if (!strcmp (mode, "%s")) {
+    C = va_arg (argp, char ***);
+    ALLOCATE ((*C), char *, table[0].Naxis[1]);
+    for (i = 0; i < table[0].Naxis[1]; i++) {
+      ALLOCATE ((*C)[i], char, width + 1);
+    }
+    M = 3;
+  }
+
+  for (i = 0; i < table[0].Naxis[1]; i++) {
+    strncpy (temp, &table[0].buffer[i*table[0].Naxis[0] + start - 1], width);
+    temp[width] = 0;
+    switch (M) {
+    case 0:
+      (*I)[i] = (int) atof (temp);
+      break;
+    case 1:
+      (*F)[i] = atof (temp);
+      break;
+    case 2:
+      (*D)[i] = atof (temp);
+      break;
+    case 3:
+      strcpy ((*C)[i], temp);
+      break;
+    default:
+      fprintf (stderr, "unknown gfits_table_column mode: %s\n", mode);
+      return (FALSE);
+    }
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_format.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_format.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_format.c	(revision 22322)
@@ -0,0 +1,329 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/***********************/
+int gfits_bintable_format (char *format, char *type, int *Nval, int *Nbytes) {
+
+  char *Lchar, *Fchar;
+  int Nv;
+
+  if (format == (char *) NULL) return (FALSE);
+  if (format[0] == 0) return (FALSE);
+  Lchar = &format[strlen(format) - 1];
+
+  Nv = strtol (format, &Fchar, 10);
+  if (Fchar != Lchar) return (FALSE);
+  if ((Nv == 0) && (Fchar == format)) Nv = 1;
+
+  *Nbytes = 0;
+  if (*Fchar == 'X') { *Nbytes = 1;  strcpy (type, "char");   *Nval = 1 + (int) Nv / 8; }
+  if (*Fchar == 'L') { *Nbytes = 1;  strcpy (type, "char");   *Nval = Nv;               }
+  if (*Fchar == 'A') { *Nbytes = 1;  strcpy (type, "char");   *Nval = Nv;               }
+  if (*Fchar == 'B') { *Nbytes = 1;  strcpy (type, "char");   *Nval = Nv;               }
+  if (*Fchar == 'I') { *Nbytes = 2;  strcpy (type, "short");  *Nval = Nv;               }
+  if (*Fchar == 'J') { *Nbytes = 4;  strcpy (type, "int");    *Nval = Nv;               }
+  if (*Fchar == 'E') { *Nbytes = 4;  strcpy (type, "float");  *Nval = Nv;               }
+  if (*Fchar == 'D') { *Nbytes = 8;  strcpy (type, "double"); *Nval = Nv;               }
+  if (*Fchar == 'P') { *Nbytes = 8;  strcpy (type, "var");    *Nval = 2*Nv;             }
+  if (*Fchar == 'C') { *Nbytes = 8;  strcpy (type, "float");  *Nval = 2*Nv;             }
+  if (*Fchar == 'M') { *Nbytes = 16; strcpy (type, "double"); *Nval = 2*Nv;             }
+  if (!*Nbytes) { return (FALSE); }
+
+  return (TRUE);
+}
+
+/* 
+   valid BINTABLE column formats:
+   L - logical
+   X - bit
+   I - 16 bit int
+   J - 32 bit int
+   A - char
+   E - float 
+   D - double
+   B - unsigned bytes
+   C - complex float
+   M - complex double
+   P - var length array descrpt (64 bit)
+   
+   all can be preceeded by integer N which specified number
+   of entries in array
+   
+*/
+
+/***********************/
+int gfits_table_format (char *format, char *type, int *Nval, int *Nbytes) {
+
+  char Fchar, Size[80], Type;
+  int Nv;
+
+  if (format == (char *) NULL) return (FALSE);
+  if (format[0] == 0) return (FALSE);
+  Fchar = format[0];
+
+  Nv = strtol (&format[1], (char **) NULL, 10);
+  if (Nv == 0) { 
+    Nv = 1;
+    strcpy (Size, "1");
+  } else {
+    strcpy (Size, &format[1]);
+  }
+  
+  Type = 'x';
+  if (Fchar == 'F') { *Nbytes = 1;  strcpy (type, "float");  Type = 'f'; *Nval = Nv; }
+  if (Fchar == 'I') { *Nbytes = 1;  strcpy (type, "int");    Type = 'd'; *Nval = Nv; }
+  if (Fchar == 'A') { *Nbytes = 1;  strcpy (type, "char");   Type = 's'; *Nval = Nv; }
+  if (!*Nbytes) { return (FALSE); }
+  
+  sprintf (format, "%%-%s%c", Size, Type);
+
+  return (TRUE);
+}
+
+/*
+  valid TABLE column formats:
+  FN.N  - floating point
+  INN   - integer
+  ANN   - NN char string
+  
+*/
+
+/** extract a table subset to a vtable ***/
+int gfits_table_to_vtable (FTable *ftable, VTable *vtable, int start, int Nkeep) {
+
+  /* gfits_table_to_vtable (f, v, 0, Ny - 1) - keep all of table
+     gfits_table_to_vtable (f, v, 0, 0)      - keep none of table 
+  */  
+
+  int i, Nx, Ny;
+
+  gfits_scan (ftable[0].header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (ftable[0].header, "NAXIS2", "%d", 1, &Ny);
+  
+  if (start + Nkeep > Ny) return (FALSE);
+  if (start < 0) return (FALSE);
+  if (Nkeep < 0) return (FALSE);
+
+  ALLOCATE (vtable[0].row, int, MAX (1, Nkeep));
+  ALLOCATE (vtable[0].buffer, char *, MAX (1, Nkeep));
+  for (i = 0; i < Nkeep; i++) {
+    ALLOCATE (vtable[0].buffer[i], char, MAX (1, Nx));
+    memcpy (vtable[0].buffer[i], &ftable[0].buffer[(i+start)*Nx], Nx);
+    vtable[0].row[i] = i + start;
+  }
+  
+  vtable[0].header = ftable[0].header;
+  vtable[0].size = ftable[0].size;
+  vtable[0].Nrow = Nkeep;
+  vtable[0].pad = vtable[0].size - Nx*Ny;
+
+  return (TRUE);
+}
+
+/** convert specified rows to vtable */
+int gfits_vtable_from_ftable (FTable *ftable, VTable *vtable, int *row, int Nrow) {
+
+  int i, N, Nx, Ny;
+
+  gfits_scan (ftable[0].header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (ftable[0].header, "NAXIS2", "%d", 1, &Ny);
+
+  /* make empty vtable from table */
+  vtable[0].header = ftable[0].header;  /* make this a copy? */
+  vtable[0].size   = ftable[0].size;
+  vtable[0].pad    = vtable[0].size - Nx*Ny;
+  vtable[0].Nrow   = Ny;
+
+  /* insert selected rows in vtable (mask rows marked with -1) */ 
+  ALLOCATE (vtable[0].row, int, Nrow);
+  ALLOCATE (vtable[0].buffer, char *, Nrow);
+  for (N = i = 0; i < Nrow; i++) {
+    if (row[i] == -1) continue;
+    vtable[0].row[N] = row[i];
+    ALLOCATE (vtable[0].buffer[N], char, Nx);
+    memcpy (vtable[0].buffer[N], &ftable[0].buffer[Nx*row[i]], Nx);
+    N++;
+  }
+  vtable[0].Nrow = N;
+  return (TRUE);
+}
+
+/* use table def to format a complete string */
+char *gfits_table_print (FTable *table,...) { 
+  
+  int i, Nx, Nfields;
+  int off, Nchar, Nval, Nbytes;
+  char *line, format[64], field[16], type[16];
+  va_list argp;
+  
+  va_start (argp, table);
+
+  gfits_scan (table[0].header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (table[0].header, "TFIELDS", "%d", 1, &Nfields);
+
+  ALLOCATE (line, char, Nx + 1);
+  
+  off = 0;
+  for (i = 1; i <= Nfields; i++) {
+
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (table[0].header, field, "%s", 1, format);       /* get field format */
+    gfits_table_format (format, type, &Nval, &Nbytes); /* convert to c-style */
+    Nchar = Nval * Nbytes;
+    if (!strcmp (type, "int"))   { 
+      /* d = va_arg (argp, int); */
+      snprintf (&line[off], Nchar + 1, format, va_arg (argp, int)); 
+    }
+    if (!strcmp (type, "float")) { 
+      /* f = va_arg (argp, double); */
+      snprintf (&line[off], Nchar + 1, format, va_arg (argp, double)); 
+    }
+    if (!strcmp (type, "char"))  { 
+      /* c = va_arg (argp, char *); */
+      snprintf (&line[off], Nchar + 1, format, va_arg (argp, char *));
+    }
+    off += Nchar;
+  }
+  va_end (argp);
+  return (line);
+}
+
+// apply table tzero, tscal in situ (from storage to data)
+int gfits_table_scale_data (FTable *ftable) {
+
+  int i, j, n, Nx, Ny, Nfields;
+  int off, Nchar, Nval, Nbytes, status;
+  char format[64], field[16], type[16];
+  double tzero, tscale;
+  char *tmpChar;
+  short *tmpShort;
+  int *tmpInt;
+
+  off = 0;
+
+  gfits_scan (ftable[0].header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (ftable[0].header, "NAXIS2",  "%d", 1, &Ny);
+  gfits_scan (ftable[0].header, "TFIELDS", "%d", 1, &Nfields);
+
+  for (i = 1; i <= Nfields; i++) {
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (ftable[0].header, field, "%s", 1, format);       /* get field format */
+    gfits_bintable_format (format, type, &Nval, &Nbytes); /* convert to c-style */
+    Nchar = Nval * Nbytes;
+
+    sprintf (field, "TZERO%d", i);
+    status = gfits_scan (ftable[0].header, field, "%lf", 1, &tzero);       /* get field format */
+    if (!status) {
+	off += Nchar; 
+	continue;
+    }
+
+    sprintf (field, "TSCAL%d", i);
+    status = gfits_scan (ftable[0].header, field, "%lf", 1, &tscale);       /* get field format */
+    if (!status) {
+	off += Nchar; 
+	continue;
+    }
+    if (tscale == 0.0) {
+	off += Nchar; 
+	continue;
+    }
+
+    if (!strcmp (type, "char"))   { 
+      for (j = 0; j < Ny; j++) {
+	for (n = 0; n < Nval; n++) {
+	  tmpChar = (char *)&ftable[0].buffer[j*Nx + n*Nbytes + off];
+	  *tmpChar = *tmpChar * tscale + tzero;
+	}
+      }
+    }
+    if (!strcmp (type, "short"))   { 
+      for (j = 0; j < Ny; j++) {
+	for (n = 0; n < Nval; n++) {
+	  tmpShort = (short *)&ftable[0].buffer[j*Nx + n*Nbytes + off];
+	  *tmpShort = *tmpShort * tscale + tzero;
+	}
+      }
+    }
+    if (!strcmp (type, "int"))   { 
+      for (j = 0; j < Ny; j++) {
+	for (n = 0; n < Nval; n++) {
+	  tmpInt = (int *)&ftable[0].buffer[j*Nx + n*Nbytes + off];
+	  *tmpInt = *tmpInt * tscale + tzero;
+	}
+      }
+    }
+    off += Nchar;
+  }
+  return (TRUE);
+}
+
+// apply table tzero, tscal in situ (from data to storage)
+int gfits_table_scale_storage (FTable *ftable) {
+
+  int i, j, n, Nx, Ny, Nfields;
+  int off, Nchar, Nval, Nbytes, status;
+  char format[64], field[16], type[16];
+  double tzero, tscale;
+  char *tmpChar;
+  short *tmpShort;
+  int *tmpInt;
+
+  off = 0;
+
+  gfits_scan (ftable[0].header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (ftable[0].header, "NAXIS2",  "%d", 1, &Ny);
+  gfits_scan (ftable[0].header, "TFIELDS", "%d", 1, &Nfields);
+
+  for (i = 1; i <= Nfields; i++) {
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (ftable[0].header, field, "%s", 1, format);       /* get field format */
+    gfits_bintable_format (format, type, &Nval, &Nbytes); /* convert to c-style */
+    Nchar = Nval * Nbytes;
+
+    sprintf (field, "TZERO%d", i);
+    status = gfits_scan (ftable[0].header, field, "%lf", 1, &tzero);       /* get field format */
+    if (!status) {
+	off += Nchar; 
+	continue;
+    }
+
+    sprintf (field, "TSCAL%d", i);
+    status = gfits_scan (ftable[0].header, field, "%lf", 1, &tscale);       /* get field format */
+    if (!status) {
+	off += Nchar; 
+	continue;
+    }
+    if (tscale == 0.0) {
+	off += Nchar; 
+	continue;
+    }
+
+    if (!strcmp (type, "char"))   { 
+      for (j = 0; j < Ny; j++) {
+	for (n = 0; n < Nval; n++) {
+	  tmpChar = (char *)&ftable[0].buffer[j*Nx + n*Nbytes + off];
+	  *tmpChar = (*tmpChar - tzero) / tscale;
+	}
+      }
+    }
+    if (!strcmp (type, "short"))   { 
+      for (j = 0; j < Ny; j++) {
+	for (n = 0; n < Nval; n++) {
+	  tmpShort = (short *)&ftable[0].buffer[j*Nx + n*Nbytes + off];
+	  *tmpShort = (*tmpShort - tzero) / tscale;
+	}
+      }
+    }
+    if (!strcmp (type, "int"))   { 
+      for (j = 0; j < Ny; j++) {
+	for (n = 0; n < Nval; n++) {
+	  tmpInt = (int *)&ftable[0].buffer[j*Nx + n*Nbytes + off];
+	  *tmpInt = (*tmpInt - tzero) / tscale;
+	}
+      }
+    }
+    off += Nchar;
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_row.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_row.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_row.c	(revision 22322)
@@ -0,0 +1,106 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits table column ****************************/
+int gfits_add_rows (FTable *table, char *data, int Nrow, int Nbytes) {
+
+  int Nx, Ny;
+  int nbytes, Nstart;
+  Header *header;
+
+  header = table[0].header;
+
+  gfits_scan (header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (header, "NAXIS2", "%d", 1, &Ny);
+  
+  if (header[0].Naxis[1] != Ny) return (FALSE);
+  if (header[0].Naxis[0] != Nx) return (FALSE);
+  if (header[0].Naxis[0] != Nbytes) return (FALSE);
+  
+  Nstart = Nx*Ny;
+
+  /* update header y dimension */
+  Ny += Nrow;
+  header[0].Naxis[1] = Ny;
+  gfits_modify (header, "NAXIS2",  "%d", 1, Ny);
+
+  nbytes = gfits_data_size (header);
+  REALLOCATE (table[0].buffer, char, MAX (nbytes, 1));
+  table[0].size = nbytes;
+  
+  memcpy (&table[0].buffer[Nstart], data, Nbytes*Nrow);
+  memset (&table[0].buffer[Nx*Ny], ' ', nbytes - Nx*Ny);
+  return (TRUE);
+}
+
+/*********************** fits add (real) rows to virtual table ****************************/
+int gfits_vadd_rows (VTable *table, char *data, int Nrow, int Nbytes) {
+
+  int i, Nx, Ny;
+  int Nstart;
+  Header *header;
+
+  header = table[0].header;
+
+  gfits_scan (header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (header, "NAXIS2", "%d", 1, &Ny);
+  
+  if (header[0].Naxis[1] != Ny) return (FALSE);
+  if (header[0].Naxis[0] != Nx) return (FALSE);
+  if (header[0].Naxis[0] != Nbytes) return (FALSE);
+  
+  /* Nstart is end of data in memory */
+  Nstart = table[0].Nrow;
+  table[0].Nrow += Nrow;
+  REALLOCATE (table[0].buffer, char *, table[0].Nrow);
+  REALLOCATE (table[0].row, int, table[0].Nrow);
+  for (i = 0; i < Nrow; i++) {
+    ALLOCATE (table[0].buffer[Nstart+i], char, MAX (1, Nx));
+    memcpy (table[0].buffer[Nstart+i], &data[i*Nx], Nx);
+    table[0].row[Nstart+i] = Ny + i;
+  }
+
+  /* update header y dimension */
+  Ny += Nrow;
+  header[0].Naxis[1] = Ny;
+  gfits_modify (header, "NAXIS2",  "%d", 1, Ny);
+
+  table[0].size   = gfits_data_size (table[0].header);
+  table[0].pad    = table[0].size - Nx*Ny;
+
+  return (TRUE);
+}
+
+/*********************** fits table column ****************************/
+int gfits_delete_rows (FTable *table, int Nstart, int Nrow) {
+
+  int Nx, Ny, N0, N1, N2;
+  int nbytes;
+  Header *header;
+
+  header = table[0].header;
+
+  gfits_scan (header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (header, "NAXIS2", "%d", 1, &Ny);
+  
+  if (header[0].Naxis[1] != Ny) return (FALSE);
+  if (header[0].Naxis[0] != Nx) return (FALSE);
+  if (Ny > Nstart + Nrow) return (FALSE);
+  
+  /* shrink buffer by Nrow entries */
+  N0 = Nx*Nstart;
+  N1 = Nx*(Nstart + Nrow);
+  N2 = Nx*(Ny - Nstart - Nrow);
+  memmove (&table[0].buffer[N0], &table[0].buffer[N1], N2);
+
+  /* update header y dimension */
+  Ny -= Nrow;
+  header[0].Naxis[1] = Ny;
+  gfits_modify (header, "NAXIS2",  "%d", 1, Ny);
+
+  nbytes = gfits_data_size (header);
+  REALLOCATE (table[0].buffer, char, MAX (nbytes, 1));
+  table[0].size = nbytes;
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_varlength.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_varlength.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_table_varlength.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+int gfits_varlength_column_define (FTable *ftable, VarLengthColumn *def, int column) {
+
+  int i, Nv, Nb;
+  char *p1, *p2, *p3;
+  char field[81];
+  char format[81];
+  char tmpline[81];
+
+  // grab the value of TFORMn for this column
+  snprintf (field, 80, "TFORM%d", column);
+  if (!gfits_scan (ftable->header, field, "%s", 1, format)) return (FALSE);
+
+  // find and remove the max field length element
+  p1 = strchr (format, '(');
+  p2 = strchr (format, ')');
+
+  if (!p1 || !p2) return (FALSE); // not a valid varlength column -- missing (e_max)
+  if (p2 - p1 < 2) return (FALSE); // not a valid varlength column -- contains ()
+
+  def->maxlen = strtol (p1 + 1, &p3, 10);
+
+  if (p3 != p2) return (FALSE); // not a valid varlength column -- (e_max) contains extra chars
+  *p1 = 0; // make the format string end here for the rest of the function
+
+  // first char may optionally be 0, 1
+  p1 = format;
+  if ((*p1 == '0') || (*p1 == '1')) p1 ++;
+
+  // now p1 must be 'P';
+  if (*p1 == 0) return (FALSE);
+  if (*p1 != 'P') return (FALSE);
+
+  // next value is the actual varlength column format
+  p1 ++;
+  if (*p1 == 0) return (FALSE);
+  if (*p1 == 'P') { return (FALSE); }
+
+  def->nbytes = 0;
+  if (*p1 == 'X') { def->nbytes = 1;  def->format = *p1; }
+  if (*p1 == 'L') { def->nbytes = 1;  def->format = *p1; }
+  if (*p1 == 'A') { def->nbytes = 1;  def->format = *p1; }
+  if (*p1 == 'B') { def->nbytes = 1;  def->format = *p1; }
+  if (*p1 == 'I') { def->nbytes = 2;  def->format = *p1; }
+  if (*p1 == 'J') { def->nbytes = 4;  def->format = *p1; }
+  if (*p1 == 'E') { def->nbytes = 4;  def->format = *p1; }
+  if (*p1 == 'D') { def->nbytes = 8;  def->format = *p1; }
+  if (*p1 == 'C') { def->nbytes = 8;  def->format = *p1; }
+  if (*p1 == 'M') { def->nbytes = 16; def->format = *p1; }
+  if (!def->nbytes) { return (FALSE); }
+  
+  /* scan columns to find column offset */
+  def->Nstart = 0;
+  for (i = 1; i < column; i++) {
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (ftable->header, field, "%s", 1, format);
+    gfits_bintable_format (format, tmpline, &Nv, &Nb);
+    def->Nstart += Nv*Nb;
+  }
+
+  if (!gfits_scan (ftable->header, "THEAP", "%d", 1, &def->heap_start)) {
+    def->heap_start = ftable->header->Naxis[0]*ftable->header->Naxis[1];
+  }
+
+  return TRUE;
+}
+
+void *gfits_varlength_column_pointer (FTable *ftable, VarLengthColumn *column, int row, int *length) {
+
+  void *result;
+  int offset, Nx, *ptr;
+
+  // find the values for the specified row
+  // the values in the main table for this row and varlength column:
+  Nx = ftable->header->Naxis[0];
+
+# define SWAP_WORD \
+  tmp = pchar[0]; pchar[0] = pchar[3]; pchar[3] = tmp; \
+  tmp = pchar[1]; pchar[1] = pchar[2]; pchar[2] = tmp;
+
+# ifdef BYTE_SWAP
+  char *pchar, tmp;
+  pchar = &ftable->buffer[row*Nx + column->Nstart];
+  SWAP_WORD;
+  pchar = &ftable->buffer[row*Nx + column->Nstart + 4];
+  SWAP_WORD;
+# endif
+
+  ptr = (int *) &ftable->buffer[row*Nx + column->Nstart];
+  *length = ptr[0];
+  offset = ptr[1];
+
+  result = (void *) (ftable->buffer + column->heap_start + offset);
+  return result;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_write_T.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_write_T.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_write_T.c	(revision 22322)
@@ -0,0 +1,115 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits write header *********************************/
+int gfits_write_table (char *filename, FTable *table) {
+  
+  FILE *f;
+  int status;
+  
+  status = TRUE;
+  
+  f = fopen (filename, "a+");
+  if (f == (FILE *) NULL) return (FALSE);
+  
+  status = fseek (f, 0, SEEK_END);  /* write table to end of file! */
+  status = gfits_fwrite_table (f, table);
+
+  fclose (f);
+  return (status);
+}	
+
+/*********************** fits write table *********************************/
+int gfits_fwrite_table (FILE *f, FTable *table) {
+  
+  int Nbytes;
+  
+  Nbytes = fwrite (table[0].buffer, sizeof(char), table[0].size, f);
+  
+  if (Nbytes != table[0].size) return (FALSE);
+  return (TRUE);
+}	
+
+/*********************** fits write virtual table *********************************/
+int gfits_fwrite_vtable (FILE *f, VTable *table) {
+  
+  int i, Nx, Ny, Npad, offset, start;
+  int Nbytes, *row, Nrow;
+  char *pad;
+
+  Nrow = table[0].Nrow;
+  row = table[0].row;
+  gfits_scan (table[0].header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (table[0].header, "NAXIS2", "%d", 1, &Ny);
+
+  /* file pointer is at beginning of desired table data */
+  start = ftell (f);
+  
+  for (i = 0; i < Nrow; i++) {
+    offset = start + Nx*row[i];
+    fseek (f, offset, SEEK_SET);
+    Nbytes = fwrite (table[0].buffer[i], sizeof (char), Nx, f);
+    if (Nbytes != Nx) { return (FALSE); }
+  }
+  
+  Npad = table[0].size - Nx*Ny;
+  ALLOCATE (pad, char, Npad);
+  bzero (pad, Npad);
+
+  offset = start + Nx*Ny;
+  fseek (f, offset, SEEK_SET);
+  Nbytes = fwrite (pad, sizeof (char), Npad, f);
+  if (Nbytes != Npad) { return (FALSE); }
+  free (pad);
+
+  return (TRUE);
+}	
+
+
+/* this will add data beyond the end of the table in the file if needed,
+   filling intervening gap with 0 */
+
+/*********************** fits read ftable data ***********************************/
+int gfits_fwrite_ftable_range (FILE *f, FTable *ftable, int start, int Nrows, int Ndisk, int Ntotal) {
+
+  int Nbytes, Nwrite, Nskip, Nx, Npad;
+  char *pad;
+
+  if (start < 0) return (FALSE);
+  
+  /* modify vtable to represent full disk table */
+  gfits_modify (ftable[0].header, "NAXIS2", "%d", 1, Ntotal);
+  ftable[0].header[0].Naxis[1] = Ntotal;
+
+  Nx = ftable[0].header[0].Naxis[0]; // final output table size on disk 
+  ftable[0].size = gfits_data_size (ftable[0].header);
+
+  Nskip = start * Nx;
+  Nbytes = Nrows * Nx;
+
+  // cursor must be at start of the table header
+  if (!gfits_fwrite_Theader (f, ftable[0].header)) {
+    fprintf (stderr, "can't write table header");
+    return (FALSE);
+  }
+
+  // cursor must be at start of the table (after table header)
+  fseek (f, Nskip, SEEK_CUR);
+  Nwrite = fwrite (ftable[0].buffer, sizeof (char), Nbytes, f);
+  if (Nwrite != Nbytes) {
+    return (FALSE);
+  }
+
+  if (Ntotal >= Ndisk) {
+    Npad = ftable[0].size - Nx*Ntotal;
+    ALLOCATE (pad, char, Npad);
+    bzero (pad, Npad);
+    Nbytes = fwrite (pad, sizeof (char), Npad, f);
+    free (pad);
+
+    if (Nbytes != Npad) return (FALSE); 
+  }
+
+  return (TRUE);
+}	
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_write_TH.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_write_TH.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libfits/table/F_write_TH.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+/*********************** fits write header *********************************/
+int gfits_write_Theader (char *filename, Header *header) {
+  
+  FILE *f;
+  int status;
+  
+  status = TRUE;
+  
+  f = fopen (filename, "a+");
+  if (f == (FILE *) NULL) return (FALSE);
+  
+  status = fseek (f, 0, SEEK_END);  /* write header to end of file! */
+  status = gfits_fwrite_Theader (f, header);
+
+  fclose (f);
+  return (status);
+}	
+
+/*********************** fits write header *********************************/
+int gfits_fwrite_Theader (FILE *f, Header *header) {
+  
+  int Nbytes;
+  
+  Nbytes = fwrite (header[0].buffer, sizeof(char), header[0].size, f);
+
+  if (Nbytes != header[0].size) return (FALSE);
+  return (TRUE);
+}	
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+lib
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/Makefile	(revision 22322)
@@ -0,0 +1,48 @@
+default: install
+help:
+	@echo "make options: install libkapa clean dist"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/libkapa
+LIB	= 	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	=	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS) -ldvo -lFITS -lohana
+
+install: $(DESTINC)/kapa.h $(DESTLIB)/libkapa.a $(DESTLIB)/libkapa.$(DLLTYPE)
+libkapa: $(LIB)/libkapa.$(ARCH).a $(LIB)/libkapa.$(ARCH).$(DLLTYPE)
+
+INCS = $(DESTINC)/kapa.h
+
+OBJS = \
+$(SRC)/IOfuncs.$(ARCH).o  \
+$(SRC)/KiiPicture.$(ARCH).o \
+$(SRC)/KiiOpen.$(ARCH).o \
+$(SRC)/KiiOverlay.$(ARCH).o \
+$(SRC)/KiiCursor.$(ARCH).o \
+$(SRC)/KiiConvert.$(ARCH).o \
+$(SRC)/KapaWindow.$(ARCH).o \
+$(SRC)/KapaColors.$(ARCH).o \
+$(SRC)/KapaOpen.$(ARCH).o
+
+DRAW = \
+$(SRC)/bDrawFuncs.$(ARCH).o     \
+$(SRC)/bDrawRotFont.$(ARCH).o   \
+$(SRC)/RotFont.$(ARCH).o        \
+$(SRC)/DrawRotString.$(ARCH).o  \
+$(SRC)/PSRotFont.$(ARCH).o
+
+$(OBJS): $(INCS)
+$(DRAW): $(INCS)
+
+$(LIB)/libkapa.$(ARCH).a: $(OBJS) $(DRAW)
+$(LIB)/libkapa.$(ARCH).$(DLLTYPE): $(OBJS) $(DRAW)
+
+$(DESTLIB)/libkapa.a:  $(LIB)/libkapa.$(ARCH).a  
+$(DESTLIB)/libkapa.$(DLLTYPE): $(LIB)/libkapa.$(ARCH).$(DLLTYPE)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,12 @@
+
+- libkapa 1.2 : 2006.08.23
+  * added KapaInitGraph
+  * added KapaColorName
+  * added KapaPrepPlot
+  * added KapaPlotVector
+
+- libkapa 1.1
+  * added bDrawCircleSingle to fix circles with weight
+  * fixed format of textline data message
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/doc/api.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/doc/api.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/doc/api.txt	(revision 22322)
@@ -0,0 +1,37 @@
+
+COMM | Kapa Function  	| libkapa file  | libkapa API 
+-----------------------------------------------------
+DBOX | LoadFrame      	| KapaWindow.c  | KapaBox
+ERSP | ErasePlots       | KapaWindow.c  | KapaClearPlots
+ERSS | EraseSections    | KapaWindow.c  | KapaClearSections
+ERSI | EraseImage       | KapaWindow.c  | KapaClearImage
+ERSC | EraseCurrentPlot | KapaWindow.c  | KapaClearCurrentPlot
+LSEC | ListSection    	| KapaWindow.c  | KapaGetSection
+PNGF | PNGit          	| KiiConvert.c  | KapaPNG
+PPMF | PPMit          	| KiiConvert.c  | KapaPPM
+PLOT | LoadObject     	| KapaWindow.c  | KapaPrepPlot
+SSEC | SetSection     	| KapaWindow.c  | KapaSelectSection
+LABL | LoadLabels     	| KapaWindow.c  | KapaSendLabel
+PTXT | LoadTextlines  	| KapaWindow.c  | KapaSendTextline
+FONT | SetFont        	| KapaWindow.c  | KapaSetFont
+SLIM | SetLimits      	| KapaWindow.c  | KapaSetLimits
+DSEC | DefineSection  	| KapaWindow.c  | KapaSetSection
+MSEC | MoveSection  	| KapaWindow.c  | KapaMoveSection
+CENT | Center           | KapaWindow.c  | KiiCenter
+QUIT | Quit           	| KiiOpen.c     | KiiClose
+NCUR | !ACTIVE_CURSOR 	| KiiCursor.c   | KiiCursorOff
+CURS | +ACTIVE_CURSOR 	| KiiCursor.c   | KiiCursorOn
+ERSO | EraseOverlay     | KiiOverlay.c  | KiiEraseOverlay
+JPEG | JPEGit24         | KiiConvert.c  | KiiJPEG
+LOAD | LoadOverlay      | KiiOverlay.c  | KiiLoadOverlay
+READ | LoadPicture      | KiiPicture.c  | KiiNewPicture1D
+READ | LoadPicture      | KiiPicture.c  | KiiNewPicture2D
+PSIT | PScommand      	| KiiConvert.c  | KiiPS
+RSIZ | Resize         	| KapaWindow.c  | KiiResize
+SAVE | SaveOverlay      | KiiOverlay.c  | KiiSaveOverlay
+CSVE | CSaveOverlay     | KiiOverlay.c  | KiiSaveOverlay
+SLIM | GetLimits      	| KapaWindow.c  | KapaGetLimits
+SSTY | SetGraphData	| KapaWindow.c 	| KapaSetGraphData
+GSTY | GetGraphData	| KapaWindow.c 	| KapaGetGraphData
+SIMD | SetImageData	| KapaWindow.c 	| KapaSetImageData
+GIMD | GetImageData	| KapaWindow.c 	| KapaGetImageData
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/doc/notes.txt	(revision 22322)
@@ -0,0 +1,31 @@
+
+KII library functions:
+
+KiiOpen ()
+KiiClose ()
+KiiNewPicture ()
+KiiLoadOverlay ()
+KiiLoadTickmarks ()
+KiiEraseOverlay ()
+KiiSaveOverlay ()
+KiiPS
+KiiJPEG
+KiiResize 
+
+
+-----------------------
+
+KapaOpen ()
+KapaClose
+KapaPS
+KapaPNG
+KapaPPM
+KapaResize
+KapaLoadObject
+KapaSetLimits
+KapaLoadLabels
+KapaSetSection
+KapaLoadText
+KapaSetFont
+KapaErase
+KapaLoadBox
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/include/alphabet.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/include/alphabet.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/include/alphabet.h	(revision 22322)
@@ -0,0 +1,53 @@
+# include "../rotfont/times8.h"
+# include "../rotfont/times12.h"
+# include "../rotfont/times14.h"
+# include "../rotfont/times18.h"
+# include "../rotfont/times24.h"
+
+# include "../rotfont/courier8.h"
+# include "../rotfont/courier12.h"
+# include "../rotfont/courier14.h"
+# include "../rotfont/courier18.h"
+# include "../rotfont/courier24.h"
+
+# include "../rotfont/helvetica8.h"
+# include "../rotfont/helvetica12.h"
+# include "../rotfont/helvetica14.h"
+# include "../rotfont/helvetica18.h"
+# include "../rotfont/helvetica24.h"
+
+# include "../rotfont/symbol8.h"
+# include "../rotfont/symbol12.h"
+# include "../rotfont/symbol14.h"
+# include "../rotfont/symbol18.h"
+# include "../rotfont/symbol24.h"
+
+# define DEFFONT 1
+static FontSet HardwiredFonts[] = {
+  {times8font,  "times", 8},
+  {times12font, "times", 12},
+  {times14font, "times", 14},
+  {times18font, "times", 18},
+  {times24font, "times", 24},
+  
+  {courier8font,  "courier", 8},
+  {courier12font, "courier", 12},
+  {courier14font, "courier", 14},
+  {courier18font, "courier", 18},
+  {courier24font, "courier", 24},
+  
+  {helvetica8font,  "helvetica", 8},
+  {helvetica12font, "helvetica", 12},
+  {helvetica14font, "helvetica", 14},
+  {helvetica18font, "helvetica", 18},
+  {helvetica24font, "helvetica", 24},
+  
+  {symbol8font,  "symbol", 8},
+  {symbol12font, "symbol", 12},
+  {symbol14font, "symbol", 14},
+  {symbol18font, "symbol", 18},
+  {symbol24font, "symbol", 24}
+};
+
+/* put these as static in RotFont.c with accessor functions */
+# define NROT 256
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/include/kapa.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/include/kapa.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/include/kapa.h	(revision 22322)
@@ -0,0 +1,262 @@
+# ifndef KAPA_H
+# define KAPA_H
+
+/* linux is happy with this, not solaris */
+# include <netinet/ip.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <X11/Xlib.h>
+# include <png.h>
+# include <dvo.h>
+
+typedef struct sockaddr_in KapaSockAddress;
+
+typedef struct {
+  float *data1d;
+  float **data2d;
+  int Nx;
+  int Ny;
+} KiiImage;
+
+typedef struct {
+  float x;
+  float y;
+  float dx;
+  float dy;
+  float angle;
+  int type;
+} KiiOverlayBase;
+
+typedef struct {
+  float x;
+  float y;
+  float dx;
+  float dy;
+  float angle;
+  int type;
+  char *text;
+} KiiOverlay;
+
+typedef enum {
+  KII_OVERLAY_NONE, 
+  KII_OVERLAY_TEXT, 
+  KII_OVERLAY_BOX, 
+  KII_OVERLAY_LINE,
+  KII_OVERLAY_CIRCLE
+} KiiOverlayType;
+
+typedef enum {
+  KAPA_LABEL_XM,
+  KAPA_LABEL_YM,
+  KAPA_LABEL_XP,
+  KAPA_LABEL_YP,
+  KAPA_LABEL_UL,
+  KAPA_LABEL_UR,
+  KAPA_LABEL_LL,
+  KAPA_LABEL_LR,
+} KapaLabelType;
+
+typedef enum {
+  KAPA_PS_NEWPLOT,
+  KAPA_PS_NEWPAGE,
+  KAPA_PS_RAWPAGE
+} KapaPSmode;
+
+typedef struct {
+  char *name;
+  float x;
+  float y;
+  float dx;
+  float dy;
+} KapaSection;
+
+typedef struct {
+  double xmin, xmax, ymin, ymax;
+  int style, ptype, ltype, etype, ebar, color;
+  double lweight, size;
+  Coords coords;
+  int flipeast, flipnorth;
+  char axis[8], labels[8], ticks[8];
+} Graphdata;
+
+typedef struct {
+  int logflux;
+  double zero, range;
+  char name[1024];
+  char file[1024];
+} KapaImageData;
+
+typedef struct {
+  int dx, dy, ascent;
+  unsigned char *bits;
+} RotFont;
+
+typedef struct {
+  RotFont *font;
+  char name[64];
+  int size;
+} FontSet;
+
+typedef png_byte bDrawColor;
+
+typedef struct {
+  int Nx, Ny;
+  bDrawColor **pixels;
+} bDrawBuffer;
+
+/* IOfuncs.c */
+int KiiSendMessage (int device, char *format, ...);
+int KiiScanMessage (int device, char *format, ...);
+int KiiSendCommand (int device, int length, char *format, ...);
+int KiiSendCommandV (int device, int length, char *format, va_list argp);
+int KiiScanCommand (int device, int length, char *format, ...);
+int KiiSendData (int device, char *data, int Nbytes);
+char *KiiRecvData (int device);
+int KiiWaitAnswer (int device, char *expect);
+
+/* KiiOpen.c */
+int KiiOpen (char *kii_exec, char *name);
+int KiiClose (int socket);
+int KiiWait (char *sockpath);
+
+/* KiiPicture.c */
+int KiiSetChannel (int fd, int channel);
+int KiiSetColormap (int fd, char *colormap);
+int KiiNewPicture1D (int fd, KiiImage *image, KapaImageData *data, Coords *coords);
+int KiiNewPicture2D (int fd, KiiImage *image, KapaImageData *data, Coords *coords);
+
+int KapaSetImageCoords (int fd, Coords *coords);
+int KapaGetImageCoords (int fd, Coords *coords);
+int KapaGetImageRange (int fd, double *Xmin, double *Xmax, double *Ymin, double *Ymax);
+
+/* KiiOverlay.c */
+int KiiSelectOverlay (char *name, int *number);
+int KiiLoadOverlay (int fd, KiiOverlay *overlay, int Noverlay, char *name);
+int KiiEraseOverlay (int fd, char *name);
+int KiiSaveOverlay (int fd, int celestial, char *name, char *file);
+int KiiOverlayTypeByName (char *name);
+char *KiiOverlayTypeByNumber (int n);
+
+/* KiiConvert.c */
+int KiiPS (int fd, const char *filename, int scaleMode, int pageMode, char *pagename);
+int KiiJPEG (int fd, const char *filename);
+int KapaPNG (int fd, const char *filename);
+int KapaPPM (int fd, const char *filename);
+
+/* KiiCursor.c */
+int KiiCursorOn (int fd);
+int KiiCursorOff (int fd);
+int KiiCursorRead (int fd, double *x, double *y, double *z, double *r, double *d, char *key);
+
+/* KapaWindow.c */
+int KiiResize (int fd, int Nx, int Ny);
+int KiiCenter (int fd, double x, double y, int zoom);
+int KapaBox (int fd, Graphdata *graphdata);
+int KapaClearCurrentPlot (int fd);
+int KapaClearPlots (int fd);
+int KapaClearSections (int fd);
+int KapaClearImage (int fd);
+int KapaInitGraph (Graphdata *graphdata);
+int KapaPrepPlot (int fd, int Npts, Graphdata *graphmode);
+int KapaPlotVector (int fd, int Npts, float *values, char *type);
+int KapaSetFont (int fd, char *name, int size);
+int KapaSendLabel (int fd, char *string, int mode);
+int KapaSendTextline (int fd, char *string, float x, float y, float angle);
+int KapaSetLimits (int fd, Graphdata *graphmode);
+int KapaGetLimits (int fd, float *dx, float *dy);
+int KapaSetSection (int fd, KapaSection *section);
+int KapaSelectSection (int fd, char *name);
+int KapaGetSection (int fd, char *name);
+int KapaMoveSection (int fd, char *name, char *direction);
+int KapaSetGraphData (int fd, Graphdata *graphmode);
+int KapaGetGraphData (int fd, Graphdata *graphmode);
+int KapaSetImageData (int fd, KapaImageData *graphmode);
+int KapaGetImageData (int fd, KapaImageData *graphmode);
+int KapaSetToolbox (int fd, int location);
+
+/* KapaColors */
+int KapaColorByName (char *name);
+int KapaColormapSize ();
+char *KapaColorRGBString (int N);
+char *KapaColorName (int N);
+png_color *KapaPNGPalette (int *Npalette);
+unsigned long *KapaX11colors (Display *display, Colormap colormap, unsigned long default_color, int *Ncolors);
+
+/* RotFont.c */
+void InitRotFonts PROTO(());
+int SetRotFont PROTO((char *name, int size));
+char *GetRotFont PROTO((int *size));
+RotFont *GetRotFontData (double *scale);
+int RotStrlen PROTO((char *c));
+
+/* DrawRotString.c */
+int DrawRotText PROTO((int x, int y, char *string, int pos, double angle));
+int DrawRotBitmap PROTO((int x, int y, int dx, int dy, unsigned char *bitmap, int mode, double angle, double scale));
+int DrawRotTextInit (Display *display, Window window, GC gc, unsigned long fore, unsigned long back);
+
+
+/* PSRotFont.c */
+void PSRotText PROTO((FILE *f, int x, int y, char *string, int pos, double angle));
+void PSDumpRotSegment PROTO((FILE *f, char *segment, int *Nseg));
+void PSSetFont PROTO((FILE *f, char *name, int size));
+
+/* bDrawFuncs.c */
+bDrawBuffer *bDrawBufferCreate (int Nx, int Ny);
+void bDrawBufferFree (bDrawBuffer *buffer);
+void bDrawSetBuffer (bDrawBuffer *buffer);
+void bDrawSetStyle (bDrawColor color, int lw, int lt);
+void bDrawPoint (int x, int y);
+void bDrawPointf (float x, float y);
+
+void bDrawArc (double Xc, double Yc, double Xr, double Yr, double Ts, double Te);
+void bDrawCircle (double Xc, double Yc, double radius);
+void bDrawCircleFill (double xc, double yc, double radius);
+
+void bDrawLine (double x1, double y1, double x2, double y2);
+void bDrawLineWeight (int X1, int Y1, int X2, int Y2, int swapcoords);
+void bDrawLineBresen (int X1, int Y1, int X2, int Y2, int swapcoords);
+void bDrawLineHorizontal (int X1, int X2, int Y);
+void bDrawLineVertical (int X, int Y1, int Y2);
+
+void bDrawRectOpen (double x1, double y1, double x2, double y2);
+void bDrawRectFill (double x1, double y1, double x2, double y2);
+void bDrawTriOpen (double x1, double y1, double x2, double y2, double x3, double y3);
+void bDrawTriFill (double x1, double y1, double x2, double y2, double x3, double y3);
+
+/* bDrawRotFont.c */
+int bDrawRotText (int x, int y, char *string, int pos, double angle);
+int bDrawRotBitmap (int x, int y, int dx, int dy, unsigned char *bitmap, int mode, double angle, double scale);
+
+/* Kapa Socket functions */
+int KapaOpen (char *kapa_exec, char *kapa_name);
+int KapaServerInit (KapaSockAddress *Address);
+int KapaServerWait (int InitSocket, KapaSockAddress *Address);
+int KapaDefineValidIP (char *ipstring);
+int KapaClientSocket (char *hostname);
+int KapaOpenNamedSocket (char *kapa_exec, char *name);
+int KapaWaitNamedSocket (char *sockpath);
+
+/* define Kapa names for shared functions */
+// # define KapaOpen(p,n) KiiOpen(p,n)
+# define KapaResize(fd,Nx,Ny) KiiResize(fd,Nx,Ny)
+# define KapaCenter(fd,x,y,z) KiiResize(fd,x,y,z)
+# define KapaCursorOn(fd) KiiCursorOn(fd)
+# define KapaCursorOff(fd) KiiCursorOff(fd)
+# define KapaCursorRead(fd,x,y,k) KiiCursorRead(fd,x,y,k)
+# define KapaPS(fd,s,r,f) KiiPS(fd,s,r,f)
+
+# define KapaClose(socket) KiiClose(socket)
+# define KapaWait(sockpath) KiiWait(sockpath)
+# define KapaSendCommandV(device, length, format, argp) KiiSendCommandV(device, length, format, argp)
+# define KapaSendData(device, data, Nbytes) KiiSendData(device, data, Nbytes)
+# define KapaRecvData(device) KiiRecvData(device)
+
+/* these use varargs: is this safe, or should we make a stub function? */
+# define KapaSendMessage KiiSendMessage
+# define KapaSendCommand KiiSendCommand
+# define KapaScanMessage KiiScanMessage
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/include/kapa_internal.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/include/kapa_internal.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/include/kapa_internal.h	(revision 22322)
@@ -0,0 +1,18 @@
+# include <signal.h>
+# include <unistd.h>
+# include <sys/uio.h>
+# include <fcntl.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <sys/time.h>
+# include <time.h>
+# include <errno.h>
+
+# include <ohana.h>
+# include <dvo.h>
+# include <kapa.h>
+
+/* do we need to move the Coords structure outside 
+   of libautocode? */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/lib/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/lib/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/lib/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.darwin.dylib
+*.darwin_x86.dylib
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+fixfont
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/ReadMe
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/ReadMe	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/ReadMe	(revision 22322)
@@ -0,0 +1,10 @@
+start font server (as root): 
+ xfs &
+
+dump font to bdf file:
+
+fstobdf -s localhost:7100 -fn "-adobe-times-medium-r-*-*-12-*-*-*-*-*-*-*" > times12.bdf
+
+convert to .h file:
+ fixfont times12.bdf times12 > times12.h
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/alphabet.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/alphabet.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/alphabet.h	(revision 22322)
@@ -0,0 +1,69 @@
+typedef struct {
+  int dx, dy, ascent;
+  char *bits;
+} RotFont;
+
+typedef struct {
+  RotFont *font;
+  char name[64];
+  int size;
+} FontSet;
+
+# include "../rotfont/times8.h"
+# include "../rotfont/times12.h"
+# include "../rotfont/times14.h"
+# include "../rotfont/times18.h"
+# include "../rotfont/times24.h"
+
+# include "../rotfont/courier8.h"
+# include "../rotfont/courier12.h"
+# include "../rotfont/courier14.h"
+# include "../rotfont/courier18.h"
+# include "../rotfont/courier24.h"
+
+# include "../rotfont/helvetica8.h"
+# include "../rotfont/helvetica12.h"
+# include "../rotfont/helvetica14.h"
+# include "../rotfont/helvetica18.h"
+# include "../rotfont/helvetica24.h"
+
+# include "../rotfont/symbol8.h"
+# include "../rotfont/symbol12.h"
+# include "../rotfont/symbol14.h"
+# include "../rotfont/symbol18.h"
+# include "../rotfont/symbol24.h"
+
+# define DEFFONT 1
+static FontSet HardwiredFonts[] = {
+  {times8font,  "times", 8},
+  {times12font, "times", 12},
+  {times14font, "times", 14},
+  {times18font, "times", 18},
+  {times24font, "times", 24},
+  
+  {courier8font,  "courier", 8},
+  {courier12font, "courier", 12},
+  {courier14font, "courier", 14},
+  {courier18font, "courier", 18},
+  {courier24font, "courier", 24},
+  
+  {helvetica8font,  "helvetica", 8},
+  {helvetica12font, "helvetica", 12},
+  {helvetica14font, "helvetica", 14},
+  {helvetica18font, "helvetica", 18},
+  {helvetica24font, "helvetica", 24},
+  
+  {symbol8font,  "symbol", 8},
+  {symbol12font, "symbol", 12},
+  {symbol14font, "symbol", 14},
+  {symbol18font, "symbol", 18},
+  {symbol24font, "symbol", 24}
+};
+
+int Nallfonts;
+FontSet *AllFonts;
+RotFont *activefont;
+double activescale;
+
+char currentname[64];
+int  currentsize;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/blank.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/blank.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/blank.h	(revision 22322)
@@ -0,0 +1,4 @@
+#define blank_width 5
+#define blank_height 1
+static unsigned char blank_bits[] = {
+   0x00};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier12.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier12.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier12.bdf	(revision 22322)
@@ -0,0 +1,2839 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) IBM Corporation 1990,1991. IBM Courier is a Trademark of the IBM Corporation.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -adobe-courier-medium-r-normal--12-116-75-75-m-70-iso8859-1
+SIZE 12 75 75
+FONTBOUNDINGBOX 8 12 0 -2
+STARTPROPERTIES 25
+FOUNDRY "adobe"
+FAMILY_NAME "courier"
+WEIGHT_NAME "medium"
+SLANT "r"
+SETWIDTH_NAME "normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 12
+POINT_SIZE 116
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "m"
+AVERAGE_WIDTH 70
+CHARSET_REGISTRY "iso8859"
+CHARSET_ENCODING "1"
+FONT "-adobe-courier-medium-r-normal--12-116-75-75-m-70-iso8859-1"
+COPYRIGHT "Copyright (c) IBM Corporation 1990,1991. IBM Courier is a Trademark of the IBM Corporation."
+RAW_PIXEL_SIZE 1000
+RAW_POINT_SIZE 964
+RAW_ASCENT 841
+RAW_DESCENT 288
+RAW_AVERAGE_WIDTH 6000
+FACE_NAME "Couriere."
+DEFAULT_CHAR 0
+FONT_ASCENT 10
+FONT_DESCENT 3
+ENDPROPERTIES
+CHARS 190
+STARTCHAR space
+ENCODING 32
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 0 0 0 0
+ATTRIBUTES 0x0258
+BITMAP
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 7 3 0
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+80
+80
+80
+00
+40
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 3 2 4
+ATTRIBUTES 0x0258
+BITMAP
+a0
+a0
+a0
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+50
+50
+50
+f8
+50
+f8
+50
+50
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 -2
+ATTRIBUTES 0x0258
+BITMAP
+20
+60
+b8
+a8
+60
+38
+a8
+f0
+20
+20
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+90
+6c
+70
+4c
+12
+1c
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+30
+48
+40
+28
+e8
+90
+68
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 3 2 4
+ATTRIBUTES 0x0258
+BITMAP
+40
+80
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 9 3 -1
+ATTRIBUTES 0x0258
+BITMAP
+40
+40
+40
+80
+80
+80
+40
+40
+40
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 9 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+40
+40
+40
+40
+40
+80
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 4 1 3
+ATTRIBUTES 0x0258
+BITMAP
+20
+f0
+20
+50
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+ATTRIBUTES 0x0258
+BITMAP
+20
+20
+f8
+20
+20
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 3 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+40
+80
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 1 1 3
+ATTRIBUTES 0x0258
+BITMAP
+fc
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 2 3 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 -2
+ATTRIBUTES 0x0258
+BITMAP
+08
+08
+10
+10
+20
+20
+20
+40
+40
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+88
+88
+88
+88
+c8
+70
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+08
+08
+10
+20
+48
+f8
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+10
+10
+30
+08
+08
+f0
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+30
+50
+50
+f8
+10
+78
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+80
+f0
+88
+08
+08
+70
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+38
+40
+b0
+c8
+88
+88
+70
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+08
+10
+10
+10
+20
+20
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+88
+88
+70
+88
+88
+70
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+88
+88
+78
+08
+10
+e0
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 5 3 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+00
+c0
+40
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 6 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+60
+20
+00
+60
+40
+40
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 1 1
+ATTRIBUTES 0x0258
+BITMAP
+18
+20
+e0
+18
+04
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 3 1 2
+ATTRIBUTES 0x0258
+BITMAP
+fc
+00
+fc
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 1 1
+ATTRIBUTES 0x0258
+BITMAP
+60
+10
+1c
+e0
+80
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+88
+08
+10
+20
+00
+20
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+38
+44
+9a
+aa
+aa
+7c
+3c
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+28
+28
+28
+7c
+42
+c6
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+88
+88
+f8
+84
+84
+f8
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+7c
+44
+80
+80
+80
+c4
+78
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+44
+42
+42
+42
+44
+f8
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+fc
+44
+50
+70
+50
+44
+fc
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+fc
+44
+50
+70
+50
+40
+e0
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+3c
+44
+80
+80
+8e
+42
+3e
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+ee
+44
+44
+7c
+44
+44
+ee
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+7c
+08
+08
+08
+88
+88
+f8
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+cc
+48
+50
+60
+50
+48
+c4
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+40
+40
+40
+44
+44
+fc
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+c6
+6a
+6a
+6a
+52
+42
+e6
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+ee
+62
+52
+52
+4a
+4a
+e6
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+38
+44
+82
+82
+82
+44
+38
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+44
+44
+4c
+78
+40
+e0
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+38
+44
+82
+82
+82
+44
+38
+30
+2e
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+88
+98
+e0
+90
+88
+cc
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+88
+80
+70
+08
+88
+f0
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+fe
+92
+92
+10
+10
+10
+38
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+ee
+44
+44
+44
+44
+44
+38
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+ee
+44
+64
+28
+28
+28
+10
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+c6
+52
+52
+6a
+6a
+6a
+44
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+6c
+28
+28
+10
+28
+44
+e6
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+cc
+48
+48
+50
+20
+20
+78
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+90
+90
+20
+48
+88
+f8
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 9 4 -1
+ATTRIBUTES 0x0258
+BITMAP
+c0
+80
+80
+80
+80
+80
+80
+80
+c0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 -2
+ATTRIBUTES 0x0258
+BITMAP
+80
+40
+40
+20
+20
+20
+10
+10
+08
+08
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 9 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+e0
+20
+20
+20
+20
+20
+20
+20
+e0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 6 1 1
+ATTRIBUTES 0x0258
+BITMAP
+20
+50
+50
+50
+88
+88
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 8 1 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+ff
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 3 2 4
+ATTRIBUTES 0x0258
+BITMAP
+40
+40
+80
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+78
+88
+88
+fc
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+40
+7c
+42
+42
+42
+fc
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+84
+80
+80
+78
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+1c
+04
+7c
+84
+84
+c4
+3e
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+8c
+fc
+80
+78
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+7c
+40
+f8
+40
+40
+40
+f8
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+7e
+84
+84
+c4
+3c
+04
+38
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+40
+7c
+44
+44
+44
+ee
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 8 1 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+00
+00
+60
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 4 10 1 -2
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+00
+f0
+10
+10
+10
+10
+10
+e0
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+40
+4e
+58
+70
+48
+4e
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+fe
+92
+92
+92
+da
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+fc
+44
+44
+44
+ee
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+7c
+82
+82
+c6
+3c
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+fc
+42
+42
+46
+7c
+40
+f0
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+7e
+c4
+84
+c4
+3c
+04
+0e
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+fc
+40
+40
+40
+f0
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+88
+f0
+c8
+b0
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+40
+f8
+40
+40
+40
+78
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+cc
+44
+44
+44
+3e
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+e6
+44
+24
+28
+10
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+c6
+52
+6a
+6c
+24
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+66
+2c
+10
+6c
+c6
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+c6
+44
+24
+28
+10
+20
+f0
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+10
+20
+48
+f8
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 9 3 -1
+ATTRIBUTES 0x0258
+BITMAP
+60
+80
+80
+40
+80
+40
+40
+80
+60
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 1 10 3 -2
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 9 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+80
+40
+40
+40
+40
+40
+40
+40
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 2 1 3
+ATTRIBUTES 0x0258
+BITMAP
+68
+90
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 7 3 -2
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 8 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+20
+78
+a8
+a0
+a0
+70
+20
+20
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+38
+40
+40
+f8
+40
+40
+78
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+ATTRIBUTES 0x0258
+BITMAP
+f8
+88
+88
+88
+f8
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+cc
+48
+50
+78
+78
+20
+78
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 1 10 3 -2
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+80
+80
+00
+00
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 8 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+78
+48
+60
+90
+48
+38
+90
+f0
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 1 2 6
+ATTRIBUTES 0x0258
+BITMAP
+a0
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+38
+7c
+c6
+c2
+e6
+7c
+38
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 4 2 3
+ATTRIBUTES 0x0258
+BITMAP
+60
+e0
+e0
+e0
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+28
+50
+a0
+50
+28
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 3 1 2
+ATTRIBUTES 0x0258
+BITMAP
+f8
+08
+08
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 1 2 3
+ATTRIBUTES 0x0258
+BITMAP
+e0
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+38
+7c
+aa
+b2
+ba
+46
+38
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 4 1 2 6
+ATTRIBUTES 0x0258
+BITMAP
+f0
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 4 3 2 4
+ATTRIBUTES 0x0258
+BITMAP
+e0
+90
+e0
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+20
+f8
+20
+20
+00
+f8
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 4 2 3
+ATTRIBUTES 0x0258
+BITMAP
+e0
+20
+40
+e0
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 4 2 3
+ATTRIBUTES 0x0258
+BITMAP
+60
+40
+20
+e0
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 1 3 6
+ATTRIBUTES 0x0258
+BITMAP
+40
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+cc
+44
+44
+44
+7e
+40
+40
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 8 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+78
+a8
+a8
+68
+28
+28
+28
+28
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 2 2 3 3
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 3 2 -2
+ATTRIBUTES 0x0258
+BITMAP
+40
+60
+60
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 4 2 3
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+40
+e0
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 4 2 3
+ATTRIBUTES 0x0258
+BITMAP
+e0
+a0
+e0
+e0
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+a0
+50
+28
+50
+a0
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 -1
+ATTRIBUTES 0x0258
+BITMAP
+02
+42
+46
+4c
+ea
+16
+26
+4e
+46
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 -1
+ATTRIBUTES 0x0258
+BITMAP
+02
+46
+44
+4c
+fe
+1a
+22
+44
+4e
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 -1
+ATTRIBUTES 0x0258
+BITMAP
+02
+62
+66
+24
+ea
+16
+26
+4e
+46
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 -2
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+20
+c0
+80
+88
+78
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+70
+28
+28
+28
+7c
+42
+c6
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+08
+00
+70
+28
+28
+28
+7c
+42
+c6
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+70
+28
+28
+28
+7c
+42
+c6
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+3c
+00
+70
+28
+28
+28
+7c
+42
+c6
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+28
+00
+70
+28
+28
+28
+7c
+42
+c6
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 10 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+28
+10
+70
+28
+28
+28
+7c
+42
+c6
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+3e
+12
+36
+3c
+74
+52
+de
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 1 -2
+ATTRIBUTES 0x0258
+BITMAP
+7c
+8c
+80
+80
+80
+86
+78
+10
+30
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 1 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+00
+fc
+44
+50
+70
+50
+44
+fc
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 1 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+fc
+44
+50
+70
+50
+44
+fc
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 1 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+00
+fc
+44
+50
+70
+50
+44
+fc
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 1 0
+ATTRIBUTES 0x0258
+BITMAP
+50
+00
+fc
+44
+50
+70
+50
+44
+fc
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+00
+f8
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+f8
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+00
+f8
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+ATTRIBUTES 0x0258
+BITMAP
+50
+00
+f8
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+44
+42
+f2
+42
+44
+f8
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+3c
+00
+ee
+62
+52
+52
+4a
+4a
+e6
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+38
+44
+82
+82
+82
+44
+38
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+08
+00
+38
+44
+82
+82
+82
+44
+38
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+38
+44
+82
+82
+82
+44
+38
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+3c
+00
+38
+44
+82
+82
+82
+44
+38
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+28
+00
+38
+44
+82
+82
+82
+44
+38
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 4 5 2 1
+ATTRIBUTES 0x0258
+BITMAP
+90
+70
+20
+d0
+90
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+3a
+44
+8a
+92
+92
+64
+78
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+ee
+44
+44
+44
+44
+44
+38
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+08
+00
+ee
+44
+44
+44
+44
+44
+38
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+ee
+44
+44
+44
+44
+44
+38
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+ATTRIBUTES 0x0258
+BITMAP
+28
+00
+ee
+44
+44
+44
+44
+44
+38
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 1 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+cc
+48
+48
+50
+20
+20
+78
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+78
+4c
+44
+4c
+78
+e0
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+88
+b0
+88
+88
+88
+b0
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+00
+70
+78
+88
+88
+fc
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+70
+78
+88
+88
+fc
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+00
+70
+78
+88
+88
+fc
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+00
+70
+78
+88
+88
+fc
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+50
+00
+70
+78
+88
+88
+fc
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 8 1 0
+ATTRIBUTES 0x0258
+BITMAP
+30
+70
+00
+70
+78
+88
+88
+fc
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+7c
+12
+7e
+90
+6e
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 -2
+ATTRIBUTES 0x0258
+BITMAP
+7c
+84
+80
+80
+78
+20
+30
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+78
+8c
+fc
+80
+78
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+78
+8c
+fc
+80
+78
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+00
+78
+8c
+fc
+80
+78
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+50
+00
+78
+8c
+fc
+80
+78
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+00
+60
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+60
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+00
+60
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+ATTRIBUTES 0x0258
+BITMAP
+50
+00
+60
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 8 0 0
+ATTRIBUTES 0x0258
+BITMAP
+30
+28
+0c
+7e
+82
+82
+c6
+3c
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+3c
+00
+fc
+44
+44
+44
+ee
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+7c
+82
+82
+c6
+3c
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+08
+00
+7c
+82
+82
+c6
+3c
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+38
+00
+7c
+82
+82
+c6
+3c
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+3c
+00
+7c
+82
+82
+c6
+3c
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+28
+00
+7c
+82
+82
+c6
+3c
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+ATTRIBUTES 0x0258
+BITMAP
+20
+00
+f8
+00
+20
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+7e
+8a
+92
+e6
+9c
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+cc
+44
+44
+44
+3e
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+08
+00
+cc
+44
+44
+44
+3e
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+38
+00
+cc
+44
+44
+44
+3e
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+28
+00
+cc
+44
+44
+44
+3e
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+08
+00
+c6
+44
+24
+28
+10
+20
+f0
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+40
+40
+7c
+42
+42
+46
+7c
+40
+f0
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+28
+00
+c6
+44
+24
+28
+10
+20
+f0
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier12.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier12.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier12.h	(revision 22322)
@@ -0,0 +1,768 @@
+static unsigned char courier12_0_bits[] = {
+0x00};
+static unsigned char courier12_1_bits[] = {
+0x00};
+static unsigned char courier12_2_bits[] = {
+0x00};
+static unsigned char courier12_3_bits[] = {
+0x00};
+static unsigned char courier12_4_bits[] = {
+0x00};
+static unsigned char courier12_5_bits[] = {
+0x00};
+static unsigned char courier12_6_bits[] = {
+0x00};
+static unsigned char courier12_7_bits[] = {
+0x00};
+static unsigned char courier12_8_bits[] = {
+0x00};
+static unsigned char courier12_9_bits[] = {
+0x00};
+static unsigned char courier12_10_bits[] = {
+0x00};
+static unsigned char courier12_11_bits[] = {
+0x00};
+static unsigned char courier12_12_bits[] = {
+0x00};
+static unsigned char courier12_13_bits[] = {
+0x00};
+static unsigned char courier12_14_bits[] = {
+0x00};
+static unsigned char courier12_15_bits[] = {
+0x00};
+static unsigned char courier12_16_bits[] = {
+0x00};
+static unsigned char courier12_17_bits[] = {
+0x00};
+static unsigned char courier12_18_bits[] = {
+0x00};
+static unsigned char courier12_19_bits[] = {
+0x00};
+static unsigned char courier12_20_bits[] = {
+0x00};
+static unsigned char courier12_21_bits[] = {
+0x00};
+static unsigned char courier12_22_bits[] = {
+0x00};
+static unsigned char courier12_23_bits[] = {
+0x00};
+static unsigned char courier12_24_bits[] = {
+0x00};
+static unsigned char courier12_25_bits[] = {
+0x00};
+static unsigned char courier12_26_bits[] = {
+0x00};
+static unsigned char courier12_27_bits[] = {
+0x00};
+static unsigned char courier12_28_bits[] = {
+0x00};
+static unsigned char courier12_29_bits[] = {
+0x00};
+static unsigned char courier12_30_bits[] = {
+0x00};
+static unsigned char courier12_31_bits[] = {
+0x00};
+static unsigned char courier12_32_bits[] = {};
+static unsigned char courier12_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02};
+static unsigned char courier12_34_bits[] = {
+0x05, 0x05, 0x05};
+static unsigned char courier12_35_bits[] = {
+0x0a, 0x0a, 0x0a, 0x1f, 0x0a, 0x1f, 0x0a, 0x0a, 0x0a};
+static unsigned char courier12_36_bits[] = {
+0x04, 0x06, 0x1d, 0x15, 0x06, 0x1c, 0x15, 0x0f, 0x04, 0x04};
+static unsigned char courier12_37_bits[] = {
+0x0e, 0x09, 0x36, 0x0e, 0x32, 0x48, 0x38};
+static unsigned char courier12_38_bits[] = {
+0x0c, 0x12, 0x02, 0x14, 0x17, 0x09, 0x16};
+static unsigned char courier12_39_bits[] = {
+0x02, 0x01, 0x01};
+static unsigned char courier12_40_bits[] = {
+0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02};
+static unsigned char courier12_41_bits[] = {
+0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01};
+static unsigned char courier12_42_bits[] = {
+0x04, 0x0f, 0x04, 0x0a};
+static unsigned char courier12_43_bits[] = {
+0x04, 0x04, 0x1f, 0x04, 0x04};
+static unsigned char courier12_44_bits[] = {
+0x02, 0x01, 0x01};
+static unsigned char courier12_45_bits[] = {
+0x3f};
+static unsigned char courier12_46_bits[] = {
+0x03, 0x02};
+static unsigned char courier12_47_bits[] = {
+0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x04, 0x02, 0x02, 0x01};
+static unsigned char courier12_48_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x11, 0x13, 0x0e};
+static unsigned char courier12_49_bits[] = {
+0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_50_bits[] = {
+0x0f, 0x10, 0x10, 0x08, 0x04, 0x12, 0x1f};
+static unsigned char courier12_51_bits[] = {
+0x0f, 0x08, 0x08, 0x0c, 0x10, 0x10, 0x0f};
+static unsigned char courier12_52_bits[] = {
+0x08, 0x0c, 0x0a, 0x0a, 0x1f, 0x08, 0x1e};
+static unsigned char courier12_53_bits[] = {
+0x0f, 0x01, 0x0f, 0x11, 0x10, 0x10, 0x0e};
+static unsigned char courier12_54_bits[] = {
+0x1c, 0x02, 0x0d, 0x13, 0x11, 0x11, 0x0e};
+static unsigned char courier12_55_bits[] = {
+0x1f, 0x10, 0x08, 0x08, 0x08, 0x04, 0x04};
+static unsigned char courier12_56_bits[] = {
+0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e};
+static unsigned char courier12_57_bits[] = {
+0x0e, 0x11, 0x11, 0x1e, 0x10, 0x08, 0x07};
+static unsigned char courier12_58_bits[] = {
+0x03, 0x02, 0x00, 0x03, 0x02};
+static unsigned char courier12_59_bits[] = {
+0x06, 0x04, 0x00, 0x06, 0x02, 0x02};
+static unsigned char courier12_60_bits[] = {
+0x18, 0x04, 0x07, 0x18, 0x20};
+static unsigned char courier12_61_bits[] = {
+0x3f, 0x00, 0x3f};
+static unsigned char courier12_62_bits[] = {
+0x06, 0x08, 0x38, 0x07, 0x01};
+static unsigned char courier12_63_bits[] = {
+0x0f, 0x11, 0x10, 0x08, 0x04, 0x00, 0x04};
+static unsigned char courier12_64_bits[] = {
+0x1c, 0x22, 0x59, 0x55, 0x55, 0x3e, 0x3c};
+static unsigned char courier12_65_bits[] = {
+0x0e, 0x14, 0x14, 0x14, 0x3e, 0x42, 0x63};
+static unsigned char courier12_66_bits[] = {
+0x0f, 0x11, 0x11, 0x1f, 0x21, 0x21, 0x1f};
+static unsigned char courier12_67_bits[] = {
+0x3e, 0x22, 0x01, 0x01, 0x01, 0x23, 0x1e};
+static unsigned char courier12_68_bits[] = {
+0x1f, 0x22, 0x42, 0x42, 0x42, 0x22, 0x1f};
+static unsigned char courier12_69_bits[] = {
+0x3f, 0x22, 0x0a, 0x0e, 0x0a, 0x22, 0x3f};
+static unsigned char courier12_70_bits[] = {
+0x3f, 0x22, 0x0a, 0x0e, 0x0a, 0x02, 0x07};
+static unsigned char courier12_71_bits[] = {
+0x3c, 0x22, 0x01, 0x01, 0x71, 0x42, 0x7c};
+static unsigned char courier12_72_bits[] = {
+0x77, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x77};
+static unsigned char courier12_73_bits[] = {
+0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_74_bits[] = {
+0x3e, 0x10, 0x10, 0x10, 0x11, 0x11, 0x1f};
+static unsigned char courier12_75_bits[] = {
+0x33, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x23};
+static unsigned char courier12_76_bits[] = {
+0x07, 0x02, 0x02, 0x02, 0x22, 0x22, 0x3f};
+static unsigned char courier12_77_bits[] = {
+0x63, 0x56, 0x56, 0x56, 0x4a, 0x42, 0x67};
+static unsigned char courier12_78_bits[] = {
+0x77, 0x46, 0x4a, 0x4a, 0x52, 0x52, 0x67};
+static unsigned char courier12_79_bits[] = {
+0x1c, 0x22, 0x41, 0x41, 0x41, 0x22, 0x1c};
+static unsigned char courier12_80_bits[] = {
+0x1f, 0x22, 0x22, 0x32, 0x1e, 0x02, 0x07};
+static unsigned char courier12_81_bits[] = {
+0x1c, 0x22, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x0c, 0x74};
+static unsigned char courier12_82_bits[] = {
+0x0f, 0x11, 0x19, 0x07, 0x09, 0x11, 0x33};
+static unsigned char courier12_83_bits[] = {
+0x1e, 0x11, 0x01, 0x0e, 0x10, 0x11, 0x0f};
+static unsigned char courier12_84_bits[] = {
+0x7f, 0x49, 0x49, 0x08, 0x08, 0x08, 0x1c};
+static unsigned char courier12_85_bits[] = {
+0x77, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c};
+static unsigned char courier12_86_bits[] = {
+0x77, 0x22, 0x26, 0x14, 0x14, 0x14, 0x08};
+static unsigned char courier12_87_bits[] = {
+0x63, 0x4a, 0x4a, 0x56, 0x56, 0x56, 0x22};
+static unsigned char courier12_88_bits[] = {
+0x36, 0x14, 0x14, 0x08, 0x14, 0x22, 0x67};
+static unsigned char courier12_89_bits[] = {
+0x33, 0x12, 0x12, 0x0a, 0x04, 0x04, 0x1e};
+static unsigned char courier12_90_bits[] = {
+0x1f, 0x09, 0x09, 0x04, 0x12, 0x11, 0x1f};
+static unsigned char courier12_91_bits[] = {
+0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03};
+static unsigned char courier12_92_bits[] = {
+0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10};
+static unsigned char courier12_93_bits[] = {
+0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x07};
+static unsigned char courier12_94_bits[] = {
+0x04, 0x0a, 0x0a, 0x0a, 0x11, 0x11};
+static unsigned char courier12_95_bits[] = {
+0xff};
+static unsigned char courier12_96_bits[] = {
+0x02, 0x02, 0x01};
+static unsigned char courier12_97_bits[] = {
+0x0e, 0x1e, 0x11, 0x11, 0x3f};
+static unsigned char courier12_98_bits[] = {
+0x02, 0x02, 0x3e, 0x42, 0x42, 0x42, 0x3f};
+static unsigned char courier12_99_bits[] = {
+0x1e, 0x21, 0x01, 0x01, 0x1e};
+static unsigned char courier12_100_bits[] = {
+0x38, 0x20, 0x3e, 0x21, 0x21, 0x23, 0x7c};
+static unsigned char courier12_101_bits[] = {
+0x1e, 0x31, 0x3f, 0x01, 0x1e};
+static unsigned char courier12_102_bits[] = {
+0x3e, 0x02, 0x1f, 0x02, 0x02, 0x02, 0x1f};
+static unsigned char courier12_103_bits[] = {
+0x7e, 0x21, 0x21, 0x23, 0x3c, 0x20, 0x1c};
+static unsigned char courier12_104_bits[] = {
+0x02, 0x02, 0x3e, 0x22, 0x22, 0x22, 0x77};
+static unsigned char courier12_105_bits[] = {
+0x04, 0x00, 0x00, 0x06, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_106_bits[] = {
+0x08, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07};
+static unsigned char courier12_107_bits[] = {
+0x02, 0x02, 0x72, 0x1a, 0x0e, 0x12, 0x72};
+static unsigned char courier12_108_bits[] = {
+0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_109_bits[] = {
+0x7f, 0x49, 0x49, 0x49, 0x5b};
+static unsigned char courier12_110_bits[] = {
+0x3f, 0x22, 0x22, 0x22, 0x77};
+static unsigned char courier12_111_bits[] = {
+0x3e, 0x41, 0x41, 0x63, 0x3c};
+static unsigned char courier12_112_bits[] = {
+0x3f, 0x42, 0x42, 0x62, 0x3e, 0x02, 0x0f};
+static unsigned char courier12_113_bits[] = {
+0x7e, 0x23, 0x21, 0x23, 0x3c, 0x20, 0x70};
+static unsigned char courier12_114_bits[] = {
+0x3f, 0x02, 0x02, 0x02, 0x0f};
+static unsigned char courier12_115_bits[] = {
+0x1e, 0x11, 0x0f, 0x13, 0x0d};
+static unsigned char courier12_116_bits[] = {
+0x02, 0x02, 0x1f, 0x02, 0x02, 0x02, 0x1e};
+static unsigned char courier12_117_bits[] = {
+0x33, 0x22, 0x22, 0x22, 0x7c};
+static unsigned char courier12_118_bits[] = {
+0x67, 0x22, 0x24, 0x14, 0x08};
+static unsigned char courier12_119_bits[] = {
+0x63, 0x4a, 0x56, 0x36, 0x24};
+static unsigned char courier12_120_bits[] = {
+0x66, 0x34, 0x08, 0x36, 0x63};
+static unsigned char courier12_121_bits[] = {
+0x63, 0x22, 0x24, 0x14, 0x08, 0x04, 0x0f};
+static unsigned char courier12_122_bits[] = {
+0x1f, 0x08, 0x04, 0x12, 0x1f};
+static unsigned char courier12_123_bits[] = {
+0x06, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x06};
+static unsigned char courier12_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char courier12_125_bits[] = {
+0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03};
+static unsigned char courier12_126_bits[] = {
+0x16, 0x09};
+static unsigned char courier12_127_bits[] = {
+0x00};
+static unsigned char courier12_128_bits[] = {
+0x00};
+static unsigned char courier12_129_bits[] = {
+0x00};
+static unsigned char courier12_130_bits[] = {
+0x00};
+static unsigned char courier12_131_bits[] = {
+0x00};
+static unsigned char courier12_132_bits[] = {
+0x00};
+static unsigned char courier12_133_bits[] = {
+0x00};
+static unsigned char courier12_134_bits[] = {
+0x00};
+static unsigned char courier12_135_bits[] = {
+0x00};
+static unsigned char courier12_136_bits[] = {
+0x00};
+static unsigned char courier12_137_bits[] = {
+0x00};
+static unsigned char courier12_138_bits[] = {
+0x00};
+static unsigned char courier12_139_bits[] = {
+0x00};
+static unsigned char courier12_140_bits[] = {
+0x00};
+static unsigned char courier12_141_bits[] = {
+0x00};
+static unsigned char courier12_142_bits[] = {
+0x00};
+static unsigned char courier12_143_bits[] = {
+0x00};
+static unsigned char courier12_144_bits[] = {
+0x00};
+static unsigned char courier12_145_bits[] = {
+0x00};
+static unsigned char courier12_146_bits[] = {
+0x00};
+static unsigned char courier12_147_bits[] = {
+0x00};
+static unsigned char courier12_148_bits[] = {
+0x00};
+static unsigned char courier12_149_bits[] = {
+0x00};
+static unsigned char courier12_150_bits[] = {
+0x00};
+static unsigned char courier12_151_bits[] = {
+0x00};
+static unsigned char courier12_152_bits[] = {
+0x00};
+static unsigned char courier12_153_bits[] = {
+0x00};
+static unsigned char courier12_154_bits[] = {
+0x00};
+static unsigned char courier12_155_bits[] = {
+0x00};
+static unsigned char courier12_156_bits[] = {
+0x00};
+static unsigned char courier12_157_bits[] = {
+0x00};
+static unsigned char courier12_158_bits[] = {
+0x00};
+static unsigned char courier12_159_bits[] = {
+0x00};
+static unsigned char courier12_160_bits[] = {
+0x00};
+static unsigned char courier12_161_bits[] = {
+0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char courier12_162_bits[] = {
+0x04, 0x1e, 0x15, 0x05, 0x05, 0x0e, 0x04, 0x04};
+static unsigned char courier12_163_bits[] = {
+0x1c, 0x02, 0x02, 0x1f, 0x02, 0x02, 0x1e};
+static unsigned char courier12_164_bits[] = {
+0x1f, 0x11, 0x11, 0x11, 0x1f};
+static unsigned char courier12_165_bits[] = {
+0x33, 0x12, 0x0a, 0x1e, 0x1e, 0x04, 0x1e};
+static unsigned char courier12_166_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01};
+static unsigned char courier12_167_bits[] = {
+0x1e, 0x12, 0x06, 0x09, 0x12, 0x1c, 0x09, 0x0f};
+static unsigned char courier12_168_bits[] = {
+0x05};
+static unsigned char courier12_169_bits[] = {
+0x1c, 0x3e, 0x63, 0x43, 0x67, 0x3e, 0x1c};
+static unsigned char courier12_170_bits[] = {
+0x06, 0x07, 0x07, 0x07};
+static unsigned char courier12_171_bits[] = {
+0x14, 0x0a, 0x05, 0x0a, 0x14};
+static unsigned char courier12_172_bits[] = {
+0x1f, 0x10, 0x10};
+static unsigned char courier12_173_bits[] = {
+0x07};
+static unsigned char courier12_174_bits[] = {
+0x1c, 0x3e, 0x55, 0x4d, 0x5d, 0x62, 0x1c};
+static unsigned char courier12_175_bits[] = {
+0x0f};
+static unsigned char courier12_176_bits[] = {
+0x07, 0x09, 0x07};
+static unsigned char courier12_177_bits[] = {
+0x04, 0x04, 0x1f, 0x04, 0x04, 0x00, 0x1f};
+static unsigned char courier12_178_bits[] = {
+0x07, 0x04, 0x02, 0x07};
+static unsigned char courier12_179_bits[] = {
+0x06, 0x02, 0x04, 0x07};
+static unsigned char courier12_180_bits[] = {
+0x02};
+static unsigned char courier12_181_bits[] = {
+0x33, 0x22, 0x22, 0x22, 0x7e, 0x02, 0x02};
+static unsigned char courier12_182_bits[] = {
+0x1e, 0x15, 0x15, 0x16, 0x14, 0x14, 0x14, 0x14};
+static unsigned char courier12_183_bits[] = {
+0x03, 0x02};
+static unsigned char courier12_184_bits[] = {
+0x02, 0x06, 0x06};
+static unsigned char courier12_185_bits[] = {
+0x03, 0x02, 0x02, 0x07};
+static unsigned char courier12_186_bits[] = {
+0x07, 0x05, 0x07, 0x07};
+static unsigned char courier12_187_bits[] = {
+0x05, 0x0a, 0x14, 0x0a, 0x05};
+static unsigned char courier12_188_bits[] = {
+0x40, 0x42, 0x62, 0x32, 0x57, 0x68, 0x64, 0x72, 0x62};
+static unsigned char courier12_189_bits[] = {
+0x40, 0x62, 0x22, 0x32, 0x7f, 0x58, 0x44, 0x22, 0x72};
+static unsigned char courier12_190_bits[] = {
+0x40, 0x46, 0x66, 0x24, 0x57, 0x68, 0x64, 0x72, 0x62};
+static unsigned char courier12_191_bits[] = {
+0x08, 0x00, 0x04, 0x03, 0x01, 0x11, 0x1e};
+static unsigned char courier12_192_bits[] = {
+0x08, 0x00, 0x0e, 0x14, 0x14, 0x14, 0x3e, 0x42, 0x63};
+static unsigned char courier12_193_bits[] = {
+0x10, 0x00, 0x0e, 0x14, 0x14, 0x14, 0x3e, 0x42, 0x63};
+static unsigned char courier12_194_bits[] = {
+0x08, 0x00, 0x0e, 0x14, 0x14, 0x14, 0x3e, 0x42, 0x63};
+static unsigned char courier12_195_bits[] = {
+0x3c, 0x00, 0x0e, 0x14, 0x14, 0x14, 0x3e, 0x42, 0x63};
+static unsigned char courier12_196_bits[] = {
+0x14, 0x00, 0x0e, 0x14, 0x14, 0x14, 0x3e, 0x42, 0x63};
+static unsigned char courier12_197_bits[] = {
+0x08, 0x14, 0x08, 0x0e, 0x14, 0x14, 0x14, 0x3e, 0x42, 0x63};
+static unsigned char courier12_198_bits[] = {
+0x7c, 0x48, 0x6c, 0x3c, 0x2e, 0x4a, 0x7b};
+static unsigned char courier12_199_bits[] = {
+0x3e, 0x31, 0x01, 0x01, 0x01, 0x61, 0x1e, 0x08, 0x0c};
+static unsigned char courier12_200_bits[] = {
+0x04, 0x00, 0x3f, 0x22, 0x0a, 0x0e, 0x0a, 0x22, 0x3f};
+static unsigned char courier12_201_bits[] = {
+0x08, 0x00, 0x3f, 0x22, 0x0a, 0x0e, 0x0a, 0x22, 0x3f};
+static unsigned char courier12_202_bits[] = {
+0x04, 0x00, 0x3f, 0x22, 0x0a, 0x0e, 0x0a, 0x22, 0x3f};
+static unsigned char courier12_203_bits[] = {
+0x0a, 0x00, 0x3f, 0x22, 0x0a, 0x0e, 0x0a, 0x22, 0x3f};
+static unsigned char courier12_204_bits[] = {
+0x04, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_205_bits[] = {
+0x08, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_206_bits[] = {
+0x04, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_207_bits[] = {
+0x0a, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_208_bits[] = {
+0x1f, 0x22, 0x42, 0x4f, 0x42, 0x22, 0x1f};
+static unsigned char courier12_209_bits[] = {
+0x3c, 0x00, 0x77, 0x46, 0x4a, 0x4a, 0x52, 0x52, 0x67};
+static unsigned char courier12_210_bits[] = {
+0x08, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x22, 0x1c};
+static unsigned char courier12_211_bits[] = {
+0x10, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x22, 0x1c};
+static unsigned char courier12_212_bits[] = {
+0x08, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x22, 0x1c};
+static unsigned char courier12_213_bits[] = {
+0x3c, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x22, 0x1c};
+static unsigned char courier12_214_bits[] = {
+0x14, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x22, 0x1c};
+static unsigned char courier12_215_bits[] = {
+0x09, 0x0e, 0x04, 0x0b, 0x09};
+static unsigned char courier12_216_bits[] = {
+0x5c, 0x22, 0x51, 0x49, 0x49, 0x26, 0x1e};
+static unsigned char courier12_217_bits[] = {
+0x08, 0x00, 0x77, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c};
+static unsigned char courier12_218_bits[] = {
+0x10, 0x00, 0x77, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c};
+static unsigned char courier12_219_bits[] = {
+0x08, 0x00, 0x77, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c};
+static unsigned char courier12_220_bits[] = {
+0x14, 0x00, 0x77, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c};
+static unsigned char courier12_221_bits[] = {
+0x08, 0x00, 0x33, 0x12, 0x12, 0x0a, 0x04, 0x04, 0x1e};
+static unsigned char courier12_222_bits[] = {
+0x07, 0x1e, 0x32, 0x22, 0x32, 0x1e, 0x07};
+static unsigned char courier12_223_bits[] = {
+0x0e, 0x11, 0x0d, 0x11, 0x11, 0x11, 0x0d};
+static unsigned char courier12_224_bits[] = {
+0x04, 0x00, 0x0e, 0x1e, 0x11, 0x11, 0x3f};
+static unsigned char courier12_225_bits[] = {
+0x08, 0x00, 0x0e, 0x1e, 0x11, 0x11, 0x3f};
+static unsigned char courier12_226_bits[] = {
+0x0e, 0x00, 0x0e, 0x1e, 0x11, 0x11, 0x3f};
+static unsigned char courier12_227_bits[] = {
+0x1e, 0x00, 0x0e, 0x1e, 0x11, 0x11, 0x3f};
+static unsigned char courier12_228_bits[] = {
+0x0a, 0x00, 0x0e, 0x1e, 0x11, 0x11, 0x3f};
+static unsigned char courier12_229_bits[] = {
+0x0c, 0x0e, 0x00, 0x0e, 0x1e, 0x11, 0x11, 0x3f};
+static unsigned char courier12_230_bits[] = {
+0x3e, 0x48, 0x7e, 0x09, 0x76};
+static unsigned char courier12_231_bits[] = {
+0x3e, 0x21, 0x01, 0x01, 0x1e, 0x04, 0x0c};
+static unsigned char courier12_232_bits[] = {
+0x08, 0x00, 0x1e, 0x31, 0x3f, 0x01, 0x1e};
+static unsigned char courier12_233_bits[] = {
+0x08, 0x00, 0x1e, 0x31, 0x3f, 0x01, 0x1e};
+static unsigned char courier12_234_bits[] = {
+0x0e, 0x00, 0x1e, 0x31, 0x3f, 0x01, 0x1e};
+static unsigned char courier12_235_bits[] = {
+0x0a, 0x00, 0x1e, 0x31, 0x3f, 0x01, 0x1e};
+static unsigned char courier12_236_bits[] = {
+0x04, 0x00, 0x06, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_237_bits[] = {
+0x08, 0x00, 0x06, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_238_bits[] = {
+0x0e, 0x00, 0x06, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_239_bits[] = {
+0x0a, 0x00, 0x06, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier12_240_bits[] = {
+0x0c, 0x14, 0x30, 0x7e, 0x41, 0x41, 0x63, 0x3c};
+static unsigned char courier12_241_bits[] = {
+0x3c, 0x00, 0x3f, 0x22, 0x22, 0x22, 0x77};
+static unsigned char courier12_242_bits[] = {
+0x08, 0x00, 0x3e, 0x41, 0x41, 0x63, 0x3c};
+static unsigned char courier12_243_bits[] = {
+0x10, 0x00, 0x3e, 0x41, 0x41, 0x63, 0x3c};
+static unsigned char courier12_244_bits[] = {
+0x1c, 0x00, 0x3e, 0x41, 0x41, 0x63, 0x3c};
+static unsigned char courier12_245_bits[] = {
+0x3c, 0x00, 0x3e, 0x41, 0x41, 0x63, 0x3c};
+static unsigned char courier12_246_bits[] = {
+0x14, 0x00, 0x3e, 0x41, 0x41, 0x63, 0x3c};
+static unsigned char courier12_247_bits[] = {
+0x04, 0x00, 0x1f, 0x00, 0x04};
+static unsigned char courier12_248_bits[] = {
+0x7e, 0x51, 0x49, 0x67, 0x39};
+static unsigned char courier12_249_bits[] = {
+0x08, 0x00, 0x33, 0x22, 0x22, 0x22, 0x7c};
+static unsigned char courier12_250_bits[] = {
+0x10, 0x00, 0x33, 0x22, 0x22, 0x22, 0x7c};
+static unsigned char courier12_251_bits[] = {
+0x1c, 0x00, 0x33, 0x22, 0x22, 0x22, 0x7c};
+static unsigned char courier12_252_bits[] = {
+0x14, 0x00, 0x33, 0x22, 0x22, 0x22, 0x7c};
+static unsigned char courier12_253_bits[] = {
+0x10, 0x00, 0x63, 0x22, 0x24, 0x14, 0x08, 0x04, 0x0f};
+static unsigned char courier12_254_bits[] = {
+0x02, 0x02, 0x3e, 0x42, 0x42, 0x62, 0x3e, 0x02, 0x0f};
+static unsigned char courier12_255_bits[] = {
+0x14, 0x00, 0x63, 0x22, 0x24, 0x14, 0x08, 0x04, 0x0f};
+static RotFont courier12font[] = {
+{5, 1, 1, courier12_0_bits},
+{5, 1, 1, courier12_1_bits},
+{5, 1, 1, courier12_2_bits},
+{5, 1, 1, courier12_3_bits},
+{5, 1, 1, courier12_4_bits},
+{5, 1, 1, courier12_5_bits},
+{5, 1, 1, courier12_6_bits},
+{5, 1, 1, courier12_7_bits},
+{5, 1, 1, courier12_8_bits},
+{5, 1, 1, courier12_9_bits},
+{5, 1, 1, courier12_10_bits},
+{5, 1, 1, courier12_11_bits},
+{5, 1, 1, courier12_12_bits},
+{5, 1, 1, courier12_13_bits},
+{5, 1, 1, courier12_14_bits},
+{5, 1, 1, courier12_15_bits},
+{5, 1, 1, courier12_16_bits},
+{5, 1, 1, courier12_17_bits},
+{5, 1, 1, courier12_18_bits},
+{5, 1, 1, courier12_19_bits},
+{5, 1, 1, courier12_20_bits},
+{5, 1, 1, courier12_21_bits},
+{5, 1, 1, courier12_22_bits},
+{5, 1, 1, courier12_23_bits},
+{5, 1, 1, courier12_24_bits},
+{5, 1, 1, courier12_25_bits},
+{5, 1, 1, courier12_26_bits},
+{5, 1, 1, courier12_27_bits},
+{5, 1, 1, courier12_28_bits},
+{5, 1, 1, courier12_29_bits},
+{5, 1, 1, courier12_30_bits},
+{5, 1, 1, courier12_31_bits},
+{5, 0, 0, courier12_32_bits},
+{2, 7, 7, courier12_33_bits},
+{3, 3, 7, courier12_34_bits},
+{5, 9, 8, courier12_35_bits},
+{5, 10, 8, courier12_36_bits},
+{7, 7, 7, courier12_37_bits},
+{5, 7, 7, courier12_38_bits},
+{2, 3, 7, courier12_39_bits},
+{3, 9, 8, courier12_40_bits},
+{2, 9, 8, courier12_41_bits},
+{5, 4, 7, courier12_42_bits},
+{5, 5, 6, courier12_43_bits},
+{2, 3, 2, courier12_44_bits},
+{6, 1, 4, courier12_45_bits},
+{2, 2, 2, courier12_46_bits},
+{5, 10, 8, courier12_47_bits},
+{5, 7, 7, courier12_48_bits},
+{5, 7, 7, courier12_49_bits},
+{5, 7, 7, courier12_50_bits},
+{5, 7, 7, courier12_51_bits},
+{5, 7, 7, courier12_52_bits},
+{5, 7, 7, courier12_53_bits},
+{5, 7, 7, courier12_54_bits},
+{5, 7, 7, courier12_55_bits},
+{5, 7, 7, courier12_56_bits},
+{5, 7, 7, courier12_57_bits},
+{2, 5, 5, courier12_58_bits},
+{3, 6, 5, courier12_59_bits},
+{6, 5, 6, courier12_60_bits},
+{6, 3, 5, courier12_61_bits},
+{6, 5, 6, courier12_62_bits},
+{5, 7, 7, courier12_63_bits},
+{7, 7, 7, courier12_64_bits},
+{7, 7, 7, courier12_65_bits},
+{6, 7, 7, courier12_66_bits},
+{6, 7, 7, courier12_67_bits},
+{7, 7, 7, courier12_68_bits},
+{6, 7, 7, courier12_69_bits},
+{6, 7, 7, courier12_70_bits},
+{7, 7, 7, courier12_71_bits},
+{7, 7, 7, courier12_72_bits},
+{5, 7, 7, courier12_73_bits},
+{6, 7, 7, courier12_74_bits},
+{6, 7, 7, courier12_75_bits},
+{6, 7, 7, courier12_76_bits},
+{7, 7, 7, courier12_77_bits},
+{7, 7, 7, courier12_78_bits},
+{7, 7, 7, courier12_79_bits},
+{6, 7, 7, courier12_80_bits},
+{7, 9, 7, courier12_81_bits},
+{6, 7, 7, courier12_82_bits},
+{5, 7, 7, courier12_83_bits},
+{7, 7, 7, courier12_84_bits},
+{7, 7, 7, courier12_85_bits},
+{7, 7, 7, courier12_86_bits},
+{7, 7, 7, courier12_87_bits},
+{7, 7, 7, courier12_88_bits},
+{6, 7, 7, courier12_89_bits},
+{5, 7, 7, courier12_90_bits},
+{2, 9, 8, courier12_91_bits},
+{5, 10, 8, courier12_92_bits},
+{3, 9, 8, courier12_93_bits},
+{5, 6, 7, courier12_94_bits},
+{8, 1, -1, courier12_95_bits},
+{2, 3, 7, courier12_96_bits},
+{6, 5, 5, courier12_97_bits},
+{7, 7, 7, courier12_98_bits},
+{6, 5, 5, courier12_99_bits},
+{7, 7, 7, courier12_100_bits},
+{6, 5, 5, courier12_101_bits},
+{6, 7, 7, courier12_102_bits},
+{7, 7, 5, courier12_103_bits},
+{7, 7, 7, courier12_104_bits},
+{5, 8, 8, courier12_105_bits},
+{4, 10, 8, courier12_106_bits},
+{7, 7, 7, courier12_107_bits},
+{5, 7, 7, courier12_108_bits},
+{7, 5, 5, courier12_109_bits},
+{7, 5, 5, courier12_110_bits},
+{7, 5, 5, courier12_111_bits},
+{7, 7, 5, courier12_112_bits},
+{7, 7, 5, courier12_113_bits},
+{6, 5, 5, courier12_114_bits},
+{5, 5, 5, courier12_115_bits},
+{5, 7, 7, courier12_116_bits},
+{7, 5, 5, courier12_117_bits},
+{7, 5, 5, courier12_118_bits},
+{7, 5, 5, courier12_119_bits},
+{7, 5, 5, courier12_120_bits},
+{7, 7, 5, courier12_121_bits},
+{5, 5, 5, courier12_122_bits},
+{3, 9, 8, courier12_123_bits},
+{1, 10, 8, courier12_124_bits},
+{2, 9, 8, courier12_125_bits},
+{5, 2, 5, courier12_126_bits},
+{5, 1, 1, courier12_127_bits},
+{5, 1, 1, courier12_128_bits},
+{5, 1, 1, courier12_129_bits},
+{5, 1, 1, courier12_130_bits},
+{5, 1, 1, courier12_131_bits},
+{5, 1, 1, courier12_132_bits},
+{5, 1, 1, courier12_133_bits},
+{5, 1, 1, courier12_134_bits},
+{5, 1, 1, courier12_135_bits},
+{5, 1, 1, courier12_136_bits},
+{5, 1, 1, courier12_137_bits},
+{5, 1, 1, courier12_138_bits},
+{5, 1, 1, courier12_139_bits},
+{5, 1, 1, courier12_140_bits},
+{5, 1, 1, courier12_141_bits},
+{5, 1, 1, courier12_142_bits},
+{5, 1, 1, courier12_143_bits},
+{5, 1, 1, courier12_144_bits},
+{5, 1, 1, courier12_145_bits},
+{5, 1, 1, courier12_146_bits},
+{5, 1, 1, courier12_147_bits},
+{5, 1, 1, courier12_148_bits},
+{5, 1, 1, courier12_149_bits},
+{5, 1, 1, courier12_150_bits},
+{5, 1, 1, courier12_151_bits},
+{5, 1, 1, courier12_152_bits},
+{5, 1, 1, courier12_153_bits},
+{5, 1, 1, courier12_154_bits},
+{5, 1, 1, courier12_155_bits},
+{5, 1, 1, courier12_156_bits},
+{5, 1, 1, courier12_157_bits},
+{5, 1, 1, courier12_158_bits},
+{5, 1, 1, courier12_159_bits},
+{5, 1, 1, courier12_160_bits},
+{2, 7, 5, courier12_161_bits},
+{5, 8, 7, courier12_162_bits},
+{5, 7, 7, courier12_163_bits},
+{5, 5, 6, courier12_164_bits},
+{6, 7, 7, courier12_165_bits},
+{1, 10, 8, courier12_166_bits},
+{5, 8, 7, courier12_167_bits},
+{3, 1, 7, courier12_168_bits},
+{7, 7, 7, courier12_169_bits},
+{3, 4, 7, courier12_170_bits},
+{5, 5, 5, courier12_171_bits},
+{5, 3, 5, courier12_172_bits},
+{3, 1, 4, courier12_173_bits},
+{7, 7, 7, courier12_174_bits},
+{4, 1, 7, courier12_175_bits},
+{4, 3, 7, courier12_176_bits},
+{5, 7, 7, courier12_177_bits},
+{3, 4, 7, courier12_178_bits},
+{3, 4, 7, courier12_179_bits},
+{2, 1, 7, courier12_180_bits},
+{7, 7, 5, courier12_181_bits},
+{5, 8, 7, courier12_182_bits},
+{2, 2, 5, courier12_183_bits},
+{3, 3, 1, courier12_184_bits},
+{3, 4, 7, courier12_185_bits},
+{3, 4, 7, courier12_186_bits},
+{5, 5, 5, courier12_187_bits},
+{7, 9, 8, courier12_188_bits},
+{7, 9, 8, courier12_189_bits},
+{7, 9, 8, courier12_190_bits},
+{5, 7, 5, courier12_191_bits},
+{7, 9, 9, courier12_192_bits},
+{7, 9, 9, courier12_193_bits},
+{7, 9, 9, courier12_194_bits},
+{7, 9, 9, courier12_195_bits},
+{7, 9, 9, courier12_196_bits},
+{7, 10, 10, courier12_197_bits},
+{7, 7, 7, courier12_198_bits},
+{7, 9, 7, courier12_199_bits},
+{6, 9, 9, courier12_200_bits},
+{6, 9, 9, courier12_201_bits},
+{6, 9, 9, courier12_202_bits},
+{6, 9, 9, courier12_203_bits},
+{5, 9, 9, courier12_204_bits},
+{5, 9, 9, courier12_205_bits},
+{5, 9, 9, courier12_206_bits},
+{5, 9, 9, courier12_207_bits},
+{7, 7, 7, courier12_208_bits},
+{7, 9, 9, courier12_209_bits},
+{7, 9, 9, courier12_210_bits},
+{7, 9, 9, courier12_211_bits},
+{7, 9, 9, courier12_212_bits},
+{7, 9, 9, courier12_213_bits},
+{7, 9, 9, courier12_214_bits},
+{4, 5, 6, courier12_215_bits},
+{7, 7, 7, courier12_216_bits},
+{7, 9, 9, courier12_217_bits},
+{7, 9, 9, courier12_218_bits},
+{7, 9, 9, courier12_219_bits},
+{7, 9, 9, courier12_220_bits},
+{6, 9, 9, courier12_221_bits},
+{6, 7, 7, courier12_222_bits},
+{5, 7, 7, courier12_223_bits},
+{6, 7, 7, courier12_224_bits},
+{6, 7, 7, courier12_225_bits},
+{6, 7, 7, courier12_226_bits},
+{6, 7, 7, courier12_227_bits},
+{6, 7, 7, courier12_228_bits},
+{6, 8, 8, courier12_229_bits},
+{7, 5, 5, courier12_230_bits},
+{6, 7, 5, courier12_231_bits},
+{6, 7, 7, courier12_232_bits},
+{6, 7, 7, courier12_233_bits},
+{6, 7, 7, courier12_234_bits},
+{6, 7, 7, courier12_235_bits},
+{5, 7, 7, courier12_236_bits},
+{5, 7, 7, courier12_237_bits},
+{5, 7, 7, courier12_238_bits},
+{5, 7, 7, courier12_239_bits},
+{7, 8, 8, courier12_240_bits},
+{7, 7, 7, courier12_241_bits},
+{7, 7, 7, courier12_242_bits},
+{7, 7, 7, courier12_243_bits},
+{7, 7, 7, courier12_244_bits},
+{7, 7, 7, courier12_245_bits},
+{7, 7, 7, courier12_246_bits},
+{5, 5, 6, courier12_247_bits},
+{7, 5, 5, courier12_248_bits},
+{7, 7, 7, courier12_249_bits},
+{7, 7, 7, courier12_250_bits},
+{7, 7, 7, courier12_251_bits},
+{7, 7, 7, courier12_252_bits},
+{7, 9, 7, courier12_253_bits},
+{7, 9, 7, courier12_254_bits},
+{7, 9, 7, courier12_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier14.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier14.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier14.bdf	(revision 22322)
@@ -0,0 +1,3076 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Courier-Medium-R-Normal--14-100-100-100-M-90-ISO8859-1
+SIZE 10 100 100
+FONTBOUNDINGBOX 10 15 -1 -3
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Courier"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 14
+POINT_SIZE 100
+RESOLUTION_X 100
+RESOLUTION_Y 100
+SPACING "M"
+AVERAGE_WIDTH 90
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 9
+X_HEIGHT 7
+FACE_NAME "Courier"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "No mark"
+_DEC_DEVICE_FONTNAMES "PS=Courier"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2, 07-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Courier"
+FONT "-Adobe-Courier-Medium-R-Normal--14-100-100-100-M-90-ISO8859-1"
+WEIGHT 10
+RESOLUTION 138
+QUAD_WIDTH 9
+DEFAULT_CHAR 32
+FONT_ASCENT 11
+FONT_DESCENT 3
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 1 9 3 0
+BITMAP
+80
+80
+80
+80
+80
+80
+00
+80
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 4 4 2 6
+BITMAP
+90
+90
+90
+90
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 10 2 -1
+BITMAP
+50
+50
+50
+f8
+50
+50
+f8
+50
+50
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 13 2 -2
+BITMAP
+20
+20
+78
+88
+80
+c0
+30
+08
+88
+f0
+20
+20
+20
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+60
+90
+90
+73
+0c
+30
+cc
+12
+12
+0c
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 8 1 0
+BITMAP
+38
+40
+40
+40
+a8
+90
+98
+64
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 4 2 6
+BITMAP
+60
+60
+c0
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 12 3 -2
+BITMAP
+20
+40
+40
+80
+80
+80
+80
+80
+80
+40
+40
+20
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 12 2 -2
+BITMAP
+80
+40
+40
+20
+20
+20
+20
+20
+20
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 6 1 3
+BITMAP
+20
+20
+f8
+20
+50
+88
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 1
+BITMAP
+10
+10
+10
+fe
+10
+10
+10
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 4 2 -2
+BITMAP
+60
+60
+c0
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 1 1 4
+BITMAP
+fe
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 2 2 3 0
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 11 1 -1
+BITMAP
+04
+08
+08
+10
+10
+20
+20
+40
+40
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 10 1 0
+BITMAP
+30
+48
+84
+84
+84
+84
+84
+84
+48
+30
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 10 1 0
+BITMAP
+20
+60
+a0
+20
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 10 1 0
+BITMAP
+70
+88
+88
+08
+10
+20
+40
+80
+88
+f8
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 10 1 0
+BITMAP
+38
+44
+04
+04
+18
+04
+04
+04
+84
+78
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 10 1 0
+BITMAP
+18
+28
+28
+48
+48
+88
+88
+fc
+08
+1c
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 10 1 0
+BITMAP
+7c
+40
+40
+40
+78
+04
+04
+04
+84
+78
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 10 1 0
+BITMAP
+38
+40
+80
+80
+b8
+c4
+84
+84
+44
+38
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 10 1 0
+BITMAP
+fc
+84
+04
+08
+08
+08
+10
+10
+10
+10
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 10 2 0
+BITMAP
+70
+88
+88
+88
+70
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 10 1 0
+BITMAP
+70
+88
+84
+84
+8c
+74
+04
+04
+08
+70
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 2 7 3 0
+BITMAP
+c0
+c0
+00
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 9 2 -2
+BITMAP
+60
+60
+00
+00
+00
+60
+60
+c0
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 0 1
+BITMAP
+06
+18
+60
+80
+60
+18
+06
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 3 1 3
+BITMAP
+fe
+00
+fe
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 1
+BITMAP
+c0
+30
+0c
+02
+0c
+30
+c0
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 9 1 0
+BITMAP
+70
+88
+08
+08
+30
+20
+00
+20
+20
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 -1
+BITMAP
+38
+44
+84
+9c
+a4
+a4
+9e
+80
+40
+38
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 9 -1 0
+BITMAP
+3800
+0800
+1400
+1400
+2200
+3e00
+4100
+4100
+f780
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 0 0
+BITMAP
+fc
+42
+42
+42
+7c
+42
+42
+42
+fc
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+3a
+46
+82
+80
+80
+80
+80
+42
+3c
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+fc
+42
+41
+41
+41
+41
+41
+42
+fc
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+fe
+42
+42
+48
+78
+48
+42
+42
+fe
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+fe
+42
+42
+48
+78
+48
+40
+40
+f0
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+3a
+46
+82
+80
+80
+8f
+82
+42
+3c
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+e7
+42
+42
+42
+7e
+42
+42
+42
+e7
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 9 2 0
+BITMAP
+f8
+20
+20
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+3e
+08
+08
+08
+08
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+ee
+44
+48
+50
+70
+48
+44
+44
+e3
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+f8
+20
+20
+20
+20
+21
+21
+21
+ff
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+e380
+6300
+5500
+5500
+4900
+4900
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+e7
+62
+52
+52
+4a
+4a
+46
+46
+e2
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+fc
+42
+42
+42
+42
+7c
+40
+40
+f0
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 11 0 -2
+BITMAP
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+31
+5e
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+fc
+42
+42
+42
+44
+78
+44
+42
+e1
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 9 1 0
+BITMAP
+74
+8c
+84
+80
+78
+04
+84
+c4
+b8
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+fe
+92
+92
+10
+10
+10
+10
+10
+7c
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+e7
+42
+42
+42
+42
+42
+42
+42
+3c
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+e380
+4100
+4100
+2200
+2200
+1400
+1400
+0800
+0800
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+e380
+4100
+4900
+4900
+5500
+5500
+2200
+2200
+2200
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+e7
+42
+24
+24
+18
+24
+24
+42
+e7
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 0 0
+BITMAP
+ee
+44
+44
+28
+28
+10
+10
+10
+7c
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 9 1 0
+BITMAP
+fc
+84
+88
+10
+20
+20
+44
+84
+fc
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 12 3 -2
+BITMAP
+e0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+e0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 11 1 -1
+BITMAP
+80
+40
+40
+20
+20
+10
+10
+08
+08
+04
+04
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 12 2 -2
+BITMAP
+e0
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+e0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 5 2 4
+BITMAP
+20
+50
+50
+88
+88
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 1 0 -3
+BITMAP
+ff80
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 4 2 6
+BITMAP
+c0
+c0
+60
+20
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 0
+BITMAP
+78
+84
+04
+7c
+84
+84
+7a
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+c0
+40
+40
+5c
+62
+41
+41
+41
+62
+dc
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 0
+BITMAP
+3a
+46
+82
+80
+80
+42
+3c
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+06
+02
+02
+3a
+46
+82
+82
+82
+46
+3b
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 0
+BITMAP
+38
+44
+82
+fe
+80
+42
+3c
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+1e
+20
+20
+fc
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 -3
+BITMAP
+3b
+46
+82
+82
+82
+46
+3a
+02
+04
+78
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+c0
+40
+40
+5c
+62
+42
+42
+42
+42
+e7
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 10 2 0
+BITMAP
+20
+20
+00
+e0
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 13 1 -3
+BITMAP
+08
+08
+00
+f8
+08
+08
+08
+08
+08
+08
+08
+10
+e0
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+c0
+40
+40
+4e
+48
+50
+60
+50
+48
+ce
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 10 2 0
+BITMAP
+e0
+20
+20
+20
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 7 0 0
+BITMAP
+db00
+6d00
+4900
+4900
+4900
+4900
+ed80
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+dc
+62
+42
+42
+42
+42
+e7
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+3c
+42
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 -3
+BITMAP
+dc
+62
+41
+41
+41
+62
+5c
+40
+40
+f0
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 -3
+BITMAP
+3b
+46
+82
+82
+82
+46
+3a
+02
+02
+0f
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 0
+BITMAP
+cc
+52
+60
+40
+40
+40
+f8
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 7 1 0
+BITMAP
+7c
+84
+80
+78
+04
+84
+f8
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 0 0
+BITMAP
+20
+20
+fc
+20
+20
+20
+20
+22
+1c
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+c6
+42
+42
+42
+42
+46
+3b
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+e7
+42
+42
+24
+24
+18
+18
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 7 0 0
+BITMAP
+e380
+4100
+4900
+4900
+2a00
+3600
+2200
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 0 0
+BITMAP
+ee
+44
+28
+10
+28
+44
+ee
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 -3
+BITMAP
+e7
+42
+42
+24
+24
+18
+08
+10
+10
+78
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 7 2 0
+BITMAP
+f8
+88
+10
+20
+40
+88
+f8
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 12 2 -2
+BITMAP
+20
+40
+40
+40
+40
+80
+40
+40
+40
+40
+40
+20
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 1 11 3 -2
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 12 2 -2
+BITMAP
+80
+40
+40
+40
+40
+20
+40
+40
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 2 1 3
+BITMAP
+64
+98
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 1 9 4 -2
+BITMAP
+80
+80
+00
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 9 2 0
+BITMAP
+20
+20
+78
+88
+80
+88
+70
+20
+20
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+18
+24
+20
+20
+78
+20
+20
+42
+fc
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 6 1 1
+BITMAP
+b4
+48
+84
+84
+48
+b4
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+ee
+44
+44
+28
+7c
+10
+7c
+10
+38
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 1 11 3 -2
+BITMAP
+80
+80
+80
+80
+00
+00
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 10 1 -1
+BITMAP
+3c
+44
+40
+f0
+88
+44
+3c
+08
+88
+f0
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 1 2 8
+BITMAP
+d8
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+3c
+42
+99
+a5
+a1
+a5
+99
+42
+3c
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 4 6 2 3
+BITMAP
+c0
+20
+e0
+b0
+00
+f0
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+11
+22
+44
+cc
+44
+22
+11
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 3 1 3
+BITMAP
+fe
+02
+02
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 1 1 4
+BITMAP
+fc
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+3c
+42
+b9
+a5
+b9
+a9
+a5
+42
+3c
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 4 1 2 8
+BITMAP
+f0
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 4 4 2 6
+BITMAP
+60
+90
+90
+60
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 1
+BITMAP
+10
+10
+fe
+10
+10
+00
+fe
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 4 6 2 4
+BITMAP
+60
+90
+10
+20
+40
+f0
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 4 6 2 4
+BITMAP
+60
+90
+60
+10
+90
+60
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 4 2 2 8
+BITMAP
+30
+c0
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 -3
+BITMAP
+c6
+42
+42
+42
+42
+46
+7b
+40
+40
+40
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 -1
+BITMAP
+7e
+94
+94
+94
+74
+14
+14
+14
+14
+3e
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 2 2 3 3
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 3 2 -3
+BITMAP
+40
+20
+e0
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 3 6 3 4
+BITMAP
+40
+c0
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 4 6 2 3
+BITMAP
+60
+90
+90
+60
+00
+f0
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+88
+44
+22
+33
+22
+44
+88
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 10 10 -1 0
+BITMAP
+4000
+c100
+4200
+4400
+4480
+e980
+1280
+1480
+27c0
+4080
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 10 10 -1 0
+BITMAP
+4000
+c100
+4200
+4400
+4580
+ea40
+1040
+1080
+2100
+43c0
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 10 10 -1 0
+BITMAP
+6000
+9100
+6200
+1400
+9480
+6980
+1280
+1480
+27c0
+4080
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 9 2 -2
+BITMAP
+20
+20
+00
+20
+60
+80
+80
+88
+70
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+3000
+0c00
+0000
+3800
+0800
+1400
+1400
+2200
+3e00
+4100
+4100
+f780
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+0c00
+3000
+0000
+3800
+0800
+1400
+1400
+2200
+3e00
+4100
+4100
+f780
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+0800
+1400
+0000
+3800
+0800
+1400
+1400
+2200
+3e00
+4100
+4100
+f780
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+1a00
+2c00
+0000
+3800
+0800
+1400
+1400
+2200
+3e00
+4100
+4100
+f780
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 11 0 0
+BITMAP
+3600
+0000
+3800
+0800
+1400
+1400
+2200
+3e00
+4100
+4100
+f780
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+1800
+2400
+1800
+3800
+0800
+1400
+1400
+2200
+3e00
+4100
+4100
+f780
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 9 -1 0
+BITMAP
+1f80
+0c80
+1400
+1480
+2780
+3c80
+4400
+4480
+ef80
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 12 1 -3
+BITMAP
+3a
+46
+82
+80
+80
+80
+80
+42
+3c
+10
+08
+38
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+60
+18
+00
+fe
+42
+42
+48
+78
+48
+42
+42
+fe
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+0c
+30
+00
+fe
+42
+42
+48
+78
+48
+42
+42
+fe
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+10
+28
+00
+fe
+42
+42
+48
+78
+48
+42
+42
+fe
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 11 1 0
+BITMAP
+6c
+00
+fe
+42
+42
+48
+78
+48
+42
+42
+fe
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 12 2 0
+BITMAP
+c0
+30
+00
+f8
+20
+20
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 12 2 0
+BITMAP
+18
+60
+00
+f8
+20
+20
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 12 2 0
+BITMAP
+20
+50
+00
+f8
+20
+20
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 11 2 0
+BITMAP
+d8
+00
+f8
+20
+20
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+fc
+42
+41
+41
+f1
+41
+41
+42
+fc
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+1a
+2c
+00
+e7
+62
+52
+52
+4a
+4a
+46
+46
+e2
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+30
+0c
+00
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+0c
+30
+00
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+10
+28
+00
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+1a
+2c
+00
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 11 0 0
+BITMAP
+66
+00
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 1
+BITMAP
+82
+44
+28
+10
+28
+44
+82
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 9 -1 0
+BITMAP
+1e80
+2100
+4280
+4480
+4880
+5080
+2080
+6100
+9e00
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+30
+0c
+00
+e7
+42
+42
+42
+42
+42
+42
+42
+3c
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+0c
+30
+00
+e7
+42
+42
+42
+42
+42
+42
+42
+3c
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+10
+28
+00
+e7
+42
+42
+42
+42
+42
+42
+42
+3c
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 11 0 0
+BITMAP
+66
+00
+e7
+42
+42
+42
+42
+42
+42
+42
+3c
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+0c
+30
+00
+ee
+44
+44
+28
+28
+10
+10
+10
+7c
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 0 0
+BITMAP
+e0
+40
+7c
+42
+42
+42
+7c
+40
+e0
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 0 0
+BITMAP
+38
+44
+44
+58
+44
+42
+42
+52
+cc
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+60
+18
+00
+78
+84
+04
+7c
+84
+8c
+76
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+18
+60
+00
+78
+84
+04
+7c
+84
+8c
+76
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+10
+28
+00
+78
+84
+04
+7c
+84
+8c
+76
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+34
+58
+00
+78
+84
+04
+7c
+84
+8c
+76
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+6c
+00
+78
+84
+04
+7c
+84
+8c
+76
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+30
+48
+30
+78
+84
+04
+7c
+84
+8c
+76
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+76
+89
+09
+7f
+88
+89
+76
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 -3
+BITMAP
+3a
+46
+82
+80
+80
+42
+3c
+10
+08
+38
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+60
+18
+00
+38
+44
+82
+fe
+80
+42
+3c
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+0c
+30
+00
+38
+44
+82
+fe
+80
+42
+3c
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+10
+28
+00
+38
+44
+82
+fe
+80
+42
+3c
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+6c
+00
+38
+44
+82
+fe
+80
+42
+3c
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 10 2 0
+BITMAP
+c0
+30
+00
+e0
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 10 2 0
+BITMAP
+30
+c0
+00
+e0
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 10 2 0
+BITMAP
+20
+50
+00
+e0
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 5 9 2 0
+BITMAP
+d8
+00
+e0
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+02
+e4
+18
+28
+44
+3c
+42
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+1a
+2c
+00
+dc
+62
+42
+42
+42
+42
+e7
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+30
+0c
+00
+3c
+42
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+0c
+30
+00
+3c
+42
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+10
+28
+00
+3c
+42
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+1a
+2c
+00
+3c
+42
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+6c
+00
+3c
+42
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 7 0 1
+BITMAP
+18
+18
+00
+ff
+00
+18
+18
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+3d
+46
+89
+91
+a1
+42
+bc
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+30
+0c
+00
+c6
+42
+42
+42
+42
+46
+3b
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+0c
+30
+00
+c6
+42
+42
+42
+42
+46
+3b
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+10
+28
+00
+c6
+42
+42
+42
+42
+46
+3b
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+6c
+00
+c6
+42
+42
+42
+42
+46
+3b
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 13 0 -3
+BITMAP
+06
+18
+00
+e7
+42
+42
+24
+24
+18
+08
+10
+10
+78
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 -3
+BITMAP
+c0
+40
+5c
+62
+41
+41
+41
+62
+5c
+40
+40
+f0
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 12 0 -3
+BITMAP
+36
+00
+e7
+42
+42
+24
+24
+18
+08
+10
+10
+78
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier14.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier14.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier14.h	(revision 22322)
@@ -0,0 +1,789 @@
+static unsigned char courier14_0_bits[] = {
+0x00};
+static unsigned char courier14_1_bits[] = {
+0x00};
+static unsigned char courier14_2_bits[] = {
+0x00};
+static unsigned char courier14_3_bits[] = {
+0x00};
+static unsigned char courier14_4_bits[] = {
+0x00};
+static unsigned char courier14_5_bits[] = {
+0x00};
+static unsigned char courier14_6_bits[] = {
+0x00};
+static unsigned char courier14_7_bits[] = {
+0x00};
+static unsigned char courier14_8_bits[] = {
+0x00};
+static unsigned char courier14_9_bits[] = {
+0x00};
+static unsigned char courier14_10_bits[] = {
+0x00};
+static unsigned char courier14_11_bits[] = {
+0x00};
+static unsigned char courier14_12_bits[] = {
+0x00};
+static unsigned char courier14_13_bits[] = {
+0x00};
+static unsigned char courier14_14_bits[] = {
+0x00};
+static unsigned char courier14_15_bits[] = {
+0x00};
+static unsigned char courier14_16_bits[] = {
+0x00};
+static unsigned char courier14_17_bits[] = {
+0x00};
+static unsigned char courier14_18_bits[] = {
+0x00};
+static unsigned char courier14_19_bits[] = {
+0x00};
+static unsigned char courier14_20_bits[] = {
+0x00};
+static unsigned char courier14_21_bits[] = {
+0x00};
+static unsigned char courier14_22_bits[] = {
+0x00};
+static unsigned char courier14_23_bits[] = {
+0x00};
+static unsigned char courier14_24_bits[] = {
+0x00};
+static unsigned char courier14_25_bits[] = {
+0x00};
+static unsigned char courier14_26_bits[] = {
+0x00};
+static unsigned char courier14_27_bits[] = {
+0x00};
+static unsigned char courier14_28_bits[] = {
+0x00};
+static unsigned char courier14_29_bits[] = {
+0x00};
+static unsigned char courier14_30_bits[] = {
+0x00};
+static unsigned char courier14_31_bits[] = {
+0x00};
+static unsigned char courier14_32_bits[] = {
+0x00};
+static unsigned char courier14_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01};
+static unsigned char courier14_34_bits[] = {
+0x09, 0x09, 0x09, 0x09};
+static unsigned char courier14_35_bits[] = {
+0x0a, 0x0a, 0x0a, 0x1f, 0x0a, 0x0a, 0x1f, 0x0a, 0x0a, 0x0a};
+static unsigned char courier14_36_bits[] = {
+0x04, 0x04, 0x1e, 0x11, 0x01, 0x03, 0x0c, 0x10, 0x11, 0x0f, 0x04, 0x04, 
+0x04};
+static unsigned char courier14_37_bits[] = {
+0x06, 0x09, 0x09, 0xce, 0x30, 0x0c, 0x33, 0x48, 0x48, 0x30};
+static unsigned char courier14_38_bits[] = {
+0x1c, 0x02, 0x02, 0x02, 0x15, 0x09, 0x19, 0x26};
+static unsigned char courier14_39_bits[] = {
+0x06, 0x06, 0x03, 0x01};
+static unsigned char courier14_40_bits[] = {
+0x04, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x04};
+static unsigned char courier14_41_bits[] = {
+0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x01};
+static unsigned char courier14_42_bits[] = {
+0x04, 0x04, 0x1f, 0x04, 0x0a, 0x11};
+static unsigned char courier14_43_bits[] = {
+0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08};
+static unsigned char courier14_44_bits[] = {
+0x06, 0x06, 0x03, 0x01};
+static unsigned char courier14_45_bits[] = {
+0x7f};
+static unsigned char courier14_46_bits[] = {
+0x03, 0x03};
+static unsigned char courier14_47_bits[] = {
+0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x01, 0x01};
+static unsigned char courier14_48_bits[] = {
+0x0c, 0x12, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x12, 0x0c};
+static unsigned char courier14_49_bits[] = {
+0x04, 0x06, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_50_bits[] = {
+0x0e, 0x11, 0x11, 0x10, 0x08, 0x04, 0x02, 0x01, 0x11, 0x1f};
+static unsigned char courier14_51_bits[] = {
+0x1c, 0x22, 0x20, 0x20, 0x18, 0x20, 0x20, 0x20, 0x21, 0x1e};
+static unsigned char courier14_52_bits[] = {
+0x18, 0x14, 0x14, 0x12, 0x12, 0x11, 0x11, 0x3f, 0x10, 0x38};
+static unsigned char courier14_53_bits[] = {
+0x3e, 0x02, 0x02, 0x02, 0x1e, 0x20, 0x20, 0x20, 0x21, 0x1e};
+static unsigned char courier14_54_bits[] = {
+0x1c, 0x02, 0x01, 0x01, 0x1d, 0x23, 0x21, 0x21, 0x22, 0x1c};
+static unsigned char courier14_55_bits[] = {
+0x3f, 0x21, 0x20, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08};
+static unsigned char courier14_56_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char courier14_57_bits[] = {
+0x0e, 0x11, 0x21, 0x21, 0x31, 0x2e, 0x20, 0x20, 0x10, 0x0e};
+static unsigned char courier14_58_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03};
+static unsigned char courier14_59_bits[] = {
+0x06, 0x06, 0x00, 0x00, 0x00, 0x06, 0x06, 0x03, 0x01};
+static unsigned char courier14_60_bits[] = {
+0x60, 0x18, 0x06, 0x01, 0x06, 0x18, 0x60};
+static unsigned char courier14_61_bits[] = {
+0x7f, 0x00, 0x7f};
+static unsigned char courier14_62_bits[] = {
+0x03, 0x0c, 0x30, 0x40, 0x30, 0x0c, 0x03};
+static unsigned char courier14_63_bits[] = {
+0x0e, 0x11, 0x10, 0x10, 0x0c, 0x04, 0x00, 0x04, 0x04};
+static unsigned char courier14_64_bits[] = {
+0x1c, 0x22, 0x21, 0x39, 0x25, 0x25, 0x79, 0x01, 0x02, 0x1c};
+static unsigned char courier14_65_bits[] = {
+0x1c, 0x00, 0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0xef, 0x01};
+static unsigned char courier14_66_bits[] = {
+0x3f, 0x42, 0x42, 0x42, 0x3e, 0x42, 0x42, 0x42, 0x3f};
+static unsigned char courier14_67_bits[] = {
+0x5c, 0x62, 0x41, 0x01, 0x01, 0x01, 0x01, 0x42, 0x3c};
+static unsigned char courier14_68_bits[] = {
+0x3f, 0x42, 0x82, 0x82, 0x82, 0x82, 0x82, 0x42, 0x3f};
+static unsigned char courier14_69_bits[] = {
+0x7f, 0x42, 0x42, 0x12, 0x1e, 0x12, 0x42, 0x42, 0x7f};
+static unsigned char courier14_70_bits[] = {
+0x7f, 0x42, 0x42, 0x12, 0x1e, 0x12, 0x02, 0x02, 0x0f};
+static unsigned char courier14_71_bits[] = {
+0x5c, 0x62, 0x41, 0x01, 0x01, 0xf1, 0x41, 0x42, 0x3c};
+static unsigned char courier14_72_bits[] = {
+0xe7, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0xe7};
+static unsigned char courier14_73_bits[] = {
+0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_74_bits[] = {
+0x7c, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char courier14_75_bits[] = {
+0x77, 0x22, 0x12, 0x0a, 0x0e, 0x12, 0x22, 0x22, 0xc7};
+static unsigned char courier14_76_bits[] = {
+0x1f, 0x04, 0x04, 0x04, 0x04, 0x84, 0x84, 0x84, 0xff};
+static unsigned char courier14_77_bits[] = {
+0xc7, 0x01, 0xc6, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x92, 0x00, 0x92, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char courier14_78_bits[] = {
+0xe7, 0x46, 0x4a, 0x4a, 0x52, 0x52, 0x62, 0x62, 0x47};
+static unsigned char courier14_79_bits[] = {
+0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_80_bits[] = {
+0x3f, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x0f};
+static unsigned char courier14_81_bits[] = {
+0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c, 0x8c, 0x7a};
+static unsigned char courier14_82_bits[] = {
+0x3f, 0x42, 0x42, 0x42, 0x22, 0x1e, 0x22, 0x42, 0x87};
+static unsigned char courier14_83_bits[] = {
+0x2e, 0x31, 0x21, 0x01, 0x1e, 0x20, 0x21, 0x23, 0x1d};
+static unsigned char courier14_84_bits[] = {
+0x7f, 0x49, 0x49, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e};
+static unsigned char courier14_85_bits[] = {
+0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c};
+static unsigned char courier14_86_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char courier14_87_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x92, 0x00, 0x92, 0x00, 0xaa, 0x00, 0xaa, 0x00, 
+0x44, 0x00, 0x44, 0x00, 0x44, 0x00};
+static unsigned char courier14_88_bits[] = {
+0xe7, 0x42, 0x24, 0x24, 0x18, 0x24, 0x24, 0x42, 0xe7};
+static unsigned char courier14_89_bits[] = {
+0x77, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x3e};
+static unsigned char courier14_90_bits[] = {
+0x3f, 0x21, 0x11, 0x08, 0x04, 0x04, 0x22, 0x21, 0x3f};
+static unsigned char courier14_91_bits[] = {
+0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07};
+static unsigned char courier14_92_bits[] = {
+0x01, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20};
+static unsigned char courier14_93_bits[] = {
+0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x07};
+static unsigned char courier14_94_bits[] = {
+0x04, 0x0a, 0x0a, 0x11, 0x11};
+static unsigned char courier14_95_bits[] = {
+0xff, 0x01};
+static unsigned char courier14_96_bits[] = {
+0x03, 0x03, 0x06, 0x04};
+static unsigned char courier14_97_bits[] = {
+0x1e, 0x21, 0x20, 0x3e, 0x21, 0x21, 0x5e};
+static unsigned char courier14_98_bits[] = {
+0x03, 0x02, 0x02, 0x3a, 0x46, 0x82, 0x82, 0x82, 0x46, 0x3b};
+static unsigned char courier14_99_bits[] = {
+0x5c, 0x62, 0x41, 0x01, 0x01, 0x42, 0x3c};
+static unsigned char courier14_100_bits[] = {
+0x60, 0x40, 0x40, 0x5c, 0x62, 0x41, 0x41, 0x41, 0x62, 0xdc};
+static unsigned char courier14_101_bits[] = {
+0x1c, 0x22, 0x41, 0x7f, 0x01, 0x42, 0x3c};
+static unsigned char courier14_102_bits[] = {
+0x78, 0x04, 0x04, 0x3f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_103_bits[] = {
+0xdc, 0x62, 0x41, 0x41, 0x41, 0x62, 0x5c, 0x40, 0x20, 0x1e};
+static unsigned char courier14_104_bits[] = {
+0x03, 0x02, 0x02, 0x3a, 0x46, 0x42, 0x42, 0x42, 0x42, 0xe7};
+static unsigned char courier14_105_bits[] = {
+0x04, 0x04, 0x00, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_106_bits[] = {
+0x10, 0x10, 0x00, 0x1f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 
+0x07};
+static unsigned char courier14_107_bits[] = {
+0x03, 0x02, 0x02, 0x72, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x73};
+static unsigned char courier14_108_bits[] = {
+0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_109_bits[] = {
+0xdb, 0x00, 0xb6, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 
+0xb7, 0x01};
+static unsigned char courier14_110_bits[] = {
+0x3b, 0x46, 0x42, 0x42, 0x42, 0x42, 0xe7};
+static unsigned char courier14_111_bits[] = {
+0x3c, 0x42, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_112_bits[] = {
+0x3b, 0x46, 0x82, 0x82, 0x82, 0x46, 0x3a, 0x02, 0x02, 0x0f};
+static unsigned char courier14_113_bits[] = {
+0xdc, 0x62, 0x41, 0x41, 0x41, 0x62, 0x5c, 0x40, 0x40, 0xf0};
+static unsigned char courier14_114_bits[] = {
+0x33, 0x4a, 0x06, 0x02, 0x02, 0x02, 0x1f};
+static unsigned char courier14_115_bits[] = {
+0x3e, 0x21, 0x01, 0x1e, 0x20, 0x21, 0x1f};
+static unsigned char courier14_116_bits[] = {
+0x04, 0x04, 0x3f, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38};
+static unsigned char courier14_117_bits[] = {
+0x63, 0x42, 0x42, 0x42, 0x42, 0x62, 0xdc};
+static unsigned char courier14_118_bits[] = {
+0xe7, 0x42, 0x42, 0x24, 0x24, 0x18, 0x18};
+static unsigned char courier14_119_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x92, 0x00, 0x92, 0x00, 0x54, 0x00, 0x6c, 0x00, 
+0x44, 0x00};
+static unsigned char courier14_120_bits[] = {
+0x77, 0x22, 0x14, 0x08, 0x14, 0x22, 0x77};
+static unsigned char courier14_121_bits[] = {
+0xe7, 0x42, 0x42, 0x24, 0x24, 0x18, 0x10, 0x08, 0x08, 0x1e};
+static unsigned char courier14_122_bits[] = {
+0x1f, 0x11, 0x08, 0x04, 0x02, 0x11, 0x1f};
+static unsigned char courier14_123_bits[] = {
+0x04, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04};
+static unsigned char courier14_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char courier14_125_bits[] = {
+0x01, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01};
+static unsigned char courier14_126_bits[] = {
+0x26, 0x19};
+static unsigned char courier14_127_bits[] = {
+0x00};
+static unsigned char courier14_128_bits[] = {
+0x00};
+static unsigned char courier14_129_bits[] = {
+0x00};
+static unsigned char courier14_130_bits[] = {
+0x00};
+static unsigned char courier14_131_bits[] = {
+0x00};
+static unsigned char courier14_132_bits[] = {
+0x00};
+static unsigned char courier14_133_bits[] = {
+0x00};
+static unsigned char courier14_134_bits[] = {
+0x00};
+static unsigned char courier14_135_bits[] = {
+0x00};
+static unsigned char courier14_136_bits[] = {
+0x00};
+static unsigned char courier14_137_bits[] = {
+0x00};
+static unsigned char courier14_138_bits[] = {
+0x00};
+static unsigned char courier14_139_bits[] = {
+0x00};
+static unsigned char courier14_140_bits[] = {
+0x00};
+static unsigned char courier14_141_bits[] = {
+0x00};
+static unsigned char courier14_142_bits[] = {
+0x00};
+static unsigned char courier14_143_bits[] = {
+0x00};
+static unsigned char courier14_144_bits[] = {
+0x00};
+static unsigned char courier14_145_bits[] = {
+0x00};
+static unsigned char courier14_146_bits[] = {
+0x00};
+static unsigned char courier14_147_bits[] = {
+0x00};
+static unsigned char courier14_148_bits[] = {
+0x00};
+static unsigned char courier14_149_bits[] = {
+0x00};
+static unsigned char courier14_150_bits[] = {
+0x00};
+static unsigned char courier14_151_bits[] = {
+0x00};
+static unsigned char courier14_152_bits[] = {
+0x00};
+static unsigned char courier14_153_bits[] = {
+0x00};
+static unsigned char courier14_154_bits[] = {
+0x00};
+static unsigned char courier14_155_bits[] = {
+0x00};
+static unsigned char courier14_156_bits[] = {
+0x00};
+static unsigned char courier14_157_bits[] = {
+0x00};
+static unsigned char courier14_158_bits[] = {
+0x00};
+static unsigned char courier14_159_bits[] = {
+0x00};
+static unsigned char courier14_160_bits[] = {
+0x00};
+static unsigned char courier14_161_bits[] = {
+0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char courier14_162_bits[] = {
+0x04, 0x04, 0x1e, 0x11, 0x01, 0x11, 0x0e, 0x04, 0x04};
+static unsigned char courier14_163_bits[] = {
+0x18, 0x24, 0x04, 0x04, 0x1e, 0x04, 0x04, 0x42, 0x3f};
+static unsigned char courier14_164_bits[] = {
+0x2d, 0x12, 0x21, 0x21, 0x12, 0x2d};
+static unsigned char courier14_165_bits[] = {
+0x77, 0x22, 0x22, 0x14, 0x3e, 0x08, 0x3e, 0x08, 0x1c};
+static unsigned char courier14_166_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char courier14_167_bits[] = {
+0x3c, 0x22, 0x02, 0x0f, 0x11, 0x22, 0x3c, 0x10, 0x11, 0x0f};
+static unsigned char courier14_168_bits[] = {
+0x1b};
+static unsigned char courier14_169_bits[] = {
+0x3c, 0x42, 0x99, 0xa5, 0x85, 0xa5, 0x99, 0x42, 0x3c};
+static unsigned char courier14_170_bits[] = {
+0x03, 0x04, 0x07, 0x0d, 0x00, 0x0f};
+static unsigned char courier14_171_bits[] = {
+0x88, 0x44, 0x22, 0x33, 0x22, 0x44, 0x88};
+static unsigned char courier14_172_bits[] = {
+0x7f, 0x40, 0x40};
+static unsigned char courier14_173_bits[] = {
+0x3f};
+static unsigned char courier14_174_bits[] = {
+0x3c, 0x42, 0x9d, 0xa5, 0x9d, 0x95, 0xa5, 0x42, 0x3c};
+static unsigned char courier14_175_bits[] = {
+0x0f};
+static unsigned char courier14_176_bits[] = {
+0x06, 0x09, 0x09, 0x06};
+static unsigned char courier14_177_bits[] = {
+0x08, 0x08, 0x7f, 0x08, 0x08, 0x00, 0x7f};
+static unsigned char courier14_178_bits[] = {
+0x06, 0x09, 0x08, 0x04, 0x02, 0x0f};
+static unsigned char courier14_179_bits[] = {
+0x06, 0x09, 0x06, 0x08, 0x09, 0x06};
+static unsigned char courier14_180_bits[] = {
+0x0c, 0x03};
+static unsigned char courier14_181_bits[] = {
+0x63, 0x42, 0x42, 0x42, 0x42, 0x62, 0xde, 0x02, 0x02, 0x02};
+static unsigned char courier14_182_bits[] = {
+0x7e, 0x29, 0x29, 0x29, 0x2e, 0x28, 0x28, 0x28, 0x28, 0x7c};
+static unsigned char courier14_183_bits[] = {
+0x03, 0x03};
+static unsigned char courier14_184_bits[] = {
+0x02, 0x04, 0x07};
+static unsigned char courier14_185_bits[] = {
+0x02, 0x03, 0x02, 0x02, 0x02, 0x07};
+static unsigned char courier14_186_bits[] = {
+0x06, 0x09, 0x09, 0x06, 0x00, 0x0f};
+static unsigned char courier14_187_bits[] = {
+0x11, 0x22, 0x44, 0xcc, 0x44, 0x22, 0x11};
+static unsigned char courier14_188_bits[] = {
+0x02, 0x00, 0x83, 0x00, 0x42, 0x00, 0x22, 0x00, 0x22, 0x01, 0x97, 0x01, 
+0x48, 0x01, 0x28, 0x01, 0xe4, 0x03, 0x02, 0x01};
+static unsigned char courier14_189_bits[] = {
+0x02, 0x00, 0x83, 0x00, 0x42, 0x00, 0x22, 0x00, 0xa2, 0x01, 0x57, 0x02, 
+0x08, 0x02, 0x08, 0x01, 0x84, 0x00, 0xc2, 0x03};
+static unsigned char courier14_190_bits[] = {
+0x06, 0x00, 0x89, 0x00, 0x46, 0x00, 0x28, 0x00, 0x29, 0x01, 0x96, 0x01, 
+0x48, 0x01, 0x28, 0x01, 0xe4, 0x03, 0x02, 0x01};
+static unsigned char courier14_191_bits[] = {
+0x04, 0x04, 0x00, 0x04, 0x06, 0x01, 0x01, 0x11, 0x0e};
+static unsigned char courier14_192_bits[] = {
+0x0c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x82, 0x00, 0xef, 0x01};
+static unsigned char courier14_193_bits[] = {
+0x30, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x82, 0x00, 0xef, 0x01};
+static unsigned char courier14_194_bits[] = {
+0x10, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x82, 0x00, 0xef, 0x01};
+static unsigned char courier14_195_bits[] = {
+0x58, 0x00, 0x34, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x82, 0x00, 0xef, 0x01};
+static unsigned char courier14_196_bits[] = {
+0x6c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 
+0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x82, 0x00, 0xef, 0x01};
+static unsigned char courier14_197_bits[] = {
+0x18, 0x00, 0x24, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x82, 0x00, 0xef, 0x01};
+static unsigned char courier14_198_bits[] = {
+0xf8, 0x01, 0x30, 0x01, 0x28, 0x00, 0x28, 0x01, 0xe4, 0x01, 0x3c, 0x01, 
+0x22, 0x00, 0x22, 0x01, 0xf7, 0x01};
+static unsigned char courier14_199_bits[] = {
+0x5c, 0x62, 0x41, 0x01, 0x01, 0x01, 0x01, 0x42, 0x3c, 0x08, 0x10, 0x1c};
+static unsigned char courier14_200_bits[] = {
+0x06, 0x18, 0x00, 0x7f, 0x42, 0x42, 0x12, 0x1e, 0x12, 0x42, 0x42, 0x7f};
+static unsigned char courier14_201_bits[] = {
+0x30, 0x0c, 0x00, 0x7f, 0x42, 0x42, 0x12, 0x1e, 0x12, 0x42, 0x42, 0x7f};
+static unsigned char courier14_202_bits[] = {
+0x08, 0x14, 0x00, 0x7f, 0x42, 0x42, 0x12, 0x1e, 0x12, 0x42, 0x42, 0x7f};
+static unsigned char courier14_203_bits[] = {
+0x36, 0x00, 0x7f, 0x42, 0x42, 0x12, 0x1e, 0x12, 0x42, 0x42, 0x7f};
+static unsigned char courier14_204_bits[] = {
+0x03, 0x0c, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_205_bits[] = {
+0x18, 0x06, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_206_bits[] = {
+0x04, 0x0a, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_207_bits[] = {
+0x1b, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_208_bits[] = {
+0x3f, 0x42, 0x82, 0x82, 0x8f, 0x82, 0x82, 0x42, 0x3f};
+static unsigned char courier14_209_bits[] = {
+0x58, 0x34, 0x00, 0xe7, 0x46, 0x4a, 0x4a, 0x52, 0x52, 0x62, 0x62, 0x47};
+static unsigned char courier14_210_bits[] = {
+0x0c, 0x30, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_211_bits[] = {
+0x30, 0x0c, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_212_bits[] = {
+0x08, 0x14, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_213_bits[] = {
+0x58, 0x34, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_214_bits[] = {
+0x66, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_215_bits[] = {
+0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41};
+static unsigned char courier14_216_bits[] = {
+0x78, 0x01, 0x84, 0x00, 0x42, 0x01, 0x22, 0x01, 0x12, 0x01, 0x0a, 0x01, 
+0x04, 0x01, 0x86, 0x00, 0x79, 0x00};
+static unsigned char courier14_217_bits[] = {
+0x0c, 0x30, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c};
+static unsigned char courier14_218_bits[] = {
+0x30, 0x0c, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c};
+static unsigned char courier14_219_bits[] = {
+0x08, 0x14, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c};
+static unsigned char courier14_220_bits[] = {
+0x66, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c};
+static unsigned char courier14_221_bits[] = {
+0x30, 0x0c, 0x00, 0x77, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x3e};
+static unsigned char courier14_222_bits[] = {
+0x07, 0x02, 0x3e, 0x42, 0x42, 0x42, 0x3e, 0x02, 0x07};
+static unsigned char courier14_223_bits[] = {
+0x1c, 0x22, 0x22, 0x1a, 0x22, 0x42, 0x42, 0x4a, 0x33};
+static unsigned char courier14_224_bits[] = {
+0x06, 0x18, 0x00, 0x1e, 0x21, 0x20, 0x3e, 0x21, 0x31, 0x6e};
+static unsigned char courier14_225_bits[] = {
+0x18, 0x06, 0x00, 0x1e, 0x21, 0x20, 0x3e, 0x21, 0x31, 0x6e};
+static unsigned char courier14_226_bits[] = {
+0x08, 0x14, 0x00, 0x1e, 0x21, 0x20, 0x3e, 0x21, 0x31, 0x6e};
+static unsigned char courier14_227_bits[] = {
+0x2c, 0x1a, 0x00, 0x1e, 0x21, 0x20, 0x3e, 0x21, 0x31, 0x6e};
+static unsigned char courier14_228_bits[] = {
+0x36, 0x00, 0x1e, 0x21, 0x20, 0x3e, 0x21, 0x31, 0x6e};
+static unsigned char courier14_229_bits[] = {
+0x0c, 0x12, 0x0c, 0x1e, 0x21, 0x20, 0x3e, 0x21, 0x31, 0x6e};
+static unsigned char courier14_230_bits[] = {
+0x6e, 0x91, 0x90, 0xfe, 0x11, 0x91, 0x6e};
+static unsigned char courier14_231_bits[] = {
+0x5c, 0x62, 0x41, 0x01, 0x01, 0x42, 0x3c, 0x08, 0x10, 0x1c};
+static unsigned char courier14_232_bits[] = {
+0x06, 0x18, 0x00, 0x1c, 0x22, 0x41, 0x7f, 0x01, 0x42, 0x3c};
+static unsigned char courier14_233_bits[] = {
+0x30, 0x0c, 0x00, 0x1c, 0x22, 0x41, 0x7f, 0x01, 0x42, 0x3c};
+static unsigned char courier14_234_bits[] = {
+0x08, 0x14, 0x00, 0x1c, 0x22, 0x41, 0x7f, 0x01, 0x42, 0x3c};
+static unsigned char courier14_235_bits[] = {
+0x36, 0x00, 0x1c, 0x22, 0x41, 0x7f, 0x01, 0x42, 0x3c};
+static unsigned char courier14_236_bits[] = {
+0x03, 0x0c, 0x00, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_237_bits[] = {
+0x0c, 0x03, 0x00, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_238_bits[] = {
+0x04, 0x0a, 0x00, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_239_bits[] = {
+0x1b, 0x00, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier14_240_bits[] = {
+0x40, 0x27, 0x18, 0x14, 0x22, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_241_bits[] = {
+0x58, 0x34, 0x00, 0x3b, 0x46, 0x42, 0x42, 0x42, 0x42, 0xe7};
+static unsigned char courier14_242_bits[] = {
+0x0c, 0x30, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_243_bits[] = {
+0x30, 0x0c, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_244_bits[] = {
+0x08, 0x14, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_245_bits[] = {
+0x58, 0x34, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_246_bits[] = {
+0x36, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier14_247_bits[] = {
+0x18, 0x18, 0x00, 0xff, 0x00, 0x18, 0x18};
+static unsigned char courier14_248_bits[] = {
+0xbc, 0x62, 0x91, 0x89, 0x85, 0x42, 0x3d};
+static unsigned char courier14_249_bits[] = {
+0x0c, 0x30, 0x00, 0x63, 0x42, 0x42, 0x42, 0x42, 0x62, 0xdc};
+static unsigned char courier14_250_bits[] = {
+0x30, 0x0c, 0x00, 0x63, 0x42, 0x42, 0x42, 0x42, 0x62, 0xdc};
+static unsigned char courier14_251_bits[] = {
+0x08, 0x14, 0x00, 0x63, 0x42, 0x42, 0x42, 0x42, 0x62, 0xdc};
+static unsigned char courier14_252_bits[] = {
+0x36, 0x00, 0x63, 0x42, 0x42, 0x42, 0x42, 0x62, 0xdc};
+static unsigned char courier14_253_bits[] = {
+0x60, 0x18, 0x00, 0xe7, 0x42, 0x42, 0x24, 0x24, 0x18, 0x10, 0x08, 0x08, 
+0x1e};
+static unsigned char courier14_254_bits[] = {
+0x03, 0x02, 0x3a, 0x46, 0x82, 0x82, 0x82, 0x46, 0x3a, 0x02, 0x02, 0x0f};
+static unsigned char courier14_255_bits[] = {
+0x6c, 0x00, 0xe7, 0x42, 0x42, 0x24, 0x24, 0x18, 0x10, 0x08, 0x08, 0x1e};
+static RotFont courier14font[] = {
+{5, 1, 1, courier14_0_bits},
+{5, 1, 1, courier14_1_bits},
+{5, 1, 1, courier14_2_bits},
+{5, 1, 1, courier14_3_bits},
+{5, 1, 1, courier14_4_bits},
+{5, 1, 1, courier14_5_bits},
+{5, 1, 1, courier14_6_bits},
+{5, 1, 1, courier14_7_bits},
+{5, 1, 1, courier14_8_bits},
+{5, 1, 1, courier14_9_bits},
+{5, 1, 1, courier14_10_bits},
+{5, 1, 1, courier14_11_bits},
+{5, 1, 1, courier14_12_bits},
+{5, 1, 1, courier14_13_bits},
+{5, 1, 1, courier14_14_bits},
+{5, 1, 1, courier14_15_bits},
+{5, 1, 1, courier14_16_bits},
+{5, 1, 1, courier14_17_bits},
+{5, 1, 1, courier14_18_bits},
+{5, 1, 1, courier14_19_bits},
+{5, 1, 1, courier14_20_bits},
+{5, 1, 1, courier14_21_bits},
+{5, 1, 1, courier14_22_bits},
+{5, 1, 1, courier14_23_bits},
+{5, 1, 1, courier14_24_bits},
+{5, 1, 1, courier14_25_bits},
+{5, 1, 1, courier14_26_bits},
+{5, 1, 1, courier14_27_bits},
+{5, 1, 1, courier14_28_bits},
+{5, 1, 1, courier14_29_bits},
+{5, 1, 1, courier14_30_bits},
+{5, 1, 1, courier14_31_bits},
+{6, 1, 1, courier14_32_bits},
+{1, 9, 9, courier14_33_bits},
+{4, 4, 10, courier14_34_bits},
+{5, 10, 9, courier14_35_bits},
+{5, 13, 11, courier14_36_bits},
+{8, 10, 10, courier14_37_bits},
+{6, 8, 8, courier14_38_bits},
+{3, 4, 10, courier14_39_bits},
+{3, 12, 10, courier14_40_bits},
+{3, 12, 10, courier14_41_bits},
+{5, 6, 9, courier14_42_bits},
+{7, 7, 8, courier14_43_bits},
+{3, 4, 2, courier14_44_bits},
+{7, 1, 5, courier14_45_bits},
+{2, 2, 2, courier14_46_bits},
+{6, 11, 10, courier14_47_bits},
+{6, 10, 10, courier14_48_bits},
+{5, 10, 10, courier14_49_bits},
+{5, 10, 10, courier14_50_bits},
+{6, 10, 10, courier14_51_bits},
+{6, 10, 10, courier14_52_bits},
+{6, 10, 10, courier14_53_bits},
+{6, 10, 10, courier14_54_bits},
+{6, 10, 10, courier14_55_bits},
+{5, 10, 10, courier14_56_bits},
+{6, 10, 10, courier14_57_bits},
+{2, 7, 7, courier14_58_bits},
+{3, 9, 7, courier14_59_bits},
+{7, 7, 8, courier14_60_bits},
+{7, 3, 6, courier14_61_bits},
+{7, 7, 8, courier14_62_bits},
+{5, 9, 9, courier14_63_bits},
+{7, 10, 9, courier14_64_bits},
+{9, 9, 9, courier14_65_bits},
+{7, 9, 9, courier14_66_bits},
+{7, 9, 9, courier14_67_bits},
+{8, 9, 9, courier14_68_bits},
+{7, 9, 9, courier14_69_bits},
+{7, 9, 9, courier14_70_bits},
+{8, 9, 9, courier14_71_bits},
+{8, 9, 9, courier14_72_bits},
+{5, 9, 9, courier14_73_bits},
+{7, 9, 9, courier14_74_bits},
+{8, 9, 9, courier14_75_bits},
+{8, 9, 9, courier14_76_bits},
+{9, 9, 9, courier14_77_bits},
+{8, 9, 9, courier14_78_bits},
+{8, 9, 9, courier14_79_bits},
+{7, 9, 9, courier14_80_bits},
+{8, 11, 9, courier14_81_bits},
+{8, 9, 9, courier14_82_bits},
+{6, 9, 9, courier14_83_bits},
+{7, 9, 9, courier14_84_bits},
+{8, 9, 9, courier14_85_bits},
+{9, 9, 9, courier14_86_bits},
+{9, 9, 9, courier14_87_bits},
+{8, 9, 9, courier14_88_bits},
+{7, 9, 9, courier14_89_bits},
+{6, 9, 9, courier14_90_bits},
+{3, 12, 10, courier14_91_bits},
+{6, 11, 10, courier14_92_bits},
+{3, 12, 10, courier14_93_bits},
+{5, 5, 9, courier14_94_bits},
+{9, 1, -2, courier14_95_bits},
+{3, 4, 10, courier14_96_bits},
+{7, 7, 7, courier14_97_bits},
+{8, 10, 10, courier14_98_bits},
+{7, 7, 7, courier14_99_bits},
+{8, 10, 10, courier14_100_bits},
+{7, 7, 7, courier14_101_bits},
+{7, 10, 10, courier14_102_bits},
+{8, 10, 7, courier14_103_bits},
+{8, 10, 10, courier14_104_bits},
+{5, 10, 10, courier14_105_bits},
+{5, 13, 10, courier14_106_bits},
+{7, 10, 10, courier14_107_bits},
+{5, 10, 10, courier14_108_bits},
+{9, 7, 7, courier14_109_bits},
+{8, 7, 7, courier14_110_bits},
+{8, 7, 7, courier14_111_bits},
+{8, 10, 7, courier14_112_bits},
+{8, 10, 7, courier14_113_bits},
+{7, 7, 7, courier14_114_bits},
+{6, 7, 7, courier14_115_bits},
+{7, 9, 9, courier14_116_bits},
+{8, 7, 7, courier14_117_bits},
+{8, 7, 7, courier14_118_bits},
+{9, 7, 7, courier14_119_bits},
+{7, 7, 7, courier14_120_bits},
+{8, 10, 7, courier14_121_bits},
+{5, 7, 7, courier14_122_bits},
+{3, 12, 10, courier14_123_bits},
+{1, 11, 9, courier14_124_bits},
+{3, 12, 10, courier14_125_bits},
+{6, 2, 5, courier14_126_bits},
+{5, 1, 1, courier14_127_bits},
+{5, 1, 1, courier14_128_bits},
+{5, 1, 1, courier14_129_bits},
+{5, 1, 1, courier14_130_bits},
+{5, 1, 1, courier14_131_bits},
+{5, 1, 1, courier14_132_bits},
+{5, 1, 1, courier14_133_bits},
+{5, 1, 1, courier14_134_bits},
+{5, 1, 1, courier14_135_bits},
+{5, 1, 1, courier14_136_bits},
+{5, 1, 1, courier14_137_bits},
+{5, 1, 1, courier14_138_bits},
+{5, 1, 1, courier14_139_bits},
+{5, 1, 1, courier14_140_bits},
+{5, 1, 1, courier14_141_bits},
+{5, 1, 1, courier14_142_bits},
+{5, 1, 1, courier14_143_bits},
+{5, 1, 1, courier14_144_bits},
+{5, 1, 1, courier14_145_bits},
+{5, 1, 1, courier14_146_bits},
+{5, 1, 1, courier14_147_bits},
+{5, 1, 1, courier14_148_bits},
+{5, 1, 1, courier14_149_bits},
+{5, 1, 1, courier14_150_bits},
+{5, 1, 1, courier14_151_bits},
+{5, 1, 1, courier14_152_bits},
+{5, 1, 1, courier14_153_bits},
+{5, 1, 1, courier14_154_bits},
+{5, 1, 1, courier14_155_bits},
+{5, 1, 1, courier14_156_bits},
+{5, 1, 1, courier14_157_bits},
+{5, 1, 1, courier14_158_bits},
+{5, 1, 1, courier14_159_bits},
+{1, 1, 1, courier14_160_bits},
+{1, 9, 7, courier14_161_bits},
+{5, 9, 9, courier14_162_bits},
+{7, 9, 9, courier14_163_bits},
+{6, 6, 7, courier14_164_bits},
+{7, 9, 9, courier14_165_bits},
+{1, 11, 9, courier14_166_bits},
+{6, 10, 9, courier14_167_bits},
+{5, 1, 9, courier14_168_bits},
+{8, 9, 9, courier14_169_bits},
+{4, 6, 9, courier14_170_bits},
+{8, 7, 7, courier14_171_bits},
+{7, 3, 6, courier14_172_bits},
+{6, 1, 5, courier14_173_bits},
+{8, 9, 9, courier14_174_bits},
+{4, 1, 9, courier14_175_bits},
+{4, 4, 10, courier14_176_bits},
+{7, 7, 8, courier14_177_bits},
+{4, 6, 10, courier14_178_bits},
+{4, 6, 10, courier14_179_bits},
+{4, 2, 10, courier14_180_bits},
+{8, 10, 7, courier14_181_bits},
+{7, 10, 9, courier14_182_bits},
+{2, 2, 5, courier14_183_bits},
+{3, 3, 0, courier14_184_bits},
+{3, 6, 10, courier14_185_bits},
+{4, 6, 9, courier14_186_bits},
+{8, 7, 7, courier14_187_bits},
+{10, 10, 10, courier14_188_bits},
+{10, 10, 10, courier14_189_bits},
+{10, 10, 10, courier14_190_bits},
+{5, 9, 7, courier14_191_bits},
+{9, 12, 12, courier14_192_bits},
+{9, 12, 12, courier14_193_bits},
+{9, 12, 12, courier14_194_bits},
+{9, 12, 12, courier14_195_bits},
+{9, 11, 11, courier14_196_bits},
+{9, 12, 12, courier14_197_bits},
+{9, 9, 9, courier14_198_bits},
+{7, 12, 9, courier14_199_bits},
+{7, 12, 12, courier14_200_bits},
+{7, 12, 12, courier14_201_bits},
+{7, 12, 12, courier14_202_bits},
+{7, 11, 11, courier14_203_bits},
+{5, 12, 12, courier14_204_bits},
+{5, 12, 12, courier14_205_bits},
+{5, 12, 12, courier14_206_bits},
+{5, 11, 11, courier14_207_bits},
+{8, 9, 9, courier14_208_bits},
+{8, 12, 12, courier14_209_bits},
+{8, 12, 12, courier14_210_bits},
+{8, 12, 12, courier14_211_bits},
+{8, 12, 12, courier14_212_bits},
+{8, 12, 12, courier14_213_bits},
+{8, 11, 11, courier14_214_bits},
+{7, 7, 8, courier14_215_bits},
+{9, 9, 9, courier14_216_bits},
+{8, 12, 12, courier14_217_bits},
+{8, 12, 12, courier14_218_bits},
+{8, 12, 12, courier14_219_bits},
+{8, 11, 11, courier14_220_bits},
+{7, 12, 12, courier14_221_bits},
+{7, 9, 9, courier14_222_bits},
+{7, 9, 9, courier14_223_bits},
+{7, 10, 10, courier14_224_bits},
+{7, 10, 10, courier14_225_bits},
+{7, 10, 10, courier14_226_bits},
+{7, 10, 10, courier14_227_bits},
+{7, 9, 9, courier14_228_bits},
+{7, 10, 10, courier14_229_bits},
+{8, 7, 7, courier14_230_bits},
+{7, 10, 7, courier14_231_bits},
+{7, 10, 10, courier14_232_bits},
+{7, 10, 10, courier14_233_bits},
+{7, 10, 10, courier14_234_bits},
+{7, 9, 9, courier14_235_bits},
+{5, 10, 10, courier14_236_bits},
+{5, 10, 10, courier14_237_bits},
+{5, 10, 10, courier14_238_bits},
+{5, 9, 9, courier14_239_bits},
+{8, 12, 12, courier14_240_bits},
+{8, 10, 10, courier14_241_bits},
+{8, 10, 10, courier14_242_bits},
+{8, 10, 10, courier14_243_bits},
+{8, 10, 10, courier14_244_bits},
+{8, 10, 10, courier14_245_bits},
+{8, 9, 9, courier14_246_bits},
+{8, 7, 8, courier14_247_bits},
+{8, 7, 7, courier14_248_bits},
+{8, 10, 10, courier14_249_bits},
+{8, 10, 10, courier14_250_bits},
+{8, 10, 10, courier14_251_bits},
+{8, 9, 9, courier14_252_bits},
+{8, 13, 10, courier14_253_bits},
+{8, 12, 9, courier14_254_bits},
+{8, 12, 9, courier14_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier18.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier18.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier18.bdf	(revision 22322)
@@ -0,0 +1,3453 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Courier-Medium-R-Normal--18-180-75-75-M-110-ISO8859-1
+SIZE 18 75 75
+FONTBOUNDINGBOX 12 19 -1 -4
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Courier"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 18
+POINT_SIZE 180
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "M"
+AVERAGE_WIDTH 110
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 11
+X_HEIGHT 8
+FACE_NAME "Courier"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "No mark"
+_DEC_DEVICE_FONTNAMES "PS=Courier"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2, 07-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Courier"
+FONT "-Adobe-Courier-Medium-R-Normal--18-180-75-75-M-110-ISO8859-1"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 11
+DEFAULT_CHAR 32
+FONT_ASCENT 14
+FONT_DESCENT 4
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 2 12 4 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+40
+40
+00
+c0
+c0
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 5 5 3 7
+BITMAP
+d8
+d8
+d8
+90
+90
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 15 1 -2
+BITMAP
+1200
+1200
+1200
+1200
+1200
+ff80
+2400
+2400
+2400
+ff00
+2400
+2400
+2400
+2400
+2400
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 15 2 -2
+BITMAP
+10
+10
+7a
+86
+82
+80
+70
+0c
+02
+82
+c2
+bc
+10
+10
+10
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+7000
+8800
+8800
+8800
+7180
+0e00
+3000
+ce00
+1100
+1100
+1100
+0e00
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 10 2 0
+BITMAP
+38
+44
+40
+40
+20
+74
+88
+88
+98
+66
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 4 5 3 7
+BITMAP
+30
+30
+60
+60
+c0
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 3 14 5 -2
+BITMAP
+20
+40
+40
+80
+80
+80
+80
+80
+80
+80
+80
+40
+40
+20
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 3 14 3 -2
+BITMAP
+80
+40
+40
+20
+20
+20
+20
+20
+20
+20
+20
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 8 2 4
+BITMAP
+10
+10
+10
+fe
+38
+28
+44
+44
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 9 1 1
+BITMAP
+0800
+0800
+0800
+0800
+ff80
+0800
+0800
+0800
+0800
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 4 5 3 -3
+BITMAP
+30
+30
+60
+60
+c0
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 1 1 5
+BITMAP
+ff80
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 2 2 4 0
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 16 1 -3
+BITMAP
+01
+01
+02
+02
+04
+04
+08
+08
+10
+10
+20
+20
+40
+40
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+38
+44
+82
+82
+82
+82
+82
+82
+82
+82
+44
+38
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+30
+d0
+10
+10
+10
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 1 0
+BITMAP
+1c
+22
+41
+41
+01
+02
+04
+08
+10
+20
+41
+ff
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+78
+84
+02
+02
+04
+38
+04
+02
+02
+02
+84
+78
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+0c
+14
+14
+24
+24
+44
+44
+84
+fe
+04
+04
+1e
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 1 0
+BITMAP
+7e
+40
+40
+40
+5c
+62
+01
+01
+01
+01
+c2
+3c
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+1e
+20
+40
+40
+80
+b8
+c4
+82
+82
+82
+44
+38
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 2 0
+BITMAP
+ff
+81
+02
+02
+04
+04
+04
+04
+08
+08
+08
+08
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+38
+44
+82
+82
+44
+38
+44
+82
+82
+82
+44
+38
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+38
+44
+82
+82
+82
+46
+3a
+02
+02
+04
+08
+f0
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 2 8 5 0
+BITMAP
+c0
+c0
+00
+00
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 5 11 2 -3
+BITMAP
+18
+18
+00
+00
+00
+00
+30
+30
+60
+60
+c0
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 9 0 1
+BITMAP
+00c0
+0300
+0c00
+3000
+c000
+3000
+0c00
+0300
+00c0
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 4 1 3
+BITMAP
+ff80
+0000
+0000
+ff80
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 9 0 1
+BITMAP
+c000
+3000
+0c00
+0300
+00c0
+0300
+0c00
+3000
+c000
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 11 2 0
+BITMAP
+7c
+86
+82
+02
+04
+08
+30
+20
+00
+30
+30
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 13 2 -1
+BITMAP
+3c
+42
+82
+82
+8e
+92
+92
+92
+8f
+80
+80
+43
+3c
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 11 0 0
+BITMAP
+3c00
+0400
+0a00
+0a00
+1100
+1100
+2080
+3f80
+4040
+4040
+f1e0
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+fe00
+4100
+4100
+4100
+4200
+7e00
+4100
+4080
+4080
+4100
+fe00
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+1e80
+6180
+4080
+8000
+8000
+8000
+8000
+8000
+4080
+6100
+1e00
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 1 0
+BITMAP
+fc
+42
+41
+41
+41
+41
+41
+41
+41
+42
+fc
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 1 0
+BITMAP
+ff
+41
+41
+41
+48
+78
+48
+41
+41
+41
+ff
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 1 0
+BITMAP
+ff
+41
+41
+41
+48
+78
+48
+40
+40
+40
+f0
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 11 1 0
+BITMAP
+1e80
+6180
+4080
+8000
+8000
+8000
+83c0
+8080
+4080
+6100
+1e00
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+e380
+4100
+4100
+4100
+4100
+7f00
+4100
+4100
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 11 2 0
+BITMAP
+fe
+10
+10
+10
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+1f80
+0200
+0200
+0200
+0200
+0200
+8200
+8200
+8200
+4400
+3800
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 11 0 0
+BITMAP
+f3c0
+4100
+4200
+4400
+4800
+5800
+6400
+4200
+4200
+4100
+f1c0
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+f800
+2000
+2000
+2000
+2000
+2000
+2000
+2080
+2080
+2080
+ff80
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 11 0 0
+BITMAP
+e0e0
+60c0
+5140
+5140
+4a40
+4a40
+4440
+4440
+4040
+4040
+f1e0
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+e780
+6100
+5100
+5100
+4900
+4900
+4500
+4500
+4300
+4300
+f100
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+fe00
+4100
+4080
+4080
+4080
+4100
+7e00
+4000
+4000
+4000
+f800
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 1 -2
+BITMAP
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+3880
+4700
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 11 1 0
+BITMAP
+fe00
+4100
+4100
+4100
+4200
+7c00
+4200
+4200
+4100
+4100
+f0c0
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 2 0
+BITMAP
+3a
+46
+82
+80
+40
+3c
+02
+01
+81
+c2
+bc
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+ff80
+8880
+8880
+8880
+0800
+0800
+0800
+0800
+0800
+0800
+3e00
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 11 0 0
+BITMAP
+f3c0
+4080
+4080
+4080
+4080
+4080
+4080
+4080
+4080
+2100
+1e00
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 11 0 0
+BITMAP
+f1e0
+4040
+4040
+2080
+2080
+1100
+1100
+0a00
+0a00
+0400
+0400
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 11 0 0
+BITMAP
+f1e0
+4040
+4440
+4440
+4440
+2a80
+2a80
+2a80
+2a80
+1100
+1100
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+e380
+4100
+2200
+2200
+1400
+0800
+1400
+2200
+2200
+4100
+e380
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+e380
+4100
+2200
+2200
+1400
+1400
+0800
+0800
+0800
+0800
+3e00
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 11 2 0
+BITMAP
+fe
+82
+84
+08
+08
+10
+20
+20
+42
+82
+fe
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 3 15 5 -3
+BITMAP
+e0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+e0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 16 2 -3
+BITMAP
+80
+80
+40
+40
+20
+20
+10
+10
+08
+08
+04
+04
+02
+02
+01
+01
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 3 15 3 -3
+BITMAP
+e0
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+e0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 4 2 8
+BITMAP
+10
+28
+44
+82
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 1 0 -4
+BITMAP
+ffe0
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 4 5 3 7
+BITMAP
+c0
+c0
+60
+60
+30
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 8 1 0
+BITMAP
+3c00
+4200
+0200
+7e00
+8200
+8200
+8600
+7b80
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+c000
+4000
+4000
+4000
+5e00
+6100
+4080
+4080
+4080
+4080
+6100
+de00
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 8 1 0
+BITMAP
+3d
+43
+81
+80
+80
+80
+43
+3c
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+0300
+0100
+0100
+0100
+3d00
+4300
+8100
+8100
+8100
+8100
+4300
+3d80
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 8 1 0
+BITMAP
+3c
+42
+81
+ff
+80
+80
+43
+3c
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 2 0
+BITMAP
+0f
+10
+20
+20
+fe
+20
+20
+20
+20
+20
+20
+fe
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 -4
+BITMAP
+3d80
+4300
+8100
+8100
+8100
+8100
+4300
+3d00
+0100
+0100
+0200
+7c00
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+c000
+4000
+4000
+4000
+5e00
+6100
+4100
+4100
+4100
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 11 2 0
+BITMAP
+10
+10
+00
+70
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 6 15 2 -4
+BITMAP
+08
+08
+00
+fc
+04
+04
+04
+04
+04
+04
+04
+04
+04
+08
+f0
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+c000
+4000
+4000
+4000
+4f00
+4400
+4800
+7000
+4800
+4400
+4200
+c780
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+f0
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 8 0 0
+BITMAP
+d980
+6640
+4440
+4440
+4440
+4440
+4440
+e660
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 8 1 0
+BITMAP
+de00
+6100
+4100
+4100
+4100
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 8 1 0
+BITMAP
+3c
+42
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 -4
+BITMAP
+de00
+6100
+4080
+4080
+4080
+4080
+6100
+5e00
+4000
+4000
+4000
+f000
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 -4
+BITMAP
+3d80
+4300
+8100
+8100
+8100
+8100
+4300
+3d00
+0100
+0100
+0100
+0780
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 8 1 0
+BITMAP
+ee
+31
+20
+20
+20
+20
+20
+fe
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 8 2 0
+BITMAP
+7a
+86
+82
+70
+0c
+82
+c2
+bc
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 2 0
+BITMAP
+20
+20
+20
+fe
+20
+20
+20
+20
+20
+21
+1e
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 8 1 0
+BITMAP
+c300
+4100
+4100
+4100
+4100
+4100
+4300
+3d80
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 8 1 0
+BITMAP
+e380
+4100
+2200
+2200
+1400
+1400
+0800
+0800
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 8 1 0
+BITMAP
+e380
+4100
+4900
+4900
+5500
+5500
+2200
+2200
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 8 1 0
+BITMAP
+e7
+42
+24
+18
+18
+24
+42
+e7
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 -4
+BITMAP
+e380
+4100
+4100
+2200
+2200
+1400
+1400
+0800
+0800
+1000
+1000
+f800
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 8 2 0
+BITMAP
+fe
+82
+84
+08
+10
+22
+42
+fe
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 5 15 3 -3
+BITMAP
+18
+20
+20
+20
+20
+20
+c0
+20
+20
+20
+20
+20
+20
+20
+18
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 1 15 5 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 5 15 3 -3
+BITMAP
+c0
+20
+20
+20
+20
+20
+18
+20
+20
+20
+20
+20
+20
+20
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 3 1 4
+BITMAP
+60
+99
+06
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 2 13 4 -4
+BITMAP
+c0
+c0
+00
+00
+80
+80
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 6 12 2 0
+BITMAP
+10
+10
+10
+3c
+44
+84
+80
+80
+44
+38
+10
+10
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 1 0
+BITMAP
+1c
+22
+20
+20
+10
+fc
+10
+20
+21
+41
+7e
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 6 7 2 2
+BITMAP
+84
+78
+84
+84
+84
+78
+84
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+e380
+4100
+2200
+2200
+1400
+1400
+7f00
+0800
+7f00
+0800
+3e00
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 1 15 5 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+00
+00
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 13 2 -1
+BITMAP
+3e
+42
+42
+40
+70
+8c
+82
+62
+1c
+04
+84
+84
+f8
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 5 1 3 10
+BITMAP
+d8
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 11 0 0
+BITMAP
+1e00
+6180
+4e80
+9240
+a040
+a040
+a040
+9240
+4c80
+6180
+1e00
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 6 8 2 3
+BITMAP
+70
+08
+78
+88
+98
+ec
+00
+fc
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 8 1 0
+BITMAP
+0880
+3300
+6600
+cc00
+cc00
+6600
+3300
+0880
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 4 1 3
+BITMAP
+ff80
+0080
+0080
+0080
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 1 1 5
+BITMAP
+ff
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 11 0 0
+BITMAP
+1f00
+60c0
+5e40
+9120
+9120
+9e20
+9420
+9220
+51c0
+6080
+1f00
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 5 1 3 9
+BITMAP
+f8
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 5 5 3 7
+BITMAP
+70
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 9 1 1
+BITMAP
+0800
+0800
+0800
+ff80
+0800
+0800
+0800
+0000
+ff80
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 5 7 3 5
+BITMAP
+70
+88
+08
+10
+20
+40
+f8
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 5 7 3 5
+BITMAP
+70
+88
+08
+30
+08
+88
+70
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 3 3 4 9
+BITMAP
+20
+40
+80
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 -4
+BITMAP
+c300
+4100
+4100
+4100
+4100
+4100
+4300
+7d80
+4000
+4000
+4000
+4000
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 13 1 -1
+BITMAP
+3f
+4a
+8a
+8a
+8a
+4a
+3a
+0a
+0a
+0a
+0a
+0a
+3b
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 2 2 4 5
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 3 3 4 -3
+BITMAP
+40
+20
+c0
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 5 7 3 5
+BITMAP
+20
+e0
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 6 8 2 3
+BITMAP
+78
+84
+84
+84
+84
+78
+00
+fc
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 8 1 0
+BITMAP
+8800
+6600
+3300
+1980
+1980
+3300
+6600
+8800
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 13 0 0
+BITMAP
+2000
+e000
+2040
+2080
+2100
+2100
+fa40
+04c0
+0940
+0a40
+13e0
+2040
+00e0
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 13 0 0
+BITMAP
+2000
+e000
+2040
+2080
+2100
+2100
+fac0
+0520
+0920
+0840
+1080
+2100
+03e0
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 13 0 0
+BITMAP
+7000
+8800
+0840
+3080
+0900
+8900
+7240
+04c0
+0940
+0a40
+13e0
+2040
+00e0
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 6 12 2 -3
+BITMAP
+18
+18
+00
+10
+10
+70
+80
+80
+84
+84
+84
+78
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 15 0 0
+BITMAP
+1000
+0800
+0400
+0000
+3c00
+0400
+0a00
+0a00
+1100
+1100
+2080
+3f80
+4040
+4040
+f1e0
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 15 0 0
+BITMAP
+0200
+0400
+0800
+0000
+3c00
+0400
+0a00
+0a00
+1100
+1100
+2080
+3f80
+4040
+4040
+f1e0
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 15 0 0
+BITMAP
+0400
+0a00
+1100
+0000
+3c00
+0400
+0a00
+0a00
+1100
+1100
+2080
+3f80
+4040
+4040
+f1e0
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 14 0 0
+BITMAP
+1900
+2600
+0000
+3c00
+0400
+0a00
+0a00
+1100
+1100
+2080
+3f80
+4040
+4040
+f1e0
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 14 0 0
+BITMAP
+1b00
+0000
+0000
+3c00
+0400
+0a00
+0a00
+1100
+1100
+2080
+3f80
+4040
+4040
+f1e0
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 15 0 0
+BITMAP
+0c00
+1200
+1200
+0c00
+7c00
+0400
+0a00
+0a00
+1100
+1100
+2080
+3f80
+4040
+4040
+f1e0
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 11 -1 0
+BITMAP
+1fe0
+0620
+0a20
+0a00
+1240
+13c0
+3e40
+2200
+4220
+4220
+e7e0
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 -3
+BITMAP
+1e80
+6180
+4080
+8000
+8000
+8000
+8000
+8000
+4080
+6100
+1e00
+0800
+0400
+1800
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 15 1 0
+BITMAP
+20
+10
+08
+00
+ff
+41
+41
+41
+48
+78
+48
+41
+41
+41
+ff
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 15 1 0
+BITMAP
+04
+08
+10
+00
+ff
+41
+41
+41
+48
+78
+48
+41
+41
+41
+ff
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 15 1 0
+BITMAP
+18
+24
+42
+00
+ff
+41
+41
+41
+48
+78
+48
+41
+41
+41
+ff
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 14 1 0
+BITMAP
+66
+00
+00
+ff
+41
+41
+41
+48
+78
+48
+41
+41
+41
+ff
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 15 2 0
+BITMAP
+20
+10
+08
+00
+fe
+10
+10
+10
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 15 2 0
+BITMAP
+08
+10
+20
+00
+fe
+10
+10
+10
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 15 2 0
+BITMAP
+10
+28
+44
+00
+fe
+10
+10
+10
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 14 2 0
+BITMAP
+6c
+00
+00
+fe
+10
+10
+10
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 1 0
+BITMAP
+fc
+42
+41
+41
+41
+f1
+41
+41
+41
+42
+fc
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+1900
+2600
+0000
+e780
+6100
+5100
+5100
+4900
+4900
+4500
+4500
+4300
+4300
+f100
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 15 1 0
+BITMAP
+1000
+0800
+0400
+0000
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 15 1 0
+BITMAP
+0400
+0800
+1000
+0000
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 15 1 0
+BITMAP
+0800
+1400
+2200
+0000
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+1900
+2600
+0000
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+3600
+0000
+0000
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 9 1 1
+BITMAP
+8080
+4100
+2200
+1400
+0800
+1400
+2200
+4100
+8080
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 11 0 0
+BITMAP
+0e20
+3140
+2080
+4140
+4240
+4440
+4840
+5040
+2080
+5180
+8e00
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 15 0 0
+BITMAP
+1000
+0800
+0400
+0000
+f3c0
+4080
+4080
+4080
+4080
+4080
+4080
+4080
+4080
+2100
+1e00
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 15 0 0
+BITMAP
+0200
+0400
+0800
+0000
+f3c0
+4080
+4080
+4080
+4080
+4080
+4080
+4080
+4080
+2100
+1e00
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 15 0 0
+BITMAP
+0c00
+1200
+2100
+0000
+f3c0
+4080
+4080
+4080
+4080
+4080
+4080
+4080
+4080
+2100
+1e00
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 14 0 0
+BITMAP
+3300
+0000
+0000
+f3c0
+4080
+4080
+4080
+4080
+4080
+4080
+4080
+4080
+2100
+1e00
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 15 1 0
+BITMAP
+0200
+0400
+0800
+0000
+e380
+4100
+2200
+2200
+1400
+1400
+0800
+0800
+0800
+0800
+3e00
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+e000
+4000
+7e00
+4100
+4080
+4080
+4100
+7e00
+4000
+4000
+e000
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 1 0
+BITMAP
+1c
+22
+42
+42
+44
+4c
+42
+41
+41
+41
+49
+e6
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+2000
+1000
+0800
+0000
+3c00
+4200
+0200
+7e00
+8200
+8200
+8600
+7b80
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+0400
+0800
+1000
+0000
+3c00
+4200
+0200
+7e00
+8200
+8200
+8600
+7b80
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+1800
+2400
+4200
+0000
+3c00
+4200
+0200
+7e00
+8200
+8200
+8600
+7b80
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+3200
+4c00
+0000
+3c00
+4200
+0200
+7e00
+8200
+8200
+8600
+7b80
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+3600
+0000
+0000
+3c00
+4200
+0200
+7e00
+8200
+8200
+8600
+7b80
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 1 0
+BITMAP
+1800
+2400
+2400
+1800
+0000
+3c00
+4200
+0200
+7e00
+8200
+8200
+8600
+7b80
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 8 0 0
+BITMAP
+7180
+8a40
+0420
+7fe0
+8400
+8400
+8a20
+71c0
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 1 -3
+BITMAP
+3d
+43
+81
+80
+80
+80
+43
+3c
+10
+08
+30
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 1 0
+BITMAP
+20
+10
+08
+00
+3c
+42
+81
+ff
+80
+80
+43
+3c
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 1 0
+BITMAP
+04
+08
+10
+00
+3c
+42
+81
+ff
+80
+80
+43
+3c
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 1 0
+BITMAP
+18
+24
+42
+00
+3c
+42
+81
+ff
+80
+80
+43
+3c
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 1 0
+BITMAP
+66
+00
+00
+3c
+42
+81
+ff
+80
+80
+43
+3c
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+40
+20
+10
+00
+70
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+08
+10
+20
+00
+70
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 12 2 0
+BITMAP
+10
+28
+44
+00
+70
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 11 2 0
+BITMAP
+6c
+00
+00
+70
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 1 0
+BITMAP
+73
+8c
+34
+42
+02
+3d
+43
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+1900
+2600
+0000
+de00
+6100
+4100
+4100
+4100
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 1 0
+BITMAP
+20
+10
+08
+00
+3c
+42
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 1 0
+BITMAP
+02
+04
+08
+00
+3c
+42
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 12 1 0
+BITMAP
+18
+24
+42
+00
+3c
+42
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 1 0
+BITMAP
+32
+4c
+00
+3c
+42
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 11 1 0
+BITMAP
+66
+00
+00
+3c
+42
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 9 1 1
+BITMAP
+18
+18
+00
+00
+ff
+00
+00
+18
+18
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 8 1 0
+BITMAP
+3d
+42
+85
+89
+91
+a1
+42
+bc
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+1000
+0800
+0400
+0000
+c300
+4100
+4100
+4100
+4100
+4100
+4300
+3d80
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+0400
+0800
+1000
+0000
+c300
+4100
+4100
+4100
+4100
+4100
+4300
+3d80
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+0800
+1400
+2200
+0000
+c300
+4100
+4100
+4100
+4100
+4100
+4300
+3d80
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+3600
+0000
+0000
+c300
+4100
+4100
+4100
+4100
+4100
+4300
+3d80
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 16 1 -4
+BITMAP
+0200
+0400
+0800
+0000
+e380
+4100
+4100
+2200
+2200
+1400
+1400
+0800
+0800
+1000
+1000
+f800
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 16 1 -4
+BITMAP
+c000
+4000
+4000
+4000
+5e00
+6100
+4080
+4080
+4080
+4080
+6100
+5e00
+4000
+4000
+4000
+f000
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 15 1 -4
+BITMAP
+3600
+0000
+0000
+e380
+4100
+4100
+2200
+2200
+1400
+1400
+0800
+0800
+1000
+1000
+f800
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier18.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier18.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier18.h	(revision 22322)
@@ -0,0 +1,906 @@
+static unsigned char courier18_0_bits[] = {
+0x00};
+static unsigned char courier18_1_bits[] = {
+0x00};
+static unsigned char courier18_2_bits[] = {
+0x00};
+static unsigned char courier18_3_bits[] = {
+0x00};
+static unsigned char courier18_4_bits[] = {
+0x00};
+static unsigned char courier18_5_bits[] = {
+0x00};
+static unsigned char courier18_6_bits[] = {
+0x00};
+static unsigned char courier18_7_bits[] = {
+0x00};
+static unsigned char courier18_8_bits[] = {
+0x00};
+static unsigned char courier18_9_bits[] = {
+0x00};
+static unsigned char courier18_10_bits[] = {
+0x00};
+static unsigned char courier18_11_bits[] = {
+0x00};
+static unsigned char courier18_12_bits[] = {
+0x00};
+static unsigned char courier18_13_bits[] = {
+0x00};
+static unsigned char courier18_14_bits[] = {
+0x00};
+static unsigned char courier18_15_bits[] = {
+0x00};
+static unsigned char courier18_16_bits[] = {
+0x00};
+static unsigned char courier18_17_bits[] = {
+0x00};
+static unsigned char courier18_18_bits[] = {
+0x00};
+static unsigned char courier18_19_bits[] = {
+0x00};
+static unsigned char courier18_20_bits[] = {
+0x00};
+static unsigned char courier18_21_bits[] = {
+0x00};
+static unsigned char courier18_22_bits[] = {
+0x00};
+static unsigned char courier18_23_bits[] = {
+0x00};
+static unsigned char courier18_24_bits[] = {
+0x00};
+static unsigned char courier18_25_bits[] = {
+0x00};
+static unsigned char courier18_26_bits[] = {
+0x00};
+static unsigned char courier18_27_bits[] = {
+0x00};
+static unsigned char courier18_28_bits[] = {
+0x00};
+static unsigned char courier18_29_bits[] = {
+0x00};
+static unsigned char courier18_30_bits[] = {
+0x00};
+static unsigned char courier18_31_bits[] = {
+0x00};
+static unsigned char courier18_32_bits[] = {
+0x00};
+static unsigned char courier18_33_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x00, 0x03, 0x03};
+static unsigned char courier18_34_bits[] = {
+0x1b, 0x1b, 0x1b, 0x09, 0x09};
+static unsigned char courier18_35_bits[] = {
+0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0xff, 0x01, 
+0x24, 0x00, 0x24, 0x00, 0x24, 0x00, 0xff, 0x00, 0x24, 0x00, 0x24, 0x00, 
+0x24, 0x00, 0x24, 0x00, 0x24, 0x00};
+static unsigned char courier18_36_bits[] = {
+0x08, 0x08, 0x5e, 0x61, 0x41, 0x01, 0x0e, 0x30, 0x40, 0x41, 0x43, 0x3d, 
+0x08, 0x08, 0x08};
+static unsigned char courier18_37_bits[] = {
+0x0e, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x8e, 0x01, 0x70, 0x00, 
+0x0c, 0x00, 0x73, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x70, 0x00};
+static unsigned char courier18_38_bits[] = {
+0x1c, 0x22, 0x02, 0x02, 0x04, 0x2e, 0x11, 0x11, 0x19, 0x66};
+static unsigned char courier18_39_bits[] = {
+0x0c, 0x0c, 0x06, 0x06, 0x03};
+static unsigned char courier18_40_bits[] = {
+0x04, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 
+0x02, 0x04};
+static unsigned char courier18_41_bits[] = {
+0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 
+0x02, 0x01};
+static unsigned char courier18_42_bits[] = {
+0x08, 0x08, 0x08, 0x7f, 0x1c, 0x14, 0x22, 0x22};
+static unsigned char courier18_43_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xff, 0x01, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char courier18_44_bits[] = {
+0x0c, 0x0c, 0x06, 0x06, 0x03};
+static unsigned char courier18_45_bits[] = {
+0xff, 0x01};
+static unsigned char courier18_46_bits[] = {
+0x03, 0x03};
+static unsigned char courier18_47_bits[] = {
+0x80, 0x80, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 
+0x02, 0x02, 0x01, 0x01};
+static unsigned char courier18_48_bits[] = {
+0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c};
+static unsigned char courier18_49_bits[] = {
+0x0c, 0x0b, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f};
+static unsigned char courier18_50_bits[] = {
+0x38, 0x44, 0x82, 0x82, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x82, 0xff};
+static unsigned char courier18_51_bits[] = {
+0x1e, 0x21, 0x40, 0x40, 0x20, 0x1c, 0x20, 0x40, 0x40, 0x40, 0x21, 0x1e};
+static unsigned char courier18_52_bits[] = {
+0x30, 0x28, 0x28, 0x24, 0x24, 0x22, 0x22, 0x21, 0x7f, 0x20, 0x20, 0x78};
+static unsigned char courier18_53_bits[] = {
+0x7e, 0x02, 0x02, 0x02, 0x3a, 0x46, 0x80, 0x80, 0x80, 0x80, 0x43, 0x3c};
+static unsigned char courier18_54_bits[] = {
+0x78, 0x04, 0x02, 0x02, 0x01, 0x1d, 0x23, 0x41, 0x41, 0x41, 0x22, 0x1c};
+static unsigned char courier18_55_bits[] = {
+0xff, 0x81, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, 0x10};
+static unsigned char courier18_56_bits[] = {
+0x1c, 0x22, 0x41, 0x41, 0x22, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x22, 0x1c};
+static unsigned char courier18_57_bits[] = {
+0x1c, 0x22, 0x41, 0x41, 0x41, 0x62, 0x5c, 0x40, 0x40, 0x20, 0x10, 0x0f};
+static unsigned char courier18_58_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03};
+static unsigned char courier18_59_bits[] = {
+0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x06, 0x06, 0x03};
+static unsigned char courier18_60_bits[] = {
+0x00, 0x03, 0xc0, 0x00, 0x30, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x0c, 0x00, 
+0x30, 0x00, 0xc0, 0x00, 0x00, 0x03};
+static unsigned char courier18_61_bits[] = {
+0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01};
+static unsigned char courier18_62_bits[] = {
+0x03, 0x00, 0x0c, 0x00, 0x30, 0x00, 0xc0, 0x00, 0x00, 0x03, 0xc0, 0x00, 
+0x30, 0x00, 0x0c, 0x00, 0x03, 0x00};
+static unsigned char courier18_63_bits[] = {
+0x3e, 0x61, 0x41, 0x40, 0x20, 0x10, 0x0c, 0x04, 0x00, 0x0c, 0x0c};
+static unsigned char courier18_64_bits[] = {
+0x3c, 0x42, 0x41, 0x41, 0x71, 0x49, 0x49, 0x49, 0xf1, 0x01, 0x01, 0xc2, 
+0x3c};
+static unsigned char courier18_65_bits[] = {
+0x3c, 0x00, 0x20, 0x00, 0x50, 0x00, 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 
+0x04, 0x01, 0xfc, 0x01, 0x02, 0x02, 0x02, 0x02, 0x8f, 0x07};
+static unsigned char courier18_66_bits[] = {
+0x7f, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x42, 0x00, 0x7e, 0x00, 
+0x82, 0x00, 0x02, 0x01, 0x02, 0x01, 0x82, 0x00, 0x7f, 0x00};
+static unsigned char courier18_67_bits[] = {
+0x78, 0x01, 0x86, 0x01, 0x02, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x02, 0x01, 0x86, 0x00, 0x78, 0x00};
+static unsigned char courier18_68_bits[] = {
+0x3f, 0x42, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x42, 0x3f};
+static unsigned char courier18_69_bits[] = {
+0xff, 0x82, 0x82, 0x82, 0x12, 0x1e, 0x12, 0x82, 0x82, 0x82, 0xff};
+static unsigned char courier18_70_bits[] = {
+0xff, 0x82, 0x82, 0x82, 0x12, 0x1e, 0x12, 0x02, 0x02, 0x02, 0x0f};
+static unsigned char courier18_71_bits[] = {
+0x78, 0x01, 0x86, 0x01, 0x02, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0xc1, 0x03, 0x01, 0x01, 0x02, 0x01, 0x86, 0x00, 0x78, 0x00};
+static unsigned char courier18_72_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xfe, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char courier18_73_bits[] = {
+0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f};
+static unsigned char courier18_74_bits[] = {
+0xf8, 0x01, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 
+0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x22, 0x00, 0x1c, 0x00};
+static unsigned char courier18_75_bits[] = {
+0xcf, 0x03, 0x82, 0x00, 0x42, 0x00, 0x22, 0x00, 0x12, 0x00, 0x1a, 0x00, 
+0x26, 0x00, 0x42, 0x00, 0x42, 0x00, 0x82, 0x00, 0x8f, 0x03};
+static unsigned char courier18_76_bits[] = {
+0x1f, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 
+0x04, 0x00, 0x04, 0x01, 0x04, 0x01, 0x04, 0x01, 0xff, 0x01};
+static unsigned char courier18_77_bits[] = {
+0x07, 0x07, 0x06, 0x03, 0x8a, 0x02, 0x8a, 0x02, 0x52, 0x02, 0x52, 0x02, 
+0x22, 0x02, 0x22, 0x02, 0x02, 0x02, 0x02, 0x02, 0x8f, 0x07};
+static unsigned char courier18_78_bits[] = {
+0xe7, 0x01, 0x86, 0x00, 0x8a, 0x00, 0x8a, 0x00, 0x92, 0x00, 0x92, 0x00, 
+0xa2, 0x00, 0xa2, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0x8f, 0x00};
+static unsigned char courier18_79_bits[] = {
+0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 0xc6, 0x00, 0x38, 0x00};
+static unsigned char courier18_80_bits[] = {
+0x7f, 0x00, 0x82, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x82, 0x00, 
+0x7e, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x1f, 0x00};
+static unsigned char courier18_81_bits[] = {
+0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 0xc6, 0x00, 0x38, 0x00, 0x1c, 0x01, 
+0xe2, 0x00};
+static unsigned char courier18_82_bits[] = {
+0x7f, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x42, 0x00, 0x3e, 0x00, 
+0x42, 0x00, 0x42, 0x00, 0x82, 0x00, 0x82, 0x00, 0x0f, 0x03};
+static unsigned char courier18_83_bits[] = {
+0x5c, 0x62, 0x41, 0x01, 0x02, 0x3c, 0x40, 0x80, 0x81, 0x43, 0x3d};
+static unsigned char courier18_84_bits[] = {
+0xff, 0x01, 0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x10, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x7c, 0x00};
+static unsigned char courier18_85_bits[] = {
+0xcf, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x84, 0x00, 0x78, 0x00};
+static unsigned char courier18_86_bits[] = {
+0x8f, 0x07, 0x02, 0x02, 0x02, 0x02, 0x04, 0x01, 0x04, 0x01, 0x88, 0x00, 
+0x88, 0x00, 0x50, 0x00, 0x50, 0x00, 0x20, 0x00, 0x20, 0x00};
+static unsigned char courier18_87_bits[] = {
+0x8f, 0x07, 0x02, 0x02, 0x22, 0x02, 0x22, 0x02, 0x22, 0x02, 0x54, 0x01, 
+0x54, 0x01, 0x54, 0x01, 0x54, 0x01, 0x88, 0x00, 0x88, 0x00};
+static unsigned char courier18_88_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char courier18_89_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x28, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x7c, 0x00};
+static unsigned char courier18_90_bits[] = {
+0x7f, 0x41, 0x21, 0x10, 0x10, 0x08, 0x04, 0x04, 0x42, 0x41, 0x7f};
+static unsigned char courier18_91_bits[] = {
+0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x07};
+static unsigned char courier18_92_bits[] = {
+0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 
+0x40, 0x40, 0x80, 0x80};
+static unsigned char courier18_93_bits[] = {
+0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0x07};
+static unsigned char courier18_94_bits[] = {
+0x08, 0x14, 0x22, 0x41};
+static unsigned char courier18_95_bits[] = {
+0xff, 0x07};
+static unsigned char courier18_96_bits[] = {
+0x03, 0x03, 0x06, 0x06, 0x0c};
+static unsigned char courier18_97_bits[] = {
+0x3c, 0x00, 0x42, 0x00, 0x40, 0x00, 0x7e, 0x00, 0x41, 0x00, 0x41, 0x00, 
+0x61, 0x00, 0xde, 0x01};
+static unsigned char courier18_98_bits[] = {
+0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7a, 0x00, 0x86, 0x00, 
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x86, 0x00, 0x7b, 0x00};
+static unsigned char courier18_99_bits[] = {
+0xbc, 0xc2, 0x81, 0x01, 0x01, 0x01, 0xc2, 0x3c};
+static unsigned char courier18_100_bits[] = {
+0xc0, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xbc, 0x00, 0xc2, 0x00, 
+0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0xc2, 0x00, 0xbc, 0x01};
+static unsigned char courier18_101_bits[] = {
+0x3c, 0x42, 0x81, 0xff, 0x01, 0x01, 0xc2, 0x3c};
+static unsigned char courier18_102_bits[] = {
+0xf0, 0x08, 0x04, 0x04, 0x7f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x7f};
+static unsigned char courier18_103_bits[] = {
+0xbc, 0x01, 0xc2, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 
+0xc2, 0x00, 0xbc, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x3e, 0x00};
+static unsigned char courier18_104_bits[] = {
+0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7a, 0x00, 0x86, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char courier18_105_bits[] = {
+0x08, 0x08, 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f};
+static unsigned char courier18_106_bits[] = {
+0x10, 0x10, 0x00, 0x3f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
+0x20, 0x10, 0x0f};
+static unsigned char courier18_107_bits[] = {
+0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0xf2, 0x00, 0x22, 0x00, 
+0x12, 0x00, 0x0e, 0x00, 0x12, 0x00, 0x22, 0x00, 0x42, 0x00, 0xe3, 0x01};
+static unsigned char courier18_108_bits[] = {
+0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f};
+static unsigned char courier18_109_bits[] = {
+0x9b, 0x01, 0x66, 0x02, 0x22, 0x02, 0x22, 0x02, 0x22, 0x02, 0x22, 0x02, 
+0x22, 0x02, 0x67, 0x06};
+static unsigned char courier18_110_bits[] = {
+0x7b, 0x00, 0x86, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0xc7, 0x01};
+static unsigned char courier18_111_bits[] = {
+0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier18_112_bits[] = {
+0x7b, 0x00, 0x86, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 
+0x86, 0x00, 0x7a, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x0f, 0x00};
+static unsigned char courier18_113_bits[] = {
+0xbc, 0x01, 0xc2, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 
+0xc2, 0x00, 0xbc, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xe0, 0x01};
+static unsigned char courier18_114_bits[] = {
+0x77, 0x8c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x7f};
+static unsigned char courier18_115_bits[] = {
+0x5e, 0x61, 0x41, 0x0e, 0x30, 0x41, 0x43, 0x3d};
+static unsigned char courier18_116_bits[] = {
+0x04, 0x04, 0x04, 0x7f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x84, 0x78};
+static unsigned char courier18_117_bits[] = {
+0xc3, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 
+0xc2, 0x00, 0xbc, 0x01};
+static unsigned char courier18_118_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x28, 0x00, 
+0x10, 0x00, 0x10, 0x00};
+static unsigned char courier18_119_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x92, 0x00, 0x92, 0x00, 0xaa, 0x00, 0xaa, 0x00, 
+0x44, 0x00, 0x44, 0x00};
+static unsigned char courier18_120_bits[] = {
+0xe7, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0xe7};
+static unsigned char courier18_121_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x10, 0x00, 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0x1f, 0x00};
+static unsigned char courier18_122_bits[] = {
+0x7f, 0x41, 0x21, 0x10, 0x08, 0x44, 0x42, 0x7f};
+static unsigned char courier18_123_bits[] = {
+0x18, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0x18};
+static unsigned char courier18_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01};
+static unsigned char courier18_125_bits[] = {
+0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x18, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0x03};
+static unsigned char courier18_126_bits[] = {
+0x06, 0x99, 0x60};
+static unsigned char courier18_127_bits[] = {
+0x00};
+static unsigned char courier18_128_bits[] = {
+0x00};
+static unsigned char courier18_129_bits[] = {
+0x00};
+static unsigned char courier18_130_bits[] = {
+0x00};
+static unsigned char courier18_131_bits[] = {
+0x00};
+static unsigned char courier18_132_bits[] = {
+0x00};
+static unsigned char courier18_133_bits[] = {
+0x00};
+static unsigned char courier18_134_bits[] = {
+0x00};
+static unsigned char courier18_135_bits[] = {
+0x00};
+static unsigned char courier18_136_bits[] = {
+0x00};
+static unsigned char courier18_137_bits[] = {
+0x00};
+static unsigned char courier18_138_bits[] = {
+0x00};
+static unsigned char courier18_139_bits[] = {
+0x00};
+static unsigned char courier18_140_bits[] = {
+0x00};
+static unsigned char courier18_141_bits[] = {
+0x00};
+static unsigned char courier18_142_bits[] = {
+0x00};
+static unsigned char courier18_143_bits[] = {
+0x00};
+static unsigned char courier18_144_bits[] = {
+0x00};
+static unsigned char courier18_145_bits[] = {
+0x00};
+static unsigned char courier18_146_bits[] = {
+0x00};
+static unsigned char courier18_147_bits[] = {
+0x00};
+static unsigned char courier18_148_bits[] = {
+0x00};
+static unsigned char courier18_149_bits[] = {
+0x00};
+static unsigned char courier18_150_bits[] = {
+0x00};
+static unsigned char courier18_151_bits[] = {
+0x00};
+static unsigned char courier18_152_bits[] = {
+0x00};
+static unsigned char courier18_153_bits[] = {
+0x00};
+static unsigned char courier18_154_bits[] = {
+0x00};
+static unsigned char courier18_155_bits[] = {
+0x00};
+static unsigned char courier18_156_bits[] = {
+0x00};
+static unsigned char courier18_157_bits[] = {
+0x00};
+static unsigned char courier18_158_bits[] = {
+0x00};
+static unsigned char courier18_159_bits[] = {
+0x00};
+static unsigned char courier18_160_bits[] = {
+0x00};
+static unsigned char courier18_161_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char courier18_162_bits[] = {
+0x08, 0x08, 0x08, 0x3c, 0x22, 0x21, 0x01, 0x01, 0x22, 0x1c, 0x08, 0x08};
+static unsigned char courier18_163_bits[] = {
+0x38, 0x44, 0x04, 0x04, 0x08, 0x3f, 0x08, 0x04, 0x84, 0x82, 0x7e};
+static unsigned char courier18_164_bits[] = {
+0x21, 0x1e, 0x21, 0x21, 0x21, 0x1e, 0x21};
+static unsigned char courier18_165_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x28, 0x00, 
+0xfe, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x10, 0x00, 0x7c, 0x00};
+static unsigned char courier18_166_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01};
+static unsigned char courier18_167_bits[] = {
+0x7c, 0x42, 0x42, 0x02, 0x0e, 0x31, 0x41, 0x46, 0x38, 0x20, 0x21, 0x21, 
+0x1f};
+static unsigned char courier18_168_bits[] = {
+0x1b};
+static unsigned char courier18_169_bits[] = {
+0x78, 0x00, 0x86, 0x01, 0x72, 0x01, 0x49, 0x02, 0x05, 0x02, 0x05, 0x02, 
+0x05, 0x02, 0x49, 0x02, 0x32, 0x01, 0x86, 0x01, 0x78, 0x00};
+static unsigned char courier18_170_bits[] = {
+0x0e, 0x10, 0x1e, 0x11, 0x19, 0x37, 0x00, 0x3f};
+static unsigned char courier18_171_bits[] = {
+0x10, 0x01, 0xcc, 0x00, 0x66, 0x00, 0x33, 0x00, 0x33, 0x00, 0x66, 0x00, 
+0xcc, 0x00, 0x10, 0x01};
+static unsigned char courier18_172_bits[] = {
+0xff, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
+static unsigned char courier18_173_bits[] = {
+0xff};
+static unsigned char courier18_174_bits[] = {
+0xf8, 0x00, 0x06, 0x03, 0x7a, 0x02, 0x89, 0x04, 0x89, 0x04, 0x79, 0x04, 
+0x29, 0x04, 0x49, 0x04, 0x8a, 0x03, 0x06, 0x01, 0xf8, 0x00};
+static unsigned char courier18_175_bits[] = {
+0x1f};
+static unsigned char courier18_176_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char courier18_177_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xff, 0x01, 0x10, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x00, 0x00, 0xff, 0x01};
+static unsigned char courier18_178_bits[] = {
+0x0e, 0x11, 0x10, 0x08, 0x04, 0x02, 0x1f};
+static unsigned char courier18_179_bits[] = {
+0x0e, 0x11, 0x10, 0x0c, 0x10, 0x11, 0x0e};
+static unsigned char courier18_180_bits[] = {
+0x04, 0x02, 0x01};
+static unsigned char courier18_181_bits[] = {
+0xc3, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 
+0xc2, 0x00, 0xbe, 0x01, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00};
+static unsigned char courier18_182_bits[] = {
+0xfc, 0x52, 0x51, 0x51, 0x51, 0x52, 0x5c, 0x50, 0x50, 0x50, 0x50, 0x50, 
+0xdc};
+static unsigned char courier18_183_bits[] = {
+0x03, 0x03};
+static unsigned char courier18_184_bits[] = {
+0x02, 0x04, 0x03};
+static unsigned char courier18_185_bits[] = {
+0x04, 0x07, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char courier18_186_bits[] = {
+0x1e, 0x21, 0x21, 0x21, 0x21, 0x1e, 0x00, 0x3f};
+static unsigned char courier18_187_bits[] = {
+0x11, 0x00, 0x66, 0x00, 0xcc, 0x00, 0x98, 0x01, 0x98, 0x01, 0xcc, 0x00, 
+0x66, 0x00, 0x11, 0x00};
+static unsigned char courier18_188_bits[] = {
+0x04, 0x00, 0x07, 0x00, 0x04, 0x02, 0x04, 0x01, 0x84, 0x00, 0x84, 0x00, 
+0x5f, 0x02, 0x20, 0x03, 0x90, 0x02, 0x50, 0x02, 0xc8, 0x07, 0x04, 0x02, 
+0x00, 0x07};
+static unsigned char courier18_189_bits[] = {
+0x04, 0x00, 0x07, 0x00, 0x04, 0x02, 0x04, 0x01, 0x84, 0x00, 0x84, 0x00, 
+0x5f, 0x03, 0xa0, 0x04, 0x90, 0x04, 0x10, 0x02, 0x08, 0x01, 0x84, 0x00, 
+0xc0, 0x07};
+static unsigned char courier18_190_bits[] = {
+0x0e, 0x00, 0x11, 0x00, 0x10, 0x02, 0x0c, 0x01, 0x90, 0x00, 0x91, 0x00, 
+0x4e, 0x02, 0x20, 0x03, 0x90, 0x02, 0x50, 0x02, 0xc8, 0x07, 0x04, 0x02, 
+0x00, 0x07};
+static unsigned char courier18_191_bits[] = {
+0x18, 0x18, 0x00, 0x08, 0x08, 0x0e, 0x01, 0x01, 0x21, 0x21, 0x21, 0x1e};
+static unsigned char courier18_192_bits[] = {
+0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x20, 0x00, 
+0x50, 0x00, 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x04, 0x01, 0xfc, 0x01, 
+0x02, 0x02, 0x02, 0x02, 0x8f, 0x07};
+static unsigned char courier18_193_bits[] = {
+0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x20, 0x00, 
+0x50, 0x00, 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x04, 0x01, 0xfc, 0x01, 
+0x02, 0x02, 0x02, 0x02, 0x8f, 0x07};
+static unsigned char courier18_194_bits[] = {
+0x20, 0x00, 0x50, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x20, 0x00, 
+0x50, 0x00, 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x04, 0x01, 0xfc, 0x01, 
+0x02, 0x02, 0x02, 0x02, 0x8f, 0x07};
+static unsigned char courier18_195_bits[] = {
+0x98, 0x00, 0x64, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x20, 0x00, 0x50, 0x00, 
+0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x04, 0x01, 0xfc, 0x01, 0x02, 0x02, 
+0x02, 0x02, 0x8f, 0x07};
+static unsigned char courier18_196_bits[] = {
+0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x20, 0x00, 0x50, 0x00, 
+0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x04, 0x01, 0xfc, 0x01, 0x02, 0x02, 
+0x02, 0x02, 0x8f, 0x07};
+static unsigned char courier18_197_bits[] = {
+0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, 0x00, 0x3e, 0x00, 0x20, 0x00, 
+0x50, 0x00, 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x04, 0x01, 0xfc, 0x01, 
+0x02, 0x02, 0x02, 0x02, 0x8f, 0x07};
+static unsigned char courier18_198_bits[] = {
+0xf8, 0x07, 0x60, 0x04, 0x50, 0x04, 0x50, 0x00, 0x48, 0x02, 0xc8, 0x03, 
+0x7c, 0x02, 0x44, 0x00, 0x42, 0x04, 0x42, 0x04, 0xe7, 0x07};
+static unsigned char courier18_199_bits[] = {
+0x78, 0x01, 0x86, 0x01, 0x02, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x02, 0x01, 0x86, 0x00, 0x78, 0x00, 0x10, 0x00, 
+0x20, 0x00, 0x18, 0x00};
+static unsigned char courier18_200_bits[] = {
+0x04, 0x08, 0x10, 0x00, 0xff, 0x82, 0x82, 0x82, 0x12, 0x1e, 0x12, 0x82, 
+0x82, 0x82, 0xff};
+static unsigned char courier18_201_bits[] = {
+0x20, 0x10, 0x08, 0x00, 0xff, 0x82, 0x82, 0x82, 0x12, 0x1e, 0x12, 0x82, 
+0x82, 0x82, 0xff};
+static unsigned char courier18_202_bits[] = {
+0x18, 0x24, 0x42, 0x00, 0xff, 0x82, 0x82, 0x82, 0x12, 0x1e, 0x12, 0x82, 
+0x82, 0x82, 0xff};
+static unsigned char courier18_203_bits[] = {
+0x66, 0x00, 0x00, 0xff, 0x82, 0x82, 0x82, 0x12, 0x1e, 0x12, 0x82, 0x82, 
+0x82, 0xff};
+static unsigned char courier18_204_bits[] = {
+0x04, 0x08, 0x10, 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0x7f};
+static unsigned char courier18_205_bits[] = {
+0x10, 0x08, 0x04, 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0x7f};
+static unsigned char courier18_206_bits[] = {
+0x08, 0x14, 0x22, 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0x7f};
+static unsigned char courier18_207_bits[] = {
+0x36, 0x00, 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x7f};
+static unsigned char courier18_208_bits[] = {
+0x3f, 0x42, 0x82, 0x82, 0x82, 0x8f, 0x82, 0x82, 0x82, 0x42, 0x3f};
+static unsigned char courier18_209_bits[] = {
+0x98, 0x00, 0x64, 0x00, 0x00, 0x00, 0xe7, 0x01, 0x86, 0x00, 0x8a, 0x00, 
+0x8a, 0x00, 0x92, 0x00, 0x92, 0x00, 0xa2, 0x00, 0xa2, 0x00, 0xc2, 0x00, 
+0xc2, 0x00, 0x8f, 0x00};
+static unsigned char courier18_210_bits[] = {
+0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x38, 0x00, 0xc6, 0x00, 
+0x82, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x82, 0x00, 0xc6, 0x00, 0x38, 0x00};
+static unsigned char courier18_211_bits[] = {
+0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x38, 0x00, 0xc6, 0x00, 
+0x82, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x82, 0x00, 0xc6, 0x00, 0x38, 0x00};
+static unsigned char courier18_212_bits[] = {
+0x10, 0x00, 0x28, 0x00, 0x44, 0x00, 0x00, 0x00, 0x38, 0x00, 0xc6, 0x00, 
+0x82, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x82, 0x00, 0xc6, 0x00, 0x38, 0x00};
+static unsigned char courier18_213_bits[] = {
+0x98, 0x00, 0x64, 0x00, 0x00, 0x00, 0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 
+0xc6, 0x00, 0x38, 0x00};
+static unsigned char courier18_214_bits[] = {
+0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 
+0xc6, 0x00, 0x38, 0x00};
+static unsigned char courier18_215_bits[] = {
+0x01, 0x01, 0x82, 0x00, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x44, 0x00, 0x82, 0x00, 0x01, 0x01};
+static unsigned char courier18_216_bits[] = {
+0x70, 0x04, 0x8c, 0x02, 0x04, 0x01, 0x82, 0x02, 0x42, 0x02, 0x22, 0x02, 
+0x12, 0x02, 0x0a, 0x02, 0x04, 0x01, 0x8a, 0x01, 0x71, 0x00};
+static unsigned char courier18_217_bits[] = {
+0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0xcf, 0x03, 0x02, 0x01, 
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 
+0x02, 0x01, 0x84, 0x00, 0x78, 0x00};
+static unsigned char courier18_218_bits[] = {
+0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0xcf, 0x03, 0x02, 0x01, 
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 
+0x02, 0x01, 0x84, 0x00, 0x78, 0x00};
+static unsigned char courier18_219_bits[] = {
+0x30, 0x00, 0x48, 0x00, 0x84, 0x00, 0x00, 0x00, 0xcf, 0x03, 0x02, 0x01, 
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 
+0x02, 0x01, 0x84, 0x00, 0x78, 0x00};
+static unsigned char courier18_220_bits[] = {
+0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x03, 0x02, 0x01, 0x02, 0x01, 
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 
+0x84, 0x00, 0x78, 0x00};
+static unsigned char courier18_221_bits[] = {
+0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0xc7, 0x01, 0x82, 0x00, 
+0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x28, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x7c, 0x00};
+static unsigned char courier18_222_bits[] = {
+0x07, 0x00, 0x02, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x02, 0x01, 0x02, 0x01, 
+0x82, 0x00, 0x7e, 0x00, 0x02, 0x00, 0x02, 0x00, 0x07, 0x00};
+static unsigned char courier18_223_bits[] = {
+0x38, 0x44, 0x42, 0x42, 0x22, 0x32, 0x42, 0x82, 0x82, 0x82, 0x92, 0x67};
+static unsigned char courier18_224_bits[] = {
+0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x42, 0x00, 
+0x40, 0x00, 0x7e, 0x00, 0x41, 0x00, 0x41, 0x00, 0x61, 0x00, 0xde, 0x01};
+static unsigned char courier18_225_bits[] = {
+0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x42, 0x00, 
+0x40, 0x00, 0x7e, 0x00, 0x41, 0x00, 0x41, 0x00, 0x61, 0x00, 0xde, 0x01};
+static unsigned char courier18_226_bits[] = {
+0x18, 0x00, 0x24, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x42, 0x00, 
+0x40, 0x00, 0x7e, 0x00, 0x41, 0x00, 0x41, 0x00, 0x61, 0x00, 0xde, 0x01};
+static unsigned char courier18_227_bits[] = {
+0x4c, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x42, 0x00, 0x40, 0x00, 
+0x7e, 0x00, 0x41, 0x00, 0x41, 0x00, 0x61, 0x00, 0xde, 0x01};
+static unsigned char courier18_228_bits[] = {
+0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x42, 0x00, 0x40, 0x00, 
+0x7e, 0x00, 0x41, 0x00, 0x41, 0x00, 0x61, 0x00, 0xde, 0x01};
+static unsigned char courier18_229_bits[] = {
+0x18, 0x00, 0x24, 0x00, 0x24, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3c, 0x00, 
+0x42, 0x00, 0x40, 0x00, 0x7e, 0x00, 0x41, 0x00, 0x41, 0x00, 0x61, 0x00, 
+0xde, 0x01};
+static unsigned char courier18_230_bits[] = {
+0x8e, 0x01, 0x51, 0x02, 0x20, 0x04, 0xfe, 0x07, 0x21, 0x00, 0x21, 0x00, 
+0x51, 0x04, 0x8e, 0x03};
+static unsigned char courier18_231_bits[] = {
+0xbc, 0xc2, 0x81, 0x01, 0x01, 0x01, 0xc2, 0x3c, 0x08, 0x10, 0x0c};
+static unsigned char courier18_232_bits[] = {
+0x04, 0x08, 0x10, 0x00, 0x3c, 0x42, 0x81, 0xff, 0x01, 0x01, 0xc2, 0x3c};
+static unsigned char courier18_233_bits[] = {
+0x20, 0x10, 0x08, 0x00, 0x3c, 0x42, 0x81, 0xff, 0x01, 0x01, 0xc2, 0x3c};
+static unsigned char courier18_234_bits[] = {
+0x18, 0x24, 0x42, 0x00, 0x3c, 0x42, 0x81, 0xff, 0x01, 0x01, 0xc2, 0x3c};
+static unsigned char courier18_235_bits[] = {
+0x66, 0x00, 0x00, 0x3c, 0x42, 0x81, 0xff, 0x01, 0x01, 0xc2, 0x3c};
+static unsigned char courier18_236_bits[] = {
+0x02, 0x04, 0x08, 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f};
+static unsigned char courier18_237_bits[] = {
+0x10, 0x08, 0x04, 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f};
+static unsigned char courier18_238_bits[] = {
+0x08, 0x14, 0x22, 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f};
+static unsigned char courier18_239_bits[] = {
+0x36, 0x00, 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f};
+static unsigned char courier18_240_bits[] = {
+0xce, 0x31, 0x2c, 0x42, 0x40, 0xbc, 0xc2, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier18_241_bits[] = {
+0x98, 0x00, 0x64, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char courier18_242_bits[] = {
+0x04, 0x08, 0x10, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier18_243_bits[] = {
+0x40, 0x20, 0x10, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier18_244_bits[] = {
+0x18, 0x24, 0x42, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier18_245_bits[] = {
+0x4c, 0x32, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier18_246_bits[] = {
+0x66, 0x00, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char courier18_247_bits[] = {
+0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x18, 0x18};
+static unsigned char courier18_248_bits[] = {
+0xbc, 0x42, 0xa1, 0x91, 0x89, 0x85, 0x42, 0x3d};
+static unsigned char courier18_249_bits[] = {
+0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc2, 0x00, 0xbc, 0x01};
+static unsigned char courier18_250_bits[] = {
+0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc2, 0x00, 0xbc, 0x01};
+static unsigned char courier18_251_bits[] = {
+0x10, 0x00, 0x28, 0x00, 0x44, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc2, 0x00, 0xbc, 0x01};
+static unsigned char courier18_252_bits[] = {
+0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x82, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc2, 0x00, 0xbc, 0x01};
+static unsigned char courier18_253_bits[] = {
+0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0xc7, 0x01, 0x82, 0x00, 
+0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x28, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0x1f, 0x00};
+static unsigned char courier18_254_bits[] = {
+0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7a, 0x00, 0x86, 0x00, 
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x86, 0x00, 0x7a, 0x00, 
+0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x0f, 0x00};
+static unsigned char courier18_255_bits[] = {
+0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x01, 0x82, 0x00, 0x82, 0x00, 
+0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x28, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x08, 0x00, 0x08, 0x00, 0x1f, 0x00};
+static RotFont courier18font[] = {
+{5, 1, 1, courier18_0_bits},
+{5, 1, 1, courier18_1_bits},
+{5, 1, 1, courier18_2_bits},
+{5, 1, 1, courier18_3_bits},
+{5, 1, 1, courier18_4_bits},
+{5, 1, 1, courier18_5_bits},
+{5, 1, 1, courier18_6_bits},
+{5, 1, 1, courier18_7_bits},
+{5, 1, 1, courier18_8_bits},
+{5, 1, 1, courier18_9_bits},
+{5, 1, 1, courier18_10_bits},
+{5, 1, 1, courier18_11_bits},
+{5, 1, 1, courier18_12_bits},
+{5, 1, 1, courier18_13_bits},
+{5, 1, 1, courier18_14_bits},
+{5, 1, 1, courier18_15_bits},
+{5, 1, 1, courier18_16_bits},
+{5, 1, 1, courier18_17_bits},
+{5, 1, 1, courier18_18_bits},
+{5, 1, 1, courier18_19_bits},
+{5, 1, 1, courier18_20_bits},
+{5, 1, 1, courier18_21_bits},
+{5, 1, 1, courier18_22_bits},
+{5, 1, 1, courier18_23_bits},
+{5, 1, 1, courier18_24_bits},
+{5, 1, 1, courier18_25_bits},
+{5, 1, 1, courier18_26_bits},
+{5, 1, 1, courier18_27_bits},
+{5, 1, 1, courier18_28_bits},
+{5, 1, 1, courier18_29_bits},
+{5, 1, 1, courier18_30_bits},
+{5, 1, 1, courier18_31_bits},
+{7, 1, 1, courier18_32_bits},
+{2, 12, 12, courier18_33_bits},
+{5, 5, 12, courier18_34_bits},
+{9, 15, 13, courier18_35_bits},
+{7, 15, 13, courier18_36_bits},
+{9, 12, 12, courier18_37_bits},
+{7, 10, 10, courier18_38_bits},
+{4, 5, 12, courier18_39_bits},
+{3, 14, 12, courier18_40_bits},
+{3, 14, 12, courier18_41_bits},
+{7, 8, 12, courier18_42_bits},
+{9, 9, 10, courier18_43_bits},
+{4, 5, 2, courier18_44_bits},
+{9, 1, 6, courier18_45_bits},
+{2, 2, 2, courier18_46_bits},
+{8, 16, 13, courier18_47_bits},
+{7, 12, 12, courier18_48_bits},
+{7, 12, 12, courier18_49_bits},
+{8, 12, 12, courier18_50_bits},
+{7, 12, 12, courier18_51_bits},
+{7, 12, 12, courier18_52_bits},
+{8, 12, 12, courier18_53_bits},
+{7, 12, 12, courier18_54_bits},
+{8, 12, 12, courier18_55_bits},
+{7, 12, 12, courier18_56_bits},
+{7, 12, 12, courier18_57_bits},
+{2, 8, 8, courier18_58_bits},
+{5, 11, 8, courier18_59_bits},
+{10, 9, 10, courier18_60_bits},
+{9, 4, 7, courier18_61_bits},
+{10, 9, 10, courier18_62_bits},
+{7, 11, 11, courier18_63_bits},
+{8, 13, 12, courier18_64_bits},
+{11, 11, 11, courier18_65_bits},
+{9, 11, 11, courier18_66_bits},
+{9, 11, 11, courier18_67_bits},
+{8, 11, 11, courier18_68_bits},
+{8, 11, 11, courier18_69_bits},
+{8, 11, 11, courier18_70_bits},
+{10, 11, 11, courier18_71_bits},
+{9, 11, 11, courier18_72_bits},
+{7, 11, 11, courier18_73_bits},
+{9, 11, 11, courier18_74_bits},
+{10, 11, 11, courier18_75_bits},
+{9, 11, 11, courier18_76_bits},
+{11, 11, 11, courier18_77_bits},
+{9, 11, 11, courier18_78_bits},
+{9, 11, 11, courier18_79_bits},
+{9, 11, 11, courier18_80_bits},
+{9, 13, 11, courier18_81_bits},
+{10, 11, 11, courier18_82_bits},
+{8, 11, 11, courier18_83_bits},
+{9, 11, 11, courier18_84_bits},
+{10, 11, 11, courier18_85_bits},
+{11, 11, 11, courier18_86_bits},
+{11, 11, 11, courier18_87_bits},
+{9, 11, 11, courier18_88_bits},
+{9, 11, 11, courier18_89_bits},
+{7, 11, 11, courier18_90_bits},
+{3, 15, 12, courier18_91_bits},
+{8, 16, 13, courier18_92_bits},
+{3, 15, 12, courier18_93_bits},
+{7, 4, 12, courier18_94_bits},
+{11, 1, -3, courier18_95_bits},
+{4, 5, 12, courier18_96_bits},
+{9, 8, 8, courier18_97_bits},
+{9, 12, 12, courier18_98_bits},
+{8, 8, 8, courier18_99_bits},
+{9, 12, 12, courier18_100_bits},
+{8, 8, 8, courier18_101_bits},
+{8, 12, 12, courier18_102_bits},
+{9, 12, 8, courier18_103_bits},
+{9, 12, 12, courier18_104_bits},
+{7, 11, 11, courier18_105_bits},
+{6, 15, 11, courier18_106_bits},
+{9, 12, 12, courier18_107_bits},
+{7, 12, 12, courier18_108_bits},
+{11, 8, 8, courier18_109_bits},
+{9, 8, 8, courier18_110_bits},
+{8, 8, 8, courier18_111_bits},
+{9, 12, 8, courier18_112_bits},
+{9, 12, 8, courier18_113_bits},
+{8, 8, 8, courier18_114_bits},
+{7, 8, 8, courier18_115_bits},
+{8, 11, 11, courier18_116_bits},
+{9, 8, 8, courier18_117_bits},
+{9, 8, 8, courier18_118_bits},
+{9, 8, 8, courier18_119_bits},
+{8, 8, 8, courier18_120_bits},
+{9, 12, 8, courier18_121_bits},
+{7, 8, 8, courier18_122_bits},
+{5, 15, 12, courier18_123_bits},
+{1, 15, 12, courier18_124_bits},
+{5, 15, 12, courier18_125_bits},
+{8, 3, 7, courier18_126_bits},
+{5, 1, 1, courier18_127_bits},
+{5, 1, 1, courier18_128_bits},
+{5, 1, 1, courier18_129_bits},
+{5, 1, 1, courier18_130_bits},
+{5, 1, 1, courier18_131_bits},
+{5, 1, 1, courier18_132_bits},
+{5, 1, 1, courier18_133_bits},
+{5, 1, 1, courier18_134_bits},
+{5, 1, 1, courier18_135_bits},
+{5, 1, 1, courier18_136_bits},
+{5, 1, 1, courier18_137_bits},
+{5, 1, 1, courier18_138_bits},
+{5, 1, 1, courier18_139_bits},
+{5, 1, 1, courier18_140_bits},
+{5, 1, 1, courier18_141_bits},
+{5, 1, 1, courier18_142_bits},
+{5, 1, 1, courier18_143_bits},
+{5, 1, 1, courier18_144_bits},
+{5, 1, 1, courier18_145_bits},
+{5, 1, 1, courier18_146_bits},
+{5, 1, 1, courier18_147_bits},
+{5, 1, 1, courier18_148_bits},
+{5, 1, 1, courier18_149_bits},
+{5, 1, 1, courier18_150_bits},
+{5, 1, 1, courier18_151_bits},
+{5, 1, 1, courier18_152_bits},
+{5, 1, 1, courier18_153_bits},
+{5, 1, 1, courier18_154_bits},
+{5, 1, 1, courier18_155_bits},
+{5, 1, 1, courier18_156_bits},
+{5, 1, 1, courier18_157_bits},
+{5, 1, 1, courier18_158_bits},
+{5, 1, 1, courier18_159_bits},
+{1, 1, 1, courier18_160_bits},
+{2, 13, 9, courier18_161_bits},
+{6, 12, 12, courier18_162_bits},
+{8, 11, 11, courier18_163_bits},
+{6, 7, 9, courier18_164_bits},
+{9, 11, 11, courier18_165_bits},
+{1, 15, 12, courier18_166_bits},
+{7, 13, 12, courier18_167_bits},
+{5, 1, 11, courier18_168_bits},
+{10, 11, 11, courier18_169_bits},
+{6, 8, 11, courier18_170_bits},
+{9, 8, 8, courier18_171_bits},
+{9, 4, 7, courier18_172_bits},
+{8, 1, 6, courier18_173_bits},
+{11, 11, 11, courier18_174_bits},
+{5, 1, 10, courier18_175_bits},
+{5, 5, 12, courier18_176_bits},
+{9, 9, 10, courier18_177_bits},
+{5, 7, 12, courier18_178_bits},
+{5, 7, 12, courier18_179_bits},
+{3, 3, 12, courier18_180_bits},
+{9, 12, 8, courier18_181_bits},
+{8, 13, 12, courier18_182_bits},
+{2, 2, 7, courier18_183_bits},
+{3, 3, 0, courier18_184_bits},
+{5, 7, 12, courier18_185_bits},
+{6, 8, 11, courier18_186_bits},
+{9, 8, 8, courier18_187_bits},
+{11, 13, 13, courier18_188_bits},
+{11, 13, 13, courier18_189_bits},
+{11, 13, 13, courier18_190_bits},
+{6, 12, 9, courier18_191_bits},
+{11, 15, 15, courier18_192_bits},
+{11, 15, 15, courier18_193_bits},
+{11, 15, 15, courier18_194_bits},
+{11, 14, 14, courier18_195_bits},
+{11, 14, 14, courier18_196_bits},
+{11, 15, 15, courier18_197_bits},
+{11, 11, 11, courier18_198_bits},
+{9, 14, 11, courier18_199_bits},
+{8, 15, 15, courier18_200_bits},
+{8, 15, 15, courier18_201_bits},
+{8, 15, 15, courier18_202_bits},
+{8, 14, 14, courier18_203_bits},
+{7, 15, 15, courier18_204_bits},
+{7, 15, 15, courier18_205_bits},
+{7, 15, 15, courier18_206_bits},
+{7, 14, 14, courier18_207_bits},
+{8, 11, 11, courier18_208_bits},
+{9, 14, 14, courier18_209_bits},
+{9, 15, 15, courier18_210_bits},
+{9, 15, 15, courier18_211_bits},
+{9, 15, 15, courier18_212_bits},
+{9, 14, 14, courier18_213_bits},
+{9, 14, 14, courier18_214_bits},
+{9, 9, 10, courier18_215_bits},
+{11, 11, 11, courier18_216_bits},
+{10, 15, 15, courier18_217_bits},
+{10, 15, 15, courier18_218_bits},
+{10, 15, 15, courier18_219_bits},
+{10, 14, 14, courier18_220_bits},
+{9, 15, 15, courier18_221_bits},
+{9, 11, 11, courier18_222_bits},
+{8, 12, 12, courier18_223_bits},
+{9, 12, 12, courier18_224_bits},
+{9, 12, 12, courier18_225_bits},
+{9, 12, 12, courier18_226_bits},
+{9, 11, 11, courier18_227_bits},
+{9, 11, 11, courier18_228_bits},
+{9, 13, 13, courier18_229_bits},
+{11, 8, 8, courier18_230_bits},
+{8, 11, 8, courier18_231_bits},
+{8, 12, 12, courier18_232_bits},
+{8, 12, 12, courier18_233_bits},
+{8, 12, 12, courier18_234_bits},
+{8, 11, 11, courier18_235_bits},
+{7, 12, 12, courier18_236_bits},
+{7, 12, 12, courier18_237_bits},
+{7, 12, 12, courier18_238_bits},
+{7, 11, 11, courier18_239_bits},
+{8, 12, 12, courier18_240_bits},
+{9, 11, 11, courier18_241_bits},
+{8, 12, 12, courier18_242_bits},
+{8, 12, 12, courier18_243_bits},
+{8, 12, 12, courier18_244_bits},
+{8, 11, 11, courier18_245_bits},
+{8, 11, 11, courier18_246_bits},
+{8, 9, 10, courier18_247_bits},
+{8, 8, 8, courier18_248_bits},
+{9, 12, 12, courier18_249_bits},
+{9, 12, 12, courier18_250_bits},
+{9, 12, 12, courier18_251_bits},
+{9, 11, 11, courier18_252_bits},
+{9, 16, 12, courier18_253_bits},
+{9, 16, 12, courier18_254_bits},
+{9, 15, 11, courier18_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier24.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier24.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier24.bdf	(revision 22322)
@@ -0,0 +1,4150 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) IBM Corporation 1990,1991. IBM Courier is a Trademark of the IBM Corporation.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -adobe-courier-medium-r-normal--24-231-75-75-m-140-iso8859-1
+SIZE 23 75 75
+FONTBOUNDINGBOX 17 24 -1 -5
+STARTPROPERTIES 25
+FOUNDRY "adobe"
+FAMILY_NAME "courier"
+WEIGHT_NAME "medium"
+SLANT "r"
+SETWIDTH_NAME "normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 24
+POINT_SIZE 231
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "m"
+AVERAGE_WIDTH 140
+CHARSET_REGISTRY "iso8859"
+CHARSET_ENCODING "1"
+FONT "-adobe-courier-medium-r-normal--24-231-75-75-m-140-iso8859-1"
+COPYRIGHT "Copyright (c) IBM Corporation 1990,1991. IBM Courier is a Trademark of the IBM Corporation."
+RAW_PIXEL_SIZE 1000
+RAW_POINT_SIZE 964
+RAW_ASCENT 841
+RAW_DESCENT 288
+RAW_AVERAGE_WIDTH 6000
+FACE_NAME "Couriere."
+DEFAULT_CHAR 0
+FONT_ASCENT 20
+FONT_DESCENT 7
+ENDPROPERTIES
+CHARS 190
+STARTCHAR space
+ENCODING 32
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 0 0 0 0
+ATTRIBUTES 0x0258
+BITMAP
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 3 14 6 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+00
+00
+e0
+e0
+e0
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 6 7 4 7
+ATTRIBUTES 0x0258
+BITMAP
+cc
+cc
+cc
+cc
+cc
+cc
+cc
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 17 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+1180
+1100
+1100
+1100
+1100
+1100
+ffe0
+2100
+2100
+2100
+2100
+ffe0
+2100
+2100
+2100
+2100
+2100
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 19 2 -3
+ATTRIBUTES 0x0258
+BITMAP
+0400
+0400
+0400
+1f80
+2580
+4480
+4480
+64c0
+3c00
+0f00
+0580
+c440
+4440
+6440
+7480
+7f00
+0400
+0400
+0400
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+3800
+4400
+8200
+8200
+8200
+4460
+39e0
+0f80
+7c00
+71c0
+0220
+0410
+0410
+0630
+01e0
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 13 3 0
+ATTRIBUTES 0x0258
+BITMAP
+3d00
+6200
+4000
+4000
+2000
+3000
+51c0
+c900
+8d00
+8600
+8600
+c700
+79c0
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 6 4 8
+ATTRIBUTES 0x0258
+BITMAP
+30
+70
+60
+60
+c0
+c0
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 18 7 -2
+ATTRIBUTES 0x0258
+BITMAP
+10
+20
+20
+40
+40
+40
+80
+80
+80
+80
+80
+80
+c0
+40
+40
+20
+20
+10
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 18 3 -2
+ATTRIBUTES 0x0258
+BITMAP
+40
+40
+60
+20
+20
+10
+10
+10
+10
+10
+10
+10
+10
+20
+20
+60
+40
+c0
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 9 3 5
+ATTRIBUTES 0x0258
+BITMAP
+0800
+0800
+8880
+ff80
+3e00
+1c00
+3600
+6300
+2200
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 11 2 2
+ATTRIBUTES 0x0258
+BITMAP
+0400
+0400
+0400
+0400
+0400
+ffe0
+0400
+0400
+0400
+0400
+0400
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 6 4 -3
+ATTRIBUTES 0x0258
+BITMAP
+30
+60
+60
+60
+c0
+c0
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 1 2 7
+ATTRIBUTES 0x0258
+BITMAP
+ffe0
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 3 5 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+f0
+70
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 20 2 -3
+ATTRIBUTES 0x0258
+BITMAP
+0040
+0080
+0080
+0180
+0100
+0300
+0200
+0200
+0400
+0400
+0800
+0800
+1000
+1000
+3000
+2000
+6000
+4000
+c000
+8000
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 15 3 0
+ATTRIBUTES 0x0258
+BITMAP
+3c00
+6200
+4100
+8100
+8080
+8080
+8080
+8080
+8080
+8080
+8080
+8100
+4100
+6200
+3c00
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0c00
+3c00
+6400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+7fc0
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+1e00
+2300
+4180
+4080
+0080
+0080
+0180
+0100
+0200
+0400
+0800
+1080
+6080
+8080
+ff80
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+3c00
+6200
+4100
+0100
+0100
+0100
+0600
+0f00
+0100
+0080
+0080
+0080
+0080
+8300
+7e00
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0600
+0600
+0a00
+0a00
+1200
+1200
+2200
+4200
+4200
+8200
+ffc0
+0200
+0200
+0200
+1fc0
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+7f80
+4000
+4000
+4000
+4000
+4000
+5e00
+6180
+00c0
+0040
+0040
+0040
+0080
+c180
+7e00
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 15 3 0
+ATTRIBUTES 0x0258
+BITMAP
+0f80
+3800
+6000
+4000
+c000
+8000
+bc00
+e200
+c300
+8100
+8100
+8100
+4100
+6200
+3c00
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+ffc0
+8040
+8080
+8080
+0080
+0100
+0100
+0300
+0200
+0200
+0600
+0400
+0c00
+0c00
+0800
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 15 3 0
+ATTRIBUTES 0x0258
+BITMAP
+3c00
+6200
+c100
+8100
+8100
+8100
+6200
+3c00
+4300
+c100
+8080
+8080
+8080
+4300
+3e00
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+3c00
+6200
+4100
+8080
+8080
+8080
+c180
+6380
+3c80
+0080
+0100
+0100
+0200
+0400
+f800
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 9 5 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+f0
+70
+00
+00
+00
+60
+f0
+70
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 5 12 4 -3
+ATTRIBUTES 0x0258
+BITMAP
+30
+78
+38
+00
+00
+00
+30
+70
+60
+60
+40
+c0
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 11 1 2
+ATTRIBUTES 0x0258
+BITMAP
+0010
+0070
+0180
+0600
+3800
+e000
+3800
+0f00
+03e0
+0070
+0010
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 5 2 5
+ATTRIBUTES 0x0258
+BITMAP
+ffe0
+0000
+0000
+0000
+ffe0
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 11 1 2
+ATTRIBUTES 0x0258
+BITMAP
+8000
+e000
+3800
+0600
+01c0
+0070
+03c0
+1f00
+fc00
+f000
+c000
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 8 14 3 0
+ATTRIBUTES 0x0258
+BITMAP
+7c
+c2
+81
+81
+01
+01
+02
+1c
+10
+10
+00
+38
+38
+38
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 13 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0f80
+3060
+6010
+4698
+8d88
+8988
+9188
+9118
+9310
+dee0
+4400
+3070
+1fc0
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 14 0 0
+ATTRIBUTES 0x0258
+BITMAP
+3f00
+0300
+0780
+0680
+04c0
+0cc0
+0840
+1860
+1820
+1fe0
+3030
+2010
+6018
+f87c
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+ff80
+20c0
+2020
+2020
+2020
+2060
+3f80
+2060
+2030
+2010
+2010
+2010
+2060
+ffc0
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0f20
+30e0
+6060
+4060
+8020
+8000
+8000
+8000
+8000
+8000
+4000
+4030
+3060
+1f80
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+ff00
+20c0
+2060
+2020
+2010
+2010
+2010
+2010
+2010
+2010
+2030
+2020
+20c0
+ff80
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+fff0
+1010
+1010
+1010
+1010
+1080
+1f80
+1080
+1080
+1000
+1010
+1010
+1010
+fff0
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 14 2 0
+ATTRIBUTES 0x0258
+BITMAP
+ffe0
+2020
+2020
+2020
+2020
+2100
+3f00
+2100
+2100
+2000
+2000
+2000
+2000
+fc00
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0fa0
+30e0
+2060
+4020
+c020
+8000
+8000
+8000
+81f8
+8010
+4010
+6010
+3030
+0fc0
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f8f0
+2020
+2020
+2020
+2020
+2020
+2020
+3fe0
+2020
+2020
+2020
+2020
+2020
+f8f0
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 14 2 0
+ATTRIBUTES 0x0258
+BITMAP
+ffc0
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0ff8
+0040
+0040
+0040
+0040
+0040
+0040
+0040
+4040
+4040
+4040
+4040
+6080
+1f80
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+fcf8
+1020
+1040
+1080
+1100
+1200
+1400
+1e00
+1180
+1080
+1040
+1060
+1020
+fc38
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+fe00
+1000
+1000
+1000
+1000
+1000
+1000
+1000
+1010
+1010
+1010
+1010
+1010
+fff0
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 14 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f03c
+2828
+2828
+2848
+2448
+2448
+2488
+2288
+2288
+2308
+2008
+2008
+2008
+f87c
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f0f8
+3810
+2810
+2810
+2410
+2410
+2210
+2210
+2110
+2110
+2090
+2090
+2050
+f870
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0f80
+30c0
+6060
+4020
+c010
+8010
+8010
+8010
+8010
+8010
+4020
+6020
+30c0
+0f80
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 14 2 0
+ATTRIBUTES 0x0258
+BITMAP
+ff00
+21c0
+2040
+2020
+2020
+2020
+2060
+21c0
+3f80
+2000
+2000
+2000
+2000
+fc00
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 17 1 -3
+ATTRIBUTES 0x0258
+BITMAP
+0f00
+30c0
+6060
+4020
+c010
+8010
+8010
+8010
+8010
+8010
+4020
+6020
+30c0
+1f80
+0c00
+1f10
+10e0
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+ff80
+20c0
+2060
+2020
+2020
+2020
+20c0
+3f80
+2300
+2100
+2080
+2040
+2040
+f838
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 14 3 0
+ATTRIBUTES 0x0258
+BITMAP
+3c80
+4380
+8180
+8080
+8080
+8000
+6000
+3f00
+0180
+0080
+8080
+8080
+c100
+be00
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+7ff0
+4210
+4210
+4210
+4210
+c218
+0200
+0200
+0200
+0200
+0200
+0200
+0200
+1fc0
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f9f8
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+6020
+6020
+3040
+1f80
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 14 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f87c
+2010
+2010
+1010
+1020
+1020
+0820
+0840
+0c40
+04c0
+0480
+0280
+0380
+0300
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 15 14 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f83e
+6004
+6004
+6108
+2308
+2388
+2288
+2488
+24c8
+3458
+3850
+3830
+1830
+1030
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f8f0
+2040
+3040
+1880
+0900
+0500
+0600
+0600
+0d00
+0880
+10c0
+3060
+2020
+f8f8
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f8f8
+2020
+3060
+1040
+0880
+0c80
+0500
+0700
+0200
+0200
+0200
+0200
+0200
+1fc0
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 14 2 0
+ATTRIBUTES 0x0258
+BITMAP
+ff80
+8080
+8100
+8100
+8200
+0400
+0c00
+0800
+1000
+3040
+2040
+4040
+c040
+ffc0
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 18 8 -2
+ATTRIBUTES 0x0258
+BITMAP
+f0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+f0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 20 2 -3
+ATTRIBUTES 0x0258
+BITMAP
+8000
+c000
+4000
+6000
+2000
+3000
+1000
+1000
+0800
+0800
+0400
+0400
+0200
+0200
+0300
+0100
+0180
+0080
+00c0
+0040
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 18 3 -2
+ATTRIBUTES 0x0258
+BITMAP
+f0
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+f0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 11 2 3
+ATTRIBUTES 0x0258
+BITMAP
+0e00
+0a00
+1a00
+1900
+1100
+3180
+2080
+60c0
+60c0
+c040
+c060
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 17 1 -1 -3
+ATTRIBUTES 0x0258
+BITMAP
+ffff80
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 6 4 8
+ATTRIBUTES 0x0258
+BITMAP
+30
+30
+60
+60
+e0
+c0
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 10 2 0
+ATTRIBUTES 0x0258
+BITMAP
+3f00
+0180
+0080
+3f80
+4180
+8080
+8080
+8180
+c380
+7ce0
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 15 0 0
+ATTRIBUTES 0x0258
+BITMAP
+e000
+2000
+2000
+2000
+2000
+27c0
+2c30
+3010
+3008
+3008
+3008
+3008
+3810
+3c30
+e7c0
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 10 2 0
+ATTRIBUTES 0x0258
+BITMAP
+1fe0
+60c0
+4040
+8000
+8000
+8000
+8000
+4020
+60c0
+1f00
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+00e0
+0020
+0020
+0020
+0020
+1f20
+20e0
+4060
+8060
+8020
+8020
+8060
+4060
+31a0
+1f38
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 10 2 0
+ATTRIBUTES 0x0258
+BITMAP
+1f00
+60c0
+c060
+8020
+ffe0
+8000
+8000
+4000
+6060
+1f80
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0fc0
+1840
+1000
+1000
+1000
+ff80
+1000
+1000
+1000
+1000
+1000
+1000
+1000
+1000
+ff80
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 1 -4
+ATTRIBUTES 0x0258
+BITMAP
+1f70
+61c0
+40c0
+8040
+8040
+8040
+c040
+40c0
+31c0
+1e40
+0040
+0040
+0080
+1f00
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e000
+2000
+2000
+2000
+2000
+2f80
+3860
+2020
+2020
+2020
+2020
+2020
+2020
+2020
+f8f8
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 16 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0c00
+0c00
+0c00
+0000
+0000
+0000
+7c00
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 8 20 2 -4
+ATTRIBUTES 0x0258
+BITMAP
+03
+03
+03
+00
+00
+00
+fe
+02
+02
+02
+02
+02
+02
+02
+02
+02
+02
+02
+84
+f8
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e000
+2000
+2000
+2000
+2000
+21f0
+2180
+2300
+2c00
+3c00
+2200
+2180
+20c0
+2060
+e0f8
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+7c00
+4400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 15 10 0 0
+ATTRIBUTES 0x0258
+BITMAP
+eef0
+3188
+2108
+2108
+2108
+2108
+2108
+2108
+2108
+f9ce
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 10 1 0
+ATTRIBUTES 0x0258
+BITMAP
+ef80
+3040
+2020
+2020
+2020
+2020
+2020
+2020
+2020
+f9f8
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 10 1 0
+ATTRIBUTES 0x0258
+BITMAP
+1f80
+6060
+4020
+8010
+8010
+8010
+8010
+4020
+3060
+1f80
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 0 -4
+ATTRIBUTES 0x0258
+BITMAP
+f7c0
+1c30
+1810
+1008
+1008
+1008
+1008
+1810
+1c20
+17c0
+1000
+1000
+1000
+fe00
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 -4
+ATTRIBUTES 0x0258
+BITMAP
+1fb8
+60e0
+4060
+8020
+8020
+8020
+8020
+4060
+30a0
+1f20
+0020
+0020
+0020
+03f8
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 10 2 0
+ATTRIBUTES 0x0258
+BITMAP
+f3e0
+1620
+1800
+1000
+1000
+1000
+1000
+1000
+1000
+ff80
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 10 2 0
+ATTRIBUTES 0x0258
+BITMAP
+3e80
+4180
+4080
+6000
+3f00
+8380
+8040
+c040
+e0c0
+9f00
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 14 3 0
+ATTRIBUTES 0x0258
+BITMAP
+4000
+4000
+4000
+4000
+ff00
+4000
+4000
+4000
+4000
+4000
+4000
+4000
+20c0
+1f00
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 10 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e1c0
+2040
+2040
+2040
+2040
+2040
+2040
+2040
+31c0
+1f70
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 10 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f8f8
+2020
+3040
+1040
+1080
+0880
+0980
+0500
+0700
+0600
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 10 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f03c
+4008
+4108
+2310
+2390
+2290
+1490
+1460
+1460
+0860
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 10 0 0
+ATTRIBUTES 0x0258
+BITMAP
+7c78
+1820
+0c40
+0680
+0300
+0780
+0cc0
+1860
+3030
+f87c
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 -4
+ATTRIBUTES 0x0258
+BITMAP
+f078
+2020
+2020
+1040
+10c0
+0880
+0980
+0500
+0600
+0600
+0400
+0400
+0800
+fe00
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 10 2 0
+ATTRIBUTES 0x0258
+BITMAP
+7fc0
+4080
+4100
+0200
+0400
+0c00
+1800
+3040
+6040
+ffc0
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 5 18 6 -2
+ATTRIBUTES 0x0258
+BITMAP
+18
+20
+40
+40
+40
+60
+20
+20
+60
+c0
+60
+20
+20
+60
+40
+40
+60
+38
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 1 21 7 -5
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 6 18 3 -2
+ATTRIBUTES 0x0258
+BITMAP
+e0
+30
+10
+10
+10
+10
+10
+10
+18
+0c
+10
+10
+10
+10
+10
+10
+10
+e0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 4 2 5
+ATTRIBUTES 0x0258
+BITMAP
+2040
+7cc0
+8780
+8000
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 3 13 6 -3
+ATTRIBUTES 0x0258
+BITMAP
+e0
+e0
+e0
+00
+60
+60
+60
+60
+60
+60
+60
+60
+60
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 16 3 -1
+ATTRIBUTES 0x0258
+BITMAP
+0800
+0800
+0800
+0800
+3e80
+4980
+8880
+8800
+8800
+8800
+4880
+6b00
+1c00
+0800
+0800
+0800
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0f00
+18c0
+3040
+2040
+2000
+2000
+3000
+ffc0
+1000
+1000
+1000
+1000
+2000
+3c20
+63c0
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 11 2 1
+ATTRIBUTES 0x0258
+BITMAP
+8040
+5ec0
+3100
+6080
+4080
+4080
+4080
+2100
+5e80
+8040
+8040
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f8f8
+2060
+1040
+18c0
+0d80
+0700
+0200
+1fe0
+0200
+1fe0
+0200
+0200
+0200
+1fc0
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 1 21 7 -5
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+00
+00
+00
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 16 3 -2
+ATTRIBUTES 0x0258
+BITMAP
+3f00
+6100
+4100
+4100
+3000
+f800
+8c00
+8600
+4100
+3080
+1980
+0f00
+0600
+8200
+8200
+fc00
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 5 3 5 12
+ATTRIBUTES 0x0258
+BITMAP
+d8
+d8
+d8
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 14 0 0
+ATTRIBUTES 0x0258
+BITMAP
+07c0
+1820
+2010
+4788
+4c4c
+9044
+9004
+9004
+9804
+4c44
+4788
+2010
+1820
+07c0
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 6 8 4 6
+ATTRIBUTES 0x0258
+BITMAP
+78
+08
+78
+88
+98
+7c
+00
+fc
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 10 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0440
+1980
+3300
+6600
+cc00
+cc00
+6200
+1100
+0cc0
+0440
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 6 2 4
+ATTRIBUTES 0x0258
+BITMAP
+ffc0
+0040
+0040
+0040
+0040
+0040
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 5 1 5 6
+ATTRIBUTES 0x0258
+BITMAP
+f8
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 14 0 0
+ATTRIBUTES 0x0258
+BITMAP
+07c0
+1820
+2010
+4788
+c444
+84c4
+8704
+8484
+8484
+4f4c
+4008
+2010
+1820
+07c0
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 8 1 3 13
+ATTRIBUTES 0x0258
+BITMAP
+ff
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 7 7 4 8
+ATTRIBUTES 0x0258
+BITMAP
+38
+44
+82
+82
+82
+c4
+78
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 13 2 1
+ATTRIBUTES 0x0258
+BITMAP
+0400
+0400
+0400
+0400
+0400
+ffe0
+0400
+0400
+0400
+0400
+0000
+0000
+ffe0
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 6 9 4 6
+ATTRIBUTES 0x0258
+BITMAP
+78
+cc
+84
+04
+08
+10
+20
+44
+fc
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 6 9 4 6
+ATTRIBUTES 0x0258
+BITMAP
+70
+88
+08
+08
+38
+08
+04
+0c
+78
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 5 3 5 12
+ATTRIBUTES 0x0258
+BITMAP
+30
+60
+c0
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 1 -4
+ATTRIBUTES 0x0258
+BITMAP
+e1c0
+2040
+2040
+2040
+2040
+2040
+2040
+2040
+30c0
+2ff0
+2000
+2000
+2000
+2000
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 16 3 -2
+ATTRIBUTES 0x0258
+BITMAP
+3f80
+4900
+8900
+8900
+8900
+8900
+4900
+3900
+0900
+0900
+0900
+0900
+0900
+0900
+0900
+3b80
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 3 5 6
+ATTRIBUTES 0x0258
+BITMAP
+60
+f0
+70
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 4 4 5 -3
+ATTRIBUTES 0x0258
+BITMAP
+20
+30
+30
+f0
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 6 9 4 6
+ATTRIBUTES 0x0258
+BITMAP
+30
+f0
+10
+10
+10
+10
+10
+10
+fc
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 6 8 4 6
+ATTRIBUTES 0x0258
+BITMAP
+78
+c4
+84
+84
+cc
+38
+00
+fc
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 10 2 0
+ATTRIBUTES 0x0258
+BITMAP
+8800
+6600
+3300
+1980
+0cc0
+0cc0
+1980
+2200
+cc00
+4400
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 17 1 -2
+ATTRIBUTES 0x0258
+BITMAP
+6008
+a018
+2010
+2020
+2040
+2040
+2080
+2100
+f230
+0230
+0450
+0850
+1890
+1110
+21f8
+4010
+4078
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 17 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+3004
+5008
+1010
+1030
+1020
+1040
+10c0
+1080
+7938
+0244
+0604
+0404
+0808
+1018
+1030
+2044
+407c
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 17 1 -2
+ATTRIBUTES 0x0258
+BITMAP
+700c
+8808
+0810
+1820
+3060
+0840
+0880
+0900
+f130
+0230
+0450
+0850
+0890
+1190
+21f8
+4010
+4078
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 8 13 3 -3
+ATTRIBUTES 0x0258
+BITMAP
+1c
+1c
+1c
+00
+08
+18
+60
+40
+80
+80
+81
+41
+3e
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 18 0 0
+ATTRIBUTES 0x0258
+BITMAP
+0200
+0100
+0080
+0000
+3f00
+0300
+0780
+0680
+04c0
+0cc0
+0840
+1860
+1820
+1fe0
+3030
+2010
+6018
+f87c
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 18 0 0
+ATTRIBUTES 0x0258
+BITMAP
+0180
+0300
+0600
+0000
+3f00
+0300
+0780
+0680
+04c0
+0cc0
+0840
+1860
+1820
+1fe0
+3030
+2010
+6018
+f87c
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 18 0 0
+ATTRIBUTES 0x0258
+BITMAP
+0300
+0480
+0840
+0000
+3f00
+0300
+0780
+0680
+04c0
+0cc0
+0840
+1860
+1820
+1fe0
+3030
+2010
+6018
+f87c
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 18 0 0
+ATTRIBUTES 0x0258
+BITMAP
+0e20
+09c0
+0080
+0000
+3f00
+0300
+0780
+0680
+04c0
+0cc0
+0840
+1860
+1820
+1fe0
+3030
+2010
+6018
+f87c
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 18 0 0
+ATTRIBUTES 0x0258
+BITMAP
+06c0
+06c0
+06c0
+0000
+3f00
+0300
+0780
+0680
+04c0
+0cc0
+0840
+1860
+1820
+1fe0
+3030
+2010
+6018
+f87c
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 14 19 0 0
+ATTRIBUTES 0x0258
+BITMAP
+0300
+0480
+0480
+0300
+0000
+3f00
+0300
+0780
+0680
+04c0
+0cc0
+0840
+1860
+1820
+1fe0
+3030
+2010
+6018
+f87c
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 14 0 0
+ATTRIBUTES 0x0258
+BITMAP
+0ff8
+0308
+0308
+0508
+0528
+0d20
+09e0
+0920
+1920
+1f20
+3108
+3108
+6108
+f9f8
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 17 2 -3
+ATTRIBUTES 0x0258
+BITMAP
+1f20
+30e0
+4060
+4060
+8020
+8000
+8000
+8000
+8000
+8000
+c000
+4030
+20e0
+1f80
+0400
+0700
+0f00
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0400
+0200
+0100
+0000
+fff0
+1010
+1010
+1010
+1010
+1080
+1f80
+1080
+1080
+1000
+1010
+1010
+1010
+fff0
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0300
+0600
+0c00
+0000
+fff0
+1010
+1010
+1010
+1010
+1080
+1f80
+1080
+1080
+1000
+1010
+1010
+1010
+fff0
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0600
+0900
+1080
+0000
+fff0
+1010
+1010
+1010
+1010
+1080
+1f80
+1080
+1080
+1000
+1010
+1010
+1010
+fff0
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0d80
+0d80
+0d80
+0000
+fff0
+1010
+1010
+1010
+1010
+1080
+1f80
+1080
+1080
+1000
+1010
+1010
+1010
+fff0
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 18 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0800
+0400
+0200
+0000
+ffc0
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 18 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0600
+0c00
+1800
+0000
+ffc0
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 18 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0c00
+1200
+2100
+0000
+ffc0
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 18 2 0
+ATTRIBUTES 0x0258
+BITMAP
+1b00
+1b00
+1b00
+0000
+ffc0
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+ff00
+20c0
+2060
+2020
+2010
+2010
+fe10
+2010
+2010
+2010
+2020
+2060
+20c0
+ff80
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+1c40
+1380
+0100
+0000
+f0f8
+3810
+2810
+2810
+2410
+2410
+2210
+2210
+2110
+2110
+2090
+2090
+2050
+f870
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0400
+0200
+0100
+0000
+0f80
+30c0
+6060
+4020
+c010
+8010
+8010
+8010
+8010
+8010
+4020
+6020
+30c0
+0f80
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0300
+0600
+0c00
+0000
+0f80
+30c0
+6060
+4020
+c010
+8010
+8010
+8010
+8010
+8010
+4020
+6020
+30c0
+0f80
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0600
+0900
+1080
+0000
+0f80
+30c0
+6060
+4020
+c010
+8010
+8010
+8010
+8010
+8010
+4020
+6020
+30c0
+0f80
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+1c40
+1380
+0100
+0000
+0f80
+30c0
+6060
+4020
+c010
+8010
+8010
+8010
+8010
+8010
+4020
+6020
+30c0
+0f80
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0d80
+0d80
+0d80
+0000
+0f80
+30c0
+6060
+4020
+c010
+8010
+8010
+8010
+8010
+8010
+4020
+6020
+30c0
+0f80
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 9 9 3 3
+ATTRIBUTES 0x0258
+BITMAP
+4080
+c180
+6380
+3600
+1c00
+1c00
+3200
+4300
+8180
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 14 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0fa0
+30e0
+6060
+4070
+c090
+8110
+8310
+8210
+8410
+c810
+5820
+7020
+70c0
+5f80
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0400
+0200
+0100
+0000
+f9f8
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+6020
+6020
+3040
+1f80
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0300
+0600
+0c00
+0000
+f9f8
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+6020
+6020
+3040
+1f80
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0600
+0900
+1080
+0000
+f9f8
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+6020
+6020
+3040
+1f80
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0d80
+0d80
+0d80
+0000
+f9f8
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+4020
+6020
+6020
+3040
+1f80
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 18 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0300
+0600
+0c00
+0000
+f8f8
+2020
+3060
+1040
+0880
+0c80
+0500
+0700
+0200
+0200
+0200
+0200
+0200
+1fc0
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 14 2 0
+ATTRIBUTES 0x0258
+BITMAP
+fc00
+2000
+2000
+3f80
+20c0
+2060
+2020
+2020
+2060
+20c0
+3f80
+2000
+2000
+fc00
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+1e00
+3100
+6080
+4080
+4080
+4300
+4e00
+4180
+4080
+4040
+4040
+4040
+4040
+4880
+cf00
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0800
+0400
+0200
+0000
+0000
+3f00
+0180
+0080
+3f80
+4180
+8080
+8080
+8180
+c380
+7ce0
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0600
+0c00
+1800
+0000
+0000
+3f00
+0180
+0080
+3f80
+4180
+8080
+8080
+8180
+c380
+7ce0
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0c00
+1600
+2100
+0000
+0000
+3f00
+0180
+0080
+3f80
+4180
+8080
+8080
+8180
+c380
+7ce0
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+3880
+2700
+0200
+0000
+0000
+3f00
+0180
+0080
+3f80
+4180
+8080
+8080
+8180
+c380
+7ce0
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+1b00
+1b00
+1b00
+0000
+0000
+3f00
+0180
+0080
+3f80
+4180
+8080
+8080
+8180
+c380
+7ce0
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0e00
+1200
+0e00
+0000
+0000
+3f00
+0180
+0080
+3f80
+4180
+8080
+8080
+8180
+c380
+7ce0
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 10 1 0
+ATTRIBUTES 0x0258
+BITMAP
+7fe0
+0230
+0210
+0210
+3ff0
+4200
+8200
+8200
+8700
+78f0
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 13 2 -3
+ATTRIBUTES 0x0258
+BITMAP
+1f60
+60c0
+4040
+8000
+8000
+8000
+8000
+4020
+60c0
+1f00
+0400
+0600
+0e00
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0800
+0400
+0200
+0000
+0000
+1f00
+60c0
+c060
+8020
+ffe0
+8000
+8000
+4000
+6060
+1f80
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0600
+0c00
+1800
+0000
+0000
+1f00
+60c0
+c060
+8020
+ffe0
+8000
+8000
+4000
+6060
+1f80
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0c00
+1600
+2100
+0000
+0000
+1f00
+60c0
+c060
+8020
+ffe0
+8000
+8000
+4000
+6060
+1f80
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+1b00
+1b00
+1b00
+0000
+0000
+1f00
+60c0
+c060
+8020
+ffe0
+8000
+8000
+4000
+6060
+1f80
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0800
+0400
+0200
+0000
+0000
+7c00
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0600
+0c00
+1800
+0000
+0000
+7c00
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+0c00
+1600
+2100
+0000
+0000
+7c00
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 10 15 2 0
+ATTRIBUTES 0x0258
+BITMAP
+1b00
+1b00
+1b00
+0000
+0000
+7c00
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+0400
+ffc0
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 17 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0200
+3c00
+0e00
+0b00
+1980
+00c0
+0060
+1fe0
+6070
+4030
+8010
+8010
+8010
+8010
+4020
+3060
+1f80
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+1c40
+1380
+0100
+0000
+0000
+ef80
+3040
+2020
+2020
+2020
+2020
+2020
+2020
+2020
+f9f8
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0400
+0200
+0100
+0000
+0000
+1f80
+6060
+4020
+8010
+8010
+8010
+8010
+4020
+3060
+1f80
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0300
+0600
+0c00
+0000
+0000
+1f80
+6060
+4020
+8010
+8010
+8010
+8010
+4020
+3060
+1f80
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0600
+0b00
+1080
+0000
+0000
+1f80
+6060
+4020
+8010
+8010
+8010
+8010
+4020
+3060
+1f80
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+1c40
+1380
+0100
+0000
+0000
+1f80
+6060
+4020
+8010
+8010
+8010
+8010
+4020
+3060
+1f80
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0d80
+0d80
+0d80
+0000
+0000
+1f80
+6060
+4020
+8010
+8010
+8010
+8010
+4020
+3060
+1f80
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 11 11 2 2
+ATTRIBUTES 0x0258
+BITMAP
+0c00
+0c00
+0000
+0000
+0000
+ffe0
+0000
+0000
+0000
+0c00
+0c00
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 10 1 0
+ATTRIBUTES 0x0258
+BITMAP
+1fa0
+6060
+40b0
+8110
+8210
+8c10
+d810
+7020
+7060
+df80
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0400
+0200
+0100
+0000
+0000
+e1c0
+2040
+2040
+2040
+2040
+2040
+2040
+2040
+31c0
+1f70
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0300
+0600
+0c00
+0000
+0000
+e1c0
+2040
+2040
+2040
+2040
+2040
+2040
+2040
+31c0
+1f70
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0600
+0b00
+1080
+0000
+0000
+e1c0
+2040
+2040
+2040
+2040
+2040
+2040
+2040
+31c0
+1f70
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 12 15 1 0
+ATTRIBUTES 0x0258
+BITMAP
+0d80
+0d80
+0d80
+0000
+0000
+e1c0
+2040
+2040
+2040
+2040
+2040
+2040
+2040
+31c0
+1f70
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 19 1 -4
+ATTRIBUTES 0x0258
+BITMAP
+0300
+0600
+0c00
+0000
+0000
+f078
+2020
+2020
+1040
+10c0
+0880
+0980
+0500
+0600
+0600
+0400
+0400
+0800
+fe00
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 19 0 -4
+ATTRIBUTES 0x0258
+BITMAP
+f000
+1000
+1000
+1000
+1000
+17c0
+1c30
+1810
+1008
+1008
+1008
+1008
+1810
+1c20
+17c0
+1000
+1000
+1000
+fe00
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 584 0
+DWIDTH 14 0
+BBX 13 19 1 -4
+ATTRIBUTES 0x0258
+BITMAP
+0d80
+0d80
+0d80
+0000
+0000
+f078
+2020
+2020
+1040
+10c0
+0880
+0980
+0500
+0600
+0600
+0400
+0400
+0800
+fe00
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier24.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier24.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier24.h	(revision 22322)
@@ -0,0 +1,1068 @@
+static unsigned char courier24_0_bits[] = {
+0x00};
+static unsigned char courier24_1_bits[] = {
+0x00};
+static unsigned char courier24_2_bits[] = {
+0x00};
+static unsigned char courier24_3_bits[] = {
+0x00};
+static unsigned char courier24_4_bits[] = {
+0x00};
+static unsigned char courier24_5_bits[] = {
+0x00};
+static unsigned char courier24_6_bits[] = {
+0x00};
+static unsigned char courier24_7_bits[] = {
+0x00};
+static unsigned char courier24_8_bits[] = {
+0x00};
+static unsigned char courier24_9_bits[] = {
+0x00};
+static unsigned char courier24_10_bits[] = {
+0x00};
+static unsigned char courier24_11_bits[] = {
+0x00};
+static unsigned char courier24_12_bits[] = {
+0x00};
+static unsigned char courier24_13_bits[] = {
+0x00};
+static unsigned char courier24_14_bits[] = {
+0x00};
+static unsigned char courier24_15_bits[] = {
+0x00};
+static unsigned char courier24_16_bits[] = {
+0x00};
+static unsigned char courier24_17_bits[] = {
+0x00};
+static unsigned char courier24_18_bits[] = {
+0x00};
+static unsigned char courier24_19_bits[] = {
+0x00};
+static unsigned char courier24_20_bits[] = {
+0x00};
+static unsigned char courier24_21_bits[] = {
+0x00};
+static unsigned char courier24_22_bits[] = {
+0x00};
+static unsigned char courier24_23_bits[] = {
+0x00};
+static unsigned char courier24_24_bits[] = {
+0x00};
+static unsigned char courier24_25_bits[] = {
+0x00};
+static unsigned char courier24_26_bits[] = {
+0x00};
+static unsigned char courier24_27_bits[] = {
+0x00};
+static unsigned char courier24_28_bits[] = {
+0x00};
+static unsigned char courier24_29_bits[] = {
+0x00};
+static unsigned char courier24_30_bits[] = {
+0x00};
+static unsigned char courier24_31_bits[] = {
+0x00};
+static unsigned char courier24_32_bits[] = {
+0x00, 0x00};
+static unsigned char courier24_33_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x07, 
+0x07, 0x07};
+static unsigned char courier24_34_bits[] = {
+0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33};
+static unsigned char courier24_35_bits[] = {
+0x88, 0x01, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 
+0xff, 0x07, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0xff, 0x07, 
+0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00};
+static unsigned char courier24_36_bits[] = {
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xf8, 0x01, 0xa4, 0x01, 0x22, 0x01, 
+0x22, 0x01, 0x26, 0x03, 0x3c, 0x00, 0xf0, 0x00, 0xa0, 0x01, 0x23, 0x02, 
+0x22, 0x02, 0x26, 0x02, 0x2e, 0x01, 0xfe, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00};
+static unsigned char courier24_37_bits[] = {
+0x1c, 0x00, 0x22, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x22, 0x06, 
+0x9c, 0x07, 0xf0, 0x01, 0x3e, 0x00, 0x8e, 0x03, 0x40, 0x04, 0x20, 0x08, 
+0x20, 0x08, 0x60, 0x0c, 0x80, 0x07};
+static unsigned char courier24_38_bits[] = {
+0xbc, 0x00, 0x46, 0x00, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x0c, 0x00, 
+0x8a, 0x03, 0x93, 0x00, 0xb1, 0x00, 0x61, 0x00, 0x61, 0x00, 0xe3, 0x00, 
+0x9e, 0x03};
+static unsigned char courier24_39_bits[] = {
+0x0c, 0x0e, 0x06, 0x06, 0x03, 0x03};
+static unsigned char courier24_40_bits[] = {
+0x08, 0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x03, 0x02, 0x02, 0x04, 0x04, 0x08};
+static unsigned char courier24_41_bits[] = {
+0x02, 0x02, 0x06, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x04, 0x04, 0x06, 0x02, 0x03};
+static unsigned char courier24_42_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x11, 0x01, 0xff, 0x01, 0x7c, 0x00, 0x38, 0x00, 
+0x6c, 0x00, 0xc6, 0x00, 0x44, 0x00};
+static unsigned char courier24_43_bits[] = {
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xff, 0x07, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00};
+static unsigned char courier24_44_bits[] = {
+0x0c, 0x06, 0x06, 0x06, 0x03, 0x03};
+static unsigned char courier24_45_bits[] = {
+0xff, 0x07};
+static unsigned char courier24_46_bits[] = {
+0x06, 0x0f, 0x0e};
+static unsigned char courier24_47_bits[] = {
+0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x80, 0x01, 0x80, 0x00, 0xc0, 0x00, 
+0x40, 0x00, 0x40, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x08, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x06, 0x00, 0x02, 0x00, 
+0x03, 0x00, 0x01, 0x00};
+static unsigned char courier24_48_bits[] = {
+0x3c, 0x00, 0x46, 0x00, 0x82, 0x00, 0x81, 0x00, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x81, 0x00, 
+0x82, 0x00, 0x46, 0x00, 0x3c, 0x00};
+static unsigned char courier24_49_bits[] = {
+0x30, 0x00, 0x3c, 0x00, 0x26, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0xfe, 0x03};
+static unsigned char courier24_50_bits[] = {
+0x78, 0x00, 0xc4, 0x00, 0x82, 0x01, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 
+0x80, 0x01, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x01, 
+0x06, 0x01, 0x01, 0x01, 0xff, 0x01};
+static unsigned char courier24_51_bits[] = {
+0x3c, 0x00, 0x46, 0x00, 0x82, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 
+0x60, 0x00, 0xf0, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 
+0x00, 0x01, 0xc1, 0x00, 0x7e, 0x00};
+static unsigned char courier24_52_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x50, 0x00, 0x50, 0x00, 0x48, 0x00, 0x48, 0x00, 
+0x44, 0x00, 0x42, 0x00, 0x42, 0x00, 0x41, 0x00, 0xff, 0x03, 0x40, 0x00, 
+0x40, 0x00, 0x40, 0x00, 0xf8, 0x03};
+static unsigned char courier24_53_bits[] = {
+0xfe, 0x01, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 
+0x7a, 0x00, 0x86, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 
+0x00, 0x01, 0x83, 0x01, 0x7e, 0x00};
+static unsigned char courier24_54_bits[] = {
+0xf0, 0x01, 0x1c, 0x00, 0x06, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 
+0x3d, 0x00, 0x47, 0x00, 0xc3, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 
+0x82, 0x00, 0x46, 0x00, 0x3c, 0x00};
+static unsigned char courier24_55_bits[] = {
+0xff, 0x03, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x80, 0x00, 
+0x80, 0x00, 0xc0, 0x00, 0x40, 0x00, 0x40, 0x00, 0x60, 0x00, 0x20, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x10, 0x00};
+static unsigned char courier24_56_bits[] = {
+0x3c, 0x00, 0x46, 0x00, 0x83, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 
+0x46, 0x00, 0x3c, 0x00, 0xc2, 0x00, 0x83, 0x00, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0xc2, 0x00, 0x7c, 0x00};
+static unsigned char courier24_57_bits[] = {
+0x3c, 0x00, 0x46, 0x00, 0x82, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x83, 0x01, 0xc6, 0x01, 0x3c, 0x01, 0x00, 0x01, 0x80, 0x00, 0x80, 0x00, 
+0x40, 0x00, 0x20, 0x00, 0x1f, 0x00};
+static unsigned char courier24_58_bits[] = {
+0x06, 0x0f, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x0f, 0x0e};
+static unsigned char courier24_59_bits[] = {
+0x0c, 0x1e, 0x1c, 0x00, 0x00, 0x00, 0x0c, 0x0e, 0x06, 0x06, 0x02, 0x03};
+static unsigned char courier24_60_bits[] = {
+0x00, 0x08, 0x00, 0x0e, 0x80, 0x01, 0x60, 0x00, 0x1c, 0x00, 0x07, 0x00, 
+0x1c, 0x00, 0xf0, 0x00, 0xc0, 0x07, 0x00, 0x0e, 0x00, 0x08};
+static unsigned char courier24_61_bits[] = {
+0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07};
+static unsigned char courier24_62_bits[] = {
+0x01, 0x00, 0x07, 0x00, 0x1c, 0x00, 0x60, 0x00, 0x80, 0x03, 0x00, 0x0e, 
+0xc0, 0x03, 0xf8, 0x00, 0x3f, 0x00, 0x0f, 0x00, 0x03, 0x00};
+static unsigned char courier24_63_bits[] = {
+0x3e, 0x43, 0x81, 0x81, 0x80, 0x80, 0x40, 0x38, 0x08, 0x08, 0x00, 0x1c, 
+0x1c, 0x1c};
+static unsigned char courier24_64_bits[] = {
+0xf0, 0x01, 0x0c, 0x06, 0x06, 0x08, 0x62, 0x19, 0xb1, 0x11, 0x91, 0x11, 
+0x89, 0x11, 0x89, 0x18, 0xc9, 0x08, 0x7b, 0x07, 0x22, 0x00, 0x0c, 0x0e, 
+0xf8, 0x03};
+static unsigned char courier24_65_bits[] = {
+0xfc, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0x60, 0x01, 0x20, 0x03, 0x30, 0x03, 
+0x10, 0x02, 0x18, 0x06, 0x18, 0x04, 0xf8, 0x07, 0x0c, 0x0c, 0x04, 0x08, 
+0x06, 0x18, 0x1f, 0x3e};
+static unsigned char courier24_66_bits[] = {
+0xff, 0x01, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 
+0xfc, 0x01, 0x04, 0x06, 0x04, 0x0c, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 
+0x04, 0x06, 0xff, 0x03};
+static unsigned char courier24_67_bits[] = {
+0xf0, 0x04, 0x0c, 0x07, 0x06, 0x06, 0x02, 0x06, 0x01, 0x04, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x0c, 
+0x0c, 0x06, 0xf8, 0x01};
+static unsigned char courier24_68_bits[] = {
+0xff, 0x00, 0x04, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x08, 0x04, 0x08, 
+0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x0c, 0x04, 0x04, 
+0x04, 0x03, 0xff, 0x01};
+static unsigned char courier24_69_bits[] = {
+0xff, 0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x01, 
+0xf8, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0xff, 0x0f};
+static unsigned char courier24_70_bits[] = {
+0xff, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x84, 0x00, 
+0xfc, 0x00, 0x84, 0x00, 0x84, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 
+0x04, 0x00, 0x3f, 0x00};
+static unsigned char courier24_71_bits[] = {
+0xf0, 0x05, 0x0c, 0x07, 0x04, 0x06, 0x02, 0x04, 0x03, 0x04, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x81, 0x1f, 0x01, 0x08, 0x02, 0x08, 0x06, 0x08, 
+0x0c, 0x0c, 0xf0, 0x03};
+static unsigned char courier24_72_bits[] = {
+0x1f, 0x0f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0xfc, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0x1f, 0x0f};
+static unsigned char courier24_73_bits[] = {
+0xff, 0x03, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_74_bits[] = {
+0xf0, 0x1f, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 
+0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
+0x06, 0x01, 0xf8, 0x01};
+static unsigned char courier24_75_bits[] = {
+0x3f, 0x1f, 0x08, 0x04, 0x08, 0x02, 0x08, 0x01, 0x88, 0x00, 0x48, 0x00, 
+0x28, 0x00, 0x78, 0x00, 0x88, 0x01, 0x08, 0x01, 0x08, 0x02, 0x08, 0x06, 
+0x08, 0x04, 0x3f, 0x1c};
+static unsigned char courier24_76_bits[] = {
+0x7f, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 
+0x08, 0x00, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0xff, 0x0f};
+static unsigned char courier24_77_bits[] = {
+0x0f, 0x3c, 0x14, 0x14, 0x14, 0x14, 0x14, 0x12, 0x24, 0x12, 0x24, 0x12, 
+0x24, 0x11, 0x44, 0x11, 0x44, 0x11, 0xc4, 0x10, 0x04, 0x10, 0x04, 0x10, 
+0x04, 0x10, 0x1f, 0x3e};
+static unsigned char courier24_78_bits[] = {
+0x0f, 0x1f, 0x1c, 0x08, 0x14, 0x08, 0x14, 0x08, 0x24, 0x08, 0x24, 0x08, 
+0x44, 0x08, 0x44, 0x08, 0x84, 0x08, 0x84, 0x08, 0x04, 0x09, 0x04, 0x09, 
+0x04, 0x0a, 0x1f, 0x0e};
+static unsigned char courier24_79_bits[] = {
+0xf0, 0x01, 0x0c, 0x03, 0x06, 0x06, 0x02, 0x04, 0x03, 0x08, 0x01, 0x08, 
+0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 0x02, 0x04, 0x06, 0x04, 
+0x0c, 0x03, 0xf0, 0x01};
+static unsigned char courier24_80_bits[] = {
+0xff, 0x00, 0x84, 0x03, 0x04, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x06, 0x84, 0x03, 0xfc, 0x01, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 
+0x04, 0x00, 0x3f, 0x00};
+static unsigned char courier24_81_bits[] = {
+0xf0, 0x00, 0x0c, 0x03, 0x06, 0x06, 0x02, 0x04, 0x03, 0x08, 0x01, 0x08, 
+0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 0x02, 0x04, 0x06, 0x04, 
+0x0c, 0x03, 0xf8, 0x01, 0x30, 0x00, 0xf8, 0x08, 0x08, 0x07};
+static unsigned char courier24_82_bits[] = {
+0xff, 0x01, 0x04, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x03, 0xfc, 0x01, 0xc4, 0x00, 0x84, 0x00, 0x04, 0x01, 0x04, 0x02, 
+0x04, 0x02, 0x1f, 0x1c};
+static unsigned char courier24_83_bits[] = {
+0x3c, 0x01, 0xc2, 0x01, 0x81, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 
+0x06, 0x00, 0xfc, 0x00, 0x80, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x83, 0x00, 0x7d, 0x00};
+static unsigned char courier24_84_bits[] = {
+0xfe, 0x0f, 0x42, 0x08, 0x42, 0x08, 0x42, 0x08, 0x42, 0x08, 0x43, 0x18, 
+0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 
+0x40, 0x00, 0xf8, 0x03};
+static unsigned char courier24_85_bits[] = {
+0x9f, 0x1f, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 
+0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x06, 0x04, 0x06, 0x04, 
+0x0c, 0x02, 0xf8, 0x01};
+static unsigned char courier24_86_bits[] = {
+0x1f, 0x3e, 0x04, 0x08, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x08, 0x04, 
+0x10, 0x04, 0x10, 0x02, 0x30, 0x02, 0x20, 0x03, 0x20, 0x01, 0x40, 0x01, 
+0xc0, 0x01, 0xc0, 0x00};
+static unsigned char courier24_87_bits[] = {
+0x1f, 0x7c, 0x06, 0x20, 0x06, 0x20, 0x86, 0x10, 0xc4, 0x10, 0xc4, 0x11, 
+0x44, 0x11, 0x24, 0x11, 0x24, 0x13, 0x2c, 0x1a, 0x1c, 0x0a, 0x1c, 0x0c, 
+0x18, 0x0c, 0x08, 0x0c};
+static unsigned char courier24_88_bits[] = {
+0x1f, 0x0f, 0x04, 0x02, 0x0c, 0x02, 0x18, 0x01, 0x90, 0x00, 0xa0, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0xb0, 0x00, 0x10, 0x01, 0x08, 0x03, 0x0c, 0x06, 
+0x04, 0x04, 0x1f, 0x1f};
+static unsigned char courier24_89_bits[] = {
+0x1f, 0x1f, 0x04, 0x04, 0x0c, 0x06, 0x08, 0x02, 0x10, 0x01, 0x30, 0x01, 
+0xa0, 0x00, 0xe0, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 
+0x40, 0x00, 0xf8, 0x03};
+static unsigned char courier24_90_bits[] = {
+0xff, 0x01, 0x01, 0x01, 0x81, 0x00, 0x81, 0x00, 0x41, 0x00, 0x20, 0x00, 
+0x30, 0x00, 0x10, 0x00, 0x08, 0x00, 0x0c, 0x02, 0x04, 0x02, 0x02, 0x02, 
+0x03, 0x02, 0xff, 0x03};
+static unsigned char courier24_91_bits[] = {
+0x0f, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x0f};
+static unsigned char courier24_92_bits[] = {
+0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0x06, 0x00, 0x04, 0x00, 0x0c, 0x00, 
+0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x10, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x40, 0x00, 0x40, 0x00, 0xc0, 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, 0x01, 
+0x00, 0x03, 0x00, 0x02};
+static unsigned char courier24_93_bits[] = {
+0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x08, 0x08, 0x0f};
+static unsigned char courier24_94_bits[] = {
+0x70, 0x00, 0x50, 0x00, 0x58, 0x00, 0x98, 0x00, 0x88, 0x00, 0x8c, 0x01, 
+0x04, 0x01, 0x06, 0x03, 0x06, 0x03, 0x03, 0x02, 0x03, 0x06};
+static unsigned char courier24_95_bits[] = {
+0xff, 0xff, 0x01};
+static unsigned char courier24_96_bits[] = {
+0x0c, 0x0c, 0x06, 0x06, 0x07, 0x03};
+static unsigned char courier24_97_bits[] = {
+0xfc, 0x00, 0x80, 0x01, 0x00, 0x01, 0xfc, 0x01, 0x82, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x81, 0x01, 0xc3, 0x01, 0x3e, 0x07};
+static unsigned char courier24_98_bits[] = {
+0x07, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0xe4, 0x03, 
+0x34, 0x0c, 0x0c, 0x08, 0x0c, 0x10, 0x0c, 0x10, 0x0c, 0x10, 0x0c, 0x10, 
+0x1c, 0x08, 0x3c, 0x0c, 0xe7, 0x03};
+static unsigned char courier24_99_bits[] = {
+0xf8, 0x07, 0x06, 0x03, 0x02, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x02, 0x04, 0x06, 0x03, 0xf8, 0x00};
+static unsigned char courier24_100_bits[] = {
+0x00, 0x07, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0xf8, 0x04, 
+0x04, 0x07, 0x02, 0x06, 0x01, 0x06, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 
+0x02, 0x06, 0x8c, 0x05, 0xf8, 0x1c};
+static unsigned char courier24_101_bits[] = {
+0xf8, 0x00, 0x06, 0x03, 0x03, 0x06, 0x01, 0x04, 0xff, 0x07, 0x01, 0x00, 
+0x01, 0x00, 0x02, 0x00, 0x06, 0x06, 0xf8, 0x01};
+static unsigned char courier24_102_bits[] = {
+0xf0, 0x03, 0x18, 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xff, 0x01, 
+0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 
+0x08, 0x00, 0x08, 0x00, 0xff, 0x01};
+static unsigned char courier24_103_bits[] = {
+0xf8, 0x0e, 0x86, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 
+0x03, 0x02, 0x02, 0x03, 0x8c, 0x03, 0x78, 0x02, 0x00, 0x02, 0x00, 0x02, 
+0x00, 0x01, 0xf8, 0x00};
+static unsigned char courier24_104_bits[] = {
+0x07, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0xf4, 0x01, 
+0x1c, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0x04, 0x04, 0x1f, 0x1f};
+static unsigned char courier24_105_bits[] = {
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x3e, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_106_bits[] = {
+0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x7f, 0x40, 0x40, 0x40, 0x40, 0x40, 
+0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x21, 0x1f};
+static unsigned char courier24_107_bits[] = {
+0x07, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x84, 0x0f, 
+0x84, 0x01, 0xc4, 0x00, 0x34, 0x00, 0x3c, 0x00, 0x44, 0x00, 0x84, 0x01, 
+0x04, 0x03, 0x04, 0x06, 0x07, 0x1f};
+static unsigned char courier24_108_bits[] = {
+0x3e, 0x00, 0x22, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_109_bits[] = {
+0x77, 0x0f, 0x8c, 0x11, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 
+0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x9f, 0x73};
+static unsigned char courier24_110_bits[] = {
+0xf7, 0x01, 0x0c, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x9f, 0x1f};
+static unsigned char courier24_111_bits[] = {
+0xf8, 0x01, 0x06, 0x06, 0x02, 0x04, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x01, 0x08, 0x02, 0x04, 0x0c, 0x06, 0xf8, 0x01};
+static unsigned char courier24_112_bits[] = {
+0xef, 0x03, 0x38, 0x0c, 0x18, 0x08, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 
+0x08, 0x10, 0x18, 0x08, 0x38, 0x04, 0xe8, 0x03, 0x08, 0x00, 0x08, 0x00, 
+0x08, 0x00, 0x7f, 0x00};
+static unsigned char courier24_113_bits[] = {
+0xf8, 0x1d, 0x06, 0x07, 0x02, 0x06, 0x01, 0x04, 0x01, 0x04, 0x01, 0x04, 
+0x01, 0x04, 0x02, 0x06, 0x0c, 0x05, 0xf8, 0x04, 0x00, 0x04, 0x00, 0x04, 
+0x00, 0x04, 0xc0, 0x1f};
+static unsigned char courier24_114_bits[] = {
+0xcf, 0x07, 0x68, 0x04, 0x18, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 
+0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xff, 0x01};
+static unsigned char courier24_115_bits[] = {
+0x7c, 0x01, 0x82, 0x01, 0x02, 0x01, 0x06, 0x00, 0xfc, 0x00, 0xc1, 0x01, 
+0x01, 0x02, 0x03, 0x02, 0x07, 0x03, 0xf9, 0x00};
+static unsigned char courier24_116_bits[] = {
+0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0xff, 0x00, 0x02, 0x00, 
+0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 
+0x04, 0x03, 0xf8, 0x00};
+static unsigned char courier24_117_bits[] = {
+0x87, 0x03, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 
+0x04, 0x02, 0x04, 0x02, 0x8c, 0x03, 0xf8, 0x0e};
+static unsigned char courier24_118_bits[] = {
+0x1f, 0x1f, 0x04, 0x04, 0x0c, 0x02, 0x08, 0x02, 0x08, 0x01, 0x10, 0x01, 
+0x90, 0x01, 0xa0, 0x00, 0xe0, 0x00, 0x60, 0x00};
+static unsigned char courier24_119_bits[] = {
+0x0f, 0x3c, 0x02, 0x10, 0x82, 0x10, 0xc4, 0x08, 0xc4, 0x09, 0x44, 0x09, 
+0x28, 0x09, 0x28, 0x06, 0x28, 0x06, 0x10, 0x06};
+static unsigned char courier24_120_bits[] = {
+0x3e, 0x1e, 0x18, 0x04, 0x30, 0x02, 0x60, 0x01, 0xc0, 0x00, 0xe0, 0x01, 
+0x30, 0x03, 0x18, 0x06, 0x0c, 0x0c, 0x1f, 0x3e};
+static unsigned char courier24_121_bits[] = {
+0x0f, 0x1e, 0x04, 0x04, 0x04, 0x04, 0x08, 0x02, 0x08, 0x03, 0x10, 0x01, 
+0x90, 0x01, 0xa0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x10, 0x00, 0x7f, 0x00};
+static unsigned char courier24_122_bits[] = {
+0xfe, 0x03, 0x02, 0x01, 0x82, 0x00, 0x40, 0x00, 0x20, 0x00, 0x30, 0x00, 
+0x18, 0x00, 0x0c, 0x02, 0x06, 0x02, 0xff, 0x03};
+static unsigned char courier24_123_bits[] = {
+0x18, 0x04, 0x02, 0x02, 0x02, 0x06, 0x04, 0x04, 0x06, 0x03, 0x06, 0x04, 
+0x04, 0x06, 0x02, 0x02, 0x06, 0x1c};
+static unsigned char courier24_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char courier24_125_bits[] = {
+0x07, 0x0c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x18, 0x30, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x08, 0x08, 0x07};
+static unsigned char courier24_126_bits[] = {
+0x04, 0x02, 0x3e, 0x03, 0xe1, 0x01, 0x01, 0x00};
+static unsigned char courier24_127_bits[] = {
+0x00};
+static unsigned char courier24_128_bits[] = {
+0x00};
+static unsigned char courier24_129_bits[] = {
+0x00};
+static unsigned char courier24_130_bits[] = {
+0x00};
+static unsigned char courier24_131_bits[] = {
+0x00};
+static unsigned char courier24_132_bits[] = {
+0x00};
+static unsigned char courier24_133_bits[] = {
+0x00};
+static unsigned char courier24_134_bits[] = {
+0x00};
+static unsigned char courier24_135_bits[] = {
+0x00};
+static unsigned char courier24_136_bits[] = {
+0x00};
+static unsigned char courier24_137_bits[] = {
+0x00};
+static unsigned char courier24_138_bits[] = {
+0x00};
+static unsigned char courier24_139_bits[] = {
+0x00};
+static unsigned char courier24_140_bits[] = {
+0x00};
+static unsigned char courier24_141_bits[] = {
+0x00};
+static unsigned char courier24_142_bits[] = {
+0x00};
+static unsigned char courier24_143_bits[] = {
+0x00};
+static unsigned char courier24_144_bits[] = {
+0x00};
+static unsigned char courier24_145_bits[] = {
+0x00};
+static unsigned char courier24_146_bits[] = {
+0x00};
+static unsigned char courier24_147_bits[] = {
+0x00};
+static unsigned char courier24_148_bits[] = {
+0x00};
+static unsigned char courier24_149_bits[] = {
+0x00};
+static unsigned char courier24_150_bits[] = {
+0x00};
+static unsigned char courier24_151_bits[] = {
+0x00};
+static unsigned char courier24_152_bits[] = {
+0x00};
+static unsigned char courier24_153_bits[] = {
+0x00};
+static unsigned char courier24_154_bits[] = {
+0x00};
+static unsigned char courier24_155_bits[] = {
+0x00};
+static unsigned char courier24_156_bits[] = {
+0x00};
+static unsigned char courier24_157_bits[] = {
+0x00};
+static unsigned char courier24_158_bits[] = {
+0x00};
+static unsigned char courier24_159_bits[] = {
+0x00};
+static unsigned char courier24_160_bits[] = {
+0x00};
+static unsigned char courier24_161_bits[] = {
+0x07, 0x07, 0x07, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06};
+static unsigned char courier24_162_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x7c, 0x01, 0x92, 0x01, 
+0x11, 0x01, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x12, 0x01, 0xd6, 0x00, 
+0x38, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char courier24_163_bits[] = {
+0xf0, 0x00, 0x18, 0x03, 0x0c, 0x02, 0x04, 0x02, 0x04, 0x00, 0x04, 0x00, 
+0x0c, 0x00, 0xff, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 
+0x04, 0x00, 0x3c, 0x04, 0xc6, 0x03};
+static unsigned char courier24_164_bits[] = {
+0x01, 0x02, 0x7a, 0x03, 0x8c, 0x00, 0x06, 0x01, 0x02, 0x01, 0x02, 0x01, 
+0x02, 0x01, 0x84, 0x00, 0x7a, 0x01, 0x01, 0x02, 0x01, 0x02};
+static unsigned char courier24_165_bits[] = {
+0x1f, 0x1f, 0x04, 0x06, 0x08, 0x02, 0x18, 0x03, 0xb0, 0x01, 0xe0, 0x00, 
+0x40, 0x00, 0xf8, 0x07, 0x40, 0x00, 0xf8, 0x07, 0x40, 0x00, 0x40, 0x00, 
+0x40, 0x00, 0xf8, 0x03};
+static unsigned char courier24_166_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char courier24_167_bits[] = {
+0xfc, 0x00, 0x86, 0x00, 0x82, 0x00, 0x82, 0x00, 0x0c, 0x00, 0x1f, 0x00, 
+0x31, 0x00, 0x61, 0x00, 0x82, 0x00, 0x0c, 0x01, 0x98, 0x01, 0xf0, 0x00, 
+0x60, 0x00, 0x41, 0x00, 0x41, 0x00, 0x3f, 0x00};
+static unsigned char courier24_168_bits[] = {
+0x1b, 0x1b, 0x1b};
+static unsigned char courier24_169_bits[] = {
+0xe0, 0x03, 0x18, 0x04, 0x04, 0x08, 0xe2, 0x11, 0x32, 0x32, 0x09, 0x22, 
+0x09, 0x20, 0x09, 0x20, 0x19, 0x20, 0x32, 0x22, 0xe2, 0x11, 0x04, 0x08, 
+0x18, 0x04, 0xe0, 0x03};
+static unsigned char courier24_170_bits[] = {
+0x1e, 0x10, 0x1e, 0x11, 0x19, 0x3e, 0x00, 0x3f};
+static unsigned char courier24_171_bits[] = {
+0x20, 0x02, 0x98, 0x01, 0xcc, 0x00, 0x66, 0x00, 0x33, 0x00, 0x33, 0x00, 
+0x46, 0x00, 0x88, 0x00, 0x30, 0x03, 0x20, 0x02};
+static unsigned char courier24_172_bits[] = {
+0xff, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02};
+static unsigned char courier24_173_bits[] = {
+0x1f};
+static unsigned char courier24_174_bits[] = {
+0xe0, 0x03, 0x18, 0x04, 0x04, 0x08, 0xe2, 0x11, 0x23, 0x22, 0x21, 0x23, 
+0xe1, 0x20, 0x21, 0x21, 0x21, 0x21, 0xf2, 0x32, 0x02, 0x10, 0x04, 0x08, 
+0x18, 0x04, 0xe0, 0x03};
+static unsigned char courier24_175_bits[] = {
+0xff};
+static unsigned char courier24_176_bits[] = {
+0x1c, 0x22, 0x41, 0x41, 0x41, 0x23, 0x1e};
+static unsigned char courier24_177_bits[] = {
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xff, 0x07, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0xff, 0x07};
+static unsigned char courier24_178_bits[] = {
+0x1e, 0x33, 0x21, 0x20, 0x10, 0x08, 0x04, 0x22, 0x3f};
+static unsigned char courier24_179_bits[] = {
+0x0e, 0x11, 0x10, 0x10, 0x1c, 0x10, 0x20, 0x30, 0x1e};
+static unsigned char courier24_180_bits[] = {
+0x0c, 0x06, 0x03};
+static unsigned char courier24_181_bits[] = {
+0x87, 0x03, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 
+0x04, 0x02, 0x04, 0x02, 0x0c, 0x03, 0xf4, 0x0f, 0x04, 0x00, 0x04, 0x00, 
+0x04, 0x00, 0x04, 0x00};
+static unsigned char courier24_182_bits[] = {
+0xfc, 0x01, 0x92, 0x00, 0x91, 0x00, 0x91, 0x00, 0x91, 0x00, 0x91, 0x00, 
+0x92, 0x00, 0x9c, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 
+0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0xdc, 0x01};
+static unsigned char courier24_183_bits[] = {
+0x06, 0x0f, 0x0e};
+static unsigned char courier24_184_bits[] = {
+0x04, 0x0c, 0x0c, 0x0f};
+static unsigned char courier24_185_bits[] = {
+0x0c, 0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3f};
+static unsigned char courier24_186_bits[] = {
+0x1e, 0x23, 0x21, 0x21, 0x33, 0x1c, 0x00, 0x3f};
+static unsigned char courier24_187_bits[] = {
+0x11, 0x00, 0x66, 0x00, 0xcc, 0x00, 0x98, 0x01, 0x30, 0x03, 0x30, 0x03, 
+0x98, 0x01, 0x44, 0x00, 0x33, 0x00, 0x22, 0x00};
+static unsigned char courier24_188_bits[] = {
+0x06, 0x10, 0x05, 0x18, 0x04, 0x08, 0x04, 0x04, 0x04, 0x02, 0x04, 0x02, 
+0x04, 0x01, 0x84, 0x00, 0x4f, 0x0c, 0x40, 0x0c, 0x20, 0x0a, 0x10, 0x0a, 
+0x18, 0x09, 0x88, 0x08, 0x84, 0x1f, 0x02, 0x08, 0x02, 0x1e};
+static unsigned char courier24_189_bits[] = {
+0x0c, 0x20, 0x0a, 0x10, 0x08, 0x08, 0x08, 0x0c, 0x08, 0x04, 0x08, 0x02, 
+0x08, 0x03, 0x08, 0x01, 0x9e, 0x1c, 0x40, 0x22, 0x60, 0x20, 0x20, 0x20, 
+0x10, 0x10, 0x08, 0x18, 0x08, 0x0c, 0x04, 0x22, 0x02, 0x3e};
+static unsigned char courier24_190_bits[] = {
+0x0e, 0x30, 0x11, 0x10, 0x10, 0x08, 0x18, 0x04, 0x0c, 0x06, 0x10, 0x02, 
+0x10, 0x01, 0x90, 0x00, 0x8f, 0x0c, 0x40, 0x0c, 0x20, 0x0a, 0x10, 0x0a, 
+0x10, 0x09, 0x88, 0x09, 0x84, 0x1f, 0x02, 0x08, 0x02, 0x1e};
+static unsigned char courier24_191_bits[] = {
+0x38, 0x38, 0x38, 0x00, 0x10, 0x18, 0x06, 0x02, 0x01, 0x01, 0x81, 0x82, 
+0x7c};
+static unsigned char courier24_192_bits[] = {
+0x40, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0xfc, 0x00, 0xc0, 0x00, 
+0xe0, 0x01, 0x60, 0x01, 0x20, 0x03, 0x30, 0x03, 0x10, 0x02, 0x18, 0x06, 
+0x18, 0x04, 0xf8, 0x07, 0x0c, 0x0c, 0x04, 0x08, 0x06, 0x18, 0x1f, 0x3e};
+static unsigned char courier24_193_bits[] = {
+0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x00, 0x00, 0xfc, 0x00, 0xc0, 0x00, 
+0xe0, 0x01, 0x60, 0x01, 0x20, 0x03, 0x30, 0x03, 0x10, 0x02, 0x18, 0x06, 
+0x18, 0x04, 0xf8, 0x07, 0x0c, 0x0c, 0x04, 0x08, 0x06, 0x18, 0x1f, 0x3e};
+static unsigned char courier24_194_bits[] = {
+0xc0, 0x00, 0x20, 0x01, 0x10, 0x02, 0x00, 0x00, 0xfc, 0x00, 0xc0, 0x00, 
+0xe0, 0x01, 0x60, 0x01, 0x20, 0x03, 0x30, 0x03, 0x10, 0x02, 0x18, 0x06, 
+0x18, 0x04, 0xf8, 0x07, 0x0c, 0x0c, 0x04, 0x08, 0x06, 0x18, 0x1f, 0x3e};
+static unsigned char courier24_195_bits[] = {
+0x70, 0x04, 0x90, 0x03, 0x00, 0x01, 0x00, 0x00, 0xfc, 0x00, 0xc0, 0x00, 
+0xe0, 0x01, 0x60, 0x01, 0x20, 0x03, 0x30, 0x03, 0x10, 0x02, 0x18, 0x06, 
+0x18, 0x04, 0xf8, 0x07, 0x0c, 0x0c, 0x04, 0x08, 0x06, 0x18, 0x1f, 0x3e};
+static unsigned char courier24_196_bits[] = {
+0x60, 0x03, 0x60, 0x03, 0x60, 0x03, 0x00, 0x00, 0xfc, 0x00, 0xc0, 0x00, 
+0xe0, 0x01, 0x60, 0x01, 0x20, 0x03, 0x30, 0x03, 0x10, 0x02, 0x18, 0x06, 
+0x18, 0x04, 0xf8, 0x07, 0x0c, 0x0c, 0x04, 0x08, 0x06, 0x18, 0x1f, 0x3e};
+static unsigned char courier24_197_bits[] = {
+0xc0, 0x00, 0x20, 0x01, 0x20, 0x01, 0xc0, 0x00, 0x00, 0x00, 0xfc, 0x00, 
+0xc0, 0x00, 0xe0, 0x01, 0x60, 0x01, 0x20, 0x03, 0x30, 0x03, 0x10, 0x02, 
+0x18, 0x06, 0x18, 0x04, 0xf8, 0x07, 0x0c, 0x0c, 0x04, 0x08, 0x06, 0x18, 
+0x1f, 0x3e};
+static unsigned char courier24_198_bits[] = {
+0xf0, 0x1f, 0xc0, 0x10, 0xc0, 0x10, 0xa0, 0x10, 0xa0, 0x14, 0xb0, 0x04, 
+0x90, 0x07, 0x90, 0x04, 0x98, 0x04, 0xf8, 0x04, 0x8c, 0x10, 0x8c, 0x10, 
+0x86, 0x10, 0x9f, 0x1f};
+static unsigned char courier24_199_bits[] = {
+0xf8, 0x04, 0x0c, 0x07, 0x02, 0x06, 0x02, 0x06, 0x01, 0x04, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x02, 0x0c, 
+0x04, 0x07, 0xf8, 0x01, 0x20, 0x00, 0xe0, 0x00, 0xf0, 0x00};
+static unsigned char courier24_200_bits[] = {
+0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x0f, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x01, 0xf8, 0x01, 0x08, 0x01, 
+0x08, 0x01, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xff, 0x0f};
+static unsigned char courier24_201_bits[] = {
+0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0x0f, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x01, 0xf8, 0x01, 0x08, 0x01, 
+0x08, 0x01, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xff, 0x0f};
+static unsigned char courier24_202_bits[] = {
+0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x00, 0xff, 0x0f, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x01, 0xf8, 0x01, 0x08, 0x01, 
+0x08, 0x01, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xff, 0x0f};
+static unsigned char courier24_203_bits[] = {
+0xb0, 0x01, 0xb0, 0x01, 0xb0, 0x01, 0x00, 0x00, 0xff, 0x0f, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x01, 0xf8, 0x01, 0x08, 0x01, 
+0x08, 0x01, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xff, 0x0f};
+static unsigned char courier24_204_bits[] = {
+0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0x03, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_205_bits[] = {
+0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x00, 0x00, 0xff, 0x03, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_206_bits[] = {
+0x30, 0x00, 0x48, 0x00, 0x84, 0x00, 0x00, 0x00, 0xff, 0x03, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_207_bits[] = {
+0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0x00, 0x00, 0xff, 0x03, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_208_bits[] = {
+0xff, 0x00, 0x04, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x08, 0x04, 0x08, 
+0x7f, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x04, 0x04, 0x06, 
+0x04, 0x03, 0xff, 0x01};
+static unsigned char courier24_209_bits[] = {
+0x38, 0x02, 0xc8, 0x01, 0x80, 0x00, 0x00, 0x00, 0x0f, 0x1f, 0x1c, 0x08, 
+0x14, 0x08, 0x14, 0x08, 0x24, 0x08, 0x24, 0x08, 0x44, 0x08, 0x44, 0x08, 
+0x84, 0x08, 0x84, 0x08, 0x04, 0x09, 0x04, 0x09, 0x04, 0x0a, 0x1f, 0x0e};
+static unsigned char courier24_210_bits[] = {
+0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x0c, 0x03, 
+0x06, 0x06, 0x02, 0x04, 0x03, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x01, 0x08, 0x01, 0x08, 0x02, 0x04, 0x06, 0x04, 0x0c, 0x03, 0xf0, 0x01};
+static unsigned char courier24_211_bits[] = {
+0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x0c, 0x03, 
+0x06, 0x06, 0x02, 0x04, 0x03, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x01, 0x08, 0x01, 0x08, 0x02, 0x04, 0x06, 0x04, 0x0c, 0x03, 0xf0, 0x01};
+static unsigned char courier24_212_bits[] = {
+0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x00, 0xf0, 0x01, 0x0c, 0x03, 
+0x06, 0x06, 0x02, 0x04, 0x03, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x01, 0x08, 0x01, 0x08, 0x02, 0x04, 0x06, 0x04, 0x0c, 0x03, 0xf0, 0x01};
+static unsigned char courier24_213_bits[] = {
+0x38, 0x02, 0xc8, 0x01, 0x80, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x0c, 0x03, 
+0x06, 0x06, 0x02, 0x04, 0x03, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x01, 0x08, 0x01, 0x08, 0x02, 0x04, 0x06, 0x04, 0x0c, 0x03, 0xf0, 0x01};
+static unsigned char courier24_214_bits[] = {
+0xb0, 0x01, 0xb0, 0x01, 0xb0, 0x01, 0x00, 0x00, 0xf0, 0x01, 0x0c, 0x03, 
+0x06, 0x06, 0x02, 0x04, 0x03, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x01, 0x08, 0x01, 0x08, 0x02, 0x04, 0x06, 0x04, 0x0c, 0x03, 0xf0, 0x01};
+static unsigned char courier24_215_bits[] = {
+0x02, 0x01, 0x83, 0x01, 0xc6, 0x01, 0x6c, 0x00, 0x38, 0x00, 0x38, 0x00, 
+0x4c, 0x00, 0xc2, 0x00, 0x81, 0x01};
+static unsigned char courier24_216_bits[] = {
+0xf0, 0x05, 0x0c, 0x07, 0x06, 0x06, 0x02, 0x0e, 0x03, 0x09, 0x81, 0x08, 
+0xc1, 0x08, 0x41, 0x08, 0x21, 0x08, 0x13, 0x08, 0x1a, 0x04, 0x0e, 0x04, 
+0x0e, 0x03, 0xfa, 0x01};
+static unsigned char courier24_217_bits[] = {
+0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x9f, 0x1f, 0x02, 0x04, 
+0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 
+0x02, 0x04, 0x02, 0x04, 0x06, 0x04, 0x06, 0x04, 0x0c, 0x02, 0xf8, 0x01};
+static unsigned char courier24_218_bits[] = {
+0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0x9f, 0x1f, 0x02, 0x04, 
+0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 
+0x02, 0x04, 0x02, 0x04, 0x06, 0x04, 0x06, 0x04, 0x0c, 0x02, 0xf8, 0x01};
+static unsigned char courier24_219_bits[] = {
+0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x00, 0x9f, 0x1f, 0x02, 0x04, 
+0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 
+0x02, 0x04, 0x02, 0x04, 0x06, 0x04, 0x06, 0x04, 0x0c, 0x02, 0xf8, 0x01};
+static unsigned char courier24_220_bits[] = {
+0xb0, 0x01, 0xb0, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x9f, 0x1f, 0x02, 0x04, 
+0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 
+0x02, 0x04, 0x02, 0x04, 0x06, 0x04, 0x06, 0x04, 0x0c, 0x02, 0xf8, 0x01};
+static unsigned char courier24_221_bits[] = {
+0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x04, 0x04, 
+0x0c, 0x06, 0x08, 0x02, 0x10, 0x01, 0x30, 0x01, 0xa0, 0x00, 0xe0, 0x00, 
+0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xf8, 0x03};
+static unsigned char courier24_222_bits[] = {
+0x3f, 0x00, 0x04, 0x00, 0x04, 0x00, 0xfc, 0x01, 0x04, 0x03, 0x04, 0x06, 
+0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x04, 0x03, 0xfc, 0x01, 0x04, 0x00, 
+0x04, 0x00, 0x3f, 0x00};
+static unsigned char courier24_223_bits[] = {
+0x78, 0x00, 0x8c, 0x00, 0x06, 0x01, 0x02, 0x01, 0x02, 0x01, 0xc2, 0x00, 
+0x72, 0x00, 0x82, 0x01, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
+0x02, 0x02, 0x12, 0x01, 0xf3, 0x00};
+static unsigned char courier24_224_bits[] = {
+0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 
+0x80, 0x01, 0x00, 0x01, 0xfc, 0x01, 0x82, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x81, 0x01, 0xc3, 0x01, 0x3e, 0x07};
+static unsigned char courier24_225_bits[] = {
+0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 
+0x80, 0x01, 0x00, 0x01, 0xfc, 0x01, 0x82, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x81, 0x01, 0xc3, 0x01, 0x3e, 0x07};
+static unsigned char courier24_226_bits[] = {
+0x30, 0x00, 0x68, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 
+0x80, 0x01, 0x00, 0x01, 0xfc, 0x01, 0x82, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x81, 0x01, 0xc3, 0x01, 0x3e, 0x07};
+static unsigned char courier24_227_bits[] = {
+0x1c, 0x01, 0xe4, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 
+0x80, 0x01, 0x00, 0x01, 0xfc, 0x01, 0x82, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x81, 0x01, 0xc3, 0x01, 0x3e, 0x07};
+static unsigned char courier24_228_bits[] = {
+0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 
+0x80, 0x01, 0x00, 0x01, 0xfc, 0x01, 0x82, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x81, 0x01, 0xc3, 0x01, 0x3e, 0x07};
+static unsigned char courier24_229_bits[] = {
+0x70, 0x00, 0x48, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 
+0x80, 0x01, 0x00, 0x01, 0xfc, 0x01, 0x82, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x81, 0x01, 0xc3, 0x01, 0x3e, 0x07};
+static unsigned char courier24_230_bits[] = {
+0xfe, 0x07, 0x40, 0x0c, 0x40, 0x08, 0x40, 0x08, 0xfc, 0x0f, 0x42, 0x00, 
+0x41, 0x00, 0x41, 0x00, 0xe1, 0x00, 0x1e, 0x0f};
+static unsigned char courier24_231_bits[] = {
+0xf8, 0x06, 0x06, 0x03, 0x02, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x02, 0x04, 0x06, 0x03, 0xf8, 0x00, 0x20, 0x00, 0x60, 0x00, 
+0x70, 0x00};
+static unsigned char courier24_232_bits[] = {
+0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 
+0x06, 0x03, 0x03, 0x06, 0x01, 0x04, 0xff, 0x07, 0x01, 0x00, 0x01, 0x00, 
+0x02, 0x00, 0x06, 0x06, 0xf8, 0x01};
+static unsigned char courier24_233_bits[] = {
+0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 
+0x06, 0x03, 0x03, 0x06, 0x01, 0x04, 0xff, 0x07, 0x01, 0x00, 0x01, 0x00, 
+0x02, 0x00, 0x06, 0x06, 0xf8, 0x01};
+static unsigned char courier24_234_bits[] = {
+0x30, 0x00, 0x68, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 
+0x06, 0x03, 0x03, 0x06, 0x01, 0x04, 0xff, 0x07, 0x01, 0x00, 0x01, 0x00, 
+0x02, 0x00, 0x06, 0x06, 0xf8, 0x01};
+static unsigned char courier24_235_bits[] = {
+0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 
+0x06, 0x03, 0x03, 0x06, 0x01, 0x04, 0xff, 0x07, 0x01, 0x00, 0x01, 0x00, 
+0x02, 0x00, 0x06, 0x06, 0xf8, 0x01};
+static unsigned char courier24_236_bits[] = {
+0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_237_bits[] = {
+0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_238_bits[] = {
+0x30, 0x00, 0x68, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_239_bits[] = {
+0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0xff, 0x03};
+static unsigned char courier24_240_bits[] = {
+0x40, 0x00, 0x3c, 0x00, 0x70, 0x00, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x03, 
+0x00, 0x06, 0xf8, 0x07, 0x06, 0x0e, 0x02, 0x0c, 0x01, 0x08, 0x01, 0x08, 
+0x01, 0x08, 0x01, 0x08, 0x02, 0x04, 0x0c, 0x06, 0xf8, 0x01};
+static unsigned char courier24_241_bits[] = {
+0x38, 0x02, 0xc8, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0x01, 
+0x0c, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0x04, 0x04, 0x9f, 0x1f};
+static unsigned char courier24_242_bits[] = {
+0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 
+0x06, 0x06, 0x02, 0x04, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x02, 0x04, 0x0c, 0x06, 0xf8, 0x01};
+static unsigned char courier24_243_bits[] = {
+0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 
+0x06, 0x06, 0x02, 0x04, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x02, 0x04, 0x0c, 0x06, 0xf8, 0x01};
+static unsigned char courier24_244_bits[] = {
+0x60, 0x00, 0xd0, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 
+0x06, 0x06, 0x02, 0x04, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x02, 0x04, 0x0c, 0x06, 0xf8, 0x01};
+static unsigned char courier24_245_bits[] = {
+0x38, 0x02, 0xc8, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 
+0x06, 0x06, 0x02, 0x04, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x02, 0x04, 0x0c, 0x06, 0xf8, 0x01};
+static unsigned char courier24_246_bits[] = {
+0xb0, 0x01, 0xb0, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 
+0x06, 0x06, 0x02, 0x04, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 0x01, 0x08, 
+0x02, 0x04, 0x0c, 0x06, 0xf8, 0x01};
+static unsigned char courier24_247_bits[] = {
+0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00};
+static unsigned char courier24_248_bits[] = {
+0xf8, 0x05, 0x06, 0x06, 0x02, 0x0d, 0x81, 0x08, 0x41, 0x08, 0x31, 0x08, 
+0x1b, 0x08, 0x0e, 0x04, 0x0e, 0x06, 0xfb, 0x01};
+static unsigned char courier24_249_bits[] = {
+0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x03, 
+0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 
+0x04, 0x02, 0x8c, 0x03, 0xf8, 0x0e};
+static unsigned char courier24_250_bits[] = {
+0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x03, 
+0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 
+0x04, 0x02, 0x8c, 0x03, 0xf8, 0x0e};
+static unsigned char courier24_251_bits[] = {
+0x60, 0x00, 0xd0, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x87, 0x03, 
+0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 
+0x04, 0x02, 0x8c, 0x03, 0xf8, 0x0e};
+static unsigned char courier24_252_bits[] = {
+0xb0, 0x01, 0xb0, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x87, 0x03, 
+0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 
+0x04, 0x02, 0x8c, 0x03, 0xf8, 0x0e};
+static unsigned char courier24_253_bits[] = {
+0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x1e, 
+0x04, 0x04, 0x04, 0x04, 0x08, 0x02, 0x08, 0x03, 0x10, 0x01, 0x90, 0x01, 
+0xa0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0x00, 
+0x7f, 0x00};
+static unsigned char courier24_254_bits[] = {
+0x0f, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xe8, 0x03, 
+0x38, 0x0c, 0x18, 0x08, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 
+0x18, 0x08, 0x38, 0x04, 0xe8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 
+0x7f, 0x00};
+static unsigned char courier24_255_bits[] = {
+0xb0, 0x01, 0xb0, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x1e, 
+0x04, 0x04, 0x04, 0x04, 0x08, 0x02, 0x08, 0x03, 0x10, 0x01, 0x90, 0x01, 
+0xa0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0x00, 
+0x7f, 0x00};
+static RotFont courier24font[] = {
+{5, 1, 1, courier24_0_bits},
+{5, 1, 1, courier24_1_bits},
+{5, 1, 1, courier24_2_bits},
+{5, 1, 1, courier24_3_bits},
+{5, 1, 1, courier24_4_bits},
+{5, 1, 1, courier24_5_bits},
+{5, 1, 1, courier24_6_bits},
+{5, 1, 1, courier24_7_bits},
+{5, 1, 1, courier24_8_bits},
+{5, 1, 1, courier24_9_bits},
+{5, 1, 1, courier24_10_bits},
+{5, 1, 1, courier24_11_bits},
+{5, 1, 1, courier24_12_bits},
+{5, 1, 1, courier24_13_bits},
+{5, 1, 1, courier24_14_bits},
+{5, 1, 1, courier24_15_bits},
+{5, 1, 1, courier24_16_bits},
+{5, 1, 1, courier24_17_bits},
+{5, 1, 1, courier24_18_bits},
+{5, 1, 1, courier24_19_bits},
+{5, 1, 1, courier24_20_bits},
+{5, 1, 1, courier24_21_bits},
+{5, 1, 1, courier24_22_bits},
+{5, 1, 1, courier24_23_bits},
+{5, 1, 1, courier24_24_bits},
+{5, 1, 1, courier24_25_bits},
+{5, 1, 1, courier24_26_bits},
+{5, 1, 1, courier24_27_bits},
+{5, 1, 1, courier24_28_bits},
+{5, 1, 1, courier24_29_bits},
+{5, 1, 1, courier24_30_bits},
+{5, 1, 1, courier24_31_bits},
+{10, 1, 1, courier24_32_bits},
+{3, 14, 14, courier24_33_bits},
+{6, 7, 14, courier24_34_bits},
+{11, 17, 16, courier24_35_bits},
+{10, 19, 16, courier24_36_bits},
+{12, 15, 15, courier24_37_bits},
+{10, 13, 13, courier24_38_bits},
+{4, 6, 14, courier24_39_bits},
+{4, 18, 16, courier24_40_bits},
+{4, 18, 16, courier24_41_bits},
+{9, 9, 14, courier24_42_bits},
+{11, 11, 13, courier24_43_bits},
+{4, 6, 3, courier24_44_bits},
+{11, 1, 8, courier24_45_bits},
+{4, 3, 3, courier24_46_bits},
+{10, 20, 17, courier24_47_bits},
+{9, 15, 15, courier24_48_bits},
+{10, 15, 15, courier24_49_bits},
+{9, 15, 15, courier24_50_bits},
+{9, 15, 15, courier24_51_bits},
+{10, 15, 15, courier24_52_bits},
+{10, 15, 15, courier24_53_bits},
+{9, 15, 15, courier24_54_bits},
+{10, 15, 15, courier24_55_bits},
+{9, 15, 15, courier24_56_bits},
+{9, 15, 15, courier24_57_bits},
+{4, 9, 9, courier24_58_bits},
+{5, 12, 9, courier24_59_bits},
+{12, 11, 13, courier24_60_bits},
+{11, 5, 10, courier24_61_bits},
+{12, 11, 13, courier24_62_bits},
+{8, 14, 14, courier24_63_bits},
+{13, 13, 13, courier24_64_bits},
+{14, 14, 14, courier24_65_bits},
+{12, 14, 14, courier24_66_bits},
+{12, 14, 14, courier24_67_bits},
+{12, 14, 14, courier24_68_bits},
+{12, 14, 14, courier24_69_bits},
+{11, 14, 14, courier24_70_bits},
+{13, 14, 14, courier24_71_bits},
+{12, 14, 14, courier24_72_bits},
+{10, 14, 14, courier24_73_bits},
+{13, 14, 14, courier24_74_bits},
+{13, 14, 14, courier24_75_bits},
+{12, 14, 14, courier24_76_bits},
+{14, 14, 14, courier24_77_bits},
+{13, 14, 14, courier24_78_bits},
+{12, 14, 14, courier24_79_bits},
+{11, 14, 14, courier24_80_bits},
+{12, 17, 14, courier24_81_bits},
+{13, 14, 14, courier24_82_bits},
+{9, 14, 14, courier24_83_bits},
+{13, 14, 14, courier24_84_bits},
+{13, 14, 14, courier24_85_bits},
+{14, 14, 14, courier24_86_bits},
+{15, 14, 14, courier24_87_bits},
+{13, 14, 14, courier24_88_bits},
+{13, 14, 14, courier24_89_bits},
+{10, 14, 14, courier24_90_bits},
+{4, 18, 16, courier24_91_bits},
+{10, 20, 17, courier24_92_bits},
+{4, 18, 16, courier24_93_bits},
+{11, 11, 14, courier24_94_bits},
+{17, 1, -2, courier24_95_bits},
+{4, 6, 14, courier24_96_bits},
+{11, 10, 10, courier24_97_bits},
+{13, 15, 15, courier24_98_bits},
+{11, 10, 10, courier24_99_bits},
+{13, 15, 15, courier24_100_bits},
+{11, 10, 10, courier24_101_bits},
+{10, 15, 15, courier24_102_bits},
+{12, 14, 10, courier24_103_bits},
+{13, 15, 15, courier24_104_bits},
+{10, 16, 16, courier24_105_bits},
+{8, 20, 16, courier24_106_bits},
+{13, 15, 15, courier24_107_bits},
+{10, 15, 15, courier24_108_bits},
+{15, 10, 10, courier24_109_bits},
+{13, 10, 10, courier24_110_bits},
+{12, 10, 10, courier24_111_bits},
+{13, 14, 10, courier24_112_bits},
+{13, 14, 10, courier24_113_bits},
+{12, 10, 10, courier24_114_bits},
+{10, 10, 10, courier24_115_bits},
+{10, 14, 14, courier24_116_bits},
+{12, 10, 10, courier24_117_bits},
+{13, 10, 10, courier24_118_bits},
+{14, 10, 10, courier24_119_bits},
+{14, 10, 10, courier24_120_bits},
+{13, 14, 10, courier24_121_bits},
+{10, 10, 10, courier24_122_bits},
+{5, 18, 16, courier24_123_bits},
+{1, 21, 16, courier24_124_bits},
+{6, 18, 16, courier24_125_bits},
+{10, 4, 9, courier24_126_bits},
+{5, 1, 1, courier24_127_bits},
+{5, 1, 1, courier24_128_bits},
+{5, 1, 1, courier24_129_bits},
+{5, 1, 1, courier24_130_bits},
+{5, 1, 1, courier24_131_bits},
+{5, 1, 1, courier24_132_bits},
+{5, 1, 1, courier24_133_bits},
+{5, 1, 1, courier24_134_bits},
+{5, 1, 1, courier24_135_bits},
+{5, 1, 1, courier24_136_bits},
+{5, 1, 1, courier24_137_bits},
+{5, 1, 1, courier24_138_bits},
+{5, 1, 1, courier24_139_bits},
+{5, 1, 1, courier24_140_bits},
+{5, 1, 1, courier24_141_bits},
+{5, 1, 1, courier24_142_bits},
+{5, 1, 1, courier24_143_bits},
+{5, 1, 1, courier24_144_bits},
+{5, 1, 1, courier24_145_bits},
+{5, 1, 1, courier24_146_bits},
+{5, 1, 1, courier24_147_bits},
+{5, 1, 1, courier24_148_bits},
+{5, 1, 1, courier24_149_bits},
+{5, 1, 1, courier24_150_bits},
+{5, 1, 1, courier24_151_bits},
+{5, 1, 1, courier24_152_bits},
+{5, 1, 1, courier24_153_bits},
+{5, 1, 1, courier24_154_bits},
+{5, 1, 1, courier24_155_bits},
+{5, 1, 1, courier24_156_bits},
+{5, 1, 1, courier24_157_bits},
+{5, 1, 1, courier24_158_bits},
+{5, 1, 1, courier24_159_bits},
+{5, 1, 1, courier24_160_bits},
+{3, 13, 10, courier24_161_bits},
+{9, 16, 15, courier24_162_bits},
+{11, 15, 15, courier24_163_bits},
+{11, 11, 12, courier24_164_bits},
+{13, 14, 14, courier24_165_bits},
+{1, 21, 16, courier24_166_bits},
+{9, 16, 14, courier24_167_bits},
+{5, 3, 15, courier24_168_bits},
+{14, 14, 14, courier24_169_bits},
+{6, 8, 14, courier24_170_bits},
+{10, 10, 10, courier24_171_bits},
+{10, 6, 10, courier24_172_bits},
+{5, 1, 7, courier24_173_bits},
+{14, 14, 14, courier24_174_bits},
+{8, 1, 14, courier24_175_bits},
+{7, 7, 15, courier24_176_bits},
+{11, 13, 14, courier24_177_bits},
+{6, 9, 15, courier24_178_bits},
+{6, 9, 15, courier24_179_bits},
+{5, 3, 15, courier24_180_bits},
+{12, 14, 10, courier24_181_bits},
+{9, 16, 14, courier24_182_bits},
+{4, 3, 9, courier24_183_bits},
+{4, 4, 1, courier24_184_bits},
+{6, 9, 15, courier24_185_bits},
+{6, 8, 14, courier24_186_bits},
+{10, 10, 10, courier24_187_bits},
+{13, 17, 15, courier24_188_bits},
+{14, 17, 15, courier24_189_bits},
+{14, 17, 15, courier24_190_bits},
+{8, 13, 10, courier24_191_bits},
+{14, 18, 18, courier24_192_bits},
+{14, 18, 18, courier24_193_bits},
+{14, 18, 18, courier24_194_bits},
+{14, 18, 18, courier24_195_bits},
+{14, 18, 18, courier24_196_bits},
+{14, 19, 19, courier24_197_bits},
+{13, 14, 14, courier24_198_bits},
+{12, 17, 14, courier24_199_bits},
+{12, 18, 18, courier24_200_bits},
+{12, 18, 18, courier24_201_bits},
+{12, 18, 18, courier24_202_bits},
+{12, 18, 18, courier24_203_bits},
+{10, 18, 18, courier24_204_bits},
+{10, 18, 18, courier24_205_bits},
+{10, 18, 18, courier24_206_bits},
+{10, 18, 18, courier24_207_bits},
+{12, 14, 14, courier24_208_bits},
+{13, 18, 18, courier24_209_bits},
+{12, 18, 18, courier24_210_bits},
+{12, 18, 18, courier24_211_bits},
+{12, 18, 18, courier24_212_bits},
+{12, 18, 18, courier24_213_bits},
+{12, 18, 18, courier24_214_bits},
+{9, 9, 12, courier24_215_bits},
+{12, 14, 14, courier24_216_bits},
+{13, 18, 18, courier24_217_bits},
+{13, 18, 18, courier24_218_bits},
+{13, 18, 18, courier24_219_bits},
+{13, 18, 18, courier24_220_bits},
+{13, 18, 18, courier24_221_bits},
+{11, 14, 14, courier24_222_bits},
+{10, 15, 15, courier24_223_bits},
+{11, 15, 15, courier24_224_bits},
+{11, 15, 15, courier24_225_bits},
+{11, 15, 15, courier24_226_bits},
+{11, 15, 15, courier24_227_bits},
+{11, 15, 15, courier24_228_bits},
+{11, 15, 15, courier24_229_bits},
+{13, 10, 10, courier24_230_bits},
+{11, 13, 10, courier24_231_bits},
+{11, 15, 15, courier24_232_bits},
+{11, 15, 15, courier24_233_bits},
+{11, 15, 15, courier24_234_bits},
+{11, 15, 15, courier24_235_bits},
+{10, 15, 15, courier24_236_bits},
+{10, 15, 15, courier24_237_bits},
+{10, 15, 15, courier24_238_bits},
+{10, 15, 15, courier24_239_bits},
+{12, 17, 17, courier24_240_bits},
+{13, 15, 15, courier24_241_bits},
+{12, 15, 15, courier24_242_bits},
+{12, 15, 15, courier24_243_bits},
+{12, 15, 15, courier24_244_bits},
+{12, 15, 15, courier24_245_bits},
+{12, 15, 15, courier24_246_bits},
+{11, 11, 13, courier24_247_bits},
+{12, 10, 10, courier24_248_bits},
+{12, 15, 15, courier24_249_bits},
+{12, 15, 15, courier24_250_bits},
+{12, 15, 15, courier24_251_bits},
+{12, 15, 15, courier24_252_bits},
+{13, 19, 15, courier24_253_bits},
+{13, 19, 15, courier24_254_bits},
+{13, 19, 15, courier24_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier8.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier8.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier8.bdf	(revision 22322)
@@ -0,0 +1,2404 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) IBM Corporation 1990,1991. IBM Courier is a Trademark of the IBM Corporation.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -adobe-courier-medium-r-normal--8-77-75-75-m-50-iso8859-1
+SIZE 8 75 75
+FONTBOUNDINGBOX 7 9 -1 -2
+STARTPROPERTIES 25
+FOUNDRY "adobe"
+FAMILY_NAME "courier"
+WEIGHT_NAME "medium"
+SLANT "r"
+SETWIDTH_NAME "normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 8
+POINT_SIZE 77
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "m"
+AVERAGE_WIDTH 50
+CHARSET_REGISTRY "iso8859"
+CHARSET_ENCODING "1"
+FONT "-adobe-courier-medium-r-normal--8-77-75-75-m-50-iso8859-1"
+COPYRIGHT "Copyright (c) IBM Corporation 1990,1991. IBM Courier is a Trademark of the IBM Corporation."
+RAW_PIXEL_SIZE 1000
+RAW_POINT_SIZE 964
+RAW_ASCENT 841
+RAW_DESCENT 288
+RAW_AVERAGE_WIDTH 6000
+FACE_NAME "Couriere."
+DEFAULT_CHAR 0
+FONT_ASCENT 7
+FONT_DESCENT 2
+ENDPROPERTIES
+CHARS 190
+STARTCHAR space
+ENCODING 32
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 0 0 0 0
+ATTRIBUTES 0x0258
+BITMAP
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 4 2 0
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 2 1 2
+ATTRIBUTES 0x0258
+BITMAP
+a0
+a0
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+a0
+a0
+e0
+e0
+a0
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 6 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+e0
+e0
+c0
+e0
+e0
+40
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+e0
+f8
+38
+38
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+80
+a0
+e0
+e0
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 2 2 2
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 6 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 6 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+80
+40
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 3 1 1
+ATTRIBUTES 0x0258
+BITMAP
+40
+40
+a0
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 3 1 1
+ATTRIBUTES 0x0258
+BITMAP
+40
+e0
+40
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 2 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 1 0 2
+ATTRIBUTES 0x0258
+BITMAP
+f0
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 1 2 0
+ATTRIBUTES 0x0258
+BITMAP
+80
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 7 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+20
+20
+20
+40
+40
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+a0
+a0
+a0
+c0
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+10
+30
+40
+f0
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+60
+20
+c0
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+60
+60
+f0
+30
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+80
+e0
+20
+e0
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+80
+e0
+a0
+e0
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+10
+20
+20
+20
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+a0
+c0
+a0
+e0
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+90
+70
+10
+60
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 3 2 0
+ATTRIBUTES 0x0258
+BITMAP
+80
+00
+80
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 4 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+40
+40
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 3 0 1
+ATTRIBUTES 0x0258
+BITMAP
+60
+c0
+20
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 3 0 1
+ATTRIBUTES 0x0258
+BITMAP
+f0
+00
+f0
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 3 0 1
+ATTRIBUTES 0x0258
+BITMAP
+60
+30
+c0
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 4 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+20
+40
+40
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+a8
+e8
+f8
+70
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+50
+70
+d8
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+70
+48
+f0
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+80
+80
+70
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+48
+48
+f0
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+70
+40
+f8
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+e0
+80
+c0
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+80
+98
+78
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+d0
+70
+50
+d0
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 4 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+40
+40
+e0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+38
+10
+90
+f0
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+d8
+60
+50
+c8
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+40
+48
+f8
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 6 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+dc
+d8
+a8
+d8
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+d8
+68
+68
+d8
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+88
+88
+70
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+90
+e0
+c0
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 -1
+ATTRIBUTES 0x0258
+BITMAP
+70
+88
+88
+70
+70
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+60
+50
+d8
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 1 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+c0
+20
+e0
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+20
+20
+70
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+d8
+90
+90
+f0
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+d8
+50
+50
+20
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+d8
+d8
+50
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+d0
+20
+50
+d8
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+50
+20
+70
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+20
+50
+70
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 7 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+c0
+80
+80
+80
+80
+80
+c0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 7 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+40
+40
+40
+20
+20
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 7 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+40
+40
+40
+40
+c0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 3 0 1
+ATTRIBUTES 0x0258
+BITMAP
+60
+e0
+90
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 6 1 -1 -1
+ATTRIBUTES 0x0258
+BITMAP
+fc
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 2 2 2
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+90
+e8
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+f0
+88
+f0
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+80
+70
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+30
+10
+f0
+90
+78
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+a8
+78
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+e0
+80
+80
+e0
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+70
+90
+70
+10
+60
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+70
+50
+d8
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+c0
+40
+e0
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 7 1 -2
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+c0
+40
+40
+40
+c0
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+70
+60
+d8
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+a8
+e8
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+50
+d8
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+88
+70
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+f0
+48
+70
+40
+c0
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+78
+90
+70
+10
+38
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 3 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+80
+e0
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+70
+f0
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 4 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+80
+80
+e0
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+d0
+50
+70
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+d8
+50
+20
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+a8
+b8
+50
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f0
+60
+d8
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+d0
+50
+20
+20
+e0
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 3 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+40
+e0
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 7 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+40
+80
+80
+80
+80
+80
+40
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 7 2 -2
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 7 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+80
+40
+40
+40
+40
+40
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 1 1 2
+ATTRIBUTES 0x0258
+BITMAP
+e0
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 4 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+e0
+c0
+f0
+40
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+80
+e0
+80
+e0
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+a0
+c0
+b0
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+50
+70
+70
+70
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 7 2 -2
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+80
+00
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+e0
+c0
+e0
+20
+e0
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 1 1 4
+ATTRIBUTES 0x0258
+BITMAP
+c0
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+e8
+88
+70
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 2 1 2
+ATTRIBUTES 0x0258
+BITMAP
+c0
+e0
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 3 1 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+c0
+c0
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 2 1 1
+ATTRIBUTES 0x0258
+BITMAP
+e0
+20
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 1 1 2
+ATTRIBUTES 0x0258
+BITMAP
+c0
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+f8
+f8
+70
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 1 1 4
+ATTRIBUTES 0x0258
+BITMAP
+e0
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 3 1 2
+ATTRIBUTES 0x0258
+BITMAP
+e0
+a0
+40
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 4 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+40
+40
+e0
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 3 1 2
+ATTRIBUTES 0x0258
+BITMAP
+e0
+20
+e0
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 3 1 2
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+e0
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 1 1 4
+ATTRIBUTES 0x0258
+BITMAP
+80
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 -1
+ATTRIBUTES 0x0258
+BITMAP
+d0
+50
+70
+40
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+e0
+e0
+60
+60
+60
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 1 2 2
+ATTRIBUTES 0x0258
+BITMAP
+80
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 2 2 -1
+ATTRIBUTES 0x0258
+BITMAP
+80
+80
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 3 1 2
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+c0
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 2 1 2
+ATTRIBUTES 0x0258
+BITMAP
+e0
+e0
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 3 1 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+60
+60
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 -1
+ATTRIBUTES 0x0258
+BITMAP
+c8
+50
+d8
+38
+58
+58
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 -1
+ATTRIBUTES 0x0258
+BITMAP
+c8
+50
+f8
+28
+48
+98
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 -1
+ATTRIBUTES 0x0258
+BITMAP
+c8
+50
+d0
+38
+58
+58
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 4 1 -1
+ATTRIBUTES 0x0258
+BITMAP
+40
+40
+80
+e0
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+70
+50
+70
+d8
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+70
+50
+70
+d8
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+50
+70
+d8
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+30
+00
+70
+50
+70
+d8
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+50
+70
+d8
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 7 0 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+60
+00
+70
+50
+70
+d8
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+30
+78
+f8
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 -1
+ATTRIBUTES 0x0258
+BITMAP
+78
+80
+80
+70
+30
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+f8
+70
+40
+f8
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+f8
+70
+40
+f8
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+f8
+70
+40
+f8
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+f8
+70
+40
+f8
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 6 1 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+00
+e0
+40
+40
+e0
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 6 1 0
+ATTRIBUTES 0x0258
+BITMAP
+80
+00
+e0
+40
+40
+e0
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 6 1 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+00
+e0
+40
+40
+e0
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 6 1 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+00
+e0
+40
+40
+e0
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+f8
+e8
+48
+f0
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+30
+00
+d8
+68
+68
+d8
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+70
+88
+88
+70
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+70
+88
+88
+70
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+88
+88
+70
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+30
+00
+70
+88
+88
+70
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+88
+88
+70
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 3 1 1
+ATTRIBUTES 0x0258
+BITMAP
+a0
+60
+a0
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+a8
+a8
+f0
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+d8
+90
+90
+f0
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+d8
+90
+90
+f0
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+d8
+90
+90
+f0
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+d8
+90
+90
+f0
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+f8
+50
+20
+70
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+90
+e0
+c0
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+e0
+a0
+e0
+a0
+e0
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+70
+90
+e8
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+70
+90
+e8
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+90
+e8
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+30
+00
+70
+90
+e8
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+90
+e8
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+90
+e8
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+70
+f8
+f8
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 4 0 -1
+ATTRIBUTES 0x0258
+BITMAP
+78
+80
+70
+20
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+70
+a8
+78
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+70
+a8
+78
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+a8
+78
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+a8
+78
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+00
+c0
+40
+e0
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+80
+00
+c0
+40
+e0
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+00
+c0
+40
+e0
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+ATTRIBUTES 0x0258
+BITMAP
+c0
+00
+c0
+40
+e0
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+ATTRIBUTES 0x0258
+BITMAP
+20
+20
+10
+f8
+88
+70
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+30
+00
+f0
+50
+d8
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+70
+88
+70
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+70
+88
+70
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+88
+70
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+30
+00
+70
+88
+70
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+70
+88
+70
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 4 2 0
+ATTRIBUTES 0x0258
+BITMAP
+80
+00
+00
+80
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 0
+ATTRIBUTES 0x0258
+BITMAP
+78
+e8
+b0
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+10
+00
+d0
+50
+70
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+d0
+50
+70
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+d0
+50
+70
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+d0
+50
+70
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 7 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+40
+00
+d0
+50
+20
+20
+e0
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 7 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+c0
+40
+70
+48
+70
+40
+c0
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 7 0 -2
+ATTRIBUTES 0x0258
+BITMAP
+60
+00
+d0
+50
+20
+20
+e0
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier8.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier8.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/courier8.h	(revision 22322)
@@ -0,0 +1,769 @@
+static unsigned char courier8_0_bits[] = {
+0x00};
+static unsigned char courier8_1_bits[] = {
+0x00};
+static unsigned char courier8_2_bits[] = {
+0x00};
+static unsigned char courier8_3_bits[] = {
+0x00};
+static unsigned char courier8_4_bits[] = {
+0x00};
+static unsigned char courier8_5_bits[] = {
+0x00};
+static unsigned char courier8_6_bits[] = {
+0x00};
+static unsigned char courier8_7_bits[] = {
+0x00};
+static unsigned char courier8_8_bits[] = {
+0x00};
+static unsigned char courier8_9_bits[] = {
+0x00};
+static unsigned char courier8_10_bits[] = {
+0x00};
+static unsigned char courier8_11_bits[] = {
+0x00};
+static unsigned char courier8_12_bits[] = {
+0x00};
+static unsigned char courier8_13_bits[] = {
+0x00};
+static unsigned char courier8_14_bits[] = {
+0x00};
+static unsigned char courier8_15_bits[] = {
+0x00};
+static unsigned char courier8_16_bits[] = {
+0x00};
+static unsigned char courier8_17_bits[] = {
+0x00};
+static unsigned char courier8_18_bits[] = {
+0x00};
+static unsigned char courier8_19_bits[] = {
+0x00};
+static unsigned char courier8_20_bits[] = {
+0x00};
+static unsigned char courier8_21_bits[] = {
+0x00};
+static unsigned char courier8_22_bits[] = {
+0x00};
+static unsigned char courier8_23_bits[] = {
+0x00};
+static unsigned char courier8_24_bits[] = {
+0x00};
+static unsigned char courier8_25_bits[] = {
+0x00};
+static unsigned char courier8_26_bits[] = {
+0x00};
+static unsigned char courier8_27_bits[] = {
+0x00};
+static unsigned char courier8_28_bits[] = {
+0x00};
+static unsigned char courier8_29_bits[] = {
+0x00};
+static unsigned char courier8_30_bits[] = {
+0x00};
+static unsigned char courier8_31_bits[] = {
+0x00};
+static unsigned char courier8_32_bits[] = {
+0x00};
+static unsigned char courier8_33_bits[] = {
+0x01, 0x01, 0x01, 0x01};
+static unsigned char courier8_34_bits[] = {
+0x05, 0x05};
+static unsigned char courier8_35_bits[] = {
+0x05, 0x05, 0x07, 0x07, 0x05};
+static unsigned char courier8_36_bits[] = {
+0x07, 0x07, 0x03, 0x07, 0x07, 0x02};
+static unsigned char courier8_37_bits[] = {
+0x07, 0x07, 0x1f, 0x1c, 0x1c};
+static unsigned char courier8_38_bits[] = {
+0x02, 0x01, 0x05, 0x07, 0x07};
+static unsigned char courier8_39_bits[] = {
+0x01, 0x01};
+static unsigned char courier8_40_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char courier8_41_bits[] = {
+0x01, 0x02, 0x02, 0x02, 0x02, 0x01};
+static unsigned char courier8_42_bits[] = {
+0x02, 0x02, 0x05};
+static unsigned char courier8_43_bits[] = {
+0x02, 0x07, 0x02};
+static unsigned char courier8_44_bits[] = {
+0x01, 0x01};
+static unsigned char courier8_45_bits[] = {
+0x0f};
+static unsigned char courier8_46_bits[] = {
+0x01};
+static unsigned char courier8_47_bits[] = {
+0x04, 0x04, 0x04, 0x02, 0x02, 0x01, 0x01};
+static unsigned char courier8_48_bits[] = {
+0x03, 0x05, 0x05, 0x05, 0x03};
+static unsigned char courier8_49_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x07};
+static unsigned char courier8_50_bits[] = {
+0x06, 0x08, 0x0c, 0x02, 0x0f};
+static unsigned char courier8_51_bits[] = {
+0x03, 0x02, 0x06, 0x04, 0x03};
+static unsigned char courier8_52_bits[] = {
+0x04, 0x06, 0x06, 0x0f, 0x0c};
+static unsigned char courier8_53_bits[] = {
+0x03, 0x01, 0x07, 0x04, 0x07};
+static unsigned char courier8_54_bits[] = {
+0x06, 0x01, 0x07, 0x05, 0x07};
+static unsigned char courier8_55_bits[] = {
+0x0f, 0x08, 0x04, 0x04, 0x04};
+static unsigned char courier8_56_bits[] = {
+0x07, 0x05, 0x03, 0x05, 0x07};
+static unsigned char courier8_57_bits[] = {
+0x06, 0x09, 0x0e, 0x08, 0x06};
+static unsigned char courier8_58_bits[] = {
+0x01, 0x00, 0x01};
+static unsigned char courier8_59_bits[] = {
+0x02, 0x00, 0x02, 0x02};
+static unsigned char courier8_60_bits[] = {
+0x06, 0x03, 0x04};
+static unsigned char courier8_61_bits[] = {
+0x0f, 0x00, 0x0f};
+static unsigned char courier8_62_bits[] = {
+0x06, 0x0c, 0x03};
+static unsigned char courier8_63_bits[] = {
+0x07, 0x04, 0x02, 0x02};
+static unsigned char courier8_64_bits[] = {
+0x0e, 0x15, 0x17, 0x1f, 0x0e};
+static unsigned char courier8_65_bits[] = {
+0x0e, 0x0a, 0x0e, 0x1b};
+static unsigned char courier8_66_bits[] = {
+0x0f, 0x0e, 0x12, 0x0f};
+static unsigned char courier8_67_bits[] = {
+0x1e, 0x01, 0x01, 0x0e};
+static unsigned char courier8_68_bits[] = {
+0x1f, 0x12, 0x12, 0x0f};
+static unsigned char courier8_69_bits[] = {
+0x1f, 0x0e, 0x02, 0x1f};
+static unsigned char courier8_70_bits[] = {
+0x0f, 0x07, 0x01, 0x03};
+static unsigned char courier8_71_bits[] = {
+0x1e, 0x01, 0x19, 0x1e};
+static unsigned char courier8_72_bits[] = {
+0x0b, 0x0e, 0x0a, 0x0b};
+static unsigned char courier8_73_bits[] = {
+0x07, 0x02, 0x02, 0x07};
+static unsigned char courier8_74_bits[] = {
+0x1c, 0x08, 0x09, 0x0f};
+static unsigned char courier8_75_bits[] = {
+0x1b, 0x06, 0x0a, 0x13};
+static unsigned char courier8_76_bits[] = {
+0x07, 0x02, 0x12, 0x1f};
+static unsigned char courier8_77_bits[] = {
+0x3b, 0x1b, 0x15, 0x1b};
+static unsigned char courier8_78_bits[] = {
+0x1b, 0x16, 0x16, 0x1b};
+static unsigned char courier8_79_bits[] = {
+0x0e, 0x11, 0x11, 0x0e};
+static unsigned char courier8_80_bits[] = {
+0x0f, 0x09, 0x07, 0x03};
+static unsigned char courier8_81_bits[] = {
+0x0e, 0x11, 0x11, 0x0e, 0x0e};
+static unsigned char courier8_82_bits[] = {
+0x0f, 0x06, 0x0a, 0x1b};
+static unsigned char courier8_83_bits[] = {
+0x0f, 0x03, 0x04, 0x07};
+static unsigned char courier8_84_bits[] = {
+0x1f, 0x04, 0x04, 0x0e};
+static unsigned char courier8_85_bits[] = {
+0x1b, 0x09, 0x09, 0x0f};
+static unsigned char courier8_86_bits[] = {
+0x1b, 0x0a, 0x0a, 0x04};
+static unsigned char courier8_87_bits[] = {
+0x1f, 0x1b, 0x1b, 0x0a};
+static unsigned char courier8_88_bits[] = {
+0x0b, 0x04, 0x0a, 0x1b};
+static unsigned char courier8_89_bits[] = {
+0x1f, 0x0a, 0x04, 0x0e};
+static unsigned char courier8_90_bits[] = {
+0x0f, 0x04, 0x0a, 0x0e};
+static unsigned char courier8_91_bits[] = {
+0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03};
+static unsigned char courier8_92_bits[] = {
+0x01, 0x01, 0x02, 0x02, 0x02, 0x04, 0x04};
+static unsigned char courier8_93_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03};
+static unsigned char courier8_94_bits[] = {
+0x06, 0x07, 0x09};
+static unsigned char courier8_95_bits[] = {
+0x3f};
+static unsigned char courier8_96_bits[] = {
+0x01, 0x01};
+static unsigned char courier8_97_bits[] = {
+0x0e, 0x09, 0x17};
+static unsigned char courier8_98_bits[] = {
+0x01, 0x01, 0x0f, 0x11, 0x0f};
+static unsigned char courier8_99_bits[] = {
+0x1e, 0x01, 0x0e};
+static unsigned char courier8_100_bits[] = {
+0x0c, 0x08, 0x0f, 0x09, 0x1e};
+static unsigned char courier8_101_bits[] = {
+0x0e, 0x15, 0x1e};
+static unsigned char courier8_102_bits[] = {
+0x07, 0x07, 0x01, 0x01, 0x07};
+static unsigned char courier8_103_bits[] = {
+0x0e, 0x09, 0x0e, 0x08, 0x06};
+static unsigned char courier8_104_bits[] = {
+0x03, 0x02, 0x0e, 0x0a, 0x1b};
+static unsigned char courier8_105_bits[] = {
+0x02, 0x00, 0x03, 0x02, 0x07};
+static unsigned char courier8_106_bits[] = {
+0x02, 0x00, 0x03, 0x02, 0x02, 0x02, 0x03};
+static unsigned char courier8_107_bits[] = {
+0x03, 0x02, 0x0e, 0x06, 0x1b};
+static unsigned char courier8_108_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x07};
+static unsigned char courier8_109_bits[] = {
+0x1f, 0x15, 0x17};
+static unsigned char courier8_110_bits[] = {
+0x0f, 0x0a, 0x1b};
+static unsigned char courier8_111_bits[] = {
+0x0e, 0x11, 0x0e};
+static unsigned char courier8_112_bits[] = {
+0x0f, 0x12, 0x0e, 0x02, 0x03};
+static unsigned char courier8_113_bits[] = {
+0x1e, 0x09, 0x0e, 0x08, 0x1c};
+static unsigned char courier8_114_bits[] = {
+0x07, 0x01, 0x07};
+static unsigned char courier8_115_bits[] = {
+0x1e, 0x0e, 0x0f};
+static unsigned char courier8_116_bits[] = {
+0x07, 0x01, 0x01, 0x07};
+static unsigned char courier8_117_bits[] = {
+0x0b, 0x0a, 0x0e};
+static unsigned char courier8_118_bits[] = {
+0x1b, 0x0a, 0x04};
+static unsigned char courier8_119_bits[] = {
+0x15, 0x1d, 0x0a};
+static unsigned char courier8_120_bits[] = {
+0x0f, 0x06, 0x1b};
+static unsigned char courier8_121_bits[] = {
+0x0b, 0x0a, 0x04, 0x04, 0x07};
+static unsigned char courier8_122_bits[] = {
+0x07, 0x02, 0x07};
+static unsigned char courier8_123_bits[] = {
+0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02};
+static unsigned char courier8_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char courier8_125_bits[] = {
+0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03};
+static unsigned char courier8_126_bits[] = {
+0x07};
+static unsigned char courier8_127_bits[] = {
+0x00};
+static unsigned char courier8_128_bits[] = {
+0x00};
+static unsigned char courier8_129_bits[] = {
+0x00};
+static unsigned char courier8_130_bits[] = {
+0x00};
+static unsigned char courier8_131_bits[] = {
+0x00};
+static unsigned char courier8_132_bits[] = {
+0x00};
+static unsigned char courier8_133_bits[] = {
+0x00};
+static unsigned char courier8_134_bits[] = {
+0x00};
+static unsigned char courier8_135_bits[] = {
+0x00};
+static unsigned char courier8_136_bits[] = {
+0x00};
+static unsigned char courier8_137_bits[] = {
+0x00};
+static unsigned char courier8_138_bits[] = {
+0x00};
+static unsigned char courier8_139_bits[] = {
+0x00};
+static unsigned char courier8_140_bits[] = {
+0x00};
+static unsigned char courier8_141_bits[] = {
+0x00};
+static unsigned char courier8_142_bits[] = {
+0x00};
+static unsigned char courier8_143_bits[] = {
+0x00};
+static unsigned char courier8_144_bits[] = {
+0x00};
+static unsigned char courier8_145_bits[] = {
+0x00};
+static unsigned char courier8_146_bits[] = {
+0x00};
+static unsigned char courier8_147_bits[] = {
+0x00};
+static unsigned char courier8_148_bits[] = {
+0x00};
+static unsigned char courier8_149_bits[] = {
+0x00};
+static unsigned char courier8_150_bits[] = {
+0x00};
+static unsigned char courier8_151_bits[] = {
+0x00};
+static unsigned char courier8_152_bits[] = {
+0x00};
+static unsigned char courier8_153_bits[] = {
+0x00};
+static unsigned char courier8_154_bits[] = {
+0x00};
+static unsigned char courier8_155_bits[] = {
+0x00};
+static unsigned char courier8_156_bits[] = {
+0x00};
+static unsigned char courier8_157_bits[] = {
+0x00};
+static unsigned char courier8_158_bits[] = {
+0x00};
+static unsigned char courier8_159_bits[] = {
+0x00};
+static unsigned char courier8_160_bits[] = {
+0x00};
+static unsigned char courier8_161_bits[] = {
+0x01, 0x01, 0x01, 0x01};
+static unsigned char courier8_162_bits[] = {
+0x02, 0x07, 0x03, 0x0f, 0x02};
+static unsigned char courier8_163_bits[] = {
+0x06, 0x01, 0x07, 0x01, 0x07};
+static unsigned char courier8_164_bits[] = {
+0x07, 0x05, 0x03, 0x0d};
+static unsigned char courier8_165_bits[] = {
+0x1f, 0x0a, 0x0e, 0x0e, 0x0e};
+static unsigned char courier8_166_bits[] = {
+0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01};
+static unsigned char courier8_167_bits[] = {
+0x07, 0x03, 0x07, 0x04, 0x07};
+static unsigned char courier8_168_bits[] = {
+0x03};
+static unsigned char courier8_169_bits[] = {
+0x0e, 0x17, 0x11, 0x0e};
+static unsigned char courier8_170_bits[] = {
+0x03, 0x07};
+static unsigned char courier8_171_bits[] = {
+0x06, 0x03, 0x03};
+static unsigned char courier8_172_bits[] = {
+0x07, 0x04};
+static unsigned char courier8_173_bits[] = {
+0x03};
+static unsigned char courier8_174_bits[] = {
+0x1e, 0x1f, 0x1f, 0x0e};
+static unsigned char courier8_175_bits[] = {
+0x07};
+static unsigned char courier8_176_bits[] = {
+0x07, 0x05, 0x02};
+static unsigned char courier8_177_bits[] = {
+0x07, 0x02, 0x02, 0x07};
+static unsigned char courier8_178_bits[] = {
+0x07, 0x04, 0x07};
+static unsigned char courier8_179_bits[] = {
+0x03, 0x02, 0x07};
+static unsigned char courier8_180_bits[] = {
+0x01};
+static unsigned char courier8_181_bits[] = {
+0x0b, 0x0a, 0x0e, 0x02};
+static unsigned char courier8_182_bits[] = {
+0x07, 0x07, 0x06, 0x06, 0x06};
+static unsigned char courier8_183_bits[] = {
+0x01};
+static unsigned char courier8_184_bits[] = {
+0x01, 0x01};
+static unsigned char courier8_185_bits[] = {
+0x03, 0x02, 0x03};
+static unsigned char courier8_186_bits[] = {
+0x07, 0x07};
+static unsigned char courier8_187_bits[] = {
+0x03, 0x06, 0x06};
+static unsigned char courier8_188_bits[] = {
+0x13, 0x0a, 0x1b, 0x1c, 0x1a, 0x1a};
+static unsigned char courier8_189_bits[] = {
+0x13, 0x0a, 0x1f, 0x14, 0x12, 0x19};
+static unsigned char courier8_190_bits[] = {
+0x13, 0x0a, 0x0b, 0x1c, 0x1a, 0x1a};
+static unsigned char courier8_191_bits[] = {
+0x02, 0x02, 0x01, 0x07};
+static unsigned char courier8_192_bits[] = {
+0x08, 0x00, 0x0e, 0x0a, 0x0e, 0x1b};
+static unsigned char courier8_193_bits[] = {
+0x02, 0x00, 0x0e, 0x0a, 0x0e, 0x1b};
+static unsigned char courier8_194_bits[] = {
+0x06, 0x00, 0x0e, 0x0a, 0x0e, 0x1b};
+static unsigned char courier8_195_bits[] = {
+0x0c, 0x00, 0x0e, 0x0a, 0x0e, 0x1b};
+static unsigned char courier8_196_bits[] = {
+0x06, 0x00, 0x0e, 0x0a, 0x0e, 0x1b};
+static unsigned char courier8_197_bits[] = {
+0x04, 0x06, 0x00, 0x0e, 0x0a, 0x0e, 0x1b};
+static unsigned char courier8_198_bits[] = {
+0x1e, 0x0c, 0x1e, 0x1f};
+static unsigned char courier8_199_bits[] = {
+0x1e, 0x01, 0x01, 0x0e, 0x0c};
+static unsigned char courier8_200_bits[] = {
+0x08, 0x00, 0x1f, 0x0e, 0x02, 0x1f};
+static unsigned char courier8_201_bits[] = {
+0x02, 0x00, 0x1f, 0x0e, 0x02, 0x1f};
+static unsigned char courier8_202_bits[] = {
+0x06, 0x00, 0x1f, 0x0e, 0x02, 0x1f};
+static unsigned char courier8_203_bits[] = {
+0x06, 0x00, 0x1f, 0x0e, 0x02, 0x1f};
+static unsigned char courier8_204_bits[] = {
+0x04, 0x00, 0x07, 0x02, 0x02, 0x07};
+static unsigned char courier8_205_bits[] = {
+0x01, 0x00, 0x07, 0x02, 0x02, 0x07};
+static unsigned char courier8_206_bits[] = {
+0x03, 0x00, 0x07, 0x02, 0x02, 0x07};
+static unsigned char courier8_207_bits[] = {
+0x03, 0x00, 0x07, 0x02, 0x02, 0x07};
+static unsigned char courier8_208_bits[] = {
+0x1f, 0x17, 0x12, 0x0f};
+static unsigned char courier8_209_bits[] = {
+0x0c, 0x00, 0x1b, 0x16, 0x16, 0x1b};
+static unsigned char courier8_210_bits[] = {
+0x08, 0x00, 0x0e, 0x11, 0x11, 0x0e};
+static unsigned char courier8_211_bits[] = {
+0x02, 0x00, 0x0e, 0x11, 0x11, 0x0e};
+static unsigned char courier8_212_bits[] = {
+0x06, 0x00, 0x0e, 0x11, 0x11, 0x0e};
+static unsigned char courier8_213_bits[] = {
+0x0c, 0x00, 0x0e, 0x11, 0x11, 0x0e};
+static unsigned char courier8_214_bits[] = {
+0x06, 0x00, 0x0e, 0x11, 0x11, 0x0e};
+static unsigned char courier8_215_bits[] = {
+0x05, 0x06, 0x05};
+static unsigned char courier8_216_bits[] = {
+0x1e, 0x15, 0x15, 0x0f};
+static unsigned char courier8_217_bits[] = {
+0x08, 0x00, 0x1b, 0x09, 0x09, 0x0f};
+static unsigned char courier8_218_bits[] = {
+0x02, 0x00, 0x1b, 0x09, 0x09, 0x0f};
+static unsigned char courier8_219_bits[] = {
+0x06, 0x00, 0x1b, 0x09, 0x09, 0x0f};
+static unsigned char courier8_220_bits[] = {
+0x06, 0x00, 0x1b, 0x09, 0x09, 0x0f};
+static unsigned char courier8_221_bits[] = {
+0x02, 0x00, 0x1f, 0x0a, 0x04, 0x0e};
+static unsigned char courier8_222_bits[] = {
+0x07, 0x09, 0x07, 0x03};
+static unsigned char courier8_223_bits[] = {
+0x07, 0x05, 0x07, 0x05, 0x07};
+static unsigned char courier8_224_bits[] = {
+0x08, 0x00, 0x0e, 0x09, 0x17};
+static unsigned char courier8_225_bits[] = {
+0x02, 0x00, 0x0e, 0x09, 0x17};
+static unsigned char courier8_226_bits[] = {
+0x06, 0x00, 0x0e, 0x09, 0x17};
+static unsigned char courier8_227_bits[] = {
+0x0c, 0x00, 0x0e, 0x09, 0x17};
+static unsigned char courier8_228_bits[] = {
+0x06, 0x00, 0x0e, 0x09, 0x17};
+static unsigned char courier8_229_bits[] = {
+0x06, 0x00, 0x0e, 0x09, 0x17};
+static unsigned char courier8_230_bits[] = {
+0x0e, 0x1f, 0x1f};
+static unsigned char courier8_231_bits[] = {
+0x1e, 0x01, 0x0e, 0x04};
+static unsigned char courier8_232_bits[] = {
+0x08, 0x00, 0x0e, 0x15, 0x1e};
+static unsigned char courier8_233_bits[] = {
+0x02, 0x00, 0x0e, 0x15, 0x1e};
+static unsigned char courier8_234_bits[] = {
+0x06, 0x00, 0x0e, 0x15, 0x1e};
+static unsigned char courier8_235_bits[] = {
+0x06, 0x00, 0x0e, 0x15, 0x1e};
+static unsigned char courier8_236_bits[] = {
+0x04, 0x00, 0x03, 0x02, 0x07};
+static unsigned char courier8_237_bits[] = {
+0x01, 0x00, 0x03, 0x02, 0x07};
+static unsigned char courier8_238_bits[] = {
+0x03, 0x00, 0x03, 0x02, 0x07};
+static unsigned char courier8_239_bits[] = {
+0x03, 0x00, 0x03, 0x02, 0x07};
+static unsigned char courier8_240_bits[] = {
+0x04, 0x04, 0x08, 0x1f, 0x11, 0x0e};
+static unsigned char courier8_241_bits[] = {
+0x0c, 0x00, 0x0f, 0x0a, 0x1b};
+static unsigned char courier8_242_bits[] = {
+0x08, 0x00, 0x0e, 0x11, 0x0e};
+static unsigned char courier8_243_bits[] = {
+0x02, 0x00, 0x0e, 0x11, 0x0e};
+static unsigned char courier8_244_bits[] = {
+0x06, 0x00, 0x0e, 0x11, 0x0e};
+static unsigned char courier8_245_bits[] = {
+0x0c, 0x00, 0x0e, 0x11, 0x0e};
+static unsigned char courier8_246_bits[] = {
+0x06, 0x00, 0x0e, 0x11, 0x0e};
+static unsigned char courier8_247_bits[] = {
+0x01, 0x00, 0x00, 0x01};
+static unsigned char courier8_248_bits[] = {
+0x1e, 0x17, 0x0d};
+static unsigned char courier8_249_bits[] = {
+0x08, 0x00, 0x0b, 0x0a, 0x0e};
+static unsigned char courier8_250_bits[] = {
+0x02, 0x00, 0x0b, 0x0a, 0x0e};
+static unsigned char courier8_251_bits[] = {
+0x06, 0x00, 0x0b, 0x0a, 0x0e};
+static unsigned char courier8_252_bits[] = {
+0x06, 0x00, 0x0b, 0x0a, 0x0e};
+static unsigned char courier8_253_bits[] = {
+0x02, 0x00, 0x0b, 0x0a, 0x04, 0x04, 0x07};
+static unsigned char courier8_254_bits[] = {
+0x03, 0x02, 0x0e, 0x12, 0x0e, 0x02, 0x03};
+static unsigned char courier8_255_bits[] = {
+0x06, 0x00, 0x0b, 0x0a, 0x04, 0x04, 0x07};
+static RotFont courier8font[] = {
+{5, 1, 1, courier8_0_bits},
+{5, 1, 1, courier8_1_bits},
+{5, 1, 1, courier8_2_bits},
+{5, 1, 1, courier8_3_bits},
+{5, 1, 1, courier8_4_bits},
+{5, 1, 1, courier8_5_bits},
+{5, 1, 1, courier8_6_bits},
+{5, 1, 1, courier8_7_bits},
+{5, 1, 1, courier8_8_bits},
+{5, 1, 1, courier8_9_bits},
+{5, 1, 1, courier8_10_bits},
+{5, 1, 1, courier8_11_bits},
+{5, 1, 1, courier8_12_bits},
+{5, 1, 1, courier8_13_bits},
+{5, 1, 1, courier8_14_bits},
+{5, 1, 1, courier8_15_bits},
+{5, 1, 1, courier8_16_bits},
+{5, 1, 1, courier8_17_bits},
+{5, 1, 1, courier8_18_bits},
+{5, 1, 1, courier8_19_bits},
+{5, 1, 1, courier8_20_bits},
+{5, 1, 1, courier8_21_bits},
+{5, 1, 1, courier8_22_bits},
+{5, 1, 1, courier8_23_bits},
+{5, 1, 1, courier8_24_bits},
+{5, 1, 1, courier8_25_bits},
+{5, 1, 1, courier8_26_bits},
+{5, 1, 1, courier8_27_bits},
+{5, 1, 1, courier8_28_bits},
+{5, 1, 1, courier8_29_bits},
+{5, 1, 1, courier8_30_bits},
+{5, 1, 1, courier8_31_bits},
+{5, 1, 1, courier8_32_bits},
+{1, 4, 4, courier8_33_bits},
+{3, 2, 4, courier8_34_bits},
+{3, 5, 5, courier8_35_bits},
+{3, 6, 5, courier8_36_bits},
+{5, 5, 5, courier8_37_bits},
+{3, 5, 5, courier8_38_bits},
+{1, 2, 4, courier8_39_bits},
+{2, 6, 5, courier8_40_bits},
+{2, 6, 5, courier8_41_bits},
+{3, 3, 4, courier8_42_bits},
+{3, 3, 4, courier8_43_bits},
+{1, 2, 1, courier8_44_bits},
+{4, 1, 3, courier8_45_bits},
+{1, 1, 1, courier8_46_bits},
+{3, 7, 6, courier8_47_bits},
+{3, 5, 5, courier8_48_bits},
+{3, 5, 5, courier8_49_bits},
+{4, 5, 5, courier8_50_bits},
+{3, 5, 5, courier8_51_bits},
+{4, 5, 5, courier8_52_bits},
+{3, 5, 5, courier8_53_bits},
+{3, 5, 5, courier8_54_bits},
+{4, 5, 5, courier8_55_bits},
+{3, 5, 5, courier8_56_bits},
+{4, 5, 5, courier8_57_bits},
+{1, 3, 3, courier8_58_bits},
+{2, 4, 3, courier8_59_bits},
+{4, 3, 4, courier8_60_bits},
+{4, 3, 4, courier8_61_bits},
+{4, 3, 4, courier8_62_bits},
+{3, 4, 4, courier8_63_bits},
+{5, 5, 5, courier8_64_bits},
+{5, 4, 4, courier8_65_bits},
+{5, 4, 4, courier8_66_bits},
+{5, 4, 4, courier8_67_bits},
+{5, 4, 4, courier8_68_bits},
+{5, 4, 4, courier8_69_bits},
+{4, 4, 4, courier8_70_bits},
+{5, 4, 4, courier8_71_bits},
+{4, 4, 4, courier8_72_bits},
+{3, 4, 4, courier8_73_bits},
+{5, 4, 4, courier8_74_bits},
+{5, 4, 4, courier8_75_bits},
+{5, 4, 4, courier8_76_bits},
+{6, 4, 4, courier8_77_bits},
+{5, 4, 4, courier8_78_bits},
+{5, 4, 4, courier8_79_bits},
+{4, 4, 4, courier8_80_bits},
+{5, 5, 4, courier8_81_bits},
+{5, 4, 4, courier8_82_bits},
+{4, 4, 4, courier8_83_bits},
+{5, 4, 4, courier8_84_bits},
+{5, 4, 4, courier8_85_bits},
+{5, 4, 4, courier8_86_bits},
+{5, 4, 4, courier8_87_bits},
+{5, 4, 4, courier8_88_bits},
+{5, 4, 4, courier8_89_bits},
+{4, 4, 4, courier8_90_bits},
+{2, 7, 6, courier8_91_bits},
+{3, 7, 6, courier8_92_bits},
+{2, 7, 6, courier8_93_bits},
+{4, 3, 4, courier8_94_bits},
+{6, 1, 0, courier8_95_bits},
+{1, 2, 4, courier8_96_bits},
+{5, 3, 3, courier8_97_bits},
+{5, 5, 5, courier8_98_bits},
+{5, 3, 3, courier8_99_bits},
+{5, 5, 5, courier8_100_bits},
+{5, 3, 3, courier8_101_bits},
+{3, 5, 5, courier8_102_bits},
+{4, 5, 3, courier8_103_bits},
+{5, 5, 5, courier8_104_bits},
+{3, 5, 5, courier8_105_bits},
+{2, 7, 5, courier8_106_bits},
+{5, 5, 5, courier8_107_bits},
+{3, 5, 5, courier8_108_bits},
+{5, 3, 3, courier8_109_bits},
+{5, 3, 3, courier8_110_bits},
+{5, 3, 3, courier8_111_bits},
+{5, 5, 3, courier8_112_bits},
+{5, 5, 3, courier8_113_bits},
+{3, 3, 3, courier8_114_bits},
+{5, 3, 3, courier8_115_bits},
+{3, 4, 4, courier8_116_bits},
+{4, 3, 3, courier8_117_bits},
+{5, 3, 3, courier8_118_bits},
+{5, 3, 3, courier8_119_bits},
+{5, 3, 3, courier8_120_bits},
+{4, 5, 3, courier8_121_bits},
+{3, 3, 3, courier8_122_bits},
+{2, 7, 6, courier8_123_bits},
+{1, 7, 5, courier8_124_bits},
+{2, 7, 6, courier8_125_bits},
+{3, 1, 3, courier8_126_bits},
+{5, 1, 1, courier8_127_bits},
+{5, 1, 1, courier8_128_bits},
+{5, 1, 1, courier8_129_bits},
+{5, 1, 1, courier8_130_bits},
+{5, 1, 1, courier8_131_bits},
+{5, 1, 1, courier8_132_bits},
+{5, 1, 1, courier8_133_bits},
+{5, 1, 1, courier8_134_bits},
+{5, 1, 1, courier8_135_bits},
+{5, 1, 1, courier8_136_bits},
+{5, 1, 1, courier8_137_bits},
+{5, 1, 1, courier8_138_bits},
+{5, 1, 1, courier8_139_bits},
+{5, 1, 1, courier8_140_bits},
+{5, 1, 1, courier8_141_bits},
+{5, 1, 1, courier8_142_bits},
+{5, 1, 1, courier8_143_bits},
+{5, 1, 1, courier8_144_bits},
+{5, 1, 1, courier8_145_bits},
+{5, 1, 1, courier8_146_bits},
+{5, 1, 1, courier8_147_bits},
+{5, 1, 1, courier8_148_bits},
+{5, 1, 1, courier8_149_bits},
+{5, 1, 1, courier8_150_bits},
+{5, 1, 1, courier8_151_bits},
+{5, 1, 1, courier8_152_bits},
+{5, 1, 1, courier8_153_bits},
+{5, 1, 1, courier8_154_bits},
+{5, 1, 1, courier8_155_bits},
+{5, 1, 1, courier8_156_bits},
+{5, 1, 1, courier8_157_bits},
+{5, 1, 1, courier8_158_bits},
+{5, 1, 1, courier8_159_bits},
+{5, 1, 1, courier8_160_bits},
+{1, 4, 3, courier8_161_bits},
+{4, 5, 5, courier8_162_bits},
+{3, 5, 5, courier8_163_bits},
+{4, 4, 4, courier8_164_bits},
+{5, 5, 5, courier8_165_bits},
+{1, 7, 5, courier8_166_bits},
+{3, 5, 4, courier8_167_bits},
+{2, 1, 5, courier8_168_bits},
+{5, 4, 4, courier8_169_bits},
+{3, 2, 4, courier8_170_bits},
+{3, 3, 3, courier8_171_bits},
+{3, 2, 3, courier8_172_bits},
+{2, 1, 3, courier8_173_bits},
+{5, 4, 4, courier8_174_bits},
+{3, 1, 5, courier8_175_bits},
+{3, 3, 5, courier8_176_bits},
+{3, 4, 4, courier8_177_bits},
+{3, 3, 5, courier8_178_bits},
+{3, 3, 5, courier8_179_bits},
+{2, 1, 5, courier8_180_bits},
+{4, 4, 3, courier8_181_bits},
+{3, 5, 4, courier8_182_bits},
+{1, 1, 3, courier8_183_bits},
+{1, 2, 1, courier8_184_bits},
+{2, 3, 5, courier8_185_bits},
+{3, 2, 4, courier8_186_bits},
+{3, 3, 3, courier8_187_bits},
+{5, 6, 5, courier8_188_bits},
+{5, 6, 5, courier8_189_bits},
+{5, 6, 5, courier8_190_bits},
+{3, 4, 3, courier8_191_bits},
+{5, 6, 6, courier8_192_bits},
+{5, 6, 6, courier8_193_bits},
+{5, 6, 6, courier8_194_bits},
+{5, 6, 6, courier8_195_bits},
+{5, 6, 6, courier8_196_bits},
+{5, 7, 7, courier8_197_bits},
+{5, 4, 4, courier8_198_bits},
+{5, 5, 4, courier8_199_bits},
+{5, 6, 6, courier8_200_bits},
+{5, 6, 6, courier8_201_bits},
+{5, 6, 6, courier8_202_bits},
+{5, 6, 6, courier8_203_bits},
+{3, 6, 6, courier8_204_bits},
+{3, 6, 6, courier8_205_bits},
+{3, 6, 6, courier8_206_bits},
+{3, 6, 6, courier8_207_bits},
+{5, 4, 4, courier8_208_bits},
+{5, 6, 6, courier8_209_bits},
+{5, 6, 6, courier8_210_bits},
+{5, 6, 6, courier8_211_bits},
+{5, 6, 6, courier8_212_bits},
+{5, 6, 6, courier8_213_bits},
+{5, 6, 6, courier8_214_bits},
+{3, 3, 4, courier8_215_bits},
+{5, 4, 4, courier8_216_bits},
+{5, 6, 6, courier8_217_bits},
+{5, 6, 6, courier8_218_bits},
+{5, 6, 6, courier8_219_bits},
+{5, 6, 6, courier8_220_bits},
+{5, 6, 6, courier8_221_bits},
+{4, 4, 4, courier8_222_bits},
+{3, 5, 5, courier8_223_bits},
+{5, 5, 5, courier8_224_bits},
+{5, 5, 5, courier8_225_bits},
+{5, 5, 5, courier8_226_bits},
+{5, 5, 5, courier8_227_bits},
+{5, 5, 5, courier8_228_bits},
+{5, 5, 5, courier8_229_bits},
+{5, 3, 3, courier8_230_bits},
+{5, 4, 3, courier8_231_bits},
+{5, 5, 5, courier8_232_bits},
+{5, 5, 5, courier8_233_bits},
+{5, 5, 5, courier8_234_bits},
+{5, 5, 5, courier8_235_bits},
+{3, 5, 5, courier8_236_bits},
+{3, 5, 5, courier8_237_bits},
+{3, 5, 5, courier8_238_bits},
+{3, 5, 5, courier8_239_bits},
+{5, 6, 6, courier8_240_bits},
+{5, 5, 5, courier8_241_bits},
+{5, 5, 5, courier8_242_bits},
+{5, 5, 5, courier8_243_bits},
+{5, 5, 5, courier8_244_bits},
+{5, 5, 5, courier8_245_bits},
+{5, 5, 5, courier8_246_bits},
+{1, 4, 4, courier8_247_bits},
+{5, 3, 3, courier8_248_bits},
+{4, 5, 5, courier8_249_bits},
+{4, 5, 5, courier8_250_bits},
+{4, 5, 5, courier8_251_bits},
+{4, 5, 5, courier8_252_bits},
+{4, 7, 5, courier8_253_bits},
+{5, 7, 5, courier8_254_bits},
+{4, 7, 5, courier8_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/fixed.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/fixed.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/fixed.h	(revision 22322)
@@ -0,0 +1,765 @@
+static unsigned char fixed_0_bits[] = {};
+static unsigned char fixed_1_bits[] = {
+0x4, 0xe, 0xe, 0x1f, 0xe, 0xe, 0x04};
+static unsigned char fixed_2_bits[] = {
+0x15, 0xa, 0x15, 0xa, 0x15, 0xa, 0x15};
+static unsigned char fixed_3_bits[] = {
+0x9, 0x9, 0xf, 0x9, 0x9, 0x1e, 0x8, 0x8, 0x08};
+static unsigned char fixed_4_bits[] = {
+0x7, 0x1, 0x3, 0x1, 0x1d, 0x4, 0xc, 0x4, 0x04};
+static unsigned char fixed_5_bits[] = {
+0xe, 0x1, 0x1, 0xe, 0xe, 0x12, 0xe, 0x12, 0x12};
+static unsigned char fixed_6_bits[] = {
+0x1, 0x1, 0x1, 0xf, 0x1e, 0x2, 0xe, 0x2, 0x02};
+static unsigned char fixed_7_bits[] = {
+0x2, 0x5, 0x02};
+static unsigned char fixed_8_bits[] = {
+0x4, 0x4, 0x1f, 0x4, 0x4, 0x1f};
+static unsigned char fixed_9_bits[] = {
+0x9, 0xb, 0xb, 0xd, 0x9, 0x2, 0x2, 0x2, 0x1e};
+static unsigned char fixed_10_bits[] = {
+0x9, 0x9, 0x6, 0x2, 0x1e, 0x8, 0x8, 0x8, 0x08};
+static unsigned char fixed_11_bits[] = {
+0x8, 0x8, 0x8, 0x8, 0x0f};
+static unsigned char fixed_12_bits[] = {
+0xf, 0x8, 0x8, 0x8, 0x8, 0x08};
+static unsigned char fixed_13_bits[] = {
+0x7, 0x1, 0x1, 0x1, 0x1, 0x01};
+static unsigned char fixed_14_bits[] = {
+0x1, 0x1, 0x1, 0x1, 0x07};
+static unsigned char fixed_15_bits[] = {
+0x8, 0x8, 0x8, 0x8, 0x3f, 0x8, 0x8, 0x8, 0x8, 0x08};
+static unsigned char fixed_16_bits[] = {
+0x3f};
+static unsigned char fixed_17_bits[] = {
+0x3f};
+static unsigned char fixed_18_bits[] = {
+0x3f};
+static unsigned char fixed_19_bits[] = {
+0x3f};
+static unsigned char fixed_20_bits[] = {
+0x3f};
+static unsigned char fixed_21_bits[] = {
+0x1, 0x1, 0x1, 0x1, 0x7, 0x1, 0x1, 0x1, 0x1, 0x01};
+static unsigned char fixed_22_bits[] = {
+0x8, 0x8, 0x8, 0x8, 0xf, 0x8, 0x8, 0x8, 0x8, 0x08};
+static unsigned char fixed_23_bits[] = {
+0x8, 0x8, 0x8, 0x8, 0x3f};
+static unsigned char fixed_24_bits[] = {
+0x3f, 0x8, 0x8, 0x8, 0x8, 0x08};
+static unsigned char fixed_25_bits[] = {
+0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x01};
+static unsigned char fixed_26_bits[] = {
+0x10, 0x8, 0x2, 0x1, 0x2, 0x8, 0x10, 0x1f};
+static unsigned char fixed_27_bits[] = {
+0x1, 0x2, 0x8, 0x10, 0x8, 0x2, 0x1, 0x1f};
+static unsigned char fixed_28_bits[] = {
+0x1f, 0xa, 0xa, 0xa, 0x0a};
+static unsigned char fixed_29_bits[] = {
+0x10, 0x8, 0x1f, 0x4, 0x1f, 0x2, 0x01};
+static unsigned char fixed_30_bits[] = {
+0xc, 0x12, 0x2, 0x7, 0x2, 0x12, 0x0d};
+static unsigned char fixed_31_bits[] = {
+0x01};
+static unsigned char fixed_32_bits[] = {};
+static unsigned char fixed_33_bits[] = {
+0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x01};
+static unsigned char fixed_34_bits[] = {
+0x5, 0x5, 0x05};
+static unsigned char fixed_35_bits[] = {
+0xa, 0xa, 0x1f, 0xa, 0x1f, 0xa, 0x0a};
+static unsigned char fixed_36_bits[] = {
+0x4, 0xe, 0x5, 0xe, 0x14, 0xe, 0x04};
+static unsigned char fixed_37_bits[] = {
+0x12, 0x15, 0xa, 0x4, 0xa, 0x15, 0x09};
+static unsigned char fixed_38_bits[] = {
+0x2, 0x5, 0x5, 0x2, 0x15, 0x9, 0x16};
+static unsigned char fixed_39_bits[] = {
+0x6, 0x2, 0x01};
+static unsigned char fixed_40_bits[] = {
+0x4, 0x2, 0x1, 0x1, 0x1, 0x2, 0x04};
+static unsigned char fixed_41_bits[] = {
+0x1, 0x2, 0x4, 0x4, 0x4, 0x2, 0x01};
+static unsigned char fixed_42_bits[] = {
+0x11, 0xa, 0x1f, 0xa, 0x11};
+static unsigned char fixed_43_bits[] = {
+0x4, 0x4, 0x1f, 0x4, 0x04};
+static unsigned char fixed_44_bits[] = {
+0x6, 0x2, 0x01};
+static unsigned char fixed_45_bits[] = {
+0x1f};
+static unsigned char fixed_46_bits[] = {
+0x2, 0x7, 0x02};
+static unsigned char fixed_47_bits[] = {
+0x10, 0x10, 0x8, 0x4, 0x2, 0x1, 0x01};
+static unsigned char fixed_48_bits[] = {
+0x4, 0xa, 0x11, 0x11, 0x11, 0xa, 0x04};
+static unsigned char fixed_49_bits[] = {
+0x4, 0x6, 0x5, 0x4, 0x4, 0x4, 0x1f};
+static unsigned char fixed_50_bits[] = {
+0xe, 0x11, 0x10, 0xc, 0x2, 0x1, 0x1f};
+static unsigned char fixed_51_bits[] = {
+0x1f, 0x10, 0x8, 0xc, 0x10, 0x11, 0x0e};
+static unsigned char fixed_52_bits[] = {
+0x8, 0xc, 0xa, 0x9, 0x1f, 0x8, 0x08};
+static unsigned char fixed_53_bits[] = {
+0x1f, 0x1, 0xd, 0x13, 0x10, 0x11, 0x0e};
+static unsigned char fixed_54_bits[] = {
+0xc, 0x2, 0x1, 0xd, 0x13, 0x11, 0x0e};
+static unsigned char fixed_55_bits[] = {
+0x1f, 0x10, 0x8, 0x8, 0x4, 0x2, 0x02};
+static unsigned char fixed_56_bits[] = {
+0xe, 0x11, 0x11, 0xe, 0x11, 0x11, 0x0e};
+static unsigned char fixed_57_bits[] = {
+0xe, 0x11, 0x19, 0x16, 0x10, 0x8, 0x06};
+static unsigned char fixed_58_bits[] = {
+0x2, 0x7, 0x2, 0x0, 0x2, 0x7, 0x02};
+static unsigned char fixed_59_bits[] = {
+0x2, 0x7, 0x2, 0x0, 0x6, 0x2, 0x01};
+static unsigned char fixed_60_bits[] = {
+0x8, 0x4, 0x2, 0x1, 0x2, 0x4, 0x08};
+static unsigned char fixed_61_bits[] = {
+0x1f, 0x0, 0x1f};
+static unsigned char fixed_62_bits[] = {
+0x1, 0x2, 0x4, 0x8, 0x4, 0x2, 0x01};
+static unsigned char fixed_63_bits[] = {
+0xe, 0x11, 0x8, 0x4, 0x4, 0x0, 0x04};
+static unsigned char fixed_64_bits[] = {
+0xe, 0x11, 0x19, 0x15, 0xd, 0x1, 0x0e};
+static unsigned char fixed_65_bits[] = {
+0x4, 0xa, 0x11, 0x11, 0x1f, 0x11, 0x11};
+static unsigned char fixed_66_bits[] = {
+0xf, 0x12, 0x12, 0xe, 0x12, 0x12, 0x0f};
+static unsigned char fixed_67_bits[] = {
+0xe, 0x11, 0x1, 0x1, 0x1, 0x11, 0x0e};
+static unsigned char fixed_68_bits[] = {
+0xf, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0f};
+static unsigned char fixed_69_bits[] = {
+0x1f, 0x1, 0x1, 0xf, 0x1, 0x1, 0x1f};
+static unsigned char fixed_70_bits[] = {
+0x1f, 0x1, 0x1, 0xf, 0x1, 0x1, 0x01};
+static unsigned char fixed_71_bits[] = {
+0xe, 0x11, 0x1, 0x1, 0x19, 0x11, 0x0e};
+static unsigned char fixed_72_bits[] = {
+0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11};
+static unsigned char fixed_73_bits[] = {
+0x7, 0x2, 0x2, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_74_bits[] = {
+0x1c, 0x10, 0x10, 0x10, 0x10, 0x11, 0x0e};
+static unsigned char fixed_75_bits[] = {
+0x11, 0x9, 0x5, 0x3, 0x5, 0x9, 0x11};
+static unsigned char fixed_76_bits[] = {
+0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f};
+static unsigned char fixed_77_bits[] = {
+0x11, 0x11, 0x1b, 0x15, 0x11, 0x11, 0x11};
+static unsigned char fixed_78_bits[] = {
+0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x11};
+static unsigned char fixed_79_bits[] = {
+0xe, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_80_bits[] = {
+0xf, 0x11, 0x11, 0xf, 0x1, 0x1, 0x01};
+static unsigned char fixed_81_bits[] = {
+0xe, 0x11, 0x11, 0x11, 0x11, 0x15, 0xe, 0x10};
+static unsigned char fixed_82_bits[] = {
+0xf, 0x11, 0x11, 0xf, 0x5, 0x9, 0x11};
+static unsigned char fixed_83_bits[] = {
+0xe, 0x11, 0x1, 0xe, 0x10, 0x11, 0x0e};
+static unsigned char fixed_84_bits[] = {
+0x1f, 0x4, 0x4, 0x4, 0x4, 0x4, 0x04};
+static unsigned char fixed_85_bits[] = {
+0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_86_bits[] = {
+0x11, 0x11, 0x11, 0xa, 0xa, 0xa, 0x04};
+static unsigned char fixed_87_bits[] = {
+0x11, 0x11, 0x11, 0x15, 0x15, 0x1b, 0x11};
+static unsigned char fixed_88_bits[] = {
+0x11, 0x11, 0xa, 0x4, 0xa, 0x11, 0x11};
+static unsigned char fixed_89_bits[] = {
+0x11, 0x11, 0xa, 0x4, 0x4, 0x4, 0x04};
+static unsigned char fixed_90_bits[] = {
+0x1f, 0x10, 0x8, 0x4, 0x2, 0x1, 0x1f};
+static unsigned char fixed_91_bits[] = {
+0x7, 0x1, 0x1, 0x1, 0x1, 0x1, 0x07};
+static unsigned char fixed_92_bits[] = {
+0x1, 0x1, 0x2, 0x4, 0x8, 0x10, 0x10};
+static unsigned char fixed_93_bits[] = {
+0x7, 0x4, 0x4, 0x4, 0x4, 0x4, 0x07};
+static unsigned char fixed_94_bits[] = {
+0x4, 0xa, 0x11};
+static unsigned char fixed_95_bits[] = {
+0x1f};
+static unsigned char fixed_96_bits[] = {
+0x3, 0x2, 0x04};
+static unsigned char fixed_97_bits[] = {
+0xe, 0x10, 0x1e, 0x11, 0x1e};
+static unsigned char fixed_98_bits[] = {
+0x1, 0x1, 0xd, 0x13, 0x11, 0x13, 0x0d};
+static unsigned char fixed_99_bits[] = {
+0xe, 0x11, 0x1, 0x11, 0x0e};
+static unsigned char fixed_100_bits[] = {
+0x10, 0x10, 0x16, 0x19, 0x11, 0x19, 0x16};
+static unsigned char fixed_101_bits[] = {
+0xe, 0x11, 0x1f, 0x1, 0x0e};
+static unsigned char fixed_102_bits[] = {
+0xc, 0x12, 0x2, 0xf, 0x2, 0x2, 0x02};
+static unsigned char fixed_103_bits[] = {
+0x16, 0x9, 0x6, 0x1, 0xe, 0x11, 0x0e};
+static unsigned char fixed_104_bits[] = {
+0x1, 0x1, 0xd, 0x13, 0x11, 0x11, 0x11};
+static unsigned char fixed_105_bits[] = {
+0x2, 0x0, 0x3, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_106_bits[] = {
+0x8, 0x0, 0xc, 0x8, 0x8, 0x8, 0x9, 0x9, 0x06};
+static unsigned char fixed_107_bits[] = {
+0x1, 0x1, 0x11, 0x9, 0x7, 0x9, 0x11};
+static unsigned char fixed_108_bits[] = {
+0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_109_bits[] = {
+0xb, 0x15, 0x15, 0x15, 0x11};
+static unsigned char fixed_110_bits[] = {
+0xd, 0x13, 0x11, 0x11, 0x11};
+static unsigned char fixed_111_bits[] = {
+0xe, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_112_bits[] = {
+0xd, 0x13, 0x13, 0xd, 0x1, 0x1, 0x01};
+static unsigned char fixed_113_bits[] = {
+0x16, 0x19, 0x19, 0x16, 0x10, 0x10, 0x10};
+static unsigned char fixed_114_bits[] = {
+0xd, 0x13, 0x1, 0x1, 0x01};
+static unsigned char fixed_115_bits[] = {
+0xe, 0x1, 0xe, 0x10, 0x0f};
+static unsigned char fixed_116_bits[] = {
+0x2, 0x2, 0xf, 0x2, 0x2, 0x12, 0x0c};
+static unsigned char fixed_117_bits[] = {
+0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char fixed_118_bits[] = {
+0x11, 0x11, 0xa, 0xa, 0x04};
+static unsigned char fixed_119_bits[] = {
+0x11, 0x11, 0x15, 0x15, 0x0a};
+static unsigned char fixed_120_bits[] = {
+0x11, 0xa, 0x4, 0xa, 0x11};
+static unsigned char fixed_121_bits[] = {
+0x11, 0x11, 0x19, 0x16, 0x10, 0x11, 0x0e};
+static unsigned char fixed_122_bits[] = {
+0x1f, 0x8, 0x4, 0x2, 0x1f};
+static unsigned char fixed_123_bits[] = {
+0xc, 0x2, 0x4, 0x3, 0x4, 0x2, 0x0c};
+static unsigned char fixed_124_bits[] = {
+0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x01};
+static unsigned char fixed_125_bits[] = {
+0x3, 0x4, 0x2, 0xc, 0x2, 0x4, 0x03};
+static unsigned char fixed_126_bits[] = {
+0x12, 0x15, 0x09};
+static unsigned char fixed_127_bits[] = {};
+static unsigned char fixed_128_bits[] = {
+0x00};
+static unsigned char fixed_129_bits[] = {
+0x00};
+static unsigned char fixed_130_bits[] = {
+0x00};
+static unsigned char fixed_131_bits[] = {
+0x00};
+static unsigned char fixed_132_bits[] = {
+0x00};
+static unsigned char fixed_133_bits[] = {
+0x00};
+static unsigned char fixed_134_bits[] = {
+0x00};
+static unsigned char fixed_135_bits[] = {
+0x00};
+static unsigned char fixed_136_bits[] = {
+0x00};
+static unsigned char fixed_137_bits[] = {
+0x00};
+static unsigned char fixed_138_bits[] = {
+0x00};
+static unsigned char fixed_139_bits[] = {
+0x00};
+static unsigned char fixed_140_bits[] = {
+0x00};
+static unsigned char fixed_141_bits[] = {
+0x00};
+static unsigned char fixed_142_bits[] = {
+0x00};
+static unsigned char fixed_143_bits[] = {
+0x00};
+static unsigned char fixed_144_bits[] = {
+0x00};
+static unsigned char fixed_145_bits[] = {
+0x00};
+static unsigned char fixed_146_bits[] = {
+0x00};
+static unsigned char fixed_147_bits[] = {
+0x00};
+static unsigned char fixed_148_bits[] = {
+0x00};
+static unsigned char fixed_149_bits[] = {
+0x00};
+static unsigned char fixed_150_bits[] = {
+0x00};
+static unsigned char fixed_151_bits[] = {
+0x00};
+static unsigned char fixed_152_bits[] = {
+0x00};
+static unsigned char fixed_153_bits[] = {
+0x00};
+static unsigned char fixed_154_bits[] = {
+0x00};
+static unsigned char fixed_155_bits[] = {
+0x00};
+static unsigned char fixed_156_bits[] = {
+0x00};
+static unsigned char fixed_157_bits[] = {
+0x00};
+static unsigned char fixed_158_bits[] = {
+0x00};
+static unsigned char fixed_159_bits[] = {
+0x00};
+static unsigned char fixed_160_bits[] = {};
+static unsigned char fixed_161_bits[] = {
+0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x01};
+static unsigned char fixed_162_bits[] = {
+0x4, 0x1e, 0x5, 0x5, 0x5, 0x1e, 0x04};
+static unsigned char fixed_163_bits[] = {
+0xc, 0x12, 0x2, 0x7, 0x2, 0x12, 0x0d};
+static unsigned char fixed_164_bits[] = {
+0x11, 0xe, 0xa, 0xe, 0x11};
+static unsigned char fixed_165_bits[] = {
+0x11, 0x11, 0xa, 0x4, 0x1f, 0x4, 0x4, 0x04};
+static unsigned char fixed_166_bits[] = {
+0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x01};
+static unsigned char fixed_167_bits[] = {
+0xe, 0x1, 0x7, 0x9, 0x12, 0x1c, 0x10, 0x0e};
+static unsigned char fixed_168_bits[] = {
+0x05};
+static unsigned char fixed_169_bits[] = {
+0xe, 0x11, 0x15, 0x13, 0x15, 0x11, 0x0e};
+static unsigned char fixed_170_bits[] = {
+0xe, 0x9, 0xd, 0xa, 0x0, 0x0f};
+static unsigned char fixed_171_bits[] = {
+0x24, 0x12, 0x9, 0x12, 0x24};
+static unsigned char fixed_172_bits[] = {
+0xf, 0x08};
+static unsigned char fixed_173_bits[] = {
+0x1f};
+static unsigned char fixed_174_bits[] = {
+0xe, 0x11, 0x17, 0x13, 0x13, 0x11, 0x0e};
+static unsigned char fixed_175_bits[] = {
+0x1f};
+static unsigned char fixed_176_bits[] = {
+0x2, 0x5, 0x02};
+static unsigned char fixed_177_bits[] = {
+0x4, 0x4, 0x1f, 0x4, 0x4, 0x1f};
+static unsigned char fixed_178_bits[] = {
+0x6, 0x9, 0x4, 0x2, 0x0f};
+static unsigned char fixed_179_bits[] = {
+0x7, 0x8, 0x6, 0x8, 0x07};
+static unsigned char fixed_180_bits[] = {
+0x6, 0x03};
+static unsigned char fixed_181_bits[] = {
+0x11, 0x11, 0x11, 0x13, 0xd, 0x01};
+static unsigned char fixed_182_bits[] = {
+0x1e, 0x17, 0x17, 0x16, 0x14, 0x14, 0x14};
+static unsigned char fixed_183_bits[] = {
+0x01};
+static unsigned char fixed_184_bits[] = {
+0x2, 0x01};
+static unsigned char fixed_185_bits[] = {
+0x2, 0x3, 0x2, 0x2, 0x07};
+static unsigned char fixed_186_bits[] = {
+0x6, 0x9, 0x9, 0x6, 0x0, 0x0f};
+static unsigned char fixed_187_bits[] = {
+0x9, 0x12, 0x24, 0x12, 0x09};
+static unsigned char fixed_188_bits[] = {
+0x2, 0x3, 0x2, 0x2, 0x27, 0x30, 0x28, 0x3c, 0x20};
+static unsigned char fixed_189_bits[] = {
+0x2, 0x3, 0x2, 0x2, 0x17, 0x28, 0x20, 0x10, 0x38};
+static unsigned char fixed_190_bits[] = {
+0x3, 0x4, 0x2, 0x4, 0x13, 0x18, 0x14, 0x1e, 0x10};
+static unsigned char fixed_191_bits[] = {
+0x4, 0x0, 0x4, 0x4, 0x2, 0x11, 0x0e};
+static unsigned char fixed_192_bits[] = {
+0x2, 0x4, 0xe, 0x11, 0x11, 0x1f, 0x11, 0x11};
+static unsigned char fixed_193_bits[] = {
+0x8, 0x4, 0xe, 0x11, 0x11, 0x1f, 0x11, 0x11};
+static unsigned char fixed_194_bits[] = {
+0x4, 0xa, 0xe, 0x11, 0x11, 0x1f, 0x11, 0x11};
+static unsigned char fixed_195_bits[] = {
+0x12, 0xd, 0xe, 0x11, 0x11, 0x1f, 0x11, 0x11};
+static unsigned char fixed_196_bits[] = {
+0xa, 0x0, 0xe, 0x11, 0x11, 0x1f, 0x11, 0x11};
+static unsigned char fixed_197_bits[] = {
+0x4, 0xa, 0xe, 0x11, 0x11, 0x1f, 0x11, 0x11};
+static unsigned char fixed_198_bits[] = {
+0x3c, 0xa, 0x9, 0x39, 0xf, 0x9, 0x39};
+static unsigned char fixed_199_bits[] = {
+0xe, 0x11, 0x1, 0x1, 0x1, 0x11, 0xe, 0x4, 0x02};
+static unsigned char fixed_200_bits[] = {
+0x2, 0x1f, 0x1, 0x1, 0xf, 0x1, 0x1, 0x1f};
+static unsigned char fixed_201_bits[] = {
+0x8, 0x1f, 0x1, 0x1, 0xf, 0x1, 0x1, 0x1f};
+static unsigned char fixed_202_bits[] = {
+0x4, 0x1f, 0x1, 0x1, 0xf, 0x1, 0x1, 0x1f};
+static unsigned char fixed_203_bits[] = {
+0xa, 0x1f, 0x1, 0x1, 0xf, 0x1, 0x1, 0x1f};
+static unsigned char fixed_204_bits[] = {
+0x1, 0x7, 0x2, 0x2, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_205_bits[] = {
+0x4, 0x7, 0x2, 0x2, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_206_bits[] = {
+0x2, 0x7, 0x2, 0x2, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_207_bits[] = {
+0x5, 0x7, 0x2, 0x2, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_208_bits[] = {
+0xf, 0x12, 0x12, 0x17, 0x12, 0x12, 0x0f};
+static unsigned char fixed_209_bits[] = {
+0xc, 0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x11};
+static unsigned char fixed_210_bits[] = {
+0x2, 0xe, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_211_bits[] = {
+0x8, 0xe, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_212_bits[] = {
+0x4, 0xe, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_213_bits[] = {
+0xe, 0xe, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_214_bits[] = {
+0xa, 0xe, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_215_bits[] = {
+0x11, 0xa, 0x4, 0xa, 0x11};
+static unsigned char fixed_216_bits[] = {
+0xe, 0x19, 0x19, 0x15, 0x13, 0x13, 0x0e};
+static unsigned char fixed_217_bits[] = {
+0x2, 0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_218_bits[] = {
+0x8, 0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_219_bits[] = {
+0x4, 0x1b, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_220_bits[] = {
+0xa, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_221_bits[] = {
+0x8, 0x15, 0x11, 0xa, 0x4, 0x4, 0x4, 0x04};
+static unsigned char fixed_222_bits[] = {
+0x1, 0xf, 0x11, 0xf, 0x1, 0x1, 0x01};
+static unsigned char fixed_223_bits[] = {
+0xe, 0x11, 0x11, 0xf, 0x11, 0x11, 0xf, 0x01};
+static unsigned char fixed_224_bits[] = {
+0x2, 0x4, 0xe, 0x10, 0x1e, 0x11, 0x1e};
+static unsigned char fixed_225_bits[] = {
+0x8, 0x4, 0xe, 0x10, 0x1e, 0x11, 0x1e};
+static unsigned char fixed_226_bits[] = {
+0x4, 0xa, 0xe, 0x10, 0x1e, 0x11, 0x1e};
+static unsigned char fixed_227_bits[] = {
+0x14, 0xa, 0xe, 0x10, 0x1e, 0x11, 0x1e};
+static unsigned char fixed_228_bits[] = {
+0xa, 0x0, 0xe, 0x10, 0x1e, 0x11, 0x1e};
+static unsigned char fixed_229_bits[] = {
+0x4, 0xa, 0x4, 0xe, 0x10, 0x1e, 0x11, 0x1e};
+static unsigned char fixed_230_bits[] = {
+0x1e, 0x28, 0x3e, 0x9, 0x3e};
+static unsigned char fixed_231_bits[] = {
+0xe, 0x11, 0x1, 0x11, 0xe, 0x4, 0x02};
+static unsigned char fixed_232_bits[] = {
+0x2, 0x4, 0xe, 0x11, 0x1f, 0x1, 0x0e};
+static unsigned char fixed_233_bits[] = {
+0x8, 0x4, 0xe, 0x11, 0x1f, 0x1, 0x0e};
+static unsigned char fixed_234_bits[] = {
+0x4, 0xa, 0xe, 0x11, 0x1f, 0x1, 0x0e};
+static unsigned char fixed_235_bits[] = {
+0xa, 0x0, 0xe, 0x11, 0x1f, 0x1, 0x0e};
+static unsigned char fixed_236_bits[] = {
+0x1, 0x2, 0x0, 0x3, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_237_bits[] = {
+0x2, 0x1, 0x0, 0x3, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_238_bits[] = {
+0x2, 0x5, 0x0, 0x3, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_239_bits[] = {
+0x5, 0x0, 0x3, 0x2, 0x2, 0x2, 0x07};
+static unsigned char fixed_240_bits[] = {
+0x3, 0xc, 0xe, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_241_bits[] = {
+0x14, 0xa, 0xd, 0x13, 0x11, 0x11, 0x11};
+static unsigned char fixed_242_bits[] = {
+0x2, 0x4, 0xe, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_243_bits[] = {
+0x8, 0x4, 0xe, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_244_bits[] = {
+0x4, 0xa, 0xe, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_245_bits[] = {
+0x14, 0xa, 0xe, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_246_bits[] = {
+0xa, 0x0, 0xe, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char fixed_247_bits[] = {
+0x4, 0x0, 0x1f, 0x0, 0x04};
+static unsigned char fixed_248_bits[] = {
+0x1e, 0x19, 0x15, 0x13, 0x0f};
+static unsigned char fixed_249_bits[] = {
+0x2, 0x4, 0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char fixed_250_bits[] = {
+0x8, 0x4, 0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char fixed_251_bits[] = {
+0x4, 0xa, 0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char fixed_252_bits[] = {
+0xa, 0x0, 0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char fixed_253_bits[] = {
+0x8, 0x4, 0x11, 0x11, 0x19, 0x16, 0x10, 0x11, 0x0e};
+static unsigned char fixed_254_bits[] = {
+0x1, 0xf, 0x11, 0x11, 0x11, 0xf, 0x1, 0x01};
+static unsigned char fixed_255_bits[] = {
+0xa, 0x0, 0x11, 0x11, 0x19, 0x16, 0x10, 0x11, 0x0e};
+static RotFont fixedfont[] = {
+{0, 0, 0, fixed_0_bits},
+{5, 7, 7, fixed_1_bits},
+{5, 7, 7, fixed_2_bits},
+{5, 9, 7, fixed_3_bits},
+{5, 9, 7, fixed_4_bits},
+{5, 9, 7, fixed_5_bits},
+{5, 9, 7, fixed_6_bits},
+{3, 3, 7, fixed_7_bits},
+{5, 6, 6, fixed_8_bits},
+{5, 9, 7, fixed_9_bits},
+{5, 9, 7, fixed_10_bits},
+{4, 5, 8, fixed_11_bits},
+{4, 6, 4, fixed_12_bits},
+{3, 6, 4, fixed_13_bits},
+{3, 5, 8, fixed_14_bits},
+{6, 10, 8, fixed_15_bits},
+{6, 1, 8, fixed_16_bits},
+{6, 1, 6, fixed_17_bits},
+{6, 1, 4, fixed_18_bits},
+{6, 1, 2, fixed_19_bits},
+{6, 1, 0, fixed_20_bits},
+{3, 10, 8, fixed_21_bits},
+{4, 10, 8, fixed_22_bits},
+{6, 5, 8, fixed_23_bits},
+{6, 6, 4, fixed_24_bits},
+{1, 10, 8, fixed_25_bits},
+{5, 8, 7, fixed_26_bits},
+{5, 8, 7, fixed_27_bits},
+{5, 5, 5, fixed_28_bits},
+{5, 7, 7, fixed_29_bits},
+{5, 7, 7, fixed_30_bits},
+{1, 1, 4, fixed_31_bits},
+{0, 0, 0, fixed_32_bits},
+{1, 7, 7, fixed_33_bits},
+{3, 3, 7, fixed_34_bits},
+{5, 7, 7, fixed_35_bits},
+{5, 7, 7, fixed_36_bits},
+{5, 7, 7, fixed_37_bits},
+{5, 7, 7, fixed_38_bits},
+{3, 3, 7, fixed_39_bits},
+{3, 7, 7, fixed_40_bits},
+{3, 7, 7, fixed_41_bits},
+{5, 5, 6, fixed_42_bits},
+{5, 5, 6, fixed_43_bits},
+{3, 3, 2, fixed_44_bits},
+{5, 1, 4, fixed_45_bits},
+{3, 3, 2, fixed_46_bits},
+{5, 7, 7, fixed_47_bits},
+{5, 7, 7, fixed_48_bits},
+{5, 7, 7, fixed_49_bits},
+{5, 7, 7, fixed_50_bits},
+{5, 7, 7, fixed_51_bits},
+{5, 7, 7, fixed_52_bits},
+{5, 7, 7, fixed_53_bits},
+{5, 7, 7, fixed_54_bits},
+{5, 7, 7, fixed_55_bits},
+{5, 7, 7, fixed_56_bits},
+{5, 7, 7, fixed_57_bits},
+{3, 7, 6, fixed_58_bits},
+{3, 7, 6, fixed_59_bits},
+{4, 7, 7, fixed_60_bits},
+{5, 3, 5, fixed_61_bits},
+{4, 7, 7, fixed_62_bits},
+{5, 7, 7, fixed_63_bits},
+{5, 7, 7, fixed_64_bits},
+{5, 7, 7, fixed_65_bits},
+{5, 7, 7, fixed_66_bits},
+{5, 7, 7, fixed_67_bits},
+{5, 7, 7, fixed_68_bits},
+{5, 7, 7, fixed_69_bits},
+{5, 7, 7, fixed_70_bits},
+{5, 7, 7, fixed_71_bits},
+{5, 7, 7, fixed_72_bits},
+{3, 7, 7, fixed_73_bits},
+{5, 7, 7, fixed_74_bits},
+{5, 7, 7, fixed_75_bits},
+{5, 7, 7, fixed_76_bits},
+{5, 7, 7, fixed_77_bits},
+{5, 7, 7, fixed_78_bits},
+{5, 7, 7, fixed_79_bits},
+{5, 7, 7, fixed_80_bits},
+{5, 8, 7, fixed_81_bits},
+{5, 7, 7, fixed_82_bits},
+{5, 7, 7, fixed_83_bits},
+{5, 7, 7, fixed_84_bits},
+{5, 7, 7, fixed_85_bits},
+{5, 7, 7, fixed_86_bits},
+{5, 7, 7, fixed_87_bits},
+{5, 7, 7, fixed_88_bits},
+{5, 7, 7, fixed_89_bits},
+{5, 7, 7, fixed_90_bits},
+{3, 7, 7, fixed_91_bits},
+{5, 7, 7, fixed_92_bits},
+{3, 7, 7, fixed_93_bits},
+{5, 3, 7, fixed_94_bits},
+{5, 1, 0, fixed_95_bits},
+{3, 3, 7, fixed_96_bits},
+{5, 5, 5, fixed_97_bits},
+{5, 7, 7, fixed_98_bits},
+{5, 5, 5, fixed_99_bits},
+{5, 7, 7, fixed_100_bits},
+{5, 5, 5, fixed_101_bits},
+{5, 7, 7, fixed_102_bits},
+{5, 7, 5, fixed_103_bits},
+{5, 7, 7, fixed_104_bits},
+{3, 7, 7, fixed_105_bits},
+{4, 9, 7, fixed_106_bits},
+{5, 7, 7, fixed_107_bits},
+{3, 7, 7, fixed_108_bits},
+{5, 5, 5, fixed_109_bits},
+{5, 5, 5, fixed_110_bits},
+{5, 5, 5, fixed_111_bits},
+{5, 7, 5, fixed_112_bits},
+{5, 7, 5, fixed_113_bits},
+{5, 5, 5, fixed_114_bits},
+{5, 5, 5, fixed_115_bits},
+{5, 7, 7, fixed_116_bits},
+{5, 5, 5, fixed_117_bits},
+{5, 5, 5, fixed_118_bits},
+{5, 5, 5, fixed_119_bits},
+{5, 5, 5, fixed_120_bits},
+{5, 7, 5, fixed_121_bits},
+{5, 5, 5, fixed_122_bits},
+{4, 7, 7, fixed_123_bits},
+{1, 7, 7, fixed_124_bits},
+{4, 7, 7, fixed_125_bits},
+{5, 3, 7, fixed_126_bits},
+{0, 0, 0, fixed_127_bits},
+{5, 1, 1, fixed_128_bits},
+{5, 1, 1, fixed_129_bits},
+{5, 1, 1, fixed_130_bits},
+{5, 1, 1, fixed_131_bits},
+{5, 1, 1, fixed_132_bits},
+{5, 1, 1, fixed_133_bits},
+{5, 1, 1, fixed_134_bits},
+{5, 1, 1, fixed_135_bits},
+{5, 1, 1, fixed_136_bits},
+{5, 1, 1, fixed_137_bits},
+{5, 1, 1, fixed_138_bits},
+{5, 1, 1, fixed_139_bits},
+{5, 1, 1, fixed_140_bits},
+{5, 1, 1, fixed_141_bits},
+{5, 1, 1, fixed_142_bits},
+{5, 1, 1, fixed_143_bits},
+{5, 1, 1, fixed_144_bits},
+{5, 1, 1, fixed_145_bits},
+{5, 1, 1, fixed_146_bits},
+{5, 1, 1, fixed_147_bits},
+{5, 1, 1, fixed_148_bits},
+{5, 1, 1, fixed_149_bits},
+{5, 1, 1, fixed_150_bits},
+{5, 1, 1, fixed_151_bits},
+{5, 1, 1, fixed_152_bits},
+{5, 1, 1, fixed_153_bits},
+{5, 1, 1, fixed_154_bits},
+{5, 1, 1, fixed_155_bits},
+{5, 1, 1, fixed_156_bits},
+{5, 1, 1, fixed_157_bits},
+{5, 1, 1, fixed_158_bits},
+{5, 1, 1, fixed_159_bits},
+{0, 0, 0, fixed_160_bits},
+{1, 7, 7, fixed_161_bits},
+{5, 7, 6, fixed_162_bits},
+{5, 7, 7, fixed_163_bits},
+{5, 5, 5, fixed_164_bits},
+{5, 8, 7, fixed_165_bits},
+{1, 7, 7, fixed_166_bits},
+{5, 8, 7, fixed_167_bits},
+{3, 1, 8, fixed_168_bits},
+{5, 7, 7, fixed_169_bits},
+{4, 6, 7, fixed_170_bits},
+{6, 5, 5, fixed_171_bits},
+{4, 2, 4, fixed_172_bits},
+{5, 1, 4, fixed_173_bits},
+{5, 7, 7, fixed_174_bits},
+{5, 1, 8, fixed_175_bits},
+{3, 3, 7, fixed_176_bits},
+{5, 6, 6, fixed_177_bits},
+{4, 5, 8, fixed_178_bits},
+{4, 5, 8, fixed_179_bits},
+{3, 2, 8, fixed_180_bits},
+{5, 6, 5, fixed_181_bits},
+{5, 7, 7, fixed_182_bits},
+{1, 1, 4, fixed_183_bits},
+{2, 2, 0, fixed_184_bits},
+{3, 5, 8, fixed_185_bits},
+{4, 6, 7, fixed_186_bits},
+{6, 5, 5, fixed_187_bits},
+{6, 9, 8, fixed_188_bits},
+{6, 9, 8, fixed_189_bits},
+{5, 9, 8, fixed_190_bits},
+{5, 7, 7, fixed_191_bits},
+{5, 8, 8, fixed_192_bits},
+{5, 8, 8, fixed_193_bits},
+{5, 8, 8, fixed_194_bits},
+{5, 8, 8, fixed_195_bits},
+{5, 8, 8, fixed_196_bits},
+{5, 8, 8, fixed_197_bits},
+{6, 7, 7, fixed_198_bits},
+{5, 9, 7, fixed_199_bits},
+{5, 8, 8, fixed_200_bits},
+{5, 8, 8, fixed_201_bits},
+{5, 8, 8, fixed_202_bits},
+{5, 8, 8, fixed_203_bits},
+{3, 8, 8, fixed_204_bits},
+{3, 8, 8, fixed_205_bits},
+{3, 8, 8, fixed_206_bits},
+{3, 8, 8, fixed_207_bits},
+{5, 7, 7, fixed_208_bits},
+{5, 8, 8, fixed_209_bits},
+{5, 8, 8, fixed_210_bits},
+{5, 8, 8, fixed_211_bits},
+{5, 8, 8, fixed_212_bits},
+{5, 8, 8, fixed_213_bits},
+{5, 8, 8, fixed_214_bits},
+{5, 5, 5, fixed_215_bits},
+{5, 7, 7, fixed_216_bits},
+{5, 8, 8, fixed_217_bits},
+{5, 8, 8, fixed_218_bits},
+{5, 8, 8, fixed_219_bits},
+{5, 8, 8, fixed_220_bits},
+{5, 8, 8, fixed_221_bits},
+{5, 7, 7, fixed_222_bits},
+{5, 8, 7, fixed_223_bits},
+{5, 7, 7, fixed_224_bits},
+{5, 7, 7, fixed_225_bits},
+{5, 7, 7, fixed_226_bits},
+{5, 7, 7, fixed_227_bits},
+{5, 7, 7, fixed_228_bits},
+{5, 8, 8, fixed_229_bits},
+{6, 5, 5, fixed_230_bits},
+{5, 7, 5, fixed_231_bits},
+{5, 7, 7, fixed_232_bits},
+{5, 7, 7, fixed_233_bits},
+{5, 7, 7, fixed_234_bits},
+{5, 7, 7, fixed_235_bits},
+{3, 8, 8, fixed_236_bits},
+{3, 8, 8, fixed_237_bits},
+{3, 8, 8, fixed_238_bits},
+{3, 7, 7, fixed_239_bits},
+{5, 7, 7, fixed_240_bits},
+{5, 7, 7, fixed_241_bits},
+{5, 7, 7, fixed_242_bits},
+{5, 7, 7, fixed_243_bits},
+{5, 7, 7, fixed_244_bits},
+{5, 7, 7, fixed_245_bits},
+{5, 7, 7, fixed_246_bits},
+{5, 5, 6, fixed_247_bits},
+{5, 5, 5, fixed_248_bits},
+{5, 7, 7, fixed_249_bits},
+{5, 7, 7, fixed_250_bits},
+{5, 7, 7, fixed_251_bits},
+{5, 7, 7, fixed_252_bits},
+{5, 9, 7, fixed_253_bits},
+{5, 8, 6, fixed_254_bits},
+{5, 9, 7, fixed_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/fixfont.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/fixfont.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/fixfont.c	(revision 22322)
@@ -0,0 +1,180 @@
+# include <stdio.h>
+# include <math.h>
+# define TRUE 1
+# define FALSE 0
+
+# define ALLOCATE(X,T,S)  \
+  X=(T *)malloc((unsigned) ((S)*sizeof(T)));\
+  if(X==NULL) \
+    { \
+      fprintf(stderr,"failed to malloc X\n");\
+        exit(0);\
+    } 
+# define REALLOCATE(X,T,S) \
+  X=(T *)realloc(X,(unsigned) ((S)*sizeof(T))); \
+  if(X==NULL) \
+    { \
+       fprintf(stderr,"failed to realloc X\n"); \
+       exit(0); \
+    }
+
+#define blank_width 5
+#define blank_height 1
+static unsigned char blank_bits[] = {0x00};
+
+typedef struct {
+  int dx, dy, ascent, Nb;
+  unsigned char *bits;
+  char name[64];
+} RotFont;
+
+char flip_bits (char a);
+int scan_line (FILE *f, char *line);
+
+main (int argc, char **argv) {
+
+  int BitMap, Nvalue, code;
+  unsigned int bits;
+  int i, j, dx, dy, ddx, ddy;
+  char buffer[1000], name[1000], glyph[1000];
+  unsigned char value[1000], bitchar[3];
+  RotFont font[256];
+  
+  FILE *f;
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: fixfont (file.bdf) (name)\n");
+    exit (0);
+  }
+
+  f = fopen (argv[1], "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "failed to open file %s\n", argv[1]);
+    exit (0);
+  }
+
+  for (i = 0; i < 256; i++) {
+    font[i].bits = 0;
+  }
+
+  BitMap = FALSE;
+  Nvalue = 0;
+  while (scan_line (f, buffer) != EOF) {
+    sscanf (buffer, "%s", name);
+    if (!strcmp (name, "ENDCHAR")) {
+      BitMap = FALSE;
+      if ((code >= 0) && (code < 256)) {
+	font[code].dx = dx;
+	font[code].dy = dy;
+	font[code].ascent = dy + ddy;
+	font[code].Nb = Nvalue;
+	ALLOCATE (font[code].bits, unsigned char, Nvalue);
+	strcpy (font[code].name, glyph);
+	for (i = 0; i < Nvalue; i++) { 
+	  font[code].bits[i] = value[i];
+	}
+      }
+      fprintf (stderr, "found %s\n", glyph);
+    }
+    if (BitMap) {
+      bitchar[2] = 0;
+      for (j = 0; buffer[j] != 0; j+=2) {
+	bitchar[0] = buffer[j];
+	bitchar[1] = buffer[j+1];
+	sscanf (bitchar, "%x", &bits);
+	value[Nvalue] = flip_bits (bits);
+	Nvalue ++;
+      }
+      continue;
+    } 
+    if (!strcmp (name, "STARTCHAR")) {
+      sscanf (buffer, "%*s %s", glyph);
+    }
+    if (!strcmp (name, "ENCODING")) {
+      sscanf (buffer, "%*s %d", &code);
+    }
+    if (!strcmp (name, "BBX")) {
+      sscanf (buffer, "%*s %d %d %d %d", &dx, &dy, &ddx, &ddy);
+    }
+    if (!strcmp (name, "BITMAP")) {
+      BitMap = TRUE;
+      Nvalue = 0;
+    }
+  }    
+
+  for (i = 0; i < 256; i++) {
+    if (font[i].bits  == 0) {
+      font[i].bits = blank_bits;
+      font[i].dx = blank_width;
+      font[i].dy = blank_height;
+      font[i].Nb = 1;
+      font[i].ascent = blank_height;
+      strcpy (font[i].name, "blank");
+    }
+    fprintf (stdout, "static unsigned char %s_%d_bits[] = {", argv[2], i);
+    for (j = 0; j < font[i].Nb; j++) {
+      if (!(j % 12)) fprintf (stdout, "\n");
+      if (j == font[i].Nb - 1) fprintf (stdout, "0x%02x", font[i].bits[j]);
+      else fprintf (stdout, "0x%02x, ", font[i].bits[j]);
+    }
+    fprintf (stdout, "};\n");
+  }
+  
+  fprintf (stdout, "static RotFont %sfont[] = {\n", argv[2]);
+  for (i = 0; i < 255; i++) {
+    fprintf (stdout, "{%d, %d, %d, %s_%d_bits},\n", 
+	     font[i].dx, font[i].dy, font[i].ascent, argv[2], i);
+  }
+  fprintf (stdout, "{%d, %d, %d, %s_%d_bits}};\n", 
+	   font[i].dx, font[i].dy, font[i].ascent, argv[2], i);
+
+      
+}
+
+int scan_line (FILE *f, char *line) {
+
+  int i, status;
+  char c;
+  
+  status = EOF + 1;
+  
+  for (i = 0, c = 0; (c != '\n') && (status != EOF); i++) {
+    status = fscanf (f, "%c", &c);
+    line[i] = c;
+  }
+  line[i - 1] = 0;  /* this could make things crash! */
+
+  if (i > 1) {
+    status = EOF + 1;
+  }
+
+  return (status);
+
+}
+
+char flip_bits (char a) {
+
+  char b, c;
+  
+  c = 0;
+  b = (a & 0x01) << 7;
+  c = c | b;
+  b = (a & 0x02) << 5;
+  c = c | b;
+  b = (a & 0x04) << 3;
+  c = c | b;
+  b = (a & 0x08) << 1;
+  c = c | b;
+  b = (a & 0x10) >> 1;
+  c = c | b;
+  b = (a & 0x20) >> 3;
+  c = c | b;
+  b = (a & 0x40) >> 5;
+  c = c | b;
+  b = (a & 0x80) >> 7;
+  c = c | b;
+
+  return (c);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica12.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica12.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica12.bdf	(revision 22322)
@@ -0,0 +1,3020 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Helvetica-Medium-R-Normal--12-120-75-75-P-67-ISO8859-1
+SIZE 12 75 75
+FONTBOUNDINGBOX 11 15 0 -3
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Helvetica"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 12
+POINT_SIZE 120
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 67
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 9
+X_HEIGHT 7
+FACE_NAME "Helvetica"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.  "
+_DEC_DEVICE_FONTNAMES "PS=Helvetica"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2, 07-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Helvetica"
+FONT "-Adobe-Helvetica-Medium-R-Normal--12-120-75-75-P-67-ISO8859-1"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 7
+DEFAULT_CHAR 32
+FONT_ASCENT 11
+FONT_DESCENT 3
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 9 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+00
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 3 1 6
+BITMAP
+a0
+a0
+a0
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 8 0 0
+BITMAP
+28
+28
+fc
+28
+fc
+50
+50
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 -1
+BITMAP
+20
+70
+a8
+a0
+70
+28
+a8
+a8
+70
+20
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+6200
+9400
+9400
+6800
+0800
+1300
+1480
+1480
+2300
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+30
+48
+48
+30
+50
+8a
+84
+8c
+72
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 3 1 6
+BITMAP
+c0
+40
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 12 1 -3
+BITMAP
+20
+40
+40
+80
+80
+80
+80
+80
+80
+40
+40
+20
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 12 0 -3
+BITMAP
+80
+40
+40
+20
+20
+20
+20
+20
+20
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 3 1 6
+BITMAP
+a0
+40
+a0
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+20
+20
+f8
+20
+20
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 2 3 1 -2
+BITMAP
+40
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 5 1 1 3
+BITMAP
+f8
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 1 1 0
+BITMAP
+80
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 9 0 0
+BITMAP
+10
+10
+20
+20
+40
+40
+40
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+70
+88
+88
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 3 9 1 0
+BITMAP
+20
+e0
+20
+20
+20
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+70
+88
+08
+10
+20
+40
+80
+80
+f8
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+70
+88
+08
+30
+08
+08
+88
+88
+70
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 0 0
+BITMAP
+08
+18
+28
+28
+48
+88
+fc
+08
+08
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+f8
+80
+80
+f0
+08
+08
+88
+88
+70
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+70
+88
+80
+b0
+c8
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+f8
+08
+10
+10
+20
+20
+20
+40
+40
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+70
+88
+88
+70
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+70
+88
+88
+88
+78
+08
+08
+88
+70
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 6 1 0
+BITMAP
+80
+00
+00
+00
+00
+80
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 8 0 -2
+BITMAP
+40
+00
+00
+00
+00
+40
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 0 1
+BITMAP
+0c
+30
+c0
+30
+0c
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 3 1 2
+BITMAP
+f8
+00
+f8
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 1 1
+BITMAP
+c0
+30
+0c
+30
+c0
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+70
+88
+88
+10
+10
+20
+20
+00
+20
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 960 0
+DWIDTH 12 0
+BBX 10 10 1 -1
+BITMAP
+1f00
+6080
+4d40
+9240
+a240
+a240
+a680
+9b00
+4000
+3e00
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+10
+28
+28
+44
+44
+7c
+82
+82
+82
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+f8
+84
+84
+84
+f8
+84
+84
+84
+f8
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+3c
+42
+80
+80
+80
+80
+80
+42
+3c
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+f8
+84
+82
+82
+82
+82
+82
+84
+f8
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+fc
+80
+80
+80
+fc
+80
+80
+80
+fc
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+fc
+80
+80
+80
+f8
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+3c
+42
+80
+80
+8e
+82
+82
+46
+3a
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+82
+82
+82
+82
+fe
+82
+82
+82
+82
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 9 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+08
+08
+08
+08
+08
+08
+88
+88
+70
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 1 0
+BITMAP
+84
+88
+90
+a0
+e0
+90
+88
+84
+82
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+f8
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+8080
+c180
+c180
+a280
+a280
+9480
+9480
+8880
+8880
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+82
+c2
+a2
+a2
+92
+8a
+8a
+86
+82
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+f8
+84
+84
+84
+f8
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+3c
+42
+81
+81
+81
+89
+85
+42
+3d
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+f8
+84
+84
+84
+f8
+88
+84
+84
+84
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+78
+84
+80
+60
+18
+04
+84
+84
+78
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+BITMAP
+fe
+10
+10
+10
+10
+10
+10
+10
+10
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+84
+84
+84
+84
+84
+84
+84
+84
+78
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+82
+82
+44
+44
+44
+28
+28
+10
+10
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+8880
+8880
+8880
+4900
+5500
+5500
+2200
+2200
+2200
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+82
+44
+44
+28
+10
+28
+44
+44
+82
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+82
+82
+44
+44
+28
+10
+10
+10
+10
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+fe
+02
+04
+08
+10
+20
+40
+80
+fe
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 12 1 -3
+BITMAP
+c0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+c0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 9 0 0
+BITMAP
+80
+80
+40
+40
+20
+20
+20
+10
+10
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 12 0 -3
+BITMAP
+c0
+40
+40
+40
+40
+40
+40
+40
+40
+40
+40
+c0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 3 0 5
+BITMAP
+20
+50
+88
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 1 0 -2
+BITMAP
+fe
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 3 0 6
+BITMAP
+40
+80
+c0
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 0
+BITMAP
+70
+88
+08
+78
+88
+88
+74
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+80
+80
+b0
+c8
+88
+88
+88
+c8
+b0
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+70
+88
+80
+80
+80
+88
+70
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+08
+08
+68
+98
+88
+88
+88
+98
+68
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+70
+88
+88
+f8
+80
+88
+70
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 4 9 0 0
+BITMAP
+30
+40
+e0
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 -3
+BITMAP
+68
+98
+88
+88
+88
+98
+68
+08
+88
+70
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+80
+80
+b0
+c8
+88
+88
+88
+88
+88
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 9 1 0
+BITMAP
+80
+00
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 12 0 -3
+BITMAP
+40
+00
+40
+40
+40
+40
+40
+40
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+80
+80
+90
+a0
+c0
+c0
+a0
+90
+88
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 9 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 7 1 0
+BITMAP
+a4
+da
+92
+92
+92
+92
+92
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+b0
+c8
+88
+88
+88
+88
+88
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+70
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 -3
+BITMAP
+b0
+c8
+88
+88
+88
+c8
+b0
+80
+80
+80
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 -3
+BITMAP
+68
+98
+88
+88
+88
+98
+68
+08
+08
+08
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 7 1 0
+BITMAP
+a0
+c0
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 7 1 0
+BITMAP
+60
+90
+80
+60
+10
+90
+60
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 9 0 0
+BITMAP
+40
+40
+e0
+40
+40
+40
+40
+40
+60
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+88
+88
+88
+88
+88
+98
+68
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+88
+88
+88
+50
+50
+20
+20
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 7 0 0
+BITMAP
+8880
+8880
+4900
+4900
+5500
+2200
+2200
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 7 0 0
+BITMAP
+84
+48
+30
+30
+48
+84
+84
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 -3
+BITMAP
+88
+88
+88
+90
+50
+50
+20
+20
+40
+80
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 7 1 0
+BITMAP
+f0
+10
+20
+40
+40
+80
+f0
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 12 0 -3
+BITMAP
+30
+40
+40
+40
+40
+80
+40
+40
+40
+40
+40
+30
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 12 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 12 0 -3
+BITMAP
+c0
+20
+20
+20
+20
+10
+20
+20
+20
+20
+20
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 2 0 3
+BITMAP
+64
+98
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 10 1 -3
+BITMAP
+80
+00
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 -1
+BITMAP
+10
+70
+a8
+a0
+a0
+a0
+c8
+70
+40
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+30
+48
+40
+40
+f0
+20
+20
+48
+b0
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 6 0 1
+BITMAP
+84
+78
+48
+48
+78
+84
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+88
+88
+50
+20
+f8
+20
+f8
+20
+20
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 11 1 -2
+BITMAP
+80
+80
+80
+80
+00
+00
+00
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 12 0 -3
+BITMAP
+70
+88
+80
+60
+90
+88
+88
+48
+30
+08
+88
+70
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 1 0 8
+BITMAP
+a0
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+3e00
+4100
+9c80
+a280
+a080
+a280
+9c80
+4100
+3e00
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 5 1 4
+BITMAP
+e0
+20
+a0
+00
+e0
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+28
+50
+a0
+50
+28
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 4 1 2
+BITMAP
+fc
+04
+04
+04
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 1 0 3
+BITMAP
+f0
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+3e00
+4100
+9c80
+9480
+9880
+9480
+9480
+4100
+3e00
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 1 0 8
+BITMAP
+f0
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 4 0 4
+BITMAP
+60
+90
+90
+60
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+20
+20
+f8
+20
+20
+00
+f8
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 5 0 3
+BITMAP
+60
+90
+20
+40
+f0
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 5 0 3
+BITMAP
+e0
+20
+40
+20
+c0
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 160 0
+DWIDTH 2 0
+BBX 2 2 0 8
+BITMAP
+40
+80
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 -3
+BITMAP
+88
+88
+88
+88
+88
+98
+e8
+80
+80
+80
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 12 0 -3
+BITMAP
+3c
+68
+e8
+e8
+e8
+68
+28
+28
+28
+28
+28
+28
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 1 1 3
+BITMAP
+80
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 4 0 -3
+BITMAP
+40
+20
+20
+c0
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 2 5 1 3
+BITMAP
+40
+c0
+40
+40
+40
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 5 1 4
+BITMAP
+e0
+a0
+e0
+00
+e0
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+a0
+50
+28
+50
+a0
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+4100
+c200
+4400
+4400
+4900
+1300
+1500
+2780
+4100
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+4100
+c200
+4400
+4800
+4b00
+1480
+1100
+2200
+4780
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+e100
+2200
+4400
+2400
+c900
+0b00
+1500
+1780
+2100
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 -3
+BITMAP
+20
+00
+20
+20
+40
+40
+88
+88
+70
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+20
+10
+00
+10
+10
+28
+44
+44
+7c
+82
+82
+82
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+08
+10
+00
+10
+10
+28
+44
+44
+7c
+82
+82
+82
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+10
+28
+00
+10
+10
+28
+44
+44
+7c
+82
+82
+82
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+14
+28
+00
+10
+10
+28
+44
+44
+7c
+82
+82
+82
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 11 1 0
+BITMAP
+28
+00
+10
+10
+28
+44
+44
+7c
+82
+82
+82
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+10
+28
+10
+10
+10
+28
+44
+44
+7c
+82
+82
+82
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+1f80
+2800
+2800
+4800
+4f80
+7800
+8800
+8800
+8f80
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 12 1 -3
+BITMAP
+3c
+42
+80
+80
+80
+80
+80
+42
+3c
+08
+08
+30
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 12 1 0
+BITMAP
+20
+10
+00
+fc
+80
+80
+80
+fc
+80
+80
+80
+fc
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 12 1 0
+BITMAP
+08
+10
+00
+fc
+80
+80
+80
+fc
+80
+80
+80
+fc
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 12 1 0
+BITMAP
+10
+28
+00
+fc
+80
+80
+80
+fc
+80
+80
+80
+fc
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+28
+00
+fc
+80
+80
+80
+fc
+80
+80
+80
+fc
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 12 0 0
+BITMAP
+80
+40
+00
+40
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 12 1 0
+BITMAP
+40
+80
+00
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 12 0 0
+BITMAP
+40
+a0
+00
+40
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 11 0 0
+BITMAP
+a0
+00
+40
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+7c
+42
+41
+41
+f1
+41
+41
+42
+7c
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+14
+28
+00
+82
+c2
+a2
+a2
+92
+8a
+8a
+86
+82
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 12 1 0
+BITMAP
+10
+08
+00
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 12 1 0
+BITMAP
+04
+08
+00
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 12 1 0
+BITMAP
+08
+14
+00
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 12 1 0
+BITMAP
+14
+28
+00
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 11 1 0
+BITMAP
+24
+00
+3c
+42
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+88
+50
+20
+50
+88
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 10 11 0 -1
+BITMAP
+0040
+1e80
+2100
+4280
+4480
+4480
+4880
+5080
+2100
+5e00
+8000
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 12 1 0
+BITMAP
+20
+10
+00
+84
+84
+84
+84
+84
+84
+84
+84
+78
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 12 1 0
+BITMAP
+08
+10
+00
+84
+84
+84
+84
+84
+84
+84
+84
+78
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 12 1 0
+BITMAP
+10
+28
+00
+84
+84
+84
+84
+84
+84
+84
+84
+78
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+48
+00
+84
+84
+84
+84
+84
+84
+84
+84
+78
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+08
+10
+00
+82
+82
+44
+44
+28
+10
+10
+10
+10
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+80
+80
+f8
+84
+84
+84
+f8
+80
+80
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+70
+88
+88
+88
+b0
+88
+88
+88
+b0
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+20
+10
+00
+70
+88
+08
+78
+88
+88
+74
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+10
+20
+00
+70
+88
+08
+78
+88
+88
+74
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+20
+50
+00
+70
+88
+08
+78
+88
+88
+74
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+28
+50
+00
+70
+88
+08
+78
+88
+88
+74
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 1 0
+BITMAP
+50
+00
+70
+88
+08
+78
+88
+88
+74
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+30
+48
+30
+70
+88
+08
+78
+88
+88
+74
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 9 7 1 0
+BITMAP
+7700
+8880
+0880
+7f80
+8800
+8880
+7700
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 -3
+BITMAP
+70
+88
+80
+80
+80
+88
+70
+20
+10
+60
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+40
+20
+00
+70
+88
+88
+f8
+80
+88
+70
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+10
+20
+00
+70
+88
+88
+f8
+80
+88
+70
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+20
+50
+00
+70
+88
+88
+f8
+80
+88
+70
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+50
+00
+70
+88
+88
+f8
+80
+88
+70
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 10 0 0
+BITMAP
+80
+40
+00
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 10 1 0
+BITMAP
+40
+80
+00
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 10 0 0
+BITMAP
+40
+a0
+00
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 9 0 0
+BITMAP
+a0
+00
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+68
+30
+50
+08
+78
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+28
+50
+00
+b0
+c8
+88
+88
+88
+88
+88
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+40
+20
+00
+70
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+10
+20
+00
+70
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+20
+50
+00
+70
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+28
+50
+00
+70
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+50
+00
+70
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+20
+00
+f8
+00
+20
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 7 0 0
+BITMAP
+3a
+44
+4c
+54
+64
+44
+b8
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+40
+20
+00
+88
+88
+88
+88
+88
+98
+68
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+10
+20
+00
+88
+88
+88
+88
+88
+98
+68
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+20
+50
+00
+88
+88
+88
+88
+88
+98
+68
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+50
+00
+88
+88
+88
+88
+88
+98
+68
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 13 1 -3
+BITMAP
+10
+20
+00
+88
+88
+88
+90
+50
+50
+20
+20
+40
+80
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 12 1 -3
+BITMAP
+80
+80
+b0
+c8
+88
+88
+88
+c8
+b0
+80
+80
+80
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 12 1 -3
+BITMAP
+50
+00
+88
+88
+48
+50
+50
+30
+20
+20
+20
+c0
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica12.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica12.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica12.h	(revision 22322)
@@ -0,0 +1,783 @@
+static unsigned char helvetica12_0_bits[] = {
+0x00};
+static unsigned char helvetica12_1_bits[] = {
+0x00};
+static unsigned char helvetica12_2_bits[] = {
+0x00};
+static unsigned char helvetica12_3_bits[] = {
+0x00};
+static unsigned char helvetica12_4_bits[] = {
+0x00};
+static unsigned char helvetica12_5_bits[] = {
+0x00};
+static unsigned char helvetica12_6_bits[] = {
+0x00};
+static unsigned char helvetica12_7_bits[] = {
+0x00};
+static unsigned char helvetica12_8_bits[] = {
+0x00};
+static unsigned char helvetica12_9_bits[] = {
+0x00};
+static unsigned char helvetica12_10_bits[] = {
+0x00};
+static unsigned char helvetica12_11_bits[] = {
+0x00};
+static unsigned char helvetica12_12_bits[] = {
+0x00};
+static unsigned char helvetica12_13_bits[] = {
+0x00};
+static unsigned char helvetica12_14_bits[] = {
+0x00};
+static unsigned char helvetica12_15_bits[] = {
+0x00};
+static unsigned char helvetica12_16_bits[] = {
+0x00};
+static unsigned char helvetica12_17_bits[] = {
+0x00};
+static unsigned char helvetica12_18_bits[] = {
+0x00};
+static unsigned char helvetica12_19_bits[] = {
+0x00};
+static unsigned char helvetica12_20_bits[] = {
+0x00};
+static unsigned char helvetica12_21_bits[] = {
+0x00};
+static unsigned char helvetica12_22_bits[] = {
+0x00};
+static unsigned char helvetica12_23_bits[] = {
+0x00};
+static unsigned char helvetica12_24_bits[] = {
+0x00};
+static unsigned char helvetica12_25_bits[] = {
+0x00};
+static unsigned char helvetica12_26_bits[] = {
+0x00};
+static unsigned char helvetica12_27_bits[] = {
+0x00};
+static unsigned char helvetica12_28_bits[] = {
+0x00};
+static unsigned char helvetica12_29_bits[] = {
+0x00};
+static unsigned char helvetica12_30_bits[] = {
+0x00};
+static unsigned char helvetica12_31_bits[] = {
+0x00};
+static unsigned char helvetica12_32_bits[] = {
+0x00};
+static unsigned char helvetica12_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01};
+static unsigned char helvetica12_34_bits[] = {
+0x05, 0x05, 0x05};
+static unsigned char helvetica12_35_bits[] = {
+0x14, 0x14, 0x3f, 0x14, 0x3f, 0x0a, 0x0a, 0x0a};
+static unsigned char helvetica12_36_bits[] = {
+0x04, 0x0e, 0x15, 0x05, 0x0e, 0x14, 0x15, 0x15, 0x0e, 0x04};
+static unsigned char helvetica12_37_bits[] = {
+0x46, 0x00, 0x29, 0x00, 0x29, 0x00, 0x16, 0x00, 0x10, 0x00, 0xc8, 0x00, 
+0x28, 0x01, 0x28, 0x01, 0xc4, 0x00};
+static unsigned char helvetica12_38_bits[] = {
+0x0c, 0x12, 0x12, 0x0c, 0x0a, 0x51, 0x21, 0x31, 0x4e};
+static unsigned char helvetica12_39_bits[] = {
+0x03, 0x02, 0x01};
+static unsigned char helvetica12_40_bits[] = {
+0x04, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x04};
+static unsigned char helvetica12_41_bits[] = {
+0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x01};
+static unsigned char helvetica12_42_bits[] = {
+0x05, 0x02, 0x05};
+static unsigned char helvetica12_43_bits[] = {
+0x04, 0x04, 0x1f, 0x04, 0x04};
+static unsigned char helvetica12_44_bits[] = {
+0x02, 0x02, 0x01};
+static unsigned char helvetica12_45_bits[] = {
+0x1f};
+static unsigned char helvetica12_46_bits[] = {
+0x01};
+static unsigned char helvetica12_47_bits[] = {
+0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01};
+static unsigned char helvetica12_48_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_49_bits[] = {
+0x04, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04};
+static unsigned char helvetica12_50_bits[] = {
+0x0e, 0x11, 0x10, 0x08, 0x04, 0x02, 0x01, 0x01, 0x1f};
+static unsigned char helvetica12_51_bits[] = {
+0x0e, 0x11, 0x10, 0x0c, 0x10, 0x10, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_52_bits[] = {
+0x10, 0x18, 0x14, 0x14, 0x12, 0x11, 0x3f, 0x10, 0x10};
+static unsigned char helvetica12_53_bits[] = {
+0x1f, 0x01, 0x01, 0x0f, 0x10, 0x10, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_54_bits[] = {
+0x0e, 0x11, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_55_bits[] = {
+0x1f, 0x10, 0x08, 0x08, 0x04, 0x04, 0x04, 0x02, 0x02};
+static unsigned char helvetica12_56_bits[] = {
+0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_57_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x11, 0x0e};
+static unsigned char helvetica12_58_bits[] = {
+0x01, 0x00, 0x00, 0x00, 0x00, 0x01};
+static unsigned char helvetica12_59_bits[] = {
+0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01};
+static unsigned char helvetica12_60_bits[] = {
+0x30, 0x0c, 0x03, 0x0c, 0x30};
+static unsigned char helvetica12_61_bits[] = {
+0x1f, 0x00, 0x1f};
+static unsigned char helvetica12_62_bits[] = {
+0x03, 0x0c, 0x30, 0x0c, 0x03};
+static unsigned char helvetica12_63_bits[] = {
+0x0e, 0x11, 0x11, 0x08, 0x08, 0x04, 0x04, 0x00, 0x04};
+static unsigned char helvetica12_64_bits[] = {
+0xf8, 0x00, 0x06, 0x01, 0xb2, 0x02, 0x49, 0x02, 0x45, 0x02, 0x45, 0x02, 
+0x65, 0x01, 0xd9, 0x00, 0x02, 0x00, 0x7c, 0x00};
+static unsigned char helvetica12_65_bits[] = {
+0x08, 0x14, 0x14, 0x22, 0x22, 0x3e, 0x41, 0x41, 0x41};
+static unsigned char helvetica12_66_bits[] = {
+0x1f, 0x21, 0x21, 0x21, 0x1f, 0x21, 0x21, 0x21, 0x1f};
+static unsigned char helvetica12_67_bits[] = {
+0x3c, 0x42, 0x01, 0x01, 0x01, 0x01, 0x01, 0x42, 0x3c};
+static unsigned char helvetica12_68_bits[] = {
+0x1f, 0x21, 0x41, 0x41, 0x41, 0x41, 0x41, 0x21, 0x1f};
+static unsigned char helvetica12_69_bits[] = {
+0x3f, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 0x3f};
+static unsigned char helvetica12_70_bits[] = {
+0x3f, 0x01, 0x01, 0x01, 0x1f, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_71_bits[] = {
+0x3c, 0x42, 0x01, 0x01, 0x71, 0x41, 0x41, 0x62, 0x5c};
+static unsigned char helvetica12_72_bits[] = {
+0x41, 0x41, 0x41, 0x41, 0x7f, 0x41, 0x41, 0x41, 0x41};
+static unsigned char helvetica12_73_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_74_bits[] = {
+0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_75_bits[] = {
+0x21, 0x11, 0x09, 0x05, 0x07, 0x09, 0x11, 0x21, 0x41};
+static unsigned char helvetica12_76_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1f};
+static unsigned char helvetica12_77_bits[] = {
+0x01, 0x01, 0x83, 0x01, 0x83, 0x01, 0x45, 0x01, 0x45, 0x01, 0x29, 0x01, 
+0x29, 0x01, 0x11, 0x01, 0x11, 0x01};
+static unsigned char helvetica12_78_bits[] = {
+0x41, 0x43, 0x45, 0x45, 0x49, 0x51, 0x51, 0x61, 0x41};
+static unsigned char helvetica12_79_bits[] = {
+0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char helvetica12_80_bits[] = {
+0x1f, 0x21, 0x21, 0x21, 0x1f, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_81_bits[] = {
+0x3c, 0x42, 0x81, 0x81, 0x81, 0x91, 0xa1, 0x42, 0xbc};
+static unsigned char helvetica12_82_bits[] = {
+0x1f, 0x21, 0x21, 0x21, 0x1f, 0x11, 0x21, 0x21, 0x21};
+static unsigned char helvetica12_83_bits[] = {
+0x1e, 0x21, 0x01, 0x06, 0x18, 0x20, 0x21, 0x21, 0x1e};
+static unsigned char helvetica12_84_bits[] = {
+0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
+static unsigned char helvetica12_85_bits[] = {
+0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1e};
+static unsigned char helvetica12_86_bits[] = {
+0x41, 0x41, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08};
+static unsigned char helvetica12_87_bits[] = {
+0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x92, 0x00, 0xaa, 0x00, 0xaa, 0x00, 
+0x44, 0x00, 0x44, 0x00, 0x44, 0x00};
+static unsigned char helvetica12_88_bits[] = {
+0x41, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x41};
+static unsigned char helvetica12_89_bits[] = {
+0x41, 0x41, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08};
+static unsigned char helvetica12_90_bits[] = {
+0x7f, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x7f};
+static unsigned char helvetica12_91_bits[] = {
+0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03};
+static unsigned char helvetica12_92_bits[] = {
+0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x08, 0x08};
+static unsigned char helvetica12_93_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03};
+static unsigned char helvetica12_94_bits[] = {
+0x04, 0x0a, 0x11};
+static unsigned char helvetica12_95_bits[] = {
+0x7f};
+static unsigned char helvetica12_96_bits[] = {
+0x02, 0x01, 0x03};
+static unsigned char helvetica12_97_bits[] = {
+0x0e, 0x11, 0x10, 0x1e, 0x11, 0x11, 0x2e};
+static unsigned char helvetica12_98_bits[] = {
+0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11, 0x13, 0x0d};
+static unsigned char helvetica12_99_bits[] = {
+0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e};
+static unsigned char helvetica12_100_bits[] = {
+0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char helvetica12_101_bits[] = {
+0x0e, 0x11, 0x11, 0x1f, 0x01, 0x11, 0x0e};
+static unsigned char helvetica12_102_bits[] = {
+0x0c, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica12_103_bits[] = {
+0x16, 0x19, 0x11, 0x11, 0x11, 0x19, 0x16, 0x10, 0x11, 0x0e};
+static unsigned char helvetica12_104_bits[] = {
+0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11, 0x11, 0x11};
+static unsigned char helvetica12_105_bits[] = {
+0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_106_bits[] = {
+0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01};
+static unsigned char helvetica12_107_bits[] = {
+0x01, 0x01, 0x09, 0x05, 0x03, 0x03, 0x05, 0x09, 0x11};
+static unsigned char helvetica12_108_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_109_bits[] = {
+0x25, 0x5b, 0x49, 0x49, 0x49, 0x49, 0x49};
+static unsigned char helvetica12_110_bits[] = {
+0x0d, 0x13, 0x11, 0x11, 0x11, 0x11, 0x11};
+static unsigned char helvetica12_111_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_112_bits[] = {
+0x0d, 0x13, 0x11, 0x11, 0x11, 0x13, 0x0d, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_113_bits[] = {
+0x16, 0x19, 0x11, 0x11, 0x11, 0x19, 0x16, 0x10, 0x10, 0x10};
+static unsigned char helvetica12_114_bits[] = {
+0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_115_bits[] = {
+0x06, 0x09, 0x01, 0x06, 0x08, 0x09, 0x06};
+static unsigned char helvetica12_116_bits[] = {
+0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x06};
+static unsigned char helvetica12_117_bits[] = {
+0x11, 0x11, 0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char helvetica12_118_bits[] = {
+0x11, 0x11, 0x11, 0x0a, 0x0a, 0x04, 0x04};
+static unsigned char helvetica12_119_bits[] = {
+0x11, 0x01, 0x11, 0x01, 0x92, 0x00, 0x92, 0x00, 0xaa, 0x00, 0x44, 0x00, 
+0x44, 0x00};
+static unsigned char helvetica12_120_bits[] = {
+0x21, 0x12, 0x0c, 0x0c, 0x12, 0x21, 0x21};
+static unsigned char helvetica12_121_bits[] = {
+0x11, 0x11, 0x11, 0x09, 0x0a, 0x0a, 0x04, 0x04, 0x02, 0x01};
+static unsigned char helvetica12_122_bits[] = {
+0x0f, 0x08, 0x04, 0x02, 0x02, 0x01, 0x0f};
+static unsigned char helvetica12_123_bits[] = {
+0x0c, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0c};
+static unsigned char helvetica12_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_125_bits[] = {
+0x03, 0x04, 0x04, 0x04, 0x04, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03};
+static unsigned char helvetica12_126_bits[] = {
+0x26, 0x19};
+static unsigned char helvetica12_127_bits[] = {
+0x00};
+static unsigned char helvetica12_128_bits[] = {
+0x00};
+static unsigned char helvetica12_129_bits[] = {
+0x00};
+static unsigned char helvetica12_130_bits[] = {
+0x00};
+static unsigned char helvetica12_131_bits[] = {
+0x00};
+static unsigned char helvetica12_132_bits[] = {
+0x00};
+static unsigned char helvetica12_133_bits[] = {
+0x00};
+static unsigned char helvetica12_134_bits[] = {
+0x00};
+static unsigned char helvetica12_135_bits[] = {
+0x00};
+static unsigned char helvetica12_136_bits[] = {
+0x00};
+static unsigned char helvetica12_137_bits[] = {
+0x00};
+static unsigned char helvetica12_138_bits[] = {
+0x00};
+static unsigned char helvetica12_139_bits[] = {
+0x00};
+static unsigned char helvetica12_140_bits[] = {
+0x00};
+static unsigned char helvetica12_141_bits[] = {
+0x00};
+static unsigned char helvetica12_142_bits[] = {
+0x00};
+static unsigned char helvetica12_143_bits[] = {
+0x00};
+static unsigned char helvetica12_144_bits[] = {
+0x00};
+static unsigned char helvetica12_145_bits[] = {
+0x00};
+static unsigned char helvetica12_146_bits[] = {
+0x00};
+static unsigned char helvetica12_147_bits[] = {
+0x00};
+static unsigned char helvetica12_148_bits[] = {
+0x00};
+static unsigned char helvetica12_149_bits[] = {
+0x00};
+static unsigned char helvetica12_150_bits[] = {
+0x00};
+static unsigned char helvetica12_151_bits[] = {
+0x00};
+static unsigned char helvetica12_152_bits[] = {
+0x00};
+static unsigned char helvetica12_153_bits[] = {
+0x00};
+static unsigned char helvetica12_154_bits[] = {
+0x00};
+static unsigned char helvetica12_155_bits[] = {
+0x00};
+static unsigned char helvetica12_156_bits[] = {
+0x00};
+static unsigned char helvetica12_157_bits[] = {
+0x00};
+static unsigned char helvetica12_158_bits[] = {
+0x00};
+static unsigned char helvetica12_159_bits[] = {
+0x00};
+static unsigned char helvetica12_160_bits[] = {
+0x00};
+static unsigned char helvetica12_161_bits[] = {
+0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_162_bits[] = {
+0x08, 0x0e, 0x15, 0x05, 0x05, 0x05, 0x13, 0x0e, 0x02};
+static unsigned char helvetica12_163_bits[] = {
+0x0c, 0x12, 0x02, 0x02, 0x0f, 0x04, 0x04, 0x12, 0x0d};
+static unsigned char helvetica12_164_bits[] = {
+0x21, 0x1e, 0x12, 0x12, 0x1e, 0x21};
+static unsigned char helvetica12_165_bits[] = {
+0x11, 0x11, 0x0a, 0x04, 0x1f, 0x04, 0x1f, 0x04, 0x04};
+static unsigned char helvetica12_166_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_167_bits[] = {
+0x0e, 0x11, 0x01, 0x06, 0x09, 0x11, 0x11, 0x12, 0x0c, 0x10, 0x11, 0x0e};
+static unsigned char helvetica12_168_bits[] = {
+0x05};
+static unsigned char helvetica12_169_bits[] = {
+0x7c, 0x00, 0x82, 0x00, 0x39, 0x01, 0x45, 0x01, 0x05, 0x01, 0x45, 0x01, 
+0x39, 0x01, 0x82, 0x00, 0x7c, 0x00};
+static unsigned char helvetica12_170_bits[] = {
+0x07, 0x04, 0x05, 0x00, 0x07};
+static unsigned char helvetica12_171_bits[] = {
+0x14, 0x0a, 0x05, 0x0a, 0x14};
+static unsigned char helvetica12_172_bits[] = {
+0x3f, 0x20, 0x20, 0x20};
+static unsigned char helvetica12_173_bits[] = {
+0x0f};
+static unsigned char helvetica12_174_bits[] = {
+0x7c, 0x00, 0x82, 0x00, 0x39, 0x01, 0x29, 0x01, 0x19, 0x01, 0x29, 0x01, 
+0x29, 0x01, 0x82, 0x00, 0x7c, 0x00};
+static unsigned char helvetica12_175_bits[] = {
+0x0f};
+static unsigned char helvetica12_176_bits[] = {
+0x06, 0x09, 0x09, 0x06};
+static unsigned char helvetica12_177_bits[] = {
+0x04, 0x04, 0x1f, 0x04, 0x04, 0x00, 0x1f};
+static unsigned char helvetica12_178_bits[] = {
+0x06, 0x09, 0x04, 0x02, 0x0f};
+static unsigned char helvetica12_179_bits[] = {
+0x07, 0x04, 0x02, 0x04, 0x03};
+static unsigned char helvetica12_180_bits[] = {
+0x02, 0x01};
+static unsigned char helvetica12_181_bits[] = {
+0x11, 0x11, 0x11, 0x11, 0x11, 0x19, 0x17, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_182_bits[] = {
+0x3c, 0x16, 0x17, 0x17, 0x17, 0x16, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14};
+static unsigned char helvetica12_183_bits[] = {
+0x01};
+static unsigned char helvetica12_184_bits[] = {
+0x02, 0x04, 0x04, 0x03};
+static unsigned char helvetica12_185_bits[] = {
+0x02, 0x03, 0x02, 0x02, 0x02};
+static unsigned char helvetica12_186_bits[] = {
+0x07, 0x05, 0x07, 0x00, 0x07};
+static unsigned char helvetica12_187_bits[] = {
+0x05, 0x0a, 0x14, 0x0a, 0x05};
+static unsigned char helvetica12_188_bits[] = {
+0x82, 0x00, 0x43, 0x00, 0x22, 0x00, 0x22, 0x00, 0x92, 0x00, 0xc8, 0x00, 
+0xa8, 0x00, 0xe4, 0x01, 0x82, 0x00};
+static unsigned char helvetica12_189_bits[] = {
+0x82, 0x00, 0x43, 0x00, 0x22, 0x00, 0x12, 0x00, 0xd2, 0x00, 0x28, 0x01, 
+0x88, 0x00, 0x44, 0x00, 0xe2, 0x01};
+static unsigned char helvetica12_190_bits[] = {
+0x87, 0x00, 0x44, 0x00, 0x22, 0x00, 0x24, 0x00, 0x93, 0x00, 0xd0, 0x00, 
+0xa8, 0x00, 0xe8, 0x01, 0x84, 0x00};
+static unsigned char helvetica12_191_bits[] = {
+0x04, 0x00, 0x04, 0x04, 0x02, 0x02, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_192_bits[] = {
+0x04, 0x08, 0x00, 0x08, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x41, 0x41, 0x41};
+static unsigned char helvetica12_193_bits[] = {
+0x10, 0x08, 0x00, 0x08, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x41, 0x41, 0x41};
+static unsigned char helvetica12_194_bits[] = {
+0x08, 0x14, 0x00, 0x08, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x41, 0x41, 0x41};
+static unsigned char helvetica12_195_bits[] = {
+0x28, 0x14, 0x00, 0x08, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x41, 0x41, 0x41};
+static unsigned char helvetica12_196_bits[] = {
+0x14, 0x00, 0x08, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x41, 0x41, 0x41};
+static unsigned char helvetica12_197_bits[] = {
+0x08, 0x14, 0x08, 0x08, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x41, 0x41, 0x41};
+static unsigned char helvetica12_198_bits[] = {
+0xf8, 0x01, 0x14, 0x00, 0x14, 0x00, 0x12, 0x00, 0xf2, 0x01, 0x1e, 0x00, 
+0x11, 0x00, 0x11, 0x00, 0xf1, 0x01};
+static unsigned char helvetica12_199_bits[] = {
+0x3c, 0x42, 0x01, 0x01, 0x01, 0x01, 0x01, 0x42, 0x3c, 0x10, 0x10, 0x0c};
+static unsigned char helvetica12_200_bits[] = {
+0x04, 0x08, 0x00, 0x3f, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 0x3f};
+static unsigned char helvetica12_201_bits[] = {
+0x10, 0x08, 0x00, 0x3f, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 0x3f};
+static unsigned char helvetica12_202_bits[] = {
+0x08, 0x14, 0x00, 0x3f, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 0x3f};
+static unsigned char helvetica12_203_bits[] = {
+0x14, 0x00, 0x3f, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 0x3f};
+static unsigned char helvetica12_204_bits[] = {
+0x01, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica12_205_bits[] = {
+0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_206_bits[] = {
+0x02, 0x05, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica12_207_bits[] = {
+0x05, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica12_208_bits[] = {
+0x3e, 0x42, 0x82, 0x82, 0x8f, 0x82, 0x82, 0x42, 0x3e};
+static unsigned char helvetica12_209_bits[] = {
+0x28, 0x14, 0x00, 0x41, 0x43, 0x45, 0x45, 0x49, 0x51, 0x51, 0x61, 0x41};
+static unsigned char helvetica12_210_bits[] = {
+0x08, 0x10, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char helvetica12_211_bits[] = {
+0x20, 0x10, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char helvetica12_212_bits[] = {
+0x10, 0x28, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char helvetica12_213_bits[] = {
+0x28, 0x14, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char helvetica12_214_bits[] = {
+0x24, 0x00, 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char helvetica12_215_bits[] = {
+0x11, 0x0a, 0x04, 0x0a, 0x11};
+static unsigned char helvetica12_216_bits[] = {
+0x00, 0x02, 0x78, 0x01, 0x84, 0x00, 0x42, 0x01, 0x22, 0x01, 0x22, 0x01, 
+0x12, 0x01, 0x0a, 0x01, 0x84, 0x00, 0x7a, 0x00, 0x01, 0x00};
+static unsigned char helvetica12_217_bits[] = {
+0x04, 0x08, 0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1e};
+static unsigned char helvetica12_218_bits[] = {
+0x10, 0x08, 0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1e};
+static unsigned char helvetica12_219_bits[] = {
+0x08, 0x14, 0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1e};
+static unsigned char helvetica12_220_bits[] = {
+0x12, 0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1e};
+static unsigned char helvetica12_221_bits[] = {
+0x10, 0x08, 0x00, 0x41, 0x41, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08};
+static unsigned char helvetica12_222_bits[] = {
+0x01, 0x01, 0x1f, 0x21, 0x21, 0x21, 0x1f, 0x01, 0x01};
+static unsigned char helvetica12_223_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x0d, 0x11, 0x11, 0x11, 0x0d};
+static unsigned char helvetica12_224_bits[] = {
+0x04, 0x08, 0x00, 0x0e, 0x11, 0x10, 0x1e, 0x11, 0x11, 0x2e};
+static unsigned char helvetica12_225_bits[] = {
+0x08, 0x04, 0x00, 0x0e, 0x11, 0x10, 0x1e, 0x11, 0x11, 0x2e};
+static unsigned char helvetica12_226_bits[] = {
+0x04, 0x0a, 0x00, 0x0e, 0x11, 0x10, 0x1e, 0x11, 0x11, 0x2e};
+static unsigned char helvetica12_227_bits[] = {
+0x14, 0x0a, 0x00, 0x0e, 0x11, 0x10, 0x1e, 0x11, 0x11, 0x2e};
+static unsigned char helvetica12_228_bits[] = {
+0x0a, 0x00, 0x0e, 0x11, 0x10, 0x1e, 0x11, 0x11, 0x2e};
+static unsigned char helvetica12_229_bits[] = {
+0x0c, 0x12, 0x0c, 0x0e, 0x11, 0x10, 0x1e, 0x11, 0x11, 0x2e};
+static unsigned char helvetica12_230_bits[] = {
+0xee, 0x00, 0x11, 0x01, 0x10, 0x01, 0xfe, 0x01, 0x11, 0x00, 0x11, 0x01, 
+0xee, 0x00};
+static unsigned char helvetica12_231_bits[] = {
+0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e, 0x04, 0x08, 0x06};
+static unsigned char helvetica12_232_bits[] = {
+0x02, 0x04, 0x00, 0x0e, 0x11, 0x11, 0x1f, 0x01, 0x11, 0x0e};
+static unsigned char helvetica12_233_bits[] = {
+0x08, 0x04, 0x00, 0x0e, 0x11, 0x11, 0x1f, 0x01, 0x11, 0x0e};
+static unsigned char helvetica12_234_bits[] = {
+0x04, 0x0a, 0x00, 0x0e, 0x11, 0x11, 0x1f, 0x01, 0x11, 0x0e};
+static unsigned char helvetica12_235_bits[] = {
+0x0a, 0x00, 0x0e, 0x11, 0x11, 0x1f, 0x01, 0x11, 0x0e};
+static unsigned char helvetica12_236_bits[] = {
+0x01, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica12_237_bits[] = {
+0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_238_bits[] = {
+0x02, 0x05, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica12_239_bits[] = {
+0x05, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica12_240_bits[] = {
+0x16, 0x0c, 0x0a, 0x10, 0x1e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_241_bits[] = {
+0x14, 0x0a, 0x00, 0x0d, 0x13, 0x11, 0x11, 0x11, 0x11, 0x11};
+static unsigned char helvetica12_242_bits[] = {
+0x02, 0x04, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_243_bits[] = {
+0x08, 0x04, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_244_bits[] = {
+0x04, 0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_245_bits[] = {
+0x14, 0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_246_bits[] = {
+0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica12_247_bits[] = {
+0x04, 0x00, 0x1f, 0x00, 0x04};
+static unsigned char helvetica12_248_bits[] = {
+0x5c, 0x22, 0x32, 0x2a, 0x26, 0x22, 0x1d};
+static unsigned char helvetica12_249_bits[] = {
+0x02, 0x04, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char helvetica12_250_bits[] = {
+0x08, 0x04, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char helvetica12_251_bits[] = {
+0x04, 0x0a, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char helvetica12_252_bits[] = {
+0x0a, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x19, 0x16};
+static unsigned char helvetica12_253_bits[] = {
+0x08, 0x04, 0x00, 0x11, 0x11, 0x11, 0x09, 0x0a, 0x0a, 0x04, 0x04, 0x02, 
+0x01};
+static unsigned char helvetica12_254_bits[] = {
+0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11, 0x13, 0x0d, 0x01, 0x01, 0x01};
+static unsigned char helvetica12_255_bits[] = {
+0x0a, 0x00, 0x11, 0x11, 0x12, 0x0a, 0x0a, 0x0c, 0x04, 0x04, 0x04, 0x03};
+static RotFont helvetica12font[] = {
+{5, 1, 1, helvetica12_0_bits},
+{5, 1, 1, helvetica12_1_bits},
+{5, 1, 1, helvetica12_2_bits},
+{5, 1, 1, helvetica12_3_bits},
+{5, 1, 1, helvetica12_4_bits},
+{5, 1, 1, helvetica12_5_bits},
+{5, 1, 1, helvetica12_6_bits},
+{5, 1, 1, helvetica12_7_bits},
+{5, 1, 1, helvetica12_8_bits},
+{5, 1, 1, helvetica12_9_bits},
+{5, 1, 1, helvetica12_10_bits},
+{5, 1, 1, helvetica12_11_bits},
+{5, 1, 1, helvetica12_12_bits},
+{5, 1, 1, helvetica12_13_bits},
+{5, 1, 1, helvetica12_14_bits},
+{5, 1, 1, helvetica12_15_bits},
+{5, 1, 1, helvetica12_16_bits},
+{5, 1, 1, helvetica12_17_bits},
+{5, 1, 1, helvetica12_18_bits},
+{5, 1, 1, helvetica12_19_bits},
+{5, 1, 1, helvetica12_20_bits},
+{5, 1, 1, helvetica12_21_bits},
+{5, 1, 1, helvetica12_22_bits},
+{5, 1, 1, helvetica12_23_bits},
+{5, 1, 1, helvetica12_24_bits},
+{5, 1, 1, helvetica12_25_bits},
+{5, 1, 1, helvetica12_26_bits},
+{5, 1, 1, helvetica12_27_bits},
+{5, 1, 1, helvetica12_28_bits},
+{5, 1, 1, helvetica12_29_bits},
+{5, 1, 1, helvetica12_30_bits},
+{5, 1, 1, helvetica12_31_bits},
+{6, 1, 1, helvetica12_32_bits},
+{1, 9, 9, helvetica12_33_bits},
+{3, 3, 9, helvetica12_34_bits},
+{6, 8, 8, helvetica12_35_bits},
+{5, 10, 9, helvetica12_36_bits},
+{9, 9, 9, helvetica12_37_bits},
+{7, 9, 9, helvetica12_38_bits},
+{2, 3, 9, helvetica12_39_bits},
+{3, 12, 9, helvetica12_40_bits},
+{3, 12, 9, helvetica12_41_bits},
+{3, 3, 9, helvetica12_42_bits},
+{5, 5, 6, helvetica12_43_bits},
+{2, 3, 1, helvetica12_44_bits},
+{5, 1, 4, helvetica12_45_bits},
+{1, 1, 1, helvetica12_46_bits},
+{4, 9, 9, helvetica12_47_bits},
+{5, 9, 9, helvetica12_48_bits},
+{3, 9, 9, helvetica12_49_bits},
+{5, 9, 9, helvetica12_50_bits},
+{5, 9, 9, helvetica12_51_bits},
+{6, 9, 9, helvetica12_52_bits},
+{5, 9, 9, helvetica12_53_bits},
+{5, 9, 9, helvetica12_54_bits},
+{5, 9, 9, helvetica12_55_bits},
+{5, 9, 9, helvetica12_56_bits},
+{5, 9, 9, helvetica12_57_bits},
+{1, 6, 6, helvetica12_58_bits},
+{2, 8, 6, helvetica12_59_bits},
+{6, 5, 6, helvetica12_60_bits},
+{5, 3, 5, helvetica12_61_bits},
+{6, 5, 6, helvetica12_62_bits},
+{5, 9, 9, helvetica12_63_bits},
+{10, 10, 9, helvetica12_64_bits},
+{7, 9, 9, helvetica12_65_bits},
+{6, 9, 9, helvetica12_66_bits},
+{7, 9, 9, helvetica12_67_bits},
+{7, 9, 9, helvetica12_68_bits},
+{6, 9, 9, helvetica12_69_bits},
+{6, 9, 9, helvetica12_70_bits},
+{7, 9, 9, helvetica12_71_bits},
+{7, 9, 9, helvetica12_72_bits},
+{1, 9, 9, helvetica12_73_bits},
+{5, 9, 9, helvetica12_74_bits},
+{7, 9, 9, helvetica12_75_bits},
+{5, 9, 9, helvetica12_76_bits},
+{9, 9, 9, helvetica12_77_bits},
+{7, 9, 9, helvetica12_78_bits},
+{8, 9, 9, helvetica12_79_bits},
+{6, 9, 9, helvetica12_80_bits},
+{8, 9, 9, helvetica12_81_bits},
+{6, 9, 9, helvetica12_82_bits},
+{6, 9, 9, helvetica12_83_bits},
+{7, 9, 9, helvetica12_84_bits},
+{6, 9, 9, helvetica12_85_bits},
+{7, 9, 9, helvetica12_86_bits},
+{9, 9, 9, helvetica12_87_bits},
+{7, 9, 9, helvetica12_88_bits},
+{7, 9, 9, helvetica12_89_bits},
+{7, 9, 9, helvetica12_90_bits},
+{2, 12, 9, helvetica12_91_bits},
+{4, 9, 9, helvetica12_92_bits},
+{2, 12, 9, helvetica12_93_bits},
+{5, 3, 8, helvetica12_94_bits},
+{7, 1, -1, helvetica12_95_bits},
+{2, 3, 9, helvetica12_96_bits},
+{6, 7, 7, helvetica12_97_bits},
+{5, 9, 9, helvetica12_98_bits},
+{5, 7, 7, helvetica12_99_bits},
+{5, 9, 9, helvetica12_100_bits},
+{5, 7, 7, helvetica12_101_bits},
+{4, 9, 9, helvetica12_102_bits},
+{5, 10, 7, helvetica12_103_bits},
+{5, 9, 9, helvetica12_104_bits},
+{1, 9, 9, helvetica12_105_bits},
+{2, 12, 9, helvetica12_106_bits},
+{5, 9, 9, helvetica12_107_bits},
+{1, 9, 9, helvetica12_108_bits},
+{7, 7, 7, helvetica12_109_bits},
+{5, 7, 7, helvetica12_110_bits},
+{5, 7, 7, helvetica12_111_bits},
+{5, 10, 7, helvetica12_112_bits},
+{5, 10, 7, helvetica12_113_bits},
+{3, 7, 7, helvetica12_114_bits},
+{4, 7, 7, helvetica12_115_bits},
+{3, 9, 9, helvetica12_116_bits},
+{5, 7, 7, helvetica12_117_bits},
+{5, 7, 7, helvetica12_118_bits},
+{9, 7, 7, helvetica12_119_bits},
+{6, 7, 7, helvetica12_120_bits},
+{5, 10, 7, helvetica12_121_bits},
+{4, 7, 7, helvetica12_122_bits},
+{4, 12, 9, helvetica12_123_bits},
+{1, 12, 9, helvetica12_124_bits},
+{4, 12, 9, helvetica12_125_bits},
+{6, 2, 5, helvetica12_126_bits},
+{5, 1, 1, helvetica12_127_bits},
+{5, 1, 1, helvetica12_128_bits},
+{5, 1, 1, helvetica12_129_bits},
+{5, 1, 1, helvetica12_130_bits},
+{5, 1, 1, helvetica12_131_bits},
+{5, 1, 1, helvetica12_132_bits},
+{5, 1, 1, helvetica12_133_bits},
+{5, 1, 1, helvetica12_134_bits},
+{5, 1, 1, helvetica12_135_bits},
+{5, 1, 1, helvetica12_136_bits},
+{5, 1, 1, helvetica12_137_bits},
+{5, 1, 1, helvetica12_138_bits},
+{5, 1, 1, helvetica12_139_bits},
+{5, 1, 1, helvetica12_140_bits},
+{5, 1, 1, helvetica12_141_bits},
+{5, 1, 1, helvetica12_142_bits},
+{5, 1, 1, helvetica12_143_bits},
+{5, 1, 1, helvetica12_144_bits},
+{5, 1, 1, helvetica12_145_bits},
+{5, 1, 1, helvetica12_146_bits},
+{5, 1, 1, helvetica12_147_bits},
+{5, 1, 1, helvetica12_148_bits},
+{5, 1, 1, helvetica12_149_bits},
+{5, 1, 1, helvetica12_150_bits},
+{5, 1, 1, helvetica12_151_bits},
+{5, 1, 1, helvetica12_152_bits},
+{5, 1, 1, helvetica12_153_bits},
+{5, 1, 1, helvetica12_154_bits},
+{5, 1, 1, helvetica12_155_bits},
+{5, 1, 1, helvetica12_156_bits},
+{5, 1, 1, helvetica12_157_bits},
+{5, 1, 1, helvetica12_158_bits},
+{5, 1, 1, helvetica12_159_bits},
+{1, 1, 1, helvetica12_160_bits},
+{1, 10, 7, helvetica12_161_bits},
+{5, 9, 8, helvetica12_162_bits},
+{5, 9, 9, helvetica12_163_bits},
+{6, 6, 7, helvetica12_164_bits},
+{5, 9, 9, helvetica12_165_bits},
+{1, 11, 9, helvetica12_166_bits},
+{5, 12, 9, helvetica12_167_bits},
+{3, 1, 9, helvetica12_168_bits},
+{9, 9, 9, helvetica12_169_bits},
+{3, 5, 9, helvetica12_170_bits},
+{5, 5, 6, helvetica12_171_bits},
+{6, 4, 6, helvetica12_172_bits},
+{4, 1, 4, helvetica12_173_bits},
+{9, 9, 9, helvetica12_174_bits},
+{4, 1, 9, helvetica12_175_bits},
+{4, 4, 8, helvetica12_176_bits},
+{5, 7, 7, helvetica12_177_bits},
+{4, 5, 8, helvetica12_178_bits},
+{3, 5, 8, helvetica12_179_bits},
+{2, 2, 10, helvetica12_180_bits},
+{5, 10, 7, helvetica12_181_bits},
+{6, 12, 9, helvetica12_182_bits},
+{1, 1, 4, helvetica12_183_bits},
+{3, 4, 1, helvetica12_184_bits},
+{2, 5, 8, helvetica12_185_bits},
+{3, 5, 9, helvetica12_186_bits},
+{5, 5, 6, helvetica12_187_bits},
+{9, 9, 9, helvetica12_188_bits},
+{9, 9, 9, helvetica12_189_bits},
+{9, 9, 9, helvetica12_190_bits},
+{5, 9, 6, helvetica12_191_bits},
+{7, 12, 12, helvetica12_192_bits},
+{7, 12, 12, helvetica12_193_bits},
+{7, 12, 12, helvetica12_194_bits},
+{7, 12, 12, helvetica12_195_bits},
+{7, 11, 11, helvetica12_196_bits},
+{7, 12, 12, helvetica12_197_bits},
+{9, 9, 9, helvetica12_198_bits},
+{7, 12, 9, helvetica12_199_bits},
+{6, 12, 12, helvetica12_200_bits},
+{6, 12, 12, helvetica12_201_bits},
+{6, 12, 12, helvetica12_202_bits},
+{6, 11, 11, helvetica12_203_bits},
+{2, 12, 12, helvetica12_204_bits},
+{2, 12, 12, helvetica12_205_bits},
+{3, 12, 12, helvetica12_206_bits},
+{3, 11, 11, helvetica12_207_bits},
+{8, 9, 9, helvetica12_208_bits},
+{7, 12, 12, helvetica12_209_bits},
+{8, 12, 12, helvetica12_210_bits},
+{8, 12, 12, helvetica12_211_bits},
+{8, 12, 12, helvetica12_212_bits},
+{8, 12, 12, helvetica12_213_bits},
+{8, 11, 11, helvetica12_214_bits},
+{5, 5, 6, helvetica12_215_bits},
+{10, 11, 10, helvetica12_216_bits},
+{6, 12, 12, helvetica12_217_bits},
+{6, 12, 12, helvetica12_218_bits},
+{6, 12, 12, helvetica12_219_bits},
+{6, 11, 11, helvetica12_220_bits},
+{7, 12, 12, helvetica12_221_bits},
+{6, 9, 9, helvetica12_222_bits},
+{5, 9, 9, helvetica12_223_bits},
+{6, 10, 10, helvetica12_224_bits},
+{6, 10, 10, helvetica12_225_bits},
+{6, 10, 10, helvetica12_226_bits},
+{6, 10, 10, helvetica12_227_bits},
+{6, 9, 9, helvetica12_228_bits},
+{6, 10, 10, helvetica12_229_bits},
+{9, 7, 7, helvetica12_230_bits},
+{5, 10, 7, helvetica12_231_bits},
+{5, 10, 10, helvetica12_232_bits},
+{5, 10, 10, helvetica12_233_bits},
+{5, 10, 10, helvetica12_234_bits},
+{5, 9, 9, helvetica12_235_bits},
+{2, 10, 10, helvetica12_236_bits},
+{2, 10, 10, helvetica12_237_bits},
+{3, 10, 10, helvetica12_238_bits},
+{3, 9, 9, helvetica12_239_bits},
+{5, 10, 10, helvetica12_240_bits},
+{5, 10, 10, helvetica12_241_bits},
+{5, 10, 10, helvetica12_242_bits},
+{5, 10, 10, helvetica12_243_bits},
+{5, 10, 10, helvetica12_244_bits},
+{5, 10, 10, helvetica12_245_bits},
+{5, 9, 9, helvetica12_246_bits},
+{5, 5, 6, helvetica12_247_bits},
+{7, 7, 7, helvetica12_248_bits},
+{5, 10, 10, helvetica12_249_bits},
+{5, 10, 10, helvetica12_250_bits},
+{5, 10, 10, helvetica12_251_bits},
+{5, 9, 9, helvetica12_252_bits},
+{5, 13, 10, helvetica12_253_bits},
+{5, 12, 9, helvetica12_254_bits},
+{5, 12, 9, helvetica12_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica14.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica14.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica14.bdf	(revision 22322)
@@ -0,0 +1,3309 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Helvetica-Medium-R-Normal--14-100-100-100-P-76-ISO8859-1
+SIZE 10 100 100
+FONTBOUNDINGBOX 14 17 -1 -3
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Helvetica"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 14
+POINT_SIZE 100
+RESOLUTION_X 100
+RESOLUTION_Y 100
+SPACING "P"
+AVERAGE_WIDTH 76
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 11
+X_HEIGHT 8
+FACE_NAME "Helvetica"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.  "
+_DEC_DEVICE_FONTNAMES "PS=Helvetica"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2, 07-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Helvetica"
+FONT "-Adobe-Helvetica-Medium-R-Normal--14-100-100-100-P-76-ISO8859-1"
+WEIGHT 10
+RESOLUTION 138
+QUAD_WIDTH 8
+DEFAULT_CHAR 32
+FONT_ASCENT 13
+FONT_DESCENT 3
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 1 11 2 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+00
+80
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 3 1 8
+BITMAP
+a0
+a0
+a0
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 10 0 0
+BITMAP
+14
+14
+14
+7e
+28
+28
+fc
+50
+50
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 14 0 -2
+BITMAP
+10
+7c
+92
+92
+90
+50
+38
+14
+12
+92
+92
+7c
+10
+10
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 864 0
+DWIDTH 12 0
+BBX 11 11 0 0
+BITMAP
+7080
+8900
+8900
+7200
+0200
+0400
+0800
+09c0
+1220
+1220
+21c0
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+30
+48
+48
+30
+20
+52
+8a
+84
+8a
+71
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 2 3 1 8
+BITMAP
+c0
+40
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 14 1 -3
+BITMAP
+20
+40
+40
+80
+80
+80
+80
+80
+80
+80
+80
+40
+40
+20
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 14 1 -3
+BITMAP
+80
+40
+40
+20
+20
+20
+20
+20
+20
+20
+20
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 5 1 6
+BITMAP
+20
+a8
+70
+a8
+20
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 1
+BITMAP
+10
+10
+10
+fe
+10
+10
+10
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 2 4 0 -2
+BITMAP
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 1 1 4
+BITMAP
+fe
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 1 2 1 0
+BITMAP
+80
+80
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 11 0 0
+BITMAP
+10
+10
+20
+20
+20
+40
+40
+40
+80
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+78
+84
+84
+84
+84
+84
+84
+84
+84
+84
+78
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 3 11 2 0
+BITMAP
+20
+e0
+20
+20
+20
+20
+20
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+78
+84
+84
+04
+08
+10
+20
+40
+80
+80
+fc
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+78
+84
+84
+04
+04
+38
+04
+04
+84
+84
+78
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 11 1 0
+BITMAP
+04
+0c
+14
+24
+44
+84
+84
+fe
+04
+04
+04
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+fc
+80
+80
+80
+f8
+04
+04
+04
+84
+84
+78
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+78
+84
+80
+80
+b8
+c4
+84
+84
+84
+84
+78
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+fc
+04
+08
+08
+10
+10
+20
+20
+40
+40
+40
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+78
+84
+84
+84
+84
+78
+84
+84
+84
+84
+78
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+78
+84
+84
+84
+84
+7c
+04
+04
+84
+84
+78
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 1 8 1 0
+BITMAP
+80
+80
+00
+00
+00
+00
+80
+80
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 2 10 0 -2
+BITMAP
+40
+40
+00
+00
+00
+00
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 5 1 2
+BITMAP
+0c
+30
+c0
+30
+0c
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 3 1 3
+BITMAP
+fc
+00
+fc
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 5 1 2
+BITMAP
+c0
+30
+0c
+30
+c0
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+30
+cc
+84
+84
+04
+08
+10
+20
+00
+20
+20
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 936 0
+DWIDTH 13 0
+BBX 11 12 1 -1
+BITMAP
+0f00
+30c0
+4020
+46a0
+8920
+9120
+9120
+9340
+8d80
+4000
+6080
+1f00
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 11 0 0
+BITMAP
+0800
+1c00
+1400
+1400
+2200
+2200
+4100
+7f00
+4100
+8080
+8080
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 11 1 0
+BITMAP
+fc
+86
+82
+82
+84
+f8
+84
+82
+82
+86
+fc
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 11 1 0
+BITMAP
+1c
+63
+41
+80
+80
+80
+80
+80
+41
+63
+1c
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 11 1 0
+BITMAP
+f8
+86
+82
+81
+81
+81
+81
+81
+82
+86
+f8
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 11 1 0
+BITMAP
+fe
+80
+80
+80
+80
+fc
+80
+80
+80
+80
+fe
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 11 1 0
+BITMAP
+fe
+80
+80
+80
+80
+fc
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+1e00
+6180
+4080
+8000
+8000
+8780
+8080
+8080
+4080
+6380
+1c80
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 11 1 0
+BITMAP
+81
+81
+81
+81
+81
+ff
+81
+81
+81
+81
+81
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 1 11 2 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 0 0
+BITMAP
+04
+04
+04
+04
+04
+04
+04
+04
+84
+84
+78
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 8 11 1 0
+BITMAP
+82
+84
+88
+90
+a0
+e0
+90
+88
+84
+82
+81
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 2 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+fc
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 864 0
+DWIDTH 12 0
+BBX 11 11 0 0
+BITMAP
+8020
+c060
+c060
+a0a0
+a0a0
+9120
+9120
+8a20
+8a20
+8420
+8420
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 11 1 0
+BITMAP
+c1
+a1
+a1
+91
+91
+89
+89
+85
+85
+83
+83
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 11 1 0
+BITMAP
+fc
+86
+82
+82
+86
+fc
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 11 1 0
+BITMAP
+1c00
+6300
+4100
+8080
+8080
+8080
+8880
+8480
+4300
+6300
+1c80
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 11 1 0
+BITMAP
+fe
+83
+81
+81
+82
+fc
+82
+81
+81
+81
+81
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 11 1 0
+BITMAP
+38
+c6
+82
+80
+60
+18
+06
+02
+82
+c6
+38
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 11 0 0
+BITMAP
+ff80
+0800
+0800
+0800
+0800
+0800
+0800
+0800
+0800
+0800
+0800
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 11 1 0
+BITMAP
+81
+81
+81
+81
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 11 0 0
+BITMAP
+8080
+8080
+4100
+4100
+6300
+2200
+2200
+1400
+1400
+0800
+0800
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 936 0
+DWIDTH 13 0
+BBX 13 11 0 0
+BITMAP
+8208
+8208
+8508
+4510
+4510
+4510
+28a0
+28a0
+28a0
+1040
+1040
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 11 0 0
+BITMAP
+8080
+4100
+2200
+1400
+0800
+0800
+1400
+2200
+4100
+4100
+8080
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 11 0 0
+BITMAP
+8080
+c180
+4100
+2200
+2200
+1400
+1c00
+0800
+0800
+0800
+0800
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 11 1 0
+BITMAP
+fe
+02
+04
+08
+18
+10
+20
+60
+40
+80
+fe
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 3 14 1 -3
+BITMAP
+e0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+e0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 11 0 0
+BITMAP
+80
+80
+40
+40
+40
+20
+20
+20
+10
+10
+10
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 3 14 0 -3
+BITMAP
+e0
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+e0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 5 1 6
+BITMAP
+20
+50
+50
+88
+88
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 8 1 0 -3
+BITMAP
+ff
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 2 3 0 8
+BITMAP
+40
+80
+c0
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 8 1 0
+BITMAP
+78
+cc
+04
+7c
+c4
+84
+cc
+76
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 1 0
+BITMAP
+80
+80
+80
+b8
+cc
+84
+84
+84
+84
+cc
+b8
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 8 1 0
+BITMAP
+78
+cc
+80
+80
+80
+84
+cc
+78
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+04
+04
+04
+74
+cc
+84
+84
+84
+84
+cc
+74
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 8 1 0
+BITMAP
+78
+cc
+84
+fc
+80
+80
+cc
+78
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 11 0 0
+BITMAP
+30
+40
+40
+e0
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 -3
+BITMAP
+74
+cc
+84
+84
+84
+84
+cc
+74
+04
+cc
+78
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+80
+80
+80
+b8
+cc
+84
+84
+84
+84
+84
+84
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 1 11 1 0
+BITMAP
+80
+80
+00
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 3 14 -1 -3
+BITMAP
+20
+20
+00
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+c0
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 1 0
+BITMAP
+80
+80
+80
+88
+90
+a0
+c0
+a0
+90
+88
+84
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 1 11 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 8 1 0
+BITMAP
+b300
+cc80
+8880
+8880
+8880
+8880
+8880
+8880
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 8 1 0
+BITMAP
+b8
+cc
+84
+84
+84
+84
+84
+84
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 8 1 0
+BITMAP
+78
+cc
+84
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 -3
+BITMAP
+b8
+cc
+84
+84
+84
+84
+cc
+b8
+80
+80
+80
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 -3
+BITMAP
+74
+cc
+84
+84
+84
+84
+cc
+74
+04
+04
+04
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 4 8 1 0
+BITMAP
+b0
+c0
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 8 1 0
+BITMAP
+70
+88
+c0
+70
+18
+08
+88
+70
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 10 0 0
+BITMAP
+40
+40
+e0
+40
+40
+40
+40
+40
+40
+30
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 8 1 0
+BITMAP
+84
+84
+84
+84
+84
+84
+cc
+74
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 8 0 0
+BITMAP
+82
+82
+44
+44
+44
+28
+28
+10
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 9 8 0 0
+BITMAP
+8880
+8880
+8880
+4900
+4900
+5500
+2200
+2200
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 8 0 0
+BITMAP
+c6
+44
+28
+10
+10
+28
+44
+c6
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 11 0 -3
+BITMAP
+82
+c2
+44
+44
+24
+28
+18
+10
+10
+30
+60
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 8 0 0
+BITMAP
+fc
+04
+08
+10
+20
+40
+80
+fc
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 5 14 0 -3
+BITMAP
+18
+20
+20
+20
+20
+40
+80
+40
+20
+20
+20
+20
+20
+18
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 1 14 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 5 14 0 -3
+BITMAP
+c0
+20
+20
+20
+20
+10
+08
+10
+20
+20
+20
+20
+20
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 3 1 3
+BITMAP
+64
+b4
+98
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 1 11 1 -3
+BITMAP
+80
+80
+00
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 10 1 -1
+BITMAP
+04
+78
+cc
+90
+90
+a0
+a4
+cc
+78
+80
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 11 0 0
+BITMAP
+38
+44
+40
+40
+f8
+20
+20
+20
+40
+62
+dc
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 6 1 2
+BITMAP
+84
+78
+48
+48
+78
+84
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 11 0 0
+BITMAP
+82
+82
+82
+44
+44
+28
+fe
+10
+fe
+10
+10
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 1 14 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+00
+00
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 5 14 2 -3
+BITMAP
+70
+d8
+88
+c0
+70
+98
+88
+88
+c8
+70
+18
+88
+d8
+70
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 5 1 0 9
+BITMAP
+d8
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 864 0
+DWIDTH 12 0
+BBX 10 11 1 0
+BITMAP
+1e00
+6180
+5c80
+a2c0
+a240
+a040
+a240
+9c40
+4080
+6180
+1e00
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 432 0
+DWIDTH 6 0
+BBX 4 7 1 4
+BITMAP
+e0
+10
+70
+90
+d0
+00
+f0
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 5 1 2
+BITMAP
+24
+48
+90
+48
+24
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 4 1 2
+BITMAP
+fe
+02
+02
+02
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 3 1 0 4
+BITMAP
+e0
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 864 0
+DWIDTH 12 0
+BBX 10 11 1 0
+BITMAP
+1e00
+6180
+5c80
+9240
+9240
+9c40
+9240
+9240
+4080
+6180
+1e00
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 1 0 9
+BITMAP
+f0
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 432 0
+DWIDTH 6 0
+BBX 4 4 1 7
+BITMAP
+60
+90
+90
+60
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 9 1 0
+BITMAP
+10
+10
+10
+fe
+10
+10
+10
+00
+fe
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 4 6 0 5
+BITMAP
+60
+90
+10
+20
+40
+f0
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 4 6 0 5
+BITMAP
+60
+90
+20
+10
+90
+60
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 2 2 2 9
+BITMAP
+40
+80
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 -3
+BITMAP
+84
+84
+84
+84
+84
+84
+cc
+b4
+80
+80
+80
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 14 0 -3
+BITMAP
+3e
+74
+f4
+f4
+f4
+74
+34
+14
+14
+14
+14
+14
+14
+14
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 2 1 1 4
+BITMAP
+c0
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 4 3 0 -3
+BITMAP
+20
+90
+60
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 2 6 1 5
+BITMAP
+40
+c0
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 432 0
+DWIDTH 6 0
+BBX 4 7 1 4
+BITMAP
+60
+90
+90
+90
+60
+00
+f0
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 5 1 2
+BITMAP
+90
+48
+24
+48
+90
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 864 0
+DWIDTH 12 0
+BBX 10 11 1 0
+BITMAP
+4200
+c200
+4400
+4400
+4800
+4880
+0980
+1280
+1480
+27c0
+2080
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 864 0
+DWIDTH 12 0
+BBX 9 11 1 0
+BITMAP
+4200
+c200
+4400
+4400
+4800
+4b00
+1480
+1080
+1100
+2200
+2780
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 864 0
+DWIDTH 12 0
+BBX 11 11 0 0
+BITMAP
+6100
+9100
+2200
+1200
+9400
+6440
+04c0
+0940
+0a40
+13e0
+1040
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 -3
+BITMAP
+10
+10
+00
+10
+20
+40
+80
+84
+84
+cc
+30
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 14 0 0
+BITMAP
+1000
+0800
+0000
+0800
+1c00
+1400
+1400
+2200
+2200
+4100
+7f00
+4100
+8080
+8080
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 14 0 0
+BITMAP
+0400
+0800
+0000
+0800
+1c00
+1400
+1400
+2200
+2200
+4100
+7f00
+4100
+8080
+8080
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 14 0 0
+BITMAP
+0c00
+1200
+0000
+0800
+1c00
+1400
+1400
+2200
+2200
+4100
+7f00
+4100
+8080
+8080
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 14 0 0
+BITMAP
+1a00
+2c00
+0000
+0800
+1c00
+1400
+1400
+2200
+2200
+4100
+7f00
+4100
+8080
+8080
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 13 0 0
+BITMAP
+3600
+0000
+0800
+1c00
+1400
+1400
+2200
+2200
+4100
+7f00
+4100
+8080
+8080
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 14 0 0
+BITMAP
+0c00
+1200
+1200
+0c00
+0c00
+1400
+1400
+2200
+2200
+4100
+7f00
+4100
+8080
+8080
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 1008 0
+DWIDTH 14 0
+BBX 12 11 1 0
+BITMAP
+1ff0
+1200
+1200
+2200
+2200
+23f0
+7e00
+4200
+4200
+8200
+83f0
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 -3
+BITMAP
+1c
+63
+41
+80
+80
+80
+80
+80
+41
+63
+1c
+08
+24
+18
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+20
+10
+00
+fe
+80
+80
+80
+80
+fc
+80
+80
+80
+80
+fe
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+08
+10
+00
+fe
+80
+80
+80
+80
+fc
+80
+80
+80
+80
+fe
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+18
+24
+00
+fe
+80
+80
+80
+80
+fc
+80
+80
+80
+80
+fe
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+6c
+00
+fe
+80
+80
+80
+80
+fc
+80
+80
+80
+80
+fe
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 2 14 1 0
+BITMAP
+80
+40
+00
+40
+40
+40
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 2 14 2 0
+BITMAP
+40
+80
+00
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 14 1 0
+BITMAP
+60
+90
+00
+40
+40
+40
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 5 13 0 0
+BITMAP
+d8
+00
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 9 11 0 0
+BITMAP
+7c00
+4300
+4100
+4080
+4080
+f080
+4080
+4080
+4100
+4300
+7c00
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+1a
+2c
+00
+c1
+a1
+a1
+91
+91
+89
+89
+85
+85
+83
+83
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+1000
+0800
+0000
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+0400
+0800
+0000
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+0c00
+1200
+0000
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+1a00
+2c00
+0000
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 13 1 0
+BITMAP
+3300
+0000
+1c00
+6300
+4100
+8080
+8080
+8080
+8080
+8080
+4100
+6300
+1c00
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 1
+BITMAP
+82
+44
+28
+10
+28
+44
+82
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 11 11 0 0
+BITMAP
+0e20
+31c0
+2080
+4140
+4240
+4440
+4840
+5040
+2080
+7180
+8e00
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+10
+08
+00
+81
+81
+81
+81
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+04
+08
+00
+81
+81
+81
+81
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+18
+24
+00
+81
+81
+81
+81
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+66
+00
+81
+81
+81
+81
+81
+81
+81
+81
+81
+42
+3c
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 14 0 0
+BITMAP
+0400
+0800
+0000
+8080
+c180
+4100
+2200
+2200
+1400
+1c00
+0800
+0800
+0800
+0800
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 11 1 0
+BITMAP
+80
+80
+fc
+86
+82
+82
+86
+fc
+80
+80
+80
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 11 1 0
+BITMAP
+70
+88
+88
+88
+b0
+90
+88
+88
+88
+88
+b0
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 11 1 0
+BITMAP
+20
+10
+00
+78
+cc
+04
+7c
+c4
+84
+cc
+76
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 11 1 0
+BITMAP
+10
+20
+00
+78
+cc
+04
+7c
+c4
+84
+cc
+76
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 11 1 0
+BITMAP
+30
+48
+00
+78
+cc
+04
+7c
+c4
+84
+cc
+76
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 11 1 0
+BITMAP
+34
+58
+00
+78
+cc
+04
+7c
+c4
+84
+cc
+76
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 11 1 0
+BITMAP
+48
+48
+00
+78
+cc
+04
+7c
+c4
+84
+cc
+76
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 12 1 0
+BITMAP
+30
+48
+30
+00
+78
+cc
+04
+7c
+c4
+84
+cc
+76
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 936 0
+DWIDTH 13 0
+BBX 11 8 1 0
+BITMAP
+7bc0
+c660
+0420
+7fe0
+c400
+8400
+ce60
+7bc0
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 -3
+BITMAP
+78
+cc
+80
+80
+80
+84
+cc
+78
+10
+48
+30
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+20
+10
+00
+78
+cc
+84
+fc
+80
+80
+cc
+78
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+10
+20
+00
+78
+cc
+84
+fc
+80
+80
+cc
+78
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+30
+48
+00
+78
+cc
+84
+fc
+80
+80
+cc
+78
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+48
+48
+00
+78
+cc
+84
+fc
+80
+80
+cc
+78
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 2 11 1 0
+BITMAP
+80
+40
+00
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 2 11 1 0
+BITMAP
+40
+80
+00
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 4 11 0 0
+BITMAP
+60
+90
+00
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 3 11 0 0
+BITMAP
+a0
+a0
+00
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+d8
+70
+90
+78
+cc
+84
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+68
+b0
+00
+b8
+cc
+84
+84
+84
+84
+84
+84
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+20
+10
+00
+78
+cc
+84
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+10
+20
+00
+78
+cc
+84
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+30
+48
+00
+78
+cc
+84
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+68
+b0
+00
+78
+cc
+84
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+48
+48
+00
+78
+cc
+84
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 7 1 1
+BITMAP
+10
+10
+00
+fe
+00
+10
+10
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 8 8 0 0
+BITMAP
+3d
+62
+46
+4a
+52
+62
+46
+bc
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+20
+10
+00
+84
+84
+84
+84
+84
+84
+cc
+74
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+10
+20
+00
+84
+84
+84
+84
+84
+84
+cc
+74
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+30
+48
+00
+84
+84
+84
+84
+84
+84
+cc
+74
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 11 1 0
+BITMAP
+48
+48
+00
+84
+84
+84
+84
+84
+84
+cc
+74
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 14 0 -3
+BITMAP
+08
+10
+00
+82
+c2
+44
+44
+24
+28
+18
+10
+10
+30
+60
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 14 1 -3
+BITMAP
+80
+80
+80
+b8
+cc
+84
+84
+84
+84
+cc
+b8
+80
+80
+80
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 14 0 -3
+BITMAP
+24
+24
+00
+82
+c2
+44
+44
+24
+28
+18
+10
+10
+30
+60
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica14.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica14.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica14.h	(revision 22322)
@@ -0,0 +1,845 @@
+static unsigned char helvetica14_0_bits[] = {
+0x00};
+static unsigned char helvetica14_1_bits[] = {
+0x00};
+static unsigned char helvetica14_2_bits[] = {
+0x00};
+static unsigned char helvetica14_3_bits[] = {
+0x00};
+static unsigned char helvetica14_4_bits[] = {
+0x00};
+static unsigned char helvetica14_5_bits[] = {
+0x00};
+static unsigned char helvetica14_6_bits[] = {
+0x00};
+static unsigned char helvetica14_7_bits[] = {
+0x00};
+static unsigned char helvetica14_8_bits[] = {
+0x00};
+static unsigned char helvetica14_9_bits[] = {
+0x00};
+static unsigned char helvetica14_10_bits[] = {
+0x00};
+static unsigned char helvetica14_11_bits[] = {
+0x00};
+static unsigned char helvetica14_12_bits[] = {
+0x00};
+static unsigned char helvetica14_13_bits[] = {
+0x00};
+static unsigned char helvetica14_14_bits[] = {
+0x00};
+static unsigned char helvetica14_15_bits[] = {
+0x00};
+static unsigned char helvetica14_16_bits[] = {
+0x00};
+static unsigned char helvetica14_17_bits[] = {
+0x00};
+static unsigned char helvetica14_18_bits[] = {
+0x00};
+static unsigned char helvetica14_19_bits[] = {
+0x00};
+static unsigned char helvetica14_20_bits[] = {
+0x00};
+static unsigned char helvetica14_21_bits[] = {
+0x00};
+static unsigned char helvetica14_22_bits[] = {
+0x00};
+static unsigned char helvetica14_23_bits[] = {
+0x00};
+static unsigned char helvetica14_24_bits[] = {
+0x00};
+static unsigned char helvetica14_25_bits[] = {
+0x00};
+static unsigned char helvetica14_26_bits[] = {
+0x00};
+static unsigned char helvetica14_27_bits[] = {
+0x00};
+static unsigned char helvetica14_28_bits[] = {
+0x00};
+static unsigned char helvetica14_29_bits[] = {
+0x00};
+static unsigned char helvetica14_30_bits[] = {
+0x00};
+static unsigned char helvetica14_31_bits[] = {
+0x00};
+static unsigned char helvetica14_32_bits[] = {
+0x00};
+static unsigned char helvetica14_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01};
+static unsigned char helvetica14_34_bits[] = {
+0x05, 0x05, 0x05};
+static unsigned char helvetica14_35_bits[] = {
+0x28, 0x28, 0x28, 0x7e, 0x14, 0x14, 0x3f, 0x0a, 0x0a, 0x0a};
+static unsigned char helvetica14_36_bits[] = {
+0x08, 0x3e, 0x49, 0x49, 0x09, 0x0a, 0x1c, 0x28, 0x48, 0x49, 0x49, 0x3e, 
+0x08, 0x08};
+static unsigned char helvetica14_37_bits[] = {
+0x0e, 0x01, 0x91, 0x00, 0x91, 0x00, 0x4e, 0x00, 0x40, 0x00, 0x20, 0x00, 
+0x10, 0x00, 0x90, 0x03, 0x48, 0x04, 0x48, 0x04, 0x84, 0x03};
+static unsigned char helvetica14_38_bits[] = {
+0x0c, 0x12, 0x12, 0x0c, 0x04, 0x4a, 0x51, 0x21, 0x51, 0x8e};
+static unsigned char helvetica14_39_bits[] = {
+0x03, 0x02, 0x01};
+static unsigned char helvetica14_40_bits[] = {
+0x04, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 
+0x02, 0x04};
+static unsigned char helvetica14_41_bits[] = {
+0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 
+0x02, 0x01};
+static unsigned char helvetica14_42_bits[] = {
+0x04, 0x15, 0x0e, 0x15, 0x04};
+static unsigned char helvetica14_43_bits[] = {
+0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08};
+static unsigned char helvetica14_44_bits[] = {
+0x02, 0x02, 0x02, 0x01};
+static unsigned char helvetica14_45_bits[] = {
+0x7f};
+static unsigned char helvetica14_46_bits[] = {
+0x01, 0x01};
+static unsigned char helvetica14_47_bits[] = {
+0x08, 0x08, 0x04, 0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_48_bits[] = {
+0x1e, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1e};
+static unsigned char helvetica14_49_bits[] = {
+0x04, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04};
+static unsigned char helvetica14_50_bits[] = {
+0x1e, 0x21, 0x21, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x01, 0x3f};
+static unsigned char helvetica14_51_bits[] = {
+0x1e, 0x21, 0x21, 0x20, 0x20, 0x1c, 0x20, 0x20, 0x21, 0x21, 0x1e};
+static unsigned char helvetica14_52_bits[] = {
+0x20, 0x30, 0x28, 0x24, 0x22, 0x21, 0x21, 0x7f, 0x20, 0x20, 0x20};
+static unsigned char helvetica14_53_bits[] = {
+0x3f, 0x01, 0x01, 0x01, 0x1f, 0x20, 0x20, 0x20, 0x21, 0x21, 0x1e};
+static unsigned char helvetica14_54_bits[] = {
+0x1e, 0x21, 0x01, 0x01, 0x1d, 0x23, 0x21, 0x21, 0x21, 0x21, 0x1e};
+static unsigned char helvetica14_55_bits[] = {
+0x3f, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x02};
+static unsigned char helvetica14_56_bits[] = {
+0x1e, 0x21, 0x21, 0x21, 0x21, 0x1e, 0x21, 0x21, 0x21, 0x21, 0x1e};
+static unsigned char helvetica14_57_bits[] = {
+0x1e, 0x21, 0x21, 0x21, 0x21, 0x3e, 0x20, 0x20, 0x21, 0x21, 0x1e};
+static unsigned char helvetica14_58_bits[] = {
+0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01};
+static unsigned char helvetica14_59_bits[] = {
+0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x01};
+static unsigned char helvetica14_60_bits[] = {
+0x30, 0x0c, 0x03, 0x0c, 0x30};
+static unsigned char helvetica14_61_bits[] = {
+0x3f, 0x00, 0x3f};
+static unsigned char helvetica14_62_bits[] = {
+0x03, 0x0c, 0x30, 0x0c, 0x03};
+static unsigned char helvetica14_63_bits[] = {
+0x0c, 0x33, 0x21, 0x21, 0x20, 0x10, 0x08, 0x04, 0x00, 0x04, 0x04};
+static unsigned char helvetica14_64_bits[] = {
+0xf0, 0x00, 0x0c, 0x03, 0x02, 0x04, 0x62, 0x05, 0x91, 0x04, 0x89, 0x04, 
+0x89, 0x04, 0xc9, 0x02, 0xb1, 0x01, 0x02, 0x00, 0x06, 0x01, 0xf8, 0x00};
+static unsigned char helvetica14_65_bits[] = {
+0x10, 0x00, 0x38, 0x00, 0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 
+0x82, 0x00, 0xfe, 0x00, 0x82, 0x00, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_66_bits[] = {
+0x3f, 0x61, 0x41, 0x41, 0x21, 0x1f, 0x21, 0x41, 0x41, 0x61, 0x3f};
+static unsigned char helvetica14_67_bits[] = {
+0x38, 0xc6, 0x82, 0x01, 0x01, 0x01, 0x01, 0x01, 0x82, 0xc6, 0x38};
+static unsigned char helvetica14_68_bits[] = {
+0x1f, 0x61, 0x41, 0x81, 0x81, 0x81, 0x81, 0x81, 0x41, 0x61, 0x1f};
+static unsigned char helvetica14_69_bits[] = {
+0x7f, 0x01, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 0x01, 0x7f};
+static unsigned char helvetica14_70_bits[] = {
+0x7f, 0x01, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_71_bits[] = {
+0x78, 0x00, 0x86, 0x01, 0x02, 0x01, 0x01, 0x00, 0x01, 0x00, 0xe1, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0xc6, 0x01, 0x38, 0x01};
+static unsigned char helvetica14_72_bits[] = {
+0x81, 0x81, 0x81, 0x81, 0x81, 0xff, 0x81, 0x81, 0x81, 0x81, 0x81};
+static unsigned char helvetica14_73_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_74_bits[] = {
+0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x1e};
+static unsigned char helvetica14_75_bits[] = {
+0x41, 0x21, 0x11, 0x09, 0x05, 0x07, 0x09, 0x11, 0x21, 0x41, 0x81};
+static unsigned char helvetica14_76_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x3f};
+static unsigned char helvetica14_77_bits[] = {
+0x01, 0x04, 0x03, 0x06, 0x03, 0x06, 0x05, 0x05, 0x05, 0x05, 0x89, 0x04, 
+0x89, 0x04, 0x51, 0x04, 0x51, 0x04, 0x21, 0x04, 0x21, 0x04};
+static unsigned char helvetica14_78_bits[] = {
+0x83, 0x85, 0x85, 0x89, 0x89, 0x91, 0x91, 0xa1, 0xa1, 0xc1, 0xc1};
+static unsigned char helvetica14_79_bits[] = {
+0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 0xc6, 0x00, 0x38, 0x00};
+static unsigned char helvetica14_80_bits[] = {
+0x3f, 0x61, 0x41, 0x41, 0x61, 0x3f, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_81_bits[] = {
+0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x11, 0x01, 0x21, 0x01, 0xc2, 0x00, 0xc6, 0x00, 0x38, 0x01};
+static unsigned char helvetica14_82_bits[] = {
+0x7f, 0xc1, 0x81, 0x81, 0x41, 0x3f, 0x41, 0x81, 0x81, 0x81, 0x81};
+static unsigned char helvetica14_83_bits[] = {
+0x1c, 0x63, 0x41, 0x01, 0x06, 0x18, 0x60, 0x40, 0x41, 0x63, 0x1c};
+static unsigned char helvetica14_84_bits[] = {
+0xff, 0x01, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char helvetica14_85_bits[] = {
+0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
+static unsigned char helvetica14_86_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 0x82, 0x00, 0xc6, 0x00, 0x44, 0x00, 
+0x44, 0x00, 0x28, 0x00, 0x28, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char helvetica14_87_bits[] = {
+0x41, 0x10, 0x41, 0x10, 0xa1, 0x10, 0xa2, 0x08, 0xa2, 0x08, 0xa2, 0x08, 
+0x14, 0x05, 0x14, 0x05, 0x14, 0x05, 0x08, 0x02, 0x08, 0x02};
+static unsigned char helvetica14_88_bits[] = {
+0x01, 0x01, 0x82, 0x00, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x82, 0x00, 0x82, 0x00, 0x01, 0x01};
+static unsigned char helvetica14_89_bits[] = {
+0x01, 0x01, 0x83, 0x01, 0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 
+0x38, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char helvetica14_90_bits[] = {
+0x7f, 0x40, 0x20, 0x10, 0x18, 0x08, 0x04, 0x06, 0x02, 0x01, 0x7f};
+static unsigned char helvetica14_91_bits[] = {
+0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x07};
+static unsigned char helvetica14_92_bits[] = {
+0x01, 0x01, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08};
+static unsigned char helvetica14_93_bits[] = {
+0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x07};
+static unsigned char helvetica14_94_bits[] = {
+0x04, 0x0a, 0x0a, 0x11, 0x11};
+static unsigned char helvetica14_95_bits[] = {
+0xff};
+static unsigned char helvetica14_96_bits[] = {
+0x02, 0x01, 0x03};
+static unsigned char helvetica14_97_bits[] = {
+0x1e, 0x33, 0x20, 0x3e, 0x23, 0x21, 0x33, 0x6e};
+static unsigned char helvetica14_98_bits[] = {
+0x01, 0x01, 0x01, 0x1d, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1d};
+static unsigned char helvetica14_99_bits[] = {
+0x1e, 0x33, 0x01, 0x01, 0x01, 0x21, 0x33, 0x1e};
+static unsigned char helvetica14_100_bits[] = {
+0x20, 0x20, 0x20, 0x2e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x2e};
+static unsigned char helvetica14_101_bits[] = {
+0x1e, 0x33, 0x21, 0x3f, 0x01, 0x01, 0x33, 0x1e};
+static unsigned char helvetica14_102_bits[] = {
+0x0c, 0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica14_103_bits[] = {
+0x2e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x2e, 0x20, 0x33, 0x1e};
+static unsigned char helvetica14_104_bits[] = {
+0x01, 0x01, 0x01, 0x1d, 0x33, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21};
+static unsigned char helvetica14_105_bits[] = {
+0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_106_bits[] = {
+0x04, 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x03};
+static unsigned char helvetica14_107_bits[] = {
+0x01, 0x01, 0x01, 0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11, 0x21};
+static unsigned char helvetica14_108_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_109_bits[] = {
+0xcd, 0x00, 0x33, 0x01, 0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 
+0x11, 0x01, 0x11, 0x01};
+static unsigned char helvetica14_110_bits[] = {
+0x1d, 0x33, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21};
+static unsigned char helvetica14_111_bits[] = {
+0x1e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char helvetica14_112_bits[] = {
+0x1d, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1d, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_113_bits[] = {
+0x2e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x2e, 0x20, 0x20, 0x20};
+static unsigned char helvetica14_114_bits[] = {
+0x0d, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_115_bits[] = {
+0x0e, 0x11, 0x03, 0x0e, 0x18, 0x10, 0x11, 0x0e};
+static unsigned char helvetica14_116_bits[] = {
+0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0c};
+static unsigned char helvetica14_117_bits[] = {
+0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, 0x2e};
+static unsigned char helvetica14_118_bits[] = {
+0x41, 0x41, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08};
+static unsigned char helvetica14_119_bits[] = {
+0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x92, 0x00, 0x92, 0x00, 0xaa, 0x00, 
+0x44, 0x00, 0x44, 0x00};
+static unsigned char helvetica14_120_bits[] = {
+0x63, 0x22, 0x14, 0x08, 0x08, 0x14, 0x22, 0x63};
+static unsigned char helvetica14_121_bits[] = {
+0x41, 0x43, 0x22, 0x22, 0x24, 0x14, 0x18, 0x08, 0x08, 0x0c, 0x06};
+static unsigned char helvetica14_122_bits[] = {
+0x3f, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x3f};
+static unsigned char helvetica14_123_bits[] = {
+0x18, 0x04, 0x04, 0x04, 0x04, 0x02, 0x01, 0x02, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x18};
+static unsigned char helvetica14_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01};
+static unsigned char helvetica14_125_bits[] = {
+0x03, 0x04, 0x04, 0x04, 0x04, 0x08, 0x10, 0x08, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x03};
+static unsigned char helvetica14_126_bits[] = {
+0x26, 0x2d, 0x19};
+static unsigned char helvetica14_127_bits[] = {
+0x00};
+static unsigned char helvetica14_128_bits[] = {
+0x00};
+static unsigned char helvetica14_129_bits[] = {
+0x00};
+static unsigned char helvetica14_130_bits[] = {
+0x00};
+static unsigned char helvetica14_131_bits[] = {
+0x00};
+static unsigned char helvetica14_132_bits[] = {
+0x00};
+static unsigned char helvetica14_133_bits[] = {
+0x00};
+static unsigned char helvetica14_134_bits[] = {
+0x00};
+static unsigned char helvetica14_135_bits[] = {
+0x00};
+static unsigned char helvetica14_136_bits[] = {
+0x00};
+static unsigned char helvetica14_137_bits[] = {
+0x00};
+static unsigned char helvetica14_138_bits[] = {
+0x00};
+static unsigned char helvetica14_139_bits[] = {
+0x00};
+static unsigned char helvetica14_140_bits[] = {
+0x00};
+static unsigned char helvetica14_141_bits[] = {
+0x00};
+static unsigned char helvetica14_142_bits[] = {
+0x00};
+static unsigned char helvetica14_143_bits[] = {
+0x00};
+static unsigned char helvetica14_144_bits[] = {
+0x00};
+static unsigned char helvetica14_145_bits[] = {
+0x00};
+static unsigned char helvetica14_146_bits[] = {
+0x00};
+static unsigned char helvetica14_147_bits[] = {
+0x00};
+static unsigned char helvetica14_148_bits[] = {
+0x00};
+static unsigned char helvetica14_149_bits[] = {
+0x00};
+static unsigned char helvetica14_150_bits[] = {
+0x00};
+static unsigned char helvetica14_151_bits[] = {
+0x00};
+static unsigned char helvetica14_152_bits[] = {
+0x00};
+static unsigned char helvetica14_153_bits[] = {
+0x00};
+static unsigned char helvetica14_154_bits[] = {
+0x00};
+static unsigned char helvetica14_155_bits[] = {
+0x00};
+static unsigned char helvetica14_156_bits[] = {
+0x00};
+static unsigned char helvetica14_157_bits[] = {
+0x00};
+static unsigned char helvetica14_158_bits[] = {
+0x00};
+static unsigned char helvetica14_159_bits[] = {
+0x00};
+static unsigned char helvetica14_160_bits[] = {
+0x00};
+static unsigned char helvetica14_161_bits[] = {
+0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_162_bits[] = {
+0x20, 0x1e, 0x33, 0x09, 0x09, 0x05, 0x25, 0x33, 0x1e, 0x01};
+static unsigned char helvetica14_163_bits[] = {
+0x1c, 0x22, 0x02, 0x02, 0x1f, 0x04, 0x04, 0x04, 0x02, 0x46, 0x3b};
+static unsigned char helvetica14_164_bits[] = {
+0x21, 0x1e, 0x12, 0x12, 0x1e, 0x21};
+static unsigned char helvetica14_165_bits[] = {
+0x41, 0x41, 0x41, 0x22, 0x22, 0x14, 0x7f, 0x08, 0x7f, 0x08, 0x08};
+static unsigned char helvetica14_166_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01};
+static unsigned char helvetica14_167_bits[] = {
+0x0e, 0x1b, 0x11, 0x03, 0x0e, 0x19, 0x11, 0x11, 0x13, 0x0e, 0x18, 0x11, 
+0x1b, 0x0e};
+static unsigned char helvetica14_168_bits[] = {
+0x1b};
+static unsigned char helvetica14_169_bits[] = {
+0x78, 0x00, 0x86, 0x01, 0x3a, 0x01, 0x45, 0x03, 0x45, 0x02, 0x05, 0x02, 
+0x45, 0x02, 0x39, 0x02, 0x02, 0x01, 0x86, 0x01, 0x78, 0x00};
+static unsigned char helvetica14_170_bits[] = {
+0x07, 0x08, 0x0e, 0x09, 0x0b, 0x00, 0x0f};
+static unsigned char helvetica14_171_bits[] = {
+0x24, 0x12, 0x09, 0x12, 0x24};
+static unsigned char helvetica14_172_bits[] = {
+0x7f, 0x40, 0x40, 0x40};
+static unsigned char helvetica14_173_bits[] = {
+0x07};
+static unsigned char helvetica14_174_bits[] = {
+0x78, 0x00, 0x86, 0x01, 0x3a, 0x01, 0x49, 0x02, 0x49, 0x02, 0x39, 0x02, 
+0x49, 0x02, 0x49, 0x02, 0x02, 0x01, 0x86, 0x01, 0x78, 0x00};
+static unsigned char helvetica14_175_bits[] = {
+0x0f};
+static unsigned char helvetica14_176_bits[] = {
+0x06, 0x09, 0x09, 0x06};
+static unsigned char helvetica14_177_bits[] = {
+0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08, 0x00, 0x7f};
+static unsigned char helvetica14_178_bits[] = {
+0x06, 0x09, 0x08, 0x04, 0x02, 0x0f};
+static unsigned char helvetica14_179_bits[] = {
+0x06, 0x09, 0x04, 0x08, 0x09, 0x06};
+static unsigned char helvetica14_180_bits[] = {
+0x02, 0x01};
+static unsigned char helvetica14_181_bits[] = {
+0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, 0x2d, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_182_bits[] = {
+0x7c, 0x2e, 0x2f, 0x2f, 0x2f, 0x2e, 0x2c, 0x28, 0x28, 0x28, 0x28, 0x28, 
+0x28, 0x28};
+static unsigned char helvetica14_183_bits[] = {
+0x03};
+static unsigned char helvetica14_184_bits[] = {
+0x04, 0x09, 0x06};
+static unsigned char helvetica14_185_bits[] = {
+0x02, 0x03, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica14_186_bits[] = {
+0x06, 0x09, 0x09, 0x09, 0x06, 0x00, 0x0f};
+static unsigned char helvetica14_187_bits[] = {
+0x09, 0x12, 0x24, 0x12, 0x09};
+static unsigned char helvetica14_188_bits[] = {
+0x42, 0x00, 0x43, 0x00, 0x22, 0x00, 0x22, 0x00, 0x12, 0x00, 0x12, 0x01, 
+0x90, 0x01, 0x48, 0x01, 0x28, 0x01, 0xe4, 0x03, 0x04, 0x01};
+static unsigned char helvetica14_189_bits[] = {
+0x42, 0x00, 0x43, 0x00, 0x22, 0x00, 0x22, 0x00, 0x12, 0x00, 0xd2, 0x00, 
+0x28, 0x01, 0x08, 0x01, 0x88, 0x00, 0x44, 0x00, 0xe4, 0x01};
+static unsigned char helvetica14_190_bits[] = {
+0x86, 0x00, 0x89, 0x00, 0x44, 0x00, 0x48, 0x00, 0x29, 0x00, 0x26, 0x02, 
+0x20, 0x03, 0x90, 0x02, 0x50, 0x02, 0xc8, 0x07, 0x08, 0x02};
+static unsigned char helvetica14_191_bits[] = {
+0x08, 0x08, 0x00, 0x08, 0x04, 0x02, 0x01, 0x21, 0x21, 0x33, 0x0c};
+static unsigned char helvetica14_192_bits[] = {
+0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_193_bits[] = {
+0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_194_bits[] = {
+0x30, 0x00, 0x48, 0x00, 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_195_bits[] = {
+0x58, 0x00, 0x34, 0x00, 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_196_bits[] = {
+0x6c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x28, 0x00, 0x28, 0x00, 
+0x44, 0x00, 0x44, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x82, 0x00, 0x01, 0x01, 
+0x01, 0x01};
+static unsigned char helvetica14_197_bits[] = {
+0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, 0x00, 0x30, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_198_bits[] = {
+0xf8, 0x0f, 0x48, 0x00, 0x48, 0x00, 0x44, 0x00, 0x44, 0x00, 0xc4, 0x0f, 
+0x7e, 0x00, 0x42, 0x00, 0x42, 0x00, 0x41, 0x00, 0xc1, 0x0f};
+static unsigned char helvetica14_199_bits[] = {
+0x38, 0xc6, 0x82, 0x01, 0x01, 0x01, 0x01, 0x01, 0x82, 0xc6, 0x38, 0x10, 
+0x24, 0x18};
+static unsigned char helvetica14_200_bits[] = {
+0x04, 0x08, 0x00, 0x7f, 0x01, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 
+0x01, 0x7f};
+static unsigned char helvetica14_201_bits[] = {
+0x10, 0x08, 0x00, 0x7f, 0x01, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 
+0x01, 0x7f};
+static unsigned char helvetica14_202_bits[] = {
+0x18, 0x24, 0x00, 0x7f, 0x01, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 
+0x01, 0x7f};
+static unsigned char helvetica14_203_bits[] = {
+0x36, 0x00, 0x7f, 0x01, 0x01, 0x01, 0x01, 0x3f, 0x01, 0x01, 0x01, 0x01, 
+0x7f};
+static unsigned char helvetica14_204_bits[] = {
+0x01, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
+0x02, 0x02};
+static unsigned char helvetica14_205_bits[] = {
+0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01};
+static unsigned char helvetica14_206_bits[] = {
+0x06, 0x09, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
+0x02, 0x02};
+static unsigned char helvetica14_207_bits[] = {
+0x1b, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04};
+static unsigned char helvetica14_208_bits[] = {
+0x3e, 0x00, 0xc2, 0x00, 0x82, 0x00, 0x02, 0x01, 0x02, 0x01, 0x0f, 0x01, 
+0x02, 0x01, 0x02, 0x01, 0x82, 0x00, 0xc2, 0x00, 0x3e, 0x00};
+static unsigned char helvetica14_209_bits[] = {
+0x58, 0x34, 0x00, 0x83, 0x85, 0x85, 0x89, 0x89, 0x91, 0x91, 0xa1, 0xa1, 
+0xc1, 0xc1};
+static unsigned char helvetica14_210_bits[] = {
+0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 
+0xc6, 0x00, 0x38, 0x00};
+static unsigned char helvetica14_211_bits[] = {
+0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 
+0xc6, 0x00, 0x38, 0x00};
+static unsigned char helvetica14_212_bits[] = {
+0x30, 0x00, 0x48, 0x00, 0x00, 0x00, 0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 
+0xc6, 0x00, 0x38, 0x00};
+static unsigned char helvetica14_213_bits[] = {
+0x58, 0x00, 0x34, 0x00, 0x00, 0x00, 0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 
+0xc6, 0x00, 0x38, 0x00};
+static unsigned char helvetica14_214_bits[] = {
+0xcc, 0x00, 0x00, 0x00, 0x38, 0x00, 0xc6, 0x00, 0x82, 0x00, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x82, 0x00, 0xc6, 0x00, 
+0x38, 0x00};
+static unsigned char helvetica14_215_bits[] = {
+0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41};
+static unsigned char helvetica14_216_bits[] = {
+0x70, 0x04, 0x8c, 0x03, 0x04, 0x01, 0x82, 0x02, 0x42, 0x02, 0x22, 0x02, 
+0x12, 0x02, 0x0a, 0x02, 0x04, 0x01, 0x8e, 0x01, 0x71, 0x00};
+static unsigned char helvetica14_217_bits[] = {
+0x08, 0x10, 0x00, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 
+0x42, 0x3c};
+static unsigned char helvetica14_218_bits[] = {
+0x20, 0x10, 0x00, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 
+0x42, 0x3c};
+static unsigned char helvetica14_219_bits[] = {
+0x18, 0x24, 0x00, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 
+0x42, 0x3c};
+static unsigned char helvetica14_220_bits[] = {
+0x66, 0x00, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 
+0x3c};
+static unsigned char helvetica14_221_bits[] = {
+0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x01, 0x83, 0x01, 0x82, 0x00, 
+0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x38, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00};
+static unsigned char helvetica14_222_bits[] = {
+0x01, 0x01, 0x3f, 0x61, 0x41, 0x41, 0x61, 0x3f, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_223_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x0d, 0x09, 0x11, 0x11, 0x11, 0x11, 0x0d};
+static unsigned char helvetica14_224_bits[] = {
+0x04, 0x08, 0x00, 0x1e, 0x33, 0x20, 0x3e, 0x23, 0x21, 0x33, 0x6e};
+static unsigned char helvetica14_225_bits[] = {
+0x08, 0x04, 0x00, 0x1e, 0x33, 0x20, 0x3e, 0x23, 0x21, 0x33, 0x6e};
+static unsigned char helvetica14_226_bits[] = {
+0x0c, 0x12, 0x00, 0x1e, 0x33, 0x20, 0x3e, 0x23, 0x21, 0x33, 0x6e};
+static unsigned char helvetica14_227_bits[] = {
+0x2c, 0x1a, 0x00, 0x1e, 0x33, 0x20, 0x3e, 0x23, 0x21, 0x33, 0x6e};
+static unsigned char helvetica14_228_bits[] = {
+0x12, 0x12, 0x00, 0x1e, 0x33, 0x20, 0x3e, 0x23, 0x21, 0x33, 0x6e};
+static unsigned char helvetica14_229_bits[] = {
+0x0c, 0x12, 0x0c, 0x00, 0x1e, 0x33, 0x20, 0x3e, 0x23, 0x21, 0x33, 0x6e};
+static unsigned char helvetica14_230_bits[] = {
+0xde, 0x03, 0x63, 0x06, 0x20, 0x04, 0xfe, 0x07, 0x23, 0x00, 0x21, 0x00, 
+0x73, 0x06, 0xde, 0x03};
+static unsigned char helvetica14_231_bits[] = {
+0x1e, 0x33, 0x01, 0x01, 0x01, 0x21, 0x33, 0x1e, 0x08, 0x12, 0x0c};
+static unsigned char helvetica14_232_bits[] = {
+0x04, 0x08, 0x00, 0x1e, 0x33, 0x21, 0x3f, 0x01, 0x01, 0x33, 0x1e};
+static unsigned char helvetica14_233_bits[] = {
+0x08, 0x04, 0x00, 0x1e, 0x33, 0x21, 0x3f, 0x01, 0x01, 0x33, 0x1e};
+static unsigned char helvetica14_234_bits[] = {
+0x0c, 0x12, 0x00, 0x1e, 0x33, 0x21, 0x3f, 0x01, 0x01, 0x33, 0x1e};
+static unsigned char helvetica14_235_bits[] = {
+0x12, 0x12, 0x00, 0x1e, 0x33, 0x21, 0x3f, 0x01, 0x01, 0x33, 0x1e};
+static unsigned char helvetica14_236_bits[] = {
+0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_237_bits[] = {
+0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica14_238_bits[] = {
+0x06, 0x09, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica14_239_bits[] = {
+0x05, 0x05, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica14_240_bits[] = {
+0x1b, 0x0e, 0x09, 0x1e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char helvetica14_241_bits[] = {
+0x16, 0x0d, 0x00, 0x1d, 0x33, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21};
+static unsigned char helvetica14_242_bits[] = {
+0x04, 0x08, 0x00, 0x1e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char helvetica14_243_bits[] = {
+0x08, 0x04, 0x00, 0x1e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char helvetica14_244_bits[] = {
+0x0c, 0x12, 0x00, 0x1e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char helvetica14_245_bits[] = {
+0x16, 0x0d, 0x00, 0x1e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char helvetica14_246_bits[] = {
+0x12, 0x12, 0x00, 0x1e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char helvetica14_247_bits[] = {
+0x08, 0x08, 0x00, 0x7f, 0x00, 0x08, 0x08};
+static unsigned char helvetica14_248_bits[] = {
+0xbc, 0x46, 0x62, 0x52, 0x4a, 0x46, 0x62, 0x3d};
+static unsigned char helvetica14_249_bits[] = {
+0x04, 0x08, 0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, 0x2e};
+static unsigned char helvetica14_250_bits[] = {
+0x08, 0x04, 0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, 0x2e};
+static unsigned char helvetica14_251_bits[] = {
+0x0c, 0x12, 0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, 0x2e};
+static unsigned char helvetica14_252_bits[] = {
+0x12, 0x12, 0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, 0x2e};
+static unsigned char helvetica14_253_bits[] = {
+0x10, 0x08, 0x00, 0x41, 0x43, 0x22, 0x22, 0x24, 0x14, 0x18, 0x08, 0x08, 
+0x0c, 0x06};
+static unsigned char helvetica14_254_bits[] = {
+0x01, 0x01, 0x01, 0x1d, 0x33, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1d, 0x01, 
+0x01, 0x01};
+static unsigned char helvetica14_255_bits[] = {
+0x24, 0x24, 0x00, 0x41, 0x43, 0x22, 0x22, 0x24, 0x14, 0x18, 0x08, 0x08, 
+0x0c, 0x06};
+static RotFont helvetica14font[] = {
+{5, 1, 1, helvetica14_0_bits},
+{5, 1, 1, helvetica14_1_bits},
+{5, 1, 1, helvetica14_2_bits},
+{5, 1, 1, helvetica14_3_bits},
+{5, 1, 1, helvetica14_4_bits},
+{5, 1, 1, helvetica14_5_bits},
+{5, 1, 1, helvetica14_6_bits},
+{5, 1, 1, helvetica14_7_bits},
+{5, 1, 1, helvetica14_8_bits},
+{5, 1, 1, helvetica14_9_bits},
+{5, 1, 1, helvetica14_10_bits},
+{5, 1, 1, helvetica14_11_bits},
+{5, 1, 1, helvetica14_12_bits},
+{5, 1, 1, helvetica14_13_bits},
+{5, 1, 1, helvetica14_14_bits},
+{5, 1, 1, helvetica14_15_bits},
+{5, 1, 1, helvetica14_16_bits},
+{5, 1, 1, helvetica14_17_bits},
+{5, 1, 1, helvetica14_18_bits},
+{5, 1, 1, helvetica14_19_bits},
+{5, 1, 1, helvetica14_20_bits},
+{5, 1, 1, helvetica14_21_bits},
+{5, 1, 1, helvetica14_22_bits},
+{5, 1, 1, helvetica14_23_bits},
+{5, 1, 1, helvetica14_24_bits},
+{5, 1, 1, helvetica14_25_bits},
+{5, 1, 1, helvetica14_26_bits},
+{5, 1, 1, helvetica14_27_bits},
+{5, 1, 1, helvetica14_28_bits},
+{5, 1, 1, helvetica14_29_bits},
+{5, 1, 1, helvetica14_30_bits},
+{5, 1, 1, helvetica14_31_bits},
+{7, 1, 1, helvetica14_32_bits},
+{1, 11, 11, helvetica14_33_bits},
+{3, 3, 11, helvetica14_34_bits},
+{7, 10, 10, helvetica14_35_bits},
+{7, 14, 12, helvetica14_36_bits},
+{11, 11, 11, helvetica14_37_bits},
+{8, 10, 10, helvetica14_38_bits},
+{2, 3, 11, helvetica14_39_bits},
+{3, 14, 11, helvetica14_40_bits},
+{3, 14, 11, helvetica14_41_bits},
+{5, 5, 11, helvetica14_42_bits},
+{7, 7, 8, helvetica14_43_bits},
+{2, 4, 2, helvetica14_44_bits},
+{7, 1, 5, helvetica14_45_bits},
+{1, 2, 2, helvetica14_46_bits},
+{4, 11, 11, helvetica14_47_bits},
+{6, 11, 11, helvetica14_48_bits},
+{3, 11, 11, helvetica14_49_bits},
+{6, 11, 11, helvetica14_50_bits},
+{6, 11, 11, helvetica14_51_bits},
+{7, 11, 11, helvetica14_52_bits},
+{6, 11, 11, helvetica14_53_bits},
+{6, 11, 11, helvetica14_54_bits},
+{6, 11, 11, helvetica14_55_bits},
+{6, 11, 11, helvetica14_56_bits},
+{6, 11, 11, helvetica14_57_bits},
+{1, 8, 8, helvetica14_58_bits},
+{2, 10, 8, helvetica14_59_bits},
+{6, 5, 7, helvetica14_60_bits},
+{6, 3, 6, helvetica14_61_bits},
+{6, 5, 7, helvetica14_62_bits},
+{6, 11, 11, helvetica14_63_bits},
+{11, 12, 11, helvetica14_64_bits},
+{9, 11, 11, helvetica14_65_bits},
+{7, 11, 11, helvetica14_66_bits},
+{8, 11, 11, helvetica14_67_bits},
+{8, 11, 11, helvetica14_68_bits},
+{7, 11, 11, helvetica14_69_bits},
+{7, 11, 11, helvetica14_70_bits},
+{9, 11, 11, helvetica14_71_bits},
+{8, 11, 11, helvetica14_72_bits},
+{1, 11, 11, helvetica14_73_bits},
+{6, 11, 11, helvetica14_74_bits},
+{8, 11, 11, helvetica14_75_bits},
+{6, 11, 11, helvetica14_76_bits},
+{11, 11, 11, helvetica14_77_bits},
+{8, 11, 11, helvetica14_78_bits},
+{9, 11, 11, helvetica14_79_bits},
+{7, 11, 11, helvetica14_80_bits},
+{9, 11, 11, helvetica14_81_bits},
+{8, 11, 11, helvetica14_82_bits},
+{7, 11, 11, helvetica14_83_bits},
+{9, 11, 11, helvetica14_84_bits},
+{8, 11, 11, helvetica14_85_bits},
+{9, 11, 11, helvetica14_86_bits},
+{13, 11, 11, helvetica14_87_bits},
+{9, 11, 11, helvetica14_88_bits},
+{9, 11, 11, helvetica14_89_bits},
+{7, 11, 11, helvetica14_90_bits},
+{3, 14, 11, helvetica14_91_bits},
+{4, 11, 11, helvetica14_92_bits},
+{3, 14, 11, helvetica14_93_bits},
+{5, 5, 11, helvetica14_94_bits},
+{8, 1, -2, helvetica14_95_bits},
+{2, 3, 11, helvetica14_96_bits},
+{7, 8, 8, helvetica14_97_bits},
+{6, 11, 11, helvetica14_98_bits},
+{6, 8, 8, helvetica14_99_bits},
+{6, 11, 11, helvetica14_100_bits},
+{6, 8, 8, helvetica14_101_bits},
+{4, 11, 11, helvetica14_102_bits},
+{6, 11, 8, helvetica14_103_bits},
+{6, 11, 11, helvetica14_104_bits},
+{1, 11, 11, helvetica14_105_bits},
+{3, 14, 11, helvetica14_106_bits},
+{6, 11, 11, helvetica14_107_bits},
+{1, 11, 11, helvetica14_108_bits},
+{9, 8, 8, helvetica14_109_bits},
+{6, 8, 8, helvetica14_110_bits},
+{6, 8, 8, helvetica14_111_bits},
+{6, 11, 8, helvetica14_112_bits},
+{6, 11, 8, helvetica14_113_bits},
+{4, 8, 8, helvetica14_114_bits},
+{5, 8, 8, helvetica14_115_bits},
+{4, 10, 10, helvetica14_116_bits},
+{6, 8, 8, helvetica14_117_bits},
+{7, 8, 8, helvetica14_118_bits},
+{9, 8, 8, helvetica14_119_bits},
+{7, 8, 8, helvetica14_120_bits},
+{7, 11, 8, helvetica14_121_bits},
+{6, 8, 8, helvetica14_122_bits},
+{5, 14, 11, helvetica14_123_bits},
+{1, 14, 11, helvetica14_124_bits},
+{5, 14, 11, helvetica14_125_bits},
+{6, 3, 6, helvetica14_126_bits},
+{5, 1, 1, helvetica14_127_bits},
+{5, 1, 1, helvetica14_128_bits},
+{5, 1, 1, helvetica14_129_bits},
+{5, 1, 1, helvetica14_130_bits},
+{5, 1, 1, helvetica14_131_bits},
+{5, 1, 1, helvetica14_132_bits},
+{5, 1, 1, helvetica14_133_bits},
+{5, 1, 1, helvetica14_134_bits},
+{5, 1, 1, helvetica14_135_bits},
+{5, 1, 1, helvetica14_136_bits},
+{5, 1, 1, helvetica14_137_bits},
+{5, 1, 1, helvetica14_138_bits},
+{5, 1, 1, helvetica14_139_bits},
+{5, 1, 1, helvetica14_140_bits},
+{5, 1, 1, helvetica14_141_bits},
+{5, 1, 1, helvetica14_142_bits},
+{5, 1, 1, helvetica14_143_bits},
+{5, 1, 1, helvetica14_144_bits},
+{5, 1, 1, helvetica14_145_bits},
+{5, 1, 1, helvetica14_146_bits},
+{5, 1, 1, helvetica14_147_bits},
+{5, 1, 1, helvetica14_148_bits},
+{5, 1, 1, helvetica14_149_bits},
+{5, 1, 1, helvetica14_150_bits},
+{5, 1, 1, helvetica14_151_bits},
+{5, 1, 1, helvetica14_152_bits},
+{5, 1, 1, helvetica14_153_bits},
+{5, 1, 1, helvetica14_154_bits},
+{5, 1, 1, helvetica14_155_bits},
+{5, 1, 1, helvetica14_156_bits},
+{5, 1, 1, helvetica14_157_bits},
+{5, 1, 1, helvetica14_158_bits},
+{5, 1, 1, helvetica14_159_bits},
+{1, 1, 1, helvetica14_160_bits},
+{1, 11, 8, helvetica14_161_bits},
+{6, 10, 9, helvetica14_162_bits},
+{7, 11, 11, helvetica14_163_bits},
+{6, 6, 8, helvetica14_164_bits},
+{7, 11, 11, helvetica14_165_bits},
+{1, 14, 11, helvetica14_166_bits},
+{5, 14, 11, helvetica14_167_bits},
+{5, 1, 10, helvetica14_168_bits},
+{10, 11, 11, helvetica14_169_bits},
+{4, 7, 11, helvetica14_170_bits},
+{6, 5, 7, helvetica14_171_bits},
+{7, 4, 6, helvetica14_172_bits},
+{3, 1, 5, helvetica14_173_bits},
+{10, 11, 11, helvetica14_174_bits},
+{4, 1, 10, helvetica14_175_bits},
+{4, 4, 11, helvetica14_176_bits},
+{7, 9, 9, helvetica14_177_bits},
+{4, 6, 11, helvetica14_178_bits},
+{4, 6, 11, helvetica14_179_bits},
+{2, 2, 11, helvetica14_180_bits},
+{6, 11, 8, helvetica14_181_bits},
+{7, 14, 11, helvetica14_182_bits},
+{2, 1, 5, helvetica14_183_bits},
+{4, 3, 0, helvetica14_184_bits},
+{2, 6, 11, helvetica14_185_bits},
+{4, 7, 11, helvetica14_186_bits},
+{6, 5, 7, helvetica14_187_bits},
+{10, 11, 11, helvetica14_188_bits},
+{9, 11, 11, helvetica14_189_bits},
+{11, 11, 11, helvetica14_190_bits},
+{6, 11, 8, helvetica14_191_bits},
+{9, 14, 14, helvetica14_192_bits},
+{9, 14, 14, helvetica14_193_bits},
+{9, 14, 14, helvetica14_194_bits},
+{9, 14, 14, helvetica14_195_bits},
+{9, 13, 13, helvetica14_196_bits},
+{9, 14, 14, helvetica14_197_bits},
+{12, 11, 11, helvetica14_198_bits},
+{8, 14, 11, helvetica14_199_bits},
+{7, 14, 14, helvetica14_200_bits},
+{7, 14, 14, helvetica14_201_bits},
+{7, 14, 14, helvetica14_202_bits},
+{7, 13, 13, helvetica14_203_bits},
+{2, 14, 14, helvetica14_204_bits},
+{2, 14, 14, helvetica14_205_bits},
+{4, 14, 14, helvetica14_206_bits},
+{5, 13, 13, helvetica14_207_bits},
+{9, 11, 11, helvetica14_208_bits},
+{8, 14, 14, helvetica14_209_bits},
+{9, 14, 14, helvetica14_210_bits},
+{9, 14, 14, helvetica14_211_bits},
+{9, 14, 14, helvetica14_212_bits},
+{9, 14, 14, helvetica14_213_bits},
+{9, 13, 13, helvetica14_214_bits},
+{7, 7, 8, helvetica14_215_bits},
+{11, 11, 11, helvetica14_216_bits},
+{8, 14, 14, helvetica14_217_bits},
+{8, 14, 14, helvetica14_218_bits},
+{8, 14, 14, helvetica14_219_bits},
+{8, 13, 13, helvetica14_220_bits},
+{9, 14, 14, helvetica14_221_bits},
+{7, 11, 11, helvetica14_222_bits},
+{5, 11, 11, helvetica14_223_bits},
+{7, 11, 11, helvetica14_224_bits},
+{7, 11, 11, helvetica14_225_bits},
+{7, 11, 11, helvetica14_226_bits},
+{7, 11, 11, helvetica14_227_bits},
+{7, 11, 11, helvetica14_228_bits},
+{7, 12, 12, helvetica14_229_bits},
+{11, 8, 8, helvetica14_230_bits},
+{6, 11, 8, helvetica14_231_bits},
+{6, 11, 11, helvetica14_232_bits},
+{6, 11, 11, helvetica14_233_bits},
+{6, 11, 11, helvetica14_234_bits},
+{6, 11, 11, helvetica14_235_bits},
+{2, 11, 11, helvetica14_236_bits},
+{2, 11, 11, helvetica14_237_bits},
+{4, 11, 11, helvetica14_238_bits},
+{3, 11, 11, helvetica14_239_bits},
+{6, 11, 11, helvetica14_240_bits},
+{6, 11, 11, helvetica14_241_bits},
+{6, 11, 11, helvetica14_242_bits},
+{6, 11, 11, helvetica14_243_bits},
+{6, 11, 11, helvetica14_244_bits},
+{6, 11, 11, helvetica14_245_bits},
+{6, 11, 11, helvetica14_246_bits},
+{7, 7, 8, helvetica14_247_bits},
+{8, 8, 8, helvetica14_248_bits},
+{6, 11, 11, helvetica14_249_bits},
+{6, 11, 11, helvetica14_250_bits},
+{6, 11, 11, helvetica14_251_bits},
+{6, 11, 11, helvetica14_252_bits},
+{7, 14, 11, helvetica14_253_bits},
+{6, 14, 11, helvetica14_254_bits},
+{7, 14, 11, helvetica14_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica18.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica18.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica18.bdf	(revision 22322)
@@ -0,0 +1,3830 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Helvetica-Medium-R-Normal--18-180-75-75-P-98-ISO8859-1
+SIZE 18 75 75
+FONTBOUNDINGBOX 18 22 -1 -4
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Helvetica"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 18
+POINT_SIZE 180
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 98
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 14
+X_HEIGHT 10
+FACE_NAME "Helvetica"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.  "
+_DEC_DEVICE_FONTNAMES "PS=Helvetica"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2, 11-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Helvetica"
+FONT "-Adobe-Helvetica-Medium-R-Normal--18-180-75-75-P-98-ISO8859-1"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 11
+DEFAULT_CHAR 32
+FONT_ASCENT 16
+FONT_DESCENT 5
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 2 14 2 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+80
+80
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 5 0 9
+BITMAP
+d8
+d8
+d8
+90
+90
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 10 13 0 0
+BITMAP
+0900
+0900
+0900
+7fc0
+7fc0
+1200
+1200
+1200
+ff80
+ff80
+2400
+2400
+2400
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 16 1 -2
+BITMAP
+0800
+3e00
+7f00
+cb00
+c800
+e800
+7800
+3e00
+0f00
+0980
+c980
+eb80
+7f00
+3e00
+0800
+0800
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 853 0
+DWIDTH 16 0
+BBX 14 13 1 0
+BITMAP
+7860
+fcc0
+ccc0
+cd80
+fd80
+7b00
+0300
+0678
+06fc
+0ccc
+0ccc
+18fc
+1878
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 13 1 0
+BITMAP
+3c00
+7e00
+6600
+6600
+3c00
+7c00
+eec0
+c6c0
+c3c0
+c380
+e7c0
+7ee0
+3c70
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 5 1 9
+BITMAP
+c0
+c0
+40
+40
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 18 1 -4
+BITMAP
+10
+30
+60
+60
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+60
+60
+30
+10
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 18 1 -4
+BITMAP
+80
+c0
+60
+60
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+60
+60
+c0
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 6 1 8
+BITMAP
+20
+20
+f8
+70
+70
+88
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+18
+18
+18
+18
+ff
+ff
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 2 5 1 -3
+BITMAP
+c0
+c0
+40
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 8 2 1 4
+BITMAP
+ff
+ff
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 2 2 1 0
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 14 0 0
+BITMAP
+18
+18
+10
+10
+30
+30
+20
+20
+60
+60
+40
+40
+c0
+c0
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+3c
+7e
+66
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+66
+7e
+3c
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 5 13 2 0
+BITMAP
+18
+f8
+f8
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+3c
+fe
+c3
+03
+07
+0e
+1c
+38
+70
+e0
+c0
+ff
+ff
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+3c
+7e
+c3
+c3
+06
+1c
+1e
+07
+03
+c3
+c7
+7e
+3c
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 13 1 0
+BITMAP
+0300
+0700
+0f00
+1b00
+3300
+3300
+6300
+c300
+ff80
+ff80
+0300
+0300
+0300
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+fe
+fe
+c0
+c0
+fc
+fe
+c7
+03
+03
+c3
+c7
+fe
+7c
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+3c
+7f
+63
+c0
+c0
+dc
+fe
+c3
+c3
+c3
+e3
+7e
+3c
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+ff
+ff
+03
+06
+0c
+0c
+18
+18
+30
+30
+30
+60
+60
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+3c
+7e
+e7
+c3
+c3
+66
+7e
+66
+c3
+c3
+e7
+7e
+3c
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+3c
+7e
+c7
+c3
+c3
+c3
+7f
+3b
+03
+03
+c6
+fe
+7c
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 2 10 1 0
+BITMAP
+c0
+c0
+00
+00
+00
+00
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 2 13 1 -3
+BITMAP
+c0
+c0
+00
+00
+00
+00
+00
+00
+c0
+c0
+40
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+03
+0f
+3c
+70
+c0
+70
+3c
+0f
+03
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 7 6 2 2
+BITMAP
+fe
+fe
+00
+00
+fe
+fe
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+c0
+f0
+3c
+0e
+03
+0e
+3c
+f0
+c0
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 7 14 1 0
+BITMAP
+7c
+fe
+c6
+c6
+0e
+1c
+38
+30
+30
+30
+00
+00
+30
+30
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 960 0
+DWIDTH 18 0
+BBX 16 17 1 -3
+BITMAP
+03f0
+0ffc
+1c0e
+3006
+63b3
+6773
+c633
+cc63
+cc66
+cc66
+cccc
+cff8
+6770
+7000
+3800
+1ff0
+07e0
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 12 14 0 0
+BITMAP
+0600
+0600
+0f00
+0f00
+1980
+1980
+30c0
+30c0
+3fc0
+7fe0
+6060
+6060
+c030
+c030
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+ff00
+ff80
+c1c0
+c0c0
+c0c0
+c180
+ff80
+ffc0
+c0e0
+c060
+c060
+c0e0
+ffc0
+ff80
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 14 1 0
+BITMAP
+0f80
+3fe0
+7070
+6030
+e000
+c000
+c000
+c000
+c000
+e000
+6030
+7070
+3fe0
+0f80
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+ff00
+ff80
+c1c0
+c0c0
+c060
+c060
+c060
+c060
+c060
+c060
+c0c0
+c1c0
+ff80
+ff00
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+ff80
+ff80
+c000
+c000
+c000
+c000
+ff00
+ff00
+c000
+c000
+c000
+c000
+ff80
+ff80
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+ff80
+ff80
+c000
+c000
+c000
+c000
+ff00
+ff00
+c000
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 14 1 0
+BITMAP
+0f80
+3fe0
+7070
+6030
+e030
+c000
+c000
+c1f0
+c1f0
+e030
+6030
+7070
+3ff0
+0fb0
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+c060
+c060
+c060
+c060
+c060
+c060
+ffe0
+ffe0
+c060
+c060
+c060
+c060
+c060
+c060
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 2 14 2 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+03
+03
+03
+03
+03
+03
+03
+03
+03
+c3
+c3
+e7
+7e
+3c
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 14 1 0
+BITMAP
+c0e0
+c1c0
+c380
+c700
+ce00
+dc00
+f800
+fc00
+ce00
+c700
+c380
+c1c0
+c0e0
+c070
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ff
+ff
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 853 0
+DWIDTH 16 0
+BBX 14 14 1 0
+BITMAP
+c00c
+c00c
+e01c
+e01c
+f03c
+f03c
+d86c
+d86c
+cccc
+cccc
+c48c
+c78c
+c30c
+c30c
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+c060
+e060
+f060
+f060
+d860
+cc60
+cc60
+c660
+c660
+c360
+c1e0
+c1e0
+c0e0
+c060
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 14 1 0
+BITMAP
+0f80
+3fe0
+7070
+6030
+e038
+c018
+c018
+c018
+c018
+e038
+6030
+7070
+3fe0
+0f80
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 14 1 0
+BITMAP
+ff00
+ff80
+c1c0
+c0c0
+c0c0
+c1c0
+ff80
+ff00
+c000
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 15 1 -1
+BITMAP
+0f80
+3fe0
+7070
+6030
+e038
+c018
+c018
+c018
+c018
+e1b8
+61b0
+70f0
+3fe0
+0fb0
+0030
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 14 1 0
+BITMAP
+ff00
+ff80
+c1c0
+c0c0
+c0c0
+c1c0
+ff80
+ff00
+c180
+c180
+c0c0
+c0c0
+c0c0
+c0c0
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+1f00
+7fc0
+e0e0
+c060
+e000
+7c00
+1f00
+03c0
+00e0
+0060
+c060
+e0e0
+7fc0
+3f00
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 14 1 0
+BITMAP
+ffc0
+ffc0
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+7fc0
+1f00
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 14 1 0
+BITMAP
+c030
+c030
+6060
+6060
+6060
+30c0
+30c0
+30c0
+1980
+1980
+1980
+0f00
+0f00
+0600
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 960 0
+DWIDTH 18 0
+BBX 16 14 1 0
+BITMAP
+c183
+c183
+c183
+c3c3
+63c6
+6246
+6666
+6666
+366c
+366c
+342c
+1c38
+1818
+1818
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+c060
+e0e0
+60c0
+71c0
+3180
+1b00
+0e00
+0e00
+1b00
+3180
+71c0
+60c0
+e0e0
+c060
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 14 1 0
+BITMAP
+c030
+c030
+6060
+6060
+30c0
+30c0
+1980
+0f00
+0600
+0600
+0600
+0600
+0600
+0600
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 14 1 0
+BITMAP
+ffc0
+ffc0
+00c0
+0180
+0300
+0600
+0c00
+1c00
+1800
+3000
+6000
+c000
+ffc0
+ffc0
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 4 18 1 -4
+BITMAP
+f0
+f0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+f0
+f0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 14 0 0
+BITMAP
+c0
+c0
+40
+40
+60
+60
+20
+20
+30
+30
+10
+10
+18
+18
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 4 18 0 -4
+BITMAP
+f0
+f0
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+f0
+f0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 5 1 8
+BITMAP
+10
+38
+6c
+c6
+82
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 10 2 0 -4
+BITMAP
+ffc0
+ffc0
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 5 1 9
+BITMAP
+40
+80
+80
+c0
+c0
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+7c
+ee
+c6
+0e
+7e
+e6
+c6
+c6
+ee
+76
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+c000
+c000
+c000
+c000
+de00
+ff00
+e300
+c180
+c180
+c180
+c180
+e300
+ff00
+de00
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+3e
+7f
+63
+c0
+c0
+c0
+c0
+63
+7f
+3e
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+0180
+0180
+0180
+0180
+3d80
+7f80
+6380
+c180
+c180
+c180
+c180
+6380
+7f80
+3d80
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+3c
+7e
+c3
+c3
+ff
+c0
+c0
+e3
+7f
+3c
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 6 14 0 0
+BITMAP
+1c
+3c
+30
+30
+fc
+fc
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 -4
+BITMAP
+3d80
+7f80
+6180
+c180
+c180
+c180
+c180
+6380
+7f80
+3d80
+0180
+6300
+7f00
+1c00
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+c0
+c0
+c0
+c0
+ce
+df
+e3
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 14 1 0
+BITMAP
+c0
+c0
+00
+00
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 4 18 -1 -4
+BITMAP
+30
+30
+00
+00
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+f0
+e0
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 14 1 0
+BITMAP
+c0
+c0
+c0
+c0
+c6
+cc
+d8
+f0
+f8
+d8
+cc
+ce
+c6
+c7
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 14 1 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 10 1 0
+BITMAP
+cc60
+def0
+e730
+c630
+c630
+c630
+c630
+c630
+c630
+c630
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+ce
+df
+e3
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+3e00
+7f00
+6300
+c180
+c180
+c180
+c180
+6300
+7f00
+3e00
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 -4
+BITMAP
+de00
+ff00
+e300
+c180
+c180
+c180
+c180
+e300
+ff00
+de00
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 -4
+BITMAP
+3d80
+7f80
+6380
+c180
+c180
+c180
+c180
+6380
+7f80
+3d80
+0180
+0180
+0180
+0180
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 5 10 1 0
+BITMAP
+d8
+d8
+e0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+3c
+7e
+c6
+c0
+fc
+3e
+06
+c6
+fc
+78
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 6 13 0 0
+BITMAP
+30
+30
+30
+fc
+fc
+30
+30
+30
+30
+30
+30
+38
+18
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+c7
+fb
+73
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+c3
+c3
+c3
+66
+66
+66
+24
+3c
+18
+18
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 10 1 0
+BITMAP
+c630
+c630
+c630
+6660
+6660
+6960
+2940
+39c0
+1980
+1980
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+c3
+e7
+66
+3c
+18
+18
+3c
+66
+e7
+c3
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 -4
+BITMAP
+c3
+c3
+c3
+66
+66
+66
+24
+3c
+18
+18
+18
+18
+70
+70
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+fe
+fe
+06
+0c
+18
+30
+60
+c0
+fe
+fe
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 6 18 0 -4
+BITMAP
+0c
+18
+30
+30
+30
+30
+30
+60
+c0
+60
+30
+30
+30
+30
+30
+30
+18
+0c
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 18 1 -4
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 6 18 0 -4
+BITMAP
+c0
+60
+30
+30
+30
+30
+30
+18
+0c
+18
+30
+30
+30
+30
+30
+30
+60
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 3 1 4
+BITMAP
+33
+7e
+cc
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 2 14 2 -4
+BITMAP
+c0
+c0
+00
+00
+40
+40
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 -2
+BITMAP
+04
+04
+3e
+7f
+6b
+c8
+c8
+c8
+c8
+6b
+7f
+3e
+10
+10
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 13 0 0
+BITMAP
+1e00
+3f00
+6180
+6180
+6000
+3000
+7e00
+1800
+1800
+3000
+6080
+ff80
+df00
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 7 1 3
+BITMAP
+c3
+ff
+66
+66
+66
+ff
+c3
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+c3
+c3
+66
+66
+66
+3c
+ff
+18
+ff
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 17 1 -3
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+00
+00
+00
+00
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 18 1 -4
+BITMAP
+3c
+7e
+c3
+c3
+f0
+7c
+6e
+c7
+c3
+e3
+73
+3e
+0e
+07
+c3
+c3
+7e
+3c
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 5 2 0 11
+BITMAP
+d8
+d8
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+0f80
+3060
+4010
+4710
+8888
+9008
+9008
+9008
+8888
+4710
+4010
+3060
+0f80
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 8 1 6
+BITMAP
+70
+c8
+38
+48
+d8
+68
+00
+f8
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 8 1 1
+BITMAP
+12
+36
+6c
+d8
+d8
+6c
+36
+12
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 5 1 3
+BITMAP
+ff80
+ff80
+0180
+0180
+0180
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 2 1 4
+BITMAP
+f8
+f8
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 13 13 1 0
+BITMAP
+0f80
+3060
+4010
+4f90
+8848
+8848
+8f88
+8908
+8888
+4850
+4010
+3060
+0f80
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 1 0 12
+BITMAP
+f8
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 5 1 8
+BITMAP
+70
+d8
+88
+d8
+70
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 11 1 0
+BITMAP
+18
+18
+18
+ff
+ff
+18
+18
+18
+00
+ff
+ff
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 5 8 0 5
+BITMAP
+70
+f8
+98
+18
+30
+60
+f8
+f8
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 5 8 0 5
+BITMAP
+70
+f8
+98
+30
+30
+98
+f8
+70
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 4 3 0 11
+BITMAP
+30
+60
+c0
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 -4
+BITMAP
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+e7
+ff
+db
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 18 1 -4
+BITMAP
+3f
+72
+f2
+f2
+f2
+f2
+72
+32
+12
+12
+12
+12
+12
+12
+12
+12
+12
+12
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 2 1 4
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 5 0 -4
+BITMAP
+60
+70
+18
+d8
+f0
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 3 8 1 5
+BITMAP
+60
+e0
+e0
+60
+60
+60
+60
+60
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 8 1 6
+BITMAP
+70
+d8
+88
+88
+d8
+70
+00
+f8
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 8 1 1
+BITMAP
+90
+d8
+6c
+36
+36
+6c
+d8
+90
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+6060
+e060
+e0c0
+6180
+6180
+6310
+6230
+6670
+0cf0
+0db0
+19f8
+3030
+3030
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+6060
+e060
+e0c0
+6180
+6180
+6370
+62f8
+6698
+0c18
+0c30
+1860
+30f8
+30f8
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 14 13 0 0
+BITMAP
+7030
+f830
+9860
+30c0
+30c0
+9988
+f918
+7338
+0678
+06d8
+0cfc
+1818
+1818
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 7 14 1 -4
+BITMAP
+18
+18
+00
+00
+18
+18
+18
+38
+70
+e0
+c6
+c6
+fe
+7c
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 12 18 0 0
+BITMAP
+1800
+0c00
+0600
+0000
+0600
+0600
+0f00
+0f00
+1980
+1980
+30c0
+30c0
+3fc0
+7fe0
+6060
+6060
+c030
+c030
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 12 18 0 0
+BITMAP
+0180
+0300
+0600
+0000
+0600
+0600
+0f00
+0f00
+1980
+1980
+30c0
+30c0
+3fc0
+7fe0
+6060
+6060
+c030
+c030
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 12 18 0 0
+BITMAP
+0600
+0f00
+1980
+0000
+0600
+0600
+0f00
+0f00
+1980
+1980
+30c0
+30c0
+3fc0
+7fe0
+6060
+6060
+c030
+c030
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 12 18 0 0
+BITMAP
+0c80
+1680
+1300
+0000
+0600
+0600
+0f00
+0f00
+1980
+1980
+30c0
+30c0
+3fc0
+7fe0
+6060
+6060
+c030
+c030
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 12 17 0 0
+BITMAP
+1980
+1980
+0000
+0600
+0600
+0f00
+0f00
+1980
+1980
+30c0
+30c0
+3fc0
+7fe0
+6060
+6060
+c030
+c030
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 12 18 0 0
+BITMAP
+0f00
+1980
+1980
+0f00
+0600
+0600
+0f00
+0f00
+1980
+1980
+30c0
+30c0
+3fc0
+7fe0
+6060
+6060
+c030
+c030
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 960 0
+DWIDTH 18 0
+BBX 16 14 1 0
+BITMAP
+07ff
+07ff
+0d80
+0d80
+1980
+1980
+31fe
+31fe
+3f80
+7f80
+6180
+6180
+c1ff
+c1ff
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 18 1 -4
+BITMAP
+0f80
+3fe0
+7070
+6030
+e000
+c000
+c000
+c000
+c000
+e000
+6030
+7070
+3fe0
+0f80
+0e00
+0300
+1b00
+1e00
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 18 1 0
+BITMAP
+3000
+1800
+0c00
+0000
+ff80
+ff80
+c000
+c000
+c000
+c000
+ff00
+ff00
+c000
+c000
+c000
+c000
+ff80
+ff80
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 18 1 0
+BITMAP
+0300
+0600
+0c00
+0000
+ff80
+ff80
+c000
+c000
+c000
+c000
+ff00
+ff00
+c000
+c000
+c000
+c000
+ff80
+ff80
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 18 1 0
+BITMAP
+0c00
+1e00
+3300
+0000
+ff80
+ff80
+c000
+c000
+c000
+c000
+ff00
+ff00
+c000
+c000
+c000
+c000
+ff80
+ff80
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+3300
+3300
+0000
+ff80
+ff80
+c000
+c000
+c000
+c000
+ff00
+ff00
+c000
+c000
+c000
+c000
+ff80
+ff80
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 18 0 0
+BITMAP
+c0
+60
+30
+00
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 18 2 0
+BITMAP
+30
+60
+c0
+00
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 6 18 0 0
+BITMAP
+30
+78
+cc
+00
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 6 17 0 0
+BITMAP
+cc
+cc
+00
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 14 0 0
+BITMAP
+7f80
+7fc0
+60e0
+6060
+6030
+6030
+fc30
+fc30
+6030
+6030
+6060
+60e0
+7fc0
+7f80
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+0c80
+1680
+1300
+0000
+e060
+e060
+f060
+d860
+d860
+cc60
+cc60
+c660
+c660
+c360
+c1e0
+c1e0
+c0e0
+c060
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 18 1 0
+BITMAP
+0c00
+0600
+0300
+0000
+0f80
+3fe0
+7070
+6030
+e038
+c018
+c018
+c018
+c018
+e038
+6030
+7070
+3fe0
+0f80
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 18 1 0
+BITMAP
+00c0
+0180
+0300
+0000
+0f80
+3fe0
+7070
+6030
+e038
+c018
+c018
+c018
+c018
+e038
+6030
+7070
+3fe0
+0f80
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 18 1 0
+BITMAP
+0300
+0780
+0cc0
+0000
+0f80
+3fe0
+7070
+6030
+e038
+c018
+c018
+c018
+c018
+e038
+6030
+7070
+3fe0
+0f80
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 18 1 0
+BITMAP
+0640
+0b40
+0980
+0000
+0f80
+3fe0
+7070
+6030
+e038
+c018
+c018
+c018
+c018
+e038
+6030
+7070
+3fe0
+0f80
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+0d80
+0d80
+0000
+0f80
+3fe0
+7070
+6030
+e038
+c018
+c018
+c018
+c018
+e038
+6030
+7070
+3fe0
+0f80
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 10 9 0 0
+BITMAP
+c0c0
+6180
+3300
+1e00
+0c00
+1e00
+3300
+6180
+c0c0
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 14 14 0 0
+BITMAP
+07cc
+1ffc
+3838
+3078
+70dc
+61cc
+638c
+670c
+6e0c
+6c1c
+3818
+7838
+fff0
+c7c0
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+1800
+0c00
+0600
+0000
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+7fc0
+1f00
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+0300
+0600
+0c00
+0000
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+7fc0
+1f00
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+0600
+0f00
+1980
+0000
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+7fc0
+1f00
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+1980
+1980
+0000
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+7fc0
+1f00
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 18 1 0
+BITMAP
+0180
+0300
+0600
+0000
+c030
+c030
+6060
+6060
+30c0
+30c0
+1980
+0f00
+0600
+0600
+0600
+0600
+0600
+0600
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 14 1 0
+BITMAP
+c000
+c000
+c000
+ff00
+ff80
+c1c0
+c0c0
+c0c0
+c1c0
+ff80
+ff00
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+38
+7c
+c6
+c6
+c6
+c6
+dc
+dc
+c6
+c6
+c6
+c6
+de
+dc
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+60
+30
+18
+00
+7c
+ee
+c6
+0e
+7e
+e6
+c6
+c6
+ee
+76
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+0c
+18
+30
+00
+7c
+ee
+c6
+0e
+7e
+e6
+c6
+c6
+ee
+76
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+18
+3c
+66
+00
+7c
+ee
+c6
+0e
+7e
+e6
+c6
+c6
+ee
+76
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+32
+5a
+4c
+00
+7c
+ee
+c6
+0e
+7e
+e6
+c6
+c6
+ee
+76
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+6c
+6c
+00
+7c
+ee
+c6
+0e
+7e
+e6
+c6
+c6
+ee
+76
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+38
+6c
+6c
+38
+7c
+ee
+c6
+0e
+7e
+e6
+c6
+c6
+ee
+76
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 10 1 0
+BITMAP
+7de0
+eff0
+c618
+0e18
+7ff8
+e600
+c600
+c718
+eff8
+75e0
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 -4
+BITMAP
+3e
+7f
+63
+c0
+c0
+c0
+c0
+63
+7f
+3e
+38
+0c
+6c
+78
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+60
+30
+18
+00
+3c
+7e
+c3
+c3
+ff
+c0
+c0
+e3
+7f
+3c
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+06
+0c
+18
+00
+3c
+7e
+c3
+c3
+ff
+c0
+c0
+e3
+7f
+3c
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+18
+3c
+66
+00
+3c
+7e
+c3
+c3
+ff
+c0
+c0
+e3
+7f
+3c
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+36
+36
+00
+3c
+7e
+c3
+c3
+ff
+c0
+c0
+e3
+7f
+3c
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 4 14 0 0
+BITMAP
+c0
+60
+30
+00
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 4 14 0 0
+BITMAP
+30
+60
+c0
+00
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 6 14 -1 0
+BITMAP
+30
+78
+cc
+00
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 5 13 0 0
+BITMAP
+d8
+d8
+00
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+6000
+3600
+3800
+4c00
+3e00
+7f00
+6300
+c180
+c180
+c180
+c180
+6300
+7f00
+3e00
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+32
+5a
+4c
+00
+ce
+df
+e3
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+3000
+1800
+0c00
+0000
+3e00
+7f00
+6300
+c180
+c180
+c180
+c180
+6300
+7f00
+3e00
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+0600
+0c00
+1800
+0000
+3e00
+7f00
+6300
+c180
+c180
+c180
+c180
+6300
+7f00
+3e00
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+0c00
+1e00
+3300
+0000
+3e00
+7f00
+6300
+c180
+c180
+c180
+c180
+6300
+7f00
+3e00
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+1900
+2d00
+2600
+0000
+3e00
+7f00
+6300
+c180
+c180
+c180
+c180
+6300
+7f00
+3e00
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 1 0
+BITMAP
+3600
+3600
+0000
+3e00
+7f00
+6300
+c180
+c180
+c180
+c180
+6300
+7f00
+3e00
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 8 1 1
+BITMAP
+18
+18
+00
+ff
+ff
+00
+18
+18
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 10 0 0
+BITMAP
+0e60
+3fc0
+3180
+63c0
+66c0
+6cc0
+78c0
+3180
+7f80
+ce00
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+30
+18
+0c
+00
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+c7
+fb
+73
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+06
+0c
+18
+00
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+c7
+fb
+73
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+18
+3c
+66
+00
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+c7
+fb
+73
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+66
+66
+00
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+c7
+fb
+73
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 18 1 -4
+BITMAP
+06
+0c
+18
+00
+c3
+c3
+c3
+66
+66
+66
+24
+3c
+18
+18
+18
+18
+70
+70
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 18 1 -4
+BITMAP
+c000
+c000
+c000
+c000
+de00
+ff00
+e300
+c180
+c180
+c180
+c180
+e300
+ff00
+de00
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 17 1 -4
+BITMAP
+66
+66
+00
+c3
+c3
+c3
+66
+66
+66
+24
+3c
+18
+18
+18
+18
+70
+70
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica18.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica18.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica18.h	(revision 22322)
@@ -0,0 +1,988 @@
+static unsigned char helvetica18_0_bits[] = {
+0x00};
+static unsigned char helvetica18_1_bits[] = {
+0x00};
+static unsigned char helvetica18_2_bits[] = {
+0x00};
+static unsigned char helvetica18_3_bits[] = {
+0x00};
+static unsigned char helvetica18_4_bits[] = {
+0x00};
+static unsigned char helvetica18_5_bits[] = {
+0x00};
+static unsigned char helvetica18_6_bits[] = {
+0x00};
+static unsigned char helvetica18_7_bits[] = {
+0x00};
+static unsigned char helvetica18_8_bits[] = {
+0x00};
+static unsigned char helvetica18_9_bits[] = {
+0x00};
+static unsigned char helvetica18_10_bits[] = {
+0x00};
+static unsigned char helvetica18_11_bits[] = {
+0x00};
+static unsigned char helvetica18_12_bits[] = {
+0x00};
+static unsigned char helvetica18_13_bits[] = {
+0x00};
+static unsigned char helvetica18_14_bits[] = {
+0x00};
+static unsigned char helvetica18_15_bits[] = {
+0x00};
+static unsigned char helvetica18_16_bits[] = {
+0x00};
+static unsigned char helvetica18_17_bits[] = {
+0x00};
+static unsigned char helvetica18_18_bits[] = {
+0x00};
+static unsigned char helvetica18_19_bits[] = {
+0x00};
+static unsigned char helvetica18_20_bits[] = {
+0x00};
+static unsigned char helvetica18_21_bits[] = {
+0x00};
+static unsigned char helvetica18_22_bits[] = {
+0x00};
+static unsigned char helvetica18_23_bits[] = {
+0x00};
+static unsigned char helvetica18_24_bits[] = {
+0x00};
+static unsigned char helvetica18_25_bits[] = {
+0x00};
+static unsigned char helvetica18_26_bits[] = {
+0x00};
+static unsigned char helvetica18_27_bits[] = {
+0x00};
+static unsigned char helvetica18_28_bits[] = {
+0x00};
+static unsigned char helvetica18_29_bits[] = {
+0x00};
+static unsigned char helvetica18_30_bits[] = {
+0x00};
+static unsigned char helvetica18_31_bits[] = {
+0x00};
+static unsigned char helvetica18_32_bits[] = {
+0x00};
+static unsigned char helvetica18_33_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 
+0x03, 0x03};
+static unsigned char helvetica18_34_bits[] = {
+0x1b, 0x1b, 0x1b, 0x09, 0x09};
+static unsigned char helvetica18_35_bits[] = {
+0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0xfe, 0x03, 0xfe, 0x03, 0x48, 0x00, 
+0x48, 0x00, 0x48, 0x00, 0xff, 0x01, 0xff, 0x01, 0x24, 0x00, 0x24, 0x00, 
+0x24, 0x00};
+static unsigned char helvetica18_36_bits[] = {
+0x10, 0x00, 0x7c, 0x00, 0xfe, 0x00, 0xd3, 0x00, 0x13, 0x00, 0x17, 0x00, 
+0x1e, 0x00, 0x7c, 0x00, 0xf0, 0x00, 0x90, 0x01, 0x93, 0x01, 0xd7, 0x01, 
+0xfe, 0x00, 0x7c, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char helvetica18_37_bits[] = {
+0x1e, 0x06, 0x3f, 0x03, 0x33, 0x03, 0xb3, 0x01, 0xbf, 0x01, 0xde, 0x00, 
+0xc0, 0x00, 0x60, 0x1e, 0x60, 0x3f, 0x30, 0x33, 0x30, 0x33, 0x18, 0x3f, 
+0x18, 0x1e};
+static unsigned char helvetica18_38_bits[] = {
+0x3c, 0x00, 0x7e, 0x00, 0x66, 0x00, 0x66, 0x00, 0x3c, 0x00, 0x3e, 0x00, 
+0x77, 0x03, 0x63, 0x03, 0xc3, 0x03, 0xc3, 0x01, 0xe7, 0x03, 0x7e, 0x07, 
+0x3c, 0x0e};
+static unsigned char helvetica18_39_bits[] = {
+0x03, 0x03, 0x02, 0x02, 0x01};
+static unsigned char helvetica18_40_bits[] = {
+0x08, 0x0c, 0x06, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x06, 0x06, 0x0c, 0x08};
+static unsigned char helvetica18_41_bits[] = {
+0x01, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x06, 0x06, 0x03, 0x01};
+static unsigned char helvetica18_42_bits[] = {
+0x04, 0x04, 0x1f, 0x0e, 0x0e, 0x11};
+static unsigned char helvetica18_43_bits[] = {
+0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18};
+static unsigned char helvetica18_44_bits[] = {
+0x03, 0x03, 0x02, 0x02, 0x01};
+static unsigned char helvetica18_45_bits[] = {
+0xff, 0xff};
+static unsigned char helvetica18_46_bits[] = {
+0x03, 0x03};
+static unsigned char helvetica18_47_bits[] = {
+0x18, 0x18, 0x08, 0x08, 0x0c, 0x0c, 0x04, 0x04, 0x06, 0x06, 0x02, 0x02, 
+0x03, 0x03};
+static unsigned char helvetica18_48_bits[] = {
+0x3c, 0x7e, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x7e, 
+0x3c};
+static unsigned char helvetica18_49_bits[] = {
+0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18};
+static unsigned char helvetica18_50_bits[] = {
+0x3c, 0x7f, 0xc3, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x03, 0xff, 
+0xff};
+static unsigned char helvetica18_51_bits[] = {
+0x3c, 0x7e, 0xc3, 0xc3, 0x60, 0x38, 0x78, 0xe0, 0xc0, 0xc3, 0xe3, 0x7e, 
+0x3c};
+static unsigned char helvetica18_52_bits[] = {
+0xc0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xcc, 0x00, 
+0xc6, 0x00, 0xc3, 0x00, 0xff, 0x01, 0xff, 0x01, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00};
+static unsigned char helvetica18_53_bits[] = {
+0x7f, 0x7f, 0x03, 0x03, 0x3f, 0x7f, 0xe3, 0xc0, 0xc0, 0xc3, 0xe3, 0x7f, 
+0x3e};
+static unsigned char helvetica18_54_bits[] = {
+0x3c, 0xfe, 0xc6, 0x03, 0x03, 0x3b, 0x7f, 0xc3, 0xc3, 0xc3, 0xc7, 0x7e, 
+0x3c};
+static unsigned char helvetica18_55_bits[] = {
+0xff, 0xff, 0xc0, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x06, 
+0x06};
+static unsigned char helvetica18_56_bits[] = {
+0x3c, 0x7e, 0xe7, 0xc3, 0xc3, 0x66, 0x7e, 0x66, 0xc3, 0xc3, 0xe7, 0x7e, 
+0x3c};
+static unsigned char helvetica18_57_bits[] = {
+0x3c, 0x7e, 0xe3, 0xc3, 0xc3, 0xc3, 0xfe, 0xdc, 0xc0, 0xc0, 0x63, 0x7f, 
+0x3e};
+static unsigned char helvetica18_58_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03};
+static unsigned char helvetica18_59_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x02, 0x02, 
+0x01};
+static unsigned char helvetica18_60_bits[] = {
+0xc0, 0xf0, 0x3c, 0x0e, 0x03, 0x0e, 0x3c, 0xf0, 0xc0};
+static unsigned char helvetica18_61_bits[] = {
+0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f};
+static unsigned char helvetica18_62_bits[] = {
+0x03, 0x0f, 0x3c, 0x70, 0xc0, 0x70, 0x3c, 0x0f, 0x03};
+static unsigned char helvetica18_63_bits[] = {
+0x3e, 0x7f, 0x63, 0x63, 0x70, 0x38, 0x1c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 
+0x0c, 0x0c};
+static unsigned char helvetica18_64_bits[] = {
+0xc0, 0x0f, 0xf0, 0x3f, 0x38, 0x70, 0x0c, 0x60, 0xc6, 0xcd, 0xe6, 0xce, 
+0x63, 0xcc, 0x33, 0xc6, 0x33, 0x66, 0x33, 0x66, 0x33, 0x33, 0xf3, 0x1f, 
+0xe6, 0x0e, 0x0e, 0x00, 0x1c, 0x00, 0xf8, 0x0f, 0xe0, 0x07};
+static unsigned char helvetica18_65_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x98, 0x01, 
+0x0c, 0x03, 0x0c, 0x03, 0xfc, 0x03, 0xfe, 0x07, 0x06, 0x06, 0x06, 0x06, 
+0x03, 0x0c, 0x03, 0x0c};
+static unsigned char helvetica18_66_bits[] = {
+0xff, 0x00, 0xff, 0x01, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x01, 
+0xff, 0x01, 0xff, 0x03, 0x03, 0x07, 0x03, 0x06, 0x03, 0x06, 0x03, 0x07, 
+0xff, 0x03, 0xff, 0x01};
+static unsigned char helvetica18_67_bits[] = {
+0xf0, 0x01, 0xfc, 0x07, 0x0e, 0x0e, 0x06, 0x0c, 0x07, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x07, 0x00, 0x06, 0x0c, 0x0e, 0x0e, 
+0xfc, 0x07, 0xf0, 0x01};
+static unsigned char helvetica18_68_bits[] = {
+0xff, 0x00, 0xff, 0x01, 0x83, 0x03, 0x03, 0x03, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x03, 0x83, 0x03, 
+0xff, 0x01, 0xff, 0x00};
+static unsigned char helvetica18_69_bits[] = {
+0xff, 0x01, 0xff, 0x01, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0xff, 0x00, 0xff, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0xff, 0x01, 0xff, 0x01};
+static unsigned char helvetica18_70_bits[] = {
+0xff, 0x01, 0xff, 0x01, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0xff, 0x00, 0xff, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00};
+static unsigned char helvetica18_71_bits[] = {
+0xf0, 0x01, 0xfc, 0x07, 0x0e, 0x0e, 0x06, 0x0c, 0x07, 0x0c, 0x03, 0x00, 
+0x03, 0x00, 0x83, 0x0f, 0x83, 0x0f, 0x07, 0x0c, 0x06, 0x0c, 0x0e, 0x0e, 
+0xfc, 0x0f, 0xf0, 0x0d};
+static unsigned char helvetica18_72_bits[] = {
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0xff, 0x07, 0xff, 0x07, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06};
+static unsigned char helvetica18_73_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03};
+static unsigned char helvetica18_74_bits[] = {
+0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc3, 0xc3, 0xe7, 
+0x7e, 0x3c};
+static unsigned char helvetica18_75_bits[] = {
+0x03, 0x07, 0x83, 0x03, 0xc3, 0x01, 0xe3, 0x00, 0x73, 0x00, 0x3b, 0x00, 
+0x1f, 0x00, 0x3f, 0x00, 0x73, 0x00, 0xe3, 0x00, 0xc3, 0x01, 0x83, 0x03, 
+0x03, 0x07, 0x03, 0x0e};
+static unsigned char helvetica18_76_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0xff, 0xff};
+static unsigned char helvetica18_77_bits[] = {
+0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x07, 0x38, 0x0f, 0x3c, 0x0f, 0x3c, 
+0x1b, 0x36, 0x1b, 0x36, 0x33, 0x33, 0x33, 0x33, 0x23, 0x31, 0xe3, 0x31, 
+0xc3, 0x30, 0xc3, 0x30};
+static unsigned char helvetica18_78_bits[] = {
+0x03, 0x06, 0x07, 0x06, 0x0f, 0x06, 0x0f, 0x06, 0x1b, 0x06, 0x33, 0x06, 
+0x33, 0x06, 0x63, 0x06, 0x63, 0x06, 0xc3, 0x06, 0x83, 0x07, 0x83, 0x07, 
+0x03, 0x07, 0x03, 0x06};
+static unsigned char helvetica18_79_bits[] = {
+0xf0, 0x01, 0xfc, 0x07, 0x0e, 0x0e, 0x06, 0x0c, 0x07, 0x1c, 0x03, 0x18, 
+0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x07, 0x1c, 0x06, 0x0c, 0x0e, 0x0e, 
+0xfc, 0x07, 0xf0, 0x01};
+static unsigned char helvetica18_80_bits[] = {
+0xff, 0x00, 0xff, 0x01, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 
+0xff, 0x01, 0xff, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00};
+static unsigned char helvetica18_81_bits[] = {
+0xf0, 0x01, 0xfc, 0x07, 0x0e, 0x0e, 0x06, 0x0c, 0x07, 0x1c, 0x03, 0x18, 
+0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x87, 0x1d, 0x86, 0x0d, 0x0e, 0x0f, 
+0xfc, 0x07, 0xf0, 0x0d, 0x00, 0x0c};
+static unsigned char helvetica18_82_bits[] = {
+0xff, 0x00, 0xff, 0x01, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 
+0xff, 0x01, 0xff, 0x00, 0x83, 0x01, 0x83, 0x01, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica18_83_bits[] = {
+0xf8, 0x00, 0xfe, 0x03, 0x07, 0x07, 0x03, 0x06, 0x07, 0x00, 0x3e, 0x00, 
+0xf8, 0x00, 0xc0, 0x03, 0x00, 0x07, 0x00, 0x06, 0x03, 0x06, 0x07, 0x07, 
+0xfe, 0x03, 0xfc, 0x00};
+static unsigned char helvetica18_84_bits[] = {
+0xff, 0x03, 0xff, 0x03, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x30, 0x00};
+static unsigned char helvetica18_85_bits[] = {
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 
+0xfe, 0x03, 0xf8, 0x00};
+static unsigned char helvetica18_86_bits[] = {
+0x03, 0x0c, 0x03, 0x0c, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0c, 0x03, 
+0x0c, 0x03, 0x0c, 0x03, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0xf0, 0x00, 
+0xf0, 0x00, 0x60, 0x00};
+static unsigned char helvetica18_87_bits[] = {
+0x83, 0xc1, 0x83, 0xc1, 0x83, 0xc1, 0xc3, 0xc3, 0xc6, 0x63, 0x46, 0x62, 
+0x66, 0x66, 0x66, 0x66, 0x6c, 0x36, 0x6c, 0x36, 0x2c, 0x34, 0x38, 0x1c, 
+0x18, 0x18, 0x18, 0x18};
+static unsigned char helvetica18_88_bits[] = {
+0x03, 0x06, 0x07, 0x07, 0x06, 0x03, 0x8e, 0x03, 0x8c, 0x01, 0xd8, 0x00, 
+0x70, 0x00, 0x70, 0x00, 0xd8, 0x00, 0x8c, 0x01, 0x8e, 0x03, 0x06, 0x03, 
+0x07, 0x07, 0x03, 0x06};
+static unsigned char helvetica18_89_bits[] = {
+0x03, 0x0c, 0x03, 0x0c, 0x06, 0x06, 0x06, 0x06, 0x0c, 0x03, 0x0c, 0x03, 
+0x98, 0x01, 0xf0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00};
+static unsigned char helvetica18_90_bits[] = {
+0xff, 0x03, 0xff, 0x03, 0x00, 0x03, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 
+0x30, 0x00, 0x38, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x03, 0x00, 
+0xff, 0x03, 0xff, 0x03};
+static unsigned char helvetica18_91_bits[] = {
+0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f};
+static unsigned char helvetica18_92_bits[] = {
+0x03, 0x03, 0x02, 0x02, 0x06, 0x06, 0x04, 0x04, 0x0c, 0x0c, 0x08, 0x08, 
+0x18, 0x18};
+static unsigned char helvetica18_93_bits[] = {
+0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f};
+static unsigned char helvetica18_94_bits[] = {
+0x08, 0x1c, 0x36, 0x63, 0x41};
+static unsigned char helvetica18_95_bits[] = {
+0xff, 0x03, 0xff, 0x03};
+static unsigned char helvetica18_96_bits[] = {
+0x02, 0x01, 0x01, 0x03, 0x03};
+static unsigned char helvetica18_97_bits[] = {
+0x3e, 0x77, 0x63, 0x70, 0x7e, 0x67, 0x63, 0x63, 0x77, 0x6e};
+static unsigned char helvetica18_98_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x7b, 0x00, 0xff, 0x00, 
+0xc7, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc7, 0x00, 
+0xff, 0x00, 0x7b, 0x00};
+static unsigned char helvetica18_99_bits[] = {
+0x7c, 0xfe, 0xc6, 0x03, 0x03, 0x03, 0x03, 0xc6, 0xfe, 0x7c};
+static unsigned char helvetica18_100_bits[] = {
+0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xbc, 0x01, 0xfe, 0x01, 
+0xc6, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc6, 0x01, 
+0xfe, 0x01, 0xbc, 0x01};
+static unsigned char helvetica18_101_bits[] = {
+0x3c, 0x7e, 0xc3, 0xc3, 0xff, 0x03, 0x03, 0xc7, 0xfe, 0x3c};
+static unsigned char helvetica18_102_bits[] = {
+0x38, 0x3c, 0x0c, 0x0c, 0x3f, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c};
+static unsigned char helvetica18_103_bits[] = {
+0xbc, 0x01, 0xfe, 0x01, 0x86, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 
+0x83, 0x01, 0xc6, 0x01, 0xfe, 0x01, 0xbc, 0x01, 0x80, 0x01, 0xc6, 0x00, 
+0xfe, 0x00, 0x38, 0x00};
+static unsigned char helvetica18_104_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x73, 0xfb, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 
+0xc3, 0xc3};
+static unsigned char helvetica18_105_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03};
+static unsigned char helvetica18_106_bits[] = {
+0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x07};
+static unsigned char helvetica18_107_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x63, 0x33, 0x1b, 0x0f, 0x1f, 0x1b, 0x33, 0x73, 
+0x63, 0xe3};
+static unsigned char helvetica18_108_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03};
+static unsigned char helvetica18_109_bits[] = {
+0x33, 0x06, 0x7b, 0x0f, 0xe7, 0x0c, 0x63, 0x0c, 0x63, 0x0c, 0x63, 0x0c, 
+0x63, 0x0c, 0x63, 0x0c, 0x63, 0x0c, 0x63, 0x0c};
+static unsigned char helvetica18_110_bits[] = {
+0x73, 0xfb, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3};
+static unsigned char helvetica18_111_bits[] = {
+0x7c, 0x00, 0xfe, 0x00, 0xc6, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 
+0x83, 0x01, 0xc6, 0x00, 0xfe, 0x00, 0x7c, 0x00};
+static unsigned char helvetica18_112_bits[] = {
+0x7b, 0x00, 0xff, 0x00, 0xc7, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 
+0x83, 0x01, 0xc7, 0x00, 0xff, 0x00, 0x7b, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00};
+static unsigned char helvetica18_113_bits[] = {
+0xbc, 0x01, 0xfe, 0x01, 0xc6, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 
+0x83, 0x01, 0xc6, 0x01, 0xfe, 0x01, 0xbc, 0x01, 0x80, 0x01, 0x80, 0x01, 
+0x80, 0x01, 0x80, 0x01};
+static unsigned char helvetica18_114_bits[] = {
+0x1b, 0x1b, 0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica18_115_bits[] = {
+0x3c, 0x7e, 0x63, 0x03, 0x3f, 0x7c, 0x60, 0x63, 0x3f, 0x1e};
+static unsigned char helvetica18_116_bits[] = {
+0x0c, 0x0c, 0x0c, 0x3f, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x1c, 
+0x18};
+static unsigned char helvetica18_117_bits[] = {
+0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe3, 0xdf, 0xce};
+static unsigned char helvetica18_118_bits[] = {
+0xc3, 0xc3, 0xc3, 0x66, 0x66, 0x66, 0x24, 0x3c, 0x18, 0x18};
+static unsigned char helvetica18_119_bits[] = {
+0x63, 0x0c, 0x63, 0x0c, 0x63, 0x0c, 0x66, 0x06, 0x66, 0x06, 0x96, 0x06, 
+0x94, 0x02, 0x9c, 0x03, 0x98, 0x01, 0x98, 0x01};
+static unsigned char helvetica18_120_bits[] = {
+0xc3, 0xe7, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xe7, 0xc3};
+static unsigned char helvetica18_121_bits[] = {
+0xc3, 0xc3, 0xc3, 0x66, 0x66, 0x66, 0x24, 0x3c, 0x18, 0x18, 0x18, 0x18, 
+0x0e, 0x0e};
+static unsigned char helvetica18_122_bits[] = {
+0x7f, 0x7f, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x7f, 0x7f};
+static unsigned char helvetica18_123_bits[] = {
+0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30};
+static unsigned char helvetica18_124_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica18_125_bits[] = {
+0x03, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x18, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x03};
+static unsigned char helvetica18_126_bits[] = {
+0xcc, 0x7e, 0x33};
+static unsigned char helvetica18_127_bits[] = {
+0x00};
+static unsigned char helvetica18_128_bits[] = {
+0x00};
+static unsigned char helvetica18_129_bits[] = {
+0x00};
+static unsigned char helvetica18_130_bits[] = {
+0x00};
+static unsigned char helvetica18_131_bits[] = {
+0x00};
+static unsigned char helvetica18_132_bits[] = {
+0x00};
+static unsigned char helvetica18_133_bits[] = {
+0x00};
+static unsigned char helvetica18_134_bits[] = {
+0x00};
+static unsigned char helvetica18_135_bits[] = {
+0x00};
+static unsigned char helvetica18_136_bits[] = {
+0x00};
+static unsigned char helvetica18_137_bits[] = {
+0x00};
+static unsigned char helvetica18_138_bits[] = {
+0x00};
+static unsigned char helvetica18_139_bits[] = {
+0x00};
+static unsigned char helvetica18_140_bits[] = {
+0x00};
+static unsigned char helvetica18_141_bits[] = {
+0x00};
+static unsigned char helvetica18_142_bits[] = {
+0x00};
+static unsigned char helvetica18_143_bits[] = {
+0x00};
+static unsigned char helvetica18_144_bits[] = {
+0x00};
+static unsigned char helvetica18_145_bits[] = {
+0x00};
+static unsigned char helvetica18_146_bits[] = {
+0x00};
+static unsigned char helvetica18_147_bits[] = {
+0x00};
+static unsigned char helvetica18_148_bits[] = {
+0x00};
+static unsigned char helvetica18_149_bits[] = {
+0x00};
+static unsigned char helvetica18_150_bits[] = {
+0x00};
+static unsigned char helvetica18_151_bits[] = {
+0x00};
+static unsigned char helvetica18_152_bits[] = {
+0x00};
+static unsigned char helvetica18_153_bits[] = {
+0x00};
+static unsigned char helvetica18_154_bits[] = {
+0x00};
+static unsigned char helvetica18_155_bits[] = {
+0x00};
+static unsigned char helvetica18_156_bits[] = {
+0x00};
+static unsigned char helvetica18_157_bits[] = {
+0x00};
+static unsigned char helvetica18_158_bits[] = {
+0x00};
+static unsigned char helvetica18_159_bits[] = {
+0x00};
+static unsigned char helvetica18_160_bits[] = {
+0x00};
+static unsigned char helvetica18_161_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03};
+static unsigned char helvetica18_162_bits[] = {
+0x20, 0x20, 0x7c, 0xfe, 0xd6, 0x13, 0x13, 0x13, 0x13, 0xd6, 0xfe, 0x7c, 
+0x08, 0x08};
+static unsigned char helvetica18_163_bits[] = {
+0x78, 0x00, 0xfc, 0x00, 0x86, 0x01, 0x86, 0x01, 0x06, 0x00, 0x0c, 0x00, 
+0x7e, 0x00, 0x18, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x06, 0x01, 0xff, 0x01, 
+0xfb, 0x00};
+static unsigned char helvetica18_164_bits[] = {
+0xc3, 0xff, 0x66, 0x66, 0x66, 0xff, 0xc3};
+static unsigned char helvetica18_165_bits[] = {
+0xc3, 0xc3, 0x66, 0x66, 0x66, 0x3c, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 
+0x18};
+static unsigned char helvetica18_166_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica18_167_bits[] = {
+0x3c, 0x7e, 0xc3, 0xc3, 0x0f, 0x3e, 0x76, 0xe3, 0xc3, 0xc7, 0xce, 0x7c, 
+0x70, 0xe0, 0xc3, 0xc3, 0x7e, 0x3c};
+static unsigned char helvetica18_168_bits[] = {
+0x1b, 0x1b};
+static unsigned char helvetica18_169_bits[] = {
+0xf0, 0x01, 0x0c, 0x06, 0x02, 0x08, 0xe2, 0x08, 0x11, 0x11, 0x09, 0x10, 
+0x09, 0x10, 0x09, 0x10, 0x11, 0x11, 0xe2, 0x08, 0x02, 0x08, 0x0c, 0x06, 
+0xf0, 0x01};
+static unsigned char helvetica18_170_bits[] = {
+0x0e, 0x13, 0x1c, 0x12, 0x1b, 0x16, 0x00, 0x1f};
+static unsigned char helvetica18_171_bits[] = {
+0x48, 0x6c, 0x36, 0x1b, 0x1b, 0x36, 0x6c, 0x48};
+static unsigned char helvetica18_172_bits[] = {
+0xff, 0x01, 0xff, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01};
+static unsigned char helvetica18_173_bits[] = {
+0x1f, 0x1f};
+static unsigned char helvetica18_174_bits[] = {
+0xf0, 0x01, 0x0c, 0x06, 0x02, 0x08, 0xf2, 0x09, 0x11, 0x12, 0x11, 0x12, 
+0xf1, 0x11, 0x91, 0x10, 0x11, 0x11, 0x12, 0x0a, 0x02, 0x08, 0x0c, 0x06, 
+0xf0, 0x01};
+static unsigned char helvetica18_175_bits[] = {
+0x1f};
+static unsigned char helvetica18_176_bits[] = {
+0x0e, 0x1b, 0x11, 0x1b, 0x0e};
+static unsigned char helvetica18_177_bits[] = {
+0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x00, 0xff, 0xff};
+static unsigned char helvetica18_178_bits[] = {
+0x0e, 0x1f, 0x19, 0x18, 0x0c, 0x06, 0x1f, 0x1f};
+static unsigned char helvetica18_179_bits[] = {
+0x0e, 0x1f, 0x19, 0x0c, 0x0c, 0x19, 0x1f, 0x0e};
+static unsigned char helvetica18_180_bits[] = {
+0x0c, 0x06, 0x03};
+static unsigned char helvetica18_181_bits[] = {
+0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0xff, 0xdb, 0x03, 0x03, 
+0x03, 0x03};
+static unsigned char helvetica18_182_bits[] = {
+0xfc, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4e, 0x4c, 0x48, 0x48, 0x48, 0x48, 
+0x48, 0x48, 0x48, 0x48, 0x48, 0x48};
+static unsigned char helvetica18_183_bits[] = {
+0x03, 0x03};
+static unsigned char helvetica18_184_bits[] = {
+0x06, 0x0e, 0x18, 0x1b, 0x0f};
+static unsigned char helvetica18_185_bits[] = {
+0x06, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06};
+static unsigned char helvetica18_186_bits[] = {
+0x0e, 0x1b, 0x11, 0x11, 0x1b, 0x0e, 0x00, 0x1f};
+static unsigned char helvetica18_187_bits[] = {
+0x09, 0x1b, 0x36, 0x6c, 0x6c, 0x36, 0x1b, 0x09};
+static unsigned char helvetica18_188_bits[] = {
+0x06, 0x06, 0x07, 0x06, 0x07, 0x03, 0x86, 0x01, 0x86, 0x01, 0xc6, 0x08, 
+0x46, 0x0c, 0x66, 0x0e, 0x30, 0x0f, 0xb0, 0x0d, 0x98, 0x1f, 0x0c, 0x0c, 
+0x0c, 0x0c};
+static unsigned char helvetica18_189_bits[] = {
+0x06, 0x06, 0x07, 0x06, 0x07, 0x03, 0x86, 0x01, 0x86, 0x01, 0xc6, 0x0e, 
+0x46, 0x1f, 0x66, 0x19, 0x30, 0x18, 0x30, 0x0c, 0x18, 0x06, 0x0c, 0x1f, 
+0x0c, 0x1f};
+static unsigned char helvetica18_190_bits[] = {
+0x0e, 0x0c, 0x1f, 0x0c, 0x19, 0x06, 0x0c, 0x03, 0x0c, 0x03, 0x99, 0x11, 
+0x9f, 0x18, 0xce, 0x1c, 0x60, 0x1e, 0x60, 0x1b, 0x30, 0x3f, 0x18, 0x18, 
+0x18, 0x18};
+static unsigned char helvetica18_191_bits[] = {
+0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x1c, 0x0e, 0x07, 0x63, 0x63, 
+0x7f, 0x3e};
+static unsigned char helvetica18_192_bits[] = {
+0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0xf0, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x98, 0x01, 0x0c, 0x03, 0x0c, 0x03, 
+0xfc, 0x03, 0xfe, 0x07, 0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c};
+static unsigned char helvetica18_193_bits[] = {
+0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0xf0, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x98, 0x01, 0x0c, 0x03, 0x0c, 0x03, 
+0xfc, 0x03, 0xfe, 0x07, 0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c};
+static unsigned char helvetica18_194_bits[] = {
+0x60, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0xf0, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x98, 0x01, 0x0c, 0x03, 0x0c, 0x03, 
+0xfc, 0x03, 0xfe, 0x07, 0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c};
+static unsigned char helvetica18_195_bits[] = {
+0x30, 0x01, 0x68, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0xf0, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x98, 0x01, 0x0c, 0x03, 0x0c, 0x03, 
+0xfc, 0x03, 0xfe, 0x07, 0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c};
+static unsigned char helvetica18_196_bits[] = {
+0x98, 0x01, 0x98, 0x01, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0xf0, 0x00, 
+0xf0, 0x00, 0x98, 0x01, 0x98, 0x01, 0x0c, 0x03, 0x0c, 0x03, 0xfc, 0x03, 
+0xfe, 0x07, 0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c};
+static unsigned char helvetica18_197_bits[] = {
+0xf0, 0x00, 0x98, 0x01, 0x98, 0x01, 0xf0, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0xf0, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x98, 0x01, 0x0c, 0x03, 0x0c, 0x03, 
+0xfc, 0x03, 0xfe, 0x07, 0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c};
+static unsigned char helvetica18_198_bits[] = {
+0xe0, 0xff, 0xe0, 0xff, 0xb0, 0x01, 0xb0, 0x01, 0x98, 0x01, 0x98, 0x01, 
+0x8c, 0x7f, 0x8c, 0x7f, 0xfc, 0x01, 0xfe, 0x01, 0x86, 0x01, 0x86, 0x01, 
+0x83, 0xff, 0x83, 0xff};
+static unsigned char helvetica18_199_bits[] = {
+0xf0, 0x01, 0xfc, 0x07, 0x0e, 0x0e, 0x06, 0x0c, 0x07, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x07, 0x00, 0x06, 0x0c, 0x0e, 0x0e, 
+0xfc, 0x07, 0xf0, 0x01, 0x70, 0x00, 0xc0, 0x00, 0xd8, 0x00, 0x78, 0x00};
+static unsigned char helvetica18_200_bits[] = {
+0x0c, 0x00, 0x18, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x00, 0xff, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x01, 0xff, 0x01};
+static unsigned char helvetica18_201_bits[] = {
+0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x00, 0xff, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x01, 0xff, 0x01};
+static unsigned char helvetica18_202_bits[] = {
+0x30, 0x00, 0x78, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x00, 0xff, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x01, 0xff, 0x01};
+static unsigned char helvetica18_203_bits[] = {
+0xcc, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x00, 0xff, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x01, 0xff, 0x01};
+static unsigned char helvetica18_204_bits[] = {
+0x03, 0x06, 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c};
+static unsigned char helvetica18_205_bits[] = {
+0x0c, 0x06, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica18_206_bits[] = {
+0x0c, 0x1e, 0x33, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c};
+static unsigned char helvetica18_207_bits[] = {
+0x33, 0x33, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c};
+static unsigned char helvetica18_208_bits[] = {
+0xfe, 0x01, 0xfe, 0x03, 0x06, 0x07, 0x06, 0x06, 0x06, 0x0c, 0x06, 0x0c, 
+0x3f, 0x0c, 0x3f, 0x0c, 0x06, 0x0c, 0x06, 0x0c, 0x06, 0x06, 0x06, 0x07, 
+0xfe, 0x03, 0xfe, 0x01};
+static unsigned char helvetica18_209_bits[] = {
+0x30, 0x01, 0x68, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x07, 0x06, 0x07, 0x06, 
+0x0f, 0x06, 0x1b, 0x06, 0x1b, 0x06, 0x33, 0x06, 0x33, 0x06, 0x63, 0x06, 
+0x63, 0x06, 0xc3, 0x06, 0x83, 0x07, 0x83, 0x07, 0x03, 0x07, 0x03, 0x06};
+static unsigned char helvetica18_210_bits[] = {
+0x30, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xf0, 0x01, 0xfc, 0x07, 
+0x0e, 0x0e, 0x06, 0x0c, 0x07, 0x1c, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 
+0x03, 0x18, 0x07, 0x1c, 0x06, 0x0c, 0x0e, 0x0e, 0xfc, 0x07, 0xf0, 0x01};
+static unsigned char helvetica18_211_bits[] = {
+0x00, 0x03, 0x80, 0x01, 0xc0, 0x00, 0x00, 0x00, 0xf0, 0x01, 0xfc, 0x07, 
+0x0e, 0x0e, 0x06, 0x0c, 0x07, 0x1c, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 
+0x03, 0x18, 0x07, 0x1c, 0x06, 0x0c, 0x0e, 0x0e, 0xfc, 0x07, 0xf0, 0x01};
+static unsigned char helvetica18_212_bits[] = {
+0xc0, 0x00, 0xe0, 0x01, 0x30, 0x03, 0x00, 0x00, 0xf0, 0x01, 0xfc, 0x07, 
+0x0e, 0x0e, 0x06, 0x0c, 0x07, 0x1c, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 
+0x03, 0x18, 0x07, 0x1c, 0x06, 0x0c, 0x0e, 0x0e, 0xfc, 0x07, 0xf0, 0x01};
+static unsigned char helvetica18_213_bits[] = {
+0x60, 0x02, 0xd0, 0x02, 0x90, 0x01, 0x00, 0x00, 0xf0, 0x01, 0xfc, 0x07, 
+0x0e, 0x0e, 0x06, 0x0c, 0x07, 0x1c, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 
+0x03, 0x18, 0x07, 0x1c, 0x06, 0x0c, 0x0e, 0x0e, 0xfc, 0x07, 0xf0, 0x01};
+static unsigned char helvetica18_214_bits[] = {
+0xb0, 0x01, 0xb0, 0x01, 0x00, 0x00, 0xf0, 0x01, 0xfc, 0x07, 0x0e, 0x0e, 
+0x06, 0x0c, 0x07, 0x1c, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 
+0x07, 0x1c, 0x06, 0x0c, 0x0e, 0x0e, 0xfc, 0x07, 0xf0, 0x01};
+static unsigned char helvetica18_215_bits[] = {
+0x03, 0x03, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x78, 0x00, 
+0xcc, 0x00, 0x86, 0x01, 0x03, 0x03};
+static unsigned char helvetica18_216_bits[] = {
+0xe0, 0x33, 0xf8, 0x3f, 0x1c, 0x1c, 0x0c, 0x1e, 0x0e, 0x3b, 0x86, 0x33, 
+0xc6, 0x31, 0xe6, 0x30, 0x76, 0x30, 0x36, 0x38, 0x1c, 0x18, 0x1e, 0x1c, 
+0xff, 0x0f, 0xe3, 0x03};
+static unsigned char helvetica18_217_bits[] = {
+0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0xfe, 0x03, 0xf8, 0x00};
+static unsigned char helvetica18_218_bits[] = {
+0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0xfe, 0x03, 0xf8, 0x00};
+static unsigned char helvetica18_219_bits[] = {
+0x60, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x00, 0x00, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0xfe, 0x03, 0xf8, 0x00};
+static unsigned char helvetica18_220_bits[] = {
+0x98, 0x01, 0x98, 0x01, 0x00, 0x00, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0xfe, 0x03, 0xf8, 0x00};
+static unsigned char helvetica18_221_bits[] = {
+0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x03, 0x0c, 
+0x06, 0x06, 0x06, 0x06, 0x0c, 0x03, 0x0c, 0x03, 0x98, 0x01, 0xf0, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char helvetica18_222_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x00, 0xff, 0x01, 0x83, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 0xff, 0x01, 0xff, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00};
+static unsigned char helvetica18_223_bits[] = {
+0x1c, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x3b, 0x3b, 0x63, 0x63, 0x63, 0x63, 
+0x7b, 0x3b};
+static unsigned char helvetica18_224_bits[] = {
+0x06, 0x0c, 0x18, 0x00, 0x3e, 0x77, 0x63, 0x70, 0x7e, 0x67, 0x63, 0x63, 
+0x77, 0x6e};
+static unsigned char helvetica18_225_bits[] = {
+0x30, 0x18, 0x0c, 0x00, 0x3e, 0x77, 0x63, 0x70, 0x7e, 0x67, 0x63, 0x63, 
+0x77, 0x6e};
+static unsigned char helvetica18_226_bits[] = {
+0x18, 0x3c, 0x66, 0x00, 0x3e, 0x77, 0x63, 0x70, 0x7e, 0x67, 0x63, 0x63, 
+0x77, 0x6e};
+static unsigned char helvetica18_227_bits[] = {
+0x4c, 0x5a, 0x32, 0x00, 0x3e, 0x77, 0x63, 0x70, 0x7e, 0x67, 0x63, 0x63, 
+0x77, 0x6e};
+static unsigned char helvetica18_228_bits[] = {
+0x36, 0x36, 0x00, 0x3e, 0x77, 0x63, 0x70, 0x7e, 0x67, 0x63, 0x63, 0x77, 
+0x6e};
+static unsigned char helvetica18_229_bits[] = {
+0x1c, 0x36, 0x36, 0x1c, 0x3e, 0x77, 0x63, 0x70, 0x7e, 0x67, 0x63, 0x63, 
+0x77, 0x6e};
+static unsigned char helvetica18_230_bits[] = {
+0xbe, 0x07, 0xf7, 0x0f, 0x63, 0x18, 0x70, 0x18, 0xfe, 0x1f, 0x67, 0x00, 
+0x63, 0x00, 0xe3, 0x18, 0xf7, 0x1f, 0xae, 0x07};
+static unsigned char helvetica18_231_bits[] = {
+0x7c, 0xfe, 0xc6, 0x03, 0x03, 0x03, 0x03, 0xc6, 0xfe, 0x7c, 0x1c, 0x30, 
+0x36, 0x1e};
+static unsigned char helvetica18_232_bits[] = {
+0x06, 0x0c, 0x18, 0x00, 0x3c, 0x7e, 0xc3, 0xc3, 0xff, 0x03, 0x03, 0xc7, 
+0xfe, 0x3c};
+static unsigned char helvetica18_233_bits[] = {
+0x60, 0x30, 0x18, 0x00, 0x3c, 0x7e, 0xc3, 0xc3, 0xff, 0x03, 0x03, 0xc7, 
+0xfe, 0x3c};
+static unsigned char helvetica18_234_bits[] = {
+0x18, 0x3c, 0x66, 0x00, 0x3c, 0x7e, 0xc3, 0xc3, 0xff, 0x03, 0x03, 0xc7, 
+0xfe, 0x3c};
+static unsigned char helvetica18_235_bits[] = {
+0x6c, 0x6c, 0x00, 0x3c, 0x7e, 0xc3, 0xc3, 0xff, 0x03, 0x03, 0xc7, 0xfe, 
+0x3c};
+static unsigned char helvetica18_236_bits[] = {
+0x03, 0x06, 0x0c, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06};
+static unsigned char helvetica18_237_bits[] = {
+0x0c, 0x06, 0x03, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06};
+static unsigned char helvetica18_238_bits[] = {
+0x0c, 0x1e, 0x33, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c};
+static unsigned char helvetica18_239_bits[] = {
+0x1b, 0x1b, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06};
+static unsigned char helvetica18_240_bits[] = {
+0x06, 0x00, 0x6c, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x7c, 0x00, 0xfe, 0x00, 
+0xc6, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc6, 0x00, 
+0xfe, 0x00, 0x7c, 0x00};
+static unsigned char helvetica18_241_bits[] = {
+0x4c, 0x5a, 0x32, 0x00, 0x73, 0xfb, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 
+0xc3, 0xc3};
+static unsigned char helvetica18_242_bits[] = {
+0x0c, 0x00, 0x18, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xfe, 0x00, 
+0xc6, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc6, 0x00, 
+0xfe, 0x00, 0x7c, 0x00};
+static unsigned char helvetica18_243_bits[] = {
+0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xfe, 0x00, 
+0xc6, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc6, 0x00, 
+0xfe, 0x00, 0x7c, 0x00};
+static unsigned char helvetica18_244_bits[] = {
+0x30, 0x00, 0x78, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xfe, 0x00, 
+0xc6, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc6, 0x00, 
+0xfe, 0x00, 0x7c, 0x00};
+static unsigned char helvetica18_245_bits[] = {
+0x98, 0x00, 0xb4, 0x00, 0x64, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xfe, 0x00, 
+0xc6, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc6, 0x00, 
+0xfe, 0x00, 0x7c, 0x00};
+static unsigned char helvetica18_246_bits[] = {
+0x6c, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xfe, 0x00, 0xc6, 0x00, 
+0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc6, 0x00, 0xfe, 0x00, 
+0x7c, 0x00};
+static unsigned char helvetica18_247_bits[] = {
+0x18, 0x18, 0x00, 0xff, 0xff, 0x00, 0x18, 0x18};
+static unsigned char helvetica18_248_bits[] = {
+0x70, 0x06, 0xfc, 0x03, 0x8c, 0x01, 0xc6, 0x03, 0x66, 0x03, 0x36, 0x03, 
+0x1e, 0x03, 0x8c, 0x01, 0xfe, 0x01, 0x73, 0x00};
+static unsigned char helvetica18_249_bits[] = {
+0x0c, 0x18, 0x30, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe3, 
+0xdf, 0xce};
+static unsigned char helvetica18_250_bits[] = {
+0x60, 0x30, 0x18, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe3, 
+0xdf, 0xce};
+static unsigned char helvetica18_251_bits[] = {
+0x18, 0x3c, 0x66, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe3, 
+0xdf, 0xce};
+static unsigned char helvetica18_252_bits[] = {
+0x66, 0x66, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe3, 0xdf, 
+0xce};
+static unsigned char helvetica18_253_bits[] = {
+0x60, 0x30, 0x18, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x66, 0x66, 0x24, 0x3c, 
+0x18, 0x18, 0x18, 0x18, 0x0e, 0x0e};
+static unsigned char helvetica18_254_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x7b, 0x00, 0xff, 0x00, 
+0xc7, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc7, 0x00, 
+0xff, 0x00, 0x7b, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00};
+static unsigned char helvetica18_255_bits[] = {
+0x66, 0x66, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x66, 0x66, 0x24, 0x3c, 0x18, 
+0x18, 0x18, 0x18, 0x0e, 0x0e};
+static RotFont helvetica18font[] = {
+{5, 1, 1, helvetica18_0_bits},
+{5, 1, 1, helvetica18_1_bits},
+{5, 1, 1, helvetica18_2_bits},
+{5, 1, 1, helvetica18_3_bits},
+{5, 1, 1, helvetica18_4_bits},
+{5, 1, 1, helvetica18_5_bits},
+{5, 1, 1, helvetica18_6_bits},
+{5, 1, 1, helvetica18_7_bits},
+{5, 1, 1, helvetica18_8_bits},
+{5, 1, 1, helvetica18_9_bits},
+{5, 1, 1, helvetica18_10_bits},
+{5, 1, 1, helvetica18_11_bits},
+{5, 1, 1, helvetica18_12_bits},
+{5, 1, 1, helvetica18_13_bits},
+{5, 1, 1, helvetica18_14_bits},
+{5, 1, 1, helvetica18_15_bits},
+{5, 1, 1, helvetica18_16_bits},
+{5, 1, 1, helvetica18_17_bits},
+{5, 1, 1, helvetica18_18_bits},
+{5, 1, 1, helvetica18_19_bits},
+{5, 1, 1, helvetica18_20_bits},
+{5, 1, 1, helvetica18_21_bits},
+{5, 1, 1, helvetica18_22_bits},
+{5, 1, 1, helvetica18_23_bits},
+{5, 1, 1, helvetica18_24_bits},
+{5, 1, 1, helvetica18_25_bits},
+{5, 1, 1, helvetica18_26_bits},
+{5, 1, 1, helvetica18_27_bits},
+{5, 1, 1, helvetica18_28_bits},
+{5, 1, 1, helvetica18_29_bits},
+{5, 1, 1, helvetica18_30_bits},
+{5, 1, 1, helvetica18_31_bits},
+{9, 1, 1, helvetica18_32_bits},
+{2, 14, 14, helvetica18_33_bits},
+{5, 5, 14, helvetica18_34_bits},
+{10, 13, 13, helvetica18_35_bits},
+{9, 16, 14, helvetica18_36_bits},
+{14, 13, 13, helvetica18_37_bits},
+{12, 13, 13, helvetica18_38_bits},
+{2, 5, 14, helvetica18_39_bits},
+{4, 18, 14, helvetica18_40_bits},
+{4, 18, 14, helvetica18_41_bits},
+{5, 6, 14, helvetica18_42_bits},
+{8, 10, 10, helvetica18_43_bits},
+{2, 5, 2, helvetica18_44_bits},
+{8, 2, 6, helvetica18_45_bits},
+{2, 2, 2, helvetica18_46_bits},
+{5, 14, 14, helvetica18_47_bits},
+{8, 13, 13, helvetica18_48_bits},
+{5, 13, 13, helvetica18_49_bits},
+{8, 13, 13, helvetica18_50_bits},
+{8, 13, 13, helvetica18_51_bits},
+{9, 13, 13, helvetica18_52_bits},
+{8, 13, 13, helvetica18_53_bits},
+{8, 13, 13, helvetica18_54_bits},
+{8, 13, 13, helvetica18_55_bits},
+{8, 13, 13, helvetica18_56_bits},
+{8, 13, 13, helvetica18_57_bits},
+{2, 10, 10, helvetica18_58_bits},
+{2, 13, 10, helvetica18_59_bits},
+{8, 9, 9, helvetica18_60_bits},
+{7, 6, 8, helvetica18_61_bits},
+{8, 9, 9, helvetica18_62_bits},
+{7, 14, 14, helvetica18_63_bits},
+{16, 17, 14, helvetica18_64_bits},
+{12, 14, 14, helvetica18_65_bits},
+{11, 14, 14, helvetica18_66_bits},
+{12, 14, 14, helvetica18_67_bits},
+{11, 14, 14, helvetica18_68_bits},
+{9, 14, 14, helvetica18_69_bits},
+{9, 14, 14, helvetica18_70_bits},
+{12, 14, 14, helvetica18_71_bits},
+{11, 14, 14, helvetica18_72_bits},
+{2, 14, 14, helvetica18_73_bits},
+{8, 14, 14, helvetica18_74_bits},
+{12, 14, 14, helvetica18_75_bits},
+{8, 14, 14, helvetica18_76_bits},
+{14, 14, 14, helvetica18_77_bits},
+{11, 14, 14, helvetica18_78_bits},
+{13, 14, 14, helvetica18_79_bits},
+{10, 14, 14, helvetica18_80_bits},
+{13, 15, 14, helvetica18_81_bits},
+{10, 14, 14, helvetica18_82_bits},
+{11, 14, 14, helvetica18_83_bits},
+{10, 14, 14, helvetica18_84_bits},
+{11, 14, 14, helvetica18_85_bits},
+{12, 14, 14, helvetica18_86_bits},
+{16, 14, 14, helvetica18_87_bits},
+{11, 14, 14, helvetica18_88_bits},
+{12, 14, 14, helvetica18_89_bits},
+{10, 14, 14, helvetica18_90_bits},
+{4, 18, 14, helvetica18_91_bits},
+{5, 14, 14, helvetica18_92_bits},
+{4, 18, 14, helvetica18_93_bits},
+{7, 5, 13, helvetica18_94_bits},
+{10, 2, -2, helvetica18_95_bits},
+{2, 5, 14, helvetica18_96_bits},
+{7, 10, 10, helvetica18_97_bits},
+{9, 14, 14, helvetica18_98_bits},
+{8, 10, 10, helvetica18_99_bits},
+{9, 14, 14, helvetica18_100_bits},
+{8, 10, 10, helvetica18_101_bits},
+{6, 14, 14, helvetica18_102_bits},
+{9, 14, 10, helvetica18_103_bits},
+{8, 14, 14, helvetica18_104_bits},
+{2, 14, 14, helvetica18_105_bits},
+{4, 18, 14, helvetica18_106_bits},
+{8, 14, 14, helvetica18_107_bits},
+{2, 14, 14, helvetica18_108_bits},
+{12, 10, 10, helvetica18_109_bits},
+{8, 10, 10, helvetica18_110_bits},
+{9, 10, 10, helvetica18_111_bits},
+{9, 14, 10, helvetica18_112_bits},
+{9, 14, 10, helvetica18_113_bits},
+{5, 10, 10, helvetica18_114_bits},
+{7, 10, 10, helvetica18_115_bits},
+{6, 13, 13, helvetica18_116_bits},
+{8, 10, 10, helvetica18_117_bits},
+{8, 10, 10, helvetica18_118_bits},
+{12, 10, 10, helvetica18_119_bits},
+{8, 10, 10, helvetica18_120_bits},
+{8, 14, 10, helvetica18_121_bits},
+{7, 10, 10, helvetica18_122_bits},
+{6, 18, 14, helvetica18_123_bits},
+{2, 18, 14, helvetica18_124_bits},
+{6, 18, 14, helvetica18_125_bits},
+{8, 3, 7, helvetica18_126_bits},
+{5, 1, 1, helvetica18_127_bits},
+{5, 1, 1, helvetica18_128_bits},
+{5, 1, 1, helvetica18_129_bits},
+{5, 1, 1, helvetica18_130_bits},
+{5, 1, 1, helvetica18_131_bits},
+{5, 1, 1, helvetica18_132_bits},
+{5, 1, 1, helvetica18_133_bits},
+{5, 1, 1, helvetica18_134_bits},
+{5, 1, 1, helvetica18_135_bits},
+{5, 1, 1, helvetica18_136_bits},
+{5, 1, 1, helvetica18_137_bits},
+{5, 1, 1, helvetica18_138_bits},
+{5, 1, 1, helvetica18_139_bits},
+{5, 1, 1, helvetica18_140_bits},
+{5, 1, 1, helvetica18_141_bits},
+{5, 1, 1, helvetica18_142_bits},
+{5, 1, 1, helvetica18_143_bits},
+{5, 1, 1, helvetica18_144_bits},
+{5, 1, 1, helvetica18_145_bits},
+{5, 1, 1, helvetica18_146_bits},
+{5, 1, 1, helvetica18_147_bits},
+{5, 1, 1, helvetica18_148_bits},
+{5, 1, 1, helvetica18_149_bits},
+{5, 1, 1, helvetica18_150_bits},
+{5, 1, 1, helvetica18_151_bits},
+{5, 1, 1, helvetica18_152_bits},
+{5, 1, 1, helvetica18_153_bits},
+{5, 1, 1, helvetica18_154_bits},
+{5, 1, 1, helvetica18_155_bits},
+{5, 1, 1, helvetica18_156_bits},
+{5, 1, 1, helvetica18_157_bits},
+{5, 1, 1, helvetica18_158_bits},
+{5, 1, 1, helvetica18_159_bits},
+{1, 1, 1, helvetica18_160_bits},
+{2, 14, 10, helvetica18_161_bits},
+{8, 14, 12, helvetica18_162_bits},
+{9, 13, 13, helvetica18_163_bits},
+{8, 7, 10, helvetica18_164_bits},
+{8, 13, 13, helvetica18_165_bits},
+{2, 17, 14, helvetica18_166_bits},
+{8, 18, 14, helvetica18_167_bits},
+{5, 2, 13, helvetica18_168_bits},
+{13, 13, 13, helvetica18_169_bits},
+{5, 8, 14, helvetica18_170_bits},
+{7, 8, 9, helvetica18_171_bits},
+{9, 5, 8, helvetica18_172_bits},
+{5, 2, 6, helvetica18_173_bits},
+{13, 13, 13, helvetica18_174_bits},
+{5, 1, 13, helvetica18_175_bits},
+{5, 5, 13, helvetica18_176_bits},
+{8, 11, 11, helvetica18_177_bits},
+{5, 8, 13, helvetica18_178_bits},
+{5, 8, 13, helvetica18_179_bits},
+{4, 3, 14, helvetica18_180_bits},
+{8, 14, 10, helvetica18_181_bits},
+{8, 18, 14, helvetica18_182_bits},
+{2, 2, 6, helvetica18_183_bits},
+{5, 5, 1, helvetica18_184_bits},
+{3, 8, 13, helvetica18_185_bits},
+{5, 8, 14, helvetica18_186_bits},
+{7, 8, 9, helvetica18_187_bits},
+{13, 13, 13, helvetica18_188_bits},
+{13, 13, 13, helvetica18_189_bits},
+{14, 13, 13, helvetica18_190_bits},
+{7, 14, 10, helvetica18_191_bits},
+{12, 18, 18, helvetica18_192_bits},
+{12, 18, 18, helvetica18_193_bits},
+{12, 18, 18, helvetica18_194_bits},
+{12, 18, 18, helvetica18_195_bits},
+{12, 17, 17, helvetica18_196_bits},
+{12, 18, 18, helvetica18_197_bits},
+{16, 14, 14, helvetica18_198_bits},
+{12, 18, 14, helvetica18_199_bits},
+{9, 18, 18, helvetica18_200_bits},
+{9, 18, 18, helvetica18_201_bits},
+{9, 18, 18, helvetica18_202_bits},
+{9, 17, 17, helvetica18_203_bits},
+{4, 18, 18, helvetica18_204_bits},
+{4, 18, 18, helvetica18_205_bits},
+{6, 18, 18, helvetica18_206_bits},
+{6, 17, 17, helvetica18_207_bits},
+{12, 14, 14, helvetica18_208_bits},
+{11, 18, 18, helvetica18_209_bits},
+{13, 18, 18, helvetica18_210_bits},
+{13, 18, 18, helvetica18_211_bits},
+{13, 18, 18, helvetica18_212_bits},
+{13, 18, 18, helvetica18_213_bits},
+{13, 17, 17, helvetica18_214_bits},
+{10, 9, 9, helvetica18_215_bits},
+{14, 14, 14, helvetica18_216_bits},
+{11, 18, 18, helvetica18_217_bits},
+{11, 18, 18, helvetica18_218_bits},
+{11, 18, 18, helvetica18_219_bits},
+{11, 17, 17, helvetica18_220_bits},
+{12, 18, 18, helvetica18_221_bits},
+{10, 14, 14, helvetica18_222_bits},
+{7, 14, 14, helvetica18_223_bits},
+{7, 14, 14, helvetica18_224_bits},
+{7, 14, 14, helvetica18_225_bits},
+{7, 14, 14, helvetica18_226_bits},
+{7, 14, 14, helvetica18_227_bits},
+{7, 13, 13, helvetica18_228_bits},
+{7, 14, 14, helvetica18_229_bits},
+{13, 10, 10, helvetica18_230_bits},
+{8, 14, 10, helvetica18_231_bits},
+{8, 14, 14, helvetica18_232_bits},
+{8, 14, 14, helvetica18_233_bits},
+{8, 14, 14, helvetica18_234_bits},
+{8, 13, 13, helvetica18_235_bits},
+{4, 14, 14, helvetica18_236_bits},
+{4, 14, 14, helvetica18_237_bits},
+{6, 14, 14, helvetica18_238_bits},
+{5, 13, 13, helvetica18_239_bits},
+{9, 14, 14, helvetica18_240_bits},
+{8, 14, 14, helvetica18_241_bits},
+{9, 14, 14, helvetica18_242_bits},
+{9, 14, 14, helvetica18_243_bits},
+{9, 14, 14, helvetica18_244_bits},
+{9, 14, 14, helvetica18_245_bits},
+{9, 13, 13, helvetica18_246_bits},
+{8, 8, 9, helvetica18_247_bits},
+{11, 10, 10, helvetica18_248_bits},
+{8, 14, 14, helvetica18_249_bits},
+{8, 14, 14, helvetica18_250_bits},
+{8, 14, 14, helvetica18_251_bits},
+{8, 13, 13, helvetica18_252_bits},
+{8, 18, 14, helvetica18_253_bits},
+{9, 18, 14, helvetica18_254_bits},
+{8, 17, 13, helvetica18_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica24.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica24.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica24.bdf	(revision 22322)
@@ -0,0 +1,4679 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Helvetica-Medium-R-Normal--24-240-75-75-P-130-ISO8859-1
+SIZE 24 75 75
+FONTBOUNDINGBOX 25 29 -1 -5
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Helvetica"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 24
+POINT_SIZE 240
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 130
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 19
+X_HEIGHT 14
+FACE_NAME "Helvetica"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.  "
+_DEC_DEVICE_FONTNAMES "PS=Helvetica"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2, 07-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Helvetica"
+FONT "-Adobe-Helvetica-Medium-R-Normal--24-240-75-75-P-130-ISO8859-1"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 15
+DEFAULT_CHAR 32
+FONT_ASCENT 22
+FONT_DESCENT 5
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 19 2 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+80
+80
+00
+00
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 6 1 13
+BITMAP
+cc
+cc
+cc
+cc
+cc
+44
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 11 17 2 0
+BITMAP
+0cc0
+0cc0
+0cc0
+1980
+ffe0
+ffe0
+1980
+1980
+3300
+3300
+ffe0
+ffe0
+3300
+3300
+6600
+6600
+6600
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 21 1 -2
+BITMAP
+0600
+0600
+3f80
+7fc0
+e6e0
+c660
+c600
+e600
+7600
+3e00
+0f80
+07c0
+06e0
+0660
+c660
+c660
+e6e0
+7fc0
+3f80
+0600
+0600
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 880 0
+DWIDTH 22 0
+BBX 19 18 1 0
+BITMAP
+000600
+3c0c00
+7e0c00
+c31800
+c31800
+c33000
+c33000
+7e6000
+3c6000
+00c000
+00c780
+018fc0
+019860
+031860
+031860
+061860
+060fc0
+040780
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 14 18 1 0
+BITMAP
+0f00
+1f80
+39c0
+30c0
+30c0
+30c0
+1980
+0f00
+1e00
+3f18
+7398
+61d8
+c0f0
+c060
+c0f0
+e1d8
+7f9c
+1e00
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 6 2 13
+BITMAP
+c0
+c0
+c0
+40
+40
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 5 24 2 -5
+BITMAP
+18
+18
+30
+30
+60
+60
+60
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+60
+60
+60
+30
+30
+18
+18
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 5 24 1 -5
+BITMAP
+c0
+c0
+60
+60
+30
+30
+30
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+30
+30
+30
+60
+60
+c0
+c0
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 7 7 1 12
+BITMAP
+10
+10
+d6
+7c
+38
+6c
+44
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 12 1 1
+BITMAP
+0600
+0600
+0600
+0600
+0600
+fff0
+fff0
+0600
+0600
+0600
+0600
+0600
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 6 2 -3
+BITMAP
+c0
+c0
+c0
+40
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 11 2 2 6
+BITMAP
+ffe0
+ffe0
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 3 2 0
+BITMAP
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 7 19 0 0
+BITMAP
+06
+06
+06
+0c
+0c
+0c
+18
+18
+18
+18
+30
+30
+30
+60
+60
+60
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+1f00
+3f80
+71c0
+60c0
+60c0
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+60c0
+71c0
+3f80
+1f00
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 6 18 2 0
+BITMAP
+0c
+0c
+1c
+fc
+fc
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+1e00
+7f80
+61c0
+c0c0
+c060
+c060
+0060
+00c0
+01c0
+0380
+0f00
+1c00
+3800
+7000
+e000
+c000
+ffe0
+ffe0
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+1f00
+7f80
+6180
+c0c0
+c0c0
+c0c0
+00c0
+0180
+0f00
+0fc0
+00c0
+0060
+0060
+c060
+c0c0
+61c0
+7f80
+1f00
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+0180
+0380
+0380
+0780
+0f80
+0d80
+1980
+3980
+3180
+6180
+e180
+c180
+ffe0
+ffe0
+0180
+0180
+0180
+0180
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+7fc0
+7fc0
+6000
+6000
+6000
+6000
+6e00
+7f80
+71c0
+00c0
+0060
+0060
+0060
+c060
+c0c0
+e1c0
+7f80
+1e00
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+0f00
+3fc0
+70c0
+6060
+e060
+c000
+c000
+cf00
+df80
+f1c0
+e0c0
+c060
+c060
+c060
+e060
+60c0
+7fc0
+1f00
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+ffe0
+ffe0
+00e0
+00c0
+0180
+0180
+0300
+0300
+0600
+0600
+0c00
+0c00
+1c00
+1800
+1800
+3800
+3000
+3000
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+0e00
+3f80
+3180
+60c0
+60c0
+60c0
+60c0
+3180
+1f00
+3f80
+60c0
+e0e0
+c060
+c060
+c060
+71c0
+7fc0
+1f00
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+1f00
+7fc0
+71c0
+e0c0
+c060
+c060
+c060
+c060
+e0e0
+71e0
+7f60
+1e60
+0060
+00e0
+c0c0
+e1c0
+7f80
+1e00
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 14 2 0
+BITMAP
+c0
+c0
+c0
+00
+00
+00
+00
+00
+00
+00
+00
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 17 2 -3
+BITMAP
+c0
+c0
+c0
+00
+00
+00
+00
+00
+00
+00
+00
+c0
+c0
+c0
+40
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 12 12 1 1
+BITMAP
+0030
+00f0
+03c0
+0f00
+3c00
+e000
+e000
+3c00
+0f00
+03c0
+00f0
+0030
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 10 6 2 5
+BITMAP
+ffc0
+ffc0
+0000
+0000
+ffc0
+ffc0
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 12 12 1 1
+BITMAP
+c000
+f000
+3c00
+0f00
+03c0
+0070
+0070
+03c0
+0f00
+3c00
+f000
+c000
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 19 1 0
+BITMAP
+1f00
+7f80
+71c0
+e0c0
+c0c0
+c1c0
+0180
+0380
+0700
+0600
+0c00
+0c00
+0c00
+0c00
+0000
+0000
+0c00
+0c00
+0c00
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 1000 0
+DWIDTH 25 0
+BBX 22 23 2 -4
+BITMAP
+00ff00
+03ffc0
+0f01e0
+1c0070
+380018
+300018
+60730c
+60fb0c
+c1c70c
+c3860c
+c3060c
+c6060c
+c60c1c
+c60c18
+c60c38
+e71c70
+63f7e0
+71e380
+380000
+1c0000
+0f0300
+07ff00
+00fc00
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 19 1 0
+BITMAP
+0380
+0380
+06c0
+06c0
+0c40
+0c60
+0c60
+1830
+1830
+1830
+3018
+3ff8
+3ff8
+600c
+600c
+600c
+c006
+c006
+c006
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 14 19 2 0
+BITMAP
+ffc0
+fff0
+c070
+c018
+c018
+c018
+c018
+c030
+ffe0
+fff0
+c018
+c00c
+c00c
+c00c
+c00c
+c01c
+c038
+fff0
+ffc0
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 19 1 0
+BITMAP
+07e0
+1ff8
+3c3c
+700e
+6006
+e006
+c000
+c000
+c000
+c000
+c000
+c000
+c003
+e003
+6006
+700e
+3c3c
+1ff8
+07e0
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 15 19 2 0
+BITMAP
+ffc0
+fff0
+c078
+c01c
+c00c
+c00e
+c006
+c006
+c006
+c006
+c006
+c006
+c006
+c00e
+c00c
+c01c
+c078
+fff0
+ffc0
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 12 19 2 0
+BITMAP
+fff0
+fff0
+c000
+c000
+c000
+c000
+c000
+c000
+ffe0
+ffe0
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+fff0
+fff0
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 11 19 2 0
+BITMAP
+ffe0
+ffe0
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+ffc0
+ffc0
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 16 19 1 0
+BITMAP
+07e0
+1ff8
+3c3c
+700e
+6006
+e006
+c000
+c000
+c000
+c07f
+c07f
+c003
+c003
+e003
+6007
+700f
+3c3f
+1ffb
+07e3
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 14 19 2 0
+BITMAP
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+fffc
+fffc
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 2 19 3 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 10 19 1 0
+BITMAP
+00c0
+00c0
+00c0
+00c0
+00c0
+00c0
+00c0
+00c0
+00c0
+00c0
+00c0
+00c0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+7f80
+3f00
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 15 19 2 0
+BITMAP
+c038
+c070
+c0e0
+c1c0
+c380
+c700
+ce00
+dc00
+fc00
+fe00
+e700
+c380
+c1c0
+c0e0
+c070
+c038
+c01c
+c00e
+c006
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 11 19 2 0
+BITMAP
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+ffe0
+ffe0
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 840 0
+DWIDTH 21 0
+BBX 17 19 2 0
+BITMAP
+c00180
+e00380
+e00380
+f00780
+f00780
+d80d80
+d80d80
+d80d80
+cc1980
+cc1980
+cc1980
+c63180
+c63180
+c63180
+c36180
+c36180
+c36180
+c1c180
+c1c180
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 14 19 2 0
+BITMAP
+e00c
+f00c
+f00c
+d80c
+dc0c
+cc0c
+ce0c
+c60c
+c70c
+c30c
+c38c
+c18c
+c1cc
+c0cc
+c0ec
+c06c
+c03c
+c03c
+c01c
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 19 1 0
+BITMAP
+07e0
+1ff8
+3c3c
+700e
+6006
+e007
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+e007
+6006
+700e
+3c3c
+1ff8
+07e0
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 13 19 2 0
+BITMAP
+ffe0
+fff0
+c030
+c018
+c018
+c018
+c018
+c030
+fff0
+ffe0
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 19 1 0
+BITMAP
+07e0
+1ff8
+3c3c
+700e
+6006
+e007
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+e007
+60e6
+707e
+3c1c
+1ffe
+07e7
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 13 19 2 0
+BITMAP
+ffe0
+fff0
+c030
+c018
+c018
+c018
+c018
+c030
+fff0
+ffe0
+c070
+c030
+c018
+c018
+c018
+c018
+c018
+c018
+c018
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 19 1 0
+BITMAP
+07c0
+1ff0
+3838
+7018
+6018
+6000
+7000
+3e00
+0fc0
+01f0
+0078
+001c
+000c
+c00c
+c00c
+e01c
+7838
+3ff0
+0fc0
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 19 1 0
+BITMAP
+fffc
+fffc
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 14 19 2 0
+BITMAP
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+6018
+7038
+3ff0
+0fc0
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 19 1 0
+BITMAP
+c006
+c006
+e00e
+600c
+701c
+3018
+3018
+3838
+1830
+1830
+1c70
+0c60
+0c60
+0ee0
+06c0
+06c0
+0380
+0380
+0380
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 880 0
+DWIDTH 22 0
+BBX 20 19 1 0
+BITMAP
+c06030
+c06030
+c06030
+c0f030
+60f060
+619860
+619860
+619860
+619860
+3198c0
+330cc0
+330cc0
+330cc0
+1b0d80
+1b0d80
+1e0780
+0e0700
+0c0300
+0c0300
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 19 1 0
+BITMAP
+c006
+e00e
+701c
+3018
+1830
+1c70
+0ee0
+07c0
+0380
+0380
+07c0
+0ee0
+0c60
+1c70
+3838
+3018
+600c
+e00e
+c006
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 19 1 0
+BITMAP
+c00c
+e01c
+6018
+7038
+3030
+3870
+1860
+1ce0
+0cc0
+0fc0
+0780
+0780
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 19 1 0
+BITMAP
+fff8
+fff8
+0038
+0070
+00e0
+01c0
+01c0
+0380
+0700
+0700
+0e00
+1c00
+1c00
+3800
+7000
+7000
+e000
+fff8
+fff8
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 4 24 2 -5
+BITMAP
+f0
+f0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+f0
+f0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 7 19 0 0
+BITMAP
+c0
+c0
+c0
+60
+60
+60
+30
+30
+30
+30
+18
+18
+18
+0c
+0c
+0c
+06
+06
+06
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 4 24 1 -5
+BITMAP
+f0
+f0
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+f0
+f0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 9 1 10
+BITMAP
+0c00
+0c00
+1e00
+1200
+3300
+6180
+6180
+c0c0
+c0c0
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 14 2 0 -5
+BITMAP
+fffc
+fffc
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 6 2 13
+BITMAP
+40
+80
+80
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+1f00
+3f80
+61c0
+60c0
+00c0
+07c0
+3fc0
+78c0
+e0c0
+c0c0
+c1c0
+e3c0
+7ee0
+3c60
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 11 19 2 0
+BITMAP
+c000
+c000
+c000
+c000
+c000
+cf00
+df80
+f1c0
+e0c0
+c060
+c060
+c060
+c060
+c060
+c060
+e0c0
+f1c0
+df80
+cf00
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 14 1 0
+BITMAP
+1f00
+3f80
+71c0
+60c0
+c000
+c000
+c000
+c000
+c000
+c000
+60c0
+71c0
+3f80
+1f00
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 11 19 1 0
+BITMAP
+0060
+0060
+0060
+0060
+0060
+1e60
+3f60
+71e0
+60e0
+c060
+c060
+c060
+c060
+c060
+c060
+60e0
+71e0
+3f60
+1e60
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+0e00
+3f80
+71c0
+60c0
+c060
+c060
+ffe0
+ffe0
+c000
+c000
+6060
+70e0
+3fc0
+0f00
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 19 1 0
+BITMAP
+1c
+3c
+30
+30
+30
+fc
+fc
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 11 19 1 -5
+BITMAP
+1e60
+3f60
+71e0
+60e0
+c060
+c060
+c060
+c060
+c060
+c060
+60e0
+71e0
+3f60
+1e60
+0060
+c060
+e0c0
+7fc0
+1f00
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 10 19 2 0
+BITMAP
+c000
+c000
+c000
+c000
+c000
+ce00
+df80
+f180
+e0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 19 2 0
+BITMAP
+c0
+c0
+c0
+00
+00
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 4 24 0 -5
+BITMAP
+30
+30
+30
+00
+00
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+f0
+e0
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 19 2 0
+BITMAP
+c000
+c000
+c000
+c000
+c000
+c180
+c380
+c700
+ce00
+dc00
+f800
+fc00
+ce00
+c600
+c700
+c380
+c180
+c1c0
+c0c0
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 19 2 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 800 0
+DWIDTH 20 0
+BBX 16 14 2 0
+BITMAP
+ce3c
+ff7e
+e3c7
+c183
+c183
+c183
+c183
+c183
+c183
+c183
+c183
+c183
+c183
+c183
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 14 2 0
+BITMAP
+ce00
+df80
+f180
+e0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+1f00
+3f80
+71c0
+60c0
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+71c0
+3f80
+1f00
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 11 19 2 -5
+BITMAP
+cf00
+df80
+f1c0
+e0c0
+c060
+c060
+c060
+c060
+c060
+c060
+e0c0
+f1c0
+df80
+cf00
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 11 19 1 -5
+BITMAP
+1e60
+3f60
+71e0
+60e0
+c060
+c060
+c060
+c060
+c060
+c060
+60e0
+71e0
+3f60
+1e60
+0060
+0060
+0060
+0060
+0060
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 360 0
+DWIDTH 9 0
+BBX 6 14 2 0
+BITMAP
+cc
+dc
+fc
+e0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 14 1 0
+BITMAP
+3e00
+7f80
+e1c0
+c0c0
+c000
+f800
+7f00
+0f80
+01c0
+00c0
+c0c0
+c1c0
+ff80
+3f00
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 18 1 0
+BITMAP
+30
+30
+30
+30
+fc
+fc
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+3c
+1c
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 14 2 0
+BITMAP
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c1c0
+63c0
+7ec0
+1cc0
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 14 1 0
+BITMAP
+c060
+c060
+c060
+60c0
+60c0
+71c0
+3180
+3180
+1b00
+1b00
+1b00
+0e00
+0e00
+0e00
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 18 14 0 0
+BITMAP
+c0c0c0
+c0c0c0
+61e180
+61e180
+61e180
+312300
+333300
+333300
+1b3600
+1a1600
+1e1e00
+0e1c00
+0c0c00
+0c0c00
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 14 1 0
+BITMAP
+c0c0
+e1c0
+6180
+3300
+3f00
+1e00
+0c00
+1e00
+1e00
+3300
+7380
+6180
+e1c0
+c0c0
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 12 19 0 -5
+BITMAP
+c030
+c030
+6030
+7060
+3060
+38e0
+18c0
+18c0
+0d80
+0d80
+0780
+0700
+0300
+0300
+0600
+0600
+0c00
+3c00
+3800
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 14 1 0
+BITMAP
+ffc0
+ffc0
+0180
+0300
+0700
+0e00
+0c00
+1c00
+3800
+3000
+6000
+e000
+ffc0
+ffc0
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 24 1 -5
+BITMAP
+0c
+18
+30
+30
+30
+30
+30
+30
+30
+30
+60
+c0
+c0
+60
+30
+30
+30
+30
+30
+30
+30
+30
+18
+0c
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 24 2 -5
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 24 1 -5
+BITMAP
+c0
+60
+30
+30
+30
+30
+30
+30
+30
+30
+18
+0c
+0c
+18
+30
+30
+30
+30
+30
+30
+30
+30
+60
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 4 2 5
+BITMAP
+70c0
+fcc0
+cfc0
+c380
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 19 2 -5
+BITMAP
+c0
+c0
+c0
+00
+00
+40
+40
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 10 18 1 -2
+BITMAP
+0180
+0180
+1f00
+3f80
+73c0
+66c0
+c600
+cc00
+cc00
+cc00
+d800
+d800
+d8c0
+71c0
+7f80
+3f00
+6000
+6000
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 18 1 0
+BITMAP
+1f80
+3fe0
+7070
+6030
+6000
+7000
+3000
+1800
+ff80
+ff80
+1800
+1800
+1800
+3000
+3000
+6730
+fff0
+f0e0
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 12 1 3
+BITMAP
+c060
+eee0
+7fc0
+3180
+60c0
+60c0
+60c0
+60c0
+3180
+7fc0
+eee0
+c060
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 14 18 0 0
+BITMAP
+e01c
+6018
+7038
+3030
+3870
+1860
+1ce0
+0cc0
+3ff0
+3ff0
+0300
+3ff0
+3ff0
+0300
+0300
+0300
+0300
+0300
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 23 2 -4
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+00
+00
+00
+00
+00
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 24 1 -5
+BITMAP
+1f00
+3f80
+71c0
+60c0
+70c0
+3800
+1c00
+7e00
+e700
+c380
+c1c0
+c0c0
+6060
+7060
+3860
+1cc0
+0f80
+0700
+0380
+61c0
+60c0
+71c0
+3f80
+1f00
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 2 1 16
+BITMAP
+cc
+cc
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 17 17 1 0
+BITMAP
+07f000
+1c1c00
+300600
+600300
+43e100
+c63180
+8c1080
+880080
+880080
+880080
+8c1080
+c63180
+43e100
+600300
+300600
+1c1c00
+07f000
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 360 0
+DWIDTH 9 0
+BBX 7 12 1 7
+BITMAP
+78
+cc
+cc
+0c
+7c
+cc
+cc
+dc
+76
+00
+fe
+fe
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 9 8 2 3
+BITMAP
+1980
+3300
+6600
+cc00
+cc00
+6600
+3300
+1980
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 8 1 3
+BITMAP
+fff8
+fff8
+0018
+0018
+0018
+0018
+0018
+0018
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 2 1 6
+BITMAP
+fc
+fc
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 17 17 1 0
+BITMAP
+07f000
+1c1c00
+300600
+600300
+43e100
+c21180
+821080
+821080
+83e080
+824080
+822080
+c22180
+421100
+600300
+300600
+1c1c00
+07f000
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 2 1 16
+BITMAP
+fc
+fc
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 360 0
+DWIDTH 9 0
+BBX 8 7 0 11
+BITMAP
+3c
+66
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+0600
+0600
+0600
+0600
+fff0
+fff0
+0600
+0600
+0600
+0600
+0000
+fff0
+fff0
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 7 10 0 8
+BITMAP
+3c
+7e
+c6
+06
+0c
+18
+30
+60
+fe
+fe
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 7 10 0 8
+BITMAP
+7c
+fe
+c6
+06
+3c
+3c
+06
+c6
+fe
+7c
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 5 4 1 15
+BITMAP
+18
+30
+60
+c0
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 19 2 -5
+BITMAP
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c1c0
+e3c0
+fec0
+dcc0
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 23 1 -4
+BITMAP
+1fc0
+7fc0
+7980
+f980
+f980
+f980
+f980
+f980
+f980
+7980
+7980
+3980
+1980
+1980
+1980
+1980
+1980
+1980
+1980
+1980
+1980
+1980
+1980
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 3 2 6
+BITMAP
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 5 6 1 -5
+BITMAP
+60
+70
+18
+18
+f8
+70
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 4 10 1 8
+BITMAP
+30
+30
+f0
+f0
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 360 0
+DWIDTH 9 0
+BBX 7 12 1 7
+BITMAP
+38
+6c
+c6
+c6
+c6
+c6
+c6
+6c
+38
+00
+fe
+fe
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 9 8 3 3
+BITMAP
+cc00
+6600
+3300
+1980
+1980
+3300
+6600
+cc00
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 18 18 1 0
+BITMAP
+301800
+301800
+f03000
+f03000
+306000
+306000
+30c000
+30c000
+318300
+318700
+030f00
+030f00
+061b00
+063300
+0c7fc0
+0c7fc0
+180300
+180300
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 18 18 1 0
+BITMAP
+301800
+301800
+f03000
+f03000
+306000
+306000
+30c000
+30c000
+318780
+318fc0
+0318c0
+0300c0
+060180
+060300
+0c0600
+0c0c00
+181fc0
+181fc0
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 19 18 0 0
+BITMAP
+7c0c00
+fe0c00
+c61800
+061800
+3c3000
+3c3000
+066000
+c66000
+fec180
+7cc380
+018780
+018780
+030d80
+031980
+063fe0
+063fe0
+0c0180
+0c0180
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 19 1 -5
+BITMAP
+0c00
+0c00
+0c00
+0000
+0000
+0c00
+0c00
+0c00
+0c00
+1800
+3800
+7000
+6000
+e0c0
+c0c0
+c1c0
+e380
+7f80
+3e00
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 24 1 0
+BITMAP
+0c00
+0600
+0300
+0180
+0000
+0380
+0380
+06c0
+06c0
+0c40
+0c60
+0c60
+1830
+1830
+1830
+3018
+3ff8
+3ff8
+600c
+600c
+600c
+c006
+c006
+c006
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 24 1 0
+BITMAP
+0060
+00c0
+0180
+0300
+0000
+0380
+0380
+06c0
+06c0
+0c40
+0c60
+0c60
+1830
+1830
+1830
+3018
+3ff8
+3ff8
+600c
+600c
+600c
+c006
+c006
+c006
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 24 1 0
+BITMAP
+0180
+03c0
+0660
+0c30
+0000
+0380
+0380
+06c0
+06c0
+0c40
+0c60
+0c60
+1830
+1830
+1830
+3018
+3ff8
+3ff8
+600c
+600c
+600c
+c006
+c006
+c006
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 23 1 0
+BITMAP
+0710
+0db0
+08e0
+0000
+0380
+0380
+06c0
+06c0
+0c40
+0c60
+0c60
+1830
+1830
+1830
+3018
+3ff8
+3ff8
+600c
+600c
+600c
+c006
+c006
+c006
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 23 1 0
+BITMAP
+0c60
+0c60
+0000
+0000
+0380
+0380
+06c0
+06c0
+0c40
+0c60
+0c60
+1830
+1830
+1830
+3018
+3ff8
+3ff8
+600c
+600c
+600c
+c006
+c006
+c006
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 24 1 0
+BITMAP
+0380
+06c0
+06c0
+0380
+0000
+0380
+0380
+06c0
+06c0
+0c40
+0c60
+0c60
+1830
+1830
+1830
+3018
+3ff8
+3ff8
+600c
+600c
+600c
+c006
+c006
+c006
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 920 0
+DWIDTH 23 0
+BBX 21 19 1 0
+BITMAP
+03fff8
+03fff8
+066000
+066000
+0c6000
+0c6000
+0c6000
+186000
+187ff8
+187ff8
+306000
+3fe000
+3fe000
+606000
+606000
+606000
+c06000
+c07ff8
+c07ff8
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 24 1 -5
+BITMAP
+07e0
+1ff8
+3c3c
+700e
+6006
+e006
+c000
+c000
+c000
+c000
+c000
+c000
+c003
+e003
+6006
+700e
+3c3c
+1ff8
+07e0
+0380
+00c0
+00c0
+07c0
+0380
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 12 24 2 0
+BITMAP
+3000
+1800
+0c00
+0600
+0000
+fff0
+fff0
+c000
+c000
+c000
+c000
+c000
+c000
+ffe0
+ffe0
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+fff0
+fff0
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 12 24 2 0
+BITMAP
+0180
+0300
+0600
+0c00
+0000
+fff0
+fff0
+c000
+c000
+c000
+c000
+c000
+c000
+ffe0
+ffe0
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+fff0
+fff0
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 12 24 2 0
+BITMAP
+0600
+0f00
+1980
+30c0
+0000
+fff0
+fff0
+c000
+c000
+c000
+c000
+c000
+c000
+ffe0
+ffe0
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+fff0
+fff0
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 12 23 2 0
+BITMAP
+1980
+1980
+0000
+0000
+fff0
+fff0
+c000
+c000
+c000
+c000
+c000
+c000
+ffe0
+ffe0
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+fff0
+fff0
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 5 24 1 0
+BITMAP
+c0
+60
+30
+18
+00
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 5 24 2 0
+BITMAP
+18
+30
+60
+c0
+00
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 8 24 0 0
+BITMAP
+18
+3c
+66
+c3
+00
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 23 1 0
+BITMAP
+cc
+cc
+00
+00
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 17 19 0 0
+BITMAP
+3ff000
+3ffc00
+301e00
+300700
+300300
+300380
+300180
+300180
+ff0180
+ff0180
+300180
+300180
+300180
+300380
+300300
+300700
+301e00
+3ffc00
+3ff000
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 14 23 2 0
+BITMAP
+0710
+0db0
+08e0
+0000
+e00c
+f00c
+f00c
+d80c
+dc0c
+cc0c
+ce0c
+c60c
+c70c
+c30c
+c38c
+c18c
+c1cc
+c0cc
+c0ec
+c06c
+c03c
+c03c
+c01c
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 24 1 0
+BITMAP
+0600
+0300
+0180
+00c0
+0000
+07e0
+1ff8
+3c3c
+700e
+6006
+e007
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+e007
+6006
+700e
+3c3c
+1ff8
+07e0
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 24 1 0
+BITMAP
+0030
+0060
+00c0
+0180
+0000
+07e0
+1ff8
+3c3c
+700e
+6006
+e007
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+e007
+6006
+700e
+3c3c
+1ff8
+07e0
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 24 1 0
+BITMAP
+0180
+03c0
+0660
+0c30
+0000
+07e0
+1ff8
+3c3c
+700e
+6006
+e007
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+e007
+6006
+700e
+3c3c
+1ff8
+07e0
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 23 1 0
+BITMAP
+0710
+0db0
+08e0
+0000
+07e0
+1ff8
+3c3c
+700e
+6006
+e007
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+e007
+6006
+700e
+3c3c
+1ff8
+07e0
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 23 1 0
+BITMAP
+0660
+0660
+0000
+0000
+07e0
+1ff8
+3c3c
+700e
+6006
+e007
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+e007
+6006
+700e
+3c3c
+1ff8
+07e0
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 13 12 0 1
+BITMAP
+c018
+6030
+3060
+18c0
+0d80
+0700
+0700
+0d80
+18c0
+3060
+6030
+c018
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 18 19 0 0
+BITMAP
+03f040
+0ffcc0
+1e1f80
+380700
+300700
+700d80
+601980
+603180
+606180
+60c180
+618180
+630180
+660180
+6c0380
+380300
+380700
+7e1e00
+cffc00
+83f000
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 14 24 2 0
+BITMAP
+0c00
+0600
+0300
+0180
+0000
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+6018
+7038
+3ff0
+0fc0
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 14 24 2 0
+BITMAP
+0060
+00c0
+0180
+0300
+0000
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+6018
+7038
+3ff0
+0fc0
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 14 24 2 0
+BITMAP
+0300
+0780
+0cc0
+1860
+0000
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+6018
+7038
+3ff0
+0fc0
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 14 23 2 0
+BITMAP
+0cc0
+0cc0
+0000
+0000
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+c00c
+6018
+7038
+3ff0
+0fc0
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 24 1 0
+BITMAP
+0060
+00c0
+0180
+0300
+0000
+c00c
+e01c
+6018
+7038
+3030
+3870
+1860
+1ce0
+0cc0
+0fc0
+0780
+0780
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 13 19 2 0
+BITMAP
+c000
+c000
+c000
+c000
+ffe0
+fff0
+c030
+c018
+c018
+c018
+c018
+c030
+fff0
+ffe0
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 10 19 3 0
+BITMAP
+1c00
+7f00
+e300
+c180
+c180
+c180
+c300
+c700
+ce00
+cf00
+c380
+c180
+c0c0
+c0c0
+c0c0
+c180
+c380
+cf00
+ce00
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+1800
+0c00
+0600
+0300
+0000
+1f00
+3f80
+61c0
+60c0
+00c0
+07c0
+3fc0
+78c0
+e0c0
+c0c0
+c1c0
+e3c0
+7ee0
+3c60
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+0180
+0300
+0600
+0c00
+0000
+1f00
+3f80
+61c0
+60c0
+00c0
+07c0
+3fc0
+78c0
+e0c0
+c0c0
+c1c0
+e3c0
+7ee0
+3c60
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+0600
+0f00
+1980
+30c0
+0000
+1f00
+3f80
+61c0
+60c0
+00c0
+07c0
+3fc0
+78c0
+e0c0
+c0c0
+c1c0
+e3c0
+7ee0
+3c60
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+1c40
+36c0
+2380
+0000
+0000
+1f00
+3f80
+61c0
+60c0
+00c0
+07c0
+3fc0
+78c0
+e0c0
+c0c0
+c1c0
+e3c0
+7ee0
+3c60
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+1980
+1980
+0000
+0000
+1f00
+3f80
+61c0
+60c0
+00c0
+07c0
+3fc0
+78c0
+e0c0
+c0c0
+c1c0
+e3c0
+7ee0
+3c60
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+0e00
+1b00
+1100
+1b00
+0e00
+1f00
+3f80
+61c0
+60c0
+00c0
+07c0
+3fc0
+78c0
+e0c0
+c0c0
+c1c0
+e3c0
+7ee0
+3c60
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 840 0
+DWIDTH 21 0
+BBX 19 14 1 0
+BITMAP
+1f0e00
+3fbf80
+61f1c0
+60e0c0
+00c060
+07c060
+3fffe0
+78ffe0
+e0c000
+c0c000
+c1e060
+e3f0e0
+7e3fc0
+3c0f00
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 19 1 -5
+BITMAP
+1f00
+3f80
+71c0
+60c0
+c000
+c000
+c000
+c000
+c000
+c000
+60c0
+71c0
+3f80
+1f00
+1c00
+0600
+0600
+3e00
+1c00
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+1800
+0c00
+0600
+0300
+0000
+0e00
+3f80
+71c0
+60c0
+c060
+c060
+ffe0
+ffe0
+c000
+c000
+6060
+70e0
+3fc0
+0f00
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+0180
+0300
+0600
+0c00
+0000
+0e00
+3f80
+71c0
+60c0
+c060
+c060
+ffe0
+ffe0
+c000
+c000
+6060
+70e0
+3fc0
+0f00
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+0600
+0f00
+1980
+30c0
+0000
+0e00
+3f80
+71c0
+60c0
+c060
+c060
+ffe0
+ffe0
+c000
+c000
+6060
+70e0
+3fc0
+0f00
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+1980
+1980
+0000
+0000
+0e00
+3f80
+71c0
+60c0
+c060
+c060
+ffe0
+ffe0
+c000
+c000
+6060
+70e0
+3fc0
+0f00
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 5 19 0 0
+BITMAP
+c0
+60
+30
+18
+00
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 5 19 1 0
+BITMAP
+18
+30
+60
+c0
+00
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 8 19 -1 0
+BITMAP
+18
+3c
+66
+c3
+00
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 6 18 0 0
+BITMAP
+cc
+cc
+00
+00
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+e000
+7180
+1e00
+3c00
+c600
+1f00
+3f80
+71c0
+60c0
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+71c0
+3f80
+1f00
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 18 2 0
+BITMAP
+3880
+6d80
+4700
+0000
+ce00
+df80
+f180
+e0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+1800
+0c00
+0600
+0300
+0000
+1f00
+3f80
+71c0
+60c0
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+71c0
+3f80
+1f00
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+0180
+0300
+0600
+0c00
+0000
+1f00
+3f80
+71c0
+60c0
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+71c0
+3f80
+1f00
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 19 1 0
+BITMAP
+0600
+0f00
+1980
+30c0
+0000
+1f00
+3f80
+71c0
+60c0
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+71c0
+3f80
+1f00
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+1c40
+36c0
+2380
+0000
+1f00
+3f80
+71c0
+60c0
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+71c0
+3f80
+1f00
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 18 1 0
+BITMAP
+3180
+3180
+0000
+0000
+1f00
+3f80
+71c0
+60c0
+c060
+c060
+c060
+c060
+c060
+c060
+60c0
+71c0
+3f80
+1f00
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 12 1 1
+BITMAP
+0600
+0600
+0600
+0000
+0000
+fff0
+fff0
+0000
+0000
+0600
+0600
+0600
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 13 14 0 0
+BITMAP
+0f98
+1ff0
+38e0
+3060
+60f0
+61b0
+6330
+6630
+6630
+6c30
+3860
+30e0
+7fc0
+cf80
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 19 2 0
+BITMAP
+3000
+1800
+0c00
+0600
+0000
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c1c0
+63c0
+7ec0
+1cc0
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 19 2 0
+BITMAP
+0180
+0300
+0600
+0c00
+0000
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c1c0
+63c0
+7ec0
+1cc0
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 19 2 0
+BITMAP
+0c00
+1e00
+3300
+6180
+0000
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c1c0
+63c0
+7ec0
+1cc0
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 18 2 0
+BITMAP
+3300
+3300
+0000
+0000
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c1c0
+63c0
+7ec0
+1cc0
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 12 24 0 -5
+BITMAP
+00c0
+0180
+0300
+0600
+0000
+c030
+c030
+6030
+7060
+3060
+38e0
+18c0
+18c0
+0d80
+0d80
+0780
+0700
+0300
+0300
+0600
+0600
+0c00
+3c00
+3800
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 11 24 2 -5
+BITMAP
+c000
+c000
+c000
+c000
+c000
+cf00
+df80
+f1c0
+e0c0
+c060
+c060
+c060
+c060
+c060
+c060
+e0c0
+f1c0
+df80
+cf00
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 12 23 0 -5
+BITMAP
+1980
+1980
+0000
+0000
+c030
+c030
+6030
+7060
+3060
+38e0
+18c0
+18c0
+0d80
+0d80
+0780
+0700
+0300
+0300
+0600
+0600
+0c00
+3c00
+3800
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica24.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica24.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica24.h	(revision 22322)
@@ -0,0 +1,1170 @@
+static unsigned char helvetica24_0_bits[] = {
+0x00};
+static unsigned char helvetica24_1_bits[] = {
+0x00};
+static unsigned char helvetica24_2_bits[] = {
+0x00};
+static unsigned char helvetica24_3_bits[] = {
+0x00};
+static unsigned char helvetica24_4_bits[] = {
+0x00};
+static unsigned char helvetica24_5_bits[] = {
+0x00};
+static unsigned char helvetica24_6_bits[] = {
+0x00};
+static unsigned char helvetica24_7_bits[] = {
+0x00};
+static unsigned char helvetica24_8_bits[] = {
+0x00};
+static unsigned char helvetica24_9_bits[] = {
+0x00};
+static unsigned char helvetica24_10_bits[] = {
+0x00};
+static unsigned char helvetica24_11_bits[] = {
+0x00};
+static unsigned char helvetica24_12_bits[] = {
+0x00};
+static unsigned char helvetica24_13_bits[] = {
+0x00};
+static unsigned char helvetica24_14_bits[] = {
+0x00};
+static unsigned char helvetica24_15_bits[] = {
+0x00};
+static unsigned char helvetica24_16_bits[] = {
+0x00};
+static unsigned char helvetica24_17_bits[] = {
+0x00};
+static unsigned char helvetica24_18_bits[] = {
+0x00};
+static unsigned char helvetica24_19_bits[] = {
+0x00};
+static unsigned char helvetica24_20_bits[] = {
+0x00};
+static unsigned char helvetica24_21_bits[] = {
+0x00};
+static unsigned char helvetica24_22_bits[] = {
+0x00};
+static unsigned char helvetica24_23_bits[] = {
+0x00};
+static unsigned char helvetica24_24_bits[] = {
+0x00};
+static unsigned char helvetica24_25_bits[] = {
+0x00};
+static unsigned char helvetica24_26_bits[] = {
+0x00};
+static unsigned char helvetica24_27_bits[] = {
+0x00};
+static unsigned char helvetica24_28_bits[] = {
+0x00};
+static unsigned char helvetica24_29_bits[] = {
+0x00};
+static unsigned char helvetica24_30_bits[] = {
+0x00};
+static unsigned char helvetica24_31_bits[] = {
+0x00, 0x00};
+static unsigned char helvetica24_32_bits[] = {
+0x00, 0x00};
+static unsigned char helvetica24_33_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x01, 0x01, 0x00, 0x00, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_34_bits[] = {
+0x33, 0x33, 0x33, 0x33, 0x33, 0x22};
+static unsigned char helvetica24_35_bits[] = {
+0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x98, 0x01, 0xff, 0x07, 0xff, 0x07, 
+0x98, 0x01, 0x98, 0x01, 0xcc, 0x00, 0xcc, 0x00, 0xff, 0x07, 0xff, 0x07, 
+0xcc, 0x00, 0xcc, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00};
+static unsigned char helvetica24_36_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0xfc, 0x01, 0xfe, 0x03, 0x67, 0x07, 0x63, 0x06, 
+0x63, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x7c, 0x00, 0xf0, 0x01, 0xe0, 0x03, 
+0x60, 0x07, 0x60, 0x06, 0x63, 0x06, 0x63, 0x06, 0x67, 0x07, 0xfe, 0x03, 
+0xfc, 0x01, 0x60, 0x00, 0x60, 0x00};
+static unsigned char helvetica24_37_bits[] = {
+0x00, 0x60, 0x00, 0x3c, 0x30, 0x00, 0x7e, 0x30, 0x00, 0xc3, 0x18, 0x00, 
+0xc3, 0x18, 0x00, 0xc3, 0x0c, 0x00, 0xc3, 0x0c, 0x00, 0x7e, 0x06, 0x00, 
+0x3c, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0xe3, 0x01, 0x80, 0xf1, 0x03, 
+0x80, 0x19, 0x06, 0xc0, 0x18, 0x06, 0xc0, 0x18, 0x06, 0x60, 0x18, 0x06, 
+0x60, 0xf0, 0x03, 0x20, 0xe0, 0x01};
+static unsigned char helvetica24_38_bits[] = {
+0xf0, 0x00, 0xf8, 0x01, 0x9c, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x03, 
+0x98, 0x01, 0xf0, 0x00, 0x78, 0x00, 0xfc, 0x18, 0xce, 0x19, 0x86, 0x1b, 
+0x03, 0x0f, 0x03, 0x06, 0x03, 0x0f, 0x87, 0x1b, 0xfe, 0x39, 0x78, 0x00};
+static unsigned char helvetica24_39_bits[] = {
+0x03, 0x03, 0x03, 0x02, 0x02, 0x01};
+static unsigned char helvetica24_40_bits[] = {
+0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18};
+static unsigned char helvetica24_41_bits[] = {
+0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03};
+static unsigned char helvetica24_42_bits[] = {
+0x08, 0x08, 0x6b, 0x3e, 0x1c, 0x36, 0x22};
+static unsigned char helvetica24_43_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xff, 0x0f, 
+0xff, 0x0f, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char helvetica24_44_bits[] = {
+0x03, 0x03, 0x03, 0x02, 0x02, 0x01};
+static unsigned char helvetica24_45_bits[] = {
+0xff, 0x07, 0xff, 0x07};
+static unsigned char helvetica24_46_bits[] = {
+0x03, 0x03, 0x03};
+static unsigned char helvetica24_47_bits[] = {
+0x60, 0x60, 0x60, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x0c, 
+0x0c, 0x06, 0x06, 0x06, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_48_bits[] = {
+0xf8, 0x00, 0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x06, 0x03, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x06, 0x03, 0x06, 0x03, 0x8e, 0x03, 0xfc, 0x01, 0xf8, 0x00};
+static unsigned char helvetica24_49_bits[] = {
+0x30, 0x30, 0x38, 0x3f, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 
+0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
+static unsigned char helvetica24_50_bits[] = {
+0x78, 0x00, 0xfe, 0x01, 0x86, 0x03, 0x03, 0x03, 0x03, 0x06, 0x03, 0x06, 
+0x00, 0x06, 0x00, 0x03, 0x80, 0x03, 0xc0, 0x01, 0xf0, 0x00, 0x38, 0x00, 
+0x1c, 0x00, 0x0e, 0x00, 0x07, 0x00, 0x03, 0x00, 0xff, 0x07, 0xff, 0x07};
+static unsigned char helvetica24_51_bits[] = {
+0xf8, 0x00, 0xfe, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x00, 0x03, 0x80, 0x01, 0xf0, 0x00, 0xf0, 0x03, 0x00, 0x03, 0x00, 0x06, 
+0x00, 0x06, 0x03, 0x06, 0x03, 0x03, 0x86, 0x03, 0xfe, 0x01, 0xf8, 0x00};
+static unsigned char helvetica24_52_bits[] = {
+0x80, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xe0, 0x01, 0xf0, 0x01, 0xb0, 0x01, 
+0x98, 0x01, 0x9c, 0x01, 0x8c, 0x01, 0x86, 0x01, 0x87, 0x01, 0x83, 0x01, 
+0xff, 0x07, 0xff, 0x07, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01};
+static unsigned char helvetica24_53_bits[] = {
+0xfe, 0x03, 0xfe, 0x03, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 
+0x76, 0x00, 0xfe, 0x01, 0x8e, 0x03, 0x00, 0x03, 0x00, 0x06, 0x00, 0x06, 
+0x00, 0x06, 0x03, 0x06, 0x03, 0x03, 0x87, 0x03, 0xfe, 0x01, 0x78, 0x00};
+static unsigned char helvetica24_54_bits[] = {
+0xf0, 0x00, 0xfc, 0x03, 0x0e, 0x03, 0x06, 0x06, 0x07, 0x06, 0x03, 0x00, 
+0x03, 0x00, 0xf3, 0x00, 0xfb, 0x01, 0x8f, 0x03, 0x07, 0x03, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x07, 0x06, 0x06, 0x03, 0xfe, 0x03, 0xf8, 0x00};
+static unsigned char helvetica24_55_bits[] = {
+0xff, 0x07, 0xff, 0x07, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0x80, 0x01, 
+0xc0, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x38, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x0c, 0x00};
+static unsigned char helvetica24_56_bits[] = {
+0x70, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x8c, 0x01, 0xf8, 0x00, 0xfc, 0x01, 0x06, 0x03, 0x07, 0x07, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x8e, 0x03, 0xfe, 0x03, 0xf8, 0x00};
+static unsigned char helvetica24_57_bits[] = {
+0xf8, 0x00, 0xfe, 0x03, 0x8e, 0x03, 0x07, 0x03, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x07, 0x07, 0x8e, 0x07, 0xfe, 0x06, 0x78, 0x06, 
+0x00, 0x06, 0x00, 0x07, 0x03, 0x03, 0x87, 0x03, 0xfe, 0x01, 0x78, 0x00};
+static unsigned char helvetica24_58_bits[] = {
+0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 
+0x03, 0x03};
+static unsigned char helvetica24_59_bits[] = {
+0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 
+0x03, 0x03, 0x02, 0x02, 0x01};
+static unsigned char helvetica24_60_bits[] = {
+0x00, 0x0c, 0x00, 0x0f, 0xc0, 0x03, 0xf0, 0x00, 0x3c, 0x00, 0x07, 0x00, 
+0x07, 0x00, 0x3c, 0x00, 0xf0, 0x00, 0xc0, 0x03, 0x00, 0x0f, 0x00, 0x0c};
+static unsigned char helvetica24_61_bits[] = {
+0xff, 0x03, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xff, 0x03};
+static unsigned char helvetica24_62_bits[] = {
+0x03, 0x00, 0x0f, 0x00, 0x3c, 0x00, 0xf0, 0x00, 0xc0, 0x03, 0x00, 0x0e, 
+0x00, 0x0e, 0xc0, 0x03, 0xf0, 0x00, 0x3c, 0x00, 0x0f, 0x00, 0x03, 0x00};
+static unsigned char helvetica24_63_bits[] = {
+0xf8, 0x00, 0xfe, 0x01, 0x8e, 0x03, 0x07, 0x03, 0x03, 0x03, 0x83, 0x03, 
+0x80, 0x01, 0xc0, 0x01, 0xe0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x30, 0x00};
+static unsigned char helvetica24_64_bits[] = {
+0x00, 0xff, 0x00, 0xc0, 0xff, 0x03, 0xf0, 0x80, 0x07, 0x38, 0x00, 0x0e, 
+0x1c, 0x00, 0x18, 0x0c, 0x00, 0x18, 0x06, 0xce, 0x30, 0x06, 0xdf, 0x30, 
+0x83, 0xe3, 0x30, 0xc3, 0x61, 0x30, 0xc3, 0x60, 0x30, 0x63, 0x60, 0x30, 
+0x63, 0x30, 0x38, 0x63, 0x30, 0x18, 0x63, 0x30, 0x1c, 0xe7, 0x38, 0x0e, 
+0xc6, 0xef, 0x07, 0x8e, 0xc7, 0x01, 0x1c, 0x00, 0x00, 0x38, 0x00, 0x00, 
+0xf0, 0xc0, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x3f, 0x00};
+static unsigned char helvetica24_65_bits[] = {
+0xc0, 0x01, 0xc0, 0x01, 0x60, 0x03, 0x60, 0x03, 0x30, 0x02, 0x30, 0x06, 
+0x30, 0x06, 0x18, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x0c, 0x18, 0xfc, 0x1f, 
+0xfc, 0x1f, 0x06, 0x30, 0x06, 0x30, 0x06, 0x30, 0x03, 0x60, 0x03, 0x60, 
+0x03, 0x60};
+static unsigned char helvetica24_66_bits[] = {
+0xff, 0x03, 0xff, 0x0f, 0x03, 0x0e, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 
+0x03, 0x18, 0x03, 0x0c, 0xff, 0x07, 0xff, 0x0f, 0x03, 0x18, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x38, 0x03, 0x1c, 0xff, 0x0f, 
+0xff, 0x03};
+static unsigned char helvetica24_67_bits[] = {
+0xe0, 0x07, 0xf8, 0x1f, 0x3c, 0x3c, 0x0e, 0x70, 0x06, 0x60, 0x07, 0x60, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0xc0, 0x07, 0xc0, 0x06, 0x60, 0x0e, 0x70, 0x3c, 0x3c, 0xf8, 0x1f, 
+0xe0, 0x07};
+static unsigned char helvetica24_68_bits[] = {
+0xff, 0x03, 0xff, 0x0f, 0x03, 0x1e, 0x03, 0x38, 0x03, 0x30, 0x03, 0x70, 
+0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 
+0x03, 0x60, 0x03, 0x70, 0x03, 0x30, 0x03, 0x38, 0x03, 0x1e, 0xff, 0x0f, 
+0xff, 0x03};
+static unsigned char helvetica24_69_bits[] = {
+0xff, 0x0f, 0xff, 0x0f, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0xff, 0x07, 0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x0f, 
+0xff, 0x0f};
+static unsigned char helvetica24_70_bits[] = {
+0xff, 0x07, 0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x03, 0xff, 0x03, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00};
+static unsigned char helvetica24_71_bits[] = {
+0xe0, 0x07, 0xf8, 0x1f, 0x3c, 0x3c, 0x0e, 0x70, 0x06, 0x60, 0x07, 0x60, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0xfe, 0x03, 0xfe, 0x03, 0xc0, 
+0x03, 0xc0, 0x07, 0xc0, 0x06, 0xe0, 0x0e, 0xf0, 0x3c, 0xfc, 0xf8, 0xdf, 
+0xe0, 0xc7};
+static unsigned char helvetica24_72_bits[] = {
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0xff, 0x3f, 0xff, 0x3f, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30};
+static unsigned char helvetica24_73_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_74_bits[] = {
+0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 
+0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x86, 0x01, 0xfe, 0x01, 
+0xfc, 0x00};
+static unsigned char helvetica24_75_bits[] = {
+0x03, 0x1c, 0x03, 0x0e, 0x03, 0x07, 0x83, 0x03, 0xc3, 0x01, 0xe3, 0x00, 
+0x73, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x7f, 0x00, 0xe7, 0x00, 0xc3, 0x01, 
+0x83, 0x03, 0x03, 0x07, 0x03, 0x0e, 0x03, 0x1c, 0x03, 0x38, 0x03, 0x70, 
+0x03, 0x60};
+static unsigned char helvetica24_76_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x07, 
+0xff, 0x07};
+static unsigned char helvetica24_77_bits[] = {
+0x03, 0x80, 0x01, 0x07, 0xc0, 0x01, 0x07, 0xc0, 0x01, 0x0f, 0xe0, 0x01, 
+0x0f, 0xe0, 0x01, 0x1b, 0xb0, 0x01, 0x1b, 0xb0, 0x01, 0x1b, 0xb0, 0x01, 
+0x33, 0x98, 0x01, 0x33, 0x98, 0x01, 0x33, 0x98, 0x01, 0x63, 0x8c, 0x01, 
+0x63, 0x8c, 0x01, 0x63, 0x8c, 0x01, 0xc3, 0x86, 0x01, 0xc3, 0x86, 0x01, 
+0xc3, 0x86, 0x01, 0x83, 0x83, 0x01, 0x83, 0x83, 0x01};
+static unsigned char helvetica24_78_bits[] = {
+0x07, 0x30, 0x0f, 0x30, 0x0f, 0x30, 0x1b, 0x30, 0x3b, 0x30, 0x33, 0x30, 
+0x73, 0x30, 0x63, 0x30, 0xe3, 0x30, 0xc3, 0x30, 0xc3, 0x31, 0x83, 0x31, 
+0x83, 0x33, 0x03, 0x33, 0x03, 0x37, 0x03, 0x36, 0x03, 0x3c, 0x03, 0x3c, 
+0x03, 0x38};
+static unsigned char helvetica24_79_bits[] = {
+0xe0, 0x07, 0xf8, 0x1f, 0x3c, 0x3c, 0x0e, 0x70, 0x06, 0x60, 0x07, 0xe0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x03, 0xc0, 0x07, 0xe0, 0x06, 0x60, 0x0e, 0x70, 0x3c, 0x3c, 0xf8, 0x1f, 
+0xe0, 0x07};
+static unsigned char helvetica24_80_bits[] = {
+0xff, 0x07, 0xff, 0x0f, 0x03, 0x0c, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 
+0x03, 0x18, 0x03, 0x0c, 0xff, 0x0f, 0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00};
+static unsigned char helvetica24_81_bits[] = {
+0xe0, 0x07, 0xf8, 0x1f, 0x3c, 0x3c, 0x0e, 0x70, 0x06, 0x60, 0x07, 0xe0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x03, 0xc0, 0x07, 0xe0, 0x06, 0x67, 0x0e, 0x7e, 0x3c, 0x38, 0xf8, 0x7f, 
+0xe0, 0xe7};
+static unsigned char helvetica24_82_bits[] = {
+0xff, 0x07, 0xff, 0x0f, 0x03, 0x0c, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 
+0x03, 0x18, 0x03, 0x0c, 0xff, 0x0f, 0xff, 0x07, 0x03, 0x0e, 0x03, 0x0c, 
+0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 
+0x03, 0x18};
+static unsigned char helvetica24_83_bits[] = {
+0xe0, 0x03, 0xf8, 0x0f, 0x1c, 0x1c, 0x0e, 0x18, 0x06, 0x18, 0x06, 0x00, 
+0x0e, 0x00, 0x7c, 0x00, 0xf0, 0x03, 0x80, 0x0f, 0x00, 0x1e, 0x00, 0x38, 
+0x00, 0x30, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x1e, 0x1c, 0xfc, 0x0f, 
+0xf0, 0x03};
+static unsigned char helvetica24_84_bits[] = {
+0xff, 0x3f, 0xff, 0x3f, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00};
+static unsigned char helvetica24_85_bits[] = {
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x06, 0x18, 0x0e, 0x1c, 0xfc, 0x0f, 
+0xf0, 0x03};
+static unsigned char helvetica24_86_bits[] = {
+0x03, 0x60, 0x03, 0x60, 0x07, 0x70, 0x06, 0x30, 0x0e, 0x38, 0x0c, 0x18, 
+0x0c, 0x18, 0x1c, 0x1c, 0x18, 0x0c, 0x18, 0x0c, 0x38, 0x0e, 0x30, 0x06, 
+0x30, 0x06, 0x70, 0x07, 0x60, 0x03, 0x60, 0x03, 0xc0, 0x01, 0xc0, 0x01, 
+0xc0, 0x01};
+static unsigned char helvetica24_87_bits[] = {
+0x03, 0x06, 0x0c, 0x03, 0x06, 0x0c, 0x03, 0x06, 0x0c, 0x03, 0x0f, 0x0c, 
+0x06, 0x0f, 0x06, 0x86, 0x19, 0x06, 0x86, 0x19, 0x06, 0x86, 0x19, 0x06, 
+0x86, 0x19, 0x06, 0x8c, 0x19, 0x03, 0xcc, 0x30, 0x03, 0xcc, 0x30, 0x03, 
+0xcc, 0x30, 0x03, 0xd8, 0xb0, 0x01, 0xd8, 0xb0, 0x01, 0x78, 0xe0, 0x01, 
+0x70, 0xe0, 0x00, 0x30, 0xc0, 0x00, 0x30, 0xc0, 0x00};
+static unsigned char helvetica24_88_bits[] = {
+0x03, 0x60, 0x07, 0x70, 0x0e, 0x38, 0x0c, 0x18, 0x18, 0x0c, 0x38, 0x0e, 
+0x70, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0xc0, 0x01, 0xe0, 0x03, 0x70, 0x07, 
+0x30, 0x06, 0x38, 0x0e, 0x1c, 0x1c, 0x0c, 0x18, 0x06, 0x30, 0x07, 0x70, 
+0x03, 0x60};
+static unsigned char helvetica24_89_bits[] = {
+0x03, 0x30, 0x07, 0x38, 0x06, 0x18, 0x0e, 0x1c, 0x0c, 0x0c, 0x1c, 0x0e, 
+0x18, 0x06, 0x38, 0x07, 0x30, 0x03, 0xf0, 0x03, 0xe0, 0x01, 0xe0, 0x01, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00};
+static unsigned char helvetica24_90_bits[] = {
+0xff, 0x1f, 0xff, 0x1f, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 
+0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 
+0x38, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x07, 0x00, 0xff, 0x1f, 
+0xff, 0x1f};
+static unsigned char helvetica24_91_bits[] = {
+0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f};
+static unsigned char helvetica24_92_bits[] = {
+0x03, 0x03, 0x03, 0x06, 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x18, 
+0x18, 0x30, 0x30, 0x30, 0x60, 0x60, 0x60};
+static unsigned char helvetica24_93_bits[] = {
+0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f};
+static unsigned char helvetica24_94_bits[] = {
+0x30, 0x00, 0x30, 0x00, 0x78, 0x00, 0x48, 0x00, 0xcc, 0x00, 0x86, 0x01, 
+0x86, 0x01, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_95_bits[] = {
+0xff, 0x3f, 0xff, 0x3f};
+static unsigned char helvetica24_96_bits[] = {
+0x02, 0x01, 0x01, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_97_bits[] = {
+0xf8, 0x00, 0xfc, 0x01, 0x86, 0x03, 0x06, 0x03, 0x00, 0x03, 0xe0, 0x03, 
+0xfc, 0x03, 0x1e, 0x03, 0x07, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc7, 0x03, 
+0x7e, 0x07, 0x3c, 0x06};
+static unsigned char helvetica24_98_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xf3, 0x00, 
+0xfb, 0x01, 0x8f, 0x03, 0x07, 0x03, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x07, 0x03, 0x8f, 0x03, 0xfb, 0x01, 
+0xf3, 0x00};
+static unsigned char helvetica24_99_bits[] = {
+0xf8, 0x00, 0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x06, 0x03, 0x8e, 0x03, 
+0xfc, 0x01, 0xf8, 0x00};
+static unsigned char helvetica24_100_bits[] = {
+0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x78, 0x06, 
+0xfc, 0x06, 0x8e, 0x07, 0x06, 0x07, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x07, 0x8e, 0x07, 0xfc, 0x06, 
+0x78, 0x06};
+static unsigned char helvetica24_101_bits[] = {
+0x70, 0x00, 0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 
+0xff, 0x07, 0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 0x06, 0x06, 0x0e, 0x07, 
+0xfc, 0x03, 0xf0, 0x00};
+static unsigned char helvetica24_102_bits[] = {
+0x38, 0x3c, 0x0c, 0x0c, 0x0c, 0x3f, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c};
+static unsigned char helvetica24_103_bits[] = {
+0x78, 0x06, 0xfc, 0x06, 0x8e, 0x07, 0x06, 0x07, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x07, 0x8e, 0x07, 
+0xfc, 0x06, 0x78, 0x06, 0x00, 0x06, 0x03, 0x06, 0x07, 0x03, 0xfe, 0x03, 
+0xf8, 0x00};
+static unsigned char helvetica24_104_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x73, 0x00, 
+0xfb, 0x01, 0x8f, 0x01, 0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03};
+static unsigned char helvetica24_105_bits[] = {
+0x03, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_106_bits[] = {
+0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x07};
+static unsigned char helvetica24_107_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x83, 0x01, 
+0xc3, 0x01, 0xe3, 0x00, 0x73, 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x3f, 0x00, 
+0x73, 0x00, 0x63, 0x00, 0xe3, 0x00, 0xc3, 0x01, 0x83, 0x01, 0x83, 0x03, 
+0x03, 0x03};
+static unsigned char helvetica24_108_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_109_bits[] = {
+0x73, 0x3c, 0xff, 0x7e, 0xc7, 0xe3, 0x83, 0xc1, 0x83, 0xc1, 0x83, 0xc1, 
+0x83, 0xc1, 0x83, 0xc1, 0x83, 0xc1, 0x83, 0xc1, 0x83, 0xc1, 0x83, 0xc1, 
+0x83, 0xc1, 0x83, 0xc1};
+static unsigned char helvetica24_110_bits[] = {
+0x73, 0x00, 0xfb, 0x01, 0x8f, 0x01, 0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_111_bits[] = {
+0xf8, 0x00, 0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0x8e, 0x03, 
+0xfc, 0x01, 0xf8, 0x00};
+static unsigned char helvetica24_112_bits[] = {
+0xf3, 0x00, 0xfb, 0x01, 0x8f, 0x03, 0x07, 0x03, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x07, 0x03, 0x8f, 0x03, 
+0xfb, 0x01, 0xf3, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00};
+static unsigned char helvetica24_113_bits[] = {
+0x78, 0x06, 0xfc, 0x06, 0x8e, 0x07, 0x06, 0x07, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x07, 0x8e, 0x07, 
+0xfc, 0x06, 0x78, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 
+0x00, 0x06};
+static unsigned char helvetica24_114_bits[] = {
+0x33, 0x3b, 0x3f, 0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03};
+static unsigned char helvetica24_115_bits[] = {
+0x7c, 0x00, 0xfe, 0x01, 0x87, 0x03, 0x03, 0x03, 0x03, 0x00, 0x1f, 0x00, 
+0xfe, 0x00, 0xf0, 0x01, 0x80, 0x03, 0x00, 0x03, 0x03, 0x03, 0x83, 0x03, 
+0xff, 0x01, 0xfc, 0x00};
+static unsigned char helvetica24_116_bits[] = {
+0x0c, 0x0c, 0x0c, 0x0c, 0x3f, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x38};
+static unsigned char helvetica24_117_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc6, 0x03, 
+0x7e, 0x03, 0x38, 0x03};
+static unsigned char helvetica24_118_bits[] = {
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0x06, 0x03, 0x8e, 0x03, 
+0x8c, 0x01, 0x8c, 0x01, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0x70, 0x00, 
+0x70, 0x00, 0x70, 0x00};
+static unsigned char helvetica24_119_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x86, 0x87, 0x01, 0x86, 0x87, 0x01, 
+0x86, 0x87, 0x01, 0x8c, 0xc4, 0x00, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0x00, 
+0xd8, 0x6c, 0x00, 0x58, 0x68, 0x00, 0x78, 0x78, 0x00, 0x70, 0x38, 0x00, 
+0x30, 0x30, 0x00, 0x30, 0x30, 0x00};
+static unsigned char helvetica24_120_bits[] = {
+0x03, 0x03, 0x87, 0x03, 0x86, 0x01, 0xcc, 0x00, 0xfc, 0x00, 0x78, 0x00, 
+0x30, 0x00, 0x78, 0x00, 0x78, 0x00, 0xcc, 0x00, 0xce, 0x01, 0x86, 0x01, 
+0x87, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_121_bits[] = {
+0x03, 0x0c, 0x03, 0x0c, 0x06, 0x0c, 0x0e, 0x06, 0x0c, 0x06, 0x1c, 0x07, 
+0x18, 0x03, 0x18, 0x03, 0xb0, 0x01, 0xb0, 0x01, 0xe0, 0x01, 0xe0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x30, 0x00, 0x3c, 0x00, 
+0x1c, 0x00};
+static unsigned char helvetica24_122_bits[] = {
+0xff, 0x03, 0xff, 0x03, 0x80, 0x01, 0xc0, 0x00, 0xe0, 0x00, 0x70, 0x00, 
+0x30, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x07, 0x00, 
+0xff, 0x03, 0xff, 0x03};
+static unsigned char helvetica24_123_bits[] = {
+0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x03, 
+0x03, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30};
+static unsigned char helvetica24_124_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_125_bits[] = {
+0x03, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 
+0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x03};
+static unsigned char helvetica24_126_bits[] = {
+0x0e, 0x03, 0x3f, 0x03, 0xf3, 0x03, 0xc3, 0x01};
+static unsigned char helvetica24_127_bits[] = {
+0x00};
+static unsigned char helvetica24_128_bits[] = {
+0x00};
+static unsigned char helvetica24_129_bits[] = {
+0x00};
+static unsigned char helvetica24_130_bits[] = {
+0x00};
+static unsigned char helvetica24_131_bits[] = {
+0x00};
+static unsigned char helvetica24_132_bits[] = {
+0x00};
+static unsigned char helvetica24_133_bits[] = {
+0x00};
+static unsigned char helvetica24_134_bits[] = {
+0x00};
+static unsigned char helvetica24_135_bits[] = {
+0x00};
+static unsigned char helvetica24_136_bits[] = {
+0x00};
+static unsigned char helvetica24_137_bits[] = {
+0x00};
+static unsigned char helvetica24_138_bits[] = {
+0x00};
+static unsigned char helvetica24_139_bits[] = {
+0x00};
+static unsigned char helvetica24_140_bits[] = {
+0x00};
+static unsigned char helvetica24_141_bits[] = {
+0x00};
+static unsigned char helvetica24_142_bits[] = {
+0x00};
+static unsigned char helvetica24_143_bits[] = {
+0x00};
+static unsigned char helvetica24_144_bits[] = {
+0x00};
+static unsigned char helvetica24_145_bits[] = {
+0x00};
+static unsigned char helvetica24_146_bits[] = {
+0x00};
+static unsigned char helvetica24_147_bits[] = {
+0x00};
+static unsigned char helvetica24_148_bits[] = {
+0x00};
+static unsigned char helvetica24_149_bits[] = {
+0x00};
+static unsigned char helvetica24_150_bits[] = {
+0x00};
+static unsigned char helvetica24_151_bits[] = {
+0x00};
+static unsigned char helvetica24_152_bits[] = {
+0x00};
+static unsigned char helvetica24_153_bits[] = {
+0x00};
+static unsigned char helvetica24_154_bits[] = {
+0x00};
+static unsigned char helvetica24_155_bits[] = {
+0x00};
+static unsigned char helvetica24_156_bits[] = {
+0x00};
+static unsigned char helvetica24_157_bits[] = {
+0x00};
+static unsigned char helvetica24_158_bits[] = {
+0x00};
+static unsigned char helvetica24_159_bits[] = {
+0x00};
+static unsigned char helvetica24_160_bits[] = {
+0x00};
+static unsigned char helvetica24_161_bits[] = {
+0x03, 0x03, 0x03, 0x00, 0x00, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_162_bits[] = {
+0x80, 0x01, 0x80, 0x01, 0xf8, 0x00, 0xfc, 0x01, 0xce, 0x03, 0x66, 0x03, 
+0x63, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x1b, 0x00, 0x1b, 0x00, 
+0x1b, 0x03, 0x8e, 0x03, 0xfe, 0x01, 0xfc, 0x00, 0x06, 0x00, 0x06, 0x00};
+static unsigned char helvetica24_163_bits[] = {
+0xf8, 0x01, 0xfc, 0x07, 0x0e, 0x0e, 0x06, 0x0c, 0x06, 0x00, 0x0e, 0x00, 
+0x0c, 0x00, 0x18, 0x00, 0xff, 0x01, 0xff, 0x01, 0x18, 0x00, 0x18, 0x00, 
+0x18, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0xe6, 0x0c, 0xff, 0x0f, 0x0f, 0x07};
+static unsigned char helvetica24_164_bits[] = {
+0x03, 0x06, 0x77, 0x07, 0xfe, 0x03, 0x8c, 0x01, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x8c, 0x01, 0xfe, 0x03, 0x77, 0x07, 0x03, 0x06};
+static unsigned char helvetica24_165_bits[] = {
+0x07, 0x38, 0x06, 0x18, 0x0e, 0x1c, 0x0c, 0x0c, 0x1c, 0x0e, 0x18, 0x06, 
+0x38, 0x07, 0x30, 0x03, 0xfc, 0x0f, 0xfc, 0x0f, 0xc0, 0x00, 0xfc, 0x0f, 
+0xfc, 0x0f, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00};
+static unsigned char helvetica24_166_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_167_bits[] = {
+0xf8, 0x00, 0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x0e, 0x03, 0x1c, 0x00, 
+0x38, 0x00, 0x7e, 0x00, 0xe7, 0x00, 0xc3, 0x01, 0x83, 0x03, 0x03, 0x03, 
+0x06, 0x06, 0x0e, 0x06, 0x1c, 0x06, 0x38, 0x03, 0xf0, 0x01, 0xe0, 0x00, 
+0xc0, 0x01, 0x86, 0x03, 0x06, 0x03, 0x8e, 0x03, 0xfc, 0x01, 0xf8, 0x00};
+static unsigned char helvetica24_168_bits[] = {
+0x33, 0x33};
+static unsigned char helvetica24_169_bits[] = {
+0xe0, 0x0f, 0x00, 0x38, 0x38, 0x00, 0x0c, 0x60, 0x00, 0x06, 0xc0, 0x00, 
+0xc2, 0x87, 0x00, 0x63, 0x8c, 0x01, 0x31, 0x08, 0x01, 0x11, 0x00, 0x01, 
+0x11, 0x00, 0x01, 0x11, 0x00, 0x01, 0x31, 0x08, 0x01, 0x63, 0x8c, 0x01, 
+0xc2, 0x87, 0x00, 0x06, 0xc0, 0x00, 0x0c, 0x60, 0x00, 0x38, 0x38, 0x00, 
+0xe0, 0x0f, 0x00};
+static unsigned char helvetica24_170_bits[] = {
+0x1e, 0x33, 0x33, 0x30, 0x3e, 0x33, 0x33, 0x3b, 0x6e, 0x00, 0x7f, 0x7f};
+static unsigned char helvetica24_171_bits[] = {
+0x98, 0x01, 0xcc, 0x00, 0x66, 0x00, 0x33, 0x00, 0x33, 0x00, 0x66, 0x00, 
+0xcc, 0x00, 0x98, 0x01};
+static unsigned char helvetica24_172_bits[] = {
+0xff, 0x1f, 0xff, 0x1f, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 
+0x00, 0x18, 0x00, 0x18};
+static unsigned char helvetica24_173_bits[] = {
+0x3f, 0x3f};
+static unsigned char helvetica24_174_bits[] = {
+0xe0, 0x0f, 0x00, 0x38, 0x38, 0x00, 0x0c, 0x60, 0x00, 0x06, 0xc0, 0x00, 
+0xc2, 0x87, 0x00, 0x43, 0x88, 0x01, 0x41, 0x08, 0x01, 0x41, 0x08, 0x01, 
+0xc1, 0x07, 0x01, 0x41, 0x02, 0x01, 0x41, 0x04, 0x01, 0x43, 0x84, 0x01, 
+0x42, 0x88, 0x00, 0x06, 0xc0, 0x00, 0x0c, 0x60, 0x00, 0x38, 0x38, 0x00, 
+0xe0, 0x0f, 0x00};
+static unsigned char helvetica24_175_bits[] = {
+0x3f, 0x3f};
+static unsigned char helvetica24_176_bits[] = {
+0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0x66, 0x3c};
+static unsigned char helvetica24_177_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xff, 0x0f, 0xff, 0x0f, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00, 0xff, 0x0f, 
+0xff, 0x0f};
+static unsigned char helvetica24_178_bits[] = {
+0x3c, 0x7e, 0x63, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x7f, 0x7f};
+static unsigned char helvetica24_179_bits[] = {
+0x3e, 0x7f, 0x63, 0x60, 0x3c, 0x3c, 0x60, 0x63, 0x7f, 0x3e};
+static unsigned char helvetica24_180_bits[] = {
+0x18, 0x0c, 0x06, 0x03};
+static unsigned char helvetica24_181_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc7, 0x03, 
+0x7f, 0x03, 0x3b, 0x03, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00};
+static unsigned char helvetica24_182_bits[] = {
+0xf8, 0x03, 0xfe, 0x03, 0x9e, 0x01, 0x9f, 0x01, 0x9f, 0x01, 0x9f, 0x01, 
+0x9f, 0x01, 0x9f, 0x01, 0x9f, 0x01, 0x9e, 0x01, 0x9e, 0x01, 0x9c, 0x01, 
+0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 
+0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01};
+static unsigned char helvetica24_183_bits[] = {
+0x03, 0x03, 0x03};
+static unsigned char helvetica24_184_bits[] = {
+0x06, 0x0e, 0x18, 0x18, 0x1f, 0x0e};
+static unsigned char helvetica24_185_bits[] = {
+0x0c, 0x0c, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c};
+static unsigned char helvetica24_186_bits[] = {
+0x1c, 0x36, 0x63, 0x63, 0x63, 0x63, 0x63, 0x36, 0x1c, 0x00, 0x7f, 0x7f};
+static unsigned char helvetica24_187_bits[] = {
+0x33, 0x00, 0x66, 0x00, 0xcc, 0x00, 0x98, 0x01, 0x98, 0x01, 0xcc, 0x00, 
+0x66, 0x00, 0x33, 0x00};
+static unsigned char helvetica24_188_bits[] = {
+0x0c, 0x18, 0x00, 0x0c, 0x18, 0x00, 0x0f, 0x0c, 0x00, 0x0f, 0x0c, 0x00, 
+0x0c, 0x06, 0x00, 0x0c, 0x06, 0x00, 0x0c, 0x03, 0x00, 0x0c, 0x03, 0x00, 
+0x8c, 0xc1, 0x00, 0x8c, 0xe1, 0x00, 0xc0, 0xf0, 0x00, 0xc0, 0xf0, 0x00, 
+0x60, 0xd8, 0x00, 0x60, 0xcc, 0x00, 0x30, 0xfe, 0x03, 0x30, 0xfe, 0x03, 
+0x18, 0xc0, 0x00, 0x18, 0xc0, 0x00};
+static unsigned char helvetica24_189_bits[] = {
+0x0c, 0x18, 0x00, 0x0c, 0x18, 0x00, 0x0f, 0x0c, 0x00, 0x0f, 0x0c, 0x00, 
+0x0c, 0x06, 0x00, 0x0c, 0x06, 0x00, 0x0c, 0x03, 0x00, 0x0c, 0x03, 0x00, 
+0x8c, 0xe1, 0x01, 0x8c, 0xf1, 0x03, 0xc0, 0x18, 0x03, 0xc0, 0x00, 0x03, 
+0x60, 0x80, 0x01, 0x60, 0xc0, 0x00, 0x30, 0x60, 0x00, 0x30, 0x30, 0x00, 
+0x18, 0xf8, 0x03, 0x18, 0xf8, 0x03};
+static unsigned char helvetica24_190_bits[] = {
+0x3e, 0x30, 0x00, 0x7f, 0x30, 0x00, 0x63, 0x18, 0x00, 0x60, 0x18, 0x00, 
+0x3c, 0x0c, 0x00, 0x3c, 0x0c, 0x00, 0x60, 0x06, 0x00, 0x63, 0x06, 0x00, 
+0x7f, 0x83, 0x01, 0x3e, 0xc3, 0x01, 0x80, 0xe1, 0x01, 0x80, 0xe1, 0x01, 
+0xc0, 0xb0, 0x01, 0xc0, 0x98, 0x01, 0x60, 0xfc, 0x07, 0x60, 0xfc, 0x07, 
+0x30, 0x80, 0x01, 0x30, 0x80, 0x01};
+static unsigned char helvetica24_191_bits[] = {
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x0e, 0x00, 
+0x06, 0x00, 0x07, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc7, 0x01, 0xfe, 0x01, 
+0x7c, 0x00};
+static unsigned char helvetica24_192_bits[] = {
+0x30, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x80, 0x01, 0x00, 0x00, 0xc0, 0x01, 
+0xc0, 0x01, 0x60, 0x03, 0x60, 0x03, 0x30, 0x02, 0x30, 0x06, 0x30, 0x06, 
+0x18, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x0c, 0x18, 0xfc, 0x1f, 0xfc, 0x1f, 
+0x06, 0x30, 0x06, 0x30, 0x06, 0x30, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60};
+static unsigned char helvetica24_193_bits[] = {
+0x00, 0x06, 0x00, 0x03, 0x80, 0x01, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x01, 
+0xc0, 0x01, 0x60, 0x03, 0x60, 0x03, 0x30, 0x02, 0x30, 0x06, 0x30, 0x06, 
+0x18, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x0c, 0x18, 0xfc, 0x1f, 0xfc, 0x1f, 
+0x06, 0x30, 0x06, 0x30, 0x06, 0x30, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60};
+static unsigned char helvetica24_194_bits[] = {
+0x80, 0x01, 0xc0, 0x03, 0x60, 0x06, 0x30, 0x0c, 0x00, 0x00, 0xc0, 0x01, 
+0xc0, 0x01, 0x60, 0x03, 0x60, 0x03, 0x30, 0x02, 0x30, 0x06, 0x30, 0x06, 
+0x18, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x0c, 0x18, 0xfc, 0x1f, 0xfc, 0x1f, 
+0x06, 0x30, 0x06, 0x30, 0x06, 0x30, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60};
+static unsigned char helvetica24_195_bits[] = {
+0xe0, 0x08, 0xb0, 0x0d, 0x10, 0x07, 0x00, 0x00, 0xc0, 0x01, 0xc0, 0x01, 
+0x60, 0x03, 0x60, 0x03, 0x30, 0x02, 0x30, 0x06, 0x30, 0x06, 0x18, 0x0c, 
+0x18, 0x0c, 0x18, 0x0c, 0x0c, 0x18, 0xfc, 0x1f, 0xfc, 0x1f, 0x06, 0x30, 
+0x06, 0x30, 0x06, 0x30, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60};
+static unsigned char helvetica24_196_bits[] = {
+0x30, 0x06, 0x30, 0x06, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0xc0, 0x01, 
+0x60, 0x03, 0x60, 0x03, 0x30, 0x02, 0x30, 0x06, 0x30, 0x06, 0x18, 0x0c, 
+0x18, 0x0c, 0x18, 0x0c, 0x0c, 0x18, 0xfc, 0x1f, 0xfc, 0x1f, 0x06, 0x30, 
+0x06, 0x30, 0x06, 0x30, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60};
+static unsigned char helvetica24_197_bits[] = {
+0xc0, 0x01, 0x60, 0x03, 0x60, 0x03, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 
+0xc0, 0x01, 0x60, 0x03, 0x60, 0x03, 0x30, 0x02, 0x30, 0x06, 0x30, 0x06, 
+0x18, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x0c, 0x18, 0xfc, 0x1f, 0xfc, 0x1f, 
+0x06, 0x30, 0x06, 0x30, 0x06, 0x30, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60};
+static unsigned char helvetica24_198_bits[] = {
+0xc0, 0xff, 0x1f, 0xc0, 0xff, 0x1f, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 
+0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x18, 0x06, 0x00, 
+0x18, 0xfe, 0x1f, 0x18, 0xfe, 0x1f, 0x0c, 0x06, 0x00, 0xfc, 0x07, 0x00, 
+0xfc, 0x07, 0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00, 
+0x03, 0x06, 0x00, 0x03, 0xfe, 0x1f, 0x03, 0xfe, 0x1f};
+static unsigned char helvetica24_199_bits[] = {
+0xe0, 0x07, 0xf8, 0x1f, 0x3c, 0x3c, 0x0e, 0x70, 0x06, 0x60, 0x07, 0x60, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0xc0, 0x07, 0xc0, 0x06, 0x60, 0x0e, 0x70, 0x3c, 0x3c, 0xf8, 0x1f, 
+0xe0, 0x07, 0xc0, 0x01, 0x00, 0x03, 0x00, 0x03, 0xe0, 0x03, 0xc0, 0x01};
+static unsigned char helvetica24_200_bits[] = {
+0x0c, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0x00, 0x00, 0xff, 0x0f, 
+0xff, 0x0f, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0xff, 0x07, 0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x0f, 0xff, 0x0f};
+static unsigned char helvetica24_201_bits[] = {
+0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0x0f, 
+0xff, 0x0f, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0xff, 0x07, 0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x0f, 0xff, 0x0f};
+static unsigned char helvetica24_202_bits[] = {
+0x60, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x0c, 0x03, 0x00, 0x00, 0xff, 0x0f, 
+0xff, 0x0f, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0xff, 0x07, 0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x0f, 0xff, 0x0f};
+static unsigned char helvetica24_203_bits[] = {
+0x98, 0x01, 0x98, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0x0f, 0xff, 0x0f, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0xff, 0x07, 0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x0f, 0xff, 0x0f};
+static unsigned char helvetica24_204_bits[] = {
+0x03, 0x06, 0x0c, 0x18, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c};
+static unsigned char helvetica24_205_bits[] = {
+0x18, 0x0c, 0x06, 0x03, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06};
+static unsigned char helvetica24_206_bits[] = {
+0x18, 0x3c, 0x66, 0xc3, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18};
+static unsigned char helvetica24_207_bits[] = {
+0x33, 0x33, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c};
+static unsigned char helvetica24_208_bits[] = {
+0xfc, 0x0f, 0x00, 0xfc, 0x3f, 0x00, 0x0c, 0x78, 0x00, 0x0c, 0xe0, 0x00, 
+0x0c, 0xc0, 0x00, 0x0c, 0xc0, 0x01, 0x0c, 0x80, 0x01, 0x0c, 0x80, 0x01, 
+0xff, 0x80, 0x01, 0xff, 0x80, 0x01, 0x0c, 0x80, 0x01, 0x0c, 0x80, 0x01, 
+0x0c, 0x80, 0x01, 0x0c, 0xc0, 0x01, 0x0c, 0xc0, 0x00, 0x0c, 0xe0, 0x00, 
+0x0c, 0x78, 0x00, 0xfc, 0x3f, 0x00, 0xfc, 0x0f, 0x00};
+static unsigned char helvetica24_209_bits[] = {
+0xe0, 0x08, 0xb0, 0x0d, 0x10, 0x07, 0x00, 0x00, 0x07, 0x30, 0x0f, 0x30, 
+0x0f, 0x30, 0x1b, 0x30, 0x3b, 0x30, 0x33, 0x30, 0x73, 0x30, 0x63, 0x30, 
+0xe3, 0x30, 0xc3, 0x30, 0xc3, 0x31, 0x83, 0x31, 0x83, 0x33, 0x03, 0x33, 
+0x03, 0x37, 0x03, 0x36, 0x03, 0x3c, 0x03, 0x3c, 0x03, 0x38};
+static unsigned char helvetica24_210_bits[] = {
+0x60, 0x00, 0xc0, 0x00, 0x80, 0x01, 0x00, 0x03, 0x00, 0x00, 0xe0, 0x07, 
+0xf8, 0x1f, 0x3c, 0x3c, 0x0e, 0x70, 0x06, 0x60, 0x07, 0xe0, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x07, 0xe0, 0x06, 0x60, 0x0e, 0x70, 0x3c, 0x3c, 0xf8, 0x1f, 0xe0, 0x07};
+static unsigned char helvetica24_211_bits[] = {
+0x00, 0x0c, 0x00, 0x06, 0x00, 0x03, 0x80, 0x01, 0x00, 0x00, 0xe0, 0x07, 
+0xf8, 0x1f, 0x3c, 0x3c, 0x0e, 0x70, 0x06, 0x60, 0x07, 0xe0, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x07, 0xe0, 0x06, 0x60, 0x0e, 0x70, 0x3c, 0x3c, 0xf8, 0x1f, 0xe0, 0x07};
+static unsigned char helvetica24_212_bits[] = {
+0x80, 0x01, 0xc0, 0x03, 0x60, 0x06, 0x30, 0x0c, 0x00, 0x00, 0xe0, 0x07, 
+0xf8, 0x1f, 0x3c, 0x3c, 0x0e, 0x70, 0x06, 0x60, 0x07, 0xe0, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x07, 0xe0, 0x06, 0x60, 0x0e, 0x70, 0x3c, 0x3c, 0xf8, 0x1f, 0xe0, 0x07};
+static unsigned char helvetica24_213_bits[] = {
+0xe0, 0x08, 0xb0, 0x0d, 0x10, 0x07, 0x00, 0x00, 0xe0, 0x07, 0xf8, 0x1f, 
+0x3c, 0x3c, 0x0e, 0x70, 0x06, 0x60, 0x07, 0xe0, 0x03, 0xc0, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x07, 0xe0, 
+0x06, 0x60, 0x0e, 0x70, 0x3c, 0x3c, 0xf8, 0x1f, 0xe0, 0x07};
+static unsigned char helvetica24_214_bits[] = {
+0x60, 0x06, 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf8, 0x1f, 
+0x3c, 0x3c, 0x0e, 0x70, 0x06, 0x60, 0x07, 0xe0, 0x03, 0xc0, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x07, 0xe0, 
+0x06, 0x60, 0x0e, 0x70, 0x3c, 0x3c, 0xf8, 0x1f, 0xe0, 0x07};
+static unsigned char helvetica24_215_bits[] = {
+0x03, 0x18, 0x06, 0x0c, 0x0c, 0x06, 0x18, 0x03, 0xb0, 0x01, 0xe0, 0x00, 
+0xe0, 0x00, 0xb0, 0x01, 0x18, 0x03, 0x0c, 0x06, 0x06, 0x0c, 0x03, 0x18};
+static unsigned char helvetica24_216_bits[] = {
+0xc0, 0x0f, 0x02, 0xf0, 0x3f, 0x03, 0x78, 0xf8, 0x01, 0x1c, 0xe0, 0x00, 
+0x0c, 0xe0, 0x00, 0x0e, 0xb0, 0x01, 0x06, 0x98, 0x01, 0x06, 0x8c, 0x01, 
+0x06, 0x86, 0x01, 0x06, 0x83, 0x01, 0x86, 0x81, 0x01, 0xc6, 0x80, 0x01, 
+0x66, 0x80, 0x01, 0x36, 0xc0, 0x01, 0x1c, 0xc0, 0x00, 0x1c, 0xe0, 0x00, 
+0x7e, 0x78, 0x00, 0xf3, 0x3f, 0x00, 0xc1, 0x0f, 0x00};
+static unsigned char helvetica24_217_bits[] = {
+0x30, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x80, 0x01, 0x00, 0x00, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x06, 0x18, 0x0e, 0x1c, 0xfc, 0x0f, 0xf0, 0x03};
+static unsigned char helvetica24_218_bits[] = {
+0x00, 0x06, 0x00, 0x03, 0x80, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x06, 0x18, 0x0e, 0x1c, 0xfc, 0x0f, 0xf0, 0x03};
+static unsigned char helvetica24_219_bits[] = {
+0xc0, 0x00, 0xe0, 0x01, 0x30, 0x03, 0x18, 0x06, 0x00, 0x00, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x06, 0x18, 0x0e, 0x1c, 0xfc, 0x0f, 0xf0, 0x03};
+static unsigned char helvetica24_220_bits[] = {
+0x30, 0x03, 0x30, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 
+0x03, 0x30, 0x06, 0x18, 0x0e, 0x1c, 0xfc, 0x0f, 0xf0, 0x03};
+static unsigned char helvetica24_221_bits[] = {
+0x00, 0x06, 0x00, 0x03, 0x80, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x03, 0x30, 
+0x07, 0x38, 0x06, 0x18, 0x0e, 0x1c, 0x0c, 0x0c, 0x1c, 0x0e, 0x18, 0x06, 
+0x38, 0x07, 0x30, 0x03, 0xf0, 0x03, 0xe0, 0x01, 0xe0, 0x01, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00};
+static unsigned char helvetica24_222_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x07, 0xff, 0x0f, 
+0x03, 0x0c, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x0c, 
+0xff, 0x0f, 0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00};
+static unsigned char helvetica24_223_bits[] = {
+0x38, 0x00, 0xfe, 0x00, 0xc7, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 
+0xc3, 0x00, 0xe3, 0x00, 0x73, 0x00, 0xf3, 0x00, 0xc3, 0x01, 0x83, 0x01, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x01, 0xc3, 0x01, 0xf3, 0x00, 
+0x73, 0x00};
+static unsigned char helvetica24_224_bits[] = {
+0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xf8, 0x00, 
+0xfc, 0x01, 0x86, 0x03, 0x06, 0x03, 0x00, 0x03, 0xe0, 0x03, 0xfc, 0x03, 
+0x1e, 0x03, 0x07, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc7, 0x03, 0x7e, 0x07, 
+0x3c, 0x06};
+static unsigned char helvetica24_225_bits[] = {
+0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x00, 
+0xfc, 0x01, 0x86, 0x03, 0x06, 0x03, 0x00, 0x03, 0xe0, 0x03, 0xfc, 0x03, 
+0x1e, 0x03, 0x07, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc7, 0x03, 0x7e, 0x07, 
+0x3c, 0x06};
+static unsigned char helvetica24_226_bits[] = {
+0x60, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x0c, 0x03, 0x00, 0x00, 0xf8, 0x00, 
+0xfc, 0x01, 0x86, 0x03, 0x06, 0x03, 0x00, 0x03, 0xe0, 0x03, 0xfc, 0x03, 
+0x1e, 0x03, 0x07, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc7, 0x03, 0x7e, 0x07, 
+0x3c, 0x06};
+static unsigned char helvetica24_227_bits[] = {
+0x38, 0x02, 0x6c, 0x03, 0xc4, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 
+0xfc, 0x01, 0x86, 0x03, 0x06, 0x03, 0x00, 0x03, 0xe0, 0x03, 0xfc, 0x03, 
+0x1e, 0x03, 0x07, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc7, 0x03, 0x7e, 0x07, 
+0x3c, 0x06};
+static unsigned char helvetica24_228_bits[] = {
+0x98, 0x01, 0x98, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0xfc, 0x01, 
+0x86, 0x03, 0x06, 0x03, 0x00, 0x03, 0xe0, 0x03, 0xfc, 0x03, 0x1e, 0x03, 
+0x07, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc7, 0x03, 0x7e, 0x07, 0x3c, 0x06};
+static unsigned char helvetica24_229_bits[] = {
+0x70, 0x00, 0xd8, 0x00, 0x88, 0x00, 0xd8, 0x00, 0x70, 0x00, 0xf8, 0x00, 
+0xfc, 0x01, 0x86, 0x03, 0x06, 0x03, 0x00, 0x03, 0xe0, 0x03, 0xfc, 0x03, 
+0x1e, 0x03, 0x07, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc7, 0x03, 0x7e, 0x07, 
+0x3c, 0x06};
+static unsigned char helvetica24_230_bits[] = {
+0xf8, 0x70, 0x00, 0xfc, 0xfd, 0x01, 0x86, 0x8f, 0x03, 0x06, 0x07, 0x03, 
+0x00, 0x03, 0x06, 0xe0, 0x03, 0x06, 0xfc, 0xff, 0x07, 0x1e, 0xff, 0x07, 
+0x07, 0x03, 0x00, 0x03, 0x03, 0x00, 0x83, 0x07, 0x06, 0xc7, 0x0f, 0x07, 
+0x7e, 0xfc, 0x03, 0x3c, 0xf0, 0x00};
+static unsigned char helvetica24_231_bits[] = {
+0xf8, 0x00, 0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x06, 0x03, 0x8e, 0x03, 
+0xfc, 0x01, 0xf8, 0x00, 0x38, 0x00, 0x60, 0x00, 0x60, 0x00, 0x7c, 0x00, 
+0x38, 0x00};
+static unsigned char helvetica24_232_bits[] = {
+0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x70, 0x00, 
+0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 0xff, 0x07, 
+0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 0x06, 0x06, 0x0e, 0x07, 0xfc, 0x03, 
+0xf0, 0x00};
+static unsigned char helvetica24_233_bits[] = {
+0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 
+0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 0xff, 0x07, 
+0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 0x06, 0x06, 0x0e, 0x07, 0xfc, 0x03, 
+0xf0, 0x00};
+static unsigned char helvetica24_234_bits[] = {
+0x60, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x0c, 0x03, 0x00, 0x00, 0x70, 0x00, 
+0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 0xff, 0x07, 
+0xff, 0x07, 0x03, 0x00, 0x03, 0x00, 0x06, 0x06, 0x0e, 0x07, 0xfc, 0x03, 
+0xf0, 0x00};
+static unsigned char helvetica24_235_bits[] = {
+0x98, 0x01, 0x98, 0x01, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xfc, 0x01, 
+0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 0xff, 0x07, 0xff, 0x07, 
+0x03, 0x00, 0x03, 0x00, 0x06, 0x06, 0x0e, 0x07, 0xfc, 0x03, 0xf0, 0x00};
+static unsigned char helvetica24_236_bits[] = {
+0x03, 0x06, 0x0c, 0x18, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c};
+static unsigned char helvetica24_237_bits[] = {
+0x18, 0x0c, 0x06, 0x03, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06};
+static unsigned char helvetica24_238_bits[] = {
+0x18, 0x3c, 0x66, 0xc3, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18};
+static unsigned char helvetica24_239_bits[] = {
+0x33, 0x33, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c};
+static unsigned char helvetica24_240_bits[] = {
+0x07, 0x00, 0x8e, 0x01, 0x78, 0x00, 0x3c, 0x00, 0x63, 0x00, 0xf8, 0x00, 
+0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0x8e, 0x03, 0xfc, 0x01, 
+0xf8, 0x00};
+static unsigned char helvetica24_241_bits[] = {
+0x1c, 0x01, 0xb6, 0x01, 0xe2, 0x00, 0x00, 0x00, 0x73, 0x00, 0xfb, 0x01, 
+0x8f, 0x01, 0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char helvetica24_242_bits[] = {
+0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xf8, 0x00, 
+0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0x8e, 0x03, 0xfc, 0x01, 
+0xf8, 0x00};
+static unsigned char helvetica24_243_bits[] = {
+0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x00, 
+0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0x8e, 0x03, 0xfc, 0x01, 
+0xf8, 0x00};
+static unsigned char helvetica24_244_bits[] = {
+0x60, 0x00, 0xf0, 0x00, 0x98, 0x01, 0x0c, 0x03, 0x00, 0x00, 0xf8, 0x00, 
+0xfc, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0x8e, 0x03, 0xfc, 0x01, 
+0xf8, 0x00};
+static unsigned char helvetica24_245_bits[] = {
+0x38, 0x02, 0x6c, 0x03, 0xc4, 0x01, 0x00, 0x00, 0xf8, 0x00, 0xfc, 0x01, 
+0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0x8e, 0x03, 0xfc, 0x01, 0xf8, 0x00};
+static unsigned char helvetica24_246_bits[] = {
+0x8c, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0xfc, 0x01, 
+0x8e, 0x03, 0x06, 0x03, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x06, 0x03, 0x8e, 0x03, 0xfc, 0x01, 0xf8, 0x00};
+static unsigned char helvetica24_247_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x0f, 
+0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char helvetica24_248_bits[] = {
+0xf0, 0x19, 0xf8, 0x0f, 0x1c, 0x07, 0x0c, 0x06, 0x06, 0x0f, 0x86, 0x0d, 
+0xc6, 0x0c, 0x66, 0x0c, 0x66, 0x0c, 0x36, 0x0c, 0x1c, 0x06, 0x0c, 0x07, 
+0xfe, 0x03, 0xf3, 0x01};
+static unsigned char helvetica24_249_bits[] = {
+0x0c, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc6, 0x03, 0x7e, 0x03, 
+0x38, 0x03};
+static unsigned char helvetica24_250_bits[] = {
+0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc6, 0x03, 0x7e, 0x03, 
+0x38, 0x03};
+static unsigned char helvetica24_251_bits[] = {
+0x30, 0x00, 0x78, 0x00, 0xcc, 0x00, 0x86, 0x01, 0x00, 0x00, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc6, 0x03, 0x7e, 0x03, 
+0x38, 0x03};
+static unsigned char helvetica24_252_bits[] = {
+0xcc, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc6, 0x03, 0x7e, 0x03, 0x38, 0x03};
+static unsigned char helvetica24_253_bits[] = {
+0x00, 0x03, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x0c, 
+0x03, 0x0c, 0x06, 0x0c, 0x0e, 0x06, 0x0c, 0x06, 0x1c, 0x07, 0x18, 0x03, 
+0x18, 0x03, 0xb0, 0x01, 0xb0, 0x01, 0xe0, 0x01, 0xe0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x30, 0x00, 0x3c, 0x00, 0x1c, 0x00};
+static unsigned char helvetica24_254_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xf3, 0x00, 
+0xfb, 0x01, 0x8f, 0x03, 0x07, 0x03, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x07, 0x03, 0x8f, 0x03, 0xfb, 0x01, 
+0xf3, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00};
+static unsigned char helvetica24_255_bits[] = {
+0x98, 0x01, 0x98, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x03, 0x0c, 
+0x06, 0x0c, 0x0e, 0x06, 0x0c, 0x06, 0x1c, 0x07, 0x18, 0x03, 0x18, 0x03, 
+0xb0, 0x01, 0xb0, 0x01, 0xe0, 0x01, 0xe0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x30, 0x00, 0x3c, 0x00, 0x1c, 0x00};
+static RotFont helvetica24font[] = {
+{5, 1, 1, helvetica24_0_bits},
+{5, 1, 1, helvetica24_1_bits},
+{5, 1, 1, helvetica24_2_bits},
+{5, 1, 1, helvetica24_3_bits},
+{5, 1, 1, helvetica24_4_bits},
+{5, 1, 1, helvetica24_5_bits},
+{5, 1, 1, helvetica24_6_bits},
+{5, 1, 1, helvetica24_7_bits},
+{5, 1, 1, helvetica24_8_bits},
+{5, 1, 1, helvetica24_9_bits},
+{5, 1, 1, helvetica24_10_bits},
+{5, 1, 1, helvetica24_11_bits},
+{5, 1, 1, helvetica24_12_bits},
+{5, 1, 1, helvetica24_13_bits},
+{5, 1, 1, helvetica24_14_bits},
+{5, 1, 1, helvetica24_15_bits},
+{5, 1, 1, helvetica24_16_bits},
+{5, 1, 1, helvetica24_17_bits},
+{5, 1, 1, helvetica24_18_bits},
+{5, 1, 1, helvetica24_19_bits},
+{5, 1, 1, helvetica24_20_bits},
+{5, 1, 1, helvetica24_21_bits},
+{5, 1, 1, helvetica24_22_bits},
+{5, 1, 1, helvetica24_23_bits},
+{5, 1, 1, helvetica24_24_bits},
+{5, 1, 1, helvetica24_25_bits},
+{5, 1, 1, helvetica24_26_bits},
+{5, 1, 1, helvetica24_27_bits},
+{5, 1, 1, helvetica24_28_bits},
+{5, 1, 1, helvetica24_29_bits},
+{5, 1, 1, helvetica24_30_bits},
+{5, 1, 1, helvetica24_31_bits},
+{10, 1, 1, helvetica24_32_bits},
+{2, 19, 19, helvetica24_33_bits},
+{6, 6, 19, helvetica24_34_bits},
+{11, 17, 17, helvetica24_35_bits},
+{11, 21, 19, helvetica24_36_bits},
+{19, 18, 18, helvetica24_37_bits},
+{14, 18, 18, helvetica24_38_bits},
+{2, 6, 19, helvetica24_39_bits},
+{5, 24, 19, helvetica24_40_bits},
+{5, 24, 19, helvetica24_41_bits},
+{7, 7, 19, helvetica24_42_bits},
+{12, 12, 13, helvetica24_43_bits},
+{2, 6, 3, helvetica24_44_bits},
+{11, 2, 8, helvetica24_45_bits},
+{2, 3, 3, helvetica24_46_bits},
+{7, 19, 19, helvetica24_47_bits},
+{11, 18, 18, helvetica24_48_bits},
+{6, 18, 18, helvetica24_49_bits},
+{11, 18, 18, helvetica24_50_bits},
+{11, 18, 18, helvetica24_51_bits},
+{11, 18, 18, helvetica24_52_bits},
+{11, 18, 18, helvetica24_53_bits},
+{11, 18, 18, helvetica24_54_bits},
+{11, 18, 18, helvetica24_55_bits},
+{11, 18, 18, helvetica24_56_bits},
+{11, 18, 18, helvetica24_57_bits},
+{2, 14, 14, helvetica24_58_bits},
+{2, 17, 14, helvetica24_59_bits},
+{12, 12, 13, helvetica24_60_bits},
+{10, 6, 11, helvetica24_61_bits},
+{12, 12, 13, helvetica24_62_bits},
+{10, 19, 19, helvetica24_63_bits},
+{22, 23, 19, helvetica24_64_bits},
+{15, 19, 19, helvetica24_65_bits},
+{14, 19, 19, helvetica24_66_bits},
+{16, 19, 19, helvetica24_67_bits},
+{15, 19, 19, helvetica24_68_bits},
+{12, 19, 19, helvetica24_69_bits},
+{11, 19, 19, helvetica24_70_bits},
+{16, 19, 19, helvetica24_71_bits},
+{14, 19, 19, helvetica24_72_bits},
+{2, 19, 19, helvetica24_73_bits},
+{10, 19, 19, helvetica24_74_bits},
+{15, 19, 19, helvetica24_75_bits},
+{11, 19, 19, helvetica24_76_bits},
+{17, 19, 19, helvetica24_77_bits},
+{14, 19, 19, helvetica24_78_bits},
+{16, 19, 19, helvetica24_79_bits},
+{13, 19, 19, helvetica24_80_bits},
+{16, 19, 19, helvetica24_81_bits},
+{13, 19, 19, helvetica24_82_bits},
+{14, 19, 19, helvetica24_83_bits},
+{14, 19, 19, helvetica24_84_bits},
+{14, 19, 19, helvetica24_85_bits},
+{15, 19, 19, helvetica24_86_bits},
+{20, 19, 19, helvetica24_87_bits},
+{15, 19, 19, helvetica24_88_bits},
+{14, 19, 19, helvetica24_89_bits},
+{13, 19, 19, helvetica24_90_bits},
+{4, 24, 19, helvetica24_91_bits},
+{7, 19, 19, helvetica24_92_bits},
+{4, 24, 19, helvetica24_93_bits},
+{10, 9, 19, helvetica24_94_bits},
+{14, 2, -3, helvetica24_95_bits},
+{2, 6, 19, helvetica24_96_bits},
+{11, 14, 14, helvetica24_97_bits},
+{11, 19, 19, helvetica24_98_bits},
+{10, 14, 14, helvetica24_99_bits},
+{11, 19, 19, helvetica24_100_bits},
+{11, 14, 14, helvetica24_101_bits},
+{6, 19, 19, helvetica24_102_bits},
+{11, 19, 14, helvetica24_103_bits},
+{10, 19, 19, helvetica24_104_bits},
+{2, 19, 19, helvetica24_105_bits},
+{4, 24, 19, helvetica24_106_bits},
+{10, 19, 19, helvetica24_107_bits},
+{2, 19, 19, helvetica24_108_bits},
+{16, 14, 14, helvetica24_109_bits},
+{10, 14, 14, helvetica24_110_bits},
+{11, 14, 14, helvetica24_111_bits},
+{11, 19, 14, helvetica24_112_bits},
+{11, 19, 14, helvetica24_113_bits},
+{6, 14, 14, helvetica24_114_bits},
+{10, 14, 14, helvetica24_115_bits},
+{6, 18, 18, helvetica24_116_bits},
+{10, 14, 14, helvetica24_117_bits},
+{11, 14, 14, helvetica24_118_bits},
+{18, 14, 14, helvetica24_119_bits},
+{10, 14, 14, helvetica24_120_bits},
+{12, 19, 14, helvetica24_121_bits},
+{10, 14, 14, helvetica24_122_bits},
+{6, 24, 19, helvetica24_123_bits},
+{2, 24, 19, helvetica24_124_bits},
+{6, 24, 19, helvetica24_125_bits},
+{10, 4, 9, helvetica24_126_bits},
+{5, 1, 1, helvetica24_127_bits},
+{5, 1, 1, helvetica24_128_bits},
+{5, 1, 1, helvetica24_129_bits},
+{5, 1, 1, helvetica24_130_bits},
+{5, 1, 1, helvetica24_131_bits},
+{5, 1, 1, helvetica24_132_bits},
+{5, 1, 1, helvetica24_133_bits},
+{5, 1, 1, helvetica24_134_bits},
+{5, 1, 1, helvetica24_135_bits},
+{5, 1, 1, helvetica24_136_bits},
+{5, 1, 1, helvetica24_137_bits},
+{5, 1, 1, helvetica24_138_bits},
+{5, 1, 1, helvetica24_139_bits},
+{5, 1, 1, helvetica24_140_bits},
+{5, 1, 1, helvetica24_141_bits},
+{5, 1, 1, helvetica24_142_bits},
+{5, 1, 1, helvetica24_143_bits},
+{5, 1, 1, helvetica24_144_bits},
+{5, 1, 1, helvetica24_145_bits},
+{5, 1, 1, helvetica24_146_bits},
+{5, 1, 1, helvetica24_147_bits},
+{5, 1, 1, helvetica24_148_bits},
+{5, 1, 1, helvetica24_149_bits},
+{5, 1, 1, helvetica24_150_bits},
+{5, 1, 1, helvetica24_151_bits},
+{5, 1, 1, helvetica24_152_bits},
+{5, 1, 1, helvetica24_153_bits},
+{5, 1, 1, helvetica24_154_bits},
+{5, 1, 1, helvetica24_155_bits},
+{5, 1, 1, helvetica24_156_bits},
+{5, 1, 1, helvetica24_157_bits},
+{5, 1, 1, helvetica24_158_bits},
+{5, 1, 1, helvetica24_159_bits},
+{1, 1, 1, helvetica24_160_bits},
+{2, 19, 14, helvetica24_161_bits},
+{10, 18, 16, helvetica24_162_bits},
+{12, 18, 18, helvetica24_163_bits},
+{11, 12, 15, helvetica24_164_bits},
+{14, 18, 18, helvetica24_165_bits},
+{2, 23, 19, helvetica24_166_bits},
+{11, 24, 19, helvetica24_167_bits},
+{6, 2, 18, helvetica24_168_bits},
+{17, 17, 17, helvetica24_169_bits},
+{7, 12, 19, helvetica24_170_bits},
+{9, 8, 11, helvetica24_171_bits},
+{13, 8, 11, helvetica24_172_bits},
+{6, 2, 8, helvetica24_173_bits},
+{17, 17, 17, helvetica24_174_bits},
+{6, 2, 18, helvetica24_175_bits},
+{8, 7, 18, helvetica24_176_bits},
+{12, 13, 13, helvetica24_177_bits},
+{7, 10, 18, helvetica24_178_bits},
+{7, 10, 18, helvetica24_179_bits},
+{5, 4, 19, helvetica24_180_bits},
+{10, 19, 14, helvetica24_181_bits},
+{10, 23, 19, helvetica24_182_bits},
+{2, 3, 9, helvetica24_183_bits},
+{5, 6, 1, helvetica24_184_bits},
+{4, 10, 18, helvetica24_185_bits},
+{7, 12, 19, helvetica24_186_bits},
+{9, 8, 11, helvetica24_187_bits},
+{18, 18, 18, helvetica24_188_bits},
+{18, 18, 18, helvetica24_189_bits},
+{19, 18, 18, helvetica24_190_bits},
+{10, 19, 14, helvetica24_191_bits},
+{15, 24, 24, helvetica24_192_bits},
+{15, 24, 24, helvetica24_193_bits},
+{15, 24, 24, helvetica24_194_bits},
+{15, 23, 23, helvetica24_195_bits},
+{15, 23, 23, helvetica24_196_bits},
+{15, 24, 24, helvetica24_197_bits},
+{21, 19, 19, helvetica24_198_bits},
+{16, 24, 19, helvetica24_199_bits},
+{12, 24, 24, helvetica24_200_bits},
+{12, 24, 24, helvetica24_201_bits},
+{12, 24, 24, helvetica24_202_bits},
+{12, 23, 23, helvetica24_203_bits},
+{5, 24, 24, helvetica24_204_bits},
+{5, 24, 24, helvetica24_205_bits},
+{8, 24, 24, helvetica24_206_bits},
+{6, 23, 23, helvetica24_207_bits},
+{17, 19, 19, helvetica24_208_bits},
+{14, 23, 23, helvetica24_209_bits},
+{16, 24, 24, helvetica24_210_bits},
+{16, 24, 24, helvetica24_211_bits},
+{16, 24, 24, helvetica24_212_bits},
+{16, 23, 23, helvetica24_213_bits},
+{16, 23, 23, helvetica24_214_bits},
+{13, 12, 13, helvetica24_215_bits},
+{18, 19, 19, helvetica24_216_bits},
+{14, 24, 24, helvetica24_217_bits},
+{14, 24, 24, helvetica24_218_bits},
+{14, 24, 24, helvetica24_219_bits},
+{14, 23, 23, helvetica24_220_bits},
+{14, 24, 24, helvetica24_221_bits},
+{13, 19, 19, helvetica24_222_bits},
+{10, 19, 19, helvetica24_223_bits},
+{11, 19, 19, helvetica24_224_bits},
+{11, 19, 19, helvetica24_225_bits},
+{11, 19, 19, helvetica24_226_bits},
+{11, 19, 19, helvetica24_227_bits},
+{11, 18, 18, helvetica24_228_bits},
+{11, 19, 19, helvetica24_229_bits},
+{19, 14, 14, helvetica24_230_bits},
+{10, 19, 14, helvetica24_231_bits},
+{11, 19, 19, helvetica24_232_bits},
+{11, 19, 19, helvetica24_233_bits},
+{11, 19, 19, helvetica24_234_bits},
+{11, 18, 18, helvetica24_235_bits},
+{5, 19, 19, helvetica24_236_bits},
+{5, 19, 19, helvetica24_237_bits},
+{8, 19, 19, helvetica24_238_bits},
+{6, 18, 18, helvetica24_239_bits},
+{11, 19, 19, helvetica24_240_bits},
+{10, 18, 18, helvetica24_241_bits},
+{11, 19, 19, helvetica24_242_bits},
+{11, 19, 19, helvetica24_243_bits},
+{11, 19, 19, helvetica24_244_bits},
+{11, 18, 18, helvetica24_245_bits},
+{11, 18, 18, helvetica24_246_bits},
+{12, 12, 13, helvetica24_247_bits},
+{13, 14, 14, helvetica24_248_bits},
+{10, 19, 19, helvetica24_249_bits},
+{10, 19, 19, helvetica24_250_bits},
+{10, 19, 19, helvetica24_251_bits},
+{10, 18, 18, helvetica24_252_bits},
+{12, 24, 19, helvetica24_253_bits},
+{11, 24, 19, helvetica24_254_bits},
+{12, 23, 18, helvetica24_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica8.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica8.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica8.bdf	(revision 22322)
@@ -0,0 +1,2572 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Helvetica-Medium-R-Normal--8-80-75-75-P-46-ISO8859-1
+SIZE 8 75 75
+FONTBOUNDINGBOX 9 11 0 -2
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Helvetica"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 8
+POINT_SIZE 80
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 46
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 6
+X_HEIGHT 5
+FACE_NAME "Helvetica"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.  "
+_DEC_DEVICE_FONTNAMES "PS=Helvetica"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2-1, 04-Dec-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Helvetica"
+FONT "-Adobe-Helvetica-Medium-R-Normal--8-80-75-75-P-46-ISO8859-1"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 5
+DEFAULT_CHAR 32
+FONT_ASCENT 8
+FONT_DESCENT 2
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 6 1 0
+BITMAP
+80
+80
+80
+80
+00
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 3 1 3
+BITMAP
+a0
+a0
+a0
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+BITMAP
+50
+f8
+50
+f8
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 7 1 -1
+BITMAP
+20
+70
+80
+60
+10
+e0
+40
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 6 6 1 0
+BITMAP
+e8
+a8
+d0
+2c
+54
+5c
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+40
+a0
+48
+a8
+b0
+58
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 3 1 3
+BITMAP
+80
+80
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 7 1 -1
+BITMAP
+40
+80
+80
+80
+80
+80
+40
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 7 1 -1
+BITMAP
+80
+40
+40
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 3 1 2
+BITMAP
+40
+e0
+40
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 1 0
+BITMAP
+20
+20
+f8
+20
+20
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 3 0 -2
+BITMAP
+40
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 1 2 2
+BITMAP
+f0
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 1 1 0
+BITMAP
+80
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 7 1 -1
+BITMAP
+40
+40
+40
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 0
+BITMAP
+60
+90
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 2 6 2 0
+BITMAP
+40
+c0
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 0
+BITMAP
+60
+90
+10
+20
+40
+f0
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 6 2 0
+BITMAP
+c0
+20
+c0
+20
+20
+c0
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 0
+BITMAP
+20
+20
+60
+f0
+20
+20
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 6 2 0
+BITMAP
+e0
+80
+c0
+20
+20
+c0
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 0
+BITMAP
+70
+80
+e0
+90
+90
+60
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 0
+BITMAP
+f0
+10
+20
+40
+40
+40
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 0
+BITMAP
+60
+90
+60
+90
+90
+60
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 0
+BITMAP
+60
+90
+90
+70
+10
+60
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 4 1 0
+BITMAP
+80
+00
+00
+80
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 6 0 -2
+BITMAP
+40
+00
+00
+40
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+BITMAP
+20
+40
+80
+40
+20
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 3 1 1
+BITMAP
+e0
+00
+e0
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 2 0
+BITMAP
+80
+40
+20
+40
+80
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 2 0
+BITMAP
+c0
+20
+40
+00
+40
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 1080 0
+DWIDTH 9 0
+BBX 8 7 1 -1
+BITMAP
+3e
+41
+99
+a5
+9e
+80
+78
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+20
+20
+50
+70
+88
+88
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 2 0
+BITMAP
+e0
+90
+e0
+90
+90
+e0
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+70
+88
+80
+80
+88
+70
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+f0
+88
+88
+88
+88
+f0
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 2 0
+BITMAP
+f0
+80
+e0
+80
+80
+f0
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 2 0
+BITMAP
+f0
+80
+e0
+80
+80
+80
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+70
+80
+98
+88
+88
+70
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+88
+88
+f8
+88
+88
+88
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 6 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 1 0
+BITMAP
+20
+20
+20
+20
+a0
+40
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 2 0
+BITMAP
+90
+a0
+c0
+e0
+90
+90
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 6 2 0
+BITMAP
+80
+80
+80
+80
+80
+e0
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 5 6 2 0
+BITMAP
+88
+d8
+a8
+a8
+a8
+a8
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+88
+c8
+a8
+a8
+98
+88
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+70
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 2 0
+BITMAP
+e0
+90
+90
+e0
+80
+80
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 1 -2
+BITMAP
+70
+88
+88
+88
+88
+70
+20
+10
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 2 0
+BITMAP
+e0
+90
+90
+e0
+90
+90
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 2 0
+BITMAP
+70
+80
+e0
+10
+10
+e0
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 1 0
+BITMAP
+e0
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 2 0
+BITMAP
+90
+90
+90
+90
+a0
+40
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 7 6 1 0
+BITMAP
+92
+92
+92
+6c
+48
+48
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 2 0
+BITMAP
+90
+90
+60
+60
+90
+90
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+c8
+48
+48
+30
+20
+20
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 2 0
+BITMAP
+f0
+10
+20
+40
+80
+f0
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 7 1 -1
+BITMAP
+c0
+80
+80
+80
+80
+80
+c0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 7 0 -1
+BITMAP
+80
+80
+80
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 7 0 -1
+BITMAP
+c0
+40
+40
+40
+40
+40
+c0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 3 0 2
+BITMAP
+20
+50
+88
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 1 0 -1
+BITMAP
+f8
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 3 1 3
+BITMAP
+80
+80
+80
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 5 1 0
+BITMAP
+c0
+20
+e0
+a0
+d0
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 7 1 0
+BITMAP
+80
+80
+e0
+90
+90
+90
+e0
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 1 0
+BITMAP
+60
+80
+80
+80
+60
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 7 1 0
+BITMAP
+10
+10
+70
+90
+90
+90
+70
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 1 0
+BITMAP
+40
+a0
+e0
+80
+60
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 7 1 0
+BITMAP
+20
+40
+e0
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 -1
+BITMAP
+70
+90
+90
+70
+10
+60
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 7 1 0
+BITMAP
+80
+80
+e0
+90
+90
+90
+90
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 7 1 0
+BITMAP
+80
+00
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 9 0 -2
+BITMAP
+40
+00
+40
+40
+40
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 1 0
+BITMAP
+80
+80
+a0
+c0
+c0
+a0
+a0
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 7 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 1 0
+BITMAP
+f0
+a8
+a8
+a8
+a8
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 1 0
+BITMAP
+e0
+90
+90
+90
+90
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 1 0
+BITMAP
+60
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 -1
+BITMAP
+e0
+90
+90
+90
+e0
+80
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 -1
+BITMAP
+70
+90
+90
+90
+70
+10
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 5 1 0
+BITMAP
+a0
+c0
+80
+80
+80
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 1 0
+BITMAP
+60
+80
+60
+20
+c0
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 7 1 0
+BITMAP
+40
+40
+e0
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 1 0
+BITMAP
+a0
+a0
+a0
+a0
+60
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 1 0
+BITMAP
+90
+90
+90
+a0
+40
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 1 0
+BITMAP
+a8
+a8
+a8
+50
+50
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 1 0
+BITMAP
+90
+90
+60
+90
+90
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 6 1 -1
+BITMAP
+90
+90
+90
+60
+40
+80
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 1 0
+BITMAP
+e0
+20
+40
+80
+e0
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 7 0 -1
+BITMAP
+20
+40
+40
+c0
+40
+40
+20
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 7 1 -1
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 7 0 -1
+BITMAP
+80
+40
+40
+60
+40
+40
+80
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 2 1 2
+BITMAP
+48
+b0
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 7 1 -2
+BITMAP
+80
+00
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 7 1 -1
+BITMAP
+40
+40
+a0
+80
+a0
+40
+40
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 0
+BITMAP
+30
+40
+e0
+40
+40
+f0
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 5 5 0 0
+BITMAP
+88
+70
+50
+70
+88
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 1 0
+BITMAP
+88
+50
+f8
+20
+20
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 7 1 -1
+BITMAP
+80
+80
+80
+00
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 1 -2
+BITMAP
+70
+80
+60
+90
+60
+30
+10
+e0
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 4 1 0 5
+BITMAP
+90
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 6 7 1 -1
+BITMAP
+78
+84
+b4
+a4
+b4
+84
+78
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 5 0 1
+BITMAP
+c0
+20
+e0
+00
+e0
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 3 1 1
+BITMAP
+50
+a0
+50
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 3 1 1
+BITMAP
+f0
+10
+10
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 1 0 2
+BITMAP
+c0
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 6 7 1 -1
+BITMAP
+78
+84
+b4
+b4
+ac
+84
+78
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 1 0 5
+BITMAP
+e0
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 3 1 3
+BITMAP
+40
+a0
+40
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 1 0
+BITMAP
+20
+f0
+20
+00
+f0
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 4 1 2
+BITMAP
+80
+40
+80
+c0
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 4 0 2
+BITMAP
+e0
+60
+20
+c0
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 2 0 4
+BITMAP
+40
+80
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 1 -2
+BITMAP
+a0
+a0
+a0
+e0
+80
+80
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 8 0 -2
+BITMAP
+78
+d0
+d0
+d0
+50
+50
+50
+50
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 2 1 1
+BITMAP
+80
+80
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 2 0 -2
+BITMAP
+40
+80
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 4 0 2
+BITMAP
+40
+c0
+40
+40
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 5 0 1
+BITMAP
+e0
+a0
+e0
+00
+e0
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 3 1 1
+BITMAP
+a0
+50
+a0
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 7 7 0 -1
+BITMAP
+40
+c4
+48
+54
+2c
+5e
+04
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 7 7 0 -1
+BITMAP
+40
+c4
+48
+5c
+22
+44
+0e
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 7 7 0 -1
+BITMAP
+e0
+64
+28
+d4
+2c
+5e
+04
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 1 -1
+BITMAP
+20
+00
+20
+40
+90
+60
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+40
+20
+00
+20
+20
+50
+70
+88
+88
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+10
+20
+00
+20
+20
+50
+70
+88
+88
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+20
+50
+00
+20
+20
+50
+70
+88
+88
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+28
+50
+00
+20
+20
+50
+70
+88
+88
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 1 0
+BITMAP
+50
+00
+20
+20
+50
+70
+88
+88
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+20
+50
+20
+20
+20
+50
+70
+88
+88
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 7 6 1 0
+BITMAP
+3e
+30
+50
+7c
+90
+9e
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 1 -2
+BITMAP
+70
+88
+88
+80
+88
+70
+40
+80
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 9 2 0
+BITMAP
+40
+20
+00
+f0
+80
+e0
+80
+80
+f0
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 9 2 0
+BITMAP
+20
+40
+00
+f0
+80
+e0
+80
+80
+f0
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 9 2 0
+BITMAP
+40
+a0
+00
+f0
+80
+e0
+80
+80
+f0
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 8 2 0
+BITMAP
+a0
+00
+f0
+80
+e0
+80
+80
+f0
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 9 0 0
+BITMAP
+80
+40
+00
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 9 1 0
+BITMAP
+40
+80
+00
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 9 0 0
+BITMAP
+40
+a0
+00
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 8 0 0
+BITMAP
+a0
+00
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+70
+48
+e8
+48
+48
+70
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+28
+50
+00
+88
+c8
+a8
+a8
+98
+88
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+40
+20
+00
+70
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+10
+20
+00
+70
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+20
+50
+00
+70
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+28
+50
+00
+70
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 1 0
+BITMAP
+50
+00
+70
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 1 0
+BITMAP
+90
+60
+60
+90
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 1 -1
+BITMAP
+08
+78
+88
+98
+a8
+c8
+f0
+80
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+40
+20
+00
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+10
+20
+00
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+20
+50
+00
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 1 0
+BITMAP
+50
+00
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+08
+10
+00
+c8
+48
+48
+30
+20
+20
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+80
+f0
+88
+88
+f0
+80
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 7 2 -1
+BITMAP
+60
+90
+a0
+90
+90
+a0
+80
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 8 1 0
+BITMAP
+80
+40
+00
+c0
+20
+e0
+a0
+d0
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 8 1 0
+BITMAP
+20
+40
+00
+c0
+20
+e0
+a0
+d0
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 8 1 0
+BITMAP
+40
+a0
+00
+c0
+20
+e0
+a0
+d0
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 5 8 0 0
+BITMAP
+68
+b0
+00
+60
+10
+70
+50
+68
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 7 1 0
+BITMAP
+a0
+00
+c0
+20
+e0
+a0
+d0
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 8 1 0
+BITMAP
+40
+a0
+40
+c0
+20
+e0
+a0
+d0
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 1 0
+BITMAP
+d0
+28
+f8
+a0
+d8
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 1 -2
+BITMAP
+60
+80
+80
+80
+60
+40
+80
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 8 1 0
+BITMAP
+40
+20
+00
+40
+a0
+e0
+80
+60
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 8 1 0
+BITMAP
+20
+40
+00
+40
+a0
+e0
+80
+60
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 8 1 0
+BITMAP
+40
+a0
+00
+40
+a0
+e0
+80
+60
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 1 0
+BITMAP
+a0
+00
+40
+a0
+e0
+80
+60
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 8 0 0
+BITMAP
+80
+40
+00
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 8 1 0
+BITMAP
+40
+80
+00
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 8 0 0
+BITMAP
+40
+a0
+00
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 7 0 0
+BITMAP
+a0
+00
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 1 0
+BITMAP
+90
+60
+a0
+70
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 1 0
+BITMAP
+50
+a0
+00
+e0
+90
+90
+90
+90
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 1 0
+BITMAP
+40
+20
+00
+60
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 1 0
+BITMAP
+10
+20
+00
+60
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 1 0
+BITMAP
+40
+a0
+00
+60
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 1 0
+BITMAP
+50
+a0
+00
+60
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 7 1 0
+BITMAP
+90
+00
+60
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 1 0
+BITMAP
+20
+00
+f0
+00
+20
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 7 7 0 -1
+BITMAP
+02
+3c
+48
+58
+68
+70
+80
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 8 1 0
+BITMAP
+80
+40
+00
+a0
+a0
+a0
+a0
+60
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 8 1 0
+BITMAP
+20
+40
+00
+a0
+a0
+a0
+a0
+60
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 8 1 0
+BITMAP
+40
+a0
+00
+a0
+a0
+a0
+a0
+60
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 1 0
+BITMAP
+a0
+00
+a0
+a0
+a0
+a0
+60
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 9 1 -1
+BITMAP
+10
+20
+00
+90
+90
+90
+60
+40
+80
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 7 1 -1
+BITMAP
+80
+e0
+90
+90
+90
+e0
+80
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 8 1 -1
+BITMAP
+50
+00
+90
+90
+90
+60
+40
+80
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica8.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica8.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/helvetica8.h	(revision 22322)
@@ -0,0 +1,769 @@
+static unsigned char helvetica8_0_bits[] = {
+0x00};
+static unsigned char helvetica8_1_bits[] = {
+0x00};
+static unsigned char helvetica8_2_bits[] = {
+0x00};
+static unsigned char helvetica8_3_bits[] = {
+0x00};
+static unsigned char helvetica8_4_bits[] = {
+0x00};
+static unsigned char helvetica8_5_bits[] = {
+0x00};
+static unsigned char helvetica8_6_bits[] = {
+0x00};
+static unsigned char helvetica8_7_bits[] = {
+0x00};
+static unsigned char helvetica8_8_bits[] = {
+0x00};
+static unsigned char helvetica8_9_bits[] = {
+0x00};
+static unsigned char helvetica8_10_bits[] = {
+0x00};
+static unsigned char helvetica8_11_bits[] = {
+0x00};
+static unsigned char helvetica8_12_bits[] = {
+0x00};
+static unsigned char helvetica8_13_bits[] = {
+0x00};
+static unsigned char helvetica8_14_bits[] = {
+0x00};
+static unsigned char helvetica8_15_bits[] = {
+0x00};
+static unsigned char helvetica8_16_bits[] = {
+0x00};
+static unsigned char helvetica8_17_bits[] = {
+0x00};
+static unsigned char helvetica8_18_bits[] = {
+0x00};
+static unsigned char helvetica8_19_bits[] = {
+0x00};
+static unsigned char helvetica8_20_bits[] = {
+0x00};
+static unsigned char helvetica8_21_bits[] = {
+0x00};
+static unsigned char helvetica8_22_bits[] = {
+0x00};
+static unsigned char helvetica8_23_bits[] = {
+0x00};
+static unsigned char helvetica8_24_bits[] = {
+0x00};
+static unsigned char helvetica8_25_bits[] = {
+0x00};
+static unsigned char helvetica8_26_bits[] = {
+0x00};
+static unsigned char helvetica8_27_bits[] = {
+0x00};
+static unsigned char helvetica8_28_bits[] = {
+0x00};
+static unsigned char helvetica8_29_bits[] = {
+0x00};
+static unsigned char helvetica8_30_bits[] = {
+0x00};
+static unsigned char helvetica8_31_bits[] = {
+0x00};
+static unsigned char helvetica8_32_bits[] = {
+0x00};
+static unsigned char helvetica8_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x00, 0x01};
+static unsigned char helvetica8_34_bits[] = {
+0x05, 0x05, 0x05};
+static unsigned char helvetica8_35_bits[] = {
+0x0a, 0x1f, 0x0a, 0x1f, 0x0a};
+static unsigned char helvetica8_36_bits[] = {
+0x04, 0x0e, 0x01, 0x06, 0x08, 0x07, 0x02};
+static unsigned char helvetica8_37_bits[] = {
+0x17, 0x15, 0x0b, 0x34, 0x2a, 0x3a};
+static unsigned char helvetica8_38_bits[] = {
+0x02, 0x05, 0x12, 0x15, 0x0d, 0x1a};
+static unsigned char helvetica8_39_bits[] = {
+0x01, 0x01, 0x01};
+static unsigned char helvetica8_40_bits[] = {
+0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02};
+static unsigned char helvetica8_41_bits[] = {
+0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01};
+static unsigned char helvetica8_42_bits[] = {
+0x02, 0x07, 0x02};
+static unsigned char helvetica8_43_bits[] = {
+0x04, 0x04, 0x1f, 0x04, 0x04};
+static unsigned char helvetica8_44_bits[] = {
+0x02, 0x02, 0x01};
+static unsigned char helvetica8_45_bits[] = {
+0x0f};
+static unsigned char helvetica8_46_bits[] = {
+0x01};
+static unsigned char helvetica8_47_bits[] = {
+0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_48_bits[] = {
+0x06, 0x09, 0x09, 0x09, 0x09, 0x06};
+static unsigned char helvetica8_49_bits[] = {
+0x02, 0x03, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_50_bits[] = {
+0x06, 0x09, 0x08, 0x04, 0x02, 0x0f};
+static unsigned char helvetica8_51_bits[] = {
+0x03, 0x04, 0x03, 0x04, 0x04, 0x03};
+static unsigned char helvetica8_52_bits[] = {
+0x04, 0x04, 0x06, 0x0f, 0x04, 0x04};
+static unsigned char helvetica8_53_bits[] = {
+0x07, 0x01, 0x03, 0x04, 0x04, 0x03};
+static unsigned char helvetica8_54_bits[] = {
+0x0e, 0x01, 0x07, 0x09, 0x09, 0x06};
+static unsigned char helvetica8_55_bits[] = {
+0x0f, 0x08, 0x04, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_56_bits[] = {
+0x06, 0x09, 0x06, 0x09, 0x09, 0x06};
+static unsigned char helvetica8_57_bits[] = {
+0x06, 0x09, 0x09, 0x0e, 0x08, 0x06};
+static unsigned char helvetica8_58_bits[] = {
+0x01, 0x00, 0x00, 0x01};
+static unsigned char helvetica8_59_bits[] = {
+0x02, 0x00, 0x00, 0x02, 0x02, 0x01};
+static unsigned char helvetica8_60_bits[] = {
+0x04, 0x02, 0x01, 0x02, 0x04};
+static unsigned char helvetica8_61_bits[] = {
+0x07, 0x00, 0x07};
+static unsigned char helvetica8_62_bits[] = {
+0x01, 0x02, 0x04, 0x02, 0x01};
+static unsigned char helvetica8_63_bits[] = {
+0x03, 0x04, 0x02, 0x00, 0x02};
+static unsigned char helvetica8_64_bits[] = {
+0x7c, 0x82, 0x99, 0xa5, 0x79, 0x01, 0x1e};
+static unsigned char helvetica8_65_bits[] = {
+0x04, 0x04, 0x0a, 0x0e, 0x11, 0x11};
+static unsigned char helvetica8_66_bits[] = {
+0x07, 0x09, 0x07, 0x09, 0x09, 0x07};
+static unsigned char helvetica8_67_bits[] = {
+0x0e, 0x11, 0x01, 0x01, 0x11, 0x0e};
+static unsigned char helvetica8_68_bits[] = {
+0x0f, 0x11, 0x11, 0x11, 0x11, 0x0f};
+static unsigned char helvetica8_69_bits[] = {
+0x0f, 0x01, 0x07, 0x01, 0x01, 0x0f};
+static unsigned char helvetica8_70_bits[] = {
+0x0f, 0x01, 0x07, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_71_bits[] = {
+0x0e, 0x01, 0x19, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_72_bits[] = {
+0x11, 0x11, 0x1f, 0x11, 0x11, 0x11};
+static unsigned char helvetica8_73_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_74_bits[] = {
+0x04, 0x04, 0x04, 0x04, 0x05, 0x02};
+static unsigned char helvetica8_75_bits[] = {
+0x09, 0x05, 0x03, 0x07, 0x09, 0x09};
+static unsigned char helvetica8_76_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x07};
+static unsigned char helvetica8_77_bits[] = {
+0x11, 0x1b, 0x15, 0x15, 0x15, 0x15};
+static unsigned char helvetica8_78_bits[] = {
+0x11, 0x13, 0x15, 0x15, 0x19, 0x11};
+static unsigned char helvetica8_79_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_80_bits[] = {
+0x07, 0x09, 0x09, 0x07, 0x01, 0x01};
+static unsigned char helvetica8_81_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e, 0x04, 0x08};
+static unsigned char helvetica8_82_bits[] = {
+0x07, 0x09, 0x09, 0x07, 0x09, 0x09};
+static unsigned char helvetica8_83_bits[] = {
+0x0e, 0x01, 0x07, 0x08, 0x08, 0x07};
+static unsigned char helvetica8_84_bits[] = {
+0x07, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_85_bits[] = {
+0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_86_bits[] = {
+0x09, 0x09, 0x09, 0x09, 0x05, 0x02};
+static unsigned char helvetica8_87_bits[] = {
+0x49, 0x49, 0x49, 0x36, 0x12, 0x12};
+static unsigned char helvetica8_88_bits[] = {
+0x09, 0x09, 0x06, 0x06, 0x09, 0x09};
+static unsigned char helvetica8_89_bits[] = {
+0x13, 0x12, 0x12, 0x0c, 0x04, 0x04};
+static unsigned char helvetica8_90_bits[] = {
+0x0f, 0x08, 0x04, 0x02, 0x01, 0x0f};
+static unsigned char helvetica8_91_bits[] = {
+0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03};
+static unsigned char helvetica8_92_bits[] = {
+0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_93_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03};
+static unsigned char helvetica8_94_bits[] = {
+0x04, 0x0a, 0x11};
+static unsigned char helvetica8_95_bits[] = {
+0x1f};
+static unsigned char helvetica8_96_bits[] = {
+0x01, 0x01, 0x01};
+static unsigned char helvetica8_97_bits[] = {
+0x03, 0x04, 0x07, 0x05, 0x0b};
+static unsigned char helvetica8_98_bits[] = {
+0x01, 0x01, 0x07, 0x09, 0x09, 0x09, 0x07};
+static unsigned char helvetica8_99_bits[] = {
+0x06, 0x01, 0x01, 0x01, 0x06};
+static unsigned char helvetica8_100_bits[] = {
+0x08, 0x08, 0x0e, 0x09, 0x09, 0x09, 0x0e};
+static unsigned char helvetica8_101_bits[] = {
+0x02, 0x05, 0x07, 0x01, 0x06};
+static unsigned char helvetica8_102_bits[] = {
+0x04, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_103_bits[] = {
+0x0e, 0x09, 0x09, 0x0e, 0x08, 0x06};
+static unsigned char helvetica8_104_bits[] = {
+0x01, 0x01, 0x07, 0x09, 0x09, 0x09, 0x09};
+static unsigned char helvetica8_105_bits[] = {
+0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_106_bits[] = {
+0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01};
+static unsigned char helvetica8_107_bits[] = {
+0x01, 0x01, 0x05, 0x03, 0x03, 0x05, 0x05};
+static unsigned char helvetica8_108_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_109_bits[] = {
+0x0f, 0x15, 0x15, 0x15, 0x15};
+static unsigned char helvetica8_110_bits[] = {
+0x07, 0x09, 0x09, 0x09, 0x09};
+static unsigned char helvetica8_111_bits[] = {
+0x06, 0x09, 0x09, 0x09, 0x06};
+static unsigned char helvetica8_112_bits[] = {
+0x07, 0x09, 0x09, 0x09, 0x07, 0x01};
+static unsigned char helvetica8_113_bits[] = {
+0x0e, 0x09, 0x09, 0x09, 0x0e, 0x08};
+static unsigned char helvetica8_114_bits[] = {
+0x05, 0x03, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_115_bits[] = {
+0x06, 0x01, 0x06, 0x04, 0x03};
+static unsigned char helvetica8_116_bits[] = {
+0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_117_bits[] = {
+0x05, 0x05, 0x05, 0x05, 0x06};
+static unsigned char helvetica8_118_bits[] = {
+0x09, 0x09, 0x09, 0x05, 0x02};
+static unsigned char helvetica8_119_bits[] = {
+0x15, 0x15, 0x15, 0x0a, 0x0a};
+static unsigned char helvetica8_120_bits[] = {
+0x09, 0x09, 0x06, 0x09, 0x09};
+static unsigned char helvetica8_121_bits[] = {
+0x09, 0x09, 0x09, 0x06, 0x02, 0x01};
+static unsigned char helvetica8_122_bits[] = {
+0x07, 0x04, 0x02, 0x01, 0x07};
+static unsigned char helvetica8_123_bits[] = {
+0x04, 0x02, 0x02, 0x03, 0x02, 0x02, 0x04};
+static unsigned char helvetica8_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_125_bits[] = {
+0x01, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01};
+static unsigned char helvetica8_126_bits[] = {
+0x12, 0x0d};
+static unsigned char helvetica8_127_bits[] = {
+0x00};
+static unsigned char helvetica8_128_bits[] = {
+0x00};
+static unsigned char helvetica8_129_bits[] = {
+0x00};
+static unsigned char helvetica8_130_bits[] = {
+0x00};
+static unsigned char helvetica8_131_bits[] = {
+0x00};
+static unsigned char helvetica8_132_bits[] = {
+0x00};
+static unsigned char helvetica8_133_bits[] = {
+0x00};
+static unsigned char helvetica8_134_bits[] = {
+0x00};
+static unsigned char helvetica8_135_bits[] = {
+0x00};
+static unsigned char helvetica8_136_bits[] = {
+0x00};
+static unsigned char helvetica8_137_bits[] = {
+0x00};
+static unsigned char helvetica8_138_bits[] = {
+0x00};
+static unsigned char helvetica8_139_bits[] = {
+0x00};
+static unsigned char helvetica8_140_bits[] = {
+0x00};
+static unsigned char helvetica8_141_bits[] = {
+0x00};
+static unsigned char helvetica8_142_bits[] = {
+0x00};
+static unsigned char helvetica8_143_bits[] = {
+0x00};
+static unsigned char helvetica8_144_bits[] = {
+0x00};
+static unsigned char helvetica8_145_bits[] = {
+0x00};
+static unsigned char helvetica8_146_bits[] = {
+0x00};
+static unsigned char helvetica8_147_bits[] = {
+0x00};
+static unsigned char helvetica8_148_bits[] = {
+0x00};
+static unsigned char helvetica8_149_bits[] = {
+0x00};
+static unsigned char helvetica8_150_bits[] = {
+0x00};
+static unsigned char helvetica8_151_bits[] = {
+0x00};
+static unsigned char helvetica8_152_bits[] = {
+0x00};
+static unsigned char helvetica8_153_bits[] = {
+0x00};
+static unsigned char helvetica8_154_bits[] = {
+0x00};
+static unsigned char helvetica8_155_bits[] = {
+0x00};
+static unsigned char helvetica8_156_bits[] = {
+0x00};
+static unsigned char helvetica8_157_bits[] = {
+0x00};
+static unsigned char helvetica8_158_bits[] = {
+0x00};
+static unsigned char helvetica8_159_bits[] = {
+0x00};
+static unsigned char helvetica8_160_bits[] = {
+0x00};
+static unsigned char helvetica8_161_bits[] = {
+0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_162_bits[] = {
+0x02, 0x02, 0x05, 0x01, 0x05, 0x02, 0x02};
+static unsigned char helvetica8_163_bits[] = {
+0x0c, 0x02, 0x07, 0x02, 0x02, 0x0f};
+static unsigned char helvetica8_164_bits[] = {
+0x11, 0x0e, 0x0a, 0x0e, 0x11};
+static unsigned char helvetica8_165_bits[] = {
+0x11, 0x0a, 0x1f, 0x04, 0x04};
+static unsigned char helvetica8_166_bits[] = {
+0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_167_bits[] = {
+0x0e, 0x01, 0x06, 0x09, 0x06, 0x0c, 0x08, 0x07};
+static unsigned char helvetica8_168_bits[] = {
+0x09};
+static unsigned char helvetica8_169_bits[] = {
+0x1e, 0x21, 0x2d, 0x25, 0x2d, 0x21, 0x1e};
+static unsigned char helvetica8_170_bits[] = {
+0x03, 0x04, 0x07, 0x00, 0x07};
+static unsigned char helvetica8_171_bits[] = {
+0x0a, 0x05, 0x0a};
+static unsigned char helvetica8_172_bits[] = {
+0x0f, 0x08, 0x08};
+static unsigned char helvetica8_173_bits[] = {
+0x03};
+static unsigned char helvetica8_174_bits[] = {
+0x1e, 0x21, 0x2d, 0x2d, 0x35, 0x21, 0x1e};
+static unsigned char helvetica8_175_bits[] = {
+0x07};
+static unsigned char helvetica8_176_bits[] = {
+0x02, 0x05, 0x02};
+static unsigned char helvetica8_177_bits[] = {
+0x04, 0x0f, 0x04, 0x00, 0x0f};
+static unsigned char helvetica8_178_bits[] = {
+0x01, 0x02, 0x01, 0x03};
+static unsigned char helvetica8_179_bits[] = {
+0x07, 0x06, 0x04, 0x03};
+static unsigned char helvetica8_180_bits[] = {
+0x02, 0x01};
+static unsigned char helvetica8_181_bits[] = {
+0x05, 0x05, 0x05, 0x07, 0x01, 0x01};
+static unsigned char helvetica8_182_bits[] = {
+0x1e, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a};
+static unsigned char helvetica8_183_bits[] = {
+0x01, 0x01};
+static unsigned char helvetica8_184_bits[] = {
+0x02, 0x01};
+static unsigned char helvetica8_185_bits[] = {
+0x02, 0x03, 0x02, 0x02};
+static unsigned char helvetica8_186_bits[] = {
+0x07, 0x05, 0x07, 0x00, 0x07};
+static unsigned char helvetica8_187_bits[] = {
+0x05, 0x0a, 0x05};
+static unsigned char helvetica8_188_bits[] = {
+0x02, 0x23, 0x12, 0x2a, 0x34, 0x7a, 0x20};
+static unsigned char helvetica8_189_bits[] = {
+0x02, 0x23, 0x12, 0x3a, 0x44, 0x22, 0x70};
+static unsigned char helvetica8_190_bits[] = {
+0x07, 0x26, 0x14, 0x2b, 0x34, 0x7a, 0x20};
+static unsigned char helvetica8_191_bits[] = {
+0x04, 0x00, 0x04, 0x02, 0x09, 0x06};
+static unsigned char helvetica8_192_bits[] = {
+0x02, 0x04, 0x00, 0x04, 0x04, 0x0a, 0x0e, 0x11, 0x11};
+static unsigned char helvetica8_193_bits[] = {
+0x08, 0x04, 0x00, 0x04, 0x04, 0x0a, 0x0e, 0x11, 0x11};
+static unsigned char helvetica8_194_bits[] = {
+0x04, 0x0a, 0x00, 0x04, 0x04, 0x0a, 0x0e, 0x11, 0x11};
+static unsigned char helvetica8_195_bits[] = {
+0x14, 0x0a, 0x00, 0x04, 0x04, 0x0a, 0x0e, 0x11, 0x11};
+static unsigned char helvetica8_196_bits[] = {
+0x0a, 0x00, 0x04, 0x04, 0x0a, 0x0e, 0x11, 0x11};
+static unsigned char helvetica8_197_bits[] = {
+0x04, 0x0a, 0x04, 0x04, 0x04, 0x0a, 0x0e, 0x11, 0x11};
+static unsigned char helvetica8_198_bits[] = {
+0x7c, 0x0c, 0x0a, 0x3e, 0x09, 0x79};
+static unsigned char helvetica8_199_bits[] = {
+0x0e, 0x11, 0x11, 0x01, 0x11, 0x0e, 0x02, 0x01};
+static unsigned char helvetica8_200_bits[] = {
+0x02, 0x04, 0x00, 0x0f, 0x01, 0x07, 0x01, 0x01, 0x0f};
+static unsigned char helvetica8_201_bits[] = {
+0x04, 0x02, 0x00, 0x0f, 0x01, 0x07, 0x01, 0x01, 0x0f};
+static unsigned char helvetica8_202_bits[] = {
+0x02, 0x05, 0x00, 0x0f, 0x01, 0x07, 0x01, 0x01, 0x0f};
+static unsigned char helvetica8_203_bits[] = {
+0x05, 0x00, 0x0f, 0x01, 0x07, 0x01, 0x01, 0x0f};
+static unsigned char helvetica8_204_bits[] = {
+0x01, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_205_bits[] = {
+0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_206_bits[] = {
+0x02, 0x05, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_207_bits[] = {
+0x05, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_208_bits[] = {
+0x0e, 0x12, 0x17, 0x12, 0x12, 0x0e};
+static unsigned char helvetica8_209_bits[] = {
+0x14, 0x0a, 0x00, 0x11, 0x13, 0x15, 0x15, 0x19, 0x11};
+static unsigned char helvetica8_210_bits[] = {
+0x02, 0x04, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_211_bits[] = {
+0x08, 0x04, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_212_bits[] = {
+0x04, 0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_213_bits[] = {
+0x14, 0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_214_bits[] = {
+0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_215_bits[] = {
+0x09, 0x06, 0x06, 0x09};
+static unsigned char helvetica8_216_bits[] = {
+0x10, 0x1e, 0x11, 0x19, 0x15, 0x13, 0x0f, 0x01};
+static unsigned char helvetica8_217_bits[] = {
+0x02, 0x04, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_218_bits[] = {
+0x08, 0x04, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_219_bits[] = {
+0x04, 0x0a, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_220_bits[] = {
+0x0a, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char helvetica8_221_bits[] = {
+0x10, 0x08, 0x00, 0x13, 0x12, 0x12, 0x0c, 0x04, 0x04};
+static unsigned char helvetica8_222_bits[] = {
+0x01, 0x0f, 0x11, 0x11, 0x0f, 0x01};
+static unsigned char helvetica8_223_bits[] = {
+0x06, 0x09, 0x05, 0x09, 0x09, 0x05, 0x01};
+static unsigned char helvetica8_224_bits[] = {
+0x01, 0x02, 0x00, 0x03, 0x04, 0x07, 0x05, 0x0b};
+static unsigned char helvetica8_225_bits[] = {
+0x04, 0x02, 0x00, 0x03, 0x04, 0x07, 0x05, 0x0b};
+static unsigned char helvetica8_226_bits[] = {
+0x02, 0x05, 0x00, 0x03, 0x04, 0x07, 0x05, 0x0b};
+static unsigned char helvetica8_227_bits[] = {
+0x16, 0x0d, 0x00, 0x06, 0x08, 0x0e, 0x0a, 0x16};
+static unsigned char helvetica8_228_bits[] = {
+0x05, 0x00, 0x03, 0x04, 0x07, 0x05, 0x0b};
+static unsigned char helvetica8_229_bits[] = {
+0x02, 0x05, 0x02, 0x03, 0x04, 0x07, 0x05, 0x0b};
+static unsigned char helvetica8_230_bits[] = {
+0x0b, 0x14, 0x1f, 0x05, 0x1b};
+static unsigned char helvetica8_231_bits[] = {
+0x06, 0x01, 0x01, 0x01, 0x06, 0x02, 0x01};
+static unsigned char helvetica8_232_bits[] = {
+0x02, 0x04, 0x00, 0x02, 0x05, 0x07, 0x01, 0x06};
+static unsigned char helvetica8_233_bits[] = {
+0x04, 0x02, 0x00, 0x02, 0x05, 0x07, 0x01, 0x06};
+static unsigned char helvetica8_234_bits[] = {
+0x02, 0x05, 0x00, 0x02, 0x05, 0x07, 0x01, 0x06};
+static unsigned char helvetica8_235_bits[] = {
+0x05, 0x00, 0x02, 0x05, 0x07, 0x01, 0x06};
+static unsigned char helvetica8_236_bits[] = {
+0x01, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_237_bits[] = {
+0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char helvetica8_238_bits[] = {
+0x02, 0x05, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_239_bits[] = {
+0x05, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char helvetica8_240_bits[] = {
+0x09, 0x06, 0x05, 0x0e, 0x09, 0x09, 0x09, 0x06};
+static unsigned char helvetica8_241_bits[] = {
+0x0a, 0x05, 0x00, 0x07, 0x09, 0x09, 0x09, 0x09};
+static unsigned char helvetica8_242_bits[] = {
+0x02, 0x04, 0x00, 0x06, 0x09, 0x09, 0x09, 0x06};
+static unsigned char helvetica8_243_bits[] = {
+0x08, 0x04, 0x00, 0x06, 0x09, 0x09, 0x09, 0x06};
+static unsigned char helvetica8_244_bits[] = {
+0x02, 0x05, 0x00, 0x06, 0x09, 0x09, 0x09, 0x06};
+static unsigned char helvetica8_245_bits[] = {
+0x0a, 0x05, 0x00, 0x06, 0x09, 0x09, 0x09, 0x06};
+static unsigned char helvetica8_246_bits[] = {
+0x09, 0x00, 0x06, 0x09, 0x09, 0x09, 0x06};
+static unsigned char helvetica8_247_bits[] = {
+0x04, 0x00, 0x0f, 0x00, 0x04};
+static unsigned char helvetica8_248_bits[] = {
+0x40, 0x3c, 0x12, 0x1a, 0x16, 0x0e, 0x01};
+static unsigned char helvetica8_249_bits[] = {
+0x01, 0x02, 0x00, 0x05, 0x05, 0x05, 0x05, 0x06};
+static unsigned char helvetica8_250_bits[] = {
+0x04, 0x02, 0x00, 0x05, 0x05, 0x05, 0x05, 0x06};
+static unsigned char helvetica8_251_bits[] = {
+0x02, 0x05, 0x00, 0x05, 0x05, 0x05, 0x05, 0x06};
+static unsigned char helvetica8_252_bits[] = {
+0x05, 0x00, 0x05, 0x05, 0x05, 0x05, 0x06};
+static unsigned char helvetica8_253_bits[] = {
+0x08, 0x04, 0x00, 0x09, 0x09, 0x09, 0x06, 0x02, 0x01};
+static unsigned char helvetica8_254_bits[] = {
+0x01, 0x07, 0x09, 0x09, 0x09, 0x07, 0x01};
+static unsigned char helvetica8_255_bits[] = {
+0x0a, 0x00, 0x09, 0x09, 0x09, 0x06, 0x02, 0x01};
+static RotFont helvetica8font[] = {
+{5, 1, 1, helvetica8_0_bits},
+{5, 1, 1, helvetica8_1_bits},
+{5, 1, 1, helvetica8_2_bits},
+{5, 1, 1, helvetica8_3_bits},
+{5, 1, 1, helvetica8_4_bits},
+{5, 1, 1, helvetica8_5_bits},
+{5, 1, 1, helvetica8_6_bits},
+{5, 1, 1, helvetica8_7_bits},
+{5, 1, 1, helvetica8_8_bits},
+{5, 1, 1, helvetica8_9_bits},
+{5, 1, 1, helvetica8_10_bits},
+{5, 1, 1, helvetica8_11_bits},
+{5, 1, 1, helvetica8_12_bits},
+{5, 1, 1, helvetica8_13_bits},
+{5, 1, 1, helvetica8_14_bits},
+{5, 1, 1, helvetica8_15_bits},
+{5, 1, 1, helvetica8_16_bits},
+{5, 1, 1, helvetica8_17_bits},
+{5, 1, 1, helvetica8_18_bits},
+{5, 1, 1, helvetica8_19_bits},
+{5, 1, 1, helvetica8_20_bits},
+{5, 1, 1, helvetica8_21_bits},
+{5, 1, 1, helvetica8_22_bits},
+{5, 1, 1, helvetica8_23_bits},
+{5, 1, 1, helvetica8_24_bits},
+{5, 1, 1, helvetica8_25_bits},
+{5, 1, 1, helvetica8_26_bits},
+{5, 1, 1, helvetica8_27_bits},
+{5, 1, 1, helvetica8_28_bits},
+{5, 1, 1, helvetica8_29_bits},
+{5, 1, 1, helvetica8_30_bits},
+{5, 1, 1, helvetica8_31_bits},
+{4, 1, 1, helvetica8_32_bits},
+{1, 6, 6, helvetica8_33_bits},
+{3, 3, 6, helvetica8_34_bits},
+{5, 5, 5, helvetica8_35_bits},
+{4, 7, 6, helvetica8_36_bits},
+{6, 6, 6, helvetica8_37_bits},
+{5, 6, 6, helvetica8_38_bits},
+{1, 3, 6, helvetica8_39_bits},
+{2, 7, 6, helvetica8_40_bits},
+{2, 7, 6, helvetica8_41_bits},
+{3, 3, 5, helvetica8_42_bits},
+{5, 5, 5, helvetica8_43_bits},
+{2, 3, 1, helvetica8_44_bits},
+{4, 1, 3, helvetica8_45_bits},
+{1, 1, 1, helvetica8_46_bits},
+{2, 7, 6, helvetica8_47_bits},
+{4, 6, 6, helvetica8_48_bits},
+{2, 6, 6, helvetica8_49_bits},
+{4, 6, 6, helvetica8_50_bits},
+{3, 6, 6, helvetica8_51_bits},
+{4, 6, 6, helvetica8_52_bits},
+{3, 6, 6, helvetica8_53_bits},
+{4, 6, 6, helvetica8_54_bits},
+{4, 6, 6, helvetica8_55_bits},
+{4, 6, 6, helvetica8_56_bits},
+{4, 6, 6, helvetica8_57_bits},
+{1, 4, 4, helvetica8_58_bits},
+{2, 6, 4, helvetica8_59_bits},
+{3, 5, 5, helvetica8_60_bits},
+{3, 3, 4, helvetica8_61_bits},
+{3, 5, 5, helvetica8_62_bits},
+{3, 5, 5, helvetica8_63_bits},
+{8, 7, 6, helvetica8_64_bits},
+{5, 6, 6, helvetica8_65_bits},
+{4, 6, 6, helvetica8_66_bits},
+{5, 6, 6, helvetica8_67_bits},
+{5, 6, 6, helvetica8_68_bits},
+{4, 6, 6, helvetica8_69_bits},
+{4, 6, 6, helvetica8_70_bits},
+{5, 6, 6, helvetica8_71_bits},
+{5, 6, 6, helvetica8_72_bits},
+{1, 6, 6, helvetica8_73_bits},
+{3, 6, 6, helvetica8_74_bits},
+{4, 6, 6, helvetica8_75_bits},
+{3, 6, 6, helvetica8_76_bits},
+{5, 6, 6, helvetica8_77_bits},
+{5, 6, 6, helvetica8_78_bits},
+{5, 6, 6, helvetica8_79_bits},
+{4, 6, 6, helvetica8_80_bits},
+{5, 8, 6, helvetica8_81_bits},
+{4, 6, 6, helvetica8_82_bits},
+{4, 6, 6, helvetica8_83_bits},
+{3, 6, 6, helvetica8_84_bits},
+{5, 6, 6, helvetica8_85_bits},
+{4, 6, 6, helvetica8_86_bits},
+{7, 6, 6, helvetica8_87_bits},
+{4, 6, 6, helvetica8_88_bits},
+{5, 6, 6, helvetica8_89_bits},
+{4, 6, 6, helvetica8_90_bits},
+{2, 7, 6, helvetica8_91_bits},
+{2, 7, 6, helvetica8_92_bits},
+{2, 7, 6, helvetica8_93_bits},
+{5, 3, 5, helvetica8_94_bits},
+{5, 1, 0, helvetica8_95_bits},
+{1, 3, 6, helvetica8_96_bits},
+{4, 5, 5, helvetica8_97_bits},
+{4, 7, 7, helvetica8_98_bits},
+{3, 5, 5, helvetica8_99_bits},
+{4, 7, 7, helvetica8_100_bits},
+{3, 5, 5, helvetica8_101_bits},
+{3, 7, 7, helvetica8_102_bits},
+{4, 6, 5, helvetica8_103_bits},
+{4, 7, 7, helvetica8_104_bits},
+{1, 7, 7, helvetica8_105_bits},
+{2, 9, 7, helvetica8_106_bits},
+{3, 7, 7, helvetica8_107_bits},
+{1, 7, 7, helvetica8_108_bits},
+{5, 5, 5, helvetica8_109_bits},
+{4, 5, 5, helvetica8_110_bits},
+{4, 5, 5, helvetica8_111_bits},
+{4, 6, 5, helvetica8_112_bits},
+{4, 6, 5, helvetica8_113_bits},
+{3, 5, 5, helvetica8_114_bits},
+{3, 5, 5, helvetica8_115_bits},
+{3, 7, 7, helvetica8_116_bits},
+{3, 5, 5, helvetica8_117_bits},
+{4, 5, 5, helvetica8_118_bits},
+{5, 5, 5, helvetica8_119_bits},
+{4, 5, 5, helvetica8_120_bits},
+{4, 6, 5, helvetica8_121_bits},
+{3, 5, 5, helvetica8_122_bits},
+{3, 7, 6, helvetica8_123_bits},
+{1, 7, 6, helvetica8_124_bits},
+{3, 7, 6, helvetica8_125_bits},
+{5, 2, 4, helvetica8_126_bits},
+{5, 1, 1, helvetica8_127_bits},
+{5, 1, 1, helvetica8_128_bits},
+{5, 1, 1, helvetica8_129_bits},
+{5, 1, 1, helvetica8_130_bits},
+{5, 1, 1, helvetica8_131_bits},
+{5, 1, 1, helvetica8_132_bits},
+{5, 1, 1, helvetica8_133_bits},
+{5, 1, 1, helvetica8_134_bits},
+{5, 1, 1, helvetica8_135_bits},
+{5, 1, 1, helvetica8_136_bits},
+{5, 1, 1, helvetica8_137_bits},
+{5, 1, 1, helvetica8_138_bits},
+{5, 1, 1, helvetica8_139_bits},
+{5, 1, 1, helvetica8_140_bits},
+{5, 1, 1, helvetica8_141_bits},
+{5, 1, 1, helvetica8_142_bits},
+{5, 1, 1, helvetica8_143_bits},
+{5, 1, 1, helvetica8_144_bits},
+{5, 1, 1, helvetica8_145_bits},
+{5, 1, 1, helvetica8_146_bits},
+{5, 1, 1, helvetica8_147_bits},
+{5, 1, 1, helvetica8_148_bits},
+{5, 1, 1, helvetica8_149_bits},
+{5, 1, 1, helvetica8_150_bits},
+{5, 1, 1, helvetica8_151_bits},
+{5, 1, 1, helvetica8_152_bits},
+{5, 1, 1, helvetica8_153_bits},
+{5, 1, 1, helvetica8_154_bits},
+{5, 1, 1, helvetica8_155_bits},
+{5, 1, 1, helvetica8_156_bits},
+{5, 1, 1, helvetica8_157_bits},
+{5, 1, 1, helvetica8_158_bits},
+{5, 1, 1, helvetica8_159_bits},
+{1, 1, 1, helvetica8_160_bits},
+{1, 7, 5, helvetica8_161_bits},
+{3, 7, 6, helvetica8_162_bits},
+{4, 6, 6, helvetica8_163_bits},
+{5, 5, 5, helvetica8_164_bits},
+{5, 5, 5, helvetica8_165_bits},
+{1, 7, 6, helvetica8_166_bits},
+{4, 8, 6, helvetica8_167_bits},
+{4, 1, 6, helvetica8_168_bits},
+{6, 7, 6, helvetica8_169_bits},
+{3, 5, 6, helvetica8_170_bits},
+{4, 3, 4, helvetica8_171_bits},
+{4, 3, 4, helvetica8_172_bits},
+{2, 1, 3, helvetica8_173_bits},
+{6, 7, 6, helvetica8_174_bits},
+{3, 1, 6, helvetica8_175_bits},
+{3, 3, 6, helvetica8_176_bits},
+{4, 5, 5, helvetica8_177_bits},
+{2, 4, 6, helvetica8_178_bits},
+{3, 4, 6, helvetica8_179_bits},
+{2, 2, 6, helvetica8_180_bits},
+{3, 6, 4, helvetica8_181_bits},
+{5, 8, 6, helvetica8_182_bits},
+{1, 2, 3, helvetica8_183_bits},
+{2, 2, 0, helvetica8_184_bits},
+{2, 4, 6, helvetica8_185_bits},
+{3, 5, 6, helvetica8_186_bits},
+{4, 3, 4, helvetica8_187_bits},
+{7, 7, 6, helvetica8_188_bits},
+{7, 7, 6, helvetica8_189_bits},
+{7, 7, 6, helvetica8_190_bits},
+{4, 6, 5, helvetica8_191_bits},
+{5, 9, 9, helvetica8_192_bits},
+{5, 9, 9, helvetica8_193_bits},
+{5, 9, 9, helvetica8_194_bits},
+{5, 9, 9, helvetica8_195_bits},
+{5, 8, 8, helvetica8_196_bits},
+{5, 9, 9, helvetica8_197_bits},
+{7, 6, 6, helvetica8_198_bits},
+{5, 8, 6, helvetica8_199_bits},
+{4, 9, 9, helvetica8_200_bits},
+{4, 9, 9, helvetica8_201_bits},
+{4, 9, 9, helvetica8_202_bits},
+{4, 8, 8, helvetica8_203_bits},
+{2, 9, 9, helvetica8_204_bits},
+{2, 9, 9, helvetica8_205_bits},
+{3, 9, 9, helvetica8_206_bits},
+{3, 8, 8, helvetica8_207_bits},
+{5, 6, 6, helvetica8_208_bits},
+{5, 9, 9, helvetica8_209_bits},
+{5, 9, 9, helvetica8_210_bits},
+{5, 9, 9, helvetica8_211_bits},
+{5, 9, 9, helvetica8_212_bits},
+{5, 9, 9, helvetica8_213_bits},
+{5, 8, 8, helvetica8_214_bits},
+{4, 4, 4, helvetica8_215_bits},
+{5, 8, 7, helvetica8_216_bits},
+{5, 9, 9, helvetica8_217_bits},
+{5, 9, 9, helvetica8_218_bits},
+{5, 9, 9, helvetica8_219_bits},
+{5, 8, 8, helvetica8_220_bits},
+{5, 9, 9, helvetica8_221_bits},
+{5, 6, 6, helvetica8_222_bits},
+{4, 7, 6, helvetica8_223_bits},
+{4, 8, 8, helvetica8_224_bits},
+{4, 8, 8, helvetica8_225_bits},
+{4, 8, 8, helvetica8_226_bits},
+{5, 8, 8, helvetica8_227_bits},
+{4, 7, 7, helvetica8_228_bits},
+{4, 8, 8, helvetica8_229_bits},
+{5, 5, 5, helvetica8_230_bits},
+{3, 7, 5, helvetica8_231_bits},
+{3, 8, 8, helvetica8_232_bits},
+{3, 8, 8, helvetica8_233_bits},
+{3, 8, 8, helvetica8_234_bits},
+{3, 7, 7, helvetica8_235_bits},
+{2, 8, 8, helvetica8_236_bits},
+{2, 8, 8, helvetica8_237_bits},
+{3, 8, 8, helvetica8_238_bits},
+{3, 7, 7, helvetica8_239_bits},
+{4, 8, 8, helvetica8_240_bits},
+{4, 8, 8, helvetica8_241_bits},
+{4, 8, 8, helvetica8_242_bits},
+{4, 8, 8, helvetica8_243_bits},
+{4, 8, 8, helvetica8_244_bits},
+{4, 8, 8, helvetica8_245_bits},
+{4, 7, 7, helvetica8_246_bits},
+{4, 5, 5, helvetica8_247_bits},
+{7, 7, 6, helvetica8_248_bits},
+{3, 8, 8, helvetica8_249_bits},
+{3, 8, 8, helvetica8_250_bits},
+{3, 8, 8, helvetica8_251_bits},
+{3, 7, 7, helvetica8_252_bits},
+{4, 9, 8, helvetica8_253_bits},
+{4, 7, 6, helvetica8_254_bits},
+{4, 8, 7, helvetica8_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol12.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol12.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol12.bdf	(revision 22322)
@@ -0,0 +1,3022 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Symbol-Medium-R-Normal--12-120-75-75-P-74-Adobe-FontSpecific
+SIZE 12 75 75
+FONTBOUNDINGBOX 14 16 -1 -4
+STARTPROPERTIES 31
+FOUNDRY "Adobe"
+FAMILY_NAME "Symbol"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 12
+POINT_SIZE 120
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 74
+CHARSET_REGISTRY "Adobe"
+CHARSET_ENCODING "FontSpecific"
+CAP_HEIGHT 9
+X_HEIGHT 6
+FACE_NAME "Symbol"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "No mark"
+_DEC_DEVICE_FONTNAMES "PS=Symbol"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2-1, 18-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+FULL_NAME "Symbol"
+FONT "-Adobe-Symbol-Medium-R-Normal--12-120-75-75-P-74-Adobe-FontSpecific"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 8
+DEFAULT_CHAR 32
+FONT_ASCENT 11
+FONT_DESCENT 3
+ENDPROPERTIES
+CHARS 188
+STARTCHAR space
+ENCODING 32
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 1 9 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+00
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+8080
+4100
+4100
+3e00
+2200
+1400
+1400
+0800
+0800
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 0 0
+BITMAP
+28
+28
+28
+fc
+50
+f8
+50
+50
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+f8
+08
+08
+08
+f8
+08
+08
+08
+f8
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+7900
+9700
+9200
+6400
+0800
+1300
+1480
+2480
+6300
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 1 0
+BITMAP
+3000
+4800
+4800
+3700
+7200
+9a00
+8c00
+8c80
+7300
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 6 1 0
+BITMAP
+e0
+10
+f0
+10
+10
+e0
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 12 1 -3
+BITMAP
+20
+40
+40
+80
+80
+80
+80
+80
+80
+40
+40
+20
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 12 0 -3
+BITMAP
+80
+40
+40
+20
+20
+20
+20
+20
+20
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 5 0 2
+BITMAP
+20
+a8
+70
+a8
+20
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+20
+20
+f8
+20
+20
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 3 0 -2
+BITMAP
+40
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 1 1 3
+BITMAP
+f8
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 1 1 0
+BITMAP
+80
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 9 0 0
+BITMAP
+20
+20
+20
+40
+40
+40
+80
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+88
+88
+88
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 3 9 1 0
+BITMAP
+40
+c0
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+88
+88
+08
+10
+20
+40
+88
+f8
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+88
+88
+08
+30
+08
+08
+88
+70
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 0
+BITMAP
+18
+28
+28
+48
+48
+88
+fc
+08
+08
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+78
+40
+80
+f0
+18
+08
+08
+88
+f0
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+38
+40
+80
+b0
+c8
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+f8
+88
+10
+10
+20
+20
+40
+40
+40
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+88
+88
+c8
+70
+98
+88
+88
+70
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+88
+88
+88
+78
+10
+10
+20
+40
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 6 2 0
+BITMAP
+80
+00
+00
+00
+00
+80
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 8 0 -2
+BITMAP
+40
+00
+00
+00
+00
+40
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 0 1
+BITMAP
+0c
+30
+c0
+30
+0c
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 3 1 2
+BITMAP
+f8
+00
+f8
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 0 1
+BITMAP
+c0
+30
+0c
+30
+c0
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+88
+88
+08
+10
+20
+20
+00
+20
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 6 1 0
+BITMAP
+68
+b0
+00
+f8
+00
+f8
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+0800
+1c00
+1400
+1400
+2200
+3e00
+2200
+4100
+e380
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+fc
+46
+42
+46
+7c
+46
+42
+46
+fc
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+e7
+42
+24
+18
+18
+24
+24
+42
+e7
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+10
+10
+28
+28
+44
+44
+44
+82
+fe
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+fe
+42
+40
+44
+7c
+44
+40
+42
+fe
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+1c00
+0800
+7f00
+c980
+8880
+c980
+7f00
+0800
+1c00
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+fe
+42
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+e7
+42
+42
+42
+7e
+42
+42
+42
+e7
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 9 0 0
+BITMAP
+e0
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 8 9 0 0
+BITMAP
+04
+0a
+0a
+66
+a3
+22
+22
+26
+1c
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+ee
+44
+48
+50
+70
+48
+44
+42
+e7
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+0800
+0800
+1400
+1400
+2200
+2200
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 11 9 0 0
+BITMAP
+e0e0
+60c0
+5140
+5140
+5140
+4a40
+4a40
+4440
+e4e0
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+e7
+62
+52
+52
+4a
+4a
+46
+46
+e2
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+3c
+66
+42
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+ff
+42
+42
+42
+42
+42
+42
+42
+e7
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+3c
+66
+42
+a5
+bd
+a5
+42
+66
+3c
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 0 0
+BITMAP
+f8
+4c
+44
+4c
+78
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+BITMAP
+fc
+84
+c0
+60
+30
+20
+40
+82
+fe
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+fe
+92
+92
+10
+10
+10
+10
+10
+38
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+e380
+4100
+2200
+1400
+0800
+0800
+0800
+0800
+1c00
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 -3
+BITMAP
+78
+80
+80
+80
+c0
+70
+08
+48
+70
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 10 9 0 0
+BITMAP
+1e00
+3300
+2100
+4080
+4080
+4080
+2100
+9240
+f3c0
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 8 9 0 0
+BITMAP
+7e
+42
+00
+24
+3c
+24
+00
+81
+ff
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+dd80
+4900
+4900
+6b00
+3e00
+0800
+0800
+0800
+1c00
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+fc
+84
+08
+10
+20
+20
+40
+84
+fc
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 12 1 -3
+BITMAP
+e0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+e0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 6 1 0
+BITMAP
+10
+10
+00
+00
+82
+82
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 12 0 -3
+BITMAP
+e0
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+e0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+10
+10
+10
+10
+10
+10
+10
+10
+fe
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 1 0 -3
+BITMAP
+fc
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 7 1 0 11
+BITMAP
+fe
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 6 1 0
+BITMAP
+74
+d4
+88
+88
+d4
+66
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 13 1 -3
+BITMAP
+70
+88
+88
+88
+b0
+88
+88
+88
+c8
+b0
+80
+80
+80
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 -3
+BITMAP
+c6
+ac
+28
+18
+30
+28
+68
+4a
+c6
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+70
+98
+80
+40
+70
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 6 1 0
+BITMAP
+70
+80
+e0
+80
+90
+70
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 11 0 -3
+BITMAP
+10
+10
+38
+54
+92
+92
+54
+38
+10
+10
+10
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 0 -3
+BITMAP
+c4
+a4
+24
+24
+28
+18
+10
+30
+20
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 -3
+BITMAP
+6c
+b2
+22
+22
+22
+22
+02
+02
+02
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 6 0 0
+BITMAP
+c0
+40
+40
+40
+40
+60
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 -3
+BITMAP
+18
+54
+92
+92
+54
+38
+10
+10
+10
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 6 0 0
+BITMAP
+4c
+d4
+60
+70
+58
+4c
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+BITMAP
+60
+50
+10
+30
+28
+48
+48
+8a
+86
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 1 -3
+BITMAP
+88
+88
+88
+88
+9a
+ec
+80
+80
+c0
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 6 0 0
+BITMAP
+cc
+44
+44
+28
+28
+30
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 6 1 0
+BITMAP
+70
+88
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 6 0 0
+BITMAP
+7e
+a4
+24
+24
+26
+64
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 0
+BITMAP
+70
+88
+88
+88
+f8
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 9 1 -3
+BITMAP
+70
+88
+88
+88
+c8
+b0
+80
+80
+80
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 6 1 0
+BITMAP
+7c
+90
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+78
+a0
+20
+20
+28
+30
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 6 0 0
+BITMAP
+64
+a2
+22
+22
+26
+1c
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+7f
+a2
+49
+49
+49
+49
+36
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 6 1 0
+BITMAP
+44
+92
+92
+92
+92
+6c
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 14 1 -3
+BITMAP
+40
+80
+70
+c0
+80
+f0
+40
+80
+80
+c0
+70
+08
+48
+70
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 -3
+BITMAP
+8880
+4900
+4900
+4900
+6b00
+3e00
+0800
+0800
+0800
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 13 1 -3
+BITMAP
+40
+98
+70
+40
+80
+80
+80
+80
+c0
+70
+08
+48
+70
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 12 0 -3
+BITMAP
+18
+20
+20
+20
+40
+80
+40
+20
+20
+20
+20
+18
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 12 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 12 0 -3
+BITMAP
+c0
+20
+20
+20
+10
+08
+10
+20
+20
+20
+20
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 3 0 2
+BITMAP
+62
+92
+8c
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 8 9 0 0
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 3 1 6
+BITMAP
+00
+00
+00
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 0 0
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 5 9 -1 0
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 4 0 1
+BITMAP
+0000
+0000
+0000
+c600
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 12 0 -3
+BITMAP
+28
+10
+10
+10
+10
+10
+38
+20
+40
+80
+0c
+30
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 7 1 -1
+BITMAP
+c0
+30
+0c
+00
+fc
+08
+10
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 6 1 0
+BITMAP
+10
+20
+20
+40
+40
+80
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 6 1 0
+BITMAP
+80
+76
+00
+98
+80
+8c
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 7 1 -1
+BITMAP
+80
+76
+00
+0c
+14
+10
+7c
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 1040 0
+DWIDTH 13 0
+BBX 11 5 1 1
+BITMAP
+1000
+1020
+2020
+a0c0
+3820
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 960 0
+DWIDTH 12 0
+BBX 12 5 0 1
+BITMAP
+d6f0
+d610
+1020
+70f0
+f870
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 16 1 -4
+BITMAP
+20
+68
+f8
+f8
+78
+38
+10
+10
+38
+78
+f8
+d0
+10
+10
+20
+80
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 960 0
+DWIDTH 12 0
+BBX 12 5 0 1
+BITMAP
+4040
+ffe0
+4040
+2080
+2000
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 15 1 -3
+BITMAP
+40
+00
+f8
+f0
+40
+00
+20
+00
+20
+70
+a8
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 4 0 5
+BITMAP
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+20
+20
+20
+20
+20
+00
+40
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 5 3 0 6
+BITMAP
+00
+20
+f8
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 0 0
+BITMAP
+f0
+00
+20
+00
+40
+20
+20
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+20
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 4 0 1
+BITMAP
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 10 0 0
+BITMAP
+20
+a8
+70
+20
+60
+90
+90
+60
+20
+20
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 4 1 1
+BITMAP
+f0
+20
+20
+00
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+f8
+28
+50
+a0
+c0
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+30
+08
+30
+c0
+00
+f8
+88
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+50
+20
+50
+88
+70
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 0 1
+BITMAP
+88
+88
+74
+60
+90
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 9 1 1 0
+BITMAP
+0800
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 1 16 3 -4
+BITMAP
+00
+80
+80
+80
+80
+00
+00
+80
+80
+00
+00
+00
+80
+00
+00
+00
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 1040 0
+DWIDTH 13 0
+BBX 13 1 0 3
+BITMAP
+10f8
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 8 0 0
+BITMAP
+20
+f8
+40
+80
+f8
+00
+f8
+00
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 7 9 1 0
+BITMAP
+f8
+64
+98
+00
+64
+98
+88
+80
+80
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 11 0 -1
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 10 0 0
+BITMAP
+8080
+8080
+ff80
+0200
+0200
+0200
+fe00
+8880
+6600
+d480
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 11 1 -3
+BITMAP
+4c
+66
+e2
+3f
+61
+42
+64
+0c
+06
+03
+01
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+6180
+1c00
+0080
+0080
+0080
+0000
+0000
+0000
+0000
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+0000
+0080
+8000
+2600
+9180
+ab80
+6480
+9000
+1c00
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+6300
+6300
+9480
+8880
+9480
+6300
+6300
+1c00
+1c00
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 6 1 0
+BITMAP
+63
+00
+49
+00
+88
+80
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 6 1 0
+BITMAP
+be
+80
+88
+80
+49
+00
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 6 1 0
+BITMAP
+63
+00
+1c
+00
+1c
+80
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 8 1 -2
+BITMAP
+63
+00
+43
+00
+84
+80
+88
+80
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 8 0 -1
+BITMAP
+90
+80
+61
+00
+63
+00
+9c
+00
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 6 0 0
+BITMAP
+3c
+42
+81
+81
+81
+81
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 8 0 -2
+BITMAP
+81
+81
+81
+81
+42
+3c
+fc
+02
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 6 1 0
+BITMAP
+00
+00
+00
+fc
+fc
+00
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 8 1 -1
+BITMAP
+00
+00
+00
+fc
+00
+fc
+00
+3c
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+4480
+8800
+3f00
+3f00
+8080
+4000
+3f00
+8080
+4000
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+00
+ff
+7c
+80
+fc
+80
+80
+7c
+04
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+7c
+88
+fc
+90
+a0
+7c
+40
+00
+80
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+01
+00
+02
+00
+04
+00
+08
+00
+10
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 11 6 0 3
+BITMAP
+0020
+0040
+00e0
+80e0
+8140
+4220
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 11 0 -1
+BITMAP
+2400
+1800
+3c00
+f980
+b980
+ed00
+3c00
+4280
+a980
+a580
+4200
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 12 0 0
+BITMAP
+fa
+80
+aa
+80
+24
+40
+24
+40
+24
+40
+76
+60
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 1 1 3
+BITMAP
+c0
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 7 4 1 0
+BITMAP
+80
+40
+00
+40
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 6 1 0
+BITMAP
+00
+40
+00
+40
+00
+40
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 6 1 0
+BITMAP
+00
+40
+00
+40
+00
+40
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 1040 0
+DWIDTH 13 0
+BBX 11 5 1 1
+BITMAP
+0040
+00e0
+8000
+0200
+0400
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 960 0
+DWIDTH 12 0
+BBX 11 5 0 1
+BITMAP
+0460
+a820
+2800
+10c0
+fe00
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 5 12 1 -1
+BITMAP
+00
+00
+30
+30
+48
+48
+80
+80
+80
+80
+48
+48
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 960 0
+DWIDTH 12 0
+BBX 11 5 1 1
+BITMAP
+3020
+2080
+7fc0
+c060
+7fc0
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 5 12 1 0
+BITMAP
+20
+80
+20
+00
+78
+e0
+c0
+00
+78
+e0
+20
+00
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 0
+BITMAP
+20
+70
+d8
+50
+50
+50
+50
+50
+50
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 11 0 -1
+BITMAP
+50
+50
+50
+00
+80
+f0
+c0
+00
+60
+f0
+c0
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+00
+80
+50
+50
+50
+50
+50
+50
+50
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+50
+50
+d8
+70
+20
+30
+30
+48
+84
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 10 6 0 3
+BITMAP
+8440
+4800
+3000
+2000
+4040
+8040
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 11 0 -1
+BITMAP
+40
+20
+20
+10
+3c
+42
+b9
+a5
+b9
+a5
+a5
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 16 1 -4
+BITMAP
+40
+30
+30
+40
+90
+a0
+a0
+a0
+90
+40
+30
+f0
+80
+20
+80
+20
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 1 16 1 -4
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+80
+80
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 16 1 -4
+BITMAP
+80
+f0
+10
+20
+20
+40
+40
+40
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 16 1 -4
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 1 16 1 -4
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 15 1 -3
+BITMAP
+20
+00
+e0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 3 16 2 -4
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 3 16 0 -4
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 3 15 2 -3
+BITMAP
+80
+e0
+60
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 1 16 2 -4
+BITMAP
+80
+80
+80
+00
+00
+00
+00
+00
+00
+00
+80
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 12 0 -2
+BITMAP
+20
+20
+20
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 5 15 0 -3
+BITMAP
+80
+80
+80
+80
+80
+60
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 5 16 4 -4
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+00
+80
+40
+40
+20
+20
+10
+10
+20
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 1 16 1 -4
+BITMAP
+00
+00
+00
+80
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 5 15 0 -3
+BITMAP
+20
+a0
+c0
+38
+58
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 16 0 -4
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 1 16 3 -4
+BITMAP
+80
+80
+80
+80
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 16 0 -4
+BITMAP
+00
+d0
+e0
+80
+40
+40
+20
+20
+20
+10
+10
+10
+10
+10
+10
+10
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 16 1 -4
+BITMAP
+00
+00
+00
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 1 16 3 -4
+BITMAP
+80
+80
+80
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 15 1 -3
+BITMAP
+40
+40
+80
+e0
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 3 16 0 -4
+BITMAP
+20
+20
+20
+20
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 3 16 2 -4
+BITMAP
+80
+80
+80
+80
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 3 15 0 -3
+BITMAP
+20
+20
+e0
+c0
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol12.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol12.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol12.h	(revision 22322)
@@ -0,0 +1,815 @@
+static unsigned char symbol12_0_bits[] = {
+0x00};
+static unsigned char symbol12_1_bits[] = {
+0x00};
+static unsigned char symbol12_2_bits[] = {
+0x00};
+static unsigned char symbol12_3_bits[] = {
+0x00};
+static unsigned char symbol12_4_bits[] = {
+0x00};
+static unsigned char symbol12_5_bits[] = {
+0x00};
+static unsigned char symbol12_6_bits[] = {
+0x00};
+static unsigned char symbol12_7_bits[] = {
+0x00};
+static unsigned char symbol12_8_bits[] = {
+0x00};
+static unsigned char symbol12_9_bits[] = {
+0x00};
+static unsigned char symbol12_10_bits[] = {
+0x00};
+static unsigned char symbol12_11_bits[] = {
+0x00};
+static unsigned char symbol12_12_bits[] = {
+0x00};
+static unsigned char symbol12_13_bits[] = {
+0x00};
+static unsigned char symbol12_14_bits[] = {
+0x00};
+static unsigned char symbol12_15_bits[] = {
+0x00};
+static unsigned char symbol12_16_bits[] = {
+0x00};
+static unsigned char symbol12_17_bits[] = {
+0x00};
+static unsigned char symbol12_18_bits[] = {
+0x00};
+static unsigned char symbol12_19_bits[] = {
+0x00};
+static unsigned char symbol12_20_bits[] = {
+0x00};
+static unsigned char symbol12_21_bits[] = {
+0x00};
+static unsigned char symbol12_22_bits[] = {
+0x00};
+static unsigned char symbol12_23_bits[] = {
+0x00};
+static unsigned char symbol12_24_bits[] = {
+0x00};
+static unsigned char symbol12_25_bits[] = {
+0x00};
+static unsigned char symbol12_26_bits[] = {
+0x00};
+static unsigned char symbol12_27_bits[] = {
+0x00};
+static unsigned char symbol12_28_bits[] = {
+0x00};
+static unsigned char symbol12_29_bits[] = {
+0x00};
+static unsigned char symbol12_30_bits[] = {
+0x00};
+static unsigned char symbol12_31_bits[] = {
+0x00};
+static unsigned char symbol12_32_bits[] = {
+0x00};
+static unsigned char symbol12_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01};
+static unsigned char symbol12_34_bits[] = {
+0x01, 0x01, 0x82, 0x00, 0x82, 0x00, 0x7c, 0x00, 0x44, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char symbol12_35_bits[] = {
+0x14, 0x14, 0x14, 0x3f, 0x0a, 0x1f, 0x0a, 0x0a, 0x0a};
+static unsigned char symbol12_36_bits[] = {
+0x1f, 0x10, 0x10, 0x10, 0x1f, 0x10, 0x10, 0x10, 0x1f};
+static unsigned char symbol12_37_bits[] = {
+0x9e, 0x00, 0xe9, 0x00, 0x49, 0x00, 0x26, 0x00, 0x10, 0x00, 0xc8, 0x00, 
+0x28, 0x01, 0x24, 0x01, 0xc6, 0x00};
+static unsigned char symbol12_38_bits[] = {
+0x0c, 0x00, 0x12, 0x00, 0x12, 0x00, 0xec, 0x00, 0x4e, 0x00, 0x59, 0x00, 
+0x31, 0x00, 0x31, 0x01, 0xce, 0x00};
+static unsigned char symbol12_39_bits[] = {
+0x07, 0x08, 0x0f, 0x08, 0x08, 0x07};
+static unsigned char symbol12_40_bits[] = {
+0x04, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x04};
+static unsigned char symbol12_41_bits[] = {
+0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x01};
+static unsigned char symbol12_42_bits[] = {
+0x04, 0x15, 0x0e, 0x15, 0x04};
+static unsigned char symbol12_43_bits[] = {
+0x04, 0x04, 0x1f, 0x04, 0x04};
+static unsigned char symbol12_44_bits[] = {
+0x02, 0x02, 0x01};
+static unsigned char symbol12_45_bits[] = {
+0x1f};
+static unsigned char symbol12_46_bits[] = {
+0x01};
+static unsigned char symbol12_47_bits[] = {
+0x04, 0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01};
+static unsigned char symbol12_48_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char symbol12_49_bits[] = {
+0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char symbol12_50_bits[] = {
+0x0e, 0x11, 0x11, 0x10, 0x08, 0x04, 0x02, 0x11, 0x1f};
+static unsigned char symbol12_51_bits[] = {
+0x0e, 0x11, 0x11, 0x10, 0x0c, 0x10, 0x10, 0x11, 0x0e};
+static unsigned char symbol12_52_bits[] = {
+0x18, 0x14, 0x14, 0x12, 0x12, 0x11, 0x3f, 0x10, 0x10};
+static unsigned char symbol12_53_bits[] = {
+0x1e, 0x02, 0x01, 0x0f, 0x18, 0x10, 0x10, 0x11, 0x0f};
+static unsigned char symbol12_54_bits[] = {
+0x1c, 0x02, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char symbol12_55_bits[] = {
+0x1f, 0x11, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x02};
+static unsigned char symbol12_56_bits[] = {
+0x0e, 0x11, 0x11, 0x13, 0x0e, 0x19, 0x11, 0x11, 0x0e};
+static unsigned char symbol12_57_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x1e, 0x08, 0x08, 0x04, 0x02};
+static unsigned char symbol12_58_bits[] = {
+0x01, 0x00, 0x00, 0x00, 0x00, 0x01};
+static unsigned char symbol12_59_bits[] = {
+0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01};
+static unsigned char symbol12_60_bits[] = {
+0x30, 0x0c, 0x03, 0x0c, 0x30};
+static unsigned char symbol12_61_bits[] = {
+0x1f, 0x00, 0x1f};
+static unsigned char symbol12_62_bits[] = {
+0x03, 0x0c, 0x30, 0x0c, 0x03};
+static unsigned char symbol12_63_bits[] = {
+0x0e, 0x11, 0x11, 0x10, 0x08, 0x04, 0x04, 0x00, 0x04};
+static unsigned char symbol12_64_bits[] = {
+0x16, 0x0d, 0x00, 0x1f, 0x00, 0x1f};
+static unsigned char symbol12_65_bits[] = {
+0x10, 0x00, 0x38, 0x00, 0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 
+0x44, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char symbol12_66_bits[] = {
+0x3f, 0x62, 0x42, 0x62, 0x3e, 0x62, 0x42, 0x62, 0x3f};
+static unsigned char symbol12_67_bits[] = {
+0xe7, 0x42, 0x24, 0x18, 0x18, 0x24, 0x24, 0x42, 0xe7};
+static unsigned char symbol12_68_bits[] = {
+0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x22, 0x41, 0x7f};
+static unsigned char symbol12_69_bits[] = {
+0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 0x7f};
+static unsigned char symbol12_70_bits[] = {
+0x38, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x93, 0x01, 0x11, 0x01, 0x93, 0x01, 
+0xfe, 0x00, 0x10, 0x00, 0x38, 0x00};
+static unsigned char symbol12_71_bits[] = {
+0x7f, 0x42, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char symbol12_72_bits[] = {
+0xe7, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0xe7};
+static unsigned char symbol12_73_bits[] = {
+0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char symbol12_74_bits[] = {
+0x20, 0x50, 0x50, 0x66, 0xc5, 0x44, 0x44, 0x64, 0x38};
+static unsigned char symbol12_75_bits[] = {
+0x77, 0x22, 0x12, 0x0a, 0x0e, 0x12, 0x22, 0x42, 0xe7};
+static unsigned char symbol12_76_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char symbol12_77_bits[] = {
+0x07, 0x07, 0x06, 0x03, 0x8a, 0x02, 0x8a, 0x02, 0x8a, 0x02, 0x52, 0x02, 
+0x52, 0x02, 0x22, 0x02, 0x27, 0x07};
+static unsigned char symbol12_78_bits[] = {
+0xe7, 0x46, 0x4a, 0x4a, 0x52, 0x52, 0x62, 0x62, 0x47};
+static unsigned char symbol12_79_bits[] = {
+0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c};
+static unsigned char symbol12_80_bits[] = {
+0xff, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0xe7};
+static unsigned char symbol12_81_bits[] = {
+0x3c, 0x66, 0x42, 0xa5, 0xbd, 0xa5, 0x42, 0x66, 0x3c};
+static unsigned char symbol12_82_bits[] = {
+0x1f, 0x32, 0x22, 0x32, 0x1e, 0x02, 0x02, 0x02, 0x07};
+static unsigned char symbol12_83_bits[] = {
+0x3f, 0x21, 0x03, 0x06, 0x0c, 0x04, 0x02, 0x41, 0x7f};
+static unsigned char symbol12_84_bits[] = {
+0x7f, 0x49, 0x49, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c};
+static unsigned char symbol12_85_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x38, 0x00};
+static unsigned char symbol12_86_bits[] = {
+0x1e, 0x01, 0x01, 0x01, 0x03, 0x0e, 0x10, 0x12, 0x0e};
+static unsigned char symbol12_87_bits[] = {
+0x78, 0x00, 0xcc, 0x00, 0x84, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 
+0x84, 0x00, 0x49, 0x02, 0xcf, 0x03};
+static unsigned char symbol12_88_bits[] = {
+0x7e, 0x42, 0x00, 0x24, 0x3c, 0x24, 0x00, 0x81, 0xff};
+static unsigned char symbol12_89_bits[] = {
+0xbb, 0x01, 0x92, 0x00, 0x92, 0x00, 0xd6, 0x00, 0x7c, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x38, 0x00};
+static unsigned char symbol12_90_bits[] = {
+0x3f, 0x21, 0x10, 0x08, 0x04, 0x04, 0x02, 0x21, 0x3f};
+static unsigned char symbol12_91_bits[] = {
+0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07};
+static unsigned char symbol12_92_bits[] = {
+0x08, 0x08, 0x00, 0x00, 0x41, 0x41};
+static unsigned char symbol12_93_bits[] = {
+0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x07};
+static unsigned char symbol12_94_bits[] = {
+0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f};
+static unsigned char symbol12_95_bits[] = {
+0x3f};
+static unsigned char symbol12_96_bits[] = {
+0x7f};
+static unsigned char symbol12_97_bits[] = {
+0x2e, 0x2b, 0x11, 0x11, 0x2b, 0x66};
+static unsigned char symbol12_98_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x0d, 0x11, 0x11, 0x11, 0x13, 0x0d, 0x01, 0x01, 
+0x01};
+static unsigned char symbol12_99_bits[] = {
+0x63, 0x35, 0x14, 0x18, 0x0c, 0x14, 0x16, 0x52, 0x63};
+static unsigned char symbol12_100_bits[] = {
+0x0e, 0x19, 0x01, 0x02, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char symbol12_101_bits[] = {
+0x0e, 0x01, 0x07, 0x01, 0x09, 0x0e};
+static unsigned char symbol12_102_bits[] = {
+0x08, 0x08, 0x1c, 0x2a, 0x49, 0x49, 0x2a, 0x1c, 0x08, 0x08, 0x08};
+static unsigned char symbol12_103_bits[] = {
+0x23, 0x25, 0x24, 0x24, 0x14, 0x18, 0x08, 0x0c, 0x04};
+static unsigned char symbol12_104_bits[] = {
+0x36, 0x4d, 0x44, 0x44, 0x44, 0x44, 0x40, 0x40, 0x40};
+static unsigned char symbol12_105_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x02, 0x06};
+static unsigned char symbol12_106_bits[] = {
+0x18, 0x2a, 0x49, 0x49, 0x2a, 0x1c, 0x08, 0x08, 0x08};
+static unsigned char symbol12_107_bits[] = {
+0x32, 0x2b, 0x06, 0x0e, 0x1a, 0x32};
+static unsigned char symbol12_108_bits[] = {
+0x06, 0x0a, 0x08, 0x0c, 0x14, 0x12, 0x12, 0x51, 0x61};
+static unsigned char symbol12_109_bits[] = {
+0x11, 0x11, 0x11, 0x11, 0x59, 0x37, 0x01, 0x01, 0x03};
+static unsigned char symbol12_110_bits[] = {
+0x33, 0x22, 0x22, 0x14, 0x14, 0x0c};
+static unsigned char symbol12_111_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char symbol12_112_bits[] = {
+0x7e, 0x25, 0x24, 0x24, 0x64, 0x26};
+static unsigned char symbol12_113_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char symbol12_114_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x13, 0x0d, 0x01, 0x01, 0x01};
+static unsigned char symbol12_115_bits[] = {
+0x3e, 0x09, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char symbol12_116_bits[] = {
+0x1e, 0x05, 0x04, 0x04, 0x14, 0x0c};
+static unsigned char symbol12_117_bits[] = {
+0x26, 0x45, 0x44, 0x44, 0x64, 0x38};
+static unsigned char symbol12_118_bits[] = {
+0xfe, 0x45, 0x92, 0x92, 0x92, 0x92, 0x6c};
+static unsigned char symbol12_119_bits[] = {
+0x22, 0x49, 0x49, 0x49, 0x49, 0x36};
+static unsigned char symbol12_120_bits[] = {
+0x02, 0x01, 0x0e, 0x03, 0x01, 0x0f, 0x02, 0x01, 0x01, 0x03, 0x0e, 0x10, 
+0x12, 0x0e};
+static unsigned char symbol12_121_bits[] = {
+0x11, 0x01, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0xd6, 0x00, 0x7c, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char symbol12_122_bits[] = {
+0x02, 0x19, 0x0e, 0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x0e, 0x10, 0x12, 
+0x0e};
+static unsigned char symbol12_123_bits[] = {
+0x18, 0x04, 0x04, 0x04, 0x02, 0x01, 0x02, 0x04, 0x04, 0x04, 0x04, 0x18};
+static unsigned char symbol12_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol12_125_bits[] = {
+0x03, 0x04, 0x04, 0x04, 0x08, 0x10, 0x08, 0x04, 0x04, 0x04, 0x04, 0x03};
+static unsigned char symbol12_126_bits[] = {
+0x46, 0x49, 0x31};
+static unsigned char symbol12_127_bits[] = {
+0x00};
+static unsigned char symbol12_128_bits[] = {
+0x00};
+static unsigned char symbol12_129_bits[] = {
+0x00};
+static unsigned char symbol12_130_bits[] = {
+0x00};
+static unsigned char symbol12_131_bits[] = {
+0x00};
+static unsigned char symbol12_132_bits[] = {
+0x00};
+static unsigned char symbol12_133_bits[] = {
+0x00};
+static unsigned char symbol12_134_bits[] = {
+0x00};
+static unsigned char symbol12_135_bits[] = {
+0x00};
+static unsigned char symbol12_136_bits[] = {
+0x00};
+static unsigned char symbol12_137_bits[] = {
+0x00};
+static unsigned char symbol12_138_bits[] = {
+0x00};
+static unsigned char symbol12_139_bits[] = {
+0x00};
+static unsigned char symbol12_140_bits[] = {
+0x00};
+static unsigned char symbol12_141_bits[] = {
+0x00};
+static unsigned char symbol12_142_bits[] = {
+0x00};
+static unsigned char symbol12_143_bits[] = {
+0x00};
+static unsigned char symbol12_144_bits[] = {
+0x00};
+static unsigned char symbol12_145_bits[] = {
+0x00};
+static unsigned char symbol12_146_bits[] = {
+0x00};
+static unsigned char symbol12_147_bits[] = {
+0x00};
+static unsigned char symbol12_148_bits[] = {
+0x00};
+static unsigned char symbol12_149_bits[] = {
+0x00};
+static unsigned char symbol12_150_bits[] = {
+0x00};
+static unsigned char symbol12_151_bits[] = {
+0x00};
+static unsigned char symbol12_152_bits[] = {
+0x00};
+static unsigned char symbol12_153_bits[] = {
+0x00};
+static unsigned char symbol12_154_bits[] = {
+0x00};
+static unsigned char symbol12_155_bits[] = {
+0x00};
+static unsigned char symbol12_156_bits[] = {
+0x00};
+static unsigned char symbol12_157_bits[] = {
+0x00};
+static unsigned char symbol12_158_bits[] = {
+0x00};
+static unsigned char symbol12_159_bits[] = {
+0x00};
+static unsigned char symbol12_160_bits[] = {
+0x00};
+static unsigned char symbol12_161_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol12_162_bits[] = {
+0x00, 0x00, 0x00};
+static unsigned char symbol12_163_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol12_164_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol12_165_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00};
+static unsigned char symbol12_166_bits[] = {
+0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x04, 0x02, 0x01, 0x30, 0x0c};
+static unsigned char symbol12_167_bits[] = {
+0x03, 0x0c, 0x30, 0x00, 0x3f, 0x10, 0x08};
+static unsigned char symbol12_168_bits[] = {
+0x08, 0x04, 0x04, 0x02, 0x02, 0x01};
+static unsigned char symbol12_169_bits[] = {
+0x01, 0x6e, 0x00, 0x19, 0x01, 0x31};
+static unsigned char symbol12_170_bits[] = {
+0x01, 0x6e, 0x00, 0x30, 0x28, 0x08, 0x3e};
+static unsigned char symbol12_171_bits[] = {
+0x08, 0x00, 0x08, 0x04, 0x04, 0x04, 0x05, 0x03, 0x1c, 0x04};
+static unsigned char symbol12_172_bits[] = {
+0x6b, 0x0f, 0x6b, 0x08, 0x08, 0x04, 0x0e, 0x0f, 0x1f, 0x0e};
+static unsigned char symbol12_173_bits[] = {
+0x04, 0x16, 0x1f, 0x1f, 0x1e, 0x1c, 0x08, 0x08, 0x1c, 0x1e, 0x1f, 0x0b, 
+0x08, 0x08, 0x04, 0x01};
+static unsigned char symbol12_174_bits[] = {
+0x02, 0x02, 0xff, 0x07, 0x02, 0x02, 0x04, 0x01, 0x04, 0x00};
+static unsigned char symbol12_175_bits[] = {
+0x02, 0x00, 0x1f, 0x0f, 0x02, 0x00, 0x04, 0x00, 0x04, 0x0e, 0x15, 0x04, 
+0x04, 0x04, 0x04};
+static unsigned char symbol12_176_bits[] = {
+0x04, 0x04, 0x04, 0x04};
+static unsigned char symbol12_177_bits[] = {
+0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x02};
+static unsigned char symbol12_178_bits[] = {
+0x00, 0x04, 0x1f};
+static unsigned char symbol12_179_bits[] = {
+0x0f, 0x00, 0x04, 0x00, 0x02, 0x04, 0x04};
+static unsigned char symbol12_180_bits[] = {
+0x04, 0x04, 0x04, 0x04, 0x04};
+static unsigned char symbol12_181_bits[] = {
+0x04, 0x04, 0x04, 0x04};
+static unsigned char symbol12_182_bits[] = {
+0x04, 0x15, 0x0e, 0x04, 0x06, 0x09, 0x09, 0x06, 0x04, 0x04};
+static unsigned char symbol12_183_bits[] = {
+0x0f, 0x04, 0x04, 0x00};
+static unsigned char symbol12_184_bits[] = {
+0x1f, 0x14, 0x0a, 0x05, 0x03};
+static unsigned char symbol12_185_bits[] = {
+0x0c, 0x10, 0x0c, 0x03, 0x00, 0x1f, 0x11};
+static unsigned char symbol12_186_bits[] = {
+0x0a, 0x04, 0x0a, 0x11, 0x0e};
+static unsigned char symbol12_187_bits[] = {
+0x11, 0x11, 0x2e, 0x06, 0x09};
+static unsigned char symbol12_188_bits[] = {
+0x10, 0x00};
+static unsigned char symbol12_189_bits[] = {
+0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 
+0x01, 0x00, 0x00, 0x00};
+static unsigned char symbol12_190_bits[] = {
+0x08, 0x1f};
+static unsigned char symbol12_191_bits[] = {
+0x04, 0x1f, 0x02, 0x01, 0x1f, 0x00, 0x1f, 0x00};
+static unsigned char symbol12_192_bits[] = {
+0x1f, 0x26, 0x19, 0x00, 0x26, 0x19, 0x11, 0x01, 0x01};
+static unsigned char symbol12_193_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol12_194_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 
+0x7f, 0x00, 0x11, 0x01, 0x66, 0x00, 0x2b, 0x01};
+static unsigned char symbol12_195_bits[] = {
+0x32, 0x66, 0x47, 0xfc, 0x86, 0x42, 0x26, 0x30, 0x60, 0xc0, 0x80};
+static unsigned char symbol12_196_bits[] = {
+0x86, 0x01, 0x38, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol12_197_bits[] = {
+0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x64, 0x00, 0x89, 0x01, 0xd5, 0x01, 
+0x26, 0x01, 0x09, 0x00, 0x38, 0x00};
+static unsigned char symbol12_198_bits[] = {
+0xc6, 0x00, 0xc6, 0x00, 0x29, 0x01, 0x11, 0x01, 0x29, 0x01, 0xc6, 0x00, 
+0xc6, 0x00, 0x38, 0x00, 0x38, 0x00};
+static unsigned char symbol12_199_bits[] = {
+0xc6, 0x00, 0x92, 0x00, 0x11, 0x01};
+static unsigned char symbol12_200_bits[] = {
+0x7d, 0x01, 0x11, 0x01, 0x92, 0x00};
+static unsigned char symbol12_201_bits[] = {
+0xc6, 0x00, 0x38, 0x00, 0x38, 0x01};
+static unsigned char symbol12_202_bits[] = {
+0xc6, 0x00, 0xc2, 0x00, 0x21, 0x01, 0x11, 0x01};
+static unsigned char symbol12_203_bits[] = {
+0x09, 0x01, 0x86, 0x00, 0xc6, 0x00, 0x39, 0x00};
+static unsigned char symbol12_204_bits[] = {
+0x3c, 0x42, 0x81, 0x81, 0x81, 0x81};
+static unsigned char symbol12_205_bits[] = {
+0x81, 0x81, 0x81, 0x81, 0x42, 0x3c, 0x3f, 0x40};
+static unsigned char symbol12_206_bits[] = {
+0x00, 0x00, 0x00, 0x3f, 0x3f, 0x00};
+static unsigned char symbol12_207_bits[] = {
+0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3c};
+static unsigned char symbol12_208_bits[] = {
+0x22, 0x01, 0x11, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x01, 0x01, 0x02, 0x00, 
+0xfc, 0x00, 0x01, 0x01, 0x02, 0x00};
+static unsigned char symbol12_209_bits[] = {
+0x00, 0xff, 0x3e, 0x01, 0x3f, 0x01, 0x01, 0x3e, 0x20};
+static unsigned char symbol12_210_bits[] = {
+0x3e, 0x11, 0x3f, 0x09, 0x05, 0x3e, 0x02, 0x00, 0x01};
+static unsigned char symbol12_211_bits[] = {
+0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08};
+static unsigned char symbol12_212_bits[] = {
+0x00, 0x04, 0x00, 0x02, 0x00, 0x07, 0x01, 0x07, 0x81, 0x02, 0x42, 0x04};
+static unsigned char symbol12_213_bits[] = {
+0x24, 0x00, 0x18, 0x00, 0x3c, 0x00, 0x9f, 0x01, 0x9d, 0x01, 0xb7, 0x00, 
+0x3c, 0x00, 0x42, 0x01, 0x95, 0x01, 0xa5, 0x01, 0x42, 0x00};
+static unsigned char symbol12_214_bits[] = {
+0x5f, 0x01, 0x55, 0x01, 0x24, 0x02, 0x24, 0x02, 0x24, 0x02, 0x6e, 0x06};
+static unsigned char symbol12_215_bits[] = {
+0x03};
+static unsigned char symbol12_216_bits[] = {
+0x01, 0x02, 0x00, 0x02};
+static unsigned char symbol12_217_bits[] = {
+0x00, 0x02, 0x00, 0x02, 0x00, 0x02};
+static unsigned char symbol12_218_bits[] = {
+0x00, 0x02, 0x00, 0x02, 0x00, 0x02};
+static unsigned char symbol12_219_bits[] = {
+0x00, 0x02, 0x00, 0x07, 0x01, 0x00, 0x40, 0x00, 0x20, 0x00};
+static unsigned char symbol12_220_bits[] = {
+0x20, 0x06, 0x15, 0x04, 0x14, 0x00, 0x08, 0x03, 0x7f, 0x00};
+static unsigned char symbol12_221_bits[] = {
+0x00, 0x00, 0x0c, 0x0c, 0x12, 0x12, 0x01, 0x01, 0x01, 0x01, 0x12, 0x12};
+static unsigned char symbol12_222_bits[] = {
+0x0c, 0x04, 0x04, 0x01, 0xfe, 0x03, 0x03, 0x06, 0xfe, 0x03};
+static unsigned char symbol12_223_bits[] = {
+0x04, 0x01, 0x04, 0x00, 0x1e, 0x07, 0x03, 0x00, 0x1e, 0x07, 0x04, 0x00};
+static unsigned char symbol12_224_bits[] = {
+0x04, 0x0e, 0x1b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a};
+static unsigned char symbol12_225_bits[] = {
+0x0a, 0x0a, 0x0a, 0x00, 0x01, 0x0f, 0x03, 0x00, 0x06, 0x0f, 0x03};
+static unsigned char symbol12_226_bits[] = {
+0x00, 0x01, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a};
+static unsigned char symbol12_227_bits[] = {
+0x0a, 0x0a, 0x1b, 0x0e, 0x04, 0x0c, 0x0c, 0x12, 0x21};
+static unsigned char symbol12_228_bits[] = {
+0x21, 0x02, 0x12, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x02, 0x02, 0x01, 0x02};
+static unsigned char symbol12_229_bits[] = {
+0x02, 0x04, 0x04, 0x08, 0x3c, 0x42, 0x9d, 0xa5, 0x9d, 0xa5, 0xa5};
+static unsigned char symbol12_230_bits[] = {
+0x02, 0x0c, 0x0c, 0x02, 0x09, 0x05, 0x05, 0x05, 0x09, 0x02, 0x0c, 0x0f, 
+0x01, 0x04, 0x01, 0x04};
+static unsigned char symbol12_231_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol12_232_bits[] = {
+0x01, 0x0f, 0x08, 0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol12_233_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol12_234_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol12_235_bits[] = {
+0x04, 0x00, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01};
+static unsigned char symbol12_236_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol12_237_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol12_238_bits[] = {
+0x01, 0x07, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01};
+static unsigned char symbol12_239_bits[] = {
+0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 
+0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol12_240_bits[] = {
+0x00};
+static unsigned char symbol12_241_bits[] = {
+0x04, 0x04, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol12_242_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01};
+static unsigned char symbol12_243_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x02, 0x02, 0x04, 
+0x04, 0x08, 0x08, 0x04};
+static unsigned char symbol12_244_bits[] = {
+0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol12_245_bits[] = {
+0x04, 0x05, 0x03, 0x1c, 0x1a, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01};
+static unsigned char symbol12_246_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol12_247_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol12_248_bits[] = {
+0x00, 0x0b, 0x07, 0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x08};
+static unsigned char symbol12_249_bits[] = {
+0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol12_250_bits[] = {
+0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol12_251_bits[] = {
+0x02, 0x02, 0x01, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0x04};
+static unsigned char symbol12_252_bits[] = {
+0x04, 0x04, 0x04, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol12_253_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0x04, 0x04};
+static unsigned char symbol12_254_bits[] = {
+0x04, 0x04, 0x07, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x04, 0x04};
+static unsigned char symbol12_255_bits[] = {
+0x00};
+static RotFont symbol12font[] = {
+{5, 1, 1, symbol12_0_bits},
+{5, 1, 1, symbol12_1_bits},
+{5, 1, 1, symbol12_2_bits},
+{5, 1, 1, symbol12_3_bits},
+{5, 1, 1, symbol12_4_bits},
+{5, 1, 1, symbol12_5_bits},
+{5, 1, 1, symbol12_6_bits},
+{5, 1, 1, symbol12_7_bits},
+{5, 1, 1, symbol12_8_bits},
+{5, 1, 1, symbol12_9_bits},
+{5, 1, 1, symbol12_10_bits},
+{5, 1, 1, symbol12_11_bits},
+{5, 1, 1, symbol12_12_bits},
+{5, 1, 1, symbol12_13_bits},
+{5, 1, 1, symbol12_14_bits},
+{5, 1, 1, symbol12_15_bits},
+{5, 1, 1, symbol12_16_bits},
+{5, 1, 1, symbol12_17_bits},
+{5, 1, 1, symbol12_18_bits},
+{5, 1, 1, symbol12_19_bits},
+{5, 1, 1, symbol12_20_bits},
+{5, 1, 1, symbol12_21_bits},
+{5, 1, 1, symbol12_22_bits},
+{5, 1, 1, symbol12_23_bits},
+{5, 1, 1, symbol12_24_bits},
+{5, 1, 1, symbol12_25_bits},
+{5, 1, 1, symbol12_26_bits},
+{5, 1, 1, symbol12_27_bits},
+{5, 1, 1, symbol12_28_bits},
+{5, 1, 1, symbol12_29_bits},
+{5, 1, 1, symbol12_30_bits},
+{5, 1, 1, symbol12_31_bits},
+{1, 1, 1, symbol12_32_bits},
+{1, 9, 9, symbol12_33_bits},
+{9, 9, 9, symbol12_34_bits},
+{6, 9, 9, symbol12_35_bits},
+{5, 9, 9, symbol12_36_bits},
+{9, 9, 9, symbol12_37_bits},
+{9, 9, 9, symbol12_38_bits},
+{4, 6, 6, symbol12_39_bits},
+{3, 12, 9, symbol12_40_bits},
+{3, 12, 9, symbol12_41_bits},
+{5, 5, 7, symbol12_42_bits},
+{5, 5, 6, symbol12_43_bits},
+{2, 3, 1, symbol12_44_bits},
+{5, 1, 4, symbol12_45_bits},
+{1, 1, 1, symbol12_46_bits},
+{3, 9, 9, symbol12_47_bits},
+{5, 9, 9, symbol12_48_bits},
+{3, 9, 9, symbol12_49_bits},
+{5, 9, 9, symbol12_50_bits},
+{5, 9, 9, symbol12_51_bits},
+{6, 9, 9, symbol12_52_bits},
+{5, 9, 9, symbol12_53_bits},
+{5, 9, 9, symbol12_54_bits},
+{5, 9, 9, symbol12_55_bits},
+{5, 9, 9, symbol12_56_bits},
+{5, 9, 9, symbol12_57_bits},
+{1, 6, 6, symbol12_58_bits},
+{2, 8, 6, symbol12_59_bits},
+{6, 5, 6, symbol12_60_bits},
+{5, 3, 5, symbol12_61_bits},
+{6, 5, 6, symbol12_62_bits},
+{5, 9, 9, symbol12_63_bits},
+{5, 6, 6, symbol12_64_bits},
+{9, 9, 9, symbol12_65_bits},
+{7, 9, 9, symbol12_66_bits},
+{8, 9, 9, symbol12_67_bits},
+{7, 9, 9, symbol12_68_bits},
+{7, 9, 9, symbol12_69_bits},
+{9, 9, 9, symbol12_70_bits},
+{7, 9, 9, symbol12_71_bits},
+{8, 9, 9, symbol12_72_bits},
+{3, 9, 9, symbol12_73_bits},
+{8, 9, 9, symbol12_74_bits},
+{8, 9, 9, symbol12_75_bits},
+{9, 9, 9, symbol12_76_bits},
+{11, 9, 9, symbol12_77_bits},
+{8, 9, 9, symbol12_78_bits},
+{8, 9, 9, symbol12_79_bits},
+{8, 9, 9, symbol12_80_bits},
+{8, 9, 9, symbol12_81_bits},
+{6, 9, 9, symbol12_82_bits},
+{7, 9, 9, symbol12_83_bits},
+{7, 9, 9, symbol12_84_bits},
+{9, 9, 9, symbol12_85_bits},
+{5, 9, 6, symbol12_86_bits},
+{10, 9, 9, symbol12_87_bits},
+{8, 9, 9, symbol12_88_bits},
+{9, 9, 9, symbol12_89_bits},
+{6, 9, 9, symbol12_90_bits},
+{3, 12, 9, symbol12_91_bits},
+{7, 6, 6, symbol12_92_bits},
+{3, 12, 9, symbol12_93_bits},
+{7, 9, 9, symbol12_94_bits},
+{6, 1, -2, symbol12_95_bits},
+{7, 1, 12, symbol12_96_bits},
+{7, 6, 6, symbol12_97_bits},
+{5, 13, 10, symbol12_98_bits},
+{7, 9, 6, symbol12_99_bits},
+{5, 10, 10, symbol12_100_bits},
+{4, 6, 6, symbol12_101_bits},
+{7, 11, 8, symbol12_102_bits},
+{6, 9, 6, symbol12_103_bits},
+{7, 9, 6, symbol12_104_bits},
+{3, 6, 6, symbol12_105_bits},
+{7, 9, 6, symbol12_106_bits},
+{6, 6, 6, symbol12_107_bits},
+{7, 9, 9, symbol12_108_bits},
+{7, 9, 6, symbol12_109_bits},
+{6, 6, 6, symbol12_110_bits},
+{5, 6, 6, symbol12_111_bits},
+{7, 6, 6, symbol12_112_bits},
+{5, 9, 9, symbol12_113_bits},
+{5, 9, 6, symbol12_114_bits},
+{6, 6, 6, symbol12_115_bits},
+{5, 6, 6, symbol12_116_bits},
+{7, 6, 6, symbol12_117_bits},
+{8, 7, 7, symbol12_118_bits},
+{7, 6, 6, symbol12_119_bits},
+{5, 14, 11, symbol12_120_bits},
+{9, 9, 6, symbol12_121_bits},
+{5, 13, 10, symbol12_122_bits},
+{5, 12, 9, symbol12_123_bits},
+{1, 12, 9, symbol12_124_bits},
+{5, 12, 9, symbol12_125_bits},
+{7, 3, 5, symbol12_126_bits},
+{5, 1, 1, symbol12_127_bits},
+{5, 1, 1, symbol12_128_bits},
+{5, 1, 1, symbol12_129_bits},
+{5, 1, 1, symbol12_130_bits},
+{5, 1, 1, symbol12_131_bits},
+{5, 1, 1, symbol12_132_bits},
+{5, 1, 1, symbol12_133_bits},
+{5, 1, 1, symbol12_134_bits},
+{5, 1, 1, symbol12_135_bits},
+{5, 1, 1, symbol12_136_bits},
+{5, 1, 1, symbol12_137_bits},
+{5, 1, 1, symbol12_138_bits},
+{5, 1, 1, symbol12_139_bits},
+{5, 1, 1, symbol12_140_bits},
+{5, 1, 1, symbol12_141_bits},
+{5, 1, 1, symbol12_142_bits},
+{5, 1, 1, symbol12_143_bits},
+{5, 1, 1, symbol12_144_bits},
+{5, 1, 1, symbol12_145_bits},
+{5, 1, 1, symbol12_146_bits},
+{5, 1, 1, symbol12_147_bits},
+{5, 1, 1, symbol12_148_bits},
+{5, 1, 1, symbol12_149_bits},
+{5, 1, 1, symbol12_150_bits},
+{5, 1, 1, symbol12_151_bits},
+{5, 1, 1, symbol12_152_bits},
+{5, 1, 1, symbol12_153_bits},
+{5, 1, 1, symbol12_154_bits},
+{5, 1, 1, symbol12_155_bits},
+{5, 1, 1, symbol12_156_bits},
+{5, 1, 1, symbol12_157_bits},
+{5, 1, 1, symbol12_158_bits},
+{5, 1, 1, symbol12_159_bits},
+{5, 1, 1, symbol12_160_bits},
+{8, 9, 9, symbol12_161_bits},
+{3, 3, 9, symbol12_162_bits},
+{6, 7, 7, symbol12_163_bits},
+{5, 9, 9, symbol12_164_bits},
+{9, 4, 5, symbol12_165_bits},
+{6, 12, 9, symbol12_166_bits},
+{7, 7, 6, symbol12_167_bits},
+{5, 6, 6, symbol12_168_bits},
+{7, 6, 6, symbol12_169_bits},
+{7, 7, 6, symbol12_170_bits},
+{11, 5, 6, symbol12_171_bits},
+{12, 5, 6, symbol12_172_bits},
+{5, 16, 12, symbol12_173_bits},
+{12, 5, 6, symbol12_174_bits},
+{5, 15, 12, symbol12_175_bits},
+{4, 4, 9, symbol12_176_bits},
+{5, 7, 7, symbol12_177_bits},
+{5, 3, 9, symbol12_178_bits},
+{6, 7, 7, symbol12_179_bits},
+{5, 5, 6, symbol12_180_bits},
+{8, 4, 5, symbol12_181_bits},
+{5, 10, 10, symbol12_182_bits},
+{4, 4, 5, symbol12_183_bits},
+{5, 5, 6, symbol12_184_bits},
+{5, 7, 7, symbol12_185_bits},
+{5, 5, 6, symbol12_186_bits},
+{6, 5, 6, symbol12_187_bits},
+{9, 1, 1, symbol12_188_bits},
+{1, 16, 12, symbol12_189_bits},
+{13, 1, 4, symbol12_190_bits},
+{7, 8, 8, symbol12_191_bits},
+{7, 9, 9, symbol12_192_bits},
+{8, 11, 10, symbol12_193_bits},
+{9, 10, 10, symbol12_194_bits},
+{8, 11, 8, symbol12_195_bits},
+{9, 9, 9, symbol12_196_bits},
+{9, 9, 9, symbol12_197_bits},
+{9, 9, 9, symbol12_198_bits},
+{8, 6, 6, symbol12_199_bits},
+{8, 6, 6, symbol12_200_bits},
+{8, 6, 6, symbol12_201_bits},
+{8, 8, 6, symbol12_202_bits},
+{8, 8, 7, symbol12_203_bits},
+{8, 6, 6, symbol12_204_bits},
+{8, 8, 6, symbol12_205_bits},
+{6, 6, 6, symbol12_206_bits},
+{6, 8, 7, symbol12_207_bits},
+{9, 9, 9, symbol12_208_bits},
+{8, 9, 9, symbol12_209_bits},
+{8, 9, 9, symbol12_210_bits},
+{8, 9, 9, symbol12_211_bits},
+{11, 6, 9, symbol12_212_bits},
+{9, 11, 10, symbol12_213_bits},
+{7, 12, 12, symbol12_214_bits},
+{2, 1, 4, symbol12_215_bits},
+{7, 4, 4, symbol12_216_bits},
+{6, 6, 6, symbol12_217_bits},
+{6, 6, 6, symbol12_218_bits},
+{11, 5, 6, symbol12_219_bits},
+{11, 5, 6, symbol12_220_bits},
+{5, 12, 11, symbol12_221_bits},
+{11, 5, 6, symbol12_222_bits},
+{5, 12, 12, symbol12_223_bits},
+{6, 9, 9, symbol12_224_bits},
+{4, 11, 10, symbol12_225_bits},
+{8, 9, 9, symbol12_226_bits},
+{8, 9, 9, symbol12_227_bits},
+{10, 6, 9, symbol12_228_bits},
+{8, 11, 10, symbol12_229_bits},
+{4, 16, 12, symbol12_230_bits},
+{1, 16, 12, symbol12_231_bits},
+{4, 16, 12, symbol12_232_bits},
+{3, 16, 12, symbol12_233_bits},
+{1, 16, 12, symbol12_234_bits},
+{3, 15, 12, symbol12_235_bits},
+{3, 16, 12, symbol12_236_bits},
+{3, 16, 12, symbol12_237_bits},
+{3, 15, 12, symbol12_238_bits},
+{1, 16, 12, symbol12_239_bits},
+{5, 1, 1, symbol12_240_bits},
+{4, 12, 10, symbol12_241_bits},
+{5, 15, 12, symbol12_242_bits},
+{5, 16, 12, symbol12_243_bits},
+{1, 16, 12, symbol12_244_bits},
+{5, 15, 12, symbol12_245_bits},
+{4, 16, 12, symbol12_246_bits},
+{1, 16, 12, symbol12_247_bits},
+{4, 16, 12, symbol12_248_bits},
+{3, 16, 12, symbol12_249_bits},
+{1, 16, 12, symbol12_250_bits},
+{3, 15, 12, symbol12_251_bits},
+{3, 16, 12, symbol12_252_bits},
+{3, 16, 12, symbol12_253_bits},
+{3, 15, 12, symbol12_254_bits},
+{5, 1, 1, symbol12_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol14.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol14.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol14.bdf	(revision 22322)
@@ -0,0 +1,3274 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Symbol-Medium-R-Normal--14-140-75-75-P-85-Adobe-FontSpecific
+SIZE 14 75 75
+FONTBOUNDINGBOX 16 20 -1 -6
+STARTPROPERTIES 31
+FOUNDRY "Adobe"
+FAMILY_NAME "Symbol"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 14
+POINT_SIZE 140
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 85
+CHARSET_REGISTRY "Adobe"
+CHARSET_ENCODING "FontSpecific"
+CAP_HEIGHT 10
+X_HEIGHT 7
+FACE_NAME "Symbol"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "No mark"
+_DEC_DEVICE_FONTNAMES "PS=Symbol"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2-1, 18-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+FULL_NAME "Symbol"
+FONT "-Adobe-Symbol-Medium-R-Normal--14-140-75-75-P-85-Adobe-FontSpecific"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 9
+DEFAULT_CHAR 32
+FONT_ASCENT 12
+FONT_DESCENT 3
+ENDPROPERTIES
+CHARS 188
+STARTCHAR space
+ENCODING 32
+SWIDTH 274 0
+DWIDTH 4 0
+BBX 1 1 1 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 342 0
+DWIDTH 5 0
+BBX 1 10 2 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+00
+80
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 9 10 0 0
+BITMAP
+8080
+4100
+4100
+3e00
+2200
+2200
+1400
+1400
+0800
+0800
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 7 10 0 0
+BITMAP
+14
+14
+14
+7e
+28
+28
+fc
+50
+50
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 6 10 1 0
+BITMAP
+fc
+04
+04
+04
+fc
+04
+04
+04
+04
+fc
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 822 0
+DWIDTH 12 0
+BBX 9 10 1 0
+BITMAP
+7980
+9700
+9200
+9400
+6c00
+1b00
+1480
+2480
+4480
+c300
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+3000
+4800
+4800
+7000
+2700
+7200
+ca00
+8400
+ce80
+7b00
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 5 7 0 0
+BITMAP
+e0
+10
+08
+78
+08
+10
+e0
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 342 0
+DWIDTH 5 0
+BBX 3 13 1 -3
+BITMAP
+20
+40
+40
+80
+80
+80
+80
+80
+80
+80
+40
+40
+20
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 342 0
+DWIDTH 5 0
+BBX 3 13 1 -3
+BITMAP
+80
+40
+40
+20
+20
+20
+20
+20
+20
+20
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 5 6 1 2
+BITMAP
+20
+a8
+70
+70
+a8
+20
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+10
+10
+10
+fe
+10
+10
+10
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 205 0
+DWIDTH 3 0
+BBX 2 4 0 -2
+BITMAP
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 1 0 3
+BITMAP
+fe
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 205 0
+DWIDTH 3 0
+BBX 1 2 1 0
+BITMAP
+80
+80
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 274 0
+DWIDTH 4 0
+BBX 4 10 0 0
+BITMAP
+10
+10
+20
+20
+20
+40
+40
+40
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+78
+cc
+84
+84
+84
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+20
+e0
+20
+20
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+70
+d8
+88
+08
+18
+10
+20
+40
+c4
+fc
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+78
+cc
+84
+0c
+38
+0c
+04
+04
+cc
+78
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+08
+18
+18
+28
+68
+48
+88
+fc
+08
+08
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+78
+40
+80
+e0
+30
+18
+08
+08
+90
+e0
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+1c
+30
+40
+40
+f8
+8c
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+fc
+84
+08
+08
+10
+10
+20
+20
+40
+40
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+70
+d8
+88
+88
+70
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+78
+cc
+84
+84
+c4
+78
+18
+10
+60
+c0
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 274 0
+DWIDTH 4 0
+BBX 1 7 2 0
+BITMAP
+80
+80
+00
+00
+00
+80
+80
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 274 0
+DWIDTH 4 0
+BBX 2 9 1 -2
+BITMAP
+40
+40
+00
+00
+00
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 1 0
+BITMAP
+02
+0c
+30
+c0
+30
+0c
+02
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 3 0 2
+BITMAP
+fe
+00
+fe
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+80
+60
+18
+06
+18
+60
+80
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 5 10 0 0
+BITMAP
+70
+88
+88
+08
+10
+20
+20
+00
+20
+20
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+72
+9c
+00
+fe
+00
+00
+fe
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+0800
+1c00
+1400
+1400
+2200
+2200
+3e00
+6300
+4100
+e380
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+fc
+46
+42
+46
+7c
+46
+42
+42
+46
+fc
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+e380
+4100
+2200
+3600
+1c00
+1400
+2200
+6300
+4100
+e380
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+10
+10
+28
+28
+28
+44
+44
+44
+82
+fe
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+fe
+42
+40
+44
+7c
+44
+40
+42
+42
+fe
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+1c00
+0800
+7f00
+c980
+8880
+8880
+c980
+7f00
+0800
+1c00
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+fe
+42
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+e380
+4100
+4100
+4100
+7f00
+4100
+4100
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 342 0
+DWIDTH 5 0
+BBX 3 10 1 0
+BITMAP
+e0
+40
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+0c
+0a
+0a
+66
+a3
+22
+22
+22
+36
+1c
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+ee
+44
+48
+50
+70
+50
+48
+44
+42
+e7
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 10 0 0
+BITMAP
+0800
+0800
+1400
+3600
+2200
+2200
+6300
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 891 0
+DWIDTH 13 0
+BBX 11 10 1 0
+BITMAP
+e0e0
+60c0
+5140
+5140
+5b40
+4a40
+4a40
+4e40
+4440
+e4e0
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+e380
+6100
+5100
+5900
+4900
+4d00
+4500
+4700
+4300
+e100
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+3c
+66
+42
+81
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+ff80
+4100
+4100
+4100
+4100
+4100
+4100
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+3c
+66
+42
+a5
+bd
+a5
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 6 10 1 0
+BITMAP
+f8
+4c
+44
+44
+4c
+78
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 8 10 1 0
+BITMAP
+fe
+c2
+60
+30
+18
+10
+20
+40
+c1
+ff
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+fe
+92
+92
+10
+10
+10
+10
+10
+10
+38
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 9 10 0 0
+BITMAP
+e380
+4100
+2200
+2200
+1400
+0800
+0800
+0800
+0800
+1c00
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 10 1 -3
+BITMAP
+38
+c0
+80
+80
+80
+60
+38
+04
+24
+38
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 10 10 0 0
+BITMAP
+0c00
+3300
+2100
+4080
+4080
+4080
+4080
+2100
+9240
+f3c0
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 9 10 0 0
+BITMAP
+7f00
+4100
+0000
+2200
+3e00
+2200
+0000
+0000
+8080
+ff80
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 11 10 0 0
+BITMAP
+ce60
+64c0
+2480
+2480
+1f00
+0400
+0400
+0400
+0400
+0e00
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+fe
+82
+04
+08
+10
+10
+20
+40
+82
+fe
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 342 0
+DWIDTH 5 0
+BBX 3 13 1 -3
+BITMAP
+e0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+e0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 8 7 1 0
+BITMAP
+18
+18
+00
+00
+00
+c3
+c3
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 342 0
+DWIDTH 5 0
+BBX 3 13 1 -3
+BITMAP
+e0
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+e0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 10 0 0
+BITMAP
+0800
+0800
+0800
+0800
+0800
+0800
+0800
+0800
+0800
+ff80
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 7 1 0 -3
+BITMAP
+fe
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 8 1 0 13
+BITMAP
+ff
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 8 7 1 0
+BITMAP
+76
+d4
+88
+88
+88
+d5
+66
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 6 14 1 -3
+BITMAP
+78
+cc
+84
+8c
+98
+8c
+84
+84
+84
+cc
+b8
+80
+80
+80
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 8 10 0 -3
+BITMAP
+c3
+a6
+24
+3c
+18
+38
+28
+64
+45
+c3
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 5 11 1 0
+BITMAP
+70
+98
+80
+40
+70
+98
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 5 7 1 0
+BITMAP
+78
+c8
+80
+60
+80
+c8
+70
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 13 1 -3
+BITMAP
+10
+10
+10
+38
+54
+92
+92
+92
+54
+38
+10
+10
+10
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 6 10 0 -3
+BITMAP
+c4
+a4
+24
+28
+28
+18
+18
+10
+30
+30
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 10 0 -3
+BITMAP
+6c
+b2
+22
+22
+22
+22
+22
+02
+02
+02
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 342 0
+DWIDTH 5 0
+BBX 5 7 0 0
+BITMAP
+60
+a0
+20
+20
+20
+28
+30
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 10 1 -3
+BITMAP
+18
+54
+d2
+92
+92
+54
+38
+10
+10
+10
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+46
+ca
+50
+70
+58
+4c
+46
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 8 10 1 0
+BITMAP
+60
+50
+10
+10
+28
+28
+48
+44
+85
+83
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 9 1 -2
+BITMAP
+88
+88
+88
+88
+88
+9a
+f6
+80
+c0
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+c6
+42
+22
+22
+14
+14
+08
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 6 7 1 0
+BITMAP
+78
+cc
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 8 7 0 0
+BITMAP
+7e
+a4
+24
+24
+24
+25
+66
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+70
+d8
+88
+88
+f8
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 6 10 1 -3
+BITMAP
+78
+cc
+84
+84
+84
+cc
+b8
+80
+80
+80
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 1 0
+BITMAP
+7e
+c8
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 5 7 0 0
+BITMAP
+78
+a0
+20
+20
+20
+28
+30
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+64
+a2
+22
+22
+22
+26
+1c
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 10 8 0 0
+BITMAP
+7fc0
+a080
+4440
+4440
+4440
+4440
+64c0
+3b80
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 7 1 0
+BITMAP
+6300
+8880
+8880
+8880
+8880
+c980
+7700
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 15 1 -3
+BITMAP
+20
+40
+38
+20
+40
+78
+20
+40
+80
+80
+c0
+78
+04
+24
+38
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 9 10 0 -3
+BITMAP
+8880
+4900
+4900
+4900
+6b00
+3e00
+0800
+0800
+0800
+0800
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 14 1 -3
+BITMAP
+20
+44
+3c
+10
+20
+40
+40
+80
+80
+c0
+78
+04
+24
+38
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 5 13 1 -3
+BITMAP
+18
+20
+20
+20
+20
+40
+80
+40
+20
+20
+20
+20
+18
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 205 0
+DWIDTH 3 0
+BBX 1 13 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 5 13 1 -3
+BITMAP
+c0
+20
+20
+20
+20
+10
+08
+10
+20
+20
+20
+20
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 3 1 2
+BITMAP
+62
+92
+8c
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 8 10 0 0
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 274 0
+DWIDTH 4 0
+BBX 4 3 0 7
+BITMAP
+00
+00
+00
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 274 0
+DWIDTH 4 0
+BBX 5 10 -1 0
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 4 0 2
+BITMAP
+0000
+c600
+2b00
+1000
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 7 13 0 -3
+BITMAP
+10
+10
+10
+38
+30
+60
+80
+02
+0c
+30
+c0
+30
+0c
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 8 1 -1
+BITMAP
+0200
+fe00
+0800
+1000
+2000
+4080
+8000
+0080
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 7 7 2 0
+BITMAP
+80
+8c
+80
+76
+00
+06
+0a
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 7 7 2 0
+BITMAP
+08
+08
+3c
+10
+10
+10
+20
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 8 1 -1
+BITMAP
+2000
+a080
+1c00
+1c00
+1c00
+eb80
+ff80
+eb80
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 1028 0
+DWIDTH 15 0
+BBX 13 7 1 0
+BITMAP
+0800
+0800
+1038
+7cf8
+7c38
+1068
+fef8
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 960 0
+DWIDTH 14 0
+BBX 14 7 0 0
+BITMAP
+fe7c
+3810
+0800
+1c00
+3e00
+7f00
+ff80
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 20 1 -6
+BITMAP
+6a
+00
+08
+00
+08
+00
+10
+40
+20
+20
+40
+10
+fe
+f8
+40
+10
+20
+20
+10
+40
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 960 0
+DWIDTH 14 0
+BBX 14 7 0 0
+BITMAP
+1000
+2000
+4000
+fffc
+4000
+2000
+1000
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 17 1 -3
+BITMAP
+10
+38
+54
+92
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 4 4 1 6
+BITMAP
+10
+10
+10
+00
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+20
+00
+10
+00
+08
+fe
+fc
+00
+08
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 7 3 -1 7
+BITMAP
+00
+10
+00
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+20
+10
+10
+10
+10
+10
+10
+10
+10
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+10
+10
+10
+10
+10
+92
+54
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 8 4 0 2
+BITMAP
+38
+10
+60
+90
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 6 11 0 0
+BITMAP
+90
+60
+10
+10
+10
+fc
+10
+10
+10
+00
+fc
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+30
+68
+90
+80
+60
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+18
+06
+18
+60
+80
+00
+fe
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+82
+44
+28
+10
+28
+44
+82
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 5 0 1
+BITMAP
+76
+88
+88
+76
+70
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 7 5 0 1
+BITMAP
+88
+04
+04
+04
+74
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 891 0
+DWIDTH 13 0
+BBX 9 2 2 0
+BITMAP
+cc80
+8480
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 1 20 4 -6
+BITMAP
+00
+00
+80
+80
+80
+00
+00
+00
+00
+80
+00
+00
+00
+00
+00
+80
+00
+80
+00
+00
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 1028 0
+DWIDTH 15 0
+BBX 15 1 0 3
+BITMAP
+fe00
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 8 9 0 0
+BITMAP
+fe
+00
+fe
+72
+9c
+00
+72
+9c
+88
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 822 0
+DWIDTH 12 0
+BBX 8 10 2 0
+BITMAP
+80
+88
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 8 12 1 -1
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 822 0
+DWIDTH 12 0
+BBX 10 11 1 0
+BITMAP
+80c0
+fe00
+0100
+0100
+0140
+ff40
+88c0
+6600
+7480
+8c40
+63c0
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 822 0
+DWIDTH 12 0
+BBX 9 12 1 -3
+BITMAP
+3f00
+4200
+2400
+0300
+6180
+1a00
+7300
+9d80
+8980
+8b00
+ce00
+6a00
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+2900
+0900
+0900
+6900
+b080
+1000
+2300
+4580
+8880
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+9080
+d480
+6900
+6b00
+b600
+9000
+9000
+6000
+1c00
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 822 0
+DWIDTH 12 0
+BBX 11 11 0 0
+BITMAP
+6300
+6300
+9480
+8880
+9480
+6300
+6300
+1c00
+1c00
+6300
+4900
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 10 7 0 0
+BITMAP
+8880
+be80
+8880
+4900
+6300
+1c00
+1f00
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 10 7 0 0
+BITMAP
+60c0
+40c0
+8100
+8200
+8400
+8800
+9000
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 7 0 0
+BITMAP
+6000
+6080
+9f00
+1e00
+6180
+4080
+8000
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 9 1 -2
+BITMAP
+8000
+8000
+8000
+8000
+8000
+8000
+8000
+4080
+6180
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 9 0 -1
+BITMAP
+1e00
+fe00
+0100
+0080
+0080
+0080
+0100
+fe00
+fe00
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 7 1 0
+BITMAP
+0100
+0080
+0080
+0080
+0100
+fe00
+0000
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 9 0 -2
+BITMAP
+ff80
+0100
+3f80
+4200
+8200
+8400
+8400
+4800
+3f80
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 7 7 1 0
+BITMAP
+10
+00
+3e
+80
+40
+00
+80
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 7 9 0 -1
+BITMAP
+00
+80
+00
+80
+00
+40
+00
+3e
+80
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 11 10 0 0
+BITMAP
+3f80
+4000
+8000
+8000
+8000
+4000
+3f80
+0000
+ff80
+3e40
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 11 0 0
+BITMAP
+8080
+8000
+3e00
+3e00
+9080
+9000
+3e00
+0000
+0080
+0100
+0200
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 822 0
+DWIDTH 12 0
+BBX 10 10 1 0
+BITMAP
+0400
+0800
+1000
+2000
+4000
+ffc0
+ff80
+8080
+4100
+4100
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 822 0
+DWIDTH 12 0
+BBX 10 10 1 0
+BITMAP
+2200
+2200
+2200
+1400
+1400
+0800
+0800
+1e00
+2100
+7c80
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 11 6 0 4
+BITMAP
+9240
+9240
+9c40
+9240
+7980
+2100
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 822 0
+DWIDTH 12 0
+BBX 10 12 1 -1
+BITMAP
+1e00
+1e00
+2100
+4e80
+9240
+9040
+9040
+9240
+4c80
+2100
+1e00
+fa80
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 548 0
+DWIDTH 8 0
+BBX 8 14 0 0
+BITMAP
+aa
+80
+26
+c0
+25
+40
+25
+40
+76
+e0
+ff
+c0
+40
+80
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 274 0
+DWIDTH 4 0
+BBX 1 2 1 3
+BITMAP
+00
+80
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 5 0 0
+BITMAP
+4080
+4080
+4080
+4080
+4080
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+40
+80
+40
+80
+40
+80
+e1
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 8 7 0 0
+BITMAP
+c0
+01
+01
+01
+02
+02
+02
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 1028 0
+DWIDTH 15 0
+BBX 13 7 1 0
+BITMAP
+0260
+a420
+1410
+0808
+8080
+ff80
+0080
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 960 0
+DWIDTH 14 0
+BBX 13 7 0 0
+BITMAP
+0080
+0080
+0080
+1818
+2420
+4240
+8180
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+42
+42
+24
+24
+18
+18
+10
+40
+20
+20
+7e
+f0
+c0
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 960 0
+DWIDTH 14 0
+BBX 13 7 1 0
+BITMAP
+1878
+f020
+2010
+4010
+0020
+0078
+f8c0
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 617 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+00
+7e
+f8
+20
+00
+10
+00
+10
+38
+6c
+aa
+28
+28
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 7 11 0 0
+BITMAP
+28
+28
+28
+28
+28
+28
+28
+00
+40
+00
+20
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 342 0
+DWIDTH 5 0
+BBX 4 15 0 -4
+BITMAP
+f0
+f0
+00
+10
+f0
+f0
+00
+20
+00
+40
+20
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 822 0
+DWIDTH 12 0
+BBX 10 10 1 0
+BITMAP
+2800
+2800
+aa40
+3800
+1000
+2840
+4480
+4440
+2800
+1000
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 822 0
+DWIDTH 12 0
+BBX 10 10 1 0
+BITMAP
+2000
+4040
+4080
+8080
+4040
+4000
+2000
+1e00
+2100
+5c80
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 754 0
+DWIDTH 11 0
+BBX 10 6 0 4
+BITMAP
+9240
+9240
+9c40
+9240
+5280
+2100
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 9 12 0 -1
+BITMAP
+1e00
+1e00
+2100
+4c80
+9200
+9000
+9000
+9200
+4c80
+2100
+1e00
+fa80
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 4 20 1 -6
+BITMAP
+20
+80
+20
+c0
+20
+40
+20
+40
+20
+40
+f0
+00
+80
+00
+40
+00
+20
+00
+10
+00
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 1 20 1 -6
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+80
+80
+80
+80
+00
+00
+00
+00
+80
+80
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 4 20 1 -6
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 4 20 1 -6
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 1 20 1 -6
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+00
+00
+00
+00
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 4 17 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 4 20 3 -6
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 3 20 1 -6
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+e0
+20
+40
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 4 17 3 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+20
+20
+20
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 1 20 3 -6
+BITMAP
+00
+00
+00
+00
+00
+00
+80
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+80
+80
+80
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 342 0
+DWIDTH 5 0
+BBX 4 15 1 -4
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+40
+30
+80
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 274 0
+DWIDTH 4 0
+BBX 5 13 0 -1
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 5 20 5 -6
+BITMAP
+80
+80
+80
+80
+80
+80
+00
+80
+40
+40
+20
+20
+20
+10
+10
+10
+20
+20
+20
+40
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 1 20 1 -6
+BITMAP
+00
+80
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+80
+80
+00
+00
+00
+80
+80
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 685 0
+DWIDTH 10 0
+BBX 5 17 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 4 20 1 -6
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+00
+00
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 1 20 4 -6
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+80
+80
+80
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 4 20 1 -6
+BITMAP
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 4 20 1 -6
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+10
+10
+10
+10
+10
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 1 20 4 -6
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+80
+80
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 411 0
+DWIDTH 6 0
+BBX 4 17 1 -3
+BITMAP
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+80
+80
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 4 20 0 -6
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+10
+10
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 3 20 3 -6
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+e0
+c0
+20
+00
+00
+00
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 480 0
+DWIDTH 7 0
+BBX 4 17 0 -3
+BITMAP
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
+80
+80
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol14.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol14.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol14.h	(revision 22322)
@@ -0,0 +1,859 @@
+static unsigned char symbol14_0_bits[] = {
+0x00};
+static unsigned char symbol14_1_bits[] = {
+0x00};
+static unsigned char symbol14_2_bits[] = {
+0x00};
+static unsigned char symbol14_3_bits[] = {
+0x00};
+static unsigned char symbol14_4_bits[] = {
+0x00};
+static unsigned char symbol14_5_bits[] = {
+0x00};
+static unsigned char symbol14_6_bits[] = {
+0x00};
+static unsigned char symbol14_7_bits[] = {
+0x00};
+static unsigned char symbol14_8_bits[] = {
+0x00};
+static unsigned char symbol14_9_bits[] = {
+0x00};
+static unsigned char symbol14_10_bits[] = {
+0x00};
+static unsigned char symbol14_11_bits[] = {
+0x00};
+static unsigned char symbol14_12_bits[] = {
+0x00};
+static unsigned char symbol14_13_bits[] = {
+0x00};
+static unsigned char symbol14_14_bits[] = {
+0x00};
+static unsigned char symbol14_15_bits[] = {
+0x00};
+static unsigned char symbol14_16_bits[] = {
+0x00};
+static unsigned char symbol14_17_bits[] = {
+0x00};
+static unsigned char symbol14_18_bits[] = {
+0x00};
+static unsigned char symbol14_19_bits[] = {
+0x00};
+static unsigned char symbol14_20_bits[] = {
+0x00};
+static unsigned char symbol14_21_bits[] = {
+0x00};
+static unsigned char symbol14_22_bits[] = {
+0x00};
+static unsigned char symbol14_23_bits[] = {
+0x00};
+static unsigned char symbol14_24_bits[] = {
+0x00};
+static unsigned char symbol14_25_bits[] = {
+0x00};
+static unsigned char symbol14_26_bits[] = {
+0x00};
+static unsigned char symbol14_27_bits[] = {
+0x00};
+static unsigned char symbol14_28_bits[] = {
+0x00};
+static unsigned char symbol14_29_bits[] = {
+0x00};
+static unsigned char symbol14_30_bits[] = {
+0x00};
+static unsigned char symbol14_31_bits[] = {
+0x00};
+static unsigned char symbol14_32_bits[] = {
+0x00};
+static unsigned char symbol14_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01};
+static unsigned char symbol14_34_bits[] = {
+0x01, 0x01, 0x82, 0x00, 0x82, 0x00, 0x7c, 0x00, 0x44, 0x00, 0x44, 0x00, 
+0x28, 0x00, 0x28, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char symbol14_35_bits[] = {
+0x28, 0x28, 0x28, 0x7e, 0x14, 0x14, 0x3f, 0x0a, 0x0a, 0x0a};
+static unsigned char symbol14_36_bits[] = {
+0x3f, 0x20, 0x20, 0x20, 0x3f, 0x20, 0x20, 0x20, 0x20, 0x3f};
+static unsigned char symbol14_37_bits[] = {
+0x9e, 0x01, 0xe9, 0x00, 0x49, 0x00, 0x29, 0x00, 0x36, 0x00, 0xd8, 0x00, 
+0x28, 0x01, 0x24, 0x01, 0x22, 0x01, 0xc3, 0x00};
+static unsigned char symbol14_38_bits[] = {
+0x0c, 0x00, 0x12, 0x00, 0x12, 0x00, 0x0e, 0x00, 0xe4, 0x00, 0x4e, 0x00, 
+0x53, 0x00, 0x21, 0x00, 0x73, 0x01, 0xde, 0x00};
+static unsigned char symbol14_39_bits[] = {
+0x07, 0x08, 0x10, 0x1e, 0x10, 0x08, 0x07};
+static unsigned char symbol14_40_bits[] = {
+0x04, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 
+0x04};
+static unsigned char symbol14_41_bits[] = {
+0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 
+0x01};
+static unsigned char symbol14_42_bits[] = {
+0x04, 0x15, 0x0e, 0x0e, 0x15, 0x04};
+static unsigned char symbol14_43_bits[] = {
+0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08};
+static unsigned char symbol14_44_bits[] = {
+0x02, 0x02, 0x02, 0x01};
+static unsigned char symbol14_45_bits[] = {
+0x7f};
+static unsigned char symbol14_46_bits[] = {
+0x01, 0x01};
+static unsigned char symbol14_47_bits[] = {
+0x08, 0x08, 0x04, 0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01};
+static unsigned char symbol14_48_bits[] = {
+0x1e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char symbol14_49_bits[] = {
+0x04, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char symbol14_50_bits[] = {
+0x0e, 0x1b, 0x11, 0x10, 0x18, 0x08, 0x04, 0x02, 0x23, 0x3f};
+static unsigned char symbol14_51_bits[] = {
+0x1e, 0x33, 0x21, 0x30, 0x1c, 0x30, 0x20, 0x20, 0x33, 0x1e};
+static unsigned char symbol14_52_bits[] = {
+0x10, 0x18, 0x18, 0x14, 0x16, 0x12, 0x11, 0x3f, 0x10, 0x10};
+static unsigned char symbol14_53_bits[] = {
+0x1e, 0x02, 0x01, 0x07, 0x0c, 0x18, 0x10, 0x10, 0x09, 0x07};
+static unsigned char symbol14_54_bits[] = {
+0x38, 0x0c, 0x02, 0x02, 0x1f, 0x31, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char symbol14_55_bits[] = {
+0x3f, 0x21, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02};
+static unsigned char symbol14_56_bits[] = {
+0x0e, 0x1b, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char symbol14_57_bits[] = {
+0x1e, 0x33, 0x21, 0x21, 0x23, 0x1e, 0x18, 0x08, 0x06, 0x03};
+static unsigned char symbol14_58_bits[] = {
+0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01};
+static unsigned char symbol14_59_bits[] = {
+0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x01};
+static unsigned char symbol14_60_bits[] = {
+0x40, 0x30, 0x0c, 0x03, 0x0c, 0x30, 0x40};
+static unsigned char symbol14_61_bits[] = {
+0x7f, 0x00, 0x7f};
+static unsigned char symbol14_62_bits[] = {
+0x01, 0x06, 0x18, 0x60, 0x18, 0x06, 0x01};
+static unsigned char symbol14_63_bits[] = {
+0x0e, 0x11, 0x11, 0x10, 0x08, 0x04, 0x04, 0x00, 0x04, 0x04};
+static unsigned char symbol14_64_bits[] = {
+0x4e, 0x39, 0x00, 0x7f, 0x00, 0x00, 0x7f};
+static unsigned char symbol14_65_bits[] = {
+0x10, 0x00, 0x38, 0x00, 0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 
+0x7c, 0x00, 0xc6, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char symbol14_66_bits[] = {
+0x3f, 0x62, 0x42, 0x62, 0x3e, 0x62, 0x42, 0x42, 0x62, 0x3f};
+static unsigned char symbol14_67_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 0x6c, 0x00, 0x38, 0x00, 0x28, 0x00, 
+0x44, 0x00, 0xc6, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char symbol14_68_bits[] = {
+0x08, 0x08, 0x14, 0x14, 0x14, 0x22, 0x22, 0x22, 0x41, 0x7f};
+static unsigned char symbol14_69_bits[] = {
+0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 0x42, 0x7f};
+static unsigned char symbol14_70_bits[] = {
+0x38, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x93, 0x01, 0x11, 0x01, 0x11, 0x01, 
+0x93, 0x01, 0xfe, 0x00, 0x10, 0x00, 0x38, 0x00};
+static unsigned char symbol14_71_bits[] = {
+0x7f, 0x42, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char symbol14_72_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char symbol14_73_bits[] = {
+0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char symbol14_74_bits[] = {
+0x30, 0x50, 0x50, 0x66, 0xc5, 0x44, 0x44, 0x44, 0x6c, 0x38};
+static unsigned char symbol14_75_bits[] = {
+0x77, 0x22, 0x12, 0x0a, 0x0e, 0x0a, 0x12, 0x22, 0x42, 0xe7};
+static unsigned char symbol14_76_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 0x6c, 0x00, 0x44, 0x00, 0x44, 0x00, 
+0xc6, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char symbol14_77_bits[] = {
+0x07, 0x07, 0x06, 0x03, 0x8a, 0x02, 0x8a, 0x02, 0xda, 0x02, 0x52, 0x02, 
+0x52, 0x02, 0x72, 0x02, 0x22, 0x02, 0x27, 0x07};
+static unsigned char symbol14_78_bits[] = {
+0xc7, 0x01, 0x86, 0x00, 0x8a, 0x00, 0x9a, 0x00, 0x92, 0x00, 0xb2, 0x00, 
+0xa2, 0x00, 0xe2, 0x00, 0xc2, 0x00, 0x87, 0x00};
+static unsigned char symbol14_79_bits[] = {
+0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c};
+static unsigned char symbol14_80_bits[] = {
+0xff, 0x01, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char symbol14_81_bits[] = {
+0x3c, 0x66, 0x42, 0xa5, 0xbd, 0xa5, 0x81, 0x42, 0x66, 0x3c};
+static unsigned char symbol14_82_bits[] = {
+0x1f, 0x32, 0x22, 0x22, 0x32, 0x1e, 0x02, 0x02, 0x02, 0x07};
+static unsigned char symbol14_83_bits[] = {
+0x7f, 0x43, 0x06, 0x0c, 0x18, 0x08, 0x04, 0x02, 0x83, 0xff};
+static unsigned char symbol14_84_bits[] = {
+0x7f, 0x49, 0x49, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c};
+static unsigned char symbol14_85_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x38, 0x00};
+static unsigned char symbol14_86_bits[] = {
+0x1c, 0x03, 0x01, 0x01, 0x01, 0x06, 0x1c, 0x20, 0x24, 0x1c};
+static unsigned char symbol14_87_bits[] = {
+0x30, 0x00, 0xcc, 0x00, 0x84, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 
+0x02, 0x01, 0x84, 0x00, 0x49, 0x02, 0xcf, 0x03};
+static unsigned char symbol14_88_bits[] = {
+0xfe, 0x00, 0x82, 0x00, 0x00, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x44, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0x01};
+static unsigned char symbol14_89_bits[] = {
+0x73, 0x06, 0x26, 0x03, 0x24, 0x01, 0x24, 0x01, 0xf8, 0x00, 0x20, 0x00, 
+0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00};
+static unsigned char symbol14_90_bits[] = {
+0x7f, 0x41, 0x20, 0x10, 0x08, 0x08, 0x04, 0x02, 0x41, 0x7f};
+static unsigned char symbol14_91_bits[] = {
+0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x07};
+static unsigned char symbol14_92_bits[] = {
+0x18, 0x18, 0x00, 0x00, 0x00, 0xc3, 0xc3};
+static unsigned char symbol14_93_bits[] = {
+0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x07};
+static unsigned char symbol14_94_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xff, 0x01};
+static unsigned char symbol14_95_bits[] = {
+0x7f};
+static unsigned char symbol14_96_bits[] = {
+0xff};
+static unsigned char symbol14_97_bits[] = {
+0x6e, 0x2b, 0x11, 0x11, 0x11, 0xab, 0x66};
+static unsigned char symbol14_98_bits[] = {
+0x1e, 0x33, 0x21, 0x31, 0x19, 0x31, 0x21, 0x21, 0x21, 0x33, 0x1d, 0x01, 
+0x01, 0x01};
+static unsigned char symbol14_99_bits[] = {
+0xc3, 0x65, 0x24, 0x3c, 0x18, 0x1c, 0x14, 0x26, 0xa2, 0xc3};
+static unsigned char symbol14_100_bits[] = {
+0x0e, 0x19, 0x01, 0x02, 0x0e, 0x19, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char symbol14_101_bits[] = {
+0x1e, 0x13, 0x01, 0x06, 0x01, 0x13, 0x0e};
+static unsigned char symbol14_102_bits[] = {
+0x08, 0x08, 0x08, 0x1c, 0x2a, 0x49, 0x49, 0x49, 0x2a, 0x1c, 0x08, 0x08, 
+0x08};
+static unsigned char symbol14_103_bits[] = {
+0x23, 0x25, 0x24, 0x14, 0x14, 0x18, 0x18, 0x08, 0x0c, 0x0c};
+static unsigned char symbol14_104_bits[] = {
+0x36, 0x4d, 0x44, 0x44, 0x44, 0x44, 0x44, 0x40, 0x40, 0x40};
+static unsigned char symbol14_105_bits[] = {
+0x06, 0x05, 0x04, 0x04, 0x04, 0x14, 0x0c};
+static unsigned char symbol14_106_bits[] = {
+0x18, 0x2a, 0x4b, 0x49, 0x49, 0x2a, 0x1c, 0x08, 0x08, 0x08};
+static unsigned char symbol14_107_bits[] = {
+0x62, 0x53, 0x0a, 0x0e, 0x1a, 0x32, 0x62};
+static unsigned char symbol14_108_bits[] = {
+0x06, 0x0a, 0x08, 0x08, 0x14, 0x14, 0x12, 0x22, 0xa1, 0xc1};
+static unsigned char symbol14_109_bits[] = {
+0x11, 0x11, 0x11, 0x11, 0x11, 0x59, 0x6f, 0x01, 0x03};
+static unsigned char symbol14_110_bits[] = {
+0x63, 0x42, 0x44, 0x44, 0x28, 0x28, 0x10};
+static unsigned char symbol14_111_bits[] = {
+0x1e, 0x33, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char symbol14_112_bits[] = {
+0x7e, 0x25, 0x24, 0x24, 0x24, 0xa4, 0x66};
+static unsigned char symbol14_113_bits[] = {
+0x0e, 0x1b, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char symbol14_114_bits[] = {
+0x1e, 0x33, 0x21, 0x21, 0x21, 0x33, 0x1d, 0x01, 0x01, 0x01};
+static unsigned char symbol14_115_bits[] = {
+0x7e, 0x13, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char symbol14_116_bits[] = {
+0x1e, 0x05, 0x04, 0x04, 0x04, 0x14, 0x0c};
+static unsigned char symbol14_117_bits[] = {
+0x26, 0x45, 0x44, 0x44, 0x44, 0x64, 0x38};
+static unsigned char symbol14_118_bits[] = {
+0xfe, 0x03, 0x05, 0x01, 0x22, 0x02, 0x22, 0x02, 0x22, 0x02, 0x22, 0x02, 
+0x26, 0x03, 0xdc, 0x01};
+static unsigned char symbol14_119_bits[] = {
+0xc6, 0x00, 0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x93, 0x01, 
+0xee, 0x00};
+static unsigned char symbol14_120_bits[] = {
+0x04, 0x02, 0x1c, 0x04, 0x02, 0x1e, 0x04, 0x02, 0x01, 0x01, 0x03, 0x1e, 
+0x20, 0x24, 0x1c};
+static unsigned char symbol14_121_bits[] = {
+0x11, 0x01, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0xd6, 0x00, 0x7c, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char symbol14_122_bits[] = {
+0x04, 0x22, 0x3c, 0x08, 0x04, 0x02, 0x02, 0x01, 0x01, 0x03, 0x1e, 0x20, 
+0x24, 0x1c};
+static unsigned char symbol14_123_bits[] = {
+0x18, 0x04, 0x04, 0x04, 0x04, 0x02, 0x01, 0x02, 0x04, 0x04, 0x04, 0x04, 
+0x18};
+static unsigned char symbol14_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01};
+static unsigned char symbol14_125_bits[] = {
+0x03, 0x04, 0x04, 0x04, 0x04, 0x08, 0x10, 0x08, 0x04, 0x04, 0x04, 0x04, 
+0x03};
+static unsigned char symbol14_126_bits[] = {
+0x46, 0x49, 0x31};
+static unsigned char symbol14_127_bits[] = {
+0x00};
+static unsigned char symbol14_128_bits[] = {
+0x00};
+static unsigned char symbol14_129_bits[] = {
+0x00};
+static unsigned char symbol14_130_bits[] = {
+0x00};
+static unsigned char symbol14_131_bits[] = {
+0x00};
+static unsigned char symbol14_132_bits[] = {
+0x00};
+static unsigned char symbol14_133_bits[] = {
+0x00};
+static unsigned char symbol14_134_bits[] = {
+0x00};
+static unsigned char symbol14_135_bits[] = {
+0x00};
+static unsigned char symbol14_136_bits[] = {
+0x00};
+static unsigned char symbol14_137_bits[] = {
+0x00};
+static unsigned char symbol14_138_bits[] = {
+0x00};
+static unsigned char symbol14_139_bits[] = {
+0x00};
+static unsigned char symbol14_140_bits[] = {
+0x00};
+static unsigned char symbol14_141_bits[] = {
+0x00};
+static unsigned char symbol14_142_bits[] = {
+0x00};
+static unsigned char symbol14_143_bits[] = {
+0x00};
+static unsigned char symbol14_144_bits[] = {
+0x00};
+static unsigned char symbol14_145_bits[] = {
+0x00};
+static unsigned char symbol14_146_bits[] = {
+0x00};
+static unsigned char symbol14_147_bits[] = {
+0x00};
+static unsigned char symbol14_148_bits[] = {
+0x00};
+static unsigned char symbol14_149_bits[] = {
+0x00};
+static unsigned char symbol14_150_bits[] = {
+0x00};
+static unsigned char symbol14_151_bits[] = {
+0x00};
+static unsigned char symbol14_152_bits[] = {
+0x00};
+static unsigned char symbol14_153_bits[] = {
+0x00};
+static unsigned char symbol14_154_bits[] = {
+0x00};
+static unsigned char symbol14_155_bits[] = {
+0x00};
+static unsigned char symbol14_156_bits[] = {
+0x00};
+static unsigned char symbol14_157_bits[] = {
+0x00};
+static unsigned char symbol14_158_bits[] = {
+0x00};
+static unsigned char symbol14_159_bits[] = {
+0x00};
+static unsigned char symbol14_160_bits[] = {
+0x00};
+static unsigned char symbol14_161_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol14_162_bits[] = {
+0x00, 0x00, 0x00};
+static unsigned char symbol14_163_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol14_164_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol14_165_bits[] = {
+0x00, 0x00, 0x63, 0x00, 0xd4, 0x00, 0x08, 0x00};
+static unsigned char symbol14_166_bits[] = {
+0x08, 0x08, 0x08, 0x1c, 0x0c, 0x06, 0x01, 0x40, 0x30, 0x0c, 0x03, 0x0c, 
+0x30};
+static unsigned char symbol14_167_bits[] = {
+0x40, 0x00, 0x7f, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x01, 
+0x01, 0x00, 0x00, 0x01};
+static unsigned char symbol14_168_bits[] = {
+0x01, 0x31, 0x01, 0x6e, 0x00, 0x60, 0x50};
+static unsigned char symbol14_169_bits[] = {
+0x10, 0x10, 0x3c, 0x08, 0x08, 0x08, 0x04};
+static unsigned char symbol14_170_bits[] = {
+0x04, 0x00, 0x05, 0x01, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0xd7, 0x01, 
+0xff, 0x01, 0xd7, 0x01};
+static unsigned char symbol14_171_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x08, 0x1c, 0x3e, 0x1f, 0x3e, 0x1c, 0x08, 0x16, 
+0x7f, 0x1f};
+static unsigned char symbol14_172_bits[] = {
+0x7f, 0x3e, 0x1c, 0x08, 0x10, 0x00, 0x38, 0x00, 0x7c, 0x00, 0xfe, 0x00, 
+0xff, 0x01};
+static unsigned char symbol14_173_bits[] = {
+0x56, 0x00, 0x10, 0x00, 0x10, 0x00, 0x08, 0x02, 0x04, 0x04, 0x02, 0x08, 
+0x7f, 0x1f, 0x02, 0x08, 0x04, 0x04, 0x08, 0x02};
+static unsigned char symbol14_174_bits[] = {
+0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0xff, 0x3f, 0x02, 0x00, 0x04, 0x00, 
+0x08, 0x00};
+static unsigned char symbol14_175_bits[] = {
+0x08, 0x1c, 0x2a, 0x49, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x08, 0x08};
+static unsigned char symbol14_176_bits[] = {
+0x08, 0x08, 0x08, 0x00};
+static unsigned char symbol14_177_bits[] = {
+0x04, 0x00, 0x08, 0x00, 0x10, 0x7f, 0x3f, 0x00, 0x10};
+static unsigned char symbol14_178_bits[] = {
+0x00, 0x08, 0x00};
+static unsigned char symbol14_179_bits[] = {
+0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
+static unsigned char symbol14_180_bits[] = {
+0x08, 0x08, 0x08, 0x08, 0x08, 0x49, 0x2a};
+static unsigned char symbol14_181_bits[] = {
+0x1c, 0x08, 0x06, 0x09};
+static unsigned char symbol14_182_bits[] = {
+0x09, 0x06, 0x08, 0x08, 0x08, 0x3f, 0x08, 0x08, 0x08, 0x00, 0x3f};
+static unsigned char symbol14_183_bits[] = {
+0x0c, 0x16, 0x09, 0x01, 0x06};
+static unsigned char symbol14_184_bits[] = {
+0x18, 0x60, 0x18, 0x06, 0x01, 0x00, 0x7f};
+static unsigned char symbol14_185_bits[] = {
+0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41};
+static unsigned char symbol14_186_bits[] = {
+0x6e, 0x11, 0x11, 0x6e, 0x0e};
+static unsigned char symbol14_187_bits[] = {
+0x11, 0x20, 0x20, 0x20, 0x2e};
+static unsigned char symbol14_188_bits[] = {
+0x33, 0x01, 0x21, 0x01};
+static unsigned char symbol14_189_bits[] = {
+0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00};
+static unsigned char symbol14_190_bits[] = {
+0x7f, 0x00};
+static unsigned char symbol14_191_bits[] = {
+0x7f, 0x00, 0x7f, 0x4e, 0x39, 0x00, 0x4e, 0x39, 0x11};
+static unsigned char symbol14_192_bits[] = {
+0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol14_193_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol14_194_bits[] = {
+0x01, 0x03, 0x7f, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x02, 0xff, 0x02, 
+0x11, 0x03, 0x66, 0x00, 0x2e, 0x01, 0x31, 0x02, 0xc6, 0x03};
+static unsigned char symbol14_195_bits[] = {
+0xfc, 0x00, 0x42, 0x00, 0x24, 0x00, 0xc0, 0x00, 0x86, 0x01, 0x58, 0x00, 
+0xce, 0x00, 0xb9, 0x01, 0x91, 0x01, 0xd1, 0x00, 0x73, 0x00, 0x56, 0x00};
+static unsigned char symbol14_196_bits[] = {
+0x94, 0x00, 0x90, 0x00, 0x90, 0x00, 0x96, 0x00, 0x0d, 0x01, 0x08, 0x00, 
+0xc4, 0x00, 0xa2, 0x01, 0x11, 0x01};
+static unsigned char symbol14_197_bits[] = {
+0x09, 0x01, 0x2b, 0x01, 0x96, 0x00, 0xd6, 0x00, 0x6d, 0x00, 0x09, 0x00, 
+0x09, 0x00, 0x06, 0x00, 0x38, 0x00};
+static unsigned char symbol14_198_bits[] = {
+0xc6, 0x00, 0xc6, 0x00, 0x29, 0x01, 0x11, 0x01, 0x29, 0x01, 0xc6, 0x00, 
+0xc6, 0x00, 0x38, 0x00, 0x38, 0x00, 0xc6, 0x00, 0x92, 0x00};
+static unsigned char symbol14_199_bits[] = {
+0x11, 0x01, 0x7d, 0x01, 0x11, 0x01, 0x92, 0x00, 0xc6, 0x00, 0x38, 0x00, 
+0xf8, 0x00};
+static unsigned char symbol14_200_bits[] = {
+0x06, 0x03, 0x02, 0x03, 0x81, 0x00, 0x41, 0x00, 0x21, 0x00, 0x11, 0x00, 
+0x09, 0x00};
+static unsigned char symbol14_201_bits[] = {
+0x06, 0x00, 0x06, 0x01, 0xf9, 0x00, 0x78, 0x00, 0x86, 0x01, 0x02, 0x01, 
+0x01, 0x00};
+static unsigned char symbol14_202_bits[] = {
+0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x02, 0x01, 0x86, 0x01};
+static unsigned char symbol14_203_bits[] = {
+0x78, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 
+0x80, 0x00, 0x7f, 0x00, 0x7f, 0x00};
+static unsigned char symbol14_204_bits[] = {
+0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x80, 0x00, 0x7f, 0x00, 
+0x00, 0x00};
+static unsigned char symbol14_205_bits[] = {
+0xff, 0x01, 0x80, 0x00, 0xfc, 0x01, 0x42, 0x00, 0x41, 0x00, 0x21, 0x00, 
+0x21, 0x00, 0x12, 0x00, 0xfc, 0x01};
+static unsigned char symbol14_206_bits[] = {
+0x08, 0x00, 0x7c, 0x01, 0x02, 0x00, 0x01};
+static unsigned char symbol14_207_bits[] = {
+0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01};
+static unsigned char symbol14_208_bits[] = {
+0xfc, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 
+0xfc, 0x01, 0x00, 0x00, 0xff, 0x01, 0x7c, 0x02};
+static unsigned char symbol14_209_bits[] = {
+0x01, 0x01, 0x01, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x09, 0x01, 0x09, 0x00, 
+0x7c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x40, 0x00};
+static unsigned char symbol14_210_bits[] = {
+0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0xff, 0x03, 
+0xff, 0x01, 0x01, 0x01, 0x82, 0x00, 0x82, 0x00};
+static unsigned char symbol14_211_bits[] = {
+0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x28, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x78, 0x00, 0x84, 0x00, 0x3e, 0x01};
+static unsigned char symbol14_212_bits[] = {
+0x49, 0x02, 0x49, 0x02, 0x39, 0x02, 0x49, 0x02, 0x9e, 0x01, 0x84, 0x00};
+static unsigned char symbol14_213_bits[] = {
+0x78, 0x00, 0x78, 0x00, 0x84, 0x00, 0x72, 0x01, 0x49, 0x02, 0x09, 0x02, 
+0x09, 0x02, 0x49, 0x02, 0x32, 0x01, 0x84, 0x00, 0x78, 0x00, 0x5f, 0x01};
+static unsigned char symbol14_214_bits[] = {
+0x55, 0x01, 0x64, 0x03, 0xa4, 0x02, 0xa4, 0x02, 0x6e, 0x07, 0xff, 0x03, 
+0x02, 0x01};
+static unsigned char symbol14_215_bits[] = {
+0x00, 0x01};
+static unsigned char symbol14_216_bits[] = {
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01};
+static unsigned char symbol14_217_bits[] = {
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x87};
+static unsigned char symbol14_218_bits[] = {
+0x03, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40};
+static unsigned char symbol14_219_bits[] = {
+0x40, 0x06, 0x25, 0x04, 0x28, 0x08, 0x10, 0x10, 0x01, 0x01, 0xff, 0x01, 
+0x00, 0x01};
+static unsigned char symbol14_220_bits[] = {
+0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x18, 0x18, 0x24, 0x04, 0x42, 0x02, 
+0x81, 0x01};
+static unsigned char symbol14_221_bits[] = {
+0x42, 0x42, 0x24, 0x24, 0x18, 0x18, 0x08, 0x02, 0x04, 0x04, 0x7e, 0x0f, 
+0x03};
+static unsigned char symbol14_222_bits[] = {
+0x18, 0x1e, 0x0f, 0x04, 0x04, 0x08, 0x02, 0x08, 0x00, 0x04, 0x00, 0x1e, 
+0x1f, 0x03};
+static unsigned char symbol14_223_bits[] = {
+0x00, 0x7e, 0x1f, 0x04, 0x00, 0x08, 0x00, 0x08, 0x1c, 0x36, 0x55, 0x14, 
+0x14};
+static unsigned char symbol14_224_bits[] = {
+0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x02, 0x00, 0x04};
+static unsigned char symbol14_225_bits[] = {
+0x0f, 0x0f, 0x00, 0x08, 0x0f, 0x0f, 0x00, 0x04, 0x00, 0x02, 0x04, 0x04, 
+0x04, 0x04, 0x04};
+static unsigned char symbol14_226_bits[] = {
+0x14, 0x00, 0x14, 0x00, 0x55, 0x02, 0x1c, 0x00, 0x08, 0x00, 0x14, 0x02, 
+0x22, 0x01, 0x22, 0x02, 0x14, 0x00, 0x08, 0x00};
+static unsigned char symbol14_227_bits[] = {
+0x04, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 
+0x04, 0x00, 0x78, 0x00, 0x84, 0x00, 0x3a, 0x01};
+static unsigned char symbol14_228_bits[] = {
+0x49, 0x02, 0x49, 0x02, 0x39, 0x02, 0x49, 0x02, 0x4a, 0x01, 0x84, 0x00};
+static unsigned char symbol14_229_bits[] = {
+0x78, 0x00, 0x78, 0x00, 0x84, 0x00, 0x32, 0x01, 0x49, 0x00, 0x09, 0x00, 
+0x09, 0x00, 0x49, 0x00, 0x32, 0x01, 0x84, 0x00, 0x78, 0x00, 0x5f, 0x01};
+static unsigned char symbol14_230_bits[] = {
+0x04, 0x01, 0x04, 0x03, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x0f, 0x00, 
+0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00};
+static unsigned char symbol14_231_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 
+0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01};
+static unsigned char symbol14_232_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol14_233_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol14_234_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 
+0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol14_235_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol14_236_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol14_237_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x07, 0x04, 0x02, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol14_238_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x04, 0x04, 0x04};
+static unsigned char symbol14_239_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01};
+static unsigned char symbol14_240_bits[] = {
+0x00};
+static unsigned char symbol14_241_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x02, 0x0c, 0x01};
+static unsigned char symbol14_242_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01};
+static unsigned char symbol14_243_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x02, 0x02, 0x04, 0x04, 
+0x04, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x02};
+static unsigned char symbol14_244_bits[] = {
+0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01};
+static unsigned char symbol14_245_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol14_246_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00};
+static unsigned char symbol14_247_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol14_248_bits[] = {
+0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol14_249_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x08, 0x08, 0x08, 0x08, 0x08};
+static unsigned char symbol14_250_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol14_251_bits[] = {
+0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x01, 0x01};
+static unsigned char symbol14_252_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x08};
+static unsigned char symbol14_253_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x07, 0x03, 0x04, 0x00, 0x00, 0x00};
+static unsigned char symbol14_254_bits[] = {
+0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 
+0x08, 0x08, 0x08, 0x01, 0x01};
+static unsigned char symbol14_255_bits[] = {
+0x00};
+static RotFont symbol14font[] = {
+{5, 1, 1, symbol14_0_bits},
+{5, 1, 1, symbol14_1_bits},
+{5, 1, 1, symbol14_2_bits},
+{5, 1, 1, symbol14_3_bits},
+{5, 1, 1, symbol14_4_bits},
+{5, 1, 1, symbol14_5_bits},
+{5, 1, 1, symbol14_6_bits},
+{5, 1, 1, symbol14_7_bits},
+{5, 1, 1, symbol14_8_bits},
+{5, 1, 1, symbol14_9_bits},
+{5, 1, 1, symbol14_10_bits},
+{5, 1, 1, symbol14_11_bits},
+{5, 1, 1, symbol14_12_bits},
+{5, 1, 1, symbol14_13_bits},
+{5, 1, 1, symbol14_14_bits},
+{5, 1, 1, symbol14_15_bits},
+{5, 1, 1, symbol14_16_bits},
+{5, 1, 1, symbol14_17_bits},
+{5, 1, 1, symbol14_18_bits},
+{5, 1, 1, symbol14_19_bits},
+{5, 1, 1, symbol14_20_bits},
+{5, 1, 1, symbol14_21_bits},
+{5, 1, 1, symbol14_22_bits},
+{5, 1, 1, symbol14_23_bits},
+{5, 1, 1, symbol14_24_bits},
+{5, 1, 1, symbol14_25_bits},
+{5, 1, 1, symbol14_26_bits},
+{5, 1, 1, symbol14_27_bits},
+{5, 1, 1, symbol14_28_bits},
+{5, 1, 1, symbol14_29_bits},
+{5, 1, 1, symbol14_30_bits},
+{5, 1, 1, symbol14_31_bits},
+{1, 1, 1, symbol14_32_bits},
+{1, 10, 10, symbol14_33_bits},
+{9, 10, 10, symbol14_34_bits},
+{7, 10, 10, symbol14_35_bits},
+{6, 10, 10, symbol14_36_bits},
+{9, 10, 10, symbol14_37_bits},
+{9, 10, 10, symbol14_38_bits},
+{5, 7, 7, symbol14_39_bits},
+{3, 13, 10, symbol14_40_bits},
+{3, 13, 10, symbol14_41_bits},
+{5, 6, 8, symbol14_42_bits},
+{7, 7, 7, symbol14_43_bits},
+{2, 4, 2, symbol14_44_bits},
+{7, 1, 4, symbol14_45_bits},
+{1, 2, 2, symbol14_46_bits},
+{4, 10, 10, symbol14_47_bits},
+{6, 10, 10, symbol14_48_bits},
+{5, 10, 10, symbol14_49_bits},
+{6, 10, 10, symbol14_50_bits},
+{6, 10, 10, symbol14_51_bits},
+{6, 10, 10, symbol14_52_bits},
+{5, 10, 10, symbol14_53_bits},
+{6, 10, 10, symbol14_54_bits},
+{6, 10, 10, symbol14_55_bits},
+{5, 10, 10, symbol14_56_bits},
+{6, 10, 10, symbol14_57_bits},
+{1, 7, 7, symbol14_58_bits},
+{2, 9, 7, symbol14_59_bits},
+{7, 7, 7, symbol14_60_bits},
+{7, 3, 5, symbol14_61_bits},
+{7, 7, 7, symbol14_62_bits},
+{5, 10, 10, symbol14_63_bits},
+{7, 7, 7, symbol14_64_bits},
+{9, 10, 10, symbol14_65_bits},
+{7, 10, 10, symbol14_66_bits},
+{9, 10, 10, symbol14_67_bits},
+{7, 10, 10, symbol14_68_bits},
+{7, 10, 10, symbol14_69_bits},
+{9, 10, 10, symbol14_70_bits},
+{7, 10, 10, symbol14_71_bits},
+{9, 10, 10, symbol14_72_bits},
+{3, 10, 10, symbol14_73_bits},
+{8, 10, 10, symbol14_74_bits},
+{8, 10, 10, symbol14_75_bits},
+{9, 10, 10, symbol14_76_bits},
+{11, 10, 10, symbol14_77_bits},
+{9, 10, 10, symbol14_78_bits},
+{8, 10, 10, symbol14_79_bits},
+{9, 10, 10, symbol14_80_bits},
+{8, 10, 10, symbol14_81_bits},
+{6, 10, 10, symbol14_82_bits},
+{8, 10, 10, symbol14_83_bits},
+{7, 10, 10, symbol14_84_bits},
+{9, 10, 10, symbol14_85_bits},
+{6, 10, 7, symbol14_86_bits},
+{10, 10, 10, symbol14_87_bits},
+{9, 10, 10, symbol14_88_bits},
+{11, 10, 10, symbol14_89_bits},
+{7, 10, 10, symbol14_90_bits},
+{3, 13, 10, symbol14_91_bits},
+{8, 7, 7, symbol14_92_bits},
+{3, 13, 10, symbol14_93_bits},
+{9, 10, 10, symbol14_94_bits},
+{7, 1, -2, symbol14_95_bits},
+{8, 1, 14, symbol14_96_bits},
+{8, 7, 7, symbol14_97_bits},
+{6, 14, 11, symbol14_98_bits},
+{8, 10, 7, symbol14_99_bits},
+{5, 11, 11, symbol14_100_bits},
+{5, 7, 7, symbol14_101_bits},
+{7, 13, 10, symbol14_102_bits},
+{6, 10, 7, symbol14_103_bits},
+{7, 10, 7, symbol14_104_bits},
+{5, 7, 7, symbol14_105_bits},
+{7, 10, 7, symbol14_106_bits},
+{7, 7, 7, symbol14_107_bits},
+{8, 10, 10, symbol14_108_bits},
+{7, 9, 7, symbol14_109_bits},
+{7, 7, 7, symbol14_110_bits},
+{6, 7, 7, symbol14_111_bits},
+{8, 7, 7, symbol14_112_bits},
+{5, 10, 10, symbol14_113_bits},
+{6, 10, 7, symbol14_114_bits},
+{7, 7, 7, symbol14_115_bits},
+{5, 7, 7, symbol14_116_bits},
+{7, 7, 7, symbol14_117_bits},
+{10, 8, 8, symbol14_118_bits},
+{9, 7, 7, symbol14_119_bits},
+{6, 15, 12, symbol14_120_bits},
+{9, 10, 7, symbol14_121_bits},
+{6, 14, 11, symbol14_122_bits},
+{5, 13, 10, symbol14_123_bits},
+{1, 13, 10, symbol14_124_bits},
+{5, 13, 10, symbol14_125_bits},
+{7, 3, 5, symbol14_126_bits},
+{5, 1, 1, symbol14_127_bits},
+{5, 1, 1, symbol14_128_bits},
+{5, 1, 1, symbol14_129_bits},
+{5, 1, 1, symbol14_130_bits},
+{5, 1, 1, symbol14_131_bits},
+{5, 1, 1, symbol14_132_bits},
+{5, 1, 1, symbol14_133_bits},
+{5, 1, 1, symbol14_134_bits},
+{5, 1, 1, symbol14_135_bits},
+{5, 1, 1, symbol14_136_bits},
+{5, 1, 1, symbol14_137_bits},
+{5, 1, 1, symbol14_138_bits},
+{5, 1, 1, symbol14_139_bits},
+{5, 1, 1, symbol14_140_bits},
+{5, 1, 1, symbol14_141_bits},
+{5, 1, 1, symbol14_142_bits},
+{5, 1, 1, symbol14_143_bits},
+{5, 1, 1, symbol14_144_bits},
+{5, 1, 1, symbol14_145_bits},
+{5, 1, 1, symbol14_146_bits},
+{5, 1, 1, symbol14_147_bits},
+{5, 1, 1, symbol14_148_bits},
+{5, 1, 1, symbol14_149_bits},
+{5, 1, 1, symbol14_150_bits},
+{5, 1, 1, symbol14_151_bits},
+{5, 1, 1, symbol14_152_bits},
+{5, 1, 1, symbol14_153_bits},
+{5, 1, 1, symbol14_154_bits},
+{5, 1, 1, symbol14_155_bits},
+{5, 1, 1, symbol14_156_bits},
+{5, 1, 1, symbol14_157_bits},
+{5, 1, 1, symbol14_158_bits},
+{5, 1, 1, symbol14_159_bits},
+{5, 1, 1, symbol14_160_bits},
+{8, 10, 10, symbol14_161_bits},
+{4, 3, 10, symbol14_162_bits},
+{7, 9, 9, symbol14_163_bits},
+{5, 10, 10, symbol14_164_bits},
+{9, 4, 6, symbol14_165_bits},
+{7, 13, 10, symbol14_166_bits},
+{9, 8, 7, symbol14_167_bits},
+{7, 7, 7, symbol14_168_bits},
+{7, 7, 7, symbol14_169_bits},
+{9, 8, 7, symbol14_170_bits},
+{13, 7, 7, symbol14_171_bits},
+{14, 7, 7, symbol14_172_bits},
+{7, 20, 14, symbol14_173_bits},
+{14, 7, 7, symbol14_174_bits},
+{7, 17, 14, symbol14_175_bits},
+{4, 4, 10, symbol14_176_bits},
+{7, 9, 9, symbol14_177_bits},
+{7, 3, 10, symbol14_178_bits},
+{7, 9, 9, symbol14_179_bits},
+{7, 7, 7, symbol14_180_bits},
+{8, 4, 6, symbol14_181_bits},
+{6, 11, 11, symbol14_182_bits},
+{5, 5, 6, symbol14_183_bits},
+{7, 7, 7, symbol14_184_bits},
+{7, 7, 7, symbol14_185_bits},
+{7, 5, 6, symbol14_186_bits},
+{7, 5, 6, symbol14_187_bits},
+{9, 2, 2, symbol14_188_bits},
+{1, 20, 14, symbol14_189_bits},
+{15, 1, 4, symbol14_190_bits},
+{8, 9, 9, symbol14_191_bits},
+{8, 10, 10, symbol14_192_bits},
+{8, 12, 11, symbol14_193_bits},
+{10, 11, 11, symbol14_194_bits},
+{9, 12, 9, symbol14_195_bits},
+{9, 9, 9, symbol14_196_bits},
+{9, 9, 9, symbol14_197_bits},
+{11, 11, 11, symbol14_198_bits},
+{10, 7, 7, symbol14_199_bits},
+{10, 7, 7, symbol14_200_bits},
+{9, 7, 7, symbol14_201_bits},
+{9, 9, 7, symbol14_202_bits},
+{9, 9, 8, symbol14_203_bits},
+{9, 7, 7, symbol14_204_bits},
+{9, 9, 7, symbol14_205_bits},
+{7, 7, 7, symbol14_206_bits},
+{7, 9, 8, symbol14_207_bits},
+{11, 10, 10, symbol14_208_bits},
+{9, 11, 11, symbol14_209_bits},
+{10, 10, 10, symbol14_210_bits},
+{10, 10, 10, symbol14_211_bits},
+{11, 6, 10, symbol14_212_bits},
+{10, 12, 11, symbol14_213_bits},
+{8, 14, 14, symbol14_214_bits},
+{1, 2, 5, symbol14_215_bits},
+{9, 5, 5, symbol14_216_bits},
+{8, 7, 7, symbol14_217_bits},
+{8, 7, 7, symbol14_218_bits},
+{13, 7, 7, symbol14_219_bits},
+{13, 7, 7, symbol14_220_bits},
+{7, 13, 13, symbol14_221_bits},
+{13, 7, 7, symbol14_222_bits},
+{7, 13, 13, symbol14_223_bits},
+{7, 11, 11, symbol14_224_bits},
+{4, 15, 11, symbol14_225_bits},
+{10, 10, 10, symbol14_226_bits},
+{10, 10, 10, symbol14_227_bits},
+{10, 6, 10, symbol14_228_bits},
+{9, 12, 11, symbol14_229_bits},
+{4, 20, 14, symbol14_230_bits},
+{1, 20, 14, symbol14_231_bits},
+{4, 20, 14, symbol14_232_bits},
+{4, 20, 14, symbol14_233_bits},
+{1, 20, 14, symbol14_234_bits},
+{4, 17, 14, symbol14_235_bits},
+{4, 20, 14, symbol14_236_bits},
+{3, 20, 14, symbol14_237_bits},
+{4, 17, 14, symbol14_238_bits},
+{1, 20, 14, symbol14_239_bits},
+{5, 1, 1, symbol14_240_bits},
+{4, 15, 11, symbol14_241_bits},
+{5, 13, 12, symbol14_242_bits},
+{5, 20, 14, symbol14_243_bits},
+{1, 20, 14, symbol14_244_bits},
+{5, 17, 14, symbol14_245_bits},
+{4, 20, 14, symbol14_246_bits},
+{1, 20, 14, symbol14_247_bits},
+{4, 20, 14, symbol14_248_bits},
+{4, 20, 14, symbol14_249_bits},
+{1, 20, 14, symbol14_250_bits},
+{4, 17, 14, symbol14_251_bits},
+{4, 20, 14, symbol14_252_bits},
+{3, 20, 14, symbol14_253_bits},
+{4, 17, 14, symbol14_254_bits},
+{5, 1, 1, symbol14_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol18.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol18.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol18.bdf	(revision 22322)
@@ -0,0 +1,3822 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Symbol-Medium-R-Normal--18-180-75-75-P-107-Adobe-FontSpecific
+SIZE 18 75 75
+FONTBOUNDINGBOX 20 25 -1 -7
+STARTPROPERTIES 31
+FOUNDRY "Adobe"
+FAMILY_NAME "Symbol"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 18
+POINT_SIZE 180
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 107
+CHARSET_REGISTRY "Adobe"
+CHARSET_ENCODING "FontSpecific"
+CAP_HEIGHT 13
+X_HEIGHT 9
+FACE_NAME "Symbol"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "No mark"
+_DEC_DEVICE_FONTNAMES "PS=Symbol"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2-1, 18-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+FULL_NAME "Symbol"
+FONT "-Adobe-Symbol-Medium-R-Normal--18-180-75-75-P-107-Adobe-FontSpecific"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 11
+DEFAULT_CHAR 32
+FONT_ASCENT 16
+FONT_DESCENT 4
+ENDPROPERTIES
+CHARS 188
+STARTCHAR space
+ENCODING 32
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 1 1 1 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 2 13 2 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+40
+40
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 13 13 0 0
+BITMAP
+c018
+c018
+6030
+6030
+3fe0
+3060
+18c0
+18c0
+0880
+0d80
+0500
+0700
+0200
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 10 13 -1 0
+BITMAP
+0900
+0900
+0900
+0900
+7fc0
+1200
+1200
+1200
+ff80
+2400
+2400
+2400
+2400
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+ff
+03
+03
+03
+03
+03
+7f
+03
+03
+03
+03
+03
+ff
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+3860
+67e0
+c4c0
+c580
+e980
+7300
+0600
+0670
+0cc8
+1988
+1988
+31d0
+30e0
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 13 1 0
+BITMAP
+1c00
+3600
+2200
+2600
+3c00
+39e0
+7cc0
+ee80
+c780
+c300
+c390
+67f0
+3ce0
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+f0
+38
+1c
+0c
+7c
+0c
+1c
+38
+f0
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 17 1 -4
+BITMAP
+10
+30
+60
+60
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+60
+60
+30
+10
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 17 1 -4
+BITMAP
+80
+c0
+60
+60
+30
+30
+30
+30
+30
+30
+30
+30
+30
+60
+60
+c0
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 7 1 3
+BITMAP
+10
+d6
+7c
+38
+7c
+d6
+10
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 8 1 1
+BITMAP
+18
+18
+18
+ff
+ff
+18
+18
+18
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 3 5 1 -3
+BITMAP
+60
+60
+20
+60
+c0
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 2 1 4
+BITMAP
+fe
+fe
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 2 1 0
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 13 0 0
+BITMAP
+18
+18
+18
+30
+30
+30
+60
+60
+60
+60
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+3c
+66
+66
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+66
+66
+3c
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 6 13 1 0
+BITMAP
+10
+30
+f0
+30
+30
+30
+30
+30
+30
+30
+30
+30
+fc
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+3c
+7e
+ce
+86
+06
+06
+0c
+18
+30
+60
+c2
+fe
+fe
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+7c
+ce
+86
+06
+0c
+38
+1c
+0e
+06
+06
+06
+cc
+f8
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+06
+0e
+0e
+1e
+36
+36
+66
+c6
+c6
+ff
+06
+06
+06
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+7e
+7c
+c0
+c0
+f0
+3c
+0c
+0e
+06
+06
+0c
+dc
+f0
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+0e
+38
+30
+60
+60
+fc
+c6
+c3
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+7f
+ff
+86
+06
+0c
+0c
+0c
+18
+18
+18
+30
+30
+30
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+3c
+66
+62
+62
+76
+3c
+3e
+67
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+3c
+6e
+c7
+c3
+c3
+c3
+e3
+7f
+3a
+06
+0c
+38
+e0
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 2 9 2 0
+BITMAP
+c0
+c0
+00
+00
+00
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 3 12 1 -3
+BITMAP
+60
+60
+00
+00
+00
+00
+00
+60
+60
+20
+60
+c0
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+0380
+0f00
+3c00
+f000
+c000
+f000
+3c00
+0f00
+0380
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 6 1 2
+BITMAP
+ff
+ff
+00
+00
+ff
+ff
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+e000
+7800
+1e00
+0780
+0180
+0780
+1e00
+7800
+e000
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 6 13 1 0
+BITMAP
+78
+dc
+cc
+cc
+0c
+0c
+18
+30
+20
+20
+00
+60
+60
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+73
+ff
+ce
+00
+ff
+ff
+00
+ff
+ff
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+0400
+0e00
+0e00
+0b00
+1b00
+1300
+1180
+3180
+3fc0
+20c0
+60c0
+60e0
+f1f0
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 1 0
+BITMAP
+fc00
+6600
+6300
+6300
+6300
+6600
+7e00
+6300
+6180
+6180
+6180
+6380
+ff00
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+f070
+7060
+38c0
+18c0
+1d80
+0f00
+0600
+0f00
+1b80
+3180
+31c0
+60e0
+e0f0
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 11 13 0 0
+BITMAP
+0400
+0600
+0e00
+0b00
+1300
+1300
+1180
+2180
+21c0
+40c0
+40c0
+8060
+ffe0
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 1 0
+BITMAP
+ff80
+6180
+6080
+6000
+6000
+6100
+7f00
+6100
+6000
+6000
+6080
+6180
+ff80
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+0f00
+0600
+1f80
+6660
+c630
+c630
+c630
+c630
+c630
+6660
+1f80
+0600
+0f00
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 13 0 0
+BITMAP
+ffc0
+70c0
+3040
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+7800
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+f1e0
+60c0
+60c0
+60c0
+60c0
+60c0
+7fc0
+60c0
+60c0
+60c0
+60c0
+60c0
+f1e0
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 13 1 0
+BITMAP
+f0
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 11 13 1 0
+BITMAP
+0600
+0b00
+0980
+0580
+6380
+f180
+b1c0
+31a0
+3180
+3180
+3180
+3b00
+1e00
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 11 13 2 0
+BITMAP
+f3c0
+6180
+6300
+6600
+6c00
+7800
+7800
+6c00
+6600
+6300
+6380
+61c0
+f1e0
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 13 0 0
+BITMAP
+0600
+0600
+0f00
+0b00
+0b00
+1980
+1180
+1180
+30c0
+20c0
+2060
+6060
+f0f0
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 853 0
+DWIDTH 16 0
+BBX 14 13 1 0
+BITMAP
+e01c
+7038
+7038
+5878
+5858
+58d8
+4c98
+4c98
+4d98
+4718
+4718
+4318
+e23c
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+e0e0
+7040
+7040
+5840
+4c40
+4c40
+4640
+4340
+43c0
+41c0
+40c0
+40c0
+e040
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+0e00
+3180
+60c0
+e0e0
+c060
+c060
+c060
+c060
+c060
+e0e0
+60c0
+3180
+0e00
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+ffe0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+f1e0
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+0e00
+3180
+60c0
+e0e0
+c060
+d160
+df60
+d160
+c060
+e0e0
+60c0
+3180
+0e00
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+fe
+67
+63
+63
+63
+67
+7e
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 13 1 0
+BITMAP
+ff80
+c180
+6080
+3000
+1800
+0c00
+0c00
+0800
+1000
+2040
+40c0
+ff80
+ff80
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 13 1 0
+BITMAP
+ffc0
+ccc0
+8c40
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+1e00
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 13 0 0
+BITMAP
+f8f0
+7060
+30c0
+18c0
+1d80
+0f00
+0f00
+0600
+0600
+0600
+0600
+0600
+0f00
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 6 13 1 -4
+BITMAP
+3c
+7c
+c0
+80
+80
+80
+c0
+78
+3c
+04
+04
+3c
+38
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 13 13 0 0
+BITMAP
+0700
+18c0
+3060
+7070
+6030
+6030
+6030
+6030
+3060
+18c0
+8888
+f8f8
+f8f8
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 13 1 0
+BITMAP
+7f80
+7f80
+4080
+0000
+2100
+3f00
+3f00
+2100
+0000
+8040
+8040
+ffc0
+ffc0
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 14 13 0 0
+BITMAP
+c78c
+6318
+6318
+6318
+6318
+3330
+1fe0
+0300
+0300
+0300
+0300
+0300
+0780
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 1 0
+BITMAP
+ff80
+c380
+8300
+0700
+0600
+0e00
+1c00
+1800
+3800
+7000
+6080
+e180
+ff80
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 17 1 -4
+BITMAP
+f0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+f0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 10 9 2 0
+BITMAP
+0c00
+0c00
+0000
+0000
+0000
+0000
+0000
+c0c0
+c0c0
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 17 1 -4
+BITMAP
+f0
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+f0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 13 1 0
+BITMAP
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+ffc0
+ffc0
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 2 0 -4
+BITMAP
+ff80
+ff80
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 10 1 0 16
+BITMAP
+ffc0
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 11 9 1 0
+BITMAP
+3cc0
+6580
+c300
+c300
+c300
+c300
+c300
+6520
+38c0
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 18 1 -4
+BITMAP
+3c
+46
+c6
+c6
+c6
+cc
+c6
+c3
+c3
+c3
+c3
+c3
+e6
+dc
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 10 13 0 -4
+BITMAP
+60c0
+f1c0
+9180
+1300
+1700
+0e00
+0c00
+1c00
+1a00
+3200
+6240
+e3c0
+c180
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+38
+4c
+40
+60
+30
+38
+6c
+c6
+c6
+c6
+c6
+c6
+6c
+38
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 6 9 1 0
+BITMAP
+78
+ec
+cc
+c0
+70
+c0
+c0
+e4
+78
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 16 1 -4
+BITMAP
+0800
+0800
+0800
+3e00
+6b00
+c980
+c980
+c980
+c980
+c980
+6b00
+3e00
+0800
+0800
+0800
+0800
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 8 13 0 -4
+BITMAP
+c3
+e3
+a3
+26
+26
+16
+14
+1c
+0c
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 1 -4
+BITMAP
+6700
+e980
+b180
+3180
+3180
+3180
+3180
+3180
+3180
+0180
+0180
+0180
+0180
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+60
+e0
+60
+60
+60
+60
+60
+68
+70
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 1 -4
+BITMAP
+6600
+cf00
+c980
+c980
+c980
+c980
+4900
+6b00
+3e00
+0800
+0800
+0800
+0800
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+6380
+e580
+6800
+7800
+7c00
+6e00
+6700
+6380
+6180
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 10 14 0 0
+BITMAP
+3000
+7800
+4800
+0800
+0800
+0800
+1c00
+1c00
+3400
+3400
+6600
+6240
+c3c0
+c180
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 2 -4
+BITMAP
+4200
+c600
+c600
+c600
+c600
+c600
+c680
+fb80
+7b00
+4000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+c3
+c3
+61
+62
+32
+34
+1c
+18
+08
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+3c
+66
+c3
+c3
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+7f80
+ff80
+9200
+1200
+1200
+1200
+3280
+7380
+6300
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+3c
+66
+c3
+c3
+c3
+c3
+ff
+c3
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 -4
+BITMAP
+3c
+46
+c3
+c3
+c3
+c3
+c3
+e6
+dc
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 9 1 0
+BITMAP
+1fc0
+7fc0
+6600
+c300
+c300
+c300
+c300
+6600
+3c00
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+7e
+fe
+90
+10
+10
+10
+12
+1e
+1c
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+6300
+f180
+b180
+3180
+3180
+3180
+3180
+3b00
+1e00
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 11 11 0 0
+BITMAP
+3fe0
+7fe0
+9980
+30c0
+2040
+6660
+6660
+6660
+6660
+36c0
+39c0
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 9 1 0
+BITMAP
+3300
+6180
+4080
+ccc0
+ccc0
+ccc0
+ccc0
+6d80
+7380
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 19 1 -4
+BITMAP
+60
+8e
+fc
+20
+40
+40
+7c
+78
+c0
+80
+80
+80
+c0
+fc
+7e
+02
+02
+1e
+1c
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 13 13 0 -4
+BITMAP
+e238
+7270
+3260
+3260
+3260
+3260
+1ac0
+1ac0
+0f80
+0200
+0200
+0200
+0200
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 19 1 -4
+BITMAP
+60
+86
+9e
+78
+60
+40
+80
+80
+80
+80
+80
+80
+c0
+fc
+7e
+02
+02
+1e
+1c
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 17 1 -4
+BITMAP
+0e
+18
+30
+30
+30
+30
+30
+60
+c0
+60
+30
+30
+30
+30
+30
+18
+0e
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 17 1 -4
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 17 1 -4
+BITMAP
+e0
+30
+18
+18
+18
+18
+18
+0c
+06
+0c
+18
+18
+18
+18
+18
+30
+e0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 3 1 4
+BITMAP
+73
+ff
+ce
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 11 13 0 0
+BITMAP
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 4 5 1 9
+BITMAP
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 12 0 0
+BITMAP
+0000
+0080
+c000
+2000
+6000
+6000
+0000
+0000
+0000
+0000
+0000
+0000
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 8 13 -1 0
+BITMAP
+00
+0c
+00
+1e
+00
+30
+70
+60
+c0
+80
+03
+80
+0f
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 5 0 3
+BITMAP
+0030
+00f0
+00c0
+00f0
+0030
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 17 0 -4
+BITMAP
+0000
+0000
+8000
+0080
+8080
+8000
+0600
+0c00
+1800
+3000
+6000
+c080
+7980
+cf00
+8600
+cf00
+7980
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 10 9 2 0
+BITMAP
+0380
+0580
+0c00
+0c00
+0c00
+7f00
+1800
+1800
+1800
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 9 9 2 0
+BITMAP
+1800
+1800
+1800
+1800
+1800
+1800
+d000
+e000
+0c00
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 9 9 2 0
+BITMAP
+1e00
+1e00
+1e00
+6d80
+ff80
+ff80
+6d80
+0c00
+0800
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 10 9 2 0
+BITMAP
+1c00
+3e00
+7f00
+ff80
+7f00
+3e00
+1c00
+0800
+6300
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 1013 0
+DWIDTH 19 0
+BBX 17 8 1 1
+BITMAP
+f78080
+80ff80
+7f0000
+003e00
+1c0000
+000c00
+1e0000
+007f80
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 960 0
+DWIDTH 18 0
+BBX 18 8 0 1
+BITMAP
+ffc0c0
+c06d80
+0c0000
+001800
+003000
+006000
+00ffc0
+80ffc0
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 25 1 -7
+BITMAP
+80
+60
+03
+00
+30
+06
+00
+18
+0c
+00
+18
+00
+00
+30
+00
+00
+60
+00
+00
+ff
+ff
+c0
+ff
+ff
+c0
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 960 0
+DWIDTH 18 0
+BBX 18 8 0 1
+BITMAP
+600000
+300000
+180000
+183c40
+db9900
+181800
+181800
+181800
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 22 1 -4
+BITMAP
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+00
+06
+00
+00
+03
+00
+00
+01
+80
+ff
+ff
+c0
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 6 1 7
+BITMAP
+f8
+f8
+c0
+00
+00
+80
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 11 1 0
+BITMAP
+00
+03
+00
+00
+06
+00
+18
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 8 5 0 9
+BITMAP
+18
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 12 0 0
+BITMAP
+1800
+1800
+1800
+1880
+db00
+3c00
+7080
+8880
+d800
+1800
+1880
+ff00
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 10 9 0 0
+BITMAP
+1800
+00c0
+ff00
+7740
+cc80
+e000
+7800
+1e00
+0780
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 11 5 0 3
+BITMAP
+0180
+0780
+1e00
+7800
+e000
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+00
+00
+fe
+80
+fe
+80
+e0
+c0
+72
+80
+32
+00
+1e
+00
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 5 6 2 2
+BITMAP
+08
+00
+18
+00
+30
+00
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 8 1 1
+BITMAP
+73
+80
+e1
+c0
+79
+e0
+cf
+00
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+86
+00
+cf
+00
+79
+e0
+78
+cc
+86
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 8 1 1
+BITMAP
+06
+06
+06
+3e
+66
+c6
+c6
+c6
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 7 1 2
+BITMAP
+c4
+ec
+78
+70
+f8
+f8
+f8
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 960 0
+DWIDTH 18 0
+BBX 14 2 2 0
+BITMAP
+f870
+1818
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 2 25 4 -7
+BITMAP
+00
+c0
+c0
+00
+00
+00
+00
+00
+c0
+c0
+00
+c0
+c0
+00
+00
+c0
+c0
+00
+c0
+c0
+00
+c0
+c0
+40
+c0
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 1013 0
+DWIDTH 19 0
+BBX 19 2 0 4
+BITMAP
+ce0060
+ffcec0
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 9 12 1 0
+BITMAP
+0c80
+0c80
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+c0c0
+c0c0
+ffe0
+e0e0
+ffe0
+0180
+0180
+0180
+0180
+0180
+0180
+2180
+6180
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 10 15 1 -1
+BITMAP
+ff80
+ff80
+6000
+2000
+c200
+e3c0
+71c0
+38c0
+3980
+7d00
+cf00
+c700
+e300
+7380
+31c0
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 14 1 0
+BITMAP
+70c0
+f040
+1fc0
+3fc0
+6080
+4100
+4200
+2200
+1300
+0380
+01c0
+00c0
+00c0
+70c0
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 15 1 -4
+BITMAP
+f880
+1f00
+0e00
+79c0
+fee0
+8e70
+8630
+c660
+7680
+3640
+2660
+0660
+0660
+0660
+0660
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+6c70
+b830
+0c00
+1800
+31e0
+6730
+cc30
+d830
+d630
+f660
+64e0
+77c0
+b380
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+9800
+9800
+d800
+7000
+1f80
+39c0
+6060
+d9b0
+ddb0
+8f10
+8610
+8f10
+dbb0
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+d9b0
+6060
+39c0
+1f80
+1f80
+39c0
+6660
+c630
+c630
+bfd0
+bfd0
+8610
+c630
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 9 1 0
+BITMAP
+c630
+6060
+39c0
+1f80
+1fb0
+39f0
+6060
+c0f0
+c1b0
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 9 1 0
+BITMAP
+8310
+8610
+8c10
+d830
+f030
+6060
+f9c0
+df80
+1f80
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 9 1 0
+BITMAP
+39c0
+6060
+e070
+c030
+c030
+c030
+c030
+c030
+c030
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 12 0 -3
+BITMAP
+c030
+c030
+c030
+c030
+e070
+6060
+39c0
+1f80
+ff80
+ffe0
+0060
+0030
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 13 0 -2
+BITMAP
+0030
+0030
+0060
+ffe0
+ff80
+ff80
+ffe0
+0060
+0030
+0030
+0030
+0060
+ffe0
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 9 0 0
+BITMAP
+ff80
+0000
+fff0
+fff0
+0060
+00c0
+1ff0
+7ff0
+6180
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 12 0 -3
+BITMAP
+c300
+c300
+c600
+6600
+7ff0
+1ff0
+1800
+3000
+1ff0
+7ff0
+6000
+c000
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 9 9 1 0
+BITMAP
+c000
+c000
+6000
+7f80
+1f80
+1f80
+7f80
+6000
+c000
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 11 1 -1
+BITMAP
+c000
+c000
+6000
+7fc0
+1fc0
+0000
+ffc0
+ffc0
+1f80
+7f80
+e000
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 13 13 0 0
+BITMAP
+c000
+ff80
+c000
+e000
+7f80
+1f80
+0080
+1fc0
+7fc0
+e200
+c400
+ff80
+c800
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 13 1 0
+BITMAP
+f000
+7fc0
+3fc0
+4000
+0010
+0030
+0060
+00c0
+0180
+0300
+0600
+0c00
+1800
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+3000
+6000
+c000
+fff8
+fff0
+e020
+6060
+6040
+70c0
+3080
+3080
+3980
+1900
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+1b00
+0e00
+0e00
+0400
+1fc0
+38e0
+6030
+df18
+cd98
+cd98
+cd98
+cf18
+cd98
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 853 0
+DWIDTH 16 0
+BBX 14 8 1 5
+BITMAP
+dcd8
+6030
+38e0
+1fc0
+1fc0
+38e0
+6030
+c798
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 14 16 0 -2
+BITMAP
+cd98
+cc98
+cc18
+cc18
+cc98
+c718
+6030
+38e0
+1fc0
+ff1c
+ab18
+2318
+22a8
+22a8
+22a8
+2248
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 10 17 0 0
+BITMAP
+7740
+ffc0
+7000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+fcc0
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 2 1 4
+BITMAP
+00
+40
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 6 1 0
+BITMAP
+0040
+0040
+0080
+0080
+0080
+0080
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 9 0 0
+BITMAP
+3080
+7100
+d900
+1900
+1900
+0d00
+0d00
+0600
+0600
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 9 0 0
+BITMAP
+0600
+c0c0
+ffc0
+ffc0
+0040
+0040
+0040
+0040
+0c00
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 960 0
+DWIDTH 18 0
+BBX 18 9 0 0
+BITMAP
+0c0000
+003300
+330040
+806180
+c0c0c0
+c0c0c0
+c0c040
+806180
+330000
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 960 0
+DWIDTH 18 0
+BBX 16 9 1 0
+BITMAP
+001e
+000c
+000c
+0008
+0400
+1806
+003f
+ff00
+7fff
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+8080
+0180
+7f80
+8000
+ff00
+1800
+0000
+0400
+0800
+1800
+3f80
+7f80
+e000
+7f80
+3f80
+1800
+0800
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 960 0
+DWIDTH 18 0
+BBX 16 9 1 0
+BITMAP
+0800
+1c00
+3600
+7700
+f780
+3600
+3600
+3600
+3600
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+3600
+3600
+3600
+3600
+3600
+3600
+3600
+3600
+0000
+0000
+ff80
+ff80
+0000
+ff80
+ff80
+0000
+0000
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 14 0 0
+BITMAP
+3600
+3600
+3600
+3600
+3600
+3600
+3600
+3600
+3600
+3600
+3600
+3600
+f780
+7700
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 5 17 0 -3
+BITMAP
+30
+00
+18
+00
+08
+00
+08
+00
+18
+00
+30
+00
+30
+00
+60
+00
+60
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+00c0
+80c0
+8060
+0060
+0030
+0030
+0018
+0008
+0018
+1830
+3060
+60c0
+c080
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+c0c0
+6060
+3030
+1818
+1fc0
+38e0
+6030
+cf18
+cd98
+cd98
+cf18
+cd98
+cd98
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 8 1 5
+BITMAP
+cd98
+6030
+38e0
+1fc0
+1fc0
+38e0
+6030
+c718
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 16 0 -2
+BITMAP
+cc90
+cc10
+cc10
+cc10
+cc90
+c710
+6030
+38e0
+1fc0
+fb10
+2310
+2310
+22a0
+22a0
+22a0
+2240
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 6 25 1 -7
+BITMAP
+20
+48
+fc
+e0
+e0
+60
+70
+20
+38
+00
+1c
+00
+0c
+00
+04
+00
+00
+00
+04
+00
+0c
+00
+18
+00
+30
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 2 25 1 -7
+BITMAP
+00
+40
+00
+c0
+00
+c0
+c0
+c0
+c0
+00
+00
+00
+00
+00
+00
+40
+40
+40
+40
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 6 25 1 -7
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 25 1 -7
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+e0
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 2 25 1 -7
+BITMAP
+40
+40
+40
+00
+00
+00
+00
+00
+00
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 22 1 -4
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 5 25 4 -7
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 5 25 1 -7
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+f8
+38
+60
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 5 22 4 -4
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+30
+e0
+e0
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 2 25 4 -7
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 5 17 0 -3
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+60
+38
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 6 22 0 -4
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+00
+c0
+c0
+60
+60
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 7 25 5 -7
+BITMAP
+30
+30
+18
+18
+08
+18
+18
+30
+30
+60
+60
+c0
+c0
+1c
+1c
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 2 25 0 -7
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+c0
+c0
+00
+40
+40
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 7 22 0 -4
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 6 25 0 -7
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+04
+04
+04
+04
+04
+04
+04
+04
+04
+04
+04
+04
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 2 25 4 -7
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+c0
+c0
+40
+80
+c0
+40
+40
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 6 25 0 -7
+BITMAP
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 25 1 -7
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+08
+08
+08
+08
+08
+08
+08
+08
+08
+08
+08
+08
+08
+08
+08
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 2 25 4 -7
+BITMAP
+00
+00
+00
+00
+00
+00
+40
+40
+c0
+80
+c0
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 22 1 -4
+BITMAP
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 5 25 1 -7
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 5 25 4 -7
+BITMAP
+18
+18
+18
+18
+18
+18
+18
+18
+18
+f8
+e0
+30
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 5 22 1 -4
+BITMAP
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+60
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol18.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol18.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol18.h	(revision 22322)
@@ -0,0 +1,1000 @@
+static unsigned char symbol18_0_bits[] = {
+0x00};
+static unsigned char symbol18_1_bits[] = {
+0x00};
+static unsigned char symbol18_2_bits[] = {
+0x00};
+static unsigned char symbol18_3_bits[] = {
+0x00};
+static unsigned char symbol18_4_bits[] = {
+0x00};
+static unsigned char symbol18_5_bits[] = {
+0x00};
+static unsigned char symbol18_6_bits[] = {
+0x00};
+static unsigned char symbol18_7_bits[] = {
+0x00};
+static unsigned char symbol18_8_bits[] = {
+0x00};
+static unsigned char symbol18_9_bits[] = {
+0x00};
+static unsigned char symbol18_10_bits[] = {
+0x00};
+static unsigned char symbol18_11_bits[] = {
+0x00};
+static unsigned char symbol18_12_bits[] = {
+0x00};
+static unsigned char symbol18_13_bits[] = {
+0x00};
+static unsigned char symbol18_14_bits[] = {
+0x00};
+static unsigned char symbol18_15_bits[] = {
+0x00};
+static unsigned char symbol18_16_bits[] = {
+0x00};
+static unsigned char symbol18_17_bits[] = {
+0x00};
+static unsigned char symbol18_18_bits[] = {
+0x00};
+static unsigned char symbol18_19_bits[] = {
+0x00};
+static unsigned char symbol18_20_bits[] = {
+0x00};
+static unsigned char symbol18_21_bits[] = {
+0x00};
+static unsigned char symbol18_22_bits[] = {
+0x00};
+static unsigned char symbol18_23_bits[] = {
+0x00};
+static unsigned char symbol18_24_bits[] = {
+0x00};
+static unsigned char symbol18_25_bits[] = {
+0x00};
+static unsigned char symbol18_26_bits[] = {
+0x00};
+static unsigned char symbol18_27_bits[] = {
+0x00};
+static unsigned char symbol18_28_bits[] = {
+0x00};
+static unsigned char symbol18_29_bits[] = {
+0x00};
+static unsigned char symbol18_30_bits[] = {
+0x00};
+static unsigned char symbol18_31_bits[] = {
+0x00};
+static unsigned char symbol18_32_bits[] = {
+0x00};
+static unsigned char symbol18_33_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x00, 0x00, 0x03, 
+0x03};
+static unsigned char symbol18_34_bits[] = {
+0x03, 0x18, 0x03, 0x18, 0x06, 0x0c, 0x06, 0x0c, 0xfc, 0x07, 0x0c, 0x06, 
+0x18, 0x03, 0x18, 0x03, 0x10, 0x01, 0xb0, 0x01, 0xa0, 0x00, 0xe0, 0x00, 
+0x40, 0x00};
+static unsigned char symbol18_35_bits[] = {
+0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0xfe, 0x03, 0x48, 0x00, 
+0x48, 0x00, 0x48, 0x00, 0xff, 0x01, 0x24, 0x00, 0x24, 0x00, 0x24, 0x00, 
+0x24, 0x00};
+static unsigned char symbol18_36_bits[] = {
+0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 
+0xff};
+static unsigned char symbol18_37_bits[] = {
+0x1c, 0x06, 0xe6, 0x07, 0x23, 0x03, 0xa3, 0x01, 0x97, 0x01, 0xce, 0x00, 
+0x60, 0x00, 0x60, 0x0e, 0x30, 0x13, 0x98, 0x11, 0x98, 0x11, 0x8c, 0x0b, 
+0x0c, 0x07};
+static unsigned char symbol18_38_bits[] = {
+0x38, 0x00, 0x6c, 0x00, 0x44, 0x00, 0x64, 0x00, 0x3c, 0x00, 0x9c, 0x07, 
+0x3e, 0x03, 0x77, 0x01, 0xe3, 0x01, 0xc3, 0x00, 0xc3, 0x09, 0xe6, 0x0f, 
+0x3c, 0x07};
+static unsigned char symbol18_39_bits[] = {
+0x0f, 0x1c, 0x38, 0x30, 0x3e, 0x30, 0x38, 0x1c, 0x0f};
+static unsigned char symbol18_40_bits[] = {
+0x08, 0x0c, 0x06, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x06, 0x06, 0x0c, 0x08};
+static unsigned char symbol18_41_bits[] = {
+0x01, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x06, 0x06, 0x03, 0x01};
+static unsigned char symbol18_42_bits[] = {
+0x08, 0x6b, 0x3e, 0x1c, 0x3e, 0x6b, 0x08};
+static unsigned char symbol18_43_bits[] = {
+0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18};
+static unsigned char symbol18_44_bits[] = {
+0x06, 0x06, 0x04, 0x06, 0x03};
+static unsigned char symbol18_45_bits[] = {
+0x7f, 0x7f};
+static unsigned char symbol18_46_bits[] = {
+0x03, 0x03};
+static unsigned char symbol18_47_bits[] = {
+0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06, 0x06, 0x03, 0x03, 
+0x03};
+static unsigned char symbol18_48_bits[] = {
+0x3c, 0x66, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x66, 
+0x3c};
+static unsigned char symbol18_49_bits[] = {
+0x08, 0x0c, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x3f};
+static unsigned char symbol18_50_bits[] = {
+0x3c, 0x7e, 0x73, 0x61, 0x60, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x43, 0x7f, 
+0x7f};
+static unsigned char symbol18_51_bits[] = {
+0x3e, 0x73, 0x61, 0x60, 0x30, 0x1c, 0x38, 0x70, 0x60, 0x60, 0x60, 0x33, 
+0x1f};
+static unsigned char symbol18_52_bits[] = {
+0x60, 0x70, 0x70, 0x78, 0x6c, 0x6c, 0x66, 0x63, 0x63, 0xff, 0x60, 0x60, 
+0x60};
+static unsigned char symbol18_53_bits[] = {
+0x7e, 0x3e, 0x03, 0x03, 0x0f, 0x3c, 0x30, 0x70, 0x60, 0x60, 0x30, 0x3b, 
+0x0f};
+static unsigned char symbol18_54_bits[] = {
+0x70, 0x1c, 0x0c, 0x06, 0x06, 0x3f, 0x63, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 
+0x3c};
+static unsigned char symbol18_55_bits[] = {
+0xfe, 0xff, 0x61, 0x60, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, 0x0c, 0x0c, 
+0x0c};
+static unsigned char symbol18_56_bits[] = {
+0x3c, 0x66, 0x46, 0x46, 0x6e, 0x3c, 0x7c, 0xe6, 0xc3, 0xc3, 0xc3, 0x66, 
+0x3c};
+static unsigned char symbol18_57_bits[] = {
+0x3c, 0x76, 0xe3, 0xc3, 0xc3, 0xc3, 0xc7, 0xfe, 0x5c, 0x60, 0x30, 0x1c, 
+0x07};
+static unsigned char symbol18_58_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03};
+static unsigned char symbol18_59_bits[] = {
+0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x04, 0x06, 0x03};
+static unsigned char symbol18_60_bits[] = {
+0xc0, 0x01, 0xf0, 0x00, 0x3c, 0x00, 0x0f, 0x00, 0x03, 0x00, 0x0f, 0x00, 
+0x3c, 0x00, 0xf0, 0x00, 0xc0, 0x01};
+static unsigned char symbol18_61_bits[] = {
+0xff, 0xff, 0x00, 0x00, 0xff, 0xff};
+static unsigned char symbol18_62_bits[] = {
+0x07, 0x00, 0x1e, 0x00, 0x78, 0x00, 0xe0, 0x01, 0x80, 0x01, 0xe0, 0x01, 
+0x78, 0x00, 0x1e, 0x00, 0x07, 0x00};
+static unsigned char symbol18_63_bits[] = {
+0x1e, 0x3b, 0x33, 0x33, 0x30, 0x30, 0x18, 0x0c, 0x04, 0x04, 0x00, 0x06, 
+0x06};
+static unsigned char symbol18_64_bits[] = {
+0xce, 0xff, 0x73, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff};
+static unsigned char symbol18_65_bits[] = {
+0x20, 0x00, 0x70, 0x00, 0x70, 0x00, 0xd0, 0x00, 0xd8, 0x00, 0xc8, 0x00, 
+0x88, 0x01, 0x8c, 0x01, 0xfc, 0x03, 0x04, 0x03, 0x06, 0x03, 0x06, 0x07, 
+0x8f, 0x0f};
+static unsigned char symbol18_66_bits[] = {
+0x3f, 0x00, 0x66, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0x66, 0x00, 
+0x7e, 0x00, 0xc6, 0x00, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, 0xc6, 0x01, 
+0xff, 0x00};
+static unsigned char symbol18_67_bits[] = {
+0x0f, 0x0e, 0x0e, 0x06, 0x1c, 0x03, 0x18, 0x03, 0xb8, 0x01, 0xf0, 0x00, 
+0x60, 0x00, 0xf0, 0x00, 0xd8, 0x01, 0x8c, 0x01, 0x8c, 0x03, 0x06, 0x07, 
+0x07, 0x0f};
+static unsigned char symbol18_68_bits[] = {
+0x20, 0x00, 0x60, 0x00, 0x70, 0x00, 0xd0, 0x00, 0xc8, 0x00, 0xc8, 0x00, 
+0x88, 0x01, 0x84, 0x01, 0x84, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x06, 
+0xff, 0x07};
+static unsigned char symbol18_69_bits[] = {
+0xff, 0x01, 0x86, 0x01, 0x06, 0x01, 0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 
+0xfe, 0x00, 0x86, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x01, 0x86, 0x01, 
+0xff, 0x01};
+static unsigned char symbol18_70_bits[] = {
+0xf0, 0x00, 0x60, 0x00, 0xf8, 0x01, 0x66, 0x06, 0x63, 0x0c, 0x63, 0x0c, 
+0x63, 0x0c, 0x63, 0x0c, 0x63, 0x0c, 0x66, 0x06, 0xf8, 0x01, 0x60, 0x00, 
+0xf0, 0x00};
+static unsigned char symbol18_71_bits[] = {
+0xff, 0x03, 0x0e, 0x03, 0x0c, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x1e, 0x00};
+static unsigned char symbol18_72_bits[] = {
+0x8f, 0x07, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0xfe, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x8f, 0x07};
+static unsigned char symbol18_73_bits[] = {
+0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x0f};
+static unsigned char symbol18_74_bits[] = {
+0x60, 0x00, 0xd0, 0x00, 0x90, 0x01, 0xa0, 0x01, 0xc6, 0x01, 0x8f, 0x01, 
+0x8d, 0x03, 0x8c, 0x05, 0x8c, 0x01, 0x8c, 0x01, 0x8c, 0x01, 0xdc, 0x00, 
+0x78, 0x00};
+static unsigned char symbol18_75_bits[] = {
+0xcf, 0x03, 0x86, 0x01, 0xc6, 0x00, 0x66, 0x00, 0x36, 0x00, 0x1e, 0x00, 
+0x1e, 0x00, 0x36, 0x00, 0x66, 0x00, 0xc6, 0x00, 0xc6, 0x01, 0x86, 0x03, 
+0x8f, 0x07};
+static unsigned char symbol18_76_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x98, 0x01, 
+0x88, 0x01, 0x88, 0x01, 0x0c, 0x03, 0x04, 0x03, 0x04, 0x06, 0x06, 0x06, 
+0x0f, 0x0f};
+static unsigned char symbol18_77_bits[] = {
+0x07, 0x38, 0x0e, 0x1c, 0x0e, 0x1c, 0x1a, 0x1e, 0x1a, 0x1a, 0x1a, 0x1b, 
+0x32, 0x19, 0x32, 0x19, 0xb2, 0x19, 0xe2, 0x18, 0xe2, 0x18, 0xc2, 0x18, 
+0x47, 0x3c};
+static unsigned char symbol18_78_bits[] = {
+0x07, 0x07, 0x0e, 0x02, 0x0e, 0x02, 0x1a, 0x02, 0x32, 0x02, 0x32, 0x02, 
+0x62, 0x02, 0xc2, 0x02, 0xc2, 0x03, 0x82, 0x03, 0x02, 0x03, 0x02, 0x03, 
+0x07, 0x02};
+static unsigned char symbol18_79_bits[] = {
+0x70, 0x00, 0x8c, 0x01, 0x06, 0x03, 0x07, 0x07, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x07, 0x07, 0x06, 0x03, 0x8c, 0x01, 
+0x70, 0x00};
+static unsigned char symbol18_80_bits[] = {
+0xff, 0x07, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x8f, 0x07};
+static unsigned char symbol18_81_bits[] = {
+0x70, 0x00, 0x8c, 0x01, 0x06, 0x03, 0x07, 0x07, 0x03, 0x06, 0x8b, 0x06, 
+0xfb, 0x06, 0x8b, 0x06, 0x03, 0x06, 0x07, 0x07, 0x06, 0x03, 0x8c, 0x01, 
+0x70, 0x00};
+static unsigned char symbol18_82_bits[] = {
+0x7f, 0xe6, 0xc6, 0xc6, 0xc6, 0xe6, 0x7e, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x0f};
+static unsigned char symbol18_83_bits[] = {
+0xff, 0x01, 0x83, 0x01, 0x06, 0x01, 0x0c, 0x00, 0x18, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x02, 0x02, 0x03, 0xff, 0x01, 
+0xff, 0x01};
+static unsigned char symbol18_84_bits[] = {
+0xff, 0x03, 0x33, 0x03, 0x31, 0x02, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x78, 0x00};
+static unsigned char symbol18_85_bits[] = {
+0x1f, 0x0f, 0x0e, 0x06, 0x0c, 0x03, 0x18, 0x03, 0xb8, 0x01, 0xf0, 0x00, 
+0xf0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0xf0, 0x00};
+static unsigned char symbol18_86_bits[] = {
+0x3c, 0x3e, 0x03, 0x01, 0x01, 0x01, 0x03, 0x1e, 0x3c, 0x20, 0x20, 0x3c, 
+0x1c};
+static unsigned char symbol18_87_bits[] = {
+0xe0, 0x00, 0x18, 0x03, 0x0c, 0x06, 0x0e, 0x0e, 0x06, 0x0c, 0x06, 0x0c, 
+0x06, 0x0c, 0x06, 0x0c, 0x0c, 0x06, 0x18, 0x03, 0x11, 0x11, 0x1f, 0x1f, 
+0x1f, 0x1f};
+static unsigned char symbol18_88_bits[] = {
+0xfe, 0x01, 0xfe, 0x01, 0x02, 0x01, 0x00, 0x00, 0x84, 0x00, 0xfc, 0x00, 
+0xfc, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x02, 0xff, 0x03, 
+0xff, 0x03};
+static unsigned char symbol18_89_bits[] = {
+0xe3, 0x31, 0xc6, 0x18, 0xc6, 0x18, 0xc6, 0x18, 0xc6, 0x18, 0xcc, 0x0c, 
+0xf8, 0x07, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0xe0, 0x01};
+static unsigned char symbol18_90_bits[] = {
+0xff, 0x01, 0xc3, 0x01, 0xc1, 0x00, 0xe0, 0x00, 0x60, 0x00, 0x70, 0x00, 
+0x38, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x06, 0x01, 0x87, 0x01, 
+0xff, 0x01};
+static unsigned char symbol18_91_bits[] = {
+0x0f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x0f};
+static unsigned char symbol18_92_bits[] = {
+0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol18_93_bits[] = {
+0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0f};
+static unsigned char symbol18_94_bits[] = {
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0xff, 0x03, 
+0xff, 0x03};
+static unsigned char symbol18_95_bits[] = {
+0xff, 0x01, 0xff, 0x01};
+static unsigned char symbol18_96_bits[] = {
+0xff, 0x03};
+static unsigned char symbol18_97_bits[] = {
+0x3c, 0x03, 0xa6, 0x01, 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 
+0xc3, 0x00, 0xa6, 0x04, 0x1c, 0x03};
+static unsigned char symbol18_98_bits[] = {
+0x3c, 0x62, 0x63, 0x63, 0x63, 0x33, 0x63, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 
+0x67, 0x3b, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol18_99_bits[] = {
+0x06, 0x03, 0x8f, 0x03, 0x89, 0x01, 0xc8, 0x00, 0xe8, 0x00, 0x70, 0x00, 
+0x30, 0x00, 0x38, 0x00, 0x58, 0x00, 0x4c, 0x00, 0x46, 0x02, 0xc7, 0x03, 
+0x83, 0x01};
+static unsigned char symbol18_100_bits[] = {
+0x1c, 0x32, 0x02, 0x06, 0x0c, 0x1c, 0x36, 0x63, 0x63, 0x63, 0x63, 0x63, 
+0x36, 0x1c};
+static unsigned char symbol18_101_bits[] = {
+0x1e, 0x37, 0x33, 0x03, 0x0e, 0x03, 0x03, 0x27, 0x1e};
+static unsigned char symbol18_102_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x7c, 0x00, 0xd6, 0x00, 0x93, 0x01, 
+0x93, 0x01, 0x93, 0x01, 0x93, 0x01, 0x93, 0x01, 0xd6, 0x00, 0x7c, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char symbol18_103_bits[] = {
+0xc3, 0xc7, 0xc5, 0x64, 0x64, 0x68, 0x28, 0x38, 0x30, 0x18, 0x18, 0x18, 
+0x18};
+static unsigned char symbol18_104_bits[] = {
+0xe6, 0x00, 0x97, 0x01, 0x8d, 0x01, 0x8c, 0x01, 0x8c, 0x01, 0x8c, 0x01, 
+0x8c, 0x01, 0x8c, 0x01, 0x8c, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 
+0x80, 0x01};
+static unsigned char symbol18_105_bits[] = {
+0x06, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x16, 0x0e};
+static unsigned char symbol18_106_bits[] = {
+0x66, 0x00, 0xf3, 0x00, 0x93, 0x01, 0x93, 0x01, 0x93, 0x01, 0x93, 0x01, 
+0x92, 0x00, 0xd6, 0x00, 0x7c, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x10, 0x00};
+static unsigned char symbol18_107_bits[] = {
+0xc6, 0x01, 0xa7, 0x01, 0x16, 0x00, 0x1e, 0x00, 0x3e, 0x00, 0x76, 0x00, 
+0xe6, 0x00, 0xc6, 0x01, 0x86, 0x01};
+static unsigned char symbol18_108_bits[] = {
+0x0c, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x38, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x2c, 0x00, 0x66, 0x00, 0x46, 0x02, 
+0xc3, 0x03, 0x83, 0x01};
+static unsigned char symbol18_109_bits[] = {
+0x42, 0x00, 0x63, 0x00, 0x63, 0x00, 0x63, 0x00, 0x63, 0x00, 0x63, 0x00, 
+0x63, 0x01, 0xdf, 0x01, 0xde, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00};
+static unsigned char symbol18_110_bits[] = {
+0xc3, 0xc3, 0x86, 0x46, 0x4c, 0x2c, 0x38, 0x18, 0x10};
+static unsigned char symbol18_111_bits[] = {
+0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c};
+static unsigned char symbol18_112_bits[] = {
+0xfe, 0x01, 0xff, 0x01, 0x49, 0x00, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 
+0x4c, 0x01, 0xce, 0x01, 0xc6, 0x00};
+static unsigned char symbol18_113_bits[] = {
+0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 
+0x3c};
+static unsigned char symbol18_114_bits[] = {
+0x3c, 0x62, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x67, 0x3b, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char symbol18_115_bits[] = {
+0xf8, 0x03, 0xfe, 0x03, 0x66, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 
+0xc3, 0x00, 0x66, 0x00, 0x3c, 0x00};
+static unsigned char symbol18_116_bits[] = {
+0x7e, 0x7f, 0x09, 0x08, 0x08, 0x08, 0x48, 0x78, 0x38};
+static unsigned char symbol18_117_bits[] = {
+0xc6, 0x00, 0x8f, 0x01, 0x8d, 0x01, 0x8c, 0x01, 0x8c, 0x01, 0x8c, 0x01, 
+0x8c, 0x01, 0xdc, 0x00, 0x78, 0x00};
+static unsigned char symbol18_118_bits[] = {
+0xfc, 0x07, 0xfe, 0x07, 0x99, 0x01, 0x0c, 0x03, 0x04, 0x02, 0x66, 0x06, 
+0x66, 0x06, 0x66, 0x06, 0x66, 0x06, 0x6c, 0x03, 0x9c, 0x03};
+static unsigned char symbol18_119_bits[] = {
+0xcc, 0x00, 0x86, 0x01, 0x02, 0x01, 0x33, 0x03, 0x33, 0x03, 0x33, 0x03, 
+0x33, 0x03, 0xb6, 0x01, 0xce, 0x01};
+static unsigned char symbol18_120_bits[] = {
+0x06, 0x71, 0x3f, 0x04, 0x02, 0x02, 0x3e, 0x1e, 0x03, 0x01, 0x01, 0x01, 
+0x03, 0x3f, 0x7e, 0x40, 0x40, 0x78, 0x38};
+static unsigned char symbol18_121_bits[] = {
+0x47, 0x1c, 0x4e, 0x0e, 0x4c, 0x06, 0x4c, 0x06, 0x4c, 0x06, 0x4c, 0x06, 
+0x58, 0x03, 0x58, 0x03, 0xf0, 0x01, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 
+0x40, 0x00};
+static unsigned char symbol18_122_bits[] = {
+0x06, 0x61, 0x79, 0x1e, 0x06, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x03, 0x3f, 0x7e, 0x40, 0x40, 0x78, 0x38};
+static unsigned char symbol18_123_bits[] = {
+0x70, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x18, 0x70};
+static unsigned char symbol18_124_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol18_125_bits[] = {
+0x07, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x60, 0x30, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x0c, 0x07};
+static unsigned char symbol18_126_bits[] = {
+0xce, 0xff, 0x73};
+static unsigned char symbol18_127_bits[] = {
+0x00};
+static unsigned char symbol18_128_bits[] = {
+0x00};
+static unsigned char symbol18_129_bits[] = {
+0x00};
+static unsigned char symbol18_130_bits[] = {
+0x00};
+static unsigned char symbol18_131_bits[] = {
+0x00};
+static unsigned char symbol18_132_bits[] = {
+0x00};
+static unsigned char symbol18_133_bits[] = {
+0x00};
+static unsigned char symbol18_134_bits[] = {
+0x00};
+static unsigned char symbol18_135_bits[] = {
+0x00};
+static unsigned char symbol18_136_bits[] = {
+0x00};
+static unsigned char symbol18_137_bits[] = {
+0x00};
+static unsigned char symbol18_138_bits[] = {
+0x00};
+static unsigned char symbol18_139_bits[] = {
+0x00};
+static unsigned char symbol18_140_bits[] = {
+0x00};
+static unsigned char symbol18_141_bits[] = {
+0x00};
+static unsigned char symbol18_142_bits[] = {
+0x00};
+static unsigned char symbol18_143_bits[] = {
+0x00};
+static unsigned char symbol18_144_bits[] = {
+0x00};
+static unsigned char symbol18_145_bits[] = {
+0x00};
+static unsigned char symbol18_146_bits[] = {
+0x00};
+static unsigned char symbol18_147_bits[] = {
+0x00};
+static unsigned char symbol18_148_bits[] = {
+0x00};
+static unsigned char symbol18_149_bits[] = {
+0x00};
+static unsigned char symbol18_150_bits[] = {
+0x00};
+static unsigned char symbol18_151_bits[] = {
+0x00};
+static unsigned char symbol18_152_bits[] = {
+0x00};
+static unsigned char symbol18_153_bits[] = {
+0x00};
+static unsigned char symbol18_154_bits[] = {
+0x00};
+static unsigned char symbol18_155_bits[] = {
+0x00};
+static unsigned char symbol18_156_bits[] = {
+0x00};
+static unsigned char symbol18_157_bits[] = {
+0x00};
+static unsigned char symbol18_158_bits[] = {
+0x00};
+static unsigned char symbol18_159_bits[] = {
+0x00};
+static unsigned char symbol18_160_bits[] = {
+0x00};
+static unsigned char symbol18_161_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00};
+static unsigned char symbol18_162_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol18_163_bits[] = {
+0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x04, 0x00, 0x06, 0x00, 0x06, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol18_164_bits[] = {
+0x00, 0x30, 0x00, 0x78, 0x00, 0x0c, 0x0e, 0x06, 0x03, 0x01, 0xc0, 0x01, 
+0xf0};
+static unsigned char symbol18_165_bits[] = {
+0x00, 0x0c, 0x00, 0x0f, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x0c};
+static unsigned char symbol18_166_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 
+0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x03, 0x01, 
+0x9e, 0x01, 0xf3, 0x00, 0x61, 0x00, 0xf3, 0x00, 0x9e, 0x01};
+static unsigned char symbol18_167_bits[] = {
+0xc0, 0x01, 0xa0, 0x01, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0xfe, 0x00, 
+0x18, 0x00, 0x18, 0x00, 0x18, 0x00};
+static unsigned char symbol18_168_bits[] = {
+0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 
+0x0b, 0x00, 0x07, 0x00, 0x30, 0x00};
+static unsigned char symbol18_169_bits[] = {
+0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0xb6, 0x01, 0xff, 0x01, 0xff, 0x01, 
+0xb6, 0x01, 0x30, 0x00, 0x10, 0x00};
+static unsigned char symbol18_170_bits[] = {
+0x38, 0x00, 0x7c, 0x00, 0xfe, 0x00, 0xff, 0x01, 0xfe, 0x00, 0x7c, 0x00, 
+0x38, 0x00, 0x10, 0x00, 0xc6, 0x00};
+static unsigned char symbol18_171_bits[] = {
+0xef, 0x01, 0x01, 0x01, 0xff, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x7c, 0x00, 
+0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x78, 0x00, 0x00, 0x00, 0xfe, 0x01};
+static unsigned char symbol18_172_bits[] = {
+0xff, 0x03, 0x03, 0x03, 0xb6, 0x01, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 
+0x00, 0x0c, 0x00, 0x00, 0x06, 0x00, 0x00, 0xff, 0x03, 0x01, 0xff, 0x03};
+static unsigned char symbol18_173_bits[] = {
+0x01, 0x06, 0xc0, 0x00, 0x0c, 0x60, 0x00, 0x18, 0x30, 0x00, 0x18, 0x00, 
+0x00, 0x0c, 0x00, 0x00, 0x06, 0x00, 0x00, 0xff, 0xff, 0x03, 0xff, 0xff, 
+0x03};
+static unsigned char symbol18_174_bits[] = {
+0x06, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x3c, 0x02, 
+0xdb, 0x99, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00};
+static unsigned char symbol18_175_bits[] = {
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x60, 
+0x00, 0x00, 0xc0, 0x00, 0x00, 0x80, 0x01, 0xff, 0xff, 0x03};
+static unsigned char symbol18_176_bits[] = {
+0x1f, 0x1f, 0x03, 0x00, 0x00, 0x01};
+static unsigned char symbol18_177_bits[] = {
+0x00, 0xc0, 0x00, 0x00, 0x60, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18};
+static unsigned char symbol18_178_bits[] = {
+0x18, 0x18, 0x18, 0x18, 0x18};
+static unsigned char symbol18_179_bits[] = {
+0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x01, 0xdb, 0x00, 0x3c, 0x00, 
+0x0e, 0x01, 0x11, 0x01, 0x1b, 0x00, 0x18, 0x00, 0x18, 0x01, 0xff, 0x00};
+static unsigned char symbol18_180_bits[] = {
+0x18, 0x00, 0x00, 0x03, 0xff, 0x00, 0xee, 0x02, 0x33, 0x01, 0x07, 0x00, 
+0x1e, 0x00, 0x78, 0x00, 0xe0, 0x01};
+static unsigned char symbol18_181_bits[] = {
+0x80, 0x01, 0xe0, 0x01, 0x78, 0x00, 0x1e, 0x00, 0x07, 0x00};
+static unsigned char symbol18_182_bits[] = {
+0x00, 0x00, 0x7f, 0x01, 0x7f, 0x01, 0x07, 0x03, 0x4e, 0x01, 0x4c, 0x00, 
+0x78, 0x00};
+static unsigned char symbol18_183_bits[] = {
+0x10, 0x00, 0x18, 0x00, 0x0c, 0x00};
+static unsigned char symbol18_184_bits[] = {
+0xce, 0x01, 0x87, 0x03, 0x9e, 0x07, 0xf3, 0x00};
+static unsigned char symbol18_185_bits[] = {
+0x61, 0x00, 0xf3, 0x00, 0x9e, 0x07, 0x1e, 0x33, 0x61};
+static unsigned char symbol18_186_bits[] = {
+0x60, 0x60, 0x60, 0x7c, 0x66, 0x63, 0x63, 0x63};
+static unsigned char symbol18_187_bits[] = {
+0x23, 0x37, 0x1e, 0x0e, 0x1f, 0x1f, 0x1f};
+static unsigned char symbol18_188_bits[] = {
+0x1f, 0x0e, 0x18, 0x18};
+static unsigned char symbol18_189_bits[] = {
+0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 
+0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x02, 
+0x03};
+static unsigned char symbol18_190_bits[] = {
+0x73, 0x00, 0x06, 0xff, 0x73, 0x03};
+static unsigned char symbol18_191_bits[] = {
+0x30, 0x01, 0x30, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 
+0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01};
+static unsigned char symbol18_192_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0xff, 0x07, 0x07, 0x07, 0xff, 0x07, 0x80, 0x01, 
+0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x84, 0x01, 
+0x86, 0x01};
+static unsigned char symbol18_193_bits[] = {
+0xff, 0x01, 0xff, 0x01, 0x06, 0x00, 0x04, 0x00, 0x43, 0x00, 0xc7, 0x03, 
+0x8e, 0x03, 0x1c, 0x03, 0x9c, 0x01, 0xbe, 0x00, 0xf3, 0x00, 0xe3, 0x00, 
+0xc7, 0x00, 0xce, 0x01, 0x8c, 0x03};
+static unsigned char symbol18_194_bits[] = {
+0x0e, 0x03, 0x0f, 0x02, 0xf8, 0x03, 0xfc, 0x03, 0x06, 0x01, 0x82, 0x00, 
+0x42, 0x00, 0x44, 0x00, 0xc8, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x03, 
+0x00, 0x03, 0x0e, 0x03};
+static unsigned char symbol18_195_bits[] = {
+0x1f, 0x01, 0xf8, 0x00, 0x70, 0x00, 0x9e, 0x03, 0x7f, 0x07, 0x71, 0x0e, 
+0x61, 0x0c, 0x63, 0x06, 0x6e, 0x01, 0x6c, 0x02, 0x64, 0x06, 0x60, 0x06, 
+0x60, 0x06, 0x60, 0x06, 0x60, 0x06};
+static unsigned char symbol18_196_bits[] = {
+0x36, 0x0e, 0x1d, 0x0c, 0x30, 0x00, 0x18, 0x00, 0x8c, 0x07, 0xe6, 0x0c, 
+0x33, 0x0c, 0x1b, 0x0c, 0x6b, 0x0c, 0x6f, 0x06, 0x26, 0x07, 0xee, 0x03, 
+0xcd, 0x01};
+static unsigned char symbol18_197_bits[] = {
+0x19, 0x00, 0x19, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0xf8, 0x01, 0x9c, 0x03, 
+0x06, 0x06, 0x9b, 0x0d, 0xbb, 0x0d, 0xf1, 0x08, 0x61, 0x08, 0xf1, 0x08, 
+0xdb, 0x0d};
+static unsigned char symbol18_198_bits[] = {
+0x9b, 0x0d, 0x06, 0x06, 0x9c, 0x03, 0xf8, 0x01, 0xf8, 0x01, 0x9c, 0x03, 
+0x66, 0x06, 0x63, 0x0c, 0x63, 0x0c, 0xfd, 0x0b, 0xfd, 0x0b, 0x61, 0x08, 
+0x63, 0x0c};
+static unsigned char symbol18_199_bits[] = {
+0x63, 0x0c, 0x06, 0x06, 0x9c, 0x03, 0xf8, 0x01, 0xf8, 0x0d, 0x9c, 0x0f, 
+0x06, 0x06, 0x03, 0x0f, 0x83, 0x0d};
+static unsigned char symbol18_200_bits[] = {
+0xc1, 0x08, 0x61, 0x08, 0x31, 0x08, 0x1b, 0x0c, 0x0f, 0x0c, 0x06, 0x06, 
+0x9f, 0x03, 0xfb, 0x01, 0xf8, 0x01};
+static unsigned char symbol18_201_bits[] = {
+0x9c, 0x03, 0x06, 0x06, 0x07, 0x0e, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 
+0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c};
+static unsigned char symbol18_202_bits[] = {
+0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x07, 0x0e, 0x06, 0x06, 
+0x9c, 0x03, 0xf8, 0x01, 0xff, 0x01, 0xff, 0x07, 0x00, 0x06, 0x00, 0x0c};
+static unsigned char symbol18_203_bits[] = {
+0x00, 0x0c, 0x00, 0x0c, 0x00, 0x06, 0xff, 0x07, 0xff, 0x01, 0xff, 0x01, 
+0xff, 0x07, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x06, 
+0xff, 0x07};
+static unsigned char symbol18_204_bits[] = {
+0xff, 0x01, 0x00, 0x00, 0xff, 0x0f, 0xff, 0x0f, 0x00, 0x06, 0x00, 0x03, 
+0xf8, 0x0f, 0xfe, 0x0f, 0x86, 0x01};
+static unsigned char symbol18_205_bits[] = {
+0xc3, 0x00, 0xc3, 0x00, 0x63, 0x00, 0x66, 0x00, 0xfe, 0x0f, 0xf8, 0x0f, 
+0x18, 0x00, 0x0c, 0x00, 0xf8, 0x0f, 0xfe, 0x0f, 0x06, 0x00, 0x03, 0x00};
+static unsigned char symbol18_206_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x06, 0x00, 0xfe, 0x01, 0xf8, 0x01, 0xf8, 0x01, 
+0xfe, 0x01, 0x06, 0x00, 0x03, 0x00};
+static unsigned char symbol18_207_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x06, 0x00, 0xfe, 0x03, 0xf8, 0x03, 0x00, 0x00, 
+0xff, 0x03, 0xff, 0x03, 0xf8, 0x01, 0xfe, 0x01, 0x07, 0x00};
+static unsigned char symbol18_208_bits[] = {
+0x03, 0x00, 0xff, 0x01, 0x03, 0x00, 0x07, 0x00, 0xfe, 0x01, 0xf8, 0x01, 
+0x00, 0x01, 0xf8, 0x03, 0xfe, 0x03, 0x47, 0x00, 0x23, 0x00, 0xff, 0x01, 
+0x13, 0x00};
+static unsigned char symbol18_209_bits[] = {
+0x0f, 0x00, 0xfe, 0x03, 0xfc, 0x03, 0x02, 0x00, 0x00, 0x08, 0x00, 0x0c, 
+0x00, 0x06, 0x00, 0x03, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 
+0x18, 0x00};
+static unsigned char symbol18_210_bits[] = {
+0x0c, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x1f, 0xff, 0x0f, 0x07, 0x04, 
+0x06, 0x06, 0x06, 0x02, 0x0e, 0x03, 0x0c, 0x01, 0x0c, 0x01, 0x9c, 0x01, 
+0x98, 0x00};
+static unsigned char symbol18_211_bits[] = {
+0xd8, 0x00, 0x70, 0x00, 0x70, 0x00, 0x20, 0x00, 0xf8, 0x03, 0x1c, 0x07, 
+0x06, 0x0c, 0xfb, 0x18, 0xb3, 0x19, 0xb3, 0x19, 0xb3, 0x19, 0xf3, 0x18, 
+0xb3, 0x19};
+static unsigned char symbol18_212_bits[] = {
+0x3b, 0x1b, 0x06, 0x0c, 0x1c, 0x07, 0xf8, 0x03, 0xf8, 0x03, 0x1c, 0x07, 
+0x06, 0x0c, 0xe3, 0x19};
+static unsigned char symbol18_213_bits[] = {
+0xb3, 0x19, 0x33, 0x19, 0x33, 0x18, 0x33, 0x18, 0x33, 0x19, 0xe3, 0x18, 
+0x06, 0x0c, 0x1c, 0x07, 0xf8, 0x03, 0xff, 0x38, 0xd5, 0x18, 0xc4, 0x18, 
+0x44, 0x15, 0x44, 0x15, 0x44, 0x15, 0x44, 0x12};
+static unsigned char symbol18_214_bits[] = {
+0xee, 0x02, 0xff, 0x03, 0x0e, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x3f, 0x03};
+static unsigned char symbol18_215_bits[] = {
+0x00, 0x02};
+static unsigned char symbol18_216_bits[] = {
+0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
+static unsigned char symbol18_217_bits[] = {
+0x0c, 0x01, 0x8e, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x98, 0x00, 0xb0, 0x00, 
+0xb0, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char symbol18_218_bits[] = {
+0x60, 0x00, 0x03, 0x03, 0xff, 0x03, 0xff, 0x03, 0x00, 0x02, 0x00, 0x02, 
+0x00, 0x02, 0x00, 0x02, 0x30, 0x00};
+static unsigned char symbol18_219_bits[] = {
+0x30, 0x00, 0x00, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0x02, 0x01, 0x86, 0x01, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x01, 0x86, 0x01, 
+0xcc, 0x00, 0x00};
+static unsigned char symbol18_220_bits[] = {
+0x00, 0x78, 0x00, 0x30, 0x00, 0x30, 0x00, 0x10, 0x20, 0x00, 0x18, 0x60, 
+0x00, 0xfc, 0xff, 0x00, 0xfe, 0xff};
+static unsigned char symbol18_221_bits[] = {
+0x01, 0x01, 0x80, 0x01, 0xfe, 0x01, 0x01, 0x00, 0xff, 0x00, 0x18, 0x00, 
+0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x18, 0x00, 0xfc, 0x01, 0xfe, 0x01, 
+0x07, 0x00, 0xfe, 0x01, 0xfc, 0x01, 0x18, 0x00, 0x10, 0x00};
+static unsigned char symbol18_222_bits[] = {
+0x10, 0x00, 0x38, 0x00, 0x6c, 0x00, 0xee, 0x00, 0xef, 0x01, 0x6c, 0x00, 
+0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00};
+static unsigned char symbol18_223_bits[] = {
+0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 
+0x6c, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 
+0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol18_224_bits[] = {
+0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 
+0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 
+0xef, 0x01, 0xee, 0x00};
+static unsigned char symbol18_225_bits[] = {
+0x0c, 0x00, 0x18, 0x00, 0x10, 0x00, 0x10, 0x00, 0x18, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x06, 0x00, 0x06};
+static unsigned char symbol18_226_bits[] = {
+0x00, 0x03, 0x01, 0x03, 0x01, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0c, 
+0x00, 0x18, 0x00, 0x10, 0x00, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 
+0x03, 0x01};
+static unsigned char symbol18_227_bits[] = {
+0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0xf8, 0x03, 0x1c, 0x07, 
+0x06, 0x0c, 0xf3, 0x18, 0xb3, 0x19, 0xb3, 0x19, 0xf3, 0x18, 0xb3, 0x19, 
+0xb3, 0x19};
+static unsigned char symbol18_228_bits[] = {
+0xb3, 0x19, 0x06, 0x0c, 0x1c, 0x07, 0xf8, 0x03, 0xf8, 0x03, 0x1c, 0x07, 
+0x06, 0x0c, 0xe3, 0x18};
+static unsigned char symbol18_229_bits[] = {
+0x33, 0x09, 0x33, 0x08, 0x33, 0x08, 0x33, 0x08, 0x33, 0x09, 0xe3, 0x08, 
+0x06, 0x0c, 0x1c, 0x07, 0xf8, 0x03, 0xdf, 0x08, 0xc4, 0x08, 0xc4, 0x08, 
+0x44, 0x05, 0x44, 0x05, 0x44, 0x05, 0x44, 0x02};
+static unsigned char symbol18_230_bits[] = {
+0x04, 0x12, 0x3f, 0x07, 0x07, 0x06, 0x0e, 0x04, 0x1c, 0x00, 0x38, 0x00, 
+0x30, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x30, 0x00, 0x18, 0x00, 
+0x0c};
+static unsigned char symbol18_231_bits[] = {
+0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char symbol18_232_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char symbol18_233_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x07};
+static unsigned char symbol18_234_bits[] = {
+0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char symbol18_235_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol18_236_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char symbol18_237_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x1f, 0x1c, 0x06, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char symbol18_238_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x07, 0x07};
+static unsigned char symbol18_239_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char symbol18_240_bits[] = {
+0x00};
+static unsigned char symbol18_241_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x1c, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol18_242_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x03, 0x06, 0x06};
+static unsigned char symbol18_243_bits[] = {
+0x0c, 0x0c, 0x18, 0x18, 0x10, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 
+0x03, 0x38, 0x38, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c};
+static unsigned char symbol18_244_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x02, 
+0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char symbol18_245_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol18_246_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
+0x20};
+static unsigned char symbol18_247_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x02, 0x01, 0x03, 
+0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00};
+static unsigned char symbol18_248_bits[] = {
+0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char symbol18_249_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x10, 0x10, 
+0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 
+0x10};
+static unsigned char symbol18_250_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x01, 0x03, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00};
+static unsigned char symbol18_251_bits[] = {
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol18_252_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18};
+static unsigned char symbol18_253_bits[] = {
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x07, 0x0c, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18};
+static unsigned char symbol18_254_bits[] = {
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06};
+static unsigned char symbol18_255_bits[] = {
+0x00};
+static RotFont symbol18font[] = {
+{5, 1, 1, symbol18_0_bits},
+{5, 1, 1, symbol18_1_bits},
+{5, 1, 1, symbol18_2_bits},
+{5, 1, 1, symbol18_3_bits},
+{5, 1, 1, symbol18_4_bits},
+{5, 1, 1, symbol18_5_bits},
+{5, 1, 1, symbol18_6_bits},
+{5, 1, 1, symbol18_7_bits},
+{5, 1, 1, symbol18_8_bits},
+{5, 1, 1, symbol18_9_bits},
+{5, 1, 1, symbol18_10_bits},
+{5, 1, 1, symbol18_11_bits},
+{5, 1, 1, symbol18_12_bits},
+{5, 1, 1, symbol18_13_bits},
+{5, 1, 1, symbol18_14_bits},
+{5, 1, 1, symbol18_15_bits},
+{5, 1, 1, symbol18_16_bits},
+{5, 1, 1, symbol18_17_bits},
+{5, 1, 1, symbol18_18_bits},
+{5, 1, 1, symbol18_19_bits},
+{5, 1, 1, symbol18_20_bits},
+{5, 1, 1, symbol18_21_bits},
+{5, 1, 1, symbol18_22_bits},
+{5, 1, 1, symbol18_23_bits},
+{5, 1, 1, symbol18_24_bits},
+{5, 1, 1, symbol18_25_bits},
+{5, 1, 1, symbol18_26_bits},
+{5, 1, 1, symbol18_27_bits},
+{5, 1, 1, symbol18_28_bits},
+{5, 1, 1, symbol18_29_bits},
+{5, 1, 1, symbol18_30_bits},
+{5, 1, 1, symbol18_31_bits},
+{1, 1, 1, symbol18_32_bits},
+{2, 13, 13, symbol18_33_bits},
+{13, 13, 13, symbol18_34_bits},
+{10, 13, 13, symbol18_35_bits},
+{8, 13, 13, symbol18_36_bits},
+{13, 13, 13, symbol18_37_bits},
+{12, 13, 13, symbol18_38_bits},
+{6, 9, 9, symbol18_39_bits},
+{4, 17, 13, symbol18_40_bits},
+{4, 17, 13, symbol18_41_bits},
+{7, 7, 10, symbol18_42_bits},
+{8, 8, 9, symbol18_43_bits},
+{3, 5, 2, symbol18_44_bits},
+{7, 2, 6, symbol18_45_bits},
+{2, 2, 2, symbol18_46_bits},
+{5, 13, 13, symbol18_47_bits},
+{8, 13, 13, symbol18_48_bits},
+{6, 13, 13, symbol18_49_bits},
+{7, 13, 13, symbol18_50_bits},
+{7, 13, 13, symbol18_51_bits},
+{8, 13, 13, symbol18_52_bits},
+{7, 13, 13, symbol18_53_bits},
+{8, 13, 13, symbol18_54_bits},
+{8, 13, 13, symbol18_55_bits},
+{8, 13, 13, symbol18_56_bits},
+{8, 13, 13, symbol18_57_bits},
+{2, 9, 9, symbol18_58_bits},
+{3, 12, 9, symbol18_59_bits},
+{9, 9, 9, symbol18_60_bits},
+{8, 6, 8, symbol18_61_bits},
+{9, 9, 9, symbol18_62_bits},
+{6, 13, 13, symbol18_63_bits},
+{8, 9, 9, symbol18_64_bits},
+{12, 13, 13, symbol18_65_bits},
+{9, 13, 13, symbol18_66_bits},
+{12, 13, 13, symbol18_67_bits},
+{11, 13, 13, symbol18_68_bits},
+{9, 13, 13, symbol18_69_bits},
+{12, 13, 13, symbol18_70_bits},
+{10, 13, 13, symbol18_71_bits},
+{11, 13, 13, symbol18_72_bits},
+{4, 13, 13, symbol18_73_bits},
+{11, 13, 13, symbol18_74_bits},
+{11, 13, 13, symbol18_75_bits},
+{12, 13, 13, symbol18_76_bits},
+{14, 13, 13, symbol18_77_bits},
+{11, 13, 13, symbol18_78_bits},
+{11, 13, 13, symbol18_79_bits},
+{11, 13, 13, symbol18_80_bits},
+{11, 13, 13, symbol18_81_bits},
+{8, 13, 13, symbol18_82_bits},
+{10, 13, 13, symbol18_83_bits},
+{10, 13, 13, symbol18_84_bits},
+{12, 13, 13, symbol18_85_bits},
+{6, 13, 9, symbol18_86_bits},
+{13, 13, 13, symbol18_87_bits},
+{10, 13, 13, symbol18_88_bits},
+{14, 13, 13, symbol18_89_bits},
+{9, 13, 13, symbol18_90_bits},
+{4, 17, 13, symbol18_91_bits},
+{10, 9, 9, symbol18_92_bits},
+{4, 17, 13, symbol18_93_bits},
+{10, 13, 13, symbol18_94_bits},
+{9, 2, -2, symbol18_95_bits},
+{10, 1, 17, symbol18_96_bits},
+{11, 9, 9, symbol18_97_bits},
+{8, 18, 14, symbol18_98_bits},
+{10, 13, 9, symbol18_99_bits},
+{7, 14, 14, symbol18_100_bits},
+{6, 9, 9, symbol18_101_bits},
+{9, 16, 12, symbol18_102_bits},
+{8, 13, 9, symbol18_103_bits},
+{9, 13, 9, symbol18_104_bits},
+{5, 9, 9, symbol18_105_bits},
+{9, 13, 9, symbol18_106_bits},
+{9, 9, 9, symbol18_107_bits},
+{10, 14, 14, symbol18_108_bits},
+{9, 13, 9, symbol18_109_bits},
+{8, 9, 9, symbol18_110_bits},
+{8, 9, 9, symbol18_111_bits},
+{9, 9, 9, symbol18_112_bits},
+{8, 13, 13, symbol18_113_bits},
+{8, 13, 9, symbol18_114_bits},
+{10, 9, 9, symbol18_115_bits},
+{7, 9, 9, symbol18_116_bits},
+{9, 9, 9, symbol18_117_bits},
+{11, 11, 11, symbol18_118_bits},
+{10, 9, 9, symbol18_119_bits},
+{7, 19, 15, symbol18_120_bits},
+{13, 13, 9, symbol18_121_bits},
+{7, 19, 15, symbol18_122_bits},
+{7, 17, 13, symbol18_123_bits},
+{2, 17, 13, symbol18_124_bits},
+{7, 17, 13, symbol18_125_bits},
+{8, 3, 7, symbol18_126_bits},
+{5, 1, 1, symbol18_127_bits},
+{5, 1, 1, symbol18_128_bits},
+{5, 1, 1, symbol18_129_bits},
+{5, 1, 1, symbol18_130_bits},
+{5, 1, 1, symbol18_131_bits},
+{5, 1, 1, symbol18_132_bits},
+{5, 1, 1, symbol18_133_bits},
+{5, 1, 1, symbol18_134_bits},
+{5, 1, 1, symbol18_135_bits},
+{5, 1, 1, symbol18_136_bits},
+{5, 1, 1, symbol18_137_bits},
+{5, 1, 1, symbol18_138_bits},
+{5, 1, 1, symbol18_139_bits},
+{5, 1, 1, symbol18_140_bits},
+{5, 1, 1, symbol18_141_bits},
+{5, 1, 1, symbol18_142_bits},
+{5, 1, 1, symbol18_143_bits},
+{5, 1, 1, symbol18_144_bits},
+{5, 1, 1, symbol18_145_bits},
+{5, 1, 1, symbol18_146_bits},
+{5, 1, 1, symbol18_147_bits},
+{5, 1, 1, symbol18_148_bits},
+{5, 1, 1, symbol18_149_bits},
+{5, 1, 1, symbol18_150_bits},
+{5, 1, 1, symbol18_151_bits},
+{5, 1, 1, symbol18_152_bits},
+{5, 1, 1, symbol18_153_bits},
+{5, 1, 1, symbol18_154_bits},
+{5, 1, 1, symbol18_155_bits},
+{5, 1, 1, symbol18_156_bits},
+{5, 1, 1, symbol18_157_bits},
+{5, 1, 1, symbol18_158_bits},
+{5, 1, 1, symbol18_159_bits},
+{5, 1, 1, symbol18_160_bits},
+{11, 13, 13, symbol18_161_bits},
+{4, 5, 14, symbol18_162_bits},
+{9, 12, 12, symbol18_163_bits},
+{8, 13, 13, symbol18_164_bits},
+{12, 5, 8, symbol18_165_bits},
+{9, 17, 13, symbol18_166_bits},
+{10, 9, 9, symbol18_167_bits},
+{9, 9, 9, symbol18_168_bits},
+{9, 9, 9, symbol18_169_bits},
+{10, 9, 9, symbol18_170_bits},
+{17, 8, 9, symbol18_171_bits},
+{18, 8, 9, symbol18_172_bits},
+{8, 25, 18, symbol18_173_bits},
+{18, 8, 9, symbol18_174_bits},
+{8, 22, 18, symbol18_175_bits},
+{5, 6, 13, symbol18_176_bits},
+{8, 11, 11, symbol18_177_bits},
+{8, 5, 14, symbol18_178_bits},
+{9, 12, 12, symbol18_179_bits},
+{10, 9, 9, symbol18_180_bits},
+{11, 5, 8, symbol18_181_bits},
+{7, 14, 14, symbol18_182_bits},
+{5, 6, 8, symbol18_183_bits},
+{8, 8, 9, symbol18_184_bits},
+{8, 9, 9, symbol18_185_bits},
+{8, 8, 9, symbol18_186_bits},
+{8, 7, 9, symbol18_187_bits},
+{14, 2, 2, symbol18_188_bits},
+{2, 25, 18, symbol18_189_bits},
+{19, 2, 6, symbol18_190_bits},
+{9, 12, 12, symbol18_191_bits},
+{11, 13, 13, symbol18_192_bits},
+{10, 15, 14, symbol18_193_bits},
+{13, 14, 14, symbol18_194_bits},
+{12, 15, 11, symbol18_195_bits},
+{12, 13, 13, symbol18_196_bits},
+{12, 13, 13, symbol18_197_bits},
+{12, 13, 13, symbol18_198_bits},
+{12, 9, 9, symbol18_199_bits},
+{12, 9, 9, symbol18_200_bits},
+{12, 9, 9, symbol18_201_bits},
+{12, 12, 9, symbol18_202_bits},
+{12, 13, 11, symbol18_203_bits},
+{12, 9, 9, symbol18_204_bits},
+{12, 12, 9, symbol18_205_bits},
+{9, 9, 9, symbol18_206_bits},
+{10, 11, 10, symbol18_207_bits},
+{13, 13, 13, symbol18_208_bits},
+{12, 13, 13, symbol18_209_bits},
+{13, 13, 13, symbol18_210_bits},
+{13, 13, 13, symbol18_211_bits},
+{14, 8, 13, symbol18_212_bits},
+{14, 16, 14, symbol18_213_bits},
+{10, 17, 17, symbol18_214_bits},
+{2, 2, 6, symbol18_215_bits},
+{11, 6, 6, symbol18_216_bits},
+{10, 9, 9, symbol18_217_bits},
+{10, 9, 9, symbol18_218_bits},
+{18, 9, 9, symbol18_219_bits},
+{16, 9, 9, symbol18_220_bits},
+{9, 17, 17, symbol18_221_bits},
+{16, 9, 9, symbol18_222_bits},
+{9, 17, 17, symbol18_223_bits},
+{9, 14, 14, symbol18_224_bits},
+{5, 17, 14, symbol18_225_bits},
+{13, 13, 13, symbol18_226_bits},
+{13, 13, 13, symbol18_227_bits},
+{13, 8, 13, symbol18_228_bits},
+{12, 16, 14, symbol18_229_bits},
+{6, 25, 18, symbol18_230_bits},
+{2, 25, 18, symbol18_231_bits},
+{6, 25, 18, symbol18_232_bits},
+{5, 25, 18, symbol18_233_bits},
+{2, 25, 18, symbol18_234_bits},
+{5, 22, 18, symbol18_235_bits},
+{5, 25, 18, symbol18_236_bits},
+{5, 25, 18, symbol18_237_bits},
+{5, 22, 18, symbol18_238_bits},
+{2, 25, 18, symbol18_239_bits},
+{5, 1, 1, symbol18_240_bits},
+{5, 17, 14, symbol18_241_bits},
+{6, 22, 18, symbol18_242_bits},
+{7, 25, 18, symbol18_243_bits},
+{2, 25, 18, symbol18_244_bits},
+{7, 22, 18, symbol18_245_bits},
+{6, 25, 18, symbol18_246_bits},
+{2, 25, 18, symbol18_247_bits},
+{6, 25, 18, symbol18_248_bits},
+{5, 25, 18, symbol18_249_bits},
+{2, 25, 18, symbol18_250_bits},
+{5, 22, 18, symbol18_251_bits},
+{5, 25, 18, symbol18_252_bits},
+{5, 25, 18, symbol18_253_bits},
+{5, 22, 18, symbol18_254_bits},
+{5, 1, 1, symbol18_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol24.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol24.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol24.bdf	(revision 22322)
@@ -0,0 +1,4576 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Symbol-Medium-R-Normal--24-240-75-75-P-142-Adobe-FontSpecific
+SIZE 24 75 75
+FONTBOUNDINGBOX 26 32 -1 -8
+STARTPROPERTIES 31
+FOUNDRY "Adobe"
+FAMILY_NAME "Symbol"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 24
+POINT_SIZE 240
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 142
+CHARSET_REGISTRY "Adobe"
+CHARSET_ENCODING "FontSpecific"
+CAP_HEIGHT 17
+X_HEIGHT 13
+FACE_NAME "Symbol"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "No mark"
+_DEC_DEVICE_FONTNAMES "PS=Symbol"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2-1, 18-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+FULL_NAME "Symbol"
+FONT "-Adobe-Symbol-Medium-R-Normal--24-240-75-75-P-142-Adobe-FontSpecific"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 15
+DEFAULT_CHAR 32
+FONT_ASCENT 20
+FONT_DESCENT 5
+ENDPROPERTIES
+CHARS 188
+STARTCHAR space
+ENCODING 32
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 1 1 1 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 3 17 2 0
+BITMAP
+40
+e0
+e0
+e0
+e0
+e0
+e0
+e0
+40
+40
+40
+40
+00
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 17 1 0
+BITMAP
+c006
+c006
+600c
+600c
+3ff8
+3ff8
+3018
+1830
+1830
+0c60
+0c60
+0c60
+06c0
+06c0
+0380
+0380
+0380
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+0880
+0880
+0880
+0880
+0880
+0880
+7fe0
+1100
+1100
+1100
+ffc0
+2200
+2200
+2200
+2200
+2200
+2200
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+ffe0
+ffe0
+0060
+0060
+0060
+0060
+0060
+ffe0
+ffe0
+0060
+0060
+0060
+0060
+0060
+0060
+ffe0
+ffe0
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 800 0
+DWIDTH 20 0
+BBX 18 17 1 0
+BITMAP
+1f0600
+39fe00
+710c00
+611800
+e31800
+c23000
+c66000
+cc6000
+78c780
+018ec0
+019c40
+033840
+0630c0
+063080
+0c3180
+183300
+181e00
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 17 1 0
+BITMAP
+0780
+0cc0
+1840
+1840
+18c0
+1d80
+0f3e
+1e1c
+3e18
+6730
+c3a0
+c1e0
+c0c0
+c1e0
+e372
+7e3e
+3c1c
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 8 13 2 0
+BITMAP
+f0
+3c
+0e
+07
+03
+03
+7f
+03
+03
+07
+0e
+3c
+f0
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 22 1 -5
+BITMAP
+0c
+18
+30
+20
+60
+60
+40
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+40
+60
+60
+20
+30
+18
+0c
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 22 1 -5
+BITMAP
+c0
+60
+30
+10
+18
+18
+08
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+08
+18
+18
+10
+30
+60
+c0
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 8 10 2 7
+BITMAP
+18
+18
+c3
+e7
+18
+18
+e7
+c3
+18
+18
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 10 1 2
+BITMAP
+0c00
+0c00
+0c00
+0c00
+ffc0
+ffc0
+0c00
+0c00
+0c00
+0c00
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 3 6 1 -4
+BITMAP
+60
+60
+20
+60
+c0
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 2 1 6
+BITMAP
+ffe0
+ffe0
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 3 2 0
+BITMAP
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 7 17 0 0
+BITMAP
+06
+06
+06
+0c
+0c
+0c
+18
+18
+30
+30
+30
+60
+60
+60
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1e00
+3300
+6180
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+6180
+3300
+1e00
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 6 17 3 0
+BITMAP
+30
+70
+f0
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+fc
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1c00
+7f00
+6700
+8380
+8180
+0180
+0180
+0380
+0300
+0700
+0c00
+0c00
+1800
+3000
+6040
+ffc0
+ff80
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 9 17 1 0
+BITMAP
+1c00
+7e00
+4700
+8300
+8300
+0600
+0c00
+1e00
+0700
+0300
+0180
+0180
+0180
+0300
+c700
+fe00
+7800
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+0300
+0700
+0700
+0f00
+0b00
+1b00
+1300
+3300
+2300
+6300
+4300
+ffc0
+ffc0
+0300
+0300
+0300
+0300
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 9 17 1 0
+BITMAP
+3f80
+3f00
+6000
+4000
+c000
+f000
+fc00
+1e00
+0700
+0380
+0180
+0180
+0180
+0180
+c300
+fe00
+7c00
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+03c0
+0e00
+3800
+7000
+6000
+e000
+cf00
+f380
+e180
+c0c0
+c0c0
+c0c0
+c0c0
+e0c0
+61c0
+7380
+1f00
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+7fc0
+ffc0
+c0c0
+8180
+0180
+0300
+0300
+0300
+0600
+0600
+0600
+0c00
+0c00
+0c00
+1800
+1800
+1800
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1e00
+3300
+6180
+6180
+6180
+7300
+3e00
+1c00
+3f00
+6380
+c1c0
+c0c0
+c0c0
+c0c0
+c1c0
+6380
+3e00
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1e00
+7380
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+61c0
+73c0
+1ec0
+0180
+0380
+0700
+0e00
+3c00
+f000
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 11 2 0
+BITMAP
+c0
+c0
+c0
+00
+00
+00
+00
+00
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 3 15 1 -4
+BITMAP
+60
+60
+60
+00
+00
+00
+00
+00
+00
+60
+60
+20
+60
+c0
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 12 12 0 1
+BITMAP
+0070
+01e0
+0780
+1e00
+7800
+e000
+e000
+7800
+1e00
+0780
+01e0
+0070
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 6 1 4
+BITMAP
+ffc0
+ffc0
+0000
+0000
+ffc0
+ffc0
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 12 12 0 1
+BITMAP
+e000
+7800
+1e00
+0780
+01e0
+0070
+0070
+01e0
+0780
+1e00
+7800
+e000
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 8 17 2 0
+BITMAP
+7c
+c6
+83
+c3
+c7
+06
+0e
+0c
+18
+18
+10
+10
+10
+00
+00
+30
+30
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 12 1 1
+BITMAP
+3820
+7c60
+c7c0
+8380
+0000
+0000
+ffe0
+ffe0
+0000
+0000
+ffe0
+ffe0
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 17 17 0 0
+BITMAP
+008000
+01c000
+01c000
+014000
+036000
+026000
+023000
+063000
+043000
+0c1800
+081800
+0ffc00
+180c00
+100c00
+300600
+300600
+f80f80
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+ffc0
+30e0
+3070
+3030
+3030
+3030
+3030
+3060
+3fc0
+3070
+3038
+3018
+3018
+3018
+3038
+3070
+ffc0
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 17 17 1 0
+BITMAP
+f81f80
+700e00
+380c00
+1c1800
+0c3000
+066000
+07c000
+038000
+018000
+03c000
+06e000
+0c7000
+083800
+181800
+301c00
+700e00
+f81f80
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 16 17 0 0
+BITMAP
+0300
+0300
+0780
+0480
+04c0
+0cc0
+0860
+0860
+1830
+1030
+3018
+2018
+200c
+600c
+4006
+c006
+ffff
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+fff0
+3030
+3010
+3000
+3000
+3000
+3020
+3020
+3fe0
+3020
+3020
+3000
+3000
+3008
+3008
+3018
+fff8
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 16 17 1 0
+BITMAP
+07e0
+0180
+0180
+0ff0
+399c
+6186
+e187
+c183
+c183
+c183
+e187
+6186
+399c
+0ff0
+0180
+0180
+07e0
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+fff8
+3018
+3008
+3008
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+fc00
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+fc3f
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+3ffc
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+fc3f
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 17 1 0
+BITMAP
+fc
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+fc
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 17 1 0
+BITMAP
+03c0
+06e0
+0460
+0660
+0330
+71b0
+f8f0
+983c
+1830
+1830
+3030
+3030
+3030
+3060
+3860
+1cc0
+0f80
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 17 1 0
+BITMAP
+fc7c
+3030
+3060
+30c0
+3180
+3300
+3600
+3e00
+3f00
+3380
+3180
+31c0
+30e0
+3070
+3038
+301c
+fc3e
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 17 1 0
+BITMAP
+0300
+0300
+0780
+0480
+04c0
+04c0
+0cc0
+0860
+0860
+1860
+1030
+1030
+3030
+2018
+2018
+601c
+f83e
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 880 0
+DWIDTH 22 0
+BBX 20 17 1 0
+BITMAP
+f001f0
+3801c0
+3803c0
+3c02c0
+2c02c0
+2e06c0
+2604c0
+270cc0
+2308c0
+2388c0
+2198c0
+21d0c0
+20d0c0
+20f0c0
+2060c0
+2060c0
+f803f0
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+f01f
+3804
+3804
+2c04
+2e04
+2604
+2304
+2304
+2184
+21c4
+20c4
+2064
+2064
+2034
+203c
+201c
+f80c
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+07e0
+1c38
+300c
+6006
+6006
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+6006
+6006
+300c
+1c38
+07e0
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+ffff
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+300c
+fc3f
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+07e0
+1c38
+300c
+6006
+6006
+c813
+c813
+cff3
+cff3
+c813
+c813
+c003
+4002
+6006
+300c
+1c38
+07e0
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 17 1 0
+BITMAP
+ff80
+30e0
+3060
+3030
+3030
+3030
+3060
+30e0
+3f80
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+fc00
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 17 1 0
+BITMAP
+fff0
+e030
+7010
+3800
+1c00
+0e00
+0700
+0380
+0300
+0600
+0c00
+1800
+3010
+6010
+c030
+fff0
+fff0
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 17 1 0
+BITMAP
+fffc
+c30c
+8304
+8304
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0fc0
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 16 17 0 0
+BITMAP
+f81f
+700e
+300c
+1818
+1c10
+0c30
+0e60
+0660
+03c0
+03c0
+0180
+0180
+0180
+0180
+0180
+0180
+07e0
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 18 1 -5
+BITMAP
+1f80
+3f80
+6000
+6000
+c000
+c000
+c000
+c000
+c000
+c000
+6000
+7f00
+1f80
+0080
+0080
+0180
+0f00
+0e00
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+07e0
+1c38
+300c
+6006
+6006
+c003
+c003
+c003
+c003
+e007
+6006
+300c
+1818
+8c31
+8421
+fc3f
+fc3f
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 17 1 0
+BITMAP
+7ff8
+7ff8
+6018
+4008
+0000
+1020
+1020
+1fe0
+1fe0
+1020
+1020
+0000
+8004
+8004
+c00c
+fffc
+fffc
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 800 0
+DWIDTH 20 0
+BBX 18 17 1 0
+BITMAP
+e3f1c0
+70c380
+30c300
+30c300
+38c700
+38c700
+18c600
+1cce00
+07f800
+00c000
+00c000
+00c000
+00c000
+00c000
+00c000
+00c000
+03f000
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 12 17 1 0
+BITMAP
+fff0
+c070
+8060
+00e0
+01c0
+0180
+0380
+0700
+0600
+0e00
+1c00
+3800
+3800
+7000
+6010
+e030
+fff0
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 5 22 2 -5
+BITMAP
+f8
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+f8
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 12 10 3 0
+BITMAP
+0600
+0600
+0600
+0000
+0000
+0000
+0000
+c030
+c030
+c030
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 5 22 1 -5
+BITMAP
+f8
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+f8
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 17 1 0
+BITMAP
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+fffc
+fffc
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 13 2 0 -5
+BITMAP
+fff8
+fff8
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 13 2 0 21
+BITMAP
+fff8
+fff8
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 13 1 0
+BITMAP
+1f18
+71b8
+60b0
+e0f0
+c060
+c060
+c060
+c060
+c060
+e0e0
+60e4
+71fc
+1f38
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 24 1 -5
+BITMAP
+3e00
+6700
+c380
+c180
+c180
+c380
+c300
+ce00
+cf00
+c380
+c180
+c1c0
+c0c0
+c0c0
+c0c0
+c1c0
+e180
+f380
+df00
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 18 1 -5
+BITMAP
+7060
+f860
+d8c0
+88c0
+0980
+0980
+0b00
+0700
+0600
+0600
+0e00
+0e00
+1900
+1900
+3110
+31b0
+61f0
+60e0
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 18 1 0
+BITMAP
+1f00
+3380
+2180
+3000
+3800
+1c00
+0e00
+3f00
+7380
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+7380
+1e00
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 13 1 0
+BITMAP
+3e00
+7300
+e300
+c300
+e000
+6000
+3c00
+6000
+e000
+c000
+e080
+7180
+3f00
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 22 1 -5
+BITMAP
+0600
+0600
+0600
+0600
+1f80
+36c0
+6660
+e670
+c630
+c630
+c630
+c630
+c630
+e670
+6660
+36c0
+1f80
+0600
+0600
+0600
+0600
+0600
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 11 18 0 -5
+BITMAP
+7060
+f860
+9860
+98c0
+18c0
+0cc0
+0d80
+0d80
+0580
+0700
+0700
+0300
+0600
+0600
+0600
+0600
+0600
+0600
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 12 18 1 -5
+BITMAP
+71c0
+fbe0
+9e30
+1c30
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+0030
+0030
+0030
+0030
+0030
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 7 13 1 0
+BITMAP
+30
+70
+f0
+30
+30
+30
+30
+30
+30
+30
+32
+3e
+1c
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 18 1 -5
+BITMAP
+3380
+73c0
+6660
+c670
+c630
+c630
+c630
+c630
+c630
+e670
+6660
+36c0
+1f80
+0600
+0600
+0600
+0600
+0600
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+3070
+70f0
+f1b0
+3300
+3600
+3c00
+3c00
+3e00
+3700
+3380
+31c0
+30e0
+31f0
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 19 1 0
+BITMAP
+7000
+f800
+8800
+0800
+0800
+0c00
+0c00
+0c00
+1c00
+1e00
+1a00
+3200
+3200
+3300
+6100
+6100
+6190
+c1f0
+c0e0
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 18 2 -5
+BITMAP
+c180
+c180
+c180
+c180
+c180
+c180
+c180
+c180
+c180
+c180
+e390
+fff0
+de60
+c000
+c000
+e000
+e000
+e000
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+c0e0
+c0e0
+c060
+6040
+60c0
+6080
+3180
+3100
+3300
+1a00
+1a00
+0e00
+0c00
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+1f00
+71c0
+60c0
+e0e0
+c060
+c060
+c060
+c060
+c060
+e0e0
+60c0
+71c0
+1f00
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 13 13 0 0
+BITMAP
+7ff8
+fff8
+98c0
+18c0
+18c0
+18c0
+18c0
+18c0
+18c0
+18c0
+18c8
+38f8
+30f0
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1e00
+3300
+6180
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+ffc0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+6180
+3300
+1e00
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 18 1 -5
+BITMAP
+3e00
+6380
+c180
+c1c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c1c0
+c180
+e380
+de00
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 14 13 1 0
+BITMAP
+1ffc
+71fc
+60c0
+e0e0
+c060
+c060
+c060
+c060
+c060
+e0e0
+60c0
+71c0
+1f00
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 10 13 0 0
+BITMAP
+3fc0
+7fc0
+cc00
+8c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c80
+0f80
+0700
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 13 13 0 0
+BITMAP
+30e0
+7830
+d818
+9818
+1818
+1818
+1818
+1818
+1818
+1830
+0c30
+0ee0
+07c0
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 14 1 0
+BITMAP
+7fff
+ffff
+9818
+300c
+318c
+6186
+6186
+6186
+6186
+6186
+6186
+318c
+3bdc
+1e78
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 13 1 0
+BITMAP
+1860
+3030
+6018
+6318
+c30c
+c30c
+c30c
+c30c
+c30c
+c30c
+6318
+77b8
+3cf0
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 24 1 -5
+BITMAP
+3000
+6380
+6780
+3f00
+3000
+2000
+3780
+1f80
+3000
+6000
+c000
+8000
+8000
+8000
+c000
+f000
+7c00
+3f80
+0fc0
+00c0
+0040
+00c0
+0f80
+0f00
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 18 1 -5
+BITMAP
+c183
+718e
+318c
+399c
+399c
+399c
+399c
+399c
+399c
+1998
+1998
+0db0
+07e0
+0180
+0180
+0180
+0180
+0180
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 24 1 -5
+BITMAP
+3000
+6380
+6780
+3f00
+1800
+3000
+2000
+6000
+4000
+c000
+8000
+8000
+8000
+8000
+c000
+f000
+7c00
+3f80
+0fc0
+00c0
+0040
+00c0
+0f80
+0f00
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 8 22 1 -5
+BITMAP
+07
+0c
+18
+18
+18
+18
+18
+10
+30
+20
+c0
+20
+30
+10
+18
+18
+18
+18
+18
+18
+0c
+07
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 160 0
+DWIDTH 4 0
+BBX 2 22 1 -5
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 8 22 1 -5
+BITMAP
+e0
+30
+18
+18
+18
+18
+18
+08
+0c
+04
+03
+04
+0c
+08
+18
+18
+18
+18
+18
+18
+30
+e0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 4 1 5
+BITMAP
+3820
+7c60
+c7c0
+8380
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 5 6 1 12
+BITMAP
+e0
+70
+70
+d8
+38
+b8
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 15 0 0
+BITMAP
+1b30
+0e00
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+1f80
+1830
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 160 0
+DWIDTH 4 0
+BBX 8 17 -1 0
+BITMAP
+70
+60
+c0
+80
+00
+70
+01
+e0
+07
+80
+1e
+00
+78
+00
+e0
+00
+e0
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 7 1 4
+BITMAP
+0078
+001e
+0002
+c000
+f000
+3000
+00fe
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 13 22 0 -5
+BITMAP
+f0f8
+f000
+0300
+0608
+0c18
+1818
+3030
+6060
+60c0
+c0c0
+3838
+7e78
+c7c0
+c380
+c6c0
+7c78
+3838
+00f0
+0198
+0198
+0300
+0300
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 14 13 2 0
+BITMAP
+0300
+1fe0
+0300
+0300
+0300
+0600
+0600
+0600
+0600
+0600
+0600
+0c00
+0c00
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 10 13 4 0
+BITMAP
+cc00
+cc00
+d800
+7000
+0780
+0fc0
+0fc0
+0fc0
+0fc0
+7780
+fb40
+ffc0
+ffc0
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 12 13 3 0
+BITMAP
+fb70
+7330
+0300
+0780
+0c00
+0c00
+1e00
+3f00
+3f00
+7f80
+ffc0
+7f80
+3f00
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 12 13 3 0
+BITMAP
+3f00
+1e00
+0c00
+0c00
+70e0
+f9f0
+f9f0
+fff0
+fff0
+fff0
+7fe0
+7fe0
+3fc0
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 1040 0
+DWIDTH 26 0
+BBX 24 12 1 1
+BITMAP
+1f800f
+000f00
+060006
+000600
+0f001f
+803fc0
+7fe07f
+e0fff0
+fff0f6
+f06660
+06000f
+000600
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 1000 0
+DWIDTH 25 0
+BBX 25 12 0 1
+BITMAP
+600c0000
+18001800
+000c6000
+06ffff80
+ffffff00
+00063000
+0c180000
+0c003000
+00600600
+00000c00
+00001800
+00003000
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 31 1 -8
+BITMAP
+0000
+6000
+0000
+fff0
+ff80
+fff0
+ff80
+6000
+0000
+3000
+0000
+1800
+0000
+0c00
+0000
+0600
+0000
+0600
+0f00
+1f80
+36c0
+6660
+c630
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 1000 0
+DWIDTH 25 0
+BBX 25 12 0 1
+BITMAP
+06000600
+06000600
+06000600
+06000600
+06000600
+06000600
+06000600
+06000600
+06000000
+30000000
+18000000
+0c000000
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 29 1 -5
+BITMAP
+0600
+0000
+0300
+fff0
+ff80
+fff0
+ff80
+0000
+0300
+0000
+0600
+0000
+0c00
+0000
+1800
+0000
+3000
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 360 0
+DWIDTH 9 0
+BBX 7 7 1 10
+BITMAP
+06
+00
+06
+00
+06
+00
+06
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 14 1 0
+BITMAP
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+00c0
+3040
+6000
+c000
+8000
+0000
+0000
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 9 6 1 12
+BITMAP
+6c80
+8280
+6c00
+0c00
+0c00
+0c00
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 15 0 0
+BITMAP
+0c00
+ffc0
+ffc0
+0c00
+0c00
+0c00
+0c00
+0000
+0000
+ffc0
+ffc0
+1980
+3b80
+7700
+6600
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 10 1 2
+BITMAP
+cc00
+8800
+e000
+7800
+1e00
+0780
+01e0
+0060
+0060
+01e0
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 7 1 4
+BITMAP
+0780
+3e00
+f000
+c000
+0000
+fff0
+fff0
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 19 1 0
+BITMAP
+c040
+60c0
+3180
+1b00
+0e00
+0e00
+1b00
+3180
+60c0
+c040
+3800
+7c40
+c6c0
+c380
+c7c0
+7c40
+3800
+3e00
+6300
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 8 8 2 3
+BITMAP
+c1
+80
+01
+80
+00
+c0
+00
+c0
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 8 1 3
+BITMAP
+00c0
+00c0
+1ec0
+73c0
+61c0
+c0c0
+c0c0
+c180
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 13 2 0
+BITMAP
+c180
+c180
+c300
+6700
+3c00
+3c40
+ffc0
+ffc0
+7e00
+0c00
+0c00
+0000
+ffc0
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 8 1 3
+BITMAP
+ffc0
+0000
+0c00
+0c00
+0180
+0180
+0300
+0300
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 8 1 3
+BITMAP
+ffc0
+ffc0
+0c00
+ffc0
+ffc0
+3000
+3000
+6000
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 960 0
+DWIDTH 24 0
+BBX 18 3 3 0
+BITMAP
+6000c0
+e0ffc0
+0000c0
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 2 32 6 -8
+BITMAP
+c0
+c0
+c0
+00
+00
+c0
+c0
+c0
+c0
+00
+00
+40
+40
+c0
+c0
+80
+80
+00
+00
+40
+40
+c0
+c0
+80
+80
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 1000 0
+DWIDTH 25 0
+BBX 25 2 0 6
+BITMAP
+c0c0c080
+c0c0c080
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 16 1 0
+BITMAP
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+fffc
+ff80
+fffc
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 16 1 0
+BITMAP
+ff80
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+2000
+6000
+fff0
+fff0
+6000
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 19 1 -1
+BITMAP
+2000
+8180
+c300
+e3f0
+71f0
+3860
+1cc0
+3c80
+6e80
+c780
+c380
+e380
+f1c0
+70e0
+3070
+7030
+e010
+0f80
+3ffc
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 17 18 1 0
+BITMAP
+70f800
+304000
+40c000
+c03080
+1c7000
+380000
+001c00
+1c0000
+300c00
+0cde00
+0ff000
+c01e00
+003f00
+006380
+00c180
+008180
+00c180
+00e180
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 15 20 2 -5
+BITMAP
+0070
+b800
+39b0
+0018
+9800
+1198
+0000
+9800
+019c
+0000
+8c00
+018c
+0018
+8680
+3f06
+804e
+0700
+0600
+0c00
+183c
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+30fe
+3186
+6306
+6606
+6c06
+680c
+799c
+7398
+7230
+7a70
+5be0
+dd80
+8c00
+8c00
+8c00
+d800
+7000
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+07e0
+1e78
+381c
+6006
+6816
+cc33
+c663
+c3c3
+c183
+c3c3
+c663
+cc33
+6816
+6006
+381c
+1e78
+07e0
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 800 0
+DWIDTH 20 0
+BBX 18 17 1 0
+BITMAP
+07e000
+783800
+618640
+86c180
+c183c0
+fbdfc0
+c183c0
+83c180
+618640
+063800
+1e7800
+e003c0
+c00fc0
+801c00
+003000
+003000
+006000
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 13 1 0
+BITMAP
+8060
+3180
+6061
+8060
+c180
+6181
+8063
+0180
+6601
+803c
+0300
+3803
+003c
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 13 1 0
+BITMAP
+0e00
+6ffc
+00c3
+f000
+07e0
+1ff8
+381c
+6006
+6006
+c003
+c003
+c003
+c003
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 12 1 0
+BITMAP
+c002
+c002
+c002
+c002
+c002
+c002
+c002
+c002
+c002
+c002
+c002
+c002
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 15 1 -3
+BITMAP
+6006
+6006
+381c
+1ff8
+07e0
+ffe0
+fff8
+001c
+000c
+0006
+0006
+0006
+0006
+000c
+001c
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 16 1 -2
+BITMAP
+fff8
+ffe0
+ffe0
+fff8
+001c
+000c
+0006
+0006
+0006
+0006
+000c
+001c
+fff8
+ffe0
+0000
+fffe
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 12 1 0
+BITMAP
+fffe
+000c
+000c
+0ffe
+3ffe
+7030
+6030
+c060
+c060
+c0c0
+c0c0
+6180
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 15 1 -3
+BITMAP
+7180
+3ffe
+0ffe
+0600
+0600
+0ffe
+3ffe
+7000
+6000
+c000
+c000
+c000
+c000
+6000
+7000
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 12 13 2 0
+BITMAP
+3ff0
+0ff0
+0ff0
+3ff0
+7000
+6000
+c000
+c000
+c000
+c000
+6000
+7000
+3ff0
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 12 17 2 -2
+BITMAP
+0ff0
+0000
+fff0
+fff0
+1ff0
+7ff0
+6000
+c000
+c000
+fff0
+fff0
+c000
+c000
+c000
+6000
+7ff0
+1ff0
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 17 16 1 0
+BITMAP
+00c000
+c01f80
+7ff000
+80c180
+c30080
+f0ff80
+c60080
+00cc00
+6c0000
+f01f80
+180000
+000000
+800000
+000000
+000000
+000000
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 18 1 0
+BITMAP
+0000
+3000
+0060
+0000
+c000
+0180
+0003
+0000
+0600
+000c
+0000
+1800
+0030
+0000
+7fff
+80ff
+ff80
+ffff
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+c003
+c002
+6006
+6004
+300c
+3008
+3018
+1810
+1830
+0c20
+0c20
+0660
+0640
+03c0
+03c0
+0180
+0180
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+07e0
+1e78
+381c
+6006
+6fe6
+c673
+c633
+c673
+c7e3
+c6c3
+c663
+c633
+6f3e
+6006
+381c
+1e78
+07e0
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 880 0
+DWIDTH 22 0
+BBX 20 10 1 7
+BITMAP
+07e010
+783810
+600660
+c6c660
+cc23c0
+03cc00
+cc03c0
+23c660
+63c660
+063810
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 17 21 1 -3
+BITMAP
+1e7800
+e0ff80
+709900
+601800
+e01800
+e01800
+601800
+601800
+601800
+601800
+603c80
+f0ff80
+803000
+003000
+003000
+003000
+003000
+003000
+003000
+003000
+003000
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 14 23 0 0
+BITMAP
+0030
+0600
+3004
+0030
+0600
+3004
+0030
+0600
+3004
+0030
+0600
+3004
+0030
+0600
+3004
+00fc
+1f80
+0004
+0004
+0004
+000c
+000c
+0008
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 3 2 6
+BITMAP
+00
+00
+00
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 8 1 0
+BITMAP
+0800
+1830
+1870
+10d8
+1018
+100c
+300c
+3006
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 12 1 0
+BITMAP
+2000
+2000
+2000
+6000
+c000
+c000
+c000
+c0c0
+c0c0
+fff8
+fff8
+0000
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 12 1 0
+BITMAP
+0000
+0000
+0000
+0000
+0000
+0700
+0700
+0d80
+0d80
+18c0
+18c0
+3060
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 1000 0
+DWIDTH 25 0
+BBX 23 12 1 0
+BITMAP
+306060
+306030
+c018c0
+18c018
+c01860
+306030
+306030
+6018c0
+18c00c
+800d80
+070006
+000600
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 1000 0
+DWIDTH 25 0
+BBX 23 12 1 0
+BITMAP
+c00c00
+601800
+303ffe
+f87ffe
+fce000
+0ee000
+0e7ffe
+fc3ffe
+f81800
+300c00
+600600
+c00600
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 23 1 0
+BITMAP
+0000
+0000
+1800
+0030
+fff0
+7ff0
+fee0
+0000
+e000
+0070
+fff0
+3ff0
+fe10
+0000
+0c00
+0000
+0000
+0600
+0f00
+1f80
+39c0
+79e0
+d9b0
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 1000 0
+DWIDTH 25 0
+BBX 23 12 1 0
+BITMAP
+198018
+801980
+198018
+801980
+198018
+801980
+198018
+801980
+198018
+801980
+198018
+800000
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 23 1 0
+BITMAP
+c000
+0060
+0000
+30f0
+fff0
+fff0
+fc00
+0000
+0000
+0ef0
+fff0
+fff0
+f800
+0030
+0000
+6000
+00c0
+1980
+1980
+1980
+1980
+1980
+1980
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 18 1 0
+BITMAP
+1980
+1980
+1980
+1980
+1980
+1980
+1980
+1980
+1980
+1980
+1980
+d980
+79c0
+39c0
+1f80
+0f00
+0600
+0c00
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 7 22 0 -5
+BITMAP
+0c
+00
+1e
+00
+32
+00
+32
+00
+60
+80
+60
+80
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+60
+80
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+6180
+3300
+3300
+1e00
+0c00
+0c00
+0606
+0c0c
+1818
+3030
+6060
+c0c0
+6060
+3030
+1818
+0c0c
+0606
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+07e0
+1e78
+381c
+6006
+67e6
+c633
+c633
+c633
+c7e3
+c663
+c633
+c633
+6632
+6006
+381c
+1e78
+07e0
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 800 0
+DWIDTH 20 0
+BBX 18 10 1 7
+BITMAP
+07e000
+783800
+600640
+c6c7c0
+ce63c0
+03cc00
+cc03c0
+63c7c0
+63c640
+063800
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 14 20 2 -2
+BITMAP
+1e78
+07e0
+fec0
+c018
+c0c0
+18e0
+c018
+e1c0
+18f0
+c018
+d2c0
+18dc
+c018
+ccc0
+18cc
+c018
+ccc0
+fff8
+e018
+7008
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 9 31 1 -8
+BITMAP
+3800
+1c00
+0e00
+0700
+0380
+0180
+0080
+0180
+0300
+0600
+0c00
+1800
+3000
+6000
+c000
+ff80
+ff80
+0180
+0300
+0600
+0c00
+1800
+1800
+3000
+3000
+6000
+6000
+6000
+6000
+c000
+c000
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 2 32 1 -8
+BITMAP
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 9 32 1 -8
+BITMAP
+c000
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 7 31 1 -8
+BITMAP
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+c0
+00
+60
+00
+60
+00
+60
+00
+60
+00
+30
+00
+30
+00
+18
+00
+18
+00
+0c
+00
+06
+00
+02
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 2 32 1 -8
+BITMAP
+00
+00
+80
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 7 29 1 -5
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 7 31 5 -8
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 5 32 2 -8
+BITMAP
+c0
+c0
+f8
+18
+30
+60
+60
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 7 29 5 -5
+BITMAP
+c0
+c0
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+10
+30
+60
+c0
+60
+30
+18
+18
+18
+18
+18
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 2 32 5 -8
+BITMAP
+00
+00
+00
+00
+00
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+40
+40
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 7 22 1 -5
+BITMAP
+30
+1e
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 8 28 0 -5
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+00
+c0
+c0
+60
+60
+30
+30
+18
+18
+0c
+0c
+06
+06
+0c
+0c
+18
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 8 31 8 -8
+BITMAP
+18
+30
+30
+60
+60
+c0
+c0
+07
+0f
+1b
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 2 32 2 -8
+BITMAP
+00
+c0
+c0
+c0
+00
+00
+00
+00
+40
+40
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 8 26 2 -2
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 9 31 0 -8
+BITMAP
+c080
+c080
+c080
+c080
+c000
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0200
+cc80
+7080
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+8000
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 2 32 7 -8
+BITMAP
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 9 32 0 -8
+BITMAP
+8000
+8080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c080
+c000
+8000
+8000
+8000
+8000
+8000
+8000
+8000
+8000
+8000
+8000
+8000
+8000
+8000
+8000
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 7 31 2 -8
+BITMAP
+80
+00
+80
+00
+80
+00
+80
+00
+80
+00
+80
+02
+00
+02
+00
+02
+00
+02
+00
+06
+00
+06
+00
+0c
+00
+0c
+00
+18
+00
+30
+00
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 2 32 7 -8
+BITMAP
+40
+00
+c0
+00
+c0
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 7 29 2 -5
+BITMAP
+06
+06
+06
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 7 31 0 -8
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+06
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 5 32 5 -8
+BITMAP
+00
+00
+00
+f8
+f0
+18
+08
+08
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 7 29 0 -5
+BITMAP
+06
+06
+06
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+40
+60
+30
+18
+30
+60
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol24.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol24.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol24.h	(revision 22322)
@@ -0,0 +1,1127 @@
+static unsigned char symbol24_0_bits[] = {
+0x00};
+static unsigned char symbol24_1_bits[] = {
+0x00};
+static unsigned char symbol24_2_bits[] = {
+0x00};
+static unsigned char symbol24_3_bits[] = {
+0x00};
+static unsigned char symbol24_4_bits[] = {
+0x00};
+static unsigned char symbol24_5_bits[] = {
+0x00};
+static unsigned char symbol24_6_bits[] = {
+0x00};
+static unsigned char symbol24_7_bits[] = {
+0x00};
+static unsigned char symbol24_8_bits[] = {
+0x00};
+static unsigned char symbol24_9_bits[] = {
+0x00};
+static unsigned char symbol24_10_bits[] = {
+0x00};
+static unsigned char symbol24_11_bits[] = {
+0x00};
+static unsigned char symbol24_12_bits[] = {
+0x00};
+static unsigned char symbol24_13_bits[] = {
+0x00};
+static unsigned char symbol24_14_bits[] = {
+0x00};
+static unsigned char symbol24_15_bits[] = {
+0x00};
+static unsigned char symbol24_16_bits[] = {
+0x00};
+static unsigned char symbol24_17_bits[] = {
+0x00};
+static unsigned char symbol24_18_bits[] = {
+0x00};
+static unsigned char symbol24_19_bits[] = {
+0x00};
+static unsigned char symbol24_20_bits[] = {
+0x00};
+static unsigned char symbol24_21_bits[] = {
+0x00};
+static unsigned char symbol24_22_bits[] = {
+0x00};
+static unsigned char symbol24_23_bits[] = {
+0x00};
+static unsigned char symbol24_24_bits[] = {
+0x00};
+static unsigned char symbol24_25_bits[] = {
+0x00};
+static unsigned char symbol24_26_bits[] = {
+0x00};
+static unsigned char symbol24_27_bits[] = {
+0x00};
+static unsigned char symbol24_28_bits[] = {
+0x00};
+static unsigned char symbol24_29_bits[] = {
+0x00};
+static unsigned char symbol24_30_bits[] = {
+0x00};
+static unsigned char symbol24_31_bits[] = {
+0x00};
+static unsigned char symbol24_32_bits[] = {
+0x00};
+static unsigned char symbol24_33_bits[] = {
+0x02, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x02, 0x02, 0x02, 0x02, 
+0x00, 0x00, 0x00, 0x03, 0x03};
+static unsigned char symbol24_34_bits[] = {
+0x03, 0x60, 0x03, 0x60, 0x06, 0x30, 0x06, 0x30, 0xfc, 0x1f, 0xfc, 0x1f, 
+0x0c, 0x18, 0x18, 0x0c, 0x18, 0x0c, 0x30, 0x06, 0x30, 0x06, 0x30, 0x06, 
+0x60, 0x03, 0x60, 0x03, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01};
+static unsigned char symbol24_35_bits[] = {
+0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 
+0xfe, 0x07, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xff, 0x03, 0x44, 0x00, 
+0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00};
+static unsigned char symbol24_36_bits[] = {
+0xff, 0x07, 0xff, 0x07, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 
+0x00, 0x06, 0xff, 0x07, 0xff, 0x07, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 
+0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0xff, 0x07, 0xff, 0x07};
+static unsigned char symbol24_37_bits[] = {
+0xf8, 0x60, 0x00, 0x9c, 0x7f, 0x00, 0x8e, 0x30, 0x00, 0x86, 0x18, 0x00, 
+0xc7, 0x18, 0x00, 0x43, 0x0c, 0x00, 0x63, 0x06, 0x00, 0x33, 0x06, 0x00, 
+0x1e, 0xe3, 0x01, 0x80, 0x71, 0x03, 0x80, 0x39, 0x02, 0xc0, 0x1c, 0x02, 
+0x60, 0x0c, 0x03, 0x60, 0x0c, 0x01, 0x30, 0x8c, 0x01, 0x18, 0xcc, 0x00, 
+0x18, 0x78, 0x00};
+static unsigned char symbol24_38_bits[] = {
+0xe0, 0x01, 0x30, 0x03, 0x18, 0x02, 0x18, 0x02, 0x18, 0x03, 0xb8, 0x01, 
+0xf0, 0x7c, 0x78, 0x38, 0x7c, 0x18, 0xe6, 0x0c, 0xc3, 0x05, 0x83, 0x07, 
+0x03, 0x03, 0x83, 0x07, 0xc7, 0x4e, 0x7e, 0x7c, 0x3c, 0x38};
+static unsigned char symbol24_39_bits[] = {
+0x0f, 0x3c, 0x70, 0xe0, 0xc0, 0xc0, 0xfe, 0xc0, 0xc0, 0xe0, 0x70, 0x3c, 
+0x0f};
+static unsigned char symbol24_40_bits[] = {
+0x30, 0x18, 0x0c, 0x04, 0x06, 0x06, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x02, 0x06, 0x06, 0x04, 0x0c, 0x18, 0x30};
+static unsigned char symbol24_41_bits[] = {
+0x03, 0x06, 0x0c, 0x08, 0x18, 0x18, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 
+0x30, 0x30, 0x30, 0x10, 0x18, 0x18, 0x08, 0x0c, 0x06, 0x03};
+static unsigned char symbol24_42_bits[] = {
+0x18, 0x18, 0xc3, 0xe7, 0x18, 0x18, 0xe7, 0xc3, 0x18, 0x18};
+static unsigned char symbol24_43_bits[] = {
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0xff, 0x03, 0xff, 0x03, 
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00};
+static unsigned char symbol24_44_bits[] = {
+0x06, 0x06, 0x04, 0x06, 0x03, 0x01};
+static unsigned char symbol24_45_bits[] = {
+0xff, 0x07, 0xff, 0x07};
+static unsigned char symbol24_46_bits[] = {
+0x03, 0x03, 0x03};
+static unsigned char symbol24_47_bits[] = {
+0x60, 0x60, 0x60, 0x30, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x06, 
+0x06, 0x06, 0x03, 0x03, 0x03};
+static unsigned char symbol24_48_bits[] = {
+0x78, 0x00, 0xcc, 0x00, 0x86, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x86, 0x01, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00};
+static unsigned char symbol24_49_bits[] = {
+0x0c, 0x0e, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x3f};
+static unsigned char symbol24_50_bits[] = {
+0x38, 0x00, 0xfe, 0x00, 0xe6, 0x00, 0xc1, 0x01, 0x81, 0x01, 0x80, 0x01, 
+0x80, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0xe0, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x18, 0x00, 0x0c, 0x00, 0x06, 0x02, 0xff, 0x03, 0xff, 0x01};
+static unsigned char symbol24_51_bits[] = {
+0x38, 0x00, 0x7e, 0x00, 0xe2, 0x00, 0xc1, 0x00, 0xc1, 0x00, 0x60, 0x00, 
+0x30, 0x00, 0x78, 0x00, 0xe0, 0x00, 0xc0, 0x00, 0x80, 0x01, 0x80, 0x01, 
+0x80, 0x01, 0xc0, 0x00, 0xe3, 0x00, 0x7f, 0x00, 0x1e, 0x00};
+static unsigned char symbol24_52_bits[] = {
+0xc0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd8, 0x00, 
+0xc8, 0x00, 0xcc, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc2, 0x00, 0xff, 0x03, 
+0xff, 0x03, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00};
+static unsigned char symbol24_53_bits[] = {
+0xfc, 0x01, 0xfc, 0x00, 0x06, 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f, 0x00, 
+0x3f, 0x00, 0x78, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x01, 0x80, 0x01, 
+0x80, 0x01, 0x80, 0x01, 0xc3, 0x00, 0x7f, 0x00, 0x3e, 0x00};
+static unsigned char symbol24_54_bits[] = {
+0xc0, 0x03, 0x70, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x07, 0x00, 
+0xf3, 0x00, 0xcf, 0x01, 0x87, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x07, 0x03, 0x86, 0x03, 0xce, 0x01, 0xf8, 0x00};
+static unsigned char symbol24_55_bits[] = {
+0xfe, 0x03, 0xff, 0x03, 0x03, 0x03, 0x81, 0x01, 0x80, 0x01, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00};
+static unsigned char symbol24_56_bits[] = {
+0x78, 0x00, 0xcc, 0x00, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, 0xce, 0x00, 
+0x7c, 0x00, 0x38, 0x00, 0xfc, 0x00, 0xc6, 0x01, 0x83, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 0xc6, 0x01, 0x7c, 0x00};
+static unsigned char symbol24_57_bits[] = {
+0x78, 0x00, 0xce, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x86, 0x03, 0xce, 0x03, 0x78, 0x03, 0x80, 0x01, 
+0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x3c, 0x00, 0x0f, 0x00};
+static unsigned char symbol24_58_bits[] = {
+0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03};
+static unsigned char symbol24_59_bits[] = {
+0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x04, 
+0x06, 0x03, 0x01};
+static unsigned char symbol24_60_bits[] = {
+0x00, 0x0e, 0x80, 0x07, 0xe0, 0x01, 0x78, 0x00, 0x1e, 0x00, 0x07, 0x00, 
+0x07, 0x00, 0x1e, 0x00, 0x78, 0x00, 0xe0, 0x01, 0x80, 0x07, 0x00, 0x0e};
+static unsigned char symbol24_61_bits[] = {
+0xff, 0x03, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xff, 0x03};
+static unsigned char symbol24_62_bits[] = {
+0x07, 0x00, 0x1e, 0x00, 0x78, 0x00, 0xe0, 0x01, 0x80, 0x07, 0x00, 0x0e, 
+0x00, 0x0e, 0x80, 0x07, 0xe0, 0x01, 0x78, 0x00, 0x1e, 0x00, 0x07, 0x00};
+static unsigned char symbol24_63_bits[] = {
+0x3e, 0x63, 0xc1, 0xc3, 0xe3, 0x60, 0x70, 0x30, 0x18, 0x18, 0x08, 0x08, 
+0x08, 0x00, 0x00, 0x0c, 0x0c};
+static unsigned char symbol24_64_bits[] = {
+0x1c, 0x04, 0x3e, 0x06, 0xe3, 0x03, 0xc1, 0x01, 0x00, 0x00, 0x00, 0x00, 
+0xff, 0x07, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0xff, 0x07};
+static unsigned char symbol24_65_bits[] = {
+0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x03, 0x00, 0x80, 0x02, 0x00, 
+0xc0, 0x06, 0x00, 0x40, 0x06, 0x00, 0x40, 0x0c, 0x00, 0x60, 0x0c, 0x00, 
+0x20, 0x0c, 0x00, 0x30, 0x18, 0x00, 0x10, 0x18, 0x00, 0xf0, 0x3f, 0x00, 
+0x18, 0x30, 0x00, 0x08, 0x30, 0x00, 0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 
+0x1f, 0xf0, 0x01};
+static unsigned char symbol24_66_bits[] = {
+0xff, 0x03, 0x0c, 0x07, 0x0c, 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x06, 0xfc, 0x03, 0x0c, 0x0e, 0x0c, 0x1c, 0x0c, 0x18, 
+0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x1c, 0x0c, 0x0e, 0xff, 0x03};
+static unsigned char symbol24_67_bits[] = {
+0x1f, 0xf8, 0x01, 0x0e, 0x70, 0x00, 0x1c, 0x30, 0x00, 0x38, 0x18, 0x00, 
+0x30, 0x0c, 0x00, 0x60, 0x06, 0x00, 0xe0, 0x03, 0x00, 0xc0, 0x01, 0x00, 
+0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, 0x60, 0x07, 0x00, 0x30, 0x0e, 0x00, 
+0x10, 0x1c, 0x00, 0x18, 0x18, 0x00, 0x0c, 0x38, 0x00, 0x0e, 0x70, 0x00, 
+0x1f, 0xf8, 0x01};
+static unsigned char symbol24_68_bits[] = {
+0xc0, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0x20, 0x01, 0x20, 0x03, 0x30, 0x03, 
+0x10, 0x06, 0x10, 0x06, 0x18, 0x0c, 0x08, 0x0c, 0x0c, 0x18, 0x04, 0x18, 
+0x04, 0x30, 0x06, 0x30, 0x02, 0x60, 0x03, 0x60, 0xff, 0xff};
+static unsigned char symbol24_69_bits[] = {
+0xff, 0x0f, 0x0c, 0x0c, 0x0c, 0x08, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x04, 0x0c, 0x04, 0xfc, 0x07, 0x0c, 0x04, 0x0c, 0x04, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x10, 0x0c, 0x10, 0x0c, 0x18, 0xff, 0x1f};
+static unsigned char symbol24_70_bits[] = {
+0xe0, 0x07, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f, 0x9c, 0x39, 0x86, 0x61, 
+0x87, 0xe1, 0x83, 0xc1, 0x83, 0xc1, 0x83, 0xc1, 0x87, 0xe1, 0x86, 0x61, 
+0x9c, 0x39, 0xf0, 0x0f, 0x80, 0x01, 0x80, 0x01, 0xe0, 0x07};
+static unsigned char symbol24_71_bits[] = {
+0xff, 0x1f, 0x0c, 0x18, 0x0c, 0x10, 0x0c, 0x10, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x3f, 0x00};
+static unsigned char symbol24_72_bits[] = {
+0x3f, 0xfc, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 
+0x0c, 0x30, 0x0c, 0x30, 0xfc, 0x3f, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 
+0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x3f, 0xfc};
+static unsigned char symbol24_73_bits[] = {
+0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x3f};
+static unsigned char symbol24_74_bits[] = {
+0xc0, 0x03, 0x60, 0x07, 0x20, 0x06, 0x60, 0x06, 0xc0, 0x0c, 0x8e, 0x0d, 
+0x1f, 0x0f, 0x19, 0x3c, 0x18, 0x0c, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x06, 0x1c, 0x06, 0x38, 0x03, 0xf0, 0x01};
+static unsigned char symbol24_75_bits[] = {
+0x3f, 0x3e, 0x0c, 0x0c, 0x0c, 0x06, 0x0c, 0x03, 0x8c, 0x01, 0xcc, 0x00, 
+0x6c, 0x00, 0x7c, 0x00, 0xfc, 0x00, 0xcc, 0x01, 0x8c, 0x01, 0x8c, 0x03, 
+0x0c, 0x07, 0x0c, 0x0e, 0x0c, 0x1c, 0x0c, 0x38, 0x3f, 0x7c};
+static unsigned char symbol24_76_bits[] = {
+0xc0, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0x20, 0x01, 0x20, 0x03, 0x20, 0x03, 
+0x30, 0x03, 0x10, 0x06, 0x10, 0x06, 0x18, 0x06, 0x08, 0x0c, 0x08, 0x0c, 
+0x0c, 0x0c, 0x04, 0x18, 0x04, 0x18, 0x06, 0x38, 0x1f, 0x7c};
+static unsigned char symbol24_77_bits[] = {
+0x0f, 0x80, 0x0f, 0x1c, 0x80, 0x03, 0x1c, 0xc0, 0x03, 0x3c, 0x40, 0x03, 
+0x34, 0x40, 0x03, 0x74, 0x60, 0x03, 0x64, 0x20, 0x03, 0xe4, 0x30, 0x03, 
+0xc4, 0x10, 0x03, 0xc4, 0x11, 0x03, 0x84, 0x19, 0x03, 0x84, 0x0b, 0x03, 
+0x04, 0x0b, 0x03, 0x04, 0x0f, 0x03, 0x04, 0x06, 0x03, 0x04, 0x06, 0x03, 
+0x1f, 0xc0, 0x0f};
+static unsigned char symbol24_78_bits[] = {
+0x0f, 0xf8, 0x1c, 0x20, 0x1c, 0x20, 0x34, 0x20, 0x74, 0x20, 0x64, 0x20, 
+0xc4, 0x20, 0xc4, 0x20, 0x84, 0x21, 0x84, 0x23, 0x04, 0x23, 0x04, 0x26, 
+0x04, 0x26, 0x04, 0x2c, 0x04, 0x3c, 0x04, 0x38, 0x1f, 0x30};
+static unsigned char symbol24_79_bits[] = {
+0xe0, 0x07, 0x38, 0x1c, 0x0c, 0x30, 0x06, 0x60, 0x06, 0x60, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x06, 0x60, 0x06, 0x60, 0x0c, 0x30, 0x38, 0x1c, 0xe0, 0x07};
+static unsigned char symbol24_80_bits[] = {
+0xff, 0xff, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 
+0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 
+0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x3f, 0xfc};
+static unsigned char symbol24_81_bits[] = {
+0xe0, 0x07, 0x38, 0x1c, 0x0c, 0x30, 0x06, 0x60, 0x06, 0x60, 0x13, 0xc8, 
+0x13, 0xc8, 0xf3, 0xcf, 0xf3, 0xcf, 0x13, 0xc8, 0x13, 0xc8, 0x03, 0xc0, 
+0x02, 0x40, 0x06, 0x60, 0x0c, 0x30, 0x38, 0x1c, 0xe0, 0x07};
+static unsigned char symbol24_82_bits[] = {
+0xff, 0x01, 0x0c, 0x07, 0x0c, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x06, 0x0c, 0x07, 0xfc, 0x01, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x3f, 0x00};
+static unsigned char symbol24_83_bits[] = {
+0xff, 0x0f, 0x07, 0x0c, 0x0e, 0x08, 0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 
+0xe0, 0x00, 0xc0, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 
+0x0c, 0x08, 0x06, 0x08, 0x03, 0x0c, 0xff, 0x0f, 0xff, 0x0f};
+static unsigned char symbol24_84_bits[] = {
+0xff, 0x3f, 0xc3, 0x30, 0xc1, 0x20, 0xc1, 0x20, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xf0, 0x03};
+static unsigned char symbol24_85_bits[] = {
+0x1f, 0xf8, 0x0e, 0x70, 0x0c, 0x30, 0x18, 0x18, 0x38, 0x08, 0x30, 0x0c, 
+0x70, 0x06, 0x60, 0x06, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, 0x80, 0x01, 
+0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xe0, 0x07};
+static unsigned char symbol24_86_bits[] = {
+0xf8, 0x01, 0xfc, 0x01, 0x06, 0x00, 0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x06, 0x00, 0xfe, 0x00, 
+0xf8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x80, 0x01, 0xf0, 0x00, 0x70, 0x00};
+static unsigned char symbol24_87_bits[] = {
+0xe0, 0x07, 0x38, 0x1c, 0x0c, 0x30, 0x06, 0x60, 0x06, 0x60, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x07, 0xe0, 0x06, 0x60, 0x0c, 0x30, 
+0x18, 0x18, 0x31, 0x8c, 0x21, 0x84, 0x3f, 0xfc, 0x3f, 0xfc};
+static unsigned char symbol24_88_bits[] = {
+0xfe, 0x1f, 0xfe, 0x1f, 0x06, 0x18, 0x02, 0x10, 0x00, 0x00, 0x08, 0x04, 
+0x08, 0x04, 0xf8, 0x07, 0xf8, 0x07, 0x08, 0x04, 0x08, 0x04, 0x00, 0x00, 
+0x01, 0x20, 0x01, 0x20, 0x03, 0x30, 0xff, 0x3f, 0xff, 0x3f};
+static unsigned char symbol24_89_bits[] = {
+0xc7, 0x8f, 0x03, 0x0e, 0xc3, 0x01, 0x0c, 0xc3, 0x00, 0x0c, 0xc3, 0x00, 
+0x1c, 0xe3, 0x00, 0x1c, 0xe3, 0x00, 0x18, 0x63, 0x00, 0x38, 0x73, 0x00, 
+0xe0, 0x1f, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 
+0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 
+0xc0, 0x0f, 0x00};
+static unsigned char symbol24_90_bits[] = {
+0xff, 0x0f, 0x03, 0x0e, 0x01, 0x06, 0x00, 0x07, 0x80, 0x03, 0x80, 0x01, 
+0xc0, 0x01, 0xe0, 0x00, 0x60, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, 
+0x1c, 0x00, 0x0e, 0x00, 0x06, 0x08, 0x07, 0x0c, 0xff, 0x0f};
+static unsigned char symbol24_91_bits[] = {
+0x1f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x1f};
+static unsigned char symbol24_92_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c};
+static unsigned char symbol24_93_bits[] = {
+0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f};
+static unsigned char symbol24_94_bits[] = {
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xff, 0x3f, 0xff, 0x3f};
+static unsigned char symbol24_95_bits[] = {
+0xff, 0x1f, 0xff, 0x1f};
+static unsigned char symbol24_96_bits[] = {
+0xff, 0x1f, 0xff, 0x1f};
+static unsigned char symbol24_97_bits[] = {
+0xf8, 0x18, 0x8e, 0x1d, 0x06, 0x0d, 0x07, 0x0f, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x07, 0x07, 0x06, 0x27, 0x8e, 0x3f, 
+0xf8, 0x1c};
+static unsigned char symbol24_98_bits[] = {
+0x7c, 0x00, 0xe6, 0x00, 0xc3, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc3, 0x01, 
+0xc3, 0x00, 0x73, 0x00, 0xf3, 0x00, 0xc3, 0x01, 0x83, 0x01, 0x83, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 0x87, 0x01, 0xcf, 0x01, 
+0xfb, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00};
+static unsigned char symbol24_99_bits[] = {
+0x0e, 0x06, 0x1f, 0x06, 0x1b, 0x03, 0x11, 0x03, 0x90, 0x01, 0x90, 0x01, 
+0xd0, 0x00, 0xe0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x70, 0x00, 0x70, 0x00, 
+0x98, 0x00, 0x98, 0x00, 0x8c, 0x08, 0x8c, 0x0d, 0x86, 0x0f, 0x06, 0x07};
+static unsigned char symbol24_100_bits[] = {
+0xf8, 0x00, 0xcc, 0x01, 0x84, 0x01, 0x0c, 0x00, 0x1c, 0x00, 0x38, 0x00, 
+0x70, 0x00, 0xfc, 0x00, 0xce, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x86, 0x01, 0xce, 0x01, 0x78, 0x00};
+static unsigned char symbol24_101_bits[] = {
+0x7c, 0x00, 0xce, 0x00, 0xc7, 0x00, 0xc3, 0x00, 0x07, 0x00, 0x06, 0x00, 
+0x3c, 0x00, 0x06, 0x00, 0x07, 0x00, 0x03, 0x00, 0x07, 0x01, 0x8e, 0x01, 
+0xfc, 0x00};
+static unsigned char symbol24_102_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xf8, 0x01, 0x6c, 0x03, 
+0x66, 0x06, 0x67, 0x0e, 0x63, 0x0c, 0x63, 0x0c, 0x63, 0x0c, 0x63, 0x0c, 
+0x63, 0x0c, 0x67, 0x0e, 0x66, 0x06, 0x6c, 0x03, 0xf8, 0x01, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char symbol24_103_bits[] = {
+0x0e, 0x06, 0x1f, 0x06, 0x19, 0x06, 0x19, 0x03, 0x18, 0x03, 0x30, 0x03, 
+0xb0, 0x01, 0xb0, 0x01, 0xa0, 0x01, 0xe0, 0x00, 0xe0, 0x00, 0xc0, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char symbol24_104_bits[] = {
+0x8e, 0x03, 0xdf, 0x07, 0x79, 0x0c, 0x38, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 
+0x18, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 
+0x18, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c};
+static unsigned char symbol24_105_bits[] = {
+0x0c, 0x0e, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x4c, 0x7c, 
+0x38};
+static unsigned char symbol24_106_bits[] = {
+0xcc, 0x01, 0xce, 0x03, 0x66, 0x06, 0x63, 0x0e, 0x63, 0x0c, 0x63, 0x0c, 
+0x63, 0x0c, 0x63, 0x0c, 0x63, 0x0c, 0x67, 0x0e, 0x66, 0x06, 0x6c, 0x03, 
+0xf8, 0x01, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char symbol24_107_bits[] = {
+0x0c, 0x0e, 0x0e, 0x0f, 0x8f, 0x0d, 0xcc, 0x00, 0x6c, 0x00, 0x3c, 0x00, 
+0x3c, 0x00, 0x7c, 0x00, 0xec, 0x00, 0xcc, 0x01, 0x8c, 0x03, 0x0c, 0x07, 
+0x8c, 0x0f};
+static unsigned char symbol24_108_bits[] = {
+0x0e, 0x00, 0x1f, 0x00, 0x11, 0x00, 0x10, 0x00, 0x10, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x78, 0x00, 0x58, 0x00, 0x4c, 0x00, 
+0x4c, 0x00, 0xcc, 0x00, 0x86, 0x00, 0x86, 0x00, 0x86, 0x09, 0x83, 0x0f, 
+0x03, 0x07};
+static unsigned char symbol24_109_bits[] = {
+0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 
+0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xc7, 0x09, 0xff, 0x0f, 
+0x7b, 0x06, 0x03, 0x00, 0x03, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00};
+static unsigned char symbol24_110_bits[] = {
+0x03, 0x07, 0x03, 0x07, 0x03, 0x06, 0x06, 0x02, 0x06, 0x03, 0x06, 0x01, 
+0x8c, 0x01, 0x8c, 0x00, 0xcc, 0x00, 0x58, 0x00, 0x58, 0x00, 0x70, 0x00, 
+0x30, 0x00};
+static unsigned char symbol24_111_bits[] = {
+0xf8, 0x00, 0x8e, 0x03, 0x06, 0x03, 0x07, 0x07, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x07, 0x07, 0x06, 0x03, 0x8e, 0x03, 
+0xf8, 0x00};
+static unsigned char symbol24_112_bits[] = {
+0xfe, 0x1f, 0xff, 0x1f, 0x19, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 
+0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x13, 0x1c, 0x1f, 
+0x0c, 0x0f};
+static unsigned char symbol24_113_bits[] = {
+0x78, 0x00, 0xcc, 0x00, 0x86, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x86, 0x01, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00};
+static unsigned char symbol24_114_bits[] = {
+0x7c, 0x00, 0xc6, 0x01, 0x83, 0x01, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x03, 0x83, 0x01, 0xc7, 0x01, 
+0x7b, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00};
+static unsigned char symbol24_115_bits[] = {
+0xf8, 0x3f, 0x8e, 0x3f, 0x06, 0x03, 0x07, 0x07, 0x03, 0x06, 0x03, 0x06, 
+0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x07, 0x07, 0x06, 0x03, 0x8e, 0x03, 
+0xf8, 0x00};
+static unsigned char symbol24_116_bits[] = {
+0xfc, 0x03, 0xfe, 0x03, 0x33, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x01, 0xf0, 0x01, 
+0xe0, 0x00};
+static unsigned char symbol24_117_bits[] = {
+0x0c, 0x07, 0x1e, 0x0c, 0x1b, 0x18, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x30, 0x0c, 0x70, 0x07, 
+0xe0, 0x03};
+static unsigned char symbol24_118_bits[] = {
+0xfe, 0xff, 0xff, 0xff, 0x19, 0x18, 0x0c, 0x30, 0x8c, 0x31, 0x86, 0x61, 
+0x86, 0x61, 0x86, 0x61, 0x86, 0x61, 0x86, 0x61, 0x86, 0x61, 0x8c, 0x31, 
+0xdc, 0x3b, 0x78, 0x1e};
+static unsigned char symbol24_119_bits[] = {
+0x18, 0x06, 0x0c, 0x0c, 0x06, 0x18, 0xc6, 0x18, 0xc3, 0x30, 0xc3, 0x30, 
+0xc3, 0x30, 0xc3, 0x30, 0xc3, 0x30, 0xc3, 0x30, 0xc6, 0x18, 0xee, 0x1d, 
+0x3c, 0x0f};
+static unsigned char symbol24_120_bits[] = {
+0x0c, 0x00, 0xc6, 0x01, 0xe6, 0x01, 0xfc, 0x00, 0x0c, 0x00, 0x04, 0x00, 
+0xec, 0x01, 0xf8, 0x01, 0x0c, 0x00, 0x06, 0x00, 0x03, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x3e, 0x00, 0xfc, 0x01, 
+0xf0, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0xf0, 0x01, 0xf0, 0x00};
+static unsigned char symbol24_121_bits[] = {
+0x83, 0xc1, 0x8e, 0x71, 0x8c, 0x31, 0x9c, 0x39, 0x9c, 0x39, 0x9c, 0x39, 
+0x9c, 0x39, 0x9c, 0x39, 0x9c, 0x39, 0x98, 0x19, 0x98, 0x19, 0xb0, 0x0d, 
+0xe0, 0x07, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01};
+static unsigned char symbol24_122_bits[] = {
+0x0c, 0x00, 0xc6, 0x01, 0xe6, 0x01, 0xfc, 0x00, 0x18, 0x00, 0x0c, 0x00, 
+0x04, 0x00, 0x06, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x3e, 0x00, 0xfc, 0x01, 
+0xf0, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0xf0, 0x01, 0xf0, 0x00};
+static unsigned char symbol24_123_bits[] = {
+0xe0, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x08, 0x0c, 0x04, 0x03, 0x04, 
+0x0c, 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0xe0};
+static unsigned char symbol24_124_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol24_125_bits[] = {
+0x07, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x10, 0x30, 0x20, 0xc0, 0x20, 
+0x30, 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x07};
+static unsigned char symbol24_126_bits[] = {
+0x1c, 0x04, 0x3e, 0x06, 0xe3, 0x03, 0xc1, 0x01};
+static unsigned char symbol24_127_bits[] = {
+0x00};
+static unsigned char symbol24_128_bits[] = {
+0x00};
+static unsigned char symbol24_129_bits[] = {
+0x00};
+static unsigned char symbol24_130_bits[] = {
+0x00};
+static unsigned char symbol24_131_bits[] = {
+0x00};
+static unsigned char symbol24_132_bits[] = {
+0x00};
+static unsigned char symbol24_133_bits[] = {
+0x00};
+static unsigned char symbol24_134_bits[] = {
+0x00};
+static unsigned char symbol24_135_bits[] = {
+0x00};
+static unsigned char symbol24_136_bits[] = {
+0x00};
+static unsigned char symbol24_137_bits[] = {
+0x00};
+static unsigned char symbol24_138_bits[] = {
+0x00};
+static unsigned char symbol24_139_bits[] = {
+0x00};
+static unsigned char symbol24_140_bits[] = {
+0x00};
+static unsigned char symbol24_141_bits[] = {
+0x00};
+static unsigned char symbol24_142_bits[] = {
+0x00};
+static unsigned char symbol24_143_bits[] = {
+0x00};
+static unsigned char symbol24_144_bits[] = {
+0x00};
+static unsigned char symbol24_145_bits[] = {
+0x00};
+static unsigned char symbol24_146_bits[] = {
+0x00};
+static unsigned char symbol24_147_bits[] = {
+0x00};
+static unsigned char symbol24_148_bits[] = {
+0x00};
+static unsigned char symbol24_149_bits[] = {
+0x00};
+static unsigned char symbol24_150_bits[] = {
+0x00};
+static unsigned char symbol24_151_bits[] = {
+0x00};
+static unsigned char symbol24_152_bits[] = {
+0x00};
+static unsigned char symbol24_153_bits[] = {
+0x00};
+static unsigned char symbol24_154_bits[] = {
+0x00};
+static unsigned char symbol24_155_bits[] = {
+0x00};
+static unsigned char symbol24_156_bits[] = {
+0x00};
+static unsigned char symbol24_157_bits[] = {
+0x00};
+static unsigned char symbol24_158_bits[] = {
+0x00};
+static unsigned char symbol24_159_bits[] = {
+0x00};
+static unsigned char symbol24_160_bits[] = {
+0x00};
+static unsigned char symbol24_161_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol24_162_bits[] = {
+0x07, 0x0e, 0x0e, 0x1b, 0x1c, 0x1d};
+static unsigned char symbol24_163_bits[] = {
+0xd8, 0x0c, 0x70, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0xf8, 0x01, 0x18, 0x0c};
+static unsigned char symbol24_164_bits[] = {
+0x0e, 0x06, 0x03, 0x01, 0x00, 0x0e, 0x80, 0x07, 0xe0, 0x01, 0x78, 0x00, 
+0x1e, 0x00, 0x07, 0x00, 0x07};
+static unsigned char symbol24_165_bits[] = {
+0x00, 0x1e, 0x00, 0x78, 0x00, 0x40, 0x03, 0x00, 0x0f, 0x00, 0x0c, 0x00, 
+0x00, 0x7f};
+static unsigned char symbol24_166_bits[] = {
+0x0f, 0x1f, 0x0f, 0x00, 0xc0, 0x00, 0x60, 0x10, 0x30, 0x18, 0x18, 0x18, 
+0x0c, 0x0c, 0x06, 0x06, 0x06, 0x03, 0x03, 0x03, 0x1c, 0x1c, 0x7e, 0x1e, 
+0xe3, 0x03, 0xc3, 0x01, 0x63, 0x03, 0x3e, 0x1e, 0x1c, 0x1c, 0x00, 0x0f, 
+0x80, 0x19, 0x80, 0x19, 0xc0, 0x00, 0xc0, 0x00};
+static unsigned char symbol24_167_bits[] = {
+0xc0, 0x00, 0xf8, 0x07, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x30, 0x00, 
+0x30, 0x00};
+static unsigned char symbol24_168_bits[] = {
+0x33, 0x00, 0x33, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0xe0, 0x01, 0xf0, 0x03, 
+0xf0, 0x03, 0xf0, 0x03, 0xf0, 0x03, 0xee, 0x01, 0xdf, 0x02, 0xff, 0x03, 
+0xff, 0x03};
+static unsigned char symbol24_169_bits[] = {
+0xdf, 0x0e, 0xce, 0x0c, 0xc0, 0x00, 0xe0, 0x01, 0x30, 0x00, 0x30, 0x00, 
+0x78, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfe, 0x01, 0xff, 0x03, 0xfe, 0x01, 
+0xfc, 0x00};
+static unsigned char symbol24_170_bits[] = {
+0xfc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x30, 0x00, 0x0e, 0x07, 0x9f, 0x0f, 
+0x9f, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0xfe, 0x07, 0xfe, 0x07, 
+0xfc, 0x03};
+static unsigned char symbol24_171_bits[] = {
+0xf8, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0xf0, 0x00, 0xf8, 0x01, 0xfc, 0x03, 0xfe, 0x07, 0xfe, 0x07, 0xff, 0x0f, 
+0xff, 0x0f, 0x6f, 0x0f, 0x66, 0x06, 0x60, 0x00, 0xf0, 0x00, 0x60, 0x00};
+static unsigned char symbol24_172_bits[] = {
+0x06, 0x30, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x30, 0x06, 0x00, 
+0x60, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x60, 0x0c, 0x00, 
+0x30, 0x18, 0x00, 0x00, 0x30, 0x00, 0x0c, 0x00, 0x00, 0x06, 0x60, 0x00, 
+0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00};
+static unsigned char symbol24_173_bits[] = {
+0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xff, 0x0f, 0xff, 0x01, 0xff, 0x0f, 
+0xff, 0x01, 0x06, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x18, 0x00, 
+0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 
+0xf0, 0x00, 0xf8, 0x01, 0x6c, 0x03, 0x66, 0x06, 0x63, 0x0c, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x60, 0x00};
+static unsigned char symbol24_174_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00, 
+0x0c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00};
+static unsigned char symbol24_175_bits[] = {
+0x60, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xff, 0x0f, 0xff, 0x01, 0xff, 0x0f, 
+0xff, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 
+0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char symbol24_176_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60};
+static unsigned char symbol24_177_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x03, 0x0c, 0x02, 0x06, 0x00, 0x03, 0x00, 0x01, 0x00, 
+0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol24_178_bits[] = {
+0x36, 0x01, 0x41, 0x01, 0x36, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00};
+static unsigned char symbol24_179_bits[] = {
+0x30, 0x00, 0xff, 0x03, 0xff, 0x03, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xff, 0x03, 0x98, 0x01, 
+0xdc, 0x01, 0xee, 0x00, 0x66, 0x00};
+static unsigned char symbol24_180_bits[] = {
+0x33, 0x00, 0x11, 0x00, 0x07, 0x00, 0x1e, 0x00, 0x78, 0x00, 0xe0, 0x01, 
+0x80, 0x07, 0x00, 0x06, 0x00, 0x06, 0x80, 0x07};
+static unsigned char symbol24_181_bits[] = {
+0xe0, 0x01, 0x7c, 0x00, 0x0f, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0x0f, 
+0xff, 0x0f};
+static unsigned char symbol24_182_bits[] = {
+0x03, 0x02, 0x06, 0x03, 0x8c, 0x01, 0xd8, 0x00, 0x70, 0x00, 0x70, 0x00, 
+0xd8, 0x00, 0x8c, 0x01, 0x06, 0x03, 0x03, 0x02, 0x1c, 0x00, 0x3e, 0x02, 
+0x63, 0x03, 0xc3, 0x01, 0xe3, 0x03, 0x3e, 0x02, 0x1c, 0x00, 0x7c, 0x00, 
+0xc6, 0x00};
+static unsigned char symbol24_183_bits[] = {
+0x83, 0x01, 0x80, 0x01, 0x00, 0x03, 0x00, 0x03};
+static unsigned char symbol24_184_bits[] = {
+0x00, 0x03, 0x00, 0x03, 0x78, 0x03, 0xce, 0x03, 0x86, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x83, 0x01};
+static unsigned char symbol24_185_bits[] = {
+0x83, 0x01, 0x83, 0x01, 0xc3, 0x00, 0xe6, 0x00, 0x3c, 0x00, 0x3c, 0x02, 
+0xff, 0x03, 0xff, 0x03, 0x7e, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 
+0xff, 0x03};
+static unsigned char symbol24_186_bits[] = {
+0xff, 0x03, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x80, 0x01, 0x80, 0x01, 
+0xc0, 0x00, 0xc0, 0x00};
+static unsigned char symbol24_187_bits[] = {
+0xff, 0x03, 0xff, 0x03, 0x30, 0x00, 0xff, 0x03, 0xff, 0x03, 0x0c, 0x00, 
+0x0c, 0x00, 0x06, 0x00};
+static unsigned char symbol24_188_bits[] = {
+0x06, 0x00, 0x03, 0x07, 0xff, 0x03, 0x00, 0x00, 0x03};
+static unsigned char symbol24_189_bits[] = {
+0x03, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x02, 
+0x02, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x03, 0x03, 0x01, 
+0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol24_190_bits[] = {
+0x03, 0x03, 0x03, 0x01, 0x03, 0x03, 0x03, 0x01};
+static unsigned char symbol24_191_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0xff, 0x3f, 0xff, 0x01, 0xff, 0x3f};
+static unsigned char symbol24_192_bits[] = {
+0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 
+0x06, 0x00, 0xff, 0x0f, 0xff, 0x0f, 0x06, 0x00};
+static unsigned char symbol24_193_bits[] = {
+0x04, 0x00, 0x81, 0x01, 0xc3, 0x00, 0xc7, 0x0f, 0x8e, 0x0f, 0x1c, 0x06, 
+0x38, 0x03, 0x3c, 0x01, 0x76, 0x01, 0xe3, 0x01, 0xc3, 0x01, 0xc7, 0x01, 
+0x8f, 0x03, 0x0e, 0x07, 0x0c, 0x0e, 0x0e, 0x0c, 0x07, 0x08, 0xf0, 0x01, 
+0xfc, 0x3f};
+static unsigned char symbol24_194_bits[] = {
+0x0e, 0x1f, 0x00, 0x0c, 0x02, 0x00, 0x02, 0x03, 0x00, 0x03, 0x0c, 0x01, 
+0x38, 0x0e, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x38, 0x00, 0x00, 
+0x0c, 0x30, 0x00, 0x30, 0x7b, 0x00, 0xf0, 0x0f, 0x00, 0x03, 0x78, 0x00, 
+0x00, 0xfc, 0x00, 0x00, 0xc6, 0x01, 0x00, 0x83, 0x01, 0x00, 0x81, 0x01, 
+0x00, 0x83, 0x01, 0x00, 0x87, 0x01};
+static unsigned char symbol24_195_bits[] = {
+0x00, 0x0e, 0x1d, 0x00, 0x9c, 0x0d, 0x00, 0x18, 0x19, 0x00, 0x88, 0x19, 
+0x00, 0x00, 0x19, 0x00, 0x80, 0x39, 0x00, 0x00, 0x31, 0x00, 0x80, 0x31, 
+0x00, 0x18, 0x61, 0x01, 0xfc, 0x60, 0x01, 0x72, 0xe0, 0x00, 0x60, 0x00, 
+0x30, 0x00, 0x18, 0x3c};
+static unsigned char symbol24_196_bits[] = {
+0x0c, 0x7f, 0x8c, 0x61, 0xc6, 0x60, 0x66, 0x60, 0x36, 0x60, 0x16, 0x30, 
+0x9e, 0x39, 0xce, 0x19, 0x4e, 0x0c, 0x5e, 0x0e, 0xda, 0x07, 0xbb, 0x01, 
+0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00};
+static unsigned char symbol24_197_bits[] = {
+0xe0, 0x07, 0x78, 0x1e, 0x1c, 0x38, 0x06, 0x60, 0x16, 0x68, 0x33, 0xcc, 
+0x63, 0xc6, 0xc3, 0xc3, 0x83, 0xc1, 0xc3, 0xc3, 0x63, 0xc6, 0x33, 0xcc, 
+0x16, 0x68, 0x06, 0x60, 0x1c, 0x38, 0x78, 0x1e, 0xe0, 0x07};
+static unsigned char symbol24_198_bits[] = {
+0xe0, 0x07, 0x00, 0x1e, 0x1c, 0x00, 0x86, 0x61, 0x02, 0x61, 0x83, 0x01, 
+0x83, 0xc1, 0x03, 0xdf, 0xfb, 0x03, 0x83, 0xc1, 0x03, 0xc1, 0x83, 0x01, 
+0x86, 0x61, 0x02, 0x60, 0x1c, 0x00, 0x78, 0x1e, 0x00, 0x07, 0xc0, 0x03, 
+0x03, 0xf0, 0x03, 0x01, 0x38, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 
+0x00, 0x06, 0x00};
+static unsigned char symbol24_199_bits[] = {
+0x01, 0x06, 0x8c, 0x01, 0x06, 0x86, 0x01, 0x06, 0x83, 0x01, 0x86, 0x81, 
+0x01, 0xc6, 0x80, 0x01, 0x66, 0x80, 0x01, 0x3c, 0xc0, 0x00, 0x1c, 0xc0, 
+0x00, 0x3c};
+static unsigned char symbol24_200_bits[] = {
+0x70, 0x00, 0xf6, 0x3f, 0x00, 0xc3, 0x0f, 0x00, 0xe0, 0x07, 0xf8, 0x1f, 
+0x1c, 0x38, 0x06, 0x60, 0x06, 0x60, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x03, 0xc0};
+static unsigned char symbol24_201_bits[] = {
+0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 
+0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40};
+static unsigned char symbol24_202_bits[] = {
+0x06, 0x60, 0x06, 0x60, 0x1c, 0x38, 0xf8, 0x1f, 0xe0, 0x07, 0xff, 0x07, 
+0xff, 0x1f, 0x00, 0x38, 0x00, 0x30, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 
+0x00, 0x60, 0x00, 0x30, 0x00, 0x38};
+static unsigned char symbol24_203_bits[] = {
+0xff, 0x1f, 0xff, 0x07, 0xff, 0x07, 0xff, 0x1f, 0x00, 0x38, 0x00, 0x30, 
+0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x30, 0x00, 0x38, 
+0xff, 0x1f, 0xff, 0x07, 0x00, 0x00, 0xff, 0x7f};
+static unsigned char symbol24_204_bits[] = {
+0xff, 0x7f, 0x00, 0x30, 0x00, 0x30, 0xf0, 0x7f, 0xfc, 0x7f, 0x0e, 0x0c, 
+0x06, 0x0c, 0x03, 0x06, 0x03, 0x06, 0x03, 0x03, 0x03, 0x03, 0x86, 0x01};
+static unsigned char symbol24_205_bits[] = {
+0x8e, 0x01, 0xfc, 0x7f, 0xf0, 0x7f, 0x60, 0x00, 0x60, 0x00, 0xf0, 0x7f, 
+0xfc, 0x7f, 0x0e, 0x00, 0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x06, 0x00, 0x0e, 0x00};
+static unsigned char symbol24_206_bits[] = {
+0xfc, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xfc, 0x0f, 0x0e, 0x00, 0x06, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x06, 0x00, 0x0e, 0x00, 
+0xfc, 0x0f};
+static unsigned char symbol24_207_bits[] = {
+0xf0, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0xff, 0x0f, 0xf8, 0x0f, 0xfe, 0x0f, 
+0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x0f, 0xff, 0x0f, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x06, 0x00, 0xfe, 0x0f, 0xf8, 0x0f};
+static unsigned char symbol24_208_bits[] = {
+0x00, 0x03, 0x00, 0x03, 0xf8, 0x01, 0xfe, 0x0f, 0x00, 0x01, 0x83, 0x01, 
+0xc3, 0x00, 0x01, 0x0f, 0xff, 0x01, 0x63, 0x00, 0x01, 0x00, 0x33, 0x00, 
+0x36, 0x00, 0x00, 0x0f, 0xf8, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol24_209_bits[] = {
+0x00, 0x00, 0x0c, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x80, 0x01, 
+0x00, 0xc0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 
+0x00, 0x0c, 0x00, 0x00, 0xfe, 0xff, 0x01, 0xff, 0xff, 0x01, 0xff, 0xff};
+static unsigned char symbol24_210_bits[] = {
+0x03, 0xc0, 0x03, 0x40, 0x06, 0x60, 0x06, 0x20, 0x0c, 0x30, 0x0c, 0x10, 
+0x0c, 0x18, 0x18, 0x08, 0x18, 0x0c, 0x30, 0x04, 0x30, 0x04, 0x60, 0x06, 
+0x60, 0x02, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, 0x80, 0x01};
+static unsigned char symbol24_211_bits[] = {
+0xe0, 0x07, 0x78, 0x1e, 0x1c, 0x38, 0x06, 0x60, 0xf6, 0x67, 0x63, 0xce, 
+0x63, 0xcc, 0x63, 0xce, 0xe3, 0xc7, 0x63, 0xc3, 0x63, 0xc6, 0x63, 0xcc, 
+0xf6, 0x7c, 0x06, 0x60, 0x1c, 0x38, 0x78, 0x1e, 0xe0, 0x07};
+static unsigned char symbol24_212_bits[] = {
+0xe0, 0x07, 0x08, 0x1e, 0x1c, 0x08, 0x06, 0x60, 0x06, 0x63, 0x63, 0x06, 
+0x33, 0xc4, 0x03, 0xc0, 0x33, 0x00, 0x33, 0xc0, 0x03, 0xc4, 0x63, 0x06, 
+0xc6, 0x63, 0x06, 0x60, 0x1c, 0x08};
+static unsigned char symbol24_213_bits[] = {
+0x78, 0x1e, 0x00, 0x07, 0xff, 0x01, 0x0e, 0x99, 0x00, 0x06, 0x18, 0x00, 
+0x07, 0x18, 0x00, 0x07, 0x18, 0x00, 0x06, 0x18, 0x00, 0x06, 0x18, 0x00, 
+0x06, 0x18, 0x00, 0x06, 0x18, 0x00, 0x06, 0x3c, 0x01, 0x0f, 0xff, 0x01, 
+0x01, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 
+0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 
+0x00, 0x0c, 0x00};
+static unsigned char symbol24_214_bits[] = {
+0x00, 0x0c, 0x60, 0x00, 0x0c, 0x20, 0x00, 0x0c, 0x60, 0x00, 0x0c, 0x20, 
+0x00, 0x0c, 0x60, 0x00, 0x0c, 0x20, 0x00, 0x0c, 0x60, 0x00, 0x0c, 0x20, 
+0x00, 0x0c, 0x60, 0x00, 0x0c, 0x20, 0x00, 0x3f, 0xf8, 0x01, 0x00, 0x20, 
+0x00, 0x20, 0x00, 0x20, 0x00, 0x30, 0x00, 0x30, 0x00, 0x10};
+static unsigned char symbol24_215_bits[] = {
+0x00, 0x00, 0x00};
+static unsigned char symbol24_216_bits[] = {
+0x10, 0x00, 0x18, 0x0c, 0x18, 0x0e, 0x08, 0x1b, 0x08, 0x18, 0x08, 0x30, 
+0x0c, 0x30, 0x0c, 0x60};
+static unsigned char symbol24_217_bits[] = {
+0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0xff, 0x1f, 0xff, 0x1f, 0x00, 0x00};
+static unsigned char symbol24_218_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 
+0xe0, 0x00, 0xb0, 0x01, 0xb0, 0x01, 0x18, 0x03, 0x18, 0x03, 0x0c, 0x06};
+static unsigned char symbol24_219_bits[] = {
+0x0c, 0x06, 0x06, 0x0c, 0x06, 0x0c, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 
+0x03, 0x18, 0x06, 0x0c, 0x06, 0x0c, 0x0c, 0x06, 0x0c, 0x06, 0x18, 0x03, 
+0x18, 0x03, 0x30, 0x01, 0xb0, 0x01, 0xe0, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char symbol24_220_bits[] = {
+0x03, 0x30, 0x00, 0x06, 0x18, 0x00, 0x0c, 0xfc, 0x7f, 0x1f, 0xfe, 0x7f, 
+0x3f, 0x07, 0x00, 0x70, 0x07, 0x00, 0x70, 0xfe, 0x7f, 0x3f, 0xfc, 0x7f, 
+0x1f, 0x18, 0x00, 0x0c, 0x30, 0x00, 0x06, 0x60, 0x00, 0x03, 0x60, 0x00};
+static unsigned char symbol24_221_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0c, 0xff, 0x0f, 0xfe, 0x0f, 
+0x7f, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0e, 0xff, 0x0f, 0xfc, 0x0f, 
+0x7f, 0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 
+0xf0, 0x00, 0xf8, 0x01, 0x9c, 0x03, 0x9e, 0x07, 0x9b, 0x0d};
+static unsigned char symbol24_222_bits[] = {
+0x98, 0x01, 0x18, 0x01, 0x98, 0x01, 0x98, 0x01, 0x18, 0x01, 0x98, 0x01, 
+0x98, 0x01, 0x18, 0x01, 0x98, 0x01, 0x98, 0x01, 0x18, 0x01, 0x98, 0x01, 
+0x98, 0x01, 0x18, 0x01, 0x98, 0x01, 0x98, 0x01, 0x18, 0x01, 0x00, 0x00};
+static unsigned char symbol24_223_bits[] = {
+0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0c, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 
+0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 
+0x1f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x98, 0x01, 
+0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01};
+static unsigned char symbol24_224_bits[] = {
+0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 
+0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x01, 0x9b, 0x01, 
+0x9e, 0x03, 0x9c, 0x03, 0xf8, 0x01, 0xf0, 0x00, 0x60, 0x00, 0x30, 0x00};
+static unsigned char symbol24_225_bits[] = {
+0x30, 0x00, 0x78, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x06, 0x01, 0x06, 0x01, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x01};
+static unsigned char symbol24_226_bits[] = {
+0x86, 0x01, 0xcc, 0x00, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03, 
+0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60};
+static unsigned char symbol24_227_bits[] = {
+0xe0, 0x07, 0x78, 0x1e, 0x1c, 0x38, 0x06, 0x60, 0xe6, 0x67, 0x63, 0xcc, 
+0x63, 0xcc, 0x63, 0xcc, 0xe3, 0xc7, 0x63, 0xc6, 0x63, 0xcc, 0x63, 0xcc, 
+0x66, 0x4c, 0x06, 0x60, 0x1c, 0x38, 0x78, 0x1e, 0xe0, 0x07};
+static unsigned char symbol24_228_bits[] = {
+0xe0, 0x07, 0x00, 0x1e, 0x1c, 0x00, 0x06, 0x60, 0x02, 0x63, 0xe3, 0x03, 
+0x73, 0xc6, 0x03, 0xc0, 0x33, 0x00, 0x33, 0xc0, 0x03, 0xc6, 0xe3, 0x03, 
+0xc6, 0x63, 0x02, 0x60, 0x1c, 0x00};
+static unsigned char symbol24_229_bits[] = {
+0x78, 0x1e, 0xe0, 0x07, 0x7f, 0x03, 0x03, 0x18, 0x03, 0x03, 0x18, 0x07, 
+0x03, 0x18, 0x87, 0x03, 0x18, 0x0f, 0x03, 0x18, 0x4b, 0x03, 0x18, 0x3b, 
+0x03, 0x18, 0x33, 0x03, 0x18, 0x33, 0x03, 0x18, 0x33, 0x03, 0xff, 0x1f, 
+0x07, 0x18, 0x0e, 0x10};
+static unsigned char symbol24_230_bits[] = {
+0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x01, 
+0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 
+0x0c, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x01, 0xff, 0x01, 0x80, 0x01, 
+0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x18, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x03, 0x00, 
+0x03, 0x00};
+static unsigned char symbol24_231_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00};
+static unsigned char symbol24_232_bits[] = {
+0x03, 0x00, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 
+0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 
+0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00};
+static unsigned char symbol24_233_bits[] = {
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x06, 0x00, 
+0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x18, 0x00, 
+0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0x40};
+static unsigned char symbol24_234_bits[] = {
+0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol24_235_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol24_236_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol24_237_bits[] = {
+0x03, 0x03, 0x1f, 0x18, 0x0c, 0x06, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol24_238_bits[] = {
+0x03, 0x03, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x08, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18};
+static unsigned char symbol24_239_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02};
+static unsigned char symbol24_240_bits[] = {
+0x00};
+static unsigned char symbol24_241_bits[] = {
+0x0c, 0x78, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol24_242_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 
+0x60, 0x30, 0x30, 0x18};
+static unsigned char symbol24_243_bits[] = {
+0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03, 0xe0, 0xf0, 0xd8, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18};
+static unsigned char symbol24_244_bits[] = {
+0x00, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol24_245_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03};
+static unsigned char symbol24_246_bits[] = {
+0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0x40, 0x00, 0x33, 0x01, 0x0e, 0x01, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x01, 0x00};
+static unsigned char symbol24_247_bits[] = {
+0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00};
+static unsigned char symbol24_248_bits[] = {
+0x01, 0x00, 0x01, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 
+0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 
+0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
+0x01, 0x00, 0x01, 0x00};
+static unsigned char symbol24_249_bits[] = {
+0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x40, 
+0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x60, 0x00, 0x60, 0x00, 0x30, 
+0x00, 0x30, 0x00, 0x18, 0x00, 0x0c, 0x00};
+static unsigned char symbol24_250_bits[] = {
+0x02, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol24_251_bits[] = {
+0x60, 0x60, 0x60, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol24_252_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 
+0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 
+0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60};
+static unsigned char symbol24_253_bits[] = {
+0x00, 0x00, 0x00, 0x1f, 0x0f, 0x18, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol24_254_bits[] = {
+0x60, 0x60, 0x60, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x02, 0x06, 0x0c, 0x18, 0x0c, 0x06, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char symbol24_255_bits[] = {
+0x00};
+static RotFont symbol24font[] = {
+{5, 1, 1, symbol24_0_bits},
+{5, 1, 1, symbol24_1_bits},
+{5, 1, 1, symbol24_2_bits},
+{5, 1, 1, symbol24_3_bits},
+{5, 1, 1, symbol24_4_bits},
+{5, 1, 1, symbol24_5_bits},
+{5, 1, 1, symbol24_6_bits},
+{5, 1, 1, symbol24_7_bits},
+{5, 1, 1, symbol24_8_bits},
+{5, 1, 1, symbol24_9_bits},
+{5, 1, 1, symbol24_10_bits},
+{5, 1, 1, symbol24_11_bits},
+{5, 1, 1, symbol24_12_bits},
+{5, 1, 1, symbol24_13_bits},
+{5, 1, 1, symbol24_14_bits},
+{5, 1, 1, symbol24_15_bits},
+{5, 1, 1, symbol24_16_bits},
+{5, 1, 1, symbol24_17_bits},
+{5, 1, 1, symbol24_18_bits},
+{5, 1, 1, symbol24_19_bits},
+{5, 1, 1, symbol24_20_bits},
+{5, 1, 1, symbol24_21_bits},
+{5, 1, 1, symbol24_22_bits},
+{5, 1, 1, symbol24_23_bits},
+{5, 1, 1, symbol24_24_bits},
+{5, 1, 1, symbol24_25_bits},
+{5, 1, 1, symbol24_26_bits},
+{5, 1, 1, symbol24_27_bits},
+{5, 1, 1, symbol24_28_bits},
+{5, 1, 1, symbol24_29_bits},
+{5, 1, 1, symbol24_30_bits},
+{5, 1, 1, symbol24_31_bits},
+{1, 1, 1, symbol24_32_bits},
+{3, 17, 17, symbol24_33_bits},
+{15, 17, 17, symbol24_34_bits},
+{11, 17, 17, symbol24_35_bits},
+{11, 17, 17, symbol24_36_bits},
+{18, 17, 17, symbol24_37_bits},
+{15, 17, 17, symbol24_38_bits},
+{8, 13, 13, symbol24_39_bits},
+{6, 22, 17, symbol24_40_bits},
+{6, 22, 17, symbol24_41_bits},
+{8, 10, 17, symbol24_42_bits},
+{10, 10, 12, symbol24_43_bits},
+{3, 6, 2, symbol24_44_bits},
+{11, 2, 8, symbol24_45_bits},
+{2, 3, 3, symbol24_46_bits},
+{7, 17, 17, symbol24_47_bits},
+{10, 17, 17, symbol24_48_bits},
+{6, 17, 17, symbol24_49_bits},
+{10, 17, 17, symbol24_50_bits},
+{9, 17, 17, symbol24_51_bits},
+{10, 17, 17, symbol24_52_bits},
+{9, 17, 17, symbol24_53_bits},
+{10, 17, 17, symbol24_54_bits},
+{10, 17, 17, symbol24_55_bits},
+{10, 17, 17, symbol24_56_bits},
+{10, 17, 17, symbol24_57_bits},
+{2, 11, 11, symbol24_58_bits},
+{3, 15, 11, symbol24_59_bits},
+{12, 12, 13, symbol24_60_bits},
+{10, 6, 10, symbol24_61_bits},
+{12, 12, 13, symbol24_62_bits},
+{8, 17, 17, symbol24_63_bits},
+{11, 12, 13, symbol24_64_bits},
+{17, 17, 17, symbol24_65_bits},
+{13, 17, 17, symbol24_66_bits},
+{17, 17, 17, symbol24_67_bits},
+{16, 17, 17, symbol24_68_bits},
+{13, 17, 17, symbol24_69_bits},
+{16, 17, 17, symbol24_70_bits},
+{13, 17, 17, symbol24_71_bits},
+{16, 17, 17, symbol24_72_bits},
+{6, 17, 17, symbol24_73_bits},
+{14, 17, 17, symbol24_74_bits},
+{15, 17, 17, symbol24_75_bits},
+{15, 17, 17, symbol24_76_bits},
+{20, 17, 17, symbol24_77_bits},
+{16, 17, 17, symbol24_78_bits},
+{16, 17, 17, symbol24_79_bits},
+{16, 17, 17, symbol24_80_bits},
+{16, 17, 17, symbol24_81_bits},
+{12, 17, 17, symbol24_82_bits},
+{12, 17, 17, symbol24_83_bits},
+{14, 17, 17, symbol24_84_bits},
+{16, 17, 17, symbol24_85_bits},
+{9, 18, 13, symbol24_86_bits},
+{16, 17, 17, symbol24_87_bits},
+{14, 17, 17, symbol24_88_bits},
+{18, 17, 17, symbol24_89_bits},
+{12, 17, 17, symbol24_90_bits},
+{5, 22, 17, symbol24_91_bits},
+{12, 10, 10, symbol24_92_bits},
+{5, 22, 17, symbol24_93_bits},
+{14, 17, 17, symbol24_94_bits},
+{13, 2, -3, symbol24_95_bits},
+{13, 2, 23, symbol24_96_bits},
+{14, 13, 13, symbol24_97_bits},
+{10, 24, 19, symbol24_98_bits},
+{12, 18, 13, symbol24_99_bits},
+{10, 18, 18, symbol24_100_bits},
+{9, 13, 13, symbol24_101_bits},
+{12, 22, 17, symbol24_102_bits},
+{11, 18, 13, symbol24_103_bits},
+{12, 18, 13, symbol24_104_bits},
+{7, 13, 13, symbol24_105_bits},
+{12, 18, 13, symbol24_106_bits},
+{12, 13, 13, symbol24_107_bits},
+{12, 19, 19, symbol24_108_bits},
+{12, 18, 13, symbol24_109_bits},
+{11, 13, 13, symbol24_110_bits},
+{11, 13, 13, symbol24_111_bits},
+{13, 13, 13, symbol24_112_bits},
+{10, 17, 17, symbol24_113_bits},
+{10, 18, 13, symbol24_114_bits},
+{14, 13, 13, symbol24_115_bits},
+{10, 13, 13, symbol24_116_bits},
+{13, 13, 13, symbol24_117_bits},
+{16, 14, 14, symbol24_118_bits},
+{14, 13, 13, symbol24_119_bits},
+{10, 24, 19, symbol24_120_bits},
+{16, 18, 13, symbol24_121_bits},
+{10, 24, 19, symbol24_122_bits},
+{8, 22, 17, symbol24_123_bits},
+{2, 22, 17, symbol24_124_bits},
+{8, 22, 17, symbol24_125_bits},
+{11, 4, 9, symbol24_126_bits},
+{5, 1, 1, symbol24_127_bits},
+{5, 1, 1, symbol24_128_bits},
+{5, 1, 1, symbol24_129_bits},
+{5, 1, 1, symbol24_130_bits},
+{5, 1, 1, symbol24_131_bits},
+{5, 1, 1, symbol24_132_bits},
+{5, 1, 1, symbol24_133_bits},
+{5, 1, 1, symbol24_134_bits},
+{5, 1, 1, symbol24_135_bits},
+{5, 1, 1, symbol24_136_bits},
+{5, 1, 1, symbol24_137_bits},
+{5, 1, 1, symbol24_138_bits},
+{5, 1, 1, symbol24_139_bits},
+{5, 1, 1, symbol24_140_bits},
+{5, 1, 1, symbol24_141_bits},
+{5, 1, 1, symbol24_142_bits},
+{5, 1, 1, symbol24_143_bits},
+{5, 1, 1, symbol24_144_bits},
+{5, 1, 1, symbol24_145_bits},
+{5, 1, 1, symbol24_146_bits},
+{5, 1, 1, symbol24_147_bits},
+{5, 1, 1, symbol24_148_bits},
+{5, 1, 1, symbol24_149_bits},
+{5, 1, 1, symbol24_150_bits},
+{5, 1, 1, symbol24_151_bits},
+{5, 1, 1, symbol24_152_bits},
+{5, 1, 1, symbol24_153_bits},
+{5, 1, 1, symbol24_154_bits},
+{5, 1, 1, symbol24_155_bits},
+{5, 1, 1, symbol24_156_bits},
+{5, 1, 1, symbol24_157_bits},
+{5, 1, 1, symbol24_158_bits},
+{5, 1, 1, symbol24_159_bits},
+{5, 1, 1, symbol24_160_bits},
+{13, 17, 17, symbol24_161_bits},
+{5, 6, 18, symbol24_162_bits},
+{12, 15, 15, symbol24_163_bits},
+{8, 17, 17, symbol24_164_bits},
+{15, 7, 11, symbol24_165_bits},
+{13, 22, 17, symbol24_166_bits},
+{14, 13, 13, symbol24_167_bits},
+{10, 13, 13, symbol24_168_bits},
+{12, 13, 13, symbol24_169_bits},
+{12, 13, 13, symbol24_170_bits},
+{24, 12, 13, symbol24_171_bits},
+{25, 12, 13, symbol24_172_bits},
+{12, 31, 23, symbol24_173_bits},
+{25, 12, 13, symbol24_174_bits},
+{12, 29, 24, symbol24_175_bits},
+{7, 7, 17, symbol24_176_bits},
+{10, 14, 14, symbol24_177_bits},
+{9, 6, 18, symbol24_178_bits},
+{12, 15, 15, symbol24_179_bits},
+{11, 10, 12, symbol24_180_bits},
+{14, 7, 11, symbol24_181_bits},
+{10, 19, 19, symbol24_182_bits},
+{8, 8, 11, symbol24_183_bits},
+{10, 8, 11, symbol24_184_bits},
+{10, 13, 13, symbol24_185_bits},
+{11, 8, 11, symbol24_186_bits},
+{11, 8, 11, symbol24_187_bits},
+{18, 3, 3, symbol24_188_bits},
+{2, 32, 24, symbol24_189_bits},
+{25, 2, 8, symbol24_190_bits},
+{14, 16, 16, symbol24_191_bits},
+{12, 16, 16, symbol24_192_bits},
+{14, 19, 18, symbol24_193_bits},
+{17, 18, 18, symbol24_194_bits},
+{15, 20, 15, symbol24_195_bits},
+{16, 17, 17, symbol24_196_bits},
+{16, 17, 17, symbol24_197_bits},
+{18, 17, 17, symbol24_198_bits},
+{16, 13, 13, symbol24_199_bits},
+{16, 13, 13, symbol24_200_bits},
+{15, 12, 12, symbol24_201_bits},
+{15, 15, 12, symbol24_202_bits},
+{15, 16, 14, symbol24_203_bits},
+{15, 12, 12, symbol24_204_bits},
+{15, 15, 12, symbol24_205_bits},
+{12, 13, 13, symbol24_206_bits},
+{12, 17, 15, symbol24_207_bits},
+{17, 16, 16, symbol24_208_bits},
+{16, 18, 18, symbol24_209_bits},
+{16, 17, 17, symbol24_210_bits},
+{16, 17, 17, symbol24_211_bits},
+{20, 10, 17, symbol24_212_bits},
+{17, 21, 18, symbol24_213_bits},
+{14, 23, 23, symbol24_214_bits},
+{2, 3, 9, symbol24_215_bits},
+{15, 8, 8, symbol24_216_bits},
+{13, 12, 12, symbol24_217_bits},
+{13, 12, 12, symbol24_218_bits},
+{23, 12, 12, symbol24_219_bits},
+{23, 12, 12, symbol24_220_bits},
+{12, 23, 23, symbol24_221_bits},
+{23, 12, 12, symbol24_222_bits},
+{12, 23, 23, symbol24_223_bits},
+{10, 18, 18, symbol24_224_bits},
+{7, 22, 17, symbol24_225_bits},
+{16, 17, 17, symbol24_226_bits},
+{16, 17, 17, symbol24_227_bits},
+{18, 10, 17, symbol24_228_bits},
+{14, 20, 18, symbol24_229_bits},
+{9, 31, 23, symbol24_230_bits},
+{2, 32, 24, symbol24_231_bits},
+{9, 32, 24, symbol24_232_bits},
+{7, 31, 23, symbol24_233_bits},
+{2, 32, 24, symbol24_234_bits},
+{7, 29, 24, symbol24_235_bits},
+{7, 31, 23, symbol24_236_bits},
+{5, 32, 24, symbol24_237_bits},
+{7, 29, 24, symbol24_238_bits},
+{2, 32, 24, symbol24_239_bits},
+{5, 1, 1, symbol24_240_bits},
+{7, 22, 17, symbol24_241_bits},
+{8, 28, 23, symbol24_242_bits},
+{8, 31, 23, symbol24_243_bits},
+{2, 32, 24, symbol24_244_bits},
+{8, 26, 24, symbol24_245_bits},
+{9, 31, 23, symbol24_246_bits},
+{2, 32, 24, symbol24_247_bits},
+{9, 32, 24, symbol24_248_bits},
+{7, 31, 23, symbol24_249_bits},
+{2, 32, 24, symbol24_250_bits},
+{7, 29, 24, symbol24_251_bits},
+{7, 31, 23, symbol24_252_bits},
+{5, 32, 24, symbol24_253_bits},
+{7, 29, 24, symbol24_254_bits},
+{5, 1, 1, symbol24_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol8.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol8.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol8.bdf	(revision 22322)
@@ -0,0 +1,2537 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Symbol-Medium-R-Normal--8-80-75-75-P-51-Adobe-FontSpecific
+SIZE 8 75 75
+FONTBOUNDINGBOX 9 11 -1 -3
+STARTPROPERTIES 31
+FOUNDRY "Adobe"
+FAMILY_NAME "Symbol"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 8
+POINT_SIZE 80
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 51
+CHARSET_REGISTRY "Adobe"
+CHARSET_ENCODING "FontSpecific"
+CAP_HEIGHT 6
+X_HEIGHT 4
+FACE_NAME "Symbol"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "No mark"
+_DEC_DEVICE_FONTNAMES "PS=Symbol"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2-1, 18-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+FULL_NAME "Symbol"
+FONT "-Adobe-Symbol-Medium-R-Normal--8-80-75-75-P-51-Adobe-FontSpecific"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 5
+DEFAULT_CHAR 32
+FONT_ASCENT 8
+FONT_DESCENT 2
+ENDPROPERTIES
+CHARS 188
+STARTCHAR space
+ENCODING 32
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 6 1 0
+BITMAP
+80
+80
+80
+80
+00
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+88
+88
+70
+50
+20
+20
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 5 6 -1 0
+BITMAP
+50
+f8
+50
+50
+f8
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+f0
+10
+10
+70
+10
+f0
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+c8
+d0
+20
+58
+98
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+60
+50
+20
+f8
+a0
+58
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 4 0 0
+BITMAP
+c0
+60
+20
+c0
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 8 0 -2
+BITMAP
+40
+80
+80
+80
+80
+80
+80
+40
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 8 1 -2
+BITMAP
+80
+40
+40
+40
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 3 1 2
+BITMAP
+40
+e0
+a0
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+BITMAP
+20
+20
+f8
+20
+20
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 3 0 -2
+BITMAP
+40
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 1 0 2
+BITMAP
+f8
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 1 1 0
+BITMAP
+80
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 6 0 0
+BITMAP
+40
+40
+40
+80
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+40
+a0
+a0
+a0
+a0
+40
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+40
+c0
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+40
+a0
+20
+40
+80
+e0
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+40
+a0
+20
+60
+20
+c0
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 6 0 0
+BITMAP
+20
+60
+a0
+f0
+20
+20
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+e0
+80
+c0
+20
+20
+c0
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+60
+80
+c0
+a0
+a0
+40
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+e0
+a0
+20
+40
+40
+40
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+40
+a0
+40
+a0
+a0
+40
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+40
+a0
+a0
+e0
+20
+c0
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 4 1 0
+BITMAP
+80
+00
+00
+80
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 6 0 -2
+BITMAP
+40
+00
+00
+40
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+18
+60
+c0
+60
+18
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 3 0 1
+BITMAP
+f0
+00
+f0
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+BITMAP
+c0
+30
+18
+30
+c0
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+40
+a0
+20
+40
+00
+40
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+50
+a0
+00
+f0
+00
+f0
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+20
+70
+50
+70
+50
+d8
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+e0
+50
+60
+50
+50
+e0
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+88
+50
+20
+20
+50
+88
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+20
+50
+50
+88
+88
+f8
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+f0
+40
+70
+40
+40
+f0
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+70
+20
+a8
+a8
+20
+70
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+f0
+50
+40
+40
+40
+c0
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+d8
+48
+78
+48
+48
+d8
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 6 0 0
+BITMAP
+c0
+40
+40
+40
+40
+c0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+60
+50
+38
+d0
+50
+60
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+d8
+50
+60
+60
+50
+d8
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 6 0 0
+BITMAP
+20
+30
+48
+48
+48
+cc
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 7 6 0 0
+BITMAP
+c6
+44
+6c
+54
+54
+d6
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+d8
+48
+68
+78
+58
+c8
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+70
+d8
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 6 0 0
+BITMAP
+fc
+48
+48
+48
+48
+d8
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+70
+d8
+a8
+a8
+d8
+70
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+e0
+50
+50
+60
+40
+e0
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+f0
+90
+40
+40
+90
+f0
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+f0
+50
+40
+40
+40
+60
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+d8
+48
+50
+20
+20
+70
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 -2
+BITMAP
+60
+80
+80
+40
+20
+40
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 5 6 1 0
+BITMAP
+20
+50
+88
+88
+50
+d8
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+70
+00
+70
+00
+88
+f8
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 7 6 0 0
+BITMAP
+d6
+54
+54
+7c
+10
+38
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+f0
+90
+20
+40
+90
+f0
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 8 0 -2
+BITMAP
+c0
+80
+80
+80
+80
+80
+80
+c0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 4 1 0
+BITMAP
+40
+00
+00
+90
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 8 1 -2
+BITMAP
+c0
+40
+40
+40
+40
+40
+40
+c0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 1 0
+BITMAP
+40
+40
+40
+40
+40
+f0
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 1 0 -2
+BITMAP
+f0
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 5 1 0 7
+BITMAP
+f8
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 4 0 0
+BITMAP
+50
+b0
+a0
+50
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 9 0 -2
+BITMAP
+40
+a0
+a0
+c0
+a0
+a0
+c0
+80
+80
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 -2
+BITMAP
+b0
+a0
+40
+40
+a0
+b0
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+40
+a0
+80
+40
+a0
+a0
+c0
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 4 0 0
+BITMAP
+60
+e0
+80
+60
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 0 -2
+BITMAP
+40
+40
+60
+d0
+d0
+60
+40
+40
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 -2
+BITMAP
+50
+d0
+60
+20
+40
+40
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 -2
+BITMAP
+60
+d0
+50
+50
+10
+10
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 4 0 0
+BITMAP
+c0
+40
+40
+40
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 -2
+BITMAP
+a0
+d0
+d0
+60
+40
+40
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+BITMAP
+50
+f0
+60
+50
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 7 0 0
+BITMAP
+80
+c0
+40
+20
+60
+90
+98
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 -2
+BITMAP
+a0
+a0
+a0
+d0
+80
+c0
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+BITMAP
+90
+90
+e0
+40
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 4 0 0
+BITMAP
+40
+a0
+a0
+40
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+BITMAP
+f0
+50
+50
+d0
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+40
+a0
+e0
+a0
+a0
+40
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 -2
+BITMAP
+40
+a0
+a0
+c0
+80
+80
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+BITMAP
+70
+a0
+a0
+40
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 4 0 0
+BITMAP
+e0
+40
+40
+60
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+BITMAP
+50
+d0
+50
+60
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+f8
+a8
+a8
+a8
+58
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 4 0 0
+BITMAP
+88
+a8
+a8
+58
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 9 0 -2
+BITMAP
+80
+e0
+40
+60
+80
+80
+40
+20
+60
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 -2
+BITMAP
+a8
+a8
+a8
+70
+20
+20
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 9 0 -2
+BITMAP
+80
+e0
+40
+80
+80
+80
+40
+20
+60
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 9 1 -2
+BITMAP
+20
+40
+40
+40
+80
+40
+40
+40
+20
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 8 0 -2
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 9 1 -2
+BITMAP
+80
+40
+40
+40
+20
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 2 0 1
+BITMAP
+50
+a0
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 3 0 4
+BITMAP
+00
+00
+00
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 4 7 -1 0
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 3 0 1
+BITMAP
+00
+00
+00
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 8 0 -2
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 1 -1
+BITMAP
+00
+90
+a0
+40
+40
+40
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 -1
+BITMAP
+40
+40
+40
+80
+30
+40
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 1 -1
+BITMAP
+80
+70
+00
+f0
+10
+20
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 1 0
+BITMAP
+20
+40
+40
+40
+80
+50
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 8 5 0 0
+BITMAP
+a8
+50
+30
+40
+e0
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 8 5 0 0
+BITMAP
+40
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 11 0 -3
+BITMAP
+60
+60
+f0
+f0
+40
+40
+20
+70
+f8
+f0
+60
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 8 5 0 0
+BITMAP
+40
+48
+d8
+f8
+f0
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 10 0 -2
+BITMAP
+60
+40
+20
+60
+f0
+f0
+50
+40
+20
+40
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 3 0 3
+BITMAP
+e0
+40
+20
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 6 0 0
+BITMAP
+20
+40
+f8
+40
+20
+20
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 4 3 0 4
+BITMAP
+70
+a0
+20
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 6 0 0
+BITMAP
+20
+20
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+BITMAP
+20
+00
+00
+f0
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 3 0 1
+BITMAP
+00
+00
+20
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+20
+20
+20
+20
+20
+20
+a0
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 3 0 1
+BITMAP
+60
+20
+40
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+BITMAP
+a0
+40
+20
+20
+f0
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 7 0 -2
+BITMAP
+20
+20
+f0
+50
+50
+a0
+c0
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+BITMAP
+20
+10
+e0
+00
+f0
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+BITMAP
+90
+60
+60
+90
+50
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 5 1 1 0
+BITMAP
+a0
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 1 11 2 -3
+BITMAP
+00
+00
+80
+00
+00
+80
+80
+00
+00
+80
+00
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 8 1 0 2
+BITMAP
+60
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+00
+f0
+00
+60
+20
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+20
+f0
+20
+f0
+40
+40
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 7 0 0
+BITMAP
+f0
+00
+f0
+00
+f0
+50
+a0
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 7 0 0
+BITMAP
+00
+50
+a0
+a8
+80
+80
+80
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 5 7 1 -2
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+80
+f8
+08
+08
+48
+f8
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+40
+d8
+48
+68
+78
+d8
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 -1
+BITMAP
+c8
+78
+88
+90
+10
+08
+c8
+78
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 4 0 0
+BITMAP
+58
+a0
+a0
+38
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 4 0 0
+BITMAP
+28
+68
+c8
+40
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 4 0 0
+BITMAP
+98
+a8
+c8
+70
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 -2
+BITMAP
+c0
+40
+70
+d8
+a8
+a8
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 -1
+BITMAP
+d8
+70
+70
+a8
+f8
+a8
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 4 0 0
+BITMAP
+a8
+70
+08
+70
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 -2
+BITMAP
+98
+a8
+a8
+c8
+70
+80
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 4 1 0
+BITMAP
+70
+80
+80
+80
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 6 1 -1
+BITMAP
+80
+80
+80
+70
+f0
+00
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+08
+f0
+f0
+08
+08
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+f0
+00
+f8
+08
+78
+90
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 7 0 -1
+BITMAP
+a0
+78
+40
+78
+80
+80
+78
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 7 0 -1
+BITMAP
+78
+80
+80
+78
+00
+f8
+70
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 8 4 0 2
+BITMAP
+f0
+80
+70
+20
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 6 8 0 -1
+BITMAP
+70
+f0
+c0
+70
+40
+08
+10
+20
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 8 0 0
+BITMAP
+40
+f8
+f8
+88
+88
+50
+70
+20
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 1 1 2
+BITMAP
+00
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 3 0 0
+BITMAP
+c8
+b8
+b0
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+BITMAP
+a0
+c0
+70
+70
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+BITMAP
+80
+b0
+a0
+b0
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 1080 0
+DWIDTH 9 0
+BBX 8 4 0 0
+BITMAP
+88
+70
+fc
+5a
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 7 4 0 0
+BITMAP
+5a
+5a
+fc
+48
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 0 0
+BITMAP
+40
+40
+40
+40
+40
+c0
+00
+00
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 7 4 0 0
+BITMAP
+10
+10
+a0
+a0
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 0 0
+BITMAP
+40
+40
+80
+f0
+00
+00
+20
+50
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+90
+90
+90
+90
+a0
+40
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 8 0 -2
+BITMAP
+00
+40
+c0
+00
+00
+c0
+40
+00
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 7 0 -1
+BITMAP
+40
+60
+f0
+60
+60
+60
+60
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 7 0 -1
+BITMAP
+60
+08
+f8
+f8
+08
+60
+60
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 6 4 0 2
+BITMAP
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 -1
+BITMAP
+60
+40
+20
+50
+c8
+c8
+50
+20
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 11 1 -3
+BITMAP
+40
+40
+80
+80
+80
+80
+40
+40
+40
+80
+80
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 1 11 1 -3
+BITMAP
+80
+80
+80
+00
+00
+80
+80
+80
+80
+80
+00
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 11 1 -3
+BITMAP
+c0
+40
+40
+40
+c0
+80
+40
+40
+00
+40
+80
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 11 1 -3
+BITMAP
+e0
+40
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 1 11 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 11 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 2 11 1 -3
+BITMAP
+40
+c0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 2 11 0 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 2 11 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 1 11 1 -3
+BITMAP
+80
+00
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 8 0 -2
+BITMAP
+80
+40
+40
+40
+40
+80
+40
+40
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 11 1 -3
+BITMAP
+40
+40
+40
+40
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 3 11 1 -3
+BITMAP
+80
+80
+80
+40
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 1 11 -1 -3
+BITMAP
+80
+80
+80
+80
+00
+80
+80
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 3 11 -1 -3
+BITMAP
+80
+80
+20
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 11 0 -3
+BITMAP
+40
+80
+40
+40
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 1 11 1 -3
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 11 0 -3
+BITMAP
+80
+80
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 11 0 -3
+BITMAP
+c0
+c0
+80
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 1 11 1 -3
+BITMAP
+00
+00
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 11 0 -3
+BITMAP
+80
+80
+40
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 2 11 0 -3
+BITMAP
+40
+80
+c0
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 2 11 1 -3
+BITMAP
+40
+40
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 2 11 0 -3
+BITMAP
+80
+80
+40
+40
+40
+40
+40
+40
+40
+40
+40
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol8.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol8.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/symbol8.h	(revision 22322)
@@ -0,0 +1,769 @@
+static unsigned char symbol8_0_bits[] = {
+0x00};
+static unsigned char symbol8_1_bits[] = {
+0x00};
+static unsigned char symbol8_2_bits[] = {
+0x00};
+static unsigned char symbol8_3_bits[] = {
+0x00};
+static unsigned char symbol8_4_bits[] = {
+0x00};
+static unsigned char symbol8_5_bits[] = {
+0x00};
+static unsigned char symbol8_6_bits[] = {
+0x00};
+static unsigned char symbol8_7_bits[] = {
+0x00};
+static unsigned char symbol8_8_bits[] = {
+0x00};
+static unsigned char symbol8_9_bits[] = {
+0x00};
+static unsigned char symbol8_10_bits[] = {
+0x00};
+static unsigned char symbol8_11_bits[] = {
+0x00};
+static unsigned char symbol8_12_bits[] = {
+0x00};
+static unsigned char symbol8_13_bits[] = {
+0x00};
+static unsigned char symbol8_14_bits[] = {
+0x00};
+static unsigned char symbol8_15_bits[] = {
+0x00};
+static unsigned char symbol8_16_bits[] = {
+0x00};
+static unsigned char symbol8_17_bits[] = {
+0x00};
+static unsigned char symbol8_18_bits[] = {
+0x00};
+static unsigned char symbol8_19_bits[] = {
+0x00};
+static unsigned char symbol8_20_bits[] = {
+0x00};
+static unsigned char symbol8_21_bits[] = {
+0x00};
+static unsigned char symbol8_22_bits[] = {
+0x00};
+static unsigned char symbol8_23_bits[] = {
+0x00};
+static unsigned char symbol8_24_bits[] = {
+0x00};
+static unsigned char symbol8_25_bits[] = {
+0x00};
+static unsigned char symbol8_26_bits[] = {
+0x00};
+static unsigned char symbol8_27_bits[] = {
+0x00};
+static unsigned char symbol8_28_bits[] = {
+0x00};
+static unsigned char symbol8_29_bits[] = {
+0x00};
+static unsigned char symbol8_30_bits[] = {
+0x00};
+static unsigned char symbol8_31_bits[] = {
+0x00};
+static unsigned char symbol8_32_bits[] = {
+0x00};
+static unsigned char symbol8_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x00, 0x01};
+static unsigned char symbol8_34_bits[] = {
+0x11, 0x11, 0x0e, 0x0a, 0x04, 0x04};
+static unsigned char symbol8_35_bits[] = {
+0x0a, 0x1f, 0x0a, 0x0a, 0x1f, 0x0a};
+static unsigned char symbol8_36_bits[] = {
+0x0f, 0x08, 0x08, 0x0e, 0x08, 0x0f};
+static unsigned char symbol8_37_bits[] = {
+0x13, 0x0b, 0x04, 0x1a, 0x19};
+static unsigned char symbol8_38_bits[] = {
+0x06, 0x0a, 0x04, 0x1f, 0x05, 0x1a};
+static unsigned char symbol8_39_bits[] = {
+0x03, 0x06, 0x04, 0x03};
+static unsigned char symbol8_40_bits[] = {
+0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02};
+static unsigned char symbol8_41_bits[] = {
+0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01};
+static unsigned char symbol8_42_bits[] = {
+0x02, 0x07, 0x05};
+static unsigned char symbol8_43_bits[] = {
+0x04, 0x04, 0x1f, 0x04, 0x04};
+static unsigned char symbol8_44_bits[] = {
+0x02, 0x02, 0x01};
+static unsigned char symbol8_45_bits[] = {
+0x1f};
+static unsigned char symbol8_46_bits[] = {
+0x01};
+static unsigned char symbol8_47_bits[] = {
+0x02, 0x02, 0x02, 0x01, 0x01, 0x01};
+static unsigned char symbol8_48_bits[] = {
+0x02, 0x05, 0x05, 0x05, 0x05, 0x02};
+static unsigned char symbol8_49_bits[] = {
+0x02, 0x03, 0x02, 0x02, 0x02, 0x07};
+static unsigned char symbol8_50_bits[] = {
+0x02, 0x05, 0x04, 0x02, 0x01, 0x07};
+static unsigned char symbol8_51_bits[] = {
+0x02, 0x05, 0x04, 0x06, 0x04, 0x03};
+static unsigned char symbol8_52_bits[] = {
+0x04, 0x06, 0x05, 0x0f, 0x04, 0x04};
+static unsigned char symbol8_53_bits[] = {
+0x07, 0x01, 0x03, 0x04, 0x04, 0x03};
+static unsigned char symbol8_54_bits[] = {
+0x06, 0x01, 0x03, 0x05, 0x05, 0x02};
+static unsigned char symbol8_55_bits[] = {
+0x07, 0x05, 0x04, 0x02, 0x02, 0x02};
+static unsigned char symbol8_56_bits[] = {
+0x02, 0x05, 0x02, 0x05, 0x05, 0x02};
+static unsigned char symbol8_57_bits[] = {
+0x02, 0x05, 0x05, 0x07, 0x04, 0x03};
+static unsigned char symbol8_58_bits[] = {
+0x01, 0x00, 0x00, 0x01};
+static unsigned char symbol8_59_bits[] = {
+0x02, 0x00, 0x00, 0x02, 0x02, 0x01};
+static unsigned char symbol8_60_bits[] = {
+0x18, 0x06, 0x03, 0x06, 0x18};
+static unsigned char symbol8_61_bits[] = {
+0x0f, 0x00, 0x0f};
+static unsigned char symbol8_62_bits[] = {
+0x03, 0x0c, 0x18, 0x0c, 0x03};
+static unsigned char symbol8_63_bits[] = {
+0x02, 0x05, 0x04, 0x02, 0x00, 0x02};
+static unsigned char symbol8_64_bits[] = {
+0x0a, 0x05, 0x00, 0x0f, 0x00, 0x0f};
+static unsigned char symbol8_65_bits[] = {
+0x04, 0x0e, 0x0a, 0x0e, 0x0a, 0x1b};
+static unsigned char symbol8_66_bits[] = {
+0x07, 0x0a, 0x06, 0x0a, 0x0a, 0x07};
+static unsigned char symbol8_67_bits[] = {
+0x11, 0x0a, 0x04, 0x04, 0x0a, 0x11};
+static unsigned char symbol8_68_bits[] = {
+0x04, 0x0a, 0x0a, 0x11, 0x11, 0x1f};
+static unsigned char symbol8_69_bits[] = {
+0x0f, 0x02, 0x0e, 0x02, 0x02, 0x0f};
+static unsigned char symbol8_70_bits[] = {
+0x0e, 0x04, 0x15, 0x15, 0x04, 0x0e};
+static unsigned char symbol8_71_bits[] = {
+0x0f, 0x0a, 0x02, 0x02, 0x02, 0x03};
+static unsigned char symbol8_72_bits[] = {
+0x1b, 0x12, 0x1e, 0x12, 0x12, 0x1b};
+static unsigned char symbol8_73_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x02, 0x03};
+static unsigned char symbol8_74_bits[] = {
+0x06, 0x0a, 0x1c, 0x0b, 0x0a, 0x06};
+static unsigned char symbol8_75_bits[] = {
+0x1b, 0x0a, 0x06, 0x06, 0x0a, 0x1b};
+static unsigned char symbol8_76_bits[] = {
+0x04, 0x0c, 0x12, 0x12, 0x12, 0x33};
+static unsigned char symbol8_77_bits[] = {
+0x63, 0x22, 0x36, 0x2a, 0x2a, 0x6b};
+static unsigned char symbol8_78_bits[] = {
+0x1b, 0x12, 0x16, 0x1e, 0x1a, 0x13};
+static unsigned char symbol8_79_bits[] = {
+0x0e, 0x1b, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char symbol8_80_bits[] = {
+0x3f, 0x12, 0x12, 0x12, 0x12, 0x1b};
+static unsigned char symbol8_81_bits[] = {
+0x0e, 0x1b, 0x15, 0x15, 0x1b, 0x0e};
+static unsigned char symbol8_82_bits[] = {
+0x07, 0x0a, 0x0a, 0x06, 0x02, 0x07};
+static unsigned char symbol8_83_bits[] = {
+0x0f, 0x09, 0x02, 0x02, 0x09, 0x0f};
+static unsigned char symbol8_84_bits[] = {
+0x0f, 0x0a, 0x02, 0x02, 0x02, 0x06};
+static unsigned char symbol8_85_bits[] = {
+0x1b, 0x12, 0x0a, 0x04, 0x04, 0x0e};
+static unsigned char symbol8_86_bits[] = {
+0x06, 0x01, 0x01, 0x02, 0x04, 0x02};
+static unsigned char symbol8_87_bits[] = {
+0x04, 0x0a, 0x11, 0x11, 0x0a, 0x1b};
+static unsigned char symbol8_88_bits[] = {
+0x0e, 0x00, 0x0e, 0x00, 0x11, 0x1f};
+static unsigned char symbol8_89_bits[] = {
+0x6b, 0x2a, 0x2a, 0x3e, 0x08, 0x1c};
+static unsigned char symbol8_90_bits[] = {
+0x0f, 0x09, 0x04, 0x02, 0x09, 0x0f};
+static unsigned char symbol8_91_bits[] = {
+0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03};
+static unsigned char symbol8_92_bits[] = {
+0x02, 0x00, 0x00, 0x09};
+static unsigned char symbol8_93_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03};
+static unsigned char symbol8_94_bits[] = {
+0x02, 0x02, 0x02, 0x02, 0x02, 0x0f};
+static unsigned char symbol8_95_bits[] = {
+0x0f};
+static unsigned char symbol8_96_bits[] = {
+0x1f};
+static unsigned char symbol8_97_bits[] = {
+0x0a, 0x0d, 0x05, 0x0a};
+static unsigned char symbol8_98_bits[] = {
+0x02, 0x05, 0x05, 0x03, 0x05, 0x05, 0x03, 0x01, 0x01};
+static unsigned char symbol8_99_bits[] = {
+0x0d, 0x05, 0x02, 0x02, 0x05, 0x0d};
+static unsigned char symbol8_100_bits[] = {
+0x02, 0x05, 0x01, 0x02, 0x05, 0x05, 0x03};
+static unsigned char symbol8_101_bits[] = {
+0x06, 0x07, 0x01, 0x06};
+static unsigned char symbol8_102_bits[] = {
+0x02, 0x02, 0x06, 0x0b, 0x0b, 0x06, 0x02, 0x02};
+static unsigned char symbol8_103_bits[] = {
+0x0a, 0x0b, 0x06, 0x04, 0x02, 0x02};
+static unsigned char symbol8_104_bits[] = {
+0x06, 0x0b, 0x0a, 0x0a, 0x08, 0x08};
+static unsigned char symbol8_105_bits[] = {
+0x03, 0x02, 0x02, 0x02};
+static unsigned char symbol8_106_bits[] = {
+0x05, 0x0b, 0x0b, 0x06, 0x02, 0x02};
+static unsigned char symbol8_107_bits[] = {
+0x0a, 0x0f, 0x06, 0x0a};
+static unsigned char symbol8_108_bits[] = {
+0x01, 0x03, 0x02, 0x04, 0x06, 0x09, 0x19};
+static unsigned char symbol8_109_bits[] = {
+0x05, 0x05, 0x05, 0x0b, 0x01, 0x03};
+static unsigned char symbol8_110_bits[] = {
+0x09, 0x09, 0x07, 0x02};
+static unsigned char symbol8_111_bits[] = {
+0x02, 0x05, 0x05, 0x02};
+static unsigned char symbol8_112_bits[] = {
+0x0f, 0x0a, 0x0a, 0x0b};
+static unsigned char symbol8_113_bits[] = {
+0x02, 0x05, 0x07, 0x05, 0x05, 0x02};
+static unsigned char symbol8_114_bits[] = {
+0x02, 0x05, 0x05, 0x03, 0x01, 0x01};
+static unsigned char symbol8_115_bits[] = {
+0x0e, 0x05, 0x05, 0x02};
+static unsigned char symbol8_116_bits[] = {
+0x07, 0x02, 0x02, 0x06};
+static unsigned char symbol8_117_bits[] = {
+0x0a, 0x0b, 0x0a, 0x06};
+static unsigned char symbol8_118_bits[] = {
+0x1f, 0x15, 0x15, 0x15, 0x1a};
+static unsigned char symbol8_119_bits[] = {
+0x11, 0x15, 0x15, 0x1a};
+static unsigned char symbol8_120_bits[] = {
+0x01, 0x07, 0x02, 0x06, 0x01, 0x01, 0x02, 0x04, 0x06};
+static unsigned char symbol8_121_bits[] = {
+0x15, 0x15, 0x15, 0x0e, 0x04, 0x04};
+static unsigned char symbol8_122_bits[] = {
+0x01, 0x07, 0x02, 0x01, 0x01, 0x01, 0x02, 0x04, 0x06};
+static unsigned char symbol8_123_bits[] = {
+0x04, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x04};
+static unsigned char symbol8_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_125_bits[] = {
+0x01, 0x02, 0x02, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01};
+static unsigned char symbol8_126_bits[] = {
+0x0a, 0x05};
+static unsigned char symbol8_127_bits[] = {
+0x00};
+static unsigned char symbol8_128_bits[] = {
+0x00};
+static unsigned char symbol8_129_bits[] = {
+0x00};
+static unsigned char symbol8_130_bits[] = {
+0x00};
+static unsigned char symbol8_131_bits[] = {
+0x00};
+static unsigned char symbol8_132_bits[] = {
+0x00};
+static unsigned char symbol8_133_bits[] = {
+0x00};
+static unsigned char symbol8_134_bits[] = {
+0x00};
+static unsigned char symbol8_135_bits[] = {
+0x00};
+static unsigned char symbol8_136_bits[] = {
+0x00};
+static unsigned char symbol8_137_bits[] = {
+0x00};
+static unsigned char symbol8_138_bits[] = {
+0x00};
+static unsigned char symbol8_139_bits[] = {
+0x00};
+static unsigned char symbol8_140_bits[] = {
+0x00};
+static unsigned char symbol8_141_bits[] = {
+0x00};
+static unsigned char symbol8_142_bits[] = {
+0x00};
+static unsigned char symbol8_143_bits[] = {
+0x00};
+static unsigned char symbol8_144_bits[] = {
+0x00};
+static unsigned char symbol8_145_bits[] = {
+0x00};
+static unsigned char symbol8_146_bits[] = {
+0x00};
+static unsigned char symbol8_147_bits[] = {
+0x00};
+static unsigned char symbol8_148_bits[] = {
+0x00};
+static unsigned char symbol8_149_bits[] = {
+0x00};
+static unsigned char symbol8_150_bits[] = {
+0x00};
+static unsigned char symbol8_151_bits[] = {
+0x00};
+static unsigned char symbol8_152_bits[] = {
+0x00};
+static unsigned char symbol8_153_bits[] = {
+0x00};
+static unsigned char symbol8_154_bits[] = {
+0x00};
+static unsigned char symbol8_155_bits[] = {
+0x00};
+static unsigned char symbol8_156_bits[] = {
+0x00};
+static unsigned char symbol8_157_bits[] = {
+0x00};
+static unsigned char symbol8_158_bits[] = {
+0x00};
+static unsigned char symbol8_159_bits[] = {
+0x00};
+static unsigned char symbol8_160_bits[] = {
+0x00};
+static unsigned char symbol8_161_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol8_162_bits[] = {
+0x00, 0x00, 0x00};
+static unsigned char symbol8_163_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol8_164_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol8_165_bits[] = {
+0x00, 0x00, 0x00};
+static unsigned char symbol8_166_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol8_167_bits[] = {
+0x00, 0x09, 0x05, 0x02, 0x02, 0x02};
+static unsigned char symbol8_168_bits[] = {
+0x02, 0x02, 0x02, 0x01, 0x0c, 0x02};
+static unsigned char symbol8_169_bits[] = {
+0x01, 0x0e, 0x00, 0x0f, 0x08, 0x04};
+static unsigned char symbol8_170_bits[] = {
+0x04, 0x02, 0x02, 0x02, 0x01, 0x0a};
+static unsigned char symbol8_171_bits[] = {
+0x15, 0x0a, 0x0c, 0x02, 0x07};
+static unsigned char symbol8_172_bits[] = {
+0x02, 0x02, 0x02, 0x02, 0x01};
+static unsigned char symbol8_173_bits[] = {
+0x06, 0x06, 0x0f, 0x0f, 0x02, 0x02, 0x04, 0x0e, 0x1f, 0x0f, 0x06};
+static unsigned char symbol8_174_bits[] = {
+0x02, 0x12, 0x1b, 0x1f, 0x0f};
+static unsigned char symbol8_175_bits[] = {
+0x06, 0x02, 0x04, 0x06, 0x0f, 0x0f, 0x0a, 0x02, 0x04, 0x02};
+static unsigned char symbol8_176_bits[] = {
+0x07, 0x02, 0x04};
+static unsigned char symbol8_177_bits[] = {
+0x04, 0x02, 0x1f, 0x02, 0x04, 0x04};
+static unsigned char symbol8_178_bits[] = {
+0x0e, 0x05, 0x04};
+static unsigned char symbol8_179_bits[] = {
+0x04, 0x04, 0x04, 0x04, 0x04, 0x04};
+static unsigned char symbol8_180_bits[] = {
+0x04, 0x00, 0x00, 0x0f};
+static unsigned char symbol8_181_bits[] = {
+0x00, 0x00, 0x04};
+static unsigned char symbol8_182_bits[] = {
+0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05};
+static unsigned char symbol8_183_bits[] = {
+0x06, 0x04, 0x02};
+static unsigned char symbol8_184_bits[] = {
+0x05, 0x02, 0x04, 0x04, 0x0f};
+static unsigned char symbol8_185_bits[] = {
+0x04, 0x04, 0x0f, 0x0a, 0x0a, 0x05, 0x03};
+static unsigned char symbol8_186_bits[] = {
+0x04, 0x08, 0x07, 0x00, 0x0f};
+static unsigned char symbol8_187_bits[] = {
+0x09, 0x06, 0x06, 0x09, 0x0a};
+static unsigned char symbol8_188_bits[] = {
+0x05};
+static unsigned char symbol8_189_bits[] = {
+0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00};
+static unsigned char symbol8_190_bits[] = {
+0x06};
+static unsigned char symbol8_191_bits[] = {
+0x00, 0x0f, 0x00, 0x06, 0x04};
+static unsigned char symbol8_192_bits[] = {
+0x04, 0x0f, 0x04, 0x0f, 0x02, 0x02};
+static unsigned char symbol8_193_bits[] = {
+0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x0a, 0x05};
+static unsigned char symbol8_194_bits[] = {
+0x00, 0x0a, 0x05, 0x15, 0x01, 0x01, 0x01};
+static unsigned char symbol8_195_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_196_bits[] = {
+0x01, 0x1f, 0x10, 0x10, 0x12, 0x1f};
+static unsigned char symbol8_197_bits[] = {
+0x02, 0x1b, 0x12, 0x16, 0x1e, 0x1b};
+static unsigned char symbol8_198_bits[] = {
+0x13, 0x1e, 0x11, 0x09, 0x08, 0x10, 0x13, 0x1e};
+static unsigned char symbol8_199_bits[] = {
+0x1a, 0x05, 0x05, 0x1c};
+static unsigned char symbol8_200_bits[] = {
+0x14, 0x16, 0x13, 0x02};
+static unsigned char symbol8_201_bits[] = {
+0x19, 0x15, 0x13, 0x0e};
+static unsigned char symbol8_202_bits[] = {
+0x03, 0x02, 0x0e, 0x1b, 0x15, 0x15};
+static unsigned char symbol8_203_bits[] = {
+0x1b, 0x0e, 0x0e, 0x15, 0x1f, 0x15};
+static unsigned char symbol8_204_bits[] = {
+0x15, 0x0e, 0x10, 0x0e};
+static unsigned char symbol8_205_bits[] = {
+0x19, 0x15, 0x15, 0x13, 0x0e, 0x01};
+static unsigned char symbol8_206_bits[] = {
+0x0e, 0x01, 0x01, 0x01};
+static unsigned char symbol8_207_bits[] = {
+0x01, 0x01, 0x01, 0x0e, 0x0f, 0x00};
+static unsigned char symbol8_208_bits[] = {
+0x10, 0x0f, 0x0f, 0x10, 0x10};
+static unsigned char symbol8_209_bits[] = {
+0x0f, 0x00, 0x1f, 0x10, 0x1e, 0x09};
+static unsigned char symbol8_210_bits[] = {
+0x05, 0x1e, 0x02, 0x1e, 0x01, 0x01, 0x1e};
+static unsigned char symbol8_211_bits[] = {
+0x1e, 0x01, 0x01, 0x1e, 0x00, 0x1f, 0x0e};
+static unsigned char symbol8_212_bits[] = {
+0x0f, 0x01, 0x0e, 0x04};
+static unsigned char symbol8_213_bits[] = {
+0x0e, 0x0f, 0x03, 0x0e, 0x02, 0x10, 0x08, 0x04};
+static unsigned char symbol8_214_bits[] = {
+0x02, 0x1f, 0x1f, 0x11, 0x11, 0x0a, 0x0e, 0x04};
+static unsigned char symbol8_215_bits[] = {
+0x00};
+static unsigned char symbol8_216_bits[] = {
+0x13, 0x1d, 0x0d};
+static unsigned char symbol8_217_bits[] = {
+0x05, 0x03, 0x0e, 0x0e};
+static unsigned char symbol8_218_bits[] = {
+0x01, 0x0d, 0x05, 0x0d};
+static unsigned char symbol8_219_bits[] = {
+0x11, 0x0e, 0x3f, 0x5a};
+static unsigned char symbol8_220_bits[] = {
+0x5a, 0x5a, 0x3f, 0x12};
+static unsigned char symbol8_221_bits[] = {
+0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x00, 0x00};
+static unsigned char symbol8_222_bits[] = {
+0x08, 0x08, 0x05, 0x05};
+static unsigned char symbol8_223_bits[] = {
+0x02, 0x02, 0x01, 0x0f, 0x00, 0x00, 0x04, 0x0a};
+static unsigned char symbol8_224_bits[] = {
+0x09, 0x09, 0x09, 0x09, 0x05, 0x02};
+static unsigned char symbol8_225_bits[] = {
+0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x00};
+static unsigned char symbol8_226_bits[] = {
+0x02, 0x06, 0x0f, 0x06, 0x06, 0x06, 0x06};
+static unsigned char symbol8_227_bits[] = {
+0x06, 0x10, 0x1f, 0x1f, 0x10, 0x06, 0x06};
+static unsigned char symbol8_228_bits[] = {
+0x06, 0x06, 0x06, 0x0f};
+static unsigned char symbol8_229_bits[] = {
+0x06, 0x02, 0x04, 0x0a, 0x13, 0x13, 0x0a, 0x04};
+static unsigned char symbol8_230_bits[] = {
+0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01};
+static unsigned char symbol8_231_bits[] = {
+0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00};
+static unsigned char symbol8_232_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x03, 0x01, 0x02, 0x02, 0x00, 0x02, 0x01};
+static unsigned char symbol8_233_bits[] = {
+0x07, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_234_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_235_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_236_bits[] = {
+0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_237_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_238_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_239_bits[] = {
+0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_240_bits[] = {
+0x00};
+static unsigned char symbol8_241_bits[] = {
+0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02};
+static unsigned char symbol8_242_bits[] = {
+0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_243_bits[] = {
+0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_244_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol8_245_bits[] = {
+0x01, 0x01, 0x04, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char symbol8_246_bits[] = {
+0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_247_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_248_bits[] = {
+0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static unsigned char symbol8_249_bits[] = {
+0x03, 0x03, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char symbol8_250_bits[] = {
+0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_251_bits[] = {
+0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char symbol8_252_bits[] = {
+0x02, 0x01, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char symbol8_253_bits[] = {
+0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char symbol8_254_bits[] = {
+0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char symbol8_255_bits[] = {
+0x00};
+static RotFont symbol8font[] = {
+{5, 1, 1, symbol8_0_bits},
+{5, 1, 1, symbol8_1_bits},
+{5, 1, 1, symbol8_2_bits},
+{5, 1, 1, symbol8_3_bits},
+{5, 1, 1, symbol8_4_bits},
+{5, 1, 1, symbol8_5_bits},
+{5, 1, 1, symbol8_6_bits},
+{5, 1, 1, symbol8_7_bits},
+{5, 1, 1, symbol8_8_bits},
+{5, 1, 1, symbol8_9_bits},
+{5, 1, 1, symbol8_10_bits},
+{5, 1, 1, symbol8_11_bits},
+{5, 1, 1, symbol8_12_bits},
+{5, 1, 1, symbol8_13_bits},
+{5, 1, 1, symbol8_14_bits},
+{5, 1, 1, symbol8_15_bits},
+{5, 1, 1, symbol8_16_bits},
+{5, 1, 1, symbol8_17_bits},
+{5, 1, 1, symbol8_18_bits},
+{5, 1, 1, symbol8_19_bits},
+{5, 1, 1, symbol8_20_bits},
+{5, 1, 1, symbol8_21_bits},
+{5, 1, 1, symbol8_22_bits},
+{5, 1, 1, symbol8_23_bits},
+{5, 1, 1, symbol8_24_bits},
+{5, 1, 1, symbol8_25_bits},
+{5, 1, 1, symbol8_26_bits},
+{5, 1, 1, symbol8_27_bits},
+{5, 1, 1, symbol8_28_bits},
+{5, 1, 1, symbol8_29_bits},
+{5, 1, 1, symbol8_30_bits},
+{5, 1, 1, symbol8_31_bits},
+{1, 1, 1, symbol8_32_bits},
+{1, 6, 6, symbol8_33_bits},
+{5, 6, 6, symbol8_34_bits},
+{5, 6, 6, symbol8_35_bits},
+{4, 6, 6, symbol8_36_bits},
+{5, 5, 5, symbol8_37_bits},
+{5, 6, 6, symbol8_38_bits},
+{3, 4, 4, symbol8_39_bits},
+{2, 8, 6, symbol8_40_bits},
+{2, 8, 6, symbol8_41_bits},
+{3, 3, 5, symbol8_42_bits},
+{5, 5, 5, symbol8_43_bits},
+{2, 3, 1, symbol8_44_bits},
+{5, 1, 3, symbol8_45_bits},
+{1, 1, 1, symbol8_46_bits},
+{2, 6, 6, symbol8_47_bits},
+{3, 6, 6, symbol8_48_bits},
+{3, 6, 6, symbol8_49_bits},
+{3, 6, 6, symbol8_50_bits},
+{3, 6, 6, symbol8_51_bits},
+{4, 6, 6, symbol8_52_bits},
+{3, 6, 6, symbol8_53_bits},
+{3, 6, 6, symbol8_54_bits},
+{3, 6, 6, symbol8_55_bits},
+{3, 6, 6, symbol8_56_bits},
+{3, 6, 6, symbol8_57_bits},
+{1, 4, 4, symbol8_58_bits},
+{2, 6, 4, symbol8_59_bits},
+{5, 5, 5, symbol8_60_bits},
+{4, 3, 4, symbol8_61_bits},
+{5, 5, 5, symbol8_62_bits},
+{3, 6, 6, symbol8_63_bits},
+{4, 6, 6, symbol8_64_bits},
+{5, 6, 6, symbol8_65_bits},
+{4, 6, 6, symbol8_66_bits},
+{5, 6, 6, symbol8_67_bits},
+{5, 6, 6, symbol8_68_bits},
+{4, 6, 6, symbol8_69_bits},
+{5, 6, 6, symbol8_70_bits},
+{4, 6, 6, symbol8_71_bits},
+{5, 6, 6, symbol8_72_bits},
+{2, 6, 6, symbol8_73_bits},
+{5, 6, 6, symbol8_74_bits},
+{5, 6, 6, symbol8_75_bits},
+{6, 6, 6, symbol8_76_bits},
+{7, 6, 6, symbol8_77_bits},
+{5, 6, 6, symbol8_78_bits},
+{5, 6, 6, symbol8_79_bits},
+{6, 6, 6, symbol8_80_bits},
+{5, 6, 6, symbol8_81_bits},
+{4, 6, 6, symbol8_82_bits},
+{4, 6, 6, symbol8_83_bits},
+{4, 6, 6, symbol8_84_bits},
+{5, 6, 6, symbol8_85_bits},
+{3, 6, 4, symbol8_86_bits},
+{5, 6, 6, symbol8_87_bits},
+{5, 6, 6, symbol8_88_bits},
+{7, 6, 6, symbol8_89_bits},
+{4, 6, 6, symbol8_90_bits},
+{2, 8, 6, symbol8_91_bits},
+{4, 4, 4, symbol8_92_bits},
+{2, 8, 6, symbol8_93_bits},
+{4, 6, 6, symbol8_94_bits},
+{4, 1, -1, symbol8_95_bits},
+{5, 1, 8, symbol8_96_bits},
+{4, 4, 4, symbol8_97_bits},
+{3, 9, 7, symbol8_98_bits},
+{4, 6, 4, symbol8_99_bits},
+{3, 7, 7, symbol8_100_bits},
+{3, 4, 4, symbol8_101_bits},
+{4, 8, 6, symbol8_102_bits},
+{4, 6, 4, symbol8_103_bits},
+{4, 6, 4, symbol8_104_bits},
+{2, 4, 4, symbol8_105_bits},
+{4, 6, 4, symbol8_106_bits},
+{4, 4, 4, symbol8_107_bits},
+{5, 7, 7, symbol8_108_bits},
+{4, 6, 4, symbol8_109_bits},
+{4, 4, 4, symbol8_110_bits},
+{3, 4, 4, symbol8_111_bits},
+{4, 4, 4, symbol8_112_bits},
+{3, 6, 6, symbol8_113_bits},
+{3, 6, 4, symbol8_114_bits},
+{4, 4, 4, symbol8_115_bits},
+{3, 4, 4, symbol8_116_bits},
+{4, 4, 4, symbol8_117_bits},
+{5, 5, 5, symbol8_118_bits},
+{5, 4, 4, symbol8_119_bits},
+{3, 9, 7, symbol8_120_bits},
+{5, 6, 4, symbol8_121_bits},
+{3, 9, 7, symbol8_122_bits},
+{3, 9, 7, symbol8_123_bits},
+{1, 8, 6, symbol8_124_bits},
+{3, 9, 7, symbol8_125_bits},
+{4, 2, 3, symbol8_126_bits},
+{5, 1, 1, symbol8_127_bits},
+{5, 1, 1, symbol8_128_bits},
+{5, 1, 1, symbol8_129_bits},
+{5, 1, 1, symbol8_130_bits},
+{5, 1, 1, symbol8_131_bits},
+{5, 1, 1, symbol8_132_bits},
+{5, 1, 1, symbol8_133_bits},
+{5, 1, 1, symbol8_134_bits},
+{5, 1, 1, symbol8_135_bits},
+{5, 1, 1, symbol8_136_bits},
+{5, 1, 1, symbol8_137_bits},
+{5, 1, 1, symbol8_138_bits},
+{5, 1, 1, symbol8_139_bits},
+{5, 1, 1, symbol8_140_bits},
+{5, 1, 1, symbol8_141_bits},
+{5, 1, 1, symbol8_142_bits},
+{5, 1, 1, symbol8_143_bits},
+{5, 1, 1, symbol8_144_bits},
+{5, 1, 1, symbol8_145_bits},
+{5, 1, 1, symbol8_146_bits},
+{5, 1, 1, symbol8_147_bits},
+{5, 1, 1, symbol8_148_bits},
+{5, 1, 1, symbol8_149_bits},
+{5, 1, 1, symbol8_150_bits},
+{5, 1, 1, symbol8_151_bits},
+{5, 1, 1, symbol8_152_bits},
+{5, 1, 1, symbol8_153_bits},
+{5, 1, 1, symbol8_154_bits},
+{5, 1, 1, symbol8_155_bits},
+{5, 1, 1, symbol8_156_bits},
+{5, 1, 1, symbol8_157_bits},
+{5, 1, 1, symbol8_158_bits},
+{5, 1, 1, symbol8_159_bits},
+{5, 1, 1, symbol8_160_bits},
+{5, 6, 6, symbol8_161_bits},
+{2, 3, 7, symbol8_162_bits},
+{4, 6, 6, symbol8_163_bits},
+{4, 7, 7, symbol8_164_bits},
+{5, 3, 4, symbol8_165_bits},
+{4, 8, 6, symbol8_166_bits},
+{4, 6, 5, symbol8_167_bits},
+{5, 6, 5, symbol8_168_bits},
+{5, 6, 5, symbol8_169_bits},
+{4, 6, 6, symbol8_170_bits},
+{8, 5, 5, symbol8_171_bits},
+{8, 5, 5, symbol8_172_bits},
+{5, 11, 8, symbol8_173_bits},
+{8, 5, 5, symbol8_174_bits},
+{5, 10, 8, symbol8_175_bits},
+{3, 3, 6, symbol8_176_bits},
+{5, 6, 6, symbol8_177_bits},
+{4, 3, 7, symbol8_178_bits},
+{4, 6, 6, symbol8_179_bits},
+{4, 4, 4, symbol8_180_bits},
+{5, 3, 4, symbol8_181_bits},
+{3, 7, 7, symbol8_182_bits},
+{3, 3, 4, symbol8_183_bits},
+{4, 5, 5, symbol8_184_bits},
+{4, 7, 5, symbol8_185_bits},
+{4, 5, 5, symbol8_186_bits},
+{4, 5, 5, symbol8_187_bits},
+{5, 1, 1, symbol8_188_bits},
+{1, 11, 8, symbol8_189_bits},
+{8, 1, 3, symbol8_190_bits},
+{5, 5, 5, symbol8_191_bits},
+{5, 6, 6, symbol8_192_bits},
+{5, 7, 7, symbol8_193_bits},
+{6, 7, 7, symbol8_194_bits},
+{5, 7, 5, symbol8_195_bits},
+{5, 6, 6, symbol8_196_bits},
+{5, 6, 6, symbol8_197_bits},
+{5, 8, 7, symbol8_198_bits},
+{5, 4, 4, symbol8_199_bits},
+{5, 4, 4, symbol8_200_bits},
+{5, 4, 4, symbol8_201_bits},
+{5, 6, 4, symbol8_202_bits},
+{5, 6, 5, symbol8_203_bits},
+{5, 4, 4, symbol8_204_bits},
+{5, 6, 4, symbol8_205_bits},
+{4, 4, 4, symbol8_206_bits},
+{4, 6, 5, symbol8_207_bits},
+{5, 5, 5, symbol8_208_bits},
+{5, 6, 6, symbol8_209_bits},
+{5, 7, 6, symbol8_210_bits},
+{5, 7, 6, symbol8_211_bits},
+{8, 4, 6, symbol8_212_bits},
+{6, 8, 7, symbol8_213_bits},
+{5, 8, 8, symbol8_214_bits},
+{1, 1, 3, symbol8_215_bits},
+{5, 3, 3, symbol8_216_bits},
+{4, 4, 4, symbol8_217_bits},
+{4, 4, 4, symbol8_218_bits},
+{8, 4, 4, symbol8_219_bits},
+{7, 4, 4, symbol8_220_bits},
+{4, 8, 8, symbol8_221_bits},
+{7, 4, 4, symbol8_222_bits},
+{4, 8, 8, symbol8_223_bits},
+{5, 6, 6, symbol8_224_bits},
+{2, 8, 6, symbol8_225_bits},
+{5, 7, 6, symbol8_226_bits},
+{5, 7, 6, symbol8_227_bits},
+{6, 4, 6, symbol8_228_bits},
+{5, 8, 7, symbol8_229_bits},
+{2, 11, 8, symbol8_230_bits},
+{1, 11, 8, symbol8_231_bits},
+{2, 11, 8, symbol8_232_bits},
+{3, 11, 8, symbol8_233_bits},
+{1, 11, 8, symbol8_234_bits},
+{2, 11, 8, symbol8_235_bits},
+{2, 11, 8, symbol8_236_bits},
+{2, 11, 8, symbol8_237_bits},
+{2, 11, 8, symbol8_238_bits},
+{1, 11, 8, symbol8_239_bits},
+{5, 1, 1, symbol8_240_bits},
+{2, 8, 6, symbol8_241_bits},
+{3, 11, 8, symbol8_242_bits},
+{3, 11, 8, symbol8_243_bits},
+{1, 11, 8, symbol8_244_bits},
+{3, 11, 8, symbol8_245_bits},
+{2, 11, 8, symbol8_246_bits},
+{1, 11, 8, symbol8_247_bits},
+{2, 11, 8, symbol8_248_bits},
+{2, 11, 8, symbol8_249_bits},
+{1, 11, 8, symbol8_250_bits},
+{2, 11, 8, symbol8_251_bits},
+{2, 11, 8, symbol8_252_bits},
+{2, 11, 8, symbol8_253_bits},
+{2, 11, 8, symbol8_254_bits},
+{5, 1, 1, symbol8_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times12.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times12.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times12.bdf	(revision 22322)
@@ -0,0 +1,2973 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Times-Medium-R-Normal--12-120-75-75-P-64-ISO8859-1
+SIZE 12 75 75
+FONTBOUNDINGBOX 12 15 0 -3
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Times"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 12
+POINT_SIZE 120
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 64
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 9
+X_HEIGHT 6
+FACE_NAME "Times Roman"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "Times is a trademark of Linotype-Hell AG and/or its subsidiaries."
+_DEC_DEVICE_FONTNAMES "PS=Times-Roman"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2, 07-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Times Roman"
+FONT "-Adobe-Times-Medium-R-Normal--12-120-75-75-P-64-ISO8859-1"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 7
+DEFAULT_CHAR 32
+FONT_ASCENT 11
+FONT_DESCENT 3
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 9 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+00
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 3 1 6
+BITMAP
+a0
+a0
+a0
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+50
+50
+f8
+50
+50
+f8
+50
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 11 0 -1
+BITMAP
+20
+70
+a8
+a0
+60
+30
+28
+28
+a8
+70
+20
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 8 9 1 0
+BITMAP
+63
+9e
+94
+68
+10
+36
+29
+49
+46
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+1800
+2400
+2400
+1b80
+7900
+ce00
+8400
+ce80
+7b00
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 2 3 1 6
+BITMAP
+c0
+40
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 12 1 -3
+BITMAP
+20
+40
+40
+80
+80
+80
+80
+80
+80
+40
+40
+20
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 12 0 -3
+BITMAP
+80
+40
+40
+20
+20
+20
+20
+20
+20
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 5 0 4
+BITMAP
+20
+a8
+70
+a8
+20
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+20
+20
+f8
+20
+20
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 3 0 -2
+BITMAP
+40
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 1 1 3
+BITMAP
+f8
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 1 1 0
+BITMAP
+80
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 9 0 0
+BITMAP
+20
+20
+20
+40
+40
+40
+80
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+d8
+88
+88
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 9 0 0
+BITMAP
+20
+60
+a0
+20
+20
+20
+20
+20
+70
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+88
+08
+08
+10
+20
+40
+88
+f8
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+88
+08
+10
+70
+08
+08
+88
+70
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+10
+30
+30
+50
+50
+90
+f8
+10
+10
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+38
+40
+40
+70
+18
+08
+08
+98
+70
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+18
+60
+40
+f0
+98
+88
+88
+c8
+70
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+f8
+88
+10
+10
+20
+20
+40
+40
+40
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+88
+88
+c8
+70
+98
+88
+88
+70
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+70
+98
+88
+88
+c8
+78
+10
+30
+c0
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 6 1 0
+BITMAP
+80
+00
+00
+00
+00
+80
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 8 0 -2
+BITMAP
+40
+00
+00
+00
+00
+40
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 0 1
+BITMAP
+0c
+30
+c0
+30
+0c
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 3 1 2
+BITMAP
+f8
+00
+f8
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 5 1 1
+BITMAP
+c0
+30
+0c
+30
+c0
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 9 0 0
+BITMAP
+60
+90
+10
+20
+40
+40
+40
+00
+40
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 10 11 1 -2
+BITMAP
+0f00
+3080
+6040
+4d40
+9240
+a240
+a480
+a480
+9b00
+4000
+3e00
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+0800
+0800
+1400
+1400
+2200
+3e00
+2200
+4100
+e380
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+fc
+46
+42
+46
+7c
+42
+42
+46
+fc
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+3a
+66
+42
+80
+80
+80
+42
+66
+3c
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 1 0
+BITMAP
+fc
+46
+42
+41
+41
+41
+42
+46
+fc
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+fe
+42
+40
+44
+7c
+44
+40
+42
+fe
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+BITMAP
+fe
+42
+40
+44
+7c
+44
+40
+40
+f0
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+3a
+66
+42
+80
+87
+82
+42
+66
+3c
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+e7
+42
+42
+42
+7e
+42
+42
+42
+e7
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 9 0 0
+BITMAP
+e0
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 9 1 0
+BITMAP
+70
+20
+20
+20
+20
+20
+20
+a0
+c0
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+ee
+44
+48
+50
+70
+58
+4c
+46
+e7
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 0 0
+BITMAP
+e0
+40
+40
+40
+40
+40
+40
+44
+fc
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 11 9 0 0
+BITMAP
+c060
+60c0
+60c0
+5140
+5140
+4a40
+4a40
+4440
+e4e0
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+c380
+6100
+6100
+5100
+4900
+4900
+4500
+4300
+e100
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+3c
+66
+42
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+BITMAP
+fc
+46
+42
+46
+7c
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 11 0 -2
+BITMAP
+3c
+66
+42
+81
+81
+81
+42
+66
+3c
+0c
+03
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 8 9 0 0
+BITMAP
+fc
+46
+42
+46
+7c
+48
+44
+42
+e3
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 9 0 0
+BITMAP
+74
+8c
+84
+60
+38
+0c
+84
+cc
+b8
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+BITMAP
+fe
+92
+10
+10
+10
+10
+10
+10
+38
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 1 0
+BITMAP
+e7
+42
+42
+42
+42
+42
+42
+66
+3c
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+e380
+4100
+2200
+2200
+1400
+1400
+1c00
+0800
+0800
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 960 0
+DWIDTH 12 0
+BBX 12 9 0 0
+BITMAP
+ee70
+4420
+6620
+2240
+3740
+1540
+1980
+0880
+0880
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 1 0
+BITMAP
+e7
+42
+24
+38
+18
+2c
+24
+42
+e7
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+e380
+4100
+2200
+1200
+1c00
+0800
+0800
+0800
+1c00
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+fe
+86
+0c
+18
+30
+60
+c0
+82
+fe
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 12 1 -3
+BITMAP
+e0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+e0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 9 0 0
+BITMAP
+80
+80
+80
+40
+40
+40
+20
+20
+20
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 12 0 -3
+BITMAP
+e0
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+e0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 5 0 4
+BITMAP
+20
+50
+50
+88
+88
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 1 0 -3
+BITMAP
+fc
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 2 3 1 6
+BITMAP
+40
+80
+c0
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+60
+90
+70
+90
+90
+68
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+c0
+40
+40
+70
+48
+48
+48
+48
+70
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 6 1 0
+BITMAP
+60
+90
+80
+80
+90
+60
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+30
+10
+10
+70
+90
+90
+90
+90
+68
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+60
+90
+f0
+80
+c8
+70
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 9 0 0
+BITMAP
+20
+40
+40
+e0
+40
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 -3
+BITMAP
+78
+90
+90
+e0
+40
+70
+88
+88
+70
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 0
+BITMAP
+c0
+40
+40
+70
+48
+48
+48
+48
+ec
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 9 0 0
+BITMAP
+40
+00
+00
+c0
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 2 12 0 -3
+BITMAP
+40
+00
+00
+c0
+40
+40
+40
+40
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 0
+BITMAP
+c0
+40
+40
+48
+50
+60
+50
+48
+4c
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 9 0 0
+BITMAP
+c0
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 6 0 0
+BITMAP
+b600
+4900
+4900
+4900
+4900
+ed80
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 6 0 0
+BITMAP
+b0
+48
+48
+48
+48
+ec
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 6 1 0
+BITMAP
+60
+90
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 -3
+BITMAP
+f0
+48
+48
+48
+48
+70
+40
+40
+e0
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 -3
+BITMAP
+70
+90
+90
+90
+90
+70
+10
+10
+38
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 6 0 0
+BITMAP
+b0
+60
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 6 1 0
+BITMAP
+70
+90
+c0
+30
+90
+e0
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 7 0 0
+BITMAP
+40
+f0
+40
+40
+40
+40
+30
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 6 0 0
+BITMAP
+d8
+48
+48
+48
+48
+34
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 6 0 0
+BITMAP
+cc
+48
+58
+50
+30
+20
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 6 0 0
+BITMAP
+ed80
+4900
+6b00
+2a00
+3400
+2400
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 6 1 0
+BITMAP
+d8
+50
+20
+20
+50
+d8
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 -3
+BITMAP
+cc
+48
+58
+50
+30
+20
+20
+40
+c0
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 6 1 0
+BITMAP
+f0
+90
+20
+40
+90
+f0
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 12 1 -3
+BITMAP
+30
+40
+40
+40
+40
+80
+40
+40
+40
+40
+40
+30
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 9 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 12 0 -3
+BITMAP
+c0
+20
+20
+20
+20
+10
+20
+20
+20
+20
+20
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 2 0 3
+BITMAP
+64
+98
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 1 9 1 -3
+BITMAP
+80
+00
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 8 1 -1
+BITMAP
+08
+70
+98
+a0
+a0
+c8
+70
+80
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+30
+48
+40
+40
+f0
+40
+40
+e8
+d8
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 7 1 1
+BITMAP
+84
+78
+48
+48
+48
+78
+84
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 0 0
+BITMAP
+88
+88
+50
+50
+f8
+20
+f8
+20
+70
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 9 1 0
+BITMAP
+80
+80
+80
+00
+00
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 12 1 -3
+BITMAP
+70
+90
+c0
+60
+b0
+90
+90
+d0
+60
+30
+90
+e0
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 1 0 7
+BITMAP
+a0
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 800 0
+DWIDTH 10 0
+BBX 9 9 1 0
+BITMAP
+1c00
+6300
+5d00
+a480
+a080
+a480
+5d00
+6300
+1c00
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 6 1 3
+BITMAP
+c0
+20
+e0
+a0
+00
+e0
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 0
+BITMAP
+28
+50
+a0
+50
+28
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 6 4 1 1
+BITMAP
+fc
+04
+04
+04
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 1 0 3
+BITMAP
+f0
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+1c00
+6300
+5d00
+9280
+9c80
+9480
+d500
+6300
+3c00
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 1 0 7
+BITMAP
+f0
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 4 0 5
+BITMAP
+60
+90
+90
+60
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+20
+20
+f8
+20
+20
+00
+f8
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 4 5 0 4
+BITMAP
+60
+90
+20
+40
+f0
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 5 0 4
+BITMAP
+e0
+20
+40
+20
+c0
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 2 2 1 7
+BITMAP
+40
+80
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 -3
+BITMAP
+d8
+48
+48
+48
+48
+74
+40
+40
+60
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 6 12 0 -3
+BITMAP
+7c
+e8
+e8
+e8
+e8
+68
+28
+28
+28
+28
+28
+28
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 1 1 1 3
+BITMAP
+80
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 3 0 -3
+BITMAP
+40
+20
+e0
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 5 0 4
+BITMAP
+40
+c0
+40
+40
+e0
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 3 6 1 3
+BITMAP
+40
+a0
+a0
+40
+00
+e0
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 0 0
+BITMAP
+a0
+50
+28
+50
+a0
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+4200
+c400
+4400
+4800
+e900
+1300
+1500
+2780
+2100
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+4200
+c400
+4400
+4800
+eb00
+1480
+1100
+2200
+2780
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+e200
+2400
+4400
+2800
+c900
+1300
+1500
+2780
+2100
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 9 0 -3
+BITMAP
+20
+00
+20
+20
+20
+40
+80
+90
+60
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+1000
+0800
+0000
+0800
+0800
+1400
+1400
+2200
+3e00
+2200
+4100
+e380
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+0400
+0800
+0000
+0800
+0800
+1400
+1400
+2200
+3e00
+2200
+4100
+e380
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+0800
+1400
+0000
+0800
+0800
+1400
+1400
+2200
+3e00
+2200
+4100
+e380
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+0a00
+1400
+0000
+0800
+0800
+1400
+1400
+2200
+3e00
+2200
+4100
+e380
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 11 0 0
+BITMAP
+1400
+0000
+0800
+0800
+1400
+1400
+2200
+3e00
+2200
+4100
+e380
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+0800
+1400
+0800
+0800
+0800
+1400
+1400
+2200
+3e00
+2200
+4100
+e380
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 880 0
+DWIDTH 11 0
+BBX 10 9 0 0
+BITMAP
+3fc0
+1840
+2800
+2880
+4f80
+7880
+4800
+8840
+cfc0
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 12 0 -3
+BITMAP
+3a
+66
+42
+80
+80
+80
+42
+66
+3c
+10
+08
+38
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 12 0 0
+BITMAP
+20
+10
+00
+fe
+42
+40
+44
+7c
+44
+40
+42
+fe
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 12 0 0
+BITMAP
+08
+10
+00
+fe
+42
+40
+44
+7c
+44
+40
+42
+fe
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 12 0 0
+BITMAP
+10
+28
+00
+fe
+42
+40
+44
+7c
+44
+40
+42
+fe
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 7 11 0 0
+BITMAP
+28
+00
+fe
+42
+40
+44
+7c
+44
+40
+42
+fe
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 12 0 0
+BITMAP
+80
+40
+00
+e0
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 12 0 0
+BITMAP
+20
+40
+00
+e0
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 12 0 0
+BITMAP
+40
+a0
+00
+e0
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 320 0
+DWIDTH 4 0
+BBX 3 11 0 0
+BITMAP
+a0
+00
+e0
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+fc
+46
+42
+41
+e1
+41
+42
+46
+fc
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+0a00
+1400
+0000
+c380
+6100
+6100
+5100
+4900
+4900
+4500
+4300
+e100
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+20
+10
+00
+3c
+66
+42
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+04
+08
+00
+3c
+66
+42
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+10
+28
+00
+3c
+66
+42
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+14
+28
+00
+3c
+66
+42
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 11 0 0
+BITMAP
+24
+00
+3c
+66
+42
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+88
+50
+20
+50
+88
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 10 0 0
+BITMAP
+0080
+3d00
+6600
+4600
+8900
+8900
+9100
+6200
+6600
+bc00
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 8 12 0 0
+BITMAP
+10
+08
+00
+e7
+42
+42
+42
+42
+42
+42
+66
+3c
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 8 12 0 0
+BITMAP
+04
+08
+00
+e7
+42
+42
+42
+42
+42
+42
+66
+3c
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 8 12 0 0
+BITMAP
+10
+28
+00
+e7
+42
+42
+42
+42
+42
+42
+66
+3c
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 640 0
+DWIDTH 8 0
+BBX 8 11 0 0
+BITMAP
+24
+00
+e7
+42
+42
+42
+42
+42
+42
+66
+3c
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+0400
+0800
+0000
+e380
+4100
+2200
+1200
+1c00
+0800
+0800
+0800
+1c00
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 7 9 0 0
+BITMAP
+e0
+40
+7c
+46
+42
+46
+7c
+40
+e0
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 0
+BITMAP
+30
+48
+48
+50
+70
+48
+44
+44
+d8
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+40
+20
+00
+60
+90
+70
+90
+90
+68
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+10
+20
+00
+60
+90
+70
+90
+90
+68
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+20
+50
+00
+60
+90
+70
+90
+90
+68
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+50
+a0
+00
+60
+90
+70
+90
+90
+68
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 8 1 0
+BITMAP
+50
+00
+60
+90
+70
+90
+90
+68
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+20
+50
+20
+60
+90
+70
+90
+90
+68
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 720 0
+DWIDTH 9 0
+BBX 8 6 1 0
+BITMAP
+6c
+92
+7e
+90
+99
+6e
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 400 0
+DWIDTH 5 0
+BBX 4 9 1 -3
+BITMAP
+60
+90
+80
+80
+90
+60
+40
+20
+e0
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+40
+20
+00
+60
+90
+f0
+80
+c8
+70
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+20
+40
+00
+60
+90
+f0
+80
+c8
+70
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 9 1 0
+BITMAP
+20
+50
+00
+60
+90
+f0
+80
+c8
+70
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 8 1 0
+BITMAP
+50
+00
+60
+90
+f0
+80
+c8
+70
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 9 0 0
+BITMAP
+80
+40
+00
+c0
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 9 0 0
+BITMAP
+40
+80
+00
+c0
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 9 0 0
+BITMAP
+40
+a0
+00
+c0
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 240 0
+DWIDTH 3 0
+BBX 3 8 0 0
+BITMAP
+a0
+00
+c0
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 9 1 0
+BITMAP
+40
+70
+a0
+70
+90
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 0
+BITMAP
+28
+50
+00
+b0
+48
+48
+48
+48
+ec
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 9 1 0
+BITMAP
+40
+20
+00
+60
+90
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 9 1 0
+BITMAP
+20
+40
+00
+60
+90
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 9 1 0
+BITMAP
+20
+50
+00
+60
+90
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 9 1 0
+BITMAP
+50
+a0
+00
+60
+90
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 4 8 1 0
+BITMAP
+a0
+00
+60
+90
+90
+90
+90
+60
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 560 0
+DWIDTH 7 0
+BBX 5 5 1 1
+BITMAP
+20
+00
+f8
+00
+20
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 7 0 -1
+BITMAP
+34
+48
+48
+48
+48
+70
+80
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 0
+BITMAP
+20
+10
+00
+d8
+48
+48
+48
+48
+34
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 0
+BITMAP
+10
+20
+00
+d8
+48
+48
+48
+48
+34
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 9 0 0
+BITMAP
+20
+50
+00
+d8
+48
+48
+48
+48
+34
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 8 0 0
+BITMAP
+50
+00
+d8
+48
+48
+48
+48
+34
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 12 0 -3
+BITMAP
+08
+10
+00
+cc
+48
+58
+50
+30
+20
+20
+40
+c0
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 5 12 0 -3
+BITMAP
+c0
+40
+40
+70
+48
+48
+48
+48
+70
+40
+40
+e0
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 480 0
+DWIDTH 6 0
+BBX 6 11 0 -3
+BITMAP
+48
+00
+cc
+48
+58
+50
+30
+20
+20
+40
+c0
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times12.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times12.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times12.h	(revision 22322)
@@ -0,0 +1,792 @@
+static unsigned char times12_0_bits[] = {
+0x00};
+static unsigned char times12_1_bits[] = {
+0x00};
+static unsigned char times12_2_bits[] = {
+0x00};
+static unsigned char times12_3_bits[] = {
+0x00};
+static unsigned char times12_4_bits[] = {
+0x00};
+static unsigned char times12_5_bits[] = {
+0x00};
+static unsigned char times12_6_bits[] = {
+0x00};
+static unsigned char times12_7_bits[] = {
+0x00};
+static unsigned char times12_8_bits[] = {
+0x00};
+static unsigned char times12_9_bits[] = {
+0x00};
+static unsigned char times12_10_bits[] = {
+0x00};
+static unsigned char times12_11_bits[] = {
+0x00};
+static unsigned char times12_12_bits[] = {
+0x00};
+static unsigned char times12_13_bits[] = {
+0x00};
+static unsigned char times12_14_bits[] = {
+0x00};
+static unsigned char times12_15_bits[] = {
+0x00};
+static unsigned char times12_16_bits[] = {
+0x00};
+static unsigned char times12_17_bits[] = {
+0x00};
+static unsigned char times12_18_bits[] = {
+0x00};
+static unsigned char times12_19_bits[] = {
+0x00};
+static unsigned char times12_20_bits[] = {
+0x00};
+static unsigned char times12_21_bits[] = {
+0x00};
+static unsigned char times12_22_bits[] = {
+0x00};
+static unsigned char times12_23_bits[] = {
+0x00};
+static unsigned char times12_24_bits[] = {
+0x00};
+static unsigned char times12_25_bits[] = {
+0x00};
+static unsigned char times12_26_bits[] = {
+0x00};
+static unsigned char times12_27_bits[] = {
+0x00};
+static unsigned char times12_28_bits[] = {
+0x00};
+static unsigned char times12_29_bits[] = {
+0x00};
+static unsigned char times12_30_bits[] = {
+0x00};
+static unsigned char times12_31_bits[] = {
+0x00};
+static unsigned char times12_32_bits[] = {
+0x00};
+static unsigned char times12_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01};
+static unsigned char times12_34_bits[] = {
+0x05, 0x05, 0x05};
+static unsigned char times12_35_bits[] = {
+0x0a, 0x0a, 0x1f, 0x0a, 0x0a, 0x1f, 0x0a, 0x0a};
+static unsigned char times12_36_bits[] = {
+0x04, 0x0e, 0x15, 0x05, 0x06, 0x0c, 0x14, 0x14, 0x15, 0x0e, 0x04};
+static unsigned char times12_37_bits[] = {
+0xc6, 0x79, 0x29, 0x16, 0x08, 0x6c, 0x94, 0x92, 0x62};
+static unsigned char times12_38_bits[] = {
+0x18, 0x00, 0x24, 0x00, 0x24, 0x00, 0xd8, 0x01, 0x9e, 0x00, 0x73, 0x00, 
+0x21, 0x00, 0x73, 0x01, 0xde, 0x00};
+static unsigned char times12_39_bits[] = {
+0x03, 0x02, 0x01};
+static unsigned char times12_40_bits[] = {
+0x04, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x04};
+static unsigned char times12_41_bits[] = {
+0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x01};
+static unsigned char times12_42_bits[] = {
+0x04, 0x15, 0x0e, 0x15, 0x04};
+static unsigned char times12_43_bits[] = {
+0x04, 0x04, 0x1f, 0x04, 0x04};
+static unsigned char times12_44_bits[] = {
+0x02, 0x02, 0x01};
+static unsigned char times12_45_bits[] = {
+0x1f};
+static unsigned char times12_46_bits[] = {
+0x01};
+static unsigned char times12_47_bits[] = {
+0x04, 0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01};
+static unsigned char times12_48_bits[] = {
+0x0e, 0x1b, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char times12_49_bits[] = {
+0x04, 0x06, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e};
+static unsigned char times12_50_bits[] = {
+0x0e, 0x11, 0x10, 0x10, 0x08, 0x04, 0x02, 0x11, 0x1f};
+static unsigned char times12_51_bits[] = {
+0x0e, 0x11, 0x10, 0x08, 0x0e, 0x10, 0x10, 0x11, 0x0e};
+static unsigned char times12_52_bits[] = {
+0x08, 0x0c, 0x0c, 0x0a, 0x0a, 0x09, 0x1f, 0x08, 0x08};
+static unsigned char times12_53_bits[] = {
+0x1c, 0x02, 0x02, 0x0e, 0x18, 0x10, 0x10, 0x19, 0x0e};
+static unsigned char times12_54_bits[] = {
+0x18, 0x06, 0x02, 0x0f, 0x19, 0x11, 0x11, 0x13, 0x0e};
+static unsigned char times12_55_bits[] = {
+0x1f, 0x11, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x02};
+static unsigned char times12_56_bits[] = {
+0x0e, 0x11, 0x11, 0x13, 0x0e, 0x19, 0x11, 0x11, 0x0e};
+static unsigned char times12_57_bits[] = {
+0x0e, 0x19, 0x11, 0x11, 0x13, 0x1e, 0x08, 0x0c, 0x03};
+static unsigned char times12_58_bits[] = {
+0x01, 0x00, 0x00, 0x00, 0x00, 0x01};
+static unsigned char times12_59_bits[] = {
+0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01};
+static unsigned char times12_60_bits[] = {
+0x30, 0x0c, 0x03, 0x0c, 0x30};
+static unsigned char times12_61_bits[] = {
+0x1f, 0x00, 0x1f};
+static unsigned char times12_62_bits[] = {
+0x03, 0x0c, 0x30, 0x0c, 0x03};
+static unsigned char times12_63_bits[] = {
+0x06, 0x09, 0x08, 0x04, 0x02, 0x02, 0x02, 0x00, 0x02};
+static unsigned char times12_64_bits[] = {
+0xf0, 0x00, 0x0c, 0x01, 0x06, 0x02, 0xb2, 0x02, 0x49, 0x02, 0x45, 0x02, 
+0x25, 0x01, 0x25, 0x01, 0xd9, 0x00, 0x02, 0x00, 0x7c, 0x00};
+static unsigned char times12_65_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 
+0x44, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char times12_66_bits[] = {
+0x3f, 0x62, 0x42, 0x62, 0x3e, 0x42, 0x42, 0x62, 0x3f};
+static unsigned char times12_67_bits[] = {
+0x5c, 0x66, 0x42, 0x01, 0x01, 0x01, 0x42, 0x66, 0x3c};
+static unsigned char times12_68_bits[] = {
+0x3f, 0x62, 0x42, 0x82, 0x82, 0x82, 0x42, 0x62, 0x3f};
+static unsigned char times12_69_bits[] = {
+0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 0x7f};
+static unsigned char times12_70_bits[] = {
+0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x02, 0x0f};
+static unsigned char times12_71_bits[] = {
+0x5c, 0x66, 0x42, 0x01, 0xe1, 0x41, 0x42, 0x66, 0x3c};
+static unsigned char times12_72_bits[] = {
+0xe7, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0xe7};
+static unsigned char times12_73_bits[] = {
+0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_74_bits[] = {
+0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x03};
+static unsigned char times12_75_bits[] = {
+0x77, 0x22, 0x12, 0x0a, 0x0e, 0x1a, 0x32, 0x62, 0xe7};
+static unsigned char times12_76_bits[] = {
+0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x22, 0x3f};
+static unsigned char times12_77_bits[] = {
+0x03, 0x06, 0x06, 0x03, 0x06, 0x03, 0x8a, 0x02, 0x8a, 0x02, 0x52, 0x02, 
+0x52, 0x02, 0x22, 0x02, 0x27, 0x07};
+static unsigned char times12_78_bits[] = {
+0xc3, 0x01, 0x86, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x92, 0x00, 0x92, 0x00, 
+0xa2, 0x00, 0xc2, 0x00, 0x87, 0x00};
+static unsigned char times12_79_bits[] = {
+0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c};
+static unsigned char times12_80_bits[] = {
+0x3f, 0x62, 0x42, 0x62, 0x3e, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_81_bits[] = {
+0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c, 0x30, 0xc0};
+static unsigned char times12_82_bits[] = {
+0x3f, 0x62, 0x42, 0x62, 0x3e, 0x12, 0x22, 0x42, 0xc7};
+static unsigned char times12_83_bits[] = {
+0x2e, 0x31, 0x21, 0x06, 0x1c, 0x30, 0x21, 0x33, 0x1d};
+static unsigned char times12_84_bits[] = {
+0x7f, 0x49, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c};
+static unsigned char times12_85_bits[] = {
+0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x66, 0x3c};
+static unsigned char times12_86_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x28, 0x00, 
+0x38, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char times12_87_bits[] = {
+0x77, 0x0e, 0x22, 0x04, 0x66, 0x04, 0x44, 0x02, 0xec, 0x02, 0xa8, 0x02, 
+0x98, 0x01, 0x10, 0x01, 0x10, 0x01};
+static unsigned char times12_88_bits[] = {
+0xe7, 0x42, 0x24, 0x1c, 0x18, 0x34, 0x24, 0x42, 0xe7};
+static unsigned char times12_89_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 0x48, 0x00, 0x38, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x38, 0x00};
+static unsigned char times12_90_bits[] = {
+0x7f, 0x61, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x41, 0x7f};
+static unsigned char times12_91_bits[] = {
+0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07};
+static unsigned char times12_92_bits[] = {
+0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04};
+static unsigned char times12_93_bits[] = {
+0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x07};
+static unsigned char times12_94_bits[] = {
+0x04, 0x0a, 0x0a, 0x11, 0x11};
+static unsigned char times12_95_bits[] = {
+0x3f};
+static unsigned char times12_96_bits[] = {
+0x02, 0x01, 0x03};
+static unsigned char times12_97_bits[] = {
+0x06, 0x09, 0x0e, 0x09, 0x09, 0x16};
+static unsigned char times12_98_bits[] = {
+0x03, 0x02, 0x02, 0x0e, 0x12, 0x12, 0x12, 0x12, 0x0e};
+static unsigned char times12_99_bits[] = {
+0x06, 0x09, 0x01, 0x01, 0x09, 0x06};
+static unsigned char times12_100_bits[] = {
+0x0c, 0x08, 0x08, 0x0e, 0x09, 0x09, 0x09, 0x09, 0x16};
+static unsigned char times12_101_bits[] = {
+0x06, 0x09, 0x0f, 0x01, 0x13, 0x0e};
+static unsigned char times12_102_bits[] = {
+0x04, 0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02};
+static unsigned char times12_103_bits[] = {
+0x1e, 0x09, 0x09, 0x07, 0x02, 0x0e, 0x11, 0x11, 0x0e};
+static unsigned char times12_104_bits[] = {
+0x03, 0x02, 0x02, 0x0e, 0x12, 0x12, 0x12, 0x12, 0x37};
+static unsigned char times12_105_bits[] = {
+0x02, 0x00, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_106_bits[] = {
+0x02, 0x00, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01};
+static unsigned char times12_107_bits[] = {
+0x03, 0x02, 0x02, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x32};
+static unsigned char times12_108_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_109_bits[] = {
+0x6d, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0xb7, 0x01};
+static unsigned char times12_110_bits[] = {
+0x0d, 0x12, 0x12, 0x12, 0x12, 0x37};
+static unsigned char times12_111_bits[] = {
+0x06, 0x09, 0x09, 0x09, 0x09, 0x06};
+static unsigned char times12_112_bits[] = {
+0x0f, 0x12, 0x12, 0x12, 0x12, 0x0e, 0x02, 0x02, 0x07};
+static unsigned char times12_113_bits[] = {
+0x0e, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x08, 0x08, 0x1c};
+static unsigned char times12_114_bits[] = {
+0x0d, 0x06, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_115_bits[] = {
+0x0e, 0x09, 0x03, 0x0c, 0x09, 0x07};
+static unsigned char times12_116_bits[] = {
+0x02, 0x0f, 0x02, 0x02, 0x02, 0x02, 0x0c};
+static unsigned char times12_117_bits[] = {
+0x1b, 0x12, 0x12, 0x12, 0x12, 0x2c};
+static unsigned char times12_118_bits[] = {
+0x33, 0x12, 0x1a, 0x0a, 0x0c, 0x04};
+static unsigned char times12_119_bits[] = {
+0xb7, 0x01, 0x92, 0x00, 0xd6, 0x00, 0x54, 0x00, 0x2c, 0x00, 0x24, 0x00};
+static unsigned char times12_120_bits[] = {
+0x1b, 0x0a, 0x04, 0x04, 0x0a, 0x1b};
+static unsigned char times12_121_bits[] = {
+0x33, 0x12, 0x1a, 0x0a, 0x0c, 0x04, 0x04, 0x02, 0x03};
+static unsigned char times12_122_bits[] = {
+0x0f, 0x09, 0x04, 0x02, 0x09, 0x0f};
+static unsigned char times12_123_bits[] = {
+0x0c, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0c};
+static unsigned char times12_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times12_125_bits[] = {
+0x03, 0x04, 0x04, 0x04, 0x04, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03};
+static unsigned char times12_126_bits[] = {
+0x26, 0x19};
+static unsigned char times12_127_bits[] = {
+0x00};
+static unsigned char times12_128_bits[] = {
+0x00};
+static unsigned char times12_129_bits[] = {
+0x00};
+static unsigned char times12_130_bits[] = {
+0x00};
+static unsigned char times12_131_bits[] = {
+0x00};
+static unsigned char times12_132_bits[] = {
+0x00};
+static unsigned char times12_133_bits[] = {
+0x00};
+static unsigned char times12_134_bits[] = {
+0x00};
+static unsigned char times12_135_bits[] = {
+0x00};
+static unsigned char times12_136_bits[] = {
+0x00};
+static unsigned char times12_137_bits[] = {
+0x00};
+static unsigned char times12_138_bits[] = {
+0x00};
+static unsigned char times12_139_bits[] = {
+0x00};
+static unsigned char times12_140_bits[] = {
+0x00};
+static unsigned char times12_141_bits[] = {
+0x00};
+static unsigned char times12_142_bits[] = {
+0x00};
+static unsigned char times12_143_bits[] = {
+0x00};
+static unsigned char times12_144_bits[] = {
+0x00};
+static unsigned char times12_145_bits[] = {
+0x00};
+static unsigned char times12_146_bits[] = {
+0x00};
+static unsigned char times12_147_bits[] = {
+0x00};
+static unsigned char times12_148_bits[] = {
+0x00};
+static unsigned char times12_149_bits[] = {
+0x00};
+static unsigned char times12_150_bits[] = {
+0x00};
+static unsigned char times12_151_bits[] = {
+0x00};
+static unsigned char times12_152_bits[] = {
+0x00};
+static unsigned char times12_153_bits[] = {
+0x00};
+static unsigned char times12_154_bits[] = {
+0x00};
+static unsigned char times12_155_bits[] = {
+0x00};
+static unsigned char times12_156_bits[] = {
+0x00};
+static unsigned char times12_157_bits[] = {
+0x00};
+static unsigned char times12_158_bits[] = {
+0x00};
+static unsigned char times12_159_bits[] = {
+0x00};
+static unsigned char times12_160_bits[] = {
+0x00};
+static unsigned char times12_161_bits[] = {
+0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times12_162_bits[] = {
+0x10, 0x0e, 0x19, 0x05, 0x05, 0x13, 0x0e, 0x01};
+static unsigned char times12_163_bits[] = {
+0x0c, 0x12, 0x02, 0x02, 0x0f, 0x02, 0x02, 0x17, 0x1b};
+static unsigned char times12_164_bits[] = {
+0x21, 0x1e, 0x12, 0x12, 0x12, 0x1e, 0x21};
+static unsigned char times12_165_bits[] = {
+0x11, 0x11, 0x0a, 0x0a, 0x1f, 0x04, 0x1f, 0x04, 0x0e};
+static unsigned char times12_166_bits[] = {
+0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times12_167_bits[] = {
+0x0e, 0x09, 0x03, 0x06, 0x0d, 0x09, 0x09, 0x0b, 0x06, 0x0c, 0x09, 0x07};
+static unsigned char times12_168_bits[] = {
+0x05};
+static unsigned char times12_169_bits[] = {
+0x38, 0x00, 0xc6, 0x00, 0xba, 0x00, 0x25, 0x01, 0x05, 0x01, 0x25, 0x01, 
+0xba, 0x00, 0xc6, 0x00, 0x38, 0x00};
+static unsigned char times12_170_bits[] = {
+0x03, 0x04, 0x07, 0x05, 0x00, 0x07};
+static unsigned char times12_171_bits[] = {
+0x14, 0x0a, 0x05, 0x0a, 0x14};
+static unsigned char times12_172_bits[] = {
+0x3f, 0x20, 0x20, 0x20};
+static unsigned char times12_173_bits[] = {
+0x0f};
+static unsigned char times12_174_bits[] = {
+0x38, 0x00, 0xc6, 0x00, 0xba, 0x00, 0x49, 0x01, 0x39, 0x01, 0x29, 0x01, 
+0xab, 0x00, 0xc6, 0x00, 0x3c, 0x00};
+static unsigned char times12_175_bits[] = {
+0x0f};
+static unsigned char times12_176_bits[] = {
+0x06, 0x09, 0x09, 0x06};
+static unsigned char times12_177_bits[] = {
+0x04, 0x04, 0x1f, 0x04, 0x04, 0x00, 0x1f};
+static unsigned char times12_178_bits[] = {
+0x06, 0x09, 0x04, 0x02, 0x0f};
+static unsigned char times12_179_bits[] = {
+0x07, 0x04, 0x02, 0x04, 0x03};
+static unsigned char times12_180_bits[] = {
+0x02, 0x01};
+static unsigned char times12_181_bits[] = {
+0x1b, 0x12, 0x12, 0x12, 0x12, 0x2e, 0x02, 0x02, 0x06};
+static unsigned char times12_182_bits[] = {
+0x3e, 0x17, 0x17, 0x17, 0x17, 0x16, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14};
+static unsigned char times12_183_bits[] = {
+0x01};
+static unsigned char times12_184_bits[] = {
+0x02, 0x04, 0x07};
+static unsigned char times12_185_bits[] = {
+0x02, 0x03, 0x02, 0x02, 0x07};
+static unsigned char times12_186_bits[] = {
+0x02, 0x05, 0x05, 0x02, 0x00, 0x07};
+static unsigned char times12_187_bits[] = {
+0x05, 0x0a, 0x14, 0x0a, 0x05};
+static unsigned char times12_188_bits[] = {
+0x42, 0x00, 0x23, 0x00, 0x22, 0x00, 0x12, 0x00, 0x97, 0x00, 0xc8, 0x00, 
+0xa8, 0x00, 0xe4, 0x01, 0x84, 0x00};
+static unsigned char times12_189_bits[] = {
+0x42, 0x00, 0x23, 0x00, 0x22, 0x00, 0x12, 0x00, 0xd7, 0x00, 0x28, 0x01, 
+0x88, 0x00, 0x44, 0x00, 0xe4, 0x01};
+static unsigned char times12_190_bits[] = {
+0x47, 0x00, 0x24, 0x00, 0x22, 0x00, 0x14, 0x00, 0x93, 0x00, 0xc8, 0x00, 
+0xa8, 0x00, 0xe4, 0x01, 0x84, 0x00};
+static unsigned char times12_191_bits[] = {
+0x04, 0x00, 0x04, 0x04, 0x04, 0x02, 0x01, 0x09, 0x06};
+static unsigned char times12_192_bits[] = {
+0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x44, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char times12_193_bits[] = {
+0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x44, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char times12_194_bits[] = {
+0x10, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x44, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char times12_195_bits[] = {
+0x50, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x44, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char times12_196_bits[] = {
+0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 
+0x44, 0x00, 0x7c, 0x00, 0x44, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char times12_197_bits[] = {
+0x10, 0x00, 0x28, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x44, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char times12_198_bits[] = {
+0xfc, 0x03, 0x18, 0x02, 0x14, 0x00, 0x14, 0x01, 0xf2, 0x01, 0x1e, 0x01, 
+0x12, 0x00, 0x11, 0x02, 0xf3, 0x03};
+static unsigned char times12_199_bits[] = {
+0x5c, 0x66, 0x42, 0x01, 0x01, 0x01, 0x42, 0x66, 0x3c, 0x08, 0x10, 0x1c};
+static unsigned char times12_200_bits[] = {
+0x04, 0x08, 0x00, 0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 0x7f};
+static unsigned char times12_201_bits[] = {
+0x10, 0x08, 0x00, 0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 0x7f};
+static unsigned char times12_202_bits[] = {
+0x08, 0x14, 0x00, 0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 0x7f};
+static unsigned char times12_203_bits[] = {
+0x14, 0x00, 0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 0x7f};
+static unsigned char times12_204_bits[] = {
+0x01, 0x02, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_205_bits[] = {
+0x04, 0x02, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_206_bits[] = {
+0x02, 0x05, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_207_bits[] = {
+0x05, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_208_bits[] = {
+0x3f, 0x62, 0x42, 0x82, 0x87, 0x82, 0x42, 0x62, 0x3f};
+static unsigned char times12_209_bits[] = {
+0x50, 0x00, 0x28, 0x00, 0x00, 0x00, 0xc3, 0x01, 0x86, 0x00, 0x86, 0x00, 
+0x8a, 0x00, 0x92, 0x00, 0x92, 0x00, 0xa2, 0x00, 0xc2, 0x00, 0x87, 0x00};
+static unsigned char times12_210_bits[] = {
+0x04, 0x08, 0x00, 0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c};
+static unsigned char times12_211_bits[] = {
+0x20, 0x10, 0x00, 0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c};
+static unsigned char times12_212_bits[] = {
+0x08, 0x14, 0x00, 0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c};
+static unsigned char times12_213_bits[] = {
+0x28, 0x14, 0x00, 0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c};
+static unsigned char times12_214_bits[] = {
+0x24, 0x00, 0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c};
+static unsigned char times12_215_bits[] = {
+0x11, 0x0a, 0x04, 0x0a, 0x11};
+static unsigned char times12_216_bits[] = {
+0x00, 0x01, 0xbc, 0x00, 0x66, 0x00, 0x62, 0x00, 0x91, 0x00, 0x91, 0x00, 
+0x89, 0x00, 0x46, 0x00, 0x66, 0x00, 0x3d, 0x00};
+static unsigned char times12_217_bits[] = {
+0x08, 0x10, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x66, 0x3c};
+static unsigned char times12_218_bits[] = {
+0x20, 0x10, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x66, 0x3c};
+static unsigned char times12_219_bits[] = {
+0x08, 0x14, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x66, 0x3c};
+static unsigned char times12_220_bits[] = {
+0x24, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x66, 0x3c};
+static unsigned char times12_221_bits[] = {
+0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 
+0x48, 0x00, 0x38, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x38, 0x00};
+static unsigned char times12_222_bits[] = {
+0x07, 0x02, 0x3e, 0x62, 0x42, 0x62, 0x3e, 0x02, 0x07};
+static unsigned char times12_223_bits[] = {
+0x0c, 0x12, 0x12, 0x0a, 0x0e, 0x12, 0x22, 0x22, 0x1b};
+static unsigned char times12_224_bits[] = {
+0x02, 0x04, 0x00, 0x06, 0x09, 0x0e, 0x09, 0x09, 0x16};
+static unsigned char times12_225_bits[] = {
+0x08, 0x04, 0x00, 0x06, 0x09, 0x0e, 0x09, 0x09, 0x16};
+static unsigned char times12_226_bits[] = {
+0x04, 0x0a, 0x00, 0x06, 0x09, 0x0e, 0x09, 0x09, 0x16};
+static unsigned char times12_227_bits[] = {
+0x0a, 0x05, 0x00, 0x06, 0x09, 0x0e, 0x09, 0x09, 0x16};
+static unsigned char times12_228_bits[] = {
+0x0a, 0x00, 0x06, 0x09, 0x0e, 0x09, 0x09, 0x16};
+static unsigned char times12_229_bits[] = {
+0x04, 0x0a, 0x04, 0x06, 0x09, 0x0e, 0x09, 0x09, 0x16};
+static unsigned char times12_230_bits[] = {
+0x36, 0x49, 0x7e, 0x09, 0x99, 0x76};
+static unsigned char times12_231_bits[] = {
+0x06, 0x09, 0x01, 0x01, 0x09, 0x06, 0x02, 0x04, 0x07};
+static unsigned char times12_232_bits[] = {
+0x02, 0x04, 0x00, 0x06, 0x09, 0x0f, 0x01, 0x13, 0x0e};
+static unsigned char times12_233_bits[] = {
+0x04, 0x02, 0x00, 0x06, 0x09, 0x0f, 0x01, 0x13, 0x0e};
+static unsigned char times12_234_bits[] = {
+0x04, 0x0a, 0x00, 0x06, 0x09, 0x0f, 0x01, 0x13, 0x0e};
+static unsigned char times12_235_bits[] = {
+0x0a, 0x00, 0x06, 0x09, 0x0f, 0x01, 0x13, 0x0e};
+static unsigned char times12_236_bits[] = {
+0x01, 0x02, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_237_bits[] = {
+0x02, 0x01, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_238_bits[] = {
+0x02, 0x05, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_239_bits[] = {
+0x05, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times12_240_bits[] = {
+0x02, 0x0e, 0x05, 0x0e, 0x09, 0x09, 0x09, 0x09, 0x06};
+static unsigned char times12_241_bits[] = {
+0x14, 0x0a, 0x00, 0x0d, 0x12, 0x12, 0x12, 0x12, 0x37};
+static unsigned char times12_242_bits[] = {
+0x02, 0x04, 0x00, 0x06, 0x09, 0x09, 0x09, 0x09, 0x06};
+static unsigned char times12_243_bits[] = {
+0x04, 0x02, 0x00, 0x06, 0x09, 0x09, 0x09, 0x09, 0x06};
+static unsigned char times12_244_bits[] = {
+0x04, 0x0a, 0x00, 0x06, 0x09, 0x09, 0x09, 0x09, 0x06};
+static unsigned char times12_245_bits[] = {
+0x0a, 0x05, 0x00, 0x06, 0x09, 0x09, 0x09, 0x09, 0x06};
+static unsigned char times12_246_bits[] = {
+0x05, 0x00, 0x06, 0x09, 0x09, 0x09, 0x09, 0x06};
+static unsigned char times12_247_bits[] = {
+0x04, 0x00, 0x1f, 0x00, 0x04};
+static unsigned char times12_248_bits[] = {
+0x2c, 0x12, 0x12, 0x12, 0x12, 0x0e, 0x01};
+static unsigned char times12_249_bits[] = {
+0x04, 0x08, 0x00, 0x1b, 0x12, 0x12, 0x12, 0x12, 0x2c};
+static unsigned char times12_250_bits[] = {
+0x08, 0x04, 0x00, 0x1b, 0x12, 0x12, 0x12, 0x12, 0x2c};
+static unsigned char times12_251_bits[] = {
+0x04, 0x0a, 0x00, 0x1b, 0x12, 0x12, 0x12, 0x12, 0x2c};
+static unsigned char times12_252_bits[] = {
+0x0a, 0x00, 0x1b, 0x12, 0x12, 0x12, 0x12, 0x2c};
+static unsigned char times12_253_bits[] = {
+0x10, 0x08, 0x00, 0x33, 0x12, 0x1a, 0x0a, 0x0c, 0x04, 0x04, 0x02, 0x03};
+static unsigned char times12_254_bits[] = {
+0x03, 0x02, 0x02, 0x0e, 0x12, 0x12, 0x12, 0x12, 0x0e, 0x02, 0x02, 0x07};
+static unsigned char times12_255_bits[] = {
+0x12, 0x00, 0x33, 0x12, 0x1a, 0x0a, 0x0c, 0x04, 0x04, 0x02, 0x03};
+static RotFont times12font[] = {
+{5, 1, 1, times12_0_bits},
+{5, 1, 1, times12_1_bits},
+{5, 1, 1, times12_2_bits},
+{5, 1, 1, times12_3_bits},
+{5, 1, 1, times12_4_bits},
+{5, 1, 1, times12_5_bits},
+{5, 1, 1, times12_6_bits},
+{5, 1, 1, times12_7_bits},
+{5, 1, 1, times12_8_bits},
+{5, 1, 1, times12_9_bits},
+{5, 1, 1, times12_10_bits},
+{5, 1, 1, times12_11_bits},
+{5, 1, 1, times12_12_bits},
+{5, 1, 1, times12_13_bits},
+{5, 1, 1, times12_14_bits},
+{5, 1, 1, times12_15_bits},
+{5, 1, 1, times12_16_bits},
+{5, 1, 1, times12_17_bits},
+{5, 1, 1, times12_18_bits},
+{5, 1, 1, times12_19_bits},
+{5, 1, 1, times12_20_bits},
+{5, 1, 1, times12_21_bits},
+{5, 1, 1, times12_22_bits},
+{5, 1, 1, times12_23_bits},
+{5, 1, 1, times12_24_bits},
+{5, 1, 1, times12_25_bits},
+{5, 1, 1, times12_26_bits},
+{5, 1, 1, times12_27_bits},
+{5, 1, 1, times12_28_bits},
+{5, 1, 1, times12_29_bits},
+{5, 1, 1, times12_30_bits},
+{5, 1, 1, times12_31_bits},
+{5, 1, 1, times12_32_bits},
+{1, 9, 9, times12_33_bits},
+{3, 3, 9, times12_34_bits},
+{5, 8, 8, times12_35_bits},
+{5, 11, 10, times12_36_bits},
+{8, 9, 9, times12_37_bits},
+{9, 9, 9, times12_38_bits},
+{2, 3, 9, times12_39_bits},
+{3, 12, 9, times12_40_bits},
+{3, 12, 9, times12_41_bits},
+{5, 5, 9, times12_42_bits},
+{5, 5, 6, times12_43_bits},
+{2, 3, 1, times12_44_bits},
+{5, 1, 4, times12_45_bits},
+{1, 1, 1, times12_46_bits},
+{3, 9, 9, times12_47_bits},
+{5, 9, 9, times12_48_bits},
+{4, 9, 9, times12_49_bits},
+{5, 9, 9, times12_50_bits},
+{5, 9, 9, times12_51_bits},
+{5, 9, 9, times12_52_bits},
+{5, 9, 9, times12_53_bits},
+{5, 9, 9, times12_54_bits},
+{5, 9, 9, times12_55_bits},
+{5, 9, 9, times12_56_bits},
+{5, 9, 9, times12_57_bits},
+{1, 6, 6, times12_58_bits},
+{2, 8, 6, times12_59_bits},
+{6, 5, 6, times12_60_bits},
+{5, 3, 5, times12_61_bits},
+{6, 5, 6, times12_62_bits},
+{4, 9, 9, times12_63_bits},
+{10, 11, 9, times12_64_bits},
+{9, 9, 9, times12_65_bits},
+{7, 9, 9, times12_66_bits},
+{7, 9, 9, times12_67_bits},
+{8, 9, 9, times12_68_bits},
+{7, 9, 9, times12_69_bits},
+{7, 9, 9, times12_70_bits},
+{8, 9, 9, times12_71_bits},
+{8, 9, 9, times12_72_bits},
+{3, 9, 9, times12_73_bits},
+{4, 9, 9, times12_74_bits},
+{8, 9, 9, times12_75_bits},
+{6, 9, 9, times12_76_bits},
+{11, 9, 9, times12_77_bits},
+{9, 9, 9, times12_78_bits},
+{8, 9, 9, times12_79_bits},
+{7, 9, 9, times12_80_bits},
+{8, 11, 9, times12_81_bits},
+{8, 9, 9, times12_82_bits},
+{6, 9, 9, times12_83_bits},
+{7, 9, 9, times12_84_bits},
+{8, 9, 9, times12_85_bits},
+{9, 9, 9, times12_86_bits},
+{12, 9, 9, times12_87_bits},
+{8, 9, 9, times12_88_bits},
+{9, 9, 9, times12_89_bits},
+{7, 9, 9, times12_90_bits},
+{3, 12, 9, times12_91_bits},
+{3, 9, 9, times12_92_bits},
+{3, 12, 9, times12_93_bits},
+{5, 5, 9, times12_94_bits},
+{6, 1, -2, times12_95_bits},
+{2, 3, 9, times12_96_bits},
+{5, 6, 6, times12_97_bits},
+{5, 9, 9, times12_98_bits},
+{4, 6, 6, times12_99_bits},
+{5, 9, 9, times12_100_bits},
+{5, 6, 6, times12_101_bits},
+{3, 9, 9, times12_102_bits},
+{5, 9, 6, times12_103_bits},
+{6, 9, 9, times12_104_bits},
+{3, 9, 9, times12_105_bits},
+{2, 12, 9, times12_106_bits},
+{6, 9, 9, times12_107_bits},
+{3, 9, 9, times12_108_bits},
+{9, 6, 6, times12_109_bits},
+{6, 6, 6, times12_110_bits},
+{4, 6, 6, times12_111_bits},
+{5, 9, 6, times12_112_bits},
+{5, 9, 6, times12_113_bits},
+{4, 6, 6, times12_114_bits},
+{4, 6, 6, times12_115_bits},
+{4, 7, 7, times12_116_bits},
+{6, 6, 6, times12_117_bits},
+{6, 6, 6, times12_118_bits},
+{9, 6, 6, times12_119_bits},
+{5, 6, 6, times12_120_bits},
+{6, 9, 6, times12_121_bits},
+{4, 6, 6, times12_122_bits},
+{4, 12, 9, times12_123_bits},
+{1, 9, 9, times12_124_bits},
+{4, 12, 9, times12_125_bits},
+{6, 2, 5, times12_126_bits},
+{5, 1, 1, times12_127_bits},
+{5, 1, 1, times12_128_bits},
+{5, 1, 1, times12_129_bits},
+{5, 1, 1, times12_130_bits},
+{5, 1, 1, times12_131_bits},
+{5, 1, 1, times12_132_bits},
+{5, 1, 1, times12_133_bits},
+{5, 1, 1, times12_134_bits},
+{5, 1, 1, times12_135_bits},
+{5, 1, 1, times12_136_bits},
+{5, 1, 1, times12_137_bits},
+{5, 1, 1, times12_138_bits},
+{5, 1, 1, times12_139_bits},
+{5, 1, 1, times12_140_bits},
+{5, 1, 1, times12_141_bits},
+{5, 1, 1, times12_142_bits},
+{5, 1, 1, times12_143_bits},
+{5, 1, 1, times12_144_bits},
+{5, 1, 1, times12_145_bits},
+{5, 1, 1, times12_146_bits},
+{5, 1, 1, times12_147_bits},
+{5, 1, 1, times12_148_bits},
+{5, 1, 1, times12_149_bits},
+{5, 1, 1, times12_150_bits},
+{5, 1, 1, times12_151_bits},
+{5, 1, 1, times12_152_bits},
+{5, 1, 1, times12_153_bits},
+{5, 1, 1, times12_154_bits},
+{5, 1, 1, times12_155_bits},
+{5, 1, 1, times12_156_bits},
+{5, 1, 1, times12_157_bits},
+{5, 1, 1, times12_158_bits},
+{5, 1, 1, times12_159_bits},
+{1, 1, 1, times12_160_bits},
+{1, 9, 6, times12_161_bits},
+{5, 8, 7, times12_162_bits},
+{5, 9, 9, times12_163_bits},
+{6, 7, 8, times12_164_bits},
+{5, 9, 9, times12_165_bits},
+{1, 9, 9, times12_166_bits},
+{4, 12, 9, times12_167_bits},
+{3, 1, 8, times12_168_bits},
+{9, 9, 9, times12_169_bits},
+{3, 6, 9, times12_170_bits},
+{5, 5, 5, times12_171_bits},
+{6, 4, 5, times12_172_bits},
+{4, 1, 4, times12_173_bits},
+{9, 9, 9, times12_174_bits},
+{4, 1, 8, times12_175_bits},
+{4, 4, 9, times12_176_bits},
+{5, 7, 7, times12_177_bits},
+{4, 5, 9, times12_178_bits},
+{3, 5, 9, times12_179_bits},
+{2, 2, 9, times12_180_bits},
+{6, 9, 6, times12_181_bits},
+{6, 12, 9, times12_182_bits},
+{1, 1, 4, times12_183_bits},
+{3, 3, 0, times12_184_bits},
+{3, 5, 9, times12_185_bits},
+{3, 6, 9, times12_186_bits},
+{5, 5, 5, times12_187_bits},
+{9, 9, 9, times12_188_bits},
+{9, 9, 9, times12_189_bits},
+{9, 9, 9, times12_190_bits},
+{4, 9, 6, times12_191_bits},
+{9, 12, 12, times12_192_bits},
+{9, 12, 12, times12_193_bits},
+{9, 12, 12, times12_194_bits},
+{9, 12, 12, times12_195_bits},
+{9, 11, 11, times12_196_bits},
+{9, 12, 12, times12_197_bits},
+{10, 9, 9, times12_198_bits},
+{7, 12, 9, times12_199_bits},
+{7, 12, 12, times12_200_bits},
+{7, 12, 12, times12_201_bits},
+{7, 12, 12, times12_202_bits},
+{7, 11, 11, times12_203_bits},
+{3, 12, 12, times12_204_bits},
+{3, 12, 12, times12_205_bits},
+{3, 12, 12, times12_206_bits},
+{3, 11, 11, times12_207_bits},
+{8, 9, 9, times12_208_bits},
+{9, 12, 12, times12_209_bits},
+{8, 12, 12, times12_210_bits},
+{8, 12, 12, times12_211_bits},
+{8, 12, 12, times12_212_bits},
+{8, 12, 12, times12_213_bits},
+{8, 11, 11, times12_214_bits},
+{5, 5, 6, times12_215_bits},
+{9, 10, 10, times12_216_bits},
+{8, 12, 12, times12_217_bits},
+{8, 12, 12, times12_218_bits},
+{8, 12, 12, times12_219_bits},
+{8, 11, 11, times12_220_bits},
+{9, 12, 12, times12_221_bits},
+{7, 9, 9, times12_222_bits},
+{6, 9, 9, times12_223_bits},
+{5, 9, 9, times12_224_bits},
+{5, 9, 9, times12_225_bits},
+{5, 9, 9, times12_226_bits},
+{5, 9, 9, times12_227_bits},
+{5, 8, 8, times12_228_bits},
+{5, 9, 9, times12_229_bits},
+{8, 6, 6, times12_230_bits},
+{4, 9, 6, times12_231_bits},
+{5, 9, 9, times12_232_bits},
+{5, 9, 9, times12_233_bits},
+{5, 9, 9, times12_234_bits},
+{5, 8, 8, times12_235_bits},
+{3, 9, 9, times12_236_bits},
+{3, 9, 9, times12_237_bits},
+{3, 9, 9, times12_238_bits},
+{3, 8, 8, times12_239_bits},
+{4, 9, 9, times12_240_bits},
+{6, 9, 9, times12_241_bits},
+{4, 9, 9, times12_242_bits},
+{4, 9, 9, times12_243_bits},
+{4, 9, 9, times12_244_bits},
+{4, 9, 9, times12_245_bits},
+{4, 8, 8, times12_246_bits},
+{5, 5, 6, times12_247_bits},
+{6, 7, 6, times12_248_bits},
+{6, 9, 9, times12_249_bits},
+{6, 9, 9, times12_250_bits},
+{6, 9, 9, times12_251_bits},
+{6, 8, 8, times12_252_bits},
+{6, 12, 9, times12_253_bits},
+{5, 12, 9, times12_254_bits},
+{6, 11, 8, times12_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times14.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times14.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times14.bdf	(revision 22322)
@@ -0,0 +1,3205 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Times-Medium-R-Normal--14-100-100-100-P-74-ISO8859-1
+SIZE 10 100 100
+FONTBOUNDINGBOX 14 17 -1 -3
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Times"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 14
+POINT_SIZE 100
+RESOLUTION_X 100
+RESOLUTION_Y 100
+SPACING "P"
+AVERAGE_WIDTH 74
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 10
+X_HEIGHT 7
+FACE_NAME "Times Roman"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "Times is a trademark of Linotype-Hell AG and/or its subsidiaries."
+_DEC_DEVICE_FONTNAMES "PS=Times-Roman"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2, 07-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Times Roman"
+FONT "-Adobe-Times-Medium-R-Normal--14-100-100-100-P-74-ISO8859-1"
+WEIGHT 10
+RESOLUTION 138
+QUAD_WIDTH 8
+DEFAULT_CHAR 32
+FONT_ASCENT 12
+FONT_DESCENT 3
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 1 10 2 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+00
+80
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 432 0
+DWIDTH 6 0
+BBX 3 3 1 7
+BITMAP
+a0
+a0
+a0
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 10 0 0
+BITMAP
+14
+14
+7e
+28
+28
+28
+fc
+50
+50
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 12 1 -1
+BITMAP
+20
+78
+a8
+a0
+a0
+70
+28
+28
+28
+a8
+f0
+20
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 864 0
+DWIDTH 12 0
+BBX 9 10 1 0
+BITMAP
+7180
+df00
+9200
+9400
+6800
+0b00
+1680
+2480
+4480
+4300
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 10 10 1 0
+BITMAP
+1800
+2400
+2400
+3800
+1380
+7900
+ca00
+8400
+ce40
+7380
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 2 3 1 7
+BITMAP
+c0
+40
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 13 1 -3
+BITMAP
+20
+20
+40
+40
+80
+80
+80
+80
+80
+40
+40
+20
+20
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 13 1 -3
+BITMAP
+80
+80
+40
+40
+20
+20
+20
+20
+20
+40
+40
+80
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 6 1 4
+BITMAP
+20
+a8
+70
+70
+a8
+20
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+10
+10
+10
+fe
+10
+10
+10
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 2 3 1 -2
+BITMAP
+c0
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 6 1 1 3
+BITMAP
+fc
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 2 1 1 0
+BITMAP
+c0
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 12 0 -2
+BITMAP
+10
+10
+10
+20
+20
+20
+40
+40
+40
+80
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+78
+cc
+84
+84
+84
+84
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+20
+e0
+20
+20
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+70
+d8
+88
+08
+18
+10
+20
+40
+88
+f8
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+78
+8c
+04
+08
+30
+38
+04
+04
+cc
+78
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+08
+18
+18
+28
+68
+48
+88
+fc
+08
+08
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+78
+40
+80
+e0
+30
+18
+08
+08
+90
+e0
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+0c
+10
+20
+40
+78
+cc
+84
+84
+cc
+78
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+fc
+84
+08
+08
+10
+10
+20
+20
+40
+40
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+70
+98
+88
+c8
+70
+98
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+78
+cc
+84
+84
+cc
+78
+08
+10
+20
+c0
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 2 7 1 0
+BITMAP
+c0
+00
+00
+00
+00
+00
+c0
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 2 9 1 -2
+BITMAP
+c0
+00
+00
+00
+00
+00
+c0
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+06
+18
+60
+c0
+60
+18
+06
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 3 0 2
+BITMAP
+fe
+00
+fe
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+c0
+30
+0c
+06
+0c
+30
+c0
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 432 0
+DWIDTH 6 0
+BBX 5 10 0 0
+BITMAP
+70
+88
+88
+08
+10
+20
+20
+00
+20
+20
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 936 0
+DWIDTH 13 0
+BBX 12 12 0 -2
+BITMAP
+0f80
+3060
+6020
+4690
+8910
+9110
+9110
+9320
+cdc0
+4000
+30c0
+0f00
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+0800
+0800
+1400
+1400
+2200
+2200
+3e00
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+fc
+46
+42
+46
+7c
+46
+42
+42
+46
+fc
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+3d
+63
+41
+81
+80
+80
+80
+c1
+62
+3c
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 9 10 0 0
+BITMAP
+fe00
+2300
+2100
+2080
+2080
+2080
+2080
+2100
+2300
+fe00
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+fe
+42
+40
+44
+7c
+44
+40
+42
+42
+fe
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 10 1 0
+BITMAP
+fe
+42
+40
+44
+7c
+44
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+3d00
+6300
+4100
+8100
+8000
+8780
+8100
+c100
+6300
+3e00
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+e7
+42
+42
+42
+7e
+42
+42
+42
+42
+e7
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 10 1 0
+BITMAP
+e0
+40
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 432 0
+DWIDTH 6 0
+BBX 4 10 1 0
+BITMAP
+70
+20
+20
+20
+20
+20
+20
+20
+a0
+c0
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+ee
+44
+48
+50
+70
+50
+48
+44
+46
+e7
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+e0
+40
+40
+40
+40
+40
+40
+40
+42
+fe
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 936 0
+DWIDTH 13 0
+BBX 11 10 1 0
+BITMAP
+e0e0
+60c0
+5140
+5140
+5b40
+4a40
+4a40
+4e40
+4440
+e4e0
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 10 1 0
+BITMAP
+e380
+6100
+5100
+5900
+4900
+4d00
+4500
+4500
+4300
+e300
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+3c
+66
+42
+81
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 10 1 0
+BITMAP
+f8
+4c
+44
+44
+4c
+78
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 13 1 -3
+BITMAP
+3c
+66
+42
+81
+81
+81
+81
+42
+66
+3c
+08
+06
+03
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 10 0 0
+BITMAP
+fc00
+2600
+2200
+2200
+2600
+3c00
+2400
+2200
+2300
+f180
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 10 1 0
+BITMAP
+74
+cc
+84
+c0
+70
+18
+04
+84
+cc
+b8
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 10 1 0
+BITMAP
+fe
+92
+92
+10
+10
+10
+10
+10
+10
+38
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 10 1 0
+BITMAP
+e7
+42
+42
+42
+42
+42
+42
+42
+66
+3c
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 10 0 0
+BITMAP
+e380
+4100
+6300
+2200
+2200
+3600
+1400
+1c00
+0800
+0800
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 936 0
+DWIDTH 13 0
+BBX 13 10 0 0
+BITMAP
+e738
+4210
+6230
+2220
+2520
+3560
+1540
+18c0
+0880
+0880
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 10 10 0 0
+BITMAP
+7380
+2100
+1200
+1200
+0c00
+0c00
+1200
+2100
+6180
+f3c0
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 10 0 0
+BITMAP
+e380
+4100
+2200
+2200
+1400
+0800
+0800
+0800
+0800
+1c00
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 8 10 0 0
+BITMAP
+7f
+43
+02
+04
+08
+10
+20
+40
+c1
+ff
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 13 1 -3
+BITMAP
+e0
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+e0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 10 0 0
+BITMAP
+80
+80
+40
+40
+40
+20
+20
+20
+10
+10
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 13 1 -3
+BITMAP
+e0
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+20
+e0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 5 1 5
+BITMAP
+20
+50
+50
+88
+88
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 1 0 -3
+BITMAP
+fe
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 2 3 1 7
+BITMAP
+40
+80
+c0
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 7 1 0
+BITMAP
+70
+c8
+18
+68
+88
+c8
+74
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+c0
+40
+40
+58
+6c
+44
+44
+44
+4c
+78
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 7 1 0
+BITMAP
+78
+cc
+80
+80
+80
+c4
+78
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+18
+08
+08
+78
+c8
+88
+88
+88
+d8
+74
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 7 1 0
+BITMAP
+70
+88
+f8
+80
+80
+c4
+78
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 5 10 0 0
+BITMAP
+38
+60
+40
+f8
+40
+40
+40
+40
+40
+f0
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 1 -3
+BITMAP
+78
+d0
+88
+c8
+70
+40
+78
+84
+cc
+70
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 10 0 0
+BITMAP
+c0
+40
+40
+58
+6c
+44
+44
+44
+44
+ee
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 3 10 0 0
+BITMAP
+40
+40
+00
+c0
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 3 13 0 -3
+BITMAP
+20
+20
+00
+60
+20
+20
+20
+20
+20
+20
+20
+a0
+c0
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 10 0 0
+BITMAP
+c0
+40
+40
+4c
+48
+70
+50
+48
+4c
+e6
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 3 10 0 0
+BITMAP
+c0
+40
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 11 7 0 0
+BITMAP
+db80
+6ec0
+4440
+4440
+4440
+4440
+eee0
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 7 0 0
+BITMAP
+d8
+6c
+44
+44
+44
+44
+ee
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 7 1 0
+BITMAP
+70
+d8
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 0 -3
+BITMAP
+d8
+6c
+44
+44
+44
+6c
+58
+40
+40
+e0
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 1 -3
+BITMAP
+78
+c8
+88
+88
+88
+d8
+68
+08
+08
+1c
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 5 7 0 0
+BITMAP
+b8
+60
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 432 0
+DWIDTH 6 0
+BBX 4 7 1 0
+BITMAP
+70
+90
+c0
+60
+30
+90
+e0
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 8 0 0
+BITMAP
+40
+f0
+40
+40
+40
+40
+40
+30
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 7 0 0
+BITMAP
+cc
+44
+44
+44
+44
+6c
+36
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 7 0 0
+BITMAP
+ee
+44
+44
+28
+28
+10
+10
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 11 7 0 0
+BITMAP
+eee0
+4440
+4440
+2480
+3b80
+1100
+1100
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 7 0 0
+BITMAP
+ee
+44
+38
+10
+38
+44
+ee
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 10 0 -3
+BITMAP
+ee
+44
+44
+28
+28
+10
+30
+20
+a0
+c0
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 432 0
+DWIDTH 6 0
+BBX 6 7 0 0
+BITMAP
+fc
+88
+18
+30
+60
+c4
+fc
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 13 1 -3
+BITMAP
+18
+20
+20
+20
+20
+40
+80
+40
+20
+20
+20
+20
+18
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 1 10 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 13 1 -3
+BITMAP
+c0
+20
+20
+20
+20
+10
+08
+10
+20
+20
+20
+20
+c0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 2 0 3
+BITMAP
+62
+9c
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 1 10 2 -3
+BITMAP
+80
+80
+00
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 9 0 -1
+BITMAP
+04
+7c
+cc
+90
+90
+a0
+e4
+78
+80
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 10 0 0
+BITMAP
+1c
+34
+20
+20
+f8
+20
+20
+20
+e2
+bc
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 7 1 1
+BITMAP
+88
+70
+88
+88
+88
+70
+88
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 10 0 0
+BITMAP
+ee
+44
+6c
+28
+7c
+10
+7c
+10
+10
+38
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 1 10 1 0
+BITMAP
+80
+80
+80
+80
+00
+00
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 13 1 -3
+BITMAP
+38
+58
+40
+60
+70
+98
+88
+c8
+70
+30
+10
+d0
+e0
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 2 1 8
+BITMAP
+a0
+a0
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 864 0
+DWIDTH 12 0
+BBX 10 10 1 0
+BITMAP
+1e00
+6180
+4e80
+9240
+9040
+9040
+9240
+4c80
+6180
+1e00
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 3 6 0 4
+BITMAP
+e0
+20
+a0
+e0
+00
+e0
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 6 0 0
+BITMAP
+24
+48
+90
+90
+48
+24
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 4 1 2
+BITMAP
+fe
+02
+02
+02
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 3 1 0 3
+BITMAP
+e0
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 864 0
+DWIDTH 12 0
+BBX 10 10 1 0
+BITMAP
+1e00
+6180
+5c80
+9240
+9c40
+9440
+9240
+5280
+6180
+1e00
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 1 0 8
+BITMAP
+f0
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 432 0
+DWIDTH 6 0
+BBX 4 4 1 6
+BITMAP
+60
+90
+90
+60
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 7 0 0
+BITMAP
+10
+10
+fe
+10
+10
+00
+fe
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 6 0 4
+BITMAP
+60
+90
+10
+20
+40
+f0
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 4 6 0 4
+BITMAP
+60
+90
+20
+10
+90
+60
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 3 1 8
+BITMAP
+20
+60
+80
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 10 0 -3
+BITMAP
+cc
+44
+44
+44
+44
+6c
+76
+40
+40
+60
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 13 0 -3
+BITMAP
+3e
+74
+f4
+f4
+f4
+74
+34
+14
+14
+14
+14
+14
+14
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 1 2 2 3
+BITMAP
+80
+80
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 3 1 -3
+BITMAP
+40
+20
+c0
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 288 0
+DWIDTH 4 0
+BBX 3 6 0 4
+BITMAP
+40
+c0
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 4 6 0 4
+BITMAP
+60
+90
+90
+60
+00
+f0
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 6 1 0
+BITMAP
+90
+48
+24
+24
+48
+90
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 10 10 0 0
+BITMAP
+4100
+c200
+4200
+4400
+4480
+e980
+0a80
+1480
+17c0
+2080
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 10 10 0 0
+BITMAP
+4100
+c200
+4200
+4400
+4580
+ea40
+0840
+1080
+1100
+23c0
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 10 10 0 0
+BITMAP
+6100
+9200
+2200
+1400
+9480
+6980
+0a80
+1480
+17c0
+2080
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 432 0
+DWIDTH 6 0
+BBX 5 10 0 -3
+BITMAP
+20
+20
+00
+20
+20
+40
+80
+88
+88
+70
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+1000
+1800
+0400
+0000
+0800
+0800
+1400
+1400
+2200
+2200
+3e00
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+0400
+0c00
+1000
+0000
+0800
+0800
+1400
+1400
+2200
+2200
+3e00
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+0800
+1c00
+2200
+0000
+0800
+0800
+1400
+1400
+2200
+2200
+3e00
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+1200
+2a00
+2400
+0000
+0800
+0800
+1400
+1400
+2200
+2200
+3e00
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 13 1 0
+BITMAP
+2400
+2400
+0000
+0800
+0800
+1400
+1400
+2200
+2200
+3e00
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+1800
+2400
+1800
+0000
+0800
+0800
+1400
+1400
+2200
+2200
+3e00
+4100
+4100
+e380
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 936 0
+DWIDTH 13 0
+BBX 11 10 1 0
+BITMAP
+1fe0
+0c20
+1400
+1440
+27c0
+2440
+3c00
+4420
+4420
+efe0
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 13 1 -3
+BITMAP
+3d
+63
+41
+81
+80
+80
+80
+c1
+62
+3c
+08
+04
+18
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+20
+30
+08
+00
+fe
+42
+40
+44
+7c
+44
+40
+42
+42
+fe
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+04
+0c
+10
+00
+fe
+42
+40
+44
+7c
+44
+40
+42
+42
+fe
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 14 1 0
+BITMAP
+10
+38
+44
+00
+fe
+42
+40
+44
+7c
+44
+40
+42
+42
+fe
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+24
+24
+00
+fe
+42
+40
+44
+7c
+44
+40
+42
+42
+fe
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 14 1 0
+BITMAP
+80
+c0
+20
+00
+e0
+40
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 14 1 0
+BITMAP
+20
+60
+80
+00
+e0
+40
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 5 14 0 0
+BITMAP
+20
+70
+88
+00
+70
+20
+20
+20
+20
+20
+20
+20
+20
+70
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 360 0
+DWIDTH 5 0
+BBX 3 13 1 0
+BITMAP
+a0
+a0
+00
+e0
+40
+40
+40
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 9 10 0 0
+BITMAP
+fe00
+2300
+2100
+2080
+f880
+2080
+2080
+2100
+2300
+fe00
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 14 1 0
+BITMAP
+1200
+2a00
+2400
+0000
+e380
+6100
+5100
+5900
+4900
+4d00
+4500
+4500
+4300
+e300
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+20
+30
+08
+00
+3c
+66
+42
+81
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+04
+0c
+10
+00
+3c
+66
+42
+81
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+10
+38
+44
+00
+3c
+66
+42
+81
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+24
+54
+48
+00
+3c
+66
+42
+81
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+24
+24
+00
+3c
+66
+42
+81
+81
+81
+81
+42
+66
+3c
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 7 1 0
+BITMAP
+82
+44
+28
+10
+28
+44
+82
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 9 12 0 -1
+BITMAP
+0080
+1f00
+3100
+2300
+4480
+4c80
+4880
+5080
+3100
+6300
+5e00
+8000
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+20
+30
+08
+00
+e7
+42
+42
+42
+42
+42
+42
+42
+66
+3c
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+04
+0c
+10
+00
+e7
+42
+42
+42
+42
+42
+42
+42
+66
+3c
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 14 1 0
+BITMAP
+10
+38
+44
+00
+e7
+42
+42
+42
+42
+42
+42
+42
+66
+3c
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 720 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+24
+24
+00
+e7
+42
+42
+42
+42
+42
+42
+42
+66
+3c
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 648 0
+DWIDTH 9 0
+BBX 9 14 0 0
+BITMAP
+0200
+0600
+0800
+0000
+e380
+4100
+2200
+2200
+1400
+0800
+0800
+0800
+0800
+1c00
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 6 10 1 0
+BITMAP
+e0
+40
+78
+4c
+44
+44
+4c
+78
+40
+e0
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 0 0
+BITMAP
+38
+6c
+44
+48
+70
+58
+4c
+44
+54
+d8
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 1 0
+BITMAP
+40
+60
+10
+00
+70
+c8
+18
+68
+88
+c8
+74
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 1 0
+BITMAP
+08
+18
+20
+00
+70
+c8
+18
+68
+88
+c8
+74
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 1 0
+BITMAP
+20
+70
+88
+00
+70
+c8
+18
+68
+88
+c8
+74
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 1 0
+BITMAP
+48
+a8
+90
+00
+70
+c8
+18
+68
+88
+c8
+74
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+50
+50
+00
+70
+c8
+18
+68
+88
+c8
+74
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 1 0
+BITMAP
+30
+48
+30
+00
+70
+c8
+18
+68
+88
+c8
+74
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 792 0
+DWIDTH 11 0
+BBX 9 7 1 0
+BITMAP
+7f00
+c980
+1f00
+6800
+8800
+cc80
+7700
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 1 -3
+BITMAP
+70
+c8
+80
+80
+80
+c4
+78
+20
+10
+60
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 1 0
+BITMAP
+40
+60
+10
+00
+70
+88
+f8
+80
+80
+c4
+78
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 1 0
+BITMAP
+08
+18
+20
+00
+70
+88
+f8
+80
+80
+c4
+78
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 11 1 0
+BITMAP
+20
+70
+88
+00
+70
+88
+f8
+80
+80
+c4
+78
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 10 1 0
+BITMAP
+50
+50
+00
+70
+88
+f8
+80
+80
+c4
+78
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 3 11 0 0
+BITMAP
+80
+c0
+20
+00
+c0
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 3 11 0 0
+BITMAP
+20
+60
+80
+00
+c0
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 5 11 -1 0
+BITMAP
+20
+70
+88
+00
+60
+20
+20
+20
+20
+20
+70
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 216 0
+DWIDTH 3 0
+BBX 3 10 0 0
+BITMAP
+a0
+a0
+00
+c0
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+d8
+60
+90
+78
+d8
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 11 0 0
+BITMAP
+24
+54
+48
+00
+d8
+6c
+44
+44
+44
+44
+ee
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 11 1 0
+BITMAP
+40
+60
+10
+00
+70
+d8
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 11 1 0
+BITMAP
+10
+30
+40
+00
+70
+d8
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 11 1 0
+BITMAP
+20
+70
+88
+00
+70
+d8
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 11 1 0
+BITMAP
+48
+a8
+90
+00
+70
+d8
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 5 10 1 0
+BITMAP
+50
+50
+00
+70
+d8
+88
+88
+88
+d8
+70
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 576 0
+DWIDTH 8 0
+BBX 7 7 1 0
+BITMAP
+10
+10
+00
+fe
+00
+10
+10
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 9 0 -1
+BITMAP
+02
+3c
+6c
+44
+44
+44
+6c
+78
+80
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 11 0 0
+BITMAP
+20
+30
+08
+00
+cc
+44
+44
+44
+44
+6c
+36
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 11 0 0
+BITMAP
+08
+18
+20
+00
+cc
+44
+44
+44
+44
+6c
+36
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 11 0 0
+BITMAP
+10
+38
+44
+00
+cc
+44
+44
+44
+44
+6c
+36
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 10 0 0
+BITMAP
+28
+28
+00
+cc
+44
+44
+44
+44
+6c
+36
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 14 0 -3
+BITMAP
+04
+0c
+10
+00
+ee
+44
+44
+28
+28
+10
+30
+20
+a0
+c0
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 6 13 0 -3
+BITMAP
+c0
+40
+40
+58
+6c
+44
+44
+44
+6c
+58
+40
+40
+e0
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 504 0
+DWIDTH 7 0
+BBX 7 13 0 -3
+BITMAP
+28
+28
+00
+ee
+44
+44
+28
+28
+10
+30
+20
+a0
+c0
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times14.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times14.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times14.h	(revision 22322)
@@ -0,0 +1,840 @@
+static unsigned char times14_0_bits[] = {
+0x00};
+static unsigned char times14_1_bits[] = {
+0x00};
+static unsigned char times14_2_bits[] = {
+0x00};
+static unsigned char times14_3_bits[] = {
+0x00};
+static unsigned char times14_4_bits[] = {
+0x00};
+static unsigned char times14_5_bits[] = {
+0x00};
+static unsigned char times14_6_bits[] = {
+0x00};
+static unsigned char times14_7_bits[] = {
+0x00};
+static unsigned char times14_8_bits[] = {
+0x00};
+static unsigned char times14_9_bits[] = {
+0x00};
+static unsigned char times14_10_bits[] = {
+0x00};
+static unsigned char times14_11_bits[] = {
+0x00};
+static unsigned char times14_12_bits[] = {
+0x00};
+static unsigned char times14_13_bits[] = {
+0x00};
+static unsigned char times14_14_bits[] = {
+0x00};
+static unsigned char times14_15_bits[] = {
+0x00};
+static unsigned char times14_16_bits[] = {
+0x00};
+static unsigned char times14_17_bits[] = {
+0x00};
+static unsigned char times14_18_bits[] = {
+0x00};
+static unsigned char times14_19_bits[] = {
+0x00};
+static unsigned char times14_20_bits[] = {
+0x00};
+static unsigned char times14_21_bits[] = {
+0x00};
+static unsigned char times14_22_bits[] = {
+0x00};
+static unsigned char times14_23_bits[] = {
+0x00};
+static unsigned char times14_24_bits[] = {
+0x00};
+static unsigned char times14_25_bits[] = {
+0x00};
+static unsigned char times14_26_bits[] = {
+0x00};
+static unsigned char times14_27_bits[] = {
+0x00};
+static unsigned char times14_28_bits[] = {
+0x00};
+static unsigned char times14_29_bits[] = {
+0x00};
+static unsigned char times14_30_bits[] = {
+0x00};
+static unsigned char times14_31_bits[] = {
+0x00};
+static unsigned char times14_32_bits[] = {
+0x00};
+static unsigned char times14_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01};
+static unsigned char times14_34_bits[] = {
+0x05, 0x05, 0x05};
+static unsigned char times14_35_bits[] = {
+0x28, 0x28, 0x7e, 0x14, 0x14, 0x14, 0x3f, 0x0a, 0x0a, 0x0a};
+static unsigned char times14_36_bits[] = {
+0x04, 0x1e, 0x15, 0x05, 0x05, 0x0e, 0x14, 0x14, 0x14, 0x15, 0x0f, 0x04};
+static unsigned char times14_37_bits[] = {
+0x8e, 0x01, 0xfb, 0x00, 0x49, 0x00, 0x29, 0x00, 0x16, 0x00, 0xd0, 0x00, 
+0x68, 0x01, 0x24, 0x01, 0x22, 0x01, 0xc2, 0x00};
+static unsigned char times14_38_bits[] = {
+0x18, 0x00, 0x24, 0x00, 0x24, 0x00, 0x1c, 0x00, 0xc8, 0x01, 0x9e, 0x00, 
+0x53, 0x00, 0x21, 0x00, 0x73, 0x02, 0xce, 0x01};
+static unsigned char times14_39_bits[] = {
+0x03, 0x02, 0x01};
+static unsigned char times14_40_bits[] = {
+0x04, 0x04, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x04, 
+0x04};
+static unsigned char times14_41_bits[] = {
+0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x01, 
+0x01};
+static unsigned char times14_42_bits[] = {
+0x04, 0x15, 0x0e, 0x0e, 0x15, 0x04};
+static unsigned char times14_43_bits[] = {
+0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08};
+static unsigned char times14_44_bits[] = {
+0x03, 0x02, 0x01};
+static unsigned char times14_45_bits[] = {
+0x3f};
+static unsigned char times14_46_bits[] = {
+0x03};
+static unsigned char times14_47_bits[] = {
+0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01};
+static unsigned char times14_48_bits[] = {
+0x1e, 0x33, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char times14_49_bits[] = {
+0x04, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char times14_50_bits[] = {
+0x0e, 0x1b, 0x11, 0x10, 0x18, 0x08, 0x04, 0x02, 0x11, 0x1f};
+static unsigned char times14_51_bits[] = {
+0x1e, 0x31, 0x20, 0x10, 0x0c, 0x1c, 0x20, 0x20, 0x33, 0x1e};
+static unsigned char times14_52_bits[] = {
+0x10, 0x18, 0x18, 0x14, 0x16, 0x12, 0x11, 0x3f, 0x10, 0x10};
+static unsigned char times14_53_bits[] = {
+0x1e, 0x02, 0x01, 0x07, 0x0c, 0x18, 0x10, 0x10, 0x09, 0x07};
+static unsigned char times14_54_bits[] = {
+0x30, 0x08, 0x04, 0x02, 0x1e, 0x33, 0x21, 0x21, 0x33, 0x1e};
+static unsigned char times14_55_bits[] = {
+0x3f, 0x21, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02};
+static unsigned char times14_56_bits[] = {
+0x0e, 0x19, 0x11, 0x13, 0x0e, 0x19, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char times14_57_bits[] = {
+0x1e, 0x33, 0x21, 0x21, 0x33, 0x1e, 0x10, 0x08, 0x04, 0x03};
+static unsigned char times14_58_bits[] = {
+0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03};
+static unsigned char times14_59_bits[] = {
+0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01};
+static unsigned char times14_60_bits[] = {
+0x60, 0x18, 0x06, 0x03, 0x06, 0x18, 0x60};
+static unsigned char times14_61_bits[] = {
+0x7f, 0x00, 0x7f};
+static unsigned char times14_62_bits[] = {
+0x03, 0x0c, 0x30, 0x60, 0x30, 0x0c, 0x03};
+static unsigned char times14_63_bits[] = {
+0x0e, 0x11, 0x11, 0x10, 0x08, 0x04, 0x04, 0x00, 0x04, 0x04};
+static unsigned char times14_64_bits[] = {
+0xf0, 0x01, 0x0c, 0x06, 0x06, 0x04, 0x62, 0x09, 0x91, 0x08, 0x89, 0x08, 
+0x89, 0x08, 0xc9, 0x04, 0xb3, 0x03, 0x02, 0x00, 0x0c, 0x03, 0xf0, 0x00};
+static unsigned char times14_65_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 
+0x7c, 0x00, 0x82, 0x00, 0x82, 0x00, 0xc7, 0x01};
+static unsigned char times14_66_bits[] = {
+0x3f, 0x62, 0x42, 0x62, 0x3e, 0x62, 0x42, 0x42, 0x62, 0x3f};
+static unsigned char times14_67_bits[] = {
+0xbc, 0xc6, 0x82, 0x81, 0x01, 0x01, 0x01, 0x83, 0x46, 0x3c};
+static unsigned char times14_68_bits[] = {
+0x7f, 0x00, 0xc4, 0x00, 0x84, 0x00, 0x04, 0x01, 0x04, 0x01, 0x04, 0x01, 
+0x04, 0x01, 0x84, 0x00, 0xc4, 0x00, 0x7f, 0x00};
+static unsigned char times14_69_bits[] = {
+0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 0x42, 0x7f};
+static unsigned char times14_70_bits[] = {
+0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times14_71_bits[] = {
+0xbc, 0x00, 0xc6, 0x00, 0x82, 0x00, 0x81, 0x00, 0x01, 0x00, 0xe1, 0x01, 
+0x81, 0x00, 0x83, 0x00, 0xc6, 0x00, 0x7c, 0x00};
+static unsigned char times14_72_bits[] = {
+0xe7, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0xe7};
+static unsigned char times14_73_bits[] = {
+0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times14_74_bits[] = {
+0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x03};
+static unsigned char times14_75_bits[] = {
+0x77, 0x22, 0x12, 0x0a, 0x0e, 0x0a, 0x12, 0x22, 0x62, 0xe7};
+static unsigned char times14_76_bits[] = {
+0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x42, 0x7f};
+static unsigned char times14_77_bits[] = {
+0x07, 0x07, 0x06, 0x03, 0x8a, 0x02, 0x8a, 0x02, 0xda, 0x02, 0x52, 0x02, 
+0x52, 0x02, 0x72, 0x02, 0x22, 0x02, 0x27, 0x07};
+static unsigned char times14_78_bits[] = {
+0xc7, 0x01, 0x86, 0x00, 0x8a, 0x00, 0x9a, 0x00, 0x92, 0x00, 0xb2, 0x00, 
+0xa2, 0x00, 0xa2, 0x00, 0xc2, 0x00, 0xc7, 0x00};
+static unsigned char times14_79_bits[] = {
+0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c};
+static unsigned char times14_80_bits[] = {
+0x1f, 0x32, 0x22, 0x22, 0x32, 0x1e, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times14_81_bits[] = {
+0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x66, 0x3c, 0x10, 0x60, 
+0xc0};
+static unsigned char times14_82_bits[] = {
+0x3f, 0x00, 0x64, 0x00, 0x44, 0x00, 0x44, 0x00, 0x64, 0x00, 0x3c, 0x00, 
+0x24, 0x00, 0x44, 0x00, 0xc4, 0x00, 0x8f, 0x01};
+static unsigned char times14_83_bits[] = {
+0x2e, 0x33, 0x21, 0x03, 0x0e, 0x18, 0x20, 0x21, 0x33, 0x1d};
+static unsigned char times14_84_bits[] = {
+0x7f, 0x49, 0x49, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c};
+static unsigned char times14_85_bits[] = {
+0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x66, 0x3c};
+static unsigned char times14_86_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0xc6, 0x00, 0x44, 0x00, 0x44, 0x00, 0x6c, 0x00, 
+0x28, 0x00, 0x38, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char times14_87_bits[] = {
+0xe7, 0x1c, 0x42, 0x08, 0x46, 0x0c, 0x44, 0x04, 0xa4, 0x04, 0xac, 0x06, 
+0xa8, 0x02, 0x18, 0x03, 0x10, 0x01, 0x10, 0x01};
+static unsigned char times14_88_bits[] = {
+0xce, 0x01, 0x84, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x48, 0x00, 0x84, 0x00, 0x86, 0x01, 0xcf, 0x03};
+static unsigned char times14_89_bits[] = {
+0xc7, 0x01, 0x82, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x38, 0x00};
+static unsigned char times14_90_bits[] = {
+0xfe, 0xc2, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x83, 0xff};
+static unsigned char times14_91_bits[] = {
+0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x07};
+static unsigned char times14_92_bits[] = {
+0x01, 0x01, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x08, 0x08};
+static unsigned char times14_93_bits[] = {
+0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x07};
+static unsigned char times14_94_bits[] = {
+0x04, 0x0a, 0x0a, 0x11, 0x11};
+static unsigned char times14_95_bits[] = {
+0x7f};
+static unsigned char times14_96_bits[] = {
+0x02, 0x01, 0x03};
+static unsigned char times14_97_bits[] = {
+0x0e, 0x13, 0x18, 0x16, 0x11, 0x13, 0x2e};
+static unsigned char times14_98_bits[] = {
+0x03, 0x02, 0x02, 0x1a, 0x36, 0x22, 0x22, 0x22, 0x32, 0x1e};
+static unsigned char times14_99_bits[] = {
+0x1e, 0x33, 0x01, 0x01, 0x01, 0x23, 0x1e};
+static unsigned char times14_100_bits[] = {
+0x18, 0x10, 0x10, 0x1e, 0x13, 0x11, 0x11, 0x11, 0x1b, 0x2e};
+static unsigned char times14_101_bits[] = {
+0x0e, 0x11, 0x1f, 0x01, 0x01, 0x23, 0x1e};
+static unsigned char times14_102_bits[] = {
+0x1c, 0x06, 0x02, 0x1f, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0f};
+static unsigned char times14_103_bits[] = {
+0x1e, 0x0b, 0x11, 0x13, 0x0e, 0x02, 0x1e, 0x21, 0x33, 0x0e};
+static unsigned char times14_104_bits[] = {
+0x03, 0x02, 0x02, 0x1a, 0x36, 0x22, 0x22, 0x22, 0x22, 0x77};
+static unsigned char times14_105_bits[] = {
+0x02, 0x02, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times14_106_bits[] = {
+0x04, 0x04, 0x00, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 
+0x03};
+static unsigned char times14_107_bits[] = {
+0x03, 0x02, 0x02, 0x32, 0x12, 0x0e, 0x0a, 0x12, 0x32, 0x67};
+static unsigned char times14_108_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times14_109_bits[] = {
+0xdb, 0x01, 0x76, 0x03, 0x22, 0x02, 0x22, 0x02, 0x22, 0x02, 0x22, 0x02, 
+0x77, 0x07};
+static unsigned char times14_110_bits[] = {
+0x1b, 0x36, 0x22, 0x22, 0x22, 0x22, 0x77};
+static unsigned char times14_111_bits[] = {
+0x0e, 0x1b, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char times14_112_bits[] = {
+0x1b, 0x36, 0x22, 0x22, 0x22, 0x36, 0x1a, 0x02, 0x02, 0x07};
+static unsigned char times14_113_bits[] = {
+0x1e, 0x13, 0x11, 0x11, 0x11, 0x1b, 0x16, 0x10, 0x10, 0x38};
+static unsigned char times14_114_bits[] = {
+0x1d, 0x06, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times14_115_bits[] = {
+0x0e, 0x09, 0x03, 0x06, 0x0c, 0x09, 0x07};
+static unsigned char times14_116_bits[] = {
+0x02, 0x0f, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0c};
+static unsigned char times14_117_bits[] = {
+0x33, 0x22, 0x22, 0x22, 0x22, 0x36, 0x6c};
+static unsigned char times14_118_bits[] = {
+0x77, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08};
+static unsigned char times14_119_bits[] = {
+0x77, 0x07, 0x22, 0x02, 0x22, 0x02, 0x24, 0x01, 0xdc, 0x01, 0x88, 0x00, 
+0x88, 0x00};
+static unsigned char times14_120_bits[] = {
+0x77, 0x22, 0x1c, 0x08, 0x1c, 0x22, 0x77};
+static unsigned char times14_121_bits[] = {
+0x77, 0x22, 0x22, 0x14, 0x14, 0x08, 0x0c, 0x04, 0x05, 0x03};
+static unsigned char times14_122_bits[] = {
+0x3f, 0x11, 0x18, 0x0c, 0x06, 0x23, 0x3f};
+static unsigned char times14_123_bits[] = {
+0x18, 0x04, 0x04, 0x04, 0x04, 0x02, 0x01, 0x02, 0x04, 0x04, 0x04, 0x04, 
+0x18};
+static unsigned char times14_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times14_125_bits[] = {
+0x03, 0x04, 0x04, 0x04, 0x04, 0x08, 0x10, 0x08, 0x04, 0x04, 0x04, 0x04, 
+0x03};
+static unsigned char times14_126_bits[] = {
+0x46, 0x39};
+static unsigned char times14_127_bits[] = {
+0x00};
+static unsigned char times14_128_bits[] = {
+0x00};
+static unsigned char times14_129_bits[] = {
+0x00};
+static unsigned char times14_130_bits[] = {
+0x00};
+static unsigned char times14_131_bits[] = {
+0x00};
+static unsigned char times14_132_bits[] = {
+0x00};
+static unsigned char times14_133_bits[] = {
+0x00};
+static unsigned char times14_134_bits[] = {
+0x00};
+static unsigned char times14_135_bits[] = {
+0x00};
+static unsigned char times14_136_bits[] = {
+0x00};
+static unsigned char times14_137_bits[] = {
+0x00};
+static unsigned char times14_138_bits[] = {
+0x00};
+static unsigned char times14_139_bits[] = {
+0x00};
+static unsigned char times14_140_bits[] = {
+0x00};
+static unsigned char times14_141_bits[] = {
+0x00};
+static unsigned char times14_142_bits[] = {
+0x00};
+static unsigned char times14_143_bits[] = {
+0x00};
+static unsigned char times14_144_bits[] = {
+0x00};
+static unsigned char times14_145_bits[] = {
+0x00};
+static unsigned char times14_146_bits[] = {
+0x00};
+static unsigned char times14_147_bits[] = {
+0x00};
+static unsigned char times14_148_bits[] = {
+0x00};
+static unsigned char times14_149_bits[] = {
+0x00};
+static unsigned char times14_150_bits[] = {
+0x00};
+static unsigned char times14_151_bits[] = {
+0x00};
+static unsigned char times14_152_bits[] = {
+0x00};
+static unsigned char times14_153_bits[] = {
+0x00};
+static unsigned char times14_154_bits[] = {
+0x00};
+static unsigned char times14_155_bits[] = {
+0x00};
+static unsigned char times14_156_bits[] = {
+0x00};
+static unsigned char times14_157_bits[] = {
+0x00};
+static unsigned char times14_158_bits[] = {
+0x00};
+static unsigned char times14_159_bits[] = {
+0x00};
+static unsigned char times14_160_bits[] = {
+0x00};
+static unsigned char times14_161_bits[] = {
+0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times14_162_bits[] = {
+0x20, 0x3e, 0x33, 0x09, 0x09, 0x05, 0x27, 0x1e, 0x01};
+static unsigned char times14_163_bits[] = {
+0x38, 0x2c, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x04, 0x47, 0x3d};
+static unsigned char times14_164_bits[] = {
+0x11, 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x11};
+static unsigned char times14_165_bits[] = {
+0x77, 0x22, 0x36, 0x14, 0x3e, 0x08, 0x3e, 0x08, 0x08, 0x1c};
+static unsigned char times14_166_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times14_167_bits[] = {
+0x1c, 0x1a, 0x02, 0x06, 0x0e, 0x19, 0x11, 0x13, 0x0e, 0x0c, 0x08, 0x0b, 
+0x07};
+static unsigned char times14_168_bits[] = {
+0x05, 0x05};
+static unsigned char times14_169_bits[] = {
+0x78, 0x00, 0x86, 0x01, 0x72, 0x01, 0x49, 0x02, 0x09, 0x02, 0x09, 0x02, 
+0x49, 0x02, 0x32, 0x01, 0x86, 0x01, 0x78, 0x00};
+static unsigned char times14_170_bits[] = {
+0x07, 0x04, 0x05, 0x07, 0x00, 0x07};
+static unsigned char times14_171_bits[] = {
+0x24, 0x12, 0x09, 0x09, 0x12, 0x24};
+static unsigned char times14_172_bits[] = {
+0x7f, 0x40, 0x40, 0x40};
+static unsigned char times14_173_bits[] = {
+0x07};
+static unsigned char times14_174_bits[] = {
+0x78, 0x00, 0x86, 0x01, 0x3a, 0x01, 0x49, 0x02, 0x39, 0x02, 0x29, 0x02, 
+0x49, 0x02, 0x4a, 0x01, 0x86, 0x01, 0x78, 0x00};
+static unsigned char times14_175_bits[] = {
+0x0f};
+static unsigned char times14_176_bits[] = {
+0x06, 0x09, 0x09, 0x06};
+static unsigned char times14_177_bits[] = {
+0x08, 0x08, 0x7f, 0x08, 0x08, 0x00, 0x7f};
+static unsigned char times14_178_bits[] = {
+0x06, 0x09, 0x08, 0x04, 0x02, 0x0f};
+static unsigned char times14_179_bits[] = {
+0x06, 0x09, 0x04, 0x08, 0x09, 0x06};
+static unsigned char times14_180_bits[] = {
+0x04, 0x06, 0x01};
+static unsigned char times14_181_bits[] = {
+0x33, 0x22, 0x22, 0x22, 0x22, 0x36, 0x6e, 0x02, 0x02, 0x06};
+static unsigned char times14_182_bits[] = {
+0x7c, 0x2e, 0x2f, 0x2f, 0x2f, 0x2e, 0x2c, 0x28, 0x28, 0x28, 0x28, 0x28, 
+0x28};
+static unsigned char times14_183_bits[] = {
+0x01, 0x01};
+static unsigned char times14_184_bits[] = {
+0x02, 0x04, 0x03};
+static unsigned char times14_185_bits[] = {
+0x02, 0x03, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times14_186_bits[] = {
+0x06, 0x09, 0x09, 0x06, 0x00, 0x0f};
+static unsigned char times14_187_bits[] = {
+0x09, 0x12, 0x24, 0x24, 0x12, 0x09};
+static unsigned char times14_188_bits[] = {
+0x82, 0x00, 0x43, 0x00, 0x42, 0x00, 0x22, 0x00, 0x22, 0x01, 0x97, 0x01, 
+0x50, 0x01, 0x28, 0x01, 0xe8, 0x03, 0x04, 0x01};
+static unsigned char times14_189_bits[] = {
+0x82, 0x00, 0x43, 0x00, 0x42, 0x00, 0x22, 0x00, 0xa2, 0x01, 0x57, 0x02, 
+0x10, 0x02, 0x08, 0x01, 0x88, 0x00, 0xc4, 0x03};
+static unsigned char times14_190_bits[] = {
+0x86, 0x00, 0x49, 0x00, 0x44, 0x00, 0x28, 0x00, 0x29, 0x01, 0x96, 0x01, 
+0x50, 0x01, 0x28, 0x01, 0xe8, 0x03, 0x04, 0x01};
+static unsigned char times14_191_bits[] = {
+0x04, 0x04, 0x00, 0x04, 0x04, 0x02, 0x01, 0x11, 0x11, 0x0e};
+static unsigned char times14_192_bits[] = {
+0x08, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0xc7, 0x01};
+static unsigned char times14_193_bits[] = {
+0x20, 0x00, 0x30, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0xc7, 0x01};
+static unsigned char times14_194_bits[] = {
+0x10, 0x00, 0x38, 0x00, 0x44, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0xc7, 0x01};
+static unsigned char times14_195_bits[] = {
+0x48, 0x00, 0x54, 0x00, 0x24, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0xc7, 0x01};
+static unsigned char times14_196_bits[] = {
+0x24, 0x00, 0x24, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 
+0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x82, 0x00, 
+0xc7, 0x01};
+static unsigned char times14_197_bits[] = {
+0x18, 0x00, 0x24, 0x00, 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x82, 0x00, 
+0x82, 0x00, 0xc7, 0x01};
+static unsigned char times14_198_bits[] = {
+0xf8, 0x07, 0x30, 0x04, 0x28, 0x00, 0x28, 0x02, 0xe4, 0x03, 0x24, 0x02, 
+0x3c, 0x00, 0x22, 0x04, 0x22, 0x04, 0xf7, 0x07};
+static unsigned char times14_199_bits[] = {
+0xbc, 0xc6, 0x82, 0x81, 0x01, 0x01, 0x01, 0x83, 0x46, 0x3c, 0x10, 0x20, 
+0x18};
+static unsigned char times14_200_bits[] = {
+0x04, 0x0c, 0x10, 0x00, 0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 
+0x42, 0x7f};
+static unsigned char times14_201_bits[] = {
+0x20, 0x30, 0x08, 0x00, 0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 
+0x42, 0x7f};
+static unsigned char times14_202_bits[] = {
+0x08, 0x1c, 0x22, 0x00, 0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 
+0x42, 0x7f};
+static unsigned char times14_203_bits[] = {
+0x24, 0x24, 0x00, 0x7f, 0x42, 0x02, 0x22, 0x3e, 0x22, 0x02, 0x42, 0x42, 
+0x7f};
+static unsigned char times14_204_bits[] = {
+0x01, 0x03, 0x04, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
+0x02, 0x07};
+static unsigned char times14_205_bits[] = {
+0x04, 0x06, 0x01, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
+0x02, 0x07};
+static unsigned char times14_206_bits[] = {
+0x04, 0x0e, 0x11, 0x00, 0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
+0x04, 0x0e};
+static unsigned char times14_207_bits[] = {
+0x05, 0x05, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
+0x07};
+static unsigned char times14_208_bits[] = {
+0x7f, 0x00, 0xc4, 0x00, 0x84, 0x00, 0x04, 0x01, 0x1f, 0x01, 0x04, 0x01, 
+0x04, 0x01, 0x84, 0x00, 0xc4, 0x00, 0x7f, 0x00};
+static unsigned char times14_209_bits[] = {
+0x48, 0x00, 0x54, 0x00, 0x24, 0x00, 0x00, 0x00, 0xc7, 0x01, 0x86, 0x00, 
+0x8a, 0x00, 0x9a, 0x00, 0x92, 0x00, 0xb2, 0x00, 0xa2, 0x00, 0xa2, 0x00, 
+0xc2, 0x00, 0xc7, 0x00};
+static unsigned char times14_210_bits[] = {
+0x04, 0x0c, 0x10, 0x00, 0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 
+0x66, 0x3c};
+static unsigned char times14_211_bits[] = {
+0x20, 0x30, 0x08, 0x00, 0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 
+0x66, 0x3c};
+static unsigned char times14_212_bits[] = {
+0x08, 0x1c, 0x22, 0x00, 0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 
+0x66, 0x3c};
+static unsigned char times14_213_bits[] = {
+0x24, 0x2a, 0x12, 0x00, 0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 
+0x66, 0x3c};
+static unsigned char times14_214_bits[] = {
+0x24, 0x24, 0x00, 0x3c, 0x66, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x66, 
+0x3c};
+static unsigned char times14_215_bits[] = {
+0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41};
+static unsigned char times14_216_bits[] = {
+0x00, 0x01, 0xf8, 0x00, 0x8c, 0x00, 0xc4, 0x00, 0x22, 0x01, 0x32, 0x01, 
+0x12, 0x01, 0x0a, 0x01, 0x8c, 0x00, 0xc6, 0x00, 0x7a, 0x00, 0x01, 0x00};
+static unsigned char times14_217_bits[] = {
+0x04, 0x0c, 0x10, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 
+0x66, 0x3c};
+static unsigned char times14_218_bits[] = {
+0x20, 0x30, 0x08, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 
+0x66, 0x3c};
+static unsigned char times14_219_bits[] = {
+0x08, 0x1c, 0x22, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 
+0x66, 0x3c};
+static unsigned char times14_220_bits[] = {
+0x24, 0x24, 0x00, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x66, 
+0x3c};
+static unsigned char times14_221_bits[] = {
+0x40, 0x00, 0x60, 0x00, 0x10, 0x00, 0x00, 0x00, 0xc7, 0x01, 0x82, 0x00, 
+0x44, 0x00, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 
+0x10, 0x00, 0x38, 0x00};
+static unsigned char times14_222_bits[] = {
+0x07, 0x02, 0x1e, 0x32, 0x22, 0x22, 0x32, 0x1e, 0x02, 0x07};
+static unsigned char times14_223_bits[] = {
+0x1c, 0x36, 0x22, 0x12, 0x0e, 0x1a, 0x32, 0x22, 0x2a, 0x1b};
+static unsigned char times14_224_bits[] = {
+0x02, 0x06, 0x08, 0x00, 0x0e, 0x13, 0x18, 0x16, 0x11, 0x13, 0x2e};
+static unsigned char times14_225_bits[] = {
+0x10, 0x18, 0x04, 0x00, 0x0e, 0x13, 0x18, 0x16, 0x11, 0x13, 0x2e};
+static unsigned char times14_226_bits[] = {
+0x04, 0x0e, 0x11, 0x00, 0x0e, 0x13, 0x18, 0x16, 0x11, 0x13, 0x2e};
+static unsigned char times14_227_bits[] = {
+0x12, 0x15, 0x09, 0x00, 0x0e, 0x13, 0x18, 0x16, 0x11, 0x13, 0x2e};
+static unsigned char times14_228_bits[] = {
+0x0a, 0x0a, 0x00, 0x0e, 0x13, 0x18, 0x16, 0x11, 0x13, 0x2e};
+static unsigned char times14_229_bits[] = {
+0x0c, 0x12, 0x0c, 0x00, 0x0e, 0x13, 0x18, 0x16, 0x11, 0x13, 0x2e};
+static unsigned char times14_230_bits[] = {
+0xfe, 0x00, 0x93, 0x01, 0xf8, 0x00, 0x16, 0x00, 0x11, 0x00, 0x33, 0x01, 
+0xee, 0x00};
+static unsigned char times14_231_bits[] = {
+0x0e, 0x13, 0x01, 0x01, 0x01, 0x23, 0x1e, 0x04, 0x08, 0x06};
+static unsigned char times14_232_bits[] = {
+0x02, 0x06, 0x08, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x01, 0x23, 0x1e};
+static unsigned char times14_233_bits[] = {
+0x10, 0x18, 0x04, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x01, 0x23, 0x1e};
+static unsigned char times14_234_bits[] = {
+0x04, 0x0e, 0x11, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x01, 0x23, 0x1e};
+static unsigned char times14_235_bits[] = {
+0x0a, 0x0a, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x01, 0x23, 0x1e};
+static unsigned char times14_236_bits[] = {
+0x01, 0x03, 0x04, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times14_237_bits[] = {
+0x04, 0x06, 0x01, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times14_238_bits[] = {
+0x04, 0x0e, 0x11, 0x00, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e};
+static unsigned char times14_239_bits[] = {
+0x05, 0x05, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times14_240_bits[] = {
+0x1b, 0x06, 0x09, 0x1e, 0x1b, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char times14_241_bits[] = {
+0x24, 0x2a, 0x12, 0x00, 0x1b, 0x36, 0x22, 0x22, 0x22, 0x22, 0x77};
+static unsigned char times14_242_bits[] = {
+0x02, 0x06, 0x08, 0x00, 0x0e, 0x1b, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char times14_243_bits[] = {
+0x08, 0x0c, 0x02, 0x00, 0x0e, 0x1b, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char times14_244_bits[] = {
+0x04, 0x0e, 0x11, 0x00, 0x0e, 0x1b, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char times14_245_bits[] = {
+0x12, 0x15, 0x09, 0x00, 0x0e, 0x1b, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char times14_246_bits[] = {
+0x0a, 0x0a, 0x00, 0x0e, 0x1b, 0x11, 0x11, 0x11, 0x1b, 0x0e};
+static unsigned char times14_247_bits[] = {
+0x08, 0x08, 0x00, 0x7f, 0x00, 0x08, 0x08};
+static unsigned char times14_248_bits[] = {
+0x40, 0x3c, 0x36, 0x22, 0x22, 0x22, 0x36, 0x1e, 0x01};
+static unsigned char times14_249_bits[] = {
+0x04, 0x0c, 0x10, 0x00, 0x33, 0x22, 0x22, 0x22, 0x22, 0x36, 0x6c};
+static unsigned char times14_250_bits[] = {
+0x10, 0x18, 0x04, 0x00, 0x33, 0x22, 0x22, 0x22, 0x22, 0x36, 0x6c};
+static unsigned char times14_251_bits[] = {
+0x08, 0x1c, 0x22, 0x00, 0x33, 0x22, 0x22, 0x22, 0x22, 0x36, 0x6c};
+static unsigned char times14_252_bits[] = {
+0x14, 0x14, 0x00, 0x33, 0x22, 0x22, 0x22, 0x22, 0x36, 0x6c};
+static unsigned char times14_253_bits[] = {
+0x20, 0x30, 0x08, 0x00, 0x77, 0x22, 0x22, 0x14, 0x14, 0x08, 0x0c, 0x04, 
+0x05, 0x03};
+static unsigned char times14_254_bits[] = {
+0x03, 0x02, 0x02, 0x1a, 0x36, 0x22, 0x22, 0x22, 0x36, 0x1a, 0x02, 0x02, 
+0x07};
+static unsigned char times14_255_bits[] = {
+0x14, 0x14, 0x00, 0x77, 0x22, 0x22, 0x14, 0x14, 0x08, 0x0c, 0x04, 0x05, 
+0x03};
+static RotFont times14font[] = {
+{5, 1, 1, times14_0_bits},
+{5, 1, 1, times14_1_bits},
+{5, 1, 1, times14_2_bits},
+{5, 1, 1, times14_3_bits},
+{5, 1, 1, times14_4_bits},
+{5, 1, 1, times14_5_bits},
+{5, 1, 1, times14_6_bits},
+{5, 1, 1, times14_7_bits},
+{5, 1, 1, times14_8_bits},
+{5, 1, 1, times14_9_bits},
+{5, 1, 1, times14_10_bits},
+{5, 1, 1, times14_11_bits},
+{5, 1, 1, times14_12_bits},
+{5, 1, 1, times14_13_bits},
+{5, 1, 1, times14_14_bits},
+{5, 1, 1, times14_15_bits},
+{5, 1, 1, times14_16_bits},
+{5, 1, 1, times14_17_bits},
+{5, 1, 1, times14_18_bits},
+{5, 1, 1, times14_19_bits},
+{5, 1, 1, times14_20_bits},
+{5, 1, 1, times14_21_bits},
+{5, 1, 1, times14_22_bits},
+{5, 1, 1, times14_23_bits},
+{5, 1, 1, times14_24_bits},
+{5, 1, 1, times14_25_bits},
+{5, 1, 1, times14_26_bits},
+{5, 1, 1, times14_27_bits},
+{5, 1, 1, times14_28_bits},
+{5, 1, 1, times14_29_bits},
+{5, 1, 1, times14_30_bits},
+{5, 1, 1, times14_31_bits},
+{6, 1, 1, times14_32_bits},
+{1, 10, 10, times14_33_bits},
+{3, 3, 10, times14_34_bits},
+{7, 10, 10, times14_35_bits},
+{5, 12, 11, times14_36_bits},
+{9, 10, 10, times14_37_bits},
+{10, 10, 10, times14_38_bits},
+{2, 3, 10, times14_39_bits},
+{3, 13, 10, times14_40_bits},
+{3, 13, 10, times14_41_bits},
+{5, 6, 10, times14_42_bits},
+{7, 7, 7, times14_43_bits},
+{2, 3, 1, times14_44_bits},
+{6, 1, 4, times14_45_bits},
+{2, 1, 1, times14_46_bits},
+{4, 12, 10, times14_47_bits},
+{6, 10, 10, times14_48_bits},
+{5, 10, 10, times14_49_bits},
+{5, 10, 10, times14_50_bits},
+{6, 10, 10, times14_51_bits},
+{6, 10, 10, times14_52_bits},
+{5, 10, 10, times14_53_bits},
+{6, 10, 10, times14_54_bits},
+{6, 10, 10, times14_55_bits},
+{5, 10, 10, times14_56_bits},
+{6, 10, 10, times14_57_bits},
+{2, 7, 7, times14_58_bits},
+{2, 9, 7, times14_59_bits},
+{7, 7, 7, times14_60_bits},
+{7, 3, 5, times14_61_bits},
+{7, 7, 7, times14_62_bits},
+{5, 10, 10, times14_63_bits},
+{12, 12, 10, times14_64_bits},
+{9, 10, 10, times14_65_bits},
+{7, 10, 10, times14_66_bits},
+{8, 10, 10, times14_67_bits},
+{9, 10, 10, times14_68_bits},
+{7, 10, 10, times14_69_bits},
+{7, 10, 10, times14_70_bits},
+{9, 10, 10, times14_71_bits},
+{8, 10, 10, times14_72_bits},
+{3, 10, 10, times14_73_bits},
+{4, 10, 10, times14_74_bits},
+{8, 10, 10, times14_75_bits},
+{7, 10, 10, times14_76_bits},
+{11, 10, 10, times14_77_bits},
+{9, 10, 10, times14_78_bits},
+{8, 10, 10, times14_79_bits},
+{6, 10, 10, times14_80_bits},
+{8, 13, 10, times14_81_bits},
+{9, 10, 10, times14_82_bits},
+{6, 10, 10, times14_83_bits},
+{7, 10, 10, times14_84_bits},
+{8, 10, 10, times14_85_bits},
+{9, 10, 10, times14_86_bits},
+{13, 10, 10, times14_87_bits},
+{10, 10, 10, times14_88_bits},
+{9, 10, 10, times14_89_bits},
+{8, 10, 10, times14_90_bits},
+{3, 13, 10, times14_91_bits},
+{4, 10, 10, times14_92_bits},
+{3, 13, 10, times14_93_bits},
+{5, 5, 10, times14_94_bits},
+{7, 1, -2, times14_95_bits},
+{2, 3, 10, times14_96_bits},
+{6, 7, 7, times14_97_bits},
+{6, 10, 10, times14_98_bits},
+{6, 7, 7, times14_99_bits},
+{6, 10, 10, times14_100_bits},
+{6, 7, 7, times14_101_bits},
+{5, 10, 10, times14_102_bits},
+{6, 10, 7, times14_103_bits},
+{7, 10, 10, times14_104_bits},
+{3, 10, 10, times14_105_bits},
+{3, 13, 10, times14_106_bits},
+{7, 10, 10, times14_107_bits},
+{3, 10, 10, times14_108_bits},
+{11, 7, 7, times14_109_bits},
+{7, 7, 7, times14_110_bits},
+{5, 7, 7, times14_111_bits},
+{6, 10, 7, times14_112_bits},
+{6, 10, 7, times14_113_bits},
+{5, 7, 7, times14_114_bits},
+{4, 7, 7, times14_115_bits},
+{4, 8, 8, times14_116_bits},
+{7, 7, 7, times14_117_bits},
+{7, 7, 7, times14_118_bits},
+{11, 7, 7, times14_119_bits},
+{7, 7, 7, times14_120_bits},
+{7, 10, 7, times14_121_bits},
+{6, 7, 7, times14_122_bits},
+{5, 13, 10, times14_123_bits},
+{1, 10, 10, times14_124_bits},
+{5, 13, 10, times14_125_bits},
+{7, 2, 5, times14_126_bits},
+{5, 1, 1, times14_127_bits},
+{5, 1, 1, times14_128_bits},
+{5, 1, 1, times14_129_bits},
+{5, 1, 1, times14_130_bits},
+{5, 1, 1, times14_131_bits},
+{5, 1, 1, times14_132_bits},
+{5, 1, 1, times14_133_bits},
+{5, 1, 1, times14_134_bits},
+{5, 1, 1, times14_135_bits},
+{5, 1, 1, times14_136_bits},
+{5, 1, 1, times14_137_bits},
+{5, 1, 1, times14_138_bits},
+{5, 1, 1, times14_139_bits},
+{5, 1, 1, times14_140_bits},
+{5, 1, 1, times14_141_bits},
+{5, 1, 1, times14_142_bits},
+{5, 1, 1, times14_143_bits},
+{5, 1, 1, times14_144_bits},
+{5, 1, 1, times14_145_bits},
+{5, 1, 1, times14_146_bits},
+{5, 1, 1, times14_147_bits},
+{5, 1, 1, times14_148_bits},
+{5, 1, 1, times14_149_bits},
+{5, 1, 1, times14_150_bits},
+{5, 1, 1, times14_151_bits},
+{5, 1, 1, times14_152_bits},
+{5, 1, 1, times14_153_bits},
+{5, 1, 1, times14_154_bits},
+{5, 1, 1, times14_155_bits},
+{5, 1, 1, times14_156_bits},
+{5, 1, 1, times14_157_bits},
+{5, 1, 1, times14_158_bits},
+{5, 1, 1, times14_159_bits},
+{1, 1, 1, times14_160_bits},
+{1, 10, 7, times14_161_bits},
+{6, 9, 8, times14_162_bits},
+{7, 10, 10, times14_163_bits},
+{5, 7, 8, times14_164_bits},
+{7, 10, 10, times14_165_bits},
+{1, 10, 10, times14_166_bits},
+{5, 13, 10, times14_167_bits},
+{3, 2, 10, times14_168_bits},
+{10, 10, 10, times14_169_bits},
+{3, 6, 10, times14_170_bits},
+{6, 6, 6, times14_171_bits},
+{7, 4, 6, times14_172_bits},
+{3, 1, 4, times14_173_bits},
+{10, 10, 10, times14_174_bits},
+{4, 1, 9, times14_175_bits},
+{4, 4, 10, times14_176_bits},
+{7, 7, 7, times14_177_bits},
+{4, 6, 10, times14_178_bits},
+{4, 6, 10, times14_179_bits},
+{3, 3, 11, times14_180_bits},
+{7, 10, 7, times14_181_bits},
+{7, 13, 10, times14_182_bits},
+{1, 2, 5, times14_183_bits},
+{3, 3, 0, times14_184_bits},
+{3, 6, 10, times14_185_bits},
+{4, 6, 10, times14_186_bits},
+{6, 6, 6, times14_187_bits},
+{10, 10, 10, times14_188_bits},
+{10, 10, 10, times14_189_bits},
+{10, 10, 10, times14_190_bits},
+{5, 10, 7, times14_191_bits},
+{9, 14, 14, times14_192_bits},
+{9, 14, 14, times14_193_bits},
+{9, 14, 14, times14_194_bits},
+{9, 14, 14, times14_195_bits},
+{9, 13, 13, times14_196_bits},
+{9, 14, 14, times14_197_bits},
+{11, 10, 10, times14_198_bits},
+{8, 13, 10, times14_199_bits},
+{7, 14, 14, times14_200_bits},
+{7, 14, 14, times14_201_bits},
+{7, 14, 14, times14_202_bits},
+{7, 13, 13, times14_203_bits},
+{3, 14, 14, times14_204_bits},
+{3, 14, 14, times14_205_bits},
+{5, 14, 14, times14_206_bits},
+{3, 13, 13, times14_207_bits},
+{9, 10, 10, times14_208_bits},
+{9, 14, 14, times14_209_bits},
+{8, 14, 14, times14_210_bits},
+{8, 14, 14, times14_211_bits},
+{8, 14, 14, times14_212_bits},
+{8, 14, 14, times14_213_bits},
+{8, 13, 13, times14_214_bits},
+{7, 7, 7, times14_215_bits},
+{9, 12, 11, times14_216_bits},
+{8, 14, 14, times14_217_bits},
+{8, 14, 14, times14_218_bits},
+{8, 14, 14, times14_219_bits},
+{8, 13, 13, times14_220_bits},
+{9, 14, 14, times14_221_bits},
+{6, 10, 10, times14_222_bits},
+{6, 10, 10, times14_223_bits},
+{6, 11, 11, times14_224_bits},
+{6, 11, 11, times14_225_bits},
+{6, 11, 11, times14_226_bits},
+{6, 11, 11, times14_227_bits},
+{6, 10, 10, times14_228_bits},
+{6, 11, 11, times14_229_bits},
+{9, 7, 7, times14_230_bits},
+{6, 10, 7, times14_231_bits},
+{6, 11, 11, times14_232_bits},
+{6, 11, 11, times14_233_bits},
+{6, 11, 11, times14_234_bits},
+{6, 10, 10, times14_235_bits},
+{3, 11, 11, times14_236_bits},
+{3, 11, 11, times14_237_bits},
+{5, 11, 11, times14_238_bits},
+{3, 10, 10, times14_239_bits},
+{5, 10, 10, times14_240_bits},
+{7, 11, 11, times14_241_bits},
+{5, 11, 11, times14_242_bits},
+{5, 11, 11, times14_243_bits},
+{5, 11, 11, times14_244_bits},
+{5, 11, 11, times14_245_bits},
+{5, 10, 10, times14_246_bits},
+{7, 7, 7, times14_247_bits},
+{7, 9, 8, times14_248_bits},
+{7, 11, 11, times14_249_bits},
+{7, 11, 11, times14_250_bits},
+{7, 11, 11, times14_251_bits},
+{7, 10, 10, times14_252_bits},
+{7, 14, 11, times14_253_bits},
+{6, 13, 10, times14_254_bits},
+{7, 13, 10, times14_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times18.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times18.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times18.bdf	(revision 22322)
@@ -0,0 +1,3675 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Times-Medium-R-Normal--18-180-75-75-P-94-ISO8859-1
+SIZE 18 75 75
+FONTBOUNDINGBOX 19 21 -1 -4
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Times"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 18
+POINT_SIZE 180
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 94
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 13
+X_HEIGHT 9
+FACE_NAME "Times Roman"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "Times is a trademark of Linotype-Hell AG and/or its subsidiaries."
+_DEC_DEVICE_FONTNAMES "PS=Times-Roman"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2-1, 22-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Times Roman"
+FONT "-Adobe-Times-Medium-R-Normal--18-180-75-75-P-94-ISO8859-1"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 10
+DEFAULT_CHAR 32
+FONT_ASCENT 15
+FONT_DESCENT 4
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 1 1 1 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 13 1 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+40
+40
+40
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 6 4 1 9
+BITMAP
+cc
+cc
+cc
+88
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 11 13 -1 0
+BITMAP
+0d80
+0d80
+0d80
+0d80
+7fe0
+1b00
+1b00
+1b00
+ffc0
+3600
+3600
+3600
+3600
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 16 1 -2
+BITMAP
+10
+7c
+d6
+d2
+d0
+f0
+78
+3c
+1c
+16
+16
+96
+d6
+7c
+10
+10
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+3830
+6fe0
+c440
+c480
+cd80
+fb00
+7270
+06d8
+0d88
+0988
+1998
+31f0
+20e0
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+1c00
+3200
+3200
+3200
+3400
+19e0
+38c0
+6d80
+cf00
+c700
+c780
+edf0
+78e0
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 3 4 1 9
+BITMAP
+c0
+e0
+20
+40
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 17 1 -4
+BITMAP
+18
+30
+60
+60
+40
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+40
+60
+60
+30
+18
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 17 1 -4
+BITMAP
+c0
+60
+30
+30
+10
+18
+18
+18
+18
+18
+18
+18
+10
+30
+30
+60
+c0
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 8 1 6
+BITMAP
+10
+10
+d6
+54
+38
+d6
+92
+10
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 9 1 0
+BITMAP
+0800
+0800
+0800
+0800
+ff80
+0800
+0800
+0800
+0800
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 3 5 0 -3
+BITMAP
+60
+60
+20
+60
+c0
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 1 1 4
+BITMAP
+ff
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 2 1 0
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 15 0 -2
+BITMAP
+08
+08
+18
+10
+10
+30
+20
+20
+60
+40
+40
+c0
+80
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+3c
+66
+42
+c3
+c3
+c3
+c3
+c3
+c3
+c3
+42
+66
+3c
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 6 13 2 0
+BITMAP
+30
+70
+f0
+30
+30
+30
+30
+30
+30
+30
+30
+78
+fc
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+3c
+7e
+ce
+86
+06
+06
+0c
+0c
+18
+30
+62
+fe
+fe
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+7c
+ce
+86
+06
+0c
+38
+3c
+0e
+06
+06
+06
+cc
+f8
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+06
+0e
+0e
+16
+26
+66
+46
+86
+ff
+ff
+06
+06
+06
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+7e
+7c
+c0
+c0
+f0
+3c
+0c
+0e
+06
+06
+0c
+dc
+f0
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+07
+1c
+30
+60
+60
+fc
+c6
+c3
+c3
+c3
+e3
+76
+3c
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+fe
+fe
+84
+0c
+08
+18
+18
+10
+30
+30
+20
+60
+60
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+3c
+66
+62
+62
+74
+3c
+3c
+6e
+c7
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+3c
+6e
+c6
+c3
+c3
+c3
+e3
+7f
+06
+06
+0c
+38
+e0
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 9 1 0
+BITMAP
+c0
+c0
+00
+00
+00
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 3 12 0 -3
+BITMAP
+60
+60
+00
+00
+00
+00
+00
+60
+60
+20
+60
+c0
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+0180
+0700
+1c00
+7000
+c000
+7000
+1c00
+0700
+0180
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 3 1 3
+BITMAP
+ff
+00
+ff
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 9 1 0
+BITMAP
+c000
+7000
+1c00
+0700
+0180
+0700
+1c00
+7000
+c000
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 6 13 1 0
+BITMAP
+78
+8c
+c4
+cc
+0c
+18
+18
+30
+20
+20
+00
+30
+30
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 906 0
+DWIDTH 17 0
+BBX 14 16 1 -3
+BITMAP
+07e0
+0e30
+3818
+3008
+63ec
+67e4
+c664
+cc64
+cccc
+ccc8
+cdd8
+6770
+6000
+3000
+1c30
+07e0
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+0200
+0700
+0700
+0500
+0d80
+0980
+19c0
+10c0
+1fc0
+30e0
+2060
+6070
+f0f8
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 13 1 0
+BITMAP
+ff00
+6380
+6180
+6180
+6180
+6300
+7f80
+61c0
+60c0
+60c0
+60c0
+6180
+ff00
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 11 13 1 0
+BITMAP
+0f20
+30e0
+6060
+6020
+c000
+c000
+c000
+c000
+c000
+6000
+6060
+38c0
+0f00
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+ff00
+6380
+60c0
+60c0
+6060
+6060
+6060
+6060
+6060
+60c0
+60c0
+6380
+ff00
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 1 0
+BITMAP
+ff80
+6180
+6080
+6000
+6000
+6100
+7f00
+6100
+6000
+6000
+6080
+6180
+ff80
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 13 1 0
+BITMAP
+ff80
+6180
+6080
+6000
+6000
+6100
+7f00
+6100
+6000
+6000
+6000
+6000
+f000
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+0f20
+30e0
+6060
+6020
+c000
+c000
+c0f0
+c060
+c060
+6060
+6060
+39c0
+0f00
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+f1e0
+60c0
+60c0
+60c0
+60c0
+60c0
+7fc0
+60c0
+60c0
+60c0
+60c0
+60c0
+f1e0
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 13 1 0
+BITMAP
+f0
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 6 13 0 0
+BITMAP
+3c
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+d8
+f0
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+f3c0
+6180
+6300
+6600
+6c00
+7800
+7800
+6c00
+6600
+6300
+6380
+61c0
+f0e0
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 13 1 0
+BITMAP
+f000
+6000
+6000
+6000
+6000
+6000
+6000
+6000
+6000
+6000
+6040
+60c0
+ffc0
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 853 0
+DWIDTH 16 0
+BBX 14 13 1 0
+BITMAP
+e01c
+7038
+7038
+7878
+5858
+58d8
+5cd8
+4c98
+4d98
+4718
+4718
+4218
+e23c
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+e0e0
+6040
+7040
+7840
+5840
+4c40
+4e40
+4740
+4340
+41c0
+41c0
+40c0
+e040
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 13 1 0
+BITMAP
+0f00
+30c0
+6060
+6060
+c030
+c030
+c030
+c030
+c030
+6060
+6060
+30c0
+0f00
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 13 1 0
+BITMAP
+ff00
+6380
+6180
+6180
+6180
+6300
+7e00
+6000
+6000
+6000
+6000
+6000
+f000
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 17 1 -4
+BITMAP
+0f00
+30c0
+6060
+6060
+c030
+c030
+c030
+c030
+c030
+6060
+6060
+30c0
+0f00
+0600
+0300
+01c0
+00f0
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 11 13 1 0
+BITMAP
+ff00
+6380
+6180
+6180
+6180
+6300
+7e00
+6600
+6300
+6180
+6180
+60c0
+f0e0
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 13 1 0
+BITMAP
+3a
+66
+c2
+c0
+e0
+78
+3c
+0e
+07
+03
+83
+c6
+bc
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 13 1 0
+BITMAP
+ffc0
+ccc0
+8c40
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+0c00
+1e00
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+f0e0
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+70c0
+3980
+1f00
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 13 13 0 0
+BITMAP
+f878
+7030
+3020
+3820
+1860
+1840
+1c40
+0cc0
+0e80
+0680
+0780
+0300
+0300
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 960 0
+DWIDTH 18 0
+BBX 18 13 0 0
+BITMAP
+f9e3c0
+30c180
+30c100
+18c100
+186300
+186200
+1ce200
+0ca600
+0cb400
+0d3400
+073c00
+061800
+061800
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 12 13 0 0
+BITMAP
+f070
+7060
+38c0
+1980
+0d00
+0e00
+0600
+0f00
+1b00
+1180
+31c0
+60e0
+f0f0
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 12 13 0 0
+BITMAP
+f0f0
+7060
+30c0
+1880
+1900
+0f00
+0600
+0600
+0600
+0600
+0600
+0600
+0f00
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 10 13 1 0
+BITMAP
+7fc0
+61c0
+4180
+0380
+0700
+0600
+0e00
+1c00
+3800
+3000
+7040
+e0c0
+ffc0
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 16 2 -3
+BITMAP
+f0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+f0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 13 0 0
+BITMAP
+80
+80
+c0
+40
+60
+60
+20
+30
+30
+10
+18
+08
+08
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 16 0 -3
+BITMAP
+f0
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+f0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 7 1 6
+BITMAP
+10
+38
+28
+6c
+44
+c6
+82
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 1 0 -4
+BITMAP
+ff80
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 3 4 1 9
+BITMAP
+40
+80
+e0
+60
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+78
+cc
+cc
+1c
+6c
+cc
+cc
+fc
+66
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+e0
+60
+60
+60
+6e
+77
+63
+63
+63
+63
+63
+76
+3c
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+3c
+66
+c6
+c0
+c0
+c0
+c0
+62
+3c
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+0e
+06
+06
+06
+3e
+66
+c6
+c6
+c6
+c6
+c6
+66
+3f
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 8 9 0 0
+BITMAP
+3c
+66
+c3
+ff
+c0
+c0
+e1
+76
+3c
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 6 13 0 0
+BITMAP
+1c
+34
+20
+60
+f8
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 8 13 0 -4
+BITMAP
+3e
+cc
+c4
+c4
+cc
+78
+40
+7c
+7f
+83
+c1
+e2
+7c
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 13 0 0
+BITMAP
+e000
+6000
+6000
+6000
+6600
+6f00
+7300
+6300
+6300
+6300
+6300
+6300
+f380
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 4 13 0 0
+BITMAP
+60
+60
+00
+00
+e0
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 4 17 -1 -4
+BITMAP
+30
+30
+00
+00
+70
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+e0
+c0
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 13 0 0
+BITMAP
+e000
+6000
+6000
+6000
+6700
+6200
+6400
+6800
+7800
+6c00
+6600
+6300
+e380
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 4 13 0 0
+BITMAP
+e0
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 14 9 0 0
+BITMAP
+e630
+6f78
+7398
+6318
+6318
+6318
+6318
+6318
+f7bc
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 9 0 0
+BITMAP
+e600
+6f00
+7300
+6300
+6300
+6300
+6300
+6300
+f380
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 9 0 0
+BITMAP
+3c
+66
+c3
+c3
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 -4
+BITMAP
+ee
+77
+63
+63
+63
+63
+63
+76
+6c
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 -4
+BITMAP
+3e
+66
+c6
+c6
+c6
+c6
+c6
+66
+3e
+06
+06
+06
+0f
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 6 9 0 0
+BITMAP
+ec
+6c
+70
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 9 0 0
+BITMAP
+68
+d8
+c8
+e0
+70
+38
+98
+d8
+b0
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 6 11 0 0
+BITMAP
+20
+60
+f8
+60
+60
+60
+60
+60
+60
+74
+38
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 9 -1 0
+BITMAP
+e700
+6300
+6300
+6300
+6300
+6300
+6300
+7700
+3b80
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 9 -1 0
+BITMAP
+f380
+6300
+6200
+3200
+3600
+1400
+1c00
+0800
+0800
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 13 9 -1 0
+BITMAP
+e738
+6630
+6260
+3660
+3760
+1d40
+1dc0
+0880
+0880
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 9 0 0
+BITMAP
+e380
+6300
+3600
+1c00
+0c00
+1c00
+3600
+6300
+e380
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 9 13 -1 -4
+BITMAP
+f380
+6100
+7300
+3200
+3600
+1c00
+1c00
+0c00
+1800
+1800
+1000
+f000
+e000
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 7 9 0 0
+BITMAP
+fe
+ce
+8c
+18
+30
+30
+62
+e6
+fe
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 17 1 -4
+BITMAP
+0e
+18
+30
+30
+30
+30
+30
+60
+c0
+60
+30
+30
+30
+30
+30
+18
+0e
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 160 0
+DWIDTH 3 0
+BBX 1 13 1 0
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 17 1 -4
+BITMAP
+e0
+30
+18
+18
+18
+18
+18
+0c
+06
+0c
+18
+18
+18
+18
+18
+30
+e0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 2 1 4
+BITMAP
+7980
+cf00
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 1 1 1 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 13 1 -4
+BITMAP
+c0
+c0
+00
+00
+80
+80
+80
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 1 -2
+BITMAP
+06
+04
+3e
+6b
+cb
+c8
+d8
+d0
+d0
+72
+7c
+40
+c0
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 10 13 0 0
+BITMAP
+0f00
+1980
+1980
+1800
+1800
+1800
+7e00
+1800
+1800
+1000
+7840
+bfc0
+e780
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 7 1 3
+BITMAP
+dd80
+f780
+6300
+4100
+6300
+f780
+dd80
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+f7
+62
+62
+76
+34
+34
+7e
+18
+7e
+18
+18
+18
+7e
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 160 0
+DWIDTH 3 0
+BBX 1 13 1 0
+BITMAP
+80
+80
+80
+80
+80
+00
+00
+00
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 16 1 -3
+BITMAP
+3c
+66
+66
+70
+38
+7c
+8e
+c7
+e3
+71
+3e
+1c
+0e
+66
+66
+3c
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 2 0 10
+BITMAP
+d8
+d8
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+0f80
+3060
+4010
+4790
+8888
+9008
+9008
+9008
+8888
+4710
+4010
+3060
+0f80
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 8 0 5
+BITMAP
+60
+90
+10
+70
+90
+e8
+00
+f8
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 7 1 1
+BITMAP
+11
+33
+66
+cc
+66
+33
+11
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 5 1 1
+BITMAP
+ff80
+0080
+0080
+0080
+0080
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 5 2 0 4
+BITMAP
+f8
+f8
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 13 1 0
+BITMAP
+0f80
+3060
+4010
+5f10
+8888
+8888
+8f08
+8908
+8888
+5cd0
+4010
+3060
+0f80
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 1 0 11
+BITMAP
+f8
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 373 0
+DWIDTH 7 0
+BBX 5 5 1 8
+BITMAP
+70
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 10 1 0
+BITMAP
+0800
+0800
+0800
+0800
+ff80
+0800
+0800
+0800
+0000
+ff80
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 8 0 5
+BITMAP
+70
+98
+18
+10
+20
+20
+40
+f8
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 8 0 5
+BITMAP
+70
+88
+18
+70
+18
+08
+88
+70
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 4 3 0 10
+BITMAP
+30
+60
+80
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 13 0 -4
+BITMAP
+e700
+6300
+6300
+6300
+6300
+6300
+6300
+7700
+7b80
+4000
+4000
+6000
+6000
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 7 17 1 -4
+BITMAP
+3e
+74
+f4
+f4
+f4
+f4
+f4
+74
+14
+14
+14
+14
+14
+14
+14
+14
+14
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 213 0
+DWIDTH 4 0
+BBX 2 2 1 4
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 5 1 -4
+BITMAP
+20
+20
+10
+b0
+60
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 3 8 1 5
+BITMAP
+40
+c0
+40
+40
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 5 8 0 5
+BITMAP
+70
+d8
+88
+88
+d8
+70
+00
+f8
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 7 1 1
+BITMAP
+88
+cc
+66
+33
+66
+cc
+88
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+4080
+c180
+4100
+4300
+4600
+4440
+4cc0
+e940
+1b40
+3240
+27e0
+6040
+4040
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 13 1 0
+BITMAP
+4080
+c180
+4100
+4300
+4600
+45c0
+4e60
+e860
+1840
+3080
+2080
+6100
+43e0
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 13 13 -1 0
+BITMAP
+7020
+8860
+1840
+70c0
+1980
+0910
+8b30
+7250
+06d0
+0c90
+09f8
+1810
+1010
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 6 13 1 -4
+BITMAP
+30
+30
+00
+10
+10
+30
+60
+60
+c0
+cc
+8c
+c4
+78
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+0c00
+0600
+0100
+0000
+0200
+0700
+0700
+0500
+0d80
+0980
+19c0
+10c0
+1fc0
+30e0
+2060
+6070
+f0f8
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+00c0
+0180
+0200
+0000
+0200
+0700
+0700
+0500
+0d80
+0980
+19c0
+10c0
+1fc0
+30e0
+2060
+6070
+f0f8
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+0200
+0700
+0880
+0000
+0200
+0700
+0700
+0500
+0d80
+0980
+19c0
+10c0
+1fc0
+30e0
+2060
+6070
+f0f8
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 16 1 0
+BITMAP
+0740
+0b80
+0000
+0200
+0700
+0700
+0500
+0d80
+0980
+19c0
+10c0
+1fc0
+30e0
+2060
+6070
+f0f8
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 16 1 0
+BITMAP
+0d80
+0d80
+0000
+0200
+0700
+0700
+0500
+0d80
+0980
+19c0
+10c0
+1fc0
+30e0
+2060
+6070
+f0f8
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 800 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+0300
+0480
+0480
+0300
+0200
+0700
+0700
+0500
+0d80
+0980
+19c0
+10c0
+1fc0
+30e0
+2060
+6070
+f0f8
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 906 0
+DWIDTH 17 0
+BBX 15 13 1 0
+BITMAP
+0ffc
+078c
+0584
+0580
+0d80
+0988
+19f8
+1f88
+1180
+3180
+2182
+6186
+f7fe
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 11 17 1 -4
+BITMAP
+0f20
+30e0
+6060
+6020
+c000
+c000
+c000
+c000
+c000
+6000
+6060
+38c0
+0f00
+0400
+0200
+1600
+0c00
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+3000
+1800
+0400
+0000
+ff80
+6180
+6080
+6000
+6000
+6100
+7f00
+6100
+6000
+6000
+6080
+6180
+ff80
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+0600
+0c00
+1000
+0000
+ff80
+6180
+6080
+6000
+6000
+6100
+7f00
+6100
+6000
+6000
+6080
+6180
+ff80
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+0800
+1c00
+2200
+0000
+ff80
+6180
+6080
+6000
+6000
+6100
+7f00
+6100
+6000
+6000
+6080
+6180
+ff80
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 586 0
+DWIDTH 11 0
+BBX 9 16 1 0
+BITMAP
+3300
+3300
+0000
+ff80
+6180
+6080
+6000
+6000
+6100
+7f00
+6100
+6000
+6000
+6080
+6180
+ff80
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 17 1 0
+BITMAP
+c0
+60
+10
+00
+f0
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 4 17 1 0
+BITMAP
+30
+60
+80
+00
+f0
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 5 17 1 0
+BITMAP
+20
+70
+88
+00
+f0
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 320 0
+DWIDTH 6 0
+BBX 6 16 0 0
+BITMAP
+cc
+cc
+00
+78
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+78
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 12 13 0 0
+BITMAP
+7f80
+31c0
+3060
+3060
+3030
+3030
+fc30
+3030
+3030
+3060
+3060
+31c0
+7f80
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 16 1 0
+BITMAP
+0e80
+1700
+0000
+e0e0
+6040
+7040
+7840
+5840
+4c40
+4e40
+4740
+4340
+41c0
+41c0
+40c0
+e040
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 17 1 0
+BITMAP
+1800
+0c00
+0200
+0000
+0f00
+30c0
+6060
+6060
+c030
+c030
+c030
+c030
+c030
+6060
+6060
+30c0
+0f00
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 17 1 0
+BITMAP
+00c0
+0180
+0200
+0000
+0f00
+30c0
+6060
+6060
+c030
+c030
+c030
+c030
+c030
+6060
+6060
+30c0
+0f00
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 17 1 0
+BITMAP
+0200
+0700
+0880
+0000
+0f00
+30c0
+6060
+6060
+c030
+c030
+c030
+c030
+c030
+6060
+6060
+30c0
+0f00
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 16 1 0
+BITMAP
+0e80
+1700
+0000
+0f00
+30c0
+6060
+6060
+c030
+c030
+c030
+c030
+c030
+6060
+6060
+30c0
+0f00
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 16 1 0
+BITMAP
+1980
+1980
+0000
+0f00
+30c0
+6060
+6060
+c030
+c030
+c030
+c030
+c030
+6060
+6060
+30c0
+0f00
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 7 1 1
+BITMAP
+c3
+66
+3c
+18
+3c
+66
+c3
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 746 0
+DWIDTH 14 0
+BBX 12 15 1 -1
+BITMAP
+0030
+0f60
+30c0
+60e0
+61a0
+c330
+c330
+c630
+cc30
+cc30
+5860
+7060
+30c0
+6f00
+c000
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+1800
+0c00
+0200
+0000
+f0e0
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+70c0
+3980
+1f00
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+0180
+0300
+0400
+0000
+f0e0
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+70c0
+3980
+1f00
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+0400
+0e00
+1100
+0000
+f0e0
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+70c0
+3980
+1f00
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 693 0
+DWIDTH 13 0
+BBX 11 16 1 0
+BITMAP
+1980
+1980
+0000
+f0e0
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+6040
+70c0
+3980
+1f00
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 12 17 0 0
+BITMAP
+0180
+0300
+0400
+0000
+f0f0
+7060
+30c0
+1880
+1900
+0f00
+0600
+0600
+0600
+0600
+0600
+0600
+0f00
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 13 1 0
+BITMAP
+f000
+6000
+6000
+7f00
+6380
+6180
+6180
+6180
+6300
+7e00
+6000
+6000
+f000
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+1c
+36
+23
+63
+63
+66
+7c
+66
+63
+63
+6b
+6b
+ee
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+c0
+60
+10
+00
+78
+c8
+cc
+1c
+6c
+cc
+cc
+fc
+66
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+0c
+18
+20
+00
+78
+c8
+cc
+1c
+6c
+cc
+cc
+fc
+66
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+10
+38
+44
+00
+78
+c8
+cc
+1c
+6c
+cc
+cc
+fc
+66
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+34
+58
+00
+78
+c8
+cc
+1c
+6c
+cc
+cc
+fc
+66
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 12 1 0
+BITMAP
+6c
+6c
+00
+78
+c8
+cc
+1c
+6c
+cc
+cc
+fc
+66
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 7 13 1 0
+BITMAP
+30
+48
+48
+30
+78
+c8
+cc
+1c
+6c
+cc
+cc
+fc
+66
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 640 0
+DWIDTH 12 0
+BBX 11 9 0 0
+BITMAP
+7bc0
+ce60
+cc20
+1fe0
+6c00
+cc00
+cc00
+fe60
+63c0
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 7 13 0 -4
+BITMAP
+3c
+66
+c0
+c0
+c0
+c0
+c0
+66
+3c
+10
+08
+58
+30
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 7 13 0 0
+BITMAP
+60
+30
+08
+00
+3c
+66
+c2
+fe
+c0
+c0
+c0
+66
+3c
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 7 13 0 0
+BITMAP
+06
+0c
+10
+00
+3c
+66
+c2
+fe
+c0
+c0
+c0
+66
+3c
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 7 13 0 0
+BITMAP
+10
+38
+44
+00
+3c
+66
+c2
+fe
+c0
+c0
+c0
+66
+3c
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 7 12 0 0
+BITMAP
+6c
+6c
+00
+3c
+66
+c2
+fe
+c0
+c0
+c0
+66
+3c
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 4 13 0 0
+BITMAP
+c0
+60
+10
+00
+e0
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 4 13 0 0
+BITMAP
+30
+60
+80
+00
+e0
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 13 0 0
+BITMAP
+20
+70
+88
+00
+e0
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 266 0
+DWIDTH 5 0
+BBX 5 12 0 0
+BITMAP
+d8
+d8
+00
+e0
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+60
+36
+38
+4c
+3e
+66
+c3
+c3
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 9 12 0 0
+BITMAP
+3a00
+5c00
+0000
+e600
+6f00
+7300
+6300
+6300
+6300
+6300
+6300
+f380
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+60
+30
+08
+00
+3c
+66
+c3
+c3
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+06
+0c
+10
+00
+3c
+66
+c3
+c3
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 13 0 0
+BITMAP
+10
+38
+44
+00
+3c
+66
+c3
+c3
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+1a
+2c
+00
+3c
+66
+c3
+c3
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 12 0 0
+BITMAP
+6c
+6c
+00
+3c
+66
+c3
+c3
+c3
+c3
+c3
+66
+3c
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 533 0
+DWIDTH 10 0
+BBX 8 7 1 1
+BITMAP
+18
+18
+00
+ff
+00
+18
+18
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 11 0 -1
+BITMAP
+01
+3f
+66
+cf
+cb
+db
+d3
+f3
+66
+7c
+c0
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 13 0 0
+BITMAP
+3000
+1800
+0400
+0000
+e700
+6300
+6300
+6300
+6300
+6300
+6300
+7700
+3b80
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 13 0 0
+BITMAP
+0600
+0c00
+1000
+0000
+e700
+6300
+6300
+6300
+6300
+6300
+6300
+7700
+3b80
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 13 0 0
+BITMAP
+0800
+1c00
+2200
+0000
+e700
+6300
+6300
+6300
+6300
+6300
+6300
+7700
+3b80
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 9 12 0 0
+BITMAP
+3600
+3600
+0000
+e700
+6300
+6300
+6300
+6300
+6300
+6300
+7700
+3b80
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 8 17 -1 -4
+BITMAP
+03
+06
+08
+00
+f3
+63
+72
+32
+36
+1c
+1c
+0c
+08
+18
+10
+f0
+e0
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 480 0
+DWIDTH 9 0
+BBX 8 17 0 -4
+BITMAP
+e0
+60
+60
+60
+6e
+77
+63
+63
+63
+63
+63
+76
+7c
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 426 0
+DWIDTH 8 0
+BBX 8 16 -1 -4
+BITMAP
+36
+36
+00
+f3
+63
+72
+32
+36
+1c
+1c
+0c
+08
+18
+10
+f0
+e0
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times18.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times18.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times18.h	(revision 22322)
@@ -0,0 +1,980 @@
+static unsigned char times18_0_bits[] = {
+0x00};
+static unsigned char times18_1_bits[] = {
+0x00};
+static unsigned char times18_2_bits[] = {
+0x00};
+static unsigned char times18_3_bits[] = {
+0x00};
+static unsigned char times18_4_bits[] = {
+0x00};
+static unsigned char times18_5_bits[] = {
+0x00};
+static unsigned char times18_6_bits[] = {
+0x00};
+static unsigned char times18_7_bits[] = {
+0x00};
+static unsigned char times18_8_bits[] = {
+0x00};
+static unsigned char times18_9_bits[] = {
+0x00};
+static unsigned char times18_10_bits[] = {
+0x00};
+static unsigned char times18_11_bits[] = {
+0x00};
+static unsigned char times18_12_bits[] = {
+0x00};
+static unsigned char times18_13_bits[] = {
+0x00};
+static unsigned char times18_14_bits[] = {
+0x00};
+static unsigned char times18_15_bits[] = {
+0x00};
+static unsigned char times18_16_bits[] = {
+0x00};
+static unsigned char times18_17_bits[] = {
+0x00};
+static unsigned char times18_18_bits[] = {
+0x00};
+static unsigned char times18_19_bits[] = {
+0x00};
+static unsigned char times18_20_bits[] = {
+0x00};
+static unsigned char times18_21_bits[] = {
+0x00};
+static unsigned char times18_22_bits[] = {
+0x00};
+static unsigned char times18_23_bits[] = {
+0x00};
+static unsigned char times18_24_bits[] = {
+0x00};
+static unsigned char times18_25_bits[] = {
+0x00};
+static unsigned char times18_26_bits[] = {
+0x00};
+static unsigned char times18_27_bits[] = {
+0x00};
+static unsigned char times18_28_bits[] = {
+0x00};
+static unsigned char times18_29_bits[] = {
+0x00};
+static unsigned char times18_30_bits[] = {
+0x00};
+static unsigned char times18_31_bits[] = {
+0x00};
+static unsigned char times18_32_bits[] = {
+0x00};
+static unsigned char times18_33_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x00, 0x00, 0x03, 
+0x03};
+static unsigned char times18_34_bits[] = {
+0x33, 0x33, 0x33, 0x11};
+static unsigned char times18_35_bits[] = {
+0xb0, 0x01, 0xb0, 0x01, 0xb0, 0x01, 0xb0, 0x01, 0xfe, 0x07, 0xd8, 0x00, 
+0xd8, 0x00, 0xd8, 0x00, 0xff, 0x03, 0x6c, 0x00, 0x6c, 0x00, 0x6c, 0x00, 
+0x6c, 0x00};
+static unsigned char times18_36_bits[] = {
+0x08, 0x3e, 0x6b, 0x4b, 0x0b, 0x0f, 0x1e, 0x3c, 0x38, 0x68, 0x68, 0x69, 
+0x6b, 0x3e, 0x08, 0x08};
+static unsigned char times18_37_bits[] = {
+0x1c, 0x0c, 0xf6, 0x07, 0x23, 0x02, 0x23, 0x01, 0xb3, 0x01, 0xdf, 0x00, 
+0x4e, 0x0e, 0x60, 0x1b, 0xb0, 0x11, 0x90, 0x11, 0x98, 0x19, 0x8c, 0x0f, 
+0x04, 0x07};
+static unsigned char times18_38_bits[] = {
+0x38, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x2c, 0x00, 0x98, 0x07, 
+0x1c, 0x03, 0xb6, 0x01, 0xf3, 0x00, 0xe3, 0x00, 0xe3, 0x01, 0xb7, 0x0f, 
+0x1e, 0x07};
+static unsigned char times18_39_bits[] = {
+0x03, 0x07, 0x04, 0x02};
+static unsigned char times18_40_bits[] = {
+0x18, 0x0c, 0x06, 0x06, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x02, 0x06, 0x06, 0x0c, 0x18};
+static unsigned char times18_41_bits[] = {
+0x03, 0x06, 0x0c, 0x0c, 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x08, 0x0c, 0x0c, 0x06, 0x03};
+static unsigned char times18_42_bits[] = {
+0x08, 0x08, 0x6b, 0x2a, 0x1c, 0x6b, 0x49, 0x08};
+static unsigned char times18_43_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xff, 0x01, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char times18_44_bits[] = {
+0x06, 0x06, 0x04, 0x06, 0x03};
+static unsigned char times18_45_bits[] = {
+0xff};
+static unsigned char times18_46_bits[] = {
+0x03, 0x03};
+static unsigned char times18_47_bits[] = {
+0x10, 0x10, 0x18, 0x08, 0x08, 0x0c, 0x04, 0x04, 0x06, 0x02, 0x02, 0x03, 
+0x01, 0x01, 0x01};
+static unsigned char times18_48_bits[] = {
+0x3c, 0x66, 0x42, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x42, 0x66, 
+0x3c};
+static unsigned char times18_49_bits[] = {
+0x0c, 0x0e, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 
+0x3f};
+static unsigned char times18_50_bits[] = {
+0x3c, 0x7e, 0x73, 0x61, 0x60, 0x60, 0x30, 0x30, 0x18, 0x0c, 0x46, 0x7f, 
+0x7f};
+static unsigned char times18_51_bits[] = {
+0x3e, 0x73, 0x61, 0x60, 0x30, 0x1c, 0x3c, 0x70, 0x60, 0x60, 0x60, 0x33, 
+0x1f};
+static unsigned char times18_52_bits[] = {
+0x60, 0x70, 0x70, 0x68, 0x64, 0x66, 0x62, 0x61, 0xff, 0xff, 0x60, 0x60, 
+0x60};
+static unsigned char times18_53_bits[] = {
+0x7e, 0x3e, 0x03, 0x03, 0x0f, 0x3c, 0x30, 0x70, 0x60, 0x60, 0x30, 0x3b, 
+0x0f};
+static unsigned char times18_54_bits[] = {
+0xe0, 0x38, 0x0c, 0x06, 0x06, 0x3f, 0x63, 0xc3, 0xc3, 0xc3, 0xc7, 0x6e, 
+0x3c};
+static unsigned char times18_55_bits[] = {
+0x7f, 0x7f, 0x21, 0x30, 0x10, 0x18, 0x18, 0x08, 0x0c, 0x0c, 0x04, 0x06, 
+0x06};
+static unsigned char times18_56_bits[] = {
+0x3c, 0x66, 0x46, 0x46, 0x2e, 0x3c, 0x3c, 0x76, 0xe3, 0xc3, 0xc3, 0x66, 
+0x3c};
+static unsigned char times18_57_bits[] = {
+0x3c, 0x76, 0x63, 0xc3, 0xc3, 0xc3, 0xc7, 0xfe, 0x60, 0x60, 0x30, 0x1c, 
+0x07};
+static unsigned char times18_58_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03};
+static unsigned char times18_59_bits[] = {
+0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x04, 0x06, 0x03};
+static unsigned char times18_60_bits[] = {
+0x80, 0x01, 0xe0, 0x00, 0x38, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x0e, 0x00, 
+0x38, 0x00, 0xe0, 0x00, 0x80, 0x01};
+static unsigned char times18_61_bits[] = {
+0xff, 0x00, 0xff};
+static unsigned char times18_62_bits[] = {
+0x03, 0x00, 0x0e, 0x00, 0x38, 0x00, 0xe0, 0x00, 0x80, 0x01, 0xe0, 0x00, 
+0x38, 0x00, 0x0e, 0x00, 0x03, 0x00};
+static unsigned char times18_63_bits[] = {
+0x1e, 0x31, 0x23, 0x33, 0x30, 0x18, 0x18, 0x0c, 0x04, 0x04, 0x00, 0x0c, 
+0x0c};
+static unsigned char times18_64_bits[] = {
+0xe0, 0x07, 0x70, 0x0c, 0x1c, 0x18, 0x0c, 0x10, 0xc6, 0x37, 0xe6, 0x27, 
+0x63, 0x26, 0x33, 0x26, 0x33, 0x33, 0x33, 0x13, 0xb3, 0x1b, 0xe6, 0x0e, 
+0x06, 0x00, 0x0c, 0x00, 0x38, 0x0c, 0xe0, 0x07};
+static unsigned char times18_65_bits[] = {
+0x40, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xa0, 0x00, 0xb0, 0x01, 0x90, 0x01, 
+0x98, 0x03, 0x08, 0x03, 0xf8, 0x03, 0x0c, 0x07, 0x04, 0x06, 0x06, 0x0e, 
+0x0f, 0x1f};
+static unsigned char times18_66_bits[] = {
+0xff, 0x00, 0xc6, 0x01, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, 0xc6, 0x00, 
+0xfe, 0x01, 0x86, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x86, 0x01, 
+0xff, 0x00};
+static unsigned char times18_67_bits[] = {
+0xf0, 0x04, 0x0c, 0x07, 0x06, 0x06, 0x06, 0x04, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x06, 0x00, 0x06, 0x06, 0x1c, 0x03, 
+0xf0, 0x00};
+static unsigned char times18_68_bits[] = {
+0xff, 0x00, 0xc6, 0x01, 0x06, 0x03, 0x06, 0x03, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x03, 0x06, 0x03, 0xc6, 0x01, 
+0xff, 0x00};
+static unsigned char times18_69_bits[] = {
+0xff, 0x01, 0x86, 0x01, 0x06, 0x01, 0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 
+0xfe, 0x00, 0x86, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x01, 0x86, 0x01, 
+0xff, 0x01};
+static unsigned char times18_70_bits[] = {
+0xff, 0x01, 0x86, 0x01, 0x06, 0x01, 0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 
+0xfe, 0x00, 0x86, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 
+0x0f, 0x00};
+static unsigned char times18_71_bits[] = {
+0xf0, 0x04, 0x0c, 0x07, 0x06, 0x06, 0x06, 0x04, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x0f, 0x03, 0x06, 0x03, 0x06, 0x06, 0x06, 0x06, 0x06, 0x9c, 0x03, 
+0xf0, 0x00};
+static unsigned char times18_72_bits[] = {
+0x8f, 0x07, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0xfe, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x8f, 0x07};
+static unsigned char times18_73_bits[] = {
+0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x0f};
+static unsigned char times18_74_bits[] = {
+0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, 
+0x0f};
+static unsigned char times18_75_bits[] = {
+0xcf, 0x03, 0x86, 0x01, 0xc6, 0x00, 0x66, 0x00, 0x36, 0x00, 0x1e, 0x00, 
+0x1e, 0x00, 0x36, 0x00, 0x66, 0x00, 0xc6, 0x00, 0xc6, 0x01, 0x86, 0x03, 
+0x0f, 0x07};
+static unsigned char times18_76_bits[] = {
+0x0f, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 
+0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x02, 0x06, 0x03, 
+0xff, 0x03};
+static unsigned char times18_77_bits[] = {
+0x07, 0x38, 0x0e, 0x1c, 0x0e, 0x1c, 0x1e, 0x1e, 0x1a, 0x1a, 0x1a, 0x1b, 
+0x3a, 0x1b, 0x32, 0x19, 0xb2, 0x19, 0xe2, 0x18, 0xe2, 0x18, 0x42, 0x18, 
+0x47, 0x3c};
+static unsigned char times18_78_bits[] = {
+0x07, 0x07, 0x06, 0x02, 0x0e, 0x02, 0x1e, 0x02, 0x1a, 0x02, 0x32, 0x02, 
+0x72, 0x02, 0xe2, 0x02, 0xc2, 0x02, 0x82, 0x03, 0x82, 0x03, 0x02, 0x03, 
+0x07, 0x02};
+static unsigned char times18_79_bits[] = {
+0xf0, 0x00, 0x0c, 0x03, 0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c, 
+0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x06, 0x06, 0x06, 0x06, 0x0c, 0x03, 
+0xf0, 0x00};
+static unsigned char times18_80_bits[] = {
+0xff, 0x00, 0xc6, 0x01, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, 0xc6, 0x00, 
+0x7e, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 
+0x0f, 0x00};
+static unsigned char times18_81_bits[] = {
+0xf0, 0x00, 0x0c, 0x03, 0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c, 
+0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x06, 0x06, 0x06, 0x06, 0x0c, 0x03, 
+0xf0, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x80, 0x03, 0x00, 0x0f};
+static unsigned char times18_82_bits[] = {
+0xff, 0x00, 0xc6, 0x01, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, 0xc6, 0x00, 
+0x7e, 0x00, 0x66, 0x00, 0xc6, 0x00, 0x86, 0x01, 0x86, 0x01, 0x06, 0x03, 
+0x0f, 0x07};
+static unsigned char times18_83_bits[] = {
+0x5c, 0x66, 0x43, 0x03, 0x07, 0x1e, 0x3c, 0x70, 0xe0, 0xc0, 0xc1, 0x63, 
+0x3d};
+static unsigned char times18_84_bits[] = {
+0xff, 0x03, 0x33, 0x03, 0x31, 0x02, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 
+0x78, 0x00};
+static unsigned char times18_85_bits[] = {
+0x0f, 0x07, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 
+0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x0e, 0x03, 0x9c, 0x01, 
+0xf8, 0x00};
+static unsigned char times18_86_bits[] = {
+0x1f, 0x1e, 0x0e, 0x0c, 0x0c, 0x04, 0x1c, 0x04, 0x18, 0x06, 0x18, 0x02, 
+0x38, 0x02, 0x30, 0x03, 0x70, 0x01, 0x60, 0x01, 0xe0, 0x01, 0xc0, 0x00, 
+0xc0, 0x00};
+static unsigned char times18_87_bits[] = {
+0x9f, 0xc7, 0x03, 0x0c, 0x83, 0x01, 0x0c, 0x83, 0x00, 0x18, 0x83, 0x00, 
+0x18, 0xc6, 0x00, 0x18, 0x46, 0x00, 0x38, 0x47, 0x00, 0x30, 0x65, 0x00, 
+0x30, 0x2d, 0x00, 0xb0, 0x2c, 0x00, 0xe0, 0x3c, 0x00, 0x60, 0x18, 0x00, 
+0x60, 0x18, 0x00};
+static unsigned char times18_88_bits[] = {
+0x0f, 0x0e, 0x0e, 0x06, 0x1c, 0x03, 0x98, 0x01, 0xb0, 0x00, 0x70, 0x00, 
+0x60, 0x00, 0xf0, 0x00, 0xd8, 0x00, 0x88, 0x01, 0x8c, 0x03, 0x06, 0x07, 
+0x0f, 0x0f};
+static unsigned char times18_89_bits[] = {
+0x0f, 0x0f, 0x0e, 0x06, 0x0c, 0x03, 0x18, 0x01, 0x98, 0x00, 0xf0, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0xf0, 0x00};
+static unsigned char times18_90_bits[] = {
+0xfe, 0x03, 0x86, 0x03, 0x82, 0x01, 0xc0, 0x01, 0xe0, 0x00, 0x60, 0x00, 
+0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x0e, 0x02, 0x07, 0x03, 
+0xff, 0x03};
+static unsigned char times18_91_bits[] = {
+0x0f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x0f};
+static unsigned char times18_92_bits[] = {
+0x01, 0x01, 0x03, 0x02, 0x06, 0x06, 0x04, 0x0c, 0x0c, 0x08, 0x18, 0x10, 
+0x10};
+static unsigned char times18_93_bits[] = {
+0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0f};
+static unsigned char times18_94_bits[] = {
+0x08, 0x1c, 0x14, 0x36, 0x22, 0x63, 0x41};
+static unsigned char times18_95_bits[] = {
+0xff, 0x01};
+static unsigned char times18_96_bits[] = {
+0x02, 0x01, 0x07, 0x06};
+static unsigned char times18_97_bits[] = {
+0x1e, 0x33, 0x33, 0x38, 0x36, 0x33, 0x33, 0x3f, 0x66};
+static unsigned char times18_98_bits[] = {
+0x07, 0x06, 0x06, 0x06, 0x76, 0xee, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6e, 
+0x3c};
+static unsigned char times18_99_bits[] = {
+0x3c, 0x66, 0x63, 0x03, 0x03, 0x03, 0x03, 0x46, 0x3c};
+static unsigned char times18_100_bits[] = {
+0x70, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x63, 0x63, 0x63, 0x63, 0x63, 0x66, 
+0xfc};
+static unsigned char times18_101_bits[] = {
+0x3c, 0x66, 0xc3, 0xff, 0x03, 0x03, 0x87, 0x6e, 0x3c};
+static unsigned char times18_102_bits[] = {
+0x38, 0x2c, 0x04, 0x06, 0x1f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x0f};
+static unsigned char times18_103_bits[] = {
+0x7c, 0x33, 0x23, 0x23, 0x33, 0x1e, 0x02, 0x3e, 0xfe, 0xc1, 0x83, 0x47, 
+0x3e};
+static unsigned char times18_104_bits[] = {
+0x07, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0xf6, 0x00, 
+0xce, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 
+0xcf, 0x01};
+static unsigned char times18_105_bits[] = {
+0x06, 0x06, 0x00, 0x00, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x0f};
+static unsigned char times18_106_bits[] = {
+0x0c, 0x0c, 0x00, 0x00, 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x07, 0x03};
+static unsigned char times18_107_bits[] = {
+0x07, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0xe6, 0x00, 0x46, 0x00, 
+0x26, 0x00, 0x16, 0x00, 0x1e, 0x00, 0x36, 0x00, 0x66, 0x00, 0xc6, 0x00, 
+0xc7, 0x01};
+static unsigned char times18_108_bits[] = {
+0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x0f};
+static unsigned char times18_109_bits[] = {
+0x67, 0x0c, 0xf6, 0x1e, 0xce, 0x19, 0xc6, 0x18, 0xc6, 0x18, 0xc6, 0x18, 
+0xc6, 0x18, 0xc6, 0x18, 0xef, 0x3d};
+static unsigned char times18_110_bits[] = {
+0x67, 0x00, 0xf6, 0x00, 0xce, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 
+0xc6, 0x00, 0xc6, 0x00, 0xcf, 0x01};
+static unsigned char times18_111_bits[] = {
+0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c};
+static unsigned char times18_112_bits[] = {
+0x77, 0xee, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6e, 0x36, 0x06, 0x06, 0x06, 
+0x0f};
+static unsigned char times18_113_bits[] = {
+0x7c, 0x66, 0x63, 0x63, 0x63, 0x63, 0x63, 0x66, 0x7c, 0x60, 0x60, 0x60, 
+0xf0};
+static unsigned char times18_114_bits[] = {
+0x37, 0x36, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0f};
+static unsigned char times18_115_bits[] = {
+0x16, 0x1b, 0x13, 0x07, 0x0e, 0x1c, 0x19, 0x1b, 0x0d};
+static unsigned char times18_116_bits[] = {
+0x04, 0x06, 0x1f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x2e, 0x1c};
+static unsigned char times18_117_bits[] = {
+0xe7, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 
+0xc6, 0x00, 0xee, 0x00, 0xdc, 0x01};
+static unsigned char times18_118_bits[] = {
+0xcf, 0x01, 0xc6, 0x00, 0x46, 0x00, 0x4c, 0x00, 0x6c, 0x00, 0x28, 0x00, 
+0x38, 0x00, 0x10, 0x00, 0x10, 0x00};
+static unsigned char times18_119_bits[] = {
+0xe7, 0x1c, 0x66, 0x0c, 0x46, 0x06, 0x6c, 0x06, 0xec, 0x06, 0xb8, 0x02, 
+0xb8, 0x03, 0x10, 0x01, 0x10, 0x01};
+static unsigned char times18_120_bits[] = {
+0xc7, 0x01, 0xc6, 0x00, 0x6c, 0x00, 0x38, 0x00, 0x30, 0x00, 0x38, 0x00, 
+0x6c, 0x00, 0xc6, 0x00, 0xc7, 0x01};
+static unsigned char times18_121_bits[] = {
+0xcf, 0x01, 0x86, 0x00, 0xce, 0x00, 0x4c, 0x00, 0x6c, 0x00, 0x38, 0x00, 
+0x38, 0x00, 0x30, 0x00, 0x18, 0x00, 0x18, 0x00, 0x08, 0x00, 0x0f, 0x00, 
+0x07, 0x00};
+static unsigned char times18_122_bits[] = {
+0x7f, 0x73, 0x31, 0x18, 0x0c, 0x0c, 0x46, 0x67, 0x7f};
+static unsigned char times18_123_bits[] = {
+0x70, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x18, 0x70};
+static unsigned char times18_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
+0x01};
+static unsigned char times18_125_bits[] = {
+0x07, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x60, 0x30, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x0c, 0x07};
+static unsigned char times18_126_bits[] = {
+0x9e, 0x01, 0xf3, 0x00};
+static unsigned char times18_127_bits[] = {
+0x00};
+static unsigned char times18_128_bits[] = {
+0x00};
+static unsigned char times18_129_bits[] = {
+0x00};
+static unsigned char times18_130_bits[] = {
+0x00};
+static unsigned char times18_131_bits[] = {
+0x00};
+static unsigned char times18_132_bits[] = {
+0x00};
+static unsigned char times18_133_bits[] = {
+0x00};
+static unsigned char times18_134_bits[] = {
+0x00};
+static unsigned char times18_135_bits[] = {
+0x00};
+static unsigned char times18_136_bits[] = {
+0x00};
+static unsigned char times18_137_bits[] = {
+0x00};
+static unsigned char times18_138_bits[] = {
+0x00};
+static unsigned char times18_139_bits[] = {
+0x00};
+static unsigned char times18_140_bits[] = {
+0x00};
+static unsigned char times18_141_bits[] = {
+0x00};
+static unsigned char times18_142_bits[] = {
+0x00};
+static unsigned char times18_143_bits[] = {
+0x00};
+static unsigned char times18_144_bits[] = {
+0x00};
+static unsigned char times18_145_bits[] = {
+0x00};
+static unsigned char times18_146_bits[] = {
+0x00};
+static unsigned char times18_147_bits[] = {
+0x00};
+static unsigned char times18_148_bits[] = {
+0x00};
+static unsigned char times18_149_bits[] = {
+0x00};
+static unsigned char times18_150_bits[] = {
+0x00};
+static unsigned char times18_151_bits[] = {
+0x00};
+static unsigned char times18_152_bits[] = {
+0x00};
+static unsigned char times18_153_bits[] = {
+0x00};
+static unsigned char times18_154_bits[] = {
+0x00};
+static unsigned char times18_155_bits[] = {
+0x00};
+static unsigned char times18_156_bits[] = {
+0x00};
+static unsigned char times18_157_bits[] = {
+0x00};
+static unsigned char times18_158_bits[] = {
+0x00};
+static unsigned char times18_159_bits[] = {
+0x00};
+static unsigned char times18_160_bits[] = {
+0x00};
+static unsigned char times18_161_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03};
+static unsigned char times18_162_bits[] = {
+0x60, 0x20, 0x7c, 0xd6, 0xd3, 0x13, 0x1b, 0x0b, 0x0b, 0x4e, 0x3e, 0x02, 
+0x03};
+static unsigned char times18_163_bits[] = {
+0xf0, 0x00, 0x98, 0x01, 0x98, 0x01, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 
+0x7e, 0x00, 0x18, 0x00, 0x18, 0x00, 0x08, 0x00, 0x1e, 0x02, 0xfd, 0x03, 
+0xe7, 0x01};
+static unsigned char times18_164_bits[] = {
+0xbb, 0x01, 0xef, 0x01, 0xc6, 0x00, 0x82, 0x00, 0xc6, 0x00, 0xef, 0x01, 
+0xbb, 0x01};
+static unsigned char times18_165_bits[] = {
+0xef, 0x46, 0x46, 0x6e, 0x2c, 0x2c, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x18, 
+0x7e};
+static unsigned char times18_166_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 
+0x01};
+static unsigned char times18_167_bits[] = {
+0x3c, 0x66, 0x66, 0x0e, 0x1c, 0x3e, 0x71, 0xe3, 0xc7, 0x8e, 0x7c, 0x38, 
+0x70, 0x66, 0x66, 0x3c};
+static unsigned char times18_168_bits[] = {
+0x1b, 0x1b};
+static unsigned char times18_169_bits[] = {
+0xf0, 0x01, 0x0c, 0x06, 0x02, 0x08, 0xe2, 0x09, 0x11, 0x11, 0x09, 0x10, 
+0x09, 0x10, 0x09, 0x10, 0x11, 0x11, 0xe2, 0x08, 0x02, 0x08, 0x0c, 0x06, 
+0xf0, 0x01};
+static unsigned char times18_170_bits[] = {
+0x06, 0x09, 0x08, 0x0e, 0x09, 0x17, 0x00, 0x1f};
+static unsigned char times18_171_bits[] = {
+0x88, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x88};
+static unsigned char times18_172_bits[] = {
+0xff, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
+static unsigned char times18_173_bits[] = {
+0x1f, 0x1f};
+static unsigned char times18_174_bits[] = {
+0xf0, 0x01, 0x0c, 0x06, 0x02, 0x08, 0xfa, 0x08, 0x11, 0x11, 0x11, 0x11, 
+0xf1, 0x10, 0x91, 0x10, 0x11, 0x11, 0x3a, 0x0b, 0x02, 0x08, 0x0c, 0x06, 
+0xf0, 0x01};
+static unsigned char times18_175_bits[] = {
+0x1f};
+static unsigned char times18_176_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char times18_177_bits[] = {
+0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xff, 0x01, 0x10, 0x00, 
+0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0x01};
+static unsigned char times18_178_bits[] = {
+0x0e, 0x19, 0x18, 0x08, 0x04, 0x04, 0x02, 0x1f};
+static unsigned char times18_179_bits[] = {
+0x0e, 0x11, 0x18, 0x0e, 0x18, 0x10, 0x11, 0x0e};
+static unsigned char times18_180_bits[] = {
+0x0c, 0x06, 0x01};
+static unsigned char times18_181_bits[] = {
+0xe7, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 
+0xc6, 0x00, 0xee, 0x00, 0xde, 0x01, 0x02, 0x00, 0x02, 0x00, 0x06, 0x00, 
+0x06, 0x00};
+static unsigned char times18_182_bits[] = {
+0x7c, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2e, 0x28, 0x28, 0x28, 0x28, 
+0x28, 0x28, 0x28, 0x28, 0x28};
+static unsigned char times18_183_bits[] = {
+0x03, 0x03};
+static unsigned char times18_184_bits[] = {
+0x04, 0x04, 0x08, 0x0d, 0x06};
+static unsigned char times18_185_bits[] = {
+0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times18_186_bits[] = {
+0x0e, 0x1b, 0x11, 0x11, 0x1b, 0x0e, 0x00, 0x1f};
+static unsigned char times18_187_bits[] = {
+0x11, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x11};
+static unsigned char times18_188_bits[] = {
+0x02, 0x01, 0x83, 0x01, 0x82, 0x00, 0xc2, 0x00, 0x62, 0x00, 0x22, 0x02, 
+0x32, 0x03, 0x97, 0x02, 0xd8, 0x02, 0x4c, 0x02, 0xe4, 0x07, 0x06, 0x02, 
+0x02, 0x02};
+static unsigned char times18_189_bits[] = {
+0x02, 0x01, 0x83, 0x01, 0x82, 0x00, 0xc2, 0x00, 0x62, 0x00, 0xa2, 0x03, 
+0x72, 0x06, 0x17, 0x06, 0x18, 0x02, 0x0c, 0x01, 0x04, 0x01, 0x86, 0x00, 
+0xc2, 0x07};
+static unsigned char times18_190_bits[] = {
+0x0e, 0x04, 0x11, 0x06, 0x18, 0x02, 0x0e, 0x03, 0x98, 0x01, 0x90, 0x08, 
+0xd1, 0x0c, 0x4e, 0x0a, 0x60, 0x0b, 0x30, 0x09, 0x90, 0x1f, 0x18, 0x08, 
+0x08, 0x08};
+static unsigned char times18_191_bits[] = {
+0x0c, 0x0c, 0x00, 0x08, 0x08, 0x0c, 0x06, 0x06, 0x03, 0x33, 0x31, 0x23, 
+0x1e};
+static unsigned char times18_192_bits[] = {
+0x30, 0x00, 0x60, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0xe0, 0x00, 
+0xe0, 0x00, 0xa0, 0x00, 0xb0, 0x01, 0x90, 0x01, 0x98, 0x03, 0x08, 0x03, 
+0xf8, 0x03, 0x0c, 0x07, 0x04, 0x06, 0x06, 0x0e, 0x0f, 0x1f};
+static unsigned char times18_193_bits[] = {
+0x00, 0x03, 0x80, 0x01, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0xe0, 0x00, 
+0xe0, 0x00, 0xa0, 0x00, 0xb0, 0x01, 0x90, 0x01, 0x98, 0x03, 0x08, 0x03, 
+0xf8, 0x03, 0x0c, 0x07, 0x04, 0x06, 0x06, 0x0e, 0x0f, 0x1f};
+static unsigned char times18_194_bits[] = {
+0x40, 0x00, 0xe0, 0x00, 0x10, 0x01, 0x00, 0x00, 0x40, 0x00, 0xe0, 0x00, 
+0xe0, 0x00, 0xa0, 0x00, 0xb0, 0x01, 0x90, 0x01, 0x98, 0x03, 0x08, 0x03, 
+0xf8, 0x03, 0x0c, 0x07, 0x04, 0x06, 0x06, 0x0e, 0x0f, 0x1f};
+static unsigned char times18_195_bits[] = {
+0xe0, 0x02, 0xd0, 0x01, 0x00, 0x00, 0x40, 0x00, 0xe0, 0x00, 0xe0, 0x00, 
+0xa0, 0x00, 0xb0, 0x01, 0x90, 0x01, 0x98, 0x03, 0x08, 0x03, 0xf8, 0x03, 
+0x0c, 0x07, 0x04, 0x06, 0x06, 0x0e, 0x0f, 0x1f};
+static unsigned char times18_196_bits[] = {
+0xb0, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x40, 0x00, 0xe0, 0x00, 0xe0, 0x00, 
+0xa0, 0x00, 0xb0, 0x01, 0x90, 0x01, 0x98, 0x03, 0x08, 0x03, 0xf8, 0x03, 
+0x0c, 0x07, 0x04, 0x06, 0x06, 0x0e, 0x0f, 0x1f};
+static unsigned char times18_197_bits[] = {
+0xc0, 0x00, 0x20, 0x01, 0x20, 0x01, 0xc0, 0x00, 0x40, 0x00, 0xe0, 0x00, 
+0xe0, 0x00, 0xa0, 0x00, 0xb0, 0x01, 0x90, 0x01, 0x98, 0x03, 0x08, 0x03, 
+0xf8, 0x03, 0x0c, 0x07, 0x04, 0x06, 0x06, 0x0e, 0x0f, 0x1f};
+static unsigned char times18_198_bits[] = {
+0xf0, 0x3f, 0xe0, 0x31, 0xa0, 0x21, 0xa0, 0x01, 0xb0, 0x01, 0x90, 0x11, 
+0x98, 0x1f, 0xf8, 0x11, 0x88, 0x01, 0x8c, 0x01, 0x84, 0x41, 0x86, 0x61, 
+0xef, 0x7f};
+static unsigned char times18_199_bits[] = {
+0xf0, 0x04, 0x0c, 0x07, 0x06, 0x06, 0x06, 0x04, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x06, 0x00, 0x06, 0x06, 0x1c, 0x03, 
+0xf0, 0x00, 0x20, 0x00, 0x40, 0x00, 0x68, 0x00, 0x30, 0x00};
+static unsigned char times18_200_bits[] = {
+0x0c, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x00, 0xff, 0x01, 0x86, 0x01, 
+0x06, 0x01, 0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 0xfe, 0x00, 0x86, 0x00, 
+0x06, 0x00, 0x06, 0x00, 0x06, 0x01, 0x86, 0x01, 0xff, 0x01};
+static unsigned char times18_201_bits[] = {
+0x60, 0x00, 0x30, 0x00, 0x08, 0x00, 0x00, 0x00, 0xff, 0x01, 0x86, 0x01, 
+0x06, 0x01, 0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 0xfe, 0x00, 0x86, 0x00, 
+0x06, 0x00, 0x06, 0x00, 0x06, 0x01, 0x86, 0x01, 0xff, 0x01};
+static unsigned char times18_202_bits[] = {
+0x10, 0x00, 0x38, 0x00, 0x44, 0x00, 0x00, 0x00, 0xff, 0x01, 0x86, 0x01, 
+0x06, 0x01, 0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 0xfe, 0x00, 0x86, 0x00, 
+0x06, 0x00, 0x06, 0x00, 0x06, 0x01, 0x86, 0x01, 0xff, 0x01};
+static unsigned char times18_203_bits[] = {
+0xcc, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xff, 0x01, 0x86, 0x01, 0x06, 0x01, 
+0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 0xfe, 0x00, 0x86, 0x00, 0x06, 0x00, 
+0x06, 0x00, 0x06, 0x01, 0x86, 0x01, 0xff, 0x01};
+static unsigned char times18_204_bits[] = {
+0x03, 0x06, 0x08, 0x00, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06, 0x06, 0x06, 0x0f};
+static unsigned char times18_205_bits[] = {
+0x0c, 0x06, 0x01, 0x00, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06, 0x06, 0x06, 0x0f};
+static unsigned char times18_206_bits[] = {
+0x04, 0x0e, 0x11, 0x00, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06, 0x06, 0x06, 0x0f};
+static unsigned char times18_207_bits[] = {
+0x33, 0x33, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x1e};
+static unsigned char times18_208_bits[] = {
+0xfe, 0x01, 0x8c, 0x03, 0x0c, 0x06, 0x0c, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x0c, 0x06, 0x8c, 0x03, 
+0xfe, 0x01};
+static unsigned char times18_209_bits[] = {
+0x70, 0x01, 0xe8, 0x00, 0x00, 0x00, 0x07, 0x07, 0x06, 0x02, 0x0e, 0x02, 
+0x1e, 0x02, 0x1a, 0x02, 0x32, 0x02, 0x72, 0x02, 0xe2, 0x02, 0xc2, 0x02, 
+0x82, 0x03, 0x82, 0x03, 0x02, 0x03, 0x07, 0x02};
+static unsigned char times18_210_bits[] = {
+0x18, 0x00, 0x30, 0x00, 0x40, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0c, 0x03, 
+0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 
+0x03, 0x0c, 0x06, 0x06, 0x06, 0x06, 0x0c, 0x03, 0xf0, 0x00};
+static unsigned char times18_211_bits[] = {
+0x00, 0x03, 0x80, 0x01, 0x40, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0c, 0x03, 
+0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 
+0x03, 0x0c, 0x06, 0x06, 0x06, 0x06, 0x0c, 0x03, 0xf0, 0x00};
+static unsigned char times18_212_bits[] = {
+0x40, 0x00, 0xe0, 0x00, 0x10, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x0c, 0x03, 
+0x06, 0x06, 0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 
+0x03, 0x0c, 0x06, 0x06, 0x06, 0x06, 0x0c, 0x03, 0xf0, 0x00};
+static unsigned char times18_213_bits[] = {
+0x70, 0x01, 0xe8, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0c, 0x03, 0x06, 0x06, 
+0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 
+0x06, 0x06, 0x06, 0x06, 0x0c, 0x03, 0xf0, 0x00};
+static unsigned char times18_214_bits[] = {
+0x98, 0x01, 0x98, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x0c, 0x03, 0x06, 0x06, 
+0x06, 0x06, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 0x03, 0x0c, 
+0x06, 0x06, 0x06, 0x06, 0x0c, 0x03, 0xf0, 0x00};
+static unsigned char times18_215_bits[] = {
+0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3};
+static unsigned char times18_216_bits[] = {
+0x00, 0x0c, 0xf0, 0x06, 0x0c, 0x03, 0x06, 0x07, 0x86, 0x05, 0xc3, 0x0c, 
+0xc3, 0x0c, 0x63, 0x0c, 0x33, 0x0c, 0x33, 0x0c, 0x1a, 0x06, 0x0e, 0x06, 
+0x0c, 0x03, 0xf6, 0x00, 0x03, 0x00};
+static unsigned char times18_217_bits[] = {
+0x18, 0x00, 0x30, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0f, 0x07, 0x06, 0x02, 
+0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 
+0x06, 0x02, 0x06, 0x02, 0x0e, 0x03, 0x9c, 0x01, 0xf8, 0x00};
+static unsigned char times18_218_bits[] = {
+0x80, 0x01, 0xc0, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0f, 0x07, 0x06, 0x02, 
+0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 
+0x06, 0x02, 0x06, 0x02, 0x0e, 0x03, 0x9c, 0x01, 0xf8, 0x00};
+static unsigned char times18_219_bits[] = {
+0x20, 0x00, 0x70, 0x00, 0x88, 0x00, 0x00, 0x00, 0x0f, 0x07, 0x06, 0x02, 
+0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 
+0x06, 0x02, 0x06, 0x02, 0x0e, 0x03, 0x9c, 0x01, 0xf8, 0x00};
+static unsigned char times18_220_bits[] = {
+0x98, 0x01, 0x98, 0x01, 0x00, 0x00, 0x0f, 0x07, 0x06, 0x02, 0x06, 0x02, 
+0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 0x06, 0x02, 
+0x06, 0x02, 0x0e, 0x03, 0x9c, 0x01, 0xf8, 0x00};
+static unsigned char times18_221_bits[] = {
+0x80, 0x01, 0xc0, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0e, 0x06, 
+0x0c, 0x03, 0x18, 0x01, 0x98, 0x00, 0xf0, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xf0, 0x00};
+static unsigned char times18_222_bits[] = {
+0x0f, 0x00, 0x06, 0x00, 0x06, 0x00, 0xfe, 0x00, 0xc6, 0x01, 0x86, 0x01, 
+0x86, 0x01, 0x86, 0x01, 0xc6, 0x00, 0x7e, 0x00, 0x06, 0x00, 0x06, 0x00, 
+0x0f, 0x00};
+static unsigned char times18_223_bits[] = {
+0x38, 0x6c, 0xc4, 0xc6, 0xc6, 0x66, 0x3e, 0x66, 0xc6, 0xc6, 0xd6, 0xd6, 
+0x77};
+static unsigned char times18_224_bits[] = {
+0x03, 0x06, 0x08, 0x00, 0x1e, 0x13, 0x33, 0x38, 0x36, 0x33, 0x33, 0x3f, 
+0x66};
+static unsigned char times18_225_bits[] = {
+0x30, 0x18, 0x04, 0x00, 0x1e, 0x13, 0x33, 0x38, 0x36, 0x33, 0x33, 0x3f, 
+0x66};
+static unsigned char times18_226_bits[] = {
+0x08, 0x1c, 0x22, 0x00, 0x1e, 0x13, 0x33, 0x38, 0x36, 0x33, 0x33, 0x3f, 
+0x66};
+static unsigned char times18_227_bits[] = {
+0x2c, 0x1a, 0x00, 0x1e, 0x13, 0x33, 0x38, 0x36, 0x33, 0x33, 0x3f, 0x66};
+static unsigned char times18_228_bits[] = {
+0x36, 0x36, 0x00, 0x1e, 0x13, 0x33, 0x38, 0x36, 0x33, 0x33, 0x3f, 0x66};
+static unsigned char times18_229_bits[] = {
+0x0c, 0x12, 0x12, 0x0c, 0x1e, 0x13, 0x33, 0x38, 0x36, 0x33, 0x33, 0x3f, 
+0x66};
+static unsigned char times18_230_bits[] = {
+0xde, 0x03, 0x73, 0x06, 0x33, 0x04, 0xf8, 0x07, 0x36, 0x00, 0x33, 0x00, 
+0x33, 0x00, 0x7f, 0x06, 0xc6, 0x03};
+static unsigned char times18_231_bits[] = {
+0x3c, 0x66, 0x03, 0x03, 0x03, 0x03, 0x03, 0x66, 0x3c, 0x08, 0x10, 0x1a, 
+0x0c};
+static unsigned char times18_232_bits[] = {
+0x06, 0x0c, 0x10, 0x00, 0x3c, 0x66, 0x43, 0x7f, 0x03, 0x03, 0x03, 0x66, 
+0x3c};
+static unsigned char times18_233_bits[] = {
+0x60, 0x30, 0x08, 0x00, 0x3c, 0x66, 0x43, 0x7f, 0x03, 0x03, 0x03, 0x66, 
+0x3c};
+static unsigned char times18_234_bits[] = {
+0x08, 0x1c, 0x22, 0x00, 0x3c, 0x66, 0x43, 0x7f, 0x03, 0x03, 0x03, 0x66, 
+0x3c};
+static unsigned char times18_235_bits[] = {
+0x36, 0x36, 0x00, 0x3c, 0x66, 0x43, 0x7f, 0x03, 0x03, 0x03, 0x66, 0x3c};
+static unsigned char times18_236_bits[] = {
+0x03, 0x06, 0x08, 0x00, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x0f};
+static unsigned char times18_237_bits[] = {
+0x0c, 0x06, 0x01, 0x00, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x0f};
+static unsigned char times18_238_bits[] = {
+0x04, 0x0e, 0x11, 0x00, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x0f};
+static unsigned char times18_239_bits[] = {
+0x1b, 0x1b, 0x00, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0f};
+static unsigned char times18_240_bits[] = {
+0x06, 0x6c, 0x1c, 0x32, 0x7c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 
+0x3c};
+static unsigned char times18_241_bits[] = {
+0x5c, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x67, 0x00, 0xf6, 0x00, 0xce, 0x00, 
+0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xcf, 0x01};
+static unsigned char times18_242_bits[] = {
+0x06, 0x0c, 0x10, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 
+0x3c};
+static unsigned char times18_243_bits[] = {
+0x60, 0x30, 0x08, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 
+0x3c};
+static unsigned char times18_244_bits[] = {
+0x08, 0x1c, 0x22, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 
+0x3c};
+static unsigned char times18_245_bits[] = {
+0x58, 0x34, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c};
+static unsigned char times18_246_bits[] = {
+0x36, 0x36, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c};
+static unsigned char times18_247_bits[] = {
+0x18, 0x18, 0x00, 0xff, 0x00, 0x18, 0x18};
+static unsigned char times18_248_bits[] = {
+0x80, 0xfc, 0x66, 0xf3, 0xd3, 0xdb, 0xcb, 0xcf, 0x66, 0x3e, 0x03};
+static unsigned char times18_249_bits[] = {
+0x0c, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x00, 0xe7, 0x00, 0xc6, 0x00, 
+0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xee, 0x00, 
+0xdc, 0x01};
+static unsigned char times18_250_bits[] = {
+0x60, 0x00, 0x30, 0x00, 0x08, 0x00, 0x00, 0x00, 0xe7, 0x00, 0xc6, 0x00, 
+0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xee, 0x00, 
+0xdc, 0x01};
+static unsigned char times18_251_bits[] = {
+0x10, 0x00, 0x38, 0x00, 0x44, 0x00, 0x00, 0x00, 0xe7, 0x00, 0xc6, 0x00, 
+0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xee, 0x00, 
+0xdc, 0x01};
+static unsigned char times18_252_bits[] = {
+0x6c, 0x00, 0x6c, 0x00, 0x00, 0x00, 0xe7, 0x00, 0xc6, 0x00, 0xc6, 0x00, 
+0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xee, 0x00, 0xdc, 0x01};
+static unsigned char times18_253_bits[] = {
+0xc0, 0x60, 0x10, 0x00, 0xcf, 0xc6, 0x4e, 0x4c, 0x6c, 0x38, 0x38, 0x30, 
+0x10, 0x18, 0x08, 0x0f, 0x07};
+static unsigned char times18_254_bits[] = {
+0x07, 0x06, 0x06, 0x06, 0x76, 0xee, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6e, 
+0x3e, 0x06, 0x06, 0x06, 0x0f};
+static unsigned char times18_255_bits[] = {
+0x6c, 0x6c, 0x00, 0xcf, 0xc6, 0x4e, 0x4c, 0x6c, 0x38, 0x38, 0x30, 0x10, 
+0x18, 0x08, 0x0f, 0x07};
+static RotFont times18font[] = {
+{5, 1, 1, times18_0_bits},
+{5, 1, 1, times18_1_bits},
+{5, 1, 1, times18_2_bits},
+{5, 1, 1, times18_3_bits},
+{5, 1, 1, times18_4_bits},
+{5, 1, 1, times18_5_bits},
+{5, 1, 1, times18_6_bits},
+{5, 1, 1, times18_7_bits},
+{5, 1, 1, times18_8_bits},
+{5, 1, 1, times18_9_bits},
+{5, 1, 1, times18_10_bits},
+{5, 1, 1, times18_11_bits},
+{5, 1, 1, times18_12_bits},
+{5, 1, 1, times18_13_bits},
+{5, 1, 1, times18_14_bits},
+{5, 1, 1, times18_15_bits},
+{5, 1, 1, times18_16_bits},
+{5, 1, 1, times18_17_bits},
+{5, 1, 1, times18_18_bits},
+{5, 1, 1, times18_19_bits},
+{5, 1, 1, times18_20_bits},
+{5, 1, 1, times18_21_bits},
+{5, 1, 1, times18_22_bits},
+{5, 1, 1, times18_23_bits},
+{5, 1, 1, times18_24_bits},
+{5, 1, 1, times18_25_bits},
+{5, 1, 1, times18_26_bits},
+{5, 1, 1, times18_27_bits},
+{5, 1, 1, times18_28_bits},
+{5, 1, 1, times18_29_bits},
+{5, 1, 1, times18_30_bits},
+{5, 1, 1, times18_31_bits},
+{8, 1, 1, times18_32_bits},
+{2, 13, 13, times18_33_bits},
+{6, 4, 13, times18_34_bits},
+{11, 13, 13, times18_35_bits},
+{7, 16, 14, times18_36_bits},
+{13, 13, 13, times18_37_bits},
+{12, 13, 13, times18_38_bits},
+{3, 4, 13, times18_39_bits},
+{5, 17, 13, times18_40_bits},
+{5, 17, 13, times18_41_bits},
+{7, 8, 14, times18_42_bits},
+{9, 9, 9, times18_43_bits},
+{3, 5, 2, times18_44_bits},
+{8, 1, 5, times18_45_bits},
+{2, 2, 2, times18_46_bits},
+{5, 15, 13, times18_47_bits},
+{8, 13, 13, times18_48_bits},
+{6, 13, 13, times18_49_bits},
+{7, 13, 13, times18_50_bits},
+{7, 13, 13, times18_51_bits},
+{8, 13, 13, times18_52_bits},
+{7, 13, 13, times18_53_bits},
+{8, 13, 13, times18_54_bits},
+{7, 13, 13, times18_55_bits},
+{8, 13, 13, times18_56_bits},
+{8, 13, 13, times18_57_bits},
+{2, 9, 9, times18_58_bits},
+{3, 12, 9, times18_59_bits},
+{9, 9, 9, times18_60_bits},
+{8, 3, 6, times18_61_bits},
+{9, 9, 9, times18_62_bits},
+{6, 13, 13, times18_63_bits},
+{14, 16, 13, times18_64_bits},
+{13, 13, 13, times18_65_bits},
+{10, 13, 13, times18_66_bits},
+{11, 13, 13, times18_67_bits},
+{11, 13, 13, times18_68_bits},
+{9, 13, 13, times18_69_bits},
+{9, 13, 13, times18_70_bits},
+{12, 13, 13, times18_71_bits},
+{11, 13, 13, times18_72_bits},
+{4, 13, 13, times18_73_bits},
+{6, 13, 13, times18_74_bits},
+{11, 13, 13, times18_75_bits},
+{10, 13, 13, times18_76_bits},
+{14, 13, 13, times18_77_bits},
+{11, 13, 13, times18_78_bits},
+{12, 13, 13, times18_79_bits},
+{9, 13, 13, times18_80_bits},
+{12, 17, 13, times18_81_bits},
+{11, 13, 13, times18_82_bits},
+{8, 13, 13, times18_83_bits},
+{10, 13, 13, times18_84_bits},
+{11, 13, 13, times18_85_bits},
+{13, 13, 13, times18_86_bits},
+{18, 13, 13, times18_87_bits},
+{12, 13, 13, times18_88_bits},
+{12, 13, 13, times18_89_bits},
+{10, 13, 13, times18_90_bits},
+{4, 16, 13, times18_91_bits},
+{5, 13, 13, times18_92_bits},
+{4, 16, 13, times18_93_bits},
+{7, 7, 13, times18_94_bits},
+{9, 1, -3, times18_95_bits},
+{3, 4, 13, times18_96_bits},
+{7, 9, 9, times18_97_bits},
+{8, 13, 13, times18_98_bits},
+{7, 9, 9, times18_99_bits},
+{8, 13, 13, times18_100_bits},
+{8, 9, 9, times18_101_bits},
+{6, 13, 13, times18_102_bits},
+{8, 13, 9, times18_103_bits},
+{9, 13, 13, times18_104_bits},
+{4, 13, 13, times18_105_bits},
+{4, 17, 13, times18_106_bits},
+{9, 13, 13, times18_107_bits},
+{4, 13, 13, times18_108_bits},
+{14, 9, 9, times18_109_bits},
+{9, 9, 9, times18_110_bits},
+{8, 9, 9, times18_111_bits},
+{8, 13, 9, times18_112_bits},
+{8, 13, 9, times18_113_bits},
+{6, 9, 9, times18_114_bits},
+{5, 9, 9, times18_115_bits},
+{6, 11, 11, times18_116_bits},
+{9, 9, 9, times18_117_bits},
+{9, 9, 9, times18_118_bits},
+{13, 9, 9, times18_119_bits},
+{9, 9, 9, times18_120_bits},
+{9, 13, 9, times18_121_bits},
+{7, 9, 9, times18_122_bits},
+{7, 17, 13, times18_123_bits},
+{1, 13, 13, times18_124_bits},
+{7, 17, 13, times18_125_bits},
+{9, 2, 6, times18_126_bits},
+{5, 1, 1, times18_127_bits},
+{5, 1, 1, times18_128_bits},
+{5, 1, 1, times18_129_bits},
+{5, 1, 1, times18_130_bits},
+{5, 1, 1, times18_131_bits},
+{5, 1, 1, times18_132_bits},
+{5, 1, 1, times18_133_bits},
+{5, 1, 1, times18_134_bits},
+{5, 1, 1, times18_135_bits},
+{5, 1, 1, times18_136_bits},
+{5, 1, 1, times18_137_bits},
+{5, 1, 1, times18_138_bits},
+{5, 1, 1, times18_139_bits},
+{5, 1, 1, times18_140_bits},
+{5, 1, 1, times18_141_bits},
+{5, 1, 1, times18_142_bits},
+{5, 1, 1, times18_143_bits},
+{5, 1, 1, times18_144_bits},
+{5, 1, 1, times18_145_bits},
+{5, 1, 1, times18_146_bits},
+{5, 1, 1, times18_147_bits},
+{5, 1, 1, times18_148_bits},
+{5, 1, 1, times18_149_bits},
+{5, 1, 1, times18_150_bits},
+{5, 1, 1, times18_151_bits},
+{5, 1, 1, times18_152_bits},
+{5, 1, 1, times18_153_bits},
+{5, 1, 1, times18_154_bits},
+{5, 1, 1, times18_155_bits},
+{5, 1, 1, times18_156_bits},
+{5, 1, 1, times18_157_bits},
+{5, 1, 1, times18_158_bits},
+{5, 1, 1, times18_159_bits},
+{1, 1, 1, times18_160_bits},
+{2, 13, 9, times18_161_bits},
+{8, 13, 11, times18_162_bits},
+{10, 13, 13, times18_163_bits},
+{9, 7, 10, times18_164_bits},
+{8, 13, 13, times18_165_bits},
+{1, 13, 13, times18_166_bits},
+{8, 16, 13, times18_167_bits},
+{5, 2, 12, times18_168_bits},
+{13, 13, 13, times18_169_bits},
+{5, 8, 13, times18_170_bits},
+{8, 7, 8, times18_171_bits},
+{9, 5, 6, times18_172_bits},
+{5, 2, 6, times18_173_bits},
+{13, 13, 13, times18_174_bits},
+{5, 1, 12, times18_175_bits},
+{5, 5, 13, times18_176_bits},
+{9, 10, 10, times18_177_bits},
+{5, 8, 13, times18_178_bits},
+{5, 8, 13, times18_179_bits},
+{4, 3, 13, times18_180_bits},
+{9, 13, 9, times18_181_bits},
+{7, 17, 13, times18_182_bits},
+{2, 2, 6, times18_183_bits},
+{4, 5, 1, times18_184_bits},
+{3, 8, 13, times18_185_bits},
+{5, 8, 13, times18_186_bits},
+{8, 7, 8, times18_187_bits},
+{11, 13, 13, times18_188_bits},
+{11, 13, 13, times18_189_bits},
+{13, 13, 13, times18_190_bits},
+{6, 13, 9, times18_191_bits},
+{13, 17, 17, times18_192_bits},
+{13, 17, 17, times18_193_bits},
+{13, 17, 17, times18_194_bits},
+{13, 16, 16, times18_195_bits},
+{13, 16, 16, times18_196_bits},
+{13, 17, 17, times18_197_bits},
+{15, 13, 13, times18_198_bits},
+{11, 17, 13, times18_199_bits},
+{9, 17, 17, times18_200_bits},
+{9, 17, 17, times18_201_bits},
+{9, 17, 17, times18_202_bits},
+{9, 16, 16, times18_203_bits},
+{4, 17, 17, times18_204_bits},
+{4, 17, 17, times18_205_bits},
+{5, 17, 17, times18_206_bits},
+{6, 16, 16, times18_207_bits},
+{12, 13, 13, times18_208_bits},
+{11, 16, 16, times18_209_bits},
+{12, 17, 17, times18_210_bits},
+{12, 17, 17, times18_211_bits},
+{12, 17, 17, times18_212_bits},
+{12, 16, 16, times18_213_bits},
+{12, 16, 16, times18_214_bits},
+{8, 7, 8, times18_215_bits},
+{12, 15, 14, times18_216_bits},
+{11, 17, 17, times18_217_bits},
+{11, 17, 17, times18_218_bits},
+{11, 17, 17, times18_219_bits},
+{11, 16, 16, times18_220_bits},
+{12, 17, 17, times18_221_bits},
+{9, 13, 13, times18_222_bits},
+{8, 13, 13, times18_223_bits},
+{7, 13, 13, times18_224_bits},
+{7, 13, 13, times18_225_bits},
+{7, 13, 13, times18_226_bits},
+{7, 12, 12, times18_227_bits},
+{7, 12, 12, times18_228_bits},
+{7, 13, 13, times18_229_bits},
+{11, 9, 9, times18_230_bits},
+{7, 13, 9, times18_231_bits},
+{7, 13, 13, times18_232_bits},
+{7, 13, 13, times18_233_bits},
+{7, 13, 13, times18_234_bits},
+{7, 12, 12, times18_235_bits},
+{4, 13, 13, times18_236_bits},
+{4, 13, 13, times18_237_bits},
+{5, 13, 13, times18_238_bits},
+{5, 12, 12, times18_239_bits},
+{8, 13, 13, times18_240_bits},
+{9, 12, 12, times18_241_bits},
+{8, 13, 13, times18_242_bits},
+{8, 13, 13, times18_243_bits},
+{8, 13, 13, times18_244_bits},
+{8, 12, 12, times18_245_bits},
+{8, 12, 12, times18_246_bits},
+{8, 7, 8, times18_247_bits},
+{8, 11, 10, times18_248_bits},
+{9, 13, 13, times18_249_bits},
+{9, 13, 13, times18_250_bits},
+{9, 13, 13, times18_251_bits},
+{9, 12, 12, times18_252_bits},
+{8, 17, 13, times18_253_bits},
+{8, 17, 13, times18_254_bits},
+{8, 16, 12, times18_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times24.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times24.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times24.bdf	(revision 22322)
@@ -0,0 +1,4377 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Times-Medium-R-Normal--24-240-75-75-P-124-ISO8859-1
+SIZE 24 75 75
+FONTBOUNDINGBOX 24 28 -1 -6
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Times"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 24
+POINT_SIZE 240
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 124
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 17
+X_HEIGHT 12
+FACE_NAME "Times Roman"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "Times is a trademark of Linotype-Hell AG and/or its subsidiaries."
+_DEC_DEVICE_FONTNAMES "PS=Times-Roman"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2-1, 22-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Times Roman"
+FONT "-Adobe-Times-Medium-R-Normal--24-240-75-75-P-124-ISO8859-1"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 14
+DEFAULT_CHAR 32
+FONT_ASCENT 20
+FONT_DESCENT 6
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 2 17 3 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+00
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 6 5 1 12
+BITMAP
+cc
+cc
+cc
+cc
+88
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+0880
+0880
+0880
+0880
+0880
+7fe0
+7fe0
+1100
+1100
+1100
+ffc0
+ffc0
+2200
+2200
+2200
+2200
+2200
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 11 21 0 -2
+BITMAP
+0400
+0400
+1f80
+34e0
+6460
+6420
+6400
+7400
+3c00
+1e00
+0780
+07c0
+04e0
+0460
+8460
+8460
+c4c0
+e5c0
+3f00
+0400
+0400
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 17 16 1 0
+BITMAP
+1e0c00
+33fc00
+611800
+c11000
+c13000
+c26000
+e44000
+78c000
+018f00
+011980
+033080
+066080
+046080
+0c6100
+187200
+303c00
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+0780
+0cc0
+1840
+1840
+18c0
+1d80
+0f00
+0e3e
+1e18
+3710
+6320
+c1a0
+c1c0
+c0c0
+e1e1
+7f7e
+3c3c
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 3 5 3 12
+BITMAP
+c0
+e0
+20
+60
+c0
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 22 1 -5
+BITMAP
+04
+08
+10
+30
+20
+60
+60
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+60
+60
+20
+30
+10
+08
+04
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 22 1 -5
+BITMAP
+80
+40
+20
+30
+10
+18
+18
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+18
+18
+10
+30
+20
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 9 9 2 8
+BITMAP
+0800
+1c00
+c980
+eb80
+1c00
+eb80
+c980
+1c00
+0800
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 12 1 1
+BITMAP
+0600
+0600
+0600
+0600
+0600
+fff0
+fff0
+0600
+0600
+0600
+0600
+0600
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 3 5 2 -3
+BITMAP
+c0
+e0
+20
+60
+c0
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 2 1 6
+BITMAP
+fff0
+fff0
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 2 2 0
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 8 20 -1 -3
+BITMAP
+03
+03
+03
+06
+06
+04
+0c
+0c
+08
+18
+18
+10
+30
+30
+20
+60
+60
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1e00
+3300
+6180
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+e1c0
+6180
+6180
+3300
+1e00
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 8 17 2 0
+BITMAP
+08
+18
+78
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+ff
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1c00
+7f00
+4380
+8180
+8180
+0180
+0180
+0300
+0300
+0600
+0400
+0c00
+1800
+3000
+6040
+ffc0
+ff80
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 9 17 1 0
+BITMAP
+1c00
+7e00
+4700
+8300
+8300
+0600
+0c00
+1e00
+0700
+0380
+0180
+0180
+0180
+0100
+c300
+e600
+7800
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+0300
+0700
+0700
+0b00
+1b00
+1300
+3300
+2300
+6300
+4300
+c300
+ffc0
+ffc0
+0300
+0300
+0300
+0300
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1fc0
+1f80
+2000
+2000
+6000
+7800
+7e00
+0f80
+0380
+01c0
+00c0
+00c0
+00c0
+00c0
+c180
+e380
+7e00
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+03c0
+0e00
+1800
+3000
+7000
+6000
+ee00
+f380
+c180
+c0c0
+c0c0
+c0c0
+c0c0
+e0c0
+6180
+7b80
+1e00
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+7fc0
+ffc0
+c0c0
+8180
+0180
+0100
+0300
+0300
+0200
+0600
+0600
+0400
+0c00
+0c00
+0c00
+1800
+1800
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1e00
+3300
+6180
+6180
+6180
+3300
+1e00
+1e00
+3700
+6180
+41c0
+c0c0
+c0c0
+c0c0
+e180
+7380
+1e00
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1e00
+7780
+6180
+c1c0
+c0c0
+c0c0
+c0c0
+c0c0
+61c0
+73c0
+1d80
+0180
+0380
+0300
+0600
+1c00
+f000
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 11 2 0
+BITMAP
+c0
+c0
+00
+00
+00
+00
+00
+00
+00
+c0
+c0
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 3 14 2 -3
+BITMAP
+c0
+c0
+00
+00
+00
+00
+00
+00
+00
+c0
+e0
+20
+60
+c0
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 11 1 1
+BITMAP
+0060
+01c0
+0700
+1c00
+7000
+c000
+7000
+1c00
+0700
+01c0
+0060
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 6 1 4
+BITMAP
+fff0
+fff0
+0000
+0000
+fff0
+fff0
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 11 1 1
+BITMAP
+c000
+7000
+1c00
+0700
+01c0
+0060
+01c0
+0700
+1c00
+7000
+c000
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 8 17 2 0
+BITMAP
+7c
+c6
+83
+c3
+c3
+07
+0e
+0c
+18
+18
+10
+10
+10
+00
+00
+30
+30
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 880 0
+DWIDTH 22 0
+BBX 18 20 2 -3
+BITMAP
+03f800
+0f0e00
+1c0300
+380180
+700080
+60ecc0
+e1fc40
+c38c40
+c30c40
+c60c40
+c61840
+c618c0
+c61880
+c63980
+637b00
+61de00
+300000
+180000
+0e0c00
+03f000
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 17 17 0 0
+BITMAP
+008000
+01c000
+01c000
+016000
+026000
+023000
+063000
+043000
+041800
+0c1800
+0ff800
+080c00
+180c00
+100c00
+100600
+300600
+fc1f80
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 17 1 0
+BITMAP
+ffc0
+3070
+3030
+3018
+3018
+3018
+3030
+3040
+3fe0
+3038
+3018
+300c
+300c
+300c
+3018
+3078
+ffe0
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 17 1 0
+BITMAP
+07e4
+1c3c
+380c
+6004
+6004
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+6000
+6004
+3808
+1e38
+07e0
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 15 17 1 0
+BITMAP
+ffc0
+3070
+3038
+300c
+300c
+3006
+3006
+3006
+3006
+3006
+3006
+3006
+300c
+300c
+3038
+3070
+ffc0
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+fff0
+3030
+3010
+3010
+3000
+3000
+3040
+3040
+3fc0
+3040
+3040
+3000
+3000
+3008
+3008
+3018
+fff8
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 17 1 0
+BITMAP
+fff0
+3030
+3010
+3010
+3000
+3000
+3020
+3020
+3fe0
+3020
+3020
+3000
+3000
+3000
+3000
+3000
+fc00
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+07e4
+1c3c
+380c
+6004
+6004
+c000
+c000
+c000
+c000
+c03f
+c00c
+c00c
+600c
+600c
+381c
+1e38
+07e0
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 17 17 1 0
+BITMAP
+fc1f80
+300600
+300600
+300600
+300600
+300600
+300600
+300600
+3ffe00
+300600
+300600
+300600
+300600
+300600
+300600
+300600
+fc1f80
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 17 1 0
+BITMAP
+fc
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+fc
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+1f80
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+0600
+c600
+cc00
+7800
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 16 17 1 0
+BITMAP
+fc7e
+3018
+3030
+3060
+30c0
+3180
+3300
+3e00
+3f00
+3380
+31c0
+30e0
+3070
+3038
+301c
+300e
+fc1f
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 13 17 1 0
+BITMAP
+fc00
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+3008
+3008
+3018
+fff8
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 880 0
+DWIDTH 22 0
+BBX 21 17 1 0
+BITMAP
+f000f8
+3000e0
+380160
+380160
+2c0260
+2c0260
+260260
+260460
+230460
+230860
+218860
+218860
+20d060
+20d060
+206060
+206060
+f821f8
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+f01f
+3004
+3804
+3804
+2c04
+2604
+2604
+2304
+2184
+2184
+20c4
+2064
+2064
+2034
+201c
+201c
+f80c
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+07e0
+1c38
+381c
+6006
+6006
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+6006
+6006
+381c
+1c38
+07e0
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+ffc0
+3070
+3030
+3018
+3018
+3018
+3030
+3070
+3fc0
+3000
+3000
+3000
+3000
+3000
+3000
+3000
+fc00
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 22 1 -5
+BITMAP
+07e0
+1c38
+381c
+6006
+6006
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+6006
+6006
+381c
+1c38
+07e0
+01c0
+00e0
+0070
+0038
+000f
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 15 17 1 0
+BITMAP
+ffc0
+3070
+3030
+3038
+3018
+3038
+3030
+3070
+3fc0
+3380
+31c0
+30c0
+3060
+3070
+3038
+301c
+fc1e
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+1e40
+63c0
+c0c0
+c040
+c040
+e000
+7800
+1e00
+0f80
+03c0
+00e0
+0060
+8060
+8060
+c0c0
+f180
+9e00
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 17 1 0
+BITMAP
+fffc
+c30c
+8304
+8304
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0300
+0fc0
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+fc1f
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3008
+1808
+1c30
+07e0
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 17 17 0 0
+BITMAP
+fc1f80
+300600
+300400
+180c00
+180800
+180800
+0c1800
+0c1000
+063000
+062000
+062000
+036000
+034000
+03c000
+018000
+018000
+018000
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 920 0
+DWIDTH 23 0
+BBX 23 17 0 0
+BITMAP
+fc7e7e
+301818
+301810
+181810
+181830
+182c20
+0c2c20
+0c2c60
+064c60
+064c40
+064640
+0346c0
+034680
+038780
+018380
+018300
+018300
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 18 17 0 0
+BITMAP
+7e0f80
+1c0600
+0e0c00
+060800
+031000
+03a000
+01c000
+00c000
+00e000
+017000
+023800
+061800
+040c00
+080e00
+180700
+300380
+fc0fc0
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 16 17 0 0
+BITMAP
+fc3f
+300c
+3808
+1818
+1c10
+0c30
+0620
+0660
+0340
+03c0
+0180
+0180
+0180
+0180
+0180
+0180
+07e0
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+fff0
+c070
+80e0
+80c0
+01c0
+0380
+0300
+0700
+0600
+0e00
+1c00
+1800
+3800
+3008
+7008
+e018
+fff8
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 5 21 2 -4
+BITMAP
+f8
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+f8
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 7 17 0 0
+BITMAP
+c0
+c0
+40
+60
+60
+20
+30
+30
+10
+18
+18
+08
+0c
+0c
+04
+06
+06
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 5 21 1 -4
+BITMAP
+f8
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+f8
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 9 1 8
+BITMAP
+0800
+1c00
+1400
+3600
+2200
+6300
+4100
+c180
+8080
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 13 2 0 -5
+BITMAP
+fff8
+fff8
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 3 5 2 12
+BITMAP
+60
+c0
+80
+e0
+60
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+3e00
+6700
+6300
+0300
+0f00
+3b00
+6300
+c300
+c300
+c700
+fb00
+7180
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+e000
+6000
+6000
+6000
+6000
+6e00
+7380
+6180
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+6180
+7380
+5e00
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+1f00
+6380
+4180
+c000
+c000
+c000
+c000
+c000
+e000
+7080
+7f00
+1e00
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+0380
+0180
+0180
+0180
+0180
+1d80
+7380
+6180
+c180
+c180
+c180
+c180
+c180
+c180
+6180
+7380
+1ec0
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 12 1 0
+BITMAP
+1e00
+6300
+4180
+c180
+ff80
+c000
+c000
+c000
+e000
+7080
+7f00
+1e00
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 7 17 0 0
+BITMAP
+0e
+16
+30
+30
+30
+fe
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+78
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 11 17 1 -5
+BITMAP
+1fc0
+3300
+6180
+6180
+6180
+6180
+3300
+3e00
+3000
+6000
+7f00
+3fc0
+6060
+c020
+c060
+f1c0
+3f00
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+e000
+6000
+6000
+6000
+6000
+6700
+6f80
+71c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+f1e0
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 4 17 1 0
+BITMAP
+60
+60
+00
+00
+00
+e0
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 4 22 0 -5
+BITMAP
+30
+30
+00
+00
+00
+70
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+e0
+c0
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 11 17 1 0
+BITMAP
+e000
+6000
+6000
+6000
+6000
+67c0
+6300
+6600
+6400
+6800
+7800
+6c00
+6e00
+6700
+6380
+61c0
+f3e0
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 4 17 1 0
+BITMAP
+e0
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 800 0
+DWIDTH 20 0
+BBX 18 12 1 0
+BITMAP
+e70e00
+6f9f00
+71e380
+60c180
+60c180
+60c180
+60c180
+60c180
+60c180
+60c180
+60c180
+f1e3c0
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 12 1 0
+BITMAP
+e700
+6f80
+71c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+f1e0
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 12 1 0
+BITMAP
+1e00
+7380
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+7380
+1e00
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 -5
+BITMAP
+ee00
+7380
+6180
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+6180
+7380
+6e00
+6000
+6000
+6000
+6000
+f000
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 -5
+BITMAP
+1d80
+7380
+6180
+c180
+c180
+c180
+c180
+c180
+c180
+6180
+7380
+1d80
+0180
+0180
+0180
+0180
+03c0
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 7 12 1 0
+BITMAP
+e6
+6e
+76
+60
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 8 12 1 0
+BITMAP
+3e
+66
+c2
+e0
+70
+7c
+1e
+07
+03
+83
+c6
+f8
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 7 15 0 0
+BITMAP
+10
+30
+70
+fe
+30
+30
+30
+30
+30
+30
+30
+30
+30
+32
+1c
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 12 1 0
+BITMAP
+e1c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+71c0
+3ec0
+1ce0
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 11 12 0 0
+BITMAP
+f1e0
+60c0
+6080
+3080
+3080
+3100
+1900
+1900
+1a00
+0e00
+0e00
+0400
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 17 12 0 0
+BITMAP
+f1e780
+60c300
+60c200
+30c200
+30c200
+316400
+196400
+1a6400
+1a2800
+0e3800
+0e3800
+041000
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 12 1 0
+BITMAP
+f1e0
+60c0
+3180
+3900
+1a00
+0c00
+0e00
+1b00
+3380
+2180
+60c0
+f1e0
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 11 17 0 -5
+BITMAP
+f1e0
+60c0
+6080
+3080
+3080
+3100
+1900
+1900
+1a00
+0e00
+0e00
+0400
+0c00
+0800
+1800
+f000
+e000
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 8 12 1 0
+BITMAP
+ff
+c3
+86
+0e
+1c
+18
+38
+30
+70
+61
+c3
+ff
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 8 22 1 -5
+BITMAP
+07
+0c
+18
+18
+18
+18
+18
+10
+30
+20
+c0
+20
+30
+10
+18
+18
+18
+18
+18
+18
+0c
+07
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 17 2 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 400 0
+DWIDTH 10 0
+BBX 8 22 1 -5
+BITMAP
+e0
+30
+18
+18
+18
+18
+18
+08
+0c
+04
+03
+04
+0c
+08
+18
+18
+18
+18
+18
+18
+30
+e0
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 4 1 5
+BITMAP
+3820
+7c60
+c7c0
+8380
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 2 17 4 -5
+BITMAP
+c0
+c0
+00
+00
+00
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 9 16 1 -2
+BITMAP
+0100
+0100
+1f00
+6380
+4380
+c400
+c400
+c800
+c800
+c800
+d000
+7080
+7f00
+3e00
+4000
+4000
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+0f00
+1980
+3180
+3000
+3000
+3000
+3000
+fc00
+3000
+3000
+3000
+3000
+3000
+3000
+7840
+bec0
+e780
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 12 1 3
+BITMAP
+c060
+eee0
+7fc0
+3180
+60c0
+60c0
+60c0
+60c0
+3180
+7fc0
+eee0
+c060
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 14 17 0 0
+BITMAP
+f87c
+7030
+3020
+1860
+1840
+0cc0
+0c80
+0780
+0300
+1fe0
+0300
+1fe0
+0300
+0300
+0300
+0300
+0fc0
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 17 2 0
+BITMAP
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+00
+00
+00
+c0
+c0
+c0
+c0
+c0
+c0
+c0
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 8 20 2 -2
+BITMAP
+1c
+26
+46
+60
+70
+38
+3c
+4e
+87
+83
+c3
+e2
+74
+38
+1c
+0e
+06
+62
+64
+38
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 2 1 14
+BITMAP
+cc
+cc
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 17 17 1 0
+BITMAP
+07f000
+1c1c00
+300600
+61e300
+473100
+c41980
+8c0080
+880080
+880080
+880080
+8c0080
+c41980
+477100
+61c300
+300600
+1c1c00
+07f000
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 7 9 0 8
+BITMAP
+78
+cc
+0c
+7c
+cc
+cc
+76
+00
+7e
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 9 10 2 1
+BITMAP
+0880
+1980
+3300
+6600
+cc00
+cc00
+6600
+3300
+1980
+0880
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 7 1 3
+BITMAP
+fff0
+fff0
+0030
+0030
+0030
+0030
+0030
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 360 0
+DWIDTH 9 0
+BBX 7 2 1 5
+BITMAP
+fe
+fe
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 760 0
+DWIDTH 19 0
+BBX 17 17 1 0
+BITMAP
+07f000
+1c1c00
+300600
+67e300
+423100
+c21180
+821080
+823080
+83e080
+824080
+822080
+c23180
+471900
+600300
+300600
+1c1c00
+07f000
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 2 1 14
+BITMAP
+fc
+fc
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 360 0
+DWIDTH 9 0
+BBX 7 7 1 10
+BITMAP
+38
+44
+82
+82
+82
+44
+38
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 15 1 0
+BITMAP
+0600
+0600
+0600
+0600
+0600
+fff0
+fff0
+0600
+0600
+0600
+0600
+0600
+0000
+fff0
+fff0
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 6 10 0 7
+BITMAP
+38
+4c
+8c
+0c
+08
+10
+30
+20
+44
+fc
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 6 10 0 7
+BITMAP
+38
+4c
+8c
+08
+30
+08
+0c
+8c
+88
+70
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 5 4 2 13
+BITMAP
+18
+38
+60
+80
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 17 1 -5
+BITMAP
+e1c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+71c0
+7ec0
+5ce0
+4000
+4000
+c000
+e000
+4000
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 22 1 -5
+BITMAP
+1f80
+3900
+7900
+7900
+f900
+f900
+f900
+7900
+7900
+3900
+1900
+0900
+0900
+0900
+0900
+0900
+0900
+0900
+0900
+0900
+0900
+0900
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 2 2 2 6
+BITMAP
+c0
+c0
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 6 1 -6
+BITMAP
+10
+30
+3c
+0c
+cc
+78
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 280 0
+DWIDTH 7 0
+BBX 5 10 1 7
+BITMAP
+20
+60
+a0
+20
+20
+20
+20
+20
+20
+f8
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 9 1 8
+BITMAP
+78
+cc
+cc
+cc
+cc
+cc
+78
+00
+fc
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 9 10 2 1
+BITMAP
+8800
+cc00
+6600
+3300
+1980
+1980
+3300
+6600
+cc00
+8800
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 17 1 0
+BITMAP
+2008
+6018
+a030
+2020
+2060
+2040
+20c0
+2184
+210c
+fb1c
+0214
+0624
+0c64
+0844
+18ff
+1004
+3004
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 15 17 1 0
+BITMAP
+2008
+6018
+a030
+2020
+2060
+2040
+20c0
+219c
+2126
+fb46
+0206
+0604
+0c08
+0818
+1810
+1022
+307e
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 17 17 0 0
+BITMAP
+380400
+4c0c00
+8c1800
+081000
+303000
+082000
+0c6000
+8cc200
+888600
+718e00
+010a00
+031200
+063200
+042200
+0c7f80
+080200
+180200
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 8 17 1 -5
+BITMAP
+0c
+0c
+00
+00
+08
+08
+18
+18
+38
+30
+70
+e0
+c3
+c3
+c1
+63
+3e
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 17 22 0 0
+BITMAP
+030000
+038000
+00c000
+002000
+000000
+008000
+01c000
+01c000
+016000
+026000
+023000
+063000
+043000
+041800
+0c1800
+0ff800
+080c00
+180c00
+100c00
+100600
+300600
+fc1f80
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 17 22 0 0
+BITMAP
+003000
+007000
+00c000
+010000
+000000
+008000
+01c000
+01c000
+016000
+026000
+023000
+063000
+043000
+041800
+0c1800
+0ff800
+080c00
+180c00
+100c00
+100600
+300600
+fc1f80
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 17 22 0 0
+BITMAP
+018000
+03c000
+066000
+081000
+000000
+008000
+01c000
+01c000
+016000
+026000
+023000
+063000
+043000
+041800
+0c1800
+0ff800
+080c00
+180c00
+100c00
+100600
+300600
+fc1f80
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 17 21 0 0
+BITMAP
+039000
+04e000
+000000
+000000
+008000
+01c000
+01c000
+016000
+026000
+023000
+063000
+043000
+041800
+0c1800
+0ff800
+080c00
+180c00
+100c00
+100600
+300700
+fc1f80
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 17 21 0 0
+BITMAP
+063000
+063000
+000000
+000000
+008000
+01c000
+01c000
+016000
+026000
+023000
+063000
+043000
+041800
+0c1800
+0ff800
+080c00
+180c00
+100c00
+100600
+300600
+fc1f80
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 17 21 0 0
+BITMAP
+01c000
+022000
+022000
+01c000
+008000
+01c000
+01c000
+016000
+026000
+023000
+063000
+043000
+041800
+0c1800
+0ff800
+080c00
+180c00
+100c00
+100600
+300600
+fc1f80
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 840 0
+DWIDTH 21 0
+BBX 20 17 0 0
+BITMAP
+03ffe0
+01e060
+016020
+016020
+026000
+026000
+066080
+046080
+047f80
+0c6080
+0fe080
+086000
+186000
+106010
+106010
+306030
+f9fff0
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 23 1 -6
+BITMAP
+07e4
+1c3c
+380c
+6004
+6004
+c000
+c000
+c000
+c000
+c000
+c000
+c000
+6000
+6004
+3808
+1e38
+07e0
+0100
+0300
+03c0
+00c0
+0cc0
+0780
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 22 1 0
+BITMAP
+1800
+1c00
+0600
+0100
+0000
+fff0
+3030
+3010
+3010
+3000
+3000
+3040
+3040
+3fc0
+3040
+3040
+3000
+3000
+3008
+3008
+3018
+fff8
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 22 1 0
+BITMAP
+00c0
+01c0
+0300
+0400
+0000
+fff0
+3030
+3010
+3010
+3000
+3000
+3040
+3040
+3fc0
+3040
+3040
+3000
+3000
+3008
+3008
+3018
+fff8
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 22 1 0
+BITMAP
+0300
+0780
+0cc0
+1020
+0000
+fff0
+3030
+3010
+3010
+3000
+3000
+3040
+3040
+3fc0
+3040
+3040
+3000
+3000
+3008
+3008
+3018
+fff8
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 21 1 0
+BITMAP
+1980
+1980
+0000
+0000
+fff0
+3030
+3010
+3010
+3000
+3000
+3040
+3040
+3fc0
+3040
+3040
+3000
+3000
+3008
+3008
+3018
+fff8
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 22 1 0
+BITMAP
+c0
+e0
+30
+08
+00
+fc
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+fc
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 22 1 0
+BITMAP
+0c
+1c
+30
+40
+00
+fc
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+fc
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 8 22 1 0
+BITMAP
+18
+3c
+66
+81
+00
+7e
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+18
+7e
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 320 0
+DWIDTH 8 0
+BBX 6 21 1 0
+BITMAP
+cc
+cc
+00
+00
+fc
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+fc
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 680 0
+DWIDTH 17 0
+BBX 16 17 0 0
+BITMAP
+7fe0
+1838
+181c
+1806
+1806
+1803
+1803
+1803
+ff03
+1803
+1803
+1803
+1806
+1806
+181c
+1838
+7fe0
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 21 1 0
+BITMAP
+0390
+04e0
+0000
+0000
+f01f
+3004
+3804
+3804
+2c04
+2604
+2604
+2304
+2184
+2184
+20c4
+2064
+2064
+2034
+201c
+201c
+f80c
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 22 1 0
+BITMAP
+0600
+0700
+0180
+0040
+0000
+07e0
+1c38
+381c
+6006
+6006
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+6006
+6006
+381c
+1c38
+07e0
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 22 1 0
+BITMAP
+0030
+0070
+00c0
+0100
+0000
+07e0
+1c38
+381c
+6006
+6006
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+6006
+6006
+381c
+1c38
+07e0
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 22 1 0
+BITMAP
+0180
+03c0
+0660
+0810
+0000
+07e0
+1c38
+381c
+6006
+6006
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+6006
+6006
+381c
+1c38
+07e0
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 21 1 0
+BITMAP
+0390
+04e0
+0000
+0000
+07e0
+1c38
+381c
+6006
+6006
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+6006
+6006
+381c
+1c38
+07e0
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 21 1 0
+BITMAP
+0660
+0660
+0000
+0000
+07e0
+1c38
+381c
+6006
+6006
+c003
+c003
+c003
+c003
+c003
+c003
+c003
+6006
+6006
+381c
+1c38
+07e0
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 10 11 2 1
+BITMAP
+8040
+c0c0
+6180
+3300
+1e00
+0c00
+1e00
+3300
+6180
+c0c0
+8040
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 19 1 -1
+BITMAP
+0004
+07e4
+1c38
+381c
+6026
+6046
+c043
+c083
+c083
+c103
+c103
+c203
+c203
+6406
+6806
+381c
+1c38
+27e0
+2000
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 22 1 0
+BITMAP
+0600
+0700
+0180
+0040
+0000
+fc1f
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3008
+1808
+1c30
+07e0
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 22 1 0
+BITMAP
+0030
+0070
+00c0
+0100
+0000
+fc1f
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3008
+1808
+1c30
+07e0
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 22 1 0
+BITMAP
+0180
+03c0
+0660
+0810
+0000
+fc1f
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3008
+1808
+1c30
+07e0
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 720 0
+DWIDTH 18 0
+BBX 16 21 1 0
+BITMAP
+0630
+0630
+0000
+0000
+fc1f
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3004
+3008
+1808
+1c30
+07e0
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 16 22 0 0
+BITMAP
+0030
+0070
+00c0
+0100
+0000
+fc3f
+300c
+3808
+1818
+1c10
+0c30
+0620
+0660
+0340
+03c0
+0180
+0180
+0180
+0180
+0180
+0180
+07e0
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 600 0
+DWIDTH 15 0
+BBX 13 17 1 0
+BITMAP
+fc00
+3000
+3000
+3000
+3fc0
+3070
+3030
+3018
+3018
+3018
+3030
+3070
+3fc0
+3000
+3000
+3000
+fc00
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+1e00
+3300
+6180
+6180
+6180
+6180
+6300
+6c00
+6700
+6380
+6180
+61c0
+60c0
+60c0
+6cc0
+6c80
+e700
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+6000
+7000
+1800
+0400
+0000
+3e00
+6700
+6300
+0300
+0f00
+3b00
+6300
+c300
+c300
+c700
+fb00
+7180
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+0300
+0700
+0c00
+1000
+0000
+3e00
+6700
+6300
+0300
+0f00
+3b00
+6300
+c300
+c300
+c700
+fb00
+7180
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+1800
+3c00
+2400
+4200
+0000
+3e00
+6700
+6300
+0300
+0f00
+3b00
+6300
+c300
+c300
+c700
+fb00
+7180
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 16 1 0
+BITMAP
+3a00
+5c00
+0000
+0000
+3e00
+6700
+6300
+0300
+0f00
+3b00
+6300
+c300
+c300
+c700
+fb00
+7180
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 16 1 0
+BITMAP
+6600
+6600
+0000
+0000
+3e00
+6700
+6300
+0300
+0f00
+3b00
+6300
+c300
+c300
+c700
+fb00
+7180
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+1c00
+2200
+2200
+1c00
+0000
+3e00
+6700
+6300
+0300
+0f00
+3b00
+6300
+c300
+c300
+c700
+fb00
+7180
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 640 0
+DWIDTH 16 0
+BBX 14 12 1 0
+BITMAP
+3cf0
+6798
+630c
+030c
+0ffc
+3b00
+6300
+c300
+c300
+c784
+fbf8
+70f0
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 18 1 -6
+BITMAP
+1f00
+6380
+4180
+c000
+c000
+c000
+c000
+c000
+e000
+7080
+7f00
+1e00
+0800
+1800
+1e00
+0600
+6600
+3c00
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+6000
+7000
+1800
+0400
+0000
+1e00
+6300
+4180
+c180
+ff80
+c000
+c000
+c000
+e000
+7080
+7f00
+1e00
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+0300
+0700
+0c00
+1000
+0000
+1e00
+6300
+4180
+c180
+ff80
+c000
+c000
+c000
+e000
+7080
+7f00
+1e00
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 17 1 0
+BITMAP
+0c00
+1e00
+1200
+2100
+0000
+1e00
+6300
+4180
+c180
+ff80
+c000
+c000
+c000
+e000
+7080
+7f00
+1e00
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 9 16 1 0
+BITMAP
+3300
+3300
+0000
+0000
+1e00
+6300
+4180
+c180
+ff80
+c000
+c000
+c000
+e000
+7080
+7f00
+1e00
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 5 17 0 0
+BITMAP
+c0
+e0
+30
+08
+00
+70
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+78
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 5 17 1 0
+BITMAP
+18
+38
+60
+80
+00
+e0
+60
+60
+60
+60
+60
+60
+60
+60
+60
+60
+f0
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 6 17 0 0
+BITMAP
+30
+78
+48
+84
+00
+70
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+78
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 240 0
+DWIDTH 6 0
+BBX 6 16 0 0
+BITMAP
+cc
+cc
+00
+00
+70
+30
+30
+30
+30
+30
+30
+30
+30
+30
+30
+78
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+c000
+7180
+1e00
+3c00
+c600
+1f00
+7380
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+7380
+1e00
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 16 1 0
+BITMAP
+1c80
+2700
+0000
+0000
+e700
+6f80
+71c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+f1e0
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+3000
+3800
+0c00
+0200
+0000
+1e00
+7380
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+7380
+1e00
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+0180
+0380
+0600
+0800
+0000
+1e00
+7380
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+7380
+1e00
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 17 1 0
+BITMAP
+0c00
+1e00
+1200
+2100
+0000
+1e00
+7380
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+7380
+1e00
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 16 1 0
+BITMAP
+1c80
+2700
+0000
+0000
+1e00
+7380
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+7380
+1e00
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 16 1 0
+BITMAP
+3300
+3300
+0000
+0000
+1e00
+7380
+6180
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+c0c0
+6180
+7380
+1e00
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 560 0
+DWIDTH 14 0
+BBX 12 10 1 2
+BITMAP
+0600
+0600
+0000
+0000
+fff0
+fff0
+0000
+0000
+0600
+0600
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 14 1 -1
+BITMAP
+00c0
+1ec0
+7380
+6380
+c6c0
+c4c0
+ccc0
+c8c0
+d8c0
+d0c0
+7180
+7380
+de00
+c000
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+3000
+3800
+0c00
+0200
+0000
+e1c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+71c0
+3ec0
+1ce0
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+0180
+0380
+0600
+0800
+0000
+e1c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+71c0
+3ec0
+1ce0
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 17 1 0
+BITMAP
+0c00
+1e00
+1200
+2100
+0000
+e1c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+71c0
+3ec0
+1ce0
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 520 0
+DWIDTH 13 0
+BBX 11 16 1 0
+BITMAP
+3300
+3300
+0000
+0000
+e1c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+71c0
+3ec0
+1ce0
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 11 22 0 -5
+BITMAP
+0180
+0380
+0600
+0800
+0000
+f1e0
+60c0
+6080
+3080
+3080
+3100
+1900
+1900
+1a00
+0e00
+0e00
+0400
+0c00
+0800
+1800
+f000
+e000
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 480 0
+DWIDTH 12 0
+BBX 10 22 1 -5
+BITMAP
+e000
+6000
+6000
+6000
+6000
+6e00
+7380
+6180
+60c0
+60c0
+60c0
+60c0
+60c0
+60c0
+6180
+7380
+6e00
+6000
+6000
+6000
+6000
+f000
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 440 0
+DWIDTH 11 0
+BBX 11 21 0 -5
+BITMAP
+3300
+3300
+0000
+0000
+f1e0
+60c0
+6080
+3080
+3080
+3100
+1900
+1900
+1a00
+0e00
+0e00
+0400
+0c00
+0800
+1800
+f000
+e000
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times24.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times24.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times24.h	(revision 22322)
@@ -0,0 +1,1110 @@
+static unsigned char times24_0_bits[] = {
+0x00};
+static unsigned char times24_1_bits[] = {
+0x00};
+static unsigned char times24_2_bits[] = {
+0x00};
+static unsigned char times24_3_bits[] = {
+0x00};
+static unsigned char times24_4_bits[] = {
+0x00};
+static unsigned char times24_5_bits[] = {
+0x00};
+static unsigned char times24_6_bits[] = {
+0x00};
+static unsigned char times24_7_bits[] = {
+0x00};
+static unsigned char times24_8_bits[] = {
+0x00};
+static unsigned char times24_9_bits[] = {
+0x00};
+static unsigned char times24_10_bits[] = {
+0x00};
+static unsigned char times24_11_bits[] = {
+0x00};
+static unsigned char times24_12_bits[] = {
+0x00};
+static unsigned char times24_13_bits[] = {
+0x00};
+static unsigned char times24_14_bits[] = {
+0x00};
+static unsigned char times24_15_bits[] = {
+0x00};
+static unsigned char times24_16_bits[] = {
+0x00};
+static unsigned char times24_17_bits[] = {
+0x00};
+static unsigned char times24_18_bits[] = {
+0x00};
+static unsigned char times24_19_bits[] = {
+0x00};
+static unsigned char times24_20_bits[] = {
+0x00};
+static unsigned char times24_21_bits[] = {
+0x00};
+static unsigned char times24_22_bits[] = {
+0x00};
+static unsigned char times24_23_bits[] = {
+0x00};
+static unsigned char times24_24_bits[] = {
+0x00};
+static unsigned char times24_25_bits[] = {
+0x00};
+static unsigned char times24_26_bits[] = {
+0x00};
+static unsigned char times24_27_bits[] = {
+0x00};
+static unsigned char times24_28_bits[] = {
+0x00};
+static unsigned char times24_29_bits[] = {
+0x00};
+static unsigned char times24_30_bits[] = {
+0x00};
+static unsigned char times24_31_bits[] = {
+0x00};
+static unsigned char times24_32_bits[] = {
+0x00};
+static unsigned char times24_33_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x00, 0x00, 0x00, 0x03, 0x03};
+static unsigned char times24_34_bits[] = {
+0x33, 0x33, 0x33, 0x33, 0x11};
+static unsigned char times24_35_bits[] = {
+0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xfe, 0x07, 
+0xfe, 0x07, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xff, 0x03, 0xff, 0x03, 
+0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00};
+static unsigned char times24_36_bits[] = {
+0x20, 0x00, 0x20, 0x00, 0xf8, 0x01, 0x2c, 0x07, 0x26, 0x06, 0x26, 0x04, 
+0x26, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xe0, 0x01, 0xe0, 0x03, 
+0x20, 0x07, 0x20, 0x06, 0x21, 0x06, 0x21, 0x06, 0x23, 0x03, 0xa7, 0x03, 
+0xfc, 0x00, 0x20, 0x00, 0x20, 0x00};
+static unsigned char times24_37_bits[] = {
+0x78, 0x30, 0x00, 0xcc, 0x3f, 0x00, 0x86, 0x18, 0x00, 0x83, 0x08, 0x00, 
+0x83, 0x0c, 0x00, 0x43, 0x06, 0x00, 0x27, 0x02, 0x00, 0x1e, 0x03, 0x00, 
+0x80, 0xf1, 0x00, 0x80, 0x98, 0x01, 0xc0, 0x0c, 0x01, 0x60, 0x06, 0x01, 
+0x20, 0x06, 0x01, 0x30, 0x86, 0x00, 0x18, 0x4e, 0x00, 0x0c, 0x3c, 0x00};
+static unsigned char times24_38_bits[] = {
+0xe0, 0x01, 0x30, 0x03, 0x18, 0x02, 0x18, 0x02, 0x18, 0x03, 0xb8, 0x01, 
+0xf0, 0x00, 0x70, 0x7c, 0x78, 0x18, 0xec, 0x08, 0xc6, 0x04, 0x83, 0x05, 
+0x83, 0x03, 0x03, 0x03, 0x87, 0x87, 0xfe, 0x7e, 0x3c, 0x3c};
+static unsigned char times24_39_bits[] = {
+0x03, 0x07, 0x04, 0x06, 0x03};
+static unsigned char times24_40_bits[] = {
+0x20, 0x10, 0x08, 0x0c, 0x04, 0x06, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x06, 0x06, 0x04, 0x0c, 0x08, 0x10, 0x20};
+static unsigned char times24_41_bits[] = {
+0x01, 0x02, 0x04, 0x0c, 0x08, 0x18, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 
+0x30, 0x30, 0x30, 0x18, 0x18, 0x08, 0x0c, 0x04, 0x02, 0x01};
+static unsigned char times24_42_bits[] = {
+0x10, 0x00, 0x38, 0x00, 0x93, 0x01, 0xd7, 0x01, 0x38, 0x00, 0xd7, 0x01, 
+0x93, 0x01, 0x38, 0x00, 0x10, 0x00};
+static unsigned char times24_43_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xff, 0x0f, 
+0xff, 0x0f, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char times24_44_bits[] = {
+0x03, 0x07, 0x04, 0x06, 0x03};
+static unsigned char times24_45_bits[] = {
+0xff, 0x0f, 0xff, 0x0f};
+static unsigned char times24_46_bits[] = {
+0x03, 0x03};
+static unsigned char times24_47_bits[] = {
+0xc0, 0xc0, 0xc0, 0x60, 0x60, 0x20, 0x30, 0x30, 0x10, 0x18, 0x18, 0x08, 
+0x0c, 0x0c, 0x04, 0x06, 0x06, 0x03, 0x03, 0x03};
+static unsigned char times24_48_bits[] = {
+0x78, 0x00, 0xcc, 0x00, 0x86, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x87, 0x03, 0x86, 0x01, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00};
+static unsigned char times24_49_bits[] = {
+0x10, 0x18, 0x1e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0xff};
+static unsigned char times24_50_bits[] = {
+0x38, 0x00, 0xfe, 0x00, 0xc2, 0x01, 0x81, 0x01, 0x81, 0x01, 0x80, 0x01, 
+0x80, 0x01, 0xc0, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x20, 0x00, 0x30, 0x00, 
+0x18, 0x00, 0x0c, 0x00, 0x06, 0x02, 0xff, 0x03, 0xff, 0x01};
+static unsigned char times24_51_bits[] = {
+0x38, 0x00, 0x7e, 0x00, 0xe2, 0x00, 0xc1, 0x00, 0xc1, 0x00, 0x60, 0x00, 
+0x30, 0x00, 0x78, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x01, 0x80, 0x01, 
+0x80, 0x01, 0x80, 0x00, 0xc3, 0x00, 0x67, 0x00, 0x1e, 0x00};
+static unsigned char times24_52_bits[] = {
+0xc0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xd0, 0x00, 0xd8, 0x00, 0xc8, 0x00, 
+0xcc, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xff, 0x03, 
+0xff, 0x03, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00};
+static unsigned char times24_53_bits[] = {
+0xf8, 0x03, 0xf8, 0x01, 0x04, 0x00, 0x04, 0x00, 0x06, 0x00, 0x1e, 0x00, 
+0x7e, 0x00, 0xf0, 0x01, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x03, 0x00, 0x03, 
+0x00, 0x03, 0x00, 0x03, 0x83, 0x01, 0xc7, 0x01, 0x7e, 0x00};
+static unsigned char times24_54_bits[] = {
+0xc0, 0x03, 0x70, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x06, 0x00, 
+0x77, 0x00, 0xcf, 0x01, 0x83, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x07, 0x03, 0x86, 0x01, 0xde, 0x01, 0x78, 0x00};
+static unsigned char times24_55_bits[] = {
+0xfe, 0x03, 0xff, 0x03, 0x03, 0x03, 0x81, 0x01, 0x80, 0x01, 0x80, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0x40, 0x00, 0x60, 0x00, 0x60, 0x00, 0x20, 0x00, 
+0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x18, 0x00, 0x18, 0x00};
+static unsigned char times24_56_bits[] = {
+0x78, 0x00, 0xcc, 0x00, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, 0xcc, 0x00, 
+0x78, 0x00, 0x78, 0x00, 0xec, 0x00, 0x86, 0x01, 0x82, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x87, 0x01, 0xce, 0x01, 0x78, 0x00};
+static unsigned char times24_57_bits[] = {
+0x78, 0x00, 0xee, 0x01, 0x86, 0x01, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x86, 0x03, 0xce, 0x03, 0xb8, 0x01, 0x80, 0x01, 
+0xc0, 0x01, 0xc0, 0x00, 0x60, 0x00, 0x38, 0x00, 0x0f, 0x00};
+static unsigned char times24_58_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03};
+static unsigned char times24_59_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x04, 
+0x06, 0x03};
+static unsigned char times24_60_bits[] = {
+0x00, 0x06, 0x80, 0x03, 0xe0, 0x00, 0x38, 0x00, 0x0e, 0x00, 0x03, 0x00, 
+0x0e, 0x00, 0x38, 0x00, 0xe0, 0x00, 0x80, 0x03, 0x00, 0x06};
+static unsigned char times24_61_bits[] = {
+0xff, 0x0f, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xff, 0x0f, 0xff, 0x0f};
+static unsigned char times24_62_bits[] = {
+0x03, 0x00, 0x0e, 0x00, 0x38, 0x00, 0xe0, 0x00, 0x80, 0x03, 0x00, 0x06, 
+0x80, 0x03, 0xe0, 0x00, 0x38, 0x00, 0x0e, 0x00, 0x03, 0x00};
+static unsigned char times24_63_bits[] = {
+0x3e, 0x63, 0xc1, 0xc3, 0xc3, 0xe0, 0x70, 0x30, 0x18, 0x18, 0x08, 0x08, 
+0x08, 0x00, 0x00, 0x0c, 0x0c};
+static unsigned char times24_64_bits[] = {
+0xc0, 0x1f, 0x00, 0xf0, 0x70, 0x00, 0x38, 0xc0, 0x00, 0x1c, 0x80, 0x01, 
+0x0e, 0x00, 0x01, 0x06, 0x37, 0x03, 0x87, 0x3f, 0x02, 0xc3, 0x31, 0x02, 
+0xc3, 0x30, 0x02, 0x63, 0x30, 0x02, 0x63, 0x18, 0x02, 0x63, 0x18, 0x03, 
+0x63, 0x18, 0x01, 0x63, 0x9c, 0x01, 0xc6, 0xde, 0x00, 0x86, 0x7b, 0x00, 
+0x0c, 0x00, 0x00, 0x18, 0x00, 0x00, 0x70, 0x30, 0x00, 0xc0, 0x0f, 0x00};
+static unsigned char times24_65_bits[] = {
+0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x03, 0x00, 0x80, 0x06, 0x00, 
+0x40, 0x06, 0x00, 0x40, 0x0c, 0x00, 0x60, 0x0c, 0x00, 0x20, 0x0c, 0x00, 
+0x20, 0x18, 0x00, 0x30, 0x18, 0x00, 0xf0, 0x1f, 0x00, 0x10, 0x30, 0x00, 
+0x18, 0x30, 0x00, 0x08, 0x30, 0x00, 0x08, 0x60, 0x00, 0x0c, 0x60, 0x00, 
+0x3f, 0xf8, 0x01};
+static unsigned char times24_66_bits[] = {
+0xff, 0x03, 0x0c, 0x0e, 0x0c, 0x0c, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x18, 
+0x0c, 0x0c, 0x0c, 0x02, 0xfc, 0x07, 0x0c, 0x1c, 0x0c, 0x18, 0x0c, 0x30, 
+0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x18, 0x0c, 0x1e, 0xff, 0x07};
+static unsigned char times24_67_bits[] = {
+0xe0, 0x27, 0x38, 0x3c, 0x1c, 0x30, 0x06, 0x20, 0x06, 0x20, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x06, 0x00, 0x06, 0x20, 0x1c, 0x10, 0x78, 0x1c, 0xe0, 0x07};
+static unsigned char times24_68_bits[] = {
+0xff, 0x03, 0x0c, 0x0e, 0x0c, 0x1c, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x60, 
+0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 
+0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x1c, 0x0c, 0x0e, 0xff, 0x03};
+static unsigned char times24_69_bits[] = {
+0xff, 0x0f, 0x0c, 0x0c, 0x0c, 0x08, 0x0c, 0x08, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x02, 0x0c, 0x02, 0xfc, 0x03, 0x0c, 0x02, 0x0c, 0x02, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x10, 0x0c, 0x10, 0x0c, 0x18, 0xff, 0x1f};
+static unsigned char times24_70_bits[] = {
+0xff, 0x0f, 0x0c, 0x0c, 0x0c, 0x08, 0x0c, 0x08, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x04, 0x0c, 0x04, 0xfc, 0x07, 0x0c, 0x04, 0x0c, 0x04, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x3f, 0x00};
+static unsigned char times24_71_bits[] = {
+0xe0, 0x27, 0x38, 0x3c, 0x1c, 0x30, 0x06, 0x20, 0x06, 0x20, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0xfc, 0x03, 0x30, 0x03, 0x30, 
+0x06, 0x30, 0x06, 0x30, 0x1c, 0x38, 0x78, 0x1c, 0xe0, 0x07};
+static unsigned char times24_72_bits[] = {
+0x3f, 0xf8, 0x01, 0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 
+0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 
+0xfc, 0x7f, 0x00, 0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 
+0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 0x0c, 0x60, 0x00, 
+0x3f, 0xf8, 0x01};
+static unsigned char times24_73_bits[] = {
+0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x3f};
+static unsigned char times24_74_bits[] = {
+0xf8, 0x01, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x60, 0x00, 0x60, 0x00, 0x63, 0x00, 0x33, 0x00, 0x1e, 0x00};
+static unsigned char times24_75_bits[] = {
+0x3f, 0x7e, 0x0c, 0x18, 0x0c, 0x0c, 0x0c, 0x06, 0x0c, 0x03, 0x8c, 0x01, 
+0xcc, 0x00, 0x7c, 0x00, 0xfc, 0x00, 0xcc, 0x01, 0x8c, 0x03, 0x0c, 0x07, 
+0x0c, 0x0e, 0x0c, 0x1c, 0x0c, 0x38, 0x0c, 0x70, 0x3f, 0xf8};
+static unsigned char times24_76_bits[] = {
+0x3f, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x10, 0x0c, 0x10, 0x0c, 0x18, 0xff, 0x1f};
+static unsigned char times24_77_bits[] = {
+0x0f, 0x00, 0x1f, 0x0c, 0x00, 0x07, 0x1c, 0x80, 0x06, 0x1c, 0x80, 0x06, 
+0x34, 0x40, 0x06, 0x34, 0x40, 0x06, 0x64, 0x40, 0x06, 0x64, 0x20, 0x06, 
+0xc4, 0x20, 0x06, 0xc4, 0x10, 0x06, 0x84, 0x11, 0x06, 0x84, 0x11, 0x06, 
+0x04, 0x0b, 0x06, 0x04, 0x0b, 0x06, 0x04, 0x06, 0x06, 0x04, 0x06, 0x06, 
+0x1f, 0x84, 0x1f};
+static unsigned char times24_78_bits[] = {
+0x0f, 0xf8, 0x0c, 0x20, 0x1c, 0x20, 0x1c, 0x20, 0x34, 0x20, 0x64, 0x20, 
+0x64, 0x20, 0xc4, 0x20, 0x84, 0x21, 0x84, 0x21, 0x04, 0x23, 0x04, 0x26, 
+0x04, 0x26, 0x04, 0x2c, 0x04, 0x38, 0x04, 0x38, 0x1f, 0x30};
+static unsigned char times24_79_bits[] = {
+0xe0, 0x07, 0x38, 0x1c, 0x1c, 0x38, 0x06, 0x60, 0x06, 0x60, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x06, 0x60, 0x06, 0x60, 0x1c, 0x38, 0x38, 0x1c, 0xe0, 0x07};
+static unsigned char times24_80_bits[] = {
+0xff, 0x03, 0x0c, 0x0e, 0x0c, 0x0c, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x18, 
+0x0c, 0x0c, 0x0c, 0x0e, 0xfc, 0x03, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x3f, 0x00};
+static unsigned char times24_81_bits[] = {
+0xe0, 0x07, 0x38, 0x1c, 0x1c, 0x38, 0x06, 0x60, 0x06, 0x60, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x06, 0x60, 0x06, 0x60, 0x1c, 0x38, 0x38, 0x1c, 0xe0, 0x07, 0x80, 0x03, 
+0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0xf0};
+static unsigned char times24_82_bits[] = {
+0xff, 0x03, 0x0c, 0x0e, 0x0c, 0x0c, 0x0c, 0x1c, 0x0c, 0x18, 0x0c, 0x1c, 
+0x0c, 0x0c, 0x0c, 0x0e, 0xfc, 0x03, 0xcc, 0x01, 0x8c, 0x03, 0x0c, 0x03, 
+0x0c, 0x06, 0x0c, 0x0e, 0x0c, 0x1c, 0x0c, 0x38, 0x3f, 0x78};
+static unsigned char times24_83_bits[] = {
+0x78, 0x02, 0xc6, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x02, 0x07, 0x00, 
+0x1e, 0x00, 0x78, 0x00, 0xf0, 0x01, 0xc0, 0x03, 0x00, 0x07, 0x00, 0x06, 
+0x01, 0x06, 0x01, 0x06, 0x03, 0x03, 0x8f, 0x01, 0x79, 0x00};
+static unsigned char times24_84_bits[] = {
+0xff, 0x3f, 0xc3, 0x30, 0xc1, 0x20, 0xc1, 0x20, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xf0, 0x03};
+static unsigned char times24_85_bits[] = {
+0x3f, 0xf8, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 
+0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 
+0x0c, 0x20, 0x0c, 0x10, 0x18, 0x10, 0x38, 0x0c, 0xe0, 0x07};
+static unsigned char times24_86_bits[] = {
+0x3f, 0xf8, 0x01, 0x0c, 0x60, 0x00, 0x0c, 0x20, 0x00, 0x18, 0x30, 0x00, 
+0x18, 0x10, 0x00, 0x18, 0x10, 0x00, 0x30, 0x18, 0x00, 0x30, 0x08, 0x00, 
+0x60, 0x0c, 0x00, 0x60, 0x04, 0x00, 0x60, 0x04, 0x00, 0xc0, 0x06, 0x00, 
+0xc0, 0x02, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 
+0x80, 0x01, 0x00};
+static unsigned char times24_87_bits[] = {
+0x3f, 0x7e, 0x7e, 0x0c, 0x18, 0x18, 0x0c, 0x18, 0x08, 0x18, 0x18, 0x08, 
+0x18, 0x18, 0x0c, 0x18, 0x34, 0x04, 0x30, 0x34, 0x04, 0x30, 0x34, 0x06, 
+0x60, 0x32, 0x06, 0x60, 0x32, 0x02, 0x60, 0x62, 0x02, 0xc0, 0x62, 0x03, 
+0xc0, 0x62, 0x01, 0xc0, 0xe1, 0x01, 0x80, 0xc1, 0x01, 0x80, 0xc1, 0x00, 
+0x80, 0xc1, 0x00};
+static unsigned char times24_88_bits[] = {
+0x7e, 0xf0, 0x01, 0x38, 0x60, 0x00, 0x70, 0x30, 0x00, 0x60, 0x10, 0x00, 
+0xc0, 0x08, 0x00, 0xc0, 0x05, 0x00, 0x80, 0x03, 0x00, 0x00, 0x03, 0x00, 
+0x00, 0x07, 0x00, 0x80, 0x0e, 0x00, 0x40, 0x1c, 0x00, 0x60, 0x18, 0x00, 
+0x20, 0x30, 0x00, 0x10, 0x70, 0x00, 0x18, 0xe0, 0x00, 0x0c, 0xc0, 0x01, 
+0x3f, 0xf0, 0x03};
+static unsigned char times24_89_bits[] = {
+0x3f, 0xfc, 0x0c, 0x30, 0x1c, 0x10, 0x18, 0x18, 0x38, 0x08, 0x30, 0x0c, 
+0x60, 0x04, 0x60, 0x06, 0xc0, 0x02, 0xc0, 0x03, 0x80, 0x01, 0x80, 0x01, 
+0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xe0, 0x07};
+static unsigned char times24_90_bits[] = {
+0xff, 0x0f, 0x03, 0x0e, 0x01, 0x07, 0x01, 0x03, 0x80, 0x03, 0xc0, 0x01, 
+0xc0, 0x00, 0xe0, 0x00, 0x60, 0x00, 0x70, 0x00, 0x38, 0x00, 0x18, 0x00, 
+0x1c, 0x00, 0x0c, 0x10, 0x0e, 0x10, 0x07, 0x18, 0xff, 0x1f};
+static unsigned char times24_91_bits[] = {
+0x1f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x1f};
+static unsigned char times24_92_bits[] = {
+0x03, 0x03, 0x02, 0x06, 0x06, 0x04, 0x0c, 0x0c, 0x08, 0x18, 0x18, 0x10, 
+0x30, 0x30, 0x20, 0x60, 0x60};
+static unsigned char times24_93_bits[] = {
+0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f};
+static unsigned char times24_94_bits[] = {
+0x10, 0x00, 0x38, 0x00, 0x28, 0x00, 0x6c, 0x00, 0x44, 0x00, 0xc6, 0x00, 
+0x82, 0x00, 0x83, 0x01, 0x01, 0x01};
+static unsigned char times24_95_bits[] = {
+0xff, 0x1f, 0xff, 0x1f};
+static unsigned char times24_96_bits[] = {
+0x06, 0x03, 0x01, 0x07, 0x06};
+static unsigned char times24_97_bits[] = {
+0x7c, 0x00, 0xe6, 0x00, 0xc6, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xdc, 0x00, 
+0xc6, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xe3, 0x00, 0xdf, 0x00, 0x8e, 0x01};
+static unsigned char times24_98_bits[] = {
+0x07, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x76, 0x00, 
+0xce, 0x01, 0x86, 0x01, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x86, 0x01, 0xce, 0x01, 0x7a, 0x00};
+static unsigned char times24_99_bits[] = {
+0xf8, 0x00, 0xc6, 0x01, 0x82, 0x01, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x01, 0xfe, 0x00, 0x78, 0x00};
+static unsigned char times24_100_bits[] = {
+0xc0, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xb8, 0x01, 
+0xce, 0x01, 0x86, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 
+0x83, 0x01, 0x83, 0x01, 0x86, 0x01, 0xce, 0x01, 0x78, 0x03};
+static unsigned char times24_101_bits[] = {
+0x78, 0x00, 0xc6, 0x00, 0x82, 0x01, 0x83, 0x01, 0xff, 0x01, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x01, 0xfe, 0x00, 0x78, 0x00};
+static unsigned char times24_102_bits[] = {
+0x70, 0x68, 0x0c, 0x0c, 0x0c, 0x7f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x1e};
+static unsigned char times24_103_bits[] = {
+0xf8, 0x03, 0xcc, 0x00, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, 
+0xcc, 0x00, 0x7c, 0x00, 0x0c, 0x00, 0x06, 0x00, 0xfe, 0x00, 0xfc, 0x03, 
+0x06, 0x06, 0x03, 0x04, 0x03, 0x06, 0x8f, 0x03, 0xfc, 0x00};
+static unsigned char times24_104_bits[] = {
+0x07, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0xe6, 0x00, 
+0xf6, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x8f, 0x07};
+static unsigned char times24_105_bits[] = {
+0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06, 0x06, 0x06, 0x0f};
+static unsigned char times24_106_bits[] = {
+0x0c, 0x0c, 0x00, 0x00, 0x00, 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x07, 0x03};
+static unsigned char times24_107_bits[] = {
+0x07, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0xe6, 0x03, 
+0xc6, 0x00, 0x66, 0x00, 0x26, 0x00, 0x16, 0x00, 0x1e, 0x00, 0x36, 0x00, 
+0x76, 0x00, 0xe6, 0x00, 0xc6, 0x01, 0x86, 0x03, 0xcf, 0x07};
+static unsigned char times24_108_bits[] = {
+0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06, 0x06, 0x06, 0x0f};
+static unsigned char times24_109_bits[] = {
+0xe7, 0x70, 0x00, 0xf6, 0xf9, 0x00, 0x8e, 0xc7, 0x01, 0x06, 0x83, 0x01, 
+0x06, 0x83, 0x01, 0x06, 0x83, 0x01, 0x06, 0x83, 0x01, 0x06, 0x83, 0x01, 
+0x06, 0x83, 0x01, 0x06, 0x83, 0x01, 0x06, 0x83, 0x01, 0x8f, 0xc7, 0x03};
+static unsigned char times24_110_bits[] = {
+0xe7, 0x00, 0xf6, 0x01, 0x8e, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x8f, 0x07};
+static unsigned char times24_111_bits[] = {
+0x78, 0x00, 0xce, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x86, 0x01, 0xce, 0x01, 0x78, 0x00};
+static unsigned char times24_112_bits[] = {
+0x77, 0x00, 0xce, 0x01, 0x86, 0x01, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x86, 0x01, 0xce, 0x01, 0x76, 0x00, 
+0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0f, 0x00};
+static unsigned char times24_113_bits[] = {
+0xb8, 0x01, 0xce, 0x01, 0x86, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 
+0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x86, 0x01, 0xce, 0x01, 0xb8, 0x01, 
+0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xc0, 0x03};
+static unsigned char times24_114_bits[] = {
+0x67, 0x76, 0x6e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0f};
+static unsigned char times24_115_bits[] = {
+0x7c, 0x66, 0x43, 0x07, 0x0e, 0x3e, 0x78, 0xe0, 0xc0, 0xc1, 0x63, 0x1f};
+static unsigned char times24_116_bits[] = {
+0x08, 0x0c, 0x0e, 0x7f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x4c, 0x38};
+static unsigned char times24_117_bits[] = {
+0x87, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x8e, 0x03, 0x7c, 0x03, 0x38, 0x07};
+static unsigned char times24_118_bits[] = {
+0x8f, 0x07, 0x06, 0x03, 0x06, 0x01, 0x0c, 0x01, 0x0c, 0x01, 0x8c, 0x00, 
+0x98, 0x00, 0x98, 0x00, 0x58, 0x00, 0x70, 0x00, 0x70, 0x00, 0x20, 0x00};
+static unsigned char times24_119_bits[] = {
+0x8f, 0xe7, 0x01, 0x06, 0xc3, 0x00, 0x06, 0x43, 0x00, 0x0c, 0x43, 0x00, 
+0x0c, 0x43, 0x00, 0x8c, 0x26, 0x00, 0x98, 0x26, 0x00, 0x58, 0x26, 0x00, 
+0x58, 0x14, 0x00, 0x70, 0x1c, 0x00, 0x70, 0x1c, 0x00, 0x20, 0x08, 0x00};
+static unsigned char times24_120_bits[] = {
+0x8f, 0x07, 0x06, 0x03, 0x8c, 0x01, 0x9c, 0x00, 0x58, 0x00, 0x30, 0x00, 
+0x70, 0x00, 0xd8, 0x00, 0xcc, 0x01, 0x84, 0x01, 0x06, 0x03, 0x8f, 0x07};
+static unsigned char times24_121_bits[] = {
+0x8f, 0x07, 0x06, 0x03, 0x06, 0x01, 0x0c, 0x01, 0x0c, 0x01, 0x8c, 0x00, 
+0x98, 0x00, 0x98, 0x00, 0x58, 0x00, 0x70, 0x00, 0x70, 0x00, 0x20, 0x00, 
+0x30, 0x00, 0x10, 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00};
+static unsigned char times24_122_bits[] = {
+0xff, 0xc3, 0x61, 0x70, 0x38, 0x18, 0x1c, 0x0c, 0x0e, 0x86, 0xc3, 0xff};
+static unsigned char times24_123_bits[] = {
+0xe0, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x08, 0x0c, 0x04, 0x03, 0x04, 
+0x0c, 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0xe0};
+static unsigned char times24_124_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char times24_125_bits[] = {
+0x07, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x10, 0x30, 0x20, 0xc0, 0x20, 
+0x30, 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x07};
+static unsigned char times24_126_bits[] = {
+0x1c, 0x04, 0x3e, 0x06, 0xe3, 0x03, 0xc1, 0x01};
+static unsigned char times24_127_bits[] = {
+0x00};
+static unsigned char times24_128_bits[] = {
+0x00};
+static unsigned char times24_129_bits[] = {
+0x00};
+static unsigned char times24_130_bits[] = {
+0x00};
+static unsigned char times24_131_bits[] = {
+0x00};
+static unsigned char times24_132_bits[] = {
+0x00};
+static unsigned char times24_133_bits[] = {
+0x00};
+static unsigned char times24_134_bits[] = {
+0x00};
+static unsigned char times24_135_bits[] = {
+0x00};
+static unsigned char times24_136_bits[] = {
+0x00};
+static unsigned char times24_137_bits[] = {
+0x00};
+static unsigned char times24_138_bits[] = {
+0x00};
+static unsigned char times24_139_bits[] = {
+0x00};
+static unsigned char times24_140_bits[] = {
+0x00};
+static unsigned char times24_141_bits[] = {
+0x00};
+static unsigned char times24_142_bits[] = {
+0x00};
+static unsigned char times24_143_bits[] = {
+0x00};
+static unsigned char times24_144_bits[] = {
+0x00};
+static unsigned char times24_145_bits[] = {
+0x00};
+static unsigned char times24_146_bits[] = {
+0x00};
+static unsigned char times24_147_bits[] = {
+0x00};
+static unsigned char times24_148_bits[] = {
+0x00};
+static unsigned char times24_149_bits[] = {
+0x00};
+static unsigned char times24_150_bits[] = {
+0x00};
+static unsigned char times24_151_bits[] = {
+0x00};
+static unsigned char times24_152_bits[] = {
+0x00};
+static unsigned char times24_153_bits[] = {
+0x00};
+static unsigned char times24_154_bits[] = {
+0x00};
+static unsigned char times24_155_bits[] = {
+0x00};
+static unsigned char times24_156_bits[] = {
+0x00};
+static unsigned char times24_157_bits[] = {
+0x00};
+static unsigned char times24_158_bits[] = {
+0x00};
+static unsigned char times24_159_bits[] = {
+0x00};
+static unsigned char times24_160_bits[] = {
+0x00};
+static unsigned char times24_161_bits[] = {
+0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char times24_162_bits[] = {
+0x80, 0x00, 0x80, 0x00, 0xf8, 0x00, 0xc6, 0x01, 0xc2, 0x01, 0x23, 0x00, 
+0x23, 0x00, 0x13, 0x00, 0x13, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x0e, 0x01, 
+0xfe, 0x00, 0x7c, 0x00, 0x02, 0x00, 0x02, 0x00};
+static unsigned char times24_163_bits[] = {
+0xf0, 0x00, 0x98, 0x01, 0x8c, 0x01, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x3f, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x00, 0x0c, 0x00, 0x1e, 0x02, 0x7d, 0x03, 0xe7, 0x01};
+static unsigned char times24_164_bits[] = {
+0x03, 0x06, 0x77, 0x07, 0xfe, 0x03, 0x8c, 0x01, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x8c, 0x01, 0xfe, 0x03, 0x77, 0x07, 0x03, 0x06};
+static unsigned char times24_165_bits[] = {
+0x1f, 0x3e, 0x0e, 0x0c, 0x0c, 0x04, 0x18, 0x06, 0x18, 0x02, 0x30, 0x03, 
+0x30, 0x01, 0xe0, 0x01, 0xc0, 0x00, 0xf8, 0x07, 0xc0, 0x00, 0xf8, 0x07, 
+0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xf0, 0x03};
+static unsigned char times24_166_bits[] = {
+0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x03};
+static unsigned char times24_167_bits[] = {
+0x38, 0x64, 0x62, 0x06, 0x0e, 0x1c, 0x3c, 0x72, 0xe1, 0xc1, 0xc3, 0x47, 
+0x2e, 0x1c, 0x38, 0x70, 0x60, 0x46, 0x26, 0x1c};
+static unsigned char times24_168_bits[] = {
+0x33, 0x33};
+static unsigned char times24_169_bits[] = {
+0xe0, 0x0f, 0x00, 0x38, 0x38, 0x00, 0x0c, 0x60, 0x00, 0x86, 0xc7, 0x00, 
+0xe2, 0x8c, 0x00, 0x23, 0x98, 0x01, 0x31, 0x00, 0x01, 0x11, 0x00, 0x01, 
+0x11, 0x00, 0x01, 0x11, 0x00, 0x01, 0x31, 0x00, 0x01, 0x23, 0x98, 0x01, 
+0xe2, 0x8e, 0x00, 0x86, 0xc3, 0x00, 0x0c, 0x60, 0x00, 0x38, 0x38, 0x00, 
+0xe0, 0x0f, 0x00};
+static unsigned char times24_170_bits[] = {
+0x1e, 0x33, 0x30, 0x3e, 0x33, 0x33, 0x6e, 0x00, 0x7e};
+static unsigned char times24_171_bits[] = {
+0x10, 0x01, 0x98, 0x01, 0xcc, 0x00, 0x66, 0x00, 0x33, 0x00, 0x33, 0x00, 
+0x66, 0x00, 0xcc, 0x00, 0x98, 0x01, 0x10, 0x01};
+static unsigned char times24_172_bits[] = {
+0xff, 0x0f, 0xff, 0x0f, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 
+0x00, 0x0c};
+static unsigned char times24_173_bits[] = {
+0x7f, 0x7f};
+static unsigned char times24_174_bits[] = {
+0xe0, 0x0f, 0x00, 0x38, 0x38, 0x00, 0x0c, 0x60, 0x00, 0xe6, 0xc7, 0x00, 
+0x42, 0x8c, 0x00, 0x43, 0x88, 0x01, 0x41, 0x08, 0x01, 0x41, 0x0c, 0x01, 
+0xc1, 0x07, 0x01, 0x41, 0x02, 0x01, 0x41, 0x04, 0x01, 0x43, 0x8c, 0x01, 
+0xe2, 0x98, 0x00, 0x06, 0xc0, 0x00, 0x0c, 0x60, 0x00, 0x38, 0x38, 0x00, 
+0xe0, 0x0f, 0x00};
+static unsigned char times24_175_bits[] = {
+0x3f, 0x3f};
+static unsigned char times24_176_bits[] = {
+0x1c, 0x22, 0x41, 0x41, 0x41, 0x22, 0x1c};
+static unsigned char times24_177_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xff, 0x0f, 
+0xff, 0x0f, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 
+0x00, 0x00, 0xff, 0x0f, 0xff, 0x0f};
+static unsigned char times24_178_bits[] = {
+0x1c, 0x32, 0x31, 0x30, 0x10, 0x08, 0x0c, 0x04, 0x22, 0x3f};
+static unsigned char times24_179_bits[] = {
+0x1c, 0x32, 0x31, 0x10, 0x0c, 0x10, 0x30, 0x31, 0x11, 0x0e};
+static unsigned char times24_180_bits[] = {
+0x18, 0x1c, 0x06, 0x01};
+static unsigned char times24_181_bits[] = {
+0x87, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x8e, 0x03, 0x7e, 0x03, 0x3a, 0x07, 
+0x02, 0x00, 0x02, 0x00, 0x03, 0x00, 0x07, 0x00, 0x02, 0x00};
+static unsigned char times24_182_bits[] = {
+0xf8, 0x01, 0x9c, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9f, 0x00, 0x9f, 0x00, 
+0x9f, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, 
+0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 
+0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00};
+static unsigned char times24_183_bits[] = {
+0x03, 0x03};
+static unsigned char times24_184_bits[] = {
+0x08, 0x0c, 0x3c, 0x30, 0x33, 0x1e};
+static unsigned char times24_185_bits[] = {
+0x04, 0x06, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f};
+static unsigned char times24_186_bits[] = {
+0x1e, 0x33, 0x33, 0x33, 0x33, 0x33, 0x1e, 0x00, 0x3f};
+static unsigned char times24_187_bits[] = {
+0x11, 0x00, 0x33, 0x00, 0x66, 0x00, 0xcc, 0x00, 0x98, 0x01, 0x98, 0x01, 
+0xcc, 0x00, 0x66, 0x00, 0x33, 0x00, 0x11, 0x00};
+static unsigned char times24_188_bits[] = {
+0x04, 0x10, 0x06, 0x18, 0x05, 0x0c, 0x04, 0x04, 0x04, 0x06, 0x04, 0x02, 
+0x04, 0x03, 0x84, 0x21, 0x84, 0x30, 0xdf, 0x38, 0x40, 0x28, 0x60, 0x24, 
+0x30, 0x26, 0x10, 0x22, 0x18, 0xff, 0x08, 0x20, 0x0c, 0x20};
+static unsigned char times24_189_bits[] = {
+0x04, 0x10, 0x06, 0x18, 0x05, 0x0c, 0x04, 0x04, 0x04, 0x06, 0x04, 0x02, 
+0x04, 0x03, 0x84, 0x39, 0x84, 0x64, 0xdf, 0x62, 0x40, 0x60, 0x60, 0x20, 
+0x30, 0x10, 0x10, 0x18, 0x18, 0x08, 0x08, 0x44, 0x0c, 0x7e};
+static unsigned char times24_190_bits[] = {
+0x1c, 0x20, 0x00, 0x32, 0x30, 0x00, 0x31, 0x18, 0x00, 0x10, 0x08, 0x00, 
+0x0c, 0x0c, 0x00, 0x10, 0x04, 0x00, 0x30, 0x06, 0x00, 0x31, 0x43, 0x00, 
+0x11, 0x61, 0x00, 0x8e, 0x71, 0x00, 0x80, 0x50, 0x00, 0xc0, 0x48, 0x00, 
+0x60, 0x4c, 0x00, 0x20, 0x44, 0x00, 0x30, 0xfe, 0x01, 0x10, 0x40, 0x00, 
+0x18, 0x40, 0x00};
+static unsigned char times24_191_bits[] = {
+0x30, 0x30, 0x00, 0x00, 0x10, 0x10, 0x18, 0x18, 0x1c, 0x0c, 0x0e, 0x07, 
+0xc3, 0xc3, 0x83, 0xc6, 0x7c};
+static unsigned char times24_192_bits[] = {
+0xc0, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x03, 0x00, 
+0x80, 0x06, 0x00, 0x40, 0x06, 0x00, 0x40, 0x0c, 0x00, 0x60, 0x0c, 0x00, 
+0x20, 0x0c, 0x00, 0x20, 0x18, 0x00, 0x30, 0x18, 0x00, 0xf0, 0x1f, 0x00, 
+0x10, 0x30, 0x00, 0x18, 0x30, 0x00, 0x08, 0x30, 0x00, 0x08, 0x60, 0x00, 
+0x0c, 0x60, 0x00, 0x3f, 0xf8, 0x01};
+static unsigned char times24_193_bits[] = {
+0x00, 0x0c, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x03, 0x00, 
+0x80, 0x06, 0x00, 0x40, 0x06, 0x00, 0x40, 0x0c, 0x00, 0x60, 0x0c, 0x00, 
+0x20, 0x0c, 0x00, 0x20, 0x18, 0x00, 0x30, 0x18, 0x00, 0xf0, 0x1f, 0x00, 
+0x10, 0x30, 0x00, 0x18, 0x30, 0x00, 0x08, 0x30, 0x00, 0x08, 0x60, 0x00, 
+0x0c, 0x60, 0x00, 0x3f, 0xf8, 0x01};
+static unsigned char times24_194_bits[] = {
+0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, 0x60, 0x06, 0x00, 0x10, 0x08, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x03, 0x00, 
+0x80, 0x06, 0x00, 0x40, 0x06, 0x00, 0x40, 0x0c, 0x00, 0x60, 0x0c, 0x00, 
+0x20, 0x0c, 0x00, 0x20, 0x18, 0x00, 0x30, 0x18, 0x00, 0xf0, 0x1f, 0x00, 
+0x10, 0x30, 0x00, 0x18, 0x30, 0x00, 0x08, 0x30, 0x00, 0x08, 0x60, 0x00, 
+0x0c, 0x60, 0x00, 0x3f, 0xf8, 0x01};
+static unsigned char times24_195_bits[] = {
+0xc0, 0x09, 0x00, 0x20, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x03, 0x00, 0x80, 0x06, 0x00, 
+0x40, 0x06, 0x00, 0x40, 0x0c, 0x00, 0x60, 0x0c, 0x00, 0x20, 0x0c, 0x00, 
+0x20, 0x18, 0x00, 0x30, 0x18, 0x00, 0xf0, 0x1f, 0x00, 0x10, 0x30, 0x00, 
+0x18, 0x30, 0x00, 0x08, 0x30, 0x00, 0x08, 0x60, 0x00, 0x0c, 0xe0, 0x00, 
+0x3f, 0xf8, 0x01};
+static unsigned char times24_196_bits[] = {
+0x60, 0x0c, 0x00, 0x60, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x03, 0x00, 0x80, 0x06, 0x00, 
+0x40, 0x06, 0x00, 0x40, 0x0c, 0x00, 0x60, 0x0c, 0x00, 0x20, 0x0c, 0x00, 
+0x20, 0x18, 0x00, 0x30, 0x18, 0x00, 0xf0, 0x1f, 0x00, 0x10, 0x30, 0x00, 
+0x18, 0x30, 0x00, 0x08, 0x30, 0x00, 0x08, 0x60, 0x00, 0x0c, 0x60, 0x00, 
+0x3f, 0xf8, 0x01};
+static unsigned char times24_197_bits[] = {
+0x80, 0x03, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, 0x80, 0x03, 0x00, 
+0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x03, 0x00, 0x80, 0x06, 0x00, 
+0x40, 0x06, 0x00, 0x40, 0x0c, 0x00, 0x60, 0x0c, 0x00, 0x20, 0x0c, 0x00, 
+0x20, 0x18, 0x00, 0x30, 0x18, 0x00, 0xf0, 0x1f, 0x00, 0x10, 0x30, 0x00, 
+0x18, 0x30, 0x00, 0x08, 0x30, 0x00, 0x08, 0x60, 0x00, 0x0c, 0x60, 0x00, 
+0x3f, 0xf8, 0x01};
+static unsigned char times24_198_bits[] = {
+0xc0, 0xff, 0x07, 0x80, 0x07, 0x06, 0x80, 0x06, 0x04, 0x80, 0x06, 0x04, 
+0x40, 0x06, 0x00, 0x40, 0x06, 0x00, 0x60, 0x06, 0x01, 0x20, 0x06, 0x01, 
+0x20, 0xfe, 0x01, 0x30, 0x06, 0x01, 0xf0, 0x07, 0x01, 0x10, 0x06, 0x00, 
+0x18, 0x06, 0x00, 0x08, 0x06, 0x08, 0x08, 0x06, 0x08, 0x0c, 0x06, 0x0c, 
+0x9f, 0xff, 0x0f};
+static unsigned char times24_199_bits[] = {
+0xe0, 0x27, 0x38, 0x3c, 0x1c, 0x30, 0x06, 0x20, 0x06, 0x20, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x06, 0x00, 0x06, 0x20, 0x1c, 0x10, 0x78, 0x1c, 0xe0, 0x07, 0x80, 0x00, 
+0xc0, 0x00, 0xc0, 0x03, 0x00, 0x03, 0x30, 0x03, 0xe0, 0x01};
+static unsigned char times24_200_bits[] = {
+0x18, 0x00, 0x38, 0x00, 0x60, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x0f, 
+0x0c, 0x0c, 0x0c, 0x08, 0x0c, 0x08, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x02, 
+0x0c, 0x02, 0xfc, 0x03, 0x0c, 0x02, 0x0c, 0x02, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x10, 0x0c, 0x10, 0x0c, 0x18, 0xff, 0x1f};
+static unsigned char times24_201_bits[] = {
+0x00, 0x03, 0x80, 0x03, 0xc0, 0x00, 0x20, 0x00, 0x00, 0x00, 0xff, 0x0f, 
+0x0c, 0x0c, 0x0c, 0x08, 0x0c, 0x08, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x02, 
+0x0c, 0x02, 0xfc, 0x03, 0x0c, 0x02, 0x0c, 0x02, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x10, 0x0c, 0x10, 0x0c, 0x18, 0xff, 0x1f};
+static unsigned char times24_202_bits[] = {
+0xc0, 0x00, 0xe0, 0x01, 0x30, 0x03, 0x08, 0x04, 0x00, 0x00, 0xff, 0x0f, 
+0x0c, 0x0c, 0x0c, 0x08, 0x0c, 0x08, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x02, 
+0x0c, 0x02, 0xfc, 0x03, 0x0c, 0x02, 0x0c, 0x02, 0x0c, 0x00, 0x0c, 0x00, 
+0x0c, 0x10, 0x0c, 0x10, 0x0c, 0x18, 0xff, 0x1f};
+static unsigned char times24_203_bits[] = {
+0x98, 0x01, 0x98, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0x0f, 0x0c, 0x0c, 
+0x0c, 0x08, 0x0c, 0x08, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x02, 0x0c, 0x02, 
+0xfc, 0x03, 0x0c, 0x02, 0x0c, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x10, 
+0x0c, 0x10, 0x0c, 0x18, 0xff, 0x1f};
+static unsigned char times24_204_bits[] = {
+0x03, 0x07, 0x0c, 0x10, 0x00, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3f};
+static unsigned char times24_205_bits[] = {
+0x30, 0x38, 0x0c, 0x02, 0x00, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3f};
+static unsigned char times24_206_bits[] = {
+0x18, 0x3c, 0x66, 0x81, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e};
+static unsigned char times24_207_bits[] = {
+0x33, 0x33, 0x00, 0x00, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3f};
+static unsigned char times24_208_bits[] = {
+0xfe, 0x07, 0x18, 0x1c, 0x18, 0x38, 0x18, 0x60, 0x18, 0x60, 0x18, 0xc0, 
+0x18, 0xc0, 0x18, 0xc0, 0xff, 0xc0, 0x18, 0xc0, 0x18, 0xc0, 0x18, 0xc0, 
+0x18, 0x60, 0x18, 0x60, 0x18, 0x38, 0x18, 0x1c, 0xfe, 0x07};
+static unsigned char times24_209_bits[] = {
+0xc0, 0x09, 0x20, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf8, 0x0c, 0x20, 
+0x1c, 0x20, 0x1c, 0x20, 0x34, 0x20, 0x64, 0x20, 0x64, 0x20, 0xc4, 0x20, 
+0x84, 0x21, 0x84, 0x21, 0x04, 0x23, 0x04, 0x26, 0x04, 0x26, 0x04, 0x2c, 
+0x04, 0x38, 0x04, 0x38, 0x1f, 0x30};
+static unsigned char times24_210_bits[] = {
+0x60, 0x00, 0xe0, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x00, 0xe0, 0x07, 
+0x38, 0x1c, 0x1c, 0x38, 0x06, 0x60, 0x06, 0x60, 0x03, 0xc0, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x06, 0x60, 
+0x06, 0x60, 0x1c, 0x38, 0x38, 0x1c, 0xe0, 0x07};
+static unsigned char times24_211_bits[] = {
+0x00, 0x0c, 0x00, 0x0e, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x07, 
+0x38, 0x1c, 0x1c, 0x38, 0x06, 0x60, 0x06, 0x60, 0x03, 0xc0, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x06, 0x60, 
+0x06, 0x60, 0x1c, 0x38, 0x38, 0x1c, 0xe0, 0x07};
+static unsigned char times24_212_bits[] = {
+0x80, 0x01, 0xc0, 0x03, 0x60, 0x06, 0x10, 0x08, 0x00, 0x00, 0xe0, 0x07, 
+0x38, 0x1c, 0x1c, 0x38, 0x06, 0x60, 0x06, 0x60, 0x03, 0xc0, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x06, 0x60, 
+0x06, 0x60, 0x1c, 0x38, 0x38, 0x1c, 0xe0, 0x07};
+static unsigned char times24_213_bits[] = {
+0xc0, 0x09, 0x20, 0x07, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x38, 0x1c, 
+0x1c, 0x38, 0x06, 0x60, 0x06, 0x60, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x06, 0x60, 0x06, 0x60, 
+0x1c, 0x38, 0x38, 0x1c, 0xe0, 0x07};
+static unsigned char times24_214_bits[] = {
+0x60, 0x06, 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x38, 0x1c, 
+0x1c, 0x38, 0x06, 0x60, 0x06, 0x60, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 
+0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x06, 0x60, 0x06, 0x60, 
+0x1c, 0x38, 0x38, 0x1c, 0xe0, 0x07};
+static unsigned char times24_215_bits[] = {
+0x01, 0x02, 0x03, 0x03, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 
+0x78, 0x00, 0xcc, 0x00, 0x86, 0x01, 0x03, 0x03, 0x01, 0x02};
+static unsigned char times24_216_bits[] = {
+0x00, 0x20, 0xe0, 0x27, 0x38, 0x1c, 0x1c, 0x38, 0x06, 0x64, 0x06, 0x62, 
+0x03, 0xc2, 0x03, 0xc1, 0x03, 0xc1, 0x83, 0xc0, 0x83, 0xc0, 0x43, 0xc0, 
+0x43, 0xc0, 0x26, 0x60, 0x16, 0x60, 0x1c, 0x38, 0x38, 0x1c, 0xe4, 0x07, 
+0x04, 0x00};
+static unsigned char times24_217_bits[] = {
+0x60, 0x00, 0xe0, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x00, 0x3f, 0xf8, 
+0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 
+0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 
+0x0c, 0x10, 0x18, 0x10, 0x38, 0x0c, 0xe0, 0x07};
+static unsigned char times24_218_bits[] = {
+0x00, 0x0c, 0x00, 0x0e, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x3f, 0xf8, 
+0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 
+0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 
+0x0c, 0x10, 0x18, 0x10, 0x38, 0x0c, 0xe0, 0x07};
+static unsigned char times24_219_bits[] = {
+0x80, 0x01, 0xc0, 0x03, 0x60, 0x06, 0x10, 0x08, 0x00, 0x00, 0x3f, 0xf8, 
+0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 
+0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 
+0x0c, 0x10, 0x18, 0x10, 0x38, 0x0c, 0xe0, 0x07};
+static unsigned char times24_220_bits[] = {
+0x60, 0x0c, 0x60, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x0c, 0x20, 
+0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 
+0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x10, 
+0x18, 0x10, 0x38, 0x0c, 0xe0, 0x07};
+static unsigned char times24_221_bits[] = {
+0x00, 0x0c, 0x00, 0x0e, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x3f, 0xfc, 
+0x0c, 0x30, 0x1c, 0x10, 0x18, 0x18, 0x38, 0x08, 0x30, 0x0c, 0x60, 0x04, 
+0x60, 0x06, 0xc0, 0x02, 0xc0, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 
+0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xe0, 0x07};
+static unsigned char times24_222_bits[] = {
+0x3f, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0xfc, 0x03, 0x0c, 0x0e, 
+0x0c, 0x0c, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x18, 0x0c, 0x0c, 0x0c, 0x0e, 
+0xfc, 0x03, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x3f, 0x00};
+static unsigned char times24_223_bits[] = {
+0x78, 0x00, 0xcc, 0x00, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, 
+0xc6, 0x00, 0x36, 0x00, 0xe6, 0x00, 0xc6, 0x01, 0x86, 0x01, 0x86, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x36, 0x03, 0x36, 0x01, 0xe7, 0x00};
+static unsigned char times24_224_bits[] = {
+0x06, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x00, 0x7c, 0x00, 
+0xe6, 0x00, 0xc6, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xdc, 0x00, 0xc6, 0x00, 
+0xc3, 0x00, 0xc3, 0x00, 0xe3, 0x00, 0xdf, 0x00, 0x8e, 0x01};
+static unsigned char times24_225_bits[] = {
+0xc0, 0x00, 0xe0, 0x00, 0x30, 0x00, 0x08, 0x00, 0x00, 0x00, 0x7c, 0x00, 
+0xe6, 0x00, 0xc6, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xdc, 0x00, 0xc6, 0x00, 
+0xc3, 0x00, 0xc3, 0x00, 0xe3, 0x00, 0xdf, 0x00, 0x8e, 0x01};
+static unsigned char times24_226_bits[] = {
+0x18, 0x00, 0x3c, 0x00, 0x24, 0x00, 0x42, 0x00, 0x00, 0x00, 0x7c, 0x00, 
+0xe6, 0x00, 0xc6, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xdc, 0x00, 0xc6, 0x00, 
+0xc3, 0x00, 0xc3, 0x00, 0xe3, 0x00, 0xdf, 0x00, 0x8e, 0x01};
+static unsigned char times24_227_bits[] = {
+0x5c, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xe6, 0x00, 
+0xc6, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xdc, 0x00, 0xc6, 0x00, 0xc3, 0x00, 
+0xc3, 0x00, 0xe3, 0x00, 0xdf, 0x00, 0x8e, 0x01};
+static unsigned char times24_228_bits[] = {
+0x66, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xe6, 0x00, 
+0xc6, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xdc, 0x00, 0xc6, 0x00, 0xc3, 0x00, 
+0xc3, 0x00, 0xe3, 0x00, 0xdf, 0x00, 0x8e, 0x01};
+static unsigned char times24_229_bits[] = {
+0x38, 0x00, 0x44, 0x00, 0x44, 0x00, 0x38, 0x00, 0x00, 0x00, 0x7c, 0x00, 
+0xe6, 0x00, 0xc6, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xdc, 0x00, 0xc6, 0x00, 
+0xc3, 0x00, 0xc3, 0x00, 0xe3, 0x00, 0xdf, 0x00, 0x8e, 0x01};
+static unsigned char times24_230_bits[] = {
+0x3c, 0x0f, 0xe6, 0x19, 0xc6, 0x30, 0xc0, 0x30, 0xf0, 0x3f, 0xdc, 0x00, 
+0xc6, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xe3, 0x21, 0xdf, 0x1f, 0x0e, 0x0f};
+static unsigned char times24_231_bits[] = {
+0xf8, 0x00, 0xc6, 0x01, 0x82, 0x01, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x01, 0xfe, 0x00, 0x78, 0x00, 
+0x10, 0x00, 0x18, 0x00, 0x78, 0x00, 0x60, 0x00, 0x66, 0x00, 0x3c, 0x00};
+static unsigned char times24_232_bits[] = {
+0x06, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x00, 0x78, 0x00, 
+0xc6, 0x00, 0x82, 0x01, 0x83, 0x01, 0xff, 0x01, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x07, 0x00, 0x0e, 0x01, 0xfe, 0x00, 0x78, 0x00};
+static unsigned char times24_233_bits[] = {
+0xc0, 0x00, 0xe0, 0x00, 0x30, 0x00, 0x08, 0x00, 0x00, 0x00, 0x78, 0x00, 
+0xc6, 0x00, 0x82, 0x01, 0x83, 0x01, 0xff, 0x01, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x07, 0x00, 0x0e, 0x01, 0xfe, 0x00, 0x78, 0x00};
+static unsigned char times24_234_bits[] = {
+0x30, 0x00, 0x78, 0x00, 0x48, 0x00, 0x84, 0x00, 0x00, 0x00, 0x78, 0x00, 
+0xc6, 0x00, 0x82, 0x01, 0x83, 0x01, 0xff, 0x01, 0x03, 0x00, 0x03, 0x00, 
+0x03, 0x00, 0x07, 0x00, 0x0e, 0x01, 0xfe, 0x00, 0x78, 0x00};
+static unsigned char times24_235_bits[] = {
+0xcc, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xc6, 0x00, 
+0x82, 0x01, 0x83, 0x01, 0xff, 0x01, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 
+0x07, 0x00, 0x0e, 0x01, 0xfe, 0x00, 0x78, 0x00};
+static unsigned char times24_236_bits[] = {
+0x03, 0x07, 0x0c, 0x10, 0x00, 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x1e};
+static unsigned char times24_237_bits[] = {
+0x18, 0x1c, 0x06, 0x01, 0x00, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 
+0x06, 0x06, 0x06, 0x06, 0x0f};
+static unsigned char times24_238_bits[] = {
+0x0c, 0x1e, 0x12, 0x21, 0x00, 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x0c, 0x1e};
+static unsigned char times24_239_bits[] = {
+0x33, 0x33, 0x00, 0x00, 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 
+0x0c, 0x0c, 0x0c, 0x1e};
+static unsigned char times24_240_bits[] = {
+0x03, 0x00, 0x8e, 0x01, 0x78, 0x00, 0x3c, 0x00, 0x63, 0x00, 0xf8, 0x00, 
+0xce, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x86, 0x01, 0xce, 0x01, 0x78, 0x00};
+static unsigned char times24_241_bits[] = {
+0x38, 0x01, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0xf6, 0x01, 
+0x8e, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x8f, 0x07};
+static unsigned char times24_242_bits[] = {
+0x0c, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x40, 0x00, 0x00, 0x00, 0x78, 0x00, 
+0xce, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x86, 0x01, 0xce, 0x01, 0x78, 0x00};
+static unsigned char times24_243_bits[] = {
+0x80, 0x01, 0xc0, 0x01, 0x60, 0x00, 0x10, 0x00, 0x00, 0x00, 0x78, 0x00, 
+0xce, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x86, 0x01, 0xce, 0x01, 0x78, 0x00};
+static unsigned char times24_244_bits[] = {
+0x30, 0x00, 0x78, 0x00, 0x48, 0x00, 0x84, 0x00, 0x00, 0x00, 0x78, 0x00, 
+0xce, 0x01, 0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x03, 0x03, 0x86, 0x01, 0xce, 0x01, 0x78, 0x00};
+static unsigned char times24_245_bits[] = {
+0x38, 0x01, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xce, 0x01, 
+0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x86, 0x01, 0xce, 0x01, 0x78, 0x00};
+static unsigned char times24_246_bits[] = {
+0xcc, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xce, 0x01, 
+0x86, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
+0x03, 0x03, 0x86, 0x01, 0xce, 0x01, 0x78, 0x00};
+static unsigned char times24_247_bits[] = {
+0x60, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x0f, 0xff, 0x0f, 
+0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00};
+static unsigned char times24_248_bits[] = {
+0x00, 0x03, 0x78, 0x03, 0xce, 0x01, 0xc6, 0x01, 0x63, 0x03, 0x23, 0x03, 
+0x33, 0x03, 0x13, 0x03, 0x1b, 0x03, 0x0b, 0x03, 0x8e, 0x01, 0xce, 0x01, 
+0x7b, 0x00, 0x03, 0x00};
+static unsigned char times24_249_bits[] = {
+0x0c, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x40, 0x00, 0x00, 0x00, 0x87, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x8e, 0x03, 0x7c, 0x03, 0x38, 0x07};
+static unsigned char times24_250_bits[] = {
+0x80, 0x01, 0xc0, 0x01, 0x60, 0x00, 0x10, 0x00, 0x00, 0x00, 0x87, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x8e, 0x03, 0x7c, 0x03, 0x38, 0x07};
+static unsigned char times24_251_bits[] = {
+0x30, 0x00, 0x78, 0x00, 0x48, 0x00, 0x84, 0x00, 0x00, 0x00, 0x87, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x8e, 0x03, 0x7c, 0x03, 0x38, 0x07};
+static unsigned char times24_252_bits[] = {
+0xcc, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x8e, 0x03, 0x7c, 0x03, 0x38, 0x07};
+static unsigned char times24_253_bits[] = {
+0x80, 0x01, 0xc0, 0x01, 0x60, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8f, 0x07, 
+0x06, 0x03, 0x06, 0x01, 0x0c, 0x01, 0x0c, 0x01, 0x8c, 0x00, 0x98, 0x00, 
+0x98, 0x00, 0x58, 0x00, 0x70, 0x00, 0x70, 0x00, 0x20, 0x00, 0x30, 0x00, 
+0x10, 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00};
+static unsigned char times24_254_bits[] = {
+0x07, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x76, 0x00, 
+0xce, 0x01, 0x86, 0x01, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 
+0x06, 0x03, 0x06, 0x03, 0x86, 0x01, 0xce, 0x01, 0x76, 0x00, 0x06, 0x00, 
+0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0f, 0x00};
+static unsigned char times24_255_bits[] = {
+0xcc, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x07, 0x06, 0x03, 
+0x06, 0x01, 0x0c, 0x01, 0x0c, 0x01, 0x8c, 0x00, 0x98, 0x00, 0x98, 0x00, 
+0x58, 0x00, 0x70, 0x00, 0x70, 0x00, 0x20, 0x00, 0x30, 0x00, 0x10, 0x00, 
+0x18, 0x00, 0x0f, 0x00, 0x07, 0x00};
+static RotFont times24font[] = {
+{5, 1, 1, times24_0_bits},
+{5, 1, 1, times24_1_bits},
+{5, 1, 1, times24_2_bits},
+{5, 1, 1, times24_3_bits},
+{5, 1, 1, times24_4_bits},
+{5, 1, 1, times24_5_bits},
+{5, 1, 1, times24_6_bits},
+{5, 1, 1, times24_7_bits},
+{5, 1, 1, times24_8_bits},
+{5, 1, 1, times24_9_bits},
+{5, 1, 1, times24_10_bits},
+{5, 1, 1, times24_11_bits},
+{5, 1, 1, times24_12_bits},
+{5, 1, 1, times24_13_bits},
+{5, 1, 1, times24_14_bits},
+{5, 1, 1, times24_15_bits},
+{5, 1, 1, times24_16_bits},
+{5, 1, 1, times24_17_bits},
+{5, 1, 1, times24_18_bits},
+{5, 1, 1, times24_19_bits},
+{5, 1, 1, times24_20_bits},
+{5, 1, 1, times24_21_bits},
+{5, 1, 1, times24_22_bits},
+{5, 1, 1, times24_23_bits},
+{5, 1, 1, times24_24_bits},
+{5, 1, 1, times24_25_bits},
+{5, 1, 1, times24_26_bits},
+{5, 1, 1, times24_27_bits},
+{5, 1, 1, times24_28_bits},
+{5, 1, 1, times24_29_bits},
+{5, 1, 1, times24_30_bits},
+{5, 1, 1, times24_31_bits},
+{10, 1, 1, times24_32_bits},
+{2, 17, 17, times24_33_bits},
+{6, 5, 17, times24_34_bits},
+{11, 17, 17, times24_35_bits},
+{11, 21, 19, times24_36_bits},
+{17, 16, 16, times24_37_bits},
+{16, 17, 17, times24_38_bits},
+{3, 5, 17, times24_39_bits},
+{6, 22, 17, times24_40_bits},
+{6, 22, 17, times24_41_bits},
+{9, 9, 17, times24_42_bits},
+{12, 12, 13, times24_43_bits},
+{3, 5, 2, times24_44_bits},
+{12, 2, 8, times24_45_bits},
+{2, 2, 2, times24_46_bits},
+{8, 20, 17, times24_47_bits},
+{10, 17, 17, times24_48_bits},
+{8, 17, 17, times24_49_bits},
+{10, 17, 17, times24_50_bits},
+{9, 17, 17, times24_51_bits},
+{10, 17, 17, times24_52_bits},
+{10, 17, 17, times24_53_bits},
+{10, 17, 17, times24_54_bits},
+{10, 17, 17, times24_55_bits},
+{10, 17, 17, times24_56_bits},
+{10, 17, 17, times24_57_bits},
+{2, 11, 11, times24_58_bits},
+{3, 14, 11, times24_59_bits},
+{11, 11, 12, times24_60_bits},
+{12, 6, 10, times24_61_bits},
+{11, 11, 12, times24_62_bits},
+{8, 17, 17, times24_63_bits},
+{18, 20, 17, times24_64_bits},
+{17, 17, 17, times24_65_bits},
+{14, 17, 17, times24_66_bits},
+{14, 17, 17, times24_67_bits},
+{15, 17, 17, times24_68_bits},
+{13, 17, 17, times24_69_bits},
+{12, 17, 17, times24_70_bits},
+{16, 17, 17, times24_71_bits},
+{17, 17, 17, times24_72_bits},
+{6, 17, 17, times24_73_bits},
+{9, 17, 17, times24_74_bits},
+{16, 17, 17, times24_75_bits},
+{13, 17, 17, times24_76_bits},
+{21, 17, 17, times24_77_bits},
+{16, 17, 17, times24_78_bits},
+{16, 17, 17, times24_79_bits},
+{13, 17, 17, times24_80_bits},
+{16, 22, 17, times24_81_bits},
+{15, 17, 17, times24_82_bits},
+{11, 17, 17, times24_83_bits},
+{14, 17, 17, times24_84_bits},
+{16, 17, 17, times24_85_bits},
+{17, 17, 17, times24_86_bits},
+{23, 17, 17, times24_87_bits},
+{18, 17, 17, times24_88_bits},
+{16, 17, 17, times24_89_bits},
+{13, 17, 17, times24_90_bits},
+{5, 21, 17, times24_91_bits},
+{7, 17, 17, times24_92_bits},
+{5, 21, 17, times24_93_bits},
+{9, 9, 17, times24_94_bits},
+{13, 2, -3, times24_95_bits},
+{3, 5, 17, times24_96_bits},
+{9, 12, 12, times24_97_bits},
+{10, 17, 17, times24_98_bits},
+{9, 12, 12, times24_99_bits},
+{10, 17, 17, times24_100_bits},
+{9, 12, 12, times24_101_bits},
+{7, 17, 17, times24_102_bits},
+{11, 17, 12, times24_103_bits},
+{11, 17, 17, times24_104_bits},
+{4, 17, 17, times24_105_bits},
+{4, 22, 17, times24_106_bits},
+{11, 17, 17, times24_107_bits},
+{4, 17, 17, times24_108_bits},
+{18, 12, 12, times24_109_bits},
+{11, 12, 12, times24_110_bits},
+{10, 12, 12, times24_111_bits},
+{10, 17, 12, times24_112_bits},
+{10, 17, 12, times24_113_bits},
+{7, 12, 12, times24_114_bits},
+{8, 12, 12, times24_115_bits},
+{7, 15, 15, times24_116_bits},
+{11, 12, 12, times24_117_bits},
+{11, 12, 12, times24_118_bits},
+{17, 12, 12, times24_119_bits},
+{11, 12, 12, times24_120_bits},
+{11, 17, 12, times24_121_bits},
+{8, 12, 12, times24_122_bits},
+{8, 22, 17, times24_123_bits},
+{2, 17, 17, times24_124_bits},
+{8, 22, 17, times24_125_bits},
+{11, 4, 9, times24_126_bits},
+{5, 1, 1, times24_127_bits},
+{5, 1, 1, times24_128_bits},
+{5, 1, 1, times24_129_bits},
+{5, 1, 1, times24_130_bits},
+{5, 1, 1, times24_131_bits},
+{5, 1, 1, times24_132_bits},
+{5, 1, 1, times24_133_bits},
+{5, 1, 1, times24_134_bits},
+{5, 1, 1, times24_135_bits},
+{5, 1, 1, times24_136_bits},
+{5, 1, 1, times24_137_bits},
+{5, 1, 1, times24_138_bits},
+{5, 1, 1, times24_139_bits},
+{5, 1, 1, times24_140_bits},
+{5, 1, 1, times24_141_bits},
+{5, 1, 1, times24_142_bits},
+{5, 1, 1, times24_143_bits},
+{5, 1, 1, times24_144_bits},
+{5, 1, 1, times24_145_bits},
+{5, 1, 1, times24_146_bits},
+{5, 1, 1, times24_147_bits},
+{5, 1, 1, times24_148_bits},
+{5, 1, 1, times24_149_bits},
+{5, 1, 1, times24_150_bits},
+{5, 1, 1, times24_151_bits},
+{5, 1, 1, times24_152_bits},
+{5, 1, 1, times24_153_bits},
+{5, 1, 1, times24_154_bits},
+{5, 1, 1, times24_155_bits},
+{5, 1, 1, times24_156_bits},
+{5, 1, 1, times24_157_bits},
+{5, 1, 1, times24_158_bits},
+{5, 1, 1, times24_159_bits},
+{1, 1, 1, times24_160_bits},
+{2, 17, 12, times24_161_bits},
+{9, 16, 14, times24_162_bits},
+{10, 17, 17, times24_163_bits},
+{11, 12, 15, times24_164_bits},
+{14, 17, 17, times24_165_bits},
+{2, 17, 17, times24_166_bits},
+{8, 20, 18, times24_167_bits},
+{6, 2, 16, times24_168_bits},
+{17, 17, 17, times24_169_bits},
+{7, 9, 17, times24_170_bits},
+{9, 10, 11, times24_171_bits},
+{12, 7, 10, times24_172_bits},
+{7, 2, 7, times24_173_bits},
+{17, 17, 17, times24_174_bits},
+{6, 2, 16, times24_175_bits},
+{7, 7, 17, times24_176_bits},
+{12, 15, 15, times24_177_bits},
+{6, 10, 17, times24_178_bits},
+{6, 10, 17, times24_179_bits},
+{5, 4, 17, times24_180_bits},
+{11, 17, 12, times24_181_bits},
+{9, 22, 17, times24_182_bits},
+{2, 2, 8, times24_183_bits},
+{6, 6, 0, times24_184_bits},
+{5, 10, 17, times24_185_bits},
+{6, 9, 17, times24_186_bits},
+{9, 10, 11, times24_187_bits},
+{16, 17, 17, times24_188_bits},
+{15, 17, 17, times24_189_bits},
+{17, 17, 17, times24_190_bits},
+{8, 17, 12, times24_191_bits},
+{17, 22, 22, times24_192_bits},
+{17, 22, 22, times24_193_bits},
+{17, 22, 22, times24_194_bits},
+{17, 21, 21, times24_195_bits},
+{17, 21, 21, times24_196_bits},
+{17, 21, 21, times24_197_bits},
+{20, 17, 17, times24_198_bits},
+{14, 23, 17, times24_199_bits},
+{13, 22, 22, times24_200_bits},
+{13, 22, 22, times24_201_bits},
+{13, 22, 22, times24_202_bits},
+{13, 21, 21, times24_203_bits},
+{6, 22, 22, times24_204_bits},
+{6, 22, 22, times24_205_bits},
+{8, 22, 22, times24_206_bits},
+{6, 21, 21, times24_207_bits},
+{16, 17, 17, times24_208_bits},
+{16, 21, 21, times24_209_bits},
+{16, 22, 22, times24_210_bits},
+{16, 22, 22, times24_211_bits},
+{16, 22, 22, times24_212_bits},
+{16, 21, 21, times24_213_bits},
+{16, 21, 21, times24_214_bits},
+{10, 11, 12, times24_215_bits},
+{16, 19, 18, times24_216_bits},
+{16, 22, 22, times24_217_bits},
+{16, 22, 22, times24_218_bits},
+{16, 22, 22, times24_219_bits},
+{16, 21, 21, times24_220_bits},
+{16, 22, 22, times24_221_bits},
+{13, 17, 17, times24_222_bits},
+{10, 17, 17, times24_223_bits},
+{9, 17, 17, times24_224_bits},
+{9, 17, 17, times24_225_bits},
+{9, 17, 17, times24_226_bits},
+{9, 16, 16, times24_227_bits},
+{9, 16, 16, times24_228_bits},
+{9, 17, 17, times24_229_bits},
+{14, 12, 12, times24_230_bits},
+{9, 18, 12, times24_231_bits},
+{9, 17, 17, times24_232_bits},
+{9, 17, 17, times24_233_bits},
+{9, 17, 17, times24_234_bits},
+{9, 16, 16, times24_235_bits},
+{5, 17, 17, times24_236_bits},
+{5, 17, 17, times24_237_bits},
+{6, 17, 17, times24_238_bits},
+{6, 16, 16, times24_239_bits},
+{10, 17, 17, times24_240_bits},
+{11, 16, 16, times24_241_bits},
+{10, 17, 17, times24_242_bits},
+{10, 17, 17, times24_243_bits},
+{10, 17, 17, times24_244_bits},
+{10, 16, 16, times24_245_bits},
+{10, 16, 16, times24_246_bits},
+{12, 10, 12, times24_247_bits},
+{10, 14, 13, times24_248_bits},
+{11, 17, 17, times24_249_bits},
+{11, 17, 17, times24_250_bits},
+{11, 17, 17, times24_251_bits},
+{11, 16, 16, times24_252_bits},
+{11, 22, 17, times24_253_bits},
+{10, 22, 17, times24_254_bits},
+{11, 21, 16, times24_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times8.bdf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times8.bdf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times8.bdf	(revision 22322)
@@ -0,0 +1,2435 @@
+STARTFONT 2.1
+COMMENT  
+COMMENT  Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
+COMMENT  
+COMMENT  WARNING:  This bdf file was generated from a font server using
+COMMENT  fstobdf.  The resulting font is subject to the same copyright,
+COMMENT  license, and trademark restrictions as the original font.  The
+COMMENT  authors and distributors of fstobdf disclaim all liability for
+COMMENT  misuse of the program or its output.
+COMMENT  
+FONT -Adobe-Times-Medium-R-Normal--8-80-75-75-P-44-ISO8859-1
+SIZE 8 75 75
+FONTBOUNDINGBOX 9 10 -1 -2
+STARTPROPERTIES 32
+FOUNDRY "Adobe"
+FAMILY_NAME "Times"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 8
+POINT_SIZE 80
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "P"
+AVERAGE_WIDTH 44
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+CAP_HEIGHT 5
+X_HEIGHT 4
+FACE_NAME "Times Roman"
+COPYRIGHT "Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved."
+NOTICE "Times is a trademark of Linotype-Hell AG and/or its subsidiaries."
+_DEC_DEVICE_FONTNAMES "PS=Times-Roman"
+_DEC_PRODUCTINFO "DECwindows Fonts V2.2, 07-Nov-1991"
+RELATIVE_SETWIDTH 50
+RELATIVE_WEIGHT 50
+CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD"
+FULL_NAME "Times Roman"
+FONT "-Adobe-Times-Medium-R-Normal--8-80-75-75-P-44-ISO8859-1"
+WEIGHT 10
+RESOLUTION 103
+QUAD_WIDTH 5
+DEFAULT_CHAR 32
+FONT_ASCENT 7
+FONT_DESCENT 2
+ENDPROPERTIES
+CHARS 191
+STARTCHAR space
+ENCODING 32
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclam
+ENCODING 33
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 6 1 0
+BITMAP
+80
+80
+80
+80
+00
+80
+ENDCHAR
+STARTCHAR quotedbl
+ENCODING 34
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 2 0 3
+BITMAP
+a0
+a0
+ENDCHAR
+STARTCHAR numbersign
+ENCODING 35
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 5 0 0
+BITMAP
+50
+f0
+50
+f0
+50
+ENDCHAR
+STARTCHAR dollar
+ENCODING 36
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 -1
+BITMAP
+40
+60
+80
+60
+a0
+c0
+40
+ENDCHAR
+STARTCHAR percent
+ENCODING 37
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+c8
+d0
+20
+58
+98
+ENDCHAR
+STARTCHAR ampersand
+ENCODING 38
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 6 0 0
+BITMAP
+20
+50
+60
+a8
+90
+78
+ENDCHAR
+STARTCHAR apostrophe
+ENCODING 39
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 2 1 4
+BITMAP
+80
+80
+ENDCHAR
+STARTCHAR parenleft
+ENCODING 40
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 7 1 -1
+BITMAP
+40
+80
+80
+80
+80
+80
+40
+ENDCHAR
+STARTCHAR parenright
+ENCODING 41
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 7 0 -1
+BITMAP
+80
+40
+40
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR asterisk
+ENCODING 42
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 3 1 2
+BITMAP
+a0
+40
+a0
+ENDCHAR
+STARTCHAR plus
+ENCODING 43
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+BITMAP
+20
+20
+f0
+20
+20
+ENDCHAR
+STARTCHAR comma
+ENCODING 44
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 2 1 -1
+BITMAP
+40
+80
+ENDCHAR
+STARTCHAR minus
+ENCODING 45
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 1 1 2
+BITMAP
+f0
+ENDCHAR
+STARTCHAR period
+ENCODING 46
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 1 1 0
+BITMAP
+80
+ENDCHAR
+STARTCHAR slash
+ENCODING 47
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 7 0 -1
+BITMAP
+20
+20
+40
+40
+40
+80
+80
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+40
+a0
+a0
+a0
+40
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+40
+c0
+40
+40
+e0
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+c0
+20
+40
+80
+e0
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+c0
+20
+40
+20
+c0
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 5 0 0
+BITMAP
+20
+60
+a0
+f0
+20
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+e0
+80
+60
+20
+c0
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+60
+80
+c0
+a0
+40
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+e0
+20
+40
+80
+80
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+40
+a0
+40
+a0
+40
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+40
+a0
+60
+20
+c0
+ENDCHAR
+STARTCHAR colon
+ENCODING 58
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 3 1 0
+BITMAP
+80
+00
+80
+ENDCHAR
+STARTCHAR semicolon
+ENCODING 59
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 4 1 -1
+BITMAP
+40
+00
+40
+80
+ENDCHAR
+STARTCHAR less
+ENCODING 60
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+20
+40
+80
+40
+20
+ENDCHAR
+STARTCHAR equal
+ENCODING 61
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 3 0 1
+BITMAP
+f0
+00
+f0
+ENDCHAR
+STARTCHAR greater
+ENCODING 62
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 1 0
+BITMAP
+80
+40
+20
+40
+80
+ENDCHAR
+STARTCHAR question
+ENCODING 63
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 5 0 0
+BITMAP
+c0
+20
+40
+00
+40
+ENDCHAR
+STARTCHAR at
+ENCODING 64
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 6 7 0 -1
+BITMAP
+78
+84
+9c
+a4
+bc
+80
+78
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+20
+50
+70
+50
+d8
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+BITMAP
+e0
+50
+60
+50
+e0
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+70
+88
+80
+88
+70
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+f0
+48
+48
+48
+f0
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+BITMAP
+f0
+40
+60
+40
+f0
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+BITMAP
+f0
+40
+70
+40
+c0
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+70
+80
+b8
+88
+70
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 5 0 0
+BITMAP
+cc
+48
+78
+48
+cc
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 5 0 0
+BITMAP
+e0
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 5 0 0
+BITMAP
+c0
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+c8
+50
+60
+50
+d8
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+BITMAP
+e0
+40
+40
+50
+f0
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 7 5 0 0
+BITMAP
+ee
+6c
+54
+54
+c6
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 5 0 0
+BITMAP
+cc
+68
+68
+58
+c8
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+70
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+BITMAP
+f0
+48
+70
+40
+c0
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 7 0 -2
+BITMAP
+70
+88
+88
+88
+70
+20
+10
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+e0
+50
+60
+50
+d8
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+BITMAP
+70
+80
+60
+10
+e0
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 5 0 0
+BITMAP
+f8
+a8
+20
+20
+70
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 5 0 0
+BITMAP
+cc
+48
+48
+48
+30
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+d8
+50
+50
+20
+20
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 960 0
+DWIDTH 8 0
+BBX 8 5 0 0
+BITMAP
+d3
+52
+4a
+5a
+2c
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 5 0 0
+BITMAP
+cc
+30
+30
+48
+cc
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+d8
+50
+20
+20
+70
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 1 0
+BITMAP
+f0
+a0
+40
+90
+f0
+ENDCHAR
+STARTCHAR bracketleft
+ENCODING 91
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 7 0 -1
+BITMAP
+c0
+80
+80
+80
+80
+80
+c0
+ENDCHAR
+STARTCHAR backslash
+ENCODING 92
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 7 0 -1
+BITMAP
+80
+80
+40
+40
+40
+20
+20
+ENDCHAR
+STARTCHAR bracketright
+ENCODING 93
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 7 0 -1
+BITMAP
+c0
+40
+40
+40
+40
+40
+c0
+ENDCHAR
+STARTCHAR asciicircum
+ENCODING 94
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 2 1 4
+BITMAP
+40
+a0
+ENDCHAR
+STARTCHAR underscore
+ENCODING 95
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 1 0 -1
+BITMAP
+f0
+ENDCHAR
+STARTCHAR grave
+ENCODING 96
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 2 1 4
+BITMAP
+80
+80
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 4 0 0
+BITMAP
+80
+40
+c0
+c0
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+80
+c0
+a0
+a0
+c0
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 2 4 0 0
+BITMAP
+40
+80
+80
+40
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+20
+60
+a0
+a0
+60
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 4 0 0
+BITMAP
+40
+e0
+80
+40
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 5 0 0
+BITMAP
+20
+40
+e0
+40
+40
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 5 0 -1
+BITMAP
+70
+a0
+40
+a0
+40
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+80
+c0
+a0
+a0
+a0
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 6 0 0
+BITMAP
+80
+00
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 8 -1 -2
+BITMAP
+40
+00
+40
+40
+40
+40
+40
+80
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+80
+a0
+c0
+c0
+a0
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 1 5 0 0
+BITMAP
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 4 0 0
+BITMAP
+d0
+a8
+a8
+a8
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 4 0 0
+BITMAP
+c0
+a0
+a0
+a0
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 4 0 0
+BITMAP
+40
+a0
+a0
+40
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 -2
+BITMAP
+c0
+a0
+a0
+c0
+80
+80
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 -2
+BITMAP
+60
+a0
+a0
+60
+20
+20
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 4 0 0
+BITMAP
+a0
+c0
+80
+80
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 4 0 0
+BITMAP
+c0
+80
+40
+c0
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 5 0 0
+BITMAP
+80
+c0
+80
+80
+40
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 4 0 0
+BITMAP
+a0
+a0
+a0
+60
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 4 0 0
+BITMAP
+a0
+a0
+a0
+40
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 4 0 0
+BITMAP
+a8
+a8
+a8
+50
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+BITMAP
+90
+60
+60
+90
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 -2
+BITMAP
+a0
+a0
+a0
+40
+40
+80
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 4 0 0
+BITMAP
+e0
+40
+80
+e0
+ENDCHAR
+STARTCHAR braceleft
+ENCODING 123
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 7 0 -1
+BITMAP
+20
+40
+40
+c0
+40
+40
+20
+ENDCHAR
+STARTCHAR bar
+ENCODING 124
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 7 0 -1
+BITMAP
+80
+80
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR braceright
+ENCODING 125
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 7 0 -1
+BITMAP
+80
+40
+40
+60
+40
+40
+80
+ENDCHAR
+STARTCHAR asciitilde
+ENCODING 126
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 2 0 2
+BITMAP
+68
+b0
+ENDCHAR
+STARTCHAR nobreakspace
+ENCODING 160
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 1 0 0
+BITMAP
+00
+ENDCHAR
+STARTCHAR exclamdown
+ENCODING 161
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 7 0 -2
+BITMAP
+80
+00
+80
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR cent
+ENCODING 162
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 -1
+BITMAP
+40
+60
+80
+80
+60
+40
+ENDCHAR
+STARTCHAR sterling
+ENCODING 163
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 5 0 0
+BITMAP
+20
+40
+e0
+40
+f0
+ENDCHAR
+STARTCHAR currency
+ENCODING 164
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 4 0 0
+BITMAP
+90
+60
+60
+90
+ENDCHAR
+STARTCHAR yen
+ENCODING 165
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+88
+50
+f8
+20
+20
+ENDCHAR
+STARTCHAR brokenbar
+ENCODING 166
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 7 0 -1
+BITMAP
+80
+80
+80
+00
+80
+80
+80
+ENDCHAR
+STARTCHAR section
+ENCODING 167
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 -1
+BITMAP
+60
+80
+c0
+a0
+60
+20
+c0
+ENDCHAR
+STARTCHAR diaeresis
+ENCODING 168
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 1 1 6
+BITMAP
+90
+ENDCHAR
+STARTCHAR copyright
+ENCODING 169
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 6 7 1 -1
+BITMAP
+78
+84
+94
+a4
+94
+84
+78
+ENDCHAR
+STARTCHAR ordfeminine
+ENCODING 170
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 5 0 1
+BITMAP
+80
+40
+c0
+00
+c0
+ENDCHAR
+STARTCHAR guillemotleft
+ENCODING 171
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 4 0 0
+BITMAP
+50
+a0
+a0
+50
+ENDCHAR
+STARTCHAR notsign
+ENCODING 172
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 4 3 1 1
+BITMAP
+f0
+10
+10
+ENDCHAR
+STARTCHAR hyphen
+ENCODING 173
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 1 0 2
+BITMAP
+c0
+ENDCHAR
+STARTCHAR registered
+ENCODING 174
+SWIDTH 840 0
+DWIDTH 7 0
+BBX 6 7 1 -1
+BITMAP
+78
+b4
+ac
+b4
+ac
+84
+78
+ENDCHAR
+STARTCHAR macron
+ENCODING 175
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 1 0 6
+BITMAP
+e0
+ENDCHAR
+STARTCHAR degree
+ENCODING 176
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 3 0 2
+BITMAP
+40
+a0
+40
+ENDCHAR
+STARTCHAR plusminus
+ENCODING 177
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 3 5 1 0
+BITMAP
+40
+e0
+40
+00
+e0
+ENDCHAR
+STARTCHAR twosuperior
+ENCODING 178
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 4 0 2
+BITMAP
+80
+40
+80
+c0
+ENDCHAR
+STARTCHAR threesuperior
+ENCODING 179
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 5 0 1
+BITMAP
+80
+40
+80
+40
+80
+ENDCHAR
+STARTCHAR acute
+ENCODING 180
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 2 0 5
+BITMAP
+40
+80
+ENDCHAR
+STARTCHAR mu
+ENCODING 181
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 5 0 -1
+BITMAP
+a0
+a0
+a0
+d0
+80
+ENDCHAR
+STARTCHAR paragraph
+ENCODING 182
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f8
+d0
+d0
+50
+50
+50
+50
+ENDCHAR
+STARTCHAR periodcentered
+ENCODING 183
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 1 1 0 2
+BITMAP
+80
+ENDCHAR
+STARTCHAR cedilla
+ENCODING 184
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 2 0 -2
+BITMAP
+40
+80
+ENDCHAR
+STARTCHAR onesuperior
+ENCODING 185
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 4 0 2
+BITMAP
+c0
+40
+40
+e0
+ENDCHAR
+STARTCHAR masculine
+ENCODING 186
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 5 0 1
+BITMAP
+40
+a0
+40
+00
+e0
+ENDCHAR
+STARTCHAR guillemotright
+ENCODING 187
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 4 0 0
+BITMAP
+a0
+50
+50
+a0
+ENDCHAR
+STARTCHAR onequarter
+ENCODING 188
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 7 0 -1
+BITMAP
+44
+48
+50
+24
+4c
+9c
+04
+ENDCHAR
+STARTCHAR onehalf
+ENCODING 189
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 7 0 -1
+BITMAP
+44
+48
+50
+28
+44
+88
+0c
+ENDCHAR
+STARTCHAR threequarters
+ENCODING 190
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 7 0 -1
+BITMAP
+80
+44
+88
+54
+ac
+5c
+04
+ENDCHAR
+STARTCHAR questiondown
+ENCODING 191
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 6 0 -1
+BITMAP
+40
+00
+40
+80
+80
+40
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+40
+20
+00
+20
+20
+50
+70
+d8
+ENDCHAR
+STARTCHAR Aacute
+ENCODING 193
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+10
+20
+00
+20
+20
+50
+70
+d8
+ENDCHAR
+STARTCHAR Acircumflex
+ENCODING 194
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+20
+50
+00
+20
+20
+50
+70
+d8
+ENDCHAR
+STARTCHAR Atilde
+ENCODING 195
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+28
+50
+00
+20
+20
+50
+70
+d8
+ENDCHAR
+STARTCHAR Adiaeresis
+ENCODING 196
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 7 0 0
+BITMAP
+50
+00
+20
+20
+50
+70
+d8
+ENDCHAR
+STARTCHAR Aring
+ENCODING 197
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+20
+50
+20
+20
+20
+50
+70
+d8
+ENDCHAR
+STARTCHAR AE
+ENCODING 198
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 5 0 0
+BITMAP
+3c
+50
+7c
+50
+dc
+ENDCHAR
+STARTCHAR Ccedilla
+ENCODING 199
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 7 0 -2
+BITMAP
+70
+88
+80
+88
+70
+20
+40
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 0 0
+BITMAP
+40
+20
+00
+f0
+40
+60
+40
+f0
+ENDCHAR
+STARTCHAR Eacute
+ENCODING 201
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 0 0
+BITMAP
+10
+20
+00
+f0
+40
+60
+40
+f0
+ENDCHAR
+STARTCHAR Ecircumflex
+ENCODING 202
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 8 0 0
+BITMAP
+20
+50
+00
+f0
+40
+60
+40
+f0
+ENDCHAR
+STARTCHAR Ediaeresis
+ENCODING 203
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 7 0 0
+BITMAP
+50
+00
+f0
+40
+60
+40
+f0
+ENDCHAR
+STARTCHAR Igrave
+ENCODING 204
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 8 0 0
+BITMAP
+80
+40
+00
+e0
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR Iacute
+ENCODING 205
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 8 0 0
+BITMAP
+20
+40
+00
+e0
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR Icircumflex
+ENCODING 206
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 8 0 0
+BITMAP
+40
+a0
+00
+e0
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR Idiaeresis
+ENCODING 207
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 7 0 0
+BITMAP
+a0
+00
+e0
+40
+40
+40
+e0
+ENDCHAR
+STARTCHAR ETH
+ENCODING 208
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 5 0 0
+BITMAP
+f0
+48
+e8
+48
+f0
+ENDCHAR
+STARTCHAR Ntilde
+ENCODING 209
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 8 0 0
+BITMAP
+28
+50
+00
+cc
+68
+58
+48
+c8
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+40
+20
+00
+70
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Oacute
+ENCODING 211
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+10
+20
+00
+70
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Ocircumflex
+ENCODING 212
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+20
+50
+00
+70
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Otilde
+ENCODING 213
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+28
+50
+00
+70
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR Odiaeresis
+ENCODING 214
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 7 0 0
+BITMAP
+50
+00
+70
+88
+88
+88
+70
+ENDCHAR
+STARTCHAR multiply
+ENCODING 215
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 4 0 0
+BITMAP
+90
+60
+60
+90
+ENDCHAR
+STARTCHAR Ooblique
+ENCODING 216
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 7 0 -1
+BITMAP
+08
+70
+98
+a8
+c8
+70
+80
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 8 0 0
+BITMAP
+20
+10
+00
+cc
+48
+48
+48
+30
+ENDCHAR
+STARTCHAR Uacute
+ENCODING 218
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 8 0 0
+BITMAP
+08
+10
+00
+cc
+48
+48
+48
+30
+ENDCHAR
+STARTCHAR Ucircumflex
+ENCODING 219
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 8 0 0
+BITMAP
+10
+28
+00
+cc
+48
+48
+48
+30
+ENDCHAR
+STARTCHAR Udiaeresis
+ENCODING 220
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 6 7 0 0
+BITMAP
+28
+00
+cc
+48
+48
+48
+30
+ENDCHAR
+STARTCHAR Yacute
+ENCODING 221
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 8 0 0
+BITMAP
+10
+20
+00
+d8
+50
+50
+20
+20
+ENDCHAR
+STARTCHAR THORN
+ENCODING 222
+SWIDTH 600 0
+DWIDTH 5 0
+BBX 4 5 0 0
+BITMAP
+c0
+60
+50
+60
+c0
+ENDCHAR
+STARTCHAR ssharp
+ENCODING 223
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 6 0 -1
+BITMAP
+60
+90
+a0
+90
+a0
+80
+ENDCHAR
+STARTCHAR agrave
+ENCODING 224
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 7 0 0
+BITMAP
+80
+40
+00
+80
+40
+c0
+c0
+ENDCHAR
+STARTCHAR aacute
+ENCODING 225
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 2 7 0 0
+BITMAP
+40
+80
+00
+80
+40
+c0
+c0
+ENDCHAR
+STARTCHAR acircumflex
+ENCODING 226
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 7 0 0
+BITMAP
+40
+a0
+00
+80
+40
+c0
+c0
+ENDCHAR
+STARTCHAR atilde
+ENCODING 227
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 4 7 0 0
+BITMAP
+50
+a0
+00
+80
+40
+c0
+c0
+ENDCHAR
+STARTCHAR adiaeresis
+ENCODING 228
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 7 0 0
+BITMAP
+a0
+00
+00
+80
+40
+c0
+c0
+ENDCHAR
+STARTCHAR aring
+ENCODING 229
+SWIDTH 360 0
+DWIDTH 3 0
+BBX 3 7 0 0
+BITMAP
+40
+a0
+40
+80
+40
+c0
+c0
+ENDCHAR
+STARTCHAR ae
+ENCODING 230
+SWIDTH 720 0
+DWIDTH 6 0
+BBX 5 4 0 0
+BITMAP
+90
+78
+e0
+d0
+ENDCHAR
+STARTCHAR ccedilla
+ENCODING 231
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 2 5 1 -1
+BITMAP
+40
+80
+80
+40
+80
+ENDCHAR
+STARTCHAR egrave
+ENCODING 232
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+80
+40
+00
+40
+e0
+80
+40
+ENDCHAR
+STARTCHAR eacute
+ENCODING 233
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+20
+40
+00
+40
+e0
+80
+40
+ENDCHAR
+STARTCHAR ecircumflex
+ENCODING 234
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+40
+a0
+00
+40
+e0
+80
+40
+ENDCHAR
+STARTCHAR ediaeresis
+ENCODING 235
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+a0
+00
+00
+40
+e0
+80
+40
+ENDCHAR
+STARTCHAR igrave
+ENCODING 236
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 7 0 0
+BITMAP
+80
+40
+00
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR iacute
+ENCODING 237
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 2 7 0 0
+BITMAP
+40
+80
+00
+80
+80
+80
+80
+ENDCHAR
+STARTCHAR icircumflex
+ENCODING 238
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 7 -1 0
+BITMAP
+40
+a0
+00
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR idiaeresis
+ENCODING 239
+SWIDTH 240 0
+DWIDTH 2 0
+BBX 3 6 -1 0
+BITMAP
+a0
+00
+40
+40
+40
+40
+ENDCHAR
+STARTCHAR eth
+ENCODING 240
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 6 0 0
+BITMAP
+a0
+40
+a0
+50
+50
+20
+ENDCHAR
+STARTCHAR ntilde
+ENCODING 241
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 7 0 0
+BITMAP
+50
+a0
+00
+c0
+a0
+a0
+a0
+ENDCHAR
+STARTCHAR ograve
+ENCODING 242
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+80
+40
+00
+40
+a0
+a0
+40
+ENDCHAR
+STARTCHAR oacute
+ENCODING 243
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+20
+40
+00
+40
+a0
+a0
+40
+ENDCHAR
+STARTCHAR ocircumflex
+ENCODING 244
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+40
+a0
+00
+40
+a0
+a0
+40
+ENDCHAR
+STARTCHAR otilde
+ENCODING 245
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 7 0 0
+BITMAP
+50
+a0
+00
+40
+a0
+a0
+40
+ENDCHAR
+STARTCHAR odiaeresis
+ENCODING 246
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+a0
+00
+00
+40
+a0
+a0
+40
+ENDCHAR
+STARTCHAR division
+ENCODING 247
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 5 0 0
+BITMAP
+40
+00
+e0
+00
+40
+ENDCHAR
+STARTCHAR oslash
+ENCODING 248
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 -1
+BITMAP
+20
+60
+a0
+a0
+c0
+80
+ENDCHAR
+STARTCHAR ugrave
+ENCODING 249
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+80
+40
+00
+a0
+a0
+a0
+60
+ENDCHAR
+STARTCHAR uacute
+ENCODING 250
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+20
+40
+00
+a0
+a0
+a0
+60
+ENDCHAR
+STARTCHAR ucircumflex
+ENCODING 251
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 7 0 0
+BITMAP
+40
+a0
+00
+a0
+a0
+a0
+60
+ENDCHAR
+STARTCHAR udiaeresis
+ENCODING 252
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 6 0 0
+BITMAP
+a0
+00
+a0
+a0
+a0
+60
+ENDCHAR
+STARTCHAR yacute
+ENCODING 253
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 9 0 -2
+BITMAP
+20
+40
+00
+a0
+a0
+a0
+40
+40
+80
+ENDCHAR
+STARTCHAR thorn
+ENCODING 254
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 4 7 -1 -2
+BITMAP
+c0
+60
+50
+50
+60
+40
+c0
+ENDCHAR
+STARTCHAR ydiaeresis
+ENCODING 255
+SWIDTH 480 0
+DWIDTH 4 0
+BBX 3 8 0 -2
+BITMAP
+a0
+00
+a0
+a0
+a0
+40
+40
+80
+ENDCHAR
+ENDFONT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times8.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times8.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/rotfont/times8.h	(revision 22322)
@@ -0,0 +1,769 @@
+static unsigned char times8_0_bits[] = {
+0x00};
+static unsigned char times8_1_bits[] = {
+0x00};
+static unsigned char times8_2_bits[] = {
+0x00};
+static unsigned char times8_3_bits[] = {
+0x00};
+static unsigned char times8_4_bits[] = {
+0x00};
+static unsigned char times8_5_bits[] = {
+0x00};
+static unsigned char times8_6_bits[] = {
+0x00};
+static unsigned char times8_7_bits[] = {
+0x00};
+static unsigned char times8_8_bits[] = {
+0x00};
+static unsigned char times8_9_bits[] = {
+0x00};
+static unsigned char times8_10_bits[] = {
+0x00};
+static unsigned char times8_11_bits[] = {
+0x00};
+static unsigned char times8_12_bits[] = {
+0x00};
+static unsigned char times8_13_bits[] = {
+0x00};
+static unsigned char times8_14_bits[] = {
+0x00};
+static unsigned char times8_15_bits[] = {
+0x00};
+static unsigned char times8_16_bits[] = {
+0x00};
+static unsigned char times8_17_bits[] = {
+0x00};
+static unsigned char times8_18_bits[] = {
+0x00};
+static unsigned char times8_19_bits[] = {
+0x00};
+static unsigned char times8_20_bits[] = {
+0x00};
+static unsigned char times8_21_bits[] = {
+0x00};
+static unsigned char times8_22_bits[] = {
+0x00};
+static unsigned char times8_23_bits[] = {
+0x00};
+static unsigned char times8_24_bits[] = {
+0x00};
+static unsigned char times8_25_bits[] = {
+0x00};
+static unsigned char times8_26_bits[] = {
+0x00};
+static unsigned char times8_27_bits[] = {
+0x00};
+static unsigned char times8_28_bits[] = {
+0x00};
+static unsigned char times8_29_bits[] = {
+0x00};
+static unsigned char times8_30_bits[] = {
+0x00};
+static unsigned char times8_31_bits[] = {
+0x00};
+static unsigned char times8_32_bits[] = {
+0x00};
+static unsigned char times8_33_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x00, 0x01};
+static unsigned char times8_34_bits[] = {
+0x05, 0x05};
+static unsigned char times8_35_bits[] = {
+0x0a, 0x0f, 0x0a, 0x0f, 0x0a};
+static unsigned char times8_36_bits[] = {
+0x02, 0x06, 0x01, 0x06, 0x05, 0x03, 0x02};
+static unsigned char times8_37_bits[] = {
+0x13, 0x0b, 0x04, 0x1a, 0x19};
+static unsigned char times8_38_bits[] = {
+0x04, 0x0a, 0x06, 0x15, 0x09, 0x1e};
+static unsigned char times8_39_bits[] = {
+0x01, 0x01};
+static unsigned char times8_40_bits[] = {
+0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02};
+static unsigned char times8_41_bits[] = {
+0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01};
+static unsigned char times8_42_bits[] = {
+0x05, 0x02, 0x05};
+static unsigned char times8_43_bits[] = {
+0x04, 0x04, 0x0f, 0x04, 0x04};
+static unsigned char times8_44_bits[] = {
+0x02, 0x01};
+static unsigned char times8_45_bits[] = {
+0x0f};
+static unsigned char times8_46_bits[] = {
+0x01};
+static unsigned char times8_47_bits[] = {
+0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01};
+static unsigned char times8_48_bits[] = {
+0x02, 0x05, 0x05, 0x05, 0x02};
+static unsigned char times8_49_bits[] = {
+0x02, 0x03, 0x02, 0x02, 0x07};
+static unsigned char times8_50_bits[] = {
+0x03, 0x04, 0x02, 0x01, 0x07};
+static unsigned char times8_51_bits[] = {
+0x03, 0x04, 0x02, 0x04, 0x03};
+static unsigned char times8_52_bits[] = {
+0x04, 0x06, 0x05, 0x0f, 0x04};
+static unsigned char times8_53_bits[] = {
+0x07, 0x01, 0x06, 0x04, 0x03};
+static unsigned char times8_54_bits[] = {
+0x06, 0x01, 0x03, 0x05, 0x02};
+static unsigned char times8_55_bits[] = {
+0x07, 0x04, 0x02, 0x01, 0x01};
+static unsigned char times8_56_bits[] = {
+0x02, 0x05, 0x02, 0x05, 0x02};
+static unsigned char times8_57_bits[] = {
+0x02, 0x05, 0x06, 0x04, 0x03};
+static unsigned char times8_58_bits[] = {
+0x01, 0x00, 0x01};
+static unsigned char times8_59_bits[] = {
+0x02, 0x00, 0x02, 0x01};
+static unsigned char times8_60_bits[] = {
+0x04, 0x02, 0x01, 0x02, 0x04};
+static unsigned char times8_61_bits[] = {
+0x0f, 0x00, 0x0f};
+static unsigned char times8_62_bits[] = {
+0x01, 0x02, 0x04, 0x02, 0x01};
+static unsigned char times8_63_bits[] = {
+0x03, 0x04, 0x02, 0x00, 0x02};
+static unsigned char times8_64_bits[] = {
+0x1e, 0x21, 0x39, 0x25, 0x3d, 0x01, 0x1e};
+static unsigned char times8_65_bits[] = {
+0x04, 0x0a, 0x0e, 0x0a, 0x1b};
+static unsigned char times8_66_bits[] = {
+0x07, 0x0a, 0x06, 0x0a, 0x07};
+static unsigned char times8_67_bits[] = {
+0x0e, 0x11, 0x01, 0x11, 0x0e};
+static unsigned char times8_68_bits[] = {
+0x0f, 0x12, 0x12, 0x12, 0x0f};
+static unsigned char times8_69_bits[] = {
+0x0f, 0x02, 0x06, 0x02, 0x0f};
+static unsigned char times8_70_bits[] = {
+0x0f, 0x02, 0x0e, 0x02, 0x03};
+static unsigned char times8_71_bits[] = {
+0x0e, 0x01, 0x1d, 0x11, 0x0e};
+static unsigned char times8_72_bits[] = {
+0x33, 0x12, 0x1e, 0x12, 0x33};
+static unsigned char times8_73_bits[] = {
+0x07, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times8_74_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x01};
+static unsigned char times8_75_bits[] = {
+0x13, 0x0a, 0x06, 0x0a, 0x1b};
+static unsigned char times8_76_bits[] = {
+0x07, 0x02, 0x02, 0x0a, 0x0f};
+static unsigned char times8_77_bits[] = {
+0x77, 0x36, 0x2a, 0x2a, 0x63};
+static unsigned char times8_78_bits[] = {
+0x33, 0x16, 0x16, 0x1a, 0x13};
+static unsigned char times8_79_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char times8_80_bits[] = {
+0x0f, 0x12, 0x0e, 0x02, 0x03};
+static unsigned char times8_81_bits[] = {
+0x0e, 0x11, 0x11, 0x11, 0x0e, 0x04, 0x08};
+static unsigned char times8_82_bits[] = {
+0x07, 0x0a, 0x06, 0x0a, 0x1b};
+static unsigned char times8_83_bits[] = {
+0x0e, 0x01, 0x06, 0x08, 0x07};
+static unsigned char times8_84_bits[] = {
+0x1f, 0x15, 0x04, 0x04, 0x0e};
+static unsigned char times8_85_bits[] = {
+0x33, 0x12, 0x12, 0x12, 0x0c};
+static unsigned char times8_86_bits[] = {
+0x1b, 0x0a, 0x0a, 0x04, 0x04};
+static unsigned char times8_87_bits[] = {
+0xcb, 0x4a, 0x52, 0x5a, 0x34};
+static unsigned char times8_88_bits[] = {
+0x33, 0x0c, 0x0c, 0x12, 0x33};
+static unsigned char times8_89_bits[] = {
+0x1b, 0x0a, 0x04, 0x04, 0x0e};
+static unsigned char times8_90_bits[] = {
+0x0f, 0x05, 0x02, 0x09, 0x0f};
+static unsigned char times8_91_bits[] = {
+0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03};
+static unsigned char times8_92_bits[] = {
+0x01, 0x01, 0x02, 0x02, 0x02, 0x04, 0x04};
+static unsigned char times8_93_bits[] = {
+0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03};
+static unsigned char times8_94_bits[] = {
+0x02, 0x05};
+static unsigned char times8_95_bits[] = {
+0x0f};
+static unsigned char times8_96_bits[] = {
+0x01, 0x01};
+static unsigned char times8_97_bits[] = {
+0x01, 0x02, 0x03, 0x03};
+static unsigned char times8_98_bits[] = {
+0x01, 0x03, 0x05, 0x05, 0x03};
+static unsigned char times8_99_bits[] = {
+0x02, 0x01, 0x01, 0x02};
+static unsigned char times8_100_bits[] = {
+0x04, 0x06, 0x05, 0x05, 0x06};
+static unsigned char times8_101_bits[] = {
+0x02, 0x07, 0x01, 0x02};
+static unsigned char times8_102_bits[] = {
+0x04, 0x02, 0x07, 0x02, 0x02};
+static unsigned char times8_103_bits[] = {
+0x0e, 0x05, 0x02, 0x05, 0x02};
+static unsigned char times8_104_bits[] = {
+0x01, 0x03, 0x05, 0x05, 0x05};
+static unsigned char times8_105_bits[] = {
+0x01, 0x00, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times8_106_bits[] = {
+0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01};
+static unsigned char times8_107_bits[] = {
+0x01, 0x05, 0x03, 0x03, 0x05};
+static unsigned char times8_108_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times8_109_bits[] = {
+0x0b, 0x15, 0x15, 0x15};
+static unsigned char times8_110_bits[] = {
+0x03, 0x05, 0x05, 0x05};
+static unsigned char times8_111_bits[] = {
+0x02, 0x05, 0x05, 0x02};
+static unsigned char times8_112_bits[] = {
+0x03, 0x05, 0x05, 0x03, 0x01, 0x01};
+static unsigned char times8_113_bits[] = {
+0x06, 0x05, 0x05, 0x06, 0x04, 0x04};
+static unsigned char times8_114_bits[] = {
+0x05, 0x03, 0x01, 0x01};
+static unsigned char times8_115_bits[] = {
+0x03, 0x01, 0x02, 0x03};
+static unsigned char times8_116_bits[] = {
+0x01, 0x03, 0x01, 0x01, 0x02};
+static unsigned char times8_117_bits[] = {
+0x05, 0x05, 0x05, 0x06};
+static unsigned char times8_118_bits[] = {
+0x05, 0x05, 0x05, 0x02};
+static unsigned char times8_119_bits[] = {
+0x15, 0x15, 0x15, 0x0a};
+static unsigned char times8_120_bits[] = {
+0x09, 0x06, 0x06, 0x09};
+static unsigned char times8_121_bits[] = {
+0x05, 0x05, 0x05, 0x02, 0x02, 0x01};
+static unsigned char times8_122_bits[] = {
+0x07, 0x02, 0x01, 0x07};
+static unsigned char times8_123_bits[] = {
+0x04, 0x02, 0x02, 0x03, 0x02, 0x02, 0x04};
+static unsigned char times8_124_bits[] = {
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times8_125_bits[] = {
+0x01, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01};
+static unsigned char times8_126_bits[] = {
+0x16, 0x0d};
+static unsigned char times8_127_bits[] = {
+0x00};
+static unsigned char times8_128_bits[] = {
+0x00};
+static unsigned char times8_129_bits[] = {
+0x00};
+static unsigned char times8_130_bits[] = {
+0x00};
+static unsigned char times8_131_bits[] = {
+0x00};
+static unsigned char times8_132_bits[] = {
+0x00};
+static unsigned char times8_133_bits[] = {
+0x00};
+static unsigned char times8_134_bits[] = {
+0x00};
+static unsigned char times8_135_bits[] = {
+0x00};
+static unsigned char times8_136_bits[] = {
+0x00};
+static unsigned char times8_137_bits[] = {
+0x00};
+static unsigned char times8_138_bits[] = {
+0x00};
+static unsigned char times8_139_bits[] = {
+0x00};
+static unsigned char times8_140_bits[] = {
+0x00};
+static unsigned char times8_141_bits[] = {
+0x00};
+static unsigned char times8_142_bits[] = {
+0x00};
+static unsigned char times8_143_bits[] = {
+0x00};
+static unsigned char times8_144_bits[] = {
+0x00};
+static unsigned char times8_145_bits[] = {
+0x00};
+static unsigned char times8_146_bits[] = {
+0x00};
+static unsigned char times8_147_bits[] = {
+0x00};
+static unsigned char times8_148_bits[] = {
+0x00};
+static unsigned char times8_149_bits[] = {
+0x00};
+static unsigned char times8_150_bits[] = {
+0x00};
+static unsigned char times8_151_bits[] = {
+0x00};
+static unsigned char times8_152_bits[] = {
+0x00};
+static unsigned char times8_153_bits[] = {
+0x00};
+static unsigned char times8_154_bits[] = {
+0x00};
+static unsigned char times8_155_bits[] = {
+0x00};
+static unsigned char times8_156_bits[] = {
+0x00};
+static unsigned char times8_157_bits[] = {
+0x00};
+static unsigned char times8_158_bits[] = {
+0x00};
+static unsigned char times8_159_bits[] = {
+0x00};
+static unsigned char times8_160_bits[] = {
+0x00};
+static unsigned char times8_161_bits[] = {
+0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times8_162_bits[] = {
+0x02, 0x06, 0x01, 0x01, 0x06, 0x02};
+static unsigned char times8_163_bits[] = {
+0x04, 0x02, 0x07, 0x02, 0x0f};
+static unsigned char times8_164_bits[] = {
+0x09, 0x06, 0x06, 0x09};
+static unsigned char times8_165_bits[] = {
+0x11, 0x0a, 0x1f, 0x04, 0x04};
+static unsigned char times8_166_bits[] = {
+0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01};
+static unsigned char times8_167_bits[] = {
+0x06, 0x01, 0x03, 0x05, 0x06, 0x04, 0x03};
+static unsigned char times8_168_bits[] = {
+0x09};
+static unsigned char times8_169_bits[] = {
+0x1e, 0x21, 0x29, 0x25, 0x29, 0x21, 0x1e};
+static unsigned char times8_170_bits[] = {
+0x01, 0x02, 0x03, 0x00, 0x03};
+static unsigned char times8_171_bits[] = {
+0x0a, 0x05, 0x05, 0x0a};
+static unsigned char times8_172_bits[] = {
+0x0f, 0x08, 0x08};
+static unsigned char times8_173_bits[] = {
+0x03};
+static unsigned char times8_174_bits[] = {
+0x1e, 0x2d, 0x35, 0x2d, 0x35, 0x21, 0x1e};
+static unsigned char times8_175_bits[] = {
+0x07};
+static unsigned char times8_176_bits[] = {
+0x02, 0x05, 0x02};
+static unsigned char times8_177_bits[] = {
+0x02, 0x07, 0x02, 0x00, 0x07};
+static unsigned char times8_178_bits[] = {
+0x01, 0x02, 0x01, 0x03};
+static unsigned char times8_179_bits[] = {
+0x01, 0x02, 0x01, 0x02, 0x01};
+static unsigned char times8_180_bits[] = {
+0x02, 0x01};
+static unsigned char times8_181_bits[] = {
+0x05, 0x05, 0x05, 0x0b, 0x01};
+static unsigned char times8_182_bits[] = {
+0x1f, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a};
+static unsigned char times8_183_bits[] = {
+0x01};
+static unsigned char times8_184_bits[] = {
+0x02, 0x01};
+static unsigned char times8_185_bits[] = {
+0x03, 0x02, 0x02, 0x07};
+static unsigned char times8_186_bits[] = {
+0x02, 0x05, 0x02, 0x00, 0x07};
+static unsigned char times8_187_bits[] = {
+0x05, 0x0a, 0x0a, 0x05};
+static unsigned char times8_188_bits[] = {
+0x22, 0x12, 0x0a, 0x24, 0x32, 0x39, 0x20};
+static unsigned char times8_189_bits[] = {
+0x22, 0x12, 0x0a, 0x14, 0x22, 0x11, 0x30};
+static unsigned char times8_190_bits[] = {
+0x01, 0x22, 0x11, 0x2a, 0x35, 0x3a, 0x20};
+static unsigned char times8_191_bits[] = {
+0x02, 0x00, 0x02, 0x01, 0x01, 0x02};
+static unsigned char times8_192_bits[] = {
+0x02, 0x04, 0x00, 0x04, 0x04, 0x0a, 0x0e, 0x1b};
+static unsigned char times8_193_bits[] = {
+0x08, 0x04, 0x00, 0x04, 0x04, 0x0a, 0x0e, 0x1b};
+static unsigned char times8_194_bits[] = {
+0x04, 0x0a, 0x00, 0x04, 0x04, 0x0a, 0x0e, 0x1b};
+static unsigned char times8_195_bits[] = {
+0x14, 0x0a, 0x00, 0x04, 0x04, 0x0a, 0x0e, 0x1b};
+static unsigned char times8_196_bits[] = {
+0x0a, 0x00, 0x04, 0x04, 0x0a, 0x0e, 0x1b};
+static unsigned char times8_197_bits[] = {
+0x04, 0x0a, 0x04, 0x04, 0x04, 0x0a, 0x0e, 0x1b};
+static unsigned char times8_198_bits[] = {
+0x3c, 0x0a, 0x3e, 0x0a, 0x3b};
+static unsigned char times8_199_bits[] = {
+0x0e, 0x11, 0x01, 0x11, 0x0e, 0x04, 0x02};
+static unsigned char times8_200_bits[] = {
+0x02, 0x04, 0x00, 0x0f, 0x02, 0x06, 0x02, 0x0f};
+static unsigned char times8_201_bits[] = {
+0x08, 0x04, 0x00, 0x0f, 0x02, 0x06, 0x02, 0x0f};
+static unsigned char times8_202_bits[] = {
+0x04, 0x0a, 0x00, 0x0f, 0x02, 0x06, 0x02, 0x0f};
+static unsigned char times8_203_bits[] = {
+0x0a, 0x00, 0x0f, 0x02, 0x06, 0x02, 0x0f};
+static unsigned char times8_204_bits[] = {
+0x01, 0x02, 0x00, 0x07, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times8_205_bits[] = {
+0x04, 0x02, 0x00, 0x07, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times8_206_bits[] = {
+0x02, 0x05, 0x00, 0x07, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times8_207_bits[] = {
+0x05, 0x00, 0x07, 0x02, 0x02, 0x02, 0x07};
+static unsigned char times8_208_bits[] = {
+0x0f, 0x12, 0x17, 0x12, 0x0f};
+static unsigned char times8_209_bits[] = {
+0x14, 0x0a, 0x00, 0x33, 0x16, 0x1a, 0x12, 0x13};
+static unsigned char times8_210_bits[] = {
+0x02, 0x04, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char times8_211_bits[] = {
+0x08, 0x04, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char times8_212_bits[] = {
+0x04, 0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char times8_213_bits[] = {
+0x14, 0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char times8_214_bits[] = {
+0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e};
+static unsigned char times8_215_bits[] = {
+0x09, 0x06, 0x06, 0x09};
+static unsigned char times8_216_bits[] = {
+0x10, 0x0e, 0x19, 0x15, 0x13, 0x0e, 0x01};
+static unsigned char times8_217_bits[] = {
+0x04, 0x08, 0x00, 0x33, 0x12, 0x12, 0x12, 0x0c};
+static unsigned char times8_218_bits[] = {
+0x10, 0x08, 0x00, 0x33, 0x12, 0x12, 0x12, 0x0c};
+static unsigned char times8_219_bits[] = {
+0x08, 0x14, 0x00, 0x33, 0x12, 0x12, 0x12, 0x0c};
+static unsigned char times8_220_bits[] = {
+0x14, 0x00, 0x33, 0x12, 0x12, 0x12, 0x0c};
+static unsigned char times8_221_bits[] = {
+0x08, 0x04, 0x00, 0x1b, 0x0a, 0x0a, 0x04, 0x04};
+static unsigned char times8_222_bits[] = {
+0x03, 0x06, 0x0a, 0x06, 0x03};
+static unsigned char times8_223_bits[] = {
+0x06, 0x09, 0x05, 0x09, 0x05, 0x01};
+static unsigned char times8_224_bits[] = {
+0x01, 0x02, 0x00, 0x01, 0x02, 0x03, 0x03};
+static unsigned char times8_225_bits[] = {
+0x02, 0x01, 0x00, 0x01, 0x02, 0x03, 0x03};
+static unsigned char times8_226_bits[] = {
+0x02, 0x05, 0x00, 0x01, 0x02, 0x03, 0x03};
+static unsigned char times8_227_bits[] = {
+0x0a, 0x05, 0x00, 0x01, 0x02, 0x03, 0x03};
+static unsigned char times8_228_bits[] = {
+0x05, 0x00, 0x00, 0x01, 0x02, 0x03, 0x03};
+static unsigned char times8_229_bits[] = {
+0x02, 0x05, 0x02, 0x01, 0x02, 0x03, 0x03};
+static unsigned char times8_230_bits[] = {
+0x09, 0x1e, 0x07, 0x0b};
+static unsigned char times8_231_bits[] = {
+0x02, 0x01, 0x01, 0x02, 0x01};
+static unsigned char times8_232_bits[] = {
+0x01, 0x02, 0x00, 0x02, 0x07, 0x01, 0x02};
+static unsigned char times8_233_bits[] = {
+0x04, 0x02, 0x00, 0x02, 0x07, 0x01, 0x02};
+static unsigned char times8_234_bits[] = {
+0x02, 0x05, 0x00, 0x02, 0x07, 0x01, 0x02};
+static unsigned char times8_235_bits[] = {
+0x05, 0x00, 0x00, 0x02, 0x07, 0x01, 0x02};
+static unsigned char times8_236_bits[] = {
+0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times8_237_bits[] = {
+0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01};
+static unsigned char times8_238_bits[] = {
+0x02, 0x05, 0x00, 0x02, 0x02, 0x02, 0x02};
+static unsigned char times8_239_bits[] = {
+0x05, 0x00, 0x02, 0x02, 0x02, 0x02};
+static unsigned char times8_240_bits[] = {
+0x05, 0x02, 0x05, 0x0a, 0x0a, 0x04};
+static unsigned char times8_241_bits[] = {
+0x0a, 0x05, 0x00, 0x03, 0x05, 0x05, 0x05};
+static unsigned char times8_242_bits[] = {
+0x01, 0x02, 0x00, 0x02, 0x05, 0x05, 0x02};
+static unsigned char times8_243_bits[] = {
+0x04, 0x02, 0x00, 0x02, 0x05, 0x05, 0x02};
+static unsigned char times8_244_bits[] = {
+0x02, 0x05, 0x00, 0x02, 0x05, 0x05, 0x02};
+static unsigned char times8_245_bits[] = {
+0x0a, 0x05, 0x00, 0x02, 0x05, 0x05, 0x02};
+static unsigned char times8_246_bits[] = {
+0x05, 0x00, 0x00, 0x02, 0x05, 0x05, 0x02};
+static unsigned char times8_247_bits[] = {
+0x02, 0x00, 0x07, 0x00, 0x02};
+static unsigned char times8_248_bits[] = {
+0x04, 0x06, 0x05, 0x05, 0x03, 0x01};
+static unsigned char times8_249_bits[] = {
+0x01, 0x02, 0x00, 0x05, 0x05, 0x05, 0x06};
+static unsigned char times8_250_bits[] = {
+0x04, 0x02, 0x00, 0x05, 0x05, 0x05, 0x06};
+static unsigned char times8_251_bits[] = {
+0x02, 0x05, 0x00, 0x05, 0x05, 0x05, 0x06};
+static unsigned char times8_252_bits[] = {
+0x05, 0x00, 0x05, 0x05, 0x05, 0x06};
+static unsigned char times8_253_bits[] = {
+0x04, 0x02, 0x00, 0x05, 0x05, 0x05, 0x02, 0x02, 0x01};
+static unsigned char times8_254_bits[] = {
+0x03, 0x06, 0x0a, 0x0a, 0x06, 0x02, 0x03};
+static unsigned char times8_255_bits[] = {
+0x05, 0x00, 0x05, 0x05, 0x05, 0x02, 0x02, 0x01};
+static RotFont times8font[] = {
+{5, 1, 1, times8_0_bits},
+{5, 1, 1, times8_1_bits},
+{5, 1, 1, times8_2_bits},
+{5, 1, 1, times8_3_bits},
+{5, 1, 1, times8_4_bits},
+{5, 1, 1, times8_5_bits},
+{5, 1, 1, times8_6_bits},
+{5, 1, 1, times8_7_bits},
+{5, 1, 1, times8_8_bits},
+{5, 1, 1, times8_9_bits},
+{5, 1, 1, times8_10_bits},
+{5, 1, 1, times8_11_bits},
+{5, 1, 1, times8_12_bits},
+{5, 1, 1, times8_13_bits},
+{5, 1, 1, times8_14_bits},
+{5, 1, 1, times8_15_bits},
+{5, 1, 1, times8_16_bits},
+{5, 1, 1, times8_17_bits},
+{5, 1, 1, times8_18_bits},
+{5, 1, 1, times8_19_bits},
+{5, 1, 1, times8_20_bits},
+{5, 1, 1, times8_21_bits},
+{5, 1, 1, times8_22_bits},
+{5, 1, 1, times8_23_bits},
+{5, 1, 1, times8_24_bits},
+{5, 1, 1, times8_25_bits},
+{5, 1, 1, times8_26_bits},
+{5, 1, 1, times8_27_bits},
+{5, 1, 1, times8_28_bits},
+{5, 1, 1, times8_29_bits},
+{5, 1, 1, times8_30_bits},
+{5, 1, 1, times8_31_bits},
+{4, 1, 1, times8_32_bits},
+{1, 6, 6, times8_33_bits},
+{3, 2, 5, times8_34_bits},
+{4, 5, 5, times8_35_bits},
+{3, 7, 6, times8_36_bits},
+{5, 5, 5, times8_37_bits},
+{5, 6, 6, times8_38_bits},
+{1, 2, 6, times8_39_bits},
+{2, 7, 6, times8_40_bits},
+{2, 7, 6, times8_41_bits},
+{3, 3, 5, times8_42_bits},
+{4, 5, 5, times8_43_bits},
+{2, 2, 1, times8_44_bits},
+{4, 1, 3, times8_45_bits},
+{1, 1, 1, times8_46_bits},
+{3, 7, 6, times8_47_bits},
+{3, 5, 5, times8_48_bits},
+{3, 5, 5, times8_49_bits},
+{3, 5, 5, times8_50_bits},
+{3, 5, 5, times8_51_bits},
+{4, 5, 5, times8_52_bits},
+{3, 5, 5, times8_53_bits},
+{3, 5, 5, times8_54_bits},
+{3, 5, 5, times8_55_bits},
+{3, 5, 5, times8_56_bits},
+{3, 5, 5, times8_57_bits},
+{1, 3, 3, times8_58_bits},
+{2, 4, 3, times8_59_bits},
+{3, 5, 5, times8_60_bits},
+{4, 3, 4, times8_61_bits},
+{3, 5, 5, times8_62_bits},
+{3, 5, 5, times8_63_bits},
+{6, 7, 6, times8_64_bits},
+{5, 5, 5, times8_65_bits},
+{4, 5, 5, times8_66_bits},
+{5, 5, 5, times8_67_bits},
+{5, 5, 5, times8_68_bits},
+{4, 5, 5, times8_69_bits},
+{4, 5, 5, times8_70_bits},
+{5, 5, 5, times8_71_bits},
+{6, 5, 5, times8_72_bits},
+{3, 5, 5, times8_73_bits},
+{2, 5, 5, times8_74_bits},
+{5, 5, 5, times8_75_bits},
+{4, 5, 5, times8_76_bits},
+{7, 5, 5, times8_77_bits},
+{6, 5, 5, times8_78_bits},
+{5, 5, 5, times8_79_bits},
+{5, 5, 5, times8_80_bits},
+{5, 7, 5, times8_81_bits},
+{5, 5, 5, times8_82_bits},
+{4, 5, 5, times8_83_bits},
+{5, 5, 5, times8_84_bits},
+{6, 5, 5, times8_85_bits},
+{5, 5, 5, times8_86_bits},
+{8, 5, 5, times8_87_bits},
+{6, 5, 5, times8_88_bits},
+{5, 5, 5, times8_89_bits},
+{4, 5, 5, times8_90_bits},
+{2, 7, 6, times8_91_bits},
+{3, 7, 6, times8_92_bits},
+{2, 7, 6, times8_93_bits},
+{3, 2, 6, times8_94_bits},
+{4, 1, 0, times8_95_bits},
+{1, 2, 6, times8_96_bits},
+{2, 4, 4, times8_97_bits},
+{3, 5, 5, times8_98_bits},
+{2, 4, 4, times8_99_bits},
+{3, 5, 5, times8_100_bits},
+{3, 4, 4, times8_101_bits},
+{3, 5, 5, times8_102_bits},
+{4, 5, 4, times8_103_bits},
+{3, 5, 5, times8_104_bits},
+{1, 6, 6, times8_105_bits},
+{2, 8, 6, times8_106_bits},
+{3, 5, 5, times8_107_bits},
+{1, 5, 5, times8_108_bits},
+{5, 4, 4, times8_109_bits},
+{3, 4, 4, times8_110_bits},
+{3, 4, 4, times8_111_bits},
+{3, 6, 4, times8_112_bits},
+{3, 6, 4, times8_113_bits},
+{3, 4, 4, times8_114_bits},
+{2, 4, 4, times8_115_bits},
+{2, 5, 5, times8_116_bits},
+{3, 4, 4, times8_117_bits},
+{3, 4, 4, times8_118_bits},
+{5, 4, 4, times8_119_bits},
+{4, 4, 4, times8_120_bits},
+{3, 6, 4, times8_121_bits},
+{3, 4, 4, times8_122_bits},
+{3, 7, 6, times8_123_bits},
+{1, 7, 6, times8_124_bits},
+{3, 7, 6, times8_125_bits},
+{5, 2, 4, times8_126_bits},
+{5, 1, 1, times8_127_bits},
+{5, 1, 1, times8_128_bits},
+{5, 1, 1, times8_129_bits},
+{5, 1, 1, times8_130_bits},
+{5, 1, 1, times8_131_bits},
+{5, 1, 1, times8_132_bits},
+{5, 1, 1, times8_133_bits},
+{5, 1, 1, times8_134_bits},
+{5, 1, 1, times8_135_bits},
+{5, 1, 1, times8_136_bits},
+{5, 1, 1, times8_137_bits},
+{5, 1, 1, times8_138_bits},
+{5, 1, 1, times8_139_bits},
+{5, 1, 1, times8_140_bits},
+{5, 1, 1, times8_141_bits},
+{5, 1, 1, times8_142_bits},
+{5, 1, 1, times8_143_bits},
+{5, 1, 1, times8_144_bits},
+{5, 1, 1, times8_145_bits},
+{5, 1, 1, times8_146_bits},
+{5, 1, 1, times8_147_bits},
+{5, 1, 1, times8_148_bits},
+{5, 1, 1, times8_149_bits},
+{5, 1, 1, times8_150_bits},
+{5, 1, 1, times8_151_bits},
+{5, 1, 1, times8_152_bits},
+{5, 1, 1, times8_153_bits},
+{5, 1, 1, times8_154_bits},
+{5, 1, 1, times8_155_bits},
+{5, 1, 1, times8_156_bits},
+{5, 1, 1, times8_157_bits},
+{5, 1, 1, times8_158_bits},
+{5, 1, 1, times8_159_bits},
+{1, 1, 1, times8_160_bits},
+{1, 7, 5, times8_161_bits},
+{3, 6, 5, times8_162_bits},
+{4, 5, 5, times8_163_bits},
+{4, 4, 4, times8_164_bits},
+{5, 5, 5, times8_165_bits},
+{1, 7, 6, times8_166_bits},
+{3, 7, 6, times8_167_bits},
+{4, 1, 7, times8_168_bits},
+{6, 7, 6, times8_169_bits},
+{2, 5, 6, times8_170_bits},
+{4, 4, 4, times8_171_bits},
+{4, 3, 4, times8_172_bits},
+{2, 1, 3, times8_173_bits},
+{6, 7, 6, times8_174_bits},
+{3, 1, 7, times8_175_bits},
+{3, 3, 5, times8_176_bits},
+{3, 5, 5, times8_177_bits},
+{2, 4, 6, times8_178_bits},
+{2, 5, 6, times8_179_bits},
+{2, 2, 7, times8_180_bits},
+{4, 5, 4, times8_181_bits},
+{5, 7, 6, times8_182_bits},
+{1, 1, 3, times8_183_bits},
+{2, 2, 0, times8_184_bits},
+{3, 4, 6, times8_185_bits},
+{3, 5, 6, times8_186_bits},
+{4, 4, 4, times8_187_bits},
+{6, 7, 6, times8_188_bits},
+{6, 7, 6, times8_189_bits},
+{6, 7, 6, times8_190_bits},
+{2, 6, 5, times8_191_bits},
+{5, 8, 8, times8_192_bits},
+{5, 8, 8, times8_193_bits},
+{5, 8, 8, times8_194_bits},
+{5, 8, 8, times8_195_bits},
+{5, 7, 7, times8_196_bits},
+{5, 8, 8, times8_197_bits},
+{6, 5, 5, times8_198_bits},
+{5, 7, 5, times8_199_bits},
+{4, 8, 8, times8_200_bits},
+{4, 8, 8, times8_201_bits},
+{4, 8, 8, times8_202_bits},
+{4, 7, 7, times8_203_bits},
+{3, 8, 8, times8_204_bits},
+{3, 8, 8, times8_205_bits},
+{3, 8, 8, times8_206_bits},
+{3, 7, 7, times8_207_bits},
+{5, 5, 5, times8_208_bits},
+{6, 8, 8, times8_209_bits},
+{5, 8, 8, times8_210_bits},
+{5, 8, 8, times8_211_bits},
+{5, 8, 8, times8_212_bits},
+{5, 8, 8, times8_213_bits},
+{5, 7, 7, times8_214_bits},
+{4, 4, 4, times8_215_bits},
+{5, 7, 6, times8_216_bits},
+{6, 8, 8, times8_217_bits},
+{6, 8, 8, times8_218_bits},
+{6, 8, 8, times8_219_bits},
+{6, 7, 7, times8_220_bits},
+{5, 8, 8, times8_221_bits},
+{4, 5, 5, times8_222_bits},
+{4, 6, 5, times8_223_bits},
+{2, 7, 7, times8_224_bits},
+{2, 7, 7, times8_225_bits},
+{3, 7, 7, times8_226_bits},
+{4, 7, 7, times8_227_bits},
+{3, 7, 7, times8_228_bits},
+{3, 7, 7, times8_229_bits},
+{5, 4, 4, times8_230_bits},
+{2, 5, 4, times8_231_bits},
+{3, 7, 7, times8_232_bits},
+{3, 7, 7, times8_233_bits},
+{3, 7, 7, times8_234_bits},
+{3, 7, 7, times8_235_bits},
+{2, 7, 7, times8_236_bits},
+{2, 7, 7, times8_237_bits},
+{3, 7, 7, times8_238_bits},
+{3, 6, 6, times8_239_bits},
+{4, 6, 6, times8_240_bits},
+{4, 7, 7, times8_241_bits},
+{3, 7, 7, times8_242_bits},
+{3, 7, 7, times8_243_bits},
+{3, 7, 7, times8_244_bits},
+{4, 7, 7, times8_245_bits},
+{3, 7, 7, times8_246_bits},
+{3, 5, 5, times8_247_bits},
+{3, 6, 5, times8_248_bits},
+{3, 7, 7, times8_249_bits},
+{3, 7, 7, times8_250_bits},
+{3, 7, 7, times8_251_bits},
+{3, 6, 6, times8_252_bits},
+{3, 9, 7, times8_253_bits},
+{4, 7, 5, times8_254_bits},
+{3, 8, 6, times8_255_bits}};
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/DrawRotString.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/DrawRotString.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/DrawRotString.c	(revision 22322)
@@ -0,0 +1,242 @@
+# include <kapa_internal.h>
+
+# define NROTCHARS 256
+# define XPROC(x,y) (scale*(cs*((x) - x0) - sn*((y) - y0)) + X0)
+# define YPROC(x,y) (scale*(cs*((y) - y0) + sn*((x) - x0)) + Y0)
+# define NEARINT(x) ((x < 0) ? ((int)(x - 0.5)) : ((int)(x + 0.5)))
+  
+static Display *RotDisplay;
+static Window   RotWindow;
+static GC       RotGC;
+static unsigned long RotForeground;
+static unsigned long RotBackground;
+
+int DrawRotTextInit (Display *display, Window window, GC gc, unsigned long fore, unsigned long back) {
+
+    RotDisplay = display;
+    RotWindow  = window;
+    RotGC      = gc;
+    RotForeground = fore;
+    RotBackground = back;
+
+    return (TRUE);
+}
+
+int DrawRotText (int x, int y, char *string, int pos, double angle) {
+
+  unsigned char *bitmap;
+  char *currentname, basename[64]; 
+  int i, dy, dx, N, X, Y, code;
+  int dX, Xoff, dY, Yoff, YoffBase;
+  int currentsize, basesize;
+  double cs, sn, currentscale;
+  RotFont *currentfont;
+
+  currentname = GetRotFont (&currentsize);
+  currentfont = GetRotFontData (&currentscale);
+  strcpy (basename, currentname);
+  basesize = currentsize;
+
+  /* strip leading WHITESPACE */
+  stripwhite (string);
+  if (*string == 0) return (FALSE);
+  
+  /* compute string length */
+  cs = cos(angle*RAD_DEG);
+  sn = sin(angle*RAD_DEG);
+  dX = RotStrlen (string);
+  dY = currentfont[65].ascent;
+
+  /* apply appropriate offset */
+  Xoff = Yoff = 0;
+  switch (pos) {
+  case 0: Xoff =     -dX; Yoff = dY; break;
+  case 1: Xoff = -0.5*dX; Yoff = dY; break;
+  case 2: Xoff =       0; Yoff = dY; break;
+  case 3: Xoff =     -dX; Yoff = 0.5*dY; break;
+  case 4: Xoff = -0.5*dX; Yoff = 0.5*dY; break;
+  case 5: Xoff =       0; Yoff = 0.5*dY; break;
+  case 6: Xoff =     -dX; Yoff = 0; break;
+  case 7: Xoff = -0.5*dX; Yoff = 0; break;
+  case 8: Xoff =       0; Yoff = 0; break;
+  }
+
+  code = FALSE;
+
+  YoffBase = Yoff;
+  /* draw characters one-by-one */
+  for (i = 0; i < strlen(string); i++) {
+    N = (int)(string[i]);
+    if ((N < 0) || (N >= NROTCHARS)) continue;
+
+    /* check for special characters */
+    if (!code) {
+      if (N == 94) {
+	SetRotFont (currentname, (int)(0.8*currentsize));
+	currentfont = GetRotFontData (&currentscale);
+	Yoff -= 0.5*currentscale*dY;
+	continue;
+      }
+      if (N == 95) { 
+	SetRotFont (currentname, (int)(0.8*currentsize));
+	currentfont = GetRotFontData (&currentscale);
+	Yoff += 0.5*currentscale*dY;
+	continue;
+      }
+      if (N == 124) {
+	SetRotFont (currentname, basesize);
+	currentfont = GetRotFontData (&currentscale);
+	Yoff = YoffBase;
+	continue;
+      }
+      if (N == 92) {
+	code = TRUE;
+	continue;
+      } 
+      if (N == 38) {
+	if (string[i+1] == 'h') {
+	  SetRotFont ("helvetica", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	}
+	if (string[i+1] == 't') {
+	  SetRotFont ("times", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	}
+	if (string[i+1] == 'c') {
+	  SetRotFont ("courier", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	}
+	if (string[i+1] == 's') {
+	  SetRotFont ("symbol", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	}
+	i++;
+	continue;
+      }
+    }
+    code = FALSE;
+
+    bitmap = currentfont[N].bits;
+    dx = currentfont[N].dx;
+    dy = currentfont[N].dy;
+    X = x + (int)(Xoff*cs - Yoff*sn) + (int)(currentscale*currentfont[N].ascent*sn);
+    Y = y + (int)(Xoff*sn + Yoff*cs) - (int)(currentscale*currentfont[N].ascent*cs);
+    DrawRotBitmap (X, Y, dx, dy, bitmap, TRUE, angle, currentscale);
+    Xoff += 1 + (int)(currentscale*dx + 0.5);
+  }
+  SetRotFont (basename, basesize);
+  return (TRUE);
+}
+
+int DrawRotBitmap (int x, int y, int dx, int dy, unsigned char *bitmap, int mode, double angle, double scale) {
+
+  int ii, jj, byte_line, byte, bit, flag;
+  unsigned long int fore, back;
+  double i, j, cs, sn, rscale, tmp;
+  int X, Y, X0, X1, X2, Y0, Y1, Y2, x0, y0;
+
+  if (mode) {
+    fore = RotForeground;
+    back = RotBackground;
+  } else {
+    fore = RotBackground;
+    back = RotForeground;
+  } 
+    
+  byte_line = (int) ((dx + 7) / 8);
+
+  cs = cos(angle*RAD_DEG);  sn = sin(angle*RAD_DEG);
+  rscale = 1.0 / scale;
+
+  X0 = 0;
+  Y0 = 0;
+  x0 = 0;
+  y0 = 0;
+
+  X2 = X1 = XPROC (0,0);
+  Y2 = Y1 = YPROC (0,0);
+
+  X = XPROC (dx,0);
+  Y = YPROC (dx,0);
+# ifdef DRAWBOXES
+  XDrawLine (RotDisplay, RotWindow, RotGC, x+X, y+Y, x+X1, y+Y1);
+  Xt = X;
+  Yt = Y;
+# endif
+  X1 = MIN (X, X1);
+  X2 = MAX (X, X2);
+  Y1 = MIN (Y, Y1);
+  Y2 = MAX (Y, Y2);
+
+  X = XPROC (dx,dy);
+  Y = YPROC (dx,dy);
+# ifdef DRAWBOXES
+  XDrawLine (RotDisplay, RotWindow, RotGC, x+X, y+Y, x+Xt, y+Yt);
+  Xt = X;
+  Yt = Y;
+# endif
+  Y1 = MIN (Y, Y1);
+  Y2 = MAX (Y, Y2);
+  X1 = MIN (X, X1);
+  X2 = MAX (X, X2);
+
+  X = XPROC (0,dy);
+  Y = YPROC (0,dy);
+# ifdef DRAWBOXES
+  XDrawLine (RotDisplay, RotWindow, RotGC, x+X, y+Y, x+Xt, y+Yt);
+  Xt = X;
+  Yt = Y;
+# endif
+  Y1 = MIN (Y, Y1);
+  Y2 = MAX (Y, Y2);
+  X1 = MIN (X, X1);
+  X2 = MAX (X, X2);
+
+  XSetForeground (RotDisplay, RotGC, fore);
+  if (scale > 1) {
+    for (i = X1; i <= X2; i+=1) {
+      for (j = Y1; j <= Y2; j+=1) {
+	tmp = rscale*(cs*(i - X0) + sn*(j - Y0)) + x0;  ii = NEARINT (tmp);
+	tmp = rscale*(cs*(j - Y0) - sn*(i - X0)) + y0;  jj = NEARINT (tmp);
+	/* fprintf (stderr, "%d %d  %d %d\n", i, j, ii, jj);  */
+	if ((ii < 0) || (ii >= dx) || (jj < 0) || (jj >= dy)) continue;
+	byte = byte_line * jj + (ii / 8);
+	bit = ii % 8;
+	flag = 0x01 & (bitmap[byte] >> bit);
+	if (flag) XDrawPoint (RotDisplay, RotWindow, RotGC, x + i, y + j);
+      }
+    }
+  } else {
+    for (i = X1; i <= X2; i+=scale) {
+      for (j = Y1; j <= Y2; j+=scale) {
+	tmp = rscale*(cs*(i - X0) + sn*(j - Y0)) + x0;  ii = NEARINT (tmp);
+	tmp = rscale*(cs*(j - Y0) - sn*(i - X0)) + y0;  jj = NEARINT (tmp);
+	/* fprintf (stderr, "%d %d  %d %d\n", i, j, ii, jj);  */
+	if ((ii < 0) || (ii >= dx) || (jj < 0) || (jj >= dy)) continue;
+	byte = byte_line * jj + (ii / 8);
+	bit = ii % 8;
+	flag = 0x01 & (bitmap[byte] >> bit);
+	/* fprintf (stderr, "%2d %2d  %3d %3d  %1d  %f  %f\n", i, j, ii, jj, flag,
+	   rscale*(cs*(i - X0) + sn*(j - Y0)) + x0, rscale*(cs*(j - Y0) - sn*(i - X0)) + y0);  */
+	if (flag) XDrawPoint (RotDisplay, RotWindow, RotGC, x + i, y + j);
+      }
+    }
+# if (0)
+    for (i = 0; i < dx; i++) {
+      for (j = 0; j < dy; j++) {
+	tmp = scale*(cs*(i - x0) - sn*(j - y0)) + X0; ii = NEARINT (tmp);
+	tmp = scale*(cs*(j - y0) + sn*(i - x0)) + Y0; jj = NEARINT (tmp);
+	/* if ((ii < 0) || (ii >= dx) || (jj < 0) || (jj >= dy)) continue; */
+	byte = byte_line * j + (i / 8);
+	bit = i % 8;
+	flag = 0x01 & (bitmap[byte] >> bit);
+	fprintf (stderr, "%2d %2d  %3d %3d  %1d  %f  %f\n", i, j, ii, jj, flag, 
+		 scale*(cs*(i - x0) - sn*(j - y0)) + X0, scale*(cs*(j - y0) + sn*(i - x0)) + Y0); 
+	if (flag) XDrawPoint (RotDisplay, RotWindow, RotGC, x + ii, y + jj);
+      }
+    }
+# endif
+  }
+  XSetForeground (RotDisplay, RotGC, RotForeground);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/IOfuncs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/IOfuncs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/IOfuncs.c	(revision 22322)
@@ -0,0 +1,176 @@
+# include <kapa_internal.h>
+# define DEBUG 0
+
+/** these function expect to operate with a BLOCKing socket **/
+
+/* why is this not defined in stdarg.h for linux/x64? */
+int vsscanf(const char *str, const char *format, va_list ap);
+
+int KiiSendData (int device, char *data, int Nbytes) {
+
+  KiiSendCommand (device, 16, "LEN: %11d", Nbytes);
+  write (device, data, Nbytes);
+  return (TRUE);
+} 
+
+char *KiiRecvData (int device) {
+
+  int status, Nbytes;
+  char *data, buffer[20];
+
+  /* read 16 bytes: LEN (length) */
+  status = read (device, buffer, 16);
+  buffer[16] = 0;
+
+  /* find the message length, allocate space */
+  sscanf (buffer, "%*s %d", &Nbytes);
+  ALLOCATE (data, char, Nbytes + 1);
+  read (device, data, Nbytes);
+  data[Nbytes] = 0;
+
+  return (data);
+} 
+
+/* send a message of arbitrary size, sending the size first */
+int KiiSendMessage (int device, char *format, ...) {
+
+  int Nbyte, status;
+  char tmp;
+  va_list argp;  
+
+  va_start (argp, format);
+  Nbyte = vsnprintf (&tmp, 0, format, argp);
+  va_end (argp);
+
+  if (!Nbyte) return (FALSE);
+
+  /* the message may contain up to 99,999,999,999 bytes (100MB) */
+  va_start (argp, format);
+  KiiSendCommand (device, 16, "LEN: %11d", Nbyte);
+  status = KiiSendCommandV (device, Nbyte, format, argp);
+  va_end (argp);
+
+  return (status);
+}
+
+/* scan a message of arbitrary size, accepting the size first */
+int KiiScanMessage (int device, char *format, ...) {
+
+  int Nbytes, status;
+  char buffer[20], *message;
+  va_list argp;  
+
+  /* read 16 bytes: LEN (length) */
+  status = read (device, buffer, 16);
+  buffer[16] = 0;
+  if (status != 16) {
+      fprintf (stderr, "dropped message length\n");
+  }
+  if (DEBUG) fprintf (stderr, "recv buffer: %s\n", buffer);
+
+  /* find the message length, allocate space */
+  sscanf (buffer, "%*s %d", &Nbytes);
+  ALLOCATE (message, char, Nbytes + 1);
+
+  /* read Nbytes from the device */
+  status = read (device, message, Nbytes);
+  if (status != Nbytes) {
+      fprintf (stderr, "Kii/Kapa comm error\n");
+  }
+  message[status] = 0;
+  /* make the string easy to parse */
+
+  if (DEBUG) fprintf (stderr, "recv: %s\n", message);
+
+  /* scan the incoming message */
+  va_start (argp, format);
+  Nbytes = vsscanf (message, format, argp);
+  va_end (argp);
+
+  free (message);
+
+  return (status);
+}
+
+/* send a command of fixed size */
+int KiiSendCommand (int device, int length, char *format, ...) {
+
+  int status;
+  va_list argp;  
+
+  va_start (argp, format);
+  status = KiiSendCommandV (device, length, format, argp);
+  va_end (argp);
+
+  return (status);
+}
+  
+int KiiSendCommandV (int device, int length, char *format, va_list argp) {
+
+  char *string;
+
+  /* string is sent WITHOUT ending NULL char */
+  /* allocate and zero length + 1 extra byte */
+  ALLOCATE (string, char, length + 1);
+  memset (string, 0, length + 1);
+  vsnprintf (string, length + 1, format, argp);
+
+  write (device, string, length);
+
+  if (DEBUG) fprintf (stderr, "send: %s\n", string);
+
+  free (string);
+  return (TRUE);
+}
+
+/* scan a command of fixed size */
+int KiiScanCommand (int device, int length, char *format, ...) {
+
+  int status;
+  char *message;
+  va_list argp;  
+
+  ALLOCATE (message, char, length + 1);
+
+  /* read Nbytes from the device */
+  status = read (device, message, length);
+  if (DEBUG) fprintf (stderr, "recv message: %s\n", message);
+
+  if (status != length) {
+      fprintf (stderr, "Kii/Kapa comm error\n");
+      return (0);
+  }
+  message[status] = 0; // make the string easy to parse
+
+  /* scan the incoming message */
+  va_start (argp, format);
+  vsscanf (message, format, argp);
+  va_end (argp);
+
+  free (message);
+
+  return (1);
+}
+
+int KiiWaitAnswer (int device, char *expect) {
+
+  int Nbytes;
+  char *answer;
+
+  Nbytes = strlen (expect);
+  ALLOCATE (answer, char, Nbytes + 1);
+
+  KiiScanCommand (device, Nbytes, "%s", answer);
+  if (strcmp (answer, expect)) {
+      fprintf (stderr, "unexpected response %s, expected %s\n", answer, expect); 
+      REALLOCATE (answer, char, 128);
+      Nbytes = read (device, answer, 127);
+      answer[Nbytes] = 0;
+      fprintf (stderr, "extra data in buffer: %d bytes\n", Nbytes);
+      fprintf (stderr, "garbage: %s\n", answer);
+      free (answer);
+      return (FALSE);
+  }
+  free (answer);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KapaColors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KapaColors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KapaColors.c	(revision 22322)
@@ -0,0 +1,103 @@
+# include <kapa_internal.h>
+
+# define N_KAPA_COLORS 23
+
+static char KAPA_COLORS[N_KAPA_COLORS][3][16] = {
+{"black",    	 "black",    	 "0.00 0.00 0.00"}, 
+{"white",    	 "white",    	 "1.00 1.00 1.00"}, 
+{"red",      	 "red",      	 "1.00 0.00 0.00"}, 
+{"pink",     	 "pink",     	 "1.00 0.75 0.80"}, 
+{"orange",   	 "orange",   	 "1.00 0.65 0.00"}, 
+{"yellow",   	 "yellow",   	 "1.00 1.00 0.00"}, 
+{"wheat",    	 "wheat",    	 "0.96 0.87 0.70"}, 
+{"gold",     	 "gold",     	 "1.00 0.84 0.00"}, 
+{"green",    	 "green",    	 "0.00 1.00 0.00"}, 
+{"darkgreen",	 "darkgreen",	 "0.00 0.40 0.00"}, 
+{"blue",     	 "blue",     	 "0.00 0.00 1.00"}, 
+{"skyblue",  	 "skyblue",  	 "0.53 0.81 0.92"}, 
+{"indigo",   	 "mediumpurple", "0.57 0.44 0.86"}, 
+{"violet",   	 "darkviolet", 	 "0.58 0.00 0.88"},
+{"grey10",   	 "grey10",   	 "0.10 0.10 0.10"},
+{"grey20",   	 "grey20",   	 "0.20 0.20 0.20"},
+{"grey30",   	 "grey30",   	 "0.30 0.30 0.30"},
+{"grey40",   	 "grey40",   	 "0.40 0.40 0.40"},
+{"grey50",   	 "grey50",   	 "0.50 0.50 0.50"},
+{"grey60",   	 "grey60",   	 "0.60 0.60 0.60"},
+{"grey70",   	 "grey70",   	 "0.70 0.70 0.70"},
+{"grey80",   	 "grey80",   	 "0.80 0.80 0.80"},
+{"grey90",   	 "grey90",   	 "0.90 0.90 0.90"}};
+
+int KapaColorByName (char *name) {
+
+  int i;
+  
+  for (i = 0; i < N_KAPA_COLORS; i++) {
+    if (!strcmp (name, KAPA_COLORS[i][0])) {
+      return (i);
+    }	
+  }
+  fprintf (stderr, "color may be one of:\n");
+  for (i = 0; i < N_KAPA_COLORS; i++) {
+    fprintf (stderr, "  %s\n", KAPA_COLORS[i][0]);
+  }
+  return (-1);
+}	
+
+int KapaColormapSize () {
+  return (N_KAPA_COLORS);
+}
+
+char *KapaColorRGBString (int N) {
+  return (KAPA_COLORS[N][2]);
+}
+
+char *KapaColorName (int N) {
+  return (KAPA_COLORS[N][0]);
+}
+
+png_color *KapaPNGPalette (int *Npalette) {
+
+  int i;
+  float red, green, blue;
+  png_color *palette;
+
+  ALLOCATE (palette, png_color, N_KAPA_COLORS);
+
+  /* define the palette */
+  for (i = 0; i < N_KAPA_COLORS; i++) {
+    sscanf (KAPA_COLORS[i][2], "%f %f %f", &red, &green, &blue);
+    palette[i].red = (0xff * red);
+    palette[i].green = (0xff * green);
+    palette[i].blue = (0xff * blue);
+  }
+  *Npalette = N_KAPA_COLORS;
+  return (palette);
+}
+
+unsigned long *KapaX11colors (Display *display, Colormap colormap, unsigned long default_color, int *Ncolors) {
+
+  int i;
+  int status;
+  unsigned long *colors;
+  XColor rgbcolor, hardwarecolor;
+
+  *Ncolors = N_KAPA_COLORS;
+  ALLOCATE (colors, unsigned long, N_KAPA_COLORS);
+
+  for (i = 0; i < N_KAPA_COLORS; i++) {
+    colors[i] = default_color;
+    status = XLookupColor (display, colormap, KAPA_COLORS[i][1], &rgbcolor, &hardwarecolor);
+    if (!status) continue;
+    status = XAllocColor (display, colormap, &hardwarecolor);
+    if (!status) continue;
+    colors[i] = hardwarecolor.pixel;
+  }
+  return (colors);
+}
+
+/*
+
+kapa objects, bDraw, and png all use the same pallete sequence, defined by
+KAPA_COLORS above.  the color correspond to a color sequence.  
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KapaOpen.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KapaOpen.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KapaOpen.c	(revision 22322)
@@ -0,0 +1,316 @@
+# include "kapa_internal.h"
+
+# define MY_PORT 2500
+# define MY_PORT_MAX 2520
+# define MY_WAIT 100000
+# define DEBUG 0
+
+static int Nvalid = 0;
+static int *VALID = NULL;
+
+int KapaServerInit (KapaSockAddress *Address) {
+
+  int status, InitSocket, length;
+
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = MY_PORT;
+  Address[0].sin_addr.s_addr = INADDR_ANY; // use this line to bind any address / port?
+
+retry_server:
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (2);
+  }
+
+  if (DEBUG) fprintf (stderr, "init sock: %d, len: %d, port %d\n", InitSocket, length, Address[0].sin_port);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+    if (errno == EADDRINUSE) {
+	close (InitSocket);
+	Address[0].sin_port ++;
+	if (Address[0].sin_port > MY_PORT_MAX) exit (2);
+	goto retry_server;
+    }
+    perror ("bind: ");
+    exit (2);
+  }
+
+  /* repeated starts of the server are limited by xinetd or something:
+     requires 60sec timeout of the selected socket */
+
+  if (DEBUG) fprintf (stderr, "bound to port: %d\n", Address[0].sin_port);
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (2);
+  }
+  return (InitSocket);
+}
+
+int KapaServerWait (int InitSocket, KapaSockAddress *Address) {
+
+  int i, status, BindSocket;
+  KapaSockAddress Address_in;
+  socklen_t length;
+  u_int32_t addr;
+  fd_set rfds;
+  struct timeval wait;
+
+  Address_in = Address[0];
+
+  length = sizeof(Address_in);
+
+  wait.tv_sec = 0;
+  wait.tv_usec = MY_WAIT;
+
+  /* do I need to clear rfds on each pass? */
+  FD_SET (InitSocket, &rfds);
+  status = select (InitSocket + 1, &rfds, NULL, NULL, &wait);
+  if (status == -1) {
+    perror ("select");
+    abort ();
+  }
+  if (!status) return (-1);
+
+  if (DEBUG) fprintf (stderr, "init sock: %d, len: %d\n", InitSocket, length);
+  BindSocket = accept (InitSocket, (struct sockaddr *) &Address_in, &length);
+  if (DEBUG) fprintf (stderr, "bind sock: %d\n", BindSocket);
+  if (BindSocket == -1) {
+    perror ("accept: ");
+    exit (2);
+  }
+
+  addr = Address_in.sin_addr.s_addr;
+  if (DEBUG) {
+    fprintf (stderr, "incoming connection from: ");
+    fprintf (stderr, " %u", (0xff & (addr >>  0)));
+    fprintf (stderr, ".%u", (0xff & (addr >>  8)));
+    fprintf (stderr, ".%u", (0xff & (addr >> 16)));
+    fprintf (stderr, ".%u", (0xff & (addr >> 24)));
+    fprintf (stderr, "\n");
+  }
+
+  if (Nvalid == 0) goto accepted;
+
+  for (i = 0; i < Nvalid; i++) {
+    /* valid IP addresses may be machines (120.90.121.142) or 
+       class C networks (120.90.121.0) */
+       
+    /* for machine, address must match */
+    if ((0xff & (VALID[i] >> 24)) != 0) {
+      if (addr == VALID[i]) goto accepted;
+    }
+
+    /* for network, lower three bytes of address must match */
+    if ((0xff & (VALID[i] >> 24)) == 0) {
+      if ((0x00ffffff & addr) == VALID[i]) goto accepted;
+    }
+  }
+
+  if (DEBUG) fprintf (stderr, "connection rejected\n");
+  close (BindSocket);
+  return (-1);
+
+accepted:
+  if (DEBUG) fprintf (stderr, "connection accepted\n");
+  return (BindSocket);
+}
+
+/* load valid ip list */
+int KapaDefineValidIP (char *ipstring) {
+
+  int ip1, ip2, ip3, ip4, test, status;
+  char string[80];
+
+  if (Nvalid == 0) {
+    Nvalid = 1;
+    ALLOCATE (VALID, int, Nvalid);
+  } else {
+    Nvalid ++;
+    REALLOCATE (VALID, int, Nvalid);
+  }
+
+  status = sscanf (ipstring, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4);
+  test = TRUE;
+  test &= (status == 4);
+  test &= ((ip1 > 0) && (ip1 < 256)); 
+  test &= ((ip2 > 0) && (ip2 < 256)); 
+  test &= ((ip3 > 0) && (ip3 < 256)); 
+  test &= ((ip4 >=0) && (ip4 < 256)); 
+  if (!test) {
+    fprintf (stderr, "invalid IP address %s\n", string);
+    exit (2);
+  }
+  VALID[Nvalid-1] = ip1 | (ip2 << 8) | (ip3 << 16) | (ip4 << 24);
+  return (TRUE);
+}
+
+/* connect to a running server on the specified host */
+int KapaClientSocket (char *hostname) {
+
+  int i, status, InitSocket, length;
+  KapaSockAddress Address;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  bzero (hostip, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+
+  if (DEBUG) {
+    fprintf (stderr, "trying %s (%s:%d)...", host[0].h_name, hostip, MY_PORT);
+  }
+
+  Address.sin_family = AF_INET;
+  Address.sin_port   = MY_PORT;
+
+retry_client:
+  status = inet_aton (hostip, &Address.sin_addr);
+  if (!status) {
+    fprintf (stderr, "invalid address\n");
+    exit (2);
+  }
+
+  length = sizeof(Address);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (2);
+  }
+
+  status = connect (InitSocket, (struct sockaddr *) &Address, length);
+  if (status == -1) {
+    if (DEBUG) fprintf (stderr, "error connecting: %d\n", errno);
+    if (errno == ECONNREFUSED) {
+      close (InitSocket);
+      Address.sin_port ++;
+      if (Address.sin_port > MY_PORT_MAX) return (-1);
+      goto retry_client;
+    }
+    perror ("connect: ");
+    exit (2);
+  }
+
+  if (DEBUG) fprintf (stderr, "connected on port: %d\n", Address.sin_port);
+  if (DEBUG) fprintf (stderr, "connected\n");
+
+  // the client uses a BLOCKing socket by default
+  fcntl (InitSocket, F_SETFL, !O_NONBLOCK); 
+  return (InitSocket);
+}
+
+int KapaOpen (char *kapa_exec, char *kapa_name) {
+
+  // kapa_exec may be kapa://host, in which case we attempt to connect to an 
+  // already running kapa, or the program path, in which case we are supposed
+  // to launch it locally, then connect to it.
+
+  int sock, Ntry;
+  char line[128];
+
+  if (!strncmp (kapa_exec, "kapa://", 7)) {
+    sock = KapaClientSocket (&kapa_exec[7]);
+    return (sock);
+  }
+
+  if (kapa_name == NULL) {
+    sprintf (line, "%s &", kapa_exec);
+  } else {
+    sprintf (line, "%s -name '%s' &", kapa_exec, kapa_name);
+  }
+  system (line);
+
+# define NTRY 500
+  Ntry = 0;
+  while (Ntry < NTRY) {
+    sock = KapaClientSocket ("localhost");
+    if (sock != -1) break;
+    // if (errno != EAGAIN) break;
+    if (errno != ECONNREFUSED) break;
+    usleep (10000);
+    Ntry ++;
+  }
+
+  if (sock < 0) return (-1);
+  return (sock);
+}
+
+/* start socketed connection (UNIX Socket) */
+int KapaOpenNamedSocket (char *kapa_exec, char *name) {
+
+  int InitSocket, status;
+  struct sockaddr_un Address;
+  socklen_t AddressLength;
+  char temp[128], socket_name[64];
+  int Ntry, fd;
+
+  sprintf (socket_name, "/tmp/Kapa.XXXXXX");
+  if ((fd = mkstemp (socket_name)) == -1) {
+    fprintf (stderr, "error starting kapa\n");
+    return (-1);
+  }
+  close (fd);
+  unlink (socket_name);
+
+  strcpy (Address.sun_path, socket_name); 
+  Address.sun_family = AF_UNIX; 
+  InitSocket = socket (AF_UNIX, SOCK_STREAM, 0); 
+  status = bind (InitSocket, (struct sockaddr *) &Address, sizeof (Address));
+  status = listen (InitSocket, 1);
+  
+  if (name == NULL) {
+    sprintf (temp, "%s -socket %s &", kapa_exec, socket_name);
+  } else {
+    sprintf (temp, "%s -socket %s -name %s &", kapa_exec, socket_name, name);
+  }
+  system (temp);
+
+  AddressLength =  sizeof (Address);
+  fcntl (InitSocket, F_SETFL, O_NONBLOCK); 
+
+# define NTRY 500
+  Ntry = 0;
+  while (Ntry < NTRY) {
+    fd = accept (InitSocket, (struct sockaddr *)&Address, &AddressLength);
+    if (fd != -1) break;
+    if (errno != EAGAIN) break;
+    usleep (10000);
+    Ntry ++;
+  }
+
+  if (fd < 0) return (-1);
+
+  // the client uses a BLOCKing socket by default
+  fcntl (fd, F_SETFL, !O_NONBLOCK); 
+  return (fd);
+}
+
+// wait for the initiating process to connect to the socket
+int KapaWaitNamedSocket (char *sockpath) {
+
+  int sock, status;
+  struct sockaddr_un Address;
+
+  strcpy (Address.sun_path, sockpath);
+  Address.sun_family = AF_UNIX;
+  sock = socket (AF_UNIX, SOCK_STREAM, 0);
+  status = connect (sock, (struct sockaddr *) &Address, sizeof (Address));
+  if (status < 0) {
+    fprintf (stderr, "unsuccessful connection: %d\n", status);
+    exit (0);
+  }
+
+  // the server uses an unblocked socket
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+  unlink (sockpath);
+  return (sock);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KapaWindow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KapaWindow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KapaWindow.c	(revision 22322)
@@ -0,0 +1,334 @@
+# include <kapa_internal.h>
+
+int KiiCenter (int fd, double x, double y, int zoom) {
+
+  KiiSendCommand (fd, 4, "CENT");
+  KiiSendMessage (fd, "%8.3f %8.3f %8d ", x, y, zoom);
+  KiiWaitAnswer (fd, "DONE");
+  
+  return (TRUE);
+}
+
+int KiiResize (int fd, int Nx, int Ny) {
+
+  KiiSendCommand (fd, 4, "RSIZ");
+  KiiSendMessage (fd, "%d %d", Nx, Ny); 
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaBox (int fd, Graphdata *graphdata) {
+
+  KiiSendCommand (fd, 4, "DBOX");
+  KiiSendMessage (fd, "%12.6g %12.6g %12.6g %12.6g", 
+		    graphdata[0].xmin, graphdata[0].xmax, graphdata[0].ymin, graphdata[0].ymax);
+
+  KiiSendMessage (fd, "%lf %d", graphdata[0].lweight, graphdata[0].color);
+
+  KiiSendMessage (fd, "%s %s %s", graphdata[0].axis, graphdata[0].labels, graphdata[0].ticks);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaClearCurrentPlot (int fd) {
+  
+  KiiSendCommand (fd, 4, "ERSC");
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaClearPlots (int fd) {
+  
+  KiiSendCommand (fd, 4, "ERSP");
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaClearSections (int fd) {
+  
+  KiiSendCommand (fd, 4, "ERSS");
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaClearImage (int fd) {
+  
+  KiiSendCommand (fd, 4, "ERSI");
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaSetToolbox (int fd, int location) {
+  
+  KiiSendCommand (fd, 4, "TOOL");
+  KiiSendMessage (fd, "%d", location); 
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaInitGraph (Graphdata *graphdata) {
+
+  graphdata[0].xmin = graphdata[0].ymin = 0.0;
+  graphdata[0].xmax = graphdata[0].ymax = 1.0;
+  graphdata[0].style = graphdata[0].ptype = 0;
+  graphdata[0].ltype = graphdata[0].color = 0;
+  graphdata[0].etype = graphdata[0].ebar = 0;
+  graphdata[0].lweight = graphdata[0].size = 1.0;
+    
+  graphdata[0].coords.pc1_1 = graphdata[0].coords.pc2_2 = 1.0;
+  graphdata[0].coords.pc1_2 = graphdata[0].coords.pc2_1 = 0.0;
+  strcpy (graphdata[0].coords.ctype, "RA---LIN");
+  graphdata[0].coords.crval1 = 0.0;
+  graphdata[0].coords.crval2 = 0.0;
+  graphdata[0].coords.crpix1 = 0.0;
+  graphdata[0].coords.crpix2 = 0.0;
+  graphdata[0].coords.cdelt1 = graphdata[0].coords.cdelt2 = 1.0;
+  graphdata[0].flipeast = TRUE;
+  graphdata[0].flipnorth = FALSE;
+  strcpy (graphdata[0].axis, "2222");
+  strcpy (graphdata[0].ticks, "2222");
+  strcpy (graphdata[0].labels, "2222");
+
+  return (TRUE);
+}
+
+int KapaPrepPlot (int fd, int Npts, Graphdata *data) {
+
+  /* tell kapa to look for the incoming image */
+  KiiSendCommand (fd, 4, "PLOT"); 
+  
+  /* send kapa the plot details */
+  KiiSendMessage (fd, "%8d %8d %d %d %d %d %d %f %f", 
+		  Npts, data[0].style, 
+		  data[0].ptype, data[0].ltype, 
+		  data[0].etype, data[0].ebar, data[0].color, 
+		  data[0].lweight, data[0].size);
+  KiiSendMessage (fd, "%g %g %g %g", 
+		  data[0].xmin, data[0].xmax, 
+		  data[0].ymin, data[0].ymax);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaSetGraphData (int fd, Graphdata *data) {
+
+  /* tell kapa to look for the incoming image */
+  KiiSendCommand (fd, 4, "SSTY"); 
+  
+  /* send kapa the plot details */
+  KiiSendMessage (fd, "%8d %d %d %d %d %d %f %f", 
+		  data[0].style, 
+		  data[0].ptype, data[0].ltype, 
+		  data[0].etype, data[0].ebar, data[0].color, 
+		  data[0].lweight, data[0].size);
+
+  KiiSendMessage (fd, "%g %g %g %g", 
+		  data[0].xmin, data[0].xmax, 
+		  data[0].ymin, data[0].ymax);
+
+  KiiSendMessage (fd, "%g %g %g %g", 
+		  data[0].coords.pc1_1, data[0].coords.pc2_2,
+		  data[0].coords.pc1_2, data[0].coords.pc2_1);
+
+  KiiSendMessage (fd, "%d %d %s", 
+		  data[0].flipeast, data[0].flipnorth,
+		  data[0].coords.ctype);
+
+  KiiSendMessage (fd, "%g %g %g %g %g %g", 
+		  data[0].coords.crval1,
+		  data[0].coords.crval2,
+		  data[0].coords.crpix1,
+		  data[0].coords.crpix2,
+		  data[0].coords.cdelt1,
+		  data[0].coords.cdelt2);
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaGetGraphData (int fd, Graphdata *data) {
+
+  /* tell kapa to look for the incoming image */
+  KiiSendCommand (fd, 4, "GSTY"); 
+  
+  /* send kapa the plot details */
+  KiiScanMessage (fd, "%d %d %d %d %d %d %lf %lf", 
+		  &data[0].style, 
+		  &data[0].ptype, &data[0].ltype, 
+		  &data[0].etype, &data[0].ebar, &data[0].color, 
+		  &data[0].lweight, &data[0].size);
+
+  KiiScanMessage (fd, "%lf %lf %lf %lf", 
+		  &data[0].xmin, &data[0].xmax, 
+		  &data[0].ymin, &data[0].ymax);
+
+  KiiScanMessage (fd, "%f %f %f %f", 
+		  &data[0].coords.pc1_1, &data[0].coords.pc2_2,
+		  &data[0].coords.pc1_2, &data[0].coords.pc2_1);
+
+  KiiScanMessage (fd, "%d %d %s", 
+		  &data[0].flipeast, &data[0].flipnorth,
+		  data[0].coords.ctype);
+
+  KiiScanMessage (fd, "%lf %lf %f %f %f %f", 
+		  &data[0].coords.crval1,
+		  &data[0].coords.crval2,
+		  &data[0].coords.crpix1,
+		  &data[0].coords.crpix2,
+		  &data[0].coords.cdelt1,
+		  &data[0].coords.cdelt2);
+
+  // XXX at some point, we need to add polynomials and 2-level mosaic
+  // astrometry here.
+
+  data[0].coords.Npolyterms = 0;
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaSetImageData (int fd, KapaImageData *data) {
+
+  /* tell kapa to look for the incoming image */
+  KiiSendCommand (fd, 4, "SIMD"); 
+  
+  /* send kapa the plot details */
+  KiiSendMessage (fd, "%g %g %s %s", 
+		  data[0].zero, data[0].range, data[0].name, data[0].file);
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaGetImageData (int fd, KapaImageData *data) {
+
+  /* tell kapa to look for the incoming image */
+  KiiSendCommand (fd, 4, "GIMD"); 
+  
+  KiiScanMessage (fd, "%lf %lf %s %s", 
+		  &data[0].zero, &data[0].range, data[0].name, data[0].file);
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaPlotVector (int fd, int Npts, float *values, char *type) {
+
+  int Nbytes;
+  int swap;
+
+# ifdef BYTE_SWAP
+  swap = 1;
+# else
+  swap = 0;
+# endif
+
+  Nbytes = Npts * sizeof (float);
+
+  if (!strcmp(type, "x")) goto valid;
+  if (!strcmp(type, "y")) goto valid;
+  if (!strcmp(type, "z")) goto valid;
+  if (!strcmp(type, "dym")) goto valid;
+  if (!strcmp(type, "dyp")) goto valid;
+  if (!strcmp(type, "dxm")) goto valid;
+  if (!strcmp(type, "dxp")) goto valid;
+  return (FALSE);
+
+valid:
+  KiiSendCommand (fd, 4, "PLOB"); 
+  KiiSendMessage (fd, "%s %d %d %d", type, Npts, Nbytes, swap); 
+
+  write (fd, values, Nbytes);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaSetFont (int fd, char *name, int size) {
+
+  KiiSendCommand (fd, 4, "FONT");
+  KiiSendCommand (fd, 16, "%s", name);
+  KiiSendCommand (fd, 16, "%d", size);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaSendLabel (int fd, char *string, int mode) {
+
+  KiiSendCommand (fd, 4, "LABL");
+  KiiSendMessage (fd, "%6d", mode);
+  KiiSendData (fd, string, strlen(string));
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaSendTextline (int fd, char *string, float x, float y, float angle) {
+  
+  KiiSendCommand (fd, 4, "PTXT");
+  KiiSendMessage (fd, "%f %f %f", x, y, angle);
+  KiiSendData (fd, string, strlen(string));
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaSetLimits (int fd, Graphdata *data) {
+
+  KiiSendCommand (fd, 4, "SLIM");
+  KiiSendMessage (fd, "%g %g %g %g ", data[0].xmin, data[0].xmax, data[0].ymin, data[0].ymax);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+// for now, this just gets the dimensions
+int KapaGetLimits (int fd, float *dx, float *dy) {
+
+  KiiSendCommand (fd, 4, "GLIM"); 
+  KiiScanMessage (fd, "%f %f", dx, dy); 
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaSetSection (int fd, KapaSection *section) {
+
+  KiiSendCommand (fd, 4, "DSEC");
+  KiiSendMessage (fd, "%s %6.3f %6.3f %6.3f %6.3f", 
+		  section[0].name, 
+		  section[0].x,
+		  section[0].y,
+		  section[0].dx,
+		  section[0].dy);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaMoveSection (int fd, char *name, char *direction) {
+
+  if (!strcasecmp(direction, "up")) goto valid;
+  if (!strcasecmp(direction, "down")) goto valid;
+  if (!strcasecmp(direction, "top")) goto valid;
+  if (!strcasecmp(direction, "bottom")) goto valid;
+  
+  fprintf (stderr, "unexpected direction %s\n", direction); 
+  return (FALSE);
+
+valid:
+  KiiSendCommand (fd, 4, "MSEC");
+  KiiSendMessage (fd, "%s %s", name, direction);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaSelectSection (int fd, char *name) {
+
+  KiiSendCommand (fd, 4, "SSEC");
+  KiiSendMessage (fd, "%s", name);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaGetSection (int fd, char *name) {
+
+  KiiSendCommand (fd, 4, "LSEC");
+  KiiSendMessage (fd, "%s", name);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiConvert.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiConvert.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiConvert.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include <kapa_internal.h>
+
+int KiiJPEG (int fd, const char *filename) {
+
+  KiiSendCommand (fd, 4, "JPEG");
+  KiiSendMessage (fd, "%s", filename);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaPNG (int fd, const char *filename) {
+
+  KiiSendCommand (fd, 4, "PNGF");
+  KiiSendMessage (fd, "%s", filename);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaPPM (int fd, const char *filename) {
+
+  KiiSendCommand (fd, 4, "PPMF");
+  KiiSendMessage (fd, "%s", filename);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KiiPS (int fd, const char *filename, int scaleMode, int pageMode, char *pagename) {
+
+  KiiSendCommand (fd, 4, "PSIT");
+  KiiSendMessage (fd, "%s %s %d %d", filename, pagename, scaleMode, pageMode);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiCursor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiCursor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiCursor.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include <kapa_internal.h>
+
+int KiiCursorOn (int fd) {
+
+  KiiSendCommand (fd, 4, "CURS");
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KiiCursorOff (int fd) {
+
+  KiiSendCommand (fd, 4, "NCUR");
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KiiCursorRead (int fd, double *x, double *y, double *z, double *r, double *d, char *key) {
+
+  KiiScanMessage (fd, "%s %lf %lf %lf %lf %lf", key, x, y, z, r, d);
+  if (ispunct(key[0])) strcpy (key, "_");
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiOpen.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiOpen.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiOpen.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include <kapa_internal.h>
+
+/** start socketed connection */
+int KiiOpen (char *kii_exec, char *name) {
+
+  int InitSocket, status;
+  struct sockaddr_un Address;
+  socklen_t AddressLength;
+  char temp[128], socket_name[64];
+  int Ntry, fd;
+
+  sprintf (socket_name, "/tmp/Kii.XXXXXX");
+  if ((fd = mkstemp (socket_name)) == -1) {
+    fprintf (stderr, "error starting kii\n");
+    return (-1);
+  }
+  close (fd);
+  unlink (socket_name);
+
+  strcpy (Address.sun_path, socket_name); 
+  Address.sun_family = AF_UNIX; 
+  InitSocket = socket (AF_UNIX, SOCK_STREAM, 0); 
+  status = bind (InitSocket, (struct sockaddr *) &Address, sizeof (Address));
+  status = listen (InitSocket, 1);
+  
+  if (name == NULL) {
+    sprintf (temp, "%s %s &", kii_exec, socket_name);
+  } else {
+    sprintf (temp, "%s %s -name %s &", kii_exec, socket_name, name);
+  }
+  system (temp);
+
+  AddressLength =  sizeof (Address);
+  fcntl (InitSocket, F_SETFL, O_NONBLOCK); 
+
+# define NTRY 500
+  Ntry = 0;
+  while (Ntry < NTRY) {
+    fd = accept (InitSocket, (struct sockaddr *)&Address, &AddressLength);
+    if (fd != -1) break;
+    if (errno != EAGAIN) break;
+    usleep (10000);
+    Ntry ++;
+  }
+
+  if (fd < 0) return (-1);
+
+  fcntl (fd, F_SETFL, !O_NONBLOCK); 
+  return (fd);
+}
+
+int KiiWait (char *sockpath) {
+
+  int sock, status;
+  struct sockaddr_un Address;
+
+  strcpy (Address.sun_path, sockpath);
+  Address.sun_family = AF_UNIX;
+  sock = socket (AF_UNIX, SOCK_STREAM, 0);
+  status = connect (sock, (struct sockaddr *) &Address, sizeof (Address));
+  if (status < 0) {
+    fprintf (stderr, "unsuccessful connection: %d\n", status);
+    exit (0);
+  }
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+  unlink (sockpath);
+  return (sock);
+}
+
+int KiiClose (int fd) {
+
+  if (fd < 1) return (FALSE);
+  KiiSendCommand (fd, 4, "QUIT");
+  close (fd);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiOverlay.c	(revision 22322)
@@ -0,0 +1,130 @@
+# include <kapa_internal.h>
+
+# define NOVERLAY_TYPE 5
+static char KiiOverlayTypeName[NOVERLAY_TYPE][16] = {
+  "NONE",
+  "TEXT", 
+  "BOX", 
+  "LINE",
+  "CIRCLE", 
+};
+
+int KiiOverlayTypeByName (char *overname) {
+
+  int i;
+
+  for (i = 1; i < NOVERLAY_TYPE; i++) {
+    if (!strcasecmp (overname, KiiOverlayTypeName[i])) return (i);
+  }
+  return (0);
+}
+
+char *KiiOverlayTypeByNumber (int n) {
+
+  if ((n < 0) || (n >= NOVERLAY_TYPE)) return NULL;
+  return (KiiOverlayTypeName[n]);
+}
+
+int KiiSelectOverlay (char *overname, int *number) {
+
+  *number = -1;
+  if (!strcmp (overname, "red") || !strcmp (overname, "0")) {
+    *number = 0;
+    return (TRUE);
+  }
+  if (!strcmp (overname, "green") || !strcmp (overname, "1")) {
+    *number = 1;
+    return (TRUE);
+  }
+  if (!strcmp (overname, "blue") || !strcmp (overname, "2")) {
+    *number = 2;
+    return (TRUE);
+  }
+  if (!strcmp (overname, "yellow") || !strcmp (overname, "3")) {
+    *number = 3;
+    return (TRUE);
+  }
+
+  fprintf (stderr, "valid overlays may be: red (0), green (1), blue (2), yellow (3)\n");
+  return (FALSE);
+}
+
+int KiiLoadOverlay (int fd, KiiOverlay *overlay, int Noverlay, char *overname) {
+
+  int i, overnum, Ntextdata, NTEXTDATA, Ntext, Nchar;
+  char *textdata;
+  KiiOverlayBase *buffer;
+
+  Ntext = 0;
+  KiiSelectOverlay (overname, &overnum);
+
+  Ntextdata = 0;
+  NTEXTDATA = 1024;
+  ALLOCATE (textdata, char, 1024);
+
+  // we send the position information as a binary block
+  ALLOCATE (buffer, KiiOverlayBase, Noverlay);
+  for (i = 0; i < Noverlay; i++) {
+    buffer[i].x     = overlay[i].x;
+    buffer[i].y     = overlay[i].y;
+    buffer[i].dx    = overlay[i].dx;
+    buffer[i].dy    = overlay[i].dy;
+    buffer[i].angle = overlay[i].angle;
+    buffer[i].type  = overlay[i].type;
+    if (buffer[i].type == KII_OVERLAY_TEXT) {
+      Ntext ++;
+      Nchar = strlen(overlay[i].text) + 1;
+      if (Ntextdata + Nchar >= NTEXTDATA) {
+	NTEXTDATA += 1024;
+	REALLOCATE (textdata, char, NTEXTDATA);
+      }
+      sprintf (&textdata[Ntextdata], "%s\n", overlay[i].text);
+      Ntextdata += Nchar;
+    }
+  }
+
+  KiiSendCommand (fd,  4, "LOAD");
+  KiiSendMessage (fd, "%d %d %d %d", overnum, Noverlay, Ntext, Ntextdata);
+
+  // we could break this into segments if we want to trap an interrupt, but why bother?
+  Nchar = write (fd, buffer, Noverlay*sizeof(KiiOverlayBase));
+  KiiWaitAnswer (fd, "DONE");
+
+  write (fd, textdata, Ntextdata);
+  KiiWaitAnswer (fd, "DONE");
+
+  free (buffer);
+  free (textdata);
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KiiEraseOverlay (int fd, char *overname) {
+
+  int n;
+
+  KiiSelectOverlay (overname, &n);
+    
+  KiiSendCommand (fd, 4, "ERSO");
+  KiiSendCommand (fd, 16, "OVERLAY %7d ", n);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KiiSaveOverlay (int fd, int celestial, char *overname, char *file) {
+
+  int n;
+
+  KiiSelectOverlay (overname, &n);
+    
+  if (celestial) {
+    KiiSendCommand (fd, 4, "CSVE");
+  } else {
+    KiiSendCommand (fd, 4, "SAVE");
+  }
+
+  KiiSendMessage (fd, "FILE: %d %s", n, file);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiPicture.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiPicture.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/KiiPicture.c	(revision 22322)
@@ -0,0 +1,239 @@
+# include <kapa_internal.h>
+
+int KiiSetChannel (int fd, int channel) {
+
+  KiiSendCommand (fd, 4, "CHAN"); /* tell kapa to look for the incoming image */
+  KiiSendMessage (fd, "%1d", channel);
+  
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KiiSetColormap (int fd, char *colormap) {
+
+  KiiSendCommand (fd, 4, "CMAP"); /* tell kapa to look for the incoming image */
+  KiiSendMessage (fd, "%s", colormap);
+  
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KiiNewPicture1D (int fd, KiiImage *image, KapaImageData *data, Coords *coords) {
+
+  int Npix, Ncolors, size;
+  float *in, min, max;
+
+  Npix = image[0].Nx*image[0].Ny;
+
+  KiiSendCommand (fd, 4, "READ"); /* tell kapa to look for the incoming image */
+  KiiScanMessage (fd, "%d", &Ncolors);
+
+  in = image[0].data1d;
+
+  /* these are for a future upgrade */
+  min = max = 0.0;
+  size = Npix*sizeof(float);
+
+  /* done with the conversion, now send kapa the converted picture */
+  KiiSendMessage (fd, "%8d %8d", image[0].Nx, image[0].Ny);
+  KiiSendMessage (fd, "-32 1 0.0 1.0");
+  KiiSendMessage (fd, "%f %f %s %s", data[0].zero, data[0].range, data[0].name, data[0].file);
+  KiiSendMessage (fd, "%f %f %d ", min, max, size);
+  KiiSendMessage (fd, "%f %f %g %g %g ", coords[0].crval1, coords[0].crpix1, coords[0].cdelt1, coords[0].pc1_1, coords[0].pc1_2);
+  KiiSendMessage (fd, "%f %f %g %g %g ", coords[0].crval2, coords[0].crpix2, coords[0].cdelt2, coords[0].pc2_1, coords[0].pc2_2);
+  KiiSendMessage (fd, "%s", coords[0].ctype);
+
+  /* send the image data */
+  write (fd, image[0].data1d, size);
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KiiNewPicture1D_8bit (int fd, KiiImage *image, KapaImageData *data, Coords *coords) {
+
+  int i;
+  int Npix, Ncolors, NNcolors, size;
+  float *in, min, max;
+  char *out, *outbuffer;
+  double a1, a2;
+
+  Npix = image[0].Nx*image[0].Ny;
+
+  KiiSendCommand (fd, 4, "READ"); /* tell kapa to look for the incoming image */
+  KiiScanMessage (fd, "%d", &Ncolors);
+
+  ALLOCATE (outbuffer, char, Npix);
+  out = outbuffer;
+  in = image[0].data1d;
+
+  /* need to invert the logic if range < 0 */
+
+  /* define color table, */
+  NNcolors = Ncolors - 1;
+  if (data[0].logflux) {
+    data[0].range = MAX (2, data[0].range);
+    a1 = Ncolors / log10 (data[0].range);
+    a2 = data[0].zero;
+    for (i = 0; i < Npix; i++, in++, out++) {
+      *out = (char) MIN (a1 * log10 (MAX (*in - a2, 1.0)), NNcolors);
+    }
+  } else {
+    a1 = Ncolors / data[0].range;
+    a2 = Ncolors * data[0].zero / data[0].range;
+    for (i = 0; i < Npix; i++, in++, out++) {
+      *out = (char) MIN (MAX (a1 * *in - a2, 0), NNcolors);
+    }
+  }
+  
+  /* these are for a future upgrade */
+  min = max = 0.0;
+  size = Npix*sizeof(char);
+
+  /* done with the conversion, now send kapa the converted picture */
+  KiiSendMessage (fd, "%8d %8d", image[0].Nx, image[0].Ny);
+  KiiSendMessage (fd, "8 1 0.0 1.0");
+  KiiSendMessage (fd, "%f %f %s %s", data[0].zero, data[0].range, data[0].name, data[0].file);
+  KiiSendMessage (fd, "%f %f %d ", min, max, size);
+  KiiSendMessage (fd, "%f %f %g %g %g ", coords[0].crval1, coords[0].crpix1, coords[0].cdelt1, coords[0].pc1_1, coords[0].pc1_2);
+  KiiSendMessage (fd, "%f %f %g %g %g ", coords[0].crval2, coords[0].crpix2, coords[0].cdelt2, coords[0].pc2_1, coords[0].pc2_2);
+  KiiSendMessage (fd, "%s", coords[0].ctype);
+
+  /* send the image data */
+  write (fd, outbuffer, size);
+  free (outbuffer);
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KiiNewPicture2D (int fd, KiiImage *image, KapaImageData *data, Coords *coords) {
+
+  int i, j;
+  int Npix, Ncolors, NNcolors, size;
+  float *in, min, max;
+  char *out, *outbuffer;
+  double a1, a2;
+
+  Npix = image[0].Nx*image[0].Ny;
+
+  KiiSendCommand (fd, 4, "READ"); /* tell kapa to look for the incoming image */
+  KiiScanMessage (fd, "%d", &Ncolors);
+
+  ALLOCATE (outbuffer, char, Npix);
+  out = outbuffer;
+
+  /* need to invert the logic if range < 0 */
+
+  /* define color table, */
+  NNcolors = Ncolors - 1;
+  if (data[0].logflux) {
+    data[0].range = MAX (2, data[0].range);
+    a1 = Ncolors / log10 (data[0].range);
+    a2 = data[0].zero;
+    for (j = 0; j < image[0].Ny; j++) {
+      in = image[0].data2d[j];
+      for (i = 0; i < image[0].Nx; i++, in++, out++) {
+	*out = (char) MIN (a1 * log10 (MAX (*in - a2, 1.0)), NNcolors);
+      }
+    }
+  } else {
+    a1 = Ncolors / data[0].range;
+    a2 = Ncolors * data[0].zero / data[0].range;
+    for (j = 0; j < image[0].Ny; j++) {
+      in = image[0].data2d[j];
+      for (i = 0; i < image[0].Nx; i++, in++, out++) {
+	*out = (char) MIN (MAX (a1 * *in - a2, 0), NNcolors);
+      }
+    }
+  }
+  
+  /* these are for a future upgrade */
+  min = max = 0.0;
+  size = Npix*sizeof(char);
+
+  /* done with the conversion, now send kapa the converted picture */
+  KiiSendMessage (fd, "%8d %8d", image[0].Nx, image[0].Ny);
+  KiiSendMessage (fd, "8 1 0.0 1.0");
+  KiiSendMessage (fd, "%f %f %s %s", data[0].zero, data[0].range, data[0].name, data[0].file);
+  KiiSendMessage (fd, "%f %f %d ", min, max, size);
+  KiiSendMessage (fd, "%f %f %g %g %g ", coords[0].crval1, coords[0].crpix1, coords[0].cdelt1, coords[0].pc1_1, coords[0].pc1_2);
+  KiiSendMessage (fd, "%f %f %g %g %g ", coords[0].crval2, coords[0].crpix2, coords[0].cdelt2, coords[0].pc2_1, coords[0].pc2_2);
+  KiiSendMessage (fd, "%s", coords[0].ctype);
+
+  /* send the image data */
+  write (fd, outbuffer, size);
+  free (outbuffer);
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaSetImageCoords (int fd, Coords *coords) {
+
+  /* tell kapa to look for the incoming image */
+  KiiSendCommand (fd, 4, "SIMC"); 
+  
+  KiiSendMessage (fd, "%g %g %g %g", 
+		  coords[0].pc1_1, coords[0].pc2_2,
+		  coords[0].pc1_2, coords[0].pc2_1);
+
+  KiiSendMessage (fd, "%s", coords[0].ctype);
+
+  KiiSendMessage (fd, "%g %g %g %g %g %g", 
+		  coords[0].crval1,
+		  coords[0].crval2,
+		  coords[0].crpix1,
+		  coords[0].crpix2,
+		  coords[0].cdelt1,
+		  coords[0].cdelt2);
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaGetImageCoords (int fd, Coords *coords) {
+
+  /* tell kapa to look for the incoming image */
+  KiiSendCommand (fd, 4, "GIMC"); 
+  
+  KiiScanMessage (fd, "%f %f %f %f", 
+		  &coords[0].pc1_1, &coords[0].pc2_2,
+		  &coords[0].pc1_2, &coords[0].pc2_1);
+
+  KiiScanMessage (fd, "%s", coords[0].ctype);
+
+  KiiScanMessage (fd, "%lf %lf %f %f %f %f", 
+		  &coords[0].crval1,
+		  &coords[0].crval2,
+		  &coords[0].crpix1,
+		  &coords[0].crpix2,
+		  &coords[0].cdelt1,
+		  &coords[0].cdelt2);
+
+  // XXX at some point, we need to add polynomials and 2-level mosaic
+  // astrometry here.
+
+  coords[0].Npolyterms = 0;
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KapaGetImageRange (int fd, double *Xmin, double *Xmax, double *Ymin, double *Ymax) {
+
+  /* tell kapa to look for the incoming image */
+  KiiSendCommand (fd, 4, "GIMR"); 
+  
+  KiiScanMessage (fd, "%lf %lf %lf %lf", Xmin, Xmax, Ymin, Ymax);
+
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+
+/* this function should be broken into pieces: 
+   KiiSendImage
+   KiiSendCoords (default to 0 otherwise)
+   KiiSendFilename
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/PSRotFont.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/PSRotFont.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/PSRotFont.c	(revision 22322)
@@ -0,0 +1,182 @@
+# include <kapa_internal.h>
+# define NROTCHARS 256
+
+/* writes commands to print string at location and angle using
+   currently set font and size */
+void PSRotText (FILE *f, int x, int y, char *string, int pos, double angle) {
+
+  char *segment, basename[64], *currentname;
+  int i, N, code;
+  int dX, dY, Xoff, Yoff, X, Y, Nseg, NSEG, YoffBase;
+  double cs, sn, fscale, currentscale;
+  int basesize, currentsize;
+  RotFont *currentfont;
+  
+  currentname = GetRotFont (&currentsize);
+  currentfont = GetRotFontData (&currentscale);
+  strcpy (basename, currentname);
+  basesize = currentsize;
+
+  /* strip off leading WHITESPACE */
+  stripwhite (string);
+  if (*string == 0) return;
+  
+  /* compute string length */
+  /* PS fonts are somewhat different scales from bitmap font equivalents */
+  fscale = 1.0;
+  if (!strcmp (currentname, "times")) fscale = 1.07;
+  if (!strcmp (currentname, "courier")) fscale = 1.5;
+  if (!strcmp (currentname, "helvetica")) fscale = 0.9;
+  if (!strcmp (currentname, "symbol")) fscale = 1.2;
+  dX = fscale*RotStrlen (string);
+  dY = currentfont[65].ascent;
+  
+  /* apply appropriate offset */
+  Xoff = Yoff = 0;
+  switch (pos) {
+  case 0: Xoff =     -dX; Yoff = -dY; break;
+  case 1: Xoff = -0.5*dX; Yoff = -dY; break;
+  case 2: Xoff =       0; Yoff = -dY; break;
+  case 3: Xoff =     -dX; Yoff = -0.5*dY; break;
+  case 4: Xoff = -0.5*dX; Yoff = -0.5*dY; break;
+  case 5: Xoff =       0; Yoff = -0.5*dY; break;
+  case 6: Xoff =     -dX; Yoff = 0; break;
+  case 7: Xoff = -0.5*dX; Yoff = 0; break;
+  case 8: Xoff =       0; Yoff = 0; break;
+  }
+  cs = cos(angle*RAD_DEG);
+  sn = sin(angle*RAD_DEG);
+  X = x + Xoff*cs + Yoff*sn;
+  Y = y - Xoff*sn + Yoff*cs;
+
+  PSSetFont (f, currentname, currentsize);
+  fprintf (f, "gsave\n");
+  fprintf (f, " %d %d moveto %f rotate\n", X, Y, -angle);
+
+  Nseg = 0;
+  NSEG = strlen(string) + 2;
+  ALLOCATE (segment, char, NSEG);
+  bzero (segment, NSEG);
+
+  code = FALSE;
+  YoffBase = 0;
+  /* accumulate string segments with common state */
+  for (i = 0; i < strlen (string); i++) {
+    N = (int)(string[i]);
+    if ((N < 0) || (N >= NROTCHARS)) continue;
+
+    /* check for special characters */
+    if (!code) {
+      /* superscript character (^) */
+      if (N == 94) {
+	PSDumpRotSegment (f, segment, &Nseg);
+	SetRotFont (currentname, (int)(0.8*currentsize));
+	currentfont = GetRotFontData (&currentscale);
+	Yoff = 0.75*currentscale*dY;
+	fprintf (f, "0 %d rmoveto\n", Yoff);
+	YoffBase += Yoff;
+	PSSetFont (f, currentname, currentsize);
+	continue;
+      }
+      /* subscript character (_) */
+      if (N == 95) { 
+	PSDumpRotSegment (f, segment, &Nseg);
+	SetRotFont (currentname, (int)(0.8*currentsize));
+	currentfont = GetRotFontData (&currentscale);
+	Yoff = -0.5*currentscale*dY;
+	fprintf (f, "0 %d rmoveto\n", Yoff);
+	YoffBase += Yoff;
+	PSSetFont (f, currentname, currentsize);
+	continue;
+      }
+      /* end super/sub script (|) */
+      if (N == 124) {
+	PSDumpRotSegment (f, segment, &Nseg);
+	SetRotFont (currentname, basesize);
+	currentfont = GetRotFontData (&currentscale);
+	fprintf (f, "0 %d rmoveto\n", -YoffBase);
+	YoffBase = 0;
+	PSSetFont (f, currentname, currentsize);
+	continue;
+      }
+      /* escape char (\) */
+      if (N == 92) {
+	code = TRUE;
+	continue;
+      } 
+      /* begin paren (insert \) */
+      if (N == 40) {
+	code = FALSE;
+	segment[Nseg] = 92;
+	Nseg ++;
+	CHECK_REALLOCATE (segment, char, NSEG, Nseg, 64);
+      }
+      /* end paren (insert \) */
+      if (N == 41) {
+	code = FALSE;
+	segment[Nseg] = 92;
+	Nseg ++;
+	CHECK_REALLOCATE (segment, char, NSEG, Nseg, 64);
+      }
+      /* font change character (&) */
+      if (N == 38) {
+	PSDumpRotSegment (f, segment, &Nseg);
+	if (string[i+1] == 'h') {
+	  SetRotFont ("helvetica", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	  PSSetFont (f, currentname, currentsize);
+	}
+	if (string[i+1] == 't') {
+	  SetRotFont ("times", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	  PSSetFont (f, currentname, currentsize);
+	}
+	if (string[i+1] == 'c') {
+	  SetRotFont ("courier", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	  PSSetFont (f, currentname, currentsize);
+	}
+	if (string[i+1] == 's') {
+	  SetRotFont ("symbol", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	  PSSetFont (f, currentname, currentsize);
+	}
+	i++;
+	continue;
+      }
+    }
+    code = FALSE;
+    segment[Nseg] = N;
+    Nseg ++;
+    CHECK_REALLOCATE (segment, char, NSEG, Nseg, 64);
+  }
+  PSDumpRotSegment (f, segment, &Nseg);
+  fprintf (f, "stroke grestore\n");
+  free (segment);
+  SetRotFont (basename, basesize);
+}
+
+void PSDumpRotSegment (FILE *f, char *segment, int *Nseg) {
+  segment[*Nseg] = 0;
+  fprintf (f, "(%s) show\n", segment);
+  bzero (segment, *Nseg);
+  *Nseg = 0;
+}
+
+void PSSetFont (FILE *f, char *name, int size) {
+  if (!strcmp (name, "times")) 
+    fprintf (f, "/Times-Roman findfont %d scalefont setfont\n", size);
+  if (!strcmp (name, "helvetica")) 
+    fprintf (f, "/Helvetica findfont %d scalefont setfont\n", size);
+  if (!strcmp (name, "courier")) 
+    fprintf (f, "/Courier findfont %d scalefont setfont\n", size);
+  if (!strcmp (name, "symbol")) 
+    fprintf (f, "/Symbol findfont %d scalefont setfont\n", size);
+}
+
+
+
+  /* test cross hair
+  fprintf (f, "%d %d %d %d L\n", x-10, y, x+10, y);
+  fprintf (f, "%d %d %d %d L\n", x, y-10, x, y+10);
+  */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/RotFont.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/RotFont.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/RotFont.c	(revision 22322)
@@ -0,0 +1,124 @@
+# include <kapa_internal.h>
+# include "alphabet.h"
+  
+static int Nrotfonts;
+static FontSet *RotFonts;
+
+static char currentname[64];
+static int  currentsize;
+static double currentscale;
+static RotFont *currentfont;
+
+void InitRotFonts () {
+
+  int i, Nhardwired;
+
+  Nhardwired = sizeof (HardwiredFonts) / sizeof (FontSet);
+  
+  Nrotfonts = Nhardwired;
+  ALLOCATE (RotFonts, FontSet, Nrotfonts);
+  
+  for (i = 0; i < Nhardwired; i++) {
+    RotFonts[i] = HardwiredFonts[i];
+  }
+
+  currentfont = RotFonts[DEFFONT].font;
+  currentscale = 1.0;
+  strcpy (currentname, RotFonts[DEFFONT].name);
+  currentsize = RotFonts[DEFFONT].size;
+}
+
+int SetRotFont (char *name, int size) {
+  
+  int i, nsize, msize, bsize, bigger, dsize, match, good;
+
+  bigger = good = match = -1;
+  dsize = 10000;
+  bsize = 10000;
+  for (i = 0; i < Nrotfonts; i++) {
+    if (!strcasecmp (RotFonts[i].name, name)) {
+      good = i;
+      nsize = abs (RotFonts[i].size - size);
+      if (nsize < dsize) {
+	match = i;
+	dsize = nsize;
+      }
+      msize = RotFonts[i].size - size;
+      if ((msize < bsize) && (msize >= 0)) {
+	bigger = i;
+	bsize = msize;
+      }
+    }
+  }
+  
+  if ((match == -1) && (good != -1)) match = good;
+  if (bigger != -1) match = bigger;
+  if (match != -1) {
+    currentfont = RotFonts[match].font;
+    currentscale = (double) size / RotFonts[match].size;
+    currentsize = size;
+    strcpy (currentname, name);
+    return (TRUE);
+  } else {
+    fprintf (stderr, "no matching font\n");
+    return (FALSE);
+  }
+
+}
+  
+char *GetRotFont (int *size) {
+
+  *size = currentsize;
+  return (currentname);
+
+}
+
+RotFont *GetRotFontData (double *scale) {
+  *scale = currentscale;
+  return (currentfont);
+}
+
+int RotStrlen (char *c) {
+
+  int i, N, dX, code;
+  double scale; 
+  
+  scale = currentscale;
+
+  /* find string length */
+  dX = 0;
+
+  code = FALSE;
+  for (i = 0; i < strlen (c); i++) {
+    N = (int)(c[i]);
+    /* skip non-printing characters */
+    if ((N < 0) || (N >= NROT)) continue;
+
+    /* check for special characters */
+    if (!code) {
+      if (N == 94) { /* super-script */
+	scale *= 0.8;
+	continue;
+      }
+      if (N == 95) { /* sub-script */
+	scale *= 0.8;
+	continue;
+      }
+      if (N == 124) { /* normal-script */
+	scale = currentscale;
+	continue;
+      }
+      if (N == 92) { /* backslash */
+	code = TRUE;
+	continue;
+      } 
+      if (N == 38) { /* font-code */
+	i++;
+	continue;
+      }
+    }
+    code = FALSE;
+    dX += scale*currentfont[N].dx + 1;
+  }
+  return (dX);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/bDrawFuncs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/bDrawFuncs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/bDrawFuncs.c	(revision 22322)
@@ -0,0 +1,418 @@
+# include <kapa_internal.h>
+
+static int bWeight;
+static int bType;
+static bDrawColor bColor;
+static bDrawBuffer *bBuffer;
+void bDrawCircleSingle (double xc, double yc, double radius);
+
+bDrawBuffer *bDrawBufferCreate (int Nx, int Ny) {
+
+  int i, j;
+  bDrawColor white;
+  bDrawBuffer *buffer;
+
+  white = KapaColorByName ("white");
+
+  ALLOCATE (buffer, bDrawBuffer, 1);
+  buffer[0].Nx = Nx;
+  buffer[0].Ny = Ny;
+
+  ALLOCATE (buffer[0].pixels, bDrawColor *, Ny);
+  for (i = 0; i < Ny; i++) {
+    ALLOCATE (buffer[0].pixels[i], bDrawColor, Nx);
+    for (j = 0; j < Nx; j++) {
+      buffer[0].pixels[i][j] = white;
+    }
+  }
+  return (buffer);
+}
+
+void bDrawBufferFree (bDrawBuffer *buffer) {
+
+  int i;
+
+  for (i = 0; i < buffer[0].Ny; i++) {
+    free (buffer[0].pixels[i]);
+  }
+  free (buffer[0].pixels);
+  free (buffer);
+  return;
+}
+
+void bDrawSetBuffer (bDrawBuffer *buffer) {
+  bBuffer = buffer;
+  return;
+}
+
+void bDrawSetStyle (bDrawColor color, int lw, int lt) {
+  bColor = color;
+  bWeight = lw;
+  bType = lt;
+  return;
+}
+
+// draw a point in the current color 
+void bDrawPoint (int x, int y) {
+
+  if (x < 0) return;
+  if (y < 0) return;
+  if (x >= bBuffer[0].Nx) return;
+  if (y >= bBuffer[0].Ny) return;
+  bBuffer[0].pixels[y][x] = bColor;
+  return;
+}
+
+// draw a point in the current color 
+void bDrawPointf (float x, float y) {
+
+  bDrawPoint (ROUND(x), ROUND(y));
+  return;
+}
+
+void bDrawTriOpen (double x1, double y1, double x2, double y2, double x3, double y3) {
+
+  bDrawLine (x1, y1, x2, y2);
+  bDrawLine (x2, y2, x3, y3);
+  bDrawLine (x3, y3, x1, y1);
+
+  return;
+}
+
+void bDrawRectOpen (double x1, double y1, double x2, double y2) {
+
+  int X1, Y1, X2, Y2;
+
+  if (x1 > x2) SWAP (x1, x2);
+  if (y1 > y2) SWAP (y1, y2);
+
+  X1 = MIN (MAX (ROUND (x1), 0), bBuffer[0].Nx - 1);
+  X2 = MIN (MAX (ROUND (x2), 1), bBuffer[0].Nx);
+
+  Y1 = MIN (MAX (ROUND (y1), 0), bBuffer[0].Ny - 1);
+  Y2 = MIN (MAX (ROUND (y2), 1), bBuffer[0].Ny);
+
+  bDrawLineHorizontal (X1, X2, Y1);
+  bDrawLineHorizontal (X1, X2, Y2);
+  bDrawLineVertical (X1, Y1, Y2);
+  bDrawLineVertical (X2, Y1, Y2);
+  return;
+}
+
+void bDrawRectFill (double x1, double y1, double x2, double y2) {
+
+  int i;
+  int X1, Y1, X2, Y2;
+
+  if (x1 > x2) SWAP (x1, x2);
+  if (y1 > y2) SWAP (y1, y2);
+
+  X1 = MIN (MAX (ROUND (x1), 0), bBuffer[0].Nx - 1);
+  X2 = MIN (MAX (ROUND (x2), 1), bBuffer[0].Nx);
+
+  Y1 = MIN (MAX (ROUND (y1), 0), bBuffer[0].Ny - 1);
+  Y2 = MIN (MAX (ROUND (y2), 0), bBuffer[0].Ny);
+
+  for (i = Y1; i < Y2; i++) {
+    bDrawLineHorizontal (X1, X2, i);
+  } 
+  return;
+}
+
+// identify the quadrant and draw the correct line
+void bDrawLine (double x1, double y1, double x2, double y2) {
+
+  int FlipDirect, FlipCoords;
+  int X1, Y1, X2, Y2, dX, dY;
+
+  /* rather than draw the line from float positions, we find the closest
+     integer end-points and draw the line between those pixels */ 
+
+  X1 = ROUND(x1);
+  Y1 = ROUND(y1);
+  X2 = ROUND(x2);
+  Y2 = ROUND(y2);
+
+  dX = X2 - X1;
+  dY = Y2 - Y1;
+
+  FlipCoords = (abs(dX) < abs(dY));
+  FlipDirect = FlipCoords ? (y1 > y2) : (x1 > x2);
+
+  if (!FlipDirect && !FlipCoords) bDrawLineWeight (X1, Y1, X2, Y2, FALSE);
+  if ( FlipDirect && !FlipCoords) bDrawLineWeight (X2, Y2, X1, Y1, FALSE);
+  if (!FlipDirect &&  FlipCoords) bDrawLineWeight (Y1, X1, Y2, X2, TRUE);
+  if ( FlipDirect &&  FlipCoords) bDrawLineWeight (Y2, X2, Y1, X1, TRUE);
+
+  return;
+}
+
+// draw a series of lines to give the line weight
+void bDrawLineWeight (int X1, int Y1, int X2, int Y2, int swapcoords) {
+
+  int dN, dNs, dNe;
+
+  dNs = -0.5*(bWeight - 1); 
+  /* 0, 0, 0, -1, -1, -2, -2 */
+
+  dNe = +0.5*bWeight + 1; 
+  /* 1, 1, 2, 2, 2, 3, 3 */
+
+  for (dN = dNs; dN < dNe; dN++) {
+    bDrawLineBresen (X1, Y1 + dN, X2, Y2 + dN, swapcoords);
+  }
+  return;
+}
+
+// use the Bresenham line drawing technique
+// integer-only Bresenham line-draw version which is fast
+void bDrawLineBresen (int X1, int Y1, int X2, int Y2, int swapcoords) {
+
+  int X, Y, dX, dY;
+  int e, e2;
+  int N, DashOn;
+
+  dX = X2 - X1;
+  dY = Y2 - Y1;
+
+  DashOn = TRUE;
+
+  Y = Y1;
+  e = 0;
+  for (X = X1, N = 0; X <= X2; X++, N++) {
+    if (bType == 1) { DashOn = (N % 10) < 5; }
+    if (bType == 2) { DashOn = (N % 6) < 3; }
+    if (swapcoords) {
+      if (DashOn) bDrawPoint (Y,X);
+    } else {
+      if (DashOn) bDrawPoint (X,Y);
+    }
+    e += dY;
+    e2 = 2 * e;
+    if (e2 > dX) {
+      Y++;
+      e -= dX;
+    } 
+    if (e2 < -dX) {
+      Y--;
+      e += dX;
+    }
+  }
+  return;
+}
+
+void bDrawLineHorizontal (int X1, int X2, int Y) {
+  
+  int i;
+
+  for (i = X1; i < X2; i++) {
+    bBuffer[0].pixels[Y][i] = bColor;
+  }
+  return;
+}
+
+void bDrawLineVertical (int X, int Y1, int Y2) {
+  
+  int i;
+
+  for (i = Y1; i < Y2; i++) {
+    bBuffer[0].pixels[i][X] = bColor;
+  }
+  return;
+}
+
+void bDrawTriFill (double x1, double y1, double x2, double y2, double x3, double y3) {
+
+  bDrawTriOpen (x1, y1, x2, y2, x3, y3);
+  return;
+}
+
+void bDrawArc (double Xc, double Yc, double Xr, double Yr, double Ts, double Te) {
+
+  float t, dt;
+  int x, y;
+
+  /* drawing a complete circle */
+//  if ((fabs(Te - Ts) > 360.0) && (Xr == Yr)) {
+//    bDrawCircle (Xc, Yc, Xr);
+//    return;
+//  }
+
+  /* only draw a single loop */
+  if (fabs(Te - Ts) > 360.0) {
+    Te = 360.0;
+    Ts = 0.0;
+  }
+
+  /* smallest angle is 1/Rmax */
+  dt = MAX (fabs(Xr * sin(Ts*RAD_DEG)), fabs(Yr * cos(Ts*RAD_DEG)));
+  dt = 1.0 / dt;
+
+  for (t = Ts*RAD_DEG; t <= Te*RAD_DEG; t += dt) {
+    x = Xr*cos(t) + Xc;
+    y = Yr*sin(t) + Yc;
+
+    /* we could use the value of MAX(dy/dt,dx/dt) to set dt */
+    bDrawPoint (x,y);
+
+    dt = MAX (fabs(Xr * sin(t)), fabs(Yr * cos(t)));
+    dt = 1.0 / dt;
+  }
+  return;
+}
+
+// draw a series of circles to give line weight
+void bDrawCircle (double xc, double yc, double radius) {
+
+  int dN, dNs, dNe;
+
+  dNs = -0.5*(bWeight - 1); 
+  /* 0, 0, 0, -1, -1, -2, -2 */
+
+  dNe = +0.5*bWeight + 1; 
+  /* 1, 1, 2, 2, 2, 3, 3 */
+
+  for (dN = dNs; dN < dNe; dN++) {
+    bDrawCircleSingle (xc, yc, radius + dN);
+  }
+  return;
+}
+
+// draw a pure circle  
+void bDrawCircleSingle (double xc, double yc, double radius) {
+
+  int Xc, Yc, Radius;
+  int x, y, d;
+
+  Xc = ROUND(xc);
+  Yc = ROUND(yc);
+  Radius = ROUND(radius);
+
+  x = 0;
+  y = Radius;
+
+  // d = 3 - 2*Radius;
+  d = 5 - 4*radius;
+
+  while (x <= y) {
+    bDrawPoint (Xc+x, Yc+y);
+    bDrawPoint (Xc+x, Yc-y);
+    bDrawPoint (Xc-x, Yc+y);
+    bDrawPoint (Xc-x, Yc-y);
+    bDrawPoint (Xc+y, Yc+x);
+    bDrawPoint (Xc+y, Yc-x);
+    bDrawPoint (Xc-y, Yc+x);
+    bDrawPoint (Xc-y, Yc-x);
+
+    if (d < 0) {
+      // d = d + 4*x + 6;
+      d = d + 8*x + 4;
+    } else {
+      // d = d + 4*(x-y) + 10;
+      d = d + 8*(x-y) + 8;
+      y--;
+    }
+    x++;
+  }
+}
+
+// draw a pure circle  
+void bDrawCircleFill (double xc, double yc, double radius) {
+
+  int Xc, Yc, Radius;
+  int x, y, d;
+
+  Xc = ROUND(xc);
+  Yc = ROUND(yc);
+  Radius = ROUND(radius);
+
+  x = 0;
+  y = Radius;
+
+  // d = 3 - 2*Radius;
+  d = 5 - 4*radius;
+
+  while (x <= y) {
+    bDrawLineHorizontal (Xc-x, Xc+x, Yc+y);
+    bDrawLineHorizontal (Xc-x, Xc+x, Yc-y);
+    bDrawLineHorizontal (Xc-y, Xc+y, Yc+x);
+    bDrawLineHorizontal (Xc-y, Xc+y, Yc-x);
+
+    if (d < 0) {
+      // d = d + 4*x + 6;
+      d = d + 8*x + 4;
+    } else {
+      // d = d + 4*(x-y) + 10;
+      d = d + 8*(x-y) + 8;
+      y--;
+    }
+    x++;
+  }
+}
+
+/* 
+the discriminant of inside or outside the circle is:
+
+f(x,y) = x^2 + y^2 - r^2
+
+- negative: (x,y) inside circle
+- positive: (x,y) outside circle
+
+given d(0) = f(x,y); find d(1) = f(x+1,y):
+d(1) = (x+1)^2 + y^2 - r^2
+d(1) = d(0) + 2x + 1
+(use d(1) if d(0) < 0, ie inside circle)
+
+given d(0) = f(x,y); find d(1) = f(x+1,y-1):
+d(1) = (x+1)^2 + (y-1)^2 - r^2
+d(1) = d(0) + 2x + 1 - 2y + 1
+d(1) = d(0) + 2(x-y) + 2
+
+* init d to f(1,r-1/2) instead of r, keeping the effective boundary 
+  between two pixels (also, an inside point)
+
+f(1,r-1/2) = 1 + (r-1/2)^2 - r^2 = 5/4 - r
+f(1,r-1/4) = 1 + (r-1/4)^2 - r^2 = 1 + r/2 + 1/16 = 17/16 + r/2
+
+f(1,r-x)   = 1 + (r-x)^2 - r^2 = 1 - 2xr + x^2 = A (3 - 2r)
+
+1+x^2 = 3A
+-2x = -2A
+
+A = x
+
+1 - 3x + x^2 = 0
+
+(3 +/- sqrt(9 - 4))/2 = (3 - sqrt(5))/2
+
+multiply all d values by 4 to get integer tests:
+
+d'(0) = 5 - 4r
+d'(in) = d' + 8x + 4
+d'(out) = d' + 8(x-y) + 8
+
+*/
+
+/* This is the Bresenham line-drawing algorithm for 1st and 4th quandrant
+   vectors with positive and negative slopes < 1. this is the sequence if we use
+   float errors and tests it is easy to understand, but slower than it could be
+
+  { 
+    float e, m;
+    m = dY / dX;
+    Y = Y1;
+    e = 0;
+    for (X = X1; X <= X2; X++) {
+      plot (X,Y);
+      e += m;
+      if (e > 0.5) {
+	Y++;
+	e -= 1.0;
+      }
+      if (e < -0.5) {
+	Y--;
+	e += 1.0;
+      }
+    }
+  }
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/bDrawRotFont.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/bDrawRotFont.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libkapa/src/bDrawRotFont.c	(revision 22322)
@@ -0,0 +1,211 @@
+# include <kapa_internal.h>
+
+# define NROTCHARS 256
+# define XPROC(x,y) (scale*(cs*((x) - x0) - sn*((y) - y0)) + X0)
+# define YPROC(x,y) (scale*(cs*((y) - y0) + sn*((x) - x0)) + Y0)
+# define NEARINT(x) ((x < 0) ? ((int)(x - 0.5)) : ((int)(x + 0.5)))
+  
+static bDrawColor black;
+static bDrawColor white;
+
+int bDrawRotText (int x, int y, char *string, int pos, double angle) {
+
+  unsigned char *bitmap;
+  char *currentname, basename[64]; 
+  int i, dy, dx, N, X, Y, code;
+  int dX, Xoff, dY, Yoff, YoffBase;
+  int currentsize, basesize;
+  double cs, sn, currentscale;
+  RotFont *currentfont;
+
+  white = KapaColorByName ("white");
+  black = KapaColorByName ("black");
+
+  currentname = GetRotFont (&currentsize);
+  currentfont = GetRotFontData (&currentscale);
+  strcpy (basename, currentname);
+  basesize = currentsize;
+
+  /* strip leading WHITESPACE */
+  stripwhite (string);
+  if (*string == 0) return (FALSE);
+  
+  /* compute string length */
+  cs = cos(angle*RAD_DEG);
+  sn = sin(angle*RAD_DEG);
+  dX = RotStrlen (string);
+  dY = currentfont[65].ascent;
+
+  /* apply appropriate offset */
+  Xoff = Yoff = 0;
+  switch (pos) {
+  case 0: Xoff =     -dX; Yoff = dY; break;
+  case 1: Xoff = -0.5*dX; Yoff = dY; break;
+  case 2: Xoff =       0; Yoff = dY; break;
+  case 3: Xoff =     -dX; Yoff = 0.5*dY; break;
+  case 4: Xoff = -0.5*dX; Yoff = 0.5*dY; break;
+  case 5: Xoff =       0; Yoff = 0.5*dY; break;
+  case 6: Xoff =     -dX; Yoff = 0; break;
+  case 7: Xoff = -0.5*dX; Yoff = 0; break;
+  case 8: Xoff =       0; Yoff = 0; break;
+  }
+
+  code = FALSE;
+
+  YoffBase = Yoff;
+  /* draw characters one-by-one */
+  for (i = 0; i < strlen(string); i++) {
+    N = (int)(string[i]);
+    if ((N < 0) || (N >= NROTCHARS)) continue;
+
+    /* check for special characters */
+    if (!code) {
+      if (N == 94) {
+	SetRotFont (currentname, (int)(0.8*currentsize));
+	currentfont = GetRotFontData (&currentscale);
+	Yoff -= 0.5*currentscale*dY;
+	continue;
+      }
+      if (N == 95) { 
+	SetRotFont (currentname, (int)(0.8*currentsize));
+	currentfont = GetRotFontData (&currentscale);
+	Yoff += 0.5*currentscale*dY;
+	continue;
+      }
+      if (N == 124) {
+	SetRotFont (currentname, basesize);
+	currentfont = GetRotFontData (&currentscale);
+	Yoff = YoffBase;
+	continue;
+      }
+      if (N == 92) {
+	code = TRUE;
+	continue;
+      } 
+      if (N == 38) {
+	if (string[i+1] == 'h') {
+	  SetRotFont ("helvetica", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	}
+	if (string[i+1] == 't') {
+	  SetRotFont ("times", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	}
+	if (string[i+1] == 'c') {
+	  SetRotFont ("courier", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	}
+	if (string[i+1] == 's') {
+	  SetRotFont ("symbol", currentsize);
+	  currentfont = GetRotFontData (&currentscale);
+	}
+	i++;
+	continue;
+      }
+    }
+    code = FALSE;
+
+    bitmap = currentfont[N].bits;
+    dx = currentfont[N].dx;
+    dy = currentfont[N].dy;
+    X = x + (int)(Xoff*cs - Yoff*sn) + (int)(currentscale*currentfont[N].ascent*sn);
+    Y = y + (int)(Xoff*sn + Yoff*cs) - (int)(currentscale*currentfont[N].ascent*cs);
+    bDrawRotBitmap (X, Y, dx, dy, bitmap, TRUE, angle, currentscale);
+    Xoff += 1 + (int)(currentscale*dx + 0.5);
+  }
+  SetRotFont (basename, basesize);
+  return (TRUE);
+}
+
+int bDrawRotBitmap (int x, int y, int dx, int dy, unsigned char *bitmap, int mode, double angle, double scale) {
+
+  int ii, jj, byte_line, byte, bit, flag;
+  bDrawColor color;
+  double i, j, cs, sn, rscale, tmp;
+  int X, Y, X0, X1, X2, Y0, Y1, Y2, x0, y0;
+
+  /* this mode option is nort actually used... */
+  if (mode) {
+    color = black;
+  } else {
+    color = white;
+  } 
+    
+  byte_line = (int) ((dx + 7) / 8);
+
+  cs = cos(angle*RAD_DEG);  sn = sin(angle*RAD_DEG);
+  rscale = 1.0 / scale;
+
+  X0 = 0;
+  Y0 = 0;
+  x0 = 0;
+  y0 = 0;
+
+  X2 = X1 = XPROC (0,0);
+  Y2 = Y1 = YPROC (0,0);
+
+  X = XPROC (dx,0);
+  Y = YPROC (dx,0);
+# ifdef DRAWBOXES
+  bDrawLine (x+X, y+Y, x+X1, y+Y1);
+  Xt = X;
+  Yt = Y;
+# endif
+  X1 = MIN (X, X1);
+  X2 = MAX (X, X2);
+  Y1 = MIN (Y, Y1);
+  Y2 = MAX (Y, Y2);
+
+  X = XPROC (dx,dy);
+  Y = YPROC (dx,dy);
+# ifdef DRAWBOXES
+  bDrawLine (x+X, y+Y, x+Xt, y+Yt);
+  Xt = X;
+  Yt = Y;
+# endif
+  Y1 = MIN (Y, Y1);
+  Y2 = MAX (Y, Y2);
+  X1 = MIN (X, X1);
+  X2 = MAX (X, X2);
+
+  X = XPROC (0,dy);
+  Y = YPROC (0,dy);
+# ifdef DRAWBOXES
+  bDrawLine (x+X, y+Y, x+Xt, y+Yt);
+  Xt = X;
+  Yt = Y;
+# endif
+  Y1 = MIN (Y, Y1);
+  Y2 = MAX (Y, Y2);
+  X1 = MIN (X, X1);
+  X2 = MAX (X, X2);
+
+  bDrawSetStyle (color, 0, 0);
+  if (scale > 1) {
+    for (i = X1; i <= X2; i+=1) {
+      for (j = Y1; j <= Y2; j+=1) {
+	tmp = rscale*(cs*(i - X0) + sn*(j - Y0)) + x0;  ii = NEARINT (tmp);
+	tmp = rscale*(cs*(j - Y0) - sn*(i - X0)) + y0;  jj = NEARINT (tmp);
+	if ((ii < 0) || (ii >= dx) || (jj < 0) || (jj >= dy)) continue;
+	byte = byte_line * jj + (ii / 8);
+	bit = ii % 8;
+	flag = 0x01 & (bitmap[byte] >> bit);
+	if (flag) bDrawPointf (x + i, y + j);
+      }
+    }
+  } else {
+    for (i = X1; i <= X2; i+=scale) {
+      for (j = Y1; j <= Y2; j+=scale) {
+	tmp = rscale*(cs*(i - X0) + sn*(j - Y0)) + x0;  ii = NEARINT (tmp);
+	tmp = rscale*(cs*(j - Y0) - sn*(i - X0)) + y0;  jj = NEARINT (tmp);
+	if ((ii < 0) || (ii >= dx) || (jj < 0) || (jj >= dy)) continue;
+	byte = byte_line * jj + (ii / 8);
+	bit = ii % 8;
+	flag = 0x01 & (bitmap[byte] >> bit);
+	if (flag) bDrawPointf (x + i, y + j);
+      }
+    }
+  }
+  bDrawSetStyle (black, 0, 0);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+lib
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/Makefile	(revision 22322)
@@ -0,0 +1,62 @@
+default: install
+help:
+	@echo "make options: install libohana clean dist"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/libohana
+BIN	= 	$(HOME)/bin
+LIB	= 	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	=	$(HOME)/include
+TESTDIR =       $(HOME)/test
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+TFLAGS        = $(FULL_CFLAGS) $(FULL_LDFLAGS) -ltap_ohana
+
+install: $(DESTLIB)/libohana.a $(DESTLIB)/libohana.$(DLLTYPE)
+libohana: $(LIB)/libohana.$(ARCH).a $(LIB)/libohana.$(ARCH).$(DLLTYPE)
+testcode: install $(TEST)
+test:
+	make testcode
+	for i in $(TEST); do $$i; done
+
+INCS = \
+$(DESTINC)/ohana_allocate.h \
+$(DESTINC)/ohana_sort.h \
+$(DESTINC)/ohana.h
+
+OBJS = \
+$(SRC)/ohana_allocate.$(ARCH).o  \
+$(SRC)/sorts.$(ARCH).o		 \
+$(SRC)/string.$(ARCH).o		 \
+$(SRC)/findexec.$(ARCH).o	 \
+$(SRC)/glockfile.$(ARCH).o	 \
+$(SRC)/time.$(ARCH).o		 \
+$(SRC)/gaussj.$(ARCH).o		 \
+$(SRC)/config.$(ARCH).o		 \
+$(SRC)/Fread.$(ARCH).o		 \
+$(SRC)/IOBufferOps.$(ARCH).o	 \
+$(SRC)/CommOps.$(ARCH).o	 \
+$(SRC)/version.$(ARCH).o
+
+TEST = \
+$(TESTDIR)/string.$(ARCH)
+
+$(OBJS): $(INCS)
+
+$(LIB)/libohana.$(ARCH).a: $(OBJS)
+$(LIB)/libohana.$(ARCH).$(DLLTYPE): $(OBJS)
+
+$(DESTLIB)/libohana.a:  $(LIB)/libohana.$(ARCH).a
+$(DESTLIB)/libohana.$(DLLTYPE): $(LIB)/libohana.$(ARCH).$(DLLTYPE)
+
+$(TESTDIR)/%.$(ARCH) : $(TESTDIR)/%.c
+	$(CC) $^ -o $@ $(TFLAGS)
+
+.PHONY: test
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,66 @@
+
+- libohana 1.8.1 : 2006.10.04
+  * added tap-based tests
+  * added strsubs functions
+
+- libohana 1.8 : 2006.08.23
+  * cleanups in ohana.h
+  * moved gaussj to libohana
+  * added PrintIOBuffer functions
+  * added check_dir_access 
+  * added ohana_lst to time.c
+
+- libohana 1.7
+  * added 'mode' to mkdirhier
+  * moved _check_permissions to check_file_exec
+
+- libohana 1.6
+  * added check_file_access (check parent dirs and permissions)
+  * cleaned up signed/unsigned inconsistencies
+  * allow date_to_set to handle enclosing quotes
+
+libohana-1-5
+  moved dvo functions to libdvo
+  made libfits depend on libohana
+  added IOBuffer and Comm functions to libohana
+  added time functions from opihi and others
+  moved Fseek into findexec.c
+
+2005.10.07
+
+	I was having some memory collision problems, and attempting to
+	use the ohana_allocate functions reminded me that the libFITS
+	functions were not supported under ohana_allocate.  This was
+	unhelpful.  I bit the bullet and split libohana into libohana
+	(base functions only, including ohana_allocate) and libdvo
+	(functions based on the libautocode structures).  Doing this
+	allowed me to make libFITS depend on libohana (including
+	ohana_allocate).  BUT, this forced me to change all LDFLAGS
+	entries in ohana to swap -lohana -lFITS for -lFITS -lohana,
+	and to add include <fitsio.h> in some cases.
+
+libohana-1-4
+  further cleanup of the autocode system
+  eliminated the elixir.h, panstarrs.h, loneos.h files:
+   these are now completely absorbed into libautocode
+  minor improvements to ohana_allocate
+
+libohana-1-3
+  changed meaning of PLY / WRP / DIS (see coordtrans.txt)
+  added check on ALLOCATE/REALLOCATE to avoid failures on Nvalues < 1
+  added ohana_allocate system (memory tracking)
+  added autocode elements (refers to libautocode)
+  cleanup glockfile APIs
+
+libohana-1-2
+  added 'STG' to allowed coordinate transforms (inaccurate & stop-gap)
+  set default Npolyterms to 1
+  initialized filemode in glockfile.c
+
+libohana-1-1
+  dropped the configuration source ./.(name)rc : was just confusing
+  in glockfile : made closing a NULL file OK
+  added imreg_datatypes (part of imregister-1-1 datatype reorganization)
+
+libohana-1-0
+  import to CVS
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/VERSIONS
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/VERSIONS	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/VERSIONS	(revision 22322)
@@ -0,0 +1,5 @@
+
+tag names used by libohana:
+
+TAG         : Comment
+libohana-1-0 : first version under CVS
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/autocode.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/autocode.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/autocode.txt	(revision 22322)
@@ -0,0 +1,71 @@
+
+This directory contains a collection of autocoded FITS I/O routines
+used to define FITS Table DB functions.  
+
+The autocoder program is a perl script 'generate'.  This scripts takes
+as input a schema file and a template source code file, and produces
+an output file from these two inputs.  A single schema file is meant
+to be associated with a single data concept which is being coded.  For
+example, in the case of the FITS DB tables, a single schema file
+defines a single FITS table, or equivalently, a single C structure
+representing a row in that FITS table.
+
+Schema File
+
+The schema file currently consists of two types of information.
+
+First, there are simple keyword / name pairs which the autocoder
+simply applies as a direct replacement anywhere in the template.  For
+example, the keyword NAME replaces any instance of "$NAME" in the
+template with the corresponding value.  
+
+Second, there is a special type of keyword: FIELD.  The FIELD entry
+defines an entry in the data structure.  The FIELD entry is followed
+by the following comma-separated pieces of information:
+
+- the element name
+- the element data type
+- the element description
+- the element physical unit
+
+Template File
+
+The template file is a source code in whatever language is desired.
+Within the template, the defined keywords may be used as desired,
+wherever needed.  In addition, there are special directives which
+invoke additional special behavior.  All special directives have the
+form of comments within their target language.  They are replaced with
+a block of code in that target language, normally one which iterates
+over the FIELD elements of the schema file.  As such, the directive
+appropriate for one language should not be used within code for a
+different language.  Here is a list of existing special directives:
+
+/** STRUCT DEFINITION **/
+
+This tells the autocoder to create a structure definition for the
+schema.  The structure definition creates a structure with elements
+based on the FIELD entries, with the name $NAME (NOTE: this is perhaps
+making too much of an assumption.  we could require the template to
+provide the wrapper: "typedef struct { } $NAME;" and have the
+autocoder only insert the field lines).  
+
+/** TABLE DEFINITION **/
+
+This tells the autocoder to output the lines which add a column to an
+exiting empty table.  The assumption is that the template contains
+code to initialize a table.  It then invokes the table definition
+code, which defines each of the appropriate columns.  This is perhaps
+followed by some code which finalizes the table definition.  (Note:
+the code output by the autocoder in this block is not very flexible:
+it the current example, it explicitly uses the Elixir FITS Table
+functions, and refers to some data elements in the template code.  The
+latter could possibly be more abstracted with the keyword / value
+pairs, but in any case, the autocoded lines will have to be
+constructed somewhat by hand for a specific API set.
+
+/** BYTE SWAP **/
+
+This tells the autocoder to output code defining the byte swaps
+appropriat to the data structure.  Again, the resulting code depends a
+bit on the intended use and API set.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/coords_minimal.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/coords_minimal.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/coords_minimal.c	(revision 22322)
@@ -0,0 +1,115 @@
+/* 
+here is some C code to project from R,D to x,y (all in decimal
+degrees) in a SIN projection, with Ro, Do as projection center:
+*/
+
+# define FULLPROJECTION 0
+# define DEG_RAD 57.295779513082322
+# define RAD_DEG  0.017453292519943
+
+RD_to_XY (double *x, double *y, double R, double D, double Ro, double Do) {
+
+  double sdp, cdp, salp, calp, sdel, cdel, stht, sphi, cphi;
+                 
+  sdp  = sin(RAD_DEG*Do);
+  cdp  = cos(RAD_DEG*Do);
+  salp = sin(RAD_DEG*(ra - Ro));
+  calp = cos(RAD_DEG*(ra - Ro));
+  sdel = sin(RAD_DEG*dec);
+  cdel = cos(RAD_DEG*dec);
+  
+  stht = sdel*sdp + cdel*cdp*calp;    /* sin(theta) */
+  sphi = cdel*salp;                   /* = cos(theta)*sin(phi) */
+  cphi = cdel*sdp*calp - sdel*cdp;    /* = cos(theta)*cos(phi) */
+  if (stht < 0) { return 0; /* projection from the wrong side of the sphere */ }
+  
+  X =  DEG_RAD * sphi;
+  Y = -DEG_RAD * cphi;
+  
+# if (FULLPROJECTION) 
+  
+  /* 
+     these lines allow for a rotation / distortion 2x2 matrix (pci_j),
+     a (two direction) plate-scale shift (cdelt1, cdelt2), 
+     and a reference center offset of Xo, Yo, if desired. 
+  */
+
+  tmp_d = 1.0 / (pc_1_1*pc_2_2 - pc_1_2*pc_2_1); 
+  *x = tmp_d * (pc_2_2*X - pc_1_2*Y) / cdelt1 + Xo;
+  *y = tmp_d * (pc_1_1*Y - pc_2_1*X) / cdelt2 + Yo;
+
+# else
+
+  *x = X;
+  *y = Y;
+  
+# endif
+
+
+  return (1);
+
+}
+
+
+/* 
+here is some C code to project from x,y to R,D (all in decimal
+degrees) in a SIN projection, with Ro, Do as projection center:
+*/
+
+XY_to_RD (double *ra, double *dec, double x, double y, double Ro, double Do) {
+
+
+  double L, M, X, Y, T, Z;
+  double R, sphi, cphi, stht, ctht;
+  double alpha, delta, salp, calp, sdel, sdp, cdp;
+  
+  *ra  = 0;
+  *dec = 0;
+  stht = ctht = 1;
+
+  
+# if (FULLPROJECTION) 
+  /* 
+     these lines allow for a rotation / distortion 2x2 matrix (pci_j),
+     a (two direction) plate-scale shift (cdelt1, cdelt2), 
+     and a reference center offset of Xo, Yo, if desired. 
+  */
+  X = cdelt1 * (x - Xo);
+  Y = cdelt2 * (y - Yo);
+
+  L = (X*pc1_1 + Y*pc1_2);
+  M = (X*pc2_1 + Y*pc2_2);
+# else 
+  L = x;
+  M = y;
+# endif
+
+  R = hypot (L,M);
+  if ((L == 0) && (M == 0)) {
+    sphi = 0;
+    cphi = 1;
+  }
+  else {
+    sphi =  L / R;
+    cphi = -M / R;
+  }
+
+  ctht = RAD_DEG * R;
+  stht = sqrt (1 - ctht*ctht);
+
+  sdp  = sin(RAD_DEG*Do);
+  cdp  = cos(RAD_DEG*Do);
+  
+  sdel = stht*sdp - ctht*cphi*cdp;
+  salp = ctht*sphi;
+  calp = stht*cdp + ctht*cphi*sdp;
+  alpha = atan2 (salp, calp);
+  delta = asin (sdel);
+  
+  *ra  = DEG_RAD*alpha + Ro;
+  *dec = DEG_RAD*delta;
+  
+  return (1);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/coordtrans.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/coordtrans.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/coordtrans.txt	(revision 22322)
@@ -0,0 +1,70 @@
+
+I have changed the meaning of coord type PLY from the historical
+meaning.  The only previous use that is in a DVO-style database is in
+the loneos data (all cfht data needed to go to linear terms).  these
+can be easily converted by changing from PLY to DIS.
+
+There are now three types of higher-order transformation which may be
+used depending on the circumstance.  The new naming scheme is:
+
+PLY : a cartesian transformation (ie, no projection implied)
+DIS : equivalent to TAN projection with up-to-3rd order polynomial
+WRP : a second-level cartesian transformation, implying the presence
+      of a DIS coord frame (must be registered with libohana function)
+
+{ 
+
+  X = coords[0].cdelt1*(x - coords[0].crpix1);
+  Y = coords[0].cdelt2*(y - coords[0].crpix2);
+  L = (X*coords[0].pc1_1 + Y*coords[0].pc1_2);
+  M = (X*coords[0].pc2_1 + Y*coords[0].pc2_2);
+  R = hypot (L,M);
+  if ((L == 0) && (M == 0)) {
+    sphi = 0;
+    cphi = 1;
+  } else {
+    sphi =  L / R;
+    cphi = -M / R;
+  }
+  if (R == 0) {
+    stht = 1.0;
+    ctht = 0.0;
+  } else {
+    T = DEG_RAD / R;
+    stht =   T / sqrt ( 1.0 + T*T);
+    ctht = 1.0 / sqrt ( 1.0 + T*T);
+  }
+  sdp  = sin(RAD_DEG*coords[0].crval2);
+  cdp  = cos(RAD_DEG*coords[0].crval2);
+    
+  sdel = stht*sdp - ctht*cphi*cdp;
+  salp = ctht*sphi;
+  calp = stht*cdp + ctht*cphi*sdp;
+  alpha = atan2 (salp, calp);
+  delta = asin (sdel);
+    
+  *ra  = DEG_RAD*alpha + coords[0].crval1;
+  *dec = DEG_RAD*delta;
+}
+
+RD_to_XY () {
+
+  sdp  = sin(RAD_DEG*coords[0].crval2);
+  cdp  = cos(RAD_DEG*coords[0].crval2);
+  salp = sin(RAD_DEG*(ra - coords[0].crval1));
+  calp = cos(RAD_DEG*(ra - coords[0].crval1));
+  sdel = sin(RAD_DEG*dec);
+  cdel = cos(RAD_DEG*dec);
+
+  stht = sdel*sdp + cdel*cdp*calp;    /* sin(theta) */
+  sphi = cdel*salp;                   /* = cos(theta)*sin(phi) */
+  cphi = cdel*sdp*calp - sdel*cdp;    /* = cos(theta)*cos(phi) */
+  if (stht < 0) status = FALSE;
+
+  X =  DEG_RAD * sphi / stht;
+  Y = -DEG_RAD * cphi / stht;
+  tmp_d = 1.0 / (coords[0].pc1_1*coords[0].pc2_2 - coords[0].pc1_2*coords[0].pc2_1);
+  *x = tmp_d * (coords[0].pc2_2*X - coords[0].pc1_2*Y) / coords[0].cdelt1 + coords[0].crpix1;
+  *y = tmp_d * (coords[0].pc1_1*Y - coords[0].pc2_1*X) / coords[0].cdelt2 + coords[0].crpix2;
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/gaussj_bevington.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/gaussj_bevington.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/gaussj_bevington.c	(revision 22322)
@@ -0,0 +1,466 @@
+// ----------------------- invert a matrix
+double MatInv()	
+{
+	int i, j, k, L, ik[10], jk[10];
+	double aMax, save, det;
+	
+	det = 0;
+//------------------------------------  find largest element 
+	for (k = 1; k <= g_m; k++)
+	{
+		aMax = 0;
+FIND_AMAX:	
+		for (i = k; i <= g_m; i++)
+		{
+			for (j = k; j <= g_m;j++)
+			{
+				if  (fabs(alpha[i][j]) > fabs(aMax)) 
+				{
+					aMax = alpha[i][j];
+					ik[k] = i;
+					jk[k] = j;
+				}   //if
+			}  //for j
+		}  //for i
+		if (aMax == 0)  return(det);  //with 0 determinant as signal
+		det = 1;
+// -------------------------------------- interchange rows and columns to put aMax in alpha[k,k]---
+		i = ik[k];
+		if (i < k) goto FIND_AMAX;
+		else if (i > k) 
+		{
+			for (j = 1;j <= g_m;j++)
+			{
+				save = alpha[k][j];
+				alpha[k][j] = alpha[i][j];
+				alpha[i][j] = -save;
+			}	  //for j
+		}  //else if 
+		j = jk[k];
+		if	(j < k) goto FIND_AMAX;
+		else if (j > k)
+		{
+			for (i = 1; i <= g_m; i++)
+			{
+				save = alpha[i][k];
+				alpha[i][k] = alpha[i][j];
+				alpha[i][j] = -save;
+			}	 //for i
+		}//else if j
+// ---------------------------------------- accumulate elements of inverse matrix 
+		for (i = 1; i <= g_m; i++)
+		{
+			if (i != k) alpha[i][k] = -alpha[i][k]/aMax;
+		}	//for i
+		for (i = 1; i <= g_m; i++)
+		{
+			for (j = 1; j <= g_m;j++) 
+			{
+				if ((i != k) && (j != k)) alpha[i][j] = alpha[i][j] + alpha[i][k]*alpha[k][j];
+			}	//for j
+		}   //for i
+		for (j = 1; j <= g_m;j++)
+		{
+			if (j != k)  alpha[k][j] = alpha[k][j]/aMax;
+		}           //for j
+		alpha[k][k] = 1/aMax;
+		det = det * aMax;
+	} //for k
+// ------------------------------------------ restore ordering of matrix 
+	for (L = 1; L <= g_m; L++)  
+	{
+		k = g_m + 1 - L;
+		j = ik[k];
+		if (j > k) 
+		{
+			for (i = 1; i <= g_m; i++) 
+			{
+				save        = alpha[i][k];
+				alpha[i][k] = -alpha[i][j];
+				alpha[i][j] = save;
+			}  //for i
+		}  //if j
+		i = jk[k];
+		if (i > k)
+		{
+			for (j = 1; j <= g_m;j++)
+			{
+				save        =  alpha[k][j];
+				alpha[k][j] = -alpha[i][j];
+				alpha[i][j] =  save;
+			} //for j
+		} //if i
+	}  //for L
+	return(det);
+}
+
+void SquareByRow()  // multiply square matrix by row matrix
+{
+	int		i,j;
+								
+	for (i = 1; i <= g_m; i++) 
+	{
+		if(g_linearFn)
+		{
+			a[i]  = 0.0;
+			for (j= 1; j<= g_m;j++)
+				a[i] = a[i] + beta[j]*alpha[i][j];
+		}
+		else
+		{
+			da[i] = 0.0;
+			for (j= 1; j<= g_m;j++)
+				da[i] = da[i] + beta[j]*alpha[i][j];
+		}
+	}
+		
+}
+
+// ----Standard deviations calc'd from chiSq change of 1 (parabola fit at Xi2 minimum)
+void  SigParab()
+{
+	int j;
+
+	for(j = 1; j<= g_m; j++)
+	{
+		chiSq2 = CalcChiSq();
+		a[j]   = a[j] + deltaa[j];
+		chiSq3 = CalcChiSq();
+		a[j]   = a[j] - 2*deltaa[j];
+		chiSq1 = CalcChiSq();
+		a[j]   = a[j] + deltaa[j];
+		siga[j] = deltaa[j]*sqrt(2/(chiSq1-2*chiSq2+chiSq3));
+	}
+}
+// ---standard deviations as sqrt of diagonal elements of eror matrix.
+void SigMatrix()
+{
+	int j;
+	for (j = 1; j <= g_m;j++)		
+
+		siga[j] = sqrt(alpha[j][j]);
+}
+
+//-------------------------  Non-linear Fits ------------------------------------
+
+//Numerical Derivatives------------------------------
+// Can be replaced by analytic derivatives, if they can be calculated. 
+// However, numerical calculation is general, and convenient.
+double dXiSq_da(int j)		//See Eq. 8.26 - this sums over nPts
+{
+	double  static XiSq0;
+	double   XiSqPlus, dXiSqDa;
+
+	if (j == 1)   
+		XiSq0 = CalcChiSq();				//starting point-calculate it once
+	a[j] = a[j] + deltaa[j];
+	XiSqPlus  = CalcChiSq();
+	a[j] = a[j] - deltaa[j];		//restore
+	dXiSqDa   =  (XiSqPlus - XiSq0)/(deltaa[j]);
+	return (dXiSqDa);
+}
+
+double d2XiSq_da2(int j) // See Eq. 8.35  - this sums over nPts
+{
+	double tem; 
+	int i;
+
+	if (j == 1) 
+		for (i = 1; i<= g_nPts; i++) 
+			y_0[i] = yFunction(x[i]);		//Starting point-calculate it once
+	a[j] = a[j] + deltaa[j];
+	tem = 0.0;
+	for (i = 1; i <= g_nPts; i++)
+		{
+			dYda[i][j] = (yFunction(x[i]) - y_0[i])/deltaa[j]/sigY[i];
+			tem  = tem + sqr(dYda[i][j]);
+		}
+		a[j] = a[j] - deltaa[j];
+		return(2*tem);
+}
+
+double d2XiSq_dajk(int j, int k)	//See Eq. 8.35
+{
+	double tem = 0.0;
+	int i;
+	for (i = 1; i <= g_nPts; i++)
+	{
+ 		tem = tem + dYda[i][j]*dYda[i][k];
+	}
+	return(2*tem);
+}
+
+// ====================  Non-linear Fitting Routines ==================================
+//---------------------------- GridSearch ---------------------------------------------
+
+// Program 8.1:Non-linear least-squares fit by the grid-search method
+
+void Gridls(double &chiSqr)
+{
+//
+	double delta;
+	double save, delta1, del1, del2, aa, bb, cc, disc, alpha, x1, x2;
+	int j;
+
+	//cout << "enter Grids, x[1], y[1] " <<x[1] <<"  "<<y[1]<<"  ";  cin >> j;
+
+	chiSq2 = CalcChiSq();
+// -find local minimum for each parameter- 
+	for (j = 1; j <= g_m;j++)
+    {
+		delta    = deltaa[j];
+		a[j]     = a[j] + delta;
+		chiSq3   = CalcChiSq();
+		if (chiSq3 > chiSq2) 
+		{                  //started in wrong direction
+			delta  = -delta;
+			a[j]   =  a[j] + delta;
+			save   =  chiSq2;    //interchange 2 and 3 so 3 is lower
+			chiSq2 =  chiSq3;
+			chiSq3 =  save;
+		}
+// -Increment or decrement a[j] until chi squared increases- 
+		do
+		{
+			chiSq1 = chiSq2; //move back to prepare for quad fit
+			chiSq2 = chiSq3;
+			a[j]   = a[j] + delta;
+			chiSq3 = CalcChiSq();
+		}  while (chiSq3 < chiSq2);
+   
+// -Find minimum of parabola defined by last three points  -
+		del1 = chiSq2 - chiSq1;
+		del2 = chiSq3 - 2*chiSq2 + chiSq1;
+		delta1 = delta * (del1/del2 + 1.5);
+		a[j] = a[j]  - delta1;
+		chiSq2 = CalcChiSq();    //	at new local minimum
+// -Adjust delta for change of 2 from chiSq at minimum  -
+		aa = del2/2;								//chiSq = aa*sqr(a[j] + bb*a[j] + cc
+		bb = del1 - del2/2;
+		cc = chiSq1-chiSq2;
+		disc = sqr(bb) -4*aa*(cc-2);				//chiSqr difference=2
+		if (disc > 0) 								//if not true, then probably not parabolic yet
+		{
+			disc = sqrt(disc) ;
+			alpha = (-bb - disc)/(2*aa);
+			x1 = alpha*delta +  a[1] - 2*delta;		//	a[j] at chiSq minimum+2
+			disc = sqr(bb) - 4*aa*cc;
+			if (disc > 0) 
+				disc=sqrt(disc); 
+			else 
+				disc=0;		// elim round err
+			alpha = (-bb - disc)/(2*aa);
+			x2 = alpha*delta + a[1] - 2*delta;		// at chiSq minimum
+			delta = x1 - x2;
+			deltaa[j] = delta;
+		}
+	}    // for j = 1 to m}
+  chiSqr = chiSq2;
+}
+
+//---------------------------- GradSearch ---------------------------------------------
+//Program 8.2 Non-linear least-squares fit by gradient-search method}
+
+	double	grad[10];
+void Gradls(double &chiSqr, double stepDown)
+// label  5;
+{
+	double stepSum,step1;
+	double fract = 0.001;
+	int   j;
+
+	CalcGrad();   //calculate the gradient
+//-Evaluate chiSqr at new point and make sure chiSqr decreases-
+	do
+	{
+		for (j = 1; j <= g_m;j++)
+			a[j] = a[j] + stepDown * grad[j]; //slide down
+		chiSq3 = CalcChiSq();
+		if (chiSq3 >= chiSq2)
+		{                         //must have overshot minimum
+			for (j = 1; j <= g_m;j++)
+				a[j] = a[j] - stepDown * grad[j]; //restore
+			stepDown = stepDown/2;              //decrease stepSize
+		}
+	} while (chiSq3 > chiSq2);
+	stepSum = 0;
+// -- Increment parameters until chiSqr starts to increase -- 
+	do
+	{
+		stepSum = stepSum + stepDown;   //counts total increment
+		chiSq1 = chiSq2;
+		chiSq2 = chiSq3;
+		for (j = 1; j <= g_m;j++) 
+			a[j] = a[j] + stepDown * grad[j];
+		chiSq3 = CalcChiSq();
+	} while (chiSq3 <= chiSq2);
+// -- Find minimum of parabola defined by last three points -- 
+	step1=stepDown*((chiSq3-chiSq2)/(chiSq1-2*chiSq2+chiSq3)+0.5);
+	for (j = 1; j <= g_m;j++)
+		a[j] = a[j] - step1 * grad[j];    //move to minimum
+	chiSqr = CalcChiSq();
+	stepDown = stepSum;                 //start with this next time
+}
+
+void CalcGrad()
+{
+	double fract = 0.001;
+	int   j;
+	double  dA, sum;
+
+	sum = 0.0;
+	chiSq2 = CalcChiSq();
+
+	for (j = 1; j <=  g_m;j++)
+  {
+		dA  = fract * deltaa[j];     //differential element for gradent
+		a[j]    = a[j] + dA;
+		chiSq1  = CalcChiSq();
+		a[j]    = a[j] - dA;
+		grad[j] = chiSq2 - chiSq1;   //2*da*grad
+		sum     = sum + sqr(grad[j]);
+  }
+	for (j = 1; j <= g_m;j++)
+		grad[j] =  deltaa[j]*grad[j]/sqrt(sum); //step * grad
+}
+
+//----------------------------------- Expand Function -------------------------------------
+//Program  8.3: Non-linear least-squares fit by expansion of the fitting function
+ 
+void ChiFit(double &chiSqr)			// double  det, chiSq1;
+{
+	int  j;
+	double det;
+
+	MakeBeta();
+	MakeAlpha();
+	det = MatInv();					// Invert matrix
+	SquareByRow();				// Evalulate parameter increments
+	for (j = 1; j <= g_m;j++)
+		a[j] = a[j] + da[j];	// Increment to next solution. 
+	chiSqr= CalcChiSq();
+	return;
+}
+
+// ------------------------------------- Marquard ---------------------------------------------
+//Program 8.4: Non-linear least-squares fit by the gradient-expansion (Marquardt) method
+
+void Marquardt(double &chiSqr, double chiCut, double lambda)
+{
+	int  j;
+	double  det, chiSq1;
+
+TOP:
+	MakeBeta();
+	MakeAlpha();
+	for (j = 1; j <= g_m;j++) 
+		alpha[j][j] = (1 + lambda) * alpha[j][j];
+	det = MatInv();					// Invert matrix 
+	if (lambda > 0)					//On final call, enter with lambda = 0 to get the error matrix
+	{
+		SquareByRow();//Evaluate parameter increments 
+		chiSq1 = chiSqr;
+		for (j = 1; j <= g_m; j++)
+			a[j] = a[j] + da[j];	//Incr to next solution
+		chiSqr = CalcChiSq();
+		if (chiSqr > (chiSq1 + chiCut) )
+		{
+			for (j = 1; j <= g_m; j++)
+				a[j]=a[j]-da[j];	//Return to prev solution
+			chiSqr = CalcChiSq();
+			lambda = 10*lambda;		//and repeat the calc, with larger lambda
+			goto TOP;
+		}
+		lambda = 0.1 * lambda;
+	}
+}
+
+// --------------------------------  Output to Disk File -------------------------------------------
+
+void FitOut(char outFile[], double chi2, char aErrorsFrom)
+{
+	int i, k, j ,nFree;
+	double X2Prob, chi2PerDof;
+
+	ofstream fOut(outFile);			// open file for output 
+	fOut << setiosflags(ios::fixed |ios::showpoint);
+	
+	nFree = g_nPts-g_m;
+	chi2PerDof = chi2/nFree;
+	X2Prob = 100*ChiProb(nFree, chi2);
+	switch(aErrorsFrom)
+	{
+		case 'D':			// already calculated in LineFit
+			;			
+		break;
+		case 'C':			// vary chi^2 by 1
+			SigParab();		
+		break;
+		case 'M':			// error Matrix
+			SigMatrix();
+		break;
+	}
+
+	fOut << title <<endl; 
+	fOut ;
+	fOut 
+		<< " ChiSqr =" 
+		<< setprecision(1)<<chi2 
+		<< " for " 
+		<< nFree 
+		<<" deg of freedom," 
+		<<" chiSqr/dof =" 
+		<< setprecision(1)<<chi2PerDof 
+		<< " Prob =" 
+		<< setprecision(1)<< X2Prob
+		<<"%"
+		<< endl;
+	fOut 
+		<< " Fitted Parameters:  a[i] +- sig-a[i]"
+		<< endl;
+	for (i = 1; i <= g_m; i++)  	
+		fOut 
+			<<setprecision(4)<<setw(10) << a[i]
+			<<"  +-  "
+			<<setw(10)<< siga[i]
+			<< "  " 
+			<< endl;
+	fOut << endl; 	
+
+	if (!(aErrorsFrom == 'M'))
+		cout <<  endl <<" Output written to disk file " << outFile << endl;
+	else
+	{
+		fOut << " Error Matrix" << endl;
+		for (k = 1; k <= g_m; k++)
+		{
+			for (j = 1; j <= g_m;j++)
+			{ 
+				fOut 
+					<<"    "
+					<< setprecision(8) <<setw(12) 
+					<< alpha[k][j];
+			}
+			fOut << endl;
+		}
+		fOut << endl;
+		cout << endl <<" Output written to disk file " << outFile << endl;
+	}
+
+// Tabulate data with fitted Y
+	fOut << "      pt #             X(cm)      Y            dY         yCalc " << endl;
+	for (i = 1; i <= g_nPts; i++)  
+	{
+			fOut 
+			<< setiosflags( ios::right )
+			<< setprecision(1)<<setw(10)<< i 
+			<< setprecision(4)
+			<< setw(16)<< x[i] 
+			<< setw(12)<< y[i]
+		    << setw(12)<< sigY[i]
+			<< setw(12)<< yCalc[i] 
+			<< endl;
+	}
+	fOut.close();
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/glockfile.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/glockfile.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/doc/glockfile.txt	(revision 22322)
@@ -0,0 +1,55 @@
+
+FILE *fsetlockfile (char *filename, double timeout, int type, int *state);
+   
+ set a lockfile on [path]/filename.  
+
+ we allow three types of locks: 
+ LCK_HARD - exclusive lock with persistent lock file
+ LCK_XCLD - exclusive lock, no persistent lock file
+ LCK_SOFT - shared lock
+
+ all three types of locks set a filesystem lock (using NFS lockd) on
+ the given file, using an exclusive lock for HARD and XCLD and a
+ shared lock for SOFT locks.  
+
+ a HARD lock also uses a lockfile to make the lock persistent in the
+ event the calling program crashes: the lockfile must be actively
+ removed and will not vanish even if the locking program dies
+
+ SOFT and XCLD locks will vanish if the locking program dies.
+
+ if a HARD or XCLD lock is set, HARD, XCLD & SOFT locks will block
+
+ if a SOFT lock is set, HARD and XCLD locks will block, but not a SOFT
+ lock
+
+ the hard lockfile uses the file [path]/.filename.lck
+
+ this function locks and opens the file, returning the file pointer to
+ the now opened file.  the file is either opened for reading and
+ writing (HARD, XCLD == r+) or just reading (SOFT).
+
+ Error Conditions:
+
+ on error, fsetlockfile closes the file, closes the lockfile, and
+ returns NULL.  In come cases, an empty file may be left behind
+ (LCK_TIMEOUT, LCK_HARDOPEN, LCK_HARDLOCK, LCK_HARDLOCKHARD,
+ LCK_HARDLOCKCLOSE)
+
+ - invalid type (LCK_INVALID)
+   * no file created, no lock file created : OK
+ - SOFT and file does not exist (LCK_EMPTY)
+   * no file created, no lock file created : OK 
+ - file not accessible, cannot be opened (LCK_ACCESS)
+   * no file created, no lock file created : OK
+ - FS lock timed out (LCK_TIMEOUT)
+   * new file is created, file is open, file is unlocked, lock file is not created : OK
+ - lockfile not accessible (LCK_HARDOPEN)
+   * new file is created, file is open, file is locked, lock file is not created : OK
+ - cannot lock lockfile, held by another user (LCK_HARDLOCK)
+   * new file is created, file is open, file is locked, lock file is created : OK
+ - lockfile says 'BUSY', previous holder crashed (LCK_HARDLOCKHARD)
+   * new file is created, file is open, file is locked, lock file exists, lock file locked : OK
+ - error writing 'BUSY' to lockfile (LCK_HARDCLOSE)
+   * new file is created, file is open, file is locked, lock file exists, lock file locked
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/Xohana.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/Xohana.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/Xohana.h	(revision 22322)
@@ -0,0 +1,8 @@
+# include <ohana.h>
+# include <X11/Xatom.h>
+# include <X11/Xlib.h>
+# include <X11/Xresource.h>
+# include <X11/Xutil.h>
+# include <X11/cursorfont.h>
+# include <X11/keysym.h>
+# include <X11/keysymdef.h>
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/convert.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/convert.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/convert.h	(revision 22322)
@@ -0,0 +1,3 @@
+Measure *FixOldMeasure (OldMeasure *in, int Nvalues);
+Average *FixOldAverage (OldAverage *in, int Nvalues);
+SecFilt *FixOldSecFilt (OldSecFilt *in, int Nvalues);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/ohana.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/ohana.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/ohana.h	(revision 22322)
@@ -0,0 +1,243 @@
+# include <stdio.h>
+# include <fcntl.h>
+# include <math.h>
+# include <float.h>
+# include <errno.h>
+# include <time.h>
+# include <stdlib.h>
+# include <string.h>
+# include <sys/socket.h>
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <unistd.h>
+# include <stdarg.h> 
+# include <readline/history.h>
+# include <readline/readline.h>
+
+// XXX I was including these before, but RHL claims they are not needed
+// # include <malloc.h>
+// # include <memory.h>
+
+/* OHANA included stuff */
+# ifndef OHANA_H
+# define OHANA_H
+
+# ifndef TRUE
+# define TRUE (1)
+# endif
+
+# ifndef FALSE
+# define FALSE (0)
+# endif 
+
+// XXX these should probably use safe name-spaces (eg, OHANA_MIN)
+# ifndef SIGN
+# define SIGN(X) (((X) == 0) ? 0 : ((fabs((double)(X))) / (X)))
+# endif
+
+# ifndef ROUND
+# define ROUND(X) ((int) ((X) + 0.5*SIGN(X)))
+# endif
+
+# ifndef SQR
+# define SQR(X)   (double) (((double)(X))*((double)(X)))
+# endif
+
+# ifndef SQ
+# define SQ(X)    (double) (((double)(X))*((double)(X)))
+# endif
+
+# ifndef MIN
+# define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+# endif
+
+# ifndef MAX
+# define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+# endif
+
+# ifndef SWAP
+# define SWAP(X,Y) {double tmp=(X); (X) = (Y); (Y) = tmp;}
+# endif
+
+# ifndef DTIME
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+# endif
+
+enum {
+ LCK_UNLOCK,        /* file is unlocked */
+ LCK_INVALID,       /* invalid locktype requested */
+ LCK_MISSING,       /* soft requested and file missing (error) */
+ LCK_ACCESS,        /* can't get access to file */
+ LCK_TIMEOUT,       /* timeout setting lock */
+ LCK_HARDOPEN,      /* cannot open hard lockfile */
+ LCK_HARDLOCK,      /* cannot lock hard lockfile */
+ LCK_HARDLOCKHARD,  /* hard lockfile is locked */
+ LCK_HARDCLOSE,     /* cannot write to hardlock */
+
+ LCK_EMPTY,         /* file is locked and empty */
+ LCK_FULL,          /* file is locked and not empty */
+ LCK_UNKNOWN,       /* file is locked, but can't get size */
+
+ LCK_SOFT,          /* soft lock */
+ LCK_HARD,          /* hard lock */
+ LCK_XCLD,          /* exclusive soft lock */
+};
+
+# ifndef M_PI
+# define M_PI 3.14159265358979323846264
+# endif
+
+# define DEG_RAD 57.295779513082322
+# define RAD_DEG  0.017453292519943
+
+# ifndef PROTO
+#   define PROTO(A) A
+# endif
+
+/* 
+# else
+#   ifndef PROTO
+#   define PROTO(A) ()
+#   endif
+# include <varargs.h>
+# endif
+*/
+
+/* ohana_allocate.h provides ohana memory tools.  to use them
+   you must # define OHANA_MEMORY before including ohana.h */
+# include <ohana_allocate.h>
+
+# ifndef FOPEN 
+# define FOPEN(F,NAME) \
+  F = fopen (NAME, "r"); \
+  if (F == NULL) { \
+    fprintf (stderr, "failed to open %s\n", NAME); \
+    exit (0); \
+  }
+# endif /* FOPEN */
+
+/*
+  isspace is c99 : do we require c99 now? 
+  isspace()
+   checks  for white-space characters.  In the "C" and "POSIX" locales, these are: space, form-feed ('\f'), newline ('\n'),
+   carriage return ('\r'), horizontal tab ('\t'), and vertical tab ('\v').
+   horiz. tab: 0x09, vert. tab: 0x0b, newline: 0x0a, form-feed: 0x0c, return: 0x0d, space: 0x20, 
+*/
+# define OHANA_WHITESPACE(c)(((c) == 0x09) || ((c) == 0x0a) || ((c) == 0x0b) || ((c) == 0x0b) || ((c) == 0x0c) || ((c) == 0x0d) || ((c) == 0x20))
+
+// sorting is now defined as a macro call
+# include <ohana_sort.h>
+
+/* socket / pipe communication buffer */
+typedef struct {
+  char *buffer;
+  int   Nalloc;
+  int   Nreset;
+  int   Nblock;
+  int   Nbuffer;
+} IOBuffer;
+
+extern double hypot();
+
+/* in string.c */
+int     stripwhite             PROTO((char *string));
+int     strnumcmp              PROTO((char *str1, char *str2));
+char   *strcreate              PROTO((char *string));
+char   *strncreate             PROTO((char *string, int n));
+int     scan_line              PROTO((FILE *f, char *line)); 
+int     dparse                 PROTO((double *X, int NX, char *line));
+int     fparse                 PROTO((float *X, int NX, char *line));
+int     get_argument           PROTO((int argc, char **argv, char *arg));
+int     remove_argument        PROTO((int N, int *argc, char **argv));
+void    uppercase              PROTO((char *string));
+char   *strip_version          PROTO((char *input));
+char   *strsubs                PROTO((char *string, char *match, char *with));
+
+/* in findexec.c */
+char   *pathname               PROTO((char *name));
+char   *filebasename           PROTO((char *name));
+char   *filerootname           PROTO((char *name));
+char   *fileextname            PROTO((char *file));
+char   *findexec               PROTO((int argc, char **argv));
+int     mkdirhier              PROTO((char *path, int mode));
+void    make_backup            PROTO((char *filename));
+int     check_file_access      PROTO((char *basefile, int backup, int verbose));
+int     check_dir_access       PROTO((char *path, int verbose));
+int     check_file_exec        PROTO((char *filename));
+
+/* in glockfile.c */
+FILE   *fsetlockfile           PROTO((char *filename, double timeout, int type, int *state));
+int     fclearlockfile         PROTO((char *filename, FILE *f, int type, int *state));
+
+/* in config.c */
+char   *SelectConfigFile       PROTO((int *argc, char **argv, char *progname));
+char   *LoadConfigFile         PROTO((char *filename));
+char   *ScanConfig             PROTO((char *config, char *field, char *mode, int N,...));
+char   *expandline             PROTO((char *line, char *config));
+char   *fileextname            PROTO((char *file));
+char   *LoadRawConfigFile      PROTO((char *, int));
+
+/* others */
+int     Fseek                  PROTO((FILE *f, long offset, int whence));
+char   *ohana_version          PROTO(());
+
+int     dgaussjordan           PROTO((double **A, double **B, int N, int M));
+int     fgaussjordan           PROTO((float **A, float **B, int n, int m));
+
+/* in time.c */
+enum {TIME_NONE, TIME_DATE, TIME_DAYS, TIME_HOURS, TIME_MINUTES, TIME_SECONDS, TIME_JD, TIME_MJD};
+
+int     ohana_chk_time         PROTO((char *line));
+time_t  ohana_date_to_sec      PROTO((char *date));
+int     ohana_dms_to_ddd       PROTO((double *Value, char *string));
+time_t  ohana_jd_to_sec        PROTO((double jd));
+time_t  ohana_mjd_to_sec       PROTO((double mjd));
+char   *ohana_sec_to_date      PROTO((time_t second));
+double  ohana_sec_to_jd        PROTO((time_t second));
+int     ohana_str_to_dtime     PROTO((char *line, double *second));
+int     ohana_str_to_time      PROTO((char *line, time_t *second));
+double  ohana_sec_to_mjd       PROTO((time_t second));
+double  ohana_lst              PROTO((double jd, double longitude));
+int     ohana_str_to_radec     PROTO((double *ra, double *dec, char *str1, char *str2));
+
+int     hstgsc_hms_to_deg      PROTO((double *h0, double *h1, double *d0, double *d1, char *string));
+
+/* IO Buffer functions */
+int     InitIOBuffer   	       PROTO((IOBuffer *buffer, int Nalloc));
+int 	FlushIOBuffer  	       PROTO((IOBuffer *buffer));
+int 	ReadtoIOBuffer 	       PROTO((IOBuffer *buffer, int fd));
+int 	EmptyIOBuffer  	       PROTO((IOBuffer *buffer, int Nmax, int fd));
+void    FreeIOBuffer   	       PROTO((IOBuffer *buffer));
+int 	PrintIOBuffer  	       PROTO((IOBuffer *buffer, char *format, ...));
+int 	vPrintIOBuffer 	       PROTO((IOBuffer *buffer, char *format, va_list argp));
+
+/* communication functions */
+int 	ExpectMessage 	       PROTO((int device, double timeout, IOBuffer *message));
+int 	ExpectCommand 	       PROTO((int device, int length, double timeout, IOBuffer *buffer));
+int 	SendMessage   	       PROTO((int device, char *format, ...));
+int 	SendMessageFixed       PROTO((int device, int length, char *messge));
+int 	SendCommand   	       PROTO((int device, int length, char *format, ...));
+int 	SendCommandV  	       PROTO((int device, int length, char *format, va_list argp));
+
+char   *CheckForMessage        PROTO((IOBuffer *buffer));
+
+/*
+#   define F_SETFL         4   
+#   define O_NONBLOCK      0200000  
+#   define AF_UNIX         1          
+#   define SOCK_STREAM     1
+*/
+
+/*
+# ifndef ANSI
+# include <varargs.h>
+# else
+# include <stdarg.h> 
+# include <cfuncs.h> 
+# endif
+*/
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/ohana_allocate.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/ohana_allocate.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/ohana_allocate.h	(revision 22322)
@@ -0,0 +1,55 @@
+# ifndef OHANA_ALLOCATE
+# define OHANA_ALLOCATE
+
+/* default is to use basic system memory functions */
+# ifdef OHANA_MEMORY
+
+void *ohana_malloc (char *file, int line, int Nelem, size_t esize);
+void *ohana_realloc (char *file, int line, void *in, int Nelem, size_t esize);
+void  ohana_free (char *file, int line, void *in);
+void  ohana_memregister_func (char *file, int line, void *ptr);
+void  ohana_memdump_func (int mode);
+void  ohana_memcheck_func (int mode);
+
+# define ohana_memregister(X) ohana_memregister_func (__FILE__, __LINE__, (X));
+# define ohana_memcheck(X) ohana_memcheck_func (X);
+# define ohana_memdump(X) ohana_memdump_func (X);
+
+# define ALLOCATE(PTR,TYPE,SIZE) \
+  PTR = (TYPE *) ohana_malloc (__FILE__, __LINE__, (SIZE), sizeof(TYPE))
+# define REALLOCATE(PTR,TYPE,SIZE) \
+  PTR = (TYPE *) ohana_realloc(__FILE__, __LINE__, PTR, (SIZE), sizeof(TYPE));
+# define CHECK_REALLOCATE(PTR,TYPE,SIZE,NCURR,DELTA) \
+  if ((NCURR) >= (SIZE)) { SIZE += DELTA; \
+  PTR = (TYPE *) ohana_realloc(__FILE__, __LINE__, PTR, (SIZE), sizeof(TYPE)); }
+# define FREE(PTR) if (PTR != NULL) { ohana_free (__FILE__, __LINE__, PTR); }
+# define free(PTR) ohana_free(__FILE__, __LINE__, PTR)
+
+# else 
+# define ohana_memregister(X) /* NOP */
+# define ohana_memcheck(X) /* NOP */
+# define ohana_memdump(X) /* NOP */
+# endif /* OHANA_MEMORY */
+
+# ifndef ALLOCATE
+# define ALLOCATE(PTR,TYPE,SIZE)  \
+  PTR = (TYPE *) malloc ((unsigned)(MAX(((SIZE)*((int)sizeof(TYPE))),1))); \
+  if (PTR == NULL) { \
+    fprintf(stderr,"failed malloc at %d in %s\n", __LINE__, __FILE__);\
+    exit (10); } 
+# define REALLOCATE(PTR,TYPE,SIZE) \
+  PTR = (TYPE *) realloc(PTR,(unsigned)(MAX(((SIZE)*((int)sizeof(TYPE))),1))); \
+  if (PTR == NULL) { \
+    fprintf(stderr,"failed realloc at %d in %s\n", __LINE__, __FILE__);\
+    exit (10); }
+# define CHECK_REALLOCATE(PTR,TYPE,SIZE,NCURR,DELTA) \
+  if ((NCURR) >= (SIZE)) { \
+    SIZE += DELTA; \
+    PTR = (TYPE *) realloc(PTR,(unsigned)(MAX(((SIZE)*((int)sizeof(TYPE))),1))); \
+    if (PTR == NULL) { \
+      fprintf(stderr,"failed realloc increment at %d in %s\n", __LINE__, __FILE__);\
+      exit (10); } }
+# define FREE(PTR) if (PTR != NULL) { free (PTR); }
+# endif /* ALLOCATE */
+
+# endif /* OHANA_ALLOCATE */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/ohana_sort.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/ohana_sort.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/include/ohana_sort.h	(revision 22322)
@@ -0,0 +1,75 @@
+// Heap sort based on descriptions in Sedgewick "Algorithms in C"
+//
+// Copyright (C) 1999  Thomas Walter
+// Copyright (C) 2007  Paul Price, Institute for Astronomy, University of Hawaii
+// Copyright (C) 2008  Eugene Magnier, Institute for Astronomy, University of Hawaii
+//
+// 18 February 2000: Modified for GSL by Brian Gough
+// 29 November 2007: Modified for psLib by Paul Price
+// 07 January 2008: Modified for ohana by Eugene Magnier
+//
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option) any
+// later version.
+//
+// This source is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+
+#ifndef OHANA_SORT_H
+#define OHANA_SORT_H
+
+// The following sort code is based on gsl_heapsort from GSL-1.8 (www.gnu.org/software/gsl), file
+// gsl-1.8/sort/sort.c, which is distributed under the GNU General Public License, version 2.
+
+// Use of the PSSORT() macro requires additional macros defined:
+// COMPAREEXPR(A,B): expression to compare element A with element B; true if element A is smaller than B.
+// SWAPFUNC(A,B): swap element A with element B
+
+# define OHANA_DOWNHEAP(COMPARE, SWAPFUNC, K) { \
+  unsigned long k = K; /* Local version of i in main loop */ \
+  while (k <= last / 2) {  \
+    unsigned long j = 2 * k;  \
+    if ((j < last) && COMPARE(j, j + 1)) {  \
+      j++;  \
+    }  \
+    if (COMPARE(k, j)) {  \
+      SWAPFUNC(j, k);  \
+    } else {  \
+      break;  \
+    }  \
+    k = j;  \
+  } \
+}
+
+# define OHANA_SORT(NVALUE, COMPARE, SWAPFUNC) { \
+  unsigned long last = NVALUE - 1; \
+  unsigned long i = last / 2 + 1; \
+  if (NVALUE > 1) { \
+    do { \
+      i--;  \
+      OHANA_DOWNHEAP (COMPARE, SWAPFUNC, i); \
+    } while (i > 0); \
+    while (last > 0) { \
+      SWAPFUNC(0, last); /* Swap elements */ \
+      /* Process the heap */ \
+      last--; \
+      OHANA_DOWNHEAP (COMPARE, SWAPFUNC, 0); \
+    } \
+  } \
+}
+
+// pre-defined function versions
+void dsort (double *value, int N);
+void fsort (float *value, int N);
+void fsortpair (float *X, float *Y, int N);
+void dsortpair (double *X, double *Y, int N);
+void isortpair (int *X, int *Y, int N);
+void fsortthree (float *X, float *Y, float *Z, int N);
+void dsortthree (double *X, double *Y, double *Z, int N);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/lib/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/lib/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/lib/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.darwin.dylib
+*.darwin_x86.dylib
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/CommOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/CommOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/CommOps.c	(revision 22322)
@@ -0,0 +1,179 @@
+# include <ohana.h>
+
+// XXX this is somewhat poor: the Send commands return TRUE / FALSE for success/failure
+// the Expect commands return 0 for success, -N for different errors
+
+int ExpectMessage (int device, double timeout, IOBuffer *message) {
+
+  int status, length;
+  IOBuffer command;
+
+  status = ExpectCommand (device, 16, timeout, &command);
+  if (status) {
+    FreeIOBuffer (&command);
+    return (status);
+  }
+
+  /* buffer contains an EOL NULL, we can just sscan it */
+  sscanf (command.buffer, "%*s %d", &length);
+  FreeIOBuffer (&command);
+  
+  status = ExpectCommand (device, length, timeout, message);
+  return (status);
+}
+
+int ExpectCommand (int device, int length, double timeout, IOBuffer *buffer) {
+
+  /* read from device until we have length bytes, or timeout */
+
+  int Nread;
+  double dtime;
+  struct timespec request, remain;
+  struct timeval start, stop;
+
+  gettimeofday (&start, NULL);
+
+  /* avoid blocking on waitpid, test every 1000 usec, up to timeout msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 1000000;
+
+  InitIOBuffer (buffer, length + 1);
+
+  while (buffer[0].Nbuffer < length) {
+    Nread = read (device, &buffer[0].buffer[buffer[0].Nbuffer], length - buffer[0].Nbuffer);
+    
+    if (Nread > 0) {
+      buffer[0].Nbuffer += Nread;
+      continue;
+    }
+
+    if (Nread == -1) {
+      switch (errno) {
+	case EAGAIN:
+	case EIO:
+	  /** no data available in pipe, wait a bit, check for timeout **/
+	  nanosleep (&request, &remain);
+	  break;
+	default:
+	  /** error reading from pipe **/
+	  perror ("ReadtoIOBuffer read error");
+	  return (-2);
+      }
+    }
+
+    if (Nread == 0) return (-3);
+
+    gettimeofday (&stop, NULL);
+    dtime = DTIME (stop, start);
+    if (dtime > timeout) return (-1);
+  }
+  return (0);
+}
+
+/* send a message of arbitrary size, sending the size first */
+int SendMessage (int device, char *format, ...) {
+
+  int Nbyte;
+  char tmp;
+  va_list argp;  
+
+  va_start (argp, format);
+  Nbyte = vsnprintf (&tmp, 0, format, argp);
+  va_end (argp);
+
+  if (!Nbyte) return (FALSE);
+
+  va_start (argp, format);
+  if (!SendCommand (device, 16, "NBYTES: %6d", Nbyte)) goto escape;
+  if (!SendCommandV (device, Nbyte, format, argp)) goto escape;
+  va_end (argp);
+  return TRUE;
+
+escape:
+  va_end (argp);
+  return FALSE;
+}
+
+/* send a message of known size, sending the size first */
+int SendMessageFixed (int device, int length, char *message) {
+
+  if (!SendCommand (device, 16, "NBYTES: %6d", length)) return FALSE;
+  if (!SendCommand (device, length, message)) return FALSE;
+  return TRUE;
+}
+
+int SendCommand (int device, int length, char *format, ...) {
+
+  int status;
+  va_list argp;  
+
+  va_start (argp, format);
+  status = SendCommandV (device, length, format, argp);
+  va_end (argp);
+  return (status);
+}
+  
+int SendCommandV (int device, int length, char *format, va_list argp) {
+
+  int status;
+  char *string;
+
+  /* I allocated and zero 1 extra byte */
+  ALLOCATE (string, char, length + 1);
+  memset (string, 0, length + 1);
+  vsnprintf (string, length + 1, format, argp);
+
+  status = write (device, string, length);
+  free (string);
+
+  if (status == -1) return FALSE;
+  return (TRUE);
+}
+
+/* 
+
+ I need an alternative ExpectCommand function which appends to an existing buffer
+ until the complete message is ready.  
+
+ A command looks like this (pre-determined length):
+ NN bytes: XXXXX\0 
+
+ A message looks like this (preceded by NBYTES command) :
+ 16 bytes: WORD NNN\0
+ NNN bytes: message.... \0
+
+ the expect function needs to monitor the number of bytes already received
+ we need to be able to call it repeatedly until the buffer is full.
+
+*/
+
+/* check if the first entry in the buffer corresponds to a message:
+   A message looks like this (preceded by NBYTES command) :
+    16 bytes: WORD NNN
+    NNN bytes: message....
+    note that the NULL bytes are not sent
+  If a message is found, it is popped off the buffer and sent back as a
+  complete line (the length portion is dropped) 
+*/
+
+char *CheckForMessage (IOBuffer *buffer) {
+
+  int Nbytes;
+  char command[20], *line;
+
+  if (buffer[0].Nbuffer < 16) return NULL;
+  memcpy (command, buffer[0].buffer, 16);
+  command[16] = 0;
+
+  sscanf (command, "NBYTES: %d", &Nbytes);
+
+  if (buffer[0].Nbuffer < Nbytes + 16) return NULL;
+
+  ALLOCATE (line, char, Nbytes + 1);
+  memcpy (line, &buffer[0].buffer[16], Nbytes);
+  line[Nbytes] = 0;
+
+  buffer[0].Nbuffer -= Nbytes + 16;
+  memmove (buffer[0].buffer, &buffer[0].buffer[Nbytes+16], buffer[0].Nbuffer);
+  return (line);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/Fread.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/Fread.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/Fread.c	(revision 22322)
@@ -0,0 +1,474 @@
+# include <ohana.h>
+
+int   ByteSwap (char *ptr, int size, int nitems, char *type);
+
+# define BYTE_EXCHANGE(X,Y) tmp = byte[X]; byte[X] = byte[Y]; byte[Y] = tmp;
+# define SWAP_BYTE(X) \
+  tmp = byte[X+0]; byte[X+0] = byte[X+1]; byte[X+1] = tmp;
+# define SWAP_WORD(X) \
+  tmp = byte[X+0]; byte[X+0] = byte[X+3]; byte[X+3] = tmp; \
+  tmp = byte[X+1]; byte[X+1] = byte[X+2]; byte[X+2] = tmp;
+# define SWAP_DBLE(X) \
+  tmp = byte[X+0]; byte[X+0] = byte[X+7]; byte[X+7] = tmp; \
+  tmp = byte[X+1]; byte[X+1] = byte[X+6]; byte[X+6] = tmp; \
+  tmp = byte[X+2]; byte[X+2] = byte[X+5]; byte[X+5] = tmp; \
+  tmp = byte[X+3]; byte[X+3] = byte[X+4]; byte[X+4] = tmp;
+
+/** this is also used in libautocode/def/common.h -- consolidate **/
+# ifdef linux
+# define BYTE_SWAP
+# endif
+
+# ifdef sid
+# define BYTE_SWAP
+# endif
+
+# ifdef dec
+# define BYTE_SWAP
+# endif
+
+/* add other architectures here, ie dec, alpha, etc */ 
+
+int Fread (void *ptr, int size, int nitems, FILE *f, char *type) {
+
+  int valid, status;
+
+  status = fread (ptr, size, nitems, f);
+
+  valid = ByteSwap (ptr, size, nitems, type);
+
+  if (!valid) return (FALSE);
+  return (status);
+}
+
+int Fwrite (void *ptr, int size, int nitems, FILE *f, char *type) {
+
+  int valid, status;
+
+  valid = ByteSwap (ptr, size, nitems, type);
+
+  if (!valid) return (FALSE);
+
+  status = fwrite (ptr, size, nitems, f);
+  return (status);
+}
+
+int ByteSwap (char *ptr, int size, int nitems, char *type) {
+
+# ifdef BYTE_SWAP
+
+  int i;
+  unsigned char *byte0, *byte1, *byte2, *byte3, *byte4, *byte5, *byte6, *byte7, tmp;
+
+  if (!strcmp (type, "char")) return (TRUE);
+
+  if (!strcmp (type, "short")) {
+    byte0 = (unsigned char *)ptr;
+    byte1 = (unsigned char *)ptr + 1;
+    for (i = 0; i < nitems; i++, byte0 += 2, byte1 += 2) {
+      tmp = *byte0;
+      *byte0 = *byte1;
+      *byte1 = tmp;
+    }
+    return (TRUE);
+  }
+
+  if (!strcmp (type, "int") || !strcmp (type, "float")) {
+    byte0 = (unsigned char *)ptr;
+    byte1 = (unsigned char *)ptr + 1;
+    byte2 = (unsigned char *)ptr + 2;
+    byte3 = (unsigned char *)ptr + 3;
+    for (i = 0; i < nitems; i++, byte0 += 4, byte1 += 4, byte2 += 4, byte3 += 4) {
+      tmp = *byte0;
+      *byte0 = *byte3;
+      *byte3 = tmp;
+      tmp = *byte1;
+      *byte1 = *byte2;
+      *byte2 = tmp;
+    }
+    return (TRUE);
+  }
+
+  if (!strcmp (type, "double")) {
+    byte0 = (unsigned char *)ptr;
+    byte1 = (unsigned char *)ptr + 1;
+    byte2 = (unsigned char *)ptr + 2;
+    byte3 = (unsigned char *)ptr + 3;
+    byte4 = (unsigned char *)ptr + 4;
+    byte5 = (unsigned char *)ptr + 5;
+    byte6 = (unsigned char *)ptr + 6;
+    byte7 = (unsigned char *)ptr + 7;
+    for (i = 0; i < nitems; i++, byte0 += 8, byte1 += 8, byte2 += 8, byte3 += 8, byte4 += 8, byte5 += 8, byte6 += 8, byte7 += 8) {
+      tmp = *byte0;
+      *byte0 = *byte7;
+      *byte7 = tmp;
+      tmp = *byte1;
+      *byte1 = *byte6;
+      *byte6 = tmp;
+      tmp = *byte1;
+      *byte2 = *byte5;
+      *byte5 = tmp;
+      tmp = *byte1;
+      *byte3 = *byte4;
+      *byte4 = tmp;
+    }
+    return (TRUE);
+  }
+
+# if (0) /** now in autocode **/
+  if (!strcmp (type, "regimage")) {
+    if (size != REGIMAGE_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (RegImage) %d vs %d\n", size, REGIMAGE_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += REGIMAGE_SIZE) {
+      SWAP_WORD (284); /* exptime */ 
+      SWAP_WORD (288); /* airmass */ 
+      SWAP_WORD (292); /* sky */     
+      SWAP_WORD (296); /* bias */    
+      SWAP_WORD (300); /* fwhm */    
+      SWAP_WORD (304); /* telfocus */ 
+      SWAP_WORD (308); /* xprobe */ 
+      SWAP_WORD (312); /* yprobe */ 
+      SWAP_WORD (316); /* zprobe */ 
+      SWAP_WORD (320); /* dettemp */ 
+      SWAP_WORD (324); /* teltemp[0] */ 
+      SWAP_WORD (328); /* teltemp[1] */  
+      SWAP_WORD (332); /* teltemp[2] */ 
+      SWAP_WORD (336); /* teltemp[3] */ 
+      SWAP_WORD (340); /* rotangle */
+      SWAP_WORD (344); /* ra */      
+      SWAP_WORD (348); /* dec */     
+      SWAP_WORD (352); /* obstime */ 
+      SWAP_WORD (356); /* regtime */  
+    }
+    return (TRUE);
+  }
+
+  if (!strcmp (type, "detreg")) {
+    if (size != DETREG_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (DetReg) %d vs %d\n", size, DETREG_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += DETREG_SIZE) {
+      SWAP_WORD (0);   /* tstart */
+      SWAP_WORD (4);   /* tstop */
+      SWAP_WORD (8);   /* treg */
+      SWAP_WORD (12);  /* exptime */
+      SWAP_WORD (16);  /* type */
+      SWAP_WORD (20);  /* filter */
+      SWAP_WORD (24);  /* ccd */
+      SWAP_WORD (28);  /* Nentry */
+      SWAP_WORD (32);  /* Norder */
+    }
+    return (TRUE);
+  }
+
+  if (!strcmp (type, "photpars")) {
+    if (size != PHOTPARS_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (PhotPars) %d vs %d\n", size, PHOTPARS_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += PHOTPARS_SIZE) {
+      SWAP_WORD (0);    /* ZP */
+      SWAP_WORD (4);    /* ZPo */
+      SWAP_WORD (8);    /* dZP */
+      SWAP_WORD (12);   /* K */
+      SWAP_WORD (16);   /* A */
+      SWAP_WORD (20);   /* tstart */
+      SWAP_WORD (24);   /* tstop */
+      SWAP_BYTE (28);   /* c1 */
+      SWAP_BYTE (30);   /* c2 */
+      SWAP_BYTE (32);   /* photcode */
+      SWAP_WORD (100);   /* Ntime */
+      SWAP_WORD (104);   /* Nmeas */
+    }
+    return (TRUE);
+  }
+
+  if (!strcmp (type, "missing")) {
+    if (size != MISSING_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (Missing) %d vs %d\n", size, MISSING_SIZE);
+      return (FALSE);
+    }
+    byte0 = (unsigned char *)ptr;
+    byte1 = (unsigned char *)ptr + 1;
+    byte2 = (unsigned char *)ptr + 2;
+    byte3 = (unsigned char *)ptr + 3;
+    for (i = 0; i < nitems; i++, byte0 += 4, byte1 += 4, byte2 += 4, byte3 += 4) {
+      tmp = *byte0;
+      *byte0 = *byte3;
+      *byte3 = tmp;
+      tmp = *byte1;
+      *byte1 = *byte2;
+      *byte2 = tmp;
+    }
+    return (TRUE);
+  }
+
+  if (!strcmp (type, "secfilt")) {
+    if (size != SECFILT_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (SecFilt) %d vs %d\n", size, SECFILT_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *) ptr;
+    for (i = 0; i < nitems; i++, byte += SECFILT_SIZE) {
+      SWAP_BYTE (0);   /* M */
+      SWAP_BYTE (2);   /* Xm */
+      SWAP_BYTE (4);   /* dM */
+    }
+    return (TRUE);
+  }
+
+  if (!strcmp (type, "spectrum")) {
+    if (size != SPECTRUM_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (Spectrum) %d vs %d\n", size, SPECTRUM_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += SPECTRUM_SIZE) {
+      SWAP_WORD (0);  /* ra */ 
+      SWAP_WORD (4);  /* dec */ 
+      SWAP_WORD (8);  /* exptime */ 
+      SWAP_WORD (12); /* airmass */ 
+      SWAP_WORD (16); /* Ws */ 
+      SWAP_WORD (20); /* We */ 
+      SWAP_WORD (24); /* dW */ 
+      SWAP_WORD (28); /* Nspec */ 
+      SWAP_WORD (32); /* obstime */ 
+      SWAP_WORD (36); /* regtime */ 
+    }
+    return (TRUE);
+  }
+
+  if (!strcmp (type, "image")) {
+    if (size != IMAGE_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (Image) %d vs %d\n", size, IMAGE_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += IMAGE_SIZE) {
+      SWAP_DBLE (0); /* coords.crval1 */
+      SWAP_DBLE (8); /* coords.crval2 */
+      for (j = 16; j < 104; j+=4) { /* coords.crpix, delt, pc_ij, polyterms[7][2] */
+	SWAP_WORD (j);
+      }
+      SWAP_WORD (120); /* tzero */
+      SWAP_WORD (124); /* nstar */
+      for (j = 128; j < 146; j+=2) { /* sec z, NX, NY, apmifit, dapmifit, source, Mcal, dMcal, Xm */
+	SWAP_BYTE (j);
+      }
+      SWAP_WORD (184); /* exptime */
+      SWAP_WORD (210); /* order */
+      /**** this (210) is an error! (don't fix, replaced with autocode) ****/
+      for (j = 212; j < 240; j+=2) {
+	SWAP_BYTE (j); /* Mx - Myyy */
+      }
+    }
+    return (TRUE);
+  }
+
+# if (PANSTARRS)
+  if (!strcmp (type, "average")) {
+    if (size != AVERAGE_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (Average) %d vs %d\n", size, AVERAGE_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += AVERAGE_SIZE) {
+      SWAP_DBLE (0);   /* R */
+      SWAP_DBLE (8);   /* D */
+      SWAP_BYTE (16);  /* M */
+      SWAP_BYTE (18);  /* Nm */
+      SWAP_BYTE (20);  /* Nn */
+      SWAP_BYTE (22);  /* Xp */
+      SWAP_BYTE (24);  /* Xm */
+      SWAP_BYTE (26);  /* code */
+      SWAP_WORD (28);  /* offset */
+      SWAP_WORD (32);  /* missing */
+      SWAP_BYTE (36);  /* dM */
+      SWAP_BYTE (38);  /* Xg */
+    }
+    return (TRUE);
+  } 
+  if (!strcmp (type, "measure")) {
+    if (size != MEASURE_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (Measure) %d vs %d\n", size, MEASURE_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += MEASURE_SIZE) {
+      SWAP_BYTE (0);   /* dR */
+      SWAP_BYTE (2);   /* dD */
+      SWAP_BYTE (4);   /* M  */
+      SWAP_BYTE (6);   /* Mcal */
+      SWAP_BYTE (8);   /* Mgal */
+      SWAP_BYTE (10);  /* Map  */
+      SWAP_BYTE (12);  /* FWx */
+      /* 14, 15, 16, 17 - char */
+      SWAP_BYTE (18);  /* source */
+      SWAP_WORD (20);  /* t */
+      SWAP_WORD (24);  /* averef */
+      SWAP_BYTE (28);  /* dt */
+      SWAP_BYTE (30);  /* flags */
+    }
+    return (TRUE);
+  }
+
+# endif /** PANSTARRS **/
+
+
+# if (ELIXIR)
+  if (!strcmp (type, "average")) {
+    if (size != AVERAGE_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (Average) %d vs %d\n", size, AVERAGE_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += AVERAGE_SIZE) {
+      SWAP_WORD (0);   /* R */
+      SWAP_WORD (4);   /* D */
+      SWAP_BYTE (8);   /* M */
+      SWAP_BYTE (10);  /* Nm */
+      SWAP_BYTE (12);  /* Nn */
+      SWAP_BYTE (14);  /* Xp */
+      SWAP_BYTE (16);  /* Xm */
+      SWAP_BYTE (18);  /* code */
+      SWAP_WORD (20);  /* offset */
+      SWAP_WORD (24);  /* missing */
+      SWAP_BYTE (28);  /* dM */
+      SWAP_BYTE (30);  /* Xg */
+    }
+    return (TRUE);
+  } 
+  if (!strcmp (type, "measure")) {
+    if (size != MEASURE_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (Measure) %d vs %d\n", size, MEASURE_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += MEASURE_SIZE) {
+      SWAP_BYTE (0);   /* dR */
+      SWAP_BYTE (2);   /* dD */
+      SWAP_BYTE (4);   /* M  */
+      SWAP_BYTE (6);   /* Mcal */
+      SWAP_BYTE (8);   /* Mgal */
+      SWAP_BYTE (10);  /* Map  */
+      SWAP_BYTE (12);  /* FWx */
+      /* 14, 15, 16, 17 - char */
+      SWAP_BYTE (18);  /* source */
+      SWAP_WORD (20);  /* t */
+      SWAP_WORD (24);  /* averef */
+      SWAP_BYTE (28);  /* dt */
+      SWAP_BYTE (30);  /* flags */
+    }
+    return (TRUE);
+  }
+
+# endif /** ELIXIR **/
+
+
+# if (LONEOS)
+  if (!strcmp (type, "average")) {
+    if (size != OLDAVERAGE_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (OldAverage) %d vs %d\n", size, OLDAVERAGE_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += OLDAVERAGE_SIZE) {
+      SWAP_WORD (0);   /* R */
+      SWAP_WORD (4);   /* D */
+      SWAP_BYTE (8);   /* M */
+      SWAP_BYTE (10); /* Nm */
+      SWAP_BYTE (12); /* Nn */
+      SWAP_BYTE (14); /* Xp */
+      SWAP_BYTE (16); /* Xm */
+      SWAP_BYTE (18); /* code */
+      SWAP_WORD (20); /* offset */
+      SWAP_WORD (24); /* missing */
+    }
+    return (TRUE);
+  } 
+  if (!strcmp (type, "measure")) {
+    if (size != OLDMEASURE_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (OldMeasure) %d vs %d\n", size, OLDMEASURE_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += OLDMEASURE_SIZE) {
+      SWAP_BYTE (0);   /* dR */
+      SWAP_BYTE (2);   /* dD */
+      SWAP_BYTE (4);   /* M  */
+      SWAP_BYTE (6);   /* Mcal */
+      SWAP_BYTE (10);  /* source */
+      SWAP_WORD (12);  /* t */
+      SWAP_WORD (16);  /* average */
+    }
+    return (TRUE);
+  }
+# endif /** LONEOS **/
+
+# if (0) /*** others ***/
+  if (!strcmp (type, "oldsecfilt")) {
+    if (size != OLDSECFILT_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (OldSecFilt) %d vs %d\n", size, OLDSECFILT_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *) ptr;
+    for (i = 0; i < nitems; i++, byte += OLDSECFILT_SIZE) {
+      SWAP_BYTE (0);   /* M */
+      SWAP_BYTE (2);   /* Xm */
+    }
+    return (TRUE);
+  }
+  if (!strcmp (type, "rufimage")) {
+    if (size != RUFIMAGE_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (RufImage) %d vs %d\n", size, RUFIMAGE_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += RUFIMAGE_SIZE) {
+      SWAP_WORD (0);   /* sky */
+      SWAP_WORD (4);   /* bias */
+      SWAP_WORD (8);  /* fwhm */
+      SWAP_WORD (12); /* exptime */
+      SWAP_WORD (16); /* airmass */
+      SWAP_WORD (20); /* obstime */
+      SWAP_BYTE (24); /* ccdnum */
+    }
+    return (TRUE);
+  }
+  if (!strcmp (type, "olddetreg")) {
+    if (size != OLDDETREG_SIZE) {
+      fprintf (stderr, "mismatch in type sizes (DetReg) %d vs %d\n", size, OLDDETREG_SIZE);
+      return (FALSE);
+    }
+    byte = (unsigned char *)ptr;
+    for (i = 0; i < nitems; i++, byte += OLDDETREG_SIZE) {
+      SWAP_WORD (0);   /* tstart */
+      SWAP_WORD (4);   /* tstop */
+      SWAP_WORD (8);   /* treg */
+      SWAP_WORD (12);  /* sigma */
+      SWAP_WORD (16);  /* clipsigma */
+      SWAP_WORD (20);  /* type */
+      SWAP_WORD (24);  /* filter */
+      SWAP_WORD (28);  /* ccd */
+    }
+    return (TRUE);
+  }
+# endif /** others **/
+# endif /** now in autocode **/
+
+  fprintf (stderr, "unknown type %s\n", type);
+  return (FALSE);
+
+# else
+
+  return (TRUE);
+
+# endif 
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/IOBufferOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/IOBufferOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/IOBufferOps.c	(revision 22322)
@@ -0,0 +1,127 @@
+# include <ohana.h>
+# define DEBUG 0
+
+int InitIOBuffer (IOBuffer *buffer, int Nalloc) {
+
+  buffer[0].Nalloc = Nalloc;
+  buffer[0].Nreset = Nalloc;
+  buffer[0].Nblock = Nalloc / 2;
+  buffer[0].Nbuffer = 0;
+
+  ALLOCATE (buffer[0].buffer, char, buffer[0].Nalloc);
+  bzero (buffer[0].buffer, buffer[0].Nalloc);
+
+  return (TRUE);
+}
+
+int FlushIOBuffer (IOBuffer *buffer) {
+
+  buffer[0].Nbuffer = 0;
+  buffer[0].Nalloc = buffer[0].Nreset;
+  REALLOCATE (buffer[0].buffer, char, buffer[0].Nalloc);
+  bzero (buffer[0].buffer, buffer[0].Nalloc);
+
+  return (TRUE);
+}
+
+int ReadtoIOBuffer (IOBuffer *buffer, int fd) {
+
+  int Nread, Nfree;
+
+  if (fd == 0) {
+    /* pipe is closed */
+    return (0);
+  }
+
+  Nfree = buffer[0].Nalloc - buffer[0].Nbuffer;
+  if (Nfree < buffer[0].Nblock) {
+    buffer[0].Nblock *= 2;
+    buffer[0].Nblock = MIN (buffer[0].Nblock, 0x10000);
+    buffer[0].Nalloc += 2*buffer[0].Nblock;
+    REALLOCATE (buffer[0].buffer, char, buffer[0].Nalloc);
+    Nfree = buffer[0].Nalloc - buffer[0].Nbuffer;
+    bzero (buffer[0].buffer + buffer[0].Nbuffer, Nfree);
+  }
+
+  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); 
+
+  /* on success, increase the block size for the next read */
+  
+  if (Nread >= 0) {
+    buffer[0].Nbuffer += Nread;
+    return (Nread);
+  }
+
+  if (Nread == -1) {
+    switch (errno) {
+    case EAGAIN:
+    case EINTR:
+      /** data not available in pipe or read interrupted : just try again **/
+      return (-1);
+    default:
+      /** serious error (buffer overflow, invalid fd, etc **/
+      perror ("ReadtoIOBuffer read error");
+      return (-2);
+    }
+  }
+  return (Nread);
+}
+
+/* read until buffer is empty (Nmax retries) */
+int EmptyIOBuffer (IOBuffer *buffer, int Nmax, int fd) {
+
+  int i, status;
+
+  status = -1;
+  for (i = 0; (status != 0) && (i < Nmax); i++) {
+    status = ReadtoIOBuffer (buffer, fd);
+    if (status == -1) usleep (10000);
+    if (status > 0) i = 0;
+  }
+  if (status == -1) return (FALSE);
+  return (TRUE);
+}
+
+void FreeIOBuffer (IOBuffer *buffer) {
+
+  if (buffer[0].buffer != (char *) NULL) {
+    free (buffer[0].buffer);
+  }
+}
+
+/* print to an IOBuffer (varargs form) */
+int PrintIOBuffer (IOBuffer *buffer, char *format, ...) {
+
+  int status;
+  va_list argp;  
+
+  va_start (argp, format);
+  status = vPrintIOBuffer (buffer, format, argp);
+  va_end (argp);
+  return (status);
+}
+
+/* print to an IOBuffer (va_list form) */
+int vPrintIOBuffer (IOBuffer *buffer, char *format, va_list argp) {
+
+  /* add the output line to the given IOBuffer */
+  
+  int Nbyte;
+  char tmp;
+  va_list argp2;
+
+  va_copy (argp2, argp);
+
+  Nbyte = vsnprintf (&tmp, 0, format, argp2);
+
+  if (buffer[0].Nbuffer + Nbyte + 1>= buffer[0].Nalloc) {
+    buffer[0].Nalloc = buffer[0].Nbuffer + Nbyte + 64;
+    REALLOCATE (buffer[0].buffer, char, buffer[0].Nalloc);
+  }
+
+  vsnprintf (&buffer[0].buffer[buffer[0].Nbuffer], Nbyte + 1, format, argp);
+  buffer[0].Nbuffer += Nbyte;
+  return (TRUE);
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/config.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/config.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/config.c	(revision 22322)
@@ -0,0 +1,454 @@
+# include <ohana.h>
+
+# define D_NBYTES 4096
+
+static char *ConfigVariable = (char *) NULL;
+static int NDefineVariable;
+static char **DefineVariable;
+static char **DefineValue;
+
+char *SelectConfigFile (int *argc, char **argv, char *progname) {
+  
+  /* 
+     config file selection rules (first ones override later ones):
+
+     1) -c Filename   : use Filename
+     2) PROGNAME      : use environment variable as config file
+     3) progname.rc   : use alternate name in local dir as config file
+     4) .prognamerc   : use rc file in local dir as config file
+     5) ~/.prognamerc : use rc file in homedir as config file
+
+     special variable definitions:
+     1) -C WORD       : set CONFIG variable to WORD
+     2) -D NAME WORD  : set NAME variable to WORD
+        -D overrides variables in param file
+
+     these command-line options are removed and a complete arg list left behind
+  */
+
+  char *filename, *find, *home;
+  struct stat filestat;
+  uid_t uid;
+  gid_t gid;
+  int i, N, NDEF, status;
+  
+  /* first look for -C CONFIG variable */
+  if ((N = get_argument (*argc, argv, "-C"))) {
+    remove_argument (N, argc, argv);
+    if (ConfigVariable != (char *) NULL) free (ConfigVariable);
+    ConfigVariable = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+    
+  /* next look for -D NAME WORD variables */
+  NDEF = 10;
+  ALLOCATE (DefineVariable, char *, NDEF);
+  ALLOCATE (DefineValue, char *, NDEF);
+  for (i = 0; (N = get_argument (*argc, argv, "-D")); i++) {
+    remove_argument (N, argc, argv);
+    DefineVariable[i] = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+    DefineValue[i] = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+    if (i == NDEF - 1) {
+      NDEF += 10;
+      REALLOCATE (DefineVariable, char *, NDEF);
+      REALLOCATE (DefineValue, char *, NDEF);
+    }      
+  }    
+  NDefineVariable = i;
+  REALLOCATE (DefineVariable, char *, MAX (1, i));
+    
+  /* look for -c FILENAME for config file */
+  if ((N = get_argument (*argc, argv, "-c"))) {
+    remove_argument (N, argc, argv);
+    filename = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+    return (filename);
+  }
+  
+  /* look for PROGNAME env var */
+  find = strcreate (progname);
+  for (i = 0; i < strlen(find); i++) find[i] = toupper (find[i]);
+  filename = getenv (find);
+  free (find);
+  if (filename != (char *) NULL) {
+    find = strcreate (filename);
+    return (find);
+  }
+
+  uid = getuid();
+  gid = getgid();
+  ALLOCATE (find, char, strlen(progname) + 32);
+
+  /* look for progname.rc */
+  sprintf (find, "%s.rc", progname);
+  status = stat (find, &filestat);
+  if (status == 0) { 
+    /* file exists, can we read it? */
+    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR)) ||
+	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP)) || 
+	(                            (filestat.st_mode & S_IROTH))) {
+      return (find);
+    }
+  }
+  free (find);
+
+  /* we eliminate this option: this is just confusing */
+# if (0)  
+  /* look for ./.prognamerc */
+  sprintf (find, ".%src", progname);
+  status = stat (find, &filestat);
+  if (status == 0) { 
+    /* file exists, can we read it? */
+    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR)) ||
+	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP)) || 
+	(                            (filestat.st_mode & S_IROTH))) {
+      return (find);
+    }
+  }
+# endif
+
+  /* look for ~/.prognamerc */
+  home = getenv ("HOME");
+  if (home == (char *) NULL) { return ((char *) NULL); }
+  ALLOCATE (find, char, 1024);
+  sprintf (find, "%s/.%src", home, progname);
+  status = stat (find, &filestat);
+  if (status == 0) { 
+    /* file exists, can we read it? */
+    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR)) ||
+	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP)) || 
+	(                            (filestat.st_mode & S_IROTH))) {
+      return (find);
+    }
+  }
+  free (find);
+  return ((char *) NULL);
+}
+
+char *LoadConfigFile (char *filename) {
+  
+  FILE *f;
+  int i, done, Nbytes, NBYTES, nbytes, Nout, Ncpy, INPUT, Nlevel;
+  char *ibuffer, *obuffer, *tbuffer;
+  char *last, *next;
+  char infile[256], line[256];
+  
+  /* open file */
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    return ((char *) NULL);
+  }
+ 
+  /* allocate tmp space, 2 extra bytes for a final return and EOL */
+  Nbytes = 0;
+  NBYTES = D_NBYTES;
+  ALLOCATE (ibuffer, char, NBYTES + 2);
+    
+  /* load data from file */
+  done = FALSE;
+  while ((nbytes = fread (&ibuffer[Nbytes], sizeof(char), D_NBYTES, f)) == D_NBYTES) {
+    Nbytes += nbytes;
+    NBYTES += D_NBYTES;
+    REALLOCATE (ibuffer, char, NBYTES + 2);
+  }
+  Nbytes += nbytes;
+  fclose (f);
+
+  /* add final return & EOL if non-existent */
+  if (ibuffer[Nbytes-1] != '\n') {
+    ibuffer[Nbytes] = '\n';
+    Nbytes ++;
+  }
+  if (ibuffer[Nbytes]) ibuffer[Nbytes] = 0;
+
+  /* add the optional variables to the end of the input buffer */
+  if (ConfigVariable != (char *) NULL) {
+    snprintf (line, 256, "%s %s\n", "CONFIG", ConfigVariable);
+    Ncpy = strlen (line);
+    if (Nbytes + Ncpy >= NBYTES - 1) {
+      NBYTES = Nbytes + Ncpy + D_NBYTES;
+      REALLOCATE (ibuffer, char, NBYTES);
+    }    
+    memcpy (&ibuffer[Nbytes], line, Ncpy);
+    Nbytes += Ncpy;
+    ibuffer[Nbytes] = 0;
+  }
+  for (i = 0; i < NDefineVariable; i++) {
+    snprintf (line, 256, "%s %s\n", DefineVariable[i], DefineValue[i]);
+    Ncpy = strlen (line);
+    if (Nbytes + Ncpy >= NBYTES - 1) {
+      NBYTES = Nbytes + Ncpy + D_NBYTES;
+      REALLOCATE (ibuffer, char, NBYTES);
+    }    
+    memcpy (&ibuffer[Nbytes], line, Ncpy);
+    Nbytes += Ncpy;
+    ibuffer[Nbytes] = 0;
+  }
+  
+  /* loop over input buffer, interpolating 'input' lines until none are added */
+  Nlevel = 0;
+  do {
+    INPUT = FALSE;
+
+    /* allocate output buffer & set counter */
+    NBYTES = Nbytes + D_NBYTES;
+    ALLOCATE (obuffer, char, NBYTES);
+    Nout = 0;
+    
+    /* copy from ibuffer to obuffer, interpolating 'input' lines */
+    last = next = ibuffer;
+    for (i = 1; (next = ScanConfig (ibuffer, "input", "%s", i, infile)) != (char *) NULL; i++) {
+      /* copy data from last point to before 'input' */ 
+      Ncpy = next - last - 5;
+      if (Nout + Ncpy >= NBYTES - 1) {
+	NBYTES = Nout + Ncpy + D_NBYTES;
+	REALLOCATE (obuffer, char, NBYTES);
+      }      
+      memcpy (&obuffer[Nout], last, Ncpy);
+      Nout += Ncpy;
+      obuffer[Nout] = 0;
+      
+      /* insert data from 'input' file */
+      tbuffer = LoadRawConfigFile (infile, FALSE);
+      if (tbuffer != (char *) NULL) {
+	Ncpy = strlen (tbuffer);
+	if (Nout + Ncpy >= NBYTES - 1) {
+	  NBYTES = Nout + Ncpy + D_NBYTES;
+	  REALLOCATE (obuffer, char, NBYTES);
+	}      
+	memcpy (&obuffer[Nout], tbuffer, Ncpy);
+	free (tbuffer);
+	Nout += Ncpy;
+	obuffer[Nout] = 0;
+      }
+      
+      /* pointer goes to end of input line */
+      last = strchr (next, '\n');
+      if (last == (char *) NULL) break;
+      last ++;
+      INPUT = TRUE;
+    }
+    /* last set of bytes after last input */
+    Ncpy = strlen (last);
+    if (Nout + Ncpy >= NBYTES - 1) {
+      NBYTES = Nout + Ncpy + D_NBYTES;
+      REALLOCATE (obuffer, char, NBYTES);
+    }      
+    memcpy (&obuffer[Nout], last, Ncpy);
+    Nout += Ncpy;
+    obuffer[Nout] = 0;
+    free (ibuffer);
+    ibuffer = obuffer;
+    Nlevel ++;
+  } while (INPUT && (Nlevel < 20));
+  if (Nlevel == 20) {
+    fprintf (stderr, "warning: config reached max depth of 20\n");
+  }
+
+  /* 'obuffer' now has complete set of interpolated lines from 'filename' */
+  return (obuffer);
+}
+
+char *ScanConfig (char *config, char *field, char *mode, int Nentry, ...) {
+  
+  int i;
+  char *p, *p2, *tmp, *tfield, *start, *expandline();
+  va_list argp;
+  double value;
+  
+  if (config == (char *) NULL) return ((char *) NULL);
+  va_start (argp, Nentry);
+
+  ALLOCATE (tfield, char, strlen (field) + 3);
+  sprintf (tfield, "\n%s", field);
+
+  /* we search for Nentry matching fields,
+     or until the end if Nentry == 0 */
+  p = (char *) NULL;
+  p2 = config;
+  for (i = 0; (i < Nentry) || !Nentry;) {
+    tmp = strstr (p2, tfield);
+    if (tmp == (char *) NULL) {
+      break;
+    }
+    p2 = tmp + strlen (tfield);
+    if (OHANA_WHITESPACE (*p2)) {
+      p = p2;
+      i++;
+    }
+  }
+  free (tfield);
+  if (Nentry && (i != Nentry)) {
+    p = va_arg (argp, char *);
+    p[0] = 0;
+    return ((char *) NULL);
+  }
+  if (p == (char *) NULL) {
+    p = va_arg (argp, char *);
+    p[0] = 0;
+    return ((char *) NULL);
+  }
+
+  start = p;
+  if (!strcmp (mode, "%s")) {
+    p2 = strchr (p, '\n');
+    if (p2 == (char *) NULL) p2 = config + strlen(config);
+    ALLOCATE (tmp, char, p2-p + 2);
+    memcpy (tmp, p, (p2-p));
+    tmp[(p2-p)] = 0;
+    stripwhite (tmp);
+    tmp = expandline (tmp, config);
+    p2 = va_arg (argp, char *);
+    strcpy (p2, tmp);
+    free (tmp);
+  } else {
+ 
+    /* try to get a numerical value from the field */
+    value = strtod (p, &p2);
+    if ((*p2 == 'd') || (*p2 == 'D')) 
+      value *= pow (10.0, atof (p2 + 1));
+    
+    if (!strcmp (mode, "%d"))  *va_arg (argp, int *)       = value;
+    if (!strcmp (mode, "%u"))  *va_arg (argp, unsigned *)  = value;
+    if (!strcmp (mode, "%ld")) *va_arg (argp, long *)      = value;
+    if (!strcmp (mode, "%hd")) *va_arg (argp, short *)     = value;
+    if (!strcmp (mode, "%f"))  *va_arg (argp, float *)     = value;
+    if (!strcmp (mode, "%lf")) *va_arg (argp, double *)    = value;
+    
+  }
+ 
+  va_end (argp);
+  return (start);
+  
+}
+
+
+char *expandline (char *line, char *config) {
+
+  int Nin, Nout, Ncpy, NBYTES;
+  char *p1, *p2, word[256], value[256];
+  char *outline;
+  
+  NBYTES = 256;
+  ALLOCATE (outline, char, NBYTES);
+  Nout = 0;
+  Nin  = 0;
+  while ((p1 = strchr (&line[Nin], '$')) != (char *) NULL) {
+    Ncpy = p1 - line - Nin;
+    if (Nout + Ncpy >= NBYTES) {
+      NBYTES = Nout + Ncpy + 256;
+      REALLOCATE (outline, char, NBYTES - 2);
+    }
+    memcpy (&outline[Nout], &line[Nin], Ncpy);
+    Nout += Ncpy;
+    
+    p1 ++;
+    for (p2 = p1; isalnum (*p2) || (*p2 == '_') || (*p2 == '-'); p2++);
+    memcpy (word, p1, p2 - p1);
+    word[p2-p1] = 0;
+    Nin += Ncpy + 1 + p2 - p1;
+    
+    /* search for last entry of word */
+    if (!ScanConfig (config, word, "%s", 0, value)) {
+      fprintf (stderr, "variable %s not found in config file\n", word);
+      return (line);
+    }
+    Ncpy = strlen(value);
+    if (Nout + Ncpy >= NBYTES - 2) {
+      NBYTES = Nout + Ncpy + 256;
+      REALLOCATE (outline, char, NBYTES);
+    }    
+    memcpy (&outline[Nout], value, Ncpy);
+    Nout += Ncpy;
+  }
+  Ncpy = strlen(&line[Nin]);
+  if (Nout + Ncpy >= NBYTES - 2) {
+    NBYTES = Nout + Ncpy + 256;
+    REALLOCATE (outline, char, NBYTES);
+  }  
+  memcpy (&outline[Nout], &line[Nin], Ncpy);
+  Nout += Ncpy;
+  outline[Nout] = 0;
+  free (line);
+  return (outline);
+
+}
+
+char *LoadRawConfigFile (char *filename, int options) {
+  
+  FILE *f;
+  int i, done, Nbytes, NBYTES, nbytes, Ncpy;
+  char *ibuffer;
+  char line[256];
+  
+  /* open file */
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    return ((char *) NULL);
+  }
+ 
+  /* allocate tmp space, 2 extra bytes for a final return and EOL */
+  Nbytes = 0;
+  NBYTES = D_NBYTES;
+  ALLOCATE (ibuffer, char, NBYTES + 2);
+    
+  /* load data from file */
+  done = FALSE;
+  while ((nbytes = fread (&ibuffer[Nbytes], sizeof(char), D_NBYTES, f)) == D_NBYTES) {
+    Nbytes += nbytes;
+    NBYTES += D_NBYTES;
+    REALLOCATE (ibuffer, char, NBYTES + 2);
+  }
+  Nbytes += nbytes;
+  fclose (f);
+
+  /* add final return & EOL, if non-existent */
+  if (ibuffer[Nbytes-1] != '\n') {
+    ibuffer[Nbytes] = '\n';
+    Nbytes ++;
+  }
+  if (ibuffer[Nbytes]) ibuffer[Nbytes] = 0;
+
+  if (options) {
+    /* write optional variables to bottom of buffer, overriding entries in the file */
+    if (ConfigVariable != (char *) NULL) {
+      snprintf (line, 256, "%s %s\n", "CONFIG", ConfigVariable);
+      Ncpy = strlen (line);
+      if (Nbytes + Ncpy >= NBYTES) {
+	NBYTES = Nbytes + Ncpy + D_NBYTES;
+	REALLOCATE (ibuffer, char, NBYTES + 2);
+      }    
+      memcpy (&ibuffer[Nbytes], line, Ncpy);
+      Nbytes += Ncpy;
+      ibuffer[Nbytes] = 0;
+    }
+    for (i = 0; i < NDefineVariable; i++) {
+      snprintf (line, 256, "%s %s\n", DefineVariable[i], DefineValue[i]);
+      Ncpy = strlen (line);
+      if (Nbytes + Ncpy >= NBYTES) {
+	NBYTES = Nbytes + Ncpy + D_NBYTES;
+	REALLOCATE (ibuffer, char, NBYTES + 2);
+      }    
+      memcpy (&ibuffer[Nbytes], line, Ncpy);
+      Nbytes += Ncpy;
+      ibuffer[Nbytes] = 0;
+    }
+  }
+
+  return (ibuffer);
+}
+
+/* 
+char *LoadConfigFile (char *filename) {
+  
+  char *config;
+
+  config = LoadSubConfigFile (filename, TRUE);
+
+  return (config);
+
+}
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/findexec.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/findexec.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/findexec.c	(revision 22322)
@@ -0,0 +1,380 @@
+# include <ohana.h>
+
+int check_file_exec (char *filename) {
+  
+  struct stat filestat;
+  uid_t uid;
+  gid_t gid;
+  int status;
+
+  uid = getuid();
+  gid = getgid();
+
+  /* check permission to exec file */
+  status = stat (filename, &filestat);
+  if (status == 0) { /* file exists, are permissions OK? */
+    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR) && (filestat.st_mode & S_IXUSR)) ||
+	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP) && (filestat.st_mode & S_IXGRP)) || 
+	(                            (filestat.st_mode & S_IROTH) && (filestat.st_mode & S_IXOTH))) {
+      return (TRUE);
+    } else {
+      return (FALSE);
+    }
+  }
+  return (FALSE);
+}
+
+/* check that:
+   - dir exists
+   - dir permissions OK
+*/
+int check_dir_access (char *path, int VERBOSE) {
+  
+  struct stat filestat;
+  uid_t uid;
+  gid_t gid;
+  int status, cmode;
+
+  uid = getuid();
+  gid = getgid();
+
+  /* check permission to write to directory */
+  status = stat (path, &filestat);
+  if (status == -1) {
+    if (VERBOSE) fprintf (stderr, "directory %s does not exist, creating...\n", path);
+    cmode = S_IRWXU | S_IRWXG | S_IRWXO;
+    status = mkdirhier (path, cmode);
+    if (status == -1) {
+      if (VERBOSE) fprintf (stderr, "can't create %s\n", path);
+      return (FALSE);
+    }
+  } 
+  status = stat (path, &filestat);
+  if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRWXU)) ||
+      ((gid == filestat.st_gid) && (filestat.st_mode & S_IRWXG)) || 
+      (filestat.st_mode & S_IRWXO)) {
+  } else {
+    if (VERBOSE) fprintf (stderr, "can't write to %s\n", path);
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+/* check that file can be written to:
+   - dir exists
+   - dir permissions OK
+   - file permissions OK, 
+   - file backup permission OK (optional)
+*/
+int check_file_access (char *basefile, int BACKUP, int VERBOSE) {
+  
+  char *path, *filename;
+  struct stat filestat;
+  uid_t uid;
+  gid_t gid;
+  int status;
+
+  uid = getuid();
+  gid = getgid();
+
+  /* check permission to write to directory */
+  path = pathname (basefile);
+  status = check_dir_access (path, VERBOSE);
+  free (path);
+  if (!status) return (FALSE);
+  
+  /* check permission to write to file */
+  status = stat (basefile, &filestat);
+  if (status == 0) { /* file exists, are permissions OK? */
+    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR) && (filestat.st_mode & S_IWUSR)) ||
+	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP) && (filestat.st_mode & S_IWGRP)) || 
+	((filestat.st_mode & S_IROTH) && (filestat.st_mode & S_IWOTH))) {
+    } else {
+      if (VERBOSE) fprintf (stderr, "can't write to %s\n", basefile);
+      return (FALSE);
+    }
+  }
+  
+  /* check permission to write to backup file */
+  if (BACKUP) {
+    ALLOCATE (filename, char, strlen(basefile) + 2);
+    sprintf (filename, "%s~", basefile);
+    status = stat (filename, &filestat);
+    if (status == 0) { /* file exists, are permissions OK? */
+      if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR) && (filestat.st_mode & S_IWUSR)) ||
+	  ((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP) && (filestat.st_mode & S_IWGRP)) || 
+	  ((filestat.st_mode & S_IROTH) && (filestat.st_mode & S_IWOTH))) {
+      } else {
+	if (VERBOSE) fprintf (stderr, "can't write to %s\n", filename);
+	return (FALSE);
+      }
+    }
+    free (filename);
+  }
+  return (TRUE);
+}
+
+/* pathname:
+   given path/filename, returns path
+   given just filename, returns . 
+   given path1/path2/,  returns path1
+*/
+
+char *pathname (char *infile) {
+ 
+  int i;
+  char *c, *file;
+
+  /* make working version */
+  file = strcreate (infile);
+
+  /* strip off trailing / */
+  for (i = strlen (file); (i > 0) && (file[i] == '/'); i--) file[i] = 0;
+
+  c = strrchr (file, '/');
+  if (c == (char *) NULL) {
+    strcpy (file, ".");
+  } else {
+    *c = 0;
+  }
+  
+  return (file);
+  
+}
+
+/* filerootname
+
+   given /path/file.ext return file 
+   given /path/file return file 
+   given /path/file/ return file 
+*/
+
+char *filerootname (char *infile) {
+
+  int i;
+  char *file, *root, *p1, *p2;
+
+  /* make working version */
+  file = strcreate (infile);
+
+  /* strip off trailing / */
+  for (i = strlen (file); (i > 0) && (file[i] == '/'); i--) file[i] = 0;
+
+  /* find last / */
+  p1 = strrchr (file, '/');
+  if (p1 == (char *) NULL) 
+    p1 = file;
+  else
+  p1 ++;
+
+  /* find last . */
+  p2 = strrchr (file, '.');
+  if (p2 == (char *) NULL) p2 = p1 + strlen(p1);
+
+  /* create new string, free working space */
+  root = strncreate (p1, p2-p1);
+  free (file);
+
+  return (root);
+
+}  
+
+/* fileextname
+
+   given /path/file.ext return ext 
+   given /path/file return NULL 
+   given /path/file/ return NULL 
+   given file.ext return ext
+*/
+
+char *fileextname (char *file) {
+
+  char *root, *p1, *p2;
+
+  /* find last / */
+  p1 = strrchr (file, '/');
+  if (p1 == (char *) NULL) p1 = file;
+
+  /* find last . after p1 */
+  p2 = strrchr (p1, '.');
+  if (p2 == (char *) NULL) return ((char *) NULL);
+  p2 ++;
+
+  /* create new string, free working space */
+  root = strncreate (p2, strlen(p2));
+  return (root);
+
+}  
+
+/* given /path/file.ext return file.ext */
+char *filebasename (char *name) {
+ 
+  char *c, *file;
+
+  ALLOCATE (file, char, strlen(name) + 1);
+
+  c = strrchr (name, '/');
+  if (c == (char *) NULL) {
+    strcpy (file, name);
+  } else {
+    strcpy (file, c+1);
+  }
+  
+  return (file);
+  
+}
+
+char *findexec (int argc, char **argv) {
+
+  int i, N, done, status;
+  char *c, *e, *dir, path[1024], name[1024];
+
+  /* if given an absolute or relative path, use it */
+  if (strchr (argv[0], '/') != (char *) NULL) {
+    status = check_file_exec (argv[0]);
+    if (status) {
+      if (realpath (argv[0], path) == (char *) NULL) return ((char *) NULL);
+      dir = pathname (path);
+      return (dir);
+    } else {
+      return ((char *) NULL);
+    }
+  }
+  N = 0;
+  for (i = argc+1; argv[i] != (char *) NULL; i++) {
+    if (!strncmp (argv[i], "PATH", 4)) {
+      N = i;
+      break;
+    }
+  }
+
+  if (N) {
+    c = &argv[N][5];
+    e = strchr (c, ':');
+    done = FALSE;
+    i = 0;
+    while (!done) {
+      bzero (path, 1024);
+      if (e == (char *) NULL) {
+	done = TRUE;
+	strncpy (path, c, strlen(c));
+      } else {
+	strncpy (path, c, e-c);
+	c = e+1;
+	e = strchr (c, ':');
+      }
+      sprintf (name, "%s/%s", path, argv[0]);
+      status = check_file_exec (name);
+
+      if (status) {
+	if (realpath (name, path) == (char *) NULL) continue;
+	dir = pathname (path);
+	return (dir);
+      }
+    }
+  }
+  return ((char *) NULL);
+}
+
+/* make directory hierarchy, 0: success, -1: failure (just like mkdir) */
+int mkdirhier (char *path, int mode) {
+
+  char *tpath;
+
+  /* force addition of user exec/read/write */
+  mode |= S_IRWXU;
+  if (mkdir (path, mode)) {
+    if (errno == ENOENT) { 
+      tpath = pathname (path);
+      if (!mkdirhier (tpath, mode)) {
+	free (tpath);
+	if (mkdir (path, mode)) {
+	  return (-1);
+	} else {
+	  errno = 0;
+	  return (0);
+	}
+      } else {
+	free (tpath);
+	return (-1);
+      }
+    } else {
+      return (-1);
+    }
+  } else {
+    return (0);
+  }
+}
+
+char *getcwd_cfht (char *path, int size) {
+  
+  char *hostname, *newpath, *p;
+
+  path = getcwd (path, size);
+  
+  if (!strncmp (path, "/local/data", strlen ("/local/data"))) {
+    
+    ALLOCATE (hostname, char, size);
+
+    if (gethostname (hostname, size-1)) {
+      fprintf (stderr, "ERROR: can't get hostname\n");
+      free (hostname);
+      return ((char *) NULL);
+    }
+    if ((p = strchr (hostname, '.')) != (char *) NULL) *p = 0;
+    
+    ALLOCATE (newpath, char, size);
+    /* path might be just /local/data or /local/data/ */
+       
+    p = path + strlen ("/local/data/");
+    if (strlen (path) <= strlen ("/local/data/")) {
+      snprintf (newpath, size, "/data/%s", hostname);
+    } else {
+      snprintf (newpath, size, "/data/%s/%s", hostname, p);
+    }      
+
+    free (hostname);
+    strcpy (path, newpath);
+
+    free (newpath);
+
+  }
+
+  return (path);
+
+}
+
+void make_backup (char *filename) {
+
+  int status, cmode;
+  struct stat filestat;
+  char line[256];
+
+  status = stat (filename, &filestat);
+  if (status == 0) { /* file exists, make backup copy */
+    sprintf (line, "cp %s %s~", filename, filename);
+    status = system (line);
+    if (status) {
+      fprintf (stderr, "ERROR: unable to create %s~, exiting\n", filename);
+      exit (1);
+    }
+    cmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+    sprintf (line, "%s~", filename);
+    chmod (line, cmode);
+  }
+}
+
+/* fseek with timeout - 0.5 sec */
+int Fseek (FILE *f, long offset, int whence) {
+
+  int status, k;
+
+  status = fseek (f, offset, whence);
+  if (status == -1) {
+    for (k = 0; (k < 10) && ((status = fseek (f, 0, SEEK_SET)) == -1); k++) usleep (50000);
+    if (status == -1) {
+      return (0);
+    }
+  }
+  return (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/gaussj.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/gaussj.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/gaussj.c	(revision 22322)
@@ -0,0 +1,262 @@
+# include <ohana.h>
+
+// Gauss-Jordan elimination using full pivots based on Press et al's description.  Substantially
+// reworked for Ohana: major modifications to conform to C indexing, use a boolean to track the
+// completed pivot rows and catch the singular matrix early on.  Also, much cleaner control loops
+// than their implementation.  XXX this really needs to check on round-off errors (see version by
+// William Kahan
+int dgaussjordan (double **A, double **B, int N, int M) {
+
+  int *colIndex;
+  int *rowIndex;
+  int *pivot;
+  
+  int diag, col, row;
+
+  ALLOCATE (colIndex, int, N);
+  ALLOCATE (rowIndex, int, N);
+  ALLOCATE (pivot, int, N);
+  memset (pivot, 0, N*sizeof(int));
+
+  // determine underflow conditions
+  // double underFlow = DBL_MIN;
+# if (0)
+  double roundTest = 4.0;
+  roundTest /= 3.0;
+  roundTest -= 1.0;
+  double epsilon = fabs(((roundTest+roundTest) - 1.0) + roundTest);
+  double growth = 1.0;
+# endif
+
+  // Following the algorithm laid out by Press et al., we loop along the matrix diagonal,
+  // but we do not operate on the diagonal elements in order instead, we are looking for
+  // the current max element and operating on that diagonal element.  this is effectively
+  // column pivoting.  row pivoting is perfomed explicitly.  
+
+  for (diag = 0; diag < N; diag++) {
+
+    double maxval = 0.0;
+    int maxrow = 0;
+    int maxcol = 0;
+
+    // search for the next pivot
+    for (row = 0; row < N; row++) {
+      if (!finite(A[row][diag])) goto escape;
+
+      // if we have already operated on this row (pivot[row] is true), skip it
+      if (pivot[row]) continue;
+
+      // if we have not yet operated on this row (pivot[row] is false), look for pivot for this row
+      for (col = 0; col < N; col++) {
+	if (pivot[col]) continue;
+	if (fabs (A[row][col]) < maxval) continue;
+	maxval = fabs (A[row][col]);
+	maxrow = row;
+	maxcol = col;
+      }
+    }
+
+    // if pivot[maxcol] is set, we have already done this row: this implies a singular matrix
+    if (pivot[maxcol]) goto escape;
+    pivot[maxcol] = TRUE;
+
+    // if the selected pivot is off the diagonal, do a row swap
+    if (maxrow != maxcol) {
+      for (col = 0; col < N; col++) SWAP (A[maxrow][col], A[maxcol][col]);
+      for (col = 0; col < M; col++) SWAP (B[maxrow][col], B[maxcol][col]);
+    }
+    rowIndex[diag] = maxrow;
+    colIndex[diag] = maxcol;
+    if (A[maxcol][maxcol] == 0.0) goto escape;
+    // XXX Kahan replaces the 0.0 pivot with epsilon*(largest element in column) + underFlow
+
+    /* rescale by pivot reciprocal */
+    double tmpval = 1.0 / A[maxcol][maxcol];
+    A[maxcol][maxcol] = 1.0;
+    for (col = 0; col < N; col++) A[maxcol][col] *= tmpval;
+    for (col = 0; col < M; col++) B[maxcol][col] *= tmpval;
+    // XXX measure the pivot growth and trigger on over/under flow
+    // growth *= tmpval;
+    // fprintf (stderr, "column: %d, growth: %e, epsilon: %e\n", maxcol, growth, epsilon);
+
+    /* adjust the elements above the pivot */
+    for (row = 0; row < N; row++) {
+      if (row == maxcol) continue;
+      tmpval = A[row][maxcol];
+      A[row][maxcol] = 0.0;
+      for (col = 0; col < N; col++) A[row][col] -= A[maxcol][col]*tmpval;
+      for (col = 0; col < M; col++) B[row][col] -= B[maxcol][col]*tmpval;
+    }
+  }
+
+  // swap back the inverse matrix based on the row swaps above
+  for (col = N - 1; col >= 0; col--) {
+    if (rowIndex[col] != colIndex[col]) {
+      for (row = 0; row < N; row++) SWAP (A[row][rowIndex[col]], A[row][colIndex[col]]);
+    }
+  }
+
+  free (pivot);
+  free (rowIndex);
+  free (colIndex);
+  return (TRUE);
+
+escape:
+  free (pivot);
+  free (rowIndex);
+  free (colIndex);
+  return (FALSE);
+}
+
+int fgaussjordan (float **A, float **B, int N, int M) {
+
+  int *colIndex;
+  int *rowIndex;
+  int *pivot;
+  
+  int diag, col, row;
+
+  ALLOCATE (colIndex, int, N);
+  ALLOCATE (rowIndex, int, N);
+  ALLOCATE (pivot, int, N);
+  memset (pivot, 0, N*sizeof(int));
+
+  // determine underflow conditions
+  // float underFlow = FLT_MIN;
+# if (0)
+  float roundTest = 4.0;
+  roundTest /= 3.0;
+  roundTest -= 1.0;
+  float epsilon = fabs(((roundTest+roundTest) - 1.0) + roundTest);
+  float growth = 1.0;
+# endif
+
+  // we loop along the matrix diagonal, but we do not operate on the diagonal elements in
+  // order instead, we are looking for the current max element and operating on that
+  // diagonal element.  this is effectively column pivoting.  row pivoting is perfomed
+  // explicitly 
+
+  for (diag = 0; diag < N; diag++) {
+
+    float maxval = 0.0;
+    int maxrow = 0;
+    int maxcol = 0;
+
+    // search for the next pivot
+    for (row = 0; row < N; row++) {
+      if (!finite(A[row][diag])) goto escape;
+
+      // if we have already operated on this row (pivot[row] is true), skip it
+      if (pivot[row]) continue;
+
+      // if we have not yet operated on this row (pivot[row] is false), look for pivot for this row
+      for (col = 0; col < N; col++) {
+	if (pivot[col]) continue;
+	if (fabs (A[row][col]) < maxval) continue;
+	maxval = fabs (A[row][col]);
+	maxrow = row;
+	maxcol = col;
+      }
+    }
+
+    // if pivot[maxcol] is set, we have already done this row: this implies a singular matrix
+    if (pivot[maxcol]) goto escape;
+    pivot[maxcol] = TRUE;
+
+    // if the selected pivot is off the diagonal, do a row swap
+    if (maxrow != maxcol) {
+      for (col = 0; col < N; col++) SWAP (A[maxrow][col], A[maxcol][col]);
+      for (col = 0; col < M; col++) SWAP (B[maxrow][col], B[maxcol][col]);
+    }
+    rowIndex[diag] = maxrow;
+    colIndex[diag] = maxcol;
+    if (A[maxcol][maxcol] == 0.0) goto escape;
+    // XXX Kahan replaces the 0.0 pivot with epsilon*(largest element in column) + underFlow
+
+    /* rescale by pivot reciprocal */
+    float tmpval = 1.0 / A[maxcol][maxcol];
+    A[maxcol][maxcol] = 1.0;
+    for (col = 0; col < N; col++) A[maxcol][col] *= tmpval;
+    for (col = 0; col < M; col++) B[maxcol][col] *= tmpval;
+    // growth *= tmpval;
+    // fprintf (stderr, "column: %d, growth: %e, epsilon: %e\n", maxcol, growth, epsilon);
+
+    /* adjust the elements above the pivot */
+    for (row = 0; row < N; row++) {
+      if (row == maxcol) continue;
+      tmpval = A[row][maxcol];
+      A[row][maxcol] = 0.0;
+      for (col = 0; col < N; col++) A[row][col] -= A[maxcol][col]*tmpval;
+      for (col = 0; col < M; col++) B[row][col] -= B[maxcol][col]*tmpval;
+    }
+  }
+
+  // swap back the inverse matrix based on the row swaps above
+  for (col = N - 1; col >= 0; col--) {
+    if (rowIndex[col] != colIndex[col]) {
+      for (row = 0; row < N; row++) SWAP (A[row][rowIndex[col]], A[row][colIndex[col]]);
+    }
+  }
+
+  free (pivot);
+  free (rowIndex);
+  free (colIndex);
+  return (TRUE);
+
+escape:
+  free (pivot);
+  free (rowIndex);
+  free (colIndex);
+  return (FALSE);
+}
+
+
+/* Gauss-Jordan Inversion from William Kahan in Basic
+500 ' Gauss-Jordan Matrix Inversion     X = A^(-1) in IBM PC BASIC
+510 ' including checks for excessive    growth despite row-pivoting,
+520 '          and adjustments for zero pivots to avoid .../0 .
+530 ' DIM A(N,N), X(N,N), P(N) ...      are assumed.
+540     DEFINT I-N ' ... integer variables; the rest are REAL.
+550   '
+560   ' First determine levels of roundoff and over/underflow.
+570     UFL = 5.9E-39 ' ... = max{ under, 1/over}flow thresholds.
+580        G=4 : G=G/3 : G=G-1      ' ... = 1/3 + roundoff in 4/3
+590     EPS = ABS( ((G+G) - 1) + G ) ' ... = roundoff level.
+600     G = 1 ' ... will record pivot-growth factor
+610   '
+620   ' Copy A to X and record each column's biggest element.
+630     FOR J=1 TO N : P(J)=0
+640          FOR I=1 TO N : T = A(I,J) : X(I,J) = T : T = ABS(T)
+650                IF T > P(J) THEN P(J) = T
+660                NEXT I : NEXT J
+670   '
+680     FOR K=1 TO N :' ... perform elimination upon column K .
+690          Q=0 : J=K : ' ... search for Kth pivot ...
+700          FOR I=K TO N
+710                T=ABS(X(I,K)) : IF T>Q THEN Q=T : J=I
+720                NEXT I
+730          IF Q=0 THEN Q = EPS*P(K) + UFL : X(K,K)=Q
+740          IF P(K)>0 THEN Q=Q/P(K) : IF Q>G THEN G=Q
+750          IF G<=8*K THEN GOTO 790
+760      PRINT "Growth factor g = ";G;" exceeds ";8*K;" ; try"
+770      PRINT "moving A's column ";K;" to col. 1 to reduce g ."
+780            STOP ' ... or go back to re-order A's columns.
+790         P(K)=J ' ... record pivotal row exchange, if any.
+800         IF J=K THEN GOTO 830 ' ... Don't bother to swap.
+810             FOR L=1 TO N : Q=X(J,L) : X(J,L)=X(K,L)
+820                              X(K,L)=Q : NEXT L
+830         Q = X(K,K) : X(K,K) = 1
+840         FOR J=1 TO N : X(K,J) = X(K,J)/Q : NEXT J
+850         FOR I=1 TO N : IF I=K THEN GOTO 890
+860              Q = X(I,K) : X(I,K) = 0
+870              FOR J=1 TO N
+880                   X(I,J) = X(I,J) - X(K,J)*Q : NEXT J
+890              NEXT I : NEXT K
+900 '
+910 FOR K=N-1 TO 1 STEP -1 ' ... unswap columns of X
+920         J=P(K) : IF J=K THEN GOTO 950
+930         FOR I=1 TO N : Q=X(I,K) : X(I,K)=X(I,J)
+940                          X(I,J)=Q : NEXT I
+950         NEXT K
+960 RETURN
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/glockfile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/glockfile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/glockfile.c	(revision 22322)
@@ -0,0 +1,242 @@
+# include <ohana.h>
+
+FILE *fsetlockfile (char *filename, double timeout, int type, int *state) {
+  
+  int i, nbytes, status;
+  char *lockname, buffer[64];
+  char *file, *path, mode[3];
+  int fd;
+  FILE *f, *flock;
+  struct stat filestat;
+  struct flock filelock;
+  struct timeval now, then;
+
+  f = flock = NULL;
+  file = path = lockname = NULL;
+
+  /* define lock type */
+  filelock.l_start  = 0;
+  filelock.l_whence = SEEK_SET;
+  filelock.l_len    = 0;
+  filelock.l_pid    = getpid ();
+  switch (type) {
+  case LCK_HARD:
+  case LCK_XCLD:
+    filelock.l_type   = F_WRLCK;  /* set an exclusive lock */
+    strcpy (mode, "r+");
+    break;
+  case LCK_SOFT:
+    filelock.l_type   = F_RDLCK;  /* set a shared lock */
+    strcpy (mode, "r");
+    break;
+  default:
+    *state = LCK_INVALID;
+    goto failure;
+  }
+
+  /* check if file exists */
+  status = stat (filename, &filestat);
+  if ((status == -1) && (errno == ENOENT)) {
+    /* if soft, return LCK_ACCESS */
+    if (type == LCK_SOFT) {
+      *state = LCK_MISSING;
+      goto failure;
+    } 
+    /* otherwise, we need to be able to create file */ 
+    strcpy (mode, "w+");
+  }
+  
+  /* try to open file (create if it does not exist) */
+  f = fopen (filename, mode);
+  if (f == NULL) {
+    *state = LCK_ACCESS;
+    goto failure;
+  }
+  fd = fileno (f);
+
+  /* we first try to set a FS level lock on the file */
+  gettimeofday (&then, (void *) NULL);
+  while (1) {
+    /* try to lock file */
+    if (fcntl (fd, F_SETLK, &filelock) != -1) goto got_lock;
+
+    /* check for timeout */
+    gettimeofday (&now, (void *) NULL);
+    if (DTIME (now, then) > timeout) {
+      *state = LCK_TIMEOUT;
+      goto failure;
+    }
+    usleep (10000); /* 10 ms is min utime */
+  }
+got_lock:
+
+  /* check if blocking hardlock exists */
+  if (type == LCK_HARD) {
+    /* set up name to lockfile */
+    path = pathname (filename);
+    file = filebasename (filename);
+    ALLOCATE (lockname, char, strlen (path) + strlen (file) + 10);
+    sprintf (lockname, "%s/.%s.lck", path, file);
+
+    status = stat (lockname, &filestat);
+    if ((status == -1) && (errno == ENOENT)) {
+      strcpy (mode, "w+");
+    } else {
+      strcpy (mode, "r+");
+    }
+
+    /* try to open lockfile */
+    flock = fopen (lockname, mode);
+    if (flock == NULL) {
+      *state = LCK_HARDOPEN;
+      goto failure;
+    }
+    fd = fileno (flock);
+    
+    /* try a few times to lock lockfile (locking the lockfile before checking
+       the contents will ensure the data is synced across NFS) */
+    for (i = 0; (i < 20) && (fcntl (fd, F_SETLK, &filelock) == -1); i++) usleep (10000);
+    if (i == 20) {
+      *state = LCK_HARDLOCK;
+      goto failure;
+    }
+    
+    /* we've locked the lockfile, now read the contents */
+    nbytes = fread (buffer, 1, 4, flock);
+    if (nbytes == 4) { /* lock file has a word in it */
+      buffer[4] = 0;
+      if (!strcmp (buffer, "BUSY")) { 
+	*state = LCK_HARDLOCKHARD;
+	goto failure;
+      }
+      /* note that we don't care if the lockfile has random garbage */
+    }
+  }
+    
+  /* set blocking hardlock */
+  if (type == LCK_HARD) {
+    /* we've really got the lock, write BUSY to protect it */
+    fseek (flock, 0, SEEK_SET);
+    nbytes = fprintf (flock, "BUSY\n");
+
+    /* 
+    if (nbytes != 5) {
+      *state = LCK_HARDCLOSE;
+      goto failure;
+      } */
+    
+    /* now fclose lockfile (also unlocks file) */
+    if (fclose (flock)) {
+      *state = LCK_HARDCLOSE;
+      goto failure;
+    }
+    flock = NULL;
+  }
+
+  /* check if file is empty or not */
+  fd = fileno (f);
+  if (fstat (fd, &filestat)) {
+    *state = LCK_UNKNOWN;
+  } else {
+    if (filestat.st_size == 0) {
+      *state = LCK_EMPTY;
+    } else {
+      *state = LCK_FULL;
+    }
+  }
+
+  if (path     != NULL) free (path);
+  if (file     != NULL) free (file);
+  if (lockname != NULL) free (lockname);
+  if (flock    != NULL) fclose (flock);
+  return (f);
+
+failure:
+  if (f        != NULL) fclose (f);
+  if (flock    != NULL) fclose (flock);
+  if (path     != NULL) free (path);
+  if (file     != NULL) free (file);
+  if (lockname != NULL) free (lockname);
+  return (NULL);
+}
+  
+/* clears lock. removes hardlock even if file pointer is not supplied */
+int fclearlockfile (char *filename, FILE *f, int type, int *state) {
+
+  int i, fd, status, nbytes;
+  char *lockname, *path, *file;
+  FILE *flock;
+  struct stat filestat;
+  struct flock filelock;
+
+  file = path = lockname = (char *) NULL;
+
+  /* define lock */
+  filelock.l_type   = F_UNLCK;
+  filelock.l_start  = 0;
+  filelock.l_whence = SEEK_SET;
+  filelock.l_len    = 0;
+  filelock.l_pid    = getpid ();
+
+  /* first clear hard lockfile */
+  if (type == LCK_HARD) {
+
+    /* define lockfile */
+    path = pathname (filename);
+    file = filebasename (filename);
+    ALLOCATE (lockname, char, strlen (path) + strlen (file) + 10);
+    sprintf (lockname, "%s/.%s.lck", path, file);
+    
+    /* check for lockfile existance */
+    status = stat (lockname, &filestat);
+    if (status == -1) {
+      *state = LCK_HARDLOCK;
+      goto failure;
+    } 
+  
+    /* try to open lockfile */
+    flock = fopen (lockname, "w+");
+    if (flock == NULL) {
+      *state = LCK_HARDOPEN;
+      goto failure;
+    }
+    fd = fileno (flock);
+
+    /* try a few times to lock lockfile */
+    filelock.l_type = F_WRLCK;
+    for (i = 0; (i < 20) && (fcntl (fd, F_SETLK, &filelock) == -1); i++) usleep (10000);
+    if (i == 20) {
+      *state = LCK_HARDLOCKHARD;
+      goto failure;
+    }
+
+    /* set value to IDLE */
+    fseek (flock, 0, SEEK_SET);
+    nbytes = fprintf (flock, "IDLE\n");
+    if (nbytes != 5) {
+      *state = LCK_HARDCLOSE;
+      goto failure;
+    }
+
+    if (fclose (flock)) {
+      *state = LCK_HARDCLOSE;
+      goto failure;
+    }
+    flock = NULL;
+  }
+  
+  /* now unlock the file */
+  fclose (f);
+
+  *state = LCK_UNLOCK;
+  if (path != NULL)     free (path);
+  if (file != NULL)     free (file);
+  if (lockname != NULL) free (lockname);
+  return (1);
+
+failure:
+  if (path != NULL)     free (path);
+  if (file != NULL)     free (file);
+  if (lockname != NULL) free (lockname);
+  return (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/ohana_allocate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/ohana_allocate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/ohana_allocate.c	(revision 22322)
@@ -0,0 +1,292 @@
+# include <stdio.h>
+# include <stdlib.h>
+# include <stdarg.h>
+
+/* need an internal version that does not use ohana_memory functions */
+# define FALSE 0 
+# define TRUE 1
+# define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+# define OHANA_ALLOCATE(X,T,S)  \
+  X = (T *) malloc ((unsigned)(MAX(((S)*((int)sizeof(T))),1))); \
+  if (X == NULL) { \
+    fprintf(stderr,"failed malloc at %d in %s\n", __LINE__, __FILE__);\
+    abort(); } 
+# define OHANA_REALLOCATE(X,T,S) \
+  X = (T *) realloc(X,(unsigned)(MAX(((S)*((int)sizeof(T))),1))); \
+  if (X == NULL) { \
+    fprintf(stderr,"failed realloc at %d in %s\n", __LINE__, __FILE__);\
+    abort(); }
+# define OHANA_CHECK_REALLOCATE(X,T,S,N,D) \
+  if ((N) >= (S)) { \
+    S += D; \
+    X = (T *) realloc(X,(unsigned)(MAX(((S)*((int)sizeof(T))),1))); \
+    if (X == NULL) { \
+      fprintf(stderr,"failed realloc increment at %d in %s\n", __LINE__, __FILE__);\
+      abort(); } }
+# define OHANA_FREE(X) free(X)
+
+# define STATE_ALLOC    0
+# define STATE_REALLOC  1
+# define STATE_FREE     2
+# define STATE_EXTERNAL 3
+
+typedef struct {
+  char *file;
+  int   line;
+  void *ptr;   // location of externally visible entry
+  int   size;
+  int   state;
+} Memlist;
+
+// XXX consider fixing the memory tracking model (list?)
+// static long *memsort;
+// static long *memhash;
+static Memlist *memlist = NULL;
+int Nmemlist = 0;
+int NMEMLIST = 0;
+int NMEMBYTE = 0;
+
+void ohana_meminit () {
+  Nmemlist = 0;
+  NMEMLIST = 1000;
+  OHANA_ALLOCATE (memlist, Memlist, NMEMLIST);
+  // OHANA_ALLOCATE (memsort, long, NMEMLIST);
+  // OHANA_ALLOCATE (memhash, long, NMEMLIST);
+  NMEMBYTE = sizeof(size_t);
+}
+
+void ohana_memabort (char *format, ...) {
+  va_list argp;  
+
+  va_start (argp, format);
+  vfprintf (stderr, format, argp);
+  va_end (argp);
+  abort();
+}
+
+void *ohana_malloc (char *file, int line, int Nelem, size_t esize) {
+
+  void *ptr, *new;
+  int size;
+  size_t *marker;
+
+  if (memlist == NULL) ohana_meminit ();
+
+  Nelem = MAX (1, Nelem);
+  size = Nelem * esize;
+
+  new = malloc (size + 2*NMEMBYTE); /* 2 extra locations to save endposts */
+  if (new == NULL) ohana_memabort ("failed to allocate memory (%s, %d)\n", file, line);
+  ptr = new + NMEMBYTE;
+
+  marker = (size_t *) new;
+  *marker = 0xdeadbeef;
+  marker = (size_t *)(new + size + NMEMBYTE);
+  *marker = 0xdeadbeef;
+
+  /* new memory, add to stack */
+  memlist[Nmemlist].ptr  = ptr;
+  memlist[Nmemlist].file = file;
+  memlist[Nmemlist].line = line;
+  memlist[Nmemlist].size = size;
+  memlist[Nmemlist].state = STATE_ALLOC;
+
+  // before each free, we sort this pair to speed up searching
+  // memsort[Nmemlist] = ptr;
+  // memhash[Nmemlist] = Nmemlist;
+
+  Nmemlist ++;
+  if (Nmemlist == NMEMLIST) {
+    NMEMLIST += 1000;
+    OHANA_REALLOCATE (memlist, Memlist, NMEMLIST);
+  }
+  return (ptr);
+}
+
+void *ohana_realloc (char *file, int line, void *in, int Nelem, size_t esize) {
+
+  int i, size;
+  void *ptr, *ref, *new;
+  size_t *marker;
+
+  if (memlist == NULL) ohana_memabort ("REALLOCATE before ALLOCATE");
+
+  Nelem = MAX (1, Nelem);
+  size = Nelem * esize;
+
+  ref = in - NMEMBYTE;
+
+  /* find old entry, update ptr, file, line */
+  for (i = 0; i < Nmemlist; i++) {
+    if (memlist[i].state == STATE_FREE) continue;
+    if (memlist[i].ptr == in) {
+
+      if (memlist[i].state == STATE_EXTERNAL) ohana_memabort ("ERROR: realloc of external memory");
+
+      /* ask for new memory */
+      new = realloc (ref, size + 2*NMEMBYTE); /* 2 extra slots to save endposts */
+      if (new == NULL) ohana_memabort ("failed to reallocate memory (%s, %d)\n", file, line);
+      ptr = new + NMEMBYTE;
+      
+      /* set the marker */
+      marker = (size_t *) new;
+      *marker = 0xdeadbeef;
+      marker = (size_t *)(ptr + size);
+      *marker = 0xdeadbeef;
+
+      /* otherwise, update memory new location */
+      memlist[i].ptr = ptr;
+      memlist[i].size = size;
+      memlist[i].state = STATE_REALLOC;
+
+      /* if new memory in new location, update references */
+      if (ptr != in) {
+	memlist[i].file = file;
+	memlist[i].line = line;
+      }
+
+      return (ptr);
+    }
+  }
+  ohana_memabort ("allocated memory not found for realloc (%s, %d)\n", file, line);
+  return (NULL);
+}
+
+// this is very slow.  should we speed this up by indexing on the ptr?
+void ohana_free (char *file, int line, void *in) {
+
+  int i;
+
+  if (memlist == NULL) ohana_memabort ("FREE before ALLOCATE");
+
+  /* find old entry, set state */
+  for (i = 0; i < Nmemlist; i++) {
+    if (memlist[i].state == STATE_FREE) continue;
+    if (memlist[i].ptr == in) {
+      memlist[i].state = STATE_FREE;
+      return;
+    }
+  }
+
+  /* find already freed examples */
+  for (i = 0; i < Nmemlist; i++) {
+    if (memlist[i].ptr == in) {
+      fprintf (stderr, "used here: %4d %s %d\n", i, memlist[i].file, memlist[i].line);
+    }
+  }
+
+  ohana_memabort ("allocated memory not found for free (%s, %d : %x)\n", file, line, in);
+  return;
+}
+
+/* register externally allocated memory with ohana memory manager */
+void ohana_memregister_func (char *file, int line, void *ptr) {
+
+  if (memlist == NULL) ohana_meminit ();
+
+  /* add to stack */
+  memlist[Nmemlist].ptr  = ptr;
+  memlist[Nmemlist].file = file;
+  memlist[Nmemlist].line = line;
+  memlist[Nmemlist].state = STATE_EXTERNAL;
+  Nmemlist ++;
+  if (Nmemlist == NMEMLIST) {
+    NMEMLIST += 1000;
+    OHANA_REALLOCATE (memlist, Memlist, NMEMLIST);
+  }
+  return;
+}
+
+void ohana_memcheck_func (int allmemory) {
+
+  int i, j, next, prev, header;
+  size_t *marker;
+  char top, bottom;
+
+  if (Nmemlist == 0) {
+    fprintf (stderr, "no memory allocated\n");
+    return;
+  }
+
+  header = FALSE;
+  fprintf (stderr, "checking %d memory blocks\n", Nmemlist);
+  for (i = 0; i < Nmemlist; i++) {
+    if (memlist[i].state == STATE_EXTERNAL) continue;
+    if ((allmemory == 0) && (memlist[i].state == STATE_FREE)) continue;
+
+    top = bottom = 'Y';
+    marker = (size_t *)(memlist[i].ptr - NMEMBYTE);
+    if (*marker == 0xdeadbeef) bottom = 'N';
+    marker = (size_t *)(memlist[i].ptr + memlist[i].size);
+    if (*marker == 0xdeadbeef) top = 'N';
+    
+    if ((top == 'N') && (bottom == 'N')) continue;
+    if (!header) {
+      fprintf (stderr, "         Nmem start end file            line\n");
+      header = TRUE;
+    }
+    fprintf (stderr, "corrupt:  %3d   %c    %c  %-15s %3d\n", i, bottom, top, memlist[i].file, memlist[i].line);
+    prev = next = -1;
+    for (j = 0; j < Nmemlist; j++) {
+      if (memlist[j].ptr < memlist[i].ptr) {
+	if (prev == -1) prev = j;
+	if (memlist[j].ptr > memlist[prev].ptr) prev = j;
+      }
+      if (memlist[j].ptr > memlist[i].ptr) {
+	if (next == -1) next = j;
+	if (memlist[j].ptr < memlist[next].ptr) next = j;
+      }
+    }	  
+    if (prev == -1) {
+      fprintf (stderr, "no previous memory block\n");
+    } else {
+      fprintf (stderr, "prev:     %3d           %-15s %3d\n", prev, memlist[prev].file, memlist[prev].line);
+    }
+    if (next == -1) {
+      fprintf (stderr, "no next memory block\n");
+    } else {
+      fprintf (stderr, "next:     %3d           %-15s %3d\n", next, memlist[next].file, memlist[next].line);
+    }
+  }
+  return;
+}
+
+void ohana_memdump_func (int allmemory) {
+
+  int i, Ns[3], N;
+  char S[3];
+
+  if (Nmemlist == 0) {
+    fprintf (stderr, "no memory allocated\n");
+    return;
+  }
+
+  S[0] = 'A';
+  S[1] = 'R';
+  S[2] = 'F';
+  S[3] = 'X';
+  Ns[0] = Ns[1] = Ns[2] = Ns[3] = 0;
+  N = 0;
+
+  for (i = 0; i < Nmemlist; i++) {
+    if (memlist[i].state == STATE_ALLOC)    N = 0;
+    if (memlist[i].state == STATE_REALLOC)  N = 1;
+    if (memlist[i].state == STATE_FREE)     N = 2;
+    if (memlist[i].state == STATE_EXTERNAL) N = 2;
+    Ns[N] ++;
+    if ((allmemory == 0) && (memlist[i].state == STATE_FREE)) continue;
+    if (memlist[i].state == STATE_EXTERNAL) {
+      fprintf (stderr, "%3d %4d %lx : %lx - extern (extern     extern) %c %s %d\n", 
+	       Ns[N], i, (long)memlist[i].ptr, (long)memlist[i].ptr, 
+	       S[N], memlist[i].file, memlist[i].line);
+    } else {
+      fprintf (stderr, "%3d %4d %lx : %lx - %lx (%lx %lx) %c %s %d\n", 
+	       Ns[N], i, (long)memlist[i].ptr, (long)(memlist[i].ptr - NMEMBYTE), (long)(memlist[i].ptr + memlist[i].size + NMEMBYTE), 
+	       *(long *)(memlist[i].ptr - NMEMBYTE), *(long *)(memlist[i].ptr + memlist[i].size), 
+	       S[N], memlist[i].file, memlist[i].line);
+    }
+  }
+  fprintf (stderr, "Nused: %d (Nalloc: %d, Nrealloc: %d), Nfree: %d\n", 
+	   Ns[0]+Ns[1], Ns[0], Ns[1], Ns[2]);
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/sorts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/sorts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/sorts.c	(revision 22322)
@@ -0,0 +1,105 @@
+# include <ohana.h>
+
+/* various widely-used sort functions for specific sets of types */
+
+void dsort (double *value, int N) {
+
+# define SWAPFUNC(A,B){ double tmp = value[A]; value[A] = value[B]; value[B] = tmp; }
+# define COMPARE(A,B)(value[A] < value[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void fsort (float *value, int N) {
+
+# define SWAPFUNC(A,B){ float tmp = value[A]; value[A] = value[B]; value[B] = tmp; }
+# define COMPARE(A,B)(value[A] < value[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void fsortpair (float *X, float *Y, int N) {
+
+# define SWAPFUNC(A,B){ float tmp; \
+  tmp = X[A]; X[A] = X[B]; X[B] = tmp; \
+  tmp = Y[A]; Y[A] = Y[B]; Y[B] = tmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void dsortpair (double *X, double *Y, int N) {
+
+# define SWAPFUNC(A,B){ double tmp; \
+  tmp = X[A]; X[A] = X[B]; X[B] = tmp; \
+  tmp = Y[A]; Y[A] = Y[B]; Y[B] = tmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+// sort two int vectors by first vector
+void isortpair (int *X, int *Y, int N) {
+
+# define SWAPFUNC(A,B){ int tmp; \
+  tmp = X[A]; X[A] = X[B]; X[B] = tmp; \
+  tmp = Y[A]; Y[A] = Y[B]; Y[B] = tmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void fsortthree (float *X, float *Y, float *Z, int N) {
+
+# define SWAPFUNC(A,B){ float tmp; \
+  tmp = X[A]; X[A] = X[B]; X[B] = tmp; \
+  tmp = Y[A]; Y[A] = Y[B]; Y[B] = tmp; \
+  tmp = Z[A]; Z[A] = Z[B]; Z[B] = tmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void dsortthree (double *X, double *Y, double *Z, int N) {
+
+# define SWAPFUNC(A,B){ double tmp; \
+  tmp = X[A]; X[A] = X[B]; X[B] = tmp; \
+  tmp = Y[A]; Y[A] = Y[B]; Y[B] = tmp; \
+  tmp = Z[A]; Z[A] = Z[B]; Z[B] = tmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/string.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/string.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/string.c	(revision 22322)
@@ -0,0 +1,240 @@
+# include <ohana.h>
+
+/* Strip WHITESPACE from the start and end of STRING. */
+int stripwhite (char *string) {
+
+  int i;
+
+  if (string == (char *) NULL) return (FALSE);
+
+  for (i = 0; OHANA_WHITESPACE (string[i]); i++);
+  if (i) memmove (string, string + i, strlen(string+i)+1);
+  for (i = strlen (string) - 1; (i > 0) && OHANA_WHITESPACE (string[i]); i--);
+  string[++i] = 0;
+  return (i);
+
+}
+
+/* compare two strings either as strings, or as numbers if both are
+   pure numeric strings (base 10) */
+int strnumcmp (char *str1, char *str2) {
+
+  char *end1;
+  char *end2;
+  int num1, num2;
+  int value;
+ 
+  value = FALSE;
+  num1 = strtol (str1, &end1, 10);
+  num2 = strtol (str2, &end2, 10);
+
+  if (!end1[0] && !end2[0] && (num1 == num2)) {
+    value = TRUE;
+  }
+  if (!strcmp (str1, str2)) {
+    value = TRUE;
+  }
+  
+  return (value);
+}
+
+/* create a new string from this string */
+char *strcreate (char *string) {
+
+  char *line;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  
+  ALLOCATE (line, char, MAX (1, strlen(string)) + 1);
+  line = strcpy (line, string);
+
+  return (line);
+}
+
+/* create a new string of length n from this string */
+char *strncreate (char *string, int n) {
+
+  char *line;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (n < 0) return NULL;
+  
+  ALLOCATE (line, char, n + 1);
+  memcpy (line, string, n);
+  line[n] = 0;
+  return (line);
+}
+
+// replace a single entry of 'match' in the string with 'with'
+// (quick-and-dirty regex for a common case...)
+char *strsubs (char *string, char *match, char *with) {
+
+  int N1, N2, N3;
+  char *root;
+  char *out;
+  char *ext;
+
+  if (string == NULL) return NULL;
+  if (match == NULL) return NULL;
+  if (with == NULL) return NULL;
+
+  root = strstr (string, match);
+  if (root == NULL) {
+    return (strcreate (string));
+  }
+  N1 = root - string;
+
+  N2 = strlen (with);
+
+  ext = root + strlen(match);
+  N3 = strlen (ext);
+
+  ALLOCATE (out, char, N1 + N2 + N3 + 1);
+
+  strncpy (out, string, N1);
+  strncpy (&out[N1], with, N2);
+  strncpy (&out[N1+N2], ext, N3);
+  out[N1+N2+N3] = 0;
+
+  return out;
+}
+
+# if 0
+// replace a single entry of 'match' in the string with 'with'
+// (quick-and-dirty regex for a common case...)
+char *strrsubs (char *string, char *match, char *with) {
+
+  char *root;
+  char *out;
+
+  if (string == NULL) return NULL;
+  if (match == NULL) return NULL;
+  if (with == NULL) return NULL;
+
+  root = strstr (string, match);
+  if (root == NULL) return NULL;
+
+  ext = string + strlen (match);
+  ALLOCATE (out, char *, strlen(with) + strlen(ext) + 1);
+  strcpy (out, with);
+  strcat (out, ext);
+
+  return out;
+}
+# endif
+
+int scan_line (FILE *f, char *line) {
+
+  int i, status;
+  char c;
+  
+  status = EOF + 1;
+  
+  for (i = 0, c = 0; (c != '\n') && (status != EOF); i++) {
+    status = fscanf (f, "%c", &c);
+    line[i] = c;
+  }
+  line[i - 1] = 0;  /* this could make things crash! */
+
+  if (i > 1) {
+    status = EOF + 1;
+  }
+
+  return (status);
+}
+
+char *_parse_nextword (char *string) {
+
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (; isspace (*string); string++);
+  for (; (*string != 0) && !isspace (*string); string++);
+  for (; isspace (*string); string++);
+  return (string);
+}
+
+int dparse (double *X, int NX, char *line) {
+
+  int i;
+  char *word;
+  char *ptr;
+
+  word = line;
+  for (i = 0; i < NX - 1; i++)
+    word = _parse_nextword (word);
+
+  *X = strtod (word, &ptr);
+  if (ptr == word) return (FALSE);
+  if (word[0] == '-') return (-1);
+  return (1);
+}
+
+int fparse (float *X, int NX, char *line) {
+
+  int i;
+  char *word;
+  char *ptr;
+
+  word = line;
+  for (i = 0; i < NX - 1; i++)
+    word = _parse_nextword (word);
+
+  *X = strtod (word, &ptr);
+  if (ptr == word) return (FALSE);
+  if (word[0] == '-') return (-1);
+  return (1);
+}
+
+int get_argument (int argc, char **argv, char *arg) {
+
+  int i;
+
+  for (i = 0; i < argc; i++) {
+    if (!strcmp(argv[i], arg))
+      return (i);
+  }
+  
+  return ((int)NULL);
+}
+
+int remove_argument (int N, int *argc, char **argv) {
+
+  int i;
+
+  if ((N != (int)NULL) && (N != 0)) {
+    (*argc)--;
+    for (i = N; i < *argc; i++) {
+      argv[i] = argv[i+1];
+    }
+  }
+
+  return (N);
+}
+
+void uppercase (char *string) {
+
+  int i;
+    
+  for (i = 0; i < strlen (string); i++) string[i] = toupper (string[i]);
+
+}
+
+/* expect a line of the form "$Name: not supported by cvs2svn $", strip out contents */
+char *strip_version (char *input) {
+
+  char *p, *q;
+
+  p = strstr (input, "$Name:");
+  if (p == NULL) return (strcreate ("NONE"));
+
+  q = strcreate (input + 6);
+  p = strrchr (q, '$');
+  if (p != NULL) *p = 0;
+  stripwhite (q);
+  if (*q == 0) {
+    free (q);
+    q = strcreate ("NONE");
+  }
+
+  return (q);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/time.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/time.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/time.c	(revision 22322)
@@ -0,0 +1,435 @@
+# include "ohana.h"
+
+/***** convert [-]00:00:00 to 0.0000 ****/
+int ohana_dms_to_ddd (double *Value, char *string) {
+  
+  int valid, neg, status;
+  double tmp, value;
+  char *p1, *p2, *px;
+
+  valid = FALSE; 
+  neg = FALSE;
+  stripwhite (string);
+  p1 = string;
+  px = string + strlen(string);
+
+  if (string[0] == '-') { 
+    valid = TRUE; 
+    neg = TRUE;
+    p1 = &string[1];
+  }
+  if (string[0] == '+') { 
+    valid = TRUE; 
+    neg = FALSE;
+    p1 = &string[1];
+  }
+  if (isdigit(string[0])) { 
+    valid = TRUE;
+    p1 = &string[0];
+  }
+  if (!valid) { return (FALSE); }
+
+  status = 1;
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) return (FALSE); /* entry not a number: +fred */
+  value = tmp;
+  if (p2 == px) goto escape;    /* entry only number: +1.0 */ 
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;    /* entry not a number: +1:fred */
+  status = 2;
+  value += tmp / 60.0;
+  if (p2 == px) goto escape;    /* entry only number: +1:1 */
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;    /* entry not a number: +1:1:fred */
+  value += tmp / 3600.0;
+
+escape:
+  if (neg) {
+    value *= -1;
+  }
+  *Value = value;
+
+  return (status);
+}
+
+/**********/
+int ohana_str_to_radec (double *ra, double *dec, char *str1, char *str2) {
+
+  double Ra, Dec;
+
+  *ra = *dec = 0;
+  switch (ohana_dms_to_ddd (&Ra, str1)) {
+    case 0:
+      fprintf (stderr, "syntax error in RA\n");
+      return (FALSE);
+    case 1:
+      break;
+    case 2:
+      Ra = Ra * 15;
+      break;
+  }
+  switch (ohana_dms_to_ddd (&Dec, str2)) {
+    case 0:
+      fprintf (stderr, "syntax error in DEC\n");
+      return (FALSE);
+    case 1:
+    case 2:
+      break;
+  }
+  *ra = Ra;
+  *dec = Dec;
+  return (TRUE);
+}
+
+/**********/
+int ohana_chk_time (char *line) {
+
+  char *p1, *p2;
+  double tmp;
+  int mode;
+
+  p1 = line;
+  tmp = strtod (p1, &p2);
+  mode = TIME_DATE;
+  if (p2 == p1 + strlen (p1) - 1) {
+    if (*p2 == 'd') {
+      mode = TIME_DAYS;
+    }
+    if (*p2 == 'h') {
+      mode = TIME_HOURS;
+    }
+    if (*p2 == 'm') {
+      mode = TIME_MINUTES;
+    }
+    if (*p2 == 's') {
+      mode = TIME_SECONDS;
+    }
+    if (*p2 == 'j') {
+      mode = TIME_JD;
+    }
+    if (*p2 == 'J') {
+      mode = TIME_MJD;
+    }
+  }
+  return (mode);
+}
+
+/**********/
+int ohana_str_to_time (char *line, time_t *second) {
+  
+  char *tmpline;
+  struct tm *gmt;
+  struct timeval now;
+  double jd;
+  time_t tsec;
+
+  if (!strcasecmp (line, "NOW")) {
+    gettimeofday (&now, (struct timezone *) NULL);
+    *second = now.tv_sec;
+    return (TRUE);
+  }
+    
+  if (!strncasecmp (line, "TODAY", 5)) {
+    gettimeofday (&now, (struct timezone *) NULL);
+    if (line[5]) { /* line has extra data (ie, hh:mm:ss) */
+      tsec = now.tv_sec;
+      ALLOCATE (tmpline, char, 64);
+      gmt   = gmtime (&tsec);
+      sprintf (tmpline, "%04d/%02d/%02d,%s", 1900 + gmt[0].tm_year, gmt[0].tm_mon+1, gmt[0].tm_mday, &line[6]);
+      *second = ohana_date_to_sec (tmpline);
+      free (tmpline);
+      return (TRUE);
+    } else {
+      *second = 86400 * ((int)(now.tv_sec / 86400));
+      return (TRUE); 
+    }
+  }
+    
+  switch (ohana_chk_time (line)) {
+    case 0:
+      return (FALSE);
+    case TIME_DATE:
+      *second = ohana_date_to_sec (line);
+      return (TRUE);
+    case TIME_DAYS:
+      *second = strtod (line, 0) * 86400.0;
+      return (TRUE);
+    case TIME_HOURS:
+      *second = strtod (line, 0) * 3600.0;
+      return (TRUE);
+    case TIME_MINUTES:
+      *second = strtod (line, 0) * 60.0;
+      return (TRUE);
+    case TIME_SECONDS:
+      *second = strtod (line, 0);
+      return (TRUE);
+    case TIME_JD:
+      jd = strtod (line, 0);
+      *second = ohana_jd_to_sec (jd);
+      return (TRUE);
+    case TIME_MJD:
+      jd = strtod (line, 0);
+      *second = ohana_mjd_to_sec (jd);
+      return (TRUE);
+  }
+  return (FALSE);
+}
+
+
+/**********/
+int ohana_str_to_dtime (char *line, double *second) {
+  
+  switch (ohana_chk_time (line)) {
+    case 0:
+    case TIME_JD:
+    case TIME_MJD:
+    case TIME_DATE:
+      return (FALSE);
+    case TIME_DAYS:
+      *second = strtod (line, 0) * 86400.0;
+      return (TRUE);
+    case TIME_HOURS:
+      *second = strtod (line, 0) * 3600.0;
+      return (TRUE);
+    case TIME_MINUTES:
+      *second = strtod (line, 0) * 60.0;
+      return (TRUE);
+    case TIME_SECONDS:
+      *second = strtod (line, 0);
+      return (TRUE);
+  }
+  return (FALSE);
+}
+
+/**********/
+double ohana_sec_to_jd (time_t second) {
+
+  double jd;
+  
+  jd = second/86400.0 + 2440587.5;
+  return (jd);
+}
+
+/**********/
+time_t ohana_jd_to_sec (double jd) {
+
+  time_t second;
+
+  second = (jd - 2440587.5)*86400;
+  return (second);
+}
+
+/**********/
+double ohana_sec_to_mjd (time_t second) {
+
+  double mjd;
+  
+  mjd = second/86400.0 + 40587.0;
+  return (mjd);
+}
+
+/**********/
+time_t ohana_mjd_to_sec (double mjd) {
+
+  time_t second;
+
+  second = (mjd - 40587.0)*86400;
+  return (second);
+}
+
+/**********/
+char *ohana_sec_to_date (time_t second) {
+  
+  struct tm *gmt;
+  char *line;
+  
+  ALLOCATE (line, char, 64);
+  gmt   = gmtime (&second);
+  sprintf (line, "%04d/%02d/%02d,%02d:%02d:%02d", 1900 + gmt[0].tm_year, gmt[0].tm_mon+1, gmt[0].tm_mday, gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+  return (line);
+
+}
+
+/***** date in format yyyy/mm/dd,hh:mm:ss *****/
+time_t ohana_date_to_sec (char *date) {
+  
+  time_t second;
+  double tmp, jd;
+  struct tm now;
+  char *p1, *p2, *px;
+  
+  p1 = date;
+  if (p1 == NULL) return 0;
+
+  /* ignore standard leading quoting options: " ' ( */
+  if (*p1 == 0x22) p1++; /* " */
+  if (*p1 == 0x27) p1++; /* ' */
+  if (*p1 == 0x28) p1++; /* ( */
+
+  px = p1 + strlen(p1);
+  bzero (&now, sizeof(now));
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  now.tm_year = tmp;
+  if (now.tm_year > 1000) now.tm_year -= 1900;
+  if (now.tm_year <   50) now.tm_year += 100;
+  if (p2 == px) goto escape;  
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  now.tm_mon = tmp - 1; /* mon runs from 0 - 11 */
+  if (p2 == px) goto escape;  
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  now.tm_mday = tmp;
+  if (p2 == px) goto escape;  
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  p1 = p2 + 1;
+  now.tm_hour = tmp;
+  if (p2 == px) goto escape;  
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  now.tm_min = tmp;
+  if (p2 == px) goto escape;  
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  now.tm_sec = tmp;
+  if (p2 == px) goto escape;  
+  p1 = p2 + 1;
+
+escape:
+  jd = now.tm_mday - 32075 + (int)(1461*(1900 + now.tm_year + 4800 + (int)(((now.tm_mon+1)-14)/12))/4)
+    + (int)(367*((now.tm_mon+1) - 2 - (int)(((now.tm_mon+1) - 14)/12)*12)/12)
+    - (int)(3*(int)((1900 + now.tm_year + 4900 + (int)(((now.tm_mon+1) - 14)/12))/100)/4) - 0.5;
+  
+  second = (jd - 2440587.5)*86400 + 3600.0*now.tm_hour + now.tm_min*60.0 + now.tm_sec;
+
+  return (second);
+}
+
+/***** short date in format yymmdd *****/
+time_t short_date_to_sec (char *date) {
+  
+  time_t second;
+  double jd;
+  struct tm now;
+  
+  bzero (&now, sizeof(now));
+
+  sscanf (date, "%2d%2d%2d", &now.tm_year, &now.tm_mon, &now.tm_mday);
+
+  if (now.tm_year >   51) now.tm_year +=   0;
+  if (now.tm_year <   50) now.tm_year += 100;
+  now.tm_mon --; /* tm_mon runs from 0 - 11 */
+
+  jd = now.tm_mday - 32075 + (int)(1461*(1900 + now.tm_year + 4800 + (int)(((now.tm_mon+1)-14)/12))/4)
+    + (int)(367*((now.tm_mon+1) - 2 - (int)(((now.tm_mon+1) - 14)/12)*12)/12)
+    - (int)(3*(int)((1900 + now.tm_year + 4900 + (int)(((now.tm_mon+1) - 14)/12))/100)/4) - 0.5;
+  
+  second = (jd - 2440587.5)*86400 + 3600.0*now.tm_hour + now.tm_min*60.0 + now.tm_sec;
+
+  return (second);
+}
+
+/**********/
+int hstgsc_hms_to_deg (double *h0, double *h1, double *d0, double *d1, char *string) {
+  
+  int flag_d0, flag_d1, flag_h0, flag_h1;
+  double tmp;
+  
+  *d0 = *h0 = *d1 = *h1 = 0;
+
+  flag_h0 = dparse (h0, 1, string);
+  flag_h1 = dparse (h1, 4, string);
+  flag_d0 = dparse (d0, 7, string);
+  flag_d1 = dparse (d1, 9, string);
+  *h0 *= flag_h0;
+  *h1 *= flag_h1;
+  *d0 *= flag_d0;
+  *d1 *= flag_d1;
+
+  dparse (&tmp, 2, string);
+  *h0 += tmp/60.0;
+  dparse (&tmp, 3, string);
+  *h0 += tmp/3600.0;
+  
+  dparse (&tmp, 5, string);
+  *h1 += tmp/60.0;
+  dparse (&tmp, 6, string);
+  *h1 += tmp/3600.0;
+  
+  dparse (&tmp, 8, string);
+  *d0 += tmp/60.0;
+
+  dparse (&tmp, 10, string);
+  *d1 += tmp/60.0;
+
+  *h0 *= 15*flag_h0;
+  *h1 *= 15*flag_h1;
+  *d0 *= flag_d0;
+  *d1 *= flag_d1;
+
+  return (TRUE);
+}
+
+/* times may be in forms as:
+ * 20040200450s (N seconds since 1970.0)
+ * 2440900.232j (julian date)
+ * 99/02/23,03:22:18 (date string)
+ * (separators may be anything except space, +, -)
+ * 99:02:15:12:23:30
+ * 99:02:15:12h23m30s
+ */
+
+
+/* returns the local MEAN sidereal time (dec hrs) at julian date jd
+   at west longitude long (decimal hours).  Follows
+   definitions in 1992 Astronomical Almanac, pp. B7 and L2.
+   Expression for GMST at 0h ut referenced to Aoki et al, A&A 105,
+   p.359, 1982.  On workstations, accuracy (numerical only!)
+   is about a millisecond in the 1990s.
+   EAM: function from skycalc (thorstensen) 
+*/
+#define  J2000             2451545.        /* Julian date at standard epoch */
+#define  SEC_IN_DAY        86400.
+
+double ohana_lst (double jd, double longitude) {
+
+  double t, ut, jdmid, jdint, jdfrac, sid_g;
+  long sid_int;
+
+  jdint = (int) jd;
+  jdfrac = jd - jdint;
+
+  if (jdfrac < 0.5) {
+    jdmid = jdint - 0.5;
+    ut = jdfrac + 0.5;
+  } else {
+    jdmid = jdint + 0.5;
+    ut = jdfrac - 0.5;
+  }
+
+  t = (jdmid - J2000)/36525;
+  sid_g = (24110.54841+8640184.812866*t+0.093104*t*t-6.2e-6*t*t*t)/SEC_IN_DAY;
+  sid_int = sid_g;
+  sid_g = sid_g - (double) sid_int;
+  sid_g = sid_g + 1.0027379093 * ut - longitude/24.;
+  sid_int = sid_g;
+  sid_g = (sid_g - (double) sid_int) * 24.;
+  if (sid_g < 0.) sid_g = sid_g + 24.;
+  return (sid_g);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/src/version.c	(revision 22322)
@@ -0,0 +1,6 @@
+# include <ohana.h>
+static char *name = "$Name: not supported by cvs2svn $";
+
+char *ohana_version () {
+  return (name);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/test/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/test/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/test/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.linux *.lin64 *.sol
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/test/string.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/test/string.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libohana/test/string.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include "ohana.h"
+# include "tap_ohana.h"
+
+int main (void) {
+
+  plan_tests (16);
+
+  diag ("libohana string.c tests");
+
+  /*** stripwhite ***/
+  {
+    int status;
+    char string[128];
+
+    status = stripwhite (NULL);
+    
+    ok (!status, "stripwhite status for NULL is FALSE");
+
+    sprintf (string, "  a test line  ");
+    status = stripwhite (string);
+    
+    ok (status, "stripwhite returns %d", status);
+    ok (!strcmp (string, "a test line"), "string is stripped");
+
+    sprintf (string, "a test line");
+    status = stripwhite (string);
+    
+    ok (status, "stripwhite returns %d", status);
+    ok (!strcmp (string, "a test line"), "string is stripped");
+  }
+    
+  /*** strsubs ***/
+  {
+    char *output;
+
+    output = strsubs (NULL, "needle", "noodle");
+    ok (output == NULL, "strsubs for NULL is NULL");
+
+    output = strsubs ("needle in haystack", NULL, "noodle");
+    ok (output == NULL, "strsubs for NULL is NULL");
+
+    output = strsubs ("needle in haystack", "needle", NULL);
+    ok (output == NULL, "strsubs for NULL is NULL");
+
+    output = strsubs ("needle in haystack", "needle", "noodle");
+    ok (output != NULL, "stripwhite status is TRUE");
+    skip_start (output == NULL, 1, "Skipping 1 test because strsubs returned NULL");
+    ok (!strcmp (output, "noodle in haystack"), "successful sustitution at beginning: %s", output);
+    skip_end();
+
+    output = strsubs ("needle in haystack", "in", "noodle");
+    ok (output != NULL, "stripwhite status is TRUE");
+    skip_start (output == NULL, 1, "Skipping 1 test because strsubs returned NULL");
+    ok (!strcmp (output, "needle noodle haystack"), "successful sustitution in middle: %s", output);
+    skip_end();
+
+    output = strsubs ("needle in haystack", "stack", "noodle");
+    ok (output != NULL, "stripwhite status is TRUE");
+    skip_start (output == NULL, 1, "Skipping 1 test because strsubs returned NULL");
+    ok (!strcmp (output, "needle in haynoodle"), "successful sustitution at end: %s", output);
+    skip_end();
+
+    output = strsubs ("needle in haystack", "junk", "noodle");
+    ok (output != NULL, "stripwhite status is TRUE");
+    skip_start (output == NULL, 1, "Skipping 1 test because strsubs returned NULL");
+    ok (!strcmp (output, "needle in haystack"), "successful sustitution without match: %s", output);
+    skip_end();
+  }
+  return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+lib
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/INSTALL	(revision 22322)
@@ -0,0 +1,8 @@
+Quick Installation
+
+    ./configure
+    make
+    make check
+    make install
+
+Run "configure --help" for additional options.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/LICENSE	(revision 22322)
@@ -0,0 +1,23 @@
+Copyright (c) 2004 Nik Clayton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/Makefile	(revision 22322)
@@ -0,0 +1,33 @@
+default: install
+help:
+	@echo "make options: install libtap clean dist"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/libtap
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+install: $(DESTLIB)/libtap_ohana.a $(DESTLIB)/libtap_ohana.$(DLLTYPE)
+libtap: $(LIB)/libtap_ohana.$(ARCH).a $(LIB)/libtap_ohana.$(ARCH).$(DLLTYPE)
+
+INCS = $(DESTINC)/tap_ohana.h
+TAP = $(SRC)/tap_ohana.$(ARCH).o
+
+$(TAP): $(INCS)
+
+$(LIB)/libtap_ohana.$(ARCH).a: $(TAP)
+$(LIB)/libtap_ohana.$(ARCH).$(DLLTYPE): $(TAP)
+
+$(DESTLIB)/libtap_ohana.a:  $(LIB)/libtap_ohana.$(ARCH).a
+$(DESTLIB)/libtap_ohana.$(DLLTYPE): $(LIB)/libtap_ohana.$(ARCH).$(DLLTYPE)
+
+# libtap tests are currently not compiled or run
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/README	(revision 22322)
@@ -0,0 +1,22 @@
+NAME
+     tap -- write tests that implement the Test Anything Protocol
+
+SYNOPSIS
+     #include <tap.h>
+
+DESCRIPTION
+     The tap library provides functions for writing test scripts that produce
+     output consistent with the Test Anything Protocol.  A test harness that
+     parses this protocol can run these tests and produce useful reports indi-
+     cating their success or failure.
+
+
+---
+
+*** Note for Ohana ***
+
+This is a locally modified version of tap to work within the Ohana
+framework.  I have stripped the library down to the source code and
+tests, removed the autogen and libtool stuff, and made a Makefile
+which uses the Ohana $(ARCH) system.  The tap library is installed
+with the name tap_ohana (-ltap_ohana, tap_ohana.h).
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/include/tap_ohana.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/include/tap_ohana.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/include/tap_ohana.h	(revision 22322)
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* '## __VA_ARGS__' is a gcc'ism. C99 doesn't allow the token pasting
+   and requires the caller to add the final comma if they've ommitted
+   the optional arguments */
+#ifdef __GNUC__
+# define ok(e, test, ...) ((e) ?					\
+			   _gen_result(1, __func__, __FILE__, __LINE__,	\
+				       test, ## __VA_ARGS__) :		\
+			   _gen_result(0, __func__, __FILE__, __LINE__,	\
+				       test, ## __VA_ARGS__))
+
+# define ok1(e) ((e) ?							\
+		 _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+		 _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(test, ...) ok(1, test, ## __VA_ARGS__);
+# define fail(test, ...) ok(0, test, ## __VA_ARGS__);
+
+# define skip_start(test, n, fmt, ...)			\
+	do {						\
+		if((test)) {				\
+			skip(n, fmt, ## __VA_ARGS__);	\
+			continue;			\
+		}
+#elif __STDC_VERSION__ >= 199901L /* __GNUC__ */
+# define ok(e, ...) ((e) ?						\
+		     _gen_result(1, __func__, __FILE__, __LINE__,	\
+				 __VA_ARGS__) :				\
+		     _gen_result(0, __func__, __FILE__, __LINE__,	\
+				 __VA_ARGS__))
+
+# define ok1(e) ((e) ?							\
+		 _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+		 _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(...) ok(1, __VA_ARGS__);
+# define fail(...) ok(0, __VA_ARGS__);
+
+# define skip_start(test, n, ...)			\
+	do {						\
+		if((test)) {				\
+			skip(n,  __VA_ARGS__);		\
+			continue;			\
+		}
+#else /* __STDC_VERSION__ */
+# error "Needs gcc or C99 compiler for variadic macros."
+#endif /* __STDC_VERSION__ */
+
+#define skip_end() } while(0);
+
+unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...);
+
+int plan_no_plan(void);
+int plan_skip_all(char *);
+int plan_tests(unsigned int);
+
+unsigned int diag(char *, ...);
+
+int skip(unsigned int, char *, ...);
+
+void todo_start(char *, ...);
+void todo_end(void);
+
+int exit_status(void);
+
+# ifndef _GNU_SOURCE
+/* if these are not in your headers, are they in your libc? */
+int asprintf(char **strp, const char *fmt, ...);
+int vasprintf(char **strp, const char *fmt, va_list ap);
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/lib/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/lib/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/lib/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.darwin.dylib
+*.darwin_x86.dylib
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap.3
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap.3	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap.3	(revision 22322)
@@ -0,0 +1,380 @@
+.Dd December 20, 2004
+.Os
+.Dt TAP 3
+.Sh NAME
+.Nm tap
+.Nd write tests that implement the Test Anything Protocol
+.Sh SYNOPSIS
+.In tap.h
+.Sh DESCRIPTION
+The
+.Nm
+library provides functions for writing test scripts that produce output
+consistent with the Test Anything Protocol.  A test harness that parses
+this protocol can run these tests and produce useful reports indicating
+their success or failure.
+.Ss PRINTF STRINGS
+In the descriptions that follow, for any function that takes as the
+last two parameters
+.Dq Fa char * , Fa ...
+it can be assumed that the
+.Fa char *
+is a
+.Fn printf
+-like format string, and the optional arguments are values to be placed
+in that string.
+.Ss TEST PLANS
+.Bl -tag -width indent
+.It Xo
+.Ft int
+.Fn plan_tests "unsigned int"
+.Xc
+.It Xo
+.Ft int
+.Fn plan_no_plan "void"
+.Xc
+.It Xo
+.Ft int
+.Fn plan_skip_all "char *" "..."
+.Xc
+.El
+.Pp
+You must first specify a test plan.  This indicates how many tests you
+intend to run, and allows the test harness to notice if any tests were
+missed, or if the test program exited prematurely.
+.Pp
+To do this, use
+.Fn plan_tests ,
+which returns the number of planned tests.  The function will cause
+your program to exit prematurely if you specify 0 tests.
+.Pp
+In some situations you may not know how many tests you will be running, or
+you are developing your test program, and do not want to update the
+.Fn plan_tests
+parameter every time you make a change.  For those situations use
+.Fn plan_no_plan .
+It returns 1, and indicates to the test harness that an indeterminate number
+of tests will be run.
+.Pp
+Both
+.Fn plan_tests
+and
+.Fn plan_no_plan
+will cause your test program to exit prematurely with a diagnostic
+message if they are called more than once.
+.Pp
+If your test program detects at run time that some required functionality
+is missing (for example, it relies on a database connection which is not
+present, or a particular configuration option that has not been included
+in the running kernel) use
+.Fn plan_skip_all ,
+passing as parameters a string to display indicating the reason for skipping
+the tests.
+.Ss SIMPLE TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft unsigned int
+.Fn ok "expression" "char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn ok1 "expression"
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn pass "char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn fail "char *" "..."
+.Xc
+.El
+.Pp
+Tests are implemented as expressions checked by calls to the
+.Fn ok
+and
+.Fn ok1
+macros.  In both cases
+.Fa expression
+should evaluate to true if the test succeeded.
+.Pp
+.Fn ok
+allows you to specify a name, or comment, describing the test which will
+be included in the output.
+.Fn ok1
+is for those times when the expression to be tested is self
+explanatory and does not need an associated comment.  In those cases
+the test expression becomes the comment.
+.Pp
+These four calls are equivalent:
+.Bd -literal -offset indent
+int i = 5;
+
+ok(i == 5, "i equals 5");      /* Overly verbose */
+ok(i == 5, "i equals %d", i);  /* Just to demonstrate printf-like
+                                  behaviour of the test name */
+ok(i == 5, "i == 5");          /* Needless repetition */
+ok1(i == 5);                   /* Just right */
+.Ed
+.Pp
+It is good practice to ensure that the test name describes the meaning
+behind the test rather than what you are testing.  Viz
+.Bd -literal -offset indent
+ok(db != NULL, "db is not NULL");            /* Not bad, but */
+ok(db != NULL, "Database conn. succeeded");  /* this is better */
+.Ed
+.Pp
+.Fn ok
+and
+.Fn ok1
+return 1 if the expression evaluated to true, and 0 if it evaluated to
+false.  This lets you chain calls from
+.Fn ok
+to
+.Fn diag
+to only produce diagnostic output if the test failed.  For example, this
+code will include diagnostic information about why the database connection
+failed, but only if the test failed.
+.Bd -literal -offset indent
+ok(db != NULL, "Database conn. succeeded") ||
+    diag("Database error code: %d", dberrno);
+.Ed
+.Pp
+You also have
+.Fn pass
+and
+.Fn fail .
+From the Test::More documentation:
+.Bd -literal -offset indent
+Sometimes you just want to say that the tests have passed.
+Usually the case is you've got some complicated condition
+that is difficult to wedge into an ok().  In this case,
+you can simply use pass() (to declare the test ok) or fail
+(for not ok).
+
+Use these very, very, very sparingly.
+.Ed
+.Pp
+These are synonyms for ok(1, ...) and ok(0, ...).
+.Ss SKIPPING TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft int
+.Fn skip "unsigned int" "char *" "..."
+.Xc
+.It Xo
+.Fn skip_start "expression" "unsigned int" "char *" "..."
+.Xc
+.It Xo
+.Fn skip_end
+.Xc
+.El
+.Pp
+Sets of tests can be skipped.  Ordinarily you would do this because
+the test can't be run in this particular testing environment.
+.Pp
+For example, suppose some tests should be run as root.  If the test is
+not being run as root then the tests should be skipped.  In this 
+implementation, skipped tests are flagged as being ok, with a special
+message indicating that they were skipped.  It is your responsibility
+to ensure that the number of tests skipped (the first parameter to
+.Fn skip )
+is correct for the number of tests to skip.
+.Pp
+One way of implementing this is with a
+.Dq do { } while(0);
+loop, or an
+.Dq if( ) { } else { }
+construct, to ensure that there are no additional side effects from the
+skipped tests.
+.Bd -literal -offset indent
+if(getuid() != 0) {
+        skip(1, "because test only works as root");
+} else {
+        ok(do_something_as_root() == 0, "Did something as root");
+}
+.Ed
+.Pp
+Two macros are provided to assist with this.  The previous example could
+be re-written as follows.
+.Bd -literal -offset indent
+skip_start(getuid() != 0, 1, "because test only works as root");
+
+ok(do_something_as_root() == 0, "Did something as root");
+
+skip_end();
+.Ed
+.Ss MARKING TESTS AS Dq TODO
+.Bl -tag -width indent
+.It Xo
+.Ft void
+.Fn todo_start "char *" "..."
+.Xc
+.It Xo
+.Ft void
+.Fn todo_end "void"
+.Xc
+.El
+.Pp
+Sets of tests can be flagged as being
+.Dq TODO .
+These are tests that you expect to fail, probably because you haven't
+fixed a bug, or finished a new feature yet.  These tests will still be
+run, but with additional output that indicates that they are expected
+to fail.  Should a test start to succeed unexpectedly, tools like
+.Xr prove 1
+will indicate this, and you can move the test out of the todo
+block.  This is much more useful than simply commenting out (or
+.Dq #ifdef 0 ... #endif )
+the tests.
+.Bd -literal -offset indent
+todo_start("dwim() not returning true yet");
+
+ok(dwim(), "Did what the user wanted");
+
+todo_end();
+.Ed
+.Pp
+Should
+.Fn dwim
+ever start succeeding you will know about it as soon as you run the
+tests.  Note that
+.Em unlike
+the
+.Fn skip_*
+family, additional code between
+.Fn todo_start
+and
+.Fn todo_end
+.Em is
+executed.
+.Ss SKIP vs. TODO
+From the Test::More documentation;
+.Bd -literal -offset indent
+If it's something the user might not be able to do, use SKIP.
+This includes optional modules that aren't installed, running
+under an OS that doesn't have some feature (like fork() or
+symlinks), or maybe you need an Internet connection and one
+isn't available.
+
+If it's something the programmer hasn't done yet, use TODO.
+This is for any code you haven't written yet, or bugs you have
+yet to fix, but want to put tests in your testing script 
+(always a good idea).
+.Ed
+.Ss DIAGNOSTIC OUTPUT
+.Bl -tag -width indent
+.It Xo
+.Fr unsigned int
+.Fn diag "char *" "..."
+.Xc
+.El
+.Pp
+If your tests need to produce diagnostic output, use
+.Fn diag .
+It ensures that the output will not be considered by the TAP test harness.
+.Fn diag
+adds the necessary trailing
+.Dq \en
+for you.
+.Bd -literal -offset indent
+diag("Expected return code 0, got return code %d", rcode);
+.Ed
+.Pp
+.Fn diag
+always returns 0.
+.Ss EXIT STATUS
+.Bl -tag -width indent
+.It Xo
+.Fr int
+.Fn exit_status void
+.Xc
+.El
+.Pp
+For maximum compatability your test program should return a particular
+exit code.  This is calculated by
+.Fn exit_status
+so it is sufficient to always return from
+.Fn main
+with either
+.Dq return exit_status();
+or
+.Dq exit(exit_status());
+as appropriate.
+.Sh ENVIRONMENT
+The following environment variables affect
+.Nm .
+.Bl -tag -width indent
+.It Ev HARNESS_ACTIVE
+Causes an extra
+.Dq \en
+to be printed before any diagnostic failure output generated by
+.Nm .
+This variable is normally set if tests are being run under Perl's
+Test::Harness.
+.El
+.Sh EXAMPLES
+The
+.Pa tests
+directory in the source distribution contains numerous tests of
+.Nm
+functionality, written using
+.Nm .
+Examine them for examples of how to construct test suites.
+.Sh COMPATABILITY
+.Nm
+strives to be compatible with the Perl Test::More and Test::Harness 
+modules.  The test suite verifies that
+.Nm
+is bug-for-bug compatible with their behaviour.  This is why some
+functions which would more naturally return nothing return constant
+values.
+.Pp
+If the
+.Lb libpthread
+is found at compile time,
+.Nm
+.Em should
+be thread safe.  Indications to the contrary (and test cases that expose
+incorrect behaviour) are very welcome.
+.Sh SEE ALSO
+.Xr Test::More 1 ,
+.Xr Test::Harness 1 ,
+.Xr prove 1
+.Sh STANDARDS
+.Nm
+requires a
+.St -isoC-99
+compiler.  Some of the
+.Nm
+functionality is implemented as variadic macros, and that functionality
+was not formally codified until C99.  Patches to use
+.Nm
+with earlier compilers that have their own implementation of variadic
+macros will be gratefully received.
+.Sh HISTORY
+.Nm
+was written to help improve the quality and coverage of the FreeBSD
+regression test suite, and released in the hope that others find it
+a useful tool to help improve the quality of their code.
+.Sh AUTHORS
+.An "Nik Clayton" Aq nik@ngo.org.uk ,
+.Aq nik@FreeBSD.org
+.Pp
+.Nm
+would not exist without the efforts of
+.An "Michael G Schwern" Aq schqern@pobox.com ,
+.An "Andy Lester" Aq andy@petdance.com ,
+and the countless others who have worked on the Perl QA programme.
+.Sh BUGS
+Ideally, running the tests would have no side effects on the behaviour
+of the application you are testing.  However, it is not always possible
+to avoid them.  The following side effects of using
+.Nm
+are known.
+.Bl -bullet -offset indent
+.It
+stdout is set to unbuffered mode after calling any of the
+.Fn plan_*
+functions.
+.El
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap.c	(revision 22322)
@@ -0,0 +1,426 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap.h"
+
+static int no_plan = 0;
+static int skip_all = 0;
+static int have_plan = 0;
+static unsigned int test_count = 0; /* Number of tests that have been run */
+static unsigned int e_tests = 0; /* Expected number of tests to run */
+static unsigned int failures = 0; /* Number of tests that failed */
+static char *todo_msg = NULL;
+static char *todo_msg_fixed = "libtap malloc issue";
+static int todo = 0;
+static int test_died = 0;
+
+/* Encapsulate the pthread code in a conditional.  In the absence of
+   libpthread the code does nothing */
+#ifdef HAVE_LIBPTHREAD
+#include <pthread.h>
+static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK pthread_mutex_lock(&M);
+# define UNLOCK pthread_mutex_unlock(&M);
+#else
+# define LOCK
+# define UNLOCK
+#endif
+
+static void _expected_tests(unsigned int);
+static void _tap_init(void);
+static void _cleanup(void);
+
+/*
+ * Generate a test result.
+ *
+ * ok -- boolean, indicates whether or not the test passed.
+ * test_name -- the name of the test, may be NULL
+ * test_comment -- a comment to print afterwards, may be NULL
+ */
+unsigned int
+_gen_result(int ok, const char *func, char *file, unsigned int line, 
+	    char *test_name, ...)
+{
+	va_list ap;
+	char *local_test_name = NULL;
+	char *c;
+	int name_is_digits;
+
+	LOCK;
+
+	test_count++;
+
+	/* Start by taking the test name and performing any printf()
+	   expansions on it */
+	if(test_name != NULL) {
+		va_start(ap, test_name);
+		vasprintf(&local_test_name, test_name, ap);
+		va_end(ap);
+
+		/* Make sure the test name contains more than digits
+		   and spaces.  Emit an error message and exit if it
+		   does */
+		if(local_test_name) {
+			name_is_digits = 1;
+			for(c = local_test_name; *c != '\0'; c++) {
+				if(!isdigit(*c) && !isspace(*c)) {
+					name_is_digits = 0;
+					break;
+				}
+			}
+
+			if(name_is_digits) {
+				diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
+				diag("    Very confusing.");
+			}
+		}
+	}
+
+	if(!ok) {
+		printf("not ");
+		failures++;
+	}
+
+	printf("ok %d", test_count);
+
+	if(test_name != NULL) {
+		printf(" - ");
+
+		/* Print the test name, escaping any '#' characters it
+		   might contain */
+		if(local_test_name != NULL) {
+			flockfile(stdout);
+			for(c = local_test_name; *c != '\0'; c++) {
+				if(*c == '#')
+					fputc('\\', stdout);
+				fputc((int)*c, stdout);
+			}
+			funlockfile(stdout);
+		} else {	/* vasprintf() failed, use a fixed message */
+			printf("%s", todo_msg_fixed);
+		}
+	}
+
+	/* If we're in a todo_start() block then flag the test as being
+	   TODO.  todo_msg should contain the message to print at this
+	   point.  If it's NULL then asprintf() failed, and we should
+	   use the fixed message.
+
+	   This is not counted as a failure, so decrement the counter if
+	   the test failed. */
+	if(todo) {
+		printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
+		if(!ok)
+			failures--;
+	}
+
+	printf("\n");
+
+	if(!ok) {
+		if(getenv("HARNESS_ACTIVE") != NULL)
+			fputs("\n", stderr);
+
+		diag("    Failed %stest (%s:%s() at line %d)", 
+		     todo ? "(TODO) " : "", file, func, line);
+	}
+	free(local_test_name);
+
+	UNLOCK;
+
+	/* We only care (when testing) that ok is positive, but here we
+	   specifically only want to return 1 or 0 */
+	return ok ? 1 : 0;
+}
+
+/*
+ * Initialise the TAP library.  Will only do so once, however many times it's
+ * called.
+ */
+void
+_tap_init(void)
+{
+	static int run_once = 0;
+
+	if(!run_once) {
+		atexit(_cleanup);
+
+		/* stdout needs to be unbuffered so that the output appears
+		   in the same place relative to stderr output as it does 
+		   with Test::Harness */
+		setbuf(stdout, 0);
+		run_once = 1;
+	}
+}
+
+/*
+ * Note that there's no plan.
+ */
+int
+plan_no_plan(void)
+{
+
+	LOCK;
+
+	_tap_init();
+
+	if(have_plan != 0) {
+		fprintf(stderr, "You tried to plan twice!\n");
+		test_died = 1;
+		UNLOCK;
+		exit(255);
+	}
+
+	have_plan = 1;
+	no_plan = 1;
+
+	UNLOCK;
+
+	return 1;
+}
+
+/*
+ * Note that the plan is to skip all tests
+ */
+int
+plan_skip_all(char *reason)
+{
+
+	LOCK;
+
+	_tap_init();
+
+	skip_all = 1;
+
+	printf("1..0");
+
+	if(reason != NULL)
+		printf(" # Skip %s", reason);
+
+	printf("\n");
+
+	UNLOCK;
+
+	exit(0);
+}
+
+/*
+ * Note the number of tests that will be run.
+ */
+int
+plan_tests(unsigned int tests)
+{
+
+	LOCK;
+
+	_tap_init();
+
+	if(have_plan != 0) {
+		fprintf(stderr, "You tried to plan twice!\n");
+		test_died = 1;
+		UNLOCK;
+		exit(255);
+	}
+
+	if(tests == 0) {
+		fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
+		test_died = 1;
+		UNLOCK;
+		exit(255);
+	}
+
+	have_plan = 1;
+
+	_expected_tests(tests);
+
+	UNLOCK;
+
+	return e_tests;
+}
+
+unsigned int
+diag(char *fmt, ...)
+{
+	va_list ap;
+
+	fputs("# ", stderr);
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+
+	fputs("\n", stderr);
+
+	return 0;
+}
+
+void
+_expected_tests(unsigned int tests)
+{
+
+	printf("1..%d\n", tests);
+	e_tests = tests;
+}
+
+int
+skip(unsigned int n, char *fmt, ...)
+{
+	va_list ap;
+	char *skip_msg;
+
+	LOCK;
+
+	va_start(ap, fmt);
+	asprintf(&skip_msg, fmt, ap);
+	va_end(ap);
+
+	while(n-- > 0) {
+		test_count++;
+		printf("ok %d # skip %s\n", test_count, 
+		       skip_msg != NULL ? 
+		       skip_msg : "libtap():malloc() failed");
+	}
+
+	free(skip_msg);
+
+	UNLOCK;
+
+	return 1;
+}
+
+void
+todo_start(char *fmt, ...)
+{
+	va_list ap;
+
+	LOCK;
+
+	va_start(ap, fmt);
+	vasprintf(&todo_msg, fmt, ap);
+	va_end(ap);
+
+	todo = 1;
+
+	UNLOCK;
+}
+
+void
+todo_end(void)
+{
+
+	LOCK;
+
+	todo = 0;
+	free(todo_msg);
+
+	UNLOCK;
+}
+
+int
+exit_status(void)
+{
+	int r;
+
+	LOCK;
+
+	/* If there's no plan, just return the number of failures */
+	if(no_plan || !have_plan) {
+		UNLOCK;
+		return failures;
+	}
+
+	/* Ran too many tests?  Return the number of tests that were run
+	   that shouldn't have been */
+	if(e_tests < test_count) {
+		r = test_count - e_tests;
+		UNLOCK;
+		return r;
+	}
+
+	/* Return the number of tests that failed + the number of tests 
+	   that weren't run */
+	r = failures + e_tests - test_count;
+	UNLOCK;
+
+	return r;
+}
+
+/*
+ * Cleanup at the end of the run, produce any final output that might be
+ * required.
+ */
+void
+_cleanup(void)
+{
+
+	LOCK;
+
+	/* If plan_no_plan() wasn't called, and we don't have a plan,
+	   and we're not skipping everything, then something happened
+	   before we could produce any output */
+	if(!no_plan && !have_plan && !skip_all) {
+		diag("Looks like your test died before it could output anything.");
+		UNLOCK;
+		return;
+	}
+
+	if(test_died) {
+		diag("Looks like your test died just after %d.", test_count);
+		UNLOCK;
+		return;
+	}
+
+
+	/* No plan provided, but now we know how many tests were run, and can
+	   print the header at the end */
+	if(!skip_all && (no_plan || !have_plan)) {
+		printf("1..%d\n", test_count);
+	}
+
+	if((have_plan && !no_plan) && e_tests < test_count) {
+		diag("Looks like you planned %d %s but ran %d extra.",
+		     e_tests, e_tests == 1 ? "test" : "tests", test_count - e_tests);
+		UNLOCK;
+		return;
+	}
+
+	if((have_plan || !no_plan) && e_tests > test_count) {
+		diag("Looks like you planned %d %s but only ran %d.",
+		     e_tests, e_tests == 1 ? "test" : "tests", test_count);
+		UNLOCK;
+		return;
+	}
+
+	if(failures)
+		diag("Looks like you failed %d %s of %d.", 
+		     failures, failures == 1 ? "test" : "tests", test_count);
+
+	UNLOCK;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap.h	(revision 22322)
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* '## __VA_ARGS__' is a gcc'ism. C99 doesn't allow the token pasting
+   and requires the caller to add the final comma if they've ommitted
+   the optional arguments */
+#ifdef __GNUC__
+# define ok(e, test, ...) ((e) ?					\
+			   _gen_result(1, __func__, __FILE__, __LINE__,	\
+				       test, ## __VA_ARGS__) :		\
+			   _gen_result(0, __func__, __FILE__, __LINE__,	\
+				       test, ## __VA_ARGS__))
+
+# define ok1(e) ((e) ?							\
+		 _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+		 _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(test, ...) ok(1, test, ## __VA_ARGS__);
+# define fail(test, ...) ok(0, test, ## __VA_ARGS__);
+
+# define skip_start(test, n, fmt, ...)			\
+	do {						\
+		if((test)) {				\
+			skip(n, fmt, ## __VA_ARGS__);	\
+			continue;			\
+		}
+#elif __STDC_VERSION__ >= 199901L /* __GNUC__ */
+# define ok(e, ...) ((e) ?						\
+		     _gen_result(1, __func__, __FILE__, __LINE__,	\
+				 __VA_ARGS__) :				\
+		     _gen_result(0, __func__, __FILE__, __LINE__,	\
+				 __VA_ARGS__))
+
+# define ok1(e) ((e) ?							\
+		 _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+		 _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(...) ok(1, __VA_ARGS__);
+# define fail(...) ok(0, __VA_ARGS__);
+
+# define skip_start(test, n, ...)			\
+	do {						\
+		if((test)) {				\
+			skip(n,  __VA_ARGS__);		\
+			continue;			\
+		}
+#else /* __STDC_VERSION__ */
+# error "Needs gcc or C99 compiler for variadic macros."
+#endif /* __STDC_VERSION__ */
+
+#define skip_end() } while(0);
+
+unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...);
+
+int plan_no_plan(void);
+int plan_skip_all(char *);
+int plan_tests(unsigned int);
+
+unsigned int diag(char *, ...);
+
+int skip(unsigned int, char *, ...);
+
+void todo_start(char *, ...);
+void todo_end(void);
+
+int exit_status(void);
+
+# ifndef _GNU_SOURCE
+/* if these are not in your headers, are they in your libc? */
+int asprintf(char **strp, const char *fmt, ...);
+int vasprintf(char **strp, const char *fmt, va_list ap);
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap_ohana.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap_ohana.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/src/tap_ohana.c	(revision 22322)
@@ -0,0 +1,426 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap_ohana.h"
+
+static int no_plan = 0;
+static int skip_all = 0;
+static int have_plan = 0;
+static unsigned int test_count = 0; /* Number of tests that have been run */
+static unsigned int e_tests = 0; /* Expected number of tests to run */
+static unsigned int failures = 0; /* Number of tests that failed */
+static char *todo_msg = NULL;
+static char *todo_msg_fixed = "libtap malloc issue";
+static int todo = 0;
+static int test_died = 0;
+
+/* Encapsulate the pthread code in a conditional.  In the absence of
+   libpthread the code does nothing */
+#ifdef HAVE_LIBPTHREAD
+#include <pthread.h>
+static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK pthread_mutex_lock(&M);
+# define UNLOCK pthread_mutex_unlock(&M);
+#else
+# define LOCK
+# define UNLOCK
+#endif
+
+static void _expected_tests(unsigned int);
+static void _tap_init(void);
+static void _cleanup(void);
+
+/*
+ * Generate a test result.
+ *
+ * ok -- boolean, indicates whether or not the test passed.
+ * test_name -- the name of the test, may be NULL
+ * test_comment -- a comment to print afterwards, may be NULL
+ */
+unsigned int
+_gen_result(int ok, const char *func, char *file, unsigned int line, 
+	    char *test_name, ...)
+{
+	va_list ap;
+	char *local_test_name = NULL;
+	char *c;
+	int name_is_digits;
+
+	LOCK;
+
+	test_count++;
+
+	/* Start by taking the test name and performing any printf()
+	   expansions on it */
+	if(test_name != NULL) {
+		va_start(ap, test_name);
+		vasprintf(&local_test_name, test_name, ap);
+		va_end(ap);
+
+		/* Make sure the test name contains more than digits
+		   and spaces.  Emit an error message and exit if it
+		   does */
+		if(local_test_name) {
+			name_is_digits = 1;
+			for(c = local_test_name; *c != '\0'; c++) {
+				if(!isdigit(*c) && !isspace(*c)) {
+					name_is_digits = 0;
+					break;
+				}
+			}
+
+			if(name_is_digits) {
+				diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
+				diag("    Very confusing.");
+			}
+		}
+	}
+
+	if(!ok) {
+		printf("not ");
+		failures++;
+	}
+
+	printf("ok %d", test_count);
+
+	if(test_name != NULL) {
+		printf(" - ");
+
+		/* Print the test name, escaping any '#' characters it
+		   might contain */
+		if(local_test_name != NULL) {
+			flockfile(stdout);
+			for(c = local_test_name; *c != '\0'; c++) {
+				if(*c == '#')
+					fputc('\\', stdout);
+				fputc((int)*c, stdout);
+			}
+			funlockfile(stdout);
+		} else {	/* vasprintf() failed, use a fixed message */
+			printf("%s", todo_msg_fixed);
+		}
+	}
+
+	/* If we're in a todo_start() block then flag the test as being
+	   TODO.  todo_msg should contain the message to print at this
+	   point.  If it's NULL then asprintf() failed, and we should
+	   use the fixed message.
+
+	   This is not counted as a failure, so decrement the counter if
+	   the test failed. */
+	if(todo) {
+		printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
+		if(!ok)
+			failures--;
+	}
+
+	printf("\n");
+
+	if(!ok) {
+		if(getenv("HARNESS_ACTIVE") != NULL)
+			fputs("\n", stderr);
+
+		diag("    Failed %stest (%s:%s() at line %d)", 
+		     todo ? "(TODO) " : "", file, func, line);
+	}
+	free(local_test_name);
+
+	UNLOCK;
+
+	/* We only care (when testing) that ok is positive, but here we
+	   specifically only want to return 1 or 0 */
+	return ok ? 1 : 0;
+}
+
+/*
+ * Initialise the TAP library.  Will only do so once, however many times it's
+ * called.
+ */
+void
+_tap_init(void)
+{
+	static int run_once = 0;
+
+	if(!run_once) {
+		atexit(_cleanup);
+
+		/* stdout needs to be unbuffered so that the output appears
+		   in the same place relative to stderr output as it does 
+		   with Test::Harness */
+		setbuf(stdout, 0);
+		run_once = 1;
+	}
+}
+
+/*
+ * Note that there's no plan.
+ */
+int
+plan_no_plan(void)
+{
+
+	LOCK;
+
+	_tap_init();
+
+	if(have_plan != 0) {
+		fprintf(stderr, "You tried to plan twice!\n");
+		test_died = 1;
+		UNLOCK;
+		exit(255);
+	}
+
+	have_plan = 1;
+	no_plan = 1;
+
+	UNLOCK;
+
+	return 1;
+}
+
+/*
+ * Note that the plan is to skip all tests
+ */
+int
+plan_skip_all(char *reason)
+{
+
+	LOCK;
+
+	_tap_init();
+
+	skip_all = 1;
+
+	printf("1..0");
+
+	if(reason != NULL)
+		printf(" # Skip %s", reason);
+
+	printf("\n");
+
+	UNLOCK;
+
+	exit(0);
+}
+
+/*
+ * Note the number of tests that will be run.
+ */
+int
+plan_tests(unsigned int tests)
+{
+
+	LOCK;
+
+	_tap_init();
+
+	if(have_plan != 0) {
+		fprintf(stderr, "You tried to plan twice!\n");
+		test_died = 1;
+		UNLOCK;
+		exit(255);
+	}
+
+	if(tests == 0) {
+		fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
+		test_died = 1;
+		UNLOCK;
+		exit(255);
+	}
+
+	have_plan = 1;
+
+	_expected_tests(tests);
+
+	UNLOCK;
+
+	return e_tests;
+}
+
+unsigned int
+diag(char *fmt, ...)
+{
+	va_list ap;
+
+	fputs("# ", stderr);
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+
+	fputs("\n", stderr);
+
+	return 0;
+}
+
+void
+_expected_tests(unsigned int tests)
+{
+
+	printf("1..%d\n", tests);
+	e_tests = tests;
+}
+
+int
+skip(unsigned int n, char *fmt, ...)
+{
+	va_list ap;
+	char *skip_msg;
+
+	LOCK;
+
+	va_start(ap, fmt);
+	asprintf(&skip_msg, fmt, ap);
+	va_end(ap);
+
+	while(n-- > 0) {
+		test_count++;
+		printf("ok %d # skip %s\n", test_count, 
+		       skip_msg != NULL ? 
+		       skip_msg : "libtap():malloc() failed");
+	}
+
+	free(skip_msg);
+
+	UNLOCK;
+
+	return 1;
+}
+
+void
+todo_start(char *fmt, ...)
+{
+	va_list ap;
+
+	LOCK;
+
+	va_start(ap, fmt);
+	vasprintf(&todo_msg, fmt, ap);
+	va_end(ap);
+
+	todo = 1;
+
+	UNLOCK;
+}
+
+void
+todo_end(void)
+{
+
+	LOCK;
+
+	todo = 0;
+	free(todo_msg);
+
+	UNLOCK;
+}
+
+int
+exit_status(void)
+{
+	int r;
+
+	LOCK;
+
+	/* If there's no plan, just return the number of failures */
+	if(no_plan || !have_plan) {
+		UNLOCK;
+		return failures;
+	}
+
+	/* Ran too many tests?  Return the number of tests that were run
+	   that shouldn't have been */
+	if(e_tests < test_count) {
+		r = test_count - e_tests;
+		UNLOCK;
+		return r;
+	}
+
+	/* Return the number of tests that failed + the number of tests 
+	   that weren't run */
+	r = failures + e_tests - test_count;
+	UNLOCK;
+
+	return r;
+}
+
+/*
+ * Cleanup at the end of the run, produce any final output that might be
+ * required.
+ */
+void
+_cleanup(void)
+{
+
+	LOCK;
+
+	/* If plan_no_plan() wasn't called, and we don't have a plan,
+	   and we're not skipping everything, then something happened
+	   before we could produce any output */
+	if(!no_plan && !have_plan && !skip_all) {
+		diag("Looks like your test died before it could output anything.");
+		UNLOCK;
+		return;
+	}
+
+	if(test_died) {
+		diag("Looks like your test died just after %d.", test_count);
+		UNLOCK;
+		return;
+	}
+
+
+	/* No plan provided, but now we know how many tests were run, and can
+	   print the header at the end */
+	if(!skip_all && (no_plan || !have_plan)) {
+		printf("1..%d\n", test_count);
+	}
+
+	if((have_plan && !no_plan) && e_tests < test_count) {
+		diag("Looks like you planned %d %s but ran %d extra.",
+		     e_tests, e_tests == 1 ? "test" : "tests", test_count - e_tests);
+		UNLOCK;
+		return;
+	}
+
+	if((have_plan || !no_plan) && e_tests > test_count) {
+		diag("Looks like you planned %d %s but only ran %d.",
+		     e_tests, e_tests == 1 ? "test" : "tests", test_count);
+		UNLOCK;
+		return;
+	}
+
+	if(failures)
+		diag("Looks like you failed %d %s of %d.", 
+		     failures, failures == 1 ? "test" : "tests", test_count);
+
+	UNLOCK;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/README	(revision 22322)
@@ -0,0 +1,12 @@
+Most of the tests follow the same pattern.
+
+ * test.pl that uses Test::More, and demonstrates whatever functionality 
+   that we're trying to test.  This is the reference code.
+
+ * test.c, which tests the libtap reimplementation of the same functionality.
+
+ * test.t, which compiles the .c program, runs both test scripts, and then 
+   diffs their output to make sure it's identical.
+
+   Right now, test.t is identical in every directory.  This sucks somewhat.
+   It should either be a symlink to a common script
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/diag/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/diag/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/diag/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	plan_tests(2);
+
+	rc = diag("A diagnostic message");
+	diag("Returned: %d", rc);
+
+	/* Make sure the failure is passed through */
+	ok(1, "test 1") || diag("ok() failed, and shouldn't");
+	ok(0, "test 2") || diag("ok() passed, and shouldn't");
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/diag/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/diag/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/diag/test.pl	(revision 22322)
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+plan tests => 2;
+
+$rc = diag("A diagnostic message");
+diag("Returned: $rc");
+
+ok(1, 'test 1') or diag "ok() failed, and shouldn't";
+ok(0, 'test 2') or diag "ok() passed, and shouldn't";
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/diag/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/diag/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/diag/test.t	(revision 22322)
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+echo '1..7'
+
+perl $srcdir/test.pl 2>/dev/null >test.pl.out
+perlstatus=$?
+
+./test > test.c.out 2>&1
+cstatus=$?
+
+if grep "^# A diagnostic message$" test.c.out > /dev/null ; then
+    echo "ok 1 - found a diagnostic message"
+else
+    echo "not ok 1 - found a diagnostic message"
+    retval=1
+fi
+
+if grep "^# Returned: 0$" test.c.out > /dev/null ; then
+    echo "ok 2 - diag() expected return value" 
+else
+    echo "not ok 2 - diag() expected return value" 
+    retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 43)$" test.c.out > /dev/null ; then
+    echo "ok 3 - 'failed test at line' diag" 
+else
+    echo "not ok 3 - 'failed test at line' diag" 
+    retval=1
+fi
+
+if grep "^# ok() passed, and shouldn't$" test.c.out > /dev/null ; then
+    echo "ok 4 - expected diag"
+else
+    echo "ok 4 - expected diag"
+    retval=1
+fi
+
+if grep "^# Looks like you failed 1 test of 2.$" test.c.out > /dev/null ; then
+    echo "ok 5 - failed 1 test"
+ else
+    echo "ok 5 - failed 1 test"
+    retval=1
+fi
+
+sed -e '/^#/D' test.c.out > tmp
+mv tmp test.c.out
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 6 - TAP output is identical'
+else
+	retval=1
+	echo 'not ok 6 - TAP output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 7 - status code'
+else
+	retval=1
+	echo 'not ok 7 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/fail/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/fail/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/fail/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(2);
+	diag("Returned: %d", rc);
+
+	rc = fail("test to fail");
+	diag("Returned: %d", rc);
+
+	rc = fail("test to fail %s", "with extra string");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/fail/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/fail/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/fail/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 2;
+diag("Returned: " . sprintf('%d', $rc));
+
+$rc = fail('test to fail');
+diag("Returned: $rc");
+
+$rc = fail('test to fail with extra string');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/fail/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/fail/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/fail/test.t	(revision 22322)
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+echo '1..8'
+
+perl $srcdir/test.pl 2>/dev/null > test.pl.out
+perlstatus=$?
+
+./test > test.c.out 2>&1 
+cstatus=$?
+
+if grep "^# Returned: 2$" test.c.out >/dev/null ; then
+  echo "ok 1 - expected return value";
+else
+  echo "not ok 1 - expected return value";
+  retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 39)$" test.c.out >/dev/null ; then
+  echo "ok 2 - failed expected test";
+else
+  echo "not ok 2 - failed expected test";
+  retval=1
+fi
+
+if grep "^# Returned: 0$" test.c.out >/dev/null ; then
+  echo "ok 3 - expected return value";
+else
+  echo "not ok 3 - expected return value";
+  retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 42)$" test.c.out >/dev/null ; then
+  echo "ok 4 - failed expected test";
+else
+  echo "not ok 4 - failed expected test";
+  retval=1
+fi
+  
+if grep "^# Returned: 0$" test.c.out >/dev/null ; then
+  echo "ok 5 - expected return value";
+else
+  echo "not ok 5 - expected return value";
+  retval=1
+fi
+
+if grep "^# Looks like you failed 2 tests of 2.$" test.c.out >/dev/null ; then
+  echo "ok 6 - expected return value";
+else
+  echo "not ok 6 - expected return value";
+  retval=1
+fi
+
+sed -e '/^#/D' test.c.out > tmp
+mv tmp test.c.out
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 7 - output is identical'
+else
+	retval=1
+	echo 'not ok 7 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 8 - status code'
+else
+	retval=1
+	echo 'not ok 8 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-hash/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-hash/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-hash/test.c	(revision 22322)
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(4);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Test with no hash");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Test with one # hash");
+	diag("Returned: %d", rc);
+
+        rc = ok(1, "Test with # two # hashes");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Test with ## back to back hashes");
+	diag("Returned: %d", rc);
+	
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-hash/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-hash/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-hash/test.pl	(revision 22322)
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 4;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1, 'Test with no hash');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with one # hash');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with # two # hashes');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with ## back to back hashes');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-hash/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-hash/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-hash/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-numeric/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-numeric/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-numeric/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(3);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "First test");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "1");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Third test");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-numeric/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-numeric/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-numeric/test.pl	(revision 22322)
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 3;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1, 'First test');
+diag("Returned: $rc");
+
+$rc = ok(1, '1');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Third test');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-numeric/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-numeric/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok-numeric/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok/test.c	(revision 22322)
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(5);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals 1");
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals %d", 1);
+	diag("Returned: %d", rc);
+
+	rc = ok1(1 == 1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 2, "1 equals 2");
+	diag("Returned: %d", rc);
+
+	rc = ok1(1 == 2);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok/test.pl	(revision 22322)
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 1, '1 equals 1'); # Used for %d testing in test.c
+diag("Returned: $rc");
+
+$rc = ok(1 == 1, '1 == 1');	# Test ok1() passes when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 2, '1 equals 2');	# Test ok() fails when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 2, '1 == 2');	# Test ok1() fails when it should
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/ok/ok/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/pass/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/pass/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/pass/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(2);
+	diag("Returned: %d", rc);
+
+	rc = pass("test to pass");
+	diag("Returned: %d", rc);
+
+	rc = pass("test to pass %s", "with extra string");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/pass/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/pass/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/pass/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 2;
+diag("Returned: " . sprintf('%d', $rc));
+
+$rc = pass('test to pass');
+diag("Returned: $rc");
+
+$rc = pass('test to pass with extra string');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/pass/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/pass/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/pass/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no-tests/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(0);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no-tests/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 0;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no-tests/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no_plan/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no_plan/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no_plan/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_no_plan();
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no_plan/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no_plan/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no_plan/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+my $rc = 0;
+
+use Test::More;
+
+$rc = plan qw(no_plan);
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no_plan/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no_plan/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/no_plan/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/not-enough-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/not-enough-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/not-enough-tests/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/not-enough-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/not-enough-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/not-enough-tests/test.pl	(revision 22322)
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/not-enough-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/not-enough-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/not-enough-tests/test.t	(revision 22322)
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	# comment this out until we're exit-code compatible with Test::More
+	#retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/sane/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/sane/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/sane/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/sane/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/sane/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/sane/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/sane/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/sane/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/sane/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/plan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/plan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/plan.c	(revision 22322)
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap.h"
+
+/* Run pre-defined tests on the test library to make sure that the basic
+   functionality works, and it can be used to test itself afterwards */
+
+int
+main(int argc, char *argv[])
+{
+	plan_skip_all("No good reason");
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/test.c	(revision 22322)
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_skip_all("No good reason");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/test.pl	(revision 22322)
@@ -0,0 +1,11 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan skip_all => "No good reason";
+diag("Returned: " . sprintf("%d", $rc));
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/skip_all/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-plans/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-plans/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-plans/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(0, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-plans/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-plans/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-plans/test.pl	(revision 22322)
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = plan tests => 1;
+diag("Returned: $rc");
+
+$rc = ok(0);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-plans/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-plans/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-plans/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-tests/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(5);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = ok(0, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-tests/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(0);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/plan/too-many-tests/test.t	(revision 22322)
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+    # we're not exit-status compatible with Test::More yet
+	#retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/skip/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/skip/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/skip/test.c	(revision 22322)
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+	unsigned int side_effect = 0;
+
+	rc = plan_tests(4);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals 1");	/* Should always work */
+	diag("Returned: %d", rc);
+
+	do {
+		if(1) {
+			rc = skip(1, "Testing skipping");
+			continue;
+		}
+		
+		side_effect++;
+
+		ok(side_effect == 1, "side_effect checked out");
+
+	} while(0);
+
+	diag("Returned: %d", rc);
+
+	skip_start(1 == 1, 1, "Testing skipping #2");
+
+	side_effect++;
+	rc = ok(side_effect == 1, "side_effect checked out");
+	diag("Returned: %d", rc);
+
+	skip_end();
+
+	rc = ok(side_effect == 0, "side_effect is %d", side_effect);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/skip/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/skip/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/skip/test.pl	(revision 22322)
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 4;
+diag("Returned: " . sprintf("%d", $rc));
+
+my $side_effect = 0;		# Check whether skipping has side effects
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+# Start skipping
+SKIP: {
+	$rc = skip "Testing skipping", 1;
+
+	$side_effect++;
+
+	$rc = ok($side_effect == 1, '$side_effect checked out');
+}
+
+diag("Returned: $rc");
+
+SKIP: {
+	$rc = skip "Testing skipping #2", 1;
+	diag("Returned: $rc");
+
+	$side_effect++;
+
+	$rc = ok($side_effect == 1, '$side_effect checked out');
+	diag("Returned: $rc");
+}
+
+$rc = ok($side_effect == 0, "side_effect is $side_effect");
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/skip/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/skip/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/skip/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/todo/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/todo/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/todo/test.c	(revision 22322)
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+	unsigned int side_effect = 0;
+
+	rc = plan_tests(5);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals 1");	/* Should always work */
+	diag("Returned: %d", rc);
+
+	todo_start("For testing purposes");
+
+	side_effect++;
+
+	/* This test should fail */
+	rc = ok(side_effect == 0, "side_effect checked out");
+	diag("Returned: %d", rc);
+
+	/* This test should unexpectedly succeed */
+	rc = ok(side_effect == 1, "side_effect checked out");
+	diag("Returned: %d", rc);
+
+	todo_end();
+
+	todo_start("Testing printf() %s in todo_start()", "expansion");
+
+	rc = ok(0, "dummy test");
+	diag("Returned: %d", rc);
+
+	todo_end();
+
+	rc = ok(side_effect == 1, "side_effect is %d", side_effect);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/todo/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/todo/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/todo/test.pl	(revision 22322)
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+my $side_effect = 0;		# Check whether TODO has side effects
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+# Start TODO tests
+TODO: {
+	local $TODO = 'For testing purposes';
+
+	$side_effect++;
+
+	# This test should fail
+	$rc = ok($side_effect == 0, 'side_effect checked out');
+	diag("Returned: $rc");
+
+	# This test should unexpectedly succeed
+	$rc = ok($side_effect == 1, 'side_effect checked out');
+	diag("Returned: $rc");
+}
+
+TODO: {
+	local $TODO = 'Testing printf() expansion in todo_start()';
+
+	$rc = ok(0, 'dummy test');
+	diag("Returned: $rc");
+}
+
+$rc = ok($side_effect == 1, "side_effect is $side_effect");
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/todo/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/todo/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/libtap/tests/todo/test.t	(revision 22322)
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+# Test:;More prints diagnostic from TODO tests on stdout
+# http://rt.cpan.org/Ticket/Display.html?id=14982
+sed '/^#/D' test.pl.out > tmp
+mv tmp test.pl.out
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/Makefile	(revision 22322)
@@ -0,0 +1,31 @@
+default: lightcurve
+help:
+	@echo "make options: lightcurve (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/lightcurve
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+lightcurve: $(BIN)/lightcurve.$(ARCH)
+install: $(DESTBIN)/lightcurve
+
+LIGHTCURVE = \
+$(SRC)/args.$(ARCH).o 		$(SRC)/get_names.$(ARCH).o \
+$(SRC)/get_stars.$(ARCH).o 	$(SRC)/sort_stars.$(ARCH).o \
+$(SRC)/get_unique.$(ARCH).o 	$(SRC)/get_sources.$(ARCH).o \
+$(SRC)/get_info.$(ARCH).o 	$(SRC)/make_table.$(ARCH).o \
+$(SRC)/lightcurve.$(ARCH).o	\
+$(SRC)/help.$(ARCH).o 
+
+$(LIGHTCURVE): $(INC)/lightcurve.h
+$(BIN)/lightcurve.$(ARCH): $(LIGHTCURVE)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/include/lightcurve.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/include/lightcurve.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/include/lightcurve.h	(revision 22322)
@@ -0,0 +1,67 @@
+# include <ohana.h>
+
+# define EMPTY          -1   
+# define END            (star_data_type *) -1
+
+# define C_LAMBDA       23.0
+# define MCAL(I,dR,dD) (((I.Mcal) + (dR)*(I.McalR) + (dD)*(I.McalD) + (dR)*(dR)*(I.McalR2) + (dD)*(dD)*(I.McalD2) + (dD)*(dR)*(I.McalRD)))
+
+int PRINT;
+int MIDAS;
+int EXTRASTARS;
+int PIXELS;
+int NLOOP;
+double RADIUS;
+double A_LAMBDA;
+double SIG;
+double COS;
+double MCUTOFF;
+char OUTFILE[100];
+char IMAGES[100];
+
+typedef struct Star {
+  double RA, Dec;
+  double ap, m, dm;
+  struct Star *next_this_unique;
+  struct Star *next_this_image;
+  int    image_number, star_number, unique_number;
+} Star;
+
+typedef struct {
+  char    name[50];
+  int     Nunique, Nstars;
+  int     fixed, empty;
+  Star   *first_this_image;
+  double  Mcal, dMcal;
+  double  exptime, airmass, clouds, Mtime, AmF, dAmF, JD;
+  double  RA_O, RA_X, RA_Y, DEC_O, DEC_X, DEC_Y;
+  double  McalR, McalD, McalR2, McalD2, McalRD;
+} Image;
+
+typedef struct {
+  double  Mrel, dMrel;
+  int     Nmeasurements;
+  Star   *first_this_unique;
+} Unique;
+
+
+/******************** PROTOTYPES ********************/
+void args             PROTO((int, char **));
+void get_names        PROTO((Image **, int  *));
+void get_sources      PROTO((Star **, int *));
+void get_stars        PROTO((Star  **, int  *, Image  *, int));
+void sort_stars       PROTO((int  **, Star *, int));
+void get_unique       PROTO((Unique **, int    *, Star *, int *, int, Star *, int));
+void count_unique     PROTO((Image *, int, int));
+void set_Mcal         PROTO((Image *, int));
+void get_Mrel         PROTO((Unique *, Image *, int)); 
+void get_Mcal         PROTO((Image *, Unique *, int));
+void ChiSquare        PROTO((Unique *, Image *, int, int));
+void get_Alam         PROTO((Unique *, Image *, int, int));
+void alter_headers    PROTO((Image *, int));
+void make_table       PROTO((Star *, int, Unique *, Image *, int));
+void get_info         PROTO((Image *));
+char *nextword        PROTO((char *));
+
+extern double hypot PROTO((double, double));
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/ChiSquare.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/ChiSquare.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/ChiSquare.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "lightcurve.h"
+
+void ChiSquare (unique, images, Nunique, Nimages)
+Unique    *unique;
+Image     *images;
+int        Nunique;
+int        Nimages;
+{
+
+  int    i, j;
+  Star   *this_star;
+  double m, dm, Mcal, Mrel, ChiSquare, Ndof;
+
+  Ndof = ChiSquare = 0.0;
+  for (i = 0; i < Nunique; i ++) {
+    this_star = unique[i].first_this_unique;
+    for (j = 0; j < unique[i].Nmeasurements; j ++) {
+      dm   = this_star[0].dm;
+      m    = this_star[0].m;
+      Mcal = images[this_star[0].image_number].Mcal;
+      Mrel = unique[i].Mrel;
+      Ndof += 1.0;
+      ChiSquare += SQ (m - Mcal - Mrel) / SQ (dm);
+      this_star = this_star[0].next_this_unique;
+    }
+  }
+  Ndof = Ndof - Nunique - Nimages;
+  fprintf (stderr, "Chi Square %9.2f  Ndof %6.0f  Reduced %6.3f  A_LAMBDA  %6.4f\n", 
+	   ChiSquare, Ndof, ChiSquare/Ndof, A_LAMBDA);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/alter_headers.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/alter_headers.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/alter_headers.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "relphot.h"
+
+void alter_headers  (images, Nimages)
+Image  *images;
+int     Nimages;
+{
+
+  int i;
+  Header header;
+  char line[1000], head[1000];
+  double clouds;
+  FILE *f;
+
+  if (!strcmp(IMFILE, "-")) {
+    fprintf (stderr, "using stderr\n");
+    f = stderr;
+  }
+  else {
+    fprintf (stderr, "using %s for image stats\n", IMFILE);
+    f = fopen (IMFILE, "w");
+    if (f == NULL) {
+      fprintf (stderr, "could not open output file, using stderr\n");
+      f = stderr;
+    }
+  }
+  
+  fprintf (f, "# name                Mcal    dMcal   [time]  airmass   clouds <ap-fit> Nstars  Nunique  fixed  empty\n");
+  for (i = 0; i < Nimages; i++) {
+    strcpy (head, images[i].name);
+    strcpy (strchr(head, '.'), ".head");
+    gfits_read_header (head, &header);
+
+    sprintf (line, "mv %s %s~\0", head, head);
+    system (line);
+
+    images[i].clouds = images[i].Mcal + C_LAMBDA - A_LAMBDA*images[i].airmass + images[i].AmF;
+
+    fprintf (f, "%s  %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f  %6d   %6d      %1d      %1d\n",  
+	     images[i].name, images[i].Mcal, images[i].dMcal, images[i].Mtime, 
+	     images[i].airmass, images[i].clouds, images[i].AmF, images[i].Nstars, 
+	     images[i].Nunique, images[i].fixed, images[i].empty);
+
+    gfits_modify (&header, "Mcal",   "%lf", 1, images[i].Mcal);
+    gfits_modify (&header, "dMcal",  "%lf", 1, images[i].dMcal);
+    gfits_modify (&header, "clouds", "%lf", 1, images[i].clouds);
+    gfits_modify (&header, "NMcal",  "%d", 1,  images[i].Nstars);
+
+    gfits_modify (&header, "Mcal",   "%C", 1, "relphot: calibration magnitude");
+    gfits_modify (&header, "dMcal",  "%C", 1, "relphot: calibration error");
+    gfits_modify (&header, "clouds", "%C", 1, "relphot: cloud level");
+    gfits_modify (&header, "NMcal",  "%C", 1, "relphot: number of stars");
+
+    gfits_write_header (head, &header);
+    gfits_free_header (&header);
+  }
+  fclose (f);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/args.c	(revision 22322)
@@ -0,0 +1,78 @@
+# include "lightcurve.h"
+# define NARGS 1
+
+void args (argc, argv)
+int      argc;
+char   **argv;
+{
+  
+  int N;
+
+  /** mandatory arguments (see help.c) **/
+  if (N = get_argument (argc, argv, "-P")) {
+    remove_argument (N, &argc, argv);
+    RADIUS = atof (argv[N]);
+    PIXELS = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  else {
+    if (N = get_argument (argc, argv, "-A")) {
+      remove_argument (N, &argc, argv);
+      RADIUS = atof (argv[N]) / 3600.0;
+      PIXELS = FALSE;
+      remove_argument (N, &argc, argv);
+    }
+    else 
+      help (argv[0]);
+  }
+  
+  if (N = get_argument (argc, argv, "-im")) {
+    remove_argument(N, &argc, argv);
+    strcpy (IMAGES, argv[N]);
+    remove_argument(N, &argc, argv);
+  }
+  else
+    help (argv[0]);
+
+  /**** optional arguments (see help.c) ****/
+  if (N = get_argument (argc, argv, "-o")) {
+    remove_argument(N, &argc, argv);
+    strcpy (OUTFILE, argv[N]);
+    remove_argument(N, &argc, argv);
+  }
+  else
+    strcpy (OUTFILE, "-");
+
+  if (N = get_argument (argc, argv, "-midas")) {
+    remove_argument(N, &argc, argv);
+    MIDAS = TRUE;
+  }
+  else
+    MIDAS = FALSE;
+
+  if (N = get_argument (argc, argv, "-m")) {
+    remove_argument(N, &argc, argv);
+    MCUTOFF = atof(argv[N]);
+    remove_argument(N, &argc, argv);
+  }
+  else 
+    MCUTOFF = 0.05;
+
+  if (N = get_argument (argc, argv, "-X")) {
+    remove_argument(N, &argc, argv);
+    EXTRASTARS = TRUE;
+  }
+  else 
+    EXTRASTARS = FALSE;
+
+  if (remove_argument(get_argument (argc, argv, "-h"), &argc, argv) ||
+      remove_argument(get_argument (argc, argv, "-help"), &argc, argv))
+    help(argv[0]);
+
+  if (argc != NARGS) {
+    fprintf (stderr,  "%s%s%s", "USAGE: ", 
+	     argv[0], " (-P / -A) radius  (-im images) [-o file] [-midas]\n");
+    exit (0);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/count_unique.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/count_unique.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/count_unique.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "lightcurve.h"
+
+void count_unique (images, Nimages, Nstars)
+Image *images;
+int    Nimages; 
+int    Nstars;
+{
+
+  int i, j;
+  Star *this_star;
+
+  for (i = 0; i < Nimages; i++) {
+    images[i].Nunique = 0;
+    this_star = images[i].first_this_image;
+    for (j = 0; j < images[i].Nstars; j++) {
+      if (this_star[0].unique_number != EMPTY)
+	images[i].Nunique ++;
+      this_star = this_star[0].next_this_image;
+    }
+  }
+}
+
+    
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/fix_header.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/fix_header.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/fix_header.c	(revision 22322)
@@ -0,0 +1,59 @@
+# include "lightcurve.h"
+
+/* this repairs headers screwed up in the very specific way of 
+   astro_again: not 80 chars per line on new lines, and no padding
+   of the buffer.
+
+*/
+
+void
+  fix_header 
+  (Header     *header)
+{
+
+  int i, j, Nbytes, N;
+  char *p1, *p2;
+
+  Nbytes = 2880 * (int) (header[0].size / 2880 + 1);
+  reallocate (header[0].buffer, char, Nbytes);
+  bzero (&header[0].buffer[header[0].size], Nbytes - header[0].size - 1);
+
+  p1 = header[0].buffer - 1;
+  p2 = strchr (p1 + 1, RETURN);
+
+  while (p2 != NULL) {
+    if ((N = p2 - p1) != 80) {
+      bcopy (p2, p1 + 80, header[0].size - (p2 - header[0].buffer));
+      for (i = 0; i < 80 - N; i++) {
+	*(p2 + i) = ' ';
+      }
+      header[0].size += 80 - N;
+    }
+    p1 = strchr (p1 + 1, RETURN);
+    p2 = strchr (p1 + 1, RETURN);
+  }
+
+  header[0].size = Nbytes;
+  p1 = gfits_header_field (header, "END", 1);
+  for (i = 3; i < 79; i++) 
+    *(p1 + i) = ' ';
+  *(p1 + 79) = RETURN;
+  
+  Nbytes = header[0].size - (p1 - header[0].buffer) - 80;
+  
+  for (i = 0; i < Nbytes; i++) {
+    for (j = 0; j < 79; j++, i++) 
+      *(p1 + i + 80) = '.';
+    *(p1 + i + 80) = RETURN;
+  }
+
+  Nbytes = header[0].size - (p1 - header[0].buffer) - 80;
+  while (Nbytes >= 2880) {
+    header[0].size -= 2880;
+    Nbytes = header[0].size - (p1 - header[0].buffer) - 80;
+  }
+
+  reallocate (header[0].buffer, char, header[0].size);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_Alam.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_Alam.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_Alam.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "lightcurve.h"
+
+void get_Alam (unique, images, Nunique, Nimages)
+Unique    *unique;
+Image     *images;
+int        Nunique;
+int        Nimages;
+{
+
+  int    i, j;
+  Star   *this_star;
+  double m, dm, Mcal, Mrel, Alm, R, zeta, clouds;
+  double Clouds, dClouds, R2, N;
+
+  R = R2 = N = 0.0;
+  for (i = 0; i < Nimages; i++) {
+    images[i].clouds = images[i].Mcal + C_LAMBDA - A_LAMBDA * images[i].airmass + images[i].AmF;
+    if (!images[i].empty && images[i].fixed) {
+      R  +=     images[i].clouds;
+      R2 += SQ (images[i].clouds);
+      N  += 1.0;
+    }
+  }
+
+  Clouds  = R / N;
+  dClouds = sqrt (R2 / N - Clouds*Clouds);
+  dClouds = MAX (0.0001, dClouds);
+
+  R = Alm = 0.0;
+  for (i = 0; i < Nunique; i ++) {
+    this_star = unique[i].first_this_unique;
+    for (j = 0; j < unique[i].Nmeasurements; j ++) {
+      dm   = this_star[0].dm;
+      m    = this_star[0].m;
+      Mrel = unique[i].Mrel;
+      zeta = images[this_star[0].image_number].airmass;
+      clouds = images[this_star[0].image_number].clouds;
+      if (fabs(clouds - Clouds) > SIG*dClouds) {
+	R   +=   zeta / SQ(dm);
+	Alm += zeta * (m + C_LAMBDA - clouds - Mrel) / SQ (dm);
+      }
+      this_star = this_star[0].next_this_unique;
+    }
+  }
+  if (R > 0) 
+    A_LAMBDA = (9.0*A_LAMBDA + Alm / R) / 10.0;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_Mcal.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_Mcal.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_Mcal.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "lightcurve.h"
+
+void get_Mcal (images, unique, Nimages)
+Image  *images;
+Unique *unique;
+int     Nimages;
+{
+  
+  int    i, j;
+  Star  *this_star;
+  double m, dm, Mrel, r, R, M, M2, N;
+  
+  for (i = 0; i < Nimages; i++) {
+    N = M = M2 = R = 0;
+    this_star = images[i].first_this_image;
+    for (j = 0; j < images[i].Nstars; j++) {
+      if (this_star[0].unique_number != EMPTY) {
+	dm   = this_star[0].dm;
+	m    = this_star[0].m;
+	Mrel = unique[this_star[0].unique_number].Mrel;
+	r    = 1.0 / (dm*dm);
+	R   += r;
+	M   +=    (m - Mrel) * r;
+	M2  +=  SQ(m - Mrel) * r;
+	N   += 1.0;
+      }
+      this_star = this_star[0].next_this_image;
+    }
+    if (R != 0) {
+      images[i].Mcal  = M / R;
+      images[i].dMcal = sqrt(fabs(M2 / R - SQ(M/R))) / sqrt (N);
+    }
+    else {
+      /* no multiple detections on this image! */
+      images[i].empty = TRUE;
+      images[i].Mcal  = 9.999;
+      images[i].dMcal = 9.999;
+    }
+  }
+}
+
+/* m (instrumental) = Mrel (star) + Mcal (image) */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_Mrel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_Mrel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_Mrel.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "relphot.h"
+
+void get_Mrel (unique, images, Nunique)
+Unique   *unique;
+Image    *images;
+int       Nunique;
+{
+  
+  int    i, j;
+  Star  *this_star;
+  double dm, m, r, R, M, M2, Mcal, N;
+
+  for (i = 0; i < Nunique; i++) {
+    N = M = M2 = R = 0;
+    this_star = unique[i].first_this_unique;
+    for (j = 0; j < unique[i].Nmeasurements; j ++) {
+      dm   = this_star[0].dm;
+      m    = this_star[0].m;
+      Mcal = images[this_star[0].image_number].Mcal;
+      r    = 1.0 / (dm*dm);
+      R   += r;
+      M   +=    (m - Mcal) * r;
+      M2  +=  SQ(m - Mcal) * r;
+      N   += 1.0;
+      this_star = this_star[0].next_this_unique;
+    }
+    unique[i].Mrel  = M / R;
+    unique[i].dMrel = sqrt(fabs(M2 / R - SQ(M/R))) / sqrt (N);
+  }
+
+  for (i = 0; i < Nunique; i++) {
+    N = X2 = 0;
+    Mrel = unique[i].Mrel;
+    this_star = unique[i].first_this_unique;
+    for (j = 0; j < unique[i].Nmeasurements; j ++) {
+      dm   = this_star[0].dm;
+      m    = this_star[0].m;
+      Mcal = images[this_star[0].image_number].Mcal;
+      X2  += SQ((m - Mcal - Mrel) / dm);
+      this_star = this_star[0].next_this_unique;
+    }
+    unique[i].ChiSq = X2 / (N - 1);
+  }
+}
+
+/* m (instrumental) = Mrel (star) + Mcal (image) */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_argument.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_argument.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_argument.c	(revision 22322)
@@ -0,0 +1,19 @@
+# include "lightcurve.h"
+
+int get_argument (argc, argv, arg)
+int    argc;
+char **argv;
+char  *arg;
+{
+
+  int i;
+
+  for (i = 0; i < argc; i++) {
+    if (!strcmp(argv[i], arg))
+      return (i);
+  }
+  
+  return ((int)NULL);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_info.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_info.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_info.c	(revision 22322)
@@ -0,0 +1,128 @@
+# include "lightcurve.h"
+
+void  get_info (images)
+Image *images;
+{
+
+  char head[500], line[500];
+  Header header;
+  int i, status;
+  double jd, days, hrs, min, sec, atof();
+
+  status = TRUE;
+
+  strcpy (head, images[0].name);
+  strcpy (strchr(head, '.'), ".head");
+
+  status = gfits_read_header (head, &header);
+  if (!status) {
+    fprintf (stderr, "could not open header file %s\n", head);
+    exit (0);
+  }
+  status = TRUE;
+
+  switch (PIXELS) {
+  case 0:
+    status &= gfits_scan (&header, "RA_O",    "%lf", 1, &images[0].RA_O);
+    status &= gfits_scan (&header, "RA_X",    "%lf", 1, &images[0].RA_X);
+    status &= gfits_scan (&header, "RA_Y",    "%lf", 1, &images[0].RA_Y);
+    status &= gfits_scan (&header, "DEC_O",   "%lf", 1, &images[0].DEC_O);
+    status &= gfits_scan (&header, "DEC_X",   "%lf", 1, &images[0].DEC_X);
+    status &= gfits_scan (&header, "DEC_Y",   "%lf", 1, &images[0].DEC_Y);
+    COS = cos (RAD_DEG * images[0].DEC_O);
+    break;
+
+  case 1:
+    status &= gfits_scan (&header, "X_O",    "%lf", 1, &images[0].RA_O);
+    status &= gfits_scan (&header, "X_X",    "%lf", 1, &images[0].RA_X);
+    status &= gfits_scan (&header, "X_Y",    "%lf", 1, &images[0].RA_Y);
+    status &= gfits_scan (&header, "Y_O",    "%lf", 1, &images[0].DEC_O);
+    status &= gfits_scan (&header, "Y_X",    "%lf", 1, &images[0].DEC_X);
+    status &= gfits_scan (&header, "Y_Y",    "%lf", 1, &images[0].DEC_Y);
+    COS = 1;
+    break;
+  }
+  
+  gfits_scan (&header, "ORIGIN", "%s", 1, line);
+  /* interpret the silly way ESO / La Palma stores exposure time and duration   */
+  if (!strcmp (line, "ESO-MIDAS")) { 
+    status &= gfits_scan (&header, "DATE-OBS", "%s", 1, line);
+    stripwhite (line);
+    fprintf (stderr, "date line: %s\n", line);
+    line[2] = 0;
+    jd = atof(line);
+    line[0] = 0;
+    status = gfits_scan (&header, "TM-START", "%lf", 1, &sec);
+    fprintf (stderr, "date: %f, sec: %f\n", jd, sec);
+    jd += (sec/86400.0) + 0.5;
+    images[0].JD =  jd;
+    gfits_scan (&header, "EXPTIME", "%lf", 1, &images[0].exptime);
+  }
+  else {
+    status &= gfits_scan (&header, "JD", "%lf", 1, &images[0].JD);
+    images[0].JD -= 2400000.5;   /* convert to MJD */
+    gfits_scan (&header, "EXPTIME", "%lf", 1, &images[0].exptime);
+  }
+
+  status &= gfits_scan (&header, "Mcal", "%lf", 1, &images[0].Mcal);
+  status &= gfits_scan (&header, "McalR", "%lf", 1, &images[0].McalR);
+  status &= gfits_scan (&header, "McalD", "%lf", 1, &images[0].McalD);
+  status &= gfits_scan (&header, "McalR2", "%lf", 1, &images[0].McalR2);
+  status &= gfits_scan (&header, "McalD2", "%lf", 1, &images[0].McalD2);
+  status &= gfits_scan (&header, "McalRD", "%lf", 1, &images[0].McalRD);
+  status &= gfits_scan (&header, "dMcal", "%lf", 1, &images[0].dMcal);
+
+  images[0].airmass = 1000;  
+  gfits_scan (&header, "SECZ", "%lf", 1, &images[0].airmass);
+  gfits_scan (&header, "AIRMASS", "%lf", 1, &images[0].airmass);
+  /* a stupid value as a flag (i hate flags!) but gfits_scan will not alter the
+     value if it fails to find the entry.  try a couple possibilities */
+ 
+  fprintf (stderr, "%s: %10.6f %10.6f  %lf  %6.3f %8.2f %5.2f\n", 
+	   head, images[0].RA_O, images[0].DEC_O, images[0].JD, 
+	   images[0].Mcal, images[0].exptime, images[0].airmass);
+   
+  if (!status) {
+    fprintf (stderr, "error getting header info from %s\n", head);
+    exit(0);
+  }
+
+  if (images[0].airmass > 10) {
+    images[0].airmass = 1.1;
+    fprintf (stderr, "warning: no airmass info\n");
+  }
+  
+  images[0].Nstars  = 0;
+  images[0].fixed   = TRUE;
+  images[0].empty   = FALSE;
+  images[0].Mtime   = 2.5*log10(images[0].exptime);
+  images[0].clouds  = 0.0;
+
+  gfits_free_header (&header);
+  
+}
+
+
+
+/* airmass formula (only used if needed) */
+/* page 264 of Kitchin */
+
+/*    z = 1 / ( .5294258 * sin(pi * d / 180.0) + .84835625 * cos(pi * d / 180.0) *
+ cos(pi * h / 12.0));
+
+n
+p status
+
+*/
+
+
+/*
+   code for using RA, DEC, ST info, if needed. 
+    status &= gfits_scan (&header, "ST", "%lf", 1, &LST);
+    ra  =  info[0].RA_O  + info[0].RA_X *CCD_X/2.0 + info[0].RA_Y *CCD_Y/2.0;
+    dec =  info[0].DEC_O + info[0].DEC_X*CCD_X/2.0 + info[0].DEC_Y*CCD_Y/2.0;
+    temp1 = sin(OBS_LAT*DEG_RAD)*sin(dec*DEG_RAD);
+    temp2 = cos(OBS_LAT*DEG_RAD)*cos(dec*DEG_RAD);
+    temp3 = cos(((360./24.)*LST - ra)*DEG_RAD);
+    (info[0].airmass) = 1.0 / (temp1 + temp2*temp3);
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_names.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_names.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_names.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "lightcurve.h"
+# define D_NIMAGE 200
+  
+void get_names (images, Nimages)
+Image **images;
+int   *Nimages;
+{
+  
+  int N, i;
+  char line[500];
+  FILE *f;
+
+  f = fopen (IMAGES, "r"); 
+  if (f == NULL) { 
+    fprintf (stderr, "failed to open %s\n", IMAGES); 
+    exit (0); 
+  }
+  N = D_NIMAGE;
+  ALLOCATE (images[0], Image, N);
+
+  for (i = 0; (fscanf (f, "%s", line) != EOF); i++) { 
+    strcpy (images[0][i].name, line);
+    strcpy (strchr(images[0][i].name, '.'), ".obj_out");
+    if (i == N - 1) {
+      N += D_NIMAGE;
+      REALLOCATE (images[0], Image, N);
+    }
+  }
+  *Nimages = i;
+  fprintf (stderr, "Nimages: %d\n", *Nimages);
+  fclose (f);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_sources.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_sources.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_sources.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "lightcurve.h"
+# define D_NSOURCES 200
+
+void get_sources (sources, Nsources)
+Star  **sources;
+int    *Nsources;
+{
+  
+  int N, i;
+
+  ALLOCATE (sources[0], Star, D_NSOURCES);
+
+  for (i = 0; (fscanf (stdin, "%lf %lf", &sources[0][i].RA, &sources[0][i].Dec) != EOF); i++) { 
+    sources[0][i].unique_number = EMPTY;
+    if (i == D_NSOURCES - 1) {
+      fprintf (stderr, "No More Sources: %d\n", i);
+      break;
+    }
+  }
+  *Nsources = i;
+  fprintf (stderr, "Nsources: %d\n", *Nsources);
+}
+
+
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_stars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_stars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_stars.c	(revision 22322)
@@ -0,0 +1,99 @@
+# include "lightcurve.h"
+# define D_NSTARS 1000
+
+void get_stars (stars, Nstars, images, Nimages)
+Star  **stars;
+int    *Nstars;
+Image   images[];
+int     Nimages;
+{
+
+  double type, mag, dmag, X, Y, ap, dap, AmF, A, A2, S;
+  int i, j, image_number;
+  int NSTARS, status, n, nperf;
+  FILE *f;
+  char line[200];
+
+  j = 0;
+  NSTARS = D_NSTARS;
+  ALLOCATE (stars[0], Star, NSTARS);
+
+  for (i = 0; i < Nimages; i++) { 
+    
+    get_info (&images[i]);
+    f = fopen (images[i].name, "r"); 
+    if (f == NULL) { 
+      fprintf (stderr, "failed to open %s\n", images[i].name); 
+      exit (0); 
+    }
+    nperf = A = A2 = S = 0;
+    for (n = 0; scan_line (f, line) != EOF; n++) {
+      status = TRUE;
+      status &= dparse (&type, 2, line);
+      status &= dparse (&X,    3, line);
+      status &= dparse (&Y,    4, line);
+      status &= dparse (&mag,  5, line);
+      status &= dparse (&dmag, 6, line);
+      status &= dparse (&ap,  12, line);
+      status &= dparse (&dap, 13, line);
+      status &= dparse (&AmF, 15, line);
+      if (!status) {
+	fprintf (stderr, "error on line %d in file %s\n", n, images[i].name);
+	continue;  /* go on to the next line */
+      }
+      stars[0][j].ap = 0;
+      if (ap < 99) {
+	nperf ++;
+	A  += AmF/SQ(dap);
+	A2 += SQ(AmF)/SQ(dap);
+	S  += 1.0/SQ(dap);
+	stars[0][j].ap = ap;    /* if the ap mag is good, use it, not the <ap-fit> adjusted value */
+      }
+      if ((dmag < MCUTOFF) && ((type == 1) || (EXTRASTARS && ((type == 2) || (type == 3) || (type == 4) || (type == 5) || (type == 7))))) {  /* for now, this uses only type 1s */
+	stars[0][j].RA  = images[i].RA_O  + X*images[i].RA_X  + Y*images[i].RA_Y;
+	stars[0][j].Dec = images[i].DEC_O + X*images[i].DEC_X + Y*images[i].DEC_Y;
+	stars[0][j].m   = mag + images[i].Mtime;
+	stars[0][j].dm  = sqrt (SQ(dmag) + SQ(images[i].dMcal));
+	
+	stars[0][j].next_this_unique = NULL;
+	stars[0][j].image_number     = i;
+	stars[0][j].star_number      = n;
+	stars[0][j].unique_number    = EMPTY;
+	
+	if (j == NSTARS - 1) {
+	  NSTARS += D_NSTARS;
+	  fprintf (stderr, "!");
+	  REALLOCATE (stars[0], Star, NSTARS);
+	}
+	j ++;
+	images[i].Nstars ++;   /* this is set to 0 in "get_info" */
+      }
+    }
+    if (nperf > 2) {
+      images[i].AmF = A / S;
+      images[i].dAmF = sqrt(A2/S - A*A/SQ(S));
+    }
+    else {
+      images[i].AmF = 0.0;
+      images[i].dAmF = 0.0;
+    }     
+    fclose (f);
+/*    images[i].Mcal    = - C_LAMBDA + A_LAMBDA*images[i].airmass - images[i].AmF; */
+    stars[0][j - 1].next_this_image = NULL;
+  }
+  
+  image_number = 0;
+  for (i = 0; i < j; ) {
+    images[image_number].first_this_image = &stars[0][i];
+    for (i++; ((stars[0][i].image_number == image_number) && (i < j)); i++)  {
+      stars[0][i - 1].next_this_image = &stars[0][i];
+    }
+    stars[0][i - 1].next_this_image = NULL;
+    image_number = stars[0][i].image_number;
+  }
+
+  *Nstars = j;
+  fprintf (stderr, "Nstars: %d\n", *Nstars);
+}
+
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_unique.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_unique.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/get_unique.c	(revision 22322)
@@ -0,0 +1,186 @@
+# include "lightcurve.h"
+# define D_NUNIQUE 1000;
+
+void get_unique (unique, N, stars, radec, Nstars, sources, Nsources)
+Unique **unique;
+int    *N;
+Star   *stars;
+int    *radec;
+int     Nstars;
+Star   *sources;
+int     Nsources;
+{
+
+  int i, j, link, k, n, confused, Nchain, NUNIQUE, Nunique, first, NTEMP;
+  int Ntemp, Nclump, Nblobs;
+  Unique  temp_unique;
+  int    *templist;
+  Star   *next_unique, *this_unique;
+  double radius, dRA, dDec, RAo, DECo, dRA2, dDec2;
+  double dRo, dDo, dRo2, dDo2;
+
+  NUNIQUE = D_NUNIQUE;
+  ALLOCATE (unique[0], Unique, NUNIQUE);
+  NTEMP = 100;
+  ALLOCATE (templist, int, NTEMP);  
+  Nblobs = dRo = dDo = dRo2 = dDo2 = 0;
+
+  for (i = Nunique = 0; (i < Nstars - 1); i++) {
+    
+    /* check if this star is not already assigned to a clump */
+    if (stars[radec[i]].next_this_unique == NULL) {
+      
+      /* first, find all stars within 2*RADIUS of this star */
+      first = i;
+      Ntemp = 1;
+      dRA = 0;
+      templist[0] = radec[first];  
+      for (j = first + 1; (dRA <= 2.0*RADIUS) && (j < Nstars); j++) {
+	if (stars[radec[j]].next_this_unique != NULL)
+	  continue;
+	dRA  = COS * (stars[radec[j]].RA - stars[radec[first]].RA);  
+	dDec = stars[radec[j]].Dec - stars[radec[first]].Dec;
+	radius = hypot(dRA, dDec);
+	if (radius < 2.0*RADIUS) {
+	  /* add to templist: */
+	  templist[Ntemp] = radec[j];
+	  Ntemp ++;
+	  if (Ntemp == NTEMP - 1) {
+	    NTEMP += 100;
+	    REALLOCATE (templist, int, NTEMP); 
+	  }
+	}
+      }
+	     
+      if (Ntemp < 3) {
+	continue; /* check this pops out of the right level... */
+      }
+
+      /* next, find the centroid of this subset of stars */
+      dRA = dDec = dRA2 = dDec2 = 0;
+      for (j = 0; j < Ntemp; j++) {
+	dRA   += stars[templist[j]].RA;
+	dDec  += stars[templist[j]].Dec;
+	dRA2  += SQ(stars[templist[j]].RA);
+	dDec2 += SQ(stars[templist[j]].Dec);
+      }
+      RAo   = dRA / (double)Ntemp;
+      DECo  = dDec / (double)Ntemp;
+      dRA2  = sqrt(dRA2 / (double)Ntemp - SQ(RAo));
+      dDec2 = sqrt(dDec2 / (double)Ntemp - SQ(DECo));
+
+      /* then, find all stars within 1 RADIUS of this centroid */
+      Nclump = 0;
+      unique[0][Nunique].first_this_unique = (Star *) NULL;
+      for (j = 0; j < Ntemp; j++) {
+	dRA  = COS * (stars[templist[j]].RA - RAo);
+	dDec = stars[templist[j]].Dec - DECo;
+	radius = hypot(dRA, dDec);
+	if (radius < RADIUS) {
+	  if (Nclump != 0) {
+	    this_unique[0].next_this_unique = &stars[templist[j]];
+	    this_unique = &stars[templist[j]];
+	    this_unique[0].unique_number = Nunique;
+	  }
+	  else {
+	    unique[0][Nunique].first_this_unique = &stars[templist[j]];
+	    this_unique = &stars[templist[j]];
+	    this_unique[0].unique_number = Nunique;
+	  }
+	  Nclump ++;
+	}
+      }
+      
+      /* if too few in clump, eliminate, move on */
+      if (Nclump < 5) {
+	this_unique = unique[0][Nunique].first_this_unique;
+	unique[0][Nunique].first_this_unique = NULL;
+	while (this_unique != NULL) {
+	  next_unique = this_unique[0].next_this_unique;
+	  this_unique[0].unique_number = EMPTY;
+	  this_unique[0].next_this_unique = NULL;
+	  this_unique = next_unique;
+	}
+	continue;
+      }
+
+      /* count number of confused stars...
+      this_unique = unique[0][Nunique].first_this_unique;
+      for (j = 0, confused = FALSE; j < Nclump - 1; j++) {
+	next_unique = this_unique[0].next_this_unique;
+	for (k = j + 1; k < Nclump; k++) {
+	  if (this_unique[0].image_number == next_unique[0].image_number) {
+	    confused = TRUE;
+	  }
+	  next_unique = next_unique[0].next_this_unique;
+	}
+	this_unique = this_unique[0].next_this_unique;
+      } 
+       */
+
+      /* if this object is confused, ignore it and move on.  
+	 keep the "next_this_unique" values on, but remove
+	 the "unique_number" */
+
+      /*
+      
+      if (confused) {
+	this_unique = unique[0][Nunique].first_this_unique;
+	unique[0][Nunique].first_this_unique = NULL;
+	while (this_unique != NULL) {
+	  this_unique[0].unique_number = EMPTY;
+	  this_unique = this_unique[0].next_this_unique;
+	}
+	Nblobs ++;
+	continue;
+      }
+      */
+      
+      this_unique = unique[0][Nunique].first_this_unique;
+      for (j = 0; j < Nclump; j++) {
+	dRA   += this_unique[0].RA;
+	dDec  += this_unique[0].Dec;
+	dRA2  += SQ(this_unique[0].RA);
+	dDec2 += SQ(this_unique[0].Dec);
+	next_unique = this_unique[0].next_this_unique;
+      }
+      RAo   = dRA / (double)Nclump;
+      DECo  = dDec / (double)Nclump;
+      for (n = 0; n < Nsources; n++) {
+	dRA = COS * (RAo - sources[n].RA);
+	dDec = (DECo - sources[n].Dec);
+	radius = hypot(dRA, dDec);
+	if (radius < RADIUS) {
+	  sources[n].unique_number = Nunique;
+	  fprintf (stderr, "%d %d  %f %f  %f %f\n", 
+		   n, Nunique, RAo, DECo, sources[n].RA, sources[n].Dec);
+	  break;
+	}
+      } 
+      
+      unique[0][Nunique].Nmeasurements = Nclump;
+      unique[0][Nunique].Mrel = 0.0;
+      /* use only stars which show up on all images */
+      
+      dRo += dRA2;
+      dDo += dDec2;
+      dRo2 += dRA2*dRA2;
+      dDo2 += dDec2*dDec2;
+      Nunique ++;
+      if (Nunique == NUNIQUE - 1) { 
+	NUNIQUE += D_NUNIQUE;
+	REALLOCATE(unique[0], Unique, NUNIQUE);
+      }
+    }
+  } 
+
+  *N = Nunique;
+  dRo = dRo / (double)Nunique;
+  dDo = dDo / (double)Nunique;
+  dRo2 = sqrt(dRo2 / (double)Nunique - SQ(dRo));
+  dDo2 = sqrt(dDo2 / (double)Nunique - SQ(dDo));
+
+  fprintf (stderr, "%f   %f %f %f %f   %4d %3d\n", 3600*RADIUS, dRo, dDo, dRo2, dDo2, Nunique, Nblobs);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/help.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/help.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/help.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "lightcurve.h"
+
+void
+help (name)
+char name[];
+{
+  
+  fprintf (stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s",
+	   "USAGE: ", name, " (-P / -A) radius  \n",
+	   "\n",
+           "  Mandatory Flags:\n",
+           "  -P / -A      one of these must be used: \n",
+           "    -P  pixs     radius of search in pixels -- uses relative astrometry (rastro)\n",
+           "    -A  asec     radius of search in arcsec -- uses absolute astrometry  (astro)\n",
+           "  -im images   file with the list of images to use\n",
+           "\n",
+           "  Optional Flags:\n",
+           "  -o  file  output file for star data\n",
+           "  -midas    use MIDAS format time info from headers\n"
+           "  -h        print this list\n\n",
+	   "  ", name, " expects a series of (Ra,Dec) or (X,Y) pairs\n\n");
+  
+  exit (0);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/imstats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/imstats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/imstats.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "lightcurve.h"
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+  
+  Image   *images;
+  Star    *stars, *sources;
+  Unique  *unique;
+  int     *radec;
+  int      Nimages, Nstars, Nunique, Nsources;
+  int      i, j;
+
+  /*** this still needs to be fixed / updated */
+  args (argc, argv);
+
+  get_names (&images, &Nimages);
+  get_sources (&sources, &Nsources);
+  get_stars  (&stars, &Nstars, images, Nimages);
+  sort_stars (&radec, stars, Nstars);
+
+  get_unique (&unique, &Nunique, stars, radec, Nstars, sources, Nsources);
+
+  make_table (sources, Nsources, unique, images, Nimages);
+  
+}
+
+
+/* 
+   list of system wide variables that are (should be) defined in relphot.h:
+
+   RADIUS  (radius of search in pixels or arcsec)
+   A_LAMBDA (airmass extinction coefficient)
+   C_LAMBDA (zero point C_\lambda)
+   NLOOP   (desired number of interations)
+   SIG     (number of sigma for cloudiness criterion)
+   OUTFILE (output file)
+   PRINT   (print processing info?)
+   PIXELS  (RADIUS in pixels (rastro) or degrees (astro))
+   COS     (RA scaling factor = cos(DEC(center of field)) if astro, 
+                              = 1 if rastro )
+
+*/
+
+   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/jd.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/jd.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/jd.c	(revision 22322)
@@ -0,0 +1,72 @@
+      SUBROUTINE locate(xx,n,x,j)
+      INTEGER j,n
+      REAL x,xx(n)
+      INTEGER jl,jm,ju
+      jl=0
+      ju=n+1
+10    if(ju-jl.gt.1)then
+        jm=(ju+jl)/2
+        if((xx(n).gt.xx(1)).eqv.(x.gt.xx(jm)))then
+          jl=jm
+        else
+          ju=jm
+        endif
+      goto 10
+      endif
+      j=jl
+      return
+      END
+
+      FUNCTION julday(mm,id,iyyy)
+      INTEGER julday,id,iyyy,mm,IGREG
+      PARAMETER (IGREG=15+31*(10+12*1582))
+      INTEGER ja,jm,jy
+      jy=iyyy
+      if (jy.eq.0) pause 'julday: there is no year zero'
+      if (jy.lt.0) jy=jy+1
+      if (mm.gt.2) then
+        jm=mm+1
+      else
+        jy=jy-1
+        jm=mm+13
+      endif
+      julday=int(365.25*jy)+int(30.6001*jm)+id+1720995
+      if (id+31*(mm+12*iyyy).ge.IGREG) then
+        ja=int(0.01*jy)
+        julday=julday+2-ja+int(0.25*ja)
+      endif
+      return
+      END
+
+function mjd(yr,hr,sc)
+	implicit none
+	integer iyr, mn, n, jd, j, julday, idy
+	real*8 yr,hr,sc,mjd
+	real*4 month(12), dy
+	n = 12
+	data month/0,31,59,90,120,151,181,212,243,273,304,334/
+	iyr = yr
+	if((mod(iyr,4).eq.0.and.mod(iyr,100).ne.0).or.(mod(iyr,400).eq.0)) then
+	  dy = int(366.*(yr-iyr)+.0001) + 1
+	  do j = 3, 12
+	    month(j) = month(j) + 1.
+	  end do
+	else
+	  dy = int(365.*(yr-iyr)+.0001) + 1
+	end if
+c 
+c locate, from Numrec library
+c
+	call locate(month,n,dy,mn)
+	dy = dy - month(mn)
+	idy = dy
+c
+c julday, from Numrec library
+c
+	jd = julday(mn,idy,iyr)
+	mjd = jd - 2440000.5
+	mjd = mjd + hr / 24. + sc / 86400. / 2.
+	end
+
+
+z
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/lightcurve.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/lightcurve.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/lightcurve.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "lightcurve.h"
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+  
+  Image   *images;
+  Star    *stars, *sources;
+  Unique  *unique;
+  int     *radec;
+  int      Nimages, Nstars, Nunique, Nsources;
+  int      i, j;
+
+  /*** this still needs to be fixed / updated */
+  args (argc, argv);
+
+  get_names (&images, &Nimages);
+  get_sources (&sources, &Nsources);
+  get_stars  (&stars, &Nstars, images, Nimages);
+  sort_stars (&radec, stars, Nstars);
+
+  get_unique (&unique, &Nunique, stars, radec, Nstars, sources, Nsources);
+
+  make_table (sources, Nsources, unique, images, Nimages);
+  
+}
+
+
+/* 
+   list of system wide variables that are (should be) defined in relphot.h:
+
+   RADIUS  (radius of search in pixels or arcsec)
+   A_LAMBDA (airmass extinction coefficient)
+   C_LAMBDA (zero point C_\lambda)
+   NLOOP   (desired number of interations)
+   SIG     (number of sigma for cloudiness criterion)
+   OUTFILE (output file)
+   PRINT   (print processing info?)
+   PIXELS  (RADIUS in pixels (rastro) or degrees (astro))
+   COS     (RA scaling factor = cos(DEC(center of field)) if astro, 
+                              = 1 if rastro )
+
+*/
+
+   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/make_table.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/make_table.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/make_table.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "lightcurve.h"
+
+void make_table (sources, Nsources, unique, images, Nimages)
+Star   *sources;
+int     Nsources;
+Unique *unique;
+Image  *images;
+int     Nimages;
+{
+
+  double  N, M, dM;
+  int     i, j, n, im;
+  Star   *next_measurement;
+  FILE   *f;
+  double  dR, dD;
+
+  if (!strcmp(OUTFILE, "-")) {
+    fprintf (stderr, "using stdout\n");
+    f = stdout;
+  }
+  else {
+    fprintf (stderr, "using %s for output\n", OUTFILE);
+    f = fopen (OUTFILE, "w");
+    if (f == NULL) {
+      fprintf (stderr, "could not open output file, using stdout\n");
+      f = stdout;
+    }
+  }
+  
+  fprintf (f, "# This is a table of data from relphot.\n");
+  fprintf (f, "# This table contains the following data:\n");
+  if (PIXELS) 
+    fprintf (f, "# star number, <X> dX  <Y> dY  <Mrel> dMrel  Nstars\n");
+  else
+    fprintf (f, "# star number, <RA> dRA  <Dec> dDec  <Mrel> dMrel  Nstars\n");
+
+  for (j = 0; j < Nsources; j++) {
+    fprintf (f, "%5d %10.6f %10.6f ", j, sources[j].RA, sources[j].Dec);
+    if (sources[j].unique_number == EMPTY) {
+      fprintf (f, "%5d\n", 0.0);
+      continue;
+    }
+    N = unique[sources[j].unique_number].Nmeasurements;
+    fprintf (f, "%5.0f\n", N);
+    next_measurement = unique[sources[j].unique_number].first_this_unique;
+    M = 100.0; dM = 0.0;
+    for (n = 0; n < N; n++) {
+      im = next_measurement[0].image_number;
+      dR = (next_measurement[0].RA - images[im].RA_O);
+      dD = (next_measurement[0].Dec - images[im].DEC_O);
+      M = next_measurement[0].m - MCAL(images[im], dR, dD);
+      dM = next_measurement[0].dm;
+      dR = (next_measurement[0].RA - sources[j].RA);
+      dD = (next_measurement[0].Dec - sources[j].Dec);
+      fprintf (f, "%-40s  %15.6f  %7.3f %7.3f  %7.3f %7.3f\n", images[im].name, images[im].JD, dR, dD, M, dM);
+      next_measurement = next_measurement[0].next_this_unique;
+    }
+  }
+  fclose (f);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/match_stars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/match_stars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/match_stars.c	(revision 22322)
@@ -0,0 +1,84 @@
+# include "relphot.h"
+# define D_NUNIQUE 1000;
+
+void match_stars (unique, N, stars, radec, Nstars, sources, Nsources)
+Unique **unique;
+int    *N;
+Star   *stars;
+int    *radec;
+int     Nstars;
+Source *sources;
+int     Nsources;
+{
+
+  int i, j, link, k, good, Nchain, NUNIQUE, Nunique;
+  Unique  temp_unique;
+  Star   *next_unique, *this_unique;
+  double radius, dRA, dDec;
+
+  NUNIQUE = D_NUNIQUE;
+  ALLOCATE (unique[0], Unique, NUNIQUE);
+
+  for (i = Nunique = 0; (i < Nstars - 1); i++) {
+    
+    dRA = COS * (stars[radec[i+1]].RA - stars[radec[i]].RA);
+
+    if (dRA < 0.0) {
+      fprintf (stderr, "stars out of order!!!  %d  %d  %f  %f\n", 
+	       radec[i+1], radec[i], stars[radec[i+1]].RA, stars[radec[i]].RA);
+    }
+
+    if ((fabs(dRA) < RADIUS) && (stars[radec[i]].next_this_unique == NULL)) {
+
+      /* set up the chain */
+      for (link = i, j = link + 1, Nchain = 1; (fabs(dRA) < RADIUS) && (j < Nstars); j++) {
+	dDec   = stars[radec[j]].Dec - stars[radec[link]].Dec;
+	radius = hypot(dRA, dDec);
+	if (radius < RADIUS) {
+	  stars[radec[link]].next_this_unique = &stars[radec[j]];
+	  link = j;
+	  Nchain ++;
+	}
+	if (j < Nstars - 1)
+	  dRA  = COS * (stars[radec[j + 1]].RA - stars[radec[link]].RA);
+      }
+
+      /* are any 2 links on the same image? */
+      this_unique = &stars[radec[i]];
+      for (j = 0, good = TRUE; j < Nchain - 1; j++) {
+	next_unique = this_unique[0].next_this_unique;
+	for (k = j + 1; k < Nchain; k++) {
+	  if (this_unique[0].image_number == next_unique[0].image_number) {
+	    good = FALSE;
+	    fprintf (stderr, "clump on image %d, stars %d and %d\n", 
+		     this_unique[0].image_number, 
+		     this_unique[0].star_number, 
+		     next_unique[0].star_number);
+	  }
+	  next_unique = next_unique[0].next_this_unique;
+	}
+	this_unique = this_unique[0].next_this_unique;
+      }
+
+      /* if not, add chain to unique[] */
+      if ((good) && (Nchain > 1)) {
+	this_unique = unique[0][Nunique].first_this_unique = &stars[radec[i]];
+	while (this_unique != NULL) {
+	  this_unique[0].unique_number = Nunique;
+	  this_unique = this_unique[0].next_this_unique;
+	}
+	unique[0][Nunique].Nmeasurements = Nchain;
+	Nunique ++;
+	if (Nunique == NUNIQUE - 1) { 
+	  NUNIQUE += D_NUNIQUE;
+	  REALLOCATE(unique[0], Unique, NUNIQUE);
+	}
+      }
+    }
+  } 
+
+  *N = Nunique;
+  fprintf (stderr, "Nunique: %d\n", Nunique);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/mjd.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/mjd.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/mjd.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "lightcurve.h"
+# define GREG (15+31*(10+12*1582))
+
+double mjd (year, hour, second)
+double year, hour, second;
+{
+
+  int iyear;
+  int Months[] = {0,31,59,90,120,151,181,212,243,273,304,334};
+
+  /* convert year, which contains year + day, into year, month, day */
+  iyear = year;
+  if ((!(iyear % 4) && (iyear % 100)) || !(iyear % 400)) { /* leap year */
+    day = 366*(year - iyear) + 1;
+    for (i = 2; i < 12; i++) 
+      Months[i]++;
+  }
+  else {
+    day = 365*(year - iyear) + 1;
+  }
+  for (i = 0; month[i] < day; i++);
+  month = Months[i-1];
+  
+  if (day + 31*(month + 12*iyear) >= GREG) 
+    extra = 2 - (int)(0.01*jy) + (int)(0.25*((int)(0.01*jy)));
+  else 
+    extra = 0;
+
+  if (year == 0) {
+    fprintf (stderr, "error: there is no year zero\n");
+    exit (0);
+  }
+  if (year < 0) 
+    year += 1.0;
+  if (month > 2) 
+    jmonth = month + 1;
+  else {
+    year -= 1;
+    month += 13;
+  }
+
+  julday = (int)(365.25*year)+int(30.6001*jm)+id+1720995
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/parse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/parse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/parse.c	(revision 22322)
@@ -0,0 +1,35 @@
+# include "lightcurve.h"
+
+int parse (X, NX, line)
+double *X;
+int NX;
+char *line;
+{
+
+  int i;
+  char *word;
+  char *ptr;
+
+  word = line;
+  for (i = 0; i < NX - 1; i++)
+    word = nextword (word);
+
+  *X = strtod (word, &ptr);
+  if (ptr == word)
+    return (FALSE);
+  else
+    return (TRUE);
+}
+
+
+char *nextword(string)
+char *string;
+{
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (; isspace (*string); string++);
+  for (; (*string != 0) && !isspace (*string); string++);
+  for (; isspace (*string); string++);
+  return (string);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/relphot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/relphot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/relphot.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "lightcurve.h"
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+  
+  Image   *images;
+  Star    *stars;
+  Unique  *unique;
+  int     *radec;
+  int      Nimages, Nstars, Nunique;
+  int      i, j;
+
+  /*** this still needs to be fixed / updated */
+  args (argc, argv);
+
+  get_names  (&images, &Nimages);
+  get_stars  (&stars, &Nstars, images, Nimages);
+  sort_stars (&radec, stars, Nstars);
+
+  get_unique (&unique, &Nunique, stars, radec, Nstars);
+  count_unique (images, Nimages, Nstars);
+
+  for (i = 0; i < NLOOP; i++) {
+    set_Mcal (images, Nimages);
+    get_Mrel (unique, images, Nunique);
+    get_Mcal (images, unique, Nimages);
+    ChiSquare (unique, images, Nunique, Nimages);
+  }  
+ 
+
+  if (PRINT) {
+    alter_headers (images, Nimages);
+    make_table (unique, images, Nunique);
+  }
+  
+}
+
+
+/* 
+   list of system wide variables that are (should be) defined in relphot.h:
+
+   RADIUS  (radius of search in pixels or arcsec)
+   A_LAMBDA (airmass extinction coefficient)
+   C_LAMBDA (zero point C_\lambda)
+   NLOOP   (desired number of interations)
+   SIG     (number of sigma for cloudiness criterion)
+   OUTFILE (output file)
+   PRINT   (print processing info?)
+   PIXELS  (RADIUS in pixels (rastro) or degrees (astro))
+   COS     (RA scaling factor = cos(DEC(center of field)) if astro, 
+                              = 1 if rastro )
+
+*/
+
+   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/remove_argument.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/remove_argument.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/remove_argument.c	(revision 22322)
@@ -0,0 +1,21 @@
+# include "lightcurve.h"
+
+int remove_argument (N, argc, argv)
+int    N;
+int   *argc;
+char **argv;
+{
+
+  int i;
+
+  if ((N != (int)NULL) && (N != 0)) {
+    (*argc)--;
+    for (i = N; i < *argc; i++) {
+      argv[i] = argv[i+1];
+    }
+  }
+
+  return (N);
+    
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/scan_line.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/scan_line.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/scan_line.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "lightcurve.h"
+
+/*************/
+int
+scan_line (f, line) 
+FILE *f;
+char line[];
+{
+
+  int i, status;
+  char c;
+  
+  status = EOF + 1;
+  
+  for (i = 0, c = 0; (c != '\n') && (status != EOF); i++) {
+    status = fscanf (f, "%c", &c);
+    line[i] = c;
+  }
+  line[i - 1] = 0;  /* this could make things crash! */
+
+  if (i > 1) {
+    status = EOF + 1;
+  }
+
+  return (status);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/set_Mcal.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/set_Mcal.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/set_Mcal.c	(revision 22322)
@@ -0,0 +1,53 @@
+# include "lightcurve.h"
+
+void set_Mcal (images, Nimages)
+Image    *images;
+int       Nimages;
+{
+
+  int i;
+  double clouds, Clouds, dClouds, R, R2, N;
+  
+  R = R2 = N = 0.0;
+  for (i = 0; i < Nimages; i++) {
+    images[i].clouds = images[i].Mcal + C_LAMBDA - A_LAMBDA * images[i].airmass + images[i].AmF;
+    if (!images[i].empty && images[i].fixed) {
+      R  +=     images[i].clouds;
+      R2 += SQ (images[i].clouds);
+      N  += 1.0;
+    }
+  }
+
+  Clouds  = R / N;
+  dClouds = sqrt (R2 / N - Clouds*Clouds);
+
+  fprintf (stderr, "Clouds = %f, dClouds = %f, N / Nimages: %f / %d\n", Clouds, dClouds, N, Nimages);
+
+  dClouds = MAX (0.0001, dClouds);
+
+  for (i = 0; i < Nimages; i++) {
+    if (fabs(images[i].clouds - Clouds) > SIG*dClouds) 
+      images[i].fixed = FALSE;
+    else {
+      images[i].fixed  = TRUE;
+      images[i].Mcal   = - C_LAMBDA + A_LAMBDA * images[i].airmass - images[i].AmF;
+      images[i].clouds = 0.0;
+    }
+  }
+}
+
+/*  BIG IMPORTANT NOTE:  equations (3) and (7) in Magnier et al 1992, A&A 96, 379
+    are not consistent with positive defined A_LAMBDA.  
+
+    the equations should read:
+
+    (3)  M_app = c_lambda + m - a_lambda*\zeta ... + clouds    (... is color term)
+    (7)  M_cal = -c_lambda + a_lambda*\zeta
+
+    The point is that a_lambda is a positive coeff, so as \zeta increases, the
+    image is *less* sensitive, so M_cal goes up.  conversely, c_lambda is also
+    a positive defined number, so as c_lambda increases, the image is *more* 
+    sensitive.  Since the a_lambda and c_lambda have opposite senses, they 
+    must have opposite signs.  
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/sort_stars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/sort_stars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/lightcurve/src/sort_stars.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "lightcurve.h"
+
+// construct an index for the stars sorted by RA
+void sort_stars (int **index, Star *stars, int Nstars) {
+  
+  int i;
+  double *RAs;
+
+  ALLOCATE (myIndex, int, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    myIndex[i] = i;
+  }
+
+# define SWAPFUNC(A,B){ int tmp = myIndex[A]; myIndex[A] = myIndex[B]; myIndex[B] = tmp; }
+# define COMPARE(A,B)(stars[myIndex[A]].RA < stars[myIndex[B]].RA)
+
+  OHANA_SORT (Nstars, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+  *index = myIndex;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/Makefile	(revision 22322)
@@ -0,0 +1,37 @@
+default: markrock
+help:
+	@echo "make options: markrock (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/markrock
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+markrock: $(BIN)/markrock.$(ARCH)
+install: $(DESTBIN)/markrock
+
+MARKROCK = \
+$(SRC)/markrock.$(ARCH).o 	$(SRC)/gcatalog.$(ARCH).o   	\
+$(SRC)/coordops.$(ARCH).o	$(SRC)/sorts.$(ARCH).o 		\
+$(SRC)/ConfigInit.$(ARCH).o 	$(SRC)/photometry.$(ARCH).o \
+$(SRC)/check_lockfile.$(ARCH).o \
+$(SRC)/find_rocks.$(ARCH).o 	$(SRC)/wcatalog.$(ARCH).o	\
+$(SRC)/gcatstats.$(ARCH).o	$(SRC)/wrocks.$(ARCH).o 	\
+$(SRC)/check_permissions.$(ARCH).o \
+$(SRC)/gregions.$(ARCH).o \
+$(SRC)/aregion.$(ARCH).o \
+$(SRC)/find_bright_stars.$(ARCH).o \
+$(SRC)/load_gsc_data.$(ARCH).o \
+$(SRC)/count_neighbors.$(ARCH).o
+
+$(MARKROCK): $(INC)/markrock.h
+$(BIN)/markrock.$(ARCH): $(MARKROCK)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/doc/example.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/doc/example.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/doc/example.txt	(revision 22322)
@@ -0,0 +1,13 @@
+
+rats: foreach f ( */*.cpt )
+foreach? echo $f
+foreach? echo $f >> bright.log
+foreach? (markstar -v $f >> bright.dat) >>& bright.log
+foreach? end
+
+
+
+ echo $f
+ echo $f >> bright.log
+ (markstar -v $f >> bright.dat) >>& bright.log
+ end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/include/markrock.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/include/markrock.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/include/markrock.h	(revision 22322)
@@ -0,0 +1,46 @@
+# include <ohana.h>
+# include <dvo.h>
+
+typedef struct {
+  Coords coords;
+  double *X, *Y;
+  int *N;
+  double RA[2], DEC[2];
+  double Area, density, spacing;
+} CatStats;
+
+typedef struct {
+  double ra[3];
+  double dec[3];
+  double X[3];
+  double Y[3];
+  unsigned int t[3];
+  double mag[3];
+  int N[3];
+} Rocks;
+
+/* global variables set in parameter file */
+
+int    VERBOSE;
+int    RESET;
+int    FORCE_RUN;
+
+char   GSCFILE[256];
+char   GSCDIR[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char   RockCat[256];
+
+double RADIUS;
+double MAX_RADIUS;
+double MAX_SPEED;
+double MAX_DELAY;
+double ZERO_POINT;
+
+double BRIGHT_HALO_MAG;
+double BRIGHT_HALO_SLOPE;
+double ROCK_NEIGHBOR_RADIUS;
+int    ROCK_NEIGHBOR_NMAX;
+
+PhotCodeData photcodes;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "markrock.h"
+
+ConfigInit (int *argc, char **argv) {
+
+  char *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  /* used in other pipeline functions */
+  ScanConfig (config, "CATDIR",                 "%s",  0, CATDIR);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig (config, "PHOTCODE_FILE",         	"%s",  0, MasterPhotcodeFile);
+
+  /* unique to markrock */
+  ScanConfig (config, "ROCK_NEIGHBOR_RADIUS",   "%lf", 0, &ROCK_NEIGHBOR_RADIUS);
+  ScanConfig (config, "ROCK_NEIGHBOR_NMAX",     "%d",  0, &ROCK_NEIGHBOR_NMAX);
+
+  ScanConfig (config, "ROCK_RADIUS",            "%lf", 0, &RADIUS);
+  ScanConfig (config, "ROCK_MAX_RADIUS",        "%lf", 0, &MAX_RADIUS);
+  ScanConfig (config, "ROCK_MAX_SPEED",         "%lf", 0, &MAX_SPEED);
+  ScanConfig (config, "ROCK_MAX_DELAY",         "%lf", 0, &MAX_DELAY);
+  ScanConfig (config, "ROCK_CATALOG",           "%s",  0, RockCat);
+
+  ScanConfig (config, "BRIGHT_HALO_MAG",        "%lf", 0, &BRIGHT_HALO_MAG);
+  ScanConfig (config, "BRIGHT_HALO_SLOPE",      "%lf", 0, &BRIGHT_HALO_SLOPE);
+  ScanConfig (config, "GSCFILE",                "%s",  0, GSCFILE);
+  ScanConfig (config, "GSCDIR",                 "%s",  0, GSCDIR);
+  ScanConfig (config, "ZERO_PT",                "%lf", 0, &ZERO_POINT);
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/count_neighbors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/count_neighbors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/count_neighbors.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include "markrock.h"
+
+count_neighbors (rocks, Nrocks, catalog, catstats)
+Rocks *rocks;
+int Nrocks;
+Catalog catalog[];
+CatStats catstats[];
+{
+
+  float *x, *y;
+  double X, Y, dx, dy, dr;
+  int i, j, k;
+
+  x = catstats[0].X;
+  y = catstats[0].Y;
+
+  for (i = 0; i < Nrocks; i++) {
+    for (k = 0; k < 3; k++) {
+      X = rocks[i].X[k];
+      Y = rocks[i].Y[k];
+      rocks[i].N[k] = 0;
+      for (j = 0; j < catalog[0].Naverage; j++) {
+	if ((dx = X - x[j]) > ROCK_NEIGHBOR_RADIUS) continue;
+	if (dx < -ROCK_NEIGHBOR_RADIUS) break;
+	dy = Y - y[j];
+	dr = hypot (dx, dy);
+	if (dr > ROCK_NEIGHBOR_RADIUS) continue;
+	rocks[i].N[k] ++;
+      }
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_bright_stars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_bright_stars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_bright_stars.c	(revision 22322)
@@ -0,0 +1,144 @@
+# include "markrock.h"
+
+void find_bright_stars (Catalog *catalog, CatStats *catstats) {
+
+  int i, j, n, m, first_j, Nave, Ngsc, Nmark;
+  Catalog GSCdata;
+  double MinRA, MinDEC, MaxRA, MaxDEC, RaCenter, DecCenter;
+  Coords *tcoords;
+  double *X1, *Y1, *X2, *Y2;
+  int *N1, *N2;
+  char *mark;
+  double dX, dY, dR, MaxDist, MaxDist1, MaxRadius, radius, radius2;
+
+  load_gsc_data (&GSCdata, catstats);
+
+  Nave = catalog[0].Naverage;
+  Ngsc = GSCdata.Naverage;
+    
+  /* in the function below, it is better to have the catalog with
+     more stars associated with index 2 (j) */ 
+  X2 = catstats[0].X;
+  Y2 = catstats[0].Y;
+  N2 = catstats[0].N;
+  ALLOCATE (X1, double, Ngsc);
+  ALLOCATE (Y1, double, Ngsc);
+  ALLOCATE (N1, int, Ngsc);
+  ALLOCATE (mark, char, Nave);
+  bzero (mark, Nave);
+
+  tcoords = &catstats[0].coords;
+  for (i = 0; i < Ngsc; i++) {
+    RD_to_XY (&X1[i], &Y1[i], GSCdata.average[i].R, GSCdata.average[i].D, tcoords);
+    N1[i] = i;
+  }
+  if (Ngsc > 1) sort_coords_index (X1, Y1, N1, Ngsc);
+  
+  /* first find stellar halos */
+  /* max radius (mag = -1) */
+  /** the j index moves quickly and is better associated with the catalog with more stars */
+  MaxRadius = BRIGHT_HALO_SLOPE * (-1.0 - BRIGHT_HALO_MAG); 
+  /** find catalog stars near GSC stars **/
+  for (i = j = 0; (i < Ngsc) && (j < Nave); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -2*MaxRadius) {
+      i++;
+      continue;
+    }
+    if (dX >= 2*MaxRadius) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    radius = MAX (BRIGHT_HALO_SLOPE * (0.001*GSCdata.average[N1[i]].M - BRIGHT_HALO_MAG), 0.0);
+    if (radius == 0) {
+      i++; 
+      continue;
+    }
+    radius2 = radius*radius;
+    for (; (dX > -2*radius) && (j < Nave); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      dR = dX*dX + dY*dY;
+      if (dR < radius2) {  /* new measurement of this star */
+	mark[j] = TRUE;
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  
+# if (0)
+  /* next find y spikes */
+  /** find catalog stars near GSC stars **/
+  for (i = j = 0; (i < Ngsc) && (j < Nave); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -BRIGHT_YTRAIL_WIDTH) {
+      i++;
+      continue;
+    }
+    if (dX >= BRIGHT_YTRAIL_WIDTH) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    MaxDist = MAX (BRIGHT_YTRAIL_SLOPE * (0.001*GSCdata.average[N1[i]].M - BRIGHT_YTRAIL_MAG), 0.0);
+    for (; (dX > -BRIGHT_YTRAIL_WIDTH) && (j < Nave); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      if ((fabs(dX) < BRIGHT_YTRAIL_WIDTH) && (fabs(dY) < MaxDist)) {  /* star on spike */
+	mark[j] = TRUE;
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  
+  /* next find x spikes */
+  /** find catalog stars near GSC stars **/
+  MaxDist = MAX (BRIGHT_XTRAIL_SLOPE * (-1.0 - BRIGHT_XTRAIL_MAG), 0.0);
+  for (i = j = 0; (i < Ngsc) && (j < Nave); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -MaxDist) {
+      i++;
+      continue;
+    }
+    if (dX >= MaxDist) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    MaxDist1 = MAX (BRIGHT_XTRAIL_SLOPE * (0.001*GSCdata.average[N1[i]].M - BRIGHT_XTRAIL_MAG), 0.0);
+    for (; (dX > -MaxDist1) && (j < Nave); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      if ((fabs(dY) < BRIGHT_XTRAIL_WIDTH) && (fabs(dX) < MaxDist1)) {  /* star on spike */
+	mark[j] = TRUE;
+      }
+    }
+    j = first_j;
+    i++;
+  }
+# endif
+
+  /* done with search, mark selected stars */
+  Nmark = 0;
+  for (i = 0; i < Nave; i++) {
+    if (mark[i]) {
+      Nmark ++;
+      catalog[0].average[N2[i]].code = ID_BLEED;
+    }
+  } 
+  fprintf (stderr, "marked %d stars for bright star\n", Nmark);
+  
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (mark);
+  free (GSCdata.average);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_ghosts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_ghosts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_ghosts.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include "markrock.h"
+
+find_ghosts (catalog, catstats, filename, image, Nimage)
+Catalog catalog[];
+CatStats catstats[];
+char *filename;
+Image image[];
+int Nimage;
+{
+
+  int i, j, first_j, Nave, Ngsc, Nregions;
+  Catalog GSCdata, Ghostdata;
+  double MinRA, MinDEC, MaxRA, MaxDEC, RaCenter, DecCenter;
+  Coords *tcoords;
+  float *X1, *Y1, *X2, *Y2;
+  int *N1, *N2, *match;
+  char *mark;
+  double dX, dY, dR, RADIUS2, BRIGHT_RADIUS, radius, X, Y, R, D;
+  Image timage;
+  GSCRegion *region, *gregions();
+
+  for (i = 0; i < Nimage; i++) {
+    
+    /* coords structure for ghost image */
+    timage = image[i];
+    timage.coords.cdelt1 = -1*image[i].coords.cdelt1;
+    timage.coords.cdelt2 = -1*image[i].coords.cdelt2;
+    timage.coords.crpix1 = 2*OPTICAL_AXIS1 - image[i].coords.crpix1;
+    timage.coords.crpix2 = 2*OPTICAL_AXIS2 - image[i].coords.crpix2;
+
+    region = gregions2 (&timage, &Nregions);
+    
+    /* find ghost stars */
+    load_gsc_data_ghost (&GSCdata, region, Nregions, &timage);
+
+    /* refect ghost stars to find locations of ghosts */
+    ALLOCATE (Ghostdata.average, Average, MAX (GSCdata.Naverage, 1));
+    for (j = 0; j < GSCdata.Naverage; j++) {
+      RD_to_XY (&X, &Y, GSCdata.average[j].R, GSCdata.average[j].D, &timage);
+      fXY_to_RD (&Ghostdata.average[j].R, &Ghostdata.average[j].D, X, Y, &image[i]);
+      Ghostdata.average[j].M = GSCdata.average[j].M;
+    }    
+    Ghostdata.Naverage = GSCdata.Naverage;
+
+    /* match ghosts and image stars, mark ghosts */
+    find_matches (catalog, catstats, &Ghostdata, i);
+
+    free (Ghostdata.average);
+    free (GSCdata.average);
+  }
+
+}
+
+/* 
+
+   just to be clear on some of the terms:
+
+   the "ghost" is the fuzzy patch of light on an image caused by
+     a reflected star
+
+   the "ghost star" is the star in the sky which causes a ghost
+
+   the "ghost image" is the image location on the sky 
+     where ghost stars may come from.
+
+   the "image stars" are observed stars at the location of the
+     ghost - these are detections of the ghost
+
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_group.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_group.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_group.c	(revision 22322)
@@ -0,0 +1,69 @@
+# include "markrock.h"
+
+find_group (catstats, mark, Npts, i, ANGLE)
+     CatStats catstats[];
+     double *ANGLE;
+     int i, Npts;
+     char *mark;
+{
+ 
+  float *R, *D;
+  int j, N, bin, Ndegbin;
+  unsigned short int *A;
+  double Ra, De, dR, dD, angle, limit;
+
+   /* assign some parameter values */
+  
+  R = catstats[0].X;
+  D = catstats[0].Y;
+  limit = 10.0*sqrt((double)(M_PI*RADIUS*RADIUS*catstats[0].density)/(double)(NBINS));
+  Ndegbin = NBINS / 180.0;
+  ALLOCATE (A, unsigned short int, NBINS)
+  bzero (A, NBINS*sizeof(short));
+
+  /* look for points concentrated in an angle bin */
+  if (mark[i]) return (FALSE);
+  Ra = R[i];
+  De = D[i];
+  N = 0;
+  /* points east */
+  for (j = i + 1; (j < Npts) && (R[j] - Ra < RADIUS); j++) {
+    if (mark[j]) continue;
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      angle = atan2 (dD,dR);
+      if (!finite(angle)) 
+	continue;
+      if (angle < 0) angle += M_PI;
+      bin = 1 + angle*DEG_RAD*Ndegbin;
+      A[bin] ++;
+      if (A[bin] > limit) {
+	*ANGLE = (bin - 1.0) / (DEG_RAD*Ndegbin);
+	return (TRUE);
+      }
+      N ++;
+    }
+  }
+  /* points west */
+  for (j = i - 1; (j >= 0) && (Ra - R[j] < RADIUS); j--) {
+    if (mark[j]) continue;
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      angle = atan2 (dD,dR);
+      if (!finite(angle)) 
+	continue;
+      if (angle < 0) angle += M_PI;
+      bin = 1 + angle*DEG_RAD*Ndegbin;
+      A[bin] ++;	
+      if (A[bin] > limit) {
+	*ANGLE = (bin - 1.0) / (DEG_RAD*Ndegbin);
+	return (TRUE);
+      }
+      N ++;
+    }
+  }
+  return (FALSE);
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_rocks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_rocks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_rocks.c	(revision 22322)
@@ -0,0 +1,189 @@
+# include "markrock.h"
+
+Rocks *find_rocks (Catalog *catalog, CatStats *catstats, int *nrocks) {
+
+  int i, j, k, n, m, N, M, first_j, NOBJECT, Nobject, Nrocks, NROCKS;
+  double X, Y, RADIUS2;
+  double *X1, *Y1;
+  double dX, dY, dR;
+  float dtime;
+  int *N1, *N0;
+  unsigned int Tref;
+  int *T1, Ttmp;
+  int Nstar, Nghost, Ng;
+  unsigned int flags;
+  unsigned short NotRock, IsProper;
+  Coords *tcoords;
+  double ax, ay, cx, cy, dt, dt1, dt2, dt3, dX1, dX2, speed, distance;
+  Rocks *rocks;
+  struct timeval now, then;  
+  double MaxSeparation;
+
+  gettimeofday (&then, (void *) NULL);
+
+  Nstar = catalog[0].Naverage;
+
+  NOBJECT = 1000;
+  ALLOCATE (X1, double, NOBJECT);
+  ALLOCATE (Y1, double, NOBJECT);
+  ALLOCATE (T1, int,   NOBJECT);
+  ALLOCATE (N1, int,   NOBJECT);
+  Nobject = 0;
+  N0 = catstats[0].N;
+
+  NROCKS = 3000;
+  ALLOCATE (rocks, Rocks, NROCKS);
+  Nrocks = 0;
+
+  Tref = 0;
+  NotRock = (ID_ROCK ^ 0xffff);
+  IsProper = ID_USNO | ID_PROPER;
+  for (i = 0; i < Nstar; i++) {
+    N = N0[i];
+    if (RESET && ((catalog[0].average[N].code & ID_ROCK) == ID_ROCK)) {
+      /* resets both relevant bits */
+      catalog[0].average[N].code &= NotRock;
+    }
+    if (RESET && (catalog[0].average[N].code == ID_COSMIC)) {
+      /* resets both relevant bits */
+      catalog[0].average[N].code = 0;
+    }
+    /* accept a measurement if it is either unmarked or marked as a proper motion star */
+    if ((catalog[0].average[N].Nm == 1) && (catalog[0].average[N].Nn > 0) && ((catalog[0].average[N].code == 0) || ((catalog[0].average[N].code & IsProper) == IsProper))) {
+      if (Tref == 0) Tref = catalog[0].measure[catalog[0].average[N].offset].t;
+      X1[Nobject] = catstats[0].X[i];
+      Y1[Nobject] = catstats[0].Y[i];
+      if (catalog[0].measure[catalog[0].average[N].offset].t > Tref) 
+	T1[Nobject] = catalog[0].measure[catalog[0].average[N].offset].t - Tref;
+      else {
+	Ttmp = Tref - catalog[0].measure[catalog[0].average[N].offset].t;
+	T1[Nobject] = -Ttmp;
+      }      
+      N1[Nobject] = N;
+      Nobject ++;
+      if (Nobject == NOBJECT - 1) {
+	NOBJECT += 1000;
+	REALLOCATE (X1, double, NOBJECT);
+	REALLOCATE (Y1, double, NOBJECT);
+	REALLOCATE (T1, int, NOBJECT);
+	REALLOCATE (N1, int,   NOBJECT);
+      }
+    }
+  }
+  REALLOCATE (X1, double, Nobject);
+  REALLOCATE (Y1, double, Nobject);
+  REALLOCATE (T1, int, Nobject);
+  REALLOCATE (N1, int,   Nobject);
+  if (Nobject > 1) sort_set (X1, Y1, T1, N1, Nobject);
+
+  fprintf (stderr, "Nobj: %d\n", Nobject);
+
+  MaxSeparation = MAX_SPEED * MAX_DELAY;
+  fprintf (stderr, "%f %f %f %f %f\n", MaxSeparation, MAX_RADIUS, RADIUS, MAX_SPEED, MAX_DELAY);
+
+  /** find 3 rocks in a line **/
+  for (i = 0; i < Nobject - 2; i++) {
+    fprintf (stderr, ".");
+    for (j = i + 1; j < Nobject - 1; j++) {
+      if (X1[j] - X1[i] > MaxSeparation) goto NEXTi;
+      if (X1[i] - X1[j] > MaxSeparation) goto NEXTj;
+      if (T1[i] == T1[j]) continue;
+      dt = (int)(T1[j] - T1[i]);
+      if (fabs(dt) > MAX_DELAY) continue;
+      ax = (X1[j] - X1[i])/dt;
+      cx = (X1[i]*T1[j] - X1[j]*T1[i])/dt;
+      ay = (Y1[j] - Y1[i])/dt;
+      cy = (Y1[i]*T1[j] - Y1[j]*T1[i])/dt;
+      speed = hypot (ax, ay);
+      if (speed > MAX_SPEED) continue;
+      for (k = j + 1; k < Nobject; k++) {
+	if (X1[k] - X1[j] > MaxSeparation) goto NEXTj;
+	if (X1[j] - X1[k] > MaxSeparation) goto NEXTk;
+	if (T1[i] == T1[k]) continue;
+	if (T1[j] == T1[k]) continue;
+	dt1 = fabs(dt);
+	dt2 = fabs ((int)(T1[i] - T1[k]));
+	dt3 = fabs ((int)(T1[j] - T1[k]));
+	dt1 = MIN(MIN(dt1,dt2),dt3);
+	dt3 = MAX(MAX(dt1,dt2),dt3);
+	if (fabs(dt3) > MAX_DELAY) continue;
+	/* errors are too large by this point */
+	if (RADIUS * dt3 / dt1 > MAX_RADIUS) continue; 
+	dX1 = T1[k]*ax - X1[k] + cx;
+	dX2 = T1[k]*ay - Y1[k] + cy;
+	distance = hypot (dX1, dX2);
+	/*
+        fprintf (stderr, "%d %d %d %f %f %d %f %f\n", i, j, k, ax, cx, T1[k], X1[k], dX1);
+	if (k > 100) exit (0);
+	*/
+	if (distance < RADIUS){
+	  catalog[0].average[N1[i]].code |= ID_ROCK;
+	  catalog[0].average[N1[j]].code |= ID_ROCK;
+	  catalog[0].average[N1[k]].code |= ID_ROCK;
+
+	  rocks[Nrocks].X[0]     = X1[i];
+	  rocks[Nrocks].Y[0]     = Y1[i];
+	  rocks[Nrocks].ra[0]    = catalog[0].average[N1[i]].R;
+	  rocks[Nrocks].dec[0]   = catalog[0].average[N1[i]].D;
+
+	  m = catalog[0].average[N1[i]].offset;
+	  rocks[Nrocks].mag[0]   = PhotRel (&catalog[0].measure[m], &photcodes, ZERO_POINT);
+	  rocks[Nrocks].t[0]     = catalog[0].measure[m].t;
+
+	  rocks[Nrocks].X[1]     = X1[j];
+	  rocks[Nrocks].Y[1]     = Y1[j];
+	  rocks[Nrocks].ra[1]    = catalog[0].average[N1[j]].R;
+	  rocks[Nrocks].dec[1]   = catalog[0].average[N1[j]].D;
+
+	  m = catalog[0].average[N1[j]].offset;
+	  rocks[Nrocks].mag[1]   = PhotRel (&catalog[0].measure[m], &photcodes, ZERO_POINT);
+	  rocks[Nrocks].t[1]     = catalog[0].measure[m].t;
+
+	  rocks[Nrocks].X[2]     = X1[k];
+	  rocks[Nrocks].Y[2]     = Y1[k];
+	  rocks[Nrocks].ra[2]    = catalog[0].average[N1[k]].R;
+	  rocks[Nrocks].dec[2]   = catalog[0].average[N1[k]].D;
+
+	  m = catalog[0].average[N1[k]].offset;
+	  rocks[Nrocks].mag[2]   = PhotRel (&catalog[0].measure[m], &photcodes, ZERO_POINT);
+	  rocks[Nrocks].t[2]     = catalog[0].measure[m].t;
+
+	  fprintf (stderr, "*");
+	  Nrocks ++;
+	  if (Nrocks >= NROCKS) {
+	    NROCKS += 3000;
+	    REALLOCATE (rocks, Rocks, NROCKS);
+	  }
+	}
+      NEXTk:
+      }
+    NEXTj:
+    }
+    gettimeofday (&now, (void *) NULL);
+    dtime = (now.tv_sec - then.tv_sec) + 1e-6*(now.tv_usec - then.tv_usec);
+    if (dtime > 200) {
+      fprintf (stderr, "taking too long to find all rocks, giving up...\n");
+      goto escape_loop;
+    }
+  NEXTi:
+  }
+
+escape_loop:
+  for (i = 0; i < Nobject; i++) {
+    if ((catalog[0].average[N1[i]].code == 0) && (catalog[0].average[N1[i]].Nn >= 2)) {
+      catalog[0].average[N1[i]].code = ID_COSMIC;
+    }
+  }
+
+  fprintf (stderr, "found %d rocks\n", Nrocks);
+
+  free (T1);
+  free (X1);
+  free (Y1);
+  free (N1);
+
+  REALLOCATE (rocks, Rocks, MAX (1, Nrocks));
+  *nrocks = Nrocks;
+  return (rocks);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_slow_rocks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_slow_rocks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/find_slow_rocks.c	(revision 22322)
@@ -0,0 +1,135 @@
+# include "markrock.h"
+
+Rocks *find_slow_rocks (catalog, catstats, nrocks)
+Catalog catalog[];
+CatStats catstats[];
+int *nrocks;
+{
+
+  int i, j, k, m, N, Nm, NOBJECT, Nobject, Nrocks, NROCKS;
+  double RADIUS2;
+  double *R1, *D1, *X1, *Y1, *M1;
+  double R, D, X, Y, M;
+  double dX, dY, dR;
+  int *N1, *N0;
+  unsigned int *T1, T;
+  int Nstar, Nghost, Ng, Nmax;
+  unsigned int flags;
+  Coords *tcoords;
+  double ax, ay, cx, cy, dt, dX1, dX2, speed, distance;
+  Rocks *rocks;
+  unsigned short IsProper;
+
+  Nstar = catalog[0].Naverage;
+
+  NOBJECT = 1000;
+  ALLOCATE (N1, int,   NOBJECT);
+  Nobject = 0;
+  N0 = catstats[0].N;
+
+  NROCKS = 3000;
+  ALLOCATE (rocks, Rocks, NROCKS);
+  Nrocks = 0;
+
+  Nmax = 0;
+  IsProper = ID_USNO | ID_PROPER;
+  for (i = 0; i < Nstar; i++) {
+    N = N0[i];
+    /* accept a measurement if it is either unmarked or marked as a proper motion star and may have multiple measurements */
+    if ((catalog[0].average[N].code == 0) || (catalog[0].average[N].code & IsProper == IsProper)) {
+      Nmax = MAX (catalog[0].average[N].Nm, Nmax);
+      N1[Nobject] = N;
+      Nobject ++;
+      if (Nobject == NOBJECT - 1) {
+	NOBJECT += 1000;
+	REALLOCATE (N1, int,   NOBJECT);
+      }
+    }
+  }
+  REALLOCATE (N1, int,   Nobject);
+  fprintf (stderr, "Nobj: %d\n", Nobject);
+
+  ALLOCATE (R1, double, Nmax);
+  ALLOCATE (D1, double, Nmax);
+  ALLOCATE (X1, double, Nmax);
+  ALLOCATE (Y1, double, Nmax);
+  ALLOCATE (T1, unsigned int, Nmax);
+  ALLOCATE (M1, double, Nmax);
+  /** find multiple measured objects, find additional measurements along that line */
+
+  for (i = 0; i < Nobject; i++) {
+    fprintf (stderr, ",");
+    if (catalog[0].average[N1[i]].Nm > 1) {
+      N = N1[i];
+      Nm = catalog[0].average[N].Nm;
+      m = catalog[0].average[N].offset;
+      for (j = 0; j < 2; j++) {
+	R1[j] = catalog[0].average[N].R - catalog[0].measure[m+j].dR/360000.0;
+	D1[j] = catalog[0].average[N].D - catalog[0].measure[m+j].dD/360000.0;
+	RD_to_XY (&X1[j], &Y1[j], R1[j], D1[j], &catstats[0].coords);
+	T1[j] = catalog[0].measure[m+j].t;
+	M1[j] = catalog[0].measure[m+j].M - catalog[0].measure[m+j].Mcal;
+      }
+      dt = T1[1] - T1[0];
+      ax = (X1[1] - X1[0])/dt;
+      cx = (X1[0]*T1[1] - X1[1]*T1[0])/dt;
+      ay = (Y1[1] - Y1[0])/dt;
+      cy = (Y1[0]*T1[1] - Y1[1]*T1[0])/dt;
+      speed = hypot (ax, ay);
+      for (k = 0; k < Nobject; k++) {
+	N = N1[k];
+	Nm = catalog[0].average[N].Nm;
+	m = catalog[0].average[N].offset;
+	for (j = 0; j < Nm; j++) {
+	  R = catalog[0].average[N].R - catalog[0].measure[m+j].dR/360000.0;
+	  D = catalog[0].average[N].D - catalog[0].measure[m+j].dD/360000.0;
+	  RD_to_XY (&X, &Y, R, D, &catstats[0].coords);
+	  T = catalog[0].measure[m+j].t;
+	  M = catalog[0].measure[m+j].M - catalog[0].measure[m+j].Mcal;
+	  if (T1[0] == T) continue;
+	  if (T1[1] == T) continue;
+	  dX1 = T*ax - X + cx;
+	  dX2 = T*ay - Y + cy;
+	  distance = hypot (dX1, dX2);
+	  if (distance < RADIUS){
+	    catalog[0].average[N1[i]].code |= ID_ROCK;
+	    catalog[0].average[N1[k]].code |= ID_ROCK;
+	    rocks[Nrocks].ra    = R1[0];
+	    rocks[Nrocks].dec   = D1[0];
+	    rocks[Nrocks].mag   = M1[0];
+	    rocks[Nrocks].t     = T1[0];
+	    rocks[Nrocks+1].ra  = R1[1];
+	    rocks[Nrocks+1].dec = D1[1];
+	    rocks[Nrocks+1].mag = T1[1];
+	    rocks[Nrocks+1].t   = M1[1];
+	    rocks[Nrocks+2].ra  = R;
+	    rocks[Nrocks+2].dec = D;
+	    rocks[Nrocks+2].mag = M;
+	    rocks[Nrocks+2].t   = T;
+	    fprintf (stderr, "%f %f %f %d   %f\n", rocks[Nrocks].ra, rocks[Nrocks].dec, rocks[Nrocks].mag, rocks[Nrocks].t, speed);
+	    Nrocks += 3;
+	    if (Nrocks > NROCKS - 3) {
+	      NROCKS += 3000;
+	      REALLOCATE (rocks, Rocks, NROCKS);
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  fprintf (stderr, "found %d rocks\n", Nrocks);
+
+  free (R1);
+  free (D1);
+  free (M1);
+  free (T1);
+  free (X1);
+  free (Y1);
+  free (N1);
+
+  REALLOCATE (rocks, Rocks, Nrocks);
+  *nrocks = Nrocks;
+  return (rocks);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/gcatstats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/gcatstats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/gcatstats.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "markrock.h"
+
+gcatstats (catalog, catstats)
+Catalog catalog[];
+CatStats catstats[];
+{
+
+  int i;
+  double RaCenter, DecCenter;
+  double MinRA, MaxRA, MinDEC, MaxDEC;
+  double *X1, *Y1;
+  int *N1;
+  Coords tcoords;
+  
+  gfits_scan (&catalog[0].header, "RA0", "%lf", 1, &MinRA);
+  gfits_scan (&catalog[0].header, "RA1", "%lf", 1, &MaxRA);
+  gfits_scan (&catalog[0].header, "DEC0", "%lf", 1, &MinDEC);
+  gfits_scan (&catalog[0].header, "DEC1", "%lf", 1, &MaxDEC);
+
+ /* double check on region RA and DEC ranges */
+  DecCenter = 0.5*(MinDEC + MaxDEC);
+  RaCenter = 0.5*(MinRA + MaxRA);
+
+  catstats[0].RA[0] = MinRA;
+  catstats[0].RA[1] = MaxRA;
+  catstats[0].DEC[0] = MinDEC;
+  catstats[0].DEC[1] = MaxDEC;
+  /* number of stars per square arcsec */
+
+  /** allocate local arrays **/
+  ALLOCATE (catstats[0].X, double, catalog[0].Naverage);
+  ALLOCATE (catstats[0].Y, double, catalog[0].Naverage);
+  ALLOCATE (catstats[0].N, int,   catalog[0].Naverage);
+
+  /* project onto rectilinear grid with 1 arcsec pixels, sort by X */
+  /* reference for coords is center of field  */
+  catstats[0].coords.crval1 = RaCenter;
+  catstats[0].coords.crval2 = DecCenter;
+  catstats[0].coords.crpix1 = catstats[0].coords.crpix2 = 0.0;
+  catstats[0].coords.cdelt1 = catstats[0].coords.cdelt2 = 1.0 / 3600.0;
+  catstats[0].coords.pc1_1 = catstats[0].coords.pc2_2 = 1.0;
+  catstats[0].coords.pc1_2 = catstats[0].coords.pc2_1 = 0.0;
+  strcpy (catstats[0].coords.ctype, "RA---TAN");
+
+  X1 = catstats[0].X;
+  Y1 = catstats[0].Y;
+  for (i = 0; i < catalog[0].Naverage; i++, X1++, Y1++) {
+    fRD_to_XY (X1, Y1, catalog[0].average[i].R, catalog[0].average[i].D, &catstats[0].coords);
+    catstats[0].N[i] = i;
+  }
+  if (catalog[0].Naverage > 1) sort_coords_index (catstats[0].X, catstats[0].Y, catstats[0].N, catalog[0].Naverage);
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/markrock.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/markrock.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/markrock.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include "markrock.h"
+
+main (int argc, char **argv) {
+  
+  int i, Nrocks1, Nrocks2;
+  Catalog catalog;
+  CatStats catstats;
+  Rocks *rocks1, *rocks2, *find_rocks(), *find_slow_rocks();
+  struct timeval now, then;  
+  
+  gettimeofday (&then, (void *) NULL);
+  ConfigInit (&argc, argv);
+  
+  VERBOSE = FALSE;
+  if ((i = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (i, &argc, argv);
+  }
+  FORCE_RUN = FALSE;
+  if ((i = get_argument (argc, argv, "-f"))) {
+    FORCE_RUN = TRUE;
+    remove_argument (i, &argc, argv);
+  }
+  RESET = FALSE;
+  if ((i = get_argument (argc, argv, "-reset"))) {
+    RESET = TRUE;
+    remove_argument (i, &argc, argv);
+  }
+  if (argc < 2) {
+    fprintf (stderr, "ERROR: Usage: markstar (catalog)\n");
+    exit (0);
+  }
+
+  /* if lockfile exists, program will complain and quit */
+  if (!check_file_access (argv[1], TRUE, TRUE)) exit (1);
+  if (!check_file_access (RockCat, TRUE, TRUE)) exit (1);
+
+  catalog.filename = argv[1];
+  catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+  catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "a")) {
+    fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+    exit (2);
+  }
+  if (catalog.Naves_disk) {
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+    continue;
+  }
+
+  gcatstats (&catalog, &catstats);
+
+  find_bright_stars (&catalog, &catstats); 
+
+  rocks1 = find_rocks (&catalog, &catstats, &Nrocks1); 
+  count_neighbors (rocks1, Nrocks1, &catalog, &catstats);
+
+  /* rocks2 = find_slow_rocks (&catalog, &catstats, &Nrocks2);  */
+  dvo_catalog_save (&catalog, VERBOSE);
+  dvo_catalog_unlock (&catalog);
+  dvo_catalog_free (&catalog);
+
+  wrocks (rocks1, Nrocks1);
+  /* wrocks (rocks2, Nrocks2); */
+
+  if (VERBOSE) {
+    gettimeofday (&now, (void *) NULL);
+    fprintf (stderr, "%s: elapsed time = %.2f sec\n", argv[1], 
+	     (now.tv_sec - then.tv_sec) + 1e-6*(now.tv_usec - then.tv_usec));
+  }
+
+  fprintf (stderr, "SUCCESS\n");
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/sorts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/sorts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/sorts.c	(revision 22322)
@@ -0,0 +1,16 @@
+# include "markrock.h"
+
+void sort_set (double *X, double *Y, int *T, int *S, int N) {
+
+# define SWAPFUNC(A,B){ double dtmp; int itmp; \
+  dtmp = X[A]; X[A] = X[B]; X[B] = dtmp; \
+  dtmp = Y[A]; Y[A] = Y[B]; Y[B] = dtmp; \
+  itmp = T[A]; T[A] = T[B]; T[B] = itmp; \
+  itmp = S[A]; S[A] = S[B]; S[B] = itmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/wrocks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/wrocks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markrock/src/wrocks.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "markrock.h"
+
+wrocks (Rocks *rocks, int Nrocks) {
+  
+  int i, j;
+  FILE *f;
+  double X, Y, t, dSx, dSy, dS, speed;
+  double Sx, Sy, Sxt, Syt, St, St2, Sn;
+  double Mx, Bx, My, By, D;
+  unsigned int Tref;
+
+  f = fopen (RockCat, "a+");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't create/open rock catalog file: %s\n", RockCat);
+    exit (0);
+  }
+  /* position to begining of file to write header */
+  fseek (f, 0, SEEK_END);
+
+  /* get statistics on rocks */
+
+  for (i = 0; i< Nrocks; i++) {
+    /* fit a line to the three points, the ask for the scatter about the solution */
+    Tref = rocks[i].t[0];
+    Sx = Sy = Sxt = Syt = St = St2 = Sn = 0;
+    for (j = 0; j < 3; j++) {
+      X = rocks[i].X[j];
+      Y = rocks[i].Y[j];
+      if (rocks[i].t[j] > Tref) 
+	t = rocks[i].t[j] - Tref;
+      else
+	t = -1*((double)(Tref - rocks[i].t[j]));
+      Sx  += X;
+      Sy  += Y;
+      Sxt += X*t;
+      Syt += Y*t;
+      St  += t;
+      St2 += t*t;
+      Sn  += 1;
+    }
+    D = St2*Sn - St*St;
+    My = (Syt*Sn - Sy*St) / D;
+    By = (Sy*St2 - Syt*St) / D;
+    Mx = (Sxt*Sn - Sx*St) / D;
+    Bx = (Sx*St2 - Sxt*St) / D;
+    
+    dS = 0;
+    for (j = 0; j < 3; j++) {
+      X = rocks[i].X[j];
+      Y = rocks[i].Y[j];
+      if (rocks[i].t[j] > Tref) 
+	t = rocks[i].t[j] - Tref;
+      else 
+	t = -1*((double)(Tref - rocks[i].t[j]));
+      dSx = (Mx*t + Bx - X);
+      dSy = (My*t + By - Y);
+      dS += dSx*dSx + dSy*dSy;
+    }
+    dS = sqrt (dS/3);
+    speed = hypot (Mx, My);
+    fprintf (f, "%5.2f %9.4e ", dS, speed);
+    for (j = 0; j < 3; j++) {
+      fprintf (f, "%10d %10.6f %10.6f %6.3f ", rocks[i].t[j], rocks[i].ra[j], rocks[i].dec[j], rocks[i].mag[j]);
+    }
+    /* fprintf (f, "%3d %3d %3d\n", rocks[i].N[0], rocks[i].N[1], rocks[i].N[2]); */
+    fprintf (f, "\n");
+  }
+  fclose (f);
+  
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/Makefile	(revision 22322)
@@ -0,0 +1,37 @@
+default: markstar
+help:
+	@echo "make options: markstar (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/markstar
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+markstar: $(BIN)/markstar.$(ARCH)
+install: $(DESTBIN)/markstar
+
+MARKSTAR = \
+$(SRC)/markstar.$(ARCH).o 	$(SRC)/gcatalog.$(ARCH).o   \
+$(SRC)/coordops.$(ARCH).o	$(SRC)/sorts.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o 	\
+$(SRC)/check_lockfile.$(ARCH).o \
+$(SRC)/find_trails.$(ARCH).o	$(SRC)/find_group.$(ARCH).o \
+$(SRC)/find_line.$(ARCH).o	$(SRC)/mark_trail.$(ARCH).o \
+$(SRC)/load_gsc_data.$(ARCH).o	$(SRC)/find_bright_stars.$(ARCH).o \
+$(SRC)/wcatalog.$(ARCH).o	$(SRC)/find_images.$(ARCH).o	\
+$(SRC)/load_gsc_data_ghost.$(ARCH).o	$(SRC)/find_ghosts.$(ARCH).o \
+$(SRC)/gcatstats.$(ARCH).o	$(SRC)/match_images.$(ARCH).o	\
+$(SRC)/gregions.$(ARCH).o	$(SRC)/aregion.$(ARCH).o	\
+$(SRC)/find_matches.$(ARCH).o	$(SRC)/check_permissions.$(ARCH).o
+
+$(MARKSTAR): $(INC)/markstar.h
+$(BIN)/markstar.$(ARCH): $(MARKSTAR)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/doc/example.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/doc/example.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/doc/example.txt	(revision 22322)
@@ -0,0 +1,13 @@
+
+rats: foreach f ( */*.cpt )
+foreach? echo $f
+foreach? echo $f >> bright.log
+foreach? (markstar -v $f >> bright.dat) >>& bright.log
+foreach? end
+
+
+
+ echo $f
+ echo $f >> bright.log
+ (markstar -v $f >> bright.dat) >>& bright.log
+ end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/doc/process.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/doc/process.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/doc/process.txt	(revision 22322)
@@ -0,0 +1,69 @@
+
+hi chad,
+
+the process i use is a bit complex, but makes some reasonable
+comprimises about distance between points in a line and processing
+time.  
+
+First, the process I am about to describe is run on every "star" in
+the image.  However, the whole process gets speeded up in part because
+entries which are known stars can be ignored and entries which are
+already identified as part of a line associated with an earlier entry
+get ignored as well.  In fact, this step is done for a new part of the
+sky before the USNO stars are identified without much reduction in the
+processing speed, so the fact that you don't identify fixed stars in
+your data should not really be an issue.
+
+In the following, I use the term "star" to mean any detected object
+in the image frame.
+
+So, let me now discuss the process for a given test star, call it
+S(0).  
+
+The first step is to find likely directions with trails.  I first look
+at all other stars within a specific radius (say 100") around that
+star, call them S(i).  For each star S(i), I find the angle between
+S(i) and S(0).  I accumulate a histogram of these angles, a bin size
+of say a degree. (Note, both the Radius and the BinSize should be
+parameters that might change depending on the density of stars).  If
+any of the angle bins have a significant number of entries, then this
+is the likely direction of a trail.  There are three things to note
+about this process.  First, it makes processing the lists much quicker
+if you have the stars sorted by X or Y (or RA or DEC) and use that
+information in your search for the stars within 100".  Second, I
+define a significance cutoff for an angle bin based on the density of
+stars in the field (this is a useful parameter for all of the
+comparisons I discuss).  Once any of the angle bins surpasses the
+significance criterion, I stop searching around this test star and
+just work on the identified angle.  I assume that if the test star
+just happens to lie at the intersection of two lines, the other line
+will be identified by another test star.  Finally, the angle histogram
+only needs to run from 0 to 180, and angles in the range 180 to 360
+should be reflected back into the first half to increase the number
+statistics.  In general, your test star will be somewhere in the
+middle of a line.
+
+Once an angle of interest is found, I fit a line to all of the points
+in that angle bin, then narrow down improve the fit by refitting to
+all points within a fixed distance of the line.  You can itereate on
+the second step if the line is not well determined the first time
+around.  One point of interest:  for the line fits, if the angle of
+the line is 0 - 45 deg, I fit the line Y = mX + b, but if the angle is
+45 - 90 deg, I fit the line X = mY + b, to keep the slope near 1.  
+
+Once I have a good fit to a line, I try to decide if the line is
+really a trail or if it is just a coincidence of points.  I demand 
+both a minimum linear density of points in the line (related to the
+density of stars in the frame) and a minimum number of points in the
+line.  Another thing to watch out for is including points well beyond
+the end of the line which just happen to lie along the line.  I demand
+that the distance between points be less than some distance related to
+the linear density.  
+
+I hope this is a helpful description.  The routine I run is quite
+quick.  It takes about 10 seconds for a 1 degree region (about 10,000
+real stars) to do the line search, the bright star wings, and the
+search for ghosts on a 300 MHz Pentium II.  
+
+good luck
+gene
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/include/markstar.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/include/markstar.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/include/markstar.h	(revision 22322)
@@ -0,0 +1,46 @@
+# include <ohana.h>
+# include <dvo.h>
+
+int    VERBOSE;
+int    RESET;
+int    FORCE_RUN;
+
+/* global variables set in parameter file */
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+
+char   GSCDIR[256];
+char   ImageCat[256];
+char   GSCFILE[256];
+
+double RADIUS;
+double TRAIL_WIDTH;
+int    NBINS;
+int    NPTSINLINE;
+double MIN_DENSITY;
+double NSIGMA;
+
+double BRIGHT_HALO_MAG;
+double BRIGHT_HALO_SLOPE;
+double BRIGHT_XTRAIL_WIDTH;
+double BRIGHT_XTRAIL_MAG;
+double BRIGHT_XTRAIL_SLOPE;
+double BRIGHT_YTRAIL_WIDTH;
+double BRIGHT_YTRAIL_MAG;
+double BRIGHT_YTRAIL_SLOPE;
+
+double GHOST_MAG;
+double GHOST_RADIUS;
+double OPTICAL_AXIS1;
+double OPTICAL_AXIS2;
+
+typedef struct {
+  Coords coords;
+  double *X, *Y;
+  int *N;
+  double RA[2], DEC[2];
+  double Area, density, spacing;
+} CatStats;
+
+PhotCodeData photcodes;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "markstar.h"
+
+ConfigInit (int *argc, char **argv) {
+
+  char *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  /* used in other pipeline functions */
+  ScanConfig (config, "CATDIR",          "%s",  0, CATDIR);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig (config, "PHOTCODE_FILE",         	"%s",  0, MasterPhotcodeFile);
+
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  ScanConfig (config, "GSCDIR",          "%s",  0, GSCDIR);
+  ScanConfig (config, "GSCFILE",         "%s",  0, GSCFILE);
+  /* unique to markstar */
+  ScanConfig (config, "SEARCH_RADIUS",   "%lf", 0, &RADIUS);
+  ScanConfig (config, "TRAIL_WIDTH",     "%lf", 0, &TRAIL_WIDTH);
+  ScanConfig (config, "NANGLE_BINS",     "%d",  0, &NBINS);
+  ScanConfig (config, "NPTSINLINE",      "%d",  0, &NPTSINLINE);
+  ScanConfig (config, "MIN_DENSITY",     "%lf", 0, &MIN_DENSITY);
+  ScanConfig (config, "SPACE_SIGMA",     "%lf", 0, &NSIGMA); 
+
+  ScanConfig (config, "BRIGHT_HALO_MAG",     "%lf", 0, &BRIGHT_HALO_MAG);
+  ScanConfig (config, "BRIGHT_HALO_SLOPE",   "%lf", 0, &BRIGHT_HALO_SLOPE);
+  ScanConfig (config, "BRIGHT_XTRAIL_WIDTH", "%lf", 0, &BRIGHT_XTRAIL_WIDTH);
+  ScanConfig (config, "BRIGHT_XTRAIL_MAG",   "%lf", 0, &BRIGHT_XTRAIL_MAG);
+  ScanConfig (config, "BRIGHT_XTRAIL_SLOPE", "%lf", 0, &BRIGHT_XTRAIL_SLOPE);
+  ScanConfig (config, "BRIGHT_YTRAIL_WIDTH", "%lf", 0, &BRIGHT_YTRAIL_WIDTH);
+  ScanConfig (config, "BRIGHT_YTRAIL_MAG",   "%lf", 0, &BRIGHT_YTRAIL_MAG);
+  ScanConfig (config, "BRIGHT_YTRAIL_SLOPE", "%lf", 0, &BRIGHT_YTRAIL_SLOPE);
+
+  ScanConfig (config, "GHOST_MAG",       "%lf", 0, &GHOST_MAG);
+  ScanConfig (config, "GHOST_RADIUS",    "%lf", 0, &GHOST_RADIUS);
+  ScanConfig (config, "OPTICAL_AXIS1",   "%lf", 0, &OPTICAL_AXIS1);
+  ScanConfig (config, "OPTICAL_AXIS2",   "%lf", 0, &OPTICAL_AXIS2);
+ 
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_bright_stars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_bright_stars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_bright_stars.c	(revision 22322)
@@ -0,0 +1,140 @@
+# include "markstar.h"
+
+void find_bright_stars (Catalog *catalog, CatStats *catstats) {
+
+  int i, j, n, m, first_j, Nave, Ngsc;
+  Catalog GSCdata;
+  double MinRA, MinDEC, MaxRA, MaxDEC, RaCenter, DecCenter;
+  Coords *tcoords;
+  double *X1, *Y1, *X2, *Y2;
+  int *N1, *N2;
+  char *mark;
+  double dX, dY, dR, MaxDist, MaxDist1, MaxRadius, radius, radius2;
+
+  // put limits on the ref catalog mags
+  load_gsc_data (&GSCdata, catstats);
+
+  Nave = catalog[0].Naverage;
+  Ngsc = GSCdata.Naverage;
+    
+  /* in the function below, it is better to have the catalog with
+     more stars associated with index 2 (j) */ 
+  X2 = catstats[0].X;
+  Y2 = catstats[0].Y;
+  N2 = catstats[0].N;
+  ALLOCATE (X1, double, Ngsc);
+  ALLOCATE (Y1, double, Ngsc);
+  ALLOCATE (N1, int, Ngsc);
+  ALLOCATE (mark, char, Nave);
+  bzero (mark, Nave);
+
+  tcoords = &catstats[0].coords;
+  for (i = 0; i < Ngsc; i++) {
+    RD_to_XY (&X1[i], &Y1[i], GSCdata.average[i].R, GSCdata.average[i].D, tcoords);
+    N1[i] = i;
+  }
+  if (Ngsc > 1) sort_coords_index (X1, Y1, N1, Ngsc);
+  
+  /* first find stellar halos */
+  /* max radius (mag = -1) */
+  /** the j index moves quickly and is better associated with the catalog with more stars */
+  MaxRadius = BRIGHT_HALO_SLOPE * (-1.0 - BRIGHT_HALO_MAG); 
+  /** find catalog stars near GSC stars **/
+  for (i = j = 0; (i < Ngsc) && (j < Nave); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -2*MaxRadius) {
+      i++;
+      continue;
+    }
+    if (dX >= 2*MaxRadius) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    radius = MAX (BRIGHT_HALO_SLOPE * (0.001*GSCdata.average[N1[i]].M - BRIGHT_HALO_MAG), 0.0);
+    if (radius == 0) {
+      i++; 
+      continue;
+    }
+    radius2 = radius*radius;
+    for (; (dX > -2*radius) && (j < Nave); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      dR = dX*dX + dY*dY;
+      if (dR < radius2) {  /* new measurement of this star */
+	mark[j] = TRUE;
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  
+  /* next find y spikes */
+  /** find catalog stars near GSC stars **/
+  for (i = j = 0; (i < Ngsc) && (j < Nave); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -BRIGHT_YTRAIL_WIDTH) {
+      i++;
+      continue;
+    }
+    if (dX >= BRIGHT_YTRAIL_WIDTH) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    MaxDist = MAX (BRIGHT_YTRAIL_SLOPE * (0.001*GSCdata.average[N1[i]].M - BRIGHT_YTRAIL_MAG), 0.0);
+    for (; (dX > -BRIGHT_YTRAIL_WIDTH) && (j < Nave); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      if ((fabs(dX) < BRIGHT_YTRAIL_WIDTH) && (fabs(dY) < MaxDist)) {  /* star on spike */
+	mark[j] = TRUE;
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  
+  /* next find x spikes */
+  /** find catalog stars near GSC stars **/
+  MaxDist = MAX (BRIGHT_XTRAIL_SLOPE * (-1.0 - BRIGHT_XTRAIL_MAG), 0.0);
+  for (i = j = 0; (i < Ngsc) && (j < Nave); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -MaxDist) {
+      i++;
+      continue;
+    }
+    if (dX >= MaxDist) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    MaxDist1 = MAX (BRIGHT_XTRAIL_SLOPE * (0.001*GSCdata.average[N1[i]].M - BRIGHT_XTRAIL_MAG), 0.0);
+    for (; (dX > -MaxDist1) && (j < Nave); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      if ((fabs(dY) < BRIGHT_XTRAIL_WIDTH) && (fabs(dX) < MaxDist1)) {  /* star on spike */
+	mark[j] = TRUE;
+      }
+    }
+    j = first_j;
+    i++;
+  }
+
+  /* done with search, mark selected stars */
+  for (i = 0; i < Nave; i++) {
+    if (mark[i]) {
+      catalog[0].average[N2[i]].code = ID_BLEED;
+    }
+  } 
+  
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (mark);
+  free (GSCdata.average);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_ghosts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_ghosts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_ghosts.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "markstar.h"
+
+find_ghosts (catalog, catstats, filename, image, Nimage)
+Catalog catalog[];
+CatStats catstats[];
+char *filename;
+Image image[];
+int Nimage;
+{
+
+  int i, j, first_j, Nave, Ngsc, Nregions;
+  Catalog GSCdata, Ghostdata;
+  double MinRA, MinDEC, MaxRA, MaxDEC, RaCenter, DecCenter;
+  Coords *tcoords;
+  float *X1, *Y1, *X2, *Y2;
+  int *N1, *N2, *match;
+  char *mark;
+  double dX, dY, dR, RADIUS2, BRIGHT_RADIUS, radius, X, Y, R, D;
+  Image timage;
+  GSCRegion *region, *gregions2();
+
+  for (i = 0; i < Nimage; i++) {
+    
+    /* coords structure for ghost image */
+    timage = image[i];
+    timage.coords.cdelt1 = -1*image[i].coords.cdelt1;
+    timage.coords.cdelt2 = -1*image[i].coords.cdelt2;
+    timage.coords.crpix1 = 2*OPTICAL_AXIS1 - image[i].coords.crpix1;
+    timage.coords.crpix2 = 2*OPTICAL_AXIS2 - image[i].coords.crpix2;
+
+    region = gregions2 (&timage, &Nregions);
+    
+    /* find ghost stars */
+    // put limits on the ref catalog mags
+    load_gsc_data_ghost (&GSCdata, region, Nregions, &timage);
+
+    /* refect ghost stars to find locations of ghosts */
+    ALLOCATE (Ghostdata.average, Average, MAX (GSCdata.Naverage, 1));
+    for (j = 0; j < GSCdata.Naverage; j++) {
+      RD_to_XY (&X, &Y, GSCdata.average[j].R, GSCdata.average[j].D, &timage);
+      fXY_to_RD (&Ghostdata.average[j].R, &Ghostdata.average[j].D, X, Y, &image[i]);
+      Ghostdata.average[j].M = GSCdata.average[j].M;
+    }    
+    Ghostdata.Naverage = GSCdata.Naverage;
+
+    /* match ghosts and image stars, mark ghosts */
+    find_matches (catalog, catstats, &Ghostdata, i);
+
+    free (Ghostdata.average);
+    free (GSCdata.average);
+  }
+
+}
+
+/* 
+
+   just to be clear on some of the terms:
+
+   the "ghost" is the fuzzy patch of light on an image caused by
+     a reflected star
+
+   the "ghost star" is the star in the sky which causes a ghost
+
+   the "ghost image" is the image location on the sky 
+     where ghost stars may come from.
+
+   the "image stars" are observed stars at the location of the
+     ghost - these are detections of the ghost
+
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_group.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_group.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_group.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "markstar.h"
+
+find_group (catstats, mark, Npts, i, ANGLE)
+     CatStats catstats[];
+     double *ANGLE;
+     int i, Npts;
+     char *mark;
+{
+ 
+  float *R, *D;
+  int j, N, bin, Ndegbin;
+  unsigned short int *A;
+  double Ra, De, dR, dD, angle, limit;
+
+   /* assign some parameter values */
+  
+  R = catstats[0].X;
+  D = catstats[0].Y;
+  Ndegbin = NBINS / 180.0;
+  ALLOCATE (A, unsigned short int, NBINS + 1)
+  bzero (A, (NBINS+1)*sizeof(short));
+
+  /* look for points concentrated in an angle bin */
+  if (mark[i]) return (FALSE);
+  Ra = R[i];
+  De = D[i];
+  N = 0;
+  /* points east */
+  for (j = i + 1; (j < Npts) && (R[j] - Ra < RADIUS); j++) {
+    if (mark[j]) continue;
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      angle = atan2 (dD,dR);
+      if (!finite(angle)) continue;  /* only NaN if dD = dR = 0 */
+      if (angle < 0) angle += M_PI;
+      bin = angle*DEG_RAD*Ndegbin;
+      A[bin] ++;
+      N ++;
+    }
+  }
+  /* points west */
+  for (j = i - 1; (j >= 0) && (Ra - R[j] < RADIUS); j--) {
+    if (mark[j]) continue;
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      angle = atan2 (dD,dR);
+      if (!finite(angle)) continue;
+      if (angle < 0) angle += M_PI;
+      bin = angle*DEG_RAD*Ndegbin;
+      A[bin] ++;	
+      N ++;
+    }
+  }
+  limit = NSIGMA*sqrt((double)(N)/(double)(NBINS));
+  for (j = 0; j < NBINS; j++) {
+    if (A[j] > limit) {
+      *ANGLE = j / (DEG_RAD*Ndegbin);
+      return (TRUE);
+    }
+  }
+  return (FALSE);
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_images.c	(revision 22322)
@@ -0,0 +1,177 @@
+# include "markstar.h"
+double opening_angle ();
+
+Image *find_images (FITS_DB *db, CatStats *catstats, int *Nimages) {
+  
+  Header header;
+  Image *timage, *image;
+  int i, j, k, found, nimage, Nimage, NIMAGE, NTIMAGE, Nloop, Nlast;
+  int n, Nim, status, InRange;
+  FILE *f;
+  double Xc[6], Yc[6], Xi[6], Yi[6], r, d, x, y, dx, dy;
+  Coords *tcoords;
+
+  /* we make positional comparisons in the projection of catalog */
+  tcoords = &catstats[0].coords;
+  /* define catalog corners */
+  Xc[0] = catstats[0].RA[0]; Yc[0] = catstats[0].DEC[0];
+  Xc[1] = catstats[0].RA[1]; Yc[1] = catstats[0].DEC[0];
+  Xc[2] = catstats[0].RA[1]; Yc[2] = catstats[0].DEC[1];
+  Xc[3] = catstats[0].RA[0]; Yc[3] = catstats[0].DEC[1];
+  Xc[4] = catstats[0].RA[0]; Yc[4] = catstats[0].DEC[0];
+  for (j = 0; j < 5; j++) {
+    r = Xc[j]; d = Yc[j];
+    RD_to_XY (&Xc[j], &Yc[j], r, d, tcoords);
+  }
+
+  timage = gfits_table_get_Image (&db[0].ftable, &Ntimage, &db[0].swapped);
+
+  /* set up buffers for images, temporary storage */
+  nimage = 0;
+  NIMAGE = 100;
+  ALLOCATE (image, Image, NIMAGE);
+
+  /* test each image in block */
+  for (i = 0; i < Ntimage; i++) {
+    /* define image corners */
+    Xi[0] = 0;            Yi[0] = 0;
+    Xi[1] = timage[i].NX; Yi[1] = 0;
+    Xi[2] = timage[i].NX; Yi[2] = timage[i].NY;
+    Xi[3] = 0;            Yi[3] = timage[i].NY;
+    Xi[4] = 0;            Yi[4] = 0;
+    found = FALSE;
+    /* transform to tcoords */
+    if (catstats[0].DEC[1] > 86.25) { /* pole */
+      for (j = 0; j < 5; j++) {
+	XY_to_RD (&r, &d, Xi[j], Yi[j], &timage[i].coords);
+	if (d > catstats[0].DEC[0] - 0.5) found = TRUE;
+      }
+    } else {
+      for (j = 0; j < 6; j++) {
+	XY_to_RD (&r, &d, Xi[j], Yi[j], &timage[i].coords);
+	InRange = RD_to_XY (&Xi[j], &Yi[j], r, d, tcoords);
+	if (!InRange) {
+	  /* if RD_to_XY returns false, the coords are ~180 away from
+	     the projection center */ 
+	  goto imskip;
+	}
+      }
+      /* check if image corner inside catalog */
+      for (j = 0; (j < 4) && !found; j++) {
+	found |= corner_check (&Xi[j], &Yi[j], &Xc[0], &Yc[0]);
+      }
+      /* check if catalog corner inside image */
+      for (j = 0; (j < 4) && !found; j++) {
+	found |= corner_check (&Xc[j], &Yc[j], &Xi[0], &Yi[0]);
+      }
+      /* check if edges cross */
+      for (j = 0; (j < 4) && !found; j++) {
+	for (k = 0; (k < 4) && !found; k++) {
+	  found |= edge_check (&Xi[j], &Yi[j], &Xc[k], &Yc[k]);
+	}
+      }
+    }
+    if (found) {
+      image[nimage] = timage[i]; 
+      image[nimage].code = 0;
+      nimage ++;
+      if (nimage == NIMAGE) {
+	NIMAGE += 100;
+	REALLOCATE (image, Image, NIMAGE);
+      }
+    }
+  imskip:
+  }
+      
+  if (VERBOSE) { 
+    for (i = 0; i < nimage; i++) {
+      XY_to_RD (&r, &d, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+      fprintf (stderr, "associated images: %d %8.4f %8.4f %10d %6d  %5.3f %6.3f %6.3f\n", 
+	       i, r, d, image[i].tzero, image[i].nstar, 0.001*image[i].secz, 
+	       0.001*image[i].Mcal, 0.001*image[i].dMcal);
+    }
+  }
+
+  REALLOCATE (image, Image, MAX (nimage, 1));
+  *Nimages = nimage;
+  return (image);
+}
+
+int edge_check (x1, y1, x2, y2)
+double *x1, *y1, *x2, *y2;
+{
+
+  double theta1, theta2;
+  double Theta1, Theta2;
+
+  theta1 = opening_angle (x1[0], y1[0], x2[0], y2[0], x1[1], y1[1]); 
+  theta2 = opening_angle (x1[0], y1[0], x2[0], y2[0], x2[1], y2[1]); 
+
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  Theta1 = theta1;
+  Theta2 = theta2;
+  theta1 = opening_angle (x2[0], y2[0], x1[1], y1[1], x2[1], y2[1]); 
+  theta2 = opening_angle (x2[0], y2[0], x1[1], y1[1], x1[0], y1[0]); 
+  
+ 
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+
+/* returns the opening angle between the three points (2 is in middle) 
+   in range -pi to pi */
+double opening_angle (x1, y1, x2, y2, x3, y3)
+double x1, y1, x2, y2, x3, y3;
+{
+
+  double dx1, dy1, dx2, dy2, ct, st, theta;
+
+  dx1 = x1 - x2;
+  dy1 = y1 - y2;
+  
+  dx2 = x3 - x2;
+  dy2 = y3 - y2;
+  
+  ct = (dx1*dx2 + dy1*dy2);
+  st = (dx1*dy2 - dx2*dy1);
+
+  theta = atan2 (st, ct);
+
+  return (theta);
+
+}
+
+/* check if point x1,y1 is in box formed by x2[0-4] */
+int corner_check (x1, y1, x2, y2)
+double *x1, *y1, *x2, *y2;
+{
+
+  int i;
+  double theta;
+
+  theta = 0;
+
+  for (i = 0; i < 4; i++) {
+    theta += opening_angle (x2[i], y2[i], x1[0], y1[0], x2[i+1], y2[i+1]); 
+  }
+  if (fabs(theta) > 6) {
+    return (TRUE);
+  } else {
+    return (FALSE);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_line.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_line.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_line.c	(revision 22322)
@@ -0,0 +1,161 @@
+# include "markstar.h"
+
+find_line (catstats, mark, Npts, i, M, B, Angle)
+     CatStats catstats[];
+     double *M, *B, Angle;
+     int i, Npts;
+     char *mark;
+{
+  
+  float *R, *D;
+  int j, N;
+  double X, Y, X2, Y2, XY, m, b, det;
+  double dR, dD, Ra, De, angle;
+  char Flipped;
+  
+  R = catstats[0].X;
+  D = catstats[0].Y;
+
+  /* fit a line to points near line */
+  Ra = R[i];
+  De = D[i];
+  X = Ra;
+  Y = De;
+  X2 = Ra*Ra;
+  Y2 = De*De;
+  XY = Ra*De;
+  N = 1;
+  /* points to the east */
+  for (j = i + 1; (j < Npts) && (R[j] - Ra < RADIUS); j++) {
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      angle = atan2 (dD,dR);
+      if (!finite(angle)) continue;
+      if (angle < 0) angle += M_PI;
+      if (fabs(angle - Angle) < RAD_DEG) {
+	X += R[j];
+	Y += D[j];
+	X2 += R[j]*R[j];
+	Y2 += D[j]*D[j];
+	N ++;
+	XY += R[j]*D[j];
+      }
+    }
+  }
+  /* points to the west */
+  for (j = i - 1; !mark[i] && (j >= 0) && (Ra - R[j] < RADIUS); j--) {
+    if (mark[j]) continue;
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      angle = atan2 (dD,dR);
+      if (!finite(angle)) continue;
+      if (angle < 0) angle += M_PI;
+      if (fabs(angle - Angle) < RAD_DEG) {
+	X += R[j];
+	Y += D[j];
+	X2 += R[j]*R[j];
+	Y2 += D[j]*D[j];
+	N ++;
+	XY += R[j]*D[j];
+      }
+    }
+  }
+  /* determine coeffs */
+  Flipped = 0;
+  det = 1.0 / (X2*N - X*X);
+  m = det * (XY*N - X*Y);
+  b = det * (X2*Y - XY*X);
+  if (fabs(m) > 1.1) { /* use a line of R = m*D + b instead */
+    /* fprintf (stderr, "high slope object: %f %f  -> ", m, b); */
+    det = 1.0 / (Y2*N - Y*Y);
+    m = det * (XY*N - X*Y);
+    b = det * (Y2*X - XY*Y);
+    Flipped = 1;
+    /* fprintf (stderr, "%f %f\n", m, b); */
+  }
+
+  *M = m;
+  *B = b;
+  return (Flipped);
+
+}
+
+
+find_better_line (catstats, mark, Npts, i, M, B, axis)
+     CatStats catstats[];
+     double *M, *B;
+     int i, Npts, axis;
+     char *mark;
+{
+  
+  float *R, *D;
+  int j, N;
+  double X, Y, X2, Y2, XY, m, b, det;
+  double dR, dD, Ra, De, delta;
+  
+  R = catstats[0].X;
+  D = catstats[0].Y;
+
+  /* fit a line to points near line */
+  Ra = R[i];
+  De = D[i];
+  X = Y = X2 = Y2 = XY = N = 0;
+  m = *M;  b = *B;
+
+  /* points to the east */
+  for (j = i; (j < Npts) && (R[j] - Ra < RADIUS); j++) {
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      if (axis == 1) 
+	delta = R[j] - m*D[j] - b;
+      else
+	delta = D[j] - m*R[j] - b;
+      if (fabs(delta) < 2*TRAIL_WIDTH) {
+	X += R[j];
+	Y += D[j];
+	X2 += R[j]*R[j];
+	Y2 += D[j]*D[j];
+	N ++;
+	XY += R[j]*D[j];
+      }
+    }
+  }
+  /* points to the west */
+  for (j = i - 1; (j >= 0) && (Ra - R[j] < RADIUS); j--) {
+    dD = D[j] - De;
+    if (fabs(dD) < RADIUS) {
+      dR = R[j] - Ra;
+      if (axis == 1) 
+	delta = R[j] - m*D[j] - b;
+      else
+	delta = D[j] - m*R[j] - b;
+      if (fabs(delta) < 2*TRAIL_WIDTH) {
+	X += R[j];
+	Y += D[j];
+	X2 += R[j]*R[j];
+	Y2 += D[j]*D[j];
+	N ++;
+	XY += R[j]*D[j];
+      }
+    }
+  }
+  /* determine coeffs */
+  if (axis == 0) {
+    det = 1.0 / (X2*N - X*X);
+    m = det * (XY*N - X*Y);
+    b = det * (X2*Y - XY*X);
+  } else {
+    det = 1.0 / (Y2*N - Y*Y);
+    m = det * (XY*N - X*Y);
+    b = det * (Y2*X - XY*Y);
+  }
+
+  /* fprintf (stderr, "%f %f %d\n", m, b, N); */
+
+  *M = m;
+  *B = b;
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_matches.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_matches.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_matches.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "markstar.h"
+
+int find_matches (Catalog *catalog, CatStats *catstats, Catalog *Ghostdata, int Nimage) {
+
+  int i, j, k, n, m, N, M, first_j;
+  double X, Y, RADIUS, RADIUS2;
+  double *X1, *Y1, *X2, *Y2;
+  double dX, dY, dR;
+  int *N1, *N2;
+  int Nstar, Nghost, Ng;
+  unsigned int flags;
+  Coords *tcoords;
+
+  Nstar = catalog[0].Naverage;
+  Nghost = Ghostdata[0].Naverage;
+
+  if (Nghost < 1) return (0);
+
+  /* it is better to have the catalog with fewer stars
+     assigned to the X1 set */
+  X2 = catstats[0].X;
+  Y2 = catstats[0].Y;
+  N2 = catstats[0].N;
+
+  ALLOCATE (X1, double, Nghost);
+  ALLOCATE (Y1, double, Nghost);
+  ALLOCATE (N1, int, Nghost);
+
+  /* project ghosts to the frame of the catalog */
+  tcoords = &catstats[0].coords;
+  for (i = 0; i < Nghost; i++) {
+    RD_to_XY (&X1[i], &Y1[i], Ghostdata[0].average[i].R, Ghostdata[0].average[i].D, tcoords);
+    N1[i] = i;
+  }
+  if (Nghost > 1) sort_coords_index (X1, Y1, N1, Nghost);
+
+  /* choose a radius for matches */
+  RADIUS2 = GHOST_RADIUS*GHOST_RADIUS;
+
+  /** find matched stars **/
+  for (i = j = 0; (i < Nghost) && (j < Nstar); ) {
+    dX = X1[i] - X2[j];
+    if (dX <= -2*GHOST_RADIUS) {
+      i++;
+      continue;
+    }
+    if (dX >= 2*GHOST_RADIUS) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    for (; (dX > -2*GHOST_RADIUS) && (j < Nstar); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      dR = dX*dX + dY*dY;
+      if (dR < RADIUS2) {  
+	/* this object may be a ghost star, 
+	   but only mark those measurements on the correct image */
+	M = N2[j];
+	m = catalog[0].average[M].offset;
+	Ng = 0;
+	for (k = 0; k < catalog[0].average[M].Nm; k++) {
+	  if (catalog[0].image[m+k] == Nimage) {
+	    catalog[0].measure[m+k].average |= GHOST_DATA;
+	    Ng ++;
+	  }
+	}
+	/* all measurements are ghosts */
+	if (catalog[0].average[M].Nm == Ng) {
+	  catalog[0].average[M].code = ID_GHOST;
+	}
+      }
+    }
+    j = first_j;
+    i++;
+  }
+
+  free (X1);
+  free (Y1);
+  free (N1);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_trails.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_trails.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/find_trails.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "markstar.h"
+
+find_trails (catalog, catstats)
+Catalog catalog[];
+CatStats catstats[];
+{
+
+  int i, j, N, Nave, axis, marked;
+  double density, spacing, Area, Angle, m, b, RaCenter, DecCenter;
+  double MinRA, MaxRA, MinDEC, MaxDEC;
+  float *X1, *Y1;
+  char *mark;
+  int *N1;
+  Coords tcoords;
+  
+  Nave = catalog[0].Naverage;
+  N1 = catstats[0].N;
+
+  ALLOCATE (mark, char, Nave);
+  bzero (mark, Nave);
+
+  for (i = 0; i < Nave; i++) {
+    /* already marked, ignore */
+    if ((mark[i]) || 
+	(catalog[0].average[N1[i]].code == ID_BLEED) || 
+	(catalog[0].average[N1[i]].code == ID_GHOST))
+      continue;
+    /* a good star, ignore */
+    if ((catalog[0].average[N1[i]].Nm > 3) && 
+	(catalog[0].average[N1[i]].Nm > 4*catalog[0].average[N1[i]].Nn)) {
+      continue;
+    }
+    if (find_group (catstats, mark, Nave, i, &Angle)) {
+      /* this point has an excess nearby concentration, find the line */
+      axis = find_line (catstats, mark, Nave, i, &m, &b, Angle);
+      find_better_line (catstats, mark, Nave, i, &m, &b, axis);
+      mark_trail (catstats, mark, Nave, i, m, b, axis, catalog);
+    }
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/gcatstats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/gcatstats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/gcatstats.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "markstar.h"
+
+void gcatstats (Catalog *catalog, CatStats *catstats) {
+
+  int i;
+  double RaCenter, DecCenter;
+  double MinRA, MaxRA, MinDEC, MaxDEC;
+  double *X1, *Y1;
+  int *N1;
+  Coords tcoords;
+  
+  gfits_scan (&catalog[0].header, "RA0", "%lf", 1, &MinRA);
+  gfits_scan (&catalog[0].header, "RA1", "%lf", 1, &MaxRA);
+  gfits_scan (&catalog[0].header, "DEC0", "%lf", 1, &MinDEC);
+  gfits_scan (&catalog[0].header, "DEC1", "%lf", 1, &MaxDEC);
+
+  /* double check on region RA and DEC ranges */
+  DecCenter = 0.5*(MinDEC + MaxDEC);
+  RaCenter = 0.5*(MinRA + MaxRA);
+  if (MaxDEC > 86.25) {  /* we are on the pole */
+    DecCenter = 90.0;
+    RaCenter = 0.0;
+  }
+
+  catstats[0].RA[0] = MinRA;
+  catstats[0].RA[1] = MaxRA;
+  catstats[0].DEC[0] = MinDEC;
+  catstats[0].DEC[1] = MaxDEC;
+  catstats[0].Area = (MaxDEC - MinDEC)*(MaxRA - MinRA) / cos (RAD_DEG*DecCenter);
+  if (MaxDEC > 86.25) {  /* we are on the pole */
+    catstats[0].Area = 44.2;
+  }
+  catstats[0].density = catalog[0].Naverage / (3600*3600*catstats[0].Area);
+  /* catstats[0].density = MIN_DENSITY; */
+  catstats[0].spacing = 1.0 / (NSIGMA * catstats[0].density * 2*TRAIL_WIDTH);
+  fprintf (stderr, "Area, density, spacing: %f %f %f\n", catstats[0].Area, 
+	   catstats[0].density, catstats[0].spacing);
+  /* number of stars per square arcsec */
+
+  /** allocate local arrays **/
+  ALLOCATE (catstats[0].X, double, catalog[0].Naverage);
+  ALLOCATE (catstats[0].Y, double, catalog[0].Naverage);
+  ALLOCATE (catstats[0].N, int,   catalog[0].Naverage);
+
+  /* project onto rectilinear grid with 1 arcsec pixels, sort by X */
+  /* reference for coords is center of field  */
+  catstats[0].coords.crval1 = RaCenter;
+  catstats[0].coords.crval2 = DecCenter;
+  catstats[0].coords.crpix1 = catstats[0].coords.crpix2 = 0.0;
+  catstats[0].coords.cdelt1 = catstats[0].coords.cdelt2 = 1.0 / 3600.0;
+  catstats[0].coords.pc1_1 = catstats[0].coords.pc2_2 = 1.0;
+  catstats[0].coords.pc1_2 = catstats[0].coords.pc2_1 = 0.0;
+  strcpy (catstats[0].coords.ctype, "RA---TAN");
+
+  X1 = catstats[0].X;
+  Y1 = catstats[0].Y;
+  for (i = 0; i < catalog[0].Naverage; i++, X1++, Y1++) {
+    RD_to_XY (X1, Y1, catalog[0].average[i].R, catalog[0].average[i].D, &catstats[0].coords);
+    catstats[0].N[i] = i;
+  }
+  if (catalog[0].Naverage > 1) sort_coords_index (catstats[0].X, catstats[0].Y, catstats[0].N, catalog[0].Naverage);
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/mark_trail.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/mark_trail.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/mark_trail.c	(revision 22322)
@@ -0,0 +1,272 @@
+# include "markstar.h"
+int *make_common_list ();
+int *check_common_list ();
+
+mark_trail (catstats, mark, Nave, i, m, b, axis, catalog)
+     CatStats catstats[];
+     char *mark;
+     int i, Nave, axis;
+     double m, b;
+     Catalog catalog[];
+{
+
+  float *R, *D;
+  int j, k, jj, kk, N, NPTS, marked, start, end, Nm;
+  int *good, *seq, *N1;
+  double *dist, *dist2;
+  double d2, di, Di, dD, n, Dist, scale, norm;
+  double spacing;
+  int *list, Nlist;
+  int M, Nmeas, thisimage;
+
+  R = catstats[0].X;
+  D = catstats[0].Y;
+  N1 = catstats[0].N;
+  spacing = catstats[0].spacing;
+
+  NPTS = 200;
+  ALLOCATE (good, int, NPTS);
+  ALLOCATE (dist, double, NPTS);
+  ALLOCATE (dist2, double, NPTS);
+  ALLOCATE (seq, int, NPTS);
+
+  /* Find all points which lie near line */
+  /* save the entry number and distance along line */
+  N = 0;
+  scale = sqrt (1.0 + m*m);
+
+  if (axis == 1) {
+    for (j = 0; j < Nave; j++) {
+      norm = scale * fabs(R[j] - m*D[j] - b);
+      if (!mark[j] && (norm < TRAIL_WIDTH)) {
+	good[N] = j;
+	dist[N] = scale * (D[j] - D[i] + m*(R[j] - R[i]));
+	seq[N] = N;
+	N++;
+	if (N == NPTS - 1) {
+	  NPTS += 200;
+	  REALLOCATE (good, int, NPTS);
+	  REALLOCATE (dist, double, NPTS);
+	  REALLOCATE (dist2, double, NPTS);
+	  REALLOCATE (seq, int, NPTS);
+	}
+      }
+    }
+  } else {
+    for (j = 0; j < Nave; j++) {
+      norm = scale * fabs(D[j] - m*R[j] - b);
+      if (!mark[j] && (norm < TRAIL_WIDTH)) {
+	good[N] = j;
+	dist[N] = scale * (R[j] - R[i] + m*(D[j] - D[i]));
+	seq[N] = N;
+	N++;
+	if (N == NPTS - 1) {
+	  NPTS += 200;
+	  REALLOCATE (good, int, NPTS);
+	  REALLOCATE (dist, double, NPTS);
+	  REALLOCATE (dist2, double, NPTS);
+	  REALLOCATE (seq, int, NPTS);
+	}
+      }
+    }
+  }
+  
+  if (N < NPTSINLINE) 
+    return (0);
+
+  sort_seq (dist, seq, N);
+  
+  start = -1; end = -1;
+  for (j = 0; j < N-1; j++) {
+    /* if we have part of a line, and next point is in the line, check for common images */
+    if ((start != -1) && (fabs(dist[j] - dist[j+1]) < spacing)) {
+      list = check_common_list (list, N1[good[seq[j+1]]], &Nlist, catalog);
+      if (Nlist == 0) { /* if no common images, dump list and continue */
+	end = j + 1;
+	if (end - start > NPTSINLINE) {
+	  if (axis == 0)
+	    fprintf (stderr, "marking line %f %f  %d pts  %d  %d %d, %f %f\n", m, b, N, end-start, start, end, dist[start], dist[end-1]);
+	  else
+	    fprintf (stderr, "marking line %f %f  %d pts  %d  %d %d, %f %f\n", 1.0/m, -1.0*b/m, N, end-start, start, end, dist[start], dist[end-1]);
+	  for (k = start; k < end; k++) {
+	    M = N1[good[seq[k]]];
+	    mark[good[seq[k]]] = TRUE;
+	    /* we need to mark measurements from all images in common on the line */
+	    Nm = 0;
+	    for (jj = 0; jj < catalog[0].average[M].Nm; jj++) {
+	      Nmeas = catalog[0].average[M].offset + jj;
+	      thisimage = catalog[0].image[Nmeas];
+	      for (kk = 0; kk < Nlist; kk++) {
+		if (thisimage == list[kk]) {
+		  catalog[0].measure[Nmeas].average |= PART_OF_TRAIL;
+		  Nm ++;
+		}
+	      }
+	    }
+	    /* if there is only 1 measurement, mark object as bad */
+	    if (catalog[0].average[M].Nm == Nm) {
+	      catalog[0].average[M].code = ID_TRAIL;
+	    }
+	    catalog[0].average[M].code = ID_TRAIL;
+	  }
+	}
+	start = -1;
+	end = -1;
+      }
+    }
+    /* if we haven't yet found a line segment, check for the beginning */
+    if ((start < 0) && (fabs(dist[j] - dist[j+1]) < spacing)) {
+      start = j;
+      list = make_common_list (N1[good[seq[j]]], N1[good[seq[j+1]]], &Nlist, catalog);
+      if (Nlist == 0) { /* if no common images, move on */
+	start = -1;
+	free (list);
+      }
+    }
+    /* if we have a complete line, check for validity.  if it has enough members,
+	 mark them and continue searching for lines */
+    if ((start != -1) && ((fabs(dist[j] - dist[j+1]) >= spacing) || (j == N-2))) {
+      end = j + 1;
+      if (end - start > NPTSINLINE) {
+	if (axis == 0)
+	  fprintf (stderr, "marking line %f %f  %d pts  %d  %d %d, %f %f\n", m, b, N, end-start, start, end, dist[start], dist[end-1]);
+	else
+	  fprintf (stderr, "marking line %f %f  %d pts  %d  %d %d, %f %f\n", 1.0/m, -1.0*b/m, N, end-start, start, end, dist[start], dist[end-1]);
+	for (k = start; k < end; k++) {
+	  M = N1[good[seq[k]]];
+	  mark[good[seq[k]]] = TRUE;
+	  if (catalog[0].average[M].code == ID_BLEED) continue;
+	  /* we need to mark measurements from all images in common on the line */
+	  Nm = 0;
+	  for (jj = 0; jj < catalog[0].average[M].Nm; jj++) {
+	    Nmeas = catalog[0].average[M].offset + jj;
+	    thisimage = catalog[0].image[Nmeas];
+	    for (kk = 0; kk < Nlist; kk++) {
+	      if (thisimage == list[kk]) {
+		catalog[0].measure[Nmeas].average |= PART_OF_TRAIL;
+		Nm ++;
+	      }
+	    }
+	  }
+	  /* if there is only 1 measurement, mark object as bad */
+	  if (catalog[0].average[M].Nm == Nm) {
+	    catalog[0].average[M].code = ID_TRAIL;
+	  }
+	  catalog[0].average[M].code = ID_TRAIL;
+	}
+      }
+      free (list);
+      start = -1;
+      end = -1;
+    }
+  }
+}
+
+
+/* I is Average seq number for star 1, J for star 2 */
+/* make a list of images in common between two measurements */
+int *make_common_list (I, J, Nlist, catalog)
+int I, J, *Nlist;
+Catalog catalog[];
+{
+
+  int i, j, N1, N2, nlist, NLIST;
+  int *list;
+  int k, already;
+
+  NLIST = 50;
+  ALLOCATE (list, int, NLIST);
+  nlist = 0;
+
+  for (i = 0; i < catalog[0].average[I].Nm; i++) {
+    N1 = catalog[0].average[I].offset + i;
+    if (catalog[0].image[N1] == -1)
+      continue; /* not a real measurement */
+    for (j = 0; j < catalog[0].average[J].Nm; j++) {
+      N2 = catalog[0].average[J].offset + j;
+      if (catalog[0].image[N2] == -1)
+	continue; /* not a real measurement */
+      if (catalog[0].image[N1] == catalog[0].image[N2]) {
+	already = FALSE; 
+	for (k = 0; !already && (k < nlist); k++) {
+	  if (catalog[0].image[N1] == list[k]) { 
+	    already = TRUE;
+	  }
+	}
+	if (!already) {
+	  list[nlist] = catalog[0].image[N1];
+	  nlist ++;
+	  if (nlist == NLIST - 1) {
+	    NLIST += 50;
+	    REALLOCATE (list, int, NLIST);
+	  }
+	}
+      }
+    }
+  }
+  
+  REALLOCATE (list, int, MAX(nlist, 1));
+  *Nlist = nlist;
+  return (list);
+
+}
+
+
+
+/* J is Average seq number for star */
+
+int *check_common_list (inlist, J, Nlist, catalog)
+int *inlist, J, *Nlist;
+Catalog catalog[];
+{
+
+  int i, j, N2, Ninlist;
+  int *list, NLIST, nlist;
+  int already, found, k;
+
+  NLIST = 50;
+  ALLOCATE (list, int, NLIST);
+  nlist = 0;
+  Ninlist = *Nlist;
+
+  for (j = 0; j < catalog[0].average[J].Nm; j++) {
+    N2 = catalog[0].average[J].offset + j;
+    found = FALSE;
+    for (i = 0; !found && (i < Ninlist); i++) {
+      if (catalog[0].image[N2] == -1)
+	continue; /* not a real measurement */
+      if (inlist[i] == catalog[0].image[N2]) {
+	found = TRUE;
+	already = FALSE; 
+	for (k = 0; !already && (k < nlist); k++) {
+	  if (inlist[i] == list[k]) {
+	    already = TRUE;
+	  }
+	}
+	if (!already) {
+	  list[nlist] = inlist[i];
+	  nlist ++;
+	  if (nlist == NLIST - 1) {
+	    NLIST += 50;
+	    REALLOCATE (list, int, NLIST);
+	  }
+	}
+      }
+    }
+  }
+  
+  /* if there are no common images, return the input list */
+  if (nlist != 0) {
+    free (inlist);
+    REALLOCATE (list, int, MAX(nlist, 1));
+    *Nlist = nlist;
+    return (list);
+  } else {
+    return (inlist);
+  }
+
+}
+
+
+/* measurements associated with image number -1 are not 
+   measurements we made, but are added, like USNO */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/markstar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/markstar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/markstar.c	(revision 22322)
@@ -0,0 +1,79 @@
+# include "markstar.h"
+
+/*** this needs to be cleaned to match current db I/O model ***/
+
+int main (int argc, char **argv) {
+
+  FILE *f;
+  int i, Nstars, Nimage, Nregions, Nmissed;
+  Image *image, *find_images();
+  Catalog catalog;
+  CatStats catstats;
+  struct timeval now, then;  
+  FITS_DB db;
+  
+  gettimeofday (&then, (void *) NULL);
+  ConfigInit (&argc, argv);
+
+  VERBOSE = FALSE;
+  if ((i = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (i, &argc, argv);
+  }
+  FORCE_RUN = FALSE;
+  if ((i = get_argument (argc, argv, "-f"))) {
+    FORCE_RUN = TRUE;
+    remove_argument (i, &argc, argv);
+  }
+  RESET = FALSE;
+  if ((i = get_argument (argc, argv, "-reset"))) {
+    RESET = TRUE;
+    remove_argument (i, &argc, argv);
+  }
+  if (argc < 2) {
+    fprintf (stderr, "ERROR: Usage: markstar (catalog)\n");
+    exit (0);
+  }
+
+  set_db (&db);
+  dvo_image_lock (&db, ImageCat, 3600.0, LCK_XCLD);
+
+  catalog.filename = argv[1];
+  catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+  catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, VERBOSE, "a")) {
+    fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+    exit (2);
+  }
+  if (!catalog.Naves_disk) {
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+    exit (0);
+  }
+
+  gcatstats (&catalog, &catstats);
+
+  image = find_images (&catstats, &Nimage);
+
+  match_images (&catalog, image, Nimage);
+    
+  find_trails (&catalog, &catstats, i);  
+
+  /* find_bright_stars (&catalog, &catstats);  */
+
+  /* find_ghosts (&catalog, &catstats, argv[1], image, Nimage); */
+
+  dvo_catalog_save (&catalog, VERBOSE);
+  dvo_catalog_unlock (&catalog);
+  dvo_catalog_free (&catalog);
+
+  if (VERBOSE) {
+    gettimeofday (&now, (void *) NULL);
+    fprintf (stderr, "%s: elapsed time = %.2f sec\n", argv[1], 
+	     (now.tv_sec - then.tv_sec) + 1e-6*(now.tv_usec - then.tv_usec));
+  }
+  fprintf (stderr, "SUCCESS\n");
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/match_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/match_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/match_images.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "markstar.h"
+
+match_images (catalog, image, Nimage)
+Catalog catalog[];
+Image   image[];
+int Nimage;
+{
+  
+  int j, k, found;
+  unsigned int *start, *stop;
+  short int *source;
+
+  /* this must be allocated so future free will not fail */
+  ALLOCATE (catalog[0].image, int, MAX (catalog[0].Nmeasure, 1));
+  if (catalog[0].Naverage == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (FALSE);
+  }
+
+  ALLOCATE (start, unsigned int, Nimage);
+  ALLOCATE (stop,  unsigned int, Nimage);
+  ALLOCATE (source, short int, Nimage);
+  for (j = 0; j < Nimage; j++) {
+    start[j] = image[j].tzero - MAX(0.01*image[j].trate*image[j].NY, 1);
+    stop[j]  = image[j].tzero + MAX(1.01*image[j].trate*image[j].NY, 1);
+    source[j] = image[j].photcode;
+  }
+
+  for (j = 0; j < catalog[0].Nmeasure; j++) {
+    found = FALSE;
+    if (catalog[0].measure[j].t == 0) {
+      catalog[0].image[j] = -1;
+      found = TRUE;
+    }
+    for (k = 0; (k < Nimage) && !found; k++) {
+      if ((catalog[0].measure[j].t >= start[k]) && 
+	  (catalog[0].measure[j].t <= stop[k])  &&
+	  (catalog[0].measure[j].photcode == source[k])) {
+	catalog[0].image[j] = k;
+	found = TRUE;
+      }
+    }
+    if (!found) {
+      fprintf (stderr, "ERROR: can't find source image for this measurement: %d %d)\n",
+	       catalog[0].measure[j].t, catalog[0].measure[j].photcode);
+      exit (0);
+    }
+  }
+  free (start);
+  free (stop);
+  free (source);
+}
+
+  /* this routine uses the time of each measurement to match the
+measurement with an image.  Since the measurement is only store to 1
+sec accuracy, which corresponds to roughly 30 rows at nominal speed,
+we can't tell exactly which image the star come from.  However, this
+doesn't matter, and in fact this helps a bit: a measurement from the
+top of one image is the same as from the bottom of the next.
+Therefore, we intentionally blur the edges of the images by 5%, which
+will help to tie together neighboring images... */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/sorts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/sorts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/markstar/src/sorts.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "markstar.h"
+
+void sort_seq (double *X, int *S, int N) {
+
+# define SWAPFUNC(A,B){ \
+  double dtmp; dtmp = X[A]; X[A] = X[B]; X[B] = dtmp; }
+  int    itmp; itmp = S[A]; S[A] = S[B]; S[B] = itmp; }
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/Makefile	(revision 22322)
@@ -0,0 +1,75 @@
+default: misc
+help:
+	@echo "make options: misc (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/misc
+SRC	=	$(HOME)/src
+LIB	= 	$(HOME)/lib
+MAN	=	$(HOME)/doc
+BIN	=	$(HOME)/bin
+INC	=	$(HOME)/include
+
+FULL_CFLAGS   =	$(CFLAGS)
+FULL_CPPFLAGS = $(CPPFLAGS) -I$(INC) -I$(DESTINC) $(INCDIRS) -D$(ARCH)
+FULL_LDFLAGS  = $(LDFLAGS) -L$(LIB) -L$(DESTLIB) $(LIBDIRS) -ldvo -lFITS -lohana $(LIBFLAGS)
+
+# these are all programs which just depend on a single c file: foo : foo.c
+# we use a special set of rules in this directory which expect this simplification
+
+PROGRAMS = mknames mkgauss mkfringetable gtfringetable applyscat fiximg
+
+misc: $(PROGRAMS)
+
+$(PROGRAMS): % : $(BIN)/%.$(ARCH)
+
+fakestars: $(BIN)/fakestars.$(ARCH)
+$(BIN)/fakestars.$(ARCH): $(SRC)/fakestars.$(ARCH).o $(SRC)/random.$(ARCH).o
+
+#### cfhtlog is special (requires additional libs and includes)
+
+$(BIN)/cfhtlog.$(ARCH): $(SRC)/cfhtlog.c
+	$(CC) -I/cfht/include -L/cfht/lib -o $(BIN)/cfhtlog.$(ARCH) $(SRC)/cfhtlog.c -lcfht 
+
+$(DESTBIN)/cfhtlog: $(BIN)/cfhtlog.$(ARCH)
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	cp $(SRC)/cfhtlog.$(ARCH) $(DESTBIN)/
+
+cfhtlog:
+	make $(DESTBIN)/cfhtlog
+
+# copied from Makefile.Common : use a single c file: foo : foo.c
+.PRECIOUS: %.$(ARCH).o
+.PRECIOUS: $(BIN)/%.$(ARCH)
+
+%.$(ARCH).o : %.c
+	$(CC) $(FULL_CFLAGS) $(FULL_CPPFLAGS) -c $< -o $@
+
+$(BIN)/%.$(ARCH) : $(SRC)/%.$(ARCH).o
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	$(CC) $(FULL_CFLAGS) -o $@ $^ $(FULL_LDFLAGS)
+
+$(DESTBIN)/%: $(BIN)/%.$(ARCH) 
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	rm -f $(DESTBIN)/$*
+	cp $(BIN)/$*.$(ARCH) $(DESTBIN)/$*
+
+%.install:
+	make $(DESTBIN)/$*
+
+install:
+	for i in $(PROGRAMS); do make $$i.install || exit; done
+
+%.clean:
+	rm -f $(SRC)/$*.$(ARCH).o
+	rm -f $(BIN)/$*.$(ARCH)
+
+clean:
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
+
+dist: clean
+	rm -rf $(BIN)
+	rm -rf $(LIB)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,26 @@
+
+- misc-1-3 : 2006.08.23
+  * converted to gfits APIs (forces libfits 1.6)
+
+misc-1-2:
+
+  - minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+2005.10.07
+
+	I was having some memory collision problems, and attempting to
+	use the ohana_allocate functions reminded me that the libFITS
+	functions were not supported under ohana_allocate.  This was
+	unhelpful.  I bit the bullet and split libohana into libohana
+	(base functions only, including ohana_allocate) and libdvo
+	(functions based on the libautocode structures).  Doing this
+	allowed me to make libFITS depend on libohana (including
+	ohana_allocate).  BUT, this forced me to change all LDFLAGS
+	entries in ohana to swap -lohana -lFITS for -lFITS -lohana,
+	and to add include <fitsio.h> in some cases.
+
+misc-1-1:
+  - dropped old, out-of-date programs
+  - minor changes for loneos.h -> dvo.h
+  - cleanup of glockfile things
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/include/cfht/cfht.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/include/cfht/cfht.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/include/cfht/cfht.h	(revision 22322)
@@ -0,0 +1,793 @@
+/* Copyright (C) 1991-1999  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         */
+/*****************************************************************************
+ *
+ * file: cfht.h
+ * $Id: cfht.h,v 1.1.1.1 2004-11-24 04:39:33 eugene Exp $
+ * $Locker:  $
+ *
+ * This file is the main include file for the generic library.
+ *
+ *
+ * HISTORY
+ *
+ * who       when          what
+ * -------   -----------   ----------------------------------------------
+ * jab       27 Jan 1988   Original coding
+ * jab        1 Mar 1988   Added config file stuff
+ * jab       12 Mar 1988   Added logging stuff
+ * jk        22 Mar 1988   Added cfht_od()
+ * jk/jab    24 Mar 1988   Added CFHT_TAPE and CFHT_DISPLAY
+ * jab        6 Apr 1988   Added puma link device
+ * jab       29 Apr 1988   Added already included check
+ * sss       08 May 1988   Added CFHT_NPIPE
+ * jk        17 May 1988   Modified tape and display because of collision
+ * jk        06 Oct 1988   added cfht_resetetimer as a define
+ * sss       17 Mar 1989   added cfht_log() support stuffs
+ * sss       11 Apr 1989   added real time defines for detectors,etc.
+ * sss       24 Oct 1989   added CFHT_RELEASE define for .,config access
+ * sss        3 Jan 1990   added CFHT_*SESSIONHOST defines for env. vars
+ * jrw       01 Oct 1990   added stuff for cfht_number
+ * jrw       02 Nov 1990   added CFHT_STR_* defines
+ * jk        27 Feb 1991   cfht_exec unions not used anymore
+ * sss       27 Mar 1991   added cfht_basename()
+ * jrw       09 May 1991   added function definitions (ANSI and crufty flavors)
+ * jk        29 Aug 1991   changed cfht_errno from BOOLEAN to int
+ *
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  2001/07/25 02:59:23  eugene
+ * import Ohana
+ *
+ * Revision 1.44  2000/04/10  11:28:40  isani
+ * Added prototype for cfht_log_proxy.
+ * 
+ * Revision 1.43  2000/02/10 13:19:08  isani
+ * Added include of sys/types.h whenever pid_t is needed.
+ *
+ * Revision 1.42  1999/12/02 21:40:30  thomas
+ * Added PFVint
+ *
+ * Revision 1.41  1999/07/25 06:29:57  thomas
+ * Added UTDATE/TIME and PFDATE defs
+ *
+ * Revision 1.40  1999/04/29 17:18:00  thomas
+ * Added @ ( # ) to RCSID string for what(1) to find
+ * Added non-gcc but STDC version of above
+ * Added changes for switch to /cfht
+ *
+ * Revision 1.39  99/02/17  13:55:15  13:55:15  thomas (Jim Thomas)
+ * Added cfht_cfhthome
+ * 
+ * Revision 1.38  98/12/11  18:23:20  18:23:20  thomas (Jim Thomas)
+ * Added CFHT_*OBSERVERPATH
+ * 
+ * Revision 1.37  98/10/22  17:52:44  17:52:44  thomas (Jim Thomas)
+ * Added CFHT_ESESSION
+ * 
+ * Revision 1.36  98/07/16  15:53:32  15:53:32  thomas (Jim Thomas)
+ * Added cfht_system
+ * 
+ * Revision 1.35  98/07/07  12:52:47  12:52:47  thomas (Jim Thomas)
+ * Added CFHTPATH , IOCONFDIR , and CONFDIR definitions
+ * 
+ * Revision 1.34  98/04/09  17:52:20  17:52:20  thomas (Jim Thomas)
+ * Added cfht_argsToString
+ * 
+ * Revision 1.33  98/02/25  16:35:00  16:35:00  thomas (Jim Thomas)
+ * Added prototype for cfht_logpv
+ * 
+ * Revision 1.32  97/12/09  00:11:48  00:11:48  healey (SueAnn Healey)
+ * added CFHT_LIBSSX
+ * 
+ * Revision 1.31  1997/12/01 16:03:29  isani
+ * Moved missing protos to separate file.
+ * Added extern "C" declarations for C++ projects.
+ *
+ * Revision 1.30  1997/09/17 13:48:31  thomas
+ * Added CFHT_EXEC_NOT_EXITED and CFHT_EXEC_FAILED
+ * Documented bogus use of FAIL in exp_controller_t
+ *
+ * Revision 1.29  97/06/29  15:41:07  15:41:07  isani (Sidik Isani)
+ * Had to fiddle with some of the includes to get to compile with GCC
+ *   on the sparc engines.
+ * 
+ * Revision 1.28  97/05/28  02:51:35  isani
+ * added CFHT_SIG_ defines for handler argument of cfht_signal()
+ * 
+ * Revision 1.27  97/04/07  20:10:26  thomas
+ * Added LIBOMS, changed cfht_signal event prototypes
+ * 
+ * Revision 1.26  96/11/22  02:36:44  02:36:44  isani (Sidik Isani)
+ * added macro for TEMP_FAILURE_RETRY, in case anyone wants to use it
+ * changed cfht_delay(n) into a macro that calls cfht_sleep(n,1)
+ * 
+ * Revision 1.25  96/11/19  22:24:19  isani
+ * moved P() and Q() macros closer to top of file
+ * added RCSID() macro
+ * define volatile to nothing on compilers where everything is volatile
+ * define UNUSED for use with gcc, or nothing on compilers that don't care
+ * added a few more missing libcfht prototypes
+ * concentrated most of the old-sun hacks here rather than in each file
+ *   (protos for getenv, putenv, different name for mktime, etc.)
+ * fixed order of arguments in cfht_fromjulian prototype
+ * 
+ * Revision 1.24  96/08/14  13:01:17  13:01:17  thomas (Jim Thomas)
+ * Added TCS_ConsolePrompt error return code
+ * 
+ * Revision 1.23  96/05/20  16:01:16  16:01:16  thomas (Jim Thomas)
+ * Added LIBCX, TCS_CommandFailed,
+ * Fixed F4 cfht_errno definitions
+ * 
+ * Revision 1.22  96/02/21  11:16:07  11:16:07  isani (Sidik Isani)
+ * cfht_errno codes for gecko and f4 server
+ * 
+ * Revision 1.21  1995/07/19 12:48:00  thomas
+ * Added AOB cfht_errno codes
+ *
+ * Revision 1.20  95/06/29  11:38:38  11:38:38  thomas (Jim Thomas)
+ * Added cfht_errno codes for IDS
+ * 
+ * Revision 1.19  95/06/13  12:03:06  12:03:06  thomas (Jim Thomas)
+ * Added environment variables and cfht_log_who entries for CS, DUCK, HELP,
+ * IDS, PIXD, TCS, and UI libraries
+ * 
+ * Revision 1.18  95/06/07  17:22:30  17:22:30  thomas (Jim Thomas)
+ * Added typedef for cfht_errno codes
+ * 
+ * Revision 1.17  94/12/28  17:06:51  17:06:51  thomas (Jim Thomas)
+ * Moved strerror definition for suns here from cfp.h (for cfht_logv)
+ * 
+ * Revision 1.16  94/12/21  10:35:46  10:35:46  john (John Kerr)
+ * add pid_t.
+ * 
+ * Revision 1.15  94/12/15  10:46:42  10:46:42  john (John Kerr)
+ * modified pid in cfht_exec_data to be a pid_t type.
+ * 
+ * Revision 1.14  94/12/14  10:50:01  10:50:01  thomas (Jim Thomas)
+ * Added CFHT_LIBMUSIC, CFHT_VALUE_SIZE, Q macro
+ * Deleted BYTE
+ * 
+ * Revision 1.13  94/09/20  18:40:45  18:40:45  thomas (Jim Thomas)
+ * added CFHT_LOGONLY, CFHT_LIBOCS
+ * reworked symbols related to cfht_log
+ * 
+ * Revision 1.12  94/08/14  10:27:17  10:27:17  jwright (Jim Wright)
+ * add warning log messages and color messages
+ * 
+ * Revision 1.11  94/07/11  15:48:53  15:48:53  veran (Jean-Pierre Veran)
+ * AAdded net logging capabilities
+ * 
+ * Revision 1.10  94/02/02  14:08:00  14:08:00  jwright (Jim Wright)
+ * remove typedef of FILE_ID form cfp.h and put it in cfht.h.  reason is
+ * that cfht_exec() has an argument of type FILE_ID and thus needs to have
+ * this type visible so that it can correctly declare an ANSI prototype.
+ * it is presumed that any file including cfp.h will first have included
+ * cfht.h.
+ * 
+ * Revision 1.9  94/01/20  20:35:13  20:35:13  jwright (Jim Wright)
+ * fix enum
+ * 
+ * Revision 1.8  94/01/20  04:51:31  04:51:31  jwright (Jim Wright)
+ * add enumeration for controller types
+ * 
+ * Revision 1.7  93/11/26  10:14:09  10:14:09  jwright (Jim Wright)
+ * added definitions to retrieve info from net.par
+ * 
+ * Revision 1.6  93/05/19  08:45:49  08:45:49  steve (Steven Smith)
+ * added cfht_logv TIMING define
+ * 
+ * Revision 1.5  93/02/16  16:21:15  16:21:15  john (John Kerr)
+ * added a CFHT_INSTRUMENT_TYPE
+ * 
+ * Revision 1.4  92/12/01  19:21:47  19:21:47  steve (Steven Smith)
+ * added CFHT_ELOGHOST for cfht_log() net based
+ * 
+ * Revision 1.3  92/11/09  13:11:03  13:11:03  john (John Kerr)
+ * added void cfht_goDaemon()
+ * 
+ * Revision 1.2  91/11/15  10:07:52  10:07:52  jwright (Jim Wright)
+ * add macro to handle both ansi and k&r style parameters
+ * in function declarations; convert declarations to use macro
+ * 
+ * Revision 1.1  91/09/24  15:59:31  15:59:31  steve (Steven S Smith)
+ * Initial revision
+ * 
+ ****************************************************************************/
+
+#ifndef CFHTDOTH
+#define CFHTDOTH
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef pid_t
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif /* pid_t */
+
+/******************************************************************
+ * macro to declare prototypes for ansi, but still works with k&r *
+ *                                                                *
+ * define prototype like                                          *
+ *        int foo P((int a, char c))                              *
+ *                                                                *
+ * note that the space before the P and the doubled parentheses   *
+ *      are needed                                                *
+ ******************************************************************/
+
+#ifndef P
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+#endif
+
+/********************************************************************
+ * macro to declare routine parameters for ansi that works with k&r *
+ *                                                                  *
+ * define actual routine like                                       *
+ *        int foo Q((int a, char c), (a, c), int a; char c;) {      *
+ *                                                                  *
+ * note that inner parentheses are needed on the ansi parameters    *
+ *                                 needed on the K&R parameters     *
+ *                             and must not be there on the K&R     *
+ *                                          type definition         *
+ *           there must be a space before the Q                     *
+ *           the arguments to Q can be on separate lines like       *
+ *                         int foo Q((int a, char c),               *
+ *                                   (a, c),                        *
+ *                                   int a; char c;)                *
+ *                         {                                        *
+ ********************************************************************/
+
+#ifndef Q
+#ifdef __STDC__
+#define Q(ansi, karparm, kartype) ansi
+#else
+#define Q(ansi, karparm, kartype) karparm kartype
+#endif
+#endif
+
+/*
+ * UNUSED attribute to tell Gnu compiler not to warn
+ * and define volatile to nothing for compilers where everything
+ * is volatile (We hope!)
+ */
+#ifdef __STDC__
+#ifdef __GNUC__
+#define UNUSED __attribute__ ((__unused__))
+#else
+#define UNUSED
+#endif
+#else
+#define UNUSED
+#define volatile
+#endif
+
+/*
+ * Macro to use for inserting rcs id's 
+ */
+
+#ifdef __LINT__
+#define RCSID(string) extern int lint_rcs_id_dummy
+#else
+#if defined(__GNUC__) && defined(__STDC__)
+#define RCSID(string) \
+  static const char rcs_id[] __attribute__ ((__unused__)) = "@(#) " string
+#else
+#if defined(__STDC__)
+#define RCSID(string) static char rcs_id[] = "@(#) " string
+#else
+#define RCSID(string) static char rcs_id[] = string
+#endif
+#endif
+#endif
+
+/*********************
+ * type definitions *
+ ********************/
+
+typedef int BOOLEAN;           /* should only contain TRUE, or FALSE */
+typedef double ANGLE;          /* floating pt. arc seconds */
+typedef double TIME;           /* floating pt. seconds */
+typedef double JDATE;          /* julian day number */
+typedef char *DATE;            /* ascii string date */
+typedef int PASSFAIL;          /* for functions that return PASS, or FAIL */
+typedef void (*PFV) P((void));   /* pointer to function that returns void */
+typedef void (*PFVint) P((int));   /*  ditto, with int parameter */
+typedef void (*PFVui) P((int,int,void*));  /* used for ui callbacks */
+typedef void (*PFVexec) P((pid_t,int));    /* used by cfht_exec() */
+
+enum exp_controller_t {       /* valid detector controller types */
+    FAIL = -1,			  /* NOTE - depends on define below :-( */
+    CONTROLLER_GENIII,
+    CONTROLLER_CC200,
+    CONTROLLER_FTS,
+    CONTROLLER_RETICON
+};
+
+typedef int FILE_ID;              /* used for parse file instance handles */
+
+/*************
+ * constants *
+ *************/
+
+#define PASS  0           /* function returning with no error */
+#define FAIL  -1          /* function returning an error in errno */
+			  /* lots of things depend on FAIL being negative!! */
+
+#define TRUE  1           /* 'C' boolean */
+#define FALSE 0           /* 'C' boolean */
+
+#ifndef NULL              /* make sure it's not already defined */
+#define NULL  0           /* used mostly for null string pointers */
+#endif  /* NULL */
+
+#define CFHTLOCKS  "/tmp/cfhtlocks"
+
+/********************
+ * real time defs   *
+ ********************/
+
+#define CFHT_RT_DETECTOR 65    /* about 1/2 */
+#define CFHT_RT_MOMMA    75    /* not as fast */
+#define CFHT_RT_HANDLER  85    /* slower yet */
+
+/********************
+ * exec definitions *
+ ********************/
+
+#define CFHT_EXEC_NUM    10    /* number of pending deaths allowed */
+#define CFHT_EXEC         1    /* straight exec */
+#define CFHT_EXECFG       2    /* wait and return exit code */
+#define CFHT_EXECBG       3    /* dont wait and dont ever bother me */
+#define CFHT_EXECBGC      4    /* dont wait but deposit exit code when ... */
+#define CFHT_EXECBGH      5    /* dont wait but call handler when ... */
+                               /* (handler is called with pid and exit code */
+#define CFHT_EXEC_NOT_EXITED -1 /* initial value of exit code */
+#define CFHT_EXEC_FAILED -2    /*  exit code if ran but failed to exit */
+/* #define CFHT_EXEC_EXITED (8 bits of system exit code) */ /* normal exit */
+
+#define CFHT_ARGV_NUM   100    /* size of cfht_argv() arg list */
+#define CFHT_ARGS_SIZE 1024    /* size of string to make args out of */
+#define CFHT_VALUE_SIZE 256    /* max length of a value (as in name=value) */
+
+union cfht_exec_addr {
+    volatile int *code;        /* exit code (-1 -> not yet exited) */
+    PFV func;                  /* handler to call */
+};
+
+struct cfht_exec_data {
+    volatile pid_t pid;        /* process id (0 -> unused) */
+    volatile int type;         /* CFHT_EXECBGC, or CFHT_EXECBGH (0 -> ignore) */
+    volatile void *addr;       /* union not used anymore */
+};
+
+extern int cfht_exec_rtprio;
+
+/*
+ * paths and file names
+ * CFHT_E* is the name in the environment, without the E is the actual value
+ * at the moment - so try not to use these (unfortunately that means programs
+ * don't work outside the session, sigh, so they appear in cfp_file.c and
+ * roll.c at least :-(
+ */
+/*
+ * The original path to everything started at /usr/local/cfht .  To get things
+ * out from under /usr/local , we created an automount collection for /cfht .
+ * To make things more consistent with other packages, we changed from
+ * /usr/local/cfht/dev to /cfht/src for the top of the source tree.  While
+ * both paths still exist, handle both possibilities here (and nowhere else in
+ * any c code please :-)
+ */
+#if defined(HPUX10) || defined(Solaris)
+#define CFHT_ECFHTPATH   "cfhtdir"			/* top of our tree */
+#define CFHT_CFHTPATH    "/cfht"			/*   which is */
+#define CFHT_CONFDIR     "src/conf"			/* dir for par files */
+#define CFHT_IOCONFDIR   "conf"				/*   except ioconfig */
+#define CFHT_ECONFPATH   "CFHTCONFPATH"			/* path to par files */
+#define CFHT_CONFPATH    "/cfht/src/conf"		/*   which is */
+#else
+#define CFHT_ECFHTPATH   "cfhtdir"			/* top of our tree */
+#define CFHT_CFHTPATH    "/usr/local/cfht"		/*   which is */
+#define CFHT_CONFDIR     "dev/conf"			/* dir for par files */
+#define CFHT_IOCONFDIR   "conf"				/*   except ioconfig */
+#define CFHT_ECONFPATH   "CFHTCONFPATH"                 /* path to par files */
+#define CFHT_CONFPATH    "/usr/local/cfht/dev/conf"     /*   which is */
+#endif /* HPUX10 or Solaris */
+#define CFHT_ECONFNAME   "CFHTCONFNAME"                 /* name for .,config */
+#define CFHT_CONFNAME    "config"                       /*   which is */
+#define CFHT_ESESSION    "Session"			/* master session name */
+#define CFHT_EOBSERVERPATH "observer"                   /* path to top of homes */
+#define CFHT_OBSERVERPATH "/users/observer"             /*   which is */
+
+/* identifiers within config file */
+#define CFHT_DETECTOR    "detector"                     /* e.g. th1 */
+#define CFHT_RELEASE     "release"                      /* e.g. 901231 */
+#define CFHT_INSTRUMENT  "instrument"                   /* 'who am i' */
+#define CFHT_INSTRUMENT_TYPE  "instrument_type"         /* e.g. FTS */
+#define CFHT_HANDLERS    "handlers"                     /* e.g. tcsh, ccdh */
+#define CFHT_FOCUS       "focus"                        /* e.g. prime */
+#define CFHT_DISPLAY     "display_guy"                  /* display handler */
+#define CFHT_TAPE        "tape_guy"                     /* tape handler */
+#define CFHT_SESSION     "session"                      /* e.g. ccd, focam */
+
+/*****************
+ * net.par stuff *
+ *****************/
+
+#define CFHT_CCDSERVER	"ccdserver"
+#define CFHT_F4SERVER	"f4server"
+#define CFHT_FOCAMSERVER "focamserver"
+#define CFHT_GENSERVER	"genserver"
+#define CFHT_LOGSERVER	"logserver"
+#define CFHT_RFSERVER	"rfserver"
+#define CFHT_TCSSERVER	"tcsserver"
+#define CFHT_TRAFFIC	"traffic"
+
+/**********************
+ * session host stuff *
+ **********************/
+
+#define CFHT_ESESSIONHOST    "SESSIONHOST"    /* used in xstart files */
+#define CFHT_SESSIONHOST     "moe"            /* perhaps hostname()? */
+
+/*****************
+ * logging stuff *
+ *****************/
+
+/* environment variables */
+
+/*
+ * The following are viewed as boolean flags.  For each, the indicated setting
+ * has the described effect (e.g., "setenv CFHTERROR off" will cause error
+ * messages not to appear in the feedback window), and the inverse setting or
+ * no setting has the opposite effect (e.g., otherwise error messages appear
+ * in the feedback window).
+ */
+#define CFHT_EERROR     "CFHTERROR"     /* Off -> no error messages to
+					 * feedback window */
+#define CFHT_EWARN      "CFHTWARN"      /* Off -> no warning messages to
+					 * feedback window */
+#define CFHT_ENODISP    "CFHTNODISP"    /* On -> no START/STATUS/DONE messages
+					 * to feedback window */
+#define CFHT_EDEBUG     "CFHTDEBUG"     /* On -> CFHT_MAIN debug messages to
+					 * log file */
+#define CFHT_ELIB       "CFHTLIB"       /* On -> unknown lib debug messages to
+					 * log file */
+#define CFHT_ELIBCAMAC  "CFHTLIBCAMAC"  /* On -> libcamac debug to log file */
+#define CFHT_ELIBCCD    "CFHTLIBCCD"    /* On -> libccd debug to log file */
+#define CFHT_ELIBCFHT   "CFHTLIBCFHT"   /* On -> libcfht debug to log file */
+#define CFHT_ELIBCFP    "CFHTLIBCFP"    /* On -> libcfp debug to log file */
+#define CFHT_ELIBCS     "CFHTLIBCS"     /* On -> libcs debug to log file */
+#define CFHT_ELIBCX     "CFHTLIBCX"     /* On -> libcx debug to log file */
+#define CFHT_ELIBDUCK   "CFHTLIBDUCK"   /* On -> libduck debug to log file */
+#define CFHT_ELIBFF     "CFHTLIBFF"     /* On -> libff debug to log file */
+#define CFHT_ELIBHELP   "CFHTLIBHELP"   /* On -> libhelp debug to log file */
+#define CFHT_ELIBHH     "CFHTLIBHH"     /* On -> libhh debug to log file */
+#define CFHT_ELIBIDS    "CFHTLIBIDS"    /* On -> libids debug to log file */
+#define CFHT_ELIBMUSIC  "CFHTLIBMUSIC"  /* On -> libmusic debug to log file */
+#define CFHT_ELIBOCS    "CFHTLIBOCS"    /* On -> libocs debug to log file */
+#define CFHT_ELIBOMS    "CFHTLIBOMS"    /* On -> liboms debug to log file */
+#define CFHT_ELIBPIXD   "CFHTLIBPIXD"   /* On -> libpixd debug to log file */
+#define CFHT_ELIBRET    "CFHTLIBRET"    /* On -> libret debug to log file */
+#define CFHT_ELIBRET    "CFHTLIBRET"    /* On -> libret debug to log file */
+#define CFHT_ELIBSSX    "CFHTLIBSSX"    /* On -> libssx debug to log file */
+#define CFHT_ELIBTCS    "CFHTLIBTCS"    /* On -> libtcs debug to log file */
+#define CFHT_ELIBUI     "CFHTLIBUI"     /* On -> libui debug to log file */
+
+#define CFHT_ECOLORLOG	"CFHTCOLORLOG" 	/* Off -> no color on user log */
+
+#define CFHT_LOGU       "CFHTLOGU"      /* user log messages file name */
+#define CFHT_LOGS       "CFHTLOGS"      /* system log messages file name */
+#define CFHT_ELOGHOST	"CFHTLOGHOST" 	/* host for netbased logging */
+
+/* who  (NOTE, should be alphabetized only at release time) */
+typedef enum {
+    CFHT_MAIN = 0,		  /* caller is end user */
+    CFHT_LIB,			  /* caller is undefined library */
+    CFHT_LIBCAMAC,		  /* caller is CAMAC library */
+    CFHT_LIBCCD,		  /* caller is ccd library */
+    CFHT_LIBCFHT,		  /* caller is CFHT library */
+    CFHT_LIBCFP,		  /* caller is CFP library */
+    CFHT_LIBFF,			  /* caller is FITS library */
+    CFHT_LIBHH,			  /* caller is high level HPIB library */
+    CFHT_LIBMUSIC,		  /* caller is music library */
+    CFHT_LIBOCS,		  /* caller is OCS library */
+    CFHT_LIBRET,		  /* caller is reticon library */
+    CFHT_LIBCS,			  /* caller is client/server library */
+    CFHT_LIBDUCK,		  /* caller is DUCK library */
+    CFHT_LIBHELP,		  /* caller is Pegasus help library */
+    CFHT_LIBIDS,		  /* caller is IDS communication library */
+    CFHT_LIBPIXD,		  /* caller is pixel library */
+    CFHT_LIBTCS,		  /* caller is Telescope Control System */
+    CFHT_LIBUI,			  /* caller is user interface library */
+    CFHT_LIBCX,			  /* caller is coordinate transform library */
+    CFHT_LIBOMS,		  /* caller is OMS library */
+    CFHT_LIBSSX			  /* caller is ssx controller library */
+} cfht_log_who;
+
+/* type */
+typedef enum {
+    CFHT_LOG_ID = 0,		  /* client side initialization msg type */
+    CFHT_START,			  /* program is starting */
+    CFHT_STATUS,		  /* something interesting to the user */
+    CFHT_ERROR,			  /* error */
+    CFHT_FATAL = CFHT_ERROR,	  /* obsolete - about to exit due to error */
+    CFHT_ERRNO,			  /* do not use - error with errno message */
+    CFHT_WARN,			  /* warning - something's wrong */
+    CFHT_DONE,			  /* about to exit normally */
+    CFHT_LOGONLY,		  /* operational diagnositc info */
+    CFHT_DEBUG,			  /* program trace info */
+    CFHT_TIMING,		  /* print elapsed + delta times */
+    CFHT_FEEDINIT,		  /* init a feedback type output sink */
+    CFHT_FEEDQUIT,		  /* free up fd's, etc for feedback sinks */
+    CFHT_ROLLINIT,		  /* start up another roll type output sink */
+    CFHT_ROLLQUIT,		  /* shut down roll type output sinks */
+    CFHT_SHOWSINKS,		  /* used by developer to debug...  */
+    CFHT_SERVINIT		  /* re-init da logserver without killing it */
+} cfht_log_type;
+
+#define CFHT_DATE_SIZE 29	  /* size needed for cfht_date result */
+#define CFHT_UTDATE_SIZE 11	  /* size needed for cfht_UTdate result */
+#define CFHT_PFDATE_SIZE 11	  /* size needed for cfht_PFdate result */
+#define CFHT_TIME_SIZE 12	  /* size needed for cfht_time result */
+#define CFHT_UTTIME_SIZE 12	  /* size needed for cfhtUTtime result */
+
+/***********************
+ * parsing definitions *
+ ***********************/
+
+#define CFHT_STR_MATCH     1  /* check if input matches one of set of strings */
+#define CFHT_STR_INCLUDE   2  /* only pass through characters mentioned */
+#define CFHT_STR_EXCLUDE   3  /* only pass through characters not mentioned */
+#define CFHT_STR_NOWHITE   4  /* remove all white space from string */
+#define CFHT_STR_NOTRAIL   5  /* remove all trailing white space */
+#define CFHT_STR_ESCAPE    6  /* convert non-printing chars to escape codes */
+#define CFHT_STR_UNESCAPE  7  /* convert escape codes to chars */
+
+/**************************
+ * cfht_errno definitions *
+ **************************/
+
+typedef enum {
+    /* general error numbers */
+    CHECK_ERRNO = -1,		  /* check unix(tm) errno variable for reason */
+
+    /* unfortunately, some code stuffs errno into cfht_errno (e.g., old libids) */
+    /* so we need to use 0 but skip all low positive values */
+
+    NO_ERROR = 0,		  /* success */
+    NOT_A_TTY = 1000,		  /* historical uses, and general laziness */
+    PARSE_FAILED,		  /* unable to cope with input */
+    TOO_BIG,			  /* input exceeds hardware limit */
+    OUT_OF_RANGE,		  /* input exceeds software restriction */
+    TYPE_ERROR,			  /* incompatible or unexpected type */
+    UNIMPLEMENTED,		  /* feature not yet implemented */
+    BAD_OPTION,			  /* option passed in to routine was invalid */
+    INVALID_RANGE,		  /* range structure malformed or illegal */
+
+    /* OCS error codes */
+
+    OCS_FAILURE = 1100,		  /* internal OCS problem, e.g., buffers*/
+    OCS_NoSuchServer,		  /* requested non-existent server name */
+    OCS_SERVER_BUSY,		  /* couldn't get server's attention */
+    OCS_SERVER_T_REJECTED,	  /* task rejected by the server */
+    OCS_SERVER_T_FAILED,	  /* task didn't complete successfully */
+    OCS_NOTIFYCLOSED,		  /* server quit during transactions */
+    OCS_SERVER_TIMEOUT,		  /* server time-out on request */
+    OCS_ERROR,			  /* catchall */
+
+    /* OCS stuffs music's merrno in, so those should be here too */
+
+    M_NOSTART = 1200,		  /* did not call mstart() */
+    M_BODYTOOBIG,		  /* body overflow */
+    M_WRITERR,			  /* system error during write */
+    M_PARTWRITE,		  /* wrote partial message */
+    M_READERR,			  /* system error during read */
+    M_READCLOSED,		  /* read a closed fd */
+    M_BODYTOOSHORT,		  /* not enough in body for mget() */
+    M_BADMAGIC,			  /* bad magic numbers in header */
+    M_TIMEOUT,			  /* mread_until() timed-out */
+    M_NOQMSG,			  /* nothing to read on msg queue */
+
+    /* ids error codes */
+    IDS_openError = 1300,	  /* problem opening terminal */
+    IDS_termioError,		  /* problem doing termio ioctl */
+    IDS_FlushError,		  /* problem flushing buffer */
+    IDS_writeError,		  /* problem on write */
+    IDS_TimeOut,		  /* no response */
+    IDS_read0Bytes,		  /* read got nothing */
+    IDS_readError,		  /* read failed - not time out */
+
+    /* Client/Server codes */
+    CS_FakeMode = 1400,		  /* server is in fake mode */
+    CS_Disabled,		  /* server is disabled */
+
+    /* TCS error return codes */
+    TCS_InvalidCommand = 1500,	  /* invalid command sent to TCS */
+    TCS_InvalidArgument,	  /* invalid command argument sent to TCS */
+    TCS_NotTracking,		  /* TCS ignored command 'cause it's not on */
+    TCS_CommandFailed,		  /* TCS did command, but it did not work */
+    TCS_ConsolePrompt,		  /* TCS command prompt is probably up */
+    TCS_UnknownStatus,		  /* we don't understand returned code */
+
+    /* AOB error return codes */
+    AOB_UnknownCommand = 1600,	  /* we do not understand the command */
+    AOB_UnlockNotLocked,	  /* unlock when not locked */
+    AOB_NotLockOwner,		  /* unlock attempt by someone else? */
+    AOB_CurrentlyLocked,	  /* OMBA locked by another process */
+    AOB_BadArgValue,		  /* some argument was bad */
+
+    /* Coude f/4 and Gecko server conditions */
+    F4_UnknownCommand = 1700,	  /* empty / badly formed command */
+    F4_UnlockNotLocked,		  /* (NYI) already unlocked */
+    F4_NotLockOwner,		  /* (NYI) not server lock owner */
+    F4_CurrentlyLocked,		  /* (NYI) server locked by another process */
+    F4_BadArgValue,		  /* (NYI) server got bad argument */
+    F4_NotInitialized,		  /* no answer from the instrument hardware */
+    F4_TrafficError,		  /* server had trouble with ocs */
+    F4_DuckCommandError,	  /* problem sending to duck */
+    F4_DuckResponseError,	  /* problem with duck response */
+
+    cfht_errno_codes_filler	  /* ending dummy entry with no comma */
+} cfht_errno_CodesType;
+
+/*************************
+ * structure definitions *
+ *************************/
+
+struct cfht_rng_double {
+    double min;
+    double max;
+    BOOLEAN min_inclusive;
+    BOOLEAN max_inclusive;
+};
+struct cfht_rng_int {
+    int min;
+    int max;
+    BOOLEAN min_inclusive;
+    BOOLEAN max_inclusive;
+};
+struct cfht_rng_string {
+    char **string_array;
+};
+struct cfht_rng_char {
+    char *list_of_chars;
+};
+union cfht_ranges {
+    struct cfht_rng_double cfht_double_range;
+    struct cfht_rng_int cfht_int_range;
+    struct cfht_rng_string cfht_string_range;
+    struct cfht_rng_char cfht_char_range;
+};
+
+typedef union cfht_ranges *cfht_range_t;
+
+/*********************
+ * global references *
+ *********************/
+
+extern BOOLEAN cfht_debug;
+extern BOOLEAN cfht_error;
+extern BOOLEAN cfht_lib;
+extern BOOLEAN cfht_nodisp;
+extern int cfht_errno;
+
+extern union cfht_ranges *CFHT_RNG_RA;
+extern union cfht_ranges *CFHT_RNG_DEC;
+
+#define cfht_resetetimer() cfht_etimer(TRUE)
+#define cfht_delay(millisec) (void)cfht_sleep((millisec),1)
+
+/*
+ * Macro to put around system calls that might be interrupted by a signal.
+ * This is ripped from glibc.  It will keep re-trying systems calls until
+ * at least a partial success, or fatal error is encountered.  Note that
+ * read might still return FEWER BYTES than you told it to read if it is
+ * interrupted!
+ */
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(expression)               \
+({ long int __result;                                \
+     do __result = (long int) (expression);          \
+     while (__result == -1L && errno == EINTR);      \
+     __result; })
+#endif
+
+/*
+ * Special values that can be passed to cfht_signal()'s arg2
+ */
+
+#include <signal.h> /* for SIG_DFL and SIG_IGN */
+
+#define CFHT_SIG_TRAP ((PFV)cfht_signal_handler)
+  /* %%% 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))
+/*
+#define CFHT_SIG_DFL  ((PFV)SIG_DFL)
+#define CFHT_SIG_IGN  ((PFV)SIG_IGN)
+*/
+
+/*********************************************
+ * complete (?) list of all cfht_* functions *
+ *********************************************/
+
+char    *cfht_argsToString P((int argc, char *argv[]));
+void	cfht_awake P((double when_from_now, double how_often));
+char	*cfht_basename P((char *dest, char *filename, char *suffix));
+char	*cfht_cfhthome P((void));
+char	*cfht_date P((long *clockp));
+char	*cfht_PFdate P((int  days));
+char	*cfht_UTdate P((long *clockp));
+char	*cfht_UTtime P((long *clockp));
+char	*cfht_doupper P((char *str));
+double	cfht_dtime P((void));
+char	*cfht_fre P((unsigned char *src, unsigned char *dest, int *dsize));
+char	*cfht_toe P((unsigned char *src, unsigned char *dest, int ssize));
+double	cfht_etimer P((BOOLEAN reset));
+void	cfht_signal P((int signo, PFV handler));
+void	cfht_signal_block P((int signo));
+void	cfht_signal_unblock P((int signo));
+void    cfht_signal_handler P((int));
+BOOLEAN	cfht_signal_event P((int signo));
+BOOLEAN	cfht_signal_peek P((int signo));
+int	cfht_exec P((FILE_ID id, char *string, int type, volatile void *addr));
+int	cfht_system P((char *string));
+char	*cfht_frs P((char *dest, double val, int scale, int prec));
+void	cfht_genh P((int argc, char **argv, char **envp));
+void	usage P((char **msg));
+void	getcurdeath P((char *what));
+double	cfht_julian P((int year, int month, int day, int hour, int minute, double sec));
+void    cfht_fromjulian P((double jd, int *year, int *month, int *day, int *hour, int *minute, double *sec));
+char	*cfht_Mjulian P((long *clockp));
+char	*cfht_LocalSidTime P((long *clockp));
+BOOLEAN	cfht_checksem P((int semid));
+int	cfht_initsem P((char *path, char id));
+PASSFAIL cfht_setsem P((int semid));
+PASSFAIL cfht_setsemwait P((int semid, BOOLEAN sigign));
+PASSFAIL cfht_relsem P((int semid));
+PASSFAIL cfht_waitsemrel P((int semid, BOOLEAN sigign));
+PASSFAIL cfht_waitsemset P((int semid, BOOLEAN sigign));
+BOOLEAN cfht_checklock P((int fd));
+int	cfht_initlock P((char *path, char id));
+PASSFAIL cfht_setlock P((int fd));
+PASSFAIL cfht_setlockwait P((int fd, BOOLEAN sigign));
+PASSFAIL cfht_rellock P((int fd));
+PASSFAIL cfht_waitlockrel P((int fd, BOOLEAN sigign));
+PASSFAIL cfht_waitlockset P((int fd, BOOLEAN sigign));
+void	cfht_logv P((int who, int type, char *fmt, ...));
+void	cfht_logpv P((int who, int type, char *fmt, ...));
+void	cfht_log P((int who, int type, char *mess));
+void	cfht_log_proxy P((int who, int type, char *mess, char* id, int pid));
+PASSFAIL cfht_int P((char *in_str, int *out_addr, cfht_range_t rangeinfo));
+PASSFAIL cfht_double P((char *in_str, double *out_addr, cfht_range_t rangeinfo));
+long	cfht_od P((void));
+int	cfht_sleep P((int msec, int igsig));
+char	*cfht_string P((char *in_str, int operation, cfht_range_t rangeinfo));
+char	*cfht_time P((long *clockp));
+BOOLEAN	cfht_tob P((char *string));
+char	*cfht_tok P((char *string, char *sep, char **tail));
+void	cfht_goDaemon P((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * See comments in missing_protos.h
+ */
+#include <missing_protos.h>
+
+#endif /* CFHTDOTH */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/include/cfht/exits.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/include/cfht/exits.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/include/cfht/exits.h	(revision 22322)
@@ -0,0 +1,61 @@
+/* Copyright (C) 1991, 94, 96    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    */
+/*****************************************************************************
+ *
+ * file: exits.h
+ * $Header: /cvsroot/pan-starrs/datasys/IPP/Ohana/src/misc/include/cfht/exits.h,v 1.1.1.1 2004-11-24 04:39:33 eugene Exp $
+ * $Locker:  $
+ *
+ * This file defines the various exit codes for controllers and handlers.
+ *
+ * NOTE: please see ui/listexits.c also when you change this !!!!!!!!!
+ *       The values of these exit codes are available symbolically in
+ *       shell scripts by using ". setexits".
+ *
+ * HISTORY
+ *
+ * who       when           what
+ * ------    -----------    ----------------------------------------------
+ * jk        24 Mar 1988    Original
+ *
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  2001/07/25 02:59:24  eugene
+ * import Ohana
+ *
+ * Revision 1.4  96/08/12  15:30:00  15:30:00  thomas (Jim Thomas)
+ * Added AOB exit codes and some comments
+ * 
+ * Revision 1.3  94/12/14  17:27:07  17:27:07  thomas (Jim Thomas)
+ * Added pinger exit codes
+ * 
+ * Revision 1.2  93/06/26  17:59:06  17:59:06  steve (Steven Smith)
+ * added define for environment var to pass exit code of data taking handlers
+ * (ccdh, etc) for passing to all interested -E handlers.
+ * 
+ * Revision 1.1  91/09/24  17:38:54  17:38:54  steve (Steven Smith)
+ * Original checkin
+ * 
+ *****************************************************************************/
+
+#define EXIT_ENV_NAME	"CFHT_EXPOSURE_EXIT"
+#define EXIT_PASS 0		  /* everything is OK */
+#define EXIT_FAIL 1		  /* there was a real problem */
+#define EXIT_EXP_IN_PROGRESS 2	  /* can't do request - exposure already on */
+#define EXIT_EXP_ABORTED 3	  /* exposure trashed by user */
+#define EXIT_EXP_STOPPED 4	  /* exposure stopped by user */
+#define EXIT_CANCEL 5		  /* they hit the cancel button */
+
+/* pinger exit codes */
+#define PNG_EXIT_PASS EXIT_PASS	  /* service is healthy */
+#define PNG_EXIT_BUSY 1		  /* service is busy */
+#define PNG_EXIT_FAKE 2		  /* service is there but in FAKE mode */
+#define PNG_EXIT_NOTTHERE 3	  /* service isn't there at all */
+#define PNG_EXIT_NOTRAFFIC 4	  /* traffic isn't there */
+#define PNG_EXIT_ERROR 5	  /* some program or argument error happened */
+#define PNG_EXIT_HELP 6		  /* typed out the help message */
+
+/* AOB exit codes */
+#define AOB_EXIT_PASS EXIT_PASS	  /* things OK */
+#define AOB_EXIT_FAIL EXIT_FAIL	  /* real problem */
+#define AOB_EXIT_SEPARATION 2	  /* guide and object too far apart */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/include/missing_protos.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/include/missing_protos.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/include/missing_protos.h	(revision 22322)
@@ -0,0 +1,307 @@
+/* Copyright (C) 1997-1999  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.2 2007-04-18 19:21:46 eugene Exp $
+ * $Locker:  $
+ *
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  2004/11/24 04:39:33  eugene
+ * importing elixir components
+ *
+ * Revision 1.1.1.1  2001/07/25 02:59:23  eugene
+ * import Ohana
+ *
+ * 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
+error: Unknown system type!
+error: This software has not been tested on this platform.
+error: (comment out this block to compile anyway).
+#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/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
+/* XXX RHL claims values.h is not needed
+ * #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     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     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	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))
+#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: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/SAOobjects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/SAOobjects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/SAOobjects.c	(revision 22322)
@@ -0,0 +1,87 @@
+# include "ohana.h"
+# include "dvo.h"
+
+main (int argc, char **argv) {
+
+  FILE *f;
+  char line[256], name[256];
+  Header header;
+  Coords coords;
+  double R, D, dR, dD;
+  double X, Y, dX, dY;
+  double angle;
+  int Nfield;
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: %s (image.fits) (input)\n", argv[0]);
+    exit (1);
+  }
+
+  if (!gfits_read_header (argv[1], &header)) {
+    fprintf (stderr, "can't read header from file %s\n", argv[1]);
+    exit (1);
+  }
+
+  if (!GetCoords (&coords, &header)) {
+    fprintf (stderr, "can't find appropriate astrometry in image header\n");
+    exit (1);
+  }
+
+  /* load in the object description list */
+
+  /* fields for each line should be:
+     name (string)
+     Ra (dec. degrees)
+     Dec (dec. degrees)
+     dRa (arcsec)
+     dDec (arcsec)
+     angle (degrees)
+  */
+
+  if (argv[2][0] == '-') {
+    f = stdin;
+  } else {
+    f = fopen (argv[2], "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "can't open input data file\n");
+      exit (1);
+    }
+  }
+
+  while (scan_line (f, line) != EOF) {
+    Nfield = sscanf (line, "%s %lf %lf %lf %lf %lf", name, &R, &D, &dR, &dD, &angle);
+    
+    if (Nfield < 4) {
+      fprintf (stderr, "*");
+      continue;
+    }
+    
+    RD_to_XY (&X, &Y, R, D, &coords);
+
+    /* we won't worry about the case of cdelt1 != cdelt2,
+       in which case we don't have to worry about rotation 
+       between RA,DEC and X,Y for the calculation of the
+       slit lenghts.  Note that this assumption does not 
+       cause errors with the varying projected slitlength in
+       RA units, that is taken care of by the projection.
+       But, we are not taking into account a rotated slit */
+
+    dX = dR / 3600.0 / coords.cdelt1;
+    dY = dD / 3600.0 / coords.cdelt2;
+
+    switch (Nfield) {
+    case 4:
+      fprintf (stdout, "%s(%f,%f,%f)\n", name, X, Y, dX);
+      break;
+    case 5:
+      fprintf (stdout, "%s(%f,%f,%f,%f)\n", name, X, Y, dX, dY);
+      break;
+    case 6:
+      fprintf (stdout, "%s(%f,%f,%f,%f,%f)\n", name, X, Y, dX, dY, angle);
+      break;
+    }
+  }
+
+  if (f != stdin) fclose (f);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/addastro.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/addastro.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/addastro.c	(revision 22322)
@@ -0,0 +1,105 @@
+# include <ohana.h>
+# define BLOCK 0x1000
+
+main (int argc, char **argv) {
+
+  /* USAGE: addastro (cmp) (ref) */
+
+  int i, j, nbytes, itmp, oldsize;
+  double tmp;
+  Header header, refhead;
+  FILE *f, *g;
+  char filename[1024];
+  char buffer[0x1000];
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: addastro (cmp) (ref)\n");
+    exit (1);
+  }
+
+  if (!gfits_read_header (argv[1], &header)) {
+    fprintf (stderr, "ERROR: can't read header for %s\n", argv[1]);
+    exit (1);
+  }
+  oldsize = header.size;
+
+  if (!gfits_read_header (argv[2], &refhead)) {
+    fprintf (stderr, "ERROR: can't read header for %s\n", argv[2]);
+    exit (1);
+  }
+
+  gfits_scan   (&refhead, "CDELT1",   "%lf", 1, &tmp);
+  gfits_modify (&header,  "CDELT1",   "%le", 1, tmp);
+  gfits_scan   (&refhead, "CDELT2",   "%lf", 1, &tmp);
+  gfits_modify (&header,  "CDELT2",   "%le", 1, tmp);
+  gfits_scan   (&refhead, "CRVAL1",   "%lf", 1, &tmp);
+  gfits_modify (&header,  "CRVAL1",   "%lf", 1, tmp);
+  gfits_scan   (&refhead, "CRVAL2",   "%lf", 1, &tmp);
+  gfits_modify (&header,  "CRVAL2",   "%lf", 1, tmp);
+  gfits_scan   (&refhead, "CRPIX1",   "%lf", 1, &tmp);
+  gfits_modify (&header,  "CRPIX1",   "%lf", 1, tmp);
+  gfits_scan   (&refhead, "CRPIX2",   "%lf", 1, &tmp);
+  gfits_modify (&header,  "CRPIX2",   "%lf", 1, tmp);
+  gfits_scan   (&refhead, "PC001001", "%lf", 1, &tmp);
+  gfits_modify (&header,  "PC001001", "%le", 1, tmp);
+  gfits_scan   (&refhead, "PC001002", "%lf", 1, &tmp);
+  gfits_modify (&header,  "PC001002", "%le", 1, tmp);
+  gfits_scan   (&refhead, "PC002001", "%lf", 1, &tmp);
+  gfits_modify (&header,  "PC002001", "%le", 1, tmp);
+  gfits_scan   (&refhead, "PC002002", "%lf", 1, &tmp);
+  gfits_modify (&header,  "PC002002", "%le", 1, tmp);
+  gfits_scan   (&refhead, "NPLYTERM", "%d",  1, &itmp);
+  gfits_modify (&header,  "NPLYTERM", "%d",  1, itmp);
+  gfits_scan   (&refhead, "NASTRO",   "%d",  1, &itmp);
+  gfits_modify (&header,  "NASTRO",   "%d",  1, itmp);
+  gfits_scan   (&refhead, "CERROR",   "%lf", 1, &tmp);
+  gfits_modify (&header,  "CERROR",   "%lf", 1, tmp);
+  gfits_scan   (&refhead, "CPRECISE", "%lf", 1, &tmp);
+  gfits_modify (&header,  "CPRECISE", "%lf", 1, tmp);
+
+  gfits_modify (&header, "CERROR", "%C", 1, "scatter in astrometry soln (arcsec)");
+  gfits_modify (&header, "CPRECISE", "%C", 1, "precision of astrometry soln (arcsec)");
+
+  /* write to file */
+  sprintf (filename, "%s.tmp", argv[1]);
+  if (!gfits_write_header (filename, &header)) {
+    fprintf (stderr, "ERROR: can't write header for %s\n", filename);
+    exit (1);
+  }
+  g = fopen (filename, "w");
+  if (g == NULL) {
+    fprintf (stderr, "ERROR: can't read write to %s\n", filename);
+    exit (1);
+  }
+  nbytes = fwrite (header.buffer, 1, header.size, g);
+  fseek (g, header.size, SEEK_SET); 
+
+  /* read date from file */
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't read data from %s\n", argv[1]);
+    exit (1);
+  }
+  fseek (f, oldsize, SEEK_SET); 
+
+  while ((nbytes = fread (buffer, 1, BLOCK, f)) > 0) {
+    if (nbytes != fwrite (buffer, 1, nbytes, g)) {
+      fprintf (stderr, "ERROR: failure writing output data file\n");
+      exit (0);
+    }
+  }
+  
+  fclose (f);
+  fclose (g);
+
+  sprintf (buffer, "mv %s %s~\n", argv[1], argv[1]);
+  system (buffer);
+
+  sprintf (buffer, "mv %s %s\n", filename, argv[1]);
+  system (buffer);
+
+  fprintf (stdout, "SUCCESS\n");
+
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/applyscat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/applyscat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/applyscat.c	(revision 22322)
@@ -0,0 +1,128 @@
+# include <ohana.h>
+# include <dvo.h>
+# define BYTES_STAR 66
+
+int main (int argc, char **argv) {
+
+  Header header;
+  Matrix matrix;
+  Stars *stars;
+  int Nstar, Nbytes, nbytes;
+  FILE *f;
+  char *buffer, line[10];
+  double dmag, ratio;
+  int i, x, y;
+
+  if (argc != 4) {
+    fprintf (stderr, "USAGE: applyscat (input) (scatter) (output)\n");
+    exit (1);
+  }
+
+  /* read smp header */
+  if (!gfits_read_header (argv[1], &header)) {
+    fprintf (stderr, "ERROR: can't find image file %s\n", argv[1]);
+    exit (1);
+  }
+
+  /* open file for star data */
+  f = fopen (argv[1], "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't read data from %s\n", argv[1]);
+    exit (1);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  /* find expected number of stars */
+  gfits_scan (&header, "NSTARS", "%d", 1, &Nstar);
+  if (Nstar == 0) {
+    fprintf (stderr, "ERROR: can't get NSTARS from header\n");
+    exit (1);
+  }
+
+  /* allocate work space */
+  Nbytes = Nstar*BYTES_STAR;
+  ALLOCATE (stars, Stars, Nstar);
+  ALLOCATE (buffer, char, Nbytes + 1);
+
+  /* load in data */
+  nbytes = fread (buffer, 1, Nbytes, f);
+  if (nbytes != Nbytes) {
+    fprintf (stderr, "ERROR: can't load in all stars\n");
+    exit (1);
+  }
+  buffer[Nbytes] = 0;
+  fclose (f);
+  
+  for (i = 0; i < Nstar; i++) {
+    dparse (&stars[i].X,  1, &buffer[i*BYTES_STAR]);
+    dparse (&stars[i].Y,  2, &buffer[i*BYTES_STAR]);
+    dparse (&stars[i].M,  3, &buffer[i*BYTES_STAR]);
+  }
+
+  if (!gfits_read_matrix (argv[2], &matrix)) {
+    fprintf (stderr, "ERROR: can't open scatter image file %s\n", argv[2]);
+    exit (1);
+  }
+  
+  /* flat' = flat*ratio
+     flux' = flux / ratio
+     mag'  = mag - dmag
+     dmag = -2.5*log10(ratio) 
+
+     ie, ratio = 1.1
+     flux' = 0.9*flux (flux' < flux)
+     dmag = -0.103
+     mag' = mag + 0.103 (mag' > mag)
+  */
+
+  for (i = 0; i < Nstar; i++) {
+    x = stars[i].X;
+    y = stars[i].Y;
+    ratio = gfits_get_matrix_value (&matrix, x, y);
+    dmag = -2.5*log10(ratio);
+    dmag = (dmag > +1.0) ? 0.0 : dmag;
+    dmag = (dmag < -1.0) ? 0.0 : dmag;
+    stars[i].M -= dmag;
+  }
+
+  /* full format line: "%6.1f %6.1f %6.3f %03d %2d %3.1f %6.3f %6.3f %6.2f %6.2f %5.1f", X, Y, M, dM, dophot, sky, Mgal, Map, fx, fy, df */
+  for (i = 0; i < Nstar; i++) {
+    sprintf (line, "%6.3f", stars[i].M);
+    memcpy (&buffer[i*BYTES_STAR+14], line, 6);
+  }
+
+  /* write smp header */
+  if (!gfits_write_header (argv[3], &header)) {
+    fprintf (stderr, "ERROR: can't write output file header %s\n", argv[3]);
+    exit (1);
+  }
+
+  /* open file for star data */
+  f = fopen (argv[3], "a");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't open output file %s\n", argv[3]);
+    exit (1);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  /* load in data */
+  nbytes = Fwrite (buffer, 1, Nbytes, f, "char");
+  if (nbytes != Nbytes) {
+    fprintf (stderr, "ERROR: problem writing stars\n");
+    exit (1);
+  }
+  fclose (f);
+
+  exit (0);
+}
+
+
+
+
+/* load in smp header
+   load in smp stars
+   load in scatter frame
+   apply corrections
+   write smp header
+   write smp stars
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/cfhtlog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/cfhtlog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/cfhtlog.c	(revision 22322)
@@ -0,0 +1,405 @@
+/****************************************************************************
+    Copyright (C) 1991, 1995    Canada-France-Hawaii Telescope Corp.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 1, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Contact information:
+    CFHT, PO Box 1597, Kamuela, HI 96743, USA
+    daprog@cfht.hawaii.edu
+
+****************************************************************************/
+/*!****************************************************************************
+ * FILE
+ *
+ * $Header: /cvsroot/pan-starrs/datasys/IPP/Ohana/src/misc/src/cfhtlog.c,v 1.1 2003-01-17 18:42:35 eugene Exp $
+ * $Locker:  $
+ *
+ * ROUTINES
+ *
+ *
+ * HISTORY
+ * who          when            what
+ * ---------    ------------    ----------------------------------------------
+ *
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  2001/07/25 02:59:22  eugene
+ * import Ohana
+ *
+ * Revision 1.4  2001/01/25 21:21:06  bernt
+ * nothing changed
+ *
+ * Revision 1.3  1999/07/06 14:14:07  bernt
+ * *** empty log message ***
+ *
+ * Revision 1.2  98/06/09  22:50:01  22:50:01  bernt (Bernt Grundseth)
+ * clean some unused vars
+ * 
+ * Revision 1.1  97/11/21  11:28:53  11:28:53  bernt (Bernt Grundseth)
+ * Initial revision
+ * 
+ * Revision 1.5  96/11/17  22:59:00  22:59:00  bernt (Bernt Grundseth)
+ * *** empty log message ***
+ * 
+ * Revision 1.4  96/05/29  15:27:03  15:27:03  bernt (Bernt Grundseth)
+ * removed reading from current.dat file, because the last data file is 
+ * copied to uwila_data every ten minutes instead of the temporary kludge
+ * with the current.dat file.
+ * 
+ * Revision 1.3  96/05/29  07:23:29  07:23:29  bernt (Bernt Grundseth)
+ * *** empty log message ***
+ * 
+ * Revision 1.2  96/02/20  21:58:38  21:58:38  bernt (Bernt Grundseth)
+ * basic operations working still need improvements, range checking
+ * 
+ * Revision 1.1  96/02/19  22:31:43  22:31:43  bernt (Bernt Grundseth)
+ * Initial revision
+ * 
+ * 
+ ***************************************************************************!*/
+
+#include <stdio.h>
+#include <string.h>		  /* for string functions */
+#include <stdlib.h>		  /* for strtoul */
+#ifdef sun
+#define strtoul strtol		  /*   but sun doesn't have one */
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>		  /* for time functions */
+#ifdef sun
+#define mktime timelocal	  /*   but sun has a different name */
+#endif
+#include <errno.h>		  /* for ERANGE */
+
+#include <cfht/cfht.h>		  /* for P macro */
+#include <cfht/exits.h>           /* for standard exit codes */
+
+
+void dp_usage P((char *s));       	  /* usage exit */
+PASSFAIL TimeStringToTime P((char *String, time_t *Time));
+double cfht_julian();
+void cfht_fromjulian();
+double atof();
+
+time_t TimeNow;			  /* current time */
+static char path[] = "/data/logger/";
+#define NUMITEMS 915
+
+int main(argc, argv)
+    int argc;
+    char *argv[];
+{
+#ifndef __LINT__
+    static char rcs_id[] =
+      "$Header: /cvsroot/pan-starrs/datasys/IPP/Ohana/src/misc/src/cfhtlog.c,v 1.1 2003-01-17 18:42:35 eugene Exp $";
+#endif
+    /* command line argument values */
+    char *OurName;		  /* name of executing program */
+    char *Input = (char *) NULL;  /* name of [pipe] to read */
+    char *SessionName = (char *) NULL; /* name of session to match */
+    char *FromString = (char *) NULL; /* time to start outputting */
+    char *ToString = (char *) NULL; /* time to stop outputting */
+    char *ProbeString = (char *) NULL; /* probes  */
+    BOOLEAN Verbose = FALSE;	  /* want lots of debug output? */
+    BOOLEAN GoBackwards = TRUE;	  /* want LIFO or FIFO?  TRUE = LIFO */
+    BOOLEAN Dosuid = FALSE;	  /* do suid?  TRUE = yes */
+    unsigned int HoursBack = 0;	  /* start output this far back */
+    time_t FromTime;		  /* from time in internal form */
+    time_t ToTime;		  /* to time in internal form */
+
+    int Probes[100], nprobes;
+    double JDFrom, JDTo;
+    struct tm *tmd;
+
+
+
+    int i;			  /* index into argv */
+    int nlen;			  /* temporary  */
+
+    
+    OurName = cfht_basename((char *) NULL, argv[0], (char *) NULL);
+    if (argc < 2) {
+        dp_usage(argv[0]); /* else error */
+        exit(EXIT_FAIL);
+    }
+
+
+    time(&TimeNow);		  /* get current time */
+    tmd = localtime(&TimeNow);
+    printf("# DATE/TIME now %d %d %d %d %d %d\n",
+		   tmd->tm_year+1900, tmd->tm_mon+1, tmd->tm_mday,
+		   tmd->tm_hour, tmd->tm_min, tmd->tm_sec);
+    
+    for (i = 1 ; i < argc ; i++) {
+	if (argv[i][0] == '-') {
+	    switch (argv[i][1]) {
+
+	      case 'f':
+		FromString = argv[++i]; /* get from time from command line */
+		printf("# from date %s \n",FromString);
+                if (get_JDdate(FromString, &JDFrom)) {
+		    printf("# malformed date: %s \n",FromString);
+                    exit(EXIT_FAIL);
+                }
+		printf("#  julian day = %lf\n", JDFrom);
+		break;
+
+	      case 't':
+		ToString = argv[++i]; /* get to time from command line */
+		printf("# to date %s \n",ToString);
+                if (get_JDdate(ToString, &JDTo)) {
+		    printf("# malformed date: %s \n",ToString);
+                    exit(EXIT_FAIL);
+                }
+		printf("#  julian day = %lf\n", JDTo);
+		break;
+
+	      case 'p':
+		ProbeString = argv[++i]; /* get probes from command line */
+		break;
+
+	      case 'h':
+		/* one hour */
+
+                JDTo = cfht_julian(tmd->tm_year + 1900, tmd->tm_mon + 1,
+		  		  tmd->tm_mday, tmd->tm_hour,
+				  tmd->tm_min, (double)tmd->tm_sec);
+                JDTo = JDTo + 10./24.;
+                JDFrom = JDTo - 1.0/24.0;
+		printf("# INTERVAL: hour\n");
+		break;
+
+	      case 'd':
+		/* one day */
+
+                JDTo = cfht_julian(tmd->tm_year + 1900, tmd->tm_mon + 1,
+		  		  tmd->tm_mday, tmd->tm_hour,
+				  tmd->tm_min, (double)tmd->tm_sec);
+                JDTo = JDTo + 10./24.;
+                JDFrom = JDTo - 1.0;
+		printf("# INTERVAL: day\n");
+		break;
+
+	      case 'w':
+		/* one week */
+
+                JDTo = cfht_julian(tmd->tm_year + 1900, tmd->tm_mon + 1,
+		  		  tmd->tm_mday, tmd->tm_hour,
+				  tmd->tm_min, (double)tmd->tm_sec);
+                JDTo = JDTo + 10./24.;
+                JDFrom = JDTo - 7.0;
+		printf("# INTERVAL: week\n");
+		break;
+
+	      case 'm':
+		/* one month */
+
+                JDTo = cfht_julian(tmd->tm_year + 1900, tmd->tm_mon + 1,
+		  		  tmd->tm_mday, tmd->tm_hour,
+				  tmd->tm_min, (double)tmd->tm_sec);
+                JDTo = JDTo + 10./24.;
+                JDFrom = JDTo - 30.0;
+		printf("# INTERVAL: month\n");
+		break;
+
+	      default:
+		dp_usage(argv[0]); /* else error */
+	    }
+	} 
+
+    }
+
+    /* parse the probes numbers requested, and total # of probes */
+    if (ProbeString == NULL) { printf("# no probes selected\n");
+        exit(EXIT_FAIL);
+	}
+
+    get_tokens(ProbeString, " ,",Probes, &nprobes);
+    printf("# PROBES: %d ", nprobes); 
+    for (i = 0; i < nprobes; i++) {
+	if (Probes[i] < 0 || Probes[i] > 99 ) {
+	    printf("# error probe # out of range\n");
+            exit(EXIT_FAIL);
+        }
+    printf(" %d ", Probes[i]); 
+    }
+    printf("\n"); 
+    get_data(JDFrom, JDTo, Probes, nprobes); 
+
+
+    exit(EXIT_PASS);
+    return(0);			  /* dummy */
+}
+
+PASSFAIL
+get_data(JDstart, JDend, probes, nprobes)
+double JDstart, JDend;
+int probes[], nprobes;
+{
+    FILE *infile;
+    double julind, jd, jd0;
+    int year, month, day, hour, min, dayno;
+    double sec;
+    char fullname[64];
+    char filename[32];
+
+    char string[512];
+    double timestamp;
+    char data[915];
+    char bufstr[12];
+    long line = 0;
+    int nchar,j;
+
+    /* make JDstart to be in the 10 minutes boundary */
+    cfht_fromjulian(JDstart, &year, &month, &day, &hour, &min, &sec);
+    min = min/10*10;
+    sec = 0.;
+    JDstart = cfht_julian(year, month , day, hour, min, sec);
+
+
+    /* At this point it is assumed that the dates are OK.  */
+    /* get get year and day number at start  */
+    for (julind = JDstart -1; julind <= JDend; julind=julind+1.) {
+	jd = julind -10./24.;
+        cfht_fromjulian(jd, &year, &month, &day, &hour, &min, &sec);
+        jd0 = cfht_julian(year, 1 , 1, 0, 0, 0.);
+	dayno = jd -jd0 + 1;
+	strcpy (fullname, path);
+	sprintf(filename, "%d/%d%.3d.dat",year,(year-1900),dayno);
+	strcat (fullname, filename);
+
+	infile = fopen(fullname, "r");
+	if (infile == NULL) {
+	    if ( (int)julind < (int)JDend ) { /* last one ??? */
+	        sprintf (string, "# unable to open input file '%s'", fullname);
+	        perror (string);
+	    }
+
+	} else {
+        /*********************
+         * process each line *
+         *********************/
+            while ((nchar = fread(data, sizeof(char), NUMITEMS, infile)) > 0 ) {
+	        timestamp = atof(data) + 0.00001;
+
+	        if ((timestamp >= JDstart) && (timestamp <= JDend)) {
+                    cfht_fromjulian((timestamp-10./24.), &year, &month, &day,
+				    &hour, &min, &sec);
+		    printf("%lf ", timestamp - JDstart);
+		    printf("%lf ", (timestamp - JDstart)*24.0);
+		    printf("%.2d/%.2d/%.2d %.2d:%.2d ",
+		           year, month,day,hour,min);
+		    for (j = 0; j < nprobes; j++) {
+		        strncpy(bufstr, data + (14 + probes[j]*9), 9);
+		        bufstr[9] =0;
+		        printf("%s ", bufstr);
+		    }
+		    printf("\n");
+                }
+            }
+	    fclose(infile);
+        }
+
+    }
+    return(PASS);
+}
+
+
+PASSFAIL
+get_JDdate(datestring, JD)
+char *datestring;
+double *JD;
+{
+    int vect[10];
+    int n;
+    int year, month, day, hour, min, sec;
+    get_tokens(datestring, " /:", vect, &n);
+    if (n < 3 ) {
+	return(FAIL);
+    }
+/*    year = vect[0] + 1900; */
+    year = vect[0];
+    if (year < 1993 || year > 2010 )
+	return(FAIL);
+    month = vect[1];
+    if (month < 1 || month > 12 )
+	return(FAIL);
+    day = vect[2];
+    if (day < 1 || day > 31 )
+	return(FAIL);
+    hour = 0;
+    min  = 0;
+    sec  = 0;
+    if ( n > 3 ) {
+       hour = vect[3];
+       if (hour < 0 || hour > 23 )
+           return(FAIL);
+    }
+    if ( n > 4 ) {
+       min = vect[4];
+       if (min < 0 || min > 59 )
+           return(FAIL);
+    }
+    if ( n > 5 ) {
+       sec = vect[5];
+       if (sec < 0 || sec > 59 )
+           return(FAIL);
+    }
+    *JD = cfht_julian(year, month, day, hour, min, (double)sec);
+    *JD = *JD + 10.0 / 24.0;
+    return(PASS);
+}
+
+PASSFAIL
+get_tokens(buf, sep, vect, nitems)
+char buf[];
+char *sep;
+int vect[];
+int *nitems;
+{
+    char *cp;
+    int i;
+
+    cp = strtok(buf, sep);
+    vect[0] = atoi(cp);
+
+
+    i = 1;
+    while( 1 ) {
+
+        if( (cp = strtok(0, sep) ) == 0 ) {
+        break;
+        }
+        vect[i] = atoi(cp);
+        i++;
+    }
+    *nitems = i;
+
+    return(PASS);
+}
+
+/* usage: prints default error message and then exits */
+/* s is program name */
+void dp_usage(s)
+    char *s;
+{
+    fprintf(stderr, "Usage: %s\n", s);
+    fprintf(stderr, " [-h ] [-d] [-w] [-m]\n");
+    fprintf(stderr, " 1 hour, 1 day, 1 week, 1 month  \n");
+    fprintf(stderr, " [-f timefrom] [-t timeto] -p probeNumber\n");
+    fprintf(stderr, " Ex. -f \"2000/02/23 10:00:00\" -t \"2000/02/24\" -p 1,2,13,45\n");
+    exit(EXIT_FAIL);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/cubefilter.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/cubefilter.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/cubefilter.c	(revision 22322)
@@ -0,0 +1,110 @@
+# include <ohana.h>
+# define D_NFILES 100
+
+main (int argc, char **argv) {
+
+  int i, j, k, n, Npixels, Npixin, Npixrd, Npixlast, N;
+  int Npass, NFILES, Nfiles;
+
+  char **filename;
+  Header head, *tmphead;
+  Matrix out,  *tmpmatr;
+  float *list, *v, *O;
+  float fmin, fmax, Nval, sum;
+
+  if (N = get_argument (argc, argv, "-unsign")) {
+    remove_argument (N, &argc, argv);
+    gfits_set_unsign_mode (TRUE);
+  }
+
+  if (argc != 5) {
+    fprintf (stderr, "USAGE: minmaxfilter infile outfile fmin fmax\n");
+    exit (0);
+  }
+  fmin = atof (argv[2]); 
+  fmax = atof (argv[3]); 
+
+  /* read in the filenames */
+  NFILES = D_NFILES;
+  ALLOCATE (filename, char *, NFILES);
+  ALLOCATE (filename[0], char, 1024);
+  for (i = 0; fscanf (stdin, "%s", filename[i]) != EOF; i++) {
+    if (i == NFILES - 1) {
+      NFILES += D_NFILES;
+      REALLOCATE (filename, char *, NFILES);
+    }
+    ALLOCATE (filename[i+1], char, 1024);
+  }
+  Nfiles = i;
+  REALLOCATE (filename, char *, Nfiles);
+
+  /* load a header, setup output header, matrix */
+  gfits_read_header (filename[0], &head);
+  gfits_create_matrix (&head, &out);
+  gfits_convert_format (&head, &out, -32, 1.0, 0.0, FALSE);
+
+  /* find size of temporary images */
+  Npixels = head.Naxis[0]*head.Naxis[1];
+  Npixin = Npixels / Nfiles;
+  Npixlast = Npixels - Npixin*Nfiles;
+  if (Npixlast == 0) {
+    Npass = Nfiles;
+    Npixlast = Npixin;
+    fprintf (stderr, "operating on %d pixels per pass\n", Npixin);
+  } else {
+    Npass = Nfiles + 1;
+    fprintf (stderr, "operating on %d pixels per pass, %d in last\n", Npixin, Npixlast);
+  }
+  
+  ALLOCATE (tmphead, Header, Nfiles);
+  ALLOCATE (tmpmatr, Matrix, Nfiles);
+  ALLOCATE (list, float, Nfiles);
+  Nval = 0;
+  for (k = fmin*Nfiles; k < fmax*Nfiles; k++) {
+    Nval += 1.0;
+  }
+
+  O = (float *) out.buffer;
+  for (n = 0; n < Npass; n++) {
+    fprintf (stderr, "pass %d\n", n);
+    Npixrd = (n == Npass - 1) ? Npixlast : Npixin;
+    for (i = 0; i < Nfiles; i++) {
+      fprintf (stderr, ".");
+      if (!gfits_read_header  (filename[i], &tmphead[i])) {
+	fprintf (stderr, "trouble reading file %s\n", filename[i]);
+	exit (1);
+      }
+      gfits_read_portion (filename[i], &tmpmatr[i], n*Npixin, Npixrd);
+      tmphead[i].Naxis[0] = 1;
+      tmphead[i].Naxis[1] = Npixrd;
+      gfits_convert_format (&tmphead[i], &tmpmatr[i], -32, 1.0, 0.0, FALSE);
+    }
+    
+    fprintf (stderr, "starting sorts\n");
+    for (j = 0; j < Npixrd; j++, O++) {
+      for (k = 0; k < Nfiles; k++) {
+	v = (float *)tmpmatr[k].buffer;
+	list[k] = v[j];
+      }
+      fsort (list, Nfiles);
+      sum = 0;
+      for (k = fmin*Nfiles; k < fmax*Nfiles; k++) {
+	sum += list[k];
+      }
+      *O = (sum / Nval);
+    }
+
+    for (i = 0; i < Nfiles; i++) {
+      fprintf (stderr, ",");
+      gfits_free_header (&tmphead[i]);
+      gfits_free_matrix (&tmpmatr[i]);
+    }
+    
+  }
+
+  gfits_write_header (argv[1], &head);
+  gfits_write_matrix (argv[1], &out);
+
+  return (0);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/dastro.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/dastro.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/dastro.c	(revision 22322)
@@ -0,0 +1,112 @@
+# include <ohana.h>
+
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  
+  int status, REL, N;
+  Header header, refhead;
+  char  filename[1024], reffile[1024], *p;
+  double X_O, X_X, X_Y, Y_O, Y_X, Y_Y;
+  double RA_O, RA_X, RA_Y, DEC_O, DEC_X, DEC_Y;
+  double ra_o, ra_x, ra_y, dec_o, dec_x, dec_y;
+  double dRA, dDEC, dX, dY, dra, ddec;
+  
+  if (N = get_argument (argc, argv, "-rel")) {
+    remove_argument (N, &argc, argv);
+    REL = TRUE;
+  }
+  else {
+    REL = FALSE;
+  }
+  
+  while (fscanf (stdin, "%s", filename) != EOF) {
+    
+    status = gfits_read_header (filename, &header);
+    if (!status) {
+      fprintf (stderr, "error opening file %s\n", filename);
+      continue;
+    }
+    status = gfits_scan (&header, "rREF", "%s", 1, reffile);
+    status &= gfits_scan (&header, "X_O", "%lf", 1, &X_O);
+    status &= gfits_scan (&header, "X_X", "%lf", 1, &X_X);
+    status &= gfits_scan (&header, "X_Y", "%lf", 1, &X_Y);
+    status &= gfits_scan (&header, "Y_O", "%lf", 1, &Y_O);
+    status &= gfits_scan (&header, "Y_X", "%lf", 1, &Y_X);
+    status &= gfits_scan (&header, "Y_Y", "%lf", 1, &Y_Y);
+    status &= gfits_scan (&header, "dX", "%lf", 1, &dX);
+    status &= gfits_scan (&header, "dY", "%lf", 1, &dY);
+    if (!status) {
+      fprintf (stderr, "file missing rastro info: %s\n", filename);
+      gfits_free_header (&header);
+      continue;
+    }
+
+    while ((p = strstr (reffile, "'")) != NULL) {
+      *p = ' ';
+    }
+    stripwhite (reffile);
+    if ((p = strrchr(reffile, '.')) != NULL)
+      strcpy(p, ".head");
+    else 
+      strcat(reffile, ".head");
+
+    if (!strcmp (reffile, filename)) {
+      fprintf (stderr, "%s is %s, not altering\n", reffile, filename);
+      continue;
+    }
+
+    status  = gfits_read_header (reffile, &refhead);
+    if (!status) {
+      fprintf (stderr, "error opening file %s\n", reffile);
+      continue;
+    }
+    status &= gfits_scan (&refhead, "RA_O", "%lf", 1, &RA_O);
+    status &= gfits_scan (&refhead, "RA_X", "%lf", 1, &RA_X);
+    status &= gfits_scan (&refhead, "RA_Y", "%lf", 1, &RA_Y);
+    status &= gfits_scan (&refhead, "DEC_O", "%lf", 1, &DEC_O);
+    status &= gfits_scan (&refhead, "DEC_X", "%lf", 1, &DEC_X);
+    status &= gfits_scan (&refhead, "DEC_Y", "%lf", 1, &DEC_Y);
+    status &= gfits_scan (&refhead, "dRA",   "%lf", 1, &dRA);
+    status &= gfits_scan (&refhead, "dDEC",  "%lf", 1, &dDEC);
+    if (!status) {
+      fprintf (stderr, "file missing rastro info: %s\n", reffile);
+      gfits_free_header (&header);
+      gfits_free_header (&refhead);
+      continue;
+    }
+
+    fprintf (stderr, "%f %f %f\n%f %f %f\n", RA_O, RA_X, RA_Y, DEC_O, DEC_X, DEC_Y);
+    fprintf (stderr, "\n%f %f %f\n%f %f %f\n", X_O, X_X, X_Y, Y_O, Y_X, Y_Y);
+    
+    ra_o = RA_X*X_O + RA_Y*Y_O + RA_O;
+    dec_o = DEC_X*X_O + DEC_Y*Y_O + DEC_O;
+    
+    ra_x = RA_X*X_X + RA_Y*Y_X;
+    ra_y = RA_X*X_Y + RA_Y*Y_Y;
+    dec_x = DEC_X*X_X + DEC_Y*Y_X;
+    dec_y = DEC_X*X_Y + DEC_Y*Y_Y;
+    
+    fprintf (stderr, "%f %f %f\n%f %f %f\n", ra_o, ra_x, ra_y, dec_o, dec_x, dec_y);
+
+    dra = sqrt(dRA*dRA + SQ(3600*dX*RA_X) + SQ(3600*dY*RA_Y));
+    ddec = sqrt(dDEC*dDEC + SQ(3600*dX*DEC_X) + SQ(3600*dY*DEC_Y));
+			   
+    status &= gfits_modify (&header, "RA_O", "%lf", 1, ra_o);
+    status &= gfits_modify (&header, "RA_X", "%le", 1, ra_x);
+    status &= gfits_modify (&header, "RA_Y", "%le", 1, ra_y);
+    status &= gfits_modify (&header, "DEC_O", "%lf", 1, dec_o);
+    status &= gfits_modify (&header, "DEC_X", "%le", 1, dec_x);
+    status &= gfits_modify (&header, "DEC_Y", "%le", 1, dec_y);
+    status &= gfits_modify (&header, "dRA",   "%lf", 1, dra);
+    status &= gfits_modify (&header, "dDEC",  "%lf", 1, ddec);
+    
+    gfits_write_header (filename, &header);
+    gfits_free_header (&header);
+    gfits_free_header (&refhead);
+    
+  }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/dbtest.c.failed
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/dbtest.c.failed	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/dbtest.c.failed	(revision 22322)
@@ -0,0 +1,106 @@
+# include <ohana.h>
+
+static char dbfile[] = "/h/eugene/d4/flattest/test.db";
+
+void main (int argc, char **argv) {
+
+  char buffer[10000];
+  int vbuffer[10000];
+  FILE *f;
+  int i, j, Nrun, status, fd;
+
+  if (argc > 1) {
+    if (!strcmp (argv[1], "-check")) {
+      f = fopen (dbfile, "r");
+      if (f == (FILE *) NULL) {
+	fprintf (stderr, "ERROR: can't open file\n");
+	exit (0);
+      }
+      status = fread (buffer, 1, 5000, f);
+      if (status != 5000) {
+	fprintf (stderr, "ERROR: can't read header\n");
+	exit (0);
+      }
+      sscanf (&buffer[500], "%*s %*s %*s %d", &Nrun);
+      for (j = 0 ; j < Nrun; j++) {
+	status = fread (vbuffer, 4, 1000, f);
+	if (status != 1000) {
+	  fprintf (stderr, "ERROR: can't read data\n");
+	  exit (0);
+	}
+	for (i = 0; i < 1000; i++) {
+	  if (vbuffer[i] != j) {
+	    fprintf (stderr, "ERROR: invalid data %d/%d,  %d - %d\n", j, Nrun, i, vbuffer[i]);
+	    exit (0);
+	  }
+	}
+      }
+      fprintf (stderr, "Data is good\n");
+      exit (0);
+    }
+  }	  
+
+  /* read the header block, change one line, write the header block */
+  /* write a block to EOF */
+
+  /* if lockfile exists, program will complain and quit */
+  if ((fd = setlockfile (dbfile, 60.0)) == -1) {
+    fprintf (stderr, "ERROR: can't set lock on %s\n", dbfile);
+    exit (1);
+  }
+
+  f = fopen (dbfile, "r");
+  if (f == (FILE *) NULL) {
+    /* make a header block */
+    for (i = 0; i < 50; i++) {
+      memset (&buffer[100*i], 65+i, 99);
+      buffer[100*i + 99] = '\n';
+    }
+    sprintf (&buffer[500], "THIS IS TEST    0   \n");
+    Nrun = 0;
+  } else {
+    status = fread (buffer, 1, 5000, f);
+    if (status != 5000) {
+      fprintf (stderr, "ERROR: can't read data\n");
+      exit (0);
+    }
+    
+    sscanf (&buffer[500], "%*s %*s %*s %d", &Nrun);
+    Nrun ++;
+    sprintf (&buffer[500], "THIS IS TEST    %d   \n", Nrun);
+  }
+
+  f = fopen (dbfile, "r+");
+  if (f == NULL) {
+    f = fopen (dbfile, "w");
+    if (f == NULL) {
+      fprintf (stderr, "ERROR: can't create/open file %s\n", dbfile);
+      exit (0);
+    }
+  }
+
+  /* position to begining of file to write header */
+  fseek (f, 0, SEEK_SET);
+  status = fwrite (buffer, 1, 5000, f);
+  if (status != 5000) {
+    fprintf (stderr, "ERROR: failed writing data to header\n");
+    exit (0);
+  }
+
+  for (i = 0; i < 1000; i++) vbuffer[i] = Nrun;
+
+  fseek (f, 0, SEEK_END);
+  status = fwrite (vbuffer, 4, 1000, f);
+  if (status != 1000) {
+    fprintf (stderr, "ERROR: failed writing data to file\n");
+    exit (0);
+  }
+
+  if (!gnfsflush (dbfile)) {
+    fprintf (stderr, "ERROR: failed to flush image catalog\n");
+    exit (0);
+  }
+
+  clearlockfile (dbfile, fd);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/doscript.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/doscript.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/doscript.c	(revision 22322)
@@ -0,0 +1,179 @@
+# include <ohana.h>
+
+void args(), error();
+int COLDSTART, OUTIMAGE;
+double THRESHOLD;
+
+main (argc, argv) 
+     int argc;
+     char **argv;
+{
+  
+  FILE *f, *ft;
+  char name[128], root[128], line[256];
+  double psfx, psfy, sky, tilt;
+
+  args (argc, argv);
+
+  while (fscanf (stdin, "%s", root) != EOF) {
+    if (COLDSTART) {
+      fscanf (stdin, "%lf %lf", &psfx, &sky);
+    }
+    else {
+      fscanf (stdin, "%lf %lf %lf %lf", &psfx, &psfy, &tilt, &sky);
+    }
+
+    /* we demand that the image names have the *.f extension */
+    if (strncmp (&root[strlen(root) - 2], ".f", 2)) {
+      fprintf (stderr, "valid image names are *.f\n");
+      exit (0);
+    }
+
+    ft = fopen (root, "r");
+    if (ft == NULL) {
+      fprintf (stderr, "image %s does not exist\n", root);
+      continue;
+    }
+    fclose(ft);
+
+    /* root contains *.f, remove the .f portion: */
+    root[strlen(root) - 2] = 0;
+
+    strcpy (name, root);
+    strcat (name, ".par_in");
+    f = fopen (name, "w");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "couldn't write to parameter file %s\n", name);
+      continue;
+    }
+    fprintf (f, "FWHM = %f\n", psfx);
+    fprintf (f, "SKY = %f\n", sky);
+    if (!COLDSTART) {
+      fprintf (f, "AXIS_RATIO = %f\n", psfy/psfx);
+      fprintf (f, "TILT = %f\n", tilt);
+    }
+    fprintf (f, "=\n");
+    fprintf (f, "PARAMS_DEFAULT = 'dophot.params'\n");
+    fprintf (f, "PARAMS_OUT = '%s.par_out'\n", root);
+    if (THRESHOLD) {
+      fprintf (f, "AUTOTHRESH = 'NO'\n");     
+      fprintf (f, "THRESHMIN = %f\n", THRESHOLD);
+    }
+    else {
+      fprintf (f, "AUTOTHRESH = 'YES'\n");     
+    }
+    fprintf (f, "=\n");
+    fprintf (f, "IMAGE_IN = '%s.f'\n", root);
+    if (OUTIMAGE) 
+      fprintf (f, "IMAGE_OUT = '%s.s'\n", root);
+    else
+      fprintf (f, "IMAGE_OUT = ' '\n");
+    if (!COLDSTART) {
+      strcpy (name, root);
+      strcat (name, ".obj_out");
+      ft = fopen (name, "r");
+      if (ft != (FILE *) NULL) {
+	fclose (ft);
+	sprintf (line, "mv %s.obj_out %s.obj_in\0", root, root);
+	system (line);
+	fprintf (f, "OBJECTS_IN = '%s.obj_in'\n", root);
+      }
+      else {
+	fprintf (f, "OBJECTS_IN = ' '\n");
+      }
+    }
+    else {
+      fprintf (f, "OBJECTS_IN = ' '\n");
+    }
+    fprintf (f, "OBJECTS_OUT = '%s.obj_out'\n", root);
+    fprintf (f, "=\n");
+
+    if (!COLDSTART) {
+      strcpy (name, root);
+      strcat (name, ".shd_out");
+      ft = fopen (name, "r");
+      if (ft != (FILE *) NULL) {
+	fclose (ft);
+	sprintf (line, "mv %s.shd_out %s.shd_in\0", root, root);
+	system (line);
+	fprintf (f, "SHADOWFILE_IN = '%s.shd_in'\n", root);
+      }
+      else {
+	fprintf (f, "SHADOWFILE_IN = ' '\n");
+      }
+    }
+    else {
+      fprintf (f, "SHADOWFILE_IN = ' '\n");
+    }
+    fprintf (f, "SHADOWFILE_OUT = '%s.shd_out'\n", root);
+    
+    fprintf (f, "END\n");
+    fclose (f);
+
+    fprintf (stdout, "dophot << END > %s.dout\n", root);
+    fprintf (stdout, "%s.par_in\n", root);
+    fprintf (stdout, "END\n");
+  }
+}
+
+/* 
+  original image: image
+  flattened im:   image.f
+  star sub image: image.s
+  dp params in:   image.par_in
+  dp params out:  image.par_out
+  dp objects in:  image.obj_in
+  dp objects out: image.obj_out
+  dp shadow in:   image.shd_in
+  dp shadow out:  image.shd_out
+*/
+
+void args (argc, argv)
+     int      argc;
+     char   **argv;
+{
+  
+  int N;
+  
+  if (get_argument (argc, argv, "-h") || get_argument (argc, argv, "-help")) {
+    error ();
+  }
+
+  COLDSTART = TRUE;
+  if (N = get_argument (argc, argv, "-w")) {
+    remove_argument (N, &argc, argv);
+    COLDSTART = FALSE;
+  }
+  if (N = get_argument (argc, argv, "-warm")) {
+    remove_argument (N, &argc, argv);
+    COLDSTART = FALSE;
+  }
+  
+  OUTIMAGE = FALSE;
+  if (N = get_argument (argc, argv, "-sub")) {
+    remove_argument (N, &argc, argv);
+    OUTIMAGE = TRUE;
+  }
+
+  THRESHOLD = 0;
+  if (N = get_argument (argc, argv, "-t")) {
+    remove_argument (N, &argc, argv);
+    if (N < argc) {
+      THRESHOLD = atof (argv[N]);
+    } 
+    else {
+      error ();
+    }   
+  }
+}
+
+void error ()
+{
+  fprintf (stderr, "USAGE:\n");
+  fprintf (stderr, "  doscript [-w/-warm] [-sub] [-t threshold]\n");
+  fprintf (stderr, "    default is coldstart -- takes from stdin: filename FWHM sky\n");
+  fprintf (stderr, "    -w, -warm: warmstart -- takes from stdin: filename FWHMx FWHMy angle sky'\n");
+  fprintf (stderr, "    -t: set minimum threshold, also sets AUTOTHRESH = 'NO'\n");
+  fprintf (stderr, "    -sub: generate star-substracted images\n");
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/dostat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/dostat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/dostat.c	(revision 22322)
@@ -0,0 +1,84 @@
+# include <ohana.h>
+
+main() {
+
+  FILE *f, *fopen();
+  char file[50];
+  int N,n1,n2,n3,n4,n5,n6,n7,n8,nperf,type,status;
+  double Fx,Fy,alpha, FWHMx,FWHMy,angle, ap, F, S, sky;
+  double dtype, A, A2, Ap, Ap2, apmifit, df, S2;
+  char line[512];
+
+  while (fscanf (stdin, "%s", file) != EOF) {
+    f = fopen (file, "r");
+    if (f == NULL) {
+      fprintf (stderr, "file %s not found\n", file);
+      continue;
+    }
+    n1 = n2 = n3 = n4 = n5 = n6 = n7 = n8 = nperf = 0;
+    S2 = S = Fx = Fy = alpha = A = A2 = 0;
+    while (scan_line (f, line) != EOF) {
+      status = TRUE;
+      status &= dparse (&dtype, 2, line);
+      status &= dparse (&df,   6, line);
+      status &= dparse (&sky,   7, line);
+      status &= dparse (&FWHMx, 8, line);
+      status &= dparse (&FWHMy, 9, line);
+      status &= dparse (&angle, 10, line);
+      status &= dparse (&ap,      12, line);
+      status &= dparse (&apmifit, 15, line);
+      if (!status) {
+	fprintf (stderr, "error in file %s, line %d\n", file, n1+n2+n3+n4+n5+n6+n7+n8);
+	continue;
+      }
+      type = dtype;
+      switch (type) {
+      case 1:
+	n1 ++;
+	S += sky;
+	if (ap < 99) {
+	  nperf ++;
+	  A += apmifit/(df*df);
+	  A2 += apmifit*apmifit/(df*df);
+	  S2 += 1.0/(df*df);
+	}
+	Fx = FWHMx;
+	Fy = FWHMy;
+	alpha = angle;
+	break;
+      case 2:
+	n2 ++;
+	break;
+      case 3:
+	n3 ++;
+	break;
+      case 4:
+	n4 ++;
+	break;
+      case 5:
+	n5 ++;
+	break;
+      case 6:
+	n6 ++;
+	break;
+      case 7:
+	n7 ++;
+	break;
+      case 8:
+	n8 ++;
+	break;
+      }
+    }
+    S= S / (1.0*n1);
+    F = nperf / (1.0 * n1);
+    N = n1+n2+n3+n4+n5+n6+n7+n8;
+    Ap = A / S2;
+    Ap2 = sqrt(A2 / (S2) - Ap*Ap);
+    fprintf (stdout, "%s: %5d %5d %5d %5d %5d %5d %5d %5d",
+	     file,n1,n2,n3,n4,n5,n6,n7,n8);
+    fprintf (stdout, "  %5d %5d  %5.3f %7.2f %7.2f %7.1f %7.1f %7.3f %7.3f\n",
+	     N, nperf, F, Fx, Fy, alpha, S, Ap, Ap2);
+    fclose(f);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakecmp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakecmp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakecmp.c	(revision 22322)
@@ -0,0 +1,137 @@
+# include <ohana.h>
+static char reserved[] = "COMMENT  Reserved space.  This line can be used to add a new FITS card.";
+
+void main (int argc, char **argv) {
+
+  int i, j, N, status;
+  int Nreserved, start_size;
+  char *p, keyword[16], line[256];
+  FILE *f;
+  Header header;
+
+  if (argc != 4) {
+    fprintf (stderr, "USAGE: gfits_insert (input) (template) (output)\n");
+    exit (2);
+  }
+  
+  /*
+    input file consists of: RA DEC MAG dMAG
+    we generate an artificial image with 1 arcsec/pix
+    and the pixel (0,0) at (<RA>,<DEC>)
+    
+    1) find <RA>, <DEC>
+    2) convert X,Y to R,D using <RA>,<DEC> (ra---sin)
+    3) fix template entries: CRVAL1, CRVAL2, NSTARS, (JD)
+    4) write out template
+    5) write out infile in correct format:
+    X     Y      M      dM   T S   Ma     Mb        Fx     Fy  Th
+    237.2 2646.1 16.748 085  1 2.0 17.930 17.768    nan    nan -48.0
+  */
+
+  /* allocate data space */
+  NSTARS = 1000;
+  ALLOCATE (ra, double, NSTARS);
+  ALLOCATE (dec, double, NSTARS);
+  ALLOCATE (mag, double, NSTARS);
+  ALLOCATE (dmag, double, NSTARS);
+
+  /* open data file */
+  f = fopen (argv[1], "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open data file %s\n", argv[1]);
+    exit (1);
+  }
+
+  /* load data file into array */
+  for (i = 0;
+       fscanf (f, "%lf %lf %lf %lf", &ra[i], &dec[i], &mag[i], &dmag[i]) != EOF;
+       i++) {
+    
+    if (i == NSTARS - 1) {
+      NSTARS += 1000;
+      REALLOCATE (ra, double, NSTARS);
+      REALLOCATE (dec, double, NSTARS);
+      REALLOCATE (mag, double, NSTARS);
+      REALLOCATE (dmag, double, NSTARS);
+    }
+  }
+  NSTARS = i;
+
+  /* calculate <ra>, <dec> */
+  RA = DEC = 0;
+  for (i = 0; i < NSTARS; i++) {
+    RA += ra[i];
+    
+
+  /* load header from image file */
+  if (!gfits_read_header (argv[1], &header)) {
+    fprintf (stderr, "can't open fits file %s\n", argv[1]);
+    exit (1);
+  }
+  start_size = header.size;
+
+  /* run through the lines in the header data file */
+  while (scan_line (f, line) != EOF) {
+    
+    /* fill in end of line with blanks, force end at 80 */
+    for (N = strlen (line); N < 80; N++) {
+      line[N] = ' ';
+    }
+    line[80] = 0;
+    bzero (keyword, 10);
+    strncpy (keyword, line, 8);
+    
+    /* replace existing keywords, unless this is a COMMENT or HISTORY field */
+    if (strncmp (keyword, "COMMENT ", 8) && strncmp (keyword, "HISTORY ", 8)) {
+      p = gfits_header_field (&header, keyword, 1);
+      if (p != (char *) NULL) {
+	strncpy (p, line, 80);
+	continue;
+      }
+    }
+
+    /* find first line with the reserved line */
+    p = (char *) NULL;
+    Nreserved = strlen (reserved);
+    for (i = 0; (i < header.size) && (p == (char *) NULL) ; i+= FT_LINE_LENGTH) {
+      if (!strncmp (&header.buffer[i], reserved, Nreserved)) {
+	p = &header.buffer[i];
+      }
+    }
+    if (p == (char *) NULL) {
+      fprintf (stderr, "no more reserved spaces!\n");
+      exit (1);
+    }
+
+    /* insert the new line here */
+    strncpy (p, line, 80);
+  }
+
+  fclose (f);
+
+  /* now write the new header on top of the old one. 
+     check first that the header size has not changed.
+  */
+
+  if (header.size != start_size) {
+    fprintf (stderr, "header changed size: should not happen!\n");
+    exit (1);
+  }
+
+  f = fopen (argv[1], "r+");
+  if (f == NULL) {
+    fprintf (stderr, "can't open file for update %s\n", argv[1]);
+    exit (1);
+  }
+
+  fseek (f, 0, SEEK_SET);
+  status = fwrite (header.buffer, 1, header.size, f);
+  if (status != header.size) {
+    fprintf (stderr, "failed to write data to image header\n");
+    exit (1);
+  }
+  fclose (f);
+
+  exit (0);
+}
+    
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakedist.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakedist.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakedist.c	(revision 22322)
@@ -0,0 +1,548 @@
+# include <ohana.h>
+
+# define NGAUSS 2048
+double drand48();
+double gaussint[NGAUSS];
+double gaussian();
+double rnd_gauss();
+
+# define MMIN 1.0
+# define MMAX 120.0
+double rnd_mass();
+double MassTerm;
+
+# define NMASS 87
+# define DMASS 0.05
+# define NPARMAX 30
+int N_Av, N_dist, N_alpha, N_age, NSTARS, NTRY;
+double Av[NPARMAX], dist[NPARMAX], alpha[NPARMAX], age[NPARMAX];
+
+double dVo, dVref, dMo;   /* determine the noise distribution */
+
+double ldA, lAo, ldM, lMo;
+double UV0, V0, DV, DUV;
+Header header, UV_h, V_h, mass_h, age_h;
+Matrix matrix, UV_i, V_i, mass_i, age_i;
+int UV_NX, UV_NY;
+float *UVbuffer, *Vbuffer;
+int MS_NX, MS_NY;
+float *Mbuffer, *Abuffer;
+
+char fakefile[256], magfile[256], colorfile[256], massfile[256], agefile[256];
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+
+  int i, j, m, n, M, A, Nstars;
+  double mag, color, mass, noise, inflation;
+  float *in;
+  char line[1024];
+
+  load_parameters (argc, argv);
+
+  init_outmatrix (); 
+ 
+  gauss_init ();
+
+  read_datafiles ();
+
+  gfits_modify (&header, "SFR", "%s", 1, "Star Formation Rate (Mo / Myr)");
+  for (i = 0; i < N_Av; i++) {
+    for (j = 0; j < N_dist; j++) {
+      for (m = 0; m < N_alpha; m++) {
+	
+	MassTerm = (pow((MMAX/MMIN), -alpha[m]) - 1.0);
+	for (n = 0; n < N_age; n++) {
+	  fprintf (stderr, "age: %d %f -- %f\n", n, age[n], age[n+1]);
+	  
+	  mass = 0;
+	  for (Nstars = 0; Nstars < NSTARS; Nstars++) {
+	    fakestar (Av[i], dist[j], alpha[m], age[n], age[n+1], 
+		      &mag, &color, &mass, &noise);
+	    
+	    magtomass (Av[i], dist[j], mag, color, noise, &M, &A, &inflation);
+
+	    if (inflation > 0.0) add_to_outmatrix (i, j, m, n, A, M, inflation);
+	    
+	  }
+	  /* store the parameters and total mass for this Mass*Age matrix in the (N_age) column */
+	  /* mass is the total mass generated between 1 and 120 Mo for the NSTARS points in this matrix */
+	  sprintf (line, "SFR_%0d\0", n);
+	  gfits_modify (&header, line, "%lf", 1, mass / (age[n+1] - age[n]));
+	  add_to_outmatrix (i, j, m, n, N_age, 0, mass);
+	  add_to_outmatrix (i, j, m, n, N_age, 1, Av[i]);
+	  add_to_outmatrix (i, j, m, n, N_age, 2, dist[j]);
+	  add_to_outmatrix (i, j, m, n, N_age, 3, alpha[m]);
+	  add_to_outmatrix (i, j, m, n, N_age, 4, age[n]);
+	  add_to_outmatrix (i, j, m, n, N_age, 5, age[n+1]);
+	  
+	}
+      }
+    }
+  }
+
+  /* 
+  in = (float *)matrix.buffer;
+  for (i = 0; i < matrix.Naxis[0]; i++) {
+    for (j = 0; j < matrix.Naxis[1]; j++) {
+      mass = in[i + matrix.Naxis[0]*(j + matrix.Naxis[1]*2)];
+      fprintf (stderr, "%f ", mass);
+    }
+    fprintf (stderr, "\n");
+  }
+  */
+
+  gfits_write_header (fakefile, &header);
+  gfits_write_matrix (fakefile, &matrix);
+}
+
+/*****************************************************************************/
+
+load_parameters (argc, argv)
+int argc;
+char **argv;
+{
+
+  int i, stat, test_dVo, test_dVref, test_dMo;
+  int Ffile, Mfile, Cfile, Afile, MSfile;
+  FILE *f;
+  char line[1024];
+  double N_stars, N_try, Ntotal, time;
+
+  if (argc < 2) {
+    fprintf (stderr, "USAGE: %s pfile\n", argv[0]);
+    exit (0);
+  }
+
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    fprintf (stderr, "parameter file %s not found\n", argv[1]);
+    exit (0);
+  }
+
+  N_Av = N_dist = N_alpha = N_age = N_stars = N_try = 0.0;
+  test_dVo = test_dVref = test_dMo = FALSE;
+  Ffile = Mfile = Cfile = Afile = MSfile = FALSE;
+  while (scan_line (f, line) != EOF) {
+    if (!stripwhite (line)) continue;
+    if (line[0] == '#') continue;
+    
+    if (!strncmp (line, "Av ", strlen ("Av "))) {
+      for (i = 0; (i < NPARMAX) && (stat = dparse (&Av[i], i+2, line)); i++);
+      if (i == NPARMAX) {
+	fprintf (stderr, "maximum of %d parameter values per parameter\n", NPARMAX);
+	exit (0);
+      }
+      N_Av = i;
+    }
+
+    if (!strncmp (line, "dist ", strlen ("dist "))) {
+      for (i = 0; (i < NPARMAX) && dparse (&dist[i], i+2, line); i++);
+      if (i == NPARMAX) {
+	fprintf (stderr, "maximum of %d parameter values per parameter\n", NPARMAX);
+	exit (0);
+      }
+      N_dist = i;
+    }
+
+    if (!strncmp (line, "alpha ", strlen ("alpha "))) {
+      for (i = 0; (i < NPARMAX) && dparse (&alpha[i], i+2, line); i++);
+      if (i == NPARMAX) {
+	fprintf (stderr, "maximum of %d parameter values per parameter\n", NPARMAX);
+	exit (0);
+      }
+      N_alpha = i;
+    }
+
+    if (!strncmp (line, "age ", strlen ("age "))) {
+      for (i = 0; (i < NPARMAX) && dparse (&age[i], i+2, line); i++);
+      if (i == NPARMAX) {
+	fprintf (stderr, "maximum of %d parameter values per parameter\n", NPARMAX);
+	exit (0);
+      }
+      N_age = MAX (0, i - 1);
+    }
+
+    if (!strncmp (line, "dVo ", strlen ("dVo "))) {
+      dparse (&dVo, 2, line);
+      test_dVo = TRUE;
+    }
+    
+    if (!strncmp (line, "dVref ", strlen ("dVref "))) {
+      dparse (&dVref, 2, line);
+      test_dVref = TRUE;
+    }
+    
+    if (!strncmp (line, "dMo ", strlen ("dMo "))) {
+      dparse (&dMo, 2, line);
+      test_dMo = TRUE;
+    }
+    
+    if (!strncmp (line, "nstars ", strlen ("nstars "))) {
+      dparse (&N_stars, 2, line);
+      NSTARS = N_stars;
+    }
+    
+    if (!strncmp (line, "ntry ", strlen ("ntry "))) {
+      dparse (&N_try, 2, line);
+      NTRY = N_try;
+    }
+    
+    if (!strncmp (line, "magfile ", strlen ("magfile "))) {
+      sscanf (line, "%*s %s", magfile);
+      fprintf (stderr, "magfile: %s\n", magfile);
+      Mfile = TRUE;
+    }
+    
+    if (!strncmp (line, "colorfile ", strlen ("colorfile "))) {
+      sscanf (line, "%*s %s", colorfile);
+      fprintf (stderr, "colorfile: %s\n", colorfile);
+      Cfile = TRUE;
+    }
+    
+    if (!strncmp (line, "agefile ", strlen ("agefile "))) {
+      sscanf (line, "%*s %s", agefile);
+      fprintf (stderr, "agefile: %s\n", agefile);
+      Afile = TRUE;
+    }
+    
+    if (!strncmp (line, "massfile ", strlen ("massfile "))) {
+      sscanf (line, "%*s %s", massfile);
+      fprintf (stderr, "massfile: %s\n", massfile);
+      MSfile = TRUE;
+    }
+    
+    if (!strncmp (line, "fakefile ", strlen ("fakefile "))) {
+      sscanf (line, "%*s %s", fakefile);
+      fprintf (stderr, "fakefile: %s\n", fakefile);
+      Ffile = TRUE;
+    }
+    
+    if (N_Av && N_dist && N_alpha && N_age && N_stars && N_try && 
+	test_dVo && test_dVref && test_dMo &&
+	Ffile && Mfile && Cfile && Afile && MSfile) {
+      Ntotal = N_Av * N_dist * N_alpha * N_age * N_stars * N_try;
+      time = (Ntotal / 1250000.0);
+      fprintf (stderr, "%.0f iterations\n", Ntotal);
+      fprintf (stderr, "process should take about %.1f minutes\n", time);
+      return;
+    }
+  }
+  
+  if (!(N_Av && N_dist && N_alpha && N_age && NSTARS && NTRY && 
+	test_dVo && test_dVref && test_dMo && 
+	Ffile && Mfile && Cfile && Afile && MSfile)) {
+    fprintf (stderr, "failed to get all parameter lines\n");
+    fprintf (stderr, "N_Av: %d\n", N_Av);
+    fprintf (stderr, "N_dist: %d\n", N_dist);
+    fprintf (stderr, "N_alpha: %d\n", N_alpha);
+    fprintf (stderr, "N_age: %d\n", N_age);
+    fprintf (stderr, "N_stars: %d\n", NSTARS);
+    fprintf (stderr, "N_try: %d\n", NTRY);
+    fprintf (stderr, "dVo: %f (%d)\n", dVo, test_dVo);
+    fprintf (stderr, "dVref: %f (%d)\n", dVref, test_dVref);
+    fprintf (stderr, "dMo: %f (%d)\n", dMo, test_dMo);
+    fprintf (stderr, "Ffile: %d\n", Ffile);
+    fprintf (stderr, "Mfile: %d\n", Mfile);
+    fprintf (stderr, "Cfile: %d\n", Cfile);
+    fprintf (stderr, "Afile: %d\n", Afile);
+    fprintf (stderr, "MSfile: %d\n", MSfile);
+    exit (0);
+  }
+  
+}
+
+/*****************************************************************************/
+
+
+init_outmatrix () 
+{
+
+  int i;
+  char line[32];
+  double value;
+
+  gfits_init_header (&header);
+  header.bitpix = -32;
+  header.Naxes = 7;
+  header.Naxis[0] = NMASS;
+  header.Naxis[1] = N_age + 1;
+  header.Naxis[2] = N_age;
+  header.Naxis[3] = N_alpha;
+  header.Naxis[4] = N_dist;
+  header.Naxis[5] = N_Av;
+  header.Naxis[6] = 2;
+  header.bzero = 0.0;
+  header.bscale = 1.0;
+  header.unsign = FALSE;
+  header.extend = FALSE;
+
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+
+  gfits_modify (&header, "PAR_1", "%s", 1, "mass (Mo)");
+  for (i = 0; i < NMASS + 1; i++) {
+    sprintf (line, "PAR_1_%0d\0", i);
+    value = 10.0 / SQ(4.5 - DMASS*i);
+    gfits_modify (&header, line, "%lf", 1, value);
+  }
+  
+  gfits_modify (&header, "PAR_2", "%s", 1, "age out (Myr)");
+  for (i = 0; i < N_age + 1; i++) {
+    sprintf (line, "PAR_2_%0d\0", i);
+    gfits_modify (&header, line, "%lf", 1, age[i]);
+  }
+
+  gfits_modify (&header, "PAR_3", "%s", 1, "age in (Myr)");
+  for (i = 0; i < N_age + 1; i++) {
+    sprintf (line, "PAR_3_%0d\0", i);
+    gfits_modify (&header, line, "%lf", 1, age[i]);
+  }
+  
+  gfits_modify (&header, "PAR_4", "%s", 1, "alpha");
+  for (i = 0; i < N_alpha; i++) {
+    sprintf (line, "PAR_4_%0d\0", i);
+    gfits_modify (&header, line, "%lf", 1, alpha[i]);
+  }
+
+  gfits_modify (&header, "PAR_5", "%s", 1, "dist (mag)");
+  for (i = 0; i < N_dist; i++) {
+    sprintf (line, "PAR_5_%0d\0", i);
+    gfits_modify (&header, line, "%lf", 1, dist[i]);
+  }
+
+  gfits_modify (&header, "PAR_6", "%s", 1, "A_V (mag)");
+  for (i = 0; i < N_Av; i++) {
+    sprintf (line, "PAR_6_%0d\0", i);
+    gfits_modify (&header, line, "%lf", 1, Av[i]);
+  }
+
+}
+
+
+/*****************************************************************************/
+
+fakestar (A_V, Dist, Alpha, AgeS, AgeE, mag, color, Mass, Noise)
+double A_V, Dist, Alpha, AgeS, AgeE;
+double *Mass, *mag, *color, *Noise;
+{
+
+  int X, Y, i;
+  double mass, Age, v, uv, noise;
+
+  for (i = 0; i < 500; i++) {
+    mass = rnd_mass (Alpha);
+    Age = (AgeE - AgeS)*drand48() + AgeS;
+    *Mass += mass;
+    
+    /* find star in color, mag images */
+    X = (log10(Age) - lAo) / ldA;
+    Y = (log10(mass) - lMo) / ldM;
+    if ((X >= 0) && (X < UV_NX) && (Y >= 0) && (Y < UV_NY)) {
+      uv = UVbuffer[X + UV_NX*Y];
+      if (uv != 100.0) {
+	uv += 0.7*A_V;   /***** this depends on the extinction law and colors U-V ***/
+	v = Vbuffer[X + UV_NX*Y] + Dist + A_V; 
+	noise = dVo*sqrt(1.0 + pow (10.0, (0.4*(v - dVref)))); 
+	if (noise < dMo) {
+	  *Noise = noise;
+	  *mag = rnd_gauss (v, noise);
+	  *color = rnd_gauss (uv, 1.4*noise);
+	  return;
+	}
+      }
+    }
+  }
+  fprintf (stderr, "problem with generating stars:  none land in safe zone\n");
+  exit (0);
+}
+
+
+
+/* rnd_mass only returns stars with masses in the range MMIN to MMAX */
+double
+rnd_mass (Alpha)
+double Alpha;
+{
+  
+  double dP, x;
+  
+  x = (MMAX + 1);
+  while (x > MMAX) {
+    dP = drand48();
+    x = MMIN * pow ((dP*MassTerm + 1.0), 1.0 / (-Alpha));
+  }
+  return (x);
+
+}
+
+/*****************************************************************************/
+
+read_datafiles ()
+{
+
+  gfits_read_header (colorfile, &UV_h);
+  gfits_read_matrix (colorfile, &UV_i);
+  gfits_read_header (magfile, &V_h);
+  gfits_read_matrix (magfile, &V_i); 
+  UV_NX = UV_h.Naxis[0];
+  UV_NY = UV_h.Naxis[1];
+  UVbuffer = (float *)UV_i.buffer;
+  Vbuffer = (float *)V_i.buffer;
+
+  gfits_scan (&UV_h, "RA_O",  "%lf", 1, &lAo);
+  gfits_scan (&UV_h, "RA_X",  "%lf", 1, &ldA);
+  gfits_scan (&UV_h, "DEC_O", "%lf", 1, &lMo);
+  gfits_scan (&UV_h, "DEC_Y", "%lf", 1, &ldM);
+
+  gfits_read_header (massfile, &mass_h);
+  gfits_read_matrix (massfile, &mass_i);
+  gfits_read_header (agefile, &age_h);
+  gfits_read_matrix (agefile, &age_i); 
+  MS_NX = mass_h.Naxis[0];
+  MS_NY = mass_h.Naxis[1];
+  Mbuffer = (float *)mass_i.buffer;
+  Abuffer = (float *)age_i.buffer;
+
+  gfits_scan (&mass_h, "RA_O",  "%lf", 1, &UV0);
+  gfits_scan (&mass_h, "RA_X",  "%lf", 1, &DUV);
+  gfits_scan (&mass_h, "DEC_O", "%lf", 1, &V0);
+  gfits_scan (&mass_h, "DEC_Y", "%lf", 1, &DV);
+} 
+
+/*****************************************************************************/
+
+double
+rnd_gauss (mean, sigma)
+double mean, sigma; 
+{
+
+  int i;
+  double y;
+
+  y = drand48();
+  i = NGAUSS*y;
+  y = gaussint[i]*sigma + mean;
+
+  return (y);
+
+}
+
+gauss_init ()
+{
+
+  int i;
+  long A, B;
+  double val, x, dx, dx1, dx2, dx3, df;
+  double mean, sigma;
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+
+  val = 0;
+  dx = 0.001;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+  mean = 0.0;
+  sigma = 1.0;
+
+  for (i = 0, x = -7.0; (i < NGAUSS) && (x < 7.0); x += dx)  {
+    df = (3.0*gaussian(x    , mean, sigma) + 
+	  9.0*gaussian(x+dx1, mean, sigma) +
+	  9.0*gaussian(x+dx2, mean, sigma) + 
+	  3.0*gaussian(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+    if (val > (i / (double) NGAUSS)) {
+      gaussint[i] = x + dx / 2.0;
+      i++;
+    }
+  }
+  return (x + dx / 2.0);
+}
+
+double 
+gaussian (x, mean, sigma) 
+double x, mean, sigma; 
+{
+
+  double f, X;
+
+  f = exp (-0.5 * SQ(x - mean) / SQ(sigma)) / sqrt(2 * M_PI * SQ(sigma));
+
+  return (f);
+
+}
+
+/*****************************************************************************/
+
+magtomass (A_V, Dist, mag, color, noise, M, A, inflation)
+double A_V, Dist, mag, color, noise;
+int *M, *A;
+double *inflation;
+{
+
+  int x, y, I, J, i, j, k;
+  double mass, Age, v, uv, Ngood;
+
+  *inflation = 0.0;
+  x = (color - UV0 - 0.7*A_V) / DUV;   /* again, depends on U-V and extinction */
+  y = (mag - Dist - V0 - A_V) / DV;
+  if ((x > 0) && (x < MS_NX) && (y > 0) && (y < MS_NY)) {
+    mass = Mbuffer[x + MS_NX*y];
+    if (mass > 0.0) {
+      Age = Abuffer[x + MS_NX*y];
+      /* age[i]: i = 0,N_age */
+      if ((Age < age[0]) || (Age > age[N_age])) {
+	*inflation = 0.0;
+	return;
+      }
+      for (I = 0; (I < N_age) && (Age > age[I+1]); I++);
+      J = MAX (MIN ((4.5 - sqrt(10.0/mass)) / DMASS, NMASS - 1), 0);
+      Ngood = 1.0;
+      for (k = 0; k < NTRY; k++) {
+	v = rnd_gauss (mag, noise);
+	uv = rnd_gauss (color, 1.4*noise);
+	x = (uv - UV0 - 0.7*A_V) / DUV;
+	y = (v - V0 - Dist - A_V) / DV;
+	if ((x > 0) && (x < MS_NX) && (y > 0) && (y < MS_NY)) {
+	  mass = Mbuffer[x + MS_NX*y];
+	  if (mass > 0.0) {
+	    Age = Abuffer[x + MS_NX*y];
+	    for (i = -1; (i < N_age) && (Age > age[i+1]); i++);
+	    j = MAX (MIN ((4.5 - sqrt(10.0/mass)) / DMASS, NMASS - 1), 0);
+	    if ((i == I) && (j == J)) {
+	      Ngood += 1.0;
+	    }
+	  }
+	}
+      }
+      *inflation = (NTRY / Ngood);
+      *M = J;
+      *A = I;
+    }
+  }
+  if (mass == 0.0) *inflation = 0.0;
+}
+ 
+
+/*****************************************************************************/
+
+add_to_outmatrix (AV, DIST, ALPHA, AGE_IN, AGE_OUT, MASS, value)
+int AV, DIST, ALPHA, AGE_IN, AGE_OUT, MASS;
+double value;
+{
+
+  float *buffer;
+
+  buffer = (float *)matrix.buffer;
+  
+  /* elements in the (N_age) row of each Mass*Age matrix contain special numbers for the matrix */
+  buffer[MASS + NMASS*(AGE_OUT + (N_age + 1)*(AGE_IN + N_age*(ALPHA + N_alpha*(DIST + N_dist*(AV + N_Av*(0))))))] += value;
+  buffer[MASS + NMASS*(AGE_OUT + (N_age + 1)*(AGE_IN + N_age*(ALPHA + N_alpha*(DIST + N_dist*(AV + N_Av*(1))))))] += 1.0;
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakedump.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakedump.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakedump.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include <ohana.h>
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+
+  Header in_h, out_h;
+  Matrix in_m, out_m;
+  int i, j;
+  int Nage, Nalpha, Ndist, NAv;
+  float *in, *out;
+
+  if (argc < 7) {
+    fprintf (stderr, "USAGE fakedump infile outfile Nage Nalpha Ndist NAv\n");
+    exit (0);
+  }
+  Nage = atof(argv[3]);
+  Nalpha = atof(argv[4]);
+  Ndist = atof(argv[5]);
+  NAv = atof(argv[6]);
+
+  gfits_read_header (argv[1], &in_h);
+  gfits_read_matrix (argv[1], &in_m);
+
+  if (Nage > in_h.Naxis[2] - 1) {
+    fprintf (stderr, "Nage too big (%d, %d)\n", Nage, in_h.Naxis[2] - 1);
+    exit (0);
+  }
+
+  if (Nalpha > in_h.Naxis[3] - 1) {
+    fprintf (stderr, "Nalpha too big (%d, %d)\n", Nalpha, in_h.Naxis[3] - 1);
+    exit (0);
+  }
+
+  if (Ndist > in_h.Naxis[4] - 1) {
+    fprintf (stderr, "Ndist too big (%d, %d)\n", Ndist, in_h.Naxis[4] - 1);
+    exit (0);
+  }
+
+  if (NAv > in_h.Naxis[5] - 1) {
+    fprintf (stderr, "NAv too big (%d, %d)\n", NAv, in_h.Naxis[5] - 1);
+    exit (0);
+  }
+
+  gfits_init_header (&out_h);
+  out_h.bitpix = -32;
+  out_h.Naxes = 2;
+  out_h.Naxis[0] = in_h.Naxis[0];
+  out_h.Naxis[1] = in_h.Naxis[1] - 1;
+  out_h.bzero = 0.0;
+  out_h.bscale = 1.0;
+  out_h.unsign = FALSE;
+  out_h.extend = FALSE;
+  gfits_create_header (&out_h);
+  gfits_create_matrix (&out_h, &out_m);
+  
+  in = (float *)in_m.buffer;
+  out = (float *)out_m.buffer;
+  for (i = 0; i < out_h.Naxis[0]; i++) {
+    for (j = 0; j < out_h.Naxis[1]; j++) {
+      out[i + out_h.Naxis[0]*j] = in[i + in_h.Naxis[0]*(j + in_h.Naxis[1]*(Nage + in_h.Naxis[2]*(Nalpha + in_h.Naxis[3]*(Ndist + in_h.Naxis[4]*(NAv + in_h.Naxis[5] * 0)))))];
+      fprintf (stderr, "%f ", out[i + out_h.Naxis[0]*j]);
+    }
+    fprintf (stderr, "\n");
+  }
+  
+  gfits_write_header (argv[2], &out_h);
+  gfits_write_matrix (argv[2], &out_m);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakepop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakepop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakepop.c	(revision 22322)
@@ -0,0 +1,293 @@
+# include <ohana.h>
+# define MMIN 1.0
+# define MMAX 120.0
+extern double drand48();
+extern double rnd_mass();
+double rnd_mass();
+double term;
+extern double rnd_gauss();
+extern double rnd_integrate ();
+extern double gaussian ();
+
+double gaussian();
+double rnd_gauss();
+double rnd_integrate();
+
+# define NGAUSS 2048
+double gaussint[NGAUSS];
+
+double AgeS, AgeE, alpha, d, Av, dMo, dVo, dVref;
+char colorfile[256], magfile[256];
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+
+  Header UV_h, V_h;
+  Matrix UV_i, V_i;
+  double Mtot, M, mass;
+  double age, noise, v, uv, V, UV;
+  double lAo, ldA, lMo, ldM, stage;
+  int X, Y;
+  long A, B;
+  
+  load_parameters (argc, argv);
+  Mtot = atof (argv[2]);
+  term = (pow((MMAX/MMIN), -alpha) - 1.0);
+  
+  gfits_read_header (colorfile, &UV_h);
+  gfits_read_matrix (colorfile, &UV_i);
+  gfits_read_header (magfile, &V_h);
+  gfits_read_matrix (magfile, &V_i); 
+
+  gfits_scan (&UV_h, "RA_O",  "%lf", 1, &lAo);
+  gfits_scan (&UV_h, "RA_X",  "%lf", 1, &ldA);
+  gfits_scan (&UV_h, "DEC_O", "%lf", 1, &lMo);
+  gfits_scan (&UV_h, "DEC_Y", "%lf", 1, &ldM);
+  
+  gauss_init ();
+
+  fprintf (stderr, "beginning main loop\n");
+  
+  stage = 1;
+  for (M = 0; M < Mtot; ) {
+    age = (AgeE - AgeS)*drand48() + AgeS;
+    X = (log10(age) - lAo) / ldA;
+    if (M > stage * Mtot / 10.0) {
+      fprintf (stderr, "M: %f\n", M);
+      stage += 1.0;
+    }
+    mass = rnd_mass (alpha);
+    Y = (log10(mass) - lMo) / ldM;
+    if ((X >= 0) && (X < UV_h.Naxis[0]) &&
+	(Y >= 0) && (Y < UV_h.Naxis[1])) {
+      uv = gfits_get_matrix_value (&UV_i, X, Y); 
+      if (uv != 100.0) {
+	uv += 0.7*Av;
+	v = gfits_get_matrix_value (&V_i, X, Y) + d + Av; 
+	noise = dVo*sqrt(1.0 + pow (10.0, (0.4*(v - dVref)))); 
+	V = rnd_gauss (v, noise);
+	UV = rnd_gauss (uv, 1.4*noise);
+	if (noise < dMo) {
+	  fprintf (stdout, "%f %f   %f %f   %f  %f\n", V, noise, V+UV, noise, mass, age);
+	}
+      }
+    }
+    M += mass;
+  }
+}
+
+/* rnd_mass only returns stars with masses in the range MMIN to MMAX */
+double
+rnd_mass (alpha)
+double alpha;
+{
+  
+  double dP, x;
+  
+  x = (MMAX + 1);
+  while (x > MMAX) {
+    dP = drand48();
+    x = MMIN * pow ((dP*term + 1.0), 1.0 / (-alpha));
+  }
+  return (x);
+
+}
+
+
+double
+rnd_gauss (mean, sigma)
+double mean, sigma; 
+{
+
+  int i;
+  double y;
+
+  y = drand48();
+  i = NGAUSS*y;
+  y = gaussint[i]*sigma + mean;
+
+  return (y);
+
+}
+
+
+gauss_init ()
+{
+
+  int i;
+  long A, B;
+  double val, x, dx, dx1, dx2, dx3, df;
+  double mean, sigma;
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+
+  val = 0;
+  dx = 0.001;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+  mean = 0.0;
+  sigma = 1.0;
+
+  for (i = 0, x = -7.0; (i < NGAUSS) && (x < 7.0); x += dx)  {
+    df = (3.0*gaussian(x    , mean, sigma) + 
+	  9.0*gaussian(x+dx1, mean, sigma) +
+	  9.0*gaussian(x+dx2, mean, sigma) + 
+	  3.0*gaussian(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+    if (val > (i / (double) NGAUSS)) {
+      gaussint[i] = x + dx / 2.0;
+      i++;
+    }
+  }
+  return (x + dx / 2.0);
+}
+
+double 
+rnd_integrate (function, range, mean, sigma) 
+double (*function) ();
+double range, mean, sigma;
+{
+
+  double val, x, dx, dx1, dx2, dx3, df;
+
+  range += 0.0001;
+  val = 0;
+  dx = sigma / 10.0;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+
+  for (x = mean - 7*sigma; (val < range) && (x < mean + 7*sigma); x += dx)  {
+    df = (3.0*function(x    , mean, sigma) + 
+	  9.0*function(x+dx1, mean, sigma) +
+	  9.0*function(x+dx2, mean, sigma) + 
+	  3.0*function(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+  }
+  return (x + dx / 2.0);
+}
+
+double 
+gaussian (x, mean, sigma) 
+double x, mean, sigma; 
+{
+
+  double f, X;
+
+  f = exp (-0.5 * SQ(x - mean) / SQ(sigma)) / sqrt(2 * M_PI * SQ(sigma));
+
+  return (f);
+
+}
+
+
+/*****************************************************************************/
+
+load_parameters (argc, argv)
+int argc;
+char **argv;
+{
+
+  int test_dVo, test_dVref, test_dMo;
+  int test_Age, test_Av, test_alpha, test_d;
+  int Mfile, Cfile;
+  FILE *f;
+  char line[1024];
+
+  if (argc < 3) {
+    fprintf (stderr, "USAGE: %s pfile Mtot\n", argv[0]);
+    exit (0);
+  }
+
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    fprintf (stderr, "parameter file %s not found\n", argv[1]);
+    exit (0);
+  }
+
+  test_Age = test_Av = test_alpha = test_d = FALSE;
+  test_dVo = test_dVref = test_dMo = FALSE;
+  Mfile = Cfile = FALSE;
+  while (scan_line (f, line) != EOF) {
+    if (!stripwhite (line)) continue;
+    if (line[0] == '#') continue;
+    
+    fprintf (stderr, "%s\n", line);
+    if (!strncmp (line, "Av ", strlen ("Av "))) {
+      dparse (&Av, 2, line);
+      test_Av = TRUE;
+    }
+
+    if (!strncmp (line, "dist ", strlen ("dist "))) {
+      dparse (&d, 2, line);
+      test_d = TRUE;
+    }
+
+    if (!strncmp (line, "alpha ", strlen ("alpha "))) {
+      dparse (&alpha, 2, line);
+      test_alpha = TRUE;
+    }
+
+    if (!strncmp (line, "age ", strlen ("age "))) {
+      test_Age  = dparse (&AgeS, 2, line);
+      test_Age &= dparse (&AgeE, 3, line);
+    }
+
+    if (!strncmp (line, "dVo ", strlen ("dVo "))) {
+      dparse (&dVo, 2, line);
+      test_dVo = TRUE;
+    }
+    
+    if (!strncmp (line, "dVref ", strlen ("dVref "))) {
+      dparse (&dVref, 2, line);
+      test_dVref = TRUE;
+    }
+    
+    if (!strncmp (line, "dMo ", strlen ("dMo "))) {
+      dparse (&dMo, 2, line);
+      test_dMo = TRUE;
+    }
+    
+    if (!strncmp (line, "magfile ", strlen ("magfile "))) {
+      sscanf (line, "%*s %s", magfile);
+      fprintf (stderr, "magfile: %s\n", magfile);
+      Mfile = TRUE;
+    }
+    
+    if (!strncmp (line, "colorfile ", strlen ("colorfile "))) {
+      sscanf (line, "%*s %s", colorfile);
+      fprintf (stderr, "colorfile: %s\n", colorfile);
+      Cfile = TRUE;
+    }
+    
+    if (test_Age && test_Av && test_d && test_alpha && 
+	test_dVo && test_dVref && test_dMo &&
+	Mfile && Cfile) {
+      return;
+    }
+  }
+  
+  if (!(test_Age && test_Av && test_d && test_alpha && 
+	test_dVo && test_dVref && test_dMo && 
+	Mfile && Cfile)) {
+    fprintf (stderr, "failed to get all parameter lines\n");
+    fprintf (stderr, "test_Av: %d\n", test_Av);
+    fprintf (stderr, "test_d: %d\n", test_d);
+    fprintf (stderr, "test_alpha: %d\n", test_alpha);
+    fprintf (stderr, "test_age: %d\n", test_Age);
+    fprintf (stderr, "dVo: %f (%d)\n", dVo, test_dVo);
+    fprintf (stderr, "dVref: %f (%d)\n", dVref, test_dVref);
+    fprintf (stderr, "dMo: %f (%d)\n", dMo, test_dMo);
+    fprintf (stderr, "Mfile: %d\n", Mfile);
+    fprintf (stderr, "Cfile: %d\n", Cfile);
+    exit (0);
+  }
+  
+}
+
+/*****************************************************************************/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakestars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakestars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fakestars.c	(revision 22322)
@@ -0,0 +1,121 @@
+# include <ohana.h>
+# define NCHAR 66 /* 65 char EXCLUDING return */
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+typedef struct {
+  double X;
+  double Y;
+  double R;
+  double D;
+  double M, dM;
+  char   dophot;
+  double sky;
+  double fx, fy, df;
+  double Mgal, Map;
+  int found;
+} Stars;
+
+void   gauss_init ();
+double gaussian (double x, double mean, double sigma);
+double rnd_gauss (double mean, double sigma);
+
+  /* 
+     load in a list of stars in cmp format
+     generate a new list of stars in cmp format
+     new measurements should have the requested noise characteristics */
+
+int main (int argc, char **argv) {
+
+  int Nstar, N, Nbytes, nbytes, i;
+  FILE *f;
+  char *input, *output, *buffer, line[NCHAR];
+  Header header;
+  Stars *stars;
+  double tmp, dMs, dMr, dMo, dM, offset;
+
+  if (argc != 5) {
+    fprintf (stderr, "USAGE: fakestars (input) (output) (syserr) (offset)\n");
+    exit (2);
+  }
+  input  = argv[1];
+  output = argv[2];
+  dMs    = atof (argv[3]);
+  offset = atof (argv[4]);
+
+  gauss_init ();
+
+  /* load header, open file */
+  if (!gfits_read_header (input, &header)) {
+    fprintf (stderr, "ERROR: can't read header for %s\n", input);
+    exit (1);
+  }
+  f = fopen (input, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't read data from %s\n", input);
+    exit (1);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  /* find expected number of stars */
+  gfits_scan (&header, "NSTARS", "%d", 1, &Nstar);
+  if (Nstar == 0) {
+    fprintf (stderr, "ERROR: can't get NSTARS from header\n");
+    exit (1);
+  }
+  ALLOCATE (stars, Stars, Nstar);
+
+  /* load in stars */
+  Nbytes = Nstar*BYTES_STAR;
+  ALLOCATE (buffer, char, Nbytes + 1);
+  nbytes = fread (buffer, 1, Nbytes, f);
+  if (nbytes != Nbytes) {
+    fprintf (stderr, "ERROR: failed to read in %d stars\n", Nstar);
+    exit (1);
+  }
+  buffer[Nbytes] = 0;
+
+  for (i = 0; i < Nstar; i++) {
+    Nbytes = i*BYTES_STAR;
+    dparse (&stars[i].X,    1, &buffer[Nbytes]);
+    dparse (&stars[i].Y,    2, &buffer[Nbytes]);
+    dparse (&stars[i].M,    3, &buffer[Nbytes]);
+    dparse (&stars[i].dM,   4, &buffer[Nbytes]);
+    dparse (&stars[i].Mgal, 7, &buffer[Nbytes]);
+    dparse (&stars[i].Map,  8, &buffer[Nbytes]);
+    dparse (&stars[i].fx,   9, &buffer[Nbytes]);
+    dparse (&stars[i].fy,  10, &buffer[Nbytes]);
+    dparse (&stars[i].df,  11, &buffer[Nbytes]);
+    
+    dparse (&tmp,           5, &buffer[Nbytes]);
+    stars[i].dophot = tmp;
+  }
+  fclose (f);
+
+  /* error per star is hypot of systematic & poisson */
+  for (i = 0; i < Nstar; i++) {
+    dMr = 0.001 * stars[i].dM;
+    dMo = hypot (dMs, dMr);
+    dM  = rnd_gauss (0.0, dMo);
+    stars[i].M += dM + offset;
+  }
+
+  gfits_write_header (output, &header);
+  f = fopen (output, "a");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open file for output: %s\n", output);
+    exit (1);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  for (i = 0; i < Nstar; i++) {
+    snprintf (line, NCHAR, "%6.1f %6.1f %6.3f %03d %2d %3.1f %6.3f %6.3f %6.2f %6.2f %5.1f", 
+	      stars[i].X, stars[i].Y, stars[i].M, 
+	      (int)stars[i].dM, stars[i].dophot, stars[i].sky, 
+	      stars[i].Mgal, stars[i].Map, stars[i].fx, stars[i].fy, stars[i].df);
+    fprintf (f, "%s\n", line);
+  }
+  fclose (f);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fitdist.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fitdist.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fitdist.c	(revision 22322)
@@ -0,0 +1,627 @@
+# include <ohana.h>
+
+# define NGAUSS 2048
+double drand48();
+double gaussint[NGAUSS];
+double gaussian();
+double rnd_gauss();
+
+# define MMIN 1.0
+# define MMAX 120.0
+double rnd_mass();
+double MassTerm;
+
+# define NPARMAX 30
+int Nmass, N_Av, N_dist, N_alpha, N_age, NTRY;
+double *Mass, Av[NPARMAX], dist[NPARMAX], alpha[NPARMAX], age[NPARMAX];
+
+int COL1, COL2;
+double *mag, *color, *noise;
+double *obsdata;
+
+double UV0, V0, DV, DUV;
+Header fake_h, mass_h, age_h;
+Matrix fake_m, mass_i, age_i;
+int MS_NX, MS_NY;
+float *Mbuffer, *Abuffer;
+
+char datafile[256], fakefile[256], massfile[256], agefile[256];
+
+double get_obs(), get_fake(), get_error(), get_fake_err(), get_chisq();
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+  
+  Header header;
+  Matrix matrix;
+  int i, j, m, n, M, A, Nstars, I, J, k, done, grid, tries;
+  double mass, inflation, sumterm, errterm;
+  double dm, a1, a2, chisq_0, chisq_1, chisq_min, Ndof, *delta;
+  double temp_Factor, *Factor, *dFactor, *SFR, dFactor1, dFactor2;
+
+  load_parameters (argc, argv);
+  fprintf (stderr, "read in parameters\n");
+
+  gauss_init ();
+
+  read_datafiles ();
+  fprintf (stderr, "read in data files (%d x %d)\n", Nmass, N_age);
+
+  Nstars = load_realstars ();
+  fprintf (stderr, "read in %d real stars\n", Nstars);
+
+  ALLOCATE (SFR, double, N_age);
+  ALLOCATE (Factor, double, N_age);
+  ALLOCATE (dFactor, double, N_age);
+  ALLOCATE (delta, double, N_age);
+
+  fprintf (stdout, "# Av   dist   alpha  chisq Ndof");
+  for (n = 0; n < N_age; n++) {
+    fprintf (stdout, " %3.0f My ", age[n]);
+  }
+  fprintf (stdout, "\n");
+
+  for (i = 0; i < N_Av; i++) {
+    for (j = 0; j < N_dist; j++) {
+      
+      /* unique obs. matrix for each Av, dist value only */
+      bzero (obsdata, sizeof(double) * N_age*Nmass*2);
+      for (k = 0; k < Nstars; k++) {
+	magtomass (Av[i], dist[j], mag[k], color[k], noise[k], &M, &A, &inflation);
+	if (inflation > 0.0) add_obs (A, M, inflation);
+      }
+      
+      /* now fit to fake matrices */
+      for (m = 0; m < N_alpha; m++) {
+
+	for (n = 0; n < N_age; n++) {
+	  dm = get_fake (i, j, m, n, N_age, 0);
+	  a1 = get_fake (i, j, m, n, N_age, 4);
+	  a2 = get_fake (i, j, m, n, N_age, 5);
+	  SFR[n] = dm / (a2 - a1);
+	  delta[n] = 0.1;
+	  Factor[n] = 0.0;
+	}	
+	
+	chisq_1 = get_chisq (Factor, i, j, m, &Ndof);
+	chisq_0 = 2*chisq_1;
+	done = FALSE;
+	/* 	while (!done) { */
+	for (tries = 0; ((fabs (chisq_1 - chisq_0) / Ndof > 0.001) && (tries < 100)); tries++) {
+	  chisq_0 = chisq_1;
+	  for (n = 0; n < N_age; n++) {
+	    Factor[n] += delta[n];
+	    chisq_1 = get_chisq (Factor, i, j, m, &Ndof);
+	    dFactor1 = MAX (0.0, MIN (0.5*delta[n], 5.0 * delta[n] * (1.0 - chisq_1/chisq_0)));
+	    Factor[n] -= 2*delta[n];
+	    chisq_1 = get_chisq (Factor, i, j, m, &Ndof);
+	    dFactor2 = MIN (0.0, MAX (-0.5*delta[n], -5.0 * delta[n] * (1.0 - chisq_1/chisq_0)));
+	    if (dFactor1 > fabs(dFactor2)) {
+	      dFactor[n] = dFactor1;
+	    }
+	    else {
+	      dFactor[n] = dFactor2;
+	    }
+	    Factor[n] += delta[n];
+	    if ((delta[n] > 0.001) && (!(tries % 5) || (fabs(dFactor[n]) < 0.01 * delta[n]))) {
+	      /* small grid search */
+	      dFactor1 = temp_Factor = Factor[n];
+	      chisq_min = chisq_0;
+	      for (grid = 0, Factor[n] = MAX (0.0, Factor[n] - 2*delta[n]); grid < 5; grid ++, Factor[n] += delta[n]) {
+		chisq_1 = get_chisq (Factor, i, j, m, &Ndof);
+		if (chisq_1 < chisq_min) {
+		  dFactor1 = Factor[n];
+		  chisq_min = chisq_1;
+		}
+	      }
+	      if (dFactor1 == temp_Factor) {
+		delta[n] = delta[n] / 2.0;
+	      }
+	      Factor[n] = dFactor1;
+	    }
+	  }
+	  done = TRUE; 
+	  for (n = 0; n < N_age; n++) {
+	    Factor[n] += dFactor[n];
+	    Factor[n] = MAX (0.0, Factor[n]);  /* negative star formation is unphysical */
+	    if ((Factor[n] > 0.0) && (fabs(dFactor[n]) == delta[n])) {
+	      delta[n] = MIN (0.1, delta[n] * 1.5);
+	    }
+	  }
+	  chisq_1 = get_chisq (Factor, i, j, m, &Ndof);
+	  /*
+	  fprintf (stderr, "%f %f ", chisq_0, chisq_1);
+	  for (n = 0; n < N_age; n++) {
+	    fprintf (stderr, "%f ", Factor[n]);
+	  }
+	  fprintf (stderr, "\n");
+	  fprintf (stderr, "                       ");
+	  for (n = 0; n < N_age; n++) {
+	    fprintf (stderr, "%f ", delta[n]);
+	  }
+	  fprintf (stderr, "\n");
+	  */
+	}
+
+	chisq_0 = chisq_1;
+	for (n = 0; n < N_age; n++) {
+	  temp_Factor = Factor[n];
+	  for (; chisq_1 < chisq_0 + 1; Factor[n] += 0.1*delta[n])
+	    chisq_1 = get_chisq (Factor, i, j, m, &Ndof);
+	  dFactor[n] = Factor[n] - temp_Factor;
+	  /* fprintf (stderr, "%d %f %f\n", n, Factor[n], dFactor[n]); */
+	  Factor[n] = temp_Factor;
+	  chisq_1 = chisq_0;
+	  for (; chisq_1 < chisq_0 + 1; Factor[n] -= 0.1*delta[n])
+	    chisq_1 = get_chisq (Factor, i, j, m, &Ndof);
+	  dFactor[n] =  0.5 * (dFactor[n] + temp_Factor  - Factor[n]);
+	  Factor[n] = temp_Factor;
+	}
+	if (tries == 100) {
+	  fprintf (stdout, "*** failed to converge *** \n");
+	}
+	fprintf (stdout, "%5.2f %6.2f %4.1f   %6.2f  %.0f ", Av[i], dist[j], alpha[m], chisq_1/Ndof, Ndof);
+	for (n = 0; n < N_age; n++) {
+	  fprintf (stdout, "%7.0f ", SFR[n]*Factor[n]);
+	}
+	fprintf (stdout, "\n");
+	fprintf (stdout, "                               ");
+	for (n = 0; n < N_age; n++) {
+	  fprintf (stdout, "%7.0f ", SFR[n]*dFactor[n]);
+	}
+	fprintf (stdout, "\n");
+      }
+    }
+  }
+}
+
+
+/*****************************************************************************/
+
+double get_chisq (Factor, AV, DIST, ALPHA, Ndof)
+double *Factor;
+int AV, DIST, ALPHA;
+double *Ndof;
+{
+
+  double chisq, sumterm, errterm;
+  int I, J, n;
+
+  *Ndof = chisq = 0.0;
+  for (I = 0; I < Nmass; I++) {
+    for (J = 0; J < N_age; J++) {
+      errterm = get_error (J, I);
+      sumterm = get_obs (J, I);
+      for (n = 0; n < N_age; n++) {
+	sumterm -= Factor[n]*get_fake (AV, DIST, ALPHA, n, J, I);
+	errterm += Factor[n]*get_fake_err (AV, DIST, ALPHA, n, J, I);
+      }
+      if (errterm > 0.0) {
+	chisq += SQ (sumterm) / errterm;
+	*Ndof += 1.0;
+      }
+    }
+  }
+  *Ndof -= N_age + N_Av + N_dist + N_alpha;
+
+  return (chisq);
+}
+
+/*****************************************************************************/
+
+load_parameters (argc, argv)
+int argc;
+char **argv;
+{
+
+  int i;
+  int Dfile, Ffile, Afile, MSfile;
+  FILE *f;
+  char line[1024];
+  double tmp;
+
+  if (argc < 2) {
+    fprintf (stderr, "USAGE: %s pfile\n", argv[0]);
+    exit (0);
+  }
+
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    fprintf (stderr, "parameter file %s not found\n", argv[1]);
+    exit (0);
+  }
+
+  COL1 = COL1 = NTRY = 0.0;
+  Dfile = Ffile = Afile = MSfile = FALSE;
+  while (scan_line (f, line) != EOF) {
+    if (!stripwhite (line)) continue;
+    if (line[0] == '#') continue;
+        
+    if (!strncmp (line, "ntry ", strlen ("ntry "))) {
+      dparse (&tmp, 2, line);
+      NTRY = tmp;
+    }
+  
+    if (!strncmp (line, "col1 ", strlen ("col1 "))) {
+      dparse (&tmp, 2, line);
+      COL1 = tmp;
+    }
+  
+    if (!strncmp (line, "col2 ", strlen ("col2 "))) {
+      dparse (&tmp, 2, line);
+      COL2 = tmp;
+    }
+  
+    if (!strncmp (line, "agefile ", strlen ("agefile "))) {
+      sscanf (line, "%*s %s", agefile);
+      fprintf (stderr, "agefile: %s\n", agefile);
+      Afile = TRUE;
+    }
+    
+    if (!strncmp (line, "massfile ", strlen ("massfile "))) {
+      sscanf (line, "%*s %s", massfile);
+      fprintf (stderr, "massfile: %s\n", massfile);
+      MSfile = TRUE;
+    }
+    
+    if (!strncmp (line, "datafile ", strlen ("datafile "))) {
+      sscanf (line, "%*s %s", datafile);
+      fprintf (stderr, "datafile: %s\n", datafile);
+      Dfile = TRUE;
+    }
+    
+    if (!strncmp (line, "fakefile ", strlen ("fakefile "))) {
+      sscanf (line, "%*s %s", fakefile);
+      fprintf (stderr, "fakefile: %s\n", fakefile);
+      Ffile = TRUE;
+    }
+    
+    if (COL1 && COL2 && NTRY && Dfile && Ffile && Afile && MSfile) {
+      return;
+    }
+  }
+  
+  if (!(COL1 && COL2 && NTRY && Dfile && Ffile && Afile && MSfile)) {
+    fprintf (stderr, "failed to get all parameter lines\n");
+    fprintf (stderr, "NTRY: %d\n", NTRY);
+    fprintf (stderr, "COL1: %d\n", COL1);
+    fprintf (stderr, "COL2: %d\n", COL2);
+    fprintf (stderr, "Dfile: %d\n", Dfile);
+    fprintf (stderr, "Ffile: %d\n", Ffile);
+    fprintf (stderr, "Afile: %d\n", Afile);
+    fprintf (stderr, "MSfile: %d\n", MSfile);
+    exit (0);
+  }
+  
+}
+
+/*****************************************************************************/
+
+read_datafiles ()
+{
+
+  int i;
+  char line[1024];
+
+  gfits_read_header (massfile, &mass_h);
+  gfits_read_matrix (massfile, &mass_i);
+  gfits_read_header (agefile, &age_h);
+  gfits_read_matrix (agefile, &age_i); 
+  MS_NX = mass_h.Naxis[0];
+  MS_NY = mass_h.Naxis[1];
+  Mbuffer = (float *)mass_i.buffer;
+  Abuffer = (float *)age_i.buffer;
+
+  gfits_scan (&mass_h, "RA_O",  "%lf", 1, &UV0);
+  gfits_scan (&mass_h, "RA_X",  "%lf", 1, &DUV);
+  gfits_scan (&mass_h, "DEC_O", "%lf", 1, &V0);
+  gfits_scan (&mass_h, "DEC_Y", "%lf", 1, &DV);
+
+  gfits_read_header (fakefile, &fake_h);
+  gfits_read_matrix (fakefile, &fake_m);
+  Nmass = fake_h.Naxis[0];
+  ALLOCATE (Mass, double, Nmass + 1);
+  for (i = 0; i < Nmass + 1; i++) {
+    sprintf (line, "PAR_1_%0d\0", i);
+    gfits_scan (&fake_h, line, "%lf", 1, &Mass[i]);
+  }
+  
+  N_age = fake_h.Naxis[1] - 1;
+  for (i = 0; i < N_age + 1; i++) {
+    sprintf (line, "PAR_2_%0d\0", i);
+    gfits_scan (&fake_h, line, "%lf", 1, &age[i]);
+  }
+
+  N_alpha = fake_h.Naxis[3];
+  for (i = 0; i < N_alpha; i++) {
+    sprintf (line, "PAR_4_%0d\0", i);
+    gfits_scan (&fake_h, line, "%lf", 1, &alpha[i]);
+  }
+
+  N_dist = fake_h.Naxis[4];
+  for (i = 0; i < N_dist; i++) {
+    sprintf (line, "PAR_5_%0d\0", i);
+    gfits_scan (&fake_h, line, "%lf", 1, &dist[i]);
+  }
+
+  N_Av = fake_h.Naxis[5];
+  for (i = 0; i < N_Av; i++) {
+    sprintf (line, "PAR_6_%0d\0", i);
+    gfits_scan (&fake_h, line, "%lf", 1, &Av[i]);
+  }
+
+  ALLOCATE (obsdata, double, Nmass*N_age*2);
+
+}
+ 
+
+/*****************************************************************************/
+
+double
+rnd_gauss (mean, sigma)
+double mean, sigma; 
+{
+
+  int i;
+  double y;
+
+  y = drand48();
+  i = NGAUSS*y;
+  y = gaussint[i]*sigma + mean;
+
+  return (y);
+
+}
+
+gauss_init ()
+{
+
+  int i;
+  long A, B;
+  double val, x, dx, dx1, dx2, dx3, df;
+  double mean, sigma;
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+
+  val = 0;
+  dx = 0.001;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+  mean = 0.0;
+  sigma = 1.0;
+
+  for (i = 0, x = -7.0; (i < NGAUSS) && (x < 7.0); x += dx)  {
+    df = (3.0*gaussian(x    , mean, sigma) + 
+	  9.0*gaussian(x+dx1, mean, sigma) +
+	  9.0*gaussian(x+dx2, mean, sigma) + 
+	  3.0*gaussian(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+    if (val > (i / (double) NGAUSS)) {
+      gaussint[i] = x + dx / 2.0;
+      i++;
+    }
+  }
+  return (x + dx / 2.0);
+}
+
+double 
+gaussian (x, mean, sigma) 
+double x, mean, sigma; 
+{
+
+  double f, X;
+
+  f = exp (-0.5 * SQ(x - mean) / SQ(sigma)) / sqrt(2 * M_PI * SQ(sigma));
+
+  return (f);
+
+}
+
+/*****************************************************************************/
+
+magtomass (A_V, Dist, Mag, Color, Noise, M, A, inflation)
+double A_V, Dist, Mag, Color, Noise;
+int *M, *A;
+double *inflation;
+{
+
+  int x, y, I, J, i, j, k;
+  double mass, Age, v, uv, Ngood;
+
+  *inflation = 0.0;
+  x = (Color - UV0 - 0.7*A_V) / DUV;   /* again, depends on U-V and extinction */
+  y = (Mag - Dist - V0 - A_V) / DV;
+  if ((x > 0) && (x < MS_NX) && (y > 0) && (y < MS_NY)) {
+    mass = Mbuffer[x + MS_NX*y];
+    if (mass > 0.0) {
+      Age = Abuffer[x + MS_NX*y];
+      if ((Age < age[0]) || (Age > age[N_age])) {
+	*inflation = 0.0;
+	return;
+      }
+      for (I = 0; (I < N_age) && (Age > age[I+1]); I++);
+      if ((mass < Mass[0]) || (mass > Mass[Nmass])) {
+	*inflation = 0.0;
+	return;
+      }
+      for (J = 0; (J < Nmass) && (mass > Mass[J+1]); J++);
+      Ngood = 1.0;
+      for (k = 0; k < NTRY; k++) {
+	v = rnd_gauss (Mag, Noise);
+	uv = rnd_gauss (Color, 1.4*Noise);
+	x = (uv - UV0 - 0.7*A_V) / DUV;
+	y = (v - V0 - Dist - A_V) / DV;
+	if ((x > 0) && (x < MS_NX) && (y > 0) && (y < MS_NY)) {
+	  mass = Mbuffer[x + MS_NX*y];
+	  if (mass > 0.0) {
+	    Age = Abuffer[x + MS_NX*y];
+	    for (i = -1; (i < N_age) && (Age > age[i+1]); i++);
+	    for (j = -1; (j < Nmass) && (mass > Mass[j+1]); j++);
+	    if ((i == I) && (j == J)) {
+	      Ngood += 1.0;
+	    }
+	  }
+	}
+      }
+      *inflation = (NTRY / Ngood);
+      *M = J;
+      *A = I;
+    }
+  }
+  if (mass == 0.0) *inflation = 0.0;
+}
+ 
+
+/*****************************************************************************/
+
+add_obs (AGE, MASS, value)
+int AGE, MASS;
+double value;
+{
+
+  if (value == 0.0) {
+    fprintf (stderr, "error: %d %d %f\n", AGE, MASS, value);
+  }
+  else {
+    obsdata[MASS + Nmass*(AGE        )] += value;
+    obsdata[MASS + Nmass*(AGE + N_age)] += 1.0;
+  }
+}
+
+
+/*****************************************************************************/
+
+double get_obs (AGE, MASS)
+int AGE, MASS;
+{
+
+  double value;
+
+  value = obsdata[MASS + Nmass*(AGE)];
+
+  return (value);
+
+}
+
+/*****************************************************************************/
+
+double get_error (AGE, MASS)
+int AGE, MASS;
+{
+
+  double value;
+
+  if (obsdata[MASS + Nmass*(AGE + N_age)] > 5) {
+    value = SQ (obsdata[MASS + Nmass*(AGE)]) / obsdata[MASS + Nmass*(AGE + N_age)];
+    return (value);
+  }
+  if ((obsdata[MASS + Nmass*(AGE + N_age)] < 1) && (obsdata[MASS + Nmass*(AGE)] > 0)) {
+    fprintf (stderr, "error: %d %d %e %e\n", AGE, MASS, 
+	     obsdata[MASS + Nmass*(AGE + 0)], obsdata[MASS + Nmass*(AGE + 1)]);
+  }
+  if (obsdata[MASS + Nmass*(AGE + N_age)] > 0) {
+    value = SQ (3.0 * obsdata[MASS + Nmass*(AGE)] / obsdata[MASS + Nmass*(AGE + N_age)]);
+    return (value);
+  }
+  value = 0.0;
+  return (value);
+
+}
+
+
+/*****************************************************************************/
+
+double get_fake (AV, DIST, ALPHA, AGE_IN, AGE_OUT, MASS)
+int AV, DIST, ALPHA, AGE_IN, AGE_OUT, MASS;
+{
+
+  double value;
+  float *buffer;
+
+  buffer = (float *)fake_m.buffer;
+  
+  /* elements in the (N_age) row of each Mass*Age matrix contain special numbers for the matrix */
+  value = buffer[MASS + Nmass*(AGE_OUT + (N_age + 1)*(AGE_IN + N_age*(ALPHA + N_alpha*(DIST + N_dist*(AV + N_Av*(0))))))];
+  /* buffer[MASS + Nmass*(AGE_OUT + (N_age + 1)*(AGE_IN + N_age*(ALPHA + N_alpha*(DIST + N_dist*(AV + N_Av*(1))))))] += 1.0; */
+
+  return (value);
+
+}
+
+/*****************************************************************************/
+
+double get_fake_err (AV, DIST, ALPHA, AGE_IN, AGE_OUT, MASS)
+int AV, DIST, ALPHA, AGE_IN, AGE_OUT, MASS;
+{
+
+  double value;
+  float *buffer;
+  int pix1, pix2;
+
+  buffer = (float *)fake_m.buffer;
+  
+  /* elements in the (N_age) row of each Mass*Age matrix contain special numbers for the matrix */
+  pix1 = MASS + Nmass*(AGE_OUT + (N_age + 1)*(AGE_IN + N_age*(ALPHA + N_alpha*(DIST + N_dist*(AV + N_Av*(0))))));
+  pix2 = MASS + Nmass*(AGE_OUT + (N_age + 1)*(AGE_IN + N_age*(ALPHA + N_alpha*(DIST + N_dist*(AV + N_Av*(1))))));
+
+  if (buffer[pix2] > 5) {
+    value = SQ (buffer[pix1]) / buffer[pix2];
+    return (value);
+  }
+
+  if (buffer[pix2] > 0) {
+    value = SQ (3.0 * buffer[pix1] / buffer[pix2]);
+    return (value);
+  }
+
+  value = 0.0;
+  return (value);
+
+}
+
+/***************************************************************************/
+
+int load_realstars () 
+
+{
+
+  char line[1024];
+  FILE *f;
+  double U;
+  int i, Nstars;
+
+  f = fopen (datafile, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "couldn't find data file %s\n", datafile);
+    exit (0);
+  }
+  
+  Nstars = 1000;
+  ALLOCATE (mag, double, Nstars);
+  ALLOCATE (color, double, Nstars);
+  ALLOCATE (noise, double, Nstars);
+  
+  /* need to fix allocations */
+  for (i = 0; scan_line (f, line) != EOF; i++) {
+    if (!stripwhite (line)) continue;
+    if (line[0] == '#') continue;
+    dparse (&mag[i], COL1, line);
+    dparse (&noise[i], (COL1+1), line);
+    dparse (&U, COL2, line);
+    color[i] = U - mag[i];
+    if (i == Nstars - 1) {
+      Nstars += 1000;
+      REALLOCATE (mag, double, Nstars);
+      REALLOCATE (color, double, Nstars);
+      REALLOCATE (noise, double, Nstars);
+    }
+  }
+
+  return (i);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fiximg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fiximg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fiximg.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+int main (int argc, char **argv) {
+
+  char *p;
+  int i, N;
+  Matrix matrix;
+  Header header;
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: fiximg (input) (output)\n");
+    exit (1);
+  }
+
+  /* read catalog header */
+  if (!gfits_read_header (argv[1], &header)) {
+    fprintf (stderr, "error loading file %s\n", argv[1]);
+    exit (1);
+  }
+  if (!gfits_read_matrix (argv[1], &matrix)) {
+    fprintf (stderr, "error loading file %s\n", argv[1]);
+    exit (1);
+  }
+
+  N = 0;
+  while (gfits_header_field (&header, "COMMENT", 1) != (char *) NULL) {
+    N++;
+    gfits_delete (&header, "COMMENT", 1);
+  }
+  fprintf (stderr, "deleted %d comments\n", N);
+  p = gfits_header_field (&header, "END", 1);
+  for (i = 3; i < header.size - (int) (p - header.buffer); i++) { 
+    p[i] = ' '; 
+  }
+
+  /* read catalog header */
+  if (!gfits_write_header (argv[2], &header)) {
+    fprintf (stderr, "error writing file %s\n", argv[2]);
+    exit (1);
+  }
+  if (!gfits_write_matrix (argv[2], &matrix)) {
+    fprintf (stderr, "error writing file %s\n", argv[2]);
+    exit (1);
+  }
+  exit (0);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fixsimple.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fixsimple.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/fixsimple.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include <ohana.h>
+
+main (int argc, char **argv) {
+
+  char *p;
+  int i, N;
+  Matrix matrix;
+  Header header;
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: fixsimple (input) (output)\n");
+    exit (1);
+  }
+
+  /* read header */
+  if (!gfits_read_header (argv[1], &header)) {
+    fprintf (stderr, "error loading file %s\n", argv[1]);
+    exit (1);
+  }
+
+  N = 0;
+  while (gfits_header_field (&header, "COMMENT", 1) != (char *) NULL) {
+    N++;
+    gfits_delete (&header, "COMMENT", 1);
+  }
+  fprintf (stderr, "deleted %d comments\n", N);
+  p = gfits_header_field (&header, "END", 1);
+  for (i = 3; i < header.size - (int) (p - header.buffer); i++) { 
+    p[i] = ' '; 
+  }
+
+  /* read catalog header */
+  if (!gfits_write_header (argv[2], &header)) {
+    fprintf (stderr, "error writing file %s\n", argv[2]);
+    exit (1);
+  }
+  if (!gfits_write_matrix (argv[2], &matrix)) {
+    fprintf (stderr, "error writing file %s\n", argv[2]);
+    exit (1);
+  }
+  exit (0);
+
+  f = fopen (argv[1], "r+");
+  if (f == NULL) {
+    fprintf (stderr, "can't open file for update %s\n", argv[1]);
+    exit (1);
+  }
+
+  fseek (f, skip, SEEK_SET);
+  status = fwrite (header.buffer, 1, header.size, f);
+  if (status != header.size) {
+    fprintf (stderr, "failed to write data to image header\n");
+    exit (1);
+  }
+  if (fclose (f)) {
+    fprintf (stderr, "error writing data to disk\n");
+    exit (1);
+  }
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/gtfringetable.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/gtfringetable.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/gtfringetable.c	(revision 22322)
@@ -0,0 +1,79 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+static char *version = "gtfringetable $Revision: 1.5 $";
+
+void get_version (int argc, char **argv, char *version);
+
+int main (int argc, char **argv) {
+
+  int i;
+  double binning;
+  float *xmin, *xmax, *ymin, *ymax;
+  FILE *f;
+  FTable table;
+  Header header;
+
+  get_version (argc, argv, version);
+  if (argc != 4) {
+    fprintf (stderr, "USAGE: (table) (ccd) (binning)\n");
+    exit (2);
+  }
+
+  binning = atof (argv[3]);
+
+  /* load data from fringe points file */
+  f = fopen (argv[1], "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "error opening fringe file %s\n", argv[1]);
+    exit (1);
+  }
+  table.header = &header;
+  if (!gfits_fread_ftable (f, &table, argv[2])) {
+    fprintf (stderr, "error reading table %s\n", argv[2]);
+    exit (1);
+  }
+  fclose (f);
+
+  gfits_get_table_column (&header, &table, "X_MIN", (void **) &xmin);
+  gfits_get_table_column (&header, &table, "X_MAX", (void **) &xmax);
+  gfits_get_table_column (&header, &table, "Y_MIN", (void **) &ymin);
+  gfits_get_table_column (&header, &table, "Y_MAX", (void **) &ymax);
+
+  for (i = 0; i < header.Naxis[1]; i++) {
+    fprintf (stdout, "%f %f\n", xmin[i] / binning, ymin[i] / binning);
+    fprintf (stdout, "%f %f\n", xmax[i] / binning, ymax[i] / binning);
+  }
+  exit (0);
+}
+
+/**** support functions ******/
+void get_version (int argc, char **argv, char *version) {
+
+  int N;
+  char *p, *q, *line;
+
+  if (get_argument (argc, argv, "-version")) {
+
+    N = strlen (version) + 2;
+    line = (char *) malloc (N);
+    bzero (line, N);
+
+    p = strstr (version, "$Revision: ");
+    if (p != (char *) NULL) 
+      p += strlen ("$Revision: ");
+    else
+      p = version;
+
+    q = strstr (p, "$");
+    if (q != (char *) NULL) 
+      N = q - p; 
+    else
+      N = strlen (p);
+
+    strncpy (line, p, N);
+
+    fprintf (stderr, "%s\n", line);
+    exit (2);
+  }
+}  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/gunlock.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/gunlock.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/gunlock.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include <stdio.h>
+
+main (int argc, char **argv) {
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: gunlock (filename)\n");
+    exit (1);
+  }
+
+  if (!clearlockfile (argv[1], -1, 1)) {
+    fprintf (stdout, "LOCK NOT REMOVED\n");
+    exit (1);
+  }
+  
+  fprintf (stdout, "LOCK REMOVED\n");
+  exit (0);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/magtoage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/magtoage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/magtoage.c	(revision 22322)
@@ -0,0 +1,221 @@
+# include <ohana.h>
+# define MMIN 1.0
+# define MMAX 120.0
+extern double drand48();
+extern double rnd_gauss();
+extern double rnd_integrate ();
+extern double gaussian ();
+
+double gaussian();
+double rnd_gauss();
+double rnd_integrate();
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+
+  Header mass_h, age_h;
+  Matrix mass_i, age_i;
+  double UV0, V0, DV, DUV;
+  double Uo, Vo;
+  double U, V, dUV, dU, dV, mass, age, d, a, da;
+  double M, dM, Ms, M2, m, dm, ldM, lMo, ldA, lAo;
+  double u, v, uv, Av, f, top, bot, alpha;
+  double **Masses, *maxmass, *minmass;
+  double **Ages;
+  int x, y, i, j, Ntry, NAGE, N;
+  char line[1024];
+  long A, B;
+  
+  lAo =   0.0;
+  ldA =   1.0;
+  NAGE =  320;
+  if (N = get_argument (argc, argv, "-age")) {
+    remove_argument (N, &argc, argv);
+    lAo = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ldA = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    NAGE = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 9) {
+    fprintf (stderr, "USAGE: magtoage massfile agefile d Av Ntry Uo Vo alpha ldM\n");
+    exit (0);
+  }
+  d = atof (argv[3]);
+  Av = atof (argv[4]);
+  Ntry = atof (argv[5]);
+  Uo = atof (argv[6]);
+  Vo = atof (argv[7]);
+  alpha = atof (argv[8]);
+  /*
+  ldM   = atof (argv[9]);
+  lMo = 0.0;
+  */
+  
+  gfits_read_header (argv[1], &mass_h);
+  gfits_read_matrix (argv[1], &mass_i);
+  gfits_read_header (argv[2], &age_h);
+  gfits_read_matrix (argv[2], &age_i); 
+
+  gfits_scan (&mass_h, "RA_O",  "%lf", 1, &UV0);
+  gfits_scan (&mass_h, "RA_X",  "%lf", 1, &DUV);
+  gfits_scan (&mass_h, "DEC_O", "%lf", 1, &V0);
+  gfits_scan (&mass_h, "DEC_Y", "%lf", 1, &DV);
+
+  ALLOCATE (Ages, double *, (Ntry+2));
+  ALLOCATE (maxmass, double, NAGE);
+  ALLOCATE (minmass, double, NAGE);
+  for (i = 0; i < Ntry + 2; i++) {
+    ALLOCATE (Ages[i], double, NAGE);
+    bzero (Ages[i], sizeof(double) * NAGE);
+  }
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+
+  /* find max and min masses for each age bin */
+  for (i = 0; i < NAGE; i++) {
+    maxmass[i] = 0.0;
+    minmass[i] = 1000.0;
+  }
+  for (x = 0; x < age_h.Naxis[0]; x++) {
+    for (y = 0; y < age_h.Naxis[1]; y++) {
+      mass = gfits_get_matrix_value (&mass_i, x, y);
+      if (mass == 0.0) continue;
+      age  = gfits_get_matrix_value (&age_i, x, y);
+      uv = x*DUV + UV0 + 0.7*Av;
+      v  = y*DV + V0 + Av + d;
+      u  = v + uv;
+      if ((u <= Uo) && (v <= Vo)) {
+	i = (age - lAo) / ldA;
+	if ((i > 0) && (i < NAGE)) {
+	  maxmass[i] = MAX(mass, maxmass[i]);
+	  minmass[i] = MIN(mass, minmass[i]);
+	}
+      }
+    }
+  }
+
+  fprintf (stderr, "beginning main loop\n");
+  while (scan_line (stdin, line) != EOF) {
+    if (!stripwhite (line)) continue;
+    if (line[0] == '#') continue;
+    dparse (&V, 8, line);
+    dparse (&dV, 9, line);
+    dparse (&U, 12, line);
+    dparse (&dU, 13, line);
+    dUV = sqrt (dU*dU + dV*dV);
+    for (i = 0; i < Ntry + 1; i++) {
+      if (i == 0) {
+	fprintf (stderr, ".");
+	v = V;
+	uv = U - V;
+      }
+      else {
+	v = rnd_gauss (V, dV);
+	uv = rnd_gauss ((U-V), dUV);
+      }
+      x = (uv - UV0 - 0.7*Av) / DUV;
+      y = (v - d - V0 - Av) / DV;
+      if ((x > 0) && (x < mass_h.Naxis[0]) &&
+	  (y > 0) && (y < mass_h.Naxis[1])) {
+	mass = gfits_get_matrix_value (&mass_i, x, y);
+	if (mass > 0.0) {
+	  age = gfits_get_matrix_value (&age_i, x, y); 
+	  j = (age - lAo) / ldA;
+	  if ((j >= 0) && (j < NAGE))
+	    Ages[i][j] += pow(mass, alpha);
+	}
+      }
+    }
+  }
+
+  top = pow (MMAX, 1.0 - alpha) - pow (MMIN, 1.0 - alpha);
+  fprintf (stderr, "finding scatter per bin\n");
+  for (j = 0; j < NAGE; j++) {
+    Ms = M2 = 0.0;
+    for (i = 1; i < Ntry + 1; i++) {
+      Ms += Ages[i][j];
+      M2 += (Ages[i][j]*Ages[i][j]);
+    }
+    M = dM = 0.0;
+    if (Ms > 0) {
+      M  = Ms / (1.0*Ntry);
+      dM = sqrt (M2 / (1.0*Ntry) - M*M);
+    }
+    Ages[1][j] = M;
+    Ages[2][j] = dM;
+    /*
+    a = pow (10.0, j*ldA + lAo);
+    da = pow (10.0, (j+1)*ldA + lAo) - a;
+    */
+    a = j*ldA + lAo;
+    da = ldA;
+    f = 0.0;
+    if (maxmass[j] > minmass[j]) {
+      bot = pow (maxmass[j], 1.0 - alpha) - pow (minmass[j], 1.0 - alpha);
+      if (bot != 0.0) 
+	f = top * top / ((1.0 - alpha) * bot * 2.30158509);
+    }
+    fprintf (stdout, "%d %f %f %f %f %f %f %f\n", j, a, da, Ages[0][j], Ages[1][j], Ages[2][j], f, Ages[1][j]*f/da);
+  }
+}
+
+double
+rnd_gauss (mean, sigma)
+double mean, sigma; 
+{
+
+  double range, x;
+
+  range = drand48();
+  x = rnd_integrate (*gaussian, range, mean, sigma);
+
+  return (x);
+
+}
+
+
+double 
+rnd_integrate (function, range, mean, sigma) 
+double (*function) ();
+double range, mean, sigma;
+{
+
+  double val, x, dx, dx1, dx2, dx3, df;
+
+  range += 0.0001;
+  val = 0;
+  dx = sigma / 10.0;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+
+  for (x = mean - 7*sigma; (val < range) && (x < mean + 7*sigma); x += dx)  {
+    df = (3.0*function(x    , mean, sigma) + 
+	  9.0*function(x+dx1, mean, sigma) +
+	  9.0*function(x+dx2, mean, sigma) + 
+	  3.0*function(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+  }
+  return (x + dx / 2.0);
+}
+
+double 
+gaussian (x, mean, sigma) 
+double x, mean, sigma; 
+{
+
+  double f, X;
+
+  f = exp (-0.5 * SQ(x - mean) / SQ(sigma)) / sqrt(2 * M_PI * SQ(sigma));
+
+  return (f);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/magtomass.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/magtomass.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/magtomass.c	(revision 22322)
@@ -0,0 +1,292 @@
+# include <ohana.h>
+extern double drand48();
+extern double rnd_gauss();
+extern double rnd_integrate ();
+extern double gaussian ();
+# define NEWWAY 1
+
+double gaussian();
+double rnd_gauss();
+double rnd_integrate();
+
+# define NGAUSS 2048
+double gaussint[NGAUSS];
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+
+  Header mass_h, age_h;
+  Matrix mass_i, age_i;
+  double UV0, V0, DV, DUV, ra, dec;
+  double U, V, dUV, dU, dV, mass, age, d;
+  double M, dM, Ms, M2, m, ldM, lMo, lAo, ldA, dlogM;
+  double v, uv, Av, eta, deta, Ngood;
+  double ***Masses, *maxmass, *minmass;
+  float *Mbuffer, *Abuffer;
+  int x, y, i, j, I, J, Ntry, dump, NMASS, N, NAGE, k, try;
+  int NX, NY;
+  char line[1024];
+  int col1, col2;
+  
+  dump = FALSE;
+  if (N = get_argument (argc, argv, "-dump")) {
+    remove_argument (N, &argc, argv);
+    dump = TRUE;
+  }
+
+  col1 = 8;
+  if (N = get_argument (argc, argv, "-col1")) {
+    remove_argument (N, &argc, argv);
+    col1 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  col2 = 12;
+  if (N = get_argument (argc, argv, "-col2")) {
+    remove_argument (N, &argc, argv);
+    col2 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  fprintf (stderr, "using mags in columns %d & %d\n", col1, col2);
+
+  gauss_init ();
+
+  lAo = 0.1;
+  ldA =  0.3;
+  NAGE = 10;
+  lMo = 0.0;
+  ldM =  0.05;
+  if (NEWWAY)
+    NMASS = 87;
+  else 
+    NMASS = 50;
+  if (N = get_argument (argc, argv, "-mass")) {
+    if (dump) {
+      fprintf (stderr, "-mass and -dump incompatible\n");
+      exit (0);
+    }
+    remove_argument (N, &argc, argv);
+    lMo = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ldM = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    NMASS = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    fprintf (stderr, "USAGE: magtomass massfile agefile d Av Ntry\n");
+    exit (0);
+  }
+  d = atof (argv[3]);
+  Av = atof (argv[4]);
+  Ntry = atof (argv[5]);
+
+  gfits_read_header (argv[1], &mass_h);
+  gfits_read_matrix (argv[1], &mass_i);
+  gfits_read_header (argv[2], &age_h);
+  gfits_read_matrix (argv[2], &age_i); 
+
+  gfits_scan (&mass_h, "RA_O",  "%lf", 1, &UV0);
+  gfits_scan (&mass_h, "RA_X",  "%lf", 1, &DUV);
+  gfits_scan (&mass_h, "DEC_O", "%lf", 1, &V0);
+  gfits_scan (&mass_h, "DEC_Y", "%lf", 1, &DV);
+  Mbuffer = (float *)mass_i.buffer;
+  Abuffer = (float *)age_i.buffer;
+  NX = mass_h.Naxis[0];
+  NY = mass_h.Naxis[1];
+  
+  ALLOCATE (Masses, double **, NAGE);
+  for (i = 0; i < NAGE; i++) {
+    ALLOCATE (Masses[i], double *, NMASS);
+    for (j = 0; j < NMASS; j++) {
+      ALLOCATE (Masses[i][j], double, 3);
+      Masses[i][j][0] = 0.0;
+	Masses[i][j][1] = 0.0;
+    }
+  }
+
+  fprintf (stderr, "beginning main loop\n");
+  while (scan_line (stdin, line) != EOF) {
+    if (!stripwhite (line)) continue;
+    if (line[0] == '#') continue;
+    /* appropriate columns hardwired */
+    dparse (&ra, 1, line);
+    dparse (&dec, 2, line);
+    dparse (&V, col1, line);
+    dparse (&dV, (col1+1), line);
+    dparse (&U, col2, line);
+    dparse (&dU, (col2+1), line);
+    dUV = sqrt (dU*dU + dV*dV);
+    x = (U-V - UV0 - 0.7*Av) / DUV;
+    y = (V - d - V0 - Av) / DV;
+    if ((x > 0) && (x < mass_h.Naxis[0]) && (y > 0) && (y < mass_h.Naxis[1])) {
+      mass = Mbuffer[x + NX*y];
+      /*      mass = gfits_get_matrix_value (&mass_i, x, y); */
+      if (mass > 0.0) {
+	age = Abuffer[x + NX*y];
+	fprintf (stdout, "%f %f %f %f %f %f\n", ra, dec, V, U-V, mass, age);
+      }
+    }
+  }
+}
+
+
+
+/*
+
+	**	age = gfits_get_matrix_value (&age_i, x, y);  **
+	I = MAX (MIN ((log10(age) - lAo) / ldA, NAGE - 1), 0); 
+	if (NEWWAY) {
+	  J = MAX (MIN ((4.5 - sqrt(10.0/mass)) / ldM, NMASS - 1), 0);
+	} else {
+	  J = MAX (MIN ((log10(mass) - lMo) / ldM, NMASS - 1), 0); 
+	}
+	Ngood = 1.0;
+	for (k = 0; k < Ntry; k++) {
+	  v = rnd_gauss (V, dV);
+	  uv = rnd_gauss ((U-V), dUV);
+	  x = (uv - UV0 - 0.7*Av) / DUV;
+	  y = (v - d - V0 - Av) / DV;
+	  if ((x > 0) && (x < mass_h.Naxis[0]) && (y > 0) && (y < mass_h.Naxis[1])) {
+	    mass = Mbuffer[x + NX*y];
+	    **	    mass = gfits_get_matrix_value (&mass_i, x, y); **
+	    if (mass > 0.0) {
+	      age = Abuffer[x + NX*y];
+	      ** age = gfits_get_matrix_value (&age_i, x, y);  **
+	      i = MAX (MIN ((log10(age) - lAo) / ldA, NAGE - 1), 0);
+	      if (NEWWAY) {
+		j = MAX (MIN ((4.5 - sqrt(10.0/mass)) / ldM, NMASS - 1), 0);
+	      } else {
+		j = MAX (MIN ((log10(mass) - lMo) / ldM, NMASS - 1), 0); 
+	      }
+	      if ((i == I) && (j == J)) {
+		Ngood += 1.0;
+	      }
+	    }
+	  }
+	}
+	Masses[I][J][0] += (Ntry / Ngood);
+	Masses[I][J][1] += 1.0;
+      }
+    }
+  }
+
+  fprintf (stderr, "finding scatter per bin\n");
+  fprintf (stdout, "# measured IMF \n");
+  fprintf (stdout, "# distance modulus = %5.2, extinction (A_V) = %5.2f\n", d, Av);
+  fprintf (stdout, "# reference mass and age filess: %s, %s\n", argv[1], argv[2]);
+  fprintf (stdout, "# eta = N(logM, logM+dlogM) / dlogM \\sim ln(10.0) * M dN/dM)\n");
+  fprintf (stdout, "# i  Mass   N(mass)  eta   d_eta\n");
+  for (i = 0; i < NAGE; i++) {
+    for (j = 0; j < NMASS; j++) {
+      if (NEWWAY) {
+	mass = 10.0 / SQ(4.5 - ldM*(j+0.5));
+	dlogM = 2.0 * (log10(4.5 - ldM*j) - log10(4.5 - ldM*(j+1)));
+      } else {
+	mass = pow (10.0, (lMo + ldM*(j+0.5)));
+	dlogM = ldM;
+      }
+      eta  = Masses[i][j][0] / dlogM;
+      deta = (Masses[i][j][1] > 0) ? eta / sqrt(Masses[i][j][1]) : 0.0;
+      age  = pow (10.0, (ldA*i + lAo));
+      M = (Masses[i][j][1] > 0) ? Masses[i][j][0]/Masses[i][j][1] : 0;  ** average correction factor **
+      dM = (Masses[i][j][1] > 0) ? Masses[i][j][0]/sqrt(Masses[i][j][1]) : 0;  ** average correction factor **
+      fprintf (stdout, "%4d %4d %5.1f %5.1f %7.1f %7.1f %6.2f %4.0f\n", i, j, mass, age, eta, deta, M, Masses[i][j][1]);
+    }
+  }
+}
+
+*/
+
+double
+rnd_gauss (mean, sigma)
+double mean, sigma; 
+{
+
+  int i;
+  double y;
+
+  y = drand48();
+  i = NGAUSS*y;
+  y = gaussint[i]*sigma + mean;
+
+  return (y);
+
+}
+
+
+gauss_init ()
+{
+
+  int i;
+  long A, B;
+  double val, x, dx, dx1, dx2, dx3, df;
+  double mean, sigma;
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+
+  val = 0;
+  dx = 0.001;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+  mean = 0.0;
+  sigma = 1.0;
+
+  for (i = 0, x = -7.0; (i < NGAUSS) && (x < 7.0); x += dx)  {
+    df = (3.0*gaussian(x    , mean, sigma) + 
+	  9.0*gaussian(x+dx1, mean, sigma) +
+	  9.0*gaussian(x+dx2, mean, sigma) + 
+	  3.0*gaussian(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+    if (val > (i / (double) NGAUSS)) {
+      gaussint[i] = x + dx / 2.0;
+      i++;
+    }
+  }
+  return (x + dx / 2.0);
+}
+
+double 
+rnd_integrate (function, range, mean, sigma) 
+double (*function) ();
+double range, mean, sigma;
+{
+
+  double val, x, dx, dx1, dx2, dx3, df;
+
+  range += 0.0001;
+  val = 0;
+  dx = sigma / 100.0;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+
+  for (x = mean - 7*sigma; (val < range) && (x < mean + 7*sigma); x += dx)  {
+    df = (3.0*function(x    , mean, sigma) + 
+	  9.0*function(x+dx1, mean, sigma) +
+	  9.0*function(x+dx2, mean, sigma) + 
+	  3.0*function(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+  }
+  return (x + dx / 2.0);
+}
+
+double 
+gaussian (x, mean, sigma) 
+double x, mean, sigma; 
+{
+
+  double f, X;
+
+  f = exp (-0.5 * SQ(x - mean) / SQ(sigma)) / sqrt(2 * M_PI * SQ(sigma));
+
+  return (f);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/mkfringetable.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/mkfringetable.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/mkfringetable.c	(revision 22322)
@@ -0,0 +1,207 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+static char *version = "mkfringetable $Revision: 1.9 $";
+
+void get_version (int argc, char **argv, char *version);
+
+int main (int argc, char **argv) {
+
+  int i, j, Npts, NPTS, Nccd, status;
+  char *layout, *config, *file;
+  char filter[64], start[64], stop[64], camera[64], *datestr;
+  char ImagetypeKeyword[64], CCDnumKeyword[64], FilterKeyword[64], CameraKeyword[64];
+  char *row, line[512], field[64], extname[64], filename[512];
+  double x, y, *xmin, *xmax, *ymin, *ymax;
+  FILE *f, *g;
+
+  Header header, theader;
+  Matrix matrix;
+  FTable table;
+
+  get_version (argc, argv, version);
+
+  /*** load ptolemy/elixir configuration info ***/
+  file = SelectConfigFile (&argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  ScanConfig (config, "IMAGETYPE-KEYWORD",           "%s", 0, ImagetypeKeyword);
+  ScanConfig (config, "CCDNUM-KEYWORD",              "%s", 0, CCDnumKeyword);
+  ScanConfig (config, "FILTER-KEYWORD",              "%s", 0, FilterKeyword);
+  ScanConfig (config, "CAMERA-KEYWORD",              "%s", 0, CameraKeyword);
+  free (config);
+  free (file);
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: (layout) (output)\n");
+    exit (2);
+  }
+
+  g = fopen (argv[2], "w");
+  if (g == (FILE *) NULL) { 
+    fprintf (stderr, "cannot open %s for output\n", argv[2]);
+    exit (1);
+  }
+
+  /* load info from layout file */
+  layout = LoadConfigFile (argv[1]);
+  if (layout == (char *) NULL) {
+    fprintf (stderr, "cannot open layout file %s\n", argv[1]);
+    exit (1);
+  }
+  status = TRUE;
+  status = status && (NULL == ScanConfig (layout, "NCCD",    "%d", 1, &Nccd));
+  status = status && (NULL == ScanConfig (layout, "FILTER",  "%s", 1, filter));
+  status = status && (NULL == ScanConfig (layout, "CAMERA",  "%s", 1, camera));
+  status = status && (NULL == ScanConfig (layout, "TVSTOP",  "%s", 1, stop));
+  status = status && (NULL == ScanConfig (layout, "TVSTART", "%s", 1, start));
+  if (!status) {
+    fprintf (stderr, "error in layout file\n");
+    fprintf (stderr, "Nccd: %d\n", Nccd);
+    fprintf (stderr, "filter: %s\n", filter);
+    fprintf (stderr, "camera: %s\n", camera);
+    fprintf (stderr, "tvstart: %s\n", start);
+    fprintf (stderr, "tvstop: %s\n", stop);
+    exit (1);
+  }
+
+  { /* save file creation date */ 
+    struct timeval now;
+    gettimeofday (&now, (struct timezone *) NULL);
+    datestr = ohana_sec_to_date (now.tv_sec);
+  }
+
+  /* make phu header (no matrix needed) */
+  gfits_init_header (&header);    
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  
+  gfits_modify (&header, "NEXTEND",        "%d", 1, Nccd);
+  gfits_modify (&header, "DATE",           "%s", 1, datestr);
+  gfits_modify (&header, "TVSTART",        "%s", 1, start);
+  gfits_modify (&header, "TVSTOP",         "%s", 1, stop);
+  gfits_modify (&header, "VERSION",        "%s", 1, version);
+  gfits_modify (&header, CameraKeyword,    "%s", 1, camera);
+  gfits_modify (&header, ImagetypeKeyword, "%s", 1, "FRPTS");
+  gfits_modify (&header, FilterKeyword,    "%s", 1, filter);
+    
+  gfits_fwrite_header  (g, &header);
+  gfits_fwrite_matrix  (g, &matrix);
+
+  ALLOCATE (xmin, double, 1);
+  ALLOCATE (xmax, double, 1);
+  ALLOCATE (ymin, double, 1);
+  ALLOCATE (ymax, double, 1);
+
+  for (i = 0; i < Nccd; i++) {
+
+    /* load entry from layout file */
+    sprintf (field, "CCD%02d", i);
+    if (!ScanConfig (layout, field, "%s", 1, line)) {
+      fprintf (stderr, "missing entry for %s\n", field);
+      exit (1);
+    }
+    sscanf (line, "%s %s", extname, filename);
+
+    /* load data from fringe points file */
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "error opening data file %s\n", filename);
+      exit (1);
+    }
+
+    NPTS = 100;
+    Npts = 0;
+    REALLOCATE (xmin, double, NPTS);
+    REALLOCATE (xmax, double, NPTS);
+    REALLOCATE (ymin, double, NPTS);
+    REALLOCATE (ymax, double, NPTS);
+
+    while (fscanf (f, "%lf %lf", &x, &y) == 2) {
+      xmin[Npts] = x;
+      ymin[Npts] = y;
+      if (fscanf (f, "%lf %lf", &x, &y) != 2) {
+	fprintf (stderr, "Funny line at %d?", Npts);
+	goto done;
+      }
+      xmax[Npts] = x;
+      ymax[Npts] = y;
+      Npts ++;
+      if (Npts == NPTS) {
+	NPTS += 100;
+	REALLOCATE (xmin, double, NPTS);
+	REALLOCATE (xmax, double, NPTS);
+	REALLOCATE (ymin, double, NPTS);
+	REALLOCATE (ymax, double, NPTS);
+      }
+    }
+  done:
+
+    /* create table header */
+    gfits_create_table_header (&theader, "TABLE", extname);
+      
+    /* add current date/time to header */
+    gfits_modify (&theader, "DATE",          "%s", 1, datestr);
+    gfits_modify (&theader, "TVSTART",       "%s", 1, start);
+    gfits_modify (&theader, "TVSTOP",        "%s", 1, stop);
+    gfits_modify (&header, CameraKeyword,    "%s", 1, camera);
+    gfits_modify (&header, ImagetypeKeyword, "%s", 1, "FRPTS");
+    gfits_modify (&header, FilterKeyword,    "%s", 1, filter);
+    gfits_modify (&header, CCDnumKeyword,    "%s", 1, extname);
+    
+    
+    /* define table layout */
+    gfits_define_table_column (&theader, "F6.1", "X_MIN", "min couple x", "pixels"); 
+    gfits_define_table_column (&theader, "F6.1", "Y_MIN", "min couple y", "pixels"); 
+    gfits_define_table_column (&theader, "F6.1", "X_MAX", "max couple x", "pixels"); 
+    gfits_define_table_column (&theader, "F6.1", "Y_MAX", "max couple y", "pixels"); 
+    
+    /* create table, add data values */
+    gfits_create_table (&theader, &table);
+  
+    for (j = 0; j < Npts; j++) {
+      row = gfits_table_print (&table, xmin[j], ymin[j], xmax[j], ymax[j]);
+      gfits_add_rows (&table, row, 1, strlen (row));
+    }
+
+    gfits_fwrite_Theader (g, &theader);
+    gfits_fwrite_table   (g, &table);
+  }
+  exit (0);
+}
+
+/**** support functions ******/
+void get_version (int argc, char **argv, char *version) {
+
+  int N;
+  char *p, *q, *line;
+
+  if (get_argument (argc, argv, "-version")) {
+
+    N = strlen (version) + 2;
+    line = (char *) malloc (N);
+    bzero (line, N);
+
+    p = strstr (version, "$Revision: ");
+    if (p != (char *) NULL) 
+      p += strlen ("$Revision: ");
+    else
+      p = version;
+
+    q = strstr (p, "$");
+    if (q != (char *) NULL) 
+      N = q - p; 
+    else
+      N = strlen (p);
+
+    strncpy (line, p, N);
+
+    fprintf (stderr, "%s\n", line);
+    exit (2);
+  }
+}  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/mkgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/mkgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/mkgauss.c	(revision 22322)
@@ -0,0 +1,88 @@
+# include <ohana.h>
+
+void   gauss_init ();
+double gaussian (double x, double mean, double sigma);
+double rnd_gauss (double mean, double sigma);
+
+/* NGAUSS defines the resolution of the distribution */
+# define NGAUSS 2048
+static double gaussint[NGAUSS];
+
+int main (int argc, char **argv) {
+
+  int i, Npts;
+  double mean, sigma, f;
+
+  if (argc != 4) {
+    fprintf (stderr, "USAGE: mkgauss (mean) (sigma) (npts)\n");
+    exit (2);
+  }
+
+  mean = atof (argv[1]);
+  sigma = atof (argv[2]);
+  Npts = atoi (argv[3]);
+
+  gauss_init ();
+
+  for (i = 0; i < Npts; i++) {
+    f = rnd_gauss (mean, sigma);
+    fprintf (stdout, "%f\n", f);
+  }
+
+  exit (0);
+}
+
+void gauss_init () {
+
+  int i;
+  long A, B;
+  double val, x, dx, dx1, dx2, dx3, df;
+  double mean, sigma;
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+
+  val = 0;
+  dx = 0.001;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+  mean = 0.0;
+  sigma = 1.0;
+
+  for (i = 0, x = -7.0; (i < NGAUSS) && (x < 7.0); x += dx)  {
+    df = (3.0*gaussian(x    , 0.0, 1.0) + 
+	  9.0*gaussian(x+dx1, 0.0, 1.0) +
+	  9.0*gaussian(x+dx2, 0.0, 1.0) + 
+	  3.0*gaussian(x+dx3, 0.0, 1.0)) * (dx1/8.0);
+    val += df;
+    if (val > (i / (double) NGAUSS)) {
+      gaussint[i] = x + dx / 2.0;
+      i++;
+    }
+  }
+  return;
+}
+
+double gaussian (double x, double mean, double sigma) {
+  double f;
+
+  f = exp (-0.5 * SQ(x - mean) / SQ(sigma)) / sqrt(2 * M_PI * SQ(sigma));
+  return (f);
+}
+
+double rnd_gauss (double mean, double sigma) {
+
+  int i;
+  double y;
+
+  i = 0;
+  while ((i == 0) || (i == NGAUSS - 1)) {
+    y = drand48();
+    i = MIN (NGAUSS - 1, MAX (0, NGAUSS*y));
+  }
+  y = gaussint[i]*sigma + mean;
+
+  return (y);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/mknames.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/mknames.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/mknames.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "ohana.h"
+extern double drand48();
+
+# define NFILTER 5
+static char filtlist[NFILTER][2] = {"B", "V", "R", "I", "Z"};
+
+int main (int argc, char **argv) {
+ 
+  FILE *f;
+  int j, rnd;
+  int ccd, state;
+  char type[64], filter[64], name[64];
+  struct timeval now;
+  struct tm *gmt;
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: mknames (fifo)\n");
+    exit (1);
+  }
+
+  /* lock fifo */
+  f = fsetlockfile (argv[1], 60.0, LCK_XCLD, &state);
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open fifo file\n");
+    fclearlockfile (argv[1], f, LCK_XCLD, &state);
+    exit (1);
+  }
+
+  gettimeofday (&now, (void *) NULL);
+  gmt = gmtime (&now.tv_sec);
+  srand48(now.tv_usec);
+  rnd = 1000*drand48();
+  sprintf (name, "%04d.%02d.%02d.%03d", gmt[0].tm_year + 1900, gmt[0].tm_mon + 1, gmt[0].tm_mday, rnd);
+
+  for (ccd = 0; ccd < 12; ccd++) {
+    strcpy (filter, "X");
+    strcpy (type, "bias");
+    fprintf (f, "%02d %s %s %s\n", ccd, type, filter, name);
+    strcpy (type, "dark");
+    fprintf (f, "%02d %s %s %s\n", ccd, type, filter, name);
+    strcpy (type, "flat");
+    for (j = 0; j < NFILTER; j++) {
+      strcpy (filter, filtlist[j]);
+      fprintf (f, "%02d %s %s %s\n", ccd, type, filter, name);
+    }
+  }
+  fclearlockfile (argv[1], f, LCK_XCLD, &state);
+  exit (0);
+}
+
+  /* 
+     
+     we need to generate a list of entries for 'elixir flips'
+     the entries look like:
+
+     (ccd) (type) (filter) (tend) (nameref)
+
+     (ccd)  - ranges from 00 to 11 
+     (type) - values are bias, dark, flat, ?
+     (filter) - for bias & dark = X
+                for flat = B,V,R,I,Z (Hon?, Hoff?)
+     (tend) = 'NOW' (probably don't need to generate this!)
+     (nameref) = SSSSSS (some string, needs to be unique... based on date?)
+
+  */
+		
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/posscontour.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/posscontour.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/posscontour.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include <stdio.h>
+# include <gfitsio.h>
+# include <math.h>
+# include <malloc.h>
+# include <string.h>
+# include <sys/time.h>
+# include <stdarg.h>
+# include <stdlib.h>
+
+# define TRUE (1)
+# define FALSE (0)
+# define DEG_RAD (57.29578)
+# define RAD_DEG (0.017453293)
+
+char PLTDECSN[5];
+
+double NX, NY, PLTRAH, PLTRAM, PLTRAS, PLTDECD, PLTDECM, PLTDECS, Do, Ro;
+double CNPIX1, CNPIX2, XPIXELSZ, YPIXELSZ, PPO1, PPO2, PPO3, PPO4, PPO5, PPO6;
+double AMDX1, AMDX2, AMDX3, AMDX4, AMDX5, AMDX6, AMDX7, AMDX8, AMDX9, AMDX10, AMDX11, AMDX12, AMDX13;
+double AMDY1, AMDY2, AMDY3, AMDY4, AMDY5, AMDY6, AMDY7, AMDY8, AMDY9, AMDY10, AMDY11, AMDY12, AMDY13;
+
+convert (ra, dec, X, Y) 
+     double *ra, *dec;
+     double X, Y;
+{
+
+  double x, y, Azeta, Aeta, eta, zeta;
+
+  x = (PPO3 - XPIXELSZ*(X+CNPIX1)) / 1000.0;
+  y = (YPIXELSZ*(Y+CNPIX2) - PPO6) / 1000.0;
+  Azeta = AMDX3 + x*(AMDX1 + x*(AMDX4 + AMDX7 + x*AMDX8)) + y*(AMDX2 + y*(AMDX6 + AMDX7 + y*AMDX11)) + x*y*(AMDX9*x + AMDX10*y + AMDX5) + x*(x*x+y*y)*(AMDX12 + AMDX13*(x*x+y*y));
+  Aeta  = AMDY3 + y*(AMDY1 + y*(AMDY4 + AMDY7 + y*AMDY8)) + x*(AMDY2 + x*(AMDY6 + AMDY7 + x*AMDY11)) + x*y*(AMDY9*y + AMDY10*x + AMDY5) + y*(x*x+y*y)*(AMDY12 + AMDY13*(x*x+y*y));
+  
+  eta = Aeta*RAD_DEG/3600.0;
+  zeta = Azeta*RAD_DEG/3600.0;
+  
+  *ra  = DEG_RAD*atan((zeta/cos(RAD_DEG*Do)) / (1.0 - eta*tan(RAD_DEG*Do))) + Ro;
+  *dec = DEG_RAD*atan(((eta + tan(RAD_DEG*Do)) * (cos (RAD_DEG*(*ra - Ro)))) / (1 - eta * tan(RAD_DEG*Do)));
+  
+}
+
+
+main (int argc, char **argv) 
+{
+
+  int i, status;
+  Header header;
+  double RA, DEC, ra, dec, dRA, dDEC;
+  double X, Y, dX, dY;
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: posscontour filename < contour.reg\n");
+    exit (0);
+  }
+
+  status = gfits_read_header (argv[1], &header);
+  if (!status) {
+    fprintf (stderr, "error opening file %s\n", argv[1]);
+    exit (0);
+  }
+
+  gfits_scan (&header, "NAXIS1", "%lf", 1, &NX);
+  gfits_scan (&header, "NAXIS2", "%lf", 1, &NY);
+  gfits_scan (&header, "PLTRAH", "%lf", 1, &PLTRAH);
+  gfits_scan (&header, "PLTRAM", "%lf", 1, &PLTRAM);
+  gfits_scan (&header, "PLTRAS", "%lf", 1, &PLTRAS);
+  gfits_scan (&header, "PLTDECD", "%lf", 1, &PLTDECD);
+  gfits_scan (&header, "PLTDECM", "%lf", 1, &PLTDECM);
+  gfits_scan (&header, "PLTDECS", "%lf", 1, &PLTDECS);
+  gfits_scan (&header, "PLTDECSN", "%s", 1, &PLTDECSN);
+  gfits_scan (&header, "CNPIX1", "%lf", 1, &CNPIX1);
+  gfits_scan (&header, "CNPIX2", "%lf", 1, &CNPIX2);
+  gfits_scan (&header, "XPIXELSZ", "%lf", 1, &XPIXELSZ);
+  gfits_scan (&header, "YPIXELSZ", "%lf", 1, &YPIXELSZ);
+  gfits_scan (&header, "PPO1", "%lf", 1, &PPO1);
+  gfits_scan (&header, "PPO2", "%lf", 1, &PPO2);
+  gfits_scan (&header, "PPO3", "%lf", 1, &PPO3);
+  gfits_scan (&header, "PPO4", "%lf", 1, &PPO4);
+  gfits_scan (&header, "PPO5", "%lf", 1, &PPO5);
+  gfits_scan (&header, "PPO6", "%lf", 1, &PPO6);
+  gfits_scan (&header, "AMDX1", "%lf", 1, &AMDX1);
+  gfits_scan (&header, "AMDX2", "%lf", 1, &AMDX2);
+  gfits_scan (&header, "AMDX3", "%lf", 1, &AMDX3);
+  gfits_scan (&header, "AMDX4", "%lf", 1, &AMDX4);
+  gfits_scan (&header, "AMDX5", "%lf", 1, &AMDX5);
+  gfits_scan (&header, "AMDX6", "%lf", 1, &AMDX6);
+  gfits_scan (&header, "AMDX7", "%lf", 1, &AMDX7);
+  gfits_scan (&header, "AMDX8", "%lf", 1, &AMDX8);
+  gfits_scan (&header, "AMDX9", "%lf", 1, &AMDX9);
+  gfits_scan (&header, "AMDX10", "%lf", 1, &AMDX10);
+  gfits_scan (&header, "AMDX11", "%lf", 1, &AMDX11);
+  gfits_scan (&header, "AMDX12", "%lf", 1, &AMDX12);
+  gfits_scan (&header, "AMDX13", "%lf", 1, &AMDX13);
+  gfits_scan (&header, "AMDY1", "%lf", 1, &AMDY1);
+  gfits_scan (&header, "AMDY2", "%lf", 1, &AMDY2);
+  gfits_scan (&header, "AMDY3", "%lf", 1, &AMDY3);
+  gfits_scan (&header, "AMDY4", "%lf", 1, &AMDY4);
+  gfits_scan (&header, "AMDY5", "%lf", 1, &AMDY5);
+  gfits_scan (&header, "AMDY6", "%lf", 1, &AMDY6);
+  gfits_scan (&header, "AMDY7", "%lf", 1, &AMDY7);
+  gfits_scan (&header, "AMDY8", "%lf", 1, &AMDY8);
+  gfits_scan (&header, "AMDY9", "%lf", 1, &AMDY9);
+  gfits_scan (&header, "AMDY10", "%lf", 1, &AMDY10);
+  gfits_scan (&header, "AMDY11", "%lf", 1, &AMDY11);
+  gfits_scan (&header, "AMDY12", "%lf", 1, &AMDY12);
+  gfits_scan (&header, "AMDY13", "%lf", 1, &AMDY13);
+  Ro = 15.0*(PLTRAH + PLTRAM/60.0 + PLTRAS/3600.0);
+  Do = (PLTDECD + PLTDECM/60.0 + PLTDECS/3600.0);
+  if (PLTDECSN[0] == '-') {
+    Do *= -1;
+  }
+
+  while (fscanf (stdin, "%*s %lf %lf %lf %lf", &X, &Y, &dX, &dY) != EOF) {
+    convert (&RA, &DEC, X, Y);
+    convert (&ra, &dec, (X+dX), (Y+dY));
+    dRA = ra - RA;
+    dDEC = dec - DEC;
+    fprintf (stdout, "LINE %f %f %f %f\n", RA, DEC, dRA, dDEC);
+  }
+  
+  gfits_free_header (&header);
+  
+}
+
+
+/* USAGE: posscontour filename < contour.reg > contour2.reg */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/smaeder.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/smaeder.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/smaeder.c	(revision 22322)
@@ -0,0 +1,516 @@
+# include <ohana.h>
+
+typedef struct {
+  double age;
+  double UV;
+  double V;
+} Model;
+
+typedef struct {
+  double mass;
+  int   Nmodel;
+  Model *model;
+} Track;
+
+int metal;
+double UV0, UV1, dUV;
+double V0, V1, dV;
+int dump, sdump;
+int col1, col2;
+char massfile[1024], agefile[1024];
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+
+  int Ntrack;
+  Track *track, *newtrack;
+
+  args (argc, argv);
+
+  fprintf (stderr, "reading in tracks...\n");
+  readmaeder (argv[1], &track, &Ntrack);
+
+  fprintf (stderr, "smoothing tracks...\n");
+  smoothtracks (&newtrack, track, Ntrack); 
+
+  fprintf (stderr, "interpolating tracks...\n");
+  interpolate_tracks (newtrack, Ntrack);
+
+}
+
+args (argc, argv) 
+int argc;
+char **argv;
+{
+
+  int N;
+
+  metal = 4; /* solar metal */
+  if (N = get_argument (argc, argv, "-metal")) {
+    remove_argument (N, &argc, argv);
+    metal = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  else {
+    fprintf (stderr, "using solar metallicity\n");
+  }
+  
+  dump = -1;
+  if (N = get_argument (argc, argv, "-dump")) {
+    remove_argument (N, &argc, argv);
+    dump = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  sdump = -1;
+  if (N = get_argument (argc, argv, "-sdump")) {
+    remove_argument (N, &argc, argv);
+    sdump = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  col1 = 3;
+  if (N = get_argument (argc, argv, "-col1")) {
+    remove_argument (N, &argc, argv);
+    col1 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  col2 = 5;
+  if (N = get_argument (argc, argv, "-col2")) {
+    remove_argument (N, &argc, argv);
+    col2 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  fprintf (stderr, "using mags in columns %d & %d\n", col1, col2);
+  
+  UV0 = -2.0;
+  UV1 =  2.0;
+  dUV =  0.01; 
+  if (N = get_argument (argc, argv, "-color")) {
+    remove_argument (N, &argc, argv);
+    UV0 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    UV1 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    dUV = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  V0 =   5.0;
+  V1 = -15.0;
+  dV =  -0.02; 
+  if (N = get_argument (argc, argv, "-mag")) {
+    remove_argument (N, &argc, argv);
+    V0 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    V1 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    dV = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  strcpy (massfile, "mass.fits");
+  if (N = get_argument (argc, argv, "-mass")) {
+    remove_argument (N, &argc, argv);
+    strcpy (massfile, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  strcpy (agefile, "age.fits");
+  if (N = get_argument (argc, argv, "-age")) {
+    remove_argument (N, &argc, argv);
+    strcpy (agefile, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  
+  if (argc < 2) {
+    fprintf (stderr, "USAGE: smaeder (filename) [options]\n");
+    fprintf (stderr, "  options:\n");
+    fprintf (stderr, "  [-metal N] (N = 0,1,2,3,4)\n");
+    fprintf (stderr, "  [-color min max delta] \n");
+    fprintf (stderr, "  [-mag min max delta] \n");
+    fprintf (stderr, "  [-mass file] \n");
+    fprintf (stderr, "  [-age file]\n");
+    exit (0);
+  }
+}
+
+/* read in the tracks for the requested metalicity */
+/* fairly dependent on the format of the files that Andy produces */
+readmaeder (maederfile, track, Ntrack)
+char  *maederfile;
+Track **track;
+int   *Ntrack;
+{
+
+  int i, j, m;
+  FILE *f;
+  char line[1024];
+  double U, V, ntrack, nmodel;
+
+  f = fopen (maederfile, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "couldn't find maeder tracks (%s)\n", maederfile);
+    exit (0);
+  }
+
+  for (m = 0; m < 5; m ++) {
+    scan_line (f, line);
+    dparse (&ntrack, 1, line);
+    *Ntrack = ntrack;
+    if (m == metal) {
+      ALLOCATE (track[0], Track, *Ntrack);
+    }
+    for (i = 0; i < ntrack; i++) {
+      scan_line (f, line);
+      dparse (&nmodel, 2, line);
+      if (m == metal) {
+	track[0][i].Nmodel = nmodel;
+	dparse (&track[0][i].mass, 1, line);
+	ALLOCATE (track[0][i].model, Model, track[0][i].Nmodel);
+      }
+      for (j = 0; j < nmodel; j++) {
+	scan_line (f, line);
+	if (m == metal) {
+	  dparse (&track[0][i].model[j].age, 1, line);
+	  track[0][i].model[j].age /= 1000000.0;   /* convert to Myrs */
+	  dparse (&U, col1, line);
+	  dparse (&V, col2, line);
+	  track[0][i].model[j].V = V;
+	  track[0][i].model[j].UV = U-V;
+	}
+      }
+    }
+  }
+  fclose (f);
+
+  if (dump > -1) {
+    if (dump >= *Ntrack) {
+      fprintf (stderr, "track %d not found\n", dump);
+      exit (0);
+    } 
+    for (i = 0; i < track[0][dump].Nmodel; i++) {
+      fprintf (stderr, "%d %d %f %e %f %f\n", 
+	       i, track[0][dump].Nmodel, track[0][dump].mass, track[0][dump].model[i].age,
+	       track[0][dump].model[i].V, track[0][dump].model[i].UV);
+    }
+    exit (0);
+  }
+
+}
+
+
+/* convert the tracks to smooth, monotonically increasing color tracks */
+smoothtracks (newtrack, track, Ntrack)
+Track **newtrack, *track;
+int Ntrack;
+{
+
+  int i, j, J1, J2, npts, done;
+  double minC, maxC, dadc, dmdc, age1, mag1, dcolor;
+  double resolution, c1, c2;
+  
+  fprintf (stderr, "smoothing..\n");
+  resolution = dUV;
+  ALLOCATE (newtrack[0], Track, Ntrack);
+  for (i = 0; i < Ntrack ; i++) {
+    /* find min & max colors */
+    minC = track[i].model[0].UV;
+    maxC = -1000;
+    for (j = 0; j < track[i].Nmodel; j++) {
+      maxC = MAX (track[i].model[j].UV, maxC);
+    }
+    npts = (int) ((maxC - minC) / resolution) + 1;
+    ALLOCATE (newtrack[0][i].model, Model, npts);
+    newtrack[0][i].mass = track[i].mass;
+    newtrack[0][i].Nmodel = npts;
+    for (j = 0; j < npts; j++) {
+      newtrack[0][i].model[j].UV = minC + resolution*j;
+    }
+    if (npts == 1) {
+      newtrack[0][i].model[0].age = track[i].model[0].age;
+      newtrack[0][i].model[0].V = track[i].model[0].V;
+      continue;
+    }
+    J1 = 0;
+    for (J2 = J1+1; (J2 < track[i].Nmodel) 
+	   && (track[i].model[J2].UV < track[i].model[J1].UV); J2++);
+    if (J2 == track[i].Nmodel) {
+      fprintf (stderr, "logical error 1\n");
+      exit (0);
+    }
+    dadc = (track[i].model[J2].age   - track[i].model[J1].age) /
+      (track[i].model[J2].UV - track[i].model[J1].UV);
+    dmdc = (track[i].model[J2].V   - track[i].model[J1].V) /
+      (track[i].model[J2].UV - track[i].model[J1].UV);
+    age1 = track[i].model[J1].age;
+    mag1 = track[i].model[J1].V;
+    for (j = 0; j < npts; j++) {
+      if (track[i].model[J2].UV < newtrack[0][i].model[j].UV) {
+	J1 = J2;
+	for (J2 = J1+1; (J2 < track[i].Nmodel) && ((track[i].model[J2].UV < track[i].model[J1].UV) || (track[i].model[J2].UV < newtrack[0][i].model[j].UV)); J2++) {
+	}
+	if (J2 == track[i].Nmodel) {
+	  fprintf (stderr, "logical error 2\n");
+	  exit (0);
+	}
+	dadc = (track[i].model[J2].age   - track[i].model[J1].age) /
+	       (track[i].model[J2].UV - track[i].model[J1].UV);
+	dmdc = (track[i].model[J2].V   - track[i].model[J1].V) /
+	       (track[i].model[J2].UV - track[i].model[J1].UV);
+	age1 = track[i].model[J1].age;
+	mag1 = track[i].model[J1].V;
+      }	
+      dcolor = newtrack[0][i].model[j].UV - track[i].model[J1].UV;
+      newtrack[0][i].model[j].age = dadc * dcolor + age1; 
+      newtrack[0][i].model[j].V = dmdc * dcolor + mag1; 
+    }
+  }
+  fprintf (stderr, "\n");
+
+  if (sdump > -1) {
+    if (sdump >= Ntrack) {
+      fprintf (stderr, "track %d not found\n", sdump);
+      exit (0);
+    }
+    for (i = 0; i < newtrack[0][sdump].Nmodel; i++) {
+      fprintf (stderr, "%d %f %e %f %f\n", 
+	       i, newtrack[0][sdump].mass, newtrack[0][sdump].model[i].age,
+	       newtrack[0][sdump].model[i].V, newtrack[0][sdump].model[i].UV);
+    }
+    exit (0);
+  }
+  
+}
+
+interpolate_tracks (newtrack, Ntrack)
+Track *newtrack;
+int Ntrack;
+{
+ 
+  Header mass_h, age_h;
+  Matrix mass_i, age_i;
+  double Ms, As, Vs, dM, dA, DV, m, a, a1, a2, m1, m2, v, dummy, tmp;
+  double d, minD, uv;
+  int i, j, k, J1, Sx, Sy, Ey;
+  int ii, I0, I1;
+
+  mass_h.bitpix = -32;
+  mass_h.Naxes = 2;
+  mass_h.Naxis[0] = (UV1 - UV0) / dUV;
+  mass_h.Naxis[1] = fabs((V1 - V0) / dV);
+  mass_h.bzero = 0.0;
+  mass_h.bscale = 1.0;
+  mass_h.unsign = FALSE;
+  mass_h.extend = FALSE;
+
+  age_h.bitpix = -32;
+  age_h.Naxes = 2;
+  age_h.Naxis[0] = (UV1 - UV0) / dUV;
+  age_h.Naxis[1] = fabs((V1 - V0) / dV);
+  age_h.bzero = 0.0;
+  age_h.bscale = 1.0;
+  age_h.unsign = FALSE;
+  age_h.extend = FALSE;
+
+  gfits_init_header (&mass_h);
+  gfits_create_header (&mass_h);
+  gfits_create_matrix (&mass_h, &mass_i);
+
+  gfits_init_header (&age_h);
+  gfits_create_header (&age_h);
+  gfits_create_matrix (&age_h, &age_i);
+
+  fprintf (stderr, "created FITS buffers\n");
+
+  for (i = 1; i < Ntrack; i++) {
+    fprintf (stderr, "%d  %d\n", i, newtrack[i].Nmodel);
+    J1 = 0;
+    for (j = 0; j < newtrack[i].Nmodel; j++) {
+      if ((newtrack[i].model[j].V < V0) &&
+	  (newtrack[i].model[j].V > V1) &&
+	  (newtrack[i].model[j].UV > UV0) &&
+	  (newtrack[i].model[j].UV < UV1)) {
+	while ((J1 < newtrack[i-1].Nmodel - 1) && ((int)((newtrack[i].model[j].UV  - UV0) / dUV) > (int)((newtrack[i-1].model[J1].UV  - UV0) / dUV))) {
+	  J1++;
+	}
+	uv = newtrack[i].model[j].UV;
+	Sx = (newtrack[i].model[j].UV  - UV0) / dUV;
+	Sy = (newtrack[i].model[j].V   -  V0) / dV;
+	if (newtrack[i].model[j].UV < newtrack[i-1].model[0].UV) {
+	  tmp = (uv - newtrack[i-1].model[0].UV) / (newtrack[i].model[0].UV -  newtrack[i-1].model[0].UV);
+	  Vs = newtrack[i-1].model[0].V +  tmp * (newtrack[i].model[0].V -  newtrack[i-1].model[0].V);
+	}
+	else {
+	  Vs = newtrack[i-1].model[J1].V;
+	}
+	Ey = (Vs - V0) / dV;
+	if ((j > 20) && (J1 > 20)) {
+	  Vs = newtrack[i-1].model[J1].V;
+	  Ms = newtrack[i-1].mass;
+	  As = newtrack[i-1].model[J1].age;
+	  Ey = (newtrack[i-1].model[J1].V - V0) / dV;
+	  DV = newtrack[i].model[j].V - Vs;
+	  dM = newtrack[i].mass - Ms;
+	  dA = newtrack[i].model[j].age - As;
+	  for (k = Sy; (k >= Ey) && (k >= 0); k--) {
+	    v = k*dV + V0;
+	    m = (v - Vs) * dM / DV + Ms;
+	    a = (v - Vs) * dA / DV + As;
+	    gfits_set_matrix_value (&mass_i, Sx, k, m);
+	    gfits_set_matrix_value (&age_i, Sx, k, a);
+	  }
+	}
+	else {
+	  for (k = Sy; (k >= Ey) && (k >= 0); k--) {
+	    v = k*dV + V0;
+	    minD = 1000;
+	    for (ii = 0; (ii < newtrack[i].Nmodel); ii++) {
+	      I0 = MIN (ii, 20);
+	      tmp = 1.0 / hypot(newtrack[i].model[ii].V - newtrack[i-1].model[I0].V, newtrack[i].model[ii].UV - newtrack[i-1].model[I0].UV);
+	      d = -tmp * ((newtrack[i].model[ii].UV - newtrack[i-1].model[I0].UV)*(v - newtrack[i-1].model[I0].V) - 
+			  (newtrack[i].model[ii].V - newtrack[i-1].model[I0].V)*(uv - newtrack[i-1].model[I0].UV));
+	      if ((d > 0) && (d < minD)) {
+		I1 = ii;
+		minD = d;
+	      }
+	      if (d > minD) {
+		break;
+	      }
+	    }
+	    I0 = MIN (I1, 20);
+	    tmp = ((newtrack[i].model[I1].UV - newtrack[i-1].model[I0].UV)*(uv - newtrack[i-1].model[I0].UV) + 
+		   (newtrack[i].model[I1].V - newtrack[i-1].model[I0].V)*(v - newtrack[i-1].model[I0].V)) / 
+	      hypot(newtrack[i].model[I1].V - newtrack[i-1].model[I0].V, newtrack[i].model[I1].UV - newtrack[i-1].model[I0].UV);
+	    d = tmp / hypot (newtrack[i].model[I1].V - newtrack[i-1].model[I0].V, newtrack[i].model[I1].UV - newtrack[i-1].model[I0].UV);
+	    dM = newtrack[i].mass - newtrack[i-1].mass;
+	    dA = newtrack[i].model[I1].age - newtrack[i-1].model[I0].age;
+	    m1 = d * dM + newtrack[i-1].mass;
+	    a1 = d * dA + newtrack[i-1].model[I0].age;
+	    /*	    fprintf (stderr, "%d  %f  %f  %f  %f  %f\n", I1, minD, d, tmp, dM, m1); */
+	    
+	    tmp = ((newtrack[i].model[I1+1].UV - newtrack[i-1].model[I0+1].UV)*(uv - newtrack[i-1].model[I0+1].UV) + 
+		   (newtrack[i].model[I1+1].V - newtrack[i-1].model[I0+1].V)*(v - newtrack[i-1].model[I0+1].V)) / 
+	      hypot(newtrack[i].model[I1+1].V - newtrack[i-1].model[I0+1].V, newtrack[i].model[I1+1].UV - newtrack[i-1].model[I0+1].UV);
+	    d = tmp / hypot (newtrack[i].model[I1+1].V - newtrack[i-1].model[I0+1].V, newtrack[i].model[I1+1].UV - newtrack[i-1].model[I0+1].UV);
+	    dM = newtrack[i].mass - newtrack[i-1].mass;
+	    dA = newtrack[i].model[I1+1].age - newtrack[i-1].model[I0+1].age;
+	    m2 = d * dM + newtrack[i-1].mass;
+	    a2 = d * dA + newtrack[i-1].model[I0+1].age;
+
+	    d = -((newtrack[i].model[I1].UV - newtrack[i-1].model[I0].UV)*(v - newtrack[i-1].model[I0].V) - 
+		 (newtrack[i].model[I1].V - newtrack[i-1].model[I0].V)*(uv - newtrack[i-1].model[I0].UV))
+	      / hypot(newtrack[i].model[I1].V - newtrack[i-1].model[I0].V, newtrack[i].model[I1].UV - newtrack[i-1].model[I0].UV);
+	    tmp = ((newtrack[i].model[I1+1].UV - newtrack[i-1].model[I0+1].UV)*(v - newtrack[i-1].model[I0+1].V) - 
+		 (newtrack[i].model[I1+1].V - newtrack[i-1].model[I0+1].V)*(uv - newtrack[i-1].model[I0+1].UV))
+	      / hypot(newtrack[i].model[I1+1].V - newtrack[i-1].model[I0+1].V, newtrack[i].model[I1+1].UV - newtrack[i-1].model[I0+1].UV);
+
+	    a = d * (a2 - a1) / (d + tmp) + a1;
+	    m = d * (m2 - m1) / (d + tmp) + m1;
+	    gfits_set_matrix_value (&mass_i, Sx, k, m);
+	    gfits_set_matrix_value (&age_i, Sx, k, a);
+	  }
+	}
+      }
+    }
+  }
+  /*   
+  for (i = 1; i < Ntrack; i++) {
+    fprintf (stderr, "%d  %d\n", i, newtrack[i].Nmodel);
+    for (j = 0; j < newtrack[i].Nmodel; j++) {
+      if ((newtrack[i].model[j].V < V0) &&
+	  (newtrack[i].model[j].V > V1) &&
+	  (newtrack[i].model[j].UV > UV0) &&
+	  (newtrack[i].model[j].UV < UV1)) {
+	Sx = (newtrack[i].model[j].UV  - UV0) / dUV;
+	Sy = (newtrack[i].model[j].V   -  V0) / dV;
+	gfits_set_matrix_value (&age_i, Sx, Sy, 1000.0);
+	gfits_set_matrix_value (&mass_i, Sx, Sy, 1000.0);
+      }
+    }
+  }
+  */
+
+  gfits_modify (&mass_h, "RA_O", "%lf", 1, UV0);
+  gfits_modify (&mass_h, "RA_X", "%lf", 1, dUV);
+  gfits_modify (&mass_h, "RA_Y", "%lf", 1, 0.0);
+  gfits_modify (&mass_h, "DEC_O", "%lf", 1, V0);
+  gfits_modify (&mass_h, "DEC_Y", "%lf", 1, dV);
+  gfits_modify (&mass_h, "DEC_X", "%lf", 1, 0.0);
+
+  gfits_write_header (massfile, &mass_h);
+  gfits_write_matrix (massfile, &mass_i);
+
+  gfits_modify (&age_h, "RA_O",  "%lf", 1, UV0);
+  gfits_modify (&age_h, "RA_X",  "%lf", 1, dUV);
+  gfits_modify (&age_h, "RA_Y",  "%lf", 1, 0.0);
+  gfits_modify (&age_h, "DEC_O", "%lf", 1, V0);
+  gfits_modify (&age_h, "DEC_Y", "%lf", 1, dV);
+  gfits_modify (&age_h, "DEC_X", "%lf", 1, 0.0);
+
+  gfits_write_header (agefile, &age_h);
+  gfits_write_matrix (agefile, &age_i);
+
+}
+
+/*
+ 	  minD = 1000;
+	  for (ii = 0; ii < newtrack[i-1].Nmodel; ii++) {
+	    d = hypot(newtrack[i-1].model[ii].V - v, newtrack[i-1].model[ii].UV - uv);
+	    if (d < minD) {
+	      I0 = ii;
+	      minD = d;
+	    }
+	  }
+	  minD = 1000;
+	  for (ii = 0; ii < newtrack[i].Nmodel; ii++) {
+	    d = hypot(newtrack[i].model[ii].V - v, newtrack[i].model[ii].UV - uv);
+	    if (d < minD) {
+	      I1 = ii;
+	      minD = d;
+	    }
+	  }
+ for (i = 1; i < Ntrack; i++) {
+    fprintf (stderr, "%d  %d\n", i, newtrack[i].Nmodel);
+    J1 = 0;
+    for (j = 0; j < newtrack[i].Nmodel; j++) {
+      if ((newtrack[i].model[j].V < V0) &&
+	  (newtrack[i].model[j].V > V1) &&
+	  (newtrack[i].model[j].UV > UV0) &&
+	  (newtrack[i].model[j].UV < UV1)) {
+	while ((J1 < newtrack[i-1].Nmodel - 1) && ((int)((newtrack[i].model[j].UV  - UV0) / dUV) > (int)((newtrack[i-1].model[J1].UV  - UV0) / dUV))) {
+	  J1++;
+	}
+	Sx = (newtrack[i].model[j].UV  - UV0) / dUV;
+	Sy = (newtrack[i].model[j].V   -  V0) / dV;
+	if (newtrack[i].model[j].UV < newtrack[i-1].model[0].UV) {
+	  tmp = (newtrack[i].model[j].UV - newtrack[i-1].model[0].UV) / (newtrack[i].model[0].UV -  newtrack[i-1].model[0].UV);
+	  Vs = newtrack[i-1].model[0].V +  tmp * (newtrack[i].model[0].V -  newtrack[i-1].model[0].V);
+	  As = newtrack[i-1].model[0].age + tmp * (newtrack[i].model[0].age -  newtrack[i-1].model[0].age);
+	  Ms = newtrack[i-1].mass + tmp * (newtrack[i].mass - newtrack[i-1].mass);
+	  Ey = (Vs  - V0) / dV;
+	}
+	else {
+	  Vs = newtrack[i-1].model[J1].V;
+	  Ms = newtrack[i-1].mass;
+	  As = newtrack[i-1].model[J1].age;
+	  Ey = (newtrack[i-1].model[J1].V - V0) / dV;
+	}
+	DV = newtrack[i].model[j].V - Vs;
+	dM = newtrack[i].mass - Ms;
+	dA = newtrack[i].model[j].age - As;
+	for (k = Sy; (k >= Ey) && (k >= 0); k--) {
+	  v = k*dV + V0;
+	  m = (v - Vs) * dM / DV + Ms;
+	  gfits_set_matrix_value (&mass_i, Sx, k, m);
+	  a = (v - Vs) * dA / DV + As;
+	  gfits_set_matrix_value (&age_i, Sx, k, a);
+	}
+      }
+    }
+  }
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/striphead.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/striphead.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/striphead.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include <ohana.h>
+
+main (argc, argv) 
+int argc;
+char *argv[];
+{
+
+  int i, j, status, N, FORCE;
+  char head[1000];
+  Header header;
+  char *p;
+  FILE *f;
+
+  if (get_argument (argc, argv, "-help") || get_argument (argc, argv, "-h")) {
+    fprintf (stderr, "USAGE: %s [-f] (filenames)\n", argv[0]);
+    exit (0);
+  }
+
+  if (N = get_argument (argc, argv, "-f")) {
+    remove_argument (N, &argc, argv);
+    FORCE = TRUE;
+  }
+  else 
+    FORCE = FALSE;
+  
+  if (argc == 1) {
+    fprintf (stderr, "USAGE: %s [-f] (filenames)\n", argv[0]);
+    exit (0);
+  }
+
+  for (i = 1; i < argc; i++) {
+    
+    strcpy (head, argv[i]);
+    if (strrchr(head, '.') !=NULL)
+      strcpy(strrchr(head, '.'), ".head");
+    else 
+      strcat(head, ".head");
+    
+    if (!FORCE) {
+      f = fopen (head, "r");
+      if (f != NULL) {
+	fprintf (stderr, "header file exists for %s, skipping\n", argv[i]);
+	fclose (f);
+	continue;
+      }
+    }
+    
+    if (!gfits_read_header (argv[i], &header)) {
+      fprintf (stderr, "failed to open file %s\n", argv[i]);
+      continue;
+    }
+    
+    for (j = 79; j < header.size; j+= 80) {
+      header.buffer[j] = 10;
+    }
+
+    if (!gfits_write_header (head, &header)) {
+      fprintf (stderr, "failed to write file %s\n", head);
+      continue;
+    }
+    
+    gfits_free_header (&header);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/usmaeder.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/usmaeder.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/misc/src/usmaeder.c	(revision 22322)
@@ -0,0 +1,194 @@
+# include <ohana.h>
+
+
+void main (argc, argv)
+int argc;
+char **argv;
+{
+
+  Header mass_h, age_h, UV_h, V_h;
+  Matrix mass_i, age_i, UV_i, V_i;
+  int x, y, X, Y, X0, X1, Y0, Y1, NAGE, NMASS, N;
+  double ldA, lAo, ldM, lMo, age, mass;
+  double v, uv, UV0, DUV, V0, DV, slope_x, slope_y, tmp;
+  
+  lAo =   0.0;
+  ldA =   0.01;
+  NAGE =  320;
+  if (N = get_argument (argc, argv, "-age")) {
+    remove_argument (N, &argc, argv);
+    lAo = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ldA = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    NAGE = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  lMo = -0.1;
+  ldM =  0.05;
+  NMASS = 50;
+  if (N = get_argument (argc, argv, "-mass")) {
+    remove_argument (N, &argc, argv);
+    lMo = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ldM = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    NMASS = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    fprintf (stderr, "USAGE %s massfile agefile colorfile magfile\n", argv[0]);
+    fprintf (stderr, "  options:\n");
+    fprintf (stderr, "  [-mass lMo ldM NMASS]\n");
+    fprintf (stderr, "  [-age  lAo ldA NAGE]\n");
+    exit (0);
+  }
+
+  gfits_read_header (argv[1], &mass_h);
+  gfits_read_matrix (argv[1], &mass_i);
+  gfits_read_header (argv[2], &age_h);
+  gfits_read_matrix (argv[2], &age_i); 
+
+  gfits_scan (&mass_h, "RA_O",  "%lf", 1, &UV0);
+  gfits_scan (&mass_h, "RA_X",  "%lf", 1, &DUV);
+  gfits_scan (&mass_h, "DEC_O", "%lf", 1, &V0);
+  gfits_scan (&mass_h, "DEC_Y", "%lf", 1, &DV);
+
+  V_h.bitpix = -32;
+  V_h.Naxes = 2;
+  V_h.Naxis[0] = NAGE;
+  V_h.Naxis[1] = NMASS;
+  V_h.bzero = 0.0;
+  V_h.bscale = 1.0;
+  V_h.unsign = FALSE;
+  V_h.extend = FALSE;
+
+  UV_h.bitpix = -32;
+  UV_h.Naxes = 2;
+  UV_h.Naxis[0] = NAGE;
+  UV_h.Naxis[1] = NMASS;
+  UV_h.bzero = 0.0;
+  UV_h.bscale = 1.0;
+  UV_h.unsign = FALSE;
+  UV_h.extend = FALSE;
+
+  gfits_init_header (&V_h);
+  gfits_create_header (&V_h);
+  gfits_create_matrix (&V_h, &V_i);
+
+  gfits_init_header (&UV_h);
+  gfits_create_header (&UV_h);
+  gfits_create_matrix (&UV_h, &UV_i);
+  fprintf (stderr, "created FITS buffers\n");
+
+  gfits_modify (&V_h, "RA_O", "%lf", 1, lAo);
+  gfits_modify (&V_h, "RA_X", "%lf", 1, ldA);
+  gfits_modify (&V_h, "RA_Y", "%lf", 1, 0.0);
+  gfits_modify (&V_h, "DEC_O", "%lf", 1, lMo);
+  gfits_modify (&V_h, "DEC_Y", "%lf", 1, ldM);
+  gfits_modify (&V_h, "DEC_X", "%lf", 1, 0.0);
+
+  gfits_modify (&UV_h, "RA_O",  "%lf", 1, lAo);
+  gfits_modify (&UV_h, "RA_X",  "%lf", 1, ldA);
+  gfits_modify (&UV_h, "RA_Y",  "%lf", 1, 0.0);
+  gfits_modify (&UV_h, "DEC_O", "%lf", 1, lMo);
+  gfits_modify (&UV_h, "DEC_Y", "%lf", 1, ldM);
+  gfits_modify (&UV_h, "DEC_X", "%lf", 1, 0.0);
+
+  /* the value of 100 is hard wired in this and the others as a bad color */
+
+  for (x = 0; x < NAGE; x++) {
+    for (y = 0; y < NMASS; y++) {
+      gfits_set_matrix_value (&UV_i, x, y, 100.0); 
+      gfits_set_matrix_value (&V_i, x, y, 100.0); 
+    }
+  }
+
+  for (x = 0; x < mass_h.Naxis[0]; x++) {
+    fprintf (stderr, ".");
+    for (y = 0; y < mass_h.Naxis[1]; y++) {
+      mass = gfits_get_matrix_value (&mass_i, x, y);
+      age = gfits_get_matrix_value (&age_i, x, y); 
+      if (mass > 0) {
+	X = (log10(age) - lAo) / ldA;
+	Y = (log10(mass) - lMo) / ldM;
+	if ((X >= 0) && (X < NAGE) &&
+	    (Y >= 0) && (Y < NMASS)) {
+	  uv = x*DUV + UV0;
+	  v  = y*DV + V0;
+	  gfits_set_matrix_value (&UV_i, X, Y, uv); 
+	  gfits_set_matrix_value (&V_i, X, Y, v); 
+	}
+      }
+    }
+  }
+  fprintf (stderr, "\n");
+
+  /* fix the holes */
+  for (x = 0; x < NAGE; x++) {
+    for (y = 0; y < NMASS; y++) {
+      uv = gfits_get_matrix_value (&UV_i, x, y);
+      if (uv == 100.0) {
+	/* find neighbors up and down */
+	X1 = NAGE; Y1 = NMASS;
+	X0 = Y0 = -1;
+	for (X = x + 1; X < NAGE; X++) {
+	  if ((gfits_get_matrix_value (&UV_i, X, y)) != 100.0) {
+	    X1 = X;
+	    break;
+	  }
+	}
+	for (X = x - 1; X >= 0; X--) {
+	  if ((gfits_get_matrix_value (&UV_i, X, y)) != 100.0) {
+	    X0 = X;
+	    break;
+	  }
+	}
+	for (Y = y + 1; Y < NMASS; Y++) {
+	  if ((gfits_get_matrix_value (&UV_i, x, Y)) != 100.0) {
+	    Y1 = Y;
+	    break;
+	  }
+	}
+	for (Y = y - 1; Y >= 0; Y--) {
+	  if ((gfits_get_matrix_value (&UV_i, x, Y)) != 100.0) {
+	    Y0 = Y;
+	    break;
+	  }
+	}
+	slope_x = slope_y = 100000.0;
+	if ((X1 < NAGE) && (X0 > -1)) {
+	  slope_x = (gfits_get_matrix_value (&UV_i, X1, y) - gfits_get_matrix_value (&UV_i, X0, y)) / (X1 - X0);
+	}
+	if ((Y1 < NMASS) && (Y0 > -1)) {
+	  slope_y = (gfits_get_matrix_value (&UV_i, x, Y1) - gfits_get_matrix_value (&UV_i, x, Y0)) / (Y1 - Y0);
+	}
+	if ((fabs(slope_x) < fabs(slope_y)) && (slope_x != 100000.0)) {
+	  uv = (x - X0) * slope_x + gfits_get_matrix_value (&UV_i, X0, y);
+	  tmp = (gfits_get_matrix_value (&V_i, X1, y) - gfits_get_matrix_value (&V_i, X0, y)) / (X1 - X0);
+	  v  = (x - X0) * tmp + gfits_get_matrix_value (&V_i, X0, y);
+	  gfits_set_matrix_value (&UV_i, x, y, uv); 
+	  gfits_set_matrix_value (&V_i, x, y, v); 
+	}	  
+	if ((fabs(slope_x) >= fabs(slope_y)) && (slope_y != 100000.0)) {
+	  uv = (y - Y0) * slope_y + gfits_get_matrix_value (&UV_i, x, Y0);
+	  tmp = (gfits_get_matrix_value (&V_i, x, Y1) - gfits_get_matrix_value (&V_i, x, Y0)) / (Y1 - Y0);
+	  v  = (y - Y0) * tmp + gfits_get_matrix_value (&V_i, x, Y0);
+	  gfits_set_matrix_value (&UV_i, x, y, uv); 
+	  gfits_set_matrix_value (&V_i, x, y, v); 
+	}	  
+      }
+    }
+  }
+	
+
+
+  gfits_write_header (argv[3], &V_h);
+  gfits_write_matrix (argv[3], &V_i);
+  gfits_write_header (argv[4], &UV_h);
+  gfits_write_matrix (argv[4], &UV_i);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/Makefile	(revision 22322)
@@ -0,0 +1,97 @@
+default: mosastro
+help:
+	@echo "make options: mosastro (default) mkstandards mkobs warptest"
+
+include ../../Makefile.System
+HOME    =       $(ROOT)/src/mosastro
+LIB	=	$(HOME)/lib
+BIN	=	$(HOME)/bin
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+mosastro: $(BIN)/mosastro.$(ARCH)
+mkstandards: $(BIN)/mkstandards.$(ARCH)
+mkobs: $(BIN)/mkobs.$(ARCH)
+warptest: $(BIN)/warptest.(ARCH)
+install: $(DESTBIN)/mosastro $(DESTBIN)/mosastro $(DESTBIN)/mosastro
+
+MOS = \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/ConvertMatch.$(ARCH).o \
+$(SRC)/FitChips.$(ARCH).o \
+$(SRC)/FitGradients.$(ARCH).o \
+$(SRC)/GetGradients.$(ARCH).o \
+$(SRC)/GetScatter.$(ARCH).o \
+$(SRC)/LoadStars.$(ARCH).o \
+$(SRC)/SaveResiduals.$(ARCH).o \
+$(SRC)/args.$(ARCH).o \
+$(SRC)/chips.$(ARCH).o \
+$(SRC)/clip.$(ARCH).o \
+$(SRC)/dump.$(ARCH).o \
+$(SRC)/field.$(ARCH).o \
+$(SRC)/fitpoly.$(ARCH).o \
+$(SRC)/getgsc.$(ARCH).o \
+$(SRC)/getptolemy.$(ARCH).o \
+$(SRC)/getstone.$(ARCH).o \
+$(SRC)/getusno.$(ARCH).o \
+$(SRC)/getusnob.$(ARCH).o \
+$(SRC)/greference.$(ARCH).o \
+$(SRC)/match.$(ARCH).o \
+$(SRC)/mkheader.$(ARCH).o \
+$(SRC)/mkmosaic.$(ARCH).o \
+$(SRC)/mkpolyterm.$(ARCH).o \
+$(SRC)/mosastro.$(ARCH).o \
+$(SRC)/output.$(ARCH).o \
+$(SRC)/parse_time.$(ARCH).o \
+$(SRC)/project.$(ARCH).o \
+$(SRC)/rfits.$(ARCH).o \
+$(SRC)/rtext.$(ARCH).o \
+$(SRC)/testcoords.$(ARCH).o \
+$(SRC)/transforms.$(ARCH).o \
+$(SRC)/wfits.$(ARCH).o \
+$(SRC)/wstars.$(ARCH).o
+
+STD = \
+$(SRC)/mkstandards.$(ARCH).o \
+$(SRC)/mkheader.$(ARCH).o \
+$(SRC)/random.$(ARCH).o \
+$(SRC)/wstars.$(ARCH).o \
+$(SRC)/wfits.$(ARCH).o
+
+OBS = \
+$(SRC)/args_obs.$(ARCH).o \
+$(SRC)/mkobs.$(ARCH).o \
+$(SRC)/mkheader.$(ARCH).o \
+$(SRC)/fakefield.$(ARCH).o \
+$(SRC)/greference.$(ARCH).o \
+$(SRC)/project.$(ARCH).o \
+$(SRC)/transforms.$(ARCH).o \
+$(SRC)/dump.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/get2mass.$(ARCH).o \
+$(SRC)/getgsc.$(ARCH).o \
+$(SRC)/getptolemy.$(ARCH).o \
+$(SRC)/getstone.$(ARCH).o \
+$(SRC)/gptolemy.$(ARCH).o \
+$(SRC)/gregions.$(ARCH).o \
+$(SRC)/gcatalog.$(ARCH).o \
+$(SRC)/getusno.$(ARCH).o \
+$(SRC)/mkpolyterm.$(ARCH).o \
+$(SRC)/random.$(ARCH).o \
+$(SRC)/wstars.$(ARCH).o
+
+$(MOS) : $(INC)/mosastro.h
+$(OBS) : $(INC)/mosastro.h
+$(STD) : $(INC)/mosastro.h
+
+$(BIN)/mosastro.$(ARCH) : $(MOS)
+$(BIN)/mkstandards.$(ARCH) : $(STD)
+$(BIN)/mkobs.$(ARCH) : $(OBS)
+$(BIN)/warptest.(ARCH) : $(SRC)/warptest.$(ARCH).o
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,17 @@
+
+- mosastro 1.3 : 2006.08.23
+  * converted to gfits APIs (forces libfits 1.6)
+  * converted to new DVO APIs (forces libdvo 1.3)
+  * removed old unused code
+
+- mosastro 1.2
+  * added USNO-B
+  * require min number on chip
+
+- mosastro 1.1
+  * minor cleanups
+
+2005.10.20 : mosastro-1-0
+
+  added support for new dvo load methods
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/astrom.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/astrom.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/astrom.txt	(revision 22322)
@@ -0,0 +1,99 @@
+mosaic astrometry keywords
+
+terms to model:
+
+boresite + projection
+Ro, Do, Theta, PS
+CTYPE, CRVAL, CRPIX, CD1_1
+
+distortion 
+A(i,j), D(i,j)
+WLi_j
+WMi_j
+
+chips
+WXi_j
+WYi_j
+
+-- these are not general since they don't allow for more than 2 dimensions
+-- these have the advantage of specifying which terms are available (WX0_2 is unambiguously X term for L^0M^2)
+
+- load the astrometry from a table (not very efficient unless all chip
+  terms are in the same table, which is an option)
+
+X = \Sum (WX_i_j) L^i M^j
+Y = \Sum (WY_i_j) L^i M^j
+
+L = \Sum (WL_i_j) P^i Q^j
+M = \Sum (WM_i_j) P^i Q^j
+
+P,Q = proj (R,D)[CTYPE, CRVAL, CRPIX, CDi_j]
+
+      
+Implementing the mosaic astrometry information in Elixir systems vs IPP.
+
+Elixir uses libohana/coordops to handle all coordinate
+transformations.  The coordinate structure (Coords) is part of the
+DVO/Images.h structure.  I cannot add an additional set of entries to
+that structure without breaking old DVO or other elixir code.  It is
+also not strictly relevant since the Coords structure is used to
+define the transformation of a single chip (and is sufficient to carry
+those terms up to 3rd order).  
+
+For elixir, we will need a two stage process, with one stage
+transforming the celestial coords to the focal plane, with PLY terms
+and a second to represent the chip to FP transform, with a varient on
+PLY terms.  (we can't use PLY since those use a projection, while we
+want a transform that only corrsponds to an Nth order polynomial onto
+a plane.  We should call this transform WRP, with ctype PLN-WRP and
+terms:
+
+crval1,2 = L,M coordinates for reference pixel
+crpix1,2 = X,Y coordinates for reference pixel
+cdelt1,2 = plate scale (should be  1,1 by default)
+etc.
+
+A third order warp couple to a third order distortion leads to 6th
+order terms in general.  
+
+--
+
+My plan is now to allow the dvo image table to represent chips in a mosaic
+with either the current (per-chip) entry or an entry per chip coupled
+to an entry per mosaic.  The chip image entries would have astrometric
+terms of up-to third order which transform from chip to focal plane,
+coupled in turn to a mosaic entry in the table giving astrometry from
+focal plane to the sky.  The chip-to-fp entries would be identified
+with astrometry CTYPE value of "-WRP" and the fp-to-sky would have
+entries with CTYPE value of "-MOS". 
+
+- does this information go in the header of each *.smp file?  
+- does this require us to package all chips in a single MEF smp
+- package?
+- mosastro would create the astrometry terms (per-chip and
+- per-mosaic), where does it store them?
+- minimal impact on addstar?
+
+- what are the implications for dvo?
+  - image I/O functions need to know about the mosaic
+
+- how do we match a chip to a mosaic?
+  chip is unique based on time and photcode.
+  - we could require the mosaic photcode entries to have values of
+  PRI/SEC
+
+: mosastro should construct a PHU element 654321oXX.smp which contains
+  the mosaic astrometry terms
+
+: alternatively: each .smp file could have a collection of header
+  keywords which specify the mosaic astrometry terms.
+
+: alternatively again, the 
+
+: photcode for the mosaic entry is CAMERA.FILTER.XX
+: addstar would have to have access to this file for each chip
+: addstar should not create a new mosaic entry
+
+: how do we handle GetCoords (which needs to construct TWO coords
+  structures for output in the case of a mosaic?)
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/notes.txt	(revision 22322)
@@ -0,0 +1,59 @@
+
+- need to update header astrometry 
+  - determine higher-order polynomial terms from fit?
+  - insert all distortion & warping terms into header?
+
+- better input parameters:
+  - use camera to define chips we expect to find
+  - camera -> default warp
+  - add dispersion stats to header in output (CERROR, CPRECISE)
+
+- allow chips to have full linear terms (after warp)
+
+- load 2mass, USNO SA data
+
+-----------------------------------------
+- load *.smp / *.cmp : 
+  chip[i].pt[j].x
+  chip[i].pt[j].y
+  chip[i].coords
+  (global Ro, Do = RA_DEG, DEC_DEG)
+
+- load usno in region
+  stars[i].r, stars[i].d
+
+- match usno:smp:
+  chip[i].pt[j].r, chip[i].pt[j].d
+
+- project ra,dec to p,q (arcsec on sky relative to Ro, Do)
+  chip[i].pt[j].p, chip[i].pt[j].q
+
+- init chip paramters:
+  chip[i].Px, chip[i].Py - parities
+  chip[i].Po, chip[i].Qo, chip[i].To
+
+  * p' = Px*(p - Po), q' = Py*(q - Qo)
+  * x = cos(To) p' + sin(To) q'
+  * y = cos(To) p' + sin(To) q'
+
+- other parameter:
+  * field.Ro, field.Do - not fitted
+  field.A[0] - field.A[5]
+  field.D[0] - field.D[5]
+
+
+  (r,d) -> field.coords -> (p,q) [arcsec from field center]
+  (p,q) -> field.A,D    -> (L,M) [pixels from field center]
+  (L,M) -> Px,Py,Po,Qo  -> (P,Q) [pixels from chip corner]
+  (P,Q) -> To		-> (x,y) [pixels on chip]
+
+  L = A[0] + A[1]*p + A[2]*q + A[3]*p^2 + A[4]*q^2 + A[5]*p*q
+  M = D[0] + D[1]*p + D[2]*q + D[3]*p^2 + D[4]*q^2 + D[5]*p*q
+
+  p' = Px*(L - Po)
+  q' = Py*(M - Qo)
+
+  x = cos(To) p' + sin(To) q'
+  y = cos(To) p' + sin(To) q'
+
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/update.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/update.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/doc/update.txt	(revision 22322)
@@ -0,0 +1,8 @@
+
+mosastro USAGE:
+
+  - mosastro (inglob) (outext) [-dist (distort)] [-chips (chipset)]
+
+  * given a collection of chip data, measure the complete model
+    parameters relative to reference.  output is individual chip files
+    with a new extension (and updated header contents)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/include/mosastro.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/include/mosastro.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/include/mosastro.h	(revision 22322)
@@ -0,0 +1,197 @@
+# include <ohana.h>
+# include <dvo.h>
+
+# define PSASTRO_MODE 0
+
+typedef struct {
+  double R, D;  /* Sky Coords    - degrees */
+  double P, Q;  /* Tangent Plane - pixels  */
+  double L, M;  /* Focal Plane   - pixels  */
+  double X, Y;  /* Chip Coords   - pixels  */
+  double Mag, dMag;
+  int mask;
+} StarData;
+
+typedef struct {
+  double RA[2], DEC[2];
+  double Area;
+  char *name;
+} CatStats;
+
+typedef struct {
+  char *file;
+  int Nstars;
+  int NX, NY;
+  StarData *stars;
+  Header header;
+  Header theader;
+  Matrix matrix;
+
+  char *buffer;
+  int Nbuffer;
+  int FITS;
+
+  int Nmatch;
+  StarData *raw, *ref;
+
+  Coords coords;
+  Coords map;
+} Chip;
+
+typedef struct {
+  double Ro, Do, PSx, PSy, To;
+  double Rmin, Rmax, Dmin, Dmax;
+  int    Norder;
+  double **A;
+  double **D;
+  Coords project;
+  Coords distort;
+  int    fit;
+} Field;
+
+typedef struct {
+  double *dPdL;
+  double *dPdM;
+  double *dQdL;
+  double *dQdM;
+  double *Lo;
+  double *Mo;
+  int Npts;
+} Gradients;
+
+typedef struct {
+  double Rraw, Draw; /* Sky Coords    - degrees */
+  float Praw, Qraw;  /* Tangent Plane - pixels  */
+  float Lraw, Mraw;  /* Focal Plane   - pixels  */
+  float Xraw, Yraw;  /* Chip Coords   - pixels  */
+
+  double Rref, Dref; /* Sky Coords    - degrees */
+  float Pref, Qref;  /* Tangent Plane - pixels  */
+  float Lref, Mref;  /* Focal Plane   - pixels  */
+  float Xref, Yref;  /* Chip Coords   - pixels  */
+
+  float Mcat, dMcat;
+  float Minst, dMinst;
+  char mask;
+} MatchData;
+
+int  ChipOrder;
+int  Nchip;
+Chip *chip;
+Field field;
+double Year;  /** carried for precession - probably put this in chip data **/
+double RADIUS; /** raw / ref matching radius (pixels on Focal Plane) **/
+double SIGMA;
+double SIGMA_LIM;
+double IMAG_MIN;
+double IMAG_MAX;
+double INST_BRIGHT;
+double ZERO_POINT;
+
+char REFCAT[256];
+char CATMODE[16];    /* raw, mef, split, mysql */
+char CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char ExptimeKeyword[256];
+char DateKeyword[256];
+char DateMode[256];
+char UTKeyword[256];
+char MJDKeyword[256];
+char JDKeyword[256];
+int VERBOSE;
+int NO_CHIPS;
+int SAVE_RESID;
+
+char CATDIR[256];
+char GSCFILE[256];
+char GSCDIR[256];
+char USNO_A_DIR[256];
+char USNO_B_DIR[256];
+char TWO_MASS_DIR[256];
+char ASTROM_CATDIR[256];
+char StoneRegions[256];
+
+char *FIELD;
+char *CHIPS;
+char *OUTPUT;
+char *DUMP;
+char *FOCAL_PLANE;
+
+/*** mosastro prototypes ***/
+void       ChipToFP           PROTO((StarData *stars, int Nstars, Coords *coords));
+void       ChipToSky          PROTO((StarData *stars, int Nstars, Coords *coords));
+int        ClipOnFP           PROTO((double Nsigma));
+void       ConfigInit         PROTO((int *argc, char **argv));
+int        ConvertMatch       PROTO((MatchData *data, int size, int nitems));
+void       FPtoChip           PROTO((StarData *stars, int Nstars, Coords *coords));
+void       FPtoTP             PROTO((StarData *stars, int Nstars, Coords *coords));
+void       FitChip            PROTO((StarData *raw, StarData *ref, int Nmatch, Coords *coords));
+void       FitChipLinear      PROTO((StarData *raw, StarData *ref, int Nmatch, Coords *coords));
+void       FitChipResid       PROTO((StarData *raw, StarData *ref, int Nmatch, Coords *coords));
+void       FitChips           PROTO((int Norder));
+void       FitGradients       PROTO((Gradients *grad));
+void       GetConfig          PROTO((char *config, char *field, char *format, int N, void *ptr));
+Gradients *GetGradients       PROTO(());
+double     GetScatter         PROTO((int *Nscatter, double *DL, double *DM, int bright));
+int        LoadStars          PROTO((int Nfile, char **file));
+void       SaveResiduals      PROTO((FILE *f, Header *header));
+void       SkyToTP            PROTO((StarData *stars, int Nstars, Coords *coords));
+void       TPtoFP             PROTO((StarData *stars, int Nstars, Coords *coords));
+void       TPtoSky            PROTO((StarData *stars, int Nstars, Coords *coords));
+void       add_to_regions     PROTO((CatStats *area));
+void       area_of_region     PROTO((CatStats *region));
+void       args               PROTO((int *argc, char **argv));
+int        deproject_raw      PROTO(());
+int        deproject_stars    PROTO(());
+int        dump_grads         PROTO((Gradients *grad, char *filename));
+int        dump_match         PROTO(());
+int        dump_rawstars      PROTO(());
+int        dump_refcat        PROTO((StarData *refcat, int Nrefcat));
+int        dump_stars         PROTO((FILE *f, StarData *stars, int Nstars));
+int        fake_field         PROTO((double RA, double DEC));
+void       field_combine      PROTO(());
+void       field_stats        PROTO(());
+int        find_dec_bands     PROTO((CatStats *area));
+void       fit_add            PROTO((double x1, double y1, double x2, double y2));
+void       fit_apply_coords   PROTO((Coords *coords));
+void       fit_apply_grads    PROTO((Coords *distort, Coords *project, int term));
+void       fit_correct_grads  PROTO((Gradients *in, Gradients *out, int term));
+void       fit_eval           PROTO(());
+void       fit_free           PROTO(());
+void       fit_init           PROTO((int order));
+int        gaussj             PROTO((double **a, int n, double **b, int m));
+StarData  *gcatalog           PROTO((char *filename, int *Nstars));
+StarData  *get2mass           PROTO((CatStats *catstats, int *NSTARS));
+StarData  *getgsc             PROTO((CatStats *catstats, int *NSTARS));
+StarData  *getptolemy         PROTO((CatStats *catstats, int *NSTARS));
+StarData  *getstone           PROTO((CatStats *input, int *nstars));
+StarData  *getusno            PROTO((CatStats *catstats, int *Nstars));
+StarData  *getusnob           PROTO((CatStats *catstats, int *Nstars));
+StarData  *gptolemy           PROTO((char *filename, int *NSTARS));
+StarData  *greference         PROTO((int *Nrefcat));
+CatStats  *gregions           PROTO((CatStats *patch, int *nregion));
+int        init_chips         PROTO(());
+int        init_field         PROTO(());
+void       init_regions       PROTO(());
+int        load_chips         PROTO((char *filename));
+int        load_field         PROTO((char *filename));
+int        load_ra_blocks     PROTO((int Ndec, CatStats *area));
+int        match              PROTO((StarData *refcat, int Nrefcat));
+Header    *mkheader           PROTO((int Nx, int Ny, int Nstars, Coords *coords));
+Header    *mkmosaic           PROTO((int Nx, int Ny, int Nstars, Coords *coords));
+int        mkpolyterm         PROTO((int n, int m));
+int        mkvector           PROTO((int n, int m, int norder));
+void       output             PROTO((char *ext, char *phu));
+int        parse_GSC_line     PROTO((CatStats *tregion, char *line));
+e_time     parse_time         PROTO((Header *header));
+void       print_help         PROTO(());
+int        project_ref        PROTO(());
+int        project_refcat     PROTO((StarData *refcat, int Nrefcat));
+int        project_stars      PROTO(());
+int        rfits              PROTO((Chip *mychip));
+int        rtext              PROTO((Chip *mychip));
+void       set_catalog        PROTO((char *catdir));
+int        sortthree          PROTO((double *X, double *Y, int *Z, int N));
+void       uppercase          PROTO((char *string));
+void       wchip              PROTO((char *filename, Chip *data));
+void       wfits              PROTO((char *filename, SMPData *stars, int Nstars, Header *header));
+void       wstars             PROTO((char *filename, SMPData *stars, int Nstars, Header *header));
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "mosastro.h"
+
+void ConfigInit (int *argc, char **argv) {
+  
+  char *config, *file;
+
+  VERBOSE = TRUE;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  ScanConfig (config, "GSCFILE",          "%s",  0, GSCFILE);
+
+  GetConfig (config, "ASTRO_REFCAT",      "%s",  0, REFCAT);
+
+  /* possible sources of astrometric reference data */
+  if (!ScanConfig (config, "USNO_A_DIR",             "%s",  0, USNO_A_DIR)) {
+    ScanConfig (config, "USNO_CDROM",             "%s",  0, USNO_A_DIR);
+  }
+  ScanConfig (config, "USNO_B_DIR",       "%s",  0, USNO_B_DIR);
+  ScanConfig (config, "GSCDIR",           "%s",  0, GSCDIR);
+  ScanConfig (config, "STONE_DIR",        "%s",  0, StoneRegions);
+  ScanConfig (config, "2MASS_DIR",        "%s",  0, TWO_MASS_DIR);
+  ScanConfig (config, "ASTROM_CATDIR",    "%s",  0, ASTROM_CATDIR);
+
+  /* abstracted header keywords - used by parse_time */
+  ScanConfig (config, "DATE-KEYWORD",     "%s",  0, DateKeyword);
+  ScanConfig (config, "DATE-MODE",        "%s",  0, DateMode);
+  ScanConfig (config, "UT-KEYWORD",       "%s",  0, UTKeyword);
+  ScanConfig (config, "MJD-KEYWORD",      "%s",  0, MJDKeyword);
+  ScanConfig (config, "JD-KEYWORD",       "%s",  0, JDKeyword);
+  ScanConfig (config, "EXPTIME-KEYWORD",  "%s",  0, ExptimeKeyword);
+
+  GetConfig  (config, "RADIUS",           "%lf", 0, &RADIUS);
+  GetConfig  (config, "SIGMA_LIM",        "%lf", 0, &SIGMA_LIM);
+  ScanConfig (config, "ZERO_PT",          "%lf", 0, &ZERO_POINT);
+
+  ScanConfig (config, "INST_MAG_MIN",     "%lf", 0, &IMAG_MIN);
+  ScanConfig (config, "INST_MAG_MAX",     "%lf", 0, &IMAG_MAX);
+  ScanConfig (config, "INST_BRIGHT",      "%lf", 0, &INST_BRIGHT);
+  
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  free (config);
+  free (file);
+
+  return;
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/ConvertMatch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/ConvertMatch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/ConvertMatch.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include "mosastro.h"
+
+# define SWAP_BYTE(X) \
+  tmp = byte[X+0]; byte[X+0] = byte[X+1]; byte[X+1] = tmp;
+# define SWAP_WORD(X) \
+  tmp = byte[X+0]; byte[X+0] = byte[X+3]; byte[X+3] = tmp; \
+  tmp = byte[X+1]; byte[X+1] = byte[X+2]; byte[X+2] = tmp;
+# define SWAP_DBLE(X) \
+  tmp = byte[X+0]; byte[X+0] = byte[X+7]; byte[X+7] = tmp; \
+  tmp = byte[X+1]; byte[X+1] = byte[X+6]; byte[X+6] = tmp; \
+  tmp = byte[X+2]; byte[X+2] = byte[X+5]; byte[X+5] = tmp; \
+  tmp = byte[X+3]; byte[X+3] = byte[X+4]; byte[X+4] = tmp;
+
+# ifdef linux
+# define BYTE_SWAP
+# endif
+
+# ifdef sid
+# define BYTE_SWAP
+# endif
+
+# ifdef dec
+# define BYTE_SWAP
+# endif
+
+# define MATCH_SIZE 104
+
+int ConvertMatch (MatchData *data, int size, int nitems) {
+
+  int i;
+  unsigned char *byte, tmp;
+
+# ifdef BYTE_SWAP
+
+  if (size != MATCH_SIZE) {
+    fprintf (stderr, "mismatch in type sizes (MatchData) %d vs %d\n", size, MATCH_SIZE);
+    return (FALSE);
+  }
+
+  byte = (unsigned char *) data;
+  for (i = 0; i < nitems; i++, byte += size) {
+    SWAP_DBLE (0);   /* R */
+    SWAP_DBLE (8);   /* D */
+    SWAP_WORD (16);  /* P */
+    SWAP_WORD (20);  /* Q */
+    SWAP_WORD (24);  /* L */
+    SWAP_WORD (28);  /* M */
+    SWAP_WORD (32);  /* X */
+    SWAP_WORD (36);  /* Y */
+
+    SWAP_DBLE (40);  /* R */
+    SWAP_DBLE (40);  /* D */
+    SWAP_WORD (56);  /* P */
+    SWAP_WORD (60);  /* Q */
+    SWAP_WORD (64);  /* L */
+    SWAP_WORD (68);  /* M */
+    SWAP_WORD (72);  /* X */
+    SWAP_WORD (76);  /* Y */
+
+    SWAP_WORD (80);  /* Mcat */
+    SWAP_WORD (84);  /* dMcat */
+    SWAP_WORD (88);  /* Minst */
+    SWAP_WORD (92);  /* dMinst */
+  }
+  return (TRUE);
+
+# else
+  return (TRUE);
+# endif  
+} 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/FitChips.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/FitChips.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/FitChips.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "mosastro.h"
+
+void FitChips (int Norder) {
+
+  int i;
+
+  for (i = 0; i < Nchip; i++) {
+    chip[i].map.Npolyterms = Norder;
+    FitChip (chip[i].raw, chip[i].ref, chip[i].Nmatch, &chip[i].map);
+  }
+  deproject_raw ();
+  project_ref ();
+}
+
+void FitChip (StarData *raw, StarData *ref, int Nmatch, Coords *coords) {
+
+  int i;
+
+  fit_init (coords[0].Npolyterms);
+  for (i = 0; i < Nmatch; i++) {
+    if (raw[i].mask) continue;
+    fit_add (raw[i].X, raw[i].Y, ref[i].L, ref[i].M);
+  }
+  fit_eval ();
+  fit_apply_coords (coords);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/FitGradients.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/FitGradients.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/FitGradients.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "mosastro.h"
+
+/* fit dPdL, etc vs L,M */
+void FitGradients (Gradients *grad) {
+
+  int i, Norder;
+  Gradients grfix;
+
+  ALLOCATE (grfix.dPdL, double, grad[0].Npts);
+  ALLOCATE (grfix.dPdM, double, grad[0].Npts);
+  ALLOCATE (grfix.dQdL, double, grad[0].Npts);
+  ALLOCATE (grfix.dQdM, double, grad[0].Npts);
+
+  ALLOCATE (grfix.Lo,   double, grad[0].Npts);
+  ALLOCATE (grfix.Mo,   double, grad[0].Npts);
+  grfix.Npts = grad[0].Npts;
+
+  /* where do we set field.distort.Npolyterms? */
+  Norder = 3;
+
+  fit_init (Norder - 1);
+  for (i = 0; i < grad[0].Npts; i++) {
+    fit_add (grad[0].Lo[i], grad[0].Mo[i], grad[0].dPdL[i], grad[0].dPdM[i]);
+  }
+  fit_eval ();
+  fit_apply_grads (&field.distort, &field.project, 0);
+  fit_correct_grads (grad, &grfix, 0);
+  fit_free ();
+
+  fit_init (Norder - 1);
+  for (i = 0; i < grad[0].Npts; i++) {
+    fit_add (grad[0].Lo[i], grad[0].Mo[i], grad[0].dQdL[i], grad[0].dQdM[i]);
+  }
+  fit_eval ();
+  fit_apply_grads (&field.distort, &field.project, 1);
+  fit_correct_grads (grad, &grfix, 1);
+  fit_free ();
+
+  /* use new model of field to get TP & FP coords */
+  deproject_raw ();
+  project_ref ();
+
+  if ((DUMP != NULL) && !strcmp (DUMP, "grads")) {
+    dump_grads (&grfix, "gradfix.dat");
+    exit (0);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/GetGradients.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/GetGradients.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/GetGradients.c	(revision 22322)
@@ -0,0 +1,108 @@
+# include "mosastro.h"
+
+/* measure local gradient vs field position : dP, dQ = f(L,M) 
+   gradient is measured in small boxes across the field */
+Gradients *GetGradients () {
+
+  int i, j, nx, ny, Nval, Nx, Ny, Npts, DX, DY;
+  double **a, **b;
+  double L, M, dP, dQ;
+  double Xmin, Xmax, Ymin, Ymax;
+
+  Gradients *grad;
+
+  ALLOCATE (grad, Gradients, 1);
+
+  /** this should not be hard-wired **/
+  Nx = 2;
+  Ny = 2;
+
+  ALLOCATE (grad[0].dPdL, double, Nchip*Nx*Ny);
+  ALLOCATE (grad[0].dPdM, double, Nchip*Nx*Ny);
+  ALLOCATE (grad[0].dQdL, double, Nchip*Nx*Ny);
+  ALLOCATE (grad[0].dQdM, double, Nchip*Nx*Ny);
+
+  ALLOCATE (grad[0].Lo, double, Nchip*Nx*Ny);
+  ALLOCATE (grad[0].Mo, double, Nchip*Nx*Ny);
+  
+  ALLOCATE (a, double *, 3);
+  ALLOCATE (b, double *, 3);
+  for (i = 0; i < 3; i++) {
+    ALLOCATE (a[i], double, 3);
+    ALLOCATE (b[i], double, 2);
+  }
+
+  Nval = 0;
+  for (i = 0; i < Nchip; i++) {
+    DX = chip[i].NX / Nx;
+    DY = chip[i].NY / Ny;
+    for (nx = 0; nx < Nx; nx++) {
+      for (ny = 0; ny < Ny; ny++) {
+	Xmin = nx*DX;
+	Xmax = Xmin + DX;
+	Ymin = ny*DY;
+	Ymax = Ymin + DY;
+
+	for (j = 0; j < 3; j++) {
+	  bzero (a[j], 3*sizeof(double));
+	  bzero (b[j], 2*sizeof(double));
+	}
+  
+	/* find local gradient in limited box */
+	for (j = 0; j < chip[i].Nmatch; j++) {
+	  if (chip[i].raw[j].X < Xmin) continue;
+	  if (chip[i].raw[j].X > Xmax) continue;
+	  if (chip[i].raw[j].Y < Ymin) continue;
+	  if (chip[i].raw[j].Y > Ymax) continue;
+	  
+	  L   = chip[i].ref[j].L;
+	  M   = chip[i].ref[j].M;
+	  dP  = chip[i].ref[j].P - chip[i].raw[j].P;
+	  dQ  = chip[i].ref[j].Q - chip[i].raw[j].Q;
+	  
+	  a[0][0] += 1;
+	  a[1][0] += L;
+	  a[2][0] += M;
+	  a[1][1] += L*L;
+	  a[2][1] += L*M;
+	  a[2][2] += M*M;
+
+	  b[0][0] += dP;
+	  b[1][0] += dP*L;
+	  b[2][0] += dP*M;
+
+	  b[0][1] += dQ;
+	  b[1][1] += dQ*L;
+	  b[2][1] += dQ*M;
+
+	}
+	/* fitting dP = a[0][0] + a[1][0]*L + a[2][0]*M
+	           dQ = a[0][1] + a[1][1]*L + a[2][1]*M 
+	*/
+	a[0][1] = a[1][0];
+	a[0][2] = a[2][0];
+	a[1][2] = a[2][1];
+
+	Npts = a[0][0];
+	grad[0].Lo[Nval] = a[1][0] / a[0][0];
+	grad[0].Mo[Nval] = a[2][0] / a[0][0];
+	/* point-weighted average coordinate */
+
+	if (Npts < 5) continue;
+	if (!dgaussjordan (a, b, 3, 2)) continue;
+
+	/* we only care about the slopes, not the offsets */
+	grad[0].dPdL[Nval] = b[1][0];
+	grad[0].dPdM[Nval] = b[2][0];
+	grad[0].dQdL[Nval] = b[1][1];
+	grad[0].dQdM[Nval] = b[2][1];
+
+	Nval ++;
+
+      }
+    }
+  }
+  grad[0].Npts = Nval;
+  if ((DUMP != NULL) && !strcmp (DUMP, "grads")) dump_grads (grad, "grads.dat");
+  return (grad);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/GetScatter.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/GetScatter.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/GetScatter.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "mosastro.h"
+
+/* measure scatter on the focal plane */
+double GetScatter (int *Nscatter, double *DL, double *DM, int bright) {
+
+  int i, j, Ntotal;
+  double dL, dM, dL2, dM2, dl, dm, dR;
+  StarData *raw, *ref;
+
+  Ntotal = 0.0;
+  dL = dL2 = dM = dM2 = 0;
+  for (i = 0; i < Nchip; i++) {
+    raw = chip[i].raw;
+    ref = chip[i].ref;
+    for (j = 0; j < chip[i].Nmatch; j++) {
+      if (raw[j].mask) continue;
+      if (bright && (raw[j].Mag > INST_BRIGHT)) continue;
+      dl = raw[j].L - ref[j].L;
+      dm = raw[j].M - ref[j].M;
+      dL  += dl;
+      dL2 += SQ(dl);
+      dM  += dm;
+      dM2 += SQ(dm);
+      Ntotal ++;
+    }
+  }
+  dL = sqrt (fabs(dL2 / Ntotal - (dL*dL) / (Ntotal*Ntotal)));
+  dM = sqrt (fabs(dM2 / Ntotal - (dM*dM) / (Ntotal*Ntotal)));
+  dR = hypot(dL, dM) * 3600.0 * field.project.cdelt1;
+  *Nscatter = Ntotal;
+  *DL = dL;
+  *DM = dM;
+  return (dR);
+}
+
+/* sigma is returned in arcsec, dL, dM are in pixels */
+/* return dL and dM independently as well */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/LoadStars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/LoadStars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/LoadStars.c	(revision 22322)
@@ -0,0 +1,77 @@
+# include "mosastro.h"
+
+int LoadStars (int Nfile, char **file) {
+
+  int i, itmp, status, extend;
+  time_t tsval;
+  struct tm *tmval;
+
+  ALLOCATE (chip, Chip, Nfile);
+
+  /* Nchip is number of valid chips */
+
+  Nchip = 0;
+  for (i = 0; i < Nfile; i++) {
+    chip[Nchip].file = strcreate (file[i]);
+
+    /* load header */
+    if (!gfits_read_header (chip[Nchip].file, &chip[Nchip].header)) {
+      fprintf (stderr, "ERROR: can't read header for %s\n", file[i]);
+      free (chip[Nchip].file);
+      continue;
+    }
+
+    /* get image dimensions */
+    gfits_scan (&chip[Nchip].header, "NAXIS1",   "%d", 1, &chip[Nchip].NX);
+    gfits_scan (&chip[Nchip].header, "NAXIS2",   "%d", 1, &chip[Nchip].NY);
+
+    /* get astrometry information */
+    if (!GetCoords (&chip[Nchip].coords, &chip[Nchip].header)) {
+      fprintf (stderr, "ERROR: no astrometric solution in header\n");
+      free (chip[Nchip].file);
+      gfits_free_header (&chip[Nchip].header);
+      continue;
+    }
+    while (chip[Nchip].coords.crval1 < 0) chip[Nchip].coords.crval1 += 360.0;
+    while (chip[Nchip].coords.crval1 > 360.0) chip[Nchip].coords.crval1 -= 360.0;
+
+    itmp = 0;
+    gfits_scan (&chip[Nchip].header, "NASTRO",   "%d", 1, &itmp);
+    if (itmp == 0) {
+      fprintf (stderr, "ERROR: bad astrometric solution in header %s\n", file[i]);
+      free (chip[Nchip].file);
+      gfits_free_header (&chip[Nchip].header);
+      continue;
+    }
+
+    /* get time info */
+    tsval = parse_time (&chip[Nchip].header);
+    tmval = gmtime (&tsval);
+    Year = tmval[0].tm_year;
+
+    extend = FALSE;
+    gfits_scan (&chip[Nchip].header, "EXTEND",  "%t", 1, &extend);
+    if (extend) {
+      status = rfits (&chip[Nchip]);
+    } else {
+      status = rtext (&chip[Nchip]);
+    }
+    if (!status) {
+      /* skip on failure */
+      free (chip[Nchip].file);
+      gfits_free_header (&chip[Nchip].header);
+      continue;
+    }
+
+    if (VERBOSE) fprintf (stderr, "loaded %d stars from %s\n", chip[Nchip].Nstars, chip[Nchip].file);
+
+    Nchip ++;
+  }
+ 
+  if (Nchip == 0) {
+    fprintf (stderr, "no valid data in chip files, exiting\n");
+    exit (1);
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/SaveResiduals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/SaveResiduals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/SaveResiduals.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include "mosastro.h"
+
+void SaveResiduals (FILE *f, Header *header) {
+
+  int i, j, N, Nmatch;
+  MatchData *match;
+  Matrix matrix;
+  Header theader;
+  FTable table;
+
+  header[0].extend = TRUE;
+  header[0].Naxes = 0;
+  gfits_modify (header, "NAXIS",   "%d", 1, 0);
+  gfits_modify (header, "EXTEND",  "%t", 1, TRUE);
+  gfits_modify (header, "NEXTEND", "%d", 1, 1);
+
+  /* add in some keywords to specify the datatype & software version? */
+
+  /* create (empty) data matrix */
+  gfits_create_matrix (header, &matrix);
+    
+  /* create bintable header */
+  gfits_create_table_header (&theader, "BINTABLE", "MOSASTRO_RESIDUALS");
+
+  /* define bintable layout */
+  gfits_define_bintable_column (&theader, "D",    "R_RAW",      "ra (raw)",             "degrees",                        1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "D",    "D_RAW",      "dec (raw)",            "degrees",                        1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "P_RAW",      "P coord (raw)",        "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "Q_RAW",      "Q coord (raw)",        "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "L_RAW",      "L coord (raw)",        "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "M_RAW",      "M coord (raw)",        "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "X_RAW",      "X coord (raw)",        "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "Y_RAW",      "Y coord (raw)",        "pixels",                         1.0, 0.0); 
+
+  gfits_define_bintable_column (&theader, "D",    "R_REF",      "ra (ref)",             "degrees",                        1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "D",    "D_REF",      "dec (ref)",            "degrees",                        1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "P_REF",      "P coord (ref)",        "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "Q_REF",      "Q coord (ref)",        "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "L_REF",      "L coord (ref)",        "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "M_REF",      "M coord (ref)",        "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "X_REF",      "X coord (ref)",        "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "Y_REF",      "Y coord (ref)",        "pixels",                         1.0, 0.0); 
+
+  gfits_define_bintable_column (&theader, "E",    "MAG_REF",    "catalog mag",          "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "DMAG_REF",   "catalog mag err",      "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "MAG_RAW",    "instrum mag",          "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "E",    "DMAG_RAW",   "instrum mag err",      "pixels",                         1.0, 0.0); 
+  gfits_define_bintable_column (&theader, "B",    "MASK",       "excluded from fit?",   "",                               1.0, 0.0);
+  gfits_define_bintable_column (&theader, "7A",   "DUMMY",      "padding",              "",                               1.0, 0.0);
+
+  /* create table, add data values */
+  gfits_create_table (&theader, &table);
+
+  /* create output data block and assign */
+  Nmatch = 0;
+  for (i = 0; i < Nchip; i++) Nmatch += chip[i].Nmatch; 
+  ALLOCATE (match, MatchData, Nmatch);
+
+  for (i = N = 0; i < Nchip; i++) {
+    for (j = 0; j < chip[i].Nmatch; j++, N++) {
+      match[N].Rraw = chip[i].raw[j].R;
+      match[N].Draw = chip[i].raw[j].D;
+      match[N].Praw = chip[i].raw[j].P;
+      match[N].Qraw = chip[i].raw[j].Q;
+      match[N].Lraw = chip[i].raw[j].L;
+      match[N].Mraw = chip[i].raw[j].M;
+      match[N].Xraw = chip[i].raw[j].X;
+      match[N].Yraw = chip[i].raw[j].Y;
+
+      match[N].Rref = chip[i].ref[j].R;
+      match[N].Dref = chip[i].ref[j].D;
+      match[N].Pref = chip[i].ref[j].P;
+      match[N].Qref = chip[i].ref[j].Q;
+      match[N].Lref = chip[i].ref[j].L;
+      match[N].Mref = chip[i].ref[j].M;
+      match[N].Xref = chip[i].ref[j].X;
+      match[N].Yref = chip[i].ref[j].Y;
+
+      match[N].Mcat   = chip[i].ref[j].Mag;
+      match[N].dMcat  = chip[i].ref[j].dMag;
+      match[N].Minst  = chip[i].raw[j].Mag;
+      match[N].dMinst = chip[i].raw[j].dMag;
+      match[N].mask   = chip[i].raw[j].mask;
+    }
+  }
+
+  /* fix byte order issues */
+  ConvertMatch (match, sizeof (MatchData), Nmatch);
+  gfits_add_rows (&table, (char *) match, Nmatch, sizeof (MatchData));
+
+  gfits_fwrite_header  (f, header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table   (f, &table);
+  fclose (f);
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/args.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "mosastro.h"
+
+void print_help () {
+
+  fprintf (stderr, "mosastro -- mosaic astrometry\n");
+  fprintf (stderr, "\n"); 
+  exit (0);
+
+}
+
+void args (int *argc, char **argv) {
+  
+  int N;
+
+  if (get_argument (*argc, argv, "-help") ||
+      get_argument (*argc, argv, "-h")) {
+    print_help ();
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (*argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  DUMP = NULL;
+  if ((N = get_argument (*argc, argv, "-dump"))) {
+    remove_argument (N, argc, argv);
+    DUMP = strcreate(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  SAVE_RESID = FALSE;
+  if ((N = get_argument (*argc, argv, "-save-residuals"))) {
+    remove_argument (N, argc, argv);
+    SAVE_RESID = TRUE;
+  }
+
+  CHIPS = (char *) NULL;
+  if ((N = get_argument (*argc, argv, "-chips"))) {
+    remove_argument (N, argc, argv);
+    CHIPS = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  FIELD = (char *) NULL;
+  if ((N = get_argument (*argc, argv, "-field"))) {
+    remove_argument (N, argc, argv);
+    FIELD = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /** currently unused **/
+  field.Norder = 0;
+  if ((N = get_argument (*argc, argv, "-order"))) {
+    remove_argument (N, argc, argv);
+    field.Norder = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /** currently unused **/
+  ChipOrder = 1;
+  if ((N = get_argument (*argc, argv, "-chiporder"))) {
+    remove_argument (N, argc, argv);
+    ChipOrder = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/args_obs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/args_obs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/args_obs.c	(revision 22322)
@@ -0,0 +1,91 @@
+# include "mosastro.h"
+
+void print_help () {
+  fprintf (stderr, "USAGE: mkobs (RA) (DEC) (output) [-p param value]\n");
+  exit (1);
+}
+
+void args (int *argc, char **argv) {
+  
+  int N, No, Np, Nx, Ny;
+  double theta;
+  char line[500];
+
+  if (get_argument (*argc, argv, "--help")) print_help ();
+  if (get_argument (*argc, argv, "-h")) print_help ();
+
+  FOCAL_PLANE = NULL;
+  if ((N = get_argument (*argc, argv, "-fp"))) {
+    remove_argument (N, argc, argv);
+    FOCAL_PLANE = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  NO_CHIPS = FALSE;
+  if ((N = get_argument (*argc, argv, "-nochips"))) {
+    remove_argument (N, argc, argv);
+    NO_CHIPS = TRUE;
+  }
+
+  SIGMA = 0;
+  if ((N = get_argument (*argc, argv, "-sigma"))) {
+    remove_argument (N, argc, argv);
+    SIGMA = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  fake_field_defaults ();
+
+  while ((N = get_argument (*argc, argv, "-p"))) {
+    remove_argument (N, argc, argv);
+    
+    if (!strcmp (argv[N], "help")) {
+      fprintf (stderr, "valid parameters:\n");
+      fprintf (stderr, "cdelt (arcsec/pix)\n");
+      fprintf (stderr, "crpix (Xo) (Yo)\n");
+      fprintf (stderr, "theta (angle)\n");
+      exit (2);
+    }
+
+    if (!strcmp (argv[N], "cdelt")) {
+      remove_argument (N, argc, argv);
+      field.project.cdelt2 = field.project.cdelt1 = atof(argv[N])/3600.0;
+      remove_argument (N, argc, argv);
+      continue;
+    }
+    if (!strcmp (argv[N], "crpix")) {
+      remove_argument (N, argc, argv);
+      field.project.crpix1 = atof(argv[N]);
+      remove_argument (N, argc, argv);
+      field.project.crpix2 = atof(argv[N]);
+      remove_argument (N, argc, argv);
+      continue;
+    }
+    if (!strcmp (argv[N], "theta")) {
+      remove_argument (N, argc, argv);
+      theta = atof(argv[N]);
+      remove_argument (N, argc, argv);
+      field.project.pc1_1 = +cos (RAD_DEG*theta);
+      field.project.pc1_2 = -sin (RAD_DEG*theta);
+      field.project.pc2_1 = +sin (RAD_DEG*theta);
+      field.project.pc2_2 = +cos (RAD_DEG*theta);
+      continue;
+    }
+    if (!strncmp (argv[N], "pca", 3)) {
+      No = argv[N][3] - '0';
+      Nx = argv[N][5] - '0';
+      Ny = argv[N][7] - '0';
+      if ((Nx + Ny > 3) || (No > 1)) {
+	fprintf (stderr, "PCA out of range\n");
+	exit (1);
+      }
+      Np = mkpolyterm (Nx, Ny);
+      remove_argument (N, argc, argv);
+      field.distort.polyterms[Np][No] = atof(argv[N]);
+      remove_argument (N, argc, argv);
+      continue;
+    }
+  }
+
+  if (*argc != 4) print_help ();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/chips.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/chips.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/chips.c	(revision 22322)
@@ -0,0 +1,82 @@
+# include "mosastro.h"
+
+/* set chip model based on initial header info */
+
+/** when the chips and field terms are treated independently, they must be of 
+    ctype PLY.  When we reach a solution, and write out a coupled set of chip 
+    and field terms, then we need to transition to WRP (chip) & DIS (field
+**/
+
+int init_chips () {
+
+  int i, j;
+  double R, D, P, Q, L, M, Scale, Det;
+
+  if (CHIPS != (char *) NULL) {
+    load_chips (CHIPS);
+    return (1);
+  }
+
+  for (i = 0; i < Nchip; i++) {
+
+    /* bore site center guess */
+    strcpy (chip[i].map.ctype, "DEC--PLY");
+    chip[i].map.crpix1 = 0.0;
+    chip[i].map.crpix2 = 0.0;
+    chip[i].map.cdelt1 = 1.0;
+    chip[i].map.cdelt2 = 1.0;
+
+    /* find (L,M) coords of reference pixel (0,0) */
+    XY_to_RD (&R, &D, 0.0, 0.0, &chip[i].coords);
+    RD_to_XY (&P, &Q, R, D, &field.project);
+    RD_to_XY (&L, &M, P, Q, &field.distort);
+    chip[i].map.crval1 = L;
+    chip[i].map.crval2 = M;
+
+    /** we preserve the rotation and parity of coords.pc_ij, but renormalize to unity scale **/
+    Det = chip[i].coords.pc1_1*chip[i].coords.pc2_2 - chip[i].coords.pc1_2*chip[i].coords.pc2_1;
+    Scale = 1.0 / sqrt(fabs(chip[i].coords.cdelt1*chip[i].coords.cdelt2*Det));
+
+    /** test for NaN Scale **/
+
+    // XXX : temporarily drop the re-scaling to compare with psastro
+    # if (PSASTRO_MODE)
+    chip[i].map.pc1_1  = chip[i].coords.pc1_1;
+    chip[i].map.pc2_2  = chip[i].coords.pc2_2;
+    chip[i].map.pc1_2  = chip[i].coords.pc1_2;
+    chip[i].map.pc2_1  = chip[i].coords.pc2_1;
+    # else
+    chip[i].map.pc1_1  = Scale * chip[i].coords.pc1_1 * chip[i].coords.cdelt1;
+    chip[i].map.pc2_2  = Scale * chip[i].coords.pc2_2 * chip[i].coords.cdelt2;
+    chip[i].map.pc1_2  = Scale * chip[i].coords.pc1_2 * chip[i].coords.cdelt2;
+    chip[i].map.pc2_1  = Scale * chip[i].coords.pc2_1 * chip[i].coords.cdelt1;
+    # endif
+
+    # if 0
+    // XXX this is the wrong choice: re-scaling each chip ruins distortion measurement
+    chip[i].map.pc1_1  = chip[i].coords.pc1_1 * chip[i].coords.cdelt1 / chip[0].coords.cdelt1;
+    chip[i].map.pc2_2  = chip[i].coords.pc2_2 * chip[i].coords.cdelt2 / chip[0].coords.cdelt2;
+    chip[i].map.pc1_2  = chip[i].coords.pc1_2 * chip[i].coords.cdelt1 / chip[0].coords.cdelt1;
+    chip[i].map.pc2_1  = chip[i].coords.pc2_1 * chip[i].coords.cdelt2 / chip[0].coords.cdelt2;
+    # endif
+
+    fprintf (stderr, "chip: %f %f (%f,%f),(%f,%f)\n", 
+	     chip[i].map.crval1, chip[i].map.crval2, 
+	     chip[i].map.pc1_1, chip[i].map.pc1_2, 
+	     chip[i].map.pc2_1, chip[i].map.pc2_2);
+
+    chip[i].map.Npolyterms = 1;
+    for (j = 0; j < 7; j++) {
+      chip[i].map.polyterms[j][0] = 0;
+      chip[i].map.polyterms[j][1] = 0;
+    }
+  }
+  return (1);
+}
+
+int load_chips (char *filename) {
+
+  fprintf (stderr, "not ready yet\n");
+  exit (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/clip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/clip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/clip.c	(revision 22322)
@@ -0,0 +1,38 @@
+# include "mosastro.h"
+
+int ClipOnFP (double Nsigma) {
+
+  int i, j, Nscatter, Nmask, Nkeep, Nkpcp;
+  double DL, DM, dL, dM;
+  double sigma;
+  StarData *raw, *ref;
+
+  Nmask = Nkeep = 0;
+  sigma = GetScatter (&Nscatter, &DL, &DM, FALSE);
+
+  for (i = 0; i < Nchip; i++) {
+    raw = chip[i].raw;
+    ref = chip[i].ref;
+    Nkpcp = 0;
+    for (j = 0; j < chip[i].Nmatch; j++) {
+      dL = raw[j].L - ref[j].L;
+      dM = raw[j].M - ref[j].M;
+      if ((fabs(dL) > Nsigma*DL) || (fabs(dM) > Nsigma*DM)) {
+	raw[j].mask = TRUE;
+	Nmask ++;
+      } else {
+	raw[j].mask = FALSE;	
+	Nkeep ++;
+	Nkpcp ++;
+      }
+    }
+    fprintf (stderr, "Nchip: %d\n", Nkpcp);
+  }
+
+  fprintf (stderr, "Nmask: %d, Nkeep: %d\n", Nmask, Nkeep);
+  return (TRUE);
+}
+
+/*
+  sigma = sigma / (3600.0 * field.project.cdelt1);
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/dump.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/dump.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/dump.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "mosastro.h"
+
+int dump_stars (FILE *f, StarData *stars, int Nstars) {
+
+  int i;
+
+  for (i = 0; i < Nstars; i++) {
+    fprintf (f, "%4d  %12.8f %12.8f  %8.2f %8.2f  %8.2f %8.2f  %7.2f %7.2f  %7.2f %7.2f  %d\n", 
+	     i, 
+	     stars[i].R, stars[i].D,
+	     stars[i].P, stars[i].Q,
+	     stars[i].L, stars[i].M,
+	     stars[i].X, stars[i].Y,
+	     stars[i].Mag, stars[i].dMag, stars[i].mask);
+  }
+  return (1);
+}
+
+int dump_grads (Gradients *grad, char *filename) {
+
+  int i;
+  FILE *f;
+
+  fprintf (stderr, "printing to file %s\n", filename);
+
+  f = fopen (filename, "w");
+
+  for (i = 0; i < grad[0].Npts; i++) {
+    fprintf (f, "%4d  %10.6f %10.6f  %10.6f %10.6f   %10.6f %10.6f\n", 
+	     i, 
+	     grad[0].dPdL[i], grad[0].dPdM[i], 
+	     grad[0].dQdL[i], grad[0].dQdM[i], 
+	     grad[0].Lo[i], grad[0].Mo[i]);
+  }
+  fclose (f);
+  return (1);
+}
+
+int dump_match () {
+
+  int i;
+  FILE *f, *g;
+  f = fopen ("raw.dat", "w");
+  g = fopen ("ref.dat", "w");
+  for (i = 0; i < Nchip; i++) {
+    dump_stars (f, chip[i].raw, chip[i].Nmatch);
+    dump_stars (g, chip[i].ref, chip[i].Nmatch);
+  }
+  fclose (f);
+  fclose (g);
+  exit (1);
+}
+
+int dump_rawstars () {
+
+  int i;
+  FILE *f;
+  f = fopen ("stars.dat", "w");
+  for (i = 0; i < Nchip; i++) {
+    dump_stars (f, chip[i].stars, chip[i].Nstars);
+  }
+  fclose (f);
+  exit (1);
+}
+
+int dump_refcat (StarData *refcat, int Nrefcat) {
+
+  FILE *f;
+  f = fopen ("refcat.dat", "w");
+  dump_stars (f, refcat, Nrefcat);
+  fclose (f);
+  exit (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/fakefield.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/fakefield.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/fakefield.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "mosastro.h"
+
+/* determine an initial guess to field parameters from data */ 
+int fake_field_center (double RA, double DEC) {
+
+  field.Rmin = RA  - 0.5;
+  field.Rmax = RA  + 0.5;
+  field.Dmin = DEC - 0.5;
+  field.Dmax = DEC + 0.5;
+
+  /* bore site center guess */
+  field.project.crval1 = 0.5*(field.Rmin + field.Rmax);
+  field.project.crval2 = 0.5*(field.Dmin + field.Dmax);
+  
+  return (1);
+}
+
+/* set default field parameters (overridden in args_obs) */ 
+int fake_field_defaults () {
+
+  int i;
+
+  /* bore site center guess */
+  strcpy (field.project.ctype, "DEC--TAN");
+  field.project.crpix1 = 0;
+  field.project.crpix2 = 0;
+  
+  /* set TP plate scale */
+  field.project.cdelt1 = 1.0/3600.0;
+  field.project.cdelt2 = 1.0/3600.0;
+
+  /** allow guess at field rotation?? **/
+  field.project.pc1_1  = 1;
+  field.project.pc2_2  = 1;
+  field.project.pc1_2  = 0;
+  field.project.pc2_1  = 0;
+  field.project.Npolyterms = 1;
+
+  /** distort only has power in polyterms **/
+  strcpy (field.distort.ctype, "DEC--PLY");
+  field.distort.crval1 = 0.0;
+  field.distort.crval2 = 0.0;
+  field.distort.crpix1 = 0.0;
+  field.distort.crpix2 = 0.0;
+  field.distort.cdelt1 = 1.0;
+  field.distort.cdelt2 = 1.0;
+  
+  field.distort.pc1_1  = 1;
+  field.distort.pc2_2  = 1;
+  field.distort.pc1_2  = 0;
+  field.distort.pc2_1  = 0;
+
+  /* how do we handle renormalization? (fixed at 1000 pixels??) */
+  field.distort.Npolyterms = 3;
+  for (i = 0; i < 7; i++) {
+    field.distort.polyterms[i][0] = 0;
+    field.distort.polyterms[i][1] = 0;
+  }
+
+  return (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/field.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/field.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/field.c	(revision 22322)
@@ -0,0 +1,199 @@
+# include "mosastro.h"
+
+/* determine an initial guess to field parameters from data */ 
+int init_field () {
+
+  int i;
+
+  if (FIELD != (char *) NULL) {
+    load_field (FIELD);
+    return (TRUE);
+  }
+
+  /* bore site center guess */
+  strcpy (field.project.ctype, "DEC--TAN");
+
+  # if (PSASTRO_MODE)
+  // XXX : temporarily use a fixed ref point to compare with psastro
+  field.project.crval1 = chip[0].coords.crval1;
+  field.project.crval2 = chip[0].coords.crval2;
+  # else
+  field.project.crval1 = 0.5*(field.Rmin + field.Rmax);
+  field.project.crval2 = 0.5*(field.Dmin + field.Dmax);
+  # endif
+
+  field.project.crpix1 = 0;
+  field.project.crpix2 = 0;
+  
+  /* measure average plate scale - would be better using parabolic min... */
+  field.project.cdelt1 = 0;
+  field.project.cdelt2 = 0;
+  for (i = 0; i < Nchip; i++) {
+    field.project.cdelt1 += chip[i].coords.cdelt1;
+    field.project.cdelt2 += chip[i].coords.cdelt2;
+  }
+  field.project.cdelt1 /= Nchip;
+  field.project.cdelt2 /= Nchip;
+
+  # if (PSASTRO_MODE)
+  // XXX : temporarily use first chip as ref scale to compare with psastro
+  field.project.cdelt1 = chip[0].coords.cdelt1;
+  field.project.cdelt2 = chip[0].coords.cdelt2;
+  # endif
+  /* force starting guess to have equal x & y plate scales? */
+
+  fprintf (stderr, "field: %f,%f  %f,%f\n", 
+	   field.project.crval1, field.project.crval2, 
+	   3600*field.project.cdelt1, 3600*field.project.cdelt2);
+
+  /** allow guess at field rotation?? **/
+  field.project.pc1_1  = 1;
+  field.project.pc2_2  = 1;
+  field.project.pc1_2  = 0;
+  field.project.pc2_1  = 0;
+  field.project.Npolyterms = 1;
+
+  /* bore site center guess */
+  strcpy (field.distort.ctype, "DEC--PLY");
+  field.distort.crval1 = 0.0;
+  field.distort.crval2 = 0.0;
+  field.distort.crpix1 = 0.0;
+  field.distort.crpix2 = 0.0;
+  field.distort.cdelt1 = 1.0;
+  field.distort.cdelt2 = 1.0;
+  
+  /** allow guess at field rotation?? **/
+  field.distort.pc1_1  = 1;
+  field.distort.pc2_2  = 1;
+  field.distort.pc1_2  = 0;
+  field.distort.pc2_1  = 0;
+
+  /* allow 2nd and 3rd order? */
+  /* how do we handle renormalization? (fixed at 1000 pixels??) */
+  field.distort.Npolyterms = 1;
+  for (i = 0; i < 7; i++) {
+    field.distort.polyterms[i][0] = 0;
+    field.distort.polyterms[i][1] = 0;
+  }
+  return (TRUE);
+}
+
+void field_stats () {
+  
+  int i, j;
+  double Rmin, Rmax, Dmin, Dmax;
+
+  /* find range for single image */
+  Rmin = Dmin = 360.0;
+  Rmax = Dmax = -90.0;
+
+  for (i = 0; i < Nchip; i++) {
+    for (j = 0; j < chip[i].Nstars; j++) {
+      Rmin = MIN (Rmin, chip[i].stars[j].R);
+      Dmin = MIN (Dmin, chip[i].stars[j].D);
+      Rmax = MAX (Rmax, chip[i].stars[j].R);
+      Dmax = MAX (Dmax, chip[i].stars[j].D);
+    }
+  }
+  field.Rmin = Rmin;
+  field.Rmax = Rmax;
+  field.Dmin = Dmin;
+  field.Dmax = Dmax;
+
+}
+
+void field_combine () {
+
+  int i;
+  double cd1, cd2, pc11, pc12, pc21, pc22;
+
+  /* combine boresite & distortion parameters: ctype DIS */
+  strcpy (field.project.ctype, "DEC--DIS");
+
+  cd1  = field.project.cdelt1;
+  cd2  = field.project.cdelt2;
+  pc11 = field.project.pc1_1;
+  pc12 = field.project.pc1_2;
+  pc21 = field.project.pc2_1;
+  pc22 = field.project.pc2_2;
+
+  field.project.Npolyterms = field.distort.Npolyterms;
+
+  for (i = 0; i < 7; i++) {
+    field.project.polyterms[i][0] = (pc11*cd1*field.distort.polyterms[i][0] + pc12*cd2*field.distort.polyterms[i][1]);
+    field.project.polyterms[i][1] = (pc21*cd1*field.distort.polyterms[i][0] + pc22*cd2*field.distort.polyterms[i][1]);
+  }
+  for (i = 0; i < 2; i++) {
+    field.project.polyterms[0][i] =  field.project.polyterms[0][i] / (cd1*cd1);
+    field.project.polyterms[1][i] =  field.project.polyterms[1][i] / (cd1*cd2);
+    field.project.polyterms[2][i] =  field.project.polyterms[2][i] / (cd2*cd2);
+
+    field.project.polyterms[3][i] =  field.project.polyterms[3][i] / (cd1*cd1*cd1);
+    field.project.polyterms[4][i] =  field.project.polyterms[4][i] / (cd1*cd1*cd2);
+    field.project.polyterms[5][i] =  field.project.polyterms[5][i] / (cd1*cd2*cd2);
+    field.project.polyterms[6][i] =  field.project.polyterms[6][i] / (cd2*cd2*cd2);
+  }
+}
+
+int load_field (char *filename) {
+
+  int i;
+  Coords coords;
+  Header header;
+
+  /* load header */
+  if (!gfits_read_header (filename, &header)) {
+    fprintf (stderr, "ERROR: can't read header for %s\n", filename);
+    exit (1);
+  }
+  /* get astrometry information */
+  if (!GetCoords (&coords, &header)) {
+    fprintf (stderr, "ERROR: no astrometric solution in field %s\n", filename);
+    exit (1);
+  }
+ 
+  /* separate field into boresite + distortion terms */
+
+  /* bore site center guess */
+  strcpy (field.project.ctype, "DEC--TAN");
+  field.project.crval1 = 0.5*(field.Rmin + field.Rmax);
+  field.project.crval2 = 0.5*(field.Dmin + field.Dmax);
+  field.project.crpix1 = 0;
+  field.project.crpix2 = 0;
+  
+  /* measure average plate scale - would be better using parabolic min... */
+  field.project.cdelt1 = coords.cdelt1;
+  field.project.cdelt2 = coords.cdelt2;
+
+  /** allow guess at field rotation?? **/
+  field.project.pc1_1  = coords.pc1_1;
+  field.project.pc2_2  = coords.pc2_2;
+  field.project.pc1_2  = coords.pc1_2;
+  field.project.pc2_1  = coords.pc2_1;
+  field.project.Npolyterms = 1;
+
+  /* bore site center guess */
+  strcpy (field.distort.ctype, "DEC--PLY");
+  field.distort.crval1 = 0.0;
+  field.distort.crval2 = 0.0;
+  field.distort.crpix1 = 0.0;
+  field.distort.crpix2 = 0.0;
+  field.distort.cdelt1 = 1.0;
+  field.distort.cdelt2 = 1.0;
+  
+  /** allow guess at field rotation?? **/
+  field.distort.pc1_1  = 1;
+  field.distort.pc2_2  = 1;
+  field.distort.pc1_2  = 0;
+  field.distort.pc2_1  = 0;
+
+  /* allow 2nd and 3rd order? */
+  /* how do we handle renormalization? (fixed at 1000 pixels??) */
+  field.distort.Npolyterms = coords.Npolyterms;
+  for (i = 0; i < 7; i++) {
+    field.distort.polyterms[i][0] = coords.polyterms[i][0];
+    field.distort.polyterms[i][1] = coords.polyterms[i][1];
+  }
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/fitpoly.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/fitpoly.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/fitpoly.c	(revision 22322)
@@ -0,0 +1,284 @@
+# include "mosastro.h"
+
+static int NTERM, NPOWER, NPARS, NORDER, Npts;
+static double **sum, **xsum, **ysum;
+static double **matrix, **vector;
+
+void fit_init (int order) {
+
+  int i;
+
+  Npts  = 0;
+  NORDER = order;
+  NPOWER = NORDER + 1;
+  NTERM = 2*NORDER + 1;
+  NPARS = (NORDER + 1)*(NORDER + 2) / 2;
+
+  /* allocate arrays for fit solution */
+  ALLOCATE (sum, double *, NTERM);
+  ALLOCATE (xsum, double *, NTERM);
+  ALLOCATE (ysum, double *, NTERM);
+  for (i = 0; i < NTERM; i++) {
+    ALLOCATE (sum[i], double, NTERM);
+    bzero (sum[i], NTERM*sizeof(double));
+    ALLOCATE (xsum[i], double, NTERM);
+    bzero (xsum[i], NTERM*sizeof(double));
+    ALLOCATE (ysum[i], double, NTERM);
+    bzero (ysum[i], NTERM*sizeof(double));
+  }
+  ALLOCATE (matrix, double *, NPARS);
+  ALLOCATE (vector, double *, NPARS);
+  for (i = 0; i < NPARS; i++) {
+    ALLOCATE (matrix[i], double, NPARS);
+    ALLOCATE (vector[i], double, 2);
+    bzero (vector[i], 2*sizeof(double));
+    bzero (matrix[i], NPARS*sizeof(double));
+  }
+
+}
+
+# define SCALE 1.0
+void fit_add (double x1, double y1, double x2, double y2) {
+
+  int n, m;
+  double xterm, yterm, term;
+
+  xterm = 1;
+  for (n = 0; n < NTERM; n++) {
+    yterm = 1;
+    for (m = 0; m < NTERM; m++) {
+      term = xterm*yterm;
+      if (n+m < NTERM) {
+	sum[n][m] += term;
+      }
+      if (n+m < NPOWER) {
+	xsum[n][m] += x2*term;
+	ysum[n][m] += y2*term;
+      }
+      yterm *= y1/SCALE;
+    }
+    xterm *= x1/SCALE;
+  }
+  Npts ++;
+}
+
+/** I am renormalizing here by the max pivots to keep gaussj sane **
+ ** would not be needed if the fit used scaled ind. variables **/
+void fit_eval () {
+
+  int i, j, n, m, M, N;
+
+  if (Npts == 0) {
+    fprintf (stderr, "warning: no valid pts\n");
+  }
+
+  i = 0;
+  for (m = 0; m < NPOWER; m++) {
+    for (n = 0; n < NPOWER - m; n++, i++) {
+      vector[i][0] = xsum[n][m];
+      vector[i][1] = ysum[n][m];
+    }	
+  }
+  j = 0;
+  for (M = 0; M < NPOWER; M++) {
+    for (N = 0; N < NPOWER - M; N++, j++) {
+      i = 0;
+      for (m = 0; m < NPOWER; m++) {
+	for (n = 0; n < NPOWER - m; n++, i++) {
+	  matrix[i][j] = sum[n+N][m+M];
+	}	
+      }
+    }
+  }       
+# if (0)
+  max = 0.0;
+  for (i = 0; i < NPARS; i++) {
+    for (j = 0; j < NPARS; j++) {
+      max = MAX (max, fabs(matrix[i][j]));
+    }
+    max = MAX (max, fabs(vector[i][0]));
+    max = MAX (max, fabs(vector[i][1]));
+  }
+  for (i = 0; i < NPARS; i++) {
+    for (j = 0; j < NPARS; j++) {
+      matrix[i][j] /= max;
+    }
+    vector[i][0] /= max;
+    vector[i][1] /= max;
+  }
+# endif
+
+  dgaussjordan (matrix, vector, NPARS, 2); 
+
+# if (0)
+  i = 0;
+  for (m = 0; m < NPOWER; m++) {
+    for (n = 0; n < NPOWER - m; n++, i++) {
+      fprintf (stderr, "RA x^%dy^%d: %10.4g    DEC x^%dy^%d: %10.4g \n", 
+	       n, m, vector[i][0], n, m, vector[i][1]);
+    }	
+  }
+# endif
+}
+
+/* linear portion of fit : NORDER is 1 */
+void fit_apply_coords (Coords *coords) {
+
+  int i, j, Np, Nv, N;
+  double c11, c12;
+  double c21, c22;
+  double R;
+
+  /* update the higher order terms */
+  if (NORDER > 1) {
+    for (i = 0; i < NPOWER; i++) {
+      for (j = 0; j < (NPOWER - i); j++) {
+	if (i + j < 2) continue;
+	Np = mkpolyterm (i, j);
+	Nv = mkvector (i, j, NORDER);
+	coords[0].polyterms[Np][0] = vector[Nv][0];
+	coords[0].polyterms[Np][1] = vector[Nv][1];
+      }
+    }
+  }
+
+  /* get the correct vector entries for the linear terms */
+  N = mkvector (0, 0, NORDER);
+  coords[0].crval1 = vector[N][0];  
+  coords[0].crval2 = vector[N][1];
+
+  N = mkvector (1, 0, NORDER);
+  c11 = vector[N][0];  
+  c21 = vector[N][1];
+  N = mkvector (0, 1, NORDER);
+  c12 = vector[N][0];  
+  c22 = vector[N][1];
+  coords[0].cdelt1 = coords[0].cdelt2 = sqrt(fabs(c11*c22 - c12*c21));
+  R = 1 / coords[0].cdelt1;
+
+  coords[0].pc1_1  = c11*R;
+  coords[0].pc2_1  = c21*R;
+  coords[0].pc1_2  = c12*R;
+  coords[0].pc2_2  = c22*R;
+
+  coords[0].crpix1 = 0;
+  coords[0].crpix2 = 0;
+
+  coords[0].Npolyterms = NORDER;
+  strcpy (coords[0].ctype, "DEC--PLY");
+}
+
+/*
+  if we have just linear terms, the following holds for crpix1,2:
+  D = R / (coords[0].pc1_1*coords[0].pc2_2 - coords[0].pc1_2*coords[0].pc2_1);
+  coords[0].crpix1 = D * (coords[0].pc1_2*c20 - coords[0].pc2_2*c10);
+  coords[0].crpix2 = D * (coords[0].pc2_1*c10 - coords[0].pc1_1*c20);
+*/
+
+/* NORDER is order of gradient fit : Npolyterms is Norder + 1 */
+void fit_apply_grads (Coords *distort, Coords *project, int term) {
+
+  int i, j, Np, Nv1, Nv2;
+
+  distort[0].Npolyterms = NORDER + 1;
+  if (distort[0].Npolyterms > 1) strcpy (distort[0].ctype, "DEC--PLY");
+
+  for (i = 0; i < NPOWER + 1; i++) {
+    for (j = 0; j < (NPOWER + 1 - i); j++) {
+      
+      if (i + j < 2) continue;
+      Np  = mkpolyterm (i, j);
+      Nv1 = mkvector (i-1, j, NORDER);
+      Nv2 = mkvector (i, j-1, NORDER);
+
+      /** why do we have the negative sign? **/
+      if (j == 0) {
+	distort[0].polyterms[Np][term] = vector[Nv1][0] / i;
+      }
+      if (i == 0) {
+	distort[0].polyterms[Np][term] = vector[Nv2][1] / j;
+      }
+      if ((i > 0) && (j > 0)) {
+	distort[0].polyterms[Np][term] = 0.5*(vector[Nv1][0] / i + vector[Nv2][1] / j);
+      }
+    }
+  }
+
+  Nv1 = mkvector (0, 0, NORDER);
+  if (term == 0) {
+    project[0].pc1_1 = project[0].pc1_1 * (1 + vector[Nv1][0]);
+    project[0].pc1_2 = project[0].pc1_2 * (1 + vector[Nv1][1]);
+  } else {
+    project[0].pc2_1 = project[0].pc2_1 * (1 + vector[Nv1][0]);
+    project[0].pc2_2 = project[0].pc2_2 * (1 + vector[Nv1][1]);
+  }
+}
+
+void fit_correct_grads (Gradients *in, Gradients *out, int term) {
+
+  int i, k, m, n;
+  double x, y, dx, dy, dz1, dz2;
+
+  for (i = 0; i < in[0].Npts; i++) {
+    
+    dx = in[0].Lo[i];
+    dy = in[0].Mo[i];
+    dz1 = dz2 = 0.0;
+
+    k = 0;
+    x = y = 1;
+    for (m = 0; m < NPOWER; m++) {
+      x = y;
+      for (n = 0; n < NPOWER - m; n++, k++) {
+	dz1 += vector[k][0]*x;
+	dz2 += vector[k][1]*x;
+	x = x * dx / SCALE;
+      }
+      y = y * dy / SCALE;
+    }
+
+    out[0].Lo[i] = dx;
+    out[0].Mo[i] = dy;
+    if (term == 0) {
+      out[0].dPdL[i] = in[0].dPdL[i] - dz1;
+      out[0].dPdM[i] = in[0].dPdM[i] - dz2;
+    } else {
+      out[0].dQdL[i] = in[0].dQdL[i] - dz1;
+      out[0].dQdM[i] = in[0].dQdM[i] - dz2;
+    }
+  }
+}
+
+int mkvector (int n, int m, int norder) {
+  
+  int i, N;
+  
+  N = 0;
+  for (i = 0; i < m; i++) {
+    N += (norder - i + 1);
+  }
+  N += n;
+  return (N);
+}
+
+void fit_free () {
+
+  int i;
+
+  for (i = 0; i < NTERM; i++) {
+    free (sum[i]);
+    free (xsum[i]);
+    free (ysum[i]);
+  }
+  free (sum);
+  free (xsum);
+  free (ysum);
+
+  for (i = 0; i < NPARS; i++) {
+    free (matrix[i]);
+    free (vector[i]);
+  }
+  free (matrix);
+  free (vector);
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getgsc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getgsc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getgsc.c	(revision 22322)
@@ -0,0 +1,87 @@
+# include "mosastro.h"
+# define BYTES_STAR 23
+# define BLOCK 1000
+
+StarData *rd_gsc (char *filename, int *Nstars);
+
+StarData *getgsc (CatStats *catstats, int *NSTARS) {
+  
+  int i, j, k, Ns, Ngsc, Nstars; 
+  StarData *gsc;
+  StarData *stars;
+  SkyList *skylist;
+  SkyTable *sky;
+  SkyRegion patch;
+
+  patch.Rmin = catstats[0].RA[0];
+  patch.Rmax = catstats[0].RA[1];
+  patch.Dmin = catstats[0].DEC[0];
+  patch.Dmax = catstats[0].DEC[1];
+
+  /* load regions from GSC table, restrict to patch */
+  sky = SkyTableFromGSC (GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  SkyTableSetFilenames (sky, GSCDIR, "cpt");
+  skylist = SkyListByPatch (sky, -1, &patch);
+  
+  Nstars = 0;
+  ALLOCATE (stars, StarData, 1);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    gsc = rd_gsc (skylist[0].filename[i], &Ngsc);
+
+    Ns = Nstars;
+    Nstars += Ngsc;
+
+    REALLOCATE (stars, StarData, MAX (1, Nstars));
+    for (k = Ns, j = 0; j < Ngsc; k++, j++) {
+      stars[k].R = gsc[j].R;
+      stars[k].D = gsc[j].D;
+      stars[k].M = gsc[j].M;
+    }      
+    free (gsc);
+  }
+  SkyTableFree (sky);
+
+  if (VERBOSE) fprintf (stderr, "%d stars from HST GSC\n", Nstars);
+  *NSTARS = Nstars;
+  return (stars);
+}  
+
+StarData *rd_gsc (char *filename, int *Nstars) {
+  
+  StarData *stars;
+  int i, NSTAR, nstar, Nbytes, nbytes;
+  char *buffer;
+  FILE *f;
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't find catalog file %s\n", filename);
+    exit (1);
+  }
+  
+  nstar = 0;
+  NSTAR = 1000;
+  ALLOCATE (stars, StarData, NSTAR);
+
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR));
+  Nbytes = BLOCK*BYTES_STAR;
+
+  while ((nbytes = fread (buffer, 1, Nbytes, f)) > 0) {
+    for (i = 0; i < nbytes / BYTES_STAR; i++) {
+      dparse (&stars[nstar].R, 1, &buffer[i*BYTES_STAR]);
+      dparse (&stars[nstar].D, 2, &buffer[i*BYTES_STAR]);
+      dparse (&stars[nstar].M, 3, &buffer[i*BYTES_STAR]);
+      nstar++;
+      if (nstar == NSTAR) {
+	NSTAR += 1000;
+	REALLOCATE (stars, StarData, NSTAR);
+      }
+    }
+  }
+
+  free (buffer);
+
+  *Nstars = nstar;
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getptolemy.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getptolemy.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getptolemy.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "mosastro.h"
+
+StarData *getptolemy (CatStats *catstats, int *NSTARS) {
+  
+  int i, j, k, Ns, Nstars; 
+  Catalog catalog;
+  StarData *stars;
+  SkyList *skylist;
+  SkyTable *sky;
+  SkyRegion patch;
+
+  patch.Rmin = catstats[0].RA[0];
+  patch.Rmax = catstats[0].RA[1];
+  patch.Dmin = catstats[0].DEC[0];
+  patch.Dmax = catstats[0].DEC[1];
+
+  Nstars = 0;
+  ALLOCATE (stars, StarData, 1);
+
+  /* load regions from GSC table, restrict to patch */
+  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  skylist = SkyListByPatch (sky, -1, &patch);
+  
+  for (i = 0; i <skylist[0].Nregions; i++) {
+    // set the parameters which guide catalog open/load/create
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS;
+    catalog.Nsecfilt  = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    // Naves_disk == 0 implies an empty catalog file
+    // for only_match, skip empty catalogs
+    if (!catalog.Naves_disk) {
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    Ns = Nstars;
+    Nstars += catalog.Naverage;
+
+    REALLOCATE (stars, StarData, MAX (1, Nstars));
+    for (k = Ns, j = 0; j < catalog.Naverage; k++, j++) {
+      stars[k].R = catalog.average[j].R;
+      stars[k].D = catalog.average[j].D;
+      stars[k].M = catalog.measure[catalog.average[j].measureOffset].M;
+    }      
+    dvo_catalog_free (&catalog);
+  }
+
+  if (VERBOSE) fprintf (stderr, "%d stars from PTOLEMY\n", Nstars);
+  *NSTARS = Nstars;
+  return (stars);
+}  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getstone.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getstone.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getstone.c	(revision 22322)
@@ -0,0 +1,89 @@
+# include "mosastro.h"
+
+StarData *getstone (CatStats *input, int *nstars) {
+
+  FILE *f;
+  int i, Nregion, NREGION, Nstars, NSTARS;
+  double r, d, R, D, M, Tr, Td, dRdT, dDdT;
+  char **regname, filename[1024], line[1024];
+  StarData *stars;
+  CatStats *regions;
+
+  sprintf (filename, "%s/Regions.dat", StoneRegions);
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't open stone %s\n", filename);
+    exit (1);
+  }
+
+  Nregion = 0;
+  NREGION = 20;
+  ALLOCATE (regions, CatStats, NREGION);
+  ALLOCATE (regname, char *, NREGION);
+
+  /* strip off first commented line */
+  scan_line (f, line);
+  while (scan_line (f, line) != EOF) {
+    sscanf (line, "%s %lf %lf %lf %lf", filename, 
+	    &regions[Nregion].RA[0], &regions[Nregion].RA[1], 
+	    &regions[Nregion].DEC[0], &regions[Nregion].DEC[1]);
+    sprintf (line, "%s/%s", StoneRegions, filename);
+    regname[Nregion] = strcreate (line);
+    Nregion ++;
+    if (Nregion == NREGION) {
+      NREGION += 50;
+      REALLOCATE (regions, CatStats, NREGION);
+      REALLOCATE (regname, char *, NREGION);
+    }	  
+  }
+  fclose (f);
+
+  Nstars = 0;
+  NSTARS = 1000;
+  ALLOCATE (stars, StarData, NSTARS);
+
+  /* find the region(s) that overlap the input region */
+  for (i = 0; i < Nregion; i++) {
+    if (input[0].RA[0] > regions[i].RA[1]) continue;
+    if (input[0].RA[1] < regions[i].RA[0]) continue;
+    if (input[0].DEC[0] > regions[i].DEC[1]) continue;
+    if (input[0].DEC[1] < regions[i].DEC[0]) continue;
+    /* this will fail on 0,360 boundary */
+    fprintf (stderr, "loading data from %s\n", regname[i]);
+
+    f = fopen (regname[i], "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open stone %s\n", regname[i]);
+      exit (1);
+    }
+
+    while (scan_line (f, line) != EOF) {
+    
+      dparse (&R, 3, line);
+      dparse (&D, 4, line);
+      dparse (&M, 5, line);
+      
+      dparse (&Tr, 12, line);
+      dparse (&Td, 13, line);
+      dparse (&dRdT, 14, line);
+      dparse (&dDdT, 15, line);
+      
+      r = R + dRdT*(Year - Tr)/(100.0*3600.0);
+      d = D + dDdT*(Year - Td)/(100.0*3600.0);
+      
+      stars[Nstars].M = M;
+      stars[Nstars].R = r*15.0;
+      stars[Nstars].D = d;
+      Nstars ++;
+      if (Nstars == NSTARS) {
+	NSTARS += 5000;
+	REALLOCATE (stars, StarData, NSTARS);
+      }	  
+    }
+    fclose (f);
+  }
+
+  *nstars = Nstars;
+  return (stars);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getusno.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getusno.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getusno.c	(revision 22322)
@@ -0,0 +1,130 @@
+# include "mosastro.h"
+# define NZONE 24
+
+int SPDzone[] = {
+  0, 75, 450, 375, 1500, 1650, 300, 1425, 1725, 525, 1275, 225, 
+  675, 150, 600, 1575, 750, 975, 900, 1050, 1125, 1200, 825, 1350};
+
+int USNOdisk[] = {
+  1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10};
+
+StarData *getusno (CatStats *catstats, int *Nstars) {
+
+  long int offset;
+  int i, bin, first, last, nitems, Nitems, Nbins;
+  float hours[100];
+  int start[100], number[100], *buffer, *buf;
+  char filename[128];
+  FILE *f;
+  int iRA0, iRA1, iDEC0, iDEC1;
+  double RA0, RA1, DEC0, DEC1, dec;
+  int spd, spd_start, spd_end, disk;
+  int NUSNO, Nusno;
+  StarData *stars;
+
+  RA0  = catstats[0].RA[0]; 
+  RA1  = catstats[0].RA[1]; 
+  DEC0 = catstats[0].DEC[0];
+  DEC1 = catstats[0].DEC[1];
+
+  /* identify ra & dec range of interest */
+  iRA0 = RA0 * 360000.0;
+  iRA1 = RA1 * 360000.0;
+  iDEC0 = (DEC0 + 90.0) * 360000.0;
+  iDEC1 = (DEC1 + 90.0) * 360000.0;
+  
+  /* data is organized in south-pole distance zones */
+  spd_start = (int)((DEC0 + 90) / 7.5) * 75.0;
+  dec = (DEC1 + 90) / 7.5;
+  if (dec > (int)(dec)) {
+    spd_end =   (int)(1 + (DEC1 + 90) / 7.5) * 75.0;
+  } else {
+    spd_end =   (int)(0 + (DEC1 + 90) / 7.5) * 75.0;
+  }
+
+  Nusno = 0;
+  NUSNO = 5000;
+  ALLOCATE (stars, StarData, NUSNO);
+
+  for (spd = spd_start; spd < spd_end; spd += 75) {
+    disk = -1;
+    for (i = 0; i < NZONE; i++) {
+      if (spd == SPDzone[i]) 
+	disk = USNOdisk[i];
+    }
+    if (disk < 0) {
+      fprintf (stderr, "ERROR: can't find cdrom for spd %d\n",  spd);
+      exit (0);
+    }
+    
+    /* load accelerator file */
+    sprintf (filename, "%s/zone%04d.acc", USNO_A_DIR, spd); 
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file %s, is cdrom %d in drive?\n", filename, disk);
+      exit (1);  
+    }
+    for (i = 0; fscanf (f, "%f %d %d", &hours[i], &start[i], &number[i]) != EOF; i++);
+    Nbins = i;
+    fclose (f);
+    
+    first = RA0 / 3.75;
+    if ((RA1 / 3.75) == (int) (RA1 / 3.75)) 
+      last  = RA1 / 3.75;
+    else 
+      last  = 1 + RA1 / 3.75;
+
+    if ((first > Nbins) || (last > Nbins)) {
+      fprintf (stderr, "ERROR: RA out of range\n");
+      exit (1);
+    }
+    
+    /* open data file */
+    sprintf (filename, "%s/zone%04d.cat", USNO_A_DIR, spd);
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file %s\n", filename);
+      exit (1);
+    }
+    /* advance file pointer to first slice */
+    offset = 3*sizeof(int)*(start[first] - 1);
+    fseek (f, offset, SEEK_SET);
+    /* on each loop, load data from an RA slice of the catalog */
+    for (bin = first; bin < last; bin++) {
+      Nitems = 3*number[bin];
+      ALLOCATE (buffer, int, Nitems);
+      nitems = Fread (buffer, sizeof(int), Nitems, f, "int");
+      if (nitems != Nitems) {
+	fprintf (stderr, "ERROR: failure reading data from file %s\n", filename);
+	exit (1);
+      }
+      buf = buffer;
+      /* print out data from slice within RA and DEC range */
+      for (i = 0; i < number[bin]; i++, buf+=3) {
+	if ((buf[0] > iRA0) && (buf[0] < iRA1) &&
+	    (buf[1] > iDEC0) && (buf[1] < iDEC1)) {
+	  bzero (&stars[Nusno], sizeof(StarData));
+	  stars[Nusno].R = buf[0]/360000.0;
+	  stars[Nusno].D = buf[1]/360000.0 - 90.0;
+	  stars[Nusno].Mag = fabs (0.1*(buf[2] - 1000*((int)(buf[2]/1000))));
+	  /* b = 0.1*((int)(buf[2] - 1000000*((int)(buf[2]/1000000))) / 1000); */
+	  Nusno ++;
+	  if (Nusno == NUSNO) {
+	    NUSNO += 5000;
+	    REALLOCATE (stars, StarData, NUSNO);
+	  }	  
+	}
+      }
+      free (buffer);
+    }
+    fclose (f);
+  }
+
+  *Nstars = Nusno;
+  if (VERBOSE) fprintf (stderr, "%d stars from USNO 1.0\n", Nusno);
+  return (stars);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getusnob.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getusnob.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/getusnob.c	(revision 22322)
@@ -0,0 +1,148 @@
+# include "mosastro.h"
+# define NBYTE   4
+# define NELEM  20
+
+StarData *getusnob (CatStats *catstats, int *Nstars) {
+
+  long int offset;
+  int i, bin, first, last, nitems, Nitems, Nbins;
+  float hours[100];
+  int start[100], number[100], *buffer, *buf;
+  char filename[128];
+  FILE *f;
+  double DEC1;
+  double uR, uD;
+  float mB1, mB2, mR1, mR2, mB, mR;
+  int iDEC0, iDEC1, iRA0, iRA1;
+  int spd, spd_start, spd_end;
+  int NUSNO, Nusno, nstars;
+  StarData *stars;
+
+  /* identify ra & dec range of interest */
+  iRA0 = catstats[0].RA[0] * 360000.0;
+  iRA1 = catstats[0].RA[1] * 360000.0;
+  iDEC0 = (catstats[0].DEC[0] + 90.0) * 360000.0;
+  iDEC1 = (catstats[0].DEC[1] + 90.0) * 360000.0;
+  /* note that DEC is in SPD, while both have units to 0.01 degrees */
+  
+  /* data is organized in south-pole distance zones, 1 deg per direction, 0.1 deg per file */
+  spd_start = (int)(10*(catstats[0].DEC[0] + 90));
+  DEC1 = 10*(catstats[0].DEC[1] + 90);
+  if (DEC1 > (int)(DEC1)) {
+    spd_end =   (int)(1 + 10*(catstats[0].DEC[1] + 90));
+  } else {
+    spd_end =   (int)(0 + 10*(catstats[0].DEC[1] + 90));
+  }
+
+  Nusno = 0;
+  NUSNO = 5000;
+  ALLOCATE (stars, StarData, NUSNO);
+
+  for (spd = spd_start; spd < spd_end; spd ++) {
+    
+    /* load accelerator file */
+    sprintf (filename, "%s/%03d/b%04d.acc", USNO_B_DIR, (int)(spd/10), spd); 
+    fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open accelerator file %s\n", filename);
+      exit (1);  
+    }
+    for (i = 0; fscanf (f, "%f %d %d", &hours[i], &start[i], &number[i]) != EOF; i++);
+    Nbins = i;
+    fclose (f);
+    
+    first = catstats[0].RA[0] / 3.75;
+    if ((catstats[0].RA[1] / 3.75) == (int) (catstats[0].RA[1] / 3.75)) 
+      last  = catstats[0].RA[1] / 3.75;
+    else 
+      last  = 1 + catstats[0].RA[1] / 3.75;
+
+    if ((first > Nbins) || (last > Nbins)) {
+      fprintf (stderr, "ERROR: RA out of range\n");
+      exit (1);
+    }
+    
+    /* open data file */
+    sprintf (filename, "%s/%03d/b%04d.cat", USNO_B_DIR, (int)(spd/10), spd); 
+    fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file %s\n", filename);
+      exit (1);
+    }
+
+    /** USNO-B consists of 20 x 4byte (int) records **/
+    /* advance file pointer to first slice */
+    offset = NELEM*NBYTE*(start[first] - 1);
+    fseek (f, offset, SEEK_SET);
+
+    /* sum the number of stars in data segment of interest */
+    nstars = 0;
+    for (bin = first; bin < last; bin++) {
+      nstars += number[bin];
+    }
+    Nitems = NELEM*nstars;  /* number of integer blocks; need to use Fread for byte-swapping read */
+
+    /* allocate space for stars in segment */
+    ALLOCATE (buffer, int, Nitems);
+    // data has the WRONG byte order?
+    // nitems = Fread (buffer, sizeof(int), Nitems, f, "int");
+    nitems = fread (buffer, sizeof(int), Nitems, f);
+    if (nitems != Nitems) {
+      fprintf (stderr, "ERROR: failure reading data from file %s\n", filename);
+      exit (1);
+    }
+
+    buf = buffer;
+    /* print out data from slice within RA and DEC range */
+    for (i = 0; i < nstars; i++, buf+=NELEM) {
+      if (buf[0] < iRA0) continue;
+      if (buf[0] > iRA1) continue;
+      if (buf[1] < iDEC0) continue;
+      if (buf[1] > iDEC1) continue;
+      
+      bzero (&stars[Nusno], sizeof(StarData));
+      stars[Nusno].R = buf[0]/360000.0;
+      stars[Nusno].D = buf[1]/360000.0 - 90.0;
+      
+      uR = (buf[2] % 10000);
+      uR = (uR - 5000.0) * 0.002 / 3600.0;
+      uD = ((buf[2] / 10000) % 10000);
+      uD = (uD - 5000.0) * 0.002 / 3600.0;
+
+      /* 1st blue mag */
+      mB1 = 0.01 * (buf[5] % 10000);
+      /* 1st blue mag */
+      mB2 = 0.01 * (buf[6] % 10000);
+      /* 1st blue mag */
+      mR1 = 0.01 * (buf[7] % 10000);
+      /* 1st blue mag */
+      mR2 = 0.01 * (buf[8] % 10000);
+
+      if (mB1 && mB2) {
+	mB = 0.5*(mB1 + mB2);
+      } else {
+	mB = (mB1) ? mB1 : mB2;
+      }
+
+      if (mR1 && mR2) {
+	mR = 0.5*(mR1 + mR2);
+      } else {
+	mR = (mR1) ? mR1 : mR2;
+      }
+      
+      stars[Nusno].M = mB;
+      stars[Nusno].R += uR*(Year - 2000.0);
+      stars[Nusno].D += uD*(Year - 2000.0);
+      Nusno ++;
+      CHECK_REALLOCATE (stars, StarData, NUSNO, Nusno, 5000);
+    }
+    free (buffer);
+    fclose (f);
+  }
+  
+  *Nstars = Nusno;
+  if (VERBOSE) fprintf (stderr, "%d stars from USNO B1.0\n", Nusno);
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/greference.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/greference.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/greference.c	(revision 22322)
@@ -0,0 +1,59 @@
+# include "mosastro.h"
+
+StarData *greference (int *Nrefcat) {
+
+  int Nstars;
+  StarData *stars;
+  CatStats catstats;
+
+  if (VERBOSE) fprintf (stderr, "loading astrometric reference data from %s\n", REFCAT); 
+
+  stars = NULL;
+  Nstars = 0;
+  catstats.RA[0]  = field.Rmin;
+  catstats.RA[1]  = field.Rmax;
+  catstats.DEC[0] = field.Dmin;
+  catstats.DEC[1] = field.Dmax;
+
+  if (VERBOSE) fprintf (stderr, "full region: %f - %f, %f - %f\n", catstats.RA[0], catstats.RA[1], catstats.DEC[0], catstats.DEC[1]);
+
+  /* get stars from the Stone et al catalog for the given region */
+  if (!strcmp (REFCAT, "STONE")) {
+    stars = getstone (&catstats, &Nstars);
+  }
+
+  /* get stars from the USNO A catalog for the given region */
+  if (!strcmp (REFCAT, "USNO")) {
+    stars = getusno (&catstats, &Nstars);
+  }
+
+  /* get stars from the USNO A catalog for the given region */
+  if (!strcmp (REFCAT, "USNOB")) {
+    stars = getusnob (&catstats, &Nstars);
+  }
+
+  /* get stars from the HST GSC catalog for the given region */
+  if (!strcmp (REFCAT, "GSC")) {
+    stars = getgsc (&catstats, &Nstars);
+  }
+  
+  /* get stars from 2MASS for the given region -- add PHOTCODE check? */
+  if (!strcmp (REFCAT, "2MASS")) {
+    strcpy (CATDIR, TWO_MASS_DIR);
+    stars = getptolemy (&catstats, &Nstars);
+  }
+  
+  /* get stars from the DVO CATDIR for the given region */
+  if (!strcmp (REFCAT, "PTOLEMY")) {
+    strcpy (CATDIR, ASTROM_CATDIR);
+    stars = getptolemy (&catstats, &Nstars);
+  }
+  
+  if (Nstars == 0) {
+    fprintf (stderr, "no ref objs: %s\n", REFCAT);
+    exit (1);
+  }
+  *Nrefcat = Nstars;
+  return (stars);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/match.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/match.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/match.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include "mosastro.h"
+/* match is done on tangent plane : should be focal plane in some cases? */
+
+int match (StarData *refcat, int Nrefcat) {
+
+  int i, j, k, K, Ntotal;
+  int Nmatch, NMATCH;
+  double dp, dq, radius, Radius;
+  double *p, *q, *P, *Q;
+  int *U, *u;
+
+  /* requested radius is in arcsec ; internally radius is in pixels */
+  Radius = RADIUS / (3600.0 * field.project.cdelt2);
+  fprintf (stderr, "mosastro match radius: %f\n", Radius);
+
+  /* sort the REFCAT data by P in tangent plane */ 
+  ALLOCATE (P, double, Nrefcat);
+  ALLOCATE (Q, double, Nrefcat);
+  ALLOCATE (U, int, Nrefcat);
+  for (i = 0; i < Nrefcat; i++) {
+    U[i] = i;
+    P[i] = refcat[i].P;
+    Q[i] = refcat[i].Q;
+  }
+  sort_coords_index (P, Q, U, Nrefcat);
+
+  for (i = 0; i < Nchip; i++) {
+
+    Nmatch = 0;
+    NMATCH = 1000;
+    ALLOCATE (chip[i].raw, StarData, NMATCH);
+    ALLOCATE (chip[i].ref, StarData, NMATCH);
+
+    /* sort the star data by P in tangent plane */ 
+    ALLOCATE (p, double, chip[i].Nstars);
+    ALLOCATE (q, double, chip[i].Nstars);
+    ALLOCATE (u, int, chip[i].Nstars);
+    for (j = 0; j < chip[i].Nstars; j++) {
+      u[j] = j;
+      p[j] = chip[i].stars[j].P;
+      q[j] = chip[i].stars[j].Q;
+    }
+    sort_coords_index (p, q, u, chip[i].Nstars);
+
+    /* find star matches in the tangent plane coord system */
+    for (j = k = 0; (j < chip[i].Nstars) && (k < Nrefcat); ) {
+
+      /** instrumental magnitude limits for raw data **/
+      if (IMAG_MIN && (chip[i].stars[u[j]].Mag - ZERO_POINT < IMAG_MIN)) {
+	j++;
+	continue;
+      }
+      if (IMAG_MAX && (chip[i].stars[u[j]].Mag - ZERO_POINT > IMAG_MAX)) {
+	j++;
+	continue;
+      }
+      /* skip anything with dMag too large */
+      if (SIGMA_LIM > 0.0) {
+	if (chip[i].stars[u[j]].dMag > SIGMA_LIM) {
+	  j++;
+	  continue;
+	}
+      }
+	
+      dp = p[j] - P[k];
+	
+      if (dp <= -2*Radius) {
+	j++;
+	continue;
+      }
+      if (dp >= 2*Radius) {
+	k++;
+	continue;
+      }
+
+      K = k;
+      for (; (dp > -2*Radius) && (k < Nrefcat); k++) {
+	dp = p[j] - P[k];
+	dq = q[j] - Q[k];
+	radius = hypot (dp, dq);
+	if (radius < Radius) {
+	  chip[i].ref[Nmatch] = refcat[U[k]];
+	  chip[i].raw[Nmatch] = chip[i].stars[u[j]];
+	  chip[i].raw[Nmatch].Mag -= ZERO_POINT;  /* raw in instrumental mags */
+	  Nmatch ++;
+	  if (Nmatch == NMATCH) {
+	    NMATCH += 1000;
+	    REALLOCATE (chip[i].raw, StarData, NMATCH);
+	    REALLOCATE (chip[i].ref, StarData, NMATCH);
+	  }
+	  goto done;
+	}
+      }
+    done:
+      k = K;
+      j++;
+    }
+    REALLOCATE (chip[i].raw, StarData, MAX (1, Nmatch));
+    REALLOCATE (chip[i].ref, StarData, MAX (1, Nmatch));
+    chip[i].Nmatch = Nmatch;
+    free (p);
+    free (q);
+    free (u);
+  }
+  free (P);
+  free (Q);
+  free (U);
+  
+  Ntotal = 0;
+  for (i = 0; i < Nchip; i++) {
+    Ntotal += chip[i].Nmatch;
+    if (VERBOSE) fprintf (stderr, "chip %d: %d of %d stars\n", 
+			  i, chip[i].Nmatch, chip[i].Nstars);
+  }
+  fprintf (stderr, "Nchips: %d  Nmatch:  %d\n", Nchip, Ntotal);
+  return (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkheader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkheader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkheader.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "mosastro.h"
+
+Header *mkheader (int Nx, int Ny, int Nstars, Coords *coords) {
+
+  Header *header;
+
+  ALLOCATE (header, Header, 1);
+
+  gfits_init_header (header);
+  header[0].bitpix = -32;
+  header[0].Naxes = 2;
+  header[0].Naxis[0] = Nx;
+  header[0].Naxis[1] = Ny;
+
+  gfits_create_header (header);
+
+  gfits_modify (header, "NSTARS",   "%d", 1, Nstars);
+  gfits_modify (header, "PHOTCODE", "%s", 1, "STD.R");
+  gfits_modify (header, "DATE-OBS", "%s", 1, "2004-04-22");
+  gfits_modify (header, "UTC-OBS",  "%s", 1, "14:27:45.30");
+  gfits_modify (header, "ZERO_PT", "%lf", 1, 25.0);
+  gfits_modify (header, "EXPTIME", "%lf", 1, 2.0);
+
+  PutCoords (coords, header);
+
+  gfits_modify (header, "NASTRO",   "%d", 1, 1); 
+
+  return (header);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkmosaic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkmosaic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkmosaic.c	(revision 22322)
@@ -0,0 +1,88 @@
+# include "mosastro.h"
+
+Header *mkmosaic (int Nx, int Ny, int Nstars, Coords *coords) {
+
+  int Nastro;
+  double Xmin, Xmax, Ymin, Ymax, tmp;
+  double DL, DM, Cerror, Cprecise;
+  Header *header;
+  char line[80];
+
+  /* mosaic header has info to define the outline of the mosaic */
+  RD_to_XY (&Xmin, &Ymin, field.Rmin, field.Dmin, coords);
+  RD_to_XY (&Xmax, &Ymax, field.Rmax, field.Dmax, coords);
+
+  ALLOCATE (header, Header, 1);
+
+  gfits_init_header (header);
+
+  header[0].bitpix = -32;
+  header[0].Naxes = 2;
+  header[0].Naxis[0] = Xmax - Xmin;
+  header[0].Naxis[1] = Ymax - Ymin;
+
+  gfits_create_header (header);
+
+  /* calculate image-wide astrometry scatter */
+  Cerror   = GetScatter (&Nastro, &DL, &DM, FALSE);
+  Cprecise = Cerror / sqrt (Nastro);
+  gfits_modify (header, "CERROR",   "%lf", 1, Cerror);
+  gfits_modify (header, "CERR_L",   "%lf", 1, DL*3600.0 * field.project.cdelt1);
+  gfits_modify (header, "CERR_M",   "%lf", 1, DM*3600.0 * field.project.cdelt1);
+
+  gfits_modify (header, "CPRECISE", "%lf", 1, Cprecise);
+  gfits_modify (header, "NSTARS",   "%d",  1, 0);  /*** modify addstar to allow NSTARS = 0 ***/
+  gfits_modify (header, "NASTRO",   "%d",  1, Nastro);
+
+  GetScatter (&Nastro, &DL, &DM, TRUE);
+  gfits_modify (header, "CERR_LB",  "%lf", 1, DL*3600.0 * field.project.cdelt1);
+  gfits_modify (header, "CERR_MB",  "%lf", 1, DM*3600.0 * field.project.cdelt1);
+  gfits_modify (header, "NBRIGHT",  "%d",  1, Nastro);
+
+  fprintf (stderr, "bright: %f %f %d\n", DL*3600.0 * field.project.cdelt1, DM*3600.0 * field.project.cdelt1, Nastro);
+
+  /* make a better selection for the mosaic photcode? */
+  gfits_scan (&chip[0].header, "PHOTCODE", "%s", 1, line);
+  gfits_modify (header, "PHOTCODE", "%s", 1, line);
+
+  gfits_scan (&chip[0].header, "ZERO_PT", "%lf", 1, &tmp);
+  gfits_modify (header, "ZERO_PT", "%lf", 1, tmp);
+
+  gfits_scan (&chip[0].header, ExptimeKeyword,  "%lf", 1, &tmp);
+  gfits_modify (header, ExptimeKeyword,  "%lf", 1, tmp);
+
+  /* insert time data from chip[0] */
+  /* try JD first */
+  if (strcasecmp (JDKeyword, "NONE")) {
+    uppercase (JDKeyword);
+    gfits_scan (&chip[0].header, JDKeyword, "%lf", 1, &tmp);
+    gfits_modify (header, JDKeyword, "%lf", 1, tmp);
+    goto got_time;
+  }
+
+  /* try MJD next */
+  if (strcasecmp (MJDKeyword, "NONE")) {
+    uppercase (MJDKeyword);
+    gfits_scan (&chip[0].header, MJDKeyword, "%lf", 1, &tmp);
+    gfits_modify (header, MJDKeyword, "%lf", 1, tmp);
+    goto got_time;
+  }
+    
+  /* get UT and DATE */
+  if (strcasecmp (UTKeyword, "NONE") && strcasecmp (DateKeyword, "NONE")) {
+    uppercase (UTKeyword);
+    gfits_scan (&chip[0].header, UTKeyword, "%s", 1, line);
+    gfits_modify (header, UTKeyword, "%s", 1, line);
+    uppercase (DateKeyword);
+    gfits_scan (&chip[0].header, DateKeyword, "%s",  1, line);
+    gfits_modify (header, DateKeyword, "%s", 1, line);
+    goto got_time;
+  }
+  fprintf (stderr, "ERROR: missing time abstraction in config\n");
+  exit (1);
+
+got_time:
+
+  PutCoords (coords, header);
+  return (header);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkobs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkobs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkobs.c	(revision 22322)
@@ -0,0 +1,142 @@
+# include "mosastro.h"
+
+int main (int argc, char **argv) {
+
+  int i, j, N, Nrefcat, Nchip, Nstars, NSTARS;
+  double Ro, Do, Po, Qo, Lo, Mo;
+  double dX, dY, Dist;
+  double RA, DEC;
+  char filename[64];
+  Coords map, coords;
+  StarData *refcat;
+  SMPData *stars;
+  Header *header;
+
+  ConfigInit (&argc, argv);
+  args (&argc, argv);
+  init_random ();
+
+  RA  = atof(argv[1]);
+  DEC = atof(argv[2]);
+  OUTPUT = argv[3];
+  fake_field_center (RA, DEC);
+
+  /* chip size in pixels (output is 5x5 grid of chips */
+  dX = 0.1 / field.project.cdelt1;
+  dY = 0.1 / field.project.cdelt2;
+
+  refcat = greference (&Nrefcat);
+  project_refcat (refcat, Nrefcat);
+  if (FOCAL_PLANE != NULL) {
+    FILE *f;
+    f = fopen (FOCAL_PLANE, "w");
+    dump_stars (f, refcat, Nrefcat);
+    fclose (f);
+  }
+  if (NO_CHIPS) exit (0);
+
+  Nchip = 0;
+  for (i = -2; i < 3; i++) {
+    for (j = -2; j < 3; j++) {
+
+      /* find chip center in TP, FP and Sky */
+      Po = 2*dX*i;
+      Qo = 2*dY*j;
+      RD_to_XY (&Lo, &Mo, Po, Qo, &field.distort);
+      XY_to_RD (&Ro, &Do, Po, Qo, &field.project);
+
+      /* FP-Chip terms */
+      strcpy (map.ctype, "DEC--WRP");
+      map.crval1 = Lo;
+      map.crval2 = Mo;
+      map.crpix1 = dX;
+      map.crpix2 = dX;
+  
+      map.cdelt1 = 1.0;
+      map.cdelt2 = 1.0;
+
+      map.pc1_1  = 1;
+      map.pc2_2  = 1;
+      map.pc1_2  = 0;
+      map.pc2_1  = 0;
+
+      map.Npolyterms = 2;
+      for (N = 0; N < 7; N++) {
+	map.polyterms[N][0] = 0;
+	map.polyterms[N][1] = 0;
+      }
+      map.polyterms[2][1] = 1e-5;
+
+      /* project catalog stars to chip */
+      FPtoChip (refcat, Nrefcat, &map);
+
+      Nstars = 0;
+      NSTARS = 1000;
+      ALLOCATE (stars, SMPData, NSTARS);
+
+      /* find catalog stars on chip */
+      for (N = 0; N < Nrefcat; N++) {
+	if (refcat[N].X < 0) continue;
+	if (refcat[N].Y < 0) continue;
+	if (refcat[N].X >= 2*dX) continue;
+	if (refcat[N].Y >= 2*dY) continue;
+
+	/* add random noise - not gaussian noise */
+	stars[Nstars].X = refcat[N].X + SIGMA*(drand48() - 0.5);
+	stars[Nstars].Y = refcat[N].Y + SIGMA*(drand48() - 0.5);
+	stars[Nstars].M = 16.0;
+	stars[Nstars].dM = 0.02;
+	stars[Nstars].dophot = 1;
+	stars[Nstars].sky = 1.0;
+	stars[Nstars].Mgal = 16.0;
+	stars[Nstars].Map = 16.0;
+	stars[Nstars].fx = 1.0;
+	stars[Nstars].fy = 1.0;
+	stars[Nstars].df = 0.0;
+
+	Nstars ++;
+	if (Nstars >= NSTARS) {
+	  NSTARS += 1000;
+	  REALLOCATE (stars, SMPData, NSTARS);
+	}
+      }
+
+
+      /* Chip-Sky terms */
+      strcpy (coords.ctype, "DEC--WRP");
+      coords.crval1 = Ro;
+      coords.crval2 = Do;
+      coords.crpix1 = dX;
+      coords.crpix2 = dY;
+  
+      { 
+	double dP, dQ, dL, dM;
+	double Mx, Lx, scale;
+
+	dP = 10;
+	dQ = 10;
+	RD_to_XY (&Lx, &Mx, Po+dP, Qo+dQ, &field.distort);
+	dL = Lx - Lo;
+	dM = Mx - Mo;
+	scale = hypot (10.0, 10.0) / hypot (dL, dM);
+	coords.cdelt1 = scale/3600.0;
+	coords.cdelt2 = scale/3600.0;
+      }
+
+      coords.pc1_1  = 1;
+      coords.pc2_2  = 1;
+      coords.pc1_2  = 0;
+      coords.pc2_1  = 0;
+      coords.Npolyterms = 1;
+
+      header = mkheader (2*dX, 2*dY, Nstars, &coords);
+
+      sprintf (filename, "%s.%02d.fits", OUTPUT, Nchip);
+      wstars (filename, stars, Nstars, header);
+      gfits_free_header (header);
+      free (stars);
+      Nchip ++;
+    }
+  }
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkpolyterm.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkpolyterm.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkpolyterm.c	(revision 22322)
@@ -0,0 +1,14 @@
+# include "mosastro.h"
+
+int mkpolyterm (int n, int m) {
+  
+  int i, nt, N;
+  
+  N = 0;
+  nt = n + m;
+  for (i = 2; i < nt; i++) {
+    N += i + 1;
+  }
+  N += m;
+  return (N);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkstandards.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkstandards.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mkstandards.c	(revision 22322)
@@ -0,0 +1,99 @@
+# include "mosastro.h"
+# define DX 30.0
+# define DY 30.0
+
+extern double drand48();
+void init_random ();
+
+/* build a grid of reference stars */
+int main (int argc, char **argv) {
+
+  int i, N, Nstars, NSTARS, Random;
+  double x, y, m, dX, dY;
+  Header *header;
+  Coords coords;
+  SMPData *stars;
+
+  Random = FALSE;
+  if ((N = get_argument (argc, argv, "-random"))) {
+    Random = TRUE;
+    remove_argument (N, &argc, argv);
+    Nstars = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    fprintf (stderr, "USAGE: mkstandards (RA) (DEC) (half-width) (output) [-random Nstars]\n");
+    exit (2);
+  }
+
+  /* generate grid of stars */
+  dX = dY = 3600*atof (argv[3]);
+
+  /* bore site center guess */
+  strcpy (coords.ctype, "DEC--TAN");
+  coords.crval1 = atof(argv[1]);
+  coords.crval2 = atof(argv[2]);
+  coords.crpix1 = dX;
+  coords.crpix2 = dX;
+  
+  coords.cdelt1 = 1/3600.0;
+  coords.cdelt2 = 1/3600.0;
+
+  coords.pc1_1  = 1;
+  coords.pc2_2  = 1;
+  coords.pc1_2  = 0;
+  coords.pc2_1  = 0;
+  coords.Npolyterms = 1;
+
+  if (Random) {
+    
+    init_random ();
+
+    ALLOCATE (stars, SMPData, Nstars);
+    for (i = 0; i < Nstars; i++) {
+      x = 2*dX*drand48();
+      y = 2*dY*drand48();
+      m = 14.0 + 4.0*drand48();
+      stars[i].X = x;
+      stars[i].Y = y;
+      stars[i].M = m;
+      stars[i].dM = 0.02;
+    }
+  } else {
+
+    Nstars = 0;
+    NSTARS = 1000;
+    ALLOCATE (stars, SMPData, NSTARS);
+
+    for (x = 0; x < 2*dX; x += DX) {
+      for (y = 0; y < 2*dY; y += DY) {
+	stars[Nstars].X = x;
+	stars[Nstars].Y = y;
+	stars[Nstars].M = 16.0;
+
+	Nstars ++;
+	if (Nstars >= NSTARS) {
+	  NSTARS += 1000;
+	  REALLOCATE (stars, SMPData, NSTARS);
+	}
+      }
+    }
+  }
+
+  for (i = 0; i < Nstars; i++) {
+    stars[i].dophot = 1;
+    stars[i].sky = 1.0;
+    stars[i].Mgal = 16.0;
+    stars[i].Map = 16.0;
+    stars[i].fx = 1.0;
+    stars[i].fy = 1.0;
+    stars[i].df = 0.0;
+  }
+
+  header = mkheader (2*dX, 2*dY, Nstars, &coords);
+  wstars (argv[4], stars, Nstars, header);
+  gfits_free_header (header);
+
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mosastro.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mosastro.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/mosastro.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include "mosastro.h"
+# include <glob.h>
+
+int main (int argc, char **argv) {
+
+  int Nrefcat, Nastro;
+  double Cerror, DL, DM;
+  double Scale;
+  glob_t pglob;
+  StarData *refcat;
+  Gradients *grad;
+
+
+  ConfigInit (&argc, argv);
+  args (&argc, argv); 
+
+  if (argc != 4) { 
+    fprintf (stderr, "USAGE: mosastro (inglob) (ext) (mosaic.phu)\n");
+    exit (2);
+  }
+
+  pglob.gl_offs = 0;
+  glob (argv[1], 0, NULL, &pglob);
+
+  LoadStars (pglob.gl_pathc, pglob.gl_pathv);
+
+  /* use per-chip astrometry to find ra,dec range */ 
+  deproject_stars ();
+  field_stats (); /** needs coords from deproject_stars **/
+  init_field ();  /** needs results from field stats **/
+  init_chips ();  /** needs results from init_field **/
+  Scale = 3600.0 * field.project.cdelt1;
+
+  /* use field model to get TP & FP coords */
+  project_stars ();
+  if ((DUMP != NULL) && !strcmp (DUMP, "rawstars")) dump_rawstars();
+
+  refcat = greference (&Nrefcat);
+  project_refcat (refcat, Nrefcat);
+  if ((DUMP != NULL) && !strcmp (DUMP, "refcat")) dump_refcat(refcat, Nrefcat);
+
+  match (refcat, Nrefcat);
+  deproject_raw ();
+  project_ref ();
+  Cerror   = GetScatter (&Nastro, &DL, &DM, TRUE);
+  fprintf (stderr, "scatter (raw) : %6.4f for %d stars (%6.4f, %6.4f)\n", Cerror, Nastro, DL*Scale, DM*Scale);
+  if ((DUMP != NULL) && !strcmp (DUMP, "rawmatch")) dump_match();
+
+  grad = GetGradients ();
+  FitGradients (grad);
+  Cerror   = GetScatter (&Nastro, &DL, &DM, TRUE);
+  fprintf (stderr, "scatter (grad) : %6.4f for %d stars (%6.4f, %6.4f)\n", Cerror, Nastro, DL*Scale, DM*Scale);
+  if ((DUMP != NULL) && !strcmp (DUMP, "fitgrads")) dump_match();
+  Scale = 3600.0 * field.project.cdelt1;
+
+  FitChips (ChipOrder);
+  Cerror   = GetScatter (&Nastro, &DL, &DM, TRUE);
+  fprintf (stderr, "scatter (chip) : %6.4f for %d stars (%6.4f, %6.4f)\n", Cerror, Nastro, DL*Scale, DM*Scale);
+  if ((DUMP != NULL) && !strcmp (DUMP, "fitchips_unclip")) dump_match();
+
+  ClipOnFP (2.5);
+  Cerror   = GetScatter (&Nastro, &DL, &DM, TRUE);
+  fprintf (stderr, "scatter (clip) : %6.4f for %d stars (%6.4f, %6.4f)\n", Cerror, Nastro, DL*Scale, DM*Scale);
+
+  FitChips (ChipOrder);
+  Cerror   = GetScatter (&Nastro, &DL, &DM, TRUE);
+  fprintf (stderr, "scatter (chip) : %6.4f for %d stars (%6.4f, %6.4f)\n", Cerror, Nastro, DL*Scale, DM*Scale);
+
+  ClipOnFP (2.5);
+  Cerror   = GetScatter (&Nastro, &DL, &DM, TRUE);
+  fprintf (stderr, "scatter (clip) : %6.4f for %d stars (%6.4f, %6.4f)\n", Cerror, Nastro, DL*Scale, DM*Scale);
+
+  FitChips (ChipOrder);
+  Cerror   = GetScatter (&Nastro, &DL, &DM, TRUE);
+  fprintf (stderr, "scatter (chip) : %6.4f for %d stars (%6.4f, %6.4f)\n", Cerror, Nastro, DL*Scale, DM*Scale);
+
+  ClipOnFP (2.5);
+  Cerror   = GetScatter (&Nastro, &DL, &DM, TRUE);
+  fprintf (stderr, "scatter (clip) : %6.4f for %d stars (%6.4f, %6.4f)\n", Cerror, Nastro, DL*Scale, DM*Scale);
+
+  FitChips (ChipOrder);
+  Cerror   = GetScatter (&Nastro, &DL, &DM, FALSE);
+  fprintf (stderr, "scatter (brit) : %6.4f for %d stars (%6.4f, %6.4f)\n", Cerror, Nastro, DL*Scale, DM*Scale);
+  Cerror   = GetScatter (&Nastro, &DL, &DM, TRUE);
+  fprintf (stderr, "scatter (chip) : %6.4f for %d stars (%6.4f, %6.4f)\n", Cerror, Nastro, DL*Scale, DM*Scale);
+  if ((DUMP != NULL) && !strcmp (DUMP, "fitchips")) dump_match();
+
+  /* testcoords (); */
+  output (argv[2], argv[3]);
+  exit (0);
+
+/*
+  FitField (3);
+  FitChips (3);
+*/
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/output.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/output.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/output.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "mosastro.h"
+
+void output (char *ext, char *phu) {
+
+  int i, N;
+  char *p, outname[256];
+  FILE *f;
+  Header *header;
+  
+  for (i = 0; i < Nchip; i++) {
+    
+    /* convert chip[i].file to output name */
+    
+    p = strrchr (chip[i].file, '.');
+    N = p - chip[i].file + 1;
+    if (p == NULL) N = strlen (chip[i].file);
+
+    bzero (outname, 256);
+    strncpy (outname, chip[i].file, N);
+
+    strcat (outname, ext);
+
+    /* convert chip from PLY to WRP (linked to field model) */
+    strcpy (chip[i].map.ctype, "DEC--WRP");
+    PutCoords (&chip[i].map, &chip[i].header);
+    wchip (outname, &chip[i]);
+  }
+
+  field_combine ();
+
+  f = fopen (phu, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't create output file %s\n", phu);
+    exit (1);
+  }
+
+  header = mkmosaic (1, 1, 0, &field.project);
+  if (SAVE_RESID) {
+    SaveResiduals (f, header);
+    return;
+  }
+
+  fwrite (header[0].buffer, 1, header[0].size, f);
+  fclose (f);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/parse_time.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/parse_time.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/parse_time.c	(revision 22322)
@@ -0,0 +1,120 @@
+# include "mosastro.h"
+
+e_time parse_time (Header *header) {
+
+  double jd;
+  int Ny, Nf, mode;
+  int hour, min, sec, year, month, day;
+  char *py, *pm, *pd, *c;
+  char line[256];
+  e_time Nsec;
+
+  /* we want to find JD or MJD to get Nsec (seconds since 01/01/1970) */
+
+  /* try JD first */
+  if (strcasecmp (JDKeyword, "NONE")) {
+    uppercase (JDKeyword);
+    gfits_scan (header, JDKeyword, "%lf", 1, &jd);
+    Nsec = (jd - 2440587.5)*86400;
+    return (Nsec);
+  }
+
+  /* try MJD next */
+  if (strcasecmp (MJDKeyword, "NONE")) {
+    uppercase (MJDKeyword);
+    gfits_scan (header, MJDKeyword, "%lf", 1, &jd);
+    Nsec = (jd - 40587.0)*86400;
+    return (Nsec);
+  }
+    
+  /* get UT and DATE */
+  uppercase (UTKeyword);
+  gfits_scan (header, UTKeyword, "%s", 1, line);
+  /* remove ':' characters */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  sscanf (line, "%d %d %d", &hour, &min, &sec);
+
+  /* parse mode line */
+  uppercase (DateMode);
+  for (Ny = 0, c = strchr (DateMode, 'Y'); c != (char ) NULL; c = strchr (c + 1, 'Y'), Ny++);
+  if ((Ny != 2) && (Ny != 4)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  py = strchr (DateMode, 'Y');
+  pm = strchr (DateMode, 'M');
+  pd = strchr (DateMode, 'D');
+  if ((py == (char *) NULL) || (pm == (char *) NULL) || (pd == (char *) NULL)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  if ((py > pm) && (py < pd)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  if ((py > pd) && (py < pm)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  mode = 0;
+  if ((py < pm) && (pm < pd)) { mode = 1; }  /* yyyy-mm-dd */
+  if ((py < pm) && (pm > pd)) { mode = 2; }  /* yyyy-dd-mm */
+  if ((py > pm) && (pm < pd)) { mode = 3; }  /* mm-dd-yyyy */
+  if ((py > pm) && (pm > pd)) { mode = 4; }  /* dd-mm-yyyy */
+  if (!mode) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+
+  /* parse date entry */
+  uppercase (DateKeyword);
+  gfits_scan (header, DateKeyword, "%s",  1, line);
+  /* remove possible separators: ':', '/' '.', '-' */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  for (c = strchr (line, 0x2f); c != (char *) NULL; c = strchr (line, 0x2f)) { *c = ' '; }
+  for (c = strchr (line, 0x2e); c != (char *) NULL; c = strchr (line, 0x2e)) { *c = ' '; }
+  for (c = strchr (line, 0x2d); c != (char *) NULL; c = strchr (line, 0x2d)) { *c = ' '; }
+
+  Nf = 0;
+  switch (mode) {
+  case 1:
+    Nf = sscanf (line, "%d %d %d", &year, &month, &day);
+    break;
+  case 2:
+    Nf = sscanf (line, "%d %d %d", &year, &day, &month);
+    break;
+  case 3:
+    Nf = sscanf (line, "%d %d %d", &month, &day, &year);
+    break;
+  case 4:
+    Nf = sscanf (line, "%d %d %d", &day, &month, &year);
+    break;
+  }
+  if (Nf != 3) {
+    fprintf (stderr, "error in date entry (%s) or DATE-MODE format (%s)\n", line, DateMode);
+    exit (1);
+  }
+
+  if (year > 1000) {
+    if (Ny == 2) {
+      fprintf (stderr, "warning: mode line claims 2 digit year, but 4 digit year found\n");
+    }
+  } else {
+    if (Ny == 4) {
+      fprintf (stderr, "warning: mode line claims 4 digit year, but 2 digit year found\n");
+    }
+    if (year < 50) year += 100;
+    year += 1900;
+  }    
+
+  /* convert yy.mm.dd hh.mm.ss to Nsec since 1970 (jd = 2440587.5) */
+  /* note that in this section, tm_mon has range 1-12, unlike for gmtime () */
+  jd = day - 32075 + (int)(1461*(year + 4800 + (int)(((month)-14)/12))/4)
+    + (int)(367*((month) - 2 - (int)(((month) - 14)/12)*12)/12)
+    - (int)(3*(int)((year + 4900 + (int)(((month) - 14)/12))/100)/4) - 0.5;
+  /* jd is the julian day of the whole day only not the time */
+  Nsec = (jd - 2440587.5)*86400 + 3600.0*hour + min*60.0 + sec;
+  
+  return (Nsec);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/project.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/project.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/project.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "mosastro.h"
+
+int project_refcat (StarData *refcat, int Nrefcat) {
+
+  SkyToTP (refcat, Nrefcat, &field.project);
+  TPtoFP  (refcat, Nrefcat, &field.distort);
+
+  return (1);
+}
+
+int deproject_stars () {
+
+  int i;
+
+  for (i = 0; i < Nchip; i++) {
+    ChipToSky (chip[i].stars, chip[i].Nstars, &chip[i].coords);
+  }
+  return (1);
+}
+
+int project_stars () {
+
+  int i;
+
+  for (i = 0; i < Nchip; i++) {
+    SkyToTP (chip[i].stars, chip[i].Nstars, &field.project);
+    TPtoFP  (chip[i].stars, chip[i].Nstars, &field.distort);
+  }
+  return (1);
+}
+
+int deproject_raw () {
+
+  int i;
+
+  for (i = 0; i < Nchip; i++) {
+    ChipToFP (chip[i].raw, chip[i].Nmatch, &chip[i].map);
+    FPtoTP   (chip[i].raw, chip[i].Nmatch, &field.distort);
+    TPtoSky  (chip[i].raw, chip[i].Nmatch, &field.project);
+  }
+  return (1);
+}
+
+int project_ref () {
+
+  int i;
+
+  for (i = 0; i < Nchip; i++) {
+    SkyToTP  (chip[i].ref, chip[i].Nmatch, &field.project);
+    TPtoFP   (chip[i].ref, chip[i].Nmatch, &field.distort);
+    FPtoChip (chip[i].ref, chip[i].Nmatch, &chip[i].map);
+  }
+  return (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/random.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/random.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/random.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include <stdio.h>
+# include <time.h>
+# include <stdlib.h>
+
+void init_random () {
+
+  long A, B;
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/rfits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/rfits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/rfits.c	(revision 22322)
@@ -0,0 +1,58 @@
+# include "mosastro.h"
+
+int rfits (Chip *mychip) {
+
+  int i, Nx;
+  FILE *f;
+  FTable table;
+  SMPData *stars;
+
+  /* open file for stars */
+  f = fopen (mychip[0].file, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't open file to load stars\n");
+    exit (1);
+  }
+  fseek (f, mychip[0].header.size, SEEK_SET); 
+
+  /* init & load in table data */
+  table.header   = &mychip[0].theader;
+  if (!gfits_fread_matrix (f, &mychip[0].matrix, &mychip[0].header)) {
+    fprintf (stderr, "error reading file\n");
+    fclose (f);
+    return (FALSE);
+  }
+  if (!gfits_fread_ftable (f, &table, "SMPFILE")) {
+    fprintf (stderr, "error reading file\n");
+    gfits_free_matrix (&mychip[0].matrix);
+    fclose (f);
+    return (FALSE);
+  }
+
+  stars = gfits_table_get_SMPData (&table, &mychip[0].Nstars, NULL);
+  gfits_scan (table.header, "NAXIS1", "%d", 1, &Nx);
+
+  /* save raw data for output */
+  mychip[0].FITS = TRUE;
+  mychip[0].buffer = (char *) stars;
+  mychip[0].Nbuffer = Nx*mychip[0].Nstars;
+
+  if (mychip[0].Nstars < 5) { 
+    fprintf (stderr, "Too few stars for reliable solution, only %d\n", mychip[0].Nstars);
+    gfits_free_matrix (&mychip[0].matrix);
+    gfits_free_table (&table);
+    fclose (f);
+    return (FALSE);
+  }
+
+  /* note the different structures for mychip[0].stars and stars */
+  ALLOCATE (mychip[0].stars, StarData, mychip[0].Nstars);
+  for (i = 0; i < mychip[0].Nstars; i++) {
+    mychip[0].stars[i].X    = stars[i].X;
+    mychip[0].stars[i].Y    = stars[i].Y;
+    mychip[0].stars[i].Mag  = stars[i].M;
+    mychip[0].stars[i].dMag = stars[i].dM;
+  }    
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/rtext.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/rtext.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/rtext.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "mosastro.h"
+# define BYTES_STAR 66
+
+int rtext (Chip *mychip) {
+
+  int i, Nbytes, nbytes;
+  FILE *f;
+
+  f = fopen (mychip[0].file, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't read data from %s\n", mychip[0].file);
+    return (FALSE);
+  }
+  fseek (f, mychip[0].header.size, SEEK_SET); 
+
+  /* find expected number of stars */
+  gfits_scan (&mychip[0].header, "NSTARS", "%d", 1, &mychip[0].Nstars);
+  if (mychip[0].Nstars == 0) {
+    fprintf (stderr, "ERROR: can't get NSTARS from header for %s\n", mychip[0].file);
+    return (FALSE);
+  }
+  ALLOCATE (mychip[0].stars, StarData, mychip[0].Nstars);
+    
+  Nbytes = BYTES_STAR * mychip[0].Nstars;
+  ALLOCATE (mychip[0].buffer, char, Nbytes + 1);
+  nbytes = fread (mychip[0].buffer, 1, Nbytes, f);
+  if (nbytes != Nbytes) { exit (1); }
+  mychip[0].Nbuffer = Nbytes;
+  mychip[0].FITS = FALSE;
+
+  for (i = 0; i < mychip[0].Nstars; i++) {
+    bzero (&mychip[0].stars[i], sizeof(StarData));
+    dparse (&mychip[0].stars[i].X,    1, &mychip[0].buffer[i*BYTES_STAR]);
+    dparse (&mychip[0].stars[i].Y,    2, &mychip[0].buffer[i*BYTES_STAR]);
+    dparse (&mychip[0].stars[i].Mag,  3, &mychip[0].buffer[i*BYTES_STAR]);
+    dparse (&mychip[0].stars[i].dMag, 4, &mychip[0].buffer[i*BYTES_STAR]);
+    mychip[0].stars[i].dMag *= 0.001;  /* millimag errors stored in file */
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/testcoords.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/testcoords.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/testcoords.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "mosastro.h"
+
+void testcoords () {
+
+  int i;
+  FILE *f;
+  double R, D, P, Q, L, M, X, Y;
+  
+  fprintf (stderr, "starting test.dat\n");
+
+  /* version 1: transform with three separate stages */
+  /* X,Y -> L,M -> P,Q -> R,D */
+  f = fopen ("test.1.dat", "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't create output file \n");
+    exit (1);
+  }
+  for (i = 0; i < chip[0].Nmatch; i++) {
+    X = chip[0].raw[i].X;
+    Y = chip[0].raw[i].Y;
+    XY_to_RD (&L, &M, X, Y, &chip[0].map);
+    XY_to_RD (&P, &Q, L, M, &field.distort);
+    XY_to_RD (&R, &D, P, Q, &field.project);
+    fprintf (f, "%4d  %10.6f %10.6f  %8.2f %8.2f %8.2f %8.2f  %8.2f %8.2f\n", 
+	     i, R, D, P, Q, L, M, X, Y);
+  }
+  fclose (f);
+
+  /* version 2: transform with two separate stages */
+  /* X,Y -> L,M -> R,D */
+  f = fopen ("test.2.dat", "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't create output file \n");
+    exit (1);
+  }
+  field_combine ();
+  for (i = 0; i < chip[0].Nmatch; i++) {
+    X = chip[0].raw[i].X;
+    Y = chip[0].raw[i].Y;
+    XY_to_RD (&L, &M, X, Y, &chip[0].map);
+    XY_to_RD (&R, &D, L, M, &field.project);
+    fprintf (f, "%4d  %10.6f %10.6f   %8.2f %8.2f  %8.2f %8.2f\n", 
+	     i, R, D, L, M, X, Y);
+  }
+  fclose (f);
+
+
+  /* version 3: transform with automatic two stages in coordops */
+  /* X,Y -> R,D */
+  f = fopen ("test.3.dat", "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't create output file \n");
+    exit (1);
+  }
+  RegisterMosaic (&field.project);
+  strcpy (chip[0].map.ctype, "DEC--WRP");
+  for (i = 0; i < chip[0].Nmatch; i++) {
+    X = chip[0].raw[i].X;
+    Y = chip[0].raw[i].Y;
+    XY_to_RD (&R, &D, X, Y, &chip[0].map);
+    fprintf (f, "%4d  %10.6f %10.6f   %8.2f %8.2f\n", 
+	     i, R, D, X, Y);
+  }
+  fclose (f);
+  exit (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/transforms.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/transforms.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/transforms.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "mosastro.h"
+
+void ChipToSky (StarData *stars, int Nstars, Coords *coords) {
+  int i;
+  for (i = 0; i < Nstars; i++) {
+    XY_to_RD (&stars[i].R, &stars[i].D, stars[i].X, stars[i].Y, coords);
+    while (stars[i].R < 0.0)    stars[i].R += 360.0;
+    while (stars[i].R >= 360.0) stars[i].R -= 360.0;
+  }
+}
+
+void ChipToFP (StarData *stars, int Nstars, Coords *coords) {
+  int i;
+  for (i = 0; i < Nstars; i++) {
+    XY_to_RD (&stars[i].L, &stars[i].M, stars[i].X, stars[i].Y, coords);
+  }
+}
+
+void FPtoTP (StarData *stars, int Nstars, Coords *coords) {
+  int i;
+  for (i = 0; i < Nstars; i++) {
+    XY_to_RD (&stars[i].P, &stars[i].Q, stars[i].L, stars[i].M, coords);
+  }
+}
+
+void TPtoSky (StarData *stars, int Nstars, Coords *coords) {
+  int i;
+  for (i = 0; i < Nstars; i++) {
+    XY_to_RD (&stars[i].R, &stars[i].D, stars[i].P, stars[i].Q, coords);
+    while (stars[i].R < 0.0)    stars[i].R += 360.0;
+    while (stars[i].R >= 360.0) stars[i].R -= 360.0;
+  }
+}
+
+void SkyToTP (StarData *stars, int Nstars, Coords *coords) {
+  int i;
+  for (i = 0; i < Nstars; i++) {
+    RD_to_XY (&stars[i].P, &stars[i].Q, stars[i].R, stars[i].D, coords);
+  }
+}
+
+void TPtoFP (StarData *stars, int Nstars, Coords *coords) {
+  int i;
+  for (i = 0; i < Nstars; i++) {
+    RD_to_XY (&stars[i].L, &stars[i].M, stars[i].P, stars[i].Q, coords);
+  }
+}
+
+void FPtoChip (StarData *stars, int Nstars, Coords *coords) {
+  int i;
+  for (i = 0; i < Nstars; i++) {
+    RD_to_XY (&stars[i].X, &stars[i].Y, stars[i].L, stars[i].M, coords);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/warptest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/warptest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/warptest.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "mosastro.h"
+int XY_to_RD (double *ra, double *dec, double x, double y, Coords *coords);
+int RD_to_XY (double *x, double *y, double ra, double dec, Coords *coords);
+
+main (int argc, char **argv) {
+
+  int i, status;
+  double X, Y, x, y, p, q;
+  Coords coords;
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: warptest (system)\n");
+    exit (2);
+  }
+
+  /* bore site center guess */
+  sprintf (coords.ctype, "RA---%s", argv[1]);
+  coords.crval1 = 0.0;
+  coords.crval2 = 0.0;
+  coords.crpix1 = 0.0;
+  coords.crpix2 = 0.0;
+  coords.cdelt1 = 1.0;
+  coords.cdelt2 = 1.0;
+
+  /* 
+  coords.cdelt1 = 1.0/3600.0;
+  coords.cdelt2 = 1.0/3600.0;
+  */
+  
+  /** allow guess at field rotation?? **/
+  coords.pc1_1  = 1;
+  coords.pc2_2  = 1;
+  coords.pc1_2  = 0;
+  coords.pc2_1  = 0;
+
+  /* allow 2nd and 3rd order? */
+  /* how do we handle renormalization? (fixed at 1000 pixels??) */
+  coords.Npolyterms = 3;
+  for (i = 0; i < 7; i++) {
+    coords.polyterms[i][0] = 0;
+    coords.polyterms[i][1] = 0;
+  }
+  coords.polyterms[3][0] = 1e-10;
+  coords.polyterms[6][1] = 1e-10;
+
+  for (x = -10000; x <= 10000; x+= 500) {
+    for (y = -10000; y <= 10000; y+= 500) {
+      status = XY_to_RD (&p, &q, x, y, &coords);
+      status = RD_to_XY (&X, &Y, p, q, &coords);
+      fprintf (stdout, "%f %f   %f %f   %f %f\n", x, y, p, q, X, Y);
+      if (!status) exit (1);
+    }
+  }
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/wfits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/wfits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/wfits.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "mosastro.h"
+
+void wfits (char *filename, SMPData *stars, int Nstars, Header *header) {
+
+  Matrix matrix;
+  Header theader;
+  FTable table;
+
+  header[0].extend = TRUE;
+  header[0].Naxes = 0;
+  gfits_modify (header, "NAXIS",   "%d", 1, 0);
+  gfits_modify (header, "EXTEND",  "%t", 1, TRUE);
+  gfits_modify (header, "NEXTEND", "%d", 1, 1);
+
+  /* add in some keywords to specify the datatype & software version? */
+
+  /* create (empty) data matrix */
+  gfits_create_matrix (header, &matrix);
+    
+  table.header = &theader;
+  gfits_table_set_SMPData (&table, stars, Nstars);
+
+  gfits_write_header  (filename, header);
+  gfits_write_matrix  (filename, &matrix);
+  gfits_write_Theader (filename, &theader);
+  gfits_write_table   (filename, &table);
+  return;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/wstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/wstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/mosastro/src/wstars.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "mosastro.h"
+# define NCHAR 66 /* 65 char EXCLUDING return */
+
+void wchip (char *filename, Chip *data) {
+  
+  FILE *g;
+
+  if (data[0].FITS) {
+    wfits (filename, (SMPData *) data[0].buffer, data[0].Nstars, &data[0].header);
+  } else {
+
+    g = fopen (filename, "w");
+    if (g == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't create output file %s\n", filename);
+      exit (1);
+    }
+
+    fwrite (data[0].header.buffer, 1, data[0].header.size, g);
+    fwrite (data[0].buffer, 1, data[0].Nbuffer, g);
+    fclose (g);
+  }  
+}
+
+void wstars (char *filename, SMPData *stars, int Nstars, Header *header) {
+  
+  int i, Nchar;
+  FILE *g;
+  char line[NCHAR + 3];
+
+  g = fopen (filename, "w");
+  if (g == (FILE *) NULL) {
+    fprintf (stderr, "ERROR: can't create output file %s\n", filename);
+    exit (1);
+  }
+
+  fwrite (header[0].buffer, 1, header[0].size, g);
+
+  for (i = 0; i < Nstars; i++) {
+    if (stars[i].dophot == 0) continue;
+    Nchar = snprintf (line, NCHAR, "%6.1f %6.1f %6.3f %03d %2d %3.1f %6.3f %6.3f %6.2f %6.2f %5.1f", 
+		      stars[i].X, stars[i].Y, stars[i].M, 
+		      (int)(1000*stars[i].dM), stars[i].dophot, stars[i].sky, 
+		      stars[i].Mgal, stars[i].Map, stars[i].fx, stars[i].fy, stars[i].df);
+    
+    /* this is just a little funny.  NCHAR (in) includes the trailing NULL, Nchar (out) excludes it */
+    if (Nchar != NCHAR - 1) {
+      fprintf (stderr, "funny line %d (%d)\n%s\n", i, Nchar, line);
+    } else {
+      fprintf (g, "%s\n", line);
+    }
+  }
+
+  fclose (g);
+
+}
+
+/*
+
+  63.6 2869.5 17.568 157 17.568 17.568 25.01 25.00 360.0 7 2.9
+
+  63.6 2869.5 17.568 157 7 2.9
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/Makefile	(revision 22322)
@@ -0,0 +1,29 @@
+default: nightd
+help:
+	@echo "make options: nightd (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/nightd
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+nightd: $(BIN)/nightd.$(ARCH)
+install: $(DESTBIN)/nightd
+
+NIGHTD = \
+$(SRC)/nightd.$(ARCH).o 	$(SRC)/ConfigInit.$(ARCH).o 	\
+$(SRC)/DoCommand.$(ARCH).o 	$(SRC)/GetStatus.$(ARCH).o 	\
+$(SRC)/SetSignals.$(ARCH).o 	$(SRC)/StartUp.$(ARCH).o 	\
+$(SRC)/misc.$(ARCH).o
+
+$(NIGHTD): $(INC)/nightd.h
+$(BIN)/nightd.$(ARCH): $(NIGHTD)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,10 @@
+
+- nightd 1.3 : 2006.08.23
+  - moved to ChangeLog.txt
+
+nightd-1-2:
+  - fixed minor compile warning
+
+nightd-1-1:
+  - minor cleanups for gcc -Wall
+  - nightd will be superceded eventually by sched/pcontrol?
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/include/nightd.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/include/nightd.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/include/nightd.h	(revision 22322)
@@ -0,0 +1,47 @@
+# include <ohana.h>
+# include <signal.h>
+# include <errno.h>
+
+FILE *LogFile;
+char *Program;
+
+char logfile[256];
+char **MainCommand;
+char **InitCommand;
+char **DoneCommand;
+
+char TestCommand[256];
+
+char DateStr[256];
+char TimeStr[256];
+
+char PIDFile[256];
+
+float NightStart;
+float NightStop;
+
+int SuspendAction;
+
+int ConfigInit (int *argc, char **argv);
+void LoadConfig (int sig);
+int dms_to_ddd (double *Value, char *string);
+int SetPID (pid_t *Xpid, char *Xuser, char *Xmachine);
+int GetDateTime (char *datestr, char *timestr, float *time);
+int WaitForMinute ();
+int GetStatus ();
+int ResetConfig ();
+int SendShutdown ();
+int StartUp ();
+int SetSignals ();
+void ToggleSuspend (int sig);
+void Shutdown (int sig);
+int Expose (char *filename);
+int vsystem (char *line);
+char *ExpandWords (char *line);
+int pcommand (char *line, int timeout);
+int WaitForPeriod ();
+int DoCommand (char *command, char *name);
+int freeargs (char **arglist);
+
+int PERIOD;
+int TIMEOUT;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/scripts/ertrc
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/scripts/ertrc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/scripts/ertrc	(revision 22322)
@@ -0,0 +1,29 @@
+
+# multiple entries are allowed for each 
+# command line, MAIN, INIT, DONE. they 
+# are performed in sequence
+
+# there are run at the start of the night
+INIT_COMMAND el_plots init
+INIT_COMMAND elixir.reload sextract
+INIT_COMMAND elixir.reload imstats
+
+MAIN_COMMAND gettemps
+MAIN_COMMAND el_plots plot
+MAIN_COMMAND elixir.link
+
+# these are run at the end of the night
+DONE_COMMAND el_plots done
+DONE_COMMAND elixir.launch mkdetrend 
+
+# PID_FILE /h/eugene/skyprobe/ert.pid
+# LOG_FILE /h/eugene/skyprobe/ert.log
+
+PID_FILE /data/milo/elixir/log/ert.pid
+LOG_FILE /data/milo/elixir/log/ert.log
+
+NIGHT_START 18:00
+NIGHT_STOP  06:00
+
+PERIOD 180
+TIMEOUT 360
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/scripts/skyproberc
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/scripts/skyproberc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/scripts/skyproberc	(revision 22322)
@@ -0,0 +1,20 @@
+
+HOME      /h/skyprobe
+
+DATA_PATH $HOME/data
+PID_FILE  $HOME/.skyprobe.pid
+LOG_FILE  $HOME/sp_daemon.log
+
+CCD_TEMP -20
+EXPTIME   30
+
+INIT_COMMAND sp_command cool $CCD_TEMP
+INIT_COMMAND sp_command init $DATA_PATH/&DATE
+MAIN_COMMAND sp_command expose $EXPTIME $DATA_PATH/&DATE/sp_&DATE_&TIME.fits
+DONE_COMMAND sp_command warm
+
+NIGHT_START 18:00
+NIGHT_STOP  06:00
+
+PERIOD 60
+TIMEOUT 300
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/scripts/srtrc
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/scripts/srtrc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/scripts/srtrc	(revision 22322)
@@ -0,0 +1,21 @@
+
+# wrap the commands (init, main, done) in a single script
+# this will let the config info come from the config system
+
+INIT_COMMAND sp_plots init
+
+MAIN_COMMAND sp_plots plot 
+
+DONE_COMMAND sp_plots done &DATE
+
+PID_FILE /data/milo/elixir/log/srt.pid
+LOG_FILE /data/milo/elixir/log/srt.log
+
+# PID_FILE /h/eugene/skyprobe/skyprobe.pid
+# LOG_FILE /h/eugene/skyprobe/skyprobe.log
+
+NIGHT_START 18:00
+NIGHT_STOP  06:00
+
+PERIOD 60
+TIMEOUT 300
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,115 @@
+# include "nightd.h"
+
+static char *file;
+
+int ConfigInit (int *argc, char **argv) {
+
+  char *home;
+
+  /*** load configuration info ***/
+  home = getenv ("HOME");
+  if (home == (char *) NULL) {
+    fprintf (stderr, "can't determine HOME, please set\n");
+    exit (2);
+  }
+  ALLOCATE (file, char, strlen (home) + strlen (Program) + 10);
+
+  sprintf (file, "%s/.%src", home, Program);
+  LoadConfig (SIGKILL);
+  return (TRUE);
+}
+
+void LoadConfig (int sig) {
+
+  int i, Ncmd;
+  char *config, *found;
+  char line[256];
+  double tmp;
+
+  if (sig == SIGUSR1) {
+    fprintf (LogFile, "re-loading config file %s\n", file);
+    fflush (LogFile);
+  }
+
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (LogFile, "can't find configuration file %s\n", file);
+    exit (2);
+  }
+
+  /* need to check for error status */
+  ScanConfig (config, "PERIOD",                 "%d", 0, &PERIOD);
+  ScanConfig (config, "TIMEOUT",                "%d", 0, &TIMEOUT);
+
+  /* load list of InitCommands */
+  found = config;
+  Ncmd = 10;
+  ALLOCATE (InitCommand, char *, Ncmd);
+  for (i = 0; found != NULL; i++) {
+    ALLOCATE (InitCommand[i], char, 256);
+    bzero (InitCommand[i], 256);
+    found = ScanConfig (config, "INIT_COMMAND", "%s", i + 1, InitCommand[i]);
+    if (i == Ncmd - 1) {
+      Ncmd += 10;
+      REALLOCATE (InitCommand, char *, Ncmd);
+    }      
+  }
+  
+  /* load list of MainCommands */
+  found = config;
+  Ncmd = 10;
+  ALLOCATE (MainCommand, char *, Ncmd);
+  for (i = 0; found != NULL; i++) {
+    ALLOCATE (MainCommand[i], char, 256);
+    bzero (MainCommand[i], 256);
+    found = ScanConfig (config, "MAIN_COMMAND", "%s", i + 1, MainCommand[i]);
+    if (i == Ncmd - 1) {
+      Ncmd += 10;
+      REALLOCATE (MainCommand, char *, Ncmd);
+    }      
+  }
+
+  /* load list of DoneCommands */
+  found = config;
+  Ncmd = 10;
+  ALLOCATE (DoneCommand, char *, Ncmd);
+  for (i = 0; found != NULL; i++) {
+    ALLOCATE (DoneCommand[i], char, 256);
+    bzero (DoneCommand[i], 256);
+    found = ScanConfig (config, "DONE_COMMAND", "%s", i + 1, DoneCommand[i]);
+    if (i == Ncmd - 1) {
+      Ncmd += 10;
+      REALLOCATE (DoneCommand, char *, Ncmd);
+    }      
+  }
+
+  ScanConfig (config, "PID_FILE",               "%s", 0, PIDFile);
+  ScanConfig (config, "LOG_FILE",               "%s", 0, logfile);
+    
+  ScanConfig (config, "NIGHT_START",            "%s", 0, line);
+  if (!ohana_dms_to_ddd (&tmp, line)) { 
+    fprintf (LogFile, "format error in NIGHT_START\n");
+    exit (2);
+  } 
+  NightStart = tmp;
+  if (NightStart < 12.0) {
+    fprintf (LogFile, "warning: night starts before noon!\n");
+    fprintf (LogFile, "if you live in the Arctic Circle (or Antarctic), you might need to adjust the program\n");
+    exit (2);
+  }
+  ScanConfig (config, "NIGHT_STOP",             "%s", 0, line);
+  if (!ohana_dms_to_ddd (&tmp, line)) { 
+    fprintf (LogFile, "format error in NIGHT_STOP\n");
+    exit (2);
+  } 
+  NightStop = tmp;
+  if (NightStop > 12.0) {
+    fprintf (LogFile, "warning: night ends after noon!\n");
+    fflush (LogFile);
+  }
+  if (NightStop < 12.0) NightStop += 24.0;
+
+  /* check format of command calls: must have a certain set of %d, %s, etc */
+  free (config);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/DoCommand.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/DoCommand.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/DoCommand.c	(revision 22322)
@@ -0,0 +1,21 @@
+# include "nightd.h"
+
+/* special words:
+   word    variable
+   DATE    DateStr
+   TIME    TimeStr
+*/
+
+int DoCommand (char *command, char *name) {
+  
+  char *line;
+
+  line = strcreate (command);
+  line = ExpandWords (line);
+  if (pcommand (line, TIMEOUT)) {
+    fprintf (LogFile, "error running %s command\n", name);
+    fflush (LogFile);
+  }
+  free (line);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/GetStatus.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/GetStatus.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/GetStatus.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "nightd.h"
+
+int GetStatus () {
+
+  char user[256], machine[256];
+  pid_t pid;
+
+  if (SetPID (&pid, user, machine)) {
+    fprintf (stderr, "%s is not running\n", Program);
+    unlink (PIDFile);
+    exit (1);
+  }
+
+  fprintf (stderr, "%s apparently running:\n\n", Program);
+  fprintf (stderr, "  machine: %s\n", machine);
+  fprintf (stderr, "  user: %s\n", user);
+  fprintf (stderr, "  PID: %d\n", pid);
+  fprintf (stderr, "  remove %s if %s has died unexpectedly\n", PIDFile, Program);
+  exit (0);
+
+}
+  
+int ResetConfig () {
+
+  char user[256], machine[256], line[256];
+  pid_t pid;
+
+  if (SetPID (&pid, user, machine)) {
+    fprintf (stderr, "%s is not running\n", Program);
+    unlink (PIDFile);
+    exit (1);
+  }
+
+  sprintf (line, "rsh %s kill -USR1 %d", machine, pid);
+  system (line);
+  exit (0);
+
+}
+  
+int SendShutdown () {
+
+  char user[256], machine[256], line[256];
+  pid_t pid;
+
+  if (SetPID (&pid, user, machine)) {
+    fprintf (stderr, "%s is not running\n", Program);
+    unlink (PIDFile);
+    exit (1);
+  }
+
+  sprintf (line, "rsh %s kill -TERM %d", machine, pid);
+  system (line);
+  exit (0);
+
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/SetSignals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/SetSignals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/SetSignals.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "nightd.h"
+
+int SetSignals () {
+
+  signal (SIGTERM, Shutdown);
+  signal (SIGUSR1, LoadConfig);
+  signal (SIGUSR2, ToggleSuspend);
+  return (TRUE);
+}
+
+void ToggleSuspend (int sig) {
+
+  SuspendAction = SuspendAction ^ TRUE;
+
+}
+
+void Shutdown (int sig) {
+
+  fprintf (LogFile, "shutting down skyprobed\n");
+  fflush (LogFile);
+
+  if (unlink (PIDFile)) {
+    fprintf (LogFile, "error deleting PID File %s\n", PIDFile);
+    exit (1);
+  }   
+
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/StartUp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/StartUp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/StartUp.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "nightd.h"
+
+int StartUp () {
+
+  int i;
+  int night, NightTime;
+  char user[256], machine[256];
+  float time;
+  pid_t pid;
+
+  if (!SetPID (&pid, user, machine)) {
+    fprintf (stderr, "%s apparently running:\n\n", Program);
+    fprintf (stderr, "  machine: %s\n", machine);
+    fprintf (stderr, "  user: %s\n", user);
+    fprintf (stderr, "  PID: %d\n", pid);
+    fprintf (stderr, "  remove %s if %s has died unexpectedly\n", PIDFile, Program);
+    exit (1);
+  }
+
+  SetSignals ();
+
+  SuspendAction = FALSE;
+  NightTime = FALSE;
+
+  /* Event Loop */
+  while (1) {
+
+    if (SuspendAction) {
+      usleep (100000);
+      continue;
+    }
+
+    /* events happen at time % PERIOD */
+    WaitForPeriod ();
+
+    /* time should be UTCmidnight - UTCmidnight + 24 */
+    GetDateTime (DateStr, TimeStr, &time);
+    night = (time > NightStart) && (time < NightStop);
+
+    /* start of night */
+    if (night && !NightTime) {
+      NightTime = TRUE;
+      fprintf (LogFile, "beginning of night %s, running INIT commands\n", DateStr);
+      fflush (LogFile);
+      for (i = 0; InitCommand[i][0]; i++) {
+	DoCommand (InitCommand[i], "INIT");
+      }
+    }
+
+    /* end of night */
+    if (!night && NightTime) {
+      NightTime = FALSE;
+      fprintf (LogFile, "end of night %s, running DONE commands\n", DateStr);
+      fflush (LogFile);
+      for (i = 0; DoneCommand[i][0]; i++) {
+	DoCommand (DoneCommand[i], "DONE");
+      }
+    }
+
+    /* main event */
+    if (NightTime) {
+      for (i = 0; MainCommand[i][0]; i++) {
+	DoCommand (MainCommand[i], "MAIN");
+      }
+    }
+
+  }
+}
+
+/* 
+   SuspendAction - loop without operations, used for testing
+   NightStart    - time in hours of beginning of night
+   NightStop     - time in hours of end of night
+   NightTime     - current night / day state
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/misc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/misc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/misc.c	(revision 22322)
@@ -0,0 +1,278 @@
+# include "nightd.h"
+# include <time.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <unistd.h>
+# include <fcntl.h>
+
+char **getargs (char *);
+
+/* SetPID is always called outside of daemon loop, send errors to stderr */
+int SetPID (pid_t *Xpid, char *Xuser, char *Xmachine) {
+  
+  pid_t pid;
+  char *username, machine[256];
+  FILE *f;
+
+  f = fopen (PIDFile, "r");
+  if (f == (FILE *) NULL) { 
+
+    pid = getpid ();
+    username = getenv ("USER");
+    if (username == (char *) NULL) {
+      fprintf (stderr, "error getting username\n");
+      exit (2);
+    }
+    bzero (machine, 256);
+    if (gethostname (machine, 256)) {
+      fprintf (stderr, "error getting hostname\n");
+      exit (2);
+    }
+
+    f = fopen (PIDFile, "w");
+    if (f == (FILE *) NULL) { 
+      fprintf (stderr, "can't write to PID file %s\n", PIDFile);
+      exit (2);
+    }
+
+    fprintf (f, "PID:     %d\n", pid);
+    fprintf (f, "USER:    %-s\n", username);
+    fprintf (f, "MACHINE: %-s\n", machine);
+    fclose (f);
+    return (TRUE); 
+  }
+
+  fscanf (f, "%*s %d", Xpid);
+  fscanf (f, "%*s %s", Xuser);
+  fscanf (f, "%*s %s", Xmachine);
+  fclose (f);
+  return (FALSE);
+}
+
+/* date is the UT date at 09:00 local time for day starting at 12:00 */
+int GetDateTime (char *datestr, char *timestr, float *time) {
+
+  struct tm *local, *gmt;
+  struct timeval now;
+  time_t tsec, tref;
+  int dsec;
+  float hour;
+
+  gettimeofday (&now, (struct timezone *) NULL);
+  tsec = now.tv_sec;
+  local = localtime (&tsec);
+
+  sprintf (timestr, "%02dh%02dm", local[0].tm_hour, local[0].tm_min);
+  hour = local[0].tm_hour + local[0].tm_min / 60.0 + local[0].tm_sec / 3600.0;
+  if (hour < 12) hour += 24.0;
+
+  dsec = 3600.0*(33.0 - hour);
+  tref = tsec + dsec;
+
+  gmt = gmtime (&tref);
+
+  sprintf (datestr, "%04d%02d%02d", 1900 + gmt[0].tm_year, gmt[0].tm_mon + 1, gmt[0].tm_mday);
+  *time = hour;
+  
+  return (TRUE);
+}
+
+int WaitForPeriod () {
+
+  int Nsec;
+  struct timeval now;
+  
+  gettimeofday (&now, (struct timezone *) NULL);
+  Nsec = PERIOD - (now.tv_sec % PERIOD);
+  sleep (Nsec);
+  return (TRUE);
+}
+
+int pcommand (char *line, int timeout) {
+
+  int i, status, eof, done, pidstat, Nread, exit_good, exit_stat;
+  int rfd[2], wfd[2];
+  pid_t pid;
+  char buffer[0x4000];
+  char **arglist;
+
+  /* create pipes for communications */
+  status = pipe (rfd);
+  if (status < 0) {
+    perror ("pipe");
+    return (1);
+  }
+  status = pipe (wfd);
+  if (status < 0) {
+    perror ("pipe");
+    return (1);
+  }
+
+  arglist = getargs (line);
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    dup2 (wfd[0], STDIN_FILENO);
+    dup2 (rfd[1], STDOUT_FILENO);
+    dup2 (rfd[1], STDERR_FILENO);
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ); 
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    status = execvp (arglist[0], arglist);
+    fprintf (stderr, "error starting command %s\n", arglist[0]);
+    exit (1);
+  }
+
+  eof = 0x04;
+  write (wfd[1], &eof, 1);
+  close (wfd[1]);
+ 
+  /* ok to close these here? */
+  close (rfd[1]);
+  close (wfd[0]);
+
+  fcntl (rfd[0], F_SETFL, O_NONBLOCK);
+
+  done = FALSE;
+  exit_good = exit_stat = FALSE;
+  for (i = 0; !done && (i < 10*timeout); i++) {
+    Nread = read (rfd[0], buffer, 0x4000);
+    if (Nread > 0) {
+      fwrite (buffer, 1, Nread, LogFile);
+    }
+    fflush (LogFile);
+    pidstat = waitpid (pid, &status, WNOHANG);
+    if (pidstat == pid) { 
+      done = TRUE;
+      exit_good = WIFEXITED (status);
+      exit_stat = WEXITSTATUS (status);
+    } else {
+      usleep (100000);
+    }
+  }
+  
+  if (i == 10*timeout) {
+    exit_good = FALSE;
+    fprintf (LogFile, "timeout on %s\n", arglist[0]);
+    fflush (LogFile);
+    fprintf (stderr, "timeout on %s\n", arglist[0]);
+    kill (pid, SIGKILL);
+
+    done = FALSE;
+    for (i = 0; !done && (i < 2*timeout); i++) {
+      pidstat = waitpid (pid, &status, WNOHANG);
+      if (pidstat == pid) { 
+	done = TRUE;
+      } else {
+	usleep (100000);
+      }
+    }
+  }
+  
+  close (rfd[0]);
+  freeargs (arglist);
+  if (exit_good) return (exit_stat);
+  return (1);
+}
+    
+/* split line into words by spaces, end with NULL */
+char **getargs (char *input) {
+
+  char **args, *line, *p, *e;
+  int Nargs, N, Nchar;
+
+  line = strcreate (input);
+  stripwhite (line);
+  p = line;
+
+  /* count words in line */
+  Nargs = 0;
+  while (*p != 0) {
+    for (; (*p != 0) && !isspace (*p); p++);
+    for (; isspace (*p); p++);
+    Nargs ++;
+  }
+  Nargs ++;
+  ALLOCATE (args, char *, Nargs);
+
+  /* extract words from line */
+  N = 0;
+  e = line;
+  p = line;
+  while (*p != 0) {
+    for (; (*e != 0) && !isspace (*e); e++);
+    Nchar = e - p;
+    ALLOCATE (args[N], char, Nchar + 1);
+    strncpy (args[N], p, Nchar);
+    args[N][Nchar] = 0;
+    p = e;
+    for (; isspace (*p); p++);
+    e = p;
+    N ++;
+  }
+  args[N] = (char *) NULL;
+  free (line);
+  return (args);
+}
+
+int freeargs (char **arglist) {
+
+  int i;
+
+  for (i = 0; arglist[i] != (char *) NULL; i++) {
+    free (arglist[i]);
+  }
+  free (arglist);
+  return (TRUE);
+}
+
+char *ExpandWords (char *line) {
+
+  int Nin, Nout, Ncpy, NBYTES;
+  char *p1, *p2, word[256], value[256];
+  char *outline;
+  
+  NBYTES = 256;
+  ALLOCATE (outline, char, NBYTES);
+  Nout = 0;
+  Nin  = 0;
+  while ((p1 = strchr (&line[Nin], '&')) != (char *) NULL) {
+    Ncpy = p1 - line - Nin;
+    if (Nout + Ncpy >= NBYTES) {
+      NBYTES = Nout + Ncpy + 256;
+      REALLOCATE (outline, char, NBYTES - 2);
+    }
+    memcpy (&outline[Nout], &line[Nin], Ncpy);
+    Nout += Ncpy;
+    
+    p1 ++;
+    for (p2 = p1; isalnum (*p2); p2++);
+    memcpy (word, p1, p2 - p1);
+    word[p2-p1] = 0;
+    Nin += Ncpy + 1 + p2 - p1;
+    
+    /* substitute value for word */
+    bzero (value, 256);
+    if (!strcasecmp (word, "DATE")) strcpy (value, DateStr);
+    if (!strcasecmp (word, "TIME")) strcpy (value, TimeStr);
+
+    Ncpy = strlen(value);
+    if (Nout + Ncpy >= NBYTES - 2) {
+      NBYTES = Nout + Ncpy + 256;
+      REALLOCATE (outline, char, NBYTES);
+    }    
+    memcpy (&outline[Nout], value, Ncpy);
+    Nout += Ncpy;
+  }
+  Ncpy = strlen(&line[Nin]);
+  if (Nout + Ncpy >= NBYTES - 2) {
+    NBYTES = Nout + Ncpy + 256;
+    REALLOCATE (outline, char, NBYTES);
+  }  
+  memcpy (&outline[Nout], &line[Nin], Ncpy);
+  Nout += Ncpy;
+  outline[Nout] = 0;
+  free (line);
+  return (outline);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/nightd.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/nightd.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/nightd/src/nightd.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include "nightd.h"
+
+int main (int argc, char **argv) {
+
+  Program = filebasename (argv[0]);
+
+  LogFile = stderr;
+  ConfigInit (&argc, argv);  
+  
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: %s [mode]\n", Program);
+    fprintf (stderr, " mode = start, stop, status, config\n");
+    exit (2);
+  }
+
+  if (!strcasecmp(argv[1], "status")) GetStatus ();
+  if (!strcasecmp(argv[1], "config")) ResetConfig ();
+  if (!strcasecmp(argv[1], "stop"))   SendShutdown ();
+
+  /* execute in background, send output to logfile */
+  LogFile = fopen (logfile, "w");
+  if (LogFile == (FILE *) NULL) {
+    LogFile = stderr;
+    fprintf (LogFile, "can't open Log File %s, writing to stderr\n", logfile);
+  }
+  if (!strcasecmp(argv[1], "start"))  StartUp ();
+  /* if (!strcasecmp(argv[1], "test"))   TestMode (); */
+
+  fprintf (stderr, "invalid %s command %s\n", Program, argv[1]);
+  exit (2);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+lib
+bin
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/Makefile	(revision 22322)
@@ -0,0 +1,81 @@
+
+### this is the top-level makefile for the opihi collection of programs
+
+default: all
+
+### the include file dependencies need to be cleaned up still
+
+mana:     	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+dimm:     	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+dvo:      	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+pantasks: 	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+pantasks_server: lib.data lib.shell cmd.basic cmd.data cmd.astro
+pantasks_client: lib.data lib.shell cmd.basic cmd.data cmd.astro
+pclient:  	 lib.data lib.shell cmd.basic
+pcontrol: 	 lib.data lib.shell cmd.basic
+
+LIBS = lib.data lib.shell cmd.basic cmd.data cmd.astro
+
+PROGRAM = mana dvo pantasks pclient pcontrol
+SPECIAL = pantasks_client pantasks_server
+
+EXTRAS = dimm
+
+all:
+	for i in $(PROGRAM) $(SPECIAL); do make $$i || exit; done
+	make pantasks_client
+	make pantasks_server
+
+libs:
+	for i in $(LIBS); do make $$i || exit; done
+
+extras:
+	for i in $(EXTRAS); do make $$i || exit; done
+
+install:
+	for i in $(PROGRAM) $(SPECIAL); do make $$i.install || exit; done
+	make pantasks_client.install
+	make pantasks_server.install
+
+extras-install:
+	for i in $(EXTRAS); do make $$i.install || exit; done
+
+clean:
+	for i in $(PROGRAM) $(SPECIAL) $(EXTRAS) $(LIBS); do make $$i.clean || exit; done
+
+libs-uninstall:
+	for i in $(LIBS); do make $$i.uninstall || exit; done
+
+dist: clean
+	for i in $(PROGRAM) $(SPECIAL) $(EXTRAS) $(LIBS); do make $$i.dist || exit; done
+	rm -rf bin
+	rm -rf lib
+
+#############################################################
+
+pantasks_client pantasks_server:
+	if [ -d pantasks ]; then (cd pantasks && make $@); fi
+
+pantasks_client.install: pantasks_client
+	if [ -d pantasks ]; then (cd pantasks && make $@); fi
+
+pantasks_server.install: pantasks_server
+	if [ -d pantasks ]; then (cd pantasks && make $@); fi
+
+$(PROGRAM) $(LIBS) $(EXTRAS):
+	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
+
+%.dist:
+	if [ -d "$*" ]; then (cd $* && make dist); fi
+
+%.uninstall:
+	if [ -d "$*" ]; then (cd $* && make uninstall); fi
+
+.PHONY: $(PROGRAM) $(LIBS) pantasks_server pantasks_client
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/Makefile.Common
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/Makefile.Common	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/Makefile.Common	(revision 22322)
@@ -0,0 +1,88 @@
+
+# utilities #################################################
+# .SUFFIXES: .$(ARCH).o
+
+BASE_CFLAGS   =	$(CFLAGS)
+BASE_CPPFLAGS =	$(CPPFLAGS) -I$(INC) -I$(DESTINC) $(INCDIRS) -D$(ARCH)
+BASE_LDFLAGS  = $(LDFLAGS) -L$(LIB) -L$(DESTLIB) $(LIBDIRS) $(LIBFLAGS)
+
+%.c : %.c.in
+	sed "s|@DATADIR@|$(DATA)|" $< > $@
+
+%.$(ARCH).o : %.c
+	$(CC) $(FULL_CFLAGS) $(FULL_CPPFLAGS) -c $< -o $@
+
+$(BIN)/%.$(ARCH):
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	$(CC) $(FULL_CFLAGS) -o $@ $^ $(FULL_LDFLAGS)
+	@echo "compiled $*"
+	@echo ""
+
+$(DESTBIN)/%: $(BIN)/%.$(ARCH) 
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	rm -f $(DESTBIN)/$*
+	cp $(BIN)/$*.$(ARCH) $(DESTBIN)/$*
+	@echo "installed $*"
+	@echo ""
+
+$(LIB)/%.$(ARCH).a:
+	@if [ ! -d $(LIB) ]; then mkdir -p $(LIB); fi
+	rm -f $@
+	ar rcv $@ $^ 
+	$(RANLIB) $@
+	@echo "compiled library $*"
+	@echo ""
+
+$(DESTLIB)/%.a: $(LIB)/%.$(ARCH).a
+	@if [ ! -d $(DESTLIB) ]; then mkdir -p $(DESTLIB); fi
+	rm -f $@
+	cp $< $@
+
+$(LIB)/%.$(ARCH).so:
+	@if [ ! -d $(LIB) ]; then mkdir -p $(LIB); fi
+	rm -f $@
+	gcc -shared -Wl,-soname,$*.so -o $@ $^ -lc
+
+$(DESTLIB)/%.so: $(LIB)/%.$(ARCH).so
+	@if [ ! -d $(DESTLIB) ]; then mkdir -p $(DESTLIB); fi
+	rm -f $@
+	cp $< $@
+
+lib%.clean:
+	rm -f $(LIB)/lib$*.$(ARCH).a
+	rm -f $($*)
+	@echo ""
+
+%.help:
+	@echo "installing help files for $*"
+	@if [ ! -d $(DATA)/help ]; then mkdir -p $(DATA)/help; fi
+	@rm -f $(HOME)/$*/help/*~
+	@rm -f $(HOME)/$*/help/#*
+	@for i in `find $(HOME)/$*/help -maxdepth 1 -type f`; do cp -f $$i $(DATA)/help; done
+
+%.modules:
+	@echo "installing modules for $*"
+	@if [ ! -d $(DATA)/modules ]; then mkdir -p $(DATA)/modules; fi
+	@if [ ! -d $(HOME)/modules/$* ]; then echo "no modules for $*"; fi
+	@if [   -d $(HOME)/modules/$* ]; then rm -f $(HOME)/modules/$*/*~; fi
+	@if [   -d $(HOME)/modules/$* ]; then rm -f $(HOME)/modules/$*/#*; fi
+	@if [   -d $(HOME)/modules/$* ]; then for i in `find $(HOME)/modules/$* -name CVS -prune -o -type f -print`; do cp -f $$i $(DATA)/modules; done; fi
+
+%.clean:
+	rm -f $(BIN)/$*.$(ARCH)
+	@echo ""
+
+clean:
+	rm -f $(BIN)/*.$(ARCH)
+	rm -f $(LIB)/*.$(ARCH).a
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
+
+dist: clean
+	rm -rf $(BIN)/*
+	rm -rf $(LIB)/*
+
+clean-help:
+	@if [ ! -d $(DATA)/help ]; then mkdir -p $(DATA)/help; fi
+	rm -f $(DATA)/help/*
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/Makefile	(revision 22322)
@@ -0,0 +1,85 @@
+default: libastrocmd
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/cmd.astro
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# astro user commands ########################
+srcs = \
+$(SRC)/init.$(ARCH).o             \
+$(SRC)/altaz.$(ARCH).o	           \
+$(SRC)/biassub.$(ARCH).o	   \
+$(SRC)/cgrid.$(ARCH).o		   \
+$(SRC)/coords.$(ARCH).o	   \
+$(SRC)/cplot.$(ARCH).o		   \
+$(SRC)/csystem.$(ARCH).o	   \
+$(SRC)/coord_systems.$(ARCH).o	   \
+$(SRC)/ctimes.$(ARCH).o	   \
+$(SRC)/cval.$(ARCH).o		   \
+$(SRC)/czplot.$(ARCH).o	   \
+$(SRC)/drizzle.$(ARCH).o	   \
+$(SRC)/flux.$(ARCH).o		   \
+$(SRC)/fixwrap.$(ARCH).o	   \
+$(SRC)/gauss.$(ARCH).o		   \
+$(SRC)/getvel.$(ARCH).o	   \
+$(SRC)/getlst.$(ARCH).o	   \
+$(SRC)/medianmap.$(ARCH).o	   \
+$(SRC)/mkgauss.$(ARCH).o	   \
+$(SRC)/multifit.$(ARCH).o	   \
+$(SRC)/objload.$(ARCH).o	   \
+$(SRC)/outline.$(ARCH).o	   \
+$(SRC)/polar.$(ARCH).o		   \
+$(SRC)/precess.$(ARCH).o	   \
+$(SRC)/profile.$(ARCH).o	   \
+$(SRC)/radec.$(ARCH).o	   \
+$(SRC)/region.$(ARCH).o	   \
+$(SRC)/rotcurve.$(ARCH).o	   \
+$(SRC)/scale.$(ARCH).o		   \
+$(SRC)/sexigesimal.$(ARCH).o	   \
+$(SRC)/spec.$(ARCH).o		   \
+$(SRC)/star.$(ARCH).o		   \
+$(SRC)/transform.$(ARCH).o        \
+$(SRC)/imsub.$(ARCH).o		   \
+$(SRC)/imfit.$(ARCH).o		   \
+$(SRC)/imfit-fgauss.$(ARCH).o	   \
+$(SRC)/imfit-pgauss.$(ARCH).o	   \
+$(SRC)/imfit-pgauss-psf.$(ARCH).o	   \
+$(SRC)/imfit-qgauss.$(ARCH).o	   \
+$(SRC)/imfit-qgauss-psf.$(ARCH).o	   \
+$(SRC)/imfit-sgauss.$(ARCH).o	   \
+$(SRC)/imfit-sgauss-psf.$(ARCH).o	   \
+$(SRC)/imfit-qfgauss.$(ARCH).o	   \
+$(SRC)/imfit-qrgauss.$(ARCH).o	   
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/convert.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libastrocmd.$(ARCH).a: $(srcs)
+$(LIB)/libastrocmd.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libastrocmd.a: $(LIB)/libastrocmd.$(ARCH).a
+$(DESTLIB)/libastrocmd.$(DLLTYPE): $(LIB)/libastrocmd.$(ARCH).$(DLLTYPE)
+
+libastrocmd: $(DESTLIB)/libastrocmd.a $(DESTLIB)/libastrocmd.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libastrocmd.a
+	rm -f $(DESTLIB)/libastrocmd.$(DLLTYPE)
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/altaz.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/altaz.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/altaz.c	(revision 22322)
@@ -0,0 +1,88 @@
+# include "astro.h"
+
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+double atan2 (double y, double x);
+
+int altaz (int argc, char **argv) {
+  
+  double alt, az, lat, rot;
+  double ha, dec;
+  double sind, sinh, cosh;
+  char *latstr;
+
+  if (argc != 6) goto usage;
+
+  if (!strcmp (argv[1], "-h")) goto radec;
+  if (!strcmp (argv[1], "-c")) goto altaz;
+
+ radec:
+  /* ha/dec -> alt/az */
+  ha  = atof (argv[2]);
+  dec = atof (argv[3]);
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return (FALSE);
+  }
+  lat = atof (latstr);
+  free (latstr);
+ 
+  sind = dSIN (dec) * dSIN (lat) + dCOS (dec) * dCOS (ha) * dCOS (lat);
+  alt  = DEG_RAD * asin (sind);
+
+  sinh = - dCOS (dec) * dSIN (ha);
+  cosh =   dSIN (dec) * dCOS (lat) - dCOS (dec) * dCOS (ha) * dSIN (lat);
+
+  az = DEG_RAD * atan2 (sinh, cosh);
+  set_variable (argv[4], alt);
+  set_variable (argv[5], az);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(ha) * dSIN(lat) - dCOS(az) * dCOS(ha);
+  rot = -DEG_RAD * atan2 (sinh, cosh);
+  set_variable ("ROT", rot);
+
+  return (TRUE);
+  
+ altaz:
+  /* alt/az -> ha/dec */
+  alt = atof (argv[4]);
+  az  = atof (argv[5]);
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return (FALSE);
+  }
+  lat = atof (latstr);
+  free (latstr);
+
+  sind = dSIN (alt) * dSIN (lat) + dCOS (alt) * dCOS (az) * dCOS (lat);
+  dec  = DEG_RAD * asin (sind);
+
+  sinh = -dCOS (alt) * dSIN (az);
+  cosh =  dSIN (alt) * dCOS (lat) - dCOS (alt) * dCOS (az) * dSIN (lat);
+
+  ha = DEG_RAD * atan2 (sinh, cosh);
+  set_variable (argv[2], ha);
+  set_variable (argv[3], dec);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(ha) * dSIN(lat) - dCOS(az) * dCOS(ha);
+  rot = -DEG_RAD * atan2 (sinh, cosh);
+  set_variable ("ROT", rot);
+
+  return (TRUE);
+  
+ usage:
+  gprint (GP_ERR, "USAGE: altaz -h (ha) (dec) (alt) (az)\n");
+  gprint (GP_ERR, "USAGE: altaz -c (ha) (dec) (alt) (az)\n");
+  gprint (GP_ERR, "       -h alt/az to ha/dec, -c ha/dec to alt/az\n");
+  gprint (GP_ERR, "       returned values in variables provided\n");
+  return (FALSE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/biassub.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/biassub.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/biassub.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include "astro.h"
+
+int biassub (int argc, char **argv) {
+  
+  int i, j, k, N, dir, nlong, nwide, start;
+  int sx, sy, nx, ny, NX, NY, NoVector, Nval;
+  float *V, *DV, dV, *vect, *segment, val;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  xvec = yvec = NULL;
+  NoVector = TRUE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    NoVector = FALSE;
+    remove_argument (N, &argc, argv);
+    if ((xvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+    if ((yvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: biassub <buffer> sx sy nx ny dir [-v N V]\n");
+    gprint (GP_ERR, "  optional storage of vector and sequence in N and V\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+  dir = atof (argv[6]);
+  if ((dir != 0) && (dir != 1)) {
+    gprint (GP_ERR, " dir must be either 0 (x) or 1 (y)\n");
+    return (FALSE);
+  }
+  if (dir) {
+    start = sy;
+    nwide = nx;
+    nlong = ny;
+  } else {
+    start = sx;
+    nwide = ny;
+    nlong = nx;
+  }    
+  gprint (GP_LOG, "start: %d %d  size: %d %d\n", sx, sy, nx, ny);
+
+    if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  ALLOCATE (vect, float, nlong);
+  ALLOCATE (segment, float, nwide);
+
+  NX = buf[0].matrix.Naxis[0];
+  NY = buf[0].matrix.Naxis[1];
+  if (dir) {
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*NX + sx; 
+      for (i = 0; i < nx; i++, V++) {
+	segment[i] = *V;
+      }
+      fsort (segment, nwide);
+      val = Nval = 0;
+      for (k = 0.25*nwide; k <=0.75*nwide; k++) {
+	val += segment[k];
+	Nval ++;
+      }
+      vect[j-sy] = val / Nval;
+    }
+  } else {
+    for (i = 0; i < nx; i++) {
+      V = (float *)(buf[0].matrix.buffer) + sy*NX + sx + i; 
+      for (j = 0; j < ny; j++, V+=NX) {
+	segment[j] = *V;
+      }
+      fsort (segment, nwide);
+      val = Nval = 0;
+      for (k = 0.25*nwide; k <=0.75*nwide; k++) {
+	val += segment[k];
+	Nval ++;
+      }
+      vect[i] = val / Nval;
+    }
+  }
+
+  if (!NoVector) {
+    xvec[0].Nelements = yvec[0].Nelements = nlong;
+    REALLOCATE (xvec[0].elements, float, nlong);
+    REALLOCATE (yvec[0].elements, float, nlong);
+    for (i = 0; i < nlong; i++) {
+      xvec[0].elements[i] = i + start;
+      yvec[0].elements[i] = vect[i];
+    }
+  }
+
+  if (dir) {
+    /* here we run all the way across in X for the defined Y range */
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*NX; 
+      dV = vect[j];
+      for (i = 0; i < NX; i++, V++) {
+	*V -= dV;
+      }
+    }
+  } else {
+    /* here we run all the way across in Y for the defined X range */
+    for (j = 0; j < NY; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*NX + sx; 
+      DV = vect;
+      for (i = 0; i < nx; i++, V++, DV++) {
+	*V -= *DV;
+      }
+    }
+  }
+
+  free (segment);
+  free (vect);
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/cgrid.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/cgrid.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/cgrid.c	(revision 22322)
@@ -0,0 +1,399 @@
+# include "astro.h"
+# define CHECKELEMENTS \
+  if (N == NELEMENTS) { \
+    NELEMENTS +=200; \
+    REALLOCATE (Xvec.elements, float, NELEMENTS); \
+    REALLOCATE (Yvec.elements, float, NELEMENTS); \
+  }
+
+int cgrid (int argc, char **argv) {
+  
+  double range, lrange, factor, mantis, power, fmantis;
+  double firstRA, firstDEC, minorRA, minorDEC;
+  double r, d, dR, dD, D;
+  double x, y;
+  Vector Xvec, Yvec;
+  int kapa, NorthPole, SouthPole, N, OnPic, LOnPic, status, NELEMENTS, First;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: cgrid [style]\n");
+    return (FALSE);
+  }
+
+  /* are we plotting one of the poles? */
+  NorthPole = SouthPole = FALSE;
+  status = RD_to_XY (&x, &y, 0.0, 90.0, &graphmode.coords);
+  if ((x >= graphmode.xmin) && (x <= graphmode.xmax) && 
+      (y >= graphmode.ymin) && (y <= graphmode.ymax) && status)
+    NorthPole = TRUE;
+  status = RD_to_XY (&x, &y, 0.0, -90.0, &graphmode.coords);
+  if ((x >= graphmode.xmin) && (x <= graphmode.xmax) && 
+      (y >= graphmode.ymin) && (y <= graphmode.ymax) && status)
+    SouthPole = TRUE;
+
+  /* set spacings for RA */
+  minorRA = minorDEC = 0.1;
+  range = MIN (fabs(graphmode.xmax-graphmode.xmin), fabs(graphmode.ymax-graphmode.ymin));
+  if (NorthPole || SouthPole) range = 360;
+  lrange = log10(MAX(fabs(range), 1e-30));
+  factor = (int) (lrange);
+  if (lrange < 0) { factor -= 1; }
+  mantis = lrange - factor;
+  power = pow(10.0, factor);
+  fmantis = pow(10.0, mantis);
+  if ((fmantis >= 1.0) && (fmantis <=  2.0)) {
+    minorRA = 0.1 * power;
+  }
+  if ((fmantis > 2.0) && (fmantis <=  4.0)) {
+    minorRA = 0.2 * power;
+  }
+  if ((fmantis > 4.0) && (fmantis <=  6.0)) {
+    minorRA = 0.5 * power;
+  }
+  if ((fmantis > 6.0) && (fmantis <=  10.0)) {
+    minorRA = 0.5 * power;
+  }
+  dR = range / 100.0;
+  /* set spacings for DEC */
+  range = MIN (fabs(graphmode.xmax-graphmode.xmin), fabs(graphmode.ymax-graphmode.ymin));
+  lrange = log10(MAX(fabs(range), 1e-30));
+  factor = (int) (lrange);
+  if (lrange < 0) { factor -= 1; }
+  mantis = lrange - factor;
+  power = pow(10.0, factor);
+  fmantis = pow(10.0, mantis);
+  if ((fmantis >= 1.0) && (fmantis <=  2.0)) {
+    minorDEC = 0.1 * power;
+  }
+  if ((fmantis > 2.0) && (fmantis <=  4.0)) {
+    minorDEC = 0.2 * power;
+  }
+  if ((fmantis > 4.0) && (fmantis <=  6.0)) {
+    minorDEC = 0.5 * power;
+  }
+  if ((fmantis > 6.0) && (fmantis <=  10.0)) {
+    minorDEC = 0.5 * power;
+  }
+  dD = range / 100.0;
+
+  /* choose a starting point */
+  if ((int)(graphmode.coords.crval1/minorRA) == (graphmode.coords.crval1/minorRA)) {
+    firstRA = graphmode.coords.crval1;
+  } else {
+    firstRA = minorRA + minorRA*((int)(graphmode.coords.crval1/minorRA));
+  }
+  if ((int)(graphmode.coords.crval2/minorDEC) == (graphmode.coords.crval2/minorDEC)) {
+    firstDEC = graphmode.coords.crval2;
+  } else {
+    firstDEC = minorDEC + minorDEC*((int)(graphmode.coords.crval2/minorDEC));
+  }
+  if (SouthPole) firstDEC = -90;
+  if (NorthPole) firstDEC = 90;
+  
+  /* prepare vectors to hold data */
+  NELEMENTS = 200;
+  ALLOCATE (Xvec.elements, float, NELEMENTS);
+  ALLOCATE (Yvec.elements, float, NELEMENTS);
+  N = 0;
+  
+  /***  do consecutive RA lines, first increasing **/
+  OnPic = TRUE;
+  for (r = firstRA; (r <= firstRA + 180) && (OnPic); r += minorRA) {
+    /* first, DEC increasing */
+    LOnPic = TRUE;
+    OnPic = FALSE;
+    First = TRUE;
+    for (d = firstDEC; (d < 90 + dD) && (LOnPic || NorthPole || SouthPole); d += dD) {
+      D = MAX (-90, MIN(90, d));
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+    /* next, DEC decreasing */
+    First = TRUE;
+    LOnPic = TRUE;
+    for (d = firstDEC; (d > -90 - dD) && (LOnPic || NorthPole || SouthPole); d -= dD) {
+      D = MAX (-90, MIN(90, d));
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      } 
+    }
+  }
+
+  /***  do consecutive RA lines, decreasing **/
+  OnPic = TRUE;
+  for (r = firstRA; (r >=  firstRA - 180) && (OnPic); r -= minorRA) {
+    /* first, DEC increasing */
+    First = TRUE;
+    LOnPic = TRUE;
+    OnPic = FALSE;
+    for (d = firstDEC; (d < 90 + dD) && (LOnPic || NorthPole || SouthPole); d += dD) {
+      D = MAX (-90, MIN(90, d));
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+    /* next, DEC decreasing */
+    First = TRUE;
+    LOnPic = TRUE;
+    for (d = firstDEC; (d > -90 - dD) && (LOnPic || NorthPole || SouthPole); d -= dD) {
+      D = MAX (-90, MIN(90, d));
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+  }
+
+  /***  do consecutive DEC lines, first increasing **/
+  OnPic = TRUE;
+  for (d = firstDEC; (d < 90 + dD) && (OnPic); d += minorDEC) {
+    D = MAX (-90, MIN(90, d));
+    /* first, RA increasing */
+    LOnPic = TRUE;
+    OnPic = FALSE;
+    First = TRUE;
+    for (r = firstRA; (r < firstRA + 180) && (LOnPic || NorthPole || SouthPole); r += dR) {
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+    /* next, RA decreasing */
+    First = TRUE;
+    LOnPic = TRUE;
+    for (r = firstRA; (r > firstRA - 180) && (LOnPic || NorthPole || SouthPole); r -= dR) {
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+  }
+
+  /***  do consecutive DEC lines, decreasing **/
+  OnPic = TRUE;
+  for (d = firstDEC; (d > -90 - dD) && (OnPic); d -= minorDEC) {
+    D = MAX (-90, MIN(90, d));
+    /* first, RA increasing */
+    LOnPic = TRUE;
+    OnPic = FALSE;
+    First = TRUE;
+    for (r = firstRA; (r < firstRA + 180) && (LOnPic || NorthPole || SouthPole); r += dR) {
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+    /* next, RA decreasing */
+    First = TRUE;
+    LOnPic = TRUE;
+    for (r = firstRA; (r > firstRA - 180) && (LOnPic || NorthPole || SouthPole); r -= dR) {
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+  }
+  
+  /* send the line segments as connect-points */
+  Xvec.Nelements = Yvec.Nelements = N;
+  graphmode.style = 2; /* points */
+  graphmode.ptype = 100; /* connect a pair */
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/coord_systems.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/coord_systems.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/coord_systems.c	(revision 22322)
@@ -0,0 +1,143 @@
+# include "astro.h"
+    
+CoordTransform *InitTransform (CoordTransformSystem input, CoordTransformSystem output) {
+
+  CoordTransform *transform;
+  struct timeval now;
+  struct tm *local;
+  double T;
+
+  ALLOCATE (transform, CoordTransform, 1);
+  transform->isIdentity = FALSE;
+  
+  switch (input) {
+    case COORD_CELESTIAL:
+      switch (output) {
+	case COORD_CELESTIAL:
+	  transform->phi = 0.0;
+	  transform->Xo  = 0.0;
+	  transform->xo  = 0.0;
+	  transform->isIdentity = TRUE;
+	  break;
+	case COORD_GALACTIC:
+	  transform->phi = -62.60*RAD_DEG;
+	  transform->Xo  = 282.25*RAD_DEG;
+	  transform->xo  =  33.00;
+	  break;
+	case COORD_ECLIPTIC:
+	  gettimeofday (&now, NULL);
+	  local = localtime (&now.tv_sec);
+	  T = local[0].tm_year / 100.0;
+	  transform->phi = -1.0*RAD_DEG*(23.452294 - 0.013013*T - 0.000001639*T*T + 0.000000503*T*T*T);
+	  transform->Xo  = 0.0;
+	  transform->xo  = 0.0;
+	  break;
+	default:
+	  abort();
+      }
+      break;
+    case COORD_ECLIPTIC:
+      switch (output) {
+	case COORD_CELESTIAL:
+	  gettimeofday (&now, (struct timezone *) NULL);
+	  local = localtime (&now.tv_sec);
+	  T = local[0].tm_year / 100.0;
+	  transform->phi = RAD_DEG*(23.452294 - 0.013013*T - 0.000001639*T*T + 0.000000503*T*T*T);
+	  transform->Xo = 0.0;
+	  transform->xo = 0.0;
+	  break;
+	case COORD_GALACTIC:
+	  return NULL;
+	  break;
+	case COORD_ECLIPTIC:
+	  transform->phi = 0.0;
+	  transform->Xo  = 0.0;
+	  transform->xo  = 0.0;
+	  transform->isIdentity = TRUE;
+	  break;
+	default:
+	  abort();
+      }
+      break;
+    case COORD_GALACTIC:
+      switch (output) {
+	case COORD_CELESTIAL:
+	  transform->phi =  62.60*RAD_DEG;
+	  transform->Xo  =  33.00*RAD_DEG;
+	  transform->xo  = 282.25;
+	  break;
+	case COORD_GALACTIC:
+	  transform->phi = 0.0;
+	  transform->Xo  = 0.0;
+	  transform->xo  = 0.0;
+	  transform->isIdentity = TRUE;
+	  break;
+	case COORD_ECLIPTIC:
+	  return NULL;
+	default:
+	  abort();
+      }
+      break;
+    default:
+      abort();
+  }
+ 
+  // pre-calculated constants:
+  transform->sin_phi_cos_Xo = sin(transform->phi)*cos(transform->Xo);
+  transform->sin_phi_sin_Xo = sin(transform->phi)*sin(transform->Xo);
+  transform->cos_phi        = cos(transform->phi);
+  
+  transform->cos_phi_cos_Xo = cos(transform->phi)*cos(transform->Xo);
+  transform->cos_phi_sin_Xo = cos(transform->phi)*sin(transform->Xo);
+  transform->sin_phi        = sin(transform->phi);
+  transform->cos_Xo 	    = cos(transform->Xo);
+  transform->sin_Xo 	    = sin(transform->Xo);
+
+  return transform;
+}
+
+// input and output coordinates are in degrees
+int ApplyTransform (double *x, double *y, double X, double Y, CoordTransform *transform) {
+
+  double sin_x, sin_y, cos_x, cos_y;
+
+
+  if (transform == NULL) return (FALSE);
+
+  if (transform->isIdentity) {
+    *x = X;
+    *y = Y;
+    return (TRUE);
+  }
+
+  X *= RAD_DEG;
+  Y *= RAD_DEG;
+
+  // recast with constants extracted:
+  sin_y = cos(Y)*sin(X)*transform->sin_phi_cos_Xo - cos(Y)*cos(X)*transform->sin_phi_sin_Xo + sin(Y)*transform->cos_phi;
+  cos_y = sqrt (1 - sin_y*sin_y);
+
+  sin_x = cos(Y)*sin(X)*transform->cos_phi_cos_Xo - cos(Y)*cos(X)*transform->cos_phi_sin_Xo - sin(Y)*transform->sin_phi;
+  cos_x = cos(Y)*cos(X)*transform->cos_Xo + cos(Y)*sin(X)*transform->sin_Xo;
+      
+  // atan2 returns -pi : +pi
+  *x = DEG_RAD * atan2 (sin_x, cos_x) + transform->xo;
+  if ((*x) < 0.0) (*x) += 360;
+
+  // should be in range -pi/2 : +pi/2
+  *y = DEG_RAD * atan2 (sin_y, cos_y);
+
+  return (TRUE);
+}
+
+// sin_y = cos(Y)*sin(X - Xo)*sin(phi) + sin(Y)*cos(phi);
+
+// sin_x = (cos(Y)*sin(X - Xo)*cos(phi) - sin(Y)*sin(phi)) /  cos_y;
+// cos_x = cos(Y)*cos(X - Xo) / cos_y;
+
+// multiplying both sides by cos_y:
+// sin_x = (cos(Y)*sin(X - Xo)*cos(phi) - sin(Y)*sin(phi));
+// cos_x = cos(Y)*cos(X - Xo);
+      
+// sin(a - b) = sin(a)*cos(b) - sin(b)*cos(a);
+// cos(a - b) = sin(a)*sin(b) + cos(a)*cos(b);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/coords.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/coords.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/coords.c	(revision 22322)
@@ -0,0 +1,111 @@
+# include "astro.h"
+
+enum {NONE, SKY, PIXEL, VECTOR, SCALAR};
+
+int coords (int argc, char **argv) {
+
+  int i, mode, form, N, Quiet;
+  double Xin, Yin, Xout, Yout;
+  char *MOSAIC;
+  Coords coords, moscoords;
+  Buffer *buf, *mosbuffer;
+  Vector *xvec, *yvec;
+
+  MOSAIC = NULL;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    MOSAIC = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  form = NONE;
+  mode = NONE;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    mode = SKY;
+  }
+  if ((N = get_argument (argc, argv, "-c"))) {
+    if (mode == SKY) goto syntax;
+    remove_argument (N, &argc, argv);
+    mode = PIXEL;
+  }
+  if (mode == NONE) goto syntax;
+  if (argc != 4) goto syntax;
+
+  if (SelectScalar (argv[2], &Xin)) {
+    if (!SelectScalar (argv[3], &Yin)) {
+      gprint (GP_ERR, "syntax error: mixed vector and scalar?\n");
+      return (FALSE);
+    }
+    form = SCALAR;
+  } else {
+    if ((xvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((yvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    if (xvec[0].Nelements != yvec[0].Nelements) {
+      fprintf (stderr, "mis-matched vector lengths\n");
+      return (FALSE);
+    }
+    form = VECTOR;
+  }      
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) goto escape;
+  GetCoords (&coords, &buf[0].header);
+  if (!strcmp(&coords.ctype[4], "-WRP")) {
+    if (MOSAIC == NULL) {
+      gprint (GP_ERR, "must supply mosaic for WRP coords\n");
+      return (FALSE);
+    }
+    if ((mosbuffer = SelectBuffer (MOSAIC, OLDBUFFER, TRUE)) == NULL) goto escape;
+    GetCoords (&moscoords, &mosbuffer[0].header);
+    RegisterMosaic (&moscoords);
+  }
+  
+  if (form == SCALAR) {
+    if (mode == SKY) {
+      XY_to_RD (&Xout, &Yout, Xin, Yin, &coords);
+      if (!Quiet) gprint (GP_LOG, "%10.6f %10.6f\n", Xout, Yout);
+      set_variable ("RA", Xout);
+      set_variable ("DEC", Yout);
+      return (TRUE);
+    }
+    if (mode == PIXEL) {
+      RD_to_XY (&Xout, &Yout, Xin, Yin, &coords);
+      if (!Quiet) gprint (GP_LOG, "%7.2f %7.2f\n", Xout, Yout);
+      set_variable ("Xc", Xout);
+      set_variable ("Yc", Yout);
+      return (TRUE);
+    }
+  }
+  if (mode == SKY) {
+    for (i = 0; i < xvec[0].Nelements; i++) {
+      fXY_to_RD (&xvec[0].elements[i], &yvec[0].elements[i], xvec[0].elements[i], yvec[0].elements[i], &coords);
+    }
+    return (TRUE);
+  }
+  if (mode == PIXEL) {
+    for (i = 0; i < xvec[0].Nelements; i++) {
+      fRD_to_XY (&xvec[0].elements[i], &yvec[0].elements[i], xvec[0].elements[i], yvec[0].elements[i], &coords);
+    }
+    return (TRUE);
+  }
+  return (FALSE);
+
+ syntax:
+  gprint (GP_ERR, "USAGE: coords [buffer] (-c R D) | (-p X Y)\n");
+  gprint (GP_ERR, "only one of -p or -c can be used\n");
+  gprint (GP_ERR, " -p : from pixels to ra/dec\n");
+  gprint (GP_ERR, " -c : from ra/dec to pixels\n");
+ escape:
+  if (MOSAIC != NULL) free (MOSAIC);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/cplot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/cplot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/cplot.c	(revision 22322)
@@ -0,0 +1,85 @@
+# include "astro.h"
+
+int cplot (int argc, char **argv) {
+  
+  int i, kapa, Npts, status, leftside;
+  float *x, *y, *r, *d, Rmin, Rmax, Rmid;
+  Vector Xvec, Yvec, *xvec, *yvec;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cplot <ra> <dec> [style]\n");
+    return (FALSE);
+  }
+
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  Rmid = 0.5*(Rmin + Rmax);
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors are not the same length\n");
+    return (FALSE);
+  }
+
+  ALLOCATE (Xvec.elements, float, xvec[0].Nelements);
+  ALLOCATE (Yvec.elements, float, xvec[0].Nelements);
+    
+  Xvec.Nelements = xvec[0].Nelements;
+  Yvec.Nelements = xvec[0].Nelements;
+  
+  r = xvec[0].elements;
+  d = yvec[0].elements;
+  x = Xvec.elements;
+  y = Yvec.elements;
+  
+  Npts = 0;
+  for (i = 0; i < Xvec.Nelements; i++, r++, d++) {
+    while (*r < Rmin) *r += 360.0;
+    while (*r > Rmax) *r -= 360.0;
+
+    // for pair-by-pair connections, check on second point if we straddle the back midline
+    if ((graphmode.ptype == 100) && (i % 2)) {
+      leftside = (r[-1] < Rmid); // if first of the pair is left, second must be as well
+      if ( leftside && (r[0] > Rmid + 90)) { r[0] -= 360.0; }
+      if (!leftside && (r[0] < Rmid - 90)) { r[0] += 360.0; }
+    }
+    status = fRD_to_XY (x, y, *r, *d, &graphmode.coords);
+
+    // if we fail on one of the points, drop the corresponding pair
+    if (!status) {
+      if (graphmode.ptype == 100) {
+	if (i % 2) {
+	  // for odd points, skip previous also
+	  x--;
+	  y--;
+	  Npts--;
+	} else {
+	  // for even points, skip previous also
+	  i++;
+	  r++;
+	  d++;
+	}
+      }
+      continue;
+    }
+    x++;
+    y++;
+    Npts++;
+  }
+  Xvec.Nelements = Npts;
+
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, Npts, Xvec.elements, Yvec.elements, &graphmode);
+  
+  free (Xvec.elements);
+  free (Yvec.elements);
+    
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/csystem.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/csystem.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/csystem.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "astro.h"
+
+int csystem (int argc, char **argv) {
+
+  /* USAGE: csystem [C/G/E/H] [C/G/E/H] [epoch] */
+  int i;
+  double X, Y, x, y;
+  float *xptr, *yptr;
+  Vector *xvec, *yvec;
+  CoordTransformSystem input, output;
+  CoordTransform *transform;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: csystems [C/G/E/H] [C/G/E/H] X Y\n");
+    return (FALSE);
+  }
+
+  switch (argv[1][0]) {
+    case 'C': input = COORD_CELESTIAL; break;
+    case 'G': input = COORD_GALACTIC; break;
+    case 'E': input = COORD_ECLIPTIC; break;
+    default: abort();
+  }
+
+  switch (argv[2][0]) {
+    case 'C': output = COORD_CELESTIAL; break;
+    case 'G': output = COORD_GALACTIC; break;
+    case 'E': output = COORD_ECLIPTIC; break;
+    default: abort();
+  }
+
+  transform = InitTransform (input, output);
+  if (transform == NULL) {
+    gprint (GP_ERR, "transform %c to %c is not yet defined\n", argv[1][0], argv[2][0]);
+    return (FALSE);
+  }
+    
+  if (SelectScalar (argv[3], &X)) {
+    if (!SelectScalar (argv[4], &Y)) return (FALSE);
+      
+    ApplyTransform (&x, &y, X, Y, transform);
+
+    gprint (GP_LOG, "%10.6f %10.6f\n", x, y);
+    return (TRUE);
+  }
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[3], argv[4]);
+    return (FALSE);
+  }
+  
+  xptr = xvec[0].elements;
+  yptr = yvec[0].elements;
+
+  for (i = 0; i < xvec[0].Nelements; i++, xptr++, yptr++) {
+    // ApplyTransform takes (double *), but xptr, yptr are (float *)
+    ApplyTransform (&x, &y, *xptr, *yptr, transform);
+    *xptr = x;
+    *yptr = y;
+  }
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/ctimes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/ctimes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/ctimes.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "astro.h"
+
+int ctimes (int argc, char **argv) {
+
+  int Reference, TimeFormat, N;
+  double value;
+  time_t time, TimeReference;
+  char *date, *Variable;
+
+  Variable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    Variable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: ctimes [-ref (value) / -abs (date)] [-var name]\n");
+    return (FALSE);
+  }
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Reference = FALSE;
+  if (!strcmp (argv[1], "-ref")) Reference = TRUE;
+
+  if (Reference) {
+
+    value = atof (argv[2]);
+    time = TimeRef (value, TimeReference, TimeFormat);
+    date = ohana_sec_to_date (time);
+    
+    if (Variable != (char *) NULL) {
+      set_str_variable (Variable, date);
+      free (Variable);
+    } else {
+      gprint (GP_ERR, "time: %s\n", date);
+    }
+
+    free (date);
+    return (TRUE);
+
+  } else {
+
+    if (strcmp (argv[1], "-abs")) {
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+
+    if (!ohana_str_to_time (argv[2], &time)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    
+    value = TimeValue (time, TimeReference, TimeFormat);
+    
+    if (Variable != (char *) NULL) {
+      set_variable (Variable, value);
+      free (Variable);
+      return (TRUE);
+    }
+    gprint (GP_ERR, "time: %f\n", value);
+    return (TRUE);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/cval.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/cval.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/cval.c	(revision 22322)
@@ -0,0 +1,51 @@
+# include "astro.h"
+
+int cval (int argc, char **argv) {
+  
+  int i, j, Nx;
+  int sx, sy, nx, ny, xo, yo, dx, dy;
+  float *V, cval, val, sn, sky;
+  Buffer *buf;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: cval <buffer> x y dx dy sky\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  xo = atof (argv[2]);
+  yo = atof (argv[3]);
+  dx = atof (argv[4]);
+  dy = atof (argv[5]);
+  sky = atof (argv[6]);
+
+  sx = xo - dx;
+  sy = yo - dy;
+  nx = 2*dx + 1;
+  ny = 2*dy + 1;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  V = (float *)buf[0].matrix.buffer;
+  Nx = buf[0].matrix.Naxis[0];
+  val = V[xo + yo*Nx];
+
+  sn = 0;
+  cval = 0;
+  for (j = sy; j < sy + ny; j++) {
+    for (i = sx; i < sx + nx; i++) {
+      cval += (val - V[i + j*Nx]) / sqrt(V[i + j*Nx]);
+      sn += SQ (V[i + j*Nx] - sky) / V[i + j*Nx];
+    }
+  }
+
+  gprint (GP_ERR, "cval: %f  sn: %f\n", cval, sqrt(sn));
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/czplot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/czplot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/czplot.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "astro.h"
+
+int czplot (int argc, char **argv) {
+  
+  int i, kapa, Npts;
+  double min, range, Rmin, Rmax;
+  float *in, *out, *r, *d, *x, *y;
+  Vector Xvec, Yvec, Zvec, *xvec, *yvec, *zvec;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: czplot <x> <y> <z> min max\n");
+    return (FALSE);
+  }
+
+  min = atof(argv[4]);
+  range = atof(argv[5]) - min;
+  Rmin = graphmode.coords.crval1 - 180.0;
+  Rmax = graphmode.coords.crval1 + 180.0;
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[2]);
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[3]);
+    return (FALSE);
+  }
+  Xvec.Nelements = xvec[0].Nelements;
+  Yvec.Nelements = xvec[0].Nelements;
+  Zvec.Nelements = zvec[0].Nelements;
+  ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+  ALLOCATE (Zvec.elements, float, Zvec.Nelements);
+  
+  r   = xvec[0].elements;
+  d   = yvec[0].elements;
+  in  = zvec[0].elements;
+  x   = Xvec.elements;
+  y   = Yvec.elements;
+  out = Zvec.elements;
+  for (i = 0; i < Zvec.Nelements; i++, in++, out++, r++, d++, x++, y++) {
+    *out = MIN (1.0, MAX (0.01, (*in - min) / range));
+    while (*r < Rmin) *r += 360.0;
+    while (*r > Rmax) *r -= 360.0;
+    fRD_to_XY (x, y, *r, *d, &graphmode.coords);
+  }
+
+  graphmode.style = 2;
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0;
+  Npts = Xvec.Nelements;
+  PlotVectorTriplet (kapa, Npts, Xvec.elements, Yvec.elements, Zvec.elements, &graphmode);
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (Zvec.elements);
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/drizzle.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/drizzle.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/drizzle.c	(revision 22322)
@@ -0,0 +1,302 @@
+# include "astro.h"
+
+/*** needs mosaic astrometry ***/
+
+static double XO, XX, XY;
+static double YO, YX, YY;
+static int ZERO;
+
+static int ROTATE;
+static double rot_phi, rot_alpha, rot_delta;
+static double rot_cdp, rot_sdp;
+
+int map_output_to_input (int Npix, double df);
+int map_input_to_output (int Npix, double df);
+int set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix);
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin);
+
+Coords coords_in, coords_out;
+Buffer *in, *out, *wt, *mask;
+
+int drizzle (int argc, char **argv) {
+
+  int Nlinear, Np, N;
+  double scale_in, scale_out, df;
+
+  ZERO = FALSE;
+  if ((N = get_argument (argc, argv, "-zero"))) {
+    ZERO = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  ROTATE = FALSE;
+  if ((N = get_argument (argc, argv, "-roll"))) {
+    /* -roll phi alpha_pole delta_pole */
+    /* XXX need to clarify the meaning of phi, alpha, delta */
+    ROTATE = TRUE;
+    remove_argument (N, &argc, argv);
+    rot_phi   = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    rot_alpha = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    rot_delta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+
+    rot_cdp = cos(RAD_DEG*rot_delta);
+    rot_sdp = sin(RAD_DEG*rot_delta);
+  }
+
+  mask = NULL;
+  if ((N = get_argument (argc, argv, "-mask"))) {
+    remove_argument (N, &argc, argv);
+    if ((mask = SelectBuffer (argv[N], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: transform <from> <to> <weight> (Nlinear)\n");
+    gprint (GP_ERR, "  output buffer must exist with target astrometry header\n");
+    gprint (GP_ERR, "  Nlinear is the pixel scale for linear astrometric transformation\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((wt  = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nlinear = atoi (argv[4]);
+
+  GetCoords (&coords_in, &in[0].header);
+  GetCoords (&coords_out, &out[0].header);
+
+  /* for the moment, disable WRP / DIS */
+  if (!strcmp(&coords_in.ctype[4], "-WRP") || !strcmp(&coords_out.ctype[4], "-WRP")) {
+    gprint (GP_ERR, "WRP mode not implemented for astrom\n");
+    return (FALSE);
+  }
+  
+  scale_in = sqrt(fabs(coords_in.cdelt1*coords_in.cdelt2*(coords_in.pc1_1*coords_in.pc2_2 - coords_in.pc1_2*coords_in.pc2_1)));
+  scale_out = sqrt(fabs(coords_out.cdelt1*coords_out.cdelt2*(coords_out.pc1_1*coords_out.pc2_2 - coords_out.pc1_2*coords_out.pc2_1)));
+  
+  gprint (GP_ERR, "%f - %f\n", scale_in, scale_out);
+
+  if (scale_in > scale_out) {
+    Np = MAX (1, 3*scale_out / scale_in);
+    df = 1.0 / Np;
+    map_output_to_input (Nlinear, df);
+  } else {
+    Np = MAX (1, 3*scale_in / scale_out);
+    df = 1.0 / Np;
+    map_input_to_output (Nlinear, df);
+  }
+  return (TRUE);
+}
+
+/* mode 1: input pixels >> output pixels: loop over output pixels */
+/* mode 2: input pixels << output pixels: loop over input pixels */
+/* mode 3: input pixels ~= output pixels: drizzle input to output */
+
+/* loop over the input pixels, map input output image */
+int map_output_to_input (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt, *Vmk;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+  Vmk  = NULL;
+  Vmk = (mask == NULL) ? NULL : (float *) mask[0].matrix.buffer;
+
+  nx = in[0].header.Naxis[0];
+  ny = in[0].header.Naxis[1];
+  Nx = out[0].header.Naxis[0];
+  Ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, Nx*Ny*sizeof(float));
+    bzero (Vwt,  Nx*Ny*sizeof(float));
+  }
+
+  gprint (GP_ERR, "mapping output to input\n");
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      if (!set_linear_terms (&coords_out, &coords_in, i, j, Npix)) continue;
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  No = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  Ni = (int)x + ((int)y)*nx;
+
+	  if (Vmk && Vmk[Ni]) continue;
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+/* loop over the input pixels, map input output image */
+int map_input_to_output (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt, *Vmk;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+  Vmk  = NULL;
+  Vmk = (mask == NULL) ? NULL : (float *) mask[0].matrix.buffer;
+
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  nx = out[0].header.Naxis[0];
+  ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, nx*ny*sizeof(float));
+    bzero (Vwt,  nx*ny*sizeof(float));
+  }
+
+  gprint (GP_ERR, "mapping input to output\n");
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      if (!set_linear_terms (&coords_in, &coords_out, i, j, Npix)) continue;
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  Ni = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  No = (int)x + ((int)y)*nx;
+
+	  if (Vmk && Vmk[Ni]) continue;
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+int rotate_coords (double *phi, double *theta, double alpha, double delta) {
+
+  double sda, cda, cd, sd, sth;
+  double x, y;
+  
+  sda = sin(RAD_DEG*(alpha - rot_alpha));
+  cda = cos(RAD_DEG*(alpha - rot_alpha));
+  sd = sin(RAD_DEG*delta);
+  cd = cos(RAD_DEG*delta);
+  
+  sth = -cd*sda*rot_cdp + sd*rot_sdp;
+  y   = +cd*sda*rot_sdp + sd*rot_cdp;
+  x   = +cd*cda;
+
+  *theta = DEG_RAD*asin(sth);
+  *phi   = DEG_RAD*atan2(y,x) + rot_phi;
+  
+  while (*phi <   0.0) *phi += 360.0;
+  while (*phi > 360.0) *phi -= 360.0;
+  return (TRUE);
+}
+
+/* find the linear astrometric fix between images at this location */
+int set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix) {
+
+  int n;
+  double x, y, x2, y2, xy, X, Y, Xx, Xy, Yx, Yy;
+  double Xin, Yin, Xout, Yout;
+  double Sx2, Sy2, Sxy, SXx, SXy, SYx, SYy;
+  double N, r, d, phi, theta;
+
+  Xin = Yin = 0;
+  N = x = y = x2 = y2 = xy = X = Y = Xx = Xy = Yx = Yy = 0;
+
+  /* define several test points, fit a line to the input,output pairs */
+  for (n = 0; n < 3; n++) {
+
+    switch (n) {
+    case 0:
+      Xin = i;
+      Yin = j;
+      break;
+    case 1:
+      Xin = i + Npix;
+      Yin = j;
+      break;
+    case 2:
+      Xin = i;
+      Yin = j + Npix;
+      break;
+    }
+
+    if (!XY_to_RD (&r, &d, Xin, Yin, in)) return (FALSE);
+    if (ROTATE) { 
+      rotate_coords (&phi, &theta, r, d);
+      r = phi;
+      d = theta;
+    }
+    if (!RD_to_XY (&Xout, &Yout, r, d, out)) return (FALSE);
+
+    x  += Xin;
+    y  += Yin;
+    x2 += Xin*Xin;
+    y2 += Yin*Yin;
+    xy += Xin*Yin;
+    X  += Xout;
+    Y  += Yout;
+    Xx += Xout*Xin;
+    Xy += Xout*Yin;
+    Yx += Yout*Xin;
+    Yy += Yout*Yin;
+    N  += 1.0;
+  }
+
+  Sx2 = x2 - x*x/N;
+  Sy2 = y2 - y*y/N;
+  Sxy = xy - x*y/N;
+  SXx = Xx - X*x/N;
+  SXy = Xy - X*y/N;
+  SYx = Yx - Y*x/N;
+  SYy = Yy - Y*y/N;
+  
+  XX = (SXx*Sy2 - SXy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XY = (SXy*Sx2 - SXx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XO = X/N - XX*x/N - XY*y/N;
+
+  YX = (SYx*Sy2 - SYy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YY = (SYy*Sx2 - SYx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YO = Y/N - YX*x/N - YY*y/N;
+  return (TRUE);
+}
+
+
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin) {
+  *Xout = XO + XX*Xin + XY*Yin;
+  *Yout = YO + YX*Xin + YY*Yin;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/fixwrap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/fixwrap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/fixwrap.c	(revision 22322)
@@ -0,0 +1,110 @@
+# include "astro.h"
+
+int fixwrap (int argc, char **argv) {
+  
+  int i, j, Nflip, n, Ny, Nx, flip, sat, rowfix;
+  float *Vin, *outf, *outb, dO;
+  Buffer *in;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: fixwrap <in> (rowfix)\n");
+    return (FALSE);
+  }
+
+  if ((in = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  rowfix = atoi (argv[2]);
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+
+  ALLOCATE (outf, float, MAX(Nx, Ny));
+  ALLOCATE (outb, float, MAX(Nx, Ny));
+
+  for (j = 0; j < Ny; j++) {
+    Vin  = (float *)(in[0].matrix.buffer)  + j*Nx;
+
+    /* measure forward flips */ 
+    sat = FALSE;
+    for (i = 0; i < Nx; i++) {
+      if ((i < 1056) || (i > 2079)) {
+	outf[i] = Vin[i];
+	continue;
+      }
+
+      dO = 2*outf[i-1] - outf[i-2] - Vin[i];
+      flip = (fabs(dO - 0x8000) < fabs(dO));
+
+      /* going onto saturation */
+      if (!sat && (Vin[i] > 32766.5) && (Vin[i+1]  > 32766.5)) sat  = TRUE;
+      if (!sat && (Vin[i] > 32766.5) && (Vin[i-Nx] > 65534.5)) sat  = TRUE;
+
+      /* exiting saturation region */
+      if ( sat && (Vin[i] < 32766.5) && (Vin[i-1] < 32766.5)) sat = FALSE;
+
+      if (sat) flip = TRUE;
+
+      outf[i] = flip ? (Vin[i] + 0x8000) : Vin[i];
+    }
+
+    /* measure backward flips */ 
+    sat = FALSE;
+    for (i = Nx - 1; i >= 0; i--) {
+      if ((i < 1056) || (i > 2077)) {
+	outb[i] = Vin[i];
+	continue;
+      }
+
+      dO = 2*outb[i+1] - outb[i+2] - Vin[i];
+      flip = (fabs(dO - 0x8000) < fabs(dO));
+
+      /* going onto saturation */
+      if (!sat && (Vin[i] > 32766.5) && (Vin[i-1] > 32766.5)) sat  = TRUE;
+
+      /* exiting saturation region */
+      if ( sat && (Vin[i] < 32766.5) && (Vin[i+1] < 32766.5)) sat = FALSE;
+
+      if (sat) flip = TRUE;
+
+      outb[i] = flip ? (Vin[i] + 0x8000) : Vin[i];
+    }
+
+    /* compare forward and backward flips: where they disagree, use column to predict */
+    for (i = 0; (j > 1) && (i < Nx); i++) {
+      if ((i < 1056) || (i > 2077)) continue;
+      if (outf[i] != outb[i]) {
+	/* use this column to predict, not the row */
+	dO = 2*Vin[i - Nx] - Vin[i-2*Nx] - Vin[i];
+	flip = (fabs(dO - 0x8000) < fabs(dO));
+	outf[i] = flip ? Vin[i] + 0x8000 : Vin[i];
+	outb[i] = outf[i];  /* save this for the row segments below */
+      }
+    }
+
+    /* compare this row and previous (now fixed) row. if large segments are flipped, fix them */
+    for (i = 0; rowfix && (j > 1) && (i < Nx); i++) {
+      if ((i < 1056) || (i > 2077)) continue;
+
+      if (fabs(outf[i] - Vin[i-Nx]) > 15000) {
+	Nflip = 0;
+	for (n = i - 8; n < i + 9; n++) {
+	  if (fabs(outb[n] - Vin[n-Nx]) > 15000) {
+	    Nflip ++;
+	  }
+	}
+	if (Nflip > 5) {
+	  if (outf[i] - Vin[i-Nx] > 15000) {
+	    outf[i] -= 0x8000;
+	  } else {
+	    outf[i] += 0x8000;
+	  }	    
+	}
+      }
+    }
+    for (i = 0; i < Nx; i++) {
+      Vin[i] = outf[i];
+    }
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/flux.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/flux.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/flux.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include "astro.h"
+
+int flux (int argc, char **argv) {
+  
+  int i, j, k, xmin, ymin, xmax, ymax;
+  void *oldsignal;
+  double ax, ay, s, S, flux;
+  double bx[5], by[5], x[5], y[5], bb[5];
+  float *V;
+  FILE *f;
+  Buffer *buf;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: flux <buffer> (region)\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "file %s not found\n", argv[2]);
+    return (FALSE);
+  }
+
+  xmin = buf[0].matrix.Naxis[0];
+  xmax = 0;
+  ymin = buf[0].matrix.Naxis[1];
+  ymax = 0;
+  for (i = 0; i < 4; i++) {
+    fscanf (f, "%lf %lf", &x[i], &y[i]);
+    xmin = MAX (0, MIN (xmin, x[i] - 1));
+    ymin = MAX (0, MIN (ymin, y[i] - 1));
+    xmax = MIN (MAX (xmax, x[i] + 1), buf[0].matrix.Naxis[0]);
+    ymax = MIN (MAX (ymax, y[i] + 1), buf[0].matrix.Naxis[1]);
+  }
+  fclose (f);
+
+  x[4] = x[0]; y[4] = y[0];
+  for (i = 0; i < 4; i++) {
+    bx[i] = x[i+1] - x[i];
+    by[i] = y[i+1] - y[i];
+  }
+  bx[4] = bx[0]; by[4] = by[0];
+  for (i = 0; i < 4; i++) {
+    bb[i] = hypot (bx[i], by[i]) * SIGN (bx[i]*by[i+1] - bx[i+1]*by[i]);
+  }
+  gprint (GP_ERR, "%f %f %f %f\n", bb[0], bb[1], bb[2], bb[3]);
+
+  /* this only works for convex contours --
+   we have to add up the angles for concave contours */
+  flux = 0;
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (j = ymin; (j < ymax) && !interrupt; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + xmin; 
+    for (i = xmin; (i < xmax) && !interrupt; i++, V++) {
+      S = 1.0;
+      for (k = 0; k < 4; k++) {
+	ax = i - x[k];
+	ay = j - y[k];
+	s = (ay*bx[k] - ax*by[k]) / bb[k] + 0.5;
+	/* s = b x a / |b|, with the correct sign (above) so inside is positive */
+	s = MAX (0.0, MIN (1.0, s));  /* s is between 0.0 and 1.0 */
+	S *= s;
+      }
+      flux += S * (*V);
+    }
+  }
+  signal (SIGINT, oldsignal);
+
+  gprint (GP_LOG, "flux: %f\n", flux);
+  set_variable ("FLUX", flux);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/gauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/gauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/gauss.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "astro.h"
+
+int gauss (int argc, char **argv) {
+
+  char key[20];
+  int i, N, Npix, Nborder, Nspot;
+  double X, Y, Z, ZP, RA, DEC, max;
+  int kapa;
+  char *name;
+  Buffer *buf;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImageData (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  Nborder = 3;
+  if ((N = get_argument (argc, argv, "-border"))) {
+    remove_argument (N, &argc, argv);
+    Nborder  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Nborder = MAX (Nborder, 1);
+  
+  max = 60000;
+  if ((N = get_argument (argc, argv, "-sat"))) {
+    remove_argument (N, &argc, argv);
+    max  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if ((argc != 2) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: gauss Npix [Nspots] [-border N] [-sat cnts]\n");
+    return (FALSE);
+  }
+  
+  if (kapa < 1) {
+    gprint (GP_ERR, "no active TV\n");
+    return (FALSE);
+  }
+
+  Nspot = 0;
+  Npix = atof (argv[1]);
+  if (argc == 3) {
+    Nspot = atof (argv[2]);
+  }
+
+  if ((buf = SelectBuffer (data.name, OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  KiiCursorOn (kapa);
+
+  for (i = 0; (i < Nspot) || (Nspot == 0); i++) {
+    KiiCursorRead (kapa, &X, &Y, &ZP, &RA, &DEC, key);
+    if (!strcasecmp (key, "Q")) break;
+    Z = get_aperture_stats (&buf[0].matrix, (int)(X+0.5), (int)(Y+0.5), Npix, Nborder, max);
+  }
+  KiiCursorOff (kapa);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/getlst.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/getlst.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/getlst.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "astro.h"
+
+int getlst (int argc, char **argv) {
+
+  int N;
+  time_t time;
+  double jd, lst, longitude;
+  char *Variable;
+
+  Variable = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    Variable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (argc != 3) goto syntax;
+
+  if (!ohana_str_to_time (argv[1], &time)) {
+      if (Variable != NULL) free (Variable);
+      return (FALSE);
+  }
+
+  longitude = atof (argv[2]);
+
+  jd = ohana_sec_to_jd (time);
+  fprintf (stderr, "jd: %f\n", jd);
+
+  lst = ohana_lst (jd, longitude);
+
+  if (Variable != NULL) {
+      set_variable (Variable, lst);
+      free (Variable);
+      return (TRUE);
+  }
+  gprint (GP_ERR, "lst: %f\n", lst);
+  return (TRUE);
+
+ syntax:
+  gprint (GP_ERR, "USAGE: getlst (time) (longitude) [-var name]\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/getvel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/getvel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/getvel.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "astro.h"
+
+int getvel (int argc, char **argv) {
+  
+  int i, n, Ncurve;
+  int nx, ny;
+  double L, V, Vo, dV, Bo, dB;
+  double xo, yo;
+  double sl, cl, wo, Ro, Rs, wr, r, fr, d, x;
+  double R[100], T[100], W[100];
+  FILE *f;
+  Buffer *buf;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: rotcurve buf X Y curve.txt\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[4], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't find rotation curve data file %s\n", argv[4]);
+    return (FALSE);
+  }
+  for (i = 0; fscanf (f, "%lf %lf", &R[i], &T[i]) != EOF; i++) {
+    W[i] = T[i] / R[i];
+  }  
+  fclose (f);
+  Ncurve = i;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  nx = buf[0].matrix.Naxis[0];
+  ny = buf[0].matrix.Naxis[1];
+
+  /* we expect the input image to have units of velocity, lattitude, and longitude */
+  gfits_scan (&buf[0].header, "CRVAL1", "%lf", 1, &Vo);
+  gfits_scan (&buf[0].header, "CDELT1", "%lf", 1, &dV);
+  gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &xo);
+  gfits_scan (&buf[0].header, "CRVAL2", "%lf", 1, &Bo);
+  gfits_scan (&buf[0].header, "CDELT2", "%lf", 1, &dB);
+  gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &yo);
+  gfits_scan (&buf[0].header, "CRVAL3", "%lf", 1, &L);
+  Vo *= 0.001;
+  dV *= 0.001;
+
+  while (L >= 360) {L -= 360.0;}
+  while (L < 0.0)  {L += 360.0;}
+  gprint (GP_ERR, "L: %f\n", L);
+
+  cl = cos (L*RAD_DEG);
+  sl = sin (L*RAD_DEG);
+  wo = 25.0;
+  Ro = 10.0;
+  Rs = Ro*sl;
+  x = atof (argv[2]);
+  /* this method depends on wr monotonically decreasing */
+
+  V = (x - xo) * dV + Vo;
+  wr = V/Rs + wo;
+  for (n = 0; (n < Ncurve) && (wr < W[n]); n++);
+  if ((n == 0) || (n == Ncurve)) {
+    gprint (GP_ERR, "velocity out of reasonable range\n");
+    gprint (GP_ERR, "%f %f %f %f\n", V, wr, W[0], W[Ncurve-1]);
+    return (TRUE);
+  }
+  r = (wr - W[n]) *  (R[n-1] - R[n]) / (W[n-1] - W[n]) + R[n];
+  fr = (Ro/r);
+  if (r < fabs(Rs)) { /* can't be on rotation curve */
+    gprint (GP_ERR, "velocity out of reasonable range\n");
+    gprint (GP_ERR, "%f %f %f %f %f %f %f\n", V, wr, W[0], W[Ncurve-1], r, fr, Rs);
+    return (TRUE);
+  }
+  if (r < Ro)
+    d = Ro*cl - sqrt(r*r - Rs*Rs);
+  else 
+    d = Ro*cl + sqrt(r*r - Rs*Rs);
+  
+  gprint (GP_ERR, "dist: %f, vel: %f\n", d, V);
+
+  return (TRUE);
+
+} 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/biassub
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/biassub	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/biassub	(revision 22322)
@@ -0,0 +1,9 @@
+
+  biassub buffer sx sy nx ny dir [-v N V]
+
+  Apply a bias correction.  The region defines a vector 
+  (median is taken perpendicular to the dir direction) which 
+  is subtracted from the image.
+
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/cgrid
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/cgrid	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/cgrid	(revision 22322)
@@ -0,0 +1,6 @@
+
+  cgrid
+
+  draws a grid in RA and DEC on window 0 approriate for the current
+  region.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/coords
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/coords	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/coords	(revision 22322)
@@ -0,0 +1,19 @@
+
+  coords <buffer> (filename)
+
+  "coords" loads astrometric parameters for the given buffer from the
+header of the named file.  The parameters are applied to the buffer,
+and will register in the Ki'i window if the buffer it tv'ed again.
+This is particularly convenient for intercomparing coordinate system
+or in the event the astrometry is stored in a different file (eg, a
+stripped off header).  Currently, the only format of coordinate
+information that is looked for are gene's linear transformation: RA_O,
+RA_X, RA_Y, DEC_O, DEC_X, DEC_Y such that the RA,DEC of an object is
+given by:
+
+RA  = RA_O  + x*RA_X  + y*RA_Y
+DEC = DEC_O + x*DEC_X + y*DEC_Y.
+
+
+  See also: rd, tv
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/flux
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/flux	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/flux	(revision 22322)
@@ -0,0 +1,7 @@
+
+   flux buffer (file)
+
+   calculate the flux enclosed by the contour given in the file.  The
+   file contains the coordinates of the polygon corners.
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/gauss
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/gauss	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/gauss	(revision 22322)
@@ -0,0 +1,15 @@
+
+   gauss Npix [Nspots] [-border N] [-sat cnts]
+
+   calculate statistics on stars, assuming a Gaussian profile.  The
+   user types a key on a star in the Kii window, and the resulting
+   information is printed on the screen.  This is really aperture
+   photometry.  Npix defines the aperture width (square aperture), the
+   optional -border defines the number of pixels in an annulus used to
+   find the background, -sat defines the value of a saturated pixel.
+   If the Nspots option is given, exactly Nspots stars are chosen.
+   Otherwise, the user may type on stars until the 'q' is typed.  The
+   results of the last star are stored the a set of Mana variables.
+
+   See also: star, cursor
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/profile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/profile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/profile	(revision 22322)
@@ -0,0 +1,6 @@
+
+   profile <buffer> <X vector> <Y vector> x y N
+
+   Find the radial profile of an image at the location x, y.  the
+   radius is placed in the X vector and the pixel value is placed in
+   the Y vector.   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/region
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/region	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/help/region	(revision 22322)
@@ -0,0 +1,18 @@
+
+  region RA DEC Radius [projection]
+
+  define the current sky region and optionally the projection.  
+  RA, DEC and Radius are all values in decimal degrees.  The possible 
+  values for "projection" are:
+
+  TAN - a tangent plane projection (default, and typical for optical images)
+  SIN - a sine plane projection (more appropriate for polar regions)
+  AIT - aitoff projection (good for the full sky)
+  GLS - ?? (also good for the full sky)
+
+  optional orientation flags:
+
+  -ew : display with East Left (default)
+  +ew : display with East Right 
+  -ns : display with North Down
+  +ns : display with North Up (default)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-fgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-fgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-fgauss.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "imfit.h"
+
+float fgaussTD (float, float, float *, int, float *);
+void  fgaussCL ();
+
+void fgauss_setup (char *name) {
+
+  if (strcmp(name, "fgauss")) return;
+
+  fitfunc = fgaussTD;
+  imfit_cleanup = fgaussCL;
+  Npar = 7;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4] = get_variable_default ("SXYg", 0);
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  sky = &par[6];
+}
+
+void fgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+}
+
+/* real 2D gaussian -- x, y, sx, sy, sxy, I, sky */
+float fgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  r = exp (-z);
+  q = par[5]*r;
+  f = q + par[6];
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+  }
+  return (f);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-pgauss-psf.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-pgauss-psf.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-pgauss-psf.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "imfit.h"
+
+float pgauss_psfTD (float, float, float *, int, float *);
+void  pgauss_psfCL ();
+
+void  pgauss_psf_setup (char *name) {
+
+  if (strcmp(name, "pgauss_psf")) return;
+
+  fitfunc = pgauss_psfTD;
+  imfit_cleanup = pgauss_psfCL;
+  Npar = 4;
+  Nfpar = 3;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = get_variable_default ("Zpk", 10000);
+  par[3] = get_variable_default ("Sg", 0.0);
+  sky = &par[3];
+
+  fpar[0] = 2.35 / get_variable_default ("SXg", 2.0);
+  fpar[1] = 2.35 / get_variable_default ("SYg", 2.0);
+  fpar[2] = get_variable_default ("SXYg", 0);
+}
+
+void pgauss_psfCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("Zpk",  par[2]);
+  set_variable ("Sg",   par[3]);
+}
+
+/* pseudo 2D gaussian -- x, y, (sx), (sy), (sxy), I, sky */
+float pgauss_psfTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  /* par -> fpar: (2,0), (3,1), (4,2) */
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = fpar[0]*X;
+  py = fpar[1]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + fpar[2]*X*Y;
+  r = 1.0 / (1 + z + 0.5*z*z*(1 + z/3)); /* ~ exp (-Z) */
+  f = par[2]*r + par[3];
+  q = par[2]*r*r*(1 + z + 0.5*z*z);
+  /* note difference from gaussian: q = par[5]*r */
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*fpar[0] + fpar[2]*Y);
+    dpar[1] = q*(2*py*fpar[1] + fpar[2]*X);
+    dpar[2] = +r;
+    dpar[3] = +1;
+  }
+  return (f);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-pgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-pgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-pgauss.c	(revision 22322)
@@ -0,0 +1,69 @@
+# include "imfit.h"
+
+float pgaussTD (float, float, float *, int, float *);
+void  pgaussCL ();
+
+void  pgauss_setup (char *name) {
+
+  if (strcmp(name, "pgauss")) return;
+
+  fitfunc = pgaussTD;
+  imfit_cleanup = pgaussCL;
+  Npar = 7;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0.0);
+  par[1] = get_variable_default ("Yg", 0.0);
+  par[2] = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4] = get_variable_default ("SXYg", 0.0);
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  sky = &par[6];
+}
+
+void pgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+}
+
+/* pseudo 2D gaussian -- x, y, sx, sy, sxy, I, sky */
+float pgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  r = 1.0 / (1 + z + 0.5*z*z*(1 + z/3)); /* ~ exp (-Z) */
+  f = par[5]*r + par[6];
+  q = par[5]*r*r*(1 + z + 0.5*z*z);
+  /* note difference from gaussian: q = par[5]*r */
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+  }
+  return (f);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qfgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qfgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qfgauss.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "imfit.h"
+
+float qfgaussTD (float, float, float *, int, float *);
+void  qfgaussCL ();
+
+void qfgauss_setup (char *name) {
+
+  if (strcmp(name, "qfgauss")) return;
+
+  fitfunc = qfgaussTD;
+  imfit_cleanup = qfgaussCL;
+  Npar = 7;
+  Nfpar = 2;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3]  = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4]  = get_variable_default ("SXYg", 0);
+  par[5]  = get_variable_default ("Zpk", 10000);
+  par[6]  = get_variable_default ("Sg", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+  fpar[1]  = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+void qfgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+}
+
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky */
+float qfgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + fpar[1]*z + pow(z,fpar[0]));
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(fpar[1] + fpar[0]*pow(z,(fpar[0]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+  }
+  return (f);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qgauss-psf.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qgauss-psf.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qgauss-psf.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "imfit.h"
+
+float qgauss_psfTD (float, float, float *, int, float *);
+void  qgauss_psfCL ();
+
+void qgauss_psf_setup (char *name) {
+
+  if (strcmp(name, "qgauss_psf")) return;
+
+  fitfunc = qgauss_psfTD;
+  imfit_cleanup = qgauss_psfCL;
+  Npar = 4;
+  Nfpar = 5;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = get_variable_default ("Zpk", 10000);
+  par[3]  = get_variable_default ("Sg", 0.0);
+
+  fpar[0] = 2.35 / get_variable_default ("SXg", 15.0);
+  fpar[1] = 2.35 / get_variable_default ("SYg", 15.0);
+  fpar[2] = get_variable_default ("SXYg", 0.0);
+  fpar[3] = get_variable_default ("Sr", 1.0);
+  fpar[4] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[3];
+}
+
+void qgauss_psfCL () {
+  set_variable ("Xg",  par[0]);
+  set_variable ("Yg",  par[1]);
+  set_variable ("Zpk", par[2]);
+  set_variable ("Sg",  par[3]);
+}
+
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky, sr */
+float qgauss_psfTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = fpar[0]*X;
+  py = fpar[1]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + fpar[2]*X*Y;
+
+  r = 1.0 / (1 + fpar[3]*z + pow(z,fpar[4]));
+  f = par[2]*r + par[3];
+  q = par[2]*SQ(r)*(fpar[3] + fpar[4]*pow(z,(fpar[4]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*fpar[0] + fpar[2]*Y);
+    dpar[1] = q*(2*py*fpar[1] + fpar[2]*X);
+    dpar[2] = +r;
+    dpar[3] = +1;
+  }
+  return (f);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qgauss.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "imfit.h"
+
+float qgaussTD (float, float, float *, int, float *);
+void  qgaussCL ();
+
+void qgauss_setup (char *name) {
+
+  if (strcmp(name, "qgauss")) return;
+
+  fitfunc = qgaussTD;
+  imfit_cleanup = qgaussCL;
+  Npar = 8;
+  Nfpar = 1;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3]  = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4]  = get_variable_default ("SXYg", 0);
+  par[5]  = get_variable_default ("Zpk", 10000);
+  par[6]  = get_variable_default ("Sg", 0.0);
+  par[7]  = get_variable_default ("Sr", 1.0);
+  fpar[0] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[6];
+}
+
+void qgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("Sr", par[7]);
+}
+
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky, sr */
+float qgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + par[7]*z + pow(z,fpar[0]));
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(par[7] + fpar[0]*pow(z,(fpar[0]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -par[5]*SQ(r)*z;
+  }
+  return (f);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qrgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qrgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-qrgauss.c	(revision 22322)
@@ -0,0 +1,110 @@
+# include "imfit.h"
+
+float qrgaussTD (float, float, float *, int, float *);
+void  qrgaussCL ();
+
+void qrgauss_setup (char *name) {
+
+  if (strcmp(name, "qrgauss")) return;
+
+  fitfunc = qrgaussTD;
+  imfit_cleanup = qrgaussCL;
+  Npar = 8;
+  Nfpar = 1;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3]  = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4]  = get_variable_default ("SXYg", 0);
+  par[5]  = get_variable_default ("Zpk", 10000);
+  par[6]  = get_variable_default ("Sg", 0.0);
+  par[7]  = get_variable_default ("Npow", 2.25);
+
+  fpar[0] = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+void qrgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("Npow", par[7]);
+}
+
+float qrgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + fpar[0]*z + pow(z,par[7]));
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(fpar[0] + par[7]*pow(z,(par[7]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -5*par[5]*SQ(r)*log(z)*pow(z,par[7]);
+  }
+  return (f);
+}
+
+
+# if (0)
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky, sr */
+float qrgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  /* if (par[7] < 1.0) par[7] = 1.0; */
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + fpar[0]*z + pow(z,par[7]) + 0.05*z*z*z);
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(fpar[0] + par[7]*pow(z,(par[7]-1)) + 0.15*z*z);
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -5*par[5]*SQ(r)*log(z)*pow(z,par[7]);
+  }
+  return (f);
+}
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-rgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-rgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-rgauss.c	(revision 22322)
@@ -0,0 +1,86 @@
+# include "imfit.h"
+
+float rgaussTD (float, float, float *, int, float *);
+void  rgaussCL ();
+
+void rgauss_setup (char *name) {
+
+  if (strcmp(name, "rgauss")) return;
+
+  fitfunc = rgaussTD;
+  cleanup = rgaussCL;
+  Npar = 10;
+  Nfpar = 1;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 2.35 * sqrt(2.0) / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 * sqrt(2.0) / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[6];
+}
+
+/* two components: (1 + z_1 + 0.5*z_1^2 + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+float rgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  r = 1.0 / (1 + z1 + 0.5*SQ(z1)+ pow(z2,fpar[0]));
+  f = par[5]*r + par[6];
+
+  q1 = par[5]*SQ(r)*(1 + z1);
+  q2 = par[5]*SQ(r)*fpar[0]*pow(z2,(fpar[0]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+    dpar[2] = -2*q1*px1*X;
+    dpar[3] = -2*q1*py1*Y;
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X;
+    dpar[8] = -2*q2*py2*Y;
+    dpar[9] = -q2*X*Y;
+  }
+  return (f);
+}
+
+int rgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf", 2.35 * sqrt(2.0) / par[7]);
+  set_variable ("SYf", 2.35 * sqrt(2.0) / par[8]);
+  set_variable ("SXYf", par[9]);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-serbulge.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-serbulge.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-serbulge.c	(revision 22322)
@@ -0,0 +1,102 @@
+# include "imfit.h"
+
+float serbulgeTD (float, float, float *, int, float *);
+void  serbulgeCL ();
+
+void serbulge_setup (char *name) {
+
+  if (strcmp(name, "serbulge")) return;
+
+  fitfunc = serbulgeTD;
+  cleanup = serbulgeCL;
+  Npar = 12;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000) / 2.0;
+
+  par[6] = get_variable_default ("Sg", 0.0);
+
+  par[7] = 2.35 * sqrt(2.0) / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 * sqrt(2.0) / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+  par[10] = get_variable_default ("Zpk", 10000) / 2.0;
+
+  par[11] = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+/*                                  0  1    2   3   4      5    6     7   8   9       10  11 */
+/* sersic galaxy model w/ bulge: -- x, y, (sx, sy, sxy)_1, I_1, sky, (sx, sy, sxy)_2, I_2, n */
+/* exp (-b (r/r_e)^(1/n)) + pgauss (r) */
+float serbulgeTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, px2, py1, py2;
+  float z1, z2, r1, r2, t, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  /* bulge component */
+  r1 = 1.0 / (1 + z1 + 0.5*z1*z1*(1 + z1/3)); /* ~ exp (-Z) */
+
+  /* disk component */
+  t = pow (z2, par[11]);
+  r2 = exp (-t);
+
+  f = par[5]*r1 + par[10]*r2 + par[6];
+
+  q1 = par[5]*r1*r1*(1 + z1 + 0.5*z1*z1);
+  q2 = par[10]*r2*par[11]*pow(z2, par[11]-1);
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+    dpar[2] = -2*q1*px1*X;
+    dpar[3] = -2*q1*py1*Y;
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r1;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X*50;
+    dpar[8] = -2*q2*py2*Y*50;
+    dpar[9] = -q2*X*Y*50;
+    dpar[10] = +r2*50;
+    dpar[11] = -q2*log(z2)*t*50;
+  }
+  return (f);
+}
+
+int serbulgeCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zb",   par[5]);
+  set_variable ("Sg",   par[6]);
+
+  set_variable ("SXf",  2.35 * sqrt(2.0) / par[7]);
+  set_variable ("SYf",  2.35 * sqrt(2.0) / par[8]);
+  set_variable ("SXYf", par[9]);
+  set_variable ("Zd",   par[10]);
+  set_variable ("Sr",   par[11]);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-sersic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-sersic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-sersic.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "imfit.h"
+
+float sersicTD (float, float, float *, int, float *);
+void  sersicCL ();
+
+void sersic_setup (char *name) {
+
+  if (strcmp(name, "sersic")) return;
+
+  fitfunc = sersicTD;
+  cleanup = sersicCL;
+  Npar = 8;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+/* sersic galaxy model -- x, y, sx, sy, sxy, I, sky, n */
+/* exp (-b (r/r_e)^(1/n)) */
+float sersicTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, t, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  t = pow (z, par[7]);
+  r = exp (-t);
+  f = par[5]*r + par[6];
+  q = par[5]*r*par[7]*pow(z, par[7]-1);
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -q*log(z)*t;
+  }
+  return (f);
+}
+
+int sersicCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("Sr", par[7]);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-sgauss-psf.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-sgauss-psf.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-sgauss-psf.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "imfit.h"
+
+float sgauss_psfTD (float, float, float *, int, float *);
+void  sgauss_psfCL ();
+
+void sgauss_psf_setup (char *name) {
+
+  if (strcmp(name, "sgauss_psf")) return;
+
+  fitfunc = sgauss_psfTD;
+  imfit_cleanup = sgauss_psfCL;
+  Npar = 4;
+  Nfpar = 7;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = get_variable_default ("Zpk", 10000);
+  par[3] = get_variable_default ("Sg", 0.0);
+
+  fpar[0] = 2.35 / get_variable_default ("SXg", 15.0);
+  fpar[1] = 2.35 / get_variable_default ("SYg", 15.0);
+  fpar[2] = get_variable_default ("SXYg", 0.0);
+  fpar[3] = 2.35 / get_variable_default ("SXf", 15.0);
+  fpar[4] = 2.35 / get_variable_default ("SYf", 15.0);
+  fpar[5] = get_variable_default ("SXYf", 0.0);
+  fpar[6] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[3];
+}
+
+void sgauss_psfCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("Zpk",  par[2]);
+  set_variable ("Sg",   par[3]);
+}
+
+/* two components: (1 + z_1 + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+float sgauss_psfTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = fpar[0]*X;
+  py1 = fpar[1]*Y;
+  px2 = fpar[3]*X;
+  py2 = fpar[4]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + fpar[2]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + fpar[5]*X*Y;
+
+  r = 1.0 / (1 + z1 + pow(z2,fpar[6]));
+  f = par[2]*r + par[3];
+
+  q1 = par[2]*SQ(r);
+  q2 = par[2]*SQ(r)*fpar[6]*pow(z2,(fpar[6]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*fpar[0] + fpar[2]*Y) + q2*(2*px2*fpar[3] + fpar[5]*Y);
+    dpar[1] = q1*(2*py1*fpar[1] + fpar[2]*X) + q2*(2*py2*fpar[4] + fpar[5]*X);
+    dpar[2] = +r;
+    dpar[3] = +1;
+  }
+  return (f);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-sgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-sgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-sgauss.c	(revision 22322)
@@ -0,0 +1,98 @@
+# include "imfit.h"
+# define FFACTOR 200
+# define FSCALE 1.2
+
+float sgaussTD (float, float, float *, int, float *);
+void  sgaussCL ();
+
+void sgauss_setup (char *name) {
+
+  if (strcmp(name, "sgauss")) return;
+
+  fitfunc = sgaussTD;
+  imfit_cleanup = sgaussCL;
+  Npar = 10;
+  Nfpar = 2;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4] = get_variable_default ("SXYg", 0);
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 2.35 / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+  fpar[1] = get_variable_default ("Npin", 1.00); // drop this?
+
+  sky = &par[6];
+}
+
+void sgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf", 2.35 / par[7]);
+  set_variable ("SYf", 2.35 / par[8]);
+  set_variable ("SXYf", par[9]);
+}
+
+/* two components: (1 + z_1 + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+float sgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r, q1, q2, f, f1, f2;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  r = 1.0 / (1 + z1 + pow(z2,fpar[0]));
+  f = par[5]*r + par[6];
+
+  q1 = par[5]*SQ(r);
+  q2 = par[5]*SQ(r)*fpar[0]*pow(z2,(fpar[0]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+
+    /* these fudge factors impede the growth of par[2] beyond par[7] */
+    f1 = fabs(par[7]) / fabs(par[2]);
+    f2 = (f1 < FSCALE) ? 1 : FFACTOR*(f1 - FSCALE) + 1;
+    dpar[2] = -2*q1*px1*X*f2;
+
+    f1 = fabs(par[8]) / fabs(par[3]);
+    f2 = (f1 < FSCALE) ? 1 : FFACTOR*(f1 - FSCALE) + 1;
+    dpar[3] = -2*q1*py1*Y*f2;
+
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X;
+    dpar[8] = -2*q2*py2*Y;
+    dpar[9] = -q2*X*Y;
+  }
+  return (f);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-test.c	(revision 22322)
@@ -0,0 +1,314 @@
+  if (ShapeVariation) {
+    /* find dChi/dSx and dChi/dSy given by increasing shape terms by 5% */
+    float tp1, tp2, chix, chiy;
+    chix = chiy = 0;
+    if (fitfunc == sgaussTD) {
+      tp1 = par[2];
+      tp2 = par[7];
+      par[2] = par[2]*1.05;
+      par[7] = par[7]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[2] = tp1;
+      par[7] = tp2;
+
+      tp1 = par[3];
+      tp2 = par[8];
+      par[3] = par[3]*1.05;
+      par[8] = par[8]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[3] = tp1;
+      par[8] = tp2;
+    }
+    if (fitfunc == pgaussTD) {
+      tp1 = par[2];
+      par[2] = par[2]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[2] = tp1;
+
+      tp1 = par[3];
+      par[3] = par[3]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[3] = tp1;
+    }
+    if (fitfunc == sgauss_psfTD) {
+      tp1 = par[0];
+      tp2 = par[3];
+      par[0] = par[0]*1.05;
+      par[3] = par[3]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[0] = tp1;
+      par[3] = tp2;
+
+      tp1 = par[1];
+      tp2 = par[4];
+      par[1] = par[1]*1.05;
+      par[4] = par[4]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[1] = tp1;
+      par[4] = tp2;
+    }
+    if (fitfunc == pgauss_psfTD) {
+      tp1 = par[0];
+      par[0] = par[0]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[0] = tp1;
+
+      tp1 = par[1];
+      par[1] = par[1]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[1] = tp1;
+    }
+    set_variable ("dChiX", chix/chisq);
+    set_variable ("dChiY", chiy/chisq);
+  }
+
+# if (0)
+/* pars: x, y, sx, sy, sxy, I, sky */
+float fgalaxyTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, Z, E, F, q, R, f, p2, p3;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  p2 = X / par[2];
+  p3 = Y / par[3];
+
+  Z = sqrt (0.5*p2*X + X*Y*par[4] + 0.5*p3*Y);                 /* R */
+  E = 1.0 / (1 + Z);   
+
+  q = par[5] * E;
+  R = q*E;
+  F = 0.5 / Z;
+  
+  f = q + par[6];
+
+  dpar[0] = F*R*(p2 + par[4]*Y);
+  dpar[1] = F*R*(p3 + par[4]*X);
+  dpar[2] = F*0.5*R*p2*p2;
+  dpar[3] = F*0.5*R*p3*p3;
+  dpar[4] = -R*X*Y*F;
+    
+  dpar[5] = E;
+  dpar[6] = 1;
+  return (f);
+}
+
+/* pars: x, y, sx, sy, sxy, I, sky */
+float fbarTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, Z, E, F, q, R, f, p2, p3;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  p2 = X / par[2];
+  p3 = Y / par[3];
+
+  Z = 0.5*p2*X + X*Y*par[4] + 0.5*p3*Y;                 /* R */
+  E = 1.0 / (1 + Z*Z*Z);   
+
+  q = par[5] * E;
+  F = 3*Z*Z;
+  R = q*E*F;
+  
+  f = q + par[6];
+
+  dpar[0] = R*(p2 + par[4]*Y);
+  dpar[1] = R*(p3 + par[4]*X);
+  dpar[2] = 0.5*R*p2*p2;
+  dpar[3] = 0.5*R*p3*p3;
+  dpar[4] = -R*X*Y;
+    
+  dpar[5] = E;
+  dpar[6] = 1;
+  return (f);
+}
+
+/* convert from x,y to major,minor */
+void fix_ellipsegauss_pars (float *par, int Npar) {
+
+  float p2, p4, angle, t1, t2, tmp, area;
+
+  /* par[0], par[1] = Xo, Yo - stay the same */
+
+  p2 = 1/par[2];
+  p4 = 1/par[3];
+
+  angle = 0.5 * atan2 (-2*par[4], p4 - p2); 
+
+  tmp = sqrt (SQ(p2 - p4) + 4*SQ(par[4]));
+  t1 = (p2 + p4 + tmp) / 2;
+  t2 = t1 - tmp;
+
+  par[2] = 2.35482*sqrt(1/t2);
+  par[3] = 2.35482*sqrt(1/t1);
+  par[4] = DEG_RAD * angle;
+
+  area = 2*M_PI/sqrt(t1*t2);
+
+  par[5] *= area;
+
+}
+# endif
+
+/***  options for later
+
+  Subtract = FALSE;
+  if ((N = get_argument (argc, argv, "-sub"))) {
+    remove_argument (N, &argc, argv);
+    Subtract  = TRUE;
+  }
+
+  DFact = 1;
+  if ((N = get_argument (argc, argv, "-D"))) {
+    remove_argument (N, &argc, argv);
+    DFact  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  fitfunc = fgaussTD;
+  if ((N = get_argument (argc, argv, "-gal"))) {
+    remove_argument (N, &argc, argv);
+    fitfunc = fgalaxyTD; 
+  }
+  if ((N = get_argument (argc, argv, "-bar"))) {
+    remove_argument (N, &argc, argv);
+    fitfunc = fbarTD; 
+  }
+
+
+  f1 = 1;
+  if ((c = get_variable ("BETA1")) != (char *) NULL) f1 = atof (c);
+
+  f2 = 1;
+  if ((c = get_variable ("BETA2")) != (char *) NULL) f2 = atof (c);
+
+  if (Subtract) {
+    tmpsky = par[6];
+    par[6] = 0;
+    for (N = j = 0; j < ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + (j+sy)*buf[0].matrix.Naxis[0] + sx; 
+      for (i = 0; i < nx; i++, V++, N++) {
+	dx = i + sx;
+	dy = j + sy;
+	*V -= fitfunc (dx, dy, par, Npar, (float *) NULL);
+      }
+    }
+    par[6] = tmpsky;
+  }
+
+***/
+
+# if (0)
+
+/* these two tests were not very succcessful.  the first did not model the shape well because 
+   it could not match the change in roundness with radius.  the second did not work because the 
+   parameters were degenerate (amplitude and slope of second component) */
+
+/* test: fixed, non-integer higher-order term -- x, y, sx, sy, sxy, I, sky, f1, f2 */
+float qgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f, k;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  k = pow(z,1.75*par[8]);
+  r = 1.0 / (1 + z + par[7]*k); /* ~ exp (-Z) */
+  q = par[5]*r*r*(1 + 1.75*par[7]*par[8]*pow(z,1.75*par[8]-1));
+  /* note difference from gaussian: q = par[5]*r */
+  f = par[5]*r + par[6];
+
+  dpar[0] = q*(2*px*par[2] + par[4]*Y);
+  dpar[1] = q*(2*py*par[3] + par[4]*X);
+  dpar[2] = -2*q*px*X;
+  dpar[3] = -2*q*py*Y;
+  dpar[4] = -q*X*Y;
+  dpar[5] = +r;
+  dpar[6] = +1;
+  dpar[7] = -10*par[5]*r*r*k;
+  dpar[8] = -10*par[5]*r*r*par[7]*k*1.75*log(z);
+
+  return (f);
+}
+
+/* test: two component model: inner pseudo gaussian with outer z^1.75 x, y, sx, sy, sxy, I, sky */
+float rgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r1, r2, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[8]*X;
+  py2 = par[9]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[10]*X*Y;
+
+  r1 = 1.0 / (1 + z1 + 0.5*SQ(z1)*(1 + z1/3)); /* ~ exp (-Z) */
+  r2 = 1.0 / (1 + pow(z2,1.75));
+
+  f = par[5]*r1 + par[6] + par[7]*r2;
+
+  q1 = par[5]*SQ(r1)*(1 + z1 + 0.5*SQ(z1));
+  q2 = par[7]*SQ(r2)*(1.75*pow(z2,0.75));
+
+  dpar[	0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[8] + par[10]*Y);
+  dpar[	1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[9] + par[10]*X);
+  dpar[	2] = -2*q1*px1*X;
+  dpar[	3] = -2*q1*py1*Y;
+  dpar[	4] = -q1*X*Y;
+  dpar[	5] = +r1;
+  dpar[	6] = +1;
+  dpar[	7] = +r2*2;
+  dpar[	8] = -2*q2*px2*X*2;
+  dpar[	9] = -2*q2*py2*Y*2;
+  dpar[10] = -q2*X*Y;
+
+  return (f);
+}
+
+# endif
+
+  /* forcing values to have a rational range
+  ALLOCATE (parmin, float, Npar);
+  ALLOCATE (parmax, float, Npar);
+  bzero (parmin, Npar*sizeof(float));
+  bzero (parmax, Npar*sizeof(float));
+  parmin[0] = parmin[1] = 0;
+  parmax[0] = buf[0].matrix.Naxis[0];
+  parmax[1] = buf[0].matrix.Naxis[1];
+
+  parmin[2] = parmin[3] = 0.01;
+  parmax[2] = parmax[3] = 100.0;
+  parmin[4] = -1000;
+  parmax[4] = -1000;
+  
+  parmin[5] = 1;
+  parmax[5] = 1e5;
+
+  parmin[6] = 0.0;
+  parmax[6] = 1e5;
+
+  if (Npar == 9) {
+    parmin[7] = parmin[8] = 0.01;
+    parmax[7] = parmax[8] = 10.0;
+  }
+  if (Npar == 10) {
+    parmin[7] = parmin[8] = 0.01;
+    parmax[7] = parmax[8] = 10.0;
+    parmin[9] = -1000;
+    parmax[9] = -1000;
+  }
+  */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-tgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-tgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-tgauss.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "imfit.h"
+
+float tgaussTD (float, float, float *, int, float *);
+void  tgaussCL ();
+
+void tgauss_setup (char *name) {
+
+  if (strcmp(name, "tgauss")) return;
+
+  fitfunc = tgaussTD;
+  cleanup = tgaussCL;
+  Npar = 10;
+  Nfpar = 2;
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 2.35 * sqrt(2.0) / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 * sqrt(2.0) / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+  fpar[1] = get_variable_default ("Npin", 1.00); // drop this?
+
+  sky = &par[6];
+}
+
+/* two components: (1 + z_1^M + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+float tgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  r = 1.0 / (1 + pow(z1,fpar[1]) + pow(z2,fpar[0]));
+  f = par[5]*r + par[6];
+
+  q1 = par[5]*SQ(r)*fpar[1]*pow(z1,(fpar[1]-1));
+  q2 = par[5]*SQ(r)*fpar[0]*pow(z2,(fpar[0]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+    dpar[2] = -2*q1*px1*X*2;
+    dpar[3] = -2*q1*py1*Y*2;
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X;
+    dpar[8] = -2*q2*py2*Y;
+    dpar[9] = -q2*X*Y;
+  }
+  return (f);
+}
+
+int tgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf",  2.35 * sqrt(2.0) / par[7]);
+  set_variable ("SYf",  2.35 * sqrt(2.0) / par[8]);
+  set_variable ("SXYf", par[9]);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-vgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-vgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit-vgauss.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include "imfit.h"
+
+float vgaussTD (float, float, float *, int, float *);
+void  vgaussCL ();
+
+void  vgauss_setup (char *name) {
+
+  if (strcmp(name, "vgauss")) return;
+
+  fitfunc = vgaussTD;
+  cleanup = vgaussCL;
+  Npar = 9;
+  Nfpar = 0;
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 1;
+  par[8] = 1;
+  sky = &par[6];
+}
+
+/* pseudo 2D gaussian with floating 2nd and 3rd order terms -- x, y, sx, sy, sxy, I, sky, f1, f2 */
+float vgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f, k;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  k = 0.5*z*z*(1 + par[8]*z/3);
+  r = 1.0 / (1 + z + par[7]*k); /* ~ exp (-Z) */
+  f = par[5]*r + par[6];
+  q = par[5]*r*r*(1 + par[7]*z*(1 + par[8]*z/2));
+  /* note difference from gaussian: q = par[5]*r */
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -100*par[5]*r*r*k;
+    dpar[8] = -100*par[5]*r*r*par[7]*(z*z*z)/6;
+  }
+  return (f);
+}
+
+int vgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf", par[7]);
+  set_variable ("SYf", par[8]);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imfit.c	(revision 22322)
@@ -0,0 +1,185 @@
+# include "imfit.h"
+
+int imfit (int argc, char **argv) {
+
+  int i, j, N, Npts, Save, VERBOSE, ShapeVariation;
+  int sx, sy, nx, ny, Nx, Ny;
+  float chisq, ochisq, dchisq, Gain, RDnoise, SatThreshold;
+  float *x, *y, *z, *dz, *V;
+  Buffer *buf;
+
+  Save = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    Save = TRUE;
+  }
+
+  ShapeVariation = FALSE;
+  if ((N = get_argument (argc, argv, "-shapes"))) {
+    remove_argument (N, &argc, argv);
+    ShapeVariation = TRUE;
+  }
+
+  SatThreshold = 0xffff;
+  if ((N = get_argument (argc, argv, "-sat"))) {
+    remove_argument (N, &argc, argv);
+    SatThreshold = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* Gain in e/DN */
+  Gain = 1.0;
+  if ((N = get_argument (argc, argv, "-gain"))) {
+    remove_argument (N, &argc, argv);
+    Gain = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* RD noise in DN */
+  RDnoise = 0.0;
+  if ((N = get_argument (argc, argv, "-rdnoise"))) {
+    remove_argument (N, &argc, argv);
+    RDnoise = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  /* set fitting function */
+  fgauss_setup ("fgauss");
+  if ((N = get_argument (argc, argv, "-func"))) {
+    fitfunc = NULL;
+    remove_argument (N, &argc, argv);
+    fgauss_setup (argv[N]);
+    pgauss_setup (argv[N]);
+    pgauss_psf_setup (argv[N]);
+    sgauss_setup (argv[N]);
+    sgauss_psf_setup (argv[N]);
+    qgauss_setup (argv[N]);
+    qgauss_psf_setup (argv[N]);
+    qfgauss_setup (argv[N]);
+    qrgauss_setup (argv[N]);
+    if (fitfunc == NULL) {
+      gprint (GP_ERR, "unknown function %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: imfit <buffer> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  /* non-optional arguments */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  /* check if region is valid */
+  if (sx + 0.5*nx < 0) goto range;
+  if (sy + 0.5*ny < 0) goto range;
+  if (sx + 0.5*nx >= Nx) goto range;
+  if (sy + 0.5*ny >= Ny) goto range;
+
+  /* convert array z[x,y] to x[i], y[i], z[i] */
+  N = 0;
+  Npts = nx*ny;
+  ALLOCATE (x,  float, 2*Npts);
+  ALLOCATE (y,  float, 2*Npts);
+  ALLOCATE (z,  float, 2*Npts);
+  ALLOCATE (dz, float, 2*Npts);
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= Ny) continue;
+    V = (float *)(buf[0].matrix.buffer) + (j+sy)*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= Nx) continue;
+      if (*V > SatThreshold) goto next;
+      dz[N] = (SQ(RDnoise) + *V/Gain);
+      if (dz[N] <= 0) goto next;
+      dz[N] = 1.0 / dz[N];
+      x[N] = i + sx;
+      y[N] = j + sy;
+      z[N] = *V;
+      N++;
+    next:
+      V++;
+    }
+  }
+  Npts = N;
+
+  /* run fit routine */
+  ochisq = mrq2dinit (x, y, z, dz, Npts, par, Npar, fitfunc, VERBOSE);
+  dchisq = ochisq;
+  chisq  = ochisq;
+  for (i = 0; (i < 25) && ((dchisq <= 0.0) || (dchisq > 0.01*(Npts - Npar))); i++) {
+    chisq = mrq2dmin (x, y, z, dz, Npts, par, Npar, fitfunc, VERBOSE);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+  }  
+  set_int_variable ("Niter",  i);
+
+  /** create output image (keep in sky) **/
+  if (Save) {
+    Buffer *out;
+    float *Vi, *Vo, vr, vf;
+
+    if ((out = SelectBuffer ("out",   ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    free (out[0].header.buffer);
+    free (out[0].matrix.buffer);
+
+    strcpy (out[0].file, "(empty)");
+    CreateBuffer (out, 2*nx, 2*ny, -32, 0.0, 1.0);
+
+    /* four panels: 1) raw image. 2) fit  3) raw - fit   4) ?? */
+    Vi = (float *)buf[0].matrix.buffer;
+    Vo = (float *)out[0].matrix.buffer;
+    for (j = 0; j < ny; j++) {
+      for (i = 0; i < nx; i++) {
+	vf = fitfunc ((float)(i+sx), (float)(j+sy), par, Npar, NULL);
+	vr = Vi[(i+sx)+(j+sy)*Nx];
+	Vo[(i   )+(j   )*2*nx] = vr;
+	Vo[(i+nx)+(j   )*2*nx] = vf;
+	Vo[(i   )+(j+ny)*2*nx] = vr - vf + *sky;
+	Vo[(i+nx)+(j+ny)*2*nx] = fabs(vr-vf) + *sky;
+      }
+    }
+  }
+
+  /* save parameters to opihi variables */
+  imfit_cleanup ();
+
+  set_variable ("ChiSq", chisq/(Npts - Npar));
+
+  if (VERBOSE) {
+    for (i = 0; i < Npar; i++) {
+      gprint (GP_ERR, "%g ", par[i]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  free (x);
+  free (y);
+  free (z);
+  free (dz);
+  free (par);
+  free (fpar);
+
+  mrq2dfree (Npar);
+  return (TRUE);
+
+range:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imsub.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imsub.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/imsub.c	(revision 22322)
@@ -0,0 +1,79 @@
+# include "imfit.h"
+
+int imsub (int argc, char **argv) {
+
+  int i, j, N, VERBOSE;
+  int sx, sy, nx, ny, Nx, Ny;
+  float value;
+  float *V;
+  Buffer *buf;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  /* set fitting function */
+  fgauss_setup ("fgauss");
+  if ((N = get_argument (argc, argv, "-func"))) {
+    fitfunc = NULL;
+    remove_argument (N, &argc, argv);
+    fgauss_setup (argv[N]);
+    pgauss_setup (argv[N]);
+    pgauss_psf_setup (argv[N]);
+    sgauss_setup (argv[N]);
+    sgauss_psf_setup (argv[N]);
+    qgauss_setup (argv[N]);
+    qgauss_psf_setup (argv[N]);
+    qfgauss_setup (argv[N]);
+    qrgauss_setup (argv[N]);
+    if (fitfunc == NULL) {
+      gprint (GP_ERR, "unknown function %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: imfit <buffer> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  /* non-optional arguments */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  /* check if region is valid */
+  if (sx >= Nx) goto range;
+  if (sy >= Ny) goto range;
+  if (sx + nx < 0) goto range;
+  if (sy + ny < 0) goto range;
+
+  /* subtract model fit, but not local sky */
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= Ny) continue;
+    V = (float *)(buf[0].matrix.buffer) + (j+sy)*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= Nx) continue;
+      value = fitfunc ((float)(i+sx), (float)(j+sy), par, Npar, NULL);
+      *V -= value;
+    }
+  }
+
+  free (par);
+  free (fpar);
+  return (TRUE);
+
+range:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/init.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/init.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/init.c	(revision 22322)
@@ -0,0 +1,92 @@
+# include "astro.h"
+
+int altaz                   PROTO((int, char **));
+int biassub                 PROTO((int, char **));
+int cgrid                   PROTO((int, char **));
+int coords                  PROTO((int, char **));
+int cplot                   PROTO((int, char **));
+int csystem                 PROTO((int, char **));
+int ctimes                  PROTO((int, char **));
+int cval                    PROTO((int, char **));
+int czplot                  PROTO((int, char **));
+int drizzle                 PROTO((int, char **));
+int flux                    PROTO((int, char **));
+int fixwrap                 PROTO((int, char **));
+int gauss                   PROTO((int, char **));
+int gaussfit                PROTO((int, char **));
+int getvel                  PROTO((int, char **));
+int getlst                  PROTO((int, char **));
+int imfit                   PROTO((int, char **));
+int imsub                   PROTO((int, char **));
+int medianmap               PROTO((int, char **));
+int mkgauss                 PROTO((int, char **));
+int multifit                PROTO((int, char **));
+int objload                 PROTO((int, char **));
+int outline                 PROTO((int, char **));
+int polar                   PROTO((int, char **));
+int precess                 PROTO((int, char **));
+int profile                 PROTO((int, char **));
+int radec                   PROTO((int, char **));
+int region                  PROTO((int, char **));
+int rotcurve                PROTO((int, char **));
+int scale                   PROTO((int, char **));
+int sexigesimal             PROTO((int, char **));
+int spec                    PROTO((int, char **));
+int star                    PROTO((int, char **));
+int times                   PROTO((int, char **));
+int transform               PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "altaz",       altaz,        "convert alt/az to/from ra/dec"},
+  {1, "biassub",     biassub,      "subtract medianed overscan row or column"},
+  {1, "cgrid",       cgrid,        "plot sky coordinate grid"},
+  {1, "coords",      coords,       "load coordinates for buffer from file"},
+  {1, "cplot",       cplot,        "plot vectors in sky coordinates"},
+  {1, "csystem",     csystem,      "convert between coordinate systems"},
+  {1, "ctimes",      ctimes,       "convert between time formats"},
+  {1, "cval",        cval,         "cosmic ray flux?"},
+  {1, "czplot",      czplot,       "plot scaled vectors in sky coordinates"},
+  {1, "drizzle",     drizzle,      "transform image to image"},
+  {1, "flux",        flux,         "flux in a convex contour"},
+  {1, "fixwrap",     fixwrap,      "fix megacam over-wrapped pixels"},
+  {1, "gauss",       gauss,        "get statistics on a star, assuming gaussian profile"},
+  {1, "getvel",      getvel,       "rotcurve to velocities"},
+  {1, "getlst",      getlst,       "return LST given time and longitude"},
+  {1, "imfit",       imfit,        "fit function"},
+  {1, "imsub",       imsub,        "subtract function"},
+  {1, "medianmap",   medianmap,    "small median image"},
+  {1, "mkgauss",     mkgauss,      "generate a 2-D gaussian centered in image"},
+  {1, "multifit",    multifit,     "fit multi-order spectrum"},
+  {1, "objload",     objload,      "plot obj data on Ximage "},
+  {1, "outline",     outline,      "fit outline region"},
+  {1, "polar",       polar,        "convert polar image to cartesian"},
+  {1, "precess",     precess,      "precess coordinates"},
+  {1, "profile",     profile,      "radial profile at X, Y"},
+  {1, "radec",       radec,        "convert to/from radec in hms or dd"},
+  {1, "region",      region,       "define sky region for plot"},
+  {1, "rotcurve",    rotcurve,     "convert CO images to polar coords"},
+  {1, "scale",       scale,        "get / set real bzero / bscale values"},
+  {1, "sexigesimal", sexigesimal,  "convert to/from sexigesimal/decimal"},
+  {1, "spec",        spec,         "extract a spectrum"},
+  {1, "star",        star,         "star stats at rough coords"},
+  {1, "transform",   transform,    "geometric transformation of image"},
+}; 
+
+/* not currently implemented 
+  {"gaussfit",    gaussfit,     "fit a gaussian to pixels in a region"},
+  {"testfit",     testfit, ""},
+  {"times", , ""},
+*/
+
+void InitAstro () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreeAstro () {
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/medianmap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/medianmap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/medianmap.c	(revision 22322)
@@ -0,0 +1,114 @@
+# include "astro.h"
+
+int medianmap (int argc, char **argv) {
+  
+  float *temp, *tp;
+  int i, j, k, I0, I1, J0, J1, I, J, n, N;
+  int nx, ny, Nx, Ny, NX, NY, Ignore;
+  float Mv, Nv, Mv2, value, min, max, IgnoreValue;
+  float *In, *Out, *ip;
+  float fx, fy;
+  Buffer *in, *out;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  min = 0.45;
+  max = 0.55;
+  if ((N = get_argument (argc, argv, "-range"))) {
+    remove_argument (N, &argc, argv);
+    min  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    max  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: medianmap (in) (out) Nx Ny [-range min max]\n");
+    gprint (GP_ERR, "       Nx, Ny specify dimensions of output image\n");
+    gprint (GP_ERR, "       min, max specify fractional range for sorted average\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = atof (argv[3]);
+  Ny = atof (argv[4]);
+  NX = in[0].header.Naxis[0];
+  NY = in[0].header.Naxis[1];
+
+  /* duplicate the (in) buffer to the (out), with different size */
+  /* this should probably be a function in misc */
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_header (&in[0].header, &out[0].header);
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, Ny);
+  out[0].header.Naxis[0] = Nx;
+  out[0].header.Naxis[1] = Ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+
+  In = (float *) in[0].matrix.buffer;
+  Out = (float *) out[0].matrix.buffer;
+
+  fx = (float) Nx / NX;
+  fy = (float) Ny / NY;
+
+  nx = 1 + 1/fx;
+  ny = 1 + 1/fy;
+
+  ALLOCATE (temp, float, 2*nx*ny);
+
+  Nv = Mv = Mv2 = 0.0;
+
+  for (j = 0; j < Ny; j++) {
+    J0 = j / fy;
+    J1 = (j + 1) / fy;
+    for (i = 0; i < Nx; i++) {
+      
+      I0 = i / fx;
+      I1 = (i + 1) / fx;
+
+      n = 0;
+      tp = temp;
+      for (J = J0; J < J1; J++) {
+	ip = &In[J*NX + I0];
+	for (I = I0; I < I1; I++, tp++, ip++) {
+	  if (Ignore && (fabs (*ip - IgnoreValue) < 0.01)) continue;
+	  if (isnan (*ip)) continue;
+	  if (isinf (*ip)) continue;
+	  *tp = *ip;
+	  n++;
+	}
+      }
+
+      fsort (temp, n);
+
+      value = 0;
+      N = 0;
+      for (k = min*n; k < max*n; k++) {
+	value += temp[k];
+	N ++;
+      }
+      if (N == 0)
+	Out[j*Nx + i] = 0;
+      else 
+	Out[j*Nx + i] = value / N;
+    }
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/mkgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/mkgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/mkgauss.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "astro.h"
+
+int mkgauss (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N;
+  float *in;
+  double Sig_x, Sig_y, Theta;
+  double root1, root2, R, A1, A2, A3;
+  double Sx, Sy, Sxy;
+  double x, y, r, f, Xo, Yo;
+  Buffer *buf;
+
+  Xo = Yo = 0;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    Xo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Yo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc < 3) || (argc > 5)) {
+    gprint (GP_ERR, "USAGE: mkgauss (buffer) (sigma) [[sy/sx] angle]\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  
+  /* gaussian parameters */
+  Sig_x = atof (argv[2]);
+  Sig_y = Sig_x;
+  Theta = 0.0;
+  if (argc > 3) {
+    Sig_y = Sig_x*atof (argv[3]);
+    if (argc == 5) {
+      Theta = atof (argv[4]);
+    }
+  }
+
+  /* given Sig_x, Sig_y, Theta, find Sx, Sy, Sxy */
+  root1 = SQ(1.0 / Sig_y);
+  root2 = SQ(1.0 / Sig_x);
+
+  R = 0.5 * (root1 - root2);
+  A1 = 0.25*(root1 + root2) - 0.5*R*cos(2*RAD_DEG*Theta);
+  A2 = 0.25*(root1 + root2) + 0.5*R*cos(2*RAD_DEG*Theta);
+  A3 = -R*sin(2*RAD_DEG*Theta);
+
+  Sx = 0.5/A1;
+  Sy = 0.5/A2;
+  Sxy = A3;
+
+  /* f = exp (-r), r = (x^2 / 2Sx) + (y^2 / 2Sy) + Sxy*x*y */
+
+  in = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+
+      x = i - Xo;
+      y = j - Yo;
+      r = 0.5*x*x/Sx + 0.5*y*y/Sy + x*y*Sxy;
+      f = exp (-r);
+      *in += f;
+    }
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/multifit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/multifit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/multifit.c	(revision 22322)
@@ -0,0 +1,179 @@
+# include "astro.h"
+
+int multifit (int argc, char **argv) {
+  
+  char *p, name[64];
+  double **a, **b, v;
+  int i, j, I, J, n, valid;
+  Vector **Nc, **Nmb, **NMb, **Nwb, **Nmh, **Nml, **Nwo;
+  int *nterm;
+  int Ndim, Norder, Nx, Ny;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: multifit (Norder)\n");
+    return (FALSE);
+  }
+
+  Ndim = 0;
+  Norder = atoi (argv[1]);
+  ALLOCATE (nterm, int, Norder);
+  for (i = 0; i < Norder; i++) {
+    sprintf (name, "nterm:%d", i);
+    p = get_variable (name);
+    nterm[i] = atoi (p); 
+    Ndim += nterm[i];
+    free (p);
+  }
+
+  ALLOCATE (a, double *, Ndim);
+  ALLOCATE (b, double *, Ndim);
+  for (i = 0; i < Ndim; i++) {
+    ALLOCATE (a[i], double, Ndim);
+    ALLOCATE (b[i], double, 1);
+    bzero (a[i], Ndim*sizeof(double));
+    bzero (b[i], sizeof(double));
+  }
+ 
+  ALLOCATE (Nc,  Vector *, Norder);
+  ALLOCATE (NMb, Vector *, Norder);
+  ALLOCATE (Nmb, Vector *, Norder);
+  ALLOCATE (Nwb, Vector *, Norder);
+  ALLOCATE (Nmh, Vector *, Norder - 1);
+  ALLOCATE (Nml, Vector *, Norder - 1);
+  ALLOCATE (Nwo, Vector *, Norder - 1);
+  
+  for (i = 0; i < Norder; i++) {
+    sprintf (name, "c%d", i);
+    if ((Nc[i]  = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+    sprintf (name, "Mb%d", i);
+    if ((NMb[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    sprintf (name, "mb%d", i);
+    if ((Nmb[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    sprintf (name, "wb%d", i);
+    if ((Nwb[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+  }
+  for (i = 0; i < Norder - 1; i++) {
+    sprintf (name, "ml%d", i);
+    if ((Nml[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    sprintf (name, "mh%d", i);
+    if ((Nmh[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    sprintf (name, "wo%d", i);
+    if ((Nwo[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+  }
+
+  Ny = 0;
+  for (i = 0; i < Norder; i++) {
+    for (j = 0; j < nterm[i]; j++, Ny++) {
+      Nx = 0;
+      valid = FALSE;
+      for (I = 0; I < Norder; I++) {
+	if (I == i - 1) { 
+	  for (J = 0; J < nterm[I]; J++) {
+	    v = 0;
+	    for (n = 0; n < Nwo[i-1][0].Nelements; n++) {
+	      v += -pow (Nwo[i-1][0].elements[n], (double)(j+J));
+	    }
+	    a[Ny][Nx] = v;
+	    Nx ++;
+	  }
+	  valid = TRUE;
+	}
+	if (I == i + 1) { 
+	  for (J = 0; J < nterm[I]; J++) {
+	    v = 0;
+	    for (n = 0; n < Nwo[i][0].Nelements; n++) {
+	      v += -pow (Nwo[i][0].elements[n], (double)(j+J));
+	    }
+	    a[Ny][Nx] = v;
+	    Nx ++;
+	  }
+	  valid = TRUE;
+	}
+	if (I == i) { 
+	  for (J = 0; J < nterm[I]; J++) {
+	    v = 0;
+	    for (n = 0; n < Nwb[i][0].Nelements; n++) {
+	      v += pow (Nwb[i][0].elements[n], (double)(j+J));
+	    }
+	    if (i > 0) {
+	      for (n = 0; n < Nwo[i-1][0].Nelements; n++) {
+		v += pow (Nwo[i-1][0].elements[n], (double)(j+J));
+	      }
+	    }
+	    if (i < Norder - 1) {
+	      for (n = 0; n < Nwo[i][0].Nelements; n++) {
+		v += pow (Nwo[i][0].elements[n], (double)(j+J));
+	      }
+	    }
+	    a[Ny][Nx] = v;
+	    Nx ++;
+	  }
+	  valid = TRUE;
+	}
+	if (!valid) {
+	  Nx += nterm[I];
+	}
+      }
+    }
+  }
+
+  Ny = 0;
+  for (i = 0; i < Norder; i++) {
+    for (j = 0; j < nterm[i]; j++, Ny++) {
+      v = 0;
+      for (n = 0; n < Nwb[i][0].Nelements; n++) {
+	v += NMb[i][0].elements[n]*pow (Nwb[i][0].elements[n], (double)j);
+	v -= Nmb[i][0].elements[n]*pow (Nwb[i][0].elements[n], (double)j);
+      }
+      if (i > 0) {
+	for (n = 0; n < Nwo[i-1][0].Nelements; n++) {
+	  v += Nmh[i-1][0].elements[n] * pow (Nwo[i-1][0].elements[n], (double)j);
+	  v -= Nml[i-1][0].elements[n] * pow (Nwo[i-1][0].elements[n], (double)j);
+	}
+      }
+      if (i < Norder - 1) {
+	for (n = 0; n < Nwo[i][0].Nelements; n++) {
+	  v += Nml[i][0].elements[n] * pow (Nwo[i][0].elements[n], (double)j);
+	  v -= Nmh[i][0].elements[n] * pow (Nwo[i][0].elements[n], (double)j);
+	}
+      }
+      b[Ny][0] = v;
+    }
+  }
+  dgaussjordan (a, b, Ndim, 1);
+
+  Ny = 0;
+  for (i = 0; i < Norder; i++) {
+    Nc[i][0].Nelements = nterm[i];
+    REALLOCATE (Nc[i][0].elements, float, nterm[i]);
+    for (j = 0; j < nterm[i]; j++, Ny++) {
+      Nc[i][0].elements[j] = b[Ny][0];
+    }
+  }
+
+  for (i = 0; i < Ndim; i++) {
+    free (a[i]);
+    free (b[i]);
+  }
+  free (a);
+  free (b);
+  free (nterm);
+  free (Nc);
+  free (NMb);
+  free (Nmb);
+  free (Nwb);
+  free (Nmh);
+  free (Nml);
+  free (Nwo);
+  
+  return (TRUE);
+  
+ escape: 
+  gprint (GP_ERR, "syntax error\n");
+  return (FALSE);
+  
+}
+
+
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/objload.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/objload.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/objload.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "dvoshell.h"
+# define CHAR_LINE 104
+# define NBLOCK 100
+
+int objload (int argc, char **argv) {
+  
+  int i, N, Objtype, type, Nline, status;
+  FILE *f;
+  char *buffer, *line, *name;
+  int kapa, Noverlay, NOVERLAY;
+  KiiOverlay *overlay;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  Objtype = 0;
+  if ((N = get_argument (argc, argv, "-t"))) {
+    remove_argument (N, &argc, argv);
+    Objtype = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: objload (overlay) <filename>\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't find object file %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  /* read average values from first line */
+  ALLOCATE (line, char, 129);
+  scan_line (f, line);
+
+  ALLOCATE (buffer, char, CHAR_LINE*NBLOCK);
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+  /* read in data from obj file */
+  while ((Nline = fread (buffer, CHAR_LINE, NBLOCK, f)) > 0) {
+    for (i = 0; i < Nline; i++) {
+      /* we are now using all entries on the *.obj line */
+      status = sscanf (&buffer[i*CHAR_LINE], "%d %f %f",  &type, &overlay[Noverlay].x, &overlay[Noverlay].y);
+      if (Objtype && (Objtype != type)) continue;
+      overlay[Noverlay].type = KII_OVERLAY_BOX;
+      overlay[Noverlay].dx = 5.0;
+      overlay[Noverlay].dy = 5.0;
+      Noverlay ++;
+      CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+    }
+  }
+  fclose (f);
+  free (buffer);
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+
+  free (overlay);
+  free (buffer);
+  free (line);
+
+  gprint (GP_ERR, "loaded %d objects\n", Noverlay);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/outline.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/outline.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/outline.c	(revision 22322)
@@ -0,0 +1,289 @@
+# include "astro.h"
+
+float par[5];
+float dpar[5];
+float Dpar[5];
+float outline_chi (float, float *, int, int, float *);
+
+int outline (int argc, char **argv) {
+  
+  int i, j, k, Npar, BigChange, ABigChange;
+  float Io, *in, ochisq, dchi, chisq, chisq_p, chisq_m, dp, tmp_par;
+  float curve, frac;
+  Buffer *buf;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: outline x y dx dy dxy Io (buffer) Npar\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[7], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  par[0] = atof(argv[1]);
+  par[1] = atof(argv[2]);
+  par[2] = atof(argv[3]);
+  par[3] = atof(argv[4]);
+  par[4] = atof(argv[5]);
+  Io = atof(argv[6]);
+  Npar = atof (argv[8]);
+
+  dpar[0] = 10;
+  dpar[1] = 10;
+  dpar[2] = 10;
+  dpar[3] = 10;
+  dpar[4] = 10;
+
+  Dpar[0] = 10;
+  Dpar[1] = 10;
+  Dpar[2] = 10;
+  Dpar[3] = 10;
+  Dpar[4] = 10;
+
+  in = (float *) buf[0].matrix.buffer;
+
+  chisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+  gprint (GP_ERR, "chisq (1): %f\n", chisq);
+  
+  for (j = 0; j < 15; j++) {
+
+    /*
+    if (!(j % 3)) {
+      chisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+      for (k = 0; (k < 3) && (fabs (frac) > 0.3); k++) {
+	tmp1 = par[2];
+	tmp2 = par[3];
+	par[2] *= 1 + 0.1*frac;
+	par[3] *= 1 + 0.1*frac;
+	nchisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	if (nchisq > chisq) {
+	  par[2] = tmp1;
+	  par[3] = tmp2;
+	  k = 3;
+	} else {
+	  chisq = nchisq;
+	}
+	gprint (GP_ERR, "frac: %f  %f %f   %f\n", frac, par[2], par[3], chisq);
+      }
+    }
+    */
+    
+    ABigChange = FALSE;
+    ochisq = chisq;
+    for (i = 4; i >= 0; i--) {
+      /* find +chisq, -chisq for this par & adjust par as needed */
+
+      for (k = 0, BigChange = TRUE; (k < 3) && BigChange; k++) {
+	tmp_par = par[i];
+	par[i] = tmp_par + dpar[i];
+	chisq_p = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	par[i] = tmp_par - dpar[i];
+	chisq_m = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	
+	/* have we braketted a minimum? (curve < 0) */
+	curve = (chisq_p - chisq) * (chisq - chisq_m);
+	if (curve > 0) {
+	  dp = 2*dpar[i];
+	} else {
+	  dp = 0.5 * dpar[i] * (chisq_m - chisq_p) / (chisq_m + chisq_p - 2*chisq);
+	}      
+	if (chisq_m + chisq_p - 2*chisq == 0) dp = 0;
+	/* don't let extrapolation go too far */
+	if (fabs (dp) > 2*fabs(dpar[i])) { dp = SIGN(dp) * fabs (2*dpar[i]); }
+	
+	par[i] = tmp_par + dp;
+	chisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	
+	BigChange = FALSE;
+	if (chisq <= 1.001*ochisq) {
+	  /* got better */
+	  dchi = (ochisq - chisq) / ochisq; 
+	  if ((dchi > 0.03) || (curve > 0)) BigChange = TRUE;
+	} else {
+	  par[i] = tmp_par;
+	  chisq = ochisq;
+	  if (chisq_m < chisq) {
+	    chisq = chisq_m;
+	    par[i] = tmp_par - dpar[i];
+	  }	
+	  if (chisq_p < chisq) {
+	    chisq = chisq_p;
+	    par[i] = tmp_par + dpar[i];
+	  }	
+	}	
+	/*
+	gprint (GP_ERR, "try: %d  %f   ", i, chisq);
+	for (k = 0; k < 5; k++) {
+	  gprint (GP_ERR, "%f ", par[k]);
+	}
+	gprint (GP_ERR, "\n");
+	*/
+	ochisq = chisq;
+	ABigChange |= BigChange;
+      }
+      if (!BigChange) dpar[i] *= 0.8;
+    }
+
+    if (ABigChange) {
+      for (i = 0; i < 5; i++) {
+	dpar[i] = Dpar[i];
+      }
+    }
+
+    gprint (GP_ERR, "try: %d  %f   ", j, chisq);
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n          ");
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", dpar[i]);
+    }
+    gprint (GP_ERR, "\n");
+    dchi -= chisq;
+
+  }
+
+    /* code to draw dots on Ximage */
+    {
+      int kapa;
+      float xp, yp, x, y;
+      float dx, dy, theta, t, dt;
+      int Noverlay, NOVERLAY;
+      KiiOverlay *overlay;
+      
+      if (!GetImage (NULL, &kapa, NULL)) return (FALSE);
+      
+      Noverlay = 0;
+      NOVERLAY = 1000;
+      ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+      dx = par[2];
+      dy = par[3];
+      dt = 1 / MAX (dx, dy);
+      theta = par[4];
+      
+      for (t = 0; t < 6.3; t += dt) {
+	xp = dx * cos (t);
+	yp = dy * sin (t);
+	
+	x = xp * cos (theta * RAD_DEG) - yp * sin (theta * RAD_DEG) + par[0];
+	y = xp * sin (theta * RAD_DEG) + yp * cos (theta * RAD_DEG) + par[1];
+	
+	overlay[Noverlay].type = KII_OVERLAY_BOX;
+	overlay[Noverlay].x = x;
+	overlay[Noverlay].y = y;
+	overlay[Noverlay].dx = 1.0;
+	overlay[Noverlay].dy = 1.0;
+
+	Noverlay ++;
+	CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+      }
+      KiiLoadOverlay (kapa, overlay, Noverlay, "red");
+      free (overlay);
+    }
+
+  return (TRUE);
+
+}
+
+/* par[0] = x
+   par[1] = y
+   par[2] = dx
+   par[3] = dy
+   par[4] = dxy
+   
+   ellipse is:  
+
+   ((X-x)/dx)^2 + ((Y-y)/dy)^2 + (X-x)(Y-y)dxy = 1
+  
+   (yp/dy)^2 + xp yp dxy + (xp/dx)^2 - 1 = 0
+
+   yp^2 + yp xp dxy dy^2 + xp^2 (dy/dx)^2 - dy^2 = 0
+
+*/
+
+float outline_chi (float Io, float *in, int Nx, int Ny, float *frac) {
+
+  int npts, xo, yo, x, y;
+  float xp, yp, dx, dy, theta;
+  float t, dt, dv, Dv;
+  float chisq, v, Frac;
+
+  /* 
+  if (!SelectVector (&Nvec, "diffs", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvec2, "angle", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecx, "xdif", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecy, "ydif", ANYVECTOR)) return (FALSE);
+  */
+
+  dx = par[2];
+  dy = par[3];
+  theta = par[4];
+  dt = 1 / MAX (dx, dy);
+
+  Frac = 0;
+  chisq = 0;
+  npts = 0;
+  xo = -1; yo = -1;  /* an impossible coordinate */
+
+  /*
+  Npts = 1000;
+  REALLOCATE (vectors[Nvec].elements, float, Npts);
+  REALLOCATE (vectors[Nvec2].elements, float, Npts);
+  REALLOCATE (vectors[Nvecx].elements, float, Npts);
+  REALLOCATE (vectors[Nvecy].elements, float, Npts);
+  */
+
+  for (t = 0; t < 6.3; t += dt) {
+    xp = dx * cos (t);
+    yp = dy * sin (t);
+    
+    x = xp * cos (theta * RAD_DEG) - yp * sin (theta * RAD_DEG) + par[0];
+    y = xp * sin (theta * RAD_DEG) + yp * cos (theta * RAD_DEG) + par[1];
+    
+    if ((x == xo) && (y == yo)) continue;
+    xo = x; yo = y;
+
+    if ((x >= 0) && (x < Nx) && (y >= 0) && (y < Ny)) {
+      v = in[y*Nx + x];
+      if (v > 0) {
+	Dv = v - Io;
+	dv = v + 0.2 * fabs (Dv);
+	chisq += Dv * Dv / dv;
+	if (Dv > sqrt(dv)) Frac += 1.0;
+	if (Dv < sqrt(dv)) Frac -= 1.0;
+	/*
+	vectors[Nvec].elements[npts] = Dv;
+	vectors[Nvec2].elements[npts] = t*DEG_RAD;
+	vectors[Nvecx].elements[npts] = x;
+	vectors[Nvecy].elements[npts] = y;
+	*/
+	npts ++;
+	/* 
+	if (npts == Npts - 1) {
+	  Npts += 1000;
+	  REALLOCATE (vectors[Nvec].elements, float, Npts);
+	  REALLOCATE (vectors[Nvec2].elements, float, Npts);
+	  REALLOCATE (vectors[Nvecx].elements, float, Npts);
+	  REALLOCATE (vectors[Nvecy].elements, float, Npts);
+	}
+	*/
+      }
+    }
+  }
+  /* 
+  vectors[Nvec].Nelements = npts;
+  vectors[Nvec2].Nelements = npts;
+  vectors[Nvecx].Nelements = npts;
+  vectors[Nvecy].Nelements = npts;
+  */
+
+  chisq = chisq / npts;
+  *frac = Frac / npts;
+  if (npts == 0) {
+    chisq = 1e8;
+    *frac = -1.0;
+  }
+
+  return (chisq);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/outline2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/outline2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/outline2.c	(revision 22322)
@@ -0,0 +1,341 @@
+# include "astro.h"
+
+int Npts;
+float *xs, *ys, *zs;
+float par[5];
+float dpar[5];
+float Dpar[5];
+float outline_chi (float, float *);
+int plot_outline ();
+
+int outline (int argc, char **argv) {
+  
+  int i, j, k, Nx, Ny, NPTS, BigChange;
+  float dIo, Io, ochisq, dchi, chisq, chisq_p, chisq_m, dp;
+  float tmp_par, curve, value;
+  float *in;
+  Buffer *buf;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: outline x y dx dy dxy Io dIo (buffer)\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[8], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  par[0] = atof(argv[1]);
+  par[1] = atof(argv[2]);
+  par[2] = atof(argv[3]);
+  par[3] = atof(argv[4]);
+  par[4] = atof(argv[5]);
+  Io = atof(argv[6]);
+  dIo = atof(argv[7]);
+
+  dpar[0] = 10;
+  dpar[1] = 10;
+  dpar[2] = 10;
+  dpar[3] = 10;
+  dpar[4] = 10;
+
+  Dpar[0] = 10;
+  Dpar[1] = 10;
+  Dpar[2] = 10;
+  Dpar[3] = 10;
+  Dpar[4] = 10;
+
+  in = (float *) buf[0].matrix.buffer;
+
+  /* find all pixels within range Io-dIo : Io+dIo, in region about center guess */
+
+  Nx = buf[0].matrix.Naxis[0];  
+  Ny = buf[0].matrix.Naxis[1];
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (xs, float, NPTS);
+  ALLOCATE (ys, float, NPTS);
+  ALLOCATE (zs, float, NPTS);
+  for (j = par[1]-2*par[3]; j < par[1]+2*par[3]; j++) {
+    if (j < 0) continue;
+    if (j >= buf[0].matrix.Naxis[1]) continue;
+    for (i = par[0]-2*par[2]; i < par[0]+2*par[2]; i++) {
+      if (i < 0) continue;
+      if (i >= buf[0].matrix.Naxis[0]) continue;
+      value = in[i + Nx*j];
+      if (fabs (value - Io) < dIo) {
+	xs[Npts] = i;
+	ys[Npts] = j;
+	zs[Npts] = value;
+	Npts ++;
+	if (Npts == NPTS) {
+	  NPTS += 1000;
+	  REALLOCATE (xs, float, NPTS);
+	  REALLOCATE (ys, float, NPTS);
+	  REALLOCATE (zs, float, NPTS);
+	}
+      }
+    }
+  }
+
+  if (Npts == 0) {
+    gprint (GP_ERR, "no valid points in box, try again\n");
+    free (xs);
+    free (ys);
+    free (zs);
+    return (FALSE);
+  }
+
+  plot_outline ();
+  chisq = outline_chi (Io, in);
+  gprint (GP_ERR, "starting chisq: %f for %d pts\n", chisq, Npts);
+
+# if (1)
+  for (j = 0; j < 15; j++) {
+    
+    ochisq = chisq;
+    for (i = 0; i < 5; i++) {
+      /* find +chisq, -chisq for this par & adjust par as needed */
+
+      for (k = 0, BigChange = TRUE; (k < 3) && BigChange; k++) {
+	tmp_par = par[i];
+	par[i] = tmp_par + dpar[i];
+	chisq_p = outline_chi (Io, in);
+	par[i] = tmp_par - dpar[i];
+	chisq_m = outline_chi (Io, in);
+	
+	/* have we braketted a minimum? (curve < 0) */
+	curve = (chisq_p - chisq) * (chisq - chisq_m);
+	if (curve > 0) {
+	  dp = 2*dpar[i];
+	} else {
+	  dp = 0.5 * dpar[i] * (chisq_m - chisq_p) / (chisq_m + chisq_p - 2*chisq);
+	}      
+	if (chisq_m + chisq_p - 2*chisq == 0) dp = 0;
+	/* don't let extrapolation go too far */
+	if (fabs (dp) > 2*fabs(dpar[i])) { dp = SIGN(dp) * fabs (2*dpar[i]); }
+	
+	par[i] = tmp_par + dp;
+	chisq = outline_chi (Io, in);
+	
+	BigChange = FALSE;
+	if (chisq <= 1.001*ochisq) {
+	  /* got better */
+	  dchi = (ochisq - chisq) / ochisq; 
+	  if ((dchi > 0.03) || (curve > 0)) BigChange = TRUE;
+	} else {
+	  par[i] = tmp_par;
+	  chisq = ochisq;
+	  if (chisq_m < chisq) {
+	    chisq = chisq_m;
+	    par[i] = tmp_par - dpar[i];
+	  }	
+	  if (chisq_p < chisq) {
+	    chisq = chisq_p;
+	    par[i] = tmp_par + dpar[i];
+	  }	
+	}	
+	ochisq = chisq;
+      }
+      if (!BigChange) dpar[i] *= 0.8;
+    }
+
+    gprint (GP_ERR, "try: %d  %f   ", j, chisq);
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n          ");
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", dpar[i]);
+    }
+    gprint (GP_ERR, "\n");
+    dchi -= chisq;
+
+  }
+# endif
+
+  free (xs);
+  free (ys);
+  free (zs);
+  
+  plot_outline ();
+  return (TRUE);
+
+}
+
+/* par[0] = x
+   par[1] = y
+   par[2] = dx
+   par[3] = dy
+   par[4] = dxy
+   
+    xp = par[2] * cos (t);
+    yp = par[3] * sin (t);
+    
+    x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG) + par[0];
+    y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG) + par[1];
+
+*/
+
+# if (1)
+
+float outline_chi (float Io, float *in) {
+
+  int i;
+  float theta, phi;
+  float xp, yp, x, y;
+  float chisq, R2;
+
+  chisq = 0;
+
+  for (i = 0; i < Npts; i++) {
+    
+    phi = atan2 (ys[i] - par[1], xs[i] - par[0]) - RAD_DEG * par[4];
+    /* find a point:
+
+       xp, yp such that atan (r2 sin(phi), r1 cos(phi)) == theta 
+
+       tan (theta) = r2 sin(phi) / r1 cos (phi)
+
+       (r1/r2) tan(theta) = sin(phi) / cos (phi);
+       (r1/r2) tan(theta) = tan (phi)
+
+       phi = atan2 (r1 sin(theta), r2 cos(theta))
+    */
+
+    theta = atan2 (par[2]*sin(phi), par[3]*cos(phi));
+
+    /* this is the point on the ellipse at the same angle as ref point */
+    /* this is wrong, but close -- tends to make ellipses too fat */
+    xp = par[2] * cos (theta);
+    yp = par[3] * sin (theta);
+    
+    x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG) + par[0];
+    y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG) + par[1];
+
+    R2 = sqrt (SQ (x - xs[i]) + SQ (y - ys[i]));
+
+    /*
+    Dv = zs[i] - Io;
+    dv = fabs(zs[i]);
+    F2 = Dv * Dv / dv;
+    */
+
+    chisq += R2;
+
+  }
+
+  chisq = chisq / Npts;
+  return (chisq);
+
+}
+
+# else 
+
+float outline_chi (float Io, float *in) {
+
+  int i;
+  float theta, theta1, theta2;
+  float xp, yp, x, y;
+  float chisq, dv, Dv, R2, F2, R, dR;
+
+  int Nvec, Nvec2, Nvecx, Nvecy, Nv, nv;
+
+  chisq = 0;
+
+  nv = 0;
+  Nv = 1000;
+  if (!SelectVector (&Nvec, "dR", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvec2, "dF", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecx, "x", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecy, "y", ANYVECTOR)) return (FALSE);
+  REALLOCATE (vectors[Nvec].elements, float, Nv);
+  REALLOCATE (vectors[Nvec2].elements, float, Nv);
+  REALLOCATE (vectors[Nvecx].elements, float, Nv);
+  REALLOCATE (vectors[Nvecy].elements, float, Nv);
+
+  for (i = 0; i < Npts; i++) {
+    
+    theta1 = atan2 (ys[i] - par[1], xs[i] - par[0]) - RAD_DEG * par[4];
+    theta = atan2 (par[2]*sin(theta1), par[3]*cos(theta1));
+
+    xp = par[2] * cos (theta);
+    yp = par[3] * sin (theta);
+    
+    x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG) + par[0];
+    y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG) + par[1];
+
+    R2 = SQ (x - xs[i]) + SQ (y - ys[i]);
+
+    /* 
+    Dv = fabs (zs[i] - Io) + 1;
+    dv = zs[i] + 0.2 * fabs (Dv);
+    F2 = Dv * Dv / dv;
+    */
+
+    vectors[Nvec].elements[nv] = x;
+    vectors[Nvec2].elements[nv] = y;
+    vectors[Nvecx].elements[nv] = xs[i];
+    vectors[Nvecy].elements[nv] = ys[i];
+    nv ++;
+    if (nv == Nv - 1) {
+      Nv += 1000;
+      REALLOCATE (vectors[Nvec].elements, float, Nv);
+      REALLOCATE (vectors[Nvec2].elements, float, Nv);
+      REALLOCATE (vectors[Nvecx].elements, float, Nv);
+      REALLOCATE (vectors[Nvecy].elements, float, Nv);
+    }
+
+    /* typical distance might be 1 - 10 pix,
+       typical z error might be 100 cts */
+    chisq += R2; 
+
+  }
+  vectors[Nvec].Nelements = nv;
+  vectors[Nvec2].Nelements = nv;
+  vectors[Nvecx].Nelements = nv;
+  vectors[Nvecy].Nelements = nv;
+
+  chisq = chisq / Npts;
+  return (chisq);
+
+}
+# endif
+
+int plot_outline () {
+  
+  int kapa;
+  float xp, yp, x, y;
+  float dx, dy, theta, t, dt;
+  int Noverlay, NOVERLAY;
+  KiiOverlay *overlay;
+  
+  if (!GetImage (NULL, &kapa, NULL)) return (FALSE);
+  
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+  dx = par[2];
+  dy = par[3];
+  dt = 1 / MAX (dx, dy);
+  theta = par[4];
+  
+  for (t = 0; t < 6.3; t += dt) {
+    xp = dx * cos (t);
+    yp = dy * sin (t);
+    
+    x = xp * cos (theta * RAD_DEG) - yp * sin (theta * RAD_DEG) + par[0];
+    y = xp * sin (theta * RAD_DEG) + yp * cos (theta * RAD_DEG) + par[1];
+    
+    overlay[Noverlay].type = KII_OVERLAY_BOX;
+    overlay[Noverlay].x = x;
+    overlay[Noverlay].y = y;
+    overlay[Noverlay].dx = 1.0;
+    overlay[Noverlay].dy = 1.0;
+
+    Noverlay ++;
+    CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+  }
+  KiiLoadOverlay (kapa, overlay, Noverlay, "red");
+  free (overlay);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/polar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/polar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/polar.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "astro.h"
+
+int polar (int argc, char **argv) {
+  
+  double Lo, dL, Do, dD, Mo, dM, No, dN;
+  double xo, yo, Xo, Yo;
+  double x, y, r, t;
+  float *Vin, *Vout, *Vmask;
+  int i, j, nx, ny, Nx, Ny;
+  int X, Y;
+  Buffer *in, *out, *mask;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: polar in out\n");
+    return (FALSE);
+  }
+
+  if ((in   = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((mask = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = out[0].matrix.Naxis[0];
+  Ny = out[0].matrix.Naxis[1];
+  nx = mask[0].matrix.Naxis[0];
+  ny = mask[0].matrix.Naxis[1];
+  if ((Nx != nx) && (Ny != ny)) {
+    gprint (GP_ERR, "output and mask must have same dimensions\n");
+    return (FALSE);
+  }
+  nx = in[0].matrix.Naxis[0];
+  ny = in[0].matrix.Naxis[1];
+
+  /* we expect the output image to have units of longitude and distance */
+  gfits_scan (&in[0].header, "CRVAL1", "%lf", 1, &Lo);
+  gfits_scan (&in[0].header, "CDELT1", "%lf", 1, &dL);
+  gfits_scan (&in[0].header, "CRPIX1", "%lf", 1, &xo);
+  gfits_scan (&in[0].header, "CRVAL2", "%lf", 1, &Do);
+  gfits_scan (&in[0].header, "CDELT2", "%lf", 1, &dD);
+  gfits_scan (&in[0].header, "CRPIX2", "%lf", 1, &yo);
+
+  /* we expect the input image to have units of distance X and Y */
+  gfits_scan (&out[0].header, "CRVAL1", "%lf", 1, &Mo);
+  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dM);
+  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &Xo);
+  gfits_scan (&out[0].header, "CRVAL2", "%lf", 1, &No);
+  gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dN);
+  gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &Yo);
+
+  Vin  = (float *)in[0].matrix.buffer;
+  Vout = (float *)out[0].matrix.buffer;
+  Vmask = (float *)mask[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, Vout++, Vmask++) {
+      x = (i - Xo) * dM + Mo;
+      y = (j - Yo) * dN + No;
+      r = hypot(x, y);
+      t = DEG_RAD*atan2 (y, x);
+      while (t < 360.0) {t += 360.0;}
+      while (t > 360.0) {t -= 360.0;}
+      X = (t - Lo) / dL + xo;
+      Y = (r - Do) / dD + yo;
+      if ((X >= 0) && (X < nx) && (Y >= 0) && (Y < ny)) {
+	*Vout += Vin[Y*nx + X];
+	*Vmask += 1;
+      }
+    }
+  }
+
+ return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/precess.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/precess.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/precess.c	(revision 22322)
@@ -0,0 +1,112 @@
+# include "astro.h"
+
+int precess (int argc, char **argv) {
+
+  int i, Julian, Besselian;
+  double T, in_epoch, out_epoch;
+  double A, D, RA, DEC, zeta, z, theta;
+  double SA, CA, SD, CD;
+  Vector *xvec, *yvec;
+
+  Besselian = Julian = 0;
+  Besselian = get_argument (argc, argv, "B");
+  Julian    = get_argument (argc, argv, "J");
+
+  in_epoch = out_epoch = 2000.0;
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE:  precess (from) (to) RA DEC \n");
+    gprint (GP_ERR, "   you may use B for B1950.0 or J for J2000.0\n");
+    return (FALSE);
+  }
+
+  if (!Julian && !Besselian) { /* assume Julian! */
+    in_epoch  = get_epoch (argv[1], 'J');
+    out_epoch = get_epoch (argv[2], 'J');
+  }
+
+  if ((Julian == 1) && !Besselian) {
+    in_epoch  = 2000.0;
+    out_epoch = get_epoch(argv[2], 'J');
+  }
+
+  if ((Julian == 2) && !Besselian) {
+    in_epoch  = get_epoch(argv[1], 'J');
+    out_epoch = 2000.0;
+  }
+
+  if ((Besselian == 1) && !Julian) {
+    in_epoch  = BtoJ(1950.0); 
+    out_epoch = get_epoch(argv[2], 'B'); 
+  }
+
+  if ((Besselian == 2) && !Julian) {
+    in_epoch  = get_epoch(argv[1], 'B'); 
+    out_epoch = BtoJ(1950.0); 
+  }
+  
+  if (Julian && Besselian) {
+    if (Julian > Besselian) {
+      in_epoch  = BtoJ(1950.0); 
+      out_epoch = 2000.0;
+    }
+    else {
+      in_epoch  = 2000.0;
+      out_epoch = BtoJ(1950.0); 
+    }
+  }
+
+  gprint (GP_ERR, "converting from J%f to J%f\n", in_epoch, out_epoch);
+
+  T = (out_epoch - in_epoch) / 100.0;
+  
+  zeta  = RAD_DEG*(0.6406161*T + 0.0000839*T*T + 0.0000050*T*T*T);
+  theta = RAD_DEG*(0.5567530*T - 0.0001185*T*T - 0.0000116*T*T*T);
+  z     =          0.6406161*T + 0.0003041*T*T + 0.0000051*T*T*T;
+
+  if (ISNUM(argv[3][0]) && ISNUM(argv[4][0])) {
+    A = atof (argv[3]);
+    D = atof (argv[4]);
+    SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+    CD = sqrt (1 - SD*SD);
+    SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+    CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+
+    DEC = DEG_RAD*asin(SD);
+    RA  = DEG_RAD*atan2(SA, CA) + z;
+
+    if (RA < 0)
+      RA += 360;
+    gprint (GP_LOG, "%f %f -> %f %f\n", A, D, RA, DEC);
+    return (TRUE);
+  }    
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[3], argv[4]);
+    return (FALSE);
+  }
+  
+  for (i = 0; i < xvec[0].Nelements; i++) {
+    A = xvec[0].elements[i];
+    D = yvec[0].elements[i];
+    SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+    CD = sqrt (1 - SD*SD);
+    SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+    CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+
+    DEC = DEG_RAD*asin(SD);
+    RA  = DEG_RAD*atan2(SA, CA) + z;
+
+    if (RA < 0)
+      RA += 360;
+    
+    xvec[0].elements[i] = RA;
+    yvec[0].elements[i] = DEC; 
+  }
+
+  return (TRUE);
+
+}  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/profile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/profile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/profile.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "astro.h"
+
+int profile (int argc, char **argv) {
+  
+  int i, j, N, Nx, Npt;
+  float *V;
+  double sx, sy;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: profile <buffer> <X vector> <Y vector> x y N\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  
+  sx = atof (argv[4]);
+  sy = atof (argv[5]);
+  N  = atof (argv[6]);
+
+  if (sx - N < 0) goto range_error;
+  if (sy - N < 0) goto range_error;
+  if (sx + N > buf[0].matrix.Naxis[0]) goto range_error;
+  if (sy + N > buf[0].matrix.Naxis[1]) goto range_error;
+
+  if ((xvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  xvec[0].Nelements = yvec[0].Nelements = (int)SQ(2*N+1);
+  REALLOCATE (xvec[0].elements, float, 5*(int)SQ(2*N+1));
+  REALLOCATE (yvec[0].elements, float, 5*(int)SQ(2*N+1));
+  bzero (yvec[0].elements, (int)SQ(2*N+1)*sizeof(float)+1);
+  V = (float *)(buf[0].matrix.buffer); 
+  Npt = 0;
+  Nx = buf[0].matrix.Naxis[0];
+  for (i = sx - N; i < sx + N; i++) {
+    for (j = sy - N; j < sy + N; j++, Npt++) {
+      yvec[0].elements[Npt] = V[i + j*Nx];
+      xvec[0].elements[Npt] = hypot (i - sx, j - sy);
+    }
+  }
+
+  fsortpair (xvec[0].elements, yvec[0].elements, xvec[0].Nelements);
+
+  return (TRUE);
+
+range_error:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/radec.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/radec.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/radec.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "astro.h"
+
+int radec (int argc, char **argv) {
+
+  int N, QUIET;
+  double ra, dec;
+  char ra_string[32], dec_string[32];
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE:  radec (-hh | -hms) RA DEC [-q | -quiet]\n");
+    gprint (GP_ERR, "   convert to decimal (-hh) or sexigesimal (-hms)\n");
+    gprint (GP_ERR, "   output goes to $RA, $DEC\n");
+    return (FALSE);
+  }
+
+  if (strcmp (argv[1], "-hh") && strcmp (argv[1], "-hms")) {
+    gprint (GP_ERR, "USAGE:  radec (-hh | -hms) RA DEC [-q | -quiet]\n");
+    gprint (GP_ERR, "   convert to decimal (-hh) or sexigesimal (-hms)\n");
+    gprint (GP_ERR, "   output goes to $RA, $DEC\n");
+    return (FALSE);
+  }    
+
+  if (!ohana_str_to_radec (&ra, &dec, argv[2], argv[3])) {
+    return (FALSE);
+  }
+
+  if (!strcmp (argv[1], "-hh")) {
+    set_variable ("RA", ra);
+    set_variable ("DEC", dec);
+    if (!QUIET) {
+      gprint (GP_LOG, "%10.6f %10.6f\n", ra, dec);
+    }
+    return (TRUE);
+  }
+
+  // convert to hms, dms
+  hms_format (ra_string, ra/15.0);
+  hms_format (dec_string, dec);
+
+  set_str_variable ("RA", ra_string);
+  set_str_variable ("DEC", dec_string);
+  if (!QUIET) {
+    gprint (GP_LOG, "%s %s\n", ra_string, dec_string);
+  }
+  return (TRUE);
+}  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/region.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/region.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/region.c	(revision 22322)
@@ -0,0 +1,136 @@
+# include "astro.h"
+
+int region (int argc, char **argv) {
+  
+  char string[256];
+  double Ra, Dec, Radius;
+  float dx, dy;
+  int N, kapa;
+  char *name;
+  Graphdata graphmode;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if ((N = get_argument (argc, argv, "-image"))) {
+    remove_argument (N, &argc, argv);
+    KapaGetImageCoords (kapa, &graphmode.coords);
+    KapaGetImageRange (kapa, &graphmode.xmin, &graphmode.xmax, &graphmode.ymax, &graphmode.ymin);
+    SetGraph (&graphmode);
+    return (TRUE);
+    // Set Region based on image
+  }
+
+  if ((N = get_argument (argc, argv, "-ew"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipeast = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "+ew"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipeast = FALSE;
+  }
+
+  if ((N = get_argument (argc, argv, "-ns"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipnorth = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "+ns"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipnorth = FALSE;
+  }
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: region Ra Dec Radius [projection] [orientation]\n");
+    gprint (GP_ERR, " current: %f %f (%f x %f) (%s)\n", 
+	     graphmode.coords.crval1, graphmode.coords.crval2, 
+	     fabs(graphmode.xmax - graphmode.xmin), 
+	     fabs(graphmode.ymax - graphmode.ymin), 
+	     &graphmode.coords.ctype[5]);
+    return (FALSE);
+  }
+  
+  if (!ohana_str_to_radec (&Ra, &Dec, argv[1], argv[2])) return (FALSE);
+  Radius = atof (argv[3]);
+  strcpy (graphmode.coords.ctype, "RA---TAN");
+  if (argc == 5) {
+    if (!strcasecmp (argv[4], "TAN")) 
+      strcpy (graphmode.coords.ctype, "RA---TAN");
+    if (!strcasecmp (argv[4], "SIN")) 
+      strcpy (graphmode.coords.ctype, "RA---SIN");
+    if (!strcasecmp (argv[4], "ARC")) 
+      strcpy (graphmode.coords.ctype, "RA---ARC");
+    if (!strcasecmp (argv[4], "STG")) 
+      strcpy (graphmode.coords.ctype, "RA---STG");
+    if (!strcasecmp (argv[4], "ZEA"))
+      strcpy (graphmode.coords.ctype, "RA---ZEA");
+    if (!strcasecmp (argv[4], "AIT")) 
+      strcpy (graphmode.coords.ctype, "RA---AIT");
+    if (!strcasecmp (argv[4], "GLS")) 
+      strcpy (graphmode.coords.ctype, "RA---GLS");
+    if (!strcasecmp (argv[4], "PAR")) 
+      strcpy (graphmode.coords.ctype, "RA---PAR");
+  }
+
+  
+  /* ask kapa for coordinate limits, to get the right aspect ratio */
+  KapaGetLimits (kapa, &dx, &dy);
+  dx = fabs (dx);
+  dy = fabs (dy); 
+
+  /* define limits for Ra, Dec at center, grid in degrees */
+  if (dy < dx) {
+    graphmode.xmin = -(dx/dy)*Radius;
+    graphmode.ymin = -Radius;
+    graphmode.xmax = (dx/dy)*Radius;
+    graphmode.ymax = Radius;
+  } else {
+    graphmode.xmin = -Radius;
+    graphmode.ymin = -(dy/dx)*Radius;
+    graphmode.xmax = Radius;
+    graphmode.ymax = (dy/dx)*Radius;
+  } 
+
+  set_variable ("XMIN", graphmode.xmin);
+  set_variable ("XMAX", graphmode.xmax);
+  set_variable ("YMIN", graphmode.ymin);
+  set_variable ("YMAX", graphmode.ymax);
+
+  set_variable ("RMIN", Ra  + graphmode.xmin);
+  set_variable ("RMAX", Ra  + graphmode.xmax);
+  set_variable ("DMIN", Dec + graphmode.ymin);
+  set_variable ("DMAX", Dec + graphmode.ymax);
+
+  set_int_variable ("EAST_RIGHT", !graphmode.flipeast);
+  set_int_variable ("NORTH_UP", !graphmode.flipnorth);
+
+  graphmode.coords.pc1_1 = (graphmode.flipeast) ? -1 : 1;
+  graphmode.coords.pc2_2 = (graphmode.flipnorth) ? -1 : 1;
+
+  graphmode.coords.pc1_2 = graphmode.coords.pc2_1 = 0.0;
+  graphmode.coords.crval1 = Ra;
+  graphmode.coords.crval2 = Dec;
+  graphmode.coords.crpix1 = 0.0;
+  graphmode.coords.crpix2 = 0.0;
+  graphmode.coords.cdelt1 = graphmode.coords.cdelt2 = 1.0;
+
+  KapaClearSections (kapa);
+  KapaSetLimits (kapa, &graphmode);
+
+  /* drop this? */
+  sprintf (string, "%8.4f %8.4f (%f)", Ra, Dec, Radius);
+  KapaSendLabel (kapa, string, 2);
+
+  // XXX is this the right thing to be doing?
+  SetGraph (&graphmode);
+  return (TRUE);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/rotcurve.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/rotcurve.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/rotcurve.c	(revision 22322)
@@ -0,0 +1,120 @@
+# include "astro.h"
+
+int rotcurve (int argc, char **argv) {
+  
+  int i, j, X, Y, n, Ncurve;
+  float *Vin, *Vout, *Vmask;
+  int nx, ny, Nx, Ny, N;
+  double L, dL, Lo, V, Vo, dV, Bo, dB, Do, dD;
+  double xo, yo, Xo, Yo;
+  double sl, cl, wo, Ro, Rs, wr, r, fr, d, min;
+  double R[100], T[100], W[100];
+  FILE *f;
+  Buffer *in, *out, *mask;
+
+  min = -1000;
+  if ((N = get_argument (argc, argv, "-min"))) {
+    remove_argument (N, &argc, argv);
+    min = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: rotcurve in out mask curve.txt\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[4], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't find rotation curve data file %s\n", argv[4]);
+    return (FALSE);
+  }
+  for (i = 0; fscanf (f, "%lf %lf", &R[i], &T[i]) != EOF; i++) {
+    W[i] = T[i] / R[i];
+  }  
+  fclose (f);
+  Ncurve = i;
+
+  if ((in   = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((mask = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = out[0].matrix.Naxis[0];
+  Ny = out[0].matrix.Naxis[1];
+  nx = mask[0].matrix.Naxis[0];
+  ny = mask[0].matrix.Naxis[1];
+  if ((Nx != nx) && (Ny != ny)) {
+    gprint (GP_ERR, "output and mask must have same dimensions\n");
+    return (FALSE);
+  }
+  nx = in[0].matrix.Naxis[0];
+  ny = in[0].matrix.Naxis[1];
+
+  /* we expect the input image to have units of velocity, lattitude, and longitude */
+  gfits_scan (&in[0].header, "CRVAL1", "%lf", 1, &Vo);
+  gfits_scan (&in[0].header, "CDELT1", "%lf", 1, &dV);
+  gfits_scan (&in[0].header, "CRPIX1", "%lf", 1, &xo);
+  gfits_scan (&in[0].header, "CRVAL2", "%lf", 1, &Bo);
+  gfits_scan (&in[0].header, "CDELT2", "%lf", 1, &dB);
+  gfits_scan (&in[0].header, "CRPIX2", "%lf", 1, &yo);
+  gfits_scan (&in[0].header, "CRVAL3", "%lf", 1, &L);
+  Vo *= 0.001;
+  dV *= 0.001;
+
+  /* we expect the output image to have units of longitude and distance */
+  gfits_scan (&out[0].header, "CRVAL1", "%lf", 1, &Lo);
+  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dL);
+  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &Xo);
+  gfits_scan (&out[0].header, "CRVAL2", "%lf", 1, &Do);
+  gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dD);
+  gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &Yo);
+
+  while (L >= 360) {L -= 360.0;}
+  while (L < 0.0)  {L += 360.0;}
+  X = (L - Lo) / dL + Xo;
+  if ((X >= Nx) || (X < 0)) {
+    gprint (GP_ERR, "X out of range\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, "L: %f (%d)\n", L, X);
+
+  cl = cos (L*RAD_DEG);
+  sl = sin (L*RAD_DEG);
+  wo = 25.0;
+  Ro = 10.0;
+  Rs = Ro*sl;
+  /* this method depends on wr monotonically decreasing */
+
+  Vin  = (float *)in[0].matrix.buffer;
+  Vout = (float *)out[0].matrix.buffer;
+  Vmask = (float *)mask[0].matrix.buffer;
+  for (j = 0; j < ny; j++) {
+    for (i = 0; i < nx; i++, Vin++) {
+      if (*Vin <= min) continue;
+      V = (i - xo) * dV + Vo;
+      wr = V/Rs + wo;
+      for (n = 0; (n < Ncurve) && (wr < W[n]); n++);
+      if ((n == 0) || (n == Ncurve)) {
+	continue;
+      }
+      r = (wr - W[n]) *  (R[n-1] - R[n]) / (W[n-1] - W[n]) + R[n];
+      fr = (Ro/r);
+      if (r < fabs(Rs)) { /* can't be on rotation curve */
+	continue;
+      }
+      if (r < Ro)
+	d = Ro*cl - sqrt(r*r - Rs*Rs);
+      else 
+	d = Ro*cl + sqrt(r*r - Rs*Rs);
+      Y = (d - Do) / dD + Yo;
+      if ((Y < Ny) && (Y >= 0)) {
+	Vout[Y*Nx + X] += *Vin;
+	Vmask[Y*Nx + X] += 1.0;
+      }
+    }
+  }
+
+
+  return (TRUE);
+
+} 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/scale.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/scale.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/scale.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "astro.h"
+
+int scale (int argc, char **argv) {
+
+  Buffer *buf;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: scale (buffer) (key) [-r/-w] (value)\n");
+    return (FALSE);
+  }  
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (strcasecmp (argv[2], "bzero") && strcasecmp (argv[2], "bscale")) {
+    gprint (GP_ERR, "use bzero or bscale only\n");
+    return (FALSE);
+  }
+    
+  if (strcmp (argv[3], "-r") && strcmp (argv[3], "-w")) {
+    gprint (GP_ERR, "use -r or -w only\n");
+    return (FALSE);
+  }
+    
+  if (!strcmp (argv[3], "-r")) {
+    if (!strcasecmp (argv[2], "bzero")) {
+      set_variable (argv[4], (double) buf[0].bzero);
+    } else {
+      set_variable (argv[4], (double) buf[0].bscale);
+    }      
+  } else {
+    if (!strcasecmp (argv[2], "bzero")) {
+      buf[0].bzero = atof (argv[4]);
+    } else {
+      buf[0].bscale = atof (argv[4]);
+    }      
+  }
+
+  return (TRUE);
+}
+
+/* get or set external bzero / bscale values 
+   (these keywords are set to 0,1 internally, 
+   so we can't just manipulate them like other keywords */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/sexigesimal.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/sexigesimal.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/sexigesimal.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include "astro.h"
+
+int sexigesimal (int argc, char **argv) {
+  
+  int HMS, N;
+  double value;
+  char string[80];
+
+  HMS = TRUE;
+  if ((N = get_argument (argc, argv, "-hms"))) {
+    HMS = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-hh"))) {
+    HMS = FALSE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 3) && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: sexigesimal (from) [to]\n");
+    return (FALSE);
+  }
+
+  if (HMS) {
+    if (!ohana_dms_to_ddd (&value, argv[1])) {
+      gprint (GP_ERR, "syntax error in input\n");
+      return (FALSE);
+    }
+    if (argc == 3) {
+      set_variable (argv[2], value);
+    } else {
+      gprint (GP_LOG, "%10.6f\n", value);
+    }
+    return (TRUE);
+  } else {
+    value = atof (argv[1]);
+    hms_format (string, value);
+    if (argc == 3) {
+      set_str_variable (argv[2], string);
+    } else {
+      gprint (GP_LOG, "%s\n", string);
+    }
+    return (TRUE);
+  }      
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/spec.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/spec.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/spec.c	(revision 22322)
@@ -0,0 +1,112 @@
+# include "astro.h"
+
+int spec (int argc, char **argv) {
+
+  int i, j, Xo, X1, y1, y2, Nx, Ny;
+  int Nlong, Ngap, Nrow, N, Nring;
+  float *buffer, *V;
+  double sky, sky2, S, SX, F, R, Npts;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  Nlong = 31;
+  if ((N = get_argument (argc, argv, "-Nlong"))) {
+    remove_argument (N, &argc, argv);
+    Nlong  = 0.5*atof(argv[N]);
+    Nlong = 2*Nlong + 1;  /* force an odd number */
+    remove_argument (N, &argc, argv);
+  }
+  
+  Ngap = 15;
+  if ((N = get_argument (argc, argv, "-Ngap"))) {
+    remove_argument (N, &argc, argv);
+    Ngap  = 0.5*atof(argv[N]);
+    Ngap = 2*Ngap + 1;  /* force an odd number */
+    remove_argument (N, &argc, argv);
+  }
+  
+  Nrow = 1;
+  if ((N = get_argument (argc, argv, "-Nrow"))) {
+    remove_argument (N, &argc, argv);
+    Nrow  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: spec buffer x y1 y2 X Y [-Nlong N] [-Ngap N] [-Nrow N]\n");
+    return (FALSE);
+  }
+  
+  if ((Nrow < 1) || (Nlong < 2) || (Ngap < 1) || (Nlong - Ngap < 2)) {
+    gprint (GP_ERR, "bad values for options\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  Xo = atof (argv[2]);
+  y1 = atof (argv[3]);
+  y2 = atof (argv[4]);
+
+  if ((xvec = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  N = y2 - y1;
+  REALLOCATE (xvec[0].elements, float, N);
+  REALLOCATE (yvec[0].elements, float, N);
+  xvec[0].Nelements = N;
+  yvec[0].Nelements = N;
+  
+  ALLOCATE (buffer, float, Nlong);
+
+  for (j = 0; j < y2 - y1; j++) {
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + Xo - (int)(0.5*Nlong);
+    /* find sky on edge */
+    for (i = 0, Nring = 0; i < 0.5*(Nlong - Ngap); i++, V++, Nring++) {
+      buffer[i] = *V;
+    }
+    fsort (buffer, Nring);
+    for (Npts = sky = 0, i = 0.25*Nring; i < 0.75*Nring; i++, Npts += 1.0) {
+      sky += buffer[i];
+    }
+    sky = sky / Npts;
+    /* find center column for this row */
+    for (S = SX = i = 0, Nring = 0; i < Ngap; i++, V++, Nring++) {
+      S += (*V - sky);
+      SX += (*V - sky)*(i + Xo - 0.5*Ngap);
+    }
+    X1 = SX / S;
+    gprint (GP_ERR, "%4d %4d %5.1f ", j+y1, X1, sky);
+    /*    X1 = MAX (MIN (X1, Xo + 0.5+Ngap), Xo - 0.5+Ngap); */
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + X1 - (int)(0.5*Nlong);
+    /* find sky on edges */
+    for (i = 0, Nring = 0; i < 0.5*(Nlong - Ngap); i++, V++, Nring++) {
+      buffer[Nring] = *V;
+    }
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + X1 + (int)(0.5*Ngap);
+    for (i = 0; i < 0.5*(Nlong - Ngap); i++, V++, Nring++) {
+      buffer[Nring] = *V;
+    }
+    fsort (buffer, Nring);
+    for (Npts = sky = sky2 = 0, i = 0.25*Nring; i < 0.75*Nring; i++, Npts += 1.0) {
+      sky += buffer[i];
+      sky2 += buffer[i]*buffer[i];
+    }
+    sky = sky / Npts;
+    sky2 = (sky2 / Npts - sky*sky);
+    /* find weighted flux */
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + X1 - (int)(0.5*Ngap);
+    for (F = R = i = 0; i < Ngap; i++, V++) {
+      F += (*V - sky) / sky2;
+      R += 1.0 / sky2;
+    }
+    xvec[0].elements[j] = j + y1; 
+    yvec[0].elements[j] = F / R; 
+    gprint (GP_ERR, " %5.1f %7.1f  %6.2f\n", sky, sky2, (F/R));
+  }    
+
+  free (buffer);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/star.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/star.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/star.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "astro.h"
+
+int star (int argc, char **argv) {
+
+  int x, y, N, dx, Nborder;
+  double Z, max;
+  Buffer *buf;
+
+  Nborder = 3;
+  if ((N = get_argument (argc, argv, "-border"))) {
+    remove_argument (N, &argc, argv);
+    Nborder  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Nborder = MAX (Nborder, 1);
+  
+  max = 60000;
+  if ((N = get_argument (argc, argv, "-sat"))) {
+    remove_argument (N, &argc, argv);
+    max  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: star (buffer) x y [dx] [-border N] [-sat cnts]\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  dx = 11;
+  x = atof (argv[2]);
+  y = atof (argv[3]);
+  if (argc == 5) {
+    dx = atof (argv[4]);
+  }
+
+  Z = get_aperture_stats (&buf[0].matrix, x, y, dx, Nborder, max);
+  
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/testfit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/testfit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/testfit.c	(revision 22322)
@@ -0,0 +1,161 @@
+# include "astro.h"
+
+/* local private functions */
+float fgaussOD (float, float *, int, float *);
+
+int imfit (int argc, char **argv) {
+
+  float par[4], *v1, *v2, *dy, chisq, **covar;
+  int i, Npts, Npar;
+  Vector *xvec, *yvec, *svec;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: imfit <x> <y> <dy>\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((svec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Npts = xvec[0].Nelements;
+  ALLOCATE (dy, float, Npts);
+  v1 = svec[0].elements;
+  v2 = dy;
+  
+  for (i = 0; i < Npts; i++, v1++, v2++) *v2 = 1.0 / (*v1 * *v1);
+  
+  par[0] = 7;
+  par[1] = 2;
+  par[2] = 6;
+  par[3] = 1;
+  Npar = 4;
+
+  mrqinit (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fgaussOD);
+
+  for (i = 0; i < 10; i++) {
+
+    chisq = mrqmin (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fgaussOD);
+    gprint (GP_ERR, "chisq: %f, %f %f %f %f\n", chisq, par[0], par[1], par[2], par[3]);
+
+  }  
+
+  covar = mrqcovar (Npar);
+
+  for (i = 0; i < Npar; i++) {
+    gprint (GP_ERR, "%d  %f  %f\n", i, par[i], covar[i][i]);
+  }
+
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+
+/* pars: x, y, sx, sy, sxy, sky I, */
+float fgaussOD (float x, float *par, int Npar, float *dpar) {
+
+  float X, S, Z, R, f;
+
+  X = x - par[0];
+  S = 1.0 / (par[1]*par[1]);
+  Z = -0.5*X*X*S;
+  R = exp (Z);
+  f = par[2]*R + par[3];
+
+  dpar[0] = par[2]*R*X*S;
+  dpar[1] = dpar[0]*X/par[1];
+  dpar[2] = R;
+  dpar[3] = 1;
+  
+  return (f);
+
+}
+
+# if (0)
+
+/* pars: x, y, sx, sy, sxy, sky I, */
+float testF (float x, float *par, int Npar, float *dpar) {
+
+  float f;
+
+  f = par[0]*x + par[1];
+
+  dpar[0] = x;
+  dpar[1] = 1;
+  
+  return (f);
+
+}
+
+
+/* pars: x, y, sx, sy, sxy, sky I, */
+float fgaussTD (float x, float y, float *par, int Npar) {
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  t1 = X / par[2];
+  t2 = Y * Y / par[3];
+  t3 = Y * par[4] * 2.0;
+
+  r = 0.5 * ((t1 + t3)*X + t2);
+  f = par[5] + par[6] / (1.0 + r*(1.0 + 0.5*r*(1.0 + 0.33333333*r)));
+  
+  return (f);
+
+}
+
+float chisq (float *buf, float *sig, int Nx, int Ny, float (func)(), float *par, int Npar) {
+
+  float *ptr;
+
+  X = 0;
+  ptr = buf;
+  for (i = 0; i < Nx; i++) {
+    for (j = 0; j < Ny; j++, ptr++, sig++) {
+      f = *ptr - func ((float) i, (float) j, par, Npar);
+      X += (f * f) / *sig;
+    }
+  }    
+  return (X);
+}
+
+  int i, j, Nbuf, status;
+  char *string;
+  double Npix, N1, N2, max, min, range, median;
+  float *V;
+  int sx, sy, nx, ny, *hist, Nhist, bin;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: imfit <buffer> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  if (!SelectBuffer (&Nbuf, argv[1], OLDBUFFER)) return (FALSE);
+
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+
+  Npix = N1 = N2 = 0;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buffers[Nbuf].matrix.Naxis[0]) || 
+      (sy+ny > buffers[Nbuf].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  Npix = nx*ny;
+
+  ALLOCATE (tempbuf, float, Npix);
+
+  buf = tempbuf;
+  for (j = 0; j < ny; j++) {
+    V = (float *)(buffers[Nbuf].matrix.buffer) + (j+sy)*buffers[Nbuf].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      *buf = *V;
+    }
+  }
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/transform.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/transform.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/transform.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "astro.h"
+
+int transform (int argc, char **argv) {
+
+  int i, j, Nx, Ny;
+  Coords coords_in, coords_out;
+  double scale_in, scale_out;
+  int X, Y;
+  double x, y, r, d, dx, dy;
+  double frac;
+  char *Sout, *S;
+  float *Vin, *Vout;
+  Buffer *in, *out;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: transform <from> <to>\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  GetCoords (&coords_in, &in[0].header);
+  GetCoords (&coords_out, &out[0].header);
+
+  /* for the moment, disable WRP / DIS */
+  if (!strcmp(&coords_in.ctype[4], "-WRP") || !strcmp(&coords_out.ctype[4], "-WRP")) {
+    gprint (GP_ERR, "WRP mode not implemented for astrom\n");
+    return (FALSE);
+  }
+  
+  scale_in = sqrt(fabs(coords_in.cdelt1*coords_in.cdelt2*(coords_in.pc1_1*coords_in.pc2_2 - coords_in.pc1_2*coords_in.pc2_1)));
+  scale_out = sqrt(fabs(coords_out.cdelt1*coords_out.cdelt2*(coords_out.pc1_1*coords_out.pc2_2 - coords_out.pc1_2*coords_out.pc2_1)));
+
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Nx = out[0].header.Naxis[0];
+  Ny = out[0].header.Naxis[1];
+  bzero (Vout, Nx*Ny*sizeof(float));
+  ALLOCATE (S, char, Nx*Ny);
+  Sout = S;
+  bzero (Sout, Nx*Ny*sizeof(char));
+  frac = 0.333;
+
+  /* if (scale_in < scale_out) { */
+
+  for (j = 0; j < in[0].header.Naxis[1]; j++) {
+    gprint (GP_ERR, ".");
+    for (i = 0; i < in[0].header.Naxis[0]; i++, Vin++) {
+      for (dx = 0.0 + 0.5*frac; dx < 1.0 - 0.5*frac; dx += frac) {
+	for (dy = 0.0 + 0.5*frac; dy < 1.0 - 0.5*frac; dy += frac) {
+	  XY_to_RD (&r, &d, i + dx, j + dy, &coords_in);
+	  RD_to_XY (&x, &y, r, d, &coords_out);
+	  X = x; Y = y;
+	  if ((X > -1) && (X < Nx) && (Y > -1) && (Y < Ny)) {
+	    Vout[X + Y*Nx] += *Vin;
+	    Sout[X + Y*Nx] ++;
+	  }
+	}
+      }
+    }
+  }
+
+  Sout = S;
+  Vout = (float *) out[0].matrix.buffer;
+  for (i = 0; i < Nx*Ny; i++, Vout++, Sout++) {
+    *Vout = *Vout / *Sout;
+  }
+
+  free (S);
+    
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/warp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/warp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.astro/warp.c	(revision 22322)
@@ -0,0 +1,237 @@
+# include "astro.h"
+
+/*** needs mosaic astrometry ***/
+
+static double XO, XX, XY;
+static double YO, YX, YY;
+int ZERO;
+
+int map_output_to_input (int Npix, double df);
+int map_input_to_output (int Npix, double df);
+void set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix);
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin);
+
+Coords coords_in, coords_out;
+Buffer *in, *out, *wt;
+
+int warp (int argc, char **argv) {
+
+  int Nlinear, Np, N;
+  double scale_in, scale_out, df;
+
+  ZERO = FALSE;
+  if ((N = get_argument (argc, argv, "-zero"))) {
+    ZERO = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: transform <from> <to> <weight> (Nlinear)\n");
+    gprint (GP_ERR, "  output buffer must exist with target astrometry header\n");
+    gprint (GP_ERR, "  Nlinear is the pixel scale for linear astrometric transformation\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((wt  = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nlinear = atoi (argv[4]);
+
+  GetCoords (&coords_in, &in[0].header);
+  GetCoords (&coords_out, &out[0].header);
+
+  /* for the moment, disable WRP / DIS */
+  if (!strcmp(&coords_in.ctype[4], "-WRP") || !strcmp(&coords_out.ctype[4], "-WRP")) {
+    gprint (GP_ERR, "WRP mode not implemented for astrom\n");
+    return (FALSE);
+  }
+  
+  scale_in = sqrt(fabs(coords_in.cdelt1*coords_in.cdelt2*(coords_in.pc1_1*coords_in.pc2_2 - coords_in.pc1_2*coords_in.pc2_1)));
+  scale_out = sqrt(fabs(coords_out.cdelt1*coords_out.cdelt2*(coords_out.pc1_1*coords_out.pc2_2 - coords_out.pc1_2*coords_out.pc2_1)));
+  
+  gprint (GP_ERR, "%f - %f\n", scale_in, scale_out);
+
+  if (scale_in > scale_out) {
+    Np = MAX (1, 3*scale_out / scale_in);
+    df = 1.0 / Np;
+    map_output_to_input (Nlinear, df);
+  } else {
+    Np = MAX (1, 3*scale_in / scale_out);
+    df = 1.0 / Np;
+    map_input_to_output (Nlinear, df);
+  }
+  return (TRUE);
+}
+
+/* mode 1: input pixels >> output pixels: loop over output pixels */
+/* mode 2: input pixels << output pixels: loop over input pixels */
+/* mode 3: input pixels ~= output pixels: drizzle input to output */
+
+/* loop over the input pixels, map input output image */
+int map_output_to_input (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+
+  nx = in[0].header.Naxis[0];
+  ny = in[0].header.Naxis[1];
+  Nx = out[0].header.Naxis[0];
+  Ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, Nx*Ny*sizeof(float));
+    bzero (Vwt,  Nx*Ny*sizeof(float));
+  }
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      set_linear_terms (&coords_out, &coords_in, i, j, Npix);
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  No = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  Ni = (int)x + ((int)y)*nx;
+
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+/* loop over the input pixels, map input output image */
+int map_input_to_output (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  nx = out[0].header.Naxis[0];
+  ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, nx*ny*sizeof(float));
+    bzero (Vwt,  nx*ny*sizeof(float));
+  }
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      set_linear_terms (&coords_in, &coords_out, i, j, Npix);
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  Ni = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  No = (int)x + ((int)y)*nx;
+
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+/* find the linear astrometric fix between images at this location */
+void set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix) {
+
+  int n;
+  double x, y, x2, y2, xy, X, Y, Xx, Xy, Yx, Yy;
+  double Xin, Yin, Xout, Yout;
+  double Sx2, Sy2, Sxy, SXx, SXy, SYx, SYy;
+  double N, r, d;
+
+  Xin = Yin = 0;
+  N = x = y = x2 = y2 = xy = X = Y = Xx = Xy = Yx = Yy = 0;
+
+  /* define several test points, fit a line to the input,output pairs */
+  for (n = 0; n < 3; n++) {
+
+    switch (n) {
+    case 0:
+      Xin = i;
+      Yin = j;
+      break;
+    case 1:
+      Xin = i + Npix;
+      Yin = j;
+      break;
+    case 2:
+      Xin = i;
+      Yin = j + Npix;
+      break;
+    }
+
+    XY_to_RD (&r, &d, Xin, Yin, in);
+    RD_to_XY (&Xout, &Yout, r, d, out);
+
+    x  += Xin;
+    y  += Yin;
+    x2 += Xin*Xin;
+    y2 += Yin*Yin;
+    xy += Xin*Yin;
+    X  += Xout;
+    Y  += Yout;
+    Xx += Xout*Xin;
+    Xy += Xout*Yin;
+    Yx += Yout*Xin;
+    Yy += Yout*Yin;
+    N  += 1.0;
+  }
+
+  Sx2 = x2 - x*x/N;
+  Sy2 = y2 - y*y/N;
+  Sxy = xy - x*y/N;
+  SXx = Xx - X*x/N;
+  SXy = Xy - X*y/N;
+  SYx = Yx - Y*x/N;
+  SYy = Yy - Y*y/N;
+  
+  XX = (SXx*Sy2 - SXy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XY = (SXy*Sx2 - SXx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XO = X/N - XX*x/N - XY*y/N;
+
+  YX = (SYx*Sy2 - SYy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YY = (SYy*Sx2 - SYx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YO = Y/N - YX*x/N - YY*y/N;
+
+}
+
+
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin) {
+  *Xout = XO + XX*Xin + XY*Yin;
+  *Yout = YO + YX*Xin + YY*Yin;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/Makefile	(revision 22322)
@@ -0,0 +1,80 @@
+default: libbasiccmd
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/cmd.basic
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# basic user commands ########################
+
+srcs = \
+$(SRC)/init.$(ARCH).o       \
+$(SRC)/break.$(ARCH).o	     \
+$(SRC)/cd.$(ARCH).o	     \
+$(SRC)/config.$(ARCH).o     \
+$(SRC)/continue.$(ARCH).o   \
+$(SRC)/basename.$(ARCH).o     \
+$(SRC)/dirname.$(ARCH).o     \
+$(SRC)/date.$(ARCH).o	     \
+$(SRC)/echo.$(ARCH).o	     \
+$(SRC)/file.$(ARCH).o	     \
+$(SRC)/getchr.$(ARCH).o     \
+$(SRC)/help.$(ARCH).o	     \
+$(SRC)/input.$(ARCH).o	     \
+$(SRC)/list.$(ARCH).o	     \
+$(SRC)/list_help.$(ARCH).o  \
+$(SRC)/list_vars.$(ARCH).o  \
+$(SRC)/local.$(ARCH).o	     \
+$(SRC)/macro.$(ARCH).o	     \
+$(SRC)/memory.$(ARCH).o     \
+$(SRC)/mkdir.$(ARCH).o     \
+$(SRC)/module.$(ARCH).o     \
+$(SRC)/output.$(ARCH).o     \
+$(SRC)/quit.$(ARCH).o	     \
+$(SRC)/run_for.$(ARCH).o    \
+$(SRC)/run_if.$(ARCH).o     \
+$(SRC)/run_while.$(ARCH).o  \
+$(SRC)/scan.$(ARCH).o	     \
+$(SRC)/shell.$(ARCH).o	     \
+$(SRC)/sprintf.$(ARCH).o    \
+$(SRC)/fprintf.$(ARCH).o    \
+$(SRC)/strlen.$(ARCH).o     \
+$(SRC)/substr.$(ARCH).o     \
+$(SRC)/strhash.$(ARCH).o     \
+$(SRC)/strpop.$(ARCH).o     \
+$(SRC)/strsub.$(ARCH).o     \
+$(SRC)/usleep.$(ARCH).o     \
+$(SRC)/sleep.$(ARCH).o	     \
+$(SRC)/wait.$(ARCH).o	     \
+$(SRC)/which.$(ARCH).o
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/convert.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libbasiccmd.$(ARCH).a: $(srcs)
+$(LIB)/libbasiccmd.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libbasiccmd.a: $(LIB)/libbasiccmd.$(ARCH).a
+$(DESTLIB)/libbasiccmd.$(DLLTYPE): $(LIB)/libbasiccmd.$(ARCH).$(DLLTYPE)
+
+libbasiccmd: $(DESTLIB)/libbasiccmd.a $(DESTLIB)/libbasiccmd.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libbasiccmd.a
+	rm -f $(DESTLIB)/libbasiccmd.$(DLLTYPE)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/basename.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/basename.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/basename.c	(revision 22322)
@@ -0,0 +1,51 @@
+# include "basic.h"
+
+int basename_opihi (int argc, char **argv) {
+
+  int N;
+  char *baseName, *varName, *suffixName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  suffixName = NULL;
+  if ((N = get_argument (argc, argv, "-suffix"))) {
+    remove_argument (N, &argc, argv);
+    suffixName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: dirname (path) [-var name] [-suffix suffix]\n");
+    return (FALSE);
+  }
+
+  baseName = filebasename (argv[1]);
+
+  // strip suffix, if supplied
+  if (suffixName != NULL) {
+      if (strlen(baseName) > strlen(suffixName)) {
+	  char *ptr = baseName + strlen(baseName) - strlen(suffixName);
+	  if (!strcmp (ptr, suffixName)) {
+	      *ptr = 0;
+	  }
+      }
+  }
+
+  if (varName == NULL) {
+    gprint (GP_LOG, "%s\n", baseName);
+  } else {
+    set_str_variable (varName, baseName);
+    free (varName);
+  }    
+
+  free (baseName);
+  return (TRUE);
+}
+
+// XXX need to add mode option
+// XXX need to respect umask (need umask command?)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/break.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/break.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/break.c	(revision 22322)
@@ -0,0 +1,27 @@
+# include "basic.h"
+
+int exec_break (int argc, char **argv) {
+
+  int N, value;
+
+  if ((N = get_argument (argc, argv, "-auto"))) {
+    remove_argument (N, &argc, argv);
+    value = -1;
+    if (!strcasecmp (argv[N], "on")) value = TRUE;
+    if (!strcasecmp (argv[N], "off")) value = FALSE;
+    if (value == -1) {
+      gprint (GP_ERR, "USAGE: break -auto [on / off]\n");
+      if (auto_break) 
+	gprint (GP_ERR, "auto break on\n");
+      else 
+	gprint (GP_ERR, "auto break off\n");
+      return (FALSE);
+    }
+    auto_break = value;
+    return (TRUE);
+  }
+
+  loop_break = TRUE;
+  return (FALSE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/cd.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/cd.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/cd.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "basic.h"
+
+int cd (int argc, char **argv) {
+
+  int N, VERBOSE, status;
+  char *cwd;
+
+  VERBOSE = TRUE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = FALSE;
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: cd <path>\n");
+    return (FALSE);
+  }
+
+  status = chdir (argv[1]);
+  if (!status) {
+    if ((cwd = getcwd (NULL, 64)) == NULL) {
+      gprint (GP_ERR, "error getting cwd\n");
+      return (FALSE);
+    }
+    if (VERBOSE) gprint (GP_LOG, "cwd: %s\n", cwd);
+    ohana_memregister (cwd);
+    free (cwd);
+    return (TRUE);
+  }
+
+  gprint (GP_ERR, "error changing to %s\n", argv[1]);
+  return (FALSE);
+
+}
+
+int pwd (int argc, char **argv) {
+
+  int N;
+  char *cwd, *var;
+
+  var = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    var = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: pwd [-var variable]\n");
+    return (FALSE);
+  }
+  
+  if ((cwd = getcwd(NULL, 64)) == NULL) {
+    gprint (GP_ERR, "error getting cwd\n");
+    if (var != NULL) free (var);
+    return (FALSE);
+  }
+  if (var == NULL) {
+      gprint (GP_LOG, "cwd: %s\n", cwd);
+  } else {
+      set_str_variable (var, cwd);
+      free (var);
+  }
+  ohana_memregister (cwd);
+  free (cwd);
+  return (TRUE);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/config.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/config.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/config.c	(revision 22322)
@@ -0,0 +1,9 @@
+# include "basic.h"
+
+int config (int argc, char **argv) {
+
+  if (!ConfigInit (&argc, argv)) return (FALSE);
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/continue.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/continue.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/continue.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include "basic.h"
+
+int exec_next (int argc, char **argv) {
+  loop_next = TRUE;
+  return (TRUE);
+}
+
+int exec_last (int argc, char **argv) {
+  loop_last = TRUE;
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/date.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/date.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/date.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "basic.h"
+
+int date (int argc, char **argv) {
+  
+  int N, SECONDS, REFTIME;
+  struct timeval now;
+  char *tstring = NULL;
+  char *varName = NULL;
+
+  SECONDS = FALSE;
+  if ((N = get_argument (argc, argv, "-seconds"))) {
+    remove_argument (N, &argc, argv);
+    SECONDS = TRUE;
+  } else {
+    ALLOCATE (tstring, char, 32);
+  }
+
+  REFTIME = 0;
+  if ((N = get_argument (argc, argv, "-reftime"))) {
+    remove_argument (N, &argc, argv);
+    REFTIME = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  } 
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: date [-var variable] [-seconds] [-reftime seconds]\n");
+    return (FALSE);
+  }
+
+  gettimeofday (&now, NULL);
+  if (SECONDS) {
+    if (varName) {
+      set_int_variable (varName, now.tv_sec - REFTIME);
+    } else {
+      gprint (GP_ERR, "%d\n", now.tv_sec - REFTIME);
+    }
+  } else {
+    ctime_r (&now.tv_sec, tstring);
+    N = strlen (tstring) - 1;
+    tstring[N] = 0;
+
+    if (varName) {
+      set_str_variable (varName, tstring);
+    } else {
+      gprint (GP_ERR, "%s\n", tstring);
+    }
+    free (tstring);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/dirname.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/dirname.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/dirname.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "basic.h"
+
+int dirname_opihi (int argc, char **argv) {
+
+  int N;
+  char *dirName, *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: dirname (path) [-var name]\n");
+    return (FALSE);
+  }
+
+  dirName = pathname (argv[1]);
+
+  if (varName == NULL) {
+    gprint (GP_LOG, "%s\n", dirName);
+  } else {
+    set_str_variable (varName, dirName);
+    free (varName);
+  }    
+
+  free (dirName);
+  return (TRUE);
+}
+
+// XXX need to add mode option
+// XXX need to respect umask (need umask command?)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/echo.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/echo.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/echo.c	(revision 22322)
@@ -0,0 +1,14 @@
+# include "basic.h"
+
+int echo (int argc, char **argv) {
+  
+  int i;
+
+  for (i = 1; i < argc - 1; i++) {
+    gprint (GP_LOG, "%s ", argv[i]);
+  }
+  if (argc >= 2) {
+      gprint (GP_LOG, "%s\n", argv[argc - 1]);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/file.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/file.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/file.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include "basic.h"
+
+int file (int argc, char **argv) {
+  
+  /* usage: file (filename) [var] */
+
+  int status, vstat;
+  struct stat fstats;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: file (filename) [var]\n");
+    return (FALSE);
+  }
+
+  status = stat (argv[1], &fstats);
+
+  vstat = !status;
+
+  if (argc == 3) {
+      
+    set_int_variable (argv[2], vstat);
+
+  } else {
+
+    gprint (GP_ERR, "file %s is ", argv[1]);
+    if (!vstat) gprint (GP_ERR, "not ");
+    gprint (GP_ERR, "found\n");
+    
+  }
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/fprintf.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/fprintf.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/fprintf.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "basic.h"
+# define NCHAR 1024
+
+// XXX this function should ALLOCATE the output buffers
+int fprintf_opihi (int argc, char **argv) {
+
+  int i;
+  char line[NCHAR], tmp[NCHAR], fmt[NCHAR];
+  char *p1, *p2, *q;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: fprintf format value value ...\n");
+    return (FALSE);
+  }
+
+  q  = line;
+  bzero (line, NCHAR);
+
+  p1 = argv[1];
+  for (i = 2; i < argc; i++) {
+    bzero (tmp, NCHAR);
+    bzero (fmt, NCHAR);
+
+    /* find next format character */
+    p2 = strchr (p1, '%');
+    if (p2 == (char *) NULL) {
+      gprint (GP_ERR, "mismatch between format and values\n");
+      return (FALSE);
+    }
+    if (strlen(q) + p2 - p1 > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, p1, p2-p1);
+    q = line + strlen(line);
+    
+    /* identify type (%NNNs %NNNNd %NNNNf) */
+    for (p1 = p2 + 1; (*p1 == '.') || (*p1 == '-') || (*p1 == '+') || (*p1 == ' ') || isdigit(*p1); p1++);
+    memcpy (fmt, p2, p1 - p2 + 1);
+    switch (*p1) {
+      case 'e':
+      case 'f':
+	sprintf (tmp, fmt, atof(argv[i]));
+	break;
+      case 's':
+	sprintf (tmp, fmt, argv[i]);
+	break;
+      case 'd':
+      case 'c':
+      case 'x':
+	sprintf (tmp, fmt, atoi(argv[i]));
+	break;
+      default:
+	gprint (GP_ERR, "syntax error in format (only e,f,s,d,c,x allowed)\n");
+	return (FALSE);
+    }
+    if (strlen(q) + strlen(tmp) > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, tmp, strlen(tmp));
+    q = line + strlen(line);
+    p1++;
+  }
+  p2 = strchr (p1, '%');
+  if (p2 != (char *) NULL) {
+    gprint (GP_ERR, "mismatch between format and values\n");
+    return (FALSE);
+  }
+  
+  p2 = p1 + strlen (p1);
+  if (strlen(q) + p2 - p1 > NCHAR) {
+    gprint (GP_ERR, "line too long");
+    return (FALSE);
+  }
+  memcpy (q, p1, p2-p1);
+  gprint (GP_LOG, "%s\n", line);
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/getchr.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/getchr.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/getchr.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "basic.h"
+
+int getchr_func (int argc, char **argv) {
+
+  /* returns position of the first given character */ 
+  char *c;
+  int pos;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: getchr (string) (char) [var]\n");
+    return (FALSE);
+  }
+
+  c = strchr (argv[1], argv[2][0]);
+
+  if (c == (char *) NULL) {
+    pos = -1;
+  } else {
+    pos = c - argv[1];
+  }
+
+  if (argc == 4) {
+    set_int_variable (argv[3], pos);
+  } else {
+    gprint (GP_ERR, "%d\n", pos);
+  }
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "basic.h"
+
+int help (int argc, char **argv) {
+
+  int Nbytes;
+  FILE *f;
+  char *helpdir, *file, buff[512];
+
+  helpdir = get_variable ("HELPDIR");
+  if (helpdir == (char *) NULL) {
+    gprint (GP_ERR, "variable HELPDIR not found\n");
+    return (FALSE);
+  }
+
+  if (argc == 1) {
+    sprintf (buff, "ls %s", helpdir);
+    system (buff);
+    return (TRUE);
+  }
+
+  Nbytes = strlen(helpdir) + strlen(argv[1]) + 2;
+  ALLOCATE (file, char, Nbytes);
+  snprintf (file, Nbytes, "%s/%s", helpdir, argv[1]);
+
+  f = fopen (file, "r");
+  free (file);
+
+  if (f == NULL) {
+    gprint (GP_ERR, "No help for: %s\n", argv[1]);
+    return (FALSE);
+  }
+
+  while (scan_line (f, buff) != EOF)
+    gprint (GP_LOG, "%s\n", buff);
+
+  fclose (f);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/!
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/!	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/!	(revision 22322)
@@ -0,0 +1,6 @@
+
+  exec (line)
+
+  perform a system call.  this can also be invoked with "!".  
+  don't use ! in a macro or input script as it is interpreted as a
+  comment character. 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/?
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/?	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/?	(revision 22322)
@@ -0,0 +1,6 @@
+
+  exec (line)
+
+  perform a system call.  this can also be invoked with "!".  
+  don't use ! in a macro or input script as it is interpreted as a
+  comment character. 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/Opihi
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/Opihi	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/Opihi	(revision 22322)
@@ -0,0 +1,5 @@
+
+
+    Opihi
+
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/Shell
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/Shell	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/Shell	(revision 22322)
@@ -0,0 +1,5 @@
+
+
+    Opihi
+
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/Variables
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/Variables	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/Variables	(revision 22322)
@@ -0,0 +1,17 @@
+
+  Opihi shell variables (scalars):
+
+  The Opihi shell can have variables which are designated by a word
+following a dollar sign: $foo.  Certain Mana function create and set
+variables (ie, stats, cursor).  You can also set a variable to the
+result of an arithmetic expression or just a number like this:
+
+  $foo = 10.0
+  $bar = 5*$foo + 0.6
+
+Mana will replace the variables on a line before executing a command.
+See also "Math" for more description of arithmetic operations.
+
+  See Also:  Math
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/break
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/break	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/break	(revision 22322)
@@ -0,0 +1,10 @@
+
+  break [-auto on/off]
+
+  halts macro, if, or for processes and returns to the basic prompt.
+
+  -auto: enables or disables DVO from automatically breaking
+   when it encounters an error
+
+  See also: macro, if, for
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/cd
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/cd	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/cd	(revision 22322)
@@ -0,0 +1,9 @@
+
+   cd (path)
+
+   change current working directory. 
+
+   Note that Kii or Kapa will be launched in the original 
+   directory, which means PS files created by Kii or Kapa 
+   will land in that directory (perhaps an unexpected result).
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/continue
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/continue	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/continue	(revision 22322)
@@ -0,0 +1,4 @@
+
+   continue
+
+   Return to the start of a for-loop.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/date
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/date	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/date	(revision 22322)
@@ -0,0 +1,38 @@
+
+  date [-var variable] [-seconds] [-reftime seconds]
+
+  Return the date and place it into (variable).
+
+  Default: print to window
+
+* by itself, just echoes the date & time
+
+* to set a variable with the date, add -var name, where 'name' is the
+  name of your variable.  by default, this is a human readable date,
+  eg Wed Mar 12 08:30:11 2008
+
+* to calculate time differences, add the -seconds flag, and the value
+  will be in seconds since 1970 Jan 01 00:00:00.  NOTE: There is a bug
+  in that the variable is represented as a float, and the value is now
+  > 1.2e9 seconds.  The difference is thus not very accurate.  To get
+  around this, use the -reftime feature to report the seconds since a
+  more recent date.  A value of something like 1205340000 works at the
+  moment.
+
+examples:
+
+  dvo: date
+  Wed Mar 12 08:30:11 2008
+
+  dvo: date -var foo -seconds
+  dvo: echo $foo
+  1205346664
+
+  dvo: date -var foo -seconds -reftime 1205340000
+  dvo: echo $foo
+  6700
+  dvo: date -var bar -seconds -reftime 1205340000
+  dvo: echo $bar
+  6708
+  dvo: echo {$bar - $foo}
+  8
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/echo
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/echo	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/echo	(revision 22322)
@@ -0,0 +1,10 @@
+
+  echo [anything]
+
+  prints the rest of the line, after variables and math expressions
+are parsed.  
+
+
+  See Also:  Opihi, math
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/exec
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/exec	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/exec	(revision 22322)
@@ -0,0 +1,6 @@
+
+   exec (shell command)
+
+   Allows a shell command to be run while running DVO
+   On the command line, this can also be done by placing a "!" before the shell
+    command
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/file
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/file	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/file	(revision 22322)
@@ -0,0 +1,9 @@
+
+   file (filename) (variable)
+
+   Checks to see if a file exists
+
+   If a variable is specified, then it is set to 1 if the file is present
+    and set to 0 if it is not.
+
+   Default: output printed to the window
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/for
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/for	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/for	(revision 22322)
@@ -0,0 +1,19 @@
+
+   for var (start) (stop) [delta]
+
+    perform a loop, with $var as the loop variable. 
+    the value of $var runs from (start) to (stop) inclusive, 
+    and is incremented by [delta] (1 by default).
+
+    The loop commands are entered until the appropriate "end" is typed;
+    if there are nested loops, macro definitions, or logical blocks,
+    the correct "end" is used!
+
+    for may be used at the command line, in a macro, or in an input
+    file with no ill effects.
+
+    the variable $var may be accessed during the loop execution.  if
+    the variable $var is assigned a value beyond (stop) during the
+    loop, the loop execution will end.
+
+    See also: macro, if
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/fprintf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/fprintf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/fprintf	(revision 22322)
@@ -0,0 +1,15 @@
+
+   fprintf ("format codes") (value1 value2 ...)
+
+   Prints output according to a specified format
+
+   Fomat codes must match the order and number of values.
+
+   FORMAT CODES:
+    %x.yf : floating point number with x total places and y places after
+             the decimal point; further decimal places are rounded
+    %x.ye : number in scientific notation with x places and y places after
+             the decimal point; further decimal places are rounded
+    %xs : ASCII output with x places
+    %xd : Integers with x places
+    %x : Express number in hexidesimal
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/getchr
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/getchr	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/getchr	(revision 22322)
@@ -0,0 +1,8 @@
+
+   getchr (string) (char) [variable]
+
+   Return the index of the string in the position of the specified character
+
+   Only the first instance of a character is returned.
+
+   If not found, the variable is set to -1.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/help
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/help	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/help	(revision 22322)
@@ -0,0 +1,7 @@
+
+   help (function)
+
+   print the contents of the help file.  
+   typing "help" without an argument will list the available help
+   files.  
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/if
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/if	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/if	(revision 22322)
@@ -0,0 +1,22 @@
+
+   if (logic)
+     (commands)
+   end
+
+   or:
+
+   if (logic)
+     (commands)
+   else 
+     (commnds)
+   end
+
+   logical block.  The commands are performed subject to the logical
+   condition.  The syntax is simplified C:  
+   examples: (x = 6), (x < 6) , (x ! 6), ((x = 6) | (x > 10))
+
+   there are no delimiter characters.
+
+   See also: for, macro
+
+   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/input
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/input	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/input	(revision 22322)
@@ -0,0 +1,15 @@
+
+   input (filename)
+
+   execute a series of commands given in the file.
+
+   Two special examples:  
+   ~/.manarc is loaded on startup, which give the user a place to
+   define personal macros, or interesting variables, etc.
+
+   If mana is called in the form "mana file", the file is loaded
+   (after .manarc) before the user is given the command-line prompt.
+   This can be used to invoke mana in a shell script, but care should
+   be taken in a shell-script that the file ends with the word "exit"
+   or "quit", or strange things may happen...
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/list
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/list	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/list	(revision 22322)
@@ -0,0 +1,23 @@
+
+   list (name) (string1) (string2) ... end
+   OR
+   list (name) -split (string1) (string2) ...
+   OR
+   list (name) -x "(ls command)"
+   OR
+   list (name) -x "cat (file)" 
+
+   Create a list of strings that are zero indexed.
+
+   $(name):n = the number of elements in the list (name)
+
+   $(name):5 = the 6th element of the list (name)
+
+   -split option: Turn a list of strings (separated by white spaces)
+     into a list
+
+   -x option: turn an ls command or file into a list (one list element per
+     line in the file + 1 white space)
+
+   ***Note: the list ... end version of the command does not currently work
+             within macros
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/local
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/local	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/local	(revision 22322)
@@ -0,0 +1,7 @@
+
+   local (var)
+
+   Creates a variable that is accessible only within the macro
+    in which it is created.
+
+   Variable value is listed as (macro).(var) using the "??" command
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/macro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/macro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/macro	(revision 22322)
@@ -0,0 +1,11 @@
+
+   macro (name)
+
+   create a macro.  the commands are entered until the word 'end' is
+   reached.  nested if, for, and macro definitions use the
+   appropriately matched occurances of the 'end'.
+
+   Within a macro, the command-line arguments are refered to as $1,
+   $2, etc.  The number of command-line arguments is given by $0.
+
+   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/memory
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/memory	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/memory	(revision 22322)
@@ -0,0 +1,12 @@
+
+  memory [x]
+
+  list the currently used memory.  This gives a (possibly very) long
+  listing of the size of every allocated piece of memory.  Placing
+  anything after the word "memory" gives a summary also.  This is
+  useful to see just how close you are to filling your computer's
+  entire memory (Does not include memory allocated to Kii!)
+
+  See Also: buffers
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/output
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/output	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/output	(revision 22322)
@@ -0,0 +1,10 @@
+
+  output (filename or "stdout")
+
+  redirect the output to a file (concat to the end of the file).  This
+  can also be done with the UNIX ">".  However, this command is useful
+  to send information to a file for a part of a Mana session.  For
+  example, you can save results of a "stats" command.
+
+  See Also: input, stats
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/pwd
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/pwd	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/pwd	(revision 22322)
@@ -0,0 +1,7 @@
+
+   pwd -var (variable)
+
+   print current working directory
+
+   Using -var will place the output into the specified variable.
+   Default: print to window
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/quit
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/quit	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/quit	(revision 22322)
@@ -0,0 +1,4 @@
+
+   quit
+
+   Exit from DVO
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/scan
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/scan	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/scan	(revision 22322)
@@ -0,0 +1,8 @@
+
+   scan (filename) (variable) [Nline]
+
+   Place the Nth line of the file into the named variable
+
+   Default N value: 1 (the 1st line)
+
+   The variable recieves the line + 1 white space
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/sleep
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/sleep	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/sleep	(revision 22322)
@@ -0,0 +1,4 @@
+
+   sleep (N)
+
+   Wait for (N) seconds before continuing
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/sprintf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/sprintf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/sprintf	(revision 22322)
@@ -0,0 +1,17 @@
+
+   sprintf (var) ("format codes") (value1 value2 ...)
+
+   Prints output according to a specified format
+
+   Works same as fprintf, except it places the output as a string into (var)
+
+   Fomat codes must match the order and number of values.
+
+   FORMAT CODES:
+    %x.yf : floating point number with x total places and y places after
+             the decimal point; further decimal places are rounded
+    %x.ye : number in scientific notation with x places and y places after
+             the decimal point; further decimal places are rounded
+    %xs : ASCII output with x places
+    %xd : Integers with x places
+    %x : Express number in hexidesimal
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/strlen
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/strlen	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/strlen	(revision 22322)
@@ -0,0 +1,10 @@
+
+   strlen (string) [var]
+
+   Determines the number of characters (plus white spaces) in a string
+    and places it into var
+
+   Default: print to window
+
+   Note: If the string has white spaces, you need to place it within
+    double-quotes.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/strpop
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/strpop	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/strpop	(revision 22322)
@@ -0,0 +1,12 @@
+
+   strpop (string) [var]
+
+   Pops part of a string or a list element that can be placed
+    into [var]
+
+   Default: print to window
+
+   The output is the first set of characters bordered by white space
+
+   ***Note: Both (string) and [var] must be entered without the "$"
+    variable identifier for the program to work properly.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/substr
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/substr	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/substr	(revision 22322)
@@ -0,0 +1,11 @@
+
+   substr (string) (index) (length) [var]
+
+   Returns part of a string starting at the position (index)
+    and of size (length), which can then be placed into [var]
+
+   Default: print to window
+
+   The sub-string is not removed from the original string
+
+   ***Note: Character positions in strings are zero-indexed
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/usleep
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/usleep	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/usleep	(revision 22322)
@@ -0,0 +1,4 @@
+
+   usleep (N)
+
+   Wait for (N) microseconds before continuing
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/wait
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/wait	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/wait	(revision 22322)
@@ -0,0 +1,5 @@
+
+  wait [anything]
+
+  wait echos the rest of the line and waits for <return> to be typed.
+Very useful for pausing the processing of an input file or a macro.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/which
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/which	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/which	(revision 22322)
@@ -0,0 +1,5 @@
+
+   which (command)
+
+   give a short summary info line on a command
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/while
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/while	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/help/while	(revision 22322)
@@ -0,0 +1,5 @@
+
+   while (condition) ... end
+
+   Perform a set of actions until the condition is true
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/init.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/init.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/init.c	(revision 22322)
@@ -0,0 +1,140 @@
+# include "basic.h"
+
+int basename_opihi  PROTO((int, char **));
+int config          PROTO((int, char **));
+int exec_sleep      PROTO((int, char **));
+int exec_usleep     PROTO((int, char **));
+int cd              PROTO((int, char **));
+int date            PROTO((int, char **));
+int dirname_opihi   PROTO((int, char **));
+int echo            PROTO((int, char **));
+int exec_last       PROTO((int, char **));
+int exec_next       PROTO((int, char **));
+int exec_break      PROTO((int, char **));
+int file            PROTO((int, char **));
+int getchr_func     PROTO((int, char **));
+int help            PROTO((int, char **));
+int input           PROTO((int, char **));
+int list            PROTO((int, char **));
+int list_help       PROTO((int, char **));
+int list_vars       PROTO((int, char **));
+int local           PROTO((int, char **)); /* data? */
+int macro           PROTO((int, char **));
+int memory          PROTO((int, char **));
+int mkdir_opihi     PROTO((int, char **));
+int module          PROTO((int, char **));
+int output          PROTO((int, char **));
+int pwd             PROTO((int, char **));
+int quit            PROTO((int, char **));
+int run_for         PROTO((int, char **));
+int run_if          PROTO((int, char **));
+int run_while       PROTO((int, char **));
+int scan            PROTO((int, char **));
+int shell           PROTO((int, char **));
+int sprintf_opihi   PROTO((int, char **));
+int fprintf_opihi   PROTO((int, char **));
+int strlen_func     PROTO((int, char **));
+int substr_func     PROTO((int, char **));
+int strpop          PROTO((int, char **));
+int strhash         PROTO((int, char **));
+int strsub          PROTO((int, char **));
+int wait_func       PROTO((int, char **));
+int which           PROTO((int, char **));
+
+/** mapping of the command names to command functions **/
+static Command cmds[] = {  
+  {1, "config",        config,             "(re)load config file?"},
+  {1, "sleep",         exec_sleep,         "sleep for N seconds"},
+  {1, "usleep",        exec_usleep,        "sleep for N microseconds"},
+  {1, "cd",            cd,                 "change directory"},
+  {1, "date",          date,               "get current date"},
+  {1, "basename",      basename_opihi,     "built-in basename function"},
+  {1, "dirname",       dirname_opihi,      "built-in dirname function"},
+  {1, "echo",          echo,               "type this line *"},
+  {1, "break",         exec_break,         "escape from function *"},
+  {1, "continue",      exec_next,          "next loop iteration"},
+  {1, "next",          exec_next,          "next loop iteration"},
+  {1, "last",          exec_last,          "last loop iteration"},
+  {1, "return",        exec_last,          "exit from macro"},
+  {1, "file",          file,               "test file existence"},
+  {1, "getchr",        getchr_func,        "find character in string"},
+  {1, "help",          help,               "get help on a function *"},
+  {1, "input",         input,              "read command lines from a file *"},
+  {1, "list",          list,               "get variable list"},
+  {1, "?",             list_help,          "list commands *"},
+  {1, "??",            list_vars,          "list variables *"},
+  {1, "local",         local,              "define local variables"},
+  {1, "macro",         macro,              "deal with the macros *"}, 
+  {1, "memory",        memory,             "long listing of the allocated memory"},
+  {1, "mkdir",         mkdir_opihi,        "built-in mkdir command"},
+  {1, "module",        module,             "load script file from the modules directories"},
+  {1, "output",        output,             "redirect output to file"},
+  {1, "pwd",           pwd,                "print current working directory"},
+  {1, "exit",          quit,               "exit program *"}, 
+  {1, "quit",          quit,               "exit program *"},
+  {1, "for",           run_for,            "for loop"}, 
+  {1, "if",            run_if,             "logical cases *"}, 
+  {1, "while",         run_while,          "while loop"}, 
+  {1, "scan",          scan,               "scan line from keyboard or file to variable *"},
+  {1, "!",             shell,              "system call"},
+  {1, "exec",          shell,              "system call"},
+  {1, "sprintf",       sprintf_opihi,      "formatted print to a variable"},
+  {1, "fprintf",       fprintf_opihi,      "formatted print to standard output"},
+  {1, "strlen",        strlen_func,        "string length"},
+  {1, "substr",        substr_func,        "substring"},
+  {1, "strhash",       strhash,            "generate a hash for a string"},
+  {1, "strpop",        strpop,             "pop a string"},
+  {1, "strsub",        strsub,             "replace instances of a key in a string"},
+  {1, "wait",          wait_func,          "wait until return is typed"},
+  {1, "which",         which,              "show command *"}
+};
+
+void InitBasic () {
+  
+  int i;
+
+  InitCommands ();
+  InitMacros ();
+  InitBuffers ();
+  InitVectors ();
+  InitVariables ();
+  InitLists ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+  
+}
+
+void InitBasic_PantasksClient () {
+  
+  int i;
+
+  InitCommands ();
+  InitMacros ();
+  InitBuffers ();
+  InitVectors ();
+  InitVariables ();
+  InitLists ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    if (!strcmp (cmds[i].name, "quit")) goto valid;
+    if (!strcmp (cmds[i].name, "exit")) goto valid;
+    if (!strcmp (cmds[i].name, "exec")) goto valid;
+    if (!strcmp (cmds[i].name, "!")) goto valid;
+    continue;
+
+  valid:
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreeBasic () {
+
+  FreeCommands ();
+  FreeMacros ();
+  FreeBuffers ();
+  FreeVectors ();
+  FreeVariables ();
+  FreeLists ();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/input.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/input.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/input.c	(revision 22322)
@@ -0,0 +1,53 @@
+# include "basic.h"
+# define D_NLINES 100
+
+int input (int argc, char **argv) {
+  
+  int i, NLINES, status;
+  FILE *infile;
+  Macro inlist;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: input <filename>\n");
+    return (FALSE);
+  }
+
+  infile = fopen (argv[1], "r");
+  if (infile == NULL) {
+    gprint (GP_ERR, "no file %s\n", argv[1]); 
+    return (FALSE);
+  }
+
+  /* read file into the current list */
+  NLINES = D_NLINES;
+  ALLOCATE (inlist.line, char *, NLINES);
+  ALLOCATE (inlist.line[0], char, 1024);
+  for (i = 0; (scan_line (infile, inlist.line[i]) != EOF);) {
+    stripwhite (inlist.line[i]);
+    if (inlist.line[i][0] == 0) continue;
+    if (inlist.line[i][0] == '#') continue;
+    if (inlist.line[i][0] == '!') continue;
+
+    REALLOCATE (inlist.line[i], char, strlen(inlist.line[i]) + 1);
+    if (i == NLINES - 1) {
+      NLINES += D_NLINES;
+      REALLOCATE (inlist.line, char *, NLINES)
+    }
+    i++;
+    ALLOCATE (inlist.line[i], char, 1024);
+  }
+  inlist.Nlines = i;
+  fclose (infile);
+
+  /* process this list */
+  status = exec_loop (&inlist);
+
+  /* cleanup list */
+  for (i = 0; i < inlist.Nlines; i++) {
+    free (inlist.line[i]);
+  }
+  free (inlist.line[i]); /* note that we always alloc one extra line */
+  free (inlist.line);
+  return (status);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/list.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/list.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/list.c	(revision 22322)
@@ -0,0 +1,201 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int list (int argc, char **argv) {
+
+  int ThisList, depth, i, done, found;
+  char *input, line[1024];
+  int N, Nbytes, NBYTES, Nread, status;
+  int RunCommand;
+  char *A, *B, *val, *Cmd;
+  FILE *f;
+
+  Cmd = NULL;
+  RunCommand = FALSE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    Cmd = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    RunCommand = TRUE;
+  }
+
+  // return an error if -add is given with no other args
+  if ((argc > 2) && (!strcmp (argv[2], "-split"))) {
+    if (argc == 3) {
+      gprint (GP_ERR, "USAGE: list (root) -split (word) (word) ...\n");
+      return (FALSE);
+    }
+    
+    for (i = 0; i < argc - 3; i++) {
+      sprintf (line, "%s:%d", argv[1], i);
+      set_str_variable (line, argv[i+3]);
+    }
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, i);
+
+    return (TRUE);
+  }
+
+  // return an error if -add is given with no other args
+  if ((argc > 2) && (!strcmp (argv[2], "-add"))) {
+    if (argc == 3) {
+      gprint (GP_ERR, "USAGE: list (root) -add (word) (word) ...\n");
+      return (FALSE);
+    }
+    
+    sprintf (line, "%s:n", argv[1]);
+    N = get_int_variable (line, &found);
+    for (i = 0; i < argc - 3; i++) {
+      sprintf (line, "%s:%d", argv[1], N + i);
+      set_str_variable (line, argv[i+3]);
+    }
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, N + i);
+
+    return (TRUE);
+  }
+
+  // remove the single named entry from the list (finds entry with given name, reduces list length by one)
+  // return an error if -add is given with no other args
+  if ((argc > 2) && (!strcmp (argv[2], "-del"))) {
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: list (root) -del (word)\n");
+      return (FALSE);
+    }
+    
+    int j;
+    char *value, *next_value;
+    char line2[1024];
+
+    sprintf (line, "%s:n", argv[1]);
+    N = get_int_variable (line, &found);
+    for (i = 0; i < N; i++) {
+      sprintf (line, "%s:%d", argv[1], i);
+      value = get_variable (line);
+      if (value == NULL) continue;
+      if (!strcmp (value, argv[3])) {
+	free (value);
+	for (j = i + 1; j < N; j++) {
+	  sprintf (line2, "%s:%d", argv[1], j);
+	  next_value = get_variable (line2);
+	  set_str_variable (line, next_value);
+	  strcpy (line, line2);
+	}
+	DeleteNamedScalar (line);
+	sprintf (line2, "%s:n", argv[1]);
+	set_int_variable (line2, N - 1);
+	return (TRUE);
+      }
+      free (value);
+    }      
+    gprint (GP_ERR, "value %s not found in list\n", argv[3]);
+    return (FALSE);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: list (root)                : supply list data, terminate with 'END'\n");
+    gprint (GP_ERR, "USAGE: list (root) -x (command)   : create list from shell output\n");
+    gprint (GP_ERR, "USAGE: list (root) -split (words) : create list from words\n");
+    gprint (GP_ERR, "USAGE: list (root) -add (words)   : extend a list\n");
+    return (FALSE);
+  }
+
+  if (RunCommand) {
+    
+    /* val will hold the result */
+    NBYTES = 1024;
+    ALLOCATE (val, char, NBYTES);
+    
+    /* need to loop until command produces no more output, 
+       REALLOCATING as needed. */
+    f = popen (Cmd, "r");
+    done = FALSE;
+    Nbytes = 0;
+    while (!done) {
+      Nread = fread (&val[Nbytes], 1, 1023, f);
+      if (Nread < 0) { 
+	gprint (GP_ERR, "error reading from command\n");
+	done = TRUE;
+      }
+      if (Nread > 0) {
+	Nbytes += Nread;
+	NBYTES = 1024 + Nbytes;
+	REALLOCATE (val, char, NBYTES);
+      }
+      if (Nread == 0) {
+	done = TRUE;
+      }
+      
+    }
+    val[Nbytes] = 0;
+    status = pclose (f);
+    free (Cmd);
+    
+    if (status) {
+      gprint (GP_ERR, "warning: exit status of command %d\n", status);
+    }
+      
+    A = B = val;
+    for (i = 0; B != (char *) NULL;) {
+      while (isspace (*A) && (*A != 0)) A++;
+      B = strchr (A, '\n');
+      if (B != (char *) NULL) { *B = 0; }
+      if (*A != 0) {
+	sprintf (line, "%s:%d", argv[1], i);
+	set_str_variable (line, A);
+	A = B + 1;
+	i++;
+      }
+    }      
+    free (val);
+    
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, i);
+    return (TRUE);
+  }
+
+  /* read in loop */
+  depth = 0;
+  ThisList = current_list_depth();
+  for (i = 0, done = FALSE; !done; ) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) {
+      input = readline (prompt);
+    } else {
+      input = get_next_listentry (ThisList);
+    }
+
+    if ((ThisList == 0) && (input == NULL)) {
+      gprint (GP_ERR, "end list with 'END'\n");
+      continue;
+    }
+    if ((ThisList >  0) && (input == NULL)) {
+      gprint (GP_ERR, "missing 'END' in list\n");
+      input = strcreate ("end");
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    stripwhite (input);
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { /* we hit the last "END", loop is done */
+	sprintf (line, "%s:n", argv[1]);
+	set_int_variable (line, i);
+	free (input);
+	return (TRUE);
+      }
+    }
+
+    if (*input) { 
+      sprintf (line, "%s:%d", argv[1], i);
+      set_str_variable (line, input);
+      free (input);
+      i++;
+   }
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/list_help.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/list_help.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/list_help.c	(revision 22322)
@@ -0,0 +1,24 @@
+# include "basic.h"
+
+int list_help (int argc, char **argv) {
+
+  FILE *f;
+  int fd;
+  char filename[128], line[256];
+
+  sprintf (filename, "/tmp/status.XXXXXX");
+  if ((fd = mkstemp (filename)) == -1) {
+    gprint (GP_ERR, "error opening output\n");
+    return (FALSE);
+  }
+  f = fdopen (fd, "w");
+  if (f == (FILE *) NULL) f = stdout;
+  print_commands (f);
+  if (f != stdout) {
+    fclose (f);
+    sprintf (line, "more %s", filename);
+    system (line);
+  }
+  unlink (filename);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/list_vars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/list_vars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/list_vars.c	(revision 22322)
@@ -0,0 +1,8 @@
+# include "basic.h"
+
+int list_vars (int argc, char **argv) {
+
+  ListVariables ();
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/local.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/local.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/local.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include "basic.h"
+
+int local (int argc, char **argv) {
+
+  int i, N, STATIC;
+  char *p;
+
+  /* create a variable named MacroDepth.argv[1] */
+
+  STATIC = FALSE;
+  if ((N = get_argument (argc, argv, "-static"))) {
+    remove_argument (N, &argc, argv);
+    STATIC = TRUE;
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: local (variable)\n");
+    return (FALSE);
+  }
+  
+  for (i = 1; i < argc; i++) {
+    if (STATIC) {
+      p = get_local_variable_ptr (argv[i]);
+      if (p == NULL) {
+	set_local_variable (argv[i], "NULL");
+      }
+    } else {
+      set_local_variable (argv[i], "NULL");
+    }      
+  }
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/macro.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/macro.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/macro.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "basic.h"
+
+int macro (int argc, char **argv) {
+
+  int status;
+  CommandF *cmd;
+
+  if ((argc != 2) && (argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: macro (cmd)\n");
+    gprint (GP_ERR, "  (cmd) can be one of:\n");
+    gprint (GP_ERR, "    (name)         -- create macro (name)\n");
+    gprint (GP_ERR, "    create (name)  -- create macro (name)\n");
+    gprint (GP_ERR, "    delete (name)  -- delete macro (name)\n");
+    gprint (GP_ERR, "    list   (name)  -- list macro (name)\n");
+    gprint (GP_ERR, "    edit   (name)  -- edit macro (name) <not working yet!> *\n");
+    gprint (GP_ERR, "    read   (name)  -- read macro(s) from file (name) <not working yet!> *\n");
+    gprint (GP_ERR, "    write  (name)  -- write macro (name) to a file <not working yet!> *\n");
+    return (FALSE);
+  }
+
+  cmd = find_macro_command (argv[1]);
+  if (cmd != NULL) {
+    status = (*cmd) (argc - 1, argv + 1);
+  } else {
+    /* sub-command was not found, pass argv[1..N] to macro_create */
+    status = macro_create (argc, argv);
+  }
+
+  return (status);
+}
+
+/* macro is called with the command "macro".  
+   the command line word "macro" is meant to be followed the one of several 
+   possible options:
+   
+   macro create
+   macro delete
+   macro list
+   macro edit
+   macro read
+   macro write
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/memory.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/memory.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/memory.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "basic.h"
+
+// XXX add a warning if ohana_memory is not compiled in 
+int memory (int argc, char **argv) {
+  
+  if (argc < 2) goto usage;
+
+  if (!strcasecmp (argv[1], "all")) {
+    ohana_memdump (1);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "leaks")) {
+    ohana_memdump (0);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "check")) {
+    ohana_memcheck (0);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "checkfree")) {
+    ohana_memcheck (1);
+    return (TRUE);
+  }
+  if (!strncasecmp ("variables", argv[1], strlen(argv[1]))) {
+    ListVariables ();
+    return (TRUE);
+  }
+  if (!strncasecmp ("vectors", argv[1], strlen(argv[1]))) {
+    ListVectors ();
+    return (TRUE);
+  }
+  if (!strncasecmp ("buffers", argv[1], strlen(argv[1]))) {
+    PrintBuffers (0);
+    return (TRUE);
+  }
+  if (!strncasecmp ("macros", argv[1], strlen(argv[1]))) {
+    ListMacros();
+    return (TRUE);
+  }
+  if (!strncasecmp ("commands", argv[1], strlen(argv[1]))) {
+    print_commands (stderr);
+    return (TRUE);
+  }
+
+usage:
+  gprint (GP_ERR, "USAGE: memory (all/leaks)\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/mkdir.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/mkdir.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/mkdir.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "basic.h"
+
+int mkdir_opihi (int argc, char **argv) {
+
+  int mode, status;
+  struct stat fstats;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: mkdir (path)\n");
+    return (FALSE);
+  }
+
+  mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
+
+  status = stat (argv[1], &fstats);
+  if (!status) {
+    // argv[1] exists, is it a directory?
+    if (!S_ISDIR(fstats.st_mode)) {
+      gprint (GP_ERR, "cannot create directory %s: is an existing file\n", argv[1]);
+      return (FALSE);
+    }
+    return (TRUE);
+  }
+
+  status = mkdirhier (argv[1], mode);
+  if (status == -1) {
+    gprint (GP_ERR, "cannot create directory %s\n", argv[1]);
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+// XXX need to add mode option
+// XXX need to respect umask (need umask command?)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/module.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/module.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/module.c	(revision 22322)
@@ -0,0 +1,77 @@
+# include "basic.h"
+# define D_NLINES 100
+
+/* module loads an opihi script files from the installed module tree */
+int module (int argc, char **argv) {
+  
+  int i, NLINES, Nmodules, Nbytes, status;
+  Macro inlist;
+  char modname[16], *modpath, *filename;
+  FILE *infile = NULL;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: module <filename>\n");
+    return (FALSE);
+  }
+
+  Nmodules = get_int_variable ("MODULES:n", &status);
+  if (!status) {
+    gprint (GP_ERR, "MODULES list not found\n");
+    return (FALSE);
+  }
+
+  /* search for requested file in MODULES:0 - MODULES:n */
+  for (i = 0; i < Nmodules; i++) {
+    snprintf (modname, 16, "MODULES:%d", i);
+    modpath = get_variable (modname);
+    if (modpath == NULL) {
+      gprint (GP_ERR, "MODULES list element %d not found\n", i);
+      return (FALSE);
+    }
+
+    Nbytes = strlen(modpath) + strlen(argv[1]) + 2;
+    ALLOCATE (filename, char, Nbytes);
+    snprintf (filename, Nbytes, "%s/%s", modpath, argv[1]);
+    
+    infile = fopen (filename, "r");
+    free (filename);
+
+    if (infile != NULL) break;
+  }
+  if (infile == NULL) {
+    gprint (GP_ERR, "module %s not found\n", argv[1]); 
+    return (FALSE);
+  }
+    
+  /* read file into the current list */
+  NLINES = D_NLINES;
+  ALLOCATE (inlist.line, char *, NLINES);
+  ALLOCATE (inlist.line[0], char, 1024);
+  for (i = 0; (scan_line (infile, inlist.line[i]) != EOF);) {
+    stripwhite (inlist.line[i]);
+    if (inlist.line[i][0] == 0) continue;
+    if (inlist.line[i][0] == '#') continue;
+    if (inlist.line[i][0] == '!') continue;
+
+    REALLOCATE (inlist.line[i], char, strlen(inlist.line[i]) + 1);
+    if (i == NLINES - 1) {
+      NLINES += D_NLINES;
+      REALLOCATE (inlist.line, char *, NLINES)
+    }
+    i++;
+    ALLOCATE (inlist.line[i], char, 1024);
+  }
+  inlist.Nlines = i;
+  fclose (infile);
+
+  /* process this list */
+  status = exec_loop (&inlist);
+
+  /* cleanup list */
+  for (i = 0; i < inlist.Nlines; i++) {
+    free (inlist.line[i]);
+  }
+  free (inlist.line[i]); /* note that we always alloc one extra line */
+  free (inlist.line);
+  return (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/output.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/output.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/output.c	(revision 22322)
@@ -0,0 +1,58 @@
+# include "basic.h"
+
+int output (int argc, char **argv) {
+  
+  int N, Noutput;
+  gpDest dest;
+  IOBuffer *buffer;
+  char *output, *current;
+
+  dest = GP_LOG;
+  if ((N = get_argument (argc, argv, "-err"))) {
+    dest = GP_ERR;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-current"))) {
+    current = gprintGetName (dest);
+    remove_argument (N, &argc, argv);
+    set_str_variable (argv[N], current);
+    remove_argument (N, &argc, argv);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-buffer"))) {
+    remove_argument (N, &argc, argv);
+    gprintSetBuffer (dest);
+    return (TRUE);
+  }
+    
+  /* set the output target and dump the current buffer there */
+  if ((N = get_argument (argc, argv, "-dump"))) {
+    buffer = gprintGetBuffer (dest);
+    if (buffer == NULL) return (FALSE);
+
+    /* save the current buffer contents */
+    Noutput = buffer[0].Nbuffer;
+    ALLOCATE (output, char, Noutput);
+    memcpy (output, buffer[0].buffer, Noutput);
+    
+    /* set the output target to the specified name */
+    remove_argument (N, &argc, argv);
+    gprintSetFileAllThreads (dest, argv[N]);
+    remove_argument (N, &argc, argv);
+
+    /* send the output to the appropriate destination */
+    gwrite (output, 1, Noutput, dest);
+    free (output);
+    return (TRUE);
+  }
+    
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: output <filename> [-err] [-buffer] [-current var] [-dump filename]\n");
+    return (FALSE);
+  }
+
+  gprintSetFileAllThreads (dest, argv[1]);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/quit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/quit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/quit.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "basic.h"
+
+int quit (int argc, char **argv) {
+
+  int state;
+
+  cleanup ();
+
+  state = 0;
+  if (argc > 1) {
+    state = atof (argv[1]);
+  } 
+
+// the libedit version of readline does not support an incremental write to history file
+# ifndef RL_READLINE_VERSION
+  history = get_variable("HISTORY");
+  if (history != NULL) write_history (history);
+# endif
+
+  exit (state);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/run_for.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/run_for.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/run_for.c	(revision 22322)
@@ -0,0 +1,127 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int run_for (int argc, char **argv) {
+
+  int ThisList, depth, i, done, status, found, NLINES, j;
+  double start, end, delta;
+  char *input;
+  double value, sign;
+  Macro loop;
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: for (var) (start) (stop) [delta] -- terminate with 'END'\n");
+    return (FALSE);
+  }
+
+  start = atof (argv[2]);
+  end   = atof (argv[3]);
+  delta = 1.0;
+  if (argc == 5) delta = atof (argv[4]);
+  sign = SIGN(delta);
+
+  NLINES = D_NLINES;
+  ALLOCATE (loop.line, char *, NLINES);
+
+  /* read in loop */
+  depth = 0;
+  ThisList = current_list_depth();
+  for (i = 0, done = FALSE; !done; ) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+    stripwhite (input);
+
+    /* check for end-of-data syntax error */
+    if (input == NULL) {
+      if (ThisList == 0)  {
+	gprint (GP_ERR, "end loop with 'END'\n");
+	continue;
+      } else {
+	gprint (GP_ERR, "misbalanced loop\n");
+	for (j = 0; j < loop.Nlines; j++) {
+	  free (loop.line[j]);
+	}
+	free (loop.line);
+	return (FALSE);
+      }	
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    /* test for new macro (or other list, in the future?) */
+    if (is_list (input)) depth ++;
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { 
+	free (input);
+	break;
+      }
+    }
+
+    /* if line has data, add to loop list */
+    if (*input) { 
+      loop.line[i] = input;
+      i++;
+      if (i == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (loop.line, char *, NLINES);
+      }
+    }
+  }
+
+  /* cleanup loop data */
+  loop.Nlines = i;
+  REALLOCATE (loop.line, char *, MAX (loop.Nlines, 1));
+
+  status = TRUE;
+  interrupt = FALSE;
+  for (value = start; (sign*value < sign*end) && !interrupt; value += delta) {
+    if ((int)value == value) 
+      set_int_variable (argv[1], (int) value);
+    else 
+      set_variable (argv[1], value);
+    status = exec_loop (&loop);
+    value = get_double_variable (argv[1], &found);
+    if (loop_next) continue;
+    if (loop_last) break;
+    if (loop_break) break;
+  }
+  /* 'last' and 'next' should only affect one loop */
+  loop_last = loop_next = FALSE; 
+
+  /* break should propagate up if auto_break is set */
+  loop_break = FALSE;
+  if (auto_break && !status) loop_break = TRUE;
+
+  /* cleanup list */
+  for (j = 0; j < loop.Nlines; j++) {
+    free (loop.line[j]);
+  }
+  free (loop.line);
+
+  if (loop_break) return (FALSE);
+  return (TRUE);
+}
+
+/*
+  If we are entering at the keyboard (ThisList == 0), use readline.
+  Otherwise, read from the current list, remove list lines.
+  execute when we hit the final "END" (the true END -- we count macro defines!!) 
+*/
+     
+/* while processing the loop, the loop status variables may be set
+   by the loop commands, or the loop may quite on a failed command,
+   setting the exec_loop return status to false. the loop status variables
+   are:
+   loop_next : stop this loop, but try another loop
+   loop_last : stop loop processing, but return true so external loop may continue
+   loop_break : stop loop processing, and return false so external loop will break
+   interrupt : external interrupt signal
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/run_if.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/run_if.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/run_if.c	(revision 22322)
@@ -0,0 +1,127 @@
+# include "basic.h"
+# define prompt    "if: "
+
+int run_if (int argc, char **argv) {
+
+  int ThisList, depth, done, status, InlineCommand;
+  int i, length, logic;
+  char *input, *val, *line;
+  int nloop, size;
+
+  InlineCommand = FALSE;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: if (conditional) : follow with commands, end with the word 'END'\n");
+    gprint (GP_ERR, "   OR: if (conditional) command\n");
+    return (FALSE);
+  }
+  if (argc > 2) {
+    InlineCommand = TRUE;
+  }
+
+  /* determine value of conditional expression */
+  val = dvomath (1, &argv[1], &size, 0);
+  if (val == NULL) {
+    gprint (GP_ERR, "syntax error in logic: %s\n", argv[1]);
+    return (FALSE);
+  }
+  logic = atof (val); /* is round-off error a danger? */ 
+  free (val);
+
+  if (InlineCommand) {
+    if (logic) {
+      /* re-build a command line from the remaining strings */
+      length = 0;
+      for (i = 2; i < argc; i++) {
+	length += strlen(argv[i]) + 1;
+      }
+      length++;
+      ALLOCATE (line, char, length);
+      memset (line, 0, length);
+      for (i = 2; i < argc; i++) {
+	if (i == 2) {
+	  strcpy (line, argv[i]);
+	} else {
+	  strcat (line, " ");
+	  strcat (line, argv[i]);
+	}
+      }
+      status = multicommand (line);
+      free (line);
+      return (status);
+    } else {
+      return (TRUE);
+    }
+  }    
+
+  /* read in if-list */
+  nloop = 0;
+  depth = 0;
+  ThisList = current_list_depth();
+
+  done = FALSE;
+  while (!done) {
+
+    nloop ++;
+    /* get the next line (from correct place) */
+    if (ThisList == 0) {
+      input = readline (prompt);
+    } else {
+      input = get_next_listentry (ThisList);
+    }
+
+    if ((ThisList == 0) && (input == NULL)) {
+      gprint (GP_ERR, "end if-block with 'END'\n");
+      continue;
+    }
+    if ((ThisList >  0) && (input == NULL)) {
+      gprint (GP_ERR, "missing 'END' in if-block\n");
+      input = strcreate ("end");
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    stripwhite (input);
+
+    /* test for new macro, search for "end" statement */
+    if (!logic && is_list (input)) {
+      depth ++;
+      free (input);
+      continue;
+    }
+    
+    /* check for an "else", invert logic */
+    if ((depth == 0) && !strncasecmp (input, "ELSE", 4)) {
+      logic ^= TRUE;
+      free (input);
+      continue;
+    }
+
+    /* test for end of nested block -- if not nested, END refers to this if */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { 
+	/* we hit the last "END", if-block is done */
+	free (input);
+	return (TRUE);	
+      }
+      free (input);  /* a do-nothing line */
+      continue;
+    }
+
+    if (logic) {
+      if (*input) { 
+	status = multicommand (input);
+	if (ThisList == 0) add_history (input);
+	if (auto_break && !status) return (FALSE);
+      }
+    } 
+    free (input);
+  }
+  return (TRUE);
+}
+
+/*
+     If we are entering at the keyboard (ThisList == 0), use readline.
+     Otherwise, read from the current list, remove list lines.
+     End when we hit the final "END" (the true END -- we count macro defines!!) 
+     */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/run_while.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/run_while.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/run_while.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int run_while (int argc, char **argv) {
+
+  int ThisList, depth, i, done, status, NLINES, j;
+  char *input, *val, *logic_line;
+  int logic, size;
+  Macro loop;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: while (condition)\n");
+    return (FALSE);
+  }
+
+  NLINES = D_NLINES;
+  ALLOCATE (loop.line, char *, NLINES);
+
+  /* read in loop */
+  depth = 0;
+  ThisList = current_list_depth();
+  for (i = 0, done = FALSE; !done; ) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+    stripwhite (input);
+
+    /* check for end-of-data syntax error */
+    if (input == (char *) NULL) {
+      if (ThisList == 0)  {
+	gprint (GP_ERR, "end 'while' loop with 'END'\n");
+	continue;
+      } else {
+	gprint (GP_ERR, "misbalanced 'while' loop\n");
+	for (j = 0; j < loop.Nlines; j++) {
+	  free (loop.line[j]);
+	}
+	free (loop.line);
+	return (FALSE);
+      }	
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    /* test for new macro (or other list, in the future?) */
+    if (is_list (input)) depth ++;
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) break;
+    }
+
+    /* if line has data, add to loop list */
+    if (*input) { 
+      loop.line[i] = input;
+      i++;
+      if (i == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (loop.line, char *, NLINES);
+      }
+    }
+  }
+
+  /* cleanup loop data */
+  free (input);
+  loop.Nlines = i;
+  REALLOCATE (loop.line, char *, MAX (loop.Nlines, 1));
+
+  // test the logic once before running the loop
+  logic_line = strcreate (argv[1]);
+  logic_line = expand_vars (logic_line);
+  val = dvomath (1, &logic_line, &size, 0);
+  free (logic_line);
+
+  // if we have a parse failure, return FALSE
+  if (val == NULL) return (FALSE);
+  logic = atof (val); /* warning: round-off error is a danger */
+  free (val);
+
+  /* execute for loop */
+  while (logic) { 
+    status = exec_loop (&loop);
+    if (loop_next) continue;
+    if (loop_last) break;
+    if (loop_break) break;
+
+    logic_line = strcreate (argv[1]);
+    logic_line = expand_vars (logic_line);
+    val = dvomath (1, &logic_line, &size, 0);
+    free (logic_line);
+
+    logic = FALSE;
+    if (val) {
+      logic = atof (val); /* warning: round-off error is a danger */
+      free (val);
+    }
+  }
+  /* 'last' and 'next' should only affect one loop */
+  loop_last = loop_next = FALSE; 
+
+  /* break should propagate up if auto_break is set */
+  loop_break = FALSE;
+  if (auto_break && !status) loop_break = TRUE;
+
+  /* cleanup list */
+  for (j = 0; j < loop.Nlines; j++) {
+    free (loop.line[j]);
+  }
+  free (loop.line);
+
+  if (loop_break) return (FALSE);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/scan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/scan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/scan.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "basic.h"
+
+int scan (int argc, char **argv) {
+
+  int i, N, status;
+  char *line;
+  FILE *f;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: scan <filename> <var> [N]\n");
+    return (FALSE);
+  }
+
+  f = stdin;
+  if (strcmp (argv[1], "stdin")) {
+    f = fopen (argv[1], "r");
+    if (f == (FILE *) NULL) {
+      gprint (GP_ERR, "file %s not found\n", argv[1]);
+      return (FALSE);
+    }
+  }
+  
+  ALLOCATE (line, char, 1024);
+  N = 1;
+  if (argc == 4) {
+    N = atof(argv[3]);
+    if (N < 1) {
+      gprint (GP_ERR, "scan: line numbers must start at 1\n");
+      return (FALSE);
+    }
+  }
+
+  for (i = 0; (i < N) && ((status = scan_line (f, line)) != EOF); i++);
+  if (i < N) {
+    set_str_variable (argv[2], "EOF");
+  } else {
+    set_str_variable (argv[2], line);
+  }
+  free (line);
+
+  if (f != stdin) {
+    fclose (f);
+  }
+  return (TRUE);
+ 
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/shell.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/shell.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/shell.c	(revision 22322)
@@ -0,0 +1,129 @@
+# include "basic.h"
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+static char *defshell = "/bin/sh";
+static char *cmdflag = "-c";
+
+// XXX add the ability to exec without a separate shell
+// XXX add an option to modify the timeout
+
+int shell (int argc, char **argv) {
+  
+  int i, pid, N;
+  int exit_status;
+  int wait_status;
+  int result, length;
+  char **args, *shell;
+  struct timeval start, now;
+  float timeout;
+
+  timeout = 0;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    remove_argument (N, &argc, argv);
+    timeout = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  shell = getenv ("SHELL");
+  if (shell == NULL) shell = defshell;
+
+  // we are creating a command of the form /bin/sh -c argv[1] argv[2] etc, where
+  // the argv[1], etc elements are concatenated into a single string
+  ALLOCATE (args, char *, 4);
+  args[0] = shell;
+  args[1] = cmdflag;
+
+  length = 0;
+  for (i = 1; i < argc; i++) {
+    length += strlen(argv[i]) + 1;
+  }
+  
+  ALLOCATE (args[2], char, length);
+  args[2][0] = 0;
+  for (i = 1; i < argc; i++) {
+    strcat (args[2], argv[i]);
+    if (i < argc - 1) strcat (args[2], " ");
+  }
+  args[3] = NULL;
+
+  // send the commands to the shell specified in the env variable SHELL, or else /bin/sh
+
+  gettimeofday (&start, NULL);
+
+  // use execvp to enable a timeout on the system call 
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    execvp (shell, args);
+    exit (1);
+  }
+  free (args[2]);
+  free (args);
+  
+  // wait for process to finish or timeout
+  // loop forever if desired, but catch C-C and stop the process on interrupt
+  interrupt = FALSE;
+  while (!interrupt) {
+    result = waitpid (pid, &wait_status, WNOHANG);
+    switch (result) {
+      case -1:   // error on waitpid
+	switch (errno) {
+	  case ECHILD:
+	    gprint (GP_ERR, "unknown PID, not a child process: %d\n", pid);
+	    return (FALSE);
+	  default:
+	    gprint (GP_ERR, "unexpected response to waitpid: %d\n", result);
+	    abort();
+	}
+	break;
+
+      case 0:   // child not yet exited
+	usleep (10000);
+	if (timeout > 0.0) {
+	  gettimeofday (&now, NULL);
+	  if (DTIME(now, start) > timeout) {
+	    gprint (GP_ERR, "timeout on %s (pid %d)\n", argv[1], pid);
+	    return (FALSE);
+	  }
+	}
+	continue;
+
+      default:
+	if (result != pid) {
+	  gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+	  abort();
+	}
+	if (WIFEXITED(wait_status)) {
+	  exit_status = WEXITSTATUS(wait_status);
+	  if (exit_status) {
+	    return FALSE;
+	  } else {
+	    return TRUE;
+	  }
+	}
+	if (WIFSIGNALED(wait_status)) {
+	  gprint (GP_ERR, "job %d exited on signal %d\n", pid, WTERMSIG(wait_status));
+	  return (FALSE);
+	}
+	if (WIFSTOPPED(wait_status)) {
+	  gprint (GP_ERR, "waitpid returns 'stopped' programming error\n");
+	  abort();
+	}
+    }
+  }
+  gprint (GP_ERR, "caught interrupt, killing %s (%d)\n", argv[1], pid);
+
+  // user hit interrupt: kill the process and return
+  kill (pid, SIGKILL);
+  result = 0;
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for job to exit
+    result = waitpid (pid, &wait_status, WNOHANG);
+  }
+  if (!result) {
+    gprint (GP_ERR, "trouble killing %s (pid %d)\n", argv[1], pid);
+  } else {
+    gprint (GP_ERR, "killed %s (pid %d)\n", argv[1], pid);
+  }
+
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/sleep.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/sleep.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/sleep.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "basic.h"
+
+int exec_sleep (int argc, char **argv) {
+
+  int i;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "usage: sleep N\n");
+    return (FALSE);
+  }
+
+  i = atof (argv[1]);
+  sleep (i);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/sprintf.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/sprintf.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/sprintf.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "basic.h"
+# define NCHAR 1024
+
+// XXX this function uses fixed string lengths....
+/* convert line, tmp, fmt to dynamic strings? */
+
+int sprintf_opihi (int argc, char **argv) {
+
+  int i;
+  char line[NCHAR], tmp[NCHAR], fmt[NCHAR];
+  char *p1, *p2, *q;
+
+  if (argc < 3) {
+    gprint (GP_ERR, "USAGE: sprintf var format value value ...\n");
+    return (FALSE);
+  }
+
+  q  = line;
+  bzero (line, NCHAR);
+
+  p1 = argv[2];
+  for (i = 3; i < argc; i++) {
+    bzero (tmp, NCHAR);
+    bzero (fmt, NCHAR);
+
+    /* find next format character */
+    p2 = strchr (p1, '%');
+    if (p2 == (char *) NULL) {
+      gprint (GP_ERR, "mismatch between format and values\n");
+      return (FALSE);
+    }
+    if (strlen(q) + p2 - p1 > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, p1, p2-p1);
+    q = line + strlen(line);
+    
+    /* identify type (%NNNs %NNNNd %NNNNf) */
+    for (p1 = p2 + 1; (*p1 == '.') || (*p1 == '-') || (*p1 == '+') || (*p1 == ' ') || isdigit(*p1); p1++);
+    memcpy (fmt, p2, p1 - p2 + 1);
+    switch (*p1) {
+      case 'e':
+      case 'f':
+	sprintf (tmp, fmt, atof(argv[i]));
+	break;
+      case 's':
+	sprintf (tmp, fmt, argv[i]);
+	break;
+      case 'd':
+      case 'c':
+      case 'x':
+	sprintf (tmp, fmt, atoi(argv[i]));
+	break;
+      default:
+	gprint (GP_ERR, "syntax error in format (only e,f,s,d,c,x allowed)\n");
+	return (FALSE);
+    }
+    if (strlen(q) + strlen(tmp) > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, tmp, strlen(tmp));
+    q = line + strlen(line);
+    p1++;
+  }
+  p2 = strchr (p1, '%');
+  if (p2 != (char *) NULL) {
+    gprint (GP_ERR, "mismatch between format and values\n");
+    return (FALSE);
+  }
+  
+  p2 = p1 + strlen (p1);
+  if (strlen(q) + p2 - p1 > NCHAR) {
+    gprint (GP_ERR, "line too long");
+    return (FALSE);
+  }
+  memcpy (q, p1, p2-p1);
+  set_str_variable (argv[1], line);
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strchr.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strchr.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strchr.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "basic.h"
+
+int getchr_func (int argc, char **argv) {
+
+  /* returns position of the first given character */ 
+  char *c;
+  int pos;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: strchr (string) (char) [var]\n");
+    return (FALSE);
+  }
+
+  c = strchr (argv[1], argv[2][0]);
+
+  if (c == (char *) NULL) {
+    pos = -1;
+  } else {
+    pos = c - argv[1];
+  }
+
+  if (argc == 5) {
+    set_variable (argv[3], pos);
+  } else {
+    gprint (GP_ERR, "%d\n", pos);
+  }
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strhash.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strhash.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strhash.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "basic.h"
+
+int strhash (int argc, char **argv) {
+
+  int i, N, Nchar, Nsum, modulus, value;
+  char *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: strhash (string) (modulus)\n");
+    return (FALSE);
+  }
+
+  modulus = atoi(argv[2]);
+  if (modulus > 255) {
+    gprint (GP_ERR, "for the moment, (modulus) is limited to 255\n");
+    return (FALSE);
+  }
+
+  Nchar = strlen (argv[1]);
+
+  Nsum = 0;
+  for (i = 0; i < Nchar; i++) {
+    Nsum += (argv[1][i] % modulus);
+  }
+  
+  value = Nsum % modulus;
+  
+  if (varName) {
+    set_int_variable (varName, value);
+  } else {
+    gprint (GP_LOG, "%d\n", value);
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strlen.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strlen.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strlen.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "basic.h"
+
+int strlen_func (int argc, char **argv) {
+
+  /* returns length of the given string */ 
+  int len;
+
+  if ((argc != 2) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: strlen (string) [var]\n");
+    return (FALSE);
+  }
+
+  len = strlen (argv[1]);
+
+  if (argc == 3) {
+    set_int_variable (argv[2], len);
+  } else {
+    gprint (GP_ERR, "%d\n", len);
+  }
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strpop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strpop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strpop.c	(revision 22322)
@@ -0,0 +1,35 @@
+# include "basic.h"
+
+int strpop (int argc, char **argv) {
+
+  char *p, *q, *string;
+
+  if ((argc != 2) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: strpop (var) [out]\n");
+    return (FALSE);
+  }
+
+  /* string is a copy of the value on the variable stack */
+  string = get_variable (argv[1]);
+  if (string == NULL) return (FALSE);
+  
+  /* thisword is an allocated string */
+  p = thisword (string);
+
+  q = nextword (string);
+  if (q == NULL) {
+    set_str_variable (argv[1], "NULL");
+  } else {
+    set_str_variable (argv[1], q);
+  }
+  
+  if (argc == 3) {
+    set_str_variable (argv[2], p);
+  } else {
+    gprint (GP_LOG, "%s\n", p);
+  }
+
+  free (p);
+  free (string);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strsub.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strsub.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/strsub.c	(revision 22322)
@@ -0,0 +1,59 @@
+# include "basic.h"
+
+int strsub (int argc, char **argv) {
+
+  int N, Nkey, Noutput;
+  char *varName;
+  char *p, *q, *input, *output, *key, *value;
+
+  // clear all sections
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: strsub (string) (key) (value) [-var out]\n");
+    gprint (GP_ERR, "  replace instances of 'key' with the 'value'\n");
+    return (FALSE);
+  }
+
+  // input string 
+  input = argv[1];
+  
+  // find this in the string
+  key = argv[2];
+  Nkey = strlen(key);
+
+  // replace with this
+  value = argv[3];
+
+  Noutput = MAX (16, strlen(input));
+  ALLOCATE (output, char, Noutput);
+  memset (output, 0, Noutput);
+
+  // 0123456789
+  // wordKEYnew
+  // p = 0, q = 4, q-p = 4 -> p = 7
+
+  p = input;
+  while (*p && ((q = strstr (p, key)) != NULL)) {
+    output = opihi_append (output, &Noutput, p, q);
+    output = opihi_append (output, &Noutput, value, value + strlen(value));
+    p = q + Nkey;
+  }
+  if (*p) {
+    output = opihi_append (output, &Noutput, p, p + strlen(p));
+  }
+  
+  if (varName) {
+    set_str_variable (varName, output);
+  } else {
+    gprint (GP_LOG, "%s\n", output);
+  }
+
+  free (output);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/substr.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/substr.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/substr.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "basic.h"
+
+int substr_func (int argc, char **argv) {
+
+  int N1, N2, len;
+  char *c, *string;
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: substr (string) N1 N2 [var]\n");
+    return (FALSE);
+  }
+
+  N1 = atof (argv[2]);
+  N2 = atof (argv[3]);
+
+  // add a range check here
+  if ((N1 < 0) || (N1 >=  strlen(argv[1]))) {
+      gprint (GP_ERR, "ERROR: N1 out of range\n");
+      return (FALSE);
+  }
+  if ((N2 < 0) || ((N2+N1) >  strlen(argv[1]))) {
+      gprint (GP_ERR, "ERROR: N2 out of range\n");
+      return (FALSE);
+  }
+
+  len = strlen (argv[1]);
+  if ((N1 >= len) || (N1 + N2 > len)) {
+    c = (char *) NULL;
+  } else {
+    c = strncreate (&argv[1][N1], N2);
+  }
+
+  if (c == (char *) NULL) {
+    string = strcreate ("");
+  } else {
+    string = strcreate (c);
+  }
+
+  if (argc == 5) {
+    set_str_variable (argv[4], string);
+  } else {
+    gprint (GP_ERR, "%s\n", string);
+  }
+  free (c);
+  free (string);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/break.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/break.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/break.sh	(revision 22322)
@@ -0,0 +1,19 @@
+
+list tests
+ test1
+end
+
+# test that break will halt operation
+macro test1
+ $PASS = 1
+ break -auto off
+ for i 0 10
+   if ($i == 5)
+     break
+   end
+ end
+ if ($i != 5)
+   $PASS = 0
+ end
+end
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/cd.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/cd.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/cd.sh	(revision 22322)
@@ -0,0 +1,34 @@
+
+list tests
+ test1
+ test2
+end
+
+# test that cd will go into a new directory
+macro test1
+ $PASS = 1
+ exec mkdir test.dir
+ output /dev/null
+ cd test.dir
+ exec touch foo.test
+ cd ..
+ output stdout
+ file test.dir/foo.test exists
+ if ($exists != 1)
+   $PASS = 0
+ end
+ exec rm -f test.dir/foo.test
+ exec rmdir test.dir
+end
+
+# test that pwd output is correct
+macro test2
+ $PASS = 1
+ exec touch foo.test
+ pwd -var testdir
+ file $testdir\/foo.test exists
+ if ($exists != 1)
+  $PASS = 0
+ end
+ exec rm foo.test
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/config.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/config.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/config.sh	(revision 22322)
@@ -0,0 +1,14 @@
+
+list tests
+ test1
+end
+
+# test that config does not return an error
+macro test1
+ $PASS = 1
+ config
+ if ($STATUS == 0)
+   $PASS = 0
+ end
+end
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/continue.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/continue.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/continue.sh	(revision 22322)
@@ -0,0 +1,18 @@
+
+list tests
+ test1
+end
+
+# test that continue skips within a loop
+macro test1
+ $PASS = 1
+ for i 0 10
+   if ($i > 5)
+      continue
+   end
+   $j = $i
+ end
+ if ($j != 5)
+   $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/date.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/date.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/date.sh	(revision 22322)
@@ -0,0 +1,33 @@
+
+list tests
+ test1
+ test2
+end
+
+# test that date does not return an error
+macro test1
+ $PASS = 1
+ date -var var
+ if ($STATUS == 0)
+   $PASS = 0
+ end
+end
+
+# test that date constructs a reasonable sample date
+macro test2
+ $PASS = 1
+ date -var date1
+ $date2 = `date`
+ list w1 -split $date1
+ list w2 -split $date2
+ # check the first 3 entries (day, month, date)
+ if ($w1:0 != $w2:0)
+   $PASS = 0
+ end
+ if ($w1:1 != $w2:1)
+   $PASS = 0
+ end
+ if ($w1:2 != $w2:2)
+   $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/echo.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/echo.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/echo.sh	(revision 22322)
@@ -0,0 +1,19 @@
+
+list tests
+ test1
+end
+
+# test that echo actually echoes
+macro test1
+ $PASS = 1
+ exec rm -f test.dat
+ output test.dat
+ echo foobar
+ output stdout
+ $line = `cat test.dat`
+ # exec rm -f test.dat
+ if ($line != foobar)
+   $PASS = 0
+ end
+end
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/file.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/file.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/file.sh	(revision 22322)
@@ -0,0 +1,19 @@
+
+list tests
+ test1
+end
+
+# test that the file test function works at all
+macro test1
+ $PASS = 1
+ exec touch foo.test
+ file foo.test exists
+ if ($exists != 1)
+   $PASS = 0
+ end
+ exec rm -f foo.test
+ file foo.test exists
+ if ($exists != 0)
+   $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/for.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/for.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/for.sh	(revision 22322)
@@ -0,0 +1,216 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ memtest1
+ memtest2
+ memtest3
+end
+
+# do we loop up correctly?
+macro test1
+
+  $PASS = 0
+
+  local i
+
+  for i 0 100
+  end    
+  
+ if ($i == 99)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+
+end
+
+# do we loop down correctly?
+macro test2
+
+  $PASS = 0
+
+  local i
+
+  for i 100 0 -1
+  end    
+  
+ if ($i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i : $i"
+ end
+
+end
+
+# do we loop up in small steps correctly?
+macro test3
+
+  $PASS = 0
+
+  local i N
+
+  $N = 0
+  for i 0 100 0.1
+   $N = $N + 1
+  end    
+  
+ if (($i == 99.9) && ($N == 1000))
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+   echo "N: $N"
+ end
+
+end
+
+# do we loop down in small steps correctly?
+macro test4
+
+  $PASS = 0
+
+  local i N
+
+  $N = 0
+  for i 100 0 -0.1
+   $N = $N + 1
+  end    
+  
+ if (($i == 0.1) && ($N == 1000))
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+   echo "N: $N"
+ end
+
+end
+
+# do we break from a loop correctly
+macro test5
+
+  $PASS = 0
+
+  break -auto off
+
+  local i N
+
+  $N = 0
+  for i 0 100
+   $N = $N + 1
+   if ($i == 30)
+     break
+   end
+  end    
+
+  $PASS = 1
+  
+  if (($i != 30) || ($N != 31))
+    $PASS = 0
+    echo "i: $i"
+    echo "N: $N"
+  end
+end
+
+# check memleaks
+macro memtest1
+
+ $PASS = 0
+
+ local i N
+
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks with many loop lines
+macro memtest2
+
+ $PASS = 0
+
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+  echo "test line in loop"
+  echo "test line in loop"
+  echo "test line in loop"
+  echo "test line in loop"
+  echo "test line in loop"
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks on break
+macro memtest3
+
+ $PASS = 0
+
+ local i N
+
+ break -auto off
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  for j 0 5
+    break
+    echo "test line in loop"
+    echo "test line in loop"
+    echo "test line in loop"
+    echo "test line in loop"
+    echo "test line in loop"
+  end
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($i != 9999)
+   $PASS = 0
+   echo "break jumped outer loop: i = $i" 
+ end
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/fprintf.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/fprintf.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/fprintf.sh	(revision 22322)
@@ -0,0 +1,84 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+end
+
+macro test1
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %03d" 50
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test 050")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+macro test2
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %6.3f" 123.45678
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test 123.457")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+macro test3
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %x" 32
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test 20")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+macro test4
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %10s" foobar
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test     foobar")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+# check for memory leaks
+macro test5
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 1000
+   fprintf "test %10s" foobar
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ($endmem - $startmem < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo growth: {$endmem - $startmem}
+   echo kB/loop: {($endmem - $startmem)/1000}
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/getchr.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/getchr.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/getchr.sh	(revision 22322)
@@ -0,0 +1,23 @@
+
+list tests
+ test1
+ test2
+end
+
+# index test
+macro test1
+ $PASS = 1
+ getchr "a long string.string" . var
+ if ($var != 13)
+   $PASS = 0
+ end
+end
+
+# null test
+macro test2
+ $PASS = 1
+ getchr "a long string.string" x var
+ if ($var != -1)
+   $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/if.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/if.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/if.sh	(revision 22322)
@@ -0,0 +1,321 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+ test8
+ test9
+ test10
+ test11
+ test12
+ test13a
+ test13
+ test14
+end
+
+# basic logical test
+macro test1
+
+ local a
+
+ $a = 1
+
+ if ($a == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test2
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a == 1) && ($b == 2))
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test3
+
+ local a b
+
+ $a = 1
+ $b = 5
+
+ if (($a == 1) && ($b < 10))
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test4
+
+ local a
+
+ $a = 0
+
+ if ($a == 1)
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test5
+
+ local a
+
+ $a = 1
+
+ if ($a > 0)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test6
+
+ local a
+
+ $a = 1
+
+ if ($a < 2)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test7
+
+ local a
+
+ $a = test
+
+ if ("$a" == "test")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test8
+
+ local a
+
+ $a = foobar
+
+ if ("$a" == "test")
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test9
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a == 0) || ($b == 2))
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test10
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a == 0) || ($b == 1))
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test11
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a <= 0) || ($b >= 3))
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test12
+
+ local a
+
+ $a = 1
+
+ if ($a != 1)
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# check memleaks
+macro test13a
+
+ local a b i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ for i 0 1000
+   if ($a == 1)
+    echo "run"    
+   end
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
+
+# check memleaks
+macro test13
+
+ local a b i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ $b = 2
+ for i 0 1000
+   if (($a == 1) && ($b == 2))
+    echo "run"    
+   end
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
+
+# memory test using continue
+macro test14
+
+ local a b i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ $b = 2
+ for i 0 1000
+   if (($a == 1) && ($b == 2))
+    continue
+    echo "run"    
+   end
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
+
+# memory test using continue
+macro test15
+
+ local a b i N bool
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ $b = 2
+ for i 0 1000
+   $bool = (($a == 1) && ($b == 2))
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/list.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/list.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/list.sh	(revision 22322)
@@ -0,0 +1,174 @@
+list tests
+ test1
+ test2
+ test3
+ test4
+ memtest2
+ memtest3
+ memtest4
+end
+
+list check
+  this
+  is
+  a
+  list
+end
+
+# Does list work?
+macro test1
+ $PASS = 1
+ if ($check:n != 4)
+  $PASS = 0
+  echo "Number of list elements: $check:n"
+ end
+ if (("$check:0" != "this") || ("$check:1" != "is") || ("$check:2" != "a") || ("$check:3" != "list"))
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+end
+
+# Test split option
+macro test2
+ $PASS = 1
+ list check2 -split This is a list
+ if ($check2:n != 4)
+  $PASS = 0
+  echo "Number of list elements: $check2:n"
+ end
+ if (("$check2:0" != "This") || ("$check2:1" != "is") || ("$check2:2" != "a") || ("$check2:3" != "list"))
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+end
+
+# Test -x option for the ls command
+macro test3
+ $PASS = 1
+ list check3 -x "ls /dev/null"
+ if ($check3:n != 1)
+  $PASS = 0
+  echo "Number of list elements: $check3:n"
+ end
+ if ("$check3:0" != "/dev/null")
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+end
+
+# Test -x for files
+macro test4
+ $PASS = 1
+ file list_test.txt fchk
+ if ($fchk)
+  exec rm list_test.txt
+ end
+ output list_test.txt
+ echo This
+ echo is
+ echo a
+ echo list
+ output stdout
+ list check4 -x "cat list_test.txt"
+ if ($check4:n != 4)
+  $PASS = 0
+  echo "Number of list elements: $check4:n"
+ end
+ if (("$check4:0" != "This") || ("$check4:1" != "is") || ("$check4:2" != "a") || ("$check4:3" != "list"))
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+ if ($PASS)
+  exec rm list_test.txt
+ end
+end
+
+# Memory test for list -split
+macro memtest2
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  list check2 -split This is a list
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
+
+# Memory test for list -x
+macro memtest3
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  list check3 -x "ls /dev/null"
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+
+end
+
+# Memory test for list -x
+macro memtest4
+
+ file list_test.txt fchk
+ if ($fchk)
+  exec rm list_test.txt
+ end
+ output list_test.txt
+ echo This
+ echo is
+ echo a
+ echo list
+ output stdout
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  list check4 -x "cat list_test.txt"
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+
+ if ($PASS)
+  exec rm list_test.txt
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/local.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/local.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/local.sh	(revision 22322)
@@ -0,0 +1,51 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does local work?
+macro test1
+
+ $PASS = 1
+
+ local lvar
+
+ $lvar = 5
+
+ if ($?lvar != 1)
+  $PASS = 0
+  echo "Local variable failed to be created!"
+ end
+
+ if ($lvar != 5)
+  $PASS = 0
+  echo "Local variable value not assigned!"
+ end
+
+end
+
+
+# Memory test for local
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  local lvar2
+  $lvar2 = 9
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/macro.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/macro.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/macro.sh	(revision 22322)
@@ -0,0 +1,48 @@
+list tests
+ test_prep
+ test1
+ memtest1
+end
+
+# Is the macro working?
+macro test_prep
+ $test_var1 = check1
+ $test_var2 = check2
+ $var_count = $0
+end
+
+macro test1
+ $PASS = 1
+ $var_count = 0
+ $test_var1 = blank
+ $test_var2 = blank
+ test_prep var1 var2 var3
+ if ($var_count != 4)
+  $PASS = 0
+  echo "Number of parameters (should be 4): $var_count"
+ end
+ if (("$test_var1" != "check1") || ("$test_var2" != "check2"))
+  $PASS = 0
+  echo "Paramaters not assigned correctly!: $test_var1 $test_var2"
+ end
+end
+
+# Memory Test for macro
+macro memtest1
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  test_prep
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/math.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/math.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/math.sh	(revision 22322)
@@ -0,0 +1,149 @@
+
+list tests
+ test1
+ test2
+ testmem1
+ testmem2
+ testmem3
+ testmem4
+end
+
+# test subtraction
+macro test1
+
+  $PASS = 1
+  local i
+
+  exec rm -f tmp.txt
+  output tmp.txt
+  echo {99 - 98}
+  output stdout
+
+  $i = `cat tmp.txt` 
+ 
+ if ($i != 1)
+   $PASS = 0
+   echo "i : $i"
+ end
+
+end
+
+# test addition, division, and multiplication
+macro test2
+
+ $PASS = 1
+ local a b c
+
+ $a = {2 + 4}
+ $b = {12 / 2}
+ $c = {2 * 3}
+
+ if (($a != 6) || ($b != 6) || ($c != 6))
+   $PASS = 0
+   echo "ALL VALUES NOT 6: a=$a\ b=$b\ c=$c\"
+ end
+
+end
+
+# check memleaks (set global)
+macro testmem1
+
+ $PASS = 1
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   echo {99 - 98}
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set global)
+macro testmem2
+
+ $PASS = 1
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $N = 99 - 98
+   $N = 1 + 1
+   $N = 2 * 2
+   $N = 22/7
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set global)
+macro testmem3
+
+ $PASS = 1
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $N = 99
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set global)
+macro testmem4
+
+ $PASS = 1
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $N = word
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/module.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/module.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/module.sh	(revision 22322)
@@ -0,0 +1,16 @@
+
+list tests
+ test1
+end
+
+# Test if the $MODULES list is available
+macro test1
+
+ $PASS = 1
+
+ if ($?MODULES:0 != 1)
+  $PASS = 0
+  echo "Modules list not loaded!"
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/output.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/output.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/output.sh	(revision 22322)
@@ -0,0 +1,63 @@
+
+list tests
+ test1
+ testmem1
+end
+
+# test subtraction
+macro test1
+
+ $PASS = 1
+
+ output testout.txt
+
+ file testout.txt fchk
+ output stdout
+ if ($fchk != 1)
+  $PASS = 0
+  echo "Output did not create test file!"
+ end
+
+ output testout.txt
+ echo "This is a test."
+ output stdout
+ $line = `cat testout.txt`
+ if ("$line" == "This is a test.")
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "Output: $line"
+ end
+
+ exec rm testout.txt
+
+end
+
+
+# check memleaks
+macro testmem1
+
+ $PASS = 1
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 100
+  output testout.txt
+  echo "This is a test."
+  output stdout
+  exec rm testout.txt
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/scan.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/scan.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/scan.sh	(revision 22322)
@@ -0,0 +1,117 @@
+list tests
+ test1
+ memtest1
+ memtest2
+end
+
+# Does scan work?
+macro test1
+ $PASS = 1
+ file test_file.txt fchk
+ if ($fchk)
+  exec rm test_file.txt
+ end
+ output test_file.txt
+ echo This
+ echo is
+ echo a
+ echo test
+ echo file
+ output stdout
+ scan test_file.txt fscan
+ if ("$fscan" != "This")
+  $PASS = 0
+  echo "Default not working!"
+ end
+ scan test_file.txt fscan 4
+ if ("$fscan" != "test")
+  $PASS = 0
+  echo "Scan failure!"
+ end
+ if ($PASS)
+  exec rm test_file.txt
+ end
+end
+
+# Memory test for scan (default)
+macro memtest1
+
+ file test_file.txt fchk
+ if ($fchk)
+  exec rm test_file.txt
+ end
+ output test_file.txt
+ echo This
+ echo is
+ echo a
+ echo test
+ echo file
+ output stdout
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  scan test_file.txt fscan
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+ if ($PASS)
+  exec rm test_file.txt
+ end
+
+end
+
+
+# Memory test for scan (specified)
+macro memtest2
+
+ file test_file.txt fchk
+ if ($fchk)
+  exec rm test_file.txt
+ end
+ output test_file.txt
+ echo This
+ echo is
+ echo a
+ echo test
+ echo file
+ output stdout
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  scan test_file.txt fscan 5
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+ if ($PASS)
+  exec rm test_file.txt
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/shell.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/shell.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/shell.sh	(revision 22322)
@@ -0,0 +1,35 @@
+
+list tests
+ test1
+ test2
+end
+
+# test that a shell function works at all
+macro test1
+ $PASS = 1
+ exec touch foo.test
+ file foo.test exists
+ if ($exists != 1)
+   $PASS = 0
+ end
+ exec rm -f foo.test
+ file foo.test exists
+ if ($exists != 0)
+   $PASS = 0
+ end
+end
+
+# test that the shell status is returned
+macro test2
+ $PASS = 1
+ exec touch foo.test
+ exec ls foo.test >& /dev/null
+ if ($STATUS != 1)
+   $PASS = 0
+ end
+ exec rm -f foo.test
+ exec ls foo.test >& /dev/null
+ if ($STATUS != 0)
+   $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/sleep.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/sleep.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/sleep.sh	(revision 22322)
@@ -0,0 +1,25 @@
+list tests
+ test1
+end
+
+# Does sleep work?
+macro test1
+ $PASS = 1
+
+ list tstart -x "date +%S"
+
+ sleep 3
+
+ list tend -x "date +%S"
+
+ if ($tstart:0 >= 57)
+  $tend:0 = $tend:0 + 60
+ end
+
+ $diff = abs (3 - abs($tstart:0 - $tend:0))
+
+ if ($diff > 1.1)
+  $PASS = 0
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/sprintf.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/sprintf.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/sprintf.sh	(revision 22322)
@@ -0,0 +1,43 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does sprintf work?
+macro test1
+ $PASS = 1
+
+ local test_var
+
+ sprint test_var "%7s %5.2f %9.3e" float 34.5 12630000
+
+ if ("$test_var" != "  float 34.50 1.263e+07")
+  $PASS = 0
+ end
+
+end
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  sprint test_var "%7s %5.2f %9.3e" float 34.5 12630000
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/strlen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/strlen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/strlen.sh	(revision 22322)
@@ -0,0 +1,47 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does strlen work?
+macro test1
+ 
+ $PASS = 1
+
+ $tstr = "Test string"
+
+ strlen $tstr len
+
+ if ($len != 11)
+  $PASS = 0
+  echo "Incorrect length: $len"
+ end
+
+end
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ $tstr = "Test string"
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  strlen $tstr len
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/strpop.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/strpop.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/strpop.sh	(revision 22322)
@@ -0,0 +1,105 @@
+list tests
+ test1
+ test2
+ memtest1
+ memtest2
+end
+
+# Does strpop work with variables?
+macro test1
+
+ $PASS = 1
+
+ $tstr = a test string
+
+ local a b c
+
+ strpop tstr a
+ strpop tstr b
+ strpop tstr c
+
+ if (("$a" != "a") || ("$b" != "test") || ("$c" != "string"))
+  $PASS = 0
+  echo "Incorrect value returned!"
+ end
+end
+
+
+# Does strpop work with lists?
+macro test2
+
+ $PASS = 1
+
+ list tlis -split list of strings
+
+ local a b c
+
+ strpop tlis:0 a
+ strpop tlis:1 b
+ strpop tlis:2 c
+
+ if (("$a" != "list") || ("$b" != "of") || ("$c" != "strings"))
+  $PASS = 0
+  echo "Incorrect value returned!"
+ end
+end
+
+
+# Memory test for variables
+macro memtest1
+
+ local i tstr a b c d
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  $tstr = one two three
+  strlen tstr a
+  strlen tstr b
+  strlen tstr c
+  strlen tstr d
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
+
+
+# Memory test for lists
+macro memtest2
+
+ local i tstr a b c d
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  list tstr -split one two three
+  strlen tstr:0 a
+  strlen tstr:1 b
+  strlen tstr:2 c
+  strlen tstr:0 d
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/substr.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/substr.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/substr.sh	(revision 22322)
@@ -0,0 +1,49 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does substr work?
+macro test1
+
+ $PASS = 1
+
+ local tstr ss
+
+ $tstr = "riddle me this"
+
+ substr $tstr 4 8 ss
+
+ if ("$ss" != "le me th")
+  $PASS = 0
+  echo "Incorrect substring returned!"
+ end
+end
+
+
+# Memory test
+macro memtest1
+
+ local i tstr ss
+
+ $tstr = "riddle me this"
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  substr $tstr 4 8 ss
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/test.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/test.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/test.dat	(revision 22322)
@@ -0,0 +1,1 @@
+test     foobar
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/usleep.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/usleep.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/usleep.sh	(revision 22322)
@@ -0,0 +1,25 @@
+list tests
+ test1
+end
+
+# Does usleep work?
+macro test1
+ $PASS = 1
+
+ list tstart -x "date +%S"
+
+ usleep 3000000
+
+ list tend -x "date +%S"
+
+ if ($tstart:0 >= 57)
+  $tend:0 = $tend:0 + 60
+ end
+
+ $diff = abs (3 - abs($tstart:0 - $tend:0))
+
+ if ($diff > 1.1)
+  $PASS = 0
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/variable.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/variable.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/variable.sh	(revision 22322)
@@ -0,0 +1,506 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+ test8
+ test9
+ test10
+ test11
+ test12
+ test13
+ test14
+ testmem1
+ testmem2
+ testmem3
+ testmem4
+ testmem5
+ testmem6
+ testmem7
+ testmem8
+ testmem9
+ testmem10
+end
+
+# do we set variables correctly?
+macro test1
+
+ local i
+
+ $i = 99
+  
+ if ($i == 99)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+
+end
+
+# do math expressions assign the correct value (test2 -> test8)
+macro test2
+
+  local i
+
+  $i = 99 - 98
+  
+ if ($i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i : $i"
+ end
+
+end
+
+macro test3
+
+  local i
+
+  $i = 2*3
+  
+ if ($i == 6)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+macro test4
+
+  local i
+
+  $i = 2^3
+
+ if ($i == 8)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+macro test5
+
+  local i
+
+  $i = 2+3
+
+ if ($i == 5)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+macro test6
+
+  local i
+
+  $i = 6/3
+
+ if ($i == 2)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+# testing operation priority
+macro test7
+
+  local i j
+
+  $i = 2 - 3*2
+  $j = 10/2*5+5
+
+ if (($i == -4) && ($j == 30))
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i\ j: $j"
+ end
+
+end
+
+# testing math on negative numbers
+macro test8
+
+  local i
+
+  $i = -2 - -3
+
+ if ($i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+# testing the existance of variables
+macro test9
+
+  local i
+
+  $i = 0
+
+ if ($?i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+end
+
+# test increment
+macro test10
+
+  local i N
+
+  $N = 0
+  for i 0 100
+    $N ++
+  end
+
+  if ($N == 100)
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "N: $N"
+ end
+end
+
+# test decrement
+macro test11
+
+  local i N
+
+  $N = 100
+  for i 0 100
+    $N --
+  end
+
+  if ($N == 0)
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "N: $N"
+ end
+end
+
+# test command assign
+macro test12
+
+  local i N
+
+  $N = `ls -dF /etc`
+
+  if ("$N" == "/etc/")
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "N: $N"
+ end
+end
+
+# test vector assign
+macro test13
+
+  local i N
+
+  create v1 0 100
+  v1[5] = 10
+  set v2 = v1 + 20
+
+  if (v2[5] == 30)
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "v1\[5\]: v1[5]"
+   echo "v2\[5\]: v2[5]"
+ end
+end
+
+# test local variables
+macro test14
+
+ $PASS = 1
+
+ # a slightly weak test: depends on no prior macros defining variables
+ # with the names below
+ if (($?very_obscure_name == 1) || ($?another_obscure_name == 1))
+  $PASS = 0
+ end
+end
+
+# check memleaks (set global)
+macro testmem1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   $Nvar = 10
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set string)
+macro testmem2
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $Nvar = test line
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set double)
+macro testmem3
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   $Nvar = 5.212
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set local)
+macro testmem4
+
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   $N = 10
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (existence)
+macro testmem5
+
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   echo $?N
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (get variable)
+macro testmem6
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ $Nvar = 5
+ output /dev/null
+ for i 0 10000
+   echo $Nvar
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (increment)
+macro testmem7
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ $Nvar = 0
+ output /dev/null
+ for i 0 10000
+   $Nvar ++
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (decrement)
+macro testmem8
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ $Nvar = 10000
+ output /dev/null
+ for i 0 10000
+   $Nvar --
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (command)
+macro testmem9
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 100
+   $Nvar = `ls -d /etc`
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
+
+# check memleaks (vector assign)
+macro testmem10
+
+ local i
+
+ create v1 0 100
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   v1[5] = $i
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/while.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/while.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/test/while.sh	(revision 22322)
@@ -0,0 +1,19 @@
+list tests
+ test1
+end
+
+# Does while work?
+macro test1
+ $PASS = 1
+ local i
+ $i = 0
+ while ($i <= 10)
+  if ($i == 11)
+   $PASS = 0
+   echo "While loop failure!"
+   echo "i: $i"
+   break
+  end
+  $i++
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/usleep.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/usleep.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/usleep.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "basic.h"
+
+int exec_usleep (int argc, char **argv) {
+
+  int i;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "usage: usleep N\n");
+    return (FALSE);
+  }
+
+  i = atof (argv[1]);
+  usleep (i);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/wait.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/wait.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/wait.c	(revision 22322)
@@ -0,0 +1,14 @@
+# include "basic.h"
+
+int wait_func (int argc, char **argv) {
+
+  char buff[1024];
+  int i;
+
+  for (i = 1; i < argc; i++) {
+    gprint (GP_ERR, "%s ", argv[i]);
+  }
+  gprint (GP_ERR, "\n");
+  scan_line (stdin, buff);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/which.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/which.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.basic/which.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "basic.h"
+
+int which (int argc, char **argv) {
+
+  Command *cmd;
+ 
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: which <filename>\n");
+    return (FALSE);
+  }
+
+  cmd = MatchCommand (argv[1], TRUE, TRUE);
+  if (cmd == NULL) return (FALSE);
+
+  gprint (GP_ERR, "%-25s -- %s\n", cmd[0].name, cmd[0].help);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/Makefile	(revision 22322)
@@ -0,0 +1,160 @@
+default: libdatacmd
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/cmd.data
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+DATA    =       $(DESTDATA)/mana
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# data user commands ########################
+srcs = \
+$(SRC)/init.$(ARCH).o          \
+$(SRC)/accum.$(ARCH).o		\
+$(SRC)/applyfit2d.$(ARCH).o	\
+$(SRC)/applyfit.$(ARCH).o	\
+$(SRC)/box.$(ARCH).o		\
+$(SRC)/book.$(ARCH).o		\
+$(SRC)/book_commands.$(ARCH).o	\
+$(SRC)/center.$(ARCH).o	\
+$(SRC)/clear.$(ARCH).o		\
+$(SRC)/clip.$(ARCH).o		\
+$(SRC)/close.$(ARCH).o		\
+$(SRC)/concat.$(ARCH).o	\
+$(SRC)/contour.$(ARCH).o	\
+$(SRC)/create.$(ARCH).o	\
+$(SRC)/cumulative.$(ARCH).o	\
+$(SRC)/cursor.$(ARCH).o	\
+$(SRC)/cut.$(ARCH).o		\
+$(SRC)/delete.$(ARCH).o	\
+$(SRC)/device.$(ARCH).o	\
+$(SRC)/dimendown.$(ARCH).o	\
+$(SRC)/dimenup.$(ARCH).o	\
+$(SRC)/dbconnect.$(ARCH).o	\
+$(SRC)/dbselect.$(ARCH).o	\
+$(SRC)/erase.$(ARCH).o		\
+$(SRC)/extract.$(ARCH).o	\
+$(SRC)/fft1d.$(ARCH).o		\
+$(SRC)/fft2d.$(ARCH).o		\
+$(SRC)/fit2d.$(ARCH).o		\
+$(SRC)/fit.$(ARCH).o		\
+$(SRC)/gaussj.$(ARCH).o	\
+$(SRC)/gaussdeviate.$(ARCH).o	\
+$(SRC)/grid.$(ARCH).o		\
+$(SRC)/gridify.$(ARCH).o       \
+$(SRC)/grow.$(ARCH).o		\
+$(SRC)/ungridify.$(ARCH).o     \
+$(SRC)/histogram.$(ARCH).o	\
+$(SRC)/imcut.$(ARCH).o	 	\
+$(SRC)/imhist.$(ARCH).o	\
+$(SRC)/imsmooth.$(ARCH).o	\
+$(SRC)/integrate.$(ARCH).o	\
+$(SRC)/interpolate.$(ARCH).o	\
+$(SRC)/jpeg.$(ARCH).o		\
+$(SRC)/kern.$(ARCH).o		\
+$(SRC)/keyword.$(ARCH).o	\
+$(SRC)/labels.$(ARCH).o	\
+$(SRC)/limits.$(ARCH).o	\
+$(SRC)/line.$(ARCH).o		\
+$(SRC)/list_buffers.$(ARCH).o	\
+$(SRC)/list_header.$(ARCH).o	\
+$(SRC)/list_vectors.$(ARCH).o	\
+$(SRC)/load.$(ARCH).o		\
+$(SRC)/lookup.$(ARCH).o	\
+$(SRC)/mkrgb.$(ARCH).o	\
+$(SRC)/mcreate.$(ARCH).o	\
+$(SRC)/medacc.$(ARCH).o	\
+$(SRC)/mget.$(ARCH).o		\
+$(SRC)/minterpolate.$(ARCH).o	\
+$(SRC)/mset.$(ARCH).o		\
+$(SRC)/peak.$(ARCH).o		\
+$(SRC)/periodogram.$(ARCH).o	\
+$(SRC)/plot.$(ARCH).o		\
+$(SRC)/dot.$(ARCH).o		\
+$(SRC)/point.$(ARCH).o		\
+$(SRC)/ps.$(ARCH).o		\
+$(SRC)/queuedelete.$(ARCH).o	\
+$(SRC)/queuedrop.$(ARCH).o	\
+$(SRC)/queuelist.$(ARCH).o	\
+$(SRC)/queueload.$(ARCH).o	\
+$(SRC)/queuesize.$(ARCH).o	\
+$(SRC)/queuepush.$(ARCH).o	\
+$(SRC)/queuepop.$(ARCH).o	\
+$(SRC)/queueprint.$(ARCH).o	\
+$(SRC)/queuesubstr.$(ARCH).o	\
+$(SRC)/queueinit.$(ARCH).o	\
+$(SRC)/radial.$(ARCH).o	\
+$(SRC)/rd.$(ARCH).o		\
+$(SRC)/rdseg.$(ARCH).o		\
+$(SRC)/read_vectors.$(ARCH).o	\
+$(SRC)/rebin.$(ARCH).o		\
+$(SRC)/resize.$(ARCH).o	\
+$(SRC)/roll.$(ARCH).o		\
+$(SRC)/rotate.$(ARCH).o	\
+$(SRC)/save.$(ARCH).o		\
+$(SRC)/section.$(ARCH).o	\
+$(SRC)/select.$(ARCH).o	\
+$(SRC)/set.$(ARCH).o		\
+$(SRC)/shift.$(ARCH).o		\
+$(SRC)/sort.$(ARCH).o		\
+$(SRC)/spline_apply.$(ARCH).o	\
+$(SRC)/spline_construct.$(ARCH).o \
+$(SRC)/stats.$(ARCH).o		   \
+$(SRC)/style.$(ARCH).o		   \
+$(SRC)/subraster.$(ARCH).o	   \
+$(SRC)/subset.$(ARCH).o	   \
+$(SRC)/svd.$(ARCH).o		   \
+$(SRC)/swapbytes.$(ARCH).o	   \
+$(SRC)/textline.$(ARCH).o	   \
+$(SRC)/tv.$(ARCH).o		   \
+$(SRC)/tvchannel.$(ARCH).o	   \
+$(SRC)/tvcolors.$(ARCH).o	   \
+$(SRC)/tvcontour.$(ARCH).o	   \
+$(SRC)/tvgrid.$(ARCH).o	   \
+$(SRC)/uniq.$(ARCH).o		   \
+$(SRC)/unsign.$(ARCH).o	   \
+$(SRC)/vbin.$(ARCH).o		   \
+$(SRC)/vclip.$(ARCH).o		   \
+$(SRC)/vgauss.$(ARCH).o           \
+$(SRC)/vmaxwell.$(ARCH).o           \
+$(SRC)/vgrid.$(ARCH).o		   \
+$(SRC)/vload.$(ARCH).o		   \
+$(SRC)/vzload.$(ARCH).o		   \
+$(SRC)/vpop.$(ARCH).o		   \
+$(SRC)/vroll.$(ARCH).o		   \
+$(SRC)/vsmooth.$(ARCH).o	\
+$(SRC)/vstat.$(ARCH).o		   \
+$(SRC)/wd.$(ARCH).o		   \
+$(SRC)/write_vectors.$(ARCH).o	   \
+$(SRC)/zap.$(ARCH).o		   \
+$(SRC)/zplot.$(ARCH).o
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/convert.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libdatacmd.$(ARCH).a: $(srcs)
+$(LIB)/libdatacmd.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libdatacmd.a: $(LIB)/libdatacmd.$(ARCH).a
+$(DESTLIB)/libdatacmd.$(DLLTYPE): $(LIB)/libdatacmd.$(ARCH).$(DLLTYPE)
+
+libdatacmd: $(DESTLIB)/libdatacmd.a  $(DESTLIB)/libdatacmd.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libdatacmd.a
+	rm -f $(DESTLIB)/libdatacmd.$(DLLTYPE)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/accum.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/accum.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/accum.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "data.h"
+
+int accum (int argc, char **argv) {
+  
+  int i, Nbins, bin, N, Normalize;
+  float start, end, delta;
+  float *V, *K, *O, *NV;
+  Vector *val, *key, *out;
+
+  NV = NULL;
+  Normalize = FALSE;
+  if ((N = get_argument (argc, argv, "-norm"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = TRUE;
+  }
+
+  if ((argc != 6) && (argc != 7)) {
+    gprint (GP_ERR, "USAGE: accum <value> <vector> <key> start end [delta]\n");
+    gprint (GP_ERR, "  sum <value> in bins corresponding to the value of <key>\n");
+    return (FALSE);
+  }
+
+  if ((val = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((key = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (val[0].Nelements != key[0].Nelements) {
+    gprint (GP_ERR, "key and value don't match\n");
+    return (FALSE);
+  }
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  start = atof (argv[4]);
+  end   = atof (argv[5]);
+  if (argc == 7) 
+    delta = atof (argv[6]);
+  else 
+    delta = 1;
+  if ((start == end) || (delta == 0)) {
+    gprint (GP_ERR, "error in value: %f to %f, %f\n", start, end, delta);
+    return (FALSE);
+  }
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+  Nbins = (end - start) / delta;
+
+  out[0].Nelements = Nbins;
+  REALLOCATE (out[0].elements, float, out[0].Nelements);
+  bzero (out[0].elements, sizeof(float)*out[0].Nelements);
+  if (Normalize) {
+    ALLOCATE (NV, float, Nbins);
+    bzero (NV, sizeof(float)*Nbins);
+  }
+
+  V = val[0].elements;
+  K = key[0].elements;
+  O = out[0].elements;
+
+  for (i = 0; i < val[0].Nelements; i++, V++, K++) {
+    bin = MIN (MAX (0, (*K - start) / delta), Nbins - 1);
+    O[bin] += *V;
+    if (Normalize) NV[bin] ++;
+  }      
+
+  if (Normalize) {
+    for (i = 0; i < Nbins; i++) {
+      O[i] /= (float) NV[i];
+    }
+    free (NV);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/applyfit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/applyfit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/applyfit.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "data.h"
+
+char *get_variable (char *);
+
+int applyfit (int argc, char **argv) {
+  
+  int i, j, order;
+  char *c, name[64];
+  double *C, X;
+  float *x, *y;
+  Vector *xvec, *yvec;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: applyfit x y\n");
+    return (FALSE);
+  }
+
+  c = get_variable ("Cn");
+  if (c == NULL) {
+    gprint (GP_ERR, "no fit available\n");
+    return (FALSE);
+  }
+  order = atof (c);
+  free (c);
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  ALLOCATE (C, double, order+1);
+  for (i = 0; i < order + 1; i++) {
+    sprintf (name, "C%d", i);
+    c = get_variable (name);
+    if (c == NULL) {
+      gprint (GP_ERR, "missing fit term %d\n", i);
+      return (FALSE);
+    }
+    C[i] = atof (c);
+    free (c);
+  }
+  yvec[0].Nelements = xvec[0].Nelements;
+  REALLOCATE (yvec[0].elements, float, yvec[0].Nelements);
+  bzero (yvec[0].elements, sizeof(float)*yvec[0].Nelements);
+  x = xvec[0].elements;
+  y = yvec[0].elements;
+
+  for (j = 0; j < xvec[0].Nelements; j++, x++, y++) {
+    X = 1;
+    for (i = 0; i < order + 1; i++) {
+      *y += C[i]*X;
+      X = X * (*x);
+    }
+  }
+
+  free (C);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/applyfit2d.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/applyfit2d.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/applyfit2d.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+char *get_variable (char *);
+
+int applyfit2d (int argc, char **argv) {
+  
+  int i, j, n, order;
+  char *c, name[64];
+  double **C, X, Y;
+  float *x, *y, *z;
+  Vector *xvec, *yvec, *zvec;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: applyfit x y z\n");
+    return (FALSE);
+  }
+
+  c = get_variable ("Cnn");
+  if (c == NULL) {
+    gprint (GP_ERR, "no fit available\n");
+    return (FALSE);
+  }
+  order = atof (c);
+  free (c);
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((zvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  ALLOCATE (C, double *, order+1);
+  for (i = 0; i < order + 1; i++) {
+    ALLOCATE (C[i], double, order+1);
+    for (j = 0; j < order + 1 - i; j++) {
+      sprintf (name, "CX%dY%d", i, j);
+      c = get_variable (name);
+      if (c == NULL) {
+	gprint (GP_ERR, "missing fit term %d,%d\n", i, j);
+	for (j = 0; j < i; j++) free (C[j]);
+	free (C);
+	return (FALSE);
+      }
+      C[i][j] = atof (c);
+      free (c);
+    }
+  }
+
+  zvec[0].Nelements = xvec[0].Nelements;
+  REALLOCATE (zvec[0].elements, float, zvec[0].Nelements);
+  bzero (zvec[0].elements, sizeof(float)*zvec[0].Nelements);
+  x = xvec[0].elements;
+  y = yvec[0].elements;
+  z = zvec[0].elements;
+
+  for (n = 0; n < xvec[0].Nelements; n++, x++, y++, z++) {
+    Y = X = 1;
+    for (j = 0; j < order + 1; j++) {
+      X = Y;
+      for (i = 0; i < order + 1 - j; i++) {
+	*z += C[i][j]*X;
+	X = X * (*x);
+      }
+      Y = Y * (*y);
+    }
+  }
+  for (i = 0; i < order + 1; i++) free (C[i]);
+  free (C);
+
+  return (TRUE);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/book.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/book.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/book.c	(revision 22322)
@@ -0,0 +1,84 @@
+# include "data.h"
+
+int book_list (int argc, char **argv);
+int book_init (int argc, char **argv);
+int book_create (int argc, char **argv);
+int book_delete (int argc, char **argv);
+int book_getbook (int argc, char **argv);
+int book_listbook (int argc, char **argv);
+int book_shuffle (int argc, char **argv);
+int book_npages (int argc, char **argv);
+int book_newpage (int argc, char **argv);
+int book_getpage (int argc, char **argv);
+int book_delpage (int argc, char **argv);
+int book_listpage (int argc, char **argv);
+int book_setword (int argc, char **argv);
+int book_getword (int argc, char **argv);
+
+static Command book_commands[] = {
+  {1, "list",     book_list,     "list books"},
+  {1, "init",     book_init,     "initialize a book"},
+  {1, "create",   book_create,   "create a book"},
+  {1, "delete",   book_delete,   "delete a book"},
+  {1, "getbook",  book_getbook,  "get book name by location"},
+  {1, "listbook", book_listbook, "list pages in a book"},
+  {1, "shuffle",  book_shuffle,  "shuffle pages in a book"},
+  {1, "npages",   book_npages,   "return number of pages in a book"},
+  {1, "newpage",  book_newpage,  "create a new page in a book"},
+  {1, "getpage",  book_getpage,  "get page name by location"},
+  {1, "delpage",  book_delpage,  "delete a page in a book"},
+  {1, "listpage", book_listpage, "list a page in a book"},
+  {1, "setword",  book_setword,  "set the value of a word in a page"},
+  {1, "getword",  book_getword,  "set the value of a word from a page"},
+};
+
+int book_command (int argc, char **argv) {
+
+  int i, N, status;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: book (command)\n");
+    gprint (GP_ERR, "    book list                                  : list books\n");
+    gprint (GP_ERR, "    book init     (book)                       : removes all pages from book\n");
+    gprint (GP_ERR, "    book create   (book)                       : create a book\n");
+    gprint (GP_ERR, "    book delete   (book)                       : delete a book\n");
+    gprint (GP_ERR, "    book getbook  (where) [-var var]           : get book name\n");
+    gprint (GP_ERR, "    book listbook (book)                       : list a book\n");
+    gprint (GP_ERR, "    book shuffle  (book)                       : randomize pages in a book\n");
+    gprint (GP_ERR, "    book npages   (book) [-var var] [-key key] : return number of pages in a book\n");
+    gprint (GP_ERR, "    book newpage  (book) (page)                : create a new page in a book\n");
+    gprint (GP_ERR, "    book getpage  (book) (where) [-var var] [-key key value] : get page name in a book\n");
+    gprint (GP_ERR, "    book delpage  (book) (page) [-key key]     : delete a page in a book\n");
+    gprint (GP_ERR, "    book delpage  (book) -key name value       : delete a page in a book\n");
+    gprint (GP_ERR, "    book listpage (book) (page)                : list a page in a book\n");
+    gprint (GP_ERR, "    book setword  (book) (page) (word) (value) : set the value of a word in a page\n");
+    gprint (GP_ERR, "    book getword  (book) (page) (word) [-var var] : set the value of a word from a page\n");
+    return (FALSE);
+  }
+
+  N = sizeof (book_commands) / sizeof (Command);
+
+  /* find the book sub-command which matches */
+  for (i = 0; i < N; i++) {
+    if (!strcmp (book_commands[i].name, argv[1])) {
+      status = (*book_commands[i].func) (argc - 1, argv + 1);
+      return (status);
+    }
+  }
+
+  gprint (GP_ERR, "unknown book command %s\n", argv[1]);
+  return (FALSE);
+}
+
+/* book is called with the command "book".  
+   the command line word "book" is meant to be followed the one of several 
+   possible options:
+   
+   book create
+   book delete
+   book list
+   book edit
+   book read
+   book write
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/book_commands.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/book_commands.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/book_commands.c	(revision 22322)
@@ -0,0 +1,489 @@
+# include "data.h"
+
+int book_list (int argc, char **argv) {
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: book list\n");
+    return FALSE;
+  }
+
+  ListBooks();
+  return TRUE;
+}
+
+int book_create (int argc, char **argv) {
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book create (book)\n");
+    return FALSE;
+  }
+
+  CreateBook (argv[1]);
+  return TRUE;
+}
+
+int book_delete (int argc, char **argv) {
+
+  int status;
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book delete (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  status = DeleteBook (book);
+  if (!status) abort ();
+  return TRUE;
+}
+
+int book_init (int argc, char **argv) {
+
+  int status;
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book init (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book != NULL) {
+      status = DeleteBook (book);
+      if (!status) abort ();
+  }
+
+  CreateBook (argv[1]);
+  return TRUE;
+}
+
+int book_listbook (int argc, char **argv) {
+
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book listbook (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  ListPages (book);
+  return TRUE;
+}
+
+int book_npages (int argc, char **argv) {
+
+  int i, N, Npages;
+  Book *book;
+  char *varName;
+  char *Key, *Value, *value;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Key = NULL;
+  Value = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book npages (book) [-var result] [-key name value]\n");
+    gprint (GP_ERR, "  reports the number of pages (optionally limited to those matching the key)\n");
+    FREE (varName);
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  Npages = book[0].Npages;
+  if (Key) {
+    /* count only matching key */
+    Npages = 0;
+    for (i = 0; i < book[0].Npages; i++) {
+      value = BookGetWord (book[0].pages[i], Key);
+      if (value == NULL) {
+	if (!strcmp(Value, "NULL")) { 
+	  Npages ++;
+	} else {
+	  continue;
+	}
+      } else {
+	if (!strcmp(value, Value)) Npages ++;
+      }
+    }
+  }
+
+  if (varName) {
+    set_int_variable (varName, Npages);
+  } else {
+    gprint (GP_ERR, "%d pages\n", Npages);
+  }
+  FREE (Key);
+  FREE (Value);
+  FREE (varName);
+  return TRUE;
+}
+
+int book_getbook (int argc, char **argv) {
+
+  int where, N;
+  char *varName;
+  Book *book;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book getbook (where) [-var var]\n");
+    FREE (varName);
+    return FALSE;
+  }
+
+  where = atoi (argv[1]);
+  book = GetBook (where);
+  if (book == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    } 
+
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  if (varName) {
+    set_str_variable (varName, book[0].name);
+  } else {
+    gprint (GP_LOG, "%s\n", book[0].name);
+  }
+  FREE (varName);
+  return TRUE;
+}
+
+int book_newpage (int argc, char **argv) {
+
+  Book *book;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: book newpage (book) (page)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+  
+  CreatePage (book, argv[2]);
+  return TRUE;
+}
+
+int book_delpage (int argc, char **argv) {
+
+  int i, N;
+  Page *page;
+  Book *book;
+  char *Key, *Value, *value;
+
+  Key = NULL;
+  Value = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 3) && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: book delpage (book) (page)\n");
+    gprint (GP_ERR, "USAGE: book delpage (book) -key name value\n");
+    FREE (Key);
+    FREE (Value);
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (Key);
+    FREE (Value);
+    return FALSE;
+  }
+  
+  if (Key) {
+    /* delete by matching key */
+    for (i = 0; i < book[0].Npages; i++) {
+      value = BookGetWord (book[0].pages[i], Key);
+      if (value == NULL) continue;
+      if (!strcmp(value, Value)) {
+	DeletePage (book, book[0].pages[i]);
+	i--; /* if we delete this page, don't advance the counter */
+      }
+    }
+    FREE (Key);
+    FREE (Value);
+    return TRUE;
+  }
+
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    FREE (Key);
+    FREE (Value);
+    return FALSE;
+  }
+
+  DeletePage (book, page);
+  FREE (Key);
+  FREE (Value);
+  return TRUE;
+}
+
+int book_listpage (int argc, char **argv) {
+
+  Page *page;
+  Book *book;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: book listpage (book) (page)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+  
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    return FALSE;
+  }
+
+  ListWords (page);
+  return TRUE;
+}
+
+int book_getpage (int argc, char **argv) {
+
+  int where, N;
+  char *varName, *keyName, *keyValue;
+  Book *book;
+  Page *page;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  keyValue = keyName = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    keyName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    keyValue = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: book getpage (book) (where) [-var var] [-key key value]\n");
+    FREE (varName);
+    FREE (keyName);
+    FREE (keyValue);
+    return FALSE;
+  }
+
+  where = atoi (argv[2]);
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      FREE (keyName);
+      FREE (keyValue);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    FREE (keyName);
+    FREE (keyValue);
+    return FALSE;
+  }
+
+  if (keyName == NULL) {
+    page = GetPage (book, where);
+  } else {
+    page = GetPageRestricted (book, where, keyName, keyValue);
+  }
+
+  if (page == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      FREE (keyName);
+      FREE (keyValue);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "page %d not found in %s\n", where, argv[1]);
+    FREE (varName);
+    FREE (keyName);
+    FREE (keyValue);
+    return FALSE;
+  }
+
+  if (varName) {
+    set_str_variable (varName, page[0].name);
+  } else {
+    gprint (GP_LOG, "%s\n", page[0].name);
+  }
+  FREE (varName);
+  FREE (keyName);
+  FREE (keyValue);
+  return TRUE;
+}
+
+int book_shuffle (int argc, char **argv) {
+
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book shuffle (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  ShufflePages (book);
+  return TRUE;
+}
+
+int book_setword (int argc, char **argv) {
+
+  Page *page;
+  Book *book;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: book setword (book) (page) (word) (value)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+  
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    return FALSE;
+  }
+
+  BookSetWord (page, argv[3], argv[4]);
+  return TRUE;
+}
+
+int book_getword (int argc, char **argv) {
+
+  int N;
+  Page *page;
+  Book *book;
+  char *value, *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: book getword (book) (page) (word)\n");
+    FREE (varName);
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+  
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  value = BookGetWord (page, argv[3]);
+  if (value == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    }
+    gprint (GP_ERR, "value %s on page %s in book %s not found\n", argv[3], argv[2], argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  if (varName) {
+    set_str_variable (varName, value);
+  } else {
+    gprint (GP_LOG, "%s\n", value);
+  }
+    
+  FREE (varName);
+  return TRUE;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/box.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/box.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/box.c	(revision 22322)
@@ -0,0 +1,106 @@
+# include "data.h"
+
+int box (int argc, char **argv) {
+  
+  int i, N, kapa;
+  char *name;
+  Graphdata graphmode;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  graphmode.lweight = 1;
+  if ((N = get_argument (argc, argv, "-lw"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.lweight = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  graphmode.color = KapaColorByName ("black");
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.color = KapaColorByName (argv[N]);
+    if (graphmode.color == -1) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  strcpy (graphmode.ticks, "2222");
+  if ((N = get_argument (argc, argv, "-ticks"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (graphmode.ticks, argv[N]);
+    remove_argument (N, &argc, argv);
+    if (strlen (graphmode.ticks) != 4) { goto usage; }
+    for (i = 0; i < strlen (graphmode.ticks); i++) {
+      if ((graphmode.ticks[i] != '0') && (graphmode.ticks[i] != '1') && (graphmode.ticks[i] != '2')) { goto usage; }
+    }
+  }
+  
+  strcpy (graphmode.labels, "2222");
+  if ((N = get_argument (argc, argv, "-labels"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (graphmode.labels, argv[N]);
+    remove_argument (N, &argc, argv);
+    if (strlen (graphmode.labels) != 4) { goto usage; }
+    for (i = 0; i < strlen (graphmode.labels); i++) {
+      if ((graphmode.labels[i] != '0') && (graphmode.labels[i] != '1') && (graphmode.labels[i] != '2')) { goto usage; }
+    }
+  }
+
+  strcpy (graphmode.axis, "2222");
+  if ((N = get_argument (argc, argv, "-axis"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (graphmode.axis, argv[N]);
+    remove_argument (N, &argc, argv);
+    if (strlen (graphmode.axis) != 4) { goto usage; }
+    for (i = 0; i < strlen (graphmode.axis); i++) {
+      if ((graphmode.axis[i] != '0') && (graphmode.axis[i] != '1') && (graphmode.axis[i] != '2')) { goto usage; }
+    }
+  }
+
+  if (argc != 1) goto usage;
+
+  KapaBox (kapa, &graphmode);
+  return (TRUE);
+      
+ usage:
+  gprint (GP_ERR, "USAGE: box [-ticks NNNN] [-axis NNNN] [-labels NNNN]\n");
+  return (FALSE);
+}
+
+
+/* box has:
+   axis
+   labels
+   ticks
+
+   assign like this:   
+   -axis 0000
+   -labels 0000
+   -ticks 0000
+
+   default:
+   -axis 1111
+   -labels 1100
+   -ticks 1111
+
+   messages to kapa:
+
+   DBOX
+   (xmin) (xmax) (ymin) (ymax)
+   AAAA LLLL TTTT
+
+   A = axis
+   L = label
+   T = ticks
+   
+   0 = off
+   1 = on
+   2 = default / maintain
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/center.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/center.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/center.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include "data.h"
+
+int center (int argc, char **argv) {
+  
+  double x, y;
+  int zoom;
+  int kapa, N;
+  char *name;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: center x y [zoom]\n");
+    return (FALSE);
+  }
+
+  x = atof (argv[1]);
+  y = atof (argv[2]);
+  zoom = 0;
+  if (argc == 4) zoom = atof (argv[3]);
+
+  KiiCenter (kapa, x, y, zoom);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/clear.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/clear.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/clear.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "data.h"
+
+// default is to clear all plots, but not the sections or the images
+int clear (int argc, char **argv) {
+  
+  int N;
+  int kapa;
+  char *name;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // clear all sections
+  if ((N = get_argument (argc, argv, "-s")) || 
+      (N = get_argument (argc, argv, "-section"))) {
+      KapaClearSections (kapa);
+      return (TRUE);
+  }
+
+  // clear all sections
+  if ((N = get_argument (argc, argv, "-graph"))) {
+      KapaClearCurrentPlot (kapa);
+      return (TRUE);
+  }
+
+  // clear image
+  if ((N = get_argument (argc, argv, "-image"))) {
+      KapaClearImage (kapa);
+      return (TRUE);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: clear [-n Xgraph] [-s|-section] [-image] [-graph]\n");
+    gprint (GP_ERR, "       [-s|-section] : clear all sections\n");
+    gprint (GP_ERR, "       [-graph]      : clear current graph\n");
+    gprint (GP_ERR, "       [-image]      : clear current image\n");
+    return (FALSE);
+  }
+
+  KapaClearPlots (kapa);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/clip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/clip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/clip.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "data.h"
+
+int clip (int argc, char **argv) {
+
+  int i, Npix, DO_NAN, DO_INF, N;
+  double min, Vmin, max, Vmax, nan_val, inf_val;
+  float *in;
+  Buffer *buf;
+
+  inf_val = nan_val = min = Vmin = max = Vmax = 0;
+
+  DO_NAN = FALSE;
+  if ((N = get_argument (argc, argv, "-nan"))) {
+    remove_argument (N, &argc, argv);
+    nan_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_NAN = TRUE;
+  }
+
+  DO_INF = FALSE;
+  if ((N = get_argument (argc, argv, "-inf"))) {
+    remove_argument (N, &argc, argv);
+    inf_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_INF = TRUE;
+  }
+
+  if ((argc != 6) && (!(DO_INF || DO_NAN))) {
+    gprint (GP_ERR, "USAGE: clip (buffer) [min Vmin max Vmax] [-inf val] [-nan val]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    min = atof (argv[2]);
+    Vmin = atof (argv[3]);
+    max = atof (argv[4]);
+    Vmax = atof (argv[5]);
+  }
+
+  Npix = buf[0].matrix.Naxis[0]*buf[0].matrix.Naxis[1];
+  in = (float *) buf[0].matrix.buffer;
+
+  if (argc == 6) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (*in < min) 
+	*in = Vmin;
+      if (*in > max)
+	*in = Vmax;
+    }
+  }
+  in = (float *) buf[0].matrix.buffer;
+  if (DO_NAN) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (isnan (*in)) {
+	*in = nan_val;
+      }
+    }
+  }
+  in = (float *) buf[0].matrix.buffer;
+  if (DO_INF) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (!finite (*in)) {
+	*in = inf_val;
+      }
+    }
+  }
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/close.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/close.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/close.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "data.h"
+
+int close_device (int argc, char **argv) {
+
+  int N, kapa;
+  char *name;
+  /* close current graphics device */
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+
+  close_kapa (name); 
+  FREE (name);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/concat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/concat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/concat.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "data.h"
+
+int concat (int argc, char **argv) {
+
+  int  i, j, Nin;
+  double value;
+  Vector *ivec, *ovec;
+
+  /** check basic syntax **/
+  if (argc != 3) {
+    gprint (GP_ERR, "SYNTAX: concat (vector/value) vector\n");
+    gprint (GP_ERR, "  concatanate (vector/value) to vector\n");
+    return (FALSE);
+  }
+
+  if ((ovec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (SelectScalar (argv[1], &value)) {
+    Nin = ovec[0].Nelements;
+    ovec[0].Nelements++;
+    REALLOCATE (ovec[0].elements, float, ovec[0].Nelements);
+    ovec[0].elements[Nin] = value;
+    return (TRUE);
+  } 
+  
+  if ((ivec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  
+  REALLOCATE (ovec[0].elements, float, ovec[0].Nelements + ivec[0].Nelements);
+  for (j = ovec[0].Nelements, i = 0; i < ivec[0].Nelements; i++, j++) {
+    ovec[0].elements[j] = ivec[0].elements[i];
+  }
+  ovec[0].Nelements += ivec[0].Nelements;
+  
+  return (TRUE);
+    
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/contour.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/contour.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/contour.c	(revision 22322)
@@ -0,0 +1,262 @@
+# include "data.h"
+# define LL { \
+ dx =  d00 / (*v01 - *v00); \
+ dy = -d00 / (*v10 - *v00); \
+ x = i - 0.5; \
+ y = j - dy - 0.5; }
+
+# define UL { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = 1 - tmp; \
+ dx =  d10 / (*v11 - *v10); \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+      
+# define LR { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = 1 - tmp; \
+ dy = d01 / (*v11 - *v01); \
+ x = i + tmp - 0.5; \
+ y = j - 0.5; }
+
+# define UR { \
+ tmp = d10 / (*v11 - *v10); \
+ dx = 1 - tmp; \
+ dy = -d11 / (*v01 - *v11); \
+ x = i + tmp - 0.5; \
+ y = j + 1 - 0.5; }
+      
+# define HZ { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = d01 / (*v11 - *v01) - tmp; \
+ dx = 1; \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+
+# define VT { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = d10 / (*v11 - *v10) - tmp; \
+ x = i + tmp - 0.5; \
+ dy = 1; \
+ y = j - 0.5; }
+
+Vector *xv, *yv;
+int N, NVEC;
+
+void DUMP (float x, float y, float dx, float dy) {
+  
+  xv[0].elements[N]   = x;
+  xv[0].elements[N+1] = x+dx;
+  
+  yv[0].elements[N]   = y;
+  yv[0].elements[N+1] = y+dy;
+  
+  N+=2;
+
+  if (N >= NVEC - 2) {
+    NVEC += 100;
+    REALLOCATE (xv[0].elements, float, NVEC);
+    REALLOCATE (yv[0].elements, float, NVEC);
+  }
+}
+
+int contour (int argc, char **argv) {
+
+  int i, j, Nx, Ny;
+  float level, d00, d01, d10, d11, tmp;
+  float x, y, dx, dy;
+  float *v00, *v01, *v10, *v11;
+  float *matrix;
+  Buffer *buf;
+  
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: contour <buffer> X Y level\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((xv = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yv = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  level = atof (argv[4]);
+  matrix = (float *)(buf[0].matrix.buffer);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  v00 = matrix;
+  v01 = matrix + 1;
+  v10 = matrix + Nx;
+  v11 = matrix + Nx + 1;
+  d01 = (level - *v00);
+  d11 = (level - *v10);
+
+  N = 0;
+  NVEC = 100;
+  REALLOCATE (xv[0].elements, float, NVEC);
+  REALLOCATE (yv[0].elements, float, NVEC);
+
+  for (j = 1; j < Ny; j++) {
+    if (!(j%10)) gprint (GP_ERR, ".");
+    for (i = 1; i < Nx; i++, v00++, v01++, v10++, v11++) {
+
+      d00 = d01;
+      d10 = d11;
+      d01 = (level - *v01);
+      d11 = (level - *v11);
+
+      if (((d00 > 0) && (d01 > 0) && (d10 > 0) && (d11 > 0)) ||
+	  ((d00 < 0) && (d01 < 0) && (d10 < 0) && (d11 < 0)))
+	continue;
+
+      if ((d00 > 0) && (d10 <= 0)) { 
+	if ((d01 <= 0) && (d11 <= 0)) { /* \  */
+	  LL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* -  */
+	  HZ;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) { /* /  */
+	  UL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* \\  */
+	  LL;
+	  DUMP (x,y,dx,dy);
+	  UR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+
+      if ((d00 <= 0) && (d10 > 0)) {
+	if ((d01 > 0) && (d11 > 0)) { /* \  */
+	  LL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* -  */
+	  HZ;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) { /* /  */
+	  UL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* //  */
+	  UL;
+	  DUMP (x,y,dx,dy);
+	  LR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+      
+
+      if ((d00 <= 0) && (d10 <= 0)) { 
+	if ((d01 > 0) && (d11 <= 0)) {
+	  LR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) {
+	  UR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) {
+	  VT;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+
+      if ((d00 > 0) && (d10 > 0)) { 
+	if ((d01 <= 0) && (d11 > 0)) {
+	  LR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) {
+	  UR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) {
+	  VT;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+
+    }
+    /* skip left-hand edge */
+    v00++; v01++; v10++; v11++;
+    d01 = (level - *v00);
+    d11 = (level - *v10);
+  }
+
+  /****** bottom line *******/
+  v00 = matrix;  
+  v01 = matrix + 1;  
+  y = 0;
+  dx = 0;
+  dy = -0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+
+  /********** top line *******/
+  v00 = matrix + Nx*(Ny - 1);  
+  v01 = v00 + 1;
+  y = Ny - 1;
+  dx = 0;
+  dy = 0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+
+  /******** left line *********/
+  v00 = matrix; 
+  v01 = matrix + Nx;
+  x = 0;
+  dx = -0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+
+  /******** right line *********/
+  v00 = matrix + Nx - 1; 
+  v01 = v00 + Nx;
+  x = Nx - 1;
+  dx = 0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+  
+/* free anything? */
+
+  xv[0].Nelements = N;
+  yv[0].Nelements = N;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/create.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/create.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/create.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "data.h"
+
+int create (int argc, char **argv) {
+  
+  int i;
+  float start, end, delta;
+  Vector *vec;
+  
+  if ((argc != 5) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: create vector start end [delta]\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  start = atof (argv[2]);
+  end   = atof (argv[3]);
+  delta = 1;
+  if (argc == 5) delta = atof (argv[4]);
+  if ((start == end) || (delta == 0)) {
+    gprint (GP_ERR, "error in value: %f to %f, %f\n", start, end, delta);
+    return (FALSE);
+  }
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+
+  vec[0].Nelements = (end - start) / delta;
+  REALLOCATE (vec[0].elements, float, vec[0].Nelements);
+
+  for (i = 0; i < vec[0].Nelements; i++) {
+    vec[0].elements[i] = start + i*delta;
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/cumulative.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/cumulative.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/cumulative.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "data.h"
+
+int cumulative (int argc, char **argv) {
+  
+  int i;
+  float *Vi, *Vo;
+  Vector *ivec, *ovec;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cumulative invec outvec\n");
+    return (FALSE);
+  }
+
+  if ((ivec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  ovec[0].Nelements = ivec[0].Nelements;
+    
+  REALLOCATE (ovec[0].elements, float, ovec[0].Nelements);
+  bzero (ovec[0].elements, sizeof(float)*ovec[0].Nelements);
+
+  Vi = ivec[0].elements;
+  Vo = ovec[0].elements;
+  *Vo = *Vi;
+  for (i = 1; i < ivec[0].Nelements; i++, Vi++, Vo++) {
+    *Vo = Vo[-1] + *Vi;
+  }      
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/cursor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/cursor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/cursor.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "data.h"
+
+int cursor (int argc, char **argv) {
+
+  char string[20], key[20], *name;
+  int i, N, kapa;
+  double X, Y, R, D, Z;
+  void *oldsignal;
+
+  // XXX need to be able to specify graph vs image coords
+  // currently, if only one exists, that frame will be used
+  // if both exist, defaults to ??
+  // if ((N = get_argument (argc, argv, "-g"))) {
+  // if ((N = get_argument (argc, argv, "-i"))) {
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraphData (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  N = 0;
+  if (argc == 2) N = atof (argv[1]);
+
+  if ((argc != 1) && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: cursor [Npts] [-n window] [-g | -i]\n");
+    return (FALSE);
+  }
+  
+  KiiCursorOn (kapa);
+  
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  for (i = 0; ((i < N) || (N == 0)) && !interrupt; i++) {
+
+    KiiCursorRead (kapa, &X, &Y, &Z, &R, &D, key);
+
+    sprintf (string, "X%s", key);
+    set_variable (string, X);
+    sprintf (string, "Y%s", key);
+    set_variable (string, Y);
+    sprintf (string, "Z%s", key);
+    set_variable (string, Z);
+    sprintf (string, "R%s", key);
+    set_variable (string, R);
+    sprintf (string, "D%s", key);
+    set_variable (string, D);
+
+    set_str_variable ("KEY", key);
+    
+    gprint (GP_LOG, "%s %f %f %f %f %f\n", key, X, Y, Z, R, D);
+
+    if (!strcasecmp (key, "Q")) break;
+  }
+
+  signal (SIGINT, oldsignal);
+  KiiCursorOff (kapa);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/cut.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/cut.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/cut.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "data.h"
+
+enum {SUM, MEAN, MEDIAN};
+
+int cut (int argc, char **argv) {
+  
+  int i, j, N, Nx, Ny, Mode;
+  float *Vin, *Vbuf, value;
+  int sx, sy, nx, ny;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  Mode = SUM;
+  if ((N = get_argument (argc, argv, "-median"))) {
+    remove_argument (N, &argc, argv);
+    Mode = MEDIAN;
+  }
+  if ((N = get_argument (argc, argv, "-mean"))) {
+    remove_argument (N, &argc, argv);
+    Mode = MEAN;
+  }
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: cut <buffer> <X vector> <Y vector> <X|Y> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+ 
+  sx = atof (argv[5]);
+  sy = atof (argv[6]);
+  nx = atof (argv[7]);
+  ny = atof (argv[8]);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  if ((sx < 0) || (sy < 0) || (sx+nx > Nx) || (sy+ny > Ny)) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  switch (argv[4][0]) {
+  case 'x':
+  case 'X':
+    /* create output vectors */
+    xvec[0].Nelements = yvec[0].Nelements = nx;
+    REALLOCATE (xvec[0].elements, float, MAX (nx, 1));
+    REALLOCATE (yvec[0].elements, float, MAX (nx, 1));
+    bzero (yvec[0].elements, nx*sizeof(float));
+    for (i = 0; i < nx; i++) {
+      xvec[0].elements[i] = i + sx; 
+    }
+    ALLOCATE (Vbuf, float, MAX (ny, 1));
+
+    for (i = 0; i < nx; i++) {
+      /* accumulate values */
+      Vin = (float *)(buf[0].matrix.buffer) + sy*Nx + sx + i; 
+      for (j = 0; j < ny; j++, Vin += Nx) {
+	Vbuf[j] = *Vin;
+      }
+      /* apply stat of choice */
+      if (Mode == MEDIAN) {
+	fsort (Vbuf, ny);
+	value = Vbuf[(int)(0.5*ny)];
+      } else {
+	value = 0;
+	for (j = 0; j < ny; j++) {
+	  value += Vbuf[j];
+	}
+	if (Mode == MEAN) { value /= ny; }
+      }
+      yvec[0].elements[i] = value;
+    }
+    free (Vbuf);
+    break;
+    
+  case 'y':
+  case 'Y':
+    xvec[0].Nelements = yvec[0].Nelements = ny;
+    REALLOCATE (xvec[0].elements, float, ny);
+    REALLOCATE (yvec[0].elements, float, ny);
+    bzero (yvec[0].elements, ny*sizeof(float));
+    for (i = 0; i < ny; i++) {
+      xvec[0].elements[i] = i + sy; 
+    }
+    ALLOCATE (Vbuf, float, MAX (nx, 1));
+
+    for (i = 0; i < ny; i++) {
+      /* accumulate values */
+      Vin = (float *)(buf[0].matrix.buffer) + (sy + i)*Nx + sx; 
+      for (j = 0; j < nx; j++, Vin ++) {
+	Vbuf[j] = *Vin;
+      }
+      /* apply stat of choice */
+      if (Mode == MEDIAN) {
+	fsort (Vbuf, nx);
+	value = Vbuf[(int)(0.5*nx)];
+      } else {
+	value = 0;
+	for (j = 0; j < nx; j++) {
+	  value += Vbuf[j];
+	}
+	if (Mode == MEAN) { value /= nx; }
+      }
+      yvec[0].elements[i] = value;
+    }
+    free (Vbuf);
+    break;
+
+  default:
+    gprint (GP_ERR, "<dir> can be X or Y\n");
+    return (FALSE);
+    break;
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dbconnect.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dbconnect.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dbconnect.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include "data.h"
+# if (HAVE_MYSQL_H)
+# include "mysql.h"
+
+MYSQL mysql;
+MYSQL *connection = NULL;
+
+int dbconnect (int argc, char **argv) {
+  
+  char query[256];
+  char password[1024];
+  MYSQL_RES *result;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: dbconnect (hostname) (username) (database)\n");
+    return FALSE;
+  }
+
+  fprintf (stdout, "Enter password: ");
+  scan_line (stdin, password);
+
+# if (0)
+  int i;
+  char c;
+
+  initscr();
+  noecho();
+  i = 0;
+  while (((c = getch()) != EOF) && (c != '\n')) {
+    password[i] = c;
+    i++;
+  }
+  password[i] = 0;
+# endif
+
+  mysql_init (&mysql);
+  connection = mysql_real_connect (&mysql, argv[1], argv[2], password, argv[3], 0, 0, 0);
+
+  if (connection == NULL) {
+    gprint (GP_ERR, "failed to connect to database\n");
+    gprint (GP_ERR, "%s\n", mysql_error (&mysql));
+    return (FALSE);
+  }
+    
+  sprintf (query, "set @@interactive_timeout = 30000");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to set interactive timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  mysql_free_result (result);
+    
+  sprintf (query, "set @@wait_timeout = 30000");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to set wait timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  mysql_free_result (result);
+    
+# if (0)
+  int Nrows;
+  MYSQL_ROW row;
+
+  sprintf (query, "select @@interactive_timeout");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to get timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  Nrows = mysql_num_rows(result);
+  row = mysql_fetch_row(result);
+  fprintf (stderr, "interactive timeout: %s\n", row[0]);
+  mysql_free_result (result);
+
+  sprintf (query, "select @@wait_timeout");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to get timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  Nrows = mysql_num_rows(result);
+  row = mysql_fetch_row(result);
+  fprintf (stderr, "wait timeout: %s\n", row[0]);
+  mysql_free_result (result);
+# endif
+    
+  return (TRUE);
+}
+
+// XXX do I need to close the connection before opening a new one? 
+
+void *db_getConnection () {
+  return connection;
+}
+
+# else 
+
+int dbconnect (int argc, char **argv) {
+
+  gprint (GP_ERR, "mysql library is not available\n");
+  return FALSE;
+}
+
+void *db_getConnection () {
+  return NULL;
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dbselect.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dbselect.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dbselect.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "data.h"
+# if (HAVE_MYSQL_H) 
+# include "mysql.h"
+
+int dbselect (int argc, char **argv) {
+  
+  time_t seconds;
+  int i, j, Nbytes, Ncols, Nrows;
+  char *query;
+  Vector **vec;
+
+  MYSQL_RES *result;
+  MYSQL_ROW row;
+  MYSQL_FIELD *fields;
+  MYSQL *connection = NULL;
+
+  if (argc < 4) {
+    gprint (GP_ERR, "USAGE: dbselect (fields) from (table) [where]\n");
+    return FALSE;
+  }
+
+  connection = db_getConnection ();
+  if (connection == NULL) {
+    gprint (GP_ERR, "database not defined; use dbconnect\n");
+    return (FALSE);
+  }
+
+  // generate the query line (concat the argv[i] entries)
+  Nbytes = 0;
+  for (i = 1; i < argc; i++) {
+    Nbytes += strlen(argv[i]) + 1;
+  }
+  Nbytes += 10;
+
+  ALLOCATE (query, char, Nbytes);
+  bzero (query, Nbytes);
+  strcat (query, "select ");
+  for (i = 1; i < argc; i++) {
+    strcat (query, argv[i]);
+    strcat (query, " ");
+  }
+  // strcat (query, ";");
+  // fprintf (stderr, "query: %s\n", query);
+
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "problem with query\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+    
+  result = mysql_store_result (connection);
+
+  Nrows = mysql_num_rows(result);
+  Ncols = mysql_num_fields(result);
+  fields = mysql_fetch_fields (result);
+
+  // fprintf (stderr, "Nrows: %d\n", Nrows);
+  // fprintf (stderr, "Ncols: %d\n", Ncols);
+
+  ALLOCATE (vec, Vector *, Ncols);
+  for (i = 0; i < Ncols; i++) {
+    if ((vec[i] = SelectVector (fields[i].name, ANYVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, "trouble creating vector named %s\n", fields[i].name);
+      free (query);
+      free (vec);
+      return (FALSE);
+    }
+    REALLOCATE (vec[i][0].elements, float, Nrows);
+    vec[i][0].Nelements = Nrows;
+  }
+
+  for (j = 0; j < Nrows; j++) {
+    row = mysql_fetch_row(result);
+    if (row == NULL) {
+      gprint (GP_ERR, "inconsistent row count: expected %d, got %d\n", Nrows, j);
+      free (query);
+      free (vec);
+      mysql_free_result (result);
+      return (FALSE);
+    }
+    for (i = 0; i < Ncols; i++) {
+      if (row[i]) {
+	switch (fields[i].type) {
+	  case FIELD_TYPE_TINY:
+	  case FIELD_TYPE_SHORT:
+	  case FIELD_TYPE_LONG:
+	  case FIELD_TYPE_INT24:
+	  case FIELD_TYPE_LONGLONG:
+	  case FIELD_TYPE_DECIMAL:
+	  case FIELD_TYPE_FLOAT:
+	  case FIELD_TYPE_DOUBLE:
+	    vec[i][0].elements[j] = atof (row[i]);
+	    break;
+	  case FIELD_TYPE_TIME:
+	  case FIELD_TYPE_DATE:
+	  case FIELD_TYPE_DATETIME:
+	    seconds = ohana_date_to_sec (row[i]);
+	    vec[i][0].elements[j] = ohana_sec_to_mjd (seconds);
+	    break;
+	  default:
+	    vec[i][0].elements[j] = NAN;
+	}
+      } else {
+	vec[i][0].elements[j] = NAN;
+      }
+    }
+  }
+  free (query);
+  free (vec);
+  mysql_free_result (result);
+  return (TRUE);
+}
+# else 
+
+int dbselect (int argc, char **argv) {
+
+  gprint (GP_ERR, "mysql library is not available\n");
+  return FALSE;
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/delete.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/delete.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/delete.c	(revision 22322)
@@ -0,0 +1,31 @@
+# include "data.h"
+
+int delete (int argc, char **argv) {
+  
+  int i, N, Quiet;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: delete <obiect> [<object> ..]\n");
+    return (FALSE);
+  }
+
+  for (i = 1; i < argc; i++) {
+    if (DeleteNamedBuffer (argv[i])) continue; 
+    if (DeleteNamedVector (argv[i])) continue;
+    if (DeleteNamedScalar (argv[i])) continue; 
+    if (!Quiet) gprint (GP_ERR, "can't find object %s\n", argv[i]);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/device.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/device.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/device.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "data.h"
+
+int device (int argc, char **argv) {
+
+  int N, kapa;
+  char *name;;
+  /* set / get current graphics device */
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (name == NULL) {
+    name = GetKapaName ();
+    if (name == NULL) {
+      gprint (GP_ERR, "no device defined\n");
+      return (FALSE);
+    }
+  } else {
+    if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+  }
+  gprint (GP_ERR, "kapa %s\n", name); 
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dimendown.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dimendown.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dimendown.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "data.h"
+
+enum {VALUE, XCOORD, YCOORD};
+
+int dimendown (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix, N, mode;
+  float *in, *out;
+  Vector *vec;
+  Buffer *buf;
+
+  mode = VALUE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    mode = XCOORD;
+  }
+  if ((N = get_argument (argc, argv, "-y"))) {
+    remove_argument (N, &argc, argv);
+    mode = YCOORD;
+  }
+
+  if (argc != 3) goto usage;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  Npix = Nx * Ny;
+
+  vec[0].Nelements = Npix;
+  REALLOCATE (vec[0].elements, float, Npix);
+
+  in = (float *) buf[0].matrix.buffer;
+  out = vec[0].elements;
+
+  switch (mode) {
+    case VALUE:
+      for (i = 0; i < Npix; i++, in++, out++) {
+	*out = *in;
+      }
+      break;
+
+    case XCOORD:
+      for (i = 0; i < Npix; i++, out++) {
+	*out = i % Nx;
+      }
+      break;
+
+    case YCOORD:
+      for (i = 0; i < Npix; i++, out++) {
+	*out = i / Nx;
+      }
+      break;
+  }
+      
+  return (TRUE);
+
+ usage:
+    gprint (GP_ERR, "USAGE: dimendown <buffer> <vector>\n");
+    gprint (GP_ERR, "  -x : fill vector with buffer x-coords\n");
+    gprint (GP_ERR, "  -y : fill vector with buffer y-coords\n");
+    return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dimenup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dimenup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dimenup.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "data.h"
+
+int dimenup (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix;
+  float *in, *out;
+  Vector *vec;
+  Buffer *buf;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: dimenup <vector> <buffer> Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((buf = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Npix = vec[0].Nelements;
+  Nx = atof (argv[3]);
+  Ny = atof (argv[4]);
+  if (Npix != Nx * Ny) {
+    gprint (GP_ERR, "dimensions don't match\n");
+    return (FALSE);
+  }
+
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, Nx, Ny, -32, 0.0, 1.0);
+
+  out = (float *) buf[0].matrix.buffer;
+  in = vec[0].elements;
+
+  for (i = 0; i < Npix; i++, in++, out++) {
+    *out = *in;
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/dot.c	(revision 22322)
@@ -0,0 +1,27 @@
+# include "data.h"
+
+int dot (int argc, char **argv) {
+  
+  int kapa;
+  Graphdata graphmode;
+  float x, y;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: dot <x> <y>\n");
+    return (FALSE);
+  }
+  x = atof(argv[1]);
+  y = atof(argv[2]);
+
+  /* set point style and errorbar mode (these are NOT sticky) */
+  graphmode.style = 2;
+  graphmode.etype = 0;
+
+  if (!KapaPrepPlot (kapa, 1, &graphmode)) return (FALSE);
+  KapaPlotVector (kapa, 1, &x, "x");
+  KapaPlotVector (kapa, 1, &y, "y");
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/erase.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/erase.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/erase.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "data.h"
+
+int erase (int argc, char **argv) {
+  
+  int i, N;
+  int kapa;
+  char *name;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: erase (overlay) [overlay, overlay, ..] \n");
+    gprint (GP_ERR, " (overlay) may be: red (0), green (1), blue (2), yellow (3) or all\n");
+    return (FALSE);
+  }
+
+  for (i = 1; i < argc; i++) {
+    if (!(strcasecmp (argv[i], "all"))) {
+      KiiEraseOverlay (kapa, "red");
+      KiiEraseOverlay (kapa, "green");
+      KiiEraseOverlay (kapa, "blue");
+      KiiEraseOverlay (kapa, "yellow");
+      continue;
+    }
+    if (!KiiSelectOverlay (argv[i], &N)) {
+      gprint (GP_ERR, "%s is not a valid overlay\n", argv[i]);
+      return (FALSE);
+    }
+    KiiEraseOverlay (kapa, argv[i]);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/extract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/extract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/extract.c	(revision 22322)
@@ -0,0 +1,92 @@
+# include "data.h"
+
+int extract (int argc, char **argv) {
+  
+  int i, j;
+  float *Vin, *Vout;
+  int sx, sy, nx, ny, NX, NY;
+  int Sx, Sy, Nx, Ny;
+  Buffer *in, *out;
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: extract <from> <to> sx sy nx ny Sx Sy Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((in = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  NX = in[0].matrix.Naxis[0];
+  NY = in[0].matrix.Naxis[1];
+
+  sx = atof (argv[3]);
+  sy = atof (argv[4]);
+  nx = atof (argv[5]);
+  ny = atof (argv[6]);
+
+  Sx = atof (argv[7]);
+  Sy = atof (argv[8]);
+  Nx = atof (argv[9]);
+  Ny = atof (argv[10]);
+
+  if ((Sy + ny > Ny) || (Sx + nx > Nx)) {
+    gprint (GP_ERR, "mismatch between source and dest regions\n");
+    gprint (GP_ERR, "%d + %d > %d or %d + %d > %d\n", Sy, ny, Ny, Sx, nx, Nx);
+    return (FALSE);
+  }
+
+  /* region is not on first image */
+  if ((sx + nx < 0) || (sy + ny < 0) || 
+      (sx > in[0].matrix.Naxis[0]) || 
+      (sy > in[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region outside of source image\n");
+    return (FALSE);
+  }
+
+  if ((Sx + nx > Nx) || (Sy + ny > Ny)) {
+    gprint (GP_ERR, "source region larger than dest region\n");
+    return (FALSE);
+  }
+  if ((Sx < 0) || (Sy < 0)) {
+    gprint (GP_ERR, "dest region out of range\n");
+    return (FALSE);
+  }
+
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, FALSE)) == NULL) {
+    if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    gfits_free_matrix (&out[0].matrix);
+    gfits_free_header (&out[0].header);
+
+    out[0].bitpix = in[0].bitpix;
+    out[0].unsign = in[0].unsign;
+    out[0].bscale = in[0].bscale;
+    out[0].bzero  = in[0].bzero;
+    gfits_copy_header (&in[0].header, &out[0].header);
+    gfits_modify (&out[0].header, "NAXIS1", "%d", 1, Nx);
+    gfits_modify (&out[0].header, "NAXIS2", "%d", 1, Ny);
+    out[0].header.Naxis[0] = Nx;
+    out[0].header.Naxis[1] = Ny;
+    gfits_create_matrix (&out[0].header, &out[0].matrix);
+  } else {
+    if ((out[0].header.Naxis[1] != Ny) || (out[0].header.Naxis[0] != Nx)) {
+      gprint (GP_ERR, "matrix sizes mis-matched\n");
+      gprint (GP_ERR, "%d x %d  vs  %d x %d\n", Nx, Ny, 
+	       out[0].header.Naxis[0], out[0].header.Naxis[1]);
+      return (FALSE);
+    }
+  }
+
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= NY) continue;
+    Vin = (float *)(in[0].matrix.buffer) + (j + sy)*in[0].matrix.Naxis[0] + sx;  
+    Vout = (float *)(out[0].matrix.buffer) + (j + Sy)*out[0].matrix.Naxis[0] + Sx;   
+    for (i = 0; i < nx; i++, Vin++, Vout++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= NX) continue;
+      *Vout = *Vin;
+    }
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft1d.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft1d.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft1d.c	(revision 22322)
@@ -0,0 +1,61 @@
+# include "data.h"
+
+int fft1d (int argc, char **argv) {
+  
+  int N, Npix, Nbit, ZeroImaginary, forward;
+  Vector *Ire, *Iim, *Ore, *Oim;
+
+  forward = TRUE;
+  if ((N = get_argument (argc, argv, "-inverse"))) {
+    remove_argument (N, &argc, argv);
+    forward = FALSE;
+  }
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft1d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  Iim = NULL;
+  ZeroImaginary = TRUE;
+  if (strcmp (argv[2], "0")) {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  
+  // check the input data (match lengths? binary length?)
+  Npix = Ire[0].Nelements;
+  if (!ZeroImaginary && (Npix != Iim[0].Nelements)) {
+    gprint (GP_ERR, "vector size mismatch\n");
+    return (FALSE);
+  }
+  if (!IsBinary (Npix, &Nbit)) {
+    gprint (GP_ERR, "Npix is not a binary number!\n");
+    return (FALSE);
+  }
+
+  // select or create the output vectors
+  if ((Ore = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  // allocate sufficient output space
+  Ore[0].Nelements = Npix;
+  Oim[0].Nelements = Npix;
+  REALLOCATE (Ore[0].elements, float, Npix);
+  REALLOCATE (Oim[0].elements, float, Npix);
+ 
+  // copy data to output vectors (fft is done in place)
+  memcpy (Ore[0].elements, Ire[0].elements, Npix*sizeof(float));
+
+  // copy imaginary vector or create a zero vector
+  if (ZeroImaginary) {
+    memset (Oim[0].elements, 0, Npix*sizeof(float));
+  } else {
+    memcpy (Oim[0].elements, Iim[0].elements, Npix*sizeof(float));
+  }    
+
+  fft1D (Ore[0].elements, Oim[0].elements, Npix, Nbit, forward); 
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft1d.old.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft1d.old.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft1d.old.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include "data.h"
+
+int fft1dold (int argc, char **argv) {
+  
+  int i, Npix, ZeroImaginary;
+  float *t1, *t2, *temp;
+  Vector *Ire, *Iim, *Ore, *Oim;
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft1d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  Iim = NULL;
+  ZeroImaginary = TRUE;
+  if (strcmp (argv[2], "0")) {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Ore = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Npix = Ire[0].Nelements;
+  if (!ZeroImaginary && (Npix != Iim[0].Nelements)) {
+    gprint (GP_ERR, "vector mismatch in size\n");
+    return (FALSE);
+  }
+
+  if (!IsBinaryOld (Npix)) {
+    gprint (GP_ERR, "Npix is not a binary number!\n");
+    return (FALSE);
+  }
+  
+  ALLOCATE (temp, float, 2*Npix);
+  if (ZeroImaginary) {
+    t1 = Ire[0].elements;
+    for (i = 0; i < Npix; i++, t1++) {
+      temp[2*i  ] = *t1;
+      temp[2*i+1] = 0;
+    }
+  } else {
+    t1 = Ire[0].elements;
+    t2 = Iim[0].elements;
+    for (i = 0; i < Npix; i++, t1++, t2++) {
+      temp[2*i  ] = *t1;
+      temp[2*i+1] = *t2;
+    }
+  }    
+    
+  fftold (temp, Npix, 1); 
+
+  Ore[0].Nelements = Npix;
+  Oim[0].Nelements = Npix;
+  REALLOCATE (Ore[0].elements, float, Npix);
+  REALLOCATE (Oim[0].elements, float, Npix);
+ 
+  t1 = Ore[0].elements;
+  t2 = Oim[0].elements;
+  for (i = 0; i < Npix; i++, t1++, t2++) {
+    *t1 = temp[2*i  ] / Npix;
+    *t2 = temp[2*i+1] / Npix;
+  }    
+  
+  free (temp);
+  
+  return (TRUE);
+}
+
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft2d.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft2d.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft2d.c	(revision 22322)
@@ -0,0 +1,79 @@
+# include "data.h"
+
+int fft2d (int argc, char **argv) {
+  
+  int ZeroImaginary, forward;
+  int N, Nx, Ny;
+  Buffer *Ire, *Iim, *Ore, *Oim;;
+
+  forward = TRUE;
+  if ((N = get_argument (argc, argv, "-inverse"))) {
+    remove_argument (N, &argc, argv);
+    forward = FALSE;
+  }
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft2d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  Iim = NULL;
+  ZeroImaginary = TRUE; /* Input(imaginary) may be 0, in which case we create a 0 filled image */
+  if (!strcmp (argv[2], "0")) { 
+  } else {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = Ire[0].header.Naxis[0];
+  Ny = Ire[0].header.Naxis[1];
+
+  // check input image dimensions (match lengths? binary lengths?)
+  if (!ZeroImaginary) {
+    if ((Nx != Iim[0].header.Naxis[0]) || 
+	(Ny != Iim[0].header.Naxis[1])) {
+      gprint (GP_ERR, "image size mismatch\n");
+      return (FALSE);
+    }
+  }
+  if (!IsBinary (Nx, NULL)) {
+    gprint (GP_ERR, "Nx is not a binary number!\n");
+    return (FALSE);
+  }
+  if (!IsBinary (Ny, NULL)) {
+    gprint (GP_ERR, "Ny is not a binary number!\n");
+    return (FALSE);
+  }
+
+  if ((Ore = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectBuffer (argv[5], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  /* free up output space */
+  gfits_free_matrix (&Ore[0].matrix);
+  gfits_free_header (&Ore[0].header);
+  gfits_free_matrix (&Oim[0].matrix);
+  gfits_free_header (&Oim[0].header);
+  
+  /* fix up output headers (real) & allocate data buffer */
+  CreateBuffer (Ore, Nx, Ny, -32, 0.0, 1.0);
+  CreateBuffer (Oim, Nx, Ny, -32, 0.0, 1.0);
+
+  gfits_copy_header (&Ire[0].header, &Ore[0].header);
+  gfits_copy_header (&Ire[0].header, &Oim[0].header);
+
+  // copy data to output buffers (fft is done in place)
+  memcpy (Ore[0].matrix.buffer, Ire[0].matrix.buffer, Nx*Ny*sizeof(float));
+
+  if (ZeroImaginary) {
+    memset (Oim[0].matrix.buffer, 0, Nx*Ny*sizeof(float));
+  } else {
+    memcpy (Oim[0].matrix.buffer, Iim[0].matrix.buffer, Nx*Ny*sizeof(float));
+  }
+
+  /* run the fft */
+  fftND ((float *)Ore[0].matrix.buffer, (float *)Oim[0].matrix.buffer, 2, Ire[0].header.Naxis, forward);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft2d.old.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft2d.old.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fft2d.old.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "data.h"
+
+int fft2dold (int argc, char **argv) {
+  
+  int i, N, Nx, Ny, Naxis[2];
+  int Npix, ZeroImaginary, isign;
+  float *t1, *t2, *out, *temp;
+  Buffer *Ire, *Iim, *Ore, *Oim;;
+
+  isign = 1;
+  if ((N = get_argument (argc, argv, "-inverse"))) {
+    remove_argument (N, &argc, argv);
+    isign = -1;
+  }
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft2d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  Iim = NULL;
+  ZeroImaginary = TRUE; /* Input(imaginary) may be 0, in which case we create a 0 filled image */
+  if (!strcmp (argv[2], "0")) { 
+  } else {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Ore = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectBuffer (argv[5], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  /* free up output space */
+  gfits_free_matrix (&Ore[0].matrix);
+  gfits_free_header (&Ore[0].header);
+  gfits_free_matrix (&Oim[0].matrix);
+  gfits_free_header (&Oim[0].header);
+
+  /* get image dimensions, check value */
+  Npix = Ire[0].header.Naxis[0]*Ire[0].header.Naxis[1];
+  Nx = Ire[0].header.Naxis[0];
+  Ny = Ire[0].header.Naxis[1];
+  Naxis[0] = Ny; Naxis[1] = Nx;
+  if (!IsBinaryOld (Npix)) {
+    gprint (GP_ERR, "dimensions are not binary!\n");
+    return (FALSE);
+  }
+  
+  /* create working space */
+  t1 = (float *) Ire[0].matrix.buffer;
+  ALLOCATE (temp, float, 2*Npix);
+  out = temp;
+
+  /* copy input to working space */
+  if (ZeroImaginary) {
+    for (i = 0; i < Npix; i++, t1++) {
+      *out = *t1;
+      out++;
+      *out = 0;
+      out++;
+    }
+  } else {
+    t2 = (float *) Iim[0].matrix.buffer;
+    for (i = 0; i < Npix; i++, t1++, t2++) {
+      *out = *t1;
+      out++;
+      *out = *t2;
+      out++;
+    }
+  } 
+    
+  /* run the fft */
+  fftNold (temp, Naxis, 2, isign);
+
+  /* fix up output headers (real) */
+  gfits_copy_header (&Ire[0].header, &Ore[0].header);
+  gfits_modify (&Ore[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&Ore[0].header, "NAXIS2", "%d", 1, Ny);
+  Ore[0].header.Naxis[0] = Nx;
+  Ore[0].header.Naxis[1] = Ny;
+  Ore[0].bitpix = Ire[0].bitpix;
+  Ore[0].unsign = Ire[0].unsign;
+  Ore[0].bscale = Ire[0].bscale;
+  Ore[0].bzero  = Ire[0].bzero;
+  gfits_create_matrix (&Ore[0].header, &Ore[0].matrix);
+
+  /* fix up output headers (imaginary) */
+  gfits_copy_header (&Ire[0].header, &Oim[0].header);
+  gfits_modify (&Oim[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&Oim[0].header, "NAXIS2", "%d", 1, Ny);
+  Oim[0].header.Naxis[0] = Nx;
+  Oim[0].header.Naxis[1] = Ny;
+  Oim[0].bitpix = Ire[0].bitpix;
+  Oim[0].unsign = Ire[0].unsign;
+  Oim[0].bscale = Ire[0].bscale;
+  Oim[0].bzero  = Ire[0].bzero;
+  gfits_create_matrix (&Oim[0].header, &Oim[0].matrix);
+
+  /* move data from working space to output buffers */
+  out = temp;
+  t1 = (float *) Ore[0].matrix.buffer;
+  t2 = (float *) Oim[0].matrix.buffer;
+  for (i = 0; i < Npix; i++, t1++, t2++) {
+    *t1 = *out / Npix;
+    out ++;
+    *t2 = *out / Npix;
+    out ++;
+  }    
+
+  free (temp);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fit.c	(revision 22322)
@@ -0,0 +1,227 @@
+# include "data.h"
+
+int fit (int argc, char **argv) {
+  
+  double **c, **b, *s, X, Y, dY, dY2;
+  double ClipNSigma, mean, sigma, maxsigma;
+  int i, j, nterm, mterm, order, Npt, Nmask;
+  int N, Weight, Quiet, ClipNiter;
+  Vector *xvec, *yvec, *dyvec;
+  float *x, *y, *dy, *yf, *yfit;
+  char name[64], *mask;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  ClipNSigma = 0;
+  ClipNiter  = 1;
+  if ((N = get_argument (argc, argv, "-clip"))) {
+    remove_argument (N, &argc, argv);
+    ClipNSigma = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ClipNiter  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  dy = NULL;
+  dyvec = NULL;
+  Weight = FALSE;
+  if ((N = get_argument (argc, argv, "-dy"))) {
+    remove_argument (N, &argc, argv);
+    if ((dyvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+    Weight = TRUE;
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: fit x y order [-dy wt] [-quiet/-q] [-clip Nsigma Niter]\n");
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  if (Weight && xvec[0].Nelements != dyvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  
+  /* nterm is number of polynomial terms, starting at x^0 */
+  order = atof (argv[3]);
+  nterm = order + 1;
+  mterm = 2*nterm;
+
+  ALLOCATE (yfit, float, xvec[0].Nelements);
+  ALLOCATE (mask, char, xvec[0].Nelements);
+  memset (mask, 0, xvec[0].Nelements);
+
+  ALLOCATE (s, double, mterm);
+  ALLOCATE (b, double *, nterm);
+  ALLOCATE (c, double *, nterm);
+  for (i = 0; i < nterm; i++) {
+    ALLOCATE (c[i], double, nterm);
+    ALLOCATE (b[i], double, 1);
+  }
+
+  Nmask = 0;
+  sigma = 0.0;
+
+  for (N = 0; N < ClipNiter; N++) {
+
+    /* init registers for current pass */
+    memset (s, 0, mterm*sizeof(double));
+    for (i = 0; i < nterm; i++) {
+      memset (c[i], 0, nterm*sizeof(double));
+      memset (b[i], 0, sizeof(double));
+    }
+
+    /* perform linear fit */
+    x = xvec[0].elements;
+    y = yvec[0].elements;
+    if (Weight) dy = dyvec[0].elements;
+
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++) {
+      if (mask[i]) continue;
+      if (!(finite(*x) && finite(*y))) continue;
+      dY = 1.0;
+      if (Weight) { 
+	dY = 1.0 / SQ(*dy);
+	dy ++;
+      }
+      X = 1*dY;
+      Y = *y*dY;
+      for (j = 0; j < nterm; j++) {
+	s[j] += X;
+	b[j][0] += Y;
+	X = X * (*x);
+	Y = Y * (*x);
+      }
+      for (j = nterm; j < mterm; j++) {
+	s[j] += X;
+	X = X * (*x);
+      }
+    }
+    for (i = 0; i < nterm; i++) {
+      for (j = 0; j < nterm; j++) {
+	c[i][j] = s[i + j];
+      }
+    }
+    if (!dgaussjordan (c, b, nterm, 1)) goto escape;
+
+    /* generate fitted values */
+    x = xvec[0].elements;
+    yf = yfit;
+    for (i = 0; i < xvec[0].Nelements; i++, x++, yf++) {
+      if (!finite(*x)) continue;
+      *yf = 0;
+      X = 1;
+      for (j = 0; j < order + 1; j++) {
+	*yf += b[j][0]*X;
+	X = X * (*x);
+      }
+    }
+
+    /* measure fit residual scatter */
+    x  = xvec[0].elements;
+    y  = yvec[0].elements;
+    yf = yfit;
+    dY = dY2 = 0;
+    for (i = Npt = 0; i < xvec[0].Nelements; i++, x++, y++, yf++) {
+      if (mask[i]) continue;
+      if (!finite(*x)) continue;
+      dY  += (*y - *yf);
+      dY2 += SQ(*y - *yf);
+      Npt ++;
+    }
+    mean  = dY / Npt;
+    sigma = sqrt (fabs(dY2/Npt - SQ(mean)));
+    maxsigma = ClipNSigma * sigma;
+
+    /* mask outlier points */
+    x  = xvec[0].elements;
+    y  = yvec[0].elements;
+    yf = yfit;
+    Nmask = 0;
+    for (i = 0; ClipNSigma && (i < xvec[0].Nelements); i++, x++, y++, yf++) {
+      dY = (*y - *yf);
+      if (fabs(dY) > maxsigma) {
+	mask[i] = TRUE;
+	Nmask ++;
+      } else {
+	mask[i] = FALSE;
+      }	
+    }
+  }
+      
+  /* print & save basic fit parameters */
+  if (!Quiet) gprint (GP_ERR, "y = ");
+  for (i = 0; i < nterm; i++) {
+    sprintf (name, "C%d", i);
+    set_variable (name, b[i][0]);
+    if (!Quiet) gprint (GP_ERR, "%f x^%d ", b[i][0], i);
+  }
+  sprintf (name, "Cn");
+  set_variable (name, (double) order);
+  
+  /* print & save basic fit parameters */
+  if (!Quiet) gprint (GP_ERR, "\n");
+  if (!Quiet) gprint (GP_ERR, "    ");
+  for (i = 0; i < nterm; i++) {
+    sprintf (name, "dC%d", i);
+    set_variable (name, sqrt(c[i][i]));
+    if (!Quiet) gprint (GP_ERR, "%f     ", sqrt(c[i][i]));
+  }
+  if (!Quiet) gprint (GP_ERR, "\n");
+
+  set_variable ("dC", sigma);
+  set_variable ("Cnv", (xvec[0].Nelements - Nmask));
+
+  /* save mask and yfit for testing? */
+  if (1) { 
+    Vector *fvec, *mvec;
+    if ((fvec = SelectVector ("yfit", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    if ((mvec = SelectVector ("mask", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    free (fvec[0].elements);
+    fvec[0].elements = yfit;
+    fvec[0].Nelements = xvec[0].Nelements;
+    mvec[0].Nelements = xvec[0].Nelements;
+
+    REALLOCATE (mvec[0].elements, float, xvec[0].Nelements);
+    for (i = 0; i < xvec[0].Nelements; i++) {
+      mvec[0].elements[i] = mask[i];
+    }
+  } else {
+    free (yfit);
+  }
+  free (mask);
+
+  for (i = 0; i < nterm; i++) {
+    free (b[i]);
+    free (c[i]);
+  }
+  free (b);
+  free (c);
+  free (s);
+  return (TRUE);
+
+escape:
+  for (i = 0; i < nterm; i++) {
+    free (b[i]);
+    free (c[i]);
+  }
+  free (b);
+  free (c);
+  free (s);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fit2d.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fit2d.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/fit2d.c	(revision 22322)
@@ -0,0 +1,265 @@
+# include "data.h"
+
+int fit2d (int argc, char **argv) {
+  
+  double **c, **b, **s, X, Y, dZ, dZ2;
+  double ClipNSigma, mean, sigma, maxsigma;
+  int k, K, i, j, n, Npt, Nmask, nx, ny, nterm, mterm, wterm, order;
+  int N, Weight, Quiet, ClipNiter, VERBOSE;
+  float *x, *y, *z, *dz, *zfit, *zf; 
+  char name[64], *mask;
+  Vector *xvec, *yvec, *zvec, *dzvec;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  ClipNSigma = 0;
+  ClipNiter  = 1;
+  if ((N = get_argument (argc, argv, "-clip"))) {
+    remove_argument (N, &argc, argv);
+    ClipNSigma = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ClipNiter  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  dz = NULL;
+  dzvec = NULL;
+  Weight = FALSE;
+  if ((N = get_argument (argc, argv, "-dz"))) {
+    remove_argument (N, &argc, argv);
+    if ((dzvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+    Weight = TRUE;
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: fit x y z order [-dz wt]\n");
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  if (Weight && xvec[0].Nelements != dzvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  
+  order = atof (argv[4]);
+  nterm = order + 1;
+  wterm = nterm*(nterm + 1)/2;
+  mterm = 2*order + 1;
+
+  ALLOCATE (zfit, float, xvec[0].Nelements);
+  ALLOCATE (mask, char, xvec[0].Nelements);
+  memset (mask, 0, xvec[0].Nelements);
+
+  /* allocate the summation matrices */
+  ALLOCATE (s, double *, mterm);
+  ALLOCATE (b, double *, wterm);
+  ALLOCATE (c, double *, wterm);
+  for (i = 0; i < wterm; i++) {
+    ALLOCATE (c[i], double, wterm);
+    ALLOCATE (b[i], double, 1);
+  }
+  for (i = 0; i < mterm; i++) {
+    ALLOCATE (s[i], double, mterm);
+  }
+
+  for (N = 0; N < ClipNiter; N++) {
+
+    /* init registers for current pass */
+    // XXX this was incorrectly using nterm (missing 1 row)
+    for (i = 0; i < wterm; i++) {
+      memset (c[i], 0, wterm*sizeof(double));
+      memset (b[i], 0, sizeof(double));
+    }
+    for (i = 0; i < mterm; i++) {
+      memset (s[i], 0, mterm*sizeof(double));
+    }
+
+    x = xvec[0].elements;
+    y = yvec[0].elements;
+    z = zvec[0].elements;
+    if (Weight) dz = dzvec[0].elements;
+
+    /* add up the x,y values */
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++) {
+      if (mask[i]) continue;
+      if (!finite(*x) || !finite(*y)) continue;
+      dZ = 1.0;
+      if (Weight) { 
+	dZ = 1.0 / SQ(*dz);
+	dz ++;
+      }
+      Y = X = 1*dZ;
+      for (ny = 0; ny < mterm; ny++) {
+	X = Y;
+	for (nx = 0; nx < mterm - ny; nx++) {
+	  s[nx][ny] += X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+    }
+
+    /* add up the z values */
+    x = xvec[0].elements;
+    y = yvec[0].elements;
+    z = zvec[0].elements;
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++, z++) {
+      if (mask[i]) continue;
+      if (!finite(*x) || !finite(*y)) continue;
+      dZ = 1.0;
+      if (Weight) { 
+	dZ = 1.0 / SQ(*dz);
+	dz ++;
+      }
+      Y = X = *z*dZ;
+      for (j = 0, ny = 0; ny < nterm; ny++) {
+	X = Y;
+	for (nx = 0; nx < nterm - ny; nx++, j++) {
+	  b[j][0] += X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+    }
+
+    /* re-sort mterm x mterm matrix to wterm matrix */
+    for (k = j = 0; j < nterm; j++) {
+      for (i = 0; i < nterm - j; i++, k++) {
+	for (K = ny = 0; ny < nterm; ny++) {
+	  for (nx = 0; nx < nterm - ny; nx++, K++) {
+	    c[K][k] = s[nx+i][ny+j];
+	  }
+	}
+      }
+    }
+
+    dgaussjordan (c, b, wterm, 1);
+
+    /** test print **/
+    if (VERBOSE) {
+      for (i = ny = 0; ny < nterm; ny++) {
+	for (nx = 0; nx < nterm - ny; nx++, i++) {
+	  gprint (GP_ERR, "x^%d y^%d: %g\n", nx, ny, b[i][0]);
+	}
+      }
+    }
+
+    /* the b[][0] terms are in the following order:
+       y^0 x^0, y^0 x^1, ... y^0 x^N
+       y^1 x^0, y^1 x^1, ... y^1 x^N
+       ...
+       y^N x^0, y^N x^1, ... y^N x^N
+    */
+    /* generate fitted values */
+    x = xvec[0].elements;
+    y = yvec[0].elements;
+    zf = zfit;
+    for (n = 0; n < xvec[0].Nelements; n++, x++, y++, zf++) {
+      if (!finite(*x) || !finite(*y)) continue;
+      *zf = 0;
+      Y = X = 1;
+      for (i = ny = 0; ny < nterm; ny++) {
+	Y = X;
+	for (nx = 0; nx < nterm - ny; nx++, i++) {
+	  *zf += b[i][0]*Y;
+	  Y = Y * (*y);
+	}
+	X = X * (*x);
+      }
+    }
+
+    /* measure fit residual scatter */
+    x  = xvec[0].elements;
+    y  = yvec[0].elements;
+    z  = zvec[0].elements;
+    zf = zfit;
+    dZ = dZ2 = 0;
+    for (i = Npt = 0; i < xvec[0].Nelements; i++, x++, y++, z++, zf++) {
+      if (mask[i]) continue;
+      if (!finite(*x) || !finite(*y)) continue;
+      dZ  += (*z - *zf);
+      dZ2 += SQ(*z - *zf);
+      Npt ++;
+    }
+    mean  = dZ / Npt;
+    sigma = sqrt (fabs(dZ2/Npt - SQ(mean)));
+    maxsigma = ClipNSigma * sigma;
+
+    if (VERBOSE) gprint (GP_ERR, "mean: %g, sigma: %g, maxsigma: %g\n", mean, sigma, maxsigma);
+
+    /* mask outlier points */
+    x  = xvec[0].elements;
+    y  = yvec[0].elements;
+    z  = zvec[0].elements;
+    zf = zfit;
+    Nmask = 0;
+    for (i = 0; ClipNSigma && (i < xvec[0].Nelements); i++, x++, y++, z++, zf++) {
+      dZ = (*z - *zf);
+      if (fabs(dZ) > maxsigma) {
+	mask[i] = TRUE;
+	Nmask ++;
+      } else {
+	mask[i] = FALSE;
+      }	
+    }
+    if (VERBOSE) gprint (GP_ERR, "pass: %d, Nmask: %d\n", N, Nmask);
+  }
+
+  if (!Quiet) gprint (GP_ERR, "z = ");
+  for (N = ny = 0; ny < nterm; ny++) {
+    for (nx = 0; nx < nterm - ny; nx++, N++) {
+      sprintf (name, "CX%dY%d", nx, ny);
+      set_variable (name, b[N][0]);
+      if (!Quiet) gprint (GP_ERR, "%f x^%d y^%d  ", b[N][0], nx, ny);
+    }
+  }
+  sprintf (name, "Cnn");
+  set_variable (name, (double) order);
+  
+  if (!Quiet) gprint (GP_ERR, "\n");
+
+  /* free internal data */
+  free (zfit);
+  free (mask);
+
+  for (i = 0; i < wterm; i++) {
+    free (c[i]);
+    free (b[i]);
+  }
+  free (b);
+  free (c);
+
+  for (i = 0; i < mterm; i++) {
+    free (s[i]);
+  }
+  free (s);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/gaussdeviate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/gaussdeviate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/gaussdeviate.c	(revision 22322)
@@ -0,0 +1,61 @@
+# include "data.h"
+
+int gaussdeviate (int argc, char **argv) {
+  
+  int i, Npts;
+  double mean, sigma;
+  Vector *vec;
+
+  if (argc != 5) goto usage;
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  Npts = atoi (argv[2]);
+  mean = atof (argv[3]);
+  sigma = atof (argv[4]);
+
+  vec[0].Nelements = Npts;
+  REALLOCATE (vec[0].elements, float, Npts);
+
+  gauss_init (2048);
+  for (i = 0; i < Npts; i++) {
+    vec[0].elements[i] = rnd_gauss (mean, sigma);
+  }
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "USAGE: gaussdeviate (vector) Npts mean sigma\n");
+  return (FALSE);
+    
+}
+
+double int_gauss (int i);
+
+int gaussintegral (int argc, char **argv) {
+  
+  int i, Npts;
+  double mean, sigma;
+  Vector *vec;
+
+  if (argc != 5) goto usage;
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  Npts = atoi (argv[2]);
+  mean = atof (argv[3]);
+  sigma = atof (argv[4]);
+
+  vec[0].Nelements = Npts;
+  REALLOCATE (vec[0].elements, float, Npts);
+
+  gauss_init (Npts);
+  for (i = 0; i < Npts; i++) {
+    vec[0].elements[i] = int_gauss (i);
+  }
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "USAGE: gaussintegral Npts mean sigma\n");
+  return (FALSE);
+    
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/gaussj.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/gaussj.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/gaussj.c	(revision 22322)
@@ -0,0 +1,69 @@
+# include "data.h"
+
+int gaussjordan (int argc, char **argv) {
+  
+  float *m, *v;
+  double **a, **b;
+  int i, j, N, status, QUIET;
+  Vector *B;
+  Buffer *A;
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) goto usage;
+
+  if ((A = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);    
+  if ((B = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  N = B[0].Nelements;
+  if (A[0].matrix.Naxis[0] != N) goto usage;
+  if (A[0].matrix.Naxis[1] != N) goto usage;
+  
+  ALLOCATE (a, double *, N);
+  ALLOCATE (b, double *, N);
+  for (i = 0; i < N; i++) {
+    ALLOCATE (a[i], double, N);
+    ALLOCATE (b[i], double, 1);
+  }
+
+  m = (float *) A[0].matrix.buffer;
+  v = B[0].elements;
+  for (i = 0; i < N; i++) {
+    for (j = 0; j < N; j++) {
+      a[i][j] = m[i+j*N];
+    }
+    b[i][0] = v[i]; 
+  }
+
+  status = dgaussjordan (a, b, N, 1);
+
+  for (i = 0; i < N; i++) {
+    for (j = 0; j < N; j++) {
+       m[i+j*N] = a[i][j];
+    }
+    v[i] = b[i][0]; 
+  }
+
+  for (i = 0; i < N; i++) {
+    free (a[i]);
+    free (b[i]);
+  }
+  free (a);
+  free (b);
+
+  if (!status && !QUIET) {
+      gprint (GP_ERR, "failure in matrix solution\n");
+  }
+  return (status);
+
+ usage:
+  gprint (GP_ERR, "USAGE: gaussj A B\n");
+  gprint (GP_ERR, "  solves Ax = B, returns 1/A in A and x in B\n");
+  gprint (GP_ERR, "  A must be square, B same dimensions\n");
+  return (FALSE);
+    
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/grid.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/grid.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/grid.c	(revision 22322)
@@ -0,0 +1,203 @@
+# include "data.h"
+
+int grid (int argc, char **argv) {
+  
+  int j, kapa, N, MinorTick, MajorTick;
+  Vector Xvec, Yvec;
+  double range, lrange, factor, mantis, fmantis, power, major, minor, first, next;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  MajorTick = TRUE;
+  MinorTick = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    MinorTick = TRUE;
+  }
+
+  if (argc > 1) {
+    gprint (GP_ERR, "USAGE: grid [-n graph]\n");
+    return (FALSE);
+  }
+
+  N = 0;
+  Xvec.Nelements = 200;
+  Yvec.Nelements = 200;
+  ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+
+  major = minor = 1;
+  range = graphmode.xmax - graphmode.xmin;
+  lrange = log10(MAX(fabs(range), 1e-30));
+  factor = (int) (lrange);
+  if (lrange < 0) { factor -= 1; }
+  mantis = lrange - factor;
+  power = pow(10.0, factor);
+  fmantis = pow(10.0, mantis);
+  if ((fmantis >= 1.0) && (fmantis <=  2.0)) {
+    major = 0.5 * power;
+    minor = 0.1 * power;
+  }
+  if ((fmantis > 2.0) && (fmantis <=  4.0)) {
+    major = 1.0 * power;
+    minor = 0.2 * power;
+  }
+  if ((fmantis > 4.0) && (fmantis <=  6.0)) {
+    major = 1.0 * power;
+    minor = 0.5 * power;
+  }
+  if ((fmantis > 6.0) && (fmantis <=  10.0)) {
+    major = 2.0 * power;
+    minor = 0.5 * power;
+  }
+  if (graphmode.xmin > 0)
+    first = minor + minor*((int)(graphmode.xmin/minor));
+  else 
+    first = -minor + minor*((int)(graphmode.xmin/minor));
+  if (minor*((int)(graphmode.xmin/minor)) == graphmode.xmin) {
+    first = graphmode.xmin;
+  }
+  
+  for (j = 0, next = first; next <= graphmode.xmax; j++) {
+    if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major))) {
+      if (MajorTick) {
+	/* major tick */
+	Xvec.elements[N] = next;
+	Yvec.elements[N] = graphmode.ymin;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+	Xvec.elements[N] = next;
+	Yvec.elements[N] = graphmode.ymax;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+      }
+    } else {
+      if (MinorTick) {
+	/* minor tick */
+	Xvec.elements[N] = next;
+	Yvec.elements[N] = graphmode.ymin;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+	Xvec.elements[N] = next;
+	Yvec.elements[N] = graphmode.ymax;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+      }
+    }
+    next += minor;
+  }
+
+  range = graphmode.ymax - graphmode.ymin;
+  lrange = log10(MAX(fabs(range), 1e-30));
+  factor = (int) (lrange);
+  if (lrange < 0) { factor -= 1; }
+  mantis = lrange - factor;
+  power = pow(10.0, factor);
+  fmantis = pow(10.0, mantis);
+  if ((fmantis >= 1.0) && (fmantis <=  2.0)) {
+    major = 0.5 * power;
+    minor = 0.1 * power;
+  }
+  if ((fmantis > 2.0) && (fmantis <=  4.0)) {
+    major = 1.0 * power;
+    minor = 0.2 * power;
+  }
+  if ((fmantis > 4.0) && (fmantis <=  6.0)) {
+    major = 1.0 * power;
+    minor = 0.5 * power;
+  }
+  if ((fmantis > 6.0) && (fmantis <=  10.0)) {
+    major = 2.0 * power;
+    minor = 0.5 * power;
+  }
+  if (graphmode.ymin > 0)
+    first = minor + minor*((int)(graphmode.ymin/minor));
+  else 
+    first = -minor + minor*((int)(graphmode.ymin/minor));
+  if (minor*((int)(graphmode.ymin/minor)) == graphmode.ymin) {
+    first = graphmode.ymin;
+  }
+  
+  for (j = 0, next = first; next <= graphmode.ymax; j++) {
+    if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major))) {
+      if (MajorTick) {
+	/* major tick */
+	Xvec.elements[N] = graphmode.xmin;
+	Yvec.elements[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+	Xvec.elements[N] = graphmode.xmax;
+	Yvec.elements[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+      }
+    } else {
+      if (MinorTick) {
+	/* minor tick */
+	Xvec.elements[N] = graphmode.xmin;
+	Yvec.elements[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+	Xvec.elements[N] = graphmode.xmax;
+	Yvec.elements[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+      }
+    }
+    next += minor;
+  }
+
+  Xvec.Nelements = Yvec.Nelements = N;
+  graphmode.style = 2; /* points */
+  graphmode.ptype = 100; /* connect a pair */
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/gridify.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/gridify.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/gridify.c	(revision 22322)
@@ -0,0 +1,82 @@
+# include "data.h"
+
+int gridify (int argc, char **argv) {
+
+  int i, Nx, Ny, Xb, Yb, Normalize, N;
+  float Xmin, Xmax, dX, Ymin, Ymax, dY;
+  float *buf, *val, *x, *y, *z;
+  int *Nval;
+  Buffer *bf;
+  Vector *vx, *vy, *vz;
+
+  Normalize = TRUE;
+  if ((N = get_argument (argc, argv, "-raw"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = FALSE;
+  }
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: gridify x y z buffer Xmin Xmax dX Ymin Ymax dY\n");
+    return (FALSE);
+  }
+  
+  if ((vx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vz = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((bf = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (vx[0].Nelements != vy[0].Nelements) return (FALSE);
+  if (vx[0].Nelements != vz[0].Nelements) return (FALSE);
+
+  Xmin = atof (argv[5]);
+  Xmax = atof (argv[6]);
+  dX   = atof (argv[7]);
+
+  Ymin = atof (argv[8]);
+  Ymax = atof (argv[9]);
+  dY   = atof (argv[10]);
+
+  Nx = (Xmax - Xmin) / dX + 1;
+  Ny = (Ymax - Ymin) / dY + 1;
+  
+  gfits_free_matrix (&bf[0].matrix);
+  gfits_free_header (&bf[0].header);
+  CreateBuffer (bf, Nx, Ny, -32, 0.0, 1.0);
+  strcpy (bf[0].file, "(empty)");
+
+  ALLOCATE (val, float, Nx*Ny);
+  bzero (val, Nx*Ny*sizeof(float));
+  ALLOCATE (Nval, int, Nx*Ny);
+  bzero (Nval, Nx*Ny*sizeof(int));
+
+  x = vx[0].elements;
+  y = vy[0].elements;
+  z = vz[0].elements;
+  for (i = 0; i < vx[0].Nelements; i++, x++, y++, z++) {
+    Xb = (*x - Xmin) / dX;
+    Yb = (*y - Ymin) / dY;
+    if (Xb >= Nx) continue;
+    if (Yb >= Ny) continue;
+    val[Xb + Yb*Nx] += *z;
+    Nval[Xb + Yb*Nx]++;
+  }
+
+  buf = (float *) bf[0].matrix.buffer;
+  for (i = 0; i < Nx*Ny; i++) {
+    if (Normalize) {
+      if (Nval[i] == 0) {
+	buf[i] = 0;
+	continue;
+      }
+      buf[i] = val[i] / Nval[i];
+    } else {
+      buf[i] = val[i];
+    }
+  }
+
+  free (val);
+  free (Nval);
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/grow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/grow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/grow.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "data.h"
+
+int grow (int argc, char **argv) {
+  
+  int i, j, nx, ny, npix, nsum, Nx, Ny;
+  float *input, *output;
+  Buffer *in;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: grow (input) (npix)\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  npix = atoi (argv[2]);
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+  ALLOCATE (output, float, Nx*Ny);
+  memset (output, 0, Nx*Ny*sizeof(float));
+
+  input = (float *) in[0].matrix.buffer;
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+      nsum = 0;
+      for (ny = -1; ny <= +1; ny++) {
+	if (j + ny < 0) continue;
+	if (j + ny >= Ny) continue;
+	for (nx = -1; nx <= +1; nx++) {
+	  if (i + nx < 0) continue;
+	  if (i + nx >= Nx) continue;
+	  if (!nx && !ny) continue;
+	  if (input[i + nx + Nx*(j + ny)] == 0) continue;
+	  nsum ++;
+	}
+      }
+      if (nsum >= npix) {
+	output[i + j*Nx] = 1;
+      } else {
+	output[i + j*Nx] = input[i + j*Nx];
+      }
+    }
+  }
+  
+  free (in[0].matrix.buffer);
+  in[0].matrix.buffer = (char *) output;
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/Graphics
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/Graphics	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/Graphics	(revision 22322)
@@ -0,0 +1,20 @@
+
+  Kapa, the graphics window.
+
+  Kapa is the program which Status uses to display plots on an X
+terminal.  There are actually 5 graphics windows available to Status,
+though several functions write to specific windows by default.  All
+plots of sky coordinates are displayed on window 0, while 
+functions which plot other types of data use window 1.  Most functions
+have an option -n which allows the user to specify which Kapa window.
+If it is not specified, the last window used will receive the action.
+
+  Below are the available Resources.  
+
+  kii*Foreground:			white
+  kii*Background:			maroon
+  kii*geometry:				540x540+10+10
+
+  See Also: box, clear, limits, plot
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/Kii
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/Kii	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/Kii	(revision 22322)
@@ -0,0 +1,57 @@
+
+
+  Kii (= "picture" in Hawaiian) is the program which Mana uses to
+display images on an X terminal.  The window consists of a main region
+where the image is displayed, a small "zoom box" where a magnified
+view of the region around the cursor is shown, a colorbar showing the
+current color map across the top of the window, a status box where the
+coordinates and pixel values are displayed, and several buttons.
+Clicking with the mounse in the different regions produces different
+effects.
+
+  Image Window:  Clicking with the left mouse button (mouse-1)
+recenters the image at the specified location.  Clicking with the
+right button (mouse-3) also recenters, but increases the zoom factor
+by one.  Clicking with the middle button centers and decreases the
+zoom factor.  
+
+  Colorbar: Clicking and draging with the left button (mouse-1) alters
+the color mapping.  Moving left-to-right slides the center of the
+color scale with the mouse.  Moving up-and-down squeezes or expands
+the color scale.  The middle button resets the color mapping.
+Clicking and dragging side-to-side with the right button expands or
+squeezes the color mapping.  **Note: the dynamic colormap cannot be
+used with a 24 or 32 bit visual.  The standard Linux XFree86 server
+does not support 8 bit visuals in 24 or 32 bit/pixel mode.  In this
+case, the Kii window must resort to a static colormap with the
+colorbar disabled.  In Linux, it is possible to run startx with the
+following command-line options to force the 8 bit/pixel mode:
+# startx -- -bpp 8 
+This assumes that you have an 8bpp Display mode set in the XF86Config
+file.  If not, you can just duplicate the 24 or 32 bpp Display modes
+which will enable this option.
+
+  Buttons:
+    PS: this button creates a PostScript version of the image.
+    Grey: this button makes the colormap a greyscale.
+    Rainbow: this button makes the colormap a rainbow.
+    Puns: this button make the colormap a blue and yellow mapping.
+    Recenter: this button recenters the image and resets the zoom.
+
+  Kii also recognizes several X Resource values:
+
+  Below are the available Resources (with Gene's default values).  Unlike
+most standard X programs, the geometry resource specifies the size of
+the Image Window, not the borders of the entire window.  Valid values
+for the colormap are: greyscale, grayscale (photonegative), -greyscale,
+-grayscale (photopositive), Puns, GoBears (buff 'n blue colormap),
+Rainbow (a rainbow map).  
+
+  kii*Foreground:			white
+  kii*Background:			maroon
+  kii*Colormap:				greyscale
+  kii*geometry:				540x540+10+10
+  
+
+  See Also: tv, cursor
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/Math
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/Math	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/Math	(revision 22322)
@@ -0,0 +1,58 @@
+
+  Opihi shell math operations:
+
+  Mana will evaluate scalar (1D) arithmetic expresions in two
+situations.  To set a variable equal to a math operation, use the
+syntax is:
+
+  $foo = expression
+
+where "expression" is an arithmetic expression on numbers and
+variables, including a variety of functions (see below).  After
+performing the arithmetic and setting the variable to the resulting
+value, mana returns the prompts without performing any further
+command.  Mana will also evaluate any arithmetic expression enclosed
+in curly brackets before attempting to perform the given command.  For
+example, if the variables $MEAN and $SIGMA contain the mean and
+standard deviation of an image, it would be possible to display the
+image with a zero and range based on $MEAN and $SIGMA like this:
+
+  tv image {$MEAN - 1.5*$SIGMA} {5*$SIGMA}
+
+
+The two expressions in curly brackets are evaluated and the resulting
+numbers passed as arguments to the "tv" command.
+
+  See Also:  Variables, tv
+
+
+list of valid math operations:
+
+  binary operators:
+  * (times)
+  / (divided by)
+  + (plus)
+  - (minus)
+  ^ (to the power of: 2^3 = 8)
+
+  unary operators:
+  exp (e to the power of)
+  ten (10 to the power of)
+  ln  (natural logarithm)
+  log (log base 10)
+
+  angles in radians:
+  sin (sine)
+  cos (cosine)
+  tan (tangent)
+  asin (arcsin)
+  acos (arccos)
+  atan (arctan)
+
+  angles in degrees:
+  dsin (sine)
+  dcos (cosine)
+  dtan (tangent)
+  dasin (arcsin)
+  dacos (arccos)
+  datan (arctan)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/applyfit
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/applyfit	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/applyfit	(revision 22322)
@@ -0,0 +1,10 @@
+
+  applyfit x y
+
+  apply the results of a polynomial fit (stored in variables 
+  $C0, $C1, etc, with $Cn representing the order of the fit).
+  The second vector will contain the function value at the 
+  corresponding x coordinates of the first vector.
+
+  See also: fit
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/box
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/box	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/box	(revision 22322)
@@ -0,0 +1,11 @@
+
+  box [-n Nwindow]
+
+  draw a coordinate box on the current kapa window, or on the window
+specified by -n (0 - 4).
+
+  See also: Kapa
+
+
+
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/buffers
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/buffers	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/buffers	(revision 22322)
@@ -0,0 +1,9 @@
+
+  buffers
+
+  buffers gives information about the currently allocated buffers.  
+A * next to the filename means the data has been altered from the
+originally read in image.
+
+  See also: memory
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/center
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/center	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/center	(revision 22322)
@@ -0,0 +1,10 @@
+
+  center (xpix) (ypix) [magnification]
+
+  "center" centers the Kii window at the specified pixel coordinates.
+Specifying a magnification will change the image scale to the
+specified magnification.  If you do not specify a magnification
+(default), the image scale will remain the same.
+
+  See also: Kii, tv
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/clear
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/clear	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/clear	(revision 22322)
@@ -0,0 +1,5 @@
+
+   clear
+
+   clear the Kapa window
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/clip
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/clip	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/clip	(revision 22322)
@@ -0,0 +1,8 @@
+
+  clip (buffer) [min Vmin max Vmax] [-inf val] [-nan val]
+
+  clip values in an image.  A min/max clip can be applied, 
+  in which case all values above max are set to Vmax and all
+  below min are set to Vmin.  -nan and -inf flags will set all 
+  instances of NaN or any non-finite values to the given value.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/close
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/close	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/close	(revision 22322)
@@ -0,0 +1,14 @@
+
+
+   close [-n win] [-g Kapa win] [-i Kii win]
+
+   Closes the specified graphics window
+
+   Default: the most recently activated window (only works
+    for the first call)
+
+   [win]: the index of the window to close
+
+   -n: any graphics or plotting window
+   -g: the Kapa plotting window
+   -i: the Kii image window
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/concat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/concat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/concat	(revision 22322)
@@ -0,0 +1,7 @@
+
+   concat v1 v2
+
+   Concatenates the values of vector v1 to the end of vector
+    v2, increasing the length of v2.
+
+   See also: subset, set
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/contour
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/contour	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/contour	(revision 22322)
@@ -0,0 +1,13 @@
+
+  contour <buffer> (overlay) level [Npix]
+
+  "contour" makes a contour plot from the given <buffer> at the
+specified level.  If Npix is specified, the image is rebinned by a
+factor of Npix in each direction.  The contour is drawn as a series of
+lines on the specified overlay.  The new lines are added to any
+existing shapes.  (Use "erase" first to erase all objects if desired).
+
+Valid overlays may be: 0, red, 1, green, 2, blue, 3, yellow.
+
+  See also: load, save, erase
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/create
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/create	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/create	(revision 22322)
@@ -0,0 +1,9 @@
+
+   create (vector) (start) (end) [delta]
+
+   Create a vector of uniformly spaced values, starting at
+   (start) and going to (end).  By default the spacing is 1, but
+   may be chosen with the [delta] option.
+
+   Note: A vector cannot be created if an image buffer already
+    exists with the same name
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/cumulative
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/cumulative	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/cumulative	(revision 22322)
@@ -0,0 +1,8 @@
+
+
+   cumulative (invec) (outvec)
+
+   Creates a vector (outvec) where the element N is the sum of the elements
+    0-N of (invec)
+
+   Note: The final element of (outvec) is always 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/cursor
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/cursor	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/cursor	(revision 22322)
@@ -0,0 +1,13 @@
+
+  cursor
+
+  place cursor coordinates (and values ??) into Mana variables.  to do
+this, type cursor, then place the cursor on the desired spot in the
+Ki'i window and type a digit (0 - 9).  The coordinates of the cursor
+are then placed in the variables $Xn and $Yn (where n is the digit you
+typed).  To exit the cursor mode, type "q" (or "Q") in the Ki'i
+window.  
+
+  See Also: Kii, tv, center
+
+ 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/cut
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/cut	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/cut	(revision 22322)
@@ -0,0 +1,9 @@
+
+   cut buffer <X vector> <Y vector> <X|Y> sx sy nx ny
+
+   take a cut from an image and place it in a pair of vectors.
+   the <X vector> gets the pixel coordinate in the given direction,
+   the <Y vector> gets the pixel values.  <X|Y> specifies the 
+   direction of the cut.  the region sx, sy, nx, ny specifies the
+   region for the cut, with summation in the cross-direction.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/datafile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/datafile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/datafile	(revision 22322)
@@ -0,0 +1,6 @@
+
+   datafile (filename)
+
+   define a data file for subsequent vector reads.
+
+   See also: read
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/delete
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/delete	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/delete	(revision 22322)
@@ -0,0 +1,10 @@
+
+  delete (buffer/vector/variable)
+
+  Delete the named buffer/vector/variable.
+
+  Warning: no second chances are given!
+
+  Note: Delete will remove the buffer/vector of a
+   given name first, then delete the variable of
+   the same name upon the second call.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/dimendown
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/dimendown	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/dimendown	(revision 22322)
@@ -0,0 +1,7 @@
+
+
+   dimendown (buffer) (vector) [-x/-y]
+
+   Compresses an image into a vector
+
+   The vector can be given either the x or y coordinates of the image
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/dimenup
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/dimenup	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/dimenup	(revision 22322)
@@ -0,0 +1,5 @@
+
+
+   dimenup (vector) (buffer) (Nx) (Ny)
+
+   Expands a vector into an image with dimensions Nx,Ny
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/dot
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/dot	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/dot	(revision 22322)
@@ -0,0 +1,7 @@
+
+
+   dot (x) (y)
+
+   Plots a single point
+
+   Uses the same style options as plot
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/erase
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/erase	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/erase	(revision 22322)
@@ -0,0 +1,8 @@
+
+  erase (overlay)
+
+  "erase" erases all objects on the specified overlay.  
+  Valid overlays may be: all, red, green, blue, yellow.
+
+  See also: load, save, contour
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/extract
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/extract	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/extract	(revision 22322)
@@ -0,0 +1,12 @@
+
+  extract <from> <to> sx sy nx ny sx sy nx ny
+
+  extract takes a portion of an image (buffer <from>) and creates a
+new image (buffer <to>).  The source region is defined by the first
+set of (sx sy nx ny), the resulting image and the location of the
+extracted image are defined by the second (sx sy nx ny).  This allows
+a portion of an image to be overlayed at a particular location in a
+larger image.
+
+  See also: Kii, tv
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/fit
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/fit	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/fit	(revision 22322)
@@ -0,0 +1,15 @@
+
+   fit (-q) x y (order) (-dy wt) (-clip sig N)
+
+   perform a lease-square polynomial fit to the data defined 
+   by vectors x and y.  the coefficients are placed in the variables
+   $C0, $C1, ..., and the order is placed in $Cn.
+
+   order: order of fit
+   -q: quiets the fit (no output to window)
+   -dy: take into account the error vector wt
+   -clip: remove outliers beyond sig*sigma from the fit;
+           perform N iterations of this
+
+   See also: applyfit
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/grid
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/grid	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/grid	(revision 22322)
@@ -0,0 +1,6 @@
+
+   grid (overlay) (buffer)
+
+   draw a coordinate grid for the given buffer in the 
+   given overlay in Kii.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/header
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/header	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/header	(revision 22322)
@@ -0,0 +1,8 @@
+
+   header (buffer)
+
+   print the header information for the given buffer.  The header
+   information is Meta-information associated with an image.  It is
+   well-defined for the FITS images, but may not exist for some of the
+   other possible data types.  
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/histogram
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/histogram	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/histogram	(revision 22322)
@@ -0,0 +1,8 @@
+
+   histogram <buffer> <x> <y> [-region sx sy nx ny] [-range min max]
+
+   calculate a histogram of the image pixel values in the given
+   buffer, optionally constrained to the given region, with optional
+   max and min values.  the results are placed in the vectors x and y,
+   which contain the pixel values and the number of occurences.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/integrate
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/integrate	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/integrate	(revision 22322)
@@ -0,0 +1,10 @@
+
+   integrate (x) (y) (start) (end)
+
+   Perform an integration of (y) as a function of (x) from (start) to (end)
+
+   The result is placed into the variable: $sum
+
+   Caution!: Program uses the front end of each x bin to approximate the integral.
+    Very coarse bins (delta x = 1) over small regions (x=1->5) can introduce
+    errors as large as 20%. Fine bins are reccommended.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/interpolate
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/interpolate	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/interpolate	(revision 22322)
@@ -0,0 +1,5 @@
+
+   interpolate (xin) (yin) (xout) (yout)
+
+   Performs an interpolation of (xin) and (yin) onto the new grid (xout),
+    producing the corresponding y values in (yout)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/kern
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/kern	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/kern	(revision 22322)
@@ -0,0 +1,4 @@
+
+   kern buffer (kernel file or -)
+
+   apply a 3x3 kernel to the image.  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/keyword
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/keyword	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/keyword	(revision 22322)
@@ -0,0 +1,11 @@
+
+  keyword <buffer> (KEYWORD) [variable] [-w value]
+
+  "keyword" extracts the specified keyword from the header of the
+   specified buffer.  If a third word is listed, the value of the
+   keyword is stored in a variable with the given name.  If the -w
+   option is given, the value is written to the header keyword.
+
+   The header Meta-data is well-defined for FITS, but not necessarily
+   for other data types.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/labels
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/labels	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/labels	(revision 22322)
@@ -0,0 +1,23 @@
+
+   labels 
+
+   write a label on the Kapa window.  there are many options:
+
+   -fn font size -- define the font (may be times, helvetica, courier)
+
+   -x "a long line" -- label on the bottom x-axis
+
+   +x "a long line" -- label on the top x-axis
+
+   -y "a long line" -- label on the left y-axis
+
+   +y "a long line" -- label on the right y-axis
+
+   -ul "a long line" -- label on the upper left of plot
+
+   -ll "a long line" -- label on the lower left of plot
+
+   -ur "a long line" -- label on the upper right of plot
+
+   -lr "a long line" -- label on the lower right of plot
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/limits
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/limits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/limits	(revision 22322)
@@ -0,0 +1,12 @@
+
+   limits min max min max
+   limits x y
+   limits x min max
+   limits min max y
+   
+   set Kapa plot limits.  The x and y axis limits may be explicitly
+   set (first example), or they may be assigned based on the range of
+   values in a pair of vectors (second examples), or one range may be
+   explicit and the other range assigned from a vector.
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/load
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/load	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/load	(revision 22322)
@@ -0,0 +1,20 @@
+
+   load (overlay) (filename)
+
+  "load" reads a file with objects in SAOimage style into the
+  specified overlay.  If the specified overlay is already used, the
+  new objects are added to the old.  If you do not want this, use
+  "erase" to erase all objects in the overlay first.  Valid overlays
+  may be: 0, red, 1, green, 2, blue, 3, yellow.
+
+Examples of SAOimage style objects:
+BOX   300  200  100 50  : draws a box centered at pixel 300,200 that
+			  is 100 pixels wide, and 50 pixels high
+
+CIRCLE 500 400 200 : draws a circle centered at pixel 500,400 with a
+                     radius of 200 pixels
+
+LINE 100 100 200 200 : draws a line from (100,100) with length (200,200)
+
+  See also: save, erase, contour
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/peak
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/peak	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/peak	(revision 22322)
@@ -0,0 +1,12 @@
+
+   peak [-q] (x) (y) [startx endx]
+
+   Determines the peak of the y vector between starx and endx
+    or between the extrema of the x vector (default)
+
+   -q: quiets the script (no output to the window)
+
+   Outputs:
+    $peakval: Value of the peak
+    $peakpos: X value corresponding to the peak value
+    $peaknum: Index of the peak value
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/periodogram
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/periodogram	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/periodogram	(revision 22322)
@@ -0,0 +1,6 @@
+
+   periodogram (time) (flux) (minP) (maxP) (period) (power)
+
+   creates a periodogram from the vectors (time) and (flux)
+    between (minP) and (maxP) and outputs the results to
+    (period) and (power)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/plot
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/plot	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/plot	(revision 22322)
@@ -0,0 +1,13 @@
+
+   plot <x> <y> [-dx dx] [-dy dy] [+dx dx] [+dy dy]
+
+   plot a pair of vectors.  the options allow for errorbars.  If only
+   one of -dy or +dy is given, the given vector is used for the
+   errorbar.  If both are given, -dy defines the lower errorbar, while
+   +dy defines the upper errorbar.  This allows for assymetric errors
+   in a trivial fashion.  The same applies to the -dx, +dx values.  
+
+   The vectors are plotted with the current plot style.  See style for
+   all of the options.
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/ps
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/ps	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/ps	(revision 22322)
@@ -0,0 +1,16 @@
+
+   ps [-name file.ps] [-g | -i] [-n device] [-raw] [-noscale] [-newpage]
+
+   create a PostScript file from the current graphics window (Kii or
+   Kapa).  The target graphics window may be selected with the -g or
+   -i options (to choose between Kii or Kapa) in combination the -n
+   option (to specify which instance).
+
+   With no additional arguments, Kii produces an encapsulated
+   postscript file called Ximage.ps, while Kapa produces a file called
+   Xgraph.ps.  The output name may be set with the -name (file.ps)
+   option. 
+
+   By default, the output is scaled to fit on a letter page 
+
+   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queuedelete
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queuedelete	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queuedelete	(revision 22322)
@@ -0,0 +1,4 @@
+
+   queuedelete (name)
+
+   Deletes the entire queue (name)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queueinit
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queueinit	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queueinit	(revision 22322)
@@ -0,0 +1,4 @@
+
+   queueinit (name)
+
+   Creates a queue (name) with an initial size of 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queuepush
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queuepush	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queuepush	(revision 22322)
@@ -0,0 +1,8 @@
+
+   queuepush (queue) (value) [-key N] [-uniq] [-replace]
+
+   Pushes (value) onto the back of (queue)
+
+   -key N:
+   -uniq:
+   -replace:
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queuesize
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queuesize	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/queuesize	(revision 22322)
@@ -0,0 +1,5 @@
+
+   queuesize (name) [-var variable]
+
+   Returns the size of the queue (name), either to variable
+    or to the window (default)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/rd
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/rd	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/rd	(revision 22322)
@@ -0,0 +1,10 @@
+
+  rd (buffer) (filename)
+
+  "rd" reads a file into the specified buffer, creating the buffer if
+  none exists.  A buffer name may consist of any letters, numbers, and
+  some limited other characters.  However, the name may not start with
+  a digit.
+
+  See also: wd, tv
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/read
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/read	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/read	(revision 22322)
@@ -0,0 +1,10 @@
+
+   read vect col [vect col, ...]
+
+   read vector values from a file.  An arbitrary number of vectors can
+   be specified, and the (whitespace-separated) field number given for
+   each vector.  Data is read from the file defined by "datafile".
+
+   See also: datafile
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/rebin
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/rebin	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/rebin	(revision 22322)
@@ -0,0 +1,9 @@
+
+  rebin <from> <to> scale
+
+  "rebin" rebins the specified buffer by the given scale factor and
+  places the result in the <to> buffer.  Negative integer values imply
+  expansion, positive numbers imply compression.
+
+  See also: extract
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/resize
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/resize	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/resize	(revision 22322)
@@ -0,0 +1,6 @@
+
+   resize Nx Ny
+
+   change the size of the Kii window to have image dimensions Nx x
+   Ny.  
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/rotate
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/rotate	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/rotate	(revision 22322)
@@ -0,0 +1,10 @@
+
+   rotate (buffer) (angle) [-center x y]
+
+   rotate the buffer by the given angle.  The angle may also be one of
+   the following special words:
+
+   -flipx - flip in the x direction
+   -flipy - flip in the y direction
+
+   if the optional center is given the rotation is about this position.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/save
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/save	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/save	(revision 22322)
@@ -0,0 +1,16 @@
+  save (overlay) (filename)
+
+  "save" stores the objects from the specified overlay into the named
+file.  Valid overlays may be: 0, red, 1, green, 2, blue, 3, yellow.
+
+Examples of SAOimage style objects:
+BOX   300  200  100 50  : draws a box centered at pixel 300,200 that
+			  is 100 pixels wide, and 50 pixels high
+
+CIRCLE 500 400 200 : draws a circle centered at pixel 500,400 with a
+                     radius of 200 pixels
+
+LINE 100 100 200 200 : draws a line from (100,100) with length (200,200)
+
+  See also: load, erase, contour
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/set
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/set	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/set	(revision 22322)
@@ -0,0 +1,41 @@
+
+  set (buffer) = expression..
+
+  perform math operations on images.  there are several allowed
+  operators.  the standard math functions are + - * /.  Exponentiation
+  is performed with ^ (set c = a ^ b).  There are several "logic"
+  operators which need some explanation.
+
+  set c = a < b  -- c is 1 if a < b, 0 otherwise
+  set c = a > b  -- c is 1 if a > b, 0 otherwise
+  set c = m1 & m1 - c is 1 if m1 AND m2 true (non-zero is true)
+  set c = m1 | m1 - c is 1 if m1 OR m2 true (non-zero is true)
+  set c = a << b -- c is the minimum of a and b
+  set c = a >> b -- c is maximum of a and b
+
+  complex operations may be performed:
+
+  set c = a*(a < b) + c*((a < c) | (c < d))
+
+  unary operators also exist:
+
+  exp(a) - e to the power of a
+  ten(a) - 10 to the power of a
+  ln(a) - log base e of a
+  log(a) - log base 10 of a
+  sqrt(a) - square root of a
+  sin(a) - sin of a
+  cos(a) - sin of a
+  tan(a) - sin of a
+  not(a) - logical inverse of a
+  abs(a) - absolute value of a
+  int(a) - integer value of a
+
+  Examples:
+  
+  set a = b + 10  (add 10 to every pixel in b and put the answer in a)
+  set a = a / b   (divide every pixel in a by the corresponding pixel
+                     b and put the answer in a)
+  set b = 10 / a  (divide 10 by every pixel in a and put the answer
+                     in b)
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/shift
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/shift	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/shift	(revision 22322)
@@ -0,0 +1,6 @@
+
+   shift buffer dx dy
+
+   shift image by dx,dy pixels (may be fractional values).
+
+   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/stats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/stats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/stats	(revision 22322)
@@ -0,0 +1,10 @@
+
+  stats (buffer) (x) (y) (nx) (ny)
+
+  report the statistics on a portion of an image.  (x) and (y) specify
+the starting corner of the region, (nx) and (ny) specify the width and
+height of the region.  stats reports the mean, sigma, and number of
+pixels in the region.
+
+  See Also: ??
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/style
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/style	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/style	(revision 22322)
@@ -0,0 +1,36 @@
+
+   style -- many options
+
+   define or check the Kapa plotting style.  without any command-line arguments,
+   style prints the current style.  
+
+   option definitions:
+
+   -n	window number (0-4), can also be used to change active window.
+   -pt	point style: 
+		0 = filled box
+		1 = open box
+		2 = +
+		3 = X
+		4 = filled triangle
+		5 = blank
+		6 = open triangle 
+		7 = open circle
+		100 = connect pair of points
+   -x	plotting method:
+		0 = connect
+		1 = histogram
+		2 = points
+   -lt	line type:
+		0 = solid
+		1 = dashed
+		2 = dotted??
+   -lw	line weight (0-10)
+   -sz	point size
+   -c   color:
+	black, white, red, orange, 
+	yellow, green, blue, indigo, violet
+   -eb  errorbar with no top
+   +eb  errorbar with top
+
+   See also: plot, box
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/subset
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/subset	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/subset	(revision 22322)
@@ -0,0 +1,12 @@
+
+   subset vec = vec if (logic expression)
+
+   reduce the length of a vector based on logical expression.  The
+   logic expression is some function of vectors of equal length to the
+   main vector.
+
+   for example, we have vectors x and y of the same length:
+   
+   subset X = x if ((y > 100) | (x < 10))
+
+    
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/textline
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/textline	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/textline	(revision 22322)
@@ -0,0 +1,7 @@
+
+   textline x y (line) [-fn (font) size] [-rot angle]
+
+   write the string at the given coordinates and rotation,
+   in the given font.
+
+   (enclose long strings with spaces in quotes)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/tv
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/tv	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/tv	(revision 22322)
@@ -0,0 +1,13 @@
+
+  tv [-log] (buffer) [zero range]
+
+  display an image in the Ki'i window (X display program).  zero and
+  range specify the data values corresponding to the color mapping.
+  If they are not specified, the old values are used (default is 0,
+  1024).
+
+  If -log is specified the color mapping scale will be logarithmic.  
+
+
+  See Also: Kii, rd, cursor
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/unsign
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/unsign	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/unsign	(revision 22322)
@@ -0,0 +1,20 @@
+  unsign
+
+  "unsign" toggles the status of the UNSIGN mode.  Warning: This is a
+non-FITS standard implementation.  The FITS standard does not allow
+for unsigned integer data values, but many sites write CCD images with
+16 bit UNSIGNED data.  Mana looks for a FITS keyword "UNSIGN" to
+determine if an image which is read in has signed or unsigned
+integers.  However, since this is a non-standard concept, many sites
+do not use the UNSIGN flag, yet still write unsigned data.  If the
+user knows the data in the file is unsigned (ie, the user must have
+apriori knowledge -- a FITS no-no!), then the user should set the
+UNSIGN mode to true.  In the event that the UNSIGN mode is set, if a
+FITS file is encountered which contains integers, but which does not
+contain the UNSIGN keyword, Mana will make the default assumption that
+the data is unsigned.  If the SIGNED mode is set, Mana will assume all
+integer FITS files contain signed integers.  
+
+((( should be able to say "unsign <buffer>" to convert the status of a
+buffer to UNSIGN or to toggle the buffer's status, but not yet
+implemented)))
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/vectors
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/vectors	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/vectors	(revision 22322)
@@ -0,0 +1,5 @@
+
+   vectors
+
+   list the currently defined vectors and lengths
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/wd
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/wd	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/wd	(revision 22322)
@@ -0,0 +1,29 @@
+
+  wd (buffer) (filename) [BITPIX]
+
+  wd writes a buffer to the specified file in FITS format.  (You may
+need quotes around the filename if there are any /'s in the path).
+The FITS keyword BITPIX may be specified.  Valid values are 8, 16, 32
+(integer formats) and -32, -64 (floating point formats).  If BITPIX is
+unspecified, the original value of the image is used.  The image is
+converted blindly to the format and written out.  This may mean that
+values are rounded (integers), or wrapped by the range of the number
+of bits.  Also beware of the issue of signed vs unsigned images.
+
+  See Also: rd
+
+bitpix  bzero  bscale  file range     data range
+16      0      1       -32k : 32k     -32k : 32k
+16	32k    1       -32k : 32k        0 : 64k
+16	0      2       -32k : 32k     -64k : 64k
+16	0      0.001   -32k : 32k     -32. : 32.
+32	0      1       -2e9 : 2e9     -2e9 : 2e9
+8	0      1       -128 : 128     -128 : 128
+
+so for example, a file with a data range of -128k to +128k written
+with bitpix 16, bzero 0, bscale 1 will have all data outside -32k :
++32k wrapped back in that range.
+
+note: on write, bzero is subtracted from the data before the data is
+      divided by bscale.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/zplot
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/zplot	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/help/zplot	(revision 22322)
@@ -0,0 +1,7 @@
+
+   zplot x y z min max
+
+   plot the vector pair x and y as a series of points with size scaled
+   by the values in the vector z, with the smallest point having
+   values <= min and the largest having values >= max.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/histogram.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/histogram.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/histogram.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include "data.h"
+
+int histogram (int argc, char **argv) {
+  
+  int i, bin, Nbins;
+  float *V, start, end, delta;
+  Vector *xvec, *yvec;
+
+  if ((argc != 6) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: hist invec outvec start end [delta]\n");
+    return (FALSE);
+  }
+
+  delta = 1;
+  start = atof (argv[3]);
+  end   = atof (argv[4]);
+  if (argc == 6) delta = atof (argv[5]);
+ 
+  if ((start == end) || (delta == 0)) {
+    gprint (GP_ERR, "error in value: %f to %f, %f\n", start, end, delta);
+    return (FALSE);
+  }
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+  Nbins = (end - start) / delta;
+  /* number here should match number in create.c */
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  yvec[0].Nelements = Nbins;
+    
+  REALLOCATE (yvec[0].elements, float, yvec[0].Nelements);
+  bzero (yvec[0].elements, sizeof(float)*yvec[0].Nelements);
+  if (Nbins < 1) {
+      return (TRUE);
+  }
+
+  V = xvec[0].elements;
+  for (i = 0; i < xvec[0].Nelements; i++, V++) {
+    if (isnan(*V)) continue;
+    bin = MIN (MAX (0, (*V - start) / delta), Nbins - 1);
+    yvec[0].elements[bin] += 1.0;
+  }      
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/imcut.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/imcut.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/imcut.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "data.h"
+
+int imcut (int argc, char **argv) {
+  
+  int i, Nx, Ny, xi, yi, L;
+  double xs, ys, xe, ye, dX, dY;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+  float *V;
+
+  if (argc != 8) {
+    gprint (GP_ERR, "USAGE: cut <buffer> <X vector> <Y vector> xs ys xe ye\n");
+    return (FALSE);
+  }
+
+  if ((buf  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) goto missed;
+  if ((xvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) goto usage;
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) goto usage;
+ 
+  xs = atof (argv[4]);
+  ys = atof (argv[5]);
+  xe = atof (argv[6]);
+  ye = atof (argv[7]);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  if ((xs < 0) || (xs > Nx)) goto range;
+  if ((ys < 0) || (ys > Ny)) goto range;
+  if ((xe < 0) || (xe > Nx)) goto range;
+  if ((ye < 0) || (ye > Ny)) goto range;
+
+  dX = xe - xs;
+  dY = ye - ys;
+  L = hypot (dX, dY);
+  dX = dX / L;
+  dY = dY / L;
+
+  REALLOCATE (xvec[0].elements, float, MAX (L, 1));
+  REALLOCATE (yvec[0].elements, float, MAX (L, 1));
+  xvec[0].Nelements = L;
+  yvec[0].Nelements = L;
+
+  V = (float *)buf[0].matrix.buffer;
+  for (i = 0; i < L; i++) {
+    xi = xs + i*dX - 0.5;
+    yi = ys + i*dY - 0.5;
+    xvec[0].elements[i] = i;
+    yvec[0].elements[i] = V[xi + Nx*yi];
+  }
+
+  return (TRUE);
+
+ usage: 
+  gprint (GP_ERR, "USAGE: circstats <buffer> x y radius\n");
+  return (FALSE);
+
+ range:
+  gprint (GP_ERR, "ERROR: coordinates out of range\n");
+  return (FALSE);
+
+ missed:
+  gprint (GP_ERR, "ERROR: buffer not found\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/imhist.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/imhist.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/imhist.c	(revision 22322)
@@ -0,0 +1,127 @@
+# include "data.h"
+
+int imhist (int argc, char **argv) {
+  
+  int i, j, N, Nbins, Quiet;
+  int sx, sy, nx, ny, bin;
+  float *V, delta;
+  double max, min, dx;
+  Vector *vec1, *vec2;
+  Buffer *buf;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  delta = 0;
+  if ((N = get_argument (argc, argv, "-delta"))) {
+    remove_argument (N, &argc, argv);
+    delta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  min = max = 0.0;
+  if ((N = get_argument (argc, argv, "-range"))) {
+    remove_argument (N, &argc, argv);
+    min = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    max = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  sx = sy = 0.0;
+  nx = ny = 0.0;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    sx = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    sy = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    nx = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ny = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: histogram <buffer> <x> <y> [-region sx sy nx ny] [-range min max] [-delta binsize]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  
+  /* if either range is set to zero, use the rest of the chip */
+  if (nx == 0)
+    nx = buf[0].matrix.Naxis[0] - sx;
+  if (ny == 0)
+    ny = buf[0].matrix.Naxis[1] - sy;
+
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  if ((vec1 = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vec2 = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* unfortunately, we must do this in two passes:
+     first pass finds the max and min and defines the bin size
+     second pass counts the pixes in each bin 
+     */
+
+  if ((max == 0) && (min == 0)) {
+    max = min = *((float *)(buf[0].matrix.buffer) + sy*buf[0].matrix.Naxis[0] + sx);
+    gprint (GP_ERR, "sx: %d, sy: %d, first: %f\n", sx, sy, max);
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+      for (i = 0; i < nx; i++, V++) {
+	max = MAX (max, *V);
+	min = MIN (min, *V);
+      }
+    }
+  }
+
+  if (delta == 0) {
+    Nbins = 1024;
+    dx = (max - min) / Nbins;
+  } else {
+    dx = delta;
+    Nbins = (max - min) / dx;
+  }
+  if (Quiet) {
+    set_variable ("MIN", min);
+    set_variable ("MAX", max);
+    set_variable ("DX",  dx);
+  } else {
+    gprint (GP_LOG, "max %f, min %f, dx %f\n", max, min, dx);
+  }  
+
+  vec1[0].Nelements = Nbins + 1;
+  vec2[0].Nelements = Nbins + 1;
+  REALLOCATE (vec1[0].elements, float, vec1[0].Nelements);
+  bzero (vec1[0].elements, vec1[0].Nelements*sizeof(float));
+  REALLOCATE (vec2[0].elements, float, vec2[0].Nelements);
+  bzero (vec2[0].elements, vec2[0].Nelements*sizeof(float));
+  
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      bin = MAX (MIN (Nbins, (*V - min) / dx), 0);
+      vec2[0].elements[bin] += 1.0;
+    }
+  }
+  for (i = 0; i < Nbins + 1; i++, V++) {
+    vec1[0].elements[i] = i*dx + min;
+  }
+  
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/imsmooth.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/imsmooth.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/imsmooth.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "data.h"
+
+int imsmooth (int argc, char **argv) {
+  
+  int i, j, n, N, Nx, Ny, Ns, Ngauss;
+  float *vi, *vo, *gauss, *gaussnorm;
+  float g, s, sigma, Nsigma;
+  Buffer *in;
+  float *temp;
+
+  Nsigma = 3;
+  if ((N = get_argument (argc, argv, "-Nsigma"))) {
+    remove_argument (N, &argc, argv);
+    Nsigma = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: imsmooth (input) sigma\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  sigma = atof (argv[2]);
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+  ALLOCATE (temp, float, Nx*Ny);
+
+  /* build a 1D gaussian */
+  Ns = (int) (Nsigma*sigma + 0.5);
+  Ngauss = 2*Ns + 1;
+  ALLOCATE (gaussnorm, float, Ngauss);
+  gauss = &gaussnorm[Ns];
+  for (i = -Ns; i < Ns + 1; i++) {
+    gauss[i] = exp ((i*i)/(-2*sigma*sigma));
+  }
+
+  /* smooth in X direction */
+  for (j = 0; j < Ny; j++) {
+    vi = (float *) in[0].matrix.buffer + j*Nx;
+    vo = &temp[j*Nx];
+    for (i = 0; i < Nx; i++) {
+      g = s = 0;
+      for (n = -Ns; n < Ns + 1; n++) {
+	if (i+n < 0) continue;
+	if (i+n >= Nx) continue;
+	s += gauss[n]*vi[i+n];
+	g += gauss[n];
+      }
+      vo[i] = s / g;
+    }
+  }
+
+  /* smooth in Y direction */
+  for (i = 0; i < Nx; i++) {
+    vi = &temp[i];
+    vo = (float *)in[0].matrix.buffer + i;
+    for (j = 0; j < Ny; j++) {
+      g = s = 0;
+      for (n = -Ns; n < Ns + 1; n++) {
+	if (j+n < 0) continue;
+	if (j+n >= Ny) continue;
+	s += gauss[n]*vi[(n+j)*Nx];
+	g += gauss[n];
+      }
+      vo[j*Nx] = s / g;
+    }
+  }
+
+  free (temp);
+  free (gaussnorm);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/init.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/init.c	(revision 22322)
@@ -0,0 +1,265 @@
+# include "data.h"
+
+int accum            PROTO((int, char **));
+int applyfit         PROTO((int, char **));
+int applyfit2d       PROTO((int, char **));
+int box              PROTO((int, char **));
+int book_command     PROTO((int, char **));
+int center           PROTO((int, char **));
+int circstats        PROTO((int, char **));
+int clear            PROTO((int, char **));
+int clip             PROTO((int, char **));
+int close_device     PROTO((int, char **));
+int concat           PROTO((int, char **));
+int contour          PROTO((int, char **));
+int create           PROTO((int, char **));
+int cumulative       PROTO((int, char **));
+int cursor           PROTO((int, char **));
+int cut              PROTO((int, char **));
+int datafile         PROTO((int, char **));
+int dbconnect        PROTO((int, char **));
+int dbselect         PROTO((int, char **));
+int delete           PROTO((int, char **));
+int device           PROTO((int, char **));
+int dimendown        PROTO((int, char **));
+int dimenup          PROTO((int, char **));
+int erase            PROTO((int, char **));
+int extract          PROTO((int, char **));
+int fft1d            PROTO((int, char **));
+int fft2d            PROTO((int, char **));
+int fit2d            PROTO((int, char **));
+int fit              PROTO((int, char **));
+int gaussjordan      PROTO((int, char **));
+int gaussdeviate     PROTO((int, char **));
+int gaussintegral    PROTO((int, char **));
+int grid             PROTO((int, char **));
+int gridify          PROTO((int, char **));
+int grow             PROTO((int, char **));
+int ungridify        PROTO((int, char **));
+int histogram        PROTO((int, char **));
+int imcut            PROTO((int, char **));
+int imhist           PROTO((int, char **));
+int imsmooth         PROTO((int, char **));
+int integrate        PROTO((int, char **));
+int interpolate      PROTO((int, char **));
+int jpeg             PROTO((int, char **));
+int kern             PROTO((int, char **));
+int keyword          PROTO((int, char **));
+int labels           PROTO((int, char **));
+int limits           PROTO((int, char **));
+int line             PROTO((int, char **));
+int list_buffers     PROTO((int, char **));
+int header           PROTO((int, char **));
+int list_vectors     PROTO((int, char **));
+int load             PROTO((int, char **));
+int lookup           PROTO((int, char **));
+int mkrgb            PROTO((int, char **));
+int mcreate          PROTO((int, char **));
+int medacc           PROTO((int, char **));
+int mget             PROTO((int, char **));
+int minterp          PROTO((int, char **));
+int mset             PROTO((int, char **));
+int peak             PROTO((int, char **));
+int periodogram      PROTO((int, char **));
+int plot             PROTO((int, char **));
+int dot              PROTO((int, char **));
+int point            PROTO((int, char **));
+int ps               PROTO((int, char **));
+int queuelist        PROTO((int, char **));
+int queueload        PROTO((int, char **));
+int queueinit        PROTO((int, char **));
+int queuedelete      PROTO((int, char **));
+int queuedrop        PROTO((int, char **));
+int queuepop         PROTO((int, char **));
+int queueprint       PROTO((int, char **));
+int queuepush        PROTO((int, char **));
+int queuesubstr      PROTO((int, char **));
+int queuesize        PROTO((int, char **));
+int rd               PROTO((int, char **));
+int rdseg            PROTO((int, char **));
+int read_vectors     PROTO((int, char **));
+int rebin            PROTO((int, char **));
+int resize           PROTO((int, char **));
+int roll             PROTO((int, char **));
+int rotate           PROTO((int, char **));
+int save             PROTO((int, char **));
+int section          PROTO((int, char **));
+int set              PROTO((int, char **));
+int shift            PROTO((int, char **));
+int sort_vectors     PROTO((int, char **));
+int spline_apply_cmd PROTO((int, char **));
+int spline_construct_cmd PROTO((int, char **));
+int stats            PROTO((int, char **));
+int style            PROTO((int, char **));
+int subraster        PROTO((int, char **));
+int subset           PROTO((int, char **));
+int svd              PROTO((int, char **));
+int swapbytes        PROTO((int, char **));
+int textline         PROTO((int, char **));
+int tv               PROTO((int, char **));
+int tvchannel        PROTO((int, char **));
+int tvcolors         PROTO((int, char **));
+int tvcontour        PROTO((int, char **));
+int tvgrid           PROTO((int, char **));
+int uniq             PROTO((int, char **));
+int unsign           PROTO((int, char **));
+int vbin             PROTO((int, char **));
+int vclip            PROTO((int, char **));
+int vect_select      PROTO((int, char **));
+int vgrid            PROTO((int, char **));
+int vgauss           PROTO((int, char **));
+int vmaxwell         PROTO((int, char **));
+int vload            PROTO((int, char **));
+int vzload           PROTO((int, char **));
+int vstat            PROTO((int, char **));
+int vroll            PROTO((int, char **));
+int vpop             PROTO((int, char **));
+int vsmooth          PROTO((int, char **));
+int wd               PROTO((int, char **));
+int write_vectors    PROTO((int, char **));
+int zap              PROTO((int, char **));
+int zplot            PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "accum",        accum,            "accumulate vector values in another vector"},
+  {1, "applyfit",     applyfit,         "apply fit to new vector"},
+  {1, "applyfit2d",   applyfit2d,       "apply 2-d fit to new vector"},
+  {1, "box",          box,              "draw a box on the plot"},
+  {1, "book",         book_command,     "commands to manipulate book/page/word data"},
+  {1, "center",       center,           "center image on coords"},
+  {1, "circstats",    circstats,        "circular statistics"},
+  {1, "clear",        clear,            "erase plot"},
+  {1, "clip",         clip,             "clip values in a buffer to be within a range"},
+  {1, "close",        close_device,     "close the current display device"},
+  {1, "concat",       concat,           "reduce vector dimension"},
+  {1, "contour",      contour,          "create contour from image"},
+  {1, "create",       create,           "create a new vector"},
+  {1, "cumulative",   cumulative,       "build a cumulative histogram from a specific histogram"},
+  {1, "cursor",       cursor,           "get coords from cursor"},
+  {1, "cut",          cut,              "extract a cut across an image"},
+  {1, "datafile",     datafile,         "define file to read vectors"},
+  {1, "dbconnect",    dbconnect,        "setup mysql db connection"},
+  {1, "dbselect",     dbselect,         "extract vectors from mysql database table"},
+  {1, "delete",       delete,           "delete vectors or matrices"},
+  {1, "device",       device,           "set / get current graphics device"},
+  {1, "dimendown",    dimendown,        "convert matrix to vector"},
+  {1, "dimenup",      dimenup,          "convert vector to matrix"},
+  {1, "erase",        erase,            "erase objects on an overlay"},
+  {1, "extract",      extract,          "extract a portion of a buffer into another buffer"},
+  {1, "fft1d",        fft1d,            "fft on the pixel-stream in an image"},
+  {1, "fft2d",        fft2d,            "fft on an image"},
+  {1, "fit",          fit,              "fit polynomial to vector pair"},
+  {1, "fit2d",        fit2d,            "fit 2-d polynomial to vector triplet"},
+  {1, "gaussj",       gaussjordan,      "solve Ax = B (N-D)"},
+  {1, "gaussdev",     gaussdeviate,     "generate a gaussian deviate vector"},
+  {1, "gaussint",     gaussintegral,    "return the integrated gaussian vector"},
+  {1, "grid",         grid,             "plot cartesian grid"},
+  {1, "gridify",      gridify,          "convert vector triplet to buffer"},
+  {1, "grow",         grow,             "grow a mask"},
+  {1, "ungridify",    ungridify,        "convert buffer region to vector triplet"},
+  {1, "header",       header,           "print buffer header"},
+  {1, "histogram",    histogram,        "generate histogram from vector"},
+  {1, "imcut",        imcut,            "linear image cut between arbitrary coords"},
+  {1, "imhist",       imhist,           "histogram of an image region"},
+  {1, "imsmooth",     imsmooth,         "circular gaussian smoothing"},
+  {1, "integrate",    integrate,        "integrate a vector"},
+  {1, "interpolate",  interpolate,      "interpolate between vector pairs"},
+  {1, "jpeg",         jpeg,             "write text line on graph"},
+  {1, "png",          jpeg,             "write text line on graph"},
+  {1, "ppm",          jpeg,             "write text line on graph"},
+  {1, "kern",         kern,             "convolve with 3x3 kernel"},
+  {1, "keyword",      keyword,          "extract a FITS keyword from buffer header"},
+  {1, "labels",       labels,           "define labels for plot"},
+  {1, "limits",       limits,           "define plot limits"},
+  {1, "line",         line,             "plot line"},
+  {1, "buffers",      list_buffers,     "list the currently allocated buffers"},
+  {1, "vectors",      list_vectors,     "list vectors"},
+  {1, "load",         load,             "load an SAOimage style overlay"},
+  {1, "lookup",       lookup,           "convert vector via lookup table (vector pair)"},
+  {1, "mkrgb",        mkrgb,            "convert 3 images to rgb jpeg"},
+  {1, "mcreate",      mcreate,          "create a matrix"},
+  {1, "medacc",       medacc,           "accumulate vector values in another vector"},
+  {1, "mget",         mget,             "extract a vector from a matrix"},
+  {1, "minterp",      minterp,          "interpolate image pixels"},
+  {1, "mset",         mset,             "insert a vector in a matrix"},
+  {1, "peak",         peak,             "find vector peak in range"},
+  {1, "periodogram",  periodogram,      "measure periods in unevenly sampled data"},
+  {1, "plot",         plot,             "plot a pair of vectors"},
+  {1, "dot",          dot,              "plot a single point"},
+  {1, "point",        point,            "load overlay with single point"},
+  {1, "ps",           ps,               "define labels for plot"},
+  {1, "queuepop",     queuepop,         "pop value from queue to variable"},
+  {1, "queuedrop",    queuedrop,        "drop values from queue matching a key"},
+  {1, "queueprint",   queueprint,       "print the contents of a queue"},
+  {1, "queuepush",    queuepush,        "push value onto queue"},
+  {1, "queueinit",    queueinit,        "create an empty queue"},
+  {1, "queuedelete",  queuedelete,      "delete a queue"},
+  {1, "queuelist",    queuelist,        "list defined queues"},
+  {1, "queueload",    queueload,        "load queue from command"},
+  {1, "queuesubstr",  queuesubstr,      "bulk replace strings in queue"},
+  {1, "queuesize",    queuesize,        "show queue size"},
+  {1, "rd",           rd,               "load fits image"},
+  {1, "rdseg",        rdseg,            "read a segment of an image from a file"},
+  {1, "read",         read_vectors,     "read vectors from datafile"},
+  {1, "rebin",        rebin,            "rebin data by factor of N"},
+  {1, "resize",       resize,           "set graphics/image window size"},
+  {1, "roll",         roll,             "roll image to new start point"},
+  {1, "rotate",       rotate,           "rotate image"},
+  {1, "save",         save,             "save an SAOimage style overlay"},
+  {1, "section",      section,          "define section of graph"},
+  {1, "set",          set,              "vector math"},
+  {1, "shift",        shift,            "shift data in an image"},
+  {1, "sort",         sort_vectors,     "sort list of vectors"},
+  {1, "spline.apply", spline_apply_cmd, "apply spline fit to generate an image"},
+  {1, "spline.const", spline_construct_cmd, "create spline 2nd deriv. terms"},
+  {1, "stats",        stats,            "give statistics on a portion of a buffer"},
+  {1, "style",        style,            "set the style for graph plots"},
+  {1, "subraster",    subraster,        "subraster of fits image"},
+  {1, "subset",       subset,           "expand vector dimension"},
+  {1, "svd",          svd,              "singular value decomposition of a matrix"},
+  {1, "swapbytes",    swapbytes,        "byte swap thing"},
+  {1, "textline",     textline,         "write text line on graph"},
+  {1, "tv",           tv,               "display an image on the Kii window"},
+  {1, "tvchannel",    tvchannel,        "set the current tv channel"},
+  {1, "tvcolors",     tvcolors,         "set the tv colormap"},
+  {1, "tvcontour",    tvcontour,        "send contour to overlay"},
+  {1, "tvgrid",       tvgrid,           "wait until return is typed"},
+  {1, "uniq",         uniq,             "create a uniq vector subset from a vector"},
+  {1, "unsign",       unsign,           "toggle the UNSIGN status"},
+  {1, "vbin",         vbin,             "bin values in a vector to be within a range"},
+  {1, "vclip",        vclip,            "clip values in a vector to be within a range"},
+  {1, "select",       vect_select,      "selective vector assignment"},
+  {1, "vgauss",       vgauss,           ""},
+  {1, "vmaxwell",     vmaxwell,         ""},
+  {1, "vgrid",        vgrid,            ""},
+  {1, "vload",        vload,            "load vectors on Kii"},
+  {1, "vzload",       vzload,           "load vectors on Kii"},
+  {1, "vstat",        vstat,            "get info from imreg database"},
+  {1, "vsmooth",      vsmooth,          "gaussian smooth of a vector"},
+  {1, "vroll",        vroll,            "roll vector elements"},
+  {1, "vpop",         vpop,             "remove first element"},
+  {1, "wd",           wd,               "write an image to a file"},
+  {1, "write",        write_vectors,    "write vectors to datafile"},
+  {1, "zap",          zap,              "delete pixels"},
+  {1, "zplot",        zplot,            "plot x y with size scaled by z"},
+}; 
+
+void InitData () {
+  
+  int i;
+
+  InitKapa ();
+  InitQueues ();
+  InitBooks ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreeData () {
+
+  FreeKapa ();
+  FreeQueues ();
+  FreeBooks ();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/integrate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/integrate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/integrate.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "data.h"
+
+int integrate (int argc, char **argv) {
+  
+  int i, N, VERBOSE;
+  float *X, *Y;
+  double start, end, value, range;
+  Vector *vecx, *vecy;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: integrate <x> <y> start end\n");
+    return (FALSE);
+  }
+
+  if ((vecx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  start = atof (argv[3]);
+  end   = atof (argv[4]);
+
+  X = vecx[0].elements;
+  Y = vecy[0].elements;
+
+  value = 0;
+  range = 0;
+  for (i = 0; i < vecx[0].Nelements-1; i++, X++, Y++) {
+    if ((*X >= start) && (*X <= end)) {
+      value += *Y * (X[1] - X[0]);
+      range += (X[1] - X[0]);
+    }
+  }      
+
+  set_variable ("sum", value); 
+  set_variable ("range", range);
+  if (VERBOSE) gprint (GP_LOG, "sum: %f\n", value);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/interpolate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/interpolate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/interpolate.c	(revision 22322)
@@ -0,0 +1,53 @@
+# include "data.h"
+
+int interpolate (int argc, char **argv) {
+
+  int  i, j;
+  double x0, x1, dx, dy, y0;
+  Vector *xout, *yout, *xin, *yin;
+
+  /** check basic syntax **/
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: interpolate Xi Yi Xo Yo\n");
+    gprint (GP_ERR, "  Xi Yi - sorted reference vectors\n");
+    gprint (GP_ERR, "  Xo    - output positions\n");
+    gprint (GP_ERR, "  Yo    - output values\n");
+    return (FALSE);
+  }
+
+  if ((xin  = SelectVector (argv[1],  OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yin  = SelectVector (argv[2],  OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((xout = SelectVector (argv[3],  OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yout = SelectVector (argv[4],  ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  yout[0].Nelements = xout[0].Nelements;
+  REALLOCATE (yout[0].elements, float, yout[0].Nelements);
+
+  dx = xin[0].elements[1] - xin[0].elements[0];
+  dy = yin[0].elements[1] - yin[0].elements[0];
+  x0 = xin[0].elements[0];
+  y0 = yin[0].elements[0];
+  
+  /* in vectors are sorted, out vectors are not */
+  for (j = 0; j < xin[0].Nelements - 1; j++) {
+    dx = xin[0].elements[j+1] - xin[0].elements[j];
+    dy = yin[0].elements[j+1] - yin[0].elements[j];
+    x0 = xin[0].elements[j];
+    y0 = yin[0].elements[j];
+    x1 = xin[0].elements[j+1];
+    for (i = 0; i < xout[0].Nelements; i++) {
+      if ((xout[0].elements[i] >= x0) && (xout[0].elements[i] < x1)) {
+	yout[0].elements[i] = (dy/dx)*(xout[0].elements[i] - x0) + y0;
+      }
+      if ((j == 0) && (xout[0].elements[i] < x0)) {
+	yout[0].elements[i] = (dy/dx)*(xout[0].elements[i] - x0) + y0;
+      }
+      if ((j == xin[0].Nelements - 2) && (xout[0].elements[i] >= x1)) {
+	yout[0].elements[i] = (dy/dx)*(xout[0].elements[i] - x0) + y0;
+      }
+    }    
+  }
+
+  return (TRUE);
+    
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/jpeg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/jpeg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/jpeg.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include "data.h"
+
+int jpeg (int argc, char **argv) {
+
+  char filename[1024];
+  int N, kapa, IsPNG, IsPPM;
+  char *name;
+  
+  if ((N = get_argument (argc, argv, "--help"))) {
+    gprint (GP_ERR, "USAGE: jpeg [-name file] [-g | -i] [-n device] [-ppm]\n");
+    return (FALSE);
+  }
+
+  /* image type */
+  IsPPM = IsPNG = FALSE;
+  if ((N = get_argument (argc, argv, "-ppm"))) {
+    remove_argument (N, &argc, argv);
+    IsPPM = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-png"))) {
+    remove_argument (N, &argc, argv);
+    IsPNG = TRUE;
+  }
+  if (!strcmp (argv[0], "png")) IsPNG = TRUE;
+  if (!strcmp (argv[0], "ppm")) IsPPM = TRUE;
+
+  /* file name */
+  filename[0] = 0;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (filename, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* display source */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* XXX output png / jpeg needs to include both graph and image
+     if available.  this is a poor mix of data representations 
+     (png for image / jpeg for plots)
+  if ((N = get_argument (argc, argv, "-g"))) {
+  if ((N = get_argument (argc, argv, "-i"))) {
+  */
+
+  if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+  if (!IsPNG && !IsPPM) {
+    if (!filename[0]) strcpy (filename, "kapa.jpg");
+    KiiJPEG (kapa, filename);
+  }
+  if (IsPNG) {
+    if (!filename[0]) strcpy (filename, "kapa.png");
+    KapaPNG (kapa, filename);
+  } 
+  if (IsPPM) {
+    if (!filename[0]) strcpy (filename, "kapa.ppm");
+    KapaPPM (kapa, filename);
+  }
+  return (TRUE);
+}
+
+/* jpeg converts graph to png or ppm
+   jpeg converts image to jpeg */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/kern.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/kern.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/kern.c	(revision 22322)
@@ -0,0 +1,123 @@
+# include "data.h"
+
+/** need to allow larger kernels (5x5, 7x7, etc) **/
+int kern (int argc, char **argv) {
+
+  int i, n, m;
+  int NX, NY, status;
+  FILE *f;
+  float *in_buff, *out_buff, *ib, *ob;
+  char line[256], *list;
+  double kernel[3][3], val;
+  Buffer *buf;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: kern buffer (file)\n");
+    gprint (GP_ERR, "USAGE: kern buffer (list)\n");
+    gprint (GP_ERR, "USAGE: kern buffer -\n");
+    gprint (GP_ERR, "kernel file contains a 3x3 matrix for convolution\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  /* open file to read in kernel */
+  if (!strcmp (argv[2], "-")) {
+    for (i = 0; i < 3; i++) {
+      status = scan_line (stdin, line);
+      if (status == EOF) {
+	gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+	return (FALSE);
+      }
+      dparse (&kernel[0][i], 1, line);
+      dparse (&kernel[1][i], 2, line);
+      dparse (&kernel[2][i], 3, line);
+    }
+    goto have_kernel;
+  }
+  /* test list */
+  sprintf (line, "%s:n", argv[2]);
+  if ((list = get_variable (line)) == (char *) NULL) {
+    /* file */
+    f = fopen (argv[2], "r");
+    if (f == NULL) {
+      gprint (GP_ERR, "file not found: %s\n", argv[2]);
+      return (FALSE);
+    }
+    for (i = 0; i < 3; i++) {
+      status = scan_line (f, line);
+      if (status == EOF) {
+	gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+	fclose (f);
+      }
+      return (FALSE);
+      dparse (&kernel[0][i], 1, line);
+      dparse (&kernel[1][i], 2, line);
+      dparse (&kernel[2][i], 3, line);
+    }
+    fclose (f);
+    goto have_kernel;
+  }
+  if (atoi (list) != 9) {
+    gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+    return (FALSE);
+  }
+  free (list);
+  for (i = 0; i < 9; i++) {
+    sprintf (line, "%s:%d", argv[2], i);
+    list = get_variable (line);
+    if (list == (char *) NULL) {
+      gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+      return (FALSE);
+    }
+    kernel[(int)(i/3)][i%3] = atof (list);
+    free (list);
+  }
+  goto have_kernel;
+
+ have_kernel:
+  /* normalize kernel */
+  val = 0;
+  for (n = 0; n < 3; n++) {
+    for (m = 0; m < 3; m++) {
+      val += kernel[n][m];
+    }
+  }
+  if (val == 0) {
+    gprint (GP_ERR, "kernel has zero power, not renormalizing...");
+  } else {
+    for (n = 0; n < 3; n++) {
+      for (m = 0; m < 3; m++) {
+	kernel[n][m] /= val;
+      }
+    }
+  }
+
+  gprint (GP_ERR, "working...");
+  
+  /* create output buffer */
+  NX = buf[0].header.Naxis[0];
+  NY = buf[0].header.Naxis[1];
+  in_buff = (float *)buf[0].matrix.buffer;
+  ALLOCATE (buf[0].matrix.buffer, char, sizeof(float)*NX*NY);
+  out_buff = (float *)buf[0].matrix.buffer;
+  
+  /* do the convolution (on all but outer rows) */
+  
+  for (n = 0; n < 3; n++) {
+    for (m = 0; m < 3; m++) {
+      gprint (GP_ERR, "%d", n*3 + m + 1);
+      val = kernel[n][m];
+      ob = out_buff + NX + 1;
+      ib = in_buff + m*NX + n;
+      for (i = 0; i < (NX-2)*(NY-2); i++, ob++, ib++) {
+	*ob += *ib*val;
+      }
+    }
+  }
+  gprint (GP_ERR, "(done)\n");
+
+  free (in_buff);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/keyword.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/keyword.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/keyword.c	(revision 22322)
@@ -0,0 +1,158 @@
+# include "data.h"
+
+enum {NONE, STRING, FLOAT, INT, BOOLEAN, KEYCOMMENT, COMMENT};
+
+/** WARNING: no error checking on variable validity **/
+int keyword (int argc, char **argv) {
+
+  int ivalue, status, N, ascomment, asfloat, delete, soft, Wmode;
+  char line[80];
+  double value;
+  Buffer *buf;
+
+  asfloat = FALSE;
+  if ((N = get_argument (argc, argv, "-f"))) {
+    remove_argument (N, &argc, argv);
+    asfloat = TRUE;
+  }
+  
+  ascomment = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    ascomment = TRUE;
+  }
+  
+  delete = FALSE;
+  if ((N = get_argument (argc, argv, "-d"))) {
+    remove_argument (N, &argc, argv);
+    delete = TRUE;
+  }
+  
+  /* return TRUE for missing keyword if soft */
+  soft = FALSE;
+  if ((N = get_argument (argc, argv, "-soft"))) {
+    remove_argument (N, &argc, argv);
+    soft = TRUE;
+  }
+  
+  /* identify write modes */
+  Wmode = NONE;
+  if ((N = get_argument (argc, argv, "-w"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = STRING;
+  }
+  if ((N = get_argument (argc, argv, "-wf"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = FLOAT;
+  }
+  if ((N = get_argument (argc, argv, "-wd"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = INT;
+  }
+  if ((N = get_argument (argc, argv, "-wc"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = KEYCOMMENT;
+  }
+  if ((N = get_argument (argc, argv, "-ws"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = COMMENT;
+  }
+  if ((N = get_argument (argc, argv, "-wb"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = BOOLEAN;
+  }
+
+  if (!((argc == 3) || (!N && argc == 4))) {
+    gprint (GP_ERR, "USAGE: keyword <buffer> (KEYWORD) [variable] [-d] [-w(mode) value]\n");
+    gprint (GP_ERR, " -w modes: \n");
+    gprint (GP_ERR, "  -w  - string\n");
+    gprint (GP_ERR, "  -wf - float\n");
+    gprint (GP_ERR, "  -wd - int\n");
+    gprint (GP_ERR, "  -wb - boolean\n");
+    gprint (GP_ERR, "  -wc - comment\n");
+    gprint (GP_ERR, "  -ws - full string comment\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (Wmode != NONE) {
+    switch (Wmode) {
+    case STRING:
+      gfits_modify (&buf[0].header, argv[2], "%s", 1, line);
+      return (TRUE);
+    case FLOAT:
+      value = atof(line);
+      gfits_modify (&buf[0].header, argv[2], "%lf", 1, value);
+      return (TRUE);
+    case INT:
+      gfits_modify (&buf[0].header, argv[2], "%d", 1, atoi(line));
+      return (TRUE);
+    case BOOLEAN:
+      if (strcasecmp (line, "T") && strcasecmp (line, "TRUE") && strcasecmp (line, "F") && strcasecmp (line, "FALSE")) {
+	gprint (GP_ERR, "syntax error in boolean value\n");
+	return (FALSE);
+      }
+      ivalue = !strcasecmp (line, "T");
+      gfits_modify (&buf[0].header, argv[2], "%t", 1, ivalue);
+      return (TRUE);
+    case KEYCOMMENT:
+      gfits_modify (&buf[0].header, argv[2], "%C", 1, line);
+      return (TRUE);
+    case COMMENT:
+      gfits_modify (&buf[0].header, argv[2], "%S", 0, line);
+      return (TRUE);
+    }
+  }
+  
+  if (delete) {
+    gfits_delete (&buf[0].header, argv[2], -1);
+    return (TRUE);
+  }
+  
+  /* grab the value in the given format, either a string or a digit */
+  if (asfloat) {
+    status = gfits_scan (&buf[0].header, argv[2], "%lf", 1, &value);
+    if (!status) goto failure;
+    if (argc == 4) 
+      set_variable (argv[3], value);
+    else 
+      gprint (GP_LOG, "%s: %f\n", argv[2], value);
+    return (TRUE);
+  } 
+
+  if (ascomment) {
+    status = gfits_scan (&buf[0].header, argv[2], "%C", 1, line);
+    if (!status) goto failure;
+    if (argc == 4) 
+      set_str_variable (argv[3], line);
+    else 
+      gprint (GP_LOG, "%s: %s\n", argv[2], line);
+    return (TRUE);
+  }    
+
+  /* not-specified */
+  status = gfits_scan (&buf[0].header, argv[2], "%s", 1, line);
+  if (!status) goto failure;
+  if (argc == 4) 
+    set_str_variable (argv[3], line);
+  else 
+    gprint (GP_LOG, "%s: %s\n", argv[2], line);
+  return (TRUE);
+
+ failure: 
+  if (!soft) gprint (GP_ERR, "keyword %s not found\n", argv[2]);
+  return (soft);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/labels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/labels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/labels.c	(revision 22322)
@@ -0,0 +1,95 @@
+# include "data.h"
+
+int labels (int argc, char **argv) {
+  
+  char name[64];
+  int N, size, kapa;
+
+  if (!GetGraph (NULL, &kapa, NULL)) return (FALSE);
+
+  if (get_argument (argc, argv, "-h")) {
+    gprint (GP_ERR, "label options: \n");
+    gprint (GP_ERR, " -x : bottom-center\n");
+    gprint (GP_ERR, " +x : top-center\n");
+    gprint (GP_ERR, " -y : right-side\n");
+    gprint (GP_ERR, " +y : left-side\n\n");
+
+    gprint (GP_ERR, " -ul : upper-left corner\n");
+    gprint (GP_ERR, " -ll : lower-left corner\n");
+    gprint (GP_ERR, " -ur : upper-right corner\n");
+    gprint (GP_ERR, " -lr : lower-right corner\n\n");
+
+    gprint (GP_ERR, " -fn (font) (size) : set font and size\n");
+    gprint (GP_ERR, "   (font) : courier, helvetica, times, symbol\n\n");
+    gprint (GP_ERR, " label special characters:\n");
+    gprint (GP_ERR, " ^ : superscript\n");
+    gprint (GP_ERR, " _ : subscript\n");
+    gprint (GP_ERR, " | : default script \n");
+    gprint (GP_ERR, " &c, &h, &t, &s : set font\n\n");
+    return (FALSE);
+  }
+
+  if ((N = get_argument (argc, argv, "-fn"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    size = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    KapaSetFont (kapa, name, size);
+  } 
+
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 0);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-y"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 1);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "+x"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 2);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "+y"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 3);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-ul"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 4);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-ur"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 5);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-ll"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 6);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-lr"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 7);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: labels [-x] [-y] [+x] [+y] [-ul] [-ur] [-ll] [-lr]\n");
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/limits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/limits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/limits.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include "data.h"
+
+int limits (int argc, char **argv) {
+
+  int N, APPLY;
+  int kapa;
+  char *name;
+  Graphdata graphmode;
+  Vector *xvec, *yvec;
+
+  xvec = yvec = NULL;
+
+  APPLY = FALSE;
+  if ((N = get_argument (argc, argv, "-a"))) {
+    remove_argument (N, &argc, argv);
+    APPLY = TRUE;
+  }
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc == 1) {
+    gprint (GP_ERR, "limits: %f %f %f %f [-a] [-n device]\n",
+	     graphmode.xmin, graphmode.xmax,
+	     graphmode.ymin, graphmode.ymax);
+    goto success;
+  }
+
+  if (argc == 3) { /* expect to see: limits x y */
+    if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    goto success;
+  }
+    
+  if (argc == 4) { /* expect to see: limits x num num or limits num num y */
+    if (ISNUM(argv[1][0]) && ISNUM(argv[2][0])) {
+      if ((yvec = SelectVector (argv[3], OLDVECTOR, FALSE)) == NULL) goto error;
+      graphmode.xmin = atof (argv[1]);
+      graphmode.xmax = atof (argv[2]);
+      goto success;
+    }
+    if (ISNUM(argv[2][0]) && ISNUM(argv[3][0])) {
+      if ((xvec = SelectVector (argv[1], OLDVECTOR, FALSE)) == NULL) goto error;
+      graphmode.ymin = atof (argv[2]);
+      graphmode.ymax = atof (argv[3]);
+      goto success;
+    }
+    goto error;
+  }
+  
+  if (argc == 5) {
+    graphmode.xmin = atof (argv[1]);
+    graphmode.xmax = atof (argv[2]);
+    graphmode.ymin = atof (argv[3]);
+    graphmode.ymax = atof (argv[4]);
+    goto success;
+  }
+
+  gprint (GP_ERR, "USAGE: limits [xrange] [yrange]\n");
+  gprint (GP_ERR, " [range] may be either [min max] or a vector\n");
+  return (FALSE);
+
+ error:
+  gprint (GP_ERR, "error in vectors\n");
+  return (FALSE);
+
+ success:
+  SetLimits (xvec, yvec, &graphmode);
+  if (APPLY) KapaSetLimits (kapa, &graphmode);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/line.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/line.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/line.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "data.h"
+
+int line (int argc, char **argv) {
+  
+  int kapa;
+  Graphdata graphmode;
+  float x[2], y[2];
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: line <x> <y> to <x> <y>\n");
+    return (FALSE);
+  }
+  x[0] = atof(argv[1]);
+  y[0] = atof(argv[2]);
+  x[1] = atof(argv[4]);
+  y[1] = atof(argv[5]);
+
+  /* set point style and errorbar mode (these are NOT sticky) */
+  graphmode.style = 0;
+  graphmode.etype = 0;
+
+  if (!KapaPrepPlot (kapa, 2, &graphmode)) return (FALSE);
+  KapaPlotVector (kapa, 2, x, "x");
+  KapaPlotVector (kapa, 2, y, "y");
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/list_buffers.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/list_buffers.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/list_buffers.c	(revision 22322)
@@ -0,0 +1,10 @@
+# include "data.h"
+
+int list_buffers (int argc, char **argv) {
+
+  if (argc == 3) PrintBuffers (TRUE);
+  else PrintBuffers (FALSE);
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/list_header.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/list_header.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/list_header.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "data.h"
+
+int header (int argc, char **argv) {
+  
+  int j, N, nlines, nbytes, Nbytes, LOADHEAD, status, bitpix, unsign;
+  char *p, filename[128];
+  FILE *f;
+  double bscale, bzero;
+  Buffer *buf;
+
+  LOADHEAD = FALSE;
+  if ((N = get_argument (argc, argv, "-w"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (filename, argv[N]);
+    remove_argument (N, &argc, argv);
+    LOADHEAD = TRUE;
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: header <buffer> [-w filename]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (LOADHEAD) {
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      gprint (GP_ERR, "file %s not found\n", filename);
+      return (FALSE);
+    }
+    fclose (f);
+    
+    bitpix = buf[0].header.bitpix;
+    bzero  = buf[0].header.bzero;
+    bscale = buf[0].header.bscale;
+    unsign = buf[0].header.unsign;
+    gfits_free_header (&buf[0].header);
+    
+    strcpy (filename, buf[0].file);
+    strcpy (buf[0].file, "*");
+    strcat (buf[0].file, filename);
+    status = gfits_read_header (argv[2], &buf[0].header);
+    buf[0].header.bitpix = bitpix;     
+    buf[0].header.bzero  = bzero;      
+    buf[0].header.bscale = bscale;     
+    buf[0].header.unsign = unsign;     
+    gfits_modify (&buf[0].header, "BITPIX", "%d",  1, bitpix);
+    gfits_modify (&buf[0].header, "BSCALE", "%lf", 1, bscale);
+    gfits_modify (&buf[0].header, "BZERO",  "%lf", 1, bzero);
+    gfits_modify (&buf[0].header, "UNSIGN", "%t",  1, unsign);
+    
+  } else {
+
+    f = popen ("more", "w");
+    
+    p = gfits_header_field (&buf[0].header, "END", 1);
+    nlines = (p - buf[0].header.buffer) / 80;
+    nbytes = 81*nlines;
+
+    /* duplicate the header, add in the <return> chars, send to more */
+    ALLOCATE (p, char, nbytes);
+    for (j = 0; j < nlines; j++) {
+      memcpy (&p[81*j], &buf[0].header.buffer[80*j], 80);
+      p[81*j+80] = 10;
+    }
+    Nbytes = fwrite (p, sizeof(char), nbytes, f);
+    if (Nbytes != nbytes) {
+      gprint (GP_ERR, "warning: not all printed...\n");
+    }
+    free (p);
+
+    pclose (f);
+
+  }
+
+  return (TRUE);
+
+}
+
+/* XXX this function is not written in the context of the output file/buffer */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/list_vectors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/list_vectors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/list_vectors.c	(revision 22322)
@@ -0,0 +1,8 @@
+# include "data.h"
+
+int list_vectors (int argc, char **argv) {
+
+  ListVectors ();
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/load.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/load.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/load.c	(revision 22322)
@@ -0,0 +1,144 @@
+# include "data.h"
+
+int load (int argc, char **argv) {
+  
+  int i, N, n, ISCEL;
+  int kapa, Noverlay, NOVERLAY;
+  char *c, type[10], string[128], line[1024];
+  double x, y, dx, dy, x1, y1;
+  double dra, ddec, ra1, dec1, ra, dec;
+  FILE *f;
+  char *buffer, *name;
+  Coords coords;
+  Buffer *buf;
+  KiiOverlay *overlay;
+  KapaImageData data;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImageData (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  ISCEL = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    ISCEL = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: load (overlay) <filename>\n [-c] [-n]");
+    gprint (GP_ERR, "  -c: read overlay in celestial coords\n");
+    return (FALSE);
+  }
+  
+  if (!strcmp (argv[2], "-")) {
+    f = stdin;
+  } else {
+    f = fopen (argv[2], "r");
+  }
+  if (f == NULL) {
+    gprint (GP_ERR, "can't find file %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  if (ISCEL) {
+    if ((buf = SelectBuffer (data.name, OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    GetCoords (&coords, &buf[0].header);
+  }
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  ALLOCATE (buffer, char, 65536);  /* space for 512 lines of 128 bytes */
+  bzero (buffer, 65536);
+
+  dx = dy = 0;
+  for (n = 0; scan_line (f, line) != EOF;) {
+    c = strchr (line, '#');
+    if (c != (char *) NULL) 
+      *c = 0;  /* force end of line at comment! */
+    while ((c = strchr (line, '(')) != (char *) NULL) 
+      *c = ' ';
+    while ((c = strchr (line, ')')) != (char *) NULL) 
+      *c = ' ';
+    while ((c = strchr (line, ',')) != (char *) NULL) 
+      *c = ' ';
+    /* we could use some syntactial checks here */
+    /* have to get all three for this to be any valid object, if the line is commented out,
+     we should get none, so check that N == 3 before continuing: */
+    N = sscanf (line, "%s %lf %lf %lf %lf", type, &ra, &dec, &dra, &ddec);
+    switch (N) {
+    case 0:
+    case -1:
+      continue;
+    case 1:
+    case 2:
+    case 3:
+      if (strcmp (type, "TEXT")) {
+	gprint (GP_ERR, "syntax error in line:\n   %s\n", line);
+	continue;
+      }
+      sscanf (line, "%s %lf %lf %127s", type, &ra, &dec, string);
+    case 4:
+      ddec = dra;
+    case 5:
+      x = ra;
+      y = dec;
+      dx = dra;
+      dy = ddec;
+      if (ISCEL) {
+	if (!strcmp (type, "LINE")) {
+	  RD_to_XY (&x, &y, ra, dec, &coords);
+	  ra1 = ra + dra;
+	  dec1 = dec + ddec;
+	  RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+	  dy = (y1 - y);
+	  dx = (x1 - x);
+	} else {
+	  RD_to_XY (&x, &y, ra, dec, &coords);
+	  ra1 = ra;
+	  dec1 = dec + ddec;
+	  RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+	  dy = (fabs(x1 - x) + fabs(y1 - y));
+	  ra1 = ra + dra/cos(dec*RAD_DEG);;
+	  dec1 = dec;
+	  RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+	  dx = (fabs(x1 - x) + fabs(y1 - y));
+	}
+      }
+    }
+    overlay[Noverlay].type = KiiOverlayTypeByName (type);
+    if (overlay[Noverlay].type == KII_OVERLAY_TEXT) {
+      overlay[Noverlay].text = strcreate (string);
+    } else {
+      overlay[Noverlay].text = NULL;
+    }
+    overlay[Noverlay].x = x;
+    overlay[Noverlay].y = y;
+    overlay[Noverlay].dx = dx;
+    overlay[Noverlay].dy = dy;
+    Noverlay++;
+    CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+  }
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+
+  for (i = 0; i < Noverlay; i++) {
+    if (overlay[i].text == NULL) continue;
+    free (overlay[i].text);
+  }
+  free (overlay);
+
+  gprint (GP_ERR, "loaded %d objects\n", Noverlay);
+
+  if (f != stdin) {
+    fclose (f);
+  }
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/lookup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/lookup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/lookup.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "data.h"
+
+int lookup (int argc, char **argv) {
+  
+  int i, j;
+  float *ip, *op, *xp, *yp;
+  Vector *in, *out, *xv, *yv;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: lookup (input) (output) (x) (y)\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((xv  = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yv  = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xv[0].Nelements != yv[0].Nelements) {
+      gprint (GP_ERR, "unmatched lookup table lengths\n");
+      return (FALSE);
+  }
+
+  out[0].Nelements = in[0].Nelements;
+  REALLOCATE (out[0].elements, float, out[0].Nelements);
+
+  ip = in[0].elements;
+  op = out[0].elements;
+
+  for (i = 0; i < in[0].Nelements; i++, ip++, op++) {
+    // re-write this using bisection
+    xp = xv[0].elements;
+    yp = yv[0].elements;
+
+    for (j = 0; (*ip < *xp) && (j < yv[0].Nelements); j++);
+    *op = j;
+  }      
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mcreate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mcreate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mcreate.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "data.h"
+
+int mcreate (int argc, char **argv) {
+  
+  int Nx, Ny;
+  Buffer *buf;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: mcreate <buffer> Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = atof (argv[2]);
+  Ny = atof (argv[3]);
+
+  /* I should encapsulate this in a create_default_buffer */
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, Nx, Ny, -32, 1.0, 0.0);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/medacc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/medacc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/medacc.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "data.h"
+
+int medacc (int argc, char **argv) {
+  
+  int i, j, Nbins, Nvalues, N, N0, N1;
+  double start, end, delta, k0, k1, fn;
+  float *V, *K, *V1, *K1, *O, *tmpvec, *tmpkey;
+  Vector *val, *key, *out;
+
+  if ((argc != 6) && (argc != 7)) {
+    gprint (GP_ERR, "USAGE: medacc <value> <vector> <key> start end [delta]\n");
+    return (FALSE);
+  }
+
+  if ((val = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((key = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (val[0].Nelements != key[0].Nelements) {
+    gprint (GP_ERR, "key and value don't match\n");
+    return (FALSE);
+  }
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  start = atof (argv[4]);
+  end   = atof (argv[5]);
+  if (argc == 7) 
+    delta = atof (argv[6]);
+  else 
+    delta = 1;
+
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+  Nbins = 1 + (int)((end - start) / delta);
+
+  out[0].Nelements = Nbins;
+  REALLOCATE (out[0].elements, float, out[0].Nelements);
+  bzero (out[0].elements, sizeof(float)*out[0].Nelements);
+
+  /* copy vec and key to temp vectors */
+  ALLOCATE (tmpvec, float, val[0].Nelements);
+  ALLOCATE (tmpkey, float, val[0].Nelements);
+
+  V = val[0].elements;
+  K = key[0].elements;
+  V1 = tmpvec;
+  K1 = tmpkey;
+  Nvalues = val[0].Nelements;
+  for (i = 0; i < Nvalues; i++, V++, K++, V1++, K1++) {
+    *V1 = *V;
+    *K1 = *K;
+  }      
+
+  /* sort vec and key by key */
+  fsortpair (tmpkey, tmpvec, Nvalues);
+
+  O = out[0].elements;
+  /* find the start and end key for each range */
+  N0 = 0;
+  N1 = 0;
+  for (i = 0; i < Nbins; i++) {
+    k0 = i*delta + start;
+    k1 = (i+1)*delta + start;
+    for (j = N1; (j < Nvalues) && (tmpkey[j] < k0); j++);
+    N0 = j;
+    for (j = N0; (j < Nvalues) && (tmpkey[j] < k1); j++);
+    N1 = j;
+    N = N1 - N0;
+    fsort (&tmpvec[N0], N);
+    fn = O[i] = 0;
+    for (j = N0 + 0.25*N; j < N0 + 0.75*N; j++) {
+      O[i] += tmpvec[j];
+      fn += 1.0;
+    }
+    if (fn > 0) O[i] /= fn;
+  }      
+  
+  free (tmpvec);
+  free (tmpkey);
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mget.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mget.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mget.c	(revision 22322)
@@ -0,0 +1,61 @@
+# include "data.h"
+
+int mget (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix, xdir, Nset;
+  float *in, *out;
+  Buffer *buf;
+  Vector *vec;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mget <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (strcasecmp (argv[3], "-x") && strcasecmp (argv[3], "-y")) {
+    gprint (GP_ERR, "USAGE: mget <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+  xdir = TRUE;
+  if (!strcasecmp (argv[3], "-y")) xdir = FALSE;
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  Nset = atof (argv[4]);
+  if (Nset < 0) {
+    gprint (GP_ERR, "selection out of range\n");
+    return (FALSE);
+  }
+
+  if (xdir) {
+    vec[0].Nelements = Npix = Nx;
+    REALLOCATE (vec[0].elements, float, Npix);
+    if (Nset >= Ny) {
+      gprint (GP_ERR, "row out of range\n");
+      return (FALSE);
+    }
+    in  = (float *) buf[0].matrix.buffer + Nx*Nset;
+    out = vec[0].elements;
+    for (i = 0; i < Npix; i++, in++, out++) {
+      *out = *in;
+    }
+    return (TRUE);
+  } else {
+    vec[0].Nelements = Npix = Ny;
+    REALLOCATE (vec[0].elements, float, Npix);
+    if (Nset >= Nx) {
+      gprint (GP_ERR, "column out of range\n");
+      return (FALSE);
+    }
+    in  = (float *) buf[0].matrix.buffer + Nset;
+    out = vec[0].elements;
+    for (i = 0; i < Npix; i++, in+=Nx, out++) {
+      *out = *in;
+    }
+    return (TRUE);
+  }    
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/minterpolate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/minterpolate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/minterpolate.c	(revision 22322)
@@ -0,0 +1,159 @@
+# include "data.h"
+
+int minterp (int argc, char **argv) {
+  
+  int i, j, status, nx, ny, Nx, Ny, N, Extrapolate;
+  int ic, jc, dx, dy, Npix;
+  char temp[1024];
+  double scale, scale2, dX, dY;
+  float *V00, *V01, *V10, *V11, *Vout, dV1, dV2, dV3;
+  float *buf, I, J, x, y, xs, xe, ys, ye;
+  Buffer *in, *out;
+
+  /* choose the appropriate graphing window */
+  Extrapolate = FALSE;
+  if ((N = get_argument (argc, argv, "-extrapolate"))) {
+    remove_argument (N, &argc, argv);
+    Extrapolate = TRUE;
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: minterpolate <from> <to> scale [-extrapolate]\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+
+  scale  = atof (argv[3]);
+  scale2 = scale*scale;
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  nx = Nx * scale;
+  ny = Ny * scale;
+
+  /* create new matrix */
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_header (&in[0].header, &out[0].header);
+
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
+  out[0].header.Naxis[0] = nx;
+  out[0].header.Naxis[1] = ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+
+  /* fix astrometric terms */
+  status =  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dY);
+  dX /= scale;
+  dY /= scale;
+  if (status) {
+    gfits_modify (&out[0].header, "CDELT1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CDELT2", "%lf", 1, dY);
+  }
+  status =  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &dY);
+  dX *= scale;
+  dY *= scale;
+  if (status) {
+    gfits_modify (&out[0].header, "CRPIX1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CRPIX2", "%lf", 1, dY);
+  }
+
+  /* adjust filename */
+  temp[0] = 0;
+  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
+    strcpy (temp, "*");
+  }
+  strcat (temp, in[0].file);
+  strcpy (out[0].file, temp);
+
+  dX = dY = scale;
+
+  buf = (float *)in[0].matrix.buffer;
+  Npix = 0;
+
+  if (Extrapolate) {
+    for (j = 0; j < Ny - 1; j++) {
+      for (i = 0; i < Nx - 1; i++) {
+	V00 = buf + i + j*Nx;
+	V10 = V00 + 1;
+	V01 = V00 + Nx;
+	V11 = V01 + 1;
+	dV1 = (*V11 + *V00 - *V01 - *V10) / scale2;
+	dV2 = (*V01 - *V00) / scale;
+	dV3 = (*V10 - *V00) / scale;
+
+	x = (i + 0.5) * scale;
+	y = (j + 0.5) * scale;
+
+	xs = ys = 0;
+	xe = ye = scale;
+
+	if (i == 0)      { xs = -0.5*scale; }
+	if (i == Nx - 2) { xe =  1.5*scale; }
+
+	if (j == 0)      { ys = -0.5*scale; }
+	if (j == Ny - 2) { ye =  1.5*scale; }
+
+	for (J = ys; J < ye; J += 1.0) {
+	  dx = (x + xs);
+	  dy = (y + J);
+	  Vout = (float *)(out[0].matrix.buffer) + dy*nx + dx;
+	  for (I = xs; I < xe; I += 1.0, Vout++) {
+	    *Vout = dV1 * (I*J) + dV2 * J + dV3 * I + *V00;
+	    Npix ++;
+	  }
+	}
+      }
+    }
+  } else {
+     for (j = -1; j < Ny; j++) {
+      for (i = -1; i < Nx; i++) {
+	ic = MIN (MAX (i, 0), Nx-1);  /* we never actually reach Nx, Ny */
+	jc = MIN (MAX (j, 0), Ny-1);
+	V00 = buf + ic + jc*Nx;
+	V10 = V00 + 1;
+	V01 = V00 + Nx;
+	V11 = V01 + 1;
+
+	if ((i == -1) || (i == Nx - 1)) { V10 = V00; } else { V10 = V00 + 1; }
+	if ((j == -1) || (j == Ny - 1)) { V01 = V00; } else { V01 = V00 + Nx; }
+	if ((i == -1) || (i == Nx - 1)) { V11 = V01; } else { V11 = V01 + 1; }
+
+	dV1 = (*V11 + *V00 - *V01 - *V10) / scale2;
+	dV2 = (*V01 - *V00) / scale;
+	dV3 = (*V10 - *V00) / scale;
+
+	x = (i + 0.5) * scale;
+	y = (j + 0.5) * scale;
+
+	xs = ys = 0;
+	xe = ye = scale;
+
+	if (i == -1)     { xs = 0.5*scale; }
+	if (i == Nx - 1) { xe = 0.5*scale; }
+
+	if (j == -1)     { ys = 0.5*scale; }
+	if (j == Ny - 1) { ye = 0.5*scale; }
+
+	for (J = ys; J < ye; J += 1.0) {
+	  dx = (x + xs);
+	  dy = (y + J);
+	  Vout = (float *)(out[0].matrix.buffer) + dy*nx + dx;
+	  for (I = xs; I < xe; I += 1.0, Vout++) {
+	    *Vout = dV1 * (I*J) + dV2 * J + dV3 * I + *V00;
+	    Npix ++;
+	  }
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mkrgb.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mkrgb.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mkrgb.c	(revision 22322)
@@ -0,0 +1,78 @@
+# include "data.h"
+# include "jpeglib.h"
+
+int mkrgb (int argc, char **argv) {
+ 
+  int i, j, Nx, Ny;
+  FILE *f;
+  Buffer *red, *green, *blue;
+  float *Vr, *Vg, *Vb;
+
+  struct jpeg_compress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
+  JSAMPLE *image_buffer;	/* Points to data for current line */
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mkrgb (red) (green) (blue) (output)\n");
+    return (FALSE);
+  }
+
+  // define the input buffer and examine the shift
+  if ((red    = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((green  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((blue   = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = red[0].matrix.Naxis[0];
+  Ny = red[0].matrix.Naxis[1];
+  if (Nx != blue[0].matrix.Naxis[0]) return (FALSE);
+  if (Ny != blue[0].matrix.Naxis[1]) return (FALSE);
+  if (Nx != green[0].matrix.Naxis[0]) return (FALSE);
+  if (Ny != green[0].matrix.Naxis[1]) return (FALSE);
+
+  f = fopen (argv[4], "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "failed to open %s for output\n", argv[4]);
+    return (FALSE);
+  }
+
+  /* set up the error handler , initialize the JPEG compression object. */
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_compress (&cinfo);
+  jpeg_stdio_dest(&cinfo, f);
+  
+  // set up the basic jpeg output file
+  cinfo.image_width = Nx; 	/* image width and height, in pixels */
+  cinfo.image_height = Ny;
+  cinfo.input_components = 3;		        
+  cinfo.in_color_space = JCS_RGB; 	
+  jpeg_set_defaults (&cinfo);
+  jpeg_set_quality (&cinfo, 75, TRUE       /* limit to baseline-JPEG values */);
+  jpeg_start_compress (&cinfo, TRUE);
+
+  ALLOCATE (image_buffer, JSAMPLE, 3*Nx);
+
+  // ??
+  // && (cinfo.next_scanline < cinfo.image_height)
+
+  Vr = (float *) red[0].matrix.buffer;
+  Vg = (float *) green[0].matrix.buffer;
+  Vb = (float *) blue[0].matrix.buffer;
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, Vr++, Vg++, Vb++) {
+      image_buffer[3*i+0] = MAX (0.0, MIN (255.0, *Vr));
+      image_buffer[3*i+1] = MAX (0.0, MIN (255.0, *Vg));
+      image_buffer[3*i+2] = MAX (0.0, MIN (255.0, *Vb));
+    }
+    row_pointer[0] = image_buffer;
+    (void) jpeg_write_scanlines (&cinfo, row_pointer, 1);
+  }
+
+  jpeg_finish_compress (&cinfo);
+  fclose (f);
+
+  jpeg_destroy_compress (&cinfo);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mset.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mset.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/mset.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "data.h"
+
+int mset (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix, xdir, Nset;
+  float *in, *out;
+  Buffer *buf;
+  Vector *vec;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mset <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (strcasecmp (argv[3], "-x") && strcasecmp (argv[3], "-y")) {
+    gprint (GP_ERR, "USAGE: mset <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+  xdir = TRUE;
+  if (!strcasecmp (argv[3], "-y")) xdir = FALSE;
+
+  Npix = vec[0].Nelements;
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  Nset = atof (argv[4]);
+  if (Nset < 0) {
+    gprint (GP_ERR, "selection out of range\n");
+    return (FALSE);
+  }
+
+  if (xdir) {
+    if (Nx != Npix) {
+      gprint (GP_ERR, "dimensions don't match\n");
+      return (FALSE);
+    }
+    if (Nset >= Ny) {
+      gprint (GP_ERR, "row out of range\n");
+      return (FALSE);
+    }
+    out = (float *) buf[0].matrix.buffer + Nx*Nset;
+    in = vec[0].elements;
+    for (i = 0; i < Npix; i++, in++, out++) {
+      *out = *in;
+    }
+    return (TRUE);
+  } else {
+    if (Ny != Npix) {
+      gprint (GP_ERR, "dimensions don't match\n");
+      return (FALSE);
+    }
+    if (Nset >= Nx) {
+      gprint (GP_ERR, "column out of range\n");
+      return (FALSE);
+    }
+    out = (float *) buf[0].matrix.buffer + Nset;
+    in = vec[0].elements;
+    for (i = 0; i < Npix; i++, in++, out+=Nx) {
+      *out = *in;
+    }
+    return (TRUE);
+  }    
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/peak.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/peak.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/peak.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "data.h"
+
+int peak (int argc, char **argv) {
+  
+  int i, N, imax, QUIET;
+  double start, end, xmax, ymax;
+  float *X, *Y;
+  Vector *vecx, *vecy;
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 5) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: peak <x> <y> [start end]\n");
+    return (FALSE);
+  }
+
+  if ((vecx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 5) {
+    start = atof (argv[3]);
+    end   = atof (argv[4]);
+  } else {
+    start = vecx[0].elements[0];
+    end   = vecx[0].elements[vecx[0].Nelements - 1];
+  }
+
+  X = vecx[0].elements;
+  Y = vecy[0].elements;
+
+  imax = -1;
+  xmax = *X;
+  ymax = *Y;
+  for (i = 1; i < vecx[0].Nelements-1; i++, X++, Y++) {
+    if ((*X >= start) && (*X <= end)) {
+      if ((imax == -1) || (*Y > ymax)) {
+	xmax = *X;
+	ymax = *Y;
+	imax = i;
+      }
+    }
+  }      
+
+  set_variable ("peakval", ymax);
+  set_variable ("peakpos", xmax);
+  set_variable ("peaknum", imax);
+
+  if (!QUIET) gprint (GP_LOG, "peak %f @ %f (%d)\n", ymax, xmax, imax);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/periodogram.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/periodogram.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/periodogram.c	(revision 22322)
@@ -0,0 +1,114 @@
+# include "data.h"
+
+int periodogram (int argc, char **argv) {
+  
+  int i, N, Npt, Np, NP, VERBOSE;
+  float *tv, *fv;
+  float minP, maxP, minT, maxT, dTime;
+  float mean, var, w, tau, P, Pc, Ps, Po;
+  float C, S, cs, sn, cs2, sn2, ratio;
+  Vector *time, *flux, *power, *period;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: periodogram (time) (flux) (minP) (maxP) (period) (power)\n");
+    return (FALSE);
+  }
+  
+  if ((time = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((flux = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  minP = atof(argv[3]);
+  maxP = atof(argv[4]);
+  if ((period = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((power = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* find the max baseline, mean, and variance */
+  minT = maxT = time[0].elements[0];
+  Npt = time[0].Nelements;
+  tv = time[0].elements;
+  fv = flux[0].elements;
+  mean = var = 0;
+  for (i = 0; i < Npt; i++, tv++, fv++) {
+    minT = MIN (minT, *tv);
+    maxT = MAX (maxT, *tv);
+    mean += *fv;
+  }
+  mean = mean / Npt;
+  fv = flux[0].elements;
+  for (i = 0; i < Npt; i++, fv++) {
+    var += SQ(*fv - mean);
+  }
+  var = var / (Npt - 1);
+
+  if (VERBOSE) gprint (GP_ERR, "mean: %f, var: %f, minT: %f, maxT: %f\n", mean, var, minT, maxT);
+
+  dTime = maxT - minT;
+  if (dTime == 0) {
+    gprint (GP_ERR, "ERROR: time range is zero\n");
+    return (FALSE);
+  }
+
+  Np = 0;
+  NP = 100;
+  REALLOCATE (power[0].elements, float, NP);
+  REALLOCATE (period[0].elements, float, NP);
+
+  P = minP;
+  while (P < maxP) {
+    w = 2*M_PI/P;
+    
+    /* find the period offset tau  */
+    tv = time[0].elements;
+    cs = sn = 0;
+    for (i = 0; i < Npt; i++, tv++) {
+      cs += cos (*tv*w*2);
+      sn += sin (*tv*2*2);
+    }
+    tau = 0.5*atan2 (sn, cs) / w;
+      
+    /* find the power at this period */
+    tv = time[0].elements;
+    fv = flux[0].elements;
+    cs = sn = cs2 = sn2 = 0;
+    for (i = 0; i < Npt; i++, tv++, fv++) {
+      C = cos (w*(*tv-tau));
+      S = sin (w*(*tv-tau));
+      // C = cos (w**tv);
+      // S = sin (w**tv);
+      cs += (*fv - mean) * C;
+      sn += (*fv - mean) * S;
+      cs2 += SQ(C);
+      sn2 += SQ(S);
+    }
+    Pc = SQ(cs) / cs2;
+    Ps = SQ(sn) / sn2;
+    Po = (Pc + Ps) / (2*var);
+
+    power[0].elements[Np] = Po;
+    period[0].elements[Np] = P;
+    Np ++;
+    if (Np >= NP) {
+      NP += 100;
+      REALLOCATE (power[0].elements, float, NP);
+      REALLOCATE (period[0].elements, float, NP);
+    }
+
+    ratio = 1 + 0.1*P/dTime;
+
+    if (VERBOSE) gprint (GP_ERR, "tau: %f, P: %f, ratio: %f, dTime: %f, nextP: %f\n", tau, P, ratio, dTime, P*ratio);
+
+    P *= ratio;
+  }
+
+  power[0].Nelements = Np;
+  period[0].Nelements = Np;
+  REALLOCATE (power[0].elements, float, Np);
+  REALLOCATE (period[0].elements, float, Np);
+ 
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/plot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/plot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/plot.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int plot (int argc, char **argv) {
+  
+  int kapa, N, Npts;
+  Graphdata graphmode;
+  Vector *xvec, *yvec, *dxmvec, *dxpvec, *dymvec, *dypvec;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  /* decide on error bars */
+  dxmvec = dxpvec = dymvec = dypvec = NULL;
+  if ((N = get_argument (argc, argv, "-dx"))) {
+    remove_argument (N, &argc, argv);
+    if ((dxmvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "+dx"))) {
+    remove_argument (N, &argc, argv);
+    if ((dxpvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-dy"))) {
+    remove_argument (N, &argc, argv);
+    if ((dymvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "+dy"))) {
+    remove_argument (N, &argc, argv);
+    if ((dypvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: plot <x> <y> [style]\n");
+    return (FALSE);
+  }
+
+  /* set errorbar mode (these are NOT sticky) */
+  graphmode.etype = 0;
+  if ((dymvec != NULL) && (dypvec == NULL)) dypvec = dymvec;
+  if ((dypvec != NULL) && (dymvec == NULL)) dymvec = dypvec;
+  if ((dypvec != NULL) || (dymvec != NULL)) graphmode.etype |= 0x01;
+  if ((dxmvec != NULL) && (dxpvec == NULL)) dxpvec = dxmvec;
+  if ((dxpvec != NULL) && (dxmvec == NULL)) dxmvec = dxpvec;
+  if ((dxpvec != NULL) || (dxmvec != NULL)) graphmode.etype |= 0x02;
+  
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[2]);
+    return (FALSE);
+  }
+  Npts = xvec[0].Nelements;
+  if (Npts == 0) return (TRUE);
+
+  if (!KapaPrepPlot (kapa, Npts, &graphmode)) return (FALSE);
+  
+  KapaPlotVector (kapa, Npts, xvec[0].elements, "x");
+  KapaPlotVector (kapa, Npts, yvec[0].elements, "y");
+  if (graphmode.etype & 0x01) {
+    KapaPlotVector (kapa, Npts, dymvec[0].elements, "dym");
+    KapaPlotVector (kapa, Npts, dypvec[0].elements, "dyp");
+  }
+  if (graphmode.etype & 0x02) {
+    KapaPlotVector (kapa, Npts, dxmvec[0].elements, "dxm");
+    KapaPlotVector (kapa, Npts, dxpvec[0].elements, "dxp");
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/point.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/point.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/point.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "data.h"
+
+int point (int argc, char **argv) {
+  
+  int N, celestial, pixscale;
+  int kapa;
+  double ra, dec, dra, ddec, angle;
+  double x1, y1, ra1, dec1;
+  char *name;
+  Coords coords;
+  Buffer *buf;
+  KiiOverlay overlay;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImageData (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  celestial = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    celestial = TRUE;
+  }
+  
+  pixscale = FALSE;
+  if ((N = get_argument (argc, argv, "-pixscale"))) {
+    remove_argument (N, &argc, argv);
+    pixscale = TRUE;
+  }
+
+  angle = 0.0;
+  if ((N = get_argument (argc, argv, "-angle"))) {
+    remove_argument (N, &argc, argv);
+    angle = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: point (overlay) TYPE x y dx dy [-c]\n");
+    return (FALSE);
+  }
+  
+  if (celestial) {
+    if ((buf = SelectBuffer (data.name, OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    GetCoords (&coords, &buf[0].header);
+  }
+
+  if (celestial) {
+    ra   = atof(argv[3]);
+    dec  = atof(argv[4]);
+    dra  = atof(argv[5]);
+    ddec = atof(argv[6]);
+
+    fRD_to_XY (&overlay.x, &overlay.y, ra, dec, &coords);
+    if (pixscale) {
+      overlay.dx = atof(argv[5]);
+      overlay.dy = atof(argv[6]);
+    } else {
+      ra1 = ra + dra;
+      dec1 = dec + ddec;
+      RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+      overlay.dx = x1 - overlay.x;
+      overlay.dy = y1 - overlay.y;
+    }
+  }
+  else {
+    overlay.x  = atof(argv[3]);
+    overlay.y  = atof(argv[4]);
+    overlay.dx = atof(argv[5]);
+    overlay.dy = atof(argv[6]);
+  }
+  overlay.angle = angle;
+  overlay.type = KiiOverlayTypeByName (argv[2]);
+  KiiLoadOverlay (kapa, &overlay, 1, argv[1]);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/ps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/ps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/ps.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int ps (int argc, char **argv) {
+
+  char filename[1024], pagename[1024], *name;
+  int N, kapa, scaleMode, pageMode;
+  
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+
+  pageMode = KAPA_PS_NEWPLOT;
+
+  /* new page? */
+  if ((N = get_argument (argc, argv, "-newpage"))) {
+    remove_argument (N, &argc, argv);
+    pageMode = KAPA_PS_NEWPAGE;
+  }
+  if ((N = get_argument (argc, argv, "-raw"))) {
+    remove_argument (N, &argc, argv);
+    pageMode = KAPA_PS_RAWPAGE;
+  }
+
+  /* scale image? */
+  scaleMode = TRUE;
+  if ((N = get_argument (argc, argv, "-noscale"))) {
+    remove_argument (N, &argc, argv);
+    scaleMode = TRUE;
+  }
+
+  /* what file? */
+  filename[0] = 0;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (filename, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* pagename ? */
+  strcpy (pagename, "default");
+  if ((N = get_argument (argc, argv, "-pagename"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (pagename, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* which tool */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc > 1) && filename[0]) goto help;
+
+  if (argc > 1) strcpy (filename, argv[1]);
+
+  // get the connection to kapa, false if none available
+  if (!GetGraphData (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (!filename[0]) strcpy (filename, "kapa.ps");
+  
+  /* tell Ximage/Xgraph to ps the image */
+  KiiPS (kapa, filename, scaleMode, pageMode, pagename);
+  return (TRUE);
+
+help:
+  gprint (GP_ERR, "USAGE: ps [-name file.ps] [-g | -i] [-n device] [-raw] [-noscale] [-newpage] [-pagename (name]\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuedelete.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuedelete.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuedelete.c	(revision 22322)
@@ -0,0 +1,21 @@
+# include "data.h"
+
+int queuedelete (int argc, char **argv) {
+  
+  Queue *queue;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queuedelete (name)\n");
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  DeleteQueue (queue);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuedrop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuedrop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuedrop.c	(revision 22322)
@@ -0,0 +1,38 @@
+# include "data.h"
+
+int queuedrop (int argc, char **argv) {
+  
+  int N;
+  char *Key;
+  char *line;
+  char *Value;
+  Queue *queue;
+
+  Key = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 2) || (Key == NULL)) {
+    gprint (GP_ERR, "USAGE: queuedrop (queue) [-key N value]\n");
+    return (FALSE);
+  }
+
+  /* will create a queue if none exists */
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  /* drop all matching entries, if any exist */
+  while ((line = PopQueueMatch (queue, Key, Value)) != NULL) {
+    free (line);
+  }
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queueinit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queueinit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queueinit.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "data.h"
+
+int queueinit (int argc, char **argv) {
+
+  Queue *queue;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queueinit (name)\n");
+    return (FALSE);
+  }
+
+  queue = CreateQueue (argv[1]);
+  InitQueue (queue);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuelist.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuelist.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuelist.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include "data.h"
+
+int queuelist (int argc, char **argv) {
+  
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: queuelist\n");
+    return (FALSE);
+  }
+
+  ListQueues ();
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queueload.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queueload.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queueload.c	(revision 22322)
@@ -0,0 +1,69 @@
+# include "data.h"
+
+int queueload (int argc, char **argv) {
+  
+  char *A, *B, *val;
+  int i, status;
+  int Nread, Nbytes, NBYTES;
+  FILE *f;
+  Queue *queue;
+
+  if (argc != 4) goto usage;
+  if (strcmp(argv[2], "-x")) goto usage;
+  
+  /* will create a queue if none exists */
+  queue = CreateQueue (argv[1]);
+
+  /* val will hold the result of the command */
+  NBYTES = 1024;
+  ALLOCATE (val, char, NBYTES);
+    
+  /* loop until command produces no more output,  REALLOCATE as needed. */
+  f = popen (argv[3], "r");
+  Nbytes = 0;
+  Nread = 1;
+  while (Nread > 0) {
+    Nread = fread (&val[Nbytes], 1, 1023, f);
+    if (Nread < 0) { 
+      gprint (GP_ERR, "error reading from command\n");
+    }
+    if (Nread > 0) {
+      Nbytes += Nread;
+      NBYTES = 1024 + Nbytes;
+      REALLOCATE (val, char, NBYTES);
+    }
+  }
+  val[Nbytes] = 0;
+  status = pclose (f);
+    
+  if (status) {
+    gprint (GP_ERR, "warning: exit status of command %d\n", status);
+  }
+      
+  A = B = val;
+  for (i = 0; B != (char *) NULL;) {
+    while (isspace (*A) && (*A != 0)) A++;
+    B = strchr (A, '\n');
+    if (B != (char *) NULL) { *B = 0; }
+    if (*A != 0) {
+      PushQueue (queue, A);
+      A = B + 1;
+      i++;
+    }
+  }      
+  free (val);
+    
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: queueload (queue) -x (command)\n");
+  return (FALSE);
+}
+
+
+/* 
+ * -key only needed for replace or unique : give an error otherwise
+ * -uniq searched for a match and does NOT replace if matched
+ * -replace searches for a match and replaces if matched
+ * should trigger an error if -uniq and -replace...
+ */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuepop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuepop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuepop.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int queuepop (int argc, char **argv) {
+  
+  int N;
+  char *Key;
+  char *var;
+  char *line;
+  char *Value;
+  Queue *queue;
+
+  var = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    var = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Key = NULL;
+  Value = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queuepop (queue) [-var variable] [-key N value]\n");
+    return (FALSE);
+  }
+
+  /* will create a queue if none exists */
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  if (Key == NULL) {
+    line = PopQueue (queue);
+  } else {
+    line = PopQueueMatch (queue, Key, Value);
+  }
+
+  if (var == NULL) {
+    if (line == NULL) {
+      gprint (GP_ERR, "queue %s is empty or match not found\n", argv[1]);
+      return (FALSE);
+    } else {
+      gprint (GP_LOG, "%s\n", line);
+      free (line);
+      return (TRUE);
+    }
+  }
+
+  if (line == NULL) {
+    set_str_variable (var, "NULL");
+  } else {
+    set_str_variable (var, line);
+    free (line);
+  }
+
+  free (var);
+  if (Key != NULL) free (Key);
+  if (Value != NULL) free (Value);
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queueprint.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queueprint.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queueprint.c	(revision 22322)
@@ -0,0 +1,21 @@
+# include "data.h"
+
+int queueprint (int argc, char **argv) {
+  
+  Queue *queue;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queueprint (name)\n");
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  PrintQueue (queue);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuepush.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuepush.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuepush.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "data.h"
+
+int queuepush (int argc, char **argv) {
+  
+  char *Key;
+  int N, Unique, Replace;
+  Queue *queue;
+
+  Unique = FALSE;
+  if ((N = get_argument (argc, argv, "-uniq"))) {
+    remove_argument (N, &argc, argv);
+    Unique = TRUE;
+  }
+
+  Replace = FALSE;
+  if ((N = get_argument (argc, argv, "-replace"))) {
+    remove_argument (N, &argc, argv);
+    Replace = TRUE;
+  }
+
+  Key = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: queuepush (queue) (value) [-key N] [-uniq] [-replace]\n");
+    return (FALSE);
+  }
+
+  /* will create a queue if none exists */
+  queue = CreateQueue (argv[1]);
+
+  if (Unique) {
+    PushQueueUnique (queue, argv[2], Key);
+  }
+  if (Replace) {
+    PushQueueReplace (queue, argv[2], Key);
+  }
+  if (!Unique && !Replace) {
+    PushQueue (queue, argv[2]);
+  }
+
+  if (Key != NULL) free (Key);
+  return (TRUE);
+}
+
+
+/* 
+ * -key only needed for replace or unique : give an error otherwise
+ * -uniq searched for a match and does NOT replace if matched
+ * -replace searches for a match and replaces if matched
+ * should trigger an error if -uniq and -replace...
+ */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuesize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuesize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuesize.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "data.h"
+
+int queuesize (int argc, char **argv) {
+  
+  int N;
+  char *var;
+  Queue *queue;
+
+  var = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    var = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queuesize (name) [-var variable]\n");
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  if (var == (char *) NULL) {
+    gprint (GP_ERR, "Nlines: %d\n", queue[0].Nlines);
+    return (TRUE);
+  }
+
+  set_int_variable (var, queue[0].Nlines);
+  free (var);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuesubstr.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuesubstr.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/queuesubstr.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "data.h"
+
+int queuesubstr (int argc, char **argv) {
+  
+  int i;
+  char *p, *q;
+  char *match, *replace;
+  Queue *queue;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: queueprint (queue) (match) (replace)\n");
+    return (FALSE);
+  }
+
+  match = argv[2];
+  replace = argv[3];
+
+  assert (match);
+  assert (replace);
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  for (i = 0; i < queue[0].Nlines; i++) {
+
+    p = queue[0].lines[i];
+    if (p == NULL) continue;
+    
+    while (strstr (p, match) != NULL) {
+      q = strsubs (p, match, replace);
+      free (p);
+      p = q;
+    }
+    queue[0].lines[i] = p;
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/radial.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/radial.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/radial.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include "data.h"
+
+int circstats (int argc, char **argv) {
+  
+  int i, j;
+  double Npix, S1, S2, max, min, Sum, Mean, Stdev, IgnoreValue; 
+  double xc, yc, radius, R2, r;
+  float *V;
+  int xs, ys, xe, ye;
+  int Ignore, Quiet, N, Nx, Ny;
+  Buffer *buf;
+
+  Ignore = FALSE;
+  IgnoreValue = 0;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) goto usage;
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) goto missed;
+
+  xc = atof (argv[2]);
+  yc = atof (argv[3]);
+  radius = atof (argv[4]);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  if (xc < 0) goto range;
+  if (yc < 0) goto range;
+  if (xc >= Nx) goto range;
+  if (yc >= Ny) goto range;
+
+  xs = MAX (0, xc - radius);
+  ys = MAX (0, yc - radius);
+  xe = MIN (Nx, xc + radius + 1);
+  ye = MAX (Ny, yc + radius + 1);
+  R2 = radius*radius;
+
+  S1 = S2 = Npix = 0;
+  min = max = *(float *)(buf[0].matrix.buffer) + (int)(yc)*buf[0].matrix.Naxis[0] + (int)(xc); 
+  for (j = ys; j < ye; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + xs; 
+    for (i = xs; i < xe; i++, V++) {
+      r = SQ(i - xc) + SQ(j - yc);
+      if (r > R2) continue;
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      S1 += *V;
+      S2 += (*V)*(*V);
+      Npix += 1.0;
+      max = MAX (max, *V);
+      min = MIN (min, *V);
+    }
+  }
+  Mean = S1 / Npix;
+  Sum  = Mean * M_PI * R2;
+  Stdev = sqrt (S2/Npix - Mean*Mean);
+
+  if (!Quiet) {
+    gprint (GP_LOG, "     mean     stdev       min       max    Npix     Total\n");
+    gprint (GP_LOG, "%9.4g %9.4g %9.4g %9.4g %7.0f %9.4g\n", Mean, Stdev, min, max, Npix, Sum);
+  }
+
+  set_variable ("MIN",    min);
+  set_variable ("MAX",    max);
+  set_variable ("MEAN",   Mean);
+  set_variable ("SUM",    Sum);
+  set_variable ("NPIX",   Npix);
+  set_variable ("SIGMA",  Stdev);
+
+  return (TRUE);
+
+ usage: 
+  gprint (GP_ERR, "USAGE: circstats <buffer> x y radius\n");
+  return (FALSE);
+
+ range:
+  gprint (GP_ERR, "ERROR: coordinates out of range\n");
+  return (FALSE);
+
+ missed:
+  gprint (GP_ERR, "ERROR: buffer not found\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rd.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rd.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rd.c	(revision 22322)
@@ -0,0 +1,230 @@
+# include "data.h"
+
+/* there is some confusion in this function with the several possible options */
+int rd (int argc, char **argv) {
+  
+  int i, N, status, plane, Nplane, extend, Nextend, Nskip, JustHead;
+  int ccdsel, done, Nword, IsCompressed;
+  char region[512], *ccdid, *filename;
+  FILE *f;
+  Buffer *buf;
+
+  JustHead = FALSE;
+  if ((N = get_argument (argc, argv, "-head"))) {
+    remove_argument (N, &argc, argv);
+    JustHead = TRUE;
+  }
+
+  plane = 1;
+  if ((N = get_argument (argc, argv, "-plane"))) {
+    remove_argument (N, &argc, argv);
+    plane  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  extend = FALSE;
+  Nextend = -1;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    Nextend  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    extend = TRUE;
+  }
+
+  ccdsel = FALSE;
+  ccdid = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    ccdid  = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    ccdsel = TRUE;
+  }
+
+  if (extend && ccdsel) {
+    gprint (GP_ERR, "only specify one of -n and -x\n");
+    return (FALSE);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: rd <buffer> <filename> [-head] [-plane N] [-n ccdid] [-x extnum]\n");
+    return (FALSE);
+  }
+
+  /* test if file exists */
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "file %s not found\n", argv[2]);
+    return (FALSE);
+  }
+
+  /* find matrix, free old data */
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) {
+      fclose (f);
+      return (FALSE);
+  }
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+
+  /* save file name */
+  filename = filebasename (argv[2]);
+  strcpy (buf[0].file, filename);
+  free (filename);
+
+  status = FALSE;
+  IsCompressed = FALSE;
+
+  /*** advance to the correct FITS extension ***/
+
+  /* FITS extension by number */
+  if (extend) {
+    /* load in appropriate header */
+    Nskip = gfits_fread_Xheader (f, &buf[0].header, Nextend);
+    if (!Nskip) {
+      gprint (GP_ERR, "entry in %s not found\n", argv[2]);
+      DeleteBuffer (buf);
+      fclose (f);
+      return (FALSE);
+    }
+    if (gfits_extension_is_compressed (&buf[0].header)) {
+	IsCompressed = TRUE;
+    }
+  } 
+
+  /* FITS extension by name */
+  if (ccdsel) {
+    char *CCDKeyword, ID[64];
+
+    CCDKeyword = get_variable ("CCDKEYWORD");
+    if (CCDKeyword == (char *) NULL) {
+      // gprint (GP_ERR, "CCDKEYWORD variable is not set; ");
+      // gprint (GP_ERR, "using EXTNAME as default\n");
+      CCDKeyword = strcreate ("EXTNAME");
+    }
+    done = FALSE;
+    for (i = 0; !done; i++) {
+      Nskip = gfits_fread_Xheader (f, &buf[0].header, i);
+      if (!Nskip) {
+	gprint (GP_ERR, "extension %s in %s not found\n", ccdid, argv[2]);
+	DeleteBuffer (buf);
+	free (CCDKeyword);
+	fclose (f);
+	return (FALSE);
+      }
+
+      // for compressed data tables, EXTNAME may be duplicated, with the first one containing the
+      // word 'COMPRESSED_IMAGE'.  in this case, check the second EXTNAME, if CCDKeyword == EXTNAME 
+      // this may have to be a more obscure test specifically for 'imcopy' data...
+      // need to check each header, since file may contain a mix
+      
+      Nword = 1;
+      IsCompressed = FALSE;
+      if (gfits_extension_is_compressed (&buf[0].header)) {
+	if (!strcmp (CCDKeyword, "EXTNAME")) Nword = 1;
+	IsCompressed = TRUE;
+      }
+      if (!gfits_scan (&buf[0].header, CCDKeyword, "%s", Nword, ID)) {
+	gprint (GP_ERR, "%s not in header\n", CCDKeyword);
+	DeleteBuffer (buf);
+	free (CCDKeyword);
+	fclose (f);
+	return (FALSE);
+      }
+
+      /* compare as numbers if both are pure numbers, else as strings */
+      done = strnumcmp (ccdid, ID);
+      if (!done) gfits_free_header (&buf[0].header);
+    }
+    free (CCDKeyword);
+  }
+
+  /* fix up header, if needed */
+  if (extend || ccdsel) {
+    if (!IsCompressed) {
+      gfits_extended_to_primary (&buf[0].header, TRUE, "Standard FITS");
+    }
+  } else {
+      gfits_fread_header (f, &buf[0].header);
+  }
+
+  /* for JustHead, we skip reading the data segment */ 
+  // XXX for compressed data, we need to convert the header to the equivalent uncompressed version
+  if (JustHead) {
+    // XXX what are we doing here exactly?
+    buf[0].header.Naxes = 0;
+    ALLOCATE (buf[0].matrix.buffer, char, 1);
+    buf[0].matrix.size = 0;
+    buf[0].bitpix = 16;
+    buf[0].bzero = 0;
+    buf[0].bscale = 1;
+    buf[0].matrix.bitpix = 16;
+    buf[0].matrix.bzero = 0;
+    buf[0].matrix.bscale = 1;
+    buf[0].header.bitpix = 16;
+    buf[0].header.bzero = 0;
+    buf[0].header.bscale = 1;
+    fclose (f);
+    return (TRUE);
+  }
+
+  /* check for valid plane */
+  Nplane = buf[0].header.Naxis[2];
+  if (Nplane == 0) Nplane = 1;
+  if (plane > Nplane) {
+    gprint (GP_ERR, "-plane is too large: %d total planes\n", Nplane);
+    DeleteBuffer (buf);
+    fclose (f);
+    return (FALSE);
+  }
+
+  /* load matrix data */
+  if (IsCompressed) {
+    FTable ftable;
+    Header theader;
+    ftable.header = &theader;
+    ftable.header[0].buffer = NULL;
+    gfits_copy_header (&buf[0].header, ftable.header);
+    status = gfits_fread_ftable_data (f, &ftable);  // this just reads the bytes (not even a SWAP)
+    status = gfits_uncompress_image (&buf[0].header, &buf[0].matrix, &ftable);
+    // uncompressing the image leaves the format as an extension
+    gfits_extended_to_primary (&buf[0].header, TRUE, "Standard FITS");
+    gfits_free_table (&ftable);
+    // XXX this currently does not work for a cube (we get a cube back, not a specific plane)
+  } else {
+    sprintf (region, "-1 -1 -1 -1 %d %d", (plane - 1), plane);
+    status = gfits_fread_matrix_segment (f, &buf[0].matrix, &buf[0].header, region);
+  }
+  fclose (f);
+
+  if (!status) {
+    gprint (GP_ERR, "problem reading file, buffer not opened\n");
+    DeleteBuffer (buf);
+    return (FALSE);
+  }
+
+  /* adjust buffer to represent 2D data */
+  if (Nplane > 1) {
+    buf[0].header.Naxis[2] = 0;
+    buf[0].header.Naxes = 2;
+    gfits_modify (&buf[0].header, "NAXIS", "%d", 1, 2);
+    gfits_delete (&buf[0].header, "NAXIS3", 1);
+  }
+
+  /* we need to return a 2D array, convert 1D images to 2D (Naxis[1] = 1) */
+  if (buf[0].header.Naxes == 1) {
+    buf[0].header.Naxes = 2;
+    buf[0].header.Naxis[1] = 1;
+    buf[0].matrix.Naxis[1] = 1;
+  }    
+
+  buf[0].bitpix = buf[0].header.bitpix;    /* store the original values */
+  buf[0].bscale = buf[0].header.bscale;    /* store the original values */
+  buf[0].bzero  = buf[0].header.bzero;     /* store the original values */
+  buf[0].unsign = buf[0].header.unsign;
+  gprint (GP_LOG, "read %d bytes from %s into buffer %s\n", 
+	   buf[0].header.size + buf[0].matrix.size, argv[2], argv[1]);
+
+  /** now - convert the matrix values to floats for internal use **/
+  gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, gfits_get_unsign_mode());
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rdseg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rdseg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rdseg.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "data.h"
+
+/* there is some confusion in this function with the several possible options */
+int rdseg (int argc, char **argv) {
+  
+  int x, y, nx, ny, status;
+  char region[512], *filename;
+  FILE *f;
+  Buffer *buf;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: rdseg <buffer> <filename> x y nx ny\n");
+    return (FALSE);
+  }
+  x = atoi (argv[3]);
+  y = atoi (argv[4]);
+  nx = atoi (argv[5]);
+  ny = atoi (argv[6]);
+
+  /* test if file exists */
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "file %s not found\n", argv[2]);
+    return (FALSE);
+  }
+  fclose (f);
+
+  /* find matrix, free old data */
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+
+  /* save file name */
+  filename = filebasename (argv[2]);
+  strcpy (buf[0].file, filename);
+  free (filename);
+
+  status = gfits_read_header (argv[2], &buf[0].header);
+  sprintf (region, "%d %d %d %d 0 1", x, nx + x, y, ny + y);
+  status = gfits_read_matrix_segment (argv[2], &buf[0].matrix, region);
+  gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, ny);
+  buf[0].header.Naxis[0] = nx;
+  buf[0].header.Naxis[1] = ny;
+  buf[0].matrix.Naxis[0] = nx;
+  buf[0].matrix.Naxis[1] = ny;
+
+  if (!status) {
+    gprint (GP_ERR, "problem reading file, buffer not opened\n");
+    DeleteBuffer (buf);
+    return (FALSE);
+  }
+  if (buf[0].header.Naxes == 1) {
+    /* we need to return an array, so make Naxis[1] = 1 */
+    buf[0].header.Naxes = 2;
+    buf[0].header.Naxis[1] = 1;
+    buf[0].matrix.Naxis[1] = 1;
+  }    
+
+  buf[0].bitpix = buf[0].header.bitpix;    /* store the original values */
+  buf[0].bscale = buf[0].header.bscale;    /* store the original values */
+  buf[0].bzero  = buf[0].header.bzero;     /* store the original values */
+  buf[0].unsign = buf[0].header.unsign;
+  gprint (GP_LOG, "read %d bytes from %s into buffer %s\n", 
+	   buf[0].header.size + buf[0].matrix.size, argv[2], argv[1]);
+
+  /** now - convert the matrix values to floats for internal use **/
+  gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, gfits_get_unsign_mode());
+  
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 22322)
@@ -0,0 +1,321 @@
+# include "data.h"
+
+FILE *f = (FILE *) NULL;
+char filename[256];
+
+int datafile (int argc, char **argv) {
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: datafile (filename)\n");
+    return (FALSE);
+  }
+  
+  strcpy (filename, argv[1]);
+  if (f != (FILE *) NULL) { fclose (f); }
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "failed to open file %s\n", argv[1]);
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int read_vectors (int argc, char **argv) {
+  
+  int i, j, Nskip, Nvec, *col, done, status;
+  int Nbytes, nbytes, Nstart, NELEM, N, nread;
+  char *colstr, *c0, *c1, *buffer, *extname;
+  double value;
+  Vector **vec;
+
+  /* auto-sense table type */
+  if ((N = get_argument (argc, argv, "-fits"))) {
+    remove_argument (N, &argc, argv);
+    extname = strcreate (argv[N]);
+    if (extname == (char *) NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+    status = read_table_vectors (argc, argv, extname);
+    free (extname);
+    return (status);
+  }
+
+  Nskip = 0;
+  if ((N = get_argument (argc, argv, "-skip"))) {
+    remove_argument (N, &argc, argv);
+    Nskip = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc < 3) || !(argc % 2)) {
+    gprint (GP_ERR, "USAGE: read name N name N ...\n");
+    return (FALSE);
+  }
+  /* read name N name N  */
+
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "no open file for read\n");
+    return (FALSE);
+  }
+  fseek (f, 0, SEEK_SET);
+
+  Nvec = (argc - 1) / 2;
+  ALLOCATE (vec, Vector *, Nvec);
+  ALLOCATE (col, int, Nvec);
+
+  for (i = 0; i < Nvec; i++) {
+    if ((vec[i] = SelectVector (argv[2*i + 1], ANYVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, "USAGE: read name N name N ...\n");
+      free (vec);
+      free (col);
+      return (FALSE);    
+    }
+    colstr = argv[2*i+2];
+    for (j = 0; j < strlen (colstr); j++) {
+      if (!isdigit(colstr[j])) {
+	gprint (GP_ERR, "USAGE: read name N name N ...\n");
+	free (vec);
+	free (col);
+	return (FALSE);    
+      }
+    }
+    col[i] = atof (colstr);
+  }
+  
+  NELEM = 1000;
+  for (i = 0; i < Nvec; i++) {
+    REALLOCATE (vec[i][0].elements, float, NELEM);
+  }
+  
+  ALLOCATE (buffer, char, 0x10001);
+  bzero (buffer, 0x10001);
+  for (i = 0; i < Nskip; i++) {
+    scan_line (f, buffer);
+  }
+
+  Nstart = 0;
+  N = 0;
+  done = FALSE;
+  while (!done) {
+    Nbytes = 0x10000 - Nstart;
+    bzero (&buffer[Nstart], Nbytes);
+    nread = fread (&buffer[Nstart], 1, Nbytes, f);
+    if (ferror (f)) {
+      perror ("error reading data file");
+      break;
+    }
+    if (nread == 0) break;
+    nbytes = nread + Nstart;
+    
+    status = TRUE;
+    c0 = buffer; 
+    while (status) {
+      c1 = strchr (c0, '\n');
+      if (c1 == (char *) NULL) {
+	Nstart = strlen (c0);
+	memmove (buffer, c0, Nstart);
+	status = FALSE;
+      } else {
+	*c1 = 0;
+      }      
+      if ((*c0 != '#') && (*c0 != '!')) {
+	for (i = 0; (i < Nvec) && status; i++) {
+	  status = dparse (&value, col[i], c0);
+	  vec[i][0].elements[N] = value;
+	  if (!status) vec[i][0].elements[N] = 0.0/0.0;
+	}
+	if (status) N++;
+      }
+      c0 = c1 + 1;
+      if (N == NELEM) {
+	NELEM += 1000;
+	for (i = 0; i < Nvec; i++) {
+	  REALLOCATE (vec[i][0].elements, float, NELEM);
+	}
+      }
+	
+    }
+  }
+  for (i = 0; i < Nvec; i++) {
+    REALLOCATE (vec[i][0].elements, float, MAX (N,1));
+    vec[i][0].Nelements = N;
+  }
+  
+  free (vec);
+  free (col);
+  free (buffer);
+  return (TRUE);
+
+}
+
+# define ESCAPE(MSG) { \
+  gprint (GP_ERR, "%s\n", MSG); \
+  if (CCDKeyword != NULL) free (CCDKeyword); \
+  gfits_free_table  (&table); \
+  gfits_free_header (&header); \
+  return (FALSE);  }
+
+int read_table_vectors (int argc, char **argv, char *extname) {
+
+  int i, j, k, N, Nbytes, Nextend, Ny, Binary;
+  char type[16], ID[80], *CCDKeyword;
+  FTable table;
+  Header header;
+  Vector **vec;
+
+  table.buffer = NULL;
+  header.buffer = NULL;
+
+  CCDKeyword = NULL;
+  if ((N = get_argument (argc, argv, "-keyword"))) {
+    remove_argument (N, &argc, argv);
+    CCDKeyword = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Nextend = -1;
+  if ((N = get_argument (argc, argv, "-extnum"))) {
+    remove_argument (N, &argc, argv);
+    Nextend = atoi (extname);
+  }
+
+  if (argc < 2) ESCAPE ("USAGE: read -fits extension [-extnum] [-keyword key] name name ...");
+
+  if (f == NULL) ESCAPE ("file not found");
+  fseek (f, 0, SEEK_SET);
+  table.header = &header;
+
+  /* load appropriate extension (if extname is a number, use count) */
+  if (Nextend > -1) {
+    // first extension is PHU, cannot be a table. 
+    // Nextend counts from 0 for first extension
+    if (!gfits_load_header (f, &header)) ESCAPE ("error reading primary header for file");
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    gfits_free_header (&header);
+
+    for (i = 0; i < Nextend; i++) {
+      if (!gfits_load_header (f, &header)) ESCAPE ("extension not found");
+      Nbytes = gfits_data_size (&header);
+      /* skip the prior data buffers */
+      fseek (f, Nbytes, SEEK_CUR);
+      gfits_free_header (&header);
+    }
+    if (!gfits_load_header (f, &header)) ESCAPE ("error reading header for extension");
+    if (!gfits_fread_ftable_data (f, &table)) ESCAPE ("error reading table for extension");
+
+  } else {
+    if (CCDKeyword == NULL) {
+      CCDKeyword = get_variable ("CCDKEYWORD");
+    }
+    if (CCDKeyword == NULL) {
+      CCDKeyword = strcreate ("EXTNAME");
+    }
+
+    while (1) {
+      if (!gfits_load_header (f, &header)) ESCAPE ("extensio not found in file");
+      Nbytes = gfits_data_size (&header);
+
+      if (!gfits_scan (&header, CCDKeyword, "%s", 1, ID)) {
+	fseek (f, Nbytes, SEEK_CUR);
+	gfits_free_header (&header);
+	continue;
+      }
+      if (strcmp (ID, extname)) {
+	fseek (f, Nbytes, SEEK_CUR);
+	gfits_free_header (&header);
+	continue;
+      }
+      if (!gfits_fread_ftable_data (f, &table)) ESCAPE ("error reading table for extension");
+      break;
+    }
+  }
+
+  /* identify table type (ascii / binary) */
+  Binary = FALSE;
+  gfits_scan (&header, "XTENSION", "%s", 1, type);
+  if (strcmp (type, "BINTABLE") && strcmp (type, "TABLE")) {
+    ESCAPE ("specified extension is not a table\n");
+  }
+  Binary = !strcmp (type, "BINTABLE");
+  Ny = header.Naxis[1];
+
+  /* find columns which match requested vectors */
+  for (i = 1; i < argc; i++) {
+    void   *data;
+    char   *Pc;
+    short  *Ps;
+    int    *Pi;
+    float  *Pf;
+    double *Pd;
+    int Nval;
+    char name[80];
+      
+    Nval = 0;
+    if (Binary) {
+      if (!gfits_get_bintable_column_type (&header, argv[i], type, &Nval)) ESCAPE ("requested field not found");
+      if (!gfits_get_bintable_column (&header, &table, argv[i], &data)) ESCAPE ("error reading data from specified field");
+    } else {
+      if (!gfits_get_table_column_type (&header, argv[i], type)) ESCAPE ("requested field not found");
+      if (!gfits_get_table_column (&header, &table, argv[i], &data)) ESCAPE ("error reading data from specified field");
+    }
+    if (Nval == 0) ESCAPE ("no data for field in table");
+
+    ALLOCATE (vec, Vector *, Nval);
+    for (j = 0; j < Nval; j++) {
+      if (Nval == 1) 
+	sprintf (name, "%s", argv[i]);
+      else
+	sprintf (name, "%s:%d", argv[i], j);
+      if ((vec[j] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name");
+      REALLOCATE (vec[j][0].elements, float, MAX (Ny,1));
+      vec[j][0].Nelements = Ny;
+    }
+
+    if (!strcmp (type, "double")) {
+      Pd = (double *) data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Pd++) {
+	  vec[k][0].elements[j] = *Pd;
+	}
+      }
+    }
+    if (!strcmp (type, "float")) {
+      Pf = (float *) data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Pf++) {
+	  vec[k][0].elements[j] = *Pf;
+	}
+      }
+    }
+    if (!strcmp (type, "int")) {
+      Pi = (int *) data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Pi++) {
+	  vec[k][0].elements[j] = *Pi;
+	}
+      }
+    }
+    if (!strcmp (type, "short")) {
+      Ps = (short *) data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ps++) {
+	  vec[k][0].elements[j] = *Ps;
+	}
+      }
+    }
+    if (!strcmp (type, "char")) {
+      Pc = (char *) data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Pc++) {
+	  vec[k][0].elements[j] = *Pc;
+	}
+      }
+    }
+    free (data);
+    free (vec);
+  }
+  if (CCDKeyword != NULL) free (CCDKeyword);
+  gfits_free_table (&table);
+  gfits_free_header (&header);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rebin.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rebin.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rebin.c	(revision 22322)
@@ -0,0 +1,228 @@
+# include "data.h"
+
+int rebin (int argc, char **argv) {
+  
+  int i, j, status, n, nx, ny, Nx, Ny, x, y, N, *Npix, *Vn;
+  int Ignore, IgnoreValue, VERBOSE, Normalize, ExactScale;
+  char temp[1024];
+  float *Vout, *Vin, *Out, *In;
+  double scale, scale2, fx, fy, dX, dY;
+  Buffer *in, *out;
+
+  Vn = Npix = NULL;
+  Normalize = FALSE;
+  if ((N = get_argument (argc, argv, "-norm"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  Ignore = FALSE;
+  IgnoreValue = 0.0;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: rebin <from> <to> scale \n");
+    gprint (GP_ERR, "  negative integer scale expands image\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+
+  scale  = atof (argv[3]);
+  if ((scale == (int) scale) || ((1.0/scale) == (int)(1.0/scale))) {
+    ExactScale = TRUE;
+    if (scale > 0) {
+      nx = in[0].header.Naxis[0] / scale;
+      ny = in[0].header.Naxis[1] / scale;
+    } else {
+      nx = in[0].header.Naxis[0] * fabs(scale);
+      ny = in[0].header.Naxis[1] * fabs(scale);
+    }    
+  } else {
+    ExactScale = FALSE;
+    if (scale > 0) {
+      nx = (int) (in[0].header.Naxis[0] / scale) + 1;
+      ny = (int) (in[0].header.Naxis[1] / scale) + 1;
+    } else {
+      nx = (int) (in[0].header.Naxis[0] * fabs(scale)) + 1;
+      ny = (int) (in[0].header.Naxis[1] * fabs(scale)) + 1;
+    }      
+  }
+  if (VERBOSE) gprint (GP_LOG, "rebin %s to %s (%d,%d to %d,%d)\n", argv[1], argv[2], in[0].header.Naxis[0], in[0].header.Naxis[1], nx, ny);
+
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_header (&in[0].header, &out[0].header);
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
+
+  status =  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dY);
+  if (scale > 0) {
+    dX *= scale;
+    dY *= scale;
+  } else {
+    dX /= fabs(scale);
+    dY /= fabs(scale);
+  }    
+  if (status) {
+    gfits_modify (&out[0].header, "CDELT1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CDELT2", "%lf", 1, dY);
+  }
+
+  status =  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &dY);
+  if (scale > 0) {
+    dX /= scale;
+    dY /= scale;
+  } else {
+    dX *= fabs(scale);
+    dY *= fabs(scale);
+  }    
+  if (status) {
+    gfits_modify (&out[0].header, "CRPIX1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CRPIX2", "%lf", 1, dY);
+  }
+
+  out[0].header.Naxis[0] = nx;
+  out[0].header.Naxis[1] = ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+  temp[0] = 0;
+  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
+    strcpy (temp, "*");
+  }
+  strcat (temp, in[0].file);
+  strcpy (out[0].file, temp);
+
+  if (Normalize) {
+    ALLOCATE (Npix, int, nx*ny);
+    bzero (Npix, nx*ny*sizeof(int));
+  }
+
+  if (ExactScale) {
+    n = scale;
+    if (n > 0) {
+      for (j = 0; j < ny; j++) {
+	for (y = 0; y < n; y++) {
+	  Vout = (float *)(out[0].matrix.buffer) + j*nx;
+	  Vin  = (float *)(in[0].matrix.buffer)  + (j*n + y)*in[0].header.Naxis[0];
+	  if (Normalize) { Vn = Npix + j*nx; }
+	  for (i = 0; i < nx; i++, Vout++) {
+	    for (x = 0; x < n; x++, Vin++) {
+	      if (Ignore && (*Vin == IgnoreValue)) continue;
+	      *Vout += *Vin;
+	      if (Normalize) {(*Vn) ++;}
+	    }
+	    if (Normalize) {Vn ++;}
+	  }
+	}
+      }
+    } else {
+      n = fabs (n);
+      for (j = 0; j < in[0].header.Naxis[1]; j++) {
+	for (y = 0; y < n; y++) {
+	  Vout = (float *)(out[0].matrix.buffer) + (j*n + y)*nx;
+	  Vin  = (float *)(in[0].matrix.buffer)  + j*in[0].header.Naxis[0];
+	  if (Normalize) { Vn = Npix + j*nx; }
+	  for (i = 0; i < in[0].header.Naxis[0]; i++, Vin++) {
+	    if (Ignore && (*Vin == IgnoreValue)) { 
+	      Vout += n; 
+	      if (Normalize) Vn += n; 
+	      continue; 
+	    }
+	    for (x = 0; x < n; x++, Vout++) {
+	      *Vout = *Vin;
+	      if (Normalize) {(*Vn) ++; Vn ++;}
+	    }
+	  }
+	}
+      }
+    }
+    if (Normalize) {
+      Vn = Npix;
+      Vout = (float *)out[0].matrix.buffer;
+      for (i = 0; i < nx*ny; i++, Vout++, Vn++) {
+	if (*Vn) { 
+	  *Vout /= *Vn; 
+	} else {
+	  *Vout = 0;
+	}
+      }
+    }
+  } else {
+    
+    /* normalization is broken.  repair please */
+    if (Normalize) { gprint (GP_ERR, "normalize not enabled for fractional scaling\n"); }
+
+    if (scale < 0) scale = 1.0 / fabs(scale);
+    In = (float *)in[0].matrix.buffer;
+    Out = (float *)out[0].matrix.buffer;
+    scale2 = scale*scale;
+    if (scale > 1) {
+      for (i = 0; i < Ny; i++) {
+	y = 0.5 + (i - 0.5) / scale;
+	fy = 0.5 + MIN (0.5, (y + 0.5) * scale - i);
+	for (j = 0; j < Nx; j++, In++) {
+	  x = 0.5 + (j - 0.5) / scale;
+	  fx = 0.5 + MIN (0.5, (x + 0.5) * scale - j);
+	  Vout = Out + y*nx + x;
+	  *Vout += fx*fy*(*In);
+	  if (fx < 1) {
+	    *(Vout+1)    += (1-fx)*fy*(*In);
+	  }
+	  if (fy < 1) {
+	    *(Vout+nx) += fx*(1-fy)*(*In);
+	  }
+	  if ((fx < 1) && (fy < 1)) {
+	    *(Vout+1+nx) += (1-fx)*(1-fy)*(*In);
+	  }
+	}
+      }
+    } else {
+      for (i = 0; i < ny; i++) {
+	y = 0.5 + (i - 0.5) * scale;
+	fy = 0.5 + MIN (0.5, (y + 0.5) / scale - i);
+	for (j = 0; j < nx; j++, Out++) {
+	  x = 0.5 + (j - 0.5) * scale;
+	  fx = 0.5 + MIN (0.5, (x + 0.5) / scale - j);
+	  Vin = In + y*Nx + x;
+	  *Out += *Vin*fx*fy;
+	  if (fx < 1) {
+	    *Out += *(Vin+1)*(1-fx)*fy;
+	  }
+	  if (fy < 1) {
+	    *Out += *(Vin+Nx)*fx*(1-fy);
+	  }
+	  if ((fx < 1) && (fy < 1)) {
+	    *Out += *(Vin+1+Nx)*(1-fx)*(1-fy);
+	  }
+	  *Out = *Out * scale2;
+	}
+      }
+    }
+  }
+
+  if (Normalize) free (Npix);
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/resize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/resize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/resize.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "data.h"
+
+int resize (int argc, char **argv) {
+
+  char *end;
+  double NX, NY;
+  int N, kapa;
+  char *name;
+  
+  /* display source */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: resize NX NY [-n] [-g | -i]\n");
+    return (FALSE);
+  }
+
+  /* NX & NY are pixels for the screen & points for PS 
+     convert units to points (1in = 72pt, 1cm = 28pt) */
+
+  /* have kapa convert physical units to screen units 
+     for now, fixed at 96 pix / in == 38 pix / cm
+  */
+
+  NX = strtod (argv[1], &end);
+  if (!strcmp (end, "in")) { NX *= 96; }
+  if (!strcmp (end, "cm")) { NX *= 38; }
+
+  NY = strtod (argv[2], &end);
+  if (!strcmp (end, "in")) { NY *= 96; }
+  if (!strcmp (end, "cm")) { NY *= 38; }
+
+  KiiResize (kapa, NX, NY);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/roll.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/roll.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/roll.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "data.h"
+
+int roll (int argc, char **argv) {
+  
+  int Nbytes, Nextra;
+  int dX, dx, dy, nx, ny;
+  Buffer *buf;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: roll <buffer> dx dy\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  dx = atof (argv[2]);
+  dy = atof (argv[3]);
+  if (dy != 0) {
+    gprint (GP_ERR, "only x rolls implemented for now\n");
+  }
+
+  /* if (dx < 0), we are moving the start position back by dx pixels,
+     if (dx > 0), we are moving the start position forward by dx pixels */
+  
+  nx = buf[0].matrix.Naxis[0];
+  ny = buf[0].matrix.Naxis[1];
+
+  dX = abs(dx);
+  Nbytes = nx * ny * sizeof (float);
+  Nextra = (nx * ny + dX) * sizeof (float);
+
+  REALLOCATE (buf[0].matrix.buffer, char, Nextra);
+
+  if (dx < 0) {
+    memmove (buf[0].matrix.buffer, &buf[0].matrix.buffer[dX*sizeof(float)], Nbytes);
+  } else {
+    memmove (&buf[0].matrix.buffer[dX*sizeof(float)], buf[0].matrix.buffer, Nbytes);
+  }
+
+  REALLOCATE (buf[0].matrix.buffer, char, Nbytes);
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rotate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rotate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/rotate.c	(revision 22322)
@@ -0,0 +1,298 @@
+# include "data.h"
+
+int rotate (int argc, char **argv) {
+  
+  int i, j, NX, NY, X, Y, Lx, Ly, N, newCenter;
+  float *in_buff, *out_buff, *c;
+  double angle, CosAngle, SinAngle, Xo, Yo, dX, dY, fx, fy, x, y, X1, Y1;
+  double pc11, pc12, pc21, pc22, PC11, PC12, PC21, PC22;
+  Buffer *buf;
+
+  Xo = 0;
+  Yo = 0;
+  if ((N = get_argument (argc, argv, "-center"))) {
+    newCenter = TRUE;
+    remove_argument (N, &argc, argv);
+    Xo  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Yo  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  else 
+    newCenter  = FALSE;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: rotate <buffer> <angle>\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if ((atof (argv[1]) < -180) || (atof (argv[1]) > 180)) {
+    gprint (GP_ERR, "valid rotate angle between -180 and +180 degrees\n");
+    return (FALSE);
+  }
+
+  /* save starting values */
+  NX = buf[0].header.Naxis[0];
+  NY = buf[0].header.Naxis[1];
+  in_buff = (float *) buf[0].matrix.buffer;  /* don't lose reference */
+
+  if (!strcasecmp (argv[2], "LEFT") || (atof (argv[2]) == -90)) {
+    buf[0].header.Naxis[0] = NY;
+    buf[0].header.Naxis[1] = NX;
+    gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, NY);
+    gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, NX);
+    gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (i = NX - 1; i > -1; i--) {
+      for (j = 0; j < NY; j++, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = Yo;
+    Y1 = NX - Xo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = pc21;
+    PC12 = pc22;
+    PC21 = -pc11;
+    PC22 = -pc12;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "RIGHT") || (atof (argv[2]) == 90)) {
+    buf[0].header.Naxis[0] = NY;
+    buf[0].header.Naxis[1] = NX;
+    gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, NY);
+    gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, NX);
+    gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (i = 0; i < NX; i++) {
+      for (j = NY - 1; j > -1; j--, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = NY - Yo;
+    Y1 = Xo;
+    gprint (GP_ERR, "%f %f -> %f %f\n", Xo, Yo, X1, Y1);
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = -pc21;
+    PC12 = -pc22;
+    PC21 = pc11;
+    PC22 = pc12;
+    gprint (GP_ERR, "%f %f  ->  %f %f\n", pc11, pc12, PC11, PC12);
+    gprint (GP_ERR, "%f %f  ->  %f %f\n", pc21, pc22, PC21, PC22);
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "UPSIDE") || (atof (argv[2]) == -180) || (atof (argv[2]) == 180)) {
+    gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (j = NY - 1; j > -1; j--) {
+      for (i = NX - 1; i > -1; i--, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = NX - Xo;
+    Y1 = NY - Yo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = -pc11;
+    PC12 = -pc12;
+    PC21 = -pc21;
+    PC22 = -pc22;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "FLIPY")) {
+    gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (j = NY - 1; j > -1; j--) {
+      for (i = 0; i < NX; i++, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = Xo;
+    Y1 = NY - Yo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = pc11;
+    PC12 = -pc12;
+    PC21 = pc21;
+    PC22 = -pc22;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "FLIPX")) {
+    gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (j = 0; j < NY; j++) {
+      for (i = NX - 1; i > -1; i--, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = NX - Xo;
+    Y1 = Yo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = -pc11;
+    PC12 = pc12;
+    PC21 = -pc21;
+    PC22 = pc22;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  angle = atof (argv[2]);
+  CosAngle = cos (angle*RAD_DEG);
+  SinAngle = sin (angle*RAD_DEG);
+  
+  gprint (GP_ERR, "rotating: %f %f %f\n", angle, CosAngle, SinAngle);
+
+  Lx = NX*fabs(CosAngle) + NY*fabs(SinAngle);
+  Ly = NX*fabs(SinAngle) + NY*fabs(CosAngle);
+  dX = MAX(0,NY*SinAngle);
+  dY = MAX(0,-NX*SinAngle);
+  /*
+  gprint (GP_ERR, "%f %f  -->  ", Xo, Yo);
+  X1 = Xo*CosAngle - Yo*SinAngle + dX;
+  Y1 =  Xo*SinAngle + Yo*CosAngle + dY;
+  gprint (GP_ERR, "%f %f\n", X1, Y1);
+  */
+
+  /* fix reference pixel */
+  gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+  gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+  /*
+  X1 = (Xo - dX)*CosAngle - (Yo - dY)*SinAngle;
+  Y1 = (dX - Xo)*SinAngle + (Yo - dY)*CosAngle;
+  */
+  X1 = Xo*CosAngle - Yo*SinAngle + dX;
+  Y1 =  Xo*SinAngle + Yo*CosAngle + dY;
+  gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+  gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+
+  /* fix rotate matrix */
+  gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+  gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+  gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+  gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+  PC11 = pc11*CosAngle - pc21*SinAngle;
+  PC12 = pc12*CosAngle - pc22*SinAngle;
+  PC21 = pc21*CosAngle + pc11*SinAngle;
+  PC22 = pc22*CosAngle + pc12*SinAngle;
+  gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+  gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+  gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+  gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+  buf[0].header.Naxis[0] = Lx;
+  buf[0].header.Naxis[1] = Ly;
+  gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, Lx);
+  gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, Ly);
+  gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+  gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+  out_buff = (float *)buf[0].matrix.buffer;
+  for (j = 0; j < Ly; j++) {
+    for (i = 0; i < Lx; i++, out_buff++) {
+
+      x = (i - dX)*CosAngle + (j - dY)*SinAngle;
+      y = (dX - i)*SinAngle + (j - dY)*CosAngle;
+      X = (int) x;
+      Y = (int) y;
+
+      if (X < 0) continue;
+      if (X >= NX - 1) continue;
+      if (Y < 0) continue;
+      if (Y >= NY - 1) continue;
+
+      c = &in_buff[X * NX*Y];
+      fx = x - X;
+      fy = y - Y;
+      *out_buff = (c[0]*(1-fx) + c[1]*fx)*(1-fy) + (c[NX+1]*fx + c[NX]*(1-fx))*fy;
+    }
+  }
+  free (in_buff);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/save.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/save.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/save.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include "data.h"
+
+int save (int argc, char **argv) {
+  
+  int N, celestial;
+  int kapa;
+  char *name;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  celestial = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    celestial = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: save (overlay) <filename> [-c]\n");
+    gprint (GP_ERR, "  -c: write contour in celestial coords\n");
+    return (FALSE);
+  }
+
+  KiiSaveOverlay (kapa, celestial, argv[1], argv[2]);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/section.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/section.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/section.c	(revision 22322)
@@ -0,0 +1,133 @@
+# include "data.h"
+
+enum {NONE, LIST, UP, DOWN, TOP, BOTTOM, TOOL};
+
+int section (int argc, char **argv) {
+  
+  int N, action, kapa;
+  char *name, *location;
+  Graphdata graphmode;
+  KapaSection section;
+
+  action = NONE;
+  if ((N = get_argument (argc, argv, "-list"))) {
+    action = LIST;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-up"))) {
+    action = UP;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-down"))) {
+    action = DOWN;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-top"))) {
+    action = TOP;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-bottom"))) {
+    action = BOTTOM;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-imtool"))) {
+    action = TOOL;
+    remove_argument (N, &argc, argv);
+    location = argv[N];
+    remove_argument (N, &argc, argv);
+  }
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  /* list sections */
+  if ((argc == 1) && (action == NONE)) {
+    KapaGetSection (kapa, "*");
+    gprint (GP_ERR, "USAGE: section name [x y dx dy] [options]\n");
+    gprint (GP_ERR, "OPTIONS: -list   : show properties of all sections\n");
+    gprint (GP_ERR, "         -up     : move section up in display stack\n");
+    gprint (GP_ERR, "         -down   : move section down in display stack\n");
+    gprint (GP_ERR, "         -top    : move section to top of display stack\n");
+    gprint (GP_ERR, "         -bottom : move section to bottom of display stack\n");
+    gprint (GP_ERR, "         -imtool (position) : set location of image zoom / status box\n");
+    gprint (GP_ERR, "                 (position may be: -x, +x, -y, +y, none)\n");
+    return (TRUE);
+  } 
+  
+  if (argc == 2) {
+    /* select / show section */
+    switch (action) {
+      case NONE:
+	KapaSelectSection (kapa, argv[1]);
+	break;
+
+      case UP:
+	KapaMoveSection (kapa, argv[1], "up");
+	break;
+      case DOWN:
+	KapaMoveSection (kapa, argv[1], "down");
+	break;
+      case TOP:
+	KapaMoveSection (kapa, argv[1], "top");
+	break;
+      case BOTTOM:
+	KapaMoveSection (kapa, argv[1], "bottom");
+	break;
+
+      case TOOL:
+	if (!strcmp(location, "-x")) {
+	  KapaSetToolbox (kapa, 1);
+	  break;
+	}
+	if (!strcmp(location, "+x")) {
+	  KapaSetToolbox (kapa, 3);
+	  break;
+	}
+	if (!strcmp(location, "-y")) {
+	  KapaSetToolbox (kapa, 2);
+	  break;
+	}
+	if (!strcmp(location, "+y")) {
+	  KapaSetToolbox (kapa, 4);
+	  break;
+	}
+	if (!strcmp(location, "none")) {
+	  KapaSetToolbox (kapa, 0);
+	  break;
+	}
+	gprint (GP_ERR, "unknown toolbox location %s\n", location);
+	gprint (GP_ERR, "valid values: -x, +x, -y, +y, none\n");
+	return (FALSE);
+
+      case LIST:
+	KapaGetSection (kapa, argv[1]);
+	break;
+    }
+    return (TRUE);
+  } 
+  
+  if (argc == 6) {
+    /* set section */
+    section.name = argv[1];
+    section.x = atof (argv[2]);
+    section.y = atof (argv[3]);
+    section.dx = atof (argv[4]);
+    section.dy = atof (argv[5]);
+    KapaSetSection (kapa, &section);
+    return (TRUE);
+  }
+  gprint (GP_ERR, "USAGE: section name [x y dx dy]\n");
+  gprint (GP_ERR, "USAGE: section name [-list] [-up] [-down] [-top] [-bottom]\n");
+  return (FALSE);
+}
+
+/* should do some range checking on x y dx dy
+   should be between 0.0 and 1.0, precision of 0.001
+   is sufficient
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/select.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/select.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/select.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "data.h"
+
+int vect_select (int argc, char **argv) {
+  
+  int  i, size;
+  char *out;
+  Vector *in1, *in2, *tvec, *ovec;
+
+  out = NULL;
+  in1 = in2 = ovec = tvec = NULL;
+
+  /** check basic syntax **/
+  if ((argc != 8) || strcmp(argv[2], "=") || strcmp (argv[4], "if") || strcmp (argv[6], "else")) {
+    gprint (GP_ERR, "SYNTAX: select vec = vec if (logic expression) else vec\n");
+    return (FALSE);
+  }
+  if ((in1  = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((in2  = SelectVector (argv[7], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  out = dvomath (argc - 5, &argv[5], &size, 1);
+  if (out == NULL) {
+    print_error ();
+    goto error;
+  }
+  if ((tvec = SelectVector (out, OLDVECTOR, TRUE)) == NULL) goto error;
+  /* check size of in1, in2, tvec: must match */
+
+  REALLOCATE (ovec[0].elements, float, MAX (tvec[0].Nelements, 1));
+  for (i = 0; i < tvec[0].Nelements; i++) {
+    ovec[0].elements[i] = tvec[0].elements[i] ? in1[0].elements[i] : in2[0].elements[i];
+  }
+  ovec[0].Nelements = tvec[0].Nelements;
+  REALLOCATE (ovec[0].elements, float, MAX (ovec[0].Nelements, 1));
+  
+  DeleteVector (tvec);
+  free (out);
+  return (TRUE);
+
+ error:
+  DeleteVector (tvec);
+  DeleteVector (ovec);
+  DeleteNamedVector (out);
+  free (out);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/set.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/set.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/set.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "data.h"
+
+int set (int argc, char **argv) {
+  
+  int size;
+  char *out;
+
+  /** check basic form for line **/
+  if ((argc < 3) || strcmp(argv[2], "=")) {
+    gprint (GP_ERR, "%s = (matrix expression)\n", argv[0]);
+    return (FALSE);
+  }
+
+  out = dvomath (argc - 3, &argv[3], &size, -1);
+  if (out == NULL) {
+    print_error ();
+    return (FALSE);
+  }
+  
+  switch (size) {
+    case 0:
+      set_str_variable (argv[1], out);
+      free (out);
+      break;
+
+    case 1:
+      if (!MoveNamedVector (argv[1], out)) {
+	DeleteNamedVector (out);
+	free (out);
+	gprint (GP_ERR, "invalid output vector name\n");
+	return (FALSE);
+      }
+      free (out);
+      break;
+  
+    case 2:
+      if (!MoveNamedBuffer (argv[1], out)) {
+	DeleteNamedBuffer (out);
+	free (out);
+	gprint (GP_ERR, "invalid output matrix name\n");
+	return (FALSE);
+      }
+      free (out);
+      break;
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/shift.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/shift.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/shift.c	(revision 22322)
@@ -0,0 +1,110 @@
+# include "data.h"
+
+int shift (int argc, char **argv) {
+ 
+  int i, j, N, ROLL;
+  int nx, ny, dx, dy, DXin, DXot, DYin, DYot;
+  float *Vin, *Vot;
+  double Dx, Dy, fdx, fdy;
+  Buffer *in, *out;
+
+  ROLL = FALSE;
+  if ((N = get_argument (argc, argv, "-roll"))) {
+    remove_argument (N, &argc, argv);
+    ROLL = TRUE;
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: shift (input) (output) dx dy\n");
+    return (FALSE);
+  }
+
+  // define the input buffer and examine the shift
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Dx = atof (argv[3]);
+  Dy = atof (argv[4]);
+
+  dx = Dx;
+  dy = Dy;
+  fdx = Dx - dx;
+  fdy = Dy - dy;
+  if (fdx < -0.000001) {dx -= 1; fdx += 1;}
+  if (fdy < -0.000001) {dy -= 1; fdy += 1;}
+  // we always specify a positive fractional shift
+  // the above choice defines a minimum fractional shift of 1e-5
+
+  nx = in[0].matrix.Naxis[0];
+  ny = in[0].matrix.Naxis[1];
+
+  if ((dx > nx) || (dy > ny)) {
+    gprint (GP_ERR, "shifting data out of image\n");
+    return (FALSE);
+  }
+  
+  // define the output buffer
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+  CreateBuffer (out, nx, ny, -32, 0.0, 1.0);
+
+  DXin = (dx < 0) ? -dx : 0;
+  DXot = (dx < 0) ?   0 : dx;
+  DYin = (dy < 0) ? -dy : 0;
+  DYot = (dy < 0) ?   0 : dy;
+  
+  for (j = 0; j < ny - abs(dy); j++) {
+    Vin = (float *)(in[0].matrix.buffer)  + (j + DYin)*nx + DXin;  
+    Vot = (float *)(out[0].matrix.buffer) + (j + DYot)*nx + DXot; 
+    for (i = 0; i < nx - abs(dx); i++, Vin++, Vot++) {
+      *Vot = *Vin;
+    }
+    // fill in the exposed x-border with 0.0
+    Vot = (dx > 0) ? 
+      (float *)(out[0].matrix.buffer) + (j + DYot)*nx : 
+      (float *)(out[0].matrix.buffer) + (j + DYot)*nx + nx - abs(dx);
+    for (i = 0; i < abs(dx); i++, Vot++) {
+      *Vot = 0.0;
+    }	
+  }
+
+  // fill in the exposed y-border with 0.0
+  Vot = (dy > 0) ? 
+    (float *)(out[0].matrix.buffer) :
+    (float *)(out[0].matrix.buffer) + (ny - abs(dy))*nx;
+
+  for (j = 0; j < nx * abs(dy); j++, Vot++) {
+    *Vot = 0.0;
+  }   
+
+  // apply the fractional shift 
+  gprint (GP_ERR, "%f %f\n", fdx, fdy);
+  if ((fdx > 0) || (fdy > 0)) {
+    double f00, f01, f10, f11;
+    float value;
+
+    f00 = (1-fdx)*(1-fdy);
+    f01 =    fdx *(1-fdy);
+    f10 = (1-fdx)*   fdy;
+    f11 =    fdx *   fdy;
+
+    Vin = (float *)out[0].matrix.buffer;
+    Vot = (float *)out[0].matrix.buffer;
+    for (j = 0; j < ny - 1; j++) {
+      for (i = 0; i < nx - 1; i++, Vin++, Vot++) {
+	value  = Vin[   0] * f00;
+	value += Vin[   1] * f01;
+	value += Vin[nx  ] * f10;
+	value += Vin[nx+1] * f11;
+	*Vot = value;
+      }
+      Vin ++;
+      Vot ++;
+    }
+  }   
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/sort.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/sort.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/sort.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "data.h"
+
+int sort_vectors (int argc, char **argv) {
+  
+  int i, j, Nvec, Nval;
+  float *temp, *index, *T, *V, *I;
+  Vector **vec;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: sort (vector) [vectors ...] \n");
+    gprint (GP_ERR, "  first vector is sort key for others\n");
+    return (FALSE);
+  }
+
+  Nvec = (argc - 1);
+  ALLOCATE (vec, Vector *, Nvec);
+
+  Nval = 0;
+  /* find vectors and check sizes */
+  for (i = 0; i < Nvec; i++) {
+    if ((vec[i] = SelectVector (argv[i + 1], OLDVECTOR, FALSE)) == NULL) {
+      gprint (GP_ERR, "USAGE: sort vector vector ...\n");
+      free (vec);
+      return (FALSE);    
+    }
+    if (i == 0) {
+      Nval = vec[i][0].Nelements;
+    } else {
+      if (Nval != vec[i][0].Nelements) {
+	free (vec);
+	gprint (GP_ERR, "vectors must all be same length\n");
+	return (FALSE);
+      }
+    }
+  }
+  
+  /* create index (use float to use sortpair) */
+  ALLOCATE (index, float, Nval);
+  for (i = 0; i < Nval; i++) index[i] = i;
+
+  /* sort key & index */
+  fsortpair (vec[0][0].elements, index, Nval);
+
+  ALLOCATE (temp, float, Nval);
+  for (i = 1; i < Nvec; i++) {
+    T = temp;
+    V = vec[i][0].elements;
+    I = index;
+    for (j = 0; j < Nval; j++, T++, I++) {
+      *T = V[(int)(*I)];
+    }
+    /* swap .elements (== V) and temp */ 
+    vec[i][0].elements = temp;
+    temp = V;
+  }
+  free (temp);
+  free (vec);
+  free (index);
+
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/spline_apply.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/spline_apply.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/spline_apply.c	(revision 22322)
@@ -0,0 +1,98 @@
+# include "data.h"
+
+// need to rename this as an image function
+int spline_apply_cmd (int argc, char **argv) {
+  
+  int i, j, I, J;
+  int nx, ny, Nx, Ny, xdir;
+  float rx, ry, x, y;
+  float *Tx1, *Tx2, *Txc, *Ty1, *Ty2, *Tyc, *V, *V1, *V2;
+  Buffer *out, *y1, *y2;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: spline_apply <Y> <Y2> <out> (x/y) Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((y1  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((y2  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[3], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  xdir = FALSE;
+  if (!strcmp (argv[4], "x")) xdir = TRUE; 
+
+  nx = atoi (argv[5]);
+  ny = atoi (argv[6]);
+
+  Nx = y1[0].matrix.Naxis[0];
+  Ny = y1[0].matrix.Naxis[1];
+
+  rx = Nx / (float) nx;
+  ry = Ny / (float) ny;
+
+  /* create an output matrix buffer with desired nx, ny */
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+
+  out[0].bitpix = y1[0].bitpix;
+  out[0].unsign = y1[0].unsign;
+  out[0].bscale = y1[0].bscale;
+  out[0].bzero  = y1[0].bzero;
+  gfits_copy_header (&y1[0].header, &out[0].header);
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
+
+  out[0].header.Naxis[0] = nx;
+  out[0].header.Naxis[1] = ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+  if ((y1[0].file[0] != '*') && (y1[0].file[0] != '(')) {
+    sprintf (out[0].file, "*%s", y1[0].file);
+  } else {
+    sprintf (out[0].file, "%s", y1[0].file);
+  }
+
+  ALLOCATE (Ty2, float, Ny);
+  ALLOCATE (Ty1, float, Ny);
+  ALLOCATE (Tyc, float, Ny);
+  for (i = 0; i < Ny; i++) { Tyc[i] = i; }
+
+  ALLOCATE (Tx1, float, Nx);
+  ALLOCATE (Tx2, float, Nx);
+  ALLOCATE (Txc, float, Nx);
+  for (i = 0; i < Nx; i++) { Txc[i] = i; }
+
+  V = (float *)(out[0].matrix.buffer);
+
+  for (J = 0; J < ny; J++) {
+    y = J * ry;
+
+    /* construct spline for each element in this row */
+    for (i = 0; i < Nx; i++) {
+      V1 = (float *)(y1[0].matrix.buffer) + i;
+      V2 = (float *)(y2[0].matrix.buffer) + i;
+      for (j = 0; j < Ny; j++, V1+=Nx, V2+=Nx) {
+	Ty1[j] = *V1;
+	Ty2[j] = *V2;
+      }
+      Tx1[i] = spline_apply (Tyc, Ty1, Ty2, Ny, y);
+    }
+    spline_construct (Txc, Tx1, Nx, Tx2);
+
+    /* apply x-dir spline to new image */
+    for (I = 0; I < nx; I++, V++) {
+      x = I * rx;
+      *V = spline_apply (Txc, Tx1, Tx2, Nx, x);
+    }
+  }
+
+  free (Ty1);
+  free (Ty2);
+  free (Tyc);
+  
+  free (Tx1);
+  free (Tx2);
+  free (Txc);
+  
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/spline_construct.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/spline_construct.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/spline_construct.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "data.h"
+
+// need to rename this as an image function
+int spline_construct_cmd (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, xdir;
+  float *Tx, *Ty, *Ty2, *V;
+  Buffer *in, *out;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: spline_construct <in> <out> (x/y)\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  // XXX move this to gfits_create_matrix
+  free (out[0].matrix.buffer);
+  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
+    sprintf (out[0].file, "*%s", in[0].file);
+  } else {
+    sprintf (out[0].file, "%s", in[0].file);
+  }
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_matrix_info (&in[0].matrix, &out[0].matrix);
+  gfits_copy_header (&in[0].header, &out[0].header);
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+
+  xdir = FALSE;
+  if (!strcmp (argv[3], "x")) xdir = TRUE; 
+  /* ideally, the resulting image should carry this info (in header?) */
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+
+  ALLOCATE (Ty2, float, Ny);
+  ALLOCATE (Ty, float, Ny);
+  ALLOCATE (Tx, float, Ny);
+
+  /** for now only perform the operation for the ydir splines */
+
+  /* construct coordinate vector */
+  for (j = 0; j < Ny; j++) { Tx[j] = j; }
+  
+  for (i = 0; i < Nx; i++) {
+    
+    /* construct temp vector with values to spline */
+    V = (float *)(in[0].matrix.buffer) + i;
+    for (j = 0; j < Ny; j++, V+=Nx) {
+      Ty[j] = *V;
+    }
+  
+    spline_construct (Tx, Ty, Ny, Ty2);
+  
+    /* copy derivatives to output buffer */
+    V = (float *)(out[0].matrix.buffer) + i;
+    for (j = 0; j < Ny; j++, V+=Nx) {
+      *V = Ty2[j];
+    }
+  }
+
+  free (Tx);
+  free (Ty);
+  free (Ty2);
+  
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/stats-new.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/stats-new.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/stats-new.c	(revision 22322)
@@ -0,0 +1,259 @@
+# include "data.h"
+
+double interpolateValue (int *hist, int Nbins, float min, float binsize, int bin, float value);
+double sampleMedian (Matrix *matrix, int Nsubset);
+Stats *robustMedian (Matrix *matrix, Region *region, float minValue, float maxValue, float sigma);
+
+typedef struct {
+  int sx;
+  int sy;
+  int nx;
+  int ny;
+} Region;
+
+typedef struct {
+  float median;
+  float mean;
+  float mode;
+  float sigma;
+} Stats;
+
+int stats (int argc, char **argv) {
+  
+  int i, j, Nmode, Imode;
+  double Npix, N1, N2, max, min, range, median, mode, IgnoreValue;
+  float *V;
+  int sx, sy, nx, ny, *hist, Nhist, bin;
+  int Ignore, Quiet, N;
+  Buffer *buf;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 2) && (argc != 6)) {
+    gprint (GP_ERR, "USAGE: stats <buffer> sx sy nx ny\n");
+    gprint (GP_ERR, "OR:    stats <buffer>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    sx = strcmp (argv[2], "-") ? atof (argv[2]) : 0;
+    sy = strcmp (argv[3], "-") ? atof (argv[3]) : 0;
+    nx = strcmp (argv[4], "-") ? atof (argv[4]) : buf[0].matrix.Naxis[0];
+    ny = strcmp (argv[5], "-") ? atof (argv[5]) : buf[0].matrix.Naxis[1];
+  } else {
+    sx = 0;
+    sy = 0;
+    nx = buf[0].matrix.Naxis[0];
+    ny = buf[0].matrix.Naxis[1];
+  }
+
+  Npix = N1 = N2 = 0;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  max = min = *((float *)(buf[0].matrix.buffer) + sy*buf[0].matrix.Naxis[0] + sx);
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      N1 += *V;
+      N2 += (*V)*(*V);
+      Npix += 1.0;
+      max = MAX (max, *V);
+      min = MIN (min, *V);
+    }
+  }
+  N1 = N1 / Npix;
+
+  sigma = sqrt (N2/Npix - N1*N1);
+
+  if (ROBUST) {
+    stats = robustMedian (matrix, min, max, sigma);
+    if (stats->sigma 
+  } 
+  
+  if (!Quiet) {
+    gprint (GP_LOG, "  mean    stdev    min     max   median   Npix   Total\n");
+    gprint (GP_LOG, "%7.4g %7.4g %7.4g %7.4g %7.4g %7.0f %7.4g\n", N1, sqrt (N2/Npix - N1*N1), 
+	    min, max, median, Npix, Npix*N1);
+  }
+
+  set_variable ("MIN",    min);
+  set_variable ("MAX",    max);
+  set_variable ("MEDIAN", median);
+  set_variable ("MEAN",   N1);
+  set_variable ("MODE",   mode);
+  set_variable ("TOTAL",  N1*Npix);
+  set_variable ("NPIX",   Npix);
+  set_variable ("SIGMA",  sqrt (N2/Npix - N1*N1));
+
+  return (TRUE);
+}
+
+double sampleMedian (Matrix *matrix, int Nsubset) {
+
+  int i, Nsample, Npix;
+  long A, B;
+  float *values, *buffer, median;
+
+  /* Generate a vector of Nsample elements */
+  Npix = matrix[0].Naxis[0]*matrix[0].Naxis[1]
+    Nsample = Nsubset;
+  if (Nsubset == 0) {
+    Nsample = Npix;
+  }
+
+  ALLOCATE (values, float, Nsample);
+    
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  *buffer = (float *) matrix[0].buffer;
+
+  for (i = 0; i < Nsample; i++) {
+    if (Nsubset) {
+      j = MIN(Npix - 1, MAX(0, Npix*drand48()));
+    } else {
+      j = i;
+    }
+    values[i] = buffer[j];
+  }
+    
+  fsort (values, Nsample);
+
+  if (Nsample == 0) return 0.0;
+
+  // these are all covered by the logic below
+  // if (Nsample == 1) return values[0];
+  // if (Nsample == 2) return (0.5*(values[0] + values[1]));
+  // if (Nsample == 3) return (values[1]);
+
+  if (Nsample % 2) {
+    median = 0.5*(values[(int)(0.5*Nsample)] + values[(int)(0.5*Nsample) - 1]);
+  } else {
+    median = values[(int)(0.5*Nsample)];
+  }
+  return (median);
+}
+
+Stats *robustMedian (Matrix *matrix, Region *region, float minValue, float maxValue, float sigma) {
+
+  Stats *stats;
+
+  ALLOCATE (stats, Stats, 1);
+
+  // no data range:
+  if (maxValue - minValue < FLT_EPSILON) {
+    stats->median = minValue;
+    stats->mean   = minValue;
+    stats->mode   = minValue;
+    stats->sigma  = 0.0;
+    return stats;
+  }
+
+  // generate a histogram ranging from min to max with step size of sigma / 4
+  // no more than 0x1000 bins, no less than 0x10
+
+  Nbins = 4.0 * (maxValue - minValue) / sigma;
+  Nbins = MIN (0x1000, MAX (0x10, Nbins));
+
+  ALLOCATE (hist, int, Nbins);
+  memset (hist, 0, Nbins*sizeof(int));
+  
+  delta = Nbins / (max - min);
+  binsize = 1.0 / delta;
+
+  Ntotal = 0;
+  for (j = region->sy; j < region->sy + region->ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + region->sx; 
+    for (i = 0; i < region->nx; i++, V++) {
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      bin = MIN (MAX (0, (*V - min) * delta), Nbins - 1);
+      hist[bin] ++;
+      Ntotal ++;
+    }
+  }
+
+  // generate the cumulative histogram & find mode
+  ALLOCATE (cumu, int, Nbins);
+  memset (cumu, 0, Nbins*sizeof(int));
+
+  Nhist = 0;
+  Imode = -1;
+  Vmode = 0;
+  for (i = 0; i < Nbins; i++) {
+    Nhist += hist[i];
+    cumu[i] = Nhist;
+    if (hist[i] > Vmode) {
+      Imode = i;
+      Vmode = hist[i];
+    }
+  }
+
+  Ioo = ibracket (cumu, Nbins, 0.500000*Nhist, 1);
+  Im1 = ibracket (cumu, Nbins, 0.308538*Nhist, 1);
+  Ip1 = ibracket (cumu, Nbins, 0.691462*Nhist, 1);
+  Im2 = ibracket (cumu, Nbins, 0.022481*Nhist, 1);
+  Ip2 = ibracket (cumu, Nbins, 0.977519*Nhist, 1);
+
+  Voo = interpolateValue (cumu, Nbins, min, delta, Ioo, 0.500000*Nhist);
+  Vm1 = interpolateValue (cumu, Nbins, min, delta, Im1, 0.308538*Nhist);
+  Vp1 = interpolateValue (cumu, Nbins, min, delta, Ip1, 0.691462*Nhist);
+  Vm2 = interpolateValue (cumu, Nbins, min, delta, Im2, 0.022481*Nhist);
+  Vp2 = interpolateValue (cumu, Nbins, min, delta, Ip2, 0.977519*Nhist);
+
+  sigma1 = (Vp1 - Vm1);
+  sigma2 = (Vp2 - Vm2) / 4.0;
+
+  Vs = Vn = 0;
+  for (i = Im1; i < Ip1 + 1; i++) {
+    Vn += hist[i];
+    Vs += hist[i] * (i * binsize + min);
+  }
+
+  stats->median = Voo;
+  stats->mean = Vs / Vn;
+  stats->mode = Imode * delta + min;
+  stats->sigma = MIN (sigma1, sigma2);
+
+  free (hist);
+  free (cumu);
+
+  return (stats);
+}
+
+// what is the fractional bin position for the given value
+double interpolateValue (int *hist, int Nbins, float min, float binsize, int bin, float value) {
+
+  V0 = bin * binsize + min;
+  V1 = (bin + 1) * binsize + min;
+
+  // fbin = (value - V0) / (V1 - V0) + bin;
+  fbin = (value - V0) / binsize + bin;
+
+  return fbin;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/stats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/stats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/stats.c	(revision 22322)
@@ -0,0 +1,120 @@
+# include "data.h"
+
+int stats (int argc, char **argv) {
+  
+  int i, j, Nmode, Imode;
+  double Npix, N1, N2, max, min, range, median, mode, IgnoreValue;
+  float *V;
+  int sx, sy, nx, ny, *hist, Nhist, bin;
+  int Ignore, Quiet, N;
+  Buffer *buf;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 2) && (argc != 6)) {
+    gprint (GP_ERR, "USAGE: stats <buffer> sx sy nx ny\n");
+    gprint (GP_ERR, "OR:    stats <buffer>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    sx = strcmp (argv[2], "-") ? atof (argv[2]) : 0;
+    sy = strcmp (argv[3], "-") ? atof (argv[3]) : 0;
+    nx = strcmp (argv[4], "-") ? atof (argv[4]) : buf[0].matrix.Naxis[0];
+    ny = strcmp (argv[5], "-") ? atof (argv[5]) : buf[0].matrix.Naxis[1];
+  } else {
+    sx = 0;
+    sy = 0;
+    nx = buf[0].matrix.Naxis[0];
+    ny = buf[0].matrix.Naxis[1];
+  }
+
+  Npix = N1 = N2 = 0;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  max = min = *((float *)(buf[0].matrix.buffer) + sy*buf[0].matrix.Naxis[0] + sx);
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      N1 += *V;
+      N2 += (*V)*(*V);
+      Npix += 1.0;
+      max = MAX (max, *V);
+      min = MIN (min, *V);
+    }
+  }
+  N1 = N1 / Npix;
+
+/* calculate mode, median */
+  median = mode = 0.5*(max + min);
+  if ((max - min) != 0) {
+    range = 0xffff / (max - min);
+    ALLOCATE (hist, int, 0x10000);
+    bzero (hist, 0x10000*sizeof(int));
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+      for (i = 0; i < nx; i++, V++) {
+	if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+	bin = MIN (MAX (0, (*V - min) * range), 0xffff);
+	hist[bin] ++;
+      }
+    }
+    Nhist = 0;
+    for (i = 0; (i < 0xffff) && (Nhist < 0.5*Npix); i++) 
+      Nhist += hist[i];
+    median = i / range + min;
+    Nmode = hist[0];
+    Imode = 0;
+    for (i = 1; i < 0x10000; i++) {
+      if (hist[i] > Nmode) {
+	Nmode = hist[i];
+	Imode = i;
+      }
+    }
+    mode = Imode / range + min;
+    free (hist);
+  }  
+  
+  if (!Quiet) {
+    gprint (GP_LOG, "  mean    stdev    min     max   median   Npix   Total\n");
+    gprint (GP_LOG, "%7.4g %7.4g %7.4g %7.4g %7.4g %7.0f %7.4g\n", N1, sqrt (N2/Npix - N1*N1), 
+	    min, max, median, Npix, Npix*N1);
+  }
+
+  set_variable ("MIN",    min);
+  set_variable ("MAX",    max);
+  set_variable ("MEDIAN", median);
+  set_variable ("MEAN",   N1);
+  set_variable ("MODE",   mode);
+  set_variable ("TOTAL",  N1*Npix);
+  set_variable ("NPIX",   Npix);
+  set_variable ("SIGMA",  sqrt (N2/Npix - N1*N1));
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/style.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/style.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/style.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "data.h"
+
+int style (int argc, char **argv) {
+  
+  int kapa;
+  Graphdata data;
+
+  if (!style_args (&data, &argc, argv, &kapa)) return FALSE;
+
+  if (argc > 1) {
+    gprint (GP_ERR, "USAGE: style [-n Ngraph] [-x plot style] [-c color] [-pt point type] [-lt line type] [-lw line width] [-sz size]\n");
+    return (FALSE);
+  }
+  KapaSetGraphData (kapa, &data);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/subraster.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/subraster.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/subraster.c	(revision 22322)
@@ -0,0 +1,86 @@
+# include "data.h"
+
+int subraster (int argc, char **argv) {
+  
+  int i, j;
+  float *Vin, *Vout;
+  int sx, sy, nx, ny;
+  int Sx, Sy, Nx, Ny;
+  int NX, NY;
+  Buffer *ibuf, *obuf;
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: extract <from> <to> sx sy nx ny Sx Sy Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((ibuf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  NX = ibuf[0].matrix.Naxis[0];
+  NY = ibuf[0].matrix.Naxis[1];
+
+  sx = atof (argv[3]);
+  sy = atof (argv[4]);
+  nx = atof (argv[5]);
+  ny = atof (argv[6]);
+
+  Sx = atof (argv[7]);
+  Sy = atof (argv[8]);
+  Nx = atof (argv[9]);
+  Ny = atof (argv[10]);
+
+  if ((Sy + ny > Ny) || (Sx + nx > Nx)) {
+    gprint (GP_ERR, "mismatch between source and dest regions\n");
+    gprint (GP_ERR, "%d + %d > %d or %d + %d > %d\n", Sy, ny, Ny, Sx, nx, Nx);
+    return (FALSE);
+  }
+
+  /* region is not on first image */
+  if ((sx + nx < 0) || (sy + ny < 0) || 
+      (sx > ibuf[0].matrix.Naxis[0]) || 
+      (sy > ibuf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region outside of source image\n");
+    return (FALSE);
+  }
+
+  if ((Sx + nx > Nx) || (Sy + ny > Ny)) {
+    gprint (GP_ERR, "source region larger than dest region\n");
+    return (FALSE);
+  }
+  if ((Sx < 0) || (Sy < 0)) {
+    gprint (GP_ERR, "dest region out of range\n");
+    return (FALSE);
+  }
+
+  if ((obuf = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  gfits_free_matrix (&obuf[0].matrix);
+  gfits_free_header (&obuf[0].header);
+
+  obuf[0].bitpix = ibuf[0].bitpix;
+  obuf[0].unsign = ibuf[0].unsign;
+  obuf[0].bscale = ibuf[0].bscale;
+  obuf[0].bzero  = ibuf[0].bzero;
+  /* strcpy (obuf[0].name, ibuf[0].name); */
+  strcpy (obuf[0].file, ibuf[0].file);
+  gfits_copy_header (&ibuf[0].header, &obuf[0].header);
+  gfits_modify (&obuf[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&obuf[0].header, "NAXIS2", "%d", 1, Ny);
+  obuf[0].header.Naxis[0] = Nx;
+  obuf[0].header.Naxis[1] = Ny;
+  gfits_create_matrix (&obuf[0].header, &obuf[0].matrix);
+
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= NY) continue;
+    Vin = (float *)(ibuf[0].matrix.buffer) + (j + sy)*ibuf[0].matrix.Naxis[0] + sx;  
+    Vout = (float *)(obuf[0].matrix.buffer) + (j + Sy)*obuf[0].matrix.Naxis[0] + Sx;   
+    for (i = 0; i < nx; i++, Vin++, Vout++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= NX) continue;
+      *Vout = *Vin;
+    }
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/subset.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/subset.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/subset.c	(revision 22322)
@@ -0,0 +1,51 @@
+# include "data.h"
+
+/* need to check dimensions of vectors */
+
+int subset (int argc, char **argv) {
+  
+  char *out;
+  int  i, j, size;
+  Vector *ivec, *ovec, *tvec;
+
+  out = NULL;
+  ivec = ovec = tvec = NULL;
+
+  if ((argc < 6) || strcmp(argv[2], "=") || strcmp (argv[4], "if")) {
+    gprint (GP_ERR, "SYNTAX: subset vec = vec if (logic expression)\n");
+    return (FALSE);
+  }
+
+  if ((ovec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto error;
+  if ((ivec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) goto error;
+
+  out = dvomath (argc - 5, &argv[5], &size, 1);
+  if (out == NULL) {
+    print_error ();
+    goto error;
+  }
+  if ((tvec = SelectVector (out, OLDVECTOR, TRUE)) == NULL) goto error;
+  /* check size of ivec, tvec: must match */
+
+  REALLOCATE (ovec[0].elements, float, MAX (tvec[0].Nelements, 1));
+  for (j = i = 0; i < tvec[0].Nelements; i++) {
+    if (tvec[0].elements[i]) {
+      ovec[0].elements[j] = ivec[0].elements[i];
+      j++;
+    }
+  }
+  ovec[0].Nelements = j;
+  REALLOCATE (ovec[0].elements, float, MAX (ovec[0].Nelements, 1));
+
+  DeleteVector (tvec);
+  free (out);
+  return (TRUE);
+
+ error:
+  DeleteVector (tvec);
+  DeleteVector (ovec);
+  DeleteNamedVector (out);
+  free (out);
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/svd.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/svd.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/svd.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int svd (int argc, char **argv) {
+  
+  int i, Nx, Ny, status;
+  float *in, *out, *A, *U, *W, *V;
+  Vector *Vw;
+  Buffer *Ma, *Mu, *Mv;
+
+  if (argc != 6) goto usage;
+  if (strcmp (argv[2], "=")) goto usage;
+
+  if ((Ma = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Mu = SelectBuffer (argv[3], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Vw = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Mv = SelectBuffer (argv[5], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = Ma[0].header.Naxis[0];
+  Ny = Ma[0].header.Naxis[1];
+
+  /* U is Nx, Ny */
+  gfits_free_matrix (&Mu[0].matrix);
+  gfits_free_header (&Mu[0].header);
+  Mu[0].bitpix = Ma[0].bitpix;
+  Mu[0].unsign = Ma[0].unsign;
+  Mu[0].bscale = Ma[0].bscale;
+  Mu[0].bzero  = Ma[0].bzero;
+  gfits_copy_header (&Ma[0].header, &Mu[0].header);
+  gfits_create_matrix (&Mu[0].header, &Mu[0].matrix);
+  
+  /* V is Nx, Nx */
+  gfits_free_matrix (&Mv[0].matrix);
+  gfits_free_header (&Mv[0].header);
+  Mv[0].bitpix = Ma[0].bitpix;
+  Mv[0].unsign = Ma[0].unsign;
+  Mv[0].bscale = Ma[0].bscale;
+  Mv[0].bzero  = Ma[0].bzero;
+  gfits_copy_header (&Ma[0].header, &Mv[0].header);
+  gfits_modify (&Mv[0].header, "NAXIS2", "%d", 1, Nx);
+  Mv[0].header.Naxis[1] = Nx;
+  gfits_create_matrix (&Mv[0].header, &Mv[0].matrix);
+
+  /* w is Nx */
+  Vw[0].Nelements = Nx;
+  REALLOCATE (Vw[0].elements, float, Nx);
+
+  /* pointers to the various arrays */
+  A = (float *) Ma[0].matrix.buffer;
+  U = (float *) Mu[0].matrix.buffer;
+  W = (float *) Vw[0].elements;
+  V = (float *) Mv[0].matrix.buffer;
+
+  /* copy A to U (svdcmp replaces A with U) */
+  in  = A;
+  out = U;
+  for (i = 0; i < Nx*Ny; i++, in++, out++) *out = *in;
+  /* use a bcopy instead? */
+
+  status = svdcmp (U, W, V, Nx, Ny);
+  if (!status) {
+    gprint (GP_ERR, "error running svdcmp\n");
+    return (FALSE);
+  }
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "USAGE: svd A = U w Vt\n");
+  return (FALSE);
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/swapbytes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/swapbytes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/swapbytes.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "data.h"
+
+int swapbytes (int argc, char **argv) {
+  
+  int i, nx, ny;
+  char *V, tmp;
+  Buffer *buf;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: swapbytes <buffer>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  nx = buf[0].matrix.Naxis[0];
+  ny = buf[0].matrix.Naxis[1];
+
+  gprint (GP_ERR, "npix: %d\n", nx*ny);
+
+  V = buf[0].matrix.buffer;
+  for (i = 0; i < nx*ny; i++, V+=4) {
+    tmp = V[0]; V[0] = V[3]; V[3] = tmp;
+    tmp = V[1]; V[1] = V[2]; V[2] = tmp;
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/applyfit.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/applyfit.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/applyfit.sh	(revision 22322)
@@ -0,0 +1,51 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if applyfit works
+macro test1
+
+ $PASS = 1
+
+ $Cn = 2
+ $C0 = 4
+ $C1 = -2
+ $C2 = 1
+
+ create x 0 10
+
+ applyfit x y
+
+ if (y[5] != 19)
+  $PASS = 0
+  echo "Value mismatch: y[5]"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  applyfit x y
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/applyfit2d.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/applyfit2d.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/applyfit2d.sh	(revision 22322)
@@ -0,0 +1,55 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if applyfit2d works
+macro test1
+
+ $PASS = 1
+
+ $Cnn = 2
+ $CX0Y0 = 1
+ $CX1Y0 = -4
+ $CX2Y0 = 2
+ $CX1Y1 = -3
+ $CX0Y1 = 1.5
+ $CX0Y2 = -2.5
+
+ create x 0 5 0.01
+ set y = 3*cos(2*3.14159*x/2.25)
+
+ applyfit2d x y z
+
+ if (abs(z[300]-12.625) > 0.001)
+  $PASS = 0
+  echo "Value mismatch: z[300]"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  applyfit2d x y z
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/book.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/book.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/book.sh	(revision 22322)
@@ -0,0 +1,109 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test book commands
+macro test1
+
+ $PASS = 1
+
+ local bname01 bname02 pcheck pname01 pname02 tword01 tword02 wcheck
+
+ book create testb01
+ book create testb02
+ book getbook 0 -var bname01
+ book getbook 1 -var bname02
+
+ if (("$bname01" != "testb01") || ("$bname02" != "testb02"))
+  $PASS = 0
+  echo "Books not created/listed correctly!"
+ end
+
+ book newpage testb01 tpage01
+ book newpage testb01 tpage02
+ book npages testb01 -var pcheck
+ 
+ if ($pcheck != 2)
+  $PASS = 0
+  echo "Book pages not added/recorded correctly!"
+ end
+
+ book getpage testb01 0 -var pname01
+ book getpage testb01 1 -var pname02
+
+ if (("$pname01" != "tpage01") || ("$pname02" != "tpage02"))
+  $PASS = 0
+  echo "Pages not created/listed correctly!"
+ end
+
+ book setword testb01 tpage01 w01 tword01
+ book setword testb01 tpage01 w02 tword02
+ book getword testb01 tpage01 w01 -var wcheck
+
+ if ("$wcheck" != "tword01")
+  $PASS = 0
+  echo "Words not created/listed correctly!"
+ end
+
+ book delpage testb01 tpage02
+ book getpage testb01 1 -var pcheck
+
+ if ("$pcheck" != "NULL")
+  $PASS = 0
+  echo "Book pages not deleted correctly (by delpage)!"
+ end
+
+ book init testb01
+ book getpage testb01 0 -var pcheck
+
+ if ("$pcheck" != "NULL")
+  $PASS = 0
+  echo "Book pages not deleted correctly (by init)!"
+ end
+
+ book delete testb01
+ book delete testb02
+
+# delete bname01 bname02 pcheck pname01 pname02 tword01 tword02 wcheck
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i bcheck pcheck wcheck
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  book create testb
+  book getbook 0 -var bcheck
+  book newpage testb testp1
+  book newpage testb testp2
+  book npages testb -var pcheck
+  book getpage testb 0 -var pcheck
+  book setword testb testp1 testw tada
+  book getword testb testp1 testw -var wcheck
+  book delpage testb testp2
+  book init testb
+  book delete testb
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+#delete i bcheck pcheck wcheck
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/concat.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/concat.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/concat.sh	(revision 22322)
@@ -0,0 +1,48 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Does concat work?
+macro test1
+
+ $PASS = 1
+
+ create a 0 10
+ set b = a
+
+ concat a b
+
+ if ((b[] != 20) || (b[10] != 0))
+  $PASS = 0
+  echo "Concat failed!: nelements: b[] b(10)= b[10]"
+ end
+end
+
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  create a 0 10
+  set b = a
+  concat a b
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/create.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/create.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/create.sh	(revision 22322)
@@ -0,0 +1,44 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test create function
+macro test1
+ 
+ $PASS = 1
+
+ create x 0 10 0.5
+
+ if ((x[1] != 0.5) || (x[9] != 4.5))
+  $PASS = 0
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  create y 0 10 0.1
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/cumulative.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/cumulative.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/cumulative.sh	(revision 22322)
@@ -0,0 +1,46 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Does cumulative work?
+macro test1
+
+ $PASS = 1
+
+ create a 5 15
+
+ cumulative a acum
+
+ if (acum[5] != 45)
+  $PASS = 0
+  echo "Cumulative failed!: nelements: acum[] acum(5)= acum[5]"
+ end
+end
+
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  create a 5 15
+  cumulative a acum
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/cut.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/cut.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/cut.sh	(revision 22322)
@@ -0,0 +1,38 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if cut works
+macro test1
+
+ $PASS = 1
+
+ mcreate tim 100 10
+ zap tim 0 0 100 10 -v 10
+
+ cut tim xdir imx X 40 4 60 6
+ cut tim ydir imy Y 40 4 60 6
+
+ if (xdir[] != 60)
+  $PASS = 0
+ end
+ if (imx[] != 60)
+  $PASS = 0
+ end
+
+ if (ydir[] != 6)
+  $PASS = 0
+ end
+ if (imy[] != 6)
+  $PASS = 0
+ end
+
+ if (imx[0] != 10*6)
+  $PASS = 0
+ end
+ if (imy[0] != 10*60)
+  $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/delete.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/delete.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/delete.sh	(revision 22322)
@@ -0,0 +1,50 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test delete
+macro test1
+
+ $PASS = 1
+
+ $v = 7
+
+# create v 0 10
+
+ delete v
+
+ if ($?v != 0)
+  $PASS = 0
+  echo "Variable not deleted!"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  $u = testing
+  delete u
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/dimendown.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/dimendown.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/dimendown.sh	(revision 22322)
@@ -0,0 +1,60 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if dimendown works
+macro test1
+
+ $PASS = 1
+
+ mcreate timg 100 10
+ zap timg 0 0 100 10 -v 10
+ dimendown timg val
+ dimendown timg xc -x
+ dimendown timg yc -y
+
+ if (val[777] != 10)
+  $PASS = 0
+  echo "Value mismatch: val[777] (should be 10)"
+ end
+
+ if (xc[777] != 77)
+  $PASS = 0
+  echo "X Coord mismatch: xc[777] (should be 77)"
+ end
+
+ if (yc[777] != 7)
+  $PASS = 0
+  echo "Y Coord mismatch: yc[77] (should be 7)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  dimendown timg val
+  dimendown timg xc -x
+  dimendown timg yc -y
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/dimenup.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/dimenup.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/dimenup.sh	(revision 22322)
@@ -0,0 +1,48 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if dimendown works
+macro test1
+
+ $PASS = 1
+
+ create tvec 0 1000
+
+ dimenup tvec timg 10 100
+
+ stats -q timg 6 34 1 1
+
+ if ($MEAN != 346)
+  $PASS = 0
+  echo "Value mismatch: $MEAN (should be 346)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  dimenup tvec timg 10 100
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fft1d.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fft1d.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fft1d.sh	(revision 22322)
@@ -0,0 +1,17 @@
+
+list tests
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create t 0 4096 1.0
+ set f = dsin(20*t)
+
+ fft1d f 0 to Frn Fin
+
+ clear
+ section a 0.0 0.0 1.0 0.5
+ lim t Fro; box; plot -pt 7 -c blue t Fro; plot -pt 2 -c red t Frn
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fft2d.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fft2d.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fft2d.sh	(revision 22322)
@@ -0,0 +1,27 @@
+
+list tests
+# test1
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ delete x y t f Frn Fin Fro Fio dfi dfr
+ mcreate t 2048 2048
+ set x = xramp(t)
+ set y = yramp(t)
+
+ # set f = dsin(3*x)*dcos(5*y)
+ set f = exp(-0.5*((x-1024)^2 + (y-1024)^2)*0.01)
+
+ date; fft2d f 0 to Frn Fin; date
+
+ date; fft2dold f 0 to Fro Fio; date
+
+ set dfr = Frn - Fro
+ set dfi = Fin - Fio
+
+ stats dfr
+ stats dfi
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fit.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fit.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fit.sh	(revision 22322)
@@ -0,0 +1,223 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+ test8
+end
+
+# fit a line without errors
+macro test1
+ $PASS = 1
+ break -auto off
+
+ delete -q x y
+
+ create x 0 100
+ set y = 3 + 5*x
+ fit -q x y 1
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 1e-5)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 1e-5)
+   $PASS = 0
+ end
+end
+
+# fit a line with errors
+macro test2
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x + dy
+ fit -q x y 1
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.01)
+   $PASS = 0
+ end
+end
+
+# fit a line with errors and weights
+macro test3
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x + dy
+ set dy = 0.1 + zero(x)
+ fit -q x y 1 -dy dy
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.02)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.02)
+   $PASS = 0
+ end
+end
+
+# fit a line with errors, weights, and outliers 
+macro test4
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x + dy
+ set dy = 0.1 + zero(x)
+ y[5] = 23
+ y[20] = -10
+ y[50] = 0.0
+ fit -q x y 1 -dy dy -clip 3 3
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if ($Cnv != 97)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.02)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.02)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic without errors
+macro test5
+ $PASS = 1
+ break -auto off
+
+ delete -q x y
+
+ create x 0 100
+ set y = 3 + 5*x - 4*x^2
+ fit -q x y 2
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 1e-5)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 1e-5)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 1e-5)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic with errors
+macro test6
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x - 4*x^2 + dy
+ fit -q x y 2
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 0.05)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic with errors and weights
+macro test7
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x - 4*x^2 + dy
+ set dy = 0.1 + zero(x)
+ fit -q x y 2 -dy dy
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 0.05)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic with errors, weights, and outliers 
+macro test8
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x - 4*x^2 + dy
+ set dy = 0.1 + zero(x)
+ y[5] = 23
+ y[20] = -10
+ y[50] = 0.0
+
+ # it takes 4 iterations to successfully reject the outliers above...
+ fit -q x y 2 -dy dy -clip 3 4
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if ($Cnv != 97)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 0.05)
+   $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fit2d.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fit2d.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/fit2d.sh	(revision 22322)
@@ -0,0 +1,118 @@
+
+list tests
+ test1
+ test2
+ memtest1
+end
+
+# fit a function without errors
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x 0 5 0.01
+ set y = sin((2*3.14159*x)/2)
+ set z = 5-4*x+x^2-3*y+6*x*y-2*y^2
+
+ fit2d -q x y z 2
+
+ if ($Cnn != 2)
+   $PASS = 0
+   echo "Function Order Incorrect!"
+ end
+ if (abs($CX0Y0 - 5) > 1e-5)
+   $PASS = 0
+   echo "Term CX0Y0 Incorrect!"
+ end
+ if (abs($CX1Y0 + 4) > 1e-5)
+   $PASS = 0
+   echo "Term CX1Y0 Incorrect!"
+ end
+ if (abs($CX2Y0 - 1) > 1e-5)
+   $PASS = 0
+   echo "Term CX2Y0 Incorrect!"
+ end
+ if (abs($CX0Y1 + 3) > 1e-5)
+   $PASS = 0
+   echo "Term CX0Y1 Incorrect!"
+ end
+ if (abs($CX1Y1 - 6) > 1e-5)
+   $PASS = 0
+   echo "Term CX1Y1 Incorrect!"
+ end
+ if (abs($CX0Y2 + 2) > 1e-5)
+   $PASS = 0
+   echo "Term CX0Y2 Incorrect!"
+ end
+end
+
+# fit a function with errors
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x 0 5 0.01
+ set y = sin((2*3.14159*x)/2)
+ set dz = 0.1*rnd(x) - 0.05
+ set z = 5-4*x+x^2-3*y+6*x*y-2*y^2+dz
+
+ fit2d -q x y z 2
+
+ if ($Cnn != 2)
+   $PASS = 0
+   echo "Function Order Incorrect!"
+ end
+ if (abs($CX0Y0 - 5) > 0.01)
+   $PASS = 0
+   echo "Term CX0Y0 Incorrect!"
+ end
+ if (abs($CX1Y0 + 4) > 0.01)
+   $PASS = 0
+   echo "Term CX1Y0 Incorrect!"
+ end
+ if (abs($CX2Y0 - 1) > 0.01)
+   $PASS = 0
+   echo "Term CX2Y0 Incorrect!"
+ end
+ if (abs($CX0Y1 + 3) > 0.01)
+   $PASS = 0
+   echo "Term CX0Y1 Incorrect!"
+ end
+ if (abs($CX1Y1 - 6) > 0.01)
+   $PASS = 0
+   echo "Term CX1Y1 Incorrect!"
+ end
+ if (abs($CX0Y2 + 2) > 0.01)
+   $PASS = 0
+   echo "Term CX0Y2 Incorrect!"
+ end
+end
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ create x 0 5 0.01
+ set y = sin((2*3.14159*x)/2)
+ set dz = 0.1*rnd(x) - 0.05
+ set z = 5-4*x+x^2-3*y+6*x*y-2*y^2+dz
+
+ for i 0 1000
+  fit2d -q x y z 2
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/gaussj.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/gaussj.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/gaussj.sh	(revision 22322)
@@ -0,0 +1,294 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+end
+
+# a very simple diagonal matrix equation
+macro test1
+ $PASS = 1
+ break -auto off
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][0] = 2
+ A[1][1] = 2
+ A[2][2] = 2
+
+ A[0][1] = -1
+ A[1][2] = -1
+ A[1][0] = -1
+ A[2][1] = -1
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ if (abs(A[0][0] - 0.75) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][2] - 0.25) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[1][0] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][1] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][2] - 0.50) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[2][0] - 0.25) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][2] - 0.75) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(B[0] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[1] - 2.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[2] - 2.00) > 0.01)
+  $PASS = 0
+ end
+end
+
+# a very simple off-diagonal matrix equation
+macro test2
+ $PASS = 1
+ break -auto off
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][1] = 2
+ A[1][0] = 2
+ A[2][2] = 2
+
+ A[0][0] = -1
+ A[1][2] = -1
+ A[1][1] = -1
+ A[2][0] = -1
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ # echo A[0][0] A[0][1] A[0][2]
+ # echo A[1][0] A[1][1] A[1][2]
+ # echo A[2][0] A[2][1] A[2][2]
+
+ if (abs(A[1][0] - 0.75) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][2] - 0.25) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[0][0] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][1] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][2] - 0.50) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[2][0] - 0.25) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][2] - 0.75) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(B[1] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[0] - 2.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[2] - 2.00) > 0.01)
+  $PASS = 0
+ end
+end
+
+# a singular matrix equation
+macro test3
+ $PASS = 1
+ break -auto off
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][0] = 2
+ A[1][0] = 2
+ A[2][2] = 2
+
+ A[0][1] = -1
+ A[1][1] = -1
+ A[2][1] = -1
+
+ gaussj -q A B
+ if ($STATUS)
+   $PASS = 0
+ end
+end
+
+# a very large matrix equation
+macro test4
+ $PASS = 1
+ break -auto off
+
+ $Ndim = 50
+ mcreate A $Ndim $Ndim
+ create B 0 $Ndim
+
+ # generate the diagonal + off-diagonal elements
+ for i 0 $Ndim
+   A[$i][$i] = 2.0
+   if ($i > 0)
+     A[$i][$i-1] = -1.0
+   end
+   if ($i < $Ndim - 1)
+     A[$i][$i+1] = -1.0
+   end
+ end
+
+ set inB = B
+ set inA = A
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ set meas = zero(inB)
+ for i 0 B[]
+  for j 0 B[]
+   meas[$i] = meas[$i] + inA[$i][$j] * B[$j]
+  end
+ end
+
+ for i 0 inB[]
+  if (abs(inB[$i]-meas[$i]) > 1e-3)
+    $PASS = 0
+    echo inB[$i] meas[$i] {inB[$i]-meas[$i]}
+  end
+ end
+end
+
+# a nearly singular matrix equation
+macro test5
+ $PASS = 1
+ break -auto off
+
+ delete A B inA inB meas
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][0] = 2
+ A[1][0] = 2.00001
+ A[2][2] = 2
+
+ A[0][1] = -1
+ A[1][1] = -1
+ A[2][1] = -1
+
+ set inB = B
+ set inA = A
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ set meas = zero(inB)
+ for i 0 B[]
+  for j 0 B[]
+   meas[$i] = meas[$i] + inA[$i][$j] * B[$j]
+  end
+ end
+
+ for i 0 inB[]
+  if (abs(inB[$i]-meas[$i]) > 1e-5)
+    $PASS = 0
+  end
+ end
+end
+
+# a very large matrix equation
+# timing on my laptop: 100 : ~1sec; 300 : ~7sec; 1000 : ~88sec
+# note: at Ndim = 1000, it failed (Ax - B was in the range -2..+2)
+macro test6
+ if ($0 != 2)
+  echo "USAGE: test6 (Ndim)"
+  break
+ end
+
+ $PASS = 1
+ break -auto off
+
+ $Ndim = $1
+ mcreate A $Ndim $Ndim
+ create B 0 $Ndim
+
+ # generate the diagonal + off-diagonal elements
+ for i 0 $Ndim
+   A[$i][$i] = 2.0
+   if ($i > 0)
+     A[$i][$i-1] = -1.0
+   end
+   if ($i < $Ndim - 1)
+     A[$i][$i+1] = -1.0
+   end
+ end
+
+ set inB = B
+ set inA = A
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ set meas = zero(inB)
+ for i 0 B[]
+  for j 0 B[]
+   meas[$i] = meas[$i] + inA[$i][$j] * B[$j]
+  end
+ end
+
+ for i 0 inB[]
+  if (abs(inB[$i]-meas[$i]) > 1e-3)
+    $PASS = 0
+    echo inB[$i] meas[$i] {inB[$i]-meas[$i]}
+  end
+ end
+end
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/histogram.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/histogram.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/histogram.sh	(revision 22322)
@@ -0,0 +1,52 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if histogram works
+macro test1
+
+ $PASS = 1
+
+ local i
+
+ create x 0 10 0.1
+
+ for i 45 55
+  x[$i] = 4.5
+ end
+
+ histogram x xhis 0 10 0.1
+
+ if ((xhis[10] != 1) || (xhis[45] != 10))
+  $PASS = 0
+  echo "Value mismatch: xhis[10] xhis[45] (should be 1,10)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  histogram x xhis 0 10 0.1
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/imhist.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/imhist.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/imhist.sh	(revision 22322)
@@ -0,0 +1,58 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if imhist works
+macro test1
+
+ $PASS = 1
+
+ local i
+
+ mcreate buff 100 10
+
+ zap buff 40 0 10 10 -v 17
+ zap buff 60 5 5 2 -v 6
+
+ imhist buff xvec yvec
+
+ if ((xvec[1024] != 17) || (yvec[1024] != 100))
+  $PASS = 0
+  echo "Value mismatch: xvec[1024] yvec[1024] (should be 17,100)"
+ end
+
+ imhist -q buff xvec yvec -region 40 0 25 10 -range 0 10
+
+ if ((xvec[1024] != 10) || (yvec[1024] != 100))
+  $PASS = 0
+  echo "Value mismatch: xvec[1024] yvec[1024] (should be 10,100)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  imhist -q buff xvec yvec -region 40 0 25 10 -range 0 10
+ end
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/imsmooth.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/imsmooth.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/imsmooth.sh	(revision 22322)
@@ -0,0 +1,59 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if imsmooth works
+macro test1
+
+ $PASS = 1
+
+ local i j
+
+ mcreate buff 100 100
+
+ for i 0 99
+  for j 0 99
+   zap buff $i $j 1 1 -v {100*rnd($i)}
+  end
+ end
+
+ stats -q buff
+ $s1 = $SIGMA
+
+ imsmooth buff 10
+ stats -q buff
+ $s2 = $SIGMA
+
+ if ($s2/$s1 >= 0.1)
+  $PASS = 0
+  echo "Inadequate noise reduction: {$s2/$s1*100}\% (should be less than 10%)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  imsmooth buff 10
+ end
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/integrate.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/integrate.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/integrate.sh	(revision 22322)
@@ -0,0 +1,47 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if integrate works
+macro test1
+
+ $PASS = 1
+
+ create x 0 10 0.01
+ set y = 1+2*x+3*x^2
+
+ integrate x y 1 5
+
+ if (abs ($sum-152) > 0.5)
+  $PASS = 0
+  echo "Inaccurate result (should be 152): $sum"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  integrate x y 1 5
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/interpolate.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/interpolate.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/interpolate.sh	(revision 22322)
@@ -0,0 +1,48 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if interpolate works
+macro test1
+
+ $PASS = 1
+
+ create x0 0 10 0.1
+ set y0 = 1+2*x0+3*x0^2
+ create x1 0 10 0.001
+ interpolate x0 y0 x1 y1
+ integrate x1 y1 1 5
+
+ if (abs ($sum-152) > 0.08)
+  $PASS = 0
+  echo "Inaccurate result (should be 152): $sum"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  interpolate x0 y0 x1 y1
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/peak.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/peak.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/peak.sh	(revision 22322)
@@ -0,0 +1,103 @@
+
+list tests
+ test1
+ test2
+ test3
+ testmem1
+end
+
+# test using full range
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x 0 100
+ set y = zero (x)
+ y[50] = 1
+
+ peak -q x y 0 100
+
+ if ($peakpos != 50)
+   $PASS = 0
+ end
+ if ($peaknum != 51)
+   $PASS = 0
+ end
+ if ($peakval != 1)
+   $PASS = 0
+ end
+end
+
+# test using auto range
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x 0 100
+ set y = zero (x)
+ y[50] = 1
+
+ peak -q x y 
+
+ if ($peakpos != 50)
+   $PASS = 0
+ end
+ if ($peaknum != 51)
+   $PASS = 0
+ end
+ if ($peakval != 1)
+   $PASS = 0
+ end
+end
+
+# test using constrained range
+macro test3
+ $PASS = 1
+ break -auto off
+
+ create x 0 100
+ set y = zero (x)
+ y[60] = 2
+ y[50] = 1
+ y[40] = 2
+
+ peak -q x y 45 55
+
+ if ($peakpos != 50)
+   $PASS = 0
+ end
+ if ($peaknum != 51)
+   $PASS = 0
+ end
+ if ($peakval != 1)
+   $PASS = 0
+ end
+end
+
+# test memory usage
+macro testmem1
+ break -auto off
+
+ create x 0 1000
+ set y = zero (x)
+ y[500] = 100
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   peak -q x y 400 600
+ end
+ 
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/periodogram.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/periodogram.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/periodogram.sh	(revision 22322)
@@ -0,0 +1,143 @@
+
+list tests
+ test1
+ test2
+ test3
+ memtest1
+end
+
+# test using even samples
+macro test1
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q t f period power
+
+ create t 0 100
+ set f = sin(2*$PI*t/$P)
+ periodogram t f 5 50 period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.5)
+   $PASS = 0
+   echo "OFFSET: {$peakpos - $P}"
+ end
+end
+
+# test using random samples
+macro test2
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q x t f period power
+
+ create x 0 100
+ set t = 100 * rnd(x) 
+ set f = sin(2*$PI*t/$P)
+ periodogram t f 5 50 period power
+
+ # lim -n 0 t f; clear; box; plot -x 2 -pt 2 t f
+ # lim -n 1 period power; clear; box; plot period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.5)
+   $PASS = 0
+ end
+end
+
+# test using random samples, higher frequency
+macro test3
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 2.0
+
+ delete -q x t f period power
+
+ create x 0 100
+ set t = 100 * rnd(x) 
+ set f = sin(2*$PI*t/$P)
+
+ periodogram t f 1 10 period power
+
+ # lim -n 0 t f; clear; box; plot -x 2 -pt 2 t f
+ # lim -n 1 period power; clear; box; plot period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.05)
+   $PASS = 0
+ end
+end
+
+# test using random samples, offset start
+macro test4
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q x t f period power
+
+ create x 500 800
+ set t = 300 * rnd(x) + 500
+ set f = sin(2*$PI*t/$P)
+
+ periodogram t f 2 30 period power
+
+#  lim -n 0 t f; clear; box; plot -x 2 -pt 2 t f
+#  lim -n 1 period power; clear; box; plot period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.05)
+   $PASS = 0
+ end
+end
+
+# Memory test
+macro memtest1
+
+ local i
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q x t f period power
+
+ create x 500 800
+ set t = 300 * rnd(x) + 500
+ set f = sin(2*$PI*t/$P)
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  periodogram t f 2 30 period power
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/queues.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/queues.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/queues.sh	(revision 22322)
@@ -0,0 +1,347 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test4.1
+ test5
+ test5.1
+ testmem1
+ testmem2
+ testmem3
+ testmem4.0
+ testmem4.1
+ testmem4.2
+ testmem4.3
+ testmem5.0
+ testmem5.1
+end
+
+# test queueinit
+macro test1
+ $PASS = 1
+ queueinit dummy
+ queuesize dummy -var N
+
+ if ($N != 0)
+   $PASS = 0
+ end
+end
+
+# test queueinit memory
+macro testmem1
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuesize
+macro test2
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+ queuesize dummy -var N
+
+ if ($N != 1)
+   $PASS = 0
+ end
+end
+
+# test queuesize memory
+macro testmem2
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queuesize dummy -var N
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuedelete
+macro test3
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+ queuepush dummy foobar
+ queuepush dummy foobar
+
+ queuedelete dummy
+ queuepush dummy foobar
+ queuesize dummy -var N
+
+ if ($N != 1)
+   $PASS = 0
+ end
+end
+
+# test queuedelete memory
+macro testmem3
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+   queuedelete dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop
+macro test4
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+ queuepop  dummy -var N
+
+ if ("$N" != "foobar")
+   $PASS = 0
+ end
+end
+
+# test queuepush / queuepop
+macro test4.1
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foo1
+ queuepush dummy foo2
+ queuepush dummy foo3
+
+ queuepop  dummy -var N
+ if ("$N" != "foo1")
+   $PASS = 0
+ end
+ queuepop  dummy -var N
+ if ("$N" != "foo2")
+   $PASS = 0
+ end
+ queuepop  dummy -var N
+ if ("$N" != "foo3")
+   $PASS = 0
+ end
+ queuesize dummy -var N
+ if ($N != 0)
+   $PASS = 0
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.0
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+   queuepop dummy -var N
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.1
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+   queuepop dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.2
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.3
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ queueinit dummy
+
+ output /dev/null
+ for i 0 10000
+   queuepush dummy foobar
+   queuepop dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop with keys
+macro test5
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy "test 1 word"
+ queuepush dummy "test 2 word"
+ queuepush dummy "test 3 word"
+ queuepop  dummy -var N -key 1 2
+
+ if ("$N" != "test 2 word")
+   $PASS = 0
+ end
+end
+
+# test queuepush / queuepop with keys
+macro test5.1
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy "test 1 word"
+ queuepush dummy "test 2 word"
+ queuepush dummy "bird 2 word"
+ queuepop  dummy -var N -key 0:1 test:2
+
+ if ("$N" != "test 2 word")
+   $PASS = 0
+ end
+end
+
+# memory test for queuepush / queuepop with keys
+macro testmem5.0
+ $PASS = 1
+ queueinit dummy
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queuepush dummy "test 1 word"
+   queuepush dummy "test 2 word"
+   queuepush dummy "test 3 word"
+   queuepop dummy -var N -key 1 2
+   queuepop dummy -var N -key 1 1
+   queuepop dummy -var N -key 1 3
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+   queuelist
+ end
+end
+
+# memory test for queuepush / queuepop with keys
+macro testmem5.1
+ $PASS = 1
+ queueinit dummy
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queuepush dummy "test 1 word"
+   queuepop dummy -var N -key 1 1
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/stats.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/stats.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/stats.sh	(revision 22322)
@@ -0,0 +1,156 @@
+
+$skip = 0
+
+list tests
+ test1
+ test2
+ test3
+end
+
+# test using full range
+macro test1
+ $PASS = 1
+ break -auto off
+
+ gaussdev dev {100*100} 0.0 1.0
+ dimenup dev buf 100 100
+ stats -q buf 
+ $npix = 100*100
+
+ if ($STATUS == 0)
+   echo "failed command"
+   $PASS = 0
+ end
+ if ($NPIX != $npix) 
+   echo "failed npix"
+   $PASS = 0
+ end
+ if (abs($MEAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed mean"
+   $PASS = 0
+ end
+ if (abs($MEDIAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed median"
+   $PASS = 0
+ end
+ if (abs($SIGMA - 1.0) > (2.0/sqrt($npix))) 
+   echo "failed sigma"
+   $PASS = 0
+ end
+ if ($MIN < -4.0) 
+   echo "failed min"
+   $PASS = 0
+ end
+ if ($MAX > +4.0)
+   echo "failed max"
+   $PASS = 0
+ end
+# I don't this MODE is being correctly calculated.
+ if ($skip && (abs($MODE - 0.0) > (2.0/sqrt($npix))))
+   echo "MODE fails: known problem with mode"
+   $PASS = 0
+ end
+ if (abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo "failed total"
+   $PASS = 0
+ end
+end
+
+# test using full range
+macro test2
+ $PASS = 1
+ break -auto off
+
+ gaussdev dev {100*100} 0.0 1.0
+ dimenup dev buf 100 100
+ stats -q buf  - - - -
+ $npix = 100*100
+
+ if ($STATUS == 0)
+   echo "failed command"
+   $PASS = 0
+ end
+ if ($NPIX != $npix) 
+   echo "failed npix"
+   $PASS = 0
+ end
+ if (abs($MEAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed mean"
+   $PASS = 0
+ end
+ if (abs($MEDIAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed median"
+   $PASS = 0
+ end
+ if (abs($SIGMA - 1.0) > (2.0/sqrt($npix))) 
+   echo "failed sigma"
+   $PASS = 0
+ end
+ if ($MIN < -4.0) 
+   echo "failed min"
+   $PASS = 0
+ end
+ if ($MAX > +4.0)
+   echo "failed max"
+   $PASS = 0
+ end
+# I don't this MODE is being correctly calculated.
+ if ($skip && (abs($MODE - 0.0) > (2.0/sqrt($npix))))
+   echo "MODE fails: known problem with mode"
+   $PASS = 0
+ end
+ if (abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo $TOTAL {$MEAN*$npix} {abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo "failed total"
+   $PASS = 0
+ end
+end
+
+# test using full range
+macro test3
+ $PASS = 1
+ break -auto off
+
+ gaussdev dev {100*100} 0.0 1.0
+ dimenup dev buf 100 100
+ stats -q buf 10 10 10 10
+ $npix = 10*10
+
+ if ($STATUS == 0)
+   echo "failed command"
+   $PASS = 0
+ end
+ if ($NPIX != $npix) 
+   echo "failed npix"
+   $PASS = 0
+ end
+ if (abs($MEAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed mean"
+   $PASS = 0
+ end
+ if (abs($MEDIAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed median"
+   $PASS = 0
+ end
+ if (abs($SIGMA - 1.0) > (2.0/sqrt($npix))) 
+   echo "failed sigma"
+   $PASS = 0
+ end
+ if ($MIN < -4.0) 
+   echo "failed min"
+   $PASS = 0
+ end
+ if ($MAX > +4.0)
+   echo "failed max"
+   $PASS = 0
+ end
+# I don't this MODE is being correctly calculated.
+ if ($skip && (abs($MODE - 0.0) > (2.0/sqrt($npix))))
+   echo "MODE fails: known problem with mode"
+   $PASS = 0
+ end
+ if (abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo "failed total"
+   $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/vgauss.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/vgauss.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/vgauss.sh	(revision 22322)
@@ -0,0 +1,99 @@
+
+list tests
+ test1
+ test2
+ test3
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 5 * exp(-0.5*x^2/3^2)
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 10
+ $C3 = 1
+ set dy = sqrt(y)
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 5.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.01)
+   $PASS = 0
+ end
+end
+
+# noise of 0.1
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 1000 * exp(-0.5*x^2/3^2)
+ set dy = (rnd(y) - 0.5)/0.5
+ set y = y + dy
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 900
+ $C3 = 1
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 1000.0) > 0.1)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.1)
+   $PASS = 0
+ end
+end
+
+# poisson-distributed noise
+macro test3
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 1000 * exp(-0.5*x^2/3^2)
+
+ gaussdev dY y[] 0.0 1.0
+ set dy = dY * sqrt(y)
+ set y = y + dy
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 900
+ $C3 = 1
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 1000.0) > 1)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.2)
+   $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/vmaxwell.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/vmaxwell.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/test/vmaxwell.sh	(revision 22322)
@@ -0,0 +1,103 @@
+
+list tests
+# test1
+ test2
+# test3
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 5 * exp(-0.5*x^2/3^2)
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 10
+ $C3 = 1
+ set dy = sqrt(y)
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 5.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.01)
+   $PASS = 0
+ end
+end
+
+# noise of 0.1
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x 0 1000 1
+ set y = 1000 * (x-200)^2 * exp(-0.5*(x-300)^2/30^2)
+ set dy = (rnd(y) - 0.5)/0.5
+ set y = y + dy
+
+ lim x y; clear; box; plot -x 1 -c black x y
+
+ $C0 = 250
+ $C1 = 25
+ $C2 = 900
+ $C3 = 1
+ $C4 = 190
+
+ vmaxwell x y dy yfit
+ plot x yfit -c red
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 1000.0) > 0.1)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.1)
+   $PASS = 0
+ end
+end
+
+# poisson-distributed noise
+macro test3
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 1000 * exp(-0.5*x^2/3^2)
+
+ gaussdev dY y[] 0.0 1.0
+ set dy = dY * sqrt(y)
+ set y = y + dy
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 900
+ $C3 = 1
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 1000.0) > 1)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.2)
+   $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/teststats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/teststats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/teststats.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include "data.h"
+
+int teststats (int argc, char **argv) {
+  
+  int i, N;
+  double max, min, sum, var, dvar, mean, stdev;
+  float *X, IgnoreValue;
+  int Ignore, Quiet;
+
+  int *Nval, bin, Nmode, Nmed;
+  double dx, mode, median;
+  Vector *vec;
+
+  Ignore = FALSE;
+  if (N = get_argument (argc, argv, "-ignore")) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if (N = get_argument (argc, argv, "-q")) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if (N = get_argument (argc, argv, "-quiet")) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: teststats (vector)\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Mean = Stdev = 0; 
+  /* first pass: measure the sample statistics */
+  for (j = 0; j < 3; j++) {
+    /* calculate mean, npix */
+    X = vec[0].elements;
+    max = min = *X;
+    sum = N = 0;
+    for (i = 0; i < vec[0].Nelements; i++, X++) {
+      if (!finite (*X)) continue;
+      if (Ignore && (*X == IgnoreValue)) continue;
+      if ((j > 0) && (fabs (*X - Mean) > 3*stdev)) continue;
+      sum += *X;
+      N++;
+    }      
+    mean = sum / N;
+    /* calculate stdev */
+    X = vec[0].elements;
+    var = 0;
+    for (i = 0; i < vec[0].Nelements; i++, X++) {
+      if (!finite (*X)) continue;
+      if (Ignore && (*X == IgnoreValue)) continue;
+      if ((j > 0) && (fabs (*X - mean) > 3*stdev)) continue;
+      dvar = (*X - mean);
+      var += dvar*dvar;
+    }      
+    stdev = sqrt (var / N);
+    Mean = mean;
+  }
+  mean = Mean;
+  set_variable ("MEAN_C",     mean);
+  set_variable ("SIGMA_C",    stdev);
+
+  /* construct histogram with resolution of 0.1*stdev and range -1000*dx : 1000*dx centered on mean */
+  dx = 0.1*stdev;
+  min = mean - 1000*dx;
+  max = mean + 1000*dx;
+  NVAL = 1 + (int)((max - min) / dx);
+  ALLOCATE (Nval, int, NVAL);
+  bzero (Nval, NVAL*sizeof(int));
+  X = vec[0].elements;
+  for (i = 0; i < vec[0].Nelements; i++, X++) {
+    if (!finite (*X)) continue;
+    if (Ignore && (*X == IgnoreValue)) continue;
+    bin = MAX (0, MIN (NVAL, (*X - min) / dx));
+    Nval[bin] ++;
+  }      
+
+  /* find mode */
+  Nmode = 0;
+  mode = Nval[Nmode];
+  for (i = 0; i < NVAL; i++) {
+    if (mode < Nval[i]) {
+      Nmode = i;
+      mode = Nval[Nmode];
+    }
+  }
+  mode = Nmode * dx + min;
+
+  
+
+  if (!Quiet) {
+    gprint (GP_ERR, "mean: %f, stdev: %f, min: %f, max: %f, median: %f, mode: %f, Npts: %d\n", 
+	     mean, stdev, min, max, median, mode, N);
+  }
+
+  set_variable ("MIN",      min);
+  set_variable ("MAX",      max);
+  set_variable ("MEDIAN",   median);
+  set_variable ("MODE",     mode);
+  set_variable ("TOTAL",    sum);
+  set_variable ("MEAN",     mean);
+  set_variable ("SIGMA",    stdev);
+
+  set_int_variable ("NPIX", N);
+
+  free (Nval);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/textline.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/textline.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/textline.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "data.h"
+
+int textline (int argc, char **argv) {
+
+  int N, size, FracPositions;
+  char name[64];
+  double x, y, angle;
+  int kapa;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  if ((N = get_argument (argc, argv, "-fn"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    size = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    KapaSetFont (kapa, name, size);
+  } 
+
+  /* FracPositions uses coordinates of 0-1 relative to axis range */
+  FracPositions = FALSE;
+  if ((N = get_argument (argc, argv, "-frac"))) {
+    remove_argument (N, &argc, argv);
+    FracPositions = TRUE;
+  } 
+
+  angle = 0.0;
+  if ((N = get_argument (argc, argv, "-rot"))) {
+    remove_argument (N, &argc, argv);
+    angle = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  } 
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: text x y (line) [-fn (font) size] [-rot angle]\n");
+    return (FALSE);
+  }
+
+  if (strlen (argv[3]) > 127) {
+    gprint (GP_ERR, "labels currently limited to 127 chars\n");
+    return (FALSE);
+  }
+
+  x = atof (argv[1]);
+  y = atof (argv[2]);
+  
+  if (FracPositions) {
+    x =  x * (graphmode.xmax - graphmode.xmin) + graphmode.xmin;
+    y =  y * (graphmode.ymax - graphmode.ymin) + graphmode.ymin;
+  }    
+
+  KapaSendTextline (kapa, argv[3], x, y, angle);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tv.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tv.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tv.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "data.h"
+
+int tv (int argc, char **argv) {
+  
+  int N, kapa;
+  char *name, *file;
+  Coords coords;
+  Buffer *buf;
+  KiiImage image;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  /* shell exits on pipe close, FIX */
+  if ((N = get_argument (argc, argv, "-kill"))) {
+    KiiClose (kapa);
+    return (TRUE);
+  }
+
+  data.logflux = FALSE;
+  if ((N = get_argument (argc, argv, "-log"))) {
+    remove_argument (N, &argc, argv);
+    data.logflux = TRUE;
+  }
+
+  // use the currently-set zero,range values if not supplied
+  if ((argc != 2) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: tv <buffer> [zero range] [-n Nimage] [-log] [-kill]\n");
+    return (FALSE);
+  }
+
+  if (argc == 4) {
+    data.zero = atof (argv[2]);
+    data.range = atof (argv[3]);
+    if (data.range == 0.0) data.range = 0.001;
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  GetCoords (&coords, &buf[0].header);
+  
+  image.data1d = (float *) buf[0].matrix.buffer;
+  image.Nx = buf[0].matrix.Naxis[0];
+  image.Ny = buf[0].matrix.Naxis[1];
+
+  // send only the root of the file, not the full path
+  file = filerootname (buf[0].file);
+  strcpy (data.file, file);
+  free (file);
+
+  strcpy (data.name, argv[1]);
+  
+  KiiNewPicture1D (kapa, &image, &data, &coords);
+
+  set_str_variable ("TV", argv[1]);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvchannel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvchannel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvchannel.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "data.h"
+
+int tvchannel (int argc, char **argv) {
+  
+  int N, kapa, Nchannel;
+  char *name;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // use the currently-set zero,range values if not supplied
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: tvchannel (channel)\n");
+    return (FALSE);
+  }
+
+  Nchannel = atoi (argv[1]);
+  if (Nchannel == 0) {
+    // try the string values R/Red, G/Green, B/Blue
+    if (!strcasecmp (argv[1], "R") || !strcasecmp (argv[1], "RED")) {
+      Nchannel = 1;
+    }
+    if (!strcasecmp (argv[1], "G") || !strcasecmp (argv[1], "GREEN")) {
+      Nchannel = 2;
+    }
+    if (!strcasecmp (argv[1], "B") || !strcasecmp (argv[1], "BLUE")) {
+      Nchannel = 3;
+    }
+  }
+  if ((Nchannel < 1) || (Nchannel > 3)) {
+    gprint (GP_ERR, "invalid channel : use 1 - 3 or (R)ed, (G)reen, (B)lue\n");
+    return (FALSE);
+  }
+    
+  KiiSetChannel (kapa, Nchannel - 1);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvcolors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvcolors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvcolors.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "data.h"
+
+int tvcolors (int argc, char **argv) {
+  
+  int N, kapa;
+  char *name;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // use the currently-set zero,range values if not supplied
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: tvcolors (colormap)\n");
+    return (FALSE);
+  }
+
+  KiiSetColormap (kapa, argv[1]);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvcontour.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvcontour.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvcontour.c	(revision 22322)
@@ -0,0 +1,287 @@
+# include "data.h"
+# define LL { \
+ dx =  d00 / (*v01 - *v00); \
+ dy = -d00 / (*v10 - *v00); \
+ x = i - 0.5; \
+ y = j - dy - 0.5; }
+
+# define UL { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = 1 - tmp; \
+ dx =  d10 / (*v11 - *v10); \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+      
+# define LR { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = 1 - tmp; \
+ dy = d01 / (*v11 - *v01); \
+ x = i + tmp - 0.5; \
+ y = j - 0.5; }
+
+# define UR { \
+ tmp = d10 / (*v11 - *v10); \
+ dx = 1 - tmp; \
+ dy = -d11 / (*v01 - *v11); \
+ x = i + tmp - 0.5; \
+ y = j + 1 - 0.5; }
+      
+# define HZ { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = d01 / (*v11 - *v01) - tmp; \
+ dx = 1; \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+
+# define VT { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = d10 / (*v11 - *v10) - tmp; \
+ x = i + tmp - 0.5; \
+ dy = 1; \
+ y = j - 0.5; }
+
+# define DUMP { \
+overlay[Noverlay].type = KII_OVERLAY_LINE; \
+overlay[Noverlay].x = Npix*x; \
+overlay[Noverlay].y = Npix*y; \
+overlay[Noverlay].dx = Npix*dx; \
+overlay[Noverlay].dy = Npix*dy; \
+Noverlay ++; \
+CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000); \
+}
+
+int tvcontour (int argc, char **argv) {
+
+  int i, j, ii, jj, Npix, Nx, Ny;
+  float level, d00, d01, d10, d11, tmp;
+  float x, y, dx, dy;
+  float *v00, *v01, *v10, *v11;
+  float *Vout, *Vin, *matrix;
+  char *name;
+  int kapa, N, Noverlay, NOVERLAY;
+  Buffer *buf;
+  KiiOverlay *overlay;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: contour <buffer> (overlay) level [Npix]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  level = atof (argv[3]);
+  if (argc == 5) 
+    Npix = (int) MAX (atof (argv[4]), 1); 
+  else 
+    Npix = 1;
+  level *= Npix*Npix;
+
+  /*** make rebinned image ***/
+  Nx = buf[0].header.Naxis[0]/Npix;
+  Ny = buf[0].header.Naxis[1]/Npix;
+  if (Npix != 1) {
+    gprint (GP_LOG, "rebin by a factor of %d (%d,%d)\n", Npix, Nx, Ny);
+    ALLOCATE (matrix, float, Nx*Ny);
+    bzero (matrix, Nx*Ny*sizeof(float));
+	
+    for (j = 0; j < Ny; j++) {
+      for (jj = 0; jj < Npix; jj++) {
+	Vout = matrix + j*Nx;
+	Vin  = (float *)(buf[0].matrix.buffer) + (j*Npix + jj)*buf[0].header.Naxis[0];
+	for (i = 0; i < Nx; i++, Vout++) {
+	  for (ii = 0; ii < Npix; ii++, Vin++) {
+	    *Vout += *Vin;
+	  }
+	}
+      }
+    }
+  } else {
+    gprint (GP_ERR, "using scale of 1\n");
+    matrix = (float *)(buf[0].matrix.buffer);
+  }
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  v00 = matrix;
+  v01 = matrix + 1;
+  v10 = matrix + Nx;
+  v11 = matrix + Nx + 1;
+  d01 = (level - *v00);
+  d11 = (level - *v10);
+  for (j = 1; j < Ny; j++) {
+    if (!(j%10)) gprint (GP_ERR, ".");
+    for (i = 1; i < Nx; i++, v00++, v01++, v10++, v11++) {
+
+      d00 = d01;
+      d10 = d11;
+      d01 = (level - *v01);
+      d11 = (level - *v11);
+
+      if (((d00 > 0) && (d01 > 0) && (d10 > 0) && (d11 > 0)) ||
+	  ((d00 < 0) && (d01 < 0) && (d10 < 0) && (d11 < 0)))
+	continue;
+
+      if ((d00 > 0) && (d10 <= 0)) { 
+	if ((d01 <= 0) && (d11 <= 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* \\  */
+	  LL;
+	  DUMP;
+	  UR;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 <= 0) && (d10 > 0)) {
+	if ((d01 > 0) && (d11 > 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* //  */
+	  UL;
+	  DUMP;
+	  LR;
+	  DUMP;
+	  continue;
+	}
+      }
+      
+
+      if ((d00 <= 0) && (d10 <= 0)) { 
+	if ((d01 > 0) && (d11 <= 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 > 0) && (d10 > 0)) { 
+	if ((d01 <= 0) && (d11 > 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+    }
+    /* skip left-hand edge */
+    v00++; v01++; v10++; v11++;
+    d01 = (level - *v00);
+    d11 = (level - *v10);
+  }
+
+  /****** bottom line *******/
+  v00 = matrix;  
+  v01 = matrix + 1;  
+  y = 0;
+  dx = 0;
+  dy = -0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+
+  /********** top line *******/
+  v00 = matrix + Nx*(Ny - 1);  
+  v01 = v00 + 1;
+  y = Ny - 1;
+  dx = 0;
+  dy = 0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+
+  /******** left line *********/
+  v00 = matrix; 
+  v01 = matrix + Nx;
+  x = 0;
+  dx = -0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+
+  /******** right line *********/
+  v00 = matrix + Nx - 1; 
+  v01 = v00 + Nx;
+  x = Nx - 1;
+  dx = 0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+  
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[2]);
+  free (overlay);
+
+  if (Npix != 1) free (matrix);
+  gprint (GP_ERR, "\n");
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvgrid.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvgrid.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/tvgrid.c	(revision 22322)
@@ -0,0 +1,152 @@
+# include "data.h"
+# define TEN(X) (pow(10.0, (double)(X)))
+
+int tvgrid (int argc, char **argv) {
+  
+  int ndig1, ndig2, NX, NY, connect;
+  int tDEC, tRA;
+  double ra, dec, ra0, dec0, ra1, dec1;
+  double x0, y0, x1, y1;
+  double dDEC, fDEC, dRA, fRA;
+  char format[16];
+  Coords coords;
+  int kapa, N, Noverlay, NOVERLAY;
+  char *name;
+  Buffer *buf;
+  KiiOverlay *overlay;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: tvgrid (overlay) (buffer)\n");
+    gprint (GP_ERR, " (overlay) may be: red, green, blue, yellow\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  GetCoords (&coords, &buf[0].header);
+
+  XY_to_RD (&ra0, &dec0, 0.0, 0.0, &coords);
+  XY_to_RD (&ra1, &dec1, (double)buf[0].header.Naxis[0], (double)buf[0].header.Naxis[1], &coords);
+  gprint (GP_ERR, "%f %f  %f %f\n", ra0, dec0, ra1, dec1);
+  
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  dDEC = fabs(dec1 - dec0);
+  tDEC = log10(dDEC) - log10(5.0);
+  fDEC = log10(dDEC) - tDEC;
+  if ((fDEC > log10(0.5)) && (fDEC < log10(1.0))) {
+    dDEC = TEN (tDEC + log10(0.1));
+  }
+  if ((fDEC > log10(1.0)) && (fDEC < log10(2.0))) {
+    dDEC = TEN (tDEC + log10(0.2));
+  }
+  if ((fDEC > log10(2.0)) && (fDEC < log10(5.0))) {
+    dDEC = TEN (tDEC + log10(0.5));
+  }
+  if ((fDEC > log10(5.0)) && (fDEC < log10(10.0))) {
+    dDEC = TEN (tDEC + log10(1.0));
+  }
+  if ((fDEC > log10(10.0)) && (fDEC < log10(20.0))) {
+    dDEC = TEN (tDEC + log10(2.0));
+  }
+  if ((fDEC > log10(20.0)) && (fDEC < log10(50.0))) {
+    dDEC = TEN (tDEC + log10(5.0));
+  }
+  ndig2 = ((log10(dDEC) < 0) ? fabs(log10(dDEC)) : 0);
+  ndig1 = 3 + log10(MAX(dec0, dec1)) + ndig2;
+  sprintf (format, "%%%d.%df", ndig1, ndig2);
+  gprint (GP_ERR, "format: %s..\n", format);
+
+  NX = buf[0].header.Naxis[0];
+  NY = buf[0].header.Naxis[1];
+
+  x0 = y0 = 0;
+  dRA = MAX (fabs(ra1 - ra0) / 100.0, 0.1);
+  connect = FALSE;
+  for (dec = dDEC * ((int)(MIN(dec0,dec1)/dDEC) + 1); dec < MAX(dec0,dec1); dec += dDEC) {
+    for (ra = 0; ra < 361; ra += dRA) {
+      RD_to_XY (&x1, &y1, ra, dec, &coords);
+      if ((x1 >= 0) && (x1 < NX) && (y1 >= 0) && (y1 < NY)) {
+	if (connect) {
+	  overlay[Noverlay].type = KII_OVERLAY_LINE;
+	  overlay[Noverlay].x = x0;
+	  overlay[Noverlay].y = y0;
+	  overlay[Noverlay].dx = x1 - x0;
+	  overlay[Noverlay].dy = y1 - y0;
+	  Noverlay ++;
+	  CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+	}
+	x0 = x1;
+	y0 = y1;
+	connect = TRUE;
+      } else {
+	connect = FALSE;
+      }
+    }
+  }
+
+  dRA = fabs(ra1 - ra0);
+  tRA = log10(dRA) - log10(5.0);
+  fRA = log10(dRA) - tRA;
+  if ((fRA > log10(0.5)) && (fRA < log10(1.0))) {
+    dRA = TEN (tRA + log10(0.1));
+  }
+  if ((fRA > log10(1.0)) && (fRA < log10(2.0))) {
+    dRA = TEN (tRA + log10(0.2));
+  }
+  if ((fRA > log10(2.0)) && (fRA < log10(5.0))) {
+    dRA = TEN (tRA + log10(0.5));
+  }
+  if ((fRA > log10(5.0)) && (fRA < log10(10.0))) {
+    dRA = TEN (tRA + log10(1.0));
+  }
+  if ((fRA > log10(10.0)) && (fRA < log10(20.0))) {
+    dRA = TEN (tRA + log10(2.0));
+  }
+  if ((fRA > log10(20.0)) && (fRA < log10(50.0))) {
+    dRA = TEN (tRA + log10(5.0));
+  }
+  ndig2 = ((log10(dRA) < 0) ? fabs(log10(dRA)) : 0);
+  ndig1 = 3 + log10(MAX(ra0, ra1)) + ndig2;
+  sprintf (format, "%%%d.%df", ndig1, ndig2);
+  gprint (GP_ERR, "format: %s..\n", format);
+
+  dDEC = MAX (fabs(dec1 - dec0) / 100.0, 0.1);
+  connect = FALSE;
+  for (ra = dRA * ((int)(MIN(ra0,ra1)/dRA) + 1); ra < MAX(ra0,ra1); ra += dRA) {
+    for (dec = -90; dec < 90; dec += dDEC) {
+      RD_to_XY (&x1, &y1, ra, dec, &coords);
+      if ((x1 >= 0) && (x1 < NX) && (y1 >= 0) && (y1 < NY)) {
+	if (connect) {
+	  overlay[Noverlay].type = KII_OVERLAY_LINE;
+	  overlay[Noverlay].x = x0;
+	  overlay[Noverlay].y = y0;
+	  overlay[Noverlay].dx = x1 - x0;
+	  overlay[Noverlay].dy = y1 - y0;
+	  Noverlay ++;
+	  CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+	}
+	x0 = x1;
+	y0 = y1;
+	connect = TRUE;
+      } else {
+	connect = FALSE;
+      }
+    }
+  }
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+  free (overlay);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/ungridify.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/ungridify.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/ungridify.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "data.h"
+
+int ungridify (int argc, char **argv) {
+
+  int i, j, n;
+  int Nx, Ny, NX, NY;
+  int Xmin, Xmax, Ymin, Ymax;
+  float *v, *x, *y, *z;
+  Buffer *bf;
+  Vector *vx, *vy, *vz;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: ungridify buffer Xmin Xmax Ymin Ymax x y z\n");
+    return (FALSE);
+  }
+  
+  if ((bf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Xmin = atof (argv[2]);
+  Xmax = atof (argv[3]);
+  Ymin = atof (argv[4]);
+  Ymax = atof (argv[5]);
+  Nx = Xmax - Xmin;
+  Ny = Ymax - Ymin;
+  
+  NX = bf[0].matrix.Naxis[0];
+  NY = bf[0].matrix.Naxis[1];
+
+  if ((vx = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vy = SelectVector (argv[7], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vz = SelectVector (argv[8], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  REALLOCATE (vx[0].elements, float, Nx*Ny);
+  REALLOCATE (vy[0].elements, float, Nx*Ny);
+  REALLOCATE (vz[0].elements, float, Nx*Ny);
+
+  x = vx[0].elements;
+  y = vy[0].elements;
+  z = vz[0].elements;
+  n = 0;
+  v = (float *)bf[0].matrix.buffer;
+  for (j = Ymin; j < Ymax; j++) {
+    for (i = Xmin; i < Xmax; i++, x++, y++, z++, n++) {
+      vx[0].elements[n] = i;
+      vy[0].elements[n] = j;
+      if (i < 0) continue;
+      if (i >= NX) continue;
+      if (j < 0) continue;
+      if (j >= NY) continue;
+      vz[0].elements[n] = v[i+j*NX];
+    }
+  }
+  if (n != Nx*Ny) {
+    gprint (GP_ERR, "error in ungrid: %d vs %d (%d x %d)\n", n, Nx*Ny, Nx, Ny);
+  }
+  vx[0].Nelements = vy[0].Nelements = vz[0].Nelements = n;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/uniq.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/uniq.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/uniq.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "data.h"
+
+int uniq (int argc, char **argv) {
+  
+  int Nnew, i, j, found;
+  float *v1, *v2;
+  Vector *ivec, *ovec;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: uniq (in) (out)\n");
+    return (FALSE);
+  }
+
+  if ((ivec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* allocate the maximum possible needed */
+  ALLOCATE (ovec[0].elements, float, ivec[0].Nelements);
+
+  Nnew = 0;
+  v1 = ivec[0].elements;
+  for (i = 0; i < ivec[0].Nelements; i++, v1++) {
+    v2 = ovec[0].elements;
+    found = FALSE;
+    for (j = 0; !found && (j < Nnew); j++, v2++) {
+      if (*v1 == *v2) found = TRUE;
+    }
+    if (!found) {
+      ovec[0].elements[Nnew] = *v1;
+      Nnew ++;
+    }
+  }
+
+  ovec[0].Nelements = Nnew;
+  REALLOCATE (ovec[0].elements, float, ovec[0].Nelements);
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/unsign.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/unsign.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/unsign.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "data.h"
+
+int unsign (int argc, char **argv) {
+  
+  if (argc == 1) {
+    if (gfits_get_unsign_mode()) 
+      gprint (GP_ERR, "mode is now UNSIGNED int \n");
+    else
+      gprint (GP_ERR, "mode is now SIGNED int \n");
+    return (TRUE);
+  }
+
+  if (argc == 2) {
+    if (!strcmp (argv[1], "1")) {
+      gfits_set_unsign_mode (TRUE);
+      set_int_variable ("UNSIGN", 1);
+      return (TRUE);
+    }
+    if (!strcmp (argv[1], "0")) {
+      gfits_set_unsign_mode (FALSE);
+      set_int_variable ("UNSIGN", 0);
+      return (TRUE);
+    }
+  }
+
+  gprint (GP_ERR, "USAGE: unsign [0/1]\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vbin.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vbin.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vbin.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "data.h"
+
+int vbin (int argc, char **argv) {
+  
+  int i, j, n, N, Nin, Nout;
+  int Normalize, Ignore;
+  float *Vout, *Vin, IgnoreValue;
+  double scale;
+  Vector *in, *out;
+
+  Normalize = FALSE;
+  if ((N = get_argument (argc, argv, "-norm"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = TRUE;
+  }
+
+  Ignore = FALSE;
+  IgnoreValue = 0.0;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: vbin <input> <output> scale \n");
+    gprint (GP_ERR, "  (use interpolate to expand)\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  scale  = atof (argv[3]);
+  if ((int)(scale) != scale) {
+    gprint (GP_ERR, "integer binning only, please\n");
+    return (FALSE);
+  }
+
+  Nin  = in[0].Nelements;
+  Nout = Nin / scale;
+
+  REALLOCATE (out[0].elements, float, Nout);
+  out[0].Nelements = Nout;
+
+  Vin  = in[0].elements;
+  Vout = out[0].elements;
+  for (n = j = 0; j < Nout; j++, Vout++) {
+    *Vout = 0;
+    for (N = i = 0; (i < scale) && (n < Nin); n++, i++, Vin++) {
+      if (!finite (*Vin)) continue;
+      if (Ignore && (*Vin == IgnoreValue)) continue;
+      *Vout += *Vin;
+      N ++;
+    } 
+    if (Normalize) {
+      if (N > 0) { 
+	*Vout /= (float) N; 
+      } else {
+	*Vout = 0;
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vclip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vclip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vclip.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "data.h"
+
+int vclip (int argc, char **argv) {
+
+  int i, Npix, DO_NAN, DO_INF, N;
+  double min, Vmin, max, Vmax, nan_val, inf_val;
+  float *in;
+  Vector *vec;
+
+  inf_val = nan_val = min = Vmin = max = Vmax = 0;
+
+  DO_NAN = FALSE;
+  if ((N = get_argument (argc, argv, "-nan"))) {
+    remove_argument (N, &argc, argv);
+    nan_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_NAN = TRUE;
+  }
+
+  DO_INF = FALSE;
+  if ((N = get_argument (argc, argv, "-inf"))) {
+    remove_argument (N, &argc, argv);
+    inf_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_INF = TRUE;
+  }
+
+  if ((argc != 6) && (!(DO_INF || DO_NAN))) {
+    gprint (GP_ERR, "USAGE: vclip (vector) [min Vmin max Vmax] [-inf val] [-nan val]\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    min = atof (argv[2]);
+    Vmin = atof (argv[3]);
+    max = atof (argv[4]);
+    Vmax = atof (argv[5]);
+  }
+
+  Npix = vec[0].Nelements;
+  in =   vec[0].elements;
+
+  if (argc == 6) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (*in < min) 
+	*in = Vmin;
+      if (*in > max)
+	*in = Vmax;
+    }
+  }
+  in = vec[0].elements;
+  if (DO_NAN) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (isnan (*in)) {
+	*in = nan_val;
+      }
+    }
+  }
+  in = vec[0].elements;
+  if (DO_INF) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (!finite (*in)) {
+	*in = inf_val;
+      }
+    }
+  }
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vcontour.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vcontour.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vcontour.c	(revision 22322)
@@ -0,0 +1,278 @@
+# include "data.h"
+# define LL { \
+ dx =  d00 / (*v01 - *v00); \
+ dy = -d00 / (*v10 - *v00); \
+ x = i - 0.5; \
+ y = j - dy - 0.5; }
+
+# define UL { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = 1 - tmp; \
+ dx =  d10 / (*v11 - *v10); \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+      
+# define LR { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = 1 - tmp; \
+ dy = d01 / (*v11 - *v01); \
+ x = i + tmp - 0.5; \
+ y = j - 0.5; }
+
+# define UR { \
+ tmp = d10 / (*v11 - *v10); \
+ dx = 1 - tmp; \
+ dy = -d11 / (*v01 - *v11); \
+ x = i + tmp - 0.5; \
+ y = j + 1 - 0.5; }
+      
+# define HZ { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = d01 / (*v11 - *v01) - tmp; \
+ dx = 1; \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+
+# define VT { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = d10 / (*v11 - *v10) - tmp; \
+ x = i + tmp - 0.5; \
+ dy = 1; \
+ y = j - 0.5; }
+
+# define DUMP { \
+overlay[Noverlay].type = KII_OVERLAY_LINE; \
+overlay[Noverlay].x = Npix*x; \
+overlay[Noverlay].y = Npix*y; \
+overlay[Noverlay].dx = Npix*dx; \
+overlay[Noverlay].dy = Npix*dy; \
+Noverlay ++; \
+CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000); \
+}
+
+int vcontour (int argc, char **argv) {
+
+  int i, j, ii, jj, n, Nbuf, Npix, Nx, Ny, Nline;
+  float level, d00, d01, d10, d11, tmp;
+  float x, y, dx, dy;
+  float *v00, *v01, *v10, *v11;
+  float *Vout, *Vin, *matrix;
+  char *buffer, line[17];
+  int Ximage, Nimage, N;
+  Vector *xvec, *yvec, *zvec;
+
+  gprint (GP_ERR, "vcontour not working yet\n");
+  return (FALSE);
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: vcontour x y z Xc Yc (level)\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((foo = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((foo = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  level = atof (argv[6]);
+
+  /* convert the x,y,z vectors to a matrix */
+  *V = xvec[0].elements;
+  max = min = *V;
+  for (i = 0; i < xvec[0].Nelements; i++, V++) {
+    if (!finite (*V)) continue;
+    max = MAX (*V, max);
+    min = MIN (*V, min);
+  }      
+  Xmax = max; Xmin = min;
+
+  *V = yvec[0].elements;
+  max = min = *V;
+  for (i = 0; i < yvec[0].Nelements; i++, V++) {
+    if (!finite (*V)) continue;
+    max = MAX (*V, max);
+    min = MIN (*V, min);
+  }      
+  Ymax = max; Ymin = min;
+
+  /* not really finished */
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  v01 = matrix + 1;
+  v10 = matrix + Nx;
+  v11 = matrix + Nx + 1;
+  d01 = (level - *v00);
+  d11 = (level - *v10);
+  for (j = 1; j < Ny; j++) {
+    if (!(j%10)) gprint (GP_ERR, ".");
+    for (i = 1; i < Nx; i++, v00++, v01++, v10++, v11++) {
+
+      d00 = d01;
+      d10 = d11;
+      d01 = (level - *v01);
+      d11 = (level - *v11);
+
+      if (((d00 > 0) && (d01 > 0) && (d10 > 0) && (d11 > 0)) ||
+	  ((d00 < 0) && (d01 < 0) && (d10 < 0) && (d11 < 0)))
+	continue;
+
+      if ((d00 > 0) && (d10 <= 0)) { 
+	if ((d01 <= 0) && (d11 <= 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* \\  */
+	  LL;
+	  DUMP;
+	  UR;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 <= 0) && (d10 > 0)) {
+	if ((d01 > 0) && (d11 > 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* //  */
+	  UL;
+	  DUMP;
+	  LR;
+	  DUMP;
+	  continue;
+	}
+      }
+      
+
+      if ((d00 <= 0) && (d10 <= 0)) { 
+	if ((d01 > 0) && (d11 <= 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 > 0) && (d10 > 0)) { 
+	if ((d01 <= 0) && (d11 > 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+    }
+    /* skip left-hand edge */
+    v00++; v01++; v10++; v11++;
+    d01 = (level - *v00);
+    d11 = (level - *v10);
+  }
+
+  /****** bottom line *******/
+  v00 = matrix;  
+  v01 = matrix + 1;  
+  y = 0;
+  dx = 0;
+  dy = -0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+
+  /********** top line *******/
+  v00 = matrix + Nx*(Ny - 1);  
+  v01 = v00 + 1;
+  y = Ny - 1;
+  dx = 0;
+  dy = 0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+
+  /******** left line *********/
+  v00 = matrix; 
+  v01 = matrix + Nx;
+  x = 0;
+  dx = -0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+
+  /******** right line *********/
+  v00 = matrix + Nx - 1; 
+  v01 = v00 + Nx;
+  x = Nx - 1;
+  dx = 0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+  
+  KiiLoadOverlay (Ximage, overlay, Noverlay, argv[2]);
+  free (overlay);
+
+  if (Npix != 1) free (matrix);
+  gprint (GP_ERR, "\n");
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vgauss.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vgauss.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vgauss.c	(revision 22322)
@@ -0,0 +1,106 @@
+# include "data.h"
+
+/* local private functions */
+float fgaussOD (float, float *, int, float *);
+
+# define GET_VAR(V,A) \
+  c = get_variable (A); \
+  if (c == NULL) { \
+    gprint (GP_ERR, "missing fit parameter A\n"); \
+    return (FALSE); \
+  } \
+  V = atof (c); \
+  free (c);
+
+int vgauss (int argc, char **argv) {
+
+  int i, N, Npts, Npar, Quiet;
+  float par[4], *v1, *v2, *dy, **covar;
+  float chisq, ochisq, dchisq;
+  Vector *xvec, *yvec, *svec, *ovec;
+  char *c, name[16];
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: vgauss <x> <y> <dy> (out)\n");
+    gprint (GP_ERR, " uses guesses: C0 (mean), C1 (sigma), C2 (norm), C3 (sky)\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((svec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Npts = xvec[0].Nelements;
+  ALLOCATE (dy, float, Npts);
+  REALLOCATE (ovec[0].elements, float, Npts);
+
+  GET_VAR (par[0], "C0");
+  GET_VAR (par[1], "C1");
+  GET_VAR (par[2], "C2");
+  GET_VAR (par[3], "C3");
+  Npar = 4;
+
+  v1 = svec[0].elements;
+  v2 = dy;
+  for (i = 0; i < Npts; i++, v1++, v2++) {
+      *v2 = (*v1 == 0.0) ? 0.0 : 1.0 / (*v1 * *v1);
+  } 
+  
+  ochisq = mrqinit (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fgaussOD, !Quiet);
+  dchisq = ochisq + 2*Npts;
+
+  for (i = 0; (i < 20) && ((dchisq > 0.1*(Npts - Npar)) || (dchisq <= 0.0)); i++) {
+    chisq = mrqmin (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fgaussOD, !Quiet);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+    if (!Quiet) gprint (GP_ERR, "dchisq: %f, Ndof: %d\n", dchisq, Npts - Npar);
+  }  
+  if (!Quiet) gprint (GP_ERR, "%d iterations\n", i); 
+
+  for (i = 0; i < Npts; i++) {
+    ovec[0].elements[i] = fgaussOD (xvec[0].elements[i], par, Npar, dy);
+  }
+  ovec[0].Nelements = Npts;
+  /* set output *before* variable renomalization */
+
+  covar = mrqcovar (Npar);
+  for (i = 0; i < Npar; i++) {
+    sprintf (name, "C%d", i);
+    set_variable (name, par[i]);
+    if (!Quiet) gprint (GP_ERR, "%d  %f  %f\n", i, par[i], sqrt(covar[i][i]));
+    sprintf (name, "dC%d", i);
+    set_variable (name, sqrt(covar[i][i]));
+  }
+
+  free (dy);
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+/* pars: x_o, sigma, I, back */
+float fgaussOD (float x, float *par, int Npar, float *dpar) {
+
+  float z, r, f;
+
+  z = (x - par[0])/par[1];
+  r = exp (-0.5*SQ(z));
+  f = par[2]*r + par[3];
+
+  dpar[0] = par[2]*r*z/par[1];
+  dpar[1] = par[2]*r*z*z/par[1];
+  dpar[2] = r;
+  dpar[3] = 1;
+  
+  return (f);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vgrid.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vgrid.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vgrid.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "data.h"
+
+int vgrid (int argc, char **argv) {
+
+  int i, Nx, Ny, Xb, Yb;
+  float Xmin, Xmax, dX, Ymin, Ymax, dY;
+  float *buf, *val, *x, *y, *z;
+  int *Nval;
+  Buffer *bf;
+  Vector *vx, *vy, *vz;
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: vgrid x y z buffer Xmin Xmax dX Ymin Ymax dY\n");
+    return (FALSE);
+  }
+  
+  if ((vx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vz = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((bf = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (vx[0].Nelements != vy[0].Nelements) return (FALSE);
+  if (vx[0].Nelements != vz[0].Nelements) return (FALSE);
+
+  Xmin = atof (argv[5]);
+  Xmax = atof (argv[6]);
+  dX   = atof (argv[7]);
+
+  Ymin = atof (argv[8]);
+  Ymax = atof (argv[9]);
+  dY   = atof (argv[10]);
+
+  Nx = (Xmax - Xmin) / dX + 1;
+  Ny = (Ymax - Ymin) / dY + 1;
+  
+  gfits_free_matrix (&bf[0].matrix);
+  gfits_free_header (&bf[0].header);
+  CreateBuffer (bf, Nx, Ny, -32, 0.0, 1.0);
+  strcpy (bf[0].file, "(empty)");
+
+  ALLOCATE (val, float, Nx*Ny);
+  bzero (val, Nx*Ny*sizeof(float));
+  ALLOCATE (Nval, int, Nx*Ny);
+  bzero (Nval, Nx*Ny*sizeof(int));
+
+  x = vx[0].elements;
+  y = vy[0].elements;
+  z = vz[0].elements;
+  for (i = 0; i < vx[0].Nelements; i++, x++, y++, z++) {
+    Xb = (*x - Xmin) / dX;
+    Yb = (*y - Ymin) / dY;
+    if (Xb >= Nx) continue;
+    if (Yb >= Ny) continue;
+    val[Xb + Yb*Nx] = *z;
+    Nval[Xb + Yb*Nx]++;
+  }
+
+  buf = (float *) bf[0].matrix.buffer;
+  for (i = 0; i < Nx*Ny; i++) {
+    if (Nval[i] == 0) {
+      buf[i] = 0;
+      continue;
+    }
+    buf[i] = val[i] / Nval[i];
+  }
+
+  free (val);
+  free (Nval);
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vload.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vload.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vload.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "data.h"
+
+int vload (int argc, char **argv) {
+  
+  int i, N, Noverlay;
+  int kapa, type;
+  char *name;
+  double dx, dy, size;
+  KiiOverlay *overlay;
+  Vector *vecx, *vecy;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  type = KII_OVERLAY_BOX;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    type = KiiOverlayTypeByName (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (!type) {
+      gprint (GP_ERR, "unknown Kii point type %s\n", argv[N]);
+      return (FALSE);
+    }
+  }
+
+  size = 1.0;
+  if ((N = get_argument (argc, argv, "-size"))) {
+    remove_argument (N, &argc, argv);
+    size = fabs(atof (argv[N]));
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: vload (overlay) (xvec) (yvec) [-n]\n");
+    return (FALSE);
+  }
+  
+  if (type == KII_OVERLAY_CIRCLE) {
+    dx = dy = size / 2;
+  } else {
+    dx = dy = size;
+  }    
+
+  if ((vecx = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (vecx[0].Nelements != vecy[0].Nelements) {
+    gprint (GP_ERR, "mismatched vector lengths\n");
+    return (FALSE);
+  }
+
+  Noverlay = vecx[0].Nelements;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+
+  for (i = 0; i < Noverlay; i++) {
+    overlay[i].type = type;
+    overlay[i].text = NULL;
+    overlay[i].x = vecx[0].elements[i]+0.5;
+    overlay[i].y = vecy[0].elements[i]+0.5;
+    overlay[i].dx = dx;
+    overlay[i].dy = dy;
+    overlay[i].angle = 0.0;
+  }
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+  free (overlay);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vmaxwell.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vmaxwell.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vmaxwell.c	(revision 22322)
@@ -0,0 +1,108 @@
+# include "data.h"
+
+/* local private functions */
+float fmaxwellOD (float, float *, int, float *);
+
+# define GET_VAR(V,A) \
+  c = get_variable (A); \
+  if (c == NULL) { \
+    gprint (GP_ERR, "missing fit parameter A\n"); \
+    return (FALSE); \
+  } \
+  V = atof (c); \
+  free (c);
+
+int vmaxwell (int argc, char **argv) {
+
+  int i, N, Npts, Npar, Quiet;
+  float par[5], *v1, *v2, *dy, **covar;
+  float chisq, ochisq, dchisq;
+  Vector *xvec, *yvec, *svec, *ovec;
+  char *c, name[16];
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: vmaxwell <x> <y> <dy> (out)\n");
+    gprint (GP_ERR, " uses guesses: C0 (mean), C1 (sigma), C2 (norm), C3 (sky), C4 (ref)\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((svec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Npts = xvec[0].Nelements;
+  ALLOCATE (dy, float, Npts);
+  REALLOCATE (ovec[0].elements, float, Npts);
+
+  GET_VAR (par[0], "C0");
+  GET_VAR (par[1], "C1");
+  GET_VAR (par[2], "C2");
+  GET_VAR (par[3], "C3");
+  GET_VAR (par[4], "C4");
+  Npar = 5;
+  /* careful of variable renomalization */
+
+  v1 = svec[0].elements;
+  v2 = dy;
+  for (i = 0; i < Npts; i++, v1++, v2++) *v2 = 1.0 / (*v1 * *v1);
+  
+  ochisq = mrqinit (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fmaxwellOD, !Quiet);
+  dchisq = ochisq + 2*Npts;
+
+  for (i = 0; (i < 20) && ((dchisq > 0.1*(Npts - Npar)) || (dchisq <= 0.0)); i++) {
+    chisq = mrqmin (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fmaxwellOD, !Quiet);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+    if (!Quiet) gprint (GP_ERR, "dchisq: %f, Ndof: %d\n", dchisq, Npts - Npar);
+  }  
+  if (!Quiet) gprint (GP_ERR, "%d iterations\n", i); 
+
+  for (i = 0; i < Npts; i++) {
+    ovec[0].elements[i] = fmaxwellOD (xvec[0].elements[i], par, Npar, dy);
+  }
+  ovec[0].Nelements = Npts;
+  /* set output *before* variable renomalization */
+
+  covar = mrqcovar (Npar);
+  for (i = 0; i < Npar; i++) {
+    sprintf (name, "C%d", i);
+    set_variable (name, par[i]);
+    if (!Quiet) gprint (GP_ERR, "%d  %f  %f\n", i, par[i], sqrt(covar[i][i]));
+    sprintf (name, "dC%d", i);
+    set_variable (name, sqrt(covar[i][i]));
+  }
+
+  free (dy);
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+/* pars: x_o, -0.5/sigma^2, I, back, ref */
+float fmaxwellOD (float x, float *par, int Npar, float *dpar) {
+
+  float z, r, f;
+
+  z = (x - par[0])/par[1];
+  r = SQ(x - par[4])*exp (-0.5*SQ(z));
+  f = par[2]*r + par[3];
+
+  dpar[0] = par[2]*r*z/par[1];
+  dpar[1] = par[2]*r*z*z/par[1];
+  dpar[2] = r;
+  dpar[3] = 1;
+  dpar[4] = -par[2]*(x - par[4])*exp(-0.5*SQ(z));
+  
+  return (f);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vpop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vpop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vpop.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "data.h"
+
+int vpop (int argc, char **argv) {
+
+  int Npix;
+  Vector *vec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vpop (vector)\n");
+    gprint (GP_ERR, "  remove first element of vector\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  Npix = vec[0].Nelements;
+  if (Npix < 1) return (TRUE);
+
+  if (Npix > 1) {
+    memmove (&vec[0].elements[0], &vec[0].elements[1], Npix*sizeof(float));
+  }
+  vec[0].Nelements = Npix - 1;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vroll.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vroll.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vroll.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "data.h"
+
+int vroll (int argc, char **argv) {
+
+  int Npix;
+  float first;
+  Vector *vec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vroll (vector)\n");
+    gprint (GP_ERR, "  roll vector elements (first goes to end)\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  Npix = vec[0].Nelements;
+  if (Npix < 2) return (TRUE);
+
+  first = vec[0].elements[0];
+  memmove (&vec[0].elements[0], &vec[0].elements[1], Npix*sizeof(float));
+  vec[0].elements[Npix-1] = first;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vsmooth.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vsmooth.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vsmooth.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "data.h"
+
+int vsmooth (int argc, char **argv) {
+  
+  int i, n, N, Nx, Ns, Ngauss;
+  float *vi, *vo, *gauss, *gaussnorm;
+  float g, s, sigma, Nsigma;
+  Vector *in;
+
+  Nsigma = 3;
+  if ((N = get_argument (argc, argv, "-Nsigma"))) {
+    remove_argument (N, &argc, argv);
+    Nsigma = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: vsmooth (input) sigma\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  sigma = atof (argv[2]);
+  Nx = in[0].Nelements;
+  vi = in[0].elements;
+
+  /* build a 1D gaussian */
+  Ns = (int) (Nsigma*sigma + 0.5);
+  Ngauss = 2*Ns + 1;
+  ALLOCATE (gaussnorm, float, Ngauss);
+  gauss = &gaussnorm[Ns];
+  for (i = -Ns; i < Ns + 1; i++) {
+    gauss[i] = exp ((i*i)/(-2*sigma*sigma));
+  }
+
+  ALLOCATE (vo, float, Nx);
+
+  for (i = 0; i < Nx; i++) {
+    g = s = 0;
+    for (n = -Ns; n < Ns + 1; n++) {
+      if (i+n < 0) continue;
+      if (i+n >= Nx) continue;
+      s += gauss[n]*vi[i+n];
+      g += gauss[n];
+    }
+    vo[i] = s / g;
+  }
+
+  free (in[0].elements);
+  free (gaussnorm);
+
+  in[0].elements = vo;
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vstat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vstat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vstat.c	(revision 22322)
@@ -0,0 +1,115 @@
+# include "data.h"
+
+int vstat (int argc, char **argv) {
+  
+  int i, N;
+  double max, min, sum, var, dvar, mean, stdev;
+  float *X, IgnoreValue;
+  int Ignore, Quiet;
+
+  int *Nval, bin, Nmode, Nmed;
+  double dx, mode, median;
+  Vector *vec;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vstat (vector)\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* we need two passes, one for max, min, mean, sum, one for median, stdev, etc */
+
+  /* calculate max, min, mean, sum, npix */
+  X = vec[0].elements;
+  max = -HUGE_VAL;
+  min = HUGE_VAL;
+  sum = N = 0;
+  for (i = 0; i < vec[0].Nelements; i++, X++) {
+    if (!finite (*X)) continue;
+    if (Ignore && (*X == IgnoreValue)) continue;
+    max = MAX (*X, max);
+    min = MIN (*X, min);
+    sum += *X;
+    N++;
+  }      
+  mean = sum / N;
+
+  /* calculate median and mode with resolution of (max - min) / 1000 */ 
+  dx = (max - min) / 1000;
+  if (dx == 0) {
+    median = mode = min;
+    stdev = 0.0;
+    goto skip;
+  }
+
+  ALLOCATE (Nval, int, 1002);
+  bzero (Nval, 1000*sizeof(int));
+  X = vec[0].elements;
+  var = 0;
+  for (i = 0; i < vec[0].Nelements; i++, X++) {
+    if (!finite (*X)) continue;
+    if (Ignore && (*X == IgnoreValue)) continue;
+    bin = MAX (0, MIN (1000, (*X - min) / dx));
+    Nval[bin] ++;
+    dvar = (*X - mean);
+    var += dvar*dvar;
+  }      
+  stdev = sqrt (var / N);
+
+  Nmode = 0;
+  mode = Nval[Nmode];
+  median = 0;
+  Nmed = -1;
+  for (i = 0; i < 1001; i++) {
+    if (Nmed == -1) {
+      median += Nval[i];
+      if (median >= N / 2.0) {
+	Nmed = i;
+	median = i * dx + min;
+      }
+    }
+    if (mode < Nval[i]) {
+      Nmode = i;
+      mode = Nval[Nmode];
+    }
+  }
+  mode = Nmode * dx + min;
+  free (Nval);
+
+skip:
+  if (!Quiet) {
+    gprint (GP_ERR, "mean: %g, stdev: %g, min: %g, max: %g, median: %g, mode: %g, Npts: %d\n", 
+	     mean, stdev, min, max, median, mode, N);
+  }
+
+  set_variable ("MIN",      min);
+  set_variable ("MAX",      max);
+  set_variable ("MEDIAN",   median);
+  set_variable ("MEAN",     mean);
+  set_variable ("MODE",     mode);
+  set_variable ("TOTAL",    sum);
+  set_int_variable ("NPIX", N);
+  set_variable ("SIGMA",    stdev);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vzload.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vzload.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/vzload.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "data.h"
+
+int vzload (int argc, char **argv) {
+  
+  int i, N, Noverlay;
+  int kapa, type;
+  char *name;
+  double size, min, range, MAX_OUTPUT_SIZE;
+  KiiOverlay *overlay;
+  Vector *vecx, *vecy, *vecz;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  MAX_OUTPUT_SIZE = 10.0;
+  if ((N = get_argument (argc, argv, "-max"))) {
+    remove_argument (N, &argc, argv);
+    MAX_OUTPUT_SIZE = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  type = KII_OVERLAY_BOX;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    type = KiiOverlayTypeByName (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (!type) {
+      gprint (GP_ERR, "unknown Kii point type %s\n", argv[N]);
+      return (FALSE);
+    }
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: vzload (overlay) (xvec) (yvec) (zvec) (min) (max) [-n] [-type]\n");
+    return (FALSE);
+  }
+  
+  if ((vecx = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecz = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (vecx[0].Nelements != vecy[0].Nelements) {
+    gprint (GP_ERR, "mismatched vector lengths\n");
+    return (FALSE);
+  }
+
+  min = atof(argv[5]);
+  range = (atof(argv[6]) - min) / MAX_OUTPUT_SIZE;
+  // renormalize to the max output size (output range is 0.1 - MAX_OUTPUT_SIZE)
+
+  Noverlay = vecx[0].Nelements;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+
+  for (i = N = 0; i < Noverlay; i++) {
+    size = MIN (MAX_OUTPUT_SIZE, (vecz[0].elements[i] - min) / range);
+    if (size < 0.1) continue;
+
+    overlay[N].type = type;
+    overlay[N].text = NULL;
+    overlay[N].x = vecx[0].elements[i]+0.5;
+    overlay[N].y = vecy[0].elements[i]+0.5;
+    overlay[N].angle = 0.0;
+
+    if (type == KII_OVERLAY_CIRCLE) {
+      overlay[N].dx = size / 2.0;
+      overlay[N].dy = size / 2.0;
+    } else {
+      overlay[N].dx = size;
+      overlay[N].dy = size;
+    }    
+    N++;
+  }
+
+  KiiLoadOverlay (kapa, overlay, N, argv[1]);
+  free (overlay);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/wd.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/wd.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/wd.c	(revision 22322)
@@ -0,0 +1,186 @@
+# include "data.h"
+
+int wd (int argc, char **argv) {
+  
+  int N, Extend;
+  int newUnsign, newBitpix, newScale, newZero;
+  int outUnsign, outBitpix;
+  double outScale, outZero;
+  Header temp_header;
+  Matrix temp_matrix;
+  Buffer *buf;
+
+  /* XXX I must have dropped the old 'newplane' option */
+  Extend  = FALSE;
+  if ((N = get_argument (argc, argv, "-extend"))) {
+    remove_argument (N, &argc, argv);
+    Extend  = TRUE;
+  }
+
+  outZero = 0;
+  newZero = FALSE;
+  if ((N = get_argument (argc, argv, "-bzero"))) {
+    remove_argument (N, &argc, argv);
+    outZero  = atof(argv[N]);
+    newZero  = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  outScale = 1;
+  newScale = FALSE;
+  if ((N = get_argument (argc, argv, "-bscale"))) {
+    remove_argument (N, &argc, argv);
+    outScale = atof(argv[N]);
+    newScale = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  outBitpix = 16;
+  newBitpix = FALSE;
+  if ((N = get_argument (argc, argv, "-bitpix"))) {
+    remove_argument (N, &argc, argv);
+    outBitpix = atof(argv[N]);
+    newBitpix = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  outUnsign = FALSE;
+  newUnsign = FALSE;
+  if ((N = get_argument (argc, argv, "-unsign"))) {
+    remove_argument (N, &argc, argv);
+    outUnsign = -1;
+    if (!strcasecmp (argv[N], "t") || !strcasecmp (argv[N], "true")) outUnsign = TRUE;
+    if (!strcasecmp (argv[N], "f") || !strcasecmp (argv[N], "false")) outUnsign = FALSE;
+    if (outUnsign == -1) {
+      gprint (GP_ERR, "-unsign options: t, f, true, false\n");
+      return (FALSE);
+    }
+    newUnsign = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: wd <buffer> <filename> [-bitpix N] [-bscale X] [-bzero X] [-extend] [-newplane]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (!newBitpix) outBitpix = buf[0].bitpix;
+  if (!newScale) outScale = buf[0].bscale;
+  if (!newZero) outZero = buf[0].bzero;
+  if (!newUnsign) outUnsign = buf[0].unsign;
+
+  /* Convert the buffer from (float) to correct format */
+  /* save the (float) version, write out a temporary buffer */
+  temp_matrix = buf[0].matrix;
+  ALLOCATE (temp_matrix.buffer, char, MAX(1, temp_matrix.size));
+  memcpy (temp_matrix.buffer, buf[0].matrix.buffer, temp_matrix.size);
+  temp_header = buf[0].header;
+  ALLOCATE (temp_header.buffer, char, MAX(1, temp_header.size));
+  memcpy (temp_header.buffer, buf[0].header.buffer, temp_header.size);
+
+  if (temp_header.Naxes) {
+    gfits_convert_format (&temp_header, &temp_matrix, outBitpix, outScale, outZero, outUnsign);
+  } else {
+    gfits_modify (&temp_header, "BITPIX", "%d", 1, outBitpix);
+    gfits_modify (&temp_header, "BSCALE", "%lf", 1, outScale);
+    gfits_modify (&temp_header, "BZERO",  "%lf", 1, outZero);
+    gfits_modify (&temp_header, "UNSIGN", "%t", 1, outUnsign);
+  }
+
+  if (Extend) {
+    Header Xhead;
+    FILE *f;
+    int status, Nextend;
+
+    /* assume failure means non-existent file */
+    if (!gfits_read_header (argv[2], &Xhead)) {
+
+      gfits_init_header (&Xhead);
+      Xhead.bitpix = 16;
+      Xhead.extend = TRUE;
+      gfits_create_header (&Xhead);
+
+      gfits_modify (&Xhead, "NEXTEND", "%d", 1, 0);
+      f = fopen (argv[2], "w");
+      fclose (f);
+    }
+
+    gfits_modify (&Xhead, "EXTEND", "%t", 1, TRUE);
+
+    Nextend = 0;
+    gfits_scan (&Xhead, "NEXTEND", "%d", 1, &Nextend);
+    Nextend ++;
+    gfits_modify (&Xhead, "NEXTEND", "%d", 1, Nextend);
+
+    /* write the main header to the start of the file */
+    f = fopen (argv[2], "r+");
+    if (f == NULL) {
+      gprint (GP_ERR, "failed to write file\n");
+      status = FALSE;
+      goto done1;
+    }
+    
+    /* position to begining of file to write header */
+    fseek (f, 0, SEEK_SET);
+    status = fwrite (Xhead.buffer, 1, Xhead.size, f);
+    if (status != Xhead.size) {
+      gprint (GP_ERR, "ERROR: failed writing data to image header\n");
+      status = FALSE;
+      goto done1;
+    }
+    
+    /* fix up header */
+    {
+      static char simple[] = "XTENSION= 'IMAGE  '            / Image extension";
+      int Ns, No;
+      Ns = strlen (simple);
+      No = 80 - Ns;
+      strncpy (temp_header.buffer, simple, Ns);
+      memset (&temp_header.buffer[Ns], ' ', No);
+    }
+
+    /* position to end of file to write new extend */
+    fseek (f, 0, SEEK_END);
+    status = fwrite (temp_header.buffer, 1, temp_header.size, f);
+    fclose (f);
+    if (status != temp_header.size) {
+      gprint (GP_ERR, "failed to write file\n");
+      status = FALSE;
+      goto done1;
+    }
+    /* write the matrix buffer (automatically goes to end of file */
+    if (!gfits_write_matrix (argv[2], &temp_matrix)) {
+      gprint (GP_ERR, "failed to write file\n");
+      status = FALSE;
+      goto done1;
+    }
+    status = TRUE;
+  done1:
+    gfits_free_header (&Xhead);
+    gfits_free_header (&temp_header);
+    gfits_free_matrix (&temp_matrix);
+    return (status);
+  }
+  
+  /* the actual write-to-disk goes here */
+  if (!gfits_write_header (argv[2], &temp_header)) {
+    gprint (GP_ERR, "failed to write header\n");
+    gfits_free_header (&temp_header);
+    gfits_free_matrix (&temp_matrix);
+    return (FALSE);
+  }
+  
+  if (!gfits_write_matrix (argv[2], &temp_matrix)) {
+    gprint (GP_ERR, "failed to write matrix\n");
+    gfits_free_header (&temp_header);
+    gfits_free_matrix (&temp_matrix);
+    return (FALSE);
+  }
+
+  gfits_free_header (&temp_header);
+  gfits_free_matrix (&temp_matrix);
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/write_vectors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 22322)
@@ -0,0 +1,151 @@
+# include "data.h"
+
+int write_vectors (int argc, char **argv) {
+  
+  int append;
+  int i, j, Nvec, Ne, N;
+  FILE *f;
+  char **fmtlist, *fmttype;
+  char *p0, *p1, *p2, *format;
+  Vector **vec;
+
+  /* look for format option */
+  format = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-f"))) {
+    remove_argument (N, &argc, argv);
+    format = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  append = FALSE;
+  if ((N = get_argument (argc, argv, "-append"))) {
+    remove_argument (N, &argc, argv);
+    append = TRUE;
+  }
+
+  if (argc < 3) {
+    gprint (GP_ERR, "USAGE: write [-append] [-f \"format\"] file vector vector ...\n");
+    return (FALSE);
+  }
+
+  /* open file for outuput */
+  if (append) {
+      f = fopen (argv[1], "a");
+  } else {
+      f = fopen (argv[1], "w");
+  }
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for write\n");
+    return (FALSE);
+  }
+
+  /* find number of output vectors */
+  Nvec = (argc - 2);
+  if (Nvec < 1) {
+      gprint (GP_ERR, "USAGE: write (file) vector vector ...\n");
+      fclose (f);
+      return (FALSE);
+  }
+  ALLOCATE (vec, Vector *, Nvec);
+
+  /* select/check vectors from list */
+  for (i = 0; i < Nvec; i++) {
+    if ((vec[i] = SelectVector (argv[i + 2], OLDVECTOR, FALSE)) == NULL) {
+      gprint (GP_ERR, "unknown vector %s\n", argv[i+2]);
+      gprint (GP_ERR, "USAGE: write (file) vector vector ...\n");
+      free (vec);
+      fclose (f);
+      return (FALSE);    
+    }
+  }
+  
+  /* select vector lengths */
+  Ne = vec[0][0].Nelements;
+  for (i = 0; i < Nvec; i++) {
+    if (vec[0][0].Nelements != Ne) {
+      gprint (GP_ERR, "error: vectors must all be the same size\n");
+      free (vec);
+      fclose (f);
+      return (FALSE);    
+    }
+  }
+
+  /* default output format */
+  if (format == (char *) NULL) {
+    for (i = 0; i < vec[0][0].Nelements; i++) {
+      for (j = 0; j < Nvec; j++) {
+	fprintf (f, "%.10g ", vec[j][0].elements[i]);
+      }
+      fprintf (f, "\n");
+    } 
+    fclose (f);
+    free (vec);
+    return (TRUE);
+  }
+
+  /* construct an array of format strings */
+  ALLOCATE (fmttype, char, Nvec);
+  ALLOCATE (fmtlist, char *, Nvec);
+  for (i = 0; i < Nvec; i++) {
+    ALLOCATE (fmtlist[i], char, 1024);
+    bzero (fmtlist[i], 1024);
+  }
+
+  p0 = format;
+  for (j = 0; j < Nvec; j++) {
+    /* find this format character */
+    p1 = strchr (p0, '%');
+    if (p1 == (char *) NULL) {
+      gprint (GP_ERR, "mismatch between format and values\n");
+      free (fmttype);
+      for (i = 0; i < Nvec; i++) free (fmtlist[i]);
+      free (fmtlist);
+      free (format);
+      fclose (f);
+      return (FALSE);
+    }
+    
+    /* identify type (%NNNNd %NNNNf) */
+    for (p2 = p1 + 1; (*p2 == '.') || (*p2 == '-') || (*p2 == '+') || (*p2 == ' ') || isdigit(*p2); p2++);
+    strncpy (fmtlist[j], p0, p2 - p0 + 1);
+    switch (*p2) {
+      case 'e':
+      case 'f':
+	fmttype[j] = 'f';
+	break;
+      case 'd':
+      case 'c':
+      case 'x':
+	fmttype[j] = 'd';
+	break;
+      default:
+	gprint (GP_ERR, "syntax error in format (only e,f,d,c,x allowed)\n");
+	return (FALSE);
+    }
+    p0 = p2 + 1;
+  }
+  strcat (fmtlist[Nvec-1], p0);
+  
+  for (i = 0; i < vec[0][0].Nelements; i++) {
+    for (j = 0; j < Nvec; j++) {
+      if (fmttype[j] == 'd') {
+	fprintf (f, fmtlist[j], (int)(vec[j][0].elements[i]));
+      } 
+      if (fmttype[j] == 'f') {
+	fprintf (f, fmtlist[j], (float)(vec[j][0].elements[i]));
+      } 
+    }
+    fprintf (f, "\n");
+  }
+  fclose (f);
+
+  free (fmttype);
+  for (i = 0; i < Nvec; i++) free (fmtlist[i]);
+  free (fmtlist);
+  free (format);
+  
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/zap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/zap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/zap.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "data.h"
+
+int zap (int argc, char **argv) {
+
+  int i, j, N;
+  int sx, sy, nx, ny;
+  float *V, value;
+  Buffer *buf;
+
+  value = 0;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    value  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: zap <buffer> sx sy nx ny [-v value]\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+
+  if (sx < 0) goto error;
+  if (sy < 0) goto error;
+  if (sx + nx > buf[0].matrix.Naxis[0]) goto error;
+  if (sy + ny > buf[0].matrix.Naxis[1]) goto error;
+
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) *V = value;
+  }
+  return (TRUE);
+
+ error:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/zplot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/zplot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/cmd.data/zplot.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "data.h"
+
+int zplot (int argc, char **argv) {
+  
+  int i, kapa, Npts;
+  float *in, *out;
+  double min, range;
+  Graphdata graphmode;
+  Vector *xvec, *yvec, *zvec, Zvec;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: zplot <x> <y> <z> min max\n");
+    return (FALSE);
+  }
+
+  min = atof(argv[4]);
+  range = atof(argv[5]) - min;
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[2]);
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[3]);
+    return (FALSE);
+  }
+  Zvec.Nelements = zvec[0].Nelements;
+  ALLOCATE (Zvec.elements, float, Zvec.Nelements);
+ 
+  in = zvec[0].elements;
+  out = Zvec.elements;
+  for (i = 0; i < Zvec.Nelements; i++, in++, out++) {
+    *out = MIN (1.0, MAX (0.01, (*in - min) / range));
+  }
+
+  /* point size determined by Zvec */
+  graphmode.style = 2; /* plot points */
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0; /* no errorbars */
+  Npts = xvec[0].Nelements;
+  PlotVectorTriplet (kapa, Npts, xvec[0].elements, yvec[0].elements, Zvec.elements, &graphmode);
+
+  free (Zvec.elements);
+
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Analysis.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Analysis.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Analysis.c	(revision 22322)
@@ -0,0 +1,112 @@
+# include "dimm.h"
+
+/* should this all be wrapped within an opihi implementation? */
+
+int subtractImage (Image *a, Image *b) {
+
+  if (a[0].Nx != b[0].Nx) return (FALSE);
+  if (a[0].Ny != b[0].Ny) return (FALSE);
+
+  Npix = a[0].Nx*a[0].Ny;
+  ap = (float *) a[0].buffer;
+  bp = (float *) b[0].buffer;
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    *ap -= *bp;
+  }
+  return (FALSE);
+}
+
+void statsImage (Image *image, Stats *stats) {
+
+  val = (float *)image[0].buffer;
+  max = min = val[0];
+  Npix = image[0].Nx*image[0].Ny;
+  for (i = 0; i < Npix; i++, val++) {
+    N1 += *val;
+    N2 += (*val)*(*val);
+    max = MAX (max, *val);
+    min = MIN (min, *val);
+  }
+  stats[0].mean  = N1 / Npix;
+  stats[0].sigma = sqrt (N2 / Npix - SQ(stats[0].mean));
+  stats[0].min = min;
+  stats[0].max = max;
+
+  stats[0].median = stats[0].mean;
+  range = MAX (0.5, 0xffff / (max - min));
+  if (range == 0) return;
+
+  ALLOCATE (hist, int, 0x10000);
+  bzero (hist, 0x10000*sizeof(int));
+
+  val = (float *)image[0].buffer;
+  for (i = 0; i < Npix; i++) {
+    bin = MIN (MAX (0, (*val - min) * range), 0xffff);
+    hist[bin] ++;
+  }
+
+  Nhist = 0;
+  for (i = 0; (i < 0xffff) && (Nhist < 0.5*Npix); i++) 
+    Nhist += hist[i];
+  stats[0].median = i / range + min;
+  free (hist);
+
+  return;
+}
+
+# if (0)
+void findStars (Image *image, Stars **stars, int *Nstars, double threshold) {
+
+  /* binarize @ threshold */
+
+  binimage = createImage (image[0].Nx, image[0].Ny);
+
+  Npix = image[0].Nx*image[0].Ny;
+  ap = image[0].buffer;
+  bp = binimage[0].buffer;
+  bzero (bp, Npix*sizeof (short));
+
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    if (*ap > threshold) * bp = 1;
+  }
+
+  clearpix ();
+
+  for (i = 0; i < Ny; i++) {
+    for (j = 0; j < Nx; j++) {
+      pix = j + i*Ny;
+      if (binimage.buffer[pix]) {
+	addpix (pix);
+	binimage.buffer[pix] = 0;
+	/* continue in row to end */
+	for (k = j + 1; k < Nx; k++) {
+	  pix = k + i*Nx;
+	  if (!binimage.buffer[pix]) { 
+	  }
+	}
+
+# endif
+
+/* find stars: 
+   - binarize @ threshold
+   - find all contiguous blobs
+   - find geom center of each blob
+*/
+
+
+
+/*
+
+.....................
+....x................
+...xxx...............
+....x...xxx..........
+.........xx..........
+.....................
+.....................
+.....................
+.....................
+.....................
+
+
+ */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Camera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Camera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Camera.c	(revision 22322)
@@ -0,0 +1,223 @@
+# include "dimm.h"
+
+static struct sbig_init info;
+
+int InitCamera (int port) {
+  
+  int i, state;
+
+  for (i = 0; i < 10; i++) {
+    state = sbig_init (port, SBIG_IMAGING_CCD, &info);
+    if (state == -6) state = 0;
+    if (state ==  0) {
+      DumpCameraInfo ();
+      return (TRUE);
+    }
+    gprint (GP_ERR, "retry...\n");
+  }
+  
+  gprint (GP_ERR, "failed to init sbig camera on %d\n", port);
+  gprint (GP_ERR, "%s\n", sbig_show_error (state));
+  return (FALSE);
+}
+
+void DumpCameraInfo () {
+
+      gprint (GP_ERR, "opened sbig camera:\n");
+      gprint (GP_ERR, "linux_version: %f\n",      info.linux_version);
+      gprint (GP_ERR, "nmbr_bad_columns: %d\n",   info.nmbr_bad_columns);
+      gprint (GP_ERR, "imaging_abg_type: %d\n",   info.imaging_abg_type);
+      gprint (GP_ERR, "serial_number: %s\n",      info.serial_number);
+      gprint (GP_ERR, "firmware_version: %d\n",   info.firmware_version);
+      gprint (GP_ERR, "camera_name: %s\n",        info.camera_name);
+      gprint (GP_ERR, "nmbr_readout_modes: %d\n", info.camera_info[0].nmbr_readout_modes);
+      gprint (GP_ERR, "mode: %d\n",               info.camera_info[0].readout_mode[0].mode);
+      gprint (GP_ERR, "width: %d\n",              info.camera_info[0].readout_mode[0].width);
+      gprint (GP_ERR, "height: %d\n",             info.camera_info[0].readout_mode[0].height);
+      gprint (GP_ERR, "gain: %d\n",               info.camera_info[0].readout_mode[0].gain);
+      gprint (GP_ERR, "pixel_width: %d\n",        info.camera_info[0].readout_mode[0].pixel_width);
+      gprint (GP_ERR, "pixel_height: %d\n",       info.camera_info[0].readout_mode[0].pixel_height);
+
+      gprint (GP_ERR, "ST5_AD_size: %d\n", info.ST5_AD_size);
+      gprint (GP_ERR, "ST5_filter_type: %d\n", info.ST5_filter_type);
+}      
+
+void CameraFullSize (int *x, int *y) {
+  *x = info.camera_info[0].readout_mode[0].width;
+  *y = info.camera_info[0].readout_mode[0].height;
+}
+
+int SetTemperature (double temp) {
+
+  int state;
+  struct sbig_cool cool;
+
+  if (temp < -50) return (FALSE);
+  if (temp > +20) return (FALSE);
+
+  cool.regulation = SBIG_TEMP_REGULATION_ON;
+  cool.temperature = (int) (10.0*temp + 0.5);
+  cool.direct_drive = 0;
+
+  state = sbig_set_cooling (&cool);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+double GetTemperature () {
+
+  int state;
+  double temp;
+  struct sbig_status status;
+
+  state = sbig_get_status (&status);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (-200.0);
+  }
+  temp = (status.ccd_temperature - 0.5) / 10.0;
+  return (temp);
+}
+
+int DumpCameraStatus () {
+
+  int state;
+  double temp;
+  struct sbig_status status;
+
+  state = sbig_get_status (&status);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+
+  gprint (GP_ERR, "imaging_ccd_status: %d\n", status.imaging_ccd_status);
+  gprint (GP_ERR, "tracking_ccd_status: %d\n", status.tracking_ccd_status);
+  gprint (GP_ERR, "fan_on: %d\n", status.fan_on);
+  gprint (GP_ERR, "shutter_state: %d\n", status.shutter_state);
+  gprint (GP_ERR, "led_state: %d\n", status.led_state);
+  gprint (GP_ERR, "shutter_edge: %d\n", status.shutter_edge);
+  gprint (GP_ERR, "plus_x_relay: %d\n", status.plus_x_relay);
+  gprint (GP_ERR, "minus_x_relay: %d\n", status.minus_x_relay);
+  gprint (GP_ERR, "plus_y_relay: %d\n", status.plus_y_relay);
+  gprint (GP_ERR, "minus_y_relay: %d\n", status.minus_y_relay);
+  gprint (GP_ERR, "pulse_active: %d\n", status.pulse_active);
+  gprint (GP_ERR, "temperature_regulation: %d\n", status.temperature_regulation);
+  gprint (GP_ERR, "temperature_setpoint: %d\n", status.temperature_setpoint);
+  gprint (GP_ERR, "cooling_power: %d\n", status.cooling_power);
+  gprint (GP_ERR, "air_temperature: %d\n", status.air_temperature);
+  gprint (GP_ERR, "ccd_temperature: %d\n", status.ccd_temperature);
+
+  return (TRUE);
+}
+
+/* block until exposure is complete */
+int Exposure (double exptime) {
+
+  int i, state;
+  struct sbig_expose expose;
+  struct sbig_status status;
+
+  expose.ccd = SBIG_IMAGING_CCD;
+  expose.exposure_time = (int)(100.0*exptime);
+  expose.abg_state = SBIG_ABG_OFF;
+  expose.shutter = SBIG_EXPOSE_SHUTTER_NORMAL;  /* shuttermode = ? */
+
+  /* drop this ? */
+  /* usleep ((int)(exptime*1000000)); */
+  state = sbig_expose (&expose);
+  if (state < 0) {
+    gprint (GP_ERR, "exposure error\n");
+    gprint (GP_ERR, "%s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  
+  for (i = 0; i < expose.exposure_time + 10; ++i) {
+    state = sbig_get_status (&status);
+    /* gprint (GP_ERR, "%d\n", state); */
+    /* gprint (GP_ERR, "%d  %d\n", status.imaging_ccd_status, status.shutter_state); */
+    /*    if (state == 0) return (TRUE); */
+    if (status.imaging_ccd_status == -SBIG_NO_EXPOSURE_IN_PROGRESS) return (TRUE);
+    if (status.imaging_ccd_status == -SBIG_EXPOSURE_IN_PROGRESS) {
+      usleep (10000);
+      continue;
+    }
+    gprint (GP_ERR, "exposure error\n");
+    gprint (GP_ERR, "%s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  gprint (GP_ERR, "exposure timeout\n");
+  return (FALSE);
+}
+
+int   readout_abort;
+float readout_percent;
+static int readout_callback (float percent) {
+  /* return 1 to continue, 0 to abort */
+  if (((int)(percent) % 10) == 0) { gprint (GP_ERR, "."); }
+  readout_percent = percent;
+  if (readout_abort) return 0;
+  return 1;
+}
+
+int ReadOut (int x, int y, int dx, int dy, int binning, unsigned short *buffer) {
+
+  int state, Nbytes;
+  static struct sbig_readout readout;
+
+  readout.x = x;
+  readout.y = y;
+  readout.width  = dx;
+  readout.height = dy;
+
+  Nbytes = readout.width*readout.height*sizeof(short);
+
+  /* for bin 2x2 or 3x3, need to adjust dx, dy above */
+  readout.ccd = SBIG_IMAGING_CCD;
+  readout.binning = SBIG_BIN_1X1;
+  readout.data = buffer;
+  readout.data_size_in_bytes = Nbytes;
+  readout.callback = readout_callback;
+    
+  gprint (GP_ERR, "%d, %d : %d x %d\n", readout.x, readout.y, readout.width, readout.height);
+  sync (); 
+  readout_abort = FALSE;
+  state = sbig_readout (&readout);
+  gprint (GP_ERR, "\n");
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int OpenShutter () {
+
+  int state;
+  struct sbig_control control;
+
+  control.shutter = SBIG_OPEN_SHUTTER;
+  state = sbig_control (&control);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int CloseShutter () {
+
+  int state;
+  struct sbig_control control;
+
+  control.shutter = SBIG_CLOSE_SHUTTER;
+  state = sbig_control (&control);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Image.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Image.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Image.c	(revision 22322)
@@ -0,0 +1,85 @@
+# include "dimm.h"
+
+/*** this uses an Image structure from DIMM which 
+     is different from the Image structure in DVO ***/
+
+static Image *images = (Image *) NULL;
+static int   Nimages = 0;
+
+Image *createImage (int Nx, int Ny) {
+
+  int N;
+
+  if (Nx*Ny <= 0) return ((Image *) NULL);
+
+  N = Nimages;
+  Nimages ++;
+  if (images == (Image *) NULL) {
+    ALLOCATE (images, Image, MAX (1, Nimages));
+  } else {
+    REALLOCATE (images, Image, MAX (1, Nimages));
+  }
+
+  images[N].Nx = Nx;
+  images[N].Ny = Ny;
+  images[N].Nbytes = Nx*Ny*sizeof (short);
+  ALLOCATE (images[N].buffer, char, images[N].Nbytes);
+
+  return (&images[N]);
+}
+
+int freeImage (Image *entry) {
+
+  int i, j, N;
+
+  N = -1;
+  for (i = 0; (i < Nimages) && (N == -1) ; i++) {
+    if (&images[i] == entry) N = i;
+  }
+  if (N == -1) return (FALSE);
+
+  free (images[N].buffer);
+
+  for (j = N; j < Nimages - 1; j++) {
+    images[j] = images[j+1];
+  }
+
+  Nimages --;
+  REALLOCATE (images, Image, MAX (1, Nimages));
+  return (TRUE);
+}
+
+int writeImage (char *filename, Image *image) {
+
+  Header header;
+  Matrix matrix;
+
+  gfits_init_header (&header);
+
+  header.Naxes = 2;
+  header.Naxis[0] = image[0].Nx;
+  header.Naxis[1] = image[0].Ny;
+  header.bitpix = 16;
+
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  free (matrix.buffer);
+
+  matrix.buffer = image[0].buffer;
+  
+  /* write meta-data to header */
+  gfits_print (&header, "RA", "%lf", 1, image[0].ra);
+  gfits_print (&header, "DEC", "%lf", 1, image[0].dec);
+  gfits_print (&header, "EQUINOX", "%lf", 1, 2000.0);
+
+  gfits_print (&header, "AIRMASS", "%lf", 1, image[0].airmass);
+  gfits_print (&header, "CCDTEMP", "%lf", 1, image[0].ccdtemp);
+  gfits_print (&header, "AIRTEMP", "%lf", 1, image[0].airtemp);
+  gfits_print (&header, "EXPTIME", "%lf", 1, image[0].exptime);
+
+  gfits_write_header (filename, &header);
+  gfits_write_matrix (filename, &matrix);
+  
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Makefile	(revision 22322)
@@ -0,0 +1,69 @@
+default: dimm
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/dimm
+DATA    =       $(DESTDATA)/dimm
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata -lsbig
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# dimm user commands and support functions #####################
+funcs = \
+$(SRC)/Camera.$(ARCH).o		\
+$(SRC)/Serial.$(ARCH).o		\
+$(SRC)/Telescope.$(ARCH).o             
+
+cmds = \
+$(SRC)/init.$(ARCH).o		  	\
+$(SRC)/dimm.$(ARCH).o		  	\
+$(SRC)/camera.$(ARCH).o	  	\
+$(SRC)/findstars.$(ARCH).o	  	\
+$(SRC)/telescope.$(ARCH).o   \
+$(SRC)/version.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a \
+$(DESTLIB)/libsbig.a
+
+dimm: sbig $(BIN)/dimm.$(ARCH)
+$(SRC)/dimm.$(ARCH).o : $(libs)
+$(BIN)/dimm.$(ARCH)   : $(funcs) $(cmds)
+
+install: $(DESTBIN)/dimm help modules
+
+help: cmd.basic.help cmd.data.help cmd.astro.help dimm.help
+
+modules: dimm.modules
+
+# SBIG install functions
+sbig: $(DESTINC)/sbig.h $(DESTLIB)/libsbig.a
+	@echo sbig code installed
+
+$(DESTINC)/sbig.h:	$(SRC)/sbig/sbig.h
+	cp $(SRC)/sbig/sbig.h $(DESTINC)/sbig.h
+
+$(DESTLIB)/libsbig.a:	$(SRC)/sbig/sbig.a
+	cp $(SRC)/sbig/sbig.a $(DESTLIB)/libsbig.a
+
+$(SRC)/sbig/sbig.a: $(SRC)/sbig/sbig.a.src
+	cp $(SRC)/sbig/sbig.a.src $(SRC)/sbig/sbig.a
+
+.PHONY: dimm
+
+# are these used or replaced?
+# $(SRC)/analysis.$(ARCH).o	  	\
+# $(SRC)/Analysis.$(ARCH).o            \
+# $(SRC)/Image.$(ARCH).o		\
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Serial.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Serial.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Serial.c	(revision 22322)
@@ -0,0 +1,388 @@
+# include "dimm.h"
+# include <termios.h>
+
+# define CR   0x0D
+# define LF   0x0A
+# define BEEP 0x07
+# define OPENERR    -1   /* Port could not be opened */
+# define PORTERR    -2   /* Opened port is not a serial (tty) port */
+# define BADCMDERR  -3   /* Command sent to camera is not understood */
+# define TIMEOUTERR -4   /* No response */
+ 
+# define SER_VERBOSE 0
+# define SER_DEBUG   0
+
+# ifndef SER_VERBOSE
+# define SER_VERBOSE 1           /* Be verbose? */
+# endif
+# ifndef SER_DEBUG 
+# define SER_DEBUG 1
+# endif
+
+/* Defines for Serial Port  */
+typedef struct {
+  int f;
+  int rate;
+  int parity;
+  int bits;
+  int stpbit;
+  char port[64];
+} Serial;
+
+static Serial serial = {0, 0, 0, 0, 0};
+static int SER_ECHO = 0;
+
+int SerialOpen (char *);
+
+int SerialVerbose (int mode) {
+
+  SER_ECHO = mode;
+  return (TRUE);
+
+}
+
+int SerialInit (char *port) {
+  
+  strcpy (serial.port, port);
+  serial.rate = 2400;
+  serial.rate = 9600;
+  serial.parity = 0;
+  serial.bits = 8;
+  serial.stpbit = 1;
+  
+  serial.f = SerialOpen (serial.port);
+  if (serial.f <= 0) {
+    gprint (GP_ERR, "Error opening serial port %s - error %d.\n", serial.port, serial.f);
+    return (FALSE);
+  }
+  if (SerialBaudRate (serial.f, serial.rate))   return (FALSE);
+  if (SerialParity   (serial.f, serial.parity)) return (FALSE);
+  if (SerialDataBits (serial.f, serial.bits))   return (FALSE);
+  if (SerialStopBit  (serial.f, serial.stpbit)) return (FALSE);
+  return (TRUE);
+}
+
+/********************************** Open *********************************/
+int SerialOpen (char *port) {
+  
+  int err = 0;
+  struct termios term;
+  char prefix[100];
+  int fdesc = -1;
+  int locked;
+  struct flock lock;
+   
+  /* open serial line */
+   if (SER_VERBOSE) printf ("Opening the serial port %s for read/write.\n", port);
+   fdesc = open (port, O_RDWR);
+   if (fdesc == -1) {
+     if (SER_VERBOSE) gprint (GP_ERR, "Cannot open %s for read/write.\n", port);
+     return OPENERR;
+   }
+   /* lock serial line */
+   lock.l_type = F_WRLCK;
+   lock.l_len = 0;
+   lock.l_start = 0;
+   lock.l_whence = 0;
+   locked = fcntl (fdesc, F_SETLK, &lock);
+   if (locked == -1) {
+     gprint (GP_ERR, "can't lock serial line\n");
+     close (fdesc);
+     return OPENERR;
+   }
+
+   /* Get the serial port's attributes... */
+   if (tcgetattr (fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   /* cfmakeraw (&term); */
+   term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+   term.c_oflag &= ~OPOST;
+   term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+   term.c_cflag &= ~(CSIZE|PARENB);
+   term.c_cflag |= CS8;
+   term.c_cc[VMIN] = 0;     /* MIN setting...if 0, wait only for timeout */
+   term.c_cc[VTIME] = 2;    /* TIME setting...wait at most 1 sec for response 
+		              (ignored if c_cc[VMIN]>0) */
+   tcsetattr (fdesc, TCSAFLUSH, &term);
+
+# if (0)   
+   term.c_lflag &= ~ICANON; /* Turn OFF canonical input 
+ 	                      (so it does it character by character) */
+   term.c_cc[VMIN] = 0;     /* MIN setting...if 0, wait only for timeout */
+   term.c_cc[VTIME] = 10;    /* TIME setting...wait at most 1 sec for response 
+		              (ignored if c_cc[VMIN]>0) */
+   term.c_lflag &= ~ECHO;   /* Turn OFF echoing... */
+   term.c_iflag &= ~ICRNL;   /* Don't map CR to NL on input */
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr (fdesc, TCSAFLUSH, &term);
+# endif
+
+   return fdesc;
+}
+
+/******************************* Baud Rate *******************************/
+int SerialBaudRate (int fdesc, int rate) {
+   int err = 0;
+   struct termios term;
+
+  /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's baud rate.\n");
+   if (tcgetattr (fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+ 
+   /* Set the input and output baud rates to 'rate'... */
+   switch (rate) { 
+   case 1200:         /* Set speed to 1200 */ 
+      if (cfgetospeed(&term) != B1200) cfsetospeed(&term, B1200);
+      if (cfgetispeed(&term) != B1200) cfsetispeed(&term, B1200);
+      break;
+   case 2400:         /* Set speed to 2400 */ 
+      if (cfgetospeed(&term) != B2400) cfsetospeed(&term, B2400);
+      if (cfgetispeed(&term) != B2400) cfsetispeed(&term, B2400);
+      break;
+   case 9600:         /* Set speed to 9600 */
+      if (cfgetospeed(&term) != B9600) cfsetospeed(&term, B9600);
+      if (cfgetispeed(&term) != B9600) cfsetispeed(&term, B9600);
+      break;
+   case 19200:        /* Set speed to 19200 */ 
+      if (cfgetospeed(&term) != B19200) cfsetospeed(&term, B19200);
+      if (cfgetispeed(&term) != B19200) cfsetispeed(&term, B19200);
+      break;
+   default:
+      printf ("ERROR:  Unknown Baud Rate\n"); 
+      break;
+   }	
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/****************************** Parity ********************************/
+int SerialParity (int fdesc, int parity) { 
+   int err=0;
+   struct termios term;
+   char *prefix = "/dev/term/";
+
+   /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's parity.\n");
+   if (tcgetattr(fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   if (parity == 0)              /* Turn off parity generation... */
+      term.c_cflag &= ~PARENB;     
+   else if (parity == 1) {            
+      term.c_cflag |= PARENB;     /* Turn on parity generation... */
+      term.c_cflag |= PARODD;     /* Sets parity to odd */ 
+   }	 
+   else if (parity == 2)         /* Turn on parity generation... */
+      term.c_cflag |= PARENB;     /* Defaults parity to even */ 
+   else 
+      printf ("ERROR:  Unknown parity specification\n");
+ 
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/****************************Data Bit Size ***************************/
+int SerialDataBits(int fdesc, int bits) { 
+   int err=0;
+   struct termios term;
+
+   /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's data bit size.\n");
+   if (tcgetattr(fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   switch (bits) {
+   case 5:  			/* Sets data bits to 5 */
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS5;
+      break; 
+   case 6:   			/* Sets data bits to 6 */ 
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS6;
+      break; 
+   case 7:     		/* Sets data bits to 7 */ 
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS7;
+      break; 
+   case 8:  			/* Sets data bits to 8 */
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS8;
+      break; 
+   default:
+      printf ("ERROR:  Illegal data bit size\n");
+      break; 
+   }
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/****************************** Stop Bit ****************************/
+int SerialStopBit(int fdesc, int stpbit) { 
+   int err=0;
+   struct termios term;
+
+   /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's stop bit.\n");
+   if (tcgetattr(fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   if (stpbit == 1) {     		/* Sets stop bit to 1 */  
+      if (term.c_cflag & CSTOPB) term.c_cflag |= CSTOPB;
+   } else { 				/* Else stop bit to 2 */ 
+      term.c_cflag & CSTOPB;
+   }
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/**************************** Stop ***********************************/
+void SerialStop (int fdesc) {
+   if (SER_VERBOSE) printf("Closing the serial port.\n");
+   close(fdesc); /* Close up shop... */
+}
+
+# define D_NREAD 1024
+
+/* send a string to the serial port, wait for an answer */
+/* answer is returned on the pointer provided */
+
+/**************************** Command ***********************************/
+int SerialCommand (char *in, char **out, int wait) {
+  
+  int i, j;
+  char *line;
+  int done, Nread, Nin, NREAD;
+  
+  if (SER_ECHO) gprint (GP_ERR, "command: %s\n", in); 
+
+  if (serial.f <= 0) {
+    gprint (GP_ERR, "serial line closed\n"); 
+    if (out != (char **) NULL) {
+      *out = strcreate ("SERIAL OFF");
+      /* return (char *) NULL instead? */
+    }
+    return (FALSE);
+  }
+
+  /* flush out the line */
+  tcflush (serial.f, TCIOFLUSH);
+
+# if (0)
+  for (i = 0; i < strlen(in); i++) {
+    gprint (GP_ERR, "%d %x\n", i, in[i]);
+  }
+# endif
+
+  /* send command to serial line */
+  Nin = write (serial.f, in, strlen(in));
+  if (Nin != strlen(in)) {
+    gprint (GP_ERR, "Serial Command not sent\n");
+    return (FALSE);
+   }
+  usleep (20000);
+  /* LX200 GPS requires some lead time (10msec) to check ready state */
+  
+  /* create space to store answer */
+  NREAD = D_NREAD;
+  ALLOCATE (line, char, NREAD);
+  bzero (line, NREAD);
+  Nread = Nin = 0;
+
+  /* read data back from serial line until no response (Nin == 0) 
+     or timeout (Nin == -1) && (i == wait) */
+
+  done = FALSE;
+  for (i = 0; !done && (i < wait); i++) {
+    Nin = read (serial.f, &line[Nread], 256);
+# if (SER_DEBUG)
+    gprint (GP_ERR, "%d ", Nin);
+# endif
+    if (Nin < 0) { /* error, check value */
+      gprint (GP_ERR, "error?");
+      continue;
+    }
+    if (Nin > 0) {
+      Nread += Nin;
+      line[Nread] = 0;
+      i = 0;
+    }
+    if (Nread > D_NREAD - 257) {
+      NREAD += D_NREAD;
+      REALLOCATE (line, char, NREAD);
+    }
+    if ((i > 0) && (Nin == 0)) done = TRUE;
+    usleep (2000);
+  }
+
+  if (SER_ECHO) gprint (GP_ERR, "answer: %s\n", line);
+
+  if (out == (char **) NULL) {
+    free (line);
+  } else {
+    *out = line;
+  }
+  return (TRUE);
+}
+
+
+/* raw:
+   cfmakeraw sets the terminal attributes as follows:
+   termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+   termios_p->c_oflag &= ~OPOST;
+   termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+   termios_p->c_cflag &= ~(CSIZE|PARENB);
+   termios_p->c_cflag |= CS8;
+
+   there conditions are turned off:
+   c_iflag:
+       IGNBRK ignore BREAK condition on input
+       BRKINT If IGNBRK is not set, generate SIGINT on BREAK condition, else read BREAK as
+              character \0.
+       PARMRK if  IGNPAR  is  not  set,  prefix a character with a parity error or framing
+              error with \377 \0.  If neither IGNPAR nor PARMRK is set, read  a  character
+              with a parity error or framing error as \0.
+       (IGNPAR ignore framing errors and parity errors.)
+
+       ISTRIP strip off eighth bit
+       INLCR  translate NL to CR on input
+       IGNCR  ignore carriage return on input
+       ICRNL  translate carriage return to newline on input (unless IGNCR is set)
+       IXON   enable XON/XOFF flow control on output
+
+   c_oflag:
+       OPOST  enable implementation-defined output processing
+
+   c_lflag:
+       ECHO   echo input characters.
+       ECHONL if ICANON is also set, echo the NL character even if ECHO is not set.
+       ICANON enable canonical mode.  This enables the special characters EOF, EOL,  EOL2,
+              ERASE, KILL, REPRINT, STATUS, and WERASE, and buffers by lines.
+       ISIG   when any of the characters INTR, QUIT, SUSP, or DSUSP are received, generate
+              the corresponding signal.
+       IEXTEN enable implementation-defined input processing.
+
+   c_cflag:
+       CSIZE  character size mask.  Values are CS5, CS6, CS7, or CS8. (CS8 set).
+       PARENB enable parity generation on output and parity checking for input.
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Telescope.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Telescope.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/Telescope.c	(revision 22322)
@@ -0,0 +1,361 @@
+# include "dimm.h"
+
+# define SER_TIMEOUT 10
+# define SLEW_TIMEOUT 30
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+double distSky (double r1, double r2, double d1, double d2) {
+
+  double x1, y1, z1;
+  double x2, y2, z2;
+  double cosT, dist;
+
+  x1 = dCOS (r1) * dCOS (d1);
+  y1 = dSIN (r1) * dCOS (d1);
+  z1 = dSIN (d1);
+
+  x2 = dCOS (r2) * dCOS (d2);
+  y2 = dSIN (r2) * dCOS (d2);
+  z2 = dSIN (d2);
+
+  cosT = x1*x2 + y1*y2 + z1*z2;
+  dist = DEG_RAD * acos (cosT);
+
+  return (dist);
+}
+
+int getRD (double *r, double *d) { 
+
+  int status;
+  char *rastr, *decstr;
+
+  status = SerialCommand (":GR#", &rastr, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  status = SerialCommand (":GD#", &decstr, SER_TIMEOUT); 
+  if (!status) return (FALSE);
+
+  status = ohana_str_to_radec (r, d, rastr, decstr);
+  if (!status) return (FALSE);
+
+  free (rastr);
+  free (decstr);
+
+  return (TRUE);
+}
+
+int gotoRD (double r, double d) {
+
+  double R, D, dist;
+  int Ntry, status;
+  char *str, *answer, cmd[64];
+
+  /* error on ra, dec means coords out of range */
+
+  /* set telescope coords, send */
+  str = meade_ra_to_str (r);
+  sprintf (cmd, ":Sr%s#", str);   free (str);
+  status = SerialCommand (cmd, &answer, SER_TIMEOUT);
+  if (!status) return (FALSE); 
+  if (answer == (char *) NULL) return (FALSE); 
+  if (strcmp (answer, "1")) return (FALSE); 
+  free (answer);
+
+  str = meade_dec_to_str (d);
+  sprintf (cmd, ":Sd%s#", str);  free (str);
+  status = SerialCommand (cmd, &answer, SER_TIMEOUT);   
+  if (!status) return (FALSE); 
+  if (answer == (char *) NULL) return (FALSE); 
+  if (strcmp (answer, "1")) return (FALSE); 
+  free (answer);
+
+  Ntry = 0;
+  status = SerialCommand (":MS#", &answer, SER_TIMEOUT);   
+  if (!status) return (FALSE); 
+  if (answer == (char *) NULL) return (FALSE); 
+  if (strcmp (answer, "0")) {
+    gprint (GP_ERR, "error: %s\n", answer);
+    return (FALSE); 
+  }
+  free (answer);
+
+  /* watch for response? */
+  status = FALSE;
+  while (!status) {
+    getRD (&R, &D);
+    dist = distSky (R, r, D, d);
+    if (dist < 0.1) return (TRUE);
+    usleep (100000);
+    Ntry ++;
+    if (Ntry > SLEW_TIMEOUT) return (FALSE);
+  }
+  return (status);
+}
+
+/* actual offsets are x,y, convert to arcmin */
+int offset (char *direction, double distance) {
+
+  /* Four rate choices: 
+     slew   (RS) -  8 degree / sec : rate 1
+     find   (RM) - 30 arcmin / sec : rate 2
+     center (RC) -  4 arcmin / sec : rate 3
+     guide  (RG) - 15 arcsec / sec : rate 4
+
+     communication requires ~1.0 sec:
+     offset should use rate which gives shortest time > 2.0 sec 
+  */
+
+# define NRATE 4
+  static double delay[NRATE]  = {0.1, 0.1, 0.1, 0.1};
+  static double rate[NRATE]   = {480.0, 30.0, 4.0, 0.25};
+  static char rcmd[NRATE][16] = {"RS", "RM", "RC", "RG"};
+
+  int i, status, rsel;
+  char dir, cmd[32];
+  double tsel, dt;
+
+  dir = 0;
+  if (!strcasecmp (direction, "y")) dir = (distance > 0) ? 'n' : 's';
+  if (!strcasecmp (direction, "x")) dir = (distance > 0) ? 'w' : 'e';
+  if (!dir) return (FALSE);
+
+  /* distance is in arcmin */
+  distance = fabs (distance);
+
+  /* logic is bad -- does not catch too small distances */  
+  rsel = -1;
+  tsel = SLEW_TIMEOUT;
+  for (i = 0; i < NRATE; i++) {
+    dt = distance / rate[i] - delay[i];
+    if ((dt > 0) && (dt < tsel)) {
+      rsel = i;
+      tsel = dt;
+    }
+  }
+  if (tsel < 0) {
+    gprint (GP_ERR, "offset %f arcmin below minimum\n", distance);
+    return (FALSE);
+  }
+  if (tsel > SLEW_TIMEOUT) {
+    gprint (GP_ERR, "offset %f arcmin above maximum\n", distance);
+    return (FALSE);
+  }
+  gprint (GP_ERR, "offsetting %c for %f seconds\n", dir, tsel);
+  
+  sprintf (cmd, ":%s#", rcmd[rsel]);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  sprintf (cmd, ":M%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  usleep ((int)(tsel*1000000));
+
+  sprintf (cmd, ":Q%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  return (TRUE);
+}  
+
+/* actual offsets are x,y, convert to arcmin */
+int toffset (char *direction, char *rate, double duration) {
+
+# define NRATE 6
+  /* static char rcmd[NRATE][16] = {"RS", "RM", "RC", "RG"};*/
+  static char rcmd[NRATE][64] = {"RS", "RM", "RC", "RG", "RA0.0085", "RE0.0085"};
+
+  int i, status, rsel;
+  char dir, cmd[32];
+  double tsel, dt;
+
+  dir = 0;
+  if (!strcasecmp (direction, "x")) dir = (duration > 0) ? 'w' : 'e';
+  if (!strcasecmp (direction, "y")) dir = (duration > 0) ? 'n' : 's';
+  if (!dir) return (FALSE);
+  duration = fabs (duration);
+  
+  status = FALSE;
+  for (i = 0; i < NRATE; i++) if (!strcmp (rcmd[i], rate)) status = TRUE;
+  if (!status) {
+    gprint (GP_ERR, "bad rate: %s\n", rate);
+    return (FALSE);
+  }
+
+  sprintf (cmd, ":%s#", rate);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  sprintf (cmd, ":M%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  usleep ((int)(duration*1000000));
+
+  sprintf (cmd, ":Q%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  return (TRUE);
+}  
+
+int getXY (double *x, double *y) {
+
+  char *answer;
+
+  SerialCommand (":GA#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (x, answer);
+  free (answer);
+
+  SerialCommand (":GZ#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (y, answer);
+  free (answer);
+
+  return (TRUE);
+}
+
+/* need error checking on these */
+int setRD (double r, double d) {
+
+  char *str, *answer, cmd[64];
+
+  /* set telescope coords, send */
+  str = meade_ra_to_str (r);
+  sprintf (cmd, ":Sr%s#", str);
+  SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  str = meade_dec_to_str (d);
+  sprintf (cmd, ":Sd%s#", str);
+  SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  SerialCommand (":CM#", &answer, SER_TIMEOUT);
+  gprint (GP_ERR, "result: %s\n", answer);
+  free (answer);
+  return (TRUE);
+}
+
+int setSite (char *sitename, double lon, double lat) {
+
+  struct tm *gmt;
+  struct timeval now;
+  char *str, line[32];
+
+  gprint (GP_ERR, "careful, this causes problems\n");
+  return (FALSE);
+
+  SerialCommand (":W1#", (char **) NULL, SER_TIMEOUT);
+
+  /* Set site name 1 */
+  sprintf (line, ":SM %s#", sitename); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /* Set site long */
+  str = meade_deg_to_str (lon);
+  str[6] = 0;
+  sprintf (line, ":Sg%s#", str); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  /* Set site lat */
+  str = meade_dec_to_str (lat);
+  sprintf (line, ":St%s#", str); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  /* set UTC offset to 0.0: offset + local = gmt */
+  sprintf (line, ":SG+00#");
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /* Set local */
+  gettimeofday (&now, (struct timezone *) NULL);
+  gmt = gmtime (&now.tv_sec);
+  sprintf (line, ":SL%02d:%02d:%02d#", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  return (TRUE);
+}
+
+int setTime (char *lst) {
+
+  struct tm *gmt;
+  struct timeval now;
+  char line[32], *answer;
+
+  /* set UTC offset to 0.0: offset + local = gmt */
+  sprintf (line, ":SG+10#");
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /* Set local */
+  gettimeofday (&now, (struct timezone *) NULL);
+  gmt = localtime (&now.tv_sec);
+  sprintf (line, ":SL%02d:%02d:%02d#", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /*
+  sprintf (line, ":SS%s#", lst);
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+  */
+
+  return (TRUE);
+}
+
+int getSite (double *lon, double *lat, double *lst) {
+
+  struct tm *gmt;
+  struct timeval now;
+  char *str, *answer, line[32];
+
+  /* : get latitude */
+  SerialCommand (":Gt#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (lat, answer);
+  free (answer);
+
+  /* : get longitude */
+  SerialCommand (":Gg#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (lon, answer);
+  free (answer);
+
+  /* : get LST */
+  SerialCommand (":GS#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (lst, answer);
+  free (answer);
+
+  return (TRUE);
+}
+
+int ParkScope() {
+
+  char *str, *answer, line[32];
+
+  SerialCommand (":hP#", &answer, SER_TIMEOUT);
+  free (answer);
+
+  return (TRUE);
+
+}
+
+int SleepScope() {
+
+  char *str, *answer, line[32];
+  
+  SerialCommand (":hN#", &answer, SER_TIMEOUT);
+  free (answer);
+  
+  return (TRUE);
+
+}
+
+int WakeScope() {
+
+  char *str, *answer, line[32];
+
+  SerialCommand (":hW#", &answer, SER_TIMEOUT);
+  free (answer);
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/altaz.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/altaz.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/altaz.c	(revision 22322)
@@ -0,0 +1,88 @@
+# include "dimm.h"
+
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+double atan2 (double y, double x);
+
+int altaz (int argc, char **argv) {
+  
+  double alt, az, lat, rot;
+  double ha, dec;
+  double sind, sinh, cosh;
+  char *latstr;
+
+  if (argc != 6) goto usage;
+
+  if (!strcmp (argv[1], "-h")) goto radec;
+  if (!strcmp (argv[1], "-c")) goto altaz;
+
+ radec:
+  /* ha/dec -> alt/az */
+  ha  = atof (argv[2]);
+  dec = atof (argv[3]);
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return (FALSE);
+  }
+  lat = atof (latstr);
+  free (latstr);
+ 
+  sind = dSIN (dec) * dSIN (lat) + dCOS (dec) * dCOS (ha) * dCOS (lat);
+  alt  = DEG_RAD * asin (sind);
+
+  sinh = - dCOS (dec) * dSIN (ha);
+  cosh =   dSIN (dec) * dCOS (lat) - dCOS (dec) * dCOS (ha) * dSIN (lat);
+
+  az = DEG_RAD * atan2 (sinh, cosh);
+  set_variable (argv[4], alt);
+  set_variable (argv[5], az);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(ha) * dSIN(lat) - dCOS(az) * dCOS(ha);
+  rot = -DEG_RAD * atan2 (sinh, cosh);
+  set_variable ("ROT", rot);
+
+  return (TRUE);
+  
+ altaz:
+  /* alt/az -> ha/dec */
+  alt = atof (argv[4]);
+  az  = atof (argv[5]);
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return (FALSE);
+  }
+  lat = atof (latstr);
+  free (latstr);
+
+  sind = dSIN (alt) * dSIN (lat) + dCOS (alt) * dCOS (az) * dCOS (lat);
+  dec  = DEG_RAD * asin (sind);
+
+  sinh = -dCOS (alt) * dSIN (az);
+  cosh =  dSIN (alt) * dCOS (lat) - dCOS (alt) * dCOS (az) * dSIN (lat);
+
+  ha = DEG_RAD * atan2 (sinh, cosh);
+  set_variable (argv[2], ha);
+  set_variable (argv[3], dec);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(ha) * dSIN(lat) - dCOS(az) * dCOS(ha);
+  rot = -DEG_RAD * atan2 (sinh, cosh);
+  set_variable ("ROT", rot);
+
+  return (TRUE);
+  
+ usage:
+  gprint (GP_ERR, "USAGE: altaz -h (ha) (dec) (alt) (az)\n");
+  gprint (GP_ERR, "USAGE: altaz -c (ha) (dec) (alt) (az)\n");
+  gprint (GP_ERR, "       -h alt/az to ha/dec, -c ha/dec to alt/az\n");
+  gprint (GP_ERR, "       returned values in variables provided\n");
+  return (FALSE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/analysis.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/analysis.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/analysis.c	(revision 22322)
@@ -0,0 +1,184 @@
+
+/* should this all be wrapped within an opihi implementation? */
+
+typedef struct {
+
+  /* image data area */
+  int Nx, Ny;
+  char *buffer;
+  int Nbytes;
+
+  /* image metadata */
+  double ccdtemp;
+  double airtemp;
+  double ra, dec, airmass;
+  double exptime;
+  int binning;
+} Image;
+
+subtractImage (Image *a, Image *b) {
+
+  if (a[0].Nx != b[0].Nx) return (FALSE);
+  if (a[0].Ny != b[0].Ny) return (FALSE);
+
+  Npix = a[0].Nx*a[0].Ny;
+  ap = (float *) a[0].buffer;
+  bp = (float *) b[0].buffer;
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    *ap -= *bp;
+  }
+  return (FALSE);
+}
+
+statsImage (Image *image, Stats *stats) {
+
+  val = (float *)image[0].buffer;
+  max = min = val[0];
+  Npix = image[0].Nx*image[0].Ny;
+  for (i = 0; i < Npix; i++, val++) {
+    N1 += *val;
+    N2 += (*val)*(*val);
+    max = MAX (max, *val);
+    min = MIN (min, *val);
+  }
+  stats[0].mean  = N1 / Npix;
+  stats[0].sigma = sqrt (N2 / Npix - SQ(stats[0].mean));
+  stats[0].min = min;
+  stats[0].max = max;
+
+  stats[0].median = stats[0].mean;
+  range = MAX (0.5, 0xffff / (max - min));
+  if (range == 0) return ();
+
+  ALLOCATE (hist, int, 0x10000);
+  bzero (hist, 0x10000*sizeof(int));
+
+  val = (float *)image[0].buffer;
+  for (i = 0; i < Npix; i++) {
+    bin = MIN (MAX (0, (*val - min) * range), 0xffff);
+    hist[bin] ++;
+  }
+
+  Nhist = 0;
+  for (i = 0; (i < 0xffff) && (Nhist < 0.5*Npix); i++) 
+    Nhist += hist[i];
+  stats[0].median = i / range + min;
+  free (hist);
+
+  return ();
+}
+
+findStars (Image *image, Stars **stars, int *Nstars, double threshold) {
+
+  /* binarize @ threshold */
+
+  binimage = createImage (image[0].Nx, image[0].Ny);
+
+  Npix = image[0].Nx*image[0].Ny;
+  ap = image[0].buffer;
+  bp = binimage[0].buffer;
+  bzero (bp, Npix*sizeof (short));
+
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    if (*ap > threshold) * bp = 1;
+  }
+
+  clearpix ();
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+      if (binimage.buffer[j*Nx + i]) {
+	status = fillrow (binimage.buffer, Nx, j*Nx, i, &xs, &xe);
+	for (J = j + 1; (J < Ny) && status; J++) {
+	  for (I = xs; !binimage.buffer[J*Nx + I] && (I < xe); I++);
+	  if (binimage.buffer[J*Nx + I]) {
+	    status = fillrow (binimage.buffer, Nx, J*Nx, I, &xs, &xe);
+	  } 
+	}  
+	/* we now have a stack of pixels, convert to a single star */
+	statpix (&x, &y, image[0].buffer, Nx);
+	addstar (x, y);
+	clearpix ();
+      }
+    }
+  }
+}
+
+/* find contiguous trigger pixels in row from starting point */
+
+int fillrow (float *buffer, int Nx, int offset, int sx, int *xs, int *xe) {
+
+  trigger = FALSE;
+  for (i = sx, pix = offset + i; buffer[pix] && (i < Nx); i++, pix++) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xe = i;
+  }
+  for (i = sx - 1, pix = offset + i; (i >= 0) && buffer[pix]; i--, pix--) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xs = i;
+  }
+  return (trigger);
+}
+
+static int Npix = 0;
+static int *Pix = (int *) NULL;
+
+addpix (int pix) {
+  Npix ++;
+  if (Pix == (int *) NULL) {
+    ALLOCATE (Pix, int, MAX (1, Npix));
+  } else {
+    REALLOCATE (Pix, int, MAX (1, Npix));
+  }    
+  Pix[Npix - 1] = pix;
+}
+
+clearpix () {
+  Npix = 0;
+  REALLOCATE (Pix, int, 1);
+}
+
+statpix (double *x, double *y, float *buffer, int Nx) {
+
+  int X, Y;
+  double Sx, Sy, So;
+
+  So = Sx = Sy = 0;
+  for (i = 0; i < Npix; i++) {
+    Y = pix / Nx;
+    X = pix % Nx;
+    So += buffer[pix];
+    Sx += X * buffer[pix];
+    Sy += Y * buffer[pix];
+  }
+  *x = Sx / So;
+  *y = Sy / So;
+}
+
+/* find stars: 
+   - binarize @ threshold
+   - find all contiguous blobs
+   - find geom center of each blob
+*/
+
+
+
+/*
+
+.....................
+....x................
+...xxx...............
+..........xxx..........
+.........xx..........
+.....................
+.....................
+.....................
+.....................
+.....................
+
+
+ */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/camera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/camera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/camera.c	(revision 22322)
@@ -0,0 +1,130 @@
+# include "dimm.h"
+# define EXIT_STATUS(S) { seteuid (UID); return (S); }
+
+static uid_t UID, EUID;
+
+SetEUID () {
+
+  /* save the UID (ID of calling process) and EUID (should be root) */
+  UID = getuid ();
+  EUID = geteuid ();
+  seteuid (UID);
+}
+
+int camera (int argc, char **argv) {
+  
+  /* USAGE: 
+     camera init port
+     camera expose exptime
+     camera readout x y dx dy
+     camera temp set value
+     camera temp get var
+  */
+
+  if (argc < 2) goto usage;
+
+  seteuid (EUID);
+  
+  if (!strcasecmp (argv[1], "init")) {
+    int port, status;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera init (port)\n");
+      EXIT_STATUS (FALSE);
+    }
+    sscanf (argv[2], "%x", &port);
+    status = InitCamera (port);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "expose")) {
+
+    int status;
+    double exptime;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera expose (exptime)\n");
+      EXIT_STATUS (FALSE);
+    }
+    exptime = atof (argv[2]);
+    status = Exposure (exptime);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "temp")) {
+
+    int status;
+    double temp;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera temp (temperature)\n");
+      EXIT_STATUS (FALSE);
+    }
+    temp = atof (argv[2]);
+    status = SetTemperature (temp);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "status")) {
+
+    int status;
+    double temp;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: camera status\n");
+      EXIT_STATUS (FALSE);
+    }
+    DumpCameraStatus ();
+    EXIT_STATUS (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "readout")) {
+
+    int Nbuf, status;
+    double temp;
+    int x, y, dx, dy, NX, NY;
+    Buffer *buf;
+
+    if ((argc != 7) && (argc != 3)) {
+      gprint (GP_ERR, "USAGE: camera readout (buffer) x y dx dy\n");
+      EXIT_STATUS (FALSE);
+    }
+
+    if ((buf = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) EXIT_STATUS (FALSE);
+
+    CameraFullSize (&NX, &NY);
+    x = y = 0;
+    dx = NX;
+    dy = NY;
+    if (argc == 7) {
+      x  = atof (argv[3]);
+      y  = atof (argv[4]);
+      dx = atof (argv[5]);
+      dy = atof (argv[6]);
+    } 
+
+    /* generate a buffer to store the image */
+    gfits_free_matrix (&buf[0].matrix);
+    gfits_free_header (&buf[0].header);
+    CreateBuffer (buf, dx, dy, -32, 0.0, 1.0);
+    strcpy (buf[0].file, "(empty)");
+
+    ReadOut (x, y, dx, dy, 1, buf[0].matrix.buffer);
+
+    gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, gfits_get_unsign_mode());
+
+    EXIT_STATUS (TRUE);
+  }
+
+usage:
+  gprint (GP_ERR, "camera init port\n");
+  gprint (GP_ERR, "camera expose exptime\n");
+  gprint (GP_ERR, "camera readout x y dx dy\n");
+  gprint (GP_ERR, "camera temp set value\n");
+  gprint (GP_ERR, "camera temp get var\n");
+  seteuid (UID);
+  return (FALSE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/dimm.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/dimm.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/dimm.c.in	(revision 22322)
@@ -0,0 +1,68 @@
+# include "opihi.h"
+
+# define opihi_name "DIMM"
+# define opihi_prompt "dimm: "
+# define opihi_description "DIMM telescope controller\n"
+# define opihi_history ".dimm"
+# define opihi_rcfile ".dimmrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitDIMM ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  { 
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreeDIMM ();
+
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/findstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/findstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/findstars.c	(revision 22322)
@@ -0,0 +1,152 @@
+# include "dimm.h"
+
+int fillrow (char *buffer, int Nx, int offset, int sx, int *xs, int *xe);
+int addpix (int pix);
+int clearpix ();
+int freepix ();
+int statpix (float *x, float *y, float *buffer, int Nx);
+int addstar (double x, double y);
+
+Vector *vecx, *vecy, *vecf, *vecn;
+
+int findstars (int argc, char **argv) {
+
+  int i, j, I, J, Npix, Nbuf, status;
+  int xs, xe, Nx, Ny;
+  char *binary, *bp;
+  float *ap, threshold, x, y;
+  Buffer *buf;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: findstars (buffer) (threshold)\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  threshold = atof (argv[2]);
+
+  if ((vecx = SelectVector ("star_x", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector ("star_y", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecf = SelectVector ("star_f", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecn = SelectVector ("star_n", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  vecx[0].Nelements = vecy[0].Nelements = 0;
+  vecf[0].Nelements = vecn[0].Nelements = 0;
+
+  REALLOCATE (vecx[0].elements, float, 1);
+  REALLOCATE (vecy[0].elements, float, 1);
+  REALLOCATE (vecf[0].elements, float, 1);
+  REALLOCATE (vecn[0].elements, float, 1);
+
+  /* binarize @ threshold */
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  Npix = Nx*Ny;
+  ALLOCATE (binary, char, Npix);
+
+  ap = (float *) buf[0].matrix.buffer;
+  bp = binary;
+  bzero (bp, Npix);
+
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    if (*ap > threshold) *bp = 1;
+  }
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+      if (binary[j*Nx + i]) {
+	clearpix ();
+	status = fillrow (binary, Nx, j*Nx, i, &xs, &xe);
+	for (J = j + 1; (J < Ny) && status; J++) {
+	  for (I = xs; !binary[J*Nx + I] && (I <= xe); I++);
+	  status = fillrow (binary, Nx, J*Nx, I, &xs, &xe);
+	}  
+	/* we now have a stack of pixels, find geometric center */
+	statpix (&x, &y, (float *)buf[0].matrix.buffer, Nx);
+      }
+    }
+  }
+  freepix ();
+  return (TRUE);
+}
+
+/* find contiguous trigger pixels in row from starting point */
+int fillrow (char *buffer, int Nx, int offset, int sx, int *xs, int *xe) {
+
+  int i, pix, trigger;
+
+  *xe = *xs = sx;
+  trigger = FALSE;
+  for (i = sx, pix = offset + i; buffer[pix] && (i < Nx); i++, pix++) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xe = i;
+  }
+  for (i = sx - 1, pix = offset + i; (i >= 0) && buffer[pix]; i--, pix--) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xs = i;
+  }
+  return (trigger);
+}
+
+static int Npixlist = 0;
+static int *pixlist = (int *) NULL;
+
+addpix (int pix) {
+  Npixlist ++;
+  if (pixlist == (int *) NULL) {
+    ALLOCATE (pixlist, int, MAX (1, Npixlist));
+  } else {
+    REALLOCATE (pixlist, int, MAX (1, Npixlist));
+  }    
+  pixlist[Npixlist - 1] = pix;
+}
+
+clearpix () {
+  Npixlist = 0;
+  REALLOCATE (pixlist, int, 1);
+}
+
+freepix () {
+  Npixlist = 0;
+  free (pixlist);
+  pixlist = (int *) NULL;
+}
+
+statpix (float *x, float *y, float *buffer, int Nx) {
+
+  int i, X, Y, pix, Nv, No;
+  double Sx, Sy, So;
+
+  So = Sx = Sy = 0;
+  for (i = 0; i < Npixlist; i++) {
+    pix = pixlist[i];
+    Y = pix / Nx;
+    X = pix % Nx;
+    So += buffer[pix];
+    Sx += X * buffer[pix];
+    Sy += Y * buffer[pix];
+  }
+  *x = Sx / So;
+  *y = Sy / So;
+  gprint (GP_ERR, "%f %f  %f %d\n", *x, *y, So, Npixlist);
+
+  No = vecx[0].Nelements;
+  Nv = No + 1;
+  vecx[0].Nelements = vecy[0].Nelements = Nv;
+  vecf[0].Nelements = vecn[0].Nelements = Nv;
+
+  REALLOCATE (vecx[0].elements, float, MAX (Nv, 1));
+  REALLOCATE (vecy[0].elements, float, MAX (Nv, 1));
+  REALLOCATE (vecf[0].elements, float, MAX (Nv, 1));
+  REALLOCATE (vecn[0].elements, float, MAX (Nv, 1));
+
+  vecx[0].elements[No] = *x;
+  vecy[0].elements[No] = *y;
+  vecf[0].elements[No] = So;
+  vecn[0].elements[No] = Npixlist;
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/init.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/init.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/init.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "dimm.h"
+
+int camera          PROTO((int, char **));
+int findstars       PROTO((int, char **));
+int telescope       PROTO((int, char **));
+int version         PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "camera",    camera,    "camera functions"},
+  {1, "findstars", findstars, "find objects on image"},
+  {1, "telescope", telescope, "telescope communications"},
+  {1, "version",   version,   "show version information"},
+}; 
+
+void InitDIMM () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreeDIMM () {
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/sbig/sbig.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/sbig/sbig.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/sbig/sbig.h	(revision 22322)
@@ -0,0 +1,222 @@
+#ifndef _SBIG_H
+#define _SBIG_H
+
+#define VERSION  2.0
+
+struct sbig_init {
+    float linux_version;
+    int	 nmbr_bad_columns;	/* bad columns in imaging CCD */
+    int  bad_columns[4];
+    int  imaging_abg_type;	/* 0 no ABG, 1 ABG present */
+    char serial_number[10];
+    unsigned firmware_version;
+    char  camera_name[64];
+    struct camera_info {
+	int  nmbr_readout_modes;
+	struct readout_mode {
+	    int  mode;
+	    int  width;			/* pixels */
+	    int  height;		/* height */
+	    int  gain;			/* in 0.01 e-/ADU */
+	    unsigned pixel_width;	/* in nanometers */
+	    unsigned pixel_height;	/* in nanometers */
+	} readout_mode[12];
+    } camera_info[2];
+    int ST5_AD_size;
+    int ST5_filter_type;
+};
+extern int  sbig_init(int port, int options, struct sbig_init *);
+
+#define SBIG_IMAGING_CCD 0
+#define SBIG_TRACKING_CCD 1
+
+#define SBIG_CCD_STATUS_IDLE  0
+#define SBIG_CCD_STATUS_IN_PROGRESS  2
+#define SBIG_CCD_STATUS_COMPLETE  3
+
+#define SBIG_CCD_SHUTTER_OPENED  0
+#define SBIG_CCD_SHUTTER_CLOSED  1
+#define SBIG_CCD_SHUTTER_OPENING  2
+#define SBIG_CCD_SHUTTER_CLOSING  3
+
+struct sbig_status {
+    int  imaging_ccd_status;
+    int  tracking_ccd_status;
+    int  fan_on;
+    int  shutter_state;
+    int  led_state;
+    int  shutter_edge;
+    int  plus_x_relay;
+    int  minus_x_relay;
+    int  plus_y_relay;
+    int  minus_y_relay;
+    int  pulse_active;
+    int  temperature_regulation;
+    int  temperature_setpoint;
+    int  cooling_power;
+    int  air_temperature;
+    int  ccd_temperature;
+};
+extern int  sbig_get_status(struct sbig_status *);
+
+
+#define SBIG_LEAVE_SHUTTER  0
+#define SBIG_OPEN_SHUTTER  1
+#define SBIG_CLOSE_SHUTTER  2
+#define SBIG_INITIALIZE_SHUTTER  3
+
+#define SBIG_LED_OFF  0
+#define SBIG_LED_ON  1
+#define SBIG_LED_BLINK_SLOW 2
+#define SBIG_LED_BLINK_FAST 3
+
+struct sbig_control {
+    int  fan_on;
+    int  shutter;
+    int  led;
+};
+extern int  sbig_control(struct sbig_control *);
+
+struct sbig_pulse {
+    int nmbr_pulses;		/* 0 to 255 */
+    int pulse_width;		/* microsec, min 9 */
+    int pulse_interval;		/* microsec, min 27+pulse_width */
+};
+extern int  sbig_pulse(struct sbig_pulse *);
+
+struct sbig_relay {
+    int x_plus_time;
+    int x_minus_time;
+    int y_plus_time;
+    int y_minus_time;
+};
+extern int  sbig_activate_relay(struct sbig_relay *);
+
+
+/*
+ * Shutter control.  0 for no change in shutter (i.e. control of shutter
+ * using SBigControl class), 1 normal shutter (open during exposure,
+ * closed otherwise), 2 shutter closed (for taking dark frames).
+ */
+#define SBIG_EXPOSE_SHUTTER_UNCHANGED  0
+#define SBIG_EXPOSE_SHUTTER_NORMAL  1
+#define SBIG_EXPOSE_SHUTTER_CLOSED  2
+
+struct sbig_expose {
+    int ccd;
+    int exposure_time;
+    int abg_state;
+    int shutter;
+};
+extern int  sbig_expose(struct sbig_expose *);
+
+
+/*
+ *  Terminate the image and read it out.
+ *  Use width == 0 and height == 0 to abort an image with no readout (all
+ *   params other than ccd ignored)
+ */
+#define SBIG_BIN_1X1  0
+#define SBIG_BIN_2X2  1
+#define SBIG_BIN_3X3  2
+
+struct sbig_readout {
+    int ccd;
+    int binning;
+    int x, y;
+    int width, height;
+    unsigned short *data;
+    int data_size_in_bytes;
+    int (*callback)(float percent_complete);
+};
+extern int  sbig_readout(struct sbig_readout *);
+
+#define SBIG_NO_SHUTTER_DELAY  0x0001	/* don't wait for shutter to close */
+struct sbig_readout2 {
+    int flags;				/* see immediately above */
+    int ccd;
+    int binning;
+    int x, y;
+    int width, height;
+    unsigned short *data;
+    int data_size_in_bytes;
+    int (*callback)(float percent_complete);
+};
+extern int  sbig_readout2(struct sbig_readout2 *);
+
+
+#define SBIG_TEMP_REGULATION_OFF  0
+#define SBIG_TEMP_REGULATION_ON  1
+#define SBIG_TEMP_REGULATION_DIRECT_DRIVE  2
+
+/*
+ * Antiblooming gate control values
+ */
+#define SBIG_ABG_OFF  0
+#define SBIG_ABG_LOW  1
+#define SBIG_ABG_MEDIUM  2
+#define SBIG_ABG_HIGH  3
+
+struct sbig_cool {
+    int regulation;		/* 0 off, 1 on, 2 direct_drive */
+    int temperature;		/* in 0.1 deg C, if 'on' */
+    int direct_drive;		/* power [0..255], direct_drive */
+};
+extern int  sbig_set_cooling(struct sbig_cool *);
+
+extern int  sbig_set_ao7_deflection(int x_deflection, int y_deflection);
+
+extern int  sbig_set_ao7_focus(int type);
+
+#define SBIG_AO7_FOCUS_SOFT_CENTER 4
+#define SBIG_AO7_FOCUS_HARD_CENTER  3
+#define SBIG_AO7_FOCUS_STEP_TOWARD_SCOPE 2
+#define SBIG_AO7_FOCUS_STEP_FROM_SCOPE 1
+
+/*
+ *	Return Error Codes
+ *
+ *	These are the error codes returned by the driver
+ *	function.  They are prefixed with CE_ to designate
+ *	them as camera errors.
+ *
+ *      The return codes from the sbig_xxx() routines will
+ *      be the NEGATIVE of these on an error.
+ *
+ */
+#define SBIG_CAMERA_NOT_FOUND	-1
+#define SBIG_EXPOSURE_IN_PROGRESS -2
+#define SBIG_NO_EXPOSURE_IN_PROGRESS -3
+#define SBIG_UNKNOWN_COMMAND	-4
+#define SBIG_BAD_CAMERA_COMMAND	-5
+#define SBIG_BAD_PARAMETER	-6
+#define SBIG_TX_TIMEOUT		-7
+#define SBIG_RX_TIMEOUT		-8
+#define SBIG_NAK_RESBIGIVED	-9
+#define SBIG_CAN_RESBIGIVED	-10
+#define SBIG_UNKNOWN_RESPONSE	-11
+#define SBIG_BAD_LENGTH		-12
+#define SBIG_AD_TIMEOUT		-13
+#define SBIG_CHECKSUM_ERROR	-14
+#define SBIG_EEPROM_ERROR	-15
+#define SBIG_SHUTTER_ERROR	-16
+#define SBIG_UNKNOWN_CAMERA	-17
+#define SBIG_DRIVER_NOT_FOUND	-18
+#define SBIG_DRIVER_NOT_OPEN	-19
+#define SBIG_DRIVER_NOT_CLOSED	-20
+#define SBIG_SHARE_ERROR	-21
+#define SBIG_TSBIG_NOT_FOUND	-22
+#define SBIG_NEXT_ERROR		-23
+#define SBIG_NOT_ROOT		-24
+char *sbig_show_error(int);
+
+/*
+ * IO strategy flags
+ */
+#define SBIG_DISABLE_INTERRUPTS 0x001
+#define SBIG_LOCK_ALL	0x002
+#define SBIG_SHORT_DELAYS 0x004
+#define SBIG_DEFAULT_STRATEGY (SBIG_DISABLE_INTERRUPTS|SBIG_LOCK_ALL)
+void sbig_set_linux_strategy(int);
+
+#endif /* S_BIG_H */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/sbig/sp_ccdcontrol.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/sbig/sp_ccdcontrol.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/sbig/sp_ccdcontrol.c	(revision 22322)
@@ -0,0 +1,472 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "sbig.h"
+#include "fh/fh.h" /* CFHT FITS Handling library */
+
+// XXX RHL claims this is not needed
+// #include <values.h>
+
+#define N_FITS_ENTRIES 33
+
+/*
+ * The following hack is needed if you want to use sbig.a w/ libc5
+ */
+#ifndef __bzero
+void __bzero(void* s, int n)
+{
+  memset(s, 0, n);
+}
+#endif
+
+static struct sbig_init info;
+
+static int write_fits(int width, int height, double etime, int binmode, unsigned short *data);
+
+static struct sbig_readout readout;
+
+/*
+ * This is the 'progress' callback
+ * passed to sbig_readout()
+ */
+static int readout_callback(float percent)
+{
+  gprint (GP_ERR, "progress: Reading %d x %d pixels (%d%%)\r",
+	  readout.width, readout.height, (int)percent);
+  /* return 1 to continue, 0 to abort */
+  return 1;
+}
+
+/*
+ *  Start an exposure, wait for it, then download from the camera
+ *  in bands.  This is somewhat complex, since it handles any size
+ *  subimage of the CCD, and bands too.
+ */
+static void take_picture(double etime, int binmode, int shuttermode, int x, int y, int w, int h)
+{
+  int  ret, i, needed;
+  static unsigned short  *buffer;
+  static int  buffer_size;
+  struct sbig_expose  expose;
+  struct sbig_status  status;
+  double  f;
+  
+  expose.ccd = 0; /* Always in imaging mode, not tracking 
+		    since CFHT PF does the tracking*/
+
+  if(binmode<0 || binmode>3){
+    gprint (GP_ERR, "Imaging binmode options: [0=1x1, *1=2x2, 2=3x3]\n");
+    exit(EXIT_FAILURE);
+  }
+  readout.binning=binmode;
+  
+  readout.x=x;
+  readout.y=y;
+  readout.width=w;
+  readout.height=h;
+  if (readout.width == 0) {
+    readout.width =
+      info.camera_info[expose.ccd].readout_mode[readout.binning].width - x;
+  } else
+    if (readout.width + x >
+	info.camera_info[expose.ccd].readout_mode[readout.binning].width) {
+    gprint (GP_ERR, "Raster X or Width parameter out of range.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  if (readout.height == 0) {
+    readout.height =
+      info.camera_info[expose.ccd].readout_mode[readout.binning].height - y;
+  } else
+    if (readout.height + y >
+	info.camera_info[expose.ccd].readout_mode[readout.binning].height) {
+    gprint (GP_ERR, "Raster Y or Height parameter out of range.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  f = etime;
+  expose.exposure_time = (int)(100.0*f);
+  expose.abg_state = SBIG_ABG_OFF; /* SBIG_ABG_MEDIUM */
+
+  expose.shutter = shuttermode;  /* No shutter present */
+
+  sync();
+  ret = sbig_expose(&expose);
+  if (ret < 0)
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+  
+  gprint (GP_ERR, "progress: Sleeping for %g seconds\r", 0.01*expose.exposure_time);
+
+  if (expose.exposure_time > 100) {
+    for (i = 0; i < expose.exposure_time; i += 100) {
+      sbig_get_status(&status);
+      gprint (GP_ERR, "progress: Exposure in progress; ccd_status=%d, shutter=%d (%d%%)\r",
+	      status.imaging_ccd_status, status.shutter_state,
+	      i * 100 / expose.exposure_time);
+      sleep(1);
+    }
+  } else
+    usleep(expose.exposure_time*10000);
+  
+  /* wait for exposure to complete */
+  gprint (GP_ERR, "progress: Waiting for controller                             (100%%)\r");
+
+  for (i = 0; i < 1000; ++i) {
+    ret = sbig_get_status(&status);
+    if (ret < 0) {
+      gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+      break;
+    }
+    if (status.imaging_ccd_status != 3)
+      gprint (GP_ERR, "logonly: ccd_status=%d\n", status.imaging_ccd_status);
+
+    if (readout.ccd == 0) {
+      if (status.imaging_ccd_status != 2)
+	break;
+    } else {
+      if (status.tracking_ccd_status != 2)
+	break;
+    }
+    usleep(50000);
+  }
+  if (i)
+    gprint (GP_ERR, "warning: Exposure took an extra %g seconds\n", 0.050*i);
+
+  if (i == 1000)
+    gprint (GP_ERR, "error: EXPOSURE DIDN'T FINISH!\n");
+  else
+    gprint (GP_ERR, "progress: Exposure complete                                  (100%%)\n");
+
+  readout.ccd = expose.ccd;
+  readout.binning = readout.binning;
+  needed = readout.height*readout.width*sizeof(short);
+  if (buffer_size < needed) {
+    if (buffer != NULL)
+      free(buffer);
+    buffer = malloc(buffer_size = needed);
+  }
+  readout.data = buffer;
+  readout.data_size_in_bytes = buffer_size;
+  readout.callback = readout_callback;
+    
+  sync();
+  if ((ret = sbig_readout(&readout)) < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+    return;
+  }
+
+  gprint (GP_ERR, "progress: Writing FITS data to stdout\n");
+
+  if(write_fits(readout.width, readout.height, etime, binmode, buffer)==-1)
+    gprint (GP_ERR, "error: Could not write FITS file to stdout.\n");
+  gprint (GP_ERR, "progress: sp_ccdcontrol done.\n");
+}
+
+static int write_fits(int w, int h, double etime, int binmode, unsigned short *data)
+{
+  HeaderUnit hu;
+  register int i;
+  double datamin = DBL_MAX, datamax = DBL_MIN;
+  unsigned short u;
+  char str[80]; /* For building string FITS headers */
+  time_t date;
+
+  int ret; /* Return value for sbig_status */
+  struct sbig_status sbstat; /* Status structure */
+  
+  /* Get status to add to FITS header */
+  sync();
+  ret = sbig_get_status(&sbstat);
+
+  if (ret < 0) {
+    gprint (GP_ERR, "error: sbig_get_status failed: %s\n", sbig_show_error(ret));
+    exit(EXIT_FAILURE);
+  }
+
+  /* shouldn't we scale this to fit, first? */
+  for (i = w*h; --i >= 0; )
+    {
+      u = data[i];
+      if (u > datamax) datamax = u;
+      if (u < datamin) datamin = u;
+      data[i] = ((u<<8)|(u>>8)) ^ 0x0080; /* -32768 */
+    }
+
+  time(&date);
+
+  hu = fh_create();
+  fh_reserve(hu, 50); /* Reserve space for 50 cards (TCS, Elixir?, etc.) */
+  fh_set_bool(hu, 0., "SIMPLE", 1, "Standard FITS");
+  fh_set_int(hu, 1.0, "BITPIX", 16,"16-bit data");
+  fh_set_int(hu, 2.0, "NAXIS", 2,  "Number of axes");
+  fh_set_int(hu, 2.1, "NAXIS1", w, "Number of pixel columns");
+  fh_set_int(hu, 2.2, "NAXIS2", h, "Number of pixel rows");
+  fh_set_int(hu, 5.0, "PCOUNT",	0, "No 'random' parameters");
+  fh_set_int(hu, 6.0, "GCOUNT",	1, "Only one group");
+
+  strftime(str, sizeof(str)-1, "%Y-%m-%dT%T", gmtime(&date));
+  fh_set_str(hu, 104, "DATE", str, "UTC Date of file creation");
+  strftime(str, sizeof(str)-1, "%a %b %d %H:%M:%S %Z %Y", localtime(&date));
+  fh_set_str(hu, 104.1, "HSTTIME", str, "Local time in Hawaii");
+
+  fh_set_str(hu, 105, "ORIGIN", "CFHT", "Canada-France-Hawaii Telescope");
+
+  fh_set_flt(hu, 141.,"BZERO",	32768.0, 6,	"Zero factor");
+  fh_set_flt(hu, 142.,"BSCALE",	1.0, 2, "Scale factor");
+  fh_set_flt(hu, 150, "DATAMIN", datamin, 6, "Minimum value of the data");
+  fh_set_flt(hu, 151, "DATAMAX", datamax, 6, "Maximum value of the data");
+  fh_set_flt(hu, 160, "SATURATE", 4016.0, 6, "Saturation value");
+
+  fh_set_flt(hu, 220, "EXPTIME", etime, 5, "Integration time (seconds)");
+  sprintf(str, "%d %d", binmode + 1, binmode + 1);
+  fh_set_str(hu, 230, "CCDSUM", str, "Binning factors");
+  fh_set_int(hu, 231, "CCDBIN1", binmode + 1, "Binning factor along first axis");
+  fh_set_int(hu, 232, "CCDBIN2", binmode + 1, "Binning factor along second axis");
+
+  fh_set_com(hu, 1400.0, "COMMENT", "");
+  fh_set_com(hu, 1400.1, "COMMENT", " SBIG status record:");
+  fh_set_com(hu, 1400.2, "COMMENT", "");
+  if (sbstat.imaging_ccd_status==0)
+    fh_set_str(hu, 1500, "DETSTAT", "ok", "(SBIG imaging_ccd_status is 0)");
+  else
+    fh_set_int(hu, 1500, "DETSTAT", sbstat.imaging_ccd_status, "SBIG error!");
+  fh_set_flt(hu, 1500.1, "DETTEM", sbstat.ccd_temperature/10., 3, "Detector temperature");
+  fh_set_int(hu, 1500.2, "DETTEMRG", sbstat.temperature_regulation, "1=set temp 0=set power");
+  fh_set_int(hu, 1500.3, "DETTEMSP", sbstat.temperature_setpoint, "Temperature setpoint");
+  fh_set_int(hu, 1500.4, "DETTEMCP", sbstat.cooling_power, "Cooling power");
+  fh_set_flt(hu, 1510,  "CAMTEM", sbstat.air_temperature/10., 3, "Outside air temperature");
+  fh_set_com(hu, 1600.0, "COMMENT", "");
+  fh_set_com(hu, 1600.1, "COMMENT", " SBIG info record:");
+  fh_set_com(hu, 1600.2, "COMMENT", "");
+  sprintf(str, "%u", info.firmware_version);
+  fh_set_str(hu, 1600.9, "CONSWV", str, "SBIG firmware version");
+  fh_set_str(hu, 1601,"DETECTOR", info.camera_name, info.serial_number);
+  fh_set_bool(hu, 1700, "ABGON", info.imaging_abg_type, "Antiblooming gate present");
+  fh_set_int(hu, 1701, "ABGMODE", info.imaging_abg_type, "Antiblooming gate control value");
+  fh_set_int(hu, 1800, "BADCOL", info.nmbr_bad_columns, "Number of bad columns");
+  for (i = 0; i < info.nmbr_bad_columns; ++i){
+    sprintf(str, "BADCOL%d", i);
+    fh_set_int(hu, 1800 + 0.1 + 0.1 * i, str, info.bad_columns[i], "Bad column");
+  }
+  fh_set_int(hu, 1901, "ST5ADSIZ", info.ST5_AD_size, "");
+  fh_set_int(hu, 1902, "ST5FLTYP", info.ST5_filter_type, "");
+
+  if (fh_write(hu, STDOUT_FILENO) != FH_SUCCESS)
+    exit(EXIT_FAILURE);
+
+  if (fh_write_padded_image(hu, STDOUT_FILENO, data, w*h*sizeof(unsigned short), FH_TYPESIZE_RAW) != FH_SUCCESS)
+    exit(EXIT_FAILURE);
+
+  gprint (GP_ERR, "logonly: Wrote %d pixels, min=%g, max=%g\n", w*h, datamin, datamax);
+  sync();
+  return 0;
+}
+
+static void show_format(void){
+  gprint (GP_ERR, "Format for sp_ccdcontrol, SkyProbe CCD controller:\n"
+	  "Writes FITS file to stdout, status messages to stderr.\n"
+	  "NOTE: -port {hex} is an option for all forms of sp_ccdcontrol,\n"
+	  "  to specify a hex adress for the parallel port.\n"
+	  "sp_ccdcontrol expose -etime {Exp time}\n"
+          "   [-binmode {Binning mode}]\n"
+          "   [-shuttermode {Shutter mode}]\n"
+          "   [-raster X Y W H]\n"
+	  "Exp time: Exposure time given in seconds.\n"
+	  "Binning mode: 0=1x1, 1=2x2, 2=3x3.  Note, 3x3 not allowed in -track mode.\n"
+	  "   OR \n"
+	  "sp_ccdcontrol cool {coolmode} [temp|power]\n"
+	  "coolmode: 0=OFF, 1=ON, 2=Direct Drive\n"
+	  "For coolmode=0: Do not specify temp or power\n"
+	  "For coolmode=1: Specify temp in deg C\n"
+	  "For coolmode=2: Specify power (0-255)\n");
+  exit(EXIT_FAILURE);
+}
+
+
+int main(int argc, char *argv[])
+{
+  /*    char buffer[80], last_command[80]; */
+  int  ret = -1, i, port;
+  int  is_small_camera;
+    
+  /* Simon Kras--command line variables */
+  char cmd[80];
+  double etime;
+  int binmode;
+  int shuttermode;
+  int coolmode;
+  int raster_x, raster_y, raster_h, raster_w;
+  double cooltemp, coolpwr;
+
+  if(argc>1) strcpy(cmd, argv[1]);
+  else show_format();
+
+  port = 1; /* 0x378 */
+  for (i = 1; i < argc; ++i) {
+    if (!strcmp(argv[i], "-port")) {
+      sscanf(argv[++i], "%x", &port);
+      gprint (GP_ERR, "port override to 0x%x\n", port);
+    }
+  }
+
+  for (i = 0; i < 4; ++i) {
+    sync();
+    /* set our verbosity to 0 (range is 0 to 5) */
+    ret = sbig_init(port, 0, &info);
+    if (ret == -6) ret = 0; /* %%% It always returns this error, but actually it worked.
+			     * Since the library is binary only, we can't fix it.
+			     */
+    if (ret == 0) break;
+    gprint (GP_ERR,"logonly: sbig_init() failed: %s\n", sbig_show_error(ret));
+    usleep(100000);
+  }
+  if (ret < 0) {
+    gprint (GP_ERR, "error: cannot open camera, giving up\n");
+    exit(EXIT_FAILURE);
+  }
+
+  is_small_camera = (strstr(info.camera_name, "237") != NULL ||
+		     index(info.camera_name, '5') != NULL);
+  for (i = 0; i < info.camera_info[0].nmbr_readout_modes; ++i) {
+    struct readout_mode  *rmp;
+    rmp = &info.camera_info[0].readout_mode[i];
+#ifdef DEBUG
+    gprint (GP_ERR, "debug: ImagingCCD mode %d: w=%d h=%d "
+	    "gain=%f "
+	    "pw=%g ph=%g\n", rmp->mode, rmp->width,
+	    rmp->height,  0.01*rmp->gain, 0.001*rmp->pixel_width, 
+	    0.001*rmp->pixel_height);
+#endif
+  }
+
+  for (i = 0; i < info.camera_info[1].nmbr_readout_modes; ++i) {
+    struct readout_mode  *rmp;
+
+    rmp = &info.camera_info[1].readout_mode[i];
+#ifdef DEBUG
+    gprint (GP_ERR, "debug: TrackingCCD mode %d: w=%d h=%d "
+	    "gain=%f "
+	    "pw=%g ph=%g\n"
+	    , rmp->mode, rmp->width, rmp->height,
+	    0.01*rmp->gain,
+	    0.001*rmp->pixel_width, 0.001*rmp->pixel_height);
+#endif
+  }
+  if(!strcmp(cmd, "expose")){
+    etime=-999.;
+    for(i=1;i<argc;++i){
+      if(!strcmp(argv[i], "-etime")){
+	if(argc>(i+1)) etime = atof(argv[++i]);
+	else
+	  gprint (GP_ERR, "No argument given to -etime\n");
+      }
+    }
+    if(etime==-999.){
+	gprint (GP_ERR, "Must specify exposure time.\n");
+	exit(EXIT_FAILURE);
+      }
+      
+      binmode=-999;
+      for(i=1;i<argc;++i){
+	if(!strcmp(argv[i], "-binmode")){
+	  if(argc>(i+1)) binmode = atoi(argv[++i]);
+	  else{
+	    gprint (GP_ERR, "No argument given for -binmode\n");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+      }
+      if(binmode==-999) binmode=0;
+
+      shuttermode=0;
+      for (i=1; i<argc;++i){
+	if (!strcmp(argv[i], "-shuttermode")){
+	  if (argc>(i+1)) shuttermode = atoi(argv[++i]);
+	  else{
+	    gprint (GP_ERR, "No argument given for -shuttermode\n");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+      }
+
+      raster_x=0;
+      raster_y=0;
+      raster_w=0;
+      raster_h=0;
+      for (i=1; i<argc;++i){
+	if (!strcmp(argv[i], "-raster")){
+	  if (argc>(i+4)) {
+	    raster_x = atoi(argv[++i]);
+	    raster_y = atoi(argv[++i]);
+	    raster_w = atoi(argv[++i]);
+	    raster_h = atoi(argv[++i]);
+	  }
+	  else{
+	    gprint (GP_ERR, "-raster requires 4 arguments: x y w h\n");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+      }
+      
+      take_picture(etime, binmode, shuttermode, raster_x, raster_y, raster_w, raster_h);
+
+  } else if(!strcmp(cmd, "cool")){
+    double  f;
+    struct sbig_cool  cool;
+    
+    if(argc<3){
+      gprint (GP_ERR, "Too few arguments for cool mode\n");
+      exit(EXIT_FAILURE);
+    } 
+    coolmode = atoi(argv[2]);
+    
+    if(coolmode<0 || coolmode>2){
+      gprint (GP_ERR, "Cooling mode values allowed: [0 Off, 1 *On, 2 DirectDrive]\n");
+      exit(EXIT_FAILURE);
+    }
+    cool.regulation = coolmode;
+    cool.direct_drive = 0;
+    if (cool.regulation == 2){
+      if(argc<4){
+	gprint (GP_ERR, "Must specify cooling power (0-255)\n");
+	exit(EXIT_FAILURE);
+      }
+      coolpwr=atof(argv[3]);
+      if(coolpwr<0 || coolpwr>255){
+	gprint (GP_ERR, "Cooling power must be 0-255.\n");
+	exit(EXIT_FAILURE);
+      }
+      cool.direct_drive = coolpwr;
+    }
+    else if (cool.regulation == 1) {
+      if(argc<4){
+	gprint (GP_ERR, "Must specify temperature in deg C\n");
+	exit(EXIT_FAILURE);
+      }
+      cooltemp=atof(argv[3]);
+      if(cooltemp<-50 || cooltemp>20){
+	gprint (GP_ERR, "Cooling temperature must be -50 to 20 deg C.\n");
+	exit(EXIT_FAILURE);
+      }
+      f = cooltemp;
+      i = 10.0*f + 0.5;
+      cool.temperature = i;
+    }
+    sync();
+    ret = sbig_set_cooling(&cool);
+    if (ret < 0){
+      gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+    }
+  }else{
+    gprint (GP_ERR, "Illegal command: '%s'\n", cmd);
+    show_format();
+  }
+  exit(EXIT_SUCCESS);
+}
+ 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/telescope.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/telescope.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/telescope.c	(revision 22322)
@@ -0,0 +1,236 @@
+# include "dimm.h"
+
+double distSky (double r1, double r2, double d1, double d2);
+
+int telescope (int argc, char **argv) {
+  
+  if (argc < 2) goto usage;
+
+  if (!strcasecmp (argv[1], "init")) {
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope init (port)\n");
+      return (FALSE);
+    }
+    if (!SerialInit (argv[2])) return (FALSE);
+    gprint (GP_ERR, "telescope on port %s\n", argv[2]);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "cmd")) {
+
+    int status;
+    char *answer = (char *) NULL;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope cmd (string)\n");
+      return (FALSE);
+    }
+    status = SerialCommand (argv[2], &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "ack")) {
+
+    int status;
+    char line[32], *answer;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope cmd (string)\n");
+      return (FALSE);
+    }
+    line[0] = 0x06;
+    line[1] = 0;
+    status = SerialCommand (line, &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "slew")) {
+
+    double ra, dec;
+    int status;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope slew (ra) (dec)\n");
+      return (FALSE);
+    }
+
+    ra  = atof (argv[2]);
+    dec = atof (argv[3]);
+    while (ra  < 360.0) ra += 360.0;   
+    while (ra  > 360.0) ra -= 360.0;   
+
+    status = gotoRD (ra, dec);
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "coords")) {
+
+    int status;
+    double ra, dec;
+    char line[64];
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope coords\n");
+      return (FALSE);
+    }
+
+    if (!getRD (&ra, &dec)) return (FALSE);
+    gprint (GP_ERR, "%f %f\n", ra, dec);
+    set_variable ("RA", ra);
+    set_variable ("DEC", dec);
+    dms_format (line, (ra/15.0));
+    set_str_variable ("Rs", line);
+    dms_format (line, dec);
+    set_str_variable ("Ds", line);
+
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "altaz")) {
+
+    int status;
+    double x, y;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope altaz\n");
+      return (FALSE);
+    }
+
+    if (!getXY (&x, &y)) return (FALSE);
+    gprint (GP_ERR, "%f %f\n", x, y);
+    set_variable ("ALT", x);
+    set_variable ("AZ", y);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "site")) {
+
+    int status;
+    double lon, lat, LST;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope site\n");
+      return (FALSE);
+    }
+
+    if (!getSite (&lon, &lat, &LST)) return (FALSE);
+    gprint (GP_ERR, "%f %f  %f\n", lon, lat, LST);
+    set_variable ("LON", lon);
+    set_variable ("LAT", lat);
+    set_variable ("LST", LST);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "setsite")) {
+
+    double lon, lat;
+
+    if (argc != 5) {
+      gprint (GP_ERR, "USAGE: telescope setsite (name) (longitude) (latitude)\n");
+      return (FALSE);
+    }
+
+    lon = atof (argv[3]);
+    lat = atof (argv[4]);
+    if (!setSite (argv[2], lon, lat)) return (FALSE);
+    set_variable ("LON", lon);
+    set_variable ("LAT", lat);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "settime")) {
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope settime (lst)\n");
+      return (FALSE);
+    }
+
+    if (!setTime (argv[2])) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "verbose")) {
+
+    int mode;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope verbose (mode)\n");
+      return (FALSE);
+    }
+
+    mode = atoi (argv[2]);
+    SerialVerbose (mode);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "setcoords")) {
+
+    int status;
+    double ra, dec;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope setcoords (ra) (dec)\n");
+      return (FALSE);
+    }
+
+    ra  = atof (argv[2]);
+    dec = atof (argv[3]);
+
+    if (!setRD (ra, dec)) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "offset")) {
+
+    int status;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope offset (direction) (distance)\n");
+      gprint (GP_ERR, "  direction : x or y\n");
+      gprint (GP_ERR, "  distance  : +arcmin or -arcmin\n");
+      return (FALSE);
+    }
+    status = offset (argv[2], atof (argv[3]));
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "toffset")) {
+
+    int status;
+
+    if (argc != 5) {
+      gprint (GP_ERR, "USAGE: telescope toffset (direction) (rate) (duration)\n");
+      gprint (GP_ERR, "example: telescope toffset x RC 1.5\n");
+      return (FALSE);
+    }
+    status = toffset (argv[2], argv[3], atof(argv[4]));
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+ usage:
+  gprint (GP_ERR, "telescope init port - set serial port (eg, /dev/ttyS0)\n");
+  gprint (GP_ERR, "telescope site - get site information\n");
+  gprint (GP_ERR, "telescope altaz - g\n");
+  gprint (GP_ERR, "telescope setcoords (ra) (dec)\n");
+  gprint (GP_ERR, "telescope setsite (name) (longitute) (latitude)\n");
+  gprint (GP_ERR, "telescope coords\n");
+  gprint (GP_ERR, "telescope slew (ra) (dec)\n");
+  gprint (GP_ERR, "telescope offset (direction) (duration)\n");
+  gprint (GP_ERR, "telescope toffset (direction) (rate) (duration)\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dimm/version.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "dimm.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pclient version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,207 @@
+
+2006.10.04:
+  opihi-2-8    added output names, gprintGetName, '-current' to 'output'
+	       removed prototypes from old 'output' code
+	       fixed memory leak in evaluate_stack
+	       added global PID variable
+	       added quiet mode to coords
+	       added various memleak tests, other tests
+	       fixed error in buffer/vector selection for dimenup
+	       fixed counting error in fit2d (missing 1 row of matrix!)
+	       added normalization to kern
+	       cleaned CreateBuffer function, usage
+	       
+  mana-1-6     removed prototypes from old 'output' code
+	       cleaned CreateBuffer function, usage
+
+  pantasks-0-6 added TASK_STDOUT, TASK_STDERR to pantasks.h
+	       added stdout, stderr output targets for tasks/jobs
+	       cleaned CreateBuffer function, usage
+
+  dvo-0-6      cleaned CreateBuffer function, usage
+
+2006.08.23:
+  opihi-2-7    converted to gfits APIs (forces libfits 1.6)
+	       consolidated style argument parsing
+	       added style arguments to astro plotting
+	       added support for pantasks_client, pantasks_server
+	       added more complex keys to queue operations
+	       added gprint to allow buffered printing in threads
+	       updates to test framework for more flexibility
+	       added time conversions sec_to_hms, sec_to_day
+	       added SetLimitsRaw
+	       added isort_pair
+	       added VarConfigEntry
+	       added strip_version to clean version output
+	       moved gaussj to libohana
+	       moved main out of opihi to mana.c, etc
+
+  mana-1-5     converted to gfits APIs (forces libfits 1.6)
+	       added option for threaded shell/backend (not used)
+
+  dvo-0-5      converted to gfits APIs (forces libfits 1.6)
+      	       consolidated style argument parsing
+	       converted to new DVO APIs (dvo_catalog_xxx)
+	       added fitsed
+	       improvements to fitcolors
+	      
+  pantasks-0-5 converted to gfits APIs (forces libfits 1.6)
+	       added 'option' task command
+	       added limits on Njobs for time ranges
+	       fixed time ranges
+	       created client/server version
+	       added controller run/stop commands
+	       added controller help
+	       added server help
+
+  pcontrol-0-5 converted to gfits APIs (forces libfits 1.6)
+	       code re-org to make thread safe
+	       added multithread option (user/client threads)
+	       liberal use of ASSERTs
+
+  pclient-0-5  converted to gfits APIs (forces libfits 1.6)
+  dimm-0-4     converted to gfits APIs (forces libfits 1.6)
+
+2006.03.26:
+  opihi-2-6 : added 'close' function for devices
+	      added test framework  
+	      added tests to cmd.basic, cmd.data
+	      added $?var syntax (variable existence test)
+	      fixed broken a[3] = 5 assignment
+	      added PopQueueMatch function
+	      added periodogram.c
+	      improvements to dimendown, peak
+	      added fixwrap for roll-over on saturation
+
+  dvo-0-4     added proper-motion, parallax
+	      dropped _PS from Stars.R,D
+	      fixed configuration flags for readline/curses
+
+  mana-1-4     fixed configuration flags for readline/curses
+  pcontrol-0-4 fixed configuration flags for readline/curses
+  pclient-0-4  fixed configuration flags for readline/curses
+
+  pantasks-0-4 fixed configuration flags for readline/curses
+	       changed name from psched
+
+
+
+- opihi-2-5
+  * major work to switch to libkapa
+  * revised init sequence (general_init, program_init, startup)
+
+- mana 1.3
+  * changes to support new init sequence
+
+- dvo 0.3
+  * major changes to support SkyRegions
+  * changes to support libkapa
+  * changes to support new init sequence
+
+- psched 0.3
+  * changes to support new init sequence
+  * changes to support libkapa
+
+- pcontrol 0.3
+  * changes to support new init sequence
+  * changes to support libkapa
+
+- pclient 0.3
+  * changes to support new init sequence
+  * changes to support libkapa
+
+- dimm 0.2
+  * changes to support new init sequence
+  * changes to support libkapa
+
+dimm-0-1: release 2005.10.20
+  first tagged dimm release
+
+pclient-0-2: release 2005.10.20
+  minor cleanups
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+pcontrol-0-2: release 2005.10.20
+  added run/stop commands
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+psched-0-2: release 2005.10.20
+  work to improve / test throughput
+  minor cleanups
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+dvo-0-2:  release 2005.10.20
+  substantial changes to work with new dvo load functions
+  support for mode/format changes
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+mana-1-2:  release 2005.10.20
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+opihi-2-4: release 2005.10.20
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+  kapa/kii cursor interaction modified (needs kapa v1.3, kii v1.3)
+
+opihi-2-3: release 2005.08.15
+  first tagged release tarball
+  fixed up versions commands
+  added list types for psched 
+  moved to Makefile.Common method.
+
+opihi-2-2:
+  fixed up inclusion of dvo.h (conflicted names)
+  added Graph commands functions
+  added mosaic astrometry support
+  a variety of memory alloc bugfixes
+  added support for ohana_allocate functions
+  added real multicommand support (correct parsing of variables)
+  added some object modelling functions
+  added clipping to fits and stats
+  added gaussdeviate
+  added extrapolation to minterp
+  default of EXTNAME for extension keyword
+  added -cpt to various dvo functions
+  better help in dvo functions
+  fixed up lockfile APIs
+  added fitcolors
+  added lightcurve
+  some cleanups in dvo/photometry.c
+  added typefrac tests is photometry.c
+  added gain test for mrqmin
+  fixed up precedence in math
+  macro-ized binary/unary math ops
+  added modulo, erf
+
+opihi-2-1:
+  major reorg of imfit / added imfit functions
+  cleaned up get_argument calls so -Wall does not give errors
+  moved the 'elixir' command to dvo
+  tag added only to elixir implementations (mana + dvo)
+  dimm - need to coordinate with Olivier and Bernt
+  dvo2, pcontrol, pclient, sched - all under development
+
+2004.12.23
+
+  Moved typename/modename funcs to libohana.  this was motivated by
+  imregister, which would not compile cleanly using the old method.
+
+  Added library dependencies to the makefiles. previously, mana (etc)
+  would not re-build if the library was modified.
+
+  Fixed incorrect exit on non-tty.  this kept perl scripts with the
+  call "|mana" from running.
+
+  Fixed dvomath for strings.  since we moved to dvomath everywhere,
+  this broke complex string conditionals: (($a == hi) && ($b == lo)).
+  I added string types to the math functions, and tests to see if the
+  stack has strings which cannot be resolved as a vector or matrix.  
+
+2004.12.10:
+
+  fixed problem in lib.data/PlotVectors.c and lib.data/graphtools.c
+  that broke plotting for data with very large or small exponents (|x|
+  < 1e-12, |x| > 1e12).  also fixed cmd.data/vstats.c with same
+  problem.
+	
+opihi-2-0:
+  import to CVS 2004.12.03
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/changes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/changes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/changes.txt	(revision 22322)
@@ -0,0 +1,54 @@
+Oct 19  2003 misc/startup.c              . minor changes, DONE
+Oct 19  2003 misc/conversions.c		 . minor changes, DONE
+Oct 19  2003 misc/match_image.c		 . copied to dvo
+Apr 19 17:41 misc/ConfigInit.c		 . minor changes, kept dvo version
+May  6 16:13 misc/ImageOps.c		 . minor changes, DONE
+May 12 09:00 misc/photometry.c           . major changes : DONE
+
+Oct 15  2003 user/badimages.c            . no change
+Oct 15  2003 user/imlist.c		 . no change
+Oct 15  2003 user/precess.c		 . dvo version ok
+Oct 15  2003 user/delete.c		 . dvo version ok
+
+May  6 15:25 user/extract.c		 . mostly deprecated (transfer USNO/HST/etc versions)
+May 10 21:13 user/mextract.c		 . used status version + vectors
+May 11 12:06 user/avextract.c		 . used status version + vectors
+Oct 27  2003 user/imextract.c		 . used status version + vectors
+
+May 12 09:06 user/dmags.c		 . used status version + vectors
+May 12 09:12 user/dmagmeas.c		 . used status version + vectors
+May 12 09:13 user/dmagaves.c		 . used status version + vectors
+May 11 16:18 user/ddmags.c		 . used status version + vectors
+
+May  2 11:14 user/calextract.c		 . used status version + vectors 
+May  3 20:08 user/calmextract.c		 . used status version + vectors
+Oct 19  2003 user/cmd.c			 . fixed (photcodes vs vectors)
+Oct 19  2003 user/ccd.c			 . used status version + vectors
+
+Apr 19 19:48 user/gimages.c		 . minor changes, fixed
+Nov 14  2003 user/gstar.c		 . minor changes, fixed
+May  3 20:49 user/gtypes.c		 . dvo version ok
+May  6 09:37 user/images.c		 . copied to dvo
+May  3 20:49 user/imdata.c		 . dvo version ok
+May  3 20:50 user/imphot.c		 . dvo version ok
+May  3 20:51 user/imrough.c		 . dvo version ok
+May  3 20:51 user/imsearch.c		 . dvo version ok
+Nov  5  2003 user/photcodes.c		 . copied to dvo
+Nov  1  2003 user/pmeasure.c		 . dvo version ok
+Mar  3 11:25 user/showtile.c		 . copied to dvo
+Nov  1  2003 user/subpix.c		 . copied to dvo
+Oct 30  2003 user/dmt.c			 . minor changes & photcodes
+Nov  1  2003 user/lcurve.c		 . minor changes & photcodes
+
+May 18 09:50 user/cgrid.c		 . copied to dvo
+
+Nov  1  2003 user/abszero.c		 . deprecated
+May  3 20:51 user/zeropts.c		 . deprecated
+Nov 13  2003 user/resid.c		 . deprecated
+Nov 13  2003 user/photresid.c		 . deprecated
+
+May  2 11:20 user/cals.c		 . deprecated?
+May  3 20:37 user/ddmagextract.c	 . deprecated?
+May  3 20:11 user/dmagextract.c		 . deprecated?
+Oct 19  2003 user/ccdextract.c		 . deprecated?
+Oct 19  2003 user/cmdextract.c		 . deprecated?
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/command.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/command.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/command.txt	(revision 22322)
@@ -0,0 +1,24 @@
+
+readline: allocates a line and returns it; empty lines return NULL
+
+expand_vars: (recursively) expand expressions of the form $X
+	     frees the input line, allocates a new one.
+	     returns NULL on error (including NULL input)
+
+expand_vectors: expand expressions of the form $X
+	     frees the input line, allocates a new one.
+	     returns NULL on error (including NULL input)
+
+parse performs math expansion on the line, returning a new line (old one is freed)
+
+
+in newmath:
+
+isolate_elements converts argc,argv to cstack,Ncstack (argc,argv NOT freed)
+convert_to_RPN takes cstack,Nstack and converts to stack,Nstack; input cstack,Ncstack are freed
+check_stack puts pointers to data types on stack, no data allocated
+evaluate_stack 
+
+
+command parses a single input line (line) and expands
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/coords.list
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/coords.list	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/coords.list	(revision 22322)
@@ -0,0 +1,47 @@
+./cmd.astro/cgrid.c - OK (graph only)
+./cmd.astro/cplot.c  - OK (graph only)
+./cmd.astro/czplot.c - OK (graph only)
+./cmd.data/cursor.c - OK (graph only)
+./dvo/catalog.c - OK (graph only)
+./dvo/pcat.c - OK (graph)
+./dvo/pmeasure.c - OK (graph)
+./dvo/procks.c - OK (graph)
+./dvo/showtile.c - OK (graph)
+./cmd.astro/coords.c - fixed
+./dvo/gimages.c - fixed
+./dvo/ImageOps.c - fixed
+./dvo/images.c - fixed
+./dvo/imdense.c - fixed
+./dvo/imextract.c - fixed
+./dvo/imlist.c - fixed
+./dvo/imstats.c - fixed
+./dvo/photometry.c - fixed
+./dvo/subpix.c - fixed
+./dvo/get_regions.c - fixed
+
+./cmd.astro/drizzle.c - needs work
+./cmd.astro/transform.c - needs work
+./cmd.data/load.c - needs work
+./cmd.data/point.c - needs work
+./cmd.data/tvgrid.c - needs work
+./dvo/cmpread.c - needs work (single image)
+./dvo/imbox.c - needs work (single image)
+./dvo/simage.c - needs work (single image)
+
+dvo commands / functions affected by WRP / DIS astrometry
+
+LoadImage - no
+badimages.c - yes : also, use LoadImages
+get_regions.c - ? : OK if we register DIS image
+gimages.c
+ImageOps.c        : need to make association with DIS
+images.c
+imbox.c
+imdense.c
+imextract.c
+imlist.c
+imstats.c
+photometry.c
+showtile.c
+simage.c
+subpix.c
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dmagoptions.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dmagoptions.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dmagoptions.txt	(revision 22322)
@@ -0,0 +1,143 @@
+
+mextract from value [options]
+avextract from value [options]
+
+imextract value [options]
+
+dmags F1 - F2 : F3 [options]
+dmagmeas F1 - F2 : value [options]
+dmagaves F1 - F2 : value [options]
+
+cmd F1 - F2 : F3 [options]
+ccd F1 - F2 : F3 - F4 [options]
+
+calextract F1 - F2 [options]
+calmextract F1 - F2 [options]
+
+options:
+
+ -photcode value
+  for avextract and dmagaves, -photcode defines the which code is used
+  to calculate certain values (mag, dmag, etc).  objects which lack a 
+  photcode return a logical NaN for that value.
+
+ -magrange min max
+
+this can apply to any photcode
+
+ -imaglim min 
+ -flag value
+ -time start stop
+ -fwhm (value)
+ -type (value)
+
+these naturally apply to the REF and DEP photcodes, but cannot apply to 
+to PRI and SEC.  (note that -type is interpretted a bit differently here
+from below. 
+
+ -chisq max
+ -errorlim max
+ -type (value)
+ -typefrac (type) (frac)
+ -nphot (value)
+ -ncode (value)
+ -fwhmfrac (value) (frac)
+
+these naturally apply to PRI and SEC photcodes, but cannot apply to
+REF.  For DEP, they could apply to the equivalent photcode.  eg
+
+  ccd I - 2MASS_J : 2MASS_J - 2MASS_K -chisq 2.0
+
+  this will exclude on chisq for the I value, and ignore the limit
+  for the 2MASS values
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -chisq 2.0
+
+  this will exclude on chisq for the I value, and ignore the limit
+  for the 2MASS values, and exclude on R (equiv to CFH12K.R.00).
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -time start stop
+
+  by default, this will filter the CFH12K.R.00 and 2MASS measurements 
+  based on
+
+we need to be able to restrict the selections to a subset of the
+extraction values.  We can use a flag like: -apply YYNN:
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -time start stop -apply NYNN
+
+to restrict the application to only the CFH12K.R.00 entry.
+
+measure values:
+  ra
+  dec
+  mag
+  dmag
+  airmass
+  exptime
+  photcode
+  time
+  dR
+  dD
+  fwhm
+  dophot
+  FLAGS
+  XCCD
+  YCCD
+  XMOSAIC
+  YMOSAIC
+
+average values:
+  ra
+  dec
+  dmag
+  Nmeas
+  Nmiss
+  Xp
+  Xm
+  flag
+  type
+  typefrac
+  Nphot
+  Ncode
+
+
+
+- ExtractDMag
+  - ExtractMeasuresDMag
+    - TestAverage
+  - ExtractMagnitudes
+    - ExtractAverages
+    - ExtractMeasures
+
+mextract
+ - ExtractMeasures
+avextract
+ - ExtractAverages
+dmags
+ - ExtractDMag
+ - ExtractMagnitudes
+ddmags
+ - ExtractDMag
+ - ExtractDMag
+dmagmeas
+ - ExtractDMag
+ - ExtractMeasures
+dmagaves
+ - ExtractDMag
+ - ExtractAverages
+cmd
+ - ExtractDMag
+ - ExtractMagnitudes
+ccd
+ - ExtractDMag
+calextract
+ - ExtractAverages
+calmextract
+ - ExtractMeasures
+
+
+cmd B - V : B
+dmagmeas B - V : ra
+dmagmeas B - V : time
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dvo-2.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dvo-2.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dvo-2.txt	(revision 22322)
@@ -0,0 +1,28 @@
+
+Region APIs
+
+Region table is a FITS table
+
+- load region data from FITS table
+- save region data to FITS table
+
+/* find region which overlaps c at given depth (-1 : max depth) */
+SkyRegion *SkyFindPoint (SkyRegion *db, SkyCoord c, int depth);
+
+/* find regions at all levels which overlap c */
+SkyRegion **SkyFindLevels (SkyRegion *db, SkyCoord c, int *Nregion);
+
+/* find regions contained within rectangular region  c1 - c2 */
+SkyRegion **SkyFindArea (SkyRegion *db, SkyCoord c1, SkyCoord c2, *nlist);
+
+- I have created table generation functions which construct a
+  collection of table entries starting with the full sky and iterating
+  down N levels.  The function also fills out the names at the
+  appropriate levels.  This is currently ok up to level 6, but runs
+  out of memory at level 7.  This should not be the case, so I suspect
+  I'm not free'ing some memory.  
+
+- I need to create a function to take the GSC table list and construct
+  a set of SkyRegions that match the layout, starting with the full
+  sky and iterating down to and past the GSC tables.  
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dvo-apis.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dvo-apis.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dvo-apis.txt	(revision 22322)
@@ -0,0 +1,89 @@
+
+skyregion APIs:
+
+// a single patch on the sky
+typedef struct {
+  float            Rmin;                 // 
+  float            Rmax;                 // 
+  float            Dmin;                 // 
+  float            Dmax;                 // 
+  int              childS;               // sequence number in full table of first child
+  int              childE;               // sequence number in full table of last child + 1
+  int              parent;               // sequence number in full table of parent
+  int              index;                // sequence number in full table of this entry
+  char             depth;                // depth of this entry
+  char             child;                // does this entry have children?
+  char             table;                // does this entry have a table?
+  char             name[21];             // name / filename
+} SkyRegion;
+
+/* SkyRegion : better implementation than GSCRegion */
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion *regions;
+} SkyTable;
+
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion **regions;
+} SkyList;
+
+SkyList *SkyRegionByPoint (SkyTable *table, int depth, double ra, double dec);
+SkyList *SkyListByName (SkyTable *table, char *name);
+SkyList *SkyListByPoint (SkyTable *table, double ra, double dec);
+SkyList *SkyListByRadius (SkyTable *table, int depth, double RA, double DEC, double radius);
+SkyList *SkyListByPatch (SkyTable *table, int depth, SkyRegion *patch);
+SkyList *SkyListByImage (SkyTable *table, int depth, Image *image);
+SkyList *SkyListByBounds (SkyTable *table, int depth, double Rmin, double Rmax, double Dmin, double Dmax);
+SkyList *SkyListChildrenByBounds (SkyTable *table, int No, int depth, double Rmin, double Rmax, double Dmin, double Dmax);
+void SetImageCorners (double *X, double *Y, Image *image);
+int SkyTableSetDepth (SkyTable *sky, int depth);
+int SkyListFree (SkyList *list, int ELEMENTS);
+int SkyTableFree (SkyTable *table);
+int SkyListMerge (SkyList **outlist, SkyList *newlist);
+
+--- db query sky region ---
+
+// options for selecting the ra,dec limits of the db selections
+typedef struct {
+  char *name;
+  char *list;
+  int useDisplay;
+  int useSkyregion;
+} SkyRegionSelection;
+
+int get_skyregion (double *Rs, double *Re, double *Ds, double *De);
+int set_skyregion (double Rs, double Re, double Ds, double De);
+
+void FreeSkyRegionSelection (SkyRegionSelection *selection);
+SkyRegionSelection *SetRegionSelection (int *argc, char **argv);
+
+
+--- photometry.c api ---
+
+* int GetTimeSelection (time_t *tz, time_t *te) {
+int GetPhotcodeInfo (char *string, PhotCode **Code, int *Mode) {
+int SetSelectionParam (int param) {
+int GetSelectionParam () {
+int GetMeasureParam (char *parname) {
+int GetAverageParam (char *parname) {
+int TestPhotSelections (PhotCode **code, int *mode, int param) {
+void GetAverageParamHelp () {
+int InitPhotcodes () {
+int ListPhotSelections () {
+int SetPhotSelections (int *argc, char **argv, int Nparams) {
+double *ExtractMeasures (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+double ExtractAverages (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param) {
+double DetermineTypefrac (Average *average, Measure *measure, PhotCode *code) {
+int DetermineTypeCode (Average *average, Measure *measure, int code) {
+int GetMeasureTypeCode (Measure *measure) {
+int Quality (Measure *measure, int IsDophot) {
+int TestAverage (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+double *ExtractMagnitudes (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *n) {
+double *ExtractDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist) {
+double *ExtractMeasuresDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist) {
+double *ExtractByDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+double *ExtractMeasuresByDMag (PhotCode **code, int *mode, int use_first, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+double GetMeasure (int param, Average *average, Measure *measure, double mag) {
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dvo.html
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dvo.html	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/dvo.html	(revision 22322)
@@ -0,0 +1,288 @@
+
+<h2> PA data requirements </h2>
+
+Counting the total size of the PA database for the PA survey, with
+roughly 20 pointings per location and 30 second exposures.  The depth
+for PA survey in P2 @ 20 \sigma is 21.3 (r).  Each detection uses 100
+bytes (<b>we need to flesh this out with more realistic numbers from
+our table of parameters</b>).  The access speed to a RAID disk is 100
+MB/sec.  The number density of stars in i' in the plane may be higher,
+but we reach saturation at ~1.4e6 detections per deg^2 (one object per
+100 pixels).  This is a factor of 5 higher than the raw numbers.
+
+<h3> stellar counts for PS-1 </h3>
+latitude                 90          30         0
+density (deg^{-2})      5e3         3e4       3e5
+N_det (FPA^{-1})        4e4         2e5       2e6
+Sum N_det (FPA^{-1})    8e5         4e6       4e7
+Sum Nbyte (FPA^{-1})  80 MB      400 MB      4 GB
+Nsec for 1 channel      0.8           4        40
+Nchannel for 2 sec        1           2        20
+
+The total number of bytes for the PA survey for P2 detections is ~12
+TB (30000 x 400MB).  The density of detections per FPA from P4 delta
+is roughly the same as P2 at 0 deg (2e5 vs 3e5 det deg^{-2}).  The
+total number of bytes needed to store the P4 Delta detections from all
+of PS-1 is 
+
+Fields in the plane will take longer to process
+
+<h2> addstar interactions for a distributed db </h2>
+
+addstar.client <---> addstar.daemon
+
+The sky is divided into hierarchical regions, each broken into smaller
+subregions.  Both the image and object tables are divided into
+subtables by region on the sky.  The density of image tables is
+smaller than the density of object tables.  A top-level table defines
+the distribution of the lower-level tables by defining the hierarchy
+of regions and subregions.  Each entry in this table contains:
+
+region ID  - an identifier for the subregion
+RAs, RAe   - RA range of region
+DECs, DECe - DEC range of region
+parent ID  - ID of the region which contains this region (NULL for allsky)
+Nchild     - number of children
+offset     - starting entry of first child
+images     - is this region used for images? (if FALSE, down one layer)
+objects    - is this region used for objects? (if FALSE, down one layer)
+
+use the machine name / file name as the indicator in the images and
+objects entries?
+
+The table defines the relationship between the subregions and provides
+a mechanism to find the subregions appropriate to a given sky
+location.  It also specified for a given depth if that depth is used
+for the image and/or the object table.  The image tables contain
+images whose reference coordinate is located in the given region.
+Other regions which the image overlaps contain entries in the image
+reference table which specify the primary image table in which they
+are stored.  
+
+The details of the sky region definitions do not matter for the
+structure of the region table.  The data structures can handle any
+arrangement of tables which meets the basic requirements that the
+boundaries be lines of constant RA & DEC and that each level defines
+all regions which cover the entire sky.  One implementation is as
+follows and is easy to generate:  
+
+Start with the complete sky as a single region (RAs = 0, RAe = 360;
+DECs = -90, DECe = 90).  To create a new region, always subdivide the
+region with the largest area (roughly (RAe - RAs)*(DECe -
+DECs)*cos((DECs + DECe)/2)).  To subdivide a region, define two
+possible subdivisions: RA = 0.5*(RAs + RAe) (which becomes RAs and RAe
+for the two new regions) or DEC = 0.5*(DECs + DECe).  Determine the
+length of these dividing lines (S1 = (DECe - DECs); S2 = (RAe - RAs) /
+cos (DEC)).  Choose the shorter of these two lines, and subdivide the
+region on that basis.  
+
+The number of subregions increases by a factor of 8 for
+each new level.  Approximate table size: Need at least 200,000
+lowest-level regions, leads to 2^18 regions (256k) in lowest-level.
+Total table size is roughly 300k rows.  Each row is roughly 32 bytes,
+for a table size of 10MB.
+
+There is one addstar.daemon per DVO server.  The addstar.daemons are
+responsible for serving the objects and images from the tables they
+contain.  
+
+addstar.client steps to load a new set of objects:
+- load region table (defined in config db)
+- find overlapping object regions
+- find overlapping image regions
+- identify primary image region
+- send image data to node for primary image region 
+- send image reference to nodes for secondary image regions
+- send detection data (with image ID) to corresponding object region nodes
+
+addstar.daemon responsibilities:
+- receive image data -> add to primary image table
+- receive detection data -> add to local region file
+- request objects in overlap regions from neighbor daemons
+- send detections to neighbor daemons if associated with object over
+  border
+- construct / update objects on basis of detections
+
+in DVO 1.0, addstar updates objects on every upload of the
+detections.  In fact, the construction of the objects need not be
+performed every time detections are added.  Do we need to construct
+objects for all single detections?  The current process is:
+
+- get list of new detections (from incoming image)
+- match each detection with each object
+- update object parameters
+
+another option:
+
+step 1:
+ - get list of new detections
+ - add to new.detection table
+
+step 2:
+ - match detections to object table
+ - update object parameters
+
+third option
+step 1:
+ - add new detections to new table
+
+step 2:
+ - compare detections to orphans 
+ - if found, promote to object
+ - if not, test against objects
+ - if found update object
+ - if not add to orphans
+
+<h2> DVO image organization </h2>
+
+DVO 1.0 uses a single image table to store all image data.  In this
+table, each image is a chip; that is, each entry represents a single
+astrometric system.  In principal, there is no reason this could not
+be an entire mosaic or (in the case of Pan-STARRS) an individual
+Cell. It is usually necessary to be able to define the relationship
+between the different detector / focal plane / etc coordinate
+systems.  If the data entity is a full mosaic, then it will be
+necessary to look up the chip (and cell) transformations.  If the data
+entity is the cell, the reverse conversions will be needed.  
+
+The image table should be distributed to multiple files to speed up
+the access.  One option is to do this by coordinate region.  In the
+plan for DVO 2.0, there will be a table of the region hierarchy for
+the object tables, and the image tables could be distibuted similarly,
+though with a different density.  In PS-1, for example, there will be
+around 250,000 images (FPAs).  If we store OTAs (chips) as the data
+entity in the image table, then we will have in the vicinity of 16M
+rows.  If each contains 256 bytes, the total data volume in the image
+table will be about 4 GB.  If we want to have typical access times to
+any image of 1 second, and we need to scan through the entire table to
+get to the image, then we will need to distribute the data across 40
+tables (note that they need not be distributed by machine).  If each
+table represents a region on the sky, this translates to 1000 - 500
+square degrees per table.  
+
+Some additional aspects are interesting.  First, the
+spatially-distributed tables correspond to specific regions on the
+sky, likely to be bounded by lines of constant RA and DEC, or
+something equivalent.  However, an image cannot be guaranteed to land
+in only one of these regions.  To mitigate this, each image (chip or
+FPA or whatever the data unit is) should have a single defined
+position with which the general location is identified and the choice
+of region is made.  In addition, there should be a table associated
+with each region which defines images in other tables (regions) which
+overlap the given region.  In general, these overlap tables will
+contain only a small fraction of the image entries (regions are much
+large than images) and they need only identify the image ID and the
+corresponding table.
+
+An additional accelerator table would include all images, their
+reference time and their reference coordinate, sorted by time, with an
+index for the name.
+
+The end result is three types of tables: images.db, overlaps.db,
+imagetimes.db.  images.db contains the bulk of the information (256
+byte / row).  overlaps.db contains only the image ID and the the RA
+and DEC of the reference position (not the actual table because that
+may change). imagetimes.db contains only image ID (8 byte? 16 byte?),
+time, RA, DEC, and and index.  This is a total of about 40 bytes per
+image, for a total of roughly 700 MB at the end of PS-1.
+
+<h3> Examples queries </h3>
+
+<h4> find a single, specific image by ID </h4>
+<ul>
+<li> open imagetimes.db
+<li> load block marker (ID,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+<li> determine appropriate image.db table (region)
+<li> open image.db 
+<li> load block marker (ra,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+</ul>
+
+<h4> find a single, specific image by ID </h4>
+<ul>
+<li> open imagetimes.db
+<li> load block marker (ID,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+<li> determine appropriate image.db table (region)
+<li> open image.db 
+<li> load block marker (ra,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+</ul>
+
+<h3> dvo / object catalog scaling to massive collections </h3>
+
+DVO divides the sky into tables which represent specific areas on the
+sky.  Currently, these are pre-defined to match the HST GSC regions,
+roughly 1 - 4 square degree patches with fixed RA and DEC boundaries.
+A future extension will provide a mechanism to increaese or decrease
+the table density on the fly.
+
+The likely data rate under Pan-STARRS PS-1 is in the vicinity of 5 x
+10<sup>5</sup> stars per square degree.  The density is likely to vary
+by a factor of 30 for the bulk of the sky.  The Pan-STARRS camera
+consists of 1.44 x 10<sup>8</sup> pixels.  The confusion limit will
+likely be reached when each object encompases 25 pixels, resulting in
+a saturation of 6 x 10<sup>6</sup> stars per square degree.  The
+Pan-STARRS camera covers a total of 7.45 square degrees, and the
+expected exposure rate is roughly 1 per minute on average.  This
+amounts to a total of 3000 square degrees covered per night of
+observation, or a total of 1.5 x 10<sup>9</sup>detections per night,
+or a total of 5 x 10<sup>11</sup> over the course of one year.  Given
+this total number of detections, and the total sky coverage of 30,000
+square degrees, the average number of detections per square degree
+will be in the vicinity of 1.7 x 10<sup>7</sup>
+
+There are two main limitations to the DVO object storage model.
+First, the large data volume translates to a finite time to read the
+data from the disk.  Second, the large number of objects limits the
+rate at which new detections may be associated with the existing
+objects.  The first of these has generally proven to be the more
+significant limitation in applications to date.  We can easily
+calculate the time needed to load the data relevant to a random image
+pointing, as well as the maximum time based on the range of data
+volumes.  A necessary assumption in this calculation is the number of
+bytes used per detection.  We generously assume 100 bytes per
+detection, 3 times the existing data structures used by DVO.  
+
+Based on the numbers above, the total data volume represented by a
+year of observations is 50 TB, assuming the detection component
+dominates the total.  The average camera footprint translates to
+roughly 13 GB worth of detections.  Since each footprint is performed
+once per minute, it will be necessary to perform the read and write in
+each 10 seconds.  This in turn corresponds to 1.3 GB per second for
+the read and write portions.  With typical local hard-drive access
+speeds for RAIDs of 100 MB/sec, the data will need to be distributed
+across more than the machines in order to achieve these average
+rates.  
+
+The existing DVO system expects only one object table to be open at a
+time.  In addition, access to the tables is not controlled through
+separate machines.  Rather, a single collection of tables is expected
+and all queries are performed independently.  In order to handle the
+above load with the DVO software, the following changes will be
+needed:
+
+<ul>
+<li> tables must be distributed across multiple machines.  This is
+mandatory regardless of the underlying database engine based on the
+data I/O analysis above.
+
+<li> the 'addstar' front end needs to send the data for each table to
+the database backends on each of the corresponding machines.  
+
+<li> the addstar update for a single image can be done in series as is
+currently done.
+
+<li> write locking should be done on the tables rather than on the
+database as a whole (currently using the image table). 
+</ul>
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/ippc_commandserver.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/ippc_commandserver.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/ippc_commandserver.c	(revision 22322)
@@ -0,0 +1,166 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include "ippc.h"
+#include "ippc_messages.h"
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include "ippc_command.h"
+
+#define MAX_KIDS    256
+#define WAIT_FOR_KIDS    5
+
+/** \file ippc_commandserver.c
+ * \brief This is basically a big fat security problem.
+ */
+
+/* global variables */
+int    kids = 0;
+
+void  decrement_kids(int sig_num);
+
+int main(int argc, char *argv[])
+{
+  int    server_sockfd, client_sockfd, server_len, client_len;
+  int    result;
+  char    buffer[STRLEN], *portString;
+  uint16_t  netSafe;
+  uint32_t  auth_token;
+  unsigned short  status;
+  struct  sockaddr_in server_address;
+  struct  sockaddr_in client_address;
+
+  if (IPPC_DEFAULT_VERBOSITY > 1) {
+    gprint (GP_ERR, "%s : THIS PROGRAM _IS_ A REMOTE EXPLOIT!!!\n", argv[0]);
+  }
+
+  if (!(portString = getenv("IPPC_COMMANDSERVER_PORT"))) {
+    portString = "1035";
+  }
+
+  server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
+        if (server_sockfd == 0) {
+                perror("socket");
+                exit(EXIT_FAILURE);
+        }
+
+  server_address.sin_family = AF_INET;
+  server_address.sin_addr.s_addr = htonl(INADDR_ANY);
+  server_address.sin_port = htons(atoi(portString));
+  server_len = sizeof(server_address);
+
+  result = bind(server_sockfd, (struct sockaddr *)&server_address, server_len); 
+        if(result == -1) {
+                perror("bind");
+                exit(EXIT_FAILURE);
+        }
+
+  result = listen(server_sockfd, 10);
+        if(result == -1) {
+                perror("listen");
+                exit(EXIT_FAILURE);
+        }
+  
+  /* decrement the kiddie count after a child exits */
+  signal(SIGCHLD, decrement_kids);
+
+  /* flush dem pesky chars outa my buffer */
+  memset(buffer, 0, sizeof(buffer));
+
+  while(1) {
+    if (IPPC_DEFAULT_VERBOSITY > 1) {
+      gprint (GP_ERR, "%s : pid: %d - waiting for connection...\n", argv[0], getpid());
+    }
+
+    if (kids < MAX_KIDS) {
+      client_len = sizeof(client_address);
+      client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);
+    } else {
+      if (IPPC_DEFAULT_VERBOSITY > 1) {
+        gprint (GP_ERR, "%s : pid: %d - kids: %d - child limit already reached, sleeping %ds...\n",
+          argv[0], getpid(), kids, WAIT_FOR_KIDS);
+      }
+      sleep(WAIT_FOR_KIDS);
+      continue;
+    }
+
+    /* increment kiddie count before forking */
+    kids++;
+
+    if (fork() == 0) {
+      if (IPPC_DEFAULT_VERBOSITY > 1) {
+        gprint (GP_ERR, "%s : pid: %d - new kid number: %d\n", argv[0], getpid(), kids);
+      }
+
+      /* a non-zero result doesn't always mean an error occured
+       * I need to find out more about how to handle error checking here
+       */
+
+      /* receive and check authentication key */
+      recv(client_sockfd, &auth_token, sizeof(auth_token), 0);
+
+      if (ntohl(auth_token) != COMMAND_KEY) {
+        if (IPPC_DEFAULT_VERBOSITY > 1) {
+          gprint (GP_ERR, "%s : pid: %d - invalid authentication key\n", argv[0], getpid());
+        }
+
+        netSafe = htons(-1);
+        send(client_sockfd, &netSafe, sizeof(status), 0);
+
+        close(client_sockfd);
+        exit(EXIT_FAILURE);
+      }
+
+      /* receive command */
+      recv(client_sockfd, buffer, STRLEN, 0);
+  
+      if (IPPC_DEFAULT_VERBOSITY > 1) {
+        gprint (GP_ERR, "%s : pid: %d - recieved: %s\n", argv[0], getpid(), buffer);
+      }
+  
+      /* oh man is this dangerous */
+      status = system(buffer);
+      if (status != 0) {
+        if (IPPC_DEFAULT_VERBOSITY > 1) {
+          gprint (GP_ERR, "%s : pid: %d - command failed - code: %d\n", argv[0], getpid(), status);
+        }
+        netSafe = htons(status);
+        send(client_sockfd, &netSafe, sizeof(netSafe), 0);
+      } else {
+        if (IPPC_DEFAULT_VERBOSITY > 1) {
+          gprint (GP_ERR, "%s : pid: %d - command successful\n", argv[0], getpid());
+        }
+        netSafe = htons(status);
+        send(client_sockfd, &netSafe, sizeof(status), 0);
+      }
+
+      status = shutdown(client_sockfd, SHUT_RDWR);
+      if (status) {
+        perror("shutdown");
+        exit(EXIT_FAILURE);
+      }
+  
+      close(client_sockfd);
+      exit(EXIT_SUCCESS);
+    } else {
+      close(client_sockfd);
+    }
+  }
+}
+
+void  decrement_kids(int sig_num)
+{
+  /* sysv requires the handler to be reinstalled */
+  signal(SIGCHLD, decrement_kids);
+
+  /* reap child exit value */
+  while (waitpid(-1, NULL, WNOHANG) > 0) { kids--; }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/libs.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/libs.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/libs.txt	(revision 22322)
@@ -0,0 +1,17 @@
+
+I have divided the (non-user) functions into classes. 
+
+- conversions can be placed in a single library
+
+- graph and image (tv) functions can be placed in a library
+
+- the opihi shell functions can go into a third library
+
+- the dvo-related functions can go into a fourth
+
+- i need to clean up the mana and dimm misc functions, and put them in libraries 
+
+- the user functions also need to go into some libraries.
+
+- how do I have the library add its functions to the command list?
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/mana.updates.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/mana.updates.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/mana.updates.txt	(revision 22322)
@@ -0,0 +1,135 @@
+
+mana functions which existed in DVO from status:
+
+applyfit2d.c         : OK - vector changes only
+applyfit.c	     : OK - vector changes only
+box.c		     : OK - no diff
+center.c	     : OK - no diff
+clear.c		     : OK - no diff
+concat.c	     : OK - vector changes only
+contour.c	     : mana version writes to Ximage, dvo version writes to vectors.  mana/contour -> tvcontour.c
+create.c	     : OK - buffer changes only
+cursor.c	     : OK - buffer & minor changes only
+delete.c	     : OK - buffer & minor changes only
+device.c	     : OK - no diff
+extract.c	     : mana extract operates on buffers / status extract is db operation
+file.c		     : OK - no diff
+fit2d.c		     : OK - vector changes only
+fit.c		     : OK - added weights and parameter errors from mana version
+gaussj.c	     : OK - buffer changes only
+grid.c		     : mana version write to Ximage, dvo version writes to Xgraph. mana/grid -> tvgrid
+histogram.c	     : OK - vector changes only
+interpolate.c	     : OK - vector changes only
+jpeg.c		     : OK - no diff
+labels.c	     : OK - no diff
+limits.c	     : OK - vector & minor changes only
+list_buffers.c	     : OK - buffer changes only
+list_vectors.c	     : OK - vector & minor changes only
+mcreate.c	     : OK - buffer changes only
+mget.c		     : OK - buffer changes only
+mset.c		     : OK - buffer changes only
+ps.c		     : OK - no diff
+rd.c		     : OK - buffer changes only
+read_vectors.c	     : OK - included reading vectors from fits tables
+region.c	     : OK - no diff
+resize.c	     : OK - no diff
+section.c	     : OK - no diff
+set.c		     : OK - mana version out of date
+sprintf.c	     : OK - no diff
+stats.c		     : OK - included threshold comparison for IgnoreValue and min == max test
+style.c		     : OK - mana version out of date
+subset.c	     : OK - mana version out of date
+textline.c	     : OK - no diff
+tv.c		     : OK - buffer changes only
+uniq.c		     : OK - vector changes only
+vcontour.c	     : mana/vcontour equiv to dvo/contour
+vstat.c		     : OK - vector changes only
+wd.c		     : OK - buffer changes only
+write_vectors.c	     : OK - vector changes only
+zap.c		     : OK - buffer changes only
+zplot.c		     : OK - vector changes only
+
+mana functions not previously in DVO from status:
+
+accum.c			: fix vectors
+adc.c			: keep - specialized
+biassub.c		: fix buffers and vectors
+clip.c			: fix buffers and vectors
+cmpload.c		: keep
+coords.c		: fix buffers and vectors
+cut.c			: fix buffers and vectors
+cval.c			: ? superceeded by 'star'?
+demux.c			: keep - specialized
+dimendown.c		: fix buffers and vectors
+dimenup.c		: fix buffers and vectors
+drawline.c		: keep - unused
+drizzle.c		: fix buffers and vectors
+erase.c			: keep
+fft1d.c			: fix buffers and vectors
+fft2d.c			: fix buffers and vectors
+flux.c			: fix buffers and vectors
+gauss.c			: fix buffers and vectors
+gaussfit.c		: ? to correct?
+getchr.c		: keep
+getvel.c		: 
+imfit.c			: 
+imhist.c		: 
+integrate.c		: 
+kern.c			: 
+keyword.c		: 
+line.c			: 
+list_header.c		: 
+load.c			: 
+medacc.c		: 
+medianmap.c		: 
+memory.c		: 
+minterpolate.c		: 
+mkgauss.c		: 
+multifit.c		: 
+nextract.c		: 
+objload.c		: 
+outline2.c		: ??
+outline.c		: ??
+pause.c			: 
+peak.c			: 
+point.c			: 
+polar.c			: 
+profile.c		: 
+rdseg.c			: 
+rebin.c			: 
+roll.c			: 
+rotate.c		: 
+rotcurve.c		: 
+save.c			: 
+scale.c			: 
+select.c		: 
+sexigesimal.c		: 
+shape.c			: 
+shift.c			: 
+simsignal.c		: 
+sort_vectors.c		: 
+spec.c			: 
+spline_apply.c		: 
+spline_construct.c	: 
+star.c			: 
+strchr.c		: 
+strlen.c		: 
+substr.c		: 
+svd.c			: 
+swapbytes.c		: 
+testfit.c		: 
+transform.c		: 
+unsign.c		: 
+usleep.c		: 
+vbin.c			: 
+vclip.c			: 
+
+outdated or unneeded functions:
+
+dummy.c
+downdimen.c
+buftovec.c
+kernel.c
+updimen.c		: 
+stupidtest.c		: 
+plot2.c			: fixed GetColor in dvo/plot.c
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/notes.dvo-data.html
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/notes.dvo-data.html	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/notes.dvo-data.html	(revision 22322)
@@ -0,0 +1,166 @@
+
+<h2> data structures in DVO </h2>
+
+DVO uses several data structures, defined in <tt>loneos.h</tt> to
+represent data in the database tables.  This document attempts to
+describe the actual usage, some of which may disagree with the comment
+entries in <tt>loneos.h</tt>.
+
+<b> Image </b>
+<table> 
+<tr><th> name               </th><th> type           </th><th> value                     </th><th> units              </th>
+<tr><td> coords             </td><td> Coords         </td><td> astrometry                </td><td>                    </td>
+<tr><td> tzero              </td><td> unsigned int   </td><td> readout time row 0        </td><td> unix time          </td>
+<tr><td> nstar              </td><td> unsigned int   </td><td> number of stars on image  </td><td>                    </td>
+<tr><td> secz               </td><td> short int      </td><td> airmass                   </td><td> 1000*airmass       </td>
+<tr><td> NX                 </td><td> short int      </td><td> image size                </td><td> pixels             </td>
+<tr><td> NY                 </td><td> short int      </td><td> image size                </td><td> pixels             </td>
+<tr><td> apmifit            </td><td> short int      </td><td> aperture correction       </td><td> 1000*mag           </td>
+<tr><td> dapmifit           </td><td> short int      </td><td> error                     </td><td> 1000*mag           </td>
+<tr><td> source             </td><td> short int      </td><td> photcode                  </td><td>                    </td>
+<tr><td> Mcal               </td><td> short int      </td><td> relphot correciton        </td><td> 1000*mag           </td>
+<tr><td> dMcal              </td><td> short int      </td><td> error                     </td><td> 1000*mag           </td>
+<tr><td> Xm                 </td><td> short int      </td><td> image chi-square          </td><td> 100*log(chisq)     </td>
+<tr><td> name[32]           </td><td> char           </td><td> image name                </td><td>                    </td>
+<tr><td> detection_limit    </td><td> unsigned char  </td><td>                           </td><td> 10*mag             </td>
+<tr><td> saturation_limit   </td><td> unsigned char  </td><td>                           </td><td> 10*mag             </td>
+<tr><td> cerror             </td><td> unsigned char  </td><td> astrometric error         </td><td> 50*arcsec          </td>
+<tr><td> fwhm_x             </td><td> unsigned char  </td><td> average FWHM X            </td><td> 25*arcsec          </td>
+<tr><td> fwhm_y             </td><td> unsigned char  </td><td> average FWHM Y            </td><td> 25*arcsec          </td>
+<tr><td> trate              </td><td> unsigned char  </td><td> drift rate                </td><td> 10000*sec/pix      </td>
+<tr><td> exptime            </td><td> float          </td><td> exposure time             </td><td> seconds            </td>
+<tr><td> code               </td><td> char           </td><td> data flags                </td><td>                    </td>
+<tr><td> ccdnum             </td><td> unsigned char  </td><td> mosaic CCD ID number      </td><td>                    </td>
+<tr><td> dummy[20]          </td><td> char           </td><td> extra                     </td><td>                    </td>
+<tr><td> order              </td><td> short int      </td><td> order of Mcal function    </td><td>                    </td>
+<tr><td> Mx                 </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> My                 </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxx                </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxy                </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Myy                </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxx               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxy               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxyy               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Myyy               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxxx              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxxy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxyy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxyyy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Myyyy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+</table>
+
+<em> apmifit, dapmifit are needed for dophot data, but not sextractor data.  are these correctly handled? </em>
+<em> skyprobe used Mxxxx for Ncal and Myyyy for sky - can this be reconciled? </em>
+
+<h3> codes in Image</h3>
+<ul>
+<li> ID_IMAGE_NOCAL = 0x04 - internal to relphot 
+<li> ID_IMAGE_POOR  = 0x02 - set by relphot
+<li> ID_IMAGE_SKIP  = 0x04 - use by relphot (!!!)
+</ul>
+
+<b> Average </b>
+<table> 
+<tr><th> name       </th><th> type           </th><th> value               </th><th> units                 </th></tr>
+<tr><td> R          </td><td> float          </td><td> RA                  </td><td> decimal degrees J2000 </td></tr>
+<tr><td> D          </td><td> float          </td><td> DEC                 </td><td> decimal degrees J2000 </td></tr>
+<tr><td> M          </td><td> short int      </td><td>                     </td><td> 1000*mag		    </td></tr>
+<tr><td> Nm         </td><td> unsigned short </td><td> N measure	    </td><td> 			    </td></tr>
+<tr><td> Nn         </td><td> unsigned short </td><td> N missing	    </td><td> 			    </td></tr>
+<tr><td> Xp         </td><td> short int      </td><td> position scatter    </td><td> 100*arcsec	    </td></tr>
+<tr><td> Xm         </td><td> short int      </td><td> mag chisq           </td><td> 100*log(chisq)	    </td></tr>
+<tr><td> code       </td><td> unsigned short </td><td> data flags	    </td><td> 			    </td></tr>
+<tr><td> offset     </td><td> signed int     </td><td> measure offset	    </td><td> 			    </td></tr>
+<tr><td> missing    </td><td> signed int     </td><td> missing offset	    </td><td> 			    </td></tr>
+<tr><td> dM         </td><td> short int      </td><td> error on M          </td><td> 1000*log(value)	    </td></tr>
+<tr><td> Xg         </td><td> short int      </td><td> best chisq value    </td><td> ??		    </td></tr>
+</table>		  
+
+<h3> codes in Average</h3>
+<li> set to 0 by addstar, etc
+<li> ID_STAR_NOCAL        = 0x0001 - set by relphot
+<li> ID_PROPER            = 0x0400 - set by addusno, used by markrock
+<li> ID_BAD_DATE          = 0x0800 - deprecate (not currently set)
+<li> ID_TRANSIENT         = 0x1000 - not currently set? 
+<li> ID_VARIABLE          = 0x2000 - not currently set? 
+<li> ID_ROCK              = 0xa000 - set by markrock
+<li> ID_GHOST             = 0xc001 - set by fixcat, markstar
+<li> ID_TRAIL             = 0xc002 - set by fixcat, markstar
+<li> ID_BLEED             = 0xc003 - set by fixcat, markstar, markrock
+<li> ID_COSMIC            = 0xc004 - set by markrock
+</ul>
+
+<b> Measure </b>
+<table> 
+<tr><th> name       </th><th> type           </th><th> value               </th><th> units             </th>
+<tr><td> dR         </td><td> short int      </td><td> RA offset           </td><td> 100*arcsec        </td>
+<tr><td> dD         </td><td> short int      </td><td> DEC offset          </td><td> 100*arcsec        </td>
+<tr><td> M          </td><td> short int      </td><td> catalog mag         </td><td> 1000*mag          </td>
+<tr><td> Mcal       </td><td> short int      </td><td> image cal mag       </td><td> 1000*mag          </td>
+<tr><td> Mgal       </td><td> short int      </td><td> 'galaxy' mag,       </td><td> 1000*mag          </td>
+<tr><td> airmass    </td><td> short int      </td><td> (airmass - 1),      </td><td> 1000*airmass      </td>
+<tr><td> FWx        </td><td> short int      </td><td> fwhm major axis     </td><td> 100*arcsec        </td>
+<tr><td> dM         </td><td> unsigned char  </td><td> mag error           </td><td> 1000*mag          </td>
+<tr><td> fwy        </td><td> unsigned char  </td><td> minor/major ratio   </td><td>                   </td>
+<tr><td> theta      </td><td> unsigned char  </td><td> angle wrt ccd X dir </td><td> degree*(256/360)  </td>
+<tr><td> dophot     </td><td> char           </td><td> dophot type         </td><td>                   </td>
+<tr><td> source     </td><td> unsigned short </td><td> photcode            </td><td>                   </td>
+<tr><td> t          </td><td> unsigned int   </td><td> time                </td><td> unix time         </td>
+<tr><td> averef     </td><td> unsigned int   </td><td> average entry       </td><td>                   </td>
+<tr><td> dt         </td><td> short int      </td><td> exposure time       </td><td> 2500*log(exptime) </td>
+<tr><td> flags      </td><td> unsigned short </td><td> data flags          </td><td>                   </td>
+</table>
+
+<h3> flags in Measure</h3>
+<ul>
+<li> ID_MEAS_POOR         = 0x0001 - set by relphot, unset?
+<li> ID_MEAS_NOCAL        = 0x0002 - set by relphot, unset?
+<li> BLEND_IMAGE          = 0x0100 - set by addstar, etc
+<li> BLEND_CATALOG        = 0x0200 - set by addstar, etc
+<li> BLEND_IMAGE_NEIGHBOR = 0x1000 - set by addstar, etc
+<li> PART_OF_TRAIL        = 0x2000 - set by markstar, fixcat, used by markrock
+<li> GHOST_DATA           = 0x4000 - set by markstar, fixcat, used by markrock
+</ul>
+
+<em> relphot flag meanings: </em>
+<b> ID_IMAGE_NOCAL </b> : completely ignore image (internal only)
+<b> ID_IMAGE_SKIP  </b> : externally marked, don't include in Mrel, calculate Mcal (?)
+<b> ID_IMAGE_POOR  </b> : internally marked, don't include in Mrel, calculate Mcal (?)
+
+<b> ID_STAR_NOCAL  </b> : poor star, don't include in Mcal, calculate Mrel
+STAR_BAD == (average.Xm > STAR_CHISQ) || (average.dM > STAR_SCATTER) || (NVALID < MEAS_TOOFEW)
+
+<b> ID_MEAS_NOCAL  </b> : skip this measurement (temporary, internal)
+<b> ID_MEAS_POOR   </b> : poor star, don't include in Mcal, calculate Mrel
+MEAS_POOR == (3 sigma outlier) || (out of detector area)
+<em> currently NOT sticky!! </em>
+
+--
+
+uniphot.h:
+# define MEAS_POOR     0x0001
+# define MEAS_NOCAL    0x0002
+# define ID_IMAGE_SKIP 0x0004
+
+relphot.h:
+# define ID_VARIABLE   0x2000
+# define ID_TRANSIENT  0x1000 /* is this mutually exclusive with USNO?  */
+# define ID_BAD_DATA   0x0800 /* stationary object with some bad data points */
+# define ID_PROPER     0x0400 /* star with large proper motion */
+
+# define MEAS_POOR     0x0001
+# define MEAS_NOCAL    0x0002
+# define ID_IMAGE_SKIP 0x0004
+
+<em> things to update, cleanup </em>
+<d> finish fix of photcode in imregister-3.0, etc </d>
+<d> move flag constants from relphot/uniphot/photdbc include -> loneos.h </d>
+<d> watch for use of Xp, Xg, code in photdbc & status.extract, status.gstar</d>
+<b> status.extract.NVALID : count !(measure.flag & MEAS_POOR) </b>
+<d> fix error with ID_IMAGE_NOCAL == ID_IMAGE_SKIP </d>
+<d> make sure ID_IMAGE_POOR is saved and ID_IMAGE_SKIP is kept, including with -mosaic </d>
+<d> should STAR_BAD be saved or not?  probably not: there is not one per photcode </d>
+<d> MEAS_POOR should be saved, MEAS_NOCAL should not </d>
+<b> recompile all dvo programs, test </b>
+
+<b> photdbc </b> <em> check on the use of flags, etc; consistent with relphot, uniphot? </em>
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/notes.photcode.html
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/notes.photcode.html	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/notes.photcode.html	(revision 22322)
@@ -0,0 +1,221 @@
+
+<h2> A Primer on DVO Photcodes and Magnitude Representation </h2>
+
+<p>
+There are two types of DVO table which store magnitude data.  The
+Average table stores magnitudes which represent averages of multiple
+measurements.  The Measure table stores magnitudes which represent
+single measurements.  
+
+<p>
+Each type of entry is defined by a Photcode.  The individual Measure
+entries have their photcodes stored as part of the structure.  The
+Average entries have photcodes which are determined externally by
+their sequence.  For historical reasons, the Average photcode are
+divided into the Primary photcode (of which there may only be one) and
+Secondary (of which there may be several).  The average magnitude
+associated with the Primary photcode is stored with the Average
+structure while the Secondary photcodes are stored in a separate,
+associated table called Secfilt.  The layout of the Average and
+Secfilt tables, and the photcodes assigned to them, are defined when
+the database is created based on the layout of the photcode table.
+This table is defined with a single Primary and a number of Secondary
+photcodes.  When the database tables are created, primary photcode is
+automatically associated with the Average entry while the sequence of
+Secondary photcodes defines the sequence of the entries in the Secfilt
+table.  This is very fragile, but very fast for making the
+association.  Thus, changing the number or sequence of secondary
+photcodes would cause an existing database table to be
+mis-interpretted.  
+
+<p>
+The Measure entries are broken into two photcode types as well:
+internal and external measurements.  Internal measurements are those
+for which the data source is well understood; the instrumental
+magnitudes are available and the instrumental parameters which are
+required to determine a calibrated magnitude.  External measurements
+are provided by external sources: published catalogs, reference
+sources, etc.  For these measurements, there is no control over the
+instrumental parameters which determined the measurement, and in many
+cases the measurement represents an average of an ensemble.
+
+<p>
+The photcode table defines relationships between photcodes,
+representing different photometry systems.  Each photcode entry
+defines the target of the calibration (the equivalence), and it may
+potentially define the zero-point (Co), the airmass slope (Ko), the
+appropriate color terms (C1, C2), the reference color term (Color),
+and the polynomial coefficients of the color equation (starting with
+the 1st order term).  
+
+<p>
+I need to include operations to handle varying color terms between
+CCDs.  I have measured color terms between CCDs in the range +/- 0.1
+mag/mag.  This is an appropriate time to clean up some of the code
+that handles the photcodes, etc.  In particular, the photcode table
+should provide the relationship beween relate between filter systems.
+
+For all of the functions below, a REF photcode simply returns the
+stored magnitude without transformation.  
+
+PhotInst (measure)
+  returns the instrumental magnitude of the given Measure
+  Minst = measure[0].M - measure[0].dt - iZERO_POINT;
+
+PhotCat (measure)
+  returns the 'catalog' magnitude of the given Measure (best guess
+  given no other information like color):
+  Mcat = measure[0].M - iZERO_POINT + Klam*(measure[0].airmass - 1000) + photcodes[0].code[Np].C;
+
+PhotSys (measure, average, secfilt)
+  returns the 'system' magnitude of the given Measure (best guess
+  using existing color information, but without application of
+  relative photometry offsets):
+  Msys = Mcat + f(color)
+  color is derived from measure.photcode.c1,c2  (measure.source.c1,c2)
+
+PhotRel (measure, average, secfilt)
+  returns the 'relative' magnitude of the given Measure (best guess
+  using existing color information and current relative photometry
+  measurment for this image).  This is the same photometry system as
+  the Average (PRI/SEC) measurement equivalent to this measurement
+  photcode:
+  Mrel = Msys + measure[0].Mcal;
+  color is derived from measure.photcode.c1,c2  (measure.source.c1,c2)
+
+PhotCal (measure, average, secfilt, allmeasures, code)
+  returns the 'calibrated' magnitude of the given Measure (best guess
+  using existing color information and current relative photometry
+  measurment for this image transformed to the appropriate reference
+  photometry system):
+  Mcal = Mref + f(color)
+  color is derived from code.c1,c2
+  the provided code must be either the measure.photcode.equiv or an
+  alternate for that code.
+
+<p>
+consider two filters, B & V, and two CCDs 00 & 01.  also, reference
+data in B_L92, V_L92
+
+<pre>
+    N  photcode     type  Co      Ko       X    C1    C2    Ao     Color  Equivalent
+  100  CFH12K.B.00  dep   26.000 -0.15     -    B     V     0.1    -      B
+  101  CFH12K.B.01  dep   26.000 -0.15     -    B     V     0.0    -      B
+
+  200  CFH12K.V.00  dep   26.100 -0.15     -    B     V     0.0    -      V
+  201  CFH12K.V.01  dep   26.100 -0.15     -    B     V     0.1    -      V
+
+    1  B            pri    0.000  -        -    B_L92 V_L92 0.016  -      B_L92
+    2  V            sec    0.000  -        -    B_L92 V_L92 0.008  -      V_L92
+
+  1003 B_L92        ref    -      -        -    -     -     -      -      -
+  1004 V_L92        ref    -      -        -    -     -     -      -      -
+  
+  B = m + Co + Ko*(secz - 1) + Ao*(B - V)
+  B_L92 = B + Co + color*A0 + color^2*A1 + color^3*A2
+</pre>
+
+<b>changes I'd like to make related to photcodes</b>
+
+<li> use structure value 'photcode', not 'source' everywhere
+<li> better abstraction / conceptualization of PhotMode values (Inst, Rel, Abs, Sys)
+<li> N-order polynomial for color-fits
+
+<b> relevant programs </b>
+
+<table>
+<tr><td>* addrefs    </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* addspphot  </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* addstar    </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* delstar    </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* photcode   </td><td> probably ok, check is consistent with new (?) photcode structs </td></tr> 
+<tr><td>* photreg    </td><td> check consistency </td></tr> 
+<tr><td>* photsearch </td><td> check consistency, use photcode table to get Equivalent </td></tr> 
+<tr><td>* relphot    </td><td> signficant changes to handle new structures </td></tr>
+<tr><td>libohana     </td><td> update LoadPhotcodes funcs, check consistency, use photcode table to get Equivalent </td></tr> 
+<tr><td>photdbc      </td><td> signficant changes to handle new structures </td></tr>
+<tr><td>status       </td><td> signficant changes to handle new structures </td></tr>
+<tr><td>dvo          </td><td> repeat fixes in status </td></tr> 
+
+<tr><td>lightcurve </td><td> unused? </td></tr>
+</table>
+
+<b> basic photcode APIs </b>
+
+int LoadPhotcodes (char *filename);
+void SetZeroPoint (double ZP);
+
+PhotCode *GetPhotcodebyName (name);
+int       GetPhotcodeCodebyName (name);
+PhotCode *GetPhotEquivbyName (name);
+int       GetPhotEquivCodebyName (name);
+
+PhotCode *GetPhotcodebyCode (code);
+char     *GetPhotcodeNamebyCode (code);
+
+PhotCode *GetPhotEquivbyCode (code);
+int       GetPhotEquivCodebyCode (code);
+
+double PhotInst (Measure *measure);
+double PhotAbs (Measure *measure);
+double PhotCat (Measure *measure);
+double PhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+double PhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+double PhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+double PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+
+<b> examples </b>
+find measures which are of photcode equivalent to X
+if (GetPhotEquivCodebyCode (measure[0].source) == N1) { } 
+
+<b> code, entry, hashcode, hashNsec </b>
+entry = 1;
+photcode[entry].code = 100 
+photcode[entry].name = B
+hashcode[100] = 1;
+
+hashNsec is only valid for type PHOT_SEC:
+hashNsec[code] = Nsec entry in SecFilt table
+
+<b> note recursion relationships </b>
+hashcode[photcode[entry].code] = entry;
+photcode[hashcode[entry]].code = entry
+
+<b> outstanding questions / issues </b>
+
+<ul>
+<li> PhotSys needs to handle missing color values
+<li> define valid range for color?
+<li> double PhotFoo functions return what on NO_MAG?
+<li> PhotFoo functions need to check for valid input types:
+  <ul> 
+  <li> PhotInst - PHOT_DEP
+  <li> PhotAbs  - PHOT_DEP, PHOT_REF
+  <li> PhotCat  - PHOT_DEP, PHOT_REF
+  <li> PhotSys  - PHOT_DEP, PHOT_REF
+  <li> PhotRel  - PHOT_DEP, PHOT_REF
+  <li> PhotAve  - PHOT_PRI, PHOT_SEC
+  <li> PhotRef  - PHOT_PRI, PHOT_SEC
+  </ul>
+</ul>
+
+<b> phot definitions </b>
+PhotInst = Measure.M - dt - ZP 
+PhotCat  = Measure.M + K*(airmass-1) + C - ZP
+PhotSys  = Measure.M + K*(airmass-1) + C - ZP + X*color
+PhotRel  = Measure.M + K*(airmass-1) + C - ZP + X*color - Mcal  -> average.m
+PhotRef  = average.M + C + X*color 
+
+C: def ~ 26.0, pri ~ 0.0
+
+PhotAbs  = deprecate (measure.M + K*(airmass-1) + C - ZP
+
+<b> alt photcodes </b>
+
+photcodes of type ALT provide alternate calibration terms for a given
+photcode.  these codes are equivalent to primary/secondary codes, but
+no data is ever saved with this type.  There are only lookups between
+the photcode name and the photcode structure, never the photcode.code
+value, since that defines the photcode for which the given structure
+is an alternate relationship.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/notes.txt	(revision 22322)
@@ -0,0 +1,103 @@
+
+opihi improvements / fixes:
+
+* single math processing system : inline shell math used one set of
+  functions (shell/math.c), while the set = () used another set, and
+  logic used part of a third set.  This has been unified into a single
+  math handling function (newmath).  The new math expression handling
+  also returns temporary in-line vectors and matrix expressions.
+
+* global user variables (Buffers, Vectors) and other global variables
+  (outfile, tv parameters, graphic and image tool parameters) have
+  been encapsulated in fuctions to avoid the global data constructs.  
+
+* improved command exit status handling : in the past, a function
+  returning FALSE would halt all script executing, returning to the
+  top level of the command stack.  we now set the variable $STATUS
+  with the exit status and allow the the user to test the exit
+  status to choose an operation.  loops, macros, and input files can
+  use the 'break' and 'continue' functions to escape from the current
+  location.  calling break returns with an exit status of FALSE,
+  calling break returns immediately with an exit status of TRUE.
+
+* more extensive use of 'outfile' 
+
+* support for opihi scripts: there is now better interpretation of
+  command-line arguments to allow for embedded opihi scripts, ie
+  scripts with the dvo or mana interpretter named on the first line
+  with the prefix #! like a shell script.  The first command-line
+  argument is interpreted as a file to input while successive ones are
+  converted to variables $argv:0 - $argv:n  WARNING: in the past,
+  multiple input files could be specified on the command-line.  this
+  now must be specified with --load file arguments.
+
+* local variables: there is now support for local (vs global) opihi
+  variables.  any macro may have a variable declared as local with the
+  command 'local (var)', which may take a list of variables.  these
+  variables are stored internally in association with the macro name.
+  the command may include the option -static, in which case the
+  variable will retain its value on successive calls to the same
+  macro.
+
+- merge of all old opihi-type programs into a single code base 
+
+- group related functions into libraries
+
+- function names, etc, should come from a single function, now a
+  global include file.
+
+- update help files
+
+- update web pages
+
+--
+
+some notes: 
+
+- new vector interpretation is now somewhat slower, due to the multiple
+  assignments.
+
+- BinaryOp does not leak memory (tested)
+
+- 55 seconds for 1000 floating-pt operations on a 4Mpix (3 GHz) -> 0.2
+  seconds per FP OP on a 4k square image
+
+- loop / list functions:
+
+  opihi.c
+  run_loop.c
+  run_if.c
+  input.c
+  for_loop.c
+  macro_exec.c
+
+  I have added continue / break functionality to all loop operations:
+
+  * the variable $STATUS is set based on the exit status of each command
+  * each loop tests for break and continue
+  * in the case of break, the loop returns a status of FALSE
+  * in the case of continue, the loop returns a status of TRUE
+  * if break -auto is set to 'on', then any exit status of FALSE escapes to  
+    the top level.
+  * interrupts are carried up to the top level.
+
+- local variables
+
+  I have added local variables.  They have names of the form
+  MacroName.VarName, where MacroName is the macro in which they are
+  local.  They are declared local with the command 'local', which
+  takes a list of variables to declare local, and the optional -static
+  flag, which requires the shell to test for the existence of the
+  variable before creating it, and only create it if it does not
+  exist.  otherwise the existing value of the variable is maintained.
+
+- math
+
+  I have removed the last instances of math interpretation being
+  performed by math, not newmath, in expand_vectors (interpreting the
+  contents of the vector square brackets: x[5]), and in the two
+  locations where logic was used in the evaluation: run_in and
+  run_while.  there are still some error in the precedence of the
+  operators: logical operators >,<,==,!=, etc need to be lower
+  priority than other binary operators.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/pantasks.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/pantasks.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/pantasks.txt	(revision 22322)
@@ -0,0 +1,52 @@
+
+- task spawning speed
+
+  I have been examining things which affect the speed of the pantasks
+  processing. I have learned some interesting things:
+
+  * pcontrol was being slammed with requests for status by pantasks.
+    this may account for Paul's controller hang-ups.  I have added a
+    long (500ms) sleep to the controller thread to limit the rate at
+    which controller checks are run
+
+  * adding even a small usleep to the task_thread or job_thread puts
+    them to sleep for a long time (>> 10ms).  it seems longer than the
+    linux time slicer.  I have removed sleeps from the task and job
+    threads.
+
+  * the job submit rate is apparently limited by two things:
+
+    * when the job is submitted (SubmitJob) the interaction with
+      the controller seems to take ~30ms or more.
+
+    * some thread (controller thread? main readline thread?) seems to
+      introduce timeouts which are very long (up to 100ms).  These
+      introduce bit delays if when they happen during the task_thread
+      loop. 
+
+- updates for queues:
+
+  -key 1:2:4 (key is string with possibly multiple columns joined)
+  string function to drop first word
+
+
+- todo:
+
+  - create the processing threads:
+    - check tasks
+    - check jobs
+    - check controller  
+    - load inputs
+
+- additional issues:
+  - server input needs to place files for input on a stack which is 
+    actually loaded by the RunScheduler loop
+  - have the client run a special multicommand function which passes 
+    any unfound commands along to the server.  the server should not 
+    be running multicommand, but it probably does not hurt.
+    (the client is not allowed to send ';' to the server)
+
+  - pantasks input: this passes an 'input' command to the server, which
+    performs the input in a different thread.  is this the same thread
+    as the scheduler loop?  another option within the scheduler loop?
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/pcontrol.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/pcontrol.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/pcontrol.txt	(revision 22322)
@@ -0,0 +1,276 @@
+
+2006.08.18
+
+ Outstanding issues related to pcontrol:
+
+ * disposition of HUNG jobs?
+ * probably should not save the history for pcontrol or pclient
+   (these will be many lines long very quickly...)
+ * need to add options to run/stop for hosts and jobs independently
+
+2006.08.11
+
+I have nearly finished the conversion of pcontrol to use a background
+thread for monitoring the remote machines.  A few questions are still
+outstanding:  
+
+- currently, we are thread-safe for interactions with the stacks.  As
+  long as an operation is only working with a single job/host, and all
+  jobs/hosts are selected by pulling them from the stacks, there will
+  never be a contention between threads for the same job/host.
+  However, are there problems for commands which require a specific
+  job or host but are unable to find it because the job/host may be in
+  flight from one stack to another.  
+
+- The CheckIdleHost command needs to join a job and a host.  In this
+  case, it is necessary to lock the job PENDING stack while searching
+  for a job to give to a host.  The host is pulled off of the IDLE
+  stack before being past to CheckIdleHost.
+
+o CheckIdleHost currently does not have a way to send a WANTHOST job
+  to any host other than the WANTHOST.  What should the rule be by
+  which a job is run on an alternative machine? (partial fix)
+
+- we are not starting any of the job or host timers?
+
+2006.08.09
+
+working on pcontrol CheckSystem background thread.  One thread runs
+the readline interaction and performs all of the user commands.  The
+second thread runs the CheckSystem loop and tests the hosts and jobs.
+We need to be sure these two do not interfere with one another.  Here
+is a list of all of the user commands and the ways in which they
+interact with the Job / Host queues:
+
+
+
+2006.08.04
+
+pcontrol gets a large delay every time it tries to connect to a host.
+this is because the readline interrupt has to wait for the connection
+to complete.  I probably need to fix this by using a threaded model
+and running CheckSystem in a background thread.
+
+-----
+
+typedef struct {
+  char *buffer;
+  int   Nalloc;
+  int   Nmaxread;
+  int   Nextra;
+  int   Nlast;
+  int   Nbuffer;
+} Fifo;
+
+typedef struct {
+  int argc; char **argv; /* a list of words that define this object */
+  struct timeval start, accum, timer;
+  int   status;
+  char *logfile;
+  char *lastproc;
+} Object;
+
+typedef struct {
+  Object **object;
+  int    Nobject;
+  int    NOBJECT;
+} Queue;
+
+typedef struct {
+  char   *hostname;
+  int     rsock, wsock;
+  int     status; /* idle, busy, etc... */
+  struct  timeval start, accum, timer;
+  Fifo    fifo;
+  int     code;
+  Object *object;
+} Machine;
+
+
+currently, the transport is /usr/bin/rsh, defined in InitMachines.c 
+
+the shell on the remote machines is /bin/tcsh, defined by rconnect.c
+
+---
+
+pcontrol.client:
+
+ - remote process initiated by pcontrol
+
+ - accepts jobs, returns status, stdout and stderr
+
+ - valid commands:
+
+   - job (argv)
+     returns PID or -1 (0?) on failure
+
+   - status
+     returns current job status:
+     BUSY
+     EXIT n 
+     CRASH n
+     
+   - stderr
+     returns the current stderr buffer:
+     NBYTES n
+     (DATA)
+
+   - stdout
+     returns the current stdout buffer:
+     NBYTES n
+     (DATA)
+
+   
+---
+
+the client needs to accept commands from the server (via
+stdin/stdout), but it also needs to monitor its process.  I can use
+the opihi structure to implement the command-line interpretation with
+readline.  I can use the readline function rl_event_hook to set the
+background functions to check and rl_set_keyboard_input_timeout to set
+the polling period.
+
+this same method can be used with the scheduler:  the command 'run'
+can set the CheckTask function to this hook (& unset it).
+
+---
+
+rl_event_hook -> CheckChild
+
+  - needs to handle the case when no child process yet exists
+  - needs to examine the child status,
+  - needs to read from child stderr and store
+  - needs to read from child stdout and store
+
+  * no warnings (will not do anything clever if buffers get too large)
+
+---
+
+ pcontrol commands:
+
+ job [options] argv0 argv1 argv2 ...
+  -host name : run job on specified host, or any other if not available
+  +host name : run job on specified host, error if not available (error when attempted, not when submitted)
+  -timeout N : seconds before controller gives up on job (once started)
+  -stdout name : redirect job stdout to file directly
+  -stderr name : redirect job stderr to file directly
+
+  * priority information?
+  * returns JobID
+  * adds job to pending queue
+
+ host (hostname) [-delete]
+ (may have multiple entries to the same machine, these are not distinguished)
+
+ stdout ID [-file name]
+ stderr ID [-file name]
+ delete ID
+
+ status -job ID
+ status -machine hostname
+ status -queues
+
+pcontrol may be given a timeout for each job.  pcontrol will monitor a
+job and kill/crash it if the timeout expires.  the timeout only
+governs how long it is allowed to execute, not how long it can sit in
+the queue.  (the scheduler / operator should decide if a job has been
+on pcontrol for too long -- this probably means there are no
+appropriate machines ).
+
+pcontrol currently does not distinguish between multiple instances of
+a single host.  all have the same name.  if you want to bring down a
+host, you need to issue N host -down commands.  perhaps this is
+silly.  a simple alternative would be for the host [-on -off -start
+-stop] commands to apply to all defined entries which match the
+hostname.  In this case, a command like 'host foo -off' would find and
+halt all connections to the machine 'foo', while 'host foo -on' would
+restart them all (or rather, given the functionality of pcontrol,
+would allow pcontrol to attempt to bring them on).
+
+It is not clear why a user should be able to execute 'start' (down ->
+idle) or 'stop' (idle -> down).  The transition down -> idle is
+automatically performed by pcontrol for any machines which are
+currently down, while the transition idle -> down is immediately
+followed by an attempt by pcontrol to move the host from down -> idle.
+This functionality can be used with non-automatic calling of
+CheckSystem to test the pcontrol host interface operations.
+
+does it makes sense to kill all jobs on a host?  this would only have
+the effect of clearing the host for a moment until pcontrol decided to
+start another job on that host.  the desired effect is gained putting
+the host to 'off'.
+ 
+currently the command 'host (hostname)' puts the host in 'down'
+state.  pcontrol then immediately tries to connect to the host, moving
+it to 'idle' state (and then 'busy' if any jobs are available).  it
+might be useful to be able to add a host in 'off' state as a starting
+point.  
+
+it is not obvious that the user should be able to run 'CheckHost',
+unless this gets expanded to return state information on the host.
+
+---
+
+Job States:
+
+PENDING
+BUSY
+EXIT
+CRASH
+NEW *
+DEL *
+
+* - invisible states 
+
+Job State Transitions:
+
+NEW     -> PENDING : AddJob
+PENDING -> BUSY    : StartJob
+PENDING -> DEL     : DelJob
+BUSY    -> DONE    : CheckBusyJob | KillJob
+DONE    -> EXIT    : CheckDoneJob
+DONE    -> CRASH   : CheckDoneJob
+BUSY    -> PENDING : CheckJob | CheckHost
+EXIT    -> DEL     : DelJob
+CRASH   -> DEL     : DelJob
+
+Host States:
+
+IDLE
+BUSY
+DOWN
+OFF
+NEW *
+DEL *
+
+* - invisible states 
+
+Host State Transitions:
+
+NEW      -> OFF      : AddHost
+OFF      -> DEL      : DelHost
+OFF      -> DOWN     : OnHost
+DOWN     -> OFF      : OffHost
+IDLE     -> OFF      : OffHost
+DOWN     -> IDLE     : StartHost
+IDLE     -> BUSY     : StartJob
+BUSY     -> IDLE     : CheckJob | KillJob
+BUSY     -> BUSY-OFF : OffHost
+BUSY     -> DOWN     : CheckJob | CheckHost
+BUSY-OFF -> OFF      : CheckJob
+
+AddJob    - U
+DelJob    - U
+StartJob  - P
+CheckJob  - P
+KillJob   - U
+
+AddHost   - U
+DelHost   - U
+OnHost    - U
+OffHost   - U
+StartHost - P
+CheckHost - P
+
+U - operation performed by the user
+P - operation performed by the program
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/psched.htm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/psched.htm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/psched.htm	(revision 22322)
@@ -0,0 +1,161 @@
+
+This article describes the concept, design, and operation of
+<tt>psched</tt>, the Pan-STARRS IPP task scheduler.  
+
+<h2> Basic Concept </h2>
+
+  <p>
+  The purpose of <tt>psched</tt> is to manage the automatic construction
+  and execution of inter-related (often repetative) operations.
+  <tt>psched</tt> uses a set of rules to define UNIX commands, and
+  their corresponding command-line arguments, to be performed on some
+  regular, repeated basis.  The utility of <tt>psched</tt> is that it
+  can easily define an analysis system which is completely
+  state-based, as opposed to an event-driven system.  
+
+  <p>
+  Consider, for example, a telescope which obtains a collection of
+  images over the course of a night.  Every minute or two, it takes an
+  image and writes the image to some disk.  An event-driven analysis
+  system would involve having the telescope initiate a process at the
+  end of the exposure.  This process would perform an analysis, write
+  some output, then send trigger another process.  This type of
+  operation works very well for a simple set up with reliable
+  hardware.  Such a system becomes more difficult to maintain when
+  hardware failures occur or when multiple systems need to interact
+  with each other.  When failures occur, the triggering information
+  (the events) is easily lost, thus some mechanisms are needed to
+  detect these failures and either re-send the trigger or send an
+  alternative failure-mode trigger.  Or, if two systems need to
+  interact, one or the other system must block for results from the
+  first.  Stopping and restarting such an analysis system is very
+  delicate since the appropriate triggers must be set up some how, eg
+  by noticing which images have not succeeded and restarting them at
+  the appropriate stage.  All of these types of methods of handling
+  complexity and failures are essentially state-based rules.
+  <tt>psched</tt> allows the easy definition of a totally state-based
+  analysis system.
+
+  <p>
+  In a state-based system, some mechanism examines the state of the
+  system and decides which actions to perform based on the current
+  state.  In the illustration above, the mechanism could examine the
+  images available (either by examining the disk or by examining the
+  state of a data table) and decide to perform an operation based on
+  what images are available.  This makes it very easy to handle
+  complexity and errors.  If an analysis fails, the state either is
+  not successfully updated or the error state is recorded, both
+  situations being easy to detect and easy to handle.  Restarting the
+  system simply involves starting the state-monitoring mechanism.
+  Combining results from multiple input sources simply involves
+  watching for the multiple inputs to be available.  <tt>psched</tt>
+  provides a mechanism to define state monitors, and to define the
+  actions which are performed when those states occur.
+  <tt>psched</tt> action consist of initiating UNIX commands, where
+  the arguments of those commands may depend on the results of the
+  state tests.
+
+  <h3> Tasks vs Jobs </h3>
+
+  <p>
+  The primary function of <tt>psched</tt> is to repeatedly perform
+  <b>tasks</b>, and execute <b>jobs</b> on the basis of those tasks.
+  A task consists of a set of rules which describe system state tests
+  to perform on a regular time scale.  Based on the results of those
+  state tests, the task will then choose whether or not to construct a
+  job.  The task also defines actions to perform upon the completion
+  of a job, based upon the output and exit status of the job.  A task
+  thus defines the repeat period.  It may optionally define valid or
+  invalid time ranges (eg, Mon-Fri or 10:00-17:00, etc).  The task may
+  also specify that the job be run locally (ie, in the background on
+  the same computer as psched) or remotely by the parallel process
+  controller (<tt>pcontrol</tt>).  A job may even be restricted to a
+  specific computer managed by <tt>pcontrol</tt>.
+
+  An example of a simple tasks is given below.  
+
+<pre>
+  task datalist
+    command ls /data/foo
+    periods -exec 5.0
+    periods -timeout 50.0
+    periods -poll 1.0
+
+    task.exit 0
+      queueprint stdout
+      queuedelete stdout
+    end
+ 
+    task.exit 1
+      queuepush failure "task failed"
+    end
+  end
+</pre>
+
+  <p>
+  This task does not perform any system state tests; it is simply
+  constructs a new job every 5.0 seconds.  The job in this case is
+  always the same: <tt> ls /data/foo </tt>.  When the job finished,
+  if the job exit status is 0 (normal UNIX success status), the
+  resulting output is printed to the screen.  If the job returns an
+  exit status of 1 (a failure), the failure queue receives a single
+  entry.  Although they are not defined in this case, it is also
+  possible to specify the action to be taken if the job crashes (does
+  not exit normally) or if it times out (runs beyond the specified
+  timeout period).
+
+  A slightly more complex task which performs a state test and
+  constructs a command based on that test is shown below
+
+<pre>
+  task datalist
+    periods -exec 5.0
+    periods -timeout 50.0
+    periods -poll 1.0
+
+    task.exec 
+      $file = `next.file`
+      if ($file == "none")
+        break
+      end
+      command cp /data/foo/$file /data/bar
+    end
+
+    task.exit 0
+      queueprint stdout
+      queuedelete stdout
+      queuepush copied $file
+    end
+ 
+    task.exit 1
+      queuepush failure $file
+    end
+  end
+</pre>
+
+  The <tt>task.exec</tt> macro is executed by psched every 5.0
+  seconds.  This macro executes a (hypothetical user-defined) UNIX
+  command (<tt>next.file</tt>) which examines the system state, return
+  either a filename or the word "none".  If the result of this test is
+  "none", the task does nothing: no job is constructed.  Otherwise, a
+  job is constructed using the name of the file returned by the state
+  test.  Successful jobs have the filename added to the 'copied'
+  queue, while failed jobs add the filename to the 'failure' queue.
+
+  <h3> Parallel vs Local Job Processing </h3>
+
+  <h3> Task Restrictions </h3>
+
+  <h3> Inter-Task and Inter-Job Communications </h3>
+
+<h2> psched Design </h2>
+
+  <h3> The Opihi Shell </h3>
+
+  <h3> Task List </h3>
+
+  <h3> Job List </h3>
+
+  <h3> pcontrol Interface </h3>
+
+  <h3> 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/rd-upgrades.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/rd-upgrades.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/rd-upgrades.txt	(revision 22322)
@@ -0,0 +1,10 @@
+
+rd a file.fits : read from PHU
+rd a file.fits -x N : read from extension N (0 - N)
+rd a file.fits -n name : read from extension name
+
+-plane : read only slice N
+
+* this function needs to check for compressed data:
+
+  * if PHU has been compressed, 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/region-image.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/region-image.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/region-image.txt	(revision 22322)
@@ -0,0 +1,13 @@
+
+I have done some work on getting region to use the image projection
+for the dvo display.  The pieces are almost there, except:
+
+* there is apparently a small offset between the two systems?
+
+* SelectRegions relies on the values of xmax and ymax being degrees
+  from a 0,0 coordinate.  this is a little tricky: this should not
+  fail for large AITOFF projections.
+
+* kapa does not do non-linear projections
+* kapa does not do multi-level projections
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/sample.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/sample.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/sample.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include <stdio.h>
+
+main (int argc, char **argv) {
+
+  int i;
+  char line[1024];
+
+  for (i = 0; i <= argc; i++) {
+    gprint (GP_ERR, "arg %2d: %s\n", i, argv[i]);
+  }
+  while (argv[i] != NULL) {
+    gprint (GP_ERR, "env %2d: %s\n", i, argv[i]);
+    i++;
+  } 
+  while (fscanf (stdin, "%s", line) != EOF) {
+    gprint (GP_ERR, "line: ...%s...\n", line);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/scheduler.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/scheduler.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/scheduler.txt	(revision 22322)
@@ -0,0 +1,206 @@
+
+2005.07.15
+
+The controller sends messages to both stdout and stderr.  I can easily
+require the messages which are immediate responses to external
+commands (status, check, etc) go back on stdout.  other messages
+should go to stderr, or be suppressed.  I suppose i can regularly
+harvest the stderr messages?
+
+2005.07.14
+
+I am still exploring the scheduler / controller interactions.  the
+automatic interactions seem to work pretty well now.  The area of
+confusion is in the user interface, both in terms of checking on the
+status of things (both controller and scheduler) and in terms of
+having user control over aspects of the controller.
+
+I have defined user functions which execute the controller commands
+'status' and 'check'.  These are straightforeward since they simply
+send a command to the controller and echo the output (or give an error
+condition message).
+
+Should the user have the ability to define a job, independent of a
+task?  This could be implemented purely as a controller action: the
+controller commands 'job', 'kill', 'delete', 'stderr', 'stdout' would
+be available from the scheduler, and the commands simply passed
+along.  This adds a bit to the complexity: if the 'delete' command is
+passed along, nothing prevents the user from deleting a job scheduled
+by the scheduler from a task.  the scheduler may then get confused
+when it tries to interact with that job in the future from the
+automatic loop.
+
+Another option is to simply have these commands interact with the
+scheduler's job stack.  this has the advantage of limiting the
+scheduler / controller responsibility errors (scheduler, not user, is
+always responsible to sending/harvesting jobs to/from the
+controller, though we still need to handle the cases if a job is lost
+or dropped by the controller).  the diffficulty here is deciding how
+to handle the job completion.  we would need a way to define a set of
+exit macros, which could then do something useful with the output.  
+
+Another possibility is to define limits on how many times a task may
+spawn a job.  There would then be no 'job' function.  If we define
+this limitation, we will still need a way of killing and deleting a
+specific job.  Thus a 'kill' and 'delete' function would examine and
+modify the scheduler's job stack.  The stderr and stdout functions are
+then already part of the task commands.  
+
+Other task options might include: 
+
+- a list of allow / exclude time periods (which should be time-of-day
+  ranges and day-of-week ranges).
+
+- a function to delete an existing task (which would have to stop the
+  spawning of new jobs, at least until no more jobs for that task
+  remain).
+
+- allow the 'periods' command to define defaults when outside of a
+  task
+
+2005.07.05
+
+At this point, scheduler / pcontrol / pclient all work in a basic way.
+pclient is the most robust of the three, having the simplest
+responsibility.  pcontrol is generally pretty good, though I need to
+flesh out the user interface a bit and clean up the output warning / info
+messages.  scheduler will need the most attention, though it is
+already fairly reasonable.  I need to flesh out the user commands to
+check on the controller status (basically, these need to replicate the
+status commands available to the controller).  
+
+I also need to handle the case of timeout on the controller,
+independently of timeout for a local job on the scheduler.  currently,
+if a local job exceeds the timeout value, scheduler flags it.  but, it
+does not make sense to use the same timeout value for a controller
+job. I could pass the timeout to the controller when the job executes,
+in which case it has the same meaning, essentially, for the controller
+jobs as it does for the local jobs: once you start the function, it
+needs to complete within NN seconds.  However, I think I still need to
+have a scheduler concept of a job which the controller is unable to
+complete.  It should be possible to prevent a job from sitting pending
+on the controller forever.  What exactly you do if the controller is
+unwilling to execute a job is another story (possible reasons:
+controller overload, missing required host, missing any hosts,
+something hung somewhere?).  
+
+The scheduler does not do a good job of shutting down the controller
+when it (the scheduler) exits.  This works well for the
+pcontrol/pclient interface, so the solution lies there.  
+
+I need to decide how to behave if the scheduler asks for a job with a
+required host which the controller knows is currently down or
+non-existent.  Several options could be used.  The controller could
+simply hold the task until the scheduler notices it is not being
+executed (after all, the controller does not know if the machine is
+being serviced for a short time or a long time, but the scheduler
+could know).  The controller could immediately return a failure noting
+the current state of the machine (this would put the burden of
+deciding that the machine should be available on the scheduler).  The
+controller could try to execute the job a certain number of times, and
+then it could report the failure to the scheduler.  This is not so
+different from having a pending-timeout which the scheduler tracks
+(moves the timeout check to the controller, essentially).  
+
+There was some odd behavior with 'exec echo $stdout >> foo'.  This
+resulted in empty files 'foo'.  The following work fine, so something
+is just weird:
+exec echo foobar >> foo
+output foo
+echo $stdout
+output stdout
+
+Various error conditions should be checked
+
+What do we do if a task requests a host which is not available to the
+controller (ie, not defined)?  this is similar to the problem of
+requesting a host which is down.  I think the controller should either
+immediately refuse or accept in anticipation that such a host may
+eventually be defined.
+
+I need to be careful about jobs sent to the controller and not
+harvested before stopping the scheduler execution.
+
+---
+
+sched / pcontrol todo:
+
+- sched: validate task hosts with controller
+
+---
+
+scheduler commands:
+
+task (taskname)
+ - define a new task
+ - loads task-related commands from list / readline
+ - commands are parsed on load
+ - end with end (like if / for)
+
+task.exit (value)
+ - define a new task macro for this exit condition
+   (value) may be an exit status (number)
+   (value) may be 'timeout'
+   (value) may be 'crash' ?
+ - commands are parsed on execution (not on definition)
+
+task.exec
+ - define a task macro for exec condition
+ - commands are parsed on execution (not on definition)
+
+command (args) (args)
+ - defines command associated with task
+ - may be in task or in task.macro (exit/exec)
+   (in task, command line is static; in task.macro, command line is expanded for each instance)
+
+host (machine) [-required]
+ - defines preferred host
+ - may be in task or in task.macro (exit/exec)
+   (in task, value is static; in task.macro, value is defined for each instance)
+ - value of LOCAL runs job as local job (not on controller)
+ - value of NONE runs job on controller without specifying host
+
+stderr (file / variable)
+ - defines destination for stderr capture from task
+ - written to destination at end of execution?
+
+stdout (file / variable)
+ - defines destination for stdout capture from task
+ - written to destination at end of execution?
+
+periods -poll 1
+periods -exec 30
+periods -timeout 2
+ - defines relevant time-scale for the task
+
+run
+stop
+ - start or stop the scheduler loop, executing the various tasks
+
+---
+
+local jobs vs controller jobs
+
+a local job is run as background fork (ie, not on controller)
+a controller job is sent to the controller to run when it can
+
+---
+
+possible errors which the scheduler may encounter when executing a
+job:
+
+  - controller is not responding
+  - controller says machine is DOWN
+  - controller says command not found
+  - controller has too many processes
+  - controller takes to long to start job (pending timeout)
+  - controller says job timed out
+  - controller says job crashed
+  - controller says job exited with status
+
+---
+
+notes:
+
+ - watch for NFS lags / blocking.  if NFS has file visibility lags, we
+   may need to add blocking as an option to the job (-block filename)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/stds.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/stds.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/stds.txt	(revision 22322)
@@ -0,0 +1,40 @@
+
+notation:
+
+object : an astronomical source, may be observed multiple times
+measurement : a single observation of an object (one per object per exposure) 
+m_inst : instrumental magnitude measurement
+m_cat  : catalog magnitude measurement (m_inst + 25.0)
+m_sys  : nominal internal system magnitude measurement (m_inst + ZP(CCD,FILTER)
+m_rel  : transparency adjusted magnitude measurement (m_sys + offset)
+
+M_ave  : average (object) magnitude in internal system
+M_ref  : average (object) magnitude in reference system
+
+test for photometric consistency:
+
+for the output from dither.corr:
+
+M_ave - m_rel(ccd) vs x_ccd, y_ccd
+M_ave - m_rel vs x_mosaic, y_mosaic
+M_ave - m_rel vs airmass?
+
+
+
+  MEGACAM.g.00 Minst - MEGACAM.g.01 Mcal : time
+
+  MEGACAM.g.00 - g : time
+
+  MEGACAM.g.00,Mrel - MEGACAM.g.01,Mcal : MEGACAM.g.00 - MEGACAM.r.00
+
+- use relphot to flag stars in poor locations, etc
+
+- we can choose for entry if it is Minst, Mcat, Msys, Mvrel, Mcal, Mave, or Mref
+
+int GetMagnitudeType (char *name, PhotCode *code, int *MagMode);
+
+- should return photcode & value MEAS_MREL, AVE_MREF, or what ever
+- should provide an appropriate default for measures, etc
+- should check for consistency with photcode type:
+  PRI/SEC : AVE_MREF, AVE_MAVE
+  DEP     : MEAS_ETC
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/todo.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/todo.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/doc/todo.txt	(revision 22322)
@@ -0,0 +1,4 @@
+
+- convert and include all mana/dimm/status derived functions
+- local scalar variables
+- break / continue in: for, macro, 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ImageOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ImageOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ImageOps.c	(revision 22322)
@@ -0,0 +1,127 @@
+# include "dvoshell.h"
+
+void image_subset (Image *image, int Nimage, int **Subset, int *Nsubset,
+		   SkyRegionSelection *selection, 
+		   unsigned long int tzero, double trange, int TimeSelect) 
+{
+
+  int i, j, flipped, status, InPic;
+  int *subset;
+  int N, n, npts;
+  double r, d, X, Y, x[4], y[4], Rmin, Rmax, Rmid;
+  Graphdata graph;
+  SkyRegion patch;
+
+  Rmin = Rmax = Rmid = 0;
+
+  if (selection->useDisplay) {
+    if (!GetGraphData (&graph, NULL, NULL)) {
+      gprint (GP_ERR, "region display not available\n");
+      return;
+    }
+    Rmin = graph.coords.crval1 - 182.0;
+    Rmax = graph.coords.crval1 + 182.0;
+    Rmid = 0.5*(Rmin + Rmax);
+    BuildChipMatch (image, Nimage);
+  }
+
+  if (selection->useSkyregion) {
+    double Rs, Re, Ds, De;
+    get_skyregion (&Rs, &Re, &Ds, &De);
+    patch.Rmin = Rs;
+    patch.Rmax = Re;
+    patch.Dmin = Ds;
+    patch.Dmax = De;
+    Rmin = patch.Rmin - 182.0;
+    Rmax = patch.Rmax + 182.0;
+    Rmid = 0.5*(Rmin + Rmax);
+  }
+
+  if (trange < 0) {
+    tzero = tzero + trange;
+    trange = fabs (trange);
+  }
+
+  npts = 200;
+  ALLOCATE (subset, int, npts);
+  n = N = 0;
+  for (i = 0; i < Nimage; i++) {
+    if (TimeSelect && ((image[i].tzero < tzero) || (image[i].tzero+image[i].trate*image[i].NY > tzero + trange))) continue;
+    if (selection->useDisplay) {
+      if (!FindMosaicForImage (image, Nimage, i)) continue;
+      // first check if region center is in image
+      status = RD_to_XY (&X, &Y, Rmid, graph.coords.crval2, &image[i].coords);
+      if (status && (X >= 0) && (X < image[i].NX) && (Y >= 0) && (Y < image[i].NY)) goto in_region;
+
+      /* project this image to screen display coords */
+      x[0] = 0;           y[0] = 0;
+      x[1] = image[i].NX; y[1] = 0;
+      x[2] = image[i].NX; y[2] = image[i].NY;
+      x[3] = 0;           y[3] = image[i].NY;
+      InPic = flipped = FALSE;
+      for (j = 0; j < 4; j++) {
+	XY_to_RD (&r, &d, x[j], y[j], &image[i].coords);
+	/* use same side of 0,360 boundary for all corners */
+	if ((j == 0) && (r < Rmin)) flipped = TRUE; 
+	if ((j == 0) && (r > Rmax)) flipped = TRUE; 
+	while (flipped && (r < Rmid)) r+= 360.0;
+	while (flipped && (r > Rmid)) r-= 360.0;
+	status = RD_to_XY (&X, &Y, r, d, &graph.coords);
+	if (!status) continue;
+	if (X < graph.xmin) continue;
+	if (X > graph.xmax) continue;
+	if (Y < graph.ymin) continue;
+	if (Y > graph.ymax) continue;
+	goto in_region;
+	/** we miss any images which surround the region.  we are also
+	    missing the DIS images for which the corners don't touch
+	    the region, but which are needed for WRP images with
+	    corners touching the region **/
+      }
+      continue;
+    }
+    if (selection->useSkyregion) {
+      if (!FindMosaicForImage (image, Nimage, i)) continue;
+      /* project this image to screen display coords */
+      x[0] = 0;           y[0] = 0;
+      x[1] = image[i].NX; y[1] = 0;
+      x[2] = image[i].NX; y[2] = image[i].NY;
+      x[3] = 0;           y[3] = image[i].NY;
+      InPic = flipped = FALSE;
+      for (j = 0; j < 4; j++) {
+	XY_to_RD (&r, &d, x[j], y[j], &image[i].coords);
+	/* use same side of 0,360 boundary for all corners */
+	if ((j == 0) && (r < Rmin)) flipped = TRUE; 
+	if ((j == 0) && (r > Rmax)) flipped = TRUE; 
+	while (flipped && (r < Rmid)) r+= 360.0;
+	while (flipped && (r > Rmid)) r-= 360.0;
+	if (r < patch.Rmin) continue;
+	if (r > patch.Rmax) continue;
+	if (d < patch.Dmin) continue;
+	if (d > patch.Dmax) continue;
+	goto in_region;
+	/** we miss any images which surround the region.  we are also
+	    missing the DIS images for which the corners don't touch
+	    the region, but which are needed for WRP images with
+	    corners touching the region **/
+      }
+      continue;
+    }
+  in_region:
+    subset[n] = i;
+    n++;
+    if (n > npts - 1) {
+      npts += 200;
+      REALLOCATE (subset, int, npts);
+    }
+  }
+
+  REALLOCATE (subset, int, MAX (n, 1));
+  *Subset = subset;
+  *Nsubset = n;
+  return;
+}
+
+/* this routine fills the subset index with the list of selected images.
+   images may be selected on the basis of the region or on a time range 
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ImageSelection.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ImageSelection.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ImageSelection.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "dvoshell.h"
+
+/* db image table */
+static Image *image = NULL;
+static int *subset = NULL;
+static int Nimage = 0;
+static int Nsubset = 0;
+static Coords mosaic;
+
+/* load images based on parameters and region, etc */
+int SetImageSelection (int mosaicMode, SkyRegionSelection *selection) {
+
+  int TimeSelect;
+  time_t tzero, tend;
+
+  image = NULL;
+  subset = NULL;
+  
+  TimeSelect = GetTimeSelection (&tzero, &tend);
+
+  if (mosaicMode) {
+      /* mosaic defines a frame with 0,0 at the mosaic center, and 1 arcsec / pixel */
+      mosaic.crpix1 = mosaic.crpix2 = 0.0;
+      mosaic.cdelt1 = mosaic.cdelt2 = 1.0 / 3600;
+      mosaic.pc1_1  = mosaic.pc2_2  = 1.0;
+      mosaic.pc1_2  = mosaic.pc2_1  = 0.0;
+      mosaic.Npolyterms = 0;
+      strcpy (mosaic.ctype, "RA---SIN");
+  }
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, (double) tend - tzero, TimeSelect);
+  sort_image_subset (image, subset, Nsubset);
+  return (TRUE);
+}
+
+/* free loaded images */
+void FreeImageSelection () {
+  if (image != NULL) free (image);
+  if (subset != NULL) free (subset);
+  image = NULL;
+  subset = NULL;
+  return;
+}
+
+Image *MatchImage (unsigned int time, short int source) { 
+
+  int m;
+
+  m = match_image_subset (image, subset, Nsubset, time, source);
+  if (m == -1) return (NULL);
+  if (!FindMosaicForImage (image, Nimage, m)) return (NULL);
+  return (&image[m]);
+}
+
+Coords *MatchMosaic (unsigned int time, short int source) { 
+
+  int m;
+
+  m = match_image_subset (image, subset, Nsubset, time, source);
+  if (m == -1) return (NULL);
+  mosaic.crval1 = image[m].coords.crval1;
+  mosaic.crval2 = image[m].coords.crval2;
+  return (&mosaic);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/LoadImages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/LoadImages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/LoadImages.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "dvoshell.h"
+
+Image *LoadImages (int *nimage) {
+
+  int status;
+  char *catdir, filename[256];
+  Image *image;
+  FITS_DB db;
+  
+  /* VarConfig ("IMAGE_CATALOG", "%s", filename); */
+
+  catdir = GetCATDIR ();
+  sprintf (filename, "%s/Images.dat", catdir);
+
+  gfits_db_init (&db);
+  db.lockstate = LCK_SOFT;
+  db.timeout   = 120.0;
+
+  if (!gfits_db_lock (&db, filename)) {
+    gprint (GP_ERR, "error opening image catalog %s (1)\n", filename);
+    return (NULL);
+  }
+
+  if (db.dbstate == LCK_EMPTY) {
+    gprint (GP_ERR, "note: image catalog is empty\n");
+    ALLOCATE (image, Image, 1);
+    *nimage = 1;
+    return (image);
+  }
+
+  status = dvo_image_load (&db, TRUE, FALSE);
+  gfits_db_close (&db);
+
+  if (!status) {
+    gprint (GP_ERR, "problem loading image database table\n");
+    return (NULL);
+  }
+
+  image = gfits_table_get_Image (&db.ftable, nimage, &db.swapped);
+  return (image);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/Makefile	(revision 22322)
@@ -0,0 +1,138 @@
+default: dvo
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/dvo
+DATA    =       $(DESTDATA)/dvo
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata 
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# dvo user commands and support functions ########################
+funcs = \
+$(SRC)/init.$(ARCH).o            	\
+$(SRC)/ImageOps.$(ARCH).o		\
+$(SRC)/ImageSelection.$(ARCH).o	        \
+$(SRC)/LoadImages.$(ARCH).o		\
+$(SRC)/cmpReadFile.$(ARCH).o		\
+$(SRC)/compare.$(ARCH).o                \
+$(SRC)/match_image.$(ARCH).o		\
+$(SRC)/photometry.$(ARCH).o             \
+$(SRC)/dvomisc.$(ARCH).o		\
+$(SRC)/region_list.$(ARCH).o		\
+$(SRC)/dvomisc.$(ARCH).o		\
+$(SRC)/dbBooleanCond.$(ARCH).o		\
+$(SRC)/dbCheckStack.$(ARCH).o		\
+$(SRC)/dbCmdlineFields.$(ARCH).o	\
+$(SRC)/dbExtractAverages.$(ARCH).o	\
+$(SRC)/dbExtractMeasures.$(ARCH).o	\
+$(SRC)/dbFields.$(ARCH).o		\
+$(SRC)/dbRPN.$(ARCH).o			\
+$(SRC)/dbStackMath.$(ARCH).o		\
+$(SRC)/dbStackOps.$(ARCH).o		\
+$(SRC)/dvo.$(ARCH).o
+
+cmds = \
+$(SRC)/avextract.$(ARCH).o	  	\
+$(SRC)/badimages.$(ARCH).o	  	\
+$(SRC)/calextract.$(ARCH).o      	\
+$(SRC)/calmextract.$(ARCH).o     	\
+$(SRC)/catdir.$(ARCH).o             	\
+$(SRC)/ccd.$(ARCH).o             	\
+$(SRC)/cmatch.$(ARCH).o	  	\
+$(SRC)/cmd.$(ARCH).o             	\
+$(SRC)/cmpload.$(ARCH).o	  	\
+$(SRC)/cmpread.$(ARCH).o	  	\
+$(SRC)/ddmags.$(ARCH).o	  	\
+$(SRC)/detrend.$(ARCH).o	  	\
+$(SRC)/dmagaves.$(ARCH).o	  	\
+$(SRC)/dmagmeas.$(ARCH).o	  	\
+$(SRC)/dmags.$(ARCH).o		  	\
+$(SRC)/dmt.$(ARCH).o		  	\
+$(SRC)/elixir.$(ARCH).o                \
+$(SRC)/fitcolors.$(ARCH).o             \
+$(SRC)/fitsed.$(ARCH).o                \
+$(SRC)/gcat.$(ARCH).o		  	\
+$(SRC)/gimages.$(ARCH).o	  	\
+$(SRC)/gstar.$(ARCH).o		  	\
+$(SRC)/images.$(ARCH).o	  	\
+$(SRC)/imbox.$(ARCH).o		  	\
+$(SRC)/imdata.$(ARCH).o	  	\
+$(SRC)/imdense.$(ARCH).o	  	\
+$(SRC)/imextract.$(ARCH).o	  	\
+$(SRC)/imlist.$(ARCH).o	  	\
+$(SRC)/imphot.$(ARCH).o	  	\
+$(SRC)/imrough.$(ARCH).o	  	\
+$(SRC)/imsearch.$(ARCH).o	  	\
+$(SRC)/imstats.$(ARCH).o	  	\
+$(SRC)/lcat.$(ARCH).o		  	\
+$(SRC)/lcurve.$(ARCH).o	  	\
+$(SRC)/lightcurve.$(ARCH).o	  	\
+$(SRC)/mextract.$(ARCH).o	  	\
+$(SRC)/mmextract.$(ARCH).o	  	\
+$(SRC)/photcodes.$(ARCH).o	  	\
+$(SRC)/pmeasure.$(ARCH).o	  	\
+$(SRC)/paverage.$(ARCH).o	  	\
+$(SRC)/procks.$(ARCH).o	  	\
+$(SRC)/skycat.$(ARCH).o	  	\
+$(SRC)/skycoverage.$(ARCH).o	  	\
+$(SRC)/skyregion.$(ARCH).o	  	\
+$(SRC)/showtile.$(ARCH).o	  	\
+$(SRC)/simage.$(ARCH).o	  	\
+$(SRC)/subpix.$(ARCH).o  \
+$(SRC)/version.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a
+
+dvo: $(BIN)/dvo.$(ARCH)
+$(SRC)/dvo.$(ARCH).o : $(libs)
+$(BIN)/dvo.$(ARCH)   : $(funcs) $(cmds)
+
+install: $(DESTBIN)/dvo help modules
+
+help: clean-help cmd.basic.help cmd.data.help cmd.astro.help dvo.help
+
+$(funcs) $(cmds) $(libs) : $(INC)/dvoshell.h
+
+modules: dvo.modules
+
+.PHONY: dvo
+
+# deprecated functions: verify & delete
+#$(SRC)/catalog.$(ARCH).o	  	\
+#$(SRC)/abszero.$(ARCH).o \
+#$(SRC)/cals.$(ARCH).o \
+#$(SRC)/dumpmags.$(ARCH).o \
+#$(SRC)/extract.$(ARCH).o \
+#$(SRC)/gtypes.$(ARCH).o \
+#$(SRC)/photresid.$(ARCH).o \
+#$(SRC)/resid.$(ARCH).o \
+#$(SRC)/zeropts.$(ARCH).o
+#$(SRC)/objload.$(ARCH).o \ - make sure we have vect to tv
+#$(SRC)/ccdextract.$(ARCH).o \
+#$(SRC)/cmdextract.$(ARCH).o \
+#$(SRC)/dmagextract.$(ARCH).o \
+#$(SRC)/ddmagextract.$(ARCH).o \
+#$(SRC)/pcat.$(ARCH).o		  	\
+
+# future functions, not fully implemented
+#$(SRC)/detrend.$(ARCH).o \
+#$(SRC)/getxtra.$(ARCH).o \
+#$(SRC)/addxtra.$(ARCH).o \
+
+# functions that need to be updated
+#$(SRC)/gregions.$(ARCH).o \
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/addxtra.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/addxtra.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/addxtra.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "dvoshell.h"
+
+typedef struct {
+  char type[64];
+  char name[64];
+  char source[256];
+  char mode[64];
+  char value[64];
+  char range[64];
+  double R, D;
+  int averef;
+} Xtras;
+
+int addxtra (int argc, char **argv) {
+  
+  Source = (char *) NULL;
+  if (N = get_argument (argc, argv, "-source")) {
+    remove_argument (N, &argc, argv);
+    Source = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Name = (char *) NULL;
+  if (N = get_argument (argc, argv, "-name")) {
+    remove_argument (N, &argc, argv);
+    Name = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Mode = (char *) NULL;
+  if (N = get_argument (argc, argv, "-mode")) {
+    remove_argument (N, &argc, argv);
+    Mode = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Range = (char *) NULL;
+  if (N = get_argument (argc, argv, "-range")) {
+    remove_argument (N, &argc, argv);
+    Range = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: addxtra R D radius (type) (value)\n");
+    return (FALSE);
+  }
+  
+  /*
+
+  validate the input values (type, defines the needed options)
+  find catalog (based on r,d)
+  load catalog (need to load measures?)
+  find the object
+  find the xtra entry (sorted by ra/dec? sorted by averef?)
+  add new entry
+  save catalog 
+  */
+}
+
+/* 
+  addxtra R D dR type value -name (name) -source (source) -mode (mode) -range (range) 
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/aregion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/aregion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/aregion.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "dvoshell.h"
+# include "hstgsc.h"
+
+/* find region file which contains ra, dec */
+void aregion (GSCRegion *region, FILE *f, double ra, double dec, char *path) {
+  
+  char buffer[28800], temp[50], file[50];
+  double RA0, RA1, DEC0, DEC1;
+  int i, NBigDec, NLINES, done;
+  
+  while (ra < 0) { ra += 360.0; }
+  while (ra >= 360.0) { ra -= 360.0; }
+
+  if (dec >= 86.25) {
+    sprintf (file, "%s/n8230/pole.cpt", path);
+    region[0].DEC[0] = 86.25;
+    region[0].DEC[1] = 93.75;
+    region[0].RA[0] =  0.0;
+    region[0].RA[1] =  360.0;
+    strcpy (region[0].filename, file);
+    return;
+  }
+    
+  NBigDec = -1;
+  for (i = 0; i < 12; i++) {
+    if ((dec >= BigDecBounds[i]) && (dec < BigDecBounds[i+1])) {
+      NBigDec = i;
+      break;
+    }
+  }
+  if (NBigDec < 0) {
+    for (i = 13; i < 24; i++) {
+      if ((dec < BigDecBounds[i]) && (dec >= BigDecBounds[i+1])) {
+	NBigDec = i;
+	break;
+      }
+    }
+  }
+  if (NBigDec < 0) {
+    gprint (GP_ERR, "dec out of range: %f\n", dec);
+  }
+    
+  NLINES = 0;
+  for (i = 0; i < NBigDec; i++) {
+    NLINES += NDecLines[i];
+  }
+  fseek (f, 5*2880 + 48*NLINES, SEEK_SET);
+      
+  done = FALSE;
+  fread (buffer, 1, 48*NDecLines[NBigDec], f);
+  for (i = 0; !done && (i < NDecLines[NBigDec]); i++) {
+    strncpy (temp, &buffer[i*48], 48);
+    temp[49] = 0;
+    hstgsc_hms_to_deg (&RA0, &RA1, &DEC0, &DEC1, &temp[7]);
+    if (RA1 < RA0) RA1 += 360.0;
+    if ((dec >= 0) && (dec >= DEC0) && (dec < DEC1) && (ra >= RA0) && (ra < RA1)) {
+      done = TRUE;
+    }
+    if ((dec < 0) && (dec < DEC0) && (dec >= DEC1) && (ra >= RA0) && (ra < RA1)) {
+      done = TRUE;
+    }
+  }
+
+  if (!done) {
+    gprint (GP_ERR, "error in search: %f %f\n", ra, dec);
+    exit (0);
+  }
+  temp[5] = 0;
+  sprintf (file, "%s/%s/%s.cpt", path, Dec2Sections[NBigDec],&temp[1]);
+  if (DEC0 < DEC1) {
+    region[0].DEC[0] = DEC0;
+    region[0].DEC[1] = DEC1;
+  } else {
+    region[0].DEC[0] = DEC1;
+    region[0].DEC[1] = DEC0;
+  }     
+  region[0].RA[0] = RA0;
+  region[0].RA[1] = RA1;
+  strcpy (region[0].filename, file);
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/avextract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/avextract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/avextract.c	(revision 22322)
@@ -0,0 +1,189 @@
+# include "dvoshell.h"
+
+int avextract (int argc, char **argv) {
+  
+  int i, j, n, m, N, Npts, NPTS, last, next, state, Nfields, Nreturn, Ncstack, Nstack;
+  int Nsecfilt, mode, VERBOSE;
+  char **cstack, name[1024];
+  float *values;
+  void *Signal;
+
+  Catalog catalog;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  PhotCode *code;
+  Vector **vec;
+  dbField *fields;
+  dbStack *stack;
+
+  /* defaults */
+  skylist = NULL;
+  selection = NULL;
+  code = NULL;
+  mode = MAG_AVE;
+  fields = NULL;
+  stack = NULL;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+
+  // command-line is of the form: avextract field,field, field [where (field op value)...]
+
+  // parse the fields to be extracted and returned
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_AVERAGE, &last, &Nfields);
+  if (fields == NULL) return (FALSE);
+
+  // examine line for 'where' or 'match to'.  'match to' is forbidden
+  state = dbCmdlineConditions (argc, argv, last, &next);
+  if (state == DVO_DB_CMDLINE_ERROR) goto escape;
+  if (state == DVO_DB_CMDLINE_IS_MATCH) goto escape; // not allowed for mextract
+
+  // parse the remainder of the line as a boolean math expression
+  cstack = isolate_elements (argc-last, &argv[last], &Ncstack);
+  
+  // construct the db Boolean math stack (frees cstack)
+  stack = dbRPN (Ncstack, cstack, &Nstack);
+
+  // add the skyregion limits to the where statement (or create)
+  dbAstroRegionLimits (&stack, &Nstack, selection, DVO_TABLE_AVERAGE);
+
+  // parse stack elements into fields and scalars as needed
+  Nreturn = Nfields; 
+  dbCheckStack (stack, Nstack, DVO_TABLE_AVERAGE, &fields, &Nfields);
+  // XXX handle errors
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* create output storage vectors */
+  ALLOCATE (values, float, Nfields);
+  ALLOCATE (vec, Vector *, Nreturn);
+  for (i = 0; i < Nreturn; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name, "v_%s", fields[i].name);
+    } else {
+      sprintf (name, "%s", fields[i].name);
+    }
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+  }
+
+  Npts = 0;
+  NPTS = 1;
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[i];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s (%d of %d)\n", catalog.filename, i, skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    for (j = 0; (j < catalog.Naverage) && !interrupt; j++) {
+      // extract the relevant values
+      // XXX for measure values, this could be optimized for one loop over measures...
+
+      m = catalog.average[j].measureOffset;
+      dbExtractAveragesInit (); // reset counters for saved fields 
+      for (n = 0; n < Nfields; n++) {
+	values[n] = dbExtractAverages (&catalog.average[j], &catalog.secfilt[j*Nsecfilt], &catalog.measure[m], &fields[n]);
+      }
+      // test the conditional statement
+      if (!dbBooleanCond (stack, Nstack, values)) continue;
+      for (n = 0; n < Nreturn; n++) {
+	vec[n][0].elements[Npts] = values[n];
+      }
+      Npts++;
+      if (Npts >= NPTS) {
+	NPTS += 2000;
+	for (n = 0; n < Nreturn; n++) {
+	  REALLOCATE (vec[n][0].elements, float, NPTS);
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+  for (n = 0; n < Nreturn; n++) {
+    vec[n][0].Nelements = Npts;
+    REALLOCATE (vec[n][0].elements, float, MAX(1,Npts));
+  }
+
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  free (stack);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+ escape:
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  free (stack);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: avextract field[,field,field...] where (expression)\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, " USAGE: avextract field[,field,field...] where (expression)\n");
+    gprint (GP_ERR, "  RA : right ascension (J2000)\n");
+    gprint (GP_ERR, "  DEC : declination \n");
+    gprint (GP_ERR, "  dRA : ra scatter \n");
+    gprint (GP_ERR, "  dDEC : dec scatter\n");
+    gprint (GP_ERR, "  uRA : proper motion in ra\n");
+    gprint (GP_ERR, "  uDEC : proper motion in dec\n");
+    gprint (GP_ERR, "  duRA : proper motion error in ra\n");
+    gprint (GP_ERR, "  duDEC : proper motion error in dec\n");
+    gprint (GP_ERR, "  PAR : parallax\n");
+    gprint (GP_ERR, "  dPAR : parallax error \n");
+    gprint (GP_ERR, "  nmeas : number of measurements\n");
+    gprint (GP_ERR, "  nmiss : number of non-detections\n");
+    gprint (GP_ERR, "  xp : positional chi-square\n");
+    gprint (GP_ERR, "  flag : object flags\n");
+    gprint (GP_ERR, "  photocode:ave : average magnitude for photcode\n");
+    gprint (GP_ERR, "  photocode:ref : reference magnitude system for photcode\n");
+    gprint (GP_ERR, "  photocode:inst : first instrumental magnitude for photcode\n");
+    gprint (GP_ERR, "  photocode:cat : first catalog magnitude for photcode\n");
+    gprint (GP_ERR, "  photocode:sys : first system magnitude for photcode\n");
+    gprint (GP_ERR, "  photocode:rel : first relative magnitude for photcode\n");
+    gprint (GP_ERR, "  photocode:cal : first calibrated magnitude for photcode \n");
+    gprint (GP_ERR, "  photcode:err : magnitude error for photcode\n");
+    gprint (GP_ERR, "  photcode:chipsq : chi-square of magnitude fit\n");
+    gprint (GP_ERR, "  type : dophot type (unused)\n");
+    gprint (GP_ERR, "  typefrac : dophot type fraction (unused)\n");
+    gprint (GP_ERR, "  photcode:ncode : number of measurements in photcode \n");
+    gprint (GP_ERR, "  photcode:nphot : number of measurements used for photcode average\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " avextract --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/badimages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/badimages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/badimages.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "dvoshell.h"
+
+static int badim_int = FALSE;
+void badim_escape () {
+  badim_int = TRUE;
+}
+
+int badimages (int argc, char **argv) {
+  
+  int i, Nimage, entry, First, Cross;
+  float *ptr;
+  double nominal, big, small, value;
+  Image *image;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: badimages entry value\n");
+    gprint (GP_ERR, "   OR: badimages -image N\n");
+    return (FALSE);
+  }
+  
+  image = LoadImages (&Nimage);
+
+  Cross = FALSE;
+  First = FALSE;
+  nominal = 1;
+  if (!strcmp (argv[1], "-image")) {
+    First = TRUE;
+    entry = atof(argv[2]);
+  } else {
+    entry = atof(argv[1]);
+    nominal = atof(argv[2]);
+    if (!strcasecmp (argv[1], "x")) {
+      Cross = TRUE;
+    }
+  }
+  
+  if (First) {
+    ptr = &image[entry].coords.crpix1;
+    for (i = 0; i < 22; i++) {
+      gprint (GP_LOG, "%2d: %g\n", i, ptr[i]);
+    }
+    value = image[entry].coords.pc1_1*image[entry].coords.pc2_2 + image[entry].coords.pc1_2*image[entry].coords.pc2_1;
+    gprint (GP_LOG, " x: %g\n", value);
+    return (TRUE);
+  }
+  
+  big = nominal * 1.05;
+  small = nominal / 1.05;
+  if (big < small) {
+    double tmp;
+    tmp = big; big = small; small = tmp;
+  }
+  
+  badim_int = FALSE;
+  if (Cross) {
+    for (i = 0; (i < Nimage) && !badim_int; i++) {
+      value = image[i].coords.pc1_1*image[i].coords.pc2_2 + image[i].coords.pc1_2*image[i].coords.pc2_1;
+      if ((value > big) || (value < small)) {
+	gprint (GP_LOG, "%5d %s: %d %g\n", i, image[i].name, image[i].tzero, value);
+      }
+    }
+  } else {
+    for (i = 0; (i < Nimage) && !badim_int; i++) {
+      ptr = &image[i].coords.crpix1;
+      if ((ptr[entry] > big) || (ptr[entry] < small)) {
+	gprint (GP_LOG, "%5d %s: %d %g\n", i, image[i].name, image[i].tzero, ptr[entry]);
+      }
+    }
+  }
+  
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/calextract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/calextract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/calextract.c	(revision 22322)
@@ -0,0 +1,141 @@
+# include "dvoshell.h"
+
+enum {Nd, Nm, NC, NR, ND, Np, Nc, Nt, Nx, Nd1, Nd2, NVEC};
+
+int calextract (int argc, char **argv) {
+  
+  int i, N, Nr, mode[2];
+  int Nsecfilt, NSTAR;
+
+  PhotCode *code[2];
+  Catalog catalog;
+  Vector **vec;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+
+  /* these need to be freed in the end */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+  vec = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* command line arguments */
+  SetSelectionParam (0);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 2)) goto usage;
+
+  if (argc != 4) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  /* code.type must be PHOT_REF */
+
+  /* one unique value per star */
+  N = 0;
+  NSTAR = 1;
+  ALLOCATE (vec, Vector *, NVEC);
+  if ((vec[Nd] 	= SelectVector ("cal:dmag",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nm] 	= SelectVector ("cal:mag",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NC] 	= SelectVector ("cal:color",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NR] 	= SelectVector ("cal:ra",       ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[ND] 	= SelectVector ("cal:dec",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Np] 	= SelectVector ("cal:nphot",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nc] 	= SelectVector ("cal:ncode",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  // if ((vec[Nt] 	= SelectVector ("cal:ncrit",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nx] 	= SelectVector ("cal:chisq",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd1] = SelectVector ("cal:dm1",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd2] = SelectVector ("cal:dm2",      ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  for (Nr = 0; Nr < skylist[0].Nregions; Nr++) {
+    if (Nr && !(Nr % 500)) { gprint (GP_ERR, "."); }
+
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[Nr];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    # if (0)
+    /* extract values, assign to vectors */
+    for (i = 0; i < catalog.Naverage; i++) {
+      if (i && !(i % 10000)) { gprint (GP_ERR, ","); }
+      m = catalog.average[i].offset;
+
+      if (code[0][0].c1 && code[0][0].c2 && !PhotColor (&catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], code[0][0].c1, code[0][0].c2, &color)) continue;
+
+      /* find data for filter 2 (PHOT_REF) */
+      M2 = NAN;
+      dM2 = NAN;
+      for (j = 0; j < catalog.average[i].Nm; j++) {
+	if (catalog.measure[m+j].photcode != code[1][0].code) continue;
+	M2 = PhotCat  (&catalog.measure[m+j]);
+	dM2 = catalog.measure[m+j].dM;
+      }	
+      if (isnan(M2)) continue;
+
+      /* find data for filter 1 */
+      M1 = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_MAG);
+      if (isnan(M1)) continue;
+
+      vec[Nd ][0].elements[N] = M1 - M2;
+      vec[Nm ][0].elements[N] = M2;
+      vec[NC ][0].elements[N] = color;
+      vec[NR ][0].elements[N] = catalog.average[i].R;
+      vec[ND ][0].elements[N] = catalog.average[i].D;
+      vec[Nd1][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_dMAG);
+      vec[Nd2][0].elements[N] = dM2;
+      vec[Nx ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_Xm);
+      vec[Nc ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_NCODE);
+      vec[Np ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_NPHOT);
+      // vec[Nt ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_NCRIT);
+      N ++;
+      if (N == NSTAR) {
+	NSTAR += 100;
+	for (j = 0; j < NVEC; j++) {
+	  REALLOCATE (vec[j][0].elements, float, NSTAR);
+	}
+      }
+    }
+    # endif
+    dvo_catalog_free (&catalog);
+  }
+
+  for (i = 0; i < NVEC; i++) {
+    vec[i][0].Nelements = N;
+  }
+
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+  
+usage:
+  gprint (GP_ERR, "USAGE: calextract F - F\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  for (i = 0; i < NVEC; i++) {
+    DeleteVector (vec[i]);
+  }
+  free (vec);
+
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/calmextract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/calmextract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/calmextract.c	(revision 22322)
@@ -0,0 +1,186 @@
+# include "dvoshell.h"
+
+enum {Nd, Nm1, Nm2, Nc, Ns, Nt, Nz, NR, ND, Nxc, Nyc, Nxm, Nym, NT, NP, Nd1, Nd2, NVEC};
+int ConcatMeasures (Vector *vec, PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param, int Nin);
+
+int calmextract (int argc, char **argv) {
+  
+  int i, k, N, Nr, mode[2];
+  int NSTAR, Nstar, Nsecfilt;
+
+  Catalog catalog;
+  PhotCode *code[2];
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector **vec;
+
+  /* these need to be freed in the end */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+  vec = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* command line arguments */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 2)) goto usage;
+
+  /* interpret required command-line arguments: calmextract F1 - F2 */
+  if (argc != 4) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* returned vectors are dmag, mag, color, time, airmass, ra, dec, x, y, exptime */
+  N = 0;
+  Nstar = 0;
+  NSTAR = 100;
+  ALLOCATE (vec, Vector *, NVEC);
+  if ((vec[Nd ] = SelectVector ("cal:dmag",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nm1] = SelectVector ("cal:mag1",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nm2] = SelectVector ("cal:mag2",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nc ] = SelectVector ("cal:color",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Ns ] = SelectVector ("cal:star",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nt ] = SelectVector ("cal:time",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nz ] = SelectVector ("cal:airmass",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NR ] = SelectVector ("cal:ra",       ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[ND ] = SelectVector ("cal:dec",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nxc] = SelectVector ("cal:xccd",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nyc] = SelectVector ("cal:yccd",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nxm] = SelectVector ("cal:xmosaic",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nym] = SelectVector ("cal:ymosaic",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NT ] = SelectVector ("cal:exptime",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NP ] = SelectVector ("cal:photcode", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd1] = SelectVector ("cal:dm1",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd2] = SelectVector ("cal:dm2",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  for (k = 0; k < NVEC; k++) {
+    REALLOCATE (vec[k][0].elements, float, NSTAR);
+    vec[k][0].Nelements = 0;
+  }
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+  if (!SetImageSelection (TRUE, selection)) goto escape;
+
+  for (Nr = 0; Nr < skylist[0].Nregions; Nr++) {
+    if (Nr && !(Nr % 500)) { gprint (GP_ERR, "."); }
+
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[Nr];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    # if (0)
+    /* extract values, assign to vectors */
+    for (i = 0; i < catalog.Naverage; i++) {
+      m = catalog.average[i].offset;
+
+      /* PRI/SEC must have data for color term */
+      if (code[0][0].c1 && code[0][0].c2 && !PhotColor (&catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], code[0][0].c1, code[0][0].c2, &color)) continue;
+
+      /* find data for filter 2 (REF) */
+      M2 = NAN;
+      dM2 = NAN;
+      for (j = 0; j < catalog.average[i].Nm; j++) {
+	if (catalog.measure[m+j].photcode != code[1][0].code) continue;
+	M2 = PhotCat  (&catalog.measure[m+j]); 
+	dM2 = catalog.measure[m+j].dM;
+      }	
+      if (isnan(M2)) continue;
+      
+      /* find data for filter 1 */
+      M1 = ExtractMeasures (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1, MEAS_MAG);
+      if (N1 == 0) goto skip;
+
+      /* extend storage vectors to take new data, if needed */
+      if (N + N1 >= NSTAR) {
+	NSTAR += N1 + 100;
+	for (k = 0; k < NVEC; k++) {
+	  REALLOCATE (vec[k][0].elements, float, NSTAR);
+	}
+      }
+
+      ConcatMeasures (vec[Nt ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_TIME); 
+      ConcatMeasures (vec[Nz ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_AIRMASS); 
+      ConcatMeasures (vec[NT ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_EXPTIME); 
+      ConcatMeasures (vec[NP ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_PHOTCODE); 
+      // ConcatMeasures (vec[Nd1], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_dMAG); 
+      ConcatMeasures (vec[Nxc], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_XCCD); 
+      ConcatMeasures (vec[Nyc], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_YCCD); 
+      ConcatMeasures (vec[Nxm], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_XMOSAIC); 
+      ConcatMeasures (vec[Nym], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_YMOSAIC); 
+
+      for (j = 0; j < N1; j++, N++) {
+	vec[Nd ][0].elements[N] = M1[j] - M2;
+	vec[Nm1][0].elements[N] = M1[j];
+	vec[Nm2][0].elements[N] = M2;
+	vec[Nd2][0].elements[N] = dM2;
+	vec[Nc ][0].elements[N] = color;
+	vec[Ns ][0].elements[N] = Nstar;
+	vec[NR ][0].elements[N] = catalog.average[i].R;
+	vec[ND ][0].elements[N] = catalog.average[i].D;
+      }
+      Nstar ++; 
+    skip:
+      if (M1 != NULL) free (M1);
+    }
+    # endif
+    dvo_catalog_free (&catalog);
+  }
+
+  for (i = 0; i < NVEC; i++) {
+    vec[i][0].Nelements = N;
+  }
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  FreeImageSelection ();
+  return (TRUE);
+  
+usage:
+  gprint (GP_ERR, "USAGE: dmags F - F : measure.param\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  FreeImageSelection ();
+  for (i = 0; i < NVEC; i++) {
+    DeleteVector (vec[i]);
+  }
+  free (vec);
+  return (FALSE);
+}
+
+int ConcatMeasures (Vector *vec, PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int Nin, int param) {
+
+  int i, Ns, N;
+  double *value;
+
+  value = ExtractMeasures (code, mode, average, secfilt, measure, &N, param); 
+  if (N != Nin) {
+    gprint (GP_ERR, "error!\n");
+    return (FALSE);
+  }
+
+  Ns = vec[0].Nelements;
+  for (i = 0; i < N; i++) {
+    vec[0].elements[Ns+i] = value[i];
+  }
+  vec[0].Nelements = Ns + N;
+
+  free (value);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/catalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/catalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/catalog.c	(revision 22322)
@@ -0,0 +1,407 @@
+# include "dvoshell.h"
+# define NBYTES 160000
+# define BYTES_STAR 23
+# define BLOCK 1000
+# define DNSTARS 1000
+
+# define MAGSCALE 0
+# define NUMSCALE 1
+# define MISSCALE 2
+
+// XXX EAM : should this function be dropped? 
+int catlog (int argc, char **argv) {
+  
+  FILE *f;
+  Catalog catalog;
+  Vector Xvec, Yvec, Zvec;
+  int i, N, Nm, Nn, NN, Nbytes, nbytes, Bytes_Star;
+  int Ar, Ad, Am, InRegion, GSC, ASCII, DVO, FIXED;
+  char filename[128];
+  double Mz, Mr, Nz, Nr;
+  int clip, mode, IDclip, IDchoice, LimExclude;
+  RegionFile *regions;
+  int j, Nregions;
+  double Radius, Rmin, Rmax;
+  Graphdata graphmode;
+  double epoch, current_epoch;
+  char gscdir[256], catdir[256];
+  int Ngraph;
+
+  if (!GetGraph (&graphmode, NULL, NULL)) return (FALSE);
+
+  VarConfig ("GSCDIR", "%s", gscdir);
+  VarConfig ("CATDIR", "%s", catdir);
+
+  Mz = 17.0;
+  Mr = -5.0;
+  mode = MAGSCALE;
+  clip = FALSE;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  regions = (RegionFile *) NULL;
+  f = (FILE *) NULL;
+  Nz = Nr = Am = Ar = Ad = 0;
+  /* either MagScale or NumScale, whichever is first is scale */
+  Nm = get_argument (argc, argv, "-m");
+  Nn = get_argument (argc, argv, "+n");
+  NN = get_argument (argc, argv, "-n");
+  if (NN && Nn) {
+    gprint (GP_ERR, "can't mix meas and miss scaling\n");
+    return (FALSE);
+  }
+ 
+  if (Nm)
+    mode = MAGSCALE;
+  if (Nn)
+    mode = NUMSCALE;
+  if (NN)
+    mode = MISSCALE;
+    
+  if (Nm && Nn) {
+    clip = TRUE;
+    if (Nm < Nn) 
+      mode = MAGSCALE;
+    else 
+      mode = NUMSCALE;
+  }
+  if (Nm && NN) {
+    clip = TRUE;
+    if (Nm < NN) 
+      mode = MAGSCALE;
+    else 
+      mode = MISSCALE;
+  }
+   
+  current_epoch = 2000.0;
+  epoch = 2000.0;
+  if ((N = get_argument (argc, argv, "-e"))) {
+    remove_argument (N, &argc, argv);
+    epoch  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  IDchoice = 0;
+  IDclip = FALSE;
+  if ((N = get_argument (argc, argv, "-ID"))) {
+    remove_argument (N, &argc, argv);
+    IDchoice  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    IDclip = TRUE;
+  }
+
+  LimExclude = FALSE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    LimExclude = TRUE;
+  }
+
+  if ((Nm = get_argument (argc, argv, "-m"))) {
+    remove_argument (Nm, &argc, argv);
+    Mr  = atof(argv[Nm]);
+    remove_argument (Nm, &argc, argv);
+    Mz = atof(argv[Nm]);
+    Mr = Mr - Mz;
+    remove_argument (Nm, &argc, argv);
+  }
+
+  if ((Nn = get_argument (argc, argv, "+n"))) {
+    remove_argument (Nn, &argc, argv);
+    Nz  = atof(argv[Nn]);
+    remove_argument (Nn, &argc, argv);
+    Nr = atof(argv[Nn]) - Nz;
+    remove_argument (Nn, &argc, argv);
+  }
+
+  if ((Nn = get_argument (argc, argv, "-n"))) {
+    remove_argument (Nn, &argc, argv);
+    Nz  = atof(argv[Nn]);
+    remove_argument (Nn, &argc, argv);
+    Nr = atof(argv[Nn]) - Nz;
+    remove_argument (Nn, &argc, argv);
+  }
+
+  InRegion = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    InRegion = TRUE;
+  }
+
+  Bytes_Star = 0;
+  ASCII = FALSE;
+  DVO = TRUE;
+  GSC = FALSE;
+  FIXED = FALSE;
+  if ((N = get_argument (argc, argv, "-g"))) {
+    remove_argument (N, &argc, argv);
+    GSC = TRUE;
+    ASCII = FALSE;
+    DVO = FALSE;
+  }
+
+  if ((N = get_argument (argc, argv, "-a"))) {
+    remove_argument (N, &argc, argv);
+    ASCII = TRUE;
+    GSC = FALSE;
+    DVO = FALSE;
+    Ar = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Ad = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Am = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    if ((N = get_argument (argc, argv, "-f"))) {
+      remove_argument (N, &argc, argv);
+      FIXED = TRUE;
+      ASCII = FALSE;
+      Bytes_Star = atof(argv[N]);
+      remove_argument (N, &argc, argv);
+    }
+  }
+
+  
+  if ((InRegion || (argc != 2)) && (!InRegion || (argc != 1))) {
+    gprint (GP_ERR, "USAGE: catalog (filename / -all) [-m M M] [-n N N] [-g] [-a RA DEC MAG] \n");
+    return (FALSE);
+  }
+  
+  if (InRegion) {
+    Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+    regions = find_regions (graphmode.coords.crval1, graphmode.coords.crval2, Radius, &Nregions);
+  } else {
+    Nregions = 1;
+  }
+  
+  for (j = 0; j < Nregions; j++) {
+    catalog.average = 0;
+    
+    /* Load in data from an ASCII file list of ra, dec, mag */
+    if (ASCII) {
+      char *tbuffer;
+      int nstar, NSTARS;
+      double R, D, M;
+      
+      f = fopen (argv[1], "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "ERROR: can't open catalog file: %s\n", argv[1]);
+	return (FALSE);
+      }
+      
+      nstar = 0;
+      NSTARS = DNSTARS;
+      ALLOCATE (tbuffer, char, 1024);
+      ALLOCATE (catalog.average, Average, NSTARS);
+      while (scan_line (f, tbuffer) != EOF) {
+	dparse (&R, Ar, tbuffer);
+	dparse (&D, Ad, tbuffer);
+	dparse (&M, Am, tbuffer);
+	catalog.average[nstar].R = R;
+	catalog.average[nstar].D = D;
+	catalog.average[nstar].M = M;
+	nstar++;
+	if (nstar == NSTARS - 1) {
+	  NSTARS += DNSTARS;
+	  REALLOCATE (catalog.average, Average, NSTARS);
+	}
+      }
+      fclose (f);
+      free (tbuffer);
+      REALLOCATE (catalog.average, Average, nstar);
+      catalog.Naverage = nstar;
+
+      if (epoch != current_epoch) {
+	cprecess (catalog.average, catalog.Naverage, epoch, current_epoch);
+      }
+
+    }
+    
+    /* Load in data from an ASCII file list of ra, dec, mag */
+    if (FIXED) {
+      char *tbuffer;
+      int nstar, NSTARS;
+      double R, D, M;
+      
+      f = fopen (argv[1], "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "ERROR: can't open catalog file: %s\n", argv[1]);
+	return (FALSE);
+      }
+      
+      nstar = 0;
+      NSTARS = DNSTARS;
+      ALLOCATE (tbuffer, char, (BLOCK*Bytes_Star));
+      ALLOCATE (catalog.average, Average, NSTARS);
+      Nbytes = BLOCK*Bytes_Star;
+      while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+	for (i = 0; i < nbytes / Bytes_Star; i++) {
+	  dparse (&R, Ar, &tbuffer[i*Bytes_Star]);
+	  dparse (&D, Ad, &tbuffer[i*Bytes_Star]);
+	  dparse (&M, Am, &tbuffer[i*Bytes_Star]);
+	  catalog.average[nstar].R = R;
+	  catalog.average[nstar].D = D;
+	  catalog.average[nstar].M = M;
+	  nstar++;
+	  if (nstar == NSTARS - 1) {
+	    NSTARS += DNSTARS;
+	    REALLOCATE (catalog.average, Average, NSTARS);
+	  }
+	}
+      }
+      fclose (f);
+      free (tbuffer);
+      REALLOCATE (catalog.average, Average, nstar);
+      catalog.Naverage = nstar;
+
+      if (epoch != current_epoch) {
+	cprecess (catalog.average, catalog.Naverage, epoch, current_epoch);
+      }
+
+    }
+    
+    /* load data from the GSC files */
+    if (GSC) {
+      char *tbuffer;
+      int nstar, NSTARS;
+      double R, D, M;
+      
+      if (InRegion) {
+	sprintf (filename, "%s/%s", gscdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s", gscdir, argv[1]);
+      }
+      
+      f = fopen (filename, "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "no stars in %s, skipping\n", filename);
+	continue;
+	/* return (FALSE); */
+      }
+      
+      nstar = 0;
+      NSTARS = DNSTARS;
+      ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+      ALLOCATE (catalog.average, Average, NSTARS);
+      Nbytes = BLOCK*BYTES_STAR;
+      while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+	for (i = 0; i < nbytes / BYTES_STAR; i++) {
+	  dparse (&R, 1, &tbuffer[i*BYTES_STAR]);
+	  dparse (&D, 2, &tbuffer[i*BYTES_STAR]);
+	  dparse (&M, 3, &tbuffer[i*BYTES_STAR]);
+	  catalog.average[nstar].R = R;
+	  catalog.average[nstar].D = D;
+	  catalog.average[nstar].M = M;
+	  nstar++;
+	  if (nstar == NSTARS - 1) {
+	    NSTARS += DNSTARS;
+	    REALLOCATE (catalog.average, Average, NSTARS);
+	  }
+	}
+      }
+      fclose (f);
+      free (tbuffer);
+      REALLOCATE (catalog.average, Average, nstar);
+      catalog.Naverage = nstar;
+    }
+  
+    /* load data from the photometry database files */
+    if (DVO) {
+      
+      if (InRegion) {
+	sprintf (filename, "%s/%s", catdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s", catdir, argv[1]);
+      }
+      
+      /* lock, load, unlock catalog */
+      catalog.filename = filename;
+      catalog.catflags = LOAD_AVES;
+
+      // an error exit status here is a significant error
+      if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+	  fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+	  exit (2);
+      }
+      dvo_catalog_unlock (&catalog);
+    }
+    
+    /* data has been loaded, get ready to plot it */
+    Xvec.Nelements = catalog.Naverage;
+    Yvec.Nelements = catalog.Naverage;
+    Zvec.Nelements = catalog.Naverage;
+    ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+    ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+    ALLOCATE (Zvec.elements, float, Zvec.Nelements);
+    /* project stars to screen display coords */
+    Xvec.Nelements = 0;
+    switch (mode) {
+    case (MAGSCALE):
+      for (N = i = 0; i < catalog.Naverage; i++) {
+	if (clip && ((catalog.average[i].Nm < Nz) || (catalog.average[i].Nm > Nr+Nz))) 
+	  continue;
+	if (IDclip && (catalog.average[i].code != IDchoice))
+	  continue;
+	Zvec.elements[N] = MIN (1.0, MAX (0.01, (catalog.average[i].M - Mz) / Mr));
+	if (LimExclude && (Zvec.elements[N] > 0.99)) continue;
+	if (Zvec.elements[N] < 0.011) continue;
+	while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+	while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+	if (fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], catalog.average[i].R, catalog.average[i].D, &graphmode.coords)) N ++;
+      }
+      break;
+    case (NUMSCALE):
+      for (N = i = 0; i < catalog.Naverage; i++) {
+	if (clip && ((catalog.average[i].M > Mz) || (catalog.average[i].M < Mr+Mz))) 
+	  continue;
+	if (IDclip && (catalog.average[i].code != IDchoice))
+	  continue;
+	Zvec.elements[N] = MIN (1.0, MAX (0.01, (catalog.average[i].Nm - Nz) / Nr));
+	if (LimExclude && (Zvec.elements[N] == 1.0)) continue;
+	if (Zvec.elements[N] == 0.01) 
+	  continue;
+	while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+	while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+	if (fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], catalog.average[i].R, catalog.average[i].D, &graphmode.coords)) N++;
+      }
+      break;
+    case (MISSCALE):
+      for (N = i = 0; i < catalog.Naverage; i++) {
+	if (clip && ((catalog.average[i].M > Mz) || (catalog.average[i].M < Mr+Mz))) 
+	  continue;
+	if (IDclip && (catalog.average[i].code != IDchoice))
+	  continue;
+	Zvec.elements[N] = MIN (1.0, MAX (0.01, (catalog.average[i].Nn - Nz) / Nr));
+	if (LimExclude && (Zvec.elements[N] == 1.0)) continue;
+	if (Zvec.elements[N] == 0.01) 
+	  continue;
+	while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+	while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+	if (fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], catalog.average[i].R, catalog.average[i].D, &graphmode.coords)) N++;
+      }
+      break;
+    }
+
+    Zvec.Nelements = Yvec.Nelements = Xvec.Nelements = N;
+    REALLOCATE (Xvec.elements, float, MAX (Xvec.Nelements, 1));
+    REALLOCATE (Yvec.elements, float, MAX (Yvec.Nelements, 1));
+    REALLOCATE (Zvec.elements, float, MAX (Zvec.Nelements, 1));
+    
+    graphmode.style = 2; /* set style to points */
+    graphmode.size = -1; /* point size determined by Zvec */
+    graphmode.etype = 0; /* no errorbars */
+    PrepPlotting (N, &graphmode);
+    
+    PlotVector (N, Xvec.elements, "x");
+    PlotVector (N, Yvec.elements, "y");
+    PlotVector (N, Zvec.elements, "z");
+    
+    free (Xvec.elements);
+    free (Yvec.elements);
+    free (Zvec.elements);
+
+    if (catalog.average != 0) free (catalog.average);
+
+  }
+  return (TRUE);
+
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/catdir.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/catdir.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/catdir.c	(revision 22322)
@@ -0,0 +1,38 @@
+# include "dvoshell.h"
+
+int catdir_define (int argc, char **argv) {
+  
+  char *current;
+  int status, N, VERBOSE;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: catdir (name)\n");
+    gprint (GP_ERR, "       (name) may be a path or 'default'\n");
+    current = GetCATDIR ();
+    if (current == NULL) {
+      gprint (GP_ERR, "catdir not defined\n");
+    } else {
+      gprint (GP_ERR, "current: %s\n", current);
+    }
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "default")) {
+    status = SetCATDIR (NULL, VERBOSE);
+  } else {
+    status = SetCATDIR (argv[1], VERBOSE);
+  }
+
+  if (!status) {
+    gprint (GP_ERR, "invalid / undefined CATDIR\n");
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ccd.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ccd.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ccd.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include "dvoshell.h"
+
+int ccd (int argc, char **argv) {
+  
+  double *M1, *M2;
+  int i, m, k, Npts, NPTS, N;
+  int N1, N2, i1, i2, mode[4];
+  int Nsecfilt, KeepNulls;
+
+  Catalog catalog;
+  PhotCode *code[4];
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line options */
+  if (argc != 8) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (strcmp (argv[6], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[7], &code[3], &mode[3])) return (FALSE);
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M2 = NULL;
+      m = catalog.average[i].measureOffset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (&code[0], &mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M2 = ExtractDMag (&code[2], &mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N2);
+      if (N2 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M2, double, 1);
+	  N2 = 1;
+	  M2[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i2 = 0; i2 < N2; i2++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M2[i2];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M2 != NULL) free (M2);
+    }
+    dvo_catalog_free (&catalog);
+  }
+
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: ccd F - F : F - F\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmatch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmatch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmatch.c	(revision 22322)
@@ -0,0 +1,82 @@
+# include "dvoshell.h"
+
+int cmatch (int argc, char **argv) {
+  
+  Catalog catalog1, catalog2;
+  char filename[128];
+  double radius;
+  Vector *rvec, *dvec, *mvec, *drvec, *ddvec, *dmvec;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: cmatch file radius (RA) (DEC) (Mag) (dRA) (dDEC) (dMag)\n");
+    gprint (GP_ERR, "       match a set of object coordinates with a DVO db table\n");
+    return (FALSE);
+  }
+
+  /*** this function is not well-defined.  re-assess it and re-code it ***/
+  gprint (GP_ERR, "disabled for now\n");
+  return (FALSE);
+
+  radius = atof (argv[2]);
+
+  if ((rvec  = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((dvec  = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((mvec  = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((drvec = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ddvec = SelectVector (argv[7], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((dmvec = SelectVector (argv[8], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* load data from the photometry database file */
+  catalog1.filename = filename;
+  catalog1.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog1, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog1.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog1);
+  gprint (GP_ERR, "read %d stars from phot catalog file %s\n", catalog1.Naverage, filename);
+
+  /* this is for loading from a text file, presumably hstgsc or usno
+     replace this with references to the ra and dec vectors?
+  nstar = 0;
+  NSTARS = DNSTARS;
+  ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+  ALLOCATE (catalog2.average, Average, NSTARS);
+  Nbytes = BLOCK*BYTES_STAR;
+  while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+    for (i = 0; i < nbytes / BYTES_STAR; i++) {
+      dparse (&R, 1, &tbuffer[i*BYTES_STAR]);
+      dparse (&D, 2, &tbuffer[i*BYTES_STAR]);
+      dparse (&M, 3, &tbuffer[i*BYTES_STAR]);
+      catalog2.average[nstar].R = R;
+      catalog2.average[nstar].D = D;
+      catalog2.average[nstar].M = M * 1000.0;
+      nstar++;
+      if (nstar == NSTARS - 1) {
+	NSTARS += DNSTARS;
+	REALLOCATE (catalog2.average, Average, NSTARS);
+      }
+    }
+  }
+  free (tbuffer);
+  REALLOCATE (catalog2.average, Average, MAX (nstar, 1));
+  catalog2.Naverage = nstar;
+  fclose (f);
+  */
+
+  /* sort data in order of RA */
+  sortave (catalog1.average, catalog1.Naverage);
+  sortave (catalog2.average, catalog2.Naverage);
+
+  /* data has been loaded, use gcompare algorithm to match */
+  compare (&catalog1, &catalog2, rvec, dvec, mvec, drvec, ddvec, dmvec, radius);
+
+  dvo_catalog_free (&catalog1);
+  free (catalog2.average);
+
+  return (TRUE);
+
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmd.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmd.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmd.c	(revision 22322)
@@ -0,0 +1,124 @@
+# include "dvoshell.h"
+
+int cmd (int argc, char **argv) { /* really need to think about upper limits & how to represent them */
+  
+  double *M1, *M3;
+  int i, j, m, i1, i3, N1, N3, N;
+  int Npts, NPTS, mode[3];
+  int Nsecfilt, KeepNulls;
+
+  PhotCode *code[3];
+  Catalog catalog;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line options */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) return (FALSE);
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+    
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M3 = NULL;
+      m = catalog.average[i].measureOffset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M3 = ExtractMagnitudes (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N3);
+      if (N3 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M3, double, 1);
+	  N3 = 1;
+	  M3[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i3 = 0; i3 < N3; i3++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M3[i3];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M3 != NULL) free (M3);
+    }
+    dvo_catalog_free (&catalog);
+  }
+
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: cmd F - F : F\n");
+  return (FALSE);
+
+escape:
+  dvo_catalog_free (&catalog);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (FALSE);
+}
+    
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmpReadFile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmpReadFile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmpReadFile.c	(revision 22322)
@@ -0,0 +1,125 @@
+# include "dvoshell.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+CMPstars *cmpReadFits (FILE *f, int *nstars) {
+
+  int i, Nstars;
+  Header theader;
+  FTable table;
+  CMPstars *stars;
+  SMPData *smpdata;
+
+  /* if no stars, no table */
+  if (*nstars == 0) return (NULL);
+
+  /* init & load in table data */
+  table.header   = &theader;
+  if (!gfits_fread_ftable (f, &table, "SMPFILE")) goto escape;
+
+  smpdata = gfits_table_get_SMPData (&table, &Nstars, NULL);
+
+  ALLOCATE (stars, CMPstars, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    stars[i].X      = smpdata[i].X;
+    stars[i].Y      = smpdata[i].Y;
+    stars[i].M      = smpdata[i].M;
+    stars[i].dM     = smpdata[i].dM;
+    stars[i].dophot = smpdata[i].dophot;
+
+    stars[i].Mgal   = smpdata[i].M;
+    stars[i].Map    = smpdata[i].dM;
+    stars[i].fx     = smpdata[i].fx;
+    stars[i].fy     = smpdata[i].fy;
+    stars[i].df     = smpdata[i].df;
+  }    
+  *nstars = Nstars;
+  return (stars);
+
+escape:
+  gprint (GP_ERR, "error reading file\n");
+  *nstars = 0;
+  return (NULL);
+}
+
+CMPstars *cmpReadText (FILE *f, int *nstars) {
+
+  int j, N, Nextra, Ninstar, Nskip, Nbytes, nbytes;
+  int done;
+  char *buffer, *c, *c2;
+  double tmp;
+  CMPstars *stars;
+  
+  /* load in stars by blocks of 1000 */
+  N = 0;
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR) + 1);
+  buffer[BLOCK*BYTES_STAR] = 0;
+  Nextra = 0;
+
+  ALLOCATE (stars, CMPstars, *nstars);
+
+  while (N < *nstars) {
+    /* load next data block */
+    Nbytes = BYTES_STAR * BLOCK - Nextra;
+    nbytes = fread (&buffer[Nextra], 1, Nbytes, f);
+    if (nbytes == 0) {
+      *nstars = N;
+      return (stars);
+    }
+    nbytes += Nextra;
+
+    /* check line-by-line integrity */
+    c = buffer;
+    done = FALSE;
+    while ((c < buffer + nbytes) && (!done)) { 
+      for (c2 = c; *c2 == '\n'; c2++);
+      if (c2 > c) { /* extra return chars */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+	/* if (VERBOSE) gprint (GP_ERR, "deleted %d extra return chars\n", Nskip); */
+      }
+      c2 = strchr (c, '\n');
+      if (c2 == (char *) NULL) {
+	done = TRUE;	
+	continue;
+      }
+      c2++;
+      if ((c2 - c) != BYTES_STAR) { /* bad line, delete it */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+	/* if (VERBOSE) gprint (GP_ERR, "deleted line, %d extra chars\n", Nskip); */
+      } else {
+	c = c2;
+      }
+    }
+
+    /* extract data for stars */
+    Ninstar = nbytes / BYTES_STAR;
+    Nextra = nbytes % BYTES_STAR;
+    for (j = 0; (j < Ninstar) && (N < *nstars); j++, N++) {
+      dparse (&stars[N].X,  1, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Y,  2, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].M,  3, &buffer[j*BYTES_STAR]);
+
+      /* cmp files carry dM in millimags */
+      dparse (&tmp, 4, &buffer[j*BYTES_STAR]);
+      stars[N].dM = 0.001*tmp;
+
+      dparse (&tmp,         5, &buffer[j*BYTES_STAR]);
+      stars[N].dophot = tmp;
+
+      dparse (&stars[N].Mgal, 7, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Map,  8, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].fx,   9, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].fy,  10, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].df,  11, &buffer[j*BYTES_STAR]);
+    }
+  }
+  *nstars = N;
+  return (stars);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmpload.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmpload.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmpload.c	(revision 22322)
@@ -0,0 +1,143 @@
+# include "dvoshell.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+int cmpload (int argc, char **argv) {
+  
+  int i, Noverlay, NOVERLAY, Nstar, N, Nin, Nextra, Objtype, type;
+  int doneread, done, Nskip, Nbytes, nbytes, Ninstar;
+  char *c, *c2, *name;
+  double dtmp;
+  FILE *f;
+  char *buffer;
+  int kapa;
+  Header header;
+  KiiOverlay *overlay;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  Objtype = 0;
+  if ((N = get_argument (argc, argv, "-t"))) {
+    remove_argument (N, &argc, argv);
+    Objtype = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cmpload (overlay) <filename>\n");
+    return (FALSE);
+  }
+
+  if (!gfits_read_header (argv[2], &header)) {
+    gprint (GP_ERR, "ERROR: can't read header for %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  /* find expected number of stars */
+  gfits_scan (&header, "NSTARS", "%d", 1, &Nstar);
+  if (Nstar == 0) {
+    gprint (GP_ERR, "ERROR: can't get NSTARS from header\n");
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+
+  f = fopen (argv[2], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't read data from %s\n", argv[2]);
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+  /* load in stars by blocks of 1000 */
+  Nin = 0;
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR) + 1);
+  buffer[BLOCK*BYTES_STAR] = 0;
+  Nextra = 0;
+  doneread = FALSE;
+  while (!doneread) {
+    Nbytes = BYTES_STAR * BLOCK - Nextra;
+    nbytes = fread (&buffer[Nextra], 1, Nbytes, f);
+    if (nbytes == 0) {
+      doneread = TRUE;
+      continue;
+    }
+    nbytes += Nextra;
+    /* check line-by-line integrity */
+    c = buffer;
+    done = FALSE;
+    while ((c < buffer + nbytes) && (!done)) { 
+      for (c2 = c; *c2 == '\n'; c2++);
+      if (c2 > c) { /* extra return chars */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+      }
+      c2 = strchr (c, '\n');
+      if (c2 == (char *) NULL) {
+	done = TRUE;	
+	continue;
+      }
+      c2++;
+      if ((c2 - c) != BYTES_STAR) { /* bad line, delete it */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+      } else {
+	c = c2;
+      }
+    }
+    Ninstar = nbytes / BYTES_STAR;
+    Nextra = nbytes % BYTES_STAR;
+    for (i = 0; i < Ninstar; i++) {
+      if (Objtype) {
+	dparse (&dtmp, 5, &buffer[i*BYTES_STAR]);
+	type = dtmp;
+	if (type != Objtype) continue;
+      }
+      # if (0)
+      if (scale) {
+	dparse (&mag,  3, &buffer[i*BYTES_STAR]);
+	overlay[Noverlay].dx = mzero + mscale * mag;
+	overlay[Noverlay].dy = mzero + mscale * mag;
+      } else {
+	overlay[Noverlay].dx = 5.0;
+	overlay[Noverlay].dy = 5.0;
+      }      
+      # endif 
+
+      fparse (&overlay[Noverlay].x,  1, &buffer[i*BYTES_STAR]);
+      fparse (&overlay[Noverlay].y,  2, &buffer[i*BYTES_STAR]);
+      overlay[Noverlay].type = KII_OVERLAY_BOX;
+      overlay[Noverlay].dx = 5.0;
+      overlay[Noverlay].dy = 5.0;
+      Noverlay ++;
+      CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+    }
+  }
+  fclose (f);
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+  free (overlay);
+
+  gfits_free_header (&header);
+  free (buffer);
+
+  gprint (GP_ERR, "loaded %d objects\n", Noverlay);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmpread.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmpread.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/cmpread.c	(revision 22322)
@@ -0,0 +1,143 @@
+# include "dvoshell.h"
+
+/* add others as needed */
+enum {F_NONE, F_RA, F_DEC, F_X, F_Y, F_MAG, F_DMAG, F_TYPE, F_SKY, F_FX, F_FY, F_APMAG, F_GALMAG};
+
+int cmpread (int argc, char **argv) {
+  
+  int i, field, Naxis, Nbytes, Nstars;
+  double tR, tD;
+  float value;
+  FILE *f;
+  Vector *vec;
+  Header header;
+  Coords coords;
+  CMPstars *stars;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cmpread field <filename>\n");
+    return (FALSE);
+  }
+
+  field = F_NONE;
+  if (!strcasecmp (argv[1], "ra"))   field = F_RA;
+  if (!strcasecmp (argv[1], "dec"))  field = F_DEC;
+  if (!strcasecmp (argv[1], "mag"))  field = F_MAG;
+  if (!strcasecmp (argv[1], "dmag")) field = F_DMAG;
+  if (!strcasecmp (argv[1], "x"))    field = F_X;
+  if (!strcasecmp (argv[1], "y"))    field = F_Y;
+  if (!strcasecmp (argv[1], "type")) field = F_TYPE;
+  if (!strcasecmp (argv[1], "sky"))  field = F_SKY;
+  if (!strcasecmp (argv[1], "fx"))   field = F_FX;
+  if (!strcasecmp (argv[1], "fy"))   field = F_FY;
+  if (!strcasecmp (argv[1], "apmag"))  field = F_APMAG;
+  if (!strcasecmp (argv[1], "galmag"))  field = F_GALMAG;
+  if (field == F_NONE) {
+    gprint (GP_ERR, "invalid cmp field: %s\n", argv[1]);
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1],  ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* load FITS header */
+  if (!gfits_read_header (argv[2], &header)) {
+    gprint (GP_ERR, "ERROR: can't read header for %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  if ((field == F_RA) || (field == F_DEC)) {
+    if (!GetCoords (&coords, &header)) {
+      gprint (GP_ERR, "can't get WCS info from header\n");
+      gfits_free_header (&header);
+      return (FALSE);
+    }
+  }
+
+  /* re-open file to load data */
+  f = fopen (argv[2], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't read data from %s\n", argv[2]);
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  /* find expected number of stars */
+  if (!gfits_scan (&header, "NSTARS", "%d", 1, &Nstars)) {
+    gprint (GP_ERR, "ERROR: can't get NSTARS from header\n");
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+
+  /* read from FITS table or from text table */
+  Naxis = 0;
+  gfits_scan (&header, "NAXIS",  "%d", 1, &Naxis);
+  if (Naxis == 2) {
+    /* allocate space for stars */
+    gprint (GP_ERR, "reading from TEXT cmp file %s\n", argv[2]);
+    if (!gfits_scan (&header, "NSTARS", "%d", 1, &Nstars)) {
+      gprint (GP_ERR, "ERROR: failed to find NSTARS\n");
+      exit (1);
+    }
+    stars = cmpReadText (f, &Nstars);
+  } else {
+    gprint (GP_ERR, "reading from FITS cmp file %s\n", argv[2]);
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR); 
+    stars = cmpReadFits (f, &Nstars);
+  }
+  fclose (f);
+
+  REALLOCATE (vec[0].elements, float, Nstars);
+  vec[0].Nelements = Nstars;
+  bzero (vec[0].elements, Nstars*sizeof(float));
+
+  value = 0;
+  for (i = 0; i < Nstars; i++) {
+    switch (field) {
+      case F_RA:
+	XY_to_RD (&tR, &tD, stars[i].X, stars[i].Y, &coords);
+	value = tR;
+	break;
+      case F_DEC:
+	XY_to_RD (&tR, &tD, stars[i].X, stars[i].Y, &coords);
+	value = tD;
+	break;
+      case F_X:
+	value = stars[i].X;
+	break;
+      case F_Y:
+	value = stars[i].Y;
+	break;
+      case F_MAG:
+	value = stars[i].M;
+	break;
+      case F_APMAG:
+	value = stars[i].Map;
+	break;
+      case F_GALMAG:
+	value = stars[i].Mgal;
+	break;
+      case F_DMAG:
+	value = stars[i].dM;
+	break;
+      case F_TYPE:
+	value = stars[i].dophot;
+	break;
+      case F_SKY:
+	value = stars[i].sky;
+	break;
+      case F_FX:
+	value = stars[i].fx;
+	break;
+      case F_FY:
+	value = stars[i].fy;
+	break;
+    }
+    vec[0].elements[i] = value;
+  }      
+  free (stars);
+  gfits_free_header (&header);
+  gprint (GP_ERR, "loaded %d objects\n", Nstars);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/compare.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/compare.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/compare.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include "dvoshell.h"
+# define D_NMATCH 500;
+
+void compare (Catalog *catlog1, Catalog *catlog2, 
+	      Vector *rvec,  Vector *dvec,  Vector *mvec, Vector *drvec, Vector *ddvec, Vector *dmvec, double radius) {
+
+  int i, j, first_j, Nmatch, NMATCH;
+  double dX, dY, dR;
+
+  Nmatch = 0;
+  NMATCH = D_NMATCH;
+  REALLOCATE (rvec[0].elements, float, NMATCH);
+  REALLOCATE (dvec[0].elements, float, NMATCH);
+  REALLOCATE (mvec[0].elements, float, NMATCH);
+  REALLOCATE (drvec[0].elements, float, NMATCH);
+  REALLOCATE (ddvec[0].elements, float, NMATCH);
+  REALLOCATE (dmvec[0].elements, float, NMATCH);
+
+  for (i = j = 0; (i < catlog1[0].Naverage) && (j < catlog2[0].Naverage);) {
+    
+    dX = catlog1[0].average[i].R - catlog2[0].average[j].R;
+
+    if (!(i % 100))
+      gprint (GP_ERR, ".");
+    
+    if (dX <= -radius)
+      i++;
+    if (dX >= radius)
+      j++;
+
+    if (fabs (dX) < radius) {
+      first_j = j;
+      for (j = first_j; (fabs (dX) < radius) && (j < catlog2[0].Naverage); j++) {
+	dX = catlog1[0].average[i].R - catlog2[0].average[j].R;
+	dY = catlog1[0].average[i].D - catlog2[0].average[j].D;
+	dR = hypot (dX, dY);
+	if (dR < radius) {
+	  rvec[0].elements[Nmatch] = catlog1[0].average[i].R;
+	  dvec[0].elements[Nmatch] = catlog1[0].average[i].D;
+	  // mvec[0].elements[Nmatch] = catlog1[0].average[i].M;
+	  drvec[0].elements[Nmatch] = dX;
+	  ddvec[0].elements[Nmatch] = dY;
+	  // dmvec[0].elements[Nmatch] = catlog1[0].average[i].M - catlog2[0].average[j].M;
+	  Nmatch ++;
+	  if (Nmatch == NMATCH - 1) {
+	    NMATCH += D_NMATCH;
+	    REALLOCATE ( rvec[0].elements, float, NMATCH);
+	    REALLOCATE ( dvec[0].elements, float, NMATCH);
+	    REALLOCATE ( mvec[0].elements, float, NMATCH);
+	    REALLOCATE (drvec[0].elements, float, NMATCH);
+	    REALLOCATE (ddvec[0].elements, float, NMATCH);
+	    REALLOCATE (dmvec[0].elements, float, NMATCH);
+	  }
+	}
+      }
+      j = first_j;
+      i++;
+    }
+  }
+
+  REALLOCATE ( rvec[0].elements, float, Nmatch);
+  REALLOCATE ( dvec[0].elements, float, Nmatch);
+  REALLOCATE ( mvec[0].elements, float, Nmatch);
+  REALLOCATE (drvec[0].elements, float, Nmatch);
+  REALLOCATE (ddvec[0].elements, float, Nmatch);
+  REALLOCATE (dmvec[0].elements, float, Nmatch);
+  
+  rvec[0].Nelements = Nmatch;
+  dvec[0].Nelements = Nmatch;
+  mvec[0].Nelements = Nmatch;
+  drvec[0].Nelements = Nmatch;
+  ddvec[0].Nelements = Nmatch;
+  dmvec[0].Nelements = Nmatch;
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbBooleanCond.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbBooleanCond.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbBooleanCond.c	(revision 22322)
@@ -0,0 +1,84 @@
+# include "dvoshell.h"
+
+// evaluate the expression in inStack as a boolean; necessary db field values are
+// supplied by fields, in order 0 - Nfields (validate before calling)
+int dbBooleanCond (dbStack *inStack, int NinStack, float *fields) {
+  
+  float value;
+  int i, j, N, Nstack;
+  dbStack **stack, *output;
+
+  // 'no stack' means 'no where statement'
+  if (NinStack == 0) return (TRUE);
+
+  Nstack = NinStack;
+  ALLOCATE (stack, dbStack *, NinStack);
+  for (i = 0; i < NinStack; i++) {
+    stack[i] = &inStack[i];
+  }
+
+  for (i = 0; i < Nstack; i++) {
+
+    /***** binary operators *****/
+    if ((stack[i][0].type >= 3) && (stack[i][0].type <= 8)) {
+
+      // pre-test that op and entries match
+      output = dbBinary (stack[i-2], stack[i-1], stack[i][0].name, fields); 
+
+      // free temporary stack items, drop external items
+      dbFreeTempEntry (stack[i-2]);
+      dbFreeTempEntry (stack[i-1]);
+
+      stack[i-2] = output;
+      for (j = i + 1; j < Nstack; j++) {
+	stack[j-2] = stack[j];
+      }
+
+      Nstack -= 2;
+      i -= 2;
+      continue;
+    }
+
+    /***** unary operators **/
+    if (stack[i][0].type == 9) {
+
+      // pre-test that op and entries match
+      output = dbUnary (stack[i-1], stack[i][0].name, fields); 
+
+      // free temporary stack items, drop external items
+      dbFreeTempEntry (stack[i-1]);
+
+      stack[i-1] = output;
+      for (j = i + 1; j < Nstack; j++) {
+	stack[j-1] = stack[j];
+      }
+
+      Nstack -= 1;
+      i -= 1;
+      continue;
+    } 
+  }
+
+  // the result here is a single stack entry with a value:
+  if (stack[0][0].type == 'F') {
+    N = stack[0][0].field;
+    value = fields[N];
+  } else {
+    value = stack[0][0].Float;
+  }
+    
+  for (i = 0; i < Nstack; i++) {
+    dbFreeEntry (stack[i]);
+  }
+  free (stack);
+
+  // XXX fix this limit
+  if (fabs(value) > 1e-7) {
+    return (TRUE);
+  } else {
+    return (FALSE);
+  }
+
+  // pre-test that op and entries match
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbCheckStack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbCheckStack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbCheckStack.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "dvoshell.h"
+
+int dbCheckStack (dbStack *stack, int Nstack, int table, dbField **inFields, int *inNfields) {
+
+  int i, j, status, NFIELDS, Nfields;
+  char *c;
+  dbField *fields;
+
+  fields = *inFields;
+  Nfields = *inNfields;
+
+  NFIELDS = Nfields + 10;
+  REALLOCATE (fields, dbField, NFIELDS);
+
+  for (i = 0; i < Nstack; i++) {
+    if (stack[i].type == 'X') {
+
+      /** if this is a number, put it on the list of scalers and move on **/
+      stack[i].Float = strtod (stack[i].name, &c);
+      if (c == stack[i].name + strlen (stack[i].name)) {
+	stack[i].type  = 'S';
+	continue;
+      } 
+
+      // this must be a field : is it already in the list?
+      for (j = 0; (j < Nfields) && strcasecmp (stack[i].name, fields[j].name); j++);
+      if (j < Nfields) {
+	stack[i].field = j;
+	stack[i].type  = 'F';
+	stack[i].name  = NULL;
+	stack[i].Float = 0.0;
+	continue;
+      }
+
+      // this must be a field : is it a valid name?
+      if (table == DVO_TABLE_MEASURE) {
+	status = ParseMeasureField (&fields[Nfields], stack[i].name);
+      } 
+      if (table == DVO_TABLE_AVERAGE) {
+	status = ParseAverageField (&fields[Nfields], stack[i].name);
+      } 
+      if (!status) {
+	gprint (GP_ERR, "unknown database field %s\n", stack[i].name);
+	return (FALSE);
+      }
+      stack[i].field = Nfields;
+      stack[i].type  = 'F';
+      stack[i].name  = NULL;
+      stack[i].Float = 0.0;
+
+      Nfields ++;
+      CHECK_REALLOCATE (fields, dbField, NFIELDS, Nfields, 10);
+    }
+  }
+
+  *inNfields = Nfields;
+  *inFields = fields;
+
+  return (TRUE);
+}
+
+/* check stack identifies the data elements as plain scalars or table fields
+   operators have already been identified.  
+   check stack returns the total stack dimensionality (0,1,2)
+   on error, check stack returns FALSE
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbCmdlineFields.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbCmdlineFields.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbCmdlineFields.c	(revision 22322)
@@ -0,0 +1,206 @@
+# include "dvoshell.h"
+
+// check for 'where' or 'matched to'; return first field after the word, and the value
+int dbCmdlineConditions (int argc, char **argv, int first, int *nextField) {
+
+  int isWhere, isMatch;
+
+  *nextField = argc;
+
+  // require 'where' or 'matched to' before boolean math
+  if (first == argc) return DVO_DB_CMDLINE_IS_END;
+
+  isWhere = !strcasecmp(argv[first], "where");
+  isMatch = !strcasecmp(argv[first], "match");
+    
+  if (!isMatch && !isWhere) {
+    gprint (GP_ERR, "WHERE or MATCH TO\n");
+    return DVO_DB_CMDLINE_ERROR;
+  }
+  if (isMatch && ((first + 1 >= argc - 1) || strcasecmp(argv[first+1], "to"))) {
+    gprint (GP_ERR, "missing boolean expression\n");
+    return DVO_DB_CMDLINE_ERROR;
+  }
+  if (isWhere && (first >= argc - 1)) {
+    gprint (GP_ERR, "missing boolean expression\n");
+    return DVO_DB_CMDLINE_ERROR;
+  }
+
+  if (isWhere) {
+    *nextField = first + 1;
+    return DVO_DB_CMDLINE_IS_WHERE;
+  }
+
+  if (isMatch) {
+    *nextField = first + 2;
+    return DVO_DB_CMDLINE_IS_MATCH;
+  }
+
+  gprint (GP_ERR, "programming error?\n");
+  return DVO_DB_CMDLINE_ERROR;
+}
+
+// identify the fields to be extracted (check syntax)
+// the 'last' pointer ends on the first word after the fields (where, match or EOL) 
+dbField *dbCmdlineFields (int argc, char **argv, int table, int *last, int *nfields) {
+
+  int i, status, Nfields, NFIELDS;
+  char *p, *q, *field;
+  dbField *fields;
+
+  *nfields = 0;
+  Nfields = 0;
+  NFIELDS = 10;
+  ALLOCATE (fields, dbField, NFIELDS);
+  dbInitField (&fields[0]);
+
+  // examine each argv[i] entry until we reach a 'where' or a 'matched' 
+  for (i = 1; (i < argc) && strcasecmp (argv[i], "where") && strcasecmp (argv[i], "match"); i++) {
+    // split the word by ","
+    p = argv[i];
+    while (*p) {
+      q = strchr (p, ',');
+      if (q == NULL) {
+	field = strcreate (p);
+	p = p + strlen(p);
+      } else {
+	field = strncreate (p, q-p);
+	p = q + 1;
+      }
+      // identify field for word
+      // need to know which type of fields to look for...
+      // xxx extend this more generally later
+      if (table == DVO_TABLE_MEASURE) {
+	status = ParseMeasureField (&fields[Nfields], field);
+      } 
+      if (table == DVO_TABLE_AVERAGE) {
+	status = ParseAverageField (&fields[Nfields], field);
+      } 
+      if (!status) {
+	gprint (GP_ERR, "unknown database field %s\n", field);
+	free (field);
+	dbFreeFields (fields, Nfields);
+	return (NULL);
+      }
+      free (field);
+
+      Nfields ++;
+      CHECK_REALLOCATE (fields, dbField, NFIELDS, Nfields, 10);
+      dbInitField (&fields[Nfields]);
+    }
+  }
+
+  *last = i;
+  *nfields = Nfields;
+
+  return (fields);
+}
+
+char *strfloat (float value) {
+
+  int Nbyte;
+  char *output;
+  char tmp;
+
+  Nbyte = snprintf (&tmp, 0, "%f", value);
+  ALLOCATE (output, char, Nbyte + 1);
+  snprintf (output, Nbyte + 1, "%f", value);
+  return output;
+}
+
+// identify the fields to be extracted (test for where, check syntax)
+int dbAstroRegionLimits (dbStack **stack, int *nstack, SkyRegionSelection *selection, int table) {
+  
+  int N;
+  double Rmin, Rmax, Dmin, Dmax;
+  char *Rname, *Dname;
+
+  if (!selection->useDisplay && !selection->useSkyregion) return (TRUE);
+
+  // get the ra,dec limits...
+  if (selection->useDisplay) {
+    return (TRUE);
+    // XXX fix this: be more careful with the projection & limits
+
+    Graphdata graphsky;
+    if (!GetGraphData (&graphsky, NULL, NULL)) {
+      gprint (GP_ERR, "region display not available\n");
+      return (FALSE);
+    }
+    
+    // XXX the ra and dec range depend on the projection. 
+    // XXX this is wrong...
+    int status;
+    status = XY_to_RD (&Rmin, &Dmin, graphsky.xmin, graphsky.ymin, &graphsky.coords);
+    status = XY_to_RD (&Rmax, &Dmax, graphsky.xmax, graphsky.ymax, &graphsky.coords);
+  }
+
+  if (selection->useSkyregion) {
+    get_skyregion (&Rmin, &Rmax, &Dmin, &Dmax);
+  }    
+
+  N = *nstack;
+  REALLOCATE (*stack, dbStack, N + 20);
+
+  if (table == DVO_TABLE_AVERAGE) {
+    Rname = strcreate ("RA");
+    Dname = strcreate ("DEC");
+  } else {
+    Rname = strcreate ("RA:AVE");
+    Dname = strcreate ("DEC:AVE");
+  }
+
+  // add: ((ra > rmin) && (ra < rmax) && (dec > dmin) && (dec < dmax))
+  // prepend with && if *nstack > 0
+
+  stack[0][N +  0].name = strcreate (Rname);
+  stack[0][N +  0].type = 'X';
+  stack[0][N +  1].name = strfloat (Rmin);
+  stack[0][N +  1].type = 'X';
+  stack[0][N +  2].name = strcreate (">");
+  stack[0][N +  2].type = 4;
+  // stack[0][N +  3].name = strcreate ("A");
+  // stack[0][N +  3].type = 3;
+
+  stack[0][N +  3].name = strcreate (Rname);
+  stack[0][N +  3].type = 'X';
+  stack[0][N +  4].name = strfloat (Rmax);
+  stack[0][N +  4].type = 'X';
+  stack[0][N +  5].name = strcreate ("<");
+  stack[0][N +  5].type = 4;
+  stack[0][N +  6].name = strcreate ("A");
+  stack[0][N +  6].type = 3;
+
+  stack[0][N +  7].name = strcreate (Dname);
+  stack[0][N +  7].type = 'X';
+  stack[0][N +  8].name = strfloat (Dmin);
+  stack[0][N +  8].type = 'X';
+  stack[0][N +  9].name = strcreate (">");
+  stack[0][N +  9].type = 4;
+  stack[0][N + 10].name = strcreate ("A");
+  stack[0][N + 10].type = 3;
+
+  stack[0][N + 11].name = strcreate (Dname);
+  stack[0][N + 11].type = 'X';
+  stack[0][N + 12].name = strfloat (Dmax);
+  stack[0][N + 12].type = 'X';
+  stack[0][N + 13].name = strcreate ("<");
+  stack[0][N + 13].type = 4;
+  stack[0][N + 14].name = strcreate ("A");
+  stack[0][N + 14].type = 3;
+
+  if (N == 0) {
+    N += 15;
+  } else {
+    stack[0][N + 15].name = strcreate ("A");
+    stack[0][N + 15].type = 3;
+    N += 16;
+  }    
+
+  free (Rname);
+  free (Dname);
+
+  *nstack = N;
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbExtractAverages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbExtractAverages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbExtractAverages.c	(revision 22322)
@@ -0,0 +1,209 @@
+# include "dvoshell.h"
+
+static CoordTransform *celestial_to_galactic = NULL;
+static CoordTransform *celestial_to_ecliptic = NULL;
+
+static int haveGalactic = FALSE;
+static double GLON = 0.0;
+static double GLAT = 0.0;
+
+static int haveEcliptic = FALSE;
+static double ELON = 0.0;
+static double ELAT = 0.0;
+
+// define a locally-static transform
+int dbExtractAveragesInitTransform (CoordTransformSystem target) {
+
+  // galactic transform is kept forever
+  if (target == COORD_GALACTIC) {
+    if (celestial_to_galactic != NULL) return (TRUE);
+    celestial_to_galactic = InitTransform (COORD_CELESTIAL, target);
+    return (TRUE);
+  }
+
+  // ecliptic transform must be updated (is weakly time-dependent)
+  if (target == COORD_ECLIPTIC) {
+    if (celestial_to_ecliptic != NULL) {
+      free (celestial_to_ecliptic);
+    }
+    celestial_to_ecliptic = InitTransform (COORD_CELESTIAL, target);
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+int dbExtractAveragesInit () {
+  haveGalactic = FALSE;
+  haveEcliptic = FALSE;
+  return (TRUE);
+}
+
+/* return average.field based on the selection */
+double dbExtractAverages (Average *average, SecFilt *secfilt, Measure *measure, dbField *field) {
+
+  int i, Nsec;
+  double value;
+
+  value = NAN;
+
+  /* assign vector values */
+  switch (field->ID) {
+    case AVE_RA:
+      value = average[0].R;
+      break;
+    case AVE_DEC:
+      value = average[0].D;
+      break;
+    case AVE_GLON:
+      if (!haveGalactic) {
+	ApplyTransform (&GLON, &GLAT, average[0].R, average[0].D, celestial_to_galactic);
+	haveGalactic = TRUE;
+      }
+      value = GLON;
+      break;
+    case AVE_GLAT:
+      if (!haveGalactic) {
+	ApplyTransform (&GLON, &GLAT, average[0].R, average[0].D, celestial_to_galactic);
+	haveGalactic = TRUE;
+      }
+      value = GLAT;
+      break;
+    case AVE_ELON:
+      if (!haveEcliptic) {
+	ApplyTransform (&ELON, &ELAT, average[0].R, average[0].D, celestial_to_ecliptic);
+	haveEcliptic = TRUE;
+      }
+      value = ELON;
+      break;
+    case AVE_ELAT:
+      if (!haveEcliptic) {
+	ApplyTransform (&ELON, &ELAT, average[0].R, average[0].D, celestial_to_ecliptic);
+	haveEcliptic = TRUE;
+      }
+      value = ELAT;
+      break;
+    case AVE_RA_ERR:
+      value = average[0].dR;
+      break;
+    case AVE_DEC_ERR:
+      value = average[0].dD;
+      break;
+
+    case AVE_U_RA:
+      value = average[0].uR;
+      break;
+    case AVE_U_DEC:
+      value = average[0].uD;
+      break;
+    case AVE_U_RA_ERR:
+      value = average[0].duR;
+      break;
+    case AVE_U_DEC_ERR:
+      value = average[0].duD;
+      break;
+
+    case AVE_PAR:
+      value = average[0].P;
+      break;
+    case AVE_PAR_ERR:
+      value = average[0].dP;
+      break;
+
+    case AVE_NMEAS:
+      value = average[0].Nmeasure;
+      break;
+    case AVE_NMISS:
+      value = average[0].Nmissing;
+      break;
+    case AVE_Xp:
+      value = 0.01*average[0].Xp;
+      break;
+    case AVE_FLAG:
+      value = average[0].code;
+      break;
+
+    case AVE_MAG:
+      switch (field->magMode) {
+	case MAG_AVE:
+	  value = PhotAve  (field->photcode, average, secfilt);
+	  break;
+	case MAG_REF:
+	  value = PhotRef  (field->photcode, average, secfilt, measure);
+	  break;
+	case MAG_INST:
+	case MAG_CAT:
+	case MAG_SYS:
+	case MAG_REL:
+	case MAG_CAL:
+	  // XXX need to code this correctly: this returns just the first matching value
+	  value = NAN;
+	  for (i = 0; i < average[0].Nmeasure; i++) {
+	    if (field->photcode->code != measure[i].photcode) continue;
+	    value = measure[i].M;
+	    break;
+	  }
+	  break;
+	case MAG_CHISQ:
+	  // GetPhotcodeEquivCodebyCode (field->photcode)
+	  value = PhotXm (field->photcode, average, secfilt);
+	  break;
+	case MAG_ERR:
+	  // GetPhotcodeEquivCodebyCode (field->photcode)
+	  if (field->photcode->type == PHOT_REF) {
+	    for (i = 0; i < average[0].Nmeasure; i++) {
+	      if (field->photcode->code != measure[i].photcode) continue;
+	      value = measure[i].dM;
+	      break;
+	    }
+	  } else {
+	    value = PhotdM (field->photcode, average, secfilt);
+	  }
+	  break;
+	case MAG_NCODE:
+	  // XXX push these into dvo_photcode_ops APIs
+	  // XXX do I need to allow for conversion to equiv?
+	  Nsec = GetPhotcodeNsec (field->photcode->code);
+	  if (Nsec == -1) break;
+	  value = secfilt[Nsec].Ncode;
+	  break;
+	case MAG_NPHOT:
+	  Nsec = GetPhotcodeNsec (field->photcode->code);
+	  if (Nsec == -1) break;
+	  value = secfilt[Nsec].Nused;
+	  break;
+
+      }
+      break;
+    case AVE_dMAG:
+      value = PhotdM (field->photcode, average, secfilt);
+      break;
+    case AVE_Xm:
+      value = PhotXm (field->photcode, average, secfilt);
+      break;
+    case AVE_TYPE:
+      break;
+    case AVE_TYPEFRAC:
+      break;
+      /*
+    case AVE_NCODE:
+      value = 0;
+      for (i = 0; i < average[0].Nm; i++) {
+	if (field->photcode->code != GetPhotcodeEquivCodebyCode (measure[i].photcode)) continue;
+	value ++;
+      }
+      break;
+    case AVE_NPHOT:
+      value = 0;
+      for (i = 0; i < average[0].Nm; i++) {
+	if (field->photcode->code != GetPhotcodeEquivCodebyCode (measure[i].photcode)) continue;
+	if (measure[i].flags & (ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM)) continue;
+	value ++;
+      }
+      break;
+    case AVE_NCRIT:
+      break;
+      */
+  }
+  return (value);
+}  
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbExtractMeasures.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbExtractMeasures.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbExtractMeasures.c	(revision 22322)
@@ -0,0 +1,399 @@
+# include "dvoshell.h"
+
+// to add a new field to the extractions:
+// 1) add the selection for the field below
+// 2) add the field to the ParseMeasureField in dbFields.c
+// 3) add the field to the measure enum list in dvoshell.h
+
+/* time concepts */
+static time_t TimeReference;
+static int TimeFormat;
+
+static time_t TimeRefPM;
+
+static CoordTransform *celestial_to_galactic = NULL;
+static CoordTransform *celestial_to_ecliptic = NULL;
+
+static int haveGalacticAve = FALSE;
+static double GLON_AVE = 0.0;
+static double GLAT_AVE = 0.0;
+
+static int haveEclipticAve = FALSE;
+static double ELON_AVE = 0.0;
+static double ELAT_AVE = 0.0;
+
+static int haveGalacticMeas = FALSE;
+static double GLON_MEAS = 0.0;
+static double GLAT_MEAS = 0.0;
+
+static int haveEclipticMeas = FALSE;
+static double ELON_MEAS = 0.0;
+static double ELAT_MEAS = 0.0;
+
+void dbExtractMeasuresInit () {
+  TimeRefPM = ohana_date_to_sec ("2000/01/01");
+  GetTimeFormat (&TimeReference, &TimeFormat);
+}
+
+// define a locally-static transform
+int dbExtractMeasuresInitTransform (CoordTransformSystem target) {
+
+  // galactic transform is kept forever
+  if (target == COORD_GALACTIC) {
+    if (celestial_to_galactic != NULL) return (TRUE);
+    celestial_to_galactic = InitTransform (COORD_CELESTIAL, target);
+    return (TRUE);
+  }
+
+  // ecliptic transform must be updated (is weakly time-dependent)
+  if (target == COORD_ECLIPTIC) {
+    if (celestial_to_ecliptic != NULL) {
+      free (celestial_to_ecliptic);
+    }
+    celestial_to_ecliptic = InitTransform (COORD_CELESTIAL, target);
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+int dbExtractMeasuresInitAve () {
+  haveGalacticAve = FALSE;
+  haveEclipticAve = FALSE;
+  return (TRUE);
+}
+
+int dbExtractMeasuresInitMeas () {
+  haveGalacticMeas = FALSE;
+  haveEclipticMeas = FALSE;
+  return (TRUE);
+}
+
+/* return measure.field based on the selection */
+double dbExtractMeasures (Average *average, SecFilt *secfilt, Measure *measure, dbField *field) {
+
+  int Nsec;
+  double value;
+  double ra, dec, x, y, dT;
+
+  Coords *mosaic;
+  PhotCode *equiv;
+
+  value = NAN;
+
+  switch (field->ID) {
+    case MEAS_MAG: /* magnitudes are already determined above */
+      equiv = GetPhotcodeEquivbyCode (measure[0].photcode);
+
+      // we return the magnitude for this measure if:
+      if (field->photcode->type == PHOT_MAG) goto valid_photcode;
+      if ((field->photcode->type == PHOT_REF) && (measure[0].photcode == field->photcode->code)) goto valid_photcode;
+      if ((field->photcode->type == PHOT_DEP) && (measure[0].photcode == field->photcode->code)) goto valid_photcode;
+
+      if ((equiv != NULL) && (field->photcode->type == PHOT_SEC) && (equiv[0].code == field->photcode->code)) goto valid_photcode;
+      break;
+
+  valid_photcode:
+      switch (field->magMode) {
+	case MAG_INST:
+	  value = PhotInst (measure);  
+	  break;
+	case MAG_CAT:
+	  value = PhotCat  (measure); 
+	  break;
+	case MAG_SYS:
+	  value = PhotSys  (measure, average, secfilt); 
+	  break;
+	case MAG_REL:
+	  value = PhotRel  (measure, average, secfilt); 
+	  break;
+	case MAG_CAL:
+	  value = PhotCal  (measure, average, secfilt, measure, equiv); 
+	  break;
+	case MAG_AVE:
+	  value = PhotAve  (equiv, average, secfilt); 
+	  break;
+	case MAG_REF:
+	  value = PhotRef  (equiv, average, secfilt, measure); 
+	  break;
+	case MAG_ERR:
+	  if (field->photcode->type == PHOT_SEC) {
+	    value = PhotdM  (equiv, average, secfilt); 
+	  } else {
+	    value = measure[0].dM;
+	  }
+	  break;
+	case MAG_CHISQ:
+	  value = PhotXm  (equiv, average, secfilt); 
+	  break;
+	case MAG_NCODE:
+	  if (equiv == NULL) {
+	    value = NAN;
+	    break;
+	  }
+	  Nsec = GetPhotcodeNsec (equiv->code);
+	  if (Nsec == -1) break;
+	  value = secfilt[Nsec].Ncode;
+	  break;
+	case MAG_NPHOT:
+	  if (equiv == NULL) {
+	    value = NAN;
+	    break;
+	  }
+	  Nsec = GetPhotcodeNsec (equiv->code);
+	  if (Nsec == -1) break;
+	  value = secfilt[Nsec].Nused;
+	  break;
+      }
+      break;
+    case MEAS_RA: /* OK */
+      value = average[0].R - measure[0].dR / 3600.0;
+      break;
+    case MEAS_DEC: /* OK */
+      value = average[0].D - measure[0].dD / 3600.0;
+      break;
+    case MEAS_RA_AVE: /* OK */
+      value = average[0].R;
+      break;
+    case MEAS_DEC_AVE: /* OK */
+      value = average[0].D;
+      break;
+
+    case MEAS_GLON:
+      if (!haveGalacticMeas) {
+	ApplyTransform (&GLON_MEAS, &GLAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_galactic);
+	haveGalacticMeas = TRUE;
+      }
+      value = GLON_MEAS;
+      break;
+    case MEAS_GLAT:
+      if (!haveGalacticMeas) {
+	ApplyTransform (&GLON_MEAS, &GLAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_galactic);
+	haveGalacticMeas = TRUE;
+      }
+      value = GLAT_MEAS;
+      break;
+    case MEAS_ELON:
+      if (!haveEclipticMeas) {
+	ApplyTransform (&ELON_MEAS, &ELAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_ecliptic);
+	haveEclipticMeas = TRUE;
+      }
+      value = ELON_MEAS;
+      break;
+    case MEAS_ELAT:
+      if (!haveEclipticMeas) {
+	ApplyTransform (&ELON_MEAS, &ELAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_ecliptic);
+	haveEclipticMeas = TRUE;
+      }
+      value = ELAT_MEAS;
+      break;
+
+    case MEAS_GLON_AVE:
+      if (!haveGalacticAve) {
+	ApplyTransform (&GLON_AVE, &GLAT_AVE, average[0].R, average[0].D, celestial_to_galactic);
+	haveGalacticAve = TRUE;
+      }
+      value = GLON_AVE;
+      break;
+    case MEAS_GLAT_AVE:
+      if (!haveGalacticAve) {
+	ApplyTransform (&GLON_AVE, &GLAT_AVE, average[0].R, average[0].D, celestial_to_galactic);
+	haveGalacticAve = TRUE;
+      }
+      value = GLAT_AVE;
+      break;
+    case MEAS_ELON_AVE:
+      if (!haveEclipticAve) {
+	ApplyTransform (&ELON_AVE, &ELAT_AVE, average[0].R, average[0].D, celestial_to_ecliptic);
+	haveEclipticAve = TRUE;
+      }
+      value = ELON_AVE;
+      break;
+    case MEAS_ELAT_AVE:
+      if (!haveEclipticAve) {
+	ApplyTransform (&ELON_AVE, &ELAT_AVE, average[0].R, average[0].D, celestial_to_ecliptic);
+	haveEclipticAve = TRUE;
+      }
+      value = ELAT_AVE;
+      break;
+
+    case MEAS_RA_AVE_ERR: /* OK */
+      value = average[0].dR;
+      break;
+    case MEAS_DEC_AVE_ERR: /* OK */
+      value = average[0].dD;
+      break;
+    case MEAS_U_RA: /* OK */
+      value = average[0].uR;
+      break;
+    case MEAS_U_DEC: /* OK */
+      value = average[0].uD;
+      break;
+    case MEAS_U_RA_ERR: /* OK */
+      value = average[0].duR;
+      break;
+    case MEAS_U_DEC_ERR: /* OK */
+      value = average[0].duD;
+      break;
+    case MEAS_PAR: /* OK */
+      value = average[0].R;
+      break;
+    case MEAS_PAR_ERR: /* OK */
+      value = average[0].D;
+      break;
+    case MEAS_XP: /* OK */
+      value = average[0].Xp;
+      break;
+    case MEAS_NMEAS: /* OK */
+      value = average[0].Nmeasure;
+      break;
+    case MEAS_NMISS: /* OK */
+      value = average[0].Nmissing;
+      break;
+    case MEAS_OBJFLAGS: /* OK */
+      value = average[0].code;
+      break;
+    // note that these represent the ra displacement relative to the average, not 
+    // the error.
+    case MEAS_RA_OFFSET: /* OK */
+      value = measure[0].dR;
+      break;
+    case MEAS_DEC_OFFSET: /* OK */
+      value = measure[0].dD;
+      break;
+    case MEAS_RA_FIT_OFFSET: /* OK */
+      dT = (measure[0].t - TimeRefPM) / (86400*365.25);
+      value = average[0].uR * dT + measure[0].dR;
+      break;
+    case MEAS_DEC_FIT_OFFSET: /* OK */
+      dT = (measure[0].t - TimeRefPM) / (86400*365.25);
+      value = average[0].uD * dT + measure[0].dD;
+      break;
+    case MEAS_RA_OFFSET_ERR: /* OK */
+      value = NAN;
+      break;
+    case MEAS_DEC_OFFSET_ERR: /* OK */
+      value = NAN;
+      break;
+    case MEAS_AIRMASS: /* OK */
+      value = measure[0].airmass;
+      break;
+    case MEAS_AZ: /* OK */
+      value = measure[0].az;
+      break;
+    case MEAS_EXPTIME: /* OK */
+      value = pow (10.0, measure[0].dt * 0.4);
+      break;
+    case MEAS_PHOTCODE: /* OK */
+      value = measure[0].photcode;
+      break;
+    case MEAS_TIME: /* OK */
+      value = TimeValue (measure[0].t, TimeReference, TimeFormat);
+      break;
+    case MEAS_FWHM: /* OK */
+      value = 0.01*(measure[0].FWx + measure[0].FWy) / 2.0;
+      break;
+    case MEAS_FWHM_MAJ: /* OK */
+      value = 0.01*measure[0].FWx;
+      break;
+    case MEAS_FWHM_MIN: /* OK */
+      value = 0.01*measure[0].FWy;
+      break;
+    case MEAS_THETA: /* OK */
+      value = measure[0].theta;
+      break;
+    case MEAS_DOPHOT: /* OK */
+      value = measure[0].dophot;
+      break;
+    case MEAS_DB_FLAGS: /* ? */
+      value = measure[0].dbFlags;
+      break;
+    case MEAS_PHOT_FLAGS: /* ? */
+      value = measure[0].photFlags;
+      break;
+    case MEAS_XCCD: /* OK */
+/* I need to perform this conversion for ELIXIR and LONEOS formats on load */      
+# if 1
+      value = measure[0].Xccd;
+# else
+      { 
+	Image *image;
+	ra  = average[0].R - measure[0].dR / 3600.0;
+	dec = average[0].D - measure[0].dD / 3600.0;
+	image = MatchImage (measure[0].t, measure[0].photcode);
+	if (image == NULL) break;
+	RD_to_XY (&x, &y, ra, dec, &image[0].coords);
+	value = x;
+      }
+# endif
+      break;
+    case MEAS_YCCD: /* OK */
+/* I need to perform this conversion for ELIXIR and LONEOS formats on load */      
+# if 1
+      value = measure[0].Yccd;
+# else
+      {
+	Image *image;
+	ra  = average[0].R - measure[0].dR / 3600.0;
+	dec = average[0].D - measure[0].dD / 3600.0;
+	image = MatchImage (measure[0].t, measure[0].photcode);
+	if (image == NULL) break;
+	RD_to_XY (&x, &y, ra, dec, &image[0].coords);
+	value = y;
+      }
+# endif
+      break;
+    case MEAS_XMOSAIC: /* OK */
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      if (mosaic == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, mosaic);
+      value = x;
+      break;
+    case MEAS_YMOSAIC: /* OK */
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      if (mosaic == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, mosaic);
+      value = y;
+      break;
+
+    case MEAS_SKY: /* OK */
+      value = measure[0].Sky;
+      break;
+    case MEAS_dSKY: /* OK */
+      value = measure[0].dSky;
+      break;
+    case MEAS_DET_ID: /* OK */
+      value = measure[0].detID;
+      break;
+    case MEAS_OBJ_ID: /* OK */
+      value = measure[0].averef;
+      break;
+    case MEAS_IMAGE_ID: /* OK */
+      value = measure[0].imageID;
+      break;
+    case MEAS_PSF_QF: /* OK */
+      value = measure[0].qPSF;
+      break;
+    case MEAS_PSF_CHISQ: /* OK */
+      value = measure[0].psfChisq;
+      break;
+    case MEAS_CR_NSIGMA: /* OK */
+      value = measure[0].crNsigma;
+      break;
+    case MEAS_EXT_NSIGMA: /* OK */
+      value = measure[0].extNsigma;
+      break;
+    case MEAS_STARGAL: /* OK */
+      value = measure[0].stargal;
+      break;
+  }
+  return (value);
+}
+
+/** the mosaic entries do not use the registered mosaic found 
+    by MatchImage (via FindMosaicForImage).  Rather, they use
+    a coordinate frame saved by SetImageSelection 
+**/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbFields.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbFields.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbFields.c	(revision 22322)
@@ -0,0 +1,268 @@
+# include "dvoshell.h"
+
+void dbFreeFields (dbField *fields, int Nfields) {
+
+  int i;
+
+  if (fields == NULL) return;
+
+  for (i = 0; i < Nfields; i++) {
+    if (fields[i].name != NULL) free (fields[i].name);
+    if (fields[i].photcode != NULL) {
+      if (fields[i].photcode[0].type == PHOT_MAG) {
+	free (fields[i].photcode);
+      }
+    }
+  }
+  free (fields);
+}
+
+void dbInitField (dbField *field) {
+  field->name = NULL;
+  field->extract = FALSE;
+  field->table = 0;
+  field->ID = 0;
+  field->magMode = 0;
+  field->photcode = NULL;
+}
+
+int GetMagMode (char *string) {
+
+  if (!strcasecmp (string, "inst"))  return (MAG_INST);
+  if (!strcasecmp (string, "cat"))   return (MAG_CAT);
+  if (!strcasecmp (string, "sys"))   return (MAG_SYS);
+  if (!strcasecmp (string, "rel"))   return (MAG_REL);
+  if (!strcasecmp (string, "cal"))   return (MAG_CAL);
+  if (!strcasecmp (string, "ave"))   return (MAG_AVE);
+  if (!strcasecmp (string, "ref"))   return (MAG_REF);
+  if (!strcasecmp (string, "err"))   return (MAG_ERR);
+  if (!strcasecmp (string, "chisq")) return (MAG_CHISQ);
+  if (!strcasecmp (string, "ncode")) return (MAG_NCODE);
+  if (!strcasecmp (string, "nphot")) return (MAG_NPHOT);
+  return (MAG_NONE);
+}
+
+PhotCode *ParsePhotcodeField (char *field, int *mode, int defMode) {
+
+  int useDefault;
+  char *tmpstring, *p;
+  PhotCode *code;
+
+  *mode = defMode;
+  useDefault = TRUE;
+
+  p = strchr (field, ':');
+  if (p != NULL) {
+    *mode = GetMagMode (p + 1);
+    useDefault = FALSE;
+    if (*mode == MAG_NONE) return (NULL);
+    tmpstring = strncreate (field, p - field);
+  } else {
+    tmpstring = strcreate (field);
+  }
+  if (!strcasecmp (tmpstring, "MAG")) {
+    ALLOCATE (code, PhotCode, 1);
+    code[0].code = 0;
+    strcpy (code[0].name, "MAG");
+    code[0].type = PHOT_MAG;
+    free (tmpstring);
+    return (code);
+  }
+  code = GetPhotcodebyName (tmpstring);
+  if (!code) {
+      return NULL;
+  }
+
+
+  // enforce compatibility
+  // XXX this is kind of poorly done
+  if ((code[0].type == PHOT_REF) && (*mode != MAG_ERR)) {
+    *mode = MAG_CAT;
+  }
+  if (code[0].type == PHOT_DEP) {
+    if (useDefault) {
+      *mode = MAG_REL;
+    }
+  } 
+  free (tmpstring);
+  return (code);
+}
+
+# define ESCAPE(F,M) { \
+  field->ID = (F); \
+  field->magMode = (M); \
+  field->photcode = NULL; \
+  return (TRUE); }
+
+int ParseMeasureField (dbField *field, char *fieldName) {
+
+  int mode;
+  PhotCode *code;
+
+  field->table = DVO_TABLE_MEASURE;
+  field->name  = strcreate (fieldName);
+
+  if (!strcasecmp (fieldName, "GLON"))       {
+    dbExtractMeasuresInitTransform (COORD_GALACTIC);
+    ESCAPE (MEAS_GLON, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "GLAT")) {
+    dbExtractMeasuresInitTransform (COORD_GALACTIC);
+    ESCAPE (MEAS_GLAT, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "GLON:AVE")) {
+    dbExtractMeasuresInitTransform (COORD_GALACTIC);
+    ESCAPE (MEAS_GLON_AVE, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "GLAT:AVE")) {
+    dbExtractMeasuresInitTransform (COORD_GALACTIC);
+    ESCAPE (MEAS_GLAT_AVE, MAG_NONE);
+  }
+
+  if (!strcasecmp (fieldName, "ELON"))       {
+    dbExtractMeasuresInitTransform (COORD_ECLIPTIC);
+    ESCAPE (MEAS_ELON, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "ELAT")) {
+    dbExtractMeasuresInitTransform (COORD_ECLIPTIC);
+    ESCAPE (MEAS_ELAT, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "ELON:AVE")) {
+    dbExtractMeasuresInitTransform (COORD_ECLIPTIC);
+    ESCAPE (MEAS_ELON_AVE, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "ELAT:AVE")) {
+    dbExtractMeasuresInitTransform (COORD_ECLIPTIC);
+    ESCAPE (MEAS_ELAT_AVE, MAG_NONE);
+  }
+
+  if (!strcasecmp (fieldName, "RA"))         ESCAPE (MEAS_RA,   	MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC"))        ESCAPE (MEAS_DEC,  	MAG_NONE);
+  if (!strcasecmp (fieldName, "RA:AVE"))     ESCAPE (MEAS_RA_AVE,      	MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC:AVE"))    ESCAPE (MEAS_DEC_AVE,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "RA:ERR"))     ESCAPE (MEAS_RA_AVE_ERR,  	MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC:ERR"))    ESCAPE (MEAS_DEC_AVE_ERR, 	MAG_NONE);
+
+  if (!strcasecmp (fieldName, "uRA"))        ESCAPE (MEAS_U_RA,        	MAG_NONE);
+  if (!strcasecmp (fieldName, "uDEC"))       ESCAPE (MEAS_U_DEC,       	MAG_NONE);
+  if (!strcasecmp (fieldName, "duRA"))       ESCAPE (MEAS_U_RA_ERR,    	MAG_NONE);
+  if (!strcasecmp (fieldName, "duDEC"))      ESCAPE (MEAS_U_DEC_ERR,   	MAG_NONE);
+  if (!strcasecmp (fieldName, "PAR"))        ESCAPE (MEAS_PAR,         	MAG_NONE);
+  if (!strcasecmp (fieldName, "dPAR"))       ESCAPE (MEAS_PAR_ERR,      MAG_NONE);
+  if (!strcasecmp (fieldName, "dR"))         ESCAPE (MEAS_RA_OFFSET,   	MAG_NONE);
+  if (!strcasecmp (fieldName, "dD"))         ESCAPE (MEAS_DEC_OFFSET,  	MAG_NONE);
+  if (!strcasecmp (fieldName, "dR:FIT"))     ESCAPE (MEAS_RA_FIT_OFFSET,  MAG_NONE);
+  if (!strcasecmp (fieldName, "dD:FIT"))     ESCAPE (MEAS_DEC_FIT_OFFSET, MAG_NONE);
+  if (!strcasecmp (fieldName, "dR:ERR"))     ESCAPE (MEAS_RA_OFFSET_ERR,  MAG_NONE);
+  if (!strcasecmp (fieldName, "dD:ERR"))     ESCAPE (MEAS_DEC_OFFSET_ERR, MAG_NONE);
+  if (!strcasecmp (fieldName, "xp"))         ESCAPE (MEAS_XP,          	MAG_NONE);
+  if (!strcasecmp (fieldName, "nmeas"))      ESCAPE (MEAS_NMEAS,       	MAG_NONE);
+  if (!strcasecmp (fieldName, "nmiss"))      ESCAPE (MEAS_NMISS,       	MAG_NONE);
+  if (!strcasecmp (fieldName, "objflags"))   ESCAPE (MEAS_OBJFLAGS,     MAG_NONE);
+  if (!strcasecmp (fieldName, "AIRMASS"))    ESCAPE (MEAS_AIRMASS,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "ALT"))        ESCAPE (MEAS_ALT,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "AZ"))         ESCAPE (MEAS_AZ,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "EXPTIME"))    ESCAPE (MEAS_EXPTIME,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "PHOTCODE"))   ESCAPE (MEAS_PHOTCODE,    	MAG_NONE);
+  if (!strcasecmp (fieldName, "TIME"))       ESCAPE (MEAS_TIME,        	MAG_NONE);
+  if (!strcasecmp (fieldName, "FWHM"))       ESCAPE (MEAS_FWHM,        	MAG_NONE);
+  if (!strcasecmp (fieldName, "FWHM_MAJ"))   ESCAPE (MEAS_FWHM_MAJ,    	MAG_NONE);
+  if (!strcasecmp (fieldName, "FWHM_MIN"))   ESCAPE (MEAS_FWHM_MIN,    	MAG_NONE);
+  if (!strcasecmp (fieldName, "THETA"))      ESCAPE (MEAS_THETA,       	MAG_NONE);
+  if (!strcasecmp (fieldName, "DOPHOT"))     ESCAPE (MEAS_DOPHOT,      	MAG_NONE);
+  if (!strcasecmp (fieldName, "DB_FLAGS"))   ESCAPE (MEAS_DB_FLAGS,    	MAG_NONE);
+  if (!strcasecmp (fieldName, "PHOT_FLAGS")) ESCAPE (MEAS_PHOT_FLAGS, 	MAG_NONE);
+  if (!strcasecmp (fieldName, "XCCD"))       ESCAPE (MEAS_XCCD, 	MAG_NONE);
+  if (!strcasecmp (fieldName, "YCCD"))       ESCAPE (MEAS_YCCD, 	MAG_NONE);
+  if (!strcasecmp (fieldName, "XMOSAIC"))    ESCAPE (MEAS_XMOSAIC,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "YMOSAIC"))    ESCAPE (MEAS_YMOSAIC,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "XCHIP"))      ESCAPE (MEAS_XCCD, 	MAG_NONE);
+  if (!strcasecmp (fieldName, "YCHIP"))      ESCAPE (MEAS_YCCD, 	MAG_NONE);
+  if (!strcasecmp (fieldName, "XFPA"))       ESCAPE (MEAS_XMOSAIC,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "YFPA"))       ESCAPE (MEAS_YMOSAIC,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "DETID"))      ESCAPE (MEAS_DET_ID,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "OBJID"))      ESCAPE (MEAS_OBJ_ID,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "IMAGEID"))    ESCAPE (MEAS_IMAGE_ID,     MAG_NONE);
+  if (!strcasecmp (fieldName, "PSF_QF"))     ESCAPE (MEAS_PSF_QF,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "PSF_CHISQ"))  ESCAPE (MEAS_PSF_CHISQ,    MAG_NONE);
+  if (!strcasecmp (fieldName, "CR_NSIGMA"))  ESCAPE (MEAS_CR_NSIGMA,    MAG_NONE);
+  if (!strcasecmp (fieldName, "EXT_NSIGMA")) ESCAPE (MEAS_EXT_NSIGMA,   MAG_NONE);
+  if (!strcasecmp (fieldName, "SKY"))        ESCAPE (MEAS_SKY,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "SKY_ERR"))    ESCAPE (MEAS_dSKY,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "STARGAL"))    ESCAPE (MEAS_STARGAL,     	MAG_NONE);
+
+  // for words that don't parse, try a photcode
+
+  // check for code:mode in photcode name 
+  code = ParsePhotcodeField (fieldName, &mode, MAG_REL);
+  if (code == NULL) return (FALSE);
+
+  field->ID = MEAS_MAG;
+  field->magMode = mode;
+  field->photcode = code;
+  return (TRUE);
+}
+  
+int ParseAverageField (dbField *field, char *fieldName) {
+
+  int mode;
+  PhotCode *code;
+
+  field->table = DVO_TABLE_AVERAGE;
+  field->name  = strcreate (fieldName);
+
+  // if either GLON or GLAT is requested, we set up a static tranformation 
+  // at prepare to calculate the values only once for each row
+  if (!strcasecmp (fieldName, "GLON"))  { 
+    dbExtractAveragesInitTransform (COORD_GALACTIC);
+    ESCAPE (AVE_GLON, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "GLAT"))  {
+    dbExtractAveragesInitTransform (COORD_GALACTIC);
+    ESCAPE (AVE_GLAT, MAG_NONE);
+  }
+
+  // if either ELON or ELAT is requested, we set up a static tranformation 
+  // at prepare to calculate the values only once for each row
+  if (!strcasecmp (fieldName, "ELON"))  { 
+    dbExtractAveragesInitTransform (COORD_ECLIPTIC);
+    ESCAPE (AVE_ELON, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "ELAT"))  {
+    dbExtractAveragesInitTransform (COORD_ECLIPTIC);
+    ESCAPE (AVE_ELAT, MAG_NONE);
+  }
+
+  if (!strcasecmp (fieldName, "RA"))    ESCAPE (AVE_RA,        MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC"))   ESCAPE (AVE_DEC,       MAG_NONE);
+  if (!strcasecmp (fieldName, "dRA"))   ESCAPE (AVE_RA_ERR,    MAG_NONE);
+  if (!strcasecmp (fieldName, "dDEC"))  ESCAPE (AVE_DEC_ERR,   MAG_NONE);
+  if (!strcasecmp (fieldName, "uRA"))   ESCAPE (AVE_U_RA,      MAG_NONE);
+  if (!strcasecmp (fieldName, "uDEC"))  ESCAPE (AVE_U_DEC,     MAG_NONE);
+  if (!strcasecmp (fieldName, "duRA"))  ESCAPE (AVE_U_RA_ERR,  MAG_NONE);
+  if (!strcasecmp (fieldName, "duDEC")) ESCAPE (AVE_U_DEC_ERR, MAG_NONE);
+  if (!strcasecmp (fieldName, "PAR"))   ESCAPE (AVE_PAR,       MAG_NONE);
+  if (!strcasecmp (fieldName, "dPAR"))  ESCAPE (AVE_PAR_ERR,   MAG_NONE);
+  if (!strcasecmp (fieldName, "Xp"))    ESCAPE (AVE_Xp,        MAG_NONE);
+  if (!strcasecmp (fieldName, "NMEAS")) ESCAPE (AVE_NMEAS,     MAG_NONE);
+  if (!strcasecmp (fieldName, "NMISS")) ESCAPE (AVE_NMISS,     MAG_NONE);
+  if (!strcasecmp (fieldName, "FLAG"))  ESCAPE (AVE_FLAG,      MAG_NONE);
+  if (!strcasecmp (fieldName, "TYPE"))  ESCAPE (AVE_TYPE,      MAG_NONE);
+  if (!strcasecmp (fieldName, "OBJID")) ESCAPE (AVE_OBJID,     MAG_NONE);
+
+  // for words that don't parse, try a photcode
+
+  // check for code:mode in photcode name 
+  code = ParsePhotcodeField (fieldName, &mode, MAG_AVE);
+  if (code == NULL) return (FALSE);
+  if (code[0].type == PHOT_MAG) {
+    gprint (GP_ERR, "'mag' is ambiguous for avextract\n");
+    free (code);
+    return (FALSE);
+  }
+
+  field->ID = AVE_MAG;
+  field->magMode = mode;
+  field->photcode = code;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbRPN.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbRPN.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbRPN.c	(revision 22322)
@@ -0,0 +1,210 @@
+# include "dvoshell.h"
+# define DUMPSTACK 0
+
+dbStack *dbRPN (int argc, char **argv, int *nstack) {
+  
+  int type, Nx, Ny;
+  int i, j, Nstack, Nop_stack, NSTACK;
+  dbStack *stack, *op_stack;
+
+  /* max total stack size is argc, though should be less, this is safe */
+  NSTACK = argc + 5;
+  ALLOCATE (stack, dbStack, NSTACK);
+  ALLOCATE (op_stack, dbStack, NSTACK);
+  for (i = 0; i < NSTACK; i++) {
+    dbInitStack (&stack[i]);
+    dbInitStack (&op_stack[i]);
+  }
+  
+  Nx = Ny = Nstack = Nop_stack = 0;
+  for (i = 0; i < argc; i++) {
+    
+    /* decide on priority of object */
+    type = 0;
+    /* unary operations */
+    if (!strcmp (argv[i], "abs"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "int"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "exp"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ten"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "log"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ln"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "sqrt"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "erf"))    { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sinh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cosh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asinh"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acosh"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sin"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cos"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "tan"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dsin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dcos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dtan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "atan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dasin"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dacos"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "datan"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "lgamma")) { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "rnd"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "xramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "yramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ramp"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "zero"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "--"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "not"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isinf"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isnan"))  { type = 9; goto gotit; }
+
+    /* binary operations */
+    if (!strcmp (argv[i], "^"))      { type = 8; goto gotit; }
+
+    if (!strcmp (argv[i], "@"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "/"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "*"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "%"))      { type = 7; goto gotit; }
+
+    if (!strcmp (argv[i], "+"))      { type = 6; goto gotit; }
+    if (!strcmp (argv[i], "-"))      { type = 6; goto gotit; }
+	
+    if (!strcmp (argv[i], "&"))      { type = 5; goto gotit; }
+    if (!strcmp (argv[i], "|"))      { type = 5; goto gotit; }
+
+    if (!strcmp (argv[i], "<"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], ">"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], "=="))     { type = 4; strcpy (argv[i], "E"); goto gotit; }
+    if (!strcmp (argv[i], "!="))     { type = 4; strcpy (argv[i], "N"); goto gotit; }
+    if (!strcmp (argv[i], "<="))     { type = 4; strcpy (argv[i], "L"); goto gotit; }
+    if (!strcmp (argv[i], ">="))     { type = 4; strcpy (argv[i], "G"); goto gotit; }
+    if (!strcmp (argv[i], ">>"))     { type = 4; strcpy (argv[i], "U"); goto gotit; }
+    if (!strcmp (argv[i], "<<"))     { type = 4; strcpy (argv[i], "D"); goto gotit; }
+
+    if (!strcmp (argv[i], "&&"))     { type = 3; strcpy (argv[i], "A"); goto gotit; }
+    if (!strcmp (argv[i], "||"))     { type = 3; strcpy (argv[i], "O"); goto gotit; }
+
+    if (!strcmp (argv[i], "("))      { type = 2; goto gotit; }
+    if (!strcmp (argv[i], ")"))      { type = 1; goto gotit; }
+
+  gotit:
+    /* choose how to deal with object */
+    switch (type) {
+      case 8:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
+	/* pop previous, higher operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type > type); j--) {
+	  stack[Nstack] = op_stack[j];
+	  op_stack[j].name = NULL;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	op_stack[Nop_stack].name = strcreate (argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 9: /* unary OPs */
+      case 7: /* binary OPs */
+      case 6:
+      case 5: 
+      case 4: 
+      case 3: 
+	/* pop previous, higher or equal operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type >= type); j--) {
+	  stack[Nstack] = op_stack[j];
+	  op_stack[j].name = NULL;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	op_stack[Nop_stack].name = strcreate (argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 2:  
+	/* push operator on OP stack */
+	op_stack[Nop_stack].name = strcreate (argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 1: 
+	/* pop rest of operators from OP stack to stack, looking for '(' */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != 2); j--) {
+	  stack[Nstack] = op_stack[j];
+	  op_stack[j].name = NULL;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	if ((j == -1) || (op_stack[j].type != 2)) {
+	  push_error ("syntax error: mismatched parenthesis");
+	  Nstack = 0;
+	  goto cleanup;
+	}
+	Nop_stack --;
+	break;
+      case 0:
+	/* place the value (number or vector/matrix name) on stack */
+	/* value of 'X' is used as sentinel until we sort out values */
+	stack[Nstack].name = strcreate (argv[i]);
+	stack[Nstack].type = 'X';
+	Nstack ++;
+	break;
+    }
+  }
+
+  /* dump remaining operators on stack, checking for ')' */
+  for (j = Nop_stack - 1; j >= 0; j--) {
+    if (op_stack[j].type == 2) {
+      push_error ("syntax error: mismatched parenthesis");
+      Nstack = 0;
+      goto cleanup;
+    }
+    stack[Nstack] = op_stack[j];
+    op_stack[j].name = NULL;
+    Nstack ++;
+  }
+
+cleanup: 
+  /*** free up unused stack space ***/
+  dbFreeStack (op_stack, NSTACK);
+  free (op_stack);
+  dbFreeStack (&stack[Nstack], NSTACK - Nstack);
+  REALLOCATE (stack, dbStack, MAX (Nstack, 1));
+  *nstack = Nstack;
+
+  for (i = 0; i < argc; i++) {
+    free (argv[i]);
+  }
+  free (argv);
+
+# if (DUMPSTACK)
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%s ", stack[i].name);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%d ", stack[i].type);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+# endif
+
+  return (stack);
+
+}
+
+/* here are the rules for parsing a math AOL expression to RPN:
+
+1) if object is a number, push on stack
+2) if object is a third order operand (exp, sin, cos), push on op stack
+3) if object is an open paren, push on op stack,
+4) if object is a second order operand, push on stack
+5) if object is a first order operand, pop all second order operands from stack 
+until paren, push on stack
+6) if object is an end paren, pop all objects from stack until paren, 
+pop next stack, if third order op
+7) if end of line, pop all remaining objects, second order first, etc.
+   
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbStackMath.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbStackMath.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbStackMath.c	(revision 22322)
@@ -0,0 +1,161 @@
+# include "dvoshell.h"
+
+static int NallocBinary = 0;
+static int NallocUnary = 0;
+
+dbStack *dbBinary (dbStack *V1, dbStack *V2, char *op, float *fields) {
+
+  int N;
+  float M1, M2;
+  dbStack *OUT;
+  
+  if (V1->type == 'F') {
+    N = V1->field;
+    M1 = fields[N];
+  } else {
+    M1 = V1->Float;
+  }
+  
+  if (V2->type == 'F') {
+    N = V2->field;
+    M2 = fields[N];
+  } else {
+    M2 = V2->Float;
+  }
+
+  NallocBinary ++;
+  ALLOCATE (OUT, dbStack, 1);
+  OUT->type = 'T';
+  OUT->name = NULL;
+
+  // use an enum for the op...
+  switch (op[0]) { 
+  case '+': 
+    OUT->Float = M1 + M2;
+    break; 
+  case '-': 
+    OUT->Float = M1 - M2;
+    break; 
+  case '*': 
+    OUT->Float = M1 * M2;
+    break; 
+  case '/': 
+    OUT->Float = M1 / M2;
+    break; 
+  case '%': 
+    OUT->Float = (int) M1 % (int) M2;
+    break; 
+  case 0x5e: 
+    OUT->Float = pow (M1, M2);
+    break; 
+  case 'D': 
+    OUT->Float = MIN (M1, M2);
+    break; 
+  case 'U': 
+    OUT->Float = MAX (M1, M2);
+    break; 
+  case '<': 
+    OUT->Float = (M1 < M2) ? 1 : 0;
+    break; 
+  case '>': 
+    OUT->Float = (M1 > M2) ? 1 : 0;
+    break; 
+  case '&': 
+    OUT->Float = ((int)M1 & (int)M2);
+    break; 
+  case '|': 
+    OUT->Float = ((int)M1 | (int)M2);
+    break; 
+  case 'E': 
+    OUT->Float = (M1 == M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    OUT->Float = (M1 != M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    OUT->Float = (M1 <= M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    OUT->Float = (M1 >= M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    OUT->Float = (M1 && M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    OUT->Float = (M1 || M2) ? 1 : 0;
+    break; 
+  default:
+    return (NULL);
+  }
+
+  return (OUT);
+}
+
+dbStack *dbUnary (dbStack *V1, char *op, float *fields) {
+
+  int N;
+  float M1;
+  dbStack *OUT;
+
+  if (V1->type == 'F') {
+    N = V1->field;
+    M1 = fields[N];
+  } else {
+    M1 = V1->Float;
+  }
+  
+  NallocUnary ++;
+  ALLOCATE (OUT, dbStack, 1);
+  OUT->type = 'T';
+  OUT->name = NULL;
+
+  if (!strcmp (op, "="))      {   OUT->Float = M1;                 }
+  if (!strcmp (op, "abs"))    {   OUT->Float = fabs(M1);           }
+  if (!strcmp (op, "int"))    {   OUT->Float = (float)(int)(M1);   }
+  if (!strcmp (op, "exp"))    {   OUT->Float = exp (M1);           }
+  if (!strcmp (op, "ten"))    {   OUT->Float = pow (10.0,M1);      }
+  if (!strcmp (op, "log"))    {   OUT->Float = log10 (M1);         }
+  if (!strcmp (op, "ln"))     {   OUT->Float = log (M1);           }
+  if (!strcmp (op, "sqrt"))   {   OUT->Float = sqrt (M1);          }
+  if (!strcmp (op, "erf"))    {   OUT->Float = erf (M1);           }
+			      			      
+  if (!strcmp (op, "sinh"))   {   OUT->Float = sinh (M1);          }
+  if (!strcmp (op, "cosh"))   {   OUT->Float = cosh (M1);          }
+  if (!strcmp (op, "asinh"))  {   OUT->Float = asinh (M1);         }
+  if (!strcmp (op, "acosh"))  {   OUT->Float = acosh (M1);         }
+  if (!strcmp (op, "lgamma")) {   OUT->Float = lgamma (M1);        }
+
+  if (!strcmp (op, "sin"))    {   OUT->Float = sin (M1);           }
+  if (!strcmp (op, "cos"))    {   OUT->Float = cos (M1);           }
+  if (!strcmp (op, "tan"))    {   OUT->Float = tan (M1);           }
+  if (!strcmp (op, "dsin"))   {   OUT->Float = sin (M1*RAD_DEG);   }
+  if (!strcmp (op, "dcos"))   {   OUT->Float = cos (M1*RAD_DEG);   }
+  if (!strcmp (op, "dtan"))   {   OUT->Float = tan (M1*RAD_DEG);   }
+  if (!strcmp (op, "asin"))   {   OUT->Float = asin (M1);          }
+  if (!strcmp (op, "acos"))   {   OUT->Float = acos (M1);          }
+  if (!strcmp (op, "atan"))   {   OUT->Float = atan (M1);          }
+  if (!strcmp (op, "dasin"))  {   OUT->Float = asin (M1)*DEG_RAD;  }
+  if (!strcmp (op, "dacos"))  {   OUT->Float = acos (M1)*DEG_RAD;  }
+  if (!strcmp (op, "datan"))  {   OUT->Float = atan (M1)*DEG_RAD;  }
+  if (!strcmp (op, "rnd"))    {   OUT->Float = drand48();          }
+  if (!strcmp (op, "not"))    {   OUT->Float = !(M1);              }
+  if (!strcmp (op, "--"))     {   OUT->Float = - (M1);             }
+  if (!strcmp (op, "isinf"))  {   OUT->Float = !finite(M1);        }
+  if (!strcmp (op, "isnan"))  {   OUT->Float = isnan(M1);          } 
+
+  return (OUT);
+}
+
+int dbStackAllocPrint () {
+
+  fprintf (stderr, "dbAllocBinary: %d\n", NallocBinary);
+  fprintf (stderr, "dbAllocUnary:  %d\n", NallocUnary);
+  return (TRUE);
+}
+
+int dbStackAllocReset () {
+
+  NallocBinary = 0;
+  NallocUnary = 0;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbStackOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbStackOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dbStackOps.c	(revision 22322)
@@ -0,0 +1,51 @@
+# include "dvoshell.h"
+
+void dbInitStack (dbStack *stack) {
+  stack[0].type   = 0;
+  stack[0].name   = NULL;
+}
+
+// free data for stack entries (free stack explicitly)
+void dbFreeStack (dbStack *stack, int Nstack) {
+
+  int i;
+
+  if (stack == NULL) return;
+
+  for (i = 0; i < Nstack; i++) {
+    if (stack[i].name != NULL) {
+      free (stack[i].name);
+      stack[i].name = NULL;
+    }
+  }
+}
+
+static int NfreeStack = 0;
+
+/* delete name and data */
+void dbFreeEntry (dbStack *stack) {
+
+  if (stack[0].name != NULL)  free (stack[0].name);
+  free (stack);
+  NfreeStack ++;
+}
+
+/* delete name and data */
+void dbFreeTempEntry (dbStack *stack) {
+
+  if (stack->type != 'T') return;
+
+  if (stack[0].name != NULL)  free (stack[0].name);
+  free (stack);
+  NfreeStack ++;
+}
+
+void dbStackFreePrint () {
+
+  fprintf (stderr, "dbFreeStack: %d\n", NfreeStack);
+}
+
+void dbStackFreeReset () {
+
+  NfreeStack = 0;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/db_check_stack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/db_check_stack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/db_check_stack.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "opihi.h"
+
+int db_check_stack (dvoStack *stack, int Nstack, int table, dvoField *fields, int *Nfields) {
+
+  int i, Nx, Ny, Nv, size;
+  char *c;
+
+  NFIELDS = *Nfields + 10;
+  REALLOCATE (fields, dvoField, NFIELDS);
+
+  for (i = 0; i < Nstack; i++) {
+    if (stack[i].type == 'X') {
+
+      /** if this is a number, put it on the list of scalers and move on **/
+      stack[i].Float = strtod (stack[i].name, &c);
+      if (c == stack[i].name + strlen (stack[i].name)) {
+	stack[i].type  = 'S';
+	continue;
+      } 
+
+      // this must be a field : is it already in the list?
+      for (j = 0; (j < *Nfields) && strcasecmp (stack[i].name, fields[j].name); j++);
+      if (j < *Nfields) {
+	stack[i].field = j;
+	stack[i].type  = 'F';
+	continue;
+      }
+
+      // this must be a field : is it a valid name?
+      if (table == DVO_TABLE_MEASURE) {
+	status = ParseMeasureField (&fields[*Nfields], stack[i].name);
+      } 
+      if (table == DVO_TABLE_AVERAGE) {
+	status = ParseAverageField (&fields[*Nfields], stack[i].name);
+      } 
+      if (!status) {
+	gprint (GP_ERR, "unknown database field %s\n", stack[i].name);
+	return (FALSE);
+      }
+      stack[i].field = *Nfields;
+      stack[i].type  = 'F';
+
+      *Nfields ++;
+      CHECK_REALLOCATE (fields, dvoField, NFIELDS, *Nfields, 10);
+    }
+  }
+  return (TRUE);
+}
+
+/* check stack identifies the data elements as plain scalars or table fields
+   operators have already been identified.  
+   check stack returns the total stack dimensionality (0,1,2)
+   on error, check stack returns FALSE
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/db_convert_to_RPN.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/db_convert_to_RPN.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/db_convert_to_RPN.c	(revision 22322)
@@ -0,0 +1,210 @@
+# include "opihi.h"
+# define DUMPSTACK 0
+
+dvoStack *convert_to_RPN (int argc, char **argv, int *nstack) {
+  
+  int type, Nx, Ny;
+  int i, j, Nstack, Nop_stack, NSTACK;
+  dvoStack *stack, *op_stack;
+
+  /* max total stack size is argc, though should be less, this is safe */
+  NSTACK = argc + 5;
+  ALLOCATE (stack, dvoStack, NSTACK);
+  ALLOCATE (op_stack, dvoStack, NSTACK);
+  for (i = 0; i < NSTACK; i++) {
+    init_stack (&stack[i]);
+    init_stack (&op_stack[i]);
+  }
+  
+  Nx = Ny = Nstack = Nop_stack = 0;
+  for (i = 0; i < argc; i++) {
+    
+    /* decide on priority of object */
+    type = 0;
+    /* unary operations */
+    if (!strcmp (argv[i], "abs"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "int"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "exp"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ten"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "log"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ln"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "sqrt"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "erf"))    { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sinh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cosh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asinh"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acosh"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sin"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cos"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "tan"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dsin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dcos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dtan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "atan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dasin"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dacos"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "datan"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "lgamma")) { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "rnd"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "xramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "yramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ramp"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "zero"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "--"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "not"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isinf"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isnan"))  { type = 9; goto gotit; }
+
+    /* binary operations */
+    if (!strcmp (argv[i], "^"))      { type = 8; goto gotit; }
+
+    if (!strcmp (argv[i], "@"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "/"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "*"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "%"))      { type = 7; goto gotit; }
+
+    if (!strcmp (argv[i], "+"))      { type = 6; goto gotit; }
+    if (!strcmp (argv[i], "-"))      { type = 6; goto gotit; }
+	
+    if (!strcmp (argv[i], "&"))      { type = 5; goto gotit; }
+    if (!strcmp (argv[i], "|"))      { type = 5; goto gotit; }
+
+    if (!strcmp (argv[i], "<"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], ">"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], "=="))     { type = 4; strcpy (argv[i], "E"); goto gotit; }
+    if (!strcmp (argv[i], "!="))     { type = 4; strcpy (argv[i], "N"); goto gotit; }
+    if (!strcmp (argv[i], "<="))     { type = 4; strcpy (argv[i], "L"); goto gotit; }
+    if (!strcmp (argv[i], ">="))     { type = 4; strcpy (argv[i], "G"); goto gotit; }
+    if (!strcmp (argv[i], ">>"))     { type = 4; strcpy (argv[i], "U"); goto gotit; }
+    if (!strcmp (argv[i], "<<"))     { type = 4; strcpy (argv[i], "D"); goto gotit; }
+
+    if (!strcmp (argv[i], "&&"))     { type = 3; strcpy (argv[i], "A"); goto gotit; }
+    if (!strcmp (argv[i], "||"))     { type = 3; strcpy (argv[i], "O"); goto gotit; }
+
+    if (!strcmp (argv[i], "("))      { type = 2; goto gotit; }
+    if (!strcmp (argv[i], ")"))      { type = 1; goto gotit; }
+
+  gotit:
+    /* choose how to deal with object */
+    switch (type) {
+      case 8:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
+	/* pop previous, higher operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type > type); j--) {
+	  strcpy (stack[Nstack].name, op_stack[j].name);
+	  stack[Nstack].type = op_stack[j].type;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	strcpy (op_stack[Nop_stack].name, argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 9: /* unary OPs */
+      case 7: /* binary OPs */
+      case 6:
+      case 5: 
+      case 4: 
+      case 3: 
+	/* pop previous, higher or equal operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type >= type); j--) {
+	  strcpy (stack[Nstack].name, op_stack[j].name);
+	  stack[Nstack].type = op_stack[j].type;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	strcpy (op_stack[Nop_stack].name, argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 2:  
+	/* push operator on OP stack */
+	strcpy (op_stack[Nop_stack].name, argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 1: 
+	/* pop rest of operators from OP stack to stack, looking for '(' */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != 2); j--) {
+	  strcpy (stack[Nstack].name, op_stack[j].name);
+	  stack[Nstack].type = op_stack[j].type;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	if ((j == -1) || (op_stack[j].type != 2)) {
+	  push_error ("syntax error: mismatched parenthesis");
+	  Nstack = 0;
+	  goto cleanup;
+	}
+	Nop_stack --;
+	break;
+      case 0:
+	/* place the value (number or vector/matrix name) on stack */
+	/* value of 'X' is used as sentinel until we sort out values */
+	strcpy (stack[Nstack].name, argv[i]);
+	stack[Nstack].type = 'X';
+	Nstack ++;
+	break;
+    }
+  }
+
+  /* dump remaining operators on stack, checking for ')' */
+  for (j = Nop_stack - 1; j >= 0; j--) {
+    if (op_stack[j].type == 2) {
+      push_error ("syntax error: mismatched parenthesis");
+      Nstack = 0;
+      goto cleanup;
+    }
+    strcpy (stack[Nstack].name, op_stack[j].name);
+    stack[Nstack].type = op_stack[j].type;
+    Nstack ++;
+  }
+
+cleanup: 
+  /*** free up unused stack space ***/
+  clean_stack (op_stack, NSTACK);
+  free (op_stack);
+  clean_stack (&stack[Nstack], NSTACK - Nstack);
+  REALLOCATE (stack, dvoStack, MAX (Nstack, 1));
+  *nstack = Nstack;
+
+  for (i = 0; i < argc; i++) {
+    free (argv[i]);
+  }
+  free (argv);
+
+# if (DUMPSTACK)
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%s ", stack[i].name);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%d ", stack[i].type);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+# endif
+
+  return (stack);
+
+}
+
+/* here are the rules for parsing a math AOL line to RPN:
+
+1) if object is a number, push on stack
+2) if object is a third order operand (exp, sin, cos), push on op stack
+3) if object is an open paren, push on op stack,
+4) if object is a second order operand, push on stack
+5) if object is a first order operand, pop all second order operands from stack 
+until paren, push on stack
+6) if object is an end paren, pop all objects from stack until paren, 
+pop next stack, if third order op
+7) if end of line, pop all remaining objects, second order first, etc.
+   
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ddmags.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ddmags.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/ddmags.c	(revision 22322)
@@ -0,0 +1,136 @@
+# include "dvoshell.h"
+
+int ddmags (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, *M2;
+  int i, m, k, N, Npts, NPTS;
+  int N1, N2, i1, i2, mode[4];
+  int Nsecfilt, KeepNulls;
+
+  Catalog catalog;
+  PhotCode *code[4];
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line options */
+  if (argc != 8) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (strcmp (argv[6], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[7], &code[3], &mode[3])) return (FALSE);
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    // gprint (GP_ERR, "seaching %s with %d stars\n", catalog.filename, catalog.Naverage);
+    // ListPhotSelections ();
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M2 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (&code[0], &mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M2 = ExtractDMag (&code[2], &mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N2);
+      if (N2 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M2, double, 1);
+	  N2 = 1;
+	  M2[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i2 = 0; i2 < N2; i2++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M2[i2];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M2 != NULL) free (M2);
+    }
+    // gprint (GP_ERR, "selected %d stars\n", Npts);
+    dvo_catalog_free (&catalog);
+  }
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: ddmags F - F : measure.param\n");
+
+escape:
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/detrend.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/detrend.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/detrend.c	(revision 22322)
@@ -0,0 +1,207 @@
+# include "dvoshell.h"
+
+/* qualities to be extracted */
+enum {ZERO, DTIME, SKY, BIAS, FWHM, AIRM, TIME, TEMP};
+
+int detrend (int argc, char **argv) {
+ 
+  FILE *f;
+  int i, Nimage, status, N, TimeSelect;
+  char DataBase[256];
+  time_t tzero, tend;
+  double trange;
+  int TypeSelect, CCDSelect, FilterSelect;
+  char *Filter;
+  int Type, mode, CCD;
+  int NVALUE;
+  float *value;
+  time_t TimeReference;
+  int TimeFormat;
+  Header header;
+  RegImage *pimage;
+  Vector *vec;
+
+  VarConfig ("REGISTRATION_DATABASE", "%s", DataBase);
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  Type = 0;
+  TypeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    Type = get_image_type (argv[N]);
+    if (Type == T_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image type %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TypeSelect = TRUE;
+  }
+
+  CCD = 0;
+  CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    CCD = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    CCDSelect = TRUE;
+  }
+ 
+  Filter = NULL;
+  FilterSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    Filter = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    FilterSelect = TRUE;
+    if (!strcasecmp (Filter, "X")) {
+      FilterSelect = FALSE;
+    }
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imrough (value)\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, "  this function is not well-defined.  re-work and re-code\n");
+  return (FALSE);
+  
+  /* identify selection */
+  mode = ZERO;
+  if (!strcasecmp (argv[1], "exptime")) mode = DTIME;
+  if (!strcasecmp (argv[1], "sky")) mode = SKY;
+  if (!strcasecmp (argv[1], "bias")) mode = BIAS;
+  if (!strcasecmp (argv[1], "fwhm")) mode = FWHM;
+  if (!strcasecmp (argv[1], "airmass")) mode = AIRM;
+  if (!strcasecmp (argv[1], "time")) mode = TIME;
+  if (!strcasecmp (argv[1], "temp")) mode = TEMP;
+  if (mode == ZERO) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " exptime, sky, bias, fwhm, airmass, time\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* load in database header */
+  if (!gfits_read_header (DataBase, &header)) {
+    gprint (GP_ERR, "ERROR: trouble reading database header\n");
+    return (FALSE);
+  }
+
+  /* open database */
+  f = fopen (DataBase, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't open Registration Database\n");
+    return (FALSE);
+  }
+  fseek (f, header.size, SEEK_SET);
+
+  /* load existing data from database */
+  gfits_scan (&header, "NIMAGES", "%d", 1, &Nimage);
+  ALLOCATE (pimage, RegImage, Nimage);
+  status = fread (pimage, sizeof(RegImage), Nimage, f);
+  fclose (f);
+
+  if (status != Nimage) {
+    gprint (GP_ERR, "ERROR: header and data in dB don't match (%d vs %d)\n", Nimage, status);
+    gfits_free_header (&header);
+    free (pimage);
+    return (FALSE);
+  }
+  gfits_convert_RegImage (pimage, sizeof (RegImage), Nimage);
+
+  N = 0;
+  NVALUE = 1000;
+  REALLOCATE (vec[0].elements, float, NVALUE);
+  value = vec[0].elements;
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+  gprint (GP_ERR, "%ld %d\n", TimeReference, TimeFormat);
+
+  /* get data */
+  for (i = 0; i < Nimage; i++) {
+    /* skip unmatched selections */
+    if (TimeSelect && ((pimage[i].obstime < tzero) || (pimage[i].obstime > tzero + trange))) continue;
+    if (TimeSelect && ((pimage[i].obstime < tzero) || (pimage[i].obstime > tzero + trange))) continue;
+    if (FilterSelect && (strcasecmp (pimage[i].filter, Filter))) continue;
+    if (CCDSelect && (pimage[i].ccd != CCD)) continue;
+    if (TypeSelect && (pimage[i].type != Type)) continue;
+
+    /* assign correct value */
+    switch (mode) {
+    case (DTIME):
+      value[N] = pimage[i].exptime;
+      break;
+    case (TIME):
+      value[N] = TimeValue (pimage[i].obstime, TimeReference, TimeFormat);
+      break;
+    case (SKY):
+      value[N] = pimage[i].sky;
+      break;
+    case (BIAS):
+      value[N] = pimage[i].bias;
+      break;
+    case (FWHM):
+      value[N] = pimage[i].fwhm;
+      break;
+    case (AIRM):
+      value[N] = pimage[i].airmass;
+      break;
+    case (TEMP):
+      value[N] = pimage[i].teltemp_0;
+      break;
+    }
+    N++;
+    if (N >= NVALUE - 1) {
+      NVALUE += 1000;
+      REALLOCATE (vec[0].elements, float, NVALUE);
+      value = vec[0].elements;
+    }
+  }
+
+  REALLOCATE (vec[0].elements, float, MAX (1,N));
+  vec[0].Nelements = N;
+
+  free (pimage);
+  gfits_free_header (&header);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmagaves.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmagaves.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmagaves.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "dvoshell.h"
+
+int dmagaves (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, M2;
+  int i, j, k, m, N1;
+  int Npts, NPTS, param, mode[3];
+  int Nsecfilt;
+
+  PhotCode *code[3];
+  Catalog catalog;
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+  code[2] = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  /* interpret command-line options: dmagaves F1 - F2 : (value) */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if ((param = GetAverageParam (argv[5])) == AVE_ZERO) goto usage;
+  if (!TestPhotSelections (&code[2], &mode[2], param)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M2 = ExtractAverages (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], param);
+
+      for (k = 0; k < N1; k++) {
+	xvec[0].elements[Npts] = M1[k];
+	yvec[0].elements[Npts] = M2;
+	Npts++;
+	if (Npts >= NPTS) {
+	  NPTS += 2000;
+	  REALLOCATE (xvec[0].elements, float, NPTS);
+	  REALLOCATE (yvec[0].elements, float, NPTS);
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+    }
+    dvo_catalog_free (&catalog);
+  }
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: dmagaves F - F : average.param\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmagmeas.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmagmeas.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmagmeas.c	(revision 22322)
@@ -0,0 +1,135 @@
+# include "dvoshell.h"
+
+int dmagmeas (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, *M3;
+  int i, j, m, i1, i3, N1, N3, N;
+  int Npts, NPTS, param, mode[3];
+  int Nsecfilt, KeepNulls;
+
+  Catalog catalog;
+  PhotCode *code[3];
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+  code[2] = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line arguments: dmagmeas F1 - F2 : (value) */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if ((param = GetMeasureParam (argv[5])) == MEAS_ZERO) goto usage;
+  if (!TestPhotSelections (&code[2], &mode[2], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+  if (!SetImageSelection (((param == MEAS_XMOSAIC) || (param == MEAS_YMOSAIC)), ((RegionName == NULL) && (RegionList == NULL)))) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M3 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M3 = ExtractMeasures (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N3, param);
+      if (N3 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M3, double, 1);
+	  N3 = 1;
+	  M3[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i3 = 0; i3 < N3; i3++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M3[i3];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M3 != NULL) free (M3);
+    }
+    dvo_catalog_free (&catalog);
+  }
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: dmagmeas F - F : measure.param\n");
+  return (FALSE);
+
+escape:
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmags.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmags.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmags.c	(revision 22322)
@@ -0,0 +1,136 @@
+# include "dvoshell.h"
+
+int dmags (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, *M3;
+  int i, j, m, i1, i3, N1, N3, N;
+  int Npts, NPTS, mode[3];
+  int Nsecfilt, KeepNulls;
+
+  PhotCode *code[3];
+  Catalog catalog;
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret required command-line arguments: dmags F1 - F2 : F3 */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) goto usage;
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M3 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M3 = ExtractMagnitudes (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N3);
+      if (N3 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M3, double, 1);
+	  N3 = 1;
+	  M3[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i3 = 0; i3 < N3; i3++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M3[i3];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M3 != NULL) free (M3);
+    }
+    dvo_catalog_free (&catalog);
+  }
+
+  /* need to free SkyList / or free all regions as well */
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: dmags F - F : F\n");
+  gprint (GP_ERR, "    F : any photcodes with matched qualifiers:\n");
+  gprint (GP_ERR, "    pri: F:inst, F:cat, F:sys, F:rel, F:cal, F:ave, F:ref\n");
+  gprint (GP_ERR, "    sec: F:inst, F:cat, F:sys, F:rel, F:cal, F:ave, F:ref\n");
+  gprint (GP_ERR, "    dep: F:inst, F:cat, F:sys, F:rel, F:cal\n");
+  gprint (GP_ERR, "    ref: F:cat\n");
+
+escape:
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmt.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmt.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dmt.c	(revision 22322)
@@ -0,0 +1,157 @@
+# include "dvoshell.h"
+
+/* extract vectors giving delta mags for multiple measurements */ 
+int dmt (int argc, char **argv) {
+  
+  // XXX this needs to be fixed: how to access different graphs at once?
+  gprint (GP_ERR, "ERROR: this function is currently disabled\n");
+  return (FALSE);
+
+# if (0)
+
+  int i, m, k, N, kapa, SaveVectors;
+  int Nsec, Nsecfilt, NPTS;
+  double Radius;
+  float dt1, dt2, dmt1, dmt2;
+  float M0, M1, M2, M3;
+  PhotCode *code;
+  Catalog catalog;
+  Graphdata graphmode, graphsky;
+  SkyTable *sky;
+  SkyList *skylist;
+  Vector Xvec, Yvec, Zvec, Rvec, Dvec;
+  Vector *vec1, *vec2, *vec3, *vec4, *vec5;
+
+  Dvec.elements = Rvec.elements = Zvec.elements = NULL;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  vec1 = vec2 = vec3 = vec4 = vec5 = NULL;
+  SaveVectors = FALSE;
+  if ((N = get_argument (argc, argv, "-vect"))) {
+    remove_argument (N, &argc, argv);
+    if ((vec1 = SelectVector ("dmtdmt", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec2 = SelectVector ("dmtvar", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec3 = SelectVector ("dmtmag", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec4 = SelectVector ("dmtra",  ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec5 = SelectVector ("dmtdec", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    SaveVectors = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: dmags filter\n");
+    return (FALSE);
+  }
+
+  if (!GetGraphData (&graphsky, &kapa, NULL)) return (FALSE);
+  if (!GetGraph (&graphmode, NULL, NULL)) return (FALSE);
+
+  if ((code = GetPhotcodebyName (argv[1])) == NULL) {
+    gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+    return (FALSE);
+  }
+  if (code[0].type != PHOT_SEC) {
+    gprint (GP_ERR, "first filter must be a average photometry type\n");
+    return (FALSE);
+  }
+  Nsecfilt = GetPhotcodeNsecfilt();
+  Nsec = GetPhotcodeNsec (code[0].code);
+
+  Radius = MAX (fabs(graphsky.xmax), fabs(graphsky.ymax));
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphsky.coords.crval1, graphsky.coords.crval2, Radius);
+  
+  N = 0;
+  NPTS = catalog.Nmeasure;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  if (SaveVectors) {
+    ALLOCATE (Zvec.elements, float, NPTS);
+    ALLOCATE (Rvec.elements, float, NPTS);
+    ALLOCATE (Dvec.elements, float, NPTS);
+  }
+
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    for (i = 0; i < catalog.Naverage; i++) {
+      if (catalog.average[i].Nm != 3) continue;
+      m = catalog.average[i].offset;
+      M0 = catalog.secfilt[i*Nsecfilt+Nsec].M;
+      M1 = PhotCat (&catalog.measure[m+0]);
+      M2 = PhotCat (&catalog.measure[m+1]);
+      M3 = PhotCat (&catalog.measure[m+2]);
+
+      dt1 = (catalog.measure[m+0].t < catalog.measure[m+1].t) ? catalog.measure[m+1].t - catalog.measure[m+0].t : -1 * ((float)(catalog.measure[m+1].t - catalog.measure[m+0].t));
+      dt2 = (catalog.measure[m+1].t < catalog.measure[m+2].t) ? catalog.measure[m+2].t - catalog.measure[m+1].t : -1 * ((float)(catalog.measure[m+2].t - catalog.measure[m+1].t));
+      dmt1 = (M2 - M1) / dt1;
+      dmt2 = (M3 - M2) / dt2;
+      Xvec.elements[N] = (dmt1 - dmt2) / (dmt1 + dmt2);
+      Yvec.elements[N] = (dmt1 + dmt2) / 2.0;
+      if (SaveVectors) {
+	Rvec.elements[N] = catalog.average[i].R;
+	Dvec.elements[N] = catalog.average[i].D;
+	Zvec.elements[N] = M0;
+      }
+      N++;
+      if (N == NPTS - 1) {
+	NPTS += 2000;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+	if (SaveVectors) {
+	  REALLOCATE (Zvec.elements, float, NPTS);
+	  REALLOCATE (Rvec.elements, float, NPTS);
+	  REALLOCATE (Dvec.elements, float, NPTS);
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+  }
+  Yvec.Nelements = Xvec.Nelements = N;
+  REALLOCATE (Xvec.elements, float, MAX (1, N));
+  REALLOCATE (Yvec.elements, float, MAX (1, N));
+  if (SaveVectors) {
+    Rvec.Nelements = Dvec.Nelements = Zvec.Nelements = N;
+    REALLOCATE (Zvec.elements, float, MAX (1, N));
+    REALLOCATE (Rvec.elements, float, MAX (1, N));
+    REALLOCATE (Dvec.elements, float, MAX (1, N));
+  }
+
+  if (SaveVectors) {
+    free (vec1[0].elements);
+    vec1[0].elements = Yvec.elements;
+    vec1[0].Nelements = Yvec.Nelements;
+    free (vec2[0].elements);
+    vec2[0].elements = Xvec.elements;
+    vec2[0].Nelements = Xvec.Nelements;
+    free (vec3[0].elements);
+    vec3[0].elements = Zvec.elements;
+    vec3[0].Nelements = Zvec.Nelements;
+    free (vec4[0].elements);
+    vec4[0].elements = Rvec.elements;
+    vec4[0].Nelements = Rvec.Nelements;
+    free (vec5[0].elements);
+    vec5[0].elements = Dvec.elements;
+    vec5[0].Nelements = Dvec.Nelements;
+  } else {
+    graphmode.style = 2; /* set style to points */
+    PlotVector (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+    free (Xvec.elements);
+    free (Yvec.elements);
+    free (Zvec.elements);
+  }
+  return (TRUE);
+# endif 
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvo.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvo.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvo.c.in	(revision 22322)
@@ -0,0 +1,73 @@
+# include "dvoshell.h"
+
+# define opihi_name "DVO"
+# define opihi_prompt "dvo: "
+# define opihi_description "desktop virtual observatory\n"
+# define opihi_history ".dvo"
+# define opihi_rcfile ".dvorc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitDVO ();
+
+  // XXX don't open the catdir until needed
+  // if (!SetCATDIR (NULL, FALSE)) {
+  //  gprint (GP_ERR, "CATDIR is not defined\n");
+  // }
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreeDVO ();
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvoBooleanElements.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvoBooleanElements.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvoBooleanElements.c	(revision 22322)
@@ -0,0 +1,158 @@
+# include "opihi.h"
+
+/* local private functions */
+void InsertValue (char c);
+void EndOfString (void);
+int IsAnOp (char *c);
+int IsTwoOp (char *c);
+
+/* local private static variables */
+int Nchar, Nout, NOUT;
+char **out;
+
+// split up the input arguments into appropriate blocks
+char **dvoBooleanElements (int Nin, char **in, int *nout) {
+  
+  int i, j, minus, negate, plus, posate, OpStat, SciNotation;
+
+  NOUT = Nin;
+  Nchar = Nout = 0;
+  ALLOCATE (out, char *, NOUT);
+  ALLOCATE (out[Nout], char, NCHARS);
+
+  for (i = 0; i < Nin; i++) {
+    for (j = 0; j < strlen(in[i]); j++) {
+      SciNotation = FALSE;
+      /* identify 'negate' or 'minus' ops */
+      negate = minus = FALSE;
+      if (in[i][j] == '-') { 
+	minus = TRUE;  
+	/* if - is first thing on line, must be a negator */
+	if ((Nout == 0) && (Nchar == 0)) {  
+	  minus = FALSE;
+	  negate = TRUE;
+	  goto skip1;
+	}
+	/* check previous entry on line */
+	if (Nchar) {
+	  OpStat = IsAnOp (out[Nout]);
+	  if (out[Nout][0] == ')') OpStat = FALSE;
+	} else {
+	  OpStat = IsAnOp (out[Nout-1]);
+	  if (out[Nout-1][0] == ')') OpStat = FALSE;
+	}
+	/* if - follows an operator, must be negator */
+	if (OpStat) {
+	  minus = FALSE;
+	  negate = TRUE;
+	  goto skip1;
+	}
+	/* if - follows 'e' is part of 1e-5 */
+	if (j == 0) goto skip1;
+	if ((in[i][j-1] == 'e') || (in[i][j-1] == 'E')) {
+	  SciNotation = TRUE;
+	  negate = minus = FALSE;
+	}
+      }
+    skip1:
+      /* idenfity 'posate' or 'plus' ops */
+      posate = plus = FALSE;
+      if (in[i][j] == '+') { 
+	plus = TRUE;  
+	/* if + is first thing on line, must be a posator */
+	if ((Nout == 0) && (Nchar == 0)) {  
+	  plus = FALSE;
+	  posate = TRUE;
+	  goto skip2;
+	}
+	/* check previous entry on line */
+	if (Nchar) {
+	  OpStat = IsAnOp (out[Nout]);
+	  if (out[Nout][0] == ')') OpStat = FALSE;
+	} else {
+	  OpStat = IsAnOp (out[Nout-1]);
+	  if (out[Nout-1][0] == ')') OpStat = FALSE;
+	}
+	/* if + follows an operator, must be posator */
+	if (OpStat) {
+	  plus = FALSE;
+	  posate = TRUE;
+	  goto skip2;
+	}
+	/* if + follows 'e' is part of 1e+5 */
+	if (j == 0) goto skip2;
+	if ((in[i][j-1] == 'e') || (in[i][j-1] == 'E')) {
+	  SciNotation = TRUE;
+	  posate = plus = FALSE;
+	}
+      }
+    skip2:
+      /* operators */
+      if (negate || minus || posate || plus || (IsAnOp (&in[i][j]) && !SciNotation)) {
+	if (posate) continue;
+	EndOfString ();
+	/* copy operator to out[Nout] */
+	InsertValue (in[i][j]);
+	if (negate) InsertValue ('-');
+
+	if (IsTwoOp (&in[i][j])) {
+	  InsertValue (in[i][j+1]);
+	  j++;
+	} 
+	EndOfString ();
+	continue;
+      }
+      /* quoted string */
+      if (in[i][j] == '"') {
+	InsertValue (in[i][j]);
+	j++;
+	while ((j < strlen(in[i])) && (in[i][j] != '"')) {
+	  InsertValue (in[i][j]);
+	  j++;
+	}
+	if (in[i][j] != '"') continue;
+	/* 
+	  gprint (GP_ERR, "mismatched quotes\n");
+	  return (FALSE);
+	}
+	*/
+	InsertValue (in[i][j]);
+	EndOfString ();
+	continue;
+      }
+      /* not an operator, not a quoted string */
+      if (!OHANA_WHITESPACE (in[i][j])) {
+	InsertValue (in[i][j]);
+      } else {
+	EndOfString ();
+      }
+    }
+    EndOfString ();
+  }
+
+  /* one extra entry is allocated, free here */
+  free (out[Nout]);
+  *nout = Nout;
+  return (out);
+
+}
+
+void InsertValue (char c) {
+  out[Nout][Nchar] = c;
+  Nchar ++;
+  out[Nout][Nchar] = 0;
+}
+
+void EndOfString () {
+  if (Nchar > 0) {
+    out[Nout][Nchar] = 0;
+    Nout ++;
+    Nchar = 0;
+    
+    if (Nout >= NOUT - 1) {
+      NOUT += 10; 
+      REALLOCATE (out, char *, NOUT); 
+    } 
+    ALLOCATE (out[Nout], char, NCHARS); 
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvoEvaluateStack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvoEvaluateStack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvoEvaluateStack.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "opihi.h"
+
+int CheckBooleanCondition (dvoStack dbStack, int NdbStack, float *values, dvoFields *fields, int Nfields) {
+  
+  int i, j, Nstack;
+  dvoStack **stack, *output;
+
+  Nstack = NdbStack;
+  ALLOCATE (stack, dvoStack *, NdbStack);
+  for (i = 0; i < NdbStack; i++) {
+    stack[i] = &dbStack[i];
+  }
+
+  for (i = 0; i < Nstack; i++) {
+
+    /***** binary operators *****/
+    if ((stack[i].type >= 3) && (stack[i].type <= 8)) {
+
+      // pre-test that op and entries match
+      output = db_binary (stack[i-2], stack[i-1], stack[i].name, fields, Nfields); 
+
+      // free temporary stack items, drop external items
+      clear_stack (stack[i-2]);
+      clear_stack (stack[i-1]);
+
+      stack[i-2] = output;
+      for (j = i + 1; j < Nstack; j++) {
+	stack[j-2] = stack[j];
+      }
+
+      Nstack -= 2;
+      i -= 2;
+      continue;
+    }
+
+    /***** unary operators **/
+    if (stack[i].type == 9) {
+
+      // pre-test that op and entries match
+      output = db_unary (&stack[i-1], stack[i].name, fields, Nfields); 
+
+      // free temporary stack items, drop external items
+      clear_stack (stack[i-2]);
+      clear_stack (stack[i-1]);
+
+      for (j = i + 1; j < Nstack; j++) {
+	stack[j-1] = stack[j];
+      }
+
+      Nstack -= 1;
+      i -= 1;
+      continue;
+    } 
+  }
+
+  // pre-test that op and entries match
+  return (TRUE);
+}
+
+/* delete name and data */
+void clear_stack (dvoStack *stack) {
+
+  if (stack->type != 'T') return;
+  free (stack);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvodb.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvodb.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvodb.c	(revision 22322)
@@ -0,0 +1,77 @@
+
+/* dvo fields and boolean expressions
+   - evaluate the command line:
+     * avextract ra,dec,g,r,i where (uRA < 0.1)
+     1) identify all fields in extraction list
+     2) parse boolean expression
+     3) identify additional fields from expression
+*/
+
+enum {DVO_TABLE_AVERAGE, DVO_TABLE_MEASURE};
+
+// a single db field 
+typedef struct {
+  char *name;
+  int extract;
+  int table;
+  int ID;
+  int magMode;
+  PhotCode *photcode;
+} dvoField;
+
+// db boolean operations
+typedef struct {
+  char   *name;
+  char    type;
+  int     field;
+  float   Float;
+} dvoStack;
+
+dvoField *ParseCmdlineFields (int argc, char **argv, int table, int *last, int *nfields) {
+
+  int i, Nfields, NFIELDS, 
+
+  *nfields = 0;
+  Nfields = 0;
+  NFIELDS = 10;
+  ALLOCATE (fields, dvoField, NFIELDS);
+
+  // examine each argv[i] entry until we reach a where 
+  for (i = 1; (i < argc) && strcasecmp (argv[i], "where"); i++) {
+    // split the word by ","
+    p = argv[i];
+    while (*p) {
+      q = strchr (p, ',');
+      if (q == NULL) {
+	field = strcreate (p);
+	p = p + strlen(p);
+      } else {
+	field = strncreate (p, q-p);
+	p = q + 1;
+      }
+      // identify field for word
+      // need to know which type of fields to look for...
+      // xxx extend this more generally later
+      if (table == DVO_TABLE_MEASURE) {
+	status = ParseMeasureField (&fields[Nfields], field);
+      } 
+      if (table == DVO_TABLE_AVERAGE) {
+	status = ParseAverageField (&fields[Nfields], field);
+      } 
+      if (!status) {
+	gprint (GP_ERR, "unknown database field %s\n", field);
+	free (field);
+	free (fields);
+	return (NULL);
+      }
+      free (field);
+
+      Nfields ++;
+      CHECK_REALLOCATE (fields, dvoField, NFIELDS, Nfields, 10);
+    }
+  }
+
+  *last = i;
+  *nfields = Nfields;
+  return (fields);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvofields.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvofields.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvofields.c	(revision 22322)
@@ -0,0 +1,129 @@
+
+int GetMagMode (char *string) {
+
+  if (!strcasecmp (string, "inst"))  return (MAG_INST);
+  if (!strcasecmp (string, "cat"))   return (MAG_CAT);
+  if (!strcasecmp (string, "sys"))   return (MAG_SYS);
+  if (!strcasecmp (string, "rel"))   return (MAG_REL);
+  if (!strcasecmp (string, "cal"))   return (MAG_CAL);
+  if (!strcasecmp (string, "ave"))   return (MAG_AVE);
+  if (!strcasecmp (string, "ref"))   return (MAG_REF);
+  if (!strcasecmp (string, "err"))   return (MAG_ERR);
+  if (!strcasecmp (string, "chisq")) return (MAG_CHISQ);
+  return (MAG_NONE);
+}
+
+PhotCode *ParsePhotcodeField (char *field, int *mode, int default) {
+
+  char *tmpstring, *p;
+  PhotCode *code;
+
+  *mode = default;
+
+  p = strchr (field, ':');
+  if (p != NULL) {
+    *mode = GetMagMode (p + 1);
+    if (*mode == MAG_NONE) return (NULL);
+    tmpstring = strncreate (field, p - field);
+  } else {
+    tmpstring = strcreate (field);
+  }
+  code = GetPhotcodebyName (tmpstring);
+  free (tmpstring);
+
+  return (code);
+}
+
+# define ESCAPE(F,M) { \
+  field->ID = (F); \
+  field->magMode = (M); \
+  field->photcode = NULL; \
+  return (TRUE);
+
+int ParseMeasureField (dvoField *field, char *fieldName) {
+
+  PhotCode *code;
+
+  field->table = DVO_TABLE_MEASURE;
+  field->name  = strcreate (fieldName);
+
+  if (!strcasecmp (fieldName, "RA"))       ESCAPE (MEAS_RA,   	  MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC"))      ESCAPE (MEAS_DEC,  	  MAG_NONE);
+  if (!strcasecmp (fieldName, "MAG"))      ESCAPE (MEAS_MAG,  	  MAG_REL);
+  if (!strcasecmp (fieldName, "dMAG"))     ESCAPE (MEAS_dMAG, 	  MAG_ERR);
+  if (!strcasecmp (fieldName, "AIRMASS"))  ESCAPE (MEAS_AIRMASS,  MAG_NONE);
+  if (!strcasecmp (fieldName, "EXPTIME"))  ESCAPE (MEAS_EXPTIME,  MAG_NONE);
+  if (!strcasecmp (fieldName, "PHOTCODE")) ESCAPE (MEAS_PHOTCODE, MAG_NONE);
+  if (!strcasecmp (fieldName, "TIME"))     ESCAPE (MEAS_TIME,     MAG_NONE);
+  if (!strcasecmp (fieldName, "dR"))       ESCAPE (MEAS_dR, 	  MAG_NONE);
+  if (!strcasecmp (fieldName, "dD"))       ESCAPE (MEAS_dD, 	  MAG_NONE);
+  if (!strcasecmp (fieldName, "FWHM"))     ESCAPE (MEAS_FWHM,     MAG_NONE);
+  if (!strcasecmp (fieldName, "DOPHOT"))   ESCAPE (MEAS_DOPHOT,   MAG_NONE);
+  if (!strcasecmp (fieldName, "FLAGS"))    ESCAPE (MEAS_FLAGS,    MAG_NONE);
+  if (!strcasecmp (fieldName, "XCCD"))     ESCAPE (MEAS_XCCD, 	  MAG_NONE);
+  if (!strcasecmp (fieldName, "YCCD"))     ESCAPE (MEAS_YCCD, 	  MAG_NONE);
+  if (!strcasecmp (fieldName, "XMOSAIC"))  ESCAPE (MEAS_XMOSAIC,  MAG_NONE);
+  if (!strcasecmp (fieldName, "YMOSAIC"))  ESCAPE (MEAS_YMOSAIC,  MAG_NONE);
+
+  // for words that don't parse, try a photcode
+
+  // check for code:mode in photcode name 
+  code = ParsePhotcodeField (&mode, MAG_REL);
+  if (code == NULL) return (FALSE);
+
+  if (mode == MAG_ERR) {
+    field->ID = MEAS_dMAG;
+  } else {
+    field->ID = MEAS_MAG;
+  }    
+  
+  field->magMode = mode;
+  field->photcode = code;
+  return (TRUE);
+}
+  
+int ParseAverageField (char *fieldName) {
+
+  PhotCode *code;
+
+  field->table = DVO_TABLE_AVERAGE;
+
+  if (!strcasecmp (fieldName, "RA"))    ESCAPE (AVE_RA,        MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC"))   ESCAPE (AVE_DEC,       MAG_NONE);
+  if (!strcasecmp (fieldName, "dRA"))   ESCAPE (AVE_RA_ERR,    MAG_NONE);
+  if (!strcasecmp (fieldName, "dDEC"))  ESCAPE (AVE_DEC_ERR,   MAG_NONE);
+  if (!strcasecmp (fieldName, "uRA"))   ESCAPE (AVE_U_RA,      MAG_NONE);
+  if (!strcasecmp (fieldName, "uDEC"))  ESCAPE (AVE_U_DEC,     MAG_NONE);
+  if (!strcasecmp (fieldName, "duRA"))  ESCAPE (AVE_U_RA_ERR,  MAG_NONE);
+  if (!strcasecmp (fieldName, "duDEC")) ESCAPE (AVE_U_DEC_ERR, MAG_NONE);
+  if (!strcasecmp (fieldName, "PAR"))   ESCAPE (AVE_PAR,       MAG_NONE);
+  if (!strcasecmp (fieldName, "dPAR"))  ESCAPE (AVE_PAR_ERR,   MAG_NONE);
+  if (!strcasecmp (fieldName, "MAG"))   ESCAPE (AVE_MAG,       MAG_AVE);
+  if (!strcasecmp (fieldName, "dMAG"))  ESCAPE (AVE_dMAG,      MAG_AVE_ERR);
+  if (!strcasecmp (fieldName, "NMEAS")) ESCAPE (AVE_NMEAS,     MAG_NONE);
+  if (!strcasecmp (fieldName, "NMISS")) ESCAPE (AVE_NMISS,     MAG_NONE);
+  if (!strcasecmp (fieldName, "Xp"))    ESCAPE (AVE_Xp,        MAG_NONE);
+  if (!strcasecmp (fieldName, "Xm"))    ESCAPE (AVE_Xm,        MAG_CHISQ);
+  if (!strcasecmp (fieldName, "FLAG"))  ESCAPE (AVE_FLAG,      MAG_NONE);
+  if (!strcasecmp (fieldName, "TYPE"))  ESCAPE (AVE_TYPE,      MAG_NONE);
+  if (!strcasecmp (fieldName, "NPHOT")) ESCAPE (AVE_NPHOT,     MAG_NONE);
+  if (!strcasecmp (fieldName, "NCODE")) ESCAPE (AVE_NCODE,     MAG_NONE);
+  if (!strcasecmp (fieldName, "NCRIT")) ESCAPE (AVE_NCRIT,     MAG_NONE);
+
+  // for words that don't parse, try a photcode
+
+  // check for code:mode in photcode name 
+  code = ParsePhotcodeField (&mode, MAG_AVE);
+  if (code == NULL) return (FALSE);
+
+  // need to distinguish phot, sys errors and scatter
+  if (mode == MAG_ERR) {
+    field->ID = MEAS_dMAG;
+  } else {
+    field->ID = MEAS_MAG;
+  }    
+  
+  field->magMode = mode;
+  field->photcode = code;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvomisc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvomisc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/dvomisc.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "dvoshell.h"
+
+void cprecess (Average *average, int Naverage, double in_epoch, double out_epoch) {
+
+  int i;
+  double T;
+  double A, D, RA, DEC, zeta, z, theta;
+  double SA, CA, SD, CD;
+  
+  T = (out_epoch - in_epoch) / 100.0;
+  
+  zeta  = RAD_DEG*(0.6406161*T + 0.0000839*T*T + 0.0000050*T*T*T);
+  theta = RAD_DEG*(0.5567530*T - 0.0001185*T*T - 0.0000116*T*T*T);
+  z     =          0.6406161*T + 0.0003041*T*T + 0.0000051*T*T*T;
+  
+  for (i = 0; i < Naverage; i++) {
+    A = average[i].R;
+    D = average[i].D;
+    SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+    CD = sqrt (1 - SD*SD);
+    SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+    CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+    
+    DEC = DEG_RAD*asin(SD);
+    RA  = DEG_RAD*atan2(SA, CA) + z;
+    
+    if (RA < 0)
+      RA += 360;
+    
+    average[i].R = RA;
+    average[i].D = DEC; 
+  }
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/elixir.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/elixir.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/elixir.c	(revision 22322)
@@ -0,0 +1,145 @@
+# include "dvoshell.h"
+
+int WriteMsg (char *fifo, char *message);
+int ReadMsg (char *fifo, char **message);
+
+int elixir (int argc, char **argv) {
+  
+  char message[256], cmd[256], ElixirBase[256];
+  char fifo[256], fifodir[256], msgfile[256];
+  char *answer;
+  int N;
+
+  sprintf (cmd, "STATUS");
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "TIMES");
+  }
+  if ((N = get_argument (argc, argv, "-live"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "ALIVE");
+  }
+  if ((N = get_argument (argc, argv, "-stop"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "STOP");
+  }
+  if ((N = get_argument (argc, argv, "-kill"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "ABORT");
+  }
+ 
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: elixir (elixir) [-time] [-live]\n");
+    return (FALSE);
+  }
+
+  if (!VarConfig (argv[1], "%s", ElixirBase)) {
+    gprint (GP_ERR, "elixir %s not in config file\n", argv[1]);
+    return (FALSE);
+  }
+  sprintf (fifo, "%s.msg", ElixirBase);
+  if (!VarConfig ("FIFOS", "%s", fifodir)) {
+    gprint (GP_ERR, "FIFOS not in config, using local /tmp\n");
+    strcpy (fifodir, "/tmp");
+  }
+  sprintf (fifo, "%s.msg", ElixirBase);
+
+  sprintf (msgfile, "%s/EMsg.XXXXXX", fifodir);
+  mkstemp (msgfile);
+  sprintf (message, "%s %s", cmd, msgfile);
+  unlink (msgfile);
+
+  if (!WriteMsg (fifo, message)) {
+    gprint (GP_ERR, "can't access fifo %s\n", fifo);
+    return (FALSE);
+  }
+
+  if (ReadMsg (msgfile, &answer)) {
+    gprint (GP_ERR, "%s\n", answer);
+  } 
+  unlink (msgfile);
+  return (TRUE);
+  
+}
+
+int WriteMsg (char *fifo, char *message) {
+
+  int state, mode;
+  FILE *f;
+
+  /* check lockfile */
+  f = fsetlockfile (fifo, 2.0, LCK_XCLD, &state);
+  if (f == NULL) return (0);
+
+  /* write message to end of file */
+  fseek (f, 0, SEEK_END);
+  fprintf (f, "%s\n", message);
+
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (fifo, mode);
+
+  fclearlockfile (fifo, f, LCK_XCLD, &state);
+  return (1);
+}
+
+int ReadMsg (char *fifo, char **message) {
+
+  int i, nbytes, Nbytes, NBYTES;
+  char *buffer;
+  int state, mode;
+  FILE *f;
+  struct stat filestat;
+
+  /* wait (2 sec) for file to exist, then try to read it */
+  for (i = 0; (stat (fifo, &filestat) == -1) && (i < 20); i++) {
+    usleep (100000);
+  }
+  if (i >= 20) {
+    gprint (GP_ERR, "no response\n");
+    return (0);
+  }
+
+  /* check lockfile */
+  f = fsetlockfile (fifo, 2.0, LCK_XCLD, &state);
+  if (f == NULL) {
+    gprint (GP_ERR, "message locked (%d)\n", state);
+    return (0);
+  }
+
+  /* if file is empty, return 0 */
+  if (state == LCK_EMPTY) {
+    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+    chmod (fifo, mode);
+    fclearlockfile (fifo, f, LCK_XCLD, &state);
+    return (0);
+  }  
+
+  Nbytes = 0;
+  NBYTES = 0x1000;
+  ALLOCATE (buffer, char, NBYTES);
+  while (TRUE) {
+    nbytes = fread (&buffer[Nbytes], 1, 0x1000, f);
+    if (nbytes < 0) { 
+      gprint (GP_ERR, "error in ReadMsg -- got -1 bytes\n");
+      return (0);
+    }
+    if (nbytes == 0) break;
+    Nbytes += nbytes;
+    NBYTES += 0x1000;
+    REALLOCATE (buffer, char, NBYTES);
+  }
+  buffer[Nbytes] = 0;
+
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (fifo, mode);
+  fclearlockfile (fifo, f, LCK_XCLD, &state);
+
+  if (Nbytes == 0) {
+    free (buffer);
+    return (0);
+  }
+
+  *message = buffer;
+  return (1);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/extract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/extract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/extract.c	(revision 22322)
@@ -0,0 +1,450 @@
+# include "dvoshell.h"
+// XXX EAM : this function is deprecated
+
+enum {ZERO, RA, DEC, MAG, dMAG, Xm, Xp, NMEAS, NMISS, REF, TYPE, NPHOT, NCODE, FLAG};
+
+# define NBYTES 160000
+# define BYTES_STAR 23
+# define BLOCK 1000
+# define DNSTARS 1000
+
+int extract (int argc, char **argv) {
+  
+  FILE *f;
+  int i, Col, N, Nbytes, nbytes, NPTS;
+  int InRegion, GSC, ASCII, LONEOS, mode, loadmode;
+  int j, k, m, Nregions;
+  float M0, m0;
+  char filename[128];
+  float Radius;
+  char catdir[256], gscdir[256];
+  int PhotcodeSelect;
+  char PhotCodeFile[256], code[64];
+  double ZERO_POINT;
+  int Ns, N1, n1, Nsec;
+  int Ngraph;
+  int value;
+  Vector *vec;
+  PhotCodeData photcodes;
+  Graphdata graphmode;
+  Catalog catalog;
+  RegionFile *regions;
+
+  if (!GetGraphData (&graphmode, NULL, NULL)) return (FALSE);
+  if (!InitPhotcodes ()) return (FALSE);
+
+  VarConfig ("GSCDIR", "%s", gscdir);
+  VarConfig ("CATDIR", "%s", catdir);
+
+  regions = (RegionFile *) NULL;
+  ASCII = FALSE;
+  LONEOS = TRUE;
+  GSC = FALSE;
+  if (N = get_argument (argc, argv, "-g")) {
+    remove_argument (N, &argc, argv);
+    GSC = TRUE;
+    ASCII = FALSE;
+    LONEOS = FALSE;
+  }
+
+  Col = 1;
+  if (N = get_argument (argc, argv, "-a")) {
+    remove_argument (N, &argc, argv);
+    ASCII = TRUE;
+    GSC = FALSE;
+    LONEOS = FALSE;
+    Col = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* check for region-based selection */
+  code = NULL;
+  PhotcodeSelect = FALSE;
+  if (N = get_argument (argc, argv, "-photcode")) {
+    PhotcodeSelect = True;
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode %s not found in photcode table\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: extract (filename) (value) [-g / -a Ncol] \n");
+    return (FALSE);
+  }
+  
+  InRegion = FALSE;
+  if (!strcmp (argv[1], "-all")) {
+    InRegion = TRUE;
+  }
+
+  /* identify selection */
+  if (LONEOS) {
+    mode = ZERO;
+    if (!strcasecmp (argv[2], "ra")) mode = RA;
+    if (!strcasecmp (argv[2], "dec")) mode = DEC;
+    if (!strcasecmp (argv[2], "mag")) mode = MAG;
+    if (!strcasecmp (argv[2], "Nmeas")) mode = NMEAS;
+    if (!strcasecmp (argv[2], "Nmiss")) mode = NMISS;
+    if (!strcasecmp (argv[2], "Xp")) mode = Xp;
+    if (!strcasecmp (argv[2], "Xm")) mode = Xm;
+    if (!strcasecmp (argv[2], "dM")) mode = dMAG;
+    if (!strcasecmp (argv[2], "flag")) mode = FLAG;
+    if (!strcasecmp (argv[2], "ref")) mode = REF;
+    if (!strcasecmp (argv[2], "type")) mode = TYPE;
+    if (!strcasecmp (argv[2], "Nphot")) mode = NPHOT;
+    if (!strcasecmp (argv[2], "Ncode")) mode = NCODE;
+    if (mode == ZERO) {
+      gprint (GP_ERR, "value may be one of the following:\n");
+      gprint (GP_ERR, " ra dec mag Nmeas Nmiss Xp Xm ID\n");
+      return (FALSE);
+    }
+  }
+
+  if ((mode == REF) && !PhotcodeSelect) {
+    gprint (GP_ERR, "must specify photcode for Reference\n");
+    return (FALSE);
+  }
+  if ((mode == TYPE) && !PhotcodeSelect) {
+    gprint (GP_ERR, "must specify photcode for Type\n");
+    return (FALSE);
+  }
+
+  /* check photcode data / selection validity */
+  Ns = -1;
+  Nsec = GetPhotcodeNsecfilt ();
+  if (PhotcodeSelect) {
+    Ns = GetPhotcodeNsec (code[0].code);
+    if ((mode != REF) && (code[0].type != PHOT_SEC)) {
+      gprint (GP_ERR, "filter must be a average photometry type\n");
+      return (FALSE);
+    }
+    if ((mode == REF) && (code[0].type != PHOT_REF)) {
+      gprint (GP_ERR, "filter must be a REFERENCE photometry type\n");
+      return (FALSE);
+    }
+  }
+
+  if (GSC) {
+    mode = ZERO;
+    if (!strcasecmp (argv[2], "ra")) 
+      mode = RA;
+    if (!strcasecmp (argv[2], "dec")) 
+      mode = DEC;
+    if (!strcasecmp (argv[2], "mag")) 
+      mode = MAG;
+    if (mode == ZERO) {
+      gprint (GP_ERR, "for GSC, value may be one of the following:\n");
+      gprint (GP_ERR, " ra dec mag\n");
+      return (FALSE);
+    }
+  }
+
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (InRegion) {
+    Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+    regions = find_regions (graphmode.coords.crval1, graphmode.coords.crval2, Radius, &Nregions);
+  } else {
+    Nregions = 1;
+  }
+  
+  /* create storage vector */
+  NPTS = 1000;
+  REALLOCATE (vec[0].elements, float, NPTS);
+  vec[0].Nelements = N = 0;
+
+  /* we loop over Nregions, but for ASCII Nregions = 1 */
+  for (j = 0; j < Nregions; j++) {
+    
+    /* Load in data from an ASCII file list of ra, dec, mag */
+    if (ASCII) {
+      char *tbuffer;
+      double tmp;
+      float *V;
+      
+      f = fopen (argv[1], "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "ERROR: can't open data file: %s\n", argv[1]);
+	/* delete new vector! */
+	return (FALSE);
+      }
+      ALLOCATE (tbuffer, char, 1024);
+      
+      V = vec[0].elements;
+      while (scan_line (f, tbuffer) != EOF) {
+	dparse (&tmp, Col, tbuffer);
+	*V = tmp;
+	V++;
+	N++;
+	if (N == NPTS - 1) {
+	  NPTS += 1000;
+	  REALLOCATE (vec[0].elements, float, NPTS);
+	  V = &vec[0].elements[N];
+	}
+      }
+      free (tbuffer);
+      vec[0].Nelements = N;
+      REALLOCATE (vec[0].elements, float, MAX(1,N));
+      fclose (f);
+      return (TRUE);
+    }
+    
+    /* load data from the GSC files */
+    if (GSC) {
+      char *tbuffer;
+      double tmp;
+      float *V;
+      
+      if (InRegion) {
+	sprintf (filename, "%s/%s\0", gscdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s\0", gscdir, argv[1]);
+      }
+      
+      f = fopen (filename, "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "no stars in %s, skipping\n", filename);
+	continue;
+      }
+      ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+      
+      V = &vec[0].elements[N];
+      Nbytes = BLOCK*BYTES_STAR;
+      while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+	for (i = 0; i < nbytes / BYTES_STAR; i++) {
+	  if (mode == RA) {
+	    dparse (&tmp, 1, &tbuffer[i*BYTES_STAR]);
+	    *V = tmp;
+	  }
+	  if (mode == DEC) {
+	    dparse (&tmp, 2, &tbuffer[i*BYTES_STAR]);
+	    *V = tmp;
+	  }
+	  if (mode == MAG) {
+	    dparse (&tmp, 3, &tbuffer[i*BYTES_STAR]);
+	    *V = tmp;
+	  }
+	  V++;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	    V = &vec[0].elements[N];
+	  }
+	}
+      }
+      free (tbuffer);
+      fclose (f);
+    }
+  
+    /* load data from the photometry database files */
+    if (LONEOS) {
+      /* find and open correct file */
+      if (InRegion) {
+	sprintf (filename, "%s/%s\0", catdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s\0", catdir, argv[1]);
+      }
+      catalog.average = (Average *) NULL;
+      catalog.measure = (Measure *) NULL;
+      catalog.secfilt = (SecFilt *) NULL;
+      loadmode = LOAD_AVES | LOAD_SECF;
+      if ((mode == REF) || (mode == TYPE) || (mode == NPHOT) || (mode == NCODE)) 
+	loadmode = loadmode | LOAD_MEAS;
+
+      /* lock, load, unlock catalog */
+      catalog.filename = filename;
+      switch (lock_catalog (&catalog, LCK_SOFT)) {
+      case 2:
+	unlock_catalog (&catalog);
+      case 0:
+	continue;
+      }
+      catalog.catflags = loadmode;
+      if (!load_catalog (&catalog, TRUE)) {
+	unlock_catalog (&catalog);
+	continue;
+      }
+      unlock_catalog (&catalog);
+
+      /* assign vector values */
+      switch (mode) {
+      case (RA):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].R;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (DEC):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].D;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (MAG):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  M0 = (Ns == -1) ? catalog.average[i].M : catalog.secfilt[i*Nsec+Ns].M;
+	  vec[0].elements[N] = M0 / 1000.0;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NMEAS):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].Nm;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NMISS):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].Nn;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (Xp):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  /* Xp is scatter in 1/100 arcsec */
+	  vec[0].elements[N] = 0.01*catalog.average[i].Xp;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NCODE):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  Ncode = 0;
+	  for (k = 0; k < catalog.average[i].Nm; k++, m++) {
+	    if (code[0].code != GetPhotcodeEquivCodebyCode (catalog.measure[m].photcode)) continue;
+	    Ncode ++;
+	  }
+	  vec[0].elements[N] = Ncode;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NPHOT):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  Ncode = 0;
+	  for (k = 0; k < catalog.average[i].Nm; k++, m++) {
+	    if (code[0].code != GetPhotcodeEquivCodebyCode (catalog.measure[m].photcode)) continue;
+	    if (catalog.measure[m].photcode & (ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM)) continue;
+	    Ncode ++;
+	  }
+	  vec[0].elements[N] = Ncode;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (Xm):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  M0 = (Ns == -1) ? catalog.average[i].Xm : catalog.secfilt[i*Nsec+Ns].Xm;
+	  vec[0].elements[N] = (M0 == NO_MAG) ? -1.0 : pow (10.0, 0.01*M0);
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (dMAG):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  /* dM is 1000.0 * error */
+	  M0 = (Ns == -1) ? catalog.average[i].dM : catalog.secfilt[i*Nsec+Ns].dM;
+	  vec[0].elements[N] = (M0 == NO_MAG) ? -1.0 : 0.001*M0;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (FLAG):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].code;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (REF):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  vec[0].elements[N] = -32;
+	  for (k = 0; k < catalog.average[i].Nm; k++) {
+	    if (catalog.measure[m+k].photcode == N1) {
+	      vec[0].elements[N] = PhotCat (&catalog.measure[m+k]);
+	      k = catalog.average[i].Nm;
+	    }
+	  }
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (TYPE):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  vec[0].elements[N] = DetermineTypeCode (&catalog.average[i], &catalog.measure[m], &photcodes, N1);
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      }
+      if (catalog.average != 0) free (catalog.average);
+      if (catalog.measure != 0) free (catalog.measure);
+      if (catalog.secfilt != 0) free (catalog.secfilt);
+    }
+  }
+
+  vec[0].Nelements = N;
+  REALLOCATE (vec[0].elements, float, MAX(1,N));
+  return (TRUE);
+
+}
+  
+  /* USAGE: extract (what) (where) (from) [option] */
+  /* examples:
+     extract n0000/n0001 mag m 
+     extract -g n0000/n0020 ra r 
+     extract -all Xm xm
+     extract fred dec d -a 2 */
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/find_regions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/find_regions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/find_regions.c	(revision 22322)
@@ -0,0 +1,156 @@
+# include "dvoshell.h"
+# include "hstgsc.h"
+
+/* returns a list of region files within the desired RA, DEC region */
+RegionFile *find_regions (double Ra, double Dec, double radius, int *Nregions) {
+  
+  char filename[256];
+  char buffer[28800], temp[50];
+  RegionFile *regions;
+  FILE *f;
+  double minRa, maxRa, minDec, maxDec, rad;
+
+  double RA0, RA1, DEC0, DEC1;
+  int i, j, NBigDec;
+  int NLINES, done, NREGIONS, nregion;
+  
+  VarConfig ("GSCFILE", "%s", filename);
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't find regions file %s\n", filename);
+    *Nregions = 0;
+    return ((RegionFile *) NULL);
+  }
+  
+  NREGIONS = 50;
+  ALLOCATE (regions, RegionFile, NREGIONS);
+  nregion = 0;
+
+  while (Ra < 0) Ra += 360.0;
+  while (Ra >= 360) Ra -= 360.0;
+
+  minDec = Dec - radius;
+  maxDec = Dec + radius;
+
+  if ((minDec <= -90) || (maxDec >= 90)) {
+    minRa = 0;
+    maxRa = 360;
+  } else {
+    rad = MAX (radius / (cos(minDec*RAD_DEG)), radius / (cos(maxDec*RAD_DEG)));
+    minRa = Ra - rad;
+    maxRa = Ra + rad;
+  }
+  
+  /* use the pole regions, if near pole */
+  if (maxDec > 86.25) {
+    sprintf (regions[nregion].name, "n8230/pole.cpt");
+    regions[nregion].RA0 = 0;
+    regions[nregion].RA1 = 360;
+    regions[nregion].DEC0 = 86.25;
+    regions[nregion].DEC1 = 90.0;
+    nregion ++;
+    if (nregion == NREGIONS) {
+      NREGIONS += 50;
+      REALLOCATE (regions, RegionFile, NREGIONS);
+    }
+  }
+
+  if (minDec > 86.25) {
+    return (regions);
+  }
+    
+  if ((minDec < 0) && (maxDec > 0)) {
+    /* Search Both Sides */
+    NBigDec = 0;
+  } else {
+    /* find large DEC region (directory) */
+    NBigDec = -1;
+    for (i = 0; i < 12; i++) {
+      if ((minDec >= BigDecBounds[i]) && (minDec < BigDecBounds[i+1])) {
+	NBigDec = i;
+	break;
+      }
+    }
+    if (NBigDec < 0) {
+      for (i = 13; i < 24; i++) {
+	if ((maxDec < BigDecBounds[i]) && (maxDec >= BigDecBounds[i+1])) {
+	  NBigDec = i;
+	  break;
+	}
+      }
+    }
+  }
+  if (NBigDec < 0) {
+    gprint (GP_ERR, "ERROR: Dec out of range: %f\n", minDec);
+    *Nregions = 0;
+    return ((RegionFile *) NULL);
+  }
+  
+  /* count lines before section */
+  NLINES = 0;
+  for (i = 0; i < NBigDec; i++) {
+    NLINES += NDecLines[i];
+  }
+  fseek (f, 5*2880 + 48*NLINES, SEEK_SET);
+  
+  /* should be in this section.  if not, there is a problem counting... */
+  /* careful with the 0,360.0 boundary **/
+  done = FALSE;
+  for (j = 0; !done && (NBigDec + j < 25); j++) {
+    fread (buffer, 48*NDecLines[NBigDec + j], 1, f);
+    for (i = 0; (i < NDecLines[NBigDec + j]); i++) {
+      strncpy (temp, &buffer[i*48], 48);
+      temp[49] = 0;
+      hstgsc_hms_to_deg (&RA0, &RA1, &DEC0, &DEC1, &temp[7]);
+      if (RA1 < RA0) RA1 += 360.0;
+      if ((DEC1 > 0) && (minDec < DEC1) && (maxDec > DEC0) && (minRa < RA1) && (maxRa > RA0)) {
+	temp[5] = 0;
+	sprintf (regions[nregion].name, "%s/%s.cpt", Dec2Sections[NBigDec + j], &temp[1]);
+	regions[nregion].RA0 = RA0;
+	regions[nregion].RA1 = RA1;
+	regions[nregion].DEC0 = DEC0;
+	regions[nregion].DEC1 = DEC1;
+	nregion ++;
+	if (nregion == NREGIONS) {
+	  NREGIONS += 50;
+	  REALLOCATE (regions, RegionFile, NREGIONS);
+	}
+      }
+      if ((DEC1 < 0) && (minDec < DEC0) && (maxDec > DEC1) && (minRa < RA1) && (maxRa > RA0)) {
+	temp[5] = 0;
+	sprintf (regions[nregion].name, "%s/%s.cpt", Dec2Sections[NBigDec + j], &temp[1]);
+	regions[nregion].RA0 = RA0;
+	regions[nregion].RA1 = RA1;
+	regions[nregion].DEC0 = DEC0;
+	regions[nregion].DEC1 = DEC1;
+	nregion ++;
+	if (nregion == NREGIONS) {
+	  NREGIONS += 50;
+	  REALLOCATE (regions, RegionFile, NREGIONS);
+	}
+      }
+      if (((DEC1 > 0) && (maxDec <= DEC1)) || ((DEC1 < 0) && (minDec >= DEC1))) {
+	done = TRUE;
+      }
+    }
+    if (done && (minDec < 0) && (maxDec > 0) && (BigDecBounds[NBigDec + j + 1] > 0)) {
+      /* skip remaining north sections, try south sections */
+      /* count lines before section */
+      NLINES = 0;
+      for (i = 0; i < 13; i++) { 
+	NLINES += NDecLines[i];
+      }
+      fseek (f, 5*2880 + 48*NLINES, SEEK_SET);
+      done = FALSE;
+      j = 12;
+    }
+  }
+
+  REALLOCATE (regions, RegionFile, MAX(1,nregion));
+  *Nregions = nregion;
+  
+  fclose (f);
+  return (regions);
+  
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/fitcolors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/fitcolors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/fitcolors.c	(revision 22322)
@@ -0,0 +1,390 @@
+# include "dvoshell.h"
+# define NMIN_PTS 100
+
+static void free_catalog (Catalog *catalog, int Ncatalog);
+
+/* this function takes a photcode (and camera name?) and measures the  *
+ * chip-to-chip slopes for all DEP photcodes equiv to the PRI/SEC code */
+
+int fitcolors (int argc, char **argv) {
+  
+  int *list, Nlist;
+  int i, k, m, N, NP1, NP2, NP, Np, Npts, NPTS;
+  int N1, N2, i1, i2, mode[4];
+  int Nsecfilt, status;
+  void *oldsignal;
+  char *cmd, *outcmd, *camera;
+  char name[64], filename[64], plotname[64], label[64];
+  double *M1, *M2;
+  float *out, *colorFit, *deltaFit, dColor, C0, C1;
+  float minDelta, maxDelta, minColor, maxColor;
+  int kapa, Npx, Npy, NPX, NPY, Nplot, PLOT;
+  Graphdata graphdata;
+  KapaSection section;
+
+  Catalog *catalog;
+  PhotCode **codelist, *tcode, *code[4];
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  /* defaults */
+  catalog  = NULL;
+  skylist  = NULL;
+  selection = NULL;
+  codelist = NULL;
+  xvec = yvec = NULL;
+  colorFit = NULL;
+  deltaFit = NULL;
+
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  // range for valid data points (exclude extreme outliers)
+  minDelta = -0.2;
+  maxDelta = +0.2;
+  minColor = -1.0;
+  maxColor = +3.0;
+  if ((N = get_argument (argc, argv, "-color-range"))) {
+    remove_argument (N, &argc, argv);
+    minColor = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    maxColor = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-delta-range"))) {
+    remove_argument (N, &argc, argv);
+    minDelta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    maxDelta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOT = FALSE;
+  NPX = NPY = 0;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (plotname, argv[N]);
+    remove_argument (N, &argc, argv);
+    NPX = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    NPY = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    PLOT = TRUE;
+  }
+
+  /* interpret command-line options */
+  if (argc != 4) goto usage;
+
+  if (PLOT) {
+    if (!GetGraph (&graphdata, &kapa, NULL)) return (FALSE);
+    Nplot = 0;
+    Npx = Npy = 0;
+    graphdata.xmin = minColor;
+    graphdata.xmax = maxColor;
+    graphdata.ymin = minDelta;
+    graphdata.ymax = maxDelta;
+    graphdata.style = 2;
+    graphdata.ptype = 2;
+    KapaClearSections (kapa);
+    KapaSetFont (kapa, "helvetica", 14);
+
+    ALLOCATE (colorFit, float, 11);
+    ALLOCATE (deltaFit, float, 11);
+    dColor = (maxColor - minColor) / 10.0;
+    for (i = 0; i < 11; i++) {
+      colorFit[i] = minColor + i*dColor;
+    }
+  }
+
+  /* determine relevant photcodes, colors */
+  if (!(Np = GetPhotcodeCodebyName (argv[2]))) {
+    gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+    goto usage;
+  }
+  camera = argv[3];
+
+  /* reduce the list of codes */
+  list = GetPhotcodeEquivList (Np, &Nlist);
+  ALLOCATE (codelist, PhotCode *, Nlist);
+  for (i = NP = 0; i < Nlist; i++) {
+    tcode = GetPhotcodebyCode (list[i]);
+    if (strncmp (tcode[0].name, camera, strlen(camera))) continue;
+    codelist[NP] = tcode;
+    NP++;
+  }
+  mode[0] = mode[1] = MAG_REL;  /* we should be applying any relative photometry corrections here */
+  mode[2] = mode[3] = MAG_AVE;
+
+  /* set the reference colors */
+  code[2] = GetPhotcodebyCode (codelist[0][0].c1);
+  code[3] = GetPhotcodebyCode (codelist[0][0].c2);
+  if ((code[2] == NULL) || (code[3] == NULL)) goto color_missing;
+
+  /* all codes must have the same colors (validate) */
+  for (i = 0; i < NP; i++) {
+    if (codelist[i][0].c1 != codelist[0][0].c1) goto color_mismatch;
+    if (codelist[i][0].c2 != codelist[0][0].c2) goto color_mismatch;
+  }
+  gprint (GP_ERR, "using %d photcodes\n", NP);
+
+  /* output is a named buffer */
+  if ((buf = SelectBuffer (argv[1], ANYVECTOR, TRUE)) == NULL) goto usage;
+
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, NP, NP, -32, 0.0, 1.0);
+  strcpy (buf[0].file, "(empty)");
+
+  out = (float *) buf[0].matrix.buffer;
+  /* we set a default flag value of -1 */
+  for (i = 0; i < NP*NP; i++) {
+    out[i] = -1;
+  }
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  ALLOCATE (catalog, Catalog, skylist[0].Nregions);
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog[k].filename = skylist[0].filename[k];
+    catalog[k].catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog[k].Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog[k], NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog[k].filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog[k]);
+    // XXX make a subset catalog consisting of only Average and Measure values which meet
+    // the selection criteria
+  }
+  gprint (GP_ERR, "using %d possible regions\n", skylist[0].Nregions);
+
+  /* vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("tmp_x", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("tmp_y", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  // set up basic windows
+  if (PLOT) {
+    Nplot = 0;
+    Npx = Npy = 0;
+    NPX = NPY = 6;
+    KapaInitGraph (&graphdata);
+    graphdata.xmin = minColor;
+    graphdata.xmax = maxColor;
+    graphdata.ymin = minDelta;
+    graphdata.ymax = maxDelta;
+    graphdata.style = 2;
+    graphdata.ptype = 2;
+  }
+
+  /*** generate the color-color vectors for the pairs ***/
+  // XXXX this function also needs to check for interrupts
+  // XXX exclude obvious outliers (eg, fabs(dM) > 0.2)
+  /* loop over chip photcode pairs */
+  for (NP1 = 0; NP1 < NP; NP1++) {
+    for (NP2 = NP1 + 1; NP2 < NP; NP2++) {
+      code[0] = codelist[NP1];
+      code[1] = codelist[NP2];
+      
+      /* extract all magnitude pairs from catalog tables */
+      Npts = 0;
+      for (k = 0; k < skylist[0].Nregions; k++) {
+	if (catalog[k].Naverage == 0) continue;
+
+	// gprint (GP_ERR, "seaching %s with %d stars\n", catalog[k].filename, catalog[k].Naverage);
+	// ListPhotSelections ();
+
+	/* get correct mags, convert to X,Y */
+	for (i = 0; i < catalog[k].Naverage; i++) {
+	  if (interrupt) goto escape;
+
+	  M1 = M2 = NULL;
+	  m = catalog[k].average[i].measureOffset;
+
+	  SetSelectionParam (0);
+	  M1 = ExtractDMag (&code[0], &mode[0], &catalog[k].average[i], &catalog[k].secfilt[i*Nsecfilt], &catalog[k].measure[m], &N1);
+	  if (N1 == 0) goto skip_star;
+
+	  SetSelectionParam (2);
+	  M2 = ExtractDMag (&code[2], &mode[2], &catalog[k].average[i], &catalog[k].secfilt[i*Nsecfilt], &catalog[k].measure[m], &N2);
+	  if (N2 == 0) goto skip_star;
+
+	  for (i1 = 0; i1 < N1; i1++) {
+	    for (i2 = 0; i2 < N2; i2++) {
+	      if (M1[i1] < minDelta) continue;
+	      if (M1[i1] > maxDelta) continue;
+	      if (M2[i2] < minColor) continue;
+	      if (M2[i2] > maxColor) continue;
+	      yvec[0].elements[Npts] = M1[i1];
+	      xvec[0].elements[Npts] = M2[i2];
+	      Npts++;
+	      if (Npts >= NPTS) {
+		NPTS += 2000;
+		REALLOCATE (xvec[0].elements, float, NPTS);
+		REALLOCATE (yvec[0].elements, float, NPTS);
+	      }
+	    }
+	  }
+	skip_star:
+	  if (M1 != NULL) free (M1);
+	  if (M2 != NULL) free (M2);
+	}
+	// gprint (GP_ERR, "selected %d stars\n", Npts);
+      }
+
+      if (Npts < NMIN_PTS) continue;
+      xvec[0].Nelements = Npts;
+      yvec[0].Nelements = Npts;
+
+      /* perform robust fit on dmag vs color */
+      cmd = strcreate ("fit tmp_x tmp_y 1 -clip 3 3 -quiet");
+      status = command (cmd, &outcmd, TRUE);
+      if (outcmd != NULL) free (outcmd);
+      
+      C0 = get_double_variable ("C0", &status);
+      C1 = get_double_variable ("C1", &status);
+      
+      /* do something useful with the results (stored in Cn, C0, C1, etc) */
+      gprint (GP_LOG, "%s - %s : ", code[0][0].name, code[1][0].name);
+      gprint (GP_LOG, "%7.4f %7.4f   %7.4f   ", 
+	       C0, C1, get_double_variable ("dC", &status));
+      gprint (GP_LOG, "%5s of %5d\n", get_variable ("Cnv"), Npts);
+      out[NP1 + NP2*NP] = C1;
+
+      // make an illustrating plot of each chip vs each other chip
+      // each page should have, say, a 6x6 grid of plots. each one should show a single chip pair
+      // as the page fills up, it gets written and a new one created.  
+      if (PLOT) {
+	sprintf (name, "s%02d.%02d", Npx, Npy);
+	section.name = strcreate (name);
+	if (Npx || Npy) {
+	  section.dx = 0.9 / NPX;
+	  section.dy = 0.9 / NPY;
+	  section.x = 0.1 + Npx * section.dx;
+	  section.y = 0.1 + Npy * section.dy;
+	  strcpy (graphdata.labels, "0000");
+	} 
+	if (Npx == 0) {
+	  section.dx = 0.9 / NPX + 0.1;
+	  section.dy = 0.9 / NPY;
+	  section.x = 0.0;
+	  section.y = 0.1 + Npy * section.dy;
+	  strcpy (graphdata.labels, "0100");
+	}
+	if (Npy == 0) {
+	  section.dx = 0.9 / NPX;
+	  section.dy = 0.9 / NPY + 0.1;
+	  section.x = 0.1 + Npx * section.dx;
+	  section.y = 0.0;
+	  strcpy (graphdata.labels, "1000");
+	}
+	if (!Npx && !Npy) {
+	  section.dx = 0.9 / NPX + 0.1;
+	  section.dy = 0.9 / NPY + 0.1;
+	  section.x = 0.0;
+	  section.y = 0.0;
+	  strcpy (graphdata.labels, "1100");
+	}
+	KapaSetSection (kapa, &section);
+	KapaSetLimits (kapa, &graphdata);
+	KapaBox (kapa, &graphdata);
+	KapaPrepPlot (kapa, Npts, &graphdata);
+	KapaPlotVector (kapa, Npts, xvec[0].elements, "x");
+	KapaPlotVector (kapa, Npts, yvec[0].elements, "y");
+
+	for (i = 0; i < 11; i++) {
+	  deltaFit[i] = C0 + C1*colorFit[i];
+	}
+	graphdata.style = 0;
+	graphdata.color = KapaColorByName ("red");
+	KapaPrepPlot (kapa, 11, &graphdata);
+	KapaPlotVector (kapa, 11, colorFit, "x");
+	KapaPlotVector (kapa, 11, deltaFit, "y");
+
+	KapaSetFont (kapa, "helvetica", 8);
+	sprintf (label, "%s", code[0][0].name);
+	KapaSendTextline (kapa, label, 0.2*maxColor + 0.8*minColor, 0.8*maxDelta + 0.2*minDelta, 0.0);
+	sprintf (label, "%s", code[1][0].name);
+	KapaSendTextline (kapa, label, 0.2*maxColor + 0.8*minColor, 0.2*maxDelta + 0.8*minDelta, 0.0);
+	KapaSetFont (kapa, "helvetica", 14);
+
+	graphdata.style = 2;
+	graphdata.color = KapaColorByName ("black");
+
+	free (section.name);
+
+	Npx++;
+	if (Npx == NPX) {
+	  Npx = 0;
+	  Npy ++;
+	  if (Npy == NPY) {
+	    Npy = 0;
+	    sprintf (filename, "%s.%02d.png", plotname, Nplot);
+	    KapaPNG (kapa, filename);
+	    KapaClearSections (kapa);
+	    Nplot++;
+	  }
+	}
+      }	
+    }
+  }
+  if (skylist != NULL) free_catalog (catalog, skylist[0].Nregions);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  if (codelist != NULL) free (codelist);
+  if (colorFit != NULL) free (colorFit);
+  if (deltaFit != NULL) free (deltaFit);
+  signal (SIGINT, oldsignal);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: chipcolors (output) (photcode) (camera)\n");
+  goto escape;
+
+color_missing:
+  gprint (GP_ERR, "error: chips are missing a color reference\n");
+  goto escape;
+
+color_mismatch:
+  gprint (GP_ERR, "error: all chips must have the same colors\n");
+  goto escape;
+
+escape:
+  if (skylist != NULL) free_catalog (catalog, skylist[0].Nregions);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  if (codelist != NULL) free (codelist);
+  if (colorFit != NULL) free (colorFit);
+  if (deltaFit != NULL) free (deltaFit);
+  DeleteVector (xvec);
+  DeleteVector (yvec);
+  signal (SIGINT, oldsignal);
+  return (FALSE);
+}
+
+static void free_catalog (Catalog *catalog, int Ncatalog) {
+
+  int i;
+
+  if (catalog == NULL) return;
+  for (i = 0; i < Ncatalog; i++) {
+    dvo_catalog_free (&catalog[i]);
+  }
+  free (catalog);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/fitsed.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/fitsed.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/fitsed.c	(revision 22322)
@@ -0,0 +1,458 @@
+# include "dvoshell.h"
+
+typedef struct {
+  float *mags;
+  float color;
+  float Temp;
+  float Av;
+} SEDtableRow;
+
+typedef struct {
+  float chisq;
+  float Md;
+  int row;
+} SEDfit;
+
+SEDtableRow **sort_SEDtable (SEDtableRow *raw, int N);
+int SEDcolorBracket (SEDtableRow **table, int Ntable, float color);
+SEDfit SEDchisq (SEDtableRow *ref, SEDtableRow *data, SEDtableRow *error, int Nfilter);
+
+/* this function takes a photcode (and camera name?) and measures the  *
+ * chip-to-chip slopes for all DEP photcodes equiv to the PRI/SEC code */
+
+int fitsed (int argc, char **argv) {
+  
+  int *hashcode;
+  int i, j, k, m, N, done, Nfit;
+  int Nsecfilt, status;
+  void *oldsignal;
+  char name[64], line[1024], key[20];
+  float *fitmags, *fiterrs, *wavecode, *vegaToAB;
+  float color;
+  double X, Y, ZP, RA, DEC;
+  int kapa, PLOT;
+  int Nrow, NROW, idx, Nfilter, start, row;
+  unsigned short colorP, colorM, code, USNOred, USNOblu;
+  int codeP, codeM;
+  FILE *f;
+
+  Graphdata graphdata;
+  KapaSection magSection, resSection;
+
+  Catalog catalog, outcat;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+
+  SEDtableRow *SEDtableRaw, **SEDtable;
+  SEDtableRow sourceValue, sourceError;
+  SEDfit minFit, testFit;
+
+  /* defaults */
+  skylist  = NULL;
+  selection = NULL;
+
+  catalog.average = NULL;
+  catalog.measure = NULL;
+  catalog.secfilt = NULL;
+
+  outcat.average = NULL;
+  outcat.measure = NULL;
+  outcat.secfilt = NULL;
+
+  SEDtable = NULL;
+  SEDtableRaw = NULL;
+  sourceValue.mags = NULL;
+  sourceError.mags = NULL;
+  wavecode = NULL;
+  hashcode = NULL;
+  magSection.name = NULL;
+  resSection.name = NULL;
+
+  Nrow = 0;
+
+  fiterrs = NULL;
+  fitmags = NULL;
+
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  PLOT = FALSE;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    remove_argument (N, &argc, argv);
+    PLOT = TRUE;
+  }
+
+  /* interpret command-line options */
+  if (argc != 6) goto usage;
+
+  Nfit = 0;
+  colorP = GetPhotcodeCodebyName (argv[3]);
+  colorM = GetPhotcodeCodebyName (argv[5]);
+  if (!colorP || !colorM) goto color_undefined;
+
+  // artificially set USNOred and blu errors to 0.3
+  USNOred = GetPhotcodeCodebyName ("USNO_RED");
+  USNOblu = GetPhotcodeCodebyName ("USNO_BLUE");
+
+  // load SED table
+  f = fopen (argv[1], "r");
+  if (f == NULL) goto table_missing;
+
+  // XXX add error checks for header data
+  scan_line (f, line);
+  sscanf (line, "%*s %*s %d", &Nfilter);
+
+  // load SED table photcodes, generate the photcode hashtable
+  ALLOCATE (hashcode, int, 0x10000);
+  ALLOCATE (wavecode, float, Nfilter);
+  ALLOCATE (vegaToAB, float, Nfilter);
+
+  for (i = 0; i < 0x10000; i++) hashcode[i] = -1;
+  for (i = 0; i < Nfilter; i++) {
+    scan_line (f, line);
+    sscanf (line, "%*s %s %f %f", name, &wavecode[i], &vegaToAB[i]);
+    code = GetPhotcodeCodebyName (name);
+    if (code == 0) goto code_missing;
+    hashcode[code] = i;
+  }
+  codeP = hashcode[colorP];
+  codeM = hashcode[colorM];
+  if ((codeP == -1) || (codeM == -1)) goto color_missing;
+    
+  // skip remaining header lines
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  
+  // load the SED table data
+  Nrow = 0;
+  NROW = 100;
+  ALLOCATE (SEDtableRaw, SEDtableRow, NROW);
+  while (scan_line(f, line) != EOF) {
+    fparse (&SEDtableRaw[Nrow].Temp, 1, line);
+    fparse (&SEDtableRaw[Nrow].Av, 2, line);
+    ALLOCATE (SEDtableRaw[Nrow].mags, float, Nfilter);
+    for (i = 0; i < Nfilter; i++) {
+      fparse (&SEDtableRaw[Nrow].mags[i], i + 3, line);
+    }
+    SEDtableRaw[Nrow].color = SEDtableRaw[Nrow].mags[codeP] - SEDtableRaw[Nrow].mags[codeM];
+    Nrow ++;
+    CHECK_REALLOCATE (SEDtableRaw, SEDtableRow, NROW, Nrow, 100);
+  }      
+
+  // sort the SEDtable by the reference colors
+  SEDtable = sort_SEDtable (SEDtableRaw, Nrow);
+
+  // create holder for the source data
+  ALLOCATE (sourceValue.mags, float, Nfilter);
+  ALLOCATE (sourceError.mags, float, Nfilter);
+
+  if (PLOT) {
+    if (!GetGraph (&graphdata, &kapa, NULL)) return (FALSE);
+    SetLimitsRaw (wavecode, NULL, Nfilter, &graphdata);
+    graphdata.style = 2;
+    graphdata.ptype = 2;
+    KapaClearSections (kapa);
+    magSection.name = strcreate ("mag");
+    magSection.x  = 0;
+    magSection.dx = 1;
+    magSection.y  = 0.5;
+    magSection.dy = 0.5;
+    resSection.name = strcreate ("res");
+    resSection.x  = 0.0;
+    resSection.dx = 1.0;
+    resSection.y  = 0.0;
+    resSection.dy = 0.5;
+    
+    KapaSetFont (kapa, "helvetica", 14);
+    ALLOCATE (fitmags, float, Nfilter);
+    ALLOCATE (fiterrs, float, Nfilter);
+  }
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  // XXX add interrupt checks
+  gprint (GP_ERR, "using %d possible regions\n", skylist[0].Nregions);
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    // perform the fit to all sources
+    for (i = 0; i < catalog.Naverage; i++) {
+
+      // blank out the source array
+      for (j = 0; j < Nfilter; j++) {
+	sourceValue.mags[j] = 100;
+      }	
+
+      // load the measurements for this source
+      m = catalog.average[i].measureOffset;
+      for (j = 0; j < catalog.average[i].Nmeasure; j++) {
+	idx = hashcode[catalog.measure[m+j].photcode];
+	if (idx == -1) continue;
+	// XXX do something more clever if more than one value exists per photcode
+	sourceValue.mags[idx] = catalog.measure[m+j].M + vegaToAB[idx];
+	sourceError.mags[idx] = catalog.measure[m+j].dM;
+	if ((catalog.measure[m+j].photcode == USNOred) || (catalog.measure[m+j].photcode == USNOblu)) {
+	  sourceError.mags[idx] = 0.3;
+	}
+      }
+
+      // XXX for the moment, skip sources without ref color
+      if (sourceValue.mags[codeP] > 50) continue;
+      if (sourceValue.mags[codeM] > 50) continue;
+      color = sourceValue.mags[codeP] - sourceValue.mags[codeM];
+
+      // XXX find tableRow within 0.1 mag of color
+      start = SEDcolorBracket (SEDtable, Nrow, color);
+      minFit = SEDchisq (SEDtable[start], &sourceValue, &sourceError, Nfilter);
+      minFit.row = start;
+
+      // search for min chisq backwards
+      done = FALSE;
+      row = start - 1;
+      while (!done && (row > 0)) {
+	testFit = SEDchisq (SEDtable[row], &sourceValue, &sourceError, Nfilter);
+	if (testFit.chisq < minFit.chisq) {
+	  minFit = testFit;
+	  minFit.row = row;
+	}
+	if (fabs(SEDtable[row][0].color - color) > 0.5) done = TRUE;
+	row --;
+      }
+
+      // search for min chisq forwards
+      done = FALSE;
+      row = start + 1;
+      while (!done && (row < Nrow)) {
+	testFit = SEDchisq (SEDtable[row], &sourceValue, &sourceError, Nfilter);
+	if (testFit.chisq < minFit.chisq) {
+	  minFit = testFit;
+	  minFit.row = row;
+	}
+	if (fabs(SEDtable[row][0].color - color) > 0.5) done = TRUE;
+	row ++;
+      }
+
+      Nfit ++;
+      // create the vectors for the example plots
+      if (PLOT) {
+	// find plot range
+	SetLimitsRaw (NULL, SEDtable[minFit.row][0].mags, Nfilter, &graphdata);
+	SWAP (graphdata.ymin, graphdata.ymax);
+
+	KapaClearSections (kapa);
+	KapaSetSection (kapa, &magSection);
+    	KapaSetLimits (kapa, &graphdata);
+	KapaBox (kapa, &graphdata);
+	graphdata.color = KapaColorByName ("blue");
+	graphdata.etype = 0;
+	KapaPrepPlot (kapa, Nfilter, &graphdata);
+	KapaPlotVector (kapa, Nfilter, wavecode, "x");
+	KapaPlotVector (kapa, Nfilter, SEDtable[minFit.row][0].mags, "y");
+	graphdata.color = KapaColorByName ("red");
+	graphdata.etype = 1;
+	for (j = 0; j < Nfilter; j++) {
+	  fitmags[j] = 100;
+	  fiterrs[j] = 0;
+	  if (sourceValue.mags[j] > 50) continue;
+	  fitmags[j] = sourceValue.mags[j] - minFit.Md;
+	  fiterrs[j] = sourceError.mags[j];
+	}
+	KapaPrepPlot (kapa, Nfilter, &graphdata);
+	KapaPlotVector (kapa, Nfilter, wavecode, "x");
+	KapaPlotVector (kapa, Nfilter, fitmags, "y");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dym");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dyp");
+	KapaSendLabel (kapa, "model,fit (mags)", 1);
+
+	sprintf (line, "star: %10.6f %10.6f  T: %5.0fK  A_V|: %4.2f  M_D|: %5.2f  &sc&h^2|: %5.2f", 
+		 catalog.average[i].R, catalog.average[i].D, 
+		 SEDtable[minFit.row][0].Temp, SEDtable[minFit.row][0].Av, minFit.Md, minFit.chisq);
+	KapaSendLabel (kapa, line, 2);
+	KapaSendLabel (kapa, "model,fit (mags)", 1);
+
+	KapaSetSection (kapa, &resSection);
+	graphdata.ymin = -1.0;
+	graphdata.ymax = +1.0;
+    	KapaSetLimits (kapa, &graphdata);
+	KapaBox (kapa, &graphdata);
+	graphdata.color = KapaColorByName ("red");
+	graphdata.etype = 1;
+
+	for (j = 0; j < Nfilter; j++) {
+	  fitmags[j] = 100;
+	  fiterrs[j] = 0;
+	  if (sourceValue.mags[j] > 50) continue;
+	  fitmags[j] = sourceValue.mags[j] - minFit.Md - SEDtable[minFit.row][0].mags[j];
+	  fiterrs[j] = sourceError.mags[j];
+	}
+	KapaPrepPlot (kapa, Nfilter, &graphdata);
+	KapaPlotVector (kapa, Nfilter, wavecode, "x");
+	KapaPlotVector (kapa, Nfilter, fitmags, "y");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dym");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dyp");
+	KapaSendLabel (kapa, "wavelength (nm)", 0);
+	KapaSendLabel (kapa, "resid (mags)", 1);
+
+	KiiCursorOn (kapa);
+	while (KiiCursorRead (kapa, &X, &Y, &ZP, &RA, &DEC, key)) {
+	  gprint (GP_ERR, "window: %f %f (%s)\n", X, Y, key);
+	  if (!strcasecmp (key, "Q")) {
+	    KiiCursorOff (kapa);
+	    break;
+	  }
+	  if (!strcasecmp (key, "ESCAPE")) {
+	    KiiCursorOff (kapa);
+	    goto escape;
+	  }
+	}
+      }
+      // we now have the min chisq row. use this to supply the other filter values....
+    }
+    dvo_catalog_free (&catalog);
+  }
+  gprint (GP_ERR, "fitted %d stars\n", Nfit);
+  status = TRUE;
+  goto finish;
+  
+usage:
+  gprint (GP_ERR, "USAGE: fitset (sedtable) : (F) - (F)\n");
+  goto escape;
+
+table_missing:
+  gprint (GP_ERR, "ERROR: can't open the SED table\n");
+  goto escape;
+
+color_missing:
+  gprint (GP_ERR, "ERROR: reference color not in SED table\n");
+  goto escape;
+
+color_undefined:
+  gprint (GP_ERR, "ERROR: undefined photcode in reference color\n");
+  goto escape;
+
+code_missing:
+  gprint (GP_ERR, "ERROR: undefined photcode in SED table\n");
+  goto escape;
+
+escape:
+  status = FALSE;
+  goto finish;
+
+finish:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  if (wavecode != NULL) free (wavecode);
+  if (hashcode != NULL) free (hashcode);
+  if (SEDtableRaw != NULL) {
+    for (i = 0; i < Nrow; i++) {
+      free (SEDtableRaw[i].mags);
+    }
+    free (SEDtableRaw);
+  }
+  if (SEDtable != NULL) free (SEDtable);
+  if (sourceValue.mags != NULL) free (sourceValue.mags);
+  if (sourceError.mags != NULL) free (sourceError.mags);
+
+  signal (SIGINT, oldsignal);
+  return (status);
+}
+
+// fit the data (with errors) to the given table row
+SEDfit SEDchisq (SEDtableRow *ref, SEDtableRow *data, SEDtableRow *error, int Nfilter) {
+
+  int i;
+  double Sm, Sd, S2, wt, dM;
+  SEDfit fit;
+
+  Sm = Sd = S2 = 0.0;
+
+  for (i = 0; i < Nfilter; i++) {
+    if (data[0].mags[i] > 50.0) continue;
+
+    if (error[0].mags[i] == 0.0) {
+      wt = 1.0;
+    } else {
+      wt = 1.0 / SQ(error[0].mags[i]);
+    }
+
+    dM = data[0].mags[i] - ref[0].mags[i];
+    S2 += SQ(dM) * wt;
+    Sm += dM * wt;
+    Sd += wt;
+  }
+    
+  // row is assigned after fit
+  fit.row = -1;
+  fit.Md = Sm / Sd;
+  fit.chisq = S2 + SQ(fit.Md) * Sd - 2*fit.Md*Sm;
+
+  return (fit);
+}
+
+// find the first table row within 0.1 mag of the requested color (or within 10)
+int SEDcolorBracket (SEDtableRow **table, int Ntable, float color) {
+
+  int Nlo, Nhi, N;
+  float tcolor;
+
+  N = Nlo = 0; Nhi = Ntable;
+  tcolor = table[Nlo][0].color;
+  while ((Nhi - Nlo > 10) && (fabs(tcolor-color) > 0.1)) {
+    N = 0.5*(Nlo + Nhi);
+    N = MAX (N, 0);
+    N = MIN (N, Ntable - 1);
+    tcolor = table[N][0].color;
+    if (tcolor < color) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  return (N);
+}
+
+SEDtableRow **sort_SEDtable (SEDtableRow *raw, int N) {
+
+  int i;
+  SEDtableRow **value;
+  
+  if (N <= 0) return (NULL);
+
+  ALLOCATE (value, SEDtableRow *, N);
+  for (i = 0; i < N; i++) {
+    value[i] = &raw[i];
+  }
+
+# define SWAPFUNC(A,B){ SEDtableRow *temp = value[A]; value[A] = value[B]; value[B] = temp; }
+# define COMPARE(A,B)(value[A][0].color < value[B][0].color)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+  return (value);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/gcat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/gcat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/gcat.c	(revision 22322)
@@ -0,0 +1,38 @@
+# include "dvoshell.h"
+
+int gcat (int argc, char **argv) {
+  
+  int i;
+  struct stat filestat;
+  double Ra, Dec, Radius;
+  SkyTable *sky;
+  SkyList *skylist;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: gcat RA DEC [Radius]\n");
+    return (FALSE);
+  }
+
+  /* load sky from correct table */
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  if (argc == 4) 
+    Radius = atof(argv[3]);
+  else 
+    Radius = 0.0001;
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    if (stat (skylist[0].filename[i], &filestat) != -1) {
+      gprint (GP_ERR, "%3d %s *\n", i, skylist[0].regions[i][0].name);
+    } else {
+      gprint (GP_ERR, "%3d %s\n", i, skylist[0].regions[i][0].name);
+    } 
+    set_str_variable ("CATNAME", skylist[0].filename[i]);
+  }
+
+  return (TRUE);
+  SkyListFree (skylist);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/get_regions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/get_regions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/get_regions.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "dvoshell.h"
+
+/* return region files containing given image */
+GSCRegion *get_regions (Image *image, int *Nregions) {
+  
+  GSCRegion *region;
+  FILE *f;
+  double x, y;
+  double dec, ra;
+  int j, done, nregion, NREGION;
+  char filename[256], path[256];
+  
+  VarConfig ("CATDIR", "%s", path);
+  VarConfig ("GSCFILE", "%s", filename);
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't find GSC region file %s\n", filename);
+    exit (0);
+  }
+  
+  /* find regions at image corners */
+  NREGION = 10;
+  ALLOCATE (region, GSCRegion, NREGION);
+  nregion = 0;
+
+  /* look for new regions on grid across image */ 
+  for (x = 0.0; x <= 1.0; x+=0.25) {
+    for (y = 0.0; y <= 1.0; y+=0.25) {
+      XY_to_RD (&ra, &dec, image[0].NX*(1.1*x - 0.05), image[0].NY*(1.1*y - 0.05), &image[0].coords);
+      aregion (&region[nregion], f, ra, dec, path);
+      done = FALSE;
+      for (j = 0; (j < nregion) && !done; j++) {
+	if (!strcmp (region[nregion].filename, region[j].filename)) {
+	  nregion --;
+	  done = TRUE;
+	}
+      }
+      nregion ++;
+      if (nregion == NREGION) {
+	NREGION += 10;
+	REALLOCATE (region, GSCRegion, NREGION);
+      }
+    }
+  }
+  *Nregions = nregion;
+  
+  fclose (f);
+  return (region);
+  
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/getxtra.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/getxtra.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/getxtra.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "dvoshell.h"
+
+int getxtra (int argc, char **argv) {
+  
+  SelectXtra = (char *) NULL;
+  if (N = get_argument (argc, argv, "-xtra")) {
+    SelectXtra = TRUE;
+    remove_argument (N, &argc, argv);
+    XtraType = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+    XtraValue = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  SelectRegion = (char *) NULL;
+  if (N = get_argument (argc, argv, "-region")) {
+    SelectRegion = TRUE;
+    remove_argument (N, &argc, argv);
+    RA = atof (atof[N]);
+    remove_argument (N, &argc, argv);
+    Dec = atof (atof[N]);
+    remove_argument (N, &argc, argv);
+    Radius = atof (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Mode = (char *) NULL;
+  if (N = get_argument (argc, argv, "-mode")) {
+    remove_argument (N, &argc, argv);
+    Mode = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Range = (char *) NULL;
+  if (N = get_argument (argc, argv, "-range")) {
+    remove_argument (N, &argc, argv);
+    Range = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: addxtra R D radius (type) (value)\n");
+    return (FALSE);
+  }
+  
+  
+
+  /*
+
+  validate the input values (type, defines the needed options)
+  find catalog (based on r,d)
+  load catalog (need to load measures?)
+  find the object
+  find the xtra entry (sorted by ra/dec? sorted by averef?)
+  add new entry
+  save catalog 
+  */
+}
+
+/* 
+   get specified values for specified objects 
+
+   getxtra type -xtra name K01.01 
+   getxtra type -region R D radius 
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/gimages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/gimages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/gimages.c	(revision 22322)
@@ -0,0 +1,184 @@
+# include "dvoshell.h"
+
+// XXX this needs some help: it should define the region for image_subset or otherwise
+// limit the input selection
+int gimages (int argc, char **argv) {
+  
+  int i, j, N, Nimage, Nfound, *subset, Nsubset, status;
+  double ra, dec, Ra, Dec, X, Y, Yo;
+  double trange, t;
+  int TimeSelect, PixelCoords, TimeFormat, PhotCodeSelect;
+  time_t tzero, TimeReference;
+  char name[64], *date;
+  int typehash;
+  SkyRegionSelection *selection;
+
+  PhotCode *code;
+  Image *image;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-tref"))) {
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+
+    t = atof (argv[N]);
+    tzero = TimeRef (t, TimeReference, TimeFormat);
+    remove_argument (N, &argc, argv);
+
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  code = NULL;
+  PhotCodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    PhotCodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PixelCoords = FALSE;
+  if ((N = get_argument (argc, argv, "-pix"))) {
+    remove_argument (N, &argc, argv);
+    PixelCoords = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: gimages RA DEC [-time t dt] [-pix]\n");
+    return (FALSE);
+  }
+
+  if (!ohana_str_to_radec (&Ra, &Dec, argv[1], argv[2])) return (FALSE);
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  BuildChipMatch (image, Nimage);
+
+  int DistortImage = wordhash ("-DIS");
+  int TriangleUp   = wordhash ("TRP-");
+  int TriangleDn   = wordhash ("TRM-");
+
+  Nfound = 0;
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    if (PhotCodeSelect) {
+      if ((code[0].type == PHOT_REF) || (code[0].type == PHOT_DEP)) {
+	if (code[0].code != image[i].photcode) continue;
+      } 
+      if (code[0].type == PHOT_SEC) {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (image[i].photcode)) continue;
+      } 
+    }      
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+
+    status = RD_to_XY (&X, &Y, Ra, Dec, &image[i].coords);
+    if (!finite(X)) continue;
+    if (!finite(Y)) continue;
+    if (!status) continue;
+
+    typehash = wordhash (&image[i].coords.ctype[4]);
+
+    if (typehash == DistortImage) {
+      if (X < -0.5*image[i].NX) continue;
+      if (Y < -0.5*image[i].NY) continue;
+      if (X > +0.5*image[i].NX) continue;
+      if (Y > +0.5*image[i].NY) continue;
+      goto got_spot;
+    } 
+
+    typehash = wordhash (image[i].coords.ctype);
+    if (typehash == TriangleUp) {
+      if (Y < -0.5*image[i].NY) continue;
+      Yo = +0.5*image[i].NY + 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y > Yo) continue;
+      Yo = +0.5*image[i].NY - 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y > Yo) continue;
+      goto got_spot;
+    }
+    if (typehash == TriangleDn) {
+      if (Y > +0.5*image[i].NY) continue;
+      Yo = -0.5*image[i].NY + 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y < Yo) continue;
+      Yo = -0.5*image[i].NY - 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y < Yo) continue;
+      goto got_spot;
+    }
+
+    {
+      if (X < 0) continue;
+      if (Y < 0) continue;
+      if (X > image[i].NX) continue;
+      if (Y > image[i].NY) continue;
+    }
+
+    /*** XXX we need to re-introduce the use of applyMcal
+    Mcal = applyMcal (&image[i], 2048.0, 2048.0);
+    ***/
+
+  got_spot:
+    date = ohana_sec_to_date (image[i].tzero);
+
+    if (PixelCoords) {
+      gprint (GP_LOG, "%3d %s %6.1f %6.1f %20s %5d %2d %4.2f %6.3f %5.3f %5.3f %4x\n", 
+	       Nfound, image[i].name, X, Y, date, image[i].nstar, image[i].photcode, image[i].secz, image[i].Mcal, image[i].dMcal, image[i].exptime, image[i].code);
+    } else {
+      XY_to_RD (&ra, &dec, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+      gprint (GP_LOG, "%3d %s %8.4f %8.4f %20s %5d %2d %4.2f %6.3f %5.3f %5.3f %4x\n", 
+	       Nfound, image[i].name, ra, dec, date, image[i].nstar, image[i].photcode, image[i].secz, image[i].Mcal, image[i].dMcal, image[i].exptime, image[i].code);
+    }
+    sprintf (name, "IMAGEx:%d", Nfound);
+    set_variable     (name, X);
+    sprintf (name, "IMAGEy:%d", Nfound);
+    set_variable     (name, Y);
+    sprintf (name, "IMAGEt:%d", Nfound);
+    set_str_variable (name, date);
+    sprintf (name, "IMAGEccd:%d", Nfound);
+    set_int_variable (name, image[i].ccdnum);
+    sprintf (name, "IMAGEname:%d", Nfound);
+    set_str_variable (name, image[i].name);
+    Nfound ++;
+    free (date);
+  }
+  set_int_variable ("IMAGEx:n", Nfound);
+  set_int_variable ("IMAGEy:n", Nfound);
+  set_int_variable ("IMAGEt:n", Nfound);
+  set_int_variable ("IMAGEccd:n", Nfound);
+  set_int_variable ("IMAGEname:n", Nfound);
+
+  free (image);
+  free (subset);
+
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/gstar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/gstar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/gstar.c	(revision 22322)
@@ -0,0 +1,368 @@
+# include "dvoshell.h"
+
+void initPhotcodeSequence (int Nsecfilt);
+void freePhotcodeSequence ();
+void printPhotcodeSequence (Average *average, SecFilt *secfilt, int entry, int type);
+
+int gstar (int argc, char **argv) {
+  
+  char *date;
+  double Ra, Dec, Radius, Radius2, r, dec0, dec1;
+  double Mcat, Mrel;
+  double *RA, *DEC;
+  int i, j, k, m, N, *N1, Nsecfilt, NPTS, QUIET, FULL_OUTPUT, INST;
+  int Nstars, found, GetMeasures, Nlo, Nhi;
+  int SaveVectors;
+  Vector *vec1, *vec2, *vec3, *vec4;
+  SkyTable *sky;
+  SkyList *skylist;
+  Catalog catalog;
+  int TimeFormat;
+  time_t TimeReference;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  INST = FALSE;
+  if ((N = get_argument (argc, argv, "-inst"))) {
+    INST = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  NPTS = 0;
+  vec1 = vec2 = vec3 = vec4 = NULL;
+  SaveVectors = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    SaveVectors = TRUE;
+    if ((vec1 = SelectVector ("gs:m", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec2 = SelectVector ("gs:t", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec3 = SelectVector ("gs:z", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec4 = SelectVector ("gs:f", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  }
+
+  FULL_OUTPUT = FALSE;
+  if ((N = get_argument (argc, argv, "-full"))) {
+    FULL_OUTPUT = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  GetMeasures = FALSE;
+  if ((N = get_argument (argc, argv, "-m"))) {
+    GetMeasures = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-meas"))) {
+    GetMeasures = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: gstar RA DEC Radius [-m]\n");
+    return (FALSE);
+  }
+  
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  Radius = atof (argv[3]);
+
+  while (Ra < 0.0) Ra += 360.0;
+  while (Ra > 360.0) Ra -= 360.0;
+  
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* lock, load, unlock catalog */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (N1, int, Nstars);
+
+  /* find star(s) in RA, DEC list -- use a dumb algorithm for now, improve later */
+  /* stars are not guaranteed to be sorted in RA or in DEC, so first sort the list */
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    N1[i] = i;
+  }
+  /* sort list by DEC */
+  if (Nstars > 1) sort_coords_index (DEC, RA, N1, Nstars);
+  /* at this point, RA, DEC, and N1 are sorted by DEC.  
+     catalog.average[N1[i]].R = RA[i] */
+
+  /* bracket the RA range of interest */
+  dec0 = Dec - Radius;
+  dec1 = Dec + Radius;
+
+  Nlo = 0; Nhi = catalog.Naverage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (DEC[N] < dec0) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  /* DEC[Nlo] is guaranteed to be just lower than dec0 */
+
+  Radius2 = Radius*Radius;
+  found = FALSE;
+
+  /* data has been loaded, get ready to plot it */
+  if (SaveVectors) {
+    N = 0;
+    NPTS = 1000;
+    ALLOCATE (vec1[0].elements, float, NPTS);
+    ALLOCATE (vec2[0].elements, float, NPTS);
+    ALLOCATE (vec3[0].elements, float, NPTS);
+    ALLOCATE (vec4[0].elements, float, NPTS);
+  }
+
+  initPhotcodeSequence (Nsecfilt);
+
+  for (i = Nlo; (i < catalog.Naverage) && !found; i++) {
+
+    if (dec0 > DEC[i]) continue;
+    if (dec1 < DEC[i]) found = TRUE;
+    
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      k = N1[i];
+      if (!QUIET) {
+	gprint (GP_LOG, "star: %d\n", k);
+	gprint (GP_LOG, "%11.7f ", catalog.average[k].R);
+	gprint (GP_LOG, "%11.7f ", catalog.average[k].D);
+	gprint (GP_LOG, "%3d   ",  catalog.average[k].Nmeasure);
+	gprint (GP_LOG, "%4.1f ",  0.01*catalog.average[k].Xp);
+	gprint (GP_LOG, "%5d",     catalog.average[k].code);
+
+	if (FULL_OUTPUT) {
+	    gprint (GP_LOG, "%f",     catalog.average[k].dR);
+	    gprint (GP_LOG, "%f",     catalog.average[k].dD);
+	    gprint (GP_LOG, "%f",     catalog.average[k].uR);
+	    gprint (GP_LOG, "%f",     catalog.average[k].uD);
+	    gprint (GP_LOG, "%f",     catalog.average[k].duR);
+	    gprint (GP_LOG, "%f",     catalog.average[k].duD);
+	    gprint (GP_LOG, "%f",     catalog.average[k].P);
+	    gprint (GP_LOG, "%f",     catalog.average[k].dP);
+	    gprint (GP_LOG, "%x",     catalog.average[k].objID);
+	    gprint (GP_LOG, "%x",     catalog.average[k].catID);
+	}
+
+	gprint (GP_LOG, "\n");
+      
+	/* filter names */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 3);
+	gprint (GP_LOG, "\n");
+
+	/* average mags */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 0);
+	gprint (GP_LOG, "\n");
+
+	/* average mag errors */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 1);
+	gprint (GP_LOG, "\n");
+
+	/* average mag chisq */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 2);
+	gprint (GP_LOG, "\n");
+      }
+
+      if (GetMeasures || SaveVectors) {
+	m = catalog.average[k].measureOffset;
+	for (j = 0; j < catalog.average[k].Nmeasure; j++, m++) {
+
+	  Mcat = PhotCat (&catalog.measure[m]);
+	  if (INST) {
+	    Mrel = PhotInst (&catalog.measure[m]);
+	  } else {
+	    Mrel = PhotRel (&catalog.measure[m], &catalog.average[k], &catalog.secfilt[k*Nsecfilt]);
+	  }
+
+	  if (GetMeasures && !QUIET) {
+	    date = ohana_sec_to_date (catalog.measure[m].t);
+	    gprint (GP_LOG, "%6.3f ",  Mcat);
+	    gprint (GP_LOG, "%6.3f ",  Mrel);
+	    gprint (GP_LOG, "%5.3f  ", catalog.measure[m].dM);
+	    gprint (GP_LOG, "%20s  ",  date);
+	    gprint (GP_LOG, "%7.4f ",  catalog.measure[m].dR);
+	    gprint (GP_LOG, "%7.4f",   catalog.measure[m].dD);
+	    gprint (GP_LOG, "%2d ",    catalog.measure[m].dophot);
+	    gprint (GP_LOG, "%3x ",    catalog.measure[m].dbFlags);
+	    gprint (GP_LOG, "%5d ",    catalog.measure[m].photcode);
+	    gprint (GP_LOG, "%-20s  ", GetPhotcodeNamebyCode (catalog.measure[m].photcode));
+	    gprint (GP_LOG, "%5.2f ",  0.01*catalog.measure[m].FWx);
+	    gprint (GP_LOG, "%5.2f ",  0.01*catalog.measure[m].FWy);
+
+	    if (FULL_OUTPUT) {
+		gprint (GP_LOG, "%f", catalog.measure[m].Mcal);
+		gprint (GP_LOG, "%f", catalog.measure[m].Map);
+		gprint (GP_LOG, "%f", pow(10.0, 0.4*catalog.measure[m].dt));
+		gprint (GP_LOG, "%f", 1.0 + catalog.measure[m].airmass);
+		gprint (GP_LOG, "%f", catalog.measure[m].az);
+		gprint (GP_LOG, "%f", catalog.measure[m].Xccd);
+		gprint (GP_LOG, "%f", catalog.measure[m].Yccd);
+		gprint (GP_LOG, "%d", catalog.measure[m].dXccd);
+		gprint (GP_LOG, "%d", catalog.measure[m].dYccd);
+		gprint (GP_LOG, "%f", catalog.measure[m].Sky);
+		gprint (GP_LOG, "%f", catalog.measure[m].dSky);
+		gprint (GP_LOG, "%d", catalog.measure[m].averef);
+		gprint (GP_LOG, "%d", catalog.measure[m].detID);
+		gprint (GP_LOG, "%d", catalog.measure[m].imageID);
+		gprint (GP_LOG, "%f", catalog.measure[m].qPSF);
+		gprint (GP_LOG, "%f", catalog.measure[m].psfChisq);
+		gprint (GP_LOG, "%f", catalog.measure[m].crNsigma);
+		gprint (GP_LOG, "%f", catalog.measure[m].extNsigma);
+		gprint (GP_LOG, "%f", 0.01*catalog.measure[m].FWx);
+		gprint (GP_LOG, "%f", 0.01*catalog.measure[m].FWy);
+		gprint (GP_LOG, "%f", (360.0/(float)0xffff)*catalog.measure[m].theta);
+
+		gprint (GP_LOG, "%x", catalog.measure[m].photFlags);
+		gprint (GP_LOG, "%d", catalog.measure[m].stargal);
+	    }
+	    gprint (GP_LOG, "\n");
+
+	    free (date);
+	  }
+	  
+	  if (SaveVectors) {
+	    vec1[0].elements[N] = Mcat;
+	    vec2[0].elements[N] = TimeValue (catalog.measure[m].t, TimeReference, TimeFormat);
+	    vec3[0].elements[N] = catalog.measure[m].airmass;
+	    vec4[0].elements[N] = catalog.measure[m].photcode;
+	    N ++;
+	    if (N == NPTS - 1) {
+	      NPTS += 2000;
+	      REALLOCATE (vec1[0].elements, float, NPTS);
+	      REALLOCATE (vec2[0].elements, float, NPTS);
+	      REALLOCATE (vec3[0].elements, float, NPTS);
+	      REALLOCATE (vec4[0].elements, float, NPTS);
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  if (SaveVectors) {
+    vec1[0].Nelements = N;
+    vec2[0].Nelements = N;
+    vec3[0].Nelements = N;
+    vec4[0].Nelements = N;
+  }
+
+  free (RA);
+  free (DEC);
+  free (N1);
+  dvo_catalog_free (&catalog);
+
+  freePhotcodeSequence ();
+  return (TRUE);
+
+}
+
+void print_double (double value) {
+  if (isnan(value)) 
+    gprint (GP_LOG, "NaN    ");
+  else 
+    gprint (GP_LOG, "%6.3f ", value);
+}
+
+void print_short (double value, short int ival) {
+  if (ival == NAN_S_SHORT) 
+    gprint (GP_LOG, "NaN    ");
+  else 
+    gprint (GP_LOG, "%6.3f ", value);
+}
+
+// XXX fix printing to be in photcode numerical order for PRI/SEC data
+static int *sequence = NULL;
+
+void initPhotcodeSequence (int Nsecfilt) {
+
+  int j;
+  int *codeNumber;
+  PhotCode *code;
+
+  // sequence contains, in desired order, secfilt number (0 == pri)
+  ALLOCATE (sequence, int, Nsecfilt);
+  ALLOCATE (codeNumber, int, Nsecfilt);
+  
+  /* filter names -- primary code is 0 in this function */
+  for (j = 0; j < Nsecfilt; j++) {
+    code = GetPhotcodebyNsec (j);
+    codeNumber[j] = code[0].code;
+    sequence[j] = j;
+  }
+
+  isortpair (codeNumber, sequence, Nsecfilt);
+  free (codeNumber);
+}
+
+void freePhotcodeSequence () {
+  free (sequence);
+}
+
+
+void printPhotcodeSequence (Average *average, SecFilt *secfilt, int entry, int type) {
+
+  int seq;
+  PhotCode *code;
+
+  seq = sequence[entry];
+
+  switch (type) {
+    case 0: /* average mags */
+      if (seq == -1) {
+	print_double (NAN);
+      } else {
+	print_double (secfilt[seq].M);
+      }
+      break;
+
+    case 1: /* average mags errors */
+      if (seq == -1) {
+	print_double (NAN);
+      } else {
+	print_double (secfilt[seq].dM);
+      }
+      break;
+
+    case 2: /* average mag chisq */
+      if (seq == -1) {
+	print_short (NAN_S_SHORT, NAN_S_SHORT);
+      } else {
+	print_short (pow (10.0, 0.01*secfilt[seq].Xm), secfilt[seq].Xm);
+      }
+      break;
+
+    case 3: /* filter names */
+      code = GetPhotcodebyNsec (seq);
+      gprint (GP_LOG, "%-6s ", code[0].name);
+      break;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/Photometry
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/Photometry	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/Photometry	(revision 22322)
@@ -0,0 +1,26 @@
+
+cals: Plot residuals for reference stars. 
+
+ This function takes the currently displayed region and looks for all
+ stars which have 'reference' photometry and plots the difference
+ between the reference magnitude (in the given filter) and the
+ observed magnitude (same filter) versus one of several options:  the
+ star color (based on the 'reference' photometry), the star magnitude,
+ the airmass, time, secqunce, etc.
+
+ These plots can use one of two options for the 'observed' magnitude.
+ Either, the magnitude for individual measurement, using the currently
+ set values for the CCD zeropoints (from the photometry database), or
+ using the relative photometry determined for the particular star.  
+
+
+dmags: plot difference between two measured magnitudes
+
+resid: 
+
+abszero
+
+zeropts
+
+cmd
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/catalog
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/catalog	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/catalog	(revision 22322)
@@ -0,0 +1,74 @@
+
+  catalog (filename/-all) [many options!]
+
+  plot the star positions from any of several possible catalogs.  By
+default, the photometry database (see Database) is used, but the HST
+Guide Star Catalog and a simple ASCII file may also be used.  The
+plotting is done on window 0, with the current point type.  The point
+size can be scaled by the magnitude of the star or by the number
+of observations (-n).  
+
+There are many options to this function.  Here they are all:
+
+-e epoch: given ASCII catalog has this epoch, plot in J2000
+
+-ID ID: plot only measurements with this ID number
+ ID may be one of:
+	0 - star
+	4 - variable
+	5 - transient
+      100 - ghost image
+      101 - trail (satellite)
+      102 - bright star garbage	
+      note that numbers < 50 are static objects, 
+	and numbers > 100 are bad data.
+
+-all:  plot all data for the current display region
+
+-g: use the HST GSC instead of the photometry database
+
+-a RA DEC MAG:  the given file is an ASCII file with 
+	RA, DEC, and MAG in columns given by the three numbers
+
+-v ra dec scale: save the values in vectors with the three names given
+	the ra and dec are always used, but the third is the size of 
+	the points plotted, based on whatever the scaling is.
+
+-m m1 m2: scale points by magnitude, with smallest points having
+	magnitude m2.
+
++n n1 n2: scale points by number of measurements, with smallest points
+	having number n1.
+
+-n n1 n2: scale points by number of missing values, with smallest points
+	having number n1.
+
+Note that if two of -m, -n, +n are given, the first determines the
+ scaling of the points.  the second provides an allowed range, so
+ points are only plotted if the second parameter has a value in the
+ given range.
+
+Examples: 
+
+  catalog -all -m 12 18  
+    plot all stars in current region with magnitude 12 having the
+    biggest points and magnitude 18 having the smallest.
+   
+  catalog -all -m 12 18 +n 2 4
+    plot all stars in current region with magnitude 12 having the
+    biggest points and magnitude 18 having the smallest, but only
+    if the stars have 2-4 measurements each
+   
+  catalog -all -m 12 18 +n 0 1 -ID 101
+    plot all stars in current region with magnitude 12 having the
+    biggest points and magnitude 18 having the smallest, but only
+    if the stars have 1 measurements each, and if the object is 
+    found to be a trail.
+   
+  catalog n2230/1819.cpt -n 2 8  
+    plot all stars in catalog n2230/1819.cpt with the biggest points
+    having 8 measurements and the smallest having only 2.
+
+  See Also: lcat, pcat, style, region
+
+   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/images
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/images	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/images	(revision 22322)
@@ -0,0 +1,9 @@
+
+  images 
+
+  plot the outlines of images in the photometry database and list
+  along with relevant information.  An example of relevant data is the
+  image time, which can be used in other functions to define a specific
+  image or a range of images relevant for the function.
+
+  See also: Database, lcat, pcat
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/imstats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/imstats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/imstats	(revision 22322)
@@ -0,0 +1,5 @@
+
+  imstats 
+
+  list interesting statistics for a set of images.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/lcat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/lcat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/lcat	(revision 22322)
@@ -0,0 +1,10 @@
+
+  lcat [-all]
+
+  list photometry database files in the current region.  if the option
+  -all is given, all database in the region are listed, along with a
+  comment to show if there is data in the file or not.  if the option
+  is not given, only those files with data are listed.
+
+  See also: region, pcat
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/lcurve
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/lcurve	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/lcurve	(revision 22322)
@@ -0,0 +1,8 @@
+
+  lcurve RA DEC Radius [-l]
+
+  plot light curves for all stars within Radius of the given
+  coordinates.  The units for all three are decimal degrees.  
+  The option -l autoscales the limits of the plot.  
+
+  See also: limits
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/mextract
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/mextract	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/mextract	(revision 22322)
@@ -0,0 +1,12 @@
+
+ mextract (filename) (value) [-g / -a Ncol]
+
+ extracts a vector from the photometry database.  The name also
+ specifies the type of data extracted (and is case insensitive).  Only
+ certain names may be used.
+
+ The possible values are:
+ ra, dec, mag, dmag, Mcal, Mrel, source, time, dR, dD
+
+
+ Note: this routine extracts the individual measurements.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/pcat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/pcat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/pcat	(revision 22322)
@@ -0,0 +1,9 @@
+
+  pcat [-all]
+
+  plot outlines of photometry database files in the current region.
+  if the option -all is given, all database in the region are listed.
+  if the option is not given, only those files with data are listed.
+
+  See also: region, lcat
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/resid
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/resid	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/resid	(revision 22322)
@@ -0,0 +1,12 @@
+
+  resid [-im time] [-X] [-l] (photometry file)
+
+  plot the photometry residuals for a photometry database file. 
+  For every star in the database, the average magnitude is plotted 
+  vs the difference between the average magnitude and each existing 
+  measurement for that star.  
+
+  -l		automatically set the plot limits.
+  -X		plot residual divided by photometric error
+  -im time	only plot the residuals for one of the images 
+		in the database, specified by the obseravation time.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/simage
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/simage	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/help/simage	(revision 22322)
@@ -0,0 +1,7 @@
+
+  simage (filename)
+
+  plot the positions of stars in a CMP file.  This function is a
+  useful test for image comparisons, and astrometry must be determined
+  before the function is run.
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/images.c	(revision 22322)
@@ -0,0 +1,301 @@
+# include "dvoshell.h"
+# define BETA 0.41421
+
+int wordhash (char *word) {
+  int value;
+
+  value = *(int *)word;
+  return value;
+}
+
+int images (int argc, char **argv) {
+
+  int i, j, Nimage, status, InPic, leftside, *plist, TimeSelect, ByName;
+  int WITH_MOSAIC, SOLO_MOSAIC, HIDDEN;
+  time_t tzero, tend;
+  int N, NPTS, n, npts, Npts, kapa;
+  Vector Xvec, Yvec;
+  double r[8], d[8], x[8], y[8], Rmin, Rmax, Rmid, trange, Radius;
+  Image *image;
+  Graphdata graphmode;
+  char name[256];
+  int typehash;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) goto usage;
+
+  WITH_MOSAIC = FALSE;
+  if ((N = get_argument (argc, argv, "+mosaic"))) {
+    remove_argument (N, &argc, argv);
+    WITH_MOSAIC = TRUE;
+  }
+
+  SOLO_MOSAIC = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    SOLO_MOSAIC = TRUE;
+    WITH_MOSAIC = TRUE;
+  }
+
+  HIDDEN = FALSE;
+  if ((N = get_argument (argc, argv, "-hidden"))) {
+    remove_argument (N, &argc, argv);
+    HIDDEN = TRUE;
+  }
+
+  ByName = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    ByName = TRUE;
+  }
+
+  Radius = 45;
+  if ((N = get_argument (argc, argv, "-radius"))) {
+    remove_argument (N, &argc, argv);
+    Radius = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) {
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  if (argc != 1) goto usage;
+  
+  /* it is not an error for the database not to have any images */
+  if ((image = LoadImages (&Nimage)) == NULL) return (TRUE);
+  BuildChipMatch (image, Nimage);
+
+  Rmin = graphmode.coords.crval1 - 180.0;
+  Rmax = graphmode.coords.crval1 + 180.0;
+  Rmid = 0.5*(Rmin + Rmax);
+  
+  int DistortImage = wordhash ("-DIS");
+  int TriangleUp   = wordhash ("TRP-");
+  int TriangleDn   = wordhash ("TRM-");
+  int TrianglePts  = wordhash ("TRI-");
+
+  npts = NPTS = 200;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  ALLOCATE (plist, int, NPTS);
+  n = N = 0;
+  for (i = 0; i < Nimage; i++) {
+    if (ByName && strncmp (image[i].name, name, strlen(name))) continue;
+    if (TimeSelect && ((image[i].tzero < tzero) || (image[i].tzero+image[i].trate*image[i].NY > tzero + trange))) continue;
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+
+    Npts = 4;
+    status = TRUE;
+    leftside = FALSE;
+
+    typehash = wordhash (&image[i].coords.ctype[4]);
+
+    /* DIS images represent a field, not a chip */
+    if ((typehash == DistortImage) && !WITH_MOSAIC) continue;
+    if ((typehash != DistortImage) &&  SOLO_MOSAIC) continue;
+    if (typehash == DistortImage) {
+      x[0] = -0.5*image[i].NX; y[0] = -0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = -0.5*image[i].NY;
+      x[2] = +0.5*image[i].NX; y[2] = +0.5*image[i].NY;
+      x[3] = -0.5*image[i].NX; y[3] = +0.5*image[i].NY;
+      goto got_type;
+    }
+
+    typehash = wordhash (image[i].coords.ctype);
+    if (typehash == TriangleUp) {
+      Npts = 3;
+      x[0] =                0; y[0] = +0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = -0.5*image[i].NY;
+      x[2] = -0.5*image[i].NX; y[2] = -0.5*image[i].NY;
+      goto got_type;
+    }
+    if (typehash == TriangleDn) {
+      Npts = 3;
+      x[0] =                0; y[0] = -0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = +0.5*image[i].NY;
+      x[2] = -0.5*image[i].NX; y[2] = +0.5*image[i].NY;
+      goto got_type;
+    }
+    // For 'TrianglePts' (TRI-), we are using the Mx,My, etc terms to save the coordinates
+    // this means triangular images cannot carry photometric zero-point variations
+    if (typehash == TrianglePts) {
+      Npts = 3;
+      x[0] = image[i].Mx;   y[0] = image[i].My;
+      x[1] = image[i].Mxxx; y[1] = image[i].Mxyy;
+      x[2] = image[i].Mxxy; y[2] = image[i].Myyy;
+      goto got_type;
+    }
+
+    // default layout
+    {
+      x[0] = 0;           y[0] = 0;
+      x[1] = image[i].NX; y[1] = 0;
+      x[2] = image[i].NX; y[2] = image[i].NY;
+      x[3] = 0;           y[3] = image[i].NY;
+    }
+
+  got_type:
+
+    /* project this image to screen display coords */
+    // check for boundary overlap?
+    for (j = 0; j < Npts; j++) {
+      status = XY_to_RD (&r[j], &d[j], x[j], y[j], &image[i].coords);
+      if (!status) break;
+      while (r[j] < Rmin) { r[j] += 360.0; }
+      while (r[j] > Rmax) { r[j] -= 360.0; }
+      if (j == 0) {
+	leftside = (r[j] < Rmid);
+      } 
+      if (j > 0) { 
+	if ( leftside && (r[j] > Rmid + 90)) { r[j] -= 360.0; }
+	if (!leftside && (r[j] < Rmid - 90)) { r[j] += 360.0; }
+      }
+    }
+
+    // extremely large-scale images with certain projections will have odd boundaries.
+    // eg, the ASCA images are essentially circles of radius ~60 degrees.  plot these as 
+    // octagons with some dummy size.
+    if (!status) {
+      int jp, xo, yo;
+      double rc, dc;
+      xo = 0.5*image[i].NX;
+      yo = 0.5*image[i].NY;
+      // is the image center on the screen?
+      status = XY_to_RD (&rc, &dc, 0.5*x[2], 0.5*y[2], &image[i].coords);
+      if (status && (image[i].NX * image[i].coords.cdelt2 > 90)) {
+	// draw an octagon with radius 45 degrees
+	double dX = Radius / image[i].coords.cdelt2;
+	x[0] = xo+dX;      y[0] = yo+BETA*dX;
+	x[1] = xo+BETA*dX; y[1] = yo+dX;
+	x[2] = xo-BETA*dX; y[2] = yo+dX;
+	x[3] = xo-dX;      y[3] = yo+BETA*dX;
+	x[4] = xo-dX;      y[4] = yo-BETA*dX;
+	x[5] = xo-BETA*dX; y[5] = yo-dX;
+	x[6] = xo+BETA*dX; y[6] = yo-dX;
+	x[7] = xo+dX;      y[7] = yo-BETA*dX;
+	Npts = 8;
+	j = 0;
+	for (jp = 0; jp < 8; jp++) {
+	  status = XY_to_RD (&r[j], &d[j], x[jp], y[jp], &image[i].coords);
+	  if (!status) continue;
+	  while (r[j] < Rmin) { r[j] += 360.0; }
+	  while (r[j] > Rmax) { r[j] -= 360.0; }
+	  if (j == 0) {
+	    leftside = (r[j] < Rmid);
+	  } 
+	  if (j > 0) { 
+	    if ( leftside && (r[j] > Rmid + 90)) { r[j] -= 360.0; }
+	    if (!leftside && (r[j] < Rmid - 90)) { r[j] += 360.0; }
+	  }
+	  j++;
+	}
+	Npts = j;
+      } else {
+	continue;
+      }
+    }
+    if (Npts == 0) continue;
+
+    status = FALSE;
+    for (j = 0; j < Npts; j++) {
+      status |= fRD_to_XY (&Xvec.elements[N+2*j], &Yvec.elements[N+2*j], r[j], d[j], &graphmode.coords);
+      if (j > 0) {
+	Xvec.elements[N+2*j - 1] = Xvec.elements[N+2*j];
+	Yvec.elements[N+2*j - 1] = Yvec.elements[N+2*j];
+      }
+    }
+    Xvec.elements[N+2*Npts-1] = Xvec.elements[N];
+    Yvec.elements[N+2*Npts-1] = Yvec.elements[N];
+    if (!status) continue;
+    // if none of the points are on the visible side of the projection, do not plot the image
+
+    InPic = FALSE;
+    for (j = 0; j < 2*Npts; j+=2) {
+      if ((Xvec.elements[N+j] >= graphmode.xmin) && 
+	  (Xvec.elements[N+j] <= graphmode.xmax) && 
+	  (Yvec.elements[N+j] >= graphmode.ymin) && 
+	  (Yvec.elements[N+j] <= graphmode.ymax))
+	InPic = TRUE;
+    }
+    // if (!status && HIDDEN) status = TRUE;
+    if (!InPic) continue;
+
+    plist[n] = i;
+    n++;
+    if (n > npts - 1) {
+      npts += 200;
+      REALLOCATE (plist, int, npts);
+    }
+    N+=2*Npts;
+    if (N + 16 >= NPTS) {  /* need to leave room for 8 point image */
+      NPTS += 400;
+      REALLOCATE (Xvec.elements, float, NPTS);
+      REALLOCATE (Yvec.elements, float, NPTS);
+    }
+  }
+
+  gprint (GP_ERR, "plotting %d images\n", n);
+  Xvec.Nelements = Xvec.Nelements = N;
+  if (N > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+  }
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (image);
+  return (TRUE);
+
+
+ usage:
+  gprint (GP_ERR, "USAGE: image [options]\n");
+  gprint (GP_ERR, "  +mosaic : show mosaic outline\n");
+  gprint (GP_ERR, "  -mosaic : only mosaic outline\n");
+  gprint (GP_ERR, "  -hidden : (deprecated)\n");
+  gprint (GP_ERR, "  -name   : only names matching (start of name)\n");
+  gprint (GP_ERR, "  -radius : display all-sky images with given radius octagon\n");
+  gprint (GP_ERR, "  -time (start) (range)  : show images for time and interval\n");
+  gprint (GP_ERR, "  -trange (start) (stop) : show images within time range\n");
+  return (FALSE);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imbox.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imbox.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imbox.c	(revision 22322)
@@ -0,0 +1,114 @@
+# include "dvoshell.h"
+
+int imbox (int argc, char **argv) {
+  
+  int j, kapa, Nskip, status, InPic, flipped, N, haveNx, haveNy, Nx, Ny, SOLO_PHU;
+  Vector Xvec, Yvec;
+  double r, d, x[4], y[4], Rmin, Rmax, Rmid;
+  Header header;
+  Coords coords;
+  Coords mosaic;
+  Graphdata graphmode;
+  FILE *f;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  SOLO_PHU = FALSE;
+  if ((N = get_argument (argc, argv, "-phu"))) {
+    SOLO_PHU = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imbox (filename)\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "file not found\n");
+    return (FALSE);
+  }
+  
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  Rmid = 0.5*(Rmin + Rmax);
+  
+  /* project this image to screen display coords */
+  ALLOCATE (Xvec.elements, float, 8);
+  ALLOCATE (Yvec.elements, float, 8);
+
+  while (gfits_fread_header (f, &header)) {
+    if (!GetCoords (&coords, &header)) goto skip;
+    if (!SOLO_PHU && !strcmp (&coords.ctype[4], "-DIS")) {
+      mosaic = coords;
+      RegisterMosaic (&mosaic);
+      goto skip;
+    }
+
+    // XXX currently, image uses an unsigned short for NX,XY. this is rather restrictive
+    // and needs to be at least checked.
+    haveNx = gfits_scan (&header, "NAXIS1",   "%d", 1, &Nx);
+    haveNy = gfits_scan (&header, "NAXIS2",   "%d", 1, &Ny);
+
+    if (!haveNx && !haveNy) {
+	haveNx = gfits_scan (&header, "IMNAXIS1",   "%d", 1, &Nx);
+	haveNy = gfits_scan (&header, "IMNAXIS2",   "%d", 1, &Ny);
+    }
+
+    if (!haveNx || !haveNy) {
+	fprintf (stderr, "missing image dimensions in header\n");
+	goto skip;
+    }
+
+    x[0] = 0;  y[0] = 0;
+    x[1] = Nx; y[1] = 0;
+    x[2] = Ny; y[2] = Ny;
+    x[3] = 0;  y[3] = Ny;
+    status = FALSE;
+    flipped = FALSE;
+    for (j = 0; j < 4; j++) {
+      XY_to_RD (&r, &d, x[j], y[j], &coords);
+      while ((j == 0) && (r < Rmin)) { flipped = TRUE; r += 360.0; }
+      while ((j == 0) && (r > Rmax)) { flipped = TRUE; r -= 360.0; }
+      if ((j > 0) && flipped) {
+	while (r < Rmid) r+= 360.0;
+	while (r > Rmid) r-= 360.0;
+      }
+      status |= fRD_to_XY (&Xvec.elements[2*j], &Yvec.elements[2*j], r, d, &graphmode.coords);
+      if (j > 0) {
+	Xvec.elements[2*j - 1] = Xvec.elements[2*j];
+	Yvec.elements[2*j - 1] = Yvec.elements[2*j];
+      }
+    }
+    Xvec.elements[7] = Xvec.elements[0];
+    Yvec.elements[7] = Yvec.elements[0];
+    InPic = FALSE;
+    for (j = 0; j < 8; j+=2) {
+      if ((Xvec.elements[j] >= graphmode.xmin) && 
+	  (Xvec.elements[j] <= graphmode.xmax) && 
+	  (Yvec.elements[j] >= graphmode.ymin) && 
+	  (Yvec.elements[j] <= graphmode.ymax))
+	InPic = TRUE;
+    }
+
+    Xvec.Nelements = Xvec.Nelements = 8;
+    if (InPic) {
+      graphmode.style = 2; /* points */
+      graphmode.ptype = 100; /* connect pairs of points */
+      graphmode.etype = 0;
+      PlotVectorPair (kapa, 8, Xvec.elements, Yvec.elements, &graphmode);
+    }
+  skip:
+    Nskip = gfits_data_size (&header);
+    fseek (f, Nskip, SEEK_CUR); 
+    gfits_free_header (&header);
+  }
+  fclose (f);
+  free (Xvec.elements);
+  free (Yvec.elements);
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imdata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imdata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imdata.c	(revision 22322)
@@ -0,0 +1,219 @@
+# include "dvoshell.h"
+
+int imdata (int argc, char **argv) {
+  
+  int i, j, k, I;
+  int Nimage, N, NPTS, found, mode, TimeSelect;
+  int n, Nregions, NREGIONS, TimeFormat;
+  int *subset, Nsubset;
+  double trange;
+  time_t tzero, start, stop, TimeReference;
+  Image *image;
+  Catalog catalog;
+  SkyTable *sky;
+  SkyList *skylist, *skyset;
+  Vector *vec;
+  SkyRegionSelection *selection;
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  start = stop = 0;
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+    gprint (GP_ERR, "searching in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+    if (trange > 0) {
+      start = tzero;
+      stop  = tzero + trange;
+    } else {
+      stop = tzero;
+      start  = tzero + trange;
+    }      
+  }
+
+  gprint (GP_ERR, "function is poorly defined; disabled and may be removed\n");
+  return (FALSE);
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: imdata (value) [-time t dt]\n");
+    return (FALSE);
+  }
+
+  /* identify selection */
+  mode = 0;
+  if (!strcasecmp (argv[1], "ra")) 
+    mode = 1;
+  if (!strcasecmp (argv[1], "dec")) 
+    mode = 2;
+  if (!strcasecmp (argv[1], "mag")) 
+    mode = 3;
+  if (!strcasecmp (argv[1], "dmag")) 
+    mode = 4;
+  if (!strcasecmp (argv[1], "Mcal")) 
+    mode = 5;
+  if (!strcasecmp (argv[1], "Mrel")) 
+    mode = 6;
+  if (!strcasecmp (argv[1], "source")) 
+    mode = 7;
+  if (!strcasecmp (argv[1], "x")) 
+    mode = 8;
+  if (!strcasecmp (argv[1], "y")) 
+    mode = 9;
+  if (!strcasecmp (argv[1], "time")) 
+    mode = 10;
+  if (mode == 0) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " ra dR dec dD mag dmag Mrel Mcal source time\n");
+    return (FALSE);
+  }
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  BuildChipMatch (image, Nimage);
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+
+  Nregions = 0;
+  NREGIONS = 10;
+  ALLOCATE (skylist, SkyList, 1);
+  ALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS);
+  skylist[0].ownElements = FALSE;
+
+  /* for each image of interest, find the appropriate region files */
+  for (i = 0; i < Nsubset; i++) {
+    I = subset[i];
+
+    if (!FindMosaicForImage (image, Nimage, I)) continue;
+    skyset = SkyListByImage (sky, -1, &image[I]);
+
+    for (j = 0; j < skyset[0].Nregions; j++) {
+      found = FALSE;
+      for (k = 0; (k < skylist[0].Nregions) && !found; k++) {
+	found = !strcmp (skylist[0].regions[k][0].name, skyset[0].regions[j][0].name);
+      }
+      if (found) continue;
+      skylist[0].regions[Nregions] = skyset[0].regions[j];
+      Nregions ++;
+      CHECK_REALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS, Nregions, 10);
+    }
+    SkyListFree (skyset);
+  }	
+  free (subset);
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    gprint (GP_ERR, "try %s\n", skylist[0].regions[i][0].name);
+  } 
+  
+  /* create output vector */
+  NPTS = 1000;
+  REALLOCATE (vec[0].elements, float, NPTS);
+  vec[0].Nelements = N = 0;
+
+  /* for each region file, extract the data of interest in the right time range */
+  for (j = 0; j < skylist[0].Nregions; j++) {
+
+    /* get file name and open */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* assign vector values */
+    switch (mode) {
+      case (1):  /* ra */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  n = catalog.measure[i].averef;
+	  vec[0].elements[N] = catalog.average[n].R - catalog.measure[i].dR / 3600.0;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (2):  /* dec */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  n = catalog.measure[i].averef;
+	  vec[0].elements[N] = catalog.average[n].D - catalog.measure[i].dD / 3600.0;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (3):  /* mag */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements[N] = catalog.measure[i].M;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (4):  /* dmag */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements[N] = catalog.measure[i].dM;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (5):  /* Mcal */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements[N] = catalog.measure[i].Mcal;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (6):  /* Mrel */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  n = catalog.measure[i].averef;
+	  // vec[0].elements[N] = catalog.average[n].M;
+	  N++;
+	}
+	break;
+      case (7):  /* source */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements[N] = catalog.measure[i].photcode;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (10):  /* time */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements[N] = TimeValue (catalog.measure[i].t, TimeReference, TimeFormat);
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+    }
+    dvo_catalog_free (&catalog);
+  }
+  
+  vec[0].Nelements = N;
+  REALLOCATE (vec[0].elements, float, MAX(1,N));
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imdense.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imdense.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imdense.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "dvoshell.h"
+extern double drand48();
+
+int imdense (int argc, char **argv) {
+  
+  long A, B;
+  int i, kapa, N, Nimage, status, NPTS;
+  double r, d, x, y, Rmin, Rmax;
+  Vector Xvec, Yvec;
+  Image *image;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  /* need options to list only images in region and only images in a time range */
+  /* also, option to list and not plot or plot and not list images */
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: image\n");
+    return (FALSE);
+  }
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+
+  NPTS = 200;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  N = 0;
+  for (i = 0; i < Nimage; i++) {
+    /* choose a position for point within image box */
+    x = (0.1 + 0.9*drand48()) * image[i].NX;
+    y = (0.1 + 0.9*drand48()) * image[i].NY;
+    /* project this image to screen display coords */
+    status = FALSE;
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    XY_to_RD (&r, &d, x, y, &image[i].coords);
+    while (r < Rmin) r += 360.0; 
+    while (r > Rmax) r -= 360.0; 
+    status |= fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, d, &graphmode.coords);
+    if ((Xvec.elements[N] >= graphmode.xmin) && 
+	(Xvec.elements[N] <= graphmode.xmax) && 
+	(Yvec.elements[N] >= graphmode.ymin) && 
+	(Yvec.elements[N] <= graphmode.ymax) && status) {
+      N++;
+      if (N > NPTS - 1) { 
+	NPTS += 200;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+      }
+    }
+  }
+
+  Xvec.Nelements = Xvec.Nelements = N;
+  if (N > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+  }
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (image);
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imextract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imextract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imextract.c	(revision 22322)
@@ -0,0 +1,320 @@
+# include "dvoshell.h"
+
+// need to upgrade this to support multiple fields and WHERE clauses
+
+enum {ZERO, 
+      RA, 
+      DEC, 
+      Xm, 
+      AIRMASS, 
+      MCAL, 
+      dMCAL, 
+      PHOTCODE, 
+      TIME, 
+      FWHM, 
+      EXPTIME, 
+      NSTAR, 
+      NCAL, 
+      SKY, 
+      FLAG, 
+      NX_PIX, 
+      NY_PIX, 
+      THETA, 
+      SKEW, 
+      SCALE, 
+      DSCALE, 
+      IMAGE_ID,
+      X_LL_CHIP,
+      X_LR_CHIP,
+      X_UL_CHIP,
+      X_UR_CHIP,
+      Y_LL_CHIP,
+      Y_LR_CHIP,
+      Y_UL_CHIP,
+      Y_UR_CHIP,
+      X_LL_FP,
+      X_LR_FP,
+      X_UL_FP,
+      X_UR_FP,
+      Y_LL_FP,
+      Y_LR_FP,
+      Y_UL_FP,
+      Y_UR_FP,
+};
+
+int imextract (int argc, char **argv) {
+  
+  int i, j, Nimage, mode, N, PhotcodeSelect;
+  int TimeSelect, *subset, Nsubset, TimeFormat, FlagSelect, FlagValue;
+  double x, y, ra, dec, t, trange;
+  time_t tzero, TimeReference;
+  SkyRegionSelection *selection;
+
+  PhotCode *code;
+  Image *image;
+  Vector *vec;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  /* check for time-based selection */
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+
+  /* check for region-based selection */
+  FlagValue = 0;
+  FlagSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-flag"))) {
+    remove_argument (N, &argc, argv);
+    FlagValue = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    FlagSelect = TRUE;
+  }
+
+  /* check for photcode-based selection */
+  code = NULL;
+  PhotcodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode %s not found in photcode table\n", argv[N]);
+      return (FALSE);
+    }
+    if ((code[0].type != PHOT_SEC) && (code[0].type != PHOT_DEP)) {
+      gprint (GP_ERR, "photcode must be primary, secondary, or dependent code\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imextract (value) [-region] [-time start range] [-photcode photcode]\n");
+    return (FALSE);
+  }
+  
+  /* identify selection */
+  mode = ZERO;
+  if (!strcasecmp (argv[1], "ra"       )) mode = RA;
+  if (!strcasecmp (argv[1], "dec"      )) mode = DEC;
+  if (!strcasecmp (argv[1], "Xm"       )) mode = Xm;
+  if (!strcasecmp (argv[1], "airmass"  )) mode = AIRMASS;
+  if (!strcasecmp (argv[1], "Mcal"     )) mode = MCAL;
+  if (!strcasecmp (argv[1], "dMcal"    )) mode = dMCAL;
+  if (!strcasecmp (argv[1], "photcode" )) mode = PHOTCODE;
+  if (!strcasecmp (argv[1], "time"     )) mode = TIME;
+  if (!strcasecmp (argv[1], "FWHM"     )) mode = FWHM;
+  if (!strcasecmp (argv[1], "exptime"  )) mode = EXPTIME;
+  if (!strcasecmp (argv[1], "nstar"    )) mode = NSTAR;
+  if (!strcasecmp (argv[1], "ncal"     )) mode = NCAL;
+  if (!strcasecmp (argv[1], "sky"      )) mode = SKY;
+  if (!strcasecmp (argv[1], "flag"     )) mode = FLAG;
+  if (!strcasecmp (argv[1], "NX"       )) mode = NX_PIX;
+  if (!strcasecmp (argv[1], "NY"       )) mode = NY_PIX;
+  if (!strcasecmp (argv[1], "theta"    )) mode = THETA;
+  if (!strcasecmp (argv[1], "skew"     )) mode = SKEW;
+  if (!strcasecmp (argv[1], "scale"    )) mode = SCALE;
+  if (!strcasecmp (argv[1], "dscale"   )) mode = DSCALE;
+  if (!strcasecmp (argv[1], "imageID"  )) mode = IMAGE_ID;
+  if (!strcasecmp (argv[1], "X_LL_CHIP")) mode = X_LL_CHIP;
+  if (!strcasecmp (argv[1], "X_LR_CHIP")) mode = X_LR_CHIP;
+  if (!strcasecmp (argv[1], "X_UL_CHIP")) mode = X_UL_CHIP;
+  if (!strcasecmp (argv[1], "X_UR_CHIP")) mode = X_UR_CHIP;
+  if (!strcasecmp (argv[1], "Y_LL_CHIP")) mode = Y_LL_CHIP;
+  if (!strcasecmp (argv[1], "Y_LR_CHIP")) mode = Y_LR_CHIP;
+  if (!strcasecmp (argv[1], "Y_UL_CHIP")) mode = Y_UL_CHIP;
+  if (!strcasecmp (argv[1], "Y_UR_CHIP")) mode = Y_UR_CHIP;
+  if (!strcasecmp (argv[1], "X_LL_FP"  )) mode = X_LL_FP;
+  if (!strcasecmp (argv[1], "X_LR_FP"  )) mode = X_LR_FP;
+  if (!strcasecmp (argv[1], "X_UL_FP"  )) mode = X_UL_FP;
+  if (!strcasecmp (argv[1], "X_UR_FP"  )) mode = X_UR_FP;
+  if (!strcasecmp (argv[1], "Y_LL_FP"  )) mode = Y_LL_FP;
+  if (!strcasecmp (argv[1], "Y_LR_FP"  )) mode = Y_LR_FP;
+  if (!strcasecmp (argv[1], "Y_UL_FP"  )) mode = Y_UL_FP;
+  if (!strcasecmp (argv[1], "Y_UR_FP"  )) mode = Y_UR_FP;
+  if (mode == ZERO) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " ra dec airmass Mcal dMcal Xm photcode time fwhm exptime nstar ncal sky flag\n");
+    return (FALSE);
+  }
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  if ((mode == RA) || (mode == DEC)) BuildChipMatch (image, Nimage);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  /* create storage vector */
+  REALLOCATE (vec[0].elements, float, Nimage);
+  vec[0].Nelements = Nimage;
+  
+  N = 0;
+  /* assign vector values */
+  for (i = 0; i < Nsubset; i++) {
+    j = subset[i];
+    if (PhotcodeSelect) {
+      if (code[0].type == PHOT_DEP) {
+	if (code[0].code != image[j].photcode) continue;
+      } else {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (image[j].photcode)) continue;
+      }
+    }
+    if (FlagSelect && (FlagValue != image[j].code)) continue;
+    switch (mode) {
+      case RA:
+	if (!FindMosaicForImage (image, Nimage, j)) continue;
+	x = 0.5*image[j].NX;
+	y = 0.5*image[j].NY;
+	XY_to_RD (&ra, &dec, x, y, &image[j].coords);
+	vec[0].elements[N] = ra;
+	break;
+      case DEC:
+	if (!FindMosaicForImage (image, Nimage, j)) continue;
+	x = 0.5*image[j].NX;
+	y = 0.5*image[j].NY;
+	XY_to_RD (&ra, &dec, x, y, &image[j].coords);
+	vec[0].elements[N] = dec;
+	break;
+      case Xm:
+	vec[0].elements[N] = pow(10.0, 0.01*image[j].Xm);
+	break;
+      case AIRMASS:
+	vec[0].elements[N] = image[j].secz;
+	break;
+      case MCAL:
+	vec[0].elements[N] = image[j].Mcal;
+	break;
+      case dMCAL:
+	vec[0].elements[N] = image[j].dMcal;
+	break;
+      case PHOTCODE:
+	vec[0].elements[N] = image[j].photcode;
+	break;
+      case TIME:
+	t = image[j].tzero + 0.5*image[j].NY * image[j].trate / 10000;
+	vec[0].elements[N] = TimeValue (t, TimeReference, TimeFormat);
+	break;
+      case FWHM:
+	vec[0].elements[N] = image[j].fwhm_x / 25.0;
+	break;
+      case EXPTIME:
+	vec[0].elements[N] = image[j].exptime;
+	break;
+      case NSTAR:
+	vec[0].elements[N] = image[j].nstar;
+	break;
+      case NCAL:
+	vec[0].elements[N] = image[j].Mxxxx;
+	break;
+      case SKY:
+	vec[0].elements[N] = image[j].Myyyy + 0x8000;
+	break;
+      case FLAG:
+	vec[0].elements[N] = image[j].code;
+	break;
+      case NX_PIX:
+	vec[0].elements[N] = image[j].NX;
+	break;
+      case NY_PIX:
+	vec[0].elements[N] = image[j].NY;
+	break;
+      case IMAGE_ID:
+	vec[0].elements[N] = image[j].imageID;
+	break;
+      case THETA: {
+	double theta1, theta2, s1, s2;
+	s1 = SIGN(image[j].coords.pc1_1);
+	s2 = SIGN(image[j].coords.pc2_2);
+	theta1 = DEG_RAD*atan2 (+s1*image[j].coords.pc1_2, s1*image[j].coords.pc1_1);
+	theta2 = DEG_RAD*atan2 (-s2*image[j].coords.pc2_1, s2*image[j].coords.pc2_2);
+	vec[0].elements[N] = 0.5*(theta1+theta2);
+	break; }
+      case SKEW: {
+	double theta1, theta2, s1, s2;
+	s1 = SIGN(image[j].coords.pc1_1);
+	s2 = SIGN(image[j].coords.pc2_2);
+	theta1 = DEG_RAD*atan2 (+s1*image[j].coords.pc1_2, s1*image[j].coords.pc1_1);
+	theta2 = DEG_RAD*atan2 (-s2*image[j].coords.pc2_1, s2*image[j].coords.pc2_2);
+	vec[0].elements[N] = (theta1-theta2);
+	break; }
+      case SCALE: {
+	double scale1, scale2;
+	scale1 = fabs(image[j].coords.cdelt1);
+	scale2 = fabs(image[j].coords.cdelt2);
+	vec[0].elements[N] = 0.5*(scale1+scale2);
+	break; }
+      case DSCALE: {
+	double scale1, scale2;
+	scale1 = fabs(image[j].coords.cdelt1);
+	scale2 = fabs(image[j].coords.cdelt2);
+	vec[0].elements[N] = (scale1-scale2);
+	break; }
+
+    // reference pixel extractions
+    case X_LL_CHIP:
+    case Y_LL_CHIP:
+    case Y_LR_CHIP:
+    case X_UL_CHIP:
+      vec[0].elements[N] = 0.0;
+      break;
+    case X_LR_CHIP:
+    case X_UR_CHIP:
+      vec[0].elements[N] = image[j].NX;
+      break;
+    case Y_UL_CHIP:
+    case Y_UR_CHIP:
+      vec[0].elements[N] = image[j].NX;
+      break;
+
+    case X_LL_FP:
+    case Y_LL_FP:
+      XY_to_LM (&x, &y, 0.0, 0.0, &image[j].coords);
+      vec[0].elements[N] = (mode == X_LL_FP) ? x : y;
+      break;
+    case X_LR_FP:
+    case Y_LR_FP:
+      XY_to_LM (&x, &y, image[j].NX, 0.0, &image[j].coords);
+      vec[0].elements[N] = (mode == X_LR_FP) ? x : y;
+      break;
+    case X_UL_FP:
+    case Y_UL_FP:
+      XY_to_LM (&x, &y, 0.0, image[j].NY, &image[j].coords);
+      vec[0].elements[N] = (mode == X_UL_FP) ? x : y;
+      break;
+    case X_UR_FP:
+    case Y_UR_FP:
+      XY_to_LM (&x, &y, image[j].NX, image[j].NY, &image[j].coords);
+      vec[0].elements[N] = (mode == X_UR_FP) ? x : y;
+      break;
+    }
+    N++;
+  }
+  
+  vec[0].Nelements = N;
+  REALLOCATE (vec[0].elements, float, N);
+
+  free (subset);
+  free (image);
+  return (TRUE);
+  
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imlist.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imlist.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imlist.c	(revision 22322)
@@ -0,0 +1,112 @@
+# include "dvoshell.h"
+
+int imlist (int argc, char **argv) {
+  
+  int i, j, N, Nimage, *subset, Nsubset, TimeSelect, RegionSelect, TimeFormat, NameSelect;
+  int PhotcodeSelect;
+  time_t tzero, TimeReference;
+  double r, d, trange, t;
+  char *name;
+  Image *image;
+  PhotCode *PhotcodeValue;
+  SkyRegionSelection *selection;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+    gprint (GP_ERR, "plotting in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+  }
+
+  RegionSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    RegionSelect = TRUE;
+  }
+
+  PhotcodeValue = NULL;
+  PhotcodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    PhotcodeValue = GetPhotcodebyName (argv[N]);
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-Nphotcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    PhotcodeValue = GetPhotcodebyCode (atoi(argv[N]));
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  name = NULL;
+  NameSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    NameSelect = TRUE;
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: image [-time start range] [-region] [-name string]\n");
+    return (FALSE);
+  }
+  
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  BuildChipMatch (image, Nimage);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    if (NameSelect && (strstr (image[i].name, name) == (char *) NULL)) continue;
+    if (PhotcodeSelect) {
+      if (PhotcodeValue[0].type == PHOT_DEP) {
+	if (PhotcodeValue[0].code != image[i].photcode) continue;
+      } else {
+	if (PhotcodeValue[0].code != GetPhotcodeEquivCodebyCode (image[i].photcode)) continue;
+      }
+    }
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    t = TimeValue (image[i].tzero, TimeReference, TimeFormat);
+    if (!strcmp(&image[i].coords.ctype[4], "-DIS")) {
+      XY_to_RD (&r, &d, 0.0, 0.0, &image[i].coords);
+    } else {
+      XY_to_RD (&r, &d, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+    }
+    gprint (GP_LOG, "%3d %s %8.4f %8.4f %f %5d %2d %4.2f %5.3f %5.3f\n", 
+	     i, image[i].name, r, d, t, image[i].nstar, image[i].photcode, image[i].secz, image[i].Mcal, image[i].dMcal);
+  }
+
+  free (subset);
+  free (image);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imphot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imphot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imphot.c	(revision 22322)
@@ -0,0 +1,98 @@
+# include "dvoshell.h"
+
+int imphot (int argc, char **argv) {
+  
+  time_t tzero;
+  double trange;
+  int N, GreyScale;
+  int i, j, Nimage, Nsubset, *subset;
+  char bufname[64];
+  float *p;
+  double fx, fy, x, y, applyMcal();
+  Image *image;
+  Buffer *buf;
+  SkyRegionSelection *selection;
+
+  GreyScale = FALSE;
+  if ((N = get_argument (argc, argv, "-g"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (bufname, argv[N]);
+    remove_argument (N, &argc, argv);
+    GreyScale = TRUE;
+  }
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: imphot tzero trange [-g buffer]\n");
+    return (FALSE);
+  }
+
+  buf = NULL;
+  if (GreyScale) {
+    if ((buf = SelectBuffer (bufname, ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    CreateBuffer (buf, 100, 200, -32, 0.0, 1.0);
+  }
+
+  /* load image(s) in time range given */
+  if (!ohana_str_to_time (argv[1], &tzero)) { 
+    gprint (GP_ERR, "syntax error\n");
+    return (FALSE);
+  }
+  if (!ohana_str_to_dtime (argv[2], &trange)) { 
+    gprint (GP_ERR, "syntax error\n");
+    return (FALSE);
+  }    
+  gprint (GP_ERR, "searching in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+  
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TRUE);
+
+  if ((Nsubset > 1) && GreyScale) {
+    gprint (GP_ERR, "more than one image selected, making GreyScale of first only\n");
+  }
+
+  if (GreyScale && Nsubset) {
+    fx = image[subset[0]].NX / 100;
+    fy = image[subset[0]].NY / 200;
+    p = (float *) buf[0].matrix.buffer;
+    for (y = 0; y < 200; y+=1.0) {
+      for (x = 0; x < 100; x+=1.0, p++) {
+	*p = applyMcal (&image[subset[0]], (fx*x), (fy*y));
+      }
+    }
+  }
+
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    switch (image[i].order) {
+    case 0:
+      gprint (GP_ERR, "%s: %d - %f\n", image[i].name, image[i].order, image[i].Mcal);
+      break;
+    case 1:
+      gprint (GP_ERR, "%s: %d - %f, %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My);
+      break;
+    case 2:
+      gprint (GP_ERR, "%s: %d - %f, %d %d, %d %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My, image[i].Mxx, image[i].Mxy, image[i].Myy);
+      break;
+    case 3:
+      gprint (GP_ERR, "%s: %d - %f, %d %d, %d %d %d, %d %d %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My, 
+	       image[i].Mxx, image[i].Mxy, image[i].Myy, image[i].Mxxx, image[i].Mxxy, image[i].Mxyy, image[i].Myyy);
+      break;
+    case 4:
+      gprint (GP_ERR, "%s: %d - %f, %d %d, %d %d %d, %d %d %d %d, %d %d %d %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My, 
+	       image[i].Mxx, image[i].Mxy, image[i].Myy, image[i].Mxxx, image[i].Mxxy, image[i].Mxyy, image[i].Myyy,
+	       image[i].Mxxxx, image[i].Mxxxy, image[i].Mxxyy, image[i].Mxyyy, image[i].Myyyy);
+      break;
+    }
+  }
+
+  free (image);
+  free (subset);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imrough.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imrough.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imrough.c	(revision 22322)
@@ -0,0 +1,310 @@
+# include "dvoshell.h"
+RegImage *load_imreg (char *DataBase, int *nimage);
+
+# define NVALUE 22
+enum {V_NONE, V_EXPTIME, V_CCDN, V_SKY, V_BIAS, V_FILTER, V_FWHM, V_AIRM, V_TIME, V_TEMP0, V_TEMP1, V_TEMP2, V_TEMP3, V_TELFOCUS, V_XPROBE, V_YPROBE, V_ZPROBE, V_RA, V_DEC, V_DETTEMP, V_ROTANGLE, V_REGTIME};
+static char valuename[NVALUE][32] = {"none", "exptime", "ccd", "sky", "bias", "filter", "fwhm", "airmass", "time", "temp0", "temp1", "temp2", "temp3", "telfocus", "xprobe", "yprobe", "zprobe", "ra", "dec", "dettemp", "rotangle", "regtime"};
+
+int imrough (int argc, char **argv) {
+ 
+  int i, N, Nimage, TimeSelect;
+  int ModeSelect, TypeSelect, CCDSelect, FilterSelect, TimeFormat;
+  int type, value, mode, CCD, NVEC;
+  char DataBase[256], *Filter;
+  double trange;
+  float *Vec;
+  time_t tzero, tend, TimeReference;
+  RegImage *image;
+  Vector *vec;
+
+  VarConfig ("REGISTRATION_DATABASE", "%s", DataBase);
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  type = T_UNDEF;
+  TypeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    TypeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    type = get_image_type (argv[N]);
+    if (type == T_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image type %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  mode = M_NONE;
+  ModeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-mode"))) {
+    ModeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    mode = get_image_mode (argv[N]);
+    if (mode == M_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image mode %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  CCD = 0;
+  CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    CCD = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    CCDSelect = TRUE;
+  }
+ 
+  Filter = NULL;
+  FilterSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    Filter = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    FilterSelect = TRUE;
+    if (!strcasecmp (Filter, "X")) {
+      FilterSelect = FALSE;
+    }
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imrough (value)\n");
+    gprint (GP_ERR, "       value options:\n");
+    for (i = 1; i < NVALUE; i++) {
+      gprint (GP_ERR, "%s\n", valuename[i]);
+    }
+    return (FALSE);
+  }
+ 
+  /* identify selection */
+  value = V_NONE;
+  for (i = 0; (i < NVALUE) && (value == V_NONE); i++) {
+    if (!strncasecmp (argv[1], valuename[i], strlen(argv[1]))) value = i;
+  }
+  if (value == V_NONE) {
+    gprint (GP_ERR, "ERROR: invalid image value %s\n", argv[1]);
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  image = load_imreg (DataBase, &Nimage);
+  if (image == (RegImage *) NULL) return (FALSE);
+
+  N = 0;
+  NVEC = 1000;
+  REALLOCATE (vec[0].elements, float, NVEC);
+  Vec = vec[0].elements;
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  /* get data */
+  for (i = 0; i < Nimage; i++) {
+    /* skip unmatched selections */
+    if (TimeSelect && ((image[i].obstime < tzero) || (image[i].obstime > tzero + trange))) continue;
+    if (TimeSelect && ((image[i].obstime < tzero) || (image[i].obstime > tzero + trange))) continue;
+    if (FilterSelect && (strcasecmp (image[i].filter, Filter))) continue;
+    if (CCDSelect && (image[i].ccd != CCD)) continue;
+    if (TypeSelect && (image[i].type != type)) continue;
+    if (ModeSelect && (image[i].mode != mode)) continue;
+
+    /* assign correct value */
+    switch (value) {
+    case (V_EXPTIME):
+      Vec[N] = image[i].exptime;
+      break;
+    case (V_CCDN):
+      Vec[N] = image[i].ccd;
+      break;
+    case (V_SKY):
+      Vec[N] = image[i].sky;
+      break;
+    case (V_BIAS):
+      Vec[N] = image[i].bias;
+      break;
+    case (V_FILTER):
+      Vec[N] = image[i].filter[0];
+      break;
+    case (V_FWHM):
+      Vec[N] = image[i].fwhm;
+      break;
+    case (V_AIRM):
+      Vec[N] = image[i].airmass;
+      break;
+    case (V_TIME):
+      Vec[N] = TimeValue (image[i].obstime, TimeReference, TimeFormat);
+      break;
+    case (V_TEMP0):
+      Vec[N] = image[i].teltemp_0;
+      break;
+    case (V_TEMP1):
+      Vec[N] = image[i].teltemp_1;
+      break;
+    case (V_TEMP2):
+      Vec[N] = image[i].teltemp_2;
+      break;
+    case (V_TEMP3):
+      Vec[N] = image[i].teltemp_3;
+      break;
+    case (V_TELFOCUS):
+      Vec[N] = image[i].telfocus;
+      break;
+    case (V_XPROBE):
+      Vec[N] = image[i].xprobe;
+      break;
+    case (V_YPROBE):
+      Vec[N] = image[i].yprobe;
+      break;
+    case (V_ZPROBE):
+      Vec[N] = image[i].zprobe;
+      break;
+    case (V_RA):
+      Vec[N] = image[i].ra;
+      break;
+    case (V_DEC):
+      Vec[N] = image[i].dec;
+      break;
+    case (V_DETTEMP):
+      Vec[N] = image[i].dettemp;
+      break;
+    case (V_ROTANGLE):
+      Vec[N] = image[i].rotangle;
+      break;
+    case (V_REGTIME):
+      Vec[N] = TimeValue (image[i].regtime, TimeReference, TimeFormat);
+      break;
+    }
+    N++;
+    if (N >= NVEC - 1) {
+      NVEC += 1000;
+      REALLOCATE (vec[0].elements, float, NVEC);
+      Vec = vec[0].elements;
+    }
+  }
+
+  REALLOCATE (vec[0].elements, float, MAX (1,N));
+  vec[0].Nelements = N;
+
+  free (image);
+  return (TRUE);
+
+}
+
+RegImage *load_imreg (char *DataBase, int *nimage) {
+
+  int Nimage, status;
+  char line[80];
+  FILE *f;
+  Header header, theader;
+  Matrix matrix;
+  FTable table;
+  RegImage *image;
+
+  *nimage = 0;
+
+  /* open database */
+  f = fopen (DataBase, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't open Registration Database\n");
+    return ((RegImage *) NULL);
+  }
+
+  /* load in database header */
+  if (!gfits_fread_header (f, &header)) {
+    fclose (f);
+    gfits_free_header (&header);
+    gprint (GP_ERR, "ERROR: trouble reading database header\n");
+    return ((RegImage *) NULL);
+  }
+
+  /* check for database v1, v2 */
+  gfits_scan (&header, "ORIGIN", "%s", 1, line);
+  if (!strcmp (line, "MDM Observatory")) {
+
+    fseek (f, header.size, SEEK_SET);
+    
+    /* load existing data from database */
+    gfits_scan (&header, "NIMAGES", "%d", 1, &Nimage);
+    ALLOCATE (image, RegImage, Nimage);
+    status = fread (image, sizeof(RegImage), Nimage, f);
+    fclose (f);
+    
+    if (status != Nimage) {
+      gprint (GP_ERR, "ERROR: header and data in dB don't match (%d vs %d)\n", Nimage, status);
+      gfits_free_header (&header);
+      free (image);
+      return ((RegImage *) NULL);
+    }
+    gfits_convert_RegImage (image, sizeof (RegImage), Nimage);
+
+    *nimage = Nimage;
+    return (image);
+  }
+
+  /* we probably have v3 */
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    fclose (f);
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    gprint (GP_ERR, "ERROR: trouble reading database matrix\n");
+    return ((RegImage *) NULL);
+  }
+
+  table.header = &theader;
+  if (!gfits_fread_ftable  (f, &table, "IMAGE_DATABASE")) {
+    fclose (f);
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    gprint (GP_ERR, "ERROR: trouble reading database table\n");
+    return ((RegImage *) NULL);
+  }
+
+  /* convert to internal format */
+  image = (RegImage *) table.buffer;
+  gfits_scan (table.header, "NAXIS2", "%d", 1, &Nimage);
+  gfits_convert_RegImage (image, sizeof (RegImage), Nimage);
+
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+
+  *nimage = Nimage;
+  return (image);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imsearch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imsearch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imsearch.c	(revision 22322)
@@ -0,0 +1,154 @@
+# include "dvoshell.h"
+
+int imsearch (int argc, char **argv) {
+ 
+  char DataBase[256], name[64];
+  FILE *f;
+  Header header;
+  RegImage *pimage;
+  int i, Nimage, status, N, TimeSelect, SaveNames;
+  int ModeSelect, TypeSelect, CCDSelect, FilterSelect;
+  char *Filter, *obstime;
+  int Type, Mode, CCD;
+  time_t tzero, obstime_sec;
+  double trange;
+   
+  VarConfig ("REGISTRATION_DATABASE", "%s", DataBase);
+
+  SaveNames = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    SaveNames = TRUE;
+  }
+ 
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+ 
+  Type = 0;
+  TypeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    Type = get_image_type (argv[N]);
+    if (Type == T_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image type %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TypeSelect = TRUE;
+  }
+ 
+  Mode = 0;
+  ModeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-mode"))) {
+    remove_argument (N, &argc, argv);
+    Mode = get_image_mode (argv[N]);
+    if (Mode == M_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image mode %s\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    ModeSelect = TRUE;
+  }
+ 
+  CCD = 0;
+  CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    CCD = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    CCDSelect = TRUE;
+  }
+ 
+  Filter = NULL;
+  FilterSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    Filter = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    FilterSelect = TRUE;
+    if (!strcasecmp (Filter, "X")) {
+      FilterSelect = FALSE;
+    }
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: imsearch [-time start range] [-type type] [-mode mode] [-ccd N] [-filter name]\n");
+    exit (1);
+  }
+
+  /* load in database header */
+  if (!gfits_read_header (DataBase, &header)) {
+    gprint (GP_ERR, "ERROR: trouble reading database header\n");
+    return (FALSE);
+  }
+
+  /* open database */
+  f = fopen (DataBase, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't open Registration Database\n");
+    return (FALSE);
+  }
+  fseek (f, header.size, SEEK_SET);
+
+  /* load existing data from database */
+  gfits_scan (&header, "NIMAGES", "%d", 1, &Nimage);
+  ALLOCATE (pimage, RegImage, Nimage);
+  status = fread (pimage, sizeof(RegImage), Nimage, f);
+  fclose (f);
+  if (status != Nimage) {
+    gprint (GP_ERR, "ERROR: header and data in dB don't match (%d vs %d)\n", Nimage, status);
+    gfits_free_header (&header);
+    free (pimage);
+    return (FALSE);
+  }
+  gfits_convert_RegImage (pimage, sizeof (RegImage), Nimage);
+
+  /* print out all data */
+  N = 0;
+  for (i = 0; i < Nimage; i++) {
+    if (TimeSelect && ((pimage[i].obstime < tzero) || (pimage[i].obstime > tzero + trange))) continue;
+    if (FilterSelect && (strcasecmp (pimage[i].filter, Filter))) continue;
+    if (ModeSelect && (pimage[i].mode != Mode)) continue;
+    if (CCDSelect && (pimage[i].ccd != CCD)) continue;
+    if (TypeSelect && (pimage[i].type != Type)) continue;
+
+    obstime_sec = (time_t) pimage[i].obstime;
+    obstime = ctime (&obstime_sec);
+    obstime[strlen(obstime)-1] = 0;
+
+    gprint (GP_LOG, "%5d %6s %6s %2d %2d   ", i, get_type_name(pimage[i].type), get_mode_name(pimage[i].mode), pimage[i].ccd, pimage[i].type);
+    gprint (GP_LOG, "%s %s  ", pimage[i].pathname, pimage[i].filename);
+    gprint (GP_LOG, "%s %s %f %s\n", pimage[i].filter, pimage[i].instrument, pimage[i].exptime, obstime);
+
+    if (SaveNames) {
+      sprintf (name, "IMAGEpath:%d", N);
+      set_str_variable (name, pimage[i].pathname);
+      sprintf (name, "IMAGEfile:%d", N);
+      set_str_variable (name, pimage[i].filename);
+      sprintf (name, "IMAGEmode:%d", N);
+      set_int_variable (name, pimage[i].mode);
+    }
+    N++;
+  }
+  if (SaveNames) {
+    set_int_variable ("IMAGEpath:n", N);
+    set_int_variable ("IMAGEfile:n", N);
+  }
+
+  free (pimage);
+  gfits_free_header (&header);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imstats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imstats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/imstats.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "dvoshell.h"
+
+int imstats (int argc, char **argv) {
+  
+  int i, kapa, Nimage, N;
+  int Mcal, AutoLimits;
+  double r, d;
+  Image *image;
+  Vector Xvec, Yvec;  
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  Mcal = TRUE;
+  if ((N = get_argument (argc, argv, "-dM"))) {
+    remove_argument (N, &argc, argv);
+    Mcal = FALSE;
+  }
+
+  AutoLimits = FALSE;
+  if ((N = get_argument (argc, argv, "-l"))) {
+    remove_argument (N, &argc, argv);
+    AutoLimits = TRUE;
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: imstats [-dM] [-l]\n");
+    return (FALSE);
+  }
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  /* assign vector values */
+  Xvec.Nelements = Nimage;
+  Yvec.Nelements = Nimage;
+  ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+  gprint (GP_LOG, "seq  ra (J2000) dec    time (s)   Nstars\n");
+  for (i = 0; i < Nimage; i++) {
+    Xvec.elements[i] = image[i].secz;
+    if (Mcal) 
+      Yvec.elements[i] = image[i].Mcal;
+    else 
+      Yvec.elements[i] = image[i].dMcal;
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    XY_to_RD (&r, &d, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+    gprint (GP_ERR, "%d %8.4f %8.4f %10d %6d  %5.3f %6.3f %6.3f\n", 
+	     i, r, d, image[i].tzero, image[i].nstar, Xvec.elements[i], 
+	     image[i].Mcal, image[i].dMcal);
+  } 
+  if (AutoLimits) SetLimits (&Xvec, &Yvec, &graphmode);
+
+  graphmode.style = 2;
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, Nimage, Xvec.elements, Yvec.elements, &graphmode);
+  
+  free (Xvec.elements);
+  free (Yvec.elements);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/init.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/init.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/init.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "dvoshell.h"
+
+int avextract       PROTO((int, char **));
+int badimages       PROTO((int, char **));
+int calextract      PROTO((int, char **));
+int calmextract     PROTO((int, char **));
+int catlog          PROTO((int, char **));
+int catdir_define   PROTO((int, char **));
+int ccd             PROTO((int, char **));
+int cmatch          PROTO((int, char **));
+int cmd             PROTO((int, char **));
+int cmpload         PROTO((int, char **));
+int cmpread         PROTO((int, char **));
+int ddmags          PROTO((int, char **));
+int detrend         PROTO((int, char **));
+int dmagaves        PROTO((int, char **));
+int dmagmeas        PROTO((int, char **));
+int dmags           PROTO((int, char **));
+int dmt             PROTO((int, char **));
+int elixir          PROTO((int, char **));
+int fitcolors       PROTO((int, char **));
+int fitsed          PROTO((int, char **));
+int gcat            PROTO((int, char **));
+int getxtra         PROTO((int, char **));
+int gimages         PROTO((int, char **));
+int gstar           PROTO((int, char **));
+int gtypes          PROTO((int, char **));
+int images          PROTO((int, char **));
+int imbox           PROTO((int, char **));
+int imdata          PROTO((int, char **));
+int imdense         PROTO((int, char **));
+int imextract       PROTO((int, char **));
+int imlist          PROTO((int, char **));
+int imphot          PROTO((int, char **));
+int imrough         PROTO((int, char **));
+int imsearch        PROTO((int, char **));
+int imstats         PROTO((int, char **));
+int lcat            PROTO((int, char **));
+int lcurve          PROTO((int, char **));
+int lightcurve      PROTO((int, char **));
+int mextract        PROTO((int, char **));
+int mmextract        PROTO((int, char **));
+int pcat            PROTO((int, char **));
+int photcodes       PROTO((int, char **));
+int pmeasure        PROTO((int, char **));
+int paverage        PROTO((int, char **));
+int procks          PROTO((int, char **));
+int showtile        PROTO((int, char **));
+int skycat          PROTO((int, char **));
+int skycoverage     PROTO((int, char **));
+int skyregion       PROTO((int, char **));
+int simage          PROTO((int, char **));
+int subpix          PROTO((int, char **));
+int version         PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "avextract",   avextract,    "extract average data values"},
+  {1, "badimages",   badimages,    "look for images with anomalous astrometry"},
+  {1, "calextract",  calextract,   "extract photometry calibration"},
+  {1, "calmextract", calmextract,  "extract photometry calibration"},
+  {1, "catdir",      catdir_define,"re-define CATDIR"},
+  {1, "ccd",         ccd,          "plot color-color diagram"},
+  {1, "cmatch",      cmatch,       "match two catalogs"},
+  {1, "cmd",         cmd,          "plot cmd of stars in current region"},
+  {1, "cmpload",     cmpload,      "load cmp file into ?"},
+  {1, "cmpread",     cmpread,      "read data from cmp format files"},
+  {1, "ddmags",      ddmags,       "plot magnitude differences"},
+  {1, "detrend",     detrend,      "extract from detrend database?"},
+  {1, "dmagaves",    dmagaves,     "foo"},
+  {1, "dmagmeas",    dmagmeas,     "foo"},
+  {1, "dmags",       dmags,        "plot differential magnitudes between filters"},
+  {1, "dmt",         dmt,          "plot mag scatter"},
+  {1, "elixir",      elixir,       "talk to elixir"},
+  {1, "fitcolors",   fitcolors,    "fit chip-to-chip color terms"},
+  {1, "fitsed",      fitsed,       "fit stellar SEDs to objects"},
+  {1, "gcat",        gcat,         "get catalog at location"},
+  {1, "gimages",     gimages,      "get images at location"},
+  {1, "gstar",       gstar,        "get star statistics"},
+  {1, "images",      images,       "plot image boxes"},
+  {1, "imbox",       imbox,        "plot expected image box"},
+  {1, "imdata",      imdata,       "extract data for specific images"},
+  {1, "imdense",     imdense,      "image density plot"},
+  {1, "imextract",   imextract,    "extract vectors from catalogs"},
+  {1, "imlist",      imlist,       "list image info"},
+  {1, "imphot",      imphot,       "image photometry info"},
+  {1, "imrough",     imrough,      "get info from imruf database"},
+  {1, "imsearch",    imsearch,     "get info from imreg database"},
+  {1, "imstats",     imstats,      "plot image statistics"},
+  {1, "lcat",        lcat,         "list catalogs in region"},
+  {1, "lcurve",      lcurve,       "plot lightcurve for a star"},
+  {1, "lightcurve",  lightcurve,   "extract lightcurve for a star"},
+  {1, "mextract",    mextract,     "extract measure data values"},
+  {1, "mmextract",   mmextract,    "extract joined measurements"},
+  {1, "pcat",        skycat,       "plot catalog boundaries"},
+  {1, "photcodes",   photcodes,    "list photometry codes"},
+  {1, "pmeasure",    pmeasure,     "plot individual measurements"},
+  {1, "paverage",    paverage,     "plot average magnitude"},
+  {1, "procks",      procks,       "plot rocks"},
+  {1, "showtile",    showtile,     "plot tile pattern"},
+  {1, "skycat",      skycat,       "show sky catalog boundaries"},
+  {1, "skycoverage", skycoverage,  "measure image union on sky"},
+  {1, "skyregion",   skyregion,    "set sky region for db queries"},
+  {1, "simage",      simage,       "plot stars in an image"},
+  {1, "subpix",      subpix,       "get subpixel positions"},
+  {1, "version",     version,      "show version information"},
+//{1, "addxtra",     addxtra,      "add extra data to object"},
+//{1, "getxtra",     getxtra,      "get extra data from object"},
+}; 
+
+/* move to astro */
+
+void InitDVO () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreeDVO () {
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/lcat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/lcat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/lcat.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "dvoshell.h"
+
+int lcat (int argc, char **argv) {
+  
+  double Radius;
+  int i, N, ShowAll;
+  char exists;
+  struct stat filestat;
+  Graphdata graphmode;
+  SkyTable *sky;
+  SkyList *skylist;
+
+  if (!GetGraphData (&graphmode, NULL, NULL)) return (FALSE);
+
+  ShowAll = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    ShowAll = TRUE;
+  }
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: lcat [-all]\n");
+    return (FALSE);
+  }
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    exists = 'Y';
+    if (stat (skylist[0].filename[i], &filestat) == -1) exists = 'N';
+    if (ShowAll) {
+      gprint (GP_ERR, "%3d %s  %c\n", i, skylist[0].regions[i][0].name, exists);
+    } else {
+      if (exists == 'Y') {
+	gprint (GP_ERR, "%3d %s\n", i, skylist[0].regions[i][0].name);
+      }
+    }
+  }
+
+  SkyListFree (skylist);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/lcurve.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/lcurve.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/lcurve.c	(revision 22322)
@@ -0,0 +1,192 @@
+# include "dvoshell.h"
+
+int lcurve (int argc, char **argv) {
+  
+  char string[128], *p;
+  double Ra, Dec, Radius, Radius2, r;
+  double *RA, *DEC;
+  int kapa, TimeFormat;
+  int Nstars, found, AutoLimits, ErrorBars, GalMag, AbsPhot, SaveVectors;
+  int i, j, m, N, NPTS, *N1;
+  time_t TimeReference;
+  struct tm *timeptr;
+  Vector *xvec, *yvec;
+  Vector Xvec, Yvec, dYvec;
+  Catalog catalog;
+  Graphdata graphmode;
+  SkyTable *sky;
+  SkyList *skylist;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  AutoLimits = FALSE;
+  if ((N = get_argument (argc, argv, "-l"))) {
+    remove_argument (N, &argc, argv);
+    AutoLimits = TRUE;
+  }
+
+  xvec = yvec = NULL;
+  SaveVectors = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    SaveVectors = TRUE;
+    if ((xvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+    if ((yvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  AbsPhot = FALSE;
+  if ((N = get_argument (argc, argv, "-abs"))) {
+    remove_argument (N, &argc, argv);
+    AbsPhot = TRUE;
+  }
+
+  GalMag = FALSE;
+  if ((N = get_argument (argc, argv, "-gal"))) {
+    gprint (GP_ERR, "galaxy magnitudes currently disabled\n");
+    return (FALSE);
+  }
+
+  ErrorBars = FALSE;
+  if ((N = get_argument (argc, argv, "-d"))) {
+    remove_argument (N, &argc, argv);
+    ErrorBars = TRUE;
+  }
+
+  if (argc < 4) {
+    gprint (GP_ERR, "USAGE: lcurve RA DEC Radius\n");
+    return (FALSE);
+  }
+  
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  Radius = atof (argv[3]);
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* set filename, read in header */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (N1, int, Nstars);
+
+  /* find star(s) in RA, DEC list -- use a dumb algorithm for now, improve later */
+  /* stars are not guaranteed to be sorted in RA or in DEC, so first sort the list */
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    N1[i] = i;
+  }
+  /* sort list by DEC */
+  if (Nstars > 1) sort_coords_index (DEC, RA, N1, Nstars);
+  /* at this point, RA, DEC, and N1 are sorted by DEC.  
+     catalog.average[N1[i]].R = RA[i] */
+
+  NPTS = 100;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  dYvec.elements = NULL;
+  if (ErrorBars) { ALLOCATE (dYvec.elements, float, NPTS); }
+  N = 0;
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Radius2 = Radius*Radius;
+  found = FALSE;
+  for (i = 0; (i < catalog.Naverage) && !found; i++) {
+
+    /* this can be improved by using a couple of jumps to get within range */
+    if (Dec > DEC[i] + Radius)
+      continue;
+    
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      /* found star, extract measurements */
+      m = catalog.average[N1[i]].measureOffset;
+      for (j = 0; j < catalog.average[N1[i]].Nmeasure; j++, m++) {
+	if (ErrorBars) dYvec.elements[N] = catalog.measure[m].dM;
+	Xvec.elements[N] = TimeValue (catalog.measure[m].t, TimeReference, TimeFormat);
+	Yvec.elements[N] = PhotCat (&catalog.measure[m]);
+	/**** need to use PhotRel optionally here ****/
+	N++; 
+	if (N == NPTS) {
+	  NPTS += 100;
+	  REALLOCATE (Xvec.elements, float, NPTS);
+	  REALLOCATE (Yvec.elements, float, NPTS);
+	  if (ErrorBars) { REALLOCATE (dYvec.elements, float, NPTS); }
+	}
+      }      
+    }
+  }
+  Xvec.Nelements = Yvec.Nelements = N;
+  if (ErrorBars) dYvec.Nelements = N;
+  
+  if (ErrorBars)
+    fsortthree (Xvec.elements, Yvec.elements, dYvec.elements, N);
+  else
+    fsortpair (Xvec.elements, Yvec.elements, N);
+
+  /* autoscale the plot */
+  if (AutoLimits) SetLimits (&Xvec, &Yvec, &graphmode);
+
+  if (ErrorBars) 
+    graphmode.etype = 1;  /* y errors only in lcurves */
+  else
+    graphmode.etype = 0;  
+
+  KapaPrepPlot (kapa, N, &graphmode);
+  KapaPlotVector (kapa, N, Xvec.elements, "x");
+  KapaPlotVector (kapa, N, Yvec.elements, "y");
+  if (ErrorBars) {
+    KapaPlotVector (kapa, N, dYvec.elements, "dym");
+    KapaPlotVector (kapa, N, dYvec.elements, "dyp");
+  }
+
+  timeptr = gmtime ((time_t *)&TimeReference);
+
+  if ((p = get_variable ("TIMEFORMAT")) == (char *) NULL) p = strcreate ("days");
+  sprintf (string, "%s since %02d/%02d/%02d UT", p,
+	   timeptr[0].tm_year, timeptr[0].tm_mon+1, timeptr[0].tm_mday);
+  free (p);
+  KapaSendLabel (kapa, string, 0);
+
+  free (RA);
+  free (DEC);
+  free (N1);
+
+  if (SaveVectors) {
+    free (xvec[0].elements);
+    free (yvec[0].elements);
+    xvec[0].elements = Xvec.elements;
+    yvec[0].elements = Yvec.elements;
+    xvec[0].Nelements = yvec[0].Nelements = Xvec.Nelements;
+  } else {
+    free (Xvec.elements);
+    free (Yvec.elements);
+  }
+
+  if (ErrorBars) free (dYvec.elements);
+  dvo_catalog_free (&catalog);
+
+  SkyListFree (skylist);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/lightcurve.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/lightcurve.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/lightcurve.c	(revision 22322)
@@ -0,0 +1,145 @@
+# include "dvoshell.h"
+
+int lightcurve (int argc, char **argv) {
+  
+  double Ra, Dec, Radius, Radius2, r;
+  double *RA, *DEC;
+  int Nstars, found, PhotCodeSelect;
+  int i, j, k, m, N, NPTS, Nsecfilt, RELPHOT, *N1, TimeFormat;
+  time_t TimeReference;
+
+  PhotCode *code;
+  Catalog catalog;
+  SkyTable *sky;
+  SkyList *skylist;
+  Vector *tvec, *mvec, *dmvec;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  if ((tvec = SelectVector ("tc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((mvec = SelectVector ("mc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((dmvec = SelectVector ("dmc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  RELPHOT = FALSE;
+  if ((N = get_argument (argc, argv, "-rel"))) {
+    remove_argument (N, &argc, argv);
+    RELPHOT = TRUE;
+  }
+
+  code = NULL;
+  PhotCodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    PhotCodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 4) {
+    gprint (GP_ERR, "USAGE: lightcurve RA DEC Radius\n");
+    return (FALSE);
+  }
+  
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  Radius = atof (argv[3]);
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* set filename, read in header */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (N1, int, Nstars);
+
+  /* find star(s) in RA, DEC list -- use a dumb algorithm for now, improve later */
+  /* stars are not guaranteed to be sorted in RA or in DEC, so first sort the list */
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    N1[i] = i;
+  }
+  /* sort list by DEC */
+  if (Nstars > 1) sort_coords_index (DEC, RA, N1, Nstars);
+  /* at this point, RA, DEC, and N1 are sorted by DEC.  
+     catalog.average[N1[i]].R = RA[i] */
+
+  N = 0;
+  NPTS = 100;
+  REALLOCATE (tvec[0].elements, float, NPTS);
+  REALLOCATE (mvec[0].elements, float, NPTS);
+  REALLOCATE (dmvec[0].elements, float, NPTS);
+  
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Radius2 = Radius*Radius;
+  found = FALSE;
+  for (i = 0; (i < catalog.Naverage) && !found; i++) {
+
+    /* this can be improved by using a couple of jumps to get within range */
+    if (Dec > DEC[i] + Radius)
+      continue;
+    
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      k = N1[i];
+      /* found star, extract measurements */
+      m = catalog.average[k].measureOffset;
+      for (j = 0; j < catalog.average[k].Nmeasure; j++, m++) {
+
+	if (PhotCodeSelect) {
+	  if ((code[0].type == PHOT_REF) || (code[0].type == PHOT_DEP)) {
+	    if (code[0].code != catalog.measure[m].photcode) continue;
+	  } 
+	  if (code[0].type == PHOT_SEC) {
+	    if (code[0].code != GetPhotcodeEquivCodebyCode (catalog.measure[m].photcode)) continue;
+	  } 
+	}      
+
+	tvec[0].elements[N] = TimeValue (catalog.measure[m].t, TimeReference, TimeFormat);
+	dmvec[0].elements[N] = catalog.measure[m].dM;
+	if (RELPHOT) {
+	  mvec[0].elements[N] = PhotCat (&catalog.measure[m]);
+	} else {
+	  mvec[0].elements[N] = PhotRel (&catalog.measure[m], &catalog.average[k], &catalog.secfilt[k*Nsecfilt]);
+	}
+	N++; 
+	if (N == NPTS) {
+	  NPTS += 100;
+	  REALLOCATE (tvec[0].elements, float, NPTS);
+	  REALLOCATE (mvec[0].elements, float, NPTS);
+	  REALLOCATE (dmvec[0].elements, float, NPTS);
+	}
+      }      
+    }
+  }
+  fsortthree (tvec[0].elements, mvec[0].elements, dmvec[0].elements, N);
+  tvec[0].Nelements = mvec[0].Nelements = dmvec[0].Nelements = N;
+
+  free (RA);
+  free (DEC);
+  free (N1);
+  dvo_catalog_free (&catalog);
+  SkyListFree (skylist);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/match_image.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/match_image.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/match_image.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "dvoshell.h"
+
+int match_image (Image *image, int Nimage, unsigned int T, short int S) {
+
+  int N, Nlo, Nhi, N1, N2;
+
+  /* bracket first value of interest */
+  Nlo = 0; Nhi = Nimage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[N].tzero < T) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  N1 = Nlo;
+
+  /* bracket last value of interest */
+  Nlo = 0; Nhi = Nimage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[N].tzero > T) {
+      Nhi = N;
+    } else {
+      Nlo = N - 1;
+    }
+  }
+  N2 = Nhi;
+
+  for (N = N1; N < N2; N++) {
+    if ((image[N].tzero == T) && (image[N].photcode == S)) {
+      return (N);
+    }
+  }
+  return (-1);
+}
+
+int match_image_subset (Image *image, int *subset, int Nsubset, unsigned int T, short int S) {
+
+  int N, Nlo, Nhi, N1, N2;
+
+  /* bracket first value of interest */
+  Nlo = 0; Nhi = Nsubset;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[subset[N]].tzero < T) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  N1 = Nlo;
+
+  /* bracket last value of interest */
+  Nlo = 0; Nhi = Nsubset;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[subset[N]].tzero > T) {
+      Nhi = N;
+    } else {
+      Nlo = N - 1;
+    }
+  }
+  N2 = Nhi;
+
+  for (N = N1; N < N2; N++) {
+    if ((image[subset[N]].tzero == T) && (image[subset[N]].photcode == S)) {
+      return (subset[N]);
+    }
+  }
+  return (-1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/mextract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/mextract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/mextract.c	(revision 22322)
@@ -0,0 +1,239 @@
+# include "dvoshell.h"
+
+int mextract (int argc, char **argv) {
+  
+  int i, j, k, m, n, N, Npts, NPTS, last, next, state, Nfields, Nreturn, Ncstack, Nstack;
+  int Nsecfilt, VERBOSE, loadImages, mosaicMode;
+  char **cstack, name[1024];
+  float *values;
+  void *Signal;
+
+  Catalog catalog;
+  SkyList *skylist;
+  PhotCode *code;
+  Vector **vec;
+  dbField *fields;
+  dbStack *stack;
+  SkyRegionSelection *selection;
+
+  /* defaults */
+  skylist = NULL;
+  code = NULL;
+  fields = NULL;
+  stack = NULL;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  
+  // init locally static variables (time refs)
+  dbExtractMeasuresInit();
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+
+  // command-line is of the form: avextract field,field, field [where (field op value)...]
+
+  // parse the fields to be extracted and returned
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_MEASURE, &last, &Nfields);
+  if (fields == NULL) return (FALSE);
+
+  // examine line for 'where' or 'match to'.  'match to' is forbidden
+  state = dbCmdlineConditions (argc, argv, last, &next);
+  if (state == DVO_DB_CMDLINE_ERROR) goto escape;
+  if (state == DVO_DB_CMDLINE_IS_MATCH) goto escape; // not allowed for mextract
+
+  // parse the remainder of the line as a boolean math expression
+  cstack = isolate_elements (argc-next, &argv[next], &Ncstack);
+  
+  // construct the db Boolean math stack (frees cstack)
+  stack = dbRPN (Ncstack, cstack, &Nstack);
+  if ((Ncstack > 0) && (Nstack < 1)) {
+    print_error ();
+    goto escape;
+  }
+
+  // add the skyregion limits to the where statement (or create)
+  dbAstroRegionLimits (&stack, &Nstack, selection, DVO_TABLE_MEASURE);
+
+  // parse stack elements into fields and scalars as needed
+  Nreturn = Nfields; 
+  dbCheckStack (stack, Nstack, DVO_TABLE_MEASURE, &fields, &Nfields);
+  // XXX handle errors
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  // load image data if needed (for fields listed below)
+  loadImages = FALSE;
+  mosaicMode = FALSE;
+  for (i = 0; !loadImages && (i < Nfields); i++) {
+    if (fields[i].ID == MEAS_XCCD) loadImages = TRUE;
+    if (fields[i].ID == MEAS_YCCD) loadImages = TRUE;
+    if (fields[i].ID == MEAS_XMOSAIC) loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_YMOSAIC) loadImages = mosaicMode = TRUE;
+  }
+  if (loadImages && !SetImageSelection (mosaicMode, selection)) goto escape;
+
+  /* create storage vector */
+  ALLOCATE (values, float, Nfields);
+  ALLOCATE (vec, Vector *, Nreturn);
+  for (i = 0; i < Nreturn; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name, "v_%s", fields[i].name);
+    } else {
+      sprintf (name, "%s", fields[i].name);
+    }
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+  }
+
+  Npts = 0;
+  NPTS = 1;
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[i];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = Nsecfilt;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s (%d of %d)\n", catalog.filename, i, skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* XXX need to call dvo_catalog_chipcoords here passing the loaded images */
+
+    for (j = 0; (j < catalog.Naverage) && !interrupt; j++) {
+      m = catalog.average[j].measureOffset;
+      dbExtractMeasuresInitAve (); // reset counters for saved fields 
+
+      for (k = 0; (k < catalog.average[j].Nmeasure); k++, m++) {
+
+	// extract the relevant values for this measurement
+	dbExtractMeasuresInitMeas (); // reset counters for saved fields 
+	for (n = 0; n < Nfields; n++) {
+	  values[n] = dbExtractMeasures (&catalog.average[j], &catalog.secfilt[j*Nsecfilt], &catalog.measure[m], &fields[n]);
+	}
+	// fprintf (stderr, "object: ave: %f, cat: %f, averef %d\n", fields[n].name, values[2], values[3], catalog.measure[m].averef);
+
+	// test the conditional statement
+	if (!dbBooleanCond (stack, Nstack, values)) continue;
+	for (n = 0; n < Nreturn; n++) {
+	  vec[n][0].elements[Npts] = values[n];
+	  // fprintf (stderr, "keep : field: %s, value: %f\n", fields[n].name, values[n]);
+	}
+	Npts++;
+	if (Npts >= NPTS) {
+	  NPTS += 2000;
+	  for (n = 0; n < Nreturn; n++) {
+	    REALLOCATE (vec[n][0].elements, float, NPTS);
+	  }
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+    // dbStackAllocPrint ();
+    // dbStackAllocReset ();
+    // dbStackFreePrint ();
+    // dbStackFreeReset ();
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+
+  for (n = 0; n < Nreturn; n++) {
+    vec[n][0].Nelements = Npts;
+    REALLOCATE (vec[n][0].elements, float, MAX(1,Npts));
+  }
+
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  free (stack);
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+escape:
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  free (stack);
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: mextract field[,field,field...] where (expression)\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, " USAGE: avextract field[,field,field...] where (expression)\n");
+    gprint (GP_ERR, "  RA : right ascension (J2000) for detection\n");
+    gprint (GP_ERR, "  DEC : declination for detection\n");
+    gprint (GP_ERR, "  RA:ave : average right ascension (J2000) for object\n");
+    gprint (GP_ERR, "  DEC:ave : average declination for object\n");
+    gprint (GP_ERR, "  RA:err : ra scatter \n");
+    gprint (GP_ERR, "  DEC:err : dec scatter\n");
+    gprint (GP_ERR, "  uRA : proper motion in ra\n");
+    gprint (GP_ERR, "  uDEC : proper motion in dec\n");
+    gprint (GP_ERR, "  duRA : proper motion error in ra\n");
+    gprint (GP_ERR, "  duDEC : proper motion error in dec\n");
+    gprint (GP_ERR, "  PAR : parallax\n");
+    gprint (GP_ERR, "  dPAR : parallax error \n");
+    gprint (GP_ERR, "  nmeas : number of measurements\n");
+    gprint (GP_ERR, "  nmiss : number of non-detections\n");
+    gprint (GP_ERR, "  xp : positional chi-square\n");
+    gprint (GP_ERR, "  objflag : object flags\n");
+    gprint (GP_ERR, "  photcode:ave : average magnitude for photcode (or equivalent)\n");
+    gprint (GP_ERR, "  photcode:ref : reference magnitude system for photcode (or equivalent)\n");
+    gprint (GP_ERR, "  photcode:inst : instrumental magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:cat :  catalog magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:sys :  system magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:rel :  relative magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:cal :  calibrated magnitude for photcode \n");
+    gprint (GP_ERR, "  photcode:err : magnitude error for photcode\n");
+    gprint (GP_ERR, "  photcode:chisq : chi-square of magnitude fit\n");
+    gprint (GP_ERR, "  photcode:ncode : number of measurements in photcode\n");
+    gprint (GP_ERR, "  photcode:nphot : number of measurements used for average magnitude\n");
+    gprint (GP_ERR, "  airmass : airmass of detection\n");
+    gprint (GP_ERR, "  exptime : exposure time\n");
+    gprint (GP_ERR, "  photcode : photcode \n");
+    gprint (GP_ERR, "  time : time of exposure\n");
+    gprint (GP_ERR, "  dR : ra offset\n");
+    gprint (GP_ERR, "  dD : dec offset\n");
+    gprint (GP_ERR, "  fwhm : fwhm (average)\n");
+    gprint (GP_ERR, "  fwhm_maj : fwhm (major axis)\n");
+    gprint (GP_ERR, "  fwhm_min : fwhm (minor axis)\n");
+    gprint (GP_ERR, "  theta : position angle\n");
+    gprint (GP_ERR, "  flags : detection flags\n");
+    gprint (GP_ERR, "  xccd : ccd x position\n");
+    gprint (GP_ERR, "  yccd : ccd y position\n");
+    gprint (GP_ERR, "  xmosaic : mosaic x position\n");
+    gprint (GP_ERR, "  ymosaic : mosaic y position\n");
+    gprint (GP_ERR, "  xchip : chip x position\n");
+    gprint (GP_ERR, "  ychip : chip y position\n");
+    gprint (GP_ERR, "  xfpa : fpa x position\n");
+    gprint (GP_ERR, "  yfpa : fpa y position\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " mextract --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/mmextract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/mmextract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/mmextract.c	(revision 22322)
@@ -0,0 +1,348 @@
+# include "dvoshell.h"
+
+int mmextract (int argc, char **argv) {
+  
+  int i, j, k, m, n, N, Npts, NPTS, last, next, state;
+  int Nfields, Nreturn, Nreturn_base, Ncstack1, Ncstack2, Nstack1, Nstack2;
+  int Nwhere, Iwhere, Nmatch, Imatch, NTABLE, Nt1, Nt2, n1, n2;
+  int Nsecfilt, VERBOSE, loadImages, mosaicMode;
+  char **cstack1, **cstack2, name1[1024], name2[1024];
+  float *values, **table1, **table2;
+  void *Signal;
+
+  Catalog catalog;
+  SkyList *skylist;
+  PhotCode *code;
+  Vector **vec;
+  dbField *fields;
+  dbStack *stack1;
+  dbStack *stack2;
+  SkyRegionSelection *selection;
+
+  /* defaults */
+  skylist = NULL;
+  code = NULL;
+  fields = NULL;
+  stack1 = NULL;
+  stack2 = NULL;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  
+  // init locally static variables (time refs)
+  dbExtractMeasuresInit();
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+
+  // command-line is of the form: avextract field,field, field [where (field op value)...]
+  // mmextract [-noauto] field,field field where conditions matched to conditions
+  // mmextract ra,dec,mag where photcode equiv g matched to photcode == 2MASS_J
+  // mmextract -noauto ra,dec,mag where photcode equiv g matched to photcode == 2MASS_J
+  // objectID matched is implied
+  // -noauto means imageID is not matched
+
+  // parse the fields to be extracted and returned : last points to end, or first 'where' or 'matched'
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_MEASURE, &last, &Nfields);
+  if (fields == NULL) return (FALSE);
+
+  // examine line for 'where' and 'match to'.  neither is required, but order is fixed
+  state = dbCmdlineConditions (argc, argv, last, &next);
+  if (state == DVO_DB_CMDLINE_ERROR) goto escape;
+
+  if (state == DVO_DB_CMDLINE_IS_END) {
+      Nwhere = Nmatch = 0;
+      Iwhere = Imatch = 0;
+  }
+
+  if (state == DVO_DB_CMDLINE_IS_MATCH) {
+      Nwhere = 0;
+      Iwhere = 0;
+      Imatch = next;
+      Nmatch = argc - next;
+  }
+
+  if (state == DVO_DB_CMDLINE_IS_WHERE) {
+      Iwhere = next;
+      // find the end or the 'match to'
+      for (last = next; (last < argc) && strcasecmp (argv[last], "match"); last++);
+      state = dbCmdlineConditions (argc, argv, last, &next);
+      if (state == DVO_DB_CMDLINE_ERROR) goto escape;
+      if (state == DVO_DB_CMDLINE_IS_WHERE) goto escape;
+      if (state == DVO_DB_CMDLINE_IS_END) {
+	  Nwhere = argc - Iwhere;
+	  Imatch = Nmatch = 0;
+      } else {
+	  Nwhere = last - Iwhere;
+	  Imatch = next;
+	  Nmatch = argc - Imatch;
+      }
+  }
+
+  // parse the 'where' and 'matched to' segments of the line as boolean math expressions
+  cstack1 = isolate_elements (Nwhere, &argv[Iwhere], &Ncstack1);
+  cstack2 = isolate_elements (Nmatch, &argv[Imatch], &Ncstack2);
+  
+  // construct the db Boolean math stack (frees cstack)
+  stack1 = dbRPN (Ncstack1, cstack1, &Nstack1);
+  if ((Ncstack1 > 0) && (Nstack1 < 1)) {
+    print_error ();
+    goto escape;
+  }
+
+  // construct the db Boolean math stack (frees cstack)
+  stack2 = dbRPN (Ncstack2, cstack2, &Nstack2);
+  if ((Ncstack2 > 0) && (Nstack2 < 1)) {
+    print_error ();
+    goto escape;
+  }
+
+  // XXX disallow skyregion limits in the matched to expression?
+
+  // add the skyregion limits to the where statement (or create)
+  dbAstroRegionLimits (&stack1, &Nstack1, selection, DVO_TABLE_MEASURE);
+  dbAstroRegionLimits (&stack2, &Nstack2, selection, DVO_TABLE_MEASURE);
+
+  // parse stack elements into fields and scalars as needed
+  Nreturn_base = Nfields;
+  Nreturn = 2*Nfields; // we are returning fieldi_1, fieldi_2 for the selected fields
+
+  dbCheckStack (stack1, Nstack1, DVO_TABLE_MEASURE, &fields, &Nfields);
+  dbCheckStack (stack2, Nstack2, DVO_TABLE_MEASURE, &fields, &Nfields);
+  // XXX handle errors
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  // load image data if needed (for fields listed below)
+  loadImages = FALSE;
+  mosaicMode = FALSE;
+  for (i = 0; !loadImages && (i < Nfields); i++) {
+    if (fields[i].ID == MEAS_XCCD) loadImages = TRUE;
+    if (fields[i].ID == MEAS_YCCD) loadImages = TRUE;
+    if (fields[i].ID == MEAS_XMOSAIC) loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_YMOSAIC) loadImages = mosaicMode = TRUE;
+  }
+  if (loadImages && !SetImageSelection (mosaicMode, selection)) goto escape;
+
+  /* create storage vector */
+  ALLOCATE (values, float, Nfields);
+  ALLOCATE (vec, Vector *, Nreturn);
+
+  for (i = 0; i < Nreturn_base; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name1, "v_%s_1", fields[i].name);
+      sprintf (name2, "v_%s_2", fields[i].name);
+    } else {
+      sprintf (name1, "%s_1", fields[i].name);
+      sprintf (name2, "%s_2", fields[i].name);
+    }
+    if ((vec[2*i+0] = SelectVector (name1, ANYVECTOR, TRUE)) == NULL) goto escape;
+    if ((vec[2*i+1] = SelectVector (name2, ANYVECTOR, TRUE)) == NULL) goto escape;
+  }
+
+  Npts = 0;
+  NPTS = 1;
+
+  // we save the selected measures for each average to temporary tables 1 and 2
+  NTABLE = 100;
+  ALLOCATE (table1, float *, Nreturn_base);
+  ALLOCATE (table2, float *, Nreturn_base);
+  for (i = 0; i < Nreturn_base; i++) {
+    ALLOCATE (table1[i], float, NTABLE);
+    ALLOCATE (table2[i], float, NTABLE);
+  }
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[i];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = Nsecfilt;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s (%d of %d)\n", catalog.filename, i, skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* XXX need to call dvo_catalog_chipcoords here passing the loaded images */
+
+    for (j = 0; (j < catalog.Naverage) && !interrupt; j++) {
+      m = catalog.average[j].measureOffset;
+      dbExtractMeasuresInitAve (); // reset counters for saved fields 
+
+      // XXX check that we have space to keep all Nmeasure
+      if (NTABLE < catalog.average[j].Nmeasure) {
+	NTABLE = catalog.average[j].Nmeasure;
+	for (n = 0; n < Nreturn_base; n++) {
+	  REALLOCATE (table1[n], float, NTABLE);
+	  REALLOCATE (table2[n], float, NTABLE);
+	}
+      }
+
+      // extract the matching measures for this object into the temp tables
+      Nt1 = Nt2 = 0;
+      for (k = 0; (k < catalog.average[j].Nmeasure); k++, m++) {
+
+	// extract the relevant values for this measurement 
+	dbExtractMeasuresInitMeas (); // reset counters for saved fields 
+	for (n = 0; n < Nfields; n++) {
+	  values[n] = dbExtractMeasures (&catalog.average[j], &catalog.secfilt[j*Nsecfilt], &catalog.measure[m], &fields[n]);
+	}
+	// fprintf (stderr, "object: ave: %f, cat: %f, averef %d\n", fields[n].name, values[2], values[3], catalog.measure[m].averef);
+
+	// test the first conditional statement
+	if (dbBooleanCond (stack1, Nstack1, values)) {
+	  for (n = 0; n < Nreturn_base; n++) {
+	    table1[n][Nt1] = values[n];
+	    // fprintf (stderr, "keep : field: %s, value: %f\n", fields[n].name, values[n]);
+	  }
+	  Nt1 ++;
+	}
+	// test the second conditional statement
+	if (dbBooleanCond (stack2, Nstack2, values)) {
+	  for (n = 0; n < Nreturn_base; n++) {
+	    table2[n][Nt2] = values[n];
+	    // fprintf (stderr, "keep : field: %s, value: %f\n", fields[n].name, values[n]);
+	  }
+	  Nt2 ++;
+	}
+      }
+
+      // XXX now do the join :: need to filter against automatch if -noauto is selected (record the index value k to test)
+      for (n1 = 0; n1 < Nt1; n1++) {
+	for (n2 = 0; n2 < Nt2; n2++) {
+	  for (n = 0; n < Nreturn_base; n++) {
+	    vec[2*n+0][0].elements[Npts] = table1[n][n1];
+	    vec[2*n+1][0].elements[Npts] = table2[n][n2];
+	  }
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    for (n = 0; n < Nreturn; n++) {
+	      REALLOCATE (vec[n][0].elements, float, NPTS);
+	    }
+	  }
+	}
+      }
+    }
+
+    dvo_catalog_free (&catalog);
+    // dbStackAllocPrint ();
+    // dbStackAllocReset ();
+    // dbStackFreePrint ();
+    // dbStackFreeReset ();
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+
+  for (n = 0; n < Nreturn; n++) {
+    vec[n][0].Nelements = Npts;
+    REALLOCATE (vec[n][0].elements, float, MAX(1,Npts));
+  }
+
+  for (n = 0; n < Nreturn_base; n++) {
+    free (table1[n]);
+    free (table2[n]);
+  }
+  free (table1);
+  free (table2);
+
+  free (values);
+
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack1, Nstack1);
+  dbFreeStack (stack2, Nstack2);
+  free (stack1);
+  free (stack2);
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+escape:
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack1, Nstack1);
+  dbFreeStack (stack2, Nstack2);
+  free (stack1);
+  free (stack2);
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: mextract field[,field,field...] where (expression)\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, " USAGE: avextract field[,field,field...] where (expression)\n");
+    gprint (GP_ERR, "  RA : right ascension (J2000) for detection\n");
+    gprint (GP_ERR, "  DEC : declination for detection\n");
+    gprint (GP_ERR, "  RA:ave : average right ascension (J2000) for object\n");
+    gprint (GP_ERR, "  DEC:ave : average declination for object\n");
+    gprint (GP_ERR, "  RA:err : ra scatter \n");
+    gprint (GP_ERR, "  DEC:err : dec scatter\n");
+    gprint (GP_ERR, "  uRA : proper motion in ra\n");
+    gprint (GP_ERR, "  uDEC : proper motion in dec\n");
+    gprint (GP_ERR, "  duRA : proper motion error in ra\n");
+    gprint (GP_ERR, "  duDEC : proper motion error in dec\n");
+    gprint (GP_ERR, "  PAR : parallax\n");
+    gprint (GP_ERR, "  dPAR : parallax error \n");
+    gprint (GP_ERR, "  nmeas : number of measurements\n");
+    gprint (GP_ERR, "  nmiss : number of non-detections\n");
+    gprint (GP_ERR, "  xp : positional chi-square\n");
+    gprint (GP_ERR, "  objflag : object flags\n");
+    gprint (GP_ERR, "  photcode:ave : average magnitude for photcode (or equivalent)\n");
+    gprint (GP_ERR, "  photcode:ref : reference magnitude system for photcode (or equivalent)\n");
+    gprint (GP_ERR, "  photcode:inst : instrumental magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:cat :  catalog magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:sys :  system magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:rel :  relative magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:cal :  calibrated magnitude for photcode \n");
+    gprint (GP_ERR, "  photcode:err : magnitude error for photcode\n");
+    gprint (GP_ERR, "  photcode:chisq : chi-square of magnitude fit\n");
+    gprint (GP_ERR, "  photcode:ncode : number of measurements in photcode\n");
+    gprint (GP_ERR, "  photcode:nphot : number of measurements used for average magnitude\n");
+    gprint (GP_ERR, "  airmass : airmass of detection\n");
+    gprint (GP_ERR, "  exptime : exposure time\n");
+    gprint (GP_ERR, "  photcode : photcode \n");
+    gprint (GP_ERR, "  time : time of exposure\n");
+    gprint (GP_ERR, "  dR : ra offset\n");
+    gprint (GP_ERR, "  dD : dec offset\n");
+    gprint (GP_ERR, "  fwhm : fwhm (average)\n");
+    gprint (GP_ERR, "  fwhm_maj : fwhm (major axis)\n");
+    gprint (GP_ERR, "  fwhm_min : fwhm (minor axis)\n");
+    gprint (GP_ERR, "  theta : position angle\n");
+    gprint (GP_ERR, "  flags : detection flags\n");
+    gprint (GP_ERR, "  xccd : ccd x position\n");
+    gprint (GP_ERR, "  yccd : ccd y position\n");
+    gprint (GP_ERR, "  xmosaic : mosaic x position\n");
+    gprint (GP_ERR, "  ymosaic : mosaic y position\n");
+    gprint (GP_ERR, "  xchip : chip x position\n");
+    gprint (GP_ERR, "  ychip : chip y position\n");
+    gprint (GP_ERR, "  xfpa : fpa x position\n");
+    gprint (GP_ERR, "  yfpa : fpa y position\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " mextract --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/notes.txt	(revision 22322)
@@ -0,0 +1,10 @@
+
+avextract ra,dec,g,r,i where (ra > 5) && (dec < 3)
+
+
+U db_check_stack.c
+U db_convert_to_RPN.c
+U dvoBooleanElements.c
+U dvoEvaluateStack.c
+U dvodb.c
+U dvofields.c
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/paverage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/paverage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/paverage.c	(revision 22322)
@@ -0,0 +1,157 @@
+# include "dvoshell.h"
+# define NCHUNK 10000
+
+int paverage (int argc, char **argv) {
+  
+  FILE *f;
+  int i, j, kapa, Narg, Npts, NPTS, status, VERBOSE;
+  int Nsecfilt, Nsec;
+  double Mz, Mr, mag;
+  double Radius, Rmin, Rmax, R, D;
+  unsigned IDclip, IDchoice, LimExclude;
+  float *Xvec, *Yvec, *Zvec;
+
+  PhotCode *photcode;
+  SkyTable *sky;
+  SkyList *skylist;
+  Catalog catalog;
+  Graphdata graphmode;
+  Average *average;
+  SecFilt *secfilt;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  f = (FILE *) NULL;
+  Mz = 17.0;
+  Mr = -5.0;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  // require a photcode?  default to 0?
+  Nsec = 0;
+  if ((Narg = get_argument (argc, argv, "-p"))) {
+    remove_argument (Narg, &argc, argv);
+    photcode = GetPhotcodebyName (argv[Narg]);
+    if (!photcode) {
+	fprintf (stderr, "unknown photcode %s\n", argv[Narg]);
+	return (FALSE);
+    }
+    remove_argument (Narg, &argc, argv);
+    Nsec = GetPhotcodeNsec (photcode[0].code);
+    if (Nsec == -1) {
+	fprintf (stderr, "photcode %s is not an AVERAGE photcode\n", argv[Narg]);
+	return (FALSE);
+    }
+  }
+
+  IDchoice = 0;
+  IDclip = FALSE;
+  if ((Narg = get_argument (argc, argv, "-ID"))) {
+    remove_argument (Narg, &argc, argv);
+    IDchoice  = atoi(argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+    IDclip = TRUE;
+  }
+
+  LimExclude = FALSE;
+  if ((Narg = get_argument (argc, argv, "-x"))) {
+    remove_argument (Narg, &argc, argv);
+    LimExclude = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((Narg = get_argument (argc, argv, "-v"))) {
+    remove_argument (Narg, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  if ((Narg = get_argument (argc, argv, "-m"))) {
+    remove_argument (Narg, &argc, argv);
+    Mr  = atof(argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+    Mz = atof(argv[Narg]);
+    Mr = Mr - Mz;
+    remove_argument (Narg, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: paverage (-all) [-m M M] [-p photcode] [-ID ID] [-flag value] [-x]\n");
+    return (FALSE);
+  }
+  graphmode.style = 2; /* set style to points */
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0; /* no errorbars */
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+  
+  /* storage for plotting the points */
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (Xvec, float, NPTS);
+  ALLOCATE (Yvec, float, NPTS);
+  ALLOCATE (Zvec, float, NPTS);
+
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_SECF;
+    catalog.Nsecfilt = Nsecfilt;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    average = catalog.average;
+    secfilt = catalog.secfilt;
+
+    /* project stars to screen display coords */
+    for (i = 0; i < catalog.Naverage; i++) {
+      if (IDclip && (average[i].code != IDchoice)) continue;
+      while (average[i].R < Rmin) average[i].R += 360.0;
+      while (average[i].R > Rmax) average[i].R -= 360.0;
+
+      mag = secfilt[i*Nsecfilt+Nsec].M;
+      Zvec[Npts] = MIN (1.0, MAX (0.01, (mag - Mz) / Mr));
+      if (LimExclude && (Zvec[Npts] > 0.99)) continue;
+      if (Zvec[Npts] < 0.011) continue;
+      R = average[i].R;
+      D = average[i].D;
+      status = fRD_to_XY (&Xvec[Npts], &Yvec[Npts], R, D, &graphmode.coords);
+      if (!status) {
+	  fprintf (stderr, ".");
+	  continue;
+      }
+      Npts ++;
+
+      if (Npts == NPTS - 1) {
+	  NPTS += 1000;
+	  REALLOCATE (Xvec, float, NPTS);
+	  REALLOCATE (Yvec, float, NPTS);
+	  REALLOCATE (Zvec, float, NPTS);
+      }
+      if (Npts > NCHUNK) {
+	  PlotVectorTriplet (kapa, Npts, Xvec, Yvec, Zvec, &graphmode);
+	  Npts = 0;
+      }
+    }
+    dvo_catalog_free (&catalog);
+  }
+  if (Npts > 0) PlotVectorTriplet (kapa, Npts, Xvec, Yvec, Zvec, &graphmode);
+
+  free (Xvec);
+  free (Yvec);
+  free (Zvec);
+
+  return (TRUE);
+
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/pcat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/pcat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/pcat.c	(revision 22322)
@@ -0,0 +1,111 @@
+# include "dvoshell.h"
+int RD_to_XYpic (double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside);
+
+// XXX EAM : this function is deprecated (now points at skycat)
+int pcat (int argc, char **argv) {
+  
+  double Radius;
+  int i, j, kapa, N, Nregions, ShowAll, NPTS, Npts, leftside;
+  RegionFile *regions;
+  char filename[128];
+  struct stat filestat;
+  Vector Xvec, Yvec;
+  Graphdata graphmode;
+  double X[4], Y[4], Rmin, Rmax, Rmid;
+  char catdir[256];
+  int VERBOSE;
+
+  VarConfig ("CATDIR", "%s", catdir);
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  ShowAll = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    ShowAll = TRUE;
+  }
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: pcat [-all]\n");
+    return (FALSE);
+  }
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+  
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+  regions = find_regions (graphmode.coords.crval1, graphmode.coords.crval2, Radius, &Nregions);
+
+  Rmin = graphmode.coords.crval1 - 180.0;
+  Rmax = graphmode.coords.crval1 + 180.0;
+  Rmid = 0.5*(Rmin + Rmax);
+
+  NPTS = 200;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  Npts = 0;
+   
+  for (i = 0; i < Nregions; i++) {
+    sprintf (filename, "%s/%s", catdir, regions[i].name);
+    if (ShowAll || (stat (filename, &filestat) != -1)) {
+      if (VERBOSE) gprint (GP_ERR, "%3d %s\n", i, regions[i].name);
+
+      leftside = -1;
+      RD_to_XYpic (&X[0], &Y[0], regions[i].RA0, regions[i].DEC0, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[1], &Y[1], regions[i].RA0, regions[i].DEC1, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[2], &Y[2], regions[i].RA1, regions[i].DEC1, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[3], &Y[3], regions[i].RA1, regions[i].DEC0, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+
+      Xvec.elements[Npts] = X[0];
+      Yvec.elements[Npts] = Y[0];
+      for (j = 1; j < 4; j++) {
+	Xvec.elements[Npts + j*2 - 0] = X[j];
+	Yvec.elements[Npts + j*2 - 0] = Y[j];
+	Xvec.elements[Npts + j*2 - 1] = X[j];
+	Yvec.elements[Npts + j*2 - 1] = Y[j];
+      }
+      Xvec.elements[Npts+7] = Xvec.elements[Npts];
+      Yvec.elements[Npts+7] = Yvec.elements[Npts];
+      Npts += 8;
+      if (Npts > NPTS - 1) {  /* this is OK because NPTS is made always a multiple of 8 */
+	NPTS += 200;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+      }
+    }
+  }
+
+  gprint (GP_ERR, "plotting %d catalogs\n", Npts/8);
+  Xvec.Nelements = Xvec.Nelements = Npts;
+  if (Npts > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, Npts, Xvec.elements, Yvec.elements, &graphmode);
+  }
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (regions);
+
+  return (TRUE);
+
+}
+
+int RD_to_XYpic (double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside) {
+
+  while (r < Rmin) { r += 360.0; }
+  while (r > Rmax) { r -= 360.0; }
+
+  if (*leftside == -1) {
+    *leftside = (r < Rmid);
+  } else {
+    if (  *leftside && (r > Rmid + 90)) { r -= 360.0; }
+    if (! *leftside && (r < Rmid - 90)) { r += 360.0; }
+  }
+
+  RD_to_XY (x, y, r, d, coords);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/photcodes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/photcodes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/photcodes.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "dvoshell.h"
+
+/* list or return all photcodes equivalent to the given filter */
+int photcodes (int argc, char **argv) {
+  
+  int i, Np;
+  int *list, Nlist;
+  char name[64];
+  PhotCode *code;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: photcodes (photcode)\n");
+    return (FALSE);
+  }
+
+  /* load photcodes, convert name to code */
+  if (!InitPhotcodes ()) return (FALSE);
+
+  if (!(Np = GetPhotcodeCodebyName (argv[1]))) {
+    gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+    return (FALSE);
+  }
+
+  list = GetPhotcodeEquivList (Np, &Nlist);
+  
+  for (i = 0; i < Nlist; i++) {
+    code = GetPhotcodebyCode (list[i]);
+
+    sprintf (name, "photcode:name:%d", i);
+    set_str_variable (name, code[0].name);
+
+    sprintf (name, "photcode:C:%d", i);
+    set_variable (name, 0.001*code[0].C);
+
+    sprintf (name, "photcode:K:%d", i);
+    set_variable (name, code[0].K);
+
+    sprintf (name, "photcode:X:%d", i);
+    set_variable (name, code[0].X[0]);
+
+    sprintf (name, "photcode:dX:%d", i);
+    set_variable (name, 0.001*code[0].dX);
+
+    sprintf (name, "photcode:code:%d", i);
+    set_int_variable (name, code[0].code);
+
+    sprintf (name, "photcode:filter:%d", i);
+    set_str_variable (name, GetPhotcodeNamebyCode (code[0].equiv));
+
+    sprintf (name, "photcode:c1:%d", i);
+    set_str_variable (name, GetPhotcodeNamebyCode (code[0].c1));
+
+    sprintf (name, "photcode:c2:%d", i);
+    set_str_variable (name, GetPhotcodeNamebyCode (code[0].c2));
+
+    gprint (GP_ERR, "%5d %s %7.4f %7.4f %7.4f\n", 
+	     code[0].code, code[0].name, 0.001*code[0].C, code[0].K, code[0].X[0]);
+  }
+  set_int_variable ("photcode:n", Nlist);
+  free (list);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/photometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/photometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/photometry.c	(revision 22322)
@@ -0,0 +1,1220 @@
+# include "dvoshell.h"
+
+/* match code to measure  */
+# define TESTCODE(C,M) \
+  if (C != NULL) { \
+    switch (C[0].type) { \
+    case PHOT_DEP: \
+    case PHOT_REF: \
+      if (C[0].code != M.photcode) continue; \
+      break; \
+    case PHOT_SEC: \
+      if (C[0].code != GetPhotcodeEquivCodebyCode (M.photcode)) continue; \
+      break; \
+    default: \
+      break; \
+  } }
+
+/* exclusions based on measure.params  */
+# define TESTMEASURE(M) \
+  if (ApplySelections[SelectionParam]) { \
+    if (TimeSelect && (M.t < tzero)) continue; \
+    if (TimeSelect && (M.t > tend)) continue; \
+    if (ErrSelect  && (M.dM > ErrValue)) continue; \
+    if (TypeSelect && (TypeValue != GetMeasureTypeCode (&M))) continue; \
+    if (iMagSelect && (PhotInst (&M) < iMagMin)) continue; \
+    if (FlagSelect && (M.dbFlags != FlagValue)) continue; \
+  }
+
+# define SETMAG(MOUT,MEAS,MODE) \
+  MOUT = NAN; \
+  if (MODE == MAG_INST) MOUT = PhotInst (&MEAS);  \
+  if (MODE == MAG_CAT)  MOUT = PhotCat  (&MEAS); \
+  if (MODE == MAG_SYS)  MOUT = PhotSys  (&MEAS, average, secfilt); \
+  if (MODE == MAG_REL)  MOUT = PhotRel  (&MEAS, average, secfilt); \
+  if (MODE == MAG_CAL)  MOUT = PhotCal  (&MEAS, average, secfilt, measure, GetPhotcodeEquivbyCode (MEAS.photcode)); \
+  if (MODE == MAG_AVE)  MOUT = PhotAve  (GetPhotcodeEquivbyCode (MEAS.photcode), average, secfilt); \
+  if (MODE == MAG_REF)  MOUT = PhotRef  (GetPhotcodeEquivbyCode (MEAS.photcode), average, secfilt, measure); \
+  if (ApplySelections[SelectionParam]) { \
+    if (MagSelect && (MOUT > MagMax)) continue; \
+    if (MagSelect && (MOUT < MagMin)) continue; \
+  }
+
+/* selection criteria */
+/* selections based on Measure quantities */
+static int TimeSelect;
+static time_t tzero, tend;
+static int MagSelect;
+static double MagMax, MagMin;
+static int TypeSelect, TypeValue;
+static int ErrSelect;
+static double ErrValue;
+static int iMagSelect;
+static double iMagMin;
+static int FlagSelect, FlagValue;
+static int TypefracSelect, TypefracType, TypefracSign;
+static double TypefracValue;
+
+/* apply selections or not */
+static int ApplySelections[4];
+static int SelectionParam;
+
+/* applied to Average quantities */
+static int PhotcodeSelect;
+static PhotCode *PhotcodeValue;
+static int PhotcodeMode;
+
+/* selections based on Average quantities */
+static int ChiSelect;
+static float ChiLimit;
+
+/* selections based on ensemble quantities */
+static int NphotSelect, NphotSign, NphotValue;
+static int NcodeSelect, NcodeSign, NcodeValue;
+static int FWHMSelect, FWHMsign;
+static double FWHMvalue, FWHMfrac;
+
+/* time concepts */
+static time_t TimeReference;
+static int TimeFormat;
+
+int GetTimeSelection (time_t *tz, time_t *te) {
+  *tz = tzero;
+  *te = tend;
+  return (TimeSelect);
+}
+
+int GetPhotcodeInfo (char *string, PhotCode **Code, int *Mode) {
+
+  PhotCode *code;
+  int mode, status;
+  char *p, *tmpstring;
+
+  /* save local copy */
+  tmpstring = strcreate (string);
+
+  /* check for code:mode in photcode name */
+  mode = MAG_NONE;
+  p = strchr (tmpstring, ':');
+  if (p != NULL) {
+    mode = GetMagMode (p + 1);
+    if (mode == MAG_NONE) {
+      gprint (GP_ERR, "syntax error in magnitude mode\n");
+      free (tmpstring);
+      return (FALSE);
+    }
+    *p = 0;
+  }
+
+  /* how do we handle this elsewhere? */
+  if (!strcasecmp (tmpstring, "mag")) {
+    /* need to validate mode */
+    *Mode = mode;
+    *Code = NULL;
+    free (tmpstring);
+    return (TRUE);
+  }
+
+  code = GetPhotcodebyName (tmpstring);
+  if (code == NULL) {
+    gprint (GP_ERR, "photcode not found in photcode table\n");
+    free (tmpstring);
+    return (FALSE);
+  }
+
+  /* test allowable cases and/or set default values */
+  status = FALSE;
+  if (code[0].type == PHOT_DEP) {
+    if (mode == MAG_NONE) mode = MAG_REL;
+    if (mode == MAG_INST) status = TRUE;
+    if (mode == MAG_CAT)  status = TRUE;
+    if (mode == MAG_SYS)  status = TRUE;
+    if (mode == MAG_REL)  status = TRUE;
+    if (mode == MAG_CAL)  status = TRUE;
+  }  
+  if (code[0].type == PHOT_SEC) {
+    if (mode == MAG_NONE) mode  = MAG_AVE;
+    if (mode == MAG_INST) status = TRUE;
+    if (mode == MAG_CAT)  status = TRUE;
+    if (mode == MAG_SYS)  status = TRUE;
+    if (mode == MAG_REL)  status = TRUE;
+    if (mode == MAG_CAL)  status = TRUE;
+    if (mode == MAG_AVE)  status = TRUE;
+    if (mode == MAG_REF)  status = TRUE;
+  }  
+  if (code[0].type == PHOT_ALT) {
+    if (mode == MAG_NONE) mode  = MAG_AVE;
+    if (mode == MAG_AVE)  status = TRUE;
+    if (mode == MAG_REF)  status = TRUE;
+  }
+
+  if (code[0].type == PHOT_REF) {
+    if (mode == MAG_NONE) mode  = MAG_CAT;
+    if (mode == MAG_CAT)  status = TRUE;
+  }
+
+  if (!status) {
+    gprint (GP_ERR, "mismatch in photcode and magmode\n");
+    free (tmpstring);
+    return (FALSE);
+  }
+  *Code = code;
+  *Mode = mode;
+  free (tmpstring);
+  return (TRUE);
+}
+ 
+int SetSelectionParam (int param) {
+  SelectionParam = param;
+  return (TRUE);
+}
+
+int GetSelectionParam () {
+  return (SelectionParam);
+}
+
+int GetMeasureParam (char *parname) {
+
+  int param;
+
+  param = MEAS_ZERO;
+  if (!strcasecmp (parname, "ra"))   	 param = MEAS_RA;
+  if (!strcasecmp (parname, "dec"))  	 param = MEAS_DEC;
+  if (!strcasecmp (parname, "mag")) 	 param = MEAS_MAG;
+  // if (!strcasecmp (parname, "dmag")) 	 param = MEAS_dMAG;
+  if (!strcasecmp (parname, "airmass"))  param = MEAS_AIRMASS;
+  if (!strcasecmp (parname, "exptime"))  param = MEAS_EXPTIME;
+  if (!strcasecmp (parname, "photcode")) param = MEAS_PHOTCODE;
+  if (!strcasecmp (parname, "time"))     param = MEAS_TIME;
+  if (!strcasecmp (parname, "dR")) 	 param = MEAS_RA_OFFSET;
+  if (!strcasecmp (parname, "dD")) 	 param = MEAS_DEC_OFFSET;
+  if (!strcasecmp (parname, "fwhm"))   	 param = MEAS_FWHM;
+  // if (!strcasecmp (parname, "dophot")) 	 param = MEAS_DOPHOT;
+  if (!strcasecmp (parname, "FLAGS"))    param = MEAS_DB_FLAGS;
+  if (!strcasecmp (parname, "XCCD"))   	 param = MEAS_XCCD;
+  if (!strcasecmp (parname, "YCCD"))   	 param = MEAS_YCCD;
+  if (!strcasecmp (parname, "XMOSAIC"))  param = MEAS_XMOSAIC;
+  if (!strcasecmp (parname, "YMOSAIC"))  param = MEAS_YMOSAIC;
+  if (!strcasecmp (parname, "help")) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " ra dR dec dD mag dmag Mrel Mcal photcode time fwhm dophot xccd yccd xmosaic ymosaic flags\n");
+    gprint (GP_ERR, "value may also be a valid photcode\n");
+    gprint (GP_ERR, "photcodes or 'mag' may have optional magnitude type: mag,[Minst, Mcat, Msys, Mrel, Mcal]\n");
+  }
+  return (param);
+}
+  
+int GetAverageParam (char *parname) {
+
+  int param;
+
+  param = AVE_ZERO;
+  if (!strcasecmp (parname, "RA"))    param = AVE_RA;
+  if (!strcasecmp (parname, "DEC"))   param = AVE_DEC;
+
+  if (!strcasecmp (parname, "dRA"))   param = AVE_RA_ERR;
+  if (!strcasecmp (parname, "dDEC"))  param = AVE_DEC_ERR;
+
+  if (!strcasecmp (parname, "uRA"))   param = AVE_U_RA;
+  if (!strcasecmp (parname, "uDEC"))  param = AVE_U_DEC;
+  if (!strcasecmp (parname, "duRA"))  param = AVE_U_RA_ERR;
+  if (!strcasecmp (parname, "duDEC")) param = AVE_U_DEC_ERR;
+
+  if (!strcasecmp (parname, "par"))   param = AVE_PAR;
+  if (!strcasecmp (parname, "dpar"))  param = AVE_PAR_ERR;
+
+  if (!strcasecmp (parname, "dmag"))  param = AVE_dMAG;
+  if (!strcasecmp (parname, "mag"))   param = AVE_MAG;
+  if (!strcasecmp (parname, "Nmeas")) param = AVE_NMEAS;
+  if (!strcasecmp (parname, "Nmiss")) param = AVE_NMISS;
+  if (!strcasecmp (parname, "Xp"))    param = AVE_Xp;
+  if (!strcasecmp (parname, "Xm"))    param = AVE_Xm;
+  if (!strcasecmp (parname, "flag"))  param = AVE_FLAG;
+  if (!strcasecmp (parname, "type"))  param = AVE_TYPE;
+  if (!strcasecmp (parname, "typefrac")) {
+    if (!TypefracType) {
+      gprint (GP_ERR, "typefrac needs to specify type to use\n");
+      return (param);
+    }
+    param = AVE_TYPEFRAC;
+  }
+  if (!strcasecmp (parname, "Nphot")) param = AVE_NPHOT;
+  if (!strcasecmp (parname, "Ncode")) param = AVE_NCODE;
+  // if (!strcasecmp (parname, "Ncrit")) param = AVE_NCRIT;
+  return (param);
+}
+
+/* I've set some selections - if these require a photcode, check if I set one */
+int TestPhotSelections (PhotCode **code, int *mode, int param) {
+
+  int NeedPhotcode, Needcode;
+
+  /* if i've supplied a photcode (code != NULL), i'm not allowed to restrict it */
+  if (code[0] != NULL) {
+    if (PhotcodeSelect) {
+      gprint (GP_ERR, "photcode selection rules violated: cannot restrict photcode with a photcode\n");
+      return (FALSE);
+    } else {
+      return (TRUE);
+    }
+  }
+
+  /* for measure tests, supply MEAS_ZERO */
+
+  /* if I have an average or ensemble restriction, I must have a PRI/SEC photcode */
+  NeedPhotcode = FALSE;
+  NeedPhotcode |= ChiSelect;
+  NeedPhotcode |= NphotSelect;
+  NeedPhotcode |= ErrSelect;
+  NeedPhotcode |= TypeSelect;
+  NeedPhotcode |= TypefracSelect;
+  
+  NeedPhotcode |= (param == AVE_Xm);
+  NeedPhotcode |= (param == AVE_MAG);
+  NeedPhotcode |= (param == AVE_dMAG);
+  NeedPhotcode |= (param == AVE_TYPE);
+  NeedPhotcode |= (param == AVE_NPHOT);
+  Needcode = (param == AVE_NCODE);
+
+  if (NeedPhotcode || Needcode || NcodeSelect || PhotcodeSelect) {
+    if (!PhotcodeSelect) {
+      gprint (GP_ERR, "photcode selection problem: value requires photcode\n");
+      return (FALSE);
+    }
+    code[0] = PhotcodeValue;
+    mode[0] = PhotcodeMode;
+  }
+  if (NeedPhotcode) {
+    if (code[0][0].type == PHOT_SEC) return (TRUE);
+    if (code[0][0].type == PHOT_REF) return (TRUE);
+    gprint (GP_ERR, "photcode selection problem: average value requires average photcode\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+void GetAverageParamHelp () {
+  gprint (GP_ERR, "value may be one of the following:\n");
+  gprint (GP_ERR, " ra dec dmag Nmeas Nmiss Xm Xp Nphot Ncode flag type typefrac\n\n");
+  gprint (GP_ERR, "value may also be a valid photcode\n");
+  gprint (GP_ERR, "photcodes or 'mag' may have optional magnitude mode: mag,[Mave, Mref]\n");
+  return;
+}
+
+/* (re)load photcodes from photcode table */
+int InitPhotcodes () {
+
+  double ZERO_POINT;
+  char MasterPhotcodeFile[256];
+  char CatdirPhotcodeFile[256];
+  char *catdir;
+
+  if (VarConfig ("ZERO_PT", "%lf", &ZERO_POINT) == (char *) NULL) {
+      gprint (GP_ERR, "ZERO_PT undefined in config\n");
+      return (FALSE);
+  }
+  SetZeroPoint (ZERO_POINT);
+
+  catdir = GetCATDIR();
+  if (catdir == NULL) {
+    CatdirPhotcodeFile[0] = 0;
+  } else {
+    sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", catdir);
+  }
+
+  if (VarConfig ("PHOTCODE_FILE", "%s", MasterPhotcodeFile) == (char *) NULL) {
+      gprint (GP_ERR, "PHOTCODE_FILE undefined in config\n");
+      return (FALSE);
+  }
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    gprint (GP_ERR, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int ListPhotSelections () {
+
+  gprint (GP_ERR, "TimeSelect: %d, %d - %d\n",      TimeSelect, tzero, tend);
+  gprint (GP_ERR, "MagSelect: %d, %f - %f\n",       MagSelect, MagMax, MagMin);
+  gprint (GP_ERR, "TypeSelect: %d, %d\n",           TypeSelect, TypeValue);
+  gprint (GP_ERR, "ErrSelect: %d, %f\n",            ErrSelect, ErrValue);
+  gprint (GP_ERR, "iMagSelect: %d, %f\n",           iMagSelect, iMagMin);
+  gprint (GP_ERR, "FlagSelect: %d, %x\n",           FlagSelect, FlagValue);
+  gprint (GP_ERR, "TypefracSelect: %d, %d %d %f\n", TypefracSelect, TypefracType, TypefracSign, TypefracValue);
+  gprint (GP_ERR, "ApplySelections: %d,%d,%d,%d : %d\n", ApplySelections[0], ApplySelections[1], ApplySelections[2], ApplySelections[3], SelectionParam);
+  if (PhotcodeSelect) {
+    gprint (GP_ERR, "PhotcodeSelect: %d, %s\n",       PhotcodeSelect, PhotcodeValue[0].name);
+  } else {
+    gprint (GP_ERR, "PhotcodeSelect: %d, none\n",       PhotcodeSelect);
+  }
+  gprint (GP_ERR, "ChiSelect: %d, %f\n",            ChiSelect, ChiLimit);
+  gprint (GP_ERR, "NphotSelect: %d, %d - %d\n",     NphotSelect, NphotSign, NphotValue);
+  gprint (GP_ERR, "NcodeSelect: %d, %d - %d\n",     NcodeSelect, NcodeSign, NcodeValue);
+  gprint (GP_ERR, "FWHMSelect: %d, %d %f %f\n",     FWHMSelect, FWHMsign, FWHMvalue, FWHMfrac);
+  return (TRUE);
+}
+
+/* remove standard photometry filtering options, set selections */
+/* not all functions respect all selections... */
+int SetPhotSelections (int *argc, char **argv, int Nparams) {
+
+  int i, N;
+  double trange;
+
+  if ((N = get_argument (*argc, argv, "-phothelp"))) {
+    gprint (GP_ERR, "optional photometry selection criteria:\n");
+    gprint (GP_ERR, " -magrange min max\n");
+    gprint (GP_ERR, " -imaglim min\n");
+    gprint (GP_ERR, " -flag value\n");
+    gprint (GP_ERR, " -chisq value\n");
+    gprint (GP_ERR, " -photcode code\n");
+    gprint (GP_ERR, " -time start range\n");
+    gprint (GP_ERR, " -errorlim value\n");
+    gprint (GP_ERR, " -type type\n");
+    gprint (GP_ERR, " -nmeas [+/-]N\n");
+    gprint (GP_ERR, " -fwhm [+/-]fraction\n");
+    return (FALSE);
+  }
+
+  /* select based on measured mag (MEASURE ONLY) */
+  MagSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-magrange"))) {
+    MagSelect = TRUE;
+    remove_argument (N, argc, argv);
+    MagMin = atof (argv[N]);
+    remove_argument (N, argc, argv);
+    MagMax = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select based on instrument mag (MEASURE ONLY) */
+  iMagSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-imaglim"))) {
+    iMagSelect = TRUE;
+    remove_argument (N, argc, argv);
+    iMagMin = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on value of flag (MEASURE ONLY) */
+  FlagSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-flag"))) {
+    FlagSelect = TRUE;
+    remove_argument (N, argc, argv);
+    FlagValue = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on value of Chisq (AVERAGE ONLY) */
+  SelectionParam = 0;
+  for (i = 0; i < 4; i++) ApplySelections[i] = TRUE;
+  if ((N = get_argument (*argc, argv, "-apply"))) {
+    remove_argument (N, argc, argv);
+    if (strlen(argv[N]) != Nparams) {
+      gprint (GP_ERR, "-apply selection must define all parameter choices\n");
+      return (FALSE);
+    }
+    for (i = 0; i < Nparams; i++) {
+      if (toupper(argv[N][i]) == 'Y') {
+	ApplySelections[i] = TRUE;
+      } else {
+	ApplySelections[i] = FALSE;
+      }
+    }
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on value of photcode */
+  PhotcodeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-photcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, argc, argv);
+    GetPhotcodeInfo (argv[N], &PhotcodeValue, &PhotcodeMode);
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);;
+    }
+    remove_argument (N, argc, argv);
+  }
+
+  /* selection on basis of time range (MEASURE only) */
+  TimeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-time"))) {
+    remove_argument (N, argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, argc, argv);
+    TimeSelect = TRUE;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tend = tzero;
+      tzero -= trange;
+    } else {
+      tend = tzero + trange;
+    }
+  }
+
+  /* select by error (on measure or average) */
+  ErrSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-errorlim"))) {
+    remove_argument (N, argc, argv);
+    ErrValue = atof (argv[N]);
+    remove_argument (N, argc, argv);
+    ErrSelect = TRUE;
+  }
+
+  /* select on value of Chisq (AVERAGE ONLY) */
+  ChiSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-chisq"))) {
+    ChiSelect = TRUE;
+    remove_argument (N, argc, argv);
+    ChiLimit = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on measurement type: 1,2,3 (AVERAGE ONLY) */
+  TypeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-type"))) {
+    remove_argument (N, argc, argv);
+    TypeValue = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+    TypeSelect = TRUE;
+  }
+
+  /* select on measurement type: 1,2,3 (AVERAGE ONLY) */
+  TypefracType = 0;
+  if ((N = get_argument (*argc, argv, "-usetype"))) {
+    remove_argument (N, argc, argv);
+    TypefracType = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on measurement type: 1,2,3 (AVERAGE ONLY) */
+  TypefracSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-typefrac"))) {
+    remove_argument (N, argc, argv);
+    TypefracType  = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+    TypefracValue = fabs (atof (argv[N]));
+    TypefracSign = 0;
+    if (argv[N][0] == '-') TypefracSign = -1;
+    if (argv[N][0] == '+') TypefracSign = +1;
+    remove_argument (N, argc, argv);
+    TypefracSelect = TRUE;
+  }
+
+  /* select by number of measurements (AVERAGE ONLY) */
+  NphotSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-nphot"))) {
+    remove_argument (N, argc, argv);
+    NphotValue = abs (atoi (argv[N]));
+    NphotSign = 0;
+    if (argv[N][0] == '-') NphotSign = -1;
+    if (argv[N][0] == '+') NphotSign = +1;
+    remove_argument (N, argc, argv);
+    NphotSelect = TRUE;
+  }
+
+  /* select by number of measurements (AVERAGE ONLY) */
+  NcodeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-ncode"))) {
+    remove_argument (N, argc, argv);
+    NcodeValue = abs (atoi (argv[N]));
+    NcodeSign = 0;
+    if (argv[N][0] == '-') NcodeSign = -1;
+    if (argv[N][0] == '+') NcodeSign = +1;
+    remove_argument (N, argc, argv);
+    NcodeSelect = TRUE;
+  }
+
+  /* -fwhm value frac (AVERAGE ONLY) */
+  FWHMSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-fwhm"))) {
+    remove_argument (N, argc, argv);
+    FWHMvalue = abs (atof (argv[N]));
+    FWHMsign = 0;
+    if (argv[N][0] == '-') FWHMsign = -1;
+    if (argv[N][0] == '+') FWHMsign = +1;
+    remove_argument (N, argc, argv);
+    FWHMSelect = TRUE;
+    remove_argument (N, argc, argv);
+    FWHMfrac = atof (argv[N]);
+  }
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+  return (TRUE);
+}
+
+/* extract a list of measure parameters from the specified average entry based on the pre-set selections */
+double *ExtractMeasures (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+
+  int i, Nlist, NLIST;
+  double M, *list;
+  
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+
+  /* check selections based on averages & ensembles: chisq, Nphot, etc */
+  if (!TestAverage (code, average, secfilt, measure)) return (list); 
+
+  /* look for measures */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    TESTCODE (code, measure[i]);  /* skip measurements not matching photcode */
+    TESTMEASURE (measure[i]);     /* exclusions based on measure.params  */
+    SETMAG (M, measure[i], mode); /* set appropriate magnitude (also does MagSelect) */ 
+
+    /* assign value */
+    list[Nlist] = GetMeasure (param, &average[0], &measure[i], M);
+    Nlist ++;
+  }
+  *nlist = Nlist;
+  return (list);
+}
+
+/* return average.param based on the selection */
+double ExtractAverages (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param) {
+
+  int i;
+  double value;
+
+  value = NAN;
+
+  /* this function requires code set for certain value of param.  
+     use TestPhotSelectionsAverage to validate code/param choices */
+
+  /* filter by average quantities (eg, chisq, Nphot, etc) */
+  if (!TestAverage (code, average, secfilt, measure)) return (NAN);
+
+  /* assign vector values */
+  switch (param) {
+    case AVE_RA:
+      value = average[0].R;
+      break;
+    case AVE_DEC:
+      value = average[0].D;
+      break;
+    case AVE_RA_ERR:
+      value = average[0].dR;
+      break;
+    case AVE_DEC_ERR:
+      value = average[0].dD;
+      break;
+
+    case AVE_U_RA:
+      value = average[0].uR;
+      break;
+    case AVE_U_DEC:
+      value = average[0].uD;
+      break;
+    case AVE_U_RA_ERR:
+      value = average[0].duR;
+      break;
+    case AVE_U_DEC_ERR:
+      value = average[0].duD;
+      break;
+
+    case AVE_PAR:
+      value = average[0].P;
+      break;
+    case AVE_PAR_ERR:
+      value = average[0].dP;
+      break;
+
+
+    case AVE_NMEAS:
+      value = average[0].Nmeasure;
+      break;
+    case AVE_NMISS:
+      value = average[0].Nmissing;
+      break;
+    case AVE_Xp:
+      value = 0.01*average[0].Xp;
+      break;
+    case AVE_FLAG:
+      value = average[0].code;
+      break;
+    case AVE_MAG:
+      switch (mode) {
+	case MAG_AVE:
+	  value = PhotAve  (code, average, secfilt);
+	  break;
+	case MAG_REF:
+	  value = PhotRef  (code, average, secfilt, measure);
+	  break;
+	case MAG_INST:
+	case MAG_CAT:
+	case MAG_SYS:
+	case MAG_REL:
+	case MAG_CAL:
+	  value = NAN;
+	  for (i = 0; i < average[0].Nmeasure; i++) {
+	      if (code[0].code != measure[i].photcode) continue;
+	      value = measure[i].M;
+	  }
+	  break;
+      }
+      break;
+    case AVE_dMAG:
+      value = PhotdM (code, average, secfilt);
+      break;
+    case AVE_Xm:
+      value = PhotXm (code, average, secfilt);
+      break;
+    case AVE_TYPE:
+      value = DetermineTypeCode (average, measure, code[0].code);
+      break;
+    case AVE_TYPEFRAC:
+      value = DetermineTypefrac (average, measure, code);
+      break;
+    case AVE_NCODE:
+      value = 0;
+      for (i = 0; i < average[0].Nmeasure; i++) {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (measure[i].photcode)) continue;
+	value ++;
+      }
+      break;
+    case AVE_NPHOT:
+      value = 0;
+      for (i = 0; i < average[0].Nmeasure; i++) {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (measure[i].photcode)) continue;
+	if (measure[i].dbFlags & (ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM)) continue;
+	value ++;
+      }
+      break;
+# if (0)
+    case AVE_NCRIT:
+      value = 0;
+      for (i = 0; i < average[0].Nmeasure; i++) {
+	if ((code != NULL) && (code[0].code != GetPhotcodeEquivCodebyCode (measure[i].photcode))) continue;
+	if (ErrSelect && (measure[i].dM > ErrValue)) continue;
+	if (FlagSelect && (measure[i].dbFlags != FlagValue)) continue;
+	if (TypeSelect && (TypeValue != GetMeasureTypeCode (&measure[i]))) continue;
+	if (iMagSelect && (PhotInst (&measure[i]) < iMagMin)) continue;
+	value ++;
+      }
+      break;
+# endif
+  }
+  return (value);
+}  
+
+/* return fraction of measures (matching code) which have requested type */
+double DetermineTypefrac (Average *average, Measure *measure, PhotCode *code) {
+
+  double frac;
+  int k, Nc, Nt;
+  
+  Nt = Nc = 0;
+  for (k = 0; k < average[0].Nmeasure; k++) {
+    if ((code != NULL) && (code[0].code != GetPhotcodeEquivCodebyCode (measure[k].photcode))) continue;
+    Nc ++;
+    if (measure[k].dophot != TypefracType) continue;
+    Nt ++;
+  }
+  frac = (double) Nt / (double) Nc;
+  return (frac);
+}
+
+/* determine the representative dophot type for this photcode (must be PRI/SEC) */
+int DetermineTypeCode (Average *average, Measure *measure, int code) {
+
+  int k, N, Nt[3];
+  
+  Nt[0] = Nt[1] = Nt[2] = 0;
+  for (k = 0; k < average[0].Nmeasure; k++) {
+    if (code != GetPhotcodeEquivCodebyCode (measure[k].photcode)) continue;
+    N = GetMeasureTypeCode (&measure[k]);
+    Nt[N] ++;
+  }
+  if (Nt[0]) return (0);
+  if (Nt[1]) return (1);
+  if (Nt[2]) return (2);
+  return (3);
+}
+
+int GetMeasureTypeCode (Measure *measure) {
+  switch (measure[0].dophot) {
+    case 0:
+    case 1:
+    case 2:
+      return (0);
+      break;
+    case 3:
+    case 4:
+    case 5:
+    case 7:
+    case 9:
+      return (1);
+      break;
+    case 10:
+    default:
+      return (2);
+  }
+  return (2);
+}  
+
+int Quality (Measure *measure, int IsDophot) {
+
+  return (TRUE);
+  
+  if (IsDophot) {
+    
+    if (measure[0].dophot == 4) return (FALSE);
+    return (TRUE);
+  
+  } else {
+    
+    if (0.01 * measure[0].FWx < 3.0) return (FALSE);
+    if (0.01 * measure[0].FWx > 10.0) return (FALSE);
+
+    return (TRUE);
+
+  }
+}
+
+/* test if this average object meets the specified selection criteria.
+   for photcode-dependent quantities, only test for PRI/SEC photcodes */
+int TestAverage (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+
+  int i, Nm, Type, Select;
+  double fwhm, typefrac, dM, Xm;
+
+  /** temporary special case for Jen Katz: exclude REF with Ncode > 1 */
+  if ((code != NULL) && (code[0].type == PHOT_REF)) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      TESTCODE (code, measure[i]);
+      Nm++;
+    }
+    if (Nm > 1) return (FALSE);
+  }
+
+  if (!ApplySelections[SelectionParam]) return (TRUE);
+
+  /* pass objects with more than FWHMfrac points with FWHM above / below FWHMvalue */ 
+  if (FWHMSelect) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      fwhm = measure[i].FWx / 100.0;
+      switch (FWHMsign) {
+	case 0:
+	  if (fwhm == FWHMvalue) break;
+	  continue;
+	case +1:
+	  if (fwhm >= FWHMvalue) break;
+	  continue;
+	case -1:
+	  if (fwhm <= FWHMvalue) break;
+	  continue;
+      }
+      Nm++;
+    }
+    if (average[0].Nmeasure * FWHMfrac > Nm) return (FALSE);
+  }
+
+  /* all selections below require a valid photcode */
+  Select = ChiSelect || ErrSelect || NcodeSelect || NphotSelect || TypeSelect || TypefracSelect;
+  if (!Select) return (TRUE);
+
+  /* must have a valid code of some kind */
+  if (code == NULL) return (FALSE);
+
+  /* for NcodeSelect, count Nmeas for appropriate photcode */
+  if (NcodeSelect) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      TESTCODE (code, measure[i]);
+      Nm++;
+    }
+    switch (NcodeSign) {
+      case 0:
+	if (Nm == NcodeValue) break;
+	return (FALSE);
+      case 1:
+	if (Nm >= NcodeValue) break;
+	return (FALSE);
+      case -1:
+	if (Nm <= NcodeValue) break;
+	return (FALSE);
+      default:
+	return (FALSE);
+    }
+  }
+
+  /* only PRI/SEC photcodes apply the filter */
+  if (code[0].type == PHOT_DEP) return (TRUE);
+  if (code[0].type == PHOT_REF) return (TRUE);
+
+  /* exclusions based on average.params  */
+  if (ChiSelect) {
+    Xm = PhotXm (code, average, secfilt);
+    if (Xm == -1) return (FALSE);
+    if (Xm > ChiLimit) return (FALSE);
+  }
+  
+  /* for ErrSelect, check average errors */
+  if (ErrSelect) {
+    dM = PhotdM (code, average, secfilt);
+    if (dM > ErrValue) return (NAN);
+  }
+  
+  /* for NphotSelect, count Nmeas for appropriate photcode */
+  if (NphotSelect) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      TESTCODE (code, measure[i]);
+      if (measure[i].dbFlags && ID_MEAS_SKIP_PHOTOM) continue;
+      Nm++;
+    }
+    switch (NphotSign) {
+      case 0:
+	if (Nm == NphotValue) break;
+	return (FALSE);
+      case 1:
+	if (Nm >= NphotValue) break;
+	return (FALSE);
+      case -1:
+	if (Nm <= NphotValue) break;
+	return (FALSE);
+      default:
+	return (FALSE);
+    }
+  }
+
+  /* for TypeSelect, check on TypeCode for this object */
+  if (TypeSelect) {
+    Type = DetermineTypeCode (average, measure, code[0].code);
+    if (Type != TypeValue) return (FALSE);
+  }
+
+  /* for TypeSelect, check on TypeCode for this object */
+  if (TypefracSelect) {
+    typefrac = DetermineTypefrac (average, measure, code);
+    switch (TypefracSign) {
+      case 0:
+	if (typefrac == TypefracValue) break;
+	return (FALSE);	
+      case +1:
+	if (typefrac >= TypefracValue) break;
+	return (FALSE);	
+      case -1:
+	if (typefrac <= TypefracValue) break;
+	return (FALSE);	
+      default:
+	return (FALSE);
+    }
+  }
+
+  return (TRUE);
+}
+
+/* for this function, we don't need to call PhotRel, etc, but we
+   do need to multiply by 0.001:
+   average[].M is stored as 1000*mag where mag is PhotAbs
+   measure[].M for PHOT_REL is the same 
+   XXX EAM : note that we are transitioning away from millimag internal storage 
+*/ 
+
+/* send in:
+   Nphot - photcode number
+   Tphot - photcode type
+   Ns    - secfilt entry (-1 for PRI)
+   &catalog.average[i], 
+   &catalog.measure[catalog.average[i].measureOffset], 
+   &catalog.secfilt[i*Nsec] 
+*/
+
+
+double *ExtractMagnitudes (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *n) {
+  
+  double *M, mag;
+  int N;
+
+  if ((mode == MAG_AVE) || (mode == MAG_REF)) {
+    ALLOCATE (M, double, 1);
+    mag = ExtractAverages (code, mode, average, secfilt, measure, AVE_MAG);
+    if (isnan(mag)) {
+      N = 0;
+    } else {
+      N = 1;
+      M[0] = mag;
+    }
+  } else {
+    M = ExtractMeasures (code, mode, average, secfilt, measure, &N, MEAS_MAG);
+  }
+  
+  *n = N;
+  return (M);
+}
+
+/* extract delta-mag pairs applying specified selections */
+double *ExtractDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist) {
+
+  int i, j, A1, A2, N1, N2, Np, Nlist, NLIST;
+  double *M1, *M2, *list;
+
+  /* check for special case of measure-measure - this is needed to drop self-matches */
+  A1 = ((mode[0] == MAG_AVE) || (mode[0] == MAG_REF));
+  A2 = ((mode[1] == MAG_AVE) || (mode[1] == MAG_REF));
+  if (!A1 && !A2) {
+    list = ExtractMeasuresDMag (code, mode, average, secfilt, measure, nlist);
+    return (list);
+  }
+
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure*average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+  M1 = M2 = NULL;
+
+  /* one of the two is an average, must do independently */
+  M1 = ExtractMagnitudes (code[0], mode[0], average, secfilt, measure, &N1);
+  if (N1 == 0) goto skip;
+  
+  Np = GetSelectionParam ();
+  SetSelectionParam (Np + 1);
+  M2 = ExtractMagnitudes (code[1], mode[1], average, secfilt, measure, &N2);
+  if (N2 == 0) goto skip;
+
+  /* magnitudes may be NAN : set delta to NAN */
+  for (i = 0; i < N1; i++) {
+    for (j = 0; j < N2; j++) {
+      if (isnan(M1[i]) || isnan(M2[j])) {
+	list[Nlist] = NAN;
+      } else {
+	list[Nlist] = M1[i] - M2[j];
+      }
+      Nlist ++;
+    }
+  }
+
+skip: 
+  if (M1 != NULL) free (M1);
+  if (M2 != NULL) free (M2);
+  *nlist = Nlist;
+  return (list);
+}
+  
+/* extract a list of delta-measure-mags from the specified average entry based on the 
+   pre-set selections - does not return self-matched measurements */
+double *ExtractMeasuresDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist) {
+
+  int i, j, Np0, Np1, Nlist, NLIST;
+  double *list, M1, M2;
+  
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure*average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+
+  /* must have two code values - drop this test? this is programming case, not a user case */
+  if (code == NULL) return (list);
+  if (code[0] == NULL) return (list);
+  if (code[1] == NULL) return (list);
+
+  /* exclude based on average parameters for both codes  */
+  if (!TestAverage (code[0], average, secfilt, measure)) return (list);
+  if (!TestAverage (code[1], average, secfilt, measure)) return (list);
+
+  Np0 = GetSelectionParam ();
+  Np1 = Np0 + 1;
+
+  /* loop twice over all measures */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    SetSelectionParam (Np0);
+    TESTCODE (code[0], measure[i]);
+    TESTMEASURE (measure[i]);
+    SETMAG(M1, measure[i], mode[0]);
+    for (j = 0; j < average[0].Nmeasure; j++) {
+      if (i == j) continue;
+      SetSelectionParam (Np1);
+      TESTCODE (code[1], measure[j]);
+      TESTMEASURE (measure[j]);
+      SETMAG(M2, measure[j], mode[1]);
+      if (isnan(M1) || isnan(M2)) {
+	list[Nlist] = NAN;
+      } else {
+	list[Nlist] = M1 - M2;
+      }
+      Nlist ++;
+    }
+  }
+  *nlist = Nlist;
+  return (list);
+}
+
+/* extract a measurement list matching the number of dmag entries */
+double *ExtractByDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+
+  int A1, A2, N1;
+  double *list;
+
+  /* check for special case of measure-measure */
+  A1 = ((mode[0] == MAG_AVE) || (mode[0] == MAG_REF));
+  A2 = ((mode[1] == MAG_AVE) || (mode[1] == MAG_REF));
+  if (!A1 && !A2) {
+    list = ExtractMeasuresByDMag (code, mode, 1, average, secfilt, measure, nlist, param);
+    return (list);
+  }
+
+  /* one of the two entries results in a single element. extract the other */
+  if (A1) {
+    list = ExtractMeasures (code[1], mode[1], average, secfilt, measure, &N1, param);
+  } else {
+    list = ExtractMeasures (code[0], mode[0], average, secfilt, measure, &N1, param);
+  }
+  *nlist = N1;
+  return (list);
+}
+  
+/* extract a list of delta-measure-mags from the specified average entry based on the pre-set selections */
+double *ExtractMeasuresByDMag (PhotCode **code, int *mode, int use_first, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+
+  int i, j, n, Nlist, NLIST;
+  double *list, M1, M2;
+  
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure*average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+
+  /* must have two code values */
+  if (code == NULL) return (list);
+  if (code[0] == NULL) return (list);
+  if (code[1] == NULL) return (list);
+
+  /* chisq, fwhm, Nphot, Ncode */
+  if (!TestAverage (code[0], average, secfilt, measure)) return (list);
+  if (!TestAverage (code[1], average, secfilt, measure)) return (list);
+
+  /* loop twice over all measures */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    TESTCODE (code[0], measure[i]);
+    TESTMEASURE (measure[i]);
+    SETMAG(M1, measure[i], mode[0]);
+    for (j = 0; j < average[0].Nmeasure; j++) {
+      if (i == j) continue;
+      TESTCODE (code[1], measure[j]);
+      TESTMEASURE (measure[j]);
+      SETMAG(M2, measure[j], mode[1]);
+      n = (use_first) ? i : j;
+
+      /* assign value */
+      list[Nlist] = GetMeasure (param, &average[0], &measure[n], (use_first ? M1 : M2));
+      Nlist ++;
+    }
+  }
+  *nlist = Nlist;
+  return (list);
+}
+
+double GetMeasure (int param, Average *average, Measure *measure, double mag) {
+
+  double ra, dec, x, y;
+  double value;
+  Image *image;
+  Coords *mosaic;
+
+  value = 0;
+  switch (param) {
+    case MEAS_MAG: /* magnitudes are already determined above */
+      value = mag;
+      break;
+    case MEAS_RA: /* OK */
+      value = average[0].R - measure[0].dR / 3600.0;
+      break;
+    case MEAS_DEC: /* OK */
+      value = average[0].D - measure[0].dD / 3600.0;
+      break;
+# if (0)
+    case MEAS_dMAG: /* OK */
+      value = measure[0].dM;
+      break;
+    case MEAS_DOPHOT: /* OK */
+      value = measure[0].dophot;
+      break;
+# endif
+    case MEAS_AIRMASS: /* OK */
+      value = measure[0].airmass;
+      break;
+    case MEAS_EXPTIME: /* OK */
+      value = pow (10.0, measure[0].dt * 0.4);
+      break;
+    case MEAS_PHOTCODE: /* OK */
+      value = measure[0].photcode;
+      break;
+    case MEAS_TIME: /* OK */
+      value = TimeValue (measure[0].t, TimeReference, TimeFormat);
+      break;
+    case MEAS_RA_OFFSET: /* OK */
+      value = measure[0].dR;
+      break;
+    case MEAS_DEC_OFFSET: /* OK */
+      value = measure[0].dD;
+      break;
+    case MEAS_FWHM: /* OK */
+      value = 0.01*measure[0].FWx;
+      break;
+    case MEAS_DB_FLAGS: /* ? */
+      value = measure[0].dbFlags;
+      break;
+    case MEAS_XCCD: /* OK */
+/* I need to perform this conversion for ELIXIR and LONEOS formats on load */      
+# if 0
+      value = measure[0].Xccd;
+# else
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      image = MatchImage (measure[0].t, measure[0].photcode);
+      if (image == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, &image[0].coords);
+      value = x;
+# endif
+      break;
+    case MEAS_YCCD: /* OK */
+/* I need to perform this conversion for ELIXIR and LONEOS formats on load */      
+# if 0
+      value = measure[0].Yccd;
+# else
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      image = MatchImage (measure[0].t, measure[0].photcode);
+      if (image == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, &image[0].coords);
+      value = y;
+# endif
+      break;
+    case MEAS_XMOSAIC: /* OK */
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      if (mosaic == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, mosaic);
+      value = x;
+      break;
+    case MEAS_YMOSAIC: /* OK */
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      if (mosaic == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, mosaic);
+      value = y;
+      break;
+  }
+  return (value);
+}
+
+/** the mosaic entries do not use the registered mosaic found 
+    by MatchImage (via FindMosaicForImage).  Rather, they use
+    a coordinate frame saved by SetImageSelection 
+**/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/pmeasure.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/pmeasure.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/pmeasure.c	(revision 22322)
@@ -0,0 +1,243 @@
+# include "dvoshell.h"
+# define NCHUNK 10000
+
+enum {FLAG_IGNORE, FLAG_SKIP, FLAG_KEEP};
+
+int pmeasure (int argc, char **argv) {
+  
+  FILE *f;
+  int i, j, k, m, kapa, Narg, Npts, NPTS, status, VERBOSE, TimeSelect;
+  double Mz, Mr, mag;
+  double Radius, Rmin, Rmax, R, D, trange;
+  unsigned IDclip, IDchoice, LimExclude;
+  unsigned dbFlagChoice, dbFlagClip;
+  unsigned photFlagChoice, photFlagClip;
+  int PhotcodeClip;
+  float *Xvec, *Yvec, *Zvec;
+  time_t tzero, tend;
+
+  SkyTable *sky;
+  SkyList *skylist;
+  Catalog catalog;
+  Graphdata graphmode;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  f = (FILE *) NULL;
+  Mz = 17.0;
+  Mr = -5.0;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  PhotcodeClip = -1;
+  if ((Narg = get_argument (argc, argv, "-p"))) {
+    remove_argument (Narg, &argc, argv);
+    PhotcodeClip = GetPhotcodeCodebyName (argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+  }
+  if ((Narg = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (Narg, &argc, argv);
+    PhotcodeClip = GetPhotcodeCodebyName (argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+  }
+  IDchoice = 0;
+  IDclip = FALSE;
+  if ((Narg = get_argument (argc, argv, "-ID"))) {
+    remove_argument (Narg, &argc, argv);
+    IDchoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    IDclip = TRUE;
+  }
+  dbFlagChoice = 0;
+  dbFlagClip = FLAG_IGNORE;
+  if ((Narg = get_argument (argc, argv, "-dbflag"))) {
+    remove_argument (Narg, &argc, argv);
+    dbFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    dbFlagClip = FLAG_SKIP;
+  }
+  if ((Narg = get_argument (argc, argv, "+dbflag"))) {
+    remove_argument (Narg, &argc, argv);
+    dbFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    dbFlagClip = FLAG_KEEP;
+  }
+  photFlagChoice = 0;
+  photFlagClip = FLAG_IGNORE;
+  if ((Narg = get_argument (argc, argv, "-photflag"))) {
+    remove_argument (Narg, &argc, argv);
+    photFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    photFlagClip = FLAG_SKIP;
+  }
+  if ((Narg = get_argument (argc, argv, "+photflag"))) {
+    remove_argument (Narg, &argc, argv);
+    photFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    photFlagClip = FLAG_KEEP;
+  }
+
+  TimeSelect = FALSE;
+  if ((Narg = get_argument (argc, argv, "-time"))) {
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_time (argv[Narg], &tzero)) {
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_dtime (argv[Narg], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((Narg = get_argument (argc, argv, "-trange"))) {
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_time (argv[Narg], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_time (argv[Narg], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+
+  LimExclude = FALSE;
+  if ((Narg = get_argument (argc, argv, "-x"))) {
+    remove_argument (Narg, &argc, argv);
+    LimExclude = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((Narg = get_argument (argc, argv, "-v"))) {
+    remove_argument (Narg, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  if ((Narg = get_argument (argc, argv, "-m"))) {
+    remove_argument (Narg, &argc, argv);
+    Mr  = atof(argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+    Mz = atof(argv[Narg]);
+    Mr = Mr - Mz;
+    remove_argument (Narg, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: pmeasure (-all) [-m M M]\n");
+    gprint (GP_ERR, " options:\n");
+    gprint (GP_ERR, " [-p photcode]\n");
+    gprint (GP_ERR, " [-photcode photcode]\n");
+    gprint (GP_ERR, " [-ID ID]\n");
+    gprint (GP_ERR, " [-dbflag value] : skip matches to these flags\n");
+    gprint (GP_ERR, " [+dbflag value] : keep matches to these flags\n");
+    gprint (GP_ERR, " [-photflag value] : skip matches to these flags\n");
+    gprint (GP_ERR, " [+photflag value] : keep matches to these flags\n");
+    gprint (GP_ERR, " [-time (start) (duration)]\n");
+    gprint (GP_ERR, " [-trange (start) (stop)]\n");
+    gprint (GP_ERR, " [-x] : exclude points larger / smaller than mag limits\n");
+    gprint (GP_ERR, " [-v] : verbose mode\n");
+    return (FALSE);
+  }
+  graphmode.style = 2; /* set style to points */
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0; /* no errorbars */
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+  
+  /* storage for plotting the points */
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (Xvec, float, NPTS);
+  ALLOCATE (Yvec, float, NPTS);
+  ALLOCATE (Zvec, float, NPTS);
+
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* project stars to screen display coords */
+    for (i = 0; i < catalog.Naverage; i++) {
+      if (IDclip && (catalog.average[i].code != IDchoice)) continue;
+      while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+      while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+      m = catalog.average[i].measureOffset;
+      for (k = 0; k < catalog.average[i].Nmeasure; k++) {
+	if ((dbFlagClip == FLAG_SKIP) &&  (catalog.measure[m+k].dbFlags & dbFlagChoice)) continue;
+	if ((dbFlagClip == FLAG_KEEP) && !(catalog.measure[m+k].dbFlags & dbFlagChoice)) continue;
+	if ((photFlagClip == FLAG_SKIP) &&  (catalog.measure[m+k].photFlags & photFlagChoice)) continue;
+	if ((photFlagClip == FLAG_KEEP) && !(catalog.measure[m+k].photFlags & photFlagChoice)) continue;
+	if (TimeSelect && (catalog.measure[m+k].t < tzero)) continue;
+	if (TimeSelect && (catalog.measure[m+k].t > tzero + trange)) continue;
+	if ((PhotcodeClip != -1) && (catalog.measure[m+k].photcode != PhotcodeClip)) continue;
+	mag = PhotCat (&catalog.measure[m+k]);
+	Zvec[Npts] = MIN (1.0, MAX (0.01, (mag - Mz) / Mr));
+	if (LimExclude && (Zvec[Npts] > 0.99)) continue;
+	if (Zvec[Npts] < 0.011) continue;
+	R = catalog.average[i].R - catalog.measure[m+k].dR/3600.0;
+	D = catalog.average[i].D - catalog.measure[m+k].dD/3600.0;
+	// XXX drop this check
+	if ((R < Rmin) || (R > Rmax) || (D < -90.0) || (D > 90.0)) {
+	  char *date;
+	  date = ohana_sec_to_date (catalog.measure[m+k].t);
+	  gprint (GP_LOG, "out: %f, %f : %s : (%f, %f) + (%f, %f)\n", R, D, date, catalog.average[i].R, catalog.average[i].D, catalog.measure[m+k].dR/3600.0, catalog.measure[m+k].dD/3600.0);
+	  free (date);
+	}
+	status = fRD_to_XY (&Xvec[Npts], &Yvec[Npts], R, D, &graphmode.coords);
+	if (!status) {
+	  fprintf (stderr, ".");
+	  continue;
+	}
+	Npts ++;
+
+	if (Npts == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (Xvec, float, NPTS);
+	    REALLOCATE (Yvec, float, NPTS);
+	    REALLOCATE (Zvec, float, NPTS);
+	}
+	if (Npts > NCHUNK) {
+	    PlotVectorTriplet (kapa, Npts, Xvec, Yvec, Zvec, &graphmode);
+	    Npts = 0;
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+  }
+  if (Npts > 0) PlotVectorTriplet (kapa, Npts, Xvec, Yvec, Zvec, &graphmode);
+
+  free (Xvec);
+  free (Yvec);
+  free (Zvec);
+
+  return (TRUE);
+
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/procks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/procks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/procks.c	(revision 22322)
@@ -0,0 +1,153 @@
+# include "dvoshell.h"
+
+typedef struct {
+  double ra[3];
+  double dec[3];
+  double X[3];
+  double Y[3];
+  unsigned int t[3];
+  double mag[3];
+} Rocks;
+static Rocks *rocks = (Rocks *) NULL;
+static int   Nrocks;
+
+int procks (int argc, char **argv) {
+  
+  FILE *f;
+  Vector Xvec, Yvec;
+  int kapa, i, j, N, NROCKS;
+  int N0, N1, SpeedClip, Reload;
+  unsigned int t0, t1;
+  double Mz, Mr, S0, S1;
+  double Rmin, Rmax;
+  Graphdata graphmode;
+  char rockcat[256];
+
+  VarConfig ("ROCK_CATALOG", "%s", rockcat);
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+
+  f = (FILE *) NULL;
+  Mz = 17.0;
+  Mr = -5.0;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  if ((N = get_argument (argc, argv, "-m"))) {
+    remove_argument (N, &argc, argv);
+    Mr  = 1000*atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Mz = 1000*atof(argv[N]);
+    Mr = Mr - Mz;
+    remove_argument (N, &argc, argv);
+  }
+
+  S0 = S1 = 0;
+  SpeedClip = FALSE;
+  if ((N = get_argument (argc, argv, "-speed"))) {
+    SpeedClip = TRUE;
+    remove_argument (N, &argc, argv);
+    S0 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    S1 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Reload = FALSE;
+  if ((N = get_argument (argc, argv, "-reload"))) {
+    Reload = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: procks [-m M M] [-speed s s] \n");
+    return (FALSE);
+  }
+  
+  f = fopen (rockcat, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open rock catalog\n");
+    return (TRUE);
+  }
+
+  if ((rocks == (Rocks *) NULL) || Reload) {
+    if (rocks != (Rocks *) NULL) free (rocks);
+    NROCKS = 100;
+    ALLOCATE (rocks, Rocks, NROCKS);
+    for (i = 0; fscanf (f, "%lf %lf %d%lf%lf%lf %d%lf%lf%lf %d%lf%lf%lf", 
+			&rocks[i].X[0], &rocks[i].Y[0], 
+			&rocks[i].t[0], &rocks[i].ra[0], &rocks[i].dec[0], &rocks[i].mag[0], 
+			&rocks[i].t[1], &rocks[i].ra[1], &rocks[i].dec[1], &rocks[i].mag[1], 
+			&rocks[i].t[2], &rocks[i].ra[2], &rocks[i].dec[2], &rocks[i].mag[2]
+			) != EOF; i++) {
+      if (i == NROCKS - 1) {
+	NROCKS += 100;
+	REALLOCATE (rocks, Rocks, NROCKS);
+      }
+    }
+    Nrocks = i;
+  }
+      
+  if (Nrocks == 0) {
+    free (rocks);
+    gprint (GP_ERR, "no rocks in datafile\n");
+    return (TRUE);
+  }
+
+  /* data has been loaded, get ready to plot it */
+  Yvec.Nelements = Xvec.Nelements = 3*Nrocks;
+  ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+  
+  /* project stars to screen display coords */
+  for (N = i = 0; i < Nrocks; i++) {
+    if (SpeedClip && ((rocks[i].Y[0] < S0) || (rocks[i].Y[0] > S1))) continue;
+    for (j = 0; j < 3; j++) {
+      while (rocks[i].ra[j] < Rmin) rocks[i].ra[j] += 360.0;
+      while (rocks[i].ra[j] > Rmax) rocks[i].ra[j] -= 360.0;
+      fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], rocks[i].ra[j], rocks[i].dec[j], &graphmode.coords);
+      N ++;
+    }
+  }
+  Yvec.Nelements = Xvec.Nelements = N;
+  
+  graphmode.style = 2; /* set style to points */
+  graphmode.etype = 0; /* no errorbars */
+  PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+  /* now plot vectors between two extrema */
+  Yvec.Nelements = Xvec.Nelements = 2*Nrocks;
+  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+  
+  /* project stars to screen display coords */
+  for (N = i = 0; i < Nrocks; i++) {
+    if (SpeedClip && ((rocks[i].Y[0] < S0) || (rocks[i].Y[0] > S1))) continue;
+    N0 = N1 = 0;
+    t0 = t1 = rocks[i].t[0];
+    for (j = 1; j < 3; j++) {
+      if (rocks[i].t[j] < t0) { N0 = j; t0 = rocks[i].t[j]; }
+      if (rocks[i].t[j] > t1) { N1 = j; t1 = rocks[i].t[j]; }
+    }
+    while (rocks[i].ra[N0] < Rmin) rocks[i].ra[N0] += 360.0;
+    while (rocks[i].ra[N0] > Rmax) rocks[i].ra[N0] -= 360.0;
+    while (rocks[i].ra[N1] < Rmin) rocks[i].ra[N1] += 360.0;
+    while (rocks[i].ra[N1] > Rmax) rocks[i].ra[N1] -= 360.0;
+    fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], rocks[i].ra[N0], rocks[i].dec[N0], &graphmode.coords);
+    N ++;
+    fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], rocks[i].ra[N1], rocks[i].dec[N1], &graphmode.coords);
+    N ++;
+  }
+  Yvec.Nelements = Xvec.Nelements = N;
+  
+  graphmode.style = 2; /* set style to points */
+  graphmode.ptype = 100; /* connect pairs */
+  graphmode.etype = 0; /* no errorbars */
+
+  PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  return (TRUE);
+
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/region_list.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/region_list.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/region_list.c	(revision 22322)
@@ -0,0 +1,215 @@
+# include "dvoshell.h"
+
+/* XXX note : for RegionName or RegionList, we need to free the skylist
+   elements, but not in the case of radius selection - this implies 
+   information carried back up */
+
+static char *CATDIR  = NULL;
+static SkyTable *sky = NULL;
+
+int SetCATDIR (char *path, int verbose) {
+
+  char *newpath;
+  char catdir_config[256];
+  char gscfile[256];
+  char skyfile[256];
+  int  skydepth;
+
+  /* find CATDIR in config system */
+  if (path == NULL) {
+    if (VarConfig ("CATDIR", "%s", catdir_config) == NULL) return (FALSE);
+    newpath = catdir_config;
+  } else {
+    newpath = path;
+  }
+
+  if (CATDIR != NULL) free (CATDIR);
+  CATDIR = strcreate (newpath);
+
+  if (VarConfig ("GSCFILE",  "%s", gscfile) == NULL) gscfile[0] = 0;
+  if (VarConfig ("SKYFILE",  "%s", skyfile) == NULL) skyfile[0] = 0;
+  if (VarConfig ("SKYDEPTH", "%d", &skydepth) == NULL) skydepth = 2;
+
+  if (verbose) {
+      gprint (GP_ERR, "CATDIR %s\n", CATDIR);
+      gprint (GP_ERR, "GSCFILE %s\n", gscfile);
+      gprint (GP_ERR, "SKYFILE %s\n", skyfile);
+      gprint (GP_ERR, "SKYDEPTH %d\n", skydepth);
+  }
+
+  /* load the SkyTable at this point */
+  /* set the image path as well */
+
+  if (sky != NULL) SkyTableFree (sky);
+  sky = SkyTableLoadOptimal (CATDIR, skyfile, gscfile, skydepth, verbose);
+  if (sky == NULL) return FALSE;
+
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+
+  return (TRUE);
+}
+
+char *GetCATDIR () {
+  if (CATDIR == NULL) {
+    SetCATDIR (NULL, FALSE);
+  }
+  return (CATDIR);
+}
+
+SkyTable *GetSkyTable () {
+  if (sky == NULL) {
+    SetCATDIR (NULL, FALSE);
+  }
+  return (sky);
+}
+
+void FreeSkyRegionSelection (SkyRegionSelection *selection) {
+
+  if (selection == NULL) return;
+  if (selection[0].name != NULL) free (selection[0].name);
+  if (selection[0].list != NULL) free (selection[0].list);
+  free (selection);
+}
+
+SkyRegionSelection *SetRegionSelection (int *argc, char **argv) {
+  
+  int N;
+  SkyRegionSelection *selection;
+
+  ALLOCATE (selection, SkyRegionSelection, 1);
+  selection[0].name = NULL;
+  selection[0].list = NULL;
+  selection[0].useDisplay = FALSE;
+  selection[0].useSkyregion = FALSE;
+
+  /* check for Region selection (named dvo catalog file) */
+  if ((N = get_argument (*argc, argv, "-cpt"))) {
+    remove_argument (N, argc, argv);
+    selection[0].name = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+    return selection;
+  }    
+
+  /* check for Region list (file containing dvo catalog file list)*/
+  if ((N = get_argument (*argc, argv, "-cptlist"))) {
+    remove_argument (N, argc, argv);
+    selection[0].list = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+    return selection;
+  } 
+
+  /* check for Region selection from display */
+  if ((N = get_argument (*argc, argv, "-region"))) {
+    remove_argument (N, argc, argv);
+    selection[0].useDisplay = TRUE;
+    return selection;
+  }    
+
+  /* check for Region selection from display */
+  if ((N = get_argument (*argc, argv, "-skyregion"))) {
+    if (N + 5 >= *argc) {
+      gprint (GP_ERR, "USAGE: -skyregion (RA) (RA) (DEC) (DEC)\n");
+      FreeSkyRegionSelection (selection);
+      return NULL;
+    }
+    remove_argument (N, argc, argv);
+    selection[0].useSkyregion = TRUE;
+    set_skyregion (atof(argv[N]), atof(argv[N+1]), atof(argv[N+2]), atof(argv[N+3]));
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    return selection;
+  }    
+
+  /* default to pre-defined sky region */
+  selection[0].useSkyregion = TRUE;
+  return selection;
+}
+
+/* given possible options (by name, by list, by graph region), select SkyRegions */
+SkyList *SelectRegions (SkyRegionSelection *selection) {
+
+  SkyList *skylist;
+
+  /* determine region-file names */
+  if (selection->name != NULL) {
+    char filename[256];
+
+    ALLOCATE (skylist, SkyList, 1);
+    ALLOCATE (skylist[0].regions, SkyRegion *, 1);
+    ALLOCATE (skylist[0].regions[0], SkyRegion, 1);
+    ALLOCATE (skylist[0].filename, char *, 1);
+    skylist[0].ownElements = TRUE; // free these elements when freeing the list
+    
+    strcpy (skylist[0].regions[0][0].name, selection->name);
+    sprintf (filename, "%s/%s.cpt", CATDIR, selection->name);
+    skylist[0].filename[0] = strcreate (filename);
+    return (skylist);
+  } 
+
+  if (selection->list != NULL) {
+    skylist = SkyListLoadFile (selection->list);
+    return (skylist);
+  }
+
+  if (selection->useDisplay) {
+    double Radius;
+    Graphdata graphsky;
+
+    if (!GetGraphData (&graphsky, NULL, NULL)) {
+      gprint (GP_ERR, "region display not available\n");
+      return (NULL);
+    }
+
+    Radius = MAX (fabs(graphsky.xmax), fabs(graphsky.ymax));
+    skylist = SkyListByRadius (sky, -1, graphsky.coords.crval1, graphsky.coords.crval2, Radius);
+    return (skylist);
+  }
+
+  if (selection->useSkyregion) {
+    double Rmin, Rmax, Dmin, Dmax;
+
+    get_skyregion (&Rmin, &Rmax, &Dmin, &Dmax);
+    skylist = SkyListByBounds (sky, -1, Rmin, Rmax, Dmin, Dmax);
+    return (skylist);
+  }    
+
+  return NULL;
+}
+
+/* returns a list of region files names from file */
+SkyList *SkyListLoadFile (char *filename) {
+  
+  FILE *f;
+  int NREGIONS, Nregions;
+  SkyList *skylist;
+
+  ALLOCATE (skylist, SkyList, 1);
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't find region list file %s\n", filename);
+    skylist[0].Nregions = 0;
+    skylist[0].regions = NULL;
+    return (skylist);
+  }
+  
+  Nregions = 0;
+  NREGIONS = 50;
+  ALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS);
+  ALLOCATE (skylist[0].filename, char *, NREGIONS);
+  skylist[0].ownElements = TRUE; // free these elements when freeing the list
+
+  while (fscanf (f, "%s", filename) != EOF) {
+    ALLOCATE (skylist[0].regions[Nregions], SkyRegion, 1);
+    strcpy (skylist[0].regions[Nregions][0].name, filename);
+    sprintf (filename, "%s/%s.cpt", CATDIR, skylist[0].regions[Nregions][0].name);
+    skylist[0].filename[Nregions] = strcreate (filename);
+    Nregions ++;
+    CHECK_REALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS, Nregions, 50);
+  }
+  skylist[0].Nregions = Nregions;
+  return (skylist);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/scripts/navigate
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/scripts/navigate	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/scripts/navigate	(revision 22322)
@@ -0,0 +1,623 @@
+# -*- perl -*-
+
+macro navigate
+  style -n 0
+  limits
+  $DRAWSTARS  = -1
+  $DRAWIMAGES =  1
+  $DRAWGRID   = -1
+  $ZOOM = 180 / ($YMAX - $YMIN)
+  #this should be changed to a while loop, except 'while' is broken for some reason
+  $KEY = "none"
+  while ("$KEY" != "q")
+    cursor 1
+
+    # help list
+    if ("$KEY" == "h")
+     echo "Arrow Keys - pan in that direction"
+     echo "PgUp,PgDn - zoom in/out a factor of 1.2"
+     echo "Home,End  - zoom in/out a factor of 2"
+     echo "1 - zoom in factor of 2 at the cursor"
+     echo "2 - zoom in factor of 1.2 at the cursor"
+     echo "3 - recenter at cursor"
+     echo "4 - zoom out factor of 1.2 at the cursor"
+     echo "5 - zoom out factor of 2 at the cursor"
+     echo "6 - zoom out factor of 10 at the cursor"
+     echo "z - zoom to radius (requires 2nd keystroke)"
+     echo "f - show full sky"
+     echo ""
+     echo "q - quit"
+     echo "S - toggle auto-plotting of stars"
+     echo "A - toggle auto-plotting of image borders"
+     echo "g - toggle skygrid on/off"
+     echo "c - plot status catalog boundaries"
+     echo "C - list catalog at cursor location"
+     echo "i - list info about images touching cursor location" 
+     echo "I - list info about images, with pixel coords of cursor position"
+     echo "j - adjust mag scale, +0.5"
+     echo "k - adjust mag scale, -0.5"
+     echo "J - adjust dmag scale, /1.25"
+     echo "K - adjust dmag scale, *1.25"
+     echo "r - plot detected asteroids (rocks)"
+     echo "l - plot HST GSC"
+     echo "L - plot Landolt stars"
+     echo "m - list measurements for stars within 1 pixel of cursor"
+     echo "M - list measurements for stars within 1.8 arcsec of cursor"
+     echo "p - ****(don't know what this does)"
+     echo "s - ****(don't know what this does)"
+     echo "t - plot light curve for star within 2 arcsec of cursor position"
+     echo "T - plot 'galaxy' light curve for star within 2 arcsec of cursor position"
+     echo "u - ****(don't know what this does)"
+     echo "x - plot stars scaled by magnitude Chisq"
+     echo "X - plot stars by magnitude scatter"
+     echo "y - ****(don't know what this does)"
+     echo ""
+     echo "@ - execute macro `user_macro`"
+     echo ": - ****input a line and execute (not yet implemented)"
+    end
+
+    # quit from navigate
+    if ("$KEY" == "q") continue
+
+    #pan controls
+    if (("$KEY" == "Left") || ("$KEY" == "Right") || ("$KEY" == "Up") || ("$KEY" == "Down"))
+      $SHIFT = 0.2
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      #assumes standard sky orientation!! (N up, E left)
+      if ("$KEY"=="Left")
+        $R$KEY = $R$KEY + $SHIFT*$XMAX
+      end
+      if ("$KEY"=="Right")
+        $R$KEY = $R$KEY + $SHIFT*$XMIN
+      end
+      if ("$KEY"=="Up")
+        $D$KEY = $D$KEY + $SHIFT*$YMAX
+      end
+      if ("$KEY"=="Down")
+        $D$KEY = $D$KEY + $SHIFT*$YMIN
+      end
+      #pretend like I hit '3' in the place to recenter it
+      nav_zoom 1      
+    end
+
+    # NEW zoom controls
+    if (("$KEY" == "Prior") || ("$KEY" == "Next") || ("$KEY" == "Home") || ("$KEY" == "End") || ("$KEY" == "Button4") || ("$KEY" == "Button5"))    
+      #move where key was hit to center      
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      if ("$KEY" == "Prior")
+        $zfac=1.2
+      end
+      if ("$KEY" == "Next")
+        $zfac={1/1.2}
+      end
+      if ("$KEY" == "Home")
+        $zfac=2
+      end
+      if ("$KEY" == "End")
+        $zfac={1/2.}
+      end
+      if ("$KEY"=="Button4")
+        $zfac=1.6
+      end
+      if ("$KEY"=="Button5")
+        $zfac={1/1.6}
+      end
+      nav_zoom $zfac
+    end
+
+    if ("$KEY"=="Button1")
+      nav_zoom 1
+    end
+    if ("$KEY"=="Button2")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY"=="Button3")
+      nav_zoom 2
+    end
+
+
+
+    # zoom controls
+    if ("$KEY" == "1")
+      nav_zoom 2
+    end
+    if ("$KEY" == "2")
+      nav_zoom 1.2
+    end
+    if ("$KEY" == "3")
+      nav_zoom 1
+    end
+    if ("$KEY" == "4")
+      nav_zoom {1/1.2}
+    end
+    if ("$KEY" == "5")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY" == "6")
+      nav_zoom {1/20.}
+    end
+
+ 
+   # measure distance
+    if ("$KEY" == "d")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'd' again at endpoint"
+      cursor 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = 3600*((dcos($d0)*($r0-$r1))^2 + ($d0-$d1)^2)^0.5
+      echo "$dr arcsec"
+    end
+    # show ra, dec
+    if ("$KEY" == "w")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      echo "$tmp $D$KEY" 
+      exec echo $tmp $D$KEY | radec -hh
+    end
+    # zoom to radius
+    if ("$KEY" == "z")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'z' again at radius"
+      cursor 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = (($r0-$r1)^2 + ($d0-$d1)^2)^0.5
+      $ZOOM = $RAD / $dr
+      nav_recenter
+      nav_redraw
+      $KEY = $ok
+      $R$KEY = $r0
+      $D$KEY = $d0
+    end
+
+    # adjust mag scaling
+    if ("$KEY" == "J")
+      $MAG = $MAG - 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "K")
+      $MAG = $MAG + 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "j")
+      $dMAG = $dMAG * 0.8
+      nav_redraw
+    end
+    if ("$KEY" == "k")
+      $dMAG = $dMAG * 1.25
+      nav_redraw
+    end
+    echo "mag, dmag: $MAG, $dMAG"
+
+
+
+    # plot full sky
+    if ("$KEY" == "f") 
+      echo "full"
+      $ZOOM = 1
+      resize 1150 600		      
+      region 0 0 90 ait
+      $RMIN = 0
+      $RMAX = 360
+      $DMIN = -90
+      $DMAX = +90
+      style -c red; cgrid
+      style -c black
+      images
+    end
+
+    # plot rocks
+    if ("$KEY" == "r") 
+#      plot.rocks
+      style -c blue   -pt 1; procks -speed 0.0041 1
+      style -c red    -pt 1; procks -speed 0.00041 0.0041
+      style -c indigo -pt 1; procks -speed 0 0.00041
+      style -c black -lw 0;
+    end
+    # plot HST-GSC
+    if ("$KEY" == "l") 
+      style -c blue -pt 7; cat -all -g -m 9 16
+      style -c black
+    end
+    # plot Landolt
+    if ("$KEY" == "L") 
+#      style -c red  -lw 2 -pt 3; cat -a 1 2 3 /data/elixir/srcdir/refs/stetson/stetsonBn.txt -m 9 18
+#      style -c blue -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.fix -m 9 18
+      style -c red -lw 2 -pt 7; cat -a 1 2 3 /data/elixir/srcdir/refs/sdss/g_SDSS.dat -m 9 14
+#      style -c red -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.hq -m 9 18
+#      style -c red -lw 2 -pt 3; cat -a 22 23 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.unfix -m 9 18
+#      style -c blue -lw 2 -pt 7; cat -a 1 2 4 /data/elixir/srcdir/refs/landolt/extreme/extreme.match -m 0 20
+#      style -x 2 -c red -pt 7 ; cplot RA DEC
+      style -c black -lw 0
+    end
+
+    # list star measurements
+    if ("$KEY" == "m") 
+        $dR = $RAD/$ZOOM/300
+        if ($dR < 0.0005)
+	 $dR = 0.0005
+        end
+	gstar $R$KEY $D$KEY $dR -m
+    end
+
+    # plot mag residuals
+    if ("$KEY" == "R") 
+      echo "filter: "
+      cursor 1
+      clear -n 1 -s; lim 10 22 -0.2 0.2; clear; box
+      dmags $KEY\:rel - $KEY : $KEY -type 0
+      plot -x 2 -pt 0 -sz 0.3 -c red yv xv
+      dmags $KEY\:rel - $KEY : $KEY -type 0 -flag 0 -nphot +3 -chisq 2.0
+      plot -x 2 -pt 2 -sz 0.5 -c black yv xv
+      $KEY = R
+      style -n 0
+    end
+
+    if ("$KEY" == "M") 
+	gstar $R$KEY $D$KEY 0.0005 -m
+    end
+    # list images
+    if ("$KEY" == "i") 
+	gimages $R$KEY $D$KEY
+    end
+    if ("$KEY" == "I") 
+	gimages $R$KEY $D$KEY -pix
+    end
+
+    #toggle images on / off
+    if ("$KEY" == "A")
+      $DRAWIMAGES = $DRAWIMAGES * -1
+      if ($DRAWIMAGES == 1)
+        images
+      end
+    end
+    # toggle stars on / off
+    if ("$KEY" == "S")
+      $DRAWSTARS = $DRAWSTARS * -1
+      if (($ZOOM > 20) && ($DRAWSTARS == 1))
+       style -pt 7
+       pmeasure -all -m $MAG {$MAG + $dMAG}
+      end
+    end
+    # turn grid on / off
+    if ("$KEY" == "g")
+      $DRAWGRID = $DRAWGRID * -1
+      if (($ZOOM > 20) && ($DRAWGRID==1))
+        style -c black; cgrid
+      end
+      if (($ZOOM > 20) && ($DRAWGRID==-1))
+        nav_redraw
+      end
+    end
+
+    # plot light-curve interactive
+    if ("$KEY" == "t")
+      style -n 1 -pt 2 -x 2
+      clear
+      if ($R$KEY < 0) 
+       $R$KEY = $R$KEY + 360
+      end
+      lcurve -l $R$KEY $D$KEY {30/3600} -d -v time mag
+      box
+      lcv
+      style -n 0
+    end
+    # plot light-curve 
+    if ("$KEY" == "T")
+      style -n 1 -pt 1 -c red -x 2
+      lcurve $R$KEY $D$KEY {30/3600} -d
+      style -c black
+      style -n 0 
+    end
+    # plot catalogs
+    if ("$KEY" == "c")
+      style -c blue; pcat; style -c black
+    end
+    # list catalogs
+    if ("$KEY" == "C")
+      gcat $R$KEY $D$KEY
+    end
+
+    # plot image chisqs
+    if ("$KEY" == "x") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME Xm -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c blue
+       czplot ra dec Xm 3 30
+       style -c black -pt 1
+    end
+    # plot meas errors
+    if ("$KEY" == "X") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME dM -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c red
+       czplot ra dec dM 0 30
+       style -c black -pt 1
+    end
+
+
+    # temp plot for skyprobe
+    if ("$KEY" == "u") 
+      imextract -region time
+      imextract -region mcal
+      imextract -region airmass
+      imextract -region nstar
+      vstat time
+      clear -n 1;
+      section a 0 0.00 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} -0.8 -0.5; box; plot time mcal
+      section b 0 0.33 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3}  0.95 3.0; box; plot time airmass
+      section c 0 0.66 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} 0 3000; box; plot time nstar
+      style -n 0
+    end
+    if ("$KEY" == "s")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      $line = `echo $tmp $D$KEY | radec -hh`
+      imextract -region photcode
+      imextract -region time
+     
+      subset t = time if (int(photcode/100) == 1)
+      uniq t T
+      $Bn = t[]
+      $BN = T[]
+      
+      subset t = time if (int(photcode/100) == 2)
+      uniq t T
+      $Vn = t[]
+      $VN = T[]
+      
+      subset t = time if (int(photcode/100) == 3)
+      uniq t T
+      $Rn = t[]
+      $RN = T[]
+      
+      subset t = time if (int(photcode/100) == 4)
+      uniq t T
+      $In = t[]
+      $IN = T[]
+     
+      echo "$line  $Bn $BN  $Vn $VN  $Rn $RN  $In $IN"
+    end
+
+    if ("$KEY" == "p") 
+      echo "P - new coords; p - old coords"
+      cursor 1
+      exec echo $Rp $Dp $RP $DP >> fix.coords
+    end
+
+    if ("$KEY" == "y")
+      ccd I - 2MASS_J : 2MASS_J - 2MASS_K
+      lim -n 1 -1 10 -1 3; clear; box; plot -x 2 -pt 2 -sz 0.5 xv yv
+      dev -n 0 -g
+    end
+
+    #  User-defined macro
+    if ("$KEY" == "at")
+      user_macro
+    end
+
+    if ("$KEY" == "colon")
+      #make this work similar to ':' in vi or iraf
+      #does not work correctly now.
+      scan stdin line
+      $line
+    end
+
+  end
+end
+
+#define this so navigate doesn't crash if you try to call it.
+#If you define a user_macro, be sure to do so AFTER this in .dvorc.
+macro user_macro
+  #echo "success!"
+  $do_nothing=0
+end
+    
+
+macro nav_zoom
+  $ZOOM = $ZOOM * $1
+  nav_recenter
+  nav_redraw
+  $Rnum = $R$KEY		      
+  $Dnum = $D$KEY
+  $KEY = num
+end
+
+macro nav_recenter
+  region $R$KEY $D$KEY {$RAD/$ZOOM} sin
+  #assumes standard sky orientation!! (N up, E left)
+  $RMIN = $R$KEY + $XMIN
+  $RMAX = $R$KEY + $XMAX
+  $DMIN = $D$KEY + $YMIN
+  $DMAX = $D$KEY + $YMAX
+end
+
+macro nav_redraw
+  clear
+  if ($ZOOM <= 20) 
+    style -c red; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWGRID==1))
+    style -c black; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWSTARS == 1))
+    pmeasure -all -m $MAG {$MAG + $dMAG}
+  end    
+  style -c black
+  if ($DRAWIMAGES == 1)
+    images
+  end
+end
+
+
+
+#==================================================
+#=================   END BSNAV   ==================
+#==================================================
+
+
+macro sigclip
+  if ("$0" == "1")
+    echo ""
+    echo "sigclip <clipvector> <N_iterations> <N_sigma> [other vectors ..]"
+    echo ""
+  end
+
+  #required parameters
+  $CLIPVECT = $1
+  $NITERATE = $2
+  $NSIGCLIP = $3
+  
+  for i 0 $NITERATE
+    vstat -q $CLIPVECT
+    #clip boundaries
+    $top = $MEAN + ($NSIGCLIP*$SIGMA)
+    $bot = $MEAN - ($NSIGCLIP*$SIGMA)
+    
+    #clip it good.
+    subset temp = $CLIPVECT if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+    
+    #if you specify other vectors, clip the same elements from them too.
+    #they must all be the same length, of course!!
+    if ($0>4)
+      for j 4 $0
+        subset $$j = $$j if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+      end
+    end
+    
+    #copy temp back to $CLIPVECT and reiterate!
+    delete $CLIPVECT
+    concat temp $CLIPVECT  
+  end
+end
+
+
+macro binvec
+  if ("$0" == "1")
+    echo ""
+    echo "binvec <vec> <Nbins> [other vectors...]"
+    echo ""
+    echo "Bin the vector 'vec' into Nbin bins.  Listing other vectors will"
+    echo "put the corresponding elements of those into other vectors which"
+    echo "are the subset of the vector in that bin.  (That can probably be"
+    echo "stated better.)  This macro makes lots of new vectors.  Hooray!"
+    echo ""
+    echo "Creates"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $NBINS   = $2
+
+  vstat -q $binvect
+  $step = ($MAX-$MIN)/$NBINS
+  $vmin = $MIN
+  $vmax = $MAX
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>3)
+      for j 3 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
+
+macro binvec.2
+  if ("$0" == "1")
+    echo ""
+    echo "binvec.2 <vec> <min> <max> <binsize> [other vectors...]"
+    echo ""
+    echo ""
+    echo "see also 'binvec'"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $vmin    = $2
+  $vmax    = $3
+  $step    = $4
+
+  $NBINS = ($MAX-$MIN)/$step
+
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>5)
+      for j 5 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/showtile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/showtile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/showtile.c	(revision 22322)
@@ -0,0 +1,95 @@
+# include "dvoshell.h"
+static float dr[] = {0.0, 1.0, 1.0, 0.0};
+static float dd[] = {0.0, 0.0, 1.0, 1.0};
+
+int showtile (int argc, char **argv) {
+
+  int kapa, Nd, N, NPTS, status, i, InPic;
+  Graphdata graphmode;
+  Coords coords;
+  Vector Xvec, Yvec;
+  float r, d, R, D;
+  float Ro[90], Do[90];
+
+  /* show tile pattern in viewed region */
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: showtile [option]\n");
+    return (FALSE);
+  }
+  
+  N = 0;
+  NPTS = 200;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+
+  /* starting position */
+
+  /* reference for coords is this image */
+  coords.crpix1 = coords.crpix2 = 0.0;
+  coords.crval1 = coords.crval2 = 0.0;
+  coords.cdelt1 = coords.cdelt2 = 1.0;
+  coords.pc1_1  = coords.pc2_2  = 1.0;
+  coords.pc1_2  = coords.pc2_1  = 0.0;
+  coords.Npolyterms = 0;
+  strcpy (coords.ctype, "RA---TAN");
+  
+  /* fill in top-left region */
+  for (r = 0; r < 3; r += 1.0) {
+    gprint (GP_ERR, "r: %f\n", r);
+    for (Nd = d = 0; d < 90; Nd ++, d += 1.0) {
+      if (r == 0) {
+	coords.crval1 = r;
+	coords.crval2 = d;
+      } else {
+	coords.crval1 = Ro[Nd];
+	coords.crval2 = Do[Nd];
+      }
+      for (i = 0; i < 4; i++) {
+	fXY_to_RD (&R, &D, dr[i], dd[i], &coords);
+	status |= fRD_to_XY (&Xvec.elements[N+2*i], &Yvec.elements[N+2*i], R, D, &graphmode.coords);
+	if (i > 0) {
+	  Xvec.elements[N+2*i - 1] = Xvec.elements[N+2*i];
+	  Yvec.elements[N+2*i - 1] = Yvec.elements[N+2*i];
+	}
+	if (i == 1) {
+	  Ro[Nd] = R;
+	  Do[Nd] = D;
+	}
+      }
+      Xvec.elements[N+7] = Xvec.elements[N];
+      Yvec.elements[N+7] = Yvec.elements[N];
+
+      /* check if any corner is in plotting region */
+      InPic = FALSE;
+      for (i = 0; i < 8; i+=2) {
+	if ((Xvec.elements[N+i] >= graphmode.xmin) && 
+	    (Xvec.elements[N+i] <= graphmode.xmax) && 
+	    (Yvec.elements[N+i] >= graphmode.ymin) && 
+	    (Yvec.elements[N+i] <= graphmode.ymax))
+	  InPic = TRUE;
+      }
+      if (!InPic) continue;
+      N+=8;
+      if (N > NPTS - 1) {  /* this is OK because NPTS is made always a multiple of 8 */
+	NPTS += 200;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+      }
+    }
+  }
+  
+  Xvec.Nelements = Xvec.Nelements = N;
+  if (N > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+  }
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/simage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/simage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/simage.c	(revision 22322)
@@ -0,0 +1,168 @@
+# include "dvoshell.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 31
+# define BLOCK 1000
+
+int simage (int argc, char **argv) {
+
+  char *buffer;
+  Vector Xvec, Yvec, Zvec;
+  double R, D, X, Y, M, zero, range;
+  FILE *f;
+  Header header;
+  Coords coords;
+  int i, j, kapa, Nstars, nstars, Nbytes, nbytes, Npts, N;
+  Graphdata graphmode;
+
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+
+  zero = 17.0;
+  range = -5.0;
+  if ((N = get_argument (argc, argv, "-m"))) {
+    remove_argument (N, &argc, argv);
+    range = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    zero  = atof(argv[N]);
+    range = range - zero;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: image (filename)\n");
+    return (FALSE);
+  }
+
+  gprint (GP_ERR, "not working at the moment (cmp format)\n");
+  return (FALSE);
+  
+  /* read header */
+  if (!gfits_read_header (argv[1], &header)) {
+    gprint (GP_ERR, "ERROR: can't find image file %s\n", argv[1]);
+    return (FALSE);
+  }
+  /* get astrometry information */
+  strcpy (coords.ctype, "NONE");
+  gfits_scan (&header, "CTYPE1",   "%s",  1, coords.ctype);
+  if (strcmp (coords.ctype, "RA---PLY")) {
+    gprint (GP_ERR, "ERROR: wrong astrometric info in header\n");
+    return (FALSE);
+  }
+  gfits_scan (&header, "CDELT1",   "%f", 1, &coords.cdelt1); 
+  gfits_scan (&header, "CDELT2",   "%f", 1, &coords.cdelt2);
+  gfits_scan (&header, "CRVAL1",   "%lf", 1, &coords.crval1);
+  gfits_scan (&header, "CRVAL2",   "%lf", 1, &coords.crval2);  
+  gfits_scan (&header, "CRPIX1",   "%f", 1, &coords.crpix1);
+  gfits_scan (&header, "CRPIX2",   "%f", 1, &coords.crpix2);
+  gfits_scan (&header, "PC001001", "%f", 1, &coords.pc1_1);
+  gfits_scan (&header, "PC001002", "%f", 1, &coords.pc1_2);
+  gfits_scan (&header, "PC002001", "%f", 1, &coords.pc2_1);
+  gfits_scan (&header, "PC002002", "%f", 1, &coords.pc2_2);
+  /* RA Terms */
+  gfits_scan (&header, "PCA1X2Y0", "%f", 1, &coords.polyterms[0][0]);
+  gfits_scan (&header, "PCA1X1Y1", "%f", 1, &coords.polyterms[1][0]);
+  gfits_scan (&header, "PCA1X0Y2", "%f", 1, &coords.polyterms[2][0]);
+  gfits_scan (&header, "PCA1X3Y0", "%f", 1, &coords.polyterms[3][0]);
+  gfits_scan (&header, "PCA1X2Y1", "%f", 1, &coords.polyterms[4][0]);
+  gfits_scan (&header, "PCA1X1Y2", "%f", 1, &coords.polyterms[5][0]);
+  gfits_scan (&header, "PCA1X0Y3", "%f", 1, &coords.polyterms[6][0]);
+  /* Dec Terms */			    
+  gfits_scan (&header, "PCA2X2Y0", "%f", 1, &coords.polyterms[0][1]);
+  gfits_scan (&header, "PCA2X1Y1", "%f", 1, &coords.polyterms[1][1]);
+  gfits_scan (&header, "PCA2X0Y2", "%f", 1, &coords.polyterms[2][1]);
+  gfits_scan (&header, "PCA2X3Y0", "%f", 1, &coords.polyterms[3][1]);
+  gfits_scan (&header, "PCA2X2Y1", "%f", 1, &coords.polyterms[4][1]);
+  gfits_scan (&header, "PCA2X1Y2", "%f", 1, &coords.polyterms[5][1]);
+  gfits_scan (&header, "PCA2X0Y3", "%f", 1, &coords.polyterms[6][1]);
+  coords.Npolyterms = 2; /* how many do we use? */
+
+  /* find number of stars */
+  gfits_scan (&header, "NSTARS", "%d", 1, &Nstars);
+  if (Nstars == 0) {
+    gprint (GP_ERR, "no stars in file\n");
+    return (FALSE);
+  }
+
+  /* open file data */
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "can't find data in file %s\n", argv[1]);
+    return (FALSE);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  /* set up storage buffers */
+  Xvec.Nelements = Nstars;
+  Yvec.Nelements = Nstars;
+  Zvec.Nelements = Nstars;
+  ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+  ALLOCATE (Zvec.elements, float, Zvec.Nelements);
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR));
+
+  /* load in stars by blocks of 1000 */
+  nstars = 0;
+  Nbytes = Nstars*BYTES_STAR;
+  for (i = 0; i < (int)(Nbytes / (BLOCK*BYTES_STAR)); i++) {
+    nbytes = fread (buffer, 1, (BLOCK*BYTES_STAR), f);
+    if (nbytes != BLOCK*BYTES_STAR) {
+      gprint (GP_ERR, "failed to read in stars (1)\n");
+      free (Xvec.elements);
+      free (Yvec.elements);
+      free (Zvec.elements);
+      free (buffer);
+      return (FALSE);
+    }
+    for (j = 0; j < BLOCK; j++, nstars++) {
+      dparse (&X,  1, &buffer[j*BYTES_STAR]);
+      dparse (&Y,  2, &buffer[j*BYTES_STAR]);
+      dparse (&M,  3, &buffer[j*BYTES_STAR]);
+      XY_to_RD (&R, &D, X, Y, &coords);
+      fRD_to_XY (&Xvec.elements[nstars], &Yvec.elements[nstars], R, D, &graphmode.coords);
+      Zvec.elements[nstars] = MIN (1.0, MAX (0.01, (M - zero) / range));
+    }
+  }
+  /* left over fraction of a block */
+  nbytes = fread (buffer, 1, (Nbytes % (BLOCK*BYTES_STAR)), f);
+  if (nbytes != (Nbytes % (BLOCK*BYTES_STAR))) {
+    gprint (GP_ERR, "ERROR: failed to read in stars (2)\n");
+    free (Xvec.elements);
+    free (Yvec.elements);
+    free (Zvec.elements);
+    free (buffer);
+    return (FALSE);
+  }
+  for (j = 0; j < nbytes / BYTES_STAR; j++, nstars++) {
+    dparse (&X,  1, &buffer[j*BYTES_STAR]);
+    dparse (&Y,  2, &buffer[j*BYTES_STAR]);
+    dparse (&M,  3, &buffer[j*BYTES_STAR]);
+    XY_to_RD (&R, &D, X, Y, &coords);
+    fRD_to_XY (&Xvec.elements[nstars], &Yvec.elements[nstars], R, D, &graphmode.coords);
+    Zvec.elements[nstars] = MIN (1.0, MAX (0.01, (M - zero) / range));
+  }
+  
+  if (nstars != Nstars) {
+    gprint (GP_ERR, "ERROR: failed to read in all stars (%d of %d)\n", nstars, Nstars);
+    free (Xvec.elements);
+    free (Yvec.elements);
+    free (Zvec.elements);
+    free (buffer);
+    return (FALSE);
+  }
+
+  graphmode.style = 2;
+  graphmode.size = -1;
+  graphmode.etype = 0;
+  Npts = Xvec.Nelements;
+
+  PlotVectorTriplet (kapa, Npts, Xvec.elements, Yvec.elements, Zvec.elements, &graphmode);
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (Zvec.elements);
+  free (buffer);
+
+  return (TRUE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skycat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skycat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skycat.c	(revision 22322)
@@ -0,0 +1,125 @@
+# include "dvoshell.h"
+
+int RD_to_XYpic (double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside);
+
+int skycat (int argc, char **argv) {
+  
+  double Radius;
+  int i, j, N, Nregions, kapa, ShowAll, NPTS, Npts, leftside, Depth, VERBOSE;
+  struct stat filestat;
+  Vector Xvec, Yvec;
+  Graphdata graphmode;
+  double X[4], Y[4], Rmin, Rmax, Rmid;
+  SkyTable *sky;
+  SkyList *skylist;
+  SkyRegion **regions;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+  ShowAll = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    ShowAll = TRUE;
+  }
+  Depth = -1;
+  if ((N = get_argument (argc, argv, "-depth"))) {
+    remove_argument (N, &argc, argv);
+    Depth = atoi (argv[N]);
+    remove_argument (N, &argc, argv);    
+  }
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: skycat [-all]\n");
+    return (FALSE);
+  }
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, Depth, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+  
+  if (VERBOSE) gprint (GP_ERR, "region: %6.2f - %6.2f, %6.2f - %6.2f\n", 
+			graphmode.coords.crval1 - Radius, graphmode.coords.crval1 + Radius, 
+			graphmode.coords.crval2 - Radius, graphmode.coords.crval2 + Radius);
+
+  Rmin = graphmode.coords.crval1 - 180.0;
+  Rmax = graphmode.coords.crval1 + 180.0;
+  Rmid = 0.5*(Rmin + Rmax);
+
+  NPTS = 200;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  Npts = 0;
+   
+  regions = skylist[0].regions;
+  Nregions = skylist[0].Nregions;
+
+  for (i = 0; i < Nregions; i++) {
+    if (ShowAll || (stat (skylist[0].filename[i], &filestat) != -1)) {
+      if (VERBOSE) gprint (GP_ERR, "%3d %s %6.2f - %6.2f, %6.2f - %6.2f\n", i, regions[i][0].name, 
+			    regions[i][0].Rmin, regions[i][0].Rmax, regions[i][0].Dmin, regions[i][0].Dmax);
+
+      leftside = -1;
+      RD_to_XYpic (&X[0], &Y[0], regions[i][0].Rmin, regions[i][0].Dmin, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[1], &Y[1], regions[i][0].Rmin, regions[i][0].Dmax, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[2], &Y[2], regions[i][0].Rmax, regions[i][0].Dmax, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[3], &Y[3], regions[i][0].Rmax, regions[i][0].Dmin, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+
+      Xvec.elements[Npts] = X[0];
+      Yvec.elements[Npts] = Y[0];
+      for (j = 1; j < 4; j++) {
+	Xvec.elements[Npts + j*2 - 0] = X[j];
+	Yvec.elements[Npts + j*2 - 0] = Y[j];
+	Xvec.elements[Npts + j*2 - 1] = X[j];
+	Yvec.elements[Npts + j*2 - 1] = Y[j];
+      }
+      Xvec.elements[Npts+7] = Xvec.elements[Npts];
+      Yvec.elements[Npts+7] = Yvec.elements[Npts];
+      Npts += 8;
+      if (Npts > NPTS - 1) {  /* this is OK because NPTS is made always a multiple of 8 */
+	NPTS += 200;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+      }
+    }
+  }
+
+  gprint (GP_ERR, "plotting %d catalogs\n", Npts/8);
+  Xvec.Nelements = Xvec.Nelements = Npts;
+  if (Npts > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, Npts, Xvec.elements, Yvec.elements, &graphmode);
+  }
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (regions);
+
+  return (TRUE);
+
+}
+
+
+int RD_to_XYpic (double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside) {
+
+  while (r < Rmin) { r += 360.0; }
+  while (r > Rmax) { r -= 360.0; }
+
+  if (*leftside == -1) {
+    *leftside = (r < Rmid);
+  } else {
+    if (  *leftside && (r > Rmid + 90)) { r -= 360.0; }
+    if (! *leftside && (r < Rmid - 90)) { r += 360.0; }
+  }
+
+  RD_to_XY (x, y, r, d, coords);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skycoverage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skycoverage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skycoverage.c	(revision 22322)
@@ -0,0 +1,146 @@
+# include "dvoshell.h"
+
+int skycoverage (int argc, char **argv) {
+
+  int i, N, Nimage, status, TimeSelect, ByName, xs, ys;
+  time_t tzero, tend;
+  double pixscale, dPix, r, d, Xi, Yi, Xs, Ys, x[2], y[2], trange;
+  Image *image;
+  char name[256];
+  float *V;
+  int Nx, Ny;
+  Buffer *buf;
+  Coords coords;
+
+  ByName = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    ByName = TRUE;
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: skycoverage (buffer) (pixscale) (dPix) [-time start range] [-name name]\n");
+    gprint (GP_ERR, "       (buffer) saves bitmapped AIT plot\n");
+    gprint (GP_ERR, "       (pixscale) specifies the pixel size in degrees\n");
+    gprint (GP_ERR, "       note: we need 64800 / (pixscale)^2 pixels to represent the sky\n");
+    return (FALSE);
+  }
+  
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  pixscale = atof(argv[2]);
+  dPix     = atof(argv[3]);
+
+  Nx = 360/pixscale;
+  Ny = 180/pixscale;
+
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, Nx, Ny, -32, 0.0, 1.0);
+  strcpy (buf[0].file, "(empty)");
+
+  coords.crval1 = 180;
+  coords.crval2 = 0;
+  coords.crpix1 = 0.5*Nx;
+  coords.crpix2 = 0.5*Ny;
+  strcpy (coords.ctype, "DEC--AIT");
+  coords.pc1_1 = coords.pc2_2 = 1;
+  coords.pc1_2 = coords.pc2_1 = 0;
+  coords.cdelt1 = coords.cdelt2 = pixscale;
+  coords.Npolyterms = 0;
+
+  PutCoords (&coords, &buf[0].header);
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  V = (float *)buf[0].matrix.buffer;
+  bzero (V, Nx*Ny*sizeof(float));
+
+  for (ys = 0; ys < Ny; ys++) {
+    for (xs = 0; xs < Nx; xs++) {
+      status = XY_to_RD (&r, &d, (double)(xs), (double)(ys), &coords);
+      status &= (r > 0);
+      status &= (r < 360);
+      if (status) {
+	V[ys*Nx + xs] = 2;
+      }
+    }
+  }
+
+  for (i = 0; i < Nimage; i++) {
+    if (ByName && strcmp (name, image[i].name)) continue;
+    if (TimeSelect && ((image[i].tzero < tzero) || (image[i].tzero+image[i].trate*image[i].NY > tzero + trange))) continue;
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+
+    /* project this image to screen display coords */
+    /* DIS images represent a field, not a chip */
+    if (!strcmp(&image[i].coords.ctype[4], "-DIS")) {
+      x[0] = -0.5*image[i].NX; y[0] = -0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = +0.5*image[i].NY;
+    } else {
+      x[0] = 0;                y[0] = 0;
+      x[1] = image[i].NX;      y[1] = image[i].NY;
+    }
+    status = FALSE;
+    
+    for (Yi = y[0] + 0.5*dPix; Yi < y[1]; Yi += dPix) {
+      for (Xi = x[0] + 0.5*dPix; Xi < x[1]; Xi += dPix) {
+	XY_to_RD (&r, &d, Xi, Yi, &image[i].coords);
+	while (r <   0.0) { r += 360.0; }
+	while (r > 360.0) { r -= 360.0; }
+	status = RD_to_XY (&Xs, &Ys, r, d, &coords);
+	if (status) {
+	  xs = (int)Xs;
+	  ys = (int)Ys;
+	  V[ys*Nx + xs] = 1;
+	}
+      }
+    }
+  }
+
+  free (image);
+  return (TRUE);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skydbtile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skydbtile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skydbtile.c	(revision 22322)
@@ -0,0 +1,283 @@
+# include "dvo2.h"
+SkyRegion *SkyDivideList (SkyRegion *list, int Nlist);
+
+/* region names:
+
+ depth Ntables  Name 
+ 0     1      	fullsky.cpt
+ 1     16     	n????.cpt, s????.cpt
+ 2     256    	n????/r????.cpt
+ 3     4096   	n????/t????.cpt
+ 4     65536  	n????/r????/r????.cpt
+ 5     1.0+06  	n????/t????/t????.cpt
+ 6     1.7+07  	n????/r????/r????/r????.cpt
+
+ depth Ntables  Name 
+ 0     1      	fullsky
+ 1     16     	d%02
+ 2     256    	d%02/r%03d
+ 3     4096   	d%02/t%04d
+ 4     65536  	d%02/r%03d/r%03d
+ 5     1.0+06  	d%02/t%04d/t%04d
+ 6     1.7+07  	d%02/r%03d/r%03d/r%03d
+ 7     2.7+08  	d%02/t%04d/t%04d/t%04d
+
+*/
+
+/* a valid SkyRegion set must always start with the fullsky as the first entry, 
+   with the sequence of entries ordered primarily by depth, followed by parent 
+*/
+
+int SkyMakeNames (SkyRegion *db, SkyRegion *ref, int depth) {
+
+  int i, j, N, Ns, Ne, ns, ne;
+
+  if (db == NULL) return (FALSE);
+  if (depth == 0) {
+    if (db[0].depth != 0) return (FALSE);
+    strcpy (db[0].name, "fullsky");
+    Ns = db[0].childS;
+    Ne = db[0].childE;
+    for (i = Ns; i < Ne; i++) {
+      sprintf (db[i].name, "d%02d", i - 1);
+      if (!SkyMakeNames (db, &db[i], 2)) return (FALSE);
+      if (!SkyMakeNames (db, &db[i], 3)) return (FALSE);
+    }
+    return (TRUE);
+  }
+
+  if (ref == NULL) return (FALSE);
+  
+  if (depth == 2) {
+    if (!ref[0].child) return (TRUE);
+    Ns = ref[0].childS;
+    Ne = ref[0].childE;
+    N = 0;
+    for (i = Ns; i < Ne; i++, N++) {
+      sprintf (db[i].name, "%s/r%02d", ref[0].name, N);
+      if (!SkyMakeNames (db, &db[i], depth + 2)) return (FALSE);
+    }
+  }
+  
+  if (depth > 2) {
+    Ns = ref[0].childS;
+    Ne = ref[0].childE;
+    N = 0;
+    for (i = Ns; i < Ne; i++) {
+      if (!ref[0].child) continue;
+      ns = db[i].childS;
+      ne = db[i].childE;
+      for (j = ns; j < ne; j++, N++) {
+	if (depth % 2) {
+	  sprintf (db[j].name, "%s/t%03d", ref[0].name, N);
+	} else {				      			    
+	  sprintf (db[j].name, "%s/r%02d", ref[0].name, N);
+	}
+	if (!SkyMakeNames (db, &db[j], depth + 2)) return (FALSE);
+      }
+    }  
+  }
+}
+
+SkyRegion *SkyMakeRegions (int Nlevels, int *nlist) {
+
+  int Nlist, Nnext, Ncurr, i, j;
+  SkyRegion *list, *curr, *next;
+
+  Nlist = 0;
+  Nnext = 1;
+  Ncurr = 1;
+  ALLOCATE (list, SkyRegion, Ncurr);
+  ALLOCATE (curr, SkyRegion, Ncurr);
+
+  curr[0].Rmin =   0;
+  curr[0].Rmax = 360;
+  curr[0].Dmin = -90;
+  curr[0].Dmax =  90;
+  curr[0].depth = 0;
+  curr[0].object = FALSE;
+  curr[0].image = FALSE;
+  curr[0].child = FALSE;
+  curr[0].childS = 0;
+  curr[0].childE = 0;
+
+  for (i = 1; i < Nlevels; i++) {
+    next  = SkyDivideRegions (curr, &Nnext);
+    for (j = 0; j < Nnext; j++) {
+      next[j].depth = i;
+    }   
+    REALLOCATE (list, SkyRegion, Nlist + Ncurr);
+    memcpy (&list[Nlist], curr, Ncurr*sizeof(SkyRegion));
+    for (j = Nlist; j < Nlist + Ncurr; j++) {
+      list[j].childS += Nlist + Ncurr;
+      list[j].childE += Nlist + Ncurr;
+    }
+    Nlist += Ncurr;
+    free (curr);
+    curr = next;
+    Ncurr = Nnext;
+  }
+  REALLOCATE (list, SkyRegion, Nlist + Ncurr);
+  memcpy (&list[Nlist], curr, Ncurr*sizeof(SkyRegion));
+  Nlist += Ncurr;
+  free (curr);
+
+  list[0].object = TRUE;
+  list[0].image = TRUE;
+  *nlist = Nlist;
+  return (list);
+}
+
+SkyRegion *SkyDivideRegions (SkyRegion *parents, int *N) {
+
+  int i, Nchildren, Nparents, Np;
+  SkyRegion *children, *temp;
+  float *child, *index;
+
+  Nchildren = Nparents = *N;
+
+  /* copy input list to a new list, set child to be sequence number */
+  ALLOCATE (children, SkyRegion, Nchildren);
+  memcpy (children, parents, Nchildren*sizeof(SkyRegion));
+  for (i = 0; i < Nchildren; i++) {
+    children[i].childS = i;
+    parents[i].childS = i;
+  }
+
+  /* double the number of children, dividing only the largest in half each pass */
+  for (i = 0; i < 4; i++) {
+    temp = SkyDivideList (children, Nchildren);
+    free (children);
+    children = temp;
+    Nchildren *= 2;
+  }
+
+  /* sort children by childS (currently is parent entry) */
+  ALLOCATE (child, float, Nchildren);
+  ALLOCATE (index, float, Nchildren);
+  for (i = 0; i < Nchildren; i++) {
+    index[i] = i;
+    child[i] = children[i].childS;
+  }
+  fsortpair (child, index, Nchildren);
+  ALLOCATE (temp, SkyRegion, Nchildren);
+  for (i = 0; i < Nchildren; i++) {
+    temp[i] = children[(int)index[i]];
+  }
+  free (children);
+  free (child);
+  free (index);
+  children = temp;
+  
+  /* look for all matching values of childS, setup parent values of childS, childE */
+  Np = children[0].childS;
+  parents[Np].childS = 0;
+  parents[Np].child = TRUE;
+  for (i = 0; i < Nchildren; i++) {
+    if (children[i].childS != Np) {
+      parents[Np].childE = i;
+      Np = children[i].childS;
+      parents[Np].childS = i;
+      parents[Np].child = TRUE;
+    }
+    children[i].childS = 0;
+    children[i].childE = 0;
+    children[i].child = FALSE;
+  }
+  parents[Np].childE = i;
+  *N = Nchildren;
+  return (children);
+}
+
+/* always break the region with the largest area */
+SkyRegion *SkyDivideBiggest (SkyRegion *list, int *nlist) {
+
+  int i, N, Nlist;
+  float *area, *indx, dec;
+  SkyRegion *out, save;
+
+  Nlist = *nlist;
+  ALLOCATE (area, float, Nlist);
+  ALLOCATE (indx, float, Nlist);
+  for (i = 0; i < Nlist; i++) {
+    dec = 0.5*(list[i].Dmax + list[i].Dmin);
+    area[i] = (list[i].Rmax - list[i].Rmin)*(list[i].Dmax - list[i].Dmin)*cos(RAD_DEG*dec);
+    indx[i] = i;
+  }
+  fsortpair (area, indx, Nlist);
+
+  N = indx[Nlist - 1];
+  out = SkyDivide (&list[N]);
+  
+  REALLOCATE (list, SkyRegion, Nlist + 1);
+  list[N] = out[0];
+  list[Nlist] = out[1];
+
+  *nlist = Nlist + 1;
+  free (out);
+  free (area);
+  free (indx);
+  return (list);
+}
+
+SkyRegion *SkyDivide (SkyRegion *in) {
+
+  SkyRegion *out;
+  double RA, DEC, R_div, D_div;
+
+  ALLOCATE (out, SkyRegion, 2);
+
+  out[0] = in[0];
+  out[1] = in[0];
+
+  RA  = 0.5*(in[0].Rmin + in[0].Rmax);
+  DEC = 0.5*(in[0].Dmin + in[0].Dmax);
+  if ((DEC >= 90) || (DEC <= -90)) {
+    gprint (GP_ERR, "error in Dmin");
+    return (NULL);
+  }
+ 
+  D_div = (in[0].Rmax - in[0].Rmin) * cos (RAD_DEG*DEC);
+  R_div = (in[0].Dmax - in[0].Dmin);
+  if (R_div < D_div) {
+    /* divide RA range */
+    out[0].Rmax = RA;
+    out[1].Rmin = RA;
+  } else {
+    out[0].Dmax = DEC;
+    out[1].Dmin = DEC;
+  }
+
+  return (out);
+}
+
+/* return a list with 2x as many entries, each split in two */
+SkyRegion *SkyDivideList (SkyRegion *in, int Nin) {
+
+  SkyRegion *out;
+  double RA, DEC, R_div, D_div;
+  int i;
+
+  ALLOCATE (out, SkyRegion, 2*Nin);
+
+  for (i = 0; i < Nin; i++) {
+    /* inherit properties from parent */
+    out[2*i + 0] = in[i];
+    out[2*i + 1] = in[i];
+
+    RA  = 0.5*(in[i].Rmin + in[i].Rmax);
+    DEC = 0.5*(in[i].Dmin + in[i].Dmax);
+    
+    /* split along longest axis */
+    D_div = (in[i].Rmax - in[i].Rmin) * cos (RAD_DEG*DEC);
+    R_div = (in[i].Dmax - in[i].Dmin);
+    if (R_div < D_div) {
+      out[2*i + 0].Rmax = RA;
+      out[2*i + 1].Rmin = RA;
+    } else {
+      out[2*i + 0].Dmax = DEC;
+      out[2*i + 1].Dmin = DEC;
+    }
+  }
+  return (out);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skyregion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skyregion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/skyregion.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "dvoshell.h"
+
+static double RAs = 0.0;
+static double RAe = 0.0;
+static double DECs = 0.0;
+static double DECe = 0.0;
+
+// define the sky region for which extractions are limited
+int skyregion (int argc, char **argv) {
+  
+  if (argc == 1) {
+    gprint (GP_ERR, "current skyregion: %f - %f : %f - %f\n", RAs, RAe, DECs, DECe);
+    gprint (GP_ERR, "USAGE:  skyregion (min RA) (max RA) (min DEC) (max DEC)\n");
+    return (FALSE);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: skyregion (min RA) (max RA) (min DEC) (max DEC)\n");
+    return (FALSE);
+  }
+
+  set_skyregion (atof(argv[1]), atof(argv[2]), atof(argv[3]), atof(argv[4]));
+
+  return (TRUE);
+}
+
+int get_skyregion (double *Rs, double *Re, double *Ds, double *De) {
+
+  *Rs = RAs;
+  *Re = RAe;
+  *Ds = DECs;
+  *De = DECe;
+
+  return TRUE;
+}
+
+int set_skyregion (double Rs, double Re, double Ds, double De) {
+
+  RAs  = Rs;
+  RAe  = Re;
+  DECs = Ds;
+  DECe = De;
+
+  return TRUE;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/subpix.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/subpix.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/subpix.c	(revision 22322)
@@ -0,0 +1,154 @@
+# include "dvoshell.h"
+
+int subpix (int argc, char **argv) {
+  
+  int i, j, I, Nlo, Nhi, Nentry, Nstars, Nimage, Nmeasure;
+  int *index, *entry;
+  int Nmin, Nsub, NSUB, status;
+  int TimeFormat;
+  time_t Timage, TimeReference;
+  double X, Y, Mabs, t;
+  double Ra, Dec, Radius, Radius2, r, Rmin;
+  double *RA, *DEC;
+  
+  SkyTable *sky;
+  SkyList *skylist;
+  Measure *measure;
+  Image *image;
+  Catalog catalog;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: subpix ra dec radius\n");
+    return (FALSE);
+  }
+  if (!ohana_str_to_radec (&Ra, &Dec, argv[1], argv[2])) return (FALSE);
+  if (Ra < 0) Ra += 360.0;
+  if (Ra > 360.0) Ra -= 360.0;
+  Radius = atof (argv[3]);
+
+  /* load star nearest position */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* lock, load, unlock catalog */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  /* quick search of star list for Ra, Dec */
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (index, int, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    index[i] = i;
+  }
+  if (Nstars > 1) sort_coords_index (DEC, RA, index, Nstars);
+
+  /* bracket the DEC range of interest */
+  Nlo = bracket (DEC, Nstars, FALSE, Dec - Radius);
+  Nhi = bracket (DEC, Nstars, TRUE,  Dec + Radius);
+  ALLOCATE (entry, int, MAX (Nhi - Nlo, 1));
+  Nentry = 0;
+
+  /* find the list of stars */
+  Radius2 = Radius*Radius;
+  for (i = Nlo; i < Nhi; i++) {
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      entry[Nentry] = i;
+      Nentry ++;
+    }
+  }
+  if (!Nentry) {
+    gprint (GP_ERR, "no stars found\n");
+    free (RA);
+    free (DEC);
+    free (entry);
+    free (index);
+    dvo_catalog_free (&catalog);
+    SkyListFree (skylist);
+    return (TRUE);
+  }
+
+  /* find the closest star */
+  Nmin = 0;
+  Rmin = SQ(Dec - DEC[entry[0]]) + SQ(Ra - RA[entry[0]]);
+  for (i = 1; i < Nentry; i++) {
+    r = SQ(Dec - DEC[entry[i]]) + SQ(Ra - RA[entry[i]]);
+    if (r < Rmin) {
+      Rmin = r;
+      Nmin = i;
+    }
+  }
+  Nentry = index[entry[Nmin]];
+  Ra = RA[entry[Nmin]];
+  Dec = DEC[entry[Nmin]];
+  gprint (GP_ERR, "finding subpix values for star @ %f %f\n", Ra, Dec);
+
+  free (RA);
+  free (DEC);
+  free (entry);
+  free (index);
+
+  /* storage for the image references */
+  Nsub = 0;
+  NSUB = 100;
+  ALLOCATE (index, int, NSUB);
+
+  /* load all images, extract those touching Ra, Dec */
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    status = RD_to_XY (&X, &Y, Ra, Dec, &image[i].coords);
+    if (!status || (X < 0) || (X > image[i].NX) || (Y < 0) || (Y > image[i].NY)) continue;
+    index[Nsub] = i;
+    Nsub ++;
+    if (Nsub == NSUB - 1) {
+      NSUB += 100;
+      REALLOCATE (index, int, NSUB);
+    }
+  }
+
+  /* only print the entries for existing measurements of this star */ 
+  measure = &catalog.measure[catalog.average[Nentry].measureOffset];
+  Nmeasure = catalog.average[Nentry].Nmeasure;
+  for (i = 0; i < Nsub; i++) {
+    I = index[i];
+    if (!FindMosaicForImage (image, Nimage, I)) continue;
+    Timage = image[I].tzero;
+    for (j = 0; j < Nmeasure; j++) {
+      if (measure[j].t == Timage) { 
+	Mabs = PhotCat (&measure[j]);
+	RD_to_XY (&X, &Y, Ra, Dec, &image[I].coords);
+	t = TimeValue (measure[j].t, TimeReference, TimeFormat);
+	gprint (GP_LOG, "%f %6.3f %7.2f %7.2f %5.3f\n", t, Mabs, X, Y, image[I].secz);
+      } 
+    }
+  }
+
+  dvo_catalog_free (&catalog);
+  free (image);
+  free (index);
+  SkyListFree (skylist);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/dvo/version.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "dvoshell.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "dvo version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "libohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "libdvo version: %s\n", (tmp = strip_version (libdvo_version()))); free (tmp);
+  gprint (GP_LOG, "libfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/astro.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/astro.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/astro.h	(revision 22322)
@@ -0,0 +1,34 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
+# include "data.h"
+
+# ifndef ASTRO_H
+# define ASTRO_H
+
+void InitAstro ();
+void FreeAstro ();
+
+typedef struct {
+  int    isIdentity;	      // identity transformation 
+  double phi;		      // saved in radians
+  double Xo;		      // saved in radians
+  double xo;		      // saved in degrees
+  double sin_phi_cos_Xo;      // pre-computed values
+  double sin_phi_sin_Xo;      // pre-computed values
+  double cos_phi_cos_Xo;      // pre-computed values
+  double cos_phi_sin_Xo;      // pre-computed values
+  double cos_phi;	      // pre-computed values
+  double sin_phi;	      // pre-computed values
+  double cos_Xo;	      // pre-computed values
+  double sin_Xo;	      // pre-computed values
+} CoordTransform;
+
+typedef enum {COORD_NONE, COORD_CELESTIAL, COORD_GALACTIC, COORD_ECLIPTIC} CoordTransformSystem;
+
+CoordTransform *InitTransform (CoordTransformSystem input, CoordTransformSystem output);
+int ApplyTransform (double *x, double *y, double X, double Y, CoordTransform *transform);
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/basic.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/basic.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/basic.h	(revision 22322)
@@ -0,0 +1,8 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+
+void cleanup ();
+void InitBasic ();
+void FreeBasic ();
+void InitBasic_PantasksClient ();
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/convert.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/convert.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/convert.h	(revision 22322)
@@ -0,0 +1,25 @@
+# include "external.h"
+
+# ifndef CONVERT_H
+# define CONVERT_H
+
+/*** time/coord conversion functions not supplied by libohana ***/
+time_t        TimeRef               PROTO((double time, time_t TimeReference, int TimeFormat));
+double        TimeValue             PROTO((time_t time, time_t TimeReference, int TimeFormat));
+
+int           hh_hms                PROTO((double hh, int *hr, int *mn, double *sc));
+int           dd_dms                PROTO((double dd, int *dg, int *mn, double *sc));
+int           hms_format            PROTO((char *line, double value));
+int           dms_format            PROTO((char *line, double value));
+int           hh_hm                 PROTO((double hh, int *hr, double *mn));
+int           day_to_sec            PROTO((char *string, time_t *second));
+int           hms_to_sec            PROTO((char *string, time_t *second));
+char         *ohana_sec_to_hms      PROTO((time_t second));
+char         *ohana_sec_to_day      PROTO((time_t second));
+
+char         *meade_deg_to_str      PROTO((double deg));
+char         *meade_ra_to_str       PROTO((double deg));
+char         *meade_dec_to_str      PROTO((double deg));
+char         *strptime              PROTO((const char *s, const char *format, struct tm *tm));
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/data.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/data.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/data.h	(revision 22322)
@@ -0,0 +1,160 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
+
+# ifndef DATA_H
+# define DATA_H
+
+/*** typedef structs used by math functions ***/
+typedef struct {
+  int   NLINES;
+  int   Nlines;
+  char **lines;
+  char  *name;
+} Queue;
+
+typedef struct {
+  char *name;
+  int NWORDS;
+  int Nwords;
+  char **words;
+  char **value;
+} Page;
+
+typedef struct {
+  char *name;
+  int NPAGES;
+  int Npages;
+  Page **pages;
+  // int *index; (why did I define this?  is it not used?)
+  char **pageIDs;
+} Book;
+
+void InitData ();
+void FreeData ();
+
+/* in book.c */
+void InitBooks ();
+void InitBook (Book *book, char *name);
+void FreeBook (Book *book);
+Book *FindBook (char *name);
+Book *GetBook (int where);
+Book *CreateBook (char *name);
+int DeleteBook (Book *book);
+void ListBooks ();
+
+/* in page.c */
+void InitPage (Page *page, char *name);
+void FreePage (Page *page);
+Page *FindPage (Book *book, char *name);
+Page *GetPage (Book *book, int where);
+Page *GetPageRestricted (Book *book, int where, char *keyName, char *keyValue);
+Page *CreatePage (Book *book, char *name);
+int ShufflePages (Book *book);
+int DeletePage (Book *book, Page *page);
+void ListPages (Book *book);
+void ListWords (Page *page);
+int BookSetWord (Page *page, char *word, char *value);
+char *BookGetWord (Page *page, char *word);
+
+/* in queues.c */
+void InitQueues ();
+void ListQueues ();
+Queue *FindQueue (char *name);
+Queue *CreateQueue (char *name);
+void PushQueue (Queue *queue, char *line);
+void PushNamedQueue (char *name, char *line);
+char *PopQueue (Queue *queue);
+char *PopQueueMatch (Queue *queue, char *Key, char *value);
+void PushQueueUnique (Queue *queue, char *line, char *Key);
+void PushQueueReplace (Queue *queue, char *line, char *Key);
+int InitQueue (Queue *queue);
+int DeleteQueue (Queue *queue);
+int PrintQueue (Queue *queue);
+
+/* in fft.c */
+void fft1D (float *dataRe, float *dataIm, int N, int Nbit, int forward);
+int fftND (float *dataRe, float *dataIm, int Ndim, int *Nsize, int forward);
+int IsBinary (int N, int *Nbit);
+
+/* in spline.c */
+void spline_construct (float *x, float *y, int N, float *y2);
+float spline_apply (float *x, float *y, float *y2, int N, float X);
+
+/* in svdcmp.c */
+int svdcmp (float *a, float *w, float *v, int Nx, int Ny);
+
+/* mrqmin.c */
+float mrqcof (float *x, float *y, float *dy, int Npts, 
+	      float *par, int Npar, float **ta, float **tb, 
+	      float (funcs)(float, float *, int, float *));
+
+float mrqmin (float *x, float *y, float *dy, int Npts, 
+	      float *par, int Npar, 
+	      float (funcs)(float, float *, int, float *), int VERBOSE);
+
+float mrqinit (float *x, float *y, float *dy, int Npts, 
+	       float *par, int Npar, 
+	       float (funcs)(float, float *, int, float *), int VERBOSE);
+
+float **mrqcovar (int Npar);
+
+void mrqfree (int Npar);
+
+/* mrq2dmin.c */
+float mrq2dcof (float *x, float *t, float *y, float *dy, int Npts, 
+		float *par, int Npar, float **ta, float **tb, 
+		float (funcs)(float, float, float *, int, float *));
+
+float mrq2dmin (float *x, float *t, float *y, float *dy, int Npts, 
+		float *par, int Npar, 
+		float (funcs)(float, float, float *, int, float *), int VERBOSE);
+
+float mrq2dinit (float *x, float *t, float *y, float *dy, int Npts, 
+		 float *par, int Npar, 
+		 float (funcs)(float, float, float *, int, float *), int VERBOSE);
+
+float mrq2dchi (float *x, float *t, float *y, float *dy, int Npts, 
+		float *par, int Npar, 
+		float (funcs)(float, float, float *, int, float *));
+
+int mrq2dlimits (float *pmin, float *pmax, int Npar);
+
+float **mrq2dcovar (int Npar);
+
+void mrq2dfree (int Npar);
+
+/* gaussian.c */
+double gaussian (double x, double mean, double sigma);
+void gauss_init (int Nbin);
+double rnd_gauss (double mean, double sigma);
+
+/* starfuncs.c */
+double get_aperture_stats (Matrix *matrix, int X, int Y, int Npix, int Nborder, double max);
+int set_rough_radii (double Ra, double Ri, double Ro);
+int get_rough_star (float *data, int Nx, int Ny, int x, int y, float *xc, float *yc, float *sx, float *sy, float *sxy, float *zs, float *zp, float *sk);
+
+/* precess.c */
+double BtoJ (double in_epoch);
+double get_epoch (char *in_epoch, char mode);
+
+/* graphtools.c */
+void          SetLimits             PROTO((Vector *xvec, Vector *yvec, Graphdata *graphmode));
+void          SetLimitsRaw          PROTO((float *xvec, float *yvec, int Npts, Graphdata *graphmode));
+void          ApplyLimits           PROTO((int Xgraph, Graphdata *graphmode, int apply));
+int           style_args            PROTO((Graphdata *graphmode, int *argc, char **argv, int *kapa));
+
+int read_table_vectors (int argc, char **argv, char *extname);
+
+void *db_getConnection ();
+
+int bracket (double *list, int Nlist, int mode, double value);
+int ibracket (int *list, int Nlist, int mode, double value);
+
+void FreeKapa ();
+void FreeQueues ();
+void FreeBooks ();
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/dimm.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/dimm.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/dimm.h	(revision 22322)
@@ -0,0 +1,71 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
+# include "sbig.h"
+
+void InitDIMM ();
+
+/* telescope.c */
+double distSky (double r1, double r2, double d1, double d2);
+int getRD (double *r, double *d) ;
+int gotoRD (double r, double d);
+int offset (char *direction, double distance);
+int toffset (char *direction, char *rate, double duration);
+int getXY (double *x, double *y);
+int setRD (double r, double d);
+int setSite (char *sitename, double lon, double lat);
+int setTime (char *lst);
+int getSite (double *lon, double *lat, double *lst);
+int ParkScope();
+int SleepScope();
+int WakeScope();
+
+/* Serial.c */
+int SerialVerbose (int mode);
+int SerialInit (char *port);
+int SerialOpen (char *port);
+int SerialBaudRate (int fdesc, int rate);
+int SerialParity (int fdesc, int parity); 
+int SerialDataBits(int fdesc, int bits); 
+int SerialStopBit(int fdesc, int stpbit); 
+void SerialStop (int fdesc);
+int SerialCommand (char *in, char **out, int wait);
+
+/* camera.c */
+int InitCamera (int port);
+void DumpCameraInfo ();
+void CameraFullSize (int *x, int *y);
+int SetTemperature (double temp);
+double GetTemperature ();
+int DumpCameraStatus ();
+int Exposure (double exptime);
+static int readout_callback (float percent);
+int ReadOut (int x, int y, int dx, int dy, int binning, unsigned short *buffer);
+int OpenShutter ();
+int CloseShutter ();
+
+  /* this image structure stuff is considered for further development */ 
+# if (0)
+/** this Image is incompatible with the DVO Image struct */
+typedef struct {
+
+  /* image data area */
+  int Nx, Ny;
+  char *buffer;
+  int Nbytes;
+
+  /* image metadata */
+  double ccdtemp;
+  double airtemp;
+  double ra, dec, airmass;
+  double exptime;
+  int binning;
+} Image;
+
+/* analysis.c */
+int subtractImage (Image *a, Image *b);
+void statsImage (Image *image, Stats *stats);
+void findStars (Image *image, Stars **stars, int *Nstars, double threshold);
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/display.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/display.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/display.h	(revision 22322)
@@ -0,0 +1,39 @@
+# include "external.h"
+# include "kapa.h"
+
+# ifndef DISPLAY_H
+# define DISPLAY_H
+
+/*** kapa graph functions ***/
+int           PlotVectorPair        PROTO((int kapa, int Npts, float *xValues, float *yValues, Graphdata *graphmode));
+int           PlotVectorTriplet     PROTO((int kapa, int Npts, float *xValues, float *yValues, float *zValues, Graphdata *graphmode));
+int           GetGraphData          PROTO((Graphdata *data, int *kapa, char *name));
+int           GetGraph              PROTO((Graphdata *data, int *kapa, char *name));
+int           SetGraph              PROTO((Graphdata *data));
+
+/*** kapa image functions */
+int           GetImageData          PROTO((KapaImageData *data, int *kapa, char *name));
+int           GetImage              PROTO((KapaImageData *data, int *kapa, char *name));
+int           SetImage              PROTO((KapaImageData *data));
+
+void	      QuitKapa              PROTO(());
+void	      InitKapa		    PROTO(());
+int 	      open_kapa		    PROTO((int entry));
+int 	      close_kapa	    PROTO((char *name));
+int 	      AddKapaDevice	    PROTO((char *name));
+int 	      DelKapaDevice	    PROTO((char *name));
+int 	      FindKapaDevice	    PROTO((char *name));
+char	     *GetKapaName	    PROTO(());
+
+/* calling program need to define a function 'get_variable' which
+ * returns the name of the executable for each of KAPA and KII
+ */
+char         *get_variable          PROTO((char *name));
+
+int SendLabel (char *string, int Xgraph, int mode);
+
+int SendGraphMessage (int device, char *format, ...);
+int SendGraphCommand (int device, int length, char *format, ...);
+int SendGraphCommandV (int device, int length, char *format, va_list argp);
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/dvomath.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/dvomath.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/dvomath.h	(revision 22322)
@@ -0,0 +1,120 @@
+/*** dvomath.h ***/
+
+# ifndef DVOMATH_H
+# define DVOMATH_H
+
+# define NCHARS 256
+
+enum {ANYVECTOR, NEWVECTOR, OLDVECTOR};
+enum {ANYBUFFER, NEWBUFFER, OLDBUFFER};
+
+typedef struct {			/* representation of a variable (0-D) */
+  char     *name;
+  char     *value;
+} Variable;
+
+typedef struct {			/* representation of a vector (1-D) */
+  char name[1024];
+  float *elements;
+  int Nelements;
+} Vector;
+
+typedef struct {			/* representation of buffer (image) */
+  char name[1024];
+  char file[1024];
+  Header header;
+  Matrix matrix;
+  int  bitpix, unsign;
+  double bscale, bzero;
+} Buffer;
+
+typedef struct {			/* math stack structure */
+  char   *name;
+  char    type;
+  float  *ptr;
+  Buffer *buffer;
+  Vector *vector;
+  float   Float;
+} StackVar;
+
+/* math functions */
+char         *dvomath               PROTO((int argc, char **argv, int *size, int maxsize));
+char        **isolate_elements      PROTO((int argc, char **argv, int *nstack));
+StackVar     *convert_to_RPN        PROTO((int argc, char **argv, int *nstack));
+int           check_stack           PROTO((StackVar *stack, int Nstack, int validsize));
+int           evaluate_stack        PROTO((StackVar *stack, int *Nstack));
+void          init_stack            PROTO((StackVar *stack));
+void          copy_stack	    PROTO((StackVar *stack1, StackVar *stack2));
+void          move_stack	    PROTO((StackVar *stack1, StackVar *stack2));
+void          clean_stack	    PROTO((StackVar *stack, int Nstack));
+void          delete_stack	    PROTO((StackVar *stack, int Nstack));
+void          clear_stack 	    PROTO((StackVar *stack));
+void          assign_stack 	    PROTO((StackVar *stack, char *name, int type));
+
+int           VV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           SV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           VS_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           MV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           VM_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           MM_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           MS_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           SM_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           SS_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           WW_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           S_unary               PROTO((StackVar *OUT, StackVar *V1, char *op));
+int           V_unary               PROTO((StackVar *OUT, StackVar *V1, char *op));
+int           M_unary               PROTO((StackVar *OUT, StackVar *V1, char *op));
+
+/* variable handling */
+char         *get_variable          PROTO((char *name));
+char         *get_variable_ptr      PROTO((char *name));
+char         *get_local_variable_ptr PROTO((char *name));
+double        get_double_variable   PROTO((char *name, int *found));
+int           get_int_variable      PROTO((char *name, int *found));
+int           DeleteNamedScalar     PROTO((char *name));
+int           IsScalar              PROTO((char *name));
+int           set_variable          PROTO((char *name, double dvalue));
+int           set_int_variable      PROTO((char *name, int ivalue));
+int           set_str_variable      PROTO((char *name, char *value));
+int           set_local_variable    PROTO((char *name, char *value));
+void          InitVariables         PROTO((void));
+void          ListVariables         PROTO((void));
+float         get_variable_default  PROTO((char *name, float dvalue));
+int           SelectScalar          PROTO((char *string, double *value));
+
+/* vector handling */
+Vector       *InitVector            PROTO((void));
+void          InitVectors           PROTO((void));
+int           CopyVector            PROTO((Vector *out, Vector *in));
+int           MoveVector            PROTO((Vector *out, Vector *in));
+int           DeleteVector          PROTO((Vector *vec));
+int           CopyNamedVector       PROTO((char *out, char *in));
+int           MoveNamedVector       PROTO((char *out, char *in));
+int           DeleteNamedVector     PROTO((char *name));
+int           IsVector              PROTO((char *name));
+int           IsVectorPtr           PROTO((Vector *vec));
+int           ListVectors           PROTO((void));
+Vector       *SelectVector          PROTO((char *name, int mode, int verbose));
+
+/* buffer handling */
+Buffer       *InitBuffer            PROTO((void));
+void          InitBuffers           PROTO((void));
+int           CopyBuffer            PROTO((Buffer *out, Buffer *in));
+int           MoveBuffer            PROTO((Buffer *out, Buffer *in));
+int           DeleteBuffer          PROTO((Buffer *buf));
+int           CopyNamedBuffer       PROTO((char *out, char *in));
+int           MoveNamedBuffer       PROTO((char *out, char *in));
+int           DeleteNamedBuffer     PROTO((char *name));
+int           IsBuffer              PROTO((char *name));
+int           IsBufferPtr           PROTO((Buffer *buf));
+int           PrintBuffers          PROTO((int Long));
+int           CreateBuffer          PROTO((Buffer *buf, int Nx, int Ny, int bitpix, float bzero, float bscale));
+Buffer       *SelectBuffer          PROTO((char *name, int mode, int verbose));
+void          dump_buffers          PROTO((int n));  /* deprecated? */
+int           SelectOverlay         PROTO((char *name, int *number));
+
+/* why are these in here? */
+int           gfits_copy_matrix_info (Matrix *matrix1, Matrix *matrix2);
+int           GetTimeFormat         PROTO((time_t *TimeReference, int *TimeFormat));
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/dvoshell.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/dvoshell.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/dvoshell.h	(revision 22322)
@@ -0,0 +1,269 @@
+# include "data.h"
+# include "basic.h"
+# include "astro.h"
+
+# ifndef DVOSHELL_H
+# define DVOSHELL_H
+
+/* magnitude types */
+enum {MAG_NONE, 
+      MAG_INST, 
+      MAG_CAT, 
+      MAG_SYS, 
+      MAG_REL, 
+      MAG_CAL, 
+      MAG_AVE, 
+      MAG_REF, 
+      MAG_ERR, 
+      MAG_CHISQ,
+      MAG_NCODE,
+      MAG_NPHOT,
+};
+
+/* measure fields */
+enum {MEAS_ZERO, 
+      MEAS_GLON, 
+      MEAS_GLAT, 
+      MEAS_GLON_AVE, 
+      MEAS_GLAT_AVE,
+      MEAS_ELON, 
+      MEAS_ELAT, 
+      MEAS_ELON_AVE, 
+      MEAS_ELAT_AVE,
+      MEAS_RA, 
+      MEAS_DEC, 
+      MEAS_RA_AVE, 
+      MEAS_DEC_AVE,
+      MEAS_RA_AVE_ERR, 
+      MEAS_DEC_AVE_ERR, 
+      MEAS_U_RA, 
+      MEAS_U_DEC, 
+      MEAS_U_RA_ERR, 
+      MEAS_U_DEC_ERR, 
+      MEAS_PAR, 
+      MEAS_PAR_ERR, 
+      MEAS_RA_OFFSET, 
+      MEAS_DEC_OFFSET, 
+      MEAS_RA_FIT_OFFSET, 
+      MEAS_DEC_FIT_OFFSET, 
+      MEAS_RA_OFFSET_ERR, 
+      MEAS_DEC_OFFSET_ERR, 
+      MEAS_XP, 
+      MEAS_NMEAS, 
+      MEAS_NMISS, 
+      MEAS_OBJFLAGS, 
+      MEAS_MAG, 
+      MEAS_MINST, 
+      MEAS_MCAT, 
+      MEAS_MSYS, 
+      MEAS_MREL, 
+      MEAS_MCAL, 
+      MEAS_EXPTIME, 
+      MEAS_AIRMASS, 
+      MEAS_ALT, 
+      MEAS_AZ, 
+      MEAS_PHOTCODE, 
+      MEAS_TIME, 
+      MEAS_FWHM, 
+      MEAS_FWHM_MAJ, 
+      MEAS_FWHM_MIN, 
+      MEAS_THETA, 
+      MEAS_DOPHOT, 
+      MEAS_DB_FLAGS, 
+      MEAS_PHOT_FLAGS, 
+      MEAS_XCCD, 
+      MEAS_YCCD, 
+      MEAS_XMOSAIC, 
+      MEAS_YMOSAIC, 
+      MEAS_SKY, 
+      MEAS_dSKY, 
+      MEAS_DET_ID, 
+      MEAS_OBJ_ID, 
+      MEAS_IMAGE_ID, 
+      MEAS_PSF_QF, 
+      MEAS_PSF_CHISQ, 
+      MEAS_CR_NSIGMA, 
+      MEAS_EXT_NSIGMA, 
+      MEAS_STARGAL, 
+};
+
+/* average fields */
+enum {AVE_ZERO, 
+      AVE_RA, 
+      AVE_DEC, 
+      AVE_RA_ERR, 
+      AVE_DEC_ERR, 
+      AVE_GLON, 
+      AVE_GLAT, 
+      AVE_ELON, 
+      AVE_ELAT, 
+      AVE_U_RA, 
+      AVE_U_DEC, 
+      AVE_U_RA_ERR, 
+      AVE_U_DEC_ERR, 
+      AVE_PAR, 
+      AVE_PAR_ERR, 
+      AVE_Xp, 
+      AVE_NMEAS, 
+      AVE_NMISS, 
+      AVE_NPHOT, 
+      AVE_NCODE, 
+      AVE_MAG, 
+      AVE_dMAG, 
+      AVE_Xm, 
+      AVE_FLAG, 
+      AVE_TYPE, 
+      AVE_TYPEFRAC,
+      AVE_OBJID
+};
+
+enum {DVO_TABLE_AVERAGE, DVO_TABLE_MEASURE};
+enum {DVO_DB_CMDLINE_ERROR, DVO_DB_CMDLINE_IS_END, DVO_DB_CMDLINE_IS_WHERE, DVO_DB_CMDLINE_IS_MATCH}; 
+
+// options for selecting the ra,dec limits of the db selections
+typedef struct {
+  char *name;
+  char *list;
+  int useDisplay;
+  int useSkyregion;
+} SkyRegionSelection;
+
+// a single db field 
+typedef struct {
+  char *name;
+  int extract;
+  int table;
+  int ID;
+  int magMode;
+  PhotCode *photcode;
+} dbField;
+
+// db boolean operations
+typedef struct {
+  char   *name;
+  char    type;
+  int     field;
+  float   Float;
+} dbStack;
+
+typedef struct {
+  char name[64];
+  double RA0, RA1, DEC0, DEC1;
+} RegionFile;
+
+typedef struct {
+  double X;
+  double Y;
+  double R;
+  double D;
+  double M, dM;
+  char   dophot;
+  double sky;
+  double fx, fy, df;
+  double Mgal, Map;
+  int found;
+  short int code;
+  e_time t;
+} CMPstars;
+
+/*** dvo prototypes ***/
+int           DetermineTypeCode     PROTO((Average *average, Measure *measure, int code));
+double        DetermineTypefrac     PROTO((Average *average, Measure *measure, PhotCode *code));
+double        ExtractAverages       PROTO((PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param));
+double       *ExtractByDMag         PROTO((PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param));
+double       *ExtractDMag           PROTO((PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist));
+double       *ExtractMagnitudes     PROTO((PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *n));
+double       *ExtractMeasures       PROTO((PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param));
+double       *ExtractMeasuresByDMag PROTO((PhotCode **code, int *mode, int use_first, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param));
+double       *ExtractMeasuresDMag   PROTO((PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist));
+void          FreeImageSelection    PROTO(());
+void          FreeImageSelection    PROTO(());
+int           GetAverageParam       PROTO((char *parname));
+void          GetAverageParamHelp   PROTO(());
+int           GetMagMode            PROTO((char *string));
+double        GetMeasure            PROTO((int param, Average *average, Measure *measure, double mag));
+int           GetMeasureParam       PROTO((char *parname));
+int           GetMeasureTypeCode    PROTO((Measure *measure));
+int           GetPhotcodeInfo       PROTO((char *string, PhotCode **Code, int *Mode));
+int           GetSelectionParam     PROTO(());
+int           GetTimeSelection      PROTO((time_t *tz, time_t *te));
+void          InitDVO               PROTO(());
+void          FreeDVO               PROTO(());
+int           InitPhotcodes         PROTO(());
+Image        *LoadImages            PROTO((int *Nimage));
+Image        *MatchImage            PROTO((unsigned int time, short int source));
+Coords       *MatchMosaic           PROTO((unsigned int time, short int source));
+int           Quality               PROTO((Measure *measure, int IsDophot));
+int           SelectMags            PROTO((int Nphot, int Tphot, int Ns, Average *average, Measure *measure, SecFilt *secfilt, int UL));
+
+SkyList      *SelectRegions         PROTO((SkyRegionSelection *selection));
+SkyList      *SkyListLoadFile       PROTO((char *filename));
+int           SetCATDIR             PROTO((char *path, int verbose));
+char *        GetCATDIR             PROTO(());
+SkyTable     *GetSkyTable           PROTO(());
+SkyList      *SkyListFromFile       PROTO((char *filename));
+SkyRegionSelection *SetRegionSelection    PROTO((int *argc, char **argv));
+
+int           SetImageSelection     PROTO((int mode, SkyRegionSelection *selection));
+int           SetPhotSelections     PROTO((int *argc, char **argv, int Nparams));
+int           SetSelectionParam     PROTO((int param));
+int           TestAverage           PROTO((PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure));
+int           TestPhotSelections    PROTO((PhotCode **code, int *mode, int param));
+void          compare               PROTO((Catalog *catlog1, Catalog *catlog2, Vector *rvec,  Vector *dvec,  Vector *mvec, Vector *drvec, Vector *ddvec, Vector *dmvec, double radius));
+void          cprecess              PROTO((Average *average, int Naverage, double in_epoch, double out_epoch));
+void          image_subset          PROTO((Image *image, int Nimage, int **Subset, int *Nsubset, SkyRegionSelection *selection, unsigned long int tzero, double trange, int TimeSelect));
+int           match_image           PROTO((Image *image, int Nimage, unsigned int T, short int S));
+int           match_image_subset    PROTO((Image *image, int *subset, int Nsubset, unsigned int T, short int S));
+void          print_value           PROTO((double value, short int ival));
+void          sort_image_subset     PROTO((Image *image, int *subset, int N));
+void          sort_images           PROTO((Image *image, int N));
+void          sortave               PROTO((Average *ave, int N));
+CMPstars     *cmpReadFits           PROTO((FILE *f, int *nstars));
+CMPstars     *cmpReadText           PROTO((FILE *f, int *nstars));
+int           RD_to_XYpic           PROTO((double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside));
+
+// dvo DB field functions
+dbField     *dbCmdlineFields        PROTO((int argc, char **argv, int table, int *last, int *nfields));
+dbStack     *dbRPN                  PROTO((int argc, char **argv, int *nstack));
+int          dbCheckStack           PROTO((dbStack *stack, int Nstack, int table, dbField **inFields, int *Nfields));
+int          dbBooleanCond          PROTO((dbStack *inStack, int NinStack, float *fields));
+void         dbInitStack            PROTO((dbStack *stack));
+void 	     dbFreeStack            PROTO((dbStack *stack, int Nstack));
+void 	     dbFreeEntry            PROTO((dbStack *stack));
+void         dbFreeTempEntry        PROTO((dbStack *stack));
+
+dbStack     *dbBinary               PROTO((dbStack *V1, dbStack *V2, char *op, float *fields));
+dbStack     *dbUnary                PROTO((dbStack *V1, char *op, float *fields));
+
+int          GetMagMode             PROTO((char *string));
+PhotCode    *ParsePhotcodeField     PROTO((char *field, int *mode, int def));
+int          ParseMeasureField      PROTO((dbField *field, char *fieldName));
+int          ParseAverageField      PROTO((dbField *field, char *fieldName));
+
+double       dbExtractAverages      PROTO((Average *average, SecFilt *secfilt, Measure *measure, dbField *field));
+double       dbExtractMeasures      PROTO((Average *average, SecFilt *secfilt, Measure *measure, dbField *field));
+void         dbExtractMeasuresInit  PROTO(());
+
+void 	     dbInitField            PROTO((dbField *field));
+void 	     dbFreeFields           PROTO((dbField *fields, int Nfields));
+int          dbAstroRegionLimits    PROTO((dbStack **stack, int *nstack, SkyRegionSelection *selection, int table));
+char        *strfloat               PROTO((float value));
+
+int get_skyregion (double *Rs, double *Re, double *Ds, double *De);
+int set_skyregion (double Rs, double Re, double Ds, double De);
+void FreeImageSelection ();
+
+void FreeSkyRegionSelection (SkyRegionSelection *selection);
+int wordhash (char *word);
+
+int dbExtractAverageInitTransform (CoordTransformSystem target);
+int dbExtractAverageInit ();
+
+int dbExtractMeasuresInitTransform (CoordTransformSystem target);
+int dbExtractMeasuresInitAve ();
+int dbExtractMeasuresInitMeas ();
+
+int dbExtractAveragesInitTransform (CoordTransformSystem target);
+int dbExtractAveragesInit ();
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/external.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/external.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/external.h	(revision 22322)
@@ -0,0 +1,30 @@
+# include <signal.h>
+# include <unistd.h>
+# include <sys/uio.h>
+# include <fcntl.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <sys/time.h>
+# include <time.h>
+# include <errno.h>
+# include <pthread.h>
+
+# include <netinet/ip.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+
+# include <ohana.h>
+# include <dvo.h>
+
+/* provide missing external defines */
+# ifdef MISSING_SOCKET_INFO
+#   define F_SETFL         4   
+#   define O_NONBLOCK       0200000  
+#   define AF_UNIX         1          
+#   define SOCK_STREAM     1          
+#   define ENOENT          2 
+# endif
+
+# define strlen(A) ((int)strlen(A))
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/hstgsc.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/hstgsc.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/hstgsc.h	(revision 22322)
@@ -0,0 +1,27 @@
+
+static double BigDecBounds[] = {0.0, 7.5, 15.0, 22.5, 30.0, 37.5, 45.0, 
+				52.5, 60.0, 67.5, 75.0, 82.5, 90.0,
+				0.0, -7.5, -15.0, -22.5, -30.0, -37.5, -45.0, 
+				-52.5, -60.0, -67.5, -75.0, -82.5, -90.0};
+static char *Dec2Sections[] = {"n0000", "n0730", "n1500", "n2230", "n3000", "n3730", "n4500", 
+			       "n5230", "n6000", "n6730", "n7500", "n8230", "weirdness", 
+			       "s0000", "s0730", "s1500", "s2230", "s3000", "s3730", "s4500", 
+			       "s5230", "s6000", "s6730", "s7500", "s8230", "weirdness"};
+
+static int NDecLines[] = {593, 584, 551, 530, 522, 465, 406, 362, 280, 198, 123, 24, 
+			  0, 597, 578, 574, 577, 534, 499, 442, 376, 294, 212, 144, 48};
+
+/** older data layout concepts 
+static char *DecSections[] = {"N0000", "N0730", "N1500", "N2230", "N3000", "N3730", "N4500", 
+			      "N5230", "N6000", "N6730", "N7500", "N8230", "weirdness", 
+			      "S0000", "S0730", "S1500", "S2230", "S3000", "S3730", "S4500", 
+			      "S5230", "S6000", "S6730", "S7500", "S8230", "weirdness"};
+
+static char *disk[] = {"disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", 
+		       "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "weirdness", 
+		       "disk 1", "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", 
+		       "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", "weirdness"};
+
+static int NBigRASections [] = {48, 47, 45, 43, 40, 36, 32, 28, 21, 15, 9, 3, 3, 48, 47, 45, 43, 40, 36, 32, 28, 21, 15, 9, 3, 3};
+
+**/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/imfit.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/imfit.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/imfit.h	(revision 22322)
@@ -0,0 +1,20 @@
+# include "astro.h"
+
+int Npar;
+int Nfpar;
+float *par;
+float *fpar;
+float *sky;
+
+float (*fitfunc)(float, float, float *, int, float *);
+void (*imfit_cleanup)();
+
+void fgauss_setup (char *name);
+void pgauss_setup (char *name);
+void sgauss_setup (char *name);
+void qgauss_setup (char *name);
+void qfgauss_setup (char *name);
+void qrgauss_setup (char *name);
+void pgauss_psf_setup (char *name);
+void qgauss_psf_setup (char *name);
+void sgauss_psf_setup (char *name);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/mana.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/mana.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/mana.h	(revision 22322)
@@ -0,0 +1,11 @@
+# include "data.h"
+# include "basic.h"
+# include "astro.h"
+
+# ifndef MANA_H
+
+void InitMana ();
+void FreeMana ();
+int *findrowpeaks (float *row, int Nrow, float threshold, int *npeaks);
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/opihi.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/opihi.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/opihi.h	(revision 22322)
@@ -0,0 +1,5 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/pantasks.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/pantasks.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/pantasks.h	(revision 22322)
@@ -0,0 +1,287 @@
+# include "data.h"
+# include "basic.h"
+# include "astro.h"
+
+# include <sys/time.h>
+# include <time.h>
+# include <zlib.h>
+
+# define DEBUG 0
+
+typedef enum {
+  JOB_NONE,
+  JOB_BUSY, 
+  JOB_EXIT, 
+  JOB_HUNG,
+  JOB_CRASH,
+  JOB_PENDING,
+} JobStat;
+
+typedef enum {
+  JOB_LOCAL, 
+  JOB_CONTROLLER, 
+} JobMode;
+
+typedef enum {
+  CONTROLLER_HUNG = -1,
+  CONTROLLER_DOWN = 0,
+  CONTROLLER_GOOD = 1,
+} ControllerStat;
+
+enum {RANGE_ABS, RANGE_DAY, RANGE_WEEK};
+enum {TIMER_ALLJOBS, TIMER_SUCCESS, TIMER_FAILURE};
+
+enum {TASK_NONE, 
+      TASK_EMPTY, 
+      TASK_COMMENT, 
+      TASK_NMAX, 
+      TASK_ACTIVE, 
+      TASK_TRANGE, 
+      TASK_END, 
+      TASK_HOST, 
+      TASK_STDOUT, 
+      TASK_STDERR, 
+      TASK_COMMAND, 
+      TASK_OPTIONS, 
+      TASK_PERIODS, 
+      TASK_NPENDING, 
+      TASK_EXIT, 
+      TASK_EXEC
+} TaskHashResults;
+
+typedef struct {
+  time_t start;
+  time_t stop;
+  char type;
+  char include;
+  int Nmax;
+  int Nrun;
+} TimeRange;
+
+/* data to define a host machine */
+typedef struct {
+  char       *hostname;
+  int         max_threads;
+} Host;
+
+/* a task is a description of the wrapping of a job */
+typedef struct {
+  Macro  *exec;				/* name is 'exec' */
+  Macro  *crash;			/* name is 'crash' */
+  Macro  *timeout;
+  Macro  *defexit;
+
+  int     NEXIT;
+  int     Nexit;
+  Macro **exit;				/* name is exit status */
+
+  int     argc;
+  char  **argv;
+
+  int     optc;
+  char  **optv;
+
+  char   *host;
+  int     host_required;
+
+  char   *name;
+
+  char   *stdout_dump;
+  char   *stderr_dump;
+
+  int       Nranges;
+  TimeRange *ranges;
+
+  int     Nmax;  // only construct Ntotal jobs for this task
+  int     Njobs;
+
+  int     Npending;  // number of currently pending jobs
+  int     NpendingMax;  // max number of pending jobs allowed
+
+  float   poll_period;
+  float   exec_period;
+  float   timeout_period;
+
+  struct timeval last;
+
+  int Nsuccess;
+  int Nfailure;
+  int Ntimeout;
+
+  double dtimeAve_alljobs, dtimeMin_alljobs, dtimeMax_alljobs;
+  double dtimeAve_success, dtimeMin_success, dtimeMax_success;
+  double dtimeAve_failure, dtimeMin_failure, dtimeMax_failure;
+
+  int active;
+
+} Task;
+
+// time period include/exclude periods: 
+// date ranges (e_time - e_time) 
+// time ranges (e_time - e_time) (use e_time % 86400)
+// time-of-week ranges  (e_time - e_time
+// -trange Mon Fri (inclusive on days -- end defaults to Fri@23:59:59)
+// -trange 08:00 - 17:00
+// -trange 2005/12/24 2005/12/31
+// be careful of HST!
+// type: day, week, date
+// keep: TRUE: perform action within this time period
+// keep: FALSE: do not perform action within this time period
+  
+typedef struct {
+  int JobID;				/* internal ID for job */
+  int pid;				/* external ID for job */
+
+  struct timeval last;
+  struct timeval start;
+  int state;
+  int exit_status;
+
+  int     argc;
+  char  **argv;
+
+  int     optc;
+  char  **optv;
+
+  Task   *task;
+
+  /* this cries out for another structure... */
+  IOBuffer    stdout_buff;    /* stdout storage buffer */
+  char       *stdout_dump;    // output target file for stdout
+  int         stdout_size;    /* size of pending stdout buffer (controller) */
+  int         stdout_fd;      /* stdout pipe (local only) */
+
+  IOBuffer    stderr_buff;    /* stderr storage buffer */
+  char       *stderr_dump;    // output target file for stderr
+  int         stderr_size;    /* size of pending stderr buffer (controller) */
+  int         stderr_fd;      /* stderr pipe (local only) */
+
+  JobMode     mode;			/* local or controller? */
+  char   *realhost;
+
+  double dtime;
+} Job;
+
+# define CONTROLLER_PROMPT "pcontrol:"
+
+/* scheduler prototypes */
+
+void InitPantasks ();
+void FreePantasks ();
+
+void InitPantasksServer ();
+void FreePantasksServer ();
+
+void InitPantasksClient ();
+void FreePantasksClient ();
+
+void InitTasks ();
+void FreeTasks ();
+
+Task *NextTask ();
+Task *FindTask (char *name);
+void ListTasks (int verbose);
+int ShowTask (char *name);
+int FreeTask (Task *task);
+Task *CreateTask (char *name);
+int ValidateTask (Task *task, int RequireStatic);
+int RegisterNewTask ();
+int DeleteNewTask ();
+Task *GetNewTask ();
+Task *GetActiveTask ();
+void SetTaskTimer (struct timeval *timer);
+double GetTaskTimer (struct timeval start, int verbose);
+void InitTaskTimers ();
+int TaskHash (char *input);
+int RemoveTask (Task *task);
+Task *SetNewTask (Task *task);
+void ListTaskStats (char *regex);
+void ResetTaskStats (char *regex);
+void UpdateTaskTimerStats (Task *task, int mode, double dtime);
+
+int NextJobID ();
+void InitJobIDs ();
+void FreeJobIDs ();
+int FreeJobID (int ID);
+
+void InitJobs ();
+void FreeJobs ();
+
+Job *NextJob ();
+Job *FindJob (int JobID);
+void ListJobs ();
+Job *CreateJob (Task *task);
+int SubmitJob (Job *job);
+int CheckJob (Job *job);
+int DeleteJob (Job *job);
+void FreeJob (Job *job);
+
+float CheckJobs ();
+float CheckTasks ();
+int CheckSystem ();
+int CheckController ();
+int CheckTimeRanges (TimeRange *ranges, int Nranges);
+int BumpTimeRanges (TimeRange *ranges, int Nranges);
+
+int GetJobOutput (char *channel, int pid, IOBuffer *buffer, int Nbytes);
+int CheckControllerJob (Job *job);
+int CheckControllerJobStatus (Job *job);
+int SubmitControllerJob (Job *job);
+int DeleteControllerJob (Job *job);
+Job *FindControllerJob (int JobID);
+int StartController ();
+int ControllerCommand (char *command, char *response, IOBuffer *buffer);
+int SubmitLocalJob (Job *job);
+int CheckLocalJob (Job *job);
+int CheckLocalJobStatus (Job *job);
+void InitTaskTimers ();
+CommandF *FindControllerCommand (char *cmd);
+int QuitController ();
+int StopController ();
+int RestartController ();
+int VerboseMode ();
+int KillLocalJob (Job *job);
+int CheckControllerOutput ();
+int PrintControllerOutput ();
+
+int KillControllerJob (Job *job);
+int CheckControllerStatus ();
+void gotsignal (int signum);
+int client_shell (int argc, char **argv);
+
+void InitClients ();
+void AddNewClient (int client);
+int  DeleteClient (int client);
+void *ListenClients (void *data);
+
+// functions related to the server threads
+void CheckTasksSetState (int state);
+int CheckTasksGetState ();
+void *CheckTasksThread (void *data);
+
+void CheckJobsSetState (int state);
+int CheckJobsGetState ();
+void *CheckJobsThread (void *data);
+
+void CheckControllerSetState (int state);
+int CheckControllerGetState ();
+void *CheckControllerThread (void *data);
+
+void CheckInputsSetState (int state);
+int CheckInputsGetState ();
+void *CheckInputsThread (void *data);
+
+// functions related to the queue of input files
+void InitInputs ();
+void FreeInputs ();
+void AddNewInput (char *input);
+int DeleteInput (char *input);
+void CheckInputs ();
+
+void SerialThreadLock ();
+void SerialThreadUnlock ();
+
+int InitPassword ();
+int CheckPassword (int BindSocket);
+
+int FlushJobs ();
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/pclient.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/pclient.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/pclient.h	(revision 22322)
@@ -0,0 +1,55 @@
+# include "data.h"
+# include "basic.h"
+
+/** pclient global data **/
+
+/** child status values **/
+enum {
+  PCLIENT_NONE,
+  PCLIENT_BUSY,  
+  PCLIENT_EXIT,
+  PCLIENT_CRASH,
+};
+
+int ChildPID;				/** current child PID **/
+int ChildStatus;			/** current status of child **/
+int ChildExitStatus;			/** recent exit status of child **/
+
+/** child may be in the following states:
+    
+PCLIENT_NONE
+PCLIENT_BUSY
+PCLIENT_EXIT
+PCLIENT_CRASH
+
+allowed transitions:
+
+PCLIENT_NONE  ->    PCLIENT_BUSY  (start a job)
+
+PCLIENT_BUSY  ->    PCLIENT_EXIT  (job complete)
+PCLIENT_BUSY  ->    PCLIENT_CRASH (job crashed)
+
+PCLIENT_EXIT  ->    PCLIENT_NONE  (cleanup)
+PCLIENT_CRASH ->    PCLIENT_NONE  (cleanup)
+
+**/
+
+/** file descriptors for child I/O **/
+int child_stdin_fd[2];
+int child_stdout_fd[2];
+int child_stderr_fd[2];
+
+/** buffers for I/O storage **/
+IOBuffer child_stdout;
+IOBuffer child_stderr;
+
+int InitChild ();
+int FreeChild ();
+int CheckChild ();
+void CheckChildStatus ();
+
+void InitPclient ();
+void FreePclient ();
+void gotsignal (int signum);
+
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/pcontrol.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/pcontrol.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/pcontrol.h	(revision 22322)
@@ -0,0 +1,270 @@
+# include "data.h"
+# include "basic.h"
+# define THREADED
+
+typedef struct timeval Ptime;
+typedef unsigned long long IDtype;
+
+/** job status values **/
+typedef enum {
+  PCONTROL_JOB_ALLJOBS,
+  PCONTROL_JOB_PENDING,
+  PCONTROL_JOB_BUSY,  
+  PCONTROL_JOB_RESP,  
+  PCONTROL_JOB_HUNG,  
+  PCONTROL_JOB_DONE,  
+  PCONTROL_JOB_KILL,  
+  PCONTROL_JOB_EXIT,
+  PCONTROL_JOB_CRASH,
+} JobStat;
+
+/** job status values **/
+typedef enum {
+  PCONTROL_JOB_ANYHOST,
+  PCONTROL_JOB_WANTHOST,
+  PCONTROL_JOB_NEEDHOST,
+} JobMode;
+
+/** job thread options values **/
+typedef enum {
+  PCONTROL_JOB_THREADS_NONE,
+  PCONTROL_JOB_THREADS_MAX,
+} JobThreadMode;
+
+/** host status values **/
+typedef enum {
+  PCONTROL_HOST_ALLHOSTS,
+  PCONTROL_HOST_IDLE,
+  PCONTROL_HOST_BUSY,  
+  PCONTROL_HOST_RESP,
+  PCONTROL_HOST_DOWN,
+  PCONTROL_HOST_DONE,
+  PCONTROL_HOST_OFF,
+} HostStat;
+
+/** host response options **/
+typedef enum {
+  PCONTROL_RESP_NONE,
+  PCONTROL_RESP_START_JOB,
+  PCONTROL_RESP_CHECK_BUSY_JOB,  
+  PCONTROL_RESP_CHECK_DONE_HOST,  
+  PCONTROL_RESP_CHECK_HOST,
+  PCONTROL_RESP_KILL_JOB,
+  PCONTROL_RESP_STOP_HOST,
+} HostResp;
+
+typedef enum {
+  PCONTROL_RUN_UNKNOWN,
+  PCONTROL_RUN_NONE,
+  PCONTROL_RUN_HOSTS,
+  PCONTROL_RUN_REAP,
+  PCONTROL_RUN_ALL,
+} RunLevels;
+
+/* stack special positions */
+typedef enum {
+  STACK_TOP = 0,
+  STACK_BOTTOM = -1,
+} StackWhere;
+
+typedef enum {
+  PCLIENT_HUNG = -1,
+  PCLIENT_DOWN = 0,
+  PCLIENT_GOOD = 1,
+} PclientStat;
+
+typedef struct {
+  char *buffer;
+  int   Nalloc;
+  int   Nmaxread;
+  int   Nextra;
+  int   Nlast;
+  int   Nbuffer;
+} Fifo;
+
+/* data to define a job */
+typedef struct {
+  int          argc; 
+  char       **argv;
+  char        *hostname;
+  char        *realhost;
+  int          exit_status;
+  int          Reset;
+  int          stdout_size;
+  int          stderr_size;
+  JobMode      mode;
+  JobStat      state;
+  JobStat      stack;
+  IOBuffer     stdout_buff;
+  IOBuffer     stderr_buff;
+  Ptime        start;
+  Ptime        stop;
+  double       dtime;
+  int          pid;
+  IDtype       JobID;
+  struct Host *host;
+} Job;
+
+/* data to define a host machine */
+typedef struct {
+  char       *hostname;
+  int         stdin_fd;
+  int         stdout_fd;
+  int         stderr_fd;
+  int         max_threads;
+  int         markoff;
+  int         pid;
+  HostStat    stack;
+  Ptime       lasttry;
+  Ptime       nexttry;
+  IDtype      HostID;
+  IOBuffer    comms_buffer;
+  char       *response;
+  HostResp    response_state;
+  struct Job *job;
+} Host;
+
+typedef struct {
+  void **object;
+  char **name;
+  int   *id;
+  int    Nobject;
+  int    NOBJECT;
+# ifdef THREADED    
+  pthread_mutex_t mutex;
+# endif
+} Stack;
+
+/* XXX if this is hard-wired, we can't change shell name in StartHost */
+# define PCLIENT_PROMPT "pclient:"
+
+// # define FREE(X) if (X != NULL) { free (X); }
+# define CLOSE(FD) { if (FD) close (FD); FD = 0; }
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+# define ZTIME(A) ((A.tv_sec == 0) && (A.tv_usec == 0))
+
+# define ASSERT(TEST,STRING) { if (!(TEST)) { gprint (GP_ERR, "programming error: %s\n", STRING); abort (); }}
+# define ABORT(STRING) { gprint (GP_ERR, "programming error: %s\n", STRING); abort (); }
+// # define ASSERT(TEST,STRING) { if (!(TEST)) { gprint (GP_ERR, "programming error: %s\n", STRING); raise (SIGINT); exit (2); }}
+// # define ABORT(STRING) { gprint (GP_ERR, "programming error: %s\n", STRING); raise (SIGINT); exit (2); }
+
+void InitPcontrol ();
+void FreePcontrol ();
+
+/*** StackOps.c ***/
+Stack *InitStack ();
+void   FreeStack (Stack *stack);
+int    PushStack (Stack *stack, int where, void *object, int id, char *name);
+void  *PullStackByLocation (Stack *stack, int where);
+void  *PullStackByName (Stack *stack, char *name);
+void  *PullStackByID (Stack *stack, int id);
+int    RemoveStackEntry (Stack *stack, int where);
+void  *RemoveStackByID (Stack *stack, int id);
+void   LockStack (Stack *stack);
+void   UnlockStack (Stack *stack);
+
+// void  *FindStackByID (Stack *stack, int id);
+// void  *FindStackByName (Stack *stack, char *name);
+
+/*** CheckSystem.c ***/
+int   CheckSystem ();
+void *CheckSystem_Threaded (void *data);
+int   CheckBusyJobs (float delay);
+int   CheckDoneJobs (float delay);
+int   CheckKillJobs (float delay);
+int   CheckDoneHosts (float delay);
+int   CheckDownHosts (float delay);
+int   CheckIdleHosts (float delay);
+int   CheckLiveHosts (float delay);
+int   SetRunSystem (int state);
+RunLevels SetRunLevel (RunLevels level);
+
+/*** own files ***/
+int StartJob (Job *job, Host *host);
+int StartJobResponse (Host *host);
+
+int CheckHost (Host *host);
+int CheckHostResponse (Host *host);
+
+int CheckDoneHost (Host *host);
+int CheckDoneHostResponse (Host *host);
+
+int CheckBusyJob (Job *job, Host *host);
+int CheckBusyJobResponse (Host *host);
+
+int KillJob (Job *job, Host *host);
+int KillJobResponse (Host *host);
+
+int StartHost (Host *host);
+int CheckIdleHost (Host *host);
+int CheckDoneJob (Job *job, Host *host);
+int GetJobOutput (char *command, Host *host, IOBuffer *buffer, int Nbytes);
+int rconnect (char *command, char *hostname, char *shell, int *stdio);
+
+int PclientCommand (Host *host, char *command, char *response, HostResp response_state);
+int PclientResponse (Host *host, char *response, IOBuffer *buffer);
+
+int CheckRespHosts (float MaxDelay);
+int CheckRespHost (Host *host);
+
+/*** misc files ***/
+int    VerboseMode ();  // in verbose.c
+void   gotsignal (int signum); // in pcontrol.c
+
+/*** IDops.c ***/
+void InitIDs ();
+IDtype NextJobID ();
+IDtype NextHostID ();
+void PrintID (gpDest dest, IDtype ID);
+IDtype GetID (char *IDword);
+
+/*** CheckPoint.c ***/
+int SetCheckPoint ();
+int ClearCheckPoint ();
+int TestCheckPoint ();
+
+/*** HostOps.c ***/
+void   InitHostStacks ();
+void   FreeHostStacks ();
+Stack *GetHostStack (int StackID);
+char  *GetHostStackName (int StackID);
+Stack *GetHostStackByName (char *name);
+int    PutHost (Host *host, int StackID, int where);
+Host  *PullHostByID (IDtype HostID, int *StackID);
+Host  *PullHostByName (char *name, int *StackID);
+Host  *PullHostFromStackByID (int StackID, IDtype ID);
+Host  *PullHostFromStackByName (int StackID, char *name);
+IDtype AddHost (char *hostname, int max_threads);
+void   DelHost (Host *host);
+
+/*** StopHosts.c ***/
+void   DownHost (Host *host);
+void   OffHost (Host *host);
+int    DownHosts ();
+int    StopHost (Host *host);
+int    StopHostResponse (Host *host);
+int    HarvestHost (int pid);
+
+/*** JobOps.c ***/
+void   InitJobStacks ();
+void   FreeJobStacks ();
+Stack *GetJobStack (int StackID);
+char  *GetJobStackName (int StackID);
+Stack *GetJobStackByName (char *name);
+int    PutJob (Job *job, int StackID, int where);
+int    PutJobSetState (Job *job, int StackID, int where, int state);
+Job   *PullJobByID (IDtype JobID, int *StackID);
+Job   *PullJobFromStackByID (int StackID, int ID);
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int argc, char **argv);
+void   DelJob (Job *job);
+Host  *UnlinkJobAndHost (Job *job);
+void   LinkJobAndHost (Job *job, Host *host);
+
+void pcontrol_exit (int n);
+
+// Job   *FindJobByID (IDtype JobID, int *StackID);
+// Job   *FindJobInStackByID (int StackID, int ID);
+// Host  *FindHostByID (IDtype HostID, int *StackID);
+// Host  *FindHostByName (char *name, int *StackID);
+// Host  *FindHostInStackByID (int StackID, IDtype ID);
+// Host  *FindHostInStackByName (int StackID, char *name);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/shell.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/shell.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/shell.h	(revision 22322)
@@ -0,0 +1,183 @@
+/*** shell.h ***/
+# include "external.h"
+
+# ifndef SHELL_H
+# define SHELL_H
+
+# define ISVAR(a) (isalnum (a) || (a == ':') || (a == '_'))
+# define ISWORD(a,q) ((q) ? (a != '"') : (isalnum(a) || (a == '/') || (a == '.') || (a == '_') || (a == '-')))
+# define ISNUM(c) (isdigit(c) || (c == '-') || (c == '.'))
+
+# define MACRO_STRING(s) #s
+# define MACRO_NAME(s) MACRO_STRING(s)
+
+/* enums used by gprint functions */
+typedef enum {GP_FILE, GP_BUFF} gpMode;
+typedef enum {GP_LOG, GP_ERR} gpDest;
+
+typedef int CommandF ();
+
+typedef struct sockaddr_in SockAddress;
+
+/*** typedef structs used by shell functions ***/
+typedef struct {			/* basic opihi command structure */
+  char      real;
+  char     *name;
+  CommandF *func;    
+  char     *help;
+} Command;
+
+typedef struct {			/* a macro (collection of commands) */
+  char   *name;
+  char  **line;
+  int     Nlines;
+} Macro;
+
+typedef struct {			/* a list (macro/loop currently being executed) */
+  char **line;
+  int    n;
+  int    Nlines;
+  int    Nalloc;
+} List;
+
+/* structure used to represent the gprint i/o stream */
+typedef struct {
+  FILE *file;
+  char *name;
+  IOBuffer *buffer;
+  gpMode mode;
+  gpDest dest;
+  pthread_t thread;
+} gpStream;
+
+/*** globals used to track the shell language concepts  ***/
+int 	     interrupt;			/* true if C-C has been pressed */
+int 	     auto_break;		/* if true, zero exit status forces macros to escape */
+int 	     loop_next; 		/* set to true when next (or continue) is called */
+int 	     loop_last; 		/* set to true when last (or return) is called */
+int 	     loop_break;		/* set to true when break is called */
+int          is_script;                 /* being run within a shell script */
+
+/*** basic opihi shell functions ***/
+void          general_init            	PROTO((int *argc, char **argv));
+void          program_init            	PROTO((int *argc, char **argv));
+void          startup               	PROTO((int *argc, char **argv));
+int           opihi                     PROTO((int argc, char **argv));
+int           multicommand          	PROTO((char *line));
+int getServer ();
+void          multicommand_InitServer   PROTO((void));
+void multicommand_StopServer ();
+
+int           command               	PROTO((char *line, char **outline, int VERBOSE));
+char         *expand_vars           	PROTO((char *line));
+char         *expand_vectors        	PROTO((char *line));
+char         *parse                 	PROTO((char *line));
+char        **parse_commands        	PROTO((char *, int *));
+void          welcome                   PROTO(());
+
+int           add_listentry             PROTO((int ThisList, char *line));
+int           is_for_loop           	PROTO((char *line));
+int           is_if_block           	PROTO((char *line));
+int           is_list               	PROTO((char *line));
+int           is_loop               	PROTO((char *line));
+int           is_task               	PROTO((char *line));
+int           is_task_exit             	PROTO((char *line));
+int           is_task_exec             	PROTO((char *line));
+int           is_macro_create       	PROTO((char *line));
+void          InitLists                 PROTO(());
+int current_list_depth ();
+int increase_list_depth ();
+int decrease_list_depth ();
+char *get_next_listentry (int ThisList);
+
+void          InitCommands              PROTO(());
+void          AddCommand                PROTO((Command *new));
+int           DeleteCommand             PROTO((Command *command));
+Command      *MatchCommand              PROTO((char *name, int VERBOSE, int EXACT));
+void          sort_commands             PROTO((int *seq));
+
+void          SetCurrentMacroData	PROTO((char *name, int depth));
+Macro        *NewMacro			PROTO((char *name));
+int           DeleteMacro		PROTO((Macro *macro));
+Macro        *MatchMacro		PROTO((char *name, int VERBOSE, int EXACT));
+void          InitMacros                PROTO((void));
+char         *GetMacroName              PROTO((void));
+int           GetMacroDepth             PROTO((void));
+void          ListMacro                 PROTO((Macro *macro));
+void          ListMacros                PROTO(());
+void          FreeMacro                 PROTO((Macro *macro));
+CommandF     *find_macro_command        PROTO((char *name));
+
+int           exec_loop                 PROTO((Macro *loop));
+/* char         *get_next_listentry    	PROTO((int ThisList)); */
+/* char         *remove_listentry      	PROTO((int current)); */
+
+int           ConfigInit            	PROTO((int *argc, char **argv));
+void          ConfigFree            	PROTO((void));
+char         *VarConfig             	PROTO((char *keyword, char *mode, void *ptr));
+char         *VarConfigEntry           	PROTO((char *keyword, char *mode, int entry, void *ptr));
+
+int           init_error                PROTO((void));
+int           push_error                PROTO((char *line));
+int           print_error               PROTO((void));
+void          handle_interrupt      	PROTO((int));
+char        **command_completer     	PROTO((const char *, int, int));
+char         *command_generator     	PROTO((const char *text, int state));
+char        **completion_matches    	PROTO((char *, rl_compentry_func_t *));
+void          print_commands            PROTO((FILE *f));
+void          InitCommands              PROTO((void));
+
+/* command line parsing */
+char         *thisword              	PROTO((char *));
+char         *nextword              	PROTO((char *));
+char         *lastword              	PROTO((char *, char *));
+char         *thisvar               	PROTO((char *));
+char         *aftervar              	PROTO((char *));
+char         *lastvar               	PROTO((char *, char *));
+char         *thiscomm              	PROTO((char *));
+char         *nextcomm              	PROTO((char *));
+char         *opihi_append              PROTO((char *output, int *Noutput, char *start, char *stop));
+void          interpolate_slash         PROTO((char *line));
+
+/* macro functions (mapped to commands) */
+int 	      macro_create 		PROTO((int, char **)); 
+int 	      macro_delete 		PROTO((int, char **)); 
+int 	      macro_edit   		PROTO((int, char **));
+int 	      macro_exec   		PROTO((int, char **));
+int 	      macro_list_f 		PROTO((int, char **));  /* "macro_list" is a readline func */
+int 	      macro_read   		PROTO((int, char **));
+int 	      macro_write   		PROTO((int, char **));
+
+char 	     *memstr        		PROTO((char *m1, char *m2, int n));
+int  	      write_fmt     		PROTO((int fd, char *format, ...));
+char 	     *opihi_version 		PROTO(());
+char 	     *strip_version 		PROTO((char *input));
+
+/* gprint functions */
+void          gprintInit      		PROTO(());
+gpStream     *gprintGetStream 		PROTO((gpDest dest));
+void          gprintSetBuffer 		PROTO((gpDest dest));
+IOBuffer     *gprintGetBuffer 		PROTO((gpDest dest));
+void          gprintSetFileAllThreads   PROTO((gpDest dest, char *filename));
+void          gprintSetFileThisThread   PROTO((gpDest dest, char *filename));
+void          gprintSetFile   		PROTO((gpStream *stream, gpDest dest, char *filename));
+FILE         *gprintGetFile   		PROTO((gpDest dest));
+char         *gprintGetName   		PROTO((gpDest dest));
+int           gprint          		PROTO((gpDest dest, char *format, ...));
+int           gwrite          		PROTO((char *buffer, int size, int N, gpDest dest));
+
+/* socket functions */
+int InitServerSocket (SockAddress *Address);
+int WaitServerSocket (int InitSocket, SockAddress *Address);
+int GetClientSocket (char *hostname);
+int InitServerSocket_Named (char *hostname, SockAddress *Address);
+int DefineValidIP ();
+
+void FreeCommands ();
+void FreeMacros ();
+void FreeBuffers ();
+void FreeVectors ();
+void FreeVariables ();
+void FreeLists ();
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/user.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/user.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/include/user.h	(revision 22322)
@@ -0,0 +1,260 @@
+int abszero         PROTO((int, char **));
+int applyfit        PROTO((int, char **));
+int applyfit2d      PROTO((int, char **));
+int badimages       PROTO((int, char **));
+int box             PROTO((int, char **));
+int calextract      PROTO((int, char **));
+int calmextract     PROTO((int, char **));
+int cals            PROTO((int, char **));
+int catlog          PROTO((int, char **));
+int ccd             PROTO((int, char **));
+int ccdextract      PROTO((int, char **));
+int center          PROTO((int, char **));
+int cgrid           PROTO((int, char **));
+int clear           PROTO((int, char **));
+int cmatch          PROTO((int, char **));
+int cmd             PROTO((int, char **));
+int cmdextract      PROTO((int, char **));
+int cmpread         PROTO((int, char **));
+int concat          PROTO((int, char **));
+int contour         PROTO((int, char **));
+int cplot           PROTO((int, char **));
+int create          PROTO((int, char **));
+int csystem         PROTO((int, char **));
+int ctimes          PROTO((int, char **));
+int cursor          PROTO((int, char **));
+int czplot          PROTO((int, char **));
+int datafile        PROTO((int, char **));
+int date            PROTO((int, char **));
+int ddmagextract    PROTO((int, char **));
+int ddmags          PROTO((int, char **));
+int delete          PROTO((int, char **));
+int device          PROTO((int, char **));
+int dmagaves        PROTO((int, char **));
+int dmagextract     PROTO((int, char **));
+int dmagmeas        PROTO((int, char **));
+int dmags           PROTO((int, char **));
+int dmt             PROTO((int, char **));
+int dumpmags        PROTO((int, char **));
+int elixir          PROTO((int, char **));
+int extract         PROTO((int, char **));
+int file            PROTO((int, char **));
+int fit             PROTO((int, char **));
+int fit2d           PROTO((int, char **));
+int gaussjordan     PROTO((int, char **));
+int gcat            PROTO((int, char **));
+int gimages         PROTO((int, char **));
+int grid            PROTO((int, char **));
+int gstar           PROTO((int, char **));
+int gtypes          PROTO((int, char **));
+int histogram       PROTO((int, char **));
+int images          PROTO((int, char **));
+int imbox           PROTO((int, char **));
+int imdata          PROTO((int, char **));
+int imdense         PROTO((int, char **)); 
+int imextract       PROTO((int, char **));
+int imlist          PROTO((int, char **));
+int imphot          PROTO((int, char **));
+int imrough         PROTO((int, char **));
+int imsearch        PROTO((int, char **));
+int imstats         PROTO((int, char **));
+int interpolate     PROTO((int, char **));
+int jpeg            PROTO((int, char **));
+int labels          PROTO((int, char **));
+int lcat            PROTO((int, char **));
+int lcurve          PROTO((int, char **));
+int limits          PROTO((int, char **));
+int list_buffers    PROTO((int, char **));
+int list_vectors    PROTO((int, char **));
+int mcreate         PROTO((int, char **));
+int mextract        PROTO((int, char **));
+int mget            PROTO((int, char **));
+int mset            PROTO((int, char **));
+int pcat            PROTO((int, char **));
+int photcodes       PROTO((int, char **));
+int photresid       PROTO((int, char **));
+int plot            PROTO((int, char **));
+int pmeasure        PROTO((int, char **));
+int precess         PROTO((int, char **));
+int print           PROTO((int, char **));
+int procks          PROTO((int, char **));
+int ps              PROTO((int, char **));
+int rd              PROTO((int, char **));
+int read_vectors    PROTO((int, char **));
+int region          PROTO((int, char **));
+int resid           PROTO((int, char **));
+int resize          PROTO((int, char **));
+int section         PROTO((int, char **));
+int set             PROTO((int, char **));
+int simage          PROTO((int, char **));
+int sort_vectors    PROTO((int, char **));
+int sprintf_opihi   PROTO((int, char **));
+int stats           PROTO((int, char **));
+int style           PROTO((int, char **));
+int subpix          PROTO((int, char **));
+int subraster       PROTO((int, char **));
+int subset          PROTO((int, char **));
+int textline        PROTO((int, char **));
+int tv              PROTO((int, char **));
+int uniq            PROTO((int, char **));
+int vectobuf        PROTO((int, char **));
+int vstat           PROTO((int, char **));
+int wd              PROTO((int, char **));
+int write_vectors   PROTO((int, char **));
+int zap             PROTO((int, char **));
+int zeropts         PROTO((int, char **));
+int zplot           PROTO((int, char **));
+
+static Command user[] = {  
+  {"abszero", 	   abszero,       "find filter zeropts"},
+  {"applyfit",     applyfit,      "apply fit to new vector"},
+  {"applyfit2d",   applyfit2d,    "apply 2-d fit to new vector"},
+  {"badimages",    badimages,     "look for images with anomalous astrometry"},
+  {"badimages",    badimages,     "look for images with anomalous astrometry"},
+  {"box",     	   box,           "draw a box on the plot"},
+  {"calextract",   calextract,    "extract dmags"},
+  {"calextract",   calextract,    "extract dmags"},
+  {"calmextract",  calmextract,   "extract dmags"},
+  {"cals",    	   cals,          "plot calibration data"},
+  {"catalog", 	   catlog,        "plot catalog stars"},
+  {"catalog", 	   catlog,        "plot catalog stars"},
+  {"ccd",     	   ccd,           "plot color-color diagram"},
+  {"ccd",     	   ccd,           "plot color-color diagram"},
+  {"ccdextract",   ccdextract,    "extract star coords from color-color diagram"},
+  {"center",       center,        "center image on coords"},
+  {"center",       center,        "center image on coords"},
+  {"cgrid",   	   cgrid,         "plot sky coordinate grid"},
+  {"cgrid",   	   cgrid,         "plot sky coordinate grid"},
+  {"clear",   	   clear,         "erase plot"},
+  {"cmatch",  	   cmatch,        "match two catalogs"},
+  {"cmatch",  	   cmatch,        "match two catalogs"},
+  {"cmd",     	   cmd,           "plot cmd of stars in current region"},
+  {"cmd",     	   cmd,           "plot cmd of stars in current region"},
+  {"cmdextract",   cmdextract,    "extract stars based on cmd regions"},
+  {"cmpread",      cmpread,       "read data from cmp format files"},
+  {"cmpread",      cmpread,       "read data from cmp format files"},
+  {"concat",  	   concat,        "reduce vector dimension"},
+  {"contour", 	   contour,       "create contour from image"},
+  {"cplot",   	   cplot,         "plot vectors in sky coordinates"},
+  {"cplot",   	   cplot,         "plot vectors in sky coordinates"},
+  {"create",  	   create,        "create a new vector"},
+  {"csystem", 	   csystem,       "convert between coordinate systems"},
+  {"csystem", 	   csystem,       "convert between coordinate systems"},
+  {"ctimes",  	   ctimes,        "convert between time formats"},
+  {"ctimes",  	   ctimes,        "convert between time formats"},
+  {"cursor",  	   cursor,        "get coords from cursor"},
+  {"czplot",  	   czplot,        "plot scaled vectors in sky coordinates"},
+  {"czplot",  	   czplot,        "plot scaled vectors in sky coordinates"},
+  {"datafile",     datafile,      "define file to read vectors"},
+  {"date",    	   date,          "get current date"},
+  {"ddmagextr",    ddmagextract,  "plot magnitude differences"},
+  {"ddmags",       ddmags,        "plot magnitude differences"},
+  {"ddmags",       ddmags,        "plot magnitude differences"},
+  {"delete",  	   delete,        "delete vectors or matrices"},
+  {"device",  	   device,        "set / get current graphics device"},
+  {"dmagaves",     dmagaves,      "plot differential magnitudes between filters"},
+  {"dmagextract",  dmagextract,   "extract stars based on differential magnitudes between filters"},
+  {"dmagmeas",     dmagmeas,      "plot differential magnitudes between filters"},
+  {"dmags",   	   dmags,         "plot differential magnitudes between filters"},
+  {"dmags",   	   dmags,         "plot differential magnitudes between filters"},
+  {"dmt",          dmt,           "plot mag scatter"},
+  {"dmt",          dmt,           "plot mag scatter"},
+  {"dumpmags",     dumpmags,      "custom dB dumping thingy"},
+  {"dumpmags",     dumpmags,      "custom dB dumping thingy"},
+  {"elixir",       elixir,        "get status info from elixir"},
+  {"elixir",       elixir,        "get status info from elixir"},
+  {"extract", 	   extract,       "extract vectors from catalogs"},
+  {"file",    	   file,          "test for a file"},
+  {"fit",     	   fit,           "fit polynomial to vector pair"},
+  {"fit2d",        fit2d,         "fit 2-d polynomial to vector triplet"},
+  {"gaussj",  	   gaussjordan,   "solve Ax = B (N-D)"},
+  {"gcat",         gcat,          "get catalog at location"},
+  {"gcat",         gcat,          "get catalog at location"},
+  {"gimages",      gimages,       "get images at location"},
+  {"gimages",      gimages,       "get images at location"},
+  {"grid",    	   grid,          "plot cartesian grid"},
+  {"gstar",   	   gstar,         "get star statistics"},
+  {"gstar",   	   gstar,         "get star statistics"},
+  {"gtypes",       gtypes,        "get type fractions"},
+  {"gtypes",       gtypes,        "get type fractions"},
+  {"histogram",    histogram,     "generate histogram from vector"},
+  {"images",  	   images,        "plot image boxes"},
+  {"images",  	   images,        "plot image boxes"},
+  {"imbox",   	   imbox,         "plot expected image box"},
+  {"imbox",   	   imbox,         "plot expected image box"},
+  {"imdata",  	   imdata,        "extract data for specific images"},
+  {"imdata",  	   imdata,        "extract data for specific images"},
+  {"imdense", 	   imdense,       "image density plot"},
+  {"imdense", 	   imdense,       "image density plot"},
+  {"imextract",    imextract,     "extract vectors from catalogs"},
+  {"imextract",    imextract,     "extract vectors from catalogs"},
+  {"imlist",  	   imlist,        "list image info"},
+  {"imlist",  	   imlist,        "list image info"},
+  {"imphot",  	   imphot,        "image photometry info"},
+  {"imphot",  	   imphot,        "image photometry info"},
+  {"imrough",      imrough,       "get info from imruf database"},
+  {"imrough",      imrough,       "get info from imruf database"},
+  {"imsearch",     imsearch,      "get info from imreg database"},
+  {"imsearch",     imsearch,      "get info from imreg database"},
+  {"imstats", 	   imstats,       "plot image statistics"},
+  {"imstats", 	   imstats,       "plot image statistics"},
+  {"interpolate",  interpolate,   "interpolate between vector pairs"},
+  {"jpeg",         jpeg,          "write text line on graph"},
+  {"labels",  	   labels,        "define labels for plot"},
+  {"lcat",    	   lcat,          "list catalogs in region"},
+  {"lcat",    	   lcat,          "list catalogs in region"},
+  {"lcurve",  	   lcurve,        "plot lightcurve for a star"},
+  {"lcurve",  	   lcurve,        "plot lightcurve for a star"},
+  {"limits",  	   limits,        "define plot limits"},
+  {"buffers",      list_buffers,  "list the currently allocated buffers"},
+  {"vectors", 	   list_vectors,  "list vectors"},
+  {"local",  	   local,         "define local variables"},
+  {"mcreate", 	   mcreate,       "create a matrix"},
+  {"mextract",	   mextract,      "extract vectors from catalogs"},
+  {"mextract",	   mextract,      "extract vectors from catalogs"},
+  {"mget",    	   mget,          "extract a vector from a matrix"},
+  {"mset",    	   mset,          "insert a vector in a matrix"},
+  {"pcat",    	   pcat,          "plot catalog boundaries"},
+  {"pcat",    	   pcat,          "plot catalog boundaries"},
+  {"photcodes",    photcodes,     "list photometry codes"},
+  {"photcodes",    photcodes,     "list photometry codes"},
+  {"photresid",    photresid,     "plot photometry residuals"},
+  {"plot",    	   plot,          "plot a pair of vectors"},
+  {"pmeasure",	   pmeasure,      "plot individual measurements"},
+  {"pmeasure",	   pmeasure,      "plot individual measurements"},
+  {"precess", 	   precess,       "precess coordinates"},
+  {"precess", 	   precess,       "precess coordinates"},
+  {"print",   	   print,         "write vectors to file"},
+  {"print",   	   print,         "write vectors to file"},
+  {"procks",  	   procks,        "plot rocks"},
+  {"procks",  	   procks,        "plot rocks"},
+  {"ps",      	   ps,            "define labels for plot"},
+  {"rd",           rd,            "load fits image"},
+  {"read",         read_vectors,  "read vectors from datafile"},
+  {"region",  	   region,        "define sky region for plot"},
+  {"region",  	   region,        "define sky region for plot"},
+  {"resid",   	   resid,         "plot residuals"},
+  {"resize",  	   resize,        "set graphics/image window size"},
+  {"section", 	   section,       "define section of graph"},
+  {"set",     	   set,           "vector math"},
+  {"simage",  	   simage,        "plot stars in an image"},
+  {"simage",  	   simage,        "plot stars in an image"},
+  {"sort",    	   sort_vectors,  "sort list of vectors"},
+  {"sprintf", 	   sprintf_opihi, "formated print to variable"},
+  {"stats",   	   stats,         "give statistics on a portion of a buffer"},
+  {"style",   	   style,         "set the style for graph plots"},
+  {"subpix",  	   subpix,        "get subpixel positions"},
+  {"subpix",  	   subpix,        "get subpixel positions"},
+  {"subraster",    subraster,     "subraster of fits image"},
+  {"subset",  	   subset,        "expand vector dimension"},
+  {"textline",     textline,      "write text line on graph"},
+  {"tv",      	   tv,            "display an image on the Kii window"},
+  {"uniq",    	   uniq,          "create a uniq vector subset from a vector"},
+  {"vectobuf",     vectobuf,      "convert vector triplet to buffer"},
+  {"vstat",        vstat,         "get info from imreg database"},
+  {"wd",      	   wd,            "write an image to a file"},
+  {"write",   	   write_vectors, "write vectors to datafile"},
+  {"zap",     	   zap,           "delete pixels"},
+  {"zeropts", 	   zeropts,       "show filter zeropts"},
+  {"zplot",   	   zplot,         "plot x y with size scaled by z"},
+}; 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/Makefile	(revision 22322)
@@ -0,0 +1,60 @@
+default: libdata
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SDIR    =       $(HOME)/lib.data
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# general numerical functions (libdata) #####################
+srcs = \
+$(SDIR)/book.$(ARCH).o                  \
+$(SDIR)/page.$(ARCH).o                  \
+$(SDIR)/fft.$(ARCH).o			\
+$(SDIR)/svdcmp.$(ARCH).o		\
+$(SDIR)/convert.$(ARCH).o		\
+$(SDIR)/bracket.$(ARCH).o		\
+$(SDIR)/spline.$(ARCH).o		\
+$(SDIR)/mrqmin.$(ARCH).o		\
+$(SDIR)/mrq2dmin.$(ARCH).o		\
+$(SDIR)/precess.$(ARCH).o		\
+$(SDIR)/starfuncs.$(ARCH).o		\
+$(SDIR)/gaussian.$(ARCH).o		\
+$(SDIR)/graphtools.$(ARCH).o            \
+$(SDIR)/queues.$(ARCH).o		\
+$(SDIR)/PlotVectors.$(ARCH).o		\
+$(SDIR)/open_kapa.$(ARCH).o             \
+$(SDIR)/style_args.$(ARCH).o
+
+#$(SDIR)/open_graph.$(ARCH).o            \
+#$(SDIR)/open_image.$(ARCH).o            \
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/convert.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libdata.$(ARCH).a: $(srcs)
+$(LIB)/libdata.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libdata.a: $(LIB)/libdata.$(ARCH).a
+$(DESTLIB)/libdata.$(DLLTYPE): $(LIB)/libdata.$(ARCH).$(DLLTYPE)
+
+libdata: $(DESTLIB)/libdata.a $(DESTLIB)/libdata.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libdata.a
+	rm -f $(DESTLIB)/libdata.$(DLLTYPE)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/PlotVectors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/PlotVectors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/PlotVectors.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "display.h"
+
+int PlotVectorPair (int kapa, int Npts, float *xValues, float *yValues, Graphdata *graphmode) {
+
+  KapaPrepPlot (kapa, Npts, graphmode);
+  KapaPlotVector (kapa, Npts, xValues, "x");
+  KapaPlotVector (kapa, Npts, yValues, "y");
+
+  return (TRUE);
+}
+
+int PlotVectorTriplet (int kapa, int Npts, float *xValues, float *yValues, float *zValues, Graphdata *graphmode) {
+
+  KapaPrepPlot   (kapa, Npts, graphmode);
+  KapaPlotVector (kapa, Npts, xValues, "x");
+  KapaPlotVector (kapa, Npts, yValues, "y");
+  KapaPlotVector (kapa, Npts, zValues, "z");
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/book.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/book.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/book.c	(revision 22322)
@@ -0,0 +1,127 @@
+# include "data.h"
+
+Book **books;   /* book to store the list of all books */
+int    Nbooks;   /* number of currently defined books */
+int    NBOOKS;   /* number of currently allocated books */
+
+void InitBooks () {
+  Nbooks = 0;
+  NBOOKS = 16;
+  ALLOCATE (books, Book *, NBOOKS); 
+}
+
+void FreeBooks () {
+
+  int i;
+
+  for (i = 0; i < Nbooks; i++) {
+    FreeBook (books[i]);
+  }
+  free (books);
+}
+
+void InitBook (Book *book, char *name) {
+
+    book[0].name = strcreate (name);
+
+    book[0].Npages = 0;
+    book[0].NPAGES = 16;
+    ALLOCATE (book[0].pages, Page *, book[0].NPAGES);
+    ALLOCATE (book[0].pageIDs, char *, book[0].NPAGES);
+    // ALLOCATE (book[0].index, int, book[0].NPAGES);
+}
+
+void FreeBook (Book *book) {
+
+    int i;
+
+    free (book[0].name);
+    for (i = 0; i < book[0].Npages; i++) {
+	FreePage (book[0].pages[i]);
+	free (book[0].pageIDs[i]);
+    }
+    free (book[0].pages);
+    free (book[0].pageIDs);
+    // free (book[0].index);
+    free (book);
+}
+
+/* return the given book */
+Book *GetBook (int where) {
+
+  if (where < 0) where += Nbooks;
+  if (where < 0) return NULL;
+  if (where >= Nbooks) return NULL;
+  return (books[where]);
+}
+
+/* return the given book */
+Book *FindBook (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nbooks; i++) {
+    if (!strcmp (books[i][0].name, name)) {
+      return (books[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named book */
+Book *CreateBook (char *name) {
+
+  int N;
+  Book *book;
+
+  book = FindBook (name);
+  if (book != NULL) return (book);
+
+  N = Nbooks;
+  Nbooks ++;
+  CHECK_REALLOCATE (books, Book *, NBOOKS, Nbooks, 16);
+  ALLOCATE (book, Book, 1);
+  InitBook (book, name);
+  books[N] = book;
+  return (book);
+}
+
+/* delete a book */
+int DeleteBook (Book *book) {
+
+  int i, N, NBOOKS_2;
+
+  /* find book in book list */
+  N = -1;
+  for (i = 0; i < Nbooks; i++) {
+    if (books[i] == book) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  for (i = N; i < Nbooks - 1; i++) {
+    books[i] = books[i + 1];
+  }
+  Nbooks --;
+  NBOOKS_2 = MAX (16, NBOOKS / 2);
+  if (Nbooks < NBOOKS_2) {
+    NBOOKS = NBOOKS_2;
+    REALLOCATE (books, Book *, NBOOKS);
+  }
+
+  FreeBook (book);
+  return (TRUE);
+}
+
+/* list known books */
+void ListBooks () {
+
+  int i;
+
+  for (i = 0; i < Nbooks; i++) {
+    gprint (GP_ERR, "%-15s %3d\n", books[i][0].name, books[i][0].Npages);
+  }
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/bracket.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/bracket.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/bracket.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "data.h"
+
+/* fast operation to find an entry just below (0) or above (1) value */
+/* input list must be sorted */
+int bracket (double *list, int Nlist, int mode, double value) {
+
+  int Nlo, Nhi, N;
+
+  if (mode == 0) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] < value) {
+	Nlo = N;
+      } else {
+	Nhi = N + 1;
+      }
+    }
+    return (Nlo);
+  }
+  if (mode == 1) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] > value) {
+	Nhi = N;
+      } else {
+	Nlo = N - 1;
+      }
+    }
+    return (Nhi);
+  }
+  return (0);
+}
+
+int ibracket (int *list, int Nlist, int mode, double value) {
+
+  int Nlo, Nhi, N;
+
+  if (mode == 0) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] < value) {
+	Nlo = N;
+      } else {
+	Nhi = N + 1;
+      }
+    }
+    return (Nlo);
+  }
+  if (mode == 1) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] > value) {
+	Nhi = N;
+      } else {
+	Nlo = N - 1;
+      }
+    }
+    return (Nhi);
+  }
+  return (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/convert.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/convert.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/convert.c	(revision 22322)
@@ -0,0 +1,305 @@
+# include "convert.h"
+# define _XOPEN_SOURCE /* glibc2 (strptime) needs this */
+# include <time.h>
+
+/** additional time / coordinate conversions not supplied by libohana **/
+
+int hh_hms (double hh, int *hr, int *mn, double *sc) {
+
+  int N, flag;
+
+  flag = SIGN(hh);
+  hh = fabs(hh);
+
+  // rationalize hh to range -24.0 < hh < 24.0
+  if (hh >= 24.0) {
+    N = (int)(hh/24.0);
+    hh -= 24.0*N;
+  }
+
+  *hr = (int) hh;
+  *mn = (int) 60*(hh - *hr);
+  *sc = 3600.0*(hh - *hr - *mn / 60.0);
+  if (*sc > 59.99) {
+    *sc = 0.0;
+    *mn += 1.0;
+  }
+  *hr *= flag;
+  return (TRUE);
+}
+ 
+int dd_dms (double dd, int *dg, int *mn, double *sc) {
+
+  int flag;
+
+  flag = SIGN(dd);
+  dd = fabs (dd);
+  *dg = (int) dd;
+  *mn = (int) 60*(dd - *dg);
+  *sc = 3600.0*(dd - *dg - *mn/60.0);
+  if (*sc > 59.99) {
+    *sc = 0;
+    *mn += 1.0;
+  }
+  *dg *= flag;
+  return (TRUE);
+}
+ 
+int hms_format (char *line, double value) {
+
+  int hr, mn;
+  double sc;
+
+  hh_hms (value, &hr, &mn, &sc);
+  hr = (int) value;
+  if (isnan (value))
+    sprintf (line, "xx:xx:xx.xx");
+  else {
+    if (value < 0) {
+      sprintf (line, "-%02d:%02d:%05.2f", abs(hr), mn, sc);
+    } else {
+      sprintf (line, "+%02d:%02d:%05.2f", hr, mn, sc);
+    }
+  }      
+  return (TRUE);
+}
+
+int dms_format (char *line, double value) {
+
+  int dg, mn;
+  double sc;
+
+  dd_dms (value, &dg, &mn, &sc);
+  if (value < 0) {
+    sprintf (line, "-%02d:%02d:%05.2f", abs(dg), mn, sc);
+  } else {
+    sprintf (line, "+%02d:%02d:%05.2f", dg, mn, sc);
+  }
+  return (TRUE);
+}
+
+/***** convert 00:00:00 or 00:00 to 0 - 86400 ****/
+int hms_to_sec (char *string, time_t *second) {
+  
+  char *p;
+  struct tm time;
+
+  p = strptime (string, "%H:%M:%S", &time);
+  if (p != NULL) goto valid;
+
+  p = strptime (string, "%H:%M", &time);
+  if (p != NULL) goto valid;
+
+  return (FALSE);
+    
+valid:
+  if (*p) return (FALSE);
+  *second = time.tm_hour*3600 + time.tm_min*60 + time.tm_sec;
+  return (TRUE);
+}
+
+/***** convert Mon[@00:00:00] or 00:00 to 0 - 86400*7 ****/
+int day_to_sec (char *string, time_t *second) {
+  
+  char *p;
+  struct tm time;
+
+  bzero (&time, sizeof(time));
+  p = strptime (string, "%A@%H:%M:%S", &time);
+  if (p != NULL) goto valid;
+
+  p = strptime (string, "%A@%H:%M", &time);
+  if (p != NULL) goto valid;
+
+  p = strptime (string, "%A@%H", &time);
+  if (p != NULL) goto valid;
+
+  p = strptime (string, "%A", &time);
+  if (p != NULL) goto valid;
+
+  return (FALSE);
+
+valid:
+  if (*p) return (FALSE);
+  *second = time.tm_wday*86400 + time.tm_hour*3600 + time.tm_min*60 + time.tm_sec;
+  return (TRUE);
+}
+
+/***** convert seconds to HH:MM:SS ****/
+char *ohana_sec_to_hms (time_t second) {
+  
+  struct tm *gmt;
+  char *line;
+
+  ALLOCATE (line, char, 64);
+  gmt   = gmtime (&second);
+  sprintf (line, "%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+  return (line);
+}
+
+/***** convert seconds to Day@HH:MM:SS ****/
+char *ohana_sec_to_day (time_t second) {
+  
+  struct tm *gmt;
+  char *line;
+
+  ALLOCATE (line, char, 64);
+  gmt   = gmtime (&second);
+  switch (gmt[0].tm_wday) {
+    case 0:
+      sprintf (line, "Sun@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 1:
+      sprintf (line, "Mon@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 2:
+      sprintf (line, "Tue@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 3:
+      sprintf (line, "Wed@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 4:
+      sprintf (line, "Thu@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 5:
+      sprintf (line, "Fri@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 6:
+      sprintf (line, "Sat@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+  }
+  return (line);
+}
+
+int hh_hm (double hh, int *hr, double *mn) {
+
+  int flag;
+
+  flag = SIGN(hh);
+  hh = fabs (hh);
+
+  *mn = 60.0*(hh - (int)hh);
+  *hr = (int) hh;
+  *hr *= flag;
+  return (TRUE);
+}
+
+char *meade_deg_to_str (double deg) {
+
+  int hr;
+  double mn;
+  char *line;
+
+  ALLOCATE (line, char, 16);
+
+  hh_hm (deg, &hr, &mn);
+
+  sprintf (line, "%03d:%04.1f", abs(hr), mn);
+  return (line);
+}
+
+char *meade_ra_to_str (double deg) {
+
+  int hr;
+  double mn;
+  char *line;
+
+  ALLOCATE (line, char, 16);
+
+  hh_hm (deg/15.0, &hr, &mn);
+
+  sprintf (line, "%02d:%04.1f", abs(hr), mn);
+  return (line);
+}
+
+char *meade_dec_to_str (double deg) {
+
+  int hr;
+  double mn;
+  char *line;
+
+  ALLOCATE (line, char, 16);
+
+  hh_hm (deg, &hr, &mn);
+
+  if (deg < 0) {
+    sprintf (line, "-%02d:%04.1f", abs(hr), mn);
+  } else {
+    sprintf (line, "+%02d:%04.1f", hr, mn);
+  }      
+  return (line);
+}
+
+/* convert UNIX time to a value referenced to the TimeReference in the given unit */
+double TimeValue (time_t time, time_t TimeReference, int TimeFormat) {
+
+  double value, dt;
+
+  dt = (time > TimeReference) ? (time - TimeReference) : -1 * (double)(TimeReference - time);
+  switch (TimeFormat) {
+  case TIME_JD:
+    value = time / 86400.0 + 2440587.5;
+    break;
+  case TIME_MJD:
+    value = time / 86400.0 + 40587.0;
+    break;
+  case TIME_DAYS:
+    value = dt / 86400.0;
+    break;
+  case TIME_HOURS:
+    value = dt / 3600.0;
+    break;
+  case TIME_MINUTES:
+    value = dt / 60.0;
+    break;
+  case TIME_SECONDS:
+  default:
+    value = dt;
+    break;
+  }
+  return (value);
+}
+  
+/* convert time value referenced to the TimeReference in the given unit to UNIX time */
+time_t TimeRef (double value, time_t TimeReference, int TimeFormat) {
+
+  int dt;
+  time_t time;
+
+  switch (TimeFormat) {
+  case TIME_JD:
+    time = (value -  2440587.5) * 86400.0;
+    return (time);
+    break;
+  case TIME_MJD:
+    time = (value -  40587.0) * 86400.0;
+    return (time);
+    break;
+  case TIME_DAYS:
+    dt = value * 86400.0;
+    break;
+  case TIME_HOURS:
+    dt = value * 3600.0;
+    break;
+  case TIME_MINUTES:
+    dt = value * 60.0;
+    break;
+  case TIME_SECONDS:
+  default:
+    dt = value;
+    break;
+  }
+
+  time = TimeReference + dt;
+  return (time);
+}
+
+/* times may be in forms as:
+ * 20040200450s (N seconds since 1970.0)
+ * 2440900.232j (julian date)
+ * 99/02/23,03:22:18 (date string)
+ * (separators may be anything except space, +, -)
+ * 99:02:15:12:23:30
+ * 99:02:15:12h23m30s
+ */
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/fft.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/fft.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/fft.c	(revision 22322)
@@ -0,0 +1,172 @@
+# include "data.h"
+
+// fft based on code by Douglas L. Jones (see note at EOF). modified for Ohana C style
+void fft1D (float *x, float *y, int n, int Nbit, int forward) {
+
+  int i,j,k,n1,n2;
+  float c,s,e,a,t1,t2;        
+  double factor;
+         
+  // bit-reverse
+  j = 0; 
+  n2 = n/2;
+  for (i = 1; i < n - 1; i++) {
+    n1 = n2;
+    while ( j >= n1 ) {
+      j -= n1;
+      n1 /= 2;
+    }
+    j += n1;
+               
+    if (i < j) {
+      t1 = x[i];
+      x[i] = x[j];
+      x[j] = t1;
+      t1 = y[i];
+      y[i] = y[j];
+      y[j] = t1;
+    }
+  }
+                                          
+  n1 = 0; /* FFT */
+  n2 = 1;
+                                             
+  if (forward) {
+    factor = +2.0*M_PI;
+  } else {
+    factor = -2.0*M_PI;
+  }
+
+  for (i=0; i < Nbit; i++) {
+    n1 = n2;
+    n2 = n2 + n2;
+    e = factor/n2;
+    a = 0.0;
+                                             
+    for (j=0; j < n1; j++) {
+      c = cos(a);
+      s = sin(a);
+      a = a + e;
+                                            
+      for (k=j; k < n; k=k+n2) {
+	t1 = c*x[k+n1] - s*y[k+n1];
+	t2 = s*x[k+n1] + c*y[k+n1];
+	x[k+n1] = x[k] - t1;
+	y[k+n1] = y[k] - t2;
+	x[k] = x[k] + t1;
+	y[k] = y[k] + t2;
+      }
+    }
+  }
+                                      
+  // re-normalize
+  for (i = 0; i < n; i++) {
+    x[i] /= n;
+    y[i] /= n;
+  }
+
+  return;
+}                          
+
+// This implementation uses the 1-D fft above for each of the vectors in each dimension.
+// This requires 2(Nx*Ny*...) mem copies, but the fft operations are likely to happen in
+// cache.
+int fftND (float *x, float *y, int Ndim, int *Nsize, int forward) {
+
+  int i, nIndex, minor, major, iDim;
+  int step, Nmajor, Nminor, Nmax, Ntotal;
+  int *Nbit;
+  float *tmpX, *tmpY;
+
+  ALLOCATE (Nbit, int, Ndim);
+
+  // find the longest axis and allocate storage for that length
+  Nmax = 0;
+  Ntotal = 1;
+  for (i = 0; i < Ndim; i++) {
+    Nmax = MAX(Nmax, Nsize[i]);
+    Ntotal *= Nsize[i];
+    if (!IsBinary (Nsize[i], &Nbit[i])) {
+      free (Nbit);
+      return (FALSE);
+    }
+  }
+  ALLOCATE (tmpX, float, Nmax);
+  ALLOCATE (tmpY, float, Nmax);
+  
+  step = 1;
+  Nminor = 1;
+  Nmajor = Ntotal;
+  for (iDim = 0; iDim < Ndim; iDim++) {
+    step *= Nsize[iDim];
+    Nmajor /= Nsize[iDim];
+
+    // we perform the FFT along all other dimensions 
+    for (major = 0; major < Nmajor; major++) {
+      for (minor = 0; minor < Nminor; minor++) {
+	// nIndex = minor + i*Nminor + major*step;
+	// extract the data values to the temp vector
+	nIndex = minor + major*step;
+	for (i = 0; i < Nsize[iDim]; i++) {
+	  tmpX[i] = x[nIndex];
+	  tmpY[i] = y[nIndex];
+	  nIndex += Nminor;
+	}
+
+	fft1D (tmpX, tmpY, Nsize[iDim], Nbit[iDim], forward);
+
+	// replace the result vectors
+	nIndex = minor + major*step;
+	for (i = 0; i < Nsize[iDim]; i++) {
+	  x[nIndex] = tmpX[i];
+	  y[nIndex] = tmpY[i];
+	  nIndex += Nminor;
+	}
+      }
+    }
+    Nminor *= Nsize[iDim];
+  }
+  free (Nbit);
+  free (tmpX);
+  free (tmpY);
+  return (TRUE);
+}
+
+// check that a number is binary (2^Nbit).  returns int(log_2(N)) in Nbit
+int IsBinary (int N, int *Nbit) {
+
+  int i, Nset;
+
+  if (Nbit != NULL) *Nbit = 0;
+  Nset = 0;
+  for (i = 0; i < 8*sizeof(int); i++) {
+    if (N & 0x01) {
+      Nset ++;
+      if (Nbit != NULL) *Nbit = i;
+    }
+    N >>= 1;
+  }
+  if (Nset > 1) return (FALSE);
+  return (TRUE);  
+}
+
+/**********************************************************/
+/* fft.c                                                  */
+/* (c) Douglas L. Jones                                   */
+/* University of Illinois at Urbana-Champaign             */
+/* January 19, 1992                                       */
+/*                                                        */
+/*   fft: in-place radix-2 DIT DFT of a complex input     */
+/*                                                        */
+/*   input:                                               */
+/* n: length of FFT: must be a power of two               */
+/* m: n = 2**m                                            */
+/*   input/output                                         */
+/* x: float array of length n with real part of data     */
+/* y: float array of length n with imag part of data     */
+/*                                                        */
+/*   Permission to copy and use this program is granted   */
+/*   under a Creative Commons "Attribution" license       */
+/*   http://creativecommons.org/licenses/by/1.0/          */
+/**********************************************************/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/gaussian.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/gaussian.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/gaussian.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "data.h"
+
+static int Ngaussint = 0;
+static double *gaussint;
+
+extern double drand48();
+
+double gaussian (double x, double mean, double sigma) {
+
+  double f;
+
+  f = exp (-0.5 * SQ(x - mean) / SQ(sigma)) / sqrt(2 * M_PI * SQ(sigma));
+
+  return (f);
+
+}
+
+/* integrate a gaussian from -5 sigma to +5 sigma */
+void gauss_init (int Nbin) {
+ 
+  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 (Ngaussint == Nbin) return;
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  Ngaussint = Nbin;
+  ALLOCATE (gaussint, double, Ngaussint + 1);
+
+  val = 0;
+  dx = 1.0 / Ngaussint;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+  mean = 0.0;
+  sigma = 1.0;
+ 
+  for (i = 0, x = -7.0; (i < Ngaussint) && (x < 7.0); x += dx)  {
+    df = (3.0*gaussian(x    , mean, sigma) + 
+          9.0*gaussian(x+dx1, mean, sigma) +
+          9.0*gaussian(x+dx2, mean, sigma) + 
+          3.0*gaussian(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+    if (val > (i + 0.5) / (double) Ngaussint) {
+      gaussint[i] = x + dx / 2.0;
+      i++;
+    }
+  }
+}
+
+double rnd_gauss (double mean, double sigma) {
+ 
+  int i;
+  double y;
+ 
+  y = drand48();
+  i = Ngaussint*y;
+  y = gaussint[i]*sigma + mean;
+ 
+  return (y);
+ 
+}
+ 
+double int_gauss (int i) {
+  double y;
+  y = gaussint[i];
+  return (y);
+}
+ 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/graphtools.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/graphtools.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/graphtools.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "data.h"
+
+void SetLimits (Vector *xvec, Vector *yvec, Graphdata *graphmode) {
+
+  double maxX, minX, maxY, minY, range;
+  int i;
+
+  if (xvec != NULL) {
+    maxX = minX = xvec[0].elements[0];
+    for (i = 1; i < xvec[0].Nelements; i++) {
+      if (!finite(xvec[0].elements[i])) continue;
+      maxX = MAX (maxX, xvec[0].elements[i]);
+      minX = MIN (minX, xvec[0].elements[i]);
+    }
+    range = maxX - minX;
+    if (range == 0) range = 0.001 * maxX;
+    if (range == 0) range = 0.001;
+    graphmode[0].xmin = minX - 0.05*range;
+    graphmode[0].xmax = maxX + 0.05*range;
+  }
+
+  if (yvec != NULL) {
+    maxY = minY = yvec[0].elements[0];
+    for (i = 1; i < yvec[0].Nelements; i++) {
+      if (!finite(yvec[0].elements[i])) continue;
+      maxY = MAX (maxY, yvec[0].elements[i]);
+      minY = MIN (minY, yvec[0].elements[i]);
+    }
+    range = maxY - minY;
+    if (range == 0) range = 0.0011 * maxY;
+    if (range == 0) range = 0.0011;
+    graphmode[0].ymin = minY - 0.05*range;
+    graphmode[0].ymax = maxY + 0.05*range;
+  }
+  SetGraph (graphmode);
+
+  set_variable ("XMIN", graphmode[0].xmin);
+  set_variable ("XMAX", graphmode[0].xmax);
+  set_variable ("YMIN", graphmode[0].ymin);
+  set_variable ("YMAX", graphmode[0].ymax);
+}
+
+void SetLimitsRaw (float *xvec, float *yvec, int Nelements, Graphdata *graphmode) {
+
+  double maxX, minX, maxY, minY, range;
+  int i;
+
+  if (xvec != NULL) {
+    maxX = minX = xvec[0];
+    for (i = 1; i < Nelements; i++) {
+      if (!finite(xvec[i])) continue;
+      maxX = MAX (maxX, xvec[i]);
+      minX = MIN (minX, xvec[i]);
+    }
+    range = maxX - minX;
+    if (range == 0) range = 0.001 * maxX;
+    if (range == 0) range = 0.001;
+    graphmode[0].xmin = minX - 0.05*range;
+    graphmode[0].xmax = maxX + 0.05*range;
+  }
+
+  if (yvec != NULL) {
+    maxY = minY = yvec[0];
+    for (i = 1; i < Nelements; i++) {
+      if (!finite(yvec[i])) continue;
+      maxY = MAX (maxY, yvec[i]);
+      minY = MIN (minY, yvec[i]);
+    }
+    range = maxY - minY;
+    if (range == 0) range = 0.0011 * maxY;
+    if (range == 0) range = 0.0011;
+    graphmode[0].ymin = minY - 0.05*range;
+    graphmode[0].ymax = maxY + 0.05*range;
+  }
+  SetGraph (graphmode);
+
+  set_variable ("XMIN", graphmode[0].xmin);
+  set_variable ("XMAX", graphmode[0].xmax);
+  set_variable ("YMIN", graphmode[0].ymin);
+  set_variable ("YMAX", graphmode[0].ymax);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/hashes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/hashes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/hashes.c	(revision 22322)
@@ -0,0 +1,49 @@
+
+/* I need a structure which will be appropriate to carry the psMetadataConfig data.  the main 
+   requirements are:
+
+   - name-based components
+   - string-indexing on columns
+
+   basic elements:
+
+   container->item->element->value
+
+   element: name, value (type?)
+   item->name, Nelement, elements
+   container->items, Nitems
+
+   user interactions
+
+   blob list
+   blob listitems (blob)
+   blob create (blob)
+   blob delete (blob)
+   blob listkeys (blob.item)
+
+   blob getvalue (blob.item.key) -var word
+   blob setvalue (blob.item.key) value
+
+   blob getitem (blob.item)  : list all key/value pairs
+   blob newitem (blob.item)
+   blob delitem (blob.item)
+   blob popitem (blob)
+
+   blob readqueue (queue)    : convert queue in MDC format to blob
+   *** this needs to be able to match by keys against existing items
+   
+   need equivalents to:
+   queuepush -uniq key
+   queuepop -
+   queuesize
+   
+
+   items should be sorted by name so we can lookup an item quickly
+
+
+
+   other related opihi data concepts
+   
+   $a = @function (output of function set to value?)
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/mrq2dmin.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/mrq2dmin.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/mrq2dmin.c	(revision 22322)
@@ -0,0 +1,210 @@
+# include "data.h"  /* only needed for the ALLOCATE def */
+
+/* need to pass in a function of the form:
+   funcs (x, t, a, Npar, dy/da) 
+   returns y (x,t) for Npar parameters a along with dy/da at (x,t)
+   dy carries 1/sig^2 
+*/
+
+# define VERY_VERBOSE 0
+
+static float **alpha, **talpha;
+static float **beta, **tbeta;
+static float *partry, *dyda;
+static float ochisq, lambda;
+
+static float *parmin = NULL;
+static float *parmax = NULL;
+
+float mrq2dcof (float *x, float *t, float *y, float *dy, int Npts, 
+	      float *par, int Npar, float **ta, float **tb, 
+	      float (funcs)(float, float, float *, int, float *)) {
+
+  int k, j, i;
+  float ydiff, wt, chisq;
+
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k <= j; k++) ta[j][k] = 0.0;
+    tb[j][0] = 0.0;
+  }
+
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+
+    ydiff = funcs (x[i], t[i], par, Npar, dyda) - y[i];
+    chisq += SQ(ydiff) * dy[i];
+    
+    for (j = 0; j < Npar; j++) {
+      wt = dyda[j] * dy[i];
+      for (k = 0; k <= j; k++) ta[j][k] += wt * dyda[k];
+      tb[j][0] += wt * ydiff;
+    }
+  }
+
+  for (j = 1; j < Npar; j++)
+    for (k = 0; k < j; k++)
+      ta[k][j] = ta[j][k];
+      
+# if (VERY_VERBOSE)
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k < Npar; k++) {
+      gprint (GP_ERR, "%9.3e  ", ta[j][k]);
+    }
+    gprint (GP_ERR, "    :   %9.3e  ", tb[j][0]);
+    gprint (GP_ERR, "\n");
+  }
+# endif
+
+  return (chisq);
+
+}
+
+float mrq2dchi (float *x, float *t, float *y, float *dy, int Npts, 
+		float *par, int Npar, 
+		float (funcs)(float, float, float *, int, float *)) {
+
+  int i;
+  float ydiff, chisq;
+
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+    ydiff = funcs (x[i], t[i], par, Npar, dyda) - y[i];
+    chisq += SQ(ydiff) * dy[i];
+  }
+  return (chisq);
+}
+
+float mrq2dmin (float *x, float *t, float *y, float *dy, int Npts, 
+	      float *par, int Npar, 
+	      float (funcs)(float, float, float *, int, float *), int VERBOSE) {
+
+  int j, k;
+  float chisq;
+
+  /* set up test matrixes for this run */
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k < Npar; k++) talpha[j][k] = alpha[j][k];
+    talpha[j][j] = alpha[j][j] * (1.0 + lambda);
+    tbeta[j][0] = beta[j][0];
+  }
+
+  /* keep this test in here? */
+  if (!fgaussjordan (talpha, tbeta, Npar, 1)) {
+    lambda *= 10.0;
+    return (ochisq);
+  }
+
+  for (j = 0; j < Npar; j++) {
+    partry[j] = par[j] - tbeta[j][0];
+    /*
+    if (parmin != NULL) partry[j] = MAX (parmin[j], partry[j]);
+    if (parmax != NULL) partry[j] = MIN (parmax[j], partry[j]);
+    */
+  }
+
+  chisq = mrq2dcof (x, t, y, dy, Npts, partry, Npar, talpha, tbeta, funcs);
+  if (VERBOSE) { 
+    gprint (GP_ERR, "chisq: %f  ", chisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (j = 0; j < Npar; j++) {
+      gprint (GP_ERR, "%f ", partry[j]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  /* if good, save temp values */
+  if (chisq < ochisq) {
+    lambda *= 0.1;
+    ochisq = chisq;
+    for (j = 0; j < Npar; j++) {
+      for (k = 0; k < Npar; k++) alpha[j][k] = talpha[j][k];
+      beta[j][0] = tbeta[j][0];
+      par[j] = partry[j];
+    }
+  } else {
+    lambda *= 10.0;
+    chisq = ochisq;
+  }
+
+  return (chisq);
+
+}
+
+int mrq2dlimits (float *pmin, float *pmax, int Npar) {
+
+  int i;
+
+  ALLOCATE (parmin, float, Npar);
+  ALLOCATE (parmax, float, Npar);
+  for (i = 0; i < Npar; i++) {
+    parmin[i] = pmin[i];
+    parmax[i] = pmax[i];
+  }
+  return (TRUE);
+}
+
+float mrq2dinit (float *x, float *t, float *y, float *dy, int Npts, 
+	      float *par, int Npar, 
+	      float (funcs)(float, float, float *, int, float *), int VERBOSE) {
+
+  int i;
+
+  ALLOCATE (dyda, float, Npar);
+  ALLOCATE (partry, float, Npar);
+  ALLOCATE (alpha, float *, Npar);
+  ALLOCATE (beta, float *, Npar);
+  ALLOCATE (talpha, float *, Npar);
+  ALLOCATE (tbeta, float *, Npar);
+  for (i = 0; i < Npar; i++) {
+    ALLOCATE (alpha[i], float, Npar);
+    ALLOCATE (beta[i], float, Npar);
+    ALLOCATE (talpha[i], float, Npar);
+    ALLOCATE (tbeta[i], float, Npar);
+  }
+
+  
+  lambda = 0.001;
+  
+  ochisq = mrq2dcof (x, t, y, dy, Npts, par, Npar, alpha, beta, funcs);
+  if (VERBOSE) {
+    gprint (GP_ERR, "chisq: %f  ", ochisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (i = 0; i < Npar; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  return (ochisq);
+
+}
+
+/* don't invoke this in the middle of a run, only near the end */ 
+float **mrq2dcovar (int Npar) {
+
+  fgaussjordan (alpha, beta, Npar, 1);
+  return (alpha);
+
+} 
+
+void mrq2dfree (int Npar) {
+
+  int i;
+
+  for (i = 0; i < Npar; i++) {
+    free (alpha[i]);
+    free (talpha[i]);
+    free (beta[i]);
+    free (tbeta[i]);
+  }
+  free (alpha);
+  free (talpha);
+  free (beta);
+  free (tbeta);
+  free (partry);
+  free (dyda);
+
+  if (parmin != NULL) free (parmin);
+  if (parmax != NULL) free (parmax);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/mrqmin.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/mrqmin.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/mrqmin.c	(revision 22322)
@@ -0,0 +1,159 @@
+# include "data.h"  /* only needed for the ALLOCATE def */
+
+/* need to pass in a function of the form:
+   funcs (x, a, Npar, dyda) 
+   returns f (x) for Npar parameters a, also df/da at x 
+   dy carries 1/sig^2 
+*/
+
+static float **alpha, **talpha;
+static float **beta, **tbeta;
+static float *partry, *dyda;
+static float ochisq, lambda;
+
+float mrqcof (float *x, float *y, float *dy, int Npts, 
+	      float *par, int Npar, float **ta, float **tb, 
+	      float (funcs)(float, float *, int, float *)) {
+
+  int k, j, i;
+  float ydiff, wt, chisq;
+
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k <= j; k++) ta[j][k] = 0.0;
+    tb[j][0] = 0.0;
+  }
+
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+
+    ydiff = funcs (x[i], par, Npar, dyda) - y[i];
+    chisq += SQ(ydiff) * dy[i];
+
+    for (j = 0; j < Npar; j++) {
+      wt = dyda[j] * dy[i];
+      for (k = 0; k <= j; k++) ta[j][k] += wt * dyda[k];
+      tb[j][0] += wt * ydiff;
+    }
+  }
+
+  for (j = 1; j < Npar; j++)
+    for (k = 0; k < j; k++) 
+      ta[k][j] = ta[j][k];
+
+  return (chisq);
+
+}
+
+float mrqmin (float *x, float *y, float *dy, int Npts, 
+	      float *par, int Npar, 
+	      float (funcs)(float, float *, int, float *), int VERBOSE) {
+
+  int j, k;
+  float chisq;
+  float rho, dX, dL;
+
+  /* set up test matrixes for this run */
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k < Npar; k++) talpha[j][k] = alpha[j][k];
+    talpha[j][j] = alpha[j][j] * (1.0 + lambda);
+    tbeta[j][0] = beta[j][0];
+  }
+
+  fgaussjordan (talpha, tbeta, Npar, 1);
+
+  for (j = 0; j < Npar; j++) partry[j] = par[j] - tbeta[j][0];
+
+  /* get linear model prediction */
+  dL = 0;
+  for (j = 0; j < Npar; j++) {
+      dL += 0.5*lambda*SQ(tbeta[j][0]) + tbeta[j][0]*beta[j][0];
+  }
+
+  chisq = mrqcof (x, y, dy, Npts, partry, Npar, talpha, tbeta, funcs);
+  if (VERBOSE) { 
+    gprint (GP_ERR, "chisq: %f  ", chisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (j = 0; j < Npar; j++) {
+      gprint (GP_ERR, "%f ", partry[j]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  /* compare linear model with actual */
+  dX = ochisq - chisq;
+  rho = dX / dL;
+
+  /* if good, save temp values */
+  if (rho > 0) {
+    lambda *= 0.1;
+    ochisq = chisq;
+    for (j = 0; j < Npar; j++) {
+      for (k = 0; k < Npar; k++) alpha[j][k] = talpha[j][k];
+      beta[j][0] = tbeta[j][0];
+      par[j] = partry[j];
+    }
+  } else {
+    lambda *= 10.0;
+    chisq = ochisq;
+  }
+
+  return (chisq);
+
+}
+
+float mrqinit (float *x, float *y, float *dy, int Npts, 
+	      float *par, int Npar, 
+	      float (funcs)(float, float *, int, float *), int VERBOSE) {
+
+  int i;
+
+  ALLOCATE (dyda, float, Npar);
+  ALLOCATE (partry, float, Npar);
+  ALLOCATE (alpha, float *, Npar);
+  ALLOCATE (beta, float *, Npar);
+  ALLOCATE (talpha, float *, Npar);
+  ALLOCATE (tbeta, float *, Npar);
+  for (i = 0; i < Npar; i++) {
+    ALLOCATE (alpha[i], float, Npar);
+    ALLOCATE (beta[i], float, Npar);
+    ALLOCATE (talpha[i], float, Npar);
+    ALLOCATE (tbeta[i], float, Npar);
+  }
+  
+  lambda = 0.01;
+  
+  ochisq = mrqcof (x, y, dy, Npts, par, Npar, alpha, beta, funcs);
+  if (VERBOSE) {
+    gprint (GP_ERR, "chisq: %f  ", ochisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (i = 0; i < Npar; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+  return (ochisq);
+}
+
+/* don't invoke this in the middle of a run, only near the end */ 
+float **mrqcovar (int Npar) {
+  fgaussjordan (alpha, beta, Npar, 1);
+  return (alpha);
+} 
+
+void mrqfree (int Npar) {
+
+  int i;
+
+  for (i = 0; i < Npar; i++) {
+    free (alpha[i]);
+    free (talpha[i]);
+    free (beta[i]);
+    free (tbeta[i]);
+  }
+  free (alpha);
+  free (talpha);
+  free (beta);
+  free (tbeta);
+  free (partry);
+  free (dyda);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/open_graph.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/open_graph.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/open_graph.c	(revision 22322)
@@ -0,0 +1,176 @@
+# include "display.h"
+# include "shell.h"
+# define DEBUG 0
+
+/* we have space for several kapa windows */
+# define NXGRAPH 5
+
+static int       Active;
+static int       Xgraph[NXGRAPH];  
+static Graphdata graphdata[NXGRAPH];
+
+void QuitGraph () {
+
+  int i;
+  
+  for (i = 0; i < NXGRAPH; i++) {
+    KiiClose (Xgraph[i]);
+  }
+}
+
+void InitGraph () {
+
+  int i;
+
+  Active = 0;
+  for (i = 0; i < NXGRAPH; i++) {
+    Xgraph[i] = -1;
+
+    graphdata[i].xmin = graphdata[i].ymin = 0.0;
+    graphdata[i].xmax = graphdata[i].ymax = 1.0;
+    graphdata[i].style = graphdata[i].ptype = 0;
+    graphdata[i].ltype = graphdata[i].color = 0;
+    graphdata[i].etype = graphdata[i].ebar = 0;
+    graphdata[i].lweight = graphdata[i].size = 1.0;
+    
+    graphdata[i].coords.pc1_1 = graphdata[i].coords.pc2_2 = 1.0;
+    graphdata[i].coords.pc1_2 = graphdata[i].coords.pc2_1 = 0.0;
+    strcpy (graphdata[i].coords.ctype, "RA---LIN");
+    graphdata[i].coords.crval1 = 0.0;
+    graphdata[i].coords.crval2 = 0.0;
+    graphdata[i].coords.crpix1 = 0.0;
+    graphdata[i].coords.crpix2 = 0.0;
+    graphdata[i].coords.cdelt1 = graphdata[i].coords.cdelt2 = 1.0;
+    graphdata[i].flipeast = TRUE;
+    graphdata[i].flipnorth = FALSE;
+    strcpy (graphdata[i].axis, "2222");
+    strcpy (graphdata[i].ticks, "2222");
+    strcpy (graphdata[i].labels, "2222");
+  }
+}
+
+/* set SIGPIPE to this function to close cleanly */ 
+void XGraphDead (int input) {
+  signal (SIGPIPE, XGraphDead);
+  gprint (GP_ERR, "kapa is dead, must restart\n");
+  Xgraph[Active] = -1;
+}
+
+/** start socketed connection */
+int open_graph (int N) {
+
+  int fd;
+  char *kapa_exec, name[16];
+  
+  kapa_exec = get_variable ("KAPA");
+  if (kapa_exec == (char *) NULL) {
+    gprint (GP_ERR, "variable KAPA not found\n");
+    return (FALSE);
+  }
+
+  snprintf (name, 16, "[%d]", N);
+  fd = KiiOpen (kapa_exec, name);
+  free (kapa_exec);
+
+  if (fd < 0) {
+    gprint (GP_ERR, "error starting kapa\n");
+    return (FALSE);
+  } 
+
+  Xgraph[N] = fd;
+  return (TRUE);
+}
+
+int close_graph (int N) {
+
+  if (N <  0) return (FALSE);
+  if (N >= NXGRAPH) return (FALSE);
+
+  KiiClose (Xgraph[N]); 
+  Xgraph[N] = -1;
+  return (TRUE);
+}
+
+/* return pointers for current Xgraph, set if desired, test, open if needed */
+int GetGraph (Graphdata *data, int *socket, int *N) {
+
+  int i, n;
+  char buffer[70];
+
+  SetImageDevice (FALSE);
+  if (N == (int *) NULL) {
+    n = Active;
+  } else {
+    if (*N >= NXGRAPH) {
+      gprint (GP_ERR, "invalid Xgraph window %d\n", *N); 
+      return (FALSE);
+    }
+    if (*N < 0) {
+      *N = n = Active;
+    } else {
+      Active = n = *N;
+    }
+  }
+  
+  /* test Xgraph[0], flush junk from pipe */
+  signal (SIGPIPE, XGraphDead);
+  fcntl (Xgraph[n], F_SETFL,  O_NONBLOCK); 
+  for (i = 0; (read (Xgraph[n], buffer, 64) > 0) && (i < 20); i++);
+  fcntl (Xgraph[n], F_SETFL, !O_NONBLOCK); 
+  
+  if (Xgraph[n] < 1) {
+    if (!open_graph(n)) {
+      return (FALSE);
+    }
+  }
+
+  if (data != (Graphdata *) NULL) *data  = graphdata[n];
+  if (socket != (int *) NULL) *socket = Xgraph[n];
+
+  return (TRUE);
+
+}
+
+/* return pointers for given Xgraph, don't set or open */
+int GetGraphData (Graphdata *data, int *sock, int *N) {
+
+  int n;
+
+  if (N == (int *) NULL) {
+    n = Active;
+  } else {
+    if (*N >= NXGRAPH) {
+      gprint (GP_ERR, "invalid Xgraph window %d\n", *N); 
+      return (FALSE);
+    }
+    if (*N < 0) {
+      n = Active;
+    } else {
+      n = *N;
+    }
+  }
+  
+  if (data != (Graphdata *) NULL) *data  = graphdata[n];
+  if (sock != (int *) NULL) *sock = Xgraph[n];
+
+  return (TRUE);
+
+}
+
+/* assign given values to current Xgraph */
+void SetGraph (Graphdata data) {
+  graphdata[Active] = data;
+}
+
+/** internal tracking of current active device type **/
+
+static int       IsImage = FALSE;
+
+int GetCurrentDevice () {
+  return (IsImage);
+}
+
+void SetImageDevice (int state) {
+  IsImage = state;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/open_image.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/open_image.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/open_image.c	(revision 22322)
@@ -0,0 +1,133 @@
+# include "display.h"
+# include "shell.h"
+# define DEBUG 0
+
+/* we have space for several kii windows */
+# define NXIMAGE 5
+
+static int       Active;
+static int       Ximage[NXIMAGE];  
+static char      Ximbuffer[NXIMAGE][512];
+static double    Xzero[NXIMAGE];
+static double    Xrange[NXIMAGE];
+
+void QuitImage () {
+
+  int i;
+  
+  for (i = 0; i < NXIMAGE; i++) {
+    KiiClose (Ximage[i]);
+  }
+}
+
+void InitImage () {
+
+  int i;
+
+  Active = 0;
+  for (i = 0; i < NXIMAGE; i++) {
+    Ximage[i] = -1;
+    Xzero[i] = 0;
+    Xrange[i] = 1024;
+    strcpy (Ximbuffer[i], "none");
+  }
+}
+
+/* set SIGPIPE to this function to close cleanly */ 
+void XImageDead (int input) {
+  signal (SIGPIPE, XImageDead);
+  gprint (GP_ERR, "kii is dead, must restart\n");
+  Ximage[Active] = -1;
+}
+
+/** start socketed connection */
+int open_image (int N) {
+
+  int fd;
+  char *kii_exec, name[16];
+
+  kii_exec = get_variable ("KII");
+  if (kii_exec == (char *) NULL) {
+    gprint (GP_ERR, "variable KII not found\n");
+    return (FALSE);
+  }
+
+  snprintf (name, 16, "[%d]", N);
+  fd = KiiOpen (kii_exec, name);
+  free (kii_exec);
+
+  if (fd < 0) {
+    gprint (GP_ERR, "error starting kii\n");
+    return (FALSE);
+  } 
+
+  Ximage[N] = fd;
+  return (TRUE);
+}
+
+int close_image (int N) {
+
+  if (N <  0) return (FALSE);
+  if (N >= NXIMAGE) return (FALSE);
+
+  KiiClose (Ximage[N]); 
+  Ximage[N] = -1;
+  return (TRUE);
+}
+
+/* return pointers for current Ximage, set if desired, test, open if needed */
+int GetImage (int *socket, int *N) {
+
+  int i, n;
+  char buffer[70];
+
+  SetImageDevice (TRUE);
+  if (N == (int *) NULL) {
+    n = Active;
+  } else {
+    if (*N >= NXIMAGE) {
+      gprint (GP_ERR, "invalid Ximage window %d\n", *N); 
+      return (FALSE);
+    }
+    if (*N < 0) {
+      *N = n = Active;
+    } else {
+      Active = n = *N;
+    }
+  }
+  
+  /* test Ximage[0], flush junk from pipe */
+  signal (SIGPIPE, XImageDead);
+  fcntl (Ximage[n], F_SETFL,  O_NONBLOCK); 
+  for (i = 0; (read (Ximage[n], buffer, 64) > 0) && (i < 20); i++);
+  fcntl (Ximage[n], F_SETFL, !O_NONBLOCK); 
+  
+  if (Ximage[n] < 1) {
+    if (!open_image(n)) {
+      return (FALSE);
+    }
+  }
+
+  if (socket != (int *) NULL) *socket = Ximage[n];
+
+  return (TRUE);
+
+}
+
+void SetImageName (char *name) {
+  strcpy (Ximbuffer[Active], name);
+}
+
+char *GetImageName () {
+  return (Ximbuffer[Active]);
+}
+ 
+// leave this information on kapa, request when needed? 
+void SetImageScale (double zero, double range) {
+  Xzero[Active] = zero;
+  Xrange[Active] = range;
+}
+void GetImageScale (double *zero, double *range) {
+  *zero = Xzero[Active];
+  *range = Xrange[Active];
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/open_kapa.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/open_kapa.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/open_kapa.c	(revision 22322)
@@ -0,0 +1,292 @@
+# include "display.h"
+# include "shell.h"
+# include <assert.h>
+
+
+/* kapa support for the new version of kapa (v2.0), which has both graph and image
+ * elements merged into a single display device.  The user may now open an arbitrary
+ * number of kapa windows, and the display information is retrieved from kapa across the
+ * socket when it is needed.  Communication is now via an INET socket (not a UNIX socket).
+ */
+
+/* list of available socket connections */
+static int        Active;        // currently active socket entry (index value, not socket value)
+static int       *Socket = NULL; // list of available sockets
+static char     **Device = NULL; // list of device names for each socket
+static int       Ndevice = 0;    // number of available sockets / devices
+
+void InitKapa () {
+
+  Active  = -1;					  // -1 is the INVALID entry
+  Ndevice = 0;					  // number of defined sockets
+  ALLOCATE (Device, char *, 1);			  // for future REALLOCATE calls
+  ALLOCATE (Socket, int, 1);			  // for future REALLOCATE calls
+}
+
+void FreeKapa () {
+  
+  free (Device);
+  free (Socket);
+}
+
+// add new device name if not found
+int AddKapaDevice (char *name) {
+
+  int N;
+
+  N = FindKapaDevice (name);
+  if (N != -1) return (N);
+  N = Ndevice;
+  Ndevice ++;
+  REALLOCATE (Device, char *, Ndevice);
+  REALLOCATE (Socket, int, Ndevice);
+  Device[N] = strcreate (name);
+  Socket[N] = -1;
+  return (N);
+}
+
+// delete device by name, close if not closed
+int DelKapaDevice (char *name) {
+
+  int i, N;
+
+  N = FindKapaDevice (name);
+  if (N == -1) return (FALSE);
+
+  if (Socket[N] != -1) close (Socket[N]);
+  free (Device[N]);
+  for (i = N; i < Ndevice - 1; i++) {
+    Device[i] = Device[i+1];
+    Socket[i] = Socket[i+1];
+  }
+
+  if (N == Active) {
+    Active = -1;
+  }
+
+  Ndevice --;
+  REALLOCATE (Device, char *, Ndevice);
+  REALLOCATE (Socket, int, Ndevice);
+
+  return (TRUE);
+}
+
+// returns the entry of the requested device, or -1 if not found
+int FindKapaDevice (char *name) {
+
+  int i;
+
+  if (name == NULL) return (-1); 
+
+  for (i = 0; i < Ndevice; i++) {
+    if (!strcmp(Device[i], name)) return (i);
+  }
+  return (-1);
+}
+
+// set the active device to the given device, open if needed
+int open_kapa (int entry) {
+
+  int fd;
+  char *kapa_exec, *kapa_name;
+
+  // find the given device number, or create. set this to active
+  assert (entry >= 0);
+  assert (entry <  Ndevice);
+
+  // if the (now) active socket is not open, open it
+  if (Socket[entry] < 0) {
+    kapa_exec = get_variable ("KAPA");
+    if (kapa_exec == (char *) NULL) {
+      gprint (GP_ERR, "variable KAPA not found\n");
+      return (FALSE);
+    }
+
+    // KAPA may be either kapa://host or /path/to/program
+    ALLOCATE (kapa_name, char, strlen(Device[entry]) + 5);
+    snprintf (kapa_name, strlen(Device[entry]) + 5, "[%s]", Device[entry]);
+
+    if (!strncmp (kapa_exec, "unix://", 7)) {
+        fd = KapaOpenNamedSocket (&kapa_exec[7], kapa_name);
+    } else {
+	fd = KapaOpen (kapa_exec, kapa_name);
+    }
+
+    free (kapa_exec);
+    free (kapa_name);
+
+    if (fd < 0) {
+      gprint (GP_ERR, "error starting kapa device %s\n", Device[entry]);
+      return (FALSE);
+    } 
+    Socket[entry] = fd;
+  } 
+  Active = entry;
+  return (TRUE);
+}
+
+/**************** graph specific ops *******************/
+
+// return the current device name, if set 
+char *GetKapaName () {
+  if (Active < 0) return NULL;
+  return Device[Active];
+}
+
+/* return pointers for named device or current; open if needed */
+// if fd == NULL, don't return the value
+// if name == NULL, use the currently active device
+int GetGraph (Graphdata *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      entry = AddKapaDevice ("0");
+    } else {
+      entry = Active;
+    }
+  } else {
+    entry = AddKapaDevice (name);
+  }
+  
+  if (!open_kapa (entry)) {
+    return (FALSE);
+  }
+  
+  if (data != NULL) KapaGetGraphData (Socket[Active], data);
+  if (fd != NULL) *fd = Socket[Active];
+
+  return (TRUE);
+}
+
+/* return pointers for given kapa, don't set or open */
+int GetGraphData (Graphdata *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      gprint (GP_ERR, "no active kapa window\n"); 
+      return (FALSE);
+    }
+    entry = Active;
+  } else {
+    entry = FindKapaDevice (name);
+    if (entry < 0) {
+      gprint (GP_ERR, "invalid kapa window %s\n", name); 
+      return (FALSE);
+    }
+  }
+
+  if (fd != NULL) *fd = Socket[entry];
+  if (data != NULL) KapaGetGraphData (Socket[entry], data);
+  return (TRUE);
+}
+
+/* assign given values to current kapa */
+int SetGraph (Graphdata *data) {
+  if (Active < 0) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  if (Socket[Active] == -1) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  KapaSetGraphData (Socket[Active], data);
+  return (TRUE);
+}
+
+/************* image ops ***********/
+
+/* return pointers for current Ximage, set if desired, test, open if needed */
+int GetImage (KapaImageData *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      entry = AddKapaDevice ("0");
+    } else {
+      entry = Active;
+    }
+  } else {
+    entry = AddKapaDevice (name);
+  }
+  
+  if (!open_kapa (entry)) {
+    return (FALSE);
+  }
+  
+  if (data != NULL) KapaGetImageData (Socket[Active], data);
+  if (fd != NULL) *fd = Socket[Active];
+
+  return (TRUE);
+}
+
+/* return pointers for given kapa, don't set or open */
+int GetImageData (KapaImageData *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      gprint (GP_ERR, "no active kapa window\n"); 
+      return (FALSE);
+    }
+    entry = Active;
+  } else {
+    entry = FindKapaDevice (name);
+    if (entry < 0) {
+      gprint (GP_ERR, "invalid kapa window %s\n", name); 
+      return (FALSE);
+    }
+  }
+
+  if (fd != NULL) *fd = Socket[entry];
+  if (data != NULL) KapaGetImageData (Socket[entry], data);
+  return (TRUE);
+}
+
+/* assign given values to current kapa */
+int SetImage (KapaImageData *data) {
+  if (Active < 0) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  if (Socket[Active] == -1) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  KapaSetImageData (Socket[Active], data);
+  return (TRUE);
+}
+
+int close_kapa (char *name) {
+
+  int N;
+
+  N = FindKapaDevice (name);
+  if (N == -1) {
+    if (Active < 0) return (FALSE);
+    name = Device[Active];
+  }
+  DelKapaDevice (name);
+  return (TRUE);
+}
+
+void QuitKapa () {
+
+  int i;
+  
+  for (i = 0; i < Ndevice; i++) {
+    if (Socket[i] != -1) close (Socket[i]);
+    if (Device[i] != NULL) free (Device[i]);
+  }
+  REALLOCATE (Socket, int, 1);
+  REALLOCATE (Device, char *, 1);
+
+  Ndevice = 0;
+  Active = -1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/page.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/page.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/page.c	(revision 22322)
@@ -0,0 +1,244 @@
+# include "data.h"
+
+void InitPage (Page *page, char *name) {
+
+    page[0].name = strcreate (name);
+
+    page[0].Nwords = 0;
+    page[0].NWORDS = 16;
+    ALLOCATE (page[0].words, char *, page[0].NWORDS);
+    ALLOCATE (page[0].value, char *, page[0].NWORDS);
+}
+
+void FreePage (Page *page) {
+
+    int i;
+
+    free (page[0].name);
+    for (i = 0; i < page[0].Nwords; i++) {
+      free (page[0].words[i]);
+      free (page[0].value[i]);
+    }
+    free (page[0].words);
+    free (page[0].value);
+    free (page);
+}
+
+/* return the given page */
+Page *GetPage (Book *book, int where) {
+
+  if (where < 0) where += book[0].Npages;
+  if (where < 0) return NULL;
+  if (where >= book[0].Npages) return NULL;
+  return (book[0].pages[where]);
+}
+
+/* return the given page with key restrictions */
+Page *GetPageRestricted (Book *book, int where, char *keyName, char *keyValue) {
+
+  int i;
+  int N, Nout;
+  char *value;
+
+  if (where < 0) where += book[0].Npages;
+  if (where < 0) return NULL;
+  if (where >= book[0].Npages) return NULL;
+
+  Nout = -1;
+  if (where >= 0) {
+    N = -1;
+    for (i = 0; (i < book[0].Npages) && (N < where); i++) {
+      value = BookGetWord (book[0].pages[i], keyName);
+      if ((value == NULL) && !strcmp (keyValue, "NULL")) {
+	N++;
+	Nout = i;
+      } 
+      if ((value != NULL) && !strcmp (keyValue, value)) {
+	N++;
+	Nout = i;
+      }
+    }
+  } else {
+    N = 0;
+    for (i = book[0].Npages - 1; (i >= 0) && (N > where); i--) {
+      value = BookGetWord (book[0].pages[i], keyName);
+      if ((value == NULL) && !strcmp (keyValue, "NULL")) {
+	N--;
+      } 
+      if ((value != NULL) && !strcmp (keyValue, value)) {
+	N--;
+      }
+    }
+  }
+
+  if (N != where) return NULL;
+
+  return (book[0].pages[Nout]);
+}
+
+/* return the given page */
+/* XXX use index to find more quickly */
+Page *FindPage (Book *book, char *name) {
+
+  int i;
+
+  for (i = 0; i < book[0].Npages; i++) {
+    if (!strcmp (book[0].pages[i][0].name, name)) {
+      return (book[0].pages[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named page */
+Page *CreatePage (Book *book, char *name) {
+
+  int N;
+  Page *page;
+
+  page = FindPage (book, name);
+  if (page != NULL) return (page);
+
+  N = book[0].Npages;
+  book[0].Npages ++;
+  if (book[0].Npages >= book[0].NPAGES) {
+    book[0].NPAGES += 16;
+    REALLOCATE (book[0].pages, Page *, book[0].NPAGES);
+    REALLOCATE (book[0].pageIDs, char *, book[0].NPAGES);
+    // REALLOCATE (book[0].index, int, book[0].NPAGES);
+  }
+  ALLOCATE (page, Page, 1);
+  InitPage (page, name);
+  book[0].pages[N] = page;
+  book[0].pageIDs[N] = strcreate(name);
+  // book[0].index[N] = N;
+  
+  /* at this point, I should sort the index */
+
+  return (page);
+}
+
+void sortpages (int *seq, Page **pages, char **pageIDs, int N) {
+
+# define SWAPFUNC(A,B){ Page *tmpPage; char *tmpID; \
+  tmpPage = pages[A];   pages[A]   = pages[B];   pages[B] = tmpPage; \
+  tmpID   = pageIDs[A]; pageIDs[A] = pageIDs[B]; pageIDs[B] = tmpID; \
+}
+# define COMPARE(A,B)(seq[A] < seq[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+}
+
+/* make a new named page */
+int ShufflePages (Book *book) {
+
+  int i, N, *seq;
+
+  N = book[0].Npages;
+
+  // generate a random index
+  ALLOCATE (seq, int, N);
+  for (i = 0; i < N; i++) {
+    seq[i] = N * drand48();
+  }
+
+  // sort the pages by the random index
+  sortpages (seq, book[0].pages, book[0].pageIDs, N);
+  free (seq);
+
+  return (TRUE);
+}
+
+/* delete a page in a book */
+int DeletePage (Book *book, Page *page) {
+
+  char *pageID;
+  int i, N, NPAGES_2;
+
+  /* find page in page list */
+  N = -1;
+  for (i = 0; i < book[0].Npages; i++) {
+    if (book[0].pages[i] == page) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  pageID = book[0].pageIDs[i];
+
+  for (i = N; i < book[0].Npages - 1; i++) {
+    book[0].pages[i] = book[0].pages[i + 1];
+    book[0].pageIDs[i] = book[0].pageIDs[i + 1];
+    // book[0].index[i] = book[0].index[i + 1];
+  }
+  book[0].Npages --;
+  NPAGES_2 = MAX (16, book[0].NPAGES / 2);
+  if (book[0].Npages < NPAGES_2) {
+    book[0].NPAGES = NPAGES_2;
+    REALLOCATE (book[0].pages, Page *, book[0].NPAGES);
+    REALLOCATE (book[0].pageIDs, char *, book[0].NPAGES);
+    // REALLOCATE (book[0].index, int, book[0].NPAGES);
+  }
+
+  FreePage (page);
+  free (pageID);
+  return (TRUE);
+}
+
+void ListPages (Book *book) {
+
+  int i;
+
+  for (i = 0; i < book[0].Npages; i++) {
+    gprint (GP_ERR, "%-15s %3d\n", book[0].pages[i][0].name, book[0].pages[i][0].Nwords);
+  }
+  return;
+}
+
+void ListWords (Page *page) {
+
+  int i;
+
+  for (i = 0; i < page[0].Nwords; i++) {
+    gprint (GP_ERR, "%-15s %15s\n", page[0].words[i], page[0].value[i]);
+  }
+  return;
+}
+
+int BookSetWord (Page *page, char *word, char *value) {
+
+  int i;
+
+  for (i = 0; i < page[0].Nwords; i++) {
+    if (!strcmp (page[0].words[i], word)) {
+      free (page[0].value[i]);
+      page[0].value[i] = strcreate (value);
+      return TRUE;
+    }
+  }
+
+  page[0].Nwords ++;
+  if (page[0].Nwords >= page[0].NWORDS) {
+    page[0].NWORDS += 16;
+    REALLOCATE (page[0].words, char *, page[0].NWORDS);
+    REALLOCATE (page[0].value, char *, page[0].NWORDS);
+  }      
+  page[0].words[i] = strcreate (word);
+  page[0].value[i] = strcreate (value);
+  return (TRUE);
+}
+
+char *BookGetWord (Page *page, char *word) {
+  int i;
+
+  for (i = 0; i < page[0].Nwords; i++) {
+    if (!strcmp (page[0].words[i], word)) {
+      return (page[0].value[i]);
+    }
+  }
+  return NULL;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/precess.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/precess.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/precess.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include "data.h"
+    
+double get_epoch (char *in_epoch, char mode) {
+
+  int done;
+  double epoch;
+
+  epoch = 2000.0;
+  done = FALSE;
+  if (in_epoch[0] == 'B') {
+    epoch = BtoJ(atof(&in_epoch[1]));
+    done = TRUE;
+  }
+
+  if (in_epoch[0] == 'J') {
+    epoch = atof(&in_epoch[1]);
+    done = TRUE;
+  }
+
+  if (!done && (mode == 'B')) {
+    epoch = BtoJ(atof(in_epoch));
+    done = TRUE;
+  }
+    
+  if (!done && (mode == 'J')) {
+    epoch = atof(in_epoch);
+    done = TRUE;
+  }
+
+  if (!done) {
+    gprint (GP_ERR, "error finding epoch %s\n", in_epoch);
+    return FALSE;
+  }
+  
+  return (epoch);
+
+}
+
+  
+double BtoJ (double in_epoch) {
+
+  double JD, out_epoch;
+
+  JD = (in_epoch - 1900.0)*365.242198781 + 2415020.31352;
+  out_epoch = 2000.0 + (JD - 2451545.0)/365.25;
+
+  return (out_epoch);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/queues.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/queues.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/queues.c	(revision 22322)
@@ -0,0 +1,399 @@
+# include "data.h"
+# define DEBUG 0
+
+Queue **queues;   /* queue to store the list of all queues */
+int    Nqueues;   /* number of currently defined queues */
+int    NQUEUES;   /* number of currently allocated queues */
+
+void InitQueues () {
+  Nqueues = 0;
+  NQUEUES = 16;
+  ALLOCATE (queues, Queue *, NQUEUES); 
+}
+
+void FreeQueues () {
+
+  int i, j;
+
+  for (i = 0; i < Nqueues; i++) {
+    for (j = 0; j < queues[i][0].Nlines; j++) {
+      free (queues[i][0].lines[j]);
+    }
+    free (queues[i][0].lines);
+    free (queues[i][0].name);
+    free (queues[i]);
+  }
+  free (queues);
+}
+
+/* list known queues */
+void ListQueues () {
+
+  int i;
+
+  for (i = 0; i < Nqueues; i++) {
+    gprint (GP_LOG, "%-15s %3d\n", queues[i][0].name, queues[i][0].Nlines);
+  }
+  return;
+}
+
+/* return the given queue */
+Queue *FindQueue (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nqueues; i++) {
+    if (!strcmp (queues[i][0].name, name)) {
+      return (&queues[i][0]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named queue */
+int InitQueue (Queue *queue) {
+
+  int i;
+
+  for (i = 0; i < queue[0].Nlines; i++) {
+    free (queue[0].lines[i]);
+  }
+  queue[0].Nlines = 0;
+  queue[0].NLINES = 16;
+  REALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+
+  if (DEBUG) fprintf (stderr, "init: %s (%zx) : %d of %d\n", queue[0].name, (size_t) queue, queue[0].Nlines, queue[0].NLINES);
+  
+  return (TRUE);
+}
+
+/* make a new named queue */
+Queue *CreateQueue (char *name) {
+
+  int N;
+  Queue *queue;
+
+  queue = FindQueue (name);
+  if (queue != NULL) return (queue);
+
+  N = Nqueues;
+  Nqueues ++;
+  CHECK_REALLOCATE (queues, Queue *, NQUEUES, Nqueues, 16);
+  ALLOCATE (queue, Queue, 1);
+  queue[0].Nlines = 0;
+  queue[0].NLINES = 16;
+  queue[0].name = strcreate (name);
+  ALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+  queues[N] = queue;
+  return (queue);
+}
+
+/* delete a queue */
+int DeleteQueue (Queue *queue) {
+
+  int i, N, NQUEUES_2;
+
+  /* find queue in queue list */
+  N = -1;
+  for (i = 0; i < Nqueues; i++) {
+    if (queues[i] == queue) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  for (i = N; i < Nqueues - 1; i++) {
+    queues[i] = queues[i + 1];
+  }
+  Nqueues --;
+  NQUEUES_2 = MAX (16, NQUEUES / 2);
+  if (Nqueues < NQUEUES_2) {
+    NQUEUES = NQUEUES_2;
+    REALLOCATE (queues, Queue *, NQUEUES);
+  }
+
+  free (queue[0].name);
+  for (i = 0; i < queue[0].Nlines; i++) {
+    free (queue[0].lines[i]);
+  }
+  free (queue[0].lines);
+  free (queue);
+  return (TRUE);
+}
+
+void PushNamedQueue (char *name, char *line) {
+
+  Queue *queue;
+  
+  queue = FindQueue (name);
+  if (queue == NULL) {
+    queue = CreateQueue (name);
+  }
+  PushQueue (queue, line);
+  return;
+}
+
+/* push line onto queue.  return chars create new lines */
+void PushQueue (Queue *queue, char *line) {
+
+  int N;
+  char *p, *q;
+
+  p = line;
+  q = strchr (line, '\n');
+  N = queue[0].Nlines;
+  while (q != NULL) {
+    queue[0].lines[N] = strncreate (p, q - p);
+    N++;
+    CHECK_REALLOCATE (queue[0].lines, char *, queue[0].NLINES, N, 16);
+    p = q + 1;
+    q = strchr (p, '\n');
+  }    
+  if (*p) {
+    queue[0].lines[N] = strcreate (p);
+    N++;
+    CHECK_REALLOCATE (queue[0].lines, char *, queue[0].NLINES, N, 16);
+  }
+  queue[0].Nlines = N;
+  return;
+}
+
+// return a newly allocated string containing the requested key value
+char *ChooseSingleKey (char *line, int Key) {
+
+  int i;
+  char *key, *p;
+
+  if (Key == -1) {
+    key = strcreate (line);
+    return (key);
+  }
+
+  key = line;
+  for (i = 0; (i < Key) && (key != NULL); i++) {
+    p = nextword (key);
+    key = p;
+  }
+  key = thisword (key);
+  return (key);
+}
+
+/* construct merged key given keylist of the form K:N:M */ 
+char *ChooseKey (char *line, char *keylist) {
+
+  char *output, *entry, *key, *p, *q;
+  int first, keynum;
+
+  if (line == NULL) return (NULL);
+  if (keylist == NULL) return (line);
+
+  ALLOCATE (output, char, strlen(line) + 1);
+  memset (output, 0, strlen(line) + 1);
+
+  first = TRUE;
+  p = q = keylist;
+  while (q != NULL) {
+    q = strchr (p, ':');
+    if (q == NULL) {
+      entry = strcreate (p);
+    } else {
+      entry = strncreate (p, q - p);
+    }
+    keynum = atoi (entry);
+    free (entry);
+
+    key = ChooseSingleKey (line, keynum);
+
+    if (!first) strcat (output, ":");
+    if (key) {
+	strcat (output, key);
+	free (key);
+    }
+
+    if (q != NULL) p = q + 1;
+    first = FALSE;
+  }
+  return (output);
+}
+
+/* push line onto queue, skipping existing matches (optionally by key) */
+void PushQueueUnique (Queue *queue, char *line, char *Key) {
+
+  int i, j, N, found;
+  char *p, *q, *key1, *key2;
+  Queue tmp;
+
+  /* init tmp queue */
+  tmp.Nlines = 0;
+  tmp.NLINES = 16;
+  ALLOCATE (tmp.lines, char *, tmp.NLINES);
+
+  /* push entries on tmp queue */
+  p = line;
+  q = strchr (line, '\n');
+  N = tmp.Nlines;
+  while (q != NULL) {
+    tmp.lines[N] = strncreate (p, q - p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+    p = q + 1;
+    q = strchr (p, '\n');
+  }    
+  if (*p) {
+    tmp.lines[N] = strcreate (p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+  }
+  tmp.Nlines = N;
+
+  /* add unique entries in tmp to queue */
+  for (i = 0; i < tmp.Nlines; i++) {
+    key1 = ChooseKey (tmp.lines[i], Key);
+    if (key1 == NULL) continue;
+    found = FALSE;
+    for (j = 0; !found && (j < queue[0].Nlines); j++) {
+      key2 = ChooseKey (queue[0].lines[j], Key);
+      if (key2 == NULL) continue;
+      found = !strcmp (key1, key2);
+      free (key2);
+    }      
+    if (!found) PushQueue (queue, tmp.lines[i]);
+    free (key1);
+  }
+  for (i = 0; i < tmp.Nlines; i++) {
+    free (tmp.lines[i]);
+  } 
+  free (tmp.lines);
+  return;
+}
+
+/* push line onto queue, replacing matches (optionally by Key) */
+void PushQueueReplace (Queue *queue, char *line, char *Key) {
+
+  int i, j, N, found;
+  char *p, *q, *key1, *key2;
+  Queue tmp;
+
+  /* init tmp queue */
+  tmp.Nlines = 0;
+  tmp.NLINES = 16;
+  ALLOCATE (tmp.lines, char *, tmp.NLINES);
+
+  /* push entries on tmp queue */
+  p = line;
+  q = strchr (line, '\n');
+  N = tmp.Nlines;
+  while (q != NULL) {
+    tmp.lines[N] = strncreate (p, q - p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+    p = q + 1;
+    q = strchr (p, '\n');
+  }    
+  if (*p) {
+    tmp.lines[N] = strcreate (p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+  }
+  tmp.Nlines = N;
+
+  /* add unique entries in tmp to queue */
+  for (i = 0; i < tmp.Nlines; i++) {
+    key1 = ChooseKey (tmp.lines[i], Key);
+    if (key1 == NULL) continue;
+    found = FALSE;
+    for (j = 0; !found && (j < queue[0].Nlines); j++) {
+      key2 = ChooseKey (queue[0].lines[j], Key);
+      if (key2 == NULL) continue;
+      found = !strcmp (key1, key2);
+      if (found) {
+	// XXX do I need to free queue[0].lines[j]??
+	queue[0].lines[j] = strcreate (tmp.lines[i]);
+      }
+      free (key2);
+    }      
+    if (!found) PushQueue (queue, tmp.lines[i]);
+    free (key1);
+  }
+  for (i = 0; i < tmp.Nlines; i++) {
+    free (tmp.lines[i]);
+  } 
+  free (tmp.lines);
+  return;
+}
+
+char *PopQueue (Queue *queue) {
+
+  int i, NLINES_2;
+  char *line;
+
+  if (queue[0].Nlines == 0) return (NULL);
+  line = queue[0].lines[0];
+
+  for (i = 0; i < queue[0].Nlines - 1; i++) {
+    queue[0].lines[i] = queue[0].lines[i+1];
+  }
+  queue[0].Nlines --;
+
+  /* shrink queue allocation if small enough */
+  NLINES_2 = MAX (16, queue[0].NLINES / 2);
+  if (queue[0].Nlines < NLINES_2) {
+    queue[0].NLINES = NLINES_2;
+    REALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+  }    
+  return (line);
+}
+
+/* pop the first entry which for which the key matches */
+char *PopQueueMatch (Queue *queue, char *Key, char *value) {
+
+  int i, choice, NLINES_2;
+  char *line, *test;
+
+  if (queue[0].Nlines == 0) return (NULL);
+
+  /* find the matching key */
+  choice = -1;
+  for (i = 0; (i < queue[0].Nlines) && (choice == -1); i++) {
+      test = ChooseKey (queue[0].lines[i], Key);
+      if (test == NULL) continue;
+      if (strcmp (value, test)) { 
+	free (test);
+	continue;
+      }
+      free (test);
+      choice = i;
+  }
+  if (choice == -1) return NULL;
+
+  line = queue[0].lines[choice];
+
+  for (i = choice; i < queue[0].Nlines - 1; i++) {
+    queue[0].lines[i] = queue[0].lines[i+1];
+  }
+  queue[0].Nlines --;
+
+  /* shrink queue allocation if small enough */
+  NLINES_2 = MAX (16, queue[0].NLINES / 2);
+  if (queue[0].Nlines < NLINES_2) {
+    queue[0].NLINES = NLINES_2;
+    REALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+  }    
+  return (line);
+}
+
+int PrintQueue (Queue *queue) {
+
+  int i;
+
+  if (queue[0].Nlines == 0) return (TRUE);
+
+  if (DEBUG) fprintf (stderr, "print: %s (%zx) : %d of %d\n", queue[0].name, (size_t) queue, queue[0].Nlines, queue[0].NLINES);
+
+  for (i = 0; i < queue[0].Nlines; i++) {
+    gprint (GP_LOG, "%s\n", queue[0].lines[i]);
+  }
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/spline.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/spline.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/spline.c	(revision 22322)
@@ -0,0 +1,59 @@
+# include "data.h"
+
+/* construct the natural spline for x, y in y2 */
+void spline_construct (float *x, float *y, int N, float *y2) {
+
+  int i;
+  float dy, dx, *tmp;
+  
+  ALLOCATE (tmp, float, N);
+
+  y2[0] = tmp[0] = 0.0;
+  
+  for (i = 1; i < N-1; i++) {
+    dx = (x[i+0] - x[i-1]) / (x[i+1] - x[i-1]);
+    dy = dx * y2[i-1] + 2.0;
+    y2[i] = (dx - 1.0) / dy;
+    tmp[i] = (y[i+1] - y[i+0]) / (x[i+1] - x[i+0]) - (y[i+0] - y[i-1]) / (x[i+0] - x[i-1]);
+    tmp[i] = (6.0 * tmp[i] / (x[i+1] - x[i-1]) - dx*tmp[i-1]) / dy;
+  }
+  
+  y2[N-1] = 0;
+  for (i = N-2; i >= 1; i--)
+    y2[i] = y2[i]*y2[i+1] + tmp[i];
+
+  free (tmp);
+}
+
+/* evaluate spline for x, y, y2 at X */
+float spline_apply (float *x, float *y, float *y2, int N, float X) {
+
+  int i, lo, hi;
+  float dx, a, b, value;
+  
+  /* find correct element in array (x must be sorted) */
+  lo = 0;
+  hi = N-1;
+  while (hi - lo > 1) {
+    i = 0.5*(hi+lo);
+    if (x[i] > X) {
+      hi = i;
+    } else {
+      lo = i;
+    }
+  }
+
+  /* error condition: duplicate abssisca */
+  dx = x[hi] - x[lo];
+  if (dx == 0.0) {
+    return (HUGE_VAL);
+  }
+
+  /* evaluate spline */
+  a = (x[hi] - X) / dx;
+  b = (X - x[lo]) / dx;
+
+  value = a*y[lo] + b*y[hi] + ((a*a*a - a)*y2[lo] + (b*b*b - b)*y2[hi])*(dx*dx) / 6.0;
+  return (value);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/starfuncs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/starfuncs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/starfuncs.c	(revision 22322)
@@ -0,0 +1,251 @@
+# include "data.h"
+
+double get_aperture_stats (Matrix *matrix, int X, int Y, int Npix, int Nborder, double max) {
+
+  double *ring;
+  double x, y, x2, y2, xy, I, sky, FWHMx, FWHMy, value, mag, Sxy;
+  int i, j, n, Npix2, Nring, Nmax;
+  double Npts, gain, dsky2, dmag, peak, offset;
+  char *string;
+  
+  string = get_variable ("GAIN");
+  if (string == (char *) NULL) {
+    gprint (GP_ERR, "assuming a value of 1.0\n");
+    gain = 1.0;
+  } else {
+    gain = atof (string);
+  }
+  Nborder = MAX (1, Nborder);
+  Nborder = MIN (1000, Nborder);
+  
+  Npix2 = (int)(0.5*Npix);
+  Npix = 2 * Npix2 + 1;
+  Nring = 4*Nborder*(Nborder + Npix);
+  ALLOCATE (ring, double, Nring);
+  bzero (ring, sizeof(double)*Nring);
+
+  n = 0;  
+  for (j = 0; j < Nborder; j++) {
+    for (i = X - Npix2 - Nborder; i < X + Npix2 + Nborder + 1; i++, n+=2) {
+      ring[n]   = gfits_get_matrix_value (matrix, i, (int)(Y - Npix2 - j));
+      ring[n+1] = gfits_get_matrix_value (matrix, i, (int)(Y + Npix2 + j));
+    }
+    for (i = Y - Npix2; i < Y + Npix2 + 1; i++, n+=2) {
+      ring[n]   = gfits_get_matrix_value (matrix, (int)(X - Npix2 - j), i);
+      ring[n+1] = gfits_get_matrix_value (matrix, (int)(X + Npix2 + j), i);
+    }
+  }
+  dsort (ring, Nring);
+  for (Npts = sky = dsky2 = 0, i = 0.25*Nring; i < 0.75*Nring; i++, Npts += 1.0) {
+    sky += ring[i];
+    dsky2 += ring[i]*ring[i];
+  }
+  sky = sky / Npts;
+  dsky2 = dsky2 / Npts - sky*sky;
+  free (ring);
+
+  peak = 0;
+  Npts = Nmax = 0;
+  x = y = x2 = y2 = xy = I = 0;
+  for (i = X - Npix2; i < X + Npix2 + 1; i++) {
+    for (j = Y - Npix2; j < Y + Npix2 + 1; j++) {
+      value = gfits_get_matrix_value (matrix, i, j);
+      offset = value - sky;
+      x  += i*offset;
+      y  += j*offset;
+      x2 += i*i*offset;
+      y2 += j*j*offset;
+      xy += i*j*offset;
+      I  += offset;
+      Npts ++;
+      if (value > max) {
+	Nmax ++;
+      }
+      if (value > peak) peak = value;
+    }
+  }
+
+  x = x / I;
+  y = y / I;
+  FWHMx = 2.355*sqrt (fabs(x2 / I - x*x));
+  FWHMy = 2.355*sqrt (fabs(y2 / I - y*y));
+  Sxy   = xy / I - x*y;
+  mag = -2.5*log10(I);
+  dmag = sqrt (fabs(1.0 / (gain*I) + Npts*dsky2 / (I*I)));
+  x = x + 1;
+  y = y + 1;
+  
+  set_variable ("Xg", x);
+  set_variable ("Yg", y);
+  set_variable ("SXg", FWHMx);
+  set_variable ("SYg", FWHMy);
+  set_variable ("SXYg", Sxy);
+  set_variable ("Sg", sky);
+  set_variable ("dSg", sqrt (fabs (dsky2)));
+  set_variable ("Zg", mag);
+  set_variable ("dZg", dmag);
+  set_variable ("Zcg", I);
+  set_variable ("Zpk", peak);
+  set_int_variable ("Nsat", Nmax);
+  
+  gprint (GP_LOG, "%f %f %f %f %f %f %f %f\n", x, y, FWHMx, FWHMy, sky, I, mag, dmag);
+
+  return (mag);
+
+}
+
+static double Raper  =  5;
+static double Rinner = 10;
+static double Router = 15;
+static double *sky = NULL;
+
+int set_rough_radii (double Ra, double Ri, double Ro) {
+
+  Raper = Ra;
+  Rinner = Ri;
+  Router = Ro;
+  if (sky == NULL) {
+    ALLOCATE (sky, double, SQ(2*Router + 1));
+  } else {
+    REALLOCATE (sky, double, SQ(2*Router + 1));
+  }
+  return (TRUE);
+}
+
+/* use a circular aperture */
+int get_rough_star (float *data, int Nx, int Ny, int x, int y,
+		    float *xc, float *yc, 
+		    float *sx, float *sy, float *sxy,
+		    float *zs, float *zp, float *sk) {
+
+  double Ra2, Ri2, Ro2, rad2;
+  int i, j, Npts, Nsky;
+  int Xs, Xe, Ys, Ye, off, Xc, Yc;
+  double peak, fsky, value;
+  double Sx, Sy, Sx2, Sy2, Sxy, Sum;
+  
+  /* define circular boundaries */
+  Ra2 = SQ(Raper);
+  Ri2 = SQ(Rinner);
+  Ro2 = SQ(Router);
+
+  /* measure the sky level */
+  /* boundaries for the outer sky region */
+  Xs = MAX (x - Router, 0);
+  Xe = MIN (x + Router + 1, Nx);
+  Ys = MAX (y - Router, 0);
+  Ye = MIN (y + Router + 1, Ny);
+
+/* this sample uses a circular aperture */
+# if (0)
+  Nsky = 0;  
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      rad2 = SQ(i - x) + SQ(j - y);
+      if (rad2 > Ro2) continue;
+      if (rad2 < Ri2) continue;
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  dsort (sky, Nsky);
+  for (Npts = fsky = 0, i = 0.25*Nsky; i < 0.75*Nsky; i++, Npts += 1.0) {
+    fsky += sky[i];
+  }
+  fsky = fsky / Npts;
+# else
+
+/* this sample uses a square outer annulus, without loop if tests */
+  Nsky = 0;  
+  Xs = MAX (x - Router, 0);
+  Xe = MIN (x - Rinner + 1, Nx);
+  Ys = MAX (y - Rinner, 0);
+  Ye = MIN (y + Rinner + 1, Ny);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  Xs = MAX (x + Rinner, 0);
+  Xe = MIN (x + Router + 1, Nx);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  Xs = MAX (x - Rinner, 0);
+  Xe = MIN (x - Rinner + 1, Nx);
+  Ys = MAX (y - Router, 0);
+  Ye = MIN (y - Rinner + 1, Ny);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  Ys = MAX (y + Rinner, 0);
+  Ye = MIN (y + Router + 1, Ny);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  dsort (sky, Nsky);
+  for (Npts = fsky = 0, i = 0.25*Nsky; i < 0.75*Nsky; i++, Npts += 1.0) {
+    fsky += sky[i];
+  }
+  fsky = fsky / Npts;
+# endif
+
+  /* boundaries for the star region */
+  Xs = MAX (x - Raper, 0);
+  Xe = MIN (x + Raper + 1, Nx);
+  Ys = MAX (y - Raper, 0);
+  Ye = MIN (y + Raper + 1, Ny);
+
+  /** note that this will fail on negative flux objects */
+  peak = Npts = 0;
+  Sx = Sy = Sx2 = Sy2 = Sxy = Sum = 0;
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    Yc = j - y;
+    for (i = Xs; i < Xe; i++) {
+      Xc = i - x;
+      rad2 = SQ(Xc) + SQ(Yc);
+      if (rad2 > Ro2) continue;
+      value = data[i+off] - fsky;
+      Sx  += Xc*value;
+      Sy  += Yc*value;
+      Sx2 += Xc*Xc*value;
+      Sy2 += Yc*Yc*value;
+      Sxy += Xc*Yc*value;
+      Sum += value;
+      Npts ++;
+      if (value > peak) peak = value;
+    }
+  }
+
+  *xc = Sx / Sum;
+  *yc = Sy / Sum;
+  *sx = sqrt (fabs (Sx2 / Sum - SQ(*xc)));
+  *sy = sqrt (fabs (Sy2 / Sum - SQ(*yc)));
+  *sxy = Sxy / Sum;
+  *xc += x;
+  *yc += y;
+  *zs = Sum;
+  *zp = peak;
+  *sk = fsky;
+  /* note sigma is rough: round-off errors can introduce errors */
+  /* using values relative to x,y should minimize this effect */
+
+  return (Npts);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/style_args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/style_args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/style_args.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include "data.h"
+# include "display.h"
+
+int style_args (Graphdata *graphmode, int *argc, char **argv, int *kapa) {
+  
+  int N;
+  char *colorName;
+  char *kapaName;
+
+  kapaName = NULL;
+  if ((N = get_argument (*argc, argv, "-n"))) {
+    remove_argument (N, argc, argv);
+    kapaName = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if (!GetGraph (graphmode, kapa, kapaName)) return (FALSE);
+  FREE (kapaName);
+
+  if (*argc == 1) {
+    kapaName = GetKapaName();
+    colorName = KapaColorName (graphmode[0].color);
+    gprint (GP_ERR, "current style (%s): -x %d -c %s -pt %d -lt %d -lw %f -sz %f\n", kapaName,
+	     graphmode[0].style, colorName, graphmode[0].ptype, 
+	     graphmode[0].ltype, graphmode[0].lweight,
+	     graphmode[0].size);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (*argc, argv, "-lt"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ltype = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-lw"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].lweight = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-pt"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ptype = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "+eb"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ebar = TRUE;
+  }
+  if ((N = get_argument (*argc, argv, "-eb"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ebar = FALSE;
+  }
+  if ((N = get_argument (*argc, argv, "-sz"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].size = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-c"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].color = KapaColorByName (argv[N]);
+    if (graphmode[0].color == -1) return (FALSE);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-x"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].style = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/svdcmp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/svdcmp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.data/svdcmp.c	(revision 22322)
@@ -0,0 +1,211 @@
+# include "data.h"
+
+/*
+  static float at,bt,ct;
+  #define PYTHAG(a,b) ((at=fabs(a)) > (bt=fabs(b)) ? \
+  (ct=bt/at,at*sqrt(1.0+ct*ct)) : (bt ? (ct=at/bt,bt*sqrt(1.0+ct*ct)): 0.0))
+  pythag -> hypot (check on roundoff errors) 
+*/
+
+/* use simple max? */
+#define FSIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
+
+/* n == Nx, m == Ny */
+int svdcmp (float *a, float *w, float *v, int Nx, int Ny) {
+
+  int flag, i, its, j, jj, k, l, nm, status;
+  float c, f, h, s, x, y, z;
+  float anorm=0.0, g = 0.0, scale = 0.0;
+  float *rv1;
+
+  if (Ny < Nx) return (0);
+
+  l = nm = 0;
+  ALLOCATE (rv1, float, Nx);
+  for (i = 0; i < Nx; i++) {
+    l = i + 1;
+    rv1[i] = scale*g;
+    g = s = scale = 0.0;
+    if (i < Ny) {
+      for (k = i; k < Ny; k++) scale += fabs(a[k*Nx + i]);
+      if (scale) {
+	for (k = i; k < Ny; k++) {
+	  a[k*Nx + i] /= scale;
+	  s += a[k*Nx + i]*a[k*Nx + i];
+	}
+	f = a[i*Nx + i];
+	g  = -FSIGN(sqrt(s), f);
+	h = f*g - s;
+	a[i*Nx + i] = f-g;
+	if (i != Nx - 1) {
+	  for (j = l; j < Nx; j++) {
+	    for (s = 0.0, k = i; k < Ny; k++) s += a[k*Nx + i]*a[k*Nx + j];
+	    f = s/h;
+	    for (k = i; k < Ny; k++) a[k*Nx + j] += f*a[k*Nx + i];
+	  }
+	}
+	for (k = i; k < Ny; k++) a[k*Nx + i] *= scale;
+      }
+    }
+    w[i] = scale*g;
+    g = s = scale = 0.0;
+    if ((i < Ny) && (i != (Nx - 1))) {
+      for (k = l; k < Nx; k++) scale += fabs(a[i*Nx + k]);
+      if (scale) {
+	for (k = l; k < Nx; k++) {
+	  a[i*Nx + k] /= scale;
+	  s += a[i*Nx + k]*a[i*Nx + k];
+	}
+	f = a[i*Nx + l];
+	g = -FSIGN(sqrt(s), f);
+	h = f*g - s;
+	a[i*Nx + l] = f-g;
+	for (k = l; k < Nx; k++) rv1[k] = a[i*Nx + k]/h;
+	if (i != Ny - 1) {
+	  for (j = l; j < Ny; j++) {
+	    for (s = 0.0, k = l; k < Nx; k++) s += a[j*Nx + k]*a[i*Nx + k];
+	    for (k = l; k < Nx; k++) a[j*Nx + k] += s*rv1[k];
+	  }
+	}
+	for (k = l; k < Nx; k++) a[i*Nx + k] *= scale;
+      }
+    }
+    anorm = MAX(anorm, (fabs(w[i]) + fabs(rv1[i])));
+  }
+
+  for (i = Nx - 1; i >= 0; i--) {
+    if (i < Nx - 1) {
+      if (g) {
+	/* isn't l == n to start?? */
+	for (j = l; j < Nx; j++) v[j*Nx + i] = (a[i*Nx + j]/a[i*Nx + l])/g;
+	for (j = l; j < Nx; j++) {
+	  for (s = 0.0, k = l; k < Nx; k++) s += a[i*Nx + k]*v[k*Nx + j];
+	  for (k = l; k < Nx; k++) v[k*Nx + j] += s*v[k*Nx + i];
+	}
+      }
+      for (j = l; j < Nx; j++) v[i*Nx + j] = v[j*Nx + i] = 0.0;
+    }
+    v[i*Nx + i] = 1.0;
+    g = rv1[i];
+    l = i;
+  }
+  for (i = Nx - 1; i >= 0; i--) {
+    l = i + 1;
+    g = w[i];
+    if (i < Nx - 1)
+      for (j = l; j < Nx; j++) a[i*Nx + j] = 0.0;
+    if (g) {
+      g = 1.0/g;
+      if (i != Nx - 1) {
+	for (j = l; j < Nx; j++) {
+	  for (s = 0.0, k = l; k < Ny; k++) s += a[k*Nx + i]*a[k*Nx + j];
+	  f = (s/a[i*Nx + i])*g;
+	  for (k = i; k < Ny; k++) a[k*Nx + j] += f*a[k*Nx + i];
+	}
+      }
+      for (j = i; j < Ny; j++) a[j*Nx + i] *= g;
+    } else {
+      for (j = i; j < Ny; j++) a[j*Nx + i] = 0.0;
+    }
+    ++a[i*Nx + i];
+  }
+
+  status = 1;
+  for (k = Nx - 1; k >= 0; k--) {
+    for (its = 0; its < 30; its++) {
+      flag = 1;
+      for (l = k; l >= 0; l--) {
+	nm = l - 1;
+	if (fabs(rv1[l])+anorm == anorm) {
+	  flag = 0;
+	  break;
+	}
+	if (fabs(w[nm])+anorm == anorm) break;
+      }
+      if (flag) {
+	c = 0.0;
+	s = 1.0;
+	for (i = l; i < k; i++) {
+	  f = s*rv1[i];
+	  if (fabs(f)+anorm != anorm) {
+	    g = w[i];
+	    h = hypot (f, g);
+	    w[i] = h;
+	    h = 1.0/h;
+	    c = g*h;
+	    s = (-f*h);
+	    for (j = 0; j < Ny; j++) {
+	      y = a[j*Nx + nm];
+	      z = a[j*Nx + i];
+	      a[j*Nx + nm] = y*c + z*s;
+	      a[j*Nx + i] = z*c - y*s;
+	    }
+	  }
+	}
+      }
+      z = w[k];
+      if (l == k) {
+	if (z < 0.0) {
+	  w[k] = -z;
+	  for (j = 0; j < Nx; j++) v[j*Nx + k] = (-v[j*Nx + k]);
+	}
+	break;
+      }
+      if (its == 29) status = 0;
+      x = w[l];
+      nm = k-1;
+      y = w[nm];
+      g = rv1[nm];
+      h = rv1[k];
+      f = ((y-z)*(y+z) + (g-h)*(g+h))/(2.0*h*y);
+      g = hypot (f, 1.0);
+      f = ((x-z)*(x+z) + h*((y/(f+FSIGN(g, f)))-h))/x;
+      c = s = 1.0;
+      for (j = l; j < nm; j++) {
+	i = j+1;
+	g = rv1[i];
+	y = w[i];
+	h = s*g;
+	g = c*g;
+	z = hypot (f, h);
+	rv1[j] = z;
+	c = f/z;
+	s = h/z;
+	f = x*c+g*s;
+	g = g*c-x*s;
+	h = y*s;
+	y = y*c;
+	for (jj = 0; jj < Nx; jj++) {
+	  x = v[jj*Nx + j];
+	  z = v[jj*Nx + i];
+	  v[jj*Nx + j] = x*c+z*s;
+	  v[jj*Nx + i] = z*c-x*s;
+	}
+	z = hypot(f, h);
+	w[j] = z;
+	if (z) {
+	  z = 1.0/z;
+	  c = f*z;
+	  s = h*z;
+	}
+	f = (c*g) + (s*y);
+	x = (c*y) - (s*g);
+	for (jj = 0; jj < Ny; jj++) {
+	  y = a[jj*Nx + j];
+	  z = a[jj*Nx + i];
+	  a[jj*Nx + j] = y*c+z*s;
+	  a[jj*Nx + i] = z*c-y*s;
+	}
+      }
+      rv1[l] = 0.0;
+      rv1[k] = f;
+      w[k] = x;
+    }
+  }
+  free(rv1);
+  return (1);
+}
+
+#undef FSIGN
+#undef MAX
+#undef PYTHAG
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/BufferOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/BufferOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/BufferOps.c	(revision 22322)
@@ -0,0 +1,257 @@
+# include "opihi.h"
+
+static Buffer **buffers;
+static int     Nbuffers;
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void InitBuffers () {
+  Nbuffers = 0;
+  ALLOCATE (buffers, Buffer *, 1);
+}
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void FreeBuffers () {
+
+  int i;
+
+  for (i = 0; i < Nbuffers; i++) {
+    gfits_free_header (&buffers[i][0].header);
+    gfits_free_matrix (&buffers[i][0].matrix);
+    free (buffers[i]);
+  }
+  free (buffers);
+}
+
+Buffer *InitBuffer () {
+  Buffer *buf;
+
+  ALLOCATE (buf, Buffer, 1);
+  bzero (buf[0].name, 1024);
+  bzero (buf[0].file, 1024);
+  ALLOCATE (buf[0].matrix.buffer, char, 1);
+  ALLOCATE (buf[0].header.buffer, char, 1);
+  return (buf);
+}
+
+int IsBuffer (char *name) {
+ 
+  int i;
+
+  if (name == NULL) return (FALSE);
+
+  for (i = 0; (i < Nbuffers) && (strcmp(buffers[i][0].name, name)); i++);
+  if (i == Nbuffers) return (FALSE);
+  return (TRUE);
+}
+
+int IsBufferPtr (Buffer *buf) {
+ 
+  int i;
+
+  if (buf == NULL) return (FALSE);
+
+  for (i = 0; (i < Nbuffers) && (buffers[i] != buf); i++);
+  if (i == Nbuffers) return (FALSE);
+  return (TRUE);
+}
+
+Buffer *SelectBuffer (char *name, int mode, int verbose) {
+
+  int i;
+
+  if (name == NULL) goto error;
+  if (ISNUM(name[0])) goto error;
+  if (IsVector(name)) goto error;
+
+  for (i = 0; (i < Nbuffers) && (strcmp(buffers[i][0].name, name)); i++);
+  /* is a new buffer */
+  if (i == Nbuffers) { 
+    if (mode == OLDBUFFER) goto error;
+    // lock to protect static array
+    pthread_mutex_lock (&mutex);
+    Nbuffers ++;
+    REALLOCATE (buffers, Buffer *, Nbuffers);
+    buffers[i] = InitBuffer ();
+    strcpy (buffers[i][0].name, name);
+    pthread_mutex_unlock (&mutex);
+    return (buffers[i]);
+  } 
+  /* is an old buffer */
+  if (mode == NEWBUFFER) goto error;
+  return (buffers[i]);
+
+ error:
+  if (verbose) gprint (GP_ERR, "invalid matrix %s\n", name);
+  return (NULL);
+}
+
+int CreateBuffer (Buffer *buf, int Nx, int Ny, int bitpix, float bzero, float bscale) {
+
+  /* store the default output values */
+  gfits_init_header (&buf[0].header);
+
+  /* assign the necessary internal values */
+  buf[0].header.bitpix   = -32;
+  buf[0].header.Naxes = 2;
+  buf[0].header.Naxis[0] = Nx;
+  buf[0].header.Naxis[1] = Ny;
+
+  buf[0].bitpix = bitpix;
+  buf[0].bzero  = bzero;
+  buf[0].bscale = bscale;
+  
+  /* make some test of the validity of the values */
+
+  /* create the appropriate header and matrix */
+  gfits_create_header (&buf[0].header);
+  gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+
+  return (TRUE);
+}
+  
+/* copy data from in to out - new memory space */
+int CopyNamedBuffer (char *out, char *in) {
+  Buffer *In, *Out;
+  if ((In  = SelectBuffer (in,  OLDBUFFER, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectBuffer (out, ANYBUFFER, FALSE)) == NULL) return (FALSE);
+  CopyBuffer (Out, In);
+  return (TRUE);
+}
+
+int CopyBuffer (Buffer *out, Buffer *in) {
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  strcpy (out[0].file, in[0].file);
+  gfits_copy_matrix (&in[0].matrix, &out[0].matrix);
+  gfits_copy_header (&in[0].header, &out[0].header);
+  return (TRUE);
+}
+
+/* move data from in to out - use old memory space */
+int MoveNamedBuffer (char *out, char *in) {
+  Buffer *In, *Out;
+  if ((In  = SelectBuffer (in,  OLDBUFFER, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectBuffer (out, ANYBUFFER, FALSE)) == NULL) return (FALSE);
+  MoveBuffer (Out, In);
+  return (TRUE);
+}
+int MoveBuffer (Buffer *out, Buffer *in) {
+  int i, j;
+
+  free (out[0].matrix.buffer);
+  free (out[0].header.buffer);
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  out[0].matrix = in[0].matrix;
+  out[0].header = in[0].header;
+  strcpy (out[0].file, in[0].file);
+
+  /* delete buffer entry from buffer list, if it exists */
+  for (i = 0; (i < Nbuffers) && (in != buffers[i]); i++);
+  if (i == Nbuffers) {
+    free (in);
+    return (TRUE);
+  }
+  free (in);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nbuffers - 1; j++) buffers[j] = buffers[j + 1];
+  Nbuffers --;
+  REALLOCATE (buffers, Buffer *, MAX (Nbuffers, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+/* delete by ptr */
+int DeleteBuffer (Buffer *buf) {
+
+  int i, j;
+
+  if (buf == NULL) return (FALSE);
+  for (i = 0; (i < Nbuffers) && (buf != buffers[i]); i++);
+  if (i == Nbuffers) return (FALSE);
+
+  gfits_free_header (&buffers[i][0].header);
+  gfits_free_matrix (&buffers[i][0].matrix);
+  free (buffers[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nbuffers - 1; j++) buffers[j] = buffers[j + 1];
+  Nbuffers --;
+  REALLOCATE (buffers, Buffer *, MAX (Nbuffers, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+  
+/* delete by name */
+int DeleteNamedBuffer (char *name) {
+
+  int i, j;
+
+  if (name == NULL) return (FALSE);
+  for (i = 0; (i < Nbuffers) && (strcmp(buffers[i][0].name, name)); i++);
+  if (i == Nbuffers) return (FALSE);
+
+  gfits_free_header (&buffers[i][0].header);
+  gfits_free_matrix (&buffers[i][0].matrix);
+  free (buffers[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nbuffers - 1; j++) buffers[j] = buffers[j + 1];
+  Nbuffers --;
+  REALLOCATE (buffers, Buffer *, MAX (Nbuffers, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+void dump_buffers (int n) {
+
+  int i;
+
+  gprint (GP_ERR, "try %d\n", n);
+  for (i = 0; i < Nbuffers; i++) {
+    gprint (GP_ERR, "%d  %lx\n", i, (long) buffers[i]);
+    gprint (GP_ERR, "%d  %lx  %s\n", i, (long) buffers[i][0].name, buffers[i][0].name);
+    gprint (GP_ERR, "%d  %lx  %s\n", i, (long) buffers[i][0].file, buffers[i][0].file);
+    gprint (GP_ERR, "%d  %lx  %lx\n", i, (long) &buffers[i][0].header, (long) buffers[i][0].header.buffer);
+    gprint (GP_ERR, "%d  %lx  %lx\n", i, (long) &buffers[i][0].matrix, (long) buffers[i][0].matrix.buffer);
+    gprint (GP_ERR, "%d  %d  %d  %f %f\n", i, buffers[i][0].bitpix, buffers[i][0].unsign, buffers[i][0].bscale, buffers[i][0].bzero);
+  }
+}
+
+int PrintBuffers (int Long) {
+
+  int i;
+
+  if (Nbuffers == 0) {
+    gprint (GP_ERR, "No allocated buffers\n");
+    return (TRUE);
+  }
+  
+  if (Long) {
+    gprint (GP_LOG, "    N       name                      file     X     Y    bytes BP  U   bzero     bscale  \n");
+    for (i = 0; i < Nbuffers; i++) {
+      gprint (GP_LOG, "%5d %10s %25s %5d %5d %10d %3d %1d %10.4e %10.4e\n",
+	       i, buffers[i][0].name, buffers[i][0].file, 
+	       buffers[i][0].header.Naxis[0], buffers[i][0].header.Naxis[1],
+	       buffers[i][0].header.size + buffers[i][0].matrix.size, buffers[i][0].bitpix, 
+	       buffers[i][0].unsign, buffers[i][0].bzero, buffers[i][0].bscale);
+    }
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "    N       name                      file     X     Y    bytes\n");
+  for (i = 0; i < Nbuffers; i++) {
+    gprint (GP_LOG, "%5d %10s %25s %5d %5d %10d\n",
+	     i, buffers[i][0].name, buffers[i][0].file, 
+	     buffers[i][0].header.Naxis[0], buffers[i][0].header.Naxis[1],
+	     buffers[i][0].header.size + buffers[i][0].matrix.size);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/CommandOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/CommandOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/CommandOps.c	(revision 22322)
@@ -0,0 +1,176 @@
+# include "opihi.h"
+
+// the command table is only modified by macro_create: if thread protection is needed, it
+// should be added there.  it may not be needed if programs only call macro_create in a
+// single thread.  pantasks_server only calls macro_create in the 'input' thread, so it is
+// safe.
+
+// if the user has installed the libedit version of readline, we need to modify a couple symbols:
+# ifndef RL_READLINE_VERSION
+# define rl_completion_matches(A,B) completion_matches(A,B)
+# endif
+
+static Command  *commands;
+static int      Ncommands;
+static int      NCOMMANDS;
+
+void InitCommands () {
+  NCOMMANDS = 20;
+  Ncommands = 0;
+  ALLOCATE (commands, Command, NCOMMANDS);
+}
+
+void FreeCommands () {
+  int i;
+
+  for (i = 0; i < Ncommands; i++) {
+    if (commands[i].real) continue;
+    free (commands[i].name);
+    free (commands[i].help);
+  }
+  free (commands);
+}
+
+void AddCommand (Command *new) {
+  
+  commands[Ncommands] = *new;
+  Ncommands ++;
+  if (Ncommands == NCOMMANDS) {
+    NCOMMANDS += 20;
+    REALLOCATE (commands, Command, NCOMMANDS);
+  }
+}
+
+int DeleteCommand (Command *command) {
+
+  int i, Nc;
+
+  Nc = -1;
+  for (i = 0; i < Ncommands; i++) {
+    if (command == &commands[i]) {
+      Nc = i;
+      break;
+    }
+  }
+  if (Nc == -1) {
+    gprint (GP_ERR, "programming error: command not found\n");
+    return (FALSE);
+  }
+
+  free (commands[Nc].name);
+  for (i = Nc + 1; i < Ncommands; i++)
+    commands[i - 1] = commands[i];
+  Ncommands --;
+  REALLOCATE (commands, Command, Ncommands);
+  return (TRUE);
+}
+
+/* return command which unambiguously matches name */
+Command *MatchCommand (char *name, int VERBOSE, int EXACT) {
+
+  int i, match[10], Nmatch;
+
+  /* try for an exact match first */
+  for (i = 0; i < Ncommands; i++) {
+    if (!strcmp (commands[i].name, name)) {
+      return (&commands[i]);
+    }
+  }
+  if (EXACT) {
+    if (VERBOSE) gprint (GP_ERR, "no exact match to %s\n", name);
+    return (NULL);
+  }
+
+  /* not found as complete command, try partial */
+  Nmatch = 0;
+  for (i = 0; (Nmatch < 10) && (i < Ncommands); i++) {
+    if (!strncmp (commands[i].name, name, strlen(name))) {  /* found a command */
+      match[Nmatch] = i;
+      Nmatch ++;
+    }
+  }
+  if (Nmatch == 1) return (&commands[match[0]]);
+
+  if (Nmatch > 1) {
+    if (VERBOSE) {
+      gprint (GP_ERR, "ambiguous command: %s ( ", name);
+      for (i = 0; i < Nmatch; i++) {
+	gprint (GP_ERR, "%s ", commands[match[i]].name);
+      }
+      gprint (GP_ERR, ")\n");
+    }
+    return (NULL);
+  }
+  if (VERBOSE) gprint (GP_ERR, "%s: Command not found.\n", name);
+  return (NULL);
+}  
+
+/* generate a command completion list for readline */
+/**** these probably do not interact well with OHANA memory!!! ****/
+char *command_generator (const char *text, int state) {
+
+  /* i must be remembered from call to call */
+  static int i, len;
+
+  /* On first call, state is set to 0: initial the state */
+  if (!state) {
+    i = -1;  
+    len = strlen (text);
+  }
+
+  /* Return the next partial match from the command list */
+  for (i++; i < Ncommands; i++) {
+    if (!len) 
+      return (strcreate(commands[i].name));
+    if (!strncmp (commands[i].name, text, len)) {
+      return (strcreate(commands[i].name));
+    }
+  }
+
+  /* If no names matched, then return NULL. */
+  return ((char *)NULL);
+}
+
+/* tell readline to use out command_generator rather than basic completion */
+char **command_completer (const char *text, int start, int end) {
+  
+  char **matches;
+
+  matches = (char **) NULL;
+  
+  if (start == 0) {
+    matches = rl_completion_matches (text, command_generator);
+  }
+
+  return (matches);
+}
+
+void sort_commands (int *seq) {
+
+  int i;
+
+  for (i = 0; i < Ncommands; i++) seq[i] = i;
+
+# define SWAPFUNC(A,B){ int tmp = seq[A]; seq[A] = seq[B]; seq[B] = tmp; }
+# define COMPARE(A,B)(strcmp (commands[seq[A]].name, commands[seq[B]].name) < 0)
+
+  OHANA_SORT (Ncommands, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+  
+}
+
+void print_commands (FILE *f) {
+
+  int i, *seq;
+
+  ALLOCATE (seq, int, Ncommands);
+  sort_commands (seq);
+  for (i = 0; i < Ncommands; i++) {
+    fprintf (f, "%-25s -- %s\n", commands[seq[i]].name, commands[seq[i]].help);
+  }
+  free (seq);
+
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/ConfigInit.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "opihi.h"
+
+static char *GlobalConfig = NULL;
+
+// this function is only called at start, so it is not thread protect
+int ConfigInit (int *argc, char **argv) {
+
+  char *config, *file;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    if (file != (char *) NULL) free (file);
+    return (FALSE);
+  }
+
+  GlobalConfig = config;
+
+  free (file);
+  return (TRUE);
+}
+
+// this function is only called at shutdown, so it is not thread protect
+void ConfigFree () {
+  if (GlobalConfig) free (GlobalConfig);
+  return;
+}
+
+char *VarConfig (char *keyword, char *mode, void *ptr) {
+
+  char *answer;
+
+  answer = get_variable (keyword);
+  if (answer == (char *) NULL) {
+    answer = ScanConfig (GlobalConfig, keyword, mode, 0, ptr);
+    return (answer);
+  }
+
+  if (!strcmp (mode, "%s"))  strcpy ((char *) ptr, answer);
+  if (!strcmp (mode, "%d"))  *(int *) ptr       = atoi (answer);
+  if (!strcmp (mode, "%u"))  *(unsigned *) ptr  = atoi (answer);
+  if (!strcmp (mode, "%ld")) *(long *) ptr      = atoi (answer);
+  if (!strcmp (mode, "%hd")) *(short *) ptr     = atoi (answer);
+  if (!strcmp (mode, "%f"))  *(float *) ptr     = atof (answer);
+  if (!strcmp (mode, "%lf")) *(double *) ptr    = atof (answer);
+
+  free (answer);
+  return (ptr);
+}
+
+char *VarConfigEntry (char *keyword, char *mode, int entry, void *ptr) {
+
+  char *answer;
+
+  answer = get_variable (keyword);
+  if (answer == (char *) NULL) {
+    answer = ScanConfig (GlobalConfig, keyword, mode, entry, ptr);
+    return (answer);
+  }
+
+  if (!strcmp (mode, "%s"))  strcpy ((char *) ptr, answer);
+  if (!strcmp (mode, "%d"))  *(int *) ptr       = atoi (answer);
+  if (!strcmp (mode, "%u"))  *(unsigned *) ptr  = atoi (answer);
+  if (!strcmp (mode, "%ld")) *(long *) ptr      = atoi (answer);
+  if (!strcmp (mode, "%hd")) *(short *) ptr     = atoi (answer);
+  if (!strcmp (mode, "%f"))  *(float *) ptr     = atof (answer);
+  if (!strcmp (mode, "%lf")) *(double *) ptr    = atof (answer);
+
+  free (answer);
+  return (ptr);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/ListOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/ListOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/ListOps.c	(revision 22322)
@@ -0,0 +1,270 @@
+# include "opihi.h"
+# include "macro.h"
+
+/*** local static variables used to track the command lists  ***/
+static List *lists;			/* variable to store the list of all lists */
+static int  Nlists;			/* number of currently available lists */
+
+void InitLists () {
+  Nlists = 0;
+  ALLOCATE (lists, List, 1); 
+  return;
+}
+
+void FreeLists () {
+
+  int i, j;
+
+  for (i = 0; i < Nlists; i++) {
+    for (j = 0; j < lists[i].Nlines; j++) {
+      free (lists[i].line[j]);
+    }
+    free (lists[i].line);
+  }
+  free (lists);
+}
+
+int current_list_depth () {
+  return Nlists;
+}
+
+int increase_list_depth () {
+  Nlists ++;
+  REALLOCATE (lists, List, MAX (Nlists + 1, 0) + 1);
+  ALLOCATE (lists[Nlists].line, char *, 16);
+  lists[Nlists].Nalloc = 16;
+  lists[Nlists].Nlines = 0;
+  lists[Nlists].n = 0;
+  return Nlists;
+}
+
+int decrease_list_depth () {
+  
+  int i;
+
+  for (i = 0; i < lists[Nlists].Nlines; i++) {
+    free (lists[Nlists].line[i]);
+  }
+  free (lists[Nlists].line);
+  Nlists --;
+  REALLOCATE (lists, List, MAX (Nlists + 1, 0) + 1);
+  return Nlists;
+}
+
+/* return a new string consisting of the next line in the current list */
+char *get_next_listentry (int ThisList) {
+
+  int Nline;
+  char *output;
+
+  Nline = lists[ThisList].n;
+
+  if (Nline >= lists[ThisList].Nlines) return (NULL);
+
+  output = strcreate (lists[ThisList].line[Nline]);
+  lists[ThisList].n ++;
+  
+  return (output);
+}
+
+# if (0)
+char *remove_listentry (int current) {
+
+  int i;
+  char *output;
+
+  if ((current + 1) >= lists[Nlists].Nlines) 
+    return ((char *) NULL);
+
+  output = lists[Nlists].line[current + 1];
+  
+  for (i = current + 1; i < lists[Nlists].Nlines - 1; i++) {
+    lists[Nlists].line[i] = lists[Nlists].line[i + 1];
+  }
+
+  lists[Nlists].Nlines --;
+  return (output);
+
+}
+# endif 
+
+int add_listentry (int ThisList, char *line) {
+
+  int Nlines;
+
+  Nlines = lists[ThisList].Nlines;
+  lists[ThisList].line[Nlines] = strcreate (line);
+  lists[ThisList].Nlines ++;
+
+  if (lists[ThisList].Nlines == lists[ThisList].Nalloc) {
+    lists[ThisList].Nalloc += 16;
+    REALLOCATE (lists[ThisList].line, char *, lists[ThisList].Nalloc);
+  }
+    
+  return (lists[ThisList].Nlines);
+}
+
+int is_for_loop (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+  
+  status = !strcmp (comm, "for");
+  free (comm);
+  return (status);
+}
+
+int is_macro_create (char *line) {
+
+  int status;
+  char *comm;
+  char *this_macro;
+  CommandF *cmd;
+
+  comm = thisword (line);
+  if (comm == NULL) return (FALSE);
+
+  status = !strcmp (comm, "macro");
+  free (comm);
+  if (!status) return (FALSE);
+  
+  this_macro = thisword (nextword (line));
+  if (this_macro == NULL) return (FALSE);
+
+  cmd = find_macro_command (this_macro);
+  free (this_macro);
+
+  if (cmd == NULL) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
+
+int is_if_block (char *line) {
+
+  char *comm, *temp;
+
+  temp = thisword (nextword (nextword (line)));
+  comm = thisword (line);
+
+  if (comm == NULL) goto escape;
+
+  if (strcmp (comm, "if")) goto escape;
+
+  /* if (cond) (command) does not define a complete block */
+  if (temp != NULL) goto escape;
+
+  if (temp != NULL) free (temp);
+  if (comm != NULL) free (comm);
+  return (TRUE);
+
+escape: 
+  if (comm != NULL) free (comm);
+  if (temp != NULL) free (temp);
+  return (FALSE);
+}
+
+// list (word) : nested list
+// list (word) -x : not nested list
+// list (word) -split : not nested list
+int is_list_data (char *line) {
+
+  char *comm, *temp;
+
+  temp = thisword (nextword (nextword (line)));
+  comm = thisword (line);
+
+  if (comm == NULL) goto escape;
+
+  if (strcmp (comm, "list")) goto escape;
+
+  /* if (cond) (command) does not define a complete block */
+  if (temp != NULL) {
+      if (!strcmp (temp, "-x")) goto escape;
+      if (!strcmp (temp, "-split")) goto escape;
+      if (!strcmp (temp, "-add")) goto escape;
+      if (!strcmp (temp, "-del")) goto escape;
+  }
+
+  if (temp != NULL) free (temp);
+  if (comm != NULL) free (comm);
+  return (TRUE);
+
+escape: 
+  if (comm != NULL) free (comm);
+  if (temp != NULL) free (temp);
+  return (FALSE);
+}
+
+int is_loop (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "while");
+  free (comm);
+  return (status);
+}
+
+int is_task (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "task");
+  free (comm);
+  return (status);
+}
+
+int is_task_exit (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "task.exit");
+  free (comm);
+  return (status);
+}
+
+int is_task_exec (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "task.exec");
+  free (comm);
+  return (status);
+}
+
+int is_list (char *line) {
+  
+  int status;
+
+  status = is_if_block (line);
+  status |= is_macro_create (line);
+  status |= is_for_loop (line);
+  status |= is_list_data (line);
+  status |= is_loop (line);
+  status |= is_task (line);
+  status |= is_task_exit (line);
+  status |= is_task_exec (line);
+
+  return (status);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/MacroOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/MacroOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/MacroOps.c	(revision 22322)
@@ -0,0 +1,158 @@
+# include "opihi.h"
+
+// the macro table is only modified by macro_create: if thread protection is needed, it
+// should be added there.  it may not be needed if programs only call macro_create in a
+// single thread.  pantasks_server only calls macro_create in the 'input' thread, so it is
+// safe.
+
+static char dot[] = ".";
+
+static Macro *macros;
+static int   Nmacros;
+static int   NMACROS;
+
+static char *MacroName = dot;
+static int   MacroDepth = 0;
+
+void InitMacros () {
+  NMACROS = 20;
+  Nmacros = 0;
+  ALLOCATE (macros, Macro, NMACROS);
+  MacroName = dot;
+  MacroDepth = 0;
+}
+
+void FreeMacros () {
+
+  int i;
+
+  for (i = 0; i < Nmacros; i++) {
+    FreeMacro (&macros[i]);
+  }
+  free (macros);
+}
+
+void SetCurrentMacroData (char *name, int depth) {
+  MacroName = name;
+  MacroDepth = depth;
+}
+
+char *GetMacroName () {
+  return (MacroName);
+}
+
+int GetMacroDepth () {
+  return (MacroDepth);
+}
+
+Macro *NewMacro (char *name) {
+  
+  macros[Nmacros].name = strcreate (name);;
+  macros[Nmacros].Nlines = 0;
+  ALLOCATE (macros[Nmacros].line, char *, 1);
+  Nmacros ++;
+  if (Nmacros == NMACROS) {
+    NMACROS += 20;
+    REALLOCATE (macros, Macro, NMACROS);
+  }
+  return (&macros[Nmacros-1]);
+}
+
+void ListMacro (Macro *macro) {
+
+  int i;
+
+  if ((macro == NULL) || (macro[0].Nlines == 0)) {
+    gprint (GP_ERR, "  macro not defined\n");
+    return;
+  }
+  for (i = 0; i < macro[0].Nlines; i++) {
+    gprint (GP_ERR, "  %s\n", macro[0].line[i]);
+  }
+  return;
+}
+
+void ListMacros () {
+  int i;
+  for (i = 0; i < Nmacros; i++) {
+    gprint (GP_ERR, "%s\n", macros[i].name);
+  }
+}
+
+void FreeMacro (Macro *macro) {
+  
+  int i;
+
+  if (macro == NULL) return;
+
+  for (i = 0; i < macro[0].Nlines; i++) {
+    free (macro[0].line[i]);
+  }
+  free (macro[0].line);
+  free (macro[0].name);
+  return;
+}
+
+int DeleteMacro (Macro *macro) {
+
+  int i, Nm;
+
+  Nm = -1;
+  for (i = 0; i < Nmacros; i++) {
+    if (macro == &macros[i]) {
+      Nm = i;
+      break;
+    }
+  }
+  if (Nm == -1) {
+    gprint (GP_ERR, "programming error: macro not found\n");
+    return (FALSE);
+  }
+
+  FreeMacro (&macros[Nm]);
+  for (i = Nm + 1; i < Nmacros; i++)
+    macros[i - 1] = macros[i];
+  Nmacros --;
+  return (TRUE);
+}
+
+/* return macro which unambiguously matches name */
+Macro *MatchMacro (char *name, int VERBOSE, int EXACT) {
+
+  int i, match[10], Nmatch;
+
+  /* try for an exact match first */
+  for (i = 0; i < Nmacros; i++) {
+    if (!strcmp (macros[i].name, name)) {
+      return (&macros[i]);
+    }
+  }
+  if (EXACT) {
+    if (VERBOSE) gprint (GP_ERR, "no exact match to %s\n", name);
+    return (NULL);
+  }
+      
+  /* not found as complete macro, try partial */
+  Nmatch = 0;
+  for (i = 0; (Nmatch < 10) && (i < Nmacros); i++) {
+    if (!strncmp (macros[i].name, name, strlen(name))) {  /* found a macro */
+      match[Nmatch] = i;
+      Nmatch ++;
+    }
+  }
+  if (Nmatch == 1) return (&macros[match[0]]);
+
+  if (Nmatch > 1) {
+    if (VERBOSE) {
+      gprint (GP_ERR, "ambiguous macro: %s ( ", name);
+      for (i = 0; i < Nmatch; i++) {
+	gprint (GP_ERR, "%s ", macros[match[i]].name);
+      }
+      gprint (GP_ERR, ")\n");
+    }
+    return (NULL);
+  }
+  if (VERBOSE) gprint (GP_ERR, "%s: Macro not found.\n", name);
+  return (NULL);
+}  
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/Makefile	(revision 22322)
@@ -0,0 +1,78 @@
+default: libshell
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SDIR    =       $(HOME)/lib.shell
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# opihi shell functions (libopihi) ########################
+shell = \
+$(SDIR)/BufferOps.$(ARCH).o             \
+$(SDIR)/CommandOps.$(ARCH).o		\
+$(SDIR)/ConfigInit.$(ARCH).o		\
+$(SDIR)/ListOps.$(ARCH).o	   	\
+$(SDIR)/MacroOps.$(ARCH).o		\
+$(SDIR)/SocketOps.$(ARCH).o             \
+$(SDIR)/VariableOps.$(ARCH).o	   	\
+$(SDIR)/VectorOps.$(ARCH).o             \
+$(SDIR)/check_stack.$(ARCH).o           \
+$(SDIR)/command.$(ARCH).o               \
+$(SDIR)/convert_to_RPN.$(ARCH).o	\
+$(SDIR)/dvomath.$(ARCH).o               \
+$(SDIR)/errors.$(ARCH).o		\
+$(SDIR)/evaluate_stack.$(ARCH).o  	\
+$(SDIR)/exec_loop.$(ARCH).o             \
+$(SDIR)/expand_vars.$(ARCH).o           \
+$(SDIR)/expand_vectors.$(ARCH).o        \
+$(SDIR)/gprint.$(ARCH).o		\
+$(SDIR)/interrupt.$(ARCH).o	        \
+$(SDIR)/isolate_elements.$(ARCH).o 	\
+$(SDIR)/macro_create.$(ARCH).o          \
+$(SDIR)/macro_delete.$(ARCH).o          \
+$(SDIR)/macro_edit.$(ARCH).o            \
+$(SDIR)/macro_exec.$(ARCH).o            \
+$(SDIR)/macro_funcs.$(ARCH).o           \
+$(SDIR)/macro_list.$(ARCH).o            \
+$(SDIR)/macro_read.$(ARCH).o            \
+$(SDIR)/macro_write.$(ARCH).o		\
+$(SDIR)/memstr.$(ARCH).o                \
+$(SDIR)/multicommand.$(ARCH).o          \
+$(SDIR)/parse.$(ARCH).o                 \
+$(SDIR)/parse_commands.$(ARCH).o	\
+$(SDIR)/stack_math.$(ARCH).o		\
+$(SDIR)/startup.$(ARCH).o		\
+$(SDIR)/string.$(ARCH).o                \
+$(SDIR)/timeformat.$(ARCH).o            \
+$(SDIR)/version.$(ARCH).o	   	\
+$(SDIR)/opihi.$(ARCH).o
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/convert.h \
+$(INC)/display.h 
+
+$(shell) : $(incs)
+
+$(LIB)/libshell.$(ARCH).a: $(shell)
+$(LIB)/libshell.$(ARCH).$(DLLTYPE): $(shell)
+
+$(DESTLIB)/libshell.a: $(LIB)/libshell.$(ARCH).a
+$(DESTLIB)/libshell.$(DLLTYPE): $(LIB)/libshell.$(ARCH).$(DLLTYPE)
+
+libshell: $(DESTLIB)/libshell.a $(DESTLIB)/libshell.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libshell.a
+	rm -f $(DESTLIB)/libshell.$(DLLTYPE)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/SocketOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/SocketOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/SocketOps.c	(revision 22322)
@@ -0,0 +1,287 @@
+# include "shell.h"
+
+# define MY_PORT 2000
+# define MY_WAIT 500
+# define DEBUG 0
+
+static int NVALID;
+static int Nvalid;
+static int *VALID;
+
+int InitServerSocket (SockAddress *Address) {
+
+  int status, InitSocket, length;
+
+# if (0)
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  bzero (hostip, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+# endif
+  
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = MY_PORT;
+  Address[0].sin_addr.s_addr = INADDR_ANY; // use this line to bind any address / port?
+
+retry_server:
+
+# if (0)  
+  status = inet_aton (hostip, &Address[0].sin_addr);
+  if (!status) {
+    gprint (GP_ERR, "invalid address\n");
+    exit (4);
+  }
+# endif
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (5);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "init sock: %d, len: %d\n", InitSocket, length);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+
+# if 0
+    fprintf (stderr, "errno: %d\n", errno);
+    fprintf (stderr, "EACCES: %d\n", EACCES);
+    fprintf (stderr, "EBADF: %d\n", EBADF);
+    fprintf (stderr, "EINVAL: %d\n", EINVAL);
+    fprintf (stderr, "ENOTSOCK: %d\n", ENOTSOCK);
+    fprintf (stderr, "EFAULT: %d\n", EFAULT);
+    fprintf (stderr, "ELOOP: %d\n", ELOOP);
+    fprintf (stderr, "ENAMETOOLONG: %d\n", ENAMETOOLONG);
+    fprintf (stderr, "ENOENT: %d\n", ENOENT);
+    fprintf (stderr, "ENOMEM: %d\n", ENOMEM);
+    fprintf (stderr, "ENOTDIR: %d\n", ENOTDIR);
+    fprintf (stderr, "EROFS: %d\n", EROFS);
+    fprintf (stderr, "EADDRNOTAVAIL: %d\n", EADDRNOTAVAIL);
+    fprintf (stderr, "EADDRINUSE: %d\n", EADDRINUSE);
+    fprintf (stderr, "ENOSR: %d\n", ENOSR);
+# endif
+
+    if (errno == EADDRINUSE) {
+	Address[0].sin_port ++;
+	if (Address[0].sin_port > MY_PORT + 10) {
+	  fprintf (stderr, "failed to find a usable port\n");
+	  exit (6);
+	}
+	goto retry_server;
+    }
+    perror ("bind: ");
+    exit (7);
+  }
+  /* repeated starts of the server are limited by xinetd or something:
+     requires 60sec timeout of the selected socket */
+
+  fprintf (stderr, "bound to port: %d\n", Address[0].sin_port);
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (8);
+  }
+  return (InitSocket);
+}
+
+int WaitServerSocket (int InitSocket, SockAddress *Address) {
+
+  int i, BindSocket;
+  SockAddress Address_in;
+  socklen_t length;
+  u_int32_t addr;
+
+  Address_in = Address[0];
+
+  length = sizeof(Address_in);
+
+  /* this is a blocking wait; use in a separate thread */
+  fcntl (InitSocket, F_SETFL, !O_NONBLOCK); 
+
+  if (DEBUG) gprint (GP_ERR, "init sock: %d, len: %d\n", InitSocket, length);
+  BindSocket = accept (InitSocket, (struct sockaddr *) &Address_in, &length);
+  if (DEBUG) gprint (GP_ERR, "bind sock: %d\n", BindSocket);
+  if (BindSocket == -1) {
+    perror ("accept: ");
+    exit (9);
+  }
+
+  addr = Address_in.sin_addr.s_addr;
+  if (DEBUG) {
+    gprint (GP_ERR, "incoming connection from: ");
+    gprint (GP_ERR, " %u", (0xff & (addr >>  0)));
+    gprint (GP_ERR, ".%u", (0xff & (addr >>  8)));
+    gprint (GP_ERR, ".%u", (0xff & (addr >> 16)));
+    gprint (GP_ERR, ".%u", (0xff & (addr >> 24)));
+    gprint (GP_ERR, "\n");
+  }
+
+  if (Nvalid == 0) goto accepted;
+
+  for (i = 0; i < Nvalid; i++) {
+    /* valid IP addresses may be machines (120.90.121.142) or 
+       class C networks (120.90.121.0) */
+       
+    /* for machine, address must match */
+    if ((0xff & (VALID[i] >> 24)) != 0) {
+      if (addr == VALID[i]) goto accepted;
+    }
+
+    /* for network, lower three bytes of address must match */
+    if ((0xff & (VALID[i] >> 24)) == 0) {
+      if ((0x00ffffff & addr) == VALID[i]) goto accepted;
+    }
+  }
+
+  if (DEBUG) gprint (GP_ERR, "connection rejected\n");
+  close (BindSocket);
+  return (-1);
+
+accepted:
+  if (DEBUG) gprint (GP_ERR, "connection accepted\n");
+  fcntl (BindSocket, F_SETFL, O_NONBLOCK); 
+  return (BindSocket);
+}
+
+int GetClientSocket (char *hostname) {
+
+  int i, status, InitSocket, length;
+  SockAddress Address;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  bzero (hostip, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+
+  if (DEBUG) {
+    gprint (GP_ERR, "trying %s (%s:%d)...", host[0].h_name, hostip, MY_PORT);
+  }
+
+  Address.sin_family = AF_INET;
+  Address.sin_port   = MY_PORT;
+
+retry_client:
+  status = inet_aton (hostip, &Address.sin_addr);
+  if (!status) {
+    gprint (GP_ERR, "invalid address\n");
+    exit (10);
+  }
+
+  length = sizeof(Address);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (11);
+  }
+
+  status = connect (InitSocket, (struct sockaddr *) &Address, length);
+  if (status == -1) {
+    if (errno == ECONNREFUSED) {
+      Address.sin_port ++;
+      if (Address.sin_port > MY_PORT + 10) exit (12);
+      goto retry_client;
+    }
+    perror ("connect: ");
+    exit (13);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "connected on port: %d\n", Address.sin_port);
+  if (DEBUG) gprint (GP_ERR, "connected\n");
+  fcntl (InitSocket, F_SETFL, O_NONBLOCK); 
+  return (InitSocket);
+}
+
+int InitServerSocket_Named (char *hostname, SockAddress *Address) {
+
+  int i, status, InitSocket, length;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  bzero (hostip, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+  
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = MY_PORT;
+  status = inet_aton (hostip, &Address[0].sin_addr);
+  if (!status) {
+    gprint (GP_ERR, "invalid address\n");
+    exit (14);
+  }
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (15);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "init sock: %d, len: %d\n", InitSocket, length);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+    perror ("bind: ");
+    exit (16);
+  }
+
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (17);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "socket listening on %s (%s:%d)\n", host[0].h_name, hostip, MY_PORT);
+  return (InitSocket);
+}
+
+/* load valid ip list */
+int DefineValidIP () {
+
+  int i, Nvalid, ip1, ip2, ip3, ip4, test, status;
+  char string[80];
+
+  Nvalid = 0;
+  NVALID = 10;
+  ALLOCATE (VALID, int, NVALID);
+  for (i = 0; VarConfigEntry ("VALID_IP", "%s", i, string) != NULL; i++) {
+    status = sscanf (string, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4);
+    test = TRUE;
+    test &= (status == 4);
+    test &= ((ip1 > 0) && (ip1 < 256)); 
+    test &= ((ip2 > 0) && (ip2 < 256)); 
+    test &= ((ip3 > 0) && (ip3 < 256)); 
+    test &= ((ip4 >=0) && (ip4 < 256)); 
+    if (!test) {
+      gprint (GP_ERR, "invalid IP address %s\n", string);
+      exit (18);
+    }
+    VALID[Nvalid] = ip1 | (ip2 << 8) | (ip3 << 16) | (ip4 << 24);
+    Nvalid ++;
+    CHECK_REALLOCATE (VALID, int, NVALID, Nvalid, 10);
+  }
+  NVALID = Nvalid;
+  REALLOCATE (VALID, int, NVALID);
+  if (NVALID == 0) {
+    free (VALID);
+    VALID = NULL;
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/VariableOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/VariableOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/VariableOps.c	(revision 22322)
@@ -0,0 +1,318 @@
+# include "opihi.h"
+
+Variable *variables;   /* variable to store the list of all variables */
+int      Nvariables;   /* number of currently available variables */
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void InitVariables () {
+  Nvariables = 0;
+  ALLOCATE (variables, Variable, 1); 
+}
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void FreeVariables () {
+
+  int i; 
+
+  for (i = 0; i < Nvariables; i++) {
+    free (variables[i].name);
+    free (variables[i].value);
+  }
+  free (variables);
+}
+
+int set_local_variable (char *name, char *value) {
+
+  int i;
+  char *local, *MacroName;
+
+  /* a local variable has the form (macroname).(varname) */
+  MacroName = GetMacroName ();
+
+  /* need to use a mutex to prevent two threads from changing variable list simultaneously */
+  pthread_mutex_lock (&mutex);
+
+  /* look for existing local variable */
+  ALLOCATE (local, char, strlen(name) + strlen(MacroName) + 2);
+  sprintf (local, "%s.%s", MacroName, name);
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (local, variables[i].name)) {
+      free (variables[i].value);
+      free (local);
+      variables[i].value = strcreate (value);
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  /* NEW variable */
+  Nvariables ++;
+  REALLOCATE (variables, Variable, Nvariables);
+  variables[Nvariables - 1].name = local;
+  variables[Nvariables - 1].value = strcreate (value);
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+int set_str_variable (char *name, char *value) {
+
+  int i;
+  char *local, *MacroName;
+
+  MacroName = GetMacroName ();
+
+  /* need to use a mutex to prevent two threads from changing variable list simultaneously */
+  pthread_mutex_lock (&mutex);
+
+  /* look for local variable first */
+  ALLOCATE (local, char, strlen(name) + strlen(MacroName) + 2);
+  sprintf (local, "%s.%s", MacroName, name);
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (local, variables[i].name)) {
+      free (variables[i].value);
+      free (local);
+      variables[i].value = strcreate (value);
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  free (local);
+
+  /* look for global variable */
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (name, variables[i].name)) {
+      free (variables[i].value);
+      variables[i].value = strcreate (value);
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  /* NEW variable */
+  Nvariables ++;
+  REALLOCATE (variables, Variable, Nvariables);
+  variables[Nvariables - 1].name = strcreate (name);
+  variables[Nvariables - 1].value = strcreate (value);
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+int set_variable (char *name, double dvalue) {
+
+  char value[1024];
+
+  sprintf (value, "%.12g", dvalue);
+  set_str_variable (name, value);
+  return (TRUE);
+}
+
+int set_int_variable (char *name, int ivalue) {
+
+  char value[1024];
+
+  sprintf (value, "%d", ivalue);
+  set_str_variable (name, value);
+  return (TRUE);
+}
+
+static char variable_true[] = "1";
+static char variable_false[] = "0";
+
+char *get_local_variable_ptr (char *name) {
+  
+  int i;
+  char *local, *MacroName;
+
+  MacroName = GetMacroName ();
+
+  /* look for local variable first */
+  ALLOCATE (local, char, strlen(name) + strlen(MacroName) + 2);
+  sprintf (local, "%s.%s", MacroName, name);
+
+  /* need to use a mutex to prevent another thread from misleading on size of Nvariables */
+  pthread_mutex_lock (&mutex);
+
+  for (i = 0; i < Nvariables; i++) { /* find the variable mentioned */
+    if (!strcmp(local, variables[i].name)) {
+      pthread_mutex_unlock (&mutex);
+      free (local);
+      return (variables[i].value);
+    }
+  }
+  pthread_mutex_unlock (&mutex);
+  free (local);
+  return (NULL);
+}
+
+char *get_variable_ptr (char *name) {
+  
+  int i;
+  char *local, *value;
+
+  if (name == NULL) return (NULL);
+  if (*name == 0) return (NULL);
+
+  /* check for the existence of the given name (after ?) */
+  /* return a string which should not be freed */
+  if (*name == '?') {
+    value = get_variable_ptr (&name[1]);
+    if (value == NULL) {
+      return variable_false;
+    }
+    return variable_true;
+  }
+
+  // check first for a local variable
+  local = get_local_variable_ptr (name);
+  if (local != NULL) return (local);
+
+  /* need to use a mutex to prevent another thread from misleading on size of Nvariables */
+  pthread_mutex_lock (&mutex);
+
+  /* look for global variable */
+  for (i = 0; i < Nvariables; i++) { /* find the variable mentioned */
+    if (!strcmp(name, variables[i].name)) {
+      pthread_mutex_unlock (&mutex);
+      return (variables[i].value);
+    }
+  }
+  pthread_mutex_unlock (&mutex);
+  return (NULL);
+}
+
+char *get_variable (char *name) {
+  
+  char *ptr, *value;
+
+  if (name == NULL) return (NULL);
+  if (*name == 0) return (NULL);
+
+  ptr = get_variable_ptr (name);
+  if (ptr) {
+    value = strcreate (ptr);
+    return value;
+  }
+  return (NULL);
+}
+
+/* return TRUE / FALSE if name is an existant variable */
+int get_variable_exists (char *name) {
+  
+  char *ptr;
+
+  ptr = get_variable_ptr (name);
+  if (ptr) return (TRUE);
+  return (FALSE);
+}
+
+float get_variable_default (char *name, float dvalue) {
+
+  char *value;
+  float fvalue;
+
+  value = get_variable (name);
+  if (value == NULL) {
+    return (dvalue);
+  }
+  fvalue = atof (value);
+  return (fvalue);
+}
+
+double get_double_variable (char *name, int *found) {
+  
+  char *ptr;
+
+  ptr = get_variable_ptr (name);
+  if (ptr) {
+    *found = TRUE;
+    return (atof (ptr));
+  }
+
+  *found = FALSE;
+  return (0.0);
+}
+
+int get_int_variable (char *name, int *found) {
+  
+  char *ptr;
+
+  ptr = get_variable_ptr (name);
+  if (ptr) {
+    *found = TRUE;
+    return (atof (ptr));
+  }
+  *found = FALSE;
+  return (0.0);
+}
+
+int DeleteNamedScalar (char *name) {
+
+  int i, j;
+
+  /* need to use a mutex to prevent two threads from simultaneously modifying Nvariables */
+  pthread_mutex_lock (&mutex);
+
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (name, variables[i].name)) {
+      free (variables[i].name);
+      free (variables[i].value);
+      for (j = i; j < Nvariables - 1; j++) {
+	variables[j] = variables[j + 1];
+      }
+      Nvariables --;
+      REALLOCATE (variables, Variable, MAX (Nvariables, 1));
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  pthread_mutex_unlock (&mutex);
+  return (FALSE);
+}
+
+int IsScalar (char *name) { 
+
+  int i;
+
+  /* need to use a mutex to prevent another thread from misleading on size of Nvariables */
+  pthread_mutex_lock (&mutex);
+
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (name, variables[i].name)) {
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  pthread_mutex_unlock (&mutex);
+  return (FALSE);
+}
+
+void ListVariables () {
+
+  int i;
+
+  if (Nvariables == 0) {
+    gprint (GP_ERR, "No defined variables\n");
+    return;
+  }
+
+  /* need to use a mutex to prevent another thread from misleading on size of Nvariables */
+  pthread_mutex_lock (&mutex);
+
+  for (i = 0; i < Nvariables; i++) {
+    gprint (GP_ERR, "%s = %s\n", variables[i].name, variables[i].value);
+  }
+
+  pthread_mutex_unlock (&mutex);
+  return;
+}
+
+int SelectScalar (char *string, double *value) {
+
+  char *end;
+
+  /* if string is a number, return TRUE */
+  *value = strtod (string, &end);
+  if (end == string + strlen(string)) return (TRUE);
+
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/VectorOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/VectorOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/VectorOps.c	(revision 22322)
@@ -0,0 +1,191 @@
+# include "opihi.h"
+
+static Vector **vectors;
+static int     Nvectors;
+  
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void InitVectors () {
+  Nvectors = 0;
+  ALLOCATE (vectors, Vector *, 1);
+}
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void FreeVectors () {
+
+  int i;
+
+  for (i = 0; i < Nvectors; i++) {
+    free (vectors[i][0].elements);
+    free (vectors[i]);
+  }
+  free (vectors);
+}
+
+Vector *InitVector () {
+  Vector *vec;
+
+  ALLOCATE (vec, Vector, 1);
+  ALLOCATE (vec[0].elements, float, 1);
+  bzero (vec[0].name, 1024);
+  vec[0].Nelements = 0;
+  return (vec);
+}
+
+int IsVector (char *name) {
+ 
+  int i;
+
+  if (name == NULL) return (FALSE);
+
+  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
+  if (i == Nvectors) return (FALSE);
+  return (TRUE);
+}
+
+int IsVectorPtr (Vector *vec) {
+ 
+  int i;
+
+  if (vec == NULL) return (FALSE);
+
+  for (i = 0; (i < Nvectors) && (vectors[i] != vec); i++);
+  if (i == Nvectors) return (FALSE);
+  return (TRUE);
+}
+
+Vector *SelectVector (char *name, int mode, int verbose) {
+
+  int i;
+
+  if (name == NULL) goto error;
+  if (ISNUM(name[0])) goto error;
+  if (IsBuffer(name)) goto error;
+
+  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
+  /* is a new vector */
+  if (i == Nvectors) { 
+    if (mode == OLDVECTOR) goto error;
+    pthread_mutex_lock (&mutex);
+    Nvectors ++;
+    REALLOCATE (vectors, Vector *, Nvectors);
+    vectors[i] = InitVector ();
+    strcpy (vectors[i][0].name, name);
+    pthread_mutex_unlock (&mutex);
+    return (vectors[i]);
+  } 
+  /* is an old vector */
+  if (mode == NEWVECTOR) goto error;
+  return (vectors[i]);
+
+ error:
+  if (verbose) gprint (GP_ERR, "invalid vector %s\n", name);
+  return (NULL);
+}
+  
+/* delete by pointer */
+int DeleteVector (Vector *vec) {
+
+  int i, j;
+
+  if (vec == NULL) return (FALSE);
+
+  for (i = 0; (i < Nvectors) && (vec != vectors[i]); i++);
+  if (i == Nvectors) return (FALSE);
+
+  free (vectors[i][0].elements);
+  free (vectors[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
+  Nvectors --;
+  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+  
+/* delete by name */
+int DeleteNamedVector (char *name) {
+
+  int i, j;
+
+  if (name == NULL) return (FALSE);
+  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
+  if (i == Nvectors) return (FALSE);
+
+  free (vectors[i][0].elements);
+  free (vectors[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
+  Nvectors --;
+  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+/* copy data from in to out - new memory space */
+int CopyNamedVector (char *out, char *in) {
+  Vector *In, *Out;
+  if ((In  = SelectVector (in,  OLDVECTOR, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectVector (out, ANYVECTOR, FALSE)) == NULL) return (FALSE);
+  CopyVector (Out, In);
+  return (TRUE);
+}
+int CopyVector (Vector *out, Vector *in) {
+  free (out[0].elements);
+  out[0].Nelements = in[0].Nelements;
+  ALLOCATE (out[0].elements, float, out[0].Nelements);
+  memcpy (out[0].elements, in[0].elements, out[0].Nelements*sizeof(float));
+  return (TRUE);
+}
+
+/* move data from in to out - use old memory space */
+int MoveNamedVector (char *out, char *in) {
+  Vector *In, *Out;
+  if ((In  = SelectVector (in,  OLDVECTOR, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectVector (out, ANYVECTOR, FALSE)) == NULL) return (FALSE);
+  MoveVector (Out, In);
+  return (TRUE);
+}
+int MoveVector (Vector *out, Vector *in) {
+  int i, j;
+
+  free (out[0].elements);
+  out[0].Nelements = in[0].Nelements;
+  out[0].elements =  in[0].elements;
+
+  /* delete vector entry from vector list, if it exists */
+  for (i = 0; (i < Nvectors) && (in != vectors[i]); i++);
+  if (i == Nvectors) {
+    free (in);
+    return (TRUE);
+  }
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
+  Nvectors --;
+  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
+  free (in);
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+int ListVectors () {
+
+  int i;
+
+  if (Nvectors == 0) {
+    gprint (GP_ERR, "No defined vectors\n");
+    return (FALSE);
+  }
+
+  gprint (GP_LOG, "    N       name      size\n");
+  for (i = 0; i < Nvectors; i++) {
+    gprint (GP_LOG, "%5d %10s %10d\n",
+	     i, vectors[i][0].name, vectors[i][0].Nelements);
+  }
+  return (TRUE);
+}
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/check_stack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/check_stack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/check_stack.c	(revision 22322)
@@ -0,0 +1,84 @@
+# include "opihi.h"
+
+int check_stack (StackVar *stack, int Nstack, int validsize) {
+
+  int i, Nx, Ny, Nv, size;
+  char *c;
+
+  Nv = Nx = Ny = -1;
+
+  for (i = 0; i < Nstack; i++) {
+    if (stack[i].type == 'X') {
+
+      /** if this is a number, put it on the list of scalers and move on **/
+      stack[i].Float = strtod (stack[i].name, &c);
+      if (c == stack[i].name + strlen (stack[i].name)) {
+	stack[i].ptr   = &(stack[i].Float);
+	stack[i].type  = 'S';
+	continue;
+      } 
+
+      /** if this is a matrix, find the dimensions and check with existing values **/
+      if (IsBuffer (stack[i].name)) {
+	stack[i].buffer = SelectBuffer (stack[i].name, OLDBUFFER, TRUE);
+	stack[i].ptr    = (float *) stack[i].buffer[0].matrix.buffer;
+	stack[i].type   = 'M';
+	if (Nx == -1) {
+	  Nx = stack[i].buffer[0].matrix.Naxis[0];
+	  Ny = stack[i].buffer[0].matrix.Naxis[1];
+	} 
+	if ((Nx != stack[i].buffer[0].matrix.Naxis[0]) && (Ny != stack[i].buffer[0].matrix.Naxis[1])) {
+	  push_error ("dimensions don't match");
+	  return (-1);
+	}	
+	if (Nv != -1) {
+	  if ((Nv != Nx) && (Nv != Ny)) {
+	    push_error ("dimensions don't match");
+	    return (-1);
+	  }
+	}	
+	continue;
+      }
+
+      /** if this is a vector, find the dimensions and check with existing values **/
+      if (IsVector (stack[i].name)) {
+	stack[i].vector = SelectVector (stack[i].name, OLDVECTOR, FALSE);
+	stack[i].ptr    = (float *) stack[i].vector[0].elements;
+	stack[i].type   = 'V';
+
+	if (Nv == -1) Nv = stack[i].vector[0].Nelements;
+	if (Nv != stack[i].vector[0].Nelements) {
+	  push_error ("dimensions don't match");
+	  return (-1);
+	}
+	if (Nx != -1) {
+	  if ((Nx != Nv) && (Ny != Nv)) {
+	    push_error ("dimensions don't match");
+	    return (-1);
+	  }
+	}	
+	continue;
+      }
+
+      /* this is not a scalar, vector, or matrix.  must be string */
+      stack[i].type  = 'W';
+    }
+  }
+
+  /* return object dimensions */
+  size = 0;
+  if (Nv != -1) size = 1;
+  if (Nx != -1) size = 2;
+  if (validsize == -1)   return (size);
+  if (validsize != size) return (-1);
+  return (size);
+}
+
+/* check stack identifies the data elements as scalar, vector, matrix, or word.
+   operators have already been identified.  
+   check stack returns the total stack dimensionality (0,1,2)
+   on error, check stack returns -1
+   possible errors:
+     - mismatch with requested dimensionality
+     - mismatch in data dimensions
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/command.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/command.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/command.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "opihi.h"
+# define DEBUG 0
+
+int command (char *line, char **outline, int VERBOSE) {
+
+  int i, status, argc;
+  char **argv, **targv;
+  Command *cmd;
+
+  /* force a space between ! and first word: !ls becomes ! ls */
+  if (line[0] == '!') {
+    REALLOCATE (line, char, strlen(line) + 5);
+    memmove (&line[2], &line[1], strlen(&line[1]) + 1);
+    line[1] = ' ';
+  }
+
+  /* expand anything of the form $fred or $fred$sam, etc */ 
+  line = expand_vars (line);     /* line is freed here, new one allocated */
+  /* expand anything of the form fred[N] */ 
+  line = expand_vectors (line);  /* line is freed here, new one allocated */
+  /* solve math expresions, assign variable, if needed */
+  line = parse (line);        /* line is freed here, new one allocated */
+  /* any entry in line of the form {foo} returns value or tmp vector / buffer */
+
+  /* we may have reallocated line, return new pointer */
+  *outline = line;
+  
+  # if (DEBUG) 
+  fprintf (stderr, "line: %s\n", line);
+  # endif
+
+  argv = parse_commands (line, &argc);
+  if (argc == 0) return (TRUE);  /* empty command or assignment */
+
+  /* save the original values of argv since command may modify the array */
+  ALLOCATE (targv, char *, argc);
+  for (i = 0; i < argc; i++) targv[i] = argv[i];
+
+  cmd = MatchCommand (argv[0], VERBOSE, FALSE);
+  if (cmd == NULL) {
+    status = -1;
+  } else {
+    free (argv[0]);
+    argv[0] = strcreate (cmd[0].name);
+    targv[0] = argv[0];
+    status = (*cmd[0].func) (argc, argv);
+  }
+  for (i = 0; i < argc; i++) free (targv[i]);
+  free (targv);
+  free (argv);
+
+  if (!status) {
+    char *msg;
+    msg = get_variable_ptr ("ERRORMSG");
+    if (msg != (char *) NULL) gprint (GP_ERR, "%s\n", msg);
+  }
+
+  set_int_variable ("STATUS", status);
+
+  # if (DEBUG) 
+  gprint (GP_ERR, "command: %s, status: %d\n", line, status);
+  # endif
+
+  return (status);
+}
+
+/* parse the input line, search for the corresponding command, and execute it
+   if no match is found, return -1; this is used above to distinguish between
+   a command error and an unknown command.  if VERBOSE is true, unknown commands
+   result in an error message.  The input line is freed and the resulting parsed
+   line is returned on 'outline'
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/convert_to_RPN.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/convert_to_RPN.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/convert_to_RPN.c	(revision 22322)
@@ -0,0 +1,212 @@
+# include "opihi.h"
+# define DUMPSTACK 0
+
+StackVar *convert_to_RPN (int argc, char **argv, int *nstack) {
+  
+  int type, Nx, Ny;
+  int i, j, Nstack, Nop_stack, NSTACK;
+  StackVar *stack, *op_stack;
+
+  /* max total stack size is argc, though should be less, this is safe */
+  NSTACK = argc + 5;
+  ALLOCATE (stack, StackVar, NSTACK);
+  ALLOCATE (op_stack, StackVar, NSTACK);
+  for (i = 0; i < NSTACK; i++) {
+    init_stack (&stack[i]);
+    init_stack (&op_stack[i]);
+  }
+  
+  Nx = Ny = Nstack = Nop_stack = 0;
+  for (i = 0; i < argc; i++) {
+    
+    /* decide on priority of object */
+    type = 0;
+    /* unary operations */
+    if (!strcmp (argv[i], "abs"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "int"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "exp"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ten"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "log"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ln"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "sqrt"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "erf"))    { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sinh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cosh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asinh"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acosh"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sin"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cos"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "tan"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dsin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dcos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dtan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "atan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dasin"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dacos"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "datan"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "lgamma")) { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "rnd"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "xramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "yramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ramp"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "zero"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "--"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "not"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isinf"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isnan"))  { type = 9; goto gotit; }
+
+    /* binary operations */
+    if (!strcmp (argv[i], "^"))      { type = 8; goto gotit; }
+
+    if (!strcmp (argv[i], "@"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "/"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "*"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "%"))      { type = 7; goto gotit; }
+
+    if (!strcmp (argv[i], "+"))      { type = 6; goto gotit; }
+    if (!strcmp (argv[i], "-"))      { type = 6; goto gotit; }
+	
+    if (!strcmp (argv[i], "&"))      { type = 5; goto gotit; }
+    if (!strcmp (argv[i], "|"))      { type = 5; goto gotit; }
+
+    if (!strcmp (argv[i], "<"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], ">"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], "=="))     { type = 4; strcpy (argv[i], "E"); goto gotit; }
+    if (!strcmp (argv[i], "!="))     { type = 4; strcpy (argv[i], "N"); goto gotit; }
+    if (!strcmp (argv[i], "<="))     { type = 4; strcpy (argv[i], "L"); goto gotit; }
+    if (!strcmp (argv[i], ">="))     { type = 4; strcpy (argv[i], "G"); goto gotit; }
+    if (!strcmp (argv[i], ">>"))     { type = 4; strcpy (argv[i], "U"); goto gotit; }
+    if (!strcmp (argv[i], "<<"))     { type = 4; strcpy (argv[i], "D"); goto gotit; }
+
+    if (!strcmp (argv[i], "&&"))     { type = 3; strcpy (argv[i], "A"); goto gotit; }
+    if (!strcmp (argv[i], "||"))     { type = 3; strcpy (argv[i], "O"); goto gotit; }
+
+    if (!strcmp (argv[i], "("))      { type = 2; goto gotit; }
+    if (!strcmp (argv[i], ")"))      { type = 1; goto gotit; }
+
+  gotit:
+    /* choose how to deal with object */
+    switch (type) {
+      case 8:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
+	/* pop previous, higher operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type > type); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	assign_stack (&op_stack[Nop_stack], argv[i], type);
+	Nop_stack ++;
+	break;
+      case 9: /* unary OPs */
+      case 7: /* binary OPs */
+      case 6:
+      case 5: 
+      case 4: 
+      case 3: 
+	/* pop previous, higher or equal operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type >= type); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	assign_stack (&op_stack[Nop_stack], argv[i], type);
+	Nop_stack ++;
+	break;
+      case 2:  
+	/* push operator on OP stack */
+	assign_stack (&op_stack[Nop_stack], argv[i], type);
+	// fprintf (stderr, "push %s\n", op_stack[Nop_stack].name);
+	Nop_stack ++;
+	break;
+      case 1: 
+	/* pop rest of operators from OP stack to stack, looking for '(' */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != 2); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	if ((j == -1) || (op_stack[j].type != 2)) {
+	  push_error ("syntax error: mismatched parenthesis");
+	  Nstack = 0;
+	  goto cleanup;
+	}
+	// delete the '(' from op_stack:
+	// fprintf (stderr, "pop %s\n", op_stack[j].name);
+	clear_stack (&op_stack[j]);
+	Nop_stack --;
+	break;
+      case 0:
+	/* place the value (number or vector/matrix name) on stack */
+	/* value of 'X' is used as sentinel until we sort out values */
+	assign_stack (&stack[Nstack], argv[i], 'X');
+	Nstack ++;
+	break;
+    }
+  }
+
+  /* dump remaining operators on stack, checking for ')' */
+  for (j = Nop_stack - 1; j >= 0; j--) {
+    if (op_stack[j].type == 2) {
+      push_error ("syntax error: mismatched parenthesis");
+      Nstack = 0;
+      goto cleanup;
+    }
+    move_stack (&stack[Nstack],  &op_stack[j]);
+    Nstack ++;
+  }
+
+cleanup: 
+  /*** free up unused stack space ***/
+
+  // If we parsed everything above, there should not be any un-freed op_stacks at this
+  // point.  However, if we had a syntax error, we will have op_stack entries left behind.
+  delete_stack (op_stack, NSTACK);
+  free (op_stack);
+
+  // XXX there should not be any data on higher stack entries
+  // clean_stack (&stack[Nstack], NSTACK - Nstack);
+  REALLOCATE (stack, StackVar, MAX (Nstack, 1));
+  *nstack = Nstack;
+
+  for (i = 0; i < argc; i++) {
+    free (argv[i]);
+  }
+  free (argv);
+
+# if (DUMPSTACK)
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%s ", stack[i].name);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%d ", stack[i].type);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+# endif
+
+  return (stack);
+
+}
+
+/* here are the rules for parsing a math AOL line to RPN:
+
+1) if object is a number, push on stack
+2) if object is a third order operand (exp, sin, cos), push on op stack
+3) if object is an open paren, push on op stack,
+4) if object is a second order operand, push on stack
+5) if object is a first order operand, pop all second order operands from stack 
+until paren, push on stack
+6) if object is an end paren, pop all objects from stack until paren, 
+pop next stack, if third order op
+7) if end of line, pop all remaining objects, second order first, etc.
+   
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/dvomath.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/dvomath.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/dvomath.c	(revision 22322)
@@ -0,0 +1,103 @@
+# include "opihi.h"
+
+/* return value on success is temp vector/buffer name or scalar value return value on error is NULL, all
+   internals freed.  errors are sent to error stack.  may be printed by calling function */
+
+/* XXX this function breaks the elements of the line into objects, numbers, and operators.  it
+ * then converts the list to an RPN expresssion.  it then evaluates the expression.  if an
+ * expression is not a valid math expression (string - string), it results in an error.  The
+ * problem is that elements of a valid expression may be invalid as expressions on their own
+ * (eg, string consisting of word-word).  this function probably should be somewhat smarter
+ * about breaking down items into strings and objects.  for the time being, the user must
+ * protect string items with double quotes for safety.
+ */
+   
+char *dvomath (int argc, char **argv, int *size, int validsize) {
+  
+  int  i, Nstack, Ncstack;
+  char   **cstack, *outname;
+  StackVar *stack;
+  Buffer *buf;
+  Vector *vec;
+
+  buf = NULL;
+  vec = NULL;
+  ALLOCATE (outname, char, 256);
+
+  /* take char array with expression, convert to important elements */
+  cstack = isolate_elements (argc, argv, &Ncstack); 
+
+  /* generate RPN stack from cstack arguments */
+  stack = convert_to_RPN (Ncstack, cstack, &Nstack);
+  if (Nstack < 1) goto error;
+
+  /* distinguish scalar, vector, matrix, check dimensions */
+  *size = check_stack (stack, Nstack, validsize);
+  if (*size < 0) goto error;
+
+  switch (*size) {
+    case 0:
+      break;
+    case 1:  /* allocate temp vector */
+      vec = NULL;
+      for (i = 0; (i < 1000) && (vec == NULL); i++) {
+	sprintf (outname, "tmp%03d", i);
+	vec = SelectVector (outname, NEWVECTOR, FALSE);
+      }
+      if (vec == NULL) { 
+	push_error ("too many tmp vectors");
+	goto error;
+      }
+      break;
+    case 2:  /* allocate temp buffer */
+      buf = NULL;
+      for (i = 0; (i < 1000) && (buf == NULL); i++) {
+	sprintf (outname, "tmp%03d", i);
+	buf = SelectBuffer (outname, NEWBUFFER, FALSE);
+      }
+      if (buf == NULL) {
+	push_error ("too many tmp buffers");
+	goto error;
+      }
+      break;
+    default:
+      goto error;
+  }
+
+  /* evaluate operations, free stack on error */
+  Ncstack = Nstack;
+  if (!evaluate_stack (stack, &Nstack)) {
+    if (*size == 1) DeleteVector (vec);
+    if (*size == 2) DeleteBuffer (buf);
+    goto error;
+  }
+
+  switch (*size) {
+    case 0:
+      if (Ncstack == 1) {
+	  /* use exact input work */
+	  sprintf (outname, "%s", stack[0].name);
+      } else {
+	  sprintf (outname, "%.12g", stack[0].Float);
+      }
+      break;
+
+    case 1:
+      MoveVector (vec, stack[0].vector);
+      break;
+  
+    case 2:
+      MoveBuffer (buf, stack[0].buffer);
+      break;
+  }
+
+  clean_stack (stack, Nstack);
+  free (stack);
+  return (outname);
+
+error:  
+  clean_stack (stack, Nstack);
+  free (stack);
+  free (outname);
+  return (NULL);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/errors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/errors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/errors.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "opihi.h"
+
+static char errorline[1024];
+
+int init_error () {
+
+  bzero (errorline, 1024);
+  return (TRUE);
+}
+
+int push_error (char *line) {
+
+  bzero (errorline, 1024);
+  strncpy (errorline, line, 1023);
+  return (TRUE);
+}
+
+int print_error () {
+
+  gprint (GP_ERR, "%s\n", errorline);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/evaluate_stack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/evaluate_stack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/evaluate_stack.c	(revision 22322)
@@ -0,0 +1,210 @@
+# include "opihi.h"
+# define VERBOSE 0
+
+# define TWO_OP(A,B,FUNC) \
+  if (!strncasecmp (&stack[i - 2].type, A, 1) && !strncasecmp (&stack[i - 1].type, B, 1)) \
+    status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); 
+
+# define ONE_OP(A,FUNC) \
+  if (!strncasecmp (&stack[i - 1].type, A, 1)) \
+    status = FUNC (&tmp_stack, &stack[i - 1], stack[i].name); 
+
+int evaluate_stack (StackVar *stack, int *Nstack) {
+  
+  int i, j, Nvar, Nout, status;
+  char line[512]; // this is only used to report an error
+  StackVar tmp_stack;
+  Nout = Nvar = 0;
+
+  status = TRUE;
+  init_stack (&tmp_stack);
+
+  if (*Nstack == 1) {
+    if (stack[0].type == 'S') {
+      clear_stack (&tmp_stack);
+      return (TRUE);
+    }
+    if (stack[0].type == 'V') {
+      /* need to make a copy so we set output value? */
+      V_unary (&tmp_stack, &stack[0], "=");
+      move_stack (&stack[0], &tmp_stack);
+      return (TRUE);
+    }
+    if (stack[0].type == 'M') {
+      /* need to make a copy so we set output value? */
+      M_unary (&tmp_stack, &stack[0], "=");
+      move_stack (&stack[0], &tmp_stack);
+      return (TRUE);
+    }
+    push_error ("syntax error: not a math expression");
+    clear_stack (&tmp_stack);
+    return (FALSE);
+  }      
+    
+  for (i = 0; i < *Nstack; i++) {
+
+    if (VERBOSE) {
+      gprint (GP_ERR, "%d: ", i);
+      for (j = 0; j < *Nstack; j++) {
+	gprint (GP_ERR, "%s ", stack[j].name);
+      }
+      if (*Nstack > 0) gprint (GP_ERR, "\n");
+      gprint (GP_ERR, "%d: ", i);
+      for (j = 0; j < *Nstack; j++) {
+	gprint (GP_ERR, "%d ", stack[j].type);
+      }
+      if (*Nstack > 0) gprint (GP_ERR, "\n");
+    }
+
+    /***** binary operators *****/
+    if ((stack[i].type >= 3) && (stack[i].type <= 8)) {
+
+      if (i < 2) {  /* need two variables to operate on */
+	sprintf (line, "syntax error: binary operator with one operand: %s\n", stack[i].name);
+	push_error (line);
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+
+      status = FALSE;
+      TWO_OP ("M","M",MM_binary);
+      TWO_OP ("M","V",MV_binary);
+      TWO_OP ("M","S",MS_binary);
+      TWO_OP ("V","M",VM_binary);
+      TWO_OP ("V","V",VV_binary);
+      TWO_OP ("V","S",VS_binary);
+      TWO_OP ("S","M",SM_binary);
+      TWO_OP ("S","V",SV_binary);
+      TWO_OP ("S","S",SS_binary);      
+      TWO_OP ("W","W",WW_binary);      
+      TWO_OP ("W","S",WW_binary);      
+      TWO_OP ("S","W",WW_binary);      
+      
+      if (!status) {
+	sprintf (line, "syntax error: invalid operand for binary operation: %s or %s\n", stack[i-1].name, stack[i-2].name);
+	push_error (line);
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+      move_stack (&stack[i-2], &tmp_stack);
+      delete_stack (&stack[i-1], 2);
+      for (j = i + 1; j < *Nstack; j++) {
+	move_stack (&stack[j - 2], &stack[j]);
+      }
+      *Nstack -= 2;
+      i -= 2;
+      init_stack (&tmp_stack);
+      continue;
+    }
+
+    /***** unary operators **/
+    if (stack[i].type == 9) {
+
+      if (i < 1) {  /* need one variable to operate on */
+	push_error ("syntax error: unary operator with no operand");
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+
+      ONE_OP ("M", M_unary);
+      ONE_OP ("V", V_unary);
+      ONE_OP ("S", S_unary);
+
+      /* there are no valid unary string operators */
+      if (!strncasecmp (&stack[i - 1].type, "W", 1)) {
+	push_error ("syntax error: no valid string unary ops");
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+
+      move_stack (&stack[i-1], &tmp_stack);
+      delete_stack (&stack[i], 1);
+      for (j = i + 1; j < *Nstack; j++) {
+	move_stack (&stack[j - 1], &stack[j]);
+      }
+      init_stack (&tmp_stack);
+      *Nstack -= 1;
+      i -= 1;
+      continue;
+    } 
+  }
+  clear_stack (&tmp_stack);
+
+  if (*Nstack > 1) {
+    push_error ("syntax error in evaluation");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+/* copy data to new stack variable */
+void copy_stack (StackVar *stack1, StackVar *stack2) {
+  stack1[0].name   = stack2[0].name  ;
+  stack1[0].type   = stack2[0].type  ;
+  stack1[0].ptr    = stack2[0].ptr   ;
+  stack1[0].buffer = stack2[0].buffer;
+  stack1[0].vector = stack2[0].vector;
+  stack1[0].Float  = stack2[0].Float ;
+  if (!strncasecmp (&stack1[0].type, "S", 1)) {
+    stack1[0].ptr    = &stack1[0].Float;
+  }
+}
+
+/* replace data with new stack variable */
+void move_stack (StackVar *stack1, StackVar *stack2) {
+  clear_stack (stack1);
+  copy_stack (stack1, stack2);
+  stack2[0].name = NULL;
+}
+
+/* delete name and data */
+void clean_stack (StackVar *stack, int Nstack) {
+
+  int i;
+
+  for (i = 0; i < Nstack; i++) {
+    if (IsBufferPtr (stack[i].buffer) && (stack[i].type == 'm')) {
+      if (VERBOSE) gprint (GP_ERR, "free %s (buff) (%lx)\n", stack[i].name, (long) stack[i].buffer);
+      free (stack[i].buffer[0].header.buffer);
+      free (stack[i].buffer[0].matrix.buffer);
+      free (stack[i].buffer);
+      stack[i].buffer = NULL;
+    }	
+    if (IsVectorPtr (stack[i].vector) && (stack[i].type == 'v')) {
+      if (VERBOSE) gprint (GP_ERR, "free %s (vect) (%lx)\n", stack[i].name, (long) stack[i].vector);
+      free (stack[i].vector[0].elements);
+      free (stack[i].vector);
+      stack[i].vector = NULL;
+    }	
+    if (VERBOSE) gprint (GP_ERR, "free %s (name) (%d) (%lx)\n", stack[i].name, i, (long) stack[i].name);
+    clear_stack (&stack[i]);
+  }
+
+}
+
+/* delete name only, not data */
+void delete_stack (StackVar *stack, int Nstack) {
+  int i;
+  for (i = 0; i < Nstack; i++) {
+    clear_stack (&stack[i]);
+  }
+}
+
+void init_stack (StackVar *stack) {
+  stack[0].buffer = NULL;
+  stack[0].vector = NULL;
+  stack[0].name = NULL;
+}
+
+void assign_stack (StackVar *stack, char *name, int type) {
+  stack->name = strcreate (name);
+  stack->type = type;
+}
+
+void clear_stack (StackVar *stack) {
+  if (stack->name == NULL) return;
+  // fprintf (stderr, "free %s\n", stack->name);
+  free (stack->name);
+  stack->name = NULL;
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/exec_loop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/exec_loop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/exec_loop.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "opihi.h"
+
+int exec_loop (Macro *loop) {
+
+  void *Signal;
+  int j, status, ThisList;
+  char *line;
+  
+  /* increase the shell level (Nlists) by one */
+  ThisList = increase_list_depth();
+  if (ThisList == 0) abort();
+
+  /* copy the macro to the current list */
+  for (j = 0; j < loop[0].Nlines; j++) {
+    add_listentry (ThisList, loop[0].line[j]);
+  }
+
+  /* set up interrupts */
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* process the list */
+  loop_next = loop_break = loop_last = FALSE;
+  status = TRUE;
+
+  while (!interrupt) {
+    line = get_next_listentry (ThisList);
+    if (line == NULL) break;
+    status = multicommand (line);
+    free (line);
+    if (auto_break && !status) loop_break = TRUE;
+    if (loop_break || loop_last || loop_next) break;
+  }
+  signal (SIGINT, Signal);
+
+  /* free remaining lines on the list, free the list, decrement the shell level */
+  /*** can we free a list which is not the bottom lists? */
+  decrease_list_depth();
+
+  if (loop_break) return (FALSE);
+  return (TRUE);
+}
+
+/** note that the list number runs from 1 - Nlists+1 **/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/expand_vars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/expand_vars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/expand_vars.c	(revision 22322)
@@ -0,0 +1,145 @@
+# include "opihi.h"
+
+char *expand_vars (char *line) {
+
+  char *L, *N, *V0, *V1, *Val, *newline, *c, found;
+  int done, MacroDepth, NLINE, Noff;
+
+  if (line == NULL) return (NULL);
+  MacroDepth = GetMacroDepth ();
+
+  found = FALSE;
+  NLINE = MAX (128, strlen(line));
+  ALLOCATE (newline, char, NLINE);  /* WARNING: this limits the length of the input line */
+
+  V0 = thiscomm (line);
+  if (V0 && !strncmp ("while", V0, strlen(V0))) {
+      // special case: don't expand variables in while () statement
+      strcpy (newline, line);
+      free (line);
+      return (newline);
+  }
+  free (V0);
+
+  /* look for form $a$b..$n and expand only the last ones */
+  for (L = line, N = newline; *L != 0; N++, L++) {  /* loop until end of line */
+    for (done = FALSE; !done;) {
+      if (*L == 0) done = TRUE;
+      if (*L == '$') {
+	V1 = aftervar(L);
+	if ((V1 != NULL) && (*V1 != '$')) done = TRUE;
+	if (V1 == NULL) done = TRUE;
+      }
+      if (!done) { 
+	*N = *L;
+	 L++; 
+	 N++;
+	 if (N - newline >= NLINE - 5) {
+	   Noff = N - newline;
+	   NLINE += 128;
+	   REALLOCATE (newline, char, NLINE);
+	   N = newline + Noff;
+	 }
+      }
+    }
+
+    if (*L == 0) break;
+
+    V1 = aftervar (L);           /* V1 points to the first non-WHITESPACE after the variable */
+    V0 = thisvar (L);            /* V0 points to the name of the var */
+    /* note: V1 points to a fraction of L, it does not need to be freed */
+
+    /* no variable name */
+    if (V0 == NULL) {
+      *N = *L;
+      if (N - newline >= NLINE - 5) {
+	Noff = N - newline;
+	NLINE += 128;
+	REALLOCATE (newline, char, NLINE);
+	N = newline + Noff;
+      }
+      continue;
+    }
+
+    /* variable assignment (skip these variables) */
+    if ((L == line) && (V1 != NULL)) {
+      if ((*V1 == '=') || !strcmp (V1, "++") || !strcmp (V1, "--")) {
+	*N = *L;
+	free (V0);
+	if (N - newline >= NLINE - 5) {
+	  Noff = N - newline;
+	  NLINE += 128;
+	  REALLOCATE (newline, char, NLINE);
+	  N = newline + Noff;
+	}
+	continue;
+      }
+    }
+
+    found = TRUE;
+    for (c = V0; isdigit(*c); c++); /* test if this is a macro argument variable (ie, $1.2) */
+    if (*c == 0) {  /* all digit var == macro parameter */
+      if (!MacroDepth) {
+	gprint (GP_ERR, "not in a macro\n");
+	goto error;
+      } else { /* if we are executing a macro, attach the list depth to the front, pass on down the line */
+	// XXX this is limiting!!!
+	ALLOCATE (c, char, 1024);
+	sprintf (c, "%d.%s", MacroDepth, V0);
+	free (V0);
+	V0 = c;
+      }
+    }
+
+    Val = get_variable_ptr (V0);
+    if (Val == NULL) {   /* var was not found! */
+      gprint (GP_ERR, "variable %s not found\n", V0);
+      goto error;
+    }
+    for (; *Val != 0; N++, Val++)  {
+      *N = *Val; /* place the value of the variable in the newline */
+      if (N - newline >= NLINE - 5) {
+	Noff = N - newline;
+	NLINE += 128;
+	REALLOCATE (newline, char, NLINE);
+	N = newline + Noff;
+      }
+    }
+    N--; /* we overshoot on last loop */
+
+    /* skip past the variable in the source line */
+    /* L currently points at $ */
+    L++;
+    if (*L == '?') L++;  /* skip past ? in $?name */
+
+    while (ISVAR(*L)) L++;
+    L--; /* we overshoot */
+
+    if (V0 != NULL) free (V0);
+  }
+
+  *N = 0;
+  free (line);
+  REALLOCATE (newline, char, strlen (newline) + 1);
+  if (found) { /* try again for new variables */
+    newline = expand_vars (newline);
+  }
+  return (newline);
+
+error:
+  free (line);
+  free (V0);
+  free (newline);
+  return (NULL);
+}
+
+
+  /************************
+    we go through the line looking for the dollar signs ($) marking the 
+    variables.  We don't expand a variable if it is the first thing on the line 
+    AND it is followed by an equals sign.  
+    that is, don't expand if:
+    V0 = NULL ($ is last non-blank char on line -- no variable) or
+    V1[0] is = (assignment of variable value), AND L == line (ie, at beginning of line)
+    *************************/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/expand_vectors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/expand_vectors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/expand_vectors.c	(revision 22322)
@@ -0,0 +1,176 @@
+# include "opihi.h"
+
+char *expand_vectors (char *line) {
+
+  char *newline, *tmpline, strValue[128], *val;
+  char *L, *N, *p, *q, *p2, *w;
+  int n, I, J, size, showLength, NLINE, Noff, isBuffer, inRange;
+  float *ptr;
+  double f1;
+  Vector *vec;
+  Buffer *buf;
+
+  if (line == NULL) return (NULL);
+
+  NLINE = MAX (128, strlen(line));
+  ALLOCATE (newline, char, NLINE);
+
+  /* look for form fred[stuff] */
+  /* skip first word (command) */
+  L = nextword (line);
+  if (L == NULL) {
+    free (newline);
+    return (line);
+  }
+
+  n = L - line;
+  strncpy (newline, line, n);
+  N = newline + n;
+  while (1) {
+    /* find square-bracket pair [..] */
+    p = strchr (L, '[');
+    q = strchr (L, ']');
+    if ((p == NULL) && (q != NULL)) goto dumpline;
+    if ((p != NULL) && (q == NULL)) goto dumpline;
+    if ((p == NULL) && (q == NULL)) goto dumpline;
+    if (p > q) goto dumpline; /* odd state: unmatched pair: ][ */
+
+    /* find vector subscript */
+    n = (int) (q - p - 1);
+    val = NULL;
+    showLength = FALSE;
+    if (n == 0) {
+      showLength = TRUE;
+    } else {
+      tmpline = strncreate (p+1, n);
+      val = dvomath (1, &tmpline, &size, 0);
+      free (tmpline);
+      if (val == NULL) goto dumpline; /* not a valid vector subscript */
+    }
+    I = 0; 
+    if (val != NULL) {
+      I = atoi (val);
+      free (val);
+    }      
+
+    /* if a second [..] immediately follows, we are a buffer, not a vector */
+    isBuffer = FALSE;
+    if (*(q + 1) == '[') {
+      p2 = strchr (q + 1, ']');
+      if (p2 != NULL) {
+	if (showLength) {
+	  gprint (GP_ERR, "unsupported : name[][..]\n"); 
+	  goto asVector;
+	}
+
+	isBuffer = TRUE;
+
+	/* find buffer second subscript */
+	n = (int) (p2 - q - 2);
+	val = NULL;
+	if (n == 0) {
+	  gprint (GP_ERR, "unsupported : name[..][]\n"); 
+	  isBuffer = FALSE;
+	  goto asVector;
+	} 
+	tmpline = strncreate (q+2, n);
+	val = dvomath (1, &tmpline, &size, 0);
+	free (tmpline);
+	if (val == NULL) {
+	  isBuffer = FALSE;
+	  goto asVector; /* not a valid vector subscript */
+	}
+	J = 0; 
+	if (val != NULL) {
+	  J = atoi (val);
+	  free (val);
+	}      
+      }
+      q = p2;
+    }
+
+  asVector:
+    /* find vector/buffer name */
+    for (w = p - 1; (w >= line) && !OHANA_WHITESPACE(*w) && (isalnum(*w) || (*w == ':') || (*w == '_')); w--);
+    w ++;
+    n = (int)(p - w);
+    tmpline = strncreate (w, n);
+
+    if (isBuffer) {
+      if ((buf = SelectBuffer (tmpline, OLDBUFFER, TRUE)) == NULL) goto dumpline;
+
+      /* find buffer element */
+      inRange = TRUE;
+      inRange &= (I <  +1*buf[0].header.Naxis[0]);
+      inRange &= (I >= -1*buf[0].header.Naxis[0]);
+      inRange &= (J <  +1*buf[0].header.Naxis[1]);
+      inRange &= (J >= -1*buf[0].header.Naxis[1]);
+      if (!inRange) {
+	gprint (GP_ERR, "buffer subscript out of range\n"); 
+	goto escape;
+      }
+      if (I < 0) I += buf[0].header.Naxis[0];
+      if (J < 0) J += buf[0].header.Naxis[1];
+      ptr = (float *) buf[0].matrix.buffer;
+      f1 = ptr[I + J*buf[0].header.Naxis[0]];
+    } else {
+      if ((vec = SelectVector (tmpline, OLDVECTOR, TRUE)) == NULL) goto dumpline;
+
+      /* find vector element */
+      if (showLength) {
+	f1 = vec[0].Nelements;
+      } else {
+	if ((I >= vec[0].Nelements) || (I < -1*vec[0].Nelements)) {
+	  gprint (GP_ERR, "vector subscript out of range\n"); 
+	  goto escape;
+	}
+	if (I < 0) I += vec[0].Nelements;
+	f1 = vec[0].elements[I];
+      }
+    }
+
+    free (tmpline);
+    if ((int)f1 == f1) 
+      snprintf (strValue, 128, "%.0f", f1);
+    else
+      snprintf (strValue, 128, "%.9g", f1);
+
+    /* interpolate vector element into newline (being accumulated) */
+    size = (N - newline) + (w - L) + strlen(strValue);
+    if (size >= NLINE) {
+      Noff = N - newline;
+      NLINE += 128 + strlen(strValue) + (w - L);
+      REALLOCATE (newline, char, NLINE);
+      N = newline + Noff;
+    }
+
+    n = (int) (w - L);
+    strncpy (N, L, n);
+    N += n;
+    n = strlen (strValue);
+    strncpy (N, strValue, n);
+    N += n;
+    L = q + 1;
+  }
+
+dumpline:
+  size = (N - newline) + strlen(L);
+  if (size >= NLINE) {
+    Noff = N - newline;
+    NLINE += 128 + strlen(L);
+    REALLOCATE (newline, char, NLINE);
+    N = newline + Noff;
+  }
+
+  n = strlen (L);
+  strncpy (N, L, n);
+  N[n] = 0;
+  free (line);
+  return (newline);
+  
+escape:
+  free (line);
+  free (newline);
+  return (NULL);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/gprint.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/gprint.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/gprint.c	(revision 22322)
@@ -0,0 +1,342 @@
+# include "shell.h"
+
+/* we need to control the output destinations a bit carefully
+   in the pantasks_server mode.  The server needs to be able to 
+   send the output to a specific output device (eg, logging file)
+   of to save it within an internal buffer */
+
+/* further notes:
+   gprintf (int destination, char *format, ...);
+
+   the destination may be GP_LOG or GP_ERR.  by default, these go to stdout and
+   stderr.  either one may be redirected to another file or to a buffer.  as a
+   stand-along program, the outfile command redirects the GP_LOG output to an
+   alternate output file.  in the server mode, we redirect LOG to a standard log
+   file and ERR to a standard error file for server messages.  Commands executed
+   by the client have both streams returned to the client in turn, and are in
+   turn sent to stderr or the current stdout destination.
+
+   each thread has an independently set output destination.
+
+   option 1: NULL for invalid element:
+
+     a stream is either set to a FILE or an IOBuffer.  when it is set to a FILE,
+     the IOBuffer is freed and set to NULL.  when it is an IOBuffer, the file
+     pointer is closed and set to NULL.  setting a new FILE results in the 
+     IObuffer being freed and an already opened FILE to be flushed and closed.
+
+
+  gprint & mutex:
+
+     gprintInit : init_stream_mutex
+     gprintGetStream : init_stream_mutex
+     gprintCloseFile : init_stream_mutex
+
+     gprintSetFileAllThreads : set_file_mutex
+     (gprintSetFile -> gprintCloseFile)
+
+     gprintSetFileThisThread : set_file_mutex
+     (gprintGetStream)
+     (gprintSetFile -> gprintCloseFile)
+
+     we are safe from dead locks: init_stream_mutex can be wrapped by set_file_mutex, but
+     not vice-versa
+
+*/
+
+static gpStream **streams = NULL;
+static int Nstreams = 0;
+
+static pthread_mutex_t init_stream_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void gprintInit () {
+
+  int N;
+  pthread_t id;
+
+  /* need to use a mutex to prevent two threads from initing simultaneously */
+  pthread_mutex_lock (&init_stream_mutex);
+
+  // streams is an array of pointers so we can add more streams without changing pointers 
+  if (streams == NULL) {
+    Nstreams = 2;
+    ALLOCATE (streams, gpStream *, Nstreams);
+  } else {
+    Nstreams += 2;
+    REALLOCATE (streams, gpStream *, Nstreams);
+  }
+
+  /* create two output streams for this thread: LOG and ERR */
+  id = pthread_self();
+
+  N = Nstreams - 2;
+  ALLOCATE (streams[N], gpStream, 1);
+  streams[N][0].dest = GP_LOG;
+  streams[N][0].file = stdout;
+  streams[N][0].name = strcreate ("stdout");
+
+  ALLOCATE (streams[N][0].buffer, IOBuffer, 1);
+  InitIOBuffer (streams[N][0].buffer, 64);
+  streams[N][0].mode = GP_FILE;
+  streams[N][0].thread = id;
+
+  N = Nstreams - 1;
+  ALLOCATE (streams[N], gpStream, 1);
+  streams[N][0].dest = GP_ERR;
+  streams[N][0].file = stderr;
+  streams[N][0].name = strcreate ("stderr");
+
+  ALLOCATE (streams[N][0].buffer, IOBuffer, 1);
+  InitIOBuffer (streams[N][0].buffer, 64);
+  streams[N][0].mode = GP_FILE;
+  streams[N][0].thread = id;
+
+  pthread_mutex_unlock (&init_stream_mutex);
+}
+
+gpStream *gprintGetStream (gpDest dest) {
+
+  int i;
+  pthread_t id;
+  gpStream *stream;
+
+  // need to wait for initialization to be finished before getting stream or the array
+  // (streams[i]) may move
+  pthread_mutex_lock (&init_stream_mutex);
+
+  id = pthread_self();
+
+  /* find the existing output stream which matches */
+  for (i = 0; i < Nstreams; i++) {
+    if (streams[i][0].dest != dest) continue;
+    if (!pthread_equal (streams[i][0].thread, id)) continue;
+    stream = streams[i];
+    pthread_mutex_unlock (&init_stream_mutex);
+    return stream;
+  }
+  pthread_mutex_unlock (&init_stream_mutex);
+  fprintf (stderr, "programming error: gprintInit not called for thread\n");
+  abort ();
+}
+
+// close if necessary, set to file (may be NULL)
+void gprintCloseFile (gpStream *stream, FILE *file) {
+
+  int i, Nmatch;
+
+  // do not close the file if the new file is the same
+  if (stream[0].file == file) return;
+
+  // do not close the file if the old one is NULL
+  if (stream[0].file == NULL) {
+    stream[0].file = file;    
+    return;
+  }
+
+  // check the special cases (do not close old file in these cases)
+  if (stream[0].file == stdout) {
+    stream[0].file = file;
+    return;
+  }
+  if (stream[0].file == stderr) {
+    stream[0].file = file;
+    return;
+  }
+
+  // we cannot do the operation below while another thread is initing
+  pthread_mutex_lock (&init_stream_mutex);
+
+  // must we close the existing file? if still being used, then no
+  Nmatch = 0;
+  for (i = 0; i < Nstreams; i++) {
+    if (stream == streams[i]) continue;
+    if (streams[i][0].file == stream[0].file) Nmatch ++;
+  }
+  pthread_mutex_unlock (&init_stream_mutex);
+
+  if (Nmatch == 0) {
+    fflush (stream[0].file);
+    fclose (stream[0].file);
+  }
+
+  stream[0].file = file;
+  return;
+}
+
+// this thread only operates on its own stream
+void gprintSetBuffer (gpDest dest) {
+
+  gpStream *stream;
+
+  stream = gprintGetStream (dest);
+
+  // close the existing file (if needed), set to NULL
+  gprintCloseFile (stream, NULL);
+
+  assert (stream[0].buffer);
+  FlushIOBuffer (stream[0].buffer);
+  
+  stream[0].mode = GP_BUFF;
+}
+
+// this thread only operates on its own stream
+IOBuffer *gprintGetBuffer (gpDest dest) {
+
+  gpStream *stream;
+  stream = gprintGetStream (dest);
+  return (stream[0].buffer);
+}
+
+static pthread_mutex_t set_file_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void gprintSetFileAllThreads (gpDest dest, char *filename) {
+
+  int i;
+
+  // be sure we are not colliding with gprintSetFileThisThread
+  pthread_mutex_lock (&set_file_mutex);
+
+  for (i = 0; i < Nstreams; i++) {
+    if (streams[i][0].dest != dest) continue;
+    gprintSetFile (streams[i], dest, filename);
+  }
+
+  pthread_mutex_unlock (&set_file_mutex);
+  return;
+}
+
+// this thread only operates on its own stream
+void gprintSetFileThisThread (gpDest dest, char *filename) {
+
+  gpStream *stream;
+
+  // be sure we are not colliding with gprintSetFileAllThreads
+  pthread_mutex_lock (&set_file_mutex);
+
+  stream = gprintGetStream (dest);
+  gprintSetFile (stream, dest, filename);
+
+  pthread_mutex_unlock (&set_file_mutex);
+  return;
+}
+
+void gprintSetFile (gpStream *stream, gpDest dest, char *filename) {
+
+  FILE *file;
+
+  assert (stream[0].buffer);
+
+  /* if we have an open buffer, free it and null it */
+  if (stream[0].buffer[0].Nbuffer) {
+    // XXX we drop what was on the buffer, send it to the old or the new file?
+    FlushIOBuffer (stream[0].buffer);
+  }
+
+  stream[0].mode = GP_FILE;
+
+  // if NULL, reuse exising stream name
+  if (filename != NULL) {
+    free (stream[0].name);
+    stream[0].name = strcreate (filename);
+  }
+
+  // we allow the user to set stdout to ERR and stderr to LOG if they want
+  if (!strcmp (stream[0].name, "stdout")) {
+    gprintCloseFile (stream, stdout);
+    return;
+  }
+  if (!strcmp (stream[0].name, "stderr")) {
+    gprintCloseFile (stream, stderr);
+    return;
+  }
+  
+  // open the file. only close old pointer if no one else is using it
+  file = fopen (stream[0].name, "a");
+  if (file == NULL) {
+    // XXX this is a problem: we are leaving open the old file
+    fprintf (stderr, "cannot open file %s\n", stream[0].name);
+    free (stream[0].name);
+    file = (dest == GP_LOG) ? stdout : stderr;
+    stream[0].name = (dest == GP_LOG) ? strcreate ("stdout") : strcreate("stderr");
+    gprintCloseFile (stream, file);
+    return;
+  }
+
+  // close the existing file (if needed), set to new file
+  gprintCloseFile (stream, file);
+  return;
+}
+
+// this thread only operates on its own stream
+FILE *gprintGetFile (gpDest dest) {
+
+  gpStream *stream;
+  stream = gprintGetStream (dest);
+  return (stream[0].file);
+}
+
+// this thread only operates on its own stream
+char *gprintGetName (gpDest dest) {
+
+  gpStream *stream;
+  stream = gprintGetStream (dest);
+  return (stream[0].name);
+}
+
+int gprint (gpDest dest, char *format, ...) {
+
+  int status;
+  gpStream *stream;
+  va_list argp;  
+
+  // this thread only writes to its own stream
+  stream = gprintGetStream (dest);
+
+  va_start (argp, format);
+
+  if (stream[0].mode == GP_FILE) {
+    status = vfprintf (stream[0].file, format, argp);
+    if (status < 0) {
+      abort();
+    }
+  } else {
+    vPrintIOBuffer (stream[0].buffer, format, argp);
+  }
+  va_end (argp);
+  return (TRUE);
+}
+
+int gwrite (char *buffer, int size, int N, gpDest dest) {
+
+  int Nbyte;
+  IOBuffer *outbuff;
+  gpStream *stream;
+
+  // this thread only writes to its own stream
+  stream = gprintGetStream (dest);
+
+  if (stream[0].mode == GP_FILE) {
+    fwrite (buffer, size, N, stream[0].file);
+  } else {
+    // XXX can we not use exising IOBuffer APIs here?
+    outbuff = stream[0].buffer;
+    Nbyte = size * N;
+    if (outbuff[0].Nbuffer + Nbyte >= outbuff[0].Nalloc) {
+      outbuff[0].Nalloc = outbuff[0].Nbuffer + Nbyte + 64;
+      REALLOCATE (outbuff[0].buffer, char, outbuff[0].Nalloc);
+    }
+    memcpy (&outbuff[0].buffer[outbuff[0].Nbuffer], buffer, Nbyte);
+    outbuff[0].Nbuffer += Nbyte;
+  }
+  return (TRUE);
+}
+
+/* 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
+   
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/interrupt.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/interrupt.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/interrupt.c	(revision 22322)
@@ -0,0 +1,53 @@
+# include "opihi.h"
+
+static int Nint = 0;
+
+/* return FALSE if we are called many times in a row 
+   without a valid answer or any answer */
+
+void handle_interrupt (int input) {
+  
+  char string[64];
+  int Nask;
+
+  signal (SIGINT, SIG_IGN);
+
+  Nask = 0;
+  Nint ++;
+
+  // 3 ctrl-c in a row will interrupt regardless
+  if (Nint > 3) { 
+    interrupt = TRUE;
+    return;
+  }
+  if (Nint > 100) { 
+    exit (3);
+  }
+  
+  while (1) {
+    gprint (GP_ERR, "operation halted, continue? (y/n) ");
+    fscanf (stdin, "%s", string);
+    
+    if ((string[0] == 'y') || (string[0] == 'Y')) {
+      interrupt = FALSE;
+      signal (SIGINT, handle_interrupt);
+      Nint = 0;
+      return;
+    }
+
+    if ((string[0] == 'n') || (string[0] == 'N')) {
+      interrupt = TRUE;
+      signal (SIGINT, handle_interrupt);
+      Nint = 0;
+      return;
+    }
+    Nask ++;
+    if (Nask > 3) {
+      interrupt = TRUE;
+      signal (SIGINT, handle_interrupt);
+      Nint = 0;
+      return;
+    }
+  }  
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/isolate_elements.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/isolate_elements.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/isolate_elements.c	(revision 22322)
@@ -0,0 +1,210 @@
+# include "opihi.h"
+
+/* local private functions */
+char **InsertValue (char **myOutput, int *Nout, int *Nchar, int *NCHAR, char c);
+char **EndOfString (char **myOutput, int *Nout, int *Nchar, int *NOUT, int *NCHAR);
+int IsAnOp (char *c);
+int IsTwoOp (char *c);
+
+char **isolate_elements (int Nin, char **in, int *nout) {
+  
+  /* local private static variables */
+  int NCHAR, Nchar, Nout, NOUT;
+  char **myOutput;
+
+  int i, j, minus, negate, plus, posate, OpStat, SciNotation;
+
+  NOUT = Nin;
+  Nchar = Nout = 0;
+  NCHAR = 256;
+  ALLOCATE (myOutput, char *, NOUT);
+  ALLOCATE (myOutput[Nout], char, NCHAR);
+
+  for (i = 0; i < Nin; i++) {
+    for (j = 0; j < strlen(in[i]); j++) {
+      SciNotation = FALSE;
+      /* identify 'negate' or 'minus' ops */
+      negate = minus = FALSE;
+      if (in[i][j] == '-') { 
+	minus = TRUE;  
+	/* if - is first thing on line, must be a negator */
+	if ((Nout == 0) && (Nchar == 0)) {  
+	  minus = FALSE;
+	  negate = TRUE;
+	  goto skip1;
+	}
+	/* check previous entry on line */
+	if (Nchar) {
+	  OpStat = IsAnOp (myOutput[Nout]);
+	  if (myOutput[Nout][0] == ')') OpStat = FALSE;
+	} else {
+	  OpStat = IsAnOp (myOutput[Nout-1]);
+	  if (myOutput[Nout-1][0] == ')') OpStat = FALSE;
+	}
+	/* if - follows an operator, must be negator */
+	if (OpStat) {
+	  minus = FALSE;
+	  negate = TRUE;
+	  goto skip1;
+	}
+	/* if - follows 'e' is part of 1e-5 */
+	if (j == 0) goto skip1;
+	if ((in[i][j-1] == 'e') || (in[i][j-1] == 'E')) {
+	  SciNotation = TRUE;
+	  negate = minus = FALSE;
+	}
+      }
+    skip1:
+      /* idenfity 'posate' or 'plus' ops */
+      posate = plus = FALSE;
+      if (in[i][j] == '+') { 
+	plus = TRUE;  
+	/* if + is first thing on line, must be a posator */
+	if ((Nout == 0) && (Nchar == 0)) {  
+	  plus = FALSE;
+	  posate = TRUE;
+	  goto skip2;
+	}
+	/* check previous entry on line */
+	if (Nchar) {
+	  OpStat = IsAnOp (myOutput[Nout]);
+	  if (myOutput[Nout][0] == ')') OpStat = FALSE;
+	} else {
+	  OpStat = IsAnOp (myOutput[Nout-1]);
+	  if (myOutput[Nout-1][0] == ')') OpStat = FALSE;
+	}
+	/* if + follows an operator, must be posator */
+	if (OpStat) {
+	  plus = FALSE;
+	  posate = TRUE;
+	  goto skip2;
+	}
+	/* if + follows 'e' is part of 1e+5 */
+	if (j == 0) goto skip2;
+	if ((in[i][j-1] == 'e') || (in[i][j-1] == 'E')) {
+	  SciNotation = TRUE;
+	  posate = plus = FALSE;
+	}
+      }
+    skip2:
+      /* operators */
+      if (negate || minus || posate || plus || (IsAnOp (&in[i][j]) && !SciNotation)) {
+	if (posate) continue;
+	myOutput = EndOfString (myOutput, &Nout, &Nchar, &NOUT, &NCHAR);
+	/* copy operator to myOutput[Nout] */
+	myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j]);
+	if (negate) {
+	  myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, '-');
+	}
+
+	if (IsTwoOp (&in[i][j])) {
+	  myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j+1]);
+	  j++;
+	} 
+	myOutput = EndOfString (myOutput, &Nout, &Nchar, &NOUT, &NCHAR);
+	continue;
+      }
+      /* quoted string */
+      if (in[i][j] == '"') {
+	myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j]);
+	j++;
+	while ((j < strlen(in[i])) && (in[i][j] != '"')) {
+	  myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j]);
+	  j++;
+	}
+	if (in[i][j] != '"') continue;
+	/* 
+	  gprint (GP_ERR, "mismatched quotes\n");
+	  return (FALSE);
+	}
+	*/
+	myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j]);
+	myOutput = EndOfString (myOutput, &Nout, &Nchar, &NOUT, &NCHAR);
+	continue;
+      }
+      /* not an operator, not a quoted string */
+      if (!OHANA_WHITESPACE (in[i][j])) {
+	myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j]);
+      } else {
+	myOutput = EndOfString (myOutput, &Nout, &Nchar, &NOUT, &NCHAR);
+      }
+    }
+	myOutput = EndOfString (myOutput, &Nout, &Nchar, &NOUT, &NCHAR);
+  }
+
+  /* one extra entry is allocated, free here */
+  free (myOutput[Nout]);
+  *nout = Nout;
+  return (myOutput);
+
+}
+
+char **InsertValue (char **myOutput, int *Nout, int *Nchar, int *NCHAR, char c) {
+  myOutput[*Nout][*Nchar] = c;
+  (*Nchar) ++;
+  if ((*Nchar) >= (*NCHAR) - 2) {
+    (*NCHAR) += 256;
+    REALLOCATE (myOutput[*Nout], char, *NCHAR);
+  }
+  myOutput[*Nout][*Nchar] = 0;
+  return (myOutput);
+}
+
+char **EndOfString (char **myOutput, int *Nout, int *Nchar, int *NOUT, int *NCHAR) {
+  if ((*Nchar) > 0) {
+    myOutput[*Nout][*Nchar] = 0;
+    (*Nout) ++;
+    (*Nchar) = 0;
+    
+    if ((*Nout) >= (*NOUT) - 1) {
+      (*NOUT) += 10; 
+      REALLOCATE (myOutput, char *, (*NOUT)); 
+    } 
+    (*NCHAR) = 256;
+    ALLOCATE (myOutput[*Nout], char, (*NCHAR)); 
+  }
+  return (myOutput);
+}
+
+int IsAnOp (char *c) {
+
+  if (!strncmp (c, "*",  1)) return (TRUE);
+  if (!strncmp (c, "/",  1)) return (TRUE);
+  if (!strncmp (c, "+",  1)) return (TRUE);
+  if (!strncmp (c, "-",  1)) return (TRUE);
+  if (!strncmp (c, "^",  1)) return (TRUE);
+  if (!strncmp (c, "@",  1)) return (TRUE);
+  if (!strncmp (c, "(",  1)) return (TRUE);
+  if (!strncmp (c, ")",  1)) return (TRUE);
+  if (!strncmp (c, "&",  1)) return (TRUE);
+  if (!strncmp (c, "|",  1)) return (TRUE);
+  if (!strncmp (c, ">",  1)) return (TRUE);
+  if (!strncmp (c, "<",  1)) return (TRUE);
+  if (!strncmp (c, "<",  1)) return (TRUE);
+  if (!strncmp (c, "<<", 2)) return (TRUE);
+  if (!strncmp (c, ">>", 2)) return (TRUE);
+  if (!strncmp (c, "&&", 2)) return (TRUE);
+  if (!strncmp (c, "||", 2)) return (TRUE);
+  if (!strncmp (c, "==", 2)) return (TRUE);
+  if (!strncmp (c, "!=", 2)) return (TRUE);
+  if (!strncmp (c, "<=", 2)) return (TRUE);
+  if (!strncmp (c, ">=", 2)) return (TRUE);
+
+  return (FALSE);
+
+}
+
+int IsTwoOp (char *c) {
+
+  if (!strncmp (c, "<<", 2)) return (TRUE);
+  if (!strncmp (c, ">>", 2)) return (TRUE);
+  if (!strncmp (c, "&&", 2)) return (TRUE);
+  if (!strncmp (c, "||", 2)) return (TRUE);
+  if (!strncmp (c, "==", 2)) return (TRUE);
+  if (!strncmp (c, "!=", 2)) return (TRUE);
+  if (!strncmp (c, "<=", 2)) return (TRUE);
+  if (!strncmp (c, ">=", 2)) return (TRUE);
+
+  return (FALSE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_create.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_create.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_create.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "opihi.h"
+# define D_NLINES 100
+# define prompt "> "
+int macro_exec   PROTO((int, char **));
+
+int macro_create (int argc, char **argv) {
+
+  int ThisList, depth, i, done, NLINES, N;
+  char *input, *help;
+  Command *cmd;
+  Macro *macro;
+
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    help = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  } else {
+    help = strcreate ("(macro)");
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: macro <name> [-c \"comment line\"]\n");
+    gprint (GP_ERR, "  (enter commands, end with the word 'END')\n");
+    return (FALSE);
+  }
+
+  /**** Check for existence of this macro ****/
+  cmd = MatchCommand (argv[1], FALSE, TRUE);
+  macro = MatchMacro (argv[1], FALSE, TRUE);
+  
+  if ((macro == NULL) && (cmd != NULL)) {
+    gprint (GP_ERR, "cannot redefine inherent command %s\n", argv[1]);
+    return (FALSE);
+  }
+  if ((macro != NULL) && (cmd == NULL)) {
+    gprint (GP_ERR, "programming error: macro not in command list (%s)\n", argv[1]);
+    return (FALSE);
+  }
+
+  if (macro == NULL) { /**** New Macro ****/
+    ALLOCATE (cmd, Command, 1);
+    cmd[0].real = FALSE;
+    cmd[0].name = strcreate (argv[1]);
+    cmd[0].help = help;
+    cmd[0].func = macro_exec;
+    AddCommand (cmd);
+    free (cmd);
+    macro = NewMacro (argv[1]);
+  } else { /**** Old Macro ****/
+    /* replace existing command help with new value */
+    free (cmd[0].help);
+    cmd[0].help = help;
+  }
+
+  /* reallocate space for macro lines */
+  NLINES = D_NLINES;
+  for (i = 0; i < macro[0].Nlines; i++) free (macro[0].line[i]);
+  REALLOCATE (macro[0].line, char *, NLINES);
+
+  /* read in macro
+     If we are entering at the keyboard (ThisList == 0), use readline.
+     Otherwise, read from the current list
+     End when we hit the final "END" (the true END -- we count macro defines!!) */
+     
+  depth = 0;
+  ThisList = current_list_depth();
+  for (i = 0, done = FALSE; !done; ) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+
+    if ((ThisList == 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "end macro with 'END'\n");
+      continue;
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    if ((ThisList > 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "missing 'END' in macro definition\n");
+      input = strcreate ("end");
+    }
+
+    stripwhite (input);
+
+    /* test for new macro (or other list, in the future?) */
+    if (is_list (input)) depth ++;
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { /* we hit the last "END", macro is done */
+	free (input);
+	macro[0].Nlines = i;
+	if (macro[0].Nlines == 0) i = 1;
+	REALLOCATE (macro[0].line, char *, i);
+	return (TRUE);
+      }
+    }
+
+    if (*input) { 
+      macro[0].line[i] = input;
+      i++;
+      if (i == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (macro[0].line, char *, NLINES);
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_delete.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_delete.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_delete.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "opihi.h"
+
+int macro_delete (int argc, char **argv) {
+
+  Command *cmd;
+  Macro *macro;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: macro delete <macro>\n");
+    return (FALSE);
+  }
+
+  macro = MatchMacro (argv[1], FALSE, TRUE);
+  if (macro == NULL) {
+    gprint (GP_ERR, "Macro %s not found\n", argv[1]);
+    return (FALSE);
+  }
+  cmd = MatchCommand (argv[1], FALSE, TRUE);
+  if (cmd == NULL) {
+    gprint (GP_ERR, "programming error: macro exists but not command\n");
+    return (FALSE);
+  }
+
+  DeleteMacro (macro);
+  DeleteCommand (cmd);
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_edit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_edit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_edit.c	(revision 22322)
@@ -0,0 +1,9 @@
+# include "opihi.h"
+
+int macro_edit (int argc, char **argv) {
+
+  gprint (GP_ERR, "this function is not implemented yet\n");
+  return (FALSE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_exec.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_exec.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_exec.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "opihi.h"
+
+int macro_exec (int argc, char **argv) {
+
+  int i, status, MacroDepth;
+  char *PreviousName, **params, tmp[1024];
+  Macro *macro;
+  
+  macro = MatchMacro (argv[0], FALSE, TRUE);
+  if (macro == NULL) {
+    gprint (GP_ERR, "%s: Command not found.\n", argv[0]);
+    return (FALSE);
+  }
+
+  /* increase MacroDepth by one - governs macro args */
+  PreviousName = GetMacroName ();
+  MacroDepth = GetMacroDepth ();
+  MacroDepth ++;
+  SetCurrentMacroData (macro[0].name, MacroDepth);
+
+  ALLOCATE (params, char *, argc);
+  sprintf (tmp, "%d.%d", MacroDepth, 0);
+  params[0] = strcreate (tmp);
+  sprintf (tmp, "%d", argc);
+  set_str_variable (params[0], tmp);
+  for (i = 1; i < argc; i++) {
+    sprintf (tmp, "%d.%d", MacroDepth, i);
+    params[i] = strcreate (tmp);
+    set_str_variable (params[i], argv[i]);
+  }
+
+  /* process this list */
+  status = exec_loop (&macro[0]);
+  loop_last = loop_next = FALSE; 
+  /* 'last' and 'next' should only affect one loop */
+
+  /* clear out the command line variables */
+  for (i = 0; i < argc; i++) {
+    DeleteNamedScalar (params[i]);
+    free (params[i]);
+  }
+  free (params);
+  
+  MacroDepth --;
+  SetCurrentMacroData (PreviousName, MacroDepth);
+  return (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_funcs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_funcs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_funcs.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "basic.h"
+
+static Command macro_command[] = {
+  {1, "create", macro_create, "create a macro *"},
+  {1, "delete", macro_delete, "delete a macro *"},
+  {1, "list",   macro_list_f, "list a macro *"},
+  {1, "edit",   macro_edit,   "edit a macro <not working yet!> *"},
+  {1, "read",   macro_read,   "read a macro from a file <not working yet!> *"},
+  {1, "write",  macro_write,  "write a macro to a file <not working yet!> *"}
+};
+
+CommandF *find_macro_command (char *name) {
+
+  int i, N;
+
+  N = sizeof (macro_command) / sizeof (Command);
+
+  /* find the macro sub-command which matches from the list. */
+  for (i = 0; i < N; i++) {
+    if (!strcmp (macro_command[i].name, name)) {
+      return (macro_command[i].func);
+    }
+  }
+
+  return NULL;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_list.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_list.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_list.c	(revision 22322)
@@ -0,0 +1,23 @@
+# include "opihi.h"
+
+int macro_list_f (int argc, char **argv) {
+
+  int i;
+  Macro *macro;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: macro list <macro>\n");
+    return (FALSE);
+  }
+
+  macro = MatchMacro (argv[0], FALSE, TRUE);
+  if (macro == NULL) {
+    gprint (GP_ERR, "%s: Macro not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  for (i = 0; i < macro[0].Nlines; i++) {
+    gprint (GP_ERR, "%s\n", macro[0].line[i]);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_read.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_read.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_read.c	(revision 22322)
@@ -0,0 +1,9 @@
+# include "opihi.h"
+
+int macro_read (int argc, char **argv) {
+
+  gprint (GP_ERR, "this function is not implemented yet\n");
+  return (FALSE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_write.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_write.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/macro_write.c	(revision 22322)
@@ -0,0 +1,9 @@
+# include "opihi.h"
+
+int macro_write (int argc, char **argv) {
+
+  gprint (GP_ERR, "this function is not implemented yet\n");
+  return (FALSE);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/memstr.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/memstr.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/memstr.c	(revision 22322)
@@ -0,0 +1,35 @@
+# include "shell.h"
+
+/* memstr returns a view, not an allocated string : don't free */
+/* returns pointer to start of m2 in m1, or NULL if failure */ 
+char *memstr (char *m1, char *m2, int n) {
+
+  int i, N;
+
+  N = strlen (m2);
+  for (i = 0; (i < n - N + 1) && memcmp (m1, m2, N); i++, m1++);
+  if (memcmp (m1, m2, N)) return (NULL);
+  return (m1);
+
+}
+
+/* formatted write statement, with intelligent allocation */
+int write_fmt (int fd, char *format, ...) {
+
+  int Nbyte, status;
+  char tmp, *line;
+  va_list argp;  
+
+  va_start (argp, format);
+  Nbyte = vsnprintf (&tmp, 0, format, argp);
+  va_end (argp);
+
+  va_start (argp, format);
+  ALLOCATE (line, char, Nbyte + 1);
+  vsnprintf (line, Nbyte + 1, format, argp);
+  status = write (fd, line, strlen(line));
+  va_end (argp);
+
+  free (line);
+  return (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/multicommand.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/multicommand.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/multicommand.c	(revision 22322)
@@ -0,0 +1,159 @@
+# include "opihi.h"
+
+static int server = 0;
+static int bufferPending = FALSE;
+
+int getServer () {
+  return (server);
+}
+
+// XXX this is rather pantasks-specific...
+void multicommand_InitServer () {
+
+  char hostname[256], PASSWORD[256];
+
+  if (server != 0) {
+    /* check if down? */
+    fprintf (stderr, "error: server fd already defined\n");
+    exit (30);
+  }
+
+  /* find the defined server hostname */
+  if (VarConfig ("PANTASKS_SERVER", "%s", hostname) == NULL) {
+    gprint (GP_ERR, "pantasks server host undefined\n");
+    exit (31);
+  }
+
+  /* attempt to connect to the server */
+  server = GetClientSocket (hostname);
+
+  /* here we can perform the security handshaking */
+  VarConfig ("PASSWORD", "%s", PASSWORD);
+  SendCommand (server, strlen(PASSWORD), PASSWORD);
+  
+  return;
+}
+
+// close connection with remote server
+void multicommand_StopServer () {
+
+  if (!server) return;
+  close (server);
+  server = 0;
+  return;
+}
+
+/* take input line and split into multiple lines
+   at the semi-colons.  send these to 'command' */
+
+int multicommand (char *line) {
+ 
+  int done, status, verbose;
+  char *p, *q, *tmpline, *outline;
+  IOBuffer message;
+
+  /* if no server is defined, use 'verbose' mode for 'command' */ 
+  verbose = (server == 0);
+
+  p = line; 
+  done = FALSE;
+  status = TRUE;
+  while (!done) {
+    q = strchr (p, ';');
+    if (q == NULL) {
+      q = p + strlen(p);
+      done = TRUE;
+    }
+    tmpline = strncreate (p, q - p);
+    stripwhite (tmpline);
+    if (*tmpline) {
+
+      if (bufferPending) {
+	// flush old messages
+	ExpectMessage (server, 0.2, &message);
+	bufferPending = FALSE;
+      }
+
+      status = command (tmpline, &outline, verbose);
+
+      if (status == -1) {
+	if (server) {
+	  // send the command to the server instead
+	  if (!SendMessage (server, outline)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (32);
+	      default:
+		gprint (GP_ERR, "server is busy...\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  }
+
+	  // receive the command exit status
+	  if (ExpectMessage (server, 10.0, &message)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (33);
+	      default:
+		gprint (GP_ERR, "server is busy...\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  } else {
+	    sscanf (message.buffer, "STATUS %d", &status);
+	  }
+
+	  // receive the resulting stderr
+	  if (ExpectMessage (server, 10.0, &message)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (34);
+	      default:
+		gprint (GP_ERR, "server is busy...\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  } else {
+	    gwrite (message.buffer, 1, message.Nbuffer, GP_ERR);
+	  }
+
+	  // receive the resulting stdout
+	  if (ExpectMessage (server, 10.0, &message)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (35);
+	      default:
+		gprint (GP_ERR, "server is busy...\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  } else {
+	    gwrite (message.buffer, 1, message.Nbuffer, GP_LOG);
+	  }
+	} else {
+	  // if no server is defined, we treat an unknown command as an error
+	  status = FALSE;
+	}	
+      }
+    escape:
+      if (outline != NULL) free (outline);
+      if (!status && auto_break) done = TRUE;
+    }
+    p = q + 1;
+  }
+  return (status);
+}
+
+/* should skip ; surrounded by "" */
+
+/* commands which are not found are sent to the server socket if 
+   defined. commands executed by the server return their stderr
+   and stdout streams.  server commands should probably not block
+   the server for very long or multiple clients will interfere
+   with each other.  
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/opihi.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/opihi.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/opihi.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "opihi.h"
+
+/******************/
+int opihi (int argc, char **argv) {
+
+  int Nbad, status;
+  char *line, *prompt, *history;
+  pid_t ppid;
+
+  general_init (&argc, argv);
+  program_init (&argc, argv);
+  startup (&argc, argv);
+  prompt = get_variable("PROMPT");
+  history = get_variable("HISTORY");
+  welcome ();
+
+  Nbad = 0;
+  while (1) {  /** must exit with command "exit" or "quit" */
+    if (Nbad == 10) exit (20);
+
+    line = readline (prompt);
+
+    if (line == NULL) { 
+      
+      ppid = getppid();
+      if (ppid == 1) {
+	signal (SIGPIPE, SIG_IGN);
+	gprint (GP_ERR, "caught parent shutdown\n");
+	exit (21);
+      }
+      if (!isatty (STDIN_FILENO)) exit (21);
+      gprint (GP_LOG, "Use \"quit\" to exit\n");
+      Nbad ++;
+      continue;
+    }
+
+    Nbad = 0;
+    ohana_memregister (line);
+
+    stripwhite (line);
+    if (*line) {
+      status = multicommand (line);
+      add_history (line);
+
+// the libedit version of readline does not support an incremental write to history file
+# ifdef RL_READLINE_VERSION
+      if (history != NULL) append_history (1, history);
+# endif
+
+    }
+    free (line);
+    line = (char *) NULL;
+  }
+}
+
+/* 
+   startup sequence:
+
+   - general_init
+   - program_init
+   - startup (exit if non-interactive)
+   - welcome
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/parse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/parse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/parse.c	(revision 22322)
@@ -0,0 +1,308 @@
+# include "opihi.h"
+
+char *parse (char *line) {
+
+  double fval;
+  float *fptr;
+  char *newline, *N, *L, *val, *B, *V, *V0, *V1, *c1, *c2, *p;
+  int Nx, Ny, Nbytes, status, size, NLINE, isBuffer, inRange;
+  FILE *f;
+  Vector *vec;
+  Buffer *buf;
+
+  val = V0 = NULL;
+
+  if (line == (char *) NULL) goto error;
+
+  /* case 1: $var = expression */
+  if (line[0] == '$') {  
+    /* find the target variable name and the rest of the line */
+    interpolate_slash (line);
+    V0 = thisvar (line);
+    if (V0 == NULL) goto error;
+    V1 = aftervar (line);
+    if (V1 == NULL) goto error;
+ 
+    /* increment operator */
+    if (!strcmp (V1, "++")) {
+      val = get_variable (V0);
+      if (val == NULL) {
+	fval = 1.0;
+      } else {
+	fval = atof (val) + 1.0;
+      }
+      set_variable (V0, fval);
+      goto escape;
+    }
+
+    /* decrement operator */
+    if (!strcmp (V1, "--")) {
+      val = get_variable (V0);
+      if (val == NULL) {
+	fval = -1.0;
+      } else {
+	fval = atof (val) - 1.0;
+      }
+      set_variable (V0, fval);
+      goto escape;
+    }
+
+    /* not an assignement, syntax error */
+    if (*V1 != '=') goto error;
+
+    /* find first non-WHITESPACE character after = */
+    V1 ++;
+    while (isspace (*V1)) V1++;
+    if (*V1 == 0) goto error;
+
+    /* command replacement.  execute the line, place answer in val. */
+    if (*V1 == '`') {
+
+      /* look for end of line, must be ` */
+      B = V1 + strlen (V1) - 1;
+      if (*B != '`') goto error;
+
+      /* val will hold the result */
+      // XXX this is limiting!!!
+      ALLOCATE (val, char, 1024);
+
+      /* B is the command to be executed */
+      B = strncreate (V1 + 1, strlen(V1) - 2);
+      f = popen (B, "r");
+      Nbytes = fread (val, 1, 1023, f);
+      val[Nbytes] = 0;
+      status = pclose (f);
+      free (B);
+
+      if (status) gprint (GP_ERR, "warning: exit status of command %d\n", status);
+
+      /* convert all but last return to ' '.  drop last return */
+      for (B = val; *B != 0; B++) {
+	if (*B == '\n') {
+	  if (B[1]) {
+	    *B = ' ';
+	  } else {
+	    *B = 0;
+	  }
+	}
+      }
+    } 
+
+    /* simple variable assignment */
+    if (*V1 != '`') {
+      /* dvomath returns a new string, or NULL, with the result of the expression */
+      val = dvomath (1, &V1, &size, 0);
+      if (val == NULL) { 
+	while (OHANA_WHITESPACE (*V1)) V1++;
+	val = strcreate (V1);
+      } 
+    }
+    /* both dvomath and command replacement create (char *) val */
+    set_str_variable (V0, val);
+    goto escape;  /* frees temp variables */
+  }
+
+  /* case 2: vect[N] = value or buff[N][M] = value */
+  V0 = thiscomm (line);
+  if (strchr(V0, '[') != (char *) NULL) { 
+    /* get vector name (left in V0) */
+    N = strchr (V0, '[');
+    if (N == V0) goto error;
+
+    /* get bracket contents (left in V) */
+    L = strchr (V0, ']');
+    if (L == (char *) NULL) goto error;
+
+    *N = 0;
+    V = strncreate (N+1, L - N - 1);
+
+    /* expand value in brackets */
+    val = dvomath (1, &V, &size, 0);
+    if (val == NULL) {
+      print_error ();
+      goto error;
+    }
+    Nx = atoi (val);
+    free (val); val = NULL;
+    free (V);
+
+    isBuffer = FALSE;
+    N = L + 1;
+    if (*N == '[') {
+      isBuffer = TRUE;
+      L = strchr (N, ']');
+
+      V = strncreate (N+1, L - N - 1);
+
+      /* expand value in brackets */
+      val = dvomath (1, &V, &size, 0);
+      if (val == NULL) {
+	print_error ();
+	goto error;
+      }
+      Ny = atoi (val);
+      free (val); val = NULL;
+      free (V);
+    }
+
+    /* find value for assignment */
+    V1 = nextcomm (line);
+    if (V1 == (char *) NULL) goto error;
+    if (V1[0] != '=') goto error;
+    V1 ++;
+
+    /*** assign vector element to value ***/
+    val = dvomath (1, &V1, &size, 0); 
+    if (val == (char *) NULL) {
+      print_error ();
+      goto error;
+    }
+
+    /* find vector */
+    if (isBuffer) {
+      if ((buf = SelectBuffer (V0, OLDBUFFER, TRUE)) == NULL) goto error;
+      free (V0); V0 = (char *) NULL;
+
+      inRange = TRUE;
+      inRange &= (Nx <  +1*buf[0].header.Naxis[0]);
+      inRange &= (Nx >= -1*buf[0].header.Naxis[0]);
+      inRange &= (Ny <  +1*buf[0].header.Naxis[1]);
+      inRange &= (Ny >= -1*buf[0].header.Naxis[1]);
+      if (!inRange) {
+	gprint (GP_ERR, "no element %d,%d\n", Nx, Ny);
+	goto escape;
+      }
+      if (Nx < 0) Nx += buf[0].header.Naxis[0];
+      if (Ny < 0) Ny += buf[0].header.Naxis[1];
+
+      fptr = (float *) buf[0].matrix.buffer;
+      fptr[Nx + Ny*buf[0].header.Naxis[0]] = atof (val);
+    } else {
+      if ((vec = SelectVector (V0, OLDVECTOR, TRUE)) == NULL) goto error;
+      free (V0); V0 = (char *) NULL;
+
+      inRange = TRUE;
+      inRange &= (Nx <  +1*vec[0].Nelements);
+      inRange &= (Nx >= -1*vec[0].Nelements);
+      if (!inRange) {
+	gprint (GP_ERR, "no element %d\n", Nx);
+	goto escape;
+      }
+      if (Nx < 0) Nx += vec[0].Nelements;
+      vec[0].elements[Nx] = atof (val);
+    }
+
+    goto escape;
+  }
+  free (V0); V0 = (char *) NULL;
+
+  /* case 3: {expression} */
+  NLINE = MAX (128, strlen (line));
+  ALLOCATE (newline, char, NLINE);
+  memset (newline, 0, NLINE);
+  for (L = line; *L != 0; ) { 
+
+    // copy elements from L (line) to newline up to first '{'
+    p = strchr (L, '{');
+    newline = opihi_append (newline, &NLINE, L, p);
+    if (p == NULL) break;
+    L = p + 1;
+
+    // check for \{ at this point, replace \ in newline with {
+    if ((p > line) && (*(p - 1) == 0x5c)) {
+      N = newline + strlen(newline) - 1;
+      *N = '{'; // replace \ with {
+      continue;
+    }
+
+    /* check on the syntax of the line */
+    L = p + 1;
+    c1 = strchr (L, '}');
+    if (c1 == NULL) {
+      gprint (GP_ERR, "no close brackets!\n");
+      goto escape;
+    }
+    c2 = strchr (L, '{');
+    if ((c2 != NULL) && (c2 < c1)) {
+      gprint (GP_ERR, "can't nest brackets!\n");
+      goto escape;
+    }
+    *c1 = 0;
+
+    /* value in brackets must be a math expression of some sort.
+       isolated words are not valid here */
+    val = dvomath (1, &L, &size, -1);
+    if (val == NULL) {
+      print_error ();
+      goto escape;
+    } 
+
+    /* interpolate vector element into newline (being accumulated) */
+    newline = opihi_append (newline, &NLINE, val, NULL);
+
+    /* copy val to outline */
+    L = c1 + 1;
+    free (val); val = (char *) NULL;
+  }
+
+  free (line); line = (char *) NULL;
+
+  /* \ protects next character */
+  interpolate_slash (newline);
+  
+  REALLOCATE (newline, char, strlen (newline) + 1);
+  return (newline); 
+
+error:
+  gprint (GP_ERR, "syntax error\n");
+
+escape:
+  if (line != (char *) NULL) free (line);
+  if (val != (char *) NULL) free (val);
+  if (V0 != (char *) NULL) free (V0);
+  return (NULL);
+}
+
+/* this routine looks for math and/or logic expressions that 
+   need to be evaluated and passes them on to "parenthesis",
+   which evaluates the expression */
+
+/* There are two situations now which define an expression to 
+   be evaluated:
+   1) $var = expression   -- everything after the = must be a math expression or a string
+   2) {expression}        -- everything within the {} must be a math expression
+*/
+
+/* case 1: $var = expression.  test that
+   a) there is a $ as the first character
+   b) the variable defined by that $ is not null
+   c) there is an = sign following the variable
+   d) there is something following the = sign
+*/
+  
+/* case 2: vect[N] = expression. check syntax:
+   a) last char must be ]
+   b) word must contain [
+   c) between [ & ] must be an integer
+   d) vector must exist
+   e) there is an = sign following the first word
+   f) there is something following the = sign
+*/
+     
+
+/* case 3: we hunt for '{', and then the first '}' and pass everything
+   inbetween.  no nested {{}}s are allowed! */
+  
+/* thisword ALLOCATES
+   thisvar  ALLOCATES
+   thiscomm ALLOCATES
+   
+   nextword !ALLOCATE
+   nextcomm !ALLOCATE
+   
+   lastword !ALLOCATE
+   lastvar !ALLOCATE
+   
+   aftervar !ALLOCATE
+*/   
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/parse_commands.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/parse_commands.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/parse_commands.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "opihi.h"
+# define D_NARG 10
+
+char **parse_commands (char *line, int *argc) {
+
+  int i, NARG;
+  char *c;
+  char **argv;
+
+  NARG = D_NARG;
+  ALLOCATE (argv, char *, NARG);
+
+  argv[0] = thisword (line);
+  if (argv[0] == (char *) NULL) {
+    free (argv);
+    *argc = 0;
+    return ((char **) NULL);
+  }
+
+  c = nextword (line);
+  for (i = 1; c != (char *) NULL; i++) {
+    argv[i] = thisword (c);
+
+    /* if one of the words does not parse (eg, ""), skip it */
+    if (argv[i] == NULL) i--;
+
+    c = nextword (c);
+    if (i == NARG - 1) {
+      NARG += D_NARG;
+      REALLOCATE (argv, char *, NARG);
+    }
+  }
+  REALLOCATE (argv, char *, i);
+  *argc = i;
+
+  return (argv);
+
+}
+
+  /* parse out the command line into (char **argv).  line looks like:
+     command word word word ... */
+
+  /* argv[0] and argv[1..argc-1] are handled differently because
+     the syntax of a valid command and a valid word are slighly 
+     different (not that this is obviously wanted...) */
+
+
+
+/* old code:
+      for (j = 0; j < i; j++) 
+	free (argv[i]);
+      free (argv);
+      *argc = 0;
+      return (argv);
+    }
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/stack_math.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/stack_math.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/stack_math.c	(revision 22322)
@@ -0,0 +1,1454 @@
+# include "opihi.h"
+
+/* the result of a matrix operation must go into a temp variable,
+   labeled by "m". if one of the input matrices is already a temp variable, we 
+   can continue using it this round 
+   */
+
+int VV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  float *out, *M1, *M2;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V1[0].vector[0].Nelements;
+  if (Nx != V2[0].vector[0].Nelements) {
+    return (FALSE);
+  }
+
+  if (V1[0].type == 'v') {  /** use V1 as temp buffer **/
+    OUT[0].vector = V1[0].vector;
+    V1[0].type = 'V'; /* prevent it from being freed below */
+  } else {
+    if (V2[0].type == 'v') { /** use V2 as temp buffer, but header of V1 **/
+      OUT[0].vector = V2[0].vector;
+      V2[0].type = 'V'; /* prevent it from being freed below */
+    } else {  /* no spare temp buffer */
+      OUT[0].vector = InitVector ();
+      CopyVector (OUT[0].vector, V1[0].vector);
+    }
+  }
+  OUT[0].type = 'v'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].vector[0].elements;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements);
+    free (V1[0].vector);
+  }
+  if (V2[0].type == 'v') {
+    free (V2[0].vector[0].elements);
+    free (V2[0].vector);
+  }
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+
+  clear_stack (V1);
+  clear_stack (V2);
+  return (TRUE);
+
+}
+
+int SV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  float *out, *M1, *M2;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V2[0].vector[0].Nelements;
+
+  if (V2[0].type == 'v') { /** use V2 as temp buffer, but header of V1 **/
+    OUT[0].vector = V2[0].vector;
+    V2[0].type = 'V'; /* prevent it from being freed below */
+  } else {  /* no spare temp buffer */
+    OUT[0].vector = InitVector ();
+    CopyVector (OUT[0].vector, V2[0].vector);
+  }
+  OUT[0].type = 'v';   /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].vector[0].elements;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx; i++, out++, M2++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx; i++, out++, M2++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx; i++, out++, M2++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx; i++, out++, M2++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx; i++, out++, M2++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+  if (V2[0].type == 'v') {
+    free (V2[0].vector[0].elements);
+    free (V2[0].vector);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+int VS_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  float *out, *M1, *M2;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V1[0].vector[0].Nelements;
+
+  if (V1[0].type == 'v') { /** use V1 as temp buffer **/
+    OUT[0].vector = V1[0].vector;
+    V1[0].type = 'V'; /* prevent it from being freed below */
+  } else {  /* no spare temp buffer */
+    OUT[0].vector = InitVector ();
+    CopyVector (OUT[0].vector, V1[0].vector);
+  }
+  OUT[0].type = 'v';   /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].vector[0].elements;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx; i++, out++, M1++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx; i++, out++, M1++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx; i++, out++, M1++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx; i++, out++, M1++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx; i++, out++, M1++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements);
+    free (V1[0].vector);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+int MV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, j, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512]; // this is only used to report an error 
+ 
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+  if (Ny != V2[0].vector[0].Nelements) {
+    push_error ("dimension mismatch");
+    return (FALSE);
+  }
+
+  /* if possible, use V1 as temp buffer, otherwise create new one */
+  if (V1[0].type == 'm') {  
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {  
+    /* do buffer.matrix.buffer and buffer.header.buffer get correctly zeroed? */
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V1[0].buffer);
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++)
+        *out = *M1 + *M2;
+    }
+    break; 
+  case '-': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++)
+        *out = *M1 - *M2;
+    }
+    break; 
+  case '*': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++)
+        *out = *M1 * *M2;
+    }
+    break; 
+  case '/': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++)
+        *out = *M1 / *M2;
+    }
+    break; 
+  case '%': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++)
+        *out = (int) *M1 % (int) *M2;
+    }
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = pow (*M1, *M2);
+    }
+    break; 
+  case '@': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = DEG_RAD*atan2 (*M1, *M2);
+    }
+    break; 
+  case 'D': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = MIN (*M1, *M2);
+    }
+    break; 
+  case 'U': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = MAX (*M1, *M2);
+    }
+    break; 
+  case '<': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 < *M2) ? 1 : 0;
+    }
+    break; 
+  case '>': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 > *M2) ? 1 : 0;
+    }
+    break; 
+  case '&': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = ((int)*M1 & (int)*M2);
+    }
+    break; 
+  case '|': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = ((int)*M1 | (int)*M2);
+    }
+    break; 
+  case 'E': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 == *M2) ? 1 : 0;
+    }
+    break; 
+  case 'N': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 != *M2) ? 1 : 0;
+    }
+    break; 
+  case 'L': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 <= *M2) ? 1 : 0;
+    }
+    break; 
+  case 'G': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 >= *M2) ? 1 : 0;
+    }
+    break; 
+  case 'A': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 && *M2) ? 1 : 0;
+    }
+    break; 
+  case 'O': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 || *M2) ? 1 : 0;
+    }
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  if (V2[0].type == 'v') {
+    free (V2[0].vector[0].elements);
+    free (V2[0].vector);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+
+int VM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, j, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V2[0].buffer[0].matrix.Naxis[0];
+  Ny = V2[0].buffer[0].matrix.Naxis[1];
+  if (Nx != V1[0].vector[0].Nelements) {
+    return (FALSE);
+  }
+
+  /* if possible, use V2 as temp buffer, otherwise create new one */
+  if (V2[0].type == 'm') {
+    OUT[0].buffer = V2[0].buffer;
+    V2[0].type = 'M'; /* prevent it from being freed below */
+  } else {  /* no spare temp buffer */
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V2[0].buffer);
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++)
+        *out = *M1 + *M2;
+    }
+    break; 
+  case '-': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++)
+        *out = *M1 - *M2;
+    }
+    break; 
+  case '*': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++)
+        *out = *M1 * *M2;
+    }
+    break; 
+  case '/': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++)
+        *out = *M1 / *M2;
+    }
+    break; 
+  case '%': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++)
+        *out = (int) *M1 % (int) *M2;
+    }
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = pow (*M1, *M2);
+    }
+    break; 
+  case '@': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = DEG_RAD*atan2 (*M1, *M2);
+    }
+    break; 
+  case 'D': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = MIN (*M1, *M2);
+    }
+    break; 
+  case 'U': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = MAX (*M1, *M2);
+    }
+    break; 
+  case '<': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 < *M2) ? 1 : 0;
+    }
+    break; 
+  case '>': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 > *M2) ? 1 : 0;
+    }
+    break; 
+  case '&': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = ((int)*M1 & (int)*M2);
+    }
+    break; 
+  case '|': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = ((int)*M1 | (int)*M2);
+    }
+    break; 
+  case 'E': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 == *M2) ? 1 : 0;
+    }
+    break; 
+  case 'N': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 != *M2) ? 1 : 0;
+    }
+    break; 
+  case 'L': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 <= *M2) ? 1 : 0;
+    }
+    break; 
+  case 'G': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 >= *M2) ? 1 : 0;
+    }
+    break; 
+  case 'A': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 && *M2) ? 1 : 0;
+    }
+    break; 
+  case 'O': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 || *M2) ? 1 : 0;
+    }
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements);
+    free (V1[0].vector);
+  }
+  if (V2[0].type == 'm') {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+int MM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  if (V1[0].type == 'm') {  /** use V1 as temp buffer **/
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    if (V2[0].type == 'm') { /** use V2 as temp buffer, but header of V1 **/
+      OUT[0].buffer = V2[0].buffer;
+      V2[0].type = 'M'; /* prevent it from being freed below */
+    } else {  /* no spare temp buffer */
+      OUT[0].buffer = InitBuffer ();
+      CopyBuffer (OUT[0].buffer, V1[0].buffer);
+    }
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  if (V2[0].type == 'm') {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+
+int MS_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  /* if possible, use V1 as temp buffer, otherwise create new one */
+  if (V1[0].type == 'm') {
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V1[0].buffer);
+  }
+  OUT[0].type = 'm';      /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  clear_stack (V1);
+  clear_stack (V2);
+
+  return (TRUE);
+
+}
+
+
+int SM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V2[0].buffer[0].matrix.Naxis[0];
+  Ny = V2[0].buffer[0].matrix.Naxis[1];
+
+  if (V2[0].type == 'm') {  /* V2[0] is NOT temporary, we can't use it for storage */
+    OUT[0].buffer = V2[0].buffer;
+    V2[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V2[0].buffer);
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  if (V2[0].type == 'm') {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+  clear_stack (V1);
+  clear_stack (V2);
+
+  return (TRUE);
+
+}
+
+
+int SS_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  float *M1, *M2, *out;
+  char line[512]; // this is only used to report an error 
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  OUT[0].ptr = V1[0].ptr;
+  out = OUT[0].ptr;
+
+
+  switch (op[0]) { 
+  case '+': 
+    *out = *M1 + *M2;
+    break;    
+  case '-': 
+    *out = *M1 - *M2;
+    break;    
+  case '*': 
+    *out = *M1 * *M2;
+    break;    
+  case '/': 
+    *out = *M1 / *M2;
+    break; 
+  case '%': 
+    *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+  OUT[0].Float = *(OUT[0].ptr);
+  OUT[0].type = 'S';
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  return (TRUE);
+
+}
+
+int WW_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int value;
+  char line[512]; // this is only used to report an error 
+
+  /* only 'N' and 'E' are allowed for WW_binary operations. anything else is either a
+   syntax error or is a string which looks like a math expression. */
+
+  if ((op[0] != 'N') && (op[0] != 'E')) {
+    sprintf (line, "error: op %c not defined for string operations!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /* evaluate stack will only call WW_binary with one numerical value,
+     and only in the case that the string did not parse to a number 
+     thus: string == number -> false */
+
+  if (!strncasecmp (&V1[0].type, "S", 1)) {
+    value = (op[0] == 'N');
+    goto escape;
+  }
+  if (!strncasecmp (&V2[0].type, "S", 1)) {
+    value = (op[0] == 'N');
+    goto escape;
+  }
+
+  switch (op[0]) { 
+  case 'E': 
+    value = strcmp (V1[0].name, V2[0].name) ? 0 : 1;
+    break; 
+  case 'N': 
+    value = strcmp (V1[0].name, V2[0].name) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined for string operations!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+escape:
+  OUT[0].Float = value;
+  OUT[0].type = 'S';
+  OUT[0].ptr = &OUT[0].Float;
+
+  clear_stack (V1);
+  clear_stack (V2);
+  return (TRUE);
+
+}
+
+
+int S_unary (StackVar *OUT, StackVar *V1, char *op) {
+
+  float *out, *M1;
+  
+  out = OUT[0].ptr = V1[0].ptr;
+  M1  = V1[0].ptr;
+
+  if (!strcmp (op, "="))     {    }
+  if (!strcmp (op, "abs"))   {    *out = fabs(*M1);           }
+  if (!strcmp (op, "int"))   {    *out = (float)(int)(*M1);   }
+  if (!strcmp (op, "exp"))   {    *out = exp (*M1);           }
+  if (!strcmp (op, "ten"))   {    *out = pow (10.0,*M1);      }
+  if (!strcmp (op, "log"))   {    *out = log10 (*M1);         }
+  if (!strcmp (op, "ln"))    {    *out = log (*M1);           }
+  if (!strcmp (op, "sqrt"))  {    *out = sqrt (*M1);          }
+  if (!strcmp (op, "erf"))   {    *out = erf (*M1);           }
+
+  if (!strcmp (op, "sinh"))  {    *out = sinh (*M1);          }
+  if (!strcmp (op, "cosh"))  {    *out = cosh (*M1);          }
+  if (!strcmp (op, "asinh")) {    *out = asinh (*M1);         }
+  if (!strcmp (op, "acosh")) {    *out = acosh (*M1);         }
+  if (!strcmp (op, "lgamma")) {   *out = lgamma (*M1);        }
+
+  if (!strcmp (op, "sin"))   {    *out = sin (*M1);           }
+  if (!strcmp (op, "cos"))   {    *out = cos (*M1);           }
+  if (!strcmp (op, "tan"))   {    *out = tan (*M1);           }
+  if (!strcmp (op, "dsin"))  {    *out = sin (*M1*RAD_DEG);   }
+  if (!strcmp (op, "dcos"))  {    *out = cos (*M1*RAD_DEG);   }
+  if (!strcmp (op, "dtan"))  {    *out = tan (*M1*RAD_DEG);   }
+  if (!strcmp (op, "asin"))  {    *out = asin (*M1);          }
+  if (!strcmp (op, "acos"))  {    *out = acos (*M1);          }
+  if (!strcmp (op, "atan"))  {    *out = atan (*M1);          }
+  if (!strcmp (op, "dasin")) {    *out = asin (*M1)*DEG_RAD;  }
+  if (!strcmp (op, "dacos")) {    *out = acos (*M1)*DEG_RAD;  }
+  if (!strcmp (op, "datan")) {    *out = atan (*M1)*DEG_RAD;  }
+  if (!strcmp (op, "rnd"))   {    *out = drand48();           }
+  if (!strcmp (op, "not"))   {    *out = !(*M1);              }
+  if (!strcmp (op, "--"))    {    *out = - (*M1);             }
+  if (!strcmp (op, "isinf")) {    *out = !finite(*M1);        }
+  if (!strcmp (op, "isnan")) {    *out = isnan(*M1);          } 
+
+  OUT[0].Float = *out;
+  OUT[0].type = 'S';
+
+  clear_stack (V1);
+  return (TRUE);
+
+}
+
+int V_unary (StackVar *OUT, StackVar *V1, char *op) {
+
+  int i, Nx;
+  float *out, *M1;
+  
+  Nx = V1[0].vector[0].Nelements;
+ 
+  if (V1[0].type == 'v') {  /** use V1 as temp buffer **/
+    OUT[0].vector = V1[0].vector;
+    V1[0].type = 'V'; /* prevent it from being freed below */
+  } else {  /* no spare temp buffer */
+    OUT[0].vector = InitVector ();
+    CopyVector (OUT[0].vector, V1[0].vector);
+  }
+  OUT[0].type = 'v'; /*** <<--- says this is a temporary matrix ***/
+  M1  = V1[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].vector[0].elements;
+
+  if (!strcmp (op, "="))     { } /* already set equal */
+  if (!strcmp (op, "abs"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = fabs(*M1);         }}
+  if (!strcmp (op, "int"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = (float)(int)(*M1); }}
+  if (!strcmp (op, "exp"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = exp(*M1);          }}
+  if (!strcmp (op, "ten"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = pow(10.0,*M1);     }}
+  if (!strcmp (op, "log"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = log10(*M1);        }}
+  if (!strcmp (op, "ln"))    { for (i = 0; i < Nx; i++, out++, M1++) { *out = log(*M1);          }}
+  if (!strcmp (op, "sqrt"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = sqrt(*M1);         }}
+  if (!strcmp (op, "erf"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = erf(*M1);          }}
+
+  if (!strcmp (op, "sinh"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = sinh(*M1);        }}
+  if (!strcmp (op, "cosh"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = cosh(*M1);        }}
+  if (!strcmp (op, "asinh"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = asinh(*M1);       }}
+  if (!strcmp (op, "acosh"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = acosh(*M1);       }}
+  if (!strcmp (op, "lgamma")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = lgamma(*M1);      }}
+
+  if (!strcmp (op, "sin"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = sin(*M1);          }}
+  if (!strcmp (op, "cos"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = cos(*M1);          }}
+  if (!strcmp (op, "tan"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = tan(*M1);          }}
+  if (!strcmp (op, "dsin"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = sin(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "dcos"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = cos(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "dtan"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = tan(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "asin"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = asin(*M1);         }}
+  if (!strcmp (op, "acos"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = acos(*M1);         }}
+  if (!strcmp (op, "atan"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = atan(*M1);         }}
+  if (!strcmp (op, "dasin")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = asin(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "dacos")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = acos(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "datan")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = atan(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "rnd"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = drand48();         }}
+  if (!strcmp (op, "ramp"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = i;                 }}
+  if (!strcmp (op, "zero"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = 0;                 }}
+  if (!strcmp (op, "not"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = !(*M1);            }}
+  if (!strcmp (op, "--"))    { for (i = 0; i < Nx; i++, out++, M1++) { *out = -(*M1);            }}
+  if (!strcmp (op, "isinf")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = !finite(*M1);      }}
+  if (!strcmp (op, "isnan")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = isnan(*M1);        }}
+
+  /* xramp and yramp only make sense in for matrices. for vectors, xramp = ramp, yramp = zero */
+  if (!strcmp (op, "xramp")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = i;                 }}
+  if (!strcmp (op, "yramp")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = 0;                 }}
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements);
+    free (V1[0].vector);
+    V1[0].vector = NULL;
+  }  
+
+  clear_stack (V1);
+  return (TRUE);
+
+}
+
+int M_unary (StackVar *OUT, StackVar *V1, char *op) {
+
+  int i, j, Nx, Ny;
+  float *out, *M1;
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  if (V1[0].type == 'm') {
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V1[0].buffer);
+  }
+  OUT[0].type = 'm';      /*** <<--- says this is a temporary matrix ***/
+  M1  = V1[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  if (!strcmp (op, "="))     { }
+  if (!strcmp (op, "abs"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = fabs(*M1);         }}
+  if (!strcmp (op, "int"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = (float)(int)(*M1); }}
+  if (!strcmp (op, "exp"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = exp(*M1);          }}
+  if (!strcmp (op, "ten"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = pow(10.0,*M1);     }}
+  if (!strcmp (op, "log"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = log10(*M1);        }}
+  if (!strcmp (op, "ln"))    { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = log(*M1);          }}
+  if (!strcmp (op, "sqrt"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sqrt(*M1);         }}
+  if (!strcmp (op, "erf"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = erf(*M1);          }}
+
+  if (!strcmp (op, "sinh"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sinh(*M1);         }}
+  if (!strcmp (op, "cosh"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = cosh(*M1);         }}
+  if (!strcmp (op, "asinh")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = asinh(*M1);        }}
+  if (!strcmp (op, "acosh")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = acosh(*M1);        }}
+  if (!strcmp (op, "lgamma")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = lgamma(*M1);      }}
+
+  if (!strcmp (op, "sin"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sin(*M1);          }}
+  if (!strcmp (op, "cos"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = cos(*M1);          }}
+  if (!strcmp (op, "tan"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = tan(*M1);          }}
+  if (!strcmp (op, "dsin"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sin(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "dcos"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = cos(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "dtan"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = tan(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "asin"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = asin(*M1);         }}
+  if (!strcmp (op, "acos"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = acos(*M1);         }}
+  if (!strcmp (op, "atan"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = atan(*M1);         }}
+  if (!strcmp (op, "dasin")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = asin(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "dacos")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = acos(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "datan")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = atan(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "not"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = !(*M1);            }}
+  if (!strcmp (op, "--"))    { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = -(*M1);            }}
+  if (!strcmp (op, "rnd"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = drand48();         }}
+  if (!strcmp (op, "ramp"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = i;                 }}
+  if (!strcmp (op, "zero"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = 0;                 }}
+  if (!strcmp (op, "isinf")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = !finite(*M1);      }}
+  if (!strcmp (op, "isnan")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = isnan(*M1);        }}
+
+  /* xrm and yrm only make sense in for matrices. see special meaning for vectors */
+  if (!strcmp (op, "xramp")) {
+    for (j = 0; j < Ny; j++) {
+      for (i = 0; i < Nx; i++, out++, M1++) {
+        *out = i;
+      }
+    }
+  }
+  if (!strcmp (op, "yramp")) {
+    for (j = 0; j < Ny; j++) {
+      for (i = 0; i < Nx; i++, out++, M1++) {
+        *out = j;
+      }
+    }
+  }
+  
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+
+  clear_stack (V1);
+  return (TRUE);
+
+}
+
+/*********************** fits copy header ***********************************/
+int gfits_copy_matrix_info (Matrix *matrix1, Matrix *matrix2) {
+
+  int i;
+
+  /* copy all but the matrix */
+
+  matrix2[0].unsign = matrix1[0].unsign;
+  matrix2[0].bitpix = matrix1[0].bitpix;
+  matrix2[0].size   = matrix1[0].size;
+  matrix2[0].bzero  = matrix1[0].bzero;
+  matrix2[0].bscale = matrix1[0].bscale;
+  matrix2[0].Naxes  = matrix1[0].Naxes;
+  for (i = 0; i < FT_MAX_NAXES; i++) 
+    matrix2[0].Naxis[i] = matrix1[0].Naxis[i];
+
+
+  return (TRUE);
+}       
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/startup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/startup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/startup.c	(revision 22322)
@@ -0,0 +1,160 @@
+# include "opihi.h"
+
+/* program-independent initialization
+ * these steps do not depend on the opihi implementation
+ * (ie, the supplied commands or data structures)
+ */
+
+void general_init (int *argc, char **argv) {
+
+  /* init srand for rnd numbers elsewhere */
+  long A, B;
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  /* set signals */
+  signal (SIGINT, SIG_IGN);
+
+  /* init for main (or only) thread */
+  gprintInit ();
+
+  init_error ();
+
+  /* load config data (.ptolemyrc) */
+  if (!ConfigInit (argc, argv)) {
+    gprint (GP_ERR, "can't find config file. some functions will be unavailable\n");
+  }
+
+  return;
+}
+
+void startup (int *argc, char **argv) {
+    
+    int i, N, status, ONLY_INPUT, LOAD_RC;
+    char *line, *home, *outline, *varname;
+    char *rcfile, **list;
+    int Nlist, NLIST;
+   
+    /* load in interesting environment variables */
+    ALLOCATE (line, char, 1024);
+    
+    home = getenv ("HOME");
+    if (home == NULL) 
+      set_str_variable ("HOME", ".");
+    else 
+      set_str_variable ("HOME", home);
+    set_str_variable ("KAPA", "kapa");
+    set_variable ("PID", getpid());
+
+    set_int_variable ("UNSIGN", 0);
+    gfits_set_unsign_mode (FALSE);
+    
+  /* check history file permission */
+  {
+    FILE *f;
+    char *opihi_history;
+
+    opihi_history = get_variable ("HISTORY");
+    if (opihi_history && *opihi_history) {
+      f = fopen (opihi_history, "a");
+      if (f == NULL) /* no current history file here */
+	gprint (GP_ERR, "can't save history.\n");
+      else
+	fclose (f);
+      stifle_history (200);
+      read_history (opihi_history);
+    }
+    if (opihi_history) free (opihi_history);
+  }
+
+  if (0) {
+    /* fix the history list to remove the timestamp */
+    char *c;
+    int i;
+    HIST_ENTRY **entry;
+    
+    entry = history_list ();
+    if (entry != (HIST_ENTRY **) NULL) {
+      for (i = 0; entry[i]; i++) {
+	if ((strlen (entry[i][0].line) > 19) &&
+	    (entry[i][0].line[2] == '/') && 
+	    (entry[i][0].line[5] == '/') && 
+	    (entry[i][0].line[11] == ':') && 
+	    (entry[i][0].line[14] == ':') && 
+	    (entry[i][0].line[17] == ':')) {
+	  c = entry[i][0].line + 19;
+	  memmove (entry[i][0].line, c, strlen(c) + 1);
+	}
+      }
+    }
+  }
+
+    LOAD_RC = TRUE;
+    if ((N = get_argument (*argc, argv, "--norc"))) {
+      remove_argument (N, argc, argv);
+      LOAD_RC = FALSE;
+    }
+    
+    ONLY_INPUT = FALSE;
+    if ((N = get_argument (*argc, argv, "--only"))) {
+      remove_argument (N, argc, argv);
+      ONLY_INPUT = TRUE;
+    }
+
+    /* load cmdline files */
+    Nlist = 0;
+    NLIST = 10;
+    ALLOCATE (list, char *, NLIST);
+    while ((N = get_argument (*argc, argv, "--load"))) {
+      remove_argument (N, argc, argv);
+      list[Nlist] = strcreate (argv[N]);
+      remove_argument (N, argc, argv);
+      Nlist++;
+      if (Nlist == NLIST) {
+	NLIST += 10;
+	REALLOCATE (list, char *, NLIST);
+      }
+    }
+
+    is_script = TRUE;
+    if (*argc == 1) is_script = FALSE;
+    set_int_variable ("SCRIPT", is_script);
+
+    if (LOAD_RC && !is_script) {
+      rcfile = get_variable ("RCFILE");
+      sprintf (line, "input %s/%s", home, rcfile);
+      status = command (line, &outline, TRUE);
+      if (outline != (char *) NULL) free (outline);
+      if (rcfile) free (rcfile);
+    }
+      
+    set_int_variable ("argv:n", 0);
+    if (is_script) {
+      /* first argument in input script, rest are argv */
+      list[Nlist] = strcreate (argv[1]);
+      Nlist ++;
+      /* generate list argv:0 - argv:n from arguments */
+      ALLOCATE (varname, char, 256);
+      for (i = 2; i < *argc; i++) {
+	sprintf (varname, "argv:%d", i - 2);
+	set_str_variable (varname, argv[i]);
+      }
+      set_int_variable ("argv:n", i - 2);
+      free (varname);
+    }
+
+    /* execute command-line entries */
+    for (i = 0; i < Nlist; i++) {
+      ALLOCATE (line, char, 1024);
+      sprintf (line, "input %s", list[i]);
+      status = command (line, &outline, TRUE);
+      if (outline != (char *) NULL) free (outline);
+    }
+    free (list);
+
+    /* if this is not an interactive session, exit here */
+    if (ONLY_INPUT) exit (40);
+    if (is_script) exit (41);
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/string.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/string.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/string.c	(revision 22322)
@@ -0,0 +1,289 @@
+# include "opihi.h"
+
+/**********************************************************************/
+/* returns a pointer to an isolated string containing the first word,
+   removing leading WHITESPACE.  A "word" is a contiguous set of 
+   characters from the set: alphanumerics, and any of: / . _ -
+   Any other single, non-WHITESPACE characters are considered to be 
+   complete words in themselves.  Any characters surrounded by quotes 
+   make a single word 
+*/
+
+char *thisword (char *string) {
+
+  int i, j, N;
+  char *word;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (i = 0; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *)NULL);
+  if (string[i] == ';') {
+    word = strncreate (&string[i], 1);
+    return (word);
+  }
+
+  /* a string of characters contained within double quotes is 
+      a single entry.  check that there are pairs of quotes,
+      return only the string between the quotes */
+  if (string[i] == '"') { 
+    i++;
+    if (string[i] == 0) return ((char *) NULL);
+    for (j = i; (string[j] != 0) && (string[j] != '"'); j++);
+    if (string[j] == 0) {
+      gprint (GP_ERR, "misbalanced quotes\n");
+      return ((char *)NULL);
+    }
+    //    for (; (string[j] != 0) && (!OHANA_WHITESPACE(string[j])); j++);
+    word = strncreate (&string[i], j - i);
+    return (word);
+  } 
+
+  /* a string of characters contained within parentheses is 
+      a single entry.  check that there are matched pairs of
+      parentheses, return string, including exterior parentheses */
+  if (string[i] == '(') { 
+    i++;
+    N = 1;
+    if (string[i] == 0) return ((char *) NULL);
+    for (j = i; (string[j] != 0) && (N > 0); j++) {
+      if (string[j] == '(') {
+	N++;
+      }
+      if (string[j] == ')') {
+	N--;
+      }
+    }
+    if ((string[j] == 0) && (N != 0)) {
+      gprint (GP_ERR, "misbalanced parenthesis\n");
+      return ((char *)NULL);
+    }
+    word = strncreate (&string[i-1], j - i + 1);
+    return (word);
+  } 
+
+
+  for (j = i; (string[j] != 0) && (string[j] != ';') && !OHANA_WHITESPACE(string[j]); j++);
+  word = strncreate (&string[i], j - i);
+  return (word);
+
+}
+
+/* returns a pointer to an isolated string containing the first command,
+   removing leading WHITESPACE.  A command ends with the first non WHITESPACE */
+char *thiscomm (char *string) {
+
+  int i, j;
+  char *word;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (i = 0; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *)NULL);
+
+  for (j = i; ((string[j] != 0) && !OHANA_WHITESPACE (string[j])); j++);
+  if (i == j) return ((char *) NULL);
+
+  word = strncreate (&string[i], j - i);
+  return (word);
+
+}
+
+/* take a pointer to the beginning of a variable (ie $foo)
+and extract only the variable name (eg, foo) */
+
+char *thisvar (char *string) {
+
+  int i, start;
+  char *word;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (string[0] != '$') return ((char *) NULL);
+
+  /* special case $?name : check that name is valid */
+  start = 1;
+  if (string[1] == '?') start = 2;
+
+  for (i = start; ISVAR(string[i]); i++);
+  if (i == start) return ((char *) NULL);
+
+  /* the ? is part of the variable */
+  word = strncreate (&string[1], i - 1);
+  return (word);
+
+}
+
+
+/**********************************************************************/
+/* returns a pointer to the next word, or (char *) NULL if there is not a next word */
+char *nextword (char *string) {
+
+  int i, j;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (i = 0; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *)NULL);
+
+  if (string[i] == '"') { 
+    i++;
+    if (string[i] == 0) return ((char *) NULL);
+    for (; (string[i] != 0) && (string[i] != '"'); i++);
+    if (string[i] == 0) {
+      gprint (GP_ERR, "misbalanced quotes\n");
+      return ((char *)NULL);
+    }
+    i++;
+    for (; (string[i] != 0) && OHANA_WHITESPACE (string[i]); i++);
+    if (string[i] == 0) return ((char *) NULL);
+    return (&string[i]);
+  } 
+
+  if (string[i] == '(') { 
+    i++; 
+    j = 1;
+    if (string[i] == 0) return ((char *) NULL);
+    for (; (string[i] != 0) && (j > 0); i++) {
+      if (string[i] == '(') {
+	j++;
+      }
+      if (string[i] == ')') {
+	j--;
+      }
+    }
+    if ((string[i] == 0) && (j != 0)) {
+      gprint (GP_ERR, "misbalanced parenthesis\n");
+      return ((char *)NULL);
+    }
+    for (; (string[i] != 0) && OHANA_WHITESPACE (string[i]); i++);
+    if (string[i] == 0) return ((char *) NULL);
+    return (&string[i]);
+  } 
+
+  if (string[i] == ';') i++;
+  for (; (string[i] != 0) && (string[i] != ';') && !OHANA_WHITESPACE(string[i]); i++);
+  for (; (string[i] != 0) && OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *) NULL);
+
+  return (&string[i]);
+}
+
+/* returns a pointer to the next command, or (char *) NULL 
+   if there is not a next command.  A command is bounded by WHITESPACE */
+char *nextcomm (char *string) {
+
+  int i;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  
+  for (i = 0; (string[i] != 0) && !OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *) NULL);
+  
+  for (; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *) NULL);
+
+  return (&string[i]);
+}
+
+/* returns a pointer to the previous word,
+   or (char *) NULL if there is not a previous word
+*/
+char *lastword (char *string, char *c) {
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (c == (char *) NULL) return ((char *) NULL);
+
+  for (; !OHANA_WHITESPACE(*c) && (c >= string); c--);
+  if (c < string) return ((char *)NULL);
+
+  for (; OHANA_WHITESPACE(*c) && (c >= string); c--);
+  if (c < string)
+    return ((char *)NULL);
+  for (; !OHANA_WHITESPACE(*c) && (c >= string); c--);
+  c++;
+  return (c);
+}
+
+
+
+/* take a pointer to the beginning of a variable (ie $fred) and return
+   a pointer to the next thing (non WHITESPACE) which is not part of the
+   variable extract only the variable name */
+
+char *aftervar (char *string) {
+
+  int i, j, start;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (string[0] != '$') return ((char *) NULL);
+
+  /* special case: $?name : test only name */
+  start = 1;
+  if (string[1] == '?') start = 2;
+
+  for (i = start; ISVAR(string[i]); i++);
+  if (i == start) return ((char *) NULL);
+
+  for (j = i; OHANA_WHITESPACE (string[j]); j++);
+  if (string[j] == 0) return ((char *)NULL);
+
+  return (&string[j]);
+
+}
+
+
+/* returns a pointer to the previous var, 
+   or (char *) NULL if there is not a previous word
+*/
+char *lastvar (char *string, char *c) {
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (c == (char *) NULL) return ((char *) NULL);
+
+  for (; (c >= string) && OHANA_WHITESPACE(*c); c--);
+  if (c < string) return ((char *)NULL);
+
+  for (; (c >= string) && ISVAR(*c) ; c--);
+  if ((c < string) || (*c != '$')) return ((char *)NULL);
+
+  return (c);
+}
+
+// append string defined by range start - stop to the end of output, which
+// currently has an allocated size of Noutput.  if this operation would overshoot Noutput, 
+// output is reallocated to a sufficiently large size
+char *opihi_append (char *output, int *Noutput, char *start, char *stop) {
+
+  int N1, N2, outlen;
+
+  // a NULL end pointer means 'go to end of line'
+  if (stop == NULL) {
+    stop = start + strlen(start);
+  }
+
+  // enough space?
+  N1 = strlen(output);
+  N2 = stop - start;
+  outlen = N1 + N2;
+  if (outlen >= *Noutput) {
+    *Noutput = outlen + 128;
+    REALLOCATE (output, char, *Noutput);
+    memset (&output[N1], 0, N2 + 128);
+  }
+
+  strncat (output, start, stop - start);
+  return output;
+}
+
+/* replace all instances of \A with A in line */
+void interpolate_slash (char *line) {
+
+  char *in, *out;
+
+  for (in = out = line; *in != 0; in++, out++) {
+    if (*in == 0x5c) in++;
+    *out = *in;
+    if (*in == 0) return;
+  }
+  *out = *in;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/timeformat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/timeformat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/timeformat.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "opihi.h"
+
+int GetTimeFormat (time_t *TimeReference, int *TimeFormat) {
+
+  char *p;
+
+  *TimeReference = 0;
+  if ((p = get_variable ("TIMEREF")) != (char *) NULL) {
+    if (!ohana_str_to_time (p, TimeReference)) {
+      gprint (GP_ERR, "error in TIME_REF format\n");
+      return (FALSE);
+    }
+    free (p);
+  }
+  *TimeFormat = FALSE;
+  if ((p = get_variable ("TIMEFORMAT")) != (char *) NULL) {
+    if (!strcasecmp (p, "JD")) *TimeFormat     = TIME_JD;
+    if (!strcasecmp (p, "MJD")) *TimeFormat    = TIME_MJD;
+    if (!strcasecmp (p, "date")) *TimeFormat   = TIME_DATE;
+    if (!strcasecmp (p, "days")) *TimeFormat   = TIME_DAYS;
+    if (!strcasecmp (p, "hours")) *TimeFormat  = TIME_HOURS;
+    if (!strcasecmp (p, "min")) *TimeFormat    = TIME_MINUTES;
+    if (!strcasecmp (p, "sec")) *TimeFormat    = TIME_SECONDS;
+    if (!*TimeFormat) gprint (GP_ERR, "unknown TIME_FORMAT\n");
+    free (p);
+    return (FALSE);
+  }
+  if (!*TimeFormat) *TimeFormat = TIME_SECONDS;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/lib.shell/version.c	(revision 22322)
@@ -0,0 +1,6 @@
+# include "shell.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+char *opihi_version () {
+  return (name);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/Makefile	(revision 22322)
@@ -0,0 +1,49 @@
+default: mana
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/mana
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+DATA    =       $(DESTDATA)/mana
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata 
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# mana user commands and support functions ########################
+funcs = \
+$(SRC)/init.$(ARCH).o \
+$(SRC)/mana.$(ARCH).o \
+$(SRC)/findrowpeaks.$(ARCH).o 
+
+cmds = \
+$(SRC)/rawstars.$(ARCH).o \
+$(SRC)/fitcontour.$(ARCH).o \
+$(SRC)/starcontour.$(ARCH).o \
+$(SRC)/version.$(ARCH).o \
+$(SRC)/findpeaks.$(ARCH).o 
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a
+
+mana: $(BIN)/mana.$(ARCH)
+$(SRC)/mana.$(ARCH).o : $(libs)
+$(BIN)/mana.$(ARCH)   : $(cmds) $(funcs)
+
+install: $(DESTBIN)/mana help modules
+
+help: clean-help cmd.basic.help cmd.data.help cmd.astro.help
+
+modules: mana.modules
+
+.PHONY: mana
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/adc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/adc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/adc.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include "dimm.h"
+
+# define DIGITAL_OUT 0x40
+# define ANALOG_IN_INIT 0x00
+# define ANALOG_IN_HIGH 0xa1
+# define ANALOG_IN_LOW  0x91
+
+static char SerialConnected = FALSE;
+static unsigned char DigitalOutState = 0x00;
+static struct timeval reftime; 
+static char reftimeset = FALSE;
+
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+int adc (int argc, char **argv) {
+  
+  struct timeval now;
+  int i, N, mode, hi, lo, value;
+  unsigned char setbit, output[5], *input;
+  double dtime;
+
+  if (N = get_argument (argc, argv, "-reset")) {
+    remove_argument (N, &argc, argv);
+    gettimeofday (&reftime, (struct timeval *) NULL);
+    reftimeset = TRUE;
+    return (TRUE);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: adc ai N (variable)\n");
+    gprint (GP_ERR, "USAGE: adc di N (variable)\n");
+    gprint (GP_ERR, "USAGE: adc do N mode\n");
+    return (FALSE);
+  }
+
+  if (!SerialConnected) {
+    if (!SerialInit ('b')) {
+      gprint (GP_ERR, "error opening serial line\n");
+      return (FALSE);
+    }
+    SerialConnected = TRUE;
+  }
+
+  if (!reftimeset) {
+    gettimeofday (&reftime, (struct timeval *) NULL);
+    reftimeset = TRUE;
+  }      
+
+  gettimeofday (&now, (struct timeval *) NULL);
+  dtime = DTIME (now, reftime);
+  set_variable ("TIME", dtime);
+  
+  /* set the digital outputs */
+  if (!strcmp (argv[1], "do")) {
+    N = atof (argv[2]);
+    if ((N < 1) || (N > 6)) {
+      gprint (GP_ERR, "digital output is between 1 and 6\n");
+      return (FALSE);
+    }
+    if (!strcasecmp (argv[3], "on")) {
+      mode = TRUE;
+    } else {
+      mode = FALSE;
+    }
+    setbit = (0x01 << N-1);
+    if (mode) {
+      DigitalOutState |= setbit;
+    } else {
+      DigitalOutState &= ~setbit;
+    }      
+    output[0] = (DIGITAL_OUT | DigitalOutState);
+    output[1] = 0;
+    SerialCommand (output, &input, 50);
+    gprint (GP_ERR, "%x (%x, %x) -> %x (%d, %d)\n", 
+	     output[0], setbit, DigitalOutState, input[0], 
+	     strlen(input), strlen(output));
+    free (input);
+    return (TRUE);
+  }
+  /* read the analog inputs */
+  if (!strcmp (argv[1], "ai")) {
+    N = atof (argv[2]);
+    if ((N < 2) || (N > 16)) {
+      gprint (GP_ERR, "analog input is between 2 and 16\n");
+      gprint (GP_ERR, " (problem with 1 for the moment...)\n");
+      return (FALSE);
+    }
+    /* init A/D converter */
+    output[0] = (ANALOG_IN_INIT | (N-1));
+    output[1] = 0;
+    SerialCommand (output, &input, 50);
+    free (input);
+
+    usleep (10000);
+    /* get high byte */
+    output[0] = ANALOG_IN_HIGH;
+    output[1] = 0;
+    do {
+      SerialCommand (output, &input, 50);
+      hi = input[0];
+      free (input);
+    } while (hi & 0x80);
+
+    output[0] = ANALOG_IN_LOW;
+    output[1] = 0;
+    SerialCommand (output, &input, 50);
+    /* gprint (GP_ERR, "%x -> %x\n", output[0], input[0]); */
+    lo = input[0];
+    free (input);
+    value = ((hi & 0x0f) << 8) | lo;
+    if (hi & 0x10) { value = -value; }
+    set_variable (argv[3], 0.0001*value);
+    return (TRUE);
+  }
+
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/demux.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/demux.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/demux.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "dimm.h"
+
+int demux (int argc, char **argv) {
+  
+  int i, j, k, N;
+  float *out, *in, *inptr, **outptr;
+  int nx, ny, Nx, Ny, NX, NY, Nmux, Nbuf;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: demux <buffer> nx ny\n");
+    return (FALSE);
+  }
+
+  if (!SelectBuffer (&Nbuf, argv[1], OLDBUFFER)) return (FALSE);
+
+  nx = atof (argv[2]);
+  ny = atof (argv[3]);
+  Nmux = nx*ny;
+
+  Nx = buffers[Nbuf].matrix.Naxis[0];
+  Ny = buffers[Nbuf].matrix.Naxis[1];
+
+  NX = Nx / nx;
+  NY = Ny / ny;
+
+  inptr = in = (float *) buffers[Nbuf].matrix.buffer;  /* don't lose reference */
+
+  ALLOCATE (out, float, Nx*Ny);
+  ALLOCATE (outptr, float *, Nmux);
+  
+  for (N = i = 0; i < nx; i++) {
+    for (j = 0; j < ny; j++, N++) {
+      outptr[N] = &out[i*NX + j*NX*NY*nx];
+    }
+  }
+
+  for (N = j = 0; j < NY; j++) {
+    for (i = 0; i < NX; i++) {
+      for (k = 0; k < Nmux; k++) {
+	*outptr[k] = *inptr;
+	outptr[k] ++;
+	inptr ++;
+      }
+    }
+    for (k = 0; k < Nmux; k++) outptr[k] += NX;
+  }
+
+  /*
+  for (X = Y = x = y = i = 0; i < NX*NY; i++) {
+    out[X + Y*NX + x*NX*(Y+1) + y*NX*NY*nx] = *in;
+    X++;
+    if (X == NX) {
+      X = 0;
+      Y++;
+    }
+    if (Y == NY) {
+      Y = 0;
+      x++;
+    }
+    if (x == nx) {
+      x = 0;
+      y++;
+    }
+  }
+  */
+
+  free (in);
+  buffers[Nbuf].matrix.buffer = (char *) out;
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/findpeaks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/findpeaks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/findpeaks.c	(revision 22322)
@@ -0,0 +1,106 @@
+# include "mana.h"
+
+int findpeaks (int argc, char **argv) {
+  
+  int i, j, n, N, Nx, Ny;
+  int xo, yo;
+  int Npeak, NPEAK, Npeaks;
+  float *v;
+  int *peaks, *keep, *xp, *yp, *zp;
+  float threshold, vt, vo;
+  Vector *vecx, *vecy, *vecz;
+  Buffer *buf;
+
+  if (argc < 3) goto usage;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  threshold = atof (argv[2]);
+
+  if ((vecx = SelectVector ("xp", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector ("yp", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecz = SelectVector ("zp", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  Npeak = 0;
+  NPEAK = Nx;
+  ALLOCATE (xp, int, NPEAK);
+  ALLOCATE (yp, int, NPEAK);
+  ALLOCATE (zp, int, NPEAK);
+
+  /* find peaks for each row */
+  v = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    peaks = findrowpeaks (&v[j*Nx], Nx, threshold, &Npeaks);
+
+    if (Npeak + Npeaks >= NPEAK) {
+      NPEAK = Npeak + Npeaks + Nx;
+      REALLOCATE (xp, int, NPEAK);
+      REALLOCATE (yp, int, NPEAK);
+      REALLOCATE (zp, int, NPEAK);
+    }
+    for (i = 0; i < Npeaks; i++) {
+      xp[Npeak + i] = peaks[i];
+      yp[Npeak + i] = j;
+      zp[Npeak + i] = v[peaks[i] + j*Nx];
+    }
+    Npeak += Npeaks;
+    free (peaks);
+  }
+  
+  /* identify non-local peaks */
+  ALLOCATE (keep, int, MAX (Npeak, 1));
+  v = (float *) buf[0].matrix.buffer;
+  for (n = 0; n < Npeak; n++) {
+    xo = xp[n];
+    yo = yp[n];
+    vo = v[xo + yo*Nx];
+    keep[n] = TRUE;
+    for (i = xo - 1; i <= xo + 1; i++) {
+      if (i < 0) continue;
+      if (i >= Nx) continue;
+      for (j = yo - 1; j <= yo + 1; j++) {
+	if ((i == xo) && (j == yo)) continue;
+	if (j < 0) continue;
+	if (j >= Ny) continue;
+	vt = v[i + j*Nx];
+	if (vt > vo) {
+	  keep[n] = FALSE;
+	  goto next_peak;
+	}
+      }
+    }
+  next_peak:
+    continue;
+  }
+
+  REALLOCATE (vecx[0].elements, float, MAX (Npeak, 1));
+  REALLOCATE (vecy[0].elements, float, MAX (Npeak, 1));
+  REALLOCATE (vecz[0].elements, float, MAX (Npeak, 1));
+  /* eliminate non-local peaks */
+  for (N = n = 0; n < Npeak; n++) {
+    if (!keep[n]) continue;
+    vecx[0].elements[N] = xp[n];
+    vecy[0].elements[N] = yp[n];
+    vecz[0].elements[N] = zp[n];
+    N ++;
+  }
+  free (xp);
+  free (yp);
+  free (zp);
+  free (keep);
+
+  REALLOCATE (vecx[0].elements, float, MAX (N, 1));
+  REALLOCATE (vecy[0].elements, float, MAX (N, 1));
+  REALLOCATE (vecz[0].elements, float, MAX (N, 1));
+  vecx[0].Nelements = vecy[0].Nelements = vecz[0].Nelements = N;
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "findpeaks (buffer) (threshold)\n");
+  return (FALSE);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/findrowpeaks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/findrowpeaks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/findrowpeaks.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "mana.h"
+
+int *findrowpeaks (float *row, int Nrow, float threshold, int *npeaks) {
+  
+  int i;
+  int Npeaks, NPEAKS;
+  int *peaks;
+
+  Npeaks = 0;
+  NPEAKS = 100;
+  ALLOCATE (peaks, int, NPEAKS);
+
+  if (Nrow < 3) {
+    *npeaks = Npeaks;
+    return (peaks);
+  }
+    
+  /* special case for first pixel in row */
+  if ((row[0] > row[1]) && (row[0] > threshold)) {
+    peaks[Npeaks] = 0;
+    Npeaks ++;
+  }    
+
+  for (i = 1; i < Nrow - 1; i++) {
+    if (row[i] < threshold) continue;
+    if (row[i] < row[i-1]) continue;
+    if (row[i] <= row[i+1]) continue;
+
+    peaks[Npeaks] = i;
+    Npeaks ++;
+    if (Npeaks >= NPEAKS) {
+      NPEAKS += 100;
+      REALLOCATE (peaks, int, NPEAKS);
+    }
+  }      
+
+  /* special case for last pixel in row */
+  if ((row[Nrow-1] >= row[Nrow-2]) && (row[Nrow-1] > threshold)) {
+    peaks[Npeaks] = Nrow-1;
+    Npeaks ++;
+  }    
+
+  *npeaks = Npeaks;
+  return (peaks);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/fitcontour.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/fitcontour.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/fitcontour.c	(revision 22322)
@@ -0,0 +1,139 @@
+# include "mana.h"
+# define NTERM 3
+
+int fitcontour (int argc, char **argv) {
+  
+  int i;
+  double **C, **B;
+  float cs1, sn1, cs, sn, x, y, r, xo, yo;
+  float dR, Rmin, Rmaj, Theta, Rx, Ry, Rxy, R1, R2, R3;
+  Vector *vecx, *vecy;
+
+  /* USAGE fitcontour x y Xo Yo */
+  if (argc < 5) goto usage;
+
+  if ((vecx = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  xo = atof (argv[3]);
+  yo = atof (argv[4]);
+
+  ALLOCATE (B, double *, NTERM);
+  ALLOCATE (C, double *, NTERM);
+  for (i = 0; i < NTERM; i++) {
+    ALLOCATE (C[i], double, NTERM);
+    bzero (C[i], NTERM*sizeof(double));
+    ALLOCATE (B[i], double, 1);
+    bzero (B[i], sizeof(double));
+  }
+
+  /* we are fitting r = ro + rs*sin(2theta) + rc*cos(2theta) */
+  /* sin(2t) = 2cos(t)sin(t)
+     cos(2t) = cos^2(t) - sin^2(t)
+  */
+  for (i = 0; i < vecx[0].Nelements; i++) {
+    x = vecx[0].elements[i] - xo;
+    y = vecy[0].elements[i] - yo;
+    r = hypot (x, y);
+
+    /* calculate sin(2t), cos(2t) using 1/2 angle tri relationship above */
+    sn1 = y / r;
+    cs1 = x / r;
+    sn = 2*sn1*cs1;
+    cs = cs1*cs1 - sn1*sn1;
+
+    C[0][0] += 1.0;
+    C[1][0] += sn;
+    C[1][1] += SQ(sn);
+    C[2][0] += cs; 
+    C[2][1] += cs*sn; 
+    C[2][2] += SQ(cs);
+
+    B[0][0] += r;
+    B[1][0] += r*sn;
+    B[2][0] += r*cs; 
+  }
+  C[0][1] = C[1][0];
+  C[0][2] = C[2][0];
+  C[1][2] = C[2][1];
+    
+  dgaussjordan (C, B, NTERM, 1);
+  
+  /** this is somewhat weak: if the object is too elongated, Rmin can be < 0 **/
+  dR = hypot (B[1][0], B[2][0]);
+  Rmaj = B[0][0] + dR;
+  Rmin = B[0][0] - dR;
+  Theta = DEG_RAD*atan2 (B[1][0], B[2][0]) / 2;
+
+  sn = B[1][0] / dR;
+  cs = B[2][0] / dR;
+
+  R1 = SQ(Rmaj) + SQ(Rmin);
+  R2 = SQ(Rmaj) - SQ(Rmin);
+  R3 = Rmaj*Rmin;
+
+  Rx = R3 / sqrt (R1 - R2*cs);
+  Ry = R3 / sqrt (R1 + R2*cs);
+  Rxy = -sn*R2 / SQ(R3);
+
+  set_variable ("Rx", Rx);
+  set_variable ("Ry", Ry);
+  set_variable ("Rxy", Rxy);
+
+  set_variable ("Rmin", Rmin);
+  set_variable ("Rmaj", Rmaj);
+  set_variable ("Theta", Theta);
+
+  for (i = 0; i < NTERM; i++) {
+    free (B[i]);
+    free (C[i]);
+  }
+  free (B);
+  free (C);
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "fitcontour x y (xo) (yo)\n");
+  return (FALSE);
+}
+
+
+/* this routine fits a single sine and cosine to the value of dr as a function of the angle around the central
+ * point, theta.  this is NOT an exact fit, an is increasingly in error for more eccentric ellipses.  however,
+ * it does a reasonable job of approximating the value of the major and minor axes, and it gets the angle of
+ * orientation right at well.  We then assume the values of major and minor axis and angle are correct to get
+ * the parameters for the elliptical gaussian fit:
+ 
+ z = (x^2) / (2 Rx^2) + (y^2) / (2 Ry^2) + Rxy x y
+
+ the functions above give the correct relationship between these:
+
+   R1 = SQ(Rmaj) + SQ(Rmin);
+   R2 = SQ(Rmaj) - SQ(Rmin);
+   R3 = Rmaj*Rmin;
+
+   Rx = R3 / sqrt (R1 - R2*cs);
+   Ry = R3 / sqrt (R1 + R2*cs);
+   Rxy = -sn*R2 / SQ(R3);
+
+ to derive these relationships, write the equation for an ellipse with major axis in the x-dir and minor in
+ the y-dir:
+
+ z = (x^2) / (2 Rmaj^2) + (y^2) / (2 Rmin^2)
+
+ apply the rotation matrix:
+
+ (x)' = (cos, -sin)(x,y)
+ (y)' = (sin, +cos)(x,y)  (modulo the sign on the sine)
+
+ then group the x^2 terms, the y^2 terms, and the xy terms to find the three coeffs.
+
+ NOTE: I spent a while having trouble deriving this from the polar form of an ellipse:
+
+ x = Rmaj*cos(theta)
+ y = Rmin*sin(theta)
+
+ it turns out that 'theta' above is NOT the angle (0,1)-(0,0)-(x,y). rather, it should be viewed as a
+ paraterization of the ellipse.  
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/focus.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/focus.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/focus.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "dimm.h"
+
+int focus (int argc, char **argv) {
+  
+  if (argc < 2) goto usage;
+
+  if (!strcasecmp (argv[1], "init")) {
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: focus init (port)\n");
+      return (FALSE);
+    }
+    if (!SerialInit (argv[2])) return (FALSE);
+    gprint (GP_ERR, "focus on port %s\n", argv[2]);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "pos")) {
+
+    int status, servo, angle;
+    char *answer = (char *) NULL;
+    char line[64];
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: focus pos Nservo (angle)\n");
+      return (FALSE);
+    }
+
+    servo = atoi (argv[2]);
+    angle = atoi (argv[3]);
+
+    sprintf (line, "%c%c%c\n", 0xff, servo, angle);
+    status = SerialCommand (line, &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "raw")) {
+
+    int status, value;
+    char *answer = (char *) NULL;
+    char line[64];
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: focus raw value\n");
+      return (FALSE);
+    }
+
+    value = atoi (argv[2]);
+
+    sprintf (line, "%c", value);
+    status = SerialCommand (line, &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+ usage:
+  gprint (GP_ERR, "focus init port\n");
+  gprint (GP_ERR, "focus pos (string)\n");
+  gprint (GP_ERR, "focus raw (value)\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/init.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/init.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/init.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "opihi.h"
+
+int findpeaks	    PROTO((int, char **));
+int fitcontour	    PROTO((int, char **));
+int starcontour	    PROTO((int, char **));
+int rawstars	    PROTO((int, char **));
+int version	    PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "findpeaks",   findpeaks,    "find image peaks"},
+  {1, "fitcontour",  fitcontour,   "fit ellipse contour"},
+  {1, "starcontour", starcontour,  "object contour"},
+  {1, "rawstars",    rawstars,     "find raw star stats"},
+  {1, "version",     version,      "show version information"},
+}; 
+
+void InitMana () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreeMana () {
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/mana.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/mana.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/mana.c.in	(revision 22322)
@@ -0,0 +1,69 @@
+# include "mana.h"
+
+# define opihi_name "MANA"
+# define opihi_prompt "mana: "
+# define opihi_description "an image manipulation tool\n"
+# define opihi_history ".mana"
+# define opihi_rcfile ".manarc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitMana ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+  
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreeMana ();
+
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/rawstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/rawstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/rawstars.c	(revision 22322)
@@ -0,0 +1,95 @@
+# include "mana.h"
+
+int rawstars (int argc, char **argv) {
+  
+  int i, x, y, N, Nx, Ny, Np;
+  float *v;
+  double Raper, Rinner, Router;
+  Vector *xp, *yp;
+  Vector *xc, *yc, *sx, *sy, *sxy, *zs, *zc, *sk;
+  Buffer *buff;
+
+  Raper = 5;
+  if ((N = get_argument (argc, argv, "-Raper"))) {
+    remove_argument (N, &argc, argv);
+    Raper = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Rinner = 10;
+  if ((N = get_argument (argc, argv, "-Rinner"))) {
+    remove_argument (N, &argc, argv);
+    Rinner = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Router = 15;
+  if ((N = get_argument (argc, argv, "-Router"))) {
+    remove_argument (N, &argc, argv);
+    Router = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 4) goto usage;
+
+  if ((buff = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((xp = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yp = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (xp[0].Nelements != yp[0].Nelements) {
+    gprint (GP_ERR, "vectors are not the same length\n");
+    return (FALSE);
+  }
+
+  set_rough_radii (Raper, Rinner, Router);
+
+  /* output vectors */
+  if ((xc = SelectVector ("xc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yc = SelectVector ("yc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zc = SelectVector ("zc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zs = SelectVector ("zs", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sk = SelectVector ("sk", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sx = SelectVector ("sx", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sy = SelectVector ("sy", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sxy = SelectVector ("sxy", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buff[0].matrix.Naxis[0];
+  Ny = buff[0].matrix.Naxis[1];
+  Np = xp[0].Nelements;
+
+  REALLOCATE (xc[0].elements, float, Np);
+  REALLOCATE (yc[0].elements, float, Np);
+  REALLOCATE (sx[0].elements, float, Np);
+  REALLOCATE (sy[0].elements, float, Np);
+  REALLOCATE (sxy[0].elements, float, Np);
+  REALLOCATE (zs[0].elements, float, Np);
+  REALLOCATE (zc[0].elements, float, Np);
+  REALLOCATE (sk[0].elements, float, Np);
+  xc[0].Nelements = yc[0].Nelements = sx[0].Nelements = Np;
+  sy[0].Nelements = zs[0].Nelements = zc[0].Nelements = Np;
+  sxy[0].Nelements = sk[0].Nelements = Np;
+
+  v = (float *) buff[0].matrix.buffer;
+  for (i = 0; i < Np; i++) {
+    x = xp[0].elements[i];
+    y = yp[0].elements[i];
+    if (x < 0) continue;
+    if (x >= Nx) continue;
+    if (y < 0) continue;
+    if (y >= Ny) continue;
+
+    get_rough_star (v, Nx, Ny, x, y, 
+		    &xc[0].elements[i], 
+		    &yc[0].elements[i], 
+		    &sx[0].elements[i], 
+		    &sy[0].elements[i], 
+		    &sxy[0].elements[i], 
+		    &zs[0].elements[i], 
+		    &zc[0].elements[i],
+		    &sk[0].elements[i]);
+  }
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "rawstars (buffer) (xp) (yp)\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/simsignal.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/simsignal.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/simsignal.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "dimm.h"
+
+int simsignal (int argc, char **argv) {
+  
+  int Nvect, Nbin, Nbit, i, ivalue, jvalue, Nshift, mask, scale;
+  float *buf;
+  double cvalue, dvalue, sigma, SN, period;
+  double rnd_gauss ();
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: simsignal (vector) Nbin Nbits period\n");
+    return (FALSE);
+  }
+
+  if (!SelectVector (&Nvect, argv[1], ANYVECTOR)) return (FALSE);
+  Nbin = atof (argv[2]);
+  Nbit = atof (argv[3]);
+  /* SN = atof (argv[4]); */
+  period = atof (argv[4]);
+
+  vectors[Nvect].Nelements = Nbin;
+  REALLOCATE (vectors[Nvect].elements, float, vectors[Nvect].Nelements);
+
+  scale = (0x1 << Nbit) - 1;
+
+  buf = vectors[Nvect].elements;
+  for (i = 0; i < Nbin; i++, buf++) {
+    ivalue = scale * 0.5 * (sin (i*2*M_PI/period) + 1) + 0.5;
+    /*
+    dvalue = rnd_gauss (cvalue, sigma);
+    cvalue = (dvalue + range) / (2.0*range);
+    dvalue = MAX (0, MIN (0.99999, cvalue));
+    ivalue = scale * dvalue;
+    *buf = (2.0 * range * ((double) ivalue + 0.5)) / scale - range; 
+    */
+    *buf = ivalue;
+  }
+
+  return (TRUE);
+}
+
+/* 
+
+8 bit = 2^8
+
+  gauss_init (2*scale);
+  sigma = 2.0 / SN;
+  range = 1 + 5*sigma;
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/starcontour.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/starcontour.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/starcontour.c	(revision 22322)
@@ -0,0 +1,130 @@
+# include "mana.h"
+
+int starcontour (int argc, char **argv) {
+  
+  int x, y, xs, xp, yp;
+  int Nx, Ny, N, Npts, min, max;
+  float *v;
+  float zt, zo, xmin, xmax;
+  Vector *vecx, *vecy;
+  Buffer *buf;
+
+  if (argc < 5) goto usage;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  xp = atof (argv[2]);
+  yp = atof (argv[3]);
+  zo = atof (argv[4]);
+
+  if ((vecx = SelectVector ("xo", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector ("yo", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  N = 0;
+  Npts = 100;
+  REALLOCATE (vecx[0].elements, float, MAX (Npts, 1));
+  REALLOCATE (vecy[0].elements, float, MAX (Npts, 1));
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  v = (float *)buf[0].matrix.buffer;
+
+  /* find transition below (limit range?) */
+  xmin = xmax = 0;
+  xs = xp;
+  for (y = yp; (y >= 0) && (v[xs + y*Nx] > zo); y--) {
+    /* find transition below (limit range?) */
+    min = max = FALSE;
+    for (x = xs; (x >= 0) && !min; x--) {
+      min = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	min = TRUE;
+	xmin = x + (zo - zt)/(v[x + 1 + y*Nx] - zt);
+	vecx[0].elements[N] = xmin;
+	vecy[0].elements[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements, float, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements, float, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    /* find transition above (limit range?) */
+    for (x = xs; (x < Nx) && !max; x++) {
+      max = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	max = TRUE;
+	xmax = x - (zo - zt)/(v[x - 1 + y*Nx] - zt);
+	vecx[0].elements[N] = xmax;
+	vecy[0].elements[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements, float, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements, float, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    if (min && max) {
+      xs = 0.5*(xmin + xmax);
+    }
+  }
+
+  /* find transition above (limit range?) */
+  xs = xp;
+  for (y = yp; (y < Ny) && (v[xs + y*Nx] > zo); y++) {
+    /* find transition below (limit range?) */
+    min = max = FALSE;
+    for (x = xs; (x >= 0) && !min; x--) {
+      min = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	min = TRUE;
+	xmin = x + (zo - zt)/(v[x + 1 + y*Nx] - zt);
+	vecx[0].elements[N] = xmin;
+	vecy[0].elements[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements, float, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements, float, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    /* find transition above (limit range?) */
+    for (x = xs; (x < Nx) && !max; x++) {
+      max = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	max = TRUE;
+	xmax = x - (zo - zt)/(v[x - 1 + y*Nx] - zt);
+	vecx[0].elements[N] = xmax;
+	vecy[0].elements[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements, float, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements, float, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    if (min && max) {
+      xs = 0.5*(xmin + xmax);
+    }
+  }
+  vecx[0].Nelements = N;
+  vecy[0].Nelements = N;
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "starcontour (buffer) (xpeak) (ypeak) (level)\n");
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/tests.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/tests.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/tests.c	(revision 22322)
@@ -0,0 +1,108 @@
+# include "opihi.h"
+
+run_tests () {
+
+  /* test1 ();
+     test1b ();
+     test2 ();
+     test3 (); */
+  test4 (); 
+  test5 ();
+}
+
+test1 () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 1...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "100");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 1\n");
+  sleep (1);
+}
+
+test1b () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 1b...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "dsin(45)");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 1b\n");
+  sleep (1);
+}
+
+test2 () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 2...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "5 * 100");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 2\n");
+  sleep (1);
+}
+
+test3 () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 3...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "5 * dsin(45)");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 3\n");
+  sleep (1);
+}
+
+test4 () {
+
+  int i;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 4...  ");
+  for (i = 0; i < 1000000; i++) {
+    ALLOCATE (test, char, 256);
+    sprintf (test, "$N = 100");
+    line = parse (test);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 4\n");
+  sleep (1);
+}
+
+test5 () {
+
+  int i;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 5...  ");
+  for (i = 0; i < 1000000; i++) {
+    ALLOCATE (test, char, 256);
+    sprintf (test, "echo {100 * 800}");
+    line = parse (test);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 5\n");
+  sleep (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/mana/version.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "mana.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "mana version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "libohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "libfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/modules/dvo/navigate
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/modules/dvo/navigate	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/modules/dvo/navigate	(revision 22322)
@@ -0,0 +1,623 @@
+# -*- perl -*-
+
+macro navigate
+  style -n 0
+  limits
+  $DRAWSTARS  = -1
+  $DRAWIMAGES =  1
+  $DRAWGRID   = -1
+  $ZOOM = 180 / ($YMAX - $YMIN)
+  #this should be changed to a while loop, except 'while' is broken for some reason
+  $KEY = "none"
+  while ("$KEY" != "q")
+    cursor -g 1
+
+    # help list
+    if ("$KEY" == "h")
+     echo "Arrow Keys - pan in that direction"
+     echo "PgUp,PgDn - zoom in/out a factor of 1.2"
+     echo "Home,End  - zoom in/out a factor of 2"
+     echo "1 - zoom in factor of 2 at the cursor"
+     echo "2 - zoom in factor of 1.2 at the cursor"
+     echo "3 - recenter at cursor"
+     echo "4 - zoom out factor of 1.2 at the cursor"
+     echo "5 - zoom out factor of 2 at the cursor"
+     echo "6 - zoom out factor of 10 at the cursor"
+     echo "z - zoom to radius (requires 2nd keystroke)"
+     echo "f - show full sky"
+     echo ""
+     echo "q - quit"
+     echo "S - toggle auto-plotting of stars"
+     echo "A - toggle auto-plotting of image borders"
+     echo "g - toggle skygrid on/off"
+     echo "c - plot status catalog boundaries"
+     echo "C - list catalog at cursor location"
+     echo "i - list info about images touching cursor location" 
+     echo "I - list info about images, with pixel coords of cursor position"
+     echo "j - adjust mag scale, +0.5"
+     echo "k - adjust mag scale, -0.5"
+     echo "J - adjust dmag scale, /1.25"
+     echo "K - adjust dmag scale, *1.25"
+     echo "r - plot detected asteroids (rocks)"
+     echo "l - plot HST GSC"
+     echo "L - plot Landolt stars"
+     echo "m - list measurements for stars within 1 pixel of cursor"
+     echo "M - list measurements for stars within 1.8 arcsec of cursor"
+     echo "p - ****(don't know what this does)"
+     echo "s - ****(don't know what this does)"
+     echo "t - plot light curve for star within 2 arcsec of cursor position"
+     echo "T - plot 'galaxy' light curve for star within 2 arcsec of cursor position"
+     echo "u - ****(don't know what this does)"
+     echo "x - plot stars scaled by magnitude Chisq"
+     echo "X - plot stars by magnitude scatter"
+     echo "y - ****(don't know what this does)"
+     echo ""
+     echo "@ - execute macro `user_macro`"
+     echo ": - ****input a line and execute (not yet implemented)"
+    end
+
+    # quit from navigate
+    if ("$KEY" == "q") continue
+
+    #pan controls
+    if (("$KEY" == "Left") || ("$KEY" == "Right") || ("$KEY" == "Up") || ("$KEY" == "Down"))
+      $SHIFT = 0.2
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      #assumes standard sky orientation!! (N up, E left)
+      if ("$KEY"=="Left")
+        $R$KEY = $R$KEY + $SHIFT*$XMAX
+      end
+      if ("$KEY"=="Right")
+        $R$KEY = $R$KEY + $SHIFT*$XMIN
+      end
+      if ("$KEY"=="Up")
+        $D$KEY = $D$KEY + $SHIFT*$YMAX
+      end
+      if ("$KEY"=="Down")
+        $D$KEY = $D$KEY + $SHIFT*$YMIN
+      end
+      #pretend like I hit '3' in the place to recenter it
+      nav_zoom 1      
+    end
+
+    # NEW zoom controls
+    if (("$KEY" == "Prior") || ("$KEY" == "Next") || ("$KEY" == "Home") || ("$KEY" == "End") || ("$KEY" == "Button4") || ("$KEY" == "Button5"))    
+      #move where key was hit to center      
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      if ("$KEY" == "Prior")
+        $zfac=1.2
+      end
+      if ("$KEY" == "Next")
+        $zfac={1/1.2}
+      end
+      if ("$KEY" == "Home")
+        $zfac=2
+      end
+      if ("$KEY" == "End")
+        $zfac={1/2.}
+      end
+      if ("$KEY"=="Button4")
+        $zfac=1.6
+      end
+      if ("$KEY"=="Button5")
+        $zfac={1/1.6}
+      end
+      nav_zoom $zfac
+    end
+
+    if ("$KEY"=="Button1")
+      nav_zoom 1
+    end
+    if ("$KEY"=="Button2")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY"=="Button3")
+      nav_zoom 2
+    end
+
+
+
+    # zoom controls
+    if ("$KEY" == "1")
+      nav_zoom 2
+    end
+    if ("$KEY" == "2")
+      nav_zoom 1.2
+    end
+    if ("$KEY" == "3")
+      nav_zoom 1
+    end
+    if ("$KEY" == "4")
+      nav_zoom {1/1.2}
+    end
+    if ("$KEY" == "5")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY" == "6")
+      nav_zoom {1/20.}
+    end
+
+ 
+   # measure distance
+    if ("$KEY" == "d")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'd' again at endpoint"
+      cursor -g 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = 3600*((dcos($d0)*($r0-$r1))^2 + ($d0-$d1)^2)^0.5
+      echo "$dr arcsec"
+    end
+    # show ra, dec
+    if ("$KEY" == "w")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      echo "$tmp $D$KEY" 
+      exec echo $tmp $D$KEY | radec -hh
+    end
+    # zoom to radius
+    if ("$KEY" == "z")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'z' again at radius"
+      cursor -g 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = (($r0-$r1)^2 + ($d0-$d1)^2)^0.5
+      $ZOOM = $RAD / $dr
+      nav_recenter
+      nav_redraw
+      $KEY = $ok
+      $R$KEY = $r0
+      $D$KEY = $d0
+    end
+
+    # adjust mag scaling
+    if ("$KEY" == "J")
+      $MAG = $MAG - 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "K")
+      $MAG = $MAG + 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "j")
+      $dMAG = $dMAG * 0.8
+      nav_redraw
+    end
+    if ("$KEY" == "k")
+      $dMAG = $dMAG * 1.25
+      nav_redraw
+    end
+    echo "mag, dmag: $MAG, $dMAG"
+
+
+
+    # plot full sky
+    if ("$KEY" == "f") 
+      echo "full"
+      $ZOOM = 1
+      resize 1150 600		      
+      region 0 0 90 ait
+      $RMIN = 0
+      $RMAX = 360
+      $DMIN = -90
+      $DMAX = +90
+      style -c red; cgrid
+      style -c black
+      images
+    end
+
+    # plot rocks
+    if ("$KEY" == "r") 
+#      plot.rocks
+      style -c blue   -pt 1; procks -speed 0.0041 1
+      style -c red    -pt 1; procks -speed 0.00041 0.0041
+      style -c indigo -pt 1; procks -speed 0 0.00041
+      style -c black -lw 0;
+    end
+    # plot HST-GSC
+    if ("$KEY" == "l") 
+      style -c blue -pt 7; cat -all -g -m 9 16
+      style -c black
+    end
+    # plot Landolt
+    if ("$KEY" == "L") 
+#      style -c red  -lw 2 -pt 3; cat -a 1 2 3 /data/elixir/srcdir/refs/stetson/stetsonBn.txt -m 9 18
+#      style -c blue -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.fix -m 9 18
+      style -c red -lw 2 -pt 7; cat -a 1 2 3 /data/elixir/srcdir/refs/sdss/g_SDSS.dat -m 9 14
+#      style -c red -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.hq -m 9 18
+#      style -c red -lw 2 -pt 3; cat -a 22 23 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.unfix -m 9 18
+#      style -c blue -lw 2 -pt 7; cat -a 1 2 4 /data/elixir/srcdir/refs/landolt/extreme/extreme.match -m 0 20
+#      style -x 2 -c red -pt 7 ; cplot RA DEC
+      style -c black -lw 0
+    end
+
+    # list star measurements
+    if ("$KEY" == "m") 
+        $dR = $RAD/$ZOOM/300
+        if ($dR < 0.0005)
+	 $dR = 0.0005
+        end
+	gstar $R$KEY $D$KEY $dR -m
+    end
+
+    # plot mag residuals
+    if ("$KEY" == "R") 
+      echo "filter: "
+      cursor 1
+      clear -n 1 -s; lim 10 22 -0.2 0.2; clear; box
+      dmags $KEY\:rel - $KEY : $KEY -type 0
+      plot -x 2 -pt 0 -sz 0.3 -c red yv xv
+      dmags $KEY\:rel - $KEY : $KEY -type 0 -flag 0 -nphot +3 -chisq 2.0
+      plot -x 2 -pt 2 -sz 0.5 -c black yv xv
+      $KEY = R
+      style -n 0
+    end
+
+    if ("$KEY" == "M") 
+	gstar $R$KEY $D$KEY 0.0005 -m
+    end
+    # list images
+    if ("$KEY" == "i") 
+	gimages $R$KEY $D$KEY
+    end
+    if ("$KEY" == "I") 
+	gimages $R$KEY $D$KEY -pix
+    end
+
+    #toggle images on / off
+    if ("$KEY" == "A")
+      $DRAWIMAGES = $DRAWIMAGES * -1
+      if ($DRAWIMAGES == 1)
+        images
+      end
+    end
+    # toggle stars on / off
+    if ("$KEY" == "S")
+      $DRAWSTARS = $DRAWSTARS * -1
+      if (($ZOOM > 20) && ($DRAWSTARS == 1))
+       style -pt 7
+       pmeasure -all -m $MAG {$MAG + $dMAG}
+      end
+    end
+    # turn grid on / off
+    if ("$KEY" == "g")
+      $DRAWGRID = $DRAWGRID * -1
+      if (($ZOOM > 20) && ($DRAWGRID==1))
+        style -c black; cgrid
+      end
+      if (($ZOOM > 20) && ($DRAWGRID==-1))
+        nav_redraw
+      end
+    end
+
+    # plot light-curve interactive
+    if ("$KEY" == "t")
+      style -n 1 -pt 2 -x 2
+      clear
+      if ($R$KEY < 0) 
+       $R$KEY = $R$KEY + 360
+      end
+      lcurve -l $R$KEY $D$KEY {30/3600} -d -v time mag
+      box
+      lcv
+      style -n 0
+    end
+    # plot light-curve 
+    if ("$KEY" == "T")
+      style -n 1 -pt 1 -c red -x 2
+      lcurve $R$KEY $D$KEY {30/3600} -d
+      style -c black
+      style -n 0 
+    end
+    # plot catalogs
+    if ("$KEY" == "c")
+      style -c blue; pcat; style -c black
+    end
+    # list catalogs
+    if ("$KEY" == "C")
+      gcat $R$KEY $D$KEY
+    end
+
+    # plot image chisqs
+    if ("$KEY" == "x") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME Xm -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c blue
+       czplot ra dec Xm 3 30
+       style -c black -pt 1
+    end
+    # plot meas errors
+    if ("$KEY" == "X") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME dM -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c red
+       czplot ra dec dM 0 30
+       style -c black -pt 1
+    end
+
+
+    # temp plot for skyprobe
+    if ("$KEY" == "u") 
+      imextract -region time
+      imextract -region mcal
+      imextract -region airmass
+      imextract -region nstar
+      vstat time
+      clear -n 1;
+      section a 0 0.00 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} -0.8 -0.5; box; plot time mcal
+      section b 0 0.33 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3}  0.95 3.0; box; plot time airmass
+      section c 0 0.66 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} 0 3000; box; plot time nstar
+      style -n 0
+    end
+    if ("$KEY" == "s")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      $line = `echo $tmp $D$KEY | radec -hh`
+      imextract -region photcode
+      imextract -region time
+     
+      subset t = time if (int(photcode/100) == 1)
+      uniq t T
+      $Bn = t[]
+      $BN = T[]
+      
+      subset t = time if (int(photcode/100) == 2)
+      uniq t T
+      $Vn = t[]
+      $VN = T[]
+      
+      subset t = time if (int(photcode/100) == 3)
+      uniq t T
+      $Rn = t[]
+      $RN = T[]
+      
+      subset t = time if (int(photcode/100) == 4)
+      uniq t T
+      $In = t[]
+      $IN = T[]
+     
+      echo "$line  $Bn $BN  $Vn $VN  $Rn $RN  $In $IN"
+    end
+
+    if ("$KEY" == "p") 
+      echo "P - new coords; p - old coords"
+      cursor -g 1
+      exec echo $Rp $Dp $RP $DP >> fix.coords
+    end
+
+    if ("$KEY" == "y")
+      ccd I - 2MASS_J : 2MASS_J - 2MASS_K
+      lim -n 1 -1 10 -1 3; clear; box; plot -x 2 -pt 2 -sz 0.5 xv yv
+      dev -n 0 -g
+    end
+
+    #  User-defined macro
+    if ("$KEY" == "at")
+      user_macro
+    end
+
+    if ("$KEY" == "colon")
+      #make this work similar to ':' in vi or iraf
+      #does not work correctly now.
+      scan stdin line
+      $line
+    end
+
+  end
+end
+
+#define this so navigate doesn't crash if you try to call it.
+#If you define a user_macro, be sure to do so AFTER this in .dvorc.
+macro user_macro
+  #echo "success!"
+  $do_nothing=0
+end
+    
+
+macro nav_zoom
+  $ZOOM = $ZOOM * $1
+  nav_recenter
+  nav_redraw
+  $Rnum = $R$KEY		      
+  $Dnum = $D$KEY
+  $KEY = num
+end
+
+macro nav_recenter
+  region $R$KEY $D$KEY {$RAD/$ZOOM} sin
+  #assumes standard sky orientation!! (N up, E left)
+  $RMIN = $R$KEY + $XMIN
+  $RMAX = $R$KEY + $XMAX
+  $DMIN = $D$KEY + $YMIN
+  $DMAX = $D$KEY + $YMAX
+end
+
+macro nav_redraw
+  clear
+  if ($ZOOM <= 20) 
+    style -c red; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWGRID==1))
+    style -c black; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWSTARS == 1))
+    pmeasure -all -m $MAG {$MAG + $dMAG}
+  end    
+  style -c black
+  if ($DRAWIMAGES == 1)
+    images
+  end
+end
+
+
+
+#==================================================
+#=================   END BSNAV   ==================
+#==================================================
+
+
+macro sigclip
+  if ("$0" == "1")
+    echo ""
+    echo "sigclip <clipvector> <N_iterations> <N_sigma> [other vectors ..]"
+    echo ""
+  end
+
+  #required parameters
+  $CLIPVECT = $1
+  $NITERATE = $2
+  $NSIGCLIP = $3
+  
+  for i 0 $NITERATE
+    vstat -q $CLIPVECT
+    #clip boundaries
+    $top = $MEAN + ($NSIGCLIP*$SIGMA)
+    $bot = $MEAN - ($NSIGCLIP*$SIGMA)
+    
+    #clip it good.
+    subset temp = $CLIPVECT if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+    
+    #if you specify other vectors, clip the same elements from them too.
+    #they must all be the same length, of course!!
+    if ($0>4)
+      for j 4 $0
+        subset $$j = $$j if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+      end
+    end
+    
+    #copy temp back to $CLIPVECT and reiterate!
+    delete $CLIPVECT
+    concat temp $CLIPVECT  
+  end
+end
+
+
+macro binvec
+  if ("$0" == "1")
+    echo ""
+    echo "binvec <vec> <Nbins> [other vectors...]"
+    echo ""
+    echo "Bin the vector 'vec' into Nbin bins.  Listing other vectors will"
+    echo "put the corresponding elements of those into other vectors which"
+    echo "are the subset of the vector in that bin.  (That can probably be"
+    echo "stated better.)  This macro makes lots of new vectors.  Hooray!"
+    echo ""
+    echo "Creates"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $NBINS   = $2
+
+  vstat -q $binvect
+  $step = ($MAX-$MIN)/$NBINS
+  $vmin = $MIN
+  $vmax = $MAX
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>3)
+      for j 3 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
+
+macro binvec.2
+  if ("$0" == "1")
+    echo ""
+    echo "binvec.2 <vec> <min> <max> <binsize> [other vectors...]"
+    echo ""
+    echo ""
+    echo "see also 'binvec'"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $vmin    = $2
+  $vmax    = $3
+  $step    = $4
+
+  $NBINS = ($MAX-$MIN)/$step
+
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>5)
+      for j 5 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/modules/mana/plots
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/modules/mana/plots	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/modules/mana/plots	(revision 22322)
@@ -0,0 +1,41 @@
+
+macro cvplot 
+  if ($0 != 6)
+    echo "USAGE: cvplot r d ur ud scale"
+    break
+  end
+ 
+  local i
+  delete -q _tmpr _tmpd
+
+  for i 0 $1[]
+   concat $1[$i] _tmpr
+   concat $2[$i] _tmpd
+   concat {$1[$i]+$3[$i]*$5} _tmpr
+   concat {$2[$i]+$4[$i]*$5} _tmpd
+  end
+  style -pt 100
+  cplot _tmpr _tmpd
+  style -pt 7
+end
+
+macro vplot 
+  if ($0 != 6)
+    echo "USAGE: cvplot r d ur ud scale"
+    break
+  end
+ 
+  local i
+  delete -q _tmpr _tmpd
+
+  for i 0 $1[]
+   concat $1[$i] _tmpr
+   concat $2[$i] _tmpd
+   concat {$1[$i]+$3[$i]*$5} _tmpr
+   concat {$2[$i]+$4[$i]*$5} _tmpd
+  end
+  style -pt 100
+  plot _tmpr _tmpd
+  style -pt 7
+end
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+pantasks.c
+pantasks_client.c
+pantasks_server.c
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckController.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckController.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckController.c	(revision 22322)
@@ -0,0 +1,116 @@
+# include "pantasks.h"
+
+static struct timeval start;
+void TimerMark ();
+float TimerElapsed (int reset);
+
+int CheckController () {
+
+  char *p, *q;
+  int i, Njobs, status, JobID;
+  Job *job;
+  IOBuffer buffer;
+
+  /* get the list of completed jobs (exit / crash), update the job status */
+  if (!CheckControllerStatus()) return (TRUE);
+
+  /*** 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 ();
+  FlushIOBuffer (&buffer);
+  status = ControllerCommand ("jobstack exit", CONTROLLER_PROMPT, &buffer);
+  if (VerboseMode()) gprint (GP_ERR, "check exit stack %f\n", TimerElapsed(TRUE));
+  if (!status) goto escape;
+
+  /** programming error **/
+  p = memstr (buffer.buffer, "USAGE: jobstack", buffer.Nbuffer);
+  if (p != NULL) goto escape;
+
+  /** parse job list **/
+  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));
+
+  p = buffer.buffer;
+  for (i = 0; i < Njobs; i++) {
+    q = strchr (p, '\n');
+    if (q == NULL) {
+      gprint (GP_ERR, "controller message error: incomplete job list\n");
+      break;
+    }
+    p = q + 1;
+    status = sscanf (p, "%d", &JobID);
+
+    job = FindControllerJob (JobID);
+    if (job == NULL) {
+      gprint (GP_ERR, "misplaced job? %d not in EXIT job list\n", JobID);
+      continue;
+    }
+    /* this checks the individual job status, grabs stdout/stderr */
+    CheckControllerJob (job);
+  }
+  if (VerboseMode()) gprint (GP_ERR, "clear %d exit jobs %f\n", i, TimerElapsed(TRUE));
+
+  /*** check CRASH jobs ***/
+  FlushIOBuffer (&buffer);
+  status = ControllerCommand ("jobstack crash", CONTROLLER_PROMPT, &buffer);
+  if (!status) goto escape;
+
+  p = memstr (buffer.buffer, "USAGE: jobstack", buffer.Nbuffer);
+  if (p != NULL) goto escape;
+
+  /** parse job list **/
+  status = sscanf (buffer.buffer, "%*s %d", &Njobs);
+  if (status != 1) goto escape;
+  if (VerboseMode()) gprint (GP_ERR, "check crash stack %f\n", TimerElapsed(TRUE)); 
+
+  p = buffer.buffer;
+  for (i = 0; i < Njobs; i++) {
+    q = strchr (p, '\n');
+    if (q == NULL) {
+      gprint (GP_ERR, "controller message error: incomplete job list\n");
+      break;
+    }
+    p = q + 1;
+    
+    status = sscanf (p, "%d", &JobID);
+    job = FindControllerJob (JobID);
+    if (job == NULL) {
+      gprint (GP_ERR, "misplaced job? %d not in CRASH job list\n", JobID);
+      continue;
+    }
+    /* this checks the individual job status, grabs stdout/stderr */
+    CheckControllerJob (job);
+  }
+  if (VerboseMode()) gprint (GP_ERR, "clear %d crash jobs %f\n", i, TimerElapsed(TRUE)); 
+
+  FlushIOBuffer (&buffer);
+  // status = ControllerCommand ("run", CONTROLLER_PROMPT, &buffer);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+
+ escape:
+  FlushIOBuffer (&buffer);
+  // status = ControllerCommand ("run", CONTROLLER_PROMPT, &buffer); 
+  FreeIOBuffer (&buffer);
+  return (FALSE);
+}
+
+void TimerMark () {
+    gettimeofday (&start, (void *) NULL);
+}
+
+float TimerElapsed (int reset) {
+
+  float dtime;
+  struct timeval stop;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  if (reset) gettimeofday (&start, (void *) NULL);
+  return (dtime);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckJobs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckJobs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckJobs.c	(revision 22322)
@@ -0,0 +1,230 @@
+# include "pantasks.h"
+
+float CheckJobs () {
+
+  FILE *f;
+  Job *job;
+  Task *task;
+  Macro *macro;
+  int i, status;
+  char varname[64];
+  Queue *queue;
+  float time_running, next_timeout;
+
+  // int Ncheck;
+  // Ncheck = 0;
+
+  // actual maximum delay is controlled in job_threads.c
+  next_timeout = 1.0;
+
+  /** test all jobs: ready to test?  finished? **/
+  while ((job = NextJob ()) != NULL) {
+    // Ncheck ++;
+
+    task = job[0].task;
+    // XXX we need to guarantee that the task exists
+    // if we delete a task, we need to keep a copy until all task jobs are
+    // removed
+
+    /* check poll period (ready to ask for status?) */
+    time_running = GetTaskTimer(job[0].last, FALSE);
+    // fprintf (stderr, "next: %f, poll: %f, run: %f\n", next_timeout, task[0].poll_period, time_running);
+    if (time_running < task[0].poll_period) {
+      next_timeout = MIN (next_timeout, task[0].poll_period - time_running);
+      continue;
+    }
+    next_timeout = 0.0;
+
+    /* check current status */
+    status = CheckJob (job);
+    switch (status) {
+      case JOB_PENDING:
+	/* if (VerboseMode()) gprint (GP_LOG, "job %s (%d) pending\n", task[0].name, job[0].JobID); */
+	break;
+
+      case JOB_BUSY:
+	/* if (VerboseMode()) gprint (GP_LOG, "job %s (%d) busy\n", task[0].name, job[0].JobID); */
+	break;
+
+      case JOB_CRASH:
+      case JOB_EXIT:
+	/* push output buffer data to the stdout and stderr queues */
+	/* XXX this will break on 0 values in output streams */
+	if (DEBUG) fprintf (stderr, "job: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+	PushNamedQueue ("stdout", job[0].stdout_buff.buffer);
+	PushNamedQueue ("stderr", job[0].stderr_buff.buffer);
+
+	/* save the stdout and stderr if desired */
+	if ((job[0].stdout_dump != NULL) && strcasecmp(job[0].stdout_dump, "NULL")) {
+	  f = fopen (job[0].stdout_dump, "a");
+	  if (f == NULL) {
+	    gprint (GP_ERR, "unable to open stdout dump file %s\n", job[0].stdout_dump);
+	  } else {
+	    fwrite (job[0].stdout_buff.buffer, 1, job[0].stdout_buff.Nbuffer, f);
+	    fclose (f);
+	  }
+	}
+	if ((job[0].stderr_dump != NULL) && strcasecmp(job[0].stderr_dump, "NULL")) {
+	  f = fopen (job[0].stderr_dump, "a");
+	  if (f == NULL) {
+	    gprint (GP_ERR, "unable to open stderr dump file %s\n", job[0].stderr_dump);
+	  } else {
+	    fwrite (job[0].stderr_buff.buffer, 1, job[0].stderr_buff.Nbuffer, f);
+	    fclose (f);
+	  }
+	}
+
+	/* set taskarg variables */
+	for (i = 0; i < job[0].argc; i++) {
+	    sprintf (varname, "taskarg:%d", i);
+	    set_str_variable (varname, job[0].argv[i]);
+	}
+	set_int_variable ("taskarg:n", job[0].argc);
+
+	/* set options variables */
+	for (i = 0; i < job[0].optc; i++) {
+	    sprintf (varname, "options:%d", i);
+	    set_str_variable (varname, job[0].optv[i]);
+	}
+	set_int_variable ("options:n", job[0].optc);
+
+	set_variable ("JOB_DTIME", job[0].dtime);
+
+	if (job[0].realhost == NULL) {
+	  set_str_variable ("JOB_HOSTNAME", "localhost");
+	} else {
+	  set_str_variable ("JOB_HOSTNAME", job[0].realhost);
+	}	    
+
+	if (status == JOB_CRASH) {
+	  /* XXX add an Ncrash element? */
+	  task[0].Nfailure ++;
+	  UpdateTaskTimerStats (task, TIMER_FAILURE, job[0].dtime);
+
+	  /* run task[0].crash macro, if it exists */
+	  /* perhaps define PushNamedQueueBuffer */
+
+	  set_str_variable ("JOB_STATUS", "CRASH");
+
+	  if (VerboseMode()) gprint (GP_LOG, "job %s (%d) crash\n", task[0].name, job[0].JobID);
+	  if (task[0].crash != NULL) {
+	    exec_loop (task[0].crash);
+	  }
+	}
+	if (status == JOB_EXIT) {
+	  /* update the exit status counters */
+	  if (job[0].exit_status) {
+	    task[0].Nfailure ++;
+	    UpdateTaskTimerStats (task, TIMER_FAILURE, job[0].dtime);
+	  } else {
+	    task[0].Nsuccess ++;
+	    UpdateTaskTimerStats (task, TIMER_SUCCESS, job[0].dtime);
+	  }
+
+	  set_int_variable ("JOB_STATUS", job[0].exit_status);
+
+	  /* run corresponding task[0].exit macro, if it exists */
+	  if (VerboseMode()) gprint (GP_LOG, "job %s (%d) exit\n", task[0].name, job[0].JobID);
+	  macro = task[0].defexit;
+	  for (i = 0; i < task[0].Nexit; i++) {
+	    if (job[0].exit_status == atoi(task[0].exit[i][0].name)) {
+	      macro = task[0].exit[i];
+	      break;
+	    }
+	  }
+	  if (macro != NULL) exec_loop (macro);
+	}
+
+	/* flush the stderr and stdout queues */
+	queue = FindQueue ("stdout");
+	if (queue) InitQueue (queue);
+	queue = FindQueue ("stderr");
+	if (queue) InitQueue (queue);
+
+	UpdateTaskTimerStats (task, TIMER_ALLJOBS, job[0].dtime);
+
+	DeleteJob (job);
+	continue;
+	break;
+
+      default:
+	if (VerboseMode()) gprint (GP_LOG, "unknown exit status: %d\n", status);
+	/** do something more useful here ?? **/
+	break;
+    }
+
+    /* 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) { 
+	/* reset polling clock */
+	SetTaskTimer (&job[0].last);
+	continue;
+      }
+      if (VerboseMode()) gprint (GP_LOG, "timeout on %s\n", task[0].name);
+
+      // XXX harvest STDERR and STDOUT from timeout job (should be available...)
+      // XXX add this to controller as well
+
+      /* update the timeout counter */
+      task[0].Ntimeout ++;
+
+      if (!KillLocalJob (job)) {
+	job[0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	continue;
+      }
+
+      /* set taskarg variables */
+      for (i = 0; i < job[0].argc; i++) {
+	  sprintf (varname, "taskarg:%d", i);
+	  set_str_variable (varname, job[0].argv[i]);
+      }
+      set_int_variable ("taskarg:n", job[0].argc);
+
+      /* set options variables */
+      for (i = 0; i < job[0].optc; i++) {
+	sprintf (varname, "options:%d", i);
+	set_str_variable (varname, job[0].optv[i]);
+      }
+      set_int_variable ("options:n", job[0].optc);
+
+      /* run task[0].timeout macro, if it exists */
+      if (task[0].timeout != NULL) {
+	exec_loop (task[0].timeout);
+      }
+
+      task[0].Npending --;
+      DeleteJob (job);
+      continue;
+    }
+
+    /* reset polling clock */
+    SetTaskTimer (&job[0].last);
+  }
+  // fprintf (stderr, "check %d jobs\n", Ncheck);
+  return (next_timeout);
+}
+
+/* 
+
+  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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckPassword.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckPassword.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckPassword.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "pantasks.h"
+# define DEBUG 0
+
+static char PASSWORD[256];
+
+int InitPassword () {
+
+  VarConfig ("PASSWORD", "%s", PASSWORD);
+  return (TRUE);
+}
+
+int CheckPassword (int BindSocket) {
+
+  IOBuffer message;
+  int status;
+
+  if (DEBUG) gprint (GP_ERR, "waiting for password %s\n", PASSWORD);
+
+  status = ExpectCommand (BindSocket, strlen(PASSWORD), 0.1, &message);
+  if (status != 0) {
+    if (DEBUG) gprint (GP_ERR, "failed connection\n");
+    FreeIOBuffer (&message);
+    close (BindSocket);
+    return (FALSE);
+  }
+  if (strncmp (message.buffer, PASSWORD, strlen(PASSWORD))) {
+    if (DEBUG) gprint (GP_ERR, "invalid password\n");
+    close (BindSocket);
+    return (FALSE);
+  }
+  if (DEBUG) gprint (GP_ERR, "accepted password (%s)\n", message.buffer);
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckTasks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckTasks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckTasks.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include "pantasks.h"
+
+float CheckTasks () {
+
+  Job *job;
+  Task *task;
+  int status;
+  float time_running, next_timeout, fuzz;
+  // struct timeval now;
+
+  // actual maximum delay is controlled in job_threads.c
+  next_timeout = 1.0;
+
+  /** test all tasks: ready to test? ready to run? **/
+  while ((task = NextTask ()) != NULL) {
+
+    /*** test for all reasons we should skip this task ***/
+
+    /* task has been de-activated by the user */
+    if (!task[0].active) {
+      continue;
+    }
+    /* current time is not within valid/invalid periods */
+    if (!CheckTimeRanges (task[0].ranges, task[0].Nranges)) {
+      continue;
+    }
+    /* all allowed tasks have been run in this period */
+    if (task[0].Nmax && (task[0].Njobs >= task[0].Nmax)) {
+      continue;
+    }
+    /* too many outstanding jobs */
+    if (task[0].NpendingMax && (task[0].Npending >= task[0].NpendingMax)) {
+      continue;
+    }
+
+    /* ready to test? : check time since last exec */
+    time_running = GetTaskTimer(task[0].last, FALSE);
+    if (time_running < task[0].exec_period) {
+      // is we are to ready to run, set time to timeout, if shortest of all tasks
+      next_timeout = MIN (next_timeout, task[0].exec_period - time_running);
+      continue;
+    }
+
+    /* ready to try running the task : reset the timer */
+    next_timeout = 0.0;
+    gettimeofday (&task[0].last, (void *) NULL);
+
+    // add random offset between 0 and 5% of exec_period
+    // XXX this should be optional
+    fuzz = 0.05*task[0].exec_period*drand48() + 1e-6*task[0].last.tv_usec; 
+    task[0].last.tv_usec = 1e6*(fuzz - (int)fuzz);
+    task[0].last.tv_sec += (int) fuzz;
+
+    /* ready to run? : run task.exec macro */
+    if (task[0].exec != NULL) {
+      status = exec_loop (task[0].exec);
+      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 */
+    if (!ValidateTask (task, TRUE)) { 
+      continue;
+    }
+    
+    // gettimeofday (&now, (void *) NULL);
+    // fprintf (stderr, "t2: %d %6d  - \n", now.tv_sec, now.tv_usec);
+
+    /* construct job from task */
+    job = CreateJob (task);
+    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);
+
+    /* increment Nrun for inclusive ranges with Nmax */
+    BumpTimeRanges (task[0].ranges, task[0].Nranges);
+  }
+  return (next_timeout);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckTimeRanges.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckTimeRanges.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/CheckTimeRanges.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include "pantasks.h"
+
+/* the tested time is saved by CheckTimeRanges for a following BumpTimeRanges
+   otherwise we could have an inconsistency between valid ranges and Nrun */
+static time_t daytime, weektime, abstime;
+
+/* test if we meet all time range qualifications */
+int CheckTimeRanges (TimeRange *ranges, int Nranges) {
+
+  int i, intime, Ninclude, include, exclude, valid;
+  time_t testtime;
+  struct timeval now;
+  struct tm Now;
+
+  /* get the current time */
+  gettimeofday (&now, NULL);
+  gmtime_r (&now.tv_sec, &Now);
+  daytime  = Now.tm_sec + Now.tm_min*60 + Now.tm_hour*3600;
+  weektime = Now.tm_sec + Now.tm_min*60 + Now.tm_hour*3600 + Now.tm_wday*86400;
+  abstime  = now.tv_sec;
+
+  Ninclude = 0;
+  include = FALSE;
+  exclude = FALSE;
+  
+  for (i = 0; i < Nranges; i++) {
+    if (ranges[i].include) Ninclude ++;
+
+    switch (ranges[i].type) {
+      /* set the testtime */
+      case RANGE_ABS:
+	testtime = abstime;
+	break;
+      case RANGE_DAY:
+	testtime = daytime;
+	break;
+      case RANGE_WEEK:
+	testtime = weektime;
+	break;
+      default:
+	abort ();
+    }
+    intime = (testtime >= ranges[i].start) && (testtime <= ranges[i].stop);
+
+    /* check for more than max runs in time range */
+    if (ranges[i].include && intime && ranges[i].Nmax) {
+      if (ranges[i].Nrun >= ranges[i].Nmax) return (FALSE);
+    }
+    /* reset Nrun if we are outside of intime */
+    if (ranges[i].include && !intime && ranges[i].Nmax && ranges[i].Nrun) {
+      ranges[i].Nrun = 0;
+    }
+
+    /* is this a valid time? */
+    if ( ranges[i].include &&  intime) include = TRUE;
+    if (!ranges[i].include && !intime) exclude = TRUE;
+  }
+
+  if (Ninclude == 0) include = TRUE;
+  valid = include && !exclude;
+
+  return (valid);
+}  
+
+/* increment the number of runs for all inclusive time ranges with Nmax > 0
+   (only call when we execute a task -- after CheckTimeRanges) */
+int BumpTimeRanges (TimeRange *ranges, int Nranges) {
+
+  int i, intime;
+  time_t testtime;
+
+  /* only increment the counter for ranges which are valid */
+  for (i = 0; i < Nranges; i++) {
+    if (!ranges[i].Nmax) continue;
+    if (!ranges[i].include) continue;
+
+    switch (ranges[i].type) {
+      /* set the testtime */
+      case RANGE_ABS:
+	testtime = abstime;
+	break;
+      case RANGE_DAY:
+	testtime = daytime;
+	break;
+      case RANGE_WEEK:
+	testtime = weektime;
+	break;
+      default:
+	abort ();
+    }
+    intime = (testtime >= ranges[i].start) && (testtime <= ranges[i].stop);
+
+    /* reset Nrun if we are outside of intime */
+    if (intime) ranges[i].Nrun ++;
+  }
+  return (TRUE);
+}  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/ControllerOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 22322)
@@ -0,0 +1,594 @@
+# include "pantasks.h"
+/* adding a new host can delay controller up to a second or so */
+# define CONTROLLER_TIMEOUT 5000
+# define CONNECT_TIMEOUT 1000
+
+/* local static variables to hold the connection to the controller */
+static int ControllerStatus = FALSE;
+static int stdin_cntl, stdout_cntl, stderr_cntl;
+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;
+
+int AddHost (char *hostname, int max_threads) {
+
+  int N;
+
+  N = Nhosts;
+  Nhosts ++;
+
+  CHECK_REALLOCATE (hosts, Host, NHOSTS, Nhosts, 16);
+
+  hosts[N].hostname = strcreate (hostname);
+  hosts[N].max_threads = max_threads;
+  
+  return (TRUE);
+}
+
+int DeleteHost (char *hostname) {
+
+  int i, j;
+
+  for (i = 0; i < Nhosts; i++) {
+    if (strcmp (hosts[i].hostname, hostname)) continue;
+    
+    // delete this one
+    free (hosts[i].hostname);
+    for (j = i; j < Nhosts - 1; j++) {
+      hosts[j].hostname = hosts[j+1].hostname;
+      hosts[j].max_threads = hosts[j+1].max_threads;
+    }
+    Nhosts --;
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+/* test if the controller is running */
+int CheckControllerStatus () {
+  return (ControllerStatus);
+}
+
+/* check job / get output if done */
+int CheckControllerJob (Job *job) {
+  struct timeval start, stop;
+  float dtime;
+
+  gettimeofday (&start, (void *) NULL);
+  CheckControllerJobStatus (job);
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  /* if (VerboseMode()) gprint (GP_ERR, "check job status %f\n", dtime); */
+
+  if ((job[0].state == JOB_EXIT) || (job[0].state == JOB_CRASH)) {
+    gettimeofday (&start, (void *) NULL);
+    GetJobOutput ("stdout", job[0].pid, &job[0].stdout_buff, job[0].stdout_size);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+    /* if (VerboseMode()) gprint (GP_ERR, "get stdout %f\n", dtime); */
+
+    gettimeofday (&start, (void *) NULL);
+    GetJobOutput ("stderr", job[0].pid, &job[0].stderr_buff, job[0].stderr_size);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+    /* if (VerboseMode()) gprint (GP_ERR, "get stderr %f\n", dtime); */
+
+    gettimeofday (&start, (void *) NULL);
+    DeleteControllerJob (job);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+    /* if (VerboseMode()) gprint (GP_ERR, "delete job %f\n", dtime); */
+  }  
+  return (TRUE);
+}
+
+int DeleteControllerJob (Job *job) {
+
+  int status;
+  char cmd[128]; 
+  IOBuffer buffer;
+
+  sprintf (cmd, "delete %d", job[0].pid);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  FreeIOBuffer (&buffer);
+  return (status);
+}
+  
+/* ask controller about job status */
+int CheckControllerJobStatus (Job *job) {
+
+  int outstate, status;
+  char cmd[128], status_string[64], string[128];
+  char *p;
+  IOBuffer buffer;
+
+  sprintf (cmd, "check job %d", job[0].pid);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  if (!status) {
+    FreeIOBuffer (&buffer);
+    return (FALSE);
+  }
+
+  /** was this a valid job? **/
+  p = memstr (buffer.buffer, "job not found", buffer.Nbuffer);
+  if (p != NULL) {
+    gprint (GP_ERR, "unknown job %d\n", job[0].pid);
+    FreeIOBuffer (&buffer);
+    return (FALSE);
+  }
+
+  /** parse status message **/
+  p = memstr (buffer.buffer, "STATUS",   buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %s", status_string);
+  p = memstr (buffer.buffer, "EXITST",   buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %d", &job[0].exit_status);
+  p = memstr (buffer.buffer, "STDOUT",   buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %d", &job[0].stdout_size);
+  p = memstr (buffer.buffer, "STDERR",   buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %d", &job[0].stderr_size);
+  p = memstr (buffer.buffer, "DTIME",    buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %lf", &job[0].dtime);
+  p = memstr (buffer.buffer, "HOSTNAME", buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %s", string);
+  job[0].realhost = strcreate (string);
+  FreeIOBuffer (&buffer);
+
+  /* possible exit status values */
+  outstate = -1;
+  if (!strcmp(status_string, "BUSY"))    outstate = JOB_BUSY;
+  if (!strcmp(status_string, "DONE"))    outstate = JOB_BUSY;
+  if (!strcmp(status_string, "PENDING")) outstate = JOB_PENDING;
+  if (!strcmp(status_string, "EXIT"))    outstate = JOB_EXIT;
+  if (!strcmp(status_string, "CRASH"))   outstate = JOB_CRASH;
+  if (outstate == -1) goto escape;
+
+  job[0].state = outstate;
+  return (TRUE);
+
+  escape:
+  gprint (GP_ERR, "garbage in pcontrol reponse\n");
+  FreeIOBuffer (&buffer);
+  return (FALSE);
+}
+
+/* we read Nbytes from the host, then watch for the prompt */ 
+int GetJobOutput (char *cmd, int pid, IOBuffer *buffer, int Nbytes) {
+  
+  int i, status, Nstart;
+  char *line;
+  struct timespec request, remain;
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* flush any earlier messages */
+  ReadtoIOBuffer (buffer, stdout_cntl);
+  FlushIOBuffer (buffer);
+  Nstart = buffer[0].Nbuffer;
+
+  /* send command to get appropriate channel */
+  status = write_fmt (stdin_cntl, "%s %d\n", cmd, pid);
+
+  /* is pipe still open? */
+  // XXX call StopController() here?
+  if ((status == -1) && (errno == EPIPE)) {
+    StopController ();
+    return (CONTROLLER_DOWN);
+  }
+
+  /* read at least Nbytes, then watch for CONTROLLER_PROMPT */
+  line = NULL;
+  status = -1;
+  for (i = 0; (i < CONTROLLER_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+    status = ReadtoIOBuffer (buffer, stdout_cntl);
+    if ((buffer[0].Nbuffer - Nstart) >= Nbytes) {
+      line = memstr (buffer[0].buffer, CONTROLLER_PROMPT, buffer[0].Nbuffer);
+    }
+    if (status == -1) nanosleep (&request, &remain);
+  }
+  if (status ==  0) {
+    StopController ();
+    return (CONTROLLER_DOWN);
+  }
+  if (status == -1) return (CONTROLLER_HUNG);
+
+  /* if (VerboseMode()) gprint (GP_ERR, "message received (GetJobOutput : %s)\n", cmd);   */
+  /* drop extra bytes from pcontrol (not pclient:job) */
+  buffer[0].Nbuffer = Nstart + Nbytes;
+  if (buffer[0].Nalloc > buffer[0].Nbuffer) {
+    bzero (buffer[0].buffer + buffer[0].Nbuffer, buffer[0].Nalloc - buffer[0].Nbuffer);
+  }
+  return (CONTROLLER_GOOD);
+}
+
+/* submitting a job to the controller automatically starts controller */
+int SubmitControllerJob (Job *job) {
+
+  int i, Nchar, status;
+  char *cmd, *p, string[64];
+  IOBuffer buffer;
+
+  if (job[0].task[0].host == NULL) return (FALSE); 
+
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to start pcontrol\n");
+    return (FALSE);
+  }
+
+  /** construct the line to be sent to the controller **/
+
+  /* determine the total line length */
+  Nchar = 0;
+  for (i = 0; i < job[0].argc; i++) {
+    Nchar += strlen (job[0].argv[i]) + 1;
+  }
+  if (job[0].task[0].host) {
+    Nchar += strlen (job[0].task[0].host) + 1;
+  }
+  Nchar += 10;
+  ALLOCATE (cmd, char, Nchar);
+  bzero (cmd, Nchar);
+
+  /* construct the controller command portion */
+  if (!strcasecmp (job[0].task[0].host, "ANYHOST")) {
+    sprintf (cmd, "job");
+  } else {
+    if (job[0].task[0].host_required) {
+      sprintf (cmd, "job +host %s", job[0].task[0].host);
+    } else {
+      sprintf (cmd, "job -host %s", job[0].task[0].host);
+    }
+  }
+
+  /* add the command arguments */
+  for (i = 0; i < job[0].task[0].argc; i++) {
+    strcat (cmd, " ");
+    strcat (cmd, job[0].task[0].argv[i]);
+  }
+
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  free (cmd);
+
+  /* extract the job PID from the controller response */
+  p = memstr (buffer.buffer, "JobID", buffer.Nbuffer);
+  if (p == NULL) {
+    gprint (GP_ERR, "missing PID in pcontrol message : programming error\n");
+    gprint (GP_ERR, "ControllerCommand returns: %d\n", status);
+    gprint (GP_ERR, "ControllerCommand response: %s\n", buffer.buffer);
+    exit (1);
+  }
+  sscanf (p, "%*s %s", string);
+  job[0].pid = atoi (string);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
+
+int StartController () {
+
+  char *p;
+  char **argv, cmd[128];
+  int i, pid, status;
+  int stdin_fd[2], stdout_fd[2], stderr_fd[2];
+  IOBuffer buffer;
+
+  if (ControllerStatus) return (TRUE);
+
+  if (VarConfig ("CONTROLLER", "%s", cmd) == NULL) strcpy (cmd, "pcontrol");
+
+  if (hosts == NULL) {
+    NHOSTS = 16;
+    ALLOCATE (hosts, Host, NHOSTS);
+  }
+
+  bzero (stdin_fd,  2*sizeof(int));
+  bzero (stdout_fd, 2*sizeof(int));
+  bzero (stderr_fd, 2*sizeof(int));
+
+  if (pipe (stdin_fd)  < 0) goto pipe_error;
+  if (pipe (stdout_fd) < 0) goto pipe_error;
+  if (pipe (stderr_fd) < 0) goto pipe_error;
+
+  ALLOCATE (argv, char *, 2);
+  argv[0] = cmd;
+  argv[1] = 0;
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    gprint (GP_LOG, "starting controller connection\n");
+
+    /* close the other ends of the pipes */
+    close (stdin_fd[1]);
+    close (stdout_fd[0]);
+    close (stderr_fd[0]);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (stdin_fd[0],  STDIN_FILENO);
+    dup2 (stdout_fd[1], STDOUT_FILENO);
+    dup2 (stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    status = execvp (argv[0], argv); 
+    exit (1);
+  }
+  free (argv);
+
+  /* close the other ends of the pipes */
+  close (stdin_fd[0]);  stdin_fd[0]  = 0;
+  close (stdout_fd[1]); stdout_fd[1] = 0;
+  close (stderr_fd[1]); stderr_fd[1] = 0;
+
+  /* make the pipes non-blocking */
+  fcntl (stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+
+  /* perform handshake with controller to verify alive & running */
+  /** this handshake is similar to ControllerCommand, but has important differences **/
+  InitIOBuffer (&buffer, 0x100);
+
+  /* send handshake command */
+  status = write_fmt (stdin_fd[1], "echo CONNECTED\n");
+  if ((status == -1) && (errno == EPIPE)) goto pipe_error;
+
+  /* try to get evidence connection is alive - wait upto a few seconds */
+  /* connection is likely slow; don't bother with nanosleep here */
+  p = NULL;
+  status = -1;
+  for (i = 0; (i < CONNECT_TIMEOUT) && (status != 0) && (p == NULL); i++) {
+    status = ReadtoIOBuffer (&buffer, stdout_fd[0]);
+    p = memstr (buffer.buffer, "CONNECTED", buffer.Nbuffer);
+    usleep (50000); // wait for controller to start up
+  }
+  if (status == 0) goto pipe_error;
+  if (status == -1) goto io_error;
+  FreeIOBuffer (&buffer);
+
+  /* set local static vars to pipe connections */
+  stdin_cntl  = stdin_fd[1];
+  stdout_cntl = stdout_fd[0];
+  stderr_cntl = stderr_fd[0];
+
+  InitIOBuffer (&stdout_buffer, 0x100);
+  InitIOBuffer (&stderr_buffer, 0x100);
+
+  ControllerPID = pid;
+  ControllerStatus = TRUE;
+  gprint (GP_LOG, "Connected\n");
+  return (TRUE);
+
+pipe_error:
+  perror ("pipe error:");
+  goto close_pipes;
+
+io_error:
+  gprint (GP_ERR, "timeout while connecting\n");
+  goto close_pipes;
+
+close_pipes:
+  if (stdin_fd[0]  != 0) close (stdin_fd[0]);
+  if (stdin_fd[1]  != 0) close (stdin_fd[1]);
+  if (stdout_fd[0] != 0) close (stdout_fd[0]);
+  if (stdout_fd[1] != 0) close (stdout_fd[1]);
+  if (stderr_fd[0] != 0) close (stderr_fd[0]);
+  if (stderr_fd[1] != 0) close (stderr_fd[1]);
+  return (FALSE);
+}
+
+int ControllerCommand (char *cmd, char *response, IOBuffer *buffer) {
+
+  int i, j, status;
+  char *line;
+  struct timespec request, remain;
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  ReadtoIOBuffer (buffer, stdout_cntl);
+  FlushIOBuffer (buffer);
+
+  /* send command, is pipe still open? */
+  status = write_fmt (stdin_cntl, "%s\n", cmd);
+  if ((status == -1) && (errno == EPIPE)) {
+    StopController ();
+    gprint (GP_ERR, "controller is down, restarting\n");
+    if (!RestartController ()) {
+      return (FALSE);
+    }
+    return (FALSE);
+  }
+  
+  /* for commands which don't return a prompt, don't look for one */
+  if (response == NULL) {
+      return (TRUE);
+  }
+
+  /* watch for response - wait up to 1 second */
+  line = NULL;
+  status = -1;
+  for (j = 0; (status == -1) && (j < 10); j++) {
+      for (i = 0; (i < CONTROLLER_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+	  status = ReadtoIOBuffer (buffer, stdout_cntl);
+	  line = memstr (buffer[0].buffer, response, buffer[0].Nbuffer);
+	  if (status == -1) nanosleep (&request, &remain);
+      }
+      if (status ==  0) {
+	  StopController ();
+	  gprint (GP_ERR, "controller is down\n");
+	  return (FALSE);
+      }
+      if (status == -1) {
+	  gprint (GP_ERR, "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");
+      return (FALSE);
+  }
+
+  /* need to strip off the prompt */
+  line = memstr (buffer[0].buffer, response, buffer[0].Nbuffer);
+  if (line != NULL) {
+    buffer[0].Nbuffer = line - buffer[0].buffer;
+    bzero (buffer[0].buffer + buffer[0].Nbuffer, buffer[0].Nalloc - buffer[0].Nbuffer);
+  }
+  /* if (VerboseMode()) gprint (GP_ERR, "message received, %d cycles\n", i); */
+  return (TRUE);
+}
+
+int CheckControllerOutput () {
+
+  int Nread;
+
+  if (!ControllerStatus) return (TRUE);
+
+  /* read stdout buffer */
+  Nread = ReadtoIOBuffer (&stdout_buffer, stdout_cntl);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      gprint (GP_ERR, "serious IO error\n");
+      exit (2);
+    case -1:  /* no data in pipe */
+    case 0:   /* pipe is closed, change child state? **/
+    default:  /* data in pipe */
+      break;
+  }
+  
+  /* read stderr buffer */
+  Nread = ReadtoIOBuffer (&stderr_buffer, stderr_cntl);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      gprint (GP_ERR, "serious IO error\n");
+      exit (2);
+    case -1:  /* no data in pipe */
+    case 0:   /* pipe is closed, change child state? **/
+    default:  /* data in pipe */
+      break;
+  }
+  return (TRUE);
+}
+
+int PrintControllerOutput () {
+
+  gprint (GP_LOG, "--- stdout ---\n");
+  gwrite (stdout_buffer.buffer, 1, stdout_buffer.Nbuffer, GP_LOG);
+  gprint (GP_LOG, "--- stderr ---\n");
+  gwrite (stderr_buffer.buffer, 1, stderr_buffer.Nbuffer, GP_LOG);
+  gprint (GP_LOG, "---  done  ---\n");
+  return (TRUE);
+}
+
+int FlushControllerOutput () {
+
+  FlushIOBuffer (&stdout_buffer);
+  FlushIOBuffer (&stderr_buffer);
+
+  return (TRUE);
+}
+
+int KillControllerJob (Job *job) {
+
+  int status;
+  char cmd[128];
+  IOBuffer buffer;
+
+  if (!ControllerStatus) return (TRUE);
+
+  sprintf (cmd, "kill %d", job[0].pid);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  FreeIOBuffer (&buffer);
+  return (status);
+
+  /** need to interpret output message & free things **/
+}
+
+int QuitController () {
+
+  int status;
+  char cmd[128];
+  IOBuffer buffer;
+
+  if (!ControllerStatus) return (TRUE);
+
+  sprintf (cmd, "quit");
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, NULL, &buffer);
+  FreeIOBuffer (&buffer);
+
+  /* the quit command does not return a prompt, 
+     check that the controller exited */
+  StopController ();
+  return (TRUE);
+}
+
+int StopController () {
+
+  int i, waitstatus, result;
+
+  if (!ControllerStatus) return (TRUE);
+
+  ControllerStatus = FALSE;
+  result = waitpid (ControllerPID, &waitstatus, WNOHANG);
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for controller to exit
+    result = waitpid (ControllerPID, &waitstatus, WNOHANG);
+  }
+  ControllerPID = 0;
+  close (stdin_cntl);
+  close (stdout_cntl);
+  close (stderr_cntl);
+  FreeIOBuffer (&stdout_buffer);
+  FreeIOBuffer (&stderr_buffer);
+  return (TRUE);
+}
+
+int RestartController () {
+
+  int i, status;
+  char command[256];
+  IOBuffer buffer;
+
+  gprint (GP_ERR, "controller is down, restarting\n");
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to re-start pcontrol\n");
+    return (FALSE);
+  }
+
+  InitIOBuffer (&buffer, 0x100);
+
+  // XXX lock the host table? SerialThreadLock ();
+  fprintf (stderr, "controller is down, restarting\n");
+  for (i = 0; i < Nhosts; i++) {
+    fprintf (stderr, "controller host add %s -threads %d\n", hosts[i].hostname, hosts[i].max_threads);
+    snprintf (command, 256, "host add %s -threads %d\n", hosts[i].hostname, hosts[i].max_threads);
+    status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  }
+  // SerialThreadUnlock ();
+
+  if (status) gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+
+  FreeIOBuffer (&buffer);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/InputQueue.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/InputQueue.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/InputQueue.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "pantasks.h"
+
+static int Ninputs = 0;
+static int NINPUTS = 0;
+static char **inputs;
+
+void InitInputs () {
+
+  Ninputs = 0;
+  NINPUTS = 10;
+  ALLOCATE (inputs, char *, NINPUTS);
+}
+
+void FreeInputs () {
+  int i;
+  for (i = 0; i < Ninputs; i++) {
+    free (inputs[i]);
+  }
+  free (inputs);
+}
+
+/* add this input to the inputs table */
+void AddNewInput (char *input) {
+
+  SerialThreadLock ();
+  if (DEBUG) gprint (GP_LOG, "adding a new input (%s)\n", input);
+  inputs[Ninputs] = input;
+  Ninputs ++;
+  if (Ninputs >= NINPUTS - 1) {
+    NINPUTS += 10;
+    REALLOCATE (inputs, char *, NINPUTS);
+  }
+  if (DEBUG) gprint (GP_LOG, "done new input (%s)\n", input);
+  SerialThreadUnlock ();
+}
+
+/* remove this input from the inputs table */
+int DeleteInput (char *input) {
+
+  int i, j;
+
+  if (DEBUG) gprint (GP_LOG, "deleting an input (%s)\n", input);
+  for (i = 0; i < Ninputs; i++) {
+    if (inputs[i] == input) {
+      free (inputs[i]);
+      for (j = i; j < Ninputs - 1; j++) {
+	inputs[j] = inputs[j+1];
+      }
+      Ninputs --;
+      if ((Ninputs > 10) && (Ninputs / 2 < NINPUTS)) {
+	NINPUTS = Ninputs + 10;
+	REALLOCATE (inputs, char *, NINPUTS);
+      }
+      if (DEBUG) gprint (GP_LOG, "deleted an input\n");
+      return TRUE;
+    }
+  }
+  // did not find the input
+  return FALSE;
+}
+
+/* if any inputs are pending, run one */
+void CheckInputs () {
+
+  int Nbytes, status;
+  char *input, *line, *outline, tmp;
+
+  if (Ninputs < 1) return;
+
+  input = inputs[0];
+  if (DEBUG) gprint (GP_LOG, "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);
+
+  status = command (line, &outline, TRUE);
+  free (outline);
+  
+  DeleteInput (input);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/JobIDOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/JobIDOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/JobIDOps.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "pantasks.h"
+
+# define MAX_N_JOBS 1000
+static char *JobIDList;
+static int   JobIDPtr;
+
+void InitJobIDs () {
+
+  JobIDPtr = 0;
+  ALLOCATE (JobIDList, char, MAX_N_JOBS);
+  bzero (JobIDList, MAX_N_JOBS*sizeof(char));
+}  
+
+void FreeJobIDs () {
+  free (JobIDList);
+}
+
+/* return next unique ID, recycle every MAX_N_JOBS */
+int NextJobID () {
+
+  int Ntry;
+
+  JobIDPtr ++;
+  if (JobIDPtr >= MAX_N_JOBS) JobIDPtr = 0;
+
+  Ntry = 0;
+  while (JobIDList[JobIDPtr]) {
+    Ntry ++;
+    JobIDPtr ++;
+    if (JobIDPtr >= MAX_N_JOBS) JobIDPtr = 0;
+    if (Ntry == MAX_N_JOBS) return (-1);
+  }
+  JobIDList[JobIDPtr] = TRUE;
+  return (JobIDPtr);
+}
+
+int FreeJobID (int JobID) {
+
+  if (JobID < 0) return (FALSE);
+  if (JobID >= MAX_N_JOBS) return (FALSE);
+
+  JobIDList[JobID] = FALSE;
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/JobOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/JobOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/JobOps.c	(revision 22322)
@@ -0,0 +1,292 @@
+# include "pantasks.h"
+
+static Job **jobs;
+static int   Njobs;
+static int   NJOBS;
+
+/* counter marking job being visited by the run loop */
+static int   ActiveJob;
+
+/* set up the jobs list */
+void InitJobs () {
+  NJOBS = 20;
+  Njobs = 0;
+  ALLOCATE (jobs, Job *, NJOBS);
+  ActiveJob = -1;
+}
+
+/* free all jobs, only used on shutdown */
+void FreeJobs () {
+  int i;
+  for (i = 0; i < Njobs; i++) {
+    FreeJob (jobs[i]);
+  }
+  free (jobs);
+}
+
+/* provide a mechanism to loop over the list of jobs */
+Job *NextJob () {
+  
+  Job *job;
+
+  ActiveJob ++;
+  if (ActiveJob < 0) ActiveJob = 0;
+  if (ActiveJob >= Njobs) {
+    ActiveJob = -1;
+    return (NULL);
+  }
+  job = jobs[ActiveJob];
+  return (job);
+}
+
+/* return job with given ID */
+Job *FindJob (int JobID) {
+
+  int i;
+
+  /* return job with matching JobID */
+  for (i = 0; i < Njobs; i++) {
+    if (jobs[i][0].JobID == JobID) {
+      return (jobs[i]);
+    }
+  }
+  return (NULL);
+}  
+
+/* return job with given controller Job ID */
+Job *FindControllerJob (int JobID) {
+
+  int i;
+
+  /* return job with matching JobID */
+  for (i = 0; i < Njobs; i++) {
+    if (jobs[i][0].mode == JOB_LOCAL) continue;
+    if (jobs[i][0].pid  == JobID) {
+      return (jobs[i]);
+    }
+  }
+  return (NULL);
+}  
+
+/* list known jobs */
+void ListJobs () {
+
+  int i;
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  gprint (GP_LOG, "\n");
+  if (Njobs == 0) {
+    gprint (GP_LOG, " no defined jobs\n");
+    return;
+  }
+
+  gprint (GP_LOG, " Jobs in Pantasks Queue\n");
+  for (i = 0; i < Njobs; i++) {
+    gprint (GP_LOG, " %d: %-15s %5d %20s (%lx)\n", Njobs, jobs[i][0].task[0].name, jobs[i][0].JobID, jobs[i][0].argv[0], (long) jobs[i][0].argv);
+  }
+
+  /* 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;
+}
+
+/* make a new job from a task */
+Job *CreateJob (Task *task) {
+  
+  int i;
+  Job *job;
+  
+  ALLOCATE (job, Job, 1);
+
+  job[0].JobID = NextJobID ();
+  job[0].pid = 0;
+  job[0].mode = JOB_LOCAL;
+  if (task[0].host != NULL) {
+    job[0].mode = JOB_CONTROLLER;
+  }
+
+  /* we need our own copy of task[0].argv argc is the number of valid args, like the usual command line.  we
+   *  allocate one extra element, with value 0 to be passed to execvp
+   */
+  job[0].argc = task[0].argc;
+  ALLOCATE (job[0].argv, char *, MAX (task[0].argc + 1, 1));
+  for (i = 0; i < task[0].argc; i++) {
+    job[0].argv[i] = strcreate (task[0].argv[i]);
+  }
+  job[0].argv[i] = 0;
+
+  /* we need our own copy of task[0].optv: optc is the number of valid opts.  */
+  job[0].optc = task[0].optc;
+  ALLOCATE (job[0].optv, char *, MAX (task[0].optc, 1));
+  for (i = 0; i < job[0].optc; i++) {
+    job[0].optv[i] = strcreate (task[0].optv[i]);
+  }
+
+  /* Other data from the task is needed by the job. We carry a pointer back to the task.  Changes to an
+     executing task are applied to the existing jobs (exit macros, poll_period, timeout) */
+
+  job[0].task = task;
+  job[0].realhost = NULL;
+  
+  /* if we decide we need to be able to dynamically set task qualities (like host, timeouts, etc), the we will
+     need to have matched entries to these quantites in the job structure */
+
+  InitIOBuffer (&job[0].stdout_buff, 0x100);
+  InitIOBuffer (&job[0].stderr_buff, 0x100);
+
+  job[0].stdout_dump = NULL;
+  job[0].stderr_dump = NULL;
+  if (task[0].stdout_dump != NULL) job[0].stdout_dump = strcreate (task[0].stdout_dump);
+  if (task[0].stderr_dump != NULL) job[0].stderr_dump = strcreate (task[0].stderr_dump);
+
+  job[0].stdout_fd = -1;
+  job[0].stderr_fd = -1;
+
+  jobs[Njobs] = job;
+  Njobs ++;
+  if (Njobs == NJOBS) {
+    NJOBS += 20;
+    REALLOCATE (jobs, Job *, NJOBS);
+  }
+  return (jobs[Njobs-1]);
+}
+
+void FreeJob (Job *job) {
+  
+  int i;
+
+  if (job == NULL) return;
+  
+  FreeJobID (job[0].JobID);
+
+  for (i = 0; i < job[0].argc; i++) {
+    free (job[0].argv[i]);
+  }
+  free (job[0].argv);
+
+  for (i = 0; i < job[0].optc; i++) {
+    free (job[0].optv[i]);
+  }
+  free (job[0].optv);
+
+  if (job[0].stdout_fd >= 0) close (job[0].stdout_fd);
+  if (job[0].stderr_fd >= 0) close (job[0].stderr_fd);
+
+  if (job[0].stdout_dump != NULL) free (job[0].stdout_dump);
+  if (job[0].stderr_dump != NULL) free (job[0].stderr_dump);
+  if (job[0].realhost != NULL) free (job[0].realhost);
+
+  FreeIOBuffer (&job[0].stdout_buff);
+  FreeIOBuffer (&job[0].stderr_buff);
+  free (job);
+  return;
+}
+
+/* delete the job from the job list & adjust ActiveJob counter */
+int DeleteJob (Job *job) {
+
+  int i, Nm;
+
+  Nm = -1;
+  for (i = 0; i < Njobs; i++) {
+    if (job == jobs[i]) {
+      Nm = i;
+      break;
+    }
+  }
+  if (Nm == -1) {
+    gprint (GP_ERR, "programming error: job not found\n");
+    return (FALSE);
+  }
+
+  FreeJob (jobs[Nm]);
+  for (i = Nm; i < Njobs - 1; i++) {
+    jobs[i] = jobs[i + 1];
+  }
+  Njobs --;
+
+  /* adjust active job number */
+  if (ActiveJob >= Nm) {
+    ActiveJob --;
+  }
+
+  job[0].task[0].Npending --;
+  return (TRUE);
+}
+
+int FlushJobs () {
+
+  int i;
+
+  // kill outstanding jobs
+  for (i = 0; i < Njobs; i++) {
+    if (jobs[i][0].mode == JOB_LOCAL) {
+      if (!KillLocalJob (jobs[i])) {
+	jobs[i][0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", jobs[i][0].pid);
+      }
+    } else {
+      if (!KillControllerJob (jobs[i])) {
+	jobs[i][0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", jobs[i][0].pid);
+      }
+    }    
+    jobs[i][0].task[0].Npending = 0;
+    FreeJob (jobs[i]);
+  }
+    
+  Njobs = 0;
+  NJOBS = 20;
+  REALLOCATE (jobs, Job *, NJOBS);
+  ActiveJob = -1;
+  return (TRUE);
+}
+
+int SubmitJob (Job *job) {
+
+  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);
+  } else {
+    SubmitControllerJob (job);
+  }
+
+  /* reset clock for start and poll-test */
+  gettimeofday (&job[0].start, (void *) NULL);
+  job[0].last = job[0].start;
+  job[0].state = JOB_PENDING;
+  return (TRUE);
+}
+
+int CheckJob (Job *job) {
+
+  /* add checks for timeouts */
+
+  if (job[0].mode == JOB_LOCAL) {
+    CheckLocalJob (job);
+  } else {
+    /* controller jobs are now checked en masse by CheckController */
+    /* CheckControllerJob (job); */
+  }
+  return (job[0].state);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/ListenClients.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/ListenClients.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/ListenClients.c	(revision 22322)
@@ -0,0 +1,182 @@
+# include "pantasks.h"
+# define DEBUG 0
+
+static int NCLIENTS;
+static int Nclients;
+static int *clients;
+static IOBuffer **buffers;
+
+void InitClients () {
+
+  Nclients = 0;
+  NCLIENTS = 10;
+  ALLOCATE (clients, int, NCLIENTS);
+  ALLOCATE (buffers, IOBuffer *, NCLIENTS);
+}
+
+/* add this client to the client table, create an IOBuffer for it */
+void AddNewClient (int client) {
+
+  if (DEBUG) gprint (GP_LOG, "adding a new client (%d)\n", client);
+  clients[Nclients] = client;
+  ALLOCATE (buffers[Nclients], IOBuffer, 1);
+  InitIOBuffer(buffers[Nclients], 256);
+  Nclients ++;
+  if (Nclients >= NCLIENTS - 1) {
+    NCLIENTS += 10;
+    REALLOCATE (clients, int, NCLIENTS);
+    REALLOCATE (buffers, IOBuffer *, NCLIENTS);
+  }
+}
+
+/* add this client to the client table, create an IOBuffer for it */
+int DeleteClient (int client) {
+
+  int i, j;
+
+  if (DEBUG) gprint (GP_LOG, "deleting a client (%d)\n", client);
+  for (i = 0; i < Nclients; i++) {
+    if (clients[i] == client) {
+      FreeIOBuffer (buffers[i]);
+      free (buffers[i]);
+      close (clients[i]);
+      for (j = i; j < Nclients - 1; j++) {
+	clients[j] = clients[j+1];
+	buffers[j] = buffers[j+1];
+      }
+      Nclients --;
+      if ((Nclients > 10) && (Nclients / 2 < NCLIENTS)) {
+	NCLIENTS = Nclients + 10;
+	REALLOCATE (clients, int, NCLIENTS);
+	REALLOCATE (buffers, IOBuffer *, NCLIENTS);
+      }
+      return TRUE;
+    }
+  }
+  // did not find the client
+  return FALSE;
+}
+
+/* select for messages from the current clients; wait for 0.5s before updating client list */
+void *ListenClients (void *data) {
+  
+  int i, Ncurrent, Nmax, status, Nread;
+  char *line, log_stdout[128], log_stderr[128];
+  fd_set fdSet;
+  struct timeval timeout;
+  IOBuffer *outbuffer;
+
+  InitClients ();
+  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");
+  }
+
+  while (1) {
+
+    /* Wait up to 0.5 second - need to timeout to update client list */
+    /* timeout gets mucked: need to reset before each select */
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 500000;
+
+    /* place all of the clients in the fdSet */
+    Ncurrent = Nclients;
+    Nmax = 0;
+    FD_ZERO (&fdSet);
+    for (i = 0; i < Ncurrent; i++) {
+      Nmax = MAX (Nmax, clients[i]);
+      FD_SET (clients[i], &fdSet);
+    }    
+    Nmax ++;
+
+    /* block until we have some data on the pipes (or timeout) */
+    if (DEBUG) gprint (GP_ERR, "listening to %d clients\n", Ncurrent);
+    status = select (Nmax, &fdSet, NULL, NULL, &timeout);
+    if (status == -1) {
+      perror("select()");
+      return (FALSE);
+    }
+
+    /* if no data, update client list, wait for another select */
+    if (status <= 0) continue;
+
+    /* loop over the clients with data */
+    for (i = 0; i < Ncurrent; i++) {
+      /* if client has no data, skip it */
+      if (!FD_ISSET(clients[i], &fdSet)) continue;
+
+      /* read until the pipe is empty: 0 is closed, -1 is empty, -2 is error */
+      Nread = 1;
+      while (Nread > 0) {
+	Nread = ReadtoIOBuffer (buffers[i], clients[i]);	
+      }
+      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");
+	DeleteClient (clients[i]);
+	continue;
+      }
+
+      if (DEBUG) gprint (GP_ERR, "read %d total bytes\n", buffers[i][0].Nbuffer);
+
+      /* see if we have a complete message waiting; if not, keep waiting for messages */
+      line = CheckForMessage (buffers[i]);
+      if (line == NULL) continue;
+
+      /* we now have a possible command from the client: run it */
+      /* in this thread, we set the print output destination to be an
+	 internal buffer, which we dump at the end of the execution */
+      /* the commands sent to the server should not have ; */
+      stripwhite (line);
+      if (*line) {
+
+	/* set buffers for the output for this client */
+	gprintSetBuffer (GP_LOG);
+	gprintSetBuffer (GP_ERR);
+
+	/* run the command, return the exit status */
+	status = multicommand (line);
+	SendMessage (clients[i], "STATUS %d", status);
+
+	// return the stderr messages first
+	outbuffer = gprintGetBuffer (GP_ERR);
+	if (outbuffer) {
+	  SendMessageFixed (clients[i], outbuffer[0].Nbuffer, outbuffer[0].buffer);
+	} else {
+	  SendMessageFixed (clients[i], 0, "");
+	}	  
+
+	// return the stdout messages first
+	outbuffer = gprintGetBuffer (GP_LOG);
+	if (outbuffer) {
+	  SendMessageFixed (clients[i], outbuffer[0].Nbuffer, outbuffer[0].buffer);
+	} else {
+	  SendMessageFixed (clients[i], 0, "");
+	}	  
+	
+	/* clear and reset the output buffers to their last output file names */
+	gprintSetFileAllThreads (GP_LOG, NULL);
+	gprintSetFileAllThreads (GP_ERR, NULL);
+      }
+      free (line);
+    }
+    /* if Nread == -2, we probably need to drop the client */
+    /* check if we need to drop / remove any clients */
+    /* check if we need to shut down the thread */
+  }
+}
+
+/* the AddClient commands are issued by the parent thread
+   the value of Nclients may increase after we check it here. 
+   only this thread is allowed to decrease Nclients and remove
+   a client from the table */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/LocalJob.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/LocalJob.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/LocalJob.c	(revision 22322)
@@ -0,0 +1,224 @@
+# include "pantasks.h"
+
+/* local jobs are forked in the background 
+   we might need to limit the maximum number of local jobs.
+   should we have a queue/stack of pending local jobs, much
+   like controller? */
+
+/* update current state, drain stdout/stderr buffers */
+int CheckLocalJob (Job *job) {
+
+  int Nread;
+
+  // XXX do something useful with exit status?
+  CheckLocalJobStatus (job);
+
+  if ((job[0].state == JOB_EXIT) || (job[0].state == JOB_CRASH)) {
+    if (DEBUG) fprintf (stderr, "empty buffer 0: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+    EmptyIOBuffer (&job[0].stdout_buff, 10, job[0].stdout_fd);
+    EmptyIOBuffer (&job[0].stderr_buff, 10, job[0].stderr_fd);
+    if (DEBUG) fprintf (stderr, "empty buffer 1: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+    close (job[0].stdout_fd);
+    close (job[0].stderr_fd);
+    job[0].stdout_fd = -1; // prevent FreeJob from trying to close again
+    job[0].stderr_fd = -1; // prevent FreeJob from trying to close again
+  } else {
+    /* read stdout buffer */
+    if (DEBUG) fprintf (stderr, "read buffer 0: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+    while ((Nread = ReadtoIOBuffer (&job[0].stdout_buff, job[0].stdout_fd)) > 0);
+    switch (Nread) {
+      case -2:  /* error in read (programming error?  system level error?) */
+	gprint (GP_ERR, "serious IO error\n");
+	exit (2);
+      case -1:  /* no data in pipe */
+      case 0:   /* pipe is closed, change child state? **/
+      default:  /* data in pipe */
+	// fprintf (stderr, "read %d bytes (Nblock: %d, Nbuffer: %d)\n", Nread, job[0].stdout.Nblock, job[0].stdout.Nbuffer);
+	break;
+    }
+    if (DEBUG) fprintf (stderr, "read buffer 1: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+  
+    /* read stderr buffer */
+    while ((Nread = ReadtoIOBuffer (&job[0].stderr_buff, job[0].stderr_fd)) > 0);
+    switch (Nread) {
+      case -2:  /* error in read (programming error?  system level error?) */
+	gprint (GP_ERR, "serious IO error\n");
+	exit (2);
+      case -1:  /* no data in pipe */
+      case 0:   /* pipe is closed, change child state? **/
+      default:  /* data in pipe */
+	break;
+    }
+  }
+  return (TRUE);
+}
+
+int CheckLocalJobStatus (Job *job) {
+
+  int result, waitstatus;
+
+  /* check local job status */
+  result = waitpid (job[0].pid, &waitstatus, WNOHANG);
+  switch (result) {
+    case -1:  /* error with waitpid */
+      switch (errno) {
+	case ECHILD:
+	  gprint (GP_ERR, "unknown PID, not a child proc\n");
+	  gprint (GP_ERR, "did process already exit?  programming error?\n");
+	  job[0].state = JOB_NONE;
+	  job[0].exit_status = 0;
+	  return (FALSE);
+	case EINVAL:
+	  gprint (GP_ERR, "error EINVAL (waitpid): programming error\n");
+	  exit (1);
+	case EINTR:
+	  gprint (GP_ERR, "error EINTR (waitpid): programming error\n");
+	  exit (1);
+	default:
+	  gprint (GP_ERR, "unknown error for waitpid (%d): programming error\n", errno);
+	  exit (1);
+      }
+      break;
+      
+    case 0:  /* process not exited */
+      job[0].state = JOB_BUSY;
+      job[0].exit_status = 0;
+      return (TRUE);
+
+    default:
+      if (result != job[0].pid) {
+	gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, job[0].pid);
+	exit (1);
+      }
+      if (WIFEXITED(waitstatus)) {
+	job[0].state = JOB_EXIT;
+	job[0].exit_status = WEXITSTATUS(waitstatus);
+      }
+      if (WIFSIGNALED(waitstatus)) {
+	job[0].state = JOB_CRASH;
+	job[0].exit_status = WTERMSIG(waitstatus);
+      }
+      if (WIFSTOPPED(waitstatus)) {
+	gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+	exit (1);
+      }
+      job[0].dtime = GetTaskTimer (job[0].start, FALSE);
+      break;
+  }
+  return (FALSE);
+}
+
+/* this could be written a just a one-way pipe */
+int SubmitLocalJob (Job *job) {
+
+  int status, pid;
+  int stdout_fd[2], stderr_fd[2];
+
+  bzero (stdout_fd, 2*sizeof(int));
+  bzero (stderr_fd, 2*sizeof(int));
+
+  if (pipe (stdout_fd) < 0) goto pipe_error;
+  if (pipe (stderr_fd) < 0) goto pipe_error;
+
+  // XXX nothing to be read at this point
+  // other threads are already halted here.
+  fflush (stdout);
+  fflush (stderr);
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    if (VerboseMode()) gprint (GP_ERR, "starting local job\n");
+
+    /* close the other ends of the pipes */
+    close (stdout_fd[0]);
+    close (stderr_fd[0]);
+
+    // XXX neither of these work to empty the child stdout buffer
+    // fflush (stdout);
+    // fflush (stderr);
+    // close (STDOUT_FILENO);
+    // close (STDERR_FILENO);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (stdout_fd[1], STDOUT_FILENO);
+    dup2 (stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    // XXX allow the parent time to read the stdout/stderr buffers
+    usleep (10000);
+
+    status = execvp (job[0].argv[0], job[0].argv); 
+    exit (1);
+  }
+  if (VerboseMode()) gprint (GP_ERR, "local job launched\n");
+
+  /* close the other ends of the pipes */
+  close (stdout_fd[1]); stdout_fd[1] = 0;
+  close (stderr_fd[1]); stderr_fd[1] = 0;
+
+  /* make the pipes non-blocking */
+  fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+
+  // XXX There seems to always be extra data on the pipe, specifically the 
+  // stdout buffer from the parent.  If I read it here, then it clears out that data.
+  // But, how can I be sure I will not start reading data from the exec-ed process?
+
+  { // test read of the stdout buffer
+    int Nread;
+    char buffer[0x1000];
+
+    Nread = read (stdout_fd[0], buffer, 0x1000);
+    if (DEBUG) fprintf (stderr, "read from stdout before exec: %d bytes\n", Nread);
+
+    Nread = read (stderr_fd[0], buffer, 0x1000);
+    if (DEBUG) fprintf (stderr, "read from stderr before exec: %d bytes\n", Nread);
+  }
+
+  job[0].stdout_fd = stdout_fd[0];
+  job[0].stderr_fd = stderr_fd[0];
+  job[0].pid = pid;
+
+  return (TRUE);
+
+pipe_error:
+  perror ("pipe error:");
+  if (stdout_fd[0] != 0) close (stdout_fd[0]);
+  if (stdout_fd[1] != 0) close (stdout_fd[1]);
+  if (stderr_fd[0] != 0) close (stderr_fd[0]);
+  if (stderr_fd[1] != 0) close (stderr_fd[1]);
+  return (FALSE);
+}
+
+/* should this function close the fd's? */
+int KillLocalJob (Job *job) {
+
+  int i, result, waitstatus;
+
+  if (job[0].state != JOB_BUSY) return (TRUE);
+
+  /* send SIGTERM signal to job */
+  kill (job[0].pid, SIGTERM);
+  result = 0;
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for job to exit
+    result = waitpid (job[0].pid, &waitstatus, WNOHANG);
+  }
+  if (result) return (TRUE);
+
+  /* send SIGKILL signal to job */
+  kill (job[0].pid, SIGKILL);
+  result = 0;
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for job to exit
+    result = waitpid (job[0].pid, &waitstatus, WNOHANG);
+  }
+  if (result) return (TRUE);
+
+  /* total failure, don't reset */
+  return (FALSE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/Makefile	(revision 22322)
@@ -0,0 +1,128 @@
+default: pantasks pantasks_client pantasks_server
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/pantasks
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+DATA    =       $(DESTDATA)/pantasks
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata 
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# sched user commands and support functions ########################
+
+client = \
+$(SRC)/pantasks_client.$(ARCH).o \
+$(SRC)/client_shell.$(ARCH).o \
+$(SRC)/invalid.$(ARCH).o \
+$(SRC)/server_shutdown.$(ARCH).o \
+$(SRC)/server_disconnect.$(ARCH).o \
+$(SRC)/server_connect.$(ARCH).o \
+$(SRC)/init_client.$(ARCH).o
+
+single = \
+$(SRC)/init.$(ARCH).o \
+$(SRC)/run.$(ARCH).o \
+$(SRC)/stop.$(ARCH).o \
+$(SRC)/pantasks.$(ARCH).o \
+$(SRC)/thread_locks.$(ARCH).o \
+$(SRC)/job_threads.$(ARCH).o \
+$(SRC)/task_threads.$(ARCH).o \
+$(SRC)/controller_threads.$(ARCH).o 
+
+server = \
+$(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 \
+$(SRC)/status_server.$(ARCH).o \
+$(SRC)/init_server.$(ARCH).o \
+$(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
+
+funcs = \
+$(SRC)/CheckJobs.$(ARCH).o \
+$(SRC)/CheckController.$(ARCH).o \
+$(SRC)/CheckTasks.$(ARCH).o \
+$(SRC)/CheckTimeRanges.$(ARCH).o \
+$(SRC)/ControllerOps.$(ARCH).o \
+$(SRC)/LocalJob.$(ARCH).o \
+$(SRC)/JobOps.$(ARCH).o \
+$(SRC)/JobIDOps.$(ARCH).o \
+$(SRC)/TaskOps.$(ARCH).o
+
+cmds = \
+$(SRC)/pulse.$(ARCH).o \
+$(SRC)/status.$(ARCH).o \
+$(SRC)/flush.$(ARCH).o \
+$(SRC)/showtask.$(ARCH).o \
+$(SRC)/kill.$(ARCH).o \
+$(SRC)/delete.$(ARCH).o \
+$(SRC)/verbose.$(ARCH).o \
+$(SRC)/controller.$(ARCH).o \
+$(SRC)/controller_host.$(ARCH).o \
+$(SRC)/controller_exit.$(ARCH).o \
+$(SRC)/controller_check.$(ARCH).o \
+$(SRC)/controller_status.$(ARCH).o \
+$(SRC)/controller_jobstack.$(ARCH).o \
+$(SRC)/controller_verbose.$(ARCH).o \
+$(SRC)/controller_run.$(ARCH).o \
+$(SRC)/controller_output.$(ARCH).o \
+$(SRC)/controller_pulse.$(ARCH).o \
+$(SRC)/task.$(ARCH).o \
+$(SRC)/task_host.$(ARCH).o \
+$(SRC)/task_nmax.$(ARCH).o \
+$(SRC)/task_active.$(ARCH).o \
+$(SRC)/task_macros.$(ARCH).o \
+$(SRC)/task_trange.$(ARCH).o \
+$(SRC)/task_stdout.$(ARCH).o \
+$(SRC)/task_periods.$(ARCH).o \
+$(SRC)/task_command.$(ARCH).o \
+$(SRC)/task_options.$(ARCH).o \
+$(SRC)/version.$(ARCH).o \
+$(SRC)/ipptool2book.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a
+
+pantasks: $(BIN)/pantasks.$(ARCH)
+$(SRC)/pantasks.$(ARCH).o : $(libs)
+$(BIN)/pantasks.$(ARCH) : $(single) $(funcs) $(cmds)
+
+pantasks_client: $(BIN)/pantasks_client.$(ARCH)
+$(SRC)/pantasks_client.$(ARCH).o : $(libs)
+$(BIN)/pantasks_client.$(ARCH) : $(client)
+
+pantasks_server: $(BIN)/pantasks_server.$(ARCH)
+$(SRC)/pantasks_server.$(ARCH).o : $(libs)
+$(BIN)/pantasks_server.$(ARCH) : $(server) $(funcs) $(cmds)
+
+pantasks_client.install: $(DESTBIN)/pantasks_client
+pantasks_server.install: $(DESTBIN)/pantasks_server
+
+$(single) $(client) $(server) $(funcs) $(cmds) : $(INC)/pantasks.h
+
+install: $(DESTBIN)/pantasks $(DESTBIN)/pantasks_client $(DESTBIN)/pantasks_server help modules
+
+help: clean-help cmd.basic.help cmd.data.help cmd.astro.help pantasks.help
+
+modules: pantasks.modules
+
+.PHONY: pantasks pantasks_client pantasks_server
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/TaskOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/TaskOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/TaskOps.c	(revision 22322)
@@ -0,0 +1,675 @@
+# include "pantasks.h"
+# include <regex.h>
+
+static Task **tasks;
+static int    Ntasks;
+static int    NTASKS;
+
+/* counter marking task being visited by the run loop */
+static int   ActiveTask;
+
+/* temporary holder for a new task */
+static Task *NewTask = NULL;
+
+/* set up the task list system */
+void InitTasks () {
+  NTASKS = 20;
+  Ntasks = 0;
+  ALLOCATE (tasks, Task *, NTASKS);
+  ActiveTask = -1;
+}
+
+void FreeTasks () {
+  int i;
+  for (i = 0; i < Ntasks; i++) {
+    FreeTask (tasks[i]);
+  }
+  free (tasks);
+}
+
+/* provide a mechanism to loop over the list of tasks */
+Task *NextTask () {
+  
+  Task *task;
+
+  /* move to the next task and return it */
+  ActiveTask ++;
+  if (ActiveTask < 0) ActiveTask = 0;
+  if (ActiveTask >= Ntasks) {
+    ActiveTask = -1;
+    return (NULL);
+  }
+  task = tasks[ActiveTask];
+  return (task);
+}
+
+/* return task with given name */
+Task *FindTask (char *name) {
+
+  int i;
+
+  /* try for an exact match first */
+  for (i = 0; i < Ntasks; i++) {
+    if (!strcmp (tasks[i][0].name, name)) {
+      return (tasks[i]);
+    }
+  }
+  return (NULL);
+}  
+
+/* list known tasks */
+void ListTasks (int verbose) {
+
+  int i, j, valid, nameLength, cmdLength;
+  char *start, *stop;
+  char format[128];
+
+  gprint (GP_LOG, "\n");
+  if (Ntasks == 0) {
+    gprint (GP_LOG, " no defined tasks\n");
+    return;
+  }
+
+  /* find string lengths */
+  nameLength = cmdLength = 0;
+  for (i = 0; i < Ntasks; i++) {
+    nameLength = MAX (nameLength, strlen(tasks[i][0].name));
+    if (tasks[i][0].argv == NULL) {
+      cmdLength = MAX (nameLength, strlen("(dynamic)"));
+    } else {
+      cmdLength = MAX (nameLength, strlen(tasks[i][0].argv[0]));
+    }
+  }
+
+  gprint (GP_LOG, " Task Status\n");
+
+  snprintf (format, 128, "  AV %%-%ds %5s  %5s %5s %5s %%-%ds\n", nameLength, "Njobs", "Ngood", "Nfail", "Ntime", cmdLength);
+  gprint (GP_LOG, format, "Name", "Command");
+
+  snprintf (format, 128, "%%-%ds %%5d  %%5d %%5d %%5d %%-%ds\n", nameLength, cmdLength);
+  for (i = 0; i < Ntasks; i++) {
+    valid = CheckTimeRanges (tasks[i][0].ranges, tasks[i][0].Nranges);
+    if (verbose) gprint (GP_LOG, "\n");
+    if (tasks[i][0].active) {
+      gprint (GP_LOG, "  +");
+    } else {
+      gprint (GP_LOG, "  -");
+    }
+    if (valid) {
+      gprint (GP_LOG, "+ ");
+    } else {
+      gprint (GP_LOG, "- ");
+    }
+    if (tasks[i][0].argv == NULL) {
+      gprint (GP_LOG, format, tasks[i][0].name, tasks[i][0].Njobs, tasks[i][0].Nsuccess, tasks[i][0].Nfailure, tasks[i][0].Ntimeout, "(dynamic)");
+    } else {
+      gprint (GP_LOG, format, tasks[i][0].name, tasks[i][0].Njobs, tasks[i][0].Nsuccess, tasks[i][0].Nfailure, tasks[i][0].Ntimeout, tasks[i][0].argv[0]);
+    }
+    if (verbose) {
+      gprint (GP_LOG, "    spawn period: %f, polling period: %f, timeout period: %f\n", 
+	       tasks[i][0].exec_period, tasks[i][0].poll_period, tasks[i][0].timeout_period);
+      for (j = 0; j < tasks[i][0].Nranges; j++) {
+	switch (tasks[i][0].ranges[j].type) {
+	  case RANGE_ABS:
+	    start = ohana_sec_to_date (tasks[i][0].ranges[j].start);
+	    stop  = ohana_sec_to_date (tasks[i][0].ranges[j].stop);
+	    break;
+	  case RANGE_DAY:
+	    start = ohana_sec_to_hms (tasks[i][0].ranges[j].start);
+	    stop  = ohana_sec_to_hms (tasks[i][0].ranges[j].stop);
+	    break;
+	  case RANGE_WEEK:
+	    start = ohana_sec_to_day (tasks[i][0].ranges[j].start);
+	    stop  = ohana_sec_to_day (tasks[i][0].ranges[j].stop);
+	    break;
+	  default:
+	    abort ();
+	}
+	if (tasks[i][0].ranges[j].include) {
+	  gprint (GP_LOG, "     active : %s - %s", start, stop);
+	  if (tasks[i][0].ranges[j].Nmax) gprint (GP_LOG, " (%d of %d)", tasks[i][0].ranges[j].Nrun, tasks[i][0].ranges[j].Nmax);
+	  gprint (GP_LOG, "\n");
+	} else {
+	  gprint (GP_LOG, "     avoid  : %s - %s\n", start, stop);
+	}
+	free (start);
+	free (stop);
+      }
+      if (tasks[i][0].host == NULL) {
+	gprint (GP_LOG, "    task runs locally\n");
+	continue;
+      }
+      if (!strcasecmp(tasks[i][0].host, "ANYHOST")) {
+	gprint (GP_LOG, "    task host selected by controller\n");
+	continue;
+      }
+      if (tasks[i][0].host_required) {
+	gprint (GP_LOG, "    host %s (required)\n", tasks[i][0].host);
+      } else {
+	gprint (GP_LOG, "    host %s (desired)\n", tasks[i][0].host);
+      }
+    }
+  }
+  return;
+}
+
+/* list known tasks */
+void ListTaskStats (char *regex) {
+
+  int i, valid, nameLength;
+  char format[128];
+  regex_t preg;
+
+  gprint (GP_LOG, "\n");
+  if (Ntasks == 0) {
+    gprint (GP_LOG, " no defined tasks\n");
+    return;
+  }
+
+  if (regex != NULL) {
+    regcomp (&preg, regex, REG_EXTENDED);
+  }
+
+  /* find string lengths */
+  nameLength = 0;
+  for (i = 0; i < Ntasks; i++) {
+    nameLength = MAX (nameLength, strlen(tasks[i][0].name));
+  }
+
+  gprint (GP_LOG, " Task Statistics\n");
+
+  snprintf (format, 128, "     %%-%ds |           alljobs          |           success          |           failure          |\n", nameLength);
+  gprint (GP_LOG, format, "");
+  snprintf (format, 128, "  AV %%-%ds | Njobs   Tmin   Tave   Tmax | Njobs   Tmin   Tave   Tmax | Njobs   Tmin   Tave   Tmax |\n", nameLength);
+  gprint (GP_LOG, format, "Name");
+
+  snprintf (format, 128, "%%-%ds", nameLength);
+  for (i = 0; i < Ntasks; i++) {
+      
+    if ((regex != NULL) && regexec (&preg, tasks[i][0].name, 0, NULL, 0)) continue;
+
+    valid = CheckTimeRanges (tasks[i][0].ranges, tasks[i][0].Nranges);
+    if (tasks[i][0].active) {
+      gprint (GP_LOG, "  +");
+    } else {
+      gprint (GP_LOG, "  -");
+    }
+    if (valid) {
+      gprint (GP_LOG, "+ ");
+    } else {
+      gprint (GP_LOG, "- ");
+    }
+    if (tasks[i][0].argv == NULL) {
+      gprint (GP_LOG, format, tasks[i][0].name);
+    } else {
+      gprint (GP_LOG, format, tasks[i][0].name);
+    }
+    if (tasks[i][0].dtimeMin_alljobs < 0) {
+      gprint (GP_LOG, " | %5d %6s %6.2f %6.2f",     tasks[i][0].Njobs,    "NONE",                       tasks[i][0].dtimeAve_alljobs, tasks[i][0].dtimeMax_alljobs);
+    } else {
+      gprint (GP_LOG, " | %5d %6.2f %6.2f %6.2f",   tasks[i][0].Njobs,    tasks[i][0].dtimeMin_alljobs, tasks[i][0].dtimeAve_alljobs, tasks[i][0].dtimeMax_alljobs);
+    }      
+    if (tasks[i][0].dtimeMin_success < 0) {
+      gprint (GP_LOG, " | %5d %6s %6.2f %6.2f",     tasks[i][0].Nsuccess, "NONE",                       tasks[i][0].dtimeAve_success, tasks[i][0].dtimeMax_success);
+    } else {
+      gprint (GP_LOG, " | %5d %6.2f %6.2f %6.2f",   tasks[i][0].Nsuccess, tasks[i][0].dtimeMin_success, tasks[i][0].dtimeAve_success, tasks[i][0].dtimeMax_success);
+    }
+    if (tasks[i][0].dtimeMin_failure < 0) {
+      gprint (GP_LOG, " | %5d %6s %6.2f %6.2f |\n",   tasks[i][0].Nfailure, "NONE",                       tasks[i][0].dtimeAve_failure, tasks[i][0].dtimeMax_failure);
+    } else {
+      gprint (GP_LOG, " | %5d %6.2f %6.2f %6.2f |\n", tasks[i][0].Nfailure, tasks[i][0].dtimeMin_failure, tasks[i][0].dtimeAve_failure, tasks[i][0].dtimeMax_failure);
+    }
+  }
+  return;
+}
+
+/* list known tasks */
+void ResetTaskStats (char *regex) {
+
+  int i, nameLength;
+  regex_t preg;
+
+  if (Ntasks == 0) {
+    return;
+  }
+
+  if (regex != NULL) {
+    regcomp (&preg, regex, REG_EXTENDED);
+  }
+
+  /* find string lengths */
+  nameLength = 0;
+  for (i = 0; i < Ntasks; i++) {
+    nameLength = MAX (nameLength, strlen(tasks[i][0].name));
+  }
+
+  for (i = 0; i < Ntasks; i++) {
+      
+    if ((regex != NULL) && regexec (&preg, tasks[i][0].name, 0, NULL, 0)) continue;
+
+    tasks[i][0].Njobs = 0;
+    tasks[i][0].dtimeMin_alljobs = 0;
+    tasks[i][0].dtimeAve_alljobs = 0;
+    tasks[i][0].dtimeMax_alljobs = 0;
+
+    tasks[i][0].Nsuccess = 0;
+    tasks[i][0].dtimeMin_success = 0;
+    tasks[i][0].dtimeAve_success = 0;
+    tasks[i][0].dtimeMax_success = 0;
+
+    tasks[i][0].Nfailure = 0;
+    tasks[i][0].dtimeMin_failure = 0;
+    tasks[i][0].dtimeAve_failure = 0;
+    tasks[i][0].dtimeMax_failure = 0;
+  }
+  return;
+}
+
+/* show details of a task */
+int ShowTask (char *name) {
+
+  int i, j;
+  char *start, *stop;
+  Task *task;
+
+  task = FindTask (name);
+  if (task == NULL) {
+    gprint (GP_LOG, "task %s not found\n", name);
+    return (FALSE);
+  }
+
+  gprint (GP_LOG, "\n macro %s\n", task[0].name);
+
+  gprint (GP_LOG, "\n command: ");
+  for (i = 0; i < task[0].argc; i++) {
+    gprint (GP_LOG, "%s ", task[0].argv[i]);
+  }
+  gprint (GP_LOG, "\n\n");
+
+  gprint (GP_LOG, "\n options: ");
+  for (i = 0; i < task[0].optc; i++) {
+    gprint (GP_LOG, "%s ", task[0].optv[i]);
+  }
+  gprint (GP_LOG, "\n\n");
+
+  if (task[0].host == NULL) {
+    gprint (GP_LOG, " task runs locally\n");
+    goto periods;
+  }
+  if (!strcasecmp(task[0].host, "ANYHOST")) {
+    gprint (GP_LOG, " task host selected by controller\n");
+    goto periods;
+  }
+  if (task[0].host_required) {
+    gprint (GP_LOG, " host %s (required)\n", task[0].host);
+  } else {
+    gprint (GP_LOG, " host %s (desired)\n", task[0].host);
+  }
+
+periods:
+  gprint (GP_LOG, " time periods: exec: %f  poll: %f  timeout: %f\n", 
+	  task[0].exec_period, task[0].poll_period, task[0].timeout_period);
+
+  for (j = 0; j < tasks[i][0].Nranges; j++) {
+    switch (tasks[i][0].ranges[j].type) {
+      case RANGE_ABS:
+	start = ohana_sec_to_date (tasks[i][0].ranges[j].start);
+	stop  = ohana_sec_to_date (tasks[i][0].ranges[j].stop);
+	break;
+      case RANGE_DAY:
+	start = ohana_sec_to_hms (tasks[i][0].ranges[j].start);
+	stop  = ohana_sec_to_hms (tasks[i][0].ranges[j].stop);
+	break;
+      case RANGE_WEEK:
+	start = ohana_sec_to_day (tasks[i][0].ranges[j].start);
+	stop  = ohana_sec_to_day (tasks[i][0].ranges[j].stop);
+	break;
+      default:
+	abort ();
+    }
+    if (tasks[i][0].ranges[j].include) {
+      gprint (GP_LOG, "     active : %s - %s", start, stop);
+      if (tasks[i][0].ranges[j].Nmax) gprint (GP_LOG, " (%d of %d)", tasks[i][0].ranges[j].Nrun, tasks[i][0].ranges[j].Nmax);
+      gprint (GP_LOG, "\n");
+    } else {
+      gprint (GP_LOG, "     avoid  : %s - %s\n", start, stop);
+    }
+    free (start);
+    free (stop);
+  }
+
+  gprint (GP_LOG, "\n pre-execute macro\n");
+  ListMacro (task[0].exec);
+
+  gprint (GP_LOG, "\n timeout macro\n");
+  ListMacro (task[0].timeout);
+
+  gprint (GP_LOG, "\n crash macro\n");
+  ListMacro (task[0].crash);
+
+  gprint (GP_LOG, "\n default exit macro\n");
+  ListMacro (task[0].defexit);
+
+  for (i = 0; i < task[0].Nexit; i++) {
+    gprint (GP_LOG, "\n exit macro (status == %d)\n", atoi(task[0].exit[i][0].name));
+    ListMacro (task[0].exit[i]);
+  }
+
+  return (TRUE);
+}
+
+/* make a new named task */
+int FreeTask (Task *task) {
+  
+  int i;
+
+  if (task == NULL) return (FALSE);
+  
+  if (task[0].name != NULL) free (task[0].name);
+  if (task[0].host != NULL) free (task[0].host);
+  if (task[0].argv != NULL) {
+    for (i = 0; i < task[0].argc; i++) {
+      free (task[0].argv[i]);
+    }
+    free (task[0].argv);
+  }
+  if (task[0].optv != NULL) {
+    for (i = 0; i < task[0].optc; i++) {
+      free (task[0].optv[i]);
+    }
+    free (task[0].optv);
+  }
+  if (task[0].exec != NULL) {
+    FreeMacro (task[0].exec);
+    free (task[0].exec);
+  }
+  if (task[0].crash != NULL) {
+    FreeMacro (task[0].crash);
+    free (task[0].crash);
+  }
+  if (task[0].timeout != NULL) {
+    FreeMacro (task[0].timeout);
+    free (task[0].timeout);
+  }
+  for (i = 0; i < task[0].Nexit; i++) {
+    if (task[0].exit[i] != NULL) {
+      FreeMacro (task[0].exit[i]);
+    }
+    free (task[0].exit[i]);
+  }
+  free (task[0].exit);
+
+  if (task[0].ranges != NULL) {
+    free (task[0].ranges);
+  }
+  return (TRUE);
+}
+
+/**** new task functions ***/
+
+/* make a new named task */
+Task *CreateTask (char *name) {
+  
+  ALLOCATE (NewTask, Task, 1);
+
+  NewTask[0].name = strcreate (name);;
+
+  NewTask[0].host = NULL;
+  NewTask[0].host_required = FALSE;
+
+  NewTask[0].argc = 0;
+  NewTask[0].argv = NULL;
+
+  NewTask[0].optc = 0;
+  NewTask[0].optv = NULL;
+
+  NewTask[0].stdout_dump = NULL;
+  NewTask[0].stderr_dump = NULL;
+
+  NewTask[0].exec = NULL;
+  NewTask[0].crash = NULL;
+  NewTask[0].timeout = NULL;
+  NewTask[0].defexit = NULL;
+
+  NewTask[0].Nexit = 0;
+  NewTask[0].NEXIT = 10;
+  ALLOCATE (NewTask[0].exit, Macro *, NewTask[0].NEXIT);
+  /* don't free tasks[0].exit, keep at least 1 allocated */
+
+  NewTask[0].exec_period = 1.0;
+  NewTask[0].poll_period = 1.0;
+  NewTask[0].timeout_period = 1.0;
+
+  NewTask[0].Nranges = 0;
+  ALLOCATE (NewTask[0].ranges, TimeRange, 1);
+
+  /* init task timer (is reset by 'run') */  
+  gettimeofday (&NewTask[0].last, (void *) NULL);
+  NewTask[0].Nmax = 0;  /* default value means 'no limit' */
+
+  NewTask[0].NpendingMax = 0;  /* default value means 'no limit' */
+  NewTask[0].Npending = 0;  /* default value means 'no limit' */
+
+  NewTask[0].Njobs = 0;
+  NewTask[0].Nsuccess = 0;
+  NewTask[0].Nfailure = 0;
+  NewTask[0].Ntimeout = 0;
+
+  /* jobs timing statistics */
+  NewTask[0].dtimeAve_alljobs =  0.0;
+  NewTask[0].dtimeMin_alljobs = -1.0;
+  NewTask[0].dtimeMax_alljobs =  0.0;
+
+  NewTask[0].dtimeAve_success =  0.0;
+  NewTask[0].dtimeMin_success = -1.0;
+  NewTask[0].dtimeMax_success =  0.0;
+
+  NewTask[0].dtimeAve_failure =  0.0;
+  NewTask[0].dtimeMin_failure = -1.0;
+  NewTask[0].dtimeMax_failure =  0.0;
+
+  NewTask[0].active = TRUE;
+  return (NewTask);
+}
+
+/* remove the task from the task list */
+int RemoveTask (Task *task) {
+  
+  int i, Nt;
+
+  /* find task in task list */
+  Nt = -1;
+  for (i = 0; i < Ntasks; i++) {
+    if (task == tasks[i]) {
+      Nt = i;
+      break;
+    }
+  }
+  if (Nt == -1) {
+    gprint (GP_ERR, "programming error: task not found\n");
+    return (FALSE);
+  }
+  for (i = Nt; i < Ntasks - 1; i++) {
+    tasks[i] = tasks[i+1];
+  }
+  Ntasks --;
+  return (TRUE);
+}
+
+int ValidateTask (Task *task, int RequireStatic) {
+
+  int i, hash;
+
+  /* is a static command defined? */
+  if (task[0].argc != 0) {
+    if (task[0].argv == NULL) {
+      gprint (GP_ERR, "task command arguments not defined (programming error)\n");
+      return (FALSE);
+    }
+    return (TRUE);
+  }
+  if (RequireStatic) {
+    gprint (GP_ERR, "task command not defined\n");
+    return (FALSE);
+  }
+
+  /* no static command; dynamic command? */
+  if (task[0].exec != NULL) {
+    for (i = 0; i < task[0].exec[0].Nlines; i++) {
+      hash = TaskHash (task[0].exec[0].line[i]);
+      if (hash == TASK_COMMAND) return (TRUE);
+    }
+  }
+  gprint (GP_ERR, "task command not defined\n");
+  return (FALSE);
+}
+
+int RegisterNewTask () {
+  
+  int N;
+
+  N = Ntasks;
+  Ntasks ++;
+  if (Ntasks == NTASKS) {
+    NTASKS += 20;
+    REALLOCATE (tasks, Task *, NTASKS);
+  }
+  tasks[N] = NewTask;
+  NewTask = NULL;
+  return (TRUE);
+}
+
+int DeleteNewTask () {
+  if (NewTask != NULL) {
+    FreeTask (NewTask);
+    free (NewTask);
+    NewTask = NULL;
+  }
+  return (TRUE);
+}
+
+Task *SetNewTask (Task *task) {
+  NewTask = task;
+  return (task);
+}
+
+Task *GetNewTask () {
+  return (NewTask);
+}
+
+Task *GetActiveTask () {
+  Task *task;
+  if (ActiveTask < 0) return (NULL);
+  task = tasks[ActiveTask];
+  return (task);
+}
+
+int TaskHash (char *input) {
+  
+  int hash;
+  char *command;
+
+  hash = TASK_NONE;
+
+  command = thisword (input);
+  if (command == NULL) return (TASK_EMPTY);
+
+  if (command[0] == '#')                  hash = TASK_COMMENT;
+  if (!strcasecmp (command, "END"))       hash = TASK_END;
+  if (!strcasecmp (command, "HOST"))      hash = TASK_HOST;
+  if (!strcasecmp (command, "NMAX"))      hash = TASK_NMAX;
+  if (!strcasecmp (command, "ACTIVE"))    hash = TASK_ACTIVE;
+  if (!strcasecmp (command, "TRANGE"))    hash = TASK_TRANGE;
+  if (!strcasecmp (command, "STDOUT"))    hash = TASK_STDOUT;
+  if (!strcasecmp (command, "STDERR"))    hash = TASK_STDERR;
+  if (!strcasecmp (command, "COMMAND"))   hash = TASK_COMMAND;
+  if (!strcasecmp (command, "OPTIONS"))   hash = TASK_OPTIONS;
+  if (!strcasecmp (command, "PERIODS"))   hash = TASK_PERIODS;
+  if (!strcasecmp (command, "NPENDING"))  hash = TASK_NPENDING;
+  if (!strcasecmp (command, "TASK.EXIT")) hash = TASK_EXIT;
+  if (!strcasecmp (command, "TASK.EXEC")) hash = TASK_EXEC;
+
+  free (command);
+  return (hash);
+}
+
+/*** task timer functions ***/
+
+double GetTaskTimer (struct timeval start, int verbose) {
+
+  double dtime;
+  struct timeval now;
+  
+  gettimeofday (&now, (void *) NULL);
+  dtime = DTIME (now, start);
+  
+  if (verbose) {
+      fprintf (stderr, "tt: %d %6d  - %d %6d : %f\n", 
+	       (int) now.tv_sec, (int) now.tv_usec, 
+	       (int) start.tv_sec, (int) start.tv_usec, dtime);
+  }
+
+  return (dtime);
+}
+
+void SetTaskTimer (struct timeval *timer) {
+  gettimeofday (timer, (void *) NULL);
+}
+
+/* start the clock for all tasks */
+void InitTaskTimers () {
+
+  Task *task;
+  double isec, fsec;
+
+  while ((task = NextTask ()) != NULL) {
+    gettimeofday (&task[0].last, (void *) NULL);
+    fsec = modf (task[0].exec_period, &isec);
+    task[0].last.tv_sec -= isec;
+    task[0].last.tv_usec -= 1e6*fsec;
+ }
+}
+
+/* must call this after updating the corresponding counter */
+void UpdateTaskTimerStats (Task *task, int mode, double dtime) {
+
+  double total;
+
+  switch (mode) {
+    case TIMER_ALLJOBS:
+      total = task[0].dtimeAve_alljobs * (task[0].Njobs - 1);
+      total += dtime;
+      task[0].dtimeAve_alljobs = total / (float) task[0].Njobs;
+      if (task[0].dtimeMin_alljobs < 0) {
+	task[0].dtimeMin_alljobs = dtime;
+      } else {
+	task[0].dtimeMin_alljobs = MIN (task[0].dtimeMin_alljobs, dtime);
+      }
+      task[0].dtimeMax_alljobs = MAX (task[0].dtimeMax_alljobs, dtime);
+      break;
+    case TIMER_SUCCESS:
+      total = task[0].dtimeAve_success * (task[0].Nsuccess - 1);
+      total += dtime;
+      task[0].dtimeAve_success = total / (float) task[0].Nsuccess;
+      if (task[0].dtimeMin_success < 0) {
+	task[0].dtimeMin_success = dtime;
+      } else {
+	task[0].dtimeMin_success = MIN (task[0].dtimeMin_success, dtime);
+      }
+      task[0].dtimeMax_success = MAX (task[0].dtimeMax_success, dtime);
+      break;
+    case TIMER_FAILURE:
+      total = task[0].dtimeAve_failure * (task[0].Nfailure - 1);
+      total += dtime;
+      task[0].dtimeAve_failure = total / (float) task[0].Nfailure;
+      if (task[0].dtimeMin_failure < 0) {
+	task[0].dtimeMin_failure = dtime;
+      } else {
+	task[0].dtimeMin_failure = MIN (task[0].dtimeMin_failure, dtime);
+      }
+      task[0].dtimeMax_failure = MAX (task[0].dtimeMax_failure, dtime);
+      break;
+    default:
+      abort();
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/client_shell.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/client_shell.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/client_shell.c	(revision 22322)
@@ -0,0 +1,99 @@
+# include "opihi.h"
+
+/******************/
+int client_shell (int argc, char **argv) {
+
+  int Nbad, status;
+  char *line, *prompt, *history;
+  pid_t ppid;
+
+  general_init (&argc, argv);
+  program_init (&argc, argv);
+  startup (&argc, argv);
+  prompt = get_variable("PROMPT");
+  history = get_variable("HISTORY");
+  welcome ();
+
+  /* attempt to connect to the pantasks server (exit on failure) */
+  multicommand_InitServer ();
+
+  Nbad = 0;
+  while (1) {  /** must exit with command "exit" or "quit" */
+    if (Nbad == 10) exit (3);
+
+    line = readline (prompt);
+
+    if (line == NULL) { 
+      
+      ppid = getppid();
+      if (ppid == 1) {
+	gprint (GP_ERR, "caught parent shutdown\n");
+	exit (2);
+      }
+      if (!isatty (STDIN_FILENO)) exit (2);
+      gprint (GP_ERR, "Use \"quit\" to exit\n");
+      Nbad ++;
+      continue;
+    }
+    Nbad = 0;
+    ohana_memregister (line);
+
+    stripwhite (line);
+
+    if (*line) {
+	status = multicommand (line);
+	add_history (line);
+	append_history (1, history);
+    }
+    free (line);
+  }
+}
+
+/* 
+   startup sequence:
+
+   - general_init
+   - program_init
+   - startup (exit if non-interactive)
+   - welcome
+
+*/
+
+/* client issues and questions:
+
+- need to identify the commands to be caught by the client
+- pass each input line through the command parser first, then
+  send to server if not identified as a valid command
+- this raises the question of variables: do we parse variables
+  at the client level?  I would not think so.  what about input?
+  if the input is to be parsed by the client, but executed on the server, 
+  I'll need to re-work the input parsing system.  would be better 
+  for the server to parse the input command.  in this case, the client 
+  user needs to have access to the directories with input scripts.
+  this may be an advantage: it would allow some limitation on who can 
+  install server scripts.
+- variables, vectors and buffers: where are they valid (client or server?)
+  it seems they should all be defined locally on the server.  this makes
+  graphics plotting a server-side action as well.  this is probably easier,
+  but we do need to be careful about multiple clients trying to make plots
+  on the same window at the same time.  Exporting the window can be done 
+  for one client with the $KII variable, but could be tricky for more 
+  complex interactions
+- list of commands which clearly must be parsed by the client:
+  - quit / exit
+  - exec / !
+  - ?
+
+- i may need an alternative version of command, modified to avoid parsing the 
+  variables and vectors.  I also need an alternative version of the Init commands
+  to just list the blocked ones.
+
+- the client now executes all of the basic opihi commands, and only passes
+  through the pantasks commands.  The pantasks server interface should only
+  accept functions which immediately return, sending back the result to be
+  printed by the client.  
+
+  The server should not accept the 'task' commands from the client command-line.
+  To define and run macros or tasks on the server, these need to be added to 
+  a script which is loaded with a varient on the input command.  
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "pantasks.h"
+
+int controller_host    	PROTO((int, char **));
+int controller_exit    	PROTO((int, char **));
+int controller_status  	PROTO((int, char **));
+int controller_jobstack PROTO((int, char **));
+int controller_verbose  PROTO((int, char **));
+int controller_run     	PROTO((int, char **));
+int controller_stop    	PROTO((int, char **));
+int controller_check   	PROTO((int, char **));
+int controller_output  	PROTO((int, char **));
+int controller_pulse   	PROTO((int, char **));
+
+static Command controller_cmds[] = {
+  {1, "exit",     controller_exit,     "shutdown controller"},
+  {1, "host",     controller_host,     "define host for controller"},
+  {1, "check",    controller_check,    "check controller host/job"},
+  {1, "run",      controller_run,      "start controller operation / set run levels"},
+  {1, "stop",     controller_run,      "stop controller (no disconnect)"},
+  {1, "status",   controller_status,   "check controller status"},
+  {1, "jobstack", controller_jobstack, "check controller status"},
+  {1, "verbose",  controller_verbose,  "set controller verbosity"},
+  {1, "output",   controller_output,   "print controller output"},
+  {1, "pulse",    controller_pulse,    "set controller pulse"},
+};
+
+int controller (int argc, char **argv) {
+
+  int i, N, status;
+  CommandF *func;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: controller (command) ... \n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "help")) {
+    N = sizeof (controller_cmds) / sizeof (Command);
+
+    for (i = 0; i < N; i++) {
+      gprint (GP_LOG, "%-15s %s\n", controller_cmds[i].name, controller_cmds[i].help);
+    }
+    return (TRUE);
+  }
+
+  func = FindControllerCommand (argv[1]);
+  if (func == NULL) {
+    gprint (GP_ERR, "invalid controller command\n");
+    return (FALSE);
+  }
+
+  status = (*func)(argc - 1, argv + 1);
+  return (status);
+}
+
+CommandF *FindControllerCommand (char *cmd) {
+
+  int i, N;
+
+  N = sizeof (controller_cmds) / sizeof (Command);
+
+  for (i = 0; i < N; i++) {
+    if (!strcmp (controller_cmds[i].name, cmd)) {
+      return (controller_cmds[i].func);
+    }
+  }
+  return (NULL);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_check.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_check.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_check.c	(revision 22322)
@@ -0,0 +1,35 @@
+# include "pantasks.h"
+
+int controller_check (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 3) goto usage;
+  if (strcasecmp (argv[1], "JOB") && 
+      strcasecmp (argv[1], "HOST")) goto usage;
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not running\n");
+    return (TRUE);
+  }
+
+  sprintf (command, "check %s %s", argv[1], argv[2]);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (VerboseMode()) {
+    gprint (GP_LOG, "controller command sent\n");  
+    gprint (GP_LOG, "\n Nbytes received: %d\n", buffer.Nbuffer);  
+  }
+  gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: controller check job (jobID)\n");
+  gprint (GP_ERR, "USAGE: controller check host (hostID)\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_exit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_exit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_exit.c	(revision 22322)
@@ -0,0 +1,14 @@
+# include "pantasks.h"
+
+int controller_exit (int argc, char **argv) {
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller exit TRUE\n");
+    return (FALSE);
+  }
+
+  if (strcasecmp (argv[1], "TRUE")) return (FALSE);
+
+  QuitController ();
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_host.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_host.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_host.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "pantasks.h"
+
+int controller_host (int argc, char **argv) {
+
+  int N, status, max_threads;
+  char command[1024];
+  IOBuffer buffer;
+
+  max_threads = 0;
+  if ((N = get_argument (argc, argv, "-threads"))) {
+    remove_argument (N, &argc, argv);
+    max_threads = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) goto usage;
+
+  /* start controller connection (if needed) */
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to start pcontrol\n");
+    return (FALSE);
+  }
+
+  // the user may issue any of these commands:
+  // ADD, ON, RETRY, CHECK, OFF, DELETE
+  // we need to catch ADD and DELETE and modify our host table accordingly
+  if (!strcasecmp (argv[1], "ADD")) {
+    AddHost (argv[2], max_threads);
+  } else {
+    if (max_threads) goto usage;
+  }
+
+  if (!strcasecmp (argv[1], "DELETE")) {
+    DeleteHost (argv[2]);
+  }
+
+  if (max_threads) {
+    sprintf (command, "host %s %s -threads %d", argv[1], argv[2], max_threads);
+  } else {
+    sprintf (command, "host %s %s", argv[1], argv[2]);
+  }
+  InitIOBuffer (&buffer, 0x100);
+
+  SerialThreadLock ();
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  SerialThreadUnlock ();
+
+  if (status) gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+  
+usage:
+  gprint (GP_LOG, "USAGE: controller host (command) (hostname)\n");
+  gprint (GP_ERR, "  valid commands: add, on, retry, check, off, delete\n");
+  gprint (GP_ERR, "  -threads Nthreads is optional for 'add'\n");
+  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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_jobstack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_jobstack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_jobstack.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "pantasks.h"
+
+int controller_jobstack (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller jobstack (jobstack)\n");
+    gprint (GP_ERR, "       (jobstack) : pending, busy, exit, crash, hung, done\n");
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  // XXX this has an error?  test this out...
+  sprintf (command, "jobstack %s", argv[1]);
+  InitIOBuffer (&buffer, 0x100);
+
+  SerialThreadLock ();
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  SerialThreadUnlock ();
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_output.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_output.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_output.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "pantasks.h"
+
+int controller_output (int argc, char **argv) {
+
+  if ((argc != 1) || ((argc == 2) && (strcmp(argv[1], "flush")))) {
+    gprint (GP_ERR, "USAGE: controller output\n");
+    return (FALSE);
+  }
+
+  CheckControllerOutput ();
+  PrintControllerOutput ();
+
+  if (argc == 2) {
+    FlushControllerOutput ();
+  }
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_pulse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_pulse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_pulse.c	(revision 22322)
@@ -0,0 +1,26 @@
+# include "pantasks.h"
+
+int controller_pulse (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller pulse (usec)\n");
+    return (FALSE);
+  }
+
+  /* start controller connection (if needed) */
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to start pcontrol\n");
+    return (FALSE);
+  }
+
+  sprintf (command, "pulse %d", atoi(argv[1]));
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (status) gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_run.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_run.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_run.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include "pantasks.h"
+
+int controller_run (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if ((argc > 2) ||
+      get_argument (argc, argv, "help") ||
+      get_argument (argc, argv, "-h") ||
+      get_argument (argc, argv, "--help")) 
+  {
+    if (!strcmp (argv[0], "run")) {
+      gprint (GP_ERR, "USAGE: controller run [level]\n");
+      gprint (GP_ERR, "  allowed levels:\n");
+      gprint (GP_ERR, "  all (default) : manage machines, spawn jobs, harvest jobs\n");
+      gprint (GP_ERR, "  reap          : manage machines, harvest jobs\n");
+      gprint (GP_ERR, "  hosts         : manage machines, not jobs\n");
+      gprint (GP_ERR, "  none          : all stop\n");
+    } else {
+      gprint (GP_ERR, "USAGE: controller stop (immediate processing halt)\n");
+    }
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  if (argc == 1) {
+    strcpy (command, argv[0]);
+  } else {
+    sprintf (command, "run %s", argv[1]);
+  }
+
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not responding\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_status.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_status.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_status.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "pantasks.h"
+
+int controller_status (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: controller status\n");
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+
+  sprintf (command, "status");
+  InitIOBuffer (&buffer, 0x100);
+
+  SerialThreadLock ();
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  SerialThreadUnlock ();
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_threads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_threads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_threads.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "pantasks.h"
+
+/** things related to CheckController **/
+
+static int CheckControllerRun = FALSE;
+
+void CheckControllerSetState (int state) {
+  CheckControllerRun = state;
+}
+int CheckControllerGetState () {
+  return (CheckControllerRun);
+}
+
+void *CheckControllerThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+
+  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) {
+
+    // check for thread suspend
+    if (!CheckControllerRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    CheckController ();
+    CheckControllerOutput ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "C");
+    // fprintf (stderr, "**** C ****");
+    usleep (500000); // allow other threads a chance to run
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_verbose.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_verbose.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/controller_verbose.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "pantasks.h"
+
+int controller_verbose (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller verbose (level)\n");
+    gprint (GP_ERR, "       (level) : off, on, toggle\n");
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  // XXX this has an error?  test this out...
+  sprintf (command, "verbose %s", argv[1]);
+  InitIOBuffer (&buffer, 0x100);
+
+  SerialThreadLock ();
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  SerialThreadUnlock ();
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/delete.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/delete.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/delete.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "pantasks.h"
+
+int delete_job (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc != 3) goto usage;
+
+  if (!strcasecmp (argv[1], "job")) {
+    JobID = atoi (argv[2]);
+
+    job = FindJob (JobID);
+    if (job == NULL) {
+      gprint (GP_LOG, "job not found\n");
+      return (TRUE);
+    }
+
+    if (job[0].mode == JOB_LOCAL) {
+      if (!KillLocalJob (job)) {
+	job[0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	return (FALSE);
+      }
+    } else {
+      if (!KillControllerJob (job)) {
+	job[0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	return (FALSE);
+      }
+    }    
+    DeleteJob (job);
+    gprint (GP_LOG, "job removed\n");
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "task")) {
+    gprint (GP_ERR, "delete task not implemented yet\n");
+    return (FALSE);
+  }
+
+usage:
+    gprint (GP_ERR, "USAGE: delete job (JobID)\n");
+    gprint (GP_ERR, "USAGE: delete task (TaskName)\n");
+    return (FALSE);
+}
+
+/* find job from jobID, kill and delete */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/flush.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/flush.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/flush.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "pantasks.h"
+
+int flush_jobs (int argc, char **argv) {
+
+  if (argc != 2) goto usage;
+
+  if (!strcasecmp (argv[1], "jobs")) {
+    FlushJobs ();
+    return (TRUE);
+  }
+  
+usage:
+  gprint (GP_ERR, "USAGE: flush jobs\n");
+  return (FALSE);
+}
+
+/* find job from jobID, kill and delete */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/controller
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/controller	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/controller	(revision 22322)
@@ -0,0 +1,29 @@
+
+   controller (command) ...
+
+   Interact with the parallel controller 'pcontrol'.  This command
+   groups together several controller sub-commands, listed below.
+
+
+   controller host (hostname)
+
+   Define a new machine available to the parallel controller.  
+
+
+   controller check (host) (HostID)
+   controller check (job) (JobID)
+
+   Check on the status of a specific host or job known to the parallel
+   controller.
+
+
+   controller status
+
+   Query the parallel controller for its current status
+
+
+   controller output
+
+   Dump the contents of the parallel controller message buffers.
+   These are the messages from the controller itself, rather than the
+   messages generated by jobs run by the controller.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/task
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/task	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/task	(revision 22322)
@@ -0,0 +1,27 @@
+
+   task (name)
+
+   Define or modify a task.  A task is defined by issuing a series of
+   task commands, listed below (see their individual help pages).  A
+   task describes the rules for constructing jobs.  At a minimum, a
+   task must include a command, defining the UNIX command to run for
+   each job.  Other task commands define optional concepts for the
+   task.  These include: 
+
+   - a macro to be run before the job is constructed (task.exec).
+   - a set of macros to be run when the job is completed (task.exit)
+   - time ranges in which the task should be run (or not) (trange) 
+   - required / desired parallel controller hosts (host)
+   - job contruction time interval (periods -exec)
+   - job polling time interval (periods -poll)
+   - job timeouts (periods -timeout)
+   - maximum number of jobs to construct
+
+   See also:
+   task.command
+   task.exit
+   task.exec
+   task.trange
+   task.nmax
+   task.host
+   task.periods
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/task.command
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/task.command	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/task.command	(revision 22322)
@@ -0,0 +1,15 @@
+
+   command (arg0) (arg1) ...
+
+   Define the rule for constructing a job command for a task.  The
+   arguments are UNIX command and the collection of command arguments.
+   The arguments may include references to Opihi variables.  The task
+   command may be included at the top level of the task, or it may be
+   defined within the task exec macro.  In the latter case, the
+   command is constructed only when the exec macro is run, every job
+   construction interval.  Thus, the command arguments may depend on
+   the result of the exec macro operation.   
+
+   See also:
+   task
+   task.exec
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/task.exec
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/task.exec	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/help/task.exec	(revision 22322)
@@ -0,0 +1,17 @@
+
+   task.exec
+
+   A special macro to be executed before constructing the job command.
+   The macro is defined by commands following the 'task.exec' command,
+   ending with the word 'end'.  If the exit status of the task.exec
+   macro is FALSE, then the job is NOT constructed.  This allows the
+   construction of a job to be conditional on other, external
+   quantites.  (Note: the exit status of the macro is false if 'break'
+   is called).  The task command may be defined within the task.exec
+   macro, in which case the command is constructed only when the exec
+   macro is run, every job construction interval.  Thus, the command
+   arguments may depend on the result of the exec macro operation.
+
+   See also:
+   task
+   task.command
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/init.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/init.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/init.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "pantasks.h"
+
+int controller      PROTO((int, char **));
+int task            PROTO((int, char **));
+int task_host       PROTO((int, char **));
+int task_nmax       PROTO((int, char **));
+int task_npending   PROTO((int, char **));
+int task_active     PROTO((int, char **));
+int task_trange     PROTO((int, char **));
+int task_macros     PROTO((int, char **));
+int task_command    PROTO((int, char **));
+int task_options    PROTO((int, char **));
+int task_periods    PROTO((int, char **));
+int task_stdout     PROTO((int, char **));
+int task_stderr     PROTO((int, char **));
+int run             PROTO((int, char **));
+int stop            PROTO((int, char **));
+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 **));
+int kill_job        PROTO((int, char **));
+int delete_job      PROTO((int, char **));
+int verbose         PROTO((int, char **));
+int version         PROTO((int, char **));
+int ipptool2book    PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "active",     task_active,   "set the active state of a task"},
+  {1, "command",    task_command,  "define executed command for a task"},
+  {1, "controller", controller,    "controller commands"},
+  {1, "delete",     delete_job,    "delete job"},
+  {1, "halt",       halt,          "halt the scheduler (no job harvesting)"},
+  {1, "host",       task_host,     "define host machine for a task"},
+  {1, "ipptool2book", ipptool2book, "convert queue with ipptool output to book"},
+  {1, "kill",       kill_job,      "kill job"},
+  {1, "nmax",       task_nmax,     "define maximum number of jobs for a task"},
+  {1, "npending",   task_npending, "define maximum number of outstanding jobs for a task"},
+  {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"},
+  {1, "showtask",   showtask,      "list a task"},
+  {1, "status",     status_sys,    "get system or task status"},
+  {1, "stderr",     task_stderr,   "define a file for the job stderr dump"},
+  {1, "stdout",     task_stdout,   "define a file for the job stdout dump"},
+  {1, "stop",       stop,          "stop the scheduler (continue job harvesting)"},
+  {1, "task",       task,          "define a schedulable task"},
+  {1, "task.exec",  task_macros,   "define pre-exec macro for a task"},
+  {1, "task.exit",  task_macros,   "define exit macros for a task"},
+  {1, "trange",     task_trange,   "define valid/invalid time periods for a task"},
+  {1, "verbose",    verbose,       "set/toggle verbose mode"},
+  {1, "version",    version,       "show version information"},
+}; 
+
+void InitPantasks () {
+  
+  int i;
+
+  InitTasks ();
+  InitJobs ();
+  InitJobIDs ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreePantasks () {
+  FreeTasks ();
+  FreeJobs ();
+  FreeJobIDs ();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/init_client.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/init_client.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/init_client.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "pantasks.h"
+
+int invalid           PROTO((int, char **));
+int server_shutdown   PROTO((int, char **));
+int server_disconnect PROTO((int, char **));
+int server_connect    PROTO((int, char **));
+
+/* we also list here commands which are not valid for the client, but represent valid
+   pantasks commands.  these are caught by the client and sent to the 'invalid'
+   function */
+
+static Command cmds[] = {  
+  {1, "task",       invalid,  	 "define a schedulable task"},
+  {1, "task.exit",  invalid,  	 "define exit macros for a task"},
+  {1, "task.exec",  invalid,  	 "define pre-exec macro for a task"},
+
+  {1, "shutdown",   server_shutdown,   "send exit signal to server"},
+  {1, "disconnect", server_disconnect, "close connection to server"},
+  {1, "connect",    server_connect,    "open connection to server"},
+
+}; 
+
+void InitPantasksClient () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreePantasksClient () {
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/init_server.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/init_server.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/init_server.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "pantasks.h"
+
+int controller      PROTO((int, char **));
+int task            PROTO((int, char **));
+int task_host       PROTO((int, char **));
+int task_nmax       PROTO((int, char **));
+int task_npending   PROTO((int, char **));
+int task_active     PROTO((int, char **));
+int task_trange     PROTO((int, char **));
+int task_macros     PROTO((int, char **));
+int task_command    PROTO((int, char **));
+int task_options    PROTO((int, char **));
+int task_periods    PROTO((int, char **));
+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 **));
+int showtask        PROTO((int, char **));
+int kill_job        PROTO((int, char **));
+int delete_job      PROTO((int, char **));
+int verbose         PROTO((int, char **));
+int version         PROTO((int, char **));
+int server          PROTO((int, char **));
+int ipptool2book    PROTO((int, char **));
+
+int server_run	    PROTO((int, char **));
+int server_stop	    PROTO((int, char **));
+int server_halt	    PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "active",     task_active,   "set the active state of a task"},
+  {1, "command",    task_command,  "define executed command for a task"},
+  {1, "controller", controller,    "controller commands"},
+  {1, "delete",     delete_job,    "delete job"},
+  {1, "host",       task_host,     "define host machine for a task"},
+  {1, "ipptool2book", ipptool2book, "convert queue with ipptool output to book"},
+  {1, "kill",       kill_job,      "kill job"},
+  {1, "nmax",       task_nmax,     "define maximum number of jobs for a task"},
+  {1, "options",    task_options, "define optional variables associated with the job task"},
+  {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"},
+  {1, "showtask",   showtask,      "list a task"},
+  {1, "status",     status_server, "get system status"},
+  {1, "stderr",     task_stderr,   "define a file for the job stderr dump"},
+  {1, "stdout",     task_stdout,   "define a file for the job stdout dump"},
+  {1, "task",       task,          "define a schedulable task"},
+  {1, "task.exec",  task_macros,   "define pre-exec macro for a task"},
+  {1, "task.exit",  task_macros,   "define exit macros for a task"},
+  {1, "trange",     task_trange,   "define valid/invalid time periods for a task"},
+  {1, "verbose",    verbose,       "set/toggle verbose mode"},
+  {1, "version",    version,       "show version information"},
+
+  {1, "run",        server_run,    "run scheduler"},
+  {1, "stop",       server_stop,   "stop scheduler"},
+  {1, "halt",       server_halt,   "halt scheduler"},
+}; 
+
+void InitPantasksServer () {
+  
+  int i;
+
+  InitTasks ();
+  InitJobs ();
+  InitJobIDs ();
+  InitInputs ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreePantasksServer () {
+  FreeTasks ();
+  FreeJobs ();
+  FreeJobIDs ();
+  FreeInputs ();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/input_threads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/input_threads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/input_threads.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "pantasks.h"
+
+/** things related to CheckInputs **/
+
+static int CheckInputsRun = TRUE;
+
+void CheckInputsSetState (int state) {
+  CheckInputsRun = state;
+}
+int CheckInputsGetState () {
+  return (CheckInputsRun);
+}
+
+void *CheckInputsThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+
+  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) {
+
+    // check for thread suspend
+    if (!CheckInputsRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    CheckInputs ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "I");
+    usleep (10000); // allow other threads a chance to run
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/invalid.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/invalid.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/invalid.c	(revision 22322)
@@ -0,0 +1,8 @@
+# include "pantasks.h"
+
+int invalid (int argc, char **argv) {
+
+  gprint (GP_ERR, "%s is not valid for the pantasks client\n", argv[0]);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/ipptool2book.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/ipptool2book.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/ipptool2book.c	(revision 22322)
@@ -0,0 +1,247 @@
+# include "pantasks.h"
+
+# define FREEKEYS \
+  if (keys) { for (i = 0; i < Nkeys; i++) { free (keys[i]); } free (keys); } \
+  for (i = 0; i < setWordN; i++) { free (setWordList[i]); free (setWordValue[i]); } \
+  free (setWordValue); free (setWordList);
+
+// convert the named queue with ipptool metadata output into book format
+int ipptool2book (int argc, char **argv) {
+
+  int i, N, onPage, found, Unique, Nkeys, NKEYS;
+  char *line, *tmpword, *tmpvalue;
+  char pagename[512]; // XXX this should be made dynamic, though it is an unlikey problem
+  char *bookName, **keys, *p, *q;
+  char **setWordList;
+  char **setWordValue;
+  int setWordN, setWordNalloc;
+
+  Page *page = NULL;
+  Book *book = NULL;
+  Queue *queue = NULL;
+
+  /* supply additional constant words */
+  setWordN = 0;
+  setWordNalloc = 10;
+  ALLOCATE (setWordList, char *, setWordNalloc);
+  ALLOCATE (setWordValue, char *, setWordNalloc);
+  while ((N = get_argument (argc, argv, "-setword"))) {
+    remove_argument (N, &argc, argv);
+    setWordList[setWordN] = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    setWordValue[setWordN] = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    setWordN ++;
+    if (setWordN >= setWordNalloc) {
+      setWordNalloc += 10;
+      REALLOCATE (setWordList, char *, setWordNalloc);
+      REALLOCATE (setWordValue, char *, setWordNalloc);
+    }      
+  }
+
+  Unique = FALSE;
+  if ((N = get_argument (argc, argv, "-uniq"))) {
+    remove_argument (N, &argc, argv);
+    Unique = TRUE;
+  }
+
+  Nkeys = 0;
+  keys = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+
+    /* parse the key (word:word:word) into constituents */
+    NKEYS = 10;
+    ALLOCATE (keys, char *, NKEYS);
+    
+    p = q = argv[N];
+    while (q != NULL) {
+      q = strchr (p, ':');
+      keys[Nkeys] = (q == NULL) ? strcreate (p) : strncreate (p, q - p);
+      p = q + 1;
+      Nkeys ++;
+      CHECK_REALLOCATE (keys, char *, NKEYS, Nkeys, 10);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: ipptool2book (queue) (book) [-uniq] [-key key[:key..]]\n");
+    FREEKEYS;
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "queue %s not found\n", argv[1]);
+    FREEKEYS;
+    return (FALSE);
+  }
+    
+  book = CreateBook (argv[2]);
+
+  /* the first non-whitespace line of the queue should contain:
+     BookName MULTI
+  */
+
+  /* scan for first non-emtpy line */
+  while (TRUE) {
+    line = PopQueue (queue);
+    if (!line) {
+      FREEKEYS;
+      return (TRUE); // no output in queue - not an error?
+    }
+    stripwhite (line);
+    if (line[0] == '#') {
+      free (line);
+      continue;
+    }
+    if (*line) break;
+    free (line);
+  }
+  bookName = thisword (line);
+  tmpword = nextword (line);
+
+  if (!tmpword || strcmp(tmpword, "MULTI")) {
+    gprint (GP_ERR, "ERROR: missing metadata output name on first line\n");
+    free (bookName);
+    free (line);
+    FREEKEYS;
+    return FALSE;
+  }
+  free (line);
+  
+  onPage = FALSE;
+  while (TRUE) {
+    line = PopQueue (queue);
+    if (!line) {
+      free (bookName);
+      if (onPage) {
+	gprint (GP_ERR, "ERROR: unterminated metadata\n");
+	FREEKEYS;
+	return (FALSE);
+      }
+      FREEKEYS;
+      return (TRUE);
+    }
+    stripwhite (line);
+    if (!*line) {
+      free (line);
+      continue;
+    }
+    if (line[0] == '#') {
+      free (line);
+      continue;
+    }
+    
+    tmpword = thisword (line);
+    if (tmpword == NULL) abort(); // should not happen if line exists
+
+    if (!onPage) {
+      if (strcmp(tmpword, bookName)) {
+	gprint (GP_ERR, "ERROR: unexpected metadata name %s\n", tmpword);
+	free (line);
+	free (tmpword);
+	free (bookName);
+	FREEKEYS;
+	return (FALSE);
+      }
+      free (tmpword);
+      tmpword = thisword (nextword (line));
+      if (strcmp(tmpword, "METADATA")) {
+	gprint (GP_ERR, "ERROR: missing METADATA tag\n");
+	free (line);
+	free (tmpword);
+	free (bookName);
+	FREEKEYS;
+	return FALSE;
+      }
+      onPage = TRUE;
+      // we don't know the page name until we load its data?
+      sprintf (pagename, "page.%03d", book[0].Npages);
+      page = CreatePage (book, pagename);
+      free (line);
+      free (tmpword);
+      continue;
+    } 
+
+    /* close out the page */
+    if (!strcmp(tmpword, "END")) {
+      // XXX set the page name based on the contents
+      free (tmpword);
+      free (line);
+
+      if (keys) {
+	memset (pagename, 0, 512);
+	for (i = 0; i < Nkeys; i++) {
+	  tmpword = BookGetWord (page, keys[i]);
+	  if (tmpword == NULL) {
+	    gprint (GP_ERR, "ERROR: missing key %s for ID\n", keys[i]);
+	    FREEKEYS;
+	    return (FALSE);
+	  }
+	  if (i > 0) strcat (pagename, ":");
+	  strcat (pagename, tmpword);
+	}
+	free (page[0].name);
+	page[0].name = strcreate (pagename);
+      }
+
+      // add the fixed words to the page
+      for (i = 0; i < setWordN; i++) {
+	BookSetWord (page, setWordList[i], setWordValue[i]);
+      }
+
+      if (Unique) {
+	/* search for an existing page with this name
+	   delete this one if one is found */
+	/* XXX don't attach this page to the book until this step is passed */	
+
+	found = FALSE;
+	for (i = 0; !found && (i < book[0].Npages); i++) {
+	  if (book[0].pages[i] == page) continue;
+	  if (!strcmp (book[0].pages[i][0].name, page[0].name)) {
+	    found = TRUE;
+	  }
+	}
+	if (found) {
+	  DeletePage (book, page);
+	}
+      }
+
+      onPage = FALSE;
+      continue;
+    }
+
+    tmpvalue = thisword (nextword(nextword(line)));
+    if (tmpvalue == NULL) {
+      gprint (GP_ERR, "ERROR: missing value for %s\n", tmpword);
+      free (tmpword);
+      free (line);
+      free (bookName);
+      FREEKEYS;
+      return (FALSE);
+    }
+    BookSetWord (page, tmpword, tmpvalue);
+    free (tmpword);
+    free (tmpvalue);
+    free (line);
+  }    
+  FREEKEYS;
+  return (FALSE);
+}
+
+/* scan through the queue lines.  these should look like:  
+
+bookName METADATA
+key type value
+key type value
+...
+END
+
+bookName METADATA
+key type value
+key type value
+...
+END
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/job.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/job.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/job.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "pantasks.h"
+
+/* this will require a bit of care: to define a job, we need
+   to specify:
+   - the command 
+     this is defined in the job command: job argv0 argv1 argv2...
+   - timeout (an option to the command)
+   - exit macros?
+     do nothing by default?
+   - stderr / stdout disposal?
+     save in a specific buffer by default?
+     send to a file?
+
+   the command can look just like the controller equivalent one:
+   job [-host host] [-timeout timeout] args...
+
+*/
+
+int job (int argc, char **argv) {
+
+  char *Host, **targv;
+  int i, N, Mode, targc, Timeout;
+  IDtype JobID;
+
+  Host = NULL;
+  Mode = PCONTROL_JOB_ANYHOST;
+  if ((N = get_argument (argc, argv, "-host"))) {
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_WANTHOST;
+  }
+  if ((N = get_argument (argc, argv, "+host"))) {
+    if (Mode == PCONTROL_JOB_WANTHOST) {
+      gprint (GP_ERR, "ERROR: -host and +host are incompatible\n");
+      FREE (Host);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_NEEDHOST;
+  }
+  if (Host == NULL) Host = strcreate ("anyhost");
+ 
+  Timeout = 100;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    remove_argument (N, &argc, argv);
+    Timeout = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: job [options] (arg0) (arg1) ... (argN)\n");
+    FREE (Host);
+    return (FALSE);
+  }
+  
+  targc = argc - 1;
+  ALLOCATE (targv, char *, targc);
+  for (i = 1; i < argc; i++) {
+    targv[i-1] = strcreate (argv[i]);
+  }
+
+  JobID = AddJob (Host, Mode, Timeout, targc, targv);
+  gprint (GP_LOG, "JobID: %d\n", JobID);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/job_threads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/job_threads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/job_threads.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "pantasks.h"
+
+/** things related to CheckJobs **/
+
+static int CheckJobsRun = FALSE;
+
+void CheckJobsSetState (int state) {
+  CheckJobsRun = state;
+}
+int CheckJobsGetState () {
+  return (CheckJobsRun);
+}
+
+void *CheckJobsThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+  float 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) {
+
+    // check for thread suspend
+    if (!CheckJobsRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    next_timeout = CheckJobs ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "J");
+
+    next_timeout = MIN (next_timeout, 0.1);
+    if (next_timeout > 0.001) {
+      usleep ((int)(1000000*next_timeout)); // allow other threads a chance to run
+    }      
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/kill.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/kill.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/kill.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "pantasks.h"
+
+int kill_job (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: kill (JobID)\n");
+    return (FALSE);
+  }
+  JobID = atoi (argv[1]);
+
+  job = FindJob (JobID);
+  if (job == NULL) {
+    gprint (GP_LOG, "job not found\n");
+    return (TRUE);
+  }
+
+  if (job[0].mode == JOB_LOCAL) {
+    if (!KillLocalJob (job)) {
+      job[0].state = JOB_HUNG;
+      if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+      return (FALSE);
+    }
+  } else {
+    if (!KillControllerJob (job)) {
+      job[0].state = JOB_HUNG;
+      if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+      return (FALSE);
+    }
+  }    
+  DeleteJob (job);
+  gprint (GP_LOG, "job removed\n");
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/notes.txt	(revision 22322)
@@ -0,0 +1,36 @@
+
+PanTasks Client / Server design
+
+Client
+
+client uses readline to parse the incoming commands commands are only
+validated by the client before being sent to the server:
+
+client loop:
+
+   line = readline ()
+   validate argv[0]
+   send to server
+   wait for response?
+
+Server
+
+Server does not use readline at all.  server has two threads.  first
+thread listens for commands from the clients.  how many clients?  use
+poll to wait for data?  can we manage enough clients with a single
+thread?
+
+the second thread runs the scheduler loop: this is the same background
+check that is being run by the current model on the timeouts.
+
+server loop:
+
+  check socket for new incoming client connection
+  check known clients for new messages
+    execute new command
+    send response to the client
+  run the scheduler for a single loop
+
+client / server communication is modelled after addstar client /
+server?
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pantasks.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pantasks.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pantasks.c.in	(revision 22322)
@@ -0,0 +1,94 @@
+# include "pantasks.h"
+
+# define opihi_name "pantasks"
+# define opihi_prompt "pantasks: "
+# define opihi_description "parallel task scheduler\n"
+# define opihi_history ".pantasks"
+# define opihi_rcfile ".pantasksrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  pthread_t jobsThread;
+  pthread_t tasksThread;
+  pthread_t controllerThread;
+
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitPantasks ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+  rl_event_hook = NULL;
+  rl_set_keyboard_input_timeout (1000); 
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  { 
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  signal (SIGPIPE, gotsignal);
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+
+  /* start up the background threads here */
+  pthread_create (&tasksThread,      NULL, &CheckTasksThread, 	   NULL);
+  pthread_create (&jobsThread,       NULL, &CheckJobsThread, 	   NULL);
+  pthread_create (&controllerThread, NULL, &CheckControllerThread, NULL);
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  QuitController ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreePantasks ();
+
+  return;
+}
+
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
+
+/* pantasks runs three (or four) threads:
+   - one thread parses commands from the connected clients
+   - one thread runs the scheduler loop
+   - one thread may run the controller loop independently
+   - the main thread runs readline and accepts user input
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pantasks_client.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pantasks_client.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pantasks_client.c.in	(revision 22322)
@@ -0,0 +1,78 @@
+# include "pantasks.h"
+
+# define opihi_name "pantasks client shell"
+# define opihi_prompt "pantasks: "
+# define opihi_description "parallel task scheduler\n"
+# define opihi_history ".pantasks"
+# define opihi_rcfile ".pantasksrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitPantasksClient ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+      char *helpdir;
+      char *modules;
+      static char *datadir = "@DATADIR@";
+      ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+      sprintf (helpdir, "%s/help", datadir);
+      set_str_variable ("HELPDIR", helpdir);
+      free (helpdir);
+      ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+      sprintf (modules, "%s/modules", datadir);
+      set_str_variable ("MODULES:0", modules);
+      set_int_variable ("MODULES:n", 1);
+      free (modules);
+  }
+
+  signal (SIGPIPE, gotsignal);
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreePantasksClient ();
+  return;
+}
+
+/* default signal handling */
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to client_shell: this opihi tool uses a slightly different
+   shell function from the standard tools */
+int main (int argc, char **argv) {
+  int status;
+  status = client_shell (argc, argv);
+  exit (status);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pantasks_server.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pantasks_server.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pantasks_server.c.in	(revision 22322)
@@ -0,0 +1,110 @@
+# include "pantasks.h"
+
+/* pantasks-server is not a readline program.
+   it runs three (or four) threads:
+   - one thread listens on the socket for incomming connections 
+     and performs client validation
+   - one thread parses commands from the connected clients
+   - one thread runs the scheduler loop
+   - one thread may run the controller loop independently
+   
+   * note: the client shell traps certain commands and performs them
+   locally. at a minimum: help.  perhaps: input, other I/O commands?
+*/
+
+/* program-dependent initialization */
+int main (int argc, char **argv) {
+  
+  char log_stdout[128], log_stderr[128];
+  pthread_t jobsThread;
+  pthread_t tasksThread;
+  pthread_t inputsThread;
+  pthread_t clientsThread;
+  pthread_t controllerThread;
+  int InitSocket, BindSocket;
+  SockAddress Address;
+
+  general_init (&argc, argv);
+
+  // 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);
+  }
+
+  DefineValidIP ();
+
+  /* XXX not sure what is a good setting for this in the server */
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitPantasksServer ();
+
+  { 
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  signal (SIGPIPE, gotsignal);
+  // signal (SIGTSTP, gotsignal);
+  // signal (SIGTTIN, gotsignal);
+  signal (SIGINT, SIG_DFL);
+
+  startup (&argc, argv);
+
+  /* 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);
+
+  /* in this loop, we listen for incoming connections, validate, and
+     pass them to the ListenClient thread */
+
+  /* create the listening socket */
+  InitSocket = InitServerSocket (&Address);
+  
+  InitPassword ();
+
+  while (1) {
+
+    /* wait for clients to make connection */
+    BindSocket = WaitServerSocket (InitSocket, &Address);
+    if (BindSocket == -1) continue;
+
+    /* validate : wait for password */
+    if (!CheckPassword (BindSocket)) continue;
+    
+    AddNewClient (BindSocket);
+  }
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  QuitController ();
+  ConfigFree ();
+  InitBasic ();
+  InitData ();
+  InitPantasksServer ();
+  return;
+}
+
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* these are needed to resolve symbols expected by libshell.so */
+void welcome () { }
+void program_init (int *argc, char **argv) {}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pulse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pulse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/pulse.c	(revision 22322)
@@ -0,0 +1,16 @@
+# include "pantasks.h"
+
+int pulse (int argc, char **argv) {
+
+  int Nusec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: pulse (microseconds)\n");
+    return (FALSE);
+  }
+
+  Nusec = atoi (argv[1]);
+  rl_set_keyboard_input_timeout (Nusec); 
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/run.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/run.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/run.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "pantasks.h"
+
+// XXX for client/server, we need to simply start or stop the
+// appropriate threads
+// with one thread for each of the major actions, this would
+// make it easy to keep the controller running and stop the 
+// scheduler (don't run CheckTasks, but run everything else 
+// until nothing is left...
+
+int run (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: run\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (TRUE);
+  CheckControllerSetState (TRUE);
+  CheckJobsSetState (TRUE);
+
+  // InitTaskTimers ();
+  // rl_event_hook = CheckSystem;
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "pantasks.h"
+
+/* most of these are defined in cmd.basic */
+int quit            PROTO((int, char **));
+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 **));
+int server_halt	    PROTO((int, char **));
+int cd              PROTO((int, char **));
+int pwd        	    PROTO((int, char **));
+int output     	    PROTO((int, char **));
+
+CommandF *FindServerCommand (char *cmd);
+
+static Command server_cmds[] = {
+  {1, "exit",   quit,   "shutdown server"},
+  {1, "quit",   quit,   "shutdown server"},
+  {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"},
+  {1, "halt",   server_halt, "halt scheduler"},
+  {1, "cd",     cd,     "set local directory"},
+  {1, "pwd",    pwd,    "check local directory"},
+  {1, "output", output, "set server output destinations"},
+};
+
+int server (int argc, char **argv) {
+
+  int i, N, status;
+  CommandF *func;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: server (command) ... \n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "help")) {
+    N = sizeof (server_cmds) / sizeof (Command);
+
+    for (i = 0; i < N; i++) {
+      gprint (GP_LOG, "%-15s %s\n", server_cmds[i].name, server_cmds[i].help);
+    }
+    return (TRUE);
+  }
+
+  func = FindServerCommand (argv[1]);
+  if (func == NULL) {
+    gprint (GP_ERR, "invalid server command\n");
+    return (FALSE);
+  }
+
+  status = (*func)(argc - 1, argv + 1);
+  return (status);
+}
+
+CommandF *FindServerCommand (char *cmd) {
+
+  int i, N;
+
+  N = sizeof (server_cmds) / sizeof (Command);
+
+  for (i = 0; i < N; i++) {
+    if (!strcmp (server_cmds[i].name, cmd)) {
+      return (server_cmds[i].func);
+    }
+  }
+  return (NULL);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_connect.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_connect.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_connect.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "pantasks.h"
+
+int server_connect (int argc, char **argv) {
+
+  int server;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: disconnect\n");
+    return (FALSE);
+  }
+  
+  server = getServer ();
+  if (server) return (TRUE);
+
+  // close connection with server
+  // catch errors (don't exit)
+  multicommand_InitServer ();
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_disconnect.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_disconnect.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_disconnect.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "pantasks.h"
+
+int server_disconnect (int argc, char **argv) {
+
+  int server;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: disconnect\n");
+    return (FALSE);
+  }
+  
+  server = getServer ();
+  
+  if (!server) return (TRUE);
+
+  // close connection with server
+  multicommand_StopServer ();
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_load.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_load.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_load.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "pantasks.h"
+
+int server_load (int argc, char **argv) {
+
+  char *input;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: server load (file)\n");
+    return (FALSE);
+  }
+
+  // the allocated input string is eventually freed 
+  // by the InputQueue system
+  input = strcreate (argv[1]);
+  AddNewInput (input);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_run.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_run.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_run.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "pantasks.h"
+
+int server_run (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: server run\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (TRUE);
+  CheckJobsSetState (TRUE);
+  CheckControllerSetState (TRUE);
+  CheckInputsSetState (TRUE);
+  return (TRUE);
+}
+
+int server_stop (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: server stop\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  // CheckJobsSetState (FALSE);
+  // CheckControllerSetState (FALSE);
+  CheckInputsSetState (FALSE);
+  return (TRUE);
+}
+
+int server_halt (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: server halt\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  CheckJobsSetState (FALSE);
+  CheckControllerSetState (FALSE);
+  CheckInputsSetState (FALSE);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_shutdown.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_shutdown.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/server_shutdown.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "pantasks.h"
+
+int server_shutdown (int argc, char **argv) {
+
+  int status, server;
+  IOBuffer message;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: shutdown now\n");
+    return (FALSE);
+  }
+  if (strcmp (argv[1], "now")) {
+    gprint (GP_ERR, "USAGE: shutdown now\n");
+    return (FALSE);
+  }
+  
+  server = getServer ();
+  
+  if (!server) return (TRUE);
+
+  // send the command to the server instead
+  status = SendMessage (server, "exit");
+  fprintf (stderr, "exit status of %d\n", status);
+
+  // try to read from the server until we get a status of 0
+  status = ExpectMessage (server, 2.0, &message);
+  fprintf (stderr, "message status of %d\n", status);
+
+  // XXX I should probably loop until we get an exit status of -3 or EPIPE
+
+  // close connection with server
+  multicommand_StopServer ();
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/showtask.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/showtask.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/showtask.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include "pantasks.h"
+
+int showtask (int argc, char **argv) {
+
+  if (argc != 2) {
+    gprint (GP_LOG, "USAGE: showtask (task)\n");
+    return (FALSE);
+  }
+
+  ShowTask (argv[1]);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/status.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/status.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/status.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include "pantasks.h"
+
+int status_sys (int argc, char **argv) {
+
+  int N;
+
+  if (get_argument (argc, argv, "-h")) goto help;
+  if (get_argument (argc, argv, "-help")) goto help;
+  if (get_argument (argc, argv, "--help")) goto help;
+
+  if ((N = get_argument (argc, argv, "-tasks"))) {
+    remove_argument (N, &argc, argv);
+    ListTasks (FALSE);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskinfo"))) {
+    remove_argument (N, &argc, argv);
+    ListTasks (TRUE);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstats"))) {
+    remove_argument (N, &argc, argv);
+    if (argc == 2) {
+      ListTaskStats (argv[N]);
+    } else {
+      ListTaskStats (NULL);
+    }      
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstatsreset"))) {
+    remove_argument (N, &argc, argv);
+    if (argc == 2) {
+      ResetTaskStats (argv[N]);
+    } else {
+      ResetTaskStats (NULL);
+    }      
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "\n");
+  if (CheckTasksGetState()) {
+    gprint (GP_LOG, " Scheduler is running\n");
+  } else {
+    if (CheckJobsGetState()) {
+      gprint (GP_LOG, " Scheduler is stopped, harvesting jobs\n");
+    } else {
+      gprint (GP_LOG, " Scheduler is stopped\n");
+    }
+  }
+  if (CheckControllerStatus()) {
+    gprint (GP_LOG, " Controller is running\n");
+  } else {
+    gprint (GP_LOG, " Controller is stopped\n");
+  }
+  ListTasks (FALSE);
+  ListJobs ();
+  return (TRUE);
+
+help:
+  gprint (GP_LOG, "USAGE: status [options]\n");
+  gprint (GP_LOG, "       (without options: over system status)\n");
+  gprint (GP_LOG, "       -tasks     : list defined tasks\n");
+  gprint (GP_LOG, "       -taskinfo  : details for tasks\n");
+  gprint (GP_LOG, "       -taskstats : processing statistics for tasks\n");
+  gprint (GP_LOG, "       -taskstatsreset : reset processing statistics for tasks\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/status_server.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/status_server.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/status_server.c	(revision 22322)
@@ -0,0 +1,69 @@
+# include "pantasks.h"
+
+int status_server (int argc, char **argv) {
+
+  int N;
+
+  if (get_argument (argc, argv, "-h")) goto help;
+  if (get_argument (argc, argv, "-help")) goto help;
+  if (get_argument (argc, argv, "--help")) goto help;
+
+  if ((N = get_argument (argc, argv, "-tasks"))) {
+    remove_argument (N, &argc, argv);
+    ListTasks (FALSE);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskinfo"))) {
+    remove_argument (N, &argc, argv);
+    ListTasks (TRUE);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstats"))) {
+    remove_argument (N, &argc, argv);
+    if (argc == 2) {
+      ListTaskStats (argv[N]);
+    } else {
+      ListTaskStats (NULL);
+    }      
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstatsreset"))) {
+    remove_argument (N, &argc, argv);
+    if (argc == 2) {
+      ResetTaskStats (argv[N]);
+    } else {
+      ResetTaskStats (NULL);
+    }      
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "\n");
+  if (CheckTasksGetState()) {
+    gprint (GP_LOG, " Scheduler is running\n");
+  } else {
+    if (CheckJobsGetState()) {
+      gprint (GP_LOG, " Scheduler is stopped, harvesting jobs\n");
+    } else {
+      gprint (GP_LOG, " Scheduler is stopped\n");
+    }
+  }
+  if (CheckControllerStatus()) {
+    gprint (GP_LOG, " Controller is running\n");
+  } else {
+    gprint (GP_LOG, " Controller is stopped\n");
+  }
+  ListTasks (FALSE);
+  ListJobs ();
+  return (TRUE);
+
+help:
+  gprint (GP_LOG, "USAGE: status [options]\n");
+  gprint (GP_LOG, "       (without options: over system status)\n");
+  gprint (GP_LOG, "       -tasks     : list defined tasks\n");
+  gprint (GP_LOG, "       -taskinfo  : details for tasks\n");
+  gprint (GP_LOG, "       -taskstats : processing statistics for tasks\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/stop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/stop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/stop.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "pantasks.h"
+
+int stop (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: stop\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  // CheckControllerSetState (FALSE);
+  // CheckJobsSetState (FALSE);
+
+  return (TRUE);
+}
+
+int halt (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: halt\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  CheckControllerSetState (FALSE);
+  CheckJobsSetState (FALSE);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task.c	(revision 22322)
@@ -0,0 +1,100 @@
+# include "pantasks.h"
+# define prompt "> "
+
+int task (int argc, char **argv) {
+
+  int hash;
+  int ThisList, status;
+  char *input, *outline;
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  task = FindTask (argv[1]);
+  if (task == NULL) { /**** new task ****/
+    task = CreateTask (argv[1]);
+  } else {
+    RemoveTask (task);
+    SetNewTask (task);
+  }
+  /* 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.  
+     XXX If an outstanding job has a task deleted, it will not be able to complete... */
+
+  /* read in task from appropriate source (keyboard or list) until end */
+
+  /* allowed tokens: command, host, stderr, periods, trange, nmax, task.exit, task.exec, end */
+
+  ThisList = current_list_depth();
+  while (1) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+
+    if ((ThisList == 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "end task with 'END'\n");
+      continue;
+    }
+    if ((ThisList > 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "missing 'END' in task definition\n");
+      input = strcreate ("end");
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    stripwhite (input);
+    hash = TaskHash (input);
+    switch (hash) {
+
+      case TASK_EMPTY:
+      case TASK_COMMENT:
+	free (input);
+	break;
+
+      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? */ 
+	if (!ValidateTask (task, FALSE)) {
+	  DeleteNewTask ();
+	  return (FALSE);
+	}
+	RegisterNewTask ();
+	return (TRUE);
+	break;
+
+      case TASK_TRANGE:
+      case TASK_NMAX:
+      case TASK_HOST:
+      case TASK_EXIT:
+      case TASK_EXEC:
+      case TASK_STDOUT:
+      case TASK_STDERR:
+      case TASK_COMMAND:
+      case TASK_OPTIONS:
+      case TASK_PERIODS:
+      case TASK_NPENDING:
+      case TASK_ACTIVE:
+	status = command (input, &outline, TRUE);
+	if (outline != NULL) free (outline);
+	/* what to do if command is invalid?
+	   if (!status) return (FALSE); */
+	break;
+
+      case TASK_NONE:
+	gprint (GP_ERR, "unknown task command %s\n", input);
+	break;
+    }
+  }
+
+usage:
+  gprint (GP_ERR, "USAGE: task -list\n");
+  gprint (GP_ERR, "USAGE: task -longlist\n");
+  gprint (GP_ERR, "USAGE: task -show (task)\n");
+  gprint (GP_ERR, "USAGE: task <name>\n");
+  gprint (GP_ERR, "  (enter commands & task functions; end with the word 'END')\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_active.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_active.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_active.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "pantasks.h"
+
+int task_active (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "true")) {
+    task[0].active = TRUE;
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "false")) {
+    task[0].active = FALSE;
+    return (TRUE);
+  }
+
+  gprint (GP_ERR, "ERROR: invalid option: %s\n", argv[1]);
+  return (FALSE);
+
+usage:
+  gprint (GP_ERR, "USAGE: active (true|false)\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_command.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_command.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_command.c	(revision 22322)
@@ -0,0 +1,38 @@
+# include "pantasks.h"
+
+int task_command (int argc, char **argv) {
+
+  int i;
+  Task *task;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: command <command> <arg>. ..\n");
+    gprint (GP_ERR, "  (define command for this task)\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+
+  /* free existing memory used by argv */
+  if (task[0].argc != 0) {
+    for (i = 0; i < task[0].argc; i++) {
+      if (task[0].argv[i] != NULL) free (task[0].argv[i]);
+    }
+    free (task[0].argv);
+  }
+
+  /* create new memory for argv */
+  task[0].argc = argc - 1;
+  ALLOCATE (task[0].argv, char *, MAX (task[0].argc, 1));
+  for (i = 0; i < task[0].argc; i++) {
+    task[0].argv[i] = strcreate (argv[i+1]);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_host.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_host.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_host.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "pantasks.h"
+
+int task_host (int argc, char **argv) {
+
+  int N, RequiredHost;
+  Task *task;
+
+  RequiredHost = FALSE;
+  if ((N = get_argument (argc, argv, "-required"))) {
+    remove_argument (N, &argc, argv);
+    RequiredHost = TRUE;
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: host <name> [-required]\n");
+    gprint (GP_ERR, "  define host machine for this task\n");
+    gprint (GP_ERR, "  -required flags indicates controller must use this host\n");
+    gprint (GP_ERR, "  value of 'local' for host indicates process not using controller\n");
+    gprint (GP_ERR, "  value of 'anyhost' for host indicates controller may assign at will\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+  task[0].host_required = RequiredHost;
+
+  if (task[0].host != NULL) free (task[0].host);
+  task[0].host = NULL;
+
+  if (!strcasecmp (argv[1], "LOCAL")) return (TRUE);
+
+  task[0].host = strcreate (argv[1]);
+  return (TRUE);
+}
+
+/* apparently, local is the default! */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_macros.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_macros.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_macros.c	(revision 22322)
@@ -0,0 +1,151 @@
+# include "pantasks.h"
+# define D_NLINES 100
+# define prompt "> "
+
+int task_macros (int argc, char **argv) {
+
+  int i, N, NLINES, done, depth, ThisList;
+  char *input;
+  Macro *macro;
+  Task *task;
+
+  if (!strcmp (argv[0], "task.exec") && (argc != 1)) {
+    gprint (GP_ERR, "USAGE: task.exec\n");
+    gprint (GP_ERR, "  (define pre-exec macro for this task)\n");
+    return (FALSE);
+  }
+
+  if (!strcmp (argv[0], "task.exit") && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: task.exit (state)\n");
+    gprint (GP_ERR, "  (define exit state macro for this task)\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    return (FALSE);
+  }
+
+  macro = NULL;
+
+  /*** identify which Macro in Task this particular macro goes with ***/ 
+  if (!strcmp (argv[0], "task.exec")) {
+    if (task[0].exec != NULL) {
+      FreeMacro (task[0].exec);
+      free (task[0].exec);
+    }
+    ALLOCATE (task[0].exec, Macro, 1);
+    macro = task[0].exec;
+    macro[0].name = strcreate ("exec");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit") && !strcmp (argv[1], "timeout")) {
+    if (task[0].timeout != NULL) {
+      FreeMacro (task[0].timeout);
+      free (task[0].timeout);
+    }
+    ALLOCATE (task[0].timeout, Macro, 1);
+    macro = task[0].timeout;
+    macro[0].name = strcreate ("timeout");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit") && !strcmp (argv[1], "crash")) {
+    if (task[0].crash != NULL) {
+      FreeMacro (task[0].crash);
+      free (task[0].crash);
+    }
+    ALLOCATE (task[0].crash, Macro, 1);
+    macro = task[0].crash;
+    macro[0].name = strcreate ("crash");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit") && !strcmp (argv[1], "default")) {
+    if (task[0].defexit != NULL) {
+      FreeMacro (task[0].defexit);
+      free (task[0].defexit);
+    }
+    ALLOCATE (task[0].defexit, Macro, 1);
+    macro = task[0].defexit;
+    macro[0].name = strcreate ("default");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit")) {	/* generic exit function */
+    for (i = 0; i < task[0].Nexit; i++) { /* find exiting exit function with same name */
+      if (!strcmp (task[0].exit[i][0].name, argv[1])) {	/*** replace this one ***/
+	FreeMacro (task[0].exit[i]);
+	free (task[0].exit[i]);
+	ALLOCATE (task[0].exit[i], Macro, 1);
+	macro = task[0].exit[i];
+	macro[0].name = strcreate (argv[1]);
+	goto found;
+      }
+    }
+    /** create a new exit status macro **/
+    N = task[0].Nexit;
+    ALLOCATE (task[0].exit[N], Macro, 1);
+    task[0].Nexit ++;
+    if (task[0].Nexit == task[0].NEXIT) {
+      task[0].NEXIT += 10;
+      REALLOCATE (task[0].exit, Macro *, task[0].NEXIT);
+    }
+    macro = task[0].exit[N];
+    macro[0].name = strcreate (argv[1]);
+    goto found;
+  }
+
+found:
+
+  macro[0].Nlines = 0;
+  NLINES = D_NLINES;
+  ALLOCATE (macro[0].line, char *, NLINES);
+
+  /* load macro lines from appropriate source (readline / list) */
+
+  depth = 0;
+  done = FALSE;
+  ThisList = current_list_depth();
+  while (!done) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+
+    if ((ThisList == 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "end macro with 'END'\n");
+      continue;
+    }
+    if ((ThisList > 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "missing 'END' in macro definition\n");
+      input = strcreate ("end");
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    stripwhite (input);
+
+    /* test for new macro (or other list, in the future?) */
+    if (is_list (input)) depth ++;
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { /* we hit the last "END", macro is done */
+	free (input);
+	REALLOCATE (macro[0].line, char *, MAX (1, macro[0].Nlines));
+	return (TRUE);
+      }
+    }
+
+    if (*input) { 
+      macro[0].line[macro[0].Nlines] = input;
+      macro[0].Nlines ++;
+      if (macro[0].Nlines == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (macro[0].line, char *, NLINES);
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_nmax.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_nmax.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_nmax.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "pantasks.h"
+
+int task_nmax (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    return (FALSE);
+  }
+
+  task[0].Nmax = atoi (argv[1]);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: nmax N\n");
+  return (FALSE);
+}
+
+int task_npending (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    return (FALSE);
+  }
+
+  task[0].NpendingMax = atoi (argv[1]);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: npending N\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_options.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_options.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_options.c	(revision 22322)
@@ -0,0 +1,38 @@
+# include "pantasks.h"
+
+int task_options (int argc, char **argv) {
+
+  int i;
+  Task *task;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: options <opt>. ..\n");
+    gprint (GP_ERR, "  (define options for this task)\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+
+  /* free existing memory used by optv */
+  if (task[0].optc != 0) {
+    for (i = 0; i < task[0].optc; i++) {
+      if (task[0].optv[i] != NULL) free (task[0].optv[i]);
+    }
+    free (task[0].optv);
+  }
+
+  /* create new memory for optv */
+  task[0].optc = argc - 1;
+  ALLOCATE (task[0].optv, char *, MAX (task[0].optc, 1));
+  for (i = 0; i < task[0].optc; i++) {
+    task[0].optv[i] = strcreate (argv[i+1]);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_periods.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_periods.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_periods.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "pantasks.h"
+
+int task_periods (int argc, char **argv) {
+
+  int N, Poll, Exec, Timeout;
+  float PollValue, ExecValue, TimeoutValue;
+  Task *task;
+
+  PollValue = 0;
+  Poll = FALSE;
+  if ((N = get_argument (argc, argv, "-poll"))) {
+    Poll = TRUE;
+    remove_argument (N, &argc, argv);
+    PollValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  ExecValue = 0;
+  Exec = FALSE;
+  if ((N = get_argument (argc, argv, "-exec"))) {
+    Exec = TRUE;
+    remove_argument (N, &argc, argv);
+    ExecValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  TimeoutValue = 0;
+  Timeout = FALSE;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    Timeout = TRUE;
+    remove_argument (N, &argc, argv);
+    TimeoutValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: periods [-poll value] [-exec value] [-timeout value]\n");
+    gprint (GP_ERR, "  define time periods for this task\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+
+  if (Poll) task[0].poll_period = PollValue;
+  if (Exec) task[0].exec_period = ExecValue;
+  if (Timeout) task[0].timeout_period = TimeoutValue;
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_stdout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_stdout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_stdout.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include "pantasks.h"
+
+int task_stdout (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stdout <name>\n");
+    gprint (GP_ERR, "  define dump file for job stdout for this task\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+  if (task[0].stdout_dump != NULL) free (task[0].stdout_dump);
+  task[0].stdout_dump = strcreate (argv[1]);
+  return (TRUE);
+}
+
+int task_stderr (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stderr <name>\n");
+    gprint (GP_ERR, "  define dump file for job stderr for this task\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+  if (task[0].stderr_dump != NULL) free (task[0].stderr_dump);
+  task[0].stderr_dump = strcreate (argv[1]);
+  return (TRUE);
+}
+
+/* apparently, local is the default! */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_threads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_threads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_threads.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "pantasks.h"
+
+/** things related to CheckTasks **/
+
+static int CheckTasksRun = FALSE;
+
+void CheckTasksSetState (int state) {
+  CheckTasksRun = state;
+}
+int CheckTasksGetState () {
+  return (CheckTasksRun);
+}
+
+void *CheckTasksThread (void *data) {
+
+  float next_timeout;
+  char log_stdout[128], log_stderr[128];
+
+  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) {
+
+    // check for thread suspend
+    if (!CheckTasksRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    next_timeout = CheckTasks ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "T");
+
+    next_timeout = MIN (next_timeout, 0.1);
+    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.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_trange.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_trange.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/task_trange.c	(revision 22322)
@@ -0,0 +1,96 @@
+# include "pantasks.h"
+
+int task_trange (int argc, char **argv) {
+
+  int N;
+  Task *task;
+  TimeRange range;
+
+  /* reset the tranges for the current task */
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    if (argc != 1) goto usage;
+
+    task = GetNewTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+
+    task[0].Nranges = 0;
+    REALLOCATE (task[0].ranges, TimeRange, 1);
+    return (TRUE);
+  } 
+
+  range.Nmax = 0;
+  range.Nrun = 0;
+  if ((N = get_argument (argc, argv, "-nmax"))) {
+    remove_argument (N, &argc, argv);
+    range.Nmax = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* keep = true means the time range defines a valid time range
+     keep = false means the time range defines an invalid range */
+  range.include = TRUE;
+  if ((N = get_argument (argc, argv, "-exclude"))) {
+    remove_argument (N, &argc, argv);
+    range.include = FALSE;
+  }
+
+  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 */
+  if (day_to_sec (argv[1], &range.start)) {
+    if (!day_to_sec (argv[2], &range.stop)) {
+      gprint (GP_ERR, "invalid day/time %s\n", argv[2]);
+      goto usage;
+    }
+    range.type = RANGE_WEEK;
+    goto valid;
+  }
+
+  /* allow a syntax which means something like "every hour at MM:SS"
+     this could by something like *:MM:SS ? */
+
+  /* test for HH:MM:SS */
+  if (hms_to_sec (argv[1], &range.start)) {
+    if (!hms_to_sec (argv[2], &range.stop)) {
+      gprint (GP_ERR, "invalid time %s\n", argv[2]);
+      goto usage;
+    }
+    range.type = RANGE_DAY;
+    goto valid;
+  }
+
+  /* this test does not fail sufficiently robustly for invalid inputs */
+  /* test for YYYY/MM/DD - both must match */
+  if (ohana_str_to_time (argv[1], &range.start)) {
+    if (!ohana_str_to_time (argv[2], &range.stop)) {
+      gprint (GP_ERR, "invalid date/time %s\n", argv[2]);
+      goto usage;
+    }
+    range.type = RANGE_ABS;
+    goto valid;
+  }
+  goto usage;
+
+valid:
+  N = task[0].Nranges;
+  task[0].Nranges ++;
+  REALLOCATE (task[0].ranges, TimeRange, task[0].Nranges);
+  
+  task[0].ranges[N] = range;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: trange start end [-nmax N]\n");
+  gprint (GP_ERR, "USAGE: trange -reset\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/iotest.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/iotest.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/iotest.sh	(revision 22322)
@@ -0,0 +1,57 @@
+
+$BLOCK = 0
+
+macro testloop
+  queuepush stdout start
+  for i 0 5
+    echo $i
+    queueprint stdout
+  end
+end
+
+# a basic task which just runs 'echo foobar'
+task	       basic
+  command      echo foobar
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    if ($BLOCK) break
+    queuepush stdout start
+    echo "command start"
+    queuelist
+    queueprint stdout
+    queueinit stdout
+    queuelist
+    echo "command stop"
+  end
+
+  # success
+  task.exit    0
+    $BLOCK = 1
+    echo "exit start"
+    queuelist
+    queueprint stdout
+    queueinit stdout
+    queuelist
+    echo "exit stop"
+    $BLOCK = 0
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/joberror.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/joberror.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/joberror.sh	(revision 22322)
@@ -0,0 +1,29 @@
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  command      ls -l /usr/bin /tmp/foobar
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $JOB_STATUS"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/load.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/load.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/load.sh	(revision 22322)
@@ -0,0 +1,37 @@
+
+# a basic task which just runs 'sleep'
+task	       newtask
+  # command      sleep 1
+  command      echo hi
+  host         local
+
+  periods      -poll 0.01
+  periods      -exec 0.01
+  periods      -timeout 20
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+    # status
+    # halt
+  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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local.sh	(revision 22322)
@@ -0,0 +1,78 @@
+
+## a basic test of memory allocation
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 0.2
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ # echo growth: {$endmem - $startmem}
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($Npass/$dtime < 4.2) 
+   echo "tasks running too slow: {$Npass/$dtime}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local2.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local2.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local2.sh	(revision 22322)
@@ -0,0 +1,79 @@
+
+## a basic test of memory allocation
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 0.2
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ echo growth: {$endmem - $startmem}
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($Npass/$dtime < 4.2) 
+   echo "tasks running too slow: {$Npass/$dtime}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local3.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local3.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local3.sh	(revision 22322)
@@ -0,0 +1,88 @@
+
+## a basic test of memory allocation
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local4.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local4.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local4.sh	(revision 22322)
@@ -0,0 +1,93 @@
+
+## a basic test of memory allocation
+## task.exec test
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Nrun = 1
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local5.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local5.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local5.sh	(revision 22322)
@@ -0,0 +1,93 @@
+
+## a basic test of memory allocation
+## dynamic command
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Nrun = 1
+    command echo "$Npass : test line"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local6.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local6.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local6.sh	(revision 22322)
@@ -0,0 +1,94 @@
+
+## a basic test of memory allocation
+## test of 'options'
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Nrun = 1
+    options "test options line"
+    command echo "$Npass : test line"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local7.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local7.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/local7.sh	(revision 22322)
@@ -0,0 +1,127 @@
+
+## a basic test of memory allocation
+## test of 'exec failure'
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+$Ntry = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Ntry ++
+    if ($Ntry > -1) break
+
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+
+    command echo "$Npass : test line"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+ end
+
+ # this task generates no output files
+ if ($VERBOSE > 3)
+   echo "task test speed: {$dtime/$Ntry} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Ntry > 0.010) 
+   echo "tasks tests running too slow: {$dtime/$Ntry}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/sleep.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/sleep.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/sleep.sh	(revision 22322)
@@ -0,0 +1,34 @@
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      sleep 10
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 0.2
+  periods      -timeout 20
+  npending 5
+  
+  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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/sleep2.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/sleep2.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/sleep2.sh	(revision 22322)
@@ -0,0 +1,39 @@
+
+$hostname = `hostname`
+
+controller exit true
+controller host add $hostname
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      sleep 1
+  host         anyhost
+
+  periods      -poll 0.01
+  periods      -exec 0.02
+  periods      -timeout 20
+  npending 100
+  
+  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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/sleep3.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/sleep3.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/sleep3.sh	(revision 22322)
@@ -0,0 +1,47 @@
+
+$hostname = `hostname`
+
+controller exit true
+controller host add po02
+controller host add po03
+controller host add po04
+controller host add po05
+controller host add po06
+controller host add po07
+controller host add po08
+controller host add po09
+controller host add po10
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      sleep 1
+  host         anyhost
+
+  periods      -poll 0.01
+  periods      -exec 0.02
+  periods      -timeout 20
+  npending 100
+  
+  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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/threads.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/threads.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/test/threads.sh	(revision 22322)
@@ -0,0 +1,40 @@
+
+$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      echo -threads @MAX_THREADS@
+  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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/thread_locks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/thread_locks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/thread_locks.c	(revision 22322)
@@ -0,0 +1,14 @@
+# include "pantasks.h"
+
+/* this mutex is used by the serialized threads */
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void SerialThreadLock () {
+    pthread_mutex_lock (&mutex);
+}
+
+void SerialThreadUnlock () {
+    pthread_mutex_unlock (&mutex);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/verbose.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/verbose.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/verbose.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "pantasks.h"
+
+static int VERBOSE = FALSE;
+
+int verbose (int argc, char **argv) {
+
+  if (argc == 1) {
+    if (VERBOSE) {
+      gprint (GP_LOG, "verbose mode ON\n");
+    } else {
+      gprint (GP_LOG, "verbose mode OFF\n");
+    }
+    return (TRUE);
+  }
+
+  if (argc == 2) {
+    if (!strcasecmp (argv[1], "ON")) {
+      VERBOSE = TRUE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "OFF")) {
+      VERBOSE = FALSE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "THREADS")) {
+      VERBOSE = 2;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "TOGGLE")) {
+      VERBOSE = ~VERBOSE;
+      return (TRUE);
+    }
+  }
+
+  gprint (GP_ERR, "USAGE: verbose (on/off/threads/toggle)\n");
+  return (FALSE);
+}
+
+int VerboseMode () {
+  return (VERBOSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pantasks/version.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "pantasks.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pantasks version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/ChildOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/ChildOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/ChildOps.c	(revision 22322)
@@ -0,0 +1,145 @@
+# include "pclient.h"
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+// #include <stropts.h>
+
+static int Nbad = 0;
+static struct timeval last = {0, 0};
+
+int InitChild () {
+
+  ChildPID         = 0;                /** current child PID **/
+  ChildStatus      = PCLIENT_NONE;     /** current status of child **/
+  ChildExitStatus  = 0;                /** recent exit status of child **/
+  
+  child_stdin_fd[0]  = 0;
+  child_stdin_fd[1]  = 0;
+  child_stdout_fd[0] = 0;
+  child_stdout_fd[1] = 0;
+  child_stderr_fd[0] = 0;
+  child_stderr_fd[1] = 0;
+
+  InitIOBuffer (&child_stdout, 1024);
+  InitIOBuffer (&child_stderr, 1024);
+  return (TRUE);
+}
+
+int FreeChild () {
+
+  FreeIOBuffer (&child_stdout);
+  FreeIOBuffer (&child_stderr);
+  return (TRUE);
+}
+
+int CheckChild () {
+
+  int Nread;
+  double dtime;
+  struct timeval now;
+
+  /* runaway test - if pcontrol is killed, pclient starts running away.  this test is a bit
+     dangerous: the choice of dtime probably depends on the processor and the value provided to
+     pclient.c:rl_set_keyboard_input_timeout (1000); note that we cannot use getppid == 1 as a test
+     because the parent of pclient is the ssh process on the pclient host, not pcontrol.  in any
+     case, the opihi shell catches if the ssh dies using getppid
+   */
+  gettimeofday (&now, (void *) NULL);
+  dtime = 1e6*DTIME (now, last);
+  if (dtime < 50) {
+    Nbad ++;
+    if (Nbad > 100) {
+      gprint (GP_ERR, "serious IO error\n");
+      abort ();
+    }
+  }
+  if (dtime > 950) Nbad = 0;
+  last = now;
+
+  CheckChildStatus ();
+  
+  /* read stdout buffer */
+  Nread = ReadtoIOBuffer (&child_stdout, child_stdout_fd[0]);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      gprint (GP_ERR, "serious IO error\n");
+      abort ();
+    case -1:  /* no data in pipe */
+      break;
+    case 0:   /* pipe is closed */
+      /** change child state? **/
+      break;
+    default:  /* data in pipe */
+      break;
+  }
+  
+  /* read stderr buffer */
+  Nread = ReadtoIOBuffer (&child_stderr, child_stderr_fd[0]);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      gprint (GP_ERR, "serious IO error\n");
+      abort ();
+    case -1:  /* no data in pipe */
+      break;
+    case 0:   /* pipe is closed */
+      /** change child state? **/
+      break;
+    default:  /* data in pipe */
+      break;
+  }
+  return (TRUE);
+}
+
+void CheckChildStatus () {
+
+  int result, waitstatus;
+
+  if (ChildStatus != PCLIENT_BUSY) return;
+
+  /* check current child status */
+  result = waitpid (ChildPID, &waitstatus, WNOHANG);
+  switch (result) {
+    case -1:  /* error with waitpid */
+      switch (errno) {
+	case ECHILD:
+	  gprint (GP_ERR, "unknown PID, not a child proc\n");
+	  gprint (GP_ERR, "did process already exit?  programming error?\n");
+	  ChildStatus = PCLIENT_NONE;
+	  break;
+	case EINVAL:
+	  gprint (GP_ERR, "error EINVAL (waitpid): programming error\n");
+	  exit (1);
+	case EINTR:
+	  gprint (GP_ERR, "error EINTR (waitpid): programming error\n");
+	  exit (1);
+	default:
+	  gprint (GP_ERR, "unknown error for waitpid (%d): programming error\n", errno);
+	  exit (1);
+      }
+      break;
+      
+    case 0:  /* process not exited */
+      ChildStatus = PCLIENT_BUSY;
+      break;
+
+    default:
+      if (result != ChildPID) {
+	gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, ChildPID);
+	exit (1);
+      }
+      
+      if (WIFEXITED(waitstatus)) {
+	ChildStatus = PCLIENT_EXIT;
+	ChildExitStatus = WEXITSTATUS(waitstatus);
+      }
+      if (WIFSIGNALED(waitstatus)) {
+	ChildStatus = PCLIENT_CRASH;
+	ChildExitStatus = WTERMSIG(waitstatus);
+      }
+      if (WIFSTOPPED(waitstatus)) {
+	gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+	exit (1);
+      }
+  }
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/Makefile	(revision 22322)
@@ -0,0 +1,46 @@
+default: pclient
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SDIR    =       $(HOME)/pclient
+DATA    =       $(DESTDATA)/pclient
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1         = -lbasiccmd -lshell -ldata 
+LIBS2         = -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# pclient user commands and support functions ########################
+funcs = \
+$(SDIR)/init.$(ARCH).o \
+$(SDIR)/pclient.$(ARCH).o \
+$(SDIR)/ChildOps.$(ARCH).o \
+
+cmds = \
+$(SDIR)/job.$(ARCH).o \
+$(SDIR)/reset.$(ARCH).o \
+$(SDIR)/check.$(ARCH).o \
+$(SDIR)/status.$(ARCH).o \
+$(SDIR)/stdout.$(ARCH).o  \
+$(SDIR)/version.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a
+
+pclient: $(BIN)/pclient.$(ARCH)
+$(SRC)/pclient.$(ARCH).o : $(libs)
+$(BIN)/pclient.$(ARCH)   : $(funcs) $(cmds)
+
+install: $(DESTBIN)/pclient help
+
+help: clean-help cmd.basic.help pclient.help
+
+.PHONY: pclient
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/check.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/check.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/check.c	(revision 22322)
@@ -0,0 +1,9 @@
+# include "pclient.h"
+
+int check (int argc, char **argv) {
+
+  /* force a check */
+  CheckChild ();
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/help/job
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/help/job	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/help/job	(revision 22322)
@@ -0,0 +1,1 @@
+0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/init.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/init.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/init.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include "pclient.h"
+
+int job	            PROTO((int, char **));
+int reset           PROTO((int, char **));
+int check           PROTO((int, char **));
+int status	    PROTO((int, char **));
+int stdout_pclient  PROTO((int, char **));
+int stderr_pclient  PROTO((int, char **));
+int version         PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "job",       job,      "start job"},
+  {1, "reset",     reset,    "reset job"},
+  {1, "check",     check,    "check job"},
+  {1, "status",    status,   "check job status"},
+  {1, "stdout",    stdout_pclient,   "get stdout buffer"},
+  {1, "stderr",    stderr_pclient,   "get stderr buffer"},
+  {1, "version",   version,      "show version information"},
+}; 
+
+void InitPclient () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+  InitChild ();
+}
+
+void FreePclient () {
+  FreeChild ();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/job.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/job.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/job.c	(revision 22322)
@@ -0,0 +1,98 @@
+# include "pclient.h"
+
+int job (int argc, char **argv) {
+
+  int i, pid, status;
+  char **targv;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: job (arg0) (arg1) ... (argN)\n");
+    gprint (GP_LOG, "STATUS %d\n", -2);
+    return (FALSE);
+  }
+  
+  if (ChildStatus != PCLIENT_NONE) {
+    gprint (GP_ERR, "need to clear existing child\n");
+    gprint (GP_LOG, "STATUS %d\n", -3);
+    return (FALSE);
+  }
+
+  if (pipe (child_stdin_fd)  < 0) goto pipe_error;
+  if (pipe (child_stdout_fd) < 0) goto pipe_error;
+  if (pipe (child_stderr_fd) < 0) goto pipe_error;
+
+  /* need to define arg list with NULL termination */
+  ALLOCATE (targv, char *, argc);
+  for (i = 1; i < argc; i++) {
+    targv[i-1] = strcreate (argv[i]);
+  }
+  targv[i-1] = 0;
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    /* gprint (GP_ERR, "starting child process %s...\n", targv[0]); */
+
+    /* close the other ends of the pipes */
+    close (child_stdin_fd[1]);
+    close (child_stdout_fd[0]);
+    close (child_stderr_fd[0]);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (child_stdin_fd[0],  STDIN_FILENO);
+    dup2 (child_stdout_fd[1], STDOUT_FILENO);
+    dup2 (child_stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    /* exec job */ 
+    status = execvp (targv[0], targv); 
+    gprint (GP_ERR, "error starting child process: %d\n", status);
+    exit (1);
+  }
+
+  /* free temporary arg list */
+  for (i = 0; i < argc - 1; i++) {
+    free (targv[i]);
+  }
+  free (targv);
+
+  /* close the other ends of the pipes */
+  close (child_stdin_fd[0]);
+  close (child_stdout_fd[1]);
+  close (child_stderr_fd[1]);
+
+  /* make the pipes non-blocking */
+  fcntl (child_stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (child_stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (child_stderr_fd[0], F_SETFL, O_NONBLOCK);
+  
+  ChildStatus = PCLIENT_BUSY;
+  ChildPID = pid;
+
+  gprint (GP_LOG, "STATUS %d\n", ChildPID);
+  return (TRUE);
+
+pipe_error:
+  perror ("pipe error:");
+  if (child_stdin_fd[0]  != 0) close (child_stdin_fd[0]);
+  if (child_stdin_fd[1]  != 0) close (child_stdin_fd[1]);
+  if (child_stdout_fd[0] != 0) close (child_stdout_fd[0]);
+  if (child_stdout_fd[1] != 0) close (child_stdout_fd[1]);
+  if (child_stderr_fd[0] != 0) close (child_stderr_fd[0]);
+  if (child_stderr_fd[1] != 0) close (child_stderr_fd[1]);
+
+  gprint (GP_LOG, "STATUS %d\n", -1);
+  return (FALSE);
+}
+
+/* possible responses:
+
+STATUS -1 - pipe error
+STATUS -2 - syntax error
+STATUS -3 - existing child
+STATUS >0 - success (PID)
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/notes.txt	(revision 22322)
@@ -0,0 +1,17 @@
+
+pclient special commands:
+
+ job 
+
+ reset
+
+ check
+
+ status
+
+ stdout
+
+ stderr
+
+- communication with pclient should search for the pclient prompt:
+  pclient:
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/outline.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/outline.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/outline.txt	(revision 22322)
@@ -0,0 +1,12 @@
+
+init stage:
+
+  - set child 
+
+readline loop:
+
+  - wait for commands
+
+
+
+  
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/pclient.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/pclient.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/pclient.c.in	(revision 22322)
@@ -0,0 +1,80 @@
+# include "pclient.h"
+
+# define opihi_name "PCLIENT"
+# define opihi_prompt "pclient: "
+# define opihi_description "pcontrol client shell\n"
+# define opihi_history ""
+# define opihi_rcfile ".pcontrolrc"
+
+void dummy_prep_term (int flag) {
+}
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitPclient ();
+
+  rl_prep_term_function = dummy_prep_term;
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+  rl_event_hook = CheckChild;
+  rl_set_keyboard_input_timeout (20000); 
+  /* 1 ms seems to be the minimum valid number */
+
+  // set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  /* ignore the history file.  to change this, see, eg, mana.c */
+  signal (SIGPIPE, gotsignal);
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  ConfigFree ();
+  FreeBasic ();
+  FreePclient ();
+  gprint (GP_LOG, "Goodbye\n");
+  return;
+}
+
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/reset.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/reset.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/reset.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "pclient.h"
+
+int reset (int argc, char **argv) {
+
+  int i, result, waitstatus;
+  struct timespec request, remain;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: reset\n");
+    gprint (GP_LOG, "STATUS -1\n");
+    return (FALSE);
+  }
+  
+  if (ChildStatus == PCLIENT_NONE) {
+    gprint (GP_ERR, "no child process, cannot reset\n");
+    gprint (GP_LOG, "STATUS 2\n");
+    return (TRUE);
+  }
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  if (ChildStatus == PCLIENT_BUSY) {
+    /* job is still running, send SIGTERM signal to job */
+    kill (ChildPID, SIGTERM);
+    result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    for (i = 0; (i < 500) && (result == 0); i++) {
+      nanosleep (&request, &remain);
+      result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    }
+    if (result) goto reset_job;
+
+    /* job did not exit, send SIGKILL signal to job */
+    kill (ChildPID, SIGKILL);
+    result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    for (i = 0; (i < 500) && (result == 0); i++) {
+      nanosleep (&request, &remain);
+      result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    }
+    if (result) goto reset_job;
+
+    /* total failure, don't reset */
+    gprint (GP_ERR, "child process %d is hung, cannot reset\n", ChildPID);
+    gprint (GP_LOG, "STATUS 0\n");
+    return (FALSE);
+  }
+
+reset_job:
+  /* close out ends of the pipes */
+  close (child_stdin_fd[1]);
+  close (child_stdout_fd[0]);
+  close (child_stderr_fd[0]);
+  child_stdin_fd[1] = 0;
+  child_stdout_fd[0] = 0;
+  child_stderr_fd[0] = 0;
+
+  FlushIOBuffer (&child_stdout);
+  FlushIOBuffer (&child_stderr);
+  ChildStatus = PCLIENT_NONE;
+  ChildPID = 0;
+  ChildExitStatus = 0;
+
+  gprint (GP_LOG, "STATUS 1\n");
+  return (TRUE);
+}
+
+/* exit conditions:
+   -1 - usage message / syntax error
+    2 - unknown job
+    0 - process hung
+    1 - successful resetn
+*/
+
+/* the linux kernel timer sticks in a 10ms lag between kill and the harvest */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/status.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/status.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/status.c	(revision 22322)
@@ -0,0 +1,24 @@
+# include "pclient.h"
+
+int status (int argc, char **argv) {
+
+  char status_string[64];
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: status\n");
+    return (FALSE);
+  }
+  
+  if (ChildStatus == PCLIENT_NONE)  strcpy (status_string, "NONE");
+  if (ChildStatus == PCLIENT_BUSY)  strcpy (status_string, "BUSY");
+  if (ChildStatus == PCLIENT_EXIT)  strcpy (status_string, "EXIT");
+  if (ChildStatus == PCLIENT_CRASH) strcpy (status_string, "CRASH");
+
+  gprint (GP_LOG, "STATUS %s\n", status_string);
+  gprint (GP_LOG, "EXITST %d\n", ChildExitStatus);
+  gprint (GP_LOG, "STDOUT %d\n", child_stdout.Nbuffer);
+  gprint (GP_LOG, "STDERR %d\n", child_stderr.Nbuffer);
+
+  set_str_variable ("JOBSTATUS", status_string);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/stdout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/stdout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/stdout.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "pclient.h"
+
+int stdout_pclient (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: stdout\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  fwrite (child_stdout.buffer, 1, child_stdout.Nbuffer, stdout);
+  gprint (GP_LOG, "STATUS %d\n", 0);
+  return (TRUE);
+
+}
+
+int stderr_pclient (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: stderr\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  fwrite (child_stderr.buffer, 1, child_stderr.Nbuffer, stdout);
+  gprint (GP_LOG, "STATUS %d\n", 0);
+  return (TRUE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/test/status.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/test/status.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/test/status.sh	(revision 22322)
@@ -0,0 +1,64 @@
+
+list tests
+ test1
+ test2
+end
+
+# check status without any jobs
+macro test1
+ $PASS = 1
+ break -auto off
+
+ output -err /dev/null
+ reset
+ exec rm -f test.txt
+ output -err stderr
+
+ output test.txt
+ status
+ output stdout
+
+ # this test requires a specific set of output
+ list line -x "cat test.txt"
+ if ($line:n != 4)
+   $PASS = 0
+ end
+ if ("$line:0" != "STATUS NONE")
+   $PASS = 0
+ end
+ if ("$line:1" != "EXITST 0")
+   $PASS = 0
+ end
+ if ("$line:2" != "STDOUT 0")
+   $PASS = 0
+ end
+ if ("$line:3" != "STDERR 0")
+   $PASS = 0
+ end
+end
+
+# check status with a basic job
+macro test2
+ $PASS = 1
+ break -auto off
+
+ output -err /dev/null
+ reset
+ exec rm -f test.txt
+ job ls 
+ usleep 500000
+ output -err stderr
+
+ output test.txt
+ status
+ output stdout
+
+ # this test requires a specific set of output
+ list line -x "cat test.txt"
+ if ($line:n != 4)
+   $PASS = 0
+ end
+ if ("$line:0" != "STATUS EXIT")
+   $PASS = 0
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/test/version.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/test/version.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/test/version.sh	(revision 22322)
@@ -0,0 +1,106 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+end
+
+## these tests check that the version command reports the versions
+## they do not validate the actual versions themselves
+
+# set up the version output file
+macro test1
+ $PASS = 1
+ break -auto off
+ exec rm test.txt
+ output -err test.txt
+ version
+ output -err stderr
+ file test.txt found
+ if ($found != 1)
+   $PASS = 0
+ end
+end
+
+# test for the correct number of version lines
+macro test2
+ $PASS = 1
+ break -auto off
+ 
+ $line = `wc -l test.txt`
+
+ list word -split $line
+ if ($word:0 != 6)
+   $PASS = 0
+ end
+end
+
+# test for pclient version name
+macro test3
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:0
+
+ if ("$word:0" != "pclient")
+   $PASS = 0
+ end
+end
+
+# test for opihi version name
+macro test4
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:1
+
+ if ("$word:0" != "opihi")
+   $PASS = 0
+ end
+end
+
+# test for ohana version name
+macro test5
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:2
+
+ if ("$word:0" != "ohana")
+   $PASS = 0
+ end
+end
+
+# test for gfits version name
+macro test6
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:3
+
+ if ("$word:0" != "gfits")
+   $PASS = 0
+ end
+end
+
+# test for compilation date/time
+macro test7
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:4
+
+ if ("$word:0" != "compiled")
+   $PASS = 0
+ end
+end
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pclient/version.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "pclient.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pclient version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckBusyJob.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckBusyJob.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckBusyJob.c	(revision 22322)
@@ -0,0 +1,105 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+int CheckBusyJob (Job *job, Host *host) {
+
+  int status;
+
+  /* we are checking a job which is currently busy.  it has been pulled from the
+     JOB_BUSY stack, and is linked to a host in the HOST_BUSY stack.  
+     XXX need to check on state of HOST on return */
+
+  ASSERT (job, "job not set");
+  ASSERT (host, "host not set");
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job  == (Job *) host[0].job, "invalid job");
+
+  status = PclientCommand (host, "status", PCLIENT_PROMPT, PCONTROL_RESP_CHECK_BUSY_JOB);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      // free the realhost name
+      if (job[0].realhost) free (job[0].realhost);
+      job[0].realhost = NULL;
+
+      // unlink host & job
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      job[0].host = NULL;
+      host[0].job = NULL;
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "message received (CheckBusyJob)");
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+}
+
+int CheckBusyJobResponse (Host *host) {
+
+  int      outstate;
+  char    *p;
+  char     string[64];
+  IOBuffer *buffer;
+  Job *job;
+
+  /* job must have assigned host */
+  ASSERT (host, "missing host");
+  ASSERT (host[0].job, "missing job");
+  buffer = &host[0].comms_buffer;
+  job = (Job *) host[0].job;
+
+  /** host is up, need to parse message **/
+  p = memstr (buffer[0].buffer, "STATUS", buffer[0].Nbuffer);
+  if (p == NULL) {
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "missing STATUS in response; try again\n");
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+      return (TRUE);
+  }
+
+  sscanf (p, "%*s %s", string);
+  ASSERT (strcmp(string, "NONE"), "no current job\n");
+
+  /** no status change, return to BUSY stack **/
+  if (!strcmp(string, "BUSY")) {
+    PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+    PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+    return (TRUE);
+  }
+
+  /* exit status better be either EXIT or CRASH */
+  outstate = PCONTROL_JOB_BUSY;
+  if (!strcmp(string, "EXIT")) outstate = PCONTROL_JOB_EXIT;
+  if (!strcmp(string, "CRASH")) outstate = PCONTROL_JOB_CRASH;
+  ASSERT (outstate != PCONTROL_JOB_BUSY, "invalid status response (CheckBusyJobResponse)");
+
+  /* parse the exit status and sizes of output buffers */
+  p = memstr (buffer[0].buffer, "EXITST", buffer[0].Nbuffer);
+  sscanf (p, "%*s %d", &job[0].exit_status);
+  p = memstr (buffer[0].buffer, "STDOUT", buffer[0].Nbuffer);
+  sscanf (p, "%*s %d", &job[0].stdout_size);
+  p = memstr (buffer[0].buffer, "STDERR", buffer[0].Nbuffer);
+  sscanf (p, "%*s %d", &job[0].stderr_size);
+
+  // XXX runaway job if output too large?
+  if (job[0].stdout_size > 0x100000) abort();
+  if (job[0].stderr_size > 0x100000) abort();
+
+  // job has exited : move to DONE stack 
+  // the host is still BUSY until job output is gathered (int CheckDoneJob)
+  // don't unlink job and host yet
+  PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+  PutJobSetState (job, PCONTROL_JOB_DONE, STACK_BOTTOM, outstate);
+  gettimeofday (&job[0].stop, NULL);
+  job[0].dtime = DTIME(job[0].stop, job[0].start);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckDoneHost.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckDoneHost.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckDoneHost.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+int CheckDoneHost (Host *host) {
+  
+  int       status;
+
+  ASSERT (host, "host not set");
+
+  status = PclientCommand (host, "reset", PCLIENT_PROMPT, PCONTROL_RESP_CHECK_DONE_HOST);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      if (DEBUG || 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);
+      /* DONE host does not have an incomplete job */
+      // XXX do we need to close the connection?
+
+    case PCLIENT_GOOD:
+      if (VerboseMode()) gprint (GP_ERR, "checking done host %s\n", host[0].hostname);  
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+  ABORT ("should not reach here (CheckDoneHost)"); 
+}
+
+int CheckDoneHostResponse (Host *host) {
+
+  int status;
+  char *p;
+  IOBuffer *buffer;
+
+  /* job must have assigned host */
+  ASSERT (host, "missing host");
+  buffer = &host[0].comms_buffer;
+
+  /** successful command, examine result **/
+  p = memstr (buffer[0].buffer, "STATUS", buffer[0].Nbuffer);
+  if (p == NULL) {
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "missing STATUS in response; try again\n");
+      PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
+      return (FALSE);
+  }
+
+  sscanf (p, "%*s %d", &status);
+  switch (status) {
+    case -1:
+      ABORT ("reset syntax error");
+      
+    case 0:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "reset failed\n");
+      PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
+      return (FALSE);
+      
+    case 1:
+    case 2:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "successful reset\n");
+      PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+      return (FALSE);
+
+    default:
+      ABORT ("should not reach here (CheckDoneHost)");
+  }
+  ABORT ("should not reach here (CheckDoneHost)");
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckDoneJob.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckDoneJob.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckDoneJob.c	(revision 22322)
@@ -0,0 +1,35 @@
+# include "pcontrol.h"
+
+int CheckDoneJob (Job *job, Host *host) {
+  
+  int success;
+
+  ASSERT (job, "job not set");
+  ASSERT (host, "host not set");
+
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job == (Job *) host[0].job, "invalid job");
+
+  success = TRUE;
+  success &= GetJobOutput ("stdout", host, &job[0].stdout_buff, job[0].stdout_size);
+  success &= GetJobOutput ("stderr", host, &job[0].stderr_buff, job[0].stderr_size);
+
+  if (!success) {
+    // XXX some kind of error?
+    // XXX try again later?
+    PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+    PutJob (job, PCONTROL_JOB_DONE, STACK_BOTTOM);
+    return (FALSE);
+  }
+
+  /* job's state is either EXIT or CRASH (verify?) */
+  // unlink host & job
+  job[0].host = NULL;
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
+  PutJob (job, job[0].state, STACK_BOTTOM);
+
+  return (TRUE);
+}
+
+/** need to add timeout check here **/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckHost.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckHost.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckHost.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "pcontrol.h"
+
+// if the host has a job, we skip it (down or crash state will be caught elsewhere)
+// in fact, just touch the IDLE hosts, not the BUSY hosts?
+int CheckHost (Host *host) {
+  
+  int status;
+
+  ASSERT (host, "host not set");
+
+  if (host[0].job != NULL) return (TRUE);
+
+  /* if this host has been marked to be turned off, do that and return */
+  if (host[0].markoff) {
+    host[0].markoff = FALSE;
+    StopHost (host);
+    return (TRUE);
+  }
+
+  // the argument to echo (OK) is the expected response below in CheckHostResponse
+  status = PclientCommand (host, "echo OK", PCLIENT_PROMPT, PCONTROL_RESP_CHECK_HOST);
+
+  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:
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+  ABORT ("should not reach here (CheckHost)"); 
+}
+
+int CheckHostResponse (Host *host) {
+  
+  IOBuffer *buffer;
+
+  /* we only check IDLE hosts without jobs */
+  ASSERT (host, "missing host");
+  buffer = &host[0].comms_buffer;
+
+  // XXX check on the value of the response? (OK)
+
+  PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckIdleHost.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 22322)
@@ -0,0 +1,101 @@
+# include "pcontrol.h"
+
+/* the supplied host is not on a stack: it cannot be taken by the other thread */
+int CheckIdleHost (Host *host) {
+
+  int i;
+  Stack *stack;
+  Job *job;
+
+  ASSERT (host, "host not set");
+
+  /* if this host has been marked to be turned off, do that and return */
+  if (host[0].markoff) {
+    host[0].markoff = FALSE;
+    StopHost (host);
+    return (TRUE);
+  }
+    
+  /* search the JOB_PENDING stack for an appropriate job */
+  stack = GetJobStack (PCONTROL_JOB_PENDING);
+  LockStack (stack);
+  
+  /* look for first NEEDHOST matching this host */
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_NEEDHOST) continue;
+    ASSERT (job[0].hostname != NULL, "NEEDHOST hostname missing");
+    if (strcasecmp (job[0].hostname, host[0].hostname)) continue;
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+
+  /* no NEEDHOST entry, look for first WANTHOST matching this host */
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_WANTHOST) continue;
+    ASSERT (job[0].hostname != NULL, "WANTHOST hostname missing");
+    if (strcasecmp (job[0].hostname, host[0].hostname)) continue;
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+
+  /* no WANTHOST entry, look for first ANYHOST matching this host */
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_ANYHOST) continue;
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+
+  /* no ANYHOST entry, look for first WANTHOST with old time */
+  /* XXX perhaps I should add this to the conditions for ANYHOST instead of
+     running a separate loop?  ie, WANTHOST && time > X == ANYHOST */
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_WANTHOST) continue;
+    // XXX test the job age and skip if too young
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+  UnlockStack (stack);
+
+  /* no jobs for host, put it back on IDLE stack */
+  PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  return (TRUE);
+}
+
+/** note : host and job popped off IDLE and PENDING stacks, 
+    unless no job is available **/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckPoint.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckPoint.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckPoint.c	(revision 22322)
@@ -0,0 +1,69 @@
+# include "pcontrol.h"
+
+// one thread (user) interacts with the user and blocks for long periods on input.
+// one thread (client) spins continuously and monitors the hosts and jobs
+// in some cases, the user thread needs to set a check point on the client thread 
+// to ensure the all HOSTs and JOBs are on one of the stacks (nothing 'in flight').
+// these are not symmetric: the client thread should not call Set/Clear, the user 
+// thread should not call Test
+
+# ifdef THREADED
+static pthread_mutex_t client = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t user = PTHREAD_MUTEX_INITIALIZER;
+# endif
+
+// The user thread calls this to stop the client thread from shuffling the Host/Job stacks
+int SetCheckPoint () {
+
+# ifdef THREADED
+  int status;
+  int Nwait;
+
+  // set my lock
+  pthread_mutex_lock (&user);
+
+  // wait until client thread sets its lock
+  Nwait = 0;
+  while (1) {
+    status = pthread_mutex_trylock (&client);
+    if (status == EBUSY) {
+      // client has reached the check-point
+      return (TRUE);
+    }
+    pthread_mutex_unlock (&client);
+    usleep (10000); // wait for client thread to set lock
+    Nwait ++;
+  }
+  // put in a timeout?  (client thread not spinning...)
+  return (FALSE);
+# else
+  return (TRUE);
+# endif
+}
+
+// The user thread calls this to allow the client thread to continue
+int ClearCheckPoint () {
+  // clear my lock
+# ifdef THREADED
+  pthread_mutex_unlock (&user);
+# endif
+  return (TRUE);
+}
+
+// The client thread calls in the thread loop somewhere the stacks are stable
+// (ie, no jobs or hosts currently in flight)
+int TestCheckPoint () {
+
+# ifdef THREADED
+  // set my lock
+  pthread_mutex_lock (&client);
+  
+  // try the user-thread lock
+  pthread_mutex_lock (&user);
+  pthread_mutex_unlock (&user);
+
+  // clear my lock
+  pthread_mutex_unlock (&client);
+# endif
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckRespHost.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckRespHost.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckRespHost.c	(revision 22322)
@@ -0,0 +1,96 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+// this function operates on hosts waiting for a response. we simply check if the message is
+// complete, and if so, send it to the correct parsing function
+int CheckRespHost (Host *host) {
+  
+  int status;
+  Job  *job;
+
+  ASSERT (host, "host not set");
+  job = (Job *) host[0].job;
+
+  status = PclientResponse (host, host[0].response, &host[0].comms_buffer);
+
+  /* check on output from pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+
+      // not all hosts here have a job; if it does, return it to PENDING
+      if (job) {
+	// unlink host & job
+	if (DEBUG || VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+	job[0].host = NULL;
+	host[0].job = NULL;
+	PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+      }
+
+      // clear the response data
+      host[0].response_state = PCONTROL_RESP_NONE;
+      host[0].response = NULL;
+
+      // host has shutdown; harvest the defunct process
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      return (FALSE);
+
+    case PCLIENT_HUNG:
+      // not done yet; try again later
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      if (job) {
+	PutJob (job, PCONTROL_JOB_RESP, STACK_BOTTOM);
+      }
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "host %s is not responding\n", host[0].hostname);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      if (VerboseMode()) gprint (GP_ERR, "message received (CheckRespHost)\n");  
+      break;
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+
+  switch (host[0].response_state) {
+    case PCONTROL_RESP_START_JOB:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_START_JOB\n");
+      status = StartJobResponse (host);
+      break;
+
+    case PCONTROL_RESP_CHECK_HOST:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_CHECK_HOST\n");
+      status = CheckHostResponse (host);
+      break;
+
+    case PCONTROL_RESP_CHECK_DONE_HOST:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_CHECK_DONE_HOST\n");
+      status = CheckDoneHostResponse (host);
+      break;
+
+    case PCONTROL_RESP_CHECK_BUSY_JOB:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_BUSY_JOB\n");
+      status = CheckBusyJobResponse (host);
+      break;
+
+    case PCONTROL_RESP_KILL_JOB:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_KILL_JOB\n");
+      status = KillJobResponse (host);
+      break;
+
+    case PCONTROL_RESP_STOP_HOST:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_STOP_HOST\n");
+      status = StopHostResponse (host);
+      break;
+
+    default:
+      ABORT ("undefined response state");
+  }
+
+  // we have detected a valid response, clear the response data
+  host[0].response_state = PCONTROL_RESP_NONE;
+  host[0].response = NULL;
+  return (status);
+}      
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckSystem.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckSystem.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/CheckSystem.c	(revision 22322)
@@ -0,0 +1,437 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+static struct timeval lastlive = {0, 0};
+static RunLevels RunLevel = PCONTROL_RUN_NONE;
+
+RunLevels SetRunLevel (RunLevels level) {
+  RunLevels oldlevel;
+  oldlevel = RunLevel;
+  RunLevel = level;
+  return oldlevel;
+}
+
+RunLevels GetRunLevel () {
+  return RunLevel;
+}
+
+int CheckSystem () {
+
+  struct timeval now;
+  float dtime;
+
+  /* we want to give each block a maximum allowed time */
+  CheckIdleHosts(0.020); /* submit a new job */
+
+  CheckBusyJobs(0.020);  /* get job status */
+  CheckDoneJobs(0.020);  /* harvest job stdout/stderr */
+  CheckKillJobs(0.020);  /* harvest job stdout/stderr */
+
+  CheckDoneHosts(0.020); /* reset the host */
+  CheckDownHosts(0.100); /* launch the host */
+
+  /* always allow at least one test */
+  /* most tests require about 2ms per host.  
+     CheckDoneJobs must depend on the size of the output buffer */
+
+  gettimeofday (&now, (void *) NULL);
+  dtime = DTIME (now, lastlive);
+  if (dtime > 1.0) {
+    CheckLiveHosts(0.040);
+    lastlive = now;
+  } 
+
+  if (DEBUG) { 
+    Stack *stack;
+    int Nidle, Ndown, Nbusy;
+    stack = GetHostStack (PCONTROL_HOST_IDLE);
+    Nidle = stack[0].Nobject;
+    stack = GetHostStack (PCONTROL_HOST_DOWN);
+    Ndown = stack[0].Nobject;
+    stack = GetHostStack (PCONTROL_HOST_BUSY);
+    Nbusy = stack[0].Nobject;
+    gprint (GP_ERR, "busy, idle, down: %2d %2d %2d\n", Nbusy, Nidle, Ndown);
+  }
+
+  return (TRUE);
+}
+
+void *CheckSystem_Threaded (void *data) {
+
+  int Njobchecks, Nhostchecks, Nlivechecks, Ndonejobs;
+
+  Nlivechecks = 0;
+
+  gprintInit ();
+
+  while (1) {
+    // stop here if the user-thread requests (no objects in flight) 
+    TestCheckPoint ();
+
+    // don't run the system checks if RunLevel is FALSE
+    // XXX stop should not suspend all checks: we should continue
+    // to harvest completed jobs and migrate idle machines to down
+    if (RunLevel == PCONTROL_RUN_NONE) {
+      usleep (100000); // idle if we are running nothing
+      continue;
+    }
+
+    /* always allow at least one test */
+    /* most tests require about 2ms per host.  
+       CheckDoneJobs must depend on the size of the output buffer */
+    /* the max delay times are fairly arbitrary and do not impact
+       the user interface.
+     */
+
+    Njobchecks = 0;
+    Nhostchecks = 0;
+    Ndonejobs = 0;
+
+    if ((RunLevel == PCONTROL_RUN_ALL) || (RunLevel == PCONTROL_RUN_REAP)) {
+      Njobchecks  += CheckBusyJobs(0.020);  /* get job status (PCLIENT) */
+      TestCheckPoint ();
+      Ndonejobs    = CheckDoneJobs(0.020);  /* harvest job stdout/stderr (!PCLIENT) */
+      Njobchecks  += Ndonejobs;
+      TestCheckPoint ();
+      Njobchecks  += CheckKillJobs(0.020);  /* harvest job stdout/stderr (PCLIENT) */
+      TestCheckPoint ();
+    }
+
+    if (RunLevel != PCONTROL_RUN_NONE) {
+      Nhostchecks += CheckRespHosts(0.020); /* check for incoming messages */
+      TestCheckPoint ();
+      Nhostchecks += CheckDoneHosts(0.020); /* reset the host */
+      TestCheckPoint ();
+      Nhostchecks += CheckDownHosts(0.100); /* launch the host */
+      TestCheckPoint ();
+    }
+
+    if (RunLevel == PCONTROL_RUN_ALL) {
+      // we want to give each block a maximum allowed time
+      Nhostchecks += CheckIdleHosts(0.020); /* submit a new job (PCLIENT) */
+      TestCheckPoint ();
+    }
+
+    // there is nothing on the stacks.  test the hosts and wait a bit
+    if (!Njobchecks && !Nhostchecks && (RunLevel != PCONTROL_RUN_NONE)) {
+      CheckLiveHosts(0.040);
+      usleep (100000); // idle if no jobs are waiting
+    } else {
+      // if we only have busy jobs, pause a moment before trying again
+      if (!Ndonejobs) {
+	usleep (100000);
+      }
+    }
+
+    if (DEBUG) { 
+      Stack *stack;
+      int Nidle, Ndown, Nbusy;
+      stack = GetHostStack (PCONTROL_HOST_IDLE);
+      Nidle = stack[0].Nobject;
+      stack = GetHostStack (PCONTROL_HOST_DOWN);
+      Ndown = stack[0].Nobject;
+      stack = GetHostStack (PCONTROL_HOST_BUSY);
+      Nbusy = stack[0].Nobject;
+      gprint (GP_ERR, "Njobchecks: %d, busy, idle, down: %2d %2d %2d\n", Njobchecks, Nbusy, Nidle, Ndown);
+    }
+  }
+  return (NULL);
+}
+
+int CheckBusyJobs (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Job   *job;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once.  Note that it is not important if the
+     stack size is modified by other threads or is changed by any of the actions performed during
+     this loop: the Nobject value is only used to get a rough number for the number of iterations.
+   */
+
+  hoststack = GetHostStack (PCONTROL_HOST_BUSY);
+  jobstack  = GetJobStack (PCONTROL_JOB_BUSY);
+  Nobject   = jobstack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    // pull both job and host from their stacks
+    LockStack (hoststack);
+    job = PullStackByLocation (jobstack, STACK_TOP);
+    if (job == NULL) {
+      UnlockStack (hoststack);
+      break;
+    }
+    host = (Host *) job[0].host;
+    ASSERT (host != NULL, "host is NULL");
+    RemoveStackByID (hoststack, host[0].HostID);
+    UnlockStack (hoststack);
+
+    CheckBusyJob (job, host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+  return (i);
+}
+
+int CheckDoneJobs (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Job   *job;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  hoststack = GetHostStack (PCONTROL_HOST_BUSY);
+  jobstack  = GetJobStack (PCONTROL_JOB_DONE);
+  Nobject   = jobstack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    LockStack (hoststack);
+    job = PullStackByLocation (jobstack, STACK_TOP);
+    if (job == NULL) {
+      UnlockStack (hoststack);
+      break;
+    }
+    host = (Host *) job[0].host;
+    ASSERT (host, "host is NULL");
+
+    RemoveStackByID (hoststack, host[0].HostID);
+    UnlockStack (hoststack);
+
+    CheckDoneJob (job, host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+  return (i);
+}
+
+int CheckKillJobs (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Job   *job;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  hoststack = GetHostStack (PCONTROL_HOST_BUSY);
+  jobstack = GetJobStack (PCONTROL_JOB_KILL);
+  Nobject = jobstack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    LockStack (hoststack);
+    job = PullStackByLocation (jobstack, STACK_TOP);
+    if (job == NULL) {
+      UnlockStack (hoststack);
+      break;
+    }
+    host = (Host *) job[0].host;
+    ASSERT (host, "host is NULL");
+
+    RemoveStackByID (hoststack, host[0].HostID);
+    UnlockStack (hoststack);
+
+    KillJob (job, host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+  return (i);
+}
+
+int CheckRespHosts (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Host *host;
+  Job *job;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  hoststack = GetHostStack (PCONTROL_HOST_RESP);
+  jobstack = GetJobStack (PCONTROL_JOB_RESP);
+  Nobject = hoststack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    LockStack (jobstack);
+    host = PullStackByLocation (hoststack, STACK_TOP);
+    if (host == NULL) {
+	UnlockStack (jobstack);
+	break;
+    }
+
+    // if the host has a job, we need to pull the job from its stack
+    job = (Job *) host[0].job;
+    if (job != NULL) {
+	RemoveStackByID (jobstack, job[0].JobID);
+    }
+    UnlockStack (jobstack);
+
+    CheckRespHost (host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+int CheckDoneHosts (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_DONE);
+  Nobject = stack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    CheckDoneHost (host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+int CheckDownHosts (float MaxDelay) {
+
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  struct timeval start, stop;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_DOWN);
+  Nobject = stack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    if (host[0].markoff) {
+      // DOWN -> OFF
+      host[0].markoff = FALSE;
+      OffHost (host);
+      return (TRUE);
+    }
+    dtime = DTIME (host[0].nexttry, start);
+    if (dtime > 0) {
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+    } else {
+      // DOWN -> IDLE (maybe)
+      // this is a race condition with "host retry", but the only 
+      // consequence is that both StartHost and reset set the times to 0.0
+      StartHost (host);
+    }
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+int CheckIdleHosts (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  float dtime;
+
+  /* check if there are any pending jobs, otherwise skip step */
+  stack = GetJobStack (PCONTROL_JOB_PENDING);
+  if (!stack[0].Nobject) return (0);
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  Nobject = stack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    CheckIdleHost (host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+/* this is just a heartbeat check (only IDLE hosts) */
+int CheckLiveHosts (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  Nobject = stack[0].Nobject;
+
+  gettimeofday (&start, (void *) NULL);
+
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    CheckHost (host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d idle hosts\n", i);
+  return (TRUE);
+}
+
+/*
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  if (VerboseMode()) gprint (GP_ERR, "check 4: %f seconds\n", dtime);
+
+  gettimeofday (&start, (void *) NULL);
+*/
+
+/** All of the CheckFooBar entries cycle though their respective queues, popping from the top and
+    pushing to the bottom.  if we stop before the loop is done there is no tendancy for bias because
+    we continue where we left off next round **/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/GetJobOutput.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/GetJobOutput.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/GetJobOutput.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "pcontrol.h"
+# define PCLIENT_TIMEOUT 500
+
+/* we read Nbytes from the host, then watch for the prompt */ 
+int GetJobOutput (char *cmd, Host *host, IOBuffer *buffer, int Nbytes) {
+  
+  int i, status, Nstart;
+  char *line;
+  struct timespec request, remain;
+
+  ASSERT (cmd, "cmd missing");
+  ASSERT (host, "host missing");
+  ASSERT (buffer, "buffer missing");
+
+  /* flush any earlier messages */
+  ReadtoIOBuffer (buffer, host[0].stdout_fd);
+  FlushIOBuffer (buffer);
+  Nstart = buffer[0].Nbuffer;
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* send cmd (stdout / stderr) */
+  status = write_fmt (host[0].stdin_fd, "%s\n", cmd);
+
+  /* is pipe still open? */
+  if ((status == -1) && (errno == EPIPE)) return (PCLIENT_DOWN);
+
+  /* read at least Nbytes, then watch for PCLIENT_PROMPT */
+  line = NULL;
+  status = -1;
+  for (i = 0; (i < PCLIENT_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+    status = ReadtoIOBuffer (buffer, host[0].stdout_fd);
+    if ((buffer[0].Nbuffer - Nstart) >= Nbytes) {
+      line = memstr (buffer[0].buffer, PCLIENT_PROMPT, buffer[0].Nbuffer);
+    }
+    if (status == -1) nanosleep (&request, &remain);
+  }
+  if (status ==  0) return (PCLIENT_DOWN);
+  if (status == -1) return (PCLIENT_HUNG);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case -1:
+      if (VerboseMode()) gprint (GP_ERR, "host %s is not responding\n", host[0].hostname);
+      return (FALSE);
+
+    case 0:
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      return (FALSE);
+
+    default:
+      if (VerboseMode()) gprint (GP_ERR, "message received (GetJobOutput : %s)\n", cmd);  
+      /* drop extra bytes from pclient (not pclient:job) */
+      buffer[0].Nbuffer = Nstart + Nbytes;
+      if (buffer[0].Nalloc > buffer[0].Nbuffer) {
+	bzero (buffer[0].buffer + buffer[0].Nbuffer, buffer[0].Nalloc - buffer[0].Nbuffer);
+      }
+      return (TRUE);
+  }
+
+  gprint (GP_ERR, "programming error: should not reach here (GetJobOutput)\n");
+  pcontrol_exit (50);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/HostOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/HostOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/HostOps.c	(revision 22322)
@@ -0,0 +1,232 @@
+# include "pcontrol.h"
+
+Stack *HostPool_AllHosts;  // virtual pool for user status queries
+
+Stack *HostPool_Idle; // these hosts are waiting for something to do
+Stack *HostPool_Busy; // these hosts are working
+Stack *HostPool_Resp; // these hosts are trying to respond
+Stack *HostPool_Done; // these hosts have finished a job
+Stack *HostPool_Down; // these hosts are not responding
+Stack *HostPool_Off;  // these hosts are off
+
+void InitHostStacks () {
+  HostPool_AllHosts = InitStack ();
+
+  HostPool_Idle = InitStack ();
+  HostPool_Busy = InitStack ();
+  HostPool_Resp = InitStack ();
+  HostPool_Done = InitStack ();
+  HostPool_Down = InitStack ();
+  HostPool_Off  = InitStack ();
+}
+
+void FreeHostStack (Stack *stack) {
+  Host *host;
+  while ((host = PullStackByLocation (stack, stack[0].Nobject - 1)) != NULL) {
+    DelHost (host);
+  }
+  FreeStack (stack);
+}
+
+void FreeHostStacks () {
+  FreeHostStack (HostPool_Idle);
+  FreeHostStack (HostPool_Busy);
+  FreeHostStack (HostPool_Resp);
+  FreeHostStack (HostPool_Done);
+  FreeHostStack (HostPool_Down);
+  FreeHostStack (HostPool_Off );
+
+  // AllHosts is a virtual stack : all hosts are references
+  FreeStack (HostPool_AllHosts);
+}
+
+char *GetHostStackName (int StackID) {
+  switch (StackID) {
+    case PCONTROL_HOST_ALLHOSTS: return ("ALLHOSTS");
+    case PCONTROL_HOST_IDLE: return ("IDLE");
+    case PCONTROL_HOST_DOWN: return ("DOWN");
+    case PCONTROL_HOST_RESP: return ("RESP");
+    case PCONTROL_HOST_DONE: return ("DONE");
+    case PCONTROL_HOST_BUSY: return ("BUSY");
+    case PCONTROL_HOST_OFF:  return ("OFF");
+  }
+  gprint (GP_ERR, "error: unknown host stack : programming error\n");
+  pcontrol_exit (51);
+  return (NULL);
+}
+
+Stack *GetHostStack (int StackID) {
+  switch (StackID) {
+    case PCONTROL_HOST_ALLHOSTS: return (HostPool_AllHosts);
+    case PCONTROL_HOST_IDLE: return (HostPool_Idle);
+    case PCONTROL_HOST_DOWN: return (HostPool_Down);
+    case PCONTROL_HOST_RESP: return (HostPool_Resp);
+    case PCONTROL_HOST_DONE: return (HostPool_Done);
+    case PCONTROL_HOST_BUSY: return (HostPool_Busy);
+    case PCONTROL_HOST_OFF:  return (HostPool_Off);
+  }
+  gprint (GP_ERR, "error: unknown host stack : programming error\n");
+  pcontrol_exit (52);
+  return (NULL);
+}
+
+Stack *GetHostStackByName (char *name) {
+  if (!strcasecmp (name, "all")) return (HostPool_AllHosts);
+  if (!strcasecmp (name, "idle")) return (HostPool_Idle);
+  if (!strcasecmp (name, "down")) return (HostPool_Down);
+  if (!strcasecmp (name, "resp")) return (HostPool_Resp);
+  if (!strcasecmp (name, "done")) return (HostPool_Done);
+  if (!strcasecmp (name, "busy")) return (HostPool_Busy);
+  if (!strcasecmp (name, "off"))  return (HostPool_Off);
+  return (NULL);
+}
+
+/* add host to position in stack */
+int PutHost (Host *host, int StackID, int where) {
+
+  int stat;
+  Stack *stack;
+
+  // fprintf (stderr, "move host %s to %s\n", host[0].hostname, GetHostStackName(StackID));
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (FALSE);
+
+  host[0].stack = StackID;
+  stat = PushStack (stack, where, host, host[0].HostID, host[0].hostname);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
+  return (stat);
+}
+  
+/* find the host by ID in the defined host stacks */
+Host *PullHostByID (IDtype HostID, int *StackID) {
+
+  Host *host;
+
+  *StackID = PCONTROL_HOST_IDLE;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DOWN;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_RESP;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DONE;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_BUSY;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_OFF;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = -1;
+  return (NULL);
+}
+
+/* find the host by ID in the defined host stacks */
+Host *PullHostByName (char *name, int *StackID) {
+
+  Host *host;
+
+  *StackID = PCONTROL_HOST_IDLE;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DOWN;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_RESP;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DONE;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_BUSY;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_OFF;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = -1;
+  return (NULL);
+}
+
+Host *PullHostFromStackByID (int StackID, IDtype ID) {
+
+  Host *host;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  host = PullStackByID (stack, ID);
+  return (host);
+}
+
+Host *PullHostFromStackByName (int StackID, char *name) {
+
+  Host *host;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  host = PullStackByName (stack, name);
+  return (host);
+}
+
+IDtype AddHost (char *hostname, int max_threads) {
+
+  Host *host;
+
+  ALLOCATE (host, Host, 1);
+
+  host[0].hostname    = strcreate (hostname);
+  host[0].max_threads = max_threads;
+  host[0].stdin_fd    = 0;
+  host[0].stdout_fd   = 0;
+  host[0].stderr_fd   = 0;
+  host[0].HostID      = NextHostID();
+
+  host[0].lasttry.tv_sec  = 0;
+  host[0].lasttry.tv_usec = 0;
+  host[0].nexttry.tv_sec  = 0;
+  host[0].nexttry.tv_usec = 0;
+
+  InitIOBuffer (&host[0].comms_buffer, 0x100);
+  host[0].response_state = PCONTROL_RESP_NONE;
+  host[0].response = NULL;
+
+  host[0].markoff  = FALSE;
+  host[0].job      = NULL;
+
+  PutHost (host, PCONTROL_HOST_ALLHOSTS, STACK_BOTTOM);
+  PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+  return (host[0].HostID);
+}
+
+void DelHost (Host *host) {
+
+  Host *copy;
+
+  copy = PullStackByID (HostPool_AllHosts, host[0].HostID);
+  ASSERT (copy == host, "programming error: ALLHOSTS entry does not match");
+
+  FreeIOBuffer (&host[0].comms_buffer);
+  FREE (host[0].hostname);
+  FREE (host[0].job);
+  FREE (host);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/IDops.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/IDops.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/IDops.c	(revision 22322)
@@ -0,0 +1,70 @@
+# include "pcontrol.h"
+
+static IDtype CurrentJobID  = 1;
+static IDtype CurrentHostID = 1;
+
+/* for now, no persistence between sessions : we could use the date/time to seed the upper
+ * byte(s) if needed */
+void InitIDs () {
+  CurrentJobID = 1;
+  CurrentHostID = 1;
+}
+
+IDtype NextJobID () {
+
+  IDtype ID;
+
+  ID = CurrentJobID;
+  CurrentJobID ++;
+  return (ID);
+}
+
+/* only used by the User thread */
+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 int word0, word1, word2, word3;
+  char *endptr;
+
+  Nargs = sscanf (IDword, "%x.%x.%x.%x", &word3, &word2, &word1, &word0);
+  if (Nargs == 4) {
+      IDtype tmp;
+    ID = 0;
+    ID |= (word0 << 0);
+    ID |= (word1 << 16);
+    tmp = word2;
+    ID |= (tmp << 32);
+    tmp = word3;
+    ID |= (tmp << 48);
+    return ID;
+  } 
+    
+  ID = strtoll (IDword, &endptr, 10);
+  if (*endptr == 0) {
+    return ID;
+  }
+
+  return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/JobID.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/JobID.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/JobID.c	(revision 22322)
@@ -0,0 +1,64 @@
+# 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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/JobOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/JobOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/JobOps.c	(revision 22322)
@@ -0,0 +1,239 @@
+# include "pcontrol.h"
+
+Stack *JobPool_AllJobs;  // virtual pool for user status queries
+
+Stack *JobPool_Pending;
+Stack *JobPool_Busy;
+Stack *JobPool_Resp;
+Stack *JobPool_Done;
+Stack *JobPool_Kill;
+Stack *JobPool_Exit;
+Stack *JobPool_Crash;
+
+void InitJobStacks () {
+  JobPool_AllJobs = InitStack ();
+
+  JobPool_Pending = InitStack ();
+  JobPool_Busy    = InitStack ();
+  JobPool_Resp    = InitStack ();
+  JobPool_Done    = InitStack ();
+  JobPool_Kill    = InitStack ();
+  JobPool_Exit    = InitStack ();
+  JobPool_Crash   = InitStack ();
+}
+
+void FreeJobStack (Stack *stack) {
+  Job *job;
+  while ((job = PullStackByLocation (stack, stack[0].Nobject - 1)) != NULL) {
+    DelJob (job);
+  }
+  FreeStack (stack);
+}
+
+void FreeJobStacks () {
+  FreeJobStack (JobPool_Pending);
+  FreeJobStack (JobPool_Busy   );
+  FreeJobStack (JobPool_Resp   );
+  FreeJobStack (JobPool_Done   );
+  FreeJobStack (JobPool_Kill   );
+  FreeJobStack (JobPool_Exit   );
+  FreeJobStack (JobPool_Crash  );
+
+  // AllJobs is a virtual stack : all jobs are references
+  FreeStack (JobPool_AllJobs);
+}
+
+char *GetJobStackName (int StackID) {
+  switch (StackID) {
+    case PCONTROL_JOB_ALLJOBS: return ("ALLJOBS");
+
+    case PCONTROL_JOB_PENDING: return ("PENDING");
+    case PCONTROL_JOB_BUSY:    return ("BUSY");
+    case PCONTROL_JOB_RESP:    return ("RESP");
+    case PCONTROL_JOB_DONE:    return ("DONE");
+    case PCONTROL_JOB_KILL:    return ("KILL");
+    case PCONTROL_JOB_EXIT:    return ("EXIT");
+    case PCONTROL_JOB_CRASH:   return ("CRASH");
+  }
+  gprint (GP_ERR, "error: unknown host stack : programming error\n");
+  pcontrol_exit (53);
+  return (NULL);
+}
+
+Stack *GetJobStack (int StackID) {
+  switch (StackID) {
+    case PCONTROL_JOB_ALLJOBS: return (JobPool_AllJobs);
+
+    case PCONTROL_JOB_PENDING: return (JobPool_Pending);
+    case PCONTROL_JOB_BUSY:    return (JobPool_Busy);
+    case PCONTROL_JOB_RESP:    return (JobPool_Resp);
+    case PCONTROL_JOB_DONE:    return (JobPool_Done);
+    case PCONTROL_JOB_KILL:    return (JobPool_Kill);
+    case PCONTROL_JOB_EXIT:    return (JobPool_Exit);
+    case PCONTROL_JOB_CRASH:   return (JobPool_Crash);
+  }
+  gprint (GP_ERR, "error: unknown job stack : programming error\n");
+  pcontrol_exit (54);
+  return (NULL);
+}
+
+Stack *GetJobStackByName (char *name) {
+
+  if (!strcasecmp (name, "all"))     return (JobPool_AllJobs);
+
+  if (!strcasecmp (name, "pending")) return (JobPool_Pending);
+  if (!strcasecmp (name, "busy"))    return (JobPool_Busy);
+  if (!strcasecmp (name, "resp"))    return (JobPool_Resp);
+  if (!strcasecmp (name, "done"))    return (JobPool_Done);
+  if (!strcasecmp (name, "exit"))    return (JobPool_Exit);
+  if (!strcasecmp (name, "crash"))   return (JobPool_Crash);
+  return (NULL);
+}
+
+/* add job to position in stack, use StackID as default state */
+int PutJob (Job *job, int StackID, int where) {
+
+  int stat;
+  Stack *stack;
+
+  // fprintf (stderr, "move job %s to %s\n", job[0].argv[0], GetJobStackName(StackID));
+
+  stack = GetJobStack (StackID);
+  if (stack == NULL) return (FALSE);
+
+  /* by default, these are both the same - to override, use PutJobSetState */
+  job[0].state = StackID;
+  job[0].stack = StackID;
+  stat = PushStack (stack, where, job, job[0].JobID, job[0].argv[0]);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
+  return (stat);
+}
+  
+/* add job to position in stack.  set state to 'state' */
+int PutJobSetState (Job *job, int StackID, int where, int state) {
+
+  int stat;
+  Stack *stack;
+
+  stack = GetJobStack (StackID);
+  if (stack == NULL) return (FALSE);
+
+  /* alternate state specified by user */
+  job[0].state = state;
+  job[0].stack = StackID;
+  stat = PushStack (stack, where, job, job[0].JobID, job[0].argv[0]);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
+  return (stat);
+}
+  
+Job *PullJobByID (IDtype JobID, int *StackID) {
+
+  Job *job;
+
+  *StackID = PCONTROL_JOB_PENDING;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_BUSY;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_RESP;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_DONE;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_KILL;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  return (NULL);
+}
+
+/* remove job from position in stack */
+Job *PullJobFromStackByID (int StackID, int ID) {
+
+  Job *job;
+  Stack *stack;
+
+  stack = GetJobStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  job = PullStackByID (stack, ID);
+  return (job);
+}
+
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int argc, char **argv) {
+
+  int JobID;
+  Job *job;
+
+  ALLOCATE (job, Job, 1);
+
+  job[0].argc     = argc;
+  job[0].argv     = argv;
+  job[0].hostname = hostname;
+  job[0].realhost = NULL;
+
+  job[0].exit_status = 0;
+  job[0].Reset    = FALSE;
+  job[0].stdout_size = 0;
+  job[0].stderr_size = 0;
+
+  job[0].mode     = mode;
+
+  job[0].state = 0;
+  job[0].stack = 0;
+
+  /* do this step on start? */
+  InitIOBuffer (&job[0].stdout_buff, 0x1000);
+  InitIOBuffer (&job[0].stderr_buff, 0x1000);
+
+  job[0].dtime = 0.0;
+  job[0].pid = 0;
+
+  job[0].JobID    = NextJobID();
+  job[0].host     = NULL;
+
+  JobID = job[0].JobID;
+
+  // Put a copy of all created jobs on the ALLJOBS stack
+  // This is a virtual stack: do not free the job from this stack
+  PutJob (job, PCONTROL_JOB_ALLJOBS, STACK_BOTTOM);
+  PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+
+  if (VerboseMode()) gprint (GP_ERR, "added new job\n");
+  return (JobID);
+}
+
+void DelJob (Job *job) {
+
+  int i;
+
+  Job *copy;
+
+  copy = PullStackByID (JobPool_AllJobs, job[0].JobID);
+  ASSERT (copy == job, "programming error: ALLJOBS entry does not match");
+
+  FREE (job[0].hostname);
+  for (i = 0; i < job[0].argc; i++) {
+    FREE (job[0].argv[i]);
+  }
+  FREE (job[0].argv);
+
+  FreeIOBuffer (&job[0].stdout_buff);
+  FreeIOBuffer (&job[0].stderr_buff);
+
+  FREE (job);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/KillJob.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/KillJob.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/KillJob.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "pcontrol.h"
+
+int KillJob (Job *job, Host *host) {
+  
+  int status;
+
+  ASSERT (host != NULL, "host missing");
+  ASSERT (job != NULL, "job missing");
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job  == (Job *) host[0].job, "invalid job");
+
+  status = PclientCommand (host, "reset", PCLIENT_PROMPT, PCONTROL_RESP_KILL_JOB);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      // unlink host & job
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      job[0].host = NULL;
+      host[0].job = NULL;
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_CRASH, STACK_BOTTOM);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      if (VerboseMode()) gprint (GP_ERR, "kill job on host %s\n", host[0].hostname);  
+      FlushIOBuffer (&host[0].comms_buffer);
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+}
+
+int KillJobResponse (Host *host) {
+  
+  int status;
+  char *p;
+  IOBuffer *buffer;
+  Job *job;
+
+  ASSERT (host != NULL, "host missing");
+  ASSERT (host[0].job, "missing job");
+  buffer = &host[0].comms_buffer;
+  job = (Job *) host[0].job;
+
+  /** check on response to pclient command **/
+  p = memstr (buffer[0].buffer, "STATUS", buffer[0].Nbuffer);
+  if (p == NULL) {
+      if (VerboseMode()) gprint (GP_ERR, "missing STATUS in response; try again\n");
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_KILL, STACK_BOTTOM);
+      return (FALSE);
+  }
+  if (VerboseMode()) gprint (GP_ERR, "client message: %s\n", buffer[0].buffer);
+
+  sscanf (p, "%*s %d", &status);
+  gprint (GP_ERR, "client status: %d\n", status);
+
+  switch (status) {
+    case -1:
+      ABORT ("syntax error to pclient");
+    case 0:
+      gprint (GP_ERR, "failure to kill child process\n");
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_KILL, STACK_BOTTOM);
+      return (FALSE);
+    case 1:
+      gprint (GP_ERR, "killed job %s on %s\n", job[0].argv[0], host[0].hostname);
+      // unlink host & job
+      job[0].host = NULL;
+      host[0].job = NULL;
+      PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_CRASH, STACK_BOTTOM);
+      return (TRUE);
+    case 2:
+      ABORT ("client has no job");
+  }
+  ABORT ("should not reach here (KillJob)");
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/Makefile	(revision 22322)
@@ -0,0 +1,76 @@
+default: pcontrol
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/pcontrol
+DATA    =       $(DESTDATA)/pcontrol
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1         = -lkapa -lFITS -lohana
+LIBS2         = -lbasiccmd -lshell -ldata 
+FULL_CFLAGS   = $(BASE_CFLAGS) -Wall -Werror
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# to build the non-threaded version, remove -lpthread and comment out
+# the THREADED line in include/pcontrol.h
+
+# pcontrol user commands and support functions ########################
+funcs = \
+$(SRC)/init.$(ARCH).o \
+$(SRC)/pcontrol.$(ARCH).o \
+$(SRC)/rconnect.$(ARCH).o \
+$(SRC)/CheckBusyJob.$(ARCH).o \
+$(SRC)/CheckDoneHost.$(ARCH).o \
+$(SRC)/CheckRespHost.$(ARCH).o \
+$(SRC)/CheckDoneJob.$(ARCH).o \
+$(SRC)/CheckHost.$(ARCH).o \
+$(SRC)/CheckIdleHost.$(ARCH).o \
+$(SRC)/CheckPoint.$(ARCH).o \
+$(SRC)/CheckSystem.$(ARCH).o \
+$(SRC)/GetJobOutput.$(ARCH).o \
+$(SRC)/HostOps.$(ARCH).o \
+$(SRC)/IDops.$(ARCH).o \
+$(SRC)/JobOps.$(ARCH).o \
+$(SRC)/StackOps.$(ARCH).o \
+$(SRC)/PclientCommand.$(ARCH).o \
+$(SRC)/StartHost.$(ARCH).o \
+$(SRC)/StopHosts.$(ARCH).o \
+$(SRC)/KillJob.$(ARCH).o \
+$(SRC)/StartJob.$(ARCH).o
+
+cmds = \
+$(SRC)/check.$(ARCH).o \
+$(SRC)/delete.$(ARCH).o \
+$(SRC)/host.$(ARCH).o \
+$(SRC)/job.$(ARCH).o \
+$(SRC)/jobstack.$(ARCH).o \
+$(SRC)/hoststack.$(ARCH).o \
+$(SRC)/kill.$(ARCH).o \
+$(SRC)/pulse.$(ARCH).o \
+$(SRC)/run.$(ARCH).o \
+$(SRC)/status.$(ARCH).o \
+$(SRC)/stdout.$(ARCH).o \
+$(SRC)/version.$(ARCH).o \
+$(SRC)/verbose.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a
+
+pcontrol: $(BIN)/pcontrol.$(ARCH)
+$(SRC)/pcontrol.$(ARCH).o : $(libs)
+$(BIN)/pcontrol.$(ARCH)   : $(cmds) $(funcs)
+
+$(cmds) $(funcs) : $(INC)/pcontrol.h
+
+install: $(DESTBIN)/pcontrol help
+
+help: clean-help cmd.basic.help pcontrol.help
+
+.PHONY: pcontrol
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/PclientCommand.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/PclientCommand.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/PclientCommand.c	(revision 22322)
@@ -0,0 +1,82 @@
+# include "pcontrol.h"
+# define PCLIENT_TIMEOUT 100
+
+// send a command and check for errors; ignore output
+int PclientCommand (Host *host, char *command, char *response, HostResp response_state) {
+
+  int status;
+
+  ASSERT (host != NULL, "host missing");
+  ASSERT (command != NULL, "command missing");
+
+  // flush the stdout and stderr buffers here
+  // recycle comms_buffer to minimize page thrashing
+  ReadtoIOBuffer (&host[0].comms_buffer, host[0].stdout_fd);
+  FlushIOBuffer (&host[0].comms_buffer);
+  ReadtoIOBuffer (&host[0].comms_buffer, host[0].stderr_fd);
+  FlushIOBuffer (&host[0].comms_buffer);
+
+  /* send command to client (adding on \n) */
+  status = write_fmt (host[0].stdin_fd, "%s\n", command);
+
+  /* is pipe still open? */
+  if ((status == -1) && (errno == EPIPE)) {
+    gprint (GP_ERR, "pclient read gives pipe error for %s\n", command);
+    return (PCLIENT_DOWN);
+  }
+  
+  // prepare host to accept response
+  host[0].response_state = response_state;
+  host[0].response = response;
+  FlushIOBuffer (&host[0].comms_buffer);
+
+  // fprintf (stderr, "command: %s\n", command);
+
+  return (PCLIENT_GOOD);
+}
+  
+// check for response; message must end with specified string.
+// accumulate the response in the buffer
+int PclientResponse (Host *host, char *response, IOBuffer *buffer) {
+
+  int i;
+  int status;
+  char *line;
+  struct timespec request, remain;
+
+  ASSERT (response != NULL, "response missing");
+  ASSERT (buffer != NULL, "buffer missing");
+
+  /* avoid blocking very long on read, test every 100 usec, up to 0.1 sec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* watch for response - wait up to 1 second */
+  line = NULL;
+  status = -1;
+
+  // how long does each cycle really take?
+  for (i = 0; (i < PCLIENT_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+    status = ReadtoIOBuffer (buffer, host[0].stdout_fd);
+    line = memstr (buffer[0].buffer, response, buffer[0].Nbuffer);
+    if (status == -1) nanosleep (&request, &remain);
+  }
+  if (status ==  0) {
+    gprint (GP_ERR, "pclient read returns 0 for %s\n", command);
+    return (PCLIENT_DOWN);
+  }
+  if (line == NULL) return (PCLIENT_HUNG);
+  if (status == -1) return (PCLIENT_HUNG);
+
+  // fprintf (stderr, "response: %s\n", buffer[0].buffer);
+
+  return (PCLIENT_GOOD);
+}
+
+/* memstr returns a view, not an allocated string : don't free */
+/* ReadtoIOBuffer returns : 
+    0 - pipe closed
+   -1 - no more data in pipe, data not ready
+   -2 - serious error reading from pipe
+   >0 - data read from pipe
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/QueueOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/QueueOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/QueueOps.c	(revision 22322)
@@ -0,0 +1,67 @@
+# 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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/ResetJob.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/ResetJob.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/ResetJob.c	(revision 22322)
@@ -0,0 +1,61 @@
+# 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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StackOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StackOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StackOps.c	(revision 22322)
@@ -0,0 +1,224 @@
+# include "pcontrol.h"
+
+/* these stacks are not super efficient, and should probably be replaced with linked lists, 
+   but I find these easier to get my brain around.
+*/
+
+/* Stacks and thread locks: interacting with the Stacks needs to be thread-safe so that the user may
+ * perform operations which interact with the stacks at the same time that the background loops
+ * check the current status of the jobs and hosts in the different stacks.  The simplest way in
+ * which the stacks are made thread safe is to lock them with a mutex before every interaction
+ */
+
+# define DEBUG 0
+
+void PrintStackInfo (Stack *stack, const char *func) {
+
+  if (!DEBUG) return;
+  fprintf (stderr, "%s: %p  ", func, stack);
+  fprintf (stderr, "objects: %p  ", stack[0].object);
+  fprintf (stderr, "Nobjects: %d, NOBJECTS: %d\n", stack[0].Nobject, stack[0].NOBJECT);
+}
+
+/* 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);
+  ALLOCATE (stack[0].name,   char *, stack[0].NOBJECT);
+  ALLOCATE (stack[0].id,     int,    stack[0].NOBJECT);
+
+# ifdef THREADED
+  pthread_mutex_init (&stack[0].mutex, NULL);
+# endif
+  return (stack);
+}
+
+void FreeStack (Stack *stack) {
+
+  free (stack[0].object);
+  free (stack[0].name);
+  free (stack[0].id);
+  free (stack);
+}
+
+/* STACK_TOP == 0, STACK_BOTTOM == -1 */
+/* this code correctly handles the negative 'where' and Nobject == 0 */
+
+/* push object on stack at given location */
+int PushStack (Stack *stack, int where, void *object, int id, char *name) {
+
+  int i;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  if (where < 0) where += stack[0].Nobject + 1;
+  if (where < 0) {
+    UnlockStack (stack);
+    return (FALSE);
+  }
+  if (where > stack[0].Nobject) {
+    UnlockStack (stack);
+    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);
+    REALLOCATE (stack[0].name,   char *, stack[0].NOBJECT);
+    REALLOCATE (stack[0].id,     int, stack[0].NOBJECT);
+  }
+
+  for (i = stack[0].Nobject; i > where; i--) {
+    stack[0].object[i] = stack[0].object[i-1];
+    stack[0].name[i]   = stack[0].name[i-1];
+    stack[0].id[i]     = stack[0].id[i-1];
+  }
+  stack[0].object[where] = object;
+  stack[0].name[where]   = name;
+  stack[0].id[where]     = id;
+  stack[0].Nobject ++;
+
+  UnlockStack (stack);
+  return (TRUE);
+}
+
+/* get object from specified point in stack (negative == distance from end) */
+void *PullStackByLocation (Stack *stack, int where) {
+
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  if (where < 0) where += stack[0].Nobject;
+  if (where < 0) { 
+    UnlockStack (stack); 
+    return (NULL); 
+  }
+  if (where >= stack[0].Nobject) {
+    UnlockStack (stack); 
+    return (NULL);
+  }
+
+  object = stack[0].object[where];
+  RemoveStackEntry (stack, where);
+  UnlockStack (stack); 
+  return (object);
+}
+
+/* get object from stack which matches name */
+void *PullStackByName (Stack *stack, char *name) {
+
+  int i;
+  void *object;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (strcasecmp (stack[0].name[i], name)) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* get object from point in stack (negative == distance from end) */
+void *PullStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* should only be called if you know where is a valid entry */
+int RemoveStackEntry (Stack *stack, int where) {
+
+  int i;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+
+  if (where < 0) abort();
+  if (where >= stack[0].Nobject) abort();
+  if (stack[0].Nobject < 1) abort();
+
+  /* shift the remaining entries by one */
+  /* XXX free associated memory */
+  stack[0].Nobject --;
+  for (i = where; i < stack[0].Nobject; i++) {
+    stack[0].object[i] = stack[0].object[i+1];
+    stack[0].name[i]   = stack[0].name[i+1];
+    stack[0].id[i]     = stack[0].id[i+1];
+  }
+  return (TRUE);
+}
+
+/* should only be called if you manually lock the stack */
+void *RemoveStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    return (object);
+  }
+  return (NULL);
+}
+
+void LockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_lock (&stack[0].mutex);
+# endif
+  return;
+}
+
+void UnlockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_unlock (&stack[0].mutex);
+# endif
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StartHost.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StartHost.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StartHost.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "pcontrol.h"
+# define RETRY_BASE 10.0
+
+int StartHost (Host *host) {
+
+  int pid;
+  int stdio[3];
+  char command[64], shell[64];
+  struct timeval now;
+  float delta;
+
+  /* perhaps change the name of these config variables... */
+  if (VarConfig ("COMMAND", "%s", command) == NULL) strcpy (command, "ssh");
+  if (VarConfig ("SHELL", "%s", shell)     == NULL) strcpy (shell, "pclient");
+
+  if (VerboseMode()) gprint (GP_ERR, "starting host within thread %d\n", pthread_self());
+
+  pid = rconnect (command, host[0].hostname, shell, stdio);
+  if (!pid) {     
+    /** failure to start: extend retry period **/
+    if (VerboseMode()) gprint (GP_ERR, "failure to start %s\n", host[0].hostname);
+    gettimeofday (&now, (void *) NULL);
+    if (ZTIME(host[0].nexttry) || ZTIME(host[0].lasttry)) {
+      /* reset retry period if either is zero */
+      delta = RETRY_BASE;
+    } else {
+      delta = 2*DTIME (host[0].nexttry, host[0].lasttry);
+    }
+    host[0].nexttry.tv_sec  = now.tv_sec  + delta;
+    host[0].nexttry.tv_usec = now.tv_usec;
+    host[0].lasttry.tv_sec  = now.tv_sec;
+    host[0].lasttry.tv_usec = now.tv_usec;
+    PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+    return (FALSE);
+  }
+  host[0].nexttry.tv_sec  = 0;
+  host[0].nexttry.tv_usec = 0;
+  host[0].lasttry.tv_sec  = 0;
+  host[0].lasttry.tv_usec = 0;
+
+  host[0].stdin_fd  = stdio[0];
+  host[0].stdout_fd = stdio[1];
+  host[0].stderr_fd = stdio[2];
+  host[0].pid       = pid;
+  PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StartJob.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StartJob.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StartJob.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "pcontrol.h"
+
+// job and host are bound together (why pass in both?)
+int StartJob (Job *job, Host *host) {
+
+  int  i, Nline, status;
+  char *line;
+
+  /* job must have assigned host */
+  ASSERT (job != NULL, "missing job");
+  ASSERT (host != NULL, "missing host");
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job  == (Job *) host[0].job, "invalid job");
+
+  /* construct command line : job arg0 arg1 ... argN\n */
+  // arguments of the form @MAX_THREADS@ are replaced here
+  Nline = 10 + job[0].argc;
+  for (i = 0; i < job[0].argc; i++) {
+    Nline += strlen (job[0].argv[i]);
+  }
+  ALLOCATE (line, char, Nline);
+  bzero (line, Nline);
+  strcpy (line, "job");
+  for (i = 0; i < job[0].argc; i++) {
+    strcat (line, " ");
+    if (!strcmp (job[0].argv[i], "@MAX_THREADS@")) {
+      char threads[10];
+      snprintf (threads, 10, "%5d", host[0].max_threads);
+      strcat (line, threads);
+      continue;
+    } 
+    strcat (line, job[0].argv[i]);
+  }
+
+  fprintf (stderr, "command: %s\n", line);
+
+  status = PclientCommand (host, line, PCLIENT_PROMPT, PCONTROL_RESP_START_JOB);
+  free (line);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      // unlink host & job
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      job[0].host = NULL;
+      host[0].job = NULL;
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      job[0].realhost = strcreate (host[0].hostname);
+      job[0].pid = -1;
+      gettimeofday (&job[0].start, (void *) NULL);
+
+      if (VerboseMode()) gprint (GP_ERR, "started job on host %s\n", host[0].hostname);  
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+}
+
+// message has been received from the host, interpret results
+int StartJobResponse (Host *host) {
+  
+  int status;
+  char *p;
+  IOBuffer *buffer;
+  Job *job;
+
+  /* job must have assigned host */
+  ASSERT (host, "missing host");
+  ASSERT (host[0].job, "missing job");
+  buffer = &host[0].comms_buffer;
+  job = (Job *) host[0].job;
+
+  /* check on result of pclient command */
+  p = memstr (buffer[0].buffer, "STATUS", buffer[0].Nbuffer);
+  if (p == NULL) {
+      // failed to get a valid response.  kill the job and try again, 
+      // or accept a running process without a PID?
+      if (VerboseMode()) gprint (GP_ERR, "failed to get a valid PID, trying to continue without\n");
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+      gettimeofday (&job[0].start, NULL);
+      return (TRUE);
+  }
+
+  sscanf (p, "%*s %d", &status);
+  switch (status) {
+    case -1:
+      if (VerboseMode()) gprint (GP_ERR, "error in pclient child\n");
+      // unlink host & job
+      job[0].host = NULL;
+      host[0].job = NULL;
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+      return (FALSE);
+
+    case -2:
+      ABORT ("syntax error in pclient command");
+
+    case -3:
+      ABORT ("existing child on pclient");
+
+    default:
+      if (VerboseMode()) gprint (GP_ERR, "message received (StartJobResponse)\n");  
+      job[0].pid = status;
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+      gettimeofday (&job[0].start, NULL);
+      return (TRUE);
+  }
+
+  /* we should never reach here */
+  ABORT ("should not reach here (StartJob)");
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StopHosts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StopHosts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/StopHosts.c	(revision 22322)
@@ -0,0 +1,159 @@
+# include "pcontrol.h"
+
+void DownHost (Host *host) {
+  CLOSE (host[0].stdin_fd);
+  CLOSE (host[0].stdout_fd);
+  CLOSE (host[0].stderr_fd);
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+}
+
+void OffHost (Host *host) {
+  CLOSE (host[0].stdin_fd);
+  CLOSE (host[0].stdout_fd);
+  CLOSE (host[0].stderr_fd);
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_OFF, STACK_BOTTOM);
+}
+
+/* for use by shutdown: force machines which are up to go down
+   wait for a little while for the client thread to take care 
+   of them
+*/
+   
+int DownHosts () {
+
+  int i, Nobject, Nwait;
+  Stack *stack;
+  Host  *host;
+
+  SetCheckPoint (); // ensure we can find the specified host
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  ASSERT (stack != NULL, "stack missing");
+  Nobject = stack[0].Nobject;
+  for (i = 0; i < Nobject; i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) continue;
+    host[0].markoff = TRUE;
+    PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  }
+
+  stack = GetHostStack (PCONTROL_HOST_BUSY);
+  ASSERT (stack != NULL, "stack missing");
+  Nobject = stack[0].Nobject;
+  for (i = 0; i < Nobject; i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) continue;
+    host[0].markoff = TRUE;
+    PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  }
+  ClearCheckPoint ();
+
+  Nwait = 0;
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  ASSERT (stack != NULL, "stack missing");
+
+  gprint (GP_ERR, "waiting for clients to exit");
+  while ((Nwait < 15) && stack[0].Nobject) {
+    gprint (GP_ERR, ".");
+    usleep (100000); // wait for clients to exit
+    Nwait++;
+  }
+  gprint (GP_ERR, "\n");
+  if (stack[0].Nobject) {
+    gprint (GP_ERR, "trouble shutting down all pclient instances: %d still alive\n", stack[0].Nobject);
+  } else {
+    gprint (GP_ERR, "done\n");
+  }
+  return (TRUE);
+}
+
+int StopHost (Host *host) {
+
+  int       status;
+
+  status = PclientCommand (host, "exit", "Goodbye", PCONTROL_RESP_STOP_HOST);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      // XXX this is the desired result in any case, so ignore it
+      break;
+
+    case PCLIENT_GOOD:
+      if (VerboseMode()) gprint (GP_ERR, "stop host %s\n", host[0].hostname);  
+      FlushIOBuffer (&host[0].comms_buffer);
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+  ABORT ("should not reach here");  
+}
+
+int StopHostResponse (Host *host) {
+
+  OffHost (host);
+  HarvestHost (host[0].pid);
+  return (TRUE);
+}
+
+/* the host is thought to be down; check for child exit status */
+int HarvestHost (int pid) {
+  
+  int i, result, waitstatus;
+
+  if (VerboseMode()) gprint (GP_ERR, "harvesting within thread %p\n", pthread_self());
+  if (VerboseMode()) gprint (GP_ERR, "child process %d is down, wait for exit status\n", pid);
+  
+  // Loop a few times waiting for child to exit
+  for (i = 0; i < 50; i++) {
+    result = waitpid (pid, &waitstatus, WNOHANG);
+    if ((result == -1) && (errno == ECHILD)) {
+      usleep (10000); // wait for child to exit
+      continue;
+    } else {
+      break;
+    }
+  }
+  switch (result) {
+    case -1:  /* error with waitpid */
+      switch (errno) {
+	case ECHILD:
+	  gprint (GP_ERR, "unknown PID, not a child proc\n");
+	  gprint (GP_ERR, "did process already exit?  programming error?\n");
+	  break;
+	case EINTR:
+	case EINVAL:
+	default:
+	  perror ("unexpected error");
+	  ABORT ("(HarvestHost)");
+      }
+      break;
+      
+    case 0:
+      gprint (GP_ERR, "child did not exit??");
+      abort ();
+      /** put back in IDLE state? **/
+      break;
+
+    default:
+      if (result != pid) {
+	gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+	pcontrol_exit (58);
+      }
+      
+      if (WIFEXITED(waitstatus)) {
+	if (VerboseMode()) gprint (GP_ERR, "child exited with status %d\n", WEXITSTATUS(waitstatus));
+      }
+      if (WIFSIGNALED(waitstatus)) {
+	if (VerboseMode()) gprint (GP_ERR, "child crashed with status %d\n", WTERMSIG(waitstatus));
+      }
+      if (WIFSTOPPED(waitstatus)) {
+        gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+	pcontrol_exit (59);
+      }
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/check.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/check.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/check.c	(revision 22322)
@@ -0,0 +1,61 @@
+# include "pcontrol.h"
+
+int check (int argc, char **argv) {
+
+  int JobID, HostID;
+
+  Stack *stack = NULL;
+  Job *job = NULL;
+  Host *host = NULL;
+
+  if (argc != 3) {
+    gprint (GP_LOG, "USAGE: check job (JobID)\n");
+    gprint (GP_LOG, "USAGE: check host (HostID)\n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "JOB")) {
+    JobID = GetID (argv[2]);
+    if (!JobID) {
+      gprint (GP_ERR, "invalid job id %s\n", argv[2]);
+      return (FALSE);
+    }
+
+    stack = GetJobStack (PCONTROL_JOB_ALLJOBS);
+    job = PullStackByID (stack, JobID);
+    if (job == NULL) {
+      gprint (GP_LOG, "job not found\n");
+      return (FALSE);
+    }
+
+    gprint (GP_LOG, "STATUS %s\n", GetJobStackName(job[0].stack));
+    gprint (GP_LOG, "EXITST %d\n", job[0].exit_status);
+    gprint (GP_LOG, "STDOUT %d\n", job[0].stdout_size);
+    gprint (GP_LOG, "STDERR %d\n", job[0].stderr_size);
+    gprint (GP_LOG, "DTIME %lf\n", job[0].dtime);
+    if (job[0].realhost) {
+	gprint (GP_LOG, "HOSTNAME %s\n", job[0].realhost);
+    } else {
+	gprint (GP_LOG, "HOSTNAME NONE\n");
+    }
+    PushStack (stack, STACK_BOTTOM, job, job[0].JobID, job[0].argv[0]);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "HOST")) {
+    HostID = atoi (argv[2]);
+
+    stack = GetHostStack (PCONTROL_HOST_ALLHOSTS);
+    host = PullStackByID (stack, HostID);
+    if (host == NULL) {
+      gprint (GP_LOG, "host not found\n");
+      return (FALSE);
+    }
+    gprint (GP_LOG, "host %s\n", GetHostStackName(host[0].stack));
+    PushStack (stack, STACK_BOTTOM, host, host[0].HostID, host[0].hostname);
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "unknown item to check\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/delete.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/delete.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/delete.c	(revision 22322)
@@ -0,0 +1,50 @@
+# include "pcontrol.h"
+
+int delete (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: delete (JobID)\n");
+    return (FALSE);
+  }
+  JobID = GetID (argv[1]);
+  if (!JobID) {
+    gprint (GP_ERR, "invalid job id %s\n", argv[1]);
+    return (FALSE);
+  }
+      
+  /* use a string interp to convert JobIDs to ints ? */
+
+  job = PullJobFromStackByID (PCONTROL_JOB_PENDING, JobID);
+  if (job != NULL) goto found;
+
+  job = PullJobFromStackByID (PCONTROL_JOB_CRASH, JobID);
+  if (job != NULL) goto found;
+
+  job = PullJobFromStackByID (PCONTROL_JOB_EXIT, JobID);
+  if (job != NULL) goto found;
+
+  gprint (GP_ERR, "job %s not PENDING, CRASH, EXIT\n", argv[1]);
+  return (FALSE);
+  
+found:
+  {
+    int j;
+    gprint (GP_LOG, "deleting job  %s  %d  ", job[0].hostname, job[0].argc);
+    for (j = 0; j < job[0].argc; j++) {
+      gprint (GP_LOG, "%s ", job[0].argv[j]);
+    }
+    PrintID (GP_LOG, job[0].JobID);
+    gprint (GP_LOG, "\n");
+  }  
+  DelJob (job);
+
+  return (TRUE);
+}
+
+/**** at the moment, this function requires the job to be in the correct state
+      to be deleted.  This should be changed to kill, then delete job if it is 
+      not in the correct state 
+****/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/help/host
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/help/host	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/help/host	(revision 22322)
@@ -0,0 +1,1 @@
+empty
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/host.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/host.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/host.c	(revision 22322)
@@ -0,0 +1,101 @@
+# include "pcontrol.h"
+
+int host (int argc, char **argv) {
+
+  int N, max_threads;
+  IDtype HostID;
+  Host *host;
+  Stack *AllHosts;
+
+  max_threads = 0;
+  if ((N = get_argument (argc, argv, "-threads"))) {
+    remove_argument (N, &argc, argv);
+    max_threads = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) goto usage;
+
+  AllHosts = GetHostStack (PCONTROL_HOST_ALLHOSTS);
+
+  if (!strcasecmp (argv[1], "ADD")) {
+    HostID = AddHost (argv[2], max_threads);
+    gprint (GP_LOG, "HostID: %d\n", (int) HostID);
+    return (TRUE);
+  }
+
+  if (max_threads) goto usage;
+
+  // this one is safe from in-flight entries: no one else pulls from OFF
+  if (!strcasecmp (argv[1], "ON")) {
+    host = PullHostFromStackByName (PCONTROL_HOST_OFF, argv[2]);
+    if (!host) {
+      gprint (GP_LOG, "host %s is not OFF\n", argv[2]);
+      return (FALSE);
+    }
+    host[0].markoff = FALSE;
+    DownHost (host);
+    return (TRUE);
+  }
+
+  // this is a race condition with "CheckDownHosts", but the only 
+  // consequence is that both StartHost and reset set the times to 0.0
+  if (!strcasecmp (argv[1], "RETRY")) {
+    // no need to use a check point [thief: CheckDownHost (DOWN->IDLE)]
+    host = PullHostFromStackByName (PCONTROL_HOST_ALLHOSTS, argv[2]);
+    if (!host) {
+      gprint (GP_LOG, "host %s not found\n", argv[2]);
+      return (FALSE);
+    }
+    if (host[0].stack != PCONTROL_HOST_DOWN) {
+      gprint (GP_LOG, "host %s is not DOWN\n", argv[2]);
+      return (FALSE);
+    }
+    /* reset time, place back on ALLHOSTS stack */
+    host[0].nexttry.tv_sec  = 0;
+    host[0].nexttry.tv_usec = 0;
+    host[0].lasttry.tv_sec  = 0;
+    host[0].lasttry.tv_usec = 0;
+    PushStack (AllHosts, STACK_BOTTOM, host, host[0].HostID, host[0].hostname);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "CHECK")) {
+    host = PullHostFromStackByName (PCONTROL_HOST_ALLHOSTS, argv[2]);
+    if (host == NULL) {
+      gprint (GP_LOG, "host %s not found\n", argv[2]);
+      return (FALSE);
+    }
+    gprint (GP_LOG, "host %s is %s\n", argv[2], GetHostStackName (host[0].stack));
+    PushStack (AllHosts, STACK_BOTTOM, host, host[0].HostID, host[0].hostname);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "OFF")) {
+    host = PullHostFromStackByName (PCONTROL_HOST_ALLHOSTS, argv[2]);
+    if (host == NULL) {
+      gprint (GP_LOG, "host %s not found\n", argv[2]);
+      return (FALSE);
+    }
+    host[0].markoff = TRUE;
+    PushStack (AllHosts, STACK_BOTTOM, host, host[0].HostID, host[0].hostname);
+    return (TRUE);
+  }
+
+  // this one is safe from in-flight entries: no one else pulls from OFF
+  if (!strcasecmp (argv[1], "DELETE")) {
+    host = PullHostFromStackByName (PCONTROL_HOST_OFF, argv[2]);
+    if (!host) {
+      gprint (GP_LOG, "host %s is not OFF\n", argv[2]);
+      return (FALSE);
+    }
+    DelHost (host);
+    return (TRUE);
+  }
+  
+usage:
+  gprint (GP_LOG, "USAGE: host (command) (hostname)\n");
+  gprint (GP_ERR, "  valid commands: add, on, retry, check, off, delete\n");
+  gprint (GP_ERR, "  -threads Nthreads is optional for 'add'\n");
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/hoststack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/hoststack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/hoststack.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "pcontrol.h"
+
+int hoststack (int argc, char **argv) {
+
+  int i;
+  Stack *stack;
+  Host *host;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: hoststack (hoststack)\n");
+    gprint (GP_ERR, "       (hoststack) : idle, busy, done, down, off\n");
+    return (FALSE);
+  }
+
+  /* select hoststack */
+  stack = GetHostStackByName (argv[1]);
+  if (stack == NULL) {
+    gprint (GP_ERR, "hoststack not found\n");
+    return (FALSE);
+  }
+
+  /* print list */
+  LockStack (stack);
+  gprint (GP_LOG, "Nhosts: %d\n", stack[0].Nobject);
+  for (i = 0; i < stack[0].Nobject; i++) {
+    host = stack[0].object[i];
+    gprint (GP_LOG, "%lld %s\n", host[0].HostID, host[0].hostname);
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/init.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/init.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/init.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "pcontrol.h"
+
+int check       PROTO((int, char **));
+int delete      PROTO((int, char **));
+int host        PROTO((int, char **));
+int hoststack   PROTO((int, char **));
+int job	        PROTO((int, char **));
+int jobstack    PROTO((int, char **));
+int kill_pc     PROTO((int, char **));
+int status      PROTO((int, char **));
+int run         PROTO((int, char **));
+int stderr_pc   PROTO((int, char **));
+int stdout_pc   PROTO((int, char **));
+int verbose     PROTO((int, char **));
+int version     PROTO((int, char **));
+
+// pulse is only available in the un-threaded version
+int pulse       PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "host",      host,      "add / delete / modify host"},
+  {1, "hoststack", hoststack, "list hosts for a single stack"},
+  {1, "status",    status,    "get system status"},
+  {1, "stop",      run,       "stop controller processing"},
+  {1, "run",       run,       "set controller runlevel"},
+  {1, "verbose",   verbose,   "set the verbose mode for job"},
+  {1, "version",   version,   "show version information"},
+  {1, "job",       job,       "add job"},
+  {1, "jobstack",  jobstack,  "list jobs for a single stack"},
+  {1, "check",     check,     "get job or host status"},
+  {1, "delete",    delete,    "delete job"},
+  {1, "kill",      kill_pc,   "kill job"},
+  {1, "stderr",    stderr_pc, "get stderr buffer for job"},
+  {1, "stdout",    stdout_pc, "get stdout buffer for job"},
+# ifndef THREADED
+  {1, "pulse",     pulse,     "set system pulse"},
+# endif
+}; 
+
+void InitPcontrol () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+  InitJobStacks ();
+  InitHostStacks ();
+}
+
+void FreePcontrol () {
+  FreeJobStacks ();
+  FreeHostStacks ();
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/job.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/job.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/job.c	(revision 22322)
@@ -0,0 +1,53 @@
+# include "pcontrol.h"
+
+int job (int argc, char **argv) {
+
+  char *Host, **targv;
+  int i, N, Mode, targc, Timeout;
+  IDtype JobID;
+
+  if (get_argument (argc, argv, "-host") && get_argument (argc, argv, "+host")) {
+      gprint (GP_ERR, "ERROR: -host and +host are incompatible\n");
+      return (FALSE);
+  }    
+
+  Host = NULL;
+  Mode = PCONTROL_JOB_ANYHOST;
+  if ((N = get_argument (argc, argv, "-host"))) {
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_WANTHOST;
+  }
+  if ((N = get_argument (argc, argv, "+host"))) {
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_NEEDHOST;
+  }
+  if (Host == NULL) Host = strcreate ("anyhost");
+ 
+  Timeout = 100;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    remove_argument (N, &argc, argv);
+    Timeout = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  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);
+  }
+  
+  targc = argc - 1;
+  ALLOCATE (targv, char *, targc);
+  for (i = 1; i < argc; i++) {
+    targv[i-1] = strcreate (argv[i]);
+  }
+
+  JobID = AddJob (Host, Mode, Timeout, targc, targv);
+  gprint (GP_LOG, "JobID: %d\n", (int) JobID);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/jobstack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/jobstack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/jobstack.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "pcontrol.h"
+
+int jobstack (int argc, char **argv) {
+
+  int i;
+  Stack *stack;
+  Job *job;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: jobstack (jobstack)\n");
+    gprint (GP_ERR, "       (jobstack) : pending, busy, exit, crash, hung, done\n");
+    return (FALSE);
+  }
+
+  /* select jobstack */
+  stack = GetJobStackByName (argv[1]);
+  if (stack == NULL) {
+    gprint (GP_ERR, "jobstack not found\n");
+    return (FALSE);
+  }
+
+  /* print list */
+  LockStack (stack);
+  gprint (GP_LOG, "Njobs: %d\n", stack[0].Nobject);
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = stack[0].object[i];
+    /* PrintID (GP_LOG, job[0].JobID); */
+    gprint (GP_LOG, "%lld ", job[0].JobID);
+    if (job[0].realhost) {
+	gprint (GP_LOG, "%s   %s\n", job[0].argv[0], job[0].realhost);
+    } else {
+	gprint (GP_LOG, "%s  (%s)\n", job[0].argv[0], job[0].hostname);
+    }
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/kill.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/kill.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/kill.c	(revision 22322)
@@ -0,0 +1,24 @@
+# include "pcontrol.h"
+
+int kill_pc (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: kill (JobID)\n");
+    return (FALSE);
+  }
+  JobID = atoi (argv[1]);
+
+  /* XXX this function should only fail if a process is hung */
+  job = PullJobFromStackByID (PCONTROL_JOB_BUSY, JobID);
+  if (job == NULL) {
+    gprint (GP_ERR, "job %s not BUSY\n", argv[1]);
+    /* make output message more readable by scheduler */
+    return (FALSE);
+  }
+
+  PutJob (job, PCONTROL_JOB_KILL, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/notes.txt	(revision 22322)
@@ -0,0 +1,69 @@
+
+2008.06.11
+
+  Upgrade: add a virtual stack of the jobs and hosts so that the user
+  status queries do not need to block until the processing thread is
+  caught up.
+
+  
+
+2008.04.21 
+
+  I am having trouble with pcontrol managing the clients.  The
+  functions which call PclientCommand expect to see a complete
+  response in the same call.  However, latency in the client means, if
+  the client is super busy, that this interaction will either result
+  in dropped messages (which look like HUNG machines) or in long
+  blocks against the pantasks/pcontrol interface.  
+
+  A possible solution may be to break the functions which call
+  PclientCommand into a PclientCommand and a PclientResponse call.
+  The PclientCommand would send the command and report any comms
+  errors in that interaction (pipe closed, interrupts, etc).  It would
+  return immediately, pushing the 
+
+-- pcontrol / pclient interactions
+
+StartJob:
+xmt: job argv[0] argv[1] ...
+rcv: 
+ STATUS (PID)
+ pclient:
+
+CheckBusyJob:
+xmt: status
+rcv: 
+ EXITST N
+ STDOUT N
+ STDERR N
+ STATUS (value)
+ pclient:
+
+CheckDoneHost:
+xmt: reset
+rcv: 
+ STATUS (status)
+ pclient:
+
+CheckHost:
+xmt: echo OK
+rcv:
+ OK
+ pclient:
+
+KillJob:
+xmt: reset
+rcv: 
+ STATUS (status)
+ pclient:
+
+ResetJob:
+xmt: reset
+rcv: 
+ STATUS (status)
+ pclient:
+
+StopHost:
+xmt: ext
+rcv: 
+ Goodbye
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/pcontrol.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/pcontrol.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/pcontrol.c.in	(revision 22322)
@@ -0,0 +1,98 @@
+# include "pcontrol.h"
+
+# define opihi_name "PCONTROL"
+# define opihi_prompt "pcontrol: "
+# define opihi_description "pcontrol client shell\n"
+# define opihi_history ".pcontrol"
+# define opihi_rcfile ".pcontrolrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+# ifdef THREADED  
+  pthread_t clientsThread;
+# endif
+
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitPcontrol ();
+
+  /* set global signal masks (these apply to all threads launched below) */
+  signal (SIGPIPE, SIG_IGN); // must ignore SIGPIPE or we get in an infinite loop when pantasks exits
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+# ifdef THREADED
+  SetRunLevel (PCONTROL_RUN_ALL);
+  pthread_create (&clientsThread, NULL, &CheckSystem_Threaded, NULL);
+  rl_event_hook = NULL;
+  rl_set_keyboard_input_timeout (1000); 
+# else
+  rl_event_hook = CheckSystem;
+  rl_set_keyboard_input_timeout (1000); 
+# endif  
+
+  // set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  /* ignore the history file.  to change this, see, eg, mana.c */
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  // stop checking on the jobs
+  SetRunLevel (PCONTROL_RUN_HOSTS); 
+  DownHosts ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreePcontrol ();
+
+  return;
+}
+
+void gotsignal (int signum) {
+  // this message is lost if we are connected to a pantasks
+  fprintf (stderr, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  abort ();	
+  exit (status);
+}
+
+void pcontrol_exit (int n) {
+  abort ();
+  exit (n);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/pulse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/pulse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/pulse.c	(revision 22322)
@@ -0,0 +1,16 @@
+# include "pantasks.h"
+
+int pulse (int argc, char **argv) {
+
+  int Nusec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: pulse (microseconds)\n");
+    return (FALSE);
+  }
+
+  Nusec = atoi (argv[1]);
+  rl_set_keyboard_input_timeout (Nusec); 
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/rconnect.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/rconnect.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/rconnect.c	(revision 22322)
@@ -0,0 +1,148 @@
+# include "pcontrol.h"
+
+/* connection can take a while, allow up to 2 sec */
+# define CONNECT_TIMEOUT 5000
+
+/* connect to host, start the shell: ssh hostname pclient -> command hostname shell
+   stdio is an array of file descriptors (stdio[3])
+*/
+
+int rconnect (char *command, char *hostname, char *shell, int *stdio) {
+
+  int i = 0, stdin_fd[2], stdout_fd[2], stderr_fd[2], status;
+  int result, waitstatus;
+  pid_t pid;
+  char *p;
+  char **argv;
+  IOBuffer buffer;
+  struct timespec request, remain;
+
+  ASSERT (command != NULL, "command is NULL");
+  ASSERT (hostname != NULL, "hostname is NULL");
+  ASSERT (shell != NULL, "shell is NULL");
+  ASSERT (stdio != NULL, "stdio is NULL");
+
+  bzero (stdin_fd,  2*sizeof(int));
+  bzero (stdout_fd, 2*sizeof(int));
+  bzero (stderr_fd, 2*sizeof(int));
+
+  if (pipe (stdin_fd)  < 0) goto pipe_error;
+  if (pipe (stdout_fd) < 0) goto pipe_error;
+  if (pipe (stderr_fd) < 0) goto pipe_error;
+
+  ALLOCATE (argv, char *, 4);
+  argv[0] = command;
+  argv[1] = hostname;
+  argv[2] = shell;
+  argv[3] = 0;
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    if (VerboseMode()) gprint (GP_ERR, "starting remote connection to %s...", hostname);
+
+    /* close the other ends of the pipes */
+    close (stdin_fd[1]);
+    close (stdout_fd[0]);
+    close (stderr_fd[0]);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (stdin_fd[0],  STDIN_FILENO);
+    dup2 (stdout_fd[1], STDOUT_FILENO);
+    dup2 (stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    status = execvp (argv[0], argv);
+    gprint (GP_ERR, "error starting remote shell process\n");
+    pcontrol_exit (60);
+  }
+  free (argv);
+
+  /* close the other ends of the pipes */
+  close (stdin_fd[0]);  stdin_fd[0]  = 0;
+  close (stdout_fd[1]); stdout_fd[1] = 0;
+  close (stderr_fd[1]); stderr_fd[1] = 0;
+
+  /* make the pipes non-blocking */
+  fcntl (stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+
+  /* perform handshake with pclient to verify alive & running */
+  /** this handshake is similar to PclientCommand, but has important differences **/
+  InitIOBuffer (&buffer, 0x100);
+
+  /* send handshake command */
+  status = write_fmt (stdin_fd[1], "echo CONNECTED\n");
+  if ((status == -1) && (errno == EPIPE)) goto connect_error;
+
+  /* try to get evidence connection is alive - wait upto a few seconds */
+  p = NULL;
+  status = -1;
+  for (i = 0; (i < CONNECT_TIMEOUT) && (status != 0) && (p == NULL); i++) {
+    status = ReadtoIOBuffer (&buffer, stdout_fd[0]);
+    p = memstr (buffer.buffer, "CONNECTED", buffer.Nbuffer);
+    usleep (10000); // wait for client to be connected
+  }
+  if (status == 0) goto connect_error;
+  if (status == -1) goto connect_error;
+  if (VerboseMode()) gprint (GP_ERR, "%d cycles to connect\n", i);
+  FreeIOBuffer (&buffer);
+
+  if (VerboseMode()) gprint (GP_ERR, "Connected\n");
+
+  stdio[0] = stdin_fd[1];
+  stdio[1] = stdout_fd[0];
+  stdio[2] = stderr_fd[0];
+
+  return (pid);
+
+pipe_error:
+  perror ("pipe error:");
+  goto close_pipes;
+
+connect_error:
+  if (VerboseMode()) gprint (GP_ERR, "error while connecting, status: %d, ncycles: %d\n", status, i);
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* harvest the child process: kill & wait (< 100 ms) for exit */
+  kill (pid, SIGKILL);
+  result = waitpid (pid, &waitstatus, WNOHANG);
+  for (i = 0; (i < 50) && (result == 0); i++) {
+    nanosleep (&request, &remain);
+    result = waitpid (pid, &waitstatus, WNOHANG);
+  }
+
+  if ((result == -1) && (errno != ECHILD)) {
+    gprint (GP_ERR, "unexpected error from waitpid (%d): programming error\n", errno);
+    pcontrol_exit (61);
+  }
+  if (result == 0) {
+    if (VerboseMode()) gprint (GP_ERR, "child did not exit (rconnect)??");
+  }
+  if (result > 0) {
+    if (result != pid) {
+      gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+      pcontrol_exit (62);
+    }
+    if (WIFSTOPPED(waitstatus)) {
+      gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+      pcontrol_exit (63);
+    }
+  }
+
+close_pipes:
+  if (stdin_fd[0]  != 0) close (stdin_fd[0]);
+  if (stdin_fd[1]  != 0) close (stdin_fd[1]);
+  if (stdout_fd[0] != 0) close (stdout_fd[0]);
+  if (stdout_fd[1] != 0) close (stdout_fd[1]);
+  if (stderr_fd[0] != 0) close (stderr_fd[0]);
+  if (stderr_fd[1] != 0) close (stderr_fd[1]);
+  return (FALSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/run.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/run.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/run.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "pcontrol.h"
+
+int run (int argc, char **argv) {
+
+  RunLevels level;
+
+  if ((argc > 2) ||
+      get_argument (argc, argv, "help") ||
+      get_argument (argc, argv, "-h") ||
+      get_argument (argc, argv, "--help")) 
+  {
+    if (!strcmp (argv[0], "run")) {
+      gprint (GP_ERR, "USAGE: run [level]\n");
+      gprint (GP_ERR, "  allowed levels:\n");
+      gprint (GP_ERR, "  all (default) : manage machines, spawn jobs, harvest jobs\n");
+      gprint (GP_ERR, "  reap          : manage machines, harvest jobs\n");
+      gprint (GP_ERR, "  hosts         : manage machines, not jobs\n");
+      gprint (GP_ERR, "  none          : all stop\n");
+    } else {
+      gprint (GP_ERR, "USAGE: stop (immediate processing halt)\n");
+    }
+    return (FALSE);
+  }
+
+  level = PCONTROL_RUN_UNKNOWN;
+  if (argc == 1) {
+    if (!strcasecmp (argv[0], "run")) level = PCONTROL_RUN_ALL;
+    if (!strcasecmp (argv[0], "stop")) level = PCONTROL_RUN_NONE;
+  } else {
+    if (!strcasecmp (argv[1], "all")) level = PCONTROL_RUN_ALL;
+    if (!strcasecmp (argv[1], "reap")) level = PCONTROL_RUN_REAP;
+    if (!strcasecmp (argv[1], "host")) level = PCONTROL_RUN_HOSTS;
+    if (!strcasecmp (argv[1], "hosts")) level = PCONTROL_RUN_HOSTS;
+    if (!strcasecmp (argv[1], "none")) level = PCONTROL_RUN_NONE;
+  }
+
+  if (level == PCONTROL_RUN_UNKNOWN) {
+    gprint (GP_ERR, "  unknown run level %s\n", argv[1]);
+    gprint (GP_ERR, "  allowed levels:\n");
+    gprint (GP_ERR, "  all (default) : manage machines, spawn jobs, harvest jobs\n");
+    gprint (GP_ERR, "  reap          : manage machines, harvest jobs\n");
+    gprint (GP_ERR, "  hosts         : manage machines, not jobs\n");
+    gprint (GP_ERR, "  none          : all stop\n");
+    return (FALSE);
+  }
+
+# ifdef THREADED
+  SetRunLevel (level);
+# else
+  if (level == PCONTROL_RUN_NONE) {
+    rl_event_hook = NULL;
+  } else {
+    rl_event_hook = CheckSystem;
+  }
+# endif
+
+  return (TRUE);
+}
+
+/* 
+   run levels:
+   all (manage machines, spawn jobs, harvest jobs)
+   reap (manage machines, harvest jobs)
+   hosts (manage machines, not jobs)
+   none (all stop)
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/status.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/status.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/status.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "pcontrol.h"
+
+int PrintJobStack (int Nstack);
+int PrintHostStack (int Nstack);
+
+int status (int argc, char **argv) {
+
+  PrintJobStack (PCONTROL_JOB_ALLJOBS);
+  PrintHostStack (PCONTROL_HOST_ALLHOSTS);
+
+  return (TRUE);
+}
+
+int PrintJobStack (int Nstack) {
+
+  int i, j, Nobject;
+  Stack *stack;
+  Job *job;
+
+  stack = GetJobStack (Nstack);
+  ASSERT (stack != NULL, "programming error");
+
+  LockStack (stack);
+  Nobject = stack[0].Nobject;
+  gprint (GP_LOG, "job stack %s:  %d objects\n", GetJobStackName(Nstack), Nobject);
+
+  for (i = 0; i < Nobject; i++) {
+    job = stack[0].object[i];
+    ASSERT (job != NULL, "programming error");
+    if (job[0].realhost == NULL) {
+	gprint (GP_LOG, "%3d %9s ", i, job[0].hostname);
+    } else {
+	gprint (GP_LOG, "%3d %9s ", i, job[0].realhost);
+    }
+    gprint (GP_LOG, "%7s  ", GetJobStackName (job[0].state));
+    for (j = 0; j < job[0].argc; j++) {
+      gprint (GP_LOG, "%s ", job[0].argv[j]);
+    }
+    PrintID (GP_LOG, job[0].JobID);
+    gprint (GP_LOG, "\n");
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+int PrintHostStack (int Nstack) {
+
+  int i, Nobject;
+  Stack *stack;
+  Host *host;
+
+  stack = GetHostStack (Nstack);
+
+  LockStack (stack);
+  Nobject = stack[0].Nobject;
+  gprint (GP_LOG, "host stack %s:  %d objects\n", GetHostStackName(Nstack), Nobject);
+
+  for (i = 0; i < Nobject; i++) {
+    host = stack[0].object[i];
+    gprint (GP_LOG, "%d  %s  ", i, host[0].hostname);
+    gprint (GP_LOG, "%5s  ", GetHostStackName (host[0].stack));
+    PrintID (GP_LOG, host[0].HostID);
+    gprint (GP_LOG, "\n");
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/stdout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/stdout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/stdout.c	(revision 22322)
@@ -0,0 +1,110 @@
+# include "pcontrol.h"
+
+// XXX unify by testing for value of argv[0]
+int stdout_pc (int argc, char **argv) {
+
+  int N, JobID, StackID;
+  Job *job;
+  IOBuffer *buffer;
+  char *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stdout (JobID) [-var name]\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  /* find Job of interest (must be EXIT or CRASH) */
+  JobID = atoi (argv[1]);
+
+  StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stdout;
+
+  StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stdout;
+
+  gprint (GP_ERR, "job not found in EXIT or CRASH\n");
+  if (varName == NULL) {
+    gprint (GP_LOG, "STATUS %d\n", -2);
+  } else {
+    set_str_variable (varName, "NULL");
+    free (varName);
+  }
+  return (FALSE);
+
+found_stdout:
+  buffer = &job[0].stdout_buff;
+  if (varName == NULL) {
+    fwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, stdout);
+    gprint (GP_LOG, "STATUS %d\n", 0);
+  } else {
+    // XXX this can drop '0' values
+    set_str_variable (varName, buffer[0].buffer);
+    free (varName);
+  }
+  PutJob (job, StackID, STACK_BOTTOM);
+  return (TRUE);
+}
+
+int stderr_pc (int argc, char **argv) {
+
+  int N, JobID, StackID;
+  Job *job;
+  IOBuffer *buffer;
+  char *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stderr (JobID) [-var name]\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  /* find Job of interest (must be EXIT or CRASH) */
+  JobID = atoi (argv[1]);
+
+  StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stderr;
+
+  StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stderr;
+
+  gprint (GP_ERR, "job not found in EXIT or CRASH\n");
+  if (varName == NULL) {
+    gprint (GP_LOG, "STATUS %d\n", -2);
+  } else {
+    set_str_variable (varName, "NULL");
+    free (varName);
+  }
+  return (FALSE);
+
+found_stderr:
+  buffer = &job[0].stderr_buff;
+  if (varName == NULL) {
+    fwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, stdout);
+    gprint (GP_LOG, "STATUS %d\n", 0);
+  } else {
+    // XXX this can drop '0' values
+    set_str_variable (varName, buffer[0].buffer);
+    free (varName);
+  }
+  PutJob (job, StackID, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/stop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/stop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/stop.c	(revision 22322)
@@ -0,0 +1,17 @@
+# 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: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/test/linesize-remote.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/test/linesize-remote.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/test/linesize-remote.sh	(revision 22322)
@@ -0,0 +1,48 @@
+# we are having trouble with pcontrol -> pclient comms with very long lines (> 512)
+# this seems to be a problem with readline buffering
+
+# generate jobs with very long lines
+# XXX this must be run as the only pass in pcontrol (to get the JobIDs right)
+
+macro test
+  if ($0 != 2)
+    echo "USAGE: test (remotehost)"
+    break
+  end
+
+  host add $1
+
+  $inline1 = abcdefghijklmnopqrstuvwxyz
+  $inline2 = $inline1+$inline1
+  $inline3 = $inline2-$inline2
+  $inline4 = $inline3=$inline3
+  $inline5 = $inline4*$inline4
+  $inline6 = $inline5^$inline5
+  $inline7 = $inline6%$inline6
+
+  strlen "inline1: $inline1"
+  strlen "inline2: $inline2"
+  strlen "inline3: $inline3"
+  strlen "inline4: $inline4"
+  strlen "inline5: $inline5"
+  strlen "inline6: $inline6"
+  strlen "inline7: $inline7"
+
+  for i 1 10
+    job echo $inline7
+  end
+
+  sleep 5
+
+  for i 1 10
+    stdout $i -var outline
+    strlen "$inline7" Nin
+    strlen "$outline" Nout
+
+    echo Nin: $Nin, Nout: $Nout
+
+    if ($Nin != $Nout - 1)
+      echo "mismatch in length!"
+    end
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/test/linesize.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/test/linesize.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/test/linesize.sh	(revision 22322)
@@ -0,0 +1,45 @@
+# we are having trouble with pcontrol -> pclient comms with very long lines (> 512)
+# this seems to be a problem with readline buffering
+
+# generate jobs with very long lines
+# XXX this must be run as the only pass in pcontrol (to get the JobIDs right)
+
+$localhost = `hostname`
+
+macro test
+  host add $localhost
+
+  $inline1 = abcdefghijklmnopqrstuvwxyz
+  $inline2 = $inline1+$inline1
+  $inline3 = $inline2-$inline2
+  $inline4 = $inline3=$inline3
+  $inline5 = $inline4*$inline4
+  $inline6 = $inline5^$inline5
+  $inline7 = $inline6%$inline6
+
+  strlen "inline1: $inline1"
+  strlen "inline2: $inline2"
+  strlen "inline3: $inline3"
+  strlen "inline4: $inline4"
+  strlen "inline5: $inline5"
+  strlen "inline6: $inline6"
+  strlen "inline7: $inline7"
+
+  for i 1 10
+    job echo $inline7
+  end
+
+  sleep 5
+
+  for i 1 10
+    stdout $i -var outline
+    strlen "$inline7" Nin
+    strlen "$outline" Nout
+
+    echo Nin: $Nin, Nout: $Nout
+
+    if ($Nin != $Nout - 1)
+      echo "mismatch in length!"
+    end
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/verbose.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/verbose.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/verbose.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "pcontrol.h"
+
+static int VERBOSE = FALSE;
+
+int verbose (int argc, char **argv) {
+
+  if (argc == 1) {
+    if (VERBOSE) {
+      gprint (GP_ERR, "verbose mode ON\n");
+    } else {
+      gprint (GP_ERR, "verbose mode OFF\n");
+    }
+    return (TRUE);
+  }
+
+  if (argc == 2) {
+    if (!strcasecmp (argv[1], "ON")) {
+      VERBOSE = TRUE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "OFF")) {
+      VERBOSE = FALSE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "TOGGLE")) {
+      VERBOSE = ~VERBOSE;
+      return (TRUE);
+    }
+  }
+
+  gprint (GP_ERR, "USAGE: verbose (on/off/toggle)\n");
+  return (FALSE);
+}
+
+int VerboseMode () {
+  return (VERBOSE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/version.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/version.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/pcontrol/version.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "pcontrol.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pcontrol version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/copy.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/copy.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/copy.pro	(revision 22322)
@@ -0,0 +1,136 @@
+
+# identify the images ready for copy 
+task	       new.images
+  command      new.images
+  host         local
+
+  periods      -poll 1
+  periods      -exec 30
+  periods      -timeout 2
+
+  # success
+  task.exit    0
+    push new.images $stdout
+    $new.image.failure = 0
+  end
+
+  # locked list
+  task.exit    1
+    echo       "new.images: exec failure"
+    $new.image.failure ++
+  end
+
+  # default exit status
+  task.exit    -
+    echo       "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "new.images: timeout"
+    $new.image.failure ++
+  end
+end
+
+# copy specific images from the summit
+task           copy.images
+
+  # these define task properties which are fixed
+
+  period       -exec 2
+  period       -poll 1
+  period       -timeout 50
+
+  # these commands are executed at the start of a new task
+  task.exec
+    local        RemoteName
+    local        FileID
+    local        Host
+
+    queuesize  NewImages -var N
+    if ($N > 0) break
+    if ($network == 0) break
+    if ($filesystem == 1) break
+
+    pop NewImages -var line
+    list tmp -split $line
+    $RemoteName = $tmp:0
+    $FileID     = $tmp:1
+    $Host       = $tmp:2
+    $Ntry       = $tmp:3
+
+    stderr       /data/logfiles/copy/$FileID.log
+    stdout       -queue $FileIDlog
+
+    echo $TaskID
+    echo $TaskName
+    spawn copy.images $RemoteName $FileID $Host -host $Host
+  end
+
+  # success
+  task.exit      0
+    $new.image.failure --
+  end
+
+  # summmit connection failed
+  task.exit      1
+    echo         "copy.images: copy failed $RemoteName $FileID"
+    push         NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+
+  # target disk full
+  task.exit      2
+    echo         "copy.images: disk full"
+    push         NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+
+  # file not found
+  task.exit      3
+    echo         "copy.images: missing file"
+    # send a message to OTIS?
+  end
+
+  # task timed out
+  task.exit      timeout
+    echo        "copy.images: timeout"
+    push        NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+end
+
+# identify the images ready for copy 
+TASK	       new.images
+  COMMAND      new.images
+  HOST         -
+  STDERR       /data/logfiles/new.images.log
+  STDOUT       $stdout
+  EXEC_PERIOD  30
+  POLL_PERIOD  1
+
+  # success
+  EXIT         0
+    STDOUT     @new.images
+    $new.image.failure = 0
+  END
+
+  # locked list
+  EXIT         1
+    MESSAGE    "new.images: exec failure"
+    $new.image.failure ++
+  END
+
+  # default exit status
+  EXIT         -
+    MESSAGE    "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  END
+
+  # operation times out?
+  TIMEOUT      2
+    MESSAGE    "new.images: timeout"
+    $new.image.failure ++
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/copypool.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/copypool.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/copypool.pro	(revision 22322)
@@ -0,0 +1,117 @@
+
+## A test task to demonstrate cycled copying using pending and done pools (directories)
+##   and pending queues
+
+queueinit stdout
+queueinit stderr
+queueinit pending
+
+$indir = dir.01
+$outdir = dir.02
+
+# set the test rate to be 1ms
+pulse 1000
+
+macro init
+  exec rm -r dir.*
+  exec mkdir $indir
+  exec mkdir $outdir
+end
+
+$Nfile = 0
+task pool.create
+  periods -exec 0.02
+  periods -poll 0.02
+
+  task.exec
+    sprintf filename "foo.%04d.dat" $Nfile
+    $Nfile ++
+    host local
+    command copypool.sh -create $indir $filename
+  end
+
+  task.exit  default
+    queueinit stdout
+    queueinit stderr
+  end
+end
+
+task pool.pending
+  command copypool.sh -pending $indir
+  host local
+
+  periods -exec 5
+  periods -poll 1
+
+  # success
+  task.exit    0
+    local i Nstdout
+
+    # keep only new, unique entries (name is key)
+    queuesize stdout -var Nstdout
+    for i 0 $Nstdout
+      queuepop stdout -var line
+      queuepush pending -uniq -key 0 "$line new"
+    end
+    queueprint pending
+  end
+end
+
+task pool.copy
+  host local
+
+  periods -exec 1
+  periods -poll 1
+
+  task.exec
+    local Npending
+    queuesize pending -var Npending
+    if ($Npending == 0) 
+      periods -exec 1
+      periods -poll 1
+      break
+    end
+
+    # if data is available, run fast to clear it out
+    periods -exec 0.002
+    periods -poll 0.002
+
+    # this step grabs and entry by key and updates a field
+    # can we do this in one step?
+    queuepop pending -var line -key 1 new
+    if ("$line" == "NULL") break
+
+    list tmp -split $line
+    $name = $tmp:0
+
+    queuepush pending "$name run"
+
+    host local
+    command copypool.sh -copy $outdir $name
+  end
+
+  # success
+  task.exit 0
+    echo "done with $taskarg:3"
+    queueinit stdout
+    queueinit stderr
+    queuepop pending -key 0 $taskarg:3 -var line
+    # echo "got line: $line"
+
+    if ("$line" == "NULL") 
+      echo "missing entry in pending queue?"
+      break
+    end
+  end
+end
+
+task pool.list
+  host local
+  periods -exec 3
+  command queueprint pending
+
+  task.exit default
+    queueinit stdout
+    queueinit stderr
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/copypool.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/copypool.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/copypool.sh	(revision 22322)
@@ -0,0 +1,31 @@
+#!/bin/csh -f
+
+# copypool.sh -pending (indir)
+# copypool.sh -copy (outdir) (path)
+# copypool.sh -create (outdir) (name)
+
+if ($#argv < 2) then
+  echo "USAGE: copypool.sh -pending (indir)"
+  echo "USAGE: copypool.sh -copy (outdir) (path)"
+  echo "USAGE: copypool.sh -create (outdir) (name)"
+  exit 2
+endif
+
+if ("$1" == "-pending") then
+  set n = `ls $2 | wc -l`
+  if ($n == 0) exit 0
+  ls $2/* | cat
+  exit 0
+endif
+
+if ("$1" == "-copy") then
+  set name = `basename $3`
+  rm $3
+  touch $2/$name
+  exit 0
+endif
+
+if ("$1" == "-create") then
+  touch $2/$3
+  exit 0
+endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/diff-opihi
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/diff-opihi	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/diff-opihi	(revision 22322)
@@ -0,0 +1,38 @@
+#!/bin/csh -f
+
+# this just organizes the tagging of the opihi components
+
+if ($#argv != 2) then
+  echo "USAGE: tag-elixir (MODULE) (TAG)"
+  echo "  MODULE choices: base mana dvo dimm pantasks pcontrol pclient"
+  exit 2
+endif
+
+if ("$1" == "base") then
+  cvs diff -r $2 Makefile
+  cvs diff -r $2 Makefile.Common
+  cvs diff -r $2 include
+  cvs diff -r $2 test
+  cvs diff -r $2 lib.data
+  cvs diff -r $2 lib.shell
+  cvs diff -r $2 cmd.astro
+  cvs diff -r $2 cmd.basic
+  cvs diff -r $2 cmd.data
+  cvs diff -r $2 scripts
+  cvs diff -r $2 doc
+  exit 0;
+endif
+
+if ("$1" == "mana") goto valid;
+if ("$1" == "dvo") goto valid;
+if ("$1" == "dimm") goto valid;
+if ("$1" == "pantasks") goto valid;
+if ("$1" == "pclient") goto valid;
+if ("$1" == "pcontrol") goto valid;
+echo "invalid opihi module $1"
+exit 1;
+
+valid:
+  # diff the module ($1) with the specified tag ($2)
+  cvs diff -r $2 $1
+  exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/jobs.plot
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/jobs.plot	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/jobs.plot	(revision 22322)
@@ -0,0 +1,24 @@
+
+macro jobs.rates
+ clear -s
+
+ section a 0.0 0.0 1.0 0.5
+ data jobs.stop.dat
+ read t 6
+ set T = t*(t > 30) + (t+60)*(t<30)
+ create n 0 t[]
+ lim T n; box; plot T n
+ subset tf = T if (T > 70)
+ subset nf = n if (T > 70)
+ fit tf nf 1
+ 
+ section b 0.0 0.5 1.0 0.5
+ data jobs.run.dat
+ read t 6
+ set T = t*(t > 30) + (t+60)*(t<30)
+ create n 0 t[]
+ lim T n; box; plot T n
+ subset tf = T if (T > 70)
+ subset nf = n if (T > 70)
+ fit tf nf 1
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/manytasks.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/manytasks.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/manytasks.pro	(revision 22322)
@@ -0,0 +1,72 @@
+
+controller exit true
+controller host add kiawe
+
+$Ntest = 0
+pulse 1000
+controller pulse 1000
+
+macro load.machines
+  if ($0 != 2)
+    echo "load.machines (nmach)"
+    break
+  end
+
+  for i 0 $1
+    $n = $i + 1
+    sprintf host "po%02d" $n
+    controller host add $host
+  end
+end
+
+macro mktask
+  if ($0 != 2)
+    echo "USAGE: mktask (n)"
+    break
+  end
+
+  $N = $1
+
+  task test.$N
+    command partest
+    # polling period is no longer valid: we check for completed controller tasks
+    # correction: still valid for local tasks
+    periods -poll 0.2
+    periods -exec 0.01
+    periods -timeout 10.0
+    nmax 20
+    host anyhost
+
+    # stdout / stderr lines on named queues
+    task.exit 0
+      # echo "task exit 0"
+      queuedelete stdout
+      queuedelete stderr
+      date date
+      queuepush done "$date"
+      $Ntest ++
+    end
+
+    task.exit 1
+      # echo "task exit 1"
+      queuesize stdout
+      queuesize stderr
+    end
+
+    task.exit crash
+      echo "crashed job"
+    end
+
+    task.exit timeout
+      output timeout.log
+      echo $stdout
+      output stdout
+    end
+  end
+end
+
+macro mktasks
+ for i 0 $1
+  mktask $i
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/notes.txt	(revision 22322)
@@ -0,0 +1,18 @@
+
+This directory contains useful opihi / dvo / mana scripts
+
+Some possible scripts:
+
+module psphot
+
+  * load image, display, overlay the detections of several classes:
+    - psf stars
+    - good non-linear fit stars
+    - good linear fit stars
+    - good extended sources
+    - saturated stars
+    - defects
+    - failed fits
+    - ??
+
+  * load image, display, load psastro reference stars, display at multiple rotation angles / offsets?
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/psched.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/psched.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/psched.pro	(revision 22322)
@@ -0,0 +1,99 @@
+
+controller exit true
+# controller host add kiawe
+$Ntest = 0
+# controller host add alala
+# verbose on
+pulse 1000
+controller pulse 1000
+
+macro load.machines
+  if ($0 != 2)
+    echo "load.machines (nmach)"
+    break
+  end
+
+  for i 0 $1
+    $n = $i + 1
+    sprintf host "po%02d" $n
+    controller host add $host
+  end
+end
+
+task test
+  command partest
+  # polling period is no longer valid: we check for completed controller tasks
+  # correction: still valid for local tasks
+  periods -poll 0.20
+  periods -exec 0.0001
+  periods -timeout 10.0
+  nmax 1024
+#  nmax 100
+  host anyhost
+
+  # stdout / stderr lines on named queues
+  task.exit 0
+    # echo "task exit 0"
+    queuedelete stdout
+    queuedelete stderr
+    date date
+    queuepush done "$date"
+    $Ntest ++
+#   memory leaks
+#   queuesize stdout -var Nstdout
+#    for i 0 $Nstdout
+#      queuepop stdout -var line
+#      queuepush results "$line"
+#    end
+  end
+
+  task.exit 1
+    # echo "task exit 1"
+    queuesize stdout
+    queuesize stderr
+  end
+
+  task.exit crash
+    echo "crashed job"
+  end
+
+  task.exit timeout
+    output timeout.log
+    echo $stdout
+    output stdout
+  end
+end
+
+# pulse == 100ms
+# poll/exit = 0.2  : 29 sec / 100 jobs
+# poll/exit = 0.1  : 20 sec / 100 jobs
+# poll/exit = 0.05 : 17 sec / 100 jobs
+# poll/exit = 0.01 : 18 sec / 100 jobs
+
+# pulse == 10ms
+# poll/exit = 0.2  : 20 sec / 100 jobs
+# poll/exit = 0.10 : 12 sec / 100 jobs
+# poll/exit = 0.05 : 12 sec / 100 jobs
+# poll/exit = 0.01 :  9 sec / 100 jobs
+
+# we are limited here by how quickly we can send data to the 
+# controller.  this is limited by the occasional 'CheckSystem'
+# loops, with ~40ms minimum.
+
+# seems to be faster on po01 from kiawe (less interference?)
+
+# pulse == 1ms, controller pulse == 1ms
+# poll/exit = 0.01 :  3 sec / 100 jobs
+# 2 mach, 3 sec
+# 4 mach, 3 sec
+# 8 mach, 3 sec
+
+# 16 machines, 500 jobs, 13 sec: 26ms / job
+# 32 machines, 1024 jobs, 26 sec: 26ms / job
+# job harvesting rate is still the limitation.  Each job harvest requires:
+#  - jobstack exit
+#  - stdout
+#  - stderr
+#  - delete
+#  - jobstack crash
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/queuetests
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/queuetests	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/queuetests	(revision 22322)
@@ -0,0 +1,19 @@
+
+macro test.unique
+  list line -x "cat new.list"
+  for i 0 $line:n
+    push t1 "$line:$i"
+  end
+
+  queuelist
+  queuesize t1
+
+  list line -x "cat copy.list"
+  for i 0 $line:n
+    push t1 "$line:$i" -uniq
+  end
+
+  queuelist
+  queuesize t1
+
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/rdiff-elixir
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/rdiff-elixir	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/rdiff-elixir	(revision 22322)
@@ -0,0 +1,31 @@
+#!/bin/csh -f
+
+# this just organizes the tagging of the elixir components
+
+if ($#argv != 1) then
+  echo "USAGE: rdiff-elixir (TAG)"
+  exit 2
+endif
+
+cvs rdiff -r $1 opihi/Makefile
+cvs rdiff -r $1 opihi/bin/
+cvs rdiff -r $1 opihi/cmd.astro/
+cvs rdiff -r $1 opihi/cmd.basic/
+cvs rdiff -r $1 opihi/cmd.data/
+cvs rdiff -r $1 opihi/doc/
+cvs rdiff -r $1 opihi/dvo/
+cvs rdiff -r $1 opihi/help/
+cvs rdiff -r $1 opihi/include/
+cvs rdiff -r $1 opihi/lib/
+cvs rdiff -r $1 opihi/lib.data/
+cvs rdiff -r $1 opihi/lib.shell/
+cvs rdiff -r $1 opihi/mana/
+cvs rdiff -r $1 opihi/scripts/
+
+# don't place tag on these implementations
+# cvs tag $1 dimm/
+# cvs tag $1 dvo2/
+# cvs tag $1 old/
+# cvs tag $1 pclient/
+# cvs tag $1 pcontrol/
+# cvs tag $1 sched/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/run.pclient
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/run.pclient	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/run.pclient	(revision 22322)
@@ -0,0 +1,4 @@
+#!/bin/csh -f
+
+# pclient
+mana
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/sample.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/sample.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/sample.sh	(revision 22322)
@@ -0,0 +1,9 @@
+#!/usr/bin/env dvo
+
+echo "test"
+
+for i 0 $argv:n
+ echo $i $argv:$i
+end
+
+exit 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/sched.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/sched.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/sched.pro	(revision 22322)
@@ -0,0 +1,43 @@
+
+task test
+  command ls
+  periods -poll 1.0
+  periods -exec 2.0
+  periods -timeout 10.0
+  # trange 07:09 07:10
+  # trange -exclude 07:09:30 07:09:45
+  nmax 5
+  host local
+  # host localhost
+  # local is default 
+
+  task.exec
+    # echo "starting job sched.test"
+  end
+
+  # stdout / stderr lines on named queues
+  task.exit 0
+#    echo "task exit 0"
+#    queuesize stdout
+#    queuesize stderr
+    queuedelete stdout
+    queuedelete stderr
+    memory leaks
+  end
+
+  task.exit 1
+    echo "task exit 1"
+    queuesize stdout
+    queuesize stderr
+  end
+
+  task.exit crash
+    echo "crashed job"
+  end
+
+  task.exit timeout
+    output timeout.log
+    echo $stdout
+    output stdout
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/tag-opihi
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/tag-opihi	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/tag-opihi	(revision 22322)
@@ -0,0 +1,38 @@
+#!/bin/csh -f
+
+# this just organizes the tagging of the opihi components
+
+if ($#argv != 2) then
+  echo "USAGE: tag-elixir (MODULE) (TAG)"
+  echo "  MODULE choices: base mana dvo dimm pantasks pcontrol pclient"
+  exit 2
+endif
+
+if ("$1" == "base") then
+  cvs tag $2 Makefile
+  cvs tag $2 Makefile.Common
+  cvs tag $2 include/
+  cvs tag $2 test/
+  cvs tag $2 lib.data/
+  cvs tag $2 lib.shell/
+  cvs tag $2 cmd.astro/
+  cvs tag $2 cmd.basic/
+  cvs tag $2 cmd.data/
+  cvs tag $2 scripts/
+  cvs tag $2 doc/
+  exit 0;
+endif
+
+if ("$1" == "mana") goto valid;
+if ("$1" == "dvo") goto valid;
+if ("$1" == "dimm") goto valid;
+if ("$1" == "pantasks") goto valid;
+if ("$1" == "pclient") goto valid;
+if ("$1" == "pcontrol") goto valid;
+echo "invalid opihi module $1"
+exit 1;
+
+valid:
+  # tag the module ($1) with the specified tag ($2)
+  cvs tag $2 $1
+  exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/test.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/test.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/test.pro	(revision 22322)
@@ -0,0 +1,144 @@
+
+macro testgauss
+ create x -20 20 0.1
+ set dy = zero(x) + 0.03
+ set X = x - $1
+ set y = $3*exp(-0.5*(X^2/$2^2)) + $4 + 0.1*(rnd(X) - 0.5)
+# set y = $3*exp(-0.5*(X^2/$2^2)) + $4
+ lim x y; clear; box; plot x y -dy dy -x 2 -pt 2
+
+ $C0 = $1+5
+ $C1 = $2-10
+ $C2 = $3
+ $C3 = $4
+
+ vgauss x y dy yf
+end
+
+macro testpoor
+ create x -20 20 0.1
+ set dy = zero(x) + 0.03
+ set X = x - $1
+ set y = 3 + zero(x) + 0.1*(rnd(X) - 0.5)
+# set y = (x + 20)/10 + $4 + 0.1*(rnd(X) - 0.5)
+# set y = $3*exp(-0.5*(X^2/$2^2)) + $4
+ lim x y; clear; box; plot x y -dy dy -x 2 -pt 2
+
+ $C0 = $1
+ $C1 = $2
+ $C2 = $3
+ $C3 = $4
+
+ vgauss x y dy yf
+end
+
+macro testfit
+ create x 0 1000
+ set y = 3 + 5*x
+ set dy = 10*(rnd(x) - 0.5)
+ set yr = y + dy
+ fit x yr 1
+ echo $Cn, $Cnv
+ echo $dC
+
+ yr[100] = yr[100] + 50
+ yr[200] = yr[200] - 50
+ yr[300] = yr[300] + 100
+ yr[400] = yr[400] - 500
+ yr[500] = yr[500] + 30
+
+ fit x yr 1
+ echo $Cn, $Cnv
+ echo $dC
+
+ fit x yr 1 -clip 3 3
+ echo $Cn, $Cnv
+ echo $dC
+end
+# expected output:
+# y = 3.013781 x^0 5.000005 x^1
+#     0.063198     0.000110
+# 1, 1000
+# 2.91071543189
+# y = 2.495040 x^0 5.000303 x^1
+#     0.063198     0.000110
+# 1, 1000
+# 16.5928919554
+# y = 3.021974 x^0 4.999991 x^1
+#     0.063419     0.000110
+# 1, 995
+# 2.90820510001
+
+macro testloops
+ for i 0 100
+  if ($i < 20)
+   continue
+  end
+  if ($i > 40)
+   break
+  end
+  echo $i
+ end
+end  
+
+# test math parsing
+macro testvars
+ echo testing variable assignment
+ $a = 1
+ echo $a is 1
+ $b = 5*3 + $a
+ echo $b is 16
+
+ $a = `ls`
+ echo $a
+ exec ls
+
+ echo testing in-line math
+ $a = 1
+ echo {1 + 1} is 2
+ echo {2^3} is 8
+ echo {$a + 2} is 3
+
+ echo testing vector assignment
+ create x 0 10
+ set y = x^2
+ echo x[2] is 2
+ echo y[2] is 4
+ lim x y; clear; box; plot x y
+ label -x "x axis" -y "&s y axis"
+end
+
+macro testspeed
+ exec date
+ create x 0 100000
+ for i 0 5000
+  set y = 3*x + $i
+ end
+ exec date
+end
+
+macro testmemory.buffers
+ mcreate a 2048 2048
+ exec date
+ for i 0 500
+  set b = 3*a + 6
+ end
+ exec date
+end
+
+macro testmemory.macros
+ exec date
+ for i 0 1000
+   for j 0 1000
+    $N = $N + 1
+   end
+ end
+ exec date
+end
+
+macro sample
+ $N = 0
+ for j 0 1000000
+  $N = 100
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/testloops.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/testloops.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/testloops.pro	(revision 22322)
@@ -0,0 +1,90 @@
+
+macro testloop1
+ local i
+ for i 0 10
+  echo "1: $i"
+    end
+end
+
+macro testloop2
+ local i
+ for i 0 5
+   echo "2: $i"
+   testloop1
+end
+end
+
+macro testlocal1
+ local -static foo
+ echo "1 - foo: $foo"
+ $foo = local.1
+ echo "1 - foo: $foo"
+end
+
+macro testlocal2
+ local foo
+ $foo = local.2
+ echo "2 - foo: $foo"
+ testlocal1
+ echo "2 - foo: $foo"
+ testlocal1
+ echo "2 - foo: $foo"
+end
+
+macro test1
+ # echo step $1
+ if ($1 == 1)
+  echo "break here"
+  break
+ end
+ if ($1 == 2)
+  continue
+ end
+end
+
+# continue
+
+macro test2
+ echo "input test"
+ break
+ echo "error, can't get here"
+end
+
+macro testm
+ break -auto off
+ test1 0
+ # echo $STATUS
+ test1 1
+ # echo $STATUS
+ test1 2
+ # echo $STATUS
+ if ($1 == 10) 
+  test1 1
+  if ($STATUS == 0)
+   echo "breaking it"
+   break
+  end
+ end
+ echo done
+end
+
+echo "done loading"
+
+macro test3
+ for i 0 12
+  echo $i
+  testm $i
+  echo "status: $STATUS"
+ end
+end
+
+macro testL
+ for i 0 10
+  for j 0 10
+   for k 0 10
+    echo $i $j $k
+    mcreate a 1024 1024   
+   end
+  end
+ end
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/testscript
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/testscript	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/scripts/testscript	(revision 22322)
@@ -0,0 +1,16 @@
+#!/bin/csh -f
+
+date
+
+ls -d *
+# sleep 3
+ls foo
+
+foreach f (*)
+  echo $f
+  ls $f/*
+#  sleep 1
+end
+
+date
+#exit 3
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/basic.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/basic.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/basic.sh	(revision 22322)
@@ -0,0 +1,9 @@
+
+list testdir
+  cmd.data/test
+  cmd.basic/test
+end
+
+input test/tests.sh
+
+fulltests
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/pclient.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/pclient.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/pclient.sh	(revision 22322)
@@ -0,0 +1,8 @@
+
+list testdir
+  pclient/test
+end
+
+input test/tests.sh
+
+fulltests
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/strings.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/strings.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/strings.pro	(revision 22322)
@@ -0,0 +1,32 @@
+
+macro test1
+ $test1 = 01234567890.01234567890.1234567890
+ $test2 = $test1 + $test1
+ $test3 = $test2 and $test2
+ $test4 = $test3 or $test3
+ $test5 = $test4 with $test4
+
+ echo strlen test1; strlen "$test1" 
+ echo strlen test2; strlen "$test2"
+ echo strlen test3; strlen "$test3"
+ echo strlen test4; strlen "$test4"
+ echo strlen test5; strlen "$test5"
+end
+
+macro test2
+ $test1 = 01234567890.01234567890.1234567890
+ appendup test1 test2
+ appendup test2 test3
+ appendup test3 test4
+ appendup test4 test5
+
+ echo strlen test1; strlen "$test1" 
+ echo strlen test2; strlen "$test2"
+ echo strlen test3; strlen "$test3"
+ echo strlen test4; strlen "$test4"
+ echo strlen test5; strlen "$test5"
+end
+ 
+macro appendup
+  $$2 = $$1 and $$1
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/tests.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/tests.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/opihi/test/tests.sh	(revision 22322)
@@ -0,0 +1,100 @@
+
+if ($?VERBOSE == 0)
+ $VERBOSE = 0
+end
+
+$failtest:n = 0
+$failfile:n = 0
+$faildirs:n = 0
+$currentdir = .
+
+macro fulltests
+  $Npass = 0
+  $Nfail = 0
+  $Ntest = 0
+  break -auto off
+
+  $failtest:n = 0
+  $failfile:n = 0
+  $faildirs:n = 0
+
+  for Ti 0 $testdir:n
+    if ($VERBOSE > 0)
+       echo "directory $testdir:$Ti"
+    end
+    runtestdir $testdir:$Ti
+  end
+
+  echo "completed $Ntest tests"
+  echo "$Npass tests passed"
+  echo "$Nfail tests failed"
+  echo "examined $testdir:n directories"
+
+  if ($Nfail > 0) 
+    echo " ** failed tests **"
+    for i 0 $Nfail
+      echo $faildirs:$i $failfile:$i $failtest:$i
+    end
+  end
+end
+
+macro runtestdir
+  if ($0 != 2)
+    echo "USAGE: runtestdir (testdir)"
+    break -auto on
+    break
+  end
+  
+  pwd -var startdir
+  cd -q $1
+  list testscripts -x "ls *.sh"
+
+  $currentdir = $1
+  for Tj 0 $testscripts:n
+    if ($VERBOSE > 1)
+      echo " running $testscripts:$Tj"
+    end
+    runtests $testscripts:$Tj      
+  end
+  
+  cd -q $startdir
+end
+
+macro runtests
+  if ($0 != 2)
+    echo "USAGE: runtests (script.sh)"
+    break -auto on
+    break
+  end
+  
+  local Tk
+
+  input $1
+  for Tk 0 $tests:n
+    if ($VERBOSE > 2)
+      echo "   running $tests:$Tk"
+    end
+    $PASS = 1
+    $tests:$Tk
+    if ($PASS == 0)
+      echo "   ** failed $tests:$Tk $1"
+      $Nfail ++
+      $n = $failtest:n
+      $failtest:$n = $tests:$Tk
+      $failfile:$n = $1
+      $faildirs:$n = $currentdir
+      $failtest:n ++
+      $failfile:n ++
+      $faildirs:n ++
+    else
+      $Npass ++
+    end
+    $Ntest ++
+    # echo "finished $tests:$Tk"
+  end
+end
+
+echo "USAGE:"
+echo "runtests (script.sh)"
+echo "runtestdir (testdir)"
+echo "set VERBOSE to 0 through 3"
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/.status
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/.status	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/.status	(revision 22322)
@@ -0,0 +1,60 @@
+elixir status
+elixir flips
+elixir ptolemy
+nav
+nav
+!radec -hms
+region 168.148 6.9777 1.0
+catalog -g -m 14 20
+catalog -g -all -m 14 20
+style -pt 1
+region 168.148 6.9777 1.0
+catalog -g -all -m 14 20
+region 168.148 6.9777 0.3
+region 168.148 6.9777 1.0
+region 168.148 6.9777 0.3
+catalog -g -all -m 14 20
+catalog -g -all -m 10 15
+region 168.148 6.9777 0.3
+catalog -g -all -m 10 15
+nav
+pmeasure -all -m 16 22
+nav
+pmeasure -all -m 16 22
+style -pt 2
+c
+elixir flips
+elixir ptolemy
+elixir flips
+elixir complete
+elixir complete -time
+elixir complete -time
+na
+region
+region 30 0 90 ait
+style -c red; cgrid
+style -c black; images
+box
+region 30 0 90 ait
+style -c red; cgrid
+region 40 0 90 ait
+style -c red; cgrid
+style -c black; images
+clear
+style -c red; cgrid
+style -c black; images
+jpeg -g -name /h/eugene/spplots/allsky.png
+elixir complete
+elixir complete
+elixir complete -time
+elixir complete -time
+elixir complete -time
+elixir complete
+elixir complete
+echo {2500*60/11}
+echo {2500*60/11/3600}
+elixir complte
+elixir complete
+elixir complete -time
+elixir complete
+elixir complete -kill
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/Makefile	(revision 22322)
@@ -0,0 +1,60 @@
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/perl
+
+perl: default
+
+default: install
+
+DADS = \
+dads.detrend dads.jpeg dads.keywords grab.keywords
+
+DETREND = \
+darktime.cfh12k darktime.linear \
+demodemap flatten.mana flatten.flips defringe getfringe defringe.ccd
+
+GENERAL = \
+cfht.names checkconfig mkrun megacenter megacoords \
+update.astrometry.iraf wfi.keywords mefheadunstrip cadc2qso \
+mkhtml mkidx
+
+IMSTATS = \
+imstatqso imstats \
+irstats seeingstats
+
+MKDETREND = \
+create.subset dt.select fixscat fixscat.mef mkscat \
+mkdetrend mosaic.merge mosaic.stats \
+normalize split.mef \
+detflips merge.lists
+
+MKFRINGE = \
+fr.mkmodes fr.modesave fr.modestats \
+fr.ckfile fr.detrend fr.frstats \
+fr.getfringe fr.imdata fr.jpegs \
+fr.medbin.spline fr.medbin.interp fr.mkhtml fr.mklists \
+fr.select mkfringe fr.defringe fr.smooth
+
+POSTRUN = \
+ckastrom ckdetrend ckphotom \
+ckrunid ckvalid elixir.photreport \
+elixir.postrun mktrans mktreport \
+validate rerun.complete elixir.rerun
+
+PTOLEMY = \
+cmp.header imclean.cfht imclean.subaru gastro.raw elixir.export
+
+REALTIME = \
+ckimregdb elixir.link elixir.reload
+
+CODE = $(DADS) $(DETREND) $(GENERAL) $(IMSTATS) $(MKDETREND) $(MKFRINGE) $(POSTRUN) $(PTOLEMY) $(REALTIME)
+
+install:
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	for file in $(CODE); do \
+	(chmod +x src/$$file && cd $(DESTBIN) && rm -f $$file && ln -s $(HOME)/src/$$file .); \
+	done
+
+# utilities #################################################
+clean:	
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,13 @@
+
+perl-1-4: 
+  - dropped mkdist (moved to separate top-level module)
+
+perl-1-3:
+  - added mkdist, mkhtml, mkidx for ohana support
+
+perl-1-2:
+  - modified elixir.postrun to use unified addstar (not addref)
+
+perl-1-1:
+  - fixed /usr/bin/perl references everywhere *except* mkdetrend and
+    mkfringe, which must have the path explicitly defined.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/keywords.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/keywords.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/keywords.txt	(revision 22322)
@@ -0,0 +1,130 @@
+
+Elixir data products are modified by several programs, each of which
+needs to make certain additions or changes to the headers.  this is a
+summary of those programs and the relevant keywords.  These programs
+may also add COMMENT and HISTORY fields, but these are ignored.
+
+* basic master bias, flat, dark frames are created by 'imcombred'
+  and adjusted by 'normalize'.
+
+- 'imcombred' adds the following keywords:
+
+IMCMB_DT= 'FLIPS ver 2.0 - Elixir by CFHT - Tue Aug 12 2003 -  9:09:32' / Date 
+IMCMB_AL= 'SIGMA   '           / CCD or SIGMA clipping, Scaled or Weighted Diff
+IMCMB_CA= 'MEAN    '           / Combining algorithm on final stack            
+IMCMB_LS=                  3.5 / Lower threshold for clipping rejection        
+IMCMB_HS=                  3.5 / Lower threshold for clipping rejection        
+IMCMB_FT= 'MASTER_DETREND_SKYFLAT' / Stacked frame type                        
+IMCMB_OP= '1DYMODEL_OVERSCAN + COMPUTED_MODE_RESCALING' / Input operations     
+IMCMB_NI=                   16 / Number of input files                         
+IMCMB_DA=                    T / Dual amplifier A,B (IMCMB_XX list format)     
+IMCMB_IF= 'NAME BIAS_A MODE_A BIAS_B MODE_B' / Input file parameters           
+IMCMB_nn= '712143f.fits[ccd00] 1287 3204 1232 3266' / Input file stats         
+
+- 'normalize' adds the following keywords:
+
+FLATSCAL=         1.0642668848 /                                               
+TVSTART = '2000/1/1          ' / data validity start time
+TVSTOP  = '2001/1/1          ' / data validity start time
+ELCONF  = '03Bm01.flat.g.0   ' / Original Elixir config
+EL_SYS  = '2.0               ' / Elixir System Version
+EL_NRM  = '1.4               ' / Elixir Normalize Version
+REL_DATE= '2003-09-30T15:59:33' / UTC Release Date
+
+* scatter-corrected flats are created by 'fixscat'
+- 'fixscat' adds the following keywords:
+
+EL_SFILE= '02Bm05.scatter.g.36.00.fits' / Scattered Light Image Used
+EL_SVER = 'B.0               ' / Scatter Correction Version
+
+* scatter frames are created by 'mkscat'
+- 'mkscat' adds the following keywords:
+
+TVSTART = '2000/1/1          ' / data validity start time
+TVSTOP  = '2001/1/1          ' / data validity start time
+FILTER  = 'g                 ' / filter uxed for model
+EL_SYS  = '2.0               ' / Elixir System Version
+EL_SVER = 'B.0               ' / Scatter Correction Version
+VERSION = 'B.0               ' / Scatter Correction Version
+
+* master fringe frames are created by 'imcombred' and adjusted by
+  'fr.smooth'
+
+- imcombred adds the following keywords:
+
+IMCMB_DT= 'FLIPS ver 2.1 - Elixir by CFHT - Tue Sep 16 2003 - 14:00:18' / Date 
+IMCMB_AL= 'SIGMA   '           / CCD or SIGMA clipping, Scaled or Weighted Diff
+IMCMB_CA= 'MEDIAN  '           / Combining algorithm on final stack            
+IMCMB_LS=                  2.0 / Lower threshold for clipping rejection        
+IMCMB_HS=                  2.0 / Lower threshold for clipping rejection        
+IMCMB_FT= 'MASTER_DETREND_FRINGE' / Stacked frame type                         
+IMCMB_OP= 'MODE_OFFSETING + FRINGE_RESCALING' / Input operations               
+IMCMB_NI=                   31 / Number of input files                         
+IMCMB_DA=                    F / Dual amplifier A,B (IMCMB_XX list format)     
+IMCMB_IF= 'NAME BIAS MODE SCALING' / Input file parameters                     
+IMCMB_00= '714505o00.fits 0 2582 0.441' / Input file stats                     
+
+- fr.smooth adds the following keywords:
+
+FRNG_C0 =         0.0000000000 / Fringe Cross Correlation linear fit term
+FRNG_C1 =         0.7197280000 / Fringe Cross Correlation linear fit term
+FRNG_SKY=      2882.0000000000 / Fringe Frame sky level
+FRNG_RNG=       -80.0000000000 / Fringe Frame fringe strength
+FRNG_REF= 'ccd13             ' / Fringe Cross Correlation reference chip
+FRNG_PTS= '2003A.frpts.i.36.00.fits' / Fringe Point File
+TVSTART = '2000/1/1          ' / data validity start time
+TVSTOP  = '2001/1/1          ' / data validity stop time
+EL_SYS  = '2.0               ' / Elixir System Version
+
+
+* processed science images are created by 'imred', defringed with
+  'imcombred', and keywords are updated with 'grab.keywords'
+
+- imred adds the following keywords:
+
+IMRED_DT= 'FLIPS ver 2.1 - Elixir by CFHT - Tue Sep 9 2003 - 12:29:37' / Date  
+IMRED_IF= '703406o.fits[ccd00]' / Input file to process by imred               
+IMRED_OF= '703406p00.fits'     / Output file resulting from the process by imre
+IMRED_OP= 'MASK_BP + OVERSCAN + BIAS + DARK_CURRENT + FLAT-FIELD' / Operation(s
+IMRED_MK= '2003A.mask.0.36.00.fits[ccd00]' / Mask of bad pixels (0/1) input fil
+IMRED_OV= '1DYMODEL_OVERSCAN'  / Type of overscan level correction             
+IMRED_BS= '03Am04n.bias.0.18.00.fits[ccd00]' / Bias frame input file           
+IMRED_DK= '03Am04n.dark.300.18.00.fits[ccd00]' / Dark frame input file         
+IMRED_DS=               1.0000 / Dark frame scaling factor                     
+IMRED_DB=                    T / Dark frame first corrected by bias frame      
+IMRED_FF= '03Am04n.flat.r.18.01.fits[ccd00]' / Flat-field frame input file     
+IMRED_NF=                    T / Normalized flat-field                         
+IMRED_SF=          1.009331465 / Output file dynamic stretching factor (BSCALe)
+
+- imcombred adds the following keywords:
+
+IMCMB_DT= 'FLIPS ver 2.1 - Elixir by CFHT - Tue Sep 9 2003 - 12:16:00' / Date  
+IMCMB_AL= 'SD      '           / CCD or SIGMA clipping, Scaled or Weighted Diff
+IMCMB_CA= 'SCALED_DIFFERENCE'  / Combining algorithm on final stack            
+IMCMB_LS=                  2.0 / Lower threshold for clipping rejection        
+IMCMB_HS=                  2.0 / Lower threshold for clipping rejection        
+IMCMB_FT= 'DEFRINGE_SCIENCE'   / Stacked frame type                            
+IMCMB_OP= 'MODE_OFFSETING + FRINGE_RESCALING' / Input operations               
+IMCMB_NI=                    2 / Number of input files                         
+IMCMB_DA=                    F / Dual amplifier A,B (IMCMB_XX list format)     
+IMCMB_IF= 'NAME BIAS MODE SCALING' / Input file parameters                     
+IMCMB_00= '703266p00.det 0 3808 1.000' / Input file stats                      
+IMCMB_01= '03Am04n.fringe.i.18.00.fits[ccd00] 0 4062 1.108' / Input file stats 
+
+- grab.keywords adds the following keywords:
+
+NASTRO  =                   49 / number of stars used for astrometry           
+CERROR  =         0.7334717934 / scatter in astrometry soln (arcsec)           
+PHOT_C  =               25.715 / Nightly zero point - actual                   
+PHOT_C0 =               25.743 / Nightly zero point - nominal                  
+PHOT_CS =                0.017 / Nightly zero point - scatter                  
+PHOT_NS =                    3 / Nightly zero point - N stars                  
+PHOT_NM =                    3 / Nightly zero point - N images                 
+PHOT_X  =                0.083 / Nightly zero point - color term               
+PHOT_K  =                -0.04 / Nightly zero point - airmass term             
+PHOT_C1 = 'r_SDSS            ' / Nightly zero point - color 1                  
+PHOT_C2 = 'i_SDSS            ' / Nightly zero point - color 2                  
+MAG_SAT =              13.8063 / approx saturation (mag)                       
+MAG_LIM =              23.2063 / approx completeness limit (mag)               
+DATEPROC= '2003-09-30T17:14:50' / Processing Date                                      
+EL_SYS  = '2.0                ' / Elixir System Version                               
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/mkdetrend-notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/mkdetrend-notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/mkdetrend-notes.txt	(revision 22322)
@@ -0,0 +1,46 @@
+
+files used by mkdetrend:
+
+flips/02Bk06.flat.I.00.0.fits     - master image, unnormalized
+flips/02Bk06.flat.I.00.0.imbin	  - binned master image
+flips/02Bk06.flat.I.00.0.log	  - log
+flips/02Bk06.flat.I.00.0.master	  - list of input images
+flips/02Bk06.flat.I.00.0.medbin	  - binned residual stack (50)
+flips/02Bk06.flat.I.00.0.mef	  - list of mef images
+flips/02Bk06.flat.I.00.0.msplit	  - list of split mef images
+flips/02Bk06.flat.I.00.0.norm	  - master image, normalized
+flips/02Bk06.flat.I.00.0.split	  - list of split images
+flips/02Bk06.flat.I.00.0.stats	  - residual stats per input image
+flips/02Bk06.flat.I.00.0.subset	  - list of images to be stacked
+flips/02Bk06.flat.I.00.0.tenbin	  - binned residual stack (10)
+
+changes:
+
+dt.select2: 
+mkdetrend2
+mode.mkdetrend2
+cfh12k.config.mef
+
+
+chip ID rules:
+
+there are two chip identifiers:  CCD ID & CCD N
+
+CCD ID is an arbitrary string
+CCD N  is a two digit code for the CCDs base on the camera config file
+
+CCD ID: cameraconfig -ccds
+CCD N:  cameraconfig -ccdn
+
+detsearch / imsearch use CCD ID for -ccd filter
+
+filenames of derivative products should use CCD N as a marker
+eg: 654321o.fits -> 654321o23.fits (for megacam ccd23 image)
+
+---
+
+dt.select2    : CCD ID fixed
+merge.lists   : CCD n/a
+create.subset : CCD n/a
+detflips      : CCD n/a - flips path needs to be set?
+normalize     : CCD ID Keyword used to id REF ccd (OK)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/mkfringe.notes
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/mkfringe.notes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/mkfringe.notes	(revision 22322)
@@ -0,0 +1,109 @@
+
+mkfringe file name examples
+
+CONFIG      = 01Ak01.fringe.I.0
+CONF-CCD    = 01Ak01.fringe.I.00.0
+RAWPATH     = /data/koa/cfh12k/01Ak04
+TMPPATH     = /data/elixir2/cfh12k/detrend/01Ak04/proc
+TMPDET-CCD  = /data/elixir2/cfh12k/detrend/01Ak04/proc/543210o/543210o00.fits
+TMPDEF-CCD  = /data/elixir2/cfh12k/detrend/01Ak04/proc/543210o.def/543210o00.fits
+
+ROOT = 543210o
+MODE = SPLIT / MEF
+status = 2 = new image, detrend it please, 1 = use to create master, 0 = don't use
+
+CONFIG.mef, CONFIG.split   [Nimage]
+ RAWPATH ROOT MODE
+ /data/koa/cfh12k/01Ak04 581688o SPLIT
+ /data/koa/cfh12k/01Ak04 581689o MEF
+
+CONFIG.master              [Nimage]
+ RAWPATH ROOT MODE status
+ /data/koa/cfh12k/01Ak04 581688o SPLIT 1
+ /data/koa/cfh12k/01Ak04 581689o MEF   1
+ 
+CONFIG.detrend             [Nimage] 
+ RAWPATH ROOT MODE
+ /data/koa/cfh12k/01Ak04 581688o SPLIT
+ /data/koa/cfh12k/01Ak04 581689o MEF
+
+CONFIG.defringe		   [Nimage]
+ TMPPATH/ROOT CONFIG
+ proc/581688o 01Ak01.fringe.I.0
+
+TMPPATH/ROOT.detlist       [Nccd]
+ TMPDET-CCD
+ proc/581688o/581688o00.fits
+
+TMPPATH/ROOT.deflist       [Nccd]
+ TMPDEF-CCD
+ proc/581688o.def/581688o00.fits
+
+CONF-CCD.ccdstats          [Nimage]
+ TMPDET-CCD (sky) (fringe) 0.0 0.0
+ proc/581688o/581688o00.fits (sky) (fringe) 0.0 0.0
+
+CONFIG.statlist: 
+ CONF-CCD.ccdstats 
+ fringe/01Ak01.fringe.I.00.0.ccdstats
+
+CONFIG.frlist               [Nccd]
+ CONF-CCD.fr.fits 
+ fringe/01Ak01.fringe.I.00.0.fr.fits
+
+CONFIG.smlist               [Nccd]
+ CONF-CCD.sm.fits 
+ fringe/01Ak01.fringe.I.00.0.sm.fits
+
+CONFIG.medbin:
+ HTML/CONFIG.medbin.NNN.jpg
+
+CONFIG.tenbin:
+ HTML/CONFIG.tenbin.NNN.jpg
+
+CONFIG.stats
+ (stats per mosaic)
+
+###### init: (takes CONFIG as input list) (mode.fringe1)
+fr.select I 2001/1/20 2001/2/5 split fringe/01Ak01.fringe.I.0.split
+fr.select I 2001/1/20 2001/2/5 mef   fringe/01Ak01.fringe.I.0.mef
+
+fr.mklists -initial  fringe/01Ak01.fringe.I.0.split fringe/01Ak01.fringe.I.0.mef fringe/01Ak01.fringe.I.0.master
+
+fr.mklists -imlist fringe/01Ak01.fringe.I.CCDNUM.0.ccdstats fringe/01Ak01.fringe.I.0.statlist
+fr.mklists -imlist fringe/01Ak01.fringe.I.CCDNUM.0.fr.fits  fringe/01Ak01.fringe.I.0.frlist
+fr.mklists -imlist fringe/01Ak01.fringe.I.CCDNUM.0.sm.fits  fringe/01Ak01.fringe.I.0.smlist
+
+fr.mklists -mosaic fringe/01Ak01.fringe.I.0.master proc det  (produces proc/ROOT.det/*NN.fits & proc/ROOT.detlist)
+fr.mklists -mosaic fringe/01Ak01.fringe.I.0.master proc def  (produces proc/ROOT.def/*NN.fits & proc/ROOT.deflist)
+fr.mklists -mosaic fringe/01Ak01.fringe.I.0.master proc med  (produces proc/ROOT.med/*NN.fits & proc/ROOT.medlist)
+
+fr.mklists -detrend  fringe/01Ak01.fringe.I.0.master fringe/01Ak01.fringe.I.0.detrend
+fr.mklists -defringe fringe/01Ak01.fringe.I.0.master fringe/01Ak01.fringe.I.0 proc fringe/01Ak01.fringe.I.0.defringe
+
+##### detrend: (takes CONFIG.detrend as input list)  (mode.fringe2)
+fr.detrend /data/koa/cfh12k/01Ak04 581688o SPLIT proc/581688o.det
+fr.medbin  proc/581688o.det 581688o 4 proc/581688o.med
+fr.gtstats proc/581688o.med 581688o 4 proc/581688o.imstats
+
+##### mkstats  (takes CONF-CCD as input list)  (mode.fringe4)
+fr.mklists -inlist  fringe/01Ak01.fringe.I.0.master 00 proc fringe/01Ak01.fringe.I.00.0.inlist
+fr.mklists -imstats fringe/01Ak01.fringe.I.0.master 00 proc fringe/01Ak01.fringe.I.0.imstats
+
+fr.mkstats fringe/01Ak01.fringe.I.00.0.inlist fringe/01Ak01.fringe.I.0.imstats 00 fringe/01Ak01.fringe.I.00.0.ccdstats fringe/01Ak01.fringe.I.0.frstats
+fr.mkrough fringe/01Ak01.fringe.I.00.0.ccdstats fringe/01Ak01.fringe.I.00.0.fr.fits comment
+
+[ fr.smooth fringe/01Ak01.fringe.I.00.0.fr.fits fringe/01Ak01.fringe.I.00.0.sm.fits ]
+
+##### defringe (takes CONFIG.defringe as input)  (mode.fringe3)
+fr.defringe proc/581688o.medlist proc/581688o.imstats fringe/01Ak01.fringe.I.00.0.frlist fringe/01Ak01.fringe.I.00.0.frstats proc/581688o.deflist 
+fr.stats proc/581688o.deflist proc/581688o.tenbin proc/581688o.medbin proc/581688o.stats
+
+# merge (not parallel)
+
+status: 2 = new image, detrend it please, 1 = use to create master, 0 = don't use
+
+fr.mklists -master fringe/01Ak01.fringe.I.0.master fringe/01Ak01.fringe.I.0.detrend
+
+[merge ccdstats > statlist]
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/mkfringe.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/mkfringe.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/doc/mkfringe.txt	(revision 22322)
@@ -0,0 +1,59 @@
+
+mkfringe states & programs [fringe only]
+
+running.init              : mode.fringe1 (config.mosaic)
+
+ - fr.select
+ - fr.mklists -initial
+ - fr.mklists -imlist
+ - fr.mklists -mosaic
+ - fr.mklists -detrend
+ - fr.mklists -maplist
+ - fr.mklists -mlist
+
+running.detrend           : mode.fringe2 (image)
+
+ - fr.ckfile
+ - fr.detrend
+ - fr.medbin.spline
+ - fr.getfringe
+ - fr.imdata
+ - fr.jpegs
+ - fr.mklists -master
+
+running.merge		  : mkfringe (config.mosaic)
+
+ - fr.mklists -subset
+ - fr.frstats -fitpars
+ - fr.frstats -plotstats
+ - fr.frstats -frstats
+ - fr.frstats -dfstats
+ - fr.mklists -imstats
+ - fr.jpegs
+ - fr.mkhtml -imstats
+ - fr.mkhtml -jpglist
+
+running.mkrough           : mkfringe / mode.fringe4 
+
+ - fr.mklists  -subset
+ - fr.frstats  -fitpars
+ - fr.frstats  -frstats
+ - fr.frstats  -dfstats
+ - fr.mklists  -imstats
+ - fr.mklists  -ccdstast
+ - fr.mkrough
+
+running.defringe          : mode.fringe3
+
+ - fr.defringe
+ - fr.getfringe
+ - fr.jpegs
+
+running.smooth            : mode.fringe4
+
+ - fr.smooth
+
+running.reg               : mkfringe
+
+ - detregister
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/archive
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/archive	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/archive	(revision 22322)
@@ -0,0 +1,353 @@
+#!/usr/bin/perl
+
+$dumpspace = "/pallas/d1/";
+$workspace = "/metis/d27/workspace/";
+# $workspace = "/irene/d11/workspace/";
+
+$scriptdir = "/hebe/d27/logs/";
+$donelogfile = $scriptdir . "done.log";
+$scriptfile = $scriptdir . "maestro.dat";
+$flatdir = "/hebe/d27/references/config/";
+
+$database = "/hebe/d27/database/";
+$lockfile = $database . "lock";
+$alockfile = $database . "alock";
+$clockfile = $database . "clock";
+$killfile  = $database . "kill";
+$haltfile  = $database . "halt";
+$reffile = $database . "start.clean";
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    vsystem ("date");
+    vsystem ("notify.pl $ARGV[0] none 0");
+    die "@_";
+}
+
+if (@ARGV < 1) {die "USAGE: archive.pl Noutfile\n"; }
+$Noutfile = $ARGV[0] + 1;
+$outfile = "archive" . $Noutfile . ".log";
+
+print STDERR "starting execution\n";
+vsystem ("date");
+if (-e $killfile) { goodbye "process killed with $killfile"; }
+
+
+if (-e $alockfile) { goodbye "archive lock $alockfile is set"; }
+open (TEMP, ">$alockfile");
+close (TEMP);
+
+# load in done date list
+open (DONELOG, $donelogfile);
+@donelist = ();
+while ($line = <DONELOG>) {
+  chop ($line);
+  @donelist = (@donelist,$line);
+}
+$Ndone = @donelist;
+close (DONELOG);
+
+# load in maestro list
+open (MAESTRO, $scriptfile);
+@todolist = ();
+while ($line = <MAESTRO>) {
+  chop ($line);
+  @todolist = (@todolist,$line);
+}
+$Ntodo = @todolist;
+close (MAESTRO);
+
+# find entries in MAESTRO that are NOT in DONELOG
+@leftlist = ();
+for ($i = 0; $i < $Ntodo; $i ++) {
+    $gotit = 0;
+    for ($j = 0; ($j < $Ndone) && ! $gotit; $j ++) {
+	@entries = split (" ",$todolist[$i]);
+	if ($entries[0] eq $donelist[$j]) {
+	    $gotit = 1;
+	}
+    }
+    if (!$gotit) {
+	@leftlist = (@leftlist, $todolist[$i]);  
+    }
+}
+$Nleft = @leftlist;
+
+if (-e $killfile) { goodbye "process killed with $killfile"; }
+# run controller on unfinished entries
+# we are going to run this in the script directory
+if ($Nleft) {
+    chdir $scriptdir;
+    for ($i = 0; $i < $Nleft; $i++) {
+	print STDERR "unfinished: $leftlist[$i]\n";
+	@entries = split (" ",$leftlist[$i]);
+	$inlist = $entries[0] . ".inlist";
+	$outlist = $entries[0] . ".outlist";
+	$subdir = $entries[0];
+	$photcode = $entries[1];
+	print STDERR "$subdir: $inlist $outlist $photcode\n";
+	if (-e $outlist) {
+	    # partially successful run, restart controller in the middle
+	    $tmp_inlist = $entries[0] . ".tmp_inlist";
+	    $tmpoutlist = $entries[0] . ".tmpoutlist";
+	    if (-e $tmpoutlist) { if (vsystem ("cat $tmpoutlist >> $outlist")) { goodbye "trouble merging $tmpoutlist and $outlist\n"; }}
+	    # partially successful re-start: make sure $outlist is complete
+	    if (parse_outlist ($outlist,$tmp_inlist,1)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s flatten", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    if (parse_outlist ($outlist,$tmp_inlist,2)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s dophot", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    if (parse_outlist ($outlist,$tmp_inlist,3)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s fstat", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    if (parse_outlist ($outlist,$tmp_inlist,4)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s gastro", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    if (parse_outlist ($outlist,$tmp_inlist,5)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s addstar", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    # do inlist entries not in outlist
+	    if (parse_inlist ($inlist,$outlist,$tmp_inlist)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s flatten", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    unless (unlink ($tmpoutlist)) { goodbye "can't unlink $tmpoutlist, why not?"; }
+	    unless (unlink ($tmp_inlist)) { goodbye "can't unlink $tmp_inlist, why not?"; }
+	    vsystem ("cleaning.pl");
+	    if (-e $lockfile) { goodbye "lock file exists"; }
+
+	    print STDERR "done: $entries[0]\n";
+	    open (DONELOG, ">>$donelogfile");
+	    print DONELOG "$entries[0]\n";
+	    close (DONELOG);
+	} else {
+	    $controlflags = "";
+	    docontroller ($subdir, $inlist, $outlist, $photcode, "-s flatten", 1);
+	    print STDERR "done: $subdir\n";
+	    open (DONELOG, ">>$donelogfile");
+	    print DONELOG "$subdir\n";
+	    close (DONELOG);
+	}
+    }
+    unless (unlink ($alockfile)) { goodbye "can't unlink $alockfile, why not?"; }
+    print STDERR "remember to delete images from complete directories\n";
+
+    unless (-e $haltfile) { 
+	print STDERR "re-did unfinished images\n";
+	print STDERR "starting a new archive.pl with output to $outfile\n";
+	vsystem ("notify.pl $ARGV[0] $outfile $Noutfile");
+	vsystem ("archive.pl $Noutfile 1>$outfile 2>&1 &");
+	exit;
+    }
+    goodbye "re-did unfinished images";
+}
+    
+print STDERR "no unfinished nights, slurping a new set\n";
+if (-e $scriptfile) { if (vsystem ("rm $scriptfile")) { goodbye "couldn't delete old MAESTRO file"; }}
+
+if (vsystem ("rsh -n pallas slurp.pl")) { goodbye "couldn't start slurp on pallas"; }
+if (-e $killfile) { goodbye "process killed with $killfile"; }
+if (!-e $scriptfile) {goodbye "slurp failed to download files"; }
+
+print STDERR "done slurping, starting controller runs\n";
+vsystem ("date");
+
+# run controller on the resulting images
+# controller wants to be run in the same directory as the inlists 
+chdir $scriptdir;
+open (MAESTRO, $scriptfile);
+while ($line = <MAESTRO>) {
+    @entries = split (" ",$line);
+    $inlist = $entries[0] . ".inlist";
+    $outlist = $entries[0] . ".outlist";
+    $subdir = $entries[0];
+    $photcode = $entries[1];
+    docontroller ($subdir, $inlist, $outlist, $photcode, "-s flatten", 1);
+    print STDERR "done: $subdir\n";
+    open (DONELOG, ">>$donelogfile");
+    print DONELOG "$entries[0]\n";
+    close (DONELOG);
+}
+close (MAESTRO);
+
+unless (unlink ($alockfile)) { goodbye "can't unlink $alockfile, why not?"; }
+print STDERR "ending execution\n";
+vsystem ("date");
+print STDERR "finished new images";
+
+unless (-e $haltfile) { 
+    print STDERR "starting a new archive.pl with output to $outfile\n";
+    vsystem ("notify.pl $ARGV[0] $outfile $Noutfile");
+    vsystem ("archive.pl $Noutfile 1>$outfile 2>&1 &");
+} else {
+    vsystem ("notify.pl $ARGV[0] none 0");
+}
+exit;
+
+# run nrphot on the altered cpt files and quit
+chdir $database;
+@files = `find . -name "*.cpt" -newer $reffile`;
+$Nfiles = @files;
+print STDERR "N: $Nfiles\n";
+foreach $f (@files) {
+    chop ($f);
+    vsystem ("nrphot -v $f");
+    if (-e $lockfile) { goodbye "lock file exists"; }
+}
+
+# delete inlist files
+
+# do controller (& cleaning if needed), taking care of flats and error conditions
+sub docontroller {
+    if (-e $killfile) { goodbye "process killed with $killfile"; }
+    if (-e $clockfile) { goodbye "lock file $clockfile exists"; }
+    # break up entry line
+    $outsubdir = $_[0];
+    $in = $_[1];
+    $out = $_[2];
+    $phot = $_[3];
+    $flags = $_[4];
+    $clean = $_[5];
+    # create sub-directory in output dir
+    $outdir = $workspace . $outsubdir;
+    unless (-e $outdir) { unless (mkdir ($outdir,0777)) { goodbye "cannot create output directory $outdir"; } }
+    # make link to correct flat
+    $flatlink = $flatdir . "newflat.fits";
+    $realflat = $flatdir . "flatcode" . $phot . ".fits";
+    if (-l $flatlink) { unless (unlink ($flatlink)) { goodbye "cannot unlink $flatlink"; }}
+    if (-e $flatlink) { goodbye "$flatlink exists, but is not a link!"; }
+    unless (symlink ($realflat, $flatlink)) { goodbye "cannot symlink $realflat to $flatlink"; }
+    # run controller and cleanup
+    if (-e $lockfile) { goodbye "lock file exists"; }
+    vsystem ("controller $flags -o $outsubdir $in $out $phot");
+    if (-e $clockfile) { goodbye "lock file $clockfile exists"; }
+    if (-e $lockfile) { goodbye "lock file exists"; }
+    if (-e $killfile) { goodbye "process killed with $killfile"; }
+    if ($clean) {
+	vsystem ("cleaning.pl");
+	if (-e $lockfile) { goodbye "lock file exists"; }
+    }
+}
+
+sub by_filename {
+    @value1 = split (" ", $a);
+    @value2 = split (" ", $b);
+    $value1[0] cmp $value2[0]; 
+}
+    
+# we read from in and write to out those files with 1st 0 in the mode column
+sub parse_outlist {
+    $in = $_[0];
+    $out = $_[1];
+    $mode = $_[2];
+    
+# load inlist
+    open (INLIST, $in);
+    @inarray = ();
+    while ($line = <INLIST>) {
+	chop ($line);
+	@inarray = (@inarray,$line);
+    }
+    $Nin = @inarray;
+    close (INLIST);
+    @inarray = sort by_filename @inarray;
+
+    @outarray = ();
+    for ($k = 0; $k < $Nin; $k++) {
+	@pvalues = split (" ", $inarray[$k]);
+	$more = 1;
+	while ($more) {
+	    print STDERR ".";
+	    @nvalues = split (" ", $inarray[$k+1]);
+	    if ($nvalues[0] cmp $pvalues[0]) {
+		$more = 0;
+	    } else {
+		$pvalues[1] |= $nvalues[1];
+		$pvalues[2] |= $nvalues[2];
+		$pvalues[3] |= $nvalues[3];
+		$pvalues[4] |= $nvalues[4];
+		$pvalues[5] |= $nvalues[5];
+		$k ++;
+	    }
+	}
+	# check the earlier status values: if failed earlier, skip
+	$skip = 0;
+	for ($m = 1; $m < $mode; $m++) { if (!$pvalues[$m]) { $skip = 1; } }
+	if (! $skip && !$pvalues[$mode]) {
+	    $name = $pvalues[0];
+	    $name =~ s/$dumpspace//;
+	    $name =~ s/://;
+	    @outarray = (@outarray,$name);
+	}
+    }
+    $Nout = @outarray;
+    if ($Nout) {
+	open (OUTLIST, ">$out");
+	for ($k = 0; $k < $Nout; $k++) {
+	    print OUTLIST "$outarray[$k]\n";
+	}
+	close (OUTLIST);
+    }
+    $Nout;
+}
+
+sub parse_inlist {
+    $in = $_[0];
+    $out = $_[1];
+    $new = $_[2];
+    
+# load inlist
+    open (INLIST, $in);
+    @inarray = ();
+    while ($line = <INLIST>) {
+	chop ($line);
+	@inarray = (@inarray,$line);
+    }
+    $Nin = @inarray;
+    close (INLIST);
+
+# load inlist
+    open (OUTLIST, $out);
+    @outarray = ();
+    while ($line = <OUTLIST>) {
+	chop ($line);
+	@outarray = (@outarray,$line);
+    }
+    $Nout = @outarray;
+    close (OUTLIST);
+
+# load outlist
+    @newarray = ();
+    for ($k = 0; $k < $Nin; $k++) {
+	$gotit = 0;
+	for ($m = 0; ! $gotit && ($m < $Nout); $m++) {
+	    if ($outarray[$m] =~ /$inarray[$k]/) {
+		$gotit = 1;
+	    }
+	}
+	if (!$gotit) {
+	    @newarray = (@newarray,$inarray[$k]);
+	}
+    }
+    $Nnew = @newarray;
+    if ($Nnew) {
+	open (NEWLIST, ">$new");
+	for ($k = 0; $k < $Nnew; $k++) {
+	    print NEWLIST "$inarray[$k]\n";
+	}
+	close (NEWLIST);
+    }
+    $Nnew;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/checkoutfail.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/checkoutfail.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/checkoutfail.pl	(revision 22322)
@@ -0,0 +1,63 @@
+#!/usr/bin/perl
+
+sub by_filename {
+    @value1 = split (" ", $a);
+    @value2 = split (" ", $b);
+    $value1[0] cmp $value2[0]; 
+}
+    
+# we read from in and write to out those files with 1st 0 in the mode column
+sub parse_outlist {
+    $in = $_[0];
+    $mode = $_[1];
+    
+# load inlist
+    open (INLIST, $in);
+    @inarray = ();
+    while ($line = <INLIST>) {
+	chop ($line);
+	@inarray = (@inarray,$line);
+    }
+    $Nin = @inarray;
+    close (INLIST);
+    @inarray = sort by_filename @inarray;
+
+    @outarray = ();
+    for ($k = 0; $k < $Nin; $k++) {
+	@pvalues = split (" ", $inarray[$k]);
+	$more = 1;
+	while ($more) {
+	    @nvalues = split (" ", $inarray[$k+1]);
+	    if ($nvalues[0] cmp $pvalues[0]) {
+		$more = 0;
+	    } else {
+		$pvalues[1] |= $nvalues[1];
+		$pvalues[2] |= $nvalues[2];
+		$pvalues[3] |= $nvalues[3];
+		$pvalues[4] |= $nvalues[4];
+		$pvalues[5] |= $nvalues[5];
+		$k ++;
+	    }
+	}
+	# check the earlier status values: if failed earlier, skip
+	$skip = 0;
+	for ($m = 1; $m < $mode; $m++) { if (!$pvalues[$m]) { $skip = 1; } }
+	if (! $skip && !$pvalues[$mode]) {
+	    $name = $pvalues[0];
+	    $name =~ s/$topdir//;
+	    $name =~ s/://;
+	    @outarray = (@outarray,$name);
+	}
+    }
+    $Nout = @outarray;
+    if ($Nout) {
+	for ($k = 0; $k < $Nout; $k++) {
+	    print STDERR "$mode: $outarray[$k]\n";
+	}
+    }
+    $Nout;
+}
+
+$outlist = $ARGV[0];
+
+parse_outlist ($outlist,5);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/checkoutlist.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/checkoutlist.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/checkoutlist.pl	(revision 22322)
@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+
+sub by_filename {
+    @value1 = split (" ", $a);
+    @value2 = split (" ", $b);
+    $value1[0] cmp $value2[0]; 
+}
+    
+# we read from in and write to out those files with 1st 0 in the mode column
+sub parse_outlist {
+    $in = $_[0];
+    $mode = $_[1];
+    
+# load inlist
+    open (INLIST, $in);
+    @inarray = ();
+    while ($line = <INLIST>) {
+	chop ($line);
+	@inarray = (@inarray,$line);
+    }
+    $Nin = @inarray;
+    close (INLIST);
+    @inarray = sort by_filename @inarray;
+
+    @outarray = ();
+    for ($k = 0; $k < $Nin; $k++) {
+	@pvalues = split (" ", $inarray[$k]);
+	$more = 1;
+	while ($more) {
+	    @nvalues = split (" ", $inarray[$k+1]);
+	    if ($nvalues[0] cmp $pvalues[0]) {
+		$more = 0;
+	    } else {
+		$pvalues[1] |= $nvalues[1];
+		$pvalues[2] |= $nvalues[2];
+		$pvalues[3] |= $nvalues[3];
+		$pvalues[4] |= $nvalues[4];
+		$pvalues[5] |= $nvalues[5];
+		$k ++;
+	    }
+	}
+	# check the earlier status values: if failed earlier, skip
+	$skip = 0;
+	for ($m = 1; $m < $mode; $m++) { if (!$pvalues[$m]) { $skip = 1; } }
+	if (! $skip && !$pvalues[$mode]) {
+	    $name = $pvalues[0];
+	    $name =~ s/$topdir//;
+	    $name =~ s/://;
+	    @outarray = (@outarray,$name);
+	}
+    }
+    $Nout = @outarray;
+    if ($Nout) {
+	for ($k = 0; $k < $Nout; $k++) {
+	    print STDOUT "$mode: $outarray[$k]\n";
+	}
+    }
+    $Nout;
+}
+
+$outlist = $ARGV[0];
+
+for ($i = 1; $i < 6; $i++) {
+    parse_outlist ($outlist,$i);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/cleaning.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/cleaning.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/cleaning.pl	(revision 22322)
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+$database = "/hebe/d27/database/";
+$lockfile = $database . "lock";
+$reffile = $database . "start.clean";
+
+chdir $database;
+
+@files = `find . -name "*.cpt" -newer $reffile`;
+$Nfiles = @files;
+print STDERR "N: $Nfiles\n";
+
+if (-e $lockfile) { die "lock file exists"; }
+
+foreach $f (@files) {
+    chop ($f);
+    vsystem ("markstar -v $f");
+    if (-e $lockfile) { die "lock file exists"; }
+    vsystem ("addusno -v $f");
+    if (-e $lockfile) { die "lock file exists"; }
+    vsystem ("markrock -v $f");
+    if (-e $lockfile) { die "lock file exists"; }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/locks.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/locks.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/locks.pl	(revision 22322)
@@ -0,0 +1,93 @@
+#!/usr/bin/perl
+
+$topdir  = "/pallas/";
+$scriptdir = "/hebe/d27/logs/";
+$donelogfile = $scriptdir . "done.log";
+$scriptfile = $scriptdir . "maestro.dat";
+$flatdir = "/hebe/d27/references/config/";
+
+$workspace = "/hebe/d27/workspace/";
+$database = "/hebe/d27/database/";
+$lockfile = $database . "lock";
+$alockfile = $database . "alock";
+$clockfile = $database . "clock";
+$killfile  = $database . "kill";
+$haltfile  = $database . "halt";
+$reffile = $database . "start.clean";
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    vsystem ("date");
+    die "@_";
+}
+
+$Narg = @ARGV;
+if ($Narg > 1) {
+    goodbye "USAGE: locks.pl [-reset] [-halt] [-kill] [-dB] [-controller] [-archive]";
+}
+
+unless ($ARGV[0] cmp "-reset") {
+    unlink ($lockfile);
+    unlink ($alockfile);
+    unlink ($clockfile);
+    unlink ($killfile);
+    unlink ($haltfile);
+    exit;
+}
+
+unless ($ARGV[0] cmp "-kill") {
+    open (TEMP, ">$killfile");
+    close (TEMP);
+    exit;
+}
+
+unless ($ARGV[0] cmp "-halt") {
+    open (TEMP, ">$haltfile");
+    close (TEMP);
+    exit;
+}
+
+unless ($ARGV[0] cmp "-dB") {
+    open (TEMP, ">$lockfile");
+    close (TEMP);
+    exit;
+}
+
+unless ($ARGV[0] cmp "-controller") {
+    open (TEMP, ">$clockfile");
+    close (TEMP);
+    exit;
+}
+
+unless ($ARGV[0] cmp "-archive") {
+    open (TEMP, ">$alockfile");
+    close (TEMP);
+    exit;
+}
+
+@locks = ();
+if (-e $lockfile) { @locks = (@locks,$lockfile); }
+if (-e $alockfile) { @locks = (@locks,$alockfile); }
+if (-e $clockfile) { @locks = (@locks,$clockfile); }
+if (-e $killfile) { @locks = (@locks,$killfile); }
+if (-e $haltfile) { @locks = (@locks,$haltfile); }
+
+$Nlocks = @locks;
+
+if ($Nlocks > 0) {
+    print STDERR "locks set:\n";
+    for ($i = 0; $i < $Nlocks; $i++) {
+	print STDERR "$locks[$i]\n";
+    }
+} else {
+    print STDERR "no locks currently set\n";
+}
+
+print STDERR "\n";
+print STDERR "USAGE: locks.pl [-reset] [-kill] [-dB] [-controller] [-archive]\n";
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/notify.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/notify.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/notify.pl	(revision 22322)
@@ -0,0 +1,12 @@
+#!/usr/bin/perl
+
+$target= "gene\@astro.washington.edu";
+$targetmachine = "astro.washington.edu";
+
+$last = "archive." . $ARGV[0];
+$next = $ARGV[1];
+$N = $ARGV[2];
+
+# system ("echo \"done with $last, starting $next ($N)\" | mail $targetname\@$targetmachine");
+system ("echo \"done with $last, starting $next ($N)\" | mail $target");
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/rearchive.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/rearchive.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/rearchive.pl	(revision 22322)
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+
+# $dumpspace = "/pallas/d1/";
+# $workspace = "/metis/d27/workspace/";
+# $workspace = "/irene/d11/workspace/";
+
+$database = "/hebe/d27/database/";
+$lockfile = $database . "lock";
+$alockfile = $database . "alock";
+$clockfile = $database . "clock";
+$killfile  = $database . "kill";
+$haltfile  = $database . "halt";
+$reffile = $database . "start.clean";
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    vsystem ("date");
+    die "@_";
+}
+
+if (@ARGV < 1) {die "USAGE: rearchive.pl dir\n"; }
+
+$target = $ARGV[0];
+
+print STDERR "starting execution\n";
+vsystem ("date");
+if (-e $killfile) { goodbye "process killed with $killfile"; }
+
+if (-e $alockfile) { goodbye "archive lock $alockfile is set"; }
+open (TEMP, ">$alockfile");
+close (TEMP);
+
+# untar directory 
+
+if (!-e $target.tgz) { goodbye "target $target.tgz not found"; }
+
+if (vsystem ("gunzip -c $target.tgz | tar xvf -")) { goodbye "untar failed for $target.tgz"; }
+if (vsystem ("ls $target/*.cmp > $target.lst")) { goodbye "failed to get directory listing for $target"; }
+
+open (TEMP, ">$target.log");
+close (TEMP);
+if (!-e $target.log) { goodbye "could not create $target.log"; }
+
+# load in done date list
+open (LISTFILE, $target.lst);
+@listfile = ();
+while ($line = <LISTFILE>) {
+  chop ($line);
+  @listfile = (@listfile,$line);
+}
+$Nlist = @listfile;
+close (LISTFILE);
+
+for ($i = 0; $i < Nlist; $i++) {
+    if (-e $lockfile) { goodbye "database lock $lockfile is set"; }
+    vsystem ("addstar -v $listfile[$i]");
+}
+
+if (-e $lockfile) { goodbye "lock file exists"; }
+vsystem ("cleaning.pl");
+if (-e $lockfile) { goodbye "lock file exists"; }
+
+# vsystem ("rm $target");
+exit;
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/slurp
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/slurp	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/slurp	(revision 22322)
@@ -0,0 +1,223 @@
+#!/usr/bin/env perl
+
+$Ngrab = 5;
+$scriptdir   = "/hebe/d27/logs/";
+$dumpspace   = "/pallas/d1/";
+$tapedrive   = "/dev/rmt/1cn";
+$tapelogfile = $scriptdir . "tape.log";
+$donelogfile = $scriptdir . "processed.log";
+$scriptfile  = $scriptdir . "extracted.log";
+
+# old directories:
+# $topdir  = "/pallas/";
+# $rootdir = "/pallas/d1/";
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "@_\n";
+    vsystem ("unload $tape");
+    vsystem ("lunlock $tapedrive");
+    vsystem ("lunlock $dumpspace");
+    die;
+}
+
+# goodbye ("quit suddenly");
+
+# checkdone (line)
+# returns 1 if not in line (and sets $name), 
+# returns 0 otherwise
+sub checkdone {
+    local($inline) = @_;
+    @entries = split (" ",$inline);
+    $found = 0;
+    if ((@entries[0] > 1996) && (@entries[0] < 2097)) {
+	$found = 1;
+	$name = substr(@entries[0],2,2) . @entries[1] . @entries[2];
+	for ($j = 0; ($j < $Ndone) && ($found); $j++) {
+	    @done = split (" ", $donelist[$j]);
+	    if ($done[0] eq $name) {
+		$found = 0;
+	    }
+	}
+    }
+    $found;
+}
+
+# checktape (name)
+# find all entries which match name and add to dolist
+sub checktape {
+  local($inname) = @_;
+  $j = 0;
+  while ($j < $Ntape) {
+    @entries = split (" ",$tapelist[$j]);
+    if ((@entries[0] > 1996) && (@entries[0] < 2097)) {
+      $tmpname = substr(@entries[0],2,2) . @entries[1] . @entries[2];
+      if ($inname eq $tmpname) {
+	@dolist = (@dolist,$tapelist[$j]);
+	@doname = (@doname,$inname);
+      }
+    }
+    $j ++;
+  }
+}
+
+
+# load in done date list
+open (DONELOG, $donelogfile);
+@donelist = ();
+while ($line = <DONELOG>) {
+  chop ($line);
+  @donelist = (@donelist,$line);
+}
+$Ndone = @donelist;
+close (DONELOG);
+print "Ndone = $Ndone\n";
+
+# load in tape.log list
+open (TAPELOG, $tapelogfile);
+@tapelist = ();
+while ($line = <TAPELOG>) {
+  chop ($line);
+  @tapelist = (@tapelist,$line);
+}
+$Ntape = @tapelist;
+close (TAPELOG);
+# @tapelist = reverse(@tapelist);
+print "Ntape = $Ntape\n";
+
+# find 5 new nights not already in the "donelist"
+$N = 0;
+@tryname = ();
+for ($i = 0; ($i < $Ntape) && ($N < $Ngrab); $i++) {
+    if (&checkdone ($tapelist[$i])) {
+	$j = 0; 
+	$gotit = 0;
+	for ($j = 0; ($j < $N) && (! $gotit); $j++) {
+	    if ($name eq $tryname[$j]) {
+		$gotit = 1;
+	    }
+	}
+	if (! $gotit) {
+	    @tryname = (@tryname, $name);  
+	    $N ++;
+	}
+    }
+}
+$Ntryname = @tryname;
+# tryname has a list of the 980103 style names
+
+# now that we have a list of interesting nights, 
+# get a list of the actual lines from tape.log, 
+# along with a list of the matchine names
+$i = 0;
+@dolist = ();
+@doname = ();
+while ($i < $Ntryname) {
+  &checktape ($tryname[$i]);
+  $i ++;
+}
+
+$i = 0;
+$Ndolist = @dolist;
+@fileno = ();
+@tapeno = ();
+@gbytes = ();
+@photno = ();
+@entry = ();
+while ($i < $Ndolist) {
+    print STDERR "$doname[$i] == $dolist[$i]\n";
+    @words = split (" ",$dolist[$i]);
+    @entry = (@entry,$i);
+    @tapeno = (@tapeno,$words[3]);
+    @fileno = (@fileno,$words[4]);
+    @gbytes = (@gbytes,$words[5]);
+    @photno = (@photno,$words[8]);
+    $i ++;
+}
+
+# delete old downloaded data
+
+$dir = $dumpspace . "9";
+print STDERR "dir: $dir\n";
+@files = <$dir*>;
+$Nfiles = @files;
+print "files: @files\n";
+for ($i = 0; $i < $Nfiles; $i++) {
+    if ((-d $files[$i]) && (-O $files[$i])) {
+	if (vsystem ("rm -r $files[$i]")) { goodbye "error deleting directory $files[$i]"; }
+    }
+}
+
+# now sort the entries by tape and file number:
+
+sub by_tape_and_file { ($tapeno[$a] <=> $tapeno[$b]) || ($fileno[$a] <=> $fileno[$b]); }
+@entry = sort by_tape_and_file @entry;
+
+# lock tape and disk space. keep trying for up to 10 hours
+for ($i = 0; ($i < 30) && vsystem ("llock $dumpspace"); $i++) { system ("sleep 1200"); }
+ if ($i == 100) { goodbye "failed to grab disk space"; }
+for ($i = 0; ($i < 30) && vsystem ("llock $tapedrive"); $i++) { system ("sleep 1200"); }
+ if ($i == 100) { goodbye "failed to grab tape drive"; }
+
+$tape = -1;
+for ($i = 0; $i < $Ndolist; $i++) {
+    # do a df and compare to $gbytes[$entry[$i]]
+    @answer = `df -k $dumpspace`;
+    @tmp = split (" ", $answer[1]);
+    $space = $tmp[3] / (1024*1024);
+    print STDERR "compare $space and $gbytes[$entry[$i]]\n";
+    if (0.95*$space < $gbytes[$entry[$i]]) { last; }
+    # load tape if needed
+    if ($tapeno[$entry[$i]] != $tape) {
+	# unload old tape, load desired tape 
+	if ($tape != -1) { if (vsystem ("unload $tape")) { goodbye "trouble unloading tape $tape"; } }
+	if (vsystem ("load $tapeno[$entry[$i]]")) { goodbye "trouble loading tape $tapeno[$entry[$i]]"; }
+	$tape = $tapeno[$entry[$i]];
+	$j = 0;
+	$offline = 1;
+	# wait for tape to come online before starting anything
+	while (($j < 12) && $offline) {
+	    vsystem ("sleep 10");
+	    $offline = vsystem ("mt -f $tapedrive status");
+	    $j ++;
+	}
+	if ($j == 12) { goodbye "tape still not online!"; }
+	$file = 0;
+    }
+    # advance to first file of interest
+    $nskip = $fileno[$entry[$i]] - $file;
+    if ($nskip > 0) {
+	if (vsystem ("mt -f $tapedrive fsf $nskip")) { goodbye "error advancing tape"; }
+    }
+    # create output directory if it doesn't exist
+    $dir = $dumpspace . $doname[$entry[$i]];
+    unless (-e $dir) {
+	unless (mkdir ($dir,0777)) { goodbye "cannot create output directory $dir"; }
+    }
+    chdir $dir;
+    # extract the files
+    vsystem ("tar -xvfb $tapedrive 128");
+    $file = $fileno[$entry[$i]] + 1;
+}
+$Ndolist = $i;
+
+if (vsystem("unload $tape")) { goodbye "trouble unloading tape $tape"; }
+if (vsystem("lunlock $tapedrive")) { goodbye "trouble unlocking tape drive $tapedrive"; }
+if (vsystem ("lunlock $dumpspace")) { goodbye "trouble unlocking disk drive $dumpspace"; }
+
+open (MAESTRO, ">$scriptfile");
+chdir $dumpspace;
+$i = 0;
+while ($i < $Ndolist) {
+    vsystem ("ls $doname[$entry[$i]]/*b.fits > $scriptdir/$doname[$entry[$i]].inlist");
+    print MAESTRO "$doname[$entry[$i]] $photno[$entry[$i]] \n";
+    $i ++;
+}
+close (MAESTRO);
+
+1;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/stage1
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/stage1	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/stage1	(revision 22322)
@@ -0,0 +1,342 @@
+#!/usr/bin/env perl
+
+# in this script, we run flatten, dophot, fstat, and gastro
+# these can be run totally in parallel, so this routine runs
+# independently of the series processing (addstar, markstar, etc)
+# in stage2
+
+# for now these directories are hard-wired.  eventually, they
+# should be looked up in the config file, so all scripts are
+# consistent
+$dumpspace = "/pallas/d1/";
+$workspace = "/metis/d27/workspace/";
+# $workspace = "/irene/d11/workspace/";
+
+$scriptdir = "/hebe/d27/logs/";
+$donelogfile = $scriptdir . "processed.log";
+$scriptfile  = $scriptdir . "extracted.log";
+$flatdir = "/hebe/d27/references/config/";
+
+$database = "/hebe/d27/database/";
+$alockfile = $database . "alock1";     # lock on stage1 (only 1 running at a time)
+$clockfile = $database . "clock1";     # lock on controller
+$killfile  = $database . "kill";       # quickly stop the processing
+$haltfile  = $database . "halt";       # halt processing after this loop
+$reffile = $database . "start.clean";
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    vsystem ("date");
+    vsystem ("notify.pl $ARGV[0] none 0");
+    die "@_";
+}
+
+if (@ARGV < 1) {die "USAGE: stage1 Noutfile\n"; }
+$Noutfile = $ARGV[0] + 1;
+$outfile = "stage1." . $Noutfile . ".log";
+
+print STDERR "starting execution\n";
+vsystem ("date");
+if (-e $killfile) { goodbye "process killed with $killfile"; }
+
+
+if (-e $alockfile) { goodbye "archive lock $alockfile is set"; }
+open (TEMP, ">$alockfile");
+close (TEMP);
+
+# load in done date list
+open (DONELOG, $donelogfile);
+@donelist = ();
+while ($line = <DONELOG>) {
+  chop ($line);
+  @donelist = (@donelist,$line);
+}
+$Ndone = @donelist;
+close (DONELOG);
+
+# load in maestro list
+open (MAESTRO, $scriptfile);
+@todolist = ();
+while ($line = <MAESTRO>) {
+  chop ($line);
+  @todolist = (@todolist,$line);
+}
+$Ntodo = @todolist;
+close (MAESTRO);
+
+# find entries in MAESTRO that are NOT in DONELOG
+@leftlist = ();
+for ($i = 0; $i < $Ntodo; $i ++) {
+    $gotit = 0;
+    for ($j = 0; ($j < $Ndone) && ! $gotit; $j ++) {
+	@todo = split (" ",$todolist[$i]);
+	@done = split (" ",$donelist[$j]);
+	if ($todo[0] eq $done[0]) {
+	    $gotit = 1;
+	}
+    }
+    if (!$gotit) {
+	@leftlist = (@leftlist, $todolist[$i]);  
+    }
+}
+$Nleft = @leftlist;
+
+if (-e $killfile) { goodbye "process killed with $killfile"; }
+# run controller on unfinished entries
+# we are going to run this in the script directory
+if ($Nleft) {
+    chdir $scriptdir;
+    for ($i = 0; $i < $Nleft; $i++) {
+	print STDERR "unfinished: $leftlist[$i]\n";
+	@entries = split (" ",$leftlist[$i]);
+	$inlist = $entries[0] . ".inlist";
+	$outlist = $entries[0] . ".addlist";
+	$subdir = $entries[0];
+	$photcode = $entries[1];
+	print STDERR "$subdir: $inlist $outlist $photcode\n";
+	if (-e $outlist) {
+	    # partially successful run, restart controller in the middle
+	    $tmp_inlist = $entries[0] . ".tmp_inlist";
+	    $tmpoutlist = $entries[0] . ".tmpaddlist";
+	    if (-e $tmpoutlist) { if (vsystem ("cat $tmpoutlist >> $outlist")) { goodbye "trouble merging $tmpoutlist and $outlist\n"; }}
+	    # partially successful re-start: make sure $outlist is complete
+	    if (parse_outlist ($outlist,$tmp_inlist,1)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s flatten", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    if (parse_outlist ($outlist,$tmp_inlist,2)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s dophot", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    if (parse_outlist ($outlist,$tmp_inlist,3)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s fstat", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    if (parse_outlist ($outlist,$tmp_inlist,4)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s gastro", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    # do inlist entries not in outlist
+	    if (parse_inlist ($inlist,$outlist,$tmp_inlist)) {
+		docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s flatten", 0);
+		vsystem ("cat $tmpoutlist >> $outlist");
+	    }
+	    unless (unlink ($tmpoutlist)) { goodbye "can't unlink $tmpoutlist, why not?"; }
+	    unless (unlink ($tmp_inlist)) { goodbye "can't unlink $tmp_inlist, why not?"; }
+	    # done with this night, add to donelogfile
+	    print STDERR "done: $subdir $photcode\n";
+	    open (DONELOG, ">>$donelogfile");
+	    print DONELOG "$subdir $photcode\n";
+	    close (DONELOG);
+	} else {
+	    docontroller ($subdir, $inlist, $outlist, $photcode, "-s flatten", 1);
+	    print STDERR "done: $subdir $photcode\n";
+	    open (DONELOG, ">>$donelogfile");
+	    print DONELOG "$subdir $photcode\n";
+	    close (DONELOG);
+	}
+    }
+    unless (unlink ($alockfile)) { goodbye "can't unlink $alockfile, why not?"; }
+    print STDERR "remember to delete images from complete directories\n";
+
+    unless (-e $haltfile) { 
+	print STDERR "re-did unfinished images\n";
+	print STDERR "starting a new stage1 with output to $outfile\n";
+	vsystem ("notify.pl $ARGV[0] $outfile $Noutfile");
+	vsystem ("stage1 $Noutfile 1>$outfile 2>&1 &");
+	exit;
+    }
+    goodbye "re-did unfinished images";
+}
+    
+# get a new set of data
+print STDERR "no unfinished nights, slurping a new set\n";
+if (-e $scriptfile) { if (vsystem ("rm $scriptfile")) { goodbye "couldn't delete old MAESTRO file"; }}
+
+if (vsystem ("rsh -n pallas slurp")) { goodbye "couldn't start slurp on pallas"; }
+if (-e $killfile) { goodbye "process killed with $killfile"; }
+if (!-e $scriptfile) {goodbye "slurp failed to download files"; }
+
+print STDERR "done slurping, starting controller runs\n";
+vsystem ("date");
+
+# run controller on the resulting images
+# controller wants to be run in the same directory as the inlists 
+$Nruns = 0;
+chdir $scriptdir;
+open (MAESTRO, $scriptfile);
+while ($line = <MAESTRO>) {
+    @entries = split (" ",$line);
+    $inlist = $entries[0] . ".inlist";
+    $outlist = $entries[0] . ".addlist";
+    $subdir = $entries[0];
+    $photcode = $entries[1];
+    docontroller ($subdir, $inlist, $outlist, $photcode, "-s flatten", 1);
+    print STDERR "done: $subdir $photcode\n";
+    open (DONELOG, ">>$donelogfile");
+    print DONELOG "$subdir $photcode\n";
+    close (DONELOG);
+    $Nruns ++;
+}
+close (MAESTRO);
+
+if (Nruns == 0) {
+    print STDERR "no data downloaded in slurp, waiting for a while\n";
+    vsystem ("sleep 3600");
+}
+unless (unlink ($alockfile)) { goodbye "can't unlink $alockfile, why not?"; }
+print STDERR "ending execution\n";
+vsystem ("date");
+print STDERR "finished new images";
+
+unless (-e $haltfile) { 
+    print STDERR "starting a new stage1 with output to $outfile\n";
+    vsystem ("notify.pl $ARGV[0] $outfile $Noutfile");
+    vsystem ("stage1 $Noutfile 1>$outfile 2>&1 &");
+} else {
+    vsystem ("notify.pl $ARGV[0] none 0");
+}
+exit;
+
+# do controller (& cleaning if needed), taking care of flats and error conditions
+sub docontroller {
+    if (-e $killfile) { goodbye "process killed with $killfile"; }
+    if (-e $clockfile) { goodbye "lock file $clockfile exists"; }
+    # break up entry line
+    $outsubdir = $_[0];
+    $in = $_[1];
+    $out = $_[2];
+    $phot = $_[3];
+    $flags = $_[4];
+    $clean = $_[5];
+    # create sub-directory in output dir
+    $outdir = $workspace . $outsubdir;
+    unless (-e $outdir) { unless (mkdir ($outdir,0777)) { goodbye "cannot create output directory $outdir"; } }
+    # make link to correct flat
+    $flatlink = $flatdir . "newflat.fits";
+    $realflat = $flatdir . "flatcode" . $phot . ".fits";
+    if (-l $flatlink) { unless (unlink ($flatlink)) { goodbye "cannot unlink $flatlink"; }}
+    if (-e $flatlink) { goodbye "$flatlink exists, but is not a link!"; }
+    unless (symlink ($realflat, $flatlink)) { goodbye "cannot symlink $realflat to $flatlink"; }
+    # run controller and cleanup
+    vsystem ("control1 $flags -o $outsubdir $in $out $phot");
+    if (-e $clockfile) { goodbye "lock file $clockfile exists"; }
+    if (-e $killfile) { goodbye "process killed with $killfile"; }
+}
+
+sub by_filename {
+    @value1 = split (" ", $a);
+    @value2 = split (" ", $b);
+    $value1[0] cmp $value2[0]; 
+}
+    
+# we read from in and write to out those files with 1st 0 in the mode column
+sub parse_outlist {
+    $in = $_[0];
+    $out = $_[1];
+    $mode = $_[2];
+    
+# load inlist
+    open (INLIST, $in);
+    @inarray = ();
+    while ($line = <INLIST>) {
+	chop ($line);
+	@inarray = (@inarray,$line);
+    }
+    $Nin = @inarray;
+    close (INLIST);
+    @inarray = sort by_filename @inarray;
+
+    @outarray = ();
+    for ($k = 0; $k < $Nin; $k++) {
+	@pvalues = split (" ", $inarray[$k]);
+	$more = 1;
+	while ($more) {
+	    print STDERR ".";
+	    @nvalues = split (" ", $inarray[$k+1]);
+	    if ($nvalues[0] cmp $pvalues[0]) {
+		$more = 0;
+	    } else {
+		$pvalues[1] |= $nvalues[1];
+		$pvalues[2] |= $nvalues[2];
+		$pvalues[3] |= $nvalues[3];
+		$pvalues[4] |= $nvalues[4];
+		$pvalues[5] |= $nvalues[5];
+		$k ++;
+	    }
+	}
+	# check the earlier status values: if failed earlier, skip
+	$skip = 0;
+	for ($m = 1; $m < $mode; $m++) { if (!$pvalues[$m]) { $skip = 1; } }
+	if (! $skip && !$pvalues[$mode]) {
+	    $name = $pvalues[0];
+	    $name =~ s/$dumpspace//;
+	    $name =~ s/://;
+	    @outarray = (@outarray,$name);
+	}
+    }
+    $Nout = @outarray;
+    if ($Nout) {
+	open (OUTLIST, ">$out");
+	for ($k = 0; $k < $Nout; $k++) {
+	    print OUTLIST "$outarray[$k]\n";
+	}
+	close (OUTLIST);
+    }
+    $Nout;
+}
+
+sub parse_inlist {
+    $in = $_[0];
+    $out = $_[1];
+    $new = $_[2];
+    
+# load inlist
+    open (INLIST, $in);
+    @inarray = ();
+    while ($line = <INLIST>) {
+	chop ($line);
+	@inarray = (@inarray,$line);
+    }
+    $Nin = @inarray;
+    close (INLIST);
+
+# load inlist
+    open (OUTLIST, $out);
+    @outarray = ();
+    while ($line = <OUTLIST>) {
+	chop ($line);
+	@outarray = (@outarray,$line);
+    }
+    $Nout = @outarray;
+    close (OUTLIST);
+
+# load outlist
+    @newarray = ();
+    for ($k = 0; $k < $Nin; $k++) {
+	$gotit = 0;
+	for ($m = 0; ! $gotit && ($m < $Nout); $m++) {
+	    if ($outarray[$m] =~ /$inarray[$k]/) {
+		$gotit = 1;
+	    }
+	}
+	if (!$gotit) {
+	    @newarray = (@newarray,$inarray[$k]);
+	}
+    }
+    $Nnew = @newarray;
+    if ($Nnew) {
+	open (NEWLIST, ">$new");
+	for ($k = 0; $k < $Nnew; $k++) {
+	    print NEWLIST "$newarray[$k]\n";
+	}
+	close (NEWLIST);
+    }
+    $Nnew;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/stage2
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/stage2	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/stage2	(revision 22322)
@@ -0,0 +1,307 @@
+#!/usr/bin/env perl
+
+# in this script, we run addstar and the cleaning routines
+# these must be run in series, so this routine runs
+# independently of the parallel processing in stage1
+
+# for now these directories are hard-wired.  eventually, they
+# should be looked up in the config file, so all scripts are
+# consistent
+$dumpspace = "/pallas/d1/";
+$workspace = "/metis/d27/workspace/";
+# $workspace = "/irene/d11/workspace/";
+
+#$scriptdir = "/hebe/d27/logs/";
+$scriptdir = "./";
+$addstarfile = $scriptdir . "addstared.log";
+$processfile = $scriptdir . "processed.log";
+$flatdir = "/hebe/d27/references/config/";
+
+$database = "/hebe/d27/database/";
+$lockfile = $database . "lock";      # lock on database files
+$alockfile = $database . "alock2";   # lock on stage2 (only 1 running at a time)
+$clockfile = $database . "clock2";   # lock on control2
+$killfile  = $database . "kill";     # quickly stop the processing
+$haltfile  = $database . "halt";     # halt processing after this loop
+$reffile = $database . "start.clean";
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    vsystem ("date");
+    vsystem ("notify.pl $ARGV[0] none 0");
+    die "@_";
+}
+
+if (@ARGV < 1) {die "USAGE: stage2 Noutfile\n"; }
+$Noutfile = $ARGV[0] + 1;
+$outfile = "stage2." . $Noutfile . ".log";
+
+print STDERR "starting execution\n";
+vsystem ("date");
+if (-e $killfile) { goodbye "process killed with $killfile"; }
+
+if (-e $alockfile) { goodbye "archive lock $alockfile is set"; }
+open (TEMP, ">$alockfile");
+close (TEMP);
+
+$Ntries = 0;
+$Nleft = 0;
+@leftlist = ();
+
+while (! $Nleft) {
+    compare_done_todo ();
+    if (-e $killfile) { goodbye "process killed with $killfile"; }
+    vsystem ("sleep 60");
+    if (-e $killfile) { goodbye "process killed with $killfile"; }
+    $Ntries ++;
+    if ($Ntries == 1440) { goodbye "timeout waiting for new data"; }
+}
+
+if (-e $killfile) { goodbye "process killed with $killfile"; }
+
+chdir $scriptdir;
+
+for ($i = 0; $i < $Nleft; $i++) {
+    print STDERR "adding: $leftlist[$i]\n";
+    @entries = split (" ",$leftlist[$i]);
+    $inlist = $entries[0] . ".addlist";
+    $outlist = $entries[0] . ".outlist";
+    $subdir = $entries[0];
+    $photcode = $entries[1];
+    print STDERR "$subdir: $inlist $outlist $photcode\n";
+    if (-e $outlist) {
+	# partially successful run, restart controller in the middle
+	$tmp_inlist = $entries[0] . ".tmpaddlist";
+	$tmpoutlist = $entries[0] . ".tmpoutlist";
+	if (-e $tmpoutlist) { if (vsystem ("cat $tmpoutlist >> $outlist")) { goodbye "trouble merging $tmpoutlist and $outlist\n"; }}
+	# partially successful re-start: make sure $outlist is complete
+	print STDERR "re-running unfinished entries in outlist\n";
+	if (parse_outlist ($outlist,$tmp_inlist, 4)) {
+	    docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s addstar", 0);
+	    vsystem ("cat $tmpoutlist >> $outlist");
+	}
+	# do inlist entries not in outlist
+	print STDERR "running undone entries in addlist\n";
+	parse_outlist ($inlist,"tmpinlist", 4);
+	parse_outlist ($outlist,"tmpoutlist", 5);
+	if (match_lists ("tmpinlist","tmpoutlist","$tmp_inlist")) {
+	    docontroller ($subdir, $tmp_inlist, $tmpoutlist, $photcode, "-s addstar", 0);
+	    vsystem ("cat $tmpoutlist >> $outlist");
+	}
+	# check ending success
+	unless (unlink ($tmpoutlist)) { goodbye "can't unlink $tmpoutlist, why not?"; }
+	unless (unlink ($tmp_inlist)) { goodbye "can't unlink $tmp_inlist, why not?"; }
+	vsystem ("cleaning.pl");
+	if (-e $lockfile) { goodbye "lock file exists"; }
+	
+	print STDERR "done: $subdir\n";
+	open (DONELOG, ">>$addstarfile");
+	print DONELOG "$subdir\n";
+	close (DONELOG);
+    } else {
+	$controlflags = "";
+	parse_outlist ($inlist,"tmpinlist", 4);
+	docontroller ($subdir, "tmpinlist", $outlist, $photcode, "-s addstar", 1);
+	print STDERR "done: $subdir\n";
+	open (DONELOG, ">>$addstarfile");
+	print DONELOG "$subdir\n";
+	close (DONELOG);
+    }
+}
+unless (unlink ($alockfile)) { goodbye "can't unlink $alockfile, why not?"; }
+print STDERR "remember to delete images from complete directories\n";
+
+unless (-e $haltfile) { 
+    print STDERR "re-did unfinished images\n";
+    print STDERR "starting a new stage2 with output to $outfile\n";
+    vsystem ("notify.pl $ARGV[0] $outfile $Noutfile");
+    vsystem ("stage2 $Noutfile 1>$outfile 2>&1 &");
+    exit;
+}
+goodbye "added images";
+exit;
+
+# do controller (& cleaning if needed), taking care of flats and error conditions
+sub docontroller {
+    if (-e $killfile) { goodbye "process killed with $killfile"; }
+    if (-e $clockfile) { goodbye "lock file $clockfile exists"; }
+    # break up entry line
+    $outsubdir = $_[0];
+    $in = $_[1];
+    $out = $_[2];
+    $phot = $_[3];
+    $flags = $_[4];
+    $clean = $_[5];
+    # run controller and cleanup
+    if (-e $lockfile) { goodbye "lock file exists"; }
+    vsystem ("control2 $flags -o $outsubdir $in $out $phot");
+    if (-e $clockfile) { goodbye "lock file $clockfile exists"; }
+    if (-e $lockfile) { goodbye "lock file exists"; }
+    if (-e $killfile) { goodbye "process killed with $killfile"; }
+    if ($clean) {
+	vsystem ("cleaning.pl");
+	if (-e $lockfile) { goodbye "lock file exists"; }
+    }
+}
+
+sub by_filename {
+    @value1 = split (" ", $a);
+    @value2 = split (" ", $b);
+    $value1[0] cmp $value2[0]; 
+}
+    
+# we read from in and write to out those files with 1st 0 in the mode column
+sub parse_outlist {
+    $in = $_[0];
+    $out = $_[1];
+    $mode = $_[2];
+    
+# load inlist
+    open (INLIST, $in);
+    @inarray = ();
+    while ($line = <INLIST>) {
+	chop ($line);
+	@inarray = (@inarray,$line);
+    }
+    $Nin = @inarray;
+    close (INLIST);
+    @inarray = sort by_filename @inarray;
+
+    @outarray = ();
+    for ($k = 0; $k < $Nin; $k++) {
+	@pvalues = split (" ", $inarray[$k]);
+	$more = 1;
+	while ($more) {
+	    print STDERR ".";
+	    @nvalues = split (" ", $inarray[$k+1]);
+	    if ($nvalues[0] cmp $pvalues[0]) {
+		$more = 0;
+	    } else {
+		$pvalues[1] |= $nvalues[1];
+		$pvalues[2] |= $nvalues[2];
+		$pvalues[3] |= $nvalues[3];
+		$pvalues[4] |= $nvalues[4];
+		$pvalues[5] |= $nvalues[5];
+		$k ++;
+	    }
+	}
+	# in this version, we only care about pvalues[4,5] (addstar) 
+	if (($mode == 4) && $pvalues[4] && !$pvalues[5]) {
+	    $name = $pvalues[0];
+	    $name =~ s/$dumpspace//;
+	    $name =~ s/://;
+	    @outarray = (@outarray,$name);
+	} 
+	if (($mode == 5) && $pvalues[5]) {
+	    $name = $pvalues[0];
+	    $name =~ s/$dumpspace//;
+	    $name =~ s/://;
+	    @outarray = (@outarray,$name);
+	}
+    }
+    $Nout = @outarray;
+    if ($Nout) {
+	open (OUTLIST, ">$out");
+	for ($k = 0; $k < $Nout; $k++) {
+	    print OUTLIST "$outarray[$k]\n";
+	}
+	close (OUTLIST);
+    }
+    $Nout;
+}
+
+# find lines in inlist which are not in outlist
+sub match_lists {
+    $in = $_[0];
+    $out = $_[1];
+    $new = $_[2];
+    
+# load inlist
+    open (INLIST, $in);
+    @inarray = ();
+    while ($line = <INLIST>) {
+	chop ($line);
+	@inarray = (@inarray,$line);
+    }
+    $Nin = @inarray;
+    close (INLIST);
+
+# load outlist
+    open (OUTLIST, $out);
+    @outarray = ();
+    while ($line = <OUTLIST>) {
+	chop ($line);
+	@outarray = (@outarray,$line);
+    }
+    $Nout = @outarray;
+    close (OUTLIST);
+
+# find matched lines 
+    @newarray = ();
+    for ($k = 0; $k < $Nin; $k++) {
+	$gotit = 0;
+	for ($m = 0; ! $gotit && ($m < $Nout); $m++) {
+	    if ($outarray[$m] =~ /$inarray[$k]/) {
+		$gotit = 1;
+	    }
+	}
+	if (!$gotit) {
+	    @newarray = (@newarray,$inarray[$k]);
+	}
+    }
+    $Nnew = @newarray;
+    if ($Nnew) {
+	open (NEWLIST, ">$new");
+	for ($k = 0; $k < $Nnew; $k++) {
+	    print NEWLIST "$newarray[$k]\n";
+	}
+	close (NEWLIST);
+    }
+    $Nnew;
+}
+
+sub compare_done_todo {
+    print STDERR "starting compare\n";
+# load in done date list
+    open (DONELOG, $addstarfile);
+    @donelist = ();
+    while ($line = <DONELOG>) {
+	chop ($line);
+	@donelist = (@donelist,$line);
+    }
+    $Ndone = @donelist;
+    close (DONELOG);
+
+# load in maestro list
+    open (TODOLOG, $processfile);
+    @todolist = ();
+    while ($line = <TODOLOG>) {
+	chop ($line);
+	@todolist = (@todolist,$line);
+    }
+    $Ntodo = @todolist;
+    close (TODOLOG);
+
+# find entries in MAESTRO that are NOT in DONELOG
+    for ($i = 0; $i < $Ntodo; $i ++) {
+	$gotit = 0;
+	for ($j = 0; ($j < $Ndone) && ! $gotit; $j ++) {
+	    @todo = split (" ",$todolist[$i]);
+	    @done = split (" ",$donelist[$j]);
+	    if ($todo[0] eq $done[0]) {
+		$gotit = 1;
+	    }
+	}
+	if (!$gotit) {
+	    @leftlist = (@leftlist, $todolist[$i]);  
+	}
+    }
+    $Nleft = @leftlist;
+    $Nleft;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/testlogs.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/testlogs.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/loneos/testlogs.pl	(revision 22322)
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    vsystem ("date");
+    die "@_";
+}
+
+$Nline = -1;
+while ($line = <STDIN>) {
+    $Nline ++;
+    if ($Nline == 0) {	next; }
+    if ($Nline == 1) {
+	@parts = split (" ",$line);
+	$N1 = $parts[9];
+	next;
+    }
+    if ($Nline == 2) {
+	@parts = split (" ",$line);
+	$N2 = $parts[2];
+	if ($N1 != $N2) {
+	    print STDERR "problem in dophot\n";
+	    exit;
+	}
+	next;
+    }
+    if ($Nline == 3) {
+	@parts = split (" ",$line);
+	$N1 = $parts[10];
+	next;
+    }
+    if ($Nline == 4) {
+	@parts = split (" ",$line);
+	$N2 = $parts[2];
+        if ($N1 != $N2) {
+            print STDERR "problem in fstat\n";
+	    exit;
+        }
+	next;
+    }
+    if ($Nline == 5) {
+	@parts = split (" ",$line);
+	$N1 = $parts[8];
+	next;
+    }
+    if ($Nline == 6) {
+	@parts = split (" ",$line);
+	$N2 = $parts[2];
+        if ($N1 != $N2) {
+            print STDERR "problem in gastro\n";
+            exit;
+        }
+	next;
+    }
+    if ($Nline == 7) {
+	@parts = split (" ",$line);
+	$N1 = $parts[6];
+	next;
+    }
+    if ($Nline == 8) {
+	@parts = split (" ",$line);
+	$N2 = $parts[2];
+        if ($N1 != $N2) {
+            print STDERR "problem in addstar\n";
+            exit;
+        }
+	next;
+    }
+    if ($Nline > 8) {
+	print STDERR "$line\n";
+	print STDERR "too many lines?\n";
+	exit;
+    }
+
+}
+
+if ($Nline != 8) {
+    print STDERR "not enough lines\n";
+    exit;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/new/defringe
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/new/defringe	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/new/defringe	(revision 22322)
@@ -0,0 +1,65 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 2) { &usage; }
+
+# command line arguments
+$inlist  = $ARGV[0];
+$outlist = $ARGV[1];
+
+# setup elixir references
+$coordpath = `gconfig FRINGE_COORD_PATH`;  chop $coordpath;
+$confdir   = `gconfig CONFDIR`;            chop $confdir;
+$script    = "$confdir/mana/fringe.pro";   
+
+$filtkwd   = `gconfig FILTER-KEYWORD`;     chop $filtkwd;
+$camerakwd = `gconfig CAMERA-KEYWORD`;     chop $camerakwd;
+$ccdkwd    = `gconfig CCDNUM-KEYWORD`;     chop $ccdkwd;
+
+$Nccd      = `cameraconfig -Nccd`;         chop $Nccd;
+$answer    = `cameraconfig -ccds`;
+@ccds      = split (" ", $answer);
+
+# temporary files needed:
+set imstats=`mktemp /tmp/defringe.XXXXXX`;
+set frstats=`mktemp /tmp/defringe.XXXXXX`;
+
+# calculate imstats for the input image, get stats from fringe image header
+set filtvalue = `head -1 $imlist | fields $filtkwd | awk '{print $2}'`;
+set filter = `filtnames $filtvalue`;
+
+set camera = `head -1 $imlist | fields $camerakwd | awk '{print $2}'`;
+if ("$camera" == "") set camera = "WFI";
+
+# load input / output lists:
+open (FILE, "$inlist");
+@inlist = <FILE>;
+close (FILE);
+
+open (FILE, "$outlist");
+@outlist = <FILE>;
+close (FILE);
+
+if (@inlist != @outlist) { die "ERROR: mismatched lists\n"; }
+ 
+# measure fringe amplitude
+
+
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print STDERR "USAGE: defringe (inlist) (outlist)\n";
+    exit 2;
+}
+
+sub uppercase {
+    return "\U$_[0]\E";
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/new/getfringe
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/new/getfringe	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/new/getfringe	(revision 22322)
@@ -0,0 +1,80 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 4) { die "USAGE: gtfringe (in.fits) (fringe.coords) (binning) (out.dat)\n"; }
+
+$input   = $ARGV[0];
+$coords  = $ARGV[1];
+$binning = $ARGV[2];
+$output  = $ARGV[3];
+
+# create temporary files:
+$PARFILE = `mktemp /tmp/gtfringe.XXXXXX`;
+$INFILE  = `mktemp /tmp/@gtfringe.XXXXXX`
+$OUTFILE = `mktemp /tmp/gtfringe.XXXXXX`
+
+# convert 
+
+open (FILE, "$inlist");
+@mef = <FILE>;
+close (FILE);
+
+@split = ();
+foreach $name (@mef) {
+    chop ($name);
+
+    # convert name /path/name.fits to /outdir/rootNN.fits
+    @words = split ("/", $name);
+    $root = $words[-1];
+    $root =~ s/.fits//;
+    
+    $new = sprintf "%s/%s%02d.fits", $outdir, $root, $ccd;
+    push @split, $new;
+}
+    
+open (MANA, "|mana --norc");
+
+# define the macro 'split'
+print MANA "macro split\n";
+print MANA " rd a \$1 -n $ccd\n";
+print MANA " keyword a NAXIS1 nx\n";
+print MANA " keyword a NAXIS2 ny\n";
+print MANA " if ((\$nx == 2080) && (\$ny == 4128))\n";
+print MANA "   wd a \$2 -bitpix 16 -bzero 32768 -bscale 1.0\n";
+print MANA "   exec echo \$2 >> $outlist\n";
+print MANA " end\n";
+print MANA "end\n";
+
+# create the macro 'go' with the commands 
+print MANA "macro go\n";
+for ($i = 0; $i < @mef; $i++) {
+    if (-e $split[$i]) { 
+	print STDERR "$split[$i] exists, skipping\n";
+	next; 
+    }
+    print MANA " split $mef[$i] $split[$i]\n";
+}
+# if the macro ends successfully, exit 0
+print MANA " exit 0\n";
+print MANA "end\n";
+
+# if the macro exits before the end, exit 1
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+
+$status = $?;
+print STDERR "mana exit status: $status\n";
+if ($status) {
+    print STDERR "ERROR: problem running split.mef\n";
+    exit 1;
+}
+
+print STDERR "SUCCESS\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/clear.rawdir
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/clear.rawdir	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/clear.rawdir	(revision 22322)
@@ -0,0 +1,258 @@
+#!/usr/bin/env perl
+
+# this script is very much specific to the CFHT data layout
+
+$archive = "/h/archive/current/instrument/cfh12k";
+$force = 0;
+
+# grab the command line arguments:
+@tARGV = ();
+for (; @ARGV > 0; ) {
+
+    if ($ARGV[0] eq "-force") {
+	shift;
+	$force = 1;
+	next;
+    }
+    
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 1) { die "USAGE: clean.rawdir (crunid)\n" ;}
+
+if ($ARGV[0] eq "-current") {
+    $runid = `gconfig -q RUNID`; chop ($runid);
+} else {
+    $runid = $ARGV[0];
+}
+print "runid: $runid\n";
+
+$rawdir = `gconfig -q -D RUNID $runid RAWDIR`; chop ($rawdir);
+if ($rawdir eq "") {
+    print STDERR "can't find config entry RAWDIR\n";
+    exit 1;
+}
+print "rawdir: $rawdir\n";
+
+@meflist = ();
+@splitlist = ();
+
+# load in the MEF files (must be of format 123456x.fits)
+@tlist = <$rawdir/*.fits>;
+foreach $tname (@tlist) { 
+    if ((-f $tname) && ($tname =~ /\d\d\d\d\d\d\w.fits$/)) { 
+	@meflist = (@meflist, $tname); 
+    } 
+}
+
+# load in the SPLIT directories (must be of format 123456x)
+@tlist = <$rawdir/???????>;
+foreach $tname (@tlist) { 
+    if ((-d $tname) && ($tname =~ /\d\d\d\d\d\d\w$/)) { 
+	@splitlist = (@splitlist, $tname); 
+    } 
+}
+$N1 = @meflist;
+$N2 = @splitlist;
+
+foreach $file (@meflist) {
+
+    # grab the root name:
+    $root = mefnames ($file, "root");
+    
+    $arname = "$archive/$root.fits";
+    $newname = $file;
+
+    # check that the file exists in the archive
+    if (-l $file) { next; }
+    if (! -e $arname) { next; }
+    if (! -f $arname) { next; }
+
+    unlink $file;
+    symlink $arname, $newname;
+}
+
+foreach $file (@splitlist) {
+
+    # grab the root name:
+    $root = splitnames ($file, "root");
+    
+    $arname = "$archive/$root.fits";
+    $newname = "$rawdir/$root.fits";
+
+    # check that the file exists in the archive
+    if (! -e $arname) { next; }
+    if (! -f $arname) { next; }
+
+    vsystem ("rm -rf $file\n");
+    symlink $arname, $newname;
+    vsystem ("imsearch -name $root -split2mef\n");
+}
+
+exit 0;
+
+# utilities ##############################################
+
+sub gt_names {
+    # in: filename mode type
+    # out: word
+
+    my ($value);
+
+    $value = "";
+    if ($_[2] eq "MEF")   { $value = mefnames ($_[0], $_[1]); }
+    if ($_[2] eq "SPLIT") { $value = splitnames ($_[0], $_[1]); }
+    
+    return $value;
+}
+
+sub mefnames {
+    # in: (/path/file.fits) (mode) 
+    # out: word
+
+    my($fullname) = $_[0];
+    my($mode) = $_[1];
+    my($answer);
+
+    # split rootdir and filename:
+    my (@words) = split ("/", $fullname);
+    $N = @words;
+
+    if ($mode eq "path") {
+	$answer = "";
+	for ($i = 0; $i < $N - 1; $i++) {
+	    $answer = $answer . $words[$i] . "/";
+	}
+	chop ($answer); # strip off last /
+	if ($answer eq "") { $answer = "."; }
+	return ($answer);
+    }
+    
+    if ($mode eq "root") {
+	$answer = "$words[$N-1]";
+	$answer =~ s/.fits$//;
+	return ($answer);
+    }
+    
+    if ($mode eq "Nccd") {
+	if (! -e $fullname) {
+	    return (0);
+	}
+	$answer = `echo $fullname | fields NEXTEND`;
+	@words = split (" ", $answer);
+	if ($words[1] eq "") {
+	    return (0);
+	}
+	return ($words[1]);
+    }
+    
+    return (0);
+}
+
+sub splitnames {
+    # in: (/path/file) (mode) 
+    # out: word
+    # /path/file is directory containing Nccd fits images
+
+    my($fullname) = $_[0];
+    my($mode) = $_[1];
+    my($answer, $N, $tmpname, @imlist, @words);
+
+    # split rootdir and filename:
+    @words = split ("/", $fullname);
+    $N = @words;
+
+    if ($mode eq "path") {
+	$answer = "";
+	for ($i = 0; $i < $N - 1; $i++) {
+	    $answer = $answer . $words[$i] . "/";
+	}
+	chop ($answer); # strip off last /
+	if ($answer eq "") { $answer = "."; }
+	return ($answer);
+    }
+    
+    if ($mode eq "root") {
+	$answer = "$words[$N-1]";
+	return ($answer);
+    }
+    
+    if ($mode eq "Nccd") {
+	
+	$tmpname = "$fullname/$words[$N-1]";
+	@imlist = <$tmpname??.fits>;
+
+	$Nccd = @imlist;
+	return $Nccd;
+    }
+    
+    return (0);
+}
+
+sub ckimtype {
+    # in: (/path/file)
+    # out: MEF | SPLIT | SINGLE | (NULL)
+    
+    my ($answer, $name, $value);
+    my ($file) = $_[0];
+
+    if (! -e $file) { 
+	print STDOUT "can't open file\n";
+	return ""; 
+    }
+
+    # if /path/file is a directory, it is split:
+    if (-d $file) { return "SPLIT"; }
+
+    # check that the file is a FITS image
+    $answer = `file -L $file`;
+    if ($?) { 
+	print STDOUT "can't open file\n";
+	return ""; 
+    }
+    ($name, $value) = split (" ", $answer);
+    if ($value ne "FITS") { 
+	print STDOUT "not a valid image\n";
+	return ""; 
+    }
+    
+    # check for NAXIS = 0 (MEF) 
+    $answer = `echo $file | fields NAXIS`;
+    # if ($?) { return ""; }
+    ($name, $value) = split (" ", $answer);
+    if ($value eq 0) { return "MEF"; }
+    
+    if ($value == 2) { return "SINGLE"; }
+
+    print STDOUT "not a valid image\n";
+    return "";
+}
+
+sub vsystem {
+    # print STDERR "@_\n";
+    my($status) = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+
+# This program calls several other perl scripts to get information 
+# about the image.  
+# ckrunid   - determine the appropriate Camera Run ID for this image
+# ckastrom  - check for the appropriate astrometry information
+# ckdetrend - check for the appropriate detrend images
+# ckphotom  - check for the appropriate photometry data
+
+# at the moment, there are different calling conventions for each of
+# these functions.  Some take a single name (MEF / SPLIT), some take a
+# reference to the specific CCD (filename CCD MODE).  
+
+# none of the called functions need to find the number of CCDs
+# ckvalid determines the number of CCDs from the header (MEF) or the
+# number of appropriately named FITS files in the directory (SPLIT).
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/create.imlists
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/create.imlists	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/create.imlists	(revision 22322)
@@ -0,0 +1,67 @@
+#!/usr/bin/env perl
+
+# take single list of images, create collection of lists in path, each with files for one image,
+# place list of lists in outlist
+
+if (@ARGV != 3) { die "ERROR: USAGE: create.subset (inlist) (path) (outlist)\n" }
+
+$inlist = $ARGV[0];
+$path = $ARGV[1];
+$outlist = $ARGV[2];
+
+open (FILE, $inlist);
+@inlist = <FILE>;
+close (FILE);
+foreach $name (@inlist) { chop ($name); }
+
+open (OUT, ">$outlist");
+
+while (@inlist) {
+    $name = pop (@inlist); 
+
+    @sublist = ($name);
+    ($subname, $ext) = $name =~ /(\S+)(\d\d\D+)/;
+    ($filename) = $subname =~ /(\d\d\d\d\d\d.)$/;
+    print STDERR "subname: $subname\n";
+    print STDERR "filename: $filename\n";
+    print STDERR "ext: $ext\n";
+    
+    @outlist = ();
+    while (@inlist) {
+	$name = pop (@inlist);
+	
+	if ($name =~ /$subname/) {
+	    push (@sublist, $name);
+	} else {
+	    push (@outlist, $name);
+	}
+    }
+    
+    @sublist = sort @sublist;
+
+    # file lists have names like /path/filename.imlist
+    open (FILE, ">$path/$filename.imlist");
+    foreach $name (@sublist) {
+	print FILE "$name\n";
+    }
+    close (FILE);
+
+    print OUT "$path/$filename.imlist\n";
+
+    @inlist = @outlist;
+}
+
+close (OUT);
+
+###########################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/elixir.grabd
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/elixir.grabd	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/elixir.grabd	(revision 22322)
@@ -0,0 +1,159 @@
+#!/usr/bin/env perl
+
+# this daemon creates links for new images in the elixir TMPDIR tree
+# and registers the images in the elixir database, and sends the image 
+# to imstats for processing.
+
+# the standard elixir config system is used to id the TMPDIR and the 
+# appropriate imstats.fifo, etc.
+
+# TMPDIR should be checked on each scan loop, in case the TMPDIR or
+# RUNID is switched
+
+# make sure the appropriate config file is used.
+
+use English;
+
+# check usage
+if (@ARGV != 0) { die "USAGE: elixir.grabd\n"; }
+
+# what do we do about config file?  demand account be set up correctly?
+$fifos = `gconfig FIFOS`; chop ($fifos);
+if ($?) { die "error in config system\n"; }
+
+# check for existing process and exit
+$pidfile = "$fifos/gradb.pid";
+if (-e $pidfile) {
+    $line = `head -1 $pidfile`;
+    ($ohost, $ouser, $opid) = split (" ", $line);
+
+    print STDERR "elixir.grabd is already being run:\n";
+    print STDERR " machine: $ohost\n";
+    print STDERR " user:    $ouser\n";
+    print STDERR " pid:     $opid\n";
+    print STDERR " pidfile: $pidfile\n";
+    exit 1;
+}
+
+# set process lock file:
+$host = `hostname`; chop ($host);
+$user = getlogin;
+open (FILE, ">$pidfile");
+print FILE "$host $user $PID\n";
+close (FILE);
+
+# place this in the config system?
+$srcdir = "/data/kapu/elixir/cfh12k";
+while (-e $pidfile) {
+    grabfiles ();
+    system ("sleep 60");
+}
+
+# clear process lock file:
+unlink ($pidfile);
+exit 0;
+
+
+##########################################################
+
+sub grabfiles () {
+    
+    $rawdir = `gconfig -q RAWDIR`;  chop $rawdir;
+
+    @files = glob ("$srcdir/*");
+    
+    if (! @files) {
+	system ("sleep 60");
+	return;
+    }
+
+    foreach $name (@files) {
+	if (! -l $name) { next; }
+	
+	# find real path
+	$path = convertlink ($name);
+	$file = filename ($path);
+
+	# set new path (skip if it exists)
+	$new = sprintf "%s/%s", $rawdir, $file;
+	if (-e $new) { 
+	    print STDERR "skipping file\n";
+	    next; 
+	}
+	
+	# print "$name -> $path ($file) -> $new\n";
+
+	# link file to rawdir:
+	symlink ($path, $new);
+	
+	# these files always come in as MEF
+	vsystem ("imsort $new");
+	if ($?) { next; }
+	
+	unlink $name
+    }
+}
+
+
+# utilities ##############################################
+sub vsystem {
+    print STDERR "@_\n";
+    my ($status) = system ("@_");
+    $status;
+}
+
+sub filename { 
+
+    my ($path) = $_[0];
+    my (@tmp);
+    
+    @tmp = split ("/", $path);
+    return $tmp[-1];
+}
+
+sub convertlink {
+    my ($link) = $_[0];
+    my ($file);
+
+    while (-l $link) {
+	$file  = readlink ($link);
+	$path1 = path ($link);
+	$path2 = path ($file);
+	
+	unless ($path2 =~ m|^/|) {
+	    $file = "$path1/$file";
+	}	
+	$link = $file;
+    }
+    return ($link);
+}    
+
+sub usage {
+    
+    if (@ARGV == 1) {
+	print STDERR "\n --- user modes ---\n";
+	print STDERR "create (run) (start) (stop)\n";
+	print STDERR "addqso (runid) (qsoid)\n";
+	print STDERR "delqso (qsoid)\n";
+	print STDERR "sched (runid)\n";
+	print STDERR "status\n";
+	&goodbye;
+    }
+    
+    if (@ARGV > 2) { print STDERR "USAGE: prepare.run (help) [mode]\n"; &goodbye; }
+    
+    &goodbye;
+}
+
+sub path {
+
+    my ($file) = $_[0];
+
+    @words = split ("/", $file);
+
+    pop (@words);
+    
+    $path = join ("/", @words);
+
+    return ($path);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fixlists
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fixlists	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fixlists	(revision 22322)
@@ -0,0 +1,43 @@
+#!/usr/bin/env perl
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+if (@ARGV != 3) { die "ERROR: USAGE: fixlists (in) (keep) (out)\n" }
+
+open (FILE, $ARGV[1]);
+@keep = ();
+while ($line = <FILE>) {
+    chop ($line);
+    @keep = (@keep, $line);
+}
+close (FILE);
+
+open (IN, $ARGV[0]);
+open (OUT, ">$ARGV[2]");
+
+for ($j = 0; $line = <IN>; $j++) {
+    chop ($line);
+    $found = 0;
+    for ($i = 0; !$found && ($i < @keep); $i++) {
+	if ($keep[$i] == $j) {
+	    $found = 1;
+	}
+    }
+    if ($found) {
+	print OUT "$line\n";
+    }
+}
+close (IN);
+close (OUT);
+
+print STDERR "SUCCESS\n";
+
+# put the logic for selecting images in this file
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fl.split2mef
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fl.split2mef	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fl.split2mef	(revision 22322)
@@ -0,0 +1,98 @@
+#!/usr/bin/env perl
+
+# check the usage
+if (@ARGV != 2) { die "USAGE: fl.split2mef (file.master) (splitdir)\n" }
+
+# assign the command line arguments
+$master = $ARGV[0];
+$splitdir = $ARGV[1];
+
+open (FILE, "$master");
+@master = <FILE>;
+close (FILE);
+
+# master contains lines from both split and mef.  
+# convert all to split format, and write to root.mef
+# empty the split file, re-write the master file with new names.
+# when the new mef list is run through split.mef, only un-split entries 
+# will get split
+
+# /data/koa/cfh12k/01Ak05/587554f/587554f04.fits
+# /data/elixir2/cfh12k/detrend/01Ak04/split/582967f00.fits 1
+
+foreach $line (@master) {
+    
+    ($name, $mode) = split (" ", $line);
+
+    # test filename:
+    unless ($name =~ m|/\d\d\d\d\d\d\w\d\d.fits\Z|) {
+	print STDERR "Error with $name: not in split-name format\n";
+	next;
+    }
+
+    ($path, $root1, $root2, $ccd) = $name =~ m|(\S+)/(\d\d\d\d\d\d\w)/(\d\d\d\d\d\d\w)(\d\d).fits\Z|;
+    
+    # test for originally SPLIT image:
+    if ($root1 eq $root2) {
+	# check for existence of mef version file:
+	$new = sprintf "%s/%s.fits", $path, $root1;
+	if (! -e $new)  {
+	    print STDERR "Error, file $new is missing\n";
+	    exit 1;
+	}
+	@newmef = (@newmef, $new);
+
+	# create split version name:
+	$new = sprintf "%s/%s%02d.fits", $splitdir, $root1, $ccd;
+	@split = (@split, $new);
+	@splitmode = (@splitmode, $mode);
+	next;
+    }
+
+    # test for previously MEF -> SPLIT images: 
+    if ($root1 eq "split") {
+	@mef = (@mef, $name);
+	@mefmode = (@mefmode, $mode);
+	next; 
+    }
+    
+    print STDERR "Error with $name - neither split nor mef\n";
+}
+
+# empty *.split file
+$splitname = $master;
+$splitname =~ s/master\Z/split/;
+open (FILE, ">$splitname");
+close (FILE);
+
+# create *.mef file with new mefs (to be split)
+$mefname = $master;
+$mefname =~ s/master\Z/mef/;
+print STDERR "mefname: $mefname\n";
+open (FILE, ">$mefname");
+foreach $name (@newmef) {
+    print FILE "$name\n";
+}
+close (FILE);
+
+# save *.master file
+vsystem ("cp $master $master~");
+
+# re-create *.master file
+open (FILE, ">$master");
+@all = (@mef, @split);
+@allmode = (@mefmode, @splitmode);
+for ($i = 0; $i < @all; $i++) {
+    print FILE "$all[$i] $allmode[$i]\n";
+}
+close (FILE);
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.detrend
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.detrend	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.detrend	(revision 22322)
@@ -0,0 +1,102 @@
+#!/usr/bin/env perl
+# calls flatten.flips, but no direct flips functions
+
+# check the usage
+if ($ARGV[0] eq "-h") { usage (); }
+if ($ARGV[0] eq "-help") { usage (); }
+if (@ARGV != 4) { die "USAGE: fr.detrend (path) (root) (mode) (outdir)\n" }
+
+# assign the command line arguments
+$path = $ARGV[0];
+$root = $ARGV[1];
+$mode = "\U$ARGV[2]\E";
+if (($mode ne "MEF") && ($mode ne "SPLIT")) { die "ERROR: mode must be either -split or -mef\n";}
+$outdir = $ARGV[3];
+if (! -d $outdir) { vsystem ("mkdir -p $outdir"); }
+
+# get info on input image
+$basename = mk_basename ($path, $root, $mode);
+$Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+
+for ($ccd = 0; $ccd < $Nccd; $ccd++) {
+    
+    $infile  = mk_filename ($path, $root, $mode, $ccd);
+    $outfile = mk_filename2 ($outdir, $root, "SPLIT", $ccd);
+
+    $status = vsystem ("flatten.flips -quiet $infile $outfile $ccd $mode");
+    if ($status) { die "ERROR: problem running flatten on $infile\n"; }
+
+}
+
+print STDOUT "SUCCESS: finished with fr.detrend\n";
+exit 0;
+
+sub mk_basename {
+    my ($path) = $_[0];
+    my ($root) = $_[1];
+    my ($mode) = $_[2];
+
+    if ($mode eq "MEF") {
+	$name = sprintf "%s/%s.fits", $path, $root;
+	return $name;
+    }
+    if ($mode eq "SPLIT") {
+	$name = sprintf "%s/%s", $path, $root;
+	return $name;
+    }
+    return "";
+}
+
+sub mk_filename {
+    my ($path) = $_[0];
+    my ($root) = $_[1];
+    my ($mode) = $_[2];
+    my ($ccd)  = $_[3];
+
+    if ($mode eq "MEF") {
+	$name = sprintf "%s/%s.fits", $path, $root;
+	return $name;
+    }
+    if ($mode eq "SPLIT") {
+	$name = sprintf "%s/%s/%s%02d.fits", $path, $root, $root, $ccd;
+	return $name;
+    }
+    return "";
+}
+
+sub mk_filename2 {
+    my ($path) = $_[0];
+    my ($root) = $_[1];
+    my ($mode) = $_[2];
+    my ($ccd)  = $_[3];
+
+    if ($mode eq "MEF") {
+	$name = sprintf "%s/%s.fits", $path, $root;
+	return $name;
+    }
+    if ($mode eq "SPLIT") {
+	$name = sprintf "%s/%s%02d.fits", $path, $root, $ccd;
+	return $name;
+    }
+    return "";
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "\n";
+    print "USAGE: fr.detrend (path) (root) (mode) (outdir)\n";
+    print " path:   path to MEF image or SPLIT dir\n";
+    print " root:   root name of image (MEF = root.fits, SPLIT = root/rootNN.fits)\n";
+    print " mode:   MEF or SPLIT\n";
+    print " outdir: directory for output results (all split)\n";
+    exit 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.gtstats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.gtstats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.gtstats	(revision 22322)
@@ -0,0 +1,75 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { usage (); }
+if ($ARGV[0] eq "-help") { usage (); }
+if (@ARGV != 3) { die "USAGE: fr.gtstats (inlist) (binning) (output)\n" }
+
+# define the path to include FLIPS:
+$flipsconf=`gconfig -q FLIPSCONF`; chop ($flipsconf);
+$flipspath=`gconfig -q FLIPSPATH`; chop ($flipspath);
+$ENV{'PATH'} = "$ENV{'PATH'}:$flipspath";
+$ENV{'FLIPSDIR'} = $flipsconf;
+
+# assign the command line arguments
+$inlist = $ARGV[0];
+$bin    = $ARGV[1];
+$output = $ARGV[2];
+
+# load input list:
+open (FILE, "$inlist");
+@inlist = <FILE>;
+close (FILE);
+
+# gtfringe is appending to the output file:
+unlink ($output);
+
+# we construct the Fringe Coord File from: FRINGE_COORD_PATH/CAMERA-FILTER-CCDNN-FringeCoord.reg
+$fkey      = `gconfig -q FILTER-KEYWORD`;    chop ($fkey);
+$ckey      = `gconfig -q CAMERA-KEYWORD`;    chop ($ckey);
+$coordpath = `gconfig -q FRINGE_COORD_PATH`; chop ($coordpath);
+$Nccd      = `cameraconfig -Nccd`;           chop ($Nccd);
+
+# double check lists size:
+if ($Nccd != @inlist) { die "ERROR: mis-matched image list sizes\n"; }
+
+# find filter and camera from image
+$answer = `echo $inlist[0] | fields $fkey $ckey`;
+($name, $filter, $camera) = split (" ", $answer);
+
+for ($ccd = 0; $ccd < $Nccd; $ccd++) {
+    
+    $infile    = $inlist[$ccd];
+    $coordfile = sprintf "%s/%s-%s-CCD%02d-FringeCoord.reg", $coordpath, $camera, $filter, $ccd;
+
+    # each call to gtfringe adds a line to $output 
+    vsystem ("gtfringe $infile $coordfile $bin $output");
+    if ($?) {
+	print STDERR "ERROR running gtfringe\n";
+	exit 1;
+    }
+}
+
+print STDOUT "SUCCESS: finished with fr.gtstats\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "\n";
+    print "USAGE: fr.gtstats (path) (root) (binning) (output)\n";
+    print " calculate fringe pattern strengths for images in (path)\n";
+    print " path:    path to MEF image or SPLIT dir\n";
+    print " root:    root name of image (MEF: root.fits, SPLIT: root/rootNN.fits)\n";
+    print " binning: binning factor of images\n";
+    print " outdir:  output file\n";
+    exit 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.medbin
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.medbin	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.medbin	(revision 22322)
@@ -0,0 +1,71 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { usage (); }
+if ($ARGV[0] eq "-help") { usage (); }
+if (@ARGV != 4) { die "USAGE: fr.medbin (path) (root) (factor) (outpath)\n" }
+
+# assign the command line arguments
+$path = $ARGV[0];
+$root = $ARGV[1];
+$factor = $ARGV[2];
+$outpath = $ARGV[3];
+
+$Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+
+# need some extra here: make the hierarchy needed, check exit status
+if (! -d $outpath) { vsystem ("mkdir -p $outpath"); }
+
+open (MANA, "|mana --norc");
+
+print MANA "macro mkmap\n";
+print MANA " rd a \$1\n";
+print MANA " keyword a NAXIS1 nx\n";
+print MANA " keyword a NAXIS2 ny\n";
+print MANA " medianmap a b {\$nx/$factor} {\$ny/$factor} -range 0.25 0.75\n";
+print MANA " wd b \$2\n";
+print MANA "end\n";
+
+print MANA "macro go\n";
+for ($ccd = 0; $ccd < $Nccd; $ccd++) {
+    $infile = sprintf "%s/%s%02d.fits", $path, $root, $ccd;
+    $outfile  = sprintf "%s/%s%02d.fits", $outpath, $root, $ccd;
+    print MANA " mkmap $infile $outfile\n";
+}
+print MANA " exit 0\n";
+print MANA "end\n";
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) {
+    print STDERR "ERROR running mana\n";
+    exit 1;
+}
+
+print STDOUT "SUCCESS: finished with fr.detrend\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "\n";
+    print "USAGE: fr.medbin (path) (root) (factor) (outpath)\n";
+    print " path:   path to MEF image or SPLIT dir\n";
+    print " root:   root name of image (MEF = root.fits, SPLIT = root/rootNN.fits)\n";
+    print " mode:   MEF or SPLIT\n";
+    print " outdir: directory for output results (all split)\n";
+    exit 1;
+}
+
+# mana within perl:  we embed the mana functions within a macro to trap the 
+# exit status.  If mana has an error with one of the steps, it will exit the macros and 
+# the second exit will be called. Otherwise, the "go" macro will perform the exit 0
+# and mana will exit with status 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.stats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.stats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.stats	(revision 22322)
@@ -0,0 +1,69 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { &usage (); }
+if ($ARGV[0] eq "-help") { &usage (); }
+if (@ARGV != 5) { &usage (); }
+
+# assign the command line arguments
+$list   = $ARGV[0];
+$tenbin = $ARGV[1];
+$medbin = $ARGV[2];
+$stats  = $ARGV[3];
+$range  = $ARGV[4];
+
+# check for files to be processed
+@status = stat ($list);
+if ($status[7] == 0) {
+    print STDERR "no files in source list, skipping\n";
+    exit 1;
+}
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/fringe.pro";
+
+# test named Xserver, or start vnc on specified machine
+$xhost=`gconfig -q XHOST`; chop ($xhost);
+$xdisp=`gconfig -q XDISP`; chop ($xdisp);
+if (vsystem ("xdpyinfo -display $xdisp")) {
+    print STDERR "X server is not running. start it now on $xhost\n";
+    exit 1;
+}
+$ENV{'DISPLAY'} = $xdisp;
+
+# run mana script:
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+print MANA "\$RANGE = $range\n";
+print MANA "fringelist $list $tenbin $medbin $stats\n";
+print MANA "exit 1\n";
+close (MANA);
+
+if ($?) {
+    print STDERR "ERROR problem with fr.stats\n";
+    exit 1;
+}
+
+print STDOUT "SUCCESS: finished with fr.stats\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "USAGE: fr.stats (list) (tenbin) (medbin) (stats) (range)\n";
+    exit 1;
+}
+
+# mana within perl:  we embed the mana functions within a macro to trap the 
+# exit status.  If mana has an error with one of the steps, it will exit the macros and 
+# the second exit will be called. Otherwise, the "go" macro will perform the exit 0
+# and mana will exit with status 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.subset
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.subset	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/fr.subset	(revision 22322)
@@ -0,0 +1,232 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { usage (); }
+if ($ARGV[0] eq "-help") { usage (); }
+if (@ARGV != 3) { die "ERROR: USAGE: fr.subset (master) (outdir) (subset)\n" }
+
+$inlist = $ARGV[0];
+$outdir = $ARGV[1];
+$outlist= $ARGV[2];
+
+print STDERR "ERROR: not fixed!!!\n";
+exit 1;
+
+# load in stats lines
+open (FILE,  "$ARGV[0]");
+@list = <FILE>;
+close (FILE);
+
+open (FILE, ">$outlist");
+
+foreach $line (@list) {
+    chop ($line);
+
+    # the infile contains (path) (root) (mode) (flag)
+    ($path, $root, $mode, $stat) = split (" ", $line);
+
+    if ($stat) {
+
+	$basename = mk_basename ($path, $root, $mode);
+	$Nccd = gt_names ($basename, "Nccd", $mode);
+
+	for ($ccd = 0; $ccd < $Nccd; $ccd++) {
+
+	    $outfile = mk_filename ($outdir, $root, "SPLIT", $ccd);
+
+	    print FILE "$outfile\n";
+
+	}
+   }      
+}    
+close (FILE);
+
+print STDERR "SUCCESS\n";
+
+exit 0;
+
+######## cfht MEF/SPLIT name utilities ###########
+
+sub gt_names {
+    # in: filename mode type
+    # out: word
+
+    my ($value);
+
+    $value = "";
+    if ($_[2] eq "MEF")   { $value = mefnames ($_[0], $_[1]); }
+    if ($_[2] eq "SPLIT") { $value = splitnames ($_[0], $_[1]); }
+    
+    return $value;
+}
+
+sub mefnames {
+    # in: (/path/file.fits) (mode) 
+    # out: word
+
+    my($fullname) = $_[0];
+    my($mode) = $_[1];
+    my($answer);
+
+    # split rootdir and filename:
+    my (@words) = split ("/", $fullname);
+    $N = @words;
+
+    if ($mode eq "path") {
+	$answer = "";
+	for ($i = 0; $i < $N - 1; $i++) {
+	    $answer = $answer . $words[$i] . "/";
+	}
+	chop ($answer); # strip off last /
+	if ($answer eq "") { $answer = "."; }
+	return ($answer);
+    }
+    
+    if ($mode eq "root") {
+	$answer = "$words[$N-1]";
+	$answer =~ s/.fits$//;
+	return ($answer);
+    }
+    
+    if ($mode eq "Nccd") {
+	if (! -e $fullname) { return 0; }
+	$answer = `echo $fullname | fields NEXTEND`;
+	@words = split (" ", $answer);
+	if ($words[1] eq "") { return 0; }
+	return ($words[1]);
+    }
+    
+    return (0);
+}
+
+sub splitnames {
+    # in: (/path/file) (mode) 
+    # out: word
+    # /path/file is directory containing Nccd fits images
+
+    my($fullname) = $_[0];
+    my($mode) = $_[1];
+    my($answer, $N, $tmpname, @imlist, @words);
+
+    # split rootdir and filename:
+    @words = split ("/", $fullname);
+    $N = @words;
+
+    if ($mode eq "path") {
+	$answer = "";
+	for ($i = 0; $i < $N - 1; $i++) {
+	    $answer = $answer . $words[$i] . "/";
+	}
+	chop ($answer); # strip off last /
+	if ($answer eq "") { $answer = "."; }
+	return ($answer);
+    }
+    
+    if ($mode eq "root") {
+	$answer = "$words[$N-1]";
+	return ($answer);
+    }
+    
+    if ($mode eq "Nccd") {
+	
+	$tmpname = "$fullname/$words[$N-1]";
+	@imlist = <$tmpname??.fits>;
+
+	$Nccd = @imlist;
+	return $Nccd;
+    }
+    
+    return (0);
+}
+
+sub ckimtype {
+    # in: (/path/file)
+    # out: MEF | SPLIT | SINGLE | (NULL)
+    
+    my ($answer, $name, $value);
+    my ($file) = $_[0];
+
+    if (! -e $file) { 
+	print STDOUT "can't open file\n";
+	return ""; 
+    }
+
+    # if /path/file is a directory, it is split:
+    if (-d $file) { return "SPLIT"; }
+
+    # check that the file is a FITS image
+    $answer = `file -L $file`;
+    if ($?) { 
+	print STDOUT "can't open file\n";
+	return ""; 
+    }
+    ($name, $value) = split (" ", $answer);
+    if ($value ne "FITS") { 
+	print STDOUT "not a valid image\n";
+	return ""; 
+    }
+    
+    # check for NAXIS = 0 (MEF) 
+    $answer = `echo $file | fields NAXIS`;
+    # if ($?) { return ""; }
+    ($name, $value) = split (" ", $answer);
+    if ($value eq 0) { return "MEF"; }
+    
+    if ($value == 2) { return "SINGLE"; }
+
+    print STDOUT "not a valid image\n";
+    return "";
+}
+
+sub mk_basename {
+    my ($path) = $_[0];
+    my ($root) = $_[1];
+    my ($mode) = $_[2];
+
+    if ($mode eq "MEF") {
+	$name = sprintf "%s/%s.fits", $path, $root;
+	return $name;
+    }
+    if ($mode eq "SPLIT") {
+	$name = sprintf "%s/%s", $path, $root;
+	return $name;
+    }
+    return "";
+}
+
+sub mk_filename {
+    my ($path) = $_[0];
+    my ($root) = $_[1];
+    my ($mode) = $_[2];
+    my ($ccd)  = $_[3];
+
+    if ($mode eq "MEF") {
+	$name = sprintf "%s/%s.fits", $path, $root;
+	return $name;
+    }
+    if ($mode eq "SPLIT") {
+	$name = sprintf "%s/%s/%s%02d.fits", $path, $root, $root, $ccd;
+	return $name;
+    }
+    return "";
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "\n";
+    print "USAGE: fr.detrend (path) (root) (mode) (outdir)\n";
+    print " path:   path to MEF image or SPLIT dir\n";
+    print " root:   root name of image (MEF = root.fits, SPLIT = root/rootNN.fits)\n";
+    print " mode:   MEF or SPLIT\n";
+    print " outdir: directory for output results (all split)\n";
+    exit 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/get.standards
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/get.standards	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/get.standards	(revision 22322)
@@ -0,0 +1,103 @@
+#!/usr/bin/env perl
+
+# this script is very much specific to the CFHT data layout
+
+$VERBOSE = 1;
+$stdpath = "/h/archive/current/01aq97";
+
+$run = "all";
+# grab the command line arguments:
+@tARGV = ();
+for (; @ARGV > 0; ) {
+
+    # by default, all OBJECT images are returned.  specify a run with -run
+    if ($ARGV[0] eq "-run") {
+	shift;
+	$run = $ARGV[0];
+	shift;
+	next;
+    }
+    
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 0) { die "USAGE: ckrunid [-run RunID]\n" ;}
+
+# the archive is supposed to contain only MEF images.  
+# The standard star images might get a few focus or detrend images
+# mixed in, so restrict the glob to only *o.fits images:
+@imlist = <$stdpath/*o.fits>;
+
+# find date range in run list
+foreach $name (@imlist) {
+
+    if ($run eq "all") { 
+	@outlist = (@outlist, $name);
+	next; 
+    }
+
+    $runid = `ckrunid $name`;
+    if ($?) {
+	if ($VERBOSE) { print STDERR "no crunid for $name\n"; }
+	next;
+    }
+    chop ($runid);
+    
+    if ($runid ne $run) {
+	if ($VERBOSE) { print STDERR "wrong crunid for $name ($runid vs $run)\n"; }
+	next;
+    }
+
+    @outlist = (@outlist, $name);
+}
+
+foreach $name (@outlist) {
+    print STDOUT "$name\n";
+}
+
+exit 0;
+
+###################################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub get_jd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5;
+    
+
+    return ($jd);
+
+}
+
+sub get_mjd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5 - 2400000.5;
+    
+
+    return ($jd);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/gtfringe
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/gtfringe	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/gtfringe	(revision 22322)
@@ -0,0 +1,80 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 4) { die "USAGE: gtfringe (in.fits) (fringe.coords) (binning) (out.dat)\n"; }
+
+$input   = $ARGV[0];
+$coords  = $ARGV[1];
+$binning = $ARGV[2];
+$output  = $ARGV[3];
+
+# create temporary files:
+$PARFILE = `mktemp /tmp/gtfringe.XXXXXX`;
+$INFILE  = `mktemp /tmp/@gtfringe.XXXXXX`
+$OUTFILE = `mktemp /tmp/gtfringe.XXXXXX`
+
+# convert 
+
+open (FILE, "$inlist");
+@mef = <FILE>;
+close (FILE);
+
+@split = ();
+foreach $name (@mef) {
+    chop ($name);
+
+    # convert name /path/name.fits to /outdir/rootNN.fits
+    @words = split ("/", $name);
+    $root = $words[-1];
+    $root =~ s/.fits//;
+    
+    $new = sprintf "%s/%s%02d.fits", $outdir, $root, $ccd;
+    push @split, $new;
+}
+    
+open (MANA, "|mana --norc");
+
+# define the macro 'split'
+print MANA "macro split\n";
+print MANA " rd a \$1 -n $ccd\n";
+print MANA " keyword a NAXIS1 nx\n";
+print MANA " keyword a NAXIS2 ny\n";
+print MANA " if ((\$nx == 2080) && (\$ny == 4128))\n";
+print MANA "   wd a \$2 -bitpix 16 -bzero 32768 -bscale 1.0\n";
+print MANA "   exec echo \$2 >> $outlist\n";
+print MANA " end\n";
+print MANA "end\n";
+
+# create the macro 'go' with the commands 
+print MANA "macro go\n";
+for ($i = 0; $i < @mef; $i++) {
+    if (-e $split[$i]) { 
+	print STDERR "$split[$i] exists, skipping\n";
+	next; 
+    }
+    print MANA " split $mef[$i] $split[$i]\n";
+}
+# if the macro ends successfully, exit 0
+print MANA " exit 0\n";
+print MANA "end\n";
+
+# if the macro exits before the end, exit 1
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+
+$status = $?;
+print STDERR "mana exit status: $status\n";
+if ($status) {
+    print STDERR "ERROR: problem running split.mef\n";
+    exit 1;
+}
+
+print STDERR "SUCCESS\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/images.names
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/images.names	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/images.names	(revision 22322)
@@ -0,0 +1,170 @@
+#!/usr/local/bin/perl
+
+$WANT = 0;
+
+# possible things to return:
+$FILE    = 1;
+$PATH    = 2;
+$ROOT    = 3;
+$EXT     = 4;
+$NCCD    = 5;
+$CCD     = 6;
+$BASEEXT = 7;
+$CCDLIST = 8;
+
+while (@ARGV) {
+    
+    $name = shift (@ARGV);
+
+    # provided component(s)
+    if ($name eq "-file") {
+	$file = shift (@ARGV);
+    }
+    if ($name eq "-path") {
+	$path = shift (@ARGV);
+    }
+    if ($name eq "-root") {
+	$root = shift (@ARGV);
+    }
+    if ($name eq "-base") {
+	$base = shift (@ARGV);
+    }
+    if ($name eq "-ext") {
+	$ext = shift (@ARGV);
+    }
+    if ($name eq "-baseext") {
+	$baseext = shift (@ARGV);
+    }
+    if ($name eq "-Nccd") {
+	$Nccd = shift (@ARGV);
+    }
+    if ($name eq "-ccd") {
+	$ccd = shift (@ARGV);
+    }
+    if ($name eq "-mode") {
+	$mode = "\U$ARGV[0]\E";
+	shift (@ARGV);
+    }
+
+    # desired component
+    if ($name eq "+file") {
+	if ($WANT) { &badchoice; }
+	$WANT = $FILE;
+    }
+    if ($name eq "+path") {
+	if ($WANT) { &badchoice; }
+	$WANT = $PATH;
+    }
+    if ($name eq "+root") {
+	if ($WANT) { &badchoice; }
+	$WANT = $ROOT;
+    }
+    if ($name eq "+ext") {
+	if ($WANT) { &badchoice; }
+	$WANT = $EXT;
+    }
+    if ($name eq "+baseext") {
+	if ($WANT) { &badchoice; }
+	$WANT = $BASEEXT;
+    }
+    if ($name eq "+Nccd") {
+	if ($WANT) { &badchoice; }
+	$WANT = $NCCD;
+    }
+    if ($name eq "+ccd") {
+	if ($WANT) { &badchoice; }
+	$WANT = $CCD;
+    }
+    if ($name eq "+mode") {
+	if ($WANT) { &badchoice; }
+	$WANT = $MODE;
+    }
+}
+
+if ($base eq "") { $base = $root; }
+if ($root eq "") { $root = $base; }
+if ($ext eq "") { $ext = "fits"; }
+if ($path eq "") { $path = "."; }
+if ($mode eq "") { $mode = "MEF"; }
+
+if (@ARGV != 0) { &usage; }
+if (!$WANT) { &usage; }
+
+if (0) {
+    print STDERR "want: $WANT\n";
+    print STDERR "file: $file\n";
+    print STDERR "path: $path\n";
+    print STDERR "root: $root\n";
+    print STDERR "base: $base\n";
+    print STDERR "ext: $ext\n";
+    print STDERR "baseext: $baseext\n";
+    print STDERR "Nccd: $Nccd\n";
+    print STDERR "ccd: $ccd\n";
+    print STDERR "mode: $mode\n";
+}
+
+if ($WANT == $FILE) {
+    if ($mode eq "SPLIT") {
+	$output = sprintf "%s/%s%s/%s%02d.%s", $path, $base, $baseext, $root, $ccd, $ext;
+    }
+    if ($mode eq "MEF") {
+	$output = sprintf "%s/%s%s.%s", $path, $base, $baseext, $ext;
+    }
+}
+
+if ($WANT == $PATH) {
+    # split path and filename:
+    @words = split ("/", $file);
+    $N = @words;
+
+    for ($i = 0; $i < $N - 1; $i++) {
+	$output = $output . $words[$i] . "/";
+    }
+    chop ($output);   # strip off last /
+}
+
+if ($WANT == $CCD) {
+    # split path and filename:
+    @words = split ("/", $file);
+    $N = @words;
+
+    for ($i = 0; $i < $N - 1; $i++) {
+	$output = $output . $words[$i] . "/";
+    }
+    chop ($output);   # strip off last /
+}
+
+print STDOUT "$output\n";
+exit 0;
+
+# definition of the path components:
+
+# MEF:
+# /path/root/base.ext
+
+# SPLIT:
+# /path/root/base.ext
+
+# file = 
+# 1: /data/koa/cfh12k/01Ak01/543210f.fits
+# 2: /data/koa/cfh12k/01Ak01/543210f/543210f00.fits
+# 3: /data/koa/cfh12k/01Ak01/543210f.alt/543210f00.fits
+
+# base = 543210f
+# path = /data/koa/cfh12k/01Ak01 
+# root = 543210f (1,2), 543210f.alt (3)
+# ext  = fits
+# ext = "" (1,2), ".alt" (3)
+# Nccd = 12 [for CFH12K]
+# ccd  = NN (1), 00 (2,3)
+
+sub badchoice {
+    print STDERR "only one choice allowed\n"; 
+    exit 1; 
+}
+
+sub usage {
+    print STDERR "USAGE: image.names [options]\n";
+    exit 2;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/imselect
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/imselect	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/imselect	(revision 22322)
@@ -0,0 +1,189 @@
+#!/usr/bin/env perl
+$SKIPDOME = 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    die "@_";
+}
+
+if (@ARGV != 7) { die "ERROR: USAGE: imselect (ccd) (type) (filter) (tstart) (tend) (output) (-split | -mef)\n" }
+
+$type = "\U$ARGV[1]\E";
+if ($type eq "DARK") { }
+
+if ($type eq "FLAT") {
+    # define some of the relevant parameters
+    $min = 2000;
+    $max = 60000;
+    $minrate = 100;
+    $ratescale = 70;
+    $filter = "\U$ARGV[2]\E";
+    if ($filter eq "B") { $minrate = 0.5  * $ratescale;  }
+    if ($filter eq "V") { $minrate = 1.0  * $ratescale;  }
+    if ($filter eq "R") { $minrate = 3.5  * $ratescale;  }
+    if ($filter eq "I") { $minrate = 14.2 * $ratescale; }
+    if ($filter eq "Z") { $minrate = 10.5 * $ratescale; }
+    if ($filter eq "HA")    { $min = 1000; }
+    if ($filter eq "HAOFF") { $min = 1000; }
+    # probably need to set these in a more intelligent fashion.
+}
+
+# $ccd is ccd number (see camera config), $ARGV[0] is ccd id
+$ccd = `cameraconfig -N $ARGV[0]`; chop ($ccd);
+if ($ccd eq "") { die "ERROR : can't find CCD $ARGV[0]\n"; } 
+
+$Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+$line = `cameraconfig -axes`; chop ($line);
+($Naxis1, $Naxis2) = split (" ", $line);
+
+$mode = "none";
+if ($ARGV[6] eq "-split") { $mode = "split"; }
+if ($ARGV[6] eq "-mef")   { $mode = "mef";   }
+if ($mode eq "none") {
+    die "ERROR: mode must be either -split or -mef\n";
+}
+
+@good = ();
+@marginal = ();
+
+# define filter lines for imsearch.  for flats, there may be alternate filter names to test
+@filtline = ();
+if ($type eq "FLAT") {
+    # convert given filter name to list of possible names:
+    $answer = `filtnames $ARGV[2] -all`;
+    @filtlist = split (" ", $answer);
+
+    foreach $name (@filtlist) {
+	$line = "-type flat -filter $name -mode $mode -trange $ARGV[3] $ARGV[4]";
+	push @filtline, $line;
+    }
+} 
+if ($type eq "BIAS") {
+    push @filtline, "-type bias -mode $mode -trange $ARGV[3] $ARGV[4]";
+} 
+if ($type eq "DARK") {
+    push @filtline, "-type dark -etime $ARGV[2] -mode $mode -trange $ARGV[3] $ARGV[4]";
+} 
+
+# run selection on each possible $filtline
+@list = ();
+foreach $line (@filtline) {
+    @tlist = `imsearch -ccd $ccd $line`;
+    @list = (@list, @tlist);
+}
+$Nlist = @list;
+print STDERR "found $Nlist images\n";
+
+for ($i = 0; $i < $Nlist; $i++) {
+    @words = split (" ",$list[$i]);
+    
+    if ($SKIPDOME) {
+	# a fairly lame and non-robust way to detect dome flats
+	if ($type eq "FLAT") {
+	    @answer = `echo $words[4]/$words[5] | fields OBJECT | grep -i dome`;
+	    if ($answer[0] ne "") { print STDERR "$words[5] rejected: dome flat\n"; next; }
+	}
+    }
+
+    if ($mode eq "split") {
+	# all $Nccd images must exist on disk, split files are assumed to be 
+	# in a subdirectory of their own, .fits extension assumed
+	@answer = <$words[4]/*.fits>;
+	if (@answer != $Nccd) { print STDERR "$words[5] rejected: missing images\n"; next; }
+    }
+
+    # images have to have the correct dimensions
+    if ($mode eq "split") {
+	@answer = `echo $words[4]/$words[5] | fields NAXIS1 NAXIS2`;
+	@wds = split (" ",$answer[0]);
+	$Nx = $wds[1]; $Ny = $wds[2];
+    } else {
+	@answer = `ftable -X 1 $words[4]/$words[5] | grep "^NAXIS1"`;
+	@wds = split (" ",$answer[0]);
+	$Nx = $wds[2];
+	@answer = `ftable -X 1 $words[4]/$words[5] | grep "^NAXIS2"`;
+	@wds = split (" ",$answer[0]);
+	$Ny = $wds[2];
+    }
+    if (($Nx != $Naxis1) || ($Ny != $Naxis2)) { print STDERR "$words[5] rejected: wrong dimensions\n"; next; }
+    
+    # $match is a word unique to the full mosaic image (the rootdir for split, the image for mef) 
+    if ($mode eq "split") { 
+	@answer = split ("/", $words[4]); 
+	$match = $answer[-1]; 
+    } else { 
+	$match = $words[5]; 
+    }
+    @list2 = `imsearch -name $match`;
+    $Nlist2 = @list2;
+    print STDERR "$match: $Nlist2\n";
+    if (@list2 != $Nccd) {
+	print STDERR "$words[5] rejected: missing entries in image database\n";
+	next;
+    }
+
+    if ($type eq "FLAT") {
+
+	if ($SKIPDOME) {
+	    # test for acceptable time (or is probably a dome flat)
+	    @answer = `dusktime $words[8]`;
+	    @wds = split (" ",$answer[0]);
+	    if ($wds[2] < 0) { print STDERR "$words[5] rejected: not twilight\n"; next; }
+	}
+	
+	$range = 1;
+	$rate  = 1;
+
+	# check for counts or flux out of range or any chip
+	for ($j = 0; $rate && $range && ($j < @list2); $j++) {
+	    @words2 = split (" ", $list2[$j]);
+	    
+	    # test for acceptable flux range
+	    if ($words2[11] < $min) { $range = 0; print STDERR "$words[5] rejected: signal too low: $words2[11] vs $min\n"; next; }
+	    if ($words2[11] > $max) { $range = 0; print STDERR "$words[5] rejected: signal too high: $words2[11] vs $max\n"; next; }
+	    
+	    # test for acceptable rate
+	    if (($words2[11] / $words2[7]) < $minrate) { 
+	      $rate = 0; 
+	      $F = $words2[11] / $words2[7];
+	      print STDERR "$words[5] rejected: flux too low: $F vs $minrate\n"; next; }
+	}
+	if (!$range) { next; }
+
+	@marginal = (@marginal, "$words[4]/$words[5]");
+	if (!$rate) { next; }
+    } else {
+	@marginal = (@marginal, "$words[4]/$words[5]");
+    }
+
+    @good = (@good, "$words[4]/$words[5]");
+}
+
+open (LIST, ">$ARGV[5]");
+if (@good > 5) {
+    for ($i = 0; $i < @good; $i++) {
+	print LIST "$good[$i]\n";
+	if (($type eq "BIAS") && ($i > 30)) { last; }
+	if (($type eq "DARK") && ($i > 30)) { last; }
+    }
+} else {
+    print STDERR "warning: limited number of flat images, scraping the bottom\n";
+    for ($i = 0; $i < @marginal; $i++) {
+	print LIST "$marginal[$i]\n";
+    }
+}
+close (LIST);
+print STDERR "SUCCESS\n";
+exit 0;
+
+# we need to select the list of images which satisfy a few criteria:
+# 1) all 12 CCDs must exist
+# 2) stats must be measured for each
+# 3) stats for all 12 must be within range (2000 - 40000)
+# 4) NAXIS1 == 2080 && NAXIS2 == 4128
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/merge.lists
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/merge.lists	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/merge.lists	(revision 22322)
@@ -0,0 +1,92 @@
+#!/usr/bin/env perl
+
+$update = 0;
+
+# grab the command line arguments:
+@tARGV = ();
+for (; @ARGV > 0; ) {
+
+    # new images get mode of 0, old image keep their mode
+    if ($ARGV[0] eq "-update") {
+	shift;
+	$update = 1;
+	next;
+    }
+    
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 3) { die "USAGE: merge.lists (list.split) (list.msplit) (list.master) [-update]\n" ;}
+
+# load names from SPLIT and MEF
+open (FILE, "$ARGV[0]");
+@split = <FILE>;
+close (FILE);
+
+open (FILE, "$ARGV[1]");
+@mef = <FILE>;
+close (FILE);
+
+# load old names from MASTER
+open (FILE, "$ARGV[2]");
+@master = <FILE>;
+close (FILE);
+
+# merge new entries into a single list, compare with state in master
+@names = (@split, @mef);
+
+@newname = ();
+@newmode = ();
+
+for ($i = 0; $i < @names; $i++) {
+
+    $name = $names[$i]; chop ($name);
+
+    $mode = 1;
+    if ($update) {
+	$mode = 0;
+
+      INNER:
+	foreach $line (@master) {
+	    @words = split (" ", $line);
+	    if ($words[0] eq $name) {
+		$mode = $words[1];
+		last INNER;
+	    }
+	}
+    }
+    @newname = (@newname, $name);
+    @newmode = (@newmode, $mode);
+}
+
+if (! @newname) {
+    print STDERR "ERROR: no relevant ccd images\n";
+    exit 1;
+}
+    
+# check on list: if there are none selected, select all
+$Ngood = 0;
+foreach $mode (@newmode) {
+    if ($mode) {
+	$Ngood ++;
+    }
+}
+if ($Ngood == 0) {
+    foreach $mode (@newmode) {
+	$mode = 1;
+    }
+}
+
+open (FILE, ">$ARGV[2]");
+for ($i = 0; $i < @newname; $i++) {
+    print FILE "$newname[$i] $newmode[$i]\n";
+}
+close (FILE);
+
+# this file must be accessible by www user  
+chmod 0666, $ARGV[2];
+
+print STDOUT "SUCCESS\n";
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/mkptolemy.names
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/mkptolemy.names	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/mkptolemy.names	(revision 22322)
@@ -0,0 +1,71 @@
+#!/usr/bin/env perl
+
+$Nccd = 12;
+
+if (@ARGV != 3) { die "USAGE: mkptolemy.names (name) (mode) (dir)\n" ;}
+
+$name = $ARGV[0];
+$mode = "\U$ARGV[1]\E";
+$dir = $ARGV[2];
+
+unless ($name =~ /^\d\d\d\d\d\d\w$/) {
+    print STDERR "name does not match the expected format 555555o\n";
+    exit 1;
+}
+
+for ($ccd = 0; $ccd < $Nccd; $ccd++) {
+
+    if ($mode eq "MEF") {
+	printf "%s/%s.fits %s/%s%02d %02d MEF\n", $dir, $name, $name, $name, $ccd, $ccd;
+    }
+
+    if ($mode eq "SPLIT") {
+	printf "%s/%s/%s%02d.fits %s/%s%02d %02d MEF\n", $dir, $name, $name, $ccd, $name, $name, $ccd, $ccd;
+    }
+
+}
+
+exit 0;
+
+###################################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub get_jd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5;
+    
+
+    return ($jd);
+
+}
+
+sub get_mjd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5 - 2400000.5;
+    
+
+    return ($jd);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/prepare.run
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/prepare.run	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/prepare.run	(revision 22322)
@@ -0,0 +1,349 @@
+#!/usr/bin/env perl
+
+# global vars:
+$atmach  = "druid.cfht.hawaii.edu";
+$runlist = "";
+@runlist = ();
+
+# check usage
+if (@ARGV < 1) {
+    print STDOUT "USAGE: mkrun (mode) [arguments]\n";
+    print STDOUT " mkrun help gives a complete list\n";
+    &goodbye;
+}
+if ($ARGV[0] eq "help") { &usage; }
+
+&set_globals;
+
+# call appropriate command
+if ($ARGV[0] eq "create") { &create (@ARGV); }
+if ($ARGV[0] eq "addqso") { &addqso (@ARGV); }
+if ($ARGV[0] eq "delqso") { &delqso (@ARGV); }
+if ($ARGV[0] eq "status") { &status; }
+if ($ARGV[0] eq "sched")  { &sched  (@ARGV); }
+
+print STDOUT "invalid mkrun command\n";
+exit 1;
+
+sub status {
+
+    &load_runlist;
+    foreach $line (@runlist) {
+	print STDOUT "$line\n";
+    }
+    exit 0;
+}
+
+############
+sub create {
+
+    my (@argv) = @_;
+    
+    if (@argv != 4) {
+	print STDOUT "USAGE: mkrun create (run) (start) (stop)\n";
+	exit 2;
+    }
+
+    $runid = $argv[1];
+    $start = $argv[2];
+    $stop  = $argv[3];
+
+    &load_runlist;
+    
+    $line = grab_runline ($runid);
+    ($Orunid, $Ostart, $Ostop, $qrunid) = split (" ", $line);
+    $line = mk_runline ($runid, $start, $stop, $qrunid);
+    insert_runline ($line);
+
+    &save_runlist;
+    exit 0;
+}
+
+############
+sub addqso {
+
+    my (@argv) = @_;
+    
+    if (@argv != 3) {
+	print STDOUT "USAGE: mkrun addqso (run) (qsorun)\n";
+	exit 2;
+    }
+
+    $runid = $argv[1];
+    $qrunid  = $argv[2];
+
+    &load_runlist;
+    
+    $line = grab_runline ($runid);
+    if ($line eq "") { 
+	print STDERR "run id $runid not in run list\n";
+	exit 1;
+    }
+    ($runid, $start, $stop, $Oqrunid) = split (" ", $line, 4);
+    if ($Oqrunid) {
+	$qrunid = $Oqrunid . " " . $qrunid;
+    } 
+
+    $line = mk_runline ($runid, $start, $stop, $qrunid);
+    insert_runline ($line);
+
+    &save_runlist;
+    exit 0;
+}
+
+############
+sub delqso {
+    my (@argv) = @_;
+    if (@argv != 2) {
+	print STDOUT "USAGE: mkrun addqso (qsorun)\n";
+	exit 2;
+    }
+
+    $qrunid  = $argv[1];
+
+    &load_runlist;
+    
+    # find listed qrunid
+    $match = "";
+  SEARCH:
+    foreach $line (@runlist) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	($tmp, $tmp, $tmp, $qlist) = split (" ", $line, 4);
+	@qlist = split (" ", $qlist);
+	foreach $qid (@qlist) {
+	    if ($qrunid ne $qid) { next; }
+	    $match = $line;
+	    last SEARCH;
+	}
+    }
+    if ($match eq "") {
+	print STDERR "QSO run id $qrunid not found in run list\n";
+	exit 1;
+    }
+
+    # recreate $qlist without $qrunid
+    $qlist = "";
+    foreach $qid (@qlist) {
+	if ($qid eq $qrunid) { next; }
+	$qlist = $qlist . $qid . " ";
+    }
+
+    ($runid, $start, $stop) = split (" ", $match);
+
+    $line = mk_runline ($runid, $start, $stop, $qlist);
+    insert_runline ($line);
+
+    &save_runlist;
+    exit 0;
+}
+
+##############
+
+sub sched {
+    my (@argv) = @_;
+    if (@argv != 2) {
+	print STDOUT "USAGE: mkrun sched (runid)\n";
+	exit 2;
+    }
+
+    $runid  = $argv[1];
+    &load_runlist;
+    
+    $line = grab_runline ($runid);
+    ($runid, $start, $stop, $qlist) = split (" ", $line, 4);
+
+    at_loop ($runid, $start, $stop);
+    
+    exit 0;
+}
+
+##############
+sub at_loop {
+    my ($runid, $start, $stop);
+    $runid = $_[0];
+    $start = $_[1];
+    $stop  = $_[2];
+    
+    $mjd_start = date_to_mjd ($start);
+    $mjd_stop  = date_to_mjd ($stop);
+
+    $Nday = $mjd_stop - $mjd_start;
+
+    ($year, $month, $day) = $start =~ /(\d+)\/(\d+)\/(\d+)/;
+    for ($Day = -1; $Day < $Nday; $Day++) {
+	
+	# 'at' needs date in format MM/DD/YYYY or DD.MM.YYYY
+	$tday = $day + $Day;
+	$date = `date -d $year/$month/$tday +%m/%d/%Y`; chop ($date);
+
+	if ($Day == -1) {
+	    atcommand ("clear.rawdir -current >& /h/eugene/cfh12k/logs/clear.log &", "10:00", $date);
+	}
+
+	if ($Day == 0) {
+	    atcommand ("echo RUNID $runid > /h/cfh12k/elixir.run", "10:00", $date);
+	    atcommand ("mkdetrend create $runid $start $stop >& /h/eugene/cfh12k/logs/mkdetrend.log", "10:05", $date);
+	    atcommand ("mkfringe config $runid >& /h/eugene/cfh12k/logs/mkdetrend.log", "10:10", $date);
+	    atcommand ("mkfringe mkconfig $runid >& /h/eugene/cfh12k/logs/mkdetrend.log", "10:15", $date);
+	    
+	    atcommand ("elixir -D mode imstats >& /h/eugene/cfh12k/logs/imstats.log &", "15:00", $date);
+	    atcommand ("elixir -D mode sextract >& /h/eugene/cfh12k/logs/ptolemy.log &", "15:00", $date);
+	    atcommand ("gettemps &", "15:00", $date);
+	    atcommand ("elixir.report &", "15:00", $date);
+	}
+
+	if (($Day > 0) && ($Day < 4)) {
+	    atcommand ("mkdetrend init >& /h/eugene/cfh12k/logs/mkdetrend.log; mkdetrend run >& /h/eugene/cfh12k/logs/mkdetrend.log", "07:00", $date);
+	}
+	if ($Day >= 4) {
+	    atcommand ("mkdetrend update >& /h/eugene/cfh12k/logs/mkdetrend.log", "07:00", $date);
+	}
+	if ($Day > 0) {
+	    # atcommand ("clear /h/cfh12k/images/elixir", "12:00", $date);
+	}
+    }
+}
+
+####### runlist utilities ############################
+sub load_runlist {
+    open (FILE, "$runlist");
+    @runlist = <FILE>;
+    foreach $line (@runlist) {
+	chop ($line);
+    }
+    close (FILE);
+}    
+
+sub save_runlist {
+    open (FILE, ">$runlist");
+    foreach $line (@runlist) {
+	print FILE "$line\n";
+    }
+    close (FILE);
+}    
+
+sub grab_runline {
+    my ($id) = $_[0];
+    my ($line);
+    my ($runid);
+
+    foreach $line (@runlist) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	($runid) = split (" ", $line);
+	if ($runid ne $id) { next; }
+	return ($line);
+    }
+    return ("");
+}
+    
+sub insert_runline {
+    my ($runline) = $_[0];
+    my ($line);
+    my ($runid, $id);
+
+    # match the run id lines
+    ($id) = split (" ", $runline);
+    foreach $line (@runlist) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	($runid) = split (" ", $line);
+	if ($runid ne $id) { next; }
+	$line = $runline;
+	return 1;
+    }
+    @runlist = (@runlist, $runline);
+    return 0;
+}
+
+sub mk_runline {
+    my ($runid, $start, $stop, $qrunid, $line);
+    $runid  = $_[0];
+    $start  = $_[1];
+    $stop   = $_[2];
+    $qrunid = $_[3];
+
+    $start = date_format ($start);
+    $stop  = date_format ($stop);
+    $line = sprintf "%s %s %s  %s", $runid, $start, $stop, $qrunid;
+    return ($line);
+}
+
+# utilities ##############################################
+
+sub atcommand {
+    my ($cmd, $time, $date);
+    $cmd = $_[0];
+    $time = $_[1];
+    $date = $_[2];
+
+    $line = sprintf "echo %s | at %s %s", $cmd, $time, $date;
+    print STDOUT "$line\n";
+}
+
+
+sub vsystem {
+    # print STDERR "@_\n";
+    my($status) = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub date_format {
+    my ($date) = $_[0];
+    my ($year, $month, $day);
+
+    ($year, $month, $day) = $date =~ /(\d+)\/(\d+)\/(\d+)/;
+    $date = sprintf "%4d/%02d/%02d", $year, $month, $day;
+    return ($date);
+}
+
+sub date_to_mjd {
+    my ($date) = $_[0];
+    my ($year, $month, $day, $mjd);
+
+    ($year, $month, $day) = $date =~ /(\d+)\/(\d+)\/(\d+)/;
+    $mjd = get_mjd ($year, $month, $day);
+
+    return ($mjd);
+}
+
+sub get_mjd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5 - 2400000.5;
+    
+
+    return ($jd);
+
+}
+
+sub set_globals {
+
+    # this should come fron the config system
+#    $runlist = "/data/milo/eugene/elixir/refs/cfh12k.runs";
+    $runlist = "/h/eugene/test.runs";
+}
+
+sub usage {
+    
+    if (@ARGV == 1) {
+	print STDERR "\n --- user modes ---\n";
+	print STDERR "create (run) (start) (stop)\n";
+	print STDERR "addqso (runid) (qsoid)\n";
+	print STDERR "delqso (qsoid)\n";
+	print STDERR "sched (runid)\n";
+	print STDERR "status\n";
+	&goodbye;
+    }
+    
+    if (@ARGV > 2) { print STDERR "USAGE: prepare.run (help) [mode]\n"; &goodbye; }
+    
+    &goodbye;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/pt.run
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/pt.run	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/pt.run	(revision 22322)
@@ -0,0 +1,66 @@
+#!/usr/bin/env perl
+
+# USAGE: pt.run (obsid)
+
+if (@ARGV != 1) { die "USAGE: ckvalid (obsid)\n" ;}
+
+$obsid = $ARGV[0];
+$archive = "/h/archive/current/instrument/cfh12k";
+
+# find the run id for this image
+$runid = `ckrunid $archive/$obsid.fits`;
+if ($?) {
+    print STDOUT "$obsid: no run ID\n";
+    exit 1;
+}
+chop ($runid);
+
+# try to use the flt files, if they exist, for headers
+$tmpdir = "/data/elixir2/cfh12k/tmpdata/$runid/$obsid";
+
+print "$tmpdir\n";
+
+@flt = <$tmpdir/*.flt>;
+$Nflt = @flt;
+if ($Nflt != 12) {
+    print STDOUT "not enough *.flt files ($Nflt)\n";
+    @smp = <$tmpdir/*.smp>;
+    if (@smp == 12) {
+	print STDOUT "using old *.smp files\n";
+	@flt = ();
+	foreach $smp (@smp) {
+	    $flt = $smp;
+	    $flt =~ s/smp/flt/;
+	    vsystem ("cp $smp $flt");
+	    @flt = (@flt, "$flt");
+	}
+    } else {
+	exit 1;
+    }
+}
+
+foreach $flt (@flt) {
+    $smp = $flt;
+    $smp =~ s/flt/smp/;
+
+    $sx = $flt;
+    $sx =~ s/flt/sx/;
+
+    vsystem ("imclean -sex $flt $sx $smp");
+    vsystem ("gastro -v $smp");
+
+    # imclean -sex *.flt *.sx *.smp
+    # gastro -v *.smp
+}
+
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    my($status) = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/scheduler
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/scheduler	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/scheduler	(revision 22322)
@@ -0,0 +1,248 @@
+#!/usr/bin/env perl
+
+$ENV{'PATH'} = "$ENV{'PATH'}:/apps/elixir/bin";
+
+$schedule = `gconfig SCHEDULE`; chop $schedule;
+$logdir   = `gconfig LOGDIR`;   chop $logdir;
+$log      = "$logdir/scheduler.log";
+$window   = 0.02; # ~0.5 hours. 
+$machine  = "milo.cfht.hawaii.edu";
+
+$LIST = 0;
+$ALL  = 0;
+$RUN = 0;
+
+# execute any event which is within the time range: 
+# $last + $window < event < NOW + $window
+
+# check usage
+if ($ARGV[0] eq "help")  { &usage; }
+if ($ARGV[0] eq "all")   { &list_all; }
+if ($ARGV[0] eq "list")  { &list_active; }
+if ($ARGV[0] eq "run")   { &run_active; }
+if ($ARGV[0] eq "clear") { &clear_all; }
+print STDERR "scheduler command '$ARGV[0]' not found\n";
+&usage;
+
+sub list_active {
+
+    &set_current_time;  # set $NOWdate, $NOWtime $NOWmjd
+    &load_schedule;     # load into @list
+    &find_previous;     # set $last (last runtime)
+    &find_active;       # fill @execute & @cmdlogs for active commands
+
+    for ($i = 0; $i < @execute; $i++) {
+	print "$execute[$i] ($cmdlogs[$i])\n";
+    }
+    exit 0;
+}
+
+sub run_active {
+
+    &set_current_time;
+    &load_schedule;
+    &find_previous;
+    &find_active;
+
+    # re-create schedule with new PREVIOUS:
+    if (@list && (-e "$schedule")) { 
+	system ("cp $schedule $schedule~"); 
+	open (FILE, ">$schedule");
+	foreach $line (@list) {
+	    if ($line =~ /^\s*PREVIOUS/) { 
+		printf FILE "PREVIOUS %s %s\n", $NOWdate, $NOWtime;
+		next;
+	    }
+	    
+	    print FILE "$line";
+	}    
+	close (FILE);
+    } else {
+	# create a new schedule with PREVIOUS set to NEVER
+	print STDERR "creating $schedule\n";
+	open (FILE, ">$schedule");
+	print FILE "# schedule for scheduler system\n";
+	print FILE "# E. Magnier\n";
+	print FILE "\n";
+	print FILE "# last time scheduler was run:\n";
+	printf FILE "PREVIOUS %s %s\n", $NOWdate, $NOWtime;
+	print FILE "\n";
+	close (FILE);
+    }
+
+    # launch commands one-by-one in background
+    for ($i = 0; $i < @execute; $i++) {
+	docommand ($cmdlogs[$i], $execute[$i]);
+	system ("sleep 60");
+    }
+    exit 0;
+}
+
+# utilities ##############################################
+
+sub docommand {
+    my ($log, $cmd);
+    $log  = $_[0];
+    $cmd  = $_[1];
+
+    $line = sprintf "scheduler.exec $log \"$cmd\" ";
+    vsystem ($line);
+}
+
+
+sub vsystem {
+    print STDERR "@_\n";
+    my($status) = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub load_schedule {
+    open (FILE, "$schedule");
+    @list = <FILE>;
+    close (FILE);
+}
+
+sub clear_all {
+    if (-e $schedule) {
+	vsystem ("cp $schedule $schedule~");
+	vsystem ("rm $schedule");
+    }
+    exit 0;
+}
+
+sub find_previous {
+    $last = 0;
+    $date = "NEVER";
+    # find previous runtime:
+    foreach $line (@list) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	unless ($line =~ /^\s*PREVIOUS/) { next; }
+	
+	($tmp, $date, $time) = split (" ", $line);
+	if ($date eq "NEVER") {
+	    $last = 0;
+	    last; 
+	}
+	$last = timedate_to_mjd ($time, $date);
+	last;
+    }
+    print STDERR "run scheduler: $NOWdate $NOWtime (prev: $date $time)\n";
+}
+
+sub find_active {
+
+    @execute = ();
+    @cmdlogs = ();
+    # find relevant commands
+    foreach $line (@list) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	if ($line =~ /^\s*PREVIOUS/) { next; }
+	
+	($date, $time, $cmdlog, $command) = split (" ", $line, 4);
+	if ($command eq "") { next; }
+	chop ($command);
+	
+	$mjd = timedate_to_mjd ($time, $date);
+	
+	if ($last + $window > $mjd) { next; }
+	if ($NOWmjd + $window < $mjd) { next; }
+	
+	@cmdlogs = (@cmdlogs, $cmdlog);
+	@execute = (@execute, $command);
+    }
+    
+}
+
+sub set_current_time {
+
+    ($sec, $min, $hour, $day, $mon, $year) = localtime ();
+    $mon += 1;
+    $year += 1900;
+    $NOWdate = sprintf "%s/%02d/%02d", $year, $mon, $day;
+    $NOWtime = sprintf "%02d:%02d:%02d", $hour, $min, $sec;
+    $NOWmjd = timedate_to_mjd ($NOWtime, $NOWdate);
+}
+
+sub date_format {
+    my ($date) = $_[0];
+    my ($year, $month, $day);
+
+    ($year, $month, $day) = $date =~ /(\d+)\/(\d+)\/(\d+)/;
+    $date = sprintf "%4d/%02d/%02d", $year, $month, $day;
+    return ($date);
+}
+
+sub timedate_to_mjd {
+    my ($time) = $_[0];
+    my ($date) = $_[1];
+    my ($mjd, $day);
+
+    $mjd  = date_to_mjd ($date);
+    $day += time_to_day ($time);
+
+    $mjd += $day;
+    return ($mjd);
+}
+    
+
+sub date_to_mjd {
+    my ($date) = $_[0];
+    my ($year, $month, $day, $mjd);
+
+    ($year, $month, $day) = $date =~ /(\d+)\/(\d+)\/(\d+)/;
+    $mjd = get_mjd ($year, $month, $day);
+
+    return ($mjd);
+}
+
+sub time_to_day {
+    my ($time) = $_[0];
+    my ($hour, $min, $sec, $days);
+
+    ($hour, $min, $sec) = split (":", $time);
+    $day = $hour / 24.0 + $min / 1440.0 + $sec / 86400.0;
+
+    return ($day);
+}
+
+sub get_mjd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5 - 2400000.5;
+    
+
+    return ($jd);
+
+}
+
+sub list_all {
+
+    # load schedule:
+    open (FILE, "$schedule");
+    @list = <FILE>;
+    close (FILE);
+    
+    foreach $line (@list) {
+	print "$line";
+    }
+    exit 0;
+}
+
+sub usage {
+    
+    print STDERR "USAGE: scheduler [mode]\n";
+    print STDERR " list  : list all active commands\n";
+    print STDERR " all   : list all commands\n";
+    print STDERR " run   : execute active commands\n";
+    print STDERR " clear : delete schedule\n";
+    &goodbye;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/scheduler.exec
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/scheduler.exec	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/scheduler.exec	(revision 22322)
@@ -0,0 +1,21 @@
+#!/bin/csh -f
+
+set logdir = `gconfig LOGDIR`
+set log = "$logdir/scheduler.log"
+
+if ($#argv < 2) then
+ echo "USAGE: scheduler.exec (logfile) (cmd)"
+ exit 2;
+endif
+
+set cmdlog = $1
+shift
+
+set start=`date`
+echo "start: $start - $*" >> $log
+
+csh -c "$*" >>& $cmdlog
+set stat=$status
+
+set stop=`date`
+echo "stop:  $stop $stat $*" >> $log
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/validate-1.0
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/validate-1.0	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/old/validate-1.0	(revision 22322)
@@ -0,0 +1,287 @@
+#!/usr/bin/env perl
+
+# validate 
+# -update sends the validation value to the QSO database
+#  1 - valid image
+#  2 - subvalid image (problem, but doesn't matter)
+#  3 - invalid image
+#  4 - data error (missing, corrupted image)
+
+# global constants:
+
+$TRUE  = 1;
+$FALSE = 0;
+$RETRY = $TRUE;
+
+$MODE_UNSET    = 0;
+$MODE_VALID    = 1;
+$MODE_SUBVALID = 2;
+$MODE_INVALID  = 3;
+$MODE_DATAERR  = 4;
+
+$infile = "";
+$update = $FALSE;
+# put this in the CONFIG file:
+$runlist = "/data/milo/eugene/elixir/refs/cfh12k.runs";
+
+# include the CFHT bin directory in the path so we have access to QSO functions:
+$ENV{'PATH'} = "$ENV{'PATH'}:/cfht/bin";
+
+# grab the command line arguments:
+@tARGV = ();
+for (; @ARGV > 0; ) {
+
+    if ($ARGV[0] eq "-update") {
+	shift;
+	$update = $TRUE;
+	next;
+    }
+    if ($ARGV[0] eq "-skip") {
+	shift;
+	$RETRY = $FALSE;
+	next;
+    }
+    if ($ARGV[0] eq "-infile") {
+	shift;
+	$infile = $ARGV[0];
+	shift;
+	next;
+    }
+    
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 1) { die "USAGE: validate (crunid) [-infile filename] [-update]\n" ;}
+
+$crunid = $ARGV[0];
+
+# find qso run id for this camera run id:
+open (FILE, "$runlist");
+@list = <FILE>;
+close (FILE);
+
+# find date range in run list
+foreach $line (@list) {
+    chop ($line);
+    if ($line =~ /^\#/) { next; }
+    ($Crunid, $start, $stop, $Qrunid) = split (" ", $line, 4);
+    if (($start eq "") || ($stop eq "")) { next; }
+    if ($Crunid eq $crunid) { $qrunid = $Qrunid; last; }
+}
+
+print STDERR "qso run ID(s): $qrunid\n";
+
+@valid    = ();
+@invalid  = ();
+@dataerr  = ();
+@subvalid = ();
+
+if ($infile) {
+    # load images from previous output run or source file:
+    open (FILE, $infile);
+    @rawlist = <FILE>;
+    close (FILE);
+    
+    $mode = $MODE_UNSET;
+    @imlist = ();
+    
+  RAWLIST:
+    foreach $name (@rawlist) {
+	chop ($name);
+	
+	if ($name eq "") { next RAWLIST; }
+	    
+	if ($name =~ /^valid images/) {
+	    $mode = $MODE_VALID;
+	    next RAWLIST;
+	}
+	if ($name =~ /^invalid images/) {
+	    $mode = $MODE_INVALID;
+	    next RAWLIST;
+	}
+	if ($name =~ /^subvalid images/) {
+	    $mode = $MODE_SUBVALID;
+	    next RAWLIST;
+	}
+	if ($name =~ /^dataerr images/) {
+	    $mode = $MODE_DATAERR;
+	    next RAWLIST;
+	}
+
+	# plain list of image names (/path/name.fits)
+	if ($mode == $MODE_UNSET) {
+	    # double check on name validity
+	    ($file, $junk) = split (" ", $name, 2);
+	    unless ($file =~ /\d\d\d\d\d\d\w/) { print STDERR "?"; next RAWLIST; }
+	    @imlist = (@imlist, $file);
+	    next RAWLIST;
+	    # if (! -e $file) { print STDERR "x"; next RAWLIST; }
+	}
+	# place image names in the appropriate list
+	if ($mode == $MODE_VALID) {
+	    ($file, $result) = split (":", $name, 2);
+	    @valid = (@valid, $file);
+	    next RAWLIST;
+	}
+	if ($mode == $MODE_INVALID) {
+	    ($file, $result) = split (":", $name, 2);
+	    @invalid = (@invalid, $file);
+	    next RAWLIST;
+	}
+	if ($mode == $MODE_SUBVALID) {
+	    ($file, $result) = split (":", $name, 2);
+	    @subvalid = (@subvalid, $file);
+	    next RAWLIST;
+	}
+	if ($mode == $MODE_DATAERR) {
+	    ($file, $result) = split (":", $name, 2);
+	    @dataerr = (@dataerr, $file);
+	    next RAWLIST;
+	}
+    }
+} else {
+    # load images directly from directory 
+    # this is not sufficient.  we need a better way to get a list of images for 
+    # a given run id (with the current MEF / SPLIT state correct).
+    # 
+    # probably we can just use imsearch -ccd 0
+    $rawdir = `gconfig -q -D RUNID $crunid RAWDIR`;
+    chop ($rawdir);
+    if ($rawdir eq "") {
+	print STDERR "can't find config entry TMPDIR\n";
+	exit 1;
+    }
+    @imlist = ();
+
+    # load in the MEF files (must be of format 123456x.fits)
+    @tlist = <$rawdir/*.fits>;
+    foreach $tname (@tlist) { 
+	if ((-f $tname) && ($tname =~ /\d\d\d\d\d\d\w.fits/)) { 
+	    @imlist = (@imlist, $tname); 
+	} 
+    }
+
+    # load in the SPLIT directories (must be of format 123456x)
+    @tlist = <$rawdir/???????>;
+    foreach $tname (@tlist) { 
+	if ((-d $tname) && ($tname =~ /\d\d\d\d\d\d\w/)) { 
+	    @imlist = (@imlist, $tname); 
+	} 
+    }
+}
+
+# load the QSO validated image list
+@qsovalid = ();
+if ($qrunid) { 
+    @qruns = split (" ", $qrunid);
+    @qsovalid = ();
+    foreach $id (@qruns) {
+	print STDERR "trying $id\n";
+	@answer = `select_val_xexp.sh -U qso_elixir -P op1eliw -qrunid $id`;
+	if ($?) {
+	    print STDERR "error in qso run ID\n";
+	    exit 1;
+	}
+	@qsovalid = (@qsovalid, @answer);
+    }
+}
+foreach $qname (@qsovalid) { chop ($qname); }
+
+if ($RETRY) {
+# evaluate the remaining entries in imlist & invalid:
+    @imlist = (@imlist, @invalid, @dataerr);
+    @invalid = ();
+    @dataerr = ();
+    
+  IMLIST:
+    foreach $name (@imlist) {
+	$answer = `ckvalid $name`;
+	if ($?) { print STDERR "error with $name\n"; }
+	chop ($answer);
+	
+	($file, $result) = split (" ", $answer, 2);
+	@words = split (" ", $answer);
+	
+	if ($words[1] eq "pass") {
+	    @valid = (@valid, $answer);
+	    next IMLIST;
+	}
+	
+	if ($result eq "binned image") {
+	    @subvalid = (@subvalid, $answer);
+	    next IMLIST;
+	}
+	
+	if ($result eq "sub-rastered image") {
+	    @subvalid = (@subvalid, $answer);
+	    next IMLIST;
+	}
+	
+	if ($result eq "wrong image type") {
+	    @subvalid = (@subvalid, $answer);
+	    next IMLIST;
+	}
+	
+	if ($result eq "can't open file") {
+	    @dataerr = (@dataerr, $answer);
+	    next IMLIST;
+	}
+	
+	if ($result eq "no run ID") {
+	    @dataerr = (@dataerr, $answer);
+	    next IMLIST;
+	}
+	
+	if ($result eq "not a valid image") {
+	    @dataerr = (@dataerr, $answer);
+	    next IMLIST;
+	}
+	
+	if ($words[1] eq "fail") {
+	    foreach $line (@qsovalid) {
+		($qname, $qmode, $emode) = split (" ", $line);
+		if ($name =~ /$qname/) {
+		    @invalid = (@invalid, $answer);
+		    next IMLIST;
+		}
+	    }
+	    @subvalid = (@subvalid, $answer);
+	    next IMLIST;
+	}
+	
+	print STDERR "unknown result $answer\n";
+	
+    }
+}
+
+@mode = ('valid',     'subvalid',      'invalid',     'dataerr');
+@code = ($MODE_VALID, $MODE_SUBVALID,  $MODE_INVALID, $MODE_DATAERR);
+
+for ($i = 0; $i < @mode; $i++) {
+    $mode = $mode[$i];
+    $code = $code[$i];
+
+    print STDOUT "$mode images\n";
+    foreach $file (@$mode) {
+	print STDOUT "$file\n";
+	if ($update) { 
+	    @w = split ("/", $file);
+	    $n = substr ($w[-1], 0, 6);
+	    vsystem ("update_xexpe.sh -U qso_elixir -P op1eliw --obsid $n --eval $code >> test.list");
+	}
+    }
+    print STDOUT "\n";
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    my($status) = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/cadc2qso
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/cadc2qso	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/cadc2qso	(revision 22322)
@@ -0,0 +1,46 @@
+#!/usr/local/bin/perl
+# extract mosaic IQ values from table, send to QSO db
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+if (@ARGV != 1) { die "USAGE: cadc2qso (table.fits)\n"; }
+
+# update the following entries to the QSO database (one per mosaic):
+
+# --iq_center (OBS_IQ_CENTER)
+# --iq_r_ratio (OBS_IQ_R_RATIO)
+# --iq_x_ratio (OBS_IQ_X_RATIO)
+# --iq_y_ratio (OBS_IQ_Y_RATIO)
+
+# add error checking on the ftable call...
+@obsid = `ftable -column OBSID $ARGV[0]`;
+@iqcen = `ftable -column OBS_IQ_CENTER $ARGV[0]`;
+@iqr   = `ftable -column OBS_IQ_R_RATIO $ARGV[0]`;
+@iqx   = `ftable -column OBS_IQ_X_RATIO $ARGV[0]`;
+@iqy   = `ftable -column OBS_IQ_Y_RATIO $ARGV[0]`;
+
+foreach $value (@obsid) { chop $value; }
+foreach $value (@iqcen) { chop $value; }
+foreach $value (@iqr)   { chop $value; }
+foreach $value (@iqx)   { chop $value; }
+foreach $value (@iqy)   { chop $value; }
+
+$Nrow = @obsid;
+if ($Nrow != @iqcen) { &goodbye ("error in OBS_IQ_CENTER"); }
+if ($Nrow != @iqr) { &goodbye ("error in OBS_IQ_R_RATIO"); }
+if ($Nrow != @iqx) { &goodbye ("error in OBS_IQ_X_RATIO"); }
+if ($Nrow != @iqy) { &goodbye ("error in OBS_IQ_Y_RATIO"); }
+
+for ($i = 0; $i < $Nrow; $i++) {
+    vsystem ("/cfht/bin/update_xexpe.sh -U qso_elixir -P op1eliw --obsid $obsid[$i] --iq_center $iqcen[$i] --iq_r_ratio $iqr[$i] --iq_x_ratio $iqx[$i] --iq_y_ratio $iqy[$i]");
+}
+print STDOUT "SUCCESS: done with $ARGV[0]\n";
+exit (0);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/cfht.names
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/cfht.names	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/cfht.names	(revision 22322)
@@ -0,0 +1,49 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 1) {
+    print STDERR "USAGE: cfht.names (file)\n";
+    exit 2;
+}
+
+# given (filename) : return (path) (name) (mef)
+
+if (! -e $ARGV[0]) {
+    print STDERR "file $ARGV[0] not found\n";
+    exit 1;
+}
+
+$fullname = $ARGV[0];
+
+# if we get a relative path, add cwd:
+@words = split ("/", $ARGV[0]);
+if ($words[0] ne "") {
+    $cwd=`pwd`;
+    chop ($cwd);
+    $fullname = "$cwd/$ARGV[0]";
+}
+# we are now guaranteed to have a /complete/path/filename
+
+# if file is a directory, we assume it is split:
+if (-d $fullname) {
+    $mef = "off";
+} else {
+    $mef = "on";
+}
+
+# split path and filename:
+$path = "/";
+@words = split ("/", $fullname);
+$N = @words;
+for ($i = 1; $i < $N - 1; $i++) {
+    $path = $path . $words[$i] . "/";
+}
+chop ($path);   # strip off last /
+
+$file = $words[$N-1];
+
+if ($mef eq "on") {
+    $file =~ s/.fits$//;
+}
+
+print STDOUT "$path $file $mef\n";
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/checkconfig
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/checkconfig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/checkconfig	(revision 22322)
@@ -0,0 +1,464 @@
+#!/usr/bin/env perl
+
+@ARGV = &elixir_config (@ARGV);
+if (@ARGV == 0) { &usage; }
+
+&pnotice;
+&pnotice ("current run ID: $run");
+&pnotice ("current camera: $camera");
+&pnotice;
+
+{ # check for valid camera
+    if ($camera eq "cfh12k")  { last; }
+    if ($camera eq "cfhtir")  { last; }
+    if ($camera eq "megacam") { last; }
+    if ($camera eq "meganorth") { last; }
+    &escape ("camera $camera not supported, skipping");
+}
+
+if ($ARGV[0] eq "init")   { 
+    &ck_dirs; 
+    &ck_flocks; 
+    &ck_fifos; 
+    
+    if ($camera eq "cfh12k") { &ck_detrend; }
+    if ($camera eq "megacam") { &ck_detrend; }
+    if ($camera eq "meganorth") { &ck_detrend; }
+    &goodbye; 
+}
+# if ($ARGV[0] eq "unlock") { &ck_dirs; &ck_flocks; &cl_locks; &goodbye; }
+
+&escape ("checkconfig command $ARGV[0] not valid");
+
+### main user functions
+
+# check state of directories
+sub ck_detrend {
+
+    $answer = `mkdetrend state -camera $camera -run $run`;
+    if ($VERBOSE) { 
+	&pnotice ("checking mkdetrend system");
+	push @notices, $answer; 
+    }
+    if ($?) {
+	&pnotice ("define mkdetrend $camera $run");
+	$answer = `mkdetrend create run $run`;
+	if ($?) {
+	    &pwarning ("can't define mkdetrend $camera $run");
+	    push @warnings, $answer;
+	}
+    }
+
+    $answer = `mkfringe state -camera $camera -run $run`;
+    if ($VERBOSE) { 
+	&pnotice ("checking mkfringe system");
+	push @notices, $answer; 
+    }
+    if ($?) {
+	&pnotice ("define mkfringe $camera $run");
+	$answer = `mkfringe create run $run`;
+	if ($?) {
+	    &pwarning ("can't define mkfringe $camera $run");
+	    push @warnings, $answer;
+	}
+    }
+}
+
+sub ck_dirs {
+    my ($i, @dirs, @need);
+
+    @dirs = ('RAWDIR', 'TMPDIR', 'HDXDIR', 'CATDIR', 'DATDIR', 'FIFOS');
+    @need = ( 20,       5,        1,        0.2,      2,        0);
+
+    if ($VERBOSE) { 
+	foreach $dir (@dirs) {
+	    $filename = `gconfig $dir`; chop ($filename);
+	    &pnotice ("check $dir : $filename");
+	}
+	&pnotice;
+    }
+
+    for ($i = 0; $i < @dirs; $i++) {
+	checkdir ($dirs[$i], 0777, $need[$i]);
+    }
+    if ($VERBOSE) { &pnotice; }
+    return 0;
+}
+
+# check state of files with locks
+sub ck_flocks { 
+
+    @files = ('REGISTRATION_DATABASE');
+    foreach $file (@files) {
+	&checklocks ($file, 0666);
+    }
+    if ($VERBOSE) { &pnotice; }
+}
+
+# check elixir fifo files
+sub ck_fifos {
+    @elixirs = ('imstats', 'ptolemy');
+    @fifos = ('global.source', 'global.msg', 'global.end');
+
+    foreach $elixir (@elixirs) {
+	if ($VERBOSE) { &pnotice ("check fifos for elixir $elixir"); }
+	foreach $fifo (@fifos) {
+	    $filename = `gconfig -D mode $elixir $fifo`; chop ($filename);
+	    setmode ($filename, 0666);
+	}
+    }
+    if ($VERBOSE) { &pnotice; }
+}
+
+sub cl_locks { 
+
+    @files = ('REGISTRATION_DATABASE');
+    foreach $file (@files) {
+
+	$filename = `gconfig $file`;  chop ($filename);
+	$lock = lockname ($filename);
+
+	if (!cklock ($lock) && $unlock) { setlock ($lock); }
+    }
+}
+
+sub ckelixir {
+    my($elixir) = $_[0];
+    
+    if ($VERBOSE) { &pnotice ("Checking on Elixir $elixir"); }
+    
+    $answer = `elixir -status -D mode $elixir`;
+    if ($?) { }
+    if ($VERBOSE) { push @notices, $answer; }
+
+    return (1);
+}
+
+# check that directory exists, create it if not, set permissions
+sub checkdir {
+    # in: dir-key, mode, bytes
+    # out: status
+
+    my ($dir) = $_[0];
+    my ($mode) = $_[1];
+    my ($bytes) = $_[2];
+    my ($status);
+
+    # set camera, runid from .current
+    $filename = `gconfig $dir`; chop ($filename);
+
+    # this will make a dir if the parent doesn't exist.
+    if (! -e $filename) {
+	$status = mkdirhier ($filename, $mode);
+	if (!$status) {
+	    &pwarning ("can't create directory $filename ($dir)");
+	    return 0;
+	}
+    }
+
+    # set mode to desired value
+    if (!ckmode ($filename, $mode)) { 
+	if (!chmod $mode, $filename) {
+	    &pwarning ("can't set permissions on $filename ($dir)");
+	    return 0;
+	}
+    }	    
+
+    # check available space
+    if ($bytes) {
+	$left = `sdf $filename`;
+	$left = sprintf "%5.1f", ($left / 1024 / 1024);
+	if ($VERBOSE) { &pnotice ("$dir : $left GB free : $filename"); }
+	if ($left < $bytes) {
+	    &pwarning ("$filename ($dir) is getting full");
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+# check existence & permissions on file & corresponding lockfile
+sub checklocks { 
+
+    my ($file) = $_[0];
+    my ($mode) = $_[1];
+    
+    $filename = `gconfig $file`;  chop ($filename);
+    $lock = lockname ($filename);
+
+    if ($VERBOSE) { &pnotice ("check $file ($filename)"); }
+
+    # create file if it does not exist
+    setmode ($filename, $mode);
+
+    # create file if it does not exist
+    setmode ($lock, $mode);
+
+    return 1;
+}
+
+# basic utilities
+
+sub lockname {
+    # in: path/file
+    # out: path/.file.lck
+
+    my($lock, $N, $i);
+
+    @words = split ("/", $_[0]);
+    
+    $lock = "";
+    $N = @words - 1;
+    for ($i = 0; $i < $N; $i++) {
+	$lock = $lock . $words[$i] . "/";
+    }
+    $lock = $lock . "." . "$words[$N]" . ".lck";
+
+    return ($lock);
+}
+
+sub ckmode {
+    # in: file mode
+    # out: yes/no
+
+    $filename = $_[0];
+    $mode = $_[1];
+    $setmode = $mode;
+
+    unless (@fstat = stat ($filename)) {
+	if ($VERBOSE) { &pwarning ("$filename not found"); }
+	return 0;
+    }
+
+    $fmode = 07777 & $fstat[2];
+    $status = !($setmode ^ $fmode);
+
+    if (!$status && $VERBOSE) {
+	$line = sprintf "$filename has wrong mode: %o vs %o\n", $fmode, $setmode;
+	&pnotice ($line);
+    }
+
+    return ($status);
+}
+
+sub cklock {
+    # in: file
+    # out: yes/no
+
+    $filename = $_[0];
+
+    if (! -e $filename) { return (1); }
+
+    open (FILE, $filename);
+    $word = <FILE>;
+    close (FILE);
+
+    unless ($word =~ /^IDLE/) {
+	&pwarning ("$filename is locked");
+	return 0;
+    }
+
+    return 1;
+}
+
+# create file & set mode 
+sub setmode {
+    # in: file mode
+    # out: yes/no
+
+    $filename = $_[0];
+    $mode = $_[1];
+
+    if (! -e $filename) {
+	if (!open (FILE, ">$filename")) {
+	    &pwarning ("can't create file $filename");
+	    return 0;
+	}
+	close (FILE);
+    }
+    
+    if (!ckmode ($filename, $mode)) {
+	if (!chmod $mode, $filename) {
+	    &pwarning ("can't set mode on $filename");
+	    return 0;
+	}
+    }
+
+    return 1;
+}
+
+sub setlock {
+    # in: file
+    # out: yes/no
+
+    $filename = $_[0];
+
+    unless (open (FILE, ">$filename")) {
+	&pwarning ("can't set lock $filename");
+	return 0;
+    }
+    print FILE "IDLE";
+    close (FILE);
+    return 1;
+}
+
+sub mkdirhier {
+    # in: directory mode
+    # out: status
+
+    my ($dir) = $_[0];
+    my ($mode) = $_[1];
+    my (@words, $first, $last, $i, $part);
+
+    @words = split ("/", $dir);
+
+    foreach $level (@words) {
+	if ($level eq "") { $part = "/"; next; }
+	$part = $part . $level; 
+
+	if (-e $part && !-d $part) { return 0; }
+	if (! -e $part && !mkdir ($part, $mode)) { return 0; }
+	chmod $mode, $part;
+
+	$part = $part . "/";
+    }
+    return 1;
+}
+
+sub usage {
+    print STDERR "USAGE: checkconfig [command]\n";
+    print STDERR "  check elixir configuration & files\n";
+    exit 2;
+}
+
+sub escape { 
+    &dump_output;
+    if ($_[0]) { print "$_[0]\n"; }
+    unlink $tmpenv;
+    exit 1;
+}
+
+sub goodbye { 
+    &dump_output;
+    if ($_[0]) { print "$_[0]\n"; }
+    unlink $tmpenv;
+    exit 0;
+}
+
+sub pwarning {
+    push @warnings, $_[0];
+}
+
+sub pnotice {
+    push @notices, $_[0];
+}
+
+sub dump_output {
+    # output to screen
+    if (@warnings) {
+	print STDERR "\n--- problem with elixir system checkconfig --\n\n";
+	foreach $line (@warnings) { print STDERR "$line\n"; }
+	print STDERR "\n--- checkconfig output ---\n";
+    }
+    foreach $line (@notices) { print STDERR "$line\n"; }
+
+    if ($notify && @warnings) {
+	$answer = `mkrun sys notify`;
+	@list = split (",", $answer);
+	open (MAIL, "|mail -s 'elixir checkconfig error' @list");
+	print MAIL "problem with elixir system checkconfig:\n";
+	foreach $line (@warnings) { print MAIL "$line\n"; }
+	print MAIL "\n--- checkconfig output ---\n";
+	foreach $line (@notices) { print MAIL "$line\n"; }
+	close (MAIL);
+    }
+}
+
+# grab complete configuration info from the elixir config system, 
+# applying appropriate command-line arguments
+sub elixir_config {
+    my (@argv) = @_;
+    my ($opt, $config, @tmparg, $status);
+    $config = "";
+    if ($ENV{'PTOLEMY'}) { $config = "-c $ENV{'PTOLEMY'}"; }
+    umask (0);
+    $notify = 0;
+    $VERBOSE = 0;
+
+    # look for optional command-line arguments
+    @tmparg = @argv;
+    foreach (@tmparg) {
+	$value = shift @argv;
+
+	if ($value eq "-h")     { &usage; }
+	if ($value eq "--help") { &usage; }
+
+	if ($value eq "-c") {
+	    $value = shift @argv;
+	    $config = "-c $value";
+	    next;
+	}
+
+	if ($value eq "-C") {
+	    push @opt, $value;
+	    $value = shift @argv;
+	    push @opt, $value;
+	    $elconf = $value;
+	    next;
+	}
+	
+	if ($value eq "-C") {
+	    push @opt, $value;
+	    $value = shift @argv;
+	    push @opt, $value;
+	    $value = shift @argv;
+	    push @opt, $value;
+	    next;
+	}
+	
+	if ($value eq "-run") {
+	    $run = shift @argv;
+	    push @opt, "-D RUNID.CURRENT $run -D RUNID $run";
+	    next;
+	}
+	if ($value eq "-camera") {
+	    $camera = shift @argv;
+	    push @opt, "-D CAMERA.CURRENT $camera -D CAMERA $camera";
+	    next;
+	}
+	if ($value eq "-notify") {
+	    $notify = 1;
+	    next;
+	}
+	if ($value eq "-v") {
+	    $VERBOSE = 1;
+	    next;
+	}
+	push @argv, $value;
+    }
+
+    # load RUNID from state.data (unless set on command line)
+    if ($run eq "") {
+	$run = `gconfig RUNID.CURRENT`; chop $run;
+	if ($?) {  &escape ("run is not defined: use 'mkrun sys'\n"); }
+	if ($run eq "") {  &escape ("run is not defined: use 'mkrun sys'\n"); }
+	push @opt, "-D RUNID.CURRENT $run -D RUNID $run";
+    }
+
+    # load CAMERA from state.data (unless set on command line)
+    if ($camera eq "") {
+	$camera = `gconfig CAMERA.CURRENT`; chop $camera;
+	if ($?) {  &escape ("camera is not defined: use 'mkrun sys' to set\n"); }
+	if ($camera eq "") {  &escape ("camera is not defined: use 'mkrun sys'\n"); }
+	push @opt, "-D CAMERA.CURRENT $camera -D CAMERA $camera";
+    }
+
+    # set env PTOLEMY using values from command line
+    $config = join (" ", $config, @opt);
+    $tmpenv = `mktemp /tmp/elixir.XXXXXX`; chop ($tmpenv);
+    $status = system ("gconfig -raw $config > $tmpenv");
+    if ($status) { &escape ("error in elixir configuration: gconfig fails"); }
+    $ENV{'PTOLEMY'} = "$tmpenv";
+
+    return (@argv);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckastrom
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckastrom	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckastrom	(revision 22322)
@@ -0,0 +1,58 @@
+#!/usr/bin/env perl
+
+# we need a mechanism to define the appropriate ext value 
+
+if (@ARGV != 3) { die "USAGE: ckastrom (run) (root) (Nccd)\n" ;}
+
+$runid = $ARGV[0];
+$root = $ARGV[1];
+$Nccd = $ARGV[2];
+
+$tmpdir = `gconfig -q -D RUNID $runid HDXDIR`; chop ($tmpdir);
+if ($?) {
+    print STDERR "can't find config entry HDXDIR\n";
+    exit 1;
+}
+chop ($runid);
+
+$Nbad = 0;
+$Nmiss = 0;
+for ($ccd = 0; $ccd < $Nccd; $ccd++) {
+
+    $found = 0;
+
+    $name = sprintf "%s/%s/%s%02d.hdx", $tmpdir, $root, $root, $ccd;
+    if (-e $name) {
+	$found = 1;
+	$answer = `echo $name | fields NASTRO`;
+	@words = split (" ", $answer);
+	if ($words[1] > 0) { next; }
+    }
+
+    if (! $found) { $Nmiss ++; }
+    $Nbad ++;
+}
+
+if (!$Nbad) {
+    print STDOUT "pass\n";
+    exit 0;
+} 
+if ($Nbad < 0.4*$Nccd) {
+    print STDOUT "pass $Nbad bad\n";
+    exit 0;
+}
+print STDOUT "$Nbad bad\n";
+exit 1;
+
+# utilities ############################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckdetrend
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckdetrend	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckdetrend	(revision 22322)
@@ -0,0 +1,55 @@
+#!/usr/bin/env perl
+
+# the specific CCD is specified on the command line
+
+if ((@ARGV != 3) && (@ARGV != 4)) { die "USAGE: ckdetrend (name) (ccd) (mode) [-fringe]\n" ;}
+
+$fringe = 0;
+if (@ARGV == 4) { $fringe = 1; }
+
+$name = $ARGV[0];
+$ccd  = $ARGV[1];
+$mode = "\U$ARGV[2]\E";
+
+if (($mode ne "SPLIT") && ($mode ne "MEF")) {
+    print STDERR "mode must be SPLIT or MEF\n";
+    exit (2);
+}
+
+if ($fringe) {
+    @type = ("fringe");
+} else {
+    @type = ("mask", "bias", "dark", "flat");
+}
+
+@missing = ();
+
+foreach $type (@type) {
+    if (vsystem ("detsearch -image $name $ccd $mode -type $type")) {
+	print STDERR "can't find $type\n";
+	@missing = (@missing, $type);
+    }
+}
+
+if (@missing) {
+    print STDOUT "$name fails, missing: @missing\n";
+    exit 1;
+} else {
+    print STDOUT "$name: pass\n";
+    exit 0;
+}
+
+# utilities ############################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+### this is no needed anymore - superceeded by detsearch -mosaic filename.fits -recipe
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckimregdb
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckimregdb	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckimregdb	(revision 22322)
@@ -0,0 +1,116 @@
+#!/usr/bin/env perl
+
+# this script deals with:
+# - archived images which have not been reported to elixir,
+# - images which need to be reprocessed,
+# - images which can be send to CADC
+
+if (@ARGV != 2) { die "USAGE: ckimregdb (start) (range)\n" ;}
+
+# imsearch and ardrange12k have different default hr:mn:sc values
+# this script converts to the ardrange12k defaults: 12:00:00
+
+# WARNING: define a ref CCD for seeing
+
+# test for current camera?
+
+system ("gconfig CAMERA");
+exit; 
+
+$start = $ARGV[0];
+$range = $ARGV[1];
+
+# force explicit starting hours:
+@words = split (/\D/, $start);
+if (@words == 3) { $start = "$start,12:00:00"; }
+@words = split (/\D/, $range);
+if (@words == 3) { $range = "$range,12:00:00"; }
+
+# get list of images in the archive & imreg.db:
+if ($range =~ m|^-\d+[dhms]|) {
+    $answer = `/h/archive/sw/tools/ardrange12k $start$range`;
+} else {
+    $answer = `/h/archive/sw/tools/ardrange12k $start+$range`;
+}
+
+@ccds = split (" ", `cameraconfig -ccds`);
+if ($?) { &escape ("error with elixir camera configuration"); }
+
+@archive = split (" ", $answer);
+$N = @archive;
+print "archive: $N files\n";
+
+@imregdb = `imsearch -ccd $ccds[0] -trange $start $range -tz 10.0`;
+$N = @imregdb;
+print "imregdb: $N files\n";
+
+# search for missing images in archive
+ ARCH: foreach $arch (@archive) {
+     
+     # is $arch in @imregdb?
+     foreach $imreg (@imregdb) {
+	 @words = split (" ", $imreg);
+	 if ($arch eq $words[5]) { next ARCH; }
+     }
+     
+     push @missing, $arch;
+ }
+
+$Nmiss = @missing;
+print "$Nmiss files missing\n";
+
+# link each missing image in /data/kapu/elixir/cfh12k:
+$linkdir = `gconfig $opts ARCLINK_DIR`;  chop $linkdir;
+if ($?) { die "error in config system: missing ARCLINK_DIR\n"; }
+
+$archive = `gconfig $opts ARCHIVE_DIR`;  chop $archive;
+if ($?) { die "error in config system: missing ARCHIVE_DIR\n"; }
+
+# add missing links, elixir.link will add them to db
+foreach $file (@missing) {
+
+    print STDERR "link: $file\n";
+    $arcfile  = sprintf "%s/%s", $archive, $file;
+    $linkfile = sprintf "%s/%s", $linkdir, $file;
+    
+    if (-l $linkfile) { 
+	print STDERR "$linkfile exists, skipping\n";
+	next;
+    }
+    symlink ($linkfile, $arcfile);
+}    
+    
+# at this point, imstats from the last night is still running
+# the unprocessed images and the newly linked images will be 
+# added to the imstats fifo (not ptolemy, don't bother!)
+
+# find images which need to be re-processed (entire run)
+$runid = `gconfig RUNID.CURRENT`; chop $runid;
+$answer = `mkrun run $runid`;
+($tmp, $start, $stop) = split (" ", $answer);
+
+$fifo = `gconfig imstats`; chop $fifo;
+
+# the db lookup may take a long time or fail, the cat probably won't
+system ("imsearch -trange $start $stop -proc f > $fifo.tmp");
+if (! -s "$fifo.tmp") {
+    system ("glockfile $fifo.source xcld 5 &");
+    system ("cat $fifo.tmp >> $fifo.source");
+}
+
+# generate table of images which can be sent to CADC, mark as distributed:
+system ("imsearch -ccd $ccds[3] -trange $start $stop -proc t -dist f -cadctable cadc.fits");
+system ("imsearch -ccd $ccds[3] -trange $start $stop -proc t -dist f -modify dist t");
+
+# utilities ############################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckphotom
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckphotom	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckphotom	(revision 22322)
@@ -0,0 +1,35 @@
+#!/usr/bin/env perl
+
+# the specific ccd is passed in from outside
+
+if (@ARGV != 3) { die "USAGE: ckphotom (name) (ccd) (mode)\n" ;}
+
+$name = $ARGV[0];
+$ccd  = $ARGV[1];
+$mode = "\U$ARGV[2]\E";
+
+if (($mode ne "SPLIT") && ($mode ne "MEF")) {
+    print STDERR "mode must be SPLIT or MEF\n";
+    exit (2);
+}
+
+if (vsystem ("photsearch -trans -image $name $ccd $mode -equiv")) {
+    print STDOUT "$name fails, no photom data\n";
+    exit 1;
+} else {
+    print STDOUT "$name: pass\n";
+    exit 0;
+}
+
+# utilities ############################
+
+sub vsystem {
+#    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckrunid
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckrunid	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckrunid	(revision 22322)
@@ -0,0 +1,228 @@
+#!/usr/bin/env perl
+
+# USAGE: ckrunid (name) 
+# (name) may be a MEF or SINGLE CCD file or a SPLIT directory
+# does not depend on the number of CCDs in the image
+
+if (@ARGV != 1) { die "USAGE: ckrunid (name)\n" ;}
+
+$name = $ARGV[0];
+$imtype = ckimtype ($name);
+if (!$imtype) { exit 1; }
+
+if ($imtype eq SPLIT) {
+    $root = gt_names ($name, "root", $imtype);
+    $tmpname = "$name/$root" . "00.fits";
+} else {
+    $tmpname = $name;
+}
+
+# need to abstract MJD-OBS keywrod
+$answer = `echo $tmpname | fields MJD-OBS`; chop ($answer);
+($fullname, $mjd_obs) = split (" ", $answer);
+if ($mjd_obs eq "") {
+    print STDOUT "MJD-OBS not found in header for $tmpname\n";
+    exit 1;
+}
+
+$date = mjd_to_date ($mjd_obs);
+print STDERR "date: $date\n";
+
+$runinfo = `mkrun date $date`;
+if ($?) { die "no run defined $date $mjd\n"; }
+
+($crunid, $start, $stop, $camera, $qrunid) = split (" ", $runinfo);
+print STDOUT "$crunid\n";
+exit 0;
+
+###################################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub get_jd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5;
+    
+
+    return ($jd);
+
+}
+
+sub get_mjd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5 - 2400000.5;
+    
+
+    return ($jd);
+
+}
+
+sub mjd_to_date {
+    my ($mjd) = $_[0];
+    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday);
+    my ($date, $time);
+    
+    $time = 86400 * ($mjd - 40587.0);
+    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime ($time);  
+    $year += 1900;
+    $mon ++;
+
+    $date = sprintf "%04d/%02d/%02d,%02d:%02d:%04.1f", $year, $mon, $mday, $hour, $min, $sec;
+    
+    return $date;
+}
+
+sub gt_names {
+    # in: filename mode type
+    # out: word
+
+    my ($value);
+
+    $value = "";
+    if ($_[2] eq "MEF")   { $value = mefnames ($_[0], $_[1]); }
+    if ($_[2] eq "SPLIT") { $value = splitnames ($_[0], $_[1]); }
+    
+    return $value;
+}
+
+sub mefnames {
+    # in: (/path/file.fits) (mode) 
+    # out: word
+
+    my($fullname) = $_[0];
+    my($mode) = $_[1];
+    my($answer);
+
+    # split rootdir and filename:
+    my (@words) = split ("/", $fullname);
+    $N = @words;
+
+    if ($mode eq "path") {
+	$answer = "";
+	for ($i = 0; $i < $N - 1; $i++) {
+	    $answer = $answer . $words[$i] . "/";
+	}
+	chop ($answer); # strip off last /
+	if ($answer eq "") { $answer = "."; }
+	return ($answer);
+    }
+    
+    if ($mode eq "root") {
+	$answer = "$words[$N-1]";
+	$answer =~ s/.fits$//;
+	return ($answer);
+    }
+    
+    if ($mode eq "Nccd") {
+	if (! -e $fullname) {
+	    return (0);
+	}
+	$answer = `echo $fullname | fields NEXTEND`;
+	@words = split (" ", $answer);
+	if ($words[1] eq "") {
+	    return (0);
+	}
+	return ($words[1]);
+    }
+    
+    return (0);
+}
+
+sub splitnames {
+    # in: (/path/file) (mode) 
+    # out: word
+    # /path/file is directory containing Nccd fits images
+
+    my($fullname) = $_[0];
+    my($mode) = $_[1];
+    my($answer, $N, $tmpname, @imlist, @words);
+
+    # split rootdir and filename:
+    @words = split ("/", $fullname);
+    $N = @words;
+
+    if ($mode eq "path") {
+	$answer = "";
+	for ($i = 0; $i < $N - 1; $i++) {
+	    $answer = $answer . $words[$i] . "/";
+	}
+	chop ($answer); # strip off last /
+	if ($answer eq "") { $answer = "."; }
+	return ($answer);
+    }
+    
+    if ($mode eq "root") {
+	$answer = "$words[$N-1]";
+	return ($answer);
+    }
+    
+    if ($mode eq "Nccd") {
+	
+	$tmpname = "$fullname/$words[$N-1]";
+	@imlist = <$tmpname??.fits>;
+
+	$Nccd = @imlist;
+	return $Nccd;
+    }
+    
+    return (0);
+}
+
+sub ckimtype {
+    # in: (/path/file)
+    # out: MEF | SPLIT | SINGLE | (NULL)
+    
+    my ($answer, $name, $value);
+    my ($file) = $_[0];
+
+    if (! -e $file) { 
+	print STDOUT "can't open file\n";
+	return ""; 
+    }
+
+    # if /path/file is a directory, it is split:
+    if (-d $file) { return "SPLIT"; }
+
+    # check that the file is a FITS image
+    $answer = `file -L $file`;
+    if ($?) { 
+	print STDOUT "can't open file\n";
+	return ""; 
+    }
+    ($name, $value) = split (" ", $answer);
+    if ($value ne "FITS") { 
+	print STDOUT "not a valid image\n";
+	return ""; 
+    }
+    
+    # check for NAXIS = 0 (MEF) 
+    $answer = `echo $file | fields NAXIS`;
+    # if ($?) { return ""; }
+    ($name, $value) = split (" ", $answer);
+    if ($value eq 0) { return "MEF"; }
+    
+    if ($value == 2) { return "SINGLE"; }
+
+    print STDOUT "not a valid image\n";
+    return "";
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckvalid
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckvalid	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/ckvalid	(revision 22322)
@@ -0,0 +1,302 @@
+#!/usr/bin/env perl
+
+$PASS = 0;
+$FAIL = 1;
+$SUBVALID = 2;
+$DATA_ERR = 3;
+$VERBOSE = 0;
+
+$eval{DATA}    = 0;  # image exists and is full-raster object MEF or SPLIT type
+$eval{DETREND} = 0;  # all needed detrend files exist for this image
+$eval{FRINGES} = 0;  # fringe frames exists if needed
+$eval{ASTROM}  = 0;  # astrometry measured for at least N / 12 chips
+$eval{PHOTOM}  = 0;  # photometry terms available for this image
+
+# USAGE: ckvalid (filename)
+# (filename) may be a MEF file or a SPLIT directory
+# [see end for notes]
+
+@tARGV = ();
+while (@ARGV) {
+    if ($ARGV[0] eq "-v") { 
+	$VERBOSE = 1;
+	shift @ARGV;
+	next;
+    }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift @ARGV;
+}    
+@ARGV = @tARGV;
+
+if (@ARGV != 1) { die "USAGE: ckvalid (filename)\n" ;}
+$name = $ARGV[0];
+$mode = "unknown";
+$type = "unknown";
+$runid = "none";
+
+# load ccd config information
+@ccdn   = split (" ", `cameraconfig -ccdn`); if ($?) { &goodbye ("ERROR in cameraconfig"); }
+@ccds   = split (" ", `cameraconfig -ccds`); if ($?) { &goodbye ("ERROR in cameraconfig"); }
+
+# determine image mode (SPLIT / MEF / SINGLE)
+$mode = ckimmode ($name);
+if ($mode eq "unknown") { 
+    if ($VERBOSE) { print "can't identify image mode\n"; }
+    &print_status;
+    exit $DATA_ERR; 
+}
+if ($mode eq "SINGLE") { 
+    if ($VERBOSE) { print STDERR "mode = SINGLE, validation undefined\n"; }
+    &print_status;
+    exit $DATA_ERR;
+}
+
+# get filename components
+$path = gt_names ($name, "path", $mode);
+$root = gt_names ($name, "root", $mode);
+$Nccd = gt_names ($name, "Nccd", $mode);
+
+# create the name of a real file:
+if ($mode eq SPLIT) {
+    $tmpname = sprintf "%s/%s%s.fits", $name, $root, $ccdn[0];
+} else {
+    $tmpname = $name;
+}
+
+# find the run id for this image (needed to get astrometry)
+$runid = `ckrunid $tmpname`; chop ($runid);
+if ($?) {
+    if ($VERBOSE) { print STDOUT "$name: no run ID\n"; }
+    $runid = "none";
+}
+
+# check the IMAGETYP:
+$answer = `echo $tmpname | fields OBSTYPE`;
+($tmp, $type) = split (" ", $answer);
+if ($type ne "OBJECT") { 
+    if ($VERBOSE) { print STDOUT "$name: wrong image type $type\n"; }
+    &print_status;
+    exit $SUBVALID;
+}
+
+# check the GEOMETRY:
+$answer = `echo $tmpname | fields CCDBIN1 CCDBIN2 RASTER`;
+@words = split (" ", $answer);
+if (($words[1] != 1) || ($words[2] != 1)) { 
+    if ($VERBOSE) { print STDOUT "$name: binned image\n"; }
+    &print_status;
+    exit $SUBVALID;
+}
+if ($words[3] ne "FULL") { 
+    if ($VERBOSE) { print STDOUT "$name: sub-rastered image\n"; }
+    &print_status;
+    exit $SUBVALID;
+}
+
+# image is OK
+$eval{DATA} = 1;
+
+# check the ASTROMETRY:
+unless ($runid eq "none") {
+    $answer = `ckastrom $runid $root $Nccd`; chop ($answer);
+    if ($?) {
+	if ($VERBOSE) { print "astrometry: $answer\n"; }
+    } else {
+	$eval{ASTROM} = 1;
+    }
+}
+
+# check the DETREND data:
+if ($mode eq "SPLIT") {
+    # we only need the name of an single representative file on disk
+    $tmpname = sprintf "%s/%s%s.fits", $name, $root, $ccdn[0];
+} else {
+    $tmpname = $name;
+}
+$answer = `detsearch -quiet -mosaic $tmpname -recipe`;
+if ($?) { 
+    if ($VERBOSE) { print "missing detrend: $answer\n"; } 
+} else {
+    $eval{DETREND} = 1;
+}
+
+# check on PHOTOMETRY
+$answer = `ckphotom $tmpname $ccds[0] $mode`;
+if ($?) {
+    if ($VEROSE) { print STDERR "photom failed: $answer\n"; }
+} else {
+    $eval{PHOTOM} = 1;
+}
+
+&print_status;
+if ($eval{DATA} && $eval{DETREND} && $eval{PHOTOM} && $eval{ASTROM}) { exit $PASS; }
+exit $FAIL;
+
+# utilities ##############################################
+
+sub print_status {
+    printf STDOUT "%s %8s %8s %8s %d %d %d %d\n", $name, $mode, $type, 
+    $runid, $eval{DATA}, $eval{DETREND}, $eval{PHOTOM}, $eval{ASTROM};
+}
+
+sub gt_names {
+    # in: filename mode type
+    # out: word
+
+    my ($value);
+
+    $value = "";
+    if ($_[2] eq "MEF")   { $value = mefnames ($_[0], $_[1]); }
+    if ($_[2] eq "SPLIT") { $value = splitnames ($_[0], $_[1]); }
+    
+    return $value;
+}
+
+sub mefnames {
+    # in: (/path/file.fits) (mode) 
+    # out: word
+
+    my($fullname) = $_[0];
+    my($mode) = $_[1];
+    my($answer);
+
+    # split rootdir and filename:
+    my (@words) = split ("/", $fullname);
+    $N = @words;
+
+    if ($mode eq "path") {
+	$answer = "";
+	for ($i = 0; $i < $N - 1; $i++) {
+	    $answer = $answer . $words[$i] . "/";
+	}
+	chop ($answer); # strip off last /
+	if ($answer eq "") { $answer = "."; }
+	return ($answer);
+    }
+    
+    if ($mode eq "root") {
+	$answer = "$words[$N-1]";
+	$answer =~ s/.fits$//;
+	return ($answer);
+    }
+    
+    if ($mode eq "Nccd") {
+	if (! -e $fullname) {
+	    return (0);
+	}
+	$answer = `echo $fullname | fields NEXTEND`;
+	@words = split (" ", $answer);
+	if ($words[1] eq "") {
+	    return (0);
+	}
+	return ($words[1]);
+    }
+    
+    return (0);
+}
+
+sub splitnames {
+    # in: (/path/file) (mode) 
+    # out: word
+    # /path/file is directory containing Nccd fits images
+
+    my($fullname) = $_[0];
+    my($mode) = $_[1];
+    my($answer, $N, $tmpname, @imlist, @words);
+
+    # split rootdir and filename:
+    @words = split ("/", $fullname);
+    $N = @words;
+
+    if ($mode eq "path") {
+	$answer = "";
+	for ($i = 0; $i < $N - 1; $i++) {
+	    $answer = $answer . $words[$i] . "/";
+	}
+	chop ($answer); # strip off last /
+	if ($answer eq "") { $answer = "."; }
+	return ($answer);
+    }
+    
+    if ($mode eq "root") {
+	$answer = "$words[$N-1]";
+	return ($answer);
+    }
+    
+    if ($mode eq "Nccd") {
+	
+	$tmpname = "$fullname/$words[$N-1]";
+	@imlist = <$tmpname??.fits>;
+
+	$Nccd = @imlist;
+	return $Nccd;
+    }
+    
+    return (0);
+}
+
+sub ckimmode {
+    # in: (/path/file)
+    # out: MEF | SPLIT | SINGLE | (NULL)
+    
+    my ($answer, $name, $value);
+    my ($file) = $_[0];
+
+    if (! -e $file) { 
+	if ($VERBOSE) { print STDOUT "$file: can't open file\n"; }
+	return "unknown"; 
+    }
+
+    # if /path/file is a directory, it is split:
+    if (-d $file) { return "SPLIT"; }
+
+    # check that the file is a FITS image
+    $answer = `file -L $file`;
+    if ($?) { 
+	if ($VERBOSE) { print STDOUT "can't open file\n"; }
+	return "unknown"; 
+    }
+    ($name, $value) = split (" ", $answer);
+    if ($value ne "FITS") { 
+	if ($VERBOSE) { print STDOUT "not a valid image\n"; }
+	return "unknown"; 
+    }
+    
+    # check for NAXIS = 0 (MEF) 
+    $answer = `echo $file | fields NAXIS`;
+    # if ($?) { return ""; }
+    ($name, $value) = split (" ", $answer);
+    if ($value eq 0) { return "MEF"; }
+    
+    if ($value == 2) { return "SINGLE"; }
+
+    if ($VERBOSE) { print STDOUT "not a valid image\n"; }
+    return "unknown";
+}
+
+sub vsystem {
+    # print STDERR "@_\n";
+    my($status) = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+
+# This program calls several other perl scripts to get information 
+# about the image.  
+# ckrunid   - determine the appropriate Camera Run ID for this image
+# ckastrom  - check for the appropriate astrometry information
+# ckdetrend - check for the appropriate detrend images
+# ckphotom  - check for the appropriate photometry data
+
+# at the moment, there are different calling conventions for each of
+# these functions.  Some take a single name (MEF / SPLIT), some take a
+# reference to the specific CCD (filename CCD MODE).  
+
+# none of the called functions need to find the number of CCDs
+# ckvalid determines the number of CCDs from the header (MEF) or the
+# number of appropriately named FITS files in the directory (SPLIT).
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/cmp.header
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/cmp.header	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/cmp.header	(revision 22322)
@@ -0,0 +1,101 @@
+#!/usr/bin/env perl
+
+$version = 1.0;
+if (@ARGV != 2) { die "USAGE: cmp.header (cmpfile) (hdrfile)\n" ; }
+
+$cmpfile   = $ARGV[0];
+$hdrfile = $ARGV[1];
+
+&ckpathname ("$hdrfile");
+
+if (! -e $cmpfile) { die "ERROR: source cmpfile $cmpfile not found\n"; }
+
+# grab astrometry keywords from $cmpfile:
+@header = &load_header ($cmpfile);
+
+# convert stripped header to valid FITS file: NAXIS -> 0
+$found = 0;
+for ($i = 0; ($i < 16) && ($i < @header); $i++) {
+    if ($header[$i] =~ /^NAXIS   =/) { 
+	$header[$i] = sprintf "NAXIS   = %20d / %-47s", 0, "Header without Pixels";
+	$found = 1;
+	last;
+    }
+}
+if (!$found) { print STDERR "header is missing NAXIS\n"; }
+
+# write output to file
+open (FILE, ">$hdrfile");
+foreach $line (@header) {
+    print FILE "$line";
+}
+for ($i = 0; $i < $Nextra; $i++) {
+    for ($j = 0; $j < 80; $j++) {
+	print FILE " ";
+    }
+}
+close (FILE);
+
+print "SUCCESS: done with cmp.header\n";
+exit 0;
+
+###################################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub load_header {
+
+    my ($file) = $_[0];
+    my ($ID, $buffer, $done, @buffer, $line);
+
+    open (FILE, "$file");
+    read FILE, $ID, 8;
+    if ($ID eq "SIMPLE  ") { goto valid; }
+    if ($ID eq "XTENSION") { goto valid; }
+    close (FILE);
+    die "SUCCESS: $file is not a valid header file\n";
+    
+valid:
+    seek FILE, 0, "SEEK_SET";
+    $done = 0;
+    @buffer = ();
+
+    while (! $done) {
+	read FILE, $buffer, 2880;
+	for ($i = 0; $i < 2880; $i+=80) {
+	    $line = substr ($buffer, $i, 80);
+	    @buffer = (@buffer, $line);
+	    if ($line =~ /^END     /) { $done = 1; }
+	}
+    }
+    close (FILE);
+    return (@buffer);
+}
+
+# given a filename, check that its path exists, create it if not
+sub ckpathname {
+    my ($file) = $_[0];
+    my (@words, $dir);
+
+    @words = split ("/", $file);
+    pop (@words);
+    $dir = join ("/", @words);
+
+    if ($dir eq "") { $dir = "."; }
+
+    if (! -d $dir) {
+	system ("mkdir -p $dir");
+	if ($?) {
+	    print STDERR "ERROR: can't make directory component for $file\n";
+	    exit 1;
+	}
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/complete.addstar
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/complete.addstar	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/complete.addstar	(revision 22322)
@@ -0,0 +1,31 @@
+#!/usr/bin/env perl
+
+# get list of *o/*.smp
+@smplist = <*o/*.smp>;
+
+$i = 0;
+foreach $file (@smplist) {
+
+    # is NASTRO > 0?
+    ($tmp, $nastro) = split (" ", `echo $file | fields NASTRO`);
+    if ($nastro == 0) { next; }
+
+    # is file already in db?
+    ($path, $root) = split ("/", $file);
+    $answer = `imphotsearch -D CATDIR $catdir -name $root`;
+    if ($answer == "") { next; }
+
+    # addstar file
+    vsystem ("addstar -D CATDIR $catdir $file");
+
+    $i ++;
+    if ($i > 30) {exit;}
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+#    my($status) = system ("@_");
+#    $status;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/create.subset
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/create.subset	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/create.subset	(revision 22322)
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+if (@ARGV != 2) { die "ERROR: USAGE: create.subset (master) (subset)\n" }
+
+# load in stats lines
+open (IN,  $ARGV[0]);
+open (OUT, ">$ARGV[1]");
+while ($line = <IN>) {
+    chop ($line);
+    @words = split (" ", $line);
+    if ($words[1]) {
+       print OUT "$words[0]\n";
+   }      
+}    
+close (IN);
+close (OUT);
+
+print STDERR "SUCCESS\n";
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dads.detrend
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dads.detrend	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dads.detrend	(revision 22322)
@@ -0,0 +1,324 @@
+#!/usr/bin/env perl
+# calls flatten.flips & defringe, but no flips components directly
+
+$CLEANUP     = 1;
+$DO_DEFRINGE = 0;
+$DO_DEMODE   = 0;
+$config = "";
+if ($ENV{'PTOLEMY'}) { $config = "-c $ENV{'PTOLEMY'}"; }
+$tmpenv = `mktemp /tmp/elixir.XXXXXX`; chop ($tmpenv);
+
+@opt = ();
+@tARGV = ();
+$LIMITMODES = "";
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-c") {
+        $config = "-c $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-C") {
+        push @opt, "-C $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-D") {
+        push @opt, "-D $ARGV[1] $ARGV[2]";
+        shift; shift; shift; next;
+    }
+    if ($ARGV[0] eq "-cleanup") {
+	$CLEANUP = 0;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-limitmodes") {
+	$LIMITMODES = "-limitmodes $ARGV[1]";
+        shift; shift; next;
+    }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+$config = join (" ", $config, @opt);
+$status = system ("gconfig -raw $config > $tmpenv");
+if ($status) { &escape ("error in elixir configuration: gconfig fails"); }
+$ENV{'PTOLEMY'} = "$tmpenv";
+
+if (@ARGV != 3) { &escape ("USAGE: dads.detrend (input) (outbase) (level) [-cleanup] [elixir options]"); }
+
+$input  = $ARGV[0];   # input : /path/path/654321o.fits 
+$outdir = $ARGV[1];   # output: /path/path/654321p
+$level =  $ARGV[2];   # level : perfect, best, raw
+
+# validate level
+$level = "\U$level\E";
+if (($level ne "PERFECT") && ($level ne "BEST") && ($level ne "RAW")) {
+    &escape ("ERROR: invalid entry for (level)");
+}
+$close = "";
+if ($level eq "BEST") { $close = "-close"; }
+
+# check for input file (must be MEF)
+if (! -e $input) { &escape ("ERROR: input file not found"); }
+
+# for raw distribution, simply use fhtool to split the files:
+if ($level eq "RAW") {
+    system ("rmdir $outdir");
+    if ($?) { &escape ("ERROR: can't remove target directory for fhtool"); }
+    system ("fhtool $input $outdir");
+    if ($?) { &goodbye ("ERROR: failure in fhtool"); }
+    print STDOUT "SUCCESS: finished with dads.detrend\n";
+    &goodbye;
+}
+
+# force output directory to exist
+if (! -d $outdir) { 
+    system ("mkdir -p $outdir"); 
+    if ($?) { &goodbye ("ERROR: can't create output directory"); }
+}
+
+# load ccd config information
+@ccdn   = split (" ", `cameraconfig -ccdn`); if ($?) { &goodbye ("ERROR in cameraconfig"); }
+@ccds   = split (" ", `cameraconfig -ccds`); if ($?) { &goodbye ("ERROR in cameraconfig"); }
+
+# load abstracted keywords
+($typekwd, $filtkwd, $exptkwd) = split (" ", `gconfig IMAGETYPE-KEYWORD FILTER-KEYWORD EXPTIME-KEYWORD`);
+if ($?) { &goodbye ("ERROR: config system missing keyword abstractions"); }
+
+# extract keyword values:
+($junk, $type, $exptime, $filtvalue) = split (" ", `echo $input | fields $typekwd $exptkwd $filtkwd`);
+if ($?) { &goodbye ("ERROR: failure reading header keywords (1)"); }
+
+# check for valid imagetype & filter
+if ($type ne "OBJECT") { &goodbye ("ERROR: $input: wrong image type"); }
+$filter = `filtnames $filtvalue`; chop $filter;
+if ($?) { &goodbye ("ERROR: filter not found"); }
+
+# check recipe file for detrend types & cutoff exptime values
+$recipefile = `gconfig DETREND_RECIPES`; chop $recipefile;
+($detypes, $cutoffs) = split (" ", `gconfig -c $recipefile $filter`);
+if ($?) { &goodbye ("ERROR: failure to get detrend recipes"); }
+@detypes = split (",", $detypes);
+@cutoffs = split (",", $cutoffs);
+
+# check for DEFRINGE, DEMODE 
+$DO_DEFRINGE = 0;
+for ($i = 0; $i < @detypes; $i++) {
+    if ($detypes[$i] eq "fringe") {
+	if ($exptime >= $cutoffs[$i]) { $DO_DEFRINGE = 1; }
+	last;
+    }
+}
+$DO_DEMODE = 0;
+for ($i = 0; $i < @detypes; $i++) {
+    if ($detypes[$i] eq "modes") {
+	if ($exptime >= $cutoffs[$i]) { $DO_DEMODE = 1; }
+	last;
+    }
+}
+$camera = `gconfig CAMERA`; chop $camera;
+if ($camera eq "meganorth") { $DO_DEMODE = 0; }
+
+# check the GEOMETRY:
+($tmp, $bin1, $bin2, $raster) = split (" ", `echo $input | fields CCDBIN1 CCDBIN2 RASTER`);
+if ($?) { &goodbye ("ERROR: failure reading header keywords (2)"); }
+
+if ($bin1 != 1) { &goodbye ("ERROR: $input: binned image"); }
+if ($bin2 != 1) { &goodbye ("ERROR: $input: binned image"); }
+if ($raster ne "FULL") { &goodbye ("ERROR: $input: sub-rastered image"); } 
+
+# print a summary of the processing
+print STDOUT "\n";
+print STDOUT "image: $input, filter: $filter, exptime: $exptime\n";
+print STDOUT "detrend processing: defringe: $DO_DEFRINGE, demode: $DO_DEMODE, level: $level\n\n";
+
+# get image size:
+@words = stat ($input);
+$Nbyte = $words[7];
+
+# check space on /tmp:
+@answer = `df -k /tmp`;
+@words = split (" ", $answer[1]);
+$Nfree = $words[3]*1024;
+
+# need space in workdir for one image for each of DEMODE & DEFRINGE:
+$Need  = 0;
+if ($DO_DEMODE)   { $Need ++; }
+if ($DO_DEFRINGE) { $Need ++; }
+$Nneed = $Need*$Nbyte*1.01;
+
+# choose a work directory (/tmp vs $outdir):
+$workdir = $outdir;
+if ($Nfree > $Nneed) {
+    $workdir = "/tmp";
+}
+
+print STDERR "Need workspace for $Need images: $Nfree vs $Nneed\n";
+
+# extract image rootname from $outdir:
+$base = &basename ($outdir);
+
+# create output lists:
+@detlist = ();
+@deflist = ();
+@demlist = ();
+@fitlist = ();
+foreach $ccd (@ccdn) {
+    push @fitlist, "$outdir/$base$ccd.fits";
+    push @detlist, "$workdir/$base$ccd.det";
+    push @deflist, "$workdir/$base$ccd.def";
+}
+# phu file:
+$phufile = "$outdir/$base.phu";
+
+# detrend  $input[ccd] @detlist
+# defringe @detlist    @deflist
+# demode   @deflist    @demlist
+# output               @fitlist
+
+#* flatten.flips input fitlist
+
+#* flatten.flips input detlist
+#* demode detlist fitlist
+
+# flatten.flips input detlist
+# defringe detlist deflist
+
+#* flatten.flips input detlist
+#* defringe detlist deflist
+#* demode deflist fitlist
+
+#            @demlist = @fitlist
+# !demode:   @deflist = @demlist
+# !defringe: @detlist = @deflist
+
+if ($DO_DEMODE) {
+    @demlist = @fitlist;
+} else {
+    if ($DO_DEFRINGE) { 
+	@deflist = @fitlist; 
+    } else {
+	@detlist = @fitlist; 
+    }
+}
+
+# run the detrend system
+for ($i = 0; $i < @ccds; $i++) {
+    $status = &vsystem ("flatten.flips $close -quiet $input $detlist[$i] $ccds[$i] mef");
+    if ($status) { &escape ("ERROR: problem running flatten on $input, $ccds[$i]"); }
+}
+$status = &vsystem ("flatten.flips $close -quiet $input $phufile phu mef");
+if ($status) { &escape ("ERROR: problem running flatten on $input, phu"); }
+
+# we need files containing detlist, demlist, deflist:
+if ($DO_DEMODE || $DO_DEFRINGE) {
+    $detlist = "$outdir.detlist";
+    open (FILE, ">$detlist");
+    foreach $name (@detlist) {
+	print FILE "$name\n";
+    }
+    close (FILE);
+}
+if ($DO_DEMODE) {
+    $mapfile = "$outdir.map.fits";
+    $deflist = $detlist;
+    $demlist = "$outdir.demlist";
+    open (FILE, ">$demlist");
+    foreach $name (@demlist) {
+	print FILE "$name\n";
+    }
+    close (FILE);
+}
+if ($DO_DEFRINGE) {
+    $deflist = "$outdir.deflist";
+    open (FILE, ">$deflist");
+    foreach $name (@deflist) {
+	print FILE "$name\n";
+    }
+    close (FILE);
+}
+
+if ($DO_DEFRINGE) {
+    $status = &vsystem ("defringe $detlist $deflist $close");
+    if ($status) { &escape ("ERROR: problem running defringe on $outdir"); }
+}
+
+if ($DO_DEMODE) {
+    $status = &vsystem ("demodemap $deflist $demlist -mapfile $mapfile $LIMITMODES $close");
+    if ($status) { &escape ("ERROR: problem running demodemap on $outdir"); }
+}
+
+# update the phu header with the results from ccd # 0
+if ($DO_DEMODE || $DO_DEFRINGE) {
+    $temp = `mktemp /tmp/elixir.XXXXXX`; chop ($temp);
+    system ("fhead $detlist[0] > $temp.1.hdr");
+    system ("fhead $fitlist[0] > $temp.2.hdr");
+    
+    @answer = `diff $temp.1.hdr $temp.2.hdr`;
+    open (FILE, ">$temp.hdr");
+    foreach $line (@answer) {
+	if ($line =~ m|^>\s|) {
+	    ($head) = $line =~ m|^>\s(.*)$|;
+	    print FILE "$head\n";
+	}
+    }
+    close FILE;
+  
+    vsystem ("fits_insert $phufile $temp.hdr");
+    if ($?) { &escape ("ERROR: cannot update phu"); }
+    unlink ("$temp");
+    unlink ("$temp.1.hdr");
+    unlink ("$temp.2.hdr");
+    unlink ("$temp.hdr");
+}
+&goodbye ("SUCCESS: finished with dads.detrend");
+
+########################################################################
+
+sub cleanup {
+
+    if (!$CLEANUP) { return; }
+
+    unlink $tmpenv;
+    # detlist, demlist, deflist
+    foreach $name (@detlist) {
+	if (-e $name && ($name =~ m|^/tmp/|)) { unlink ($name); }
+    }
+    foreach $name (@demlist) {
+	if (-e $name && ($name =~ m|^/tmp/|)) { unlink ($name); }
+    }
+    foreach $name (@deflist) {
+	if (-e $name && ($name =~ m|^/tmp/|)) { unlink ($name); }
+    }
+    
+    if (-e $detlist) { unlink ($detlist); }
+    if (-e $demlist) { unlink ($demlist); }
+    if (-e $deflist) { unlink ($deflist); }
+    
+    return;
+}
+
+sub basename {
+
+    my ($file) = $_[0];
+    my ($base, @words);
+
+    @words = split ("/", $file);
+    $base = $words[-1];
+    return $base;
+}
+
+sub vsystem {
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    &cleanup; 
+    print STDERR "@_\n";
+    exit 0;
+}
+
+sub escape {
+    &cleanup;
+    print STDERR "@_\n";
+    exit 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dads.jpeg
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dads.jpeg	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dads.jpeg	(revision 22322)
@@ -0,0 +1,59 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 3) { die "USAGE: dads.jpeg (inbase) (outbase) (level)\n" }
+
+# test named Xserver, or start vnc on specified machine
+$xhost = `gconfig XHOST`; chop $xhost;
+$xdisp = `gconfig XDISP`; chop $xdisp;
+if (vsystem ("xdpyinfo -display $xdisp")) {
+    print STDERR "X server is not running. start it now on $xhost\n";
+    exit 1;
+}
+$ENV{'DISPLAY'} = $xdisp;
+
+# find mana script
+$confdir = `gconfig CONFDIR`;  chop $confdir;
+$script = "$confdir/mana/dads.pro";
+
+# get image list
+@ilist = <$ARGV[0]/*.fits>;
+
+# level may be: 'perfect', 'best', 'raw'
+$level = "\U$ARGV[2]\E";
+if (($level ne "PERFECT") && ($level ne "BEST") && ($level ne "RAW")) {
+    die "ERROR: invalid entry for (level)\n";
+}
+
+$out1 = "$ARGV[1].b10.jpg";
+$out2 = "$ARGV[1].b50.jpg";
+
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+print MANA "init.mosaic\n";
+print MANA "macro go\n";
+foreach $in (@ilist) {
+    print MANA "load.image $in\n";
+}
+print MANA "do.jpeg $out1 $out2 $level\n";
+print MANA "exit 0\n";
+print MANA "end\n";
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) {
+    die "ERROR: problem running dads.jpeg\n"; 
+} 
+
+print STDOUT "SUCCESS: finished running dads.jpeg\n"; 
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dads.keywords
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dads.keywords	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dads.keywords	(revision 22322)
@@ -0,0 +1,96 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 1) { die "USAGE: dads.keywords (outdir)\n" ;}
+
+# get list of image files:
+@list = <$ARGV[0]/*.fits>;
+$name = $list[0];
+
+# get appropriate run ID
+$runid = `ckrunid $name`; chop ($runid);
+if ($?) {
+    print STDERR "ERROR: can't find run ID\n";
+    exit 1;
+}
+
+# get the appropriate reduced data directory
+$tmpdir=`gconfig -D RUNID $runid TMPDIR`; chop ($tmpdir);
+if ($?) {
+    print STDERR "ERROR: can't find tmpdir\n";
+    exit 1;
+}
+
+if (! -d $tmpdir) { 
+    print STDERR "ERROR: can't open $tmpdir\n";
+    exit 1;
+}
+
+foreach $name (@list) {
+
+    # files have names like /path/654321p/654321p00.fits
+
+    # split rootdir and filename:
+    @words = split ("/", $name);
+    $N = @words;
+
+    # extract root (654321p) convert to original form (654321o)
+    ($root, $ccd) = $words[$N-1] =~ /(\d\d\d\d\d\d)\S(\d\d)/;
+    $base = sprintf "%so/%so%02d", $root, $root, $ccd;
+
+    # generate name of output header file
+    $hdxfile = $name;
+    $hdxfile =~ s/.fits$/.hdx/;
+    if ($hdxfile eq $name) {
+	print STDERR "ERROR in format of filename $name\n";
+	exit 1;
+    }
+
+    # grab.keywords tests both smp & cmp
+    $cmproot = "$tmpdir/$base";
+    if (vsystem ("grab.keywords $name $cmproot $hdxfile")) {
+	print STDERR "ERROR: can't get keywords for $name\n";
+	exit 1;
+    }
+
+    if (vsystem ("fits_insert $name $hdxfile -comment 5 'Fully Characterized and Analysed by Elixir @ CFHT'" )) {
+	print STDERR "ERROR inserting keywords in $name\n";
+	exit 1;
+    }
+}
+
+# add data for phu file:
+@list = <$ARGV[0]/*.phu>;
+$phufile = $list[0];
+
+# generate name of output header file
+$hdxfile = $name;
+$hdxfile =~ s/.fits$/.phu/;
+if ($hdxfile eq $name) {
+    print STDERR "ERROR in format of filename $name\n";
+    exit 1;
+}
+
+if (vsystem ("grab.keywords $phufile phu $hdxfile")) {
+    print STDERR "ERROR: can't get keywords for $name\n";
+    exit 1;
+}
+
+if (vsystem ("fits_insert $phufile $hdxfile -comment 5 'Fully Characterized and Analysed by Elixir @ CFHT'" )) {
+    print STDERR "ERROR inserting keywords in $name\n";
+    exit 1;
+}
+
+print STDERR "SUCCESS: applied new keywords\n";
+exit 0;
+
+# utilities ##############################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/darktime.cfh12k
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/darktime.cfh12k	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/darktime.cfh12k	(revision 22322)
@@ -0,0 +1,33 @@
+#!/usr/bin/env perl
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+if (@ARGV != 2) { die "USAGE: darktime.cfht (image.exptime) (dark.exptime)\n" }
+
+$t1 = $ARGV[0];
+$t2 = $ARGV[1];
+
+$a = 0.000781480;
+$b = 5.45879e-06;
+$c = -1.21674e-09;
+
+$d1 = $a*$t1 + $b*$t1**2 + $c*$t1**3;
+
+$d2 = $a*$t2 + $b*$t2**2 + $c*$t2**3;
+
+$f = $d1 / $d2;
+
+# no dark correction for exptime < 15 sec
+if ($t1 < 15) { $f = 0.001; }
+
+print STDOUT "$f\n";
+
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/darktime.linear
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/darktime.linear	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/darktime.linear	(revision 22322)
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+if (@ARGV != 2) { die "USAGE: darktime.linear (image.exptime) (dark.exptime)\n" }
+
+
+$t1 = $ARGV[0];
+$t2 = $ARGV[1];
+
+$r = $t1 / $t2;
+
+# no dark correction for exptime < 60 sec
+if ($t1 < 60) { $r = 0.0; }
+
+print STDOUT "$r\n";
+
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/defringe
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/defringe	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/defringe	(revision 22322)
@@ -0,0 +1,177 @@
+#!/usr/bin/env perl
+
+# grab the command line options
+$close = "";
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-close") {
+	$close = "-close";
+        shift; next;
+    }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 2) { &escape ("USAGE: defringe (inlist) (outlist) [-close]"); }
+
+# load config info
+$confdir = `gconfig CONFDIR`; chop $confdir;
+if ($?) { &escape ("config failure"); }
+$script = "$confdir/mana/fringe.pro";
+
+# various lists
+@input  = split (" ", `cat $ARGV[0]`);
+@output = split (" ", `cat $ARGV[1]`);
+@ccds   = split (" ", `cameraconfig -ccds`);
+$Nccd   = @ccds;
+$dbmode = `gconfig DETREND-DB-MODE`; chop $dbmode;
+
+# find fringe-point file
+$frpts  = `detsearch -quiet -image $input[0] $ccds[0] split -type frpts`; chop $frpts;
+if ($?) { &escape ("can't get fringe points for image"); }
+
+# temporary files
+$frstats = mktemp ("defringe");
+$imstats = mktemp ("defringe");
+$fitpars = mktemp ("defringe");
+
+# prepare for defringing:
+# 1) measure input image fringe stats (imstats)
+# 2) extract master fringe frame stats (frstats)
+# 3) create output directory if needed
+# 4) delete output files if present
+
+open (FILE, ">$frstats");
+for ($i = 0; $i < $Nccd; $i++) {
+    # measure fringe amplitude of input image
+    vsystem ("getfringe $input[$i] $ccds[$i]  $frpts 1 >> $imstats");
+    if ($?) { die "failure to measure fringe amplitude\n"; }
+
+    print "imstats; $imstats\n";
+
+    # find fringe master frames
+    @file = `detsearch -quiet $close -image $input[$i] $ccds[$i] split -type fringe`; 
+    $file = $file[-1]; chop $file;
+    if ($?) { &escape ("can't find fringe master for $input[$i]"); }
+    $fringe[$i] = $file;
+    if ($dbmode eq "MEF") { $fringe[$i] = "$file\[$ccds[$i]\]"; }
+
+    print "file: $file\n";
+    print "fringe: $fringe[$i]\n";
+    print "dbmode; $dbmode\n";
+
+    # get fringe master stats
+    if ($dbmode eq "MEF") {
+	($name, $C0, $C1, $SKY, $RNG) = split (" ", `echo $file | fields -x $i FRNG_C0 FRNG_C1 FRNG_SKY FRNG_RNG`);
+    } else {
+	($name, $C0, $C1, $SKY, $RNG) = split (" ", `echo $file | fields FRNG_C0 FRNG_C1 FRNG_SKY FRNG_RNG`);
+    }
+    print FILE "$C0 $C1 $SKY $RNG\n";
+    print STDERR "$C0 $C1 $SKY $RNG\n";
+
+    # check on output directory
+    &ckdir ($output[$i]);
+
+    if (-e $output[$i]) { unlink $output[$i]; }
+}
+close (FILE);
+
+# convert image stats to optimal image parameters
+vsystem ("fr.frstats -applyfit $imstats $frstats $fitpars");
+if ($?) { &escape ("failure in fr.frstats"); }
+@fitpars = `cat $fitpars`;
+
+# load flips parameter file
+$paramfile = `gconfig FLIPS_PARAM_DEFRINGE`; chop $paramfile;
+open (FILE, "$paramfile");
+@rawpars = <FILE>;
+close (FILE);
+
+$infile  = mktemp ("\@defringe");
+$parfile = mktemp ("defringe");
+for ($i = 0; $i < $Nccd; $i++) {
+
+    # create input list file
+    ($s0, $s1, $RF, $ds) = split (" ", $fitpars[$i]);
+    open (FILE, ">$infile");
+    print FILE "$input[$i]  $s0 1.0 $ds\n";
+    print FILE "$fringe[$i] $s1 $RF $ds\n";
+    close (FILE);
+
+    # create input par file
+    @params = @rawpars;
+    foreach $line (@params) {
+	$line =~ s|LISTNAME|$infile|;
+	$line =~ s|FILENAME|$output[$i]|;
+	$line =~ s|COMMENT|Elixir Defringing|;
+    }
+    open (FILE, ">$parfile");
+    foreach $line (@params) { print FILE "$line"; }
+    close (FILE);
+
+    vsystem ("imcombred $parfile");
+    if ($?) { &escape ("failure running imcombred"); }
+}
+
+&goodbye;
+
+########
+
+sub ckdir {
+
+    my ($file) = $_[0];
+    my ($dir, @words);
+
+    @words = split ("/", $file);
+    pop (@words);
+    $dir = join ("/", @words);
+    if ($dir eq "") { $dir = "."; }
+    if (! -e $dir) {
+	system ("mkdir -p $dir");
+	if ($?) { die "can't create output directory $dir\n"; }
+    }
+}
+
+sub loadlist {
+
+    my($command) = $_[0];
+    my(@list, $line);
+
+    @list = `$command`;
+
+    foreach $line (@list) { chop ($line); }
+
+    return @list;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+@temp = ();
+sub mktemp {
+
+    my ($base) = $_[0];
+    my ($name);
+    $name = `mktemp /tmp/$base.XXXXXX`;
+    chop $name;
+
+    push @temp, $name;
+    return $name;
+}
+
+sub escape {
+    $message = $_[0];
+    foreach $name (@temp) {
+	unlink ($name);
+    }
+    die "$message\n";
+}
+
+sub goodbye {
+    foreach $name (@temp) { unlink ($name); }
+    exit 0;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/defringe.ccd
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/defringe.ccd	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/defringe.ccd	(revision 22322)
@@ -0,0 +1,141 @@
+#!/usr/bin/env perl
+
+# grab the command line options
+$close = "";
+$quiet = 0;
+$preserve = 0;
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-close") {
+	$close = "-close";
+        shift; next;
+    }
+    if ($ARGV[0] eq "-quiet") {
+	$quiet = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-preserve") {
+        $preserve = 1;
+        shift; next;
+    }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 4) { &escape ("USAGE: defringe.chip (input) (output) (ccd) (mode) [-close]"); }
+
+# various lists
+$input  = $ARGV[0];
+$output = $ARGV[1];
+$ccd    = $ARGV[2];
+$mode   = uppercase ($ARGV[3]);
+if ($mode ne "SPLIT") { &escape ("defringe.ccd requires SPLIT input data for now"); }
+
+# load config info
+$dbmode = `gconfig DETREND-DB-MODE`; chop $dbmode;
+$ccdn   = `cameraconfig -N $ccd`; chop $ccdn;
+
+# find fringe-point file (always MEF)
+$frpts  = `detsearch -quiet -image $input $ccd split -type frpts`; chop $frpts;
+if ($?) { &escape ("can't get fringe points for image"); }
+
+# measure fringe amplitude of input image
+($name, $sky, $rng, $drng) = split (" ", `getfringe $input $ccd $frpts 1`);
+if ($?) { &escape ("failure to measure fringe amplitude"); }
+
+# find fringe master frames
+$file = `detsearch -quiet $close -image $input $ccd split -type fringe`; chop $file;
+if ($?) { &escape ("can't find fringe master for $input"); }
+
+$frmaster = $file;
+if ($dbmode eq "MEF") { $frmaster = "$file\[$ccd\]"; }
+
+# get fringe master stats
+if ($dbmode eq "MEF") {
+    ($name, $C0, $C1, $SKY, $RNG) = split (" ", `echo $file | fields -x $ccdn FRNG_C0 FRNG_C1 FRNG_SKY FRNG_RNG`);
+} else {
+    ($name, $C0, $C1, $SKY, $RNG) = split (" ", `echo $file | fields FRNG_C0 FRNG_C1 FRNG_SKY FRNG_RNG`);
+}
+
+if (-e $output) { unlink $output; }
+
+# load flips parameter file
+$paramfile = `gconfig FLIPS_PARAM_DEFRINGE`; chop $paramfile;
+open (FILE, "$paramfile");
+@rawpars = <FILE>;
+close (FILE);
+
+$infile  = mktemp ("\@defringe");
+$parfile = mktemp ("defringe");
+
+# create input list file
+$s0 = $sky;
+$s1 = $SKY;
+$RF = $RNG / $rng;
+$ds = 0.0;
+open (FILE, ">$infile");
+print FILE "$input    $s0 1.0 $ds\n";
+print FILE "$frmaster $s1 $RF $ds\n";
+close (FILE);
+
+# create input par file
+@params = @rawpars;
+foreach $line (@params) {
+    $line =~ s|LISTNAME|$infile|;
+    $line =~ s|FILENAME|$output|;
+    $line =~ s|COMMENT|Elixir Defringing|;
+}
+open (FILE, ">$parfile");
+foreach $line (@params) { print FILE "$line"; }
+close (FILE);
+
+vsystem ("imcombred $parfile");
+if ($?) { &escape ("failure running imcombred"); }
+
+&goodbye;
+
+########
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+@temp = ();
+sub mktemp {
+
+    my ($base) = $_[0];
+    my ($name);
+    $name = `mktemp /tmp/$base.XXXXXX`;
+    chop $name;
+
+    push @temp, $name;
+    return $name;
+}
+
+sub uppercase {
+    return "\U$_[0]\E";
+}
+
+sub escape {
+    $message = $_[0];
+    foreach $name (@temp) {
+	unlink ($name);
+    }
+    if ($quiet) { 
+	die "$message\n";
+    } else {
+	die "ERROR: $message\n"; 
+    }
+}
+
+sub goodbye {
+    foreach $name (@temp) { 
+	if ($preserve) { print STDERR "tmp file: $name\n"; }
+	else { unlink ($name); }
+    }
+    if (!$quiet) { print STDERR "SUCCESS\n"; }
+    exit 0;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/demodemap
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/demodemap	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/demodemap	(revision 22322)
@@ -0,0 +1,74 @@
+#!/usr/bin/env perl
+
+# grab the command line options
+$limitmodes = "";
+$mapfile = "";
+$close = "";
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-close") {
+	$close = "-close";
+        shift; next;
+    }
+    if ($ARGV[0] eq "-mapfile") {
+	$mapfile = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-limitmodes") {
+	$limitmodes = $ARGV[1];
+        shift; shift; next;
+    }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 2) { die "USAGE: demodemap (inlist) (outlist) [-mapfile file] [-limitmodes N] [-close]\n" }
+
+$inlist  = $ARGV[0];
+$outlist = $ARGV[1];
+
+open (FILE, "$inlist");
+@list = <FILE>;
+$first = $list[$i]; chop ($first);
+close (FILE);
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/modes.pro";
+$answer  = `cameraconfig -ccds`;
+@ccds    = split (" ", $answer);
+
+$modefits = `detsearch $close -quiet -image $first $ccds[0] split -type modes`; chop ($modefits);
+if ($?) { die "can't get mode image\n"; }
+
+@words = split ("/", $modefits);
+$basename = $words[-1];
+
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+
+# override default values (set in modes.pro):
+if ($limitmodes) { print MANA "\$Nmodes = $limitmodes\n"; }
+if ($mapfile) { print MANA "\$savemap = $mapfile\n"; }
+print MANA "demodemap $inlist $outlist $modefits $basename\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) {
+    print STDERR "problem with demodemap\n";
+    exit 1;
+}
+
+print STDOUT "finished with demodemap\n";
+
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/detflips
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/detflips	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/detflips	(revision 22322)
@@ -0,0 +1,90 @@
+#!/usr/bin/env perl
+
+# grab the command line options [-C config] [-c configfile]
+$config = "";
+$preserve = 0;
+@tARGV = ();
+while (@ARGV) {
+    if ($ARGV[0] eq "-C") {
+	$config = "$config -C $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-c") {
+	$config = "$config -c $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-preserve") {
+        $preserve = 1;
+        shift; next;
+    }
+    push @tARGV, $ARGV[0];
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 3) { die "ERROR: USAGE: detflips (inlist) (output) (type)\n" }
+
+$inlist = $ARGV[0];
+$output = $ARGV[1];
+$type   = "\U$ARGV[2]\E";
+
+# count lines in $inlist, adjust statmode (FLAT only)
+($nlines) = split (" ", `wc -l $inlist`);
+$statmode = 0;
+if ($nlines < 6) { $statmode = 1; }
+if ($nlines == 0) { die "ERROR: no files in source list, skipping\n"; }
+
+# delete old output file 
+if (-e $output) { unlink "$output"; }
+
+$temp1 = `mktemp /tmp/detflips.XXXXXX`; chop $temp1;
+$temp2 = `mktemp /tmp/\@detflips.XXXXXX`; chop $temp2;
+
+if ($type eq "BIAS")    { $paramkey = "FLIPS_PARAM_DARK";   $detype = "bias";    }
+if ($type eq "DARK")    { $paramkey = "FLIPS_PARAM_DARK";   $detype = "dark";    }
+if ($type eq "FLAT")    { $paramkey = "FLIPS_PARAM_FLAT";   $detype = "skyflat"; }
+if ($type eq "FRINGE")  { $paramkey = "FLIPS_PARAM_FRINGE"; $detype = "fringe";  }
+if ($type eq "STACK")   { $paramkey = "FLIPS_PARAM_STACK";  $detype = "science";  }
+
+if ($paramkey eq "")  { 
+    print STDERR "ERROR: invalid detrend type $type\n";
+    exit 1;
+}
+
+$paramfile = `gconfig $config $paramkey`; chop $paramfile;
+if ($?) { die "ERROR: missing parameter file $paramkey from config\n"; }
+
+open (FILE, "$paramfile");
+@params = <FILE>;
+close (FILE);
+
+print STDERR "detflips parameter file: $paramfile\n";
+
+foreach $line (@params) {
+    $line =~ s|LISTNAME|$temp2|;
+    $line =~ s|FILENAME|$output|;
+    $line =~ s|COMMENT|$type|;
+    $line =~ s|DETRENDTYPE|$detype|;
+    if ($type eq "FLAT") { $line =~ s|STATMODE|$statmode|; }
+}
+
+open (FILE, ">$temp1");
+print FILE "@params\n";
+close $temp1;
+
+system ("cp $inlist $temp2");
+system ("imcombred $temp1");
+$stat = $?;
+
+if (! $preserve) {
+    unlink ($temp1);
+    unlink ($temp2);
+}
+
+if ($stat) {
+    print STDERR "ERROR: failure in detflips\n";
+} else {
+    print STDERR "SUCCESS\n";
+}
+
+exit $stat;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dt.select
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dt.select	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/dt.select	(revision 22322)
@@ -0,0 +1,219 @@
+#!/usr/bin/env perl
+$SKIPDOME = 0;
+
+if (@ARGV != 6) { die "ERROR: USAGE: imselect (type) (filter) (tstart) (tend) (output) (SPLIT | MEF)\n" }
+
+$type   = "\U$ARGV[0]\E";
+$filter = "\U$ARGV[1]\E";
+$start  = $ARGV[2];
+$stop   = $ARGV[3];
+$output = $ARGV[4];
+$mode   = $ARGV[5];
+
+unless ($output =~ /CCDNUM/) { die "ERROR: output file needs CCDNUM component\n"; }
+
+if (($mode ne "MEF") && ($mode ne "SPLIT")) { die "ERROR: mode must be either -split or -mef\n"; }
+
+if ($type eq "DARK") { }
+
+if ($type eq "FLAT") {
+    # define some of the relevant parameters
+    $max = 60000;
+
+    $tmp = `filtnames $filter`; chop $tmp;
+    if ($?) { die "ERROR: invalid filter $filter\n"; }
+    $filter = $tmp;
+
+    # recipe file defines detrend types & cutoff exptime values
+    $recipefile = `gconfig DETREND_RECIPES`; chop $recipefile;
+    if ($?) { die "ERROR: missing DETREND_RECIPES in configuration\n"; }
+
+    ($tmp, $tmp, $extras) = split (" ", `gconfig -c $recipefile $filter`);
+    if ($?) { die "ERROR: missing detrend recipe for $filter\n"; }
+
+    # all filters have 'flat', grab extras:
+    ($detype, $minflux, $min) = split (",", $extras);
+    if ("\U$detype\E" ne "FLAT") { die "ERROR: flat extras are missing\n"; }
+}
+
+$CCDWORD      = `gconfig CCDNUM-KEYWORD`; chop $CCDWORD;
+$CHIPSKIP     = `gconfig CHIP_SKIP_LIST`; chop $CHIPSKIP;
+@chipskip = ();
+if (-e $CHIPSKIP) { 
+    open (FILE, $CHIPSKIP);
+    @chipskip = <FILE>;
+    close (FILE);
+    foreach $chip (@chipskip) { chop $chip; }
+}
+
+# relevant camera parameters
+$Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+if ($?) { die "error in config\n"; }
+
+($Naxis1, $Naxis2) = split (" ", `cameraconfig -axes`);
+if ($?) { die "error in config\n"; }
+
+@ccds = split (" ", `cameraconfig -ccds`);
+if ($?) { die "error in config\n"; }
+
+@ccdn = split (" ", `cameraconfig -ccdn`);
+if ($?) { die "error in config\n"; }
+
+@good = ();
+@marginal = ();
+
+# define filter lines for imsearch.  for flats, there may be alternate filter names to test
+@filtline = ();
+if ($type eq "FLAT") {
+    # convert given filter name to list of possible names:
+    @filtlist = split (" ", `filtnames $filter -all`);
+    foreach $filt (@filtlist) {
+	push @filtline, "-type flat -filter $filt -mode $mode -trange $start $stop";
+    }
+} 
+if ($type eq "BIAS") {
+    push @filtline, "-type bias -mode $mode -trange $start $stop";
+} 
+if ($type eq "DARK") {
+    push @filtline, "-type dark -etime $ARGV[1] -mode $mode -trange $start $stop";
+} 
+
+# run selection on each possible $filtline
+@list = ();
+foreach $line (@filtline) {
+    @tlist = `imsearch -ccd $ccds[0] $line`;
+    @list = (@list, @tlist);
+}
+$Nlist = @list;
+print STDERR "found $Nlist images\n";
+
+FRAME:
+foreach $frame (@list) {
+    @words = split (" ", $frame);
+
+    # skip domeflat images
+    if ($SKIPDOME && ($type eq "FLAT")) {
+	# a fairly lame and non-robust way to detect dome flats
+	@answer = `echo $words[4]/$words[5] | fields OBJECT | grep -i dome`;
+	if ($answer[0] ne "") { print STDERR "$words[5] rejected: dome flat\n"; next FRAME; }
+	
+	# test for acceptable time (or is probably a dome flat)
+	$answer = `dusktime $words[8]`;
+	($tmp, $tmp, $dt) = split (" ", $answer);
+	if ($dt < 0) { print STDERR "$words[5] rejected: not twilight\n"; next FRAME; }
+    }
+
+    # image(s) must exist on disk:
+    if ($mode eq "SPLIT") {
+	# all $Nccd images must exist on disk, split files are assumed to be 
+	# in a subdirectory of their own, .fits extension assumed
+	@answer = <$words[4]/*.fits>;
+	if (@answer != $Nccd) { print STDERR "$words[5] rejected: missing images\n"; next FRAME; }
+    } else {
+	# base file should exist:
+	if (! -e "$words[4]/$words[5]") { print STDERR "file $words[4]/$words[5] missing, skipping\n"; next FRAME; }
+    }
+
+    # images have to have the correct dimensions
+    if ($mode eq "SPLIT") {
+	($tmp, $Nx, $Ny) = split (" ", `echo $words[4]/$words[5] | fields NAXIS1 NAXIS2`);
+    } else {
+	($tmp, $Nx, $Ny) = split (" ", `echo $words[4]/$words[5] | fields -x 0 NAXIS1 NAXIS2`);
+    }
+    if ($?) { print STDERR "failure to find NAXIS1, NAXIS2 in file $words[4]/$words[5], skipping\n"; next FRAME; }
+    if ($Nx != $Naxis1) { print STDERR "$words[5] rejected: wrong dimensions\n"; next FRAME; }
+    if ($Ny != $Naxis2) { print STDERR "$words[5] rejected: wrong dimensions\n"; next FRAME; }
+    
+    # must have Nccd entries in db:
+    if ($mode eq "SPLIT") { 
+	@answer = split ("/", $words[4]); 
+	$match = $answer[-1]; 
+    } else { 
+	$match = $words[5]; 
+    }
+    @list2 = `imsearch -name $match`;
+    if (@list2 != $Nccd) { print STDERR "$words[5] rejected: missing entries in image database\n"; next FRAME; }
+
+    # test flux levels for FLATS
+    if ($type eq "FLAT") {
+       CHIP:  # check for counts or flux out of range or any chip
+	foreach $chip (@list2) {
+	    @words2 = split (" ", $chip);
+	    foreach $chipskip (@chipskip) { 
+		if ($words2[3] eq $chipskip) { next CHIP; }
+	    }
+	    
+	    # test for acceptable counts range
+	    if ($words2[11] < $min) { print STDERR "$words[5] rejected: signal too low: $words2[11] vs $min\n"; next FRAME; }
+	    if ($words2[11] > $max) { print STDERR "$words[5] rejected: signal too high: $words2[11] vs $max\n"; next FRAME; }
+	    
+	    # test for acceptable flux
+            if ($words2[7] > 0.0) {
+	       $Flux = $words2[11] / $words2[7];
+	    } else {
+	       $Flux = 0.0;
+	    }
+	    if ($Flux < $minflux) { print STDERR "$words[5] rejected: flux too low: $Flux vs $minflux\n"; next FRAME; }
+	}
+    }
+    # test bias levels for BIAS / DARK
+    if (($type eq "BIAS") || ($type eq "DARK")) {
+       CHIP:  # check for values in bias column
+	foreach $chip (@list2) {
+	    @words2 = split (" ", $chip);
+	    if ($words2[10] == 0.0) { print STDERR "$words[5] rejected: no imstats\n"; next FRAME; }
+	}
+    }
+
+    # add good images to the output lists (use CCD N for output filenames, CCD ID for [extname] in mef files)
+    # entries are not guaranteed to be in CCD N sequence
+
+    foreach $line (@list2) {
+	@words2 = split (" ", $line);
+	$ccdid  = $words2[3];
+	if ($mode eq "MEF") {
+	    @$ccdid = (@$ccdid, "$words2[4]/$words2[5]\[$ccdid]");
+	} else {
+	    @$ccdid = (@$ccdid, "$words2[4]/$words2[5]");
+	}
+    }
+    if (($type eq "BIAS") && (@$ccdid > 30)) { last FRAME; }
+    if (($type eq "DARK") && (@$ccdid > 30)) { last FRAME; }
+    if (($type eq "FLAT") && (@$ccdid > 50)) { last FRAME; }
+}
+
+# write output to Nccd list files 
+for ($i = 0; $i < $Nccd; $i++) {
+    $ccd   = $ccdn[$i];
+    $ccdid = $ccds[$i];
+
+    $name = $output;
+    $name =~ s/CCDNUM/$ccd/;
+
+    open (LIST, ">$name");
+    foreach $line (@$ccdid) {
+	print LIST "$line\n";
+    } 
+    close (LIST);
+}
+print STDERR "SUCCESS\n\n";
+exit 0;
+
+# we need to select the list of images which satisfy a few criteria:
+# 1) all 12 CCDs must exist
+# 2) stats must be measured for each
+# 3) stats for all 12 must be within range (2000 - 40000)
+# 4) NAXIS1 == 2080 && NAXIS2 == 4128
+
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    die "@_";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.export
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.export	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.export	(revision 22322)
@@ -0,0 +1,88 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 3) { die "ERROR: usage: elixir.export (rawfile) (datfile) (mode)\n"; }
+
+$datdir = `gconfig DATDIR`; chop $datdir;
+$logdir = `gconfig LOGDIR`; chop $logdir;
+$config = "$datdir/export.conf";
+
+$rawfile = $ARGV[0];
+$datfile = $ARGV[1];
+$MODE    = $ARGV[2];
+
+if (! -e $config) { die "ERROR: config file $config missing\n"; }
+
+open (FILE, $config);
+while ($line = <FILE>) {
+    if ($line =~ /^\s*$/) { next; } # skip empty lines
+    if ($line =~ /^\s*\#/) { next; } # skip commented lines
+    $Nlist = ($id, $runids, $filters, $modes, $outdir) = split (" ", $line);
+    if ($Nlist != 6) { print STDERR "WARNING: error in config file: $line\n"; }
+    push @id, $id;
+    $runids{$id}  = $runids;
+    $filters{$id} = $filters;
+    $modes{$id}   = $modes;
+    $outdir{$id}  = $outdir;
+}
+close (FILE);
+
+($tmp, $RUNID) = split (" ", `echo $rawfile | fields RUNID`);
+($tmp, $FILTER) = split (" ", `echo $rawfile | fields FILTER`);
+
+$FILTER = `filtnames $FILTER`; chop $FILTER;
+
+open (LOG, ">>$logdir/export.log");
+
+if ($MODE eq "any") { goto wanted; }
+
+foreach $id (@id) {
+
+    if (!findword ($MODE, $modes{$id})) { next; }
+    if (!findword ($RUNID, $runids{$id})) { next; }
+    if (!findword ($FILTER, $filters{$id})) { next; }
+
+    # success, copy datfile to outdir:
+
+    system ("cp $datfile $outdir{$id}\n");
+    if ($?) { print STDERR "WARNING: can't write $datfile to $outdir{$id}\n"; }
+
+    open (FILE, ">>$outdir/export.tbl");
+    print FILE "$datfile\n";
+    close (FILE);
+
+    print LOG "export $datfile to $outdir for $id\n";
+}
+close (LOG);
+
+print STDERR "SUCCESS: export $datfile to $outdir for $id\n";
+exit 0;
+
+wanted:
+foreach $id (@id) {
+
+    if (!findword ($RUNID, $runids{$id})) { next; }
+    if (!findword ($FILTER, $filters{$id})) { next; }
+
+    print LOG "export $datfile is wanted, process\n";
+    close (LOG);
+    print STDERR "SUCCESS: $datfile is wanted\n";
+    exit 0;
+}
+
+print LOG "export $datfile is not wanted, skip\n";
+close (LOG);
+print STDERR "ERROR: $datfile is not wanted\n";
+exit 1;
+
+sub findword {
+    # findword $WORD $list
+    $WORD = "\U$_[0]\E";
+    $list = $_[1];
+
+    @word = split (",", $list);
+    foreach $word (@word) {
+	$word = "\U$word\E";
+	if ($word eq $WORD) { return 1; }
+    }
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.link
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.link	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.link	(revision 22322)
@@ -0,0 +1,150 @@
+#!/usr/bin/env perl
+
+# this script searches for new links in the archive elixir directory,
+# creates links for the new images in the elixir TMPDIR tree,
+# registers the images in the elixir database, and 
+# sends the images to imstats for processing.
+
+use English;
+
+# check usage
+if (@ARGV != 0) { die "USAGE: elixir.link\n"; }
+
+# what do we do about config file?  demand account be set up correctly?
+$camera = `gconfig CAMERA.CURRENT`; chop $camera;
+$runid  = `gconfig RUNID.CURRENT`; chop $runid;
+$opts = "-D CAMERA $camera -D RUNID $runid";
+
+{ # check camera validity
+    if ($camera eq "cfh12k")  { last; } 
+    if ($camera eq "megacam") { last; } 
+    if ($camera eq "meganorth") { last; } 
+    if ($camera eq "cfhtir")  { last; } 
+    die "invalid camera $camera";
+}
+
+$fifos = `gconfig $opts FIFOS`; chop $fifos;
+if ($?) { die "error in config system: missing FIFOS\n"; }
+
+$rawdir = `gconfig $opts RAWDIR`;  chop $rawdir;
+if ($?) { die "error in config system: missing RAWDIR\n"; }
+
+$linkdir = `gconfig $opts ARCLINK_DIR`;  chop $linkdir;
+if ($?) { die "error in config system: missing ARCLINK_DIR\n"; }
+
+$archive = `gconfig $opts ARCHIVE_DIR`;  chop $archive;
+if ($?) { die "error in config system: missing ARCHIVE_DIR\n"; }
+
+@links = glob ("$linkdir/*");
+
+if (! @links) { exit 0; }
+
+$Ngood = 0;
+$Nskip = 0;
+$Nfail = 0;
+
+foreach $name (@links) {
+    if (! -l $name) { $Nskip ++; next; }
+    
+    # find real path
+    $rootname     = filename ($name);
+    $arcfile = sprintf "%s/%s", $archive, $rootname;
+    
+    # set new path (skip if it exists)
+    $rawfile = sprintf "%s/%s", $rawdir, $rootname;
+    if ((-e $rawfile) || (-l $rawfile)) { 
+	$Nskip ++;
+	unlink ($name);
+	next; 
+    }
+    
+    # link file into rawdir:
+    $status = symlink ($arcfile, $rawfile);
+    if (! $status) {
+	$Nfail ++;
+	next;
+    }
+    
+    # these files always come in as MEF or CUBE 
+    # -needtype will skip focus frames
+    vsystem ("imsort -needtype $opts $rawfile");
+    if ($?) { 
+	# allow for retry on failure
+	$Nfail ++;
+
+	# save links for record.
+	if (! -d "$linkdir/failure") { mkdir "$linkdir/failure"; }
+	system ("mv $name $linkdir/failure/");
+	next; 
+    }
+
+    $Ngood ++;
+    unlink $name;
+}
+
+print STDERR "elixir.link: $Ngood good, $Nskip skip, $Nfail fail\n";
+exit 0;
+
+# utilities ##############################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    my ($status) = system ("@_");
+    $status;
+}
+
+sub filename { 
+
+    my ($path) = $_[0];
+    my (@tmp);
+    
+    @tmp = split ("/", $path);
+    return $tmp[-1];
+}
+
+sub convertlink {
+    my ($link) = $_[0];
+    my ($file);
+
+    while (-l $link) {
+	$file  = readlink ($link);
+	$path1 = path ($link);
+	$path2 = path ($file);
+	
+	unless ($path2 =~ m|^/|) {
+	    $file = "$path1/$file";
+	}	
+	$link = $file;
+    }
+    return ($link);
+}    
+
+sub usage {
+    
+    if (@ARGV == 1) {
+	print STDERR "\n --- user modes ---\n";
+	print STDERR "create (run) (start) (stop)\n";
+	print STDERR "addqso (runid) (qsoid)\n";
+	print STDERR "delqso (qsoid)\n";
+	print STDERR "sched (runid)\n";
+	print STDERR "status\n";
+	&goodbye;
+    }
+    
+    if (@ARGV > 2) { print STDERR "USAGE: prepare.run (help) [mode]\n"; &goodbye; }
+    
+    &goodbye;
+}
+
+sub path {
+
+    my ($file) = $_[0];
+
+    @words = split ("/", $file);
+
+    pop (@words);
+    
+    $path = join ("/", @words);
+
+    return ($path);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.photreport
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.photreport	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.photreport	(revision 22322)
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 2) { die "USAGE : elixir.photreport (name) (catdir)\n"; }
+
+$name   = $ARGV[0];
+$catdir = $ARGV[1];
+
+# test named Xserver, or start vnc on specified machine
+$xhost = `gconfig XHOST`; chop $xhost;
+$xdisp = `gconfig XDISP`; chop $xdisp;
+if (system ("/usr/bin/X11/xdpyinfo -display $xdisp")) {
+    print STDERR "X server is not running. start it now on $xhost\n";
+    exit 1;
+}
+$ENV{'DISPLAY'} = $xdisp;
+
+# select filters 
+@filter = split (" ", `filtnames list`);
+
+$logdir   = `gconfig LOGDIR`;   chop $logdir;
+$confdir  = `gconfig CONFDIR`;  chop $confdir;
+$script   = "$confdir/mana/standards";
+
+# run mana, call script
+open (STAT, "|dvo -D CATDIR $catdir --norc");
+print STAT "input $script\n";
+
+# place commands in a macro
+print STAT "macro go\n";
+foreach $filter (@filter) {
+    @word = split (" ", `filtnames $filter -cal`);
+    if (@word[1] == 0) { next; }
+    $target = $word[2];
+    print STAT "echo $filter -> $target\n";
+    print STAT "photreport $target $name\n";
+}
+print STAT "exit 0\n";
+print STAT "end\n";
+
+print STAT "go\n";
+print STAT "exit 1\n";
+close (STAT);
+if ($?) {
+    print STDERR "ERROR problem with photreport\n";
+    exit 1;
+}
+print STDOUT "SUCCESS: finished with photreport\n";
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.postrun
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.postrun	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.postrun	(revision 22322)
@@ -0,0 +1,690 @@
+#!/usr/bin/env perl
+
+# check usage
+if (@ARGV == 0) { &usage; }
+if ($ARGV[0] eq "help") { &usage; }
+
+@ARGV = elixir_config (@ARGV);
+
+if ($ARGV[0] eq "config") { &set_config; }
+if ($ARGV[0] eq "set")    { &set_entry; }
+if ($ARGV[0] eq "state")  { &state; }
+if ($ARGV[0] eq "update") { &update; }
+
+&load_state;
+   
+if ($ARGV[0] eq "imstats")   { &imstats_state ("imstats");     &save_state; &goodbye; }
+if ($ARGV[0] eq "ptolemy")   { &ptolemy_state ("ptolemy");     &save_state; &goodbye; }
+if ($ARGV[0] eq "standards") { &standards_state ("standards"); &save_state; &goodbye; }
+if ($ARGV[0] eq "validate")  { &validate_state ("validate");   &save_state; &goodbye; }
+
+&escape ("unknown command $ARGV[0]");
+
+############# change current run ##############
+sub set_config {
+    if (@ARGV != 3) { &escape ("USAGE: elixir.postrun config (camera) (run)"); }
+    
+    $camera = $ARGV[1];
+    $run    = $ARGV[2];
+
+    # set current run id:
+    system ("mkrun sys camera.postrun $camera");
+    if ($?) { &escape ("can't define camera: error in mkrun"); }
+    
+    # set current run id:
+    system ("mkrun sys runid.postrun $run");
+    if ($?) { &escape ("can't define run: error in mkrun"); }
+    
+    &goodbye;
+}
+
+##############
+sub state {
+    print STDOUT "CAMERA: $camera\n";
+    print STDOUT "RUNID:  $runid\n";
+    print STDOUT "RUNDIR: $rundir\n";
+
+    &load_state;
+    foreach $entry (@entry) {
+	printf STDERR "%-15s %-10s\n", $entry,  $state{$entry};
+    }
+    &goodbye;
+}
+
+##############
+sub load_state {
+    # db contains: (entry) (state)
+    open (FILE, $dbfile);
+    @list = <FILE>;
+    close (FILE);
+
+    # there should be one line for each possible entry in @entry
+    # if a line is missing, set it to init
+    # if a line is extra, kill it.
+
+    # look for invalid entries
+    foreach $line (@list) {
+	($entry, $state) = split (" ", $line);
+	if (! $def_state{$entry}) { next; }
+	$state{$entry} = $state;
+    }
+
+    # look for missing entries
+    foreach $entry (@entry) {
+	if (! $state{$entry}) { $state{$entry} = $def_state{$entry}; }
+    }
+}
+
+##############
+sub save_state {
+    open (FILE, ">$dbfile");
+    foreach $entry (@entry) {
+	print FILE "$entry $state{$entry}\n";
+    }
+    close (FILE);
+}
+   
+##############
+sub set_entry {
+
+    if (@ARGV != 3) { &escape ("USAGE: elixir.postrun set (entry) (value)"); }
+
+    $reset = $ARGV[1];
+    $value = $ARGV[2];
+    
+    &load_state;
+
+    if (! $state{$reset}) { &escape ("invalid entry $reset"); }
+
+    $state{$reset} = $value;
+    &save_state;
+    &goodbye;
+}
+
+##############
+sub update {
+    
+    &load_state;
+	   
+    foreach $entry (@entry) {
+	update_state ($entry);
+	printf STDERR "%-15s %-10s\n", $entry,  $state{$entry};
+    }
+
+    &save_state;
+    &goodbye;
+}
+
+##############
+sub update_state {
+    
+    my ($entry) = $_[0];
+    
+    if ($entry eq "camera")    { &camera_state    ($entry); }
+    if ($entry eq "imstats")   { &imstats_state   ($entry); }
+    if ($entry eq "mkdetrend") { &detrend_state   ($entry); }
+    if ($entry eq "mkfringe")  { &detrend_state   ($entry); }
+    if ($entry eq "ptolemy")   { &ptolemy_state   ($entry); }
+    if ($entry eq "standards") { &standards_state ($entry); }
+    if ($entry eq "validate")  { &validate_state  ($entry); }
+}
+
+##############
+sub ptolemy_state {
+    
+    my ($entry) = "ptolemy";
+    $current = $state{$entry};
+    if (! $current) { &escape ("error: ptolemy state not set?"); }
+    
+    $fifos  = `gconfig FIFOS`; chop $fifos;
+    $ptlist = "$datdir/$runid/$runid.ptlist";
+    $ptlog  = "$datdir/$runid/$runid.log";
+    
+    # if we can, run 'elixir complete'
+    if (($current eq "init") && ($state{"mkdetrend"} eq "done")) {
+	$state{$entry} = "running";
+	&save_state;
+
+	if (-e "$fifos/complete.pid") { 
+	    print STDERR "elixir (sex)complete already running? check pid file $fifos/complete.pid"; 
+	    &save_escape ($entry, "failed to start ptolemy"); 
+	}
+
+	if (! -d "$datdir/$runid") { mkdir ("$datdir/$runid", 0777); }
+	($tmp, $start, $stop) = split (" ", `mkrun run $runid`);
+	if ($?) { &save_escape ($entry, "error in RUNID $runid"); }
+	vsystem ("imsearch -type object -trange $start $stop -pt > $ptlist");
+	if ($?) { &save_escape ($entry, "error extracting images from db"); }
+ 	vsystem ("elixir -D mode sexcomplete $ptlist >& $ptlog &");
+
+	# wait for up to 10 seconds for $fifos/complete.pid to exist 
+	for ($Nwait = 0; ($Nwait < 30) && (! -e "$fifos/complete.pid"); $Nwait ++) { sleep 2; }
+
+
+	if (! -e "$fifos/complete.pid") { &save_escape ($entry, "failed to start elixir (sex)complete"); }
+    }
+
+    # has 'elixir complete' finished? successfully?
+    if ($current eq "running") {
+	if (-e "$fifos/complete.end") {
+	    $answer = `wc -l $fifos/complete.success`;
+	    ($Nsuccess) = split (" ", $answer);
+	    $answer = `wc -l $fifos/complete.failure`;
+	    ($Nfailure) = split (" ", $answer);
+	    $answer = `wc -l $ptlist`;
+	    ($Ntotal) = split (" ", $answer);
+
+	    $state{$entry} = "fail";
+	    if ($Nsuccess > 0.8*$Ntotal) { 
+		$state{$entry} = "done"; 
+	    } else {
+		print STDERR "too many failures: $Nsuccess of $Ntotal\n";
+	    }
+	    if ($Nsuccess + $Nfailure > $Ntotal) { 
+		print STDERR "output list is inconsistent\n";
+		$state{$entry} = "fail"; 
+	    }
+	}
+	if ((! -e "$fifos/complete.end") && (! -e "$fifos/complete.pid")) {
+	    print STDERR "elixir complete killed?\n";
+	    $state{$entry} = "fail"; 
+	}
+    }
+}
+
+##############
+sub standards_state {
+
+    my ($entry) = "standards";
+    $current = $state{$entry};
+
+    print "entry: $entry\n";
+    print "current: $current\n";
+
+    if (! $current) { &escape ("error: standards state not set?"); }
+
+    # if elixir complete is done, add standards, run photometry routines
+    if (($current eq "init") && ($state{"ptolemy"} eq "done")) {
+	$state{$entry} = "running";
+	&save_state;
+
+	$refs = `gconfig REFSDIR`; chop $refs;
+	$stds = "$refs/standards";
+    
+	# insert standards in photometry database
+	foreach $filter (@filter) {
+	    @words = split (" ", `filtnames $filter -cal`);
+	    if ($words[1] == 0) { next; }
+	    $photref = $words[2];
+	    vsystem ("addstar -replace -v -ref $stds/$photref.dat -p $photref");
+	}
+
+	# cd to $rundir so photreport plots land there?
+	chdir $rundir;
+
+	# run elixir.photreport to generate plots (eg 02Ak03.B.time.png) and phot.reg files
+	vsystem ("elixir.photreport $runid $catdir");
+	if ($?) { goto standards_fail; }
+
+	# load lines from phot.reg files and execute:
+	@reglist = ();
+	foreach $filter (@filter) {
+	    $regfile = "$runid.$filter.phot.reg";
+	    open (FILE, $regfile);
+	    @list = <FILE>;
+	    close (FILE);
+	    @reglist = (@reglist, @list);
+	}
+	foreach $line (@reglist) {
+	    vsystem ($line);
+	}
+
+	# get date range for this run
+	$answer = `mkrun run $runid`;
+	($tmp, $start, $stop) = split (" ", $answer);
+	if ($start eq "") { &escape ("error with date range: $line"); }
+	if ($stop eq "") { &escape ("error with date range: $line"); }
+
+	# create transparency list, execute
+	vsystem ("mktrans $start $stop > $runid.trans.reg");
+	open (FILE, "$runid.trans.reg");
+	@list = <FILE>;
+	close (FILE);
+	foreach $line (@list) {
+	    vsystem ($line);
+	}
+
+	# create transparency report, execute
+	vsystem ("mktreport $start $stop > $runid.trans.txt");
+
+	$state{$entry} = "done";
+	&save_state;
+    }
+    return;
+
+  standards_fail:
+    $state{$entry} = "fail";
+    return;
+}
+
+##############
+sub camera_state {
+
+    # camera : 'pending' (date < start) 
+    # camera : 'on' (date in range start - stop) 
+    # camera : 'off' (date > stop) 
+
+    # transition on -> off : trigger message about mkdetrend 
+    
+    # get date range for this run
+    $answer = `mkrun run $runid`;
+    ($tmp, $start, $stop) = split (" ", $answer);
+    if ($start eq "") { &escape ("error with date range: $line"); }
+    if ($stop eq "") { &escape ("error with date range: $line"); }
+    $mjd_start = &date_to_mjd ($start);
+    $mjd_stop  = &date_to_mjd ($stop);
+    
+    # get current value of mjd
+    ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime ();
+    $year += 1900;
+    $mon += 1;
+    $mjd_now = &get_mjd ($year, $mon, $mday);
+
+    # check appropriate state
+    $state = "on";
+    if ($mjd_now < $mjd_start) { $state = "pending"; }
+    if ($mjd_now > $mjd_stop)  { $state = "off"; }
+
+    # if we are at end of run, check that mkdetrend has been started
+    if (($state{"camera"} eq "on") && ($state eq "off")) {
+	if ($state{"mkdetrend"} eq "init") {
+	    &notify ("start mkdetrend");
+	}
+    }
+    $state{"camera"} = $state;
+}
+	       
+##############
+sub notify {
+    print STDERR "$_[0]\n";
+}
+
+##############
+sub validate_state {
+
+    my ($entry) = "validate";
+    $current = $state{$entry};
+    if (! $current) { &escape ("error: validate state not set?"); }
+
+    chdir $rundir;
+
+    # if elixir complete is done, add standards, run photometry routines
+    if (($current eq "init") && ($state{"standards"} eq "done")) {
+	$state{$entry} = "running";
+	&save_state;
+	
+	# find the first unused val file (run.val.N)
+	$ver = 1;
+	$outfile = "$rundir/$runid.val.$ver";
+	while (-e $outfile) {
+	    $ver ++;
+	    $outfile = "$rundir/$runid.val.$ver";
+	}
+
+	# run first pass
+	vsystem ("validate $runid > $outfile");
+	if ($?) { goto validate_fail; }
+
+	# check output results
+	print STDERR "validate $runid -stats -infile $outfile\n";
+	@result = `validate $runid -stats -infile $outfile`;
+	if ($?) { goto validate_fail; }
+	
+	system ("validate $runid -infile $outfile -update");
+
+	# order should be: run, valid, invalid, subvalid, dataerr
+	($value, $Nvalid) = split (" ", $result[1]);
+	if ($value ne "valid") { goto validate_fail; }
+	($value, $Ninvalid) = split (" ", $result[2]);
+	if ($value ne "invalid") { goto validate_fail; }
+	($value, $Nsubvalid) = split (" ", $result[3]);
+	if ($value ne "subvalid") { goto validate_fail; }
+	($value, $Ndataerr) = split (" ", $result[4]);
+	if ($value ne "dataerr") { goto validate_fail; }
+	
+	if ($invalid > 0) { goto validate_fail; }
+	if ($dataerr > 0) { goto validate_fail; }
+
+	# if we pass validation, run validate -update:
+
+	$state{$entry} = "done";
+    }
+
+    if ($current eq "fail") {
+
+	# recheck the last validation file (run.val.N)
+	$outfile = "";
+	$ver = 1;
+	$try = "$rundir/$runid.val.$ver";
+	while (-e $try) {
+	    $outfile = $try;
+	    $ver ++;
+	    $try = "$rundir/$runid.val.$ver";
+	}
+	if ($outfile eq "") { goto validate_fail; }
+
+	# check output results
+	print STDERR "validate $runid -stats -infile $outfile\n";
+	@result = `validate $runid -stats -infile $outfile`;
+	if ($?) { goto validate_fail; }
+	
+	system ("validate $runid -infile $outfile -update");
+
+	# order should be: run, valid, invalid, subvalid, dataerr
+	$Nvalid = $Ninvalid = $Nsubvalid = $Ndataerr = -1;
+	foreach $line (@result) {
+	    ($value, $Nvalue) = split (" ", $line);
+	    if ($value eq "valid")    { $Nvalid    = $Nvalue; }
+	    if ($value eq "invalid")  { $Ninvalid  = $Nvalue; }
+	    if ($value eq "subvalid") { $Nsubvalid = $Nvalue; }
+	    if ($value eq "dataerr")  { $Ndataerr  = $Nvalue; }
+	}
+	if ($Nvalid == -1)                        { goto validate_fail; }
+	if ($Nsubvalid == -1)                     { goto validate_fail; }
+	if (($Ninvalid == -1) || ($Ninvalid > 0)) { goto validate_fail; }
+	if (($Ndataerr == -1) || ($Ndataerr > 0)) { goto validate_fail; }
+
+	# if we pass validation, run validate -update:
+	$state{$entry} = "done";
+    }
+    return ;
+
+  validate_fail:
+    $state{$entry} = "fail";
+    return;
+}
+
+##############
+sub imstats_state {
+
+    my ($entry) = "imstats";
+    $current = $state{$entry};
+    if (! $current) { &escape ("error: imstats state not set?"); }
+    
+    $fifos  = `gconfig FIFOS`; chop $fifos;
+    $pid    = "$fifos/imstats.pid";
+    $end    = "$fifos/imstats.pid";
+    $ptlist = "$datdir/$runid/$runid.redo.list";
+    $ptlog  = "$datdir/$runid/$runid.redo.log";
+
+    # if we can, run 'elixir complete'
+    if (($current eq "init") && ($state{"camera"} eq "off")) {
+	$state{$entry} = "running";
+	&save_state;
+	
+	# check for currently running imstats
+	if (-e $pid) { 
+	    print STDERR "elixir imstats still running?\n"; 
+	    print STDERR "run 'elixir -C $camera -D mode imstats -kill' to stop\n";
+	    &save_escape ($entry, "failure on imstats");
+	}
+
+	# extract redo list, run processing
+	($tmp, $start, $stop) = split (" ", `mkrun run $runid`);
+	if ($?) { &save_escape ($entry, "error in RUNID $runid"); }
+	vsystem ("imsearch -trange $start $stop -proc f -pt > $ptlist");
+	if ($?) { &save_escape ($entry, "error extracting unprocessed data"); }
+	vsystem ("elixir -D mode imstats $ptlist >& $ptlog &");
+	
+	# wait for up to 10 seconds for $pid to exist 
+	for ($Nwait = 0; ($Nwait < 30) && (! -e "$pid"); $Nwait ++) { sleep 2; }
+	if (! -e "$pid") { &save_escape ($entry, "failed to start elixir imstats"); }
+    }
+
+    # has 'elixir complete' finished? successfully?
+    if ($current eq "running") {
+	if (-e "$fifos/imstats.end") {
+	    $answer = `wc -l $fifos/imstats.success`;
+	    ($Nsuccess) = split (" ", $answer);
+	    $answer = `wc -l $fifos/imstats.failure`;
+	    ($Nfailure) = split (" ", $answer);
+	    $answer = `wc -l $ptlist`;
+	    ($Ntotal) = split (" ", $answer);
+
+	    print "Ntotal: $Ntotal\n";
+	    print "Nsuccess: $Nsuccess\n";
+	    print "Nfailure: $Nfailure\n";
+
+	    if ($Nsuccess < 0.5*$Ntotal) { &save_escape ($entry, "too many failures: $Nsuccess of $Ntotal"); }
+	    if ($Nsuccess + $Nfailure > $Ntotal) { &save_escape ($entry, "output list is inconsistent"); }
+	    $state{$entry} = "done"; 
+	    &save_state;
+	    return;
+	}
+	if ((! -e "$fifos/imstats.end") && (! -e "$fifos/imstats.pid")) { &save_escape ($entry, "elixir complete killed?"); }
+    }
+}
+
+##############
+sub detrend_state {
+
+    # force sequencing (mkdetrend -> mkfringe):
+    if (($entry eq "mkfringe") && ($state{"mkdetrend"} ne "done")) {
+	$state{$entry} = "init";
+	return;
+    }
+
+    # mkdetrend & mkfringe determine run from their own references, 
+    # which can be overridden with the '-run RUNID' flag
+
+    # determine state from mkdetrend / mkfringe response
+    $entry = $_[0];
+    print STDERR "$entry state\n";
+    @list = `$entry state`;
+    print STDERR "@list\n";
+
+    # skip first 4 lines
+    shift @list; shift @list; shift @list; shift @list; shift @list;
+
+    $Ninit = $Nfail = $Ndone = $Nrun = $Nmiss = 0;
+    foreach $line (@list) {
+	# interesting values for state: 
+	# init, not.available, running.xxx, done, fail.xxx
+	@words = split (" ", $line);
+	if ($words[7] eq "not.available") { $Nmiss ++; }
+	if ($words[7] eq "init")          { $Ninit ++; }
+	if ($words[7] eq "done")          { $Ndone ++; }
+	if ($words[7] eq "clean")         { $Ndone ++; }
+	if ($words[7] =~ /^fail./)        { $Nfail ++; }
+	if ($words[7] =~ /^running./)     { $Nrun ++; }
+    }
+
+    $state = "running";
+    if ($Ninit > 0) { $state = "init"; }
+    if ($Nfail > 0) { $state = "fail"; }
+    if ($Nrun  > 0) { $state = "running"; }
+    if ($Ndone + $Nmiss == @list) { $state = "done"; }
+
+    # check for mkdetrend transition -> "done"
+    if (($entry eq "mkdetrend") && ($state{$entry} ne "done") && ($state eq "done")) {
+	&notify ("start mkfringe");
+    }
+
+    $state{$entry} = $state;
+}
+
+#######################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    my($status) = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    unlink $tmpenv;
+    if ($_[0]) {
+	print STDERR "@_\n";
+    }
+    exit 0;
+}
+
+sub escape { 
+    unlink $tmpenv;
+    print "$_[0]\n";
+    exit 1;
+}
+
+sub save_escape {
+    my ($entry) = $_[0];
+    my ($message) = $_[1];
+    $state{$entry} = "fail";
+    &save_state;
+    &escape ($message);
+}
+
+sub elixir_config {
+    my (@arg) = @_;
+    my ($opt, $config, @tmparg, $status);
+    $config = "";
+    if ($ENV{'PTOLEMY'}) { $config = "-c $ENV{'PTOLEMY'}"; }
+
+    # default values, override on command line
+    $runid  = `gconfig RUNID.POSTRUN`;  chop $runid;
+    $camera = `gconfig CAMERA.POSTRUN`; chop $camera;
+
+    # extract command line options
+    @tmparg = ();
+    while (@arg > 0) {
+	
+	# grab elixir/ptolemy config option -c 
+	if ($arg[0] eq "-c") {
+	    shift @arg;
+	    $config = "-c $arg[0]";
+	    shift @arg;
+	    next;
+	}
+	
+	# grab elixir/ptolemy config option -C 
+	if ($arg[0] eq "-C") {
+	    @opt = (@opt, $arg[0]);
+	    shift @arg;
+	    @opt = (@opt, $arg[0]);
+	    $elconf = $arg[0];
+	    shift @arg;
+	    next;
+	}
+	
+	# grab elixir/ptolemy config option -D
+	if ($arg[0] eq "-D") {
+	    @opt = (@opt, $arg[0]);
+	    shift @arg;
+	    @opt = (@opt, $arg[0]);
+	    shift @arg;
+	    @opt = (@opt, $arg[0]);
+	    shift @arg;
+	    next;
+	}
+	
+	# by default, $run is loaded from state.data file
+	if ($arg[0] eq "-run") {
+	    shift @arg;
+	    $runid = $arg[0];
+	    shift @arg;
+	    next;
+	}
+	
+	# by default, $camera is loaded from state.data file
+	if ($arg[0] eq "-camera") {
+	    shift @arg;
+	    $camera = $arg[0];
+	    shift @arg;
+	    next;
+	}
+	@tmparg = (@tmparg, $arg[0]);
+	shift @arg;
+    }
+
+    # create temporary ptolemy.rc file:
+    $catdir = `gconfig DB_STDS -D RUNID $runid -D CAMERA $camera`; chop $catdir;
+    @opt = (@opt, "-D CATDIR $catdir");
+    @opt = (@opt, "-D RUNID $runid");
+    @opt = (@opt, "-D CAMERA $camera");
+    @opt = (@opt, "-D RUNID.MKDETREND $runid");
+    @opt = (@opt, "-D CAMERA.MKDETREND $camera");
+    @opt = (@opt, "-D RUNID.MKFRINGE $runid");
+    @opt = (@opt, "-D CAMERA.MKFRINGE $camera");
+
+    $config = join (" ", $config, @opt);
+    $tmpenv = `mktemp /tmp/elixir.XXXXXX`; chop ($tmpenv);
+    $status = system ("gconfig -raw $config > $tmpenv");
+    if ($status) { &escape ("error in elixir configuration: gconfig fails"); }
+
+    $ENV{'PTOLEMY'} = "$tmpenv";
+
+    # global variables
+    # add this from the filter configuration system, eventually
+    @filter = split (" ", `filtnames list`);
+
+    @def_state = ("camera",    "off",
+		  "imstats",   "init",
+		  "mkdetrend", "init",
+		  "mkfringe",  "init", 
+		  "ptolemy",   "init", 
+		  "standards", "init", 
+		  "validate",  "init");
+    %def_state = @def_state;
+
+    # generate list of keys
+    @entry = ();
+    foreach $name (@def_state) {
+	push @entry, $name;
+	shift @def_state;
+    }
+
+    # global configuration variables
+    $datdir = `gconfig DATDIR`;  chop $datdir;
+    $rundir = "$datdir/$runid";
+    $dbfile = "$rundir/postrun.db";
+
+    if (! -d $rundir) { mkdir ($rundir, 0777); }
+
+    return (@tmparg);
+}
+
+sub set_globals {
+}    
+
+sub date_to_mjd {
+    my ($date) = $_[0];
+    my ($year, $month, $day, $mjd);
+
+    ($year, $month, $day) = $date =~ /(\d+)\/(\d+)\/(\d+)/;
+    $mjd = get_mjd ($year, $month, $day);
+
+    return ($mjd);
+}
+
+sub get_mjd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5 - 2400000.5;
+    
+
+    return ($jd);
+
+}
+
+sub usage {
+    
+    print STDERR "elixir.postrun (mode)\n";
+    print STDERR "  state  - show current states\n";
+    print STDERR "  update - update system states\n";
+    print STDERR "  set (entry) (value) - manually set a state\n";
+    &goodbye;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.reload
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.reload	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.reload	(revision 22322)
@@ -0,0 +1,57 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 1) { die "USAGE: elixir.reload (elixir)\n"; }
+
+$elixir = $ARGV[0];
+
+# if an elixir is not running, start it, send output to $logdir/$elixir.log
+$logdir = `gconfig LOGDIR`; chop $logdir;
+if ($?) { die "error getting LOGDIR from configuration system\n"; }
+chdir $logdir;
+
+$pidfile = `gconfig -D mode $elixir global.pid`; chop ($pidfile);
+if ($?) { die "error getting PID from configuration system - is elixir $elixir defined?\n"; }
+
+# check that elixir is running
+if (! -f $pidfile) { &launch ($elixir, $logdir); }
+
+# load pid information
+open (FILE, "$pidfile");
+@list = <FILE>;
+close (FILE);
+
+($name, $pid)     = split (" ", $list[0]);
+($name, $user)    = split (" ", $list[1]);
+($name, $machine) = split (" ", $list[2]);
+
+# check that this process exists?
+@list = `rsh $machine /bin/ps -A -o pid,cmd`;
+$dead = 1;
+foreach $line (@list) {
+    ($rpid, $cmd) = split (" ", $line);
+    if (($rpid == $pid) && ($cmd =~ /elixir/)) {
+	$dead = 0;
+	print STDERR "elixir $elixir is running as $pid on $machine\n";
+	last;
+    }
+}
+if ($dead) { 
+    unlink ($pidfile);
+    &launch ($elixir, $logdir); 
+}
+
+# send USR1 signal to process:
+print STDERR "restarting process $pid\n";
+system ("rsh $machine kill -USR1 $pid");
+exit 0;
+
+sub launch {
+
+    my ($elixir) = $_[0];
+    my ($logdir) = $_[1];
+
+    # if elixir is not running, start it
+    print STDERR "starting elixir $elixir\n"; 
+    system ("elixir -D mode $elixir >& $logdir/$elixir.log &");
+    exit 0;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.rerun
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.rerun	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/elixir.rerun	(revision 22322)
@@ -0,0 +1,152 @@
+#!/usr/bin/env perl
+
+# case 1: run on a list of MEF images:
+# case 2: run on a list from ptolemy.failure / ptolemy.success
+
+$complete = 0;
+$stds = 0;
+$demo = 0;
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-C") {
+        $config = "$config -C $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-c") {
+        $config = "$config -c $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-D") {
+        $config = "$config -D $ARGV[1] $ARGV[2]";
+        shift; shift; shift; next;
+    }
+    if ($ARGV[0] eq "-stds") {
+	$stds = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-demo") {
+	$demo = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-complete") {
+	$complete = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-h") {
+	print "USAGE: elixir.rerun (list) (mode) [elixir options]\n";
+	print "  options: \n";
+	print "  -C config (set value of CONFIG variable)\n";
+	print "  -c config.file (specify alternate to config file)\n";
+	print "  -D key value (set value of key)\n";
+	print "  -stds (send output to STDS directory)\n";
+	print "  -complete (use sexcomplete not sextract)\n";
+	print "  -demo (show input list and command)\n";
+	exit 1;
+    }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+if (@ARGV != 2) { die "USAGE: elixir.rerun (list) (mode) [elixir options]\n"; }
+
+if ($stds) {
+    $catdir = `gconfig $config DB_STDS`; chop $catdir;
+    if ($?) { die "config error with CATDIR\n" }
+} else {
+    $catdir = `gconfig $config CATDIR`; chop $catdir;
+    if ($?) { die "config error with CATDIR\n" }
+}
+
+$tmpdir = `gconfig $config TMPDIR`; chop $tmpdir;
+if ($?) { die "config error with TMPDIR\n" }
+
+$input  = $ARGV[0];
+$mode   = $ARGV[1];
+
+# check on mode:
+if ($mode eq "fifo") { &getfifo; }
+if ($mode eq "mef")  { &getmef;  }
+
+die "ERROR: unknown mode $mode\n";
+
+sub getfifo {
+
+    $temp = `mktemp elixir.XXXXXX`; chop $temp;
+    $templog = `mktemp elixir-log.XXXXXX`; chop $templog;
+
+    open (FILE, "$input");
+    @list = <FILE>;
+    close (FILE);
+
+    # write input list in correct format to $temp
+    open (FILE, ">$temp");
+    foreach $line (@list) {
+	($raw, $root, $ccd, $inmode) = split (" ", $line);
+	print FILE "$raw $root $ccd $inmode\n";
+    }
+    &runelixir;
+}
+
+sub getmef {
+
+    $temp = `mktemp elixir.XXXXXX`; chop $temp;
+    $templog = `mktemp elixir-log.XXXXXX`; chop $templog;
+
+    @ccds = split (" ", `cameraconfig -ccds`);
+    if ($?) { die "error in cameraconfig -ccds\n"; }
+
+    @ccdn = split (" ", `cameraconfig -ccdn`);
+    if ($?) { die "error in cameraconfig -ccdn\n"; }
+
+    $Nccd = `cameraconfig -Nccd`; chop $Nccd;
+    if ($?) { die "error in cameraconfig -Nccd\n"; }
+
+    open (FILE, "$input");
+    @list = <FILE>;
+    close (FILE);
+
+    # write input list in correct format to $temp
+    open (FILE, ">$temp");
+    foreach $line (@list) {
+	($file) = split (" ", $line);
+	if ($line =~ m|^\s*$|) { next; } # skip blank lines
+	if ($line =~ m|^\s*\#|) { next; } # skip commented lines
+	
+	# must be absolute path for elixir to work anyway
+	@words = split ("/", $file);
+	$root = $words[-1];
+	$root =~ s/.fits$//;
+	for ($i = 0; $i < $Nccd; $i++) {
+	    printf FILE "$file %s/%s%02d %s MEF\n", $root, $root, $ccdn[$i], $ccds[$i];
+	}
+    }
+    &runelixir; 
+}
+
+sub runelixir {
+    
+    if ($demo) {  
+	if ($complete) {
+	    print "elixir -D mode sexcomplete -D CATDIR $catdir -D TMPDIR $tmpdir $temp >& $templog\n";
+	} else {
+	    print "elixir -D mode sextract -D CATDIR $catdir -D TMPDIR $tmpdir $temp >& $templog\n";
+	}
+	printf "list is in $temp\n";
+	exit 0;
+    }
+
+    # launch elixir for input list
+    if ($complete) {
+	vsystem ("elixir -D mode sexcomplete -D CATDIR $catdir -D TMPDIR $tmpdir $temp >& $templog &");
+    } else {
+	vsystem ("elixir -D mode sextract -D CATDIR $catdir -D TMPDIR $tmpdir $temp >& $templog &");
+    }
+    exit 0;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fixcoords2
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fixcoords2	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fixcoords2	(revision 22322)
@@ -0,0 +1,77 @@
+#!/usr/bin/env perl
+
+# USAGE: fixcoords (input) (output) (ref) (chip)
+use Math::Trig;
+# $DEG_RAD = 57.298688;
+# $RAD_DEG = 0.017452406;
+$DEG_RAD = 57.29577951;
+$RAD_DEG = 0.017453292;
+
+if (@ARGV != 4) { die "USAGE: fixcoords (input) (output) (ref) (chip)\n"; }
+
+$input  = $ARGV[0];
+$output = $ARGV[1];
+$ref    = $ARGV[2];
+$chip   = $ARGV[3];
+
+if (! -f $input) { die "file $input not found\n"; }
+
+# load astrometric terms from header of reference:
+
+$answer = `echo $ref | fields CRPIX1 CRPIX2 CRVAL1 CRVAL2 NASTRO`;
+($file, $crpix1, $crpix2, $crval1, $crval2, $Nastro) = split (" ", $answer);
+if ($Nastro == 0) { die "no astrometry solution in header\n"; }
+
+$answer = `echo $ref | fields PC001001 PC001002 PC002001 PC002002 CDELT1 CDELT2`;
+($file, $pc11, $pc12, $pc21, $pc22, $cd1, $cd2) = split (" ", $answer);
+
+open (FILE, "$input");
+open (OUT, ">$output");
+
+# copy header lines:
+for ($i = 0; $i < 16; $i++)  { $line = <FILE>; print OUT "$line"; }
+
+while ($line = <FILE>) {
+
+    @words = split (" ", $line);
+    xy2rd ($words[12], $words[13]);
+
+    if ($words[17] eq $chip) {
+	printf OUT "%4d %7.4f %7.4f %8.4f  %9.1f %6.3f %5.2f %2d  %9.1f %6.3f %5.2f %2d  %8.3f %8.3f  %15.11f %11.8f  %1s %2s\n",
+	    $words[0], $words[1], $words[2], $words[3], 
+	    $words[4], $words[5], $words[6], $words[7], 
+	    $words[8], $words[9], $words[10], $words[11], 
+	    $words[12], $words[13], $ra, $dec,
+	    $words[16], $words[17];
+    }
+}
+
+close (OUT);
+close (FILE);
+
+sub xy2rd {
+ $x = $_[0];
+ $y = $_[1];
+
+ $X = $cd1*($x - $crpix1);
+ $Y = $cd2*($y - $crpix2);
+
+ $L = $X*$pc11 + $Y*$pc12;
+ $M = $X*$pc21 + $Y*$pc22;
+
+ $chi = $RAD_DEG*$L;
+ $eta = $RAD_DEG*$M;
+
+ $sdp = sin ($RAD_DEG*$crval2);
+ $cdp = cos ($RAD_DEG*$crval2);
+
+ $talp = $chi / ($cdp - $eta*$sdp);
+ $alpha = atan ($talp);
+ $calp = cos ($alpha);
+ $tdel = $calp*($sdp + $eta*$cdp) / ($cdp - $eta*$sdp);
+ $delta = atan ($tdel);
+    
+ $ra  = $DEG_RAD*$alpha + $crval1;
+ $dec = $DEG_RAD*$delta;
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fixscat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fixscat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fixscat	(revision 22322)
@@ -0,0 +1,75 @@
+#!/usr/bin/env perl
+
+# warning: this function currently uses a fixed temporary name 'fix.NN.fits' 
+# for the intermediate product.  If we add this to elixir, convert to mktemp
+
+# check the usage
+if ($ARGV[0] eq "-h") { &usage (); }
+if ($ARGV[0] eq "-help") { &usage (); }
+if (@ARGV != 3) { &usage (); }
+
+# assign the command line arguments
+$inlist   = $ARGV[0];
+$version  = $ARGV[1];
+$ID       = $ARGV[2];
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/scatter.pro";
+
+# run mana script:
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+print MANA "applyscat $inlist $version $ID\n";
+print MANA "exit 1\n";
+close (MANA);
+print STDERR "exit statue: $?\n";
+if ($? == 2 * 256) { 
+    print STDERR "no scattered light term available\n"; 
+    exit 2;
+}
+if ($?) { die "ERROR problem with fixscat\n"; }
+
+print STDOUT "SUCCESS: finished with fixscat\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "USAGE: fixscat (inlist) (version) (ID)\n";
+    exit 1;
+}
+
+# mana within perl:  we embed the mana functions within a macro to trap the 
+# exit status.  If mana has an error with one of the steps, it will exit the macros and 
+# the second exit will be called. Otherwise, the "go" macro will perform the exit 0
+# and mana will exit with status 0
+
+# there are two possible flat-field photometric corrections: additive
+# and multiplicative:  
+# The correction images constructed by examining
+# the *difference* between dome-flats with and without the petals
+# covered is additive:  FLAT_new = (FLAT_old - SCAT) * A
+# (A is set to maintain the original normalization of chip 04)
+# 
+# The correction images constructed by examining stellar magnitude
+# difference is multiplicative: FLAT_new = (FLAT_old * SCAT) * A
+# (A is set to maintain the original normalization of chip 04)
+
+# there is a different scattered light image for each filter.
+# (these images are the same file for the additive version)
+
+# there should be no arbitrary normalization in the scattered frames
+# in the database. (we used to multiply the scattered light image by a
+# value of 10.0).  
+
+# images of the first class will be identified with the label scatter-A.0
+# images of the second class will be identified with the label scatter-B.0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fixscat.mef
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fixscat.mef	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fixscat.mef	(revision 22322)
@@ -0,0 +1,75 @@
+#!/usr/bin/env perl
+
+# warning: this function currently uses a fixed temporary name 'fix.NN.fits' 
+# for the intermediate product.  If we add this to elixir, convert to mktemp
+
+# check the usage
+if ($ARGV[0] eq "-h") { &usage (); }
+if ($ARGV[0] eq "-help") { &usage (); }
+if (@ARGV != 3) { &usage (); }
+
+# assign the command line arguments
+$inlist   = $ARGV[0];
+$version  = $ARGV[1];
+$ID       = $ARGV[2];
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/scatter.pro";
+
+# run mana script:
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+print MANA "applyscat-mef $inlist $version $ID\n";
+print MANA "exit 1\n";
+close (MANA);
+print STDERR "exit statue: $?\n";
+if ($? == 2 * 256) { 
+    print STDERR "no scattered light term available\n"; 
+    exit 2;
+}
+if ($?) { die "ERROR problem with fixscat\n"; }
+
+print STDOUT "SUCCESS: finished with fixscat\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "USAGE: fixscat (inlist) (version) (ID)\n";
+    exit 1;
+}
+
+# mana within perl:  we embed the mana functions within a macro to trap the 
+# exit status.  If mana has an error with one of the steps, it will exit the macros and 
+# the second exit will be called. Otherwise, the "go" macro will perform the exit 0
+# and mana will exit with status 0
+
+# there are two possible flat-field photometric corrections: additive
+# and multiplicative:  
+# The correction images constructed by examining
+# the *difference* between dome-flats with and without the petals
+# covered is additive:  FLAT_new = (FLAT_old - SCAT) * A
+# (A is set to maintain the original normalization of chip 04)
+# 
+# The correction images constructed by examining stellar magnitude
+# difference is multiplicative: FLAT_new = (FLAT_old * SCAT) * A
+# (A is set to maintain the original normalization of chip 04)
+
+# there is a different scattered light image for each filter.
+# (these images are the same file for the additive version)
+
+# there should be no arbitrary normalization in the scattered frames
+# in the database. (we used to multiply the scattered light image by a
+# value of 10.0).  
+
+# images of the first class will be identified with the label scatter-A.0
+# images of the second class will be identified with the label scatter-B.0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/flatten.flips
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/flatten.flips	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/flatten.flips	(revision 22322)
@@ -0,0 +1,276 @@
+#!/usr/bin/env perl
+
+### darks are currently disabled (look for dark detsearch and darkfactor)
+
+# grab the command line options
+$defringe = 0;
+$use_mask = 1;
+$fast = 0;
+$quiet = 0;
+$close = "";
+$config = "";
+$preserve = 0;
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-C") {
+        $config = "$config -C $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-c") {
+        $config = "$config -c $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-D") {
+        $config = "$config -D $ARGV[1] $ARGV[2]";
+        shift; shift; shift; next;
+    }
+    if ($ARGV[0] eq "-preserve") {
+        $preserve = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-fast") {
+        $fast = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-quiet") {
+	$quiet = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-close") {
+	$close = "-close";
+        shift; next;
+    }
+    if ($ARGV[0] eq "-defringe") {
+	$defringe = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-nomask") {
+	$use_mask = 0;
+        shift; next;
+    }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 4) { &usage; }
+
+# command line arguments
+$input  = $ARGV[0];
+$output = $ARGV[1];
+$ccd    = $ARGV[2];
+$mode   = uppercase ($ARGV[3]);
+
+$chipname = "-chipname";
+$infile = $input;
+if ($mode eq "MEF") { $infile = "$input\[$ccd]"; }
+if ($ccd eq "phu") {
+    $infile = "$input"; 
+    $ccd = "ccd00";
+    $chipname = "";
+}
+
+# remove old file if it exists
+if (-e $output) { unlink "$output"; }
+ckpathname ($output);
+
+# find the appropriate detrend images (need to fix output name for MEF - option?)
+$flat=`detsearch $config $chipname -quiet -image $input $ccd $mode -type flat $close`; chop ($flat);
+if ($?) { die "ERROR: can't get flat for image\n"; }
+
+if (!$fast) {
+    $bias=`detsearch $config $chipname -quiet -image $input $ccd $mode -type bias $close`; chop ($bias);
+    if ($?) { die "ERROR: can't get bias for image\n"; }
+
+#    $dark=`detsearch $config $chipname -quiet -image $input $ccd $mode -type dark $close`; chop ($dark);
+#    if ($?) { die "ERROR: can't get dark for image\n"; }
+}
+
+# find the appropriate mask image
+$mask = "none";
+if ($use_mask) {
+    $mask=`detsearch $config $chipname -quiet -image $input $ccd $mode -type mask $close`; chop ($mask);
+    if ($?) { die "ERROR: can't get mask for image\n"; }
+}
+
+# abstracted keywords
+$ccdkeyword  = `gconfig $config CCDNUM-KEYWORD`;    chop ($ccdkeyword);
+$darkword    = `gconfig $config DARKTIME-FUNCTION`; chop ($darkword);
+$exptimeword = `gconfig $config EXPTIME-KEYWORD`;   chop ($exptimeword);
+
+# find the appropriate fringe frame & coords file
+$fringe = "none";
+$frpts  = "none";
+if ($defringe) {
+
+    $filterword  = `gconfig $config FILTER-KEYWORD`;    chop ($filterword);
+
+    # check the FILTER and EXPTIME, include fringe correction?:
+    ($junk, $exptime, $filter) = `echo $input | fields $exptimeword $filterword`;
+    $filter = uppercase ($filter);
+
+    # recipe file defines detrend types & cutoff exptime values
+    $recipefile = `gconfig $config DETREND_RECIPES`; chop $recipefile;
+    ($detypes, $cutoffs) = split (" ", `gconfig -c $recipefile $filter`);
+    if ($?) { die "ERROR: failure to get detrend recipes\n"; }
+    @detypes = split (",", $detypes);
+    @cutoffs = split (",", $cutoffs);
+    
+    # check for DEFRINGE, DEMODE 
+    $defringe = 0;
+    for ($i = 0; $i < @detypes; $i++) {
+	if ($detypes[$i] eq "fringe") {
+	    if ($exptime >= $cutoffs[$i]) { $defringe = 1; }
+	    last;
+	}
+    }
+}
+
+if (! $fast && 0) {
+    # determine scale factor for dark image (use MEF rootname if EXTNAME is given)
+    $darkfile = $dark;
+    $darkfile =~ s|\[.*\]||;
+    ($junk, $t1) = split (" ", `echo $input    | fields $exptimeword`);
+    ($name, $t2) = split (" ", `echo $darkfile | fields $exptimeword`);
+    $darkfactor = `darktime.$darkword $t1 $t2`; chop ($darkfactor);
+    if ($?) { die "ERROR: failure to get darkfactor $t1 $t2\n"; }
+}
+
+# load FLIPS parameter file, fill in missing words:
+$paramfile = `gconfig $config FLIPS_PARAM_IMRED`; chop $paramfile;
+if ($?) { die "ERROR: missing FLIPS_PARAM_IMRED in config\n"; }
+open (FILE, "$paramfile");
+@params = <FILE>;
+close (FILE);
+
+foreach $line (@params) {
+    # input / output file names
+    $line =~ s|INFILE|$infile|;
+    $line =~ s|OUTFILE|$output|;
+    $line =~ s|COMMENT|Elixir / Flips2 flatten.flips|;
+
+    # overscan correction
+    $line =~ s|DOOVER|1|;
+
+    if ($fast) {
+	$line =~ s|DOBIAS|0|;
+	$line =~ s|DODARK|0|;
+    } else {
+	# bias corrections
+	$line =~ s|DOBIAS|1|;
+	$line =~ s|BIASNAME|$bias|;
+
+	# dark corrections - avoid for now
+	$line =~ s|DODARK|0|;
+#	$line =~ s|DODARK|1|;
+#	$line =~ s|DARK_FACT|$darkfactor|;
+#	$line =~ s|DARKNAME|$dark|;
+    }
+
+    # flat corrections
+    $line =~ s|DOFLAT|1|;
+    $line =~ s|FLATNAME|$flat|;
+
+    # mask corrections
+    if ($mask eq "none") {
+	$line =~ s|DOMASK|0|;
+    } else {
+	$line =~ s|DOMASK|1|;
+	$line =~ s|MASKNAME|$mask|;
+    }
+
+    # header blocks - we need an extra block for elixir keywords
+    $line =~ s|DOHEADER|1|;
+    $line =~ s|NBLOCKS|2|;
+}
+
+$temp = `mktemp /tmp/flatten.XXXXXX`; chop $temp;
+open (FILE, ">$temp");
+foreach $line (@params) {
+    print FILE "$line";
+}
+close (FILE);
+
+system ("imred $temp");
+$stat = $?;
+
+if ($preserve) { 
+    print STDERR "imred input file: $temp\n"; 
+} else {
+    unlink $temp; 
+}
+
+if ($stat) {
+    if (! $quiet) { print STDOUT "ERROR: failure running flatten.flips\n"; }
+    exit 1;
+}
+
+if ($defringe) {
+    system ("mv $output $output.det");
+    if ($?) { 
+	print STDERR "failure to save temporary output file\n";
+	exit 1;
+    }
+
+    system ("defringe.ccd -quiet $close $output.dat $output $ccd SPLIT");
+    if ($?) { 
+	print STDERR "failure in defringe\n";
+	$stat = 1;
+    }
+    unlink "$output.dat";
+}    
+
+if (! $quiet) {
+    if ($stat) { 
+	print STDOUT "ERROR: failure running flatten.flips\n"; 
+    } else {
+	print STDOUT "SUCCESS\n"; 
+    }
+}
+
+# always return 1, don't return just the exit status
+if ($stat) {
+    exit 1;
+} else {
+    exit 0;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print STDERR "USAGE: flatten.flips (input) (output) CCD MODE [-fast] [-close] [-quiet]\n";
+    exit 2;
+}
+
+sub uppercase {
+    return "\U$_[0]\E";
+}
+
+# given a filename, check that its path exists, create it if not
+sub ckpathname {
+    my ($file) = $_[0];
+    my (@words, $dir);
+
+    @words = split ("/", $file);
+    pop (@words);
+    $dir = join ("/", @words);
+
+    if ($dir eq "") { $dir = "."; }
+
+    if (! -d $dir) {
+	system ("mkdir -p $dir");
+	if ($?) {
+	    print STDERR "ERROR: can't make directory component for $file\n";
+	    exit 1;
+	}
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/flatten.mana
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/flatten.mana	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/flatten.mana	(revision 22322)
@@ -0,0 +1,269 @@
+#!/usr/bin/env perl
+
+# grab the command line options
+$use_fringe = 0;
+$use_mask = 1;
+$fast = 0;
+$quiet = 0;
+$close = "";
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-fast") {
+        $fast = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-quiet") {
+	$quiet = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-close") {
+	$close = "-close";
+        shift; next;
+    }
+    if ($ARGV[0] eq "-defringe") {
+	$use_fringe = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-nomask") {
+	$use_mask = 0;
+        shift; next;
+    }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 4) { &usage; }
+
+# command line arguments
+$input  = $ARGV[0];
+$output = $ARGV[1];
+$ccd    = $ARGV[2];
+$mode   = uppercase ($ARGV[3]);
+
+ckpathname ($output);
+
+if ($fast) { &fast; } else { &complete; }
+exit 1;
+
+sub complete {
+    # find the appropriate flat-field image
+    $flat=`detsearch -quiet -image $input $ccd $mode -type flat $close`; chop ($flat);
+    if ($?) {
+	print STDERR "ERROR: can't get flat for image\n";
+	exit 1;
+    }
+    # find the appropriate bias image
+    $bias=`detsearch -quiet -image $input $ccd $mode -type bias $close`; chop ($bias);
+    if ($?) {
+	print STDERR "ERROR: can't get bias for image\n";
+	exit 1;
+    }
+    # find the appropriate dark image
+    $dark=`detsearch -quiet -image $input $ccd $mode -type dark $close`; chop ($dark);
+    if ($?) {
+	print STDERR "ERROR: can't get dark for image\n";
+	exit 1;
+    }
+
+    # find the appropriate mask image
+    $mask = "none";
+    if ($use_mask) {
+	$mask=`detsearch -quiet -image $input $ccd $mode -type mask $close`; chop ($mask);
+	if ($?) {
+	    print STDERR "ERROR: can't get mask for image\n";
+	    exit 1;
+	}
+    }
+
+    # abstracted keywords
+    $ccdkeyword  = `gconfig -q CCDNUM-KEYWORD`; chop ($ccdkeyword);
+    $darkword    = `gconfig -q DARKTIME-FUNCTION`; chop ($darkword);
+
+    # find the appropriate fringe frame & coords file
+    $fringe = "none";
+    $coordfile = "none";
+    if ($use_fringe) {
+	$exptimeword = `gconfig -q EXPTIME-KEYWORD`; chop ($exptimeword);
+	$filterword  = `gconfig -q FILTER-KEYWORD`; chop ($filterword);
+	$cameraword  = `gconfig -q CAMERA-KEYWORD`; chop ($cameraword);
+	$coordpath   = `gconfig -q FRINGE_COORD_PATH`; chop ($coordpath);
+
+	$ccdnum = `cameraconfig -N $ccd`;
+
+	# check the FILTER and EXPTIME, include fringe correction?:
+	$answer = `echo $input | fields $exptimeword $filterword $cameraword`;
+	($junk, $exptime, $filter, $camera) = split (" ", $answer);
+	# i need to do a look-up on the valid filter filternames (filtname)
+	$filter = uppercase ($filter);
+
+	$use_fringe = 0; 
+	# abstract this a bit: recipe gives guidance
+        # set defringe based on filter & exptime
+	if (($filter eq "I")     && ($exptime > 60)) { $use_fringe = 1; }
+	if (($filter eq "Z")     && ($exptime > 60)) { $use_fringe = 1; }
+	if (($filter eq "NB920") && ($exptime > 60)) { $use_fringe = 1; }
+
+	if ($use_fringe) {
+	    $fringe = `detsearch -quiet -image $input $ccd $mode -type fringe $close`; chop ($fringe);
+	    if ($?) {
+		print STDERR "ERROR: can't get fringe for image\n";
+		exit 1;
+	    }
+	    $coordfile = sprintf "%s/%s-%s-CCD%02d-FringeCoord.reg", $coordpath, $camera, $filter, $ccd;
+	}
+    }
+
+    # determine scale factor for dark image
+    $answer = `echo $input | fields $exptimeword`;
+    ($name, $t1) = split (" ", $answer);
+    $answer = `echo $dark | fields $exptimeword`;
+    ($name, $t2) = split (" ", $answer);
+    $darkfactor = `darktime.$darkword $t1 $t2`; chop ($darkfactor);
+
+    # find the appropriate script file
+    $confdir = `gconfig -q CONFDIR`; chop ($confdir);
+    $script  = "$confdir/mana/flatten.pro";
+    
+    # run mana, call script
+    open (MANA, "|mana --norc");
+    print MANA "input $script\n";
+    print MANA "\$CCDKEYWORD = $ccdkeyword\n";
+    print MANA "\$bias       = $bias\n";
+    print MANA "\$dark       = $dark\n";
+    print MANA "\$flat       = $flat\n";
+    print MANA "\$mask       = $mask\n";
+    print MANA "\$fringe     = $fringe\n";
+    print MANA "\$use:mask   = $use_mask\n";
+    print MANA "\$use:fringe = $use_fringe\n";
+    print MANA "\$darkfactor = $darkfactor\n";
+    print MANA "\$coordfile  = $coordfile\n";
+    print MANA "flatten.complete $input $output $ccd $mode\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) {
+	print STDERR "ERROR problem with imstats\n";
+	exit 1;
+    }
+    print STDOUT "SUCCESS: finished with imstats\n";
+    exit 0;
+}
+    
+sub fast {
+    # find the appropriate flat-field image
+    $flat=`detsearch -quiet -image $input $ccd $mode -type flat -close`; chop ($flat);
+    if ($?) {
+	print STDERR "ERROR: can't get flat for image\n";
+	exit 1;
+    }
+
+    # find the appropriate mask image
+    $mask = "none";
+    if ($use_mask) {
+	$mask=`detsearch -quiet -image $input $ccd $mode -type mask -close`; chop ($mask);
+	if ($?) {
+	    print STDERR "ERROR: can't get mask for image\n";
+	    exit 1;
+	}
+    }
+
+    # abstracted keywords
+    $ccdkeyword  = `gconfig -q CCDNUM-KEYWORD`; chop ($ccdkeyword);
+
+    # find the appropriate fringe frame & coords file
+    $fringe = "none";
+    $coordfile = "none";
+    if ($use_fringe) {
+	$exptimeword = `gconfig -q EXPTIME-KEYWORD`; chop ($exptimeword);
+	$filterword  = `gconfig -q FILTER-KEYWORD`; chop ($filterword);
+	$cameraword  = `gconfig -q CAMERA-KEYWORD`; chop ($cameraword);
+	$coordpath   = `gconfig -q FRINGE_COORD_PATH`; chop ($coordpath);
+
+	$ccdnum = `cameraconfig -N $ccd`;
+
+	# check the FILTER and EXPTIME, include fringe correction?:
+	$answer = `echo $input | fields $exptimeword $filterword $cameraword`;
+	($junk, $exptime, $filter, $camera) = split (" ", $answer);
+	# i need to do a look-up on the valid filter filternames (filtname)
+	$filter = uppercase ($filter);
+
+	$use_fringe = 0; 
+        # set defringe based on filter & exptime
+	if (($filter eq "I")     && ($exptime > 60)) { $use_fringe = 1; }
+	if (($filter eq "Z")     && ($exptime > 60)) { $use_fringe = 1; }
+	if (($filter eq "NB920") && ($exptime > 60)) { $use_fringe = 1; }
+
+	if ($use_fringe) {
+	    $fringe = `detsearch -quiet -image $input $ccd $mode -type fringe -close`; chop ($fringe);
+	    if ($?) {
+		print STDERR "ERROR: can't get fringe for image\n";
+		exit 1;
+	    }
+	    $coordfile = sprintf "%s/%s-%s-CCD%02d-FringeCoord.reg", $coordpath, $camera, $filter, $ccd;
+	}
+    }
+
+    # find the appropriate script file
+    $confdir = `gconfig -q CONFDIR`; chop ($confdir);
+    $script  = "$confdir/mana/flatten.pro";
+    
+    # run mana, call script
+    open (MANA, "|mana --norc");
+    print MANA "input $script\n";
+    print MANA "\$CCDKEYWORD = $ccdkeyword\n";
+    print MANA "\$flat       = $flat\n";
+    print MANA "\$mask       = $mask\n";
+    print MANA "\$fringe     = $fringe\n";
+    print MANA "\$use:mask   = $use_mask\n";
+    print MANA "\$use:fringe = $use_fringe\n";
+    print MANA "\$coordfile  = $coordfile\n";
+    print MANA "flatten.fast $input $output $ccd $mode\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) {
+	print STDERR "ERROR problem with imstats\n";
+	exit 1;
+    }
+    print STDOUT "SUCCESS: finished with imstats\n";
+    exit 0;
+}
+    
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print STDERR "USAGE: flatten.mana (input) (output) CCD MODE [-fast] [-close] [-quiet]\n";
+    exit 2;
+}
+
+sub uppercase {
+    return "\U$_[0]\E";
+}
+
+# given a filename, check that its path exists, create it if not
+sub ckpathname {
+    my ($file) = $_[0];
+    my (@words, $dir);
+
+    @words = split ("/", $file);
+    pop (@words);
+    $dir = join ("/", @words);
+
+    if ($dir eq "") { $dir = "."; }
+
+    if (! -d $dir) {
+	system ("mkdir -p $dir");
+	if ($?) {
+	    print STDERR "ERROR: can't make directory component for $file\n";
+	    exit 1;
+	}
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.ckfile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.ckfile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.ckfile	(revision 22322)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { &usage (); }
+if ($ARGV[0] eq "-help") { &usage (); }
+if (@ARGV != 1) { &usage (); }
+
+# assign the command line arguments
+$list   = $ARGV[0];
+
+# check for files to be processed
+open (FILE, "$list");
+@list = <FILE>;
+close (FILE);
+
+foreach $file (@list) {
+    chop ($file);
+    if (-e $file) { next; }
+    print STDERR "ERROR: missing file $file\n";
+    exit 1;
+}
+
+print STDOUT "SUCCESS: found all files from $list\n";
+exit 0;
+
+sub usage {
+    print "USAGE: fr.ckfile (list)\n";
+    print " checks for the existence of each file in the list\n";
+    exit 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.defringe
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.defringe	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.defringe	(revision 22322)
@@ -0,0 +1,133 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 5) { &escape ("ERROR: USAGE: defringe (imlist) (imstats) (frlist) (frstats) (outlist)"); }
+
+$imlist  = $ARGV[0];
+$imstats = $ARGV[1];
+$frlist  = $ARGV[2];
+$frstats = $ARGV[3];
+$output  = $ARGV[4];
+
+# load config info
+$confdir = `gconfig CONFDIR`; chop $confdir;
+if ($?) { &escape ("ERROR: config failure"); }
+
+# various lists
+@input  = split (" ", `cat $imlist`);
+@output = split (" ", `cat $output`);
+@ccds   = split (" ", `cameraconfig -ccds`);
+@ccdn   = split (" ", `cameraconfig -ccdn`);
+$Nccd   = @ccds;
+
+# output directories must exist
+for ($i = 0; $i < $Nccd; $i++) { &ckdir ($output[$i]); }
+
+# convert image stats to optimal image parameters
+$fitpars = &mktemp ("defringe");
+vsystem ("fr.frstats -applyfit $imstats $frstats $fitpars");
+@fitpars = `cat $fitpars`; foreach $line (@fitpars) { chop $line; }
+@fringe  = `cat $frlist`; foreach $line (@fringe) { chop $line; }
+
+# load flips parameter file
+$paramfile = `gconfig FLIPS_PARAM_DEFRINGE`; chop $paramfile;
+if ($?) { die "ERROR: missing FLIPS_PARAM_DEFRINGE in config\n"; }
+
+open (FILE, "$paramfile");
+@rawpars = <FILE>;
+close (FILE);
+
+$infile  = &mktemp ("\@defringe");
+$parfile = &mktemp ("defringe");
+for ($i = 0; $i < $Nccd; $i++) {
+
+    # create input list file
+    ($s0, $s1, $RF, $ds) = split (" ", $fitpars[$i]);
+    open (FILE, ">$infile");
+    print FILE "$input[$i]  $s0 1.0 $ds\n";
+    print FILE "$fringe[$i] $s1 $RF $ds\n";
+    close (FILE);
+
+    print STDOUT "$fitpars[$i]\n";
+    print STDOUT "$input[$i]  $s0 1.0 $ds\n";
+    print STDOUT "$fringe[$i] $s1 $RF $ds\n";
+
+    # create input par file
+    @params = @rawpars;
+    foreach $line (@params) {
+	$line =~ s|LISTNAME|$infile|;
+	$line =~ s|FILENAME|$output[$i]|;
+	$line =~ s|COMMENT|Elixir Defringing|;
+    }
+    open (FILE, ">$parfile");
+    foreach $line (@params) { print FILE "$line"; }
+    close (FILE);
+
+    if (-e $output[$i]) { unlink $output[$i]; }
+    vsystem ("imcombred $parfile");
+    if ($?) { &escape ("ERROR: failure running imcombred"); }
+}
+
+&goodbye;
+
+########################################################
+
+sub ckdir {
+
+    my ($file) = $_[0];
+    my ($dir, @words);
+
+    @words = split ("/", $file);
+    pop (@words);
+    $dir = join ("/", @words);
+    if ($dir eq "") { $dir = "."; }
+    if (! -e $dir) {
+	system ("mkdir -p $dir");
+	if ($?) { die "ERROR: can't create output directory $dir\n"; }
+    }
+}
+
+sub loadlist {
+
+    my($command) = $_[0];
+    my(@list, $line);
+
+    @list = `$command`;
+
+    foreach $line (@list) { chop ($line); }
+
+    return @list;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+@temp = ();
+sub mktemp {
+
+    my ($base) = $_[0];
+    my ($name);
+    $name = `mktemp /tmp/$base.XXXXXX`;
+    chop $name;
+
+    push @temp, $name;
+    return $name;
+}
+
+sub escape {
+    $message = $_[0];
+    foreach $name (@temp) {
+	unlink ($name);
+    }
+    die "$message\n";
+}
+
+sub goodbye {
+    foreach $name (@temp) { unlink ($name); }
+    print STDERR "SUCCESS\n";
+    exit 0;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.detrend
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.detrend	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.detrend	(revision 22322)
@@ -0,0 +1,79 @@
+#!/usr/bin/env perl
+# calls flatten.flips, but no direct flips functions
+
+# check the usage
+if ($ARGV[0] eq "-h") { usage (); }
+if ($ARGV[0] eq "-help") { usage (); }
+if (@ARGV != 4) { die "USAGE: fr.detrend (path) (root) (mode) (outlist)\n" }
+
+# assign the command line arguments
+$path = $ARGV[0];
+$root = $ARGV[1];
+$mode = "\U$ARGV[2]\E";
+if (($mode ne "MEF") && ($mode ne "SPLIT")) { die "ERROR: mode must be either -split or -mef\n";}
+
+# load output list (must be created in ccd order)
+$outlist = $ARGV[3];
+open (FILE, "$outlist");
+@outlist = <FILE>;
+close (FILE);
+foreach $line (@outlist) { chop ($line); }
+       
+# double checks: should be Nccd 
+$Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+if ($Nccd != @outlist) { die "ERROR: mis-matched number of images\n"; }
+@ccds    = split (" ", `cameraconfig -ccds`);
+if ($?) { die "ERROR: cameraconfig error\n"; }
+@ccdn    = split (" ", `cameraconfig -ccdn`);
+if ($?) { die "ERROR: cameraconfig error\n"; }
+
+# run flatten.flips for each input and output image:
+for ($ccd = 0; $ccd < $Nccd; $ccd++) {
+    
+    $infile  = mk_filename ($path, $root, $mode, $ccdn[$ccd]);
+    $outfile = $outlist[$ccd];
+
+    $status = vsystem ("flatten.flips -quiet $infile $outfile $ccds[$ccd] $mode");
+    if ($status) { die "ERROR: problem running flatten on $infile\n"; }
+}
+print STDOUT "SUCCESS: finished with fr.detrend\n";
+exit 0;
+
+## utilities
+
+sub mk_filename {
+    my ($path) = $_[0];
+    my ($root) = $_[1];
+    my ($mode) = $_[2];
+    my ($ccd)  = $_[3];
+
+    if ($mode eq "MEF") {
+	$name = sprintf "%s/%s.fits", $path, $root;
+	return $name;
+    }
+    if ($mode eq "SPLIT") {
+	$name = sprintf "%s/%s/%s%s.fits", $path, $root, $root, $ccd;
+	return $name;
+    }
+    return "";
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "\n";
+    print "USAGE: fr.detrend (path) (root) (mode) (outlist)\n";
+    print " path:    path to MEF image or SPLIT dir\n";
+    print " root:    root name of image (MEF = root.fits, SPLIT = root/rootNN.fits)\n";
+    print " mode:    MEF or SPLIT\n";
+    print " outlist: list of output files\n";
+    exit 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.frstats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.frstats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.frstats	(revision 22322)
@@ -0,0 +1,171 @@
+#!/usr/bin/env perl
+
+if (@ARGV < 1) { &escape ("USAGE: fr.frstats -mode [args]"); }
+if ($ARGV[0] eq "help") { &usage; }
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/fringe.pro";
+
+# test named Xserver, or start vnc on specified machine
+$xhost=`gconfig -q XHOST`; chop ($xhost);
+$xdisp=`gconfig -q XDISP`; chop ($xdisp);
+if (vsystem ("xdpyinfo -display $xdisp > /dev/null")) {
+    print STDERR "X server is not running. start it now on $xhost\n";
+    exit 1;
+}
+$ENV{'DISPLAY'} = $xdisp;
+
+# determine fit parameters for list of raw fringe measurements
+if ($ARGV[0] eq "-fitpars") {
+    
+    $list = $ARGV[1];
+    $pars = $ARGV[2];
+
+    # run mana script:
+    open (MANA, "|mana --norc");
+    print MANA "input $script\n";
+    print MANA "getfitpars $list $pars\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) { &escape ("problem with fr.frstats -fitpars"); }
+
+    &goodbye;
+}
+
+# apply fit parameters for given chip
+if ($ARGV[0] eq "-applyfit") {
+    
+    if (@ARGV != 4) { die "USAGE: fr.frstats -applyfit (imlist) (fitpars) (output)\n"; }
+    $list = $ARGV[1];
+    $pars = $ARGV[2];
+    $output = $ARGV[3];
+
+    if (-e $output) { unlink $output; }
+
+    # run mana script:
+    open (MANA, "|mana --norc");
+    print MANA "input $script\n";
+    print MANA "applyfitstats $list $pars $output\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) { &escape ("problem with fr.frstats -applyfit"); }
+
+    &goodbye;
+}
+
+# determine fit parameters for list of raw fringe measurements
+if ($ARGV[0] eq "-plotstats") {
+    
+    $list = $ARGV[1];
+    $pars = $ARGV[2];
+    $output = $ARGV[3];
+
+    # run mana script:
+    open (MANA, "|mana --norc");
+    print MANA "input $script\n";
+    print MANA "showfits $list $pars $output\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) { &escape ("problem with fr.frstats -plotstats"); }
+
+    &goodbye;
+}
+
+# create frstats file for list of raw fringe measurements using measure fringe parameters
+if ($ARGV[0] eq "-frstats") {
+    
+    $list    = $ARGV[1];
+    $pars    = $ARGV[2];
+    $frstats = $ARGV[3];
+
+    # check if none of list exist, then skip
+    # otherwise, report error
+
+    open (FILE, "$list");
+    @list = <FILE>;
+    close (FILE);
+
+    $Nfound = 0;
+    foreach $line (@list) {
+	chop $line;
+	if (-e $line) { $Nfound ++; }
+    }
+    if ($Nfound < @list) { 
+	print STDERR "NOTE: missing some file from $list: assuming initial state\n";
+	&goodbye; 
+    }
+
+    # run mana script:
+    open (MANA, "|mana --norc");
+    print MANA "input $script\n";
+    print MANA "getfrstats $pars $list $frstats\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) { &escape ("problem with fr.frstats -frstats"); }
+
+    &goodbye;
+}
+
+# create dfstats file for list of fringe residual measurements
+if ($ARGV[0] eq "-dfstats") {
+    
+    $list    = $ARGV[1];
+    $pars    = $ARGV[2];
+    $frstats = $ARGV[3];
+
+    # check if none of list exist, then skip
+    # otherwise, report error
+
+    open (FILE, "$list");
+    @list = <FILE>;
+    close (FILE);
+
+    $Nfound = 0;
+    foreach $line (@list) {
+	chop $line;
+	if (-e $line) { $Nfound ++; }
+    }
+    if ($Nfound < @list) { 
+	print STDERR "NOTE: missing some files from $list: assuming initial state\n";
+	&goodbye; 
+    }
+
+    # run mana script:
+    open (MANA, "|mana --norc");
+    print MANA "input $script\n";
+    print MANA "getdfstats $pars $list $frstats\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) { &escape ("problem with fr.frstats -frstats"); }
+
+    &goodbye;
+}
+
+&escape ("unknown fr.frstats command $ARGV[0]");
+
+## utilities
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+  print STDOUT "finished with fr.stats\n";
+  exit 0;
+}
+
+sub escape {
+  print STDOUT "ERROR: $_[0]\n";
+  exit 1;
+}
+
+sub usage {
+
+    print STDOUT "fr.mklists -fitpars CONFIG.med.sublist  CONFIG.fitpars\n";
+    print STDOUT "fr.frstats -frstats CONFIG.med.statlist CONFIG.fitpars CONFIG.med.imstats\n";
+
+    exit 2;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.getfringe
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.getfringe	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.getfringe	(revision 22322)
@@ -0,0 +1,59 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { usage (); }
+if ($ARGV[0] eq "-help") { usage (); }
+if (@ARGV != 3) { die "USAGE: fr.getfringe (inlist) (binning) (output)\n" }
+
+# assign the command line arguments
+$inlist = $ARGV[0];
+$bin    = $ARGV[1];
+$output = $ARGV[2];
+
+# load input list:
+open (FILE, "$inlist");
+@inlist = <FILE>;
+close (FILE);
+foreach $line (@inlist) { chop $line; }
+
+# getfringe appends to the output file:
+unlink ($output);
+
+# basic camera data
+$Nccd      = `cameraconfig -Nccd`;  chop $Nccd;
+if ($Nccd != @inlist) { die "ERROR: mis-matched image list sizes\n"; }
+@ccds = split (" ", `cameraconfig -ccds`); 
+if ($?) { die "ERROR: cameraconfig error\n"; }
+
+# find fringe coordinate file
+$coordfile = `detsearch -quiet -image $inlist[0] $ccds[0] split -type frpts`; chop $coordfile;
+if ($?) { die "ERROR: can't find fringe point file for this image\n"; }
+
+# each call to getfringe adds a line to $output 
+for ($ccd = 0; $ccd < $Nccd; $ccd++) {
+    vsystem ("getfringe $inlist[$ccd] $ccds[$ccd] $coordfile $bin >> $output");
+    if ($?) { die "ERROR running getfringe\n"; }
+}
+
+print STDOUT "SUCCESS: finished with fr.getfringe\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "\n";
+    print "USAGE: fr.getfringe (inlist) (binning) (output)\n";
+    print " calculate fringe pattern strengths for images in (inlist)\n";
+    print " inlist  : list of input CCDs in CCD order\n";
+    print " binning : binning factor of images\n";
+    print " outdir  : output file\n";
+    exit 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.imdata
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.imdata	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.imdata	(revision 22322)
@@ -0,0 +1,55 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { &usage (); }
+if ($ARGV[0] eq "-help") { &usage (); }
+if (@ARGV != 2) { &usage (); }
+
+# assign the command line arguments
+$list   = $ARGV[0];
+$imdata = $ARGV[1];
+
+# load input list:
+open (FILE, "$list");
+@list = <FILE>;
+close (FILE);
+foreach $line (@list) {chop $line;}
+
+# check for file
+if (! -e $list[0]) { die "ERROR: image file $list[0] is missing\n"; }
+
+print STDOUT "$list[0]\n";
+
+# abstract header keywords
+($datekwd, $timekwd, $obsidkwd, $exptimekwd) = 
+    split (" ", `gconfig DATE-KEYWORD UT-KEYWORD ID-KEYWORD EXPTIME-KEYWORD`);
+if ($?) { die "ERROR problem with keywords\n"; }
+
+($file, $date, $utc, $obsid, $exptime) = 
+    split (" ", `echo $list[0] | fields $datekwd $timekwd $obsidkwd $exptimekwd`);
+if ($?) { die "ERROR missing header information\n"; }
+ 
+open (FILE, ">$imdata");
+print FILE "$obsid $date $utc $exptime\n";
+close (FILE);
+
+print STDOUT "SUCCESS: finished with fr.stats\n";
+exit 0;
+
+## utilities
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "USAGE: fr.imdata (list) (imdata)\n";
+    print "  extract basic image data from header for later reference\n";
+    exit 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.jpegs
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.jpegs	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.jpegs	(revision 22322)
@@ -0,0 +1,65 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { &usage (); }
+if ($ARGV[0] eq "-help") { &usage (); }
+if (@ARGV != 4) { &usage (); }
+
+# assign the command line arguments
+$list   = $ARGV[0];
+$tenbin = $ARGV[1];
+$medbin = $ARGV[2];
+$range  = $ARGV[3];
+
+# check for files to be processed
+@status = stat ($list);
+if ($status[7] == 0) {
+    print STDERR "no files in source list, skipping\n";
+    exit 1;
+}
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/fringe.pro";
+
+# test named Xserver, or start vnc on specified machine
+$xhost=`gconfig -q XHOST`; chop ($xhost);
+$xdisp=`gconfig -q XDISP`; chop ($xdisp);
+if (vsystem ("xdpyinfo -display $xdisp")) {
+    print STDERR "X server is not running. start it now on $xhost\n";
+    exit 1;
+}
+$ENV{'DISPLAY'} = $xdisp;
+
+# run mana script:
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+print MANA "\$RANGE = $range\n";
+print MANA "mkjpegs $list $tenbin $medbin $stats\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) { die "ERROR problem with fr.jpegs\n"; }
+
+print STDOUT "SUCCESS: finished with fr.jpegs\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "USAGE: fr.jpegs (list) (tenbin) (medbin) (range)\n";
+    print "  create big and small jpeg images from the input list\n";
+    exit 1;
+}
+
+# mana within perl:  we embed the mana functions within a macro to trap the 
+# exit status.  If mana has an error with one of the steps, it will exit the macros and 
+# the second exit will be called. Otherwise, the "go" macro will perform the exit 0
+# and mana will exit with status 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.medbin.interp
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.medbin.interp	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.medbin.interp	(revision 22322)
@@ -0,0 +1,130 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { usage (); }
+if ($ARGV[0] eq "-help") { usage (); }
+if (@ARGV != 5) { die "USAGE: fr.medbin (path) (root) (factor) (meddir) (mapdir)\n" }
+
+# assign the command line arguments
+$path   = $ARGV[0];
+$root   = $ARGV[1];
+$factor = $ARGV[2];
+$meddir = $ARGV[3];
+$mapdir = $ARGV[4];
+
+$Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+@ccds = split (" ", `cameraconfig -ccdn`);
+if ($?) { die "ERROR: cameraconfig error\n"; }
+
+($Xmap, $Ymap) = split (" ", `gconfig FRINGE_MAP_SCALE`);
+if ($?) { die "ERROR: missing FRINGE_MAP_SCALE in config system\n"; }
+
+if (! -d $meddir) { vsystem ("mkdir -p $meddir"); }
+if (! -d $mapdir) { vsystem ("mkdir -p $mapdir"); }
+
+open (MANA, "|mana --norc");
+
+# convert BIASSEC string to x1, x2, y1, y2 vars
+print MANA "macro parsesec\n";
+print MANA "  \$f = \$1\n";
+print MANA "  getchr \$f : n\n";
+print MANA "  substr \$f 1 {\$n-1} x1\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+  
+print MANA "  getchr \$f , n\n";
+print MANA "  substr \$f 1 {\$n-1} x2\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+  
+print MANA "  getchr \$f : n\n";
+print MANA "  substr \$f 1 {\$n-1} y1\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+  
+print MANA "  getchr \$f ] n\n";
+print MANA "  substr \$f 1 {\$n-1} y2\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+print MANA "end\n";
+
+print MANA "macro mkmap\n";
+# load detrended image
+print MANA " rd a \$1\n";
+print MANA " keyword a NAXIS1 nx\n";
+print MANA " keyword a NAXIS2 ny\n";
+
+# generate binned image 
+print MANA " medianmap a b {\$nx/$factor} {\$ny/$factor} -range 0.25 0.75\n";
+print MANA " keyword a DATASEC line\n";
+print MANA " parsesec \$line\n";
+print MANA " sprint newline \"[%d:%d,%d:%d]\" {int(\$x1/2 + 1)} {int(\$x2/2 + 1)} {int(\$y1/2 + 1)} {int(\$y2/2 + 1)}\n";
+print MANA " keyword b DATASEC -w \$newline\n";
+print MANA " keyword b NAXIS1 nx\n";
+print MANA " keyword b NAXIS2 ny\n";
+
+# sky from first ccd saved for reference
+print MANA " if (\$setsky)\n";
+print MANA "  stats -q b - - - -\n";
+print MANA "  \$sky = \$MEDIAN\n";
+print MANA "  \$setsky = 0\n";
+print MANA " end\n";
+# we use the sky value of the first chip to remove the DC level for map, add it to med
+
+# create Xmap x Ymap sky map, interpolate to medbin scale
+print MANA " medianmap b m $Xmap $Ymap -ignore 0.0 -range 0.25 0.75\n";
+print MANA " set m = m - \$sky\n";
+print MANA " \$fm = \$nx / $Xmap\n";
+print MANA " \$fy = \$ny / $Ymap\n";
+print MANA " if (\$fy > \$fm)\n";
+print MANA "  \$fm = \$fy\n";
+print MANA " end\n";
+print MANA " minterp m M \$fm\n";
+print MANA " extract M sky 0 0 \$nx \$ny 0 0 \$nx \$ny\n";
+
+# multiplying by (abs(b) > 0.001) maintains the mask
+print MANA " set c = (b - sky) * (abs(b) > 0.001)\n";
+print MANA " wd c \$2\n";
+print MANA " wd m \$3 -bitpix -32 -bzero 0.0 -bscale 1.0\n";
+print MANA "end\n";
+
+print MANA "macro go\n";
+print MANA " \$setsky = 1\n";
+for ($ccd = 0; $ccd < $Nccd; $ccd++) {
+    $infile = sprintf "%s/%s%s.fits", $path,   $root, $ccds[$ccd];
+    $outmed = sprintf "%s/%s%s.fits", $meddir, $root, $ccds[$ccd];
+    $outmap = sprintf "%s/%s%s.fits", $mapdir, $root, $ccds[$ccd];
+    print MANA " mkmap $infile $outmed $outmap\n";
+}
+print MANA " exit 0\n";
+print MANA "end\n";
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) {
+    print STDERR "ERROR running mana\n";
+    exit 1;
+}
+
+print STDOUT "SUCCESS: finished with fr.medbin.spline\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "\n";
+    print "USAGE: fr.medbin.spline (path) (root) (factor) (outpath)\n";
+    print " path:   path to MEF image or SPLIT dir\n";
+    print " root:   root name of image (MEF = root.fits, SPLIT = root/rootNN.fits)\n";
+    print " mode:   MEF or SPLIT\n";
+    print " outdir: directory for output results (all split)\n";
+    exit 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.medbin.spline
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.medbin.spline	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.medbin.spline	(revision 22322)
@@ -0,0 +1,125 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { usage (); }
+if ($ARGV[0] eq "-help") { usage (); }
+if (@ARGV != 5) { die "USAGE: fr.medbin (path) (root) (factor) (meddir) (mapdir)\n" }
+
+# assign the command line arguments
+$path   = $ARGV[0];
+$root   = $ARGV[1];
+$factor = $ARGV[2];
+$meddir = $ARGV[3];
+$mapdir = $ARGV[4];
+
+$Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+@ccds = split (" ", `cameraconfig -ccdn`);
+if ($?) { die "ERROR: cameraconfig error\n"; }
+
+($Xmap, $Ymap) = split (" ", `gconfig FRINGE_MAP_SCALE`);
+if ($?) { die "ERROR: missing FRINGE_MAP_SCALE in config system\n"; }
+
+if (! -d $meddir) { vsystem ("mkdir -p $meddir"); }
+if (! -d $mapdir) { vsystem ("mkdir -p $mapdir"); }
+
+open (MANA, "|mana --norc");
+
+# convert BIASSEC string to x1, x2, y1, y2 vars
+print MANA "macro parsesec\n";
+print MANA "  \$f = \$1\n";
+print MANA "  getchr \$f : n\n";
+print MANA "  substr \$f 1 {\$n-1} x1\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+  
+print MANA "  getchr \$f , n\n";
+print MANA "  substr \$f 1 {\$n-1} x2\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+  
+print MANA "  getchr \$f : n\n";
+print MANA "  substr \$f 1 {\$n-1} y1\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+  
+print MANA "  getchr \$f ] n\n";
+print MANA "  substr \$f 1 {\$n-1} y2\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+print MANA "end\n";
+
+print MANA "macro mkmap\n";
+# load detrended image
+print MANA " rd a \$1\n";
+print MANA " keyword a NAXIS1 nx\n";
+print MANA " keyword a NAXIS2 ny\n";
+
+# generate binned image 
+print MANA " medianmap a b {\$nx/$factor} {\$ny/$factor} -range 0.25 0.75\n";
+print MANA " keyword a DATASEC line\n";
+print MANA " parsesec \$line\n";
+print MANA " sprint newline \"[%d:%d,%d:%d]\" {int(\$x1/2 + 1)} {int(\$x2/2 + 1)} {int(\$y1/2 + 1)} {int(\$y2/2 + 1)}\n";
+print MANA " keyword b DATASEC -w \$newline\n";
+print MANA " keyword b NAXIS1 nx\n";
+print MANA " keyword b NAXIS2 ny\n";
+
+# sky from first ccd saved for reference
+print MANA " if (\$setsky)\n";
+print MANA "  stats -q b - - - -\n";
+print MANA "  \$sky = \$MEDIAN\n";
+print MANA "  \$setsky = 0\n";
+print MANA " end\n";
+# we use the sky value of the first chip to remove the DC level for map, add it to med
+
+# create Xmap x Ymap sky map, spline-fit to medbin scale
+print MANA " medianmap b m $Xmap $Ymap -ignore 0.0 -range 0.25 0.75\n";
+print MANA " set m = m - \$sky\n";
+print MANA " spline.construct m M x\n";
+print MANA " spline.apply m M sky x \$nx \$ny\n";
+
+# multiplying by (abs(b) > 0.001) maintains the mask
+print MANA " set c = (b - sky) * (abs(b) > 0.001)\n";
+print MANA " wd c \$2\n";
+print MANA " wd m \$3 -bitpix -32 -bzero 0.0 -bscale 1.0\n";
+print MANA "end\n";
+
+print MANA "macro go\n";
+print MANA " \$setsky = 1\n";
+for ($ccd = 0; $ccd < $Nccd; $ccd++) {
+    $infile = sprintf "%s/%s%s.fits", $path,   $root, $ccds[$ccd];
+    $outmed = sprintf "%s/%s%s.fits", $meddir, $root, $ccds[$ccd];
+    $outmap = sprintf "%s/%s%s.fits", $mapdir, $root, $ccds[$ccd];
+    print MANA " mkmap $infile $outmed $outmap\n";
+}
+print MANA " exit 0\n";
+print MANA "end\n";
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) {
+    print STDERR "ERROR running mana\n";
+    exit 1;
+}
+
+print STDOUT "SUCCESS: finished with fr.medbin.spline\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "\n";
+    print "USAGE: fr.medbin.spline (path) (root) (factor) (outpath)\n";
+    print " path:   path to MEF image or SPLIT dir\n";
+    print " root:   root name of image (MEF = root.fits, SPLIT = root/rootNN.fits)\n";
+    print " mode:   MEF or SPLIT\n";
+    print " outdir: directory for output results (all split)\n";
+    exit 1;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.mkhtml
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.mkhtml	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.mkhtml	(revision 22322)
@@ -0,0 +1,96 @@
+#!/usr/bin/env perl
+
+if (@ARGV < 1) { &escape ("USAGE: fr.mkhtml -mode [args]"); }
+if ($ARGV[0] eq "help") { &usage; }
+
+# copy CONFIG.imstats to html dir
+if ($ARGV[0] eq "-imstats") {
+    
+    $input  = $ARGV[1];
+    $outdir = $ARGV[2];
+
+    if (! -d $outdir) {
+	vsystem ("mkdir -p $outdir");
+	if ($?) { &escape ("can't make directory component for output $outdir"); }
+    }
+
+    vsystem ("cp $input $outdir");
+    if ($?) { &escape ("problem copying $input to $outdir"); }
+
+    &goodbye;
+}
+
+# create frstats file for list of raw fringe measurements using measure fringe parameters
+if ($ARGV[0] eq "-jpglist") {
+    
+    $list    = $ARGV[1];
+    $outdir  = $ARGV[2];
+    $root    = $ARGV[3];
+    $outlist = sprintf "%s/%s.list", $outdir, $root;
+    print STDERR "outlist: $outlist\n";
+
+    if (! -d $outdir) {
+	vsystem ("mkdir -p $outdir");
+	if ($?) { &escape ("can't make directory component for output $outdir"); }
+    }
+
+    open (FILE, "$list");
+    @list = <FILE>;
+    close (FILE);
+    foreach $line (@list) { chop $line; }
+
+    # list contains MOSAIC.med.medbin.jpg
+    # copy to html/CONFIG.med.medbin.NNN.jpg
+
+    $Nfound = 0;
+    foreach $line (@list) {
+	if (-e $line) { $Nfound ++; }
+    }
+    if ($Nfound < @list) { 
+	print STDERR "NOTE: no files from $list: assuming initial state\n";
+	open (FILE, ">$outlist");
+	close (FILE);
+	&goodbye; 
+    }
+
+    open (FILE, ">$outlist");
+    for ($i = 0; $i < @list; $i++) {
+
+	$inname  = $list[$i];
+	$outname = sprintf "%s/%s.%03d.jpg", $outdir, $root, $i;
+	vsystem ("cp -f $inname $outname");
+	if ($?) { &escape ("problem copying $inname to $outname"); }
+	
+	print FILE "$outname\n";
+    }
+    close (FILE);
+    &goodbye;
+}
+
+&escape ("unknown fr.frstats command $ARGV[0]");
+
+## utilities
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+  print STDOUT "SUCCESS: finished with fr.stats\n";
+  exit 0;
+}
+
+sub escape {
+  print STDOUT "ERROR: $_[0]\n";
+  exit 1;
+}
+
+sub usage {
+
+    print STDOUT "fr.mklists -fitpars CONFIG.med.sublist  CONFIG.fitpars\n";
+    print STDOUT "fr.frstats -frstats CONFIG.med.statlist CONFIG.fitpars CONFIG.med.imstats\n";
+
+    exit 2;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.mklists
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.mklists	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.mklists	(revision 22322)
@@ -0,0 +1,529 @@
+#!/usr/bin/env perl
+
+if (@ARGV < 1) {
+    print STDERR "USAGE: fr.mklists -mode [args]\n";
+    &escape;
+}
+
+if ($ARGV[0] eq "help") {
+
+    print STDERR "fr.mklists -initial  CONFIG.split CONFIG.mef CONFIG.master\n";
+    print STDERR "fr.mklists -detrend  CONFIG.master CONFIG.detrend\n";
+    print STDERR "fr.mklists -defringe CONFIG.master CONFIG.defringe\n";
+    print STDERR "fr.mklists -imlist   TEMPLATE CONFIG.statlist [Nccd?]\n";
+    print STDERR "fr.mklists -mosaic   CONFIG.master proc name\n";
+    print STDERR "fr.mklists -master   CONFIG.detrend CONFIG.master\n";
+    print STDERR "fr.mklists -inlist   CONFIG.master 00 proc CONF-CCD.inlist\n";
+    print STDERR "fr.mklists -imstats  CONFIG.master 00 proc CONF-CCD.imstats\n";
+    print STDERR "fr.mklists -frstats  CONFIG.master proc CONF-CCD.imstats\n";
+    &escape;
+}
+
+@ccds = split (" ", `cameraconfig -ccdn`);
+if ($?) { die "error in config\n"; }
+
+$STAT_XCLD = 0;
+$STAT_KEEP = 1;
+$STAT_XDET = 2;
+$STAT_DETR = 3;
+
+# merge CONFIG.mef & CONFIG.split to CONFIG.master [starting status = 3]
+if ($ARGV[0] eq "-initial") {
+
+    if (@ARGV != 4) {
+	print STDERR "fr.mklists -initial CONFIG.split CONFIG.mef CONFIG.master\n";
+	&escape;
+    }
+
+    open (FILE, "$ARGV[1]");
+    @list1 = <FILE>;
+    close (FILE);
+
+    open (FILE, "$ARGV[2]");
+    @list2 = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$ARGV[3]");
+    foreach $line (@list1) {
+	chop $line;
+	print FILE "$line $STAT_DETR\n";
+    }
+    foreach $line (@list2) {
+	chop $line;
+	print FILE "$line $STAT_DETR\n";
+    }
+    close (FILE);
+
+    if ((@list1 == 0) && (@list2 == 0)) {
+	print STDERR "no images available\n";
+	&escape;
+    }
+
+    &goodbye;
+}
+
+# convert CONFIG.master to CONFIG.detrend (status == XDET || DETR, strip status)
+if ($ARGV[0] eq "-detrend") {
+
+    if (@ARGV != 3) {
+	print STDERR "fr.mklists -detrend CONFIG.master CONFIG.detrend\n";
+	&escape;
+    }
+
+    open (FILE, "$ARGV[1]");
+    @list = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$ARGV[2]");
+    foreach $line (@list) {
+	@words = split (" ", $line);
+	if (($words[3] == $STAT_XDET) || ($words[3] == $STAT_DETR)) {
+	    print FILE "$words[0] $words[1] $words[2]\n";
+	}
+    }
+    close (FILE);
+    &goodbye;
+}
+
+# convert CONFIG.master to CONFIG.defringe, used to generate input list to mode.fringe3
+if ($ARGV[0] eq "-defringe") {
+
+    if (@ARGV != 5) {
+	print STDERR "@ARGV\n";
+	print STDERR "fr.mklists -detrend CONFIG.master CONFIG TMPPATH CONFIG.defringe\n";
+	&escape;
+    }
+
+    $infile   = $ARGV[1];
+    $confline = $ARGV[2];
+    $tmppath  = $ARGV[3];
+    $outfile  = $ARGV[4];
+
+    open (FILE, "$infile");
+    @list = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$outfile");
+    foreach $line (@list) {
+	($path, $root, $mode, $status) = split (" ", $line);
+	print FILE "$tmppath/$root $confline\n";
+    }
+    close (FILE);
+    &goodbye;
+}
+
+# use TEMPLATE to create a list with NCCD entries replacing CCDNUM
+if ($ARGV[0] eq "-imlist") {
+
+    if (@ARGV != 3) {
+	print STDERR "fr.mklists -detrend TEMPLATE CONFIG.defringe\n";
+	&escape;
+    }
+
+    open (FILE, ">$ARGV[2]");
+    foreach $ccd (@ccds) {
+	$name = $ARGV[1];
+	$name =~ s/CCDNUM/$ccd/;
+	print FILE "$name\n";
+    }
+    close (FILE);
+    &goodbye;
+}
+
+# use CONFIG.master to create Nimage lists, each called OUTDIR/root.WORDlist with
+# NCCD entries OUTDIR/root.WORD/rootNN.fits
+if ($ARGV[0] eq "-mosaic") {
+
+    if (@ARGV != 4) {
+	print STDERR "fr.mklists -mosaic CONFIG.master OUTDIR WORD\n";
+	&escape;
+    }
+
+    $infile = $ARGV[1];
+    $outpath = $ARGV[2];
+    $word = $ARGV[3];
+
+    open (FILE, $infile);
+    @inlist = <FILE>;
+    close (FILE);
+
+    foreach $name (@inlist) { 
+	($path, $root, $mode) = split (" ", $name);
+
+	$outfile = sprintf "%s/%s.%slist", $outpath, $root, $word;
+	
+	open (FILE, ">$outfile");
+	foreach $ccd (@ccds) {
+	    printf FILE "$outpath/$root.$word/$root$ccd.fits\n";
+	}
+	close (FILE);
+    }
+    &goodbye;
+}
+
+# use CONFIG.master to create OUTPUT list with Nimage lines: OUTDIR/root.WORD
+if ($ARGV[0] eq "-mlist") {
+
+    if (@ARGV != 5) { &escape ("USAGE: fr.mklists -mlist CONFIG.master OUTDIR WORD OUTPUT"); }
+
+    $infile   = $ARGV[1];
+    $outpath  = $ARGV[2];
+    $word     = $ARGV[3];
+    $outfile  = $ARGV[4];
+
+    open (FILE, $infile);
+    @inlist = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$outfile");
+    foreach $name (@inlist) { 
+	($path, $root, $mode) = split (" ", $name);
+	$file = sprintf "%s/%s.%s", $outpath, $root, $word;
+	printf FILE "$file\n";
+    }
+    close (FILE);
+
+    &goodbye;
+}
+
+# merge data from master, med.imstats, def.imstats, datlist into a single file for the html scripts
+if ($ARGV[0] eq "-imstats") {
+
+    if (@ARGV != 6) {
+	print STDERR "fr.mklists -imstats CONFIG.master CONFIG.med.imstats CONFIG.def.imstats CONFIG.datlist CONFIG.imstats\n";
+	&escape;
+    }
+
+    $master  = $ARGV[1];
+    $medstat = $ARGV[2];
+    $defstat = $ARGV[3];
+    $datlist = $ARGV[4];
+    $output  = $ARGV[5];
+
+    open (FILE, "$master");
+    @master = <FILE>;
+    close (FILE);
+
+    open (FILE, "$medstat");
+    @medstat = <FILE>;
+    close (FILE);
+    if (@master != @medstat) { &escape ("mismatch in master & medstat lists"); }
+
+    open (FILE, "$defstat");
+    @defstat = <FILE>;
+    close (FILE);
+    if (@master != @defstat) { print STDERR "NOTE: no data in $defstat\n"; }
+
+    open (FILE, "$datlist");
+    @datlist = <FILE>;
+    close (FILE);
+    if (@master != @datlist) { &escape ("mismatch in master & datlist lists"); }
+
+    open (FILE, ">$output");
+    for ($i = 0; $i < @master; $i++) {
+	($path, $root, $mode, $status) = split (" ", $master[$i]);
+	
+	open (LIST, "$datlist[$i]");
+	$line = <LIST>;
+	close (LIST);
+	($obsid, $date, $time, $exptime) = split (" ", $line);
+
+	($S1, $F1, $dF1) = split (" ", $medstat[$i]);
+	if (@defstat) {
+	    ($S2, $F2, $dF2) = split (" ", $defstat[$i]);
+	}
+	$flux = $S1 / $exptime;
+
+	if (@defstat) {
+	    printf FILE "%8s %11s %12s %7.1f %6.2f %6.1f %6.1f %6.1f %6.1f %2d %2d\n", $obsid, $date, $time, $S1, $flux, $F1, $dF1, $F2, $dF2, $status, $status;
+	} else {
+	    printf FILE "%8s %11s %12s %7.1f %6.2f %6.1f %6.1f   N/A    N/A  %2d %2d\n", $obsid, $date, $time, $S1, $flux, $F1, $dF1, $status, $status;
+	}
+    }
+    close (FILE);
+    &goodbye;
+}
+
+# use CONFIG.master to create a subset of INPUT in OUTPUT
+if ($ARGV[0] eq "-subset") {
+
+    if (@ARGV != 4) { &escape ("USAGE: fr.mklists -subset CONFIG.master INPUT OUTPUT"); }
+
+    $master = $ARGV[1];
+    $input  = $ARGV[2];
+    $output = $ARGV[3];
+
+    open (FILE, $master);
+    @master = <FILE>;
+    close (FILE);
+
+    open (FILE, $input);
+    @input = <FILE>;
+    close (FILE);
+
+    if (@master != @input) { &escape ("mismatch in input & master file lengths"); }
+
+    open (FILE, ">$output");
+
+    for ($i = 0; $i < @master; $i++) {
+	$line = $master[$i]; chop $line;
+	($path, $root, $mode, $status) = split (" ", $line);
+	if ($status != $STAT_KEEP) { next; }
+
+	$file = $input[$i]; chop $file;
+	printf FILE "$file\n";
+    }
+    close (FILE);
+
+    &goodbye;
+}
+
+# use master, med.imstats, and statlist to create input list for fr.mkrough (file sky fringe 0 0)
+if ($ARGV[0] eq "-ccdstats") {
+
+    if (@ARGV != 6) { &escape ("USAGE: fr.mklists -ccdstats CONFIG.master CONFIG.med.imstats CONFIG.med.statlist CCD CONFIG.CCD.ccdstats"); }
+
+    $master   = $ARGV[1];
+    $imstats  = $ARGV[2]; # list of basic image stats
+    $statlist = $ARGV[3]; # list of image stat files (1 line per frame)
+    $ccd      = $ARGV[4];
+    $output   = $ARGV[5];
+
+    # $ccd = `cameraconfig -N $ccdid`;
+    # if ($ccd eq "") { &escape ("error in CCD ID"); }
+
+    open (FILE, $master);
+    @master = <FILE>;
+    close (FILE);
+
+    open (FILE, $imstats);
+    @imstats = <FILE>;
+    close (FILE);
+    if (@master != @imstats) { &escape ("mismatch in master & imstats file lengths"); }
+
+    open (FILE, $statlist);
+    @statlist = <FILE>;
+    close (FILE);
+    if (@master != @statlist) { &escape ("mismatch in master & statlist file lengths"); }
+
+    # find image with strongest fringe amplitude
+    $fringemax = "";
+    for ($i = 0; $i < @imstats; $i++) {
+	$line = $master[$i]; chop $line;
+	($path, $root, $mode, $status) = split (" ", $line);
+	if ($status != $STAT_KEEP) { next; }
+	
+	$line = $imstats[$i]; chop $line;
+	($SKY, $fringe, $dfringe) = split (" ", $line);
+	if ($fringemax eq "") { $fringemax = $fringe; }
+	if ($fringemax < $fringe) { $fringemax = $fringe; }
+    }
+    print "fringemax: $fringemax\n";
+
+    open (FILE, ">$output");
+    for ($i = 0; $i < @master; $i++) {
+	$line = $master[$i]; chop $line;
+	($path, $root, $mode, $status) = split (" ", $line);
+	if ($status != $STAT_KEEP) { next; }
+
+	$line = $imstats[$i]; chop $line;
+	($SKY, $fringe, $dfringe) = split (" ", $line);
+	$frnorm = $fringe / $fringemax;
+	# imcombred is not renormalizing: this will force fringe amplitude to a useful level
+
+	# file is list of fringe stats (1 line per chip)
+	$file = $statlist[$i]; chop $file;
+	open (STAT, "$file");
+	@stats = <STAT>;
+	close (STAT);
+
+	$line = $stats[$ccd];
+	($name, $sky) = split (" ", $line);
+	
+	# compare $sky with average sky $SKY?
+	$dsky = $sky - $SKY;
+	print FILE "$name $sky $frnorm\n";
+    }
+    close (FILE);
+
+    &goodbye;
+}
+
+# re-create master list, applying changes from detrend list 
+if ($ARGV[0] eq "-master") {
+
+    if (@ARGV != 3) {
+	print STDERR "fr.mklists -master CONFIG.master CONFIG.detrend\n";
+	&escape;
+    }
+
+    $master = $ARGV[1];
+    $detrend = $ARGV[2];
+
+    open (FILE, $master);
+    @master = <FILE>;
+    close (FILE);
+    foreach $name (@master) { chop ($name); }
+
+    open (FILE, $detrend);
+    @detrend = <FILE>;
+    close (FILE);
+    foreach $name (@detrend) { chop ($name); }
+
+    @remaster = ();
+  MASTER:
+    foreach $name (@master) {
+	($path, $root, $mode, $status) = split (" ", $name);
+	
+	if (($status == $STAT_XDET) || ($status == $STAT_DETR)) {
+	    # find entry in detrend list.  if it exists, set status to status - 2
+	  DETREND:
+	    foreach $dname (@detrend) {
+		($dpath, $droot, $dmode) = split (" ", $name);
+		if ($droot eq $root) {
+		    if ($status == $STAT_XDET) { $status = $STAT_XCLD; }
+		    if ($status == $STAT_DETR) { $status = $STAT_KEEP; }
+		    last DETREND;
+		}
+	    }
+	}
+
+	$new = "$path $root $mode $status";
+	@remaster = (@remaster, $new);
+    }
+
+    open (FILE, ">$master");
+    foreach $name (@remaster) { 
+	print FILE "$name\n";
+    }
+    close (FILE);
+    &goodbye;
+}
+
+# create list of input (detrended) frames for a single CCD for mkstats (status == 1)
+if ($ARGV[0] eq "-inlist") {
+
+    if (@ARGV != 5) {
+	print STDERR "fr.mklists -inlist CONFIG.master CCD TMPPATH CONF-CCD.inlist\n";
+	&escape;
+    }
+
+    $infile  = $ARGV[1];
+    $ccd     = $ARGV[2];
+    $tmppath = $ARGV[3];
+    $outfile = $ARGV[4];
+
+    open (FILE, "$infile");
+    @inlist = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$outfile");
+    foreach $name (@inlist) { 
+	($path, $root, $mode, $status) = split (" ", $name);
+	if ($status != $STAT_KEEP) { next; }
+	
+	printf FILE "%s/%s.med/%s%s.fits\n", $tmppath, $root, $root, $ccd;
+    }
+    close (FILE);
+    &goodbye;
+}
+
+### this old -imstats will be replaced.
+# create list of input (detrended) frames for a single CCD for mkstats (status == 1)
+if ($ARGV[0] eq "-imstats") {
+
+    if (@ARGV != 5) {
+	print STDERR "fr.mklists -inlist CONFIG.master CCD TMPPATH CONF-CCD.imstats\n";
+	&escape;
+    }
+
+    $infile  = $ARGV[1];
+    $ccd     = $ARGV[2];
+    $tmppath = $ARGV[3];
+    $outfile = $ARGV[4];
+
+    open (FILE, "$infile");
+    @inlist = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$outfile");
+    foreach $name (@inlist) { 
+	($path, $root, $mode, $status) = split (" ", $name);
+	if ($status != $STAT_KEEP) { next; }
+	
+	printf FILE "%s/%s.imstats\n", $tmppath, $root;
+    }
+    close (FILE);
+    &goodbye;
+}
+
+# create list of input stats files 
+if ($ARGV[0] eq "-frstats") {
+
+    if (@ARGV != 4) {
+	print STDERR "fr.mklists -frstats CONFIG.master TMPPATH CONF-CCD.imstats\n";
+	&escape;
+    }
+
+    $infile  = $ARGV[1];
+    $tmppath = $ARGV[2];
+    $outfile = $ARGV[3];
+
+    open (FILE, "$infile");
+    @inlist = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$outfile");
+    foreach $name (@inlist) { 
+	($path, $root, $mode, $status) = split (" ", $name);
+	printf FILE "%s/%s.imstats\n", $tmppath, $root;
+    }
+    close (FILE);
+    &goodbye;
+}
+
+# create list of input stats files 
+if ($ARGV[0] eq "-maplist") {
+
+    if (@ARGV != 4) {
+	print STDERR "fr.mklists -maplist CONFIG.master TMPPATH CONFIG.maplists\n";
+	&escape;
+    }
+
+    $infile  = $ARGV[1];
+    $tmppath = $ARGV[2];
+    $outfile = $ARGV[3];
+
+    open (FILE, "$infile");
+    @inlist = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$outfile");
+    foreach $name (@inlist) { 
+	($path, $root, $mode, $status) = split (" ", $name);
+	printf FILE "%s/%s.maplist 1\n", $tmppath, $root;
+    }
+    close (FILE);
+    &goodbye;
+}
+
+&escape ("unknown mode $ARGV[0]");
+
+###########################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub escape {
+    print STDERR "ERROR: @_\n";
+    exit 1;
+}
+
+sub goodbye {
+    print STDERR "SUCCESS @_\n";
+    exit 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.mkmodes
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.mkmodes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.mkmodes	(revision 22322)
@@ -0,0 +1,146 @@
+#!/usr/bin/env perl
+
+{ # grab the command line arguments:
+    $elconf = "";
+    $elixcf = "-c /h/eugene/.ptolemyrc";
+    @tARGV = ();
+    for (; @ARGV > 0; ) {
+	
+	# grab elixir/ptolemy config option -c 
+	if ($ARGV[0] eq "-c") {
+	    shift;
+	    $elixcf = "-c $ARGV[0]";
+	    shift;
+	    next;
+	}
+	
+	# grab elixir/ptolemy config option -C 
+	if ($ARGV[0] eq "-C") {
+	    @opt = (@opt, $ARGV[0]);
+	    shift;
+	    @opt = (@opt, $ARGV[0]);
+	    $elconf = $ARGV[0];
+	    shift;
+	    next;
+	}
+	
+	# grab elixir/ptolemy config option -D
+	if ($ARGV[0] eq "-D") {
+	    @opt = (@opt, $ARGV[0]);
+	    shift;
+	    @opt = (@opt, $ARGV[0]);
+	    shift;
+	    @opt = (@opt, $ARGV[0]);
+	    shift;
+	    next;
+	}
+	
+	@tARGV = (@tARGV, $ARGV[0]);
+	shift;
+    }
+    @ARGV = @tARGV;
+    $elixir = join (" ", $elixcf, @opt);
+}
+
+# check the usage
+if ($ARGV[0] eq "-h") { usage (); }
+if ($ARGV[0] eq "-help") { usage (); }
+if (@ARGV != 3) { die "USAGE: fr.mkmodes (maplist) (mode.fits) (mode.list)\n" }
+
+# assign the command line arguments
+$list = $ARGV[0];
+$output = $ARGV[1];
+$modes = $ARGV[2];
+
+# find the appropriate script file
+$confdir = `gconfig $elixir -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/modes.pro";
+$Nccd = `cameraconfig $elixir -Nccd`; chop ($Nccd);
+
+# load in list of input image lists
+open (LIST, $list);
+@list = <LIST>;
+close (LIST);
+
+# if mode list exists, load it in and use the selected modes
+# to set the new default selection
+$GotModes = 0;
+if (-e $modes) {
+    open (LIST, $modes);
+    @modes = <LIST>;
+    close (LIST);
+    $GotModes = 1;
+}
+
+# send commands to mana:
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+
+# create a mana list of input file names
+print MANA "list names\n";
+$Nimages = 0;
+foreach $line (@list) {
+    ($name, $status) = split (" ", $line);
+    if ($status) { print MANA " $name\n"; }
+    $Nimages ++;
+}
+print MANA "end\n";
+
+# create a mana list of input file names
+print MANA "macro go\n";
+print MANA "  mkimage $output\n";
+print MANA "  exit 0\n";
+print MANA "end\n";
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) {
+    print STDERR "ERROR running mana\n";
+    exit 1;
+}
+
+# create new list of modes.  if modes existed, use selected modes, otherwisaudio/x-pn-realaudio-plugine first 8:
+$DefaultStatus = 1;
+open (LIST, ">$modes");
+for ($i = 0; $i < $Nimages; $i++) {
+    # this is just a little funny: there is not a 1-1 relationship 
+    # between the selected images and the modes in this selection.
+    # only the first few modes will be significant, so we are just
+    # cutting of the few end modes
+    if ($i >= 8) { $DefaultStatus = 0; }
+    if ($GotModes) {
+	($mode, $status) = split (" ", $modes[$i]);
+	print LIST "$mode $status\n";
+    } else {
+	print LIST "$i $DefaultStatus\n";
+    }
+}
+close (LIST);
+
+print STDOUT "SUCCESS: finished with fr.detrend\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "\n";
+    print "USAGE: fr.mkmodes (list) (output) (modes)\n";
+    print " list:   list of lists - each list contains names of the input images (*.map/*.fits)\n";
+    print " output: master image of orthonormal modes\n";
+    print " modes:  list of modes in output, with status\n";
+    exit 1;
+}
+
+# mana within perl:  we embed the mana functions within a macro to trap the 
+# exit status.  If mana has an error with one of the steps, it will exit the macros and 
+# the second exit will be called. Otherwise, the "go" macro will perform the exit 0
+# and mana will exit with status 0
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.modesave
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.modesave	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.modesave	(revision 22322)
@@ -0,0 +1,78 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { &usage (); }
+if ($ARGV[0] eq "-help") { &usage (); }
+if (@ARGV != 6) { &usage (); }
+
+# assign the command line arguments
+$modefits  = $ARGV[0];
+$modelist  = $ARGV[1];
+$modesave  = $ARGV[2];
+$startime  = $ARGV[3];
+$stoptime  = $ARGV[4];
+$filter    = $ARGV[5];
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/modes.pro";
+
+# load in list of modes
+open (LIST, $modelist);
+@modelist = <LIST>;
+close (LIST);
+
+$Nmode = 8;
+
+# run mana script:
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+
+print MANA "macro go\n";
+print MANA "\$filter = $filter\n";
+print MANA "\$start = \"$startime\"\n";
+print MANA "\$stop = \"$stoptime\"\n";
+print MANA "\$Nmode = $Nmode\n";
+for ($i = 0; $i < $Nmode; $i++ ) {
+    ($mode, $status) = split (" ", $modelist[$i]);
+    if ($status) {
+	print MANA "\$mode$i = 1\n";
+    } else {
+	print MANA "\$mode$i = 0\n";
+    }
+}
+print MANA "rd U $modefits\n";
+print MANA "savemodes $modesave\n";
+print MANA "exit 0\n";
+
+print MANA "end\n";
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) {
+    print STDERR "ERROR problem with fr.modesave\n";
+    exit 1;
+}
+
+print STDOUT "SUCCESS: finished with fr.modesave\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "USAGE: fr.modesave (mode.fits) (mode.list) (mode.mef.fits) (start) (stop) (filter)\n";
+    exit 1;
+}
+
+# mana within perl:  we embed the mana functions within a macro to trap the 
+# exit status.  If mana has an error with one of the steps, it will exit the macros and 
+# the second exit will be called. Otherwise, the "go" macro will perform the exit 0
+# and mana will exit with status 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.modestats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.modestats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.modestats	(revision 22322)
@@ -0,0 +1,98 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { &usage (); }
+if ($ARGV[0] eq "-help") { &usage (); }
+if (@ARGV != 6) { &usage (); }
+
+# assign the command line arguments
+$imlist    = $ARGV[0];
+$modefits  = $ARGV[1];
+$modelist  = $ARGV[2];
+$modejpg   = $ARGV[3]; # root of jpg images
+$residjpg  = $ARGV[4]; # root of jpg images
+$inmapjpg  = $ARGV[5]; # root of jpg images
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/modes.pro";
+
+# test named Xserver, or start vnc on specified machine
+$xhost=`gconfig -q XHOST`; chop ($xhost);
+$xdisp=`gconfig -q XDISP`; chop ($xdisp);
+if (vsystem ("xdpyinfo -display $xdisp >& /dev/null")) {
+    print STDERR "X server is not running. start it now on $xhost\n";
+    exit 1;
+}
+$ENV{'DISPLAY'} = $xdisp;
+
+# load in list of input image lists
+open (LIST, $imlist);
+@imlist = <LIST>;
+close (LIST);
+
+# load in list of input image lists
+open (LIST, $modelist);
+@modelist = <LIST>;
+close (LIST);
+$Nmode = 0;
+foreach $line (@modelist) {
+    ($mode, $status) = split (" ", $line);
+    if ($status) { $Nmode ++; }
+}
+
+# run mana script:
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+print MANA "macro go\n";
+
+# we'll use the default 8 to generate the jpgs of the modes
+print MANA "jpgmodes $modefits $modejpg\n";
+
+system ("rm -f $residjpg");
+system ("rm -f $inmapjpg");
+
+# reset $Nmode for the actual number desired
+print MANA "\$Nmode = $Nmode\n";
+print MANA "getmodes\n";
+for ($i = 0; $i < @imlist; $i++) {
+    ($name, $status) = split (" ", $imlist[$i]);
+    $jpgname = sprintf "%s.%03d.jpg", $residjpg, $i;
+    system ("basename $jpgname >> $residjpg");
+    $srcname = sprintf "%s.%03d.jpg", $inmapjpg, $i;
+    system ("basename $srcname >> $inmapjpg");
+    print MANA "jpgresid $name $jpgname $srcname\n";
+}
+print MANA "exit 0\n";
+
+print MANA "end\n";
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) {
+    print STDERR "ERROR problem with fr.modestats\n";
+    exit 1;
+}
+
+print STDOUT "SUCCESS: finished with fr.modestats\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "USAGE: fr.modestats (map.list) (mode.fits) (mode.list) (modes[.NNN.jpg]) (resid[.NNN.jpg]) (source[.NNN.jpg])\n";
+    exit 1;
+}
+
+# mana within perl:  we embed the mana functions within a macro to trap the 
+# exit status.  If mana has an error with one of the steps, it will exit the macros and 
+# the second exit will be called. Otherwise, the "go" macro will perform the exit 0
+# and mana will exit with status 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.select
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.select	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.select	(revision 22322)
@@ -0,0 +1,325 @@
+#!/usr/bin/env perl
+
+# check the usage
+if ($ARGV[0] eq "-h") { usage (); }
+if ($ARGV[0] eq "-help") { usage (); }
+if (@ARGV != 5) { die "ERROR: USAGE: fr.select (filter) (start) (stop) (mode) (output)\n"; }
+
+# assign the command line arguments
+$filter = $ARGV[0];
+$start  = $ARGV[1];
+$stop   = $ARGV[2];
+$mode = "\U$ARGV[3]\E";
+if (($mode ne "MEF") && ($mode ne "SPLIT")) { die "ERROR: mode must be either SPLIT or MEF\n";}
+$output = $ARGV[4];
+
+# global constants
+# these probably should go in the config files eventually.
+$MAX_CNTS = 60000;
+$MAX_FLUX = 40;
+
+# define some of the relevant parameters
+$tmp = `filtnames $filter`; chop $tmp;
+if ($?) { die "ERROR: invalid filter $filter\n"; }
+$filter = $tmp;
+
+# recipe file defines detrend types & cutoff exptime values
+$recipefile = `gconfig DETREND_RECIPES`; chop $recipefile;
+if ($?) { die "ERROR: missing DETREND_RECIPES in configuration\n"; }
+
+# extract filter extras list
+($tmp, $tmp, $tmp, $extras) = split (" ", `gconfig -c $recipefile $filter`);
+if ($?) { die "ERROR: missing detrend recipe for $filter\n"; }
+
+# grab extra values:
+($detype, $MIN_FLUX, $MIN_CNTS) = split (",", $extras);
+if ("\U$detype\E" ne "FRINGE") { die "ERROR: fringe extras are missing\n"; }
+
+# relevant camera parameters
+$Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+if ($?) { die "error in config\n"; }
+
+($Naxis1, $Naxis2) = split (" ", `cameraconfig -axes`);
+if ($?) { die "error in config\n"; }
+
+@ccds = split (" ", `cameraconfig -ccds`);
+if ($?) { die "error in config\n"; }
+
+@good = ();
+@marginal = ();
+
+# special list to skip specific chips
+$CHIPSKIP     = `gconfig CHIP_SKIP_LIST`; chop $CHIPSKIP;
+@chipskip = ();
+if (-e $CHIPSKIP) { 
+    open (FILE, $CHIPSKIP);
+    @chipskip = <FILE>;
+    close (FILE);
+    foreach $chip (@chipskip) { chop $chip; }
+}
+
+$criteria = "-type object -mode $mode -trange $start $stop -filter $filter";
+
+# define filter lines.
+@filtline = ();
+# convert given filter name to list of possible names:
+$answer = `filtnames $ARGV[0] -all`;
+@filtlist = split (" ", $answer);
+
+foreach $name (@filtlist) {
+    $line = "-type object -mode $mode -trange $start $stop -filter $name";
+    push @filtline, $line;
+}
+
+# run selection on each possible $filtline
+@list = ();
+foreach $line (@filtline) {
+    @tlist = `imsearch -ccd $ccds[0] $line`;
+    @list = (@list, @tlist);
+}
+$Nlist = @list;
+print STDERR "found $Nlist images\n";
+
+FRAME:
+    foreach $image (@list) {
+    @words = split (" ",$image);
+    
+    # poor way to search for all images
+    if ($mode eq "SPLIT") {
+	# all $Nccd images must exist on disk, split files are assumed to be 
+	# in a subdirectory of their own, .fits extension assumed
+	@answer = <$words[4]/*.fits>;
+	if (@answer != $Nccd) { print STDERR "$words[5] rejected: missing images\n"; next FRAME; }
+    }
+
+    # images have to have the correct dimensions
+    if ($mode eq "SPLIT") {
+	$answer = `echo $words[4]/$words[5] | fields NAXIS1 NAXIS2`;
+    } else {
+	$answer = `echo $words[4]/$words[5] | fields -x 0 NAXIS1 NAXIS2`;
+    }
+    ($tmp, $Nx, $Ny) = split (" ", $answer);
+    if (($Nx != $Naxis1) || ($Ny != $Naxis2)) { print STDERR "$words[5] rejected: wrong dimensions\n"; next FRAME; }
+
+    # $match is a word unique to the full mosaic image (the rootdir for split, the image for mef) 
+    if ($mode eq "split") { 
+	@answer = split ("/", $words[4]); 
+	$match = $answer[-1]; 
+    } else { 
+	$match = $words[5]; 
+    }
+    @list2 = `imsearch -name $match`;
+    
+  CCD:
+    foreach $ccd (@list2) {
+	@words2 = split (" ", $ccd);
+	foreach $chipskip (@chipskip) { 
+	    if ($words2[3] eq $chipskip) { next CCD; }
+	}
+	
+	$cnts = $words2[11];
+	$flux = $words2[11] / $words2[7];
+	# test for acceptable flux range
+	if ($cnts < $MIN_CNTS) { print STDERR "$words[5] rejected: cnts too low:  $cnts vs $MIN_CNTS\n"; next FRAME; }
+	if ($cnts > $MAX_CNTS) { print STDERR "$words[5] rejected: cnts too high: $cnts vs $MAX_CNTS\n"; next FRAME; }
+	if ($flux < $MIN_FLUX) { print STDERR "$words[5] rejected: flux too low:  $flux vs $MIN_FLUX\n"; next FRAME; }
+	if ($flux > $MAX_FLUX) { print STDERR "$words[5] rejected: flux too high: $flux vs $MAX_FLUX\n"; next FRAME; }
+    }
+
+    $fullname = "$words[4]/$words[5]";
+    $root = gt_names ($fullname, "root", $mode);
+    $path = gt_names ($fullname, "path", $mode);
+    @good = (@good, "$path $root $mode");
+}
+
+open (LIST, ">$output");
+# write output to Nccd list files 
+# keep a random sample of roughly 60 images:
+$Nselect = @good;
+for ($i = 0; $i < $Nselect; $i++) {
+    if (rand ($Nselect) < 60) {
+	print LIST "$good[$i]\n";
+    }
+}
+close (LIST);
+print STDERR "SUCCESS\n\n";
+exit 0;
+
+# cfht MEF/SPLIT name utilities:
+
+sub gt_names {
+    # in: filename mode type
+    # out: word
+
+    my ($value);
+
+    $value = "";
+    if ($_[2] eq "MEF")   { $value = mefnames ($_[0], $_[1]); }
+    if ($_[2] eq "SPLIT") { $value = splitnames ($_[0], $_[1]); }
+    
+    return $value;
+}
+
+sub mefnames {
+    # in: (/path/file.fits) (mode) 
+    # out: word
+
+    my($fullname) = $_[0];
+    my($mode) = $_[1];
+    my($answer);
+
+    # split rootdir and filename:
+    my (@words) = split ("/", $fullname);
+    $N = @words;
+
+    if ($mode eq "path") {
+	$answer = "";
+	for ($i = 0; $i < $N - 1; $i++) {
+	    $answer = $answer . $words[$i] . "/";
+	}
+	chop ($answer); # strip off last /
+	if ($answer eq "") { $answer = "."; }
+	return ($answer);
+    }
+    
+    if ($mode eq "root") {
+	$answer = "$words[$N-1]";
+	$answer =~ s/.fits$//;
+	return ($answer);
+    }
+    
+    if ($mode eq "Nccd") {
+	if (! -e $fullname) {
+	    return (0);
+	}
+	$answer = `echo $fullname | fields NEXTEND`;
+	@words = split (" ", $answer);
+	if ($words[1] eq "") {
+	    return (0);
+	}
+	return ($words[1]);
+    }
+    
+    return (0);
+}
+
+sub splitnames {
+    # in: (/path/file) (mode) 
+    # out: word
+    # /path/file is directory containing Nccd fits images
+
+    my($fullname) = $_[0];
+    my($mode) = $_[1];
+    my($answer, $N, $tmpname, @imlist, @words);
+
+    # split rootdir and filename:
+    @words = split ("/", $fullname);
+    $N = @words;
+
+    if ($mode eq "path") {
+	$answer = "";
+	for ($i = 0; $i < $N - 2; $i++) {
+	    $answer = $answer . $words[$i] . "/";
+	}
+	chop ($answer); # strip off last /
+	if ($answer eq "") { $answer = "."; }
+	return ($answer);
+    }
+    
+    if ($mode eq "root") {
+	$answer = "$words[$N-2]";
+	return ($answer);
+    }
+    
+    if ($mode eq "Nccd") {
+	
+	$tmpname = "$fullname/$words[$N-1]";
+	@imlist = <$tmpname??.fits>;
+
+	$Nccd = @imlist;
+	return $Nccd;
+    }
+    
+    return (0);
+}
+
+sub ckimtype {
+    # in: (/path/file)
+    # out: MEF | SPLIT | SINGLE | (NULL)
+    
+    my ($answer, $name, $value);
+    my ($file) = $_[0];
+
+    if (! -e $file) { 
+	print STDOUT "can't open file\n";
+	return ""; 
+    }
+
+    # if /path/file is a directory, it is split:
+    if (-d $file) { return "SPLIT"; }
+
+    # check that the file is a FITS image
+    $answer = `file -L $file`;
+    if ($?) { 
+	print STDOUT "can't open file\n";
+	return ""; 
+    }
+    ($name, $value) = split (" ", $answer);
+    if ($value ne "FITS") { 
+	print STDOUT "not a valid image\n";
+	return ""; 
+    }
+    
+    # check for NAXIS = 0 (MEF) 
+    $answer = `echo $file | fields NAXIS`;
+    # if ($?) { return ""; }
+    ($name, $value) = split (" ", $answer);
+    if ($value eq 0) { return "MEF"; }
+    
+    if ($value == 2) { return "SINGLE"; }
+
+    print STDOUT "not a valid image\n";
+    return "";
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    die "@_";
+}
+
+sub usage {
+    print "USAGE: fr.select (filter) (start) (stop) (mode) (output)\n";
+    print " filter: R I Z (etc)\n";
+    print " start:  start date (yyyy/mm/dd,hh:mm:ss)\n";
+    print " stop:   stop date\n";
+    print " mode:   MEF or SPLIT\n";
+    print " output: output file name\n";
+    exit 1;
+}
+
+# we need to select the list of images which satisfy a few criteria:
+# 1) all 12 CCDs must exist
+# 2) stats must be measured for each
+# 3) stats for all 12 must be within range (2000 - 40000)
+# 4) NAXIS1 == 2080 && NAXIS2 == 4128
+
+# fr.select: 
+# this program takes a list of criteria which define a fringe /
+# skyring frame, and an output filename, and fills the file with all
+# images that fit the given criteria.  It checks to make sure that the
+# images have flux and counts in the appropriate range, and that all
+# CCDs are available, and that they have the correct dimensions.
+
+# the output file consists of lines with the following entries:
+# (path) (root) (mode)
+# path: complete path to the image
+# root: base name for file - MEF: root.fits, SPLIT: root/rootNN.fits
+# mode: MEF / SPLIT
+
+ 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.smooth
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.smooth	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/fr.smooth	(revision 22322)
@@ -0,0 +1,196 @@
+#!/usr/bin/env perl
+
+# this program converts the binned fringe image to an unbinned image,
+# adds necessary header information.  it could be used to smooth the 
+# image based on the filter and chip, if needed
+
+$version = 2.0;
+
+if (@ARGV != 8) { &escape ("ERROR: USAGE: fr.smooth (infile) (filter) (CCD) (unbin) (start) (stop) (frstats) (outfile)"); }
+
+$infile  = $ARGV[0];  # input image file
+$filter  = $ARGV[1];  # filter for image
+$ccd     = $ARGV[2];  # ccd number
+$binning = $ARGV[3];  # binning of input image
+$start   = $ARGV[4];  # valid time range start
+$stop    = $ARGV[5];  # valid time range stop
+$frstats = $ARGV[6];  # fringe statistics file
+$outfile = $ARGV[7];  # output image file
+
+@ccds   = split (" ", `cameraconfig -ccds`);
+@ccdn   = split (" ", `cameraconfig -ccdn`);
+$Nccd   = @ccds;
+
+$CCDKEYWORD = `gconfig CCDNUM-KEYWORD`; chop $CCDKEYWORD;
+$dbmode = `gconfig DETREND-DB-MODE`; chop $dbmode;
+
+@frstats = `cat $frstats`;
+for ($i = 0; $i < @frstats; $i++) {
+    if ($frstats[$i] =~ m|^\s*\#|) { 
+	($tmp, $entry, $value) = split (" ", $frstats[$i]);
+	if ($entry eq "REF_CHIP") { $RefChip = $ccds[$value]; }
+	splice @frstats, $i, 1; $i--; 
+    }
+}
+($C0, $C1, $Fs, $Fr) = split (" ", $frstats[$ccd]);
+
+# find fringe-point file
+$frpts  = `detsearch -quiet -image $infile $ccds[$ccd] split -type frpts`; chop $frpts;
+if ($?) { &escape ("can't get fringe points for image"); }
+($name, $Fs, $Fr) = split (" ", `getfringe $infile $ccds[$ccd] $frpts $binning`);
+
+$mask = `detsearch -ccd $ccds[$ccd] -type mask -time $start -select -quiet`; chop $mask;
+if ($?) { &escape ("ERROR: can't find mask for image $1"); }
+
+# unbin and insert header keywords
+open (MANA, "|mana --norc");
+
+# convert BIASSEC string to x1, x2, y1, y2 vars
+print MANA "macro parsesec\n";
+print MANA "  \$f = \$1\n";
+print MANA "  getchr \$f : n\n";
+print MANA "  substr \$f 1 {\$n-1} x1\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+  
+print MANA "  getchr \$f , n\n";
+print MANA "  substr \$f 1 {\$n-1} x2\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+  
+print MANA "  getchr \$f : n\n";
+print MANA "  substr \$f 1 {\$n-1} y1\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+  
+print MANA "  getchr \$f ] n\n";
+print MANA "  substr \$f 1 {\$n-1} y2\n";
+print MANA "  strlen \$f N\n";
+print MANA "  substr \$f \$n {\$N-\$n} f\n";
+print MANA "end\n";
+
+print MANA "list kernel\n";
+print MANA "  0.025\n";
+print MANA "  0.100\n";
+print MANA "  0.025\n";
+print MANA "  0.100\n";
+print MANA "  0.500\n";
+print MANA "  0.100\n";
+print MANA "  0.025\n";
+print MANA "  0.100\n";
+print MANA "  0.025\n";
+print MANA "end\n";
+print MANA "macro go\n";
+print MANA " \$CCDKEYWORD = $CCDKEYWORD\n";
+# read in mask, rebin
+if ($dbmode eq "MEF") {
+    print MANA " rd mask $mask -n $ccds[$ccd]\n";
+} else {
+    print MANA " rd mask $mask\n";
+}
+print MANA " keyword mask NAXIS1 nx2\n";
+print MANA " keyword mask NAXIS2 ny2\n";
+print MANA " rebin mask binmask $binning -norm\n";
+print MANA " clip binmask 0.9 0.0 1.1 0.0\n";
+
+print MANA " rd a $infile\n";
+print MANA " set a = a * binmask\n";
+# print MANA " set a = a * binmask + $Fs*not(binmask)\n";
+print MANA " rebin a A -$binning\n";
+print MANA " keyword A NAXIS1 nx1\n";
+print MANA " keyword A NAXIS2 ny1\n";
+print MANA " delete a\n";
+
+print MANA " extract A b 0 0 \$nx1 \$ny1 0 0 \$nx2 \$ny2\n";
+print MANA " keyword b DATASEC line\n";
+print MANA " parsesec \$line\n";
+print MANA " sprint newline \"[%d:%d,%d:%d]\" {\$x1*2} {\$x2*2} {\$y1*2} {\$y2*2}\n";
+print MANA " keyword b DATASEC -w \$newline\n";
+
+@time  = localtime;
+$now   = sprintf "%04d-%02d-%02dT%02d:%02d:%02d", $time[5]+1900, $time[4]+1, $time[3], $time[2], $time[1], $time[0];
+$elsys = `gconfig ELIXIR_VERSION`; chop ($elsys);
+
+@word = split ("/", $frpts);
+$frpts = $word[-1];
+
+# add keywords for fringe amplitude, fit, etc
+print MANA " keyword b FRNG_C0  -wf $C0\n";
+print MANA " keyword b FRNG_C1  -wf $C1\n";
+print MANA " keyword b FRNG_SKY -wf $Fs\n";
+print MANA " keyword b FRNG_RNG -wf $Fr\n";
+print MANA " keyword b FRNG_REF -w  $RefChip\n";
+print MANA " keyword b FRNG_PTS -w  \"$frpts\"\n";
+print MANA " keyword b TVSTART  -w  $start\n";
+print MANA " keyword b TVSTOP   -w  $stop\n";
+print MANA " keyword b EL_SYS   -w  \"$elsys\"\n";
+print MANA " keyword b EL_SMTH  -w  \"$version\"\n";
+print MANA " keyword b REL_DATE -w  \"$now\"\n";
+
+# add comments for the new keywords
+print MANA " keyword b FRNG_C0  -wc \"Fringe Cross Correlation linear fit term\"\n";
+print MANA " keyword b FRNG_C1  -wc \"Fringe Cross Correlation linear fit term\"\n";
+print MANA " keyword b FRNG_SKY -wc \"Fringe Frame sky level\" \n";
+print MANA " keyword b FRNG_RNG -wc \"Fringe Frame fringe strength\"\n";
+print MANA " keyword b FRNG_REF -wc \"Fringe Cross Correlation reference chip\"\n";
+print MANA " keyword b FRNG_PTS -wc \"Fringe Point File\"\n";
+print MANA " keyword b TVSTART  -wc \"data validity start time\"\n";
+print MANA " keyword b TVSTOP   -wc \"data validity stop time\"\n";
+print MANA " keyword b OBSTYPE  -w  \"FRINGE\"\n";
+print MANA " keyword b EXPTYPE  -w  \"FRINGE\"\n";
+print MANA " keyword b EL_SYS   -wc \"Elixir System Version\"\n";
+print MANA " keyword b EL_SMTH  -wc \"Elixir Smooth Version\"\n";
+print MANA " keyword b REL_DATE -wc \"UTC Release Date\"\n";
+
+print MANA " keyword b COMMENT  -ws \"Fringe (NN) = C0 + C1 * Fringe (REF)\"\n";
+print MANA " keyword b COMMENT  -ws \"Elixir:fr.smooth version $version\"\n";
+print MANA " keyword b COMMENT  -ws \"Elixir Fringe Frame\"\n";
+print MANA " keyword b COMMENT  -ws \"Elixir smoothing: 3x3 kernel (1): 0.025, 0.100, 0.025\"\n";
+print MANA " keyword b COMMENT  -ws \"Elixir smoothing: 3x3 kernel (2): 0.100, 0.500, 0.100\"\n";
+print MANA " keyword b COMMENT  -ws \"Elixir smoothing: 3x3 kernel (3): 0.025, 0.100, 0.025\"\n";
+
+print MANA " stat b - - - -\n";
+print MANA " \$range = \$MAX - \$MIN + 4\n";
+print MANA " if (\$range < 100)\n";
+print MANA "  \$range = 100\n";
+print MANA " end\n";
+print MANA " sprintf bscale %9.4f {\$range / 2^16}\n";
+print MANA " \$bzero = \$bscale * int ((\$MIN + \$range / 2) / \$bscale)\n";
+
+print MANA " wd b $outfile -bzero \$bzero -bscale \$bscale \n";
+print MANA " exit 0\n";
+
+print MANA "end\n";
+print MANA "macro list go\n";
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) { &escape ("ERROR: failure running mana"); }
+
+print STDERR "SUCCESS\n";
+exit 0;
+
+####################################################
+
+@temp = ();
+sub mktemp {
+
+    my ($base) = $_[0];
+    my ($name);
+    $name = `mktemp /tmp/$base.XXXXXX`;
+    chop $name;
+
+    push @temp, $name;
+    return $name;
+}
+
+sub escape {
+    $message = $_[0];
+    foreach $name (@temp) {
+	unlink ($name);
+    }
+    die "$message\n";
+}
+
+#print MANA " kern a kernel \n";
+#print MANA " set b = a*mask + $Fs*not(mask)\n";
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/gastro.raw
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/gastro.raw	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/gastro.raw	(revision 22322)
@@ -0,0 +1,79 @@
+#!/usr/bin/env perl
+
+$config = "";
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-C") {
+        $config = "-C $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-c") {
+        $config = "-c $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-D") {
+        $config = "-D $ARGV[1] $ARGV[2]";
+        shift; shift; shift; next;
+    }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 1) { die "USAGE: gastro.raw (filename)\n"; }
+
+if (! -e $ARGV[0]) { die "ERROR: file $ARGV[0] missing\n"; }
+
+($tmp, $RA, $DEC, $EXT) = split (" ", `echo $ARGV[0] | fields RA DEC EXTNAME`);
+
+if ($RA  eq "") { die "ERROR: missing RA in header\n"; }
+if ($DEC eq "") { die "ERROR: missing DEC in header\n"; }
+if ($EXT eq "") { die "ERROR: missing EXTNAME in header\n"; }
+
+while ($RA  =~ s|\:| |) { }
+while ($DEC =~ s|\:| |) { }
+
+($ra, $dec) = split (" ", `echo $RA $DEC | radec -hms`);
+
+@ccdx = split (" ", `cameraconfig $config -xoff`);
+@ccdy = split (" ", `cameraconfig $config -yoff`);
+@dx   = split (" ", `cameraconfig $config -xflip`);
+@dy   = split (" ", `cameraconfig $config -yflip`);
+
+$DX = `cameraconfig $config -axis0`; chop $DX;
+$DY = `cameraconfig $config -axis1`; chop $DY;
+$NX = `cameraconfig $config -mosaicx`; chop $NX;
+$NY = `cameraconfig $config -mosaicy`; chop $NY;
+
+$N = `cameraconfig $config -N $EXT`; chop $N;
+
+$Ro = $ra  - ($ccdx[$N] - $NX/2)*$DX*0.187/3600;
+$Do = $dec + ($ccdy[$N] - $NY/2)*$DY*0.187/3600;
+$Theta = 4;
+$P1 = -1;
+$P2 =  1;
+if ($dx[$N] == 1) {
+    $P1 =  1;
+    $Theta = $Theta * -1;
+}
+if ($dy[$N] == 1) {
+    $P2 =  -1;
+    $Theta = $Theta * -1;
+}
+
+vsystem ("gastro -v $config $ARGV[0] -coords $Ro $Do -D CCD_PC1_1 $P1 -D CCD_PC2_2 $P2 -D ROT_ZERO $Theta");
+exit 0;
+
+############
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    die "@_";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/getfringe
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/getfringe	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/getfringe	(revision 22322)
@@ -0,0 +1,118 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 4) { &escape ("USAGE: getfringe (input) (ccd) (frpts) (bin)"); }
+
+# assign the command line arguments
+$input   = $ARGV[0];
+$ccd     = $ARGV[1];
+$frpts   = $ARGV[2];
+$binning = $ARGV[3];
+
+# temporary files for flips input/output
+$temp1   = mktemp ("\@getfringe");
+$temp2   = mktemp ("getfringe");
+$temp3   = mktemp ("getfringe");
+
+# create input list file
+@coords = `gtfringetable $frpts $ccd $binning`;
+if ($?) { &escape ("failure to extract fringe point table"); }
+open (FILE, ">$temp1");
+foreach $line (@coords) {
+    chop ($line);
+    print FILE "$input $line\n";
+}
+close (FILE);
+
+# box size for stats:
+$dx = 1 + 2*int(0.5*25/$binning);
+
+# find input parameter file
+$paramfile = `gconfig FLIPS_PARAM_IMSTAT`; chop $paramfile;
+if ($?) { &escape ("paramfile FLIPS_PARAM_IMSTAT not in config system"); }
+
+# fill in missing keywords
+open (FILE, "$paramfile");
+@params = <FILE>;
+close (FILE);
+
+foreach $line (@params) {
+    chop ($line);
+    $line =~ s|INFILE|$temp1|;
+    $line =~ s|SIZEX|$dx|;
+    $line =~ s|SIZEY|$dx|;
+    $line =~ s|OUTFILE|$temp3|;
+}
+
+# save new parameter file
+open (FILE, ">$temp2");
+foreach $line (@params) {
+    print FILE "$line\n";
+}
+close $temp2;
+
+# run imstat (change to flips2 version)
+system ("imstat $temp2 > /dev/null");
+if ($?) { &escape ("failure in imstat"); }
+
+# accumulate sky, fringe
+open (FILE, "$temp3");
+@stats = <FILE>;
+@sky    = ();
+@fringe = ();
+for ($i = 0; $i < @stats; $i++) {
+    ($name, $max) = split (" ", $stats[$i]);
+    $i ++;
+    ($name, $min) = split (" ", $stats[$i]);
+
+    $fringe = $max - $min;
+    push @sky, $min;
+    push @fringe, $fringe;
+}
+
+# calculate fringe sigma:
+$F1 = $F2 = 0;
+foreach $fringe (@fringe) { 
+    $F1 += $fringe;
+    $F2 += $fringe*$fringe;
+}
+$N = @fringe;
+$F1 = $F1 / $N;
+$dfringe = sqrt ($F2/$N - $F1*$F1);
+
+# calculate fringe & sky medians
+@fringe = sort {$a <=> $b} @fringe;
+$fringe = $fringe[int(0.5*$N)];
+@sky = sort @sky;
+$sky = $sky[int(0.5*$N)];
+
+# write out results 
+print STDOUT "$input $sky $fringe $dfringe\n";
+
+&goodbye;
+
+########
+
+@temp = ();
+sub mktemp {
+
+    my ($base) = $_[0];
+    my ($name);
+    $name = `mktemp /tmp/$base.XXXXXX`;
+    chop $name;
+
+    push @temp, $name;
+    return $name;
+}
+
+sub escape {
+    $message = $_[0];
+    foreach $name (@temp) {
+	unlink ($name);
+    }
+    die "$message\n";
+}
+
+sub goodbye {
+    foreach $name (@temp) { unlink ($name); }
+    exit 0;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/grab.keywords
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/grab.keywords	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/grab.keywords	(revision 22322)
@@ -0,0 +1,243 @@
+#!/usr/bin/env perl
+
+$version = 2.0;
+
+if (@ARGV != 3) { die "USAGE: grab.keywords (infile) (cmproot) (outfile)\n" ; }
+
+$infile  = $ARGV[0];
+$cmproot = $ARGV[1];
+$outfile = $ARGV[2];
+
+# $infile  is the image.fits file
+# $cmproot is the rootname of the processed output for this file (contains astrom keywords)
+# $outfile is the output header data file
+
+if ($cmproot eq "phu") { goto photometry; }
+
+# grab astrometry keywords from $cmpfile:
+@header = ();
+
+ CMPTEST: {
+     # make this look for hdx files in hdxdir (fix dads.keywords as well)
+     # need to extract all header files first
+
+     # try *.smp:
+     $cmpfile = "$cmproot.smp";
+     if (-e $cmpfile) {
+	 $answer = `echo $cmpfile | fields NASTRO`;
+	 @words = split (" ", $answer);
+	 if ($words[1] > 0) { last CMPTEST; }
+     }
+    
+     # try *.cmp:
+     $cmpfile = "$cmproot.cmp";
+     if (-e $cmpfile) {
+	 $answer = `echo $cmpfile | fields NASTRO`;
+	 @words = split (" ", $answer);
+	 if ($words[1] > 0) { last CMPTEST; }
+     }
+
+     $cmpfile = "";
+ }
+
+if ($cmpfile) { @header = `fhead $cmpfile`; }
+
+# set some default values:
+@astrom = ();
+$fsatur = 0;
+$flimit = 0;
+$exptime = 0;
+$Nastro = 0;
+
+# search for the appropriate lines:
+foreach $line (@header) {
+    
+    # astrometry keywords which are copied directly
+    if ($line =~ /CTYPE1  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /CTYPE2  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /CRPIX1  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /CRPIX2  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /CRVAL1  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /CRVAL2  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /NASTRO  =/) { @astrom = (@astrom, $line); $Nastro = substr ($line, 10, 21); }
+    if ($line =~ /CERROR  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /EQUINOX =/) { @astrom = (@astrom, $line);}
+
+    # convert the PC00i00j matrix & CDELTi to CDi_j
+    if ($line =~ /PC001001=/) { $pc11 = substr ($line, 10, 21); }
+    if ($line =~ /PC001002=/) { $pc12 = substr ($line, 10, 21); }
+    if ($line =~ /PC002001=/) { $pc21 = substr ($line, 10, 21); }
+    if ($line =~ /PC002002=/) { $pc22 = substr ($line, 10, 21); }
+    if ($line =~ /CDELT1  =/) { $cd1  = substr ($line, 10, 21); }
+    if ($line =~ /CDELT2  =/) { $cd2  = substr ($line, 10, 21); }
+
+    # save these values for the photometry section
+    if ($line =~ /FSATUR  =/) { $fsatur = substr ($line, 10, 21); }
+    if ($line =~ /FLIMIT  =/) { $flimit = substr ($line, 10, 21); }
+    if ($line =~ /EXPTIME =/) { $exptime = substr ($line, 10, 21); }
+
+    # we will not use the FWHM values until those are better defined...
+    if (0) {
+	if ($line =~ /FWHM_X  =/) { 
+	    $tmp = substr ($line, 10, 21); 
+	    $tmp = $tmp * 0.2;
+	    $line = sprintf "FWHM_A  = %20G / stellar profile major axis (arcsec)           \n", $tmp; 
+	    @astrom = (@astrom, $line);
+	}
+	if ($line =~ /FWHM_Y  =/) { 
+	    $tmp = substr ($line, 10, 21); 
+	    $tmp = $tmp * 0.2;
+	    $line = sprintf "FWHM_B  = %20G / stellar profile minor axis (arcsec)           \n", $tmp; 
+	    @astrom = (@astrom, $line);
+	}
+	if ($line =~ /ANGLE   =/) { @astrom = (@astrom, $line);}
+    }
+}
+
+# check for validity of astrometry data
+if ($Nastro == 0) {
+    print STDERR "can't find all astrometry keywords from $cmproot\n";
+    $line = sprintf "COMMENT  Elixir:  no astrometry solution for this image\n"; 
+    @astrom = ($line);
+} else {
+    # convert the pc, cdelt terms to cd terms
+    $cd11 = $cd1*$pc11;
+    $cd12 = $cd2*$pc12;
+    $cd21 = $cd1*$pc21;
+    $cd22 = $cd2*$pc22;
+    $line = sprintf "CD1_1   = %20.5E / WCS Coordinate scale matrix                   \n", $cd11; @astrom = (@astrom, $line);
+    $line = sprintf "CD1_2   = %20.5E / WCS Coordinate scale matrix                   \n", $cd12; @astrom = (@astrom, $line);
+    $line = sprintf "CD2_1   = %20.5E / WCS Coordinate scale matrix                   \n", $cd21; @astrom = (@astrom, $line);
+    $line = sprintf "CD2_2   = %20.5E / WCS Coordinate scale matrix                   \n", $cd22; @astrom = (@astrom, $line);
+}
+
+# define a reference chip to grab the photometry data
+ photometry: 
+     if ($cmproot eq "phu") {
+	 $ccd = "phu";
+     } else {
+	 $ccd = `cameraconfig -ID 0`; chop $ccd;
+	 if ($?) { die ("error in camera config\n"); }
+     }
+
+# grab the appropriate photometry data
+@photom = ();
+@photlist = `photsearch -trans -image $infile $ccd split -equiv`;
+if ($?) { @photlist = (); }
+
+# check validity of photometry data
+$Nphot = @photlist;
+if ($Nphot == 0) {
+    print STDERR "can't get photometry data for this image\n";
+    $line = sprintf "COMMENT  Elixir: No photometry calibrations for this night & filter\n"; 
+    @photom = ($line);
+} else {
+    # parse the photometry data line
+    @words = split (" ", $photlist[$Nphot-1]);
+#   we conclude that the nightly zero point contains insufficient information 
+    $line = sprintf "PHOT_C  = %20.4F / Elixir zero point - measured for camera run \n", $words[2];  @photom = (@photom, $line);
+    $line = sprintf "PHOT_CS = %20.4F / Elixir zero point - scatter                \n", $words[4];  @photom = (@photom, $line);
+    $line = sprintf "PHOT_NS = %20d / Elixir zero point - N stars                  \n", $words[5];  @photom = (@photom, $line);
+    $line = sprintf "PHOT_NM = %20d / Elixir zero point - N images                 \n", $words[6];  @photom = (@photom, $line);
+    $line = sprintf "PHOT_C0 = %20.4F / Elixir zero point - nominal                \n", $words[3];  @photom = (@photom, $line);
+    $line = sprintf "PHOT_X  = %20.4F / Elixir zero point - color term             \n", $words[7];  @photom = (@photom, $line);
+    $line = sprintf "PHOT_K  = %20.4F / Elixir zero point - airmass term           \n", $words[8];  @photom = (@photom, $line);
+    $line = sprintf "PHOT_C1 = '%-18s' / Elixir zero point - color 1               \n", $words[10];  @photom = (@photom, $line);
+    $line = sprintf "PHOT_C2 = '%-18s' / Elixir zero point - color 2               \n", $words[11]; @photom = (@photom, $line);
+    
+    if ($cmproot ne "phu") {
+	# is there was no cmpfile, we should not set the SAT and LIM values.
+	if ($exptime == 0) { 
+	    $line = sprintf "COMMENT   Photometric Analysis is incomplete for this image.\n";
+	    @photom = (@photom, $line);
+	    $line = sprintf "COMMENT   MAG_SAT and MAG_LIM cannot be determined.\n";
+	    @photom = (@photom, $line);
+	} else {
+	    # adjust fsatur & flimit values by phot data
+	    $fsatur = $fsatur - 25.0 + $words[2] + 2.5*log($exptime) / log(10.0);
+	    $line = sprintf "MAG_SAT = %20G / approx saturation (mag)                       \n", $fsatur; 
+	    @photom = (@photom, $line);
+	    
+	    $flimit = $flimit - 25.0 + $words[2] + 2.5*log($exptime) / log(10.0);
+	    $line = sprintf "MAG_LIM = %20G / approx completeness limit (mag)               \n", $flimit; 
+	    @photom = (@photom, $line);
+	}
+    }
+
+    # add some comments 
+    $line = sprintf "COMMENT   Formula for Photometry, based on keywords given in this header:      \n"; @photom = (@photom, $line);
+    $line = sprintf "COMMENT   m = -2.5*log(DN) + 2.5*log(EXPTIME)                                  \n"; @photom = (@photom, $line);
+    $line = sprintf "COMMENT   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)   \n"; @photom = (@photom, $line);
+}
+
+# add generic Elixir comments:
+
+@time = localtime;
+$now = sprintf "%04d-%02d-%02dT%02d:%02d:%02d", $time[5]+1900, $time[4]+1, $time[3], $time[2], $time[1], $time[0];
+$elsys = `gconfig ELIXIR_VERSION`; chop ($elsys);
+# $frpts  = `detsearch -quiet -image $infile $ccds[$ccd] split -type frpts`; chop $frpts;
+
+@comment = ();
+$line = sprintf "COMMENT  --------------- Elixir Analysis ---------------                \n"; @comment = (@comment, $line);
+$line = sprintf "COMMENT  Elixir: processing performed \@ CFHT, version $elsys           \n"; @comment = (@comment, $line);
+$line = sprintf "COMMENT  Elixir: version: $elsys                                        \n"; @comment = (@comment, $line);
+$line = sprintf "COMMENT  Elixir: processing date: $now                                  \n"; @comment = (@comment, $line);
+$line = sprintf "COMMENT  Elixir: website: http://www.cfht.hawaii.edu/Instruments/Elixir \n"; @comment = (@comment, $line);
+$line = sprintf "COMMENT  Elixir: email: elixir\@cfht.hawaii.edu                         \n"; @comment = (@comment, $line);
+$line = sprintf "COMMENT  Elixir: team: Eugene Magnier & Jean-Charles Cuillandre         \n"; @comment = (@comment, $line);
+$line = sprintf "DATEPROC= '$now' / Processing Date                                      \n"; @comment = (@comment, $line);
+$line = sprintf "EL_SYS  = '%-18s' / Elixir System Version                               \n", "$elsys"; @comment = (@comment, $line);
+# (line < 80)    12345678901234567890123456789012345678901234567890123456789012345678901234567890
+
+@output = (@comment, @astrom, @photom);
+$line = sprintf "COMMENT  --------------- Elixir Analysis (end) ---------------         \n"; @output = (@output, $line);
+
+# write output to file
+open (FILE, ">$outfile");
+foreach $line (@output) {
+    print FILE "$line";
+}
+close (FILE);
+print STDERR "wrote $outfile\n";
+
+exit 0;
+
+# given an image directory with a name like /path/output (a SPLIT):
+
+# for each ccd:
+
+# 1) get astrometry
+# 2) 
+
+#ckrunid 570965o/570965o00.fits
+#set dir=`gconfig -q -D RUNID 01Ak01 TMPDIR`
+#$dir/570965o/570965o00.cmp
+
+
+###################################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+
+
+sub junk {
+
+    # create the basic entries for a header
+    $line = sprintf "SIMPLE  =                    T / Standard FITS \n"; @output = (@output, $line);
+    $line = sprintf "BITPIX  =                    8 / no actual data in this file\n"; @output = (@output, $line);
+    $line = sprintf "NAXIS   =                    0 / Number of axes \n"; @output = (@output, $line);                                
+
+
+
+    # fill in the bottom of the header
+    $line = sprintf "END"; @output = (@output, $line);                                
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imclean.cfht
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imclean.cfht	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imclean.cfht	(revision 22322)
@@ -0,0 +1,64 @@
+#!/usr/bin/env perl
+
+$config = "";
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-C") {
+        $config = "-C $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-c") {
+        $config = "-c $ARGV[1]";
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-D") {
+        $config = "-D $ARGV[1] $ARGV[2]";
+        shift; shift; shift; next;
+    }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV < 3) { 
+    print STDERR "ERROR: USAGE: USAGE: imclean.cfht (fits) (obj) (cmp) [option]\n";
+    print STDERR "  corrects for errors with CD1_1, CRPIX1 on 12k for 51400 < MJD < 51540\n";
+    print STDERR "  also determines and inserts the photcode for the image\n";
+    exit 2;
+}
+
+# input image must already be in SPLIT mode
+
+# determine the photcode of this image:
+$photcode=`photcode $config -quiet $ARGV[0] 0 split`; chop $photcode;
+if ($?) { die "ERROR: cannot find photcode\n"; }
+
+# check if we need to correct the WCS terms (only CFH12K)
+($name, $id, $mjd) = split (" ", `echo $ARGV[0] | fields IMAGEID MJD-OBS`);
+
+$fix="";
+if (($mjd > 51400) && ($mjd < 51540) && (($id == 2) || ($id == 6))) {
+    $line = `echo $ARGV[0] | fields CD1_1 CRPIX1 NAXIS1`;
+    ($name, $cd11, $crpix1, $nx) = split (" ", $line, 4);
+    $crpix1 = $nx - $crpix1;
+    $cd11 = -$cd11;
+    $fix = "-key CD1_1 %f $cd11  -key CRPIX1 %f $crpix1";
+}
+
+vsystem ("imclean $config @ARGV -p $photcode $fix");
+
+exit 0;
+
+############
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    die "@_";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imclean.subaru
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imclean.subaru	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imclean.subaru	(revision 22322)
@@ -0,0 +1,52 @@
+#!/usr/bin/env perl
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    print STDERR "ending execution\n";
+    die "@_";
+}
+
+if (@ARGV < 3) { 
+    print STDERR "ERROR: USAGE: USAGE: imclean.cfht (fits) (obj) (cmp) [option]\n";
+    print STDERR "  corrects for errors with CD1_1, CRPIX1 on 12k for 51400 < MJD < 51540\n";
+    print STDERR "  also determines and inserts the photcode for the image\n";
+    exit 2;
+}
+
+# input image must already be in SPLIT mode
+# determine the photcode of this image:
+$photcode=`photcode -quiet $ARGV[0] 0 split`;
+if ($?) {
+    print STDERR "ERROR: cannot find photcode\n";
+    exit 1;
+}
+chop ($photcode);
+
+# check if we need to correct the WCS terms
+$line = `echo $ARGV[0] | fields DET-ID`;
+chop ($line);
+($name, $id) = split (" ", $line);
+
+$cfix = "";
+if ($id == 0) { $cfix = "-key CRPIX1 %f 4000.0 -key CRPIX2 %f  5000.0"; }
+if ($id == 1) { $cfix = "-key CRPIX1 %f 4000.0 -key CRPIX2 %f  3000.0"; }
+if ($id == 2) { $cfix = "-key CRPIX1 %f    0.0 -key CRPIX2 %f  1000.0"; }
+if ($id == 3) { $cfix = "-key CRPIX1 %f    0.0 -key CRPIX2 %f -1000.0"; }
+if ($id == 4) { $cfix = "-key CRPIX1 %f 4000.0 -key CRPIX2 %f -1000.0"; }
+if ($id == 5) { $cfix = "-key CRPIX1 %f 4000.0 -key CRPIX2 %f  1000.0"; }
+if ($id == 6) { $cfix = "-key CRPIX1 %f    0.0 -key CRPIX2 %f  5000.0"; }
+if ($id == 7) { $cfix = "-key CRPIX1 %f    0.0 -key CRPIX2 %f  3000.0"; }
+if ($id == 8) { $cfix = "-key CRPIX1 %f    0.0 -key CRPIX2 %f -3000.0"; }
+if ($id == 9) { $cfix = "-key CRPIX1 %f 4000.0 -key CRPIX2 %f -3000.0"; }
+
+$fix = "-key PC001001 %f 1.0 -key PC001002 %f 0.0 -key PC002001 %f 0.0 -key PC002002 %f 1.0";
+
+vsystem ("imclean @ARGV -p $photcode $fix $cfix");
+
+exit 0;
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imstatqso
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imstatqso	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imstatqso	(revision 22322)
@@ -0,0 +1,87 @@
+#!/usr/bin/env perl
+
+# send sky & seeing to QSO db
+# probably need some error checking on the 
+#  system call functions (seeingstats & fields)
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+if (@ARGV != 3) { die "USAGE: imstatqso (name) (stats) (sdat)\n"; }
+
+$ccdkeyword = `gconfig CCDNUM-KEYWORD`; chop ($ccdkeyword);
+if ($?) { die "ERROR: missing CCDNUM-KEYWORD in config system\n"; } 
+
+$refccd = `gconfig SEEING_REF_CCD`; chop ($refccd);
+if ($?) { die "ERROR: missing SEEING_REF_CCD in config system\n"; } 
+
+$pixscale = `gconfig ASEC_PIX`; chop ($pixscale);
+if ($?) { die "ERROR: missing ASEC_PIX in config system\n"; } 
+
+$line = `echo $ARGV[0] | fields $ccdkeyword`;
+($tmp, $ccd) = split (" ", $line);
+
+if ($ccd =~ m|amp|) { 
+    # convert to equivalent ccd
+    ($n) = $ccd =~ m|amp(\d*)|;
+    $ccd = sprintf "ccd%02d", $n/2;
+}
+
+if ($ccd ne $refccd) {
+    print STDOUT "SUCCESS: skipping ccd $ccd\n";
+    exit 0;
+}
+
+$line = `echo $ARGV[0] | fields OBSID`;
+($tmp, $obs) = split (" ", $line);
+$obs = substr ($obs, 0, 6);
+
+$line = `echo $ARGV[0] | fields OBSTYPE`;
+($tmp, $type) = split (" ", $line);
+$type = uc ($type);
+
+$line = `echo $ARGV[0] | fields EXPTIME`;
+($tmp, $etime) = split (" ", $line);
+
+open (FILE, "$ARGV[1]");
+@lines = <FILE>;
+close (FILE);
+@words = split (" ", $lines[0]);
+$skybg = $words[0];
+$bias = $words[1];
+
+if ($etime == 0) {
+    $flux = -1000;
+} else {
+    $flux = $skybg / $etime;
+}
+
+$fwhm = `seeingstats $ARGV[2]`; 
+chop ($fwhm);
+$fwhm = $fwhm * $pixscale;
+
+# if this is a BIAS, send just the bias for skybg
+if ($type eq "BIAS") {
+    vsystem ("/cfht/bin/update_xexpe.sh -U qso_elixir -P op1eliw --obsid $obs --skybg $bias");
+    print STDOUT "SUCCESS: done with $ARGV[0]\n";
+    exit (0);
+}
+
+# if this is an OBJECT, send both fwhm & flux
+if ($type eq "OBJECT") {
+    vsystem ("/cfht/bin/update_xexpe.sh -U qso_elixir -P op1eliw --obsid $obs --skybg $flux --iq $fwhm");
+    print STDOUT "SUCCESS: done with $ARGV[0]\n";
+    exit (0);
+}
+
+# otherwise, just send flux
+vsystem ("/cfht/bin/update_xexpe.sh -U qso_elixir -P op1eliw --obsid $obs --skybg $flux");
+print STDOUT "SUCCESS: done with $ARGV[0]\n";
+exit (0);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imstats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imstats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imstats	(revision 22322)
@@ -0,0 +1,63 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 5) { die "USAGE: imstats (file.fits) (file.stats) (file.split) CCD MODE\n" }
+
+$file = $ARGV[0];
+$stats = $ARGV[1];
+$split = $ARGV[2];
+$ccd = $ARGV[3];
+$mode = $ARGV[4];
+
+# check for outdir:
+@words = split ("/", $stats);
+pop (@words);
+$outdir = join ("/", @words);
+if ($outdir eq "") { $outdir = "."; }
+if (! -d $outdir) {
+    vsystem ("mkdir -p $outdir");
+    if ($?) {
+	print STDERR "ERROR: can't make directory component for output $outdir\n";
+	exit 1;
+    }
+}
+
+if (-e $stats) { unlink ($stats); }
+if (-e $split) { unlink ($split); }
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/imstats.pro";
+
+$ccdkeyword = `gconfig CCDNUM-KEYWORD`; chop ($ccdkeyword);
+
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+
+print MANA "\$CCDKEYWORD = $ccdkeyword\n";
+print MANA "macro go\n";
+print MANA " getstats $file $ccd $mode $stats $split\n";            # load images, create mosaic
+print MANA " exit 0\n";
+print MANA "end\n";
+
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) {
+    print STDERR "ERROR problem with imstats\n";
+    exit 1;
+}
+
+print STDOUT "SUCCESS: finished with imstats\n";
+
+exit 0;
+    
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imstats.mana
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imstats.mana	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/imstats.mana	(revision 22322)
@@ -0,0 +1,203 @@
+#!/usr/bin/env mana
+
+macro getstats 
+ # usage getstats (filename) (ccd) (mef) (stats) (split)
+
+ # load image into 'b'
+ if (($3 == mef) || ($3 == MEF))
+  rd b $1 -n $2
+ else
+  rd b $1
+ end
+
+ # raster goes in buffer 'a'
+ # $flux has sky + bias 
+ getraster
+
+ # result of this is $bias
+ getbiassec b $2
+
+ mkoutput $4 $5
+end
+
+# extract subraster from image center 0.1-0.9*NAXIS1 (max 800,800)
+macro getraster
+ keyword b NAXIS1 nx
+ keyword b NAXIS2 ny
+
+# $DX = $nx * 0.8 
+# if ($DX > $dx) 
+#   $DX = $dx
+# end
+# $DY = $ny * 0.8
+# if ($DY > $dy) 
+#   $DY = $dy
+# end
+#
+# $X0 = 0.5*($nx - $DX)
+# $Y0 = 0.5*($ny - $DX)
+#
+# extract b a $X0 $Y0 $DX $DY 0 0 $DX $DY
+
+ if ($nx < (2*$dx))
+   $dx = 0.5 * $nx
+ end
+ if ($ny < (2*$dy))
+   $dy = 0.5 * $ny
+ end
+ echo extract a b {0.5*$nx - $dx} {0.5*$ny - $dy} {2*$dx} {2*$dy} 0 0 {2*$dx} {2*$dy}
+ extract b a {0.5*$nx - $dx} {0.5*$ny - $dy} {2*$dx} {2*$dy} 0 0 {2*$dx} {2*$dy}
+
+ stats -q a - - - -
+ $flux = $MEDIAN
+end
+
+# usage: mkoutput (stats) (split)
+macro mkoutput 
+ $sky = $flux - $bias
+ output $1
+ echo $sky $bias
+ output stdout
+ wd a $2 
+end
+
+# get bias from BIASSEC (header or config)
+macro getbiassec 
+ if ($BIASSEC) 
+  echo "using header biassec"
+  keyword $1 BIASSEC biassec
+ else 
+  # this line converts 2 digit zero-padded number to single
+  echo "using config biassec"
+  $ccd = $2
+  $biassec = 0
+  for n 0 $biassec:n
+    if ($ccdlist:$n == $ccd) 
+      $biassec = $biassec:$n
+    end
+  end
+  if ($biassec == 0)
+    echo "biassec for chip $ccd not found"
+    exit 1
+  end
+ end
+ parsesec $biassec
+ stats -q $1 $x1 $y1 {$x2-$x1} {$y2-$y1}
+ $bias = $MEDIAN
+end
+
+# convert BIASSEC string to x1, x2, y1, y2 vars
+macro parsesec
+  $f = $1
+  getchr $f : n
+  substr $f 1 {$n-1} x1
+  strlen $f N
+  substr $f $n {$N-$n} f
+  
+  getchr $f , n
+  substr $f 1 {$n-1} x2
+  strlen $f N
+  substr $f $n {$N-$n} f
+  
+  getchr $f : n
+  substr $f 1 {$n-1} y1
+  strlen $f N
+  substr $f $n {$N-$n} f
+  
+  getchr $f ] n
+  substr $f 1 {$n-1} y2
+  strlen $f N
+  substr $f $n {$N-$n} f
+end
+
+macro vysub
+ if ($0 != 2)
+  echo "USAGE vysub (buffer)"
+  break
+ end
+
+ keyword $1 NAXIS1 nx
+ keyword $1 NAXIS2 ny
+
+ create dy 0 $ny
+ create dx 0 $ny
+ for ty 0 $ny
+  cut $1 x y x 0 $ty $nx 1
+  vstat -q y
+  dy[$ty] = $MEDIAN
+ end
+
+ # this subtraction is not at all obvious
+ set $1 = $1 - dy
+end
+
+macro vxsub
+ if ($0 != 2)
+  echo "USAGE vxsub (buffer)"
+  break
+ end
+
+ keyword $1 NAXIS1 nx
+ keyword $1 NAXIS2 ny
+
+ create dy 0 $nx
+ create dx 0 $nx
+ for tx 0 $nx
+  cut $1 x y y $tx 0 1 $ny
+  vstat -q y
+  dy[$tx] = $MEDIAN
+ end
+
+ # this subtraction is not at all obvious
+ set $1 = -(dy - $1)
+end
+
+macro irstats
+ $plane = $2 + 1
+ rd a $1 -plane $plane
+ vysub a
+ vstat -q dy
+ $sky = $MEDIAN
+ vxsub a
+ set a = a / $sky
+ wd a $3 -bitpix 16 -bzero 0 -bscale {1/$sky}
+ exec sexstats $3 $4
+ $fwhm = `seeingstats $4`
+ exec echo "$2 $sky $fwhm >> $5"
+ echo "$2 $sky $fwhm to $5"
+end
+
+unsign 0
+$dx = 800
+$dy = 800
+
+$CCDKEYWORD = `gconfig CCDNUM-KEYWORD`
+
+list biassec -x "cameraconfig -biassec"
+$BIASSEC = `cameraconfig -usebiassec`
+list ccdlist -x "cameraconfig -ccds"
+
+# if we can use BIASSEC, use it - BIASSEC changes with rastered, binned images
+# if not, use xbias, ybias lists to extract bias
+
+if ($argv:n != 5) 
+  echo "USAGE: imstats (file.fits) (file.stats) (file.split) CCD MODE"
+  exit 1
+end
+
+break -auto off
+$path = `dirname $argv:1`
+exec test -d $path
+if ($STATUS == 0)
+  exec mkdir -p $path
+  if ($STATUS == 0)
+    echo "ERROR: can't make directory"
+    exit 1
+  end
+end
+
+echo exec rm -f $argv:1
+echo exec rm -f $argv:2
+
+getstats $argv:0 $argv:3 $argv:4 $argv:1 $argv:2
+exit 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/irstats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/irstats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/irstats	(revision 22322)
@@ -0,0 +1,111 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 2) { die "USAGE: imstats (file.fits) (file.stats)\n" }
+
+$file = $ARGV[0];
+$stats = $ARGV[1];
+@medlist = ();
+$cube = 1;
+
+# check for outdir:
+@words = split ("/", $stats);
+pop (@words);
+$outdir = join ("/", @words);
+if ($outdir eq "") { $outdir = "."; }
+if (! -d $outdir) {
+    system ("mkdir -p $outdir");
+    if ($?) { &escape ("ERROR: can't make directory component for output $outdir"); }
+}
+
+if (-e $stats) { unlink ($stats); }
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/imstats.pro";
+
+# check that image is not MEF?
+$answer = `echo $file | fields NAXIS3`;
+($tmp, $Nplane) = split (" ", $answer);
+if ($Nplane == 0) { 
+    $Nplane = 1; 
+    $cube = 0;
+}
+
+$temp = `mktemp /tmp/imstats.XXXXXX`; chop ($temp);
+
+# loop over cube planes:
+open (MANA, "|mana --norc > /dev/null");
+print MANA "input $script\n";
+
+print MANA "macro go\n";
+for ($plane = 0; $plane < $Nplane; $plane++) {
+    print MANA " irstats $file $plane $temp.$plane.fits $temp.stats $stats\n";  
+    @medlist = (@medlist, "$temp.$plane.fits");
+}
+print MANA " exit 0\n";
+print MANA "end\n";
+
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) { &escape ("ERROR problem with imstats"); }
+
+if (@medlist > 1) {
+    open (MED, "|medianfilter $temp.fits 0.25 0.75");
+    foreach $file (@medlist) {
+	print MED "$file\n";
+    }
+    close (MED);
+    if ($?) { &escape ("ERROR problem with medianfilter"); }
+
+    system ("sexstats $temp.fits $temp.stats > /dev/null");
+    if ($?) { &escape ("ERROR problem with sexstats"); }
+} else {
+    system ("sexstats $temp.0.fits $temp.stats > /dev/null");
+    if ($?) { &escape ("ERROR problem with sexstats"); }
+}    
+
+if ($cube) {
+    $fwhm = `seeingstats $temp.stats`; chop ($fwhm);
+    if ($?) { &escape ("ERROR problem with sexstats"); }
+    
+    open (MED, "$stats");
+    @list = <MED>;
+    close (MED);
+    
+    $sky = 0;
+    foreach $line (@list) {
+	($tmp, $isky, $tmp) = split (" ", $line);
+	$sky += $isky;
+    }
+    $sky = $sky / @list;
+    
+    open (MED, ">>$stats");
+    print MED "$Nplane $sky $fwhm\n";
+    close (MED);
+}
+
+&cleanup;
+print STDOUT "SUCCESS: finished with imstats\n";
+
+exit 0;
+    
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub escape {
+    &cleanup;
+    die "@_\n";
+}
+    
+sub cleanup { 
+    if (-e "$temp")       { unlink ("$temp"); }
+    if (-e "$temp.stats") { unlink ("$temp.stats"); }
+    if (-e "$temp.fits")  { unlink ("$temp.fits"); }
+    foreach $file (@medlist) {
+	if (-e $file) { unlink $file; }
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mefheadunstrip
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mefheadunstrip	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mefheadunstrip	(revision 22322)
@@ -0,0 +1,88 @@
+#!/usr/bin/env perl
+
+$xtline = "XTENSION= 'IMAGE   '           / Image extension ";
+$rwline = "SIMPLE  =                    T / Standard FITS   ";
+
+# input file is PHU: foo.fits.hdr
+# expect NEXTEND files: fooNN.fits.hdr
+
+if (@ARGV != 2) { die "ERROR: USAGE: mefheadunstrip (input) (output)\n" }
+$input  = $ARGV[0];
+$output = $ARGV[1];
+($root) = $input =~ m|(\S*).fits.hdr|;
+
+# blank line for padding:
+$blank = "";
+for ($i = 0; $i < 80; $i++) { $blank = $blank . " "; }
+
+if (-e $output) { die "remove existing target\n"; }
+
+open (OUT, ">$output");
+
+# load primary header
+@head = &loadheader ($input);
+foreach $line (@head) {
+    print OUT "$line";
+}
+
+# find N extensions
+$Nextend = 0;
+foreach $line (@head) {
+    if ($line =~ m|^NEXTEND =\s+\d+|) {
+	($Nextend) = $line =~ m|^NEXTEND =\s+(\d+)|;
+	last;
+    }
+}
+if (!$Nextend) { die "ERROR: no extensions\n"; }
+
+# load extension headers
+for ($i = 0; $i < $Nextend; $i++) {
+    $file = sprintf "%s%02d.fits.hdr", $root, $i;
+    print STDERR "$file\n";
+    
+    @head = &loadheader ($file);
+    foreach $line (@head) {
+	if ($line =~ s|^$rwline|$xtline|) {
+	    print STDERR "fixed SIMPLE\n";
+	}
+    }
+    foreach $line (@head) {
+	print OUT "$line";
+    }
+}
+
+close (OUT);
+exit 0;
+
+# load, strip, pad, fix NAXIS:
+sub loadheader {
+
+    my ($file) = $_[0];
+    my (@input, $i);
+
+    open (FILE, "$file");
+    @input = <FILE>;
+    close (FILE);
+    
+    # strip return chars
+    foreach $line (@input) { chop $line; }
+
+    # pad end of block
+    $N = @input;
+    if ($N % 36) {
+	$Nxtra = 36 - ($N % 36);
+	for ($i = 0; $i < $Nxtra; $i++) {
+	    @input = (@input, $blank);
+	}
+    }
+
+    # NAXIS = 2 -> NAXIS = 0
+    foreach $line (@input) {
+	if ($line =~ s|^NAXIS   =                    2|NAXIS   =                    0|) {
+	    print STDERR "fixed NAXIS\n";
+	}
+    }
+    
+    return (@input);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/megacenter
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/megacenter	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/megacenter	(revision 22322)
@@ -0,0 +1,91 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 1) { die "USAGE: megacenter (filename)\n"; }
+$ENV{'PATH'} = "/apps/elixir/bin:$ENV{'PATH'}";
+
+# currently we keep things hardwired:
+
+$input = $ARGV[0];
+$camera = `gconfig CAMERA`; chop $camera;
+if ($camera eq "megacam") {
+    $ccd   = "amp44";
+    $Xref  = 973;
+    $Yref  = 4560;
+}
+if ($camera eq "meganorth") {
+    $ccd   = "amp26";
+    $Xref  = 1140;
+    $Yref  = 4717;
+}
+if ($ccd eq "") { die "ERROR: camera $camera not found\n"; }
+
+$temp = `mktemp /tmp/center.XXXXXX`; chop ($temp);
+
+# loop over cube planes:
+open (MANA, "|mana --norc >& /dev/null");
+
+print MANA "\$CCDKEYWORD = EXTNAME\n";
+print MANA "macro go\n";
+print MANA " rd a $input -n $ccd\n";
+print MANA " wd a $temp.fits\n";
+print MANA " exec gosexphot $temp.fits $temp.sx >& /dev/null\n";
+print MANA " exec imclean -sex $temp.fits $temp.sx $temp.smp >& /dev/null\n";
+print MANA " exec gastro $temp.smp >& /dev/null\n";
+print MANA " header a -w $temp.smp\n";
+print MANA " coords a -p $Xref $Yref\n";
+print MANA " exec echo \$RA \$DEC > $temp.coords\n";
+print MANA " exit 0\n";
+print MANA "end\n";
+
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+
+($Robs, $Dobs) = split (" ", `cat $temp.coords`);
+($tmp, $Rreq, $Dreq) = split (" ", `echo $input | fields RA_DEG DEC_DEG`);
+
+($tmp, $Nastro, $Cerror) = split (" ", `echo $temp.smp | fields NASTRO CERROR`);
+
+if ($Robs < 0.0)   { $Robs += 360.0 }
+if ($Robs > 360.0) { $Robs -= 360.0 }
+
+if ($Rreq < 0.0)   { $Rreq += 360.0 }
+if ($Rreq > 360.0) { $Rreq -= 360.0 }
+
+$dR = sprintf "%.2f", 3600.0 * ($Rreq - $Robs) * cos ($Dobs*3.141592643589/180.0);
+$dD = sprintf "%.2f", 3600.0 * ($Dreq - $Dobs);
+
+$obscoords = `echo $Rreq $Dreq | radec -hh`; chop $obscoords;
+$reqcoords = `echo $Robs $Dobs | radec -hh`; chop $reqcoords;
+
+print "\n";
+printf STDOUT "$Nastro stars used in astrometry, astrometry error: %.2f\n", $Cerror;
+
+printf STDOUT "REQ COORDS: %10.6f %10.6f ($reqcoords )\n", $Rreq, $Dreq;
+printf STDOUT "OBS COORDS: %10.6f %10.6f ($obscoords )\n", $Robs, $Dobs;
+printf STDOUT "OFFSET IS: $dR $dD (arcsec)\n";
+
+if ($Nastro == 0) { 
+    print "No Astrometry solution was found! please try again\n";
+    goto escape;
+}
+
+if ($Cerror > 2.5) { print "WARNING: astrometry error is surprisingly high\n"; }
+if ($dR > 3600.0)  { print "WARNING: RA offset > 1 degree! is this correct?\n"; }
+if ($dD > 3600.0)  { print "WARNING: DEC offset > 1 degree! is this correct?\n"; }
+
+print STDOUT "apply this offset? y/[n] ";
+$answer = <STDIN>; chop $answer;
+$answer = "\U$answer\E";
+
+if ($answer ne "Y") { 
+    print STDOUT "offset not applied\n";
+    goto escape;
+}
+
+system ("clicmd ocoords rel t $dR $dD\n");
+system ("clicmd tcs.offset\n");
+
+escape:
+system ("rm -f $temp $temp.fits $temp.sx $temp.smp $temp.coords");
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/megacoords
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/megacoords	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/megacoords	(revision 22322)
@@ -0,0 +1,58 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 2) { die "USAGE: megacoords (mode) (filename)\n"; }
+$ENV{'PATH'} = "$ENV{'PATH'}:/apps/elixir/bin";
+
+# currently we keep things hardwired:
+
+$mode = $ARGV[0];
+$input = $ARGV[1];
+
+if ($mode eq "chip") { $ccd   = "ccd22"; }
+if ($mode eq "amp")  { $ccd   = "amp44"; }
+$Xref  = 973;
+$Yref  = 4560;
+
+if ($ccd eq "") { die "ERROR: camera $camera not found\n"; }
+
+$temp = `mktemp /tmp/center.XXXXXX`; chop ($temp);
+
+# mana processing of image
+open (MANA, "|mana --norc >& /dev/null");
+
+print MANA "\$CCDKEYWORD = EXTNAME\n";
+print MANA "macro go\n";
+print MANA " rd a $input -n $ccd\n";
+print MANA " wd a $temp.fits\n";
+print MANA " exec gosexphot $temp.fits $temp.sx >& /dev/null\n";
+print MANA " exec imclean -sex $temp.fits $temp.sx $temp.smp >& /dev/null\n";
+print MANA " exec gastro $temp.smp >& /dev/null\n";
+print MANA " header a -w $temp.smp\n";
+print MANA " coords a -p $Xref $Yref\n";
+print MANA " exec echo \$RA \$DEC > $temp.coords\n";
+print MANA " exit 0\n";
+print MANA "end\n";
+
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+
+($Robs, $Dobs) = split (" ", `cat $temp.coords`);
+($tmp, $Rreq, $Dreq) = split (" ", `echo $input | fields RA_DEG DEC_DEG`);
+
+($tmp, $obsid, $Nastro, $Cerror) = split (" ", `echo $temp.smp | fields OBSID NASTRO CERROR`);
+
+if ($Robs < 0.0)   { $Robs += 360.0 }
+if ($Robs > 360.0) { $Robs -= 360.0 }
+
+if ($Rreq < 0.0)   { $Rreq += 360.0 }
+if ($Rreq > 360.0) { $Rreq -= 360.0 }
+
+$dR = 3600.0 * ($Rreq - $Robs) * cos ($Dobs*3.141592643589/180.0);
+$dD = 3600.0 * ($Dreq - $Dobs);
+
+printf "$obsid %10.6f %10.6f  %10.6f %10.6f  %6.1f %6.1f  %3d %4.2f\n", $Rreq, $Dreq, $Robs, $Dobs, $dR, $dD, $Nastro, $Cerror;
+
+escape:
+system ("rm -f $temp $temp.fits $temp.sx $temp.smp $temp.coords");
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/merge.lists
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/merge.lists	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/merge.lists	(revision 22322)
@@ -0,0 +1,101 @@
+#!/usr/bin/env perl
+
+$update = 0;
+
+# grab the command line arguments:
+@tARGV = ();
+for (; @ARGV > 0; ) {
+
+    # new images get mode of 0, old image keep their mode
+    if ($ARGV[0] eq "-update") {
+	shift;
+	$update = 1;
+	next;
+    }
+    
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 3) { die "USAGE: merge.lists (list.split) (list.msplit) (list.master) [-update]\n" ;}
+
+print STDERR "$ARGV[0], $ARGV[1], $ARGV[2]\n";
+
+# load names from SPLIT and MEF
+open (FILE, "$ARGV[0]");
+@split = <FILE>;
+close (FILE);
+
+open (FILE, "$ARGV[1]");
+@mef = <FILE>;
+close (FILE);
+
+# load old names from MASTER
+open (FILE, "$ARGV[2]");
+@master = <FILE>;
+close (FILE);
+
+$N = @split;
+print STDERR "N: $N\n";
+$N = @mef;
+print STDERR "N: $N\n";
+$N = @master;
+print STDERR "N: $N\n";
+
+# merge new entries into a single list, compare with state in master
+@names = (@split, @mef);
+
+@newname = ();
+@newmode = ();
+
+for ($i = 0; $i < @names; $i++) {
+
+    $name = $names[$i]; chop ($name);
+
+    $mode = 1;
+    if ($update) {
+	$mode = 0;
+
+      INNER:
+	foreach $line (@master) {
+	    @words = split (" ", $line);
+	    if ($words[0] eq $name) {
+		$mode = $words[1];
+		last INNER;
+	    }
+	}
+    }
+    @newname = (@newname, $name);
+    @newmode = (@newmode, $mode);
+}
+
+if (! @newname) {
+    print STDERR "ERROR: no relevant ccd images\n";
+    exit 1;
+}
+    
+# check on list: if there are none selected, select all
+$Ngood = 0;
+foreach $mode (@newmode) {
+    if ($mode) {
+	$Ngood ++;
+    }
+}
+if ($Ngood == 0) {
+    foreach $mode (@newmode) {
+	$mode = 1;
+    }
+}
+
+open (FILE, ">$ARGV[2]");
+for ($i = 0; $i < @newname; $i++) {
+    print FILE "$newname[$i] $newmode[$i]\n";
+}
+close (FILE);
+
+# this file must be accessible by www user  
+chmod 0666, $ARGV[2];
+
+print STDOUT "SUCCESS\n";
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkdetrend
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkdetrend	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkdetrend	(revision 22322)
@@ -0,0 +1,2486 @@
+#!/usr/bin/perl
+# this function is called from the detrend apache user: 
+# it must be explicit about the location of perl
+
+$MKDETREND = "mkdetrend";
+
+# strip off args related to the elixir config system, set env PTOLEMY
+@ARGV = elixir_config (@ARGV);
+
+# interpret command-line arguments
+if (@ARGV < 1) {
+    print STDOUT "USAGE: $MKDETREND (mode) [arguments]\n";
+    print STDOUT "mode = help gives a complete list\n";
+    &escape;
+}
+if ($ARGV[0] eq "help") { &usage; }
+
+# main user level commands
+if ($ARGV[0] eq "create")       { &mk_create;     }
+if ($ARGV[0] eq "config")       { &mk_config;     }
+if ($ARGV[0] eq "run")          { &mk_run;        }
+if ($ARGV[0] eq "state")        { &mk_state;      }
+if ($ARGV[0] eq "list.runs")    { &mk_list_runs;  }
+if ($ARGV[0] eq "clean")        { &mk_clean;    }
+if ($ARGV[0] eq "reset")        { &mk_reset;      }
+if ($ARGV[0] eq "init")         { &mk_init;       }
+if ($ARGV[0] eq "update")       { &mk_update;     }
+if ($ARGV[0] eq "auto")         { &mk_auto;       }
+# lower level user comands
+if ($ARGV[0] eq "dup")          { &mk_dup;        }
+if ($ARGV[0] eq "del")          { &mk_del;        }
+if ($ARGV[0] eq "def")          { &mk_def;        }
+if ($ARGV[0] eq "set")          { &mk_set;        }
+if ($ARGV[0] eq "list.init")    { &mk_list_init;  }
+if ($ARGV[0] eq "split")        { &mk_split;      }
+if ($ARGV[0] eq "flips")        { &mk_flips;      }
+if ($ARGV[0] eq "norm")         { &mk_norm;       }
+if ($ARGV[0] eq "reg")          { &mk_reg;        }
+if ($ARGV[0] eq "scat")         { &mk_scat;       }
+if ($ARGV[0] eq "merge")        { &mk_merge;      }
+if ($ARGV[0] eq "update.list")  { &mk_update_list;     }
+if ($ARGV[0] eq "update.stats") { &mk_update_stats;     }
+# HTML cgibin commands
+if ($ARGV[0] eq "htmldup")      { &mk_htmldup;    }
+if ($ARGV[0] eq "htmldef")      { &mk_htmldef;    }
+if ($ARGV[0] eq "htmldel")      { &mk_htmldel;    }
+if ($ARGV[0] eq "htmlmod")      { &mk_htmlmod;    }
+if ($ARGV[0] eq "htmlkeep")     { &mk_htmlkeep;   }
+if ($ARGV[0] eq "htmlconfig")   { &mk_htmlconfig; }
+if ($ARGV[0] eq "html1")        { &mk_html1;      }
+if ($ARGV[0] eq "html2")        { &mk_html2;      }
+if ($ARGV[0] eq "html3")        { &mk_html3;      }
+# other support commands
+if ($ARGV[0] eq "dads")         { &mk_dads;       }
+if ($ARGV[0] eq "dads.top")     { &mk_dads_top;   }
+if ($ARGV[0] eq "meval")        { &mk_meval;      }
+if ($ARGV[0] eq "eval")         { &mk_eval;       }
+if ($ARGV[0] eq "fix.masters")  { &mk_fixmasters;  }
+if ($ARGV[0] eq "check.splits") { &mk_checksplits; }
+
+if ($ARGV[0] eq "test")         { &mk_test;       }
+
+&escape ("invalid $MKDETREND command $ARGV[0]");
+
+######## top level functions ######################
+
+sub mk_test {
+
+    print "Content-type: text/plain\n\n";
+
+    print "$MKDETREND\n";
+    exit 0;
+}
+
+############# define run ##############
+sub mk_create {
+    if ($ARGV[1] eq "run") { &mk_createrun; }
+    if (@ARGV != 5) { &escape ("USAGE: $MKDETREND create (camera) (run) (startdate) (stopdate)"); }
+    
+    $camera = $ARGV[1];
+    $run    = $ARGV[2];
+    $start  = $ARGV[3];
+    $stop   = $ARGV[4];
+
+    # create should NOT set the mkdetrend defaults 
+    if (0) {
+	# set current run id:
+	system ("mkrun sys camera.mkdetrend $camera");
+	if ($?) { &escape ("can't define camera: error in mkrun"); }
+	
+	# set current camera:
+	system ("mkrun sys runid.mkdetrend $run");
+	if ($?) { &escape ("can't define run: error in mkrun"); }
+    }
+
+    # reset values for the directories, etc.
+    $oldenv = $tmpenv;
+    &elixir_config (split (" ", "-run $run -camera $camera"));
+    unlink ($oldenv);
+
+    # create necessary directories, empty them if they exist
+    foreach $name ("detdir", "html", "stats", "flips") {
+	$file = mkfiles ($name);
+	if (! -d $file) {
+	    if (! mkdir ($file, 0777)) {
+		&escape ("can't create detdir $file\n");
+	    }
+	    chmod 0775, $file;
+	}
+    }
+    &save_dates ($start, $stop, "init");
+
+    # create run-specific config file
+    @list = ();
+    for ($i = 0; $i < @filt; $i++) {
+	$line = stconfig ("$run $type[$i] $filt[$i] 0 $start $stop init", "all");
+	@list = (@list, $line);
+    }
+    save_config (@list);
+    
+    # make the necessary directories?
+    &goodbye;
+}
+
+############# define run ##############
+sub mk_createrun {
+    if (@ARGV != 3) { &escape ("USAGE: $MKDETREND create run (run)"); }
+    if ($ARGV[1] ne "run") { &escape ("USAGE: $MKDETREND create run (run)"); }
+    
+    $run    = $ARGV[2];
+    $answer = `mkrun run $run`;
+    if ($?) { &escape ("run $run not defined, use 'mkrun'"); }
+    ($newrun, $start, $stop, $camera) = split (" ", $answer);
+    if ($run ne $newrun) { &escape ("inconsistent answer from mkrun run $run"); }
+
+    # reset values for the directories, etc.
+    $oldenv = $tmpenv;
+    &elixir_config (split (" ", "-run $run -camera $camera"));
+    unlink ($oldenv);
+
+    # create necessary directories, empty them if they exist
+    foreach $name ("detdir", "html", "stats", "flips") {
+	$file = mkfiles ($name);
+	if (! -d $file) {
+	    if (! mkdir ($file, 0777)) {
+		&escape ("can't create detdir $file\n");
+	    }
+	    chmod 0775, $file;
+	}
+    }
+    &save_dates ($start, $stop, "init");
+
+    # create run-specific config file
+    @list = ();
+    for ($i = 0; $i < @filt; $i++) {
+	$line = stconfig ("$run $type[$i] $filt[$i] 0 $start $stop init", "all");
+	@list = (@list, $line);
+    }
+    save_config (@list);
+    
+    # make the necessary directories?
+    &goodbye;
+}
+
+############# change current run ##############
+sub mk_config {
+    if (@ARGV != 3) { &escape ("USAGE: $MKDETREND config (camera) (run)"); }
+    
+    $camera = $ARGV[1];
+    $run    = $ARGV[2];
+
+    # set current run id:
+    system ("mkrun sys camera.mkdetrend $camera");
+    if ($?) { &escape ("can't define camera: error in mkrun"); }
+    
+    # set current run id:
+    system ("mkrun sys runid.mkdetrend $run");
+    if ($?) { &escape ("can't define run: error in mkrun"); }
+    
+    &goodbye;
+}
+
+############# auto run system ##############
+sub mk_auto {
+    my ($start, $stop, $mode);
+
+    if ((@ARGV != 2) && (@ARGV != 3)) { &escape ("USAGE: $MKDETREND auto [command]"); }
+    $command = $ARGV[1];
+
+    ($start, $stop, $mode) = load_dates ();
+    if ($command eq "set") {
+	if (@ARGV != 3) { &escape ("USAGE: $MKDETREND auto set (mode)"); }
+	&save_dates ($start, $stop, $ARGV[2]);
+	&goodbye;
+    }
+    
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND auto [command]"); }
+    if ($command eq "show") {
+	print STDERR "MODE : $mode\n";
+	&goodbye;
+    }
+    
+    if ($command eq "run") {
+	if ($mode eq "init") {
+	    system ("$MKDETREND reset hard");
+	    system ("$MKDETREND init");
+	    system ("$MKDETREND run");
+	    &save_dates ($start, $stop, "init");
+	    &goodbye;
+	}
+	
+	if ($mode eq "update") {
+	    system ("$MKDETREND update");
+	    &save_dates ($start, $stop, "update");
+	    &goodbye;
+	}
+	
+	if ($mode eq "hold") {
+	    &goodbye;
+	}
+    }
+    &escape ("$MKDETREND auto $command : command not found\n");
+}
+
+############# main run ##############
+sub mk_run {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND run"); }
+    
+    system ("$MKDETREND flips");
+    system ("$MKDETREND norm");
+    system ("$MKDETREND merge");
+    
+    &goodbye;
+}
+
+############# main init ##############
+sub mk_init {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND init"); }
+    
+    system ("$MKDETREND list.init");
+    system ("$MKDETREND split");
+    
+    &goodbye;
+}
+
+############# main update ##############
+sub mk_update {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND update"); }
+    
+    system ("$MKDETREND reset update");
+    system ("$MKDETREND init");
+    system ("$MKDETREND update.list");
+    system ("$MKDETREND update.stats");
+    system ("$MKDETREND run");
+    
+    &goodbye;
+}
+
+############# show current state ##############
+sub mk_state {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND state"); }
+    
+    $file = mkfiles ("dates");
+    if (! -e $file) { 
+	print STDOUT "CAMERA    $camera\n";
+	print STDOUT "RUN       $run\n";
+	&escape ("setup not defined");
+    }
+    ($start, $stop, $mode) = load_dates ();
+
+    print STDOUT "CAMERA    $camera\n";
+    print STDOUT "RUN       $run\n";
+    print STDOUT "START     $start\n";
+    print STDOUT "STOP      $stop\n";
+    print STDOUT "MODE      $mode\n";
+    
+    @list = &load_config;
+    if (@list == 0) { &escape ("$MKDETREND not configured"); }
+
+    for ($i = 0; $i < @list; $i++) {
+	printf STDOUT "%3d %s\n", $i, $list[$i];
+    }
+    &goodbye;
+}
+
+############# show current state ##############
+sub mk_list_runs {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND list.runs"); }
+    
+    system ("ls $root");
+    &goodbye;
+}
+
+############## duplicate config ###############
+sub mk_dup {
+    if (@ARGV != 2) { &escape ("USAGE: detrend dup (config)"); }
+    
+    $key = $ARGV[1];
+    @list = load_config ();
+    
+    # look for entry that matches requested config
+    @match = ();
+    for ($i = 0; $i < @list; $i++) {
+	$config = gtconfig ($list[$i], "config");
+	if ($key eq $config) { @match = (@match, $i); }	
+    }
+    if (@match == 0) { &escape ("config $key not found\n"); }
+    
+    # create new config version based on version 0
+    $Nnew = @match;
+    $old = $list[$match[0]];
+    $new = stconfig ($old, "version", $Nnew);
+    @list = (@list, $new);
+    save_config (@list);
+    
+    # now we need to duplicate all of the reference files from the first config 
+    foreach $ccd (@ccds) {
+	$fold = mknames ("master", $old, $ccd);
+	$fnew = mknames ("master", $new, $ccd);
+	system ("cp $fold $fnew");
+    }
+    foreach $name ("stats", "medbin", "tenbin", "imbin") {
+	$fold = mknames ($name, $old);
+	$fnew = mknames ($name, $new);
+	system ("cp $fold $fnew");
+    }
+    
+    &goodbye;
+    
+}
+
+############## clean config ###############
+sub mk_clean {
+    if (@ARGV != 1) { &escape ("USAGE: detrend clean"); }
+    # only deletes temporary files for configs in state 'done'
+    
+    @list = load_config ();
+    
+    $detdir = mkfiles ("detdir");
+    open (CF, ">$detdir/clean.fail");
+
+    $Nmiss = 0;
+    $Nfail = 0;
+    $Ngood = 0;
+
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "done") { next; }
+
+	print STDERR "deleting files from $confline\n";
+	$confline = stconfig ($confline, "status", "running.clean");
+	save_config (@list);
+
+	foreach $ccd (@ccds) { 
+	    # delete these specific files:
+	    @dlist = ();
+	    push @dlist, &mknames ("fits", $confline, $ccd); 
+	    push @dlist, &mknames ("norm", $confline, $ccd); 
+	    push @dlist, &mknames ("proc.imbin", $confline, $ccd); 
+	    push @dlist, &mknames ("proc.medbin", $confline, $ccd); 
+	    push @dlist, &mknames ("proc.tenbin", $confline, $ccd); 
+
+	    # delete file, if it exists
+	    foreach $file (@dlist) { 
+		if (! -e $file) { $Nmiss ++; next; }
+		if (!unlink $file) { $Nfail ++; print CF "$file\n"; next; }
+		$Ngood ++;
+	    }
+	}
+	$confline = stconfig ($confline, "status", "clean");
+	save_config (@list);
+    }
+    close (CF);
+    
+    print STDERR "$Ngood files deleted\n";
+    print STDERR "$Nmiss files already removed\n";
+    print STDERR "$Nfail files not deleted, see '$detdir/clean.fail'\n";
+
+    &goodbye;
+}
+
+############## delete config ###############
+sub mk_del {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND del (config.ver)"); }
+    
+    $key = $ARGV[1];
+    @list = load_config ();
+    
+    # look for entry that matches requested config
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}	
+    }
+    if ($match == -1) {
+	print STDERR "config.ver $key not found\n";
+	&escape;
+    }
+    
+    # create new list without match:
+    @newlist = ();
+    for ($i = 0; $i < @list; $i++) {
+	if ($i == $match) { next; }
+	@newlist = (@newlist, $list[$i]);
+    }
+    
+    save_config (@newlist);
+    &goodbye;
+    
+}
+
+############## reset config ###############
+sub mk_reset {
+    if (@ARGV != 2) {
+	print STDERR "USAGE: $MKDETREND reset (level)\n";
+	print STDERR "       level = hard || update\n";
+	&escape; 
+    }
+
+    $level = $ARGV[1];
+    @list = load_config ();
+    
+    # find specific config:
+RESET:
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+
+	if ($level eq "hard" ) {
+	    $confline = stconfig ($confline, "status", "init");
+	}
+	if ($level eq "update") {
+	    if (($status eq "done.merge") || ($status eq "modified") || ($status eq "update")) {
+		$confline = stconfig ($confline, "status", "update");
+		next RESET;
+	    } 
+	    if (($status eq "freeze") || ($status eq "accepted")) {
+		next RESET;
+	    } 
+	    $confline = stconfig ($confline, "status", "init");
+	}
+    }
+    save_config (@list);
+    &goodbye;
+}
+
+############## define config ###############
+sub mk_def {
+    if (@ARGV != 4) { &escape ("USAGE: $MKDETREND def (config.ver) (start) (stop)"); }
+    
+    $key   = $ARGV[1];
+    $start = $ARGV[2];
+    $stop  = $ARGV[3];
+    
+    @list = load_config ();
+    
+    # find specific config:
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	}	
+    }
+    if (@match == -1) {
+	print STDERR "config.ver $key not found\n";
+	&escape;
+    }
+    
+    $new = $list[$match];
+    $new = stconfig ($new, "start", $start);
+    $new = stconfig ($new, "stop", $stop);
+    $list[$match] = $new;
+    
+    save_config (@list);
+    &goodbye;
+    
+}
+
+############## list init ###############
+sub mk_list_init {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND list.init"); }
+    
+    @list = load_config ();
+    
+    # create namelist
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "init") { next; }
+	$Nrun ++;
+	$line = gtconfig ($confline, "mosaic");
+	print FILE "$line\n";
+	$confline = stconfig ($confline, "status", "running.init");
+    }
+    close (FILE);
+    save_config (@list);
+
+    if ($Nrun == 0) { &goodbye; }
+    
+    vsystem ("elixir -D DETREND_DIR $det -D mode $MKDETREND -D global.pending imselect.split $file");
+    system ("$MKDETREND meval init");
+    
+    &goodbye;
+}
+
+############## split ###############
+sub mk_split {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND split"); }
+    
+    @list = load_config ();
+    
+    # create namelist
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.init") && ($status ne "split")) { next; }
+	$Nrun ++;
+	foreach $ccd (@ccds) {
+	    $line = gtconfig ($confline, "chiprun", $ccd);
+	    print FILE "$line\n";
+	}
+	$confline = stconfig ($confline, "status", "running.split");
+    }
+    close (FILE);
+    save_config (@list);
+
+    if ($Nrun == 0) { &goodbye; }
+    
+    system ("elixir -D DETREND_DIR $det -D mode $MKDETREND -D global.pending merge.lists $file");
+    system ("$MKDETREND eval split");
+    
+    &goodbye;
+}
+
+############## update lists ###############
+sub mk_update_list {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND update.list"); }
+    
+    @list = load_config ();
+    
+    # create namelist
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "update") { next; }
+	$Nrun ++;
+	$line = gtconfig ($confline, "mosaic");
+	print FILE "$line\n";
+	$confline = stconfig ($confline, "status", "running.update.list");
+    }
+    close (FILE);
+    save_config (@list);
+    
+    if ($Nrun == 0) { &goodbye; }
+    
+    system ("elixir -D DETREND_DIR $det -D mode $MKDETREND -D global.pending update.split $file");
+    system ("$MKDETREND meval update.list");
+    
+    &goodbye;
+}
+
+############## update stats ###############
+sub mk_update_stats {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND update.stats"); }
+    
+    @list = load_config ();
+    
+    # create namelist
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.update.list") && ($status ne "update.stats")) { next; }
+	$Nrun ++;
+	foreach $ccd (@ccds) {
+	    $line = gtconfig ($confline, "chiprun", $ccd);
+	    print FILE "$line\n";
+	}
+	$confline = stconfig ($confline, "status", "running.update.stats");
+
+    }
+    close (FILE);
+    save_config (@list);
+    
+    if ($Nrun == 0) { &goodbye; }
+    
+    system ("elixir -D DETREND_DIR $det -D mode $MKDETREND -D global.pending update.msplit $file");
+    system ("$MKDETREND eval update.stats");
+    
+    &goodbye;
+}
+
+############## set state ###############
+sub mk_set {
+    if (@ARGV != 3) { &escape ("USAGE: $MKDETREND set (config number) (state)"); }
+    
+    @list = load_config ();
+    
+    $key = $ARGV[1];
+    $state = $ARGV[2];
+    
+    $new = stconfig ($list[$key], "status", "$state");
+    $list[$key] = $new;
+    save_config (@list);
+    
+    &goodbye;
+}
+
+############## flips run ###############
+sub mk_flips {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND flips"); }
+    
+    @list = load_config ();
+    
+    # create namelist for those with appropriate status
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.split") && ($status ne "modified") && ($status ne "flips")) { next; }
+	$Nrun ++;
+	foreach $ccd (@ccds) {
+	    $line = gtconfig ($confline, "chiprun", $ccd);
+	    print FILE "$line\n";
+	}
+	$confline = stconfig ($confline, "status", "running.flips");
+    }
+    close (FILE);
+    save_config (@list);
+
+    if ($Nrun == 0) { &goodbye; }
+
+    system ("elixir -D DETREND_DIR $det -D mode $MKDETREND -D global.pending create.subset $file"); 
+    system ("$MKDETREND eval flips");
+
+    &goodbye;
+}
+
+############## norm run ###############
+sub mk_norm {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND norm"); }
+
+    @list = load_config ();
+
+    # create namelist for those with appropriate status
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.flips") && ($status ne "norm")) { next; }
+	$Nrun ++;
+	foreach $ccd (@ccds) {
+	    $line = gtconfig ($confline, "chiprun", $ccd);
+	    print FILE "$line\n";
+	}
+	$confline = stconfig ($confline, "status", "running.norm");
+    }
+    close (FILE);
+    save_config (@list);
+
+    if ($Nrun == 0) { &goodbye; }
+
+    vsystem ("elixir -D DETREND_DIR $det -D mode $MKDETREND -D global.pending normal $file");
+    vsystem ("$MKDETREND eval norm");
+
+    &goodbye;
+}
+
+############## merge run ###############
+sub mk_merge {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND merge"); }
+    
+    @list = load_config ();
+    
+    # create namelist for those with appropriate status
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.norm") && ($status ne "done.update.stats") && ($status ne "merge")) { next; }
+	$Nrun ++;
+	$line = gtconfig ($confline, "mosaic");
+	$confline = stconfig ($confline, "status", "running.merge");
+	print FILE "$line\n";
+    }
+    close (FILE);
+    save_config (@list);
+
+    if ($Nrun == 0) { &goodbye; }
+    
+    vsystem ("elixir -D DETREND_DIR $det -D mode $MKDETREND -D global.pending stats $file");
+    vsystem ("$MKDETREND meval merge");
+    
+    &goodbye;
+}
+
+############## reg run ###############
+sub mk_reg {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND reg"); }
+    
+    @list = load_config ();
+    
+    # create directory for fhtool to work in
+    $links = mkfiles ("links");
+    if (! -e $links) { mkdir ($links); }
+
+    # create namelist for those with appropriate status
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "accepted") && ($status ne "reg")) { next; }
+	$confline = stconfig ($confline, "status", "running.reg");
+	save_config (@list);
+
+	$success = 1;
+	$ID = gtconfig ($confline, "ID");
+
+	$DBmode = `gconfig DETREND-DB-MODE`; chop $DBmode;
+	$DBmode = "\U$DBmode\E";
+
+	if ($DBmode eq "SPLIT") {
+	    for ($j = 0; $j < $Nccd; $j++) {
+		$file = mknames ("norm", $confline, $ccds[$j]);
+		$line = "detregister $file -label elixir -ID $ID";
+		$status = system ($line);
+		if ($status) { 
+		    print STDERR "error registering $file\n";
+		    $success = 0;
+		}
+	    }
+	    goto registered;
+	}
+
+	if ($DBmode eq "MEF") {
+	    # empty link directory:
+	    system ("rm -f $links/*.fits");
+
+	    # make links 
+	    foreach $ccd (@ccds) {
+		$file = mknames ("norm", $confline, $ccd);
+		$link = mknames ("link", $confline, $ccd);
+		symlink $file, $link;
+	    }
+
+	    # mef, register
+	    $mef = mknames ("mef", $confline);
+	    if (-e $mef) { unlink $mef; }
+	    vsystem ("fhtool -P $links $mef");
+	    if ($status) { $success = 0; }
+	    vsystem ("detregister $mef -label elixir -ID $ID");
+	    if ($status) { $success = 0; }
+
+	    # remove links
+	    foreach $ccd (@ccds) {
+		$link = mknames ("link", $confline, $ccd);
+		unlink $link;
+	    }
+	    goto registered;
+	}
+	print STDERR "DB mode not defined?\n";
+	$success = 0;
+
+      registered:
+	if ($success) { 
+	    $confline = stconfig ($confline, "status", "done.reg");
+	} else {
+	    print STDERR "error registering $file\n"; 
+	    $confline = stconfig ($confline, "status", "fail.reg");
+	}
+	save_config (@list);
+
+    }
+
+    # use recipe file to decide if we apply 'scat' or not!
+    $file = mkfiles ("list");
+    # create list of images to apply scattered-light correction:
+    foreach $confline (@list) {
+	$type = gtconfig ($confline, "type");
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.reg") && ($status ne "scat")) { next; }
+
+	# only flats get scat correction
+	if ($type eq "flat") { 
+	    $ID = gtconfig ($confline, "ID");
+
+	    open (FILE, ">$file");
+	    foreach $ccd (@ccds) {
+		$line = mknames ("norm", $confline, $ccd);
+		print FILE "$line\n";
+	    }
+	    close (FILE);
+	    
+	    $confline = stconfig ($confline, "status", "running.scat");
+	    save_config (@list);
+
+	    $status = vsystem ("fixscat $file B.2 $ID");
+	    if (($status == 0) || ($status == 2 * 256)) { 
+		$confline = stconfig ($confline, "status", "done.scat");
+	    } else {
+		$confline = stconfig ($confline, "status", "fail.scat");
+	    }		
+	    save_config (@list);
+	}
+    }
+
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.reg") && ($status ne "done.scat")) { next; }
+	$confline = stconfig ($confline, "status", "done");
+    }
+    save_config (@list);
+    &goodbye;
+}
+
+############## html modify ###############
+sub mk_fixmasters {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND fix.masters"); }
+    
+    @list = load_config ();
+
+    foreach $name (@list) {
+
+	@mstate = ();
+	$master = mknames ("master", $name, $ccds[0]); 
+	print STDERR "$master: $name\n";
+	open (FILE, $master);
+	@master = <FILE>;
+	close FILE;
+	for ($j = 0; $j < @master; $j++) {
+	    ($tmp, $state) = split (" ", $master[$j]);
+	    @mstate = (@mstate, $state);
+	}
+
+	# re-create all masters to match master for chip 00
+	for ($i = 1; $i < $Nccd; $i++) {
+
+	    # load existing master file
+	    $master = mknames ("master", $name, $ccds[$i]); 
+	    open (FILE, $master);
+	    @master = <FILE>;
+	    close FILE;
+	    
+	    $Nm = @master;
+	    $Ns = @mstate;
+	    if (@mstate != @master) {
+		print STDOUT "error: mis-match in image lists ccd $i\n";
+		&escape;
+	    }
+	    
+	    # write results back to master file
+	    open (OUT, ">$master") or die "can't open $master";
+	    for ($j = 0; $j < @master; $j++) {
+		($fname, $state) = split (" ", $master[$j]);
+		if ($state != $mstate[$j]) { print STDERR "error on $j $name\n"; }
+		printf OUT "%s %d\n", $fname, $mstate[$j];
+	    }
+	    close (OUT);
+	}
+    }
+    &goodbye;
+}
+
+############## html modify ###############
+sub mk_checksplits {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND check.splits (config)"); }
+    
+    $Nconfig = $ARGV[1];
+    @list = load_config ();
+    $name = $list[$Nconfig];
+
+    # re-create all masters to match master for chip 00
+    foreach $ccd (@ccds) {
+	
+	# load existing master file
+	$master = mknames ("master", $name, $ccd); 
+	open (FILE, $master);
+	@master = <FILE>;
+	close FILE;
+	
+	for ($j = 0; $j < @master; $j++) {
+	    ($fname, $state) = split (" ", $master[$j]);
+	    if (! -e $fname) { 
+		print STDERR "missing file $fname\n";
+	    }
+	}
+    }
+    &goodbye;
+}
+
+####  HTML cgibin functions ################ 
+
+############## html duplicate ###############
+sub mk_htmldup {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND htmldup (config)"); }
+    
+    $config = $ARGV[1];
+    # we are just calling the "mkdetrend dup" function and then calling "$MKDETREND html1"
+    
+    system ("$MKDETREND dup $config");
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/dt.elixir1?&elconf=$elconf\">\n";
+    
+    &goodbye;
+    
+}
+
+############## html delete ###############
+sub mk_htmldel {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND htmldel (config.ver)"); }
+    
+    $config = $ARGV[1];
+    # we are just calling the "mkdetrend del" function and then calling "mkdetrend html1"
+    
+    system ("$MKDETREND del $config");
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/dt.elixir1?&elconf=$elconf\">\n";
+    
+    &goodbye;;
+    
+}
+
+############## html define ###############
+sub mk_htmldef {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND htmldef (config.ver)"); }
+    
+    $key = $ARGV[1];
+    
+    # find (config.ver) in list:
+    
+    @list = load_config ();
+    
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}
+    }
+    if ($match == -1) { &escape ("error: can't find config entry"); }
+    
+    # show the complete list for reference
+    print STDOUT "<HEADER> <TITLE>Elixir Mkdetrend Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0e0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+    print STDOUT "<table border>\n";
+    for ($i = 0; $i < @list; $i++) {
+	$config  = gtconfig ($list[$i], "config");
+	$version = gtconfig ($list[$i], "version");
+	$status  = gtconfig ($list[$i], "status");
+	$from    = gtconfig ($list[$i], "from");
+	$to      = gtconfig ($list[$i], "to");
+	print STDOUT "<tr><td> $version </td>\n";
+	print STDOUT "<td> $from </td><td> $to </td>\n";
+	print STDOUT "<td> <a href=$CGI/dt.elixir2?$version&elconf=$elconf> $status </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/dt.split?$config&elconf=$elconf>      split </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/dt.delete?$version&elconf=$elconf>   delete </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/dt.redef?$version&elconf=$elconf>  redefine </a></td>\n";
+	print STDOUT "</tr>\n";
+    }
+    print STDOUT "</table></body><br><br>\n";
+    
+    $version = gtconfig ($list[$match], "version");
+    $status  = gtconfig ($list[$match], "status");
+    $from    = gtconfig ($list[$match], "from");
+    $to      = gtconfig ($list[$match], "to");
+    print STDOUT "redefining <b> $key: </b> <br>\n";    
+    print STDOUT "<table border>\n";
+    print STDOUT "<tr><td> $version </td>\n";
+    print STDOUT "<td> $from </td><td> $to </td><td> $status </a></td>\n";
+    print STDOUT "</tr></table>\n";
+    
+    
+    # create a simple form for redefine:
+    print STDOUT "<form method=post action=$CGI/dt.redefine&elconf=$elconf>\n";
+    print STDOUT "start: <input type=text value=$from name=start size=40 maxlength=40> <br>\n";
+    print STDOUT "stop: <input type=text value=$to name=stop size=40 maxlength=40> <br>\n";
+    print STDOUT "<input type=hidden name=config value=\"$key\">\n";
+    print STDOUT "<input type=\"submit\" value=\"submit\"><input type=\"reset\" value=\"reset\">\n";
+    print STDOUT "</form>\n";
+    print STDOUT "</body>\n";
+    
+    &goodbye;;
+    
+}
+
+############## html modify ###############
+sub mk_htmlmod {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND htmlmod"); }
+    
+    $key = $ENV{'WWW_config'};
+    $Nimage = $ENV{'WWW_Nimage'};
+    
+    @list = load_config ();
+    
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}
+    }
+    if ($match == -1) { &escape ("error: can't find config entry"); }
+    
+    # re-create $master to match selections
+    # depends on the format of data in the 'master' file
+    foreach $ccd (@ccds) {
+
+	# load existing master file
+	$master = mknames ("master", $list[$match], $ccd); 
+	open (FILE, $master);
+	@master = <FILE>;
+	close FILE;
+
+	if ($Nimage != @master) {
+	    print STDOUT "error: mis-match in image lists ccd $ccd\n";
+	    &escape;
+	}
+
+	# write results back to master file
+	open (OUT, ">$master") or die "can't open $master";
+	for ($j = 0; $j < @master; $j++) {
+	    @tmpw = split (" ", $master[$j]);
+	    $name = "WWW_image_$j";
+
+	    $tmpw[1] = 0;
+	    if ($ENV{$name} ne "") { $tmpw[1] = 1; }
+	    printf OUT "%s %d\n", $tmpw[0], $tmpw[1];
+	}
+	close (OUT);
+    }
+    # adjust the config file: convert status to 'modified'
+    $list[$match] = stconfig ($list[$match], "status", "modified");
+    save_config (@list);
+
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/dt.elixir2?$key&elconf=$elconf\">\n";
+    &goodbye;
+}
+
+############## html accept ###############
+sub mk_htmlkeep {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND htmlkeep (config.ver)"); }
+    
+    $key = $ARGV[1];
+    @list = load_config ();
+    
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}
+    }
+    if ($match == -1) {
+	print STDERR "error: can't find config entry\n";
+	&escape;
+    } 
+    
+    # adjust the config file: convert status to 'accepted'
+    $list[$match] = stconfig ($list[$match], "status", "accepted");
+    save_config (@list);
+    
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/dt.elixir1?&elconf=$elconf\">";
+    &goodbye;
+}
+
+############## html config ###############
+sub mk_htmlconfig {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND htmlconfig"); }
+    
+    ## html headers
+    print STDOUT "<HEADER> <TITLE>Elixir Mkdetrend Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0f0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+    
+    # create a simple form for redefine:
+    print STDOUT "<form method=post action=$CGI/dt.newconfig>\n";
+    if ($elconf) {
+	print STDOUT "enter new config: <input type=text value=$elconf name=config size=40 maxlength=40> <br>\n";
+    } else {
+	print STDOUT "enter new config: <input type=text name=config size=40 maxlength=40> <br>\n";
+    }
+    print STDOUT "<input type=\"submit\" value=\"submit\"><input type=\"reset\" value=\"reset\">\n";
+    print STDOUT "</form>\n";
+    print STDOUT "</body>\n";
+    
+    &goodbye;
+}
+
+############## html level 1 ###############
+sub mk_html1 {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND html1"); }
+    
+    @list = load_config ();
+    
+    ## html headers
+    print STDOUT "<HEADER> <TITLE>Elixir Mkdetrend Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0f0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+
+    print STDOUT "<table border>\n";
+    for ($i = 0; $i < @list; $i++) {
+	$config  = gtconfig ($list[$i], "config");
+	$version = gtconfig ($list[$i], "version");
+	$from    = gtconfig ($list[$i], "from");
+	$to      = gtconfig ($list[$i], "to");
+	$status  = gtconfig ($list[$i], "status");
+	print STDOUT "<tr><td> $version </td>\n";
+	print STDOUT "<td> $from </td><td> $to </td>\n";
+	print STDOUT "<td> <a href=$CGI/dt.elixir2?$version&elconf=$elconf> $status </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/dt.split?$config&elconf=$elconf> split </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/dt.delete?$version&elconf=$elconf> delete </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/dt.redef?$version&elconf=$elconf> redefine </a></td>\n";
+	print STDOUT "</tr>\n";
+    }
+    
+    print STDOUT "</table><br><br>\n";
+    if ($elconf eq "") {
+	print STDOUT "Current Config: <b>default</b><br>\n";
+    } else {
+	print STDOUT "Current Config: <b>$elconf</b><br>\n";
+    }
+    print STDOUT "<a href=$CGI/dt.config?&elconf=$elconf>Change Config</a></td>\n";
+    print STDOUT "</body>\n";
+    &goodbye;;
+}
+
+############## html level 2 ###############
+sub mk_html2 {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND html2 (config.ver)"); }
+    
+    @list = load_config ();
+    
+    # entry on line will be either config.ver (== key) or config.ver.col for sorting
+    # grab just the key portion
+    $status = "none";
+    ($key) = $ARGV[1] =~ /(\w+.\w+.\w+.\w+)/; # (config.ver)
+    $arg = $ARGV[1];
+    
+    # find appropriate config.ver entry:
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $confline = $list[$i];
+	    $status = gtconfig ($confline, "status");
+	    last;
+	}
+    }
+    
+    $from = gtconfig ($confline, "from");
+    $to   = gtconfig ($confline, "to");
+    print STDOUT "<HEADER> <TITLE>Elixir Mkdetrend Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0f0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+    print STDOUT "<center><table border>\n";
+    print STDOUT "<tr><th>config   </th><th> from  </th><th> to </th></tr>\n";
+    print STDOUT "<tr><td>$version </td><td> $from </td><td> $to</td></tr>\n";
+    print STDOUT "</table></center><hr>\n";
+    
+    $master = mknames ("master", $confline, $ccds[0]);
+    
+    if ($status eq "modified")   { flips_report ($arg, $confline,      "modified"); }
+    if ($status eq "accepted")   { flips_report ($arg, $confline,      "accepted"); }
+    if ($status eq "done.merge") { flips_report ($arg, $confline,      "merged"); }
+    if ($status eq "done.reg")   { flips_report_done ($key, $confline, "registered"); }
+    if ($status eq "done")       { flips_report_done ($key, $confline, "registered"); }
+    if ($status eq "clean")      { flips_report ($key, $confline,      "registered"); }
+    
+    if ($status eq "none")              { $message = "not found"; }
+    if ($status eq "init")              { $message = "initialized, not yet run."; }
+    if ($status eq "not.available")     { $message = "has no images available for detrend creation."; }
+    
+    foreach $step (init, flips, norm, merge) {
+	if ($status eq "running.$step") { $message = "running step $step"; }
+	if ($status eq "fail.$step")    { $message = "failed step $step"; }
+	if ($status eq "done.$step") { $message = "has finished $step"; }
+    }      
+
+    print STDOUT "config <b> $key </b> $message <br>\n";
+    
+    print STDOUT "<table border>\n";
+    open (FILE, "$master");
+    while ($line = <FILE>) {
+	chop ($line);
+	@entry = split ("/", $line);
+	$N = @entry - 1;
+	print STDOUT "<tr><td>$entry[$N]</td></tr>\n";
+    }
+    print STDOUT "</table>\n";
+    
+    print STDOUT "</body>\n";
+    &goodbye;
+}
+
+############## html level 3 ###############
+sub mk_html3 {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND html3 (entry.config.ver)"); }
+    
+    @list = load_config ();
+    
+    # extract the entry and key values from $ARGV[1]
+    ($entry, $key) = $ARGV[1] =~ /(\d+)\Q.\E(.*)/;
+    
+    $status = "none";
+    # find appropriate config.ver entry:
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $confline = $list[$i];
+	    $status = gtconfig ($confline, "status");
+	    last;
+	}
+    }
+    
+    print STDOUT "<HEADER> <TITLE>Elixir Mkdetrend Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0f0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+    
+    if ($status eq "none")              { print STDOUT "config <b> $key </b> not found\n </body>\n"; &goodbye; }
+    if ($status eq "init")              { print STDOUT "config <b> $key </b> initialized, not yet run\n </body>\n"; &goodbye; }
+    if ($status eq "not.available")     { print STDOUT "config <b> $key </b> has no images available for detrend creation\n </body>\n"; &goodbye; }
+    
+    # if ($status eq "done.update") { flips_entry ($key, $entry, $confline); }
+    if ($status eq "done.merge") { flips_entry ($key, $entry, $confline); }
+    if ($status eq "accepted")   { flips_entry ($key, $entry, $confline); }
+    if ($status eq "modified")   { flips_entry ($key, $entry, $confline); }
+    if ($status eq "done.reg")   { flips_entry ($key, $entry, $confline); }
+    
+    foreach $step (init, flips, norm, merge) {
+	# print "step: $step, $status: $status\n";
+	if ($status eq "running.$step") { print STDOUT "config <b> $key </b> running step $step \n </body>\n"; &goodbye; }
+	if ($status eq "fail.$step")    { print STDOUT "config <b> $key </b> failed step $step\n </body>\n"; &goodbye; }
+	
+	if ($status eq "done.$step") {
+	    print STDOUT "config <b> $key </b> has finished $step\n";
+	    
+	    # load image names from CCD 0 list:
+	    $master = mknames ("master", $confline, $ccds[0]);
+	    print STDOUT "<table border>\n";
+	    open (FILE, "$master");
+	    while ($line = <FILE>) {
+		chop ($line);
+		@entry = split ("/", $line);
+		$N = @entry - 1;
+		print STDOUT "<tr><td>$entry[$N]</td></tr>\n";
+	    }
+	    print STDOUT "</table>\n";
+	}
+    }
+    
+    # print STDOUT "</body>\n";
+    &goodbye;
+}
+
+###  other support commands ####################
+
+############## evaluate chip run ###############
+sub mk_eval {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND eval (step)"); }
+    
+    $step = $ARGV[1];
+    @list = load_config ();
+    
+    # load success fifo info:
+    $file = `gconfig -D mode $MKDETREND global.success`; chop $file;
+    open (FILE, "$file");
+    @success = <FILE>;
+    close (FILE);
+    
+    # load failure fifo info:
+    $file = `gconfig -D mode $MKDETREND global.failure`; chop $file;
+    open (FILE, "$file");
+    @failure = <FILE>;
+    close (FILE);
+    
+    # entry must be in state 'running.$step'
+    # possible states:
+    #   1 all 12 chips in success          == done.$step
+    #   2 any chips in failure             == fail.$step
+    #   3 any chips missing                == fail.$step
+    #   4 any chips in failure, merge.list == not.available (only for $step eq init)
+    # valid steps: init flips norm
+    
+  CONFIG:
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "running.$step") { next; }
+	$state = 1;
+
+      CCD:
+	foreach $ccd (@ccds) {
+	    $entry = gtconfig ($confline, "chiprun", $ccd);
+
+	    # search for entry in success fifo:
+	    foreach $value (@success) {
+		if ($value =~ /^$entry/) { next CCD; }
+	    }
+	    
+	    # search for entry in failure fifo: note the special ending state for 'init'
+	    foreach $value (@failure) {
+		if ($value =~ /^$entry/) {
+		    $state = 2;
+		    if ($step eq "split") {
+			($status) = $value =~ /$entry\s+\S+\s+(\S+)/;
+			if ($status eq "merge.lists") {
+			    $state = 4;
+			}
+		    }
+		    last CCD;
+		}
+	    }
+	    
+	    # if we make it here, it wasn't in either fifo.success or fifo.failure...
+	    $state = 3;
+	    last CCD;
+	}
+	
+	if ($state == 1) { $confline = stconfig ($confline, "status", "done.$step"); } 
+	if ($state == 2) { $confline = stconfig ($confline, "status", "fail.$step"); }
+	if ($state == 3) { $confline = stconfig ($confline, "status", "fail.$step"); } 
+	if ($state == 4) { $confline = stconfig ($confline, "status", "not.available"); }
+    }
+    save_config (@list);
+
+    system ("$MKDETREND state");
+    &goodbye;
+}
+
+############## evaluate mosaic run ###############
+sub mk_meval {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND meval (step)"); }
+    
+    $step = $ARGV[1];
+    @list = load_config ();
+    
+    # load success fifo info:
+    $file = `gconfig -D mode $MKDETREND global.success`; chop $file;
+    open (FILE, "$file");
+    @success = <FILE>;
+    close (FILE);
+    
+    # load failure fifo info:
+    $file = `gconfig -D mode $MKDETREND global.failure`; chop $file;
+    open (FILE, "$file");
+    @failure = <FILE>;
+    close (FILE);
+    
+    # entry must be in state 'running.$step'
+    # possible states:
+    #   1 entry in success          == done.$step
+    #   2 entry in failure          == fail.$step
+    #   3 entry missing             == fail.$step
+    # valid steps: merge
+    
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "running.$step") { next; }
+	$state = 3;
+	$entry = gtconfig ($confline, "mosaic");
+
+      MCHECK: 
+	{
+	    foreach $value (@success) {
+		if ($value =~ /^$entry/) { 
+		    $state = 1;
+		    last MCHECK;
+		}
+	    }
+	    
+	    foreach $value (@failure) {
+		if ($value =~ /^$entry/) { 
+		    $state = 2;
+		    next MCHECK;
+		}
+	    }
+	}
+	
+	if ($state == 1) { $confline = stconfig ($confline, "status", "done.$step"); } 
+	if ($state == 2) { $confline = stconfig ($confline, "status", "fail.$step"); }
+	if ($state == 3) { $confline = stconfig ($confline, "status", "fail.$step"); }
+    }
+    
+    save_config (@list);
+    system ("$MKDETREND state");
+    
+    &goodbye;
+}
+
+############## html for dads ###############
+sub mk_dads {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND dads (config.ver)"); }
+    
+    # options: mkdetrend dads (config.ver)
+    # return all relevant images (jpegs) for given config.ver
+
+    @list = load_config ();
+    
+    # entry on line will be either config.ver (== key) or config.ver.col for sorting
+    # grab just the key portion
+    $status = "none";
+    $arg = $ARGV[1];
+    ($key) = $arg =~ /(\w+.\w+.\w+.\w+)/; # (config.ver)
+    
+    # find appropriate config.ver entry:
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $confline = $list[$i];
+	    $status = gtconfig ($confline, "status");
+	    last;
+	}
+    }
+    if (!$confline) { &escape ("$key not found"); }
+    
+    # only print for config.ver entries which are ready (done, clean)
+    if (($status ne "done") && ($status ne "clean")) { 
+	&escape ("$version is not ready for release by Elixir"); 
+    }
+
+    # parse config entry and get data from appropriate files
+    $from = gtconfig ($confline, "from");
+    $to   = gtconfig ($confline, "to");
+    open (STAT,   mknames ("stats", $confline));            @statdata = <STAT>;        close (STAT);
+    open (MED,    mknames ("medbin", $confline));           @medndata = <MED>;         close (MED);
+    open (TEN,    mknames ("tenbin", $confline));           @tenndata = <TEN>;         close (TEN);
+    open (MASTER, mknames ("master", $confline, $ccds[0])); @mastdata = <MASTER>;      close (MASTER);
+    
+    $tmpname = mknames ("master", $confline, $ccds[0]);
+
+    # get stats from the stats & master files
+    $Nkeep = 0;
+    $Ncnt = 0;
+    $Sigma = 0;
+    $Nimage = @statdata;
+    for ($i = 0; $i < $Nimage; $i++) {
+	@tmpw = split (" ", $mastdata[$i]);
+	@tmps = split (" ", $statdata[$i]);
+	if ($tmpw[1]) { $Nkeep++; $Ncnt += $tmps[3]; $Sigma += $tmps[8]*$tmps[8]; }
+    }
+    $imbin = mknames ("dads.imbin", $confline);
+
+    ($start, $stop, $mode) = &load_dates;
+    print STDOUT "$imbin $Nimage $start $stop\n";
+
+    $dir = mkfiles ("html");
+    # create the image-by-image table lines
+    for ($i = 0; $i < $Nimage; $i++) {
+	@word = split (" ", $statdata[$i]);
+	$used = $word[12];
+	$dawn = $word[11];
+	
+	@tmpw = split (" ", $mastdata[$i]);
+	$keep = $tmpw[1];
+	
+	# data files contain the complete path, but this may have changed
+	# replace path with mkfiles ("html") -> $dir
+	@tmpw = split ('/', $tenndata[$i]);
+	$tenimg = "$dir/$tmpw[-1]"; chop $tenimg;
+	@tmpw = split ('/', $medndata[$i]);
+	$medimg = "$dir/$tmpw[-1]"; chop $medimg;
+
+	print STDOUT "$tenimg $medimg $word[0] $word[1] $dawn $word[2] $word[3] $word[5] $word[6] $word[8] $used\n";
+    }
+    &goodbye;
+}
+
+############## html for dads ###############
+sub mk_dads_html {
+    if (@ARGV != 2) { &escape ("USAGE: $MKDETREND dads (config.ver)"); }
+    
+    @list = load_config ();
+    
+    # entry on line will be either config.ver (== key) or config.ver.col for sorting
+    # grab just the key portion
+    $status = "none";
+    $arg = $ARGV[1];
+    ($key) = $arg =~ /(\w+.\w+.\w+.\w+)/; # (config.ver)
+    
+    # find appropriate config.ver entry:
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $confline = $list[$i];
+	    $status = gtconfig ($confline, "status");
+	    last;
+	}
+    }
+    
+    print STDOUT "<HEADER> <TITLE>Elixir Mkdetrend Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#ffffff TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+
+    # only print for config.ver entries which are ready
+    if ($status ne "done.reg") { &escape ("$version is not ready for release by Elixir"); }
+
+    # parse config entry and get data from appropriate files
+    $from = gtconfig ($confline, "from");
+    $to   = gtconfig ($confline, "to");
+    open (STAT,   mknames ("stats", $confline));     @statdata = <STAT>;        close (STAT);
+    open (MED,    mknames ("medbin", $confline));    @medndata = <MED>;         close (MED);
+    open (TEN,    mknames ("tenbin", $confline));    @tenndata = <TEN>;         close (TEN);
+    open (MASTER, mknames ("master", $confline, $ccds[0])); @mastdata = <MASTER>;      close (MASTER);
+    
+    $tmpname = mknames ("master", $confline, $ccds[0]);
+
+    # get stats from the stats & master files
+    $Nkeep = 0;
+    $Ncnt = 0;
+    $Sigma = 0;
+    $Nimage = @statdata;
+    for ($i = 0; $i < $Nimage; $i++) {
+	@tmpw = split (" ", $mastdata[$i]);
+	@tmps = split (" ", $statdata[$i]);
+	if ($tmpw[1]) { $Nkeep++; $Ncnt += $tmps[3]; $Sigma += $tmps[8]*$tmps[8]; }
+    }
+    $Sigma = sprintf "%5.3f", 100*sqrt ($Sigma/$Nkeep);
+
+    print STDOUT "<center><table border>\n";
+    print STDOUT "<tr><th>config   </th><th> from  </th><th> to </th></tr>\n";
+    print STDOUT "<tr><td>$version </td><td> $from </td><td> $to</td></tr>\n";
+    print STDOUT "</table></center><hr>\n";
+
+    $imbin = mknames ("dads.imbin", $confline);
+    print STDOUT "<table width=80%><tr><td valign=top>\n";
+    print STDOUT "this config has been created by Elixir. <br>\n";
+    print STDOUT "there are $Nimage images, $Nkeep used. <br>\n";
+    print STDOUT "Total Counts: $Ncnt<br> r.m.s. median sigma: $Sigma% <br><br>\n";
+    print STDOUT "</td><td align=right><img src=$imbin></td></tr></table>\n";
+    
+    print STDOUT "<center><b>Statistics on each available input image.</b></center>
+  Time is in UT, light background means dawn, grey means dusk.\n";
+
+    print STDOUT "<table bgcolor=#fff0e0 border=1 cellspacing=0 cellpadding=3>\n";
+    print STDOUT "<tr >\n";
+    print STDOUT "<th > image </th>\n";
+    print STDOUT "<th > Date  </th>\n";
+    print STDOUT "<th > Time  </th>\n";
+    print STDOUT "<th > Mean  </a></th>\n";
+    print STDOUT "<th > Sigma (raw) </a></th>\n";
+    print STDOUT "<th > Sigma (clipped) </a></th>\n";
+    print STDOUT "<th > Sigma (med) </a></th>\n";
+    print STDOUT "<th > image </th>\n";
+    print STDOUT "<th > used? </th></tr>\n";
+    
+    # sort the list by image seq number
+    @order = ();
+    for ($i = 0; $i < $Nimage; $i++) { @order = (@order, $i); }
+    $col = 0;
+    @entry = sort by_col @order;
+    
+    # create the image-by-image table lines
+    for ($i = 0; $i < $Nimage; $i++) {
+	$N = $entry[$i];
+	
+	@word = split (" ", $statdata[$N]);
+	$used = $word[12];
+	$dawn = $word[11];
+	
+	@tmpw = split (" ", $mastdata[$N]);
+	$keep = $tmpw[1];
+	
+	@tmpw = split ("/", $medndata[$N]);
+	$medname = $tmpw[-1];
+	
+	@tmpw = split ("/", $tenndata[$N]);
+	$tenname = $tmpw[-1];
+	
+	print STDOUT "<tr><td> $word[0] </td><td> $word[1] </td>\n";
+	if ($dawn) { print STDOUT "<td bgcolor=#cccccc> $word[2] </td>\n"; }
+	else       { print STDOUT "<td> $word[2] </td>\n"; }
+	print STDOUT "<td> $word[3] </td>\n";
+	print STDOUT "<td> $word[5] </td><td> $word[6] </td><td> $word[8] </td>\n";
+	print STDOUT "<td> <a border=0 href=$dadswww/$tenname> <img src=$dadswww/$medname></a></td>\n";
+	if ($used) { print STDOUT "<td bgcolor=#00c000>yes</td>\n"; }
+	else       { print STDOUT "<td bgcolor=#d00000>no</td>\n"; }
+	print STDOUT "</tr>\n\n"; 	
+    }
+    
+    print STDOUT "</table>\n";
+    &goodbye;
+}
+
+############## dads level 1 ###############
+sub mk_dads_top {
+    if (@ARGV != 1) { &escape ("USAGE: $MKDETREND dads.top"); }
+    
+    @list = load_config ();
+    
+    ## html headers
+    print STDOUT "<HEADER> <TITLE>Elixir Mkdetrend Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0f0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+
+    print STDOUT "<table border>\n";
+    for ($i = 0; $i < @list; $i++) {
+	$config  = gtconfig ($list[$i], "config");
+	$version = gtconfig ($list[$i], "version");
+	$from    = gtconfig ($list[$i], "from");
+	$to      = gtconfig ($list[$i], "to");
+	$status  = gtconfig ($list[$i], "status");
+	print STDOUT "<tr><td> $version </td>\n";
+	print STDOUT "<td> $from </td><td> $to </td>\n";
+	print STDOUT "<td> <a href=$version.html> $status </a></td>\n";
+	print STDOUT "</tr>\n";
+    }
+    
+    print STDOUT "</table></body>\n";
+    &goodbye;;
+    
+}
+
+sub by_col { 
+    @word = split (" ", $statdata[$a]); $va = $word[$col]; 
+    @word = split (" ", $statdata[$b]); $vb = $word[$col]; 
+    $va <=> $vb;
+}
+
+sub flips_report {
+    
+    $col = 0;
+    ($type)   = $_[0] =~ /\w+.(\w+).\w+/;
+    ($config) = $_[0] =~ /(\w+.\w+.\w+)/;
+    ($key)    = $_[0] =~ /(\w+.\w+.\w+.\w+)/; # (config.ver)
+
+    # look for optional sort key (config.ver[.key])
+    if ($_[0] =~ /$key.(\d+)/) {
+	($col) = $_[0] =~ /$key.(\d+)/; 
+    } else {
+	$col = 0;
+    }
+
+    $line = $_[1];
+    $message = $_[2];
+    
+    open (FILE, mknames ("stats",  $line));            @statdata = <FILE>; close (FILE);
+    open (FILE, mknames ("medbin", $line));            @medndata = <FILE>; close (FILE);
+    open (FILE, mknames ("master", $line, $ccds[0]));  @mastdata = <FILE>; close (FILE);
+
+    $Nkeep = 0;
+    $Ncnt = 0;
+    $Sigma = 0;
+    $Nimage = @statdata;
+    for ($i = 0; $i < $Nimage; $i++) {
+	@tmpw = split (" ", $mastdata[$i]);
+	@tmps = split (" ", $statdata[$i]);
+	if ($tmpw[1]) { $Nkeep++; $Ncnt += $tmps[3]; $Sigma += $tmps[8]*$tmps[8]; }
+    }
+    if ($Nkeep > 0) {
+	if ($type eq "flat") { 
+	    $Sigma = sprintf "%5.3f", 100*sqrt ($Sigma/$Nkeep);
+	    $statline = "Total Counts: $Ncnt<br> r.m.s. median sigma: $Sigma% <br><br>\n";
+	} else {
+	    $Sigma = sprintf "%5.3f", sqrt ($Sigma/$Nkeep);
+	    $Ncnt  = sprintf "%5.3f", ($Ncnt / $Nkeep);
+	    $statline = "Mean Counts: $Ncnt<br> r.m.s. median sigma: $Sigma <br><br>\n";
+	}
+    } else {
+	$statline = "";
+    }
+
+    $imbin = mknames ("imbin",  $line);
+    print STDOUT "<table><tr><td width=300px valign=top>\n";
+    print STDOUT "<b>this config has been $message. </b><br>\n";
+    print STDOUT "there are $Nimage images, $Nkeep used. <br>\n";
+    print STDOUT "$statline\n";
+    print STDOUT "you have the following choices: <br>\n";
+    print STDOUT "<a href=$CGI/dt.elixir1?&elconf=$elconf> <b> return to top level </b><br></a>\n";
+    print STDOUT "<a href=$CGI/dt.split?$config&elconf=$elconf> <b> split config </b><br></a>\n";
+    print STDOUT "<a href=$CGI/dt.accept?$key&elconf=$elconf> <b> accept config </b><br></a>\n";
+    print STDOUT "</td><td width=640px align=right><img width=100% src=$imbin></td></tr></table>\n";
+    
+    print STDOUT "<form method=post action=$CGI/dt.modify?&elconf=$elconf>\n";
+
+    print STDOUT "<center><b>Statistics on each available input image.</b></center>
+  Time is in UT, light background means dawn, grey means dusk. Click
+  on the column heading to sort by that entry\n";
+
+    print STDOUT "<table bgcolor=#fff0e0 border=1 cellspacing=0 cellpadding=3>\n";
+    print STDOUT "<tr bgcolor=#00aaaa>\n";
+    print STDOUT "<th > <a href=$CGI/dt.elixir2?$key.0&elconf=$elconf> image </a></th>\n";
+    print STDOUT "<th > Date </th>\n";
+    print STDOUT "<th > Time </th>\n";
+    print STDOUT "<th > <a href=$CGI/dt.elixir2?$key.3&elconf=$elconf> Mean </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/dt.elixir2?$key.5&elconf=$elconf> Sigma (raw) </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/dt.elixir2?$key.6&elconf=$elconf> Sigma (clipped) </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/dt.elixir2?$key.8&elconf=$elconf> Sigma (med) </a></th>\n";
+    print STDOUT "<th > image </th>\n";
+    print STDOUT "<th > used? </th>\n";
+    print STDOUT "<th > keep? </th></tr>\n\n";
+    
+    @order = ();
+    for ($i = 0; $i < $Nimage; $i++) { @order = (@order, $i); }
+    
+    @entry = sort by_col @order;
+    
+    for ($i = 0; $i < $Nimage; $i++) {
+	$N = $entry[$i];
+	
+	@word = split (" ", $statdata[$N]);
+	$used = $word[12];
+	$dawn = $word[11];
+	
+	@tmpw = split (" ", $mastdata[$N]);
+	$keep = $tmpw[1];
+	
+	@tmpw = split ("/", $medndata[$N]);
+	$medname = $tmpw[-1];
+	
+	print STDOUT "<tr><td> <a href=$CGI/dt.elixir3?$N.$key&elconf=$elconf> $word[0] </a></td><td> $word[1] </td>\n";
+	if ($dawn) { print STDOUT "<td bgcolor=#cccccc> $word[2] </td>\n"; }
+	else       { print STDOUT "<td> $word[2] </td>\n"; }
+	print STDOUT "<td> $word[3] </td>\n"; # mean
+	print STDOUT "<td> $word[5] </td>\n"; # Sigma (raw)
+	print STDOUT "<td> $word[6] </td>\n"; # Sigma (clip)
+	print STDOUT "<td> $word[8] </td>\n"; # Sigma (med)
+	print STDOUT "<td> <a border=0 href=$CGI/dt.elixir3?$N.$key&elconf=$elconf>\n";
+	print STDOUT "<img src=$www/$medname></a></td>\n";
+	if ($used) { print STDOUT "<td>yes</td>\n"; }
+	else       { print STDOUT "<td>no</td>\n"; }
+	if ($keep) { print STDOUT "<td bgcolor=#00c000><input type=checkbox checked name=image.$N value=$N size=1></td></tr>\n\n"; }
+	else       { print STDOUT "<td bgcolor=#d00000><input type=checkbox         name=image.$N value=$N size=1></td></tr>\n\n"; }	
+	
+    }
+    
+    print STDOUT "</table>\n";
+    print STDOUT "<input type=hidden    name=Nimage value=\"$Nimage\">\n";
+    print STDOUT "<input type=hidden    name=config value=\"$key\">\n";
+    print STDOUT "<input type=submit    value=\"apply changes\">\n";
+    print STDOUT "<input type=reset     value=Reset>\n";
+    # print STDOUT "<select name=status   size=1> \n";
+    print STDOUT "</form></body>\n";
+    &goodbye;
+    
+}
+
+sub flips_report_done {
+    
+    $col = 0;
+    ($type)   = $_[0] =~ /\w+.(\w+).\w+/;
+    ($config) = $_[0] =~ /(\w+.\w+.\w+)/;
+    ($key)    = $_[0] =~ /(\w+.\w+.\w+.\w+)/; # (config.ver)
+
+    # look for optional sort key (config.ver[.key])
+    if ($_[0] =~ /$key.(\d+)/) {
+	($col) = $_[0] =~ /$key.(\d+)/; 
+    } else {
+	$col = 0;
+    }
+
+    $line = $_[1];
+    $message = $_[2];
+    
+    open (FILE, mknames ("stats",  $line));            @statdata = <FILE>; close (FILE);
+    open (FILE, mknames ("medbin", $line));            @medndata = <FILE>; close (FILE);
+    open (FILE, mknames ("master", $line, $ccds[0]));  @mastdata = <FILE>; close (FILE);
+
+    $Nkeep = 0;
+    $Ncnt = 0;
+    $Sigma = 0;
+    $Nimage = @statdata;
+    for ($i = 0; $i < $Nimage; $i++) {
+	@tmpw = split (" ", $mastdata[$i]);
+	@tmps = split (" ", $statdata[$i]);
+	if ($tmpw[1]) { $Nkeep++; $Ncnt += $tmps[3]; $Sigma += $tmps[8]*$tmps[8]; }
+    }
+    if ($Nkeep > 0) {
+	if ($type eq "flat") { 
+	    $Sigma = sprintf "%5.3f", 100*sqrt ($Sigma/$Nkeep);
+	    $statline = "Total Counts: $Ncnt<br> r.m.s. median sigma: $Sigma% <br><br>\n";
+	} else {
+	    $Sigma = sprintf "%5.3f", sqrt ($Sigma/$Nkeep);
+	    $Ncnt  = sprintf "%5.3f", ($Ncnt / $Nkeep);
+	    $statline = "Mean Counts: $Ncnt<br> r.m.s. median sigma: $Sigma <br><br>\n";
+	}
+    } else {
+	$statline = "";
+    }
+
+    $imbin = mknames ("imbin",  $line);
+    print STDOUT "<table><tr><td width=300px valign=top>\n";
+    print STDOUT "<b>this config has been $message. </b><br>\n";
+    print STDOUT "there are $Nimage images, $Nkeep used. <br>\n";
+    print STDOUT "$statline\n";
+    print STDOUT "you have the following choices: <br>\n";
+    print STDOUT "<a href=$CGI/dt.elixir1?&elconf=$elconf> <b> return to top level </b><br></a>\n";
+    print STDOUT "<a href=$CGI/dt.split?$config&elconf=$elconf> <b> split config </b><br></a>\n";
+    print STDOUT "<a href=$CGI/dt.accept?$key&elconf=$elconf> <b> accept config </b><br></a>\n";
+    print STDOUT "</td><td width=640px align=right><img width=100% src=$imbin></td></tr></table>\n";
+    
+    print STDOUT "<center><b>Statistics on each available input image.</b></center>
+  Time is in UT, light background means dawn, grey means dusk. Click
+  on the column heading to sort by that entry\n";
+
+    print STDOUT "<table bgcolor=#fff0e0 border=1 cellspacing=0 cellpadding=3>\n";
+    print STDOUT "<tr bgcolor=#00aaaa>\n";
+    print STDOUT "<th > <a href=$CGI/dt.elixir2?$key.0&elconf=$elconf> image </a></th>\n";
+    print STDOUT "<th > Date </th>\n";
+    print STDOUT "<th > Time </th>\n";
+    print STDOUT "<th > <a href=$CGI/dt.elixir2?$key.3&elconf=$elconf> Mean </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/dt.elixir2?$key.5&elconf=$elconf> Sigma (raw) </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/dt.elixir2?$key.6&elconf=$elconf> Sigma (clipped) </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/dt.elixir2?$key.8&elconf=$elconf> Sigma (med) </a></th>\n";
+    print STDOUT "<th > image </th>\n";
+    print STDOUT "<th > used? </th></tr>\n\n";
+    
+    @order = ();
+    for ($i = 0; $i < $Nimage; $i++) { @order = (@order, $i); }
+    
+    @entry = sort by_col @order;
+    
+    for ($i = 0; $i < $Nimage; $i++) {
+	$N = $entry[$i];
+	
+	@word = split (" ", $statdata[$N]);
+	$used = $word[12];
+	$dawn = $word[11];
+	
+	@tmpw = split (" ", $mastdata[$N]);
+	$keep = $tmpw[1];
+	
+	@tmpw = split ("/", $medndata[$N]);
+	$medname = $tmpw[-1];
+	
+	print STDOUT "<tr><td> <a href=$CGI/dt.elixir3?$N.$key&elconf=$elconf> $word[0] </a></td><td> $word[1] </td>\n";
+	if ($dawn) { print STDOUT "<td bgcolor=#cccccc> $word[2] </td>\n"; }
+	else       { print STDOUT "<td> $word[2] </td>\n"; }
+	print STDOUT "<td> $word[3] </td>\n"; # mean
+	print STDOUT "<td> $word[5] </td>\n"; # Sigma (raw)
+	print STDOUT "<td> $word[6] </td>\n"; # Sigma (clip)
+	print STDOUT "<td> $word[8] </td>\n"; # Sigma (med)
+	print STDOUT "<td> <a border=0 href=$CGI/dt.elixir3?$N.$key&elconf=$elconf>\n";
+	print STDOUT "<img src=$www/$medname></a></td>\n";
+	if ($used) { print STDOUT "<td>yes</td>\n"; }
+	else       { print STDOUT "<td>no</td>\n"; }
+	print STDOUT "</tr>\n";
+    }
+    
+    print STDOUT "</table></body>\n";
+    &goodbye;
+    
+}
+
+#### process report for html2 ##########
+sub flips_report_done_old {
+    
+    ($config) = $_[0] =~ /(\w+.\w+.\w+)/;
+    ($key)    = $_[0] =~ /(\w+.\w+.\w+.\w+)/; # (config.ver)
+    $line = $_[1];
+    
+    open (STAT, mknames ("stats", $line));
+    open (MED,  mknames ("medbin", $line));
+    open (MASTER, mknames ("master", $line, $ccds[0]));
+    
+    print STDOUT "<table border>\n";
+    print STDOUT "<tr><th> image </th><th> Date </th><th> Time </th><th> Mean </th>\n";
+    print STDOUT "<th> Sigma (raw) </th><th> Sigma (clipped) </th><th> Sigma (med) </th><th> image </th><th> used? </th></tr>\n\n";
+    
+    for ($i = 0; $line = <STAT>; $i++) {
+	
+	chop ($line);
+	# print STDOUT "$line<br>\n";
+	@word = split (" ",$line);
+	$used = $word[12];
+	$dawn = $word[11];
+	
+	$line = <MASTER>;
+	chop ($line);
+	# print STDOUT "$line<br>\n";
+	@tmpw = split (" ",$line);
+	$keep = $tmpw[1];
+	
+	# mkdetrend works with files in the user's paths, but the web page needs 
+	# to work with files visible to the web server
+	$line = <MED>;
+	chop ($line);
+	@tmpw = split ("/", $line);
+	$N = @tmpw - 1;
+	$medname = $tmpw[$N];
+	
+	$sig1 = sprintf "%5.3f", $word[4];
+	$sig2 = sprintf "%5.3f", $word[5];
+	$sig3 = sprintf "%5.3f", $word[6];
+	$sig4 = sprintf "%5.3f", $word[7];
+	$mean = sprintf "%8.1f", $word[3];
+	
+	print STDOUT "<tr><td> <a href=$CGI/dt.elixir3?$i.$key&elconf=$elconf> $word[0] </a></td><td> $word[1] </td>\n";
+	if ($dawn) { print STDOUT "<td bgcolor=#cccccc> $word[2] </td>\n"; }
+	else       { print STDOUT "<td> $word[2] </td>\n"; } 
+	print STDOUT "<td> $word[3] </td>\n";
+	print STDOUT "<td> $word[5] </td><td> $word[6] </td><td> $word[8] </td><td><img src=$www/$medname></td>\n";
+	if ($used) { print STDOUT "<td bgcolor=#00c000>yes</td>\n"; }
+	else       { print STDOUT "<td bgcolor=#d00000>no</td>\n"; }
+    }
+    $Nimage = $i;
+    
+    print STDOUT "</table>\n";
+    print STDOUT "</body>\n";
+    
+    &goodbye;
+    
+}
+
+#### process report for html3 ##########
+sub flips_entry {
+    
+    my($version)= $_[0];		# version of desired entry
+    my($entry)  = $_[1];		# desired entry in list
+    my($line)   = $_[2];		# list of image stats
+    
+    open (STAT, mknames ("stats", $line));
+    open (TEN,  mknames ("tenbin", $line));
+    
+    for ($i = 0; ($i < $entry + 1) && ($line = <STAT>) && ($tenline = <TEN>); $i++) {}
+    if ($i != $entry + 1) { &escape ("</table> selected entry out of range"); }
+    
+    $first = ($entry == 0);
+    $tmp = <STAT>;
+    $last = ($tmp == "");
+    $next = $entry + 1;
+    $prev = $entry - 1;
+    
+    chop ($line);
+    @word = split (" ",$line);
+    $keep = $word[12];
+    
+    # mkdetrend works with files in the user's paths, but the web page needs 
+    # to work with files visible to the web server
+    chop ($tenline);
+    @tmpw = split ("/", $tenline);
+    $N = @tmpw - 1;
+    $name = $tmpw[$N];
+    
+    $sig1 = sprintf "%5.3f", $word[4];
+    $sig2 = sprintf "%5.3f", $word[5];
+    $sig3 = sprintf "%5.3f", $word[6];
+    $sig4 = sprintf "%5.3f", $word[7];
+    $mean = sprintf "%8.1f", $word[3];
+    
+    print STDOUT "<img width=100% src=$www/$name>\n";
+    
+    print STDOUT "<table><tr><td>\n";
+    print STDOUT "<table border>\n";
+    print STDOUT "<tr><th> image </th><th> Date </th><th> Time </th><th> Mean </th>\n";
+    print STDOUT "<th> Sigma (raw) </th><th> Sigma (clipped) </th><th> Sigma (med) </th><th> used? </th></tr>\n\n";
+    
+    print STDOUT "<tr><td> $word[0] </a></td><td> $word[1] </td><td> $word[2] </td><td> $word[3] </td>\n";
+    print STDOUT "<td> $word[5] </td><td> $word[6] </td><td> $word[8] </td>\n";
+    if ($keep) {
+	print STDOUT "<td>yes</td>\n";
+    } else {
+	print STDOUT "<td bgcolor=#ff0000>no</td>\n";
+    }	
+    print STDOUT "</tr>\n\n";
+    print STDOUT "</table>\n";
+    
+    print STDOUT "</td></tr>\n";
+
+    print STDOUT "<tr><td> return to <a href=$CGI/dt.elixir1&elconf=$elconf> config list </a></tr></td>\n"; 
+    print STDOUT "<tr><td> return to <a href=$CGI/dt.elixir2?$version&elconf=$elconf> $version </a></tr></td>\n"; 
+    if (!$first) { print STDOUT "<tr><td> goto <a href=$CGI/dt.elixir3?$prev.$version&elconf=$elconf> prev </a> image </td></tr>\n"; }
+    if (!$last)  { print STDOUT "<tr><td> goto <a href=$CGI/dt.elixir3?$next.$version&elconf=$elconf> next </a> image </td></tr>\n"; }
+    print STDOUT "</table>\n";
+    
+    print STDOUT "</body>\n";
+    &goodbye;
+}
+
+#################### functions to abstract file databases ###################
+
+# set config value
+# TYPE FILTER CRUNID VERSION START STOP STATE
+# CRUNID TYPE FILTER [CCD] VERSION START STOP STATE
+#**************************
+sub stconfig {
+    # in: (config line) (value) (type)
+    my (@words) = split (" ", $_[0]);
+    my ($type)  = $_[1];
+    my ($value) = $_[2];
+    my ($line);
+
+    $format = "%s %6s %5s %2d %s %s %s";
+
+    { 
+	if ($type eq "all") {
+	    $line = sprintf $format, $words[0], $words[1], $words[2], $words[3], $words[4], $words[5], $words[6];
+	    last;
+	}
+	if ($type eq "version") {
+	    $line = sprintf $format, $words[0], $words[1], $words[2], $value, $words[4], $words[5], $words[6];
+	    last;
+	}
+	if ($type eq "start") {
+	    $line = sprintf $format, $words[0], $words[1], $words[2], $words[3], $value, $words[5], $words[6];
+	    last;
+	}
+	if ($type eq "stop") {
+	    $line = sprintf $format, $words[0], $words[1], $words[2], $words[3], $words[4], $value, $words[6];
+	    last;
+	}
+	if ($type eq "status") {
+	    $line = sprintf $format, $words[0], $words[1], $words[2], $words[3], $words[4], $words[5], $value;
+	    last;
+	}
+	&escape ("unknown type $type for stconfig\n");
+    }
+
+    return $line;
+}
+
+# load config lines (returns @list)
+sub load_config {
+    my ($file, $line, @list);
+    
+    $file = mkfiles ("config");
+    @list = ();
+    open (FILE, "$file");
+    while ($line = <FILE>) {
+	chop ($line);
+	@list = (@list, $line);
+    }
+    close (FILE);
+    
+    @list;
+}
+
+# write to config file
+sub save_config {
+
+    my(@outlist) = sort @_;
+    
+    my($file) = mkfiles ("config");
+    open (FILE, ">$file");
+    for ($i = 0; $i < @outlist; $i++) {
+	print FILE "$outlist[$i]\n";
+    }
+    close (FILE);
+}    
+
+# load dates & mode (returns @list)
+sub load_dates {
+    my ($file, $line, @list);
+    my ($start, $stop, $mode);
+    
+    $file = mkfiles ("dates");
+    @list = ();
+    open (FILE, "$file");
+    @list = <FILE>;
+    close (FILE);
+
+    ($tmp, $start) = split (" ", $list[0]);
+    if ($tmp ne "START") { print STDERR "dates file is confused\n"; }
+
+    ($tmp, $stop) = split (" ", $list[1]);
+    if ($tmp ne "STOP") { print STDERR "dates file is confused\n"; }
+    
+    ($tmp, $mode) = split (" ", $list[2]);
+    if ($tmp ne "MODE") { print STDERR "dates file missing mode\n"; }
+    if ($mode eq "") { $mode = "hold"; }
+
+    ($start, $stop, $mode);
+}
+
+# write to dates file
+sub save_dates {
+    # save_dates start stop auto.mode
+    my (@outlist) = @_;
+
+    my ($file) = mkfiles ("dates");
+    open (FILE, ">$file");
+    print FILE "START $outlist[0]\n";
+    print FILE "STOP  $outlist[1]\n";
+    print FILE "MODE  $outlist[2]\n";
+    close (FILE);
+}    
+
+# $root/detrend.config    - current run
+# $det/config.dat         - config info for specific run, 1 line per setup
+# $det/dates.dat          - dates for specific run
+# $det/detrend.list       - temporary list to pass to elixirs
+# $det/flips              - directory to store temporary data
+# $det/split              - directory to store temporary images
+# $det/html               - directory to store results for www
+# $www/$run/html          - html directory as seen by Web server
+
+# construct specific file names
+sub mkfiles {
+    # in: (type)
+    my($type) = $_[0];
+    my($value);
+    
+    { 
+	if ($type eq "run") {
+	    $value = "$root/detrend.config";
+	    last;
+	}
+	if ($type eq "detdir") {
+	    $value = "$det";
+	    last;
+	}
+	if ($type eq "config") {
+	    $value = "$det/config.dat";
+	    last;
+	}
+	if ($type eq "dates") {
+	    $value = "$det/dates.dat";
+	    last;
+	}
+	if ($type eq "list") {
+	    $value = "$det/detrend.list";
+	    last;
+	}
+	if ($type eq "html") {
+	    $value = "$det/html";
+	    last;
+	}
+	if ($type eq "stats") {
+	    $value = "$det/stats";
+	    last;
+	}
+	if ($type eq "flips") {
+	    $value = "$det/flips";
+	    last;
+	}
+	if ($type eq "links") {
+	    $value = "$det/flips/links";
+	    last;
+	}
+	if ($type eq "split") {
+	    $value = "$det/split";
+	    last;
+	}
+	if ($type eq "www") {
+	    $value = "$www";
+	    last;
+	}
+	&escape ("unknown type $type for mkfiles");
+    }
+    $value;    
+}
+
+# construct files from config info:
+# CRUNID TYPE FILTER [CCD] VERSION START STOP STATE
+#**************************
+
+sub mknames {
+    # in: (type) (config.ver) [ccd]
+    my ($type, $version, @words, $dir, $value, $ccd);
+
+    $type = $_[0];
+    $version = $_[1];
+    @words = split (" ", $version);
+    
+    { 
+	# final versions in html dir:
+	if ($type eq "stats") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].stats";
+	    last;
+	}
+	if ($type eq "medbin") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].medbin";
+	    last;
+	}
+	if ($type eq "tenbin") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].tenbin";
+	    last;
+	}
+	if ($type eq "imbin") {
+	    $dir = mkfiles ("www");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].imbin.000.jpg";
+	    last;
+	}
+	if ($type eq "dads.imbin") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].imbin.000.jpg";
+	    last;
+	}
+	# files used for processing:
+	if ($type eq "master") {
+	    $dir = mkfiles ("flips");
+	    $ccd = $_[2];
+	    if ($ccd eq "") { &escape ("missing CCD for mknames master"); }
+	    $value = "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].master";
+	    last;
+	}
+	if ($type eq "msplit") {
+	    $dir = mkfiles ("flips");
+	    $ccd = $_[2];
+	    if ($ccd eq "") { &escape ("missing CCD for mknames master"); }
+	    $value = "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].msplit";
+	    last;
+	}
+	if ($type eq "norm") {
+	    $dir = mkfiles ("flips");
+	    $ccd = $_[2];
+	    if ($ccd eq "") { &escape ("missing CCD for mknames norm"); }
+	    $value = "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].norm";
+	    last;
+	}
+	if ($type eq "fits") {
+	    $dir = mkfiles ("flips");
+	    $ccd = $_[2];
+	    if ($ccd eq "") { &escape ("missing CCD for mknames fits"); }
+	    $value = "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].fits";
+	    last;
+	}
+	if ($type eq "link") {
+	    $dir = mkfiles ("links");
+	    $ccd = $_[2];
+	    if ($ccd eq "") { &escape ("missing CCD for mknames fits"); }
+	    $value = "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].fits";
+	    last;
+	}
+	if ($type eq "mef") {
+	    $dir = mkfiles ("flips");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].fits";
+	    last;
+	}
+	if ($type eq "proc.medbin") {
+	    $dir = mkfiles ("flips");
+	    $ccd = $_[2];
+	    if ($ccd eq "") { &escape ("missing CCD for mknames proc.medbin"); }
+	    $value = "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].medbin";
+	    last;
+	}
+	if ($type eq "proc.tenbin") {
+	    $dir = mkfiles ("flips");
+	    $ccd = $_[2];
+	    if ($ccd eq "") { &escape ("missing CCD for mknames proc.tenbin"); }
+	    $value = "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].tenbin";
+	    last;
+	}
+	if ($type eq "proc.imbin") {
+	    $dir = mkfiles ("flips");
+	    $ccd = $_[2];
+	    if ($ccd eq "") { &escape ("missing CCD for mknames proc.imbin"); }
+	    $value = "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].imbin";
+	    last;
+	}
+	&escape ("unknown type $type for mknames");
+    }    
+    return ($value);
+}
+
+# parse config info
+# TYPE FILTER CRUNID VERSION START STOP STATE
+# CRUNID TYPE FILTER [CCD] VERSION START STOP STATE
+#**************************
+sub gtconfig {
+    my ($value, $type, @words);
+    # in: (config line) (type)
+    @words = split (" ", $_[0]);
+    $type = $_[1];
+
+    { 
+	if ($type eq "elixir") {
+	    $value = "$words[0] $words[1] $words[2] $words[3]";
+	    last;
+	}
+	if ($type eq "mosaic") {
+	    $ccd = "xx";
+	    $value = "$words[0] $words[1] $words[2] $ccd $words[3] $words[4] $words[5]";
+	    last;
+	}
+	if ($type eq "chiprun") {
+	    $ccd  = $_[2];
+	    $value = "$words[0] $words[1] $words[2] $ccd $words[3] $words[4] $words[5]";
+	    last;
+	}
+	if ($type eq "config") {
+	    $value = "$words[0].$words[1].$words[2]";
+	    last;
+	}
+	if ($type eq "version") {
+	    $value = "$words[0].$words[1].$words[2].$words[3]";
+	    last;
+	}
+	if ($type eq "ID") {
+	    $value = "$words[0]";
+	    last;
+	}
+	if ($type eq "type") {
+	    $value = "$words[1]";
+	    last;
+	}
+	if ($type eq "filter") {
+	    $value = "$words[2]";
+	    last;
+	}
+	if ($type eq "from") {
+	    $value = "$words[4]";
+	    last;
+	}
+	if ($type eq "to") {
+	    $value = "$words[5]";
+	    last;
+	}
+	if ($type eq "status") {
+	    $value = "$words[6]";
+	    last;
+	}
+	&escape ("unknown type for gtconfig: $type");
+    }
+    
+    return $value;
+}
+
+# grab complete configuration info from the elixir config system, 
+# applying appropriate command-line arguments
+sub elixir_config {
+    my (@argv) = @_;
+    my ($opt, $config, @tmparg, $status, $keyword, $value);
+    $config = "";
+    if ($ENV{'PTOLEMY'}) { $config = "-c $ENV{'PTOLEMY'}"; }
+    umask (0);
+
+    # look for optional command-line arguments
+    while (@argv) {
+	$value = shift @argv;
+
+	if ($value eq "-c") {
+	    $value = shift @argv;
+	    $config = "-c $value";
+	    next;
+	}
+
+	if ($value eq "-C") {
+	    $elconf = shift @argv;
+	    push @opt, "-C $elconf";
+	    next;
+	}
+	
+	if ($value eq "-D") {
+	    $keyword = shift @argv;
+	    $value = shift @argv;
+	    push @opt, "-D $keyword $value";
+	    next;
+	}
+	
+	if ($value eq "-run") {
+	    $run = shift @argv;
+	    push @opt, "-D RUNID.MKDETREND $run -D RUNID $run";
+	    next;
+	}
+	
+	if ($value eq "-camera") {
+	    $camera = shift @argv;
+	    push @opt, "-D CAMERA.MKDETREND $camera -D CAMERA $camera";
+	    next;
+	}
+	push @argt, $value;
+    }
+    @argv = @argt;
+
+    # load RUNID from state.data (unless set on command line)
+    if ($run eq "") {
+	$run = `gconfig RUNID.MKDETREND`; chop $run;
+	if ($?) {  &escape ("run is not defined: use '$MKDETREND config' or 'mkrun sys'\n"); }
+	if ($run eq "") {  &escape ("run is not defined: use '$MKDETREND config' or 'mkrun sys'\n"); }
+	push @opt, "-D RUNID.MKDETREND $run -D RUNID $run";
+    }
+
+    # load CAMERA from state.data (unless set on command line)
+    if ($camera eq "") {
+	$camera = `gconfig CAMERA.MKDETREND`; chop $camera;
+	if ($?) {  &escape ("camera is not defined: use 'mkrun sys' to set\n"); }
+	if ($camera eq "") {  &escape ("camera is not defined: use 'mkrun sys'\n"); }
+	push @opt, "-D CAMERA.MKDETREND $camera -D CAMERA $camera";
+    }
+
+    # set env PTOLEMY using values from command line
+    $config = join (" ", $config, @opt);
+    $tmpenv = `mktemp /tmp/elixir.XXXXXX`; chop ($tmpenv);
+    $status = system ("gconfig -raw $config > $tmpenv");
+    if ($status) { &escape ("error in elixir configuration: gconfig fails"); }
+    $ENV{'PTOLEMY'} = "$tmpenv";
+
+    # set global variables based on new config info
+    $root = `gconfig DETREND_ROOT`; chop $root;
+    if ($?) { &escape ("error with elixir configuration variable: DETREND_ROOT"); }
+
+    $wwwroot  = `gconfig DETREND_WWW`; chop $wwwroot;
+    if ($?) { &escape ("error with elixir configuration variable: DETREND_WWW"); }
+    
+    $Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+    if ($?) { &escape ("error with elixir camera configuration"); }
+
+    @ccds = split (" ", `cameraconfig -ccdn`);
+    if ($?) { &escape ("error with elixir camera configuration"); }
+
+    # get list of valid filters from lookup table
+    $answer = `filtnames list`;
+    if ($?) { &escape ("error with elixir filter configuration"); }
+
+    # these are the 'flat' types to create:
+    @filt = split (" ", $answer);
+    @type = ();
+    foreach $name (@filt) { push @type, "flat"; }
+    
+    # bias and dark use the exposure time as the 'filter'
+    @type = (@type, 'bias', 'dark', 'dark', 'dark', 'dark');
+    @filt = (@filt, '0',    '60',   '300',  '900',  '1800');
+    # determine dark times from existing data?
+
+    $CGI = `gconfig CGIBIN`; chop ($CGI);
+    if ($?) { &escape ("error with elixir configuration variable: CGIBIN"); }
+
+    $det  = "$root/$run";
+    $www  = "$wwwroot/$run/html";
+    $dadswww = "img";
+
+    return (@argv);
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye { 
+    unlink $tmpenv;
+    exit 0; 
+}
+
+sub escape { 
+    unlink $tmpenv;
+    print "$_[0]\n";
+    exit 1;
+}
+
+sub usage {
+    if (@ARGV == 1) {
+	print STDERR "\n --- user modes ---\n";
+	print STDERR "create (run) (startdate) (stopdate)\n";
+	print STDERR "config (run)\n";
+	print STDERR "init\n";
+	print STDERR "run\n";
+	print STDERR "list.runs\n";
+	print STDERR "set (config.ver) (state)\n";
+	print STDERR "reg\n";
+	print STDERR "state \n";
+	print STDERR "help \n";
+	print STDERR "\n --- internal modes --- \n";
+	print STDERR "flips\n";
+	print STDERR "norm\n";
+	print STDERR "update\n";
+	print STDERR "merge\n";
+	print STDERR "dup (config)\n";
+	print STDERR "del (config)\n";
+	print STDERR "def (config.ver) (start) (stop)\n";
+	print STDERR "eval (step)\n";
+	print STDERR "mevel (step)\n";
+	print STDERR "htmldup\n";
+	print STDERR "htmldel\n";
+	print STDERR "htmldef\n";
+	print STDERR "htmlmod\n";
+	print STDERR "htmlkeep\n";
+	print STDERR "html1\n";
+	print STDERR "html2 (config.ver)\n";
+	print STDERR "html3 (entry.config.ver)\n";
+	&escape;
+    }
+    
+    if (@ARGV > 2) { print STDERR "USAGE: $MKDETREND (help) [mode]\n"; &escape; }
+    
+    if ($ARGV[1] eq "config") {
+	print STDERR "set up initial configuration\n";
+	&escape;
+    }
+    
+    if ($ARGV[1] eq "save") {
+	print STDERR "save results from current configuration\n";
+	&escape;
+    }
+    
+    print STDERR "help for $ARGV[1] no defined\n";
+    &escape;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkfringe
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkfringe	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkfringe	(revision 22322)
@@ -0,0 +1,2811 @@
+#!/usr/bin/perl
+$MKFRINGE = mkfringe;
+
+# strip off args related to the elixir config system, set env PTOLEMY
+@ARGV = elixir_config (@ARGV);
+
+# interpret command-line arguments
+if (@ARGV < 1) {
+    print STDOUT "USAGE: $MKFRINGE (mode) [arguments]\n";
+    print STDOUT "mode = help gives a complete list\n";
+    &escape;
+}
+if ($ARGV[0] eq "help") { &usage; }
+
+# user level commands
+if ($ARGV[0] eq "create")     { &mk_create;     }
+if ($ARGV[0] eq "config")     { &mk_config;     }
+if ($ARGV[0] eq "init")       { &mk_init;       }
+if ($ARGV[0] eq "run")        { &mk_run;        }
+if ($ARGV[0] eq "state")      { &mk_state;      }
+if ($ARGV[0] eq "mkconfig")   { &mk_mkconfig;   }
+if ($ARGV[0] eq "list.runs")  { &mk_list_runs;  }
+if ($ARGV[0] eq "clean")      { &mk_cleanup;    }
+if ($ARGV[0] eq "set")        { &mk_set;        }
+if ($ARGV[0] eq "reg")        { &mk_reg;        }
+
+if ($ARGV[0] eq "dup")        { &mk_dup;        }
+if ($ARGV[0] eq "del")        { &mk_del;        }
+if ($ARGV[0] eq "def")        { &mk_def;        }
+if ($ARGV[0] eq "list.init")  { &mk_list_init;  }
+if ($ARGV[0] eq "list.reinit")  { &mk_list_reinit;  }
+if ($ARGV[0] eq "detrend")    { &mk_detrend;    }
+if ($ARGV[0] eq "merge")      { &mk_merge;      }
+if ($ARGV[0] eq "mkrough")    { &mk_mkrough;    }
+if ($ARGV[0] eq "mksmooth")   { &mk_smooth;    }
+if ($ARGV[0] eq "regimage")   { &mk_regimage;    }
+if ($ARGV[0] eq "defringe")   { &mk_defringe;   }
+
+if ($ARGV[0] eq "map")        { &mk_map;        }
+if ($ARGV[0] eq "map.reg")    { &mk_map_reg;    }
+
+# HTML cgibin commands
+if ($ARGV[0] eq "htmldup")    { &mk_htmldup;    }
+if ($ARGV[0] eq "htmldel")    { &mk_htmldel;    }
+if ($ARGV[0] eq "htmldef")    { &mk_htmldef;    }
+if ($ARGV[0] eq "htmlmod")    { &mk_htmlmod;    }
+if ($ARGV[0] eq "htmlmodes")  { &mk_htmlmodes;  }
+if ($ARGV[0] eq "htmlmaps")   { &mk_htmlmaps;   }
+if ($ARGV[0] eq "htmlkeep")   { &mk_htmlkeep;   }
+if ($ARGV[0] eq "htmlmapkeep"){ &mk_htmlmapkeep;}
+if ($ARGV[0] eq "htmlconfig") { &mk_htmlconfig; }
+if ($ARGV[0] eq "html1")      { &mk_html1;      }
+if ($ARGV[0] eq "html2")      { &mk_html2;      }
+if ($ARGV[0] eq "html3")      { &mk_html3;      }
+# support commands
+if ($ARGV[0] eq "dads")       { &mk_dads;       }
+if ($ARGV[0] eq "dads.top")   { &mk_dads_top;   }
+if ($ARGV[0] eq "eval")       { &mk_eval;       }
+
+&escape ("invalid mkfring command $ARGV[0]");
+
+############# define/redefine config ##############
+sub mk_create {
+    
+    if ($ARGV[1] eq "run") { &mk_createrun; }
+    if (@ARGV != 5) { &escape ("USAGE: $MKFRINGE create (camera) (run) (startdate) (stopdate)"); }
+    
+    # setting $run & $start here overrides default values loaded by set_globals()
+    $camera = $ARGV[1];
+    $run    = $ARGV[2];
+    $start  = $ARGV[3];
+    $stop   = $ARGV[4];
+
+    if (0) {
+	# set current run id:
+	system ("mkrun sys camera.mkfringe $camera");
+	if ($?) { &escape ("can't define camera: error in mkrun"); }
+    
+	# set current camera:
+	system ("mkrun sys runid.mkfringe $run");
+	if ($?) { &escape ("can't define run: error in mkrun"); }
+    }
+
+    # reset values for the directories, etc.
+    $oldenv = $tmpenv;
+    &elixir_config (split (" ", "-run $run -camera $camera"));
+    unlink ($oldenv);
+
+    # create the new detdir (& subdirectories?)
+    $file = mkfiles ("detdir");
+    if (! -d $file) {
+	if (! mkdir ($file, 0777)) {
+	    &escape ("can't create detdir $file");
+	}
+    }
+    ($tmp, $tmp, $mode) = &load_dates;
+    &save_dates ($start, $stop, $mode);
+
+    # create run-specific config file
+    @list = ();
+    for ($i = 0; $i < @filt; $i++) {
+	$line = stconfig ("$run $type[$i] $filt[$i] 0 $start $stop init", "all");
+	@list = (@list, $line);
+    }
+    save_config (@list);
+    
+    &goodbye;
+}
+
+############# define/redefine config ##############
+sub mk_createrun {
+    
+    if (@ARGV != 3) { &escape ("USAGE: $MKFRINGE create run (run)"); }
+    if ($ARGV[1] ne "run") { &escape ("USAGE: mkdetrend create run (run)"); }
+    
+    # setting $run & $start here overrides default values loaded by set_globals()
+    $run    = $ARGV[2];
+    $answer = `mkrun run $run`;
+    if ($?) { &escape ("run $run not defined, use 'mkrun'"); }
+    ($newrun, $start, $stop, $camera) = split (" ", $answer);
+    if ($run ne $newrun) { &escape ("inconsistent answer from mkrun run $run"); }
+
+    # reset values for the directories, etc.
+    $oldenv = $tmpenv;
+    &elixir_config (split (" ", "-run $run -camera $camera"));
+    unlink ($oldenv);
+
+    # create the new detdir (& subdirectories?)
+    $file = mkfiles ("detdir");
+    if (! -d $file) {
+	if (! mkdir ($file, 0777)) {
+	    &escape ("can't create detdir $file");
+	}
+    }
+    ($tmp, $tmp, $mode) = &load_dates;
+    &save_dates ($start, $stop, $mode);
+
+    # create run-specific config file
+    @list = ();
+    for ($i = 0; $i < @filt; $i++) {
+	$line = stconfig ("$run $type[$i] $filt[$i] 0 $start $stop init", "all");
+	@list = (@list, $line);
+    }
+    save_config (@list);
+    
+    &goodbye;
+}
+
+############# change current run ##############
+sub mk_config {
+    
+    if (@ARGV != 3) { &escape ("USAGE: $MKFRINGE config (camera) (run)"); }    
+
+    $camera = $ARGV[1];
+    $run    = $ARGV[2];
+
+    # set current run id:
+    system ("mkrun sys camera.mkfringe $camera");
+    if ($?) { &escape ("can't define camera: error in mkrun"); }
+    
+    # set current run id:
+    system ("mkrun sys runid.mkfringe $run");
+    if ($?) { &escape ("can't define run: error in mkrun"); }
+    
+    &goodbye;
+}
+
+############# init ##############
+sub mk_init {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE run"); }
+
+    system "$MKFRINGE list.init\n";
+    system "$MKFRINGE detrend\n";
+    system "$MKFRINGE merge\n";
+    &goodbye;
+}
+
+############# main run ##############
+sub mk_run {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE run"); }
+    
+    system "$MKFRINGE mkrough";
+    system "$MKFRINGE defringe";
+    system "$MKFRINGE merge";
+    
+    &goodbye;
+}
+
+############# main run ##############
+sub mk_reg {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE reg"); }
+    
+    system "$MKFRINGE mksmooth";
+    system "$MKFRINGE regimage";
+    
+    &goodbye;
+}
+
+############# show current state ##############
+sub mk_state {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE state"); }
+    
+    ($start, $stop, $mode) = &load_dates;
+
+    print STDOUT "CAMERA    $camera\n";
+    print STDOUT "RUN       $run\n";
+    print STDOUT "START     $start\n";
+    print STDOUT "STOP      $stop\n";
+    print STDOUT "MODE      $mode\n";
+    
+    @list = &load_config;
+    if (@list == 0) { &escape ("$MKFRINGE not configured"); }
+
+    for ($i = 0; $i < @list; $i++) {
+	printf STDOUT "%3d %s\n", $i, $list[$i];
+    }
+    &goodbye;
+}
+
+############# show existing runs ##############
+sub mk_list_runs {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE list.runs"); }
+    
+    system ("ls $root");
+    &goodbye;
+}
+
+############# create config list ##############
+sub mk_mkconfig {
+    
+    # create mkfringe config file from existing date entries
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE mkconfig"); }
+    
+    # if new run, need to create the new detdir
+    $file = mkfiles ("detdir");
+    if (! -d $file) {
+	if (! mkdir ($file, 0777)) {
+	    &escape ("can't create detdir $file");
+	}
+    }
+
+    ($start, $stop, $mode) = &load_dates;
+    if (($start eq "") || ($stop eq "")) { &escape ("dates are not defined: use '$MKFRINGE config'"); }
+
+    # create run-specific config file
+    @list = ();
+    for ($i = 0; $i < @filt; $i++) {
+	$line = stconfig ("$run $type[$i] $filt[$i] 0 $start $stop init", "all");
+	@list = (@list, $line);
+    }
+    save_config (@list);
+    &goodbye;
+}
+
+############## duplicate config ###############
+sub mk_dup {
+    
+    if (@ARGV != 2) { &escape ("USAGE: $MKFRINGE dup (config)"); }
+    
+    $key = $ARGV[1];
+    @list = load_config ();
+    
+    # look for entry that matches requested config.ver
+    @match = ();
+    for ($i = 0; $i < @list; $i++) {
+	$config = gtconfig ($list[$i], "config");
+	if ($key eq $config) { @match = (@match, $i); }
+    }
+    if (@match == 0) { &escape ("config.ver $key not found"); }
+    
+    # create new config version based on config version # 0
+    $Nnew = @match;
+    $old = $list[$match[0]];
+    $new = stconfig ($old, "version", $Nnew);
+    @list = (@list, $new);
+    save_config (@list);
+    
+    # now we need to duplicate all of the reference files from the first config 
+    &goodbye;
+}
+
+############## cleanup config ###############
+sub mk_cleanup {
+    
+    if ((@ARGV != 1) && (@ARGV != 2)) { &escape ("USAGE: $MKFRINGE cleanup [mode]"); }
+
+    @list = load_config ();
+
+    $detdir = mkfiles ("detdir");
+    open (CF, ">$detdir/clean.fail");
+
+    $Nmiss = 0;
+    $Nfail = 0;
+    $Ngood = 0;
+
+    $mode = "partial"; 
+    if (@ARGV == 2) { $mode = $ARGV[1]; }
+
+    if (($mode ne "partial") && ($mode ne "total")) { &escape ("invalid cleanup mode $mode: use 'partial' or 'total'"); }
+
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "done") { next; }
+
+	print STDERR "deleting files from $confline\n";
+	$confline = stconfig ($confline, "status", "running.clean");
+	save_config (@list);
+
+	foreach $ccd (@ccds) { 
+	    @dlist = ();
+
+	    # delete these specific files:
+	    push @dlist, &mknames ("frfile", $confline, $ccd); 
+	    push @dlist, &mknames ("smfile", $confline, $ccd); 
+
+	    # delete file, if it exists
+	    foreach $file (@dlist) { 
+		if (! -e $file) { $Nmiss ++; next; }
+		if (!unlink $file) { $Nfail ++; print CF "$file\n"; next; }
+		$Ngood ++;
+	    }
+	}
+
+	# delete final MEF image
+	@dlist = ();
+	push @dlist, &mknames ("mef", $confline); 
+
+	# delete proc defringe, medbin images
+	$name = &mknames ("detrend", $confline);
+	open (FILE, $name);
+	@deflist = <FILE>;
+	close (FILE);
+
+	$proc = &mkfiles ("proc");
+	foreach $line (@deflist) {
+	    ($tmp, $root) = split (" ", $line);
+	    push @dlist, <$proc/$root.med/*.fits>;
+	    push @dlist, <$proc/$root.def/*.fits>;
+	}	    
+
+	# delete file, if it exists
+	foreach $file (@dlist) { 
+	    if (! -e $file) { $Nmiss ++; next; }
+	    if (!unlink $file) { $Nfail ++; print CF "$file\n"; next; }
+	    $Ngood ++;
+	}
+
+	$confline = stconfig ($confline, "status", "clean");
+	save_config (@list);
+    }
+    close (CF);
+
+    print STDERR "$Ngood files deleted\n";
+    print STDERR "$Nmiss files already removed\n";
+    print STDERR "$Nfail files not deleted, see '$detdir/clean.fail'\n";
+
+    &goodbye;
+}
+
+
+
+############## delete config ###############
+sub mk_del {
+    
+    if (@ARGV != 2) { &escape ("USAGE: $MKFRINGE del (config.ver)"); }
+    
+    $key = $ARGV[1];
+    @list = load_config ();
+    
+    # look for entry that matches requested config
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}	
+    }
+    if ($match == -1) { &escape ("config.ver $key not found"); }
+    
+    # create new list without match:
+    @newlist = ();
+    for ($i = 0; $i < @list; $i++) {
+	if ($i == $match) { next; }
+	@newlist = (@newlist, $list[$i]);
+    }
+    
+    save_config (@newlist);
+    &goodbye;
+    
+}
+
+
+
+############## define config ###############
+sub mk_def {
+    
+    if (@ARGV != 4) { &escape ("USAGE: $MKFRINGE def (config.ver) (start) (stop)"); }
+    
+    $key   = $ARGV[1];
+    $start = $ARGV[2];
+    $stop  = $ARGV[3];
+    
+    @list = load_config ();
+    
+    # find specific config:
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	}	
+    }
+    if (@match == -1) { &escape ("config.ver $key not found"); }
+    
+    $new = $list[$match];
+    $new = stconfig ($new, "start", $start);
+    $new = stconfig ($new, "stop", $stop);
+    $list[$match] = $new;
+    save_config (@list);
+
+    &goodbye;
+}
+
+
+
+############## set state ###############
+sub mk_set {
+    
+    if (@ARGV != 3) { &escape ("USAGE: $MKFRINGE set (config number) (state)"); }
+    
+    @list = load_config ();
+    
+    $key = $ARGV[1];
+    $state = $ARGV[2];
+    
+    $new = stconfig ($list[$key], "status", $state);
+    $list[$key] = $new;
+    
+    save_config (@list);
+    
+    &goodbye;
+}
+
+############## init run ###############
+
+
+# get valid images, create static lists
+sub mk_list_init {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE list.init"); }
+    
+    # create necessary directories
+    foreach $name ("html", "proc", "fringe") {
+	$dir = mkfiles ($name);
+	if (! -e $dir) {
+	    print STDERR "making $dir\n";
+	    $status = mkdir ("$dir", 0777);
+	    if (!$status) { die "can't create directory $dir\n"; }
+	}
+    }
+    @list = load_config ();
+    
+    # create elixir input list, set status to 'running.init'
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status eq "skip.init") { next; }
+
+	$line = gtconfig ($confline, "elixir");
+	print FILE "$line\n";
+	$confline = stconfig ($confline, "status", "running.init");
+    }
+    close (FILE);
+    save_config (@list);
+    
+    vsystem ("elixir -D DETREND_DIR $det -D mode fringe1 $file");
+    vsystem ("$MKFRINGE eval $file init");
+
+    open (FILE, "$file.eval");
+    @eval = <FILE>;
+    close (FILE);
+
+    # there is an entry in @eval for each @list with status of "running.init"
+    $N = 0;
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "running.init") { next; }
+	@words = split (" ", $eval[$N]);
+	if ($words[-1] == 0) { $status = "done.init"; }
+	if ($words[-1] == 1) { $status = "fail.init"; }
+	if ($words[-1] == 2) { $status = "fail.init"; }
+	if ($words[-1] == 3) { $status = "not.available"; }
+	$confline = stconfig ($confline, "status", $status);
+	$N++;
+    }
+    save_config (@list);
+    &goodbye;
+}
+
+# is this segment still used?
+# run mode.fringe1, skipping image selection
+sub mk_list_reinit {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE list.init"); }
+    
+    # create necessary directories
+    foreach $name ("html", "proc", "fringe") {
+	$dir = mkfiles ($name);
+	mkdir ("$dir", 0777);
+    }
+    
+    @list = load_config ();
+    
+    # create elixir input list, set status to 'running.init'
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status eq "skip.init") { next; }
+
+	$line = gtconfig ($confline, "elixir");
+	print FILE "$line\n";
+	$confline = stconfig ($confline, "status", "running.reinit");
+    }
+    close (FILE);
+    save_config (@list);
+    
+    vsystem ("elixir -D DETREND_DIR $det -D mode fringe1 -D global.pending master.lists $file");
+    vsystem ("$MKFRINGE eval $file init");
+
+    open (FILE, "$file.eval");
+    @eval = <FILE>;
+    close (FILE);
+
+    # there is an entry in @eval for each @list with status of "running.init"
+    $N = 0;
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "running.reinit") { next; }
+	@words = split (" ", $eval[$N]);
+	if ($words[-1] == 0) { $status = "done.init"; }
+	if ($words[-1] == 1) { $status = "fail.init"; }
+	if ($words[-1] == 2) { $status = "fail.init"; }
+	if ($words[-1] == 3) { $status = "not.available"; }
+	$confline = stconfig ($confline, "status", $status);
+	$N++;
+    }
+    save_config (@list);
+    &goodbye;
+}
+
+
+############## detrend ###############
+sub mk_detrend {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE detrend"); }
+
+    @list = load_config ();
+    
+    # create namelist for those with appropriate status
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    system ("rm -f $file");
+
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.init") && ($status ne "modified") && ($status ne "detrend")) { next; }
+
+	$Nrun ++;
+	$detrend = mknames ("detrend", $confline);
+
+	# merge lines from the "detrend" list to make elixir list
+	system ("cat $detrend >> $file");
+	
+	# set status for these config entries
+	$confline = stconfig ($confline, "status", "running.detrend");
+    }
+    save_config (@list);
+    
+    if ($Nrun == 0) { &goodbye; }
+    
+    vsystem ("elixir -D DETREND_DIR $det -D mode fringe2 $file"); 
+    vsystem ("$MKFRINGE eval $file detrend");
+
+    open (FILE, "$file.eval");
+    @eval = <FILE>;
+    close (FILE);
+
+    $N = 0;
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "running.detrend") { next; }
+
+	$detrend = mknames ("detrend", $confline);
+	open (FILE, "$detrend");
+	@testlist = <FILE>;
+	close (FILE);
+	
+	# each entry in $detrend must succeed for config to succeed
+	$status = "done.detrend";
+	for ($i = 0; $i < @testlist; $i++) {
+	    @words = split (" ", $eval[$N]);
+	    if ($words[-1] == 1) { $status = "fail.detrend"; }
+	    if ($words[-1] == 2) { $status = "fail.detrend"; }
+	    $N++;
+	}
+	$confline = stconfig ($confline, "status", $status);
+
+	$detrend = mknames ("detrend", $confline);
+	$master = mknames ("master", $confline);
+	# update master list (2 -> 0, 3 -> 1)
+	vsystem ("fr.mklists -master $master $detrend");
+    }
+    save_config (@list);
+
+    &goodbye;
+}
+
+############## map run ###############
+sub mk_map {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE map"); }
+
+    @list = load_config ();
+    
+    # this can be parallelized with elixir later...
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "map") && ($status ne "modified.map")) { next; }
+	$Nrun ++;
+	$confline = stconfig ($confline, "status", "running.map");
+	save_config (@list);
+
+	$maplist  = mknames ("maplist",  $confline);
+	$modelist = mknames ("modelist", $confline);
+	$modefits = mknames ("modefits", $confline);
+
+	$modesjpg = mknames ("modesjpg", $confline);
+	$residjpg = mknames ("residjpg", $confline);
+	$inmapjpg = mknames ("inmapjpg", $confline);
+
+	vsystem ("fr.mkmodes $maplist $modefits $modelist");
+	if ($status) { 
+	    $confline = stconfig ($confline, "status", "fail.map");
+	    save_config (@list);
+	    next;
+	}
+	vsystem ("fr.modestats $maplist $modefits $modelist $modesjpg $residjpg $inmapjpg");
+	if ($status) { 
+	    $confline = stconfig ($confline, "status", "fail.map");
+	    save_config (@list);
+	    next;
+	}
+	$confline = stconfig ($confline, "status", "done.map");
+	save_config (@list);
+    }
+    &goodbye;
+}
+
+############## map reg ###############
+sub mk_map_reg {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE map.reg"); }
+    
+    @list = load_config ();
+    
+    # this can be parallelized with elixir later...
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "accept.map") { next; }
+	$Nrun ++;
+	$confline = stconfig ($confline, "status", "register.map");
+	save_config (@list);
+
+	$modelist = mknames ("modelist", $confline);
+	$modefits = mknames ("modefits", $confline);
+	$modesave = mknames ("modesave", $confline);
+	$ID     = gtconfig ($confline, "ID");
+	$filter = gtconfig ($confline, "filter");
+	$from   = gtconfig ($confline, "from");
+	$to     = gtconfig ($confline, "to");
+
+	vsystem ("fr.modesave $modefits $modelist $modesave $from $to $filter");
+	vsystem ("detregister $modesave -label elixir -ID $ID");
+	$confline = stconfig ($confline, "status", "done.map.reg");
+	save_config (@list);
+    }
+    &goodbye;
+}
+
+############## mkrough run ###############
+sub mk_mkrough {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE mkrough"); }
+    
+    @list = load_config ();
+    
+    # create namelist for those with appropriate status
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.prep") && ($status ne "mkrough") && ($status ne "modified")) { next; }
+
+	$Nrun ++;
+
+	# one line for each config.ver.ccd:
+	for ($j = 0; $j < $Nccd; $j++) {
+	    $line = gtconfig ($confline, "chiprun", $ccds[$j]);
+	    print FILE "$line\n";
+	}
+	$confline = stconfig ($confline, "status", "running.mkrough");
+
+	$CONFIG  = mknames ("config", $confline);
+
+	# extract the selected subset of detrend images from master & statlist to sublist
+	vsystem ("fr.mklists -subset  $CONFIG.master $CONFIG.med.statlist $CONFIG.med.sublist");
+	if ($?) { &escape ("failure running fr.mklists -subset"); }
+
+	# extract the selected subset of defringed images from master & statlist to sublist
+	vsystem ("fr.mklists -subset  $CONFIG.master $CONFIG.def.statlist $CONFIG.def.sublist");
+	if ($?) { &escape ("failure running fr.mklists -subset"); }
+
+	# calculate fit parameters based on subset
+	vsystem ("fr.frstats -fitpars $CONFIG.med.sublist $CONFIG.fitpar");
+	if ($?) { &escape ("failure running fr.frstats -fitpars"); }
+
+	# calculate optimal per-image fringe statistics based on fit parameters
+	vsystem ("fr.frstats -frstats $CONFIG.med.statlist $CONFIG.fitpar $CONFIG.med.imstats");
+	if ($?) { &escape ("failure running fr.frstats -frstats"); }
+
+	# calculate per-image residual fringe stats 
+	vsystem ("fr.frstats -dfstats $CONFIG.def.statlist $CONFIG.fitpar $CONFIG.def.imstats");
+	if ($?) { &escape ("failure running fr.frstats -dfstats"); }
+
+	# assemble master fringe statistic file needed by web tools
+	vsystem ("fr.mklists -imstats $CONFIG.master $CONFIG.med.imstats $CONFIG.def.imstats $CONFIG.datlist $CONFIG.imstats");
+	if ($?) { &escape ("failure running fr.mklists -imstats"); }
+
+    }
+    close (FILE);
+    save_config (@list);
+
+    if ($Nrun == 0) { &goodbye; }
+    
+    vsystem ("elixir -D DETREND_DIR $det -D mode fringe4 $file");
+    vsystem ("$MKFRINGE eval $file mkrough");
+
+    open (FILE, "$file.eval");
+    @eval = <FILE>;
+    close (FILE);
+
+    # there is one entry in @eval for each ccd for each confline:
+    $N = 0;
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "running.mkrough") { next; }
+
+	# each chip must succeed for config to succeed
+	$status = "done.mkrough";
+	for ($j = 0; $j < $Nccd; $j++) {
+	    @words = split (" ", $eval[$N]);
+	    if ($words[-1] == 1) { $status = "fail.mkrough"; }
+	    if ($words[-1] == 2) { $status = "fail.mkrough"; }
+	    $N++;
+	}
+	$confline = stconfig ($confline, "status", $status);
+    }
+    save_config (@list);
+    
+    &goodbye;
+}
+
+############## defringe ###############
+sub mk_defringe {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE defringe"); }
+
+    @list = load_config ();
+    
+    # create namelist for those with appropriate status
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    system ("rm $file");
+
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.mkrough") && ($status ne "defringe")) { next; }
+
+	$Nrun ++;
+	$defringe = mknames ("defringe", $confline);
+	
+	# merge lines from the "defringe" list to make elixir list
+	system ("cat $defringe >> $file");
+	
+	$confline = stconfig ($confline, "status", "running.defringe");
+    }
+    save_config (@list);
+    
+    if ($Nrun == 0) { &goodbye; }
+    
+    vsystem ("elixir -D DETREND_DIR $det -D mode fringe3 $file"); 
+    vsystem ("$MKFRINGE eval $file defringe");
+
+    open (FILE, "$file.eval");
+    @eval = <FILE>;
+    close (FILE);
+
+    $N = 0;
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if ($status ne "running.defringe") { next; }
+
+	$defringe = mknames ("defringe", $confline);
+	open (FILE, "$defringe");
+	@testlist = <FILE>;
+	close (FILE);
+	
+	# each entry in $defringe must succeed for config to succeed
+	$status = "done.defringe";
+	for ($i = 0; $i < @testlist; $i++) {
+	    @words = split (" ", $eval[$N]);
+	    if ($words[-1] == 1) { $status = "fail.defringe"; }
+	    if ($words[-1] == 2) { $status = "fail.defringe"; }
+	    $N++;
+	}
+	$confline = stconfig ($confline, "status", $status);
+    }
+    save_config (@list);
+    &goodbye;
+}
+
+
+
+############## merge run ###############
+sub mk_merge {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE merge"); }
+
+    @list = load_config ();
+    
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.detrend") && ($status ne "done.defringe") && ($status ne "merge")) { next; }
+
+	$confline = stconfig ($confline, "status", "running.merge");
+	save_config (@list);
+
+	$CONFIG  = mknames ("config", $confline);
+	$VERSION = gtconfig ($confline, "version");
+	$HTML    = mkfiles ("html");
+
+	vsystem ("fr.mklists -subset  $CONFIG.master $CONFIG.med.statlist $CONFIG.med.sublist");
+	if ($?) { &escape ("failure running fr.mklists -subset"); }
+
+	vsystem ("fr.mklists -subset  $CONFIG.master $CONFIG.def.statlist $CONFIG.def.sublist");
+	if ($?) { &escape ("failure running fr.mklists -subset"); }
+
+	vsystem ("fr.frstats -fitpars $CONFIG.med.sublist $CONFIG.fitpar");
+	if ($?) { &escape ("failure running fr.frstats -fitpars"); }
+
+	vsystem ("fr.frstats -plotstats $CONFIG.med.statlist $CONFIG.fitpar $CONFIG.stats.png");
+	if ($?) { &escape ("failure running fr.frstats -plotstats"); }
+
+	vsystem ("fr.frstats -frstats $CONFIG.med.statlist $CONFIG.fitpar $CONFIG.med.imstats");
+	if ($?) { &escape ("failure running fr.frstats -frstats"); }
+
+	vsystem ("fr.frstats -dfstats $CONFIG.def.statlist $CONFIG.fitpar $CONFIG.def.imstats");
+	if ($?) { &escape ("failure running fr.frstats -dfstats"); }
+
+	vsystem ("fr.mklists -imstats $CONFIG.master $CONFIG.med.imstats $CONFIG.def.imstats $CONFIG.datlist $CONFIG.imstats");
+	if ($?) { &escape ("failure running fr.mklists -imstats"); }
+
+	$file = `head -1 $CONFIG.frlist`; chop ($file);
+	if (-e "$file") { 
+	    vsystem ("fr.jpegs $CONFIG.frlist $CONFIG.medbin.jpg $CONFIG.tenbin.jpg 0.02");
+	    if ($?) { &escape ("failure running fr.jpegs"); }
+	    vsystem ("fr.mkhtml -imstats $CONFIG.medbin.jpg $HTML");
+	    if ($?) { &escape ("failure running fr.mkhtml -jpgmain"); }
+	    vsystem ("fr.mkhtml -imstats $CONFIG.tenbin.jpg $HTML");
+	    if ($?) { &escape ("failure running fr.mkhtml -jpgmain"); }
+	} else {
+	    print STDOUT "no fits file $file\n";
+	}
+
+	vsystem ("fr.mkhtml -imstats $CONFIG.imstats $HTML");
+	if ($?) { &escape ("failure running fr.mkhtml -imstats"); }
+
+	vsystem ("fr.mkhtml -imstats $CONFIG.stats.png $HTML");
+	if ($?) { &escape ("failure running fr.mkhtml -imstats"); }
+
+	vsystem ("fr.mkhtml -jpglist $CONFIG.med.medbin.list $HTML $VERSION.med.medbin");
+	if ($?) { &escape ("failure running fr.mkhtml -jpglist"); }
+
+	vsystem ("fr.mkhtml -jpglist $CONFIG.med.tenbin.list $HTML $VERSION.med.tenbin");
+	if ($?) { &escape ("failure running fr.mkhtml -jpglist"); }
+
+	vsystem ("fr.mkhtml -jpglist $CONFIG.def.medbin.list $HTML $VERSION.def.medbin");
+	if ($?) { &escape ("failure running fr.mkhtml -jpglist"); }
+
+	vsystem ("fr.mkhtml -jpglist $CONFIG.def.tenbin.list $HTML $VERSION.def.tenbin");
+	if ($?) { &escape ("failure running fr.mkhtml -jpglist"); }
+
+	print STDOUT "CONFIG: $CONFIG.def.imstats\n";
+
+	if (-e "$CONFIG.def.imstats") { 
+	    $confline = stconfig ($confline, "status", "done.merge");
+	} else {
+	    $confline = stconfig ($confline, "status", "done.prep");
+	}
+	save_config (@list);
+    }
+
+    &goodbye;
+}
+
+############## reg run ###############
+sub mk_smooth {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE mksmooth"); }
+    
+    @list = load_config ();
+    
+    # create namelist for those with appropriate status
+    $Nrun = 0;
+    $file = mkfiles ("list");
+    open (FILE, ">$file");
+    foreach $confline (@list) {
+        $status = gtconfig ($confline, "status");
+        if (($status ne "accepted") && ($status ne "smooth")) { next; }
+
+        $Nrun ++;
+
+        # one line for each config.ver.ccd:
+        for ($j = 0; $j < $Nccd; $j++) {
+            $line = gtconfig ($confline, "chiprun", $ccds[$j]);
+            print FILE "$line\n";
+        }
+        $confline = stconfig ($confline, "status", "running.smooth");
+    }
+    close (FILE);
+    save_config (@list);
+
+    if ($Nrun == 0) { &goodbye; }
+    
+    vsystem ("elixir -D DETREND_DIR $det -D mode fringe4 -D global.pending mksmooth $file");
+    vsystem ("$MKFRINGE eval $file smooth");
+
+    open (FILE, "$file.eval");
+    @eval = <FILE>;
+    close (FILE);
+
+    # there is one entry in @eval for each ccd for each confline:
+    $N = 0;
+    foreach $confline (@list) {
+        $status = gtconfig ($confline, "status");
+        if ($status ne "running.smooth") { next; }
+
+        # each chip must succeed for config to succeed
+        $status = "done.smooth";
+        for ($j = 0; $j < $Nccd; $j++) {
+            @words = split (" ", $eval[$N]);
+            if ($words[-1] == 1) { $status = "fail.smooth"; }
+            if ($words[-1] == 2) { $status = "fail.smooth"; }
+            $N++;
+        }
+        $confline = stconfig ($confline, "status", $status);
+    }
+    save_config (@list);
+    
+    &goodbye;
+}
+
+############## reg run ###############
+sub mk_regimage {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE regimage"); }
+    
+    @list = load_config ();
+    
+    # create directory for fhtool to work in
+    $links = mkfiles ("links");
+    if (! -e $links) { system ("mkdir -p $links"); }
+
+    # create namelist for those with appropriate status
+    foreach $confline (@list) {
+	$status = gtconfig ($confline, "status");
+	if (($status ne "done.smooth") && ($status ne "reg")) { next; }
+	$confline = stconfig ($confline, "status", "running.reg");
+	save_config (@list);
+
+	$success = 1;
+	$ID = gtconfig ($confline, "ID");
+
+	$DBmode = `gconfig DETREND-DB-MODE`; chop $DBmode;
+	$DBmode = "\U$DBmode\E";
+
+	if ($DBmode eq "SPLIT") {
+	    for ($j = 0; $j < $Nccd; $j++) {
+		$file = mknames ("smfile", $confline, $ccds[$j]);
+		$line = "detregister $file -label fringe-2.1 -ID $ID";
+		$status = system ($line);
+		if ($status) { 
+		    print STDERR "error registering $file\n";
+		    $success = 0;
+		}
+	    }
+	    goto registered;
+	}
+
+	if ($DBmode eq "MEF") {
+	    # empty link directory:
+	    system ("rm -f $links/*.fits");
+
+	    # make links 
+	    foreach $ccd (@ccds) {
+		$file = mknames ("smfile", $confline, $ccd);
+		$link = mknames ("link", $confline, $ccd);
+		symlink $file, $link;
+	    }
+
+	    # mef, register
+	    $mef = mknames ("mef", $confline);
+	    system ("rm -f $mef");
+
+	    vsystem ("fhtool -P $links $mef");
+	    if ($status) { $success = 0; }
+	    vsystem ("detregister $mef -label fringe-2.1 -ID $ID");
+	    if ($status) { $success = 0; }
+
+	    # remove links
+	    foreach $ccd (@ccds) {
+		$link = mknames ("link", $confline, $ccd);
+		unlink $link;
+	    }
+	    goto registered;
+	}
+	print STDERR "DB mode not defined?\n";
+	$success = 0;
+
+      registered:
+	if ($success) {
+	    $confline = stconfig ($confline, "status", "done");
+	} else {
+	    print STDERR "error registering $file\n"; 
+	    $confline = stconfig ($confline, "status", "fail.reg");
+	}
+	save_config (@list);
+    }
+    &goodbye;
+}
+
+############## evaluate elixir run ###############
+sub mk_eval {
+    
+    if (@ARGV != 3) { &escape ("USAGE: $MKFRINGE meval (file) (step)"); }
+    
+    $infile = $ARGV[1];
+    $step = $ARGV[2];
+
+    open (FILE, "$infile");
+    @list = <FILE>;
+    close (file);
+    
+    # load success fifo info:
+    $file = `gconfig -D mode fringe1 global.success`; chop $file;
+    open (FILE, "$file");
+    @success = <FILE>;
+    close (FILE);
+    
+    # load failure fifo info:
+    $file = `gconfig -D mode fringe1 global.failure`; chop $file;
+    open (FILE, "$file");
+    @failure = <FILE>;
+    close (FILE);
+    
+    # possible states:
+    #   0 entry in success
+    #   1 entry in failure
+    #   2 entry missing   
+    #   3 special case    
+    
+    $outfile = "$infile.eval";
+    open (FILE, ">$outfile");
+    foreach $list (@list) {
+
+	# force lines to have only single spaces between words
+	@words = split (" ", $list);
+	$key = join (' ', @words);
+
+	$state = 2;
+      MCHECK: {
+	  foreach $entry (@success) {
+	      @words = split (" ", $entry);
+	      $value = join (' ', @words);
+	      if ($value =~ /$key/) { 
+		  $state = 0;
+		  last MCHECK;
+	      }
+	  }
+	  
+	  foreach $entry (@failure) {
+	      @words = split (" ", $entry);
+	      $value = join (' ', @words);
+	      if ($value =~ /$key/) { 
+		  $state = 1;
+		  $level = $words[-1];
+		  # SPECIAL CASES:
+		  if (($step eq "init") && ($level eq "master.lists")) {
+		      $state = 3;
+		  }
+		  last MCHECK;
+	      }
+	  }
+	  
+      }
+	print FILE "$key $state\n";
+    }
+    close (FILE);
+    &goodbye;
+}
+
+############## html duplicate ###############
+sub mk_htmldup {
+    
+    if (@ARGV != 2) { &escape ("USAGE: $MKFRINGE htmldup (config)"); }
+    
+    $config = $ARGV[1];
+    system ("$MKFRINGE dup $config");
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/fr.elixir1?&elconf=$elconf\">\n";
+    
+    &goodbye;
+}
+
+############## html delete ###############
+sub mk_htmldel {
+    
+    if (@ARGV != 2) { &escape ("USAGE: $MKFRINGE htmldel (config.ver)"); }
+    
+    $config = $ARGV[1];
+    system ("$MKFRINGE del $config");
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/fr.elixir1?&elconf=$elconf\">\n";
+    
+    &goodbye;
+}
+
+############## html define ###############
+sub mk_htmldef {
+    
+    if (@ARGV != 2) { &escape ("USAGE: $MKFRINGE htmldef (config.ver)"); }
+    
+    $key = $ARGV[1];
+    
+    # find (config.ver) in list:
+    
+    @list = load_config ();
+    
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}
+    }
+    if ($match == -1) { &escape ("error: can't find config entry"); }
+    
+    # show the complete list for reference
+    print STDOUT "<HEADER> <TITLE>Elixir Mkfringe Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0e0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+    print STDOUT "<table border>\n";
+    for ($i = 0; $i < @list; $i++) {
+	$config  = gtconfig ($list[$i], "config");
+	$version = gtconfig ($list[$i], "version");
+	$status  = gtconfig ($list[$i], "status");
+	$from    = gtconfig ($list[$i], "from");
+	$to      = gtconfig ($list[$i], "to");
+	print STDOUT "<tr><td> $version </td>\n";
+	print STDOUT "<td> $from </td><td> $to </td>\n";
+	print STDOUT "<td> <a href=$CGI/fr.elixir2?$version&elconf=$elconf> $status </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/fr.split?$config&elconf=$elconf>      split </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/fr.delete?$version&elconf=$elconf>   delete </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/fr.redef?$version&elconf=$elconf>  redefine </a></td>\n";
+	print STDOUT "</tr>\n";
+    }
+    print STDOUT "</table></body><br><br>\n";
+    
+    $version = gtconfig ($list[$match], "version");
+    $status  = gtconfig ($list[$match], "status");
+    $from    = gtconfig ($list[$match], "from");
+    $to      = gtconfig ($list[$match], "to");
+    print STDOUT "redefining <b> $key: </b> <br>\n";    
+    print STDOUT "<table border>\n";
+    print STDOUT "<tr><td> $version </td>\n";
+    print STDOUT "<td> $from </td><td> $to </td><td> $status </a></td>\n";
+    print STDOUT "</tr></table>\n";
+    
+    
+    # create a simple form for redefine:
+    print STDOUT "<form method=post action=$CGI/fr.redefine?&elconf=$elconf>\n";
+    print STDOUT "start: <input type=text value=$from name=start size=40 maxlength=40> <br>\n";
+    print STDOUT "stop: <input type=text value=$to name=stop size=40 maxlength=40> <br>\n";
+    print STDOUT "<input type=hidden name=config value=\"$key\">\n";
+    print STDOUT "<input type=\"submit\" value=\"submit\"><input type=\"reset\" value=\"reset\">\n";
+    print STDOUT "</form>\n";
+    print STDOUT "</body>\n";
+    
+    &goodbye;
+}
+
+############## html modify ###############
+sub mk_htmlmod {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE htmlmod"); }
+    
+    # images which are marked for exclusion are included in the 
+    # environment variable as $WWW_image_NN -- NN = sequence number
+    # in the master / defringe file
+
+    $key = $ENV{'WWW_config'};
+    $Nimage = $ENV{'WWW_Nimage'};
+    
+    @list = load_config ();
+
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}
+    }
+    if ($match == -1) { &escape ("ERROR: can't find config entry"); }
+    
+    # change status entry in master list & $HTML.imstats
+    $HTML    = mkfiles ("html");
+    $imstat = "$HTML/$version.imstats";
+    $master = mknames ("master", $list[$match]); 
+
+    # load data from master list
+    open (FILE, "$master");
+    @master = <FILE>;
+    close (FILE);
+    if ($Nimage != @master) { &escape ("mis-match in image list lengths"); }
+
+    # load data from imstats list
+    open (FILE, "$imstat");
+    @imstat = <FILE>;
+    close (FILE);
+    if ($Nimage != @imstat) { &escape ("mis-match in image list lengths"); }
+
+    for ($i = 0; $i < @master; $i++) {
+	chop ($master[$i]);
+	chop ($imstat[$i]);
+	($path, $root, $mode, $status) = split (" ", $master[$i]);
+	$name = "WWW_image_$i";
+	$newstatus = $status;
+	if ($ENV{$name} eq "") { 
+	    $hstatus = 0;
+	    if (($status == 0) || ($status == 1)) { $newstatus = 0; }
+	    if (($status == 2) || ($status == 3)) { $newstatus = 2; }
+	} else {
+	    $hstatus = 1;
+	    if (($status == 0) || ($status == 1)) { $newstatus = 1; }
+	    if (($status == 2) || ($status == 3)) { $newstatus = 3; }
+	}	    
+	$master[$i] = "$path $root $mode $newstatus";
+	($line, $status) = $imstat[$i] =~ m|(\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+)(\S)|;
+	$imstat[$i] = "$line $hstatus";
+    }
+
+    # save master list
+    open (FILE, ">$master");
+    foreach $line (@master) { print FILE "$line\n"; }
+    close (FILE);
+
+    # save imstats list
+    open (FILE, ">$imstat");
+    foreach $line (@imstat) { print FILE "$line\n"; }
+    close (FILE);
+
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/fr.elixir2?$key&elconf=$elconf\">\n";
+    
+    # adjust the config file: convert status to 'modified'
+    $list[$match] = stconfig ($list[$match], "status", "modified");
+    save_config (@list);
+    &goodbye;
+    
+}
+
+############## html changes modes ###############
+sub mk_htmlmodes {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE htmlmodes"); }
+    
+    # modes which are marked for exclusion are included in the 
+    # environment variable as $WWW_mode_NN -- NN = sequence number
+
+    $key = $ENV{'WWW_config'};
+    $Nmode = $ENV{'WWW_Nmode'};
+    
+    @list = load_config ();
+
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}
+    }
+    if ($match == -1) { &escape ("ERROR: can't find config entry"); }
+    
+    $modelist = mknames ("modelist", $list[$match]);
+    open (FILE, "$modelist");
+    @inlist = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$modelist");
+    for ($i = 0; $i < @inlist; $i++) {
+	($N, $status) = split (" ", $inlist[$i]);
+	if ($i < $Nmode) {
+	    $name = "WWW_mode_$i";
+	    $newstatus = $status;
+	    if ($ENV{$name} eq "") { 
+		$newstatus = 0;
+	    } else {
+		$newstatus = 1;
+	    }	    
+	} else {
+	    $newstatus = 0;
+	}
+	print FILE "$i $newstatus\n";
+    }
+    close (FILE);
+
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/fr.elixir2?$key&elconf=$elconf\">\n";
+    
+    # adjust the config file: convert status to 'modified'
+    $list[$match] = stconfig ($list[$match], "status", "modified.map");
+    save_config (@list);
+    &goodbye;
+}
+
+############## html changes modes ###############
+sub mk_htmlmaps {
+    
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE htmlmaps"); }
+    
+    # maps which are marked for inclusion are given in the 
+    # environment variable as $WWW_mode_NN -- NN = sequence number
+
+    $key = $ENV{'WWW_config'};
+    $Nmaps = $ENV{'WWW_Nmaps'};
+    
+    @list = load_config ();
+
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}
+    }
+    if ($match == -1) { &escape ("ERROR: can't find config entry"); }
+
+    $maplist = mknames ("maplist", $list[$match]);
+    open (FILE, "$maplist");
+    @inlist = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$maplist");
+    for ($i = 0; $i < @inlist; $i++) {
+	($Name, $status) = split (" ", $inlist[$i]);
+	$name = "WWW_map_$i";
+	$newstatus = $status;
+	if ($ENV{$name} eq "") { 
+	    $newstatus = 0;
+	} else {
+	    $newstatus = 1;
+	}	    
+	print FILE "$Name $newstatus\n";
+    }
+    close (FILE);
+
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/fr.elixir2?$key&elconf=$elconf\">\n";
+
+    # adjust the config file: convert status to 'modified'
+    $list[$match] = stconfig ($list[$match], "status", "modified.map");
+    save_config (@list);
+    &goodbye;
+}
+
+############## html accept ###############
+sub mk_htmlkeep {
+    
+    if (@ARGV != 2) { &escape ("USAGE: $MKFRINGE htmlkeep (config.ver)"); }
+    
+    $key = $ARGV[1];
+    @list = load_config ();
+    
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}
+    }
+    if ($match == -1) { &escape ("ERROR: can't find config entry"); }
+    
+    # adjust the config file: convert status to 'accepted'
+    $list[$match] = stconfig ($list[$match], "status", "accepted");
+    save_config (@list);
+    
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/fr.elixir1?&elconf=$elconf\">";
+    &goodbye;
+}
+
+############## html accept ###############
+sub mk_htmlmapkeep {
+    
+    if (@ARGV != 2) { &escape ("USAGE: $MKFRINGE htmlmapkeep (config.ver)"); }
+    
+    $key = $ARGV[1];
+    @list = load_config ();
+    
+    $match = -1;
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $match = $i;
+	    last;
+	}
+    }
+    if ($match == -1) { &escape ("ERROR: can't find config entry"); }
+    
+    # adjust the config file: convert status to 'accepted'
+    $list[$match] = stconfig ($list[$match], "status", "accept.map");
+    save_config (@list);
+    
+    print STDOUT "<meta http-equiv=refresh content=\"0; url=$CGI/fr.elixir1?&elconf=$elconf\">";
+    &goodbye;
+}
+
+############## html config ###############
+sub mk_htmlconfig {
+    
+    if (@ARGV != 1) { &escape ("USAGE: mkdetrend htmlconfig"); }
+    
+    ## html headers
+    print STDOUT "<HEADER> <TITLE>Elixir Mkdetrend Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0f0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+    
+    # create a simple form for redefine:
+    print STDOUT "<form method=post action=$CGI/fr.newconfig?&elconf=$elconf>\n";
+    if ($elconf) {
+	print STDOUT "enter new config: <input type=text value=$elconf name=config size=40 maxlength=40> <br>\n";
+    } else {
+	print STDOUT "enter new config: <input type=text name=config size=40 maxlength=40> <br>\n";
+    }
+    print STDOUT "<input type=\"submit\" value=\"submit\"><input type=\"reset\" value=\"reset\">\n";
+    print STDOUT "</form>\n";
+    print STDOUT "</body>\n";
+    
+    &goodbye;
+}
+
+############## html level 1 ###############
+sub mk_html1 {
+    
+
+    if (@ARGV != 1) { &escape ("USAGE: $MKFRINGE html1"); }
+    
+    @list = load_config ();
+    
+    ## html headers
+    print STDOUT "<HEADER> <TITLE>Elixir Mkfringe Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0f0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+
+    print STDOUT "<table border>\n";
+    for ($i = 0; $i < @list; $i++) {
+	$config  = gtconfig ($list[$i], "config");
+	$version = gtconfig ($list[$i], "version");
+	$from    = gtconfig ($list[$i], "from");
+	$to      = gtconfig ($list[$i], "to");
+	$status  = gtconfig ($list[$i], "status");
+	print STDOUT "<tr><td> $version </td>\n";
+	print STDOUT "<td> $from </td><td> $to </td>\n";
+	print STDOUT "<td> <a href=$CGI/fr.elixir2?$version&elconf=$elconf> $status </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/fr.split?$config&elconf=$elconf> split </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/fr.delete?$version&elconf=$elconf> delete </a></td>\n";
+	print STDOUT "<td> <a href=$CGI/fr.redef?$version&elconf=$elconf> redefine </a></td>\n";
+	print STDOUT "</tr>\n";
+    }
+    
+    print STDOUT "</table><br>\n";
+    if ($elconf eq "") {
+	print STDOUT "Current Config: <b>default</b><br>\n";
+    } else {
+	print STDOUT "Current Config: <b>$elconf</b><br>\n";
+    }
+    print STDOUT "<a href=$CGI/fr.config?&elconf=$elconf>Change Config</a></td>\n";
+    print STDOUT "</body>\n";
+    &goodbye;
+    
+}
+
+
+
+############## html level 2 ###############
+sub mk_html2 {
+    
+    if (@ARGV != 2) {
+	print STDERR "USAGE: $MKFRINGE html2 (config.ver)\n";
+	&goodbye; 
+    }
+    
+    @list = load_config ();
+    
+    # config:  run.type.filter
+    # key:     run.type.filter.ver
+    # key.col  run.type.filter.ver.column
+    # the words in config and key may consist of alphanumeric plus '_', '-'
+    # the words in col number be digits
+
+    # entry on line will be either config.ver (== key) or config.ver.col for sorting
+    # grab just the key portion
+    $status = "none";
+    ($key) = $ARGV[1] =~ /([-\w]+\.[-\w]+\.[-\w]+\.[-\w]+)/; # (config.ver)
+    $arg = $ARGV[1];
+    
+    # find appropriate config.ver entry:
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $confline = $list[$i];
+	    $status = gtconfig ($confline, "status");
+	    last;
+	}
+    }
+    
+    $from = gtconfig ($confline, "from");
+    $to   = gtconfig ($confline, "to");
+    print STDOUT "<HEADER> <TITLE>Elixir Mkfringe Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0f0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+    print STDOUT "<center><table border>\n";
+    print STDOUT "<tr><th>config   </th><th> from  </th><th> to  </th><th> status  </th></tr>\n";
+    print STDOUT "<tr><td>$version </td><td> $from </td><td> $to </td><td> $status </th></tr>\n";
+    print STDOUT "</table></center><hr>\n";
+    
+    if ($status eq "modified")    { flips_report ($arg, $confline, "modified"); }
+    if ($status eq "accepted")    { flips_report ($arg, $confline, "accepted"); }
+    if ($status eq "done.merge")  { flips_report ($arg, $confline, "merged"); }
+    if ($status eq "done.prep")   { flips_report ($arg, $confline, "prepped"); }
+    if ($status eq "done.reg")    { flips_report ($key, $confline, "registered"); }
+    if ($status eq "done")        { flips_report ($key, $confline, "done"); }
+
+    if ($status eq "done.map")     { map_report ($arg, $confline, "mapped"); }
+    if ($status eq "modified.map") { map_report ($arg, $confline, "mapped"); }
+    
+    if ($status eq "none")              { $message = "not found"; }
+    if ($status eq "init")              { $message = "initialized, not yet run."; }
+    if ($status eq "not.available")     { $message = "has no images available for detrend creation."; }
+    
+    foreach $step (init, flips, norm, merge) {
+	if ($status eq "running.$step") { $message = "running step $step"; }
+	if ($status eq "fail.$step")    { $message = "failed step $step"; }
+	if ($status eq "done.$step")    { $message = "has finished $step"; }
+    }      
+
+    print STDOUT "config <b> $key </b> $message <br>\n";
+    
+    $master = mknames ("master", $confline);
+    print STDOUT "<table border>\n";
+    open (FILE, "$master");
+    while ($line = <FILE>) {
+	chop ($line);
+	@entry = split ("/", $line);
+	$N = @entry - 1;
+	print STDOUT "<tr><td>$entry[$N]</td></tr>\n";
+    }
+    print STDOUT "</table>\n";
+    
+    # print STDOUT "</body>\n";
+    &goodbye;
+    print STDOUT "<form method=get action=$CGI/fr.elixir1?&elconf=$elconf> <input type=submit value=\"back to config list\"></form>";
+}
+
+
+
+############## html level 3 ###############
+sub mk_html3 {
+    
+    if (@ARGV != 3) {
+	print STDERR "USAGE: $MKFRINGE html3 (entry.config.ver) (mode)\n";
+	&goodbye; 
+    }
+    
+    @list = load_config ();
+    
+    # extract the entry and key values from $ARGV[1]
+    ($entry, $key) = $ARGV[1] =~ /(\d+)\Q.\E(.*)/;
+    $mode = $ARGV[2];
+    
+    $status = "none";
+    # find appropriate config.ver entry:
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $confline = $list[$i];
+	    $status = gtconfig ($confline, "status");
+	    last;
+	}
+    }
+    
+    print STDOUT "<HEADER> <TITLE>Elixir Mkfringe Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0f0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+    
+    if ($status eq "none")              { print STDOUT "config <b> $key </b> not found\n </body>\n"; &goodbye; }
+    if ($status eq "init")              { print STDOUT "config <b> $key </b> initialized, not yet run\n </body>\n"; &goodbye; }
+    if ($status eq "not.available")     { print STDOUT "config <b> $key </b> has no images available for detrend creation\n </body>\n"; &goodbye; }
+
+    if ($status eq "done.prep")   { flips_entry ($key, $entry, $confline, $mode); }
+    if ($status eq "done.merge")  { flips_entry ($key, $entry, $confline, $mode); }
+    if ($status eq "accepted")    { flips_entry ($key, $entry, $confline, $mode); }
+    if ($status eq "modified")    { flips_entry ($key, $entry, $confline, $mode); }
+    if ($status eq "done.reg")    { flips_entry ($key, $entry, $confline, $mode); }
+    if ($status eq "done")        { flips_entry ($key, $entry, $confline, $mode); }
+    
+    foreach $step (init, flips, norm, merge) {
+	# print "step: $step, $status: $status\n";
+	if ($status eq "running.$step") { print STDOUT "config <b> $key </b> running step $step \n </body>\n"; &goodbye; }
+	if ($status eq "fail.$step")    { print STDOUT "config <b> $key </b> failed step $step\n </body>\n"; &goodbye; }
+	
+	if ($status eq "done.$step") {
+	    print STDOUT "config <b> $key </b> has finished $step\n";
+	    
+	    # load image names from CCD 0 list:
+	    $master = mknames ("master", $confline, "00");
+	    print STDOUT "<table border>\n";
+	    open (FILE, "$master");
+	    while ($line = <FILE>) {
+		chop ($line);
+		@entry = split ("/", $line);
+		$N = @entry - 1;
+		print STDOUT "<tr><td>$entry[$N]</td></tr>\n";
+	    }
+	    print STDOUT "</table>\n";
+	}
+    }
+    
+    # print STDOUT "</body>\n";
+    &goodbye;
+}
+
+$dadswww = "img";
+
+
+############## html for dads ###############
+sub mk_dads {
+    
+    if (@ARGV != 2) {
+	print STDERR "USAGE: $MKFRINGE dads (config.ver)\n";
+	&goodbye; 
+    }
+    
+    @list = load_config ();
+    
+    # entry on line will be either config.ver (== key) or config.ver.col for sorting
+    # grab just the key portion
+    $status = "none";
+    $arg = $ARGV[1];
+    ($key) = $arg =~ /([-\w]+\.[-\w]+\.[-\w]+\.[-\w]+)/; # (config.ver)
+    
+    # find appropriate config.ver entry:
+    for ($i = 0; $i < @list; $i++) {
+	$version = gtconfig ($list[$i], "version");
+	if ($key eq $version) {
+	    $confline = $list[$i];
+	    $status = gtconfig ($confline, "status");
+	    last;
+	}
+    }
+    
+    print STDOUT "<HEADER> <TITLE>Elixir Mkfringe Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#ffffff TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+
+    # only print for config.ver entries which are ready
+    if ($status ne "done") {
+	&escape ("$version is not ready for release by Elixir");
+    }
+
+    # parse config entry and get data from appropriate files
+    $from = gtconfig ($confline, "from");
+    $to   = gtconfig ($confline, "to");
+    open (STAT,   mknames ("stats", $confline));     @statdata = <STAT>;        close (STAT);
+    open (MED,    mknames ("medbin", $confline));    @medndata = <MED>;         close (MED);
+    open (TEN,    mknames ("tenbin", $confline));    @tenndata = <TEN>;         close (TEN);
+    open (MASTER, mknames ("master", $confline, 0)); @mastdata = <MASTER>;      close (MASTER);
+    
+    $tmpname = mknames ("master", $confline, 0);
+
+    # get stats from the stats & master files
+    $Nkeep = 0;
+    $Ncnt = 0;
+    $Sigma = 0;
+    $Nimage = @statdata;
+    for ($i = 0; $i < $Nimage; $i++) {
+	@tmpw = split (" ", $mastdata[$i]);
+	@tmps = split (" ", $statdata[$i]);
+	if ($tmpw[1]) { $Nkeep++; $Ncnt += $tmps[3]; $Sigma += $tmps[8]*$tmps[8]; }
+    }
+    $Sigma = sprintf "%5.3f", 100*sqrt ($Sigma/$Nkeep);
+
+    print STDOUT "<center><table border>\n";
+    print STDOUT "<tr><th>config   </th><th> from  </th><th> to </th></tr>\n";
+    print STDOUT "<tr><td>$version </td><td> $from </td><td> $to</td></tr>\n";
+    print STDOUT "</table></center><hr>\n";
+
+    $imbin = mknames ("dads.imbin", $confline);
+    print STDOUT "<table width=80%><tr><td valign=top>\n";
+    print STDOUT "this config has been created by Elixir. <br>\n";
+    print STDOUT "there are $Nimage images, $Nkeep used. <br>\n";
+    print STDOUT "Total Counts: $Ncnt<br> r.m.s. median sigma: $Sigma% <br><br>\n";
+    print STDOUT "</td><td align=right><img src=$imbin></td></tr></table>\n";
+    
+    print STDOUT "<center><b>Statistics on each available input image.</b></center>
+  Time is in UT, light background means dawn, grey means dusk.\n";
+
+    print STDOUT "<table bgcolor=#fff0e0 border=1 cellspacing=0 cellpadding=3>\n";
+    print STDOUT "<tr >\n";
+    print STDOUT "<th > image </th>\n";
+    print STDOUT "<th > Date  </th>\n";
+    print STDOUT "<th > Time  </th>\n";
+    print STDOUT "<th > Mean  </a></th>\n";
+    print STDOUT "<th > Sigma (raw) </a></th>\n";
+    print STDOUT "<th > Sigma (clipped) </a></th>\n";
+    print STDOUT "<th > Sigma (med) </a></th>\n";
+    print STDOUT "<th > image </th>\n";
+    print STDOUT "<th > used? </th></tr>\n";
+    
+    # sort the list by image seq number
+    @order = ();
+    for ($i = 0; $i < $Nimage; $i++) { @order = (@order, $i); }
+    $col = 0;
+    @entry = sort by_col @order;
+    
+    # create the image-by-image table lines
+    for ($i = 0; $i < $Nimage; $i++) {
+	$N = $entry[$i];
+	
+	@word = split (" ", $statdata[$N]);
+	$used = $word[12];
+	$dawn = $word[11];
+	
+	@tmpw = split (" ", $mastdata[$N]);
+	$keep = $tmpw[1];
+	
+	@tmpw = split ("/", $medndata[$N]);
+	$medname = $tmpw[-1];
+	
+	@tmpw = split ("/", $tenndata[$N]);
+	$tenname = $tmpw[-1];
+	
+	print STDOUT "<tr><td> $word[0] </td><td> $word[1] </td>\n";
+	if ($dawn) { print STDOUT "<td bgcolor=#cccccc> $word[2] </td>\n"; }
+	else       { print STDOUT "<td> $word[2] </td>\n"; }
+	print STDOUT "<td> $word[3] </td>\n";
+	print STDOUT "<td> $word[4] </td><td> $word[6] </td><td> $word[8] </td>\n";
+	print STDOUT "<td> <a border=0 href=$dadswww/$tenname> <img src=$dadswww/$medname></a></td>\n";
+	if ($used) { print STDOUT "<td bgcolor=#00c000>yes</td>\n"; }
+	else       { print STDOUT "<td bgcolor=#d00000>no</td>\n"; }
+	print STDOUT "</tr>\n\n"; 	
+    }
+    
+    print STDOUT "</table>\n";
+    &goodbye;
+}
+
+
+
+############## html level 1 ###############
+sub mk_dads_top {
+    
+    if (@ARGV != 1) {
+	print STDERR "USAGE: $MKFRINGE dads.top\n";
+	&goodbye; 
+    }
+    
+    @list = load_config ();
+    
+    ## html headers
+    print STDOUT "<HEADER> <TITLE>Elixir Mkfringe Report</TITLE> </HEADER>\n";
+    print STDOUT "<BODY BGCOLOR=#fff0f0 TEXT=#000000 LINK=#0000ff VLINK=#6600ff ALINK=#a0a0a0>\n";
+
+    print STDOUT "<table border>\n";
+    for ($i = 0; $i < @list; $i++) {
+	$config  = gtconfig ($list[$i], "config");
+	$version = gtconfig ($list[$i], "version");
+	$from    = gtconfig ($list[$i], "from");
+	$to      = gtconfig ($list[$i], "to");
+	$status  = gtconfig ($list[$i], "status");
+	print STDOUT "<tr><td> $version </td>\n";
+	print STDOUT "<td> $from </td><td> $to </td>\n";
+	print STDOUT "<td> <a href=$version.html> $status </a></td>\n";
+	print STDOUT "</tr>\n";
+    }
+    
+    print STDOUT "</table></body>\n";
+    &goodbye;
+    
+}
+
+sub by_col { 
+    @word = split (" ", $statdata[$a]); $va = $word[$col]; 
+    @word = split (" ", $statdata[$b]); $vb = $word[$col]; 
+    $va <=> $vb;
+}
+
+sub flips_report {
+    
+    $col = 0;
+    ($config) = $_[0] =~ /([-\w]+\.[-\w]+\.[-\w]+)/;
+    ($key)    = $_[0] =~ /([-\w]+\.[-\w]+\.[-\w]+\.[-\w]+)/; # (config.ver)
+
+    # look for optional sort key (config.ver[.key])
+    if ($_[0] =~ /$key.(\d+)/) {
+	($col) = $_[0] =~ /$key.(\d+)/; 
+    } else {
+	$col = 0;
+    }
+
+    $line = $_[1];
+    $message = $_[2];
+    
+    # list files: VERSION.imstats, VERSION.med.medbin.list, VERSION.med.tenbin.list, VERSION.def.medbin.list VERSION.def.tenbin.list
+
+    $root = mknames ("htmlroot", $line);
+    open (FILE, "$root.imstats");         @statdata = <FILE>; close (FILE);
+    open (FILE, "$root.med.medbin.list"); @meddata = <FILE>; close (FILE);
+    open (FILE, "$root.def.medbin.list"); @defdata = <FILE>; close (FILE);
+
+# format of statdata file:
+#  OBS     DATE        TIME        SKY      FLUX   FR1    dFR1    FR2   dFR2  stat keep
+#  606363  2001-10-09  10:21:15.62 12455.7  20.76  246.9   84.0   N/A    N/A   1   1
+
+    $Nkeep = 0;
+    $Ncnt = 0;
+    $Resid = 0;
+    $fResid = 0;
+    $Nimage = @statdata;
+    for ($i = 0; $i < $Nimage; $i++) {
+	@tmp = split (" ", $statdata[$i]);
+	if ($tmp[9]) { $Nkeep++; $Ncnt += $tmp[3]; }
+	$Resid  += $tmp[8];
+	$fResid += $tmp[8] / $tmp[3];
+    }
+    if ($Nimage > 0) { $Resid = $Resid / $Nimage; }
+    if ($Nimage > 0) { $fResid = $fResid / $Nimage; }
+    $Resid = sprintf "%7.3f", $Resid;
+    $fResid = sprintf "%7.3f", $fResid;
+
+    # $imbin = mknames ("dads.imbin", $line);
+    print STDOUT "<table width=100%><tr><td valign=top>\n";
+    print STDOUT "this config has been $message. <br>\n";
+    print STDOUT "there are $Nimage images, $Nkeep used. <br>\n";
+    print STDOUT "Total Counts: $Ncnt<br>\n";
+    print STDOUT "Median residual scatter: $Resid <br>\n";
+    print STDOUT "Median fractional residual: $fResid <br><br>\n";
+    print STDOUT "you have the following choices: <br>\n";
+    print STDOUT "<a href=$CGI/fr.elixir1?&elconf=$elconf> <b> return to top level </b><br></a>";
+    print STDOUT "<a href=$CGI/fr.split?$config&elconf=$elconf> <b> split config </b><br></a>";
+    print STDOUT "<a href=$CGI/fr.accept?$key&elconf=$elconf> <b> accept config </b><br></a></td>";
+    if (-e "$root.medbin.jpg") { 
+	@tmpw = split ("/", "$root.medbin.jpg");
+	$imname = "$www/$tmpw[-1]";
+	print STDOUT "<td align=right><img width=100% src=$imname></td>\n";
+    }
+    print STDOUT "</tr></table>\n";
+
+    print STDOUT "<form method=post action=$CGI/fr.modify?&elconf=$elconf>\n";
+    print STDOUT "<center><b>Statistics on each available input image.</b></center>\n";
+    print STDOUT "Time is in UT. Click on the column heading to sort by that entry\n";
+
+    print STDOUT "<table bgcolor=#fff0e0 border=1 cellspacing=0 cellpadding=3>\n";
+    print STDOUT "<tr bgcolor=#00aaaa>\n";
+    print STDOUT "<th > <a href=$CGI/fr.elixir2?$key.0&elconf=$elconf> image </a></th>\n";
+    print STDOUT "<th > Date </th>\n";
+    print STDOUT "<th > Time </th>\n";
+    print STDOUT "<th > <a href=$CGI/fr.elixir2?$key.3&elconf=$elconf> Sky </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/fr.elixir2?$key.4&elconf=$elconf> Flux </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/fr.elixir2?$key.5&elconf=$elconf> Fringe </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/fr.elixir2?$key.6&elconf=$elconf> dFringe </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/fr.elixir2?$key.7&elconf=$elconf> Resid </a></th>\n";
+    print STDOUT "<th > <a href=$CGI/fr.elixir2?$key.8&elconf=$elconf> dResid </a></th>\n";
+    print STDOUT "<th > F image </th>\n";
+    print STDOUT "<th > R image </th>\n";
+    print STDOUT "<th > used? </th>\n";
+    print STDOUT "<th > keep? </th></tr>\n\n";
+    
+    @order = ();
+    for ($i = 0; $i < $Nimage; $i++) { @order = (@order, $i); }
+    
+    @entry = sort by_col @order;
+    
+    for ($i = 0; $i < $Nimage; $i++) {
+	$N = $entry[$i];
+	
+	@word = split (" ", $statdata[$N]);
+	$used = $word[9];
+	$keep = $word[10];
+	
+	@tmpw = split ("/", $meddata[$N]);
+	$medname = $tmpw[-1];
+	@tmpw = split ("/", $defdata[$N]);
+	$defname = $tmpw[-1];
+	
+	print STDOUT "<tr><td> <a href=$CGI/fr.elixir3?$N.$key&med&elconf=$elconf> $word[0] </a></td><td> $word[1] </td>\n";
+	print STDOUT "<td> $word[2] </td>\n"; 
+	print STDOUT "<td> $word[3] </td>\n";
+	print STDOUT "<td> $word[4] </td>\n";
+	print STDOUT "<td> $word[5] </td>\n";
+	print STDOUT "<td> $word[6] </td>\n";
+	print STDOUT "<td> $word[7] </td>\n";
+	print STDOUT "<td> $word[8] </td>\n";
+	print STDOUT "<td> <a border=0 href=$CGI/fr.elixir3?$N.$key&med&elconf=$elconf> <img src=$www/$medname></a></td>\n";
+	if ("$defname" ne "") {
+	    print STDOUT "<td> <a border=0 href=$CGI/fr.elixir3?$N.$key&def&elconf=$elconf> <img src=$www/$defname></a></td>\n";
+	} else {
+	    print STDOUT "<td> N/A </td>\n";
+	}
+	if ($used) { print STDOUT "<td>yes</td>\n"; }
+	else       { print STDOUT "<td>no</td>\n"; }
+	if ($keep) { print STDOUT "<td bgcolor=#00c000><input type=checkbox checked name=image.$N value=$N size=1></td></tr>\n\n"; }
+	else       { print STDOUT "<td bgcolor=#d00000><input type=checkbox name=image.$N value=$N size=1></td></tr>\n\n"; }	
+	
+    }
+    
+    print STDOUT "</table>\n";
+    print STDOUT "<input type=hidden    name=Nimage value=\"$Nimage\">\n";
+    print STDOUT "<input type=hidden    name=config value=\"$key\">\n";
+    print STDOUT "<input type=submit    value=\"apply changes\">\n";
+    print STDOUT "<input type=reset     value=Reset>\n";
+    print STDOUT "</form>\n";
+
+    print STDOUT "<center>\n";
+    print STDOUT "<b>Fringe Correlation Plot</b><br>\n";
+    if (-e "$root.stats.png") { 
+	@tmpw = split ("/", "$root.stats.png");
+	$imname = "$www/$tmpw[-1]";
+	print STDOUT "<img src=$imname>\n";
+    }
+    print STDOUT "</center>\n";
+
+    print STDOUT "</body>\n";
+    &goodbye;
+    
+}
+
+sub flips_report_done {
+    
+    ($config) = $_[0] =~ /([-\w]+\.[-\w]+\.[-\w]+)/;
+    ($key)    = $_[0] =~ /([-\w]+\.[-\w]+\.[-\w]+\.[-\w]+)/; # (config.ver)
+
+    $line = $_[1];
+    
+    open (STAT, mknames ("stats", $line));
+    open (MED,  mknames ("medbin", $line));
+    open (MASTER, mknames ("master", $line, 0));
+    
+    print STDOUT "<table border>\n";
+    print STDOUT "<tr><th> image </th><th> Date </th><th> Time </th><th> Mean </th>\n";
+    print STDOUT "<th> Sigma (raw) </th><th> Sigma (clipped) </th><th> Sigma (med) </th><th> image </th><th> used? </th></tr>\n\n";
+    
+    for ($i = 0; $line = <STAT>; $i++) {
+	
+	chop ($line);
+	# print STDOUT "$line<br>\n";
+	@word = split (" ",$line);
+	$used = $word[12];
+	$dawn = $word[11];
+	
+	$line = <MASTER>;
+	chop ($line);
+	# print STDOUT "$line<br>\n";
+	@tmpw = split (" ",$line);
+	$keep = $tmpw[1];
+	
+	# mkfringe works with files in the user's paths, but the web page needs 
+	# to work with files visible to the web server
+	$line = <MED>;
+	chop ($line);
+	@tmpw = split ("/", $line);
+	$N = @tmpw - 1;
+	$medname = $tmpw[$N];
+	
+	$sig1 = sprintf "%5.3f", $word[4];
+	$sig2 = sprintf "%5.3f", $word[5];
+	$sig3 = sprintf "%5.3f", $word[6];
+	$sig4 = sprintf "%5.3f", $word[7];
+	$mean = sprintf "%8.1f", $word[3];
+	
+	print STDOUT "<tr><td> <a href=$CGI/fr.elixir3?$i.$key&med&elconf=$elconf> $word[0] </a></td><td> $word[1] </td>\n";
+	if ($dawn) { print STDOUT "<td bgcolor=#cccccc> $word[2] </td>\n"; }
+	else       { print STDOUT "<td> $word[2] </td>\n"; } 
+	print STDOUT "<td> $word[3] </td>\n";
+	print STDOUT "<td> $word[5] </td><td> $word[6] </td><td> $word[8] </td><td><img src=$www/$medname></td>\n";
+	if ($used) { print STDOUT "<td bgcolor=#00c000>yes</td>\n"; }
+	else       { print STDOUT "<td bgcolor=#d00000>no</td>\n"; }
+    }
+    $Nimage = $i;
+    
+    print STDOUT "</table>\n";
+    print STDOUT "</body>\n";
+    
+    &goodbye;
+    
+}
+
+sub flips_entry {
+    
+    my($version)= $_[0];		# version of desired entry
+    my($entry)  = $_[1];		# desired entry in list
+    my($line)   = $_[2];		# list of image stats
+    my($mode)   = $_[3];                # 'def' or 'med'
+    
+    if (($mode ne "def") && ($mode ne "med")) {
+	print STDOUT "error in $MKFRINGE mode\n";
+	&goodbye;
+    }
+
+    # load image & stat lists
+    $root = mknames ("htmlroot", $line);
+    open (FILE, "$root.imstats");         @statdata = <FILE>; close (FILE);
+    open (FILE, "$root.$mode.tenbin.list"); @meddata = <FILE>; close (FILE);
+
+    $Nimage = @statdata;
+    if ($entry >= $Nimage) {
+	print STDOUT "</table> selected entry out of range\n";
+	&goodbye;
+    }
+    
+    $first = ($entry == 0);
+    $last  = ($entry == $Nimage - 1);
+    $next  = $entry + 1;
+    $prev  = $entry - 1;
+    
+    $line = $statdata[$entry];
+    chop ($line);
+    @word = split (" ",$line);
+    $used = $word[9];
+    $keep = $word[10];
+    
+    # mkfringe works with files in the user's paths, but the web page needs 
+    # to work with files visible to the web server
+    $line = $meddata[$entry];
+    chop ($line);
+    @tmpw = split ("/", $line);
+    $name = "$www/$tmpw[-1]";
+    
+    print STDOUT "<img width=100% src=$name>\n";
+    print STDOUT "mode: $mode<br>\n";
+    
+    print STDOUT "<table><tr><td>\n";
+    print STDOUT "<table border>\n";
+    print STDOUT "<tr><th> image    </th><th> Date     </th><th> Time     </th><th> Sky      </th><th> Flux     </th><th> Fringe   </th><th> dFringe  </th><th> Resid    </th><th> dResid   </th><th> used? </th></tr>\n";
+    if ($mode eq "med") {
+	print STDOUT "<tr><td> <a href=$CGI/fr.elixir3?$entry.$version&def&elconf=$elconf> $word[0] </a></td>\n";
+    } else {
+	print STDOUT "<tr><td> <a href=$CGI/fr.elixir3?$entry.$version&med&elconf=$elconf> $word[0] </a></td>\n";
+    }
+    print STDOUT "<td> $word[1] </td><td> $word[2] </td><td> $word[3] </td><td> $word[4] </td><td> $word[5] </td><td> $word[6] </td><td> $word[7] </td><td> $word[8] </td>\n";
+    if ($used) {
+	print STDOUT "<td>yes</td>\n";
+    } else {
+	print STDOUT "<td bgcolor=#ff0000>no</td>\n";
+    }	
+    print STDOUT "</tr>\n\n";
+    print STDOUT "</table>\n";
+    
+    print STDOUT "</td></tr>\n";
+
+    print STDOUT "<tr><td> return to <a href=$CGI/fr.elixir1?&elconf=$elconf> config list </a></tr></td>\n"; 
+    print STDOUT "<tr><td> return to <a href=$CGI/fr.elixir2?$version&elconf=$elconf> $version </a></tr></td>\n"; 
+    if (!$first) { print STDOUT "<tr><td> goto <a href=$CGI/fr.elixir3?$prev.$version&$mode&elconf=$elconf> prev </a> image </td></tr>\n"; }
+    if (!$last)  { print STDOUT "<tr><td> goto <a href=$CGI/fr.elixir3?$next.$version&$mode&elconf=$elconf> next </a> image </td></tr>\n"; }
+    print STDOUT "</table>\n";
+    
+    print STDOUT "</body>\n";
+    &goodbye;
+}
+
+sub map_report {
+    
+    print STDOUT "A Simple Test <br>\n";
+
+    $col = 0;
+    ($config) = $_[0] =~ /([-\w]+\.[-\w]+\.[-\w]+)/;
+    ($key)    = $_[0] =~ /([-\w]+\.[-\w]+\.[-\w]+\.[-\w]+)/; # (config.ver)
+
+    # look for optional sort key (config.ver[.key])
+    if ($_[0] =~ /$key.(\d+)/) {
+	($col) = $_[0] =~ /$key.(\d+)/; 
+    } else {
+	$col = 0;
+    }
+
+    $line = $_[1];
+    $message = $_[2];
+    
+    open (LIST, mknames ("maplist",  $line)); @mapdata  = <LIST>; close (LIST);
+    open (LIST, mknames ("modelist", $line)); @modedata = <LIST>; close (LIST);
+    
+    open (LIST, mknames ("modesjpg", $line)); @modeslist = <LIST>; close (LIST);
+    open (LIST, mknames ("residjpg", $line)); @residlist = <LIST>; close (LIST);
+    open (LIST, mknames ("inmapjpg", $line)); @inmaplist = <LIST>; close (LIST);
+    foreach $name (@modeslist) { chop ($name); }
+    foreach $name (@residlist) { chop ($name); }
+    foreach $name (@inmaplist) { chop ($name); }
+
+    print STDOUT "You have the following choices: <br>\n";
+    print STDOUT "<a href=$CGI/fr.elixir1?&elconf=$elconf> <b> return to top level </b><br></a>";
+    print STDOUT "<a href=$CGI/fr.mapaccept?$key&elconf=$elconf> <b> accept config </b><br></a></td>";
+
+    print STDOUT "Modes avaliable for this config <br>\n";
+    print STDOUT "<form method=post action=$CGI/fr.modes?&elconf=$elconf>\n";
+    # 4 x N table of possible modes
+    $MaxModes = 8;
+    print STDOUT "<table bgcolor=#fff0e0 border=1 cellspacing=0 cellpadding=3>\n";
+    for ($i = 0; $i < $MaxModes; $i++) {
+	if ((($i - 0) % 4) == 0) { print STDOUT "<tr>\n"; }
+	($junk, $status) = split (" ", $modedata[$i]);
+	$name = sprintf "%s/%s", $www, $modeslist[$i];
+	print STDOUT "<td> <img height=64 src=$name> </td>\n";
+	if ($status) { print STDOUT "<td bgcolor=#00c000> <input type=checkbox checked name=mode.$i value=$i size=1> </td>\n"; }
+	else {         print STDOUT "<td bgcolor=#d00000> <input type=checkbox name=mode.$i value=$i size=1> </td>\n"; }
+	if ((($i - 3) % 4) == 0) { print STDOUT "</tr>\n"; }
+    }
+    print STDOUT "</table>\n";
+    print STDOUT "<input type=hidden    name=Nmode value=\"$MaxModes\">\n";
+    print STDOUT "<input type=hidden    name=config value=\"$key\">\n";
+    print STDOUT "<input type=submit    value=\"set mode selection\">\n";
+    print STDOUT "<input type=reset     value=Reset>\n";
+    print STDOUT "</form>\n";
+
+    # 2 x N table of image residuals
+    print STDOUT "Residual images <br>\n";
+    print STDOUT "<form method=post action=$CGI/fr.maps?&elconf=$elconf>\n";
+    print STDOUT "<table bgcolor=#fff0e0 border=1 cellspacing=0 cellpadding=3>\n";
+    for ($i = 0; $i < @mapdata; $i++) { 
+	if ((($i - 0) % 6) == 0) { print STDOUT "<tr>\n"; }
+
+	($name, $status) = split (" ", $mapdata[$i]);
+	@words = split ("/", $name);
+	$basename = $words[-1];
+	$basename =~ s/.maplist$//;
+
+	$imname = sprintf "%s/%s", $www, $residlist[$i];
+	print STDOUT "<td> $basename </td>\n";
+	print STDOUT "<td> <img height=64 src=$imname> </td>\n";
+	if ($status) { print STDOUT "<td bgcolor=#00c000> <input checked type=checkbox name=map.$i value=$i size=1> </td>\n"; }
+	else         { print STDOUT "<td bgcolor=#d00000> <input         type=checkbox name=map.$i value=$i size=1> </td>\n"; }
+
+	if ((($i - 5) % 6) == 0) { print STDOUT "</tr>\n"; } 
+	else { print STDOUT "<td width=10 bgcolor=#ffffff> &nbsp; </td>\n"; }
+	
+    }
+    print STDOUT "</table>\n";
+    $Nmaps = @mapdata;
+    print STDOUT "<input type=hidden    name=Nmaps value=\"$Nmaps\">\n";
+    print STDOUT "<input type=hidden    name=config value=\"$key\">\n";
+    print STDOUT "<input type=submit    value=\"set image selection\">\n";
+    print STDOUT "<input type=reset     value=Reset>\n";
+    print STDOUT "</form>\n";
+
+    # 2 x N table of source images 
+    print STDOUT "Source images <br>\n";
+    print STDOUT "<form method=post action=$CGI/fr.maps?&elconf=$elconf>\n";
+    print STDOUT "<table bgcolor=#fff0e0 border=1 cellspacing=0 cellpadding=3>\n";
+    for ($i = 0; $i < @mapdata; $i++) { 
+	if ((($i - 0) % 6) == 0) { print STDOUT "<tr>\n"; }
+
+	($name, $status) = split (" ", $mapdata[$i]);
+	@words = split ("/", $name);
+	$basename = $words[-1];
+	$basename =~ s/.maplist$//;
+
+	$imname = sprintf "%s/%s", $www, $inmaplist[$i];
+	print STDOUT "<td> $basename </td>\n";
+	print STDOUT "<td> <img height=64 src=$imname> </td>\n";
+	if ($status) { print STDOUT "<td bgcolor=#00c000> <input checked type=checkbox name=map.$i value=$i size=1> </td>\n"; }
+	else         { print STDOUT "<td bgcolor=#d00000> <input         type=checkbox name=map.$i value=$i size=1> </td>\n"; }
+
+	if ((($i - 5) % 6) == 0) { print STDOUT "</tr>\n"; } 
+	else { print STDOUT "<td width=10 bgcolor=#ffffff> &nbsp; </td>\n"; }
+    }
+    print STDOUT "</table>\n";
+    $Nmaps = @mapdata;
+    print STDOUT "<input type=hidden    name=Nmaps value=\"$Nmaps\">\n";
+    print STDOUT "<input type=hidden    name=config value=\"$key\">\n";
+    print STDOUT "<input type=submit    value=\"set image selection\">\n";
+    print STDOUT "<input type=reset     value=Reset>\n";
+    print STDOUT "</form>\n";
+    &goodbye;
+    
+}
+
+print STDOUT "error: invalid $MKFRINGE command\n";
+
+# run:    $root/detrend.config    - current run
+# detdir: $det                    - directory for current run
+# config: $det/fringe.config      - config info for specific run, 1 line per setup
+# dates:  $det/dates.dat          - dates for specific run
+# list:   $det/fringe.list        - temporary list to pass to elixirs
+# plist:  $det/fringe.plist       - list of files to process (detrend) for elixir
+# proc:   $det/proc               - directory to store temporary processed images
+# fringe: $det/fringe             - directory to store data files
+# html:   $det/html               - directory to store results for www
+# www:    $www/$run/html          - html directory as seen by Web server
+
+####  TOOLS TO ABSTRACT FILENAMES  ###################################################################
+
+# construct specific file names
+sub mkfiles {
+    # in: (type)
+    my($type) = $_[0];
+    my($value);
+    
+    { 
+	if ($type eq "run") {
+	    $value = "$root/fringe.config";
+	    last;
+	}
+	if ($type eq "detdir") {
+	    $value = "$det";
+	    last;
+	}
+	if ($type eq "config") {
+	    $value = "$det/fringe.config";
+	    last;
+	}
+	if ($type eq "dates") {
+	    $value = "$det/dates.dat";
+	    last;
+	}
+	if ($type eq "list") {
+	    $value = "$det/fringe.list";
+	    last;
+	}
+	if ($type eq "plist") {
+	    $value = "$det/fringe.plist";
+	    last;
+	}
+	if ($type eq "proc") {
+	    $value = "$det/proc";
+	    last;
+	}
+	if ($type eq "fringe") {
+	    $value = "$det/fringe";
+	    last;
+	}
+	if ($type eq "links") {
+	    $value = "$det/flips/links";
+	    last;
+	}
+	if ($type eq "html") {
+	    $value = "$det/html";
+	    last;
+	}
+	if ($type eq "www") {
+	    $value = "$www";
+	    last;
+	}
+	&escape ("unknown type for mkfiles");
+    }
+    $value;    
+}
+
+# construct files from config info:
+# CRUNID TYPE FILTER [CCD] VERSION START STOP STATE
+#**************************
+
+sub mknames {
+    # in: (type) (config.ver) [ccd]
+    my ($type, $version, @words, $dir, $value, $ccd);
+
+    $type = $_[0];
+    $version = $_[1];
+    @words = split (" ", $version);
+    $ccd = $_[2];
+    
+    { 
+	# list of fringe frames
+	if ($type eq "config") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3]";
+	    last;
+	}
+	# list of fringe frames
+	if ($type eq "frlist") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3].frlist";
+	    last;
+	}
+	# smoothed (final) fringe image
+	if ($type eq "smfile") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].sm.fits";
+	    last;
+	}
+	# raw fringe image
+	if ($type eq "frfile") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].fr.fits";
+	    last;
+	}
+	# master list of input images
+	if ($type eq "master") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3].master";
+	    last;
+	}
+	# output link file for fhtool
+	if ($type eq "link") {
+	    $dir = mkfiles ("links");
+	    if ($ccd eq "") { &escape ("missing CCD for mknames link"); }
+	    $value = "$dir/$words[0].$words[1].$words[2].$ccd.$words[3].fits";
+	    last;
+	}
+	# output mef file name
+	if ($type eq "mef") {
+	    $dir = mkfiles ("fringe");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].fits";
+	    last;
+	}
+	# list of image statistics files
+	if ($type eq "imstats") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3].imstats";
+	    last;
+	}
+	# list of image map lists
+	if ($type eq "maplist") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3].maplist";
+	    last;
+	}
+	# list of image map lists
+	if ($type eq "modelist") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3].mode.list";
+	    last;
+	}
+	# list of image map lists
+	if ($type eq "modefits") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3].mode.fits";
+	    last;
+	}
+	# list of image map lists
+	if ($type eq "modesave") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3].mode.mef.fits";
+	    last;
+	}
+	# mosaic fringe statistics file
+	if ($type eq "frstats") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3].frstats";
+	    last;
+	}
+	# images to be detrended
+	if ($type eq "detrend") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3].detrend";
+	    last;
+	}
+	# list of input image lists to defringe
+	if ($type eq "defringe") {
+	    $dir = mkfiles ("fringe");
+	    $value = sprintf "$dir/$words[0].$words[1].$words[2].$words[3].defringe";
+	    last;
+	}
+	# file in HTML dir with all image stats
+	if ($type eq "htmlroot") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3]";
+	    last;
+	}
+	# file in HTML dir with all image stats
+	if ($type eq "stats") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].stats";
+	    last;
+	}
+	# file in HTML dir with all medbin jpg image names (also root for images)
+	if ($type eq "modesjpg") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].modes";
+	    last;
+	}
+	# file in HTML dir with all medbin jpg image names (also root for images)
+	if ($type eq "residjpg") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].resid";
+	    last;
+	}
+	# file in HTML dir with all medbin jpg image names (also root for images)
+	if ($type eq "inmapjpg") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].inmap";
+	    last;
+	}
+	# file in HTML dir with all medbin jpg image names (also root for images)
+	if ($type eq "medbin") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].medbin";
+	    last;
+	}
+	# file in HTML dir with all tenbin jpg image names (also root for images)
+	if ($type eq "tenbin") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].tenbin";
+	    last;
+	}
+	# file in HTML dir with jpg image of fringe framge
+	if ($type eq "imbin") {
+	    $dir = mkfiles ("html");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].imbin.jpg";
+	    last;
+	}
+	# file in WWW dir with jpg image of fringe framge
+	if ($type eq "dads.imbin") {
+	    $dir = mkfiles ("www");
+	    $value = "$dir/$words[0].$words[1].$words[2].$words[3].imbin.jpg";
+	    last;
+	}
+	&escape ("unknown type for mknames: $type");
+    }    
+    return ($value);
+}
+
+####  TOOLS TO INTERACT WITH FORMATED CONFIG FILE  #####################################################
+
+# parse config info
+# CRUNID TYPE FILTER [CCD] VERSION START STOP STATE
+#**************************
+sub gtconfig {
+    my ($value, $type, @words, $ccd);
+    # in: (config line) (type)
+    @words = split (" ", $_[0]);
+    $type = $_[1];
+
+    { 
+	if ($type eq "elixir") {
+	    $value = "$words[0] $words[1] $words[2] $words[3] $words[4] $words[5]";
+	    last;
+	}
+	if ($type eq "chiprun") {
+	    $ccd  = $_[2];
+	    $value = "$words[0] $words[1] $words[2] $ccd $words[3] $words[4] $words[5]";
+	    last;
+	}
+	if ($type eq "config") {
+	    $value = "$words[0].$words[1].$words[2]";
+	    last;
+	}
+	if ($type eq "filter") {
+	    $value = "$words[2]";
+	    last;
+	}
+	if ($type eq "ID") {
+	    $value = "$words[0]";
+	    last;
+	}
+	if ($type eq "version") {
+	    $value = "$words[0].$words[1].$words[2].$words[3]";
+	    last;
+	}
+	if ($type eq "from") {
+	    $value = "$words[4]";
+	    last;
+	}
+	if ($type eq "to") {
+	    $value = "$words[5]";
+	    last;
+	}
+	if ($type eq "status") {
+	    $value = "$words[6]";
+	    last;
+	}
+	&escape ("unknown type for gtconfig");
+    }
+
+    return $value;
+}
+
+# set config value
+# TYPE FILTER CRUNID VERSION START STOP STATE
+# CRUNID TYPE FILTER [CCD] VERSION START STOP STATE
+#**************************
+sub stconfig {
+    # in: (config line) (value) (type)
+    my (@words) = split (" ", $_[0]);
+    my ($type)  = $_[1];
+    my ($value) = $_[2];
+    my ($line);
+
+    { 
+	if ($type eq "all") {
+	    $line = sprintf "%s %6s %4s %2d %s %s %s", $words[0], $words[1], $words[2], $words[3], $words[4], $words[5], $words[6];
+	    last;
+	}
+	if ($type eq "version") {
+	    $line = sprintf "%s %6s %4s %2d %s %s %s", $words[0], $words[1], $words[2], $value, $words[4], $words[5], $words[6];
+	    last;
+	}
+	if ($type eq "start") {
+	    $line = sprintf "%s %6s %4s %2d %s %s %s", $words[0], $words[1], $words[2], $words[3], $value, $words[5], $words[6];
+	    last;
+	}
+	if ($type eq "stop") {
+	    $line = sprintf "%s %6s %4s %2d %s %s %s", $words[0], $words[1], $words[2], $words[3], $words[4], $value, $words[6];
+	    last;
+	}
+	if ($type eq "status") {
+	    $line = sprintf "%s %6s %4s %2d %s %s %s", $words[0], $words[1], $words[2], $words[3], $words[4], $words[5], $value;
+	    last;
+	}
+	&escape ("unknown type for stconfig");
+    }
+
+    return $line;
+}
+
+# load dates & mode (returns @list)
+sub load_dates {
+    my ($file, $line, @list);
+    my ($start, $stop);
+    
+    $file = mkfiles ("dates");
+    @list = ();
+    open (FILE, "$file");
+    @list = <FILE>;
+    close (FILE);
+
+    ($tmp, $start) = split (" ", $list[0]);
+    if ($tmp ne "START") { print STDERR "dates file is confused\n"; }
+
+    ($tmp, $stop) = split (" ", $list[1]);
+    if ($tmp ne "STOP") { print STDERR "dates file is confused\n"; }
+    
+    ($tmp, $mode) = split (" ", $list[2]);
+    if ($tmp ne "MODE") { print STDERR "dates file missing mode\n"; }
+    if ($mode eq "") { $mode = "hold"; }
+
+    ($start, $stop, $mode);
+}
+
+# write to dates file
+sub save_dates {
+    # save_dates start stop auto.mode
+    my (@outlist) = @_;
+
+    my ($file) = mkfiles ("dates");
+    open (FILE, ">$file");
+    print FILE "START $outlist[0]\n";
+    print FILE "STOP  $outlist[1]\n";
+    print FILE "MODE  $outlist[2]\n";
+    close (FILE);
+}    
+
+# load config lines (returns @list)
+sub load_config {
+    my ($file, $line, @list);
+    
+    $file = mkfiles ("config");
+    @list = ();
+    open (FILE, "$file");
+    while ($line = <FILE>) {
+	chop ($line);
+	@list = (@list, $line);
+    }
+    close (FILE);
+    
+    @list;
+}
+
+# write to config file
+sub save_config {
+
+    my(@outlist) = sort @_;
+    
+    my($file) = mkfiles ("config");
+    open (FILE, ">$file");
+    for ($i = 0; $i < @outlist; $i++) {
+	print FILE "$outlist[$i]\n";
+    }
+    close (FILE);
+    
+}    
+
+####  USEFUL TOOLS  #################################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye { 
+    unlink $tmpenv;
+    exit 0; 
+}
+
+sub escape { 
+    unlink $tmpenv;
+    print STDERR "$_[0]\n";
+    exit 1;
+}
+
+# grab complete configuration info from the elixir config system, 
+# applying appropriate command-line arguments
+sub elixir_config {
+    my (@argv) = @_;
+    my ($opt, $config, @tmparg, $status);
+    $config = "";
+    if ($ENV{'PTOLEMY'}) { $config = "-c $ENV{'PTOLEMY'}"; }
+    umask (0);
+
+    # look for optional command-line arguments
+    @tmparg = @argv;
+    foreach (@tmparg) {
+	$value = shift @argv;
+
+	if ($value eq "-c") {
+	    $value = shift @argv;
+	    $config = "-c $value";
+	    next;
+	}
+
+	if ($value eq "-C") {
+	    push @opt, $value;
+	    $value = shift @argv;
+	    push @opt, $value;
+	    $elconf = $value;
+	    next;
+	}
+	
+	if ($value eq "-D") {
+	    push @opt, $value;
+	    $value = shift @argv;
+	    push @opt, $value;
+	    $value = shift @argv;
+	    push @opt, $value;
+	    next;
+	}
+	
+	if ($value eq "-run") {
+	    $run = shift @argv;
+	    push @opt, "-D RUNID.MKFRINGE $run -D RUNID $run";
+	    next;
+	}
+	
+	if ($value eq "-camera") {
+	    $camera = shift @argv;
+	    push @opt, "-D CAMERA.MKFRINGE $camera -D CAMERA $camera";
+	    next;
+	}
+	push @argv, $value;
+    }
+
+    # load RUNID from state.data (unless set on command line)
+    if ($run eq "") {
+	$run = `gconfig RUNID.MKFRINGE`; chop $run;
+	if ($?) {  &escape ("run is not defined: use 'mkdetrend config' or 'mkrun sys'\n"); }
+	if ($run eq "") {  &escape ("run is not defined: use 'mkdetrend config' or 'mkrun sys'\n"); }
+	push @opt, "-D RUNID.MKDETREND $run -D RUNID $run";
+    }
+
+    # load CAMERA from state.data (unless set on command line)
+    if ($camera eq "") {
+	$camera = `gconfig CAMERA.MKFRINGE`; chop $camera;
+	if ($?) {  &escape ("camera is not defined: use 'mkrun sys' to set\n"); }
+	if ($camera eq "") {  &escape ("camera is not defined: use 'mkrun sys'\n"); }
+	push @opt, "-D CAMERA.MKDETREND $camera -D CAMERA $camera";
+    }
+
+    # set env PTOLEMY using values from command line
+    $config = join (" ", $config, @opt);
+    $tmpenv = `mktemp /tmp/elixir.XXXXXX`; chop ($tmpenv);
+    $status = system ("gconfig -raw $config > $tmpenv");
+    if ($status) { &escape ("error in elixir configuration: gconfig fails"); }
+    $ENV{'PTOLEMY'} = "$tmpenv";
+
+    # set global variables based on new config info
+    $root = `gconfig DETREND_ROOT`;  chop $root;
+    if ($?) { &escape ("error with elixir configuration variable: DETREND_ROOT"); }
+
+    $wwwroot  = `gconfig DETREND_WWW`;  chop $wwwroot;
+    if ($?) { &escape ("error with elixir configuration variable: DETREND_WWW"); }
+    
+    $Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+    if ($?) { &escape ("error with elixir camera configuration"); }
+
+    @ccds = split (" ", `cameraconfig -ccdn`);
+    if ($?) { &escape ("error with elixir camera configuration"); }
+
+    # get list of valid filters from lookup table
+    @filters = split (" ", `filtnames list`);
+    if ($?) { &escape ("error with elixir filter configuration"); }
+
+    # recipe file defines detrend types & cutoff exptime values
+    $recipefile = `gconfig DETREND_RECIPES`; chop $recipefile;
+    if ($?) { &escape ("missing DETREND_RECIPES in configuration"); }
+
+    @filt = ();
+    foreach $filter (@filters) {
+	($detypes) = split (" ", `gconfig -c $recipefile $filter`);
+	if ($?) { die "ERROR: missing detrend recipe for $filter\n"; }
+
+	@detypes = split (",", $detypes);
+	for ($i = 0; $i < @detypes; $i++) {
+	    if ($detypes[$i] eq "fringe") {
+		push @filt, $filter;
+		push @type, "fringe";
+		last;
+	    }
+	}
+    }
+
+    $CGI = `gconfig CGIBIN`; chop ($CGI);
+    if ($?) { &escape ("error with elixir configuration variable: CGIBIN"); }
+
+    $det  = "$root/$run";
+    $www  = "$wwwroot/$run/html";
+    return (@argv);
+}
+
+sub usage {
+
+    if (@ARGV == 1) {
+	print STDERR "\n --- user modes ---\n";
+	print STDERR "create (run) (startdate) (stopdate)\n";
+	print STDERR "mkconfig\n";
+	print STDERR "config (run)\n";
+	print STDERR "init\n";
+	print STDERR "run\n";
+	print STDERR "list.runs\n";
+	print STDERR "set (config.ver) (state)\n";
+	print STDERR "reg\n";
+	print STDERR "state \n";
+	print STDERR "help \n";
+	print STDERR "\n --- internal modes --- \n";
+	print STDERR "flips\n";
+	print STDERR "norm\n";
+	print STDERR "update\n";
+	print STDERR "merge\n";
+	print STDERR "dup (config)\n";
+	print STDERR "del (config)\n";
+	print STDERR "def (config.ver) (start) (stop)\n";
+	print STDERR "eval (step)\n";
+	print STDERR "mevel (step)\n";
+	print STDERR "htmldup\n";
+	print STDERR "htmldel\n";
+	print STDERR "htmldef\n";
+	print STDERR "htmlmod\n";
+	print STDERR "htmlkeep\n";
+	print STDERR "html1\n";
+	print STDERR "html2 (config.ver)\n";
+	print STDERR "html3 (entry.config.ver)\n";
+	&goodbye;
+    }
+    
+    if (@ARGV > 2) { print STDERR "USAGE: $MKFRINGE (help) [mode]\n"; &goodbye; }
+    
+    if ($ARGV[1] eq "config") {
+	print STDERR "set up initial configuration\n";
+	&goodbye;
+    }
+    
+    if ($ARGV[1] eq "save") {
+	print STDERR "save results from current configuration\n";
+	&goodbye;
+    }
+    
+    print STDERR "help for $ARGV[1] not defined\n";
+    &goodbye;
+}
+
+#### overview ########
+
+# init: create lists of possible images for each master type:
+#       01Ak01.fringe.I.0.master  (MOSAIC)
+
+# detrend: detrend the input images
+
+# files needed for html pages:
+
+# html/*
+# fringe/*.master
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkhtml
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkhtml	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkhtml	(revision 22322)
@@ -0,0 +1,183 @@
+#!/usr/bin/env perl
+
+$template = "template.htm";
+if ($ARGV[0] eq "-template") { 
+    shift @ARGV;
+    $template = $ARGV[0]; 
+    shift @ARGV; 
+}
+
+if (@ARGV != 3) { die "USAGE: mkhtml [-template template] (root) (src) (html)\n"; }
+
+$root     = $ARGV[0];
+$root     =~ s|/*$||; # strip trailing /'s to clean path
+
+$input    = $ARGV[1];
+$output   = $ARGV[2];
+$config   = "Configure";
+
+# print STDERR "template: $template\n";
+
+# load variables from Configure (eg, ROOT, etc)
+# these are interpolated except in the content section
+open (FILE, $config);
+@input = <FILE>;
+close (FILE);
+foreach $line (@input) {
+    ($var, $eq, $value) = split (" ", $line);
+    $$var = $value;
+}
+
+# load data from template
+open (FILE, $template);
+@template = <FILE>;
+close (FILE);
+
+# load data from input file
+open (FILE, $input);
+@input = <FILE>;
+close (FILE);
+
+# default metadata values:
+$title = &rootname ($input);
+if ($title eq "index") { $title = &basename (&dirname ($input)); }
+
+$page  = &rootname ($input);
+if ($page  eq "index") { $page  = &basename (&dirname ($input)); }
+
+$path  = &dirname ($input);
+
+$ext   = &extname ($input);
+
+# file is the entry in the index listing
+$file  = $output;
+if ($file =~ m|index.html|) { $file = &dirname ($output); }
+$file  =~ s|^$root|ROOT|;
+
+# apply ROOT variable to content (this comes from Configure)
+# foreach $line (@input) { while ($line =~ m|\$ROOT|) { $line =~ s|\$ROOT|$ROOT|; } }
+
+# search for the metadata lines in input file
+while (@input) {
+    $line = shift @input;
+    if ($line =~ /^\s*$/) { next; }
+    if ($line =~ /<meta\s+name=title\s+content=/) {
+	($title) = $line =~ /<meta\s+name=title\s+content=(.*)>/;
+	next;
+    }
+    if ($line =~ /<meta\s+name=page\s+content=/) {
+	($page) = $line =~ /<meta\s+name=page\s+content=(.*)>/;
+	next;
+    }
+    unshift @input, $line;
+    last;
+}
+
+# create the output file
+open (OUT, ">$output");
+
+$found = $menu = 0;
+foreach $line (@template) {
+    
+    chop $line;
+    while ($line =~ m|ROOT|) { $line =~ s|ROOT|$ROOT|; }
+    
+    # interpolate over basic metadata entries
+    if ($line =~ m|<meta\s+name=page>|) {
+	print OUT "$page\n";
+	next;
+    }
+    if ($line =~ m|<meta\s+name=title>|) {
+	print OUT "$title\n";
+	next;
+    }
+    if ($line =~ m|<meta\s+name=body>|) {
+	# add in parsing of <p> tags, etc in here
+	&dumpinput;
+	next;
+    }
+
+    # parse the index file
+    if ($line =~ m|<meta\s+name=index\s+file=|) {
+	($index) = $line =~ m|<meta\s+name=index\s+file=(.*)>|;
+	$index = "$path/$index";
+	open (INDEX, $index);
+	@index = <INDEX>;
+	# need to find and tag this file 
+	foreach $index (@index) {
+	    if ($index =~ m|href=$file>|) {
+		$index =~ s|class=doc|class=sel|;
+		$index =~ s|class=dir|class=dsl|;
+		$index =~ s|class=dl|class=sl|;
+		$index =~ s|$file>\s+-\s+|$file> + |;
+	    }
+	    # interpolate ROOT
+	    while ($index =~ m|ROOT|) { $index =~ s|ROOT|$ROOT|; }
+	}
+
+	close (INDEX);
+	print OUT @index;
+	next;
+    }
+    print OUT "$line\n";
+}
+    
+close (OUT);
+exit 0;
+
+sub dumpinput {
+
+    if ($ext eq "txt") {
+	print OUT "<pre>\n";
+    }
+    print OUT "@input";
+    if ($ext eq "txt") {
+	print OUT "</pre>\n";
+    }
+}
+
+# behaves just like system basename function (returns last portion of path)
+sub basename {
+
+    my ($file) = $_[0];
+    my ($base, @words);
+
+    @words = split ("/", $file);
+    $base = $words[-1];
+    return $base;
+}
+
+
+# behaves just like system dirname function (strips last portion of path)
+sub dirname {
+
+    my ($file) = $_[0];
+    my ($base, @words);
+
+    @words = split ("/", $file);
+    if (@words == 1) { return "."; }
+
+    pop @words;
+    $file = join ('/', @words);
+    return $file;
+}
+
+# behaves just like system basename function (returns last portion of path)
+sub extname {
+    my ($file) = &basename ($_[0]);
+    my($ext) = $file =~ m|\S*\.(\S*)$|;
+    return $ext;
+}
+
+# behaves just like system basename function (returns last portion of path)
+sub rootname {
+    my ($file) = &basename ($_[0]);
+    my($ext) = $file =~ m|(\S*)\.\S*$|;
+    return $ext;
+}
+
+# input metadata lines:
+# <meta name="file"  content="test">
+# <meta name="title" content="a test title">
+# <meta name="page"  content="test">
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkidx
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkidx	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkidx	(revision 22322)
@@ -0,0 +1,232 @@
+#!/usr/bin/env perl
+
+use strict;
+
+if (@ARGV != 1) { die "USAGE: mkidx (directory)\n"; }
+
+# input is the name of a directory and current level
+# outputs are: directory list, document list, index file for directory
+# menu for that directory
+
+# parse input info
+my($path);
+$path  = $ARGV[0];
+$path  =~ s|/*$||; # strip trailing /'s to clean path
+
+local($mkidx::root) = $path;
+
+&mksubidx ($path, 0, 0, "template.htm");
+exit 0;
+
+sub mksubidx {
+    # USAGE: mksubidx (path) (entry) (template) (index)
+    my($path)  	  = shift @_;
+    my($level) 	  = shift @_;
+    my($entry)    = shift @_;
+    my($template) = shift @_;
+    my(@updex)    = @_;
+
+    my($i, $j);
+    my($line, $value, $dir);
+    my(@list);
+    my($file, $root, $link, $ext);
+    my(@seq)    = ();
+    my(@dirs)   = ();
+    my(@mydirs) = ();
+    my(@docs)   = ();
+    my(@txts)   = ();
+    my(@index)  = ();
+    my(@mydex)  = ();
+    my($Nmydex);
+    my(@seqline);
+
+    # if this directory contains a template file, reset entry and level
+    # supply the template file to mkhtml here and below
+    if (-e "$path/template.htm") {
+	@updex = ();
+	$level = 0;
+	$entry = 0;
+	$template = "$path/template.htm";
+    }
+    # print STDERR "idx: $template\n";
+
+    # put leading portion of updex here
+    for ($i = 0; $i < $entry; $i++) {
+	push @index, $updex[$i];
+    }
+
+    # find all dirs in this directory
+    @list = <$path/*>;
+    foreach $file (@list) {
+	if (-e "$path/nochild.idx") { last; }
+	if (! -d $file) { next; }
+	$root = &basename ($file);
+	$link = $file;
+	$link =~ s|^$mkidx::root|ROOT|;
+
+	# list special directories here
+	if ($root eq "CVS") { next; }
+	if (-e "$file/noindex.idx") { next; }
+
+	push @mydirs, $file;
+
+	$line = sprintf "<tr><td class=dir%d> <a class=dl %-30s -%20s </a></td></tr>", $level, "href=$link>", $root;
+	push @mydex, $line;
+	push @seq, $root;
+    }
+
+    # find all docs (htm, txt) in this directory
+    @list = (<$path/*.htm>,<$path/*.hts>,<$path/*.txt>);
+    foreach $file (@list) {
+	if (! -f $file) { next; }
+
+	$ext = &extname ($file);
+	$root = &rootname ($file);
+
+	$link = $file;
+	$link =~ s|.$ext$|.html|;
+	$link =~ s|^$mkidx::root|ROOT|;
+
+	# list special files here
+	if ($root eq "index") { next; }
+	if ($root eq "template") { next; }
+	if ($root eq "sequence") { next; }
+
+	# print STDERR "ext: $ext, root: $root\n";
+
+	push @docs, $file;
+	push @mydirs, "0";
+	
+	if ($ext ne "hts") {
+	    # simple case: no extra information:
+	    $line = sprintf "<tr><td class=doc%d> <a class=dl %-30s &nbsp; %20s </a></td></tr>", $level, "href=$link>", $root;
+	    push @mydex, $line;
+	    push @seq, &basename ($file);
+	}
+    }
+
+    # look for directory sequencing information
+    if (-f "$path/sequence.idx") { 
+	open (FILE, "$path/sequence.idx");
+	@seqline = <FILE>;
+	close (FILE);
+      OUT:
+	for ($i = 0; $i < @seqline; $i++) {
+	    $Nmydex = @mydex;
+	    chop $seqline[$i];
+	  IN:
+	    for ($j = 0; $j < $Nmydex; $j++) {
+		$line  = shift @mydex;
+		$value = shift @seq;
+		$dir   = shift @mydirs;
+		if ($seqline[$i] eq $value) {
+		    push @index, $line;
+		    push @dirs, $dir;
+		    # print "ADD SEQ: $dir, $line\n";
+		    next OUT;
+		} else {
+		    push @mydex, $line;
+		    push @mydirs, $dir;
+		    push @seq, $value;
+		}
+	    }
+	}	
+    }
+    # add trailing portion of updex here
+    foreach $line (@mydex) {
+	$dir = shift @mydirs;
+	# print "ADD VAL: $dir, $line\n";
+	push @dirs, $dir;
+	push @index, $line;
+    }
+
+    # add trailing portion of updex here
+    for ($i = $entry; $i < @updex; $i++) {
+	push @index, $updex[$i];
+    }
+
+    for ($i = 0; $i < @dirs; $i++) {
+	if ($dirs[$i] eq 0) { next; }
+	&mksubidx ($dirs[$i], $level + 1, $i + $entry + 1, $template, @index);
+    }
+
+    # output index for this directory
+    open (FILE, ">$path/index.idx");
+    foreach $line (@index) {
+	print FILE "$line\n";
+    }
+    close (FILE);
+
+    # create html for docs in this directory
+    foreach $file (@docs, "$path/index.htm") {
+	$ext  = &extname ($file);
+	# print STDERR "ext: $ext\n";
+	# $root  = &rootname ($file);
+	# print STDERR "root: $root\n";
+	$link = $file;
+	$link =~ s|.$ext$|.html|;
+	system "mkhtml -template $template $mkidx::root $file $link";
+    }
+    return;
+}
+
+# behaves just like system basename function (returns last portion of path)
+sub basename {
+
+    my ($file) = $_[0];
+    my ($base, @words);
+
+    @words = split ("/", $file);
+    $base = $words[-1];
+    return $base;
+}
+
+# behaves just like system basename function (returns last portion of path)
+sub extname {
+    my ($file) = &basename ($_[0]);
+    my($ext) = $file =~ m|\S*\.(\S*)$|;
+    return $ext;
+}
+
+# behaves just like system basename function (returns last portion of path)
+sub rootname {
+    my ($file) = &basename ($_[0]);
+    my($ext) = $file =~ m|(\S*)\.\S*$|;
+    return $ext;
+}
+
+# behaves just like system dirname function (strips last portion of path)
+sub dirname {
+
+    my ($file) = $_[0];
+    my ($base, @words);
+
+    @words = split ("/", $file);
+    if (@words == 1) { return "."; }
+
+    pop @words;
+    $file = join ('/', @words);
+    return $file;
+}
+
+###
+#    # find all txt files in this directory
+#    @list = <$path/*.txt>;
+#    foreach $file (@list) {
+#	if (! -f $file) { next; }
+#
+#	$ext = &extname ($file);
+#
+#	$root = &basename ($file);
+#	$root =~ s|.txt$||;
+#	$link = $file;
+#	$link =~ s|.txt$|.html|;
+#	$link =~ s|^$mkidx::root|ROOT|;
+#
+#	push @txts, $file;
+#
+#	# simple case: no extra information:
+#	$line = sprintf "<tr><td class=doc%d> <a class=dl %-30s  %20s </a></td></tr>", $level, "href=$link>", $root;
+#	push @index, $line;
+#    }
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkrun
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkrun	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkrun	(revision 22322)
@@ -0,0 +1,711 @@
+#!/usr/bin/env perl
+
+# check usage
+if (@ARGV < 1) {
+    print STDOUT "USAGE: mkrun (mode) [arguments]\n";
+    print STDOUT " mkrun help gives a complete list\n";
+    &goodbye;
+}
+if ($ARGV[0] eq "help") { &usage; }
+
+# this should come fron the config system
+$runlist  = `gconfig RUN_LIST`; chop $runlist;
+@runlist  = ();
+
+# call appropriate command
+if ($ARGV[0] eq "create")    { &create; }
+if ($ARGV[0] eq "delete")    { &delete; }
+if ($ARGV[0] eq "hstcreate") { &hstcreate; }
+if ($ARGV[0] eq "addqso")    { &addqso; }
+if ($ARGV[0] eq "delqso")    { &delqso; }
+if ($ARGV[0] eq "comment")   { &comment; }
+if ($ARGV[0] eq "status")    { &status; }
+if ($ARGV[0] eq "state")     { &status; }
+if ($ARGV[0] eq "sys")       { &sysfunc; }
+if ($ARGV[0] eq "run")       { &getrun; }
+if ($ARGV[0] eq "date")      { &getdate; }
+if ($ARGV[0] eq "update")    { &update; }
+if ($ARGV[0] eq "test")      { &test; }
+if ($ARGV[0] eq "today")     { &today; }
+
+print STDOUT "invalid mkrun command\n";
+exit 1;
+
+###########################################################
+
+######
+sub test {
+
+    $date = mjd_to_date ($ARGV[1]);
+    print "$ARGV[1] : $date\n";
+    exit 1;
+}
+
+######
+sub sysfunc {
+
+    $statedata = `gconfig STATE_DATA`; chop ($statedata);
+    if ($?) { die "can't find STATE_DATA\n"; }
+
+    # show current state
+    if (@ARGV == 1) {
+	system ("cat $statedata");
+	exit 0;
+    }
+
+    if (@ARGV == 2) {
+	$var  = "\U$ARGV[1]\E";
+	if ($var eq "INIT")          { goto initsys; }
+	if ($var eq "RUNID")         { goto getsys; }
+	if ($var eq "PROCESS")       { goto getsys; }
+	if ($var eq "NOTIFY")        { goto getsys; }
+	if ($var =~ m|CAMERA.(\S+)|) { goto getsys; }
+	if ($var =~ m|RUNID.(\S+)|)  { goto getsys; }
+	die "invalid sys parameter $var\n"; 
+
+      getsys:
+	open (FILE, "$statedata");
+	@list = <FILE>;
+	close (FILE);
+
+	foreach $line (@list) {
+	    ($key, $value) = split (" ", $line);
+	    $key  = "\U$key\E";
+	    if ($key ne $var) { next; }
+	    print STDOUT "$value\n";
+	    exit 0;
+	}
+	die "can't find entry $var\n"; 
+
+      initsys:
+	open (FILE, "$statedata");
+	print FILE "# elixir current state\n";
+	print FILE "\n";
+	print FILE "RUNID 02Bk03\n";
+	print FILE "PROCESS 02Bk03\n";
+	print FILE "\n";
+	print FILE "CAMERA.CURRENT       none\n";
+	print FILE "CAMERA.MKDETREND     none\n";
+	print FILE "CAMERA.MKFRINGE      none\n";
+	print FILE "CAMERA.POSTRUN       none\n";
+	print FILE "\n";
+	print FILE "RUNID.CURRENT        none\n";
+	print FILE "RUNID.MKDETREND      none\n";
+	print FILE "RUNID.MKFRINGE       none\n";
+	print FILE "RUNID.POSTRUN        none\n";
+	print FILE "\n";
+	print FILE "NOTIFY               \n";
+	close (FILE);
+	exit 0;
+    }
+
+    if (@ARGV == 3) {
+	$var  = "\U$ARGV[1]\E";
+	$nval = $ARGV[2];
+	if ($var eq "RUNID")         { goto setsys; }
+	if ($var eq "PROCESS")       { goto setsys; }
+	if ($var eq "NOTIFY")        { goto setsys; }
+	if ($var =~ m|CAMERA.(\S+)|) { goto setsys; }
+	if ($var =~ m|RUNID.(\S+)|)  { goto setsys; }
+	die "invalid sys parameter $var\n"; 
+
+      setsys:
+	open (FILE, "$statedata");
+	@list = <FILE>;
+	close (FILE);
+
+	$found = 0;
+	foreach $line (@list) {
+	    ($key, $value) = split (" ", $line);
+	    $key  = "\U$key\E";
+	    if ($key ne $var) { next; }
+	    $found = 1;
+	    $line = sprintf "%-20s %s\n", $var, $nval;
+	}
+
+	if (! $found) { die "can't find entry $var\n"; }
+	open (FILE, ">$statedata");
+	foreach $line (@list) {
+	    print FILE "$line";
+	}
+	close (FILE);
+	exit 0;
+    }
+
+    print "USAGE: mkrun sys\n";
+    print "USAGE: mkrun sys init\n";
+    print "USAGE: mkrun sys (key)\n";
+    print "USAGE: mkrun sys (key) (value)\n";
+    exit 1;
+}
+
+######
+sub update {
+
+    $now = time;
+    $mjd = &sec_to_mjd ($now);
+
+    &load_runlist;
+
+    $match = "";
+    foreach $line (@runlist) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	($runid, $start, $stop, $camera) = split (" ", $line);
+	if ($runid eq "") { next; } # skip empty lines
+	$mjd_start = date_to_mjd ($start);
+	$mjd_stop  = date_to_mjd ($stop);
+	if (($mjd >= $mjd_start) && ($mjd <= $mjd_stop)) { 
+	    $match = $line;
+	    last;
+	}
+    }
+    if ($match eq "") { 
+	$camera = "none";
+	$runid  = "none";
+    }
+    system ("mkrun sys camera.current $camera");
+    system ("mkrun sys runid.current $runid");
+    system ("ssPut /e/state/$camera/cameraRun $runid COMMENT=\"valid current camera run ID (CRUNID)\" LIFETIME=90000");
+    system ("ssPut /e/state/masterRun $runid COMMENT=\"master current camera run ID (CRUNID)\" LIFETIME=90000");
+    exit 0;
+}
+
+######
+sub today {
+
+    $now = time;
+    $mjd = &sec_to_mjd ($now);
+
+    &load_runlist;
+
+    $match = "";
+    foreach $line (@runlist) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	($runid, $start, $stop, $camera) = split (" ", $line);
+	if ($runid eq "") { next; } # skip empty lines
+	$mjd_start = date_to_mjd ($start);
+	$mjd_stop  = date_to_mjd ($stop);
+	if (($mjd >= $mjd_start) && ($mjd <= $mjd_stop)) { 
+	    $match = $line;
+	    last;
+	}
+    }
+    if ($match eq "") { 
+	$camera = "none";
+	$runid  = "none";
+    }
+    print STDOUT "CAMERA: $camera, RUNID: $runid\n";
+    exit 0;
+}
+
+######
+sub getrun {
+
+    if (@ARGV != 2) { die "USAGE: mkrun run (runid)\n"; }
+
+    &load_runlist;
+    $line = grab_runline ($ARGV[1]);
+    print STDOUT "$line\n";
+    if ($line eq "") { 
+	exit 1;
+    } else {
+	exit 0;
+    }
+}
+
+######
+sub getdate {
+
+    if (@ARGV != 2) { die "USAGE: mkrun date (yyyy/mm/dd)\n"; }
+
+    my ($mjd) = date_to_mjd ($ARGV[1]);
+
+    &load_runlist;
+
+    foreach $line (@runlist) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	($runid, $start, $stop) = split (" ", $line);
+	if ($runid eq "") { next; } # skip empty lines
+	$mjd_start = date_to_mjd ($start);
+	$mjd_stop  = date_to_mjd ($stop);
+	if (($mjd >= $mjd_start) && ($mjd < $mjd_stop)) { 
+	    print STDOUT "$line\n";
+	    exit 0;
+	}
+    }
+    print STDOUT "";
+    exit 1;
+}
+
+############
+sub status {
+
+    &load_runlist;
+    foreach $line (@runlist) {
+	print STDOUT "$line\n";
+    }
+    exit 0;
+}
+
+############
+sub comment {
+
+    my (@argv) = @_;
+    
+    if (@argv != 2) {
+	print STDOUT "USAGE: mkrun comment (comment line)\n";
+	exit 2;
+    }
+
+    $comment = $argv[1];
+
+    &load_runlist;
+    insert_comment ($argv[1]);
+    &save_runlist;
+    exit 0;
+}
+
+############
+sub create {
+
+    if (@ARGV != 5) {
+	print STDOUT "USAGE: mkrun create (run) (start) (stop) (camera)\n";
+	exit 2;
+    }
+
+    $runid  = $ARGV[1];
+    $start  = $ARGV[2];
+    $stop   = $ARGV[3];
+    $camera = $ARGV[4];
+
+    &load_runlist;
+    
+    $line = grab_runline ($runid);
+    ($Orunid, $Ostart, $Ostop, $Ocamera, $qrunid) = split (" ", $line);
+    $line = mk_runline ($runid, $start, $stop, $camera, $qrunid);
+    insert_runline ($line);
+
+    # does not check for overlaps, but probably should
+
+    &save_runlist;
+    exit 0;
+}
+
+############
+sub delete {
+
+    if (@ARGV != 2) {
+	print STDOUT "USAGE: mkrun delete (run)\n";
+	exit 2;
+    }
+
+    $runid  = $ARGV[1];
+
+    &load_runlist;
+    
+    $Ns = @runlist;
+    delete_runline ($runid);
+    $Ne = @runlist;
+
+    if ($Ns == $Ne) {
+	print STDERR "runid $runid not found\n";
+	exit 1;
+    }
+
+    &save_runlist;
+    exit 0;
+}
+
+############
+sub hstcreate {
+
+    if (@ARGV != 1) {
+	print STDOUT "USAGE: mkrun hstcreate\n";
+	exit 2;
+    }
+
+    print STDOUT "Enter HST date for start of first night (YYYY/MM/DD): ";
+    $start = <STDIN>; chop $start;
+    $mjd = date_to_mjd ($start);
+    $mjd += 1.0;
+    $start = date_format (&mjd_to_date ($mjd));
+
+    print STDOUT "Enter HST date for start of last night (YYYY/MM/DD): ";
+    $stop = <STDIN>; chop $stop;
+    $mjd = date_to_mjd ($stop);
+    $mjd += 2.0;
+    $stop = date_format (&mjd_to_date ($mjd));
+
+    print STDOUT "Enter camera: ";
+    $camera = <STDIN>; chop $camera;
+
+    print STDOUT "Enter run ID: ";
+    $runid = <STDIN>; chop $runid;
+
+    print STDOUT "Enter associated QSO run IDs (separated by commas): ";
+    $qrunid = <STDIN>; chop $qrunid;
+
+    &load_runlist;
+    
+#    $line = grab_runline ($runid);
+#    ($Orunid, $Ostart, $Ostop, $Ocamera, $Oqrunid) = split (" ", $line);
+    $line = mk_runline ($runid, $start, $stop, $camera, $qrunid);
+    insert_runline ($line);
+
+    &save_runlist;
+    exit 0;
+}
+
+############
+sub addqso {
+    if (@ARGV != 3) {
+	print STDOUT "USAGE: mkrun addqso (run) (qsorun)\n";
+	exit 2;
+    }
+
+    $runid = $ARGV[1];
+    $qrunid  = $ARGV[2];
+
+    &load_runlist;
+    
+    $line = grab_runline ($runid);
+    if ($line eq "") { 
+	print STDERR "run id $runid not in run list\n";
+	exit 1;
+    }
+    ($runid, $start, $stop, $camera, $Oqrunid) = split (" ", $line);
+    if ($Oqrunid) {
+	$qrunid = $Oqrunid . "," . $qrunid;
+    } 
+
+    $line = mk_runline ($runid, $start, $stop, $camera, $qrunid);
+    insert_runline ($line);
+
+    &save_runlist;
+    exit 0;
+}
+
+############
+sub delqso {
+    if (@ARGV != 2) {
+	print STDOUT "USAGE: mkrun delqso (qsorun)\n";
+	exit 2;
+    }
+
+    $qrunid  = $ARGV[1];
+
+    &load_runlist;
+    
+    # find listed qrunid
+    $match = "";
+  SEARCH:
+    foreach $line (@runlist) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	($tmp, $tmp, $tmp, $tmp, $qlist) = split (" ", $line);
+	@qlist = split (",", $qlist);
+	foreach $qid (@qlist) {
+	    if ($qrunid ne $qid) { next; }
+	    $match = $line;
+	    last SEARCH;
+	}
+    }
+    if ($match eq "") {
+	print STDERR "QSO run id $qrunid not found in run list\n";
+	exit 1;
+    }
+
+    # recreate $qlist without $qrunid
+    @qnew = ();
+    foreach $qid (@qlist) {
+	if ($qid eq $qrunid) { next; }
+	push @qnew, $qid;
+    }
+    $qlist = join (",", @qnew);
+
+    ($runid, $start, $stop, $camera) = split (" ", $match);
+
+    $line = mk_runline ($runid, $start, $stop, $camera, $qlist);
+    insert_runline ($line);
+
+    &save_runlist;
+    exit 0;
+}
+
+####### runlist utilities ############################
+sub load_runlist {
+    open (FILE, "$runlist");
+    @runlist = <FILE>;
+    foreach $line (@runlist) {
+	chop ($line);
+    }
+    close (FILE);
+}    
+
+sub save_runlist {
+    open (FILE, ">$runlist");
+    foreach $line (@runlist) {
+	print FILE "$line\n";
+    }
+    close (FILE);
+}    
+
+sub grab_runline {
+    my ($id) = $_[0];
+    my ($line);
+    my ($runid);
+
+    foreach $line (@runlist) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	($runid) = split (" ", $line);
+	if ($runid ne $id) { next; }
+	return ($line);
+    }
+    return ("");
+}
+    
+sub insert_comment {
+    my ($comment) = $_[0];
+
+    @runlist = (@runlist, $comment);
+    return 1;
+}
+
+sub insert_runline {
+    my ($runline) = $_[0];
+    my ($line);
+    my ($runid, $id);
+
+    # match the run id lines
+    ($id) = split (" ", $runline);
+    foreach $line (@runlist) {
+	if ($line =~ /^\#/) { next; } # skip commented lines
+	($runid) = split (" ", $line);
+	if ($runid ne $id) { next; }
+	$line = $runline;
+	return 1;
+    }
+    @runlist = (@runlist, $runline);
+    return 0;
+}
+
+sub delete_runline {
+    my ($id) = $_[0];
+    my ($line, $runid);
+
+    # match the run id lines
+    @newlist = ();
+    foreach $line (@runlist) {
+	($runid) = split (" ", $line);
+	if ($runid eq $id) { next; }
+	@newlist = (@newlist, $line); 
+    }
+    @runlist = @newlist;
+    return 0;
+}
+
+sub mk_runline {
+    my ($runid, $start, $stop, $camera, $qrunid, $line);
+    $runid  = $_[0];
+    $start  = $_[1];
+    $stop   = $_[2];
+    $camera = $_[3];
+    $qrunid = $_[4];
+
+    $start = date_format ($start);
+    $stop  = date_format ($stop);
+    $line = sprintf "%s %s %s %-9s  %s", $runid, $start, $stop, $camera, $qrunid;
+    return ($line);
+}
+
+# utilities ##############################################
+
+sub atcommand {
+    my ($cmd, $log, $time, $date);
+    $cmd  = $_[0];
+    $log  = $_[1];
+    $time = $_[2];
+    $date = $_[3];
+
+    if (($log eq "") || ($log eq "NONE")) { $log = "/dev/null"; } 
+
+    printf "%s %s: %s\n", $date, $time, $cmd;
+    $line = sprintf "%s %s %s %s", $date, $time, $log, $cmd;
+    print SCHED "$line\n";
+}
+
+
+sub vsystem {
+    # print STDERR "@_\n";
+    my($status) = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub date_format {
+    my ($date) = $_[0];
+    my ($year, $month, $day);
+
+    ($year, $month, $day) = $date =~ /(\d+)\/(\d+)\/(\d+)/;
+    $date = sprintf "%4d/%02d/%02d", $year, $month, $day;
+    return ($date);
+}
+
+sub date_to_mjd {
+    my ($date) = $_[0];
+    my ($year, $month, $day, $mjd);
+
+    ($year, $month, $day) = $date =~ /(\d+)\/(\d+)\/(\d+)/;
+
+    $mjd = get_mjd ($year, $month, $day);
+
+    return ($mjd);
+}
+
+sub mjd_to_date {
+    my ($mjd) = $_[0];
+    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday);
+    my ($date, $time);
+    
+    $time = 86400 * ($mjd - 40587.0);
+    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime ($time);  
+    $year += 1900;
+    $mon ++;
+
+    $date = sprintf "%04d/%02d/%02d,%02d:%02d:%04.1f", $year, $mon, $mday, $hour, $min, $sec;
+    
+    return $date;
+}
+
+sub sec_to_mjd {
+    my ($sec) = $_[0];
+    my ($mjd);
+    
+    $mjd = $sec / 86400 + 40587.0;
+    return $mjd;
+}
+
+# gmtime
+# ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime(time);  
+# ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime(time);  
+# $time = time
+# 0 sec (unix) = 2440587.5 jd = 40587.0 mjd
+
+sub get_mjd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5 - 2400000.5;
+
+    return ($jd);
+}
+
+sub usage {
+    
+    if (@ARGV == 1) {
+	print STDERR "create (runid) (start) (stop) (camera) - create / change a camera run\n";
+	print STDERR "delete (runid)                         - delete a camera run\n";
+	print STDERR "hstcreate                              - interactive camera run creation\n";
+	print STDERR "addqso (runid) (qsoid)                 - add a qso run to camera run\n";
+	print STDERR "delqso (qsoid)                         - delete a qso run\n";
+	print STDERR "comment (line)                         - add a comment line to run list\n";
+	print STDERR "status                                 - check current run list\n";
+	print STDERR "date (date)                            - return run info for given date\n";
+	print STDERR "run (runid)                            - return run info for given camera run\n";
+	print STDERR "\n";
+	print STDERR "sys                                    - display current system variables\n";
+	print STDERR "sys init                               - set defaults for system variables\n";
+	print STDERR "sys (key)                              - fetch value of system variable\n";
+	print STDERR "sys (key) (value)                      - set value of system variable\n";
+	print STDERR "\n";
+	&goodbye;
+    }
+    
+    if (@ARGV > 2) { print STDERR "USAGE: prepare.run (help) [mode]\n"; &goodbye; }
+    
+    &goodbye;
+}
+
+
+
+##############
+sub mk_events {
+    my ($runid, $start, $stop);
+    $runid = $_[0];
+    $start = $_[1];
+    $stop  = $_[2];
+    
+    $mjd_start = date_to_mjd ($start);
+    $mjd_stop  = date_to_mjd ($stop);
+
+    $Nday = $mjd_stop - $mjd_start + 1;
+
+    if (! -e $schedule) {
+	# create a new schedule with PREVIOUS set to NEVER
+	open (SCHED, ">$schedule");
+	print SCHED "# schedule for scheduler system\n";
+	print SCHED "# E. Magnier\n";
+	print SCHED "\n";
+	print SCHED "# last time scheduler was run:\n";
+	print SCHED "PREVIOUS NEVER\n";
+	print SCHED "\n";
+	close (SCHED);
+    }
+	
+    open (SCHED, ">>$schedule");
+    print "\n";
+    $date = `date "+%Y/%m/%d %k:%M:%S"`; chop ($date);
+    print SCHED "# added $date\n";
+    
+
+    ($year, $month, $day) = $start =~ /(\d+)\/(\d+)\/(\d+)/;
+    for ($Day = -1; $Day < $Nday + 7; $Day++) {
+
+	$tday = $day + $Day - 1;
+	$date = `date -d $year/$month/$tday +%Y/%m/%d`; chop ($date);
+
+	if ($Day == 0) {
+	    atcommand ("mkrun sys RUNID $runid",                   "$logdir/config.log",    "10:00", $date);
+	    atcommand ("checkconfig -ckdirs",                      "$logdir/config.log",    "10:00", $date);
+	    atcommand ("mkdetrend create $runid $start $stop",     "$logdir/mkdetrend.log", "10:00", $date);
+	    atcommand ("mkfringe config $runid",                   "$logdir/mkdetrend.log", "10:00", $date);
+	    atcommand ("mkfringe mkconfig",                        "$logdir/mkdetrend.log", "10:00", $date);
+	}
+	if ($Day == 4) {
+	    atcommand ("mkdetrend auto set update",                "$logdir/mkdetrend.log", "10:00", $date);
+	}
+	if ($Day == $Nday) {
+	    atcommand ("mkdetrend auto set hold",                  "$logdir/mkdetrend.log", "10:00", $date);
+	}
+    }
+    close (SCHED);
+
+}
+
+
+##############
+
+sub sched {
+    if (@ARGV != 2) {
+	print STDOUT "USAGE: mkrun sched (runid)\n";
+	exit 2;
+    }
+
+    $runid  = $ARGV[1];
+    &load_runlist;
+    
+    $line = grab_runline ($runid);
+    ($runid, $start, $stop, $camera, $qlist) = split (" ", $line, 4);
+
+    mk_events ($runid, $start, $stop);
+    
+    exit 0;
+}
+
+if ($ARGV[0] eq "sched")   { &sched  (@ARGV); }
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkscat
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkscat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mkscat	(revision 22322)
@@ -0,0 +1,72 @@
+#!/usr/bin/env perl
+
+# warning: this function currently uses a fixed temporary name 'fix.NN.fits' 
+# for the intermediate product.  If we add this to elixir, convert to mktemp
+
+# need to assimilate the -c -C -D cmd line options -> env
+
+# check the usage
+if ($ARGV[0] eq "-h") { &usage (); }
+if ($ARGV[0] eq "-help") { &usage (); }
+if (@ARGV != 5) { &usage (); }
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`; chop ($confdir);
+$script  = "$confdir/mana/scatter.pro";
+
+# run mana script:
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+print MANA "mkscat $ARGV[0] $ARGV[1] $ARGV[2] $ARGV[3] $ARGV[4]\n";
+print MANA "exit 1\n";
+close (MANA);
+print STDERR "exit statue: $?\n";
+if ($? == 2 * 256) { 
+    print STDERR "no scattered light term available\n"; 
+    exit 2;
+}
+if ($?) { die "ERROR problem with mkscat\n"; }
+
+print STDOUT "SUCCESS: finished with mkscat\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub usage {
+    print "USAGE: mkscat (mosiac) (start) (stop) (filter) (ID)\n";
+    exit 1;
+}
+
+# mana within perl:  we embed the mana functions within a macro to trap the 
+# exit status.  If mana has an error with one of the steps, it will exit the macros and 
+# the second exit will be called. Otherwise, the "go" macro will perform the exit 0
+# and mana will exit with status 0
+
+# there are two possible flat-field photometric corrections: additive
+# and multiplicative:  
+# The correction images constructed by examining
+# the *difference* between dome-flats with and without the petals
+# covered is additive:  FLAT_new = (FLAT_old - SCAT) * A
+# (A is set to maintain the original normalization of chip 04)
+# 
+# The correction images constructed by examining stellar magnitude
+# difference is multiplicative: FLAT_new = (FLAT_old * SCAT) * A
+# (A is set to maintain the original normalization of chip 04)
+
+# there is a different scattered light image for each filter.
+# (these images are the same file for the additive version)
+
+# there should be no arbitrary normalization in the scattered frames
+# in the database. (we used to multiply the scattered light image by a
+# value of 10.0).  
+
+# images of the first class will be identified with the label scatter-A.0
+# images of the second class will be identified with the label scatter-B.0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mktrans
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mktrans	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mktrans	(revision 22322)
@@ -0,0 +1,141 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 2) { die "USAGE: mktrans (start) (stop)\n" ;}
+
+$start = $ARGV[0];
+$stop  = $ARGV[1];
+
+@photcodes = split (" ", `filtnames list`);
+
+foreach $code (@photcodes) {
+
+    print STDERR "filter: $code\n";
+    @list = `photsearch -trange $start $stop -photcode $code`;
+    if (! @list) { next; }
+
+    # the lines from photsearch look like this:
+    # filter date            zp      dzp   N T label (N - Nmeas, T - Ntime)
+    # V 2001/02/03,15:38:53  26.249  0.017 3 1 elixir
+
+    if (1) {
+
+	# first pass, get list of zp, sort, find median 
+	@zp = ();
+	$sumN = $sumM = $sum1 = $sum2 = $n = 0;
+	foreach $line (@list) {
+	    @words = split (" ", $line);
+	    push @zp, $words[2];
+	    $sumN ++;
+	    $sumM += $words[5];
+	}
+	@zp = sort {$a <=> $b} @zp;
+
+	$N = @zp;
+	for ($i = 0.2*$N; $i < 0.8*$N; $i++) {
+	    $sum1 += $zp[$i];
+	    $sum2 += $zp[$i]*$zp[$i];
+	    $n ++;
+	}
+	
+	$zp = $sum1 / $n;
+	$dzp = sqrt (abs($sum2 / $n - $zp*$zp));
+
+	printf STDOUT "photreg -trans -photcode $code -zp %6.3f -dzp %6.4f -trange $start $stop -Nmeas $sumM -Ntime $sumN\n", $zp, $dzp;
+
+    } else {
+	# use date hashes to extract stats for each date:
+	@dates = ();
+	foreach $line (@list) {
+	    
+	    # parse the line for the date
+	    @words = split (" ", $line);
+	    ($date) = $words[1] =~ /(\d\d\d\d\/\d\d\/\d\d),/;
+	    
+	    # if this is a new date, save it in the list
+	    unless ($sum1{$date}) { @dates = (@dates, $date); }
+
+	    # add to the stats 
+	    $sumN{$date} ++;
+	    $sumM{$date} += $words[5];
+	    $sum1{$date} += $words[2];
+	    $sum2{$date} += $words[2]*$words[2];
+	}
+
+	@sdates = sort by_date @dates;
+
+	# calculate mean and stdev for each date
+	foreach $date (@sdates) {
+	    
+	    $Nm  = $sumM{$date};
+	    $Np  = $sumN{$date};
+	    $zp  = $sum1{$date} / $Np;
+	    $dzp = sqrt (abs($sum2{$date} / $Np - $zp*$zp));
+
+	    printf STDOUT "photreg -trans -photcode $code -zp %6.3f -dzp %6.4f -trange $start $stop -Nmeas $Nm -Ntime $Np\n", $zp, $dzp;
+	}
+
+	%sumM = ();
+	%sumN = ();
+	%sum1 = ();
+	%sum2 = ();
+	print STDOUT "\n";
+    }
+    
+}
+
+exit 0;
+
+sub by_date {
+    
+    ($year, $month, $day) = $a =~ /(\d\d\d\d).(\d\d).(\d\d)/;
+    $va = get_jd ($year, $month, $day);
+    ($year, $month, $day) = $b =~ /(\d\d\d\d).(\d\d).(\d\d)/;
+    $vb = get_jd ($year, $month, $day);
+    $va <=> $vb;
+}
+    
+
+
+###################################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+
+sub get_jd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5;
+    
+
+    return ($jd);
+
+}
+
+sub get_mjd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5 - 2400000.5;
+    
+
+    return ($jd);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mktreport
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mktreport	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mktreport	(revision 22322)
@@ -0,0 +1,135 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 2) { die "USAGE: mktreport (start) (stop)\n" ;}
+
+$start = $ARGV[0];
+$stop  = $ARGV[1];
+
+@photcodes = split (" ", `filtnames list`);
+
+@list = `photsearch -trans -offset`;
+
+# the lines from transearch look like this:
+# R 2001/04/15,00:00:00  -0.032 26.259  0.004  0.040 -0.090 B_L92 V_L92 5 15 elixir
+#      (date)             (ZP)  (ZPo)   (sigma)
+
+@dates = ();
+
+# get the complete list of unique dates:
+
+LIST:
+foreach $line (@list) {
+	
+    # parse the line for the date
+    @words = split (" ", $line);
+    ($date) = $words[1] =~ /(\d\d\d\d\/\d\d\/\d\d),/;
+    
+    unless ($found{$date}) { @dates = (@dates, $date); $found{$date} = 1; }
+
+    for ($i = 0; $i < @photcodes; $i++) {
+	
+	if ($words[0] ne $photcodes[$i]) { next; }
+
+	$name1 = "ZPo$i";
+	$name2 = "dZP$i";
+	$$name1{$date} = $words[2] - $words[3];
+	$$name2{$date} = $words[4];
+	next LIST;
+    }
+
+    print STDERR "$words[0] not found in photcode list\n";
+}
+
+@sdates = sort by_date @dates;
+
+print STDOUT "           ";
+for ($i = 0; $i < @photcodes; $i++) {
+    printf STDOUT "%6s ", $photcodes[$i];
+}
+print STDOUT "  ";
+for ($i = 0; $i < @photcodes; $i++) {
+    printf STDOUT "%6s ", "d$photcodes[$i]";
+}
+print STDOUT "\n";
+
+foreach $date (@sdates) {
+    
+    print STDOUT "$date ";
+    
+    for ($i = 0; $i < @photcodes; $i++) {
+	$name = "ZPo$i";
+	if ($$name{$date}) {
+	    printf STDOUT "%6.3f ", $$name{$date};
+	} else {
+	    print STDOUT "     - ";
+	}
+    }
+    print STDOUT "  ";
+    for ($i = 0; $i < @photcodes; $i++) {
+	$name = "dZP$i";
+	if ($$name{$date}) {
+	    printf STDOUT "%6.3f ", $$name{$date};
+	} else {
+	    print STDOUT "     - ";
+	}
+    }
+	
+    print STDOUT "\n";
+}
+
+exit 0;
+
+sub by_date {
+    
+    ($year, $month, $day) = $a =~ /(\d\d\d\d).(\d\d).(\d\d)/;
+    $va = get_jd ($year, $month, $day);
+    ($year, $month, $day) = $b =~ /(\d\d\d\d).(\d\d).(\d\d)/;
+    $vb = get_jd ($year, $month, $day);
+    $va <=> $vb;
+}
+    
+
+
+###################################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+
+sub get_jd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5;
+    
+
+    return ($jd);
+
+}
+
+sub get_mjd {
+
+    my($year) = $_[0];
+    my($month) = $_[1];
+    my($day) = $_[2];
+
+    my($jd) = $day - 32075 + int (1461*($year + 4800 + int (($month - 14)/12))/4)
+	+ int(367*($month - 2 - int(($month - 14)/12)*12)/12)
+	    - int(3*int(($year + 4900 + int(($month - 14)/12))/100)/4) - 0.5 - 2400000.5;
+    
+
+    return ($jd);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mosaic.merge
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mosaic.merge	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mosaic.merge	(revision 22322)
@@ -0,0 +1,110 @@
+#!/usr/bin/env perl
+# this program takes a file template, with CCDNUM substituted for the 
+# CCD number.  each file contains the ccd frames as separate extents.  The program
+# loads the names, then for each frame, it goes through the CCDNUM entries
+# and mosaics together the appropriate image, writing them to an output filename 
+# formed from ARGV[1] (ARGV[1].n.jpg).  These names are written to ARGV[1]
+#
+# this program returns immediately if all CCDNUM entries are not found.
+# the previous function, detstats, must not create the *.stats file until the 
+# process is done.  similarly, this program deletes the *.stats files to avoid 
+# confusion on a second pass
+
+@ccds = split (" ", `cameraconfig -ccdn`);
+if ($?) { die "ERROR: configuration problem\n"; }
+
+if (@ARGV != 3) { die "ERROR: USAGE: mosaic.tenbin (name) (out) (type)\n" }
+$type = "\U$ARGV[2]\E";
+
+# find the appropriate script file
+$confdir = `gconfig -q CONFDIR`;  chop $confdir;
+$script = "$confdir/mana/flips.pro";
+
+# test named Xserver, or start vnc on specified machine
+$xhost = `gconfig -q XHOST`; chop $xhost;
+$xdisp = `gconfig -q XDISP`; chop $xdisp;
+if (vsystem ("xdpyinfo -display $xdisp")) {
+    print STDERR "ERROR: X server is not running. start it now on $xhost\n";
+    exit 1;
+}
+$ENV{'DISPLAY'} = $xdisp;
+
+# check for the existence of each input file, exit if non-existent
+foreach $ccd (@ccds) {
+    $name = $ARGV[0];
+    $name =~ s/CCDNUM/$ccd/;
+    unless (-r $name) {
+	print STDERR "ERROR: can't find all files\n";
+	exit 1;
+    }
+}
+
+# we are constructing the names of the input files.  there are NCCD
+# files with names of the form path/01Ak01.flat.I.CCDNUM.0.medbin we
+# need to loop over all NEXTEND entries in these files, creating
+# NEXTEND output images.  First check that each file has the same NEXTEND
+
+@filenames = ();
+foreach $ccd (@ccds) {
+    $name = $ARGV[0];
+    $name =~ s/CCDNUM/$ccd/;
+    push @filenames, $name;
+
+    ($junk, $N) = split (" ", `echo $name | fields NEXTEND`);
+    if ($ccd eq $ccds[0]) {
+	$Nline = $N;
+    } 
+    if ($N != $Nline) { die "ERROR: mis-matched number of images in $name\n"; }
+}    
+
+open (MANA, "|mana --norc");
+print MANA "input $script\n";
+
+# define list of names as mana list
+print MANA "list names\n";
+foreach $name (@filenames) {
+    print MANA "$name\n";
+}
+print MANA "end\n";
+
+# define macro for 1 image
+print MANA "macro mkimage\n";
+print MANA " mergemosaic \$1\n";
+if ($type eq "RAW")  { print MANA " jpgraw \$2\n";    } 
+if ($type eq "FLAT") { print MANA " jpgnorm \$2\n";   } 
+if ($type eq "BIAS") { print MANA " jpgmosaic \$2\n"; }
+if ($type eq "DARK") { print MANA " jpgmosaic \$2\n"; }
+print MANA "end\n";
+
+# define macro for image list 
+print MANA "macro go\n";
+open (LIST, ">$ARGV[1]");
+for ($i = 0; $i < $Nline; $i++) {
+    $outfile = sprintf "%s.%03d.jpg", $ARGV[1], $i;
+    print MANA " mkimage $i $outfile\n";
+    print LIST "$outfile\n";
+}
+close (LIST);
+print MANA " exit 0\n";
+print MANA "end\n";
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+if ($?) {
+    print STDERR "ERROR running mana\n";
+    exit 1;
+}
+
+print STDOUT "SUCCESS\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mosaic.stats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mosaic.stats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/mosaic.stats	(revision 22322)
@@ -0,0 +1,156 @@
+#!/usr/bin/env perl
+# this program takes a list template, with CCDNUM substituted for the 
+# CCD number.  each file contains a list of statistics for each image.  The program
+# loads the names, then for each frame, it goes through the CCDNUM entries
+# and loads the appropriate statistics, writing them to the output file
+#
+# this program returns immediately if all CCDNUM entries are not found.
+# the previous function, detstats, must not create the *.stats file until the 
+# process is done.  similarly, this program deletes the *.stats files to avoid 
+# confusion on a second pass
+
+@ccdn = split (" ", `cameraconfig -ccdn`);
+@ccds = split (" ", `cameraconfig -ccds`);
+if ($?) { die "ERROR: configuration problem\n"; }
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+if (@ARGV != 3) { die "ERROR: USAGE: mosaic.stats (name) (out) (type)\n" }
+
+$type = "\U$ARGV[2]\E";
+
+# load option list of ccds to skip (specific to run/camera)
+$CHIPSKIP     = `gconfig CHIP_SKIP_LIST`; chop $CHIPSKIP;
+@chipskip = ();
+if (-e $CHIPSKIP) { 
+    open (FILE, $CHIPSKIP);
+    @chipskip = <FILE>;
+    close (FILE);
+    foreach $chip (@chipskip) { chop $chip; }
+}
+
+# modify the list of ccds to exclude chipskip entries
+$N = @ccds;
+CCD: for ($i = 0; $i < $N; $i++) {
+    $ccds = shift @ccds;
+    $ccdn = shift @ccdn;
+
+    foreach $skip (@chipskip) { if ($skip eq $ccds) { next CCD; } }
+    push @ccds, $ccds;
+    push @ccdn, $ccdn;
+}
+
+# check for the existence of each input file, exit if non-existent
+foreach $ccd (@ccdn) {
+    $name = $ARGV[0];
+    $name =~ s/CCDNUM/$ccd/;
+    unless (-r $name) {
+	print STDERR "ERROR: can't find $name\n";
+	exit (0);
+    }
+}
+
+# load in stats from files (Nlist*Nccd entries)
+# each file contains stats for a single ccd, for all N files
+foreach $ccd (@ccdn) {
+    $name = $ARGV[0];
+    $name =~ s/CCDNUM/$ccd/;
+
+    open (FILE, $name);
+    @$ccd = <FILE>;
+    close (FILE);
+
+    if ($ccd eq $ccdn[0]) { $Nline = @$ccd; }
+    if (@$ccd != $Nline) { die "ERROR mis-matched file lengths\n"; }
+}    
+
+$dawn = 0;
+$dusk = 1;
+@pdate = ();
+@ptwil = ();
+$Nperiod = 0;
+@period = ();
+
+open (OUTPUT, ">$ARGV[1]");
+for ($i = 0; $i < $Nline; $i++) {
+
+    $mean  = 0;
+    $medn  = 0;
+    $sig12 = 0;
+    $sig22 = 0;
+    $mean2 = 0;
+    $mbin  = 0;
+    $sbin2 = 0;
+    $mbin2 = 0;
+
+    foreach $ccd (@ccdn) {
+	$line = $$ccd[$i];
+	chop ($line);
+	@words = split (" ", $line);
+	$file = $words[0];
+	$date = $words[1];
+	$time = $words[2];
+	$stat = $words[9];
+	$mean  += $words[3];
+	$medn  += $words[4];
+	$sig12 += $words[5]*$words[5];
+	$sig22 += $words[6]*$words[6];
+	$mean2 += $words[3]*$words[3];
+
+	$mbin  += $words[7];
+	$sbin2 += $words[8]*$words[8];
+	$mbin2 += $words[7]*$words[7];
+    }
+    $N = @ccdn;
+
+    # identify even or morn twilight
+    @words = split (':', $time);
+    if (($words[0] > 12) && ($words[0] < 24)) {
+	$twilite = $dawn;
+    } else {
+	$twilite = $dusk;
+    }
+    $ThisPeriod = -1;
+    for ($j = 0; $j < $Nperiod; $j++) {
+	if (($pdate[$j] eq $date) && ($ptwil[$j] == $twilite)) {
+	    $ThisPeriod = $j;
+	    last;
+	}
+    }
+    if ($ThisPeriod == -1) {
+	$ThisPeriod = $Nperiod;
+	@pdate = (@pdate, $date);
+	@ptwil = (@ptwil, $twilite);
+	$Nperiod ++;
+    }
+    @period = (@period, $ThisPeriod);
+
+    # determine global statistics
+    $Mean  = $mean / $N;
+    $Medn  = $medn / $N;
+    $Mbin  = $mbin / $N;
+
+    if ($type eq "FLAT") {
+      $Sig1  = sqrt ($sig12 / $N + $mean2 / $N - $Mean*$Mean) / abs ($Mean);
+      $Sig2  = sqrt ($sig22 / $N + $mean2 / $N - $Mean*$Mean) / abs ($Mean);
+      $Sbin  = sqrt ($sbin2 / $N + $mbin2 / $N - $Mbin*$Mbin) / abs ($Mbin);
+    } else {
+      $Sig1  = sqrt ($sig12 / $N + $mean2 / $N - $Mean*$Mean);
+      $Sig2  = sqrt ($sig22 / $N + $mean2 / $N - $Mean*$Mean);
+      $Sbin  = sqrt ($sbin2 / $N + $mbin2 / $N - $Mbin*$Mbin);
+    }      
+
+    printf OUTPUT "%6s %10s %11s %7.1f %7.1f %6.4f %6.4f %7.1f %6.4f %d %s %d %d\n", 
+    $file, $date, $time, $Mean, $Medn, $Sig1, $Sig2, $Mbin, $Sbin, $period[$i], $pdate[$period[$i]], $ptwil[$period[$i]], $stat;
+
+}    
+close (OUTPUT);
+print STDERR "SUCCESS\n";
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/normalize
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/normalize	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/normalize	(revision 22322)
@@ -0,0 +1,306 @@
+#!/usr/bin/env perl
+
+$revline = "\$Revision: 1.6 $?";
+($version) = $revline =~ m|\$Revision:\s*(\S*)|;
+
+# default variable values
+$start     = 0;
+$stop      = 0;
+$oldflips  = 0;
+$addtime   = 0;
+$addconfig = 0;
+
+# grab the command line options
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-oldflips") {
+        $oldflips = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-time") {
+	$addtime = 1;
+	$start = $ARGV[1];
+	$stop = $ARGV[2];
+	shift; shift; shift; next;
+    }
+    if ($ARGV[0] eq "-config") {
+	$addconfig = 1;
+	$elconf = $ARGV[1];
+	shift; shift; next;
+    }
+    if ($ARGV[0] eq "-h") { &usage; }
+    if ($ARGV[0] eq "--help") { &usage; }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 4) { &usage; }
+if ($addtime && ("$start" == "")) { &usage; }
+if ($addtime && ("$stop" == ""))  { &usage; }
+
+# command line arguments
+$input  = $ARGV[0];
+$output = $ARGV[1];
+$ref    = $ARGV[2];
+$type   = uppercase ($ARGV[3]);
+
+$ccdword = `gconfig -q CCDNUM-KEYWORD`; chop ($ccdword);
+$typeword = `gconfig -q IMAGETYPE-KEYWORD`; chop ($typeword);
+$dbmode = `gconfig -q DETREND-DB-MODE`; chop ($dbmode);
+
+# choose appropriate type
+if ($type eq "FLAT") { &mk_flat; }
+if ($type eq "DARK") { &mk_dark; }
+if ($type eq "BIAS") { &mk_dark; }
+print STDERR "unknown type $type";
+exit 1;
+
+sub mk_dark { 
+# create mana macro, run (allows exit status to be checked)
+    open (MANA, "|mana --norc");
+    print MANA "macro go\n";
+    print MANA "echo 'starting normalize'\n";
+    print MANA "rd a $input\n";
+    print MANA "keyword a $typeword -w $type\n";
+    
+    if ($addtime == 1) {
+	print MANA "keyword a TVSTART -w $start\n";
+	print MANA "keyword a TVSTART -wc \"data validity start time\"\n";
+	print MANA "keyword a TVSTOP  -w $stop\n";
+	print MANA "keyword a TVSTOP -wc \"data validity start time\"\n";
+    }
+    if ($addconfig == 1) {
+	print MANA "keyword a ELCONF -w $elconf\n";
+	print MANA "keyword a ELCONF -wc \"Original Elixir config\"\n";
+    }	
+    
+    $elsys = `gconfig ELIXIR_VERSION`; chop ($elsys);
+    @time = localtime;
+    $now = sprintf "%04d-%02d-%02dT%02d:%02d:%02d", $time[5]+1900, $time[4]+1, $time[3], $time[2], $time[1], $time[0];
+    $elsys = `gconfig ELIXIR_VERSION`; chop ($elsys);
+    print MANA "keyword a EL_SYS -w    \"$elsys\"\n";
+    print MANA "keyword a EL_SYS -wc   \"Elixir System Version\"\n";
+    print MANA "keyword a EL_NRM -w    \"$version\"\n";
+    print MANA "keyword a EL_NRM -wc   \"Elixir Normalize Version\"\n";
+    print MANA "keyword a REL_DATE -w  \"$now\"\n";
+    print MANA "keyword a REL_DATE -wc \"UTC Release Date\"\n";
+
+    print MANA "keyword a COMMENT -ws \"Elixir:normalize $version\"\n";
+    print MANA "keyword a COMMENT -ws \"no renormalization needed for $type\"\n";
+    print MANA "wd a $output -bitpix 16 -bzero 32668.0 -bscale 1.0\n";
+    print MANA "echo normlized done ($input to $output)\n";
+    print MANA "exit 0\n";
+    print MANA "end\n";  # end of macro 'go'
+
+    print MANA "go\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) {
+	print STDERR "ERROR problem running mana in normalize (bias, dark)\n";
+	exit 1;
+    }
+    print STDOUT "SUCCESS: finished with normalize\n";
+    exit 0;
+}
+   
+sub mk_flat {
+    # check on reference existence (not needed for BIAS, DARK)
+    if (! -f $ref) {
+	print STDERR "ERROR: ref file $args[3] is missing\n";
+	exit 1;
+    }
+    $chipref = `gconfig FLIPS_REF_CCD`; chop ($chipref);
+    $baseref = $chipref;
+    print STDERR "Normalizing reference: using file $ref\n";
+
+    # Check that FLIPS_REF_CCD is not in CHIPSKIP, otherwise find another CCD
+    $Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+    @ccdn = split (" ", `cameraconfig -ccdn`);
+    $CHIPSKIP = `gconfig CHIP_SKIP_LIST`; chop $CHIPSKIP;
+    @chipskip = ();
+    if (-e $CHIPSKIP) {
+
+       open (FILE, $CHIPSKIP);
+       @chipskip = <FILE>;
+       close (FILE);
+       foreach $chip (@chipskip) { chop $chip; }
+
+       foreach $chip (@chipskip) {
+          $chip =~ s/ccd//;
+	  if ($chip eq $chipref) {
+             print STDERR "Warning: found reference chip in skip list ($chipref)\n";
+	     
+	     # Find a new reference: increment by one
+             for ($i = 0; $i < $Nccd; $i++) {
+                $ccdindex = $ccdn[$i];
+	        if ( $ccdindex eq $chipref ) {
+                   $newref = $ccdn[$i + 1];
+                }
+	     }
+	     $chipref = $newref
+	  }
+       }
+       # Substitute the CCD reference number in the name of the ref FITS file
+       $ref =~ s/.$baseref./.$chipref./;
+       print STDERR "New SkipChip selection: using ref file $ref\n";
+    }
+    
+    # we need keywords defined by the process (flips) which creates the master frame
+    if ($oldflips) {
+        # these lines were used before flips was upgraded to add the DATAMODE keywords
+	$answer = `fhead $ref | grep "Output file mode"`;
+	@words = split (" ", $answer);
+	$refmode = $words[-1];
+
+	$answer = `fhead $input | grep "Output file mode"`;
+	@words = split (" ", $answer);
+	$mode = $words[-1];
+    } else {
+	# various options for datamode, refmode:
+	
+	# check for readout mode
+	($junk, $amplist)    = split (" ", `echo $input | fields AMPLIST`, 2);
+	if ($? == 0) {
+	    # multiamp mode, expect IMMODE$amp, create FLATSCL$amp
+	    @amps = split (" ", $amplist);
+	    foreach $amp (@amps) {
+		($junk, $mode)    = split (" ", `echo $input | fields IMMODE$amp`);
+		if ($?) { die "ERROR: missing IMMODE$amp\n"; }
+		@ampmode = (@ampmode, $mode);
+	    }
+	}
+	# try new IMMODE keyword:
+	$modeword = "IMMODE";
+	($junk, $mode)    = split (" ", `echo $input | fields $modeword`);
+	if ($?) {
+	    if (@amps) { die "ERROR: multiamp mode missing IMMODE\n"; }
+	    $modeword = "DATAMODE";
+	    ($junk, $mode)    = split (" ", `echo $input | fields $modeword`);
+	    if ($?) { die "ERROR: missing DATAMODE & IMMODE\n"; }
+	}
+	($junk, $refmode) = split (" ", `echo $ref   | fields $modeword`);
+	if ($?) { die "ERROR: ref file missing $modeword\n"; }
+    }
+    ($junk, $refccd) = split (" ", `echo $ref | fields $ccdword`);
+    if ($?) { die "ERROR: ref file missing $ccdword in ref\n"; }
+    
+    # grab mask file
+    $mask = `detsearch -quiet -image $input 0 split -type mask`; chop ($mask);
+    if ($? || ($mask eq "")) { 
+	print STDERR "can't find mask for this image, no mask applied\n";
+	$mask = "none"; 
+    }
+
+    # run MANA
+    open (MANA, "|mana --norc");
+    print MANA "macro go\n";
+    print MANA "echo 'starting normalize'\n";
+    print MANA "rd a $input\n";
+    print MANA "\$CCDKEYWORD = $ccdword\n";
+    print MANA "keyword a $ccdword ccd\n";
+    if ($mask eq "none") {
+	print MANA "set b = a / $refmode\n";
+    } else {
+	if ($dbmode eq "MEF") {
+	    print MANA "rd c $mask -n \$ccd\n";
+	} else {
+	    print MANA "rd c $mask\n";
+	}
+	print MANA "set b = (a / $refmode) * c\n";
+    }
+
+    $scale = $refmode / $mode;
+    $newmode = $mode / $refmode;
+    print MANA "keyword b $modeword -wf $newmode\n";
+    print MANA "keyword b FLATSCAL -wf $scale\n";
+
+    if (@amps) {
+	for ($i = 0; $i < @amps; $i++) {
+	    $amp = $amps[$i];
+	    $scale   = $refmode / $ampmode[$i];
+	    $newmode = $ampmode[$i] / $refmode;
+	    print MANA "keyword b IMMODE$amp -wf $newmode\n";
+	    print MANA "keyword b FLATSCL$amp -wf $scale\n";
+	}
+    }
+    print MANA "keyword b $typeword -w $type\n";
+    if ($addtime == 1) {
+	print MANA " keyword b TVSTART -w $start\n";
+	print MANA " keyword b TVSTART -wc \"data validity start time\"\n";
+	print MANA " keyword b TVSTOP  -w $stop\n";
+	print MANA " keyword b TVSTOP -wc \"data validity start time\"\n";
+    }
+    if ($addconfig == 1) {
+	print MANA "keyword b ELCONF -w $elconf\n";
+	print MANA "keyword b ELCONF -wc \"Original Elixir config\"\n";
+    }	
+
+    @time = localtime;
+    $now = sprintf "%04d-%02d-%02dT%02d:%02d:%02d", $time[5]+1900, $time[4]+1, $time[3], $time[2], $time[1], $time[0];
+    $elsys = `gconfig ELIXIR_VERSION`; chop ($elsys);
+    print MANA "keyword b EL_SYS   -w  \"$elsys\"\n";
+    print MANA "keyword b EL_SYS   -wc \"Elixir System Version\"\n";
+    print MANA "keyword b EL_NRM   -w  \"$version\"\n";
+    print MANA "keyword b EL_NRM   -wc \"Elixir Normalize Version\"\n";
+    print MANA "keyword b REL_DATE -w \"$now\"\n";
+    print MANA "keyword b REL_DATE -wc \"UTC Release Date\"\n";
+
+    print MANA "keyword b COMMENT  -ws \"Elixir:normalize version $version\"\n";
+    print MANA "keyword b COMMENT  -ws \"FLAT image normalized wrt CCD $refccd\"\n";
+    print MANA "clip b 0.0 0.0 6.4 0.0\n";
+    print MANA "wd b $output -bitpix 16 -bzero 3.2 -bscale 0.0001\n";
+    print MANA "echo normlized done ($input to $output)\n";
+    print MANA "exit 0\n";
+    print MANA "end\n";  # end of macro 'go'
+
+    print MANA "go\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) {
+	print STDERR "ERROR problem running mana in normalize (flat)\n";
+	exit 1;
+    }
+    print STDOUT "SUCCESS: finished with normalize\n";
+    exit 0;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub uppercase {
+    return "\U$_[0]\E";
+}
+
+# given a filename, check that its path exists, create it if not
+sub ckpathname {
+    my ($file) = $_[0];
+    my (@words, $dir);
+
+    @words = split ("/", $file);
+    pop (@words);
+    $dir = join ("/", @words);
+
+    if ($dir eq "") { $dir = "."; }
+
+    if (! -d $dir) {
+	system ("mkdir -p $dir");
+	if ($?) {
+	    print STDERR "ERROR: can't make directory component for $file\n";
+	    exit 1;
+	}
+    }
+}
+
+sub usage {
+    print STDERR "normalize (input) (output) (ref) (type) [-time start stop] [-oldflips]\n";
+    exit 2;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/normalize.pre12KfiltersHandling
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/normalize.pre12KfiltersHandling	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/normalize.pre12KfiltersHandling	(revision 22322)
@@ -0,0 +1,271 @@
+#!/usr/bin/env perl
+
+$revline = "\$Revision: 1.1 $?";
+($version) = $revline =~ m|\$Revision:\s*(\S*)|;
+
+# default variable values
+$start     = 0;
+$stop      = 0;
+$oldflips  = 0;
+$addtime   = 0;
+$addconfig = 0;
+
+# grab the command line options
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-oldflips") {
+        $oldflips = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-time") {
+	$addtime = 1;
+	$start = $ARGV[1];
+	$stop = $ARGV[2];
+	shift; shift; shift; next;
+    }
+    if ($ARGV[0] eq "-config") {
+	$addconfig = 1;
+	$elconf = $ARGV[1];
+	shift; shift; next;
+    }
+    if ($ARGV[0] eq "-h") { &usage; }
+    if ($ARGV[0] eq "--help") { &usage; }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 4) { &usage; }
+if ($addtime && ("$start" == "")) { &usage; }
+if ($addtime && ("$stop" == ""))  { &usage; }
+
+# command line arguments
+$input  = $ARGV[0];
+$output = $ARGV[1];
+$ref    = $ARGV[2];
+$type   = uppercase ($ARGV[3]);
+
+$ccdword = `gconfig -q CCDNUM-KEYWORD`; chop ($ccdword);
+$typeword = `gconfig -q IMAGETYPE-KEYWORD`; chop ($typeword);
+$dbmode = `gconfig -q DETREND-DB-MODE`; chop ($dbmode);
+
+# choose appropriate type
+if ($type eq "FLAT") { &mk_flat; }
+if ($type eq "DARK") { &mk_dark; }
+if ($type eq "BIAS") { &mk_dark; }
+print STDERR "unknown type $type";
+exit 1;
+
+sub mk_dark { 
+# create mana macro, run (allows exit status to be checked)
+    open (MANA, "|mana --norc");
+    print MANA "macro go\n";
+    print MANA "echo 'starting normalize'\n";
+    print MANA "rd a $input\n";
+    print MANA "keyword a $typeword -w $type\n";
+    
+    if ($addtime == 1) {
+	print MANA "keyword a TVSTART -w $start\n";
+	print MANA "keyword a TVSTART -wc \"data validity start time\"\n";
+	print MANA "keyword a TVSTOP  -w $stop\n";
+	print MANA "keyword a TVSTOP -wc \"data validity start time\"\n";
+    }
+    if ($addconfig == 1) {
+	print MANA "keyword a ELCONF -w $elconf\n";
+	print MANA "keyword a ELCONF -wc \"Original Elixir config\"\n";
+    }	
+    
+    $elsys = `gconfig ELIXIR_VERSION`; chop ($elsys);
+    @time = localtime;
+    $now = sprintf "%04d-%02d-%02dT%02d:%02d:%02d", $time[5]+1900, $time[4]+1, $time[3], $time[2], $time[1], $time[0];
+    $elsys = `gconfig ELIXIR_VERSION`; chop ($elsys);
+    print MANA "keyword a EL_SYS -w    \"$elsys\"\n";
+    print MANA "keyword a EL_SYS -wc   \"Elixir System Version\"\n";
+    print MANA "keyword a EL_NRM -w    \"$version\"\n";
+    print MANA "keyword a EL_NRM -wc   \"Elixir Normalize Version\"\n";
+    print MANA "keyword a REL_DATE -w  \"$now\"\n";
+    print MANA "keyword a REL_DATE -wc \"UTC Release Date\"\n";
+
+    print MANA "keyword a COMMENT -ws \"Elixir:normalize $version\"\n";
+    print MANA "keyword a COMMENT -ws \"no renormalization needed for $type\"\n";
+    print MANA "wd a $output -bitpix 16 -bzero 32668.0 -bscale 1.0\n";
+    print MANA "echo normlized done ($input to $output)\n";
+    print MANA "exit 0\n";
+    print MANA "end\n";  # end of macro 'go'
+
+    print MANA "go\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) {
+	print STDERR "ERROR problem running mana in normalize (bias, dark)\n";
+	exit 1;
+    }
+    print STDOUT "SUCCESS: finished with normalize\n";
+    exit 0;
+}
+   
+sub mk_flat {
+    # check on reference existence (not needed for BIAS, DARK)
+    if (! -f $ref) {
+	print STDERR "ERROR: ref file $args[3] is missing\n";
+	exit 1;
+    }
+    
+    # we need keywords defined by the process (flips) which creates the master frame
+    if ($oldflips) {
+        # these lines were used before flips was upgraded to add the DATAMODE keywords
+	$answer = `fhead $ref | grep "Output file mode"`;
+	@words = split (" ", $answer);
+	$refmode = $words[-1];
+
+	$answer = `fhead $input | grep "Output file mode"`;
+	@words = split (" ", $answer);
+	$mode = $words[-1];
+    } else {
+	# various options for datamode, refmode:
+	
+	# check for readout mode
+	($junk, $amplist)    = split (" ", `echo $input | fields AMPLIST`, 2);
+	if ($? == 0) {
+	    # multiamp mode, expect IMMODE$amp, create FLATSCL$amp
+	    @amps = split (" ", $amplist);
+	    foreach $amp (@amps) {
+		($junk, $mode)    = split (" ", `echo $input | fields IMMODE$amp`);
+		if ($?) { die "ERROR: missing IMMODE$amp\n"; }
+		@ampmode = (@ampmode, $mode);
+	    }
+	}
+	# try new IMMODE keyword:
+	$modeword = "IMMODE";
+	($junk, $mode)    = split (" ", `echo $input | fields $modeword`);
+	if ($?) {
+	    if (@amps) { die "ERROR: multiamp mode missing IMMODE\n"; }
+	    $modeword = "DATAMODE";
+	    ($junk, $mode)    = split (" ", `echo $input | fields $modeword`);
+	    if ($?) { die "ERROR: missing DATAMODE & IMMODE\n"; }
+	}
+	($junk, $refmode) = split (" ", `echo $ref   | fields $modeword`);
+	if ($?) { die "ERROR: ref file missing $modeword\n"; }
+    }
+    ($junk, $refccd) = split (" ", `echo $ref | fields $ccdword`);
+    if ($?) { die "ERROR: ref file missing $ccdword in ref\n"; }
+    
+    # grab mask file
+    $mask = `detsearch -quiet -image $input 0 split -type mask`; chop ($mask);
+    if ($? || ($mask eq "")) { 
+	print STDERR "can't find mask for this image, no mask applied\n";
+	$mask = "none"; 
+    }
+
+    # run MANA
+    open (MANA, "|mana --norc");
+    print MANA "macro go\n";
+    print MANA "echo 'starting normalize'\n";
+    print MANA "rd a $input\n";
+    print MANA "\$CCDKEYWORD = $ccdword\n";
+    print MANA "keyword a $ccdword ccd\n";
+    if ($mask eq "none") {
+	print MANA "set b = a / $refmode\n";
+    } else {
+	if ($dbmode eq "MEF") {
+	    print MANA "rd c $mask -n \$ccd\n";
+	} else {
+	    print MANA "rd c $mask\n";
+	}
+	print MANA "set b = (a / $refmode) * c\n";
+    }
+
+    $scale = $refmode / $mode;
+    $newmode = $mode / $refmode;
+    print MANA "keyword b $modeword -wf $newmode\n";
+    print MANA "keyword b FLATSCAL -wf $scale\n";
+
+    if (@amps) {
+	for ($i = 0; $i < @amps; $i++) {
+	    $amp = $amps[$i];
+	    $scale   = $refmode / $ampmode[$i];
+	    $newmode = $ampmode[$i] / $refmode;
+	    print MANA "keyword b IMMODE$amp -wf $newmode\n";
+	    print MANA "keyword b FLATSCL$amp -wf $scale\n";
+	}
+    }
+    print MANA "keyword b $typeword -w $type\n";
+    if ($addtime == 1) {
+	print MANA " keyword b TVSTART -w $start\n";
+	print MANA " keyword b TVSTART -wc \"data validity start time\"\n";
+	print MANA " keyword b TVSTOP  -w $stop\n";
+	print MANA " keyword b TVSTOP -wc \"data validity start time\"\n";
+    }
+    if ($addconfig == 1) {
+	print MANA "keyword b ELCONF -w $elconf\n";
+	print MANA "keyword b ELCONF -wc \"Original Elixir config\"\n";
+    }	
+
+    @time = localtime;
+    $now = sprintf "%04d-%02d-%02dT%02d:%02d:%02d", $time[5]+1900, $time[4]+1, $time[3], $time[2], $time[1], $time[0];
+    $elsys = `gconfig ELIXIR_VERSION`; chop ($elsys);
+    print MANA "keyword b EL_SYS   -w  \"$elsys\"\n";
+    print MANA "keyword b EL_SYS   -wc \"Elixir System Version\"\n";
+    print MANA "keyword b EL_NRM   -w  \"$version\"\n";
+    print MANA "keyword b EL_NRM   -wc \"Elixir Normalize Version\"\n";
+    print MANA "keyword b REL_DATE -w \"$now\"\n";
+    print MANA "keyword b REL_DATE -wc \"UTC Release Date\"\n";
+
+    print MANA "keyword b COMMENT  -ws \"Elixir:normalize version $version\"\n";
+    print MANA "keyword b COMMENT  -ws \"FLAT image normalized wrt CCD $refccd\"\n";
+    print MANA "clip b 0.0 0.0 6.4 0.0\n";
+    print MANA "wd b $output -bitpix 16 -bzero 3.2 -bscale 0.0001\n";
+    print MANA "echo normlized done ($input to $output)\n";
+    print MANA "exit 0\n";
+    print MANA "end\n";  # end of macro 'go'
+
+    print MANA "go\n";
+    print MANA "exit 1\n";
+    close (MANA);
+    if ($?) {
+	print STDERR "ERROR problem running mana in normalize (flat)\n";
+	exit 1;
+    }
+    print STDOUT "SUCCESS: finished with normalize\n";
+    exit 0;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub uppercase {
+    return "\U$_[0]\E";
+}
+
+# given a filename, check that its path exists, create it if not
+sub ckpathname {
+    my ($file) = $_[0];
+    my (@words, $dir);
+
+    @words = split ("/", $file);
+    pop (@words);
+    $dir = join ("/", @words);
+
+    if ($dir eq "") { $dir = "."; }
+
+    if (! -d $dir) {
+	system ("mkdir -p $dir");
+	if ($?) {
+	    print STDERR "ERROR: can't make directory component for $file\n";
+	    exit 1;
+	}
+    }
+}
+
+sub usage {
+    print STDERR "normalize (input) (output) (ref) (type) [-time start stop] [-oldflips]\n";
+    exit 2;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/rerun.complete
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/rerun.complete	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/rerun.complete	(revision 22322)
@@ -0,0 +1,31 @@
+#!/usr/bin/env perl
+
+$fifo = `gconfig FIFOS`; chop $fifo;
+$failure = "$fifo/complete.failure"; 
+
+$Nflatten = `grep flatten $failure | wc -l`; chop $Nflatten;
+$Ngophot  = `grep gophot  $failure | wc -l`; chop $Ngophot;
+$Nimclean = `grep imclean $failure | wc -l`; chop $Nimclean;
+$Ngastro  = `grep gastro  $failure | wc -l`; chop $Ngastro;
+$Naddstar = `grep addstar $failure | wc -l`; chop $Naddstar;
+
+print "flatten: $Nflatten\n";
+print "gophot:  $Ngophot\n";
+print "imclean: $Nimclean\n";
+print "gastro:  $Ngastro\n";
+print "addstar: $Naddstar\n";
+
+# get the list of things to re-run:
+$runid  = `gconfig RUNID.POSTRUN`; chop $runid;
+$camera = `gconfig CAMERA.POSTRUN`; chop $camera;
+$catdir = `gconfig DB_STDS -D RUNID $runid -D CAMERA $camera`; chop $catdir;
+$datdir = `gconfig DATDIR`; chop $datdir;
+$ptlist = "$datdir/$runid/$runid.rerun.dat";
+$ptlist = "$datdir/$runid/$runid.rerun.log";
+
+#system ("grep addstar $failure | awk '{print \$1, \$2, \$3, \$4}' > $ptlist");
+#system ("elixir -D global.pending addstar -D RUNID $runid -D CAMERA $camera -D mode sexcomplete $ptlist >& $ptlog &");
+
+system ("cat $failure | awk '{print \$1, \$2, \$3, \$4}' > $ptlist");
+system ("elixir -D RUNID $runid -D CAMERA $camera -D mode sexcomplete $ptlist >& $ptlog &");
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/seeingstats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/seeingstats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/seeingstats	(revision 22322)
@@ -0,0 +1,113 @@
+#!/usr/bin/env perl
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+sub min {
+  if ($_[0] < $_[1]) {
+    return ($_[0]);
+  } else {
+    return ($_[1]);
+  }
+}
+
+sub max {
+  if ($_[0] > $_[1]) {
+    return ($_[0]);
+  } else {
+    return ($_[1]);
+  }
+}
+
+if (@ARGV != 1) { die "ERROR: USAGE: seeingstats (file)\n" }
+
+open (FILE, $ARGV[0]);
+@list = <FILE>;
+close (FILE);
+
+# the file contains results from sextractor. the lines
+# contain: FWHM FLAGS CLASS MAG 
+# reject bad objects, find mag min & number
+@mag = ();
+for ($i = 0; $i < @list; $i++) {
+  @words = split (" ", $list[$i]);
+  if ($words[1] > 0) { next; }
+  if ($words[3] > 0) { next; }
+  @mag = (@mag, $words[3]);
+}
+
+# find Mag threshold 
+@Mag = sort {$a <=> $b} @mag;
+$Nmag = @Mag;
+$Nwant = int(min(max(0.25*$Nmag, 10), $Nmag-1));
+$Mlim = $Mag[$Nwant];
+
+# fast way to zero a list of 1024 elements
+@Nfwhm = (0.0);
+for ($i = 0; $i < 10; $i++) {
+  @Nfwhm = (@Nfwhm, @Nfwhm);
+}
+$N = @Nfwhm;
+
+# reject bad objects, find mag min & number
+@fwhm = ();
+for ($i = 0; $i < @list; $i++) {
+  @words = split (" ", $list[$i]);
+  if ($words[1] > 0) { next; }
+  if ($words[3] > $Mlim) { next; }
+  @fwhm = (@fwhm, $words[0]);
+
+  # accumulate entry in histogram (@Nfwhm)
+  $bin = int ($words[0]*10.0);
+  if ($bin < 0)   { next; }
+  if ($bin > 999) { next; }
+  $Nfwhm[$bin] ++;
+}
+
+# find the mode (bin size = 0.1 pixels)
+$mode = 0;
+$Nmode = $Nfwhm[$mode];
+for ($i = 1; $i < @Nfwhm; $i++) {
+  if ($Nfwhm[$i] > $Nmode) {
+    $mode = $i;
+    $Nmode = $Nfwhm[$mode];
+  }
+}
+$Vmode = $mode * 0.1;
+$Fmin = $Vmode - 0.2;
+$Fmax = $Vmode + 0.2;
+
+# find the mean of values within 0.2 pix of the mode
+$F = 0;
+$N = 0;
+for ($i = 0; $i < @fwhm; $i++) {
+  if ($fwhm[$i] < $Fmin) { next; }
+  if ($fwhm[$i] > $Fmax) { next; }
+  $N++;
+  $F += $fwhm[$i];
+}
+if ($N > 3) {
+  $fwhm = $F / $N;
+  printf "%7.3f\n", $fwhm;
+  exit 0;
+}
+
+# I don't think the above can ever fail to get an answer, but...
+# if the mode search didn't work, resort to median
+$F = 0;
+$N = 0;
+@Fwhm = sort {$a <=> $b} @fwhm;
+$Nc = int(0.5*@Fwhm);
+for ($i = $Nc-1; $i < $Nc+2; $i++) {
+  $N++;
+  $F += $Fwhm[$i];
+}
+$fwhm = $F / $N;
+printf "%7.3f\n", $fwhm;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/split.mef
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/split.mef	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/split.mef	(revision 22322)
@@ -0,0 +1,99 @@
+#!/usr/bin/env perl
+
+# we could test for each resulting file and re-construct the
+# output lists using only the successful ones.  This would work
+# best after we re-organize the flips elixirs to run imselect 1x
+# and the split parallel by image, not config
+
+if (@ARGV != 4) { die "USAGE: split.mef (list.mef) (outdir) (ccd) (list.msplit)\n"; }
+
+$inlist = $ARGV[0];
+$outdir = $ARGV[1];
+$ccd    = $ARGV[2];
+$outlist= $ARGV[3];
+
+$ccdkeyword = `gconfig CCDNUM-KEYWORD`; chop ($ccdkeyword);
+
+$line = `cameraconfig -axes`; chop ($line);
+($Naxis1, $Naxis2) = split (" ", $line);
+
+if (! -d $outdir) { vsystem ("mkdir -p $outdir"); }
+
+open (FILE, "$inlist");
+@mef = <FILE>;
+close (FILE);
+
+@split = ();
+foreach $name (@mef) {
+    chop ($name);
+
+    # convert name /path/name.fits to /outdir/rootccd.fits
+    @words = split ("/", $name);
+    $root = $words[-1];
+    $root =~ s/.fits//;
+    
+    # change to use ccd number, not id?
+    $new = sprintf "%s/%s%s.fits", $outdir, $root, $ccd;
+    push @split, $new;
+}
+ 
+# convert mef file to split files in outdir
+# write all valid entries to outlist
+   
+# start with empty file for msplit
+unlink ($outlist);
+
+open (MANA, "|mana --norc");
+
+# define the macro 'split'
+print MANA "macro split\n";
+print MANA " \$CCDKEYWORD = $ccdkeyword\n";
+print MANA " rd a \$1 -n $ccd\n";
+print MANA " keyword a NAXIS1 nx\n";
+print MANA " keyword a NAXIS2 ny\n";
+print MANA " if ((\$nx == $Naxis1) && (\$ny == $Naxis2))\n";
+print MANA "   wd a \$2 -bitpix 16 -bzero 32768 -bscale 1.0\n";
+print MANA "   exec echo \$2 >> $outlist\n";
+print MANA " end\n";
+print MANA "end\n";
+
+# create the macro 'go' with the commands 
+print MANA "macro go\n";
+for ($i = 0; $i < @mef; $i++) {
+    if (-e $split[$i]) { 
+	# if NAXIS1 & NAXIS2 are valid, add entry to outlist
+	$answer = `echo $split[$i] | fields NAXIS1 NAXIS2`;
+	($tmp, $nx, $ny) = split (" ", $answer);
+	if ($nx != $Naxis1) { next; }
+	if ($ny != $Naxis2) { next; }
+	print MANA "exec echo $split[$i] >> $outlist\n";
+	# print STDERR "$split[$i] exists, skipping\n";
+	next; 
+    }
+    print MANA " split $mef[$i] $split[$i]\n";
+}
+# if the macro ends successfully, exit 0
+print MANA " exit 0\n";
+print MANA "end\n";
+
+# if the macro exits before the end, exit 1
+print MANA "go\n";
+print MANA "exit 1\n";
+close (MANA);
+
+$status = $?;
+print STDERR "mana exit status: $status\n";
+if ($status) {
+    print STDERR "ERROR: problem running split.mef\n";
+    exit 1;
+}
+
+print STDERR "SUCCESS\n";
+exit 0;
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/update.astrometry.iraf
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/update.astrometry.iraf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/update.astrometry.iraf	(revision 22322)
@@ -0,0 +1,116 @@
+#!/usr/bin/env perl
+
+$version = 1.0;
+if (@ARGV != 2) { die "USAGE: update.astrometry.iraf (efile) (cmpfile)\n" ; }
+
+$efile   = $ARGV[0];
+$cmpfile = $ARGV[1];
+
+if (! -e $efile) { die "output file $efile not found\n"; }
+if (! -e $cmpfile) { die "header source file $cmpfile not found\n"; }
+
+# grab astrometry keywords from $cmpfile:
+@header = &load_header ($cmpfile);
+
+# set some default values:
+@astrom = ();
+$Nastro = 0;
+
+# search for the appropriate lines:
+foreach $line (@header) {
+    
+    # astrometry keywords which are copied directly
+    if ($line =~ /NASTRO  =/) { $Nastro = substr ($line, 10, 21); }
+
+    if ($line =~ /CTYPE1  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /CTYPE2  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /CRPIX1  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /CRPIX2  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /CRVAL1  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /CRVAL2  =/) { @astrom = (@astrom, $line);}
+#    if ($line =~ /NASTRO  =/) { @astrom = (@astrom, $line); $Nastro = substr ($line, 10, 21); }
+#    if ($line =~ /CERROR  =/) { @astrom = (@astrom, $line);}
+    if ($line =~ /EQUINOX =/) { @astrom = (@astrom, $line);}
+
+    # convert the PC00i00j matrix & CDELTi to CDi_j
+    if ($line =~ /PC001001=/) { $pc11 = substr ($line, 10, 21); }
+    if ($line =~ /PC001002=/) { $pc12 = substr ($line, 10, 21); }
+    if ($line =~ /PC002001=/) { $pc21 = substr ($line, 10, 21); }
+    if ($line =~ /PC002002=/) { $pc22 = substr ($line, 10, 21); }
+    if ($line =~ /CDELT1  =/) { $cd1  = substr ($line, 10, 21); }
+    if ($line =~ /CDELT2  =/) { $cd2  = substr ($line, 10, 21); }
+}
+
+# check for validity of astrometry data
+if ($Nastro == 0) {
+    print STDERR "can't find all astrometry keywords from $infile\n";
+    @astrom = ($line);
+} else {
+    # convert the pc, cdelt terms to cd terms
+    $cd11 = $cd1*$pc11;
+    $cd12 = $cd2*$pc12;
+    $cd21 = $cd1*$pc21;
+    $cd22 = $cd2*$pc22;
+    $line = sprintf "CD1_1   = %20G / WCS Coordinate scale matrix                   ", $cd11; @astrom = (@astrom, $line);
+    $line = sprintf "CD1_2   = %20G / WCS Coordinate scale matrix                   ", $cd12; @astrom = (@astrom, $line);
+    $line = sprintf "CD2_1   = %20G / WCS Coordinate scale matrix                   ", $cd21; @astrom = (@astrom, $line);
+    $line = sprintf "CD2_2   = %20G / WCS Coordinate scale matrix                   ", $cd22; @astrom = (@astrom, $line);
+}
+
+# write output to file
+foreach $line (@astrom) {
+    $hedit = convert_to_hedit ($line);
+    # print STDOUT "$line\n";
+    print STDOUT "$hedit\n";
+}
+
+exit 0;
+
+###################################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
+# load FITS header lines (up to including END)
+sub load_header {
+
+    my ($file) = $_[0];
+    my ($buffer, $done, @buffer, $line);
+
+    open (FILE, "$file");
+    
+    $done = 0;
+    @buffer = ();
+
+    while (! $done) {
+	read FILE, $buffer, 2880;
+	for ($i = 0; $i < 2880; $i+=80) {
+	    $line = substr ($buffer, $i, 80);
+	    if (! $done) {
+		@buffer = (@buffer, $line);
+	    }
+	    if ($line =~ /^END     /) { $done = 1; }
+	}
+    }
+    close (FILE);
+    return (@buffer);
+}
+
+sub convert_to_hedit {
+
+    my ($line) = $_[0];
+    my ($hedit, $keyword, $value);
+
+    $keyword = substr ($line, 0, 8);
+    $value   = substr ($line, 10, 21);
+
+    $hedit = sprintf "hedit $efile $keyword $value ver- show-";
+    return ($hedit);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/validate
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/validate	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/validate	(revision 22322)
@@ -0,0 +1,309 @@
+#!/usr/bin/env perl
+
+# validate 
+# -update sends the validation value to the QSO database
+#  1 - valid image
+#  2 - subvalid image (problem, but doesn't matter)
+#  3 - invalid image
+#  4 - data error (missing, corrupted image)
+
+# global constants:
+
+$TRUE  = 1;
+$FALSE = 0;
+$RETRY = $TRUE;
+
+$PASS = (0 << 8);
+$FAIL = (1 << 8);
+$SUBVALID = (2 << 8);
+$DATA_ERR = (3 << 8);
+
+$MODE_UNSET    = 0;
+$MODE_VALID    = 1;
+$MODE_SUBVALID = 2;
+$MODE_INVALID  = 3;
+$MODE_DATAERR  = 4;
+
+$STATS = $FALSE;
+$update = $FALSE;
+
+$infile = "";
+
+# include the CFHT bin directory in the path so we have access to QSO functions:
+$ENV{'PATH'} = "$ENV{'PATH'}:/cfht/bin";
+
+# grab the command line arguments:
+@tARGV = ();
+for (; @ARGV > 0; ) {
+
+    if ($ARGV[0] eq "-stats") {
+	shift;
+	$STATS = $TRUE;
+	next;
+    }
+    if ($ARGV[0] eq "-update") {
+	shift;
+	$update = $TRUE;
+	next;
+    }
+    if ($ARGV[0] eq "-skip") {
+	shift;
+	$RETRY = $FALSE;
+	next;
+    }
+    if ($ARGV[0] eq "-infile") {
+	shift;
+	$infile = $ARGV[0];
+	shift;
+	next;
+    }
+    
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if (@ARGV != 1) { die "USAGE: validate (crunid) [-infile filename] [-update]\n" ;}
+
+$crunid = $ARGV[0];
+
+$answer = `mkrun run $crunid`; 
+if ($?) { die "ERROR finding run id: $crunid\n"; }
+($id, $start, $stop, $camera, $qrunid) = split (" ", $answer);
+# qrunid is space-separated (change?)
+if ($id ne $crunid) { die "ERROR with run id: $crunid\n"; }
+print STDERR "qso run ID(s): $qrunid\n";
+
+@valid    = ();
+@invalid  = ();
+@dataerr  = ();
+@subvalid = ();
+
+if ($infile) {
+    # load images from previous output run or source file:
+    open (FILE, $infile);
+    @rawlist = <FILE>;
+    close (FILE);
+    
+    $mode = $MODE_UNSET;
+    @imlist = ();
+    
+  RAWLIST:
+    foreach $name (@rawlist) {
+	chop ($name);
+	
+	if ($name eq "") { next RAWLIST; }
+	    
+	if ($name =~ /^valid images/) {
+	    $mode = $MODE_VALID;
+	    next RAWLIST;
+	}
+	if ($name =~ /^invalid images/) {
+	    $mode = $MODE_INVALID;
+	    next RAWLIST;
+	}
+	if ($name =~ /^subvalid images/) {
+	    $mode = $MODE_SUBVALID;
+	    next RAWLIST;
+	}
+	if ($name =~ /^dataerr images/) {
+	    $mode = $MODE_DATAERR;
+	    next RAWLIST;
+	}
+
+	# plain list of image names (/path/name.fits)
+	if ($mode == $MODE_UNSET) {
+	    # double check on name validity
+	    ($file, $junk) = split (" ", $name, 2);
+	    unless ($file =~ /\d\d\d\d\d\d\w/) { print STDERR "?"; next RAWLIST; }
+	    @imlist = (@imlist, $file);
+	    next RAWLIST;
+	}
+
+	# ($file) = split (" ", $name);
+	$file = $name;
+	# place image names in the appropriate list
+	if ($mode == $MODE_VALID) {
+	    @valid = (@valid, $file);
+	    next RAWLIST;
+	}
+	if ($mode == $MODE_INVALID) {
+	    @invalid = (@invalid, $file);
+	    next RAWLIST;
+	}
+	if ($mode == $MODE_SUBVALID) {
+	    @subvalid = (@subvalid, $file);
+	    next RAWLIST;
+	}
+	if ($mode == $MODE_DATAERR) {
+	    @dataerr = (@dataerr, $file);
+	    next RAWLIST;
+	}
+    }
+} else {
+    # load images directly from directory 
+    # this is not sufficient.  we need a better way to get a list of images for 
+    # a given run id (with the current MEF / SPLIT state correct).
+    # 
+    # probably we can just use imsearch -ccd 0
+    $rawdir = `gconfig -q -D RUNID $crunid RAWDIR`;
+    chop ($rawdir);
+    if ($rawdir eq "") {
+	print STDERR "can't find config entry RAWDIR\n";
+	exit 1;
+    }
+    @imlist = ();
+
+    # load in the MEF files (must be of format 123456x.fits)
+    @tlist = <$rawdir/*.fits>;
+    foreach $tname (@tlist) { 
+	if ((-f $tname) && ($tname =~ /\d\d\d\d\d\d\w.fits/)) { 
+	    @imlist = (@imlist, $tname); 
+	} 
+    }
+
+    # load in the SPLIT directories (must be of format 123456x)
+    @tlist = <$rawdir/???????>;
+    foreach $tname (@tlist) { 
+	if ((-d $tname) && ($tname =~ /\d\d\d\d\d\d\w/)) { 
+	    @imlist = (@imlist, $tname); 
+	} 
+    }
+}
+
+if ($STATS) {
+    $Nvalid = @valid;
+    $Ninvalid = @invalid;
+    $Nsubvalid = @subvalid;
+    $Ndataerr = @dataerr;
+
+    print STDOUT "valid    $Nvalid\n";
+    print STDOUT "invalid  $Ninvalid\n";
+    print STDOUT "subvalid $Nsubvalid\n";
+    print STDOUT "dataerr  $Ndataerr\n";
+    exit 0;
+}
+
+# load the QSO validated image list
+@qsovalid = ();
+if ($qrunid) { 
+    @qruns = split (",", $qrunid);
+    @qsovalid = ();
+    foreach $id (@qruns) {
+	print STDERR "$id:\n";
+	@answer = `select_val_xexp.sh -U qso_elixir -P op1eliw -qrunid $id`;
+	if ($?) {
+	    print STDERR "error in qso run ID\n";
+	    exit 1;
+	}
+	@qsovalid = (@qsovalid, @answer);
+    }
+}
+# create qsostate hash to quickly find valid qso images
+foreach $qline (@qsovalid) { 
+    chop ($qline); 
+    ($qname, $qeval, $eval, $seval) = split (" ", $qline);
+    $qsostate{$qname} = 1;
+}
+
+if ($RETRY) {
+# evaluate the remaining entries in imlist & invalid:
+    @imlist = (@imlist, @invalid, @dataerr);
+    @invalid = ();
+    @dataerr = ();
+    
+  IMLIST:
+    foreach $line (@imlist) {
+	($name) = split (" ", $line);
+	@words = split ("/", $name);
+	$tmp = $words[-1];
+	($obsid) = $tmp =~ /(\d\d\d\d\d\d\w)/;
+
+	$answer = `ckvalid $name`; chop ($answer);   
+	$status = $?;
+	if ($status) { 
+	    print STDERR "-";
+	} else {
+	    print STDERR "+";
+	}
+	
+	($file, $mode, $type, $runid, $state) = split (" ", $answer, 2);
+
+	if ($status == $PASS) { 
+	    @valid = (@valid, $answer);
+	    next IMLIST;
+	}
+	if ($status == $SUBVALID) {
+	    @subvalid = (@subvalid, $answer);
+	    next IMLIST;
+	}
+	if ($status == $DATA_ERR) {
+	    @dataerr = (@dataerr, $answer);
+	    next IMLIST;
+	}
+
+	# subvalid if not in qso list 
+	if ($status == $FAIL) {
+	    # are we in QSO list?
+	    if ($qsostate{$obsid}) {
+		@invalid = (@invalid, $answer);
+		next IMLIST;
+	    } else {
+		@subvalid = (@subvalid, $answer);
+		next IMLIST;
+	    }
+	}
+	print STDERR "unknown result $answer\n";
+    }
+}
+
+@mode = ('valid',     'subvalid',      'invalid',     'dataerr');
+@code = ($MODE_VALID, $MODE_SUBVALID,  $MODE_INVALID, $MODE_DATAERR);
+
+for ($i = 0; $i < @mode; $i++) {
+    $mode = $mode[$i];
+    $code = $code[$i];
+
+    print STDOUT "$mode images\n";
+    foreach $line (@$mode) {
+	print STDOUT "$line\n";
+	if ($update) { 
+	    ($file, $mode, $type, $runid, $state) = split (" ", $line, 5);
+	    $seval = get_seval ($state);
+
+	    @w = split ("/", $file);
+	    $n = substr ($w[-1], 0, 6);
+	    # print STDERR "update_xexpe.sh -U qso_elixir -P op1eliw --obsid $n --eval $code --Seval $seval >> test.list\n";
+	    vsystem ("update_xexpe.sh -U qso_elixir -P op1eliw --obsid $n --eval 0 --Seval $seval >> xexpe.list");
+	}
+    }
+    print STDOUT "\n";
+}
+
+sub get_seval {
+
+    my($state) = $_[0];
+    my (@evals, $seval);
+
+    @evals = split (" ", $state);
+    
+    $seval = "";
+    if ($evals[0]) { $seval = $seval . "t"; } else { $seval = $seval . "f"; }  # DATA
+    if ($evals[1]) { $seval = $seval . "t"; } else { $seval = $seval . "f"; }  # DETREND
+    $seval = $seval . "-";
+    if ($evals[2]) { $seval = $seval . "t"; } else { $seval = $seval . "f"; }  # PHOTOM
+    if ($evals[3]) { $seval = $seval . "t"; } else { $seval = $seval . "f"; }  # ASTROM
+
+    return ($seval);
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    my($status) = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/wfi.keywords
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/wfi.keywords	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/perl/src/wfi.keywords	(revision 22322)
@@ -0,0 +1,110 @@
+#!/usr/bin/env perl
+
+$version = 1.0;
+$Nccd = `cameraconfig -Nccd`; chop ($Nccd);
+
+if (@ARGV != 2) { die "USAGE: grab.keywords (fitsfile) (hdrfile)\n" ; }
+
+$fitsfile  = $ARGV[0];  # input image file
+$hdrfile   = $ARGV[1];  # hdr file for fits_insert
+
+@header = `fhead $fitsfile`;
+
+@newhead = ();
+# grab lines from primary header
+foreach $line (@header) {
+    # astrometry keywords which are copied directly
+    if ($line =~ /HIERARCH ESO INS FILT NAME  =/) { 
+	@word = split ("'", $line);
+	$newline = sprintf "FILTER  = '%-18s' / %-46s \n", $word[1], "Filter name";	
+	@newhead = (@newhead, $newline);
+    }
+    if ($line =~ /HIERARCH ESO DPR TYPE       =/) { 
+	@word = split ("'", $line);
+	$value = @word[1];
+	if ($value eq "SKY") { $value = "OBJECT"; }
+	if ($value eq "SKY,FLAT") { $value = "FLAT"; }
+	if ($value eq "DOME,FLAT") { $value = "FLAT"; }
+	$newline = sprintf "OBSTYPE = '%-18s' / %-46s \n", $value, "Obs Type";	
+	@newhead = (@newhead, $newline);
+    }
+    if ($line =~ /HIERARCH ESO TEL AIRM START =/) {
+	($value) = $line =~ /HIERARCH ESO TEL AIRM START = \s*(\S+)/;
+	$newline = sprintf "AIRMASS = %20.10f / %-46s \n", $value, "Airmass";	
+	@newhead = (@newhead, $newline);
+    }
+    if ($line =~ /HIERARCH ESO TEL FOCU VALUE =/) {
+	($value) = $line =~ /HIERARCH ESO TEL FOCU VALUE = \s*(\S+)/;
+	$newline = sprintf "FOCUS   = %20.10f / %-46s \n", $value, "Focus Value";	
+	@newhead = (@newhead, $newline);
+    }
+    if ($line =~ /HIERARCH ESO INS SIGNAL1 ST =/) {
+	($value) = $line =~ /HIERARCH ESO INS SIGNAL1 ST = \s*(\S+)/;
+	$newline = sprintf "SIGNAL1 = %20.10f / %-46s \n", $value, "Detector temp";	
+	@newhead = (@newhead, $newline);
+    }
+    if ($line =~ /HIERARCH ESO INS SIGNAL2 ST =/) {
+	($value) = $line =~ /HIERARCH ESO INS SIGNAL2 ST = \s*(\S+)/;
+	$newline = sprintf "SIGNAL2 = %20.10f / %-46s \n", $value, "Electronic temp";	
+	@newhead = (@newhead, $newline);
+    }
+    if ($line =~ /HIERARCH ESO INS SIGNAL3 ST =/) {
+	($value) = $line =~ /HIERARCH ESO INS SIGNAL3 ST = \s*(\S+)/;
+	$newline = sprintf "SIGNAL3 = %20.10f / %-46s \n", $value, "Barom press";	
+	@newhead = (@newhead, $newline);
+    }
+    $newline = sprintf "ROTATE  = %20.10f / %-46s \n", 0.0, "Barom press";	
+    @newhead = (@newhead, $newline);
+    $newline = sprintf "NEXTEND = %20d / %-46s \n", $Nccd, "Nextend";	
+    @newhead = (@newhead, $newline);
+}
+ 
+for ($i = 0; $i < $Nccd; $i++) {
+    @chiphead = ();
+    @header = `fhead -x $i $fitsfile`;
+    foreach $line (@header) {
+	if ($line =~ /HIERARCH ESO DET CHIP\d INDEX=/) {
+	    ($value) = $line =~ /HIERARCH ESO DET CHIP\d INDEX= \s*(\S+)/;
+#	    $newline = sprintf "CHIPID  = %20d / %-46s \n", $value, "Detector ID";	
+#	    @chiphead = (@chiphead, $newline);
+	    $newline = sprintf "IMAGEID = %20d / %-46s \n", $value, "Detector ID";	
+	    @chiphead = (@chiphead, $newline);
+	    last;
+	}
+    }
+
+    # write output to file
+    open (FILE, ">$hdrfile");
+    foreach $line (@newhead) {
+	print FILE "$line";
+    }
+    foreach $line (@chiphead) {
+	print FILE "$line";
+    }
+    close (FILE);
+
+    system ("fits_insert -X $i $fitsfile $hdrfile");
+    if ($?) { die "problem with extension $i\n"; }
+}
+
+# write output to file
+open (FILE, ">$hdrfile");
+foreach $line (@newhead) {
+    print FILE "$line";
+}
+system ("fits_insert $fitsfile $hdrfile");
+if ($?) { die "problem with phu\n"; }
+
+exit 0;
+
+###################################################################################
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub goodbye {
+    die "@_\n";
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/Makefile	(revision 22322)
@@ -0,0 +1,54 @@
+default: photdbc
+help:
+	@echo "make options: photdbc (default)"
+
+include ../../Makefile.System
+HOME    =       $(ROOT)/src/photdbc
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+photdbc: $(BIN)/photdbc.$(ARCH)
+install: $(DESTBIN)/photdbc
+
+PHOTDBC = \
+$(SRC)/photdbc.$(ARCH).o	   \
+$(SRC)/initialize.$(ARCH).o	   \
+$(SRC)/ConfigInit.$(ARCH).o	   \
+$(SRC)/args.$(ARCH).o		   \
+$(SRC)/copy_images.$(ARCH).o	   \
+$(SRC)/Shutdown.$(ARCH).o          \
+$(SRC)/join_stars.$(ARCH).o        \
+$(SRC)/make_subcatalog.$(ARCH).o        
+
+OLD = \
+$(SRC)/liststats.$(ARCH).o	   \
+$(SRC)/name_region.$(ARCH).o	   \
+$(SRC)/find_images.$(ARCH).o	   \
+$(SRC)/find_regions.$(ARCH).o	   \
+$(SRC)/get_regions.$(ARCH).o	   \
+$(SRC)/load_catalogs.$(ARCH).o	   \
+$(SRC)/gcatalog.$(ARCH).o 	   \
+$(SRC)/unique_measures.$(ARCH).o   \
+$(SRC)/flag_measures.$(ARCH).o     \
+$(SRC)/get_mags.$(ARCH).o          \
+$(SRC)/check_directory.$(ARCH).o   \
+$(SRC)/overlap_funcs.$(ARCH).o     \
+$(SRC)/image-db.$(ARCH).o          \
+$(SRC)/check_permissions.$(ARCH).o \
+$(SRC)/misc.$(ARCH).o		   \
+$(SRC)/SetSignals.$(ARCH).o        \
+$(SRC)/Shutdown.$(ARCH).o          \
+$(SRC)/wcatalog.$(ARCH).o
+
+
+$(PHOTDBC): $(INC)/photdbc.h
+$(BIN)/photdbc.$(ARCH): $(PHOTDBC)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,10 @@
+
+photdbc-1-1 : 
+  * converted to gfits APIs (forces libfits 1.6)
+  * updated to use sky regions
+  * general overhaul:
+    * performs a basic copy
+    * operations restricted by region
+    * output catalog may be filtered by
+      -maxminmag MAG
+      -instmag Mmin Mmax
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/doc/notes.txt	(revision 22322)
@@ -0,0 +1,28 @@
+
+- select a region
+
+- load average / measure / secfilt / missing
+
+- construct subset based on selection criteria:
+  - Minst
+  - dMinst
+  - X,Y
+  - R,D
+
+- merge neighbor stars (join within X.X arcsec)
+
+- mark bad measurements:
+
+  - outside area
+  - Minst < cutoff
+  - Mcal > 0.015 (lookup from table?)
+
+  - subset for given filter (match by string?):
+    - 2x 3sigma 
+    - softened chisq > 3.0
+
+- mark objects with fewer than N measurements
+
+
+-----
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/include/photdbc.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/include/photdbc.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/include/photdbc.h	(revision 22322)
@@ -0,0 +1,114 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+
+typedef struct {
+  int measure;          // pointer to measure entry
+  unsigned int averef;  // old measures come from multiple averages
+  float R, D;           // actual coordinates of this measure
+} Mpointer;
+
+typedef struct {
+  double median;
+  double mean;
+  double sigma;
+  double error;
+  double chisq;
+  double min;
+  double max;
+  double total;
+  int    Nmeas;
+} StatType;
+
+// need to get RADIUS from Config 
+
+/* global variables */
+int    SHOW_PARAMS;
+int    VERBOSE;
+double JOIN_RADIUS;
+double UNIQ_RADIUS;
+double DMCAL_MIN;;
+char   ImageCat[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char   PhotCodeFile[256];
+
+double RMIN;
+double RMAX;
+double DMIN;
+double DMAX;
+ 
+double XMIN;
+double XMAX;
+double YMIN;
+double YMAX;
+double MMIN;
+double MMAX;
+
+double DMSYS;
+double DMGAIN;
+double CHISQ_MAX;
+double SIGMA_MAX;
+int    NMEAS_MIN;
+double ZERO_POINT;
+
+int ExcludeByInstMag;
+double INST_MAG_MIN;
+double INST_MAG_MAX;
+
+int ExcludeByMaxMinMag;
+double MAX_MIN_MAG;
+
+SkyRegion REGION;
+PhotCodeData photcodes;
+
+# define FLAG_AREA            0X0001
+# define FLAG_MINST           0X0002
+# define FLAG_DMCAL           0x0004
+# define FLAG_SIGCLIP         0X0008
+# define FLAG_CHISQ           0x0010
+# define FLAG_TOOFEW          0x0020
+# define FLAG_DUPMEAS         0x0040
+
+Image        *find_images (FITS_DB *db, GSCRegion *region, int Nregion, int *Nimage, int **LineNum);
+GSCRegion    *find_regions (Image *image, int Nimage, int *Nregions, GSCRegion *fullregion); 
+GSCRegion    *get_regions (double minRa, double maxRa, double minDec, double maxDec, int *Nregions); 
+Catalog      *load_catalogs (GSCRegion *region, int Nregion); 
+GSCRegion    *load_images (FITS_DB *db, char *seed, int *nregion);
+GSCRegion    *name_region (char *name, int *Nregions); 
+void          ConfigInit (int *argc, char **argv); 
+int           args (int argc, char **argv); 
+int           corner_check (double *x1, double *y1, double *x2, double *y2); 
+int           edge_check (double *x1, double *y1, double *x2, double *y2); 
+void          flag_measures (FITS_DB *db, Catalog *catalog); 
+int           gcatalog (Catalog *catalog); 
+void          get_mags (Catalog *catalog); 
+void          getfullregion (Image *image, int Nimage, GSCRegion *fullregion); 
+void          initialize (int argc, char **argv); 
+void          initstats (char *mode); 
+void          join_stars (Catalog *catalog); 
+int           liststats (double *value, double *dvalue, int N, StatType *stats); 
+double        opening_angle (double x1, double y1, double x2, double y2, double x3, double y3); 
+void          set_ZP (double ZERO); 
+void          sortA (double *X, int N); 
+void          sortB (double *X, double *Y, int N); 
+void          sortC (double *X, double *Y, double *F1, double *F2, int N); 
+void          sortD (double *X, double *Y, double *Z, int N); 
+void          sort_lists (float *X, float *Y, int *index, int N); 
+void          sort_time (unsigned int *X, int *Y, int N); 
+void          unique_measures (Catalog *catalog);
+void          wcatalog (Catalog *catalog);
+int           make_subcatalog (Catalog *subcatalog, Catalog *catalog);
+void          check_directory (char *basefile);
+
+int Shutdown (char *format, ...);
+void set_db (FITS_DB *in);
+void lock_image_db (FITS_DB *db, char *filename);
+void unlock_image_db (FITS_DB *db);
+void check_permissions (char *basefile);
+void TrapSignal (int sig);
+void SetProtect (int mode);
+int SetSignals ();
+int copy_images (char *outdir);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "photdbc.h"
+
+int success = TRUE;
+
+void WarnConfig (char *config, char *key, char * mode, int N, void *var) {
+  if (!ScanConfig (config, key, mode, N, var)) {
+    fprintf (stderr, "missing config variable %s\n", key);
+    success = FALSE;
+  }
+}
+
+void ConfigInit (int *argc, char **argv) {
+
+  char *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  WarnConfig (config, "PHOTDBC_JOIN_RADIUS",       "%lf", 0, &JOIN_RADIUS);
+  // WarnConfig (config, "UNIQ_RADIUS",            "%lf", 0, &UNIQ_RADIUS);
+
+  // WarnConfig (config, "XMIN",                   "%lf", 0, &XMIN);
+  // WarnConfig (config, "XMAX",                   "%lf", 0, &XMAX);
+  // WarnConfig (config, "YMIN",                   "%lf", 0, &YMIN);
+  // WarnConfig (config, "YMAX",                   "%lf", 0, &YMAX);
+  
+  // WarnConfig (config, "DMCAL_MIN",              "%lf", 0, &tmp);  DMCAL_MIN = 1000*tmp;
+  // WarnConfig (config, "MMIN",                   "%lf", 0, &tmp);  MMIN      = 1000*tmp;
+  // WarnConfig (config, "MMAX",                   "%lf", 0, &tmp);  MMAX      = 1000*tmp;
+  // WarnConfig (config, "DMSYS",                  "%lf", 0, &tmp);  DMSYS     = SQ(1000*tmp);
+  // WarnConfig (config, "DMGAIN",                 "%lf", 0, &DMGAIN); 
+  // WarnConfig (config, "CHISQ_MAX",              "%lf", 0, &CHISQ_MAX);
+
+  ScanConfig (config, "SIGMA_MAX",              "%lf", 0, &SIGMA_MAX);
+  ScanConfig (config, "NMEAS_MIN",              "%d",  0, &NMEAS_MIN);
+
+  WarnConfig (config, "GSCFILE",                "%s",  0, GSCFILE);
+  WarnConfig (config, "CATDIR",                 "%s",  0, CATDIR);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig (config, "PHOTCODE_FILE",         	"%s",  0, MasterPhotcodeFile);
+
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  WarnConfig (config, "ZERO_PT",                "%lf", 0, &ZERO_POINT);
+
+  if (!success) { 
+    fprintf (stderr, "missing config parameter\n");
+    exit (1);
+  }
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+  SetZeroPoint (ZERO_POINT);
+
+  free (config);
+  free (file);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/SetSignals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/SetSignals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/SetSignals.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "photdbc.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect this signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore */
+    case SIGCONT:    /* continue - maintain this action */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? */
+    case SIGURG:     /* socket signal, ignore this */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/Shutdown.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/Shutdown.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/Shutdown.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include "photdbc.h"
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format));
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  fprintf (stderr, "ERROR: photdbc halted\n");
+  exit (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/args.c	(revision 22322)
@@ -0,0 +1,102 @@
+# include "photdbc.h"
+
+int args (int argc, char **argv) {
+
+  int N;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  SHOW_PARAMS = FALSE;
+  if ((N = get_argument (argc, argv, "-params"))) {
+    remove_argument (N, &argc, argv);
+    SHOW_PARAMS = TRUE;
+  }
+
+  ExcludeByInstMag = FALSE;
+  if ((N = get_argument (argc, argv, "-instmag"))) {
+    remove_argument (N, &argc, argv);
+    ExcludeByInstMag = TRUE;
+    remove_argument (N, &argc, argv);
+    INST_MAG_MIN = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    INST_MAG_MAX = atof(argv[N]);
+  }
+
+  ExcludeByMaxMinMag = FALSE;
+  if ((N = get_argument (argc, argv, "-maxminmag"))) {
+    remove_argument (N, &argc, argv);
+    ExcludeByMaxMinMag = TRUE;
+    remove_argument (N, &argc, argv);
+    MAX_MIN_MAG = atof(argv[N]);
+  }
+
+  /* specify portion of the sky */
+  REGION.Rmin = 0;
+  REGION.Rmax = 360;
+  REGION.Dmin = -90;
+  REGION.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    REGION.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    REGION.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    REGION.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    REGION.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+
+    if (REGION.Rmin == REGION.Rmax) {
+      fprintf (stderr, "ERROR: selected region is ill-defined: Rmin == Rmax\n");
+      exit (2);
+    }
+    if (REGION.Dmin == REGION.Dmax) {
+      fprintf (stderr, "ERROR: selected region is ill-defined: Dmin == Dmax\n");
+      exit (2);
+    }
+  }
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: photdbc (output)\n");
+    exit (2);
+  } 
+
+  if ((REGION.Rmin == 0) && (REGION.Rmax == 360) && (REGION.Dmin == -90) && (REGION.Dmax == +90)) {
+    int i;
+    fprintf (stderr, "you have requested a copy of the entire sky in one pass\n");
+    fprintf (stderr, "this could be a time consuming operation.  type Ctrl-C within 5 seconds to cancel\n");
+    for (i = 5; i > 0; i--) {
+      fprintf (stderr, "%d.. ", i);
+      usleep (1000000);
+    }
+    fprintf (stderr, "\n");
+  }
+
+  return (TRUE);
+}
+
+/**
+
+ this program takes an existing DVO database and makes a copy, applying a number of optional
+ filters in the process.  
+
+ photdbc (output)
+
+ allowed filters / restrictions include:
+
+ -region ra dec ra dec : limit operation to the specified region 
+ -join                 : join measurements between stars using JOIN_RADIUS
+ -ccdregion X Y X Y    : only keep detections within the specified detector region
+                         (can this be limited to specific photcodes? cameras? detectors?)
+ -photcode_limits code Mmin Mmax : allow multiples of these
+
+ SIGMA_MAX
+ NMEAS_MIN
+ INST_MAG_MAX
+ INST_MAG_MIN
+
+**/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/copy_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/copy_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/copy_images.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "photdbc.h"
+
+int copy_images (char *outdir) {
+
+  int status, Nimage;
+  char *ImageOut;
+  FITS_DB in;
+  FITS_DB out;
+  Image *image;
+  struct stat filestat;
+  char *path;
+  char filename[1024];
+
+  path = pathname (ImageCat);
+  status = stat (path, &filestat);
+  if (status == -1) Shutdown ("ERROR: CATDIR %s does not exist, no data in database", path);
+
+  status = dvo_image_lock (&in, ImageCat, 60.0, LCK_SOFT);
+  if (!status) {
+    fprintf (stderr, "No images in catalog %s\n", ImageCat);
+    return (TRUE);
+  }
+  if (in.dbstate == LCK_EMPTY) {
+    fprintf (stderr, "No images in catalog %s\n", ImageCat);
+    dvo_image_unlock (&in);
+    return (TRUE);
+  }
+
+  // load the input image data
+  if (!dvo_image_load (&in, VERBOSE, FALSE)) Shutdown ("can't read image catalog %s", in.filename);
+
+  // define output filename (replace CATDIR)
+  ImageOut = strsubs (in.filename, CATDIR, outdir);
+  if (ImageOut == NULL) Shutdown ("error with input or output catalog name");
+
+  // lock the output catalog
+  status = dvo_image_lock (&out, ImageOut, 60.0, LCK_XCLD);
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", ImageOut);
+  if (out.dbstate != LCK_EMPTY) Shutdown ("ERROR: image table exists %s", ImageOut);
+
+  out.mode   = dvo_catalog_catmode (CATMODE);
+  out.format = dvo_catalog_catformat (CATFORMAT);
+  dvo_image_create (&out, ZERO_POINT);
+    
+  image = gfits_table_get_Image (&in.ftable, &Nimage, &in.swapped);
+  dvo_image_addrows (&out, image, Nimage);
+
+  dvo_image_update (&out, VERBOSE);
+  dvo_image_unlock (&out);
+
+  dvo_image_unlock (&in);
+
+  // create the photcode file
+  sprintf (filename, "%s/Photcodes.dat", outdir);
+  SavePhotcodesFITS (filename);
+
+  return (TRUE);
+}
+
+// globals: ImageCat, VERBOSE, CATMODE, CATFORMAT, ZeroPt
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/find_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/find_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/find_images.c	(revision 22322)
@@ -0,0 +1,129 @@
+# include "photdbc.h"
+
+/* this function returns a list of all images which overlap the given
+   set of region files.  All images in the image catalog are tested
+   once, so there is no check that an image already has been included.
+   LineNum stores the locations in the Image database of the list of
+   images */
+
+Image *find_images (FITS_DB *db, GSCRegion *region, int Nregion, int *Nimage, int **LineNum) {
+  
+  Image *timage, *image;
+  int i, j, k, m, found, nimage, Ntimage, NIMAGE;
+  int InRange;
+  double Xc[5], Yc[5], Xi[5], Yi[5], r, d, dx, dy;
+  int *line_number;
+  Coords tcoords;
+
+  if (VERBOSE) fprintf (stderr, "finding images\n");
+
+  /* we make positional comparisons in the projection of catalog */
+  tcoords.crval1 = 0.5*(region[0].RA[0]  + region[0].RA[1]);
+  tcoords.crval2 = 0.5*(region[0].DEC[0] + region[0].DEC[1]);
+  tcoords.crpix1 = tcoords.crpix2 = 0.0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1  = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2  = tcoords.pc2_1 = 0.0;
+  strcpy (tcoords.ctype, "RA---TAN");
+
+  timage = gfits_table_get_Image (&db[0].ftable, &Ntimage, &db[0].swapped);
+
+  nimage = 0;
+  NIMAGE = 100;
+  ALLOCATE (image, Image, NIMAGE);
+  ALLOCATE (line_number, int, NIMAGE);
+  
+  for (i = 0; i < Ntimage; i++) {
+      
+# if (0)
+    /* select images by photcode */
+    ecode = GetPhotcodeEquivCodebyCode (timage[i].photcode);
+    if (ecode != photcode[0].code) continue;
+
+    /* select images by time */
+    if (TimeSelect) {
+      if (timage[i].tzero < TSTART) continue;
+      if (timage[i].tzero > TSTOP) continue;
+    }
+# endif
+
+    /* define image corners */
+    Xi[0] = 0;            Yi[0] = 0;
+    Xi[1] = timage[i].NX; Yi[1] = 0;
+    Xi[2] = timage[i].NX; Yi[2] = timage[i].NY;
+    Xi[3] = 0;            Yi[3] = timage[i].NY;
+    Xi[4] = 0;            Yi[4] = 0;
+    found = FALSE;
+    /* transform to tcoords */
+    for (j = 0; j < 5; j++) {
+      XY_to_RD (&r, &d, Xi[j], Yi[j], &timage[i].coords);
+      InRange = RD_to_XY (&Xi[j], &Yi[j], r, d, &tcoords);
+      if (!InRange) goto imskip;
+    }
+    /* compare with each region file */
+    for (m = 0; (m < Nregion) && !found; m++) { 
+      /* define catalog corners */
+      Xc[0] = region[m].RA[0]; Yc[0] = region[m].DEC[0];
+      Xc[1] = region[m].RA[1]; Yc[1] = region[m].DEC[0];
+      Xc[2] = region[m].RA[1]; Yc[2] = region[m].DEC[1];
+      Xc[3] = region[m].RA[0]; Yc[3] = region[m].DEC[1];
+      Xc[4] = region[m].RA[0]; Yc[4] = region[m].DEC[0];
+      for (j = 0; j < 5; j++) {
+	r = Xc[j]; d = Yc[j];
+	RD_to_XY (&Xc[j], &Yc[j], r, d, &tcoords);
+      }
+      dx = 0.02*(Xc[2] - Xc[0]);
+      dy = 0.02*(Yc[2] - Yc[0]);
+      Xc[0] -= dx; Yc[0] -= dy;
+      Xc[1] += dx; Yc[1] -= dy;
+      Xc[2] += dx; Yc[2] += dy;
+      Xc[3] -= dx; Yc[3] += dy;
+      Xc[4] -= dx; Yc[4] -= dy;
+      
+      /* check if image corner inside catalog */
+      for (j = 0; (j < 4) && !found; j++) {
+	found |= corner_check (&Xi[j], &Yi[j], &Xc[0], &Yc[0]);
+      }
+      /* check if catalog corner inside image */
+      for (j = 0; (j < 4) && !found; j++) {
+	found |= corner_check (&Xc[j], &Yc[j], &Xi[0], &Yi[0]);
+      }
+      /* check if edges cross */
+      for (j = 0; (j < 4) && !found; j++) {
+	for (k = 0; (k < 4) && !found; k++) {
+	  found |= edge_check (&Xi[j], &Yi[j], &Xc[k], &Yc[k]);
+	}
+      }
+      if (found) {
+	image[nimage] = timage[i]; 
+	if (image[nimage].code == ID_IMAGE_NOCAL) {
+	  image[nimage].code &= ~ID_IMAGE_NOCAL;
+	}	    
+	line_number[nimage] = i;
+	nimage ++;
+	if (nimage == NIMAGE) {
+	  NIMAGE += 100;
+	  REALLOCATE (image, Image, NIMAGE);
+	  REALLOCATE (line_number, int, NIMAGE);
+	}
+      }
+    }
+  imskip:
+    continue;
+  }
+      
+  if (VERBOSE) fprintf (stderr, "found %d images\n", nimage);
+
+  /* we are going to use image[nimage] to represent images not in the database:
+     these are measurements from external sources, like USNO */
+
+  assignMcal (&image[nimage], (double *) NULL, -1);
+  image[nimage].code = ID_IMAGE_NEW;
+  
+  REALLOCATE (image, Image, MAX (nimage + 1, 1));
+  REALLOCATE (line_number, int, MAX (nimage + 1, 1));
+  *Nimage = nimage;
+  *LineNum = line_number;
+  return (image);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/flag_measures.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/flag_measures.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/flag_measures.c	(revision 22322)
@@ -0,0 +1,202 @@
+# include "photdbc.h"
+
+void flag_measures (FITS_DB *db, Catalog *catalog) {
+
+  int i, j, k, m, n, N, found;
+  int *imagelink, Nimage, Nlist, PhotNsec;
+  unsigned int Time;
+  int Ncode, Photcode, Minst, Mag, BAD_MEASURE;
+  double ra, dec, x, y, Nsigma;
+  double *list, *dlist;
+
+  Image *image;
+  StatType stats;
+
+  if (VERBOSE) fprintf (stderr, "flagging bad measurements\n");
+
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  PhotNsec = GetPhotcodeNsecfilt ();
+  /* need to replace dMcal with values from external table? */
+
+  /* clear existing measure flags */
+  for (i = 0; i < catalog[0].Nmeasure; i++) catalog[0].measure[i].flags = 0;
+
+  /* generate image index */
+  ALLOCATE (imagelink, int, catalog[0].Nmeasure);
+  for (i = 0; i < catalog[0].Nmeasure; i++) {
+    n = catalog[0].measure[i].averef;
+    Time = catalog[0].measure[i].t;
+    Photcode = catalog[0].measure[i].photcode;
+    found = FALSE;
+    for (k = 0; !found && (k < Nimage); k++) { 
+      if ((Time == image[k].tzero) && (Photcode == image[k].photcode)) {
+	imagelink[i] = k;
+	found = TRUE;
+      }
+    }
+    if (!found) {
+      /* some error? */
+    }
+  }
+
+  /* flag by area : find source image, get X,Y coords, set flag */
+  for (i = 0; i < catalog[0].Nmeasure; i++) {
+    n = catalog[0].measure[i].averef;
+    ra  = catalog[0].average[n].R - catalog[0].measure[i].dR / 3600.0;
+    dec = catalog[0].average[n].D - catalog[0].measure[i].dD / 3600.0;
+    k = imagelink[i];
+    RD_to_XY (&x, &y, ra, dec, &image[k].coords);
+    if (x < XMIN) goto flagarea;
+    if (x > XMAX) goto flagarea;
+    if (y < YMIN) goto flagarea;
+    if (y > YMAX) goto flagarea;
+    continue;
+  flagarea:
+    catalog[0].measure[i].flags |= FLAG_AREA;
+  }
+
+  /* flag by Minst */
+  for (i = 0; i < catalog[0].Nmeasure; i++) {
+    Minst = iPhotInst (&catalog[0].measure[i]);
+    if (Minst < MMIN) goto flagminst;
+    if (Minst > MMAX) goto flagminst;
+    continue;
+  flagminst:
+    catalog[0].measure[i].flags |= FLAG_MINST;
+  }
+
+  /* flag by image (dMcal? time from table?) */
+  for (i = 0; i < catalog[0].Nmeasure; i++) {
+    k = imagelink[i];
+    if (image[k].dMcal > DMCAL_MIN) {
+      catalog[0].measure[i].flags |= FLAG_DMCAL;
+    }
+  }
+
+  /* allocate a list for temp storage of mag values */
+  Nlist = 0;
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    Nlist = MAX (Nlist, catalog[0].average[i].Nm);
+  }
+  ALLOCATE (list, double, Nlist);
+  ALLOCATE (dlist, double, Nlist);
+  initstats ("MEAN");
+
+  /* the following sections apply only to PRI/SEC photcodes */
+  for (n = 0; n < photcodes.Ncode; n++) {
+    if (photcodes.code[n].type == PHOT_DEP) continue;
+    if (photcodes.code[n].type == PHOT_REF) continue;
+    Photcode = photcodes.code[n].code;
+    
+    BAD_MEASURE = FLAG_AREA | FLAG_MINST | FLAG_DMCAL;
+    /* 2x 3sigma clipping */
+    for (i = 0; i < catalog[0].Naverage; i++) {
+
+      /* extract good measures, calculate stats */
+      N = 0;
+      m = catalog[0].average[i].offset;
+      for (j = 0; j < catalog[0].average[i].Nm; j++, m++) {
+	if ((Ncode = photcodes.hashcode[catalog[0].measure[m].photcode]) < 0) continue;
+	if (photcodes.code[Ncode].equiv != Photcode) continue;
+	if (photcodes.code[Ncode].type != PHOT_DEP) continue;
+	if (catalog[0].measure[m].flags & BAD_MEASURE) continue;
+	list[N] = iPhotRel (&catalog[0].measure[m], &catalog[0].average[i],  &catalog[0].secfilt[i*PhotNsec]);
+	dlist[N] = sqrt (SQ(DMGAIN*catalog[0].measure[m].dM) + DMSYS);
+	N++;
+      }
+      if (N < 3) continue;
+      liststats (list, dlist, N, &stats);
+      Nsigma = 3.0;
+      if (N < 10) Nsigma = 0.9*N / sqrt (N - 1);
+
+      /* re-extract good measures, calculate stats */
+      N = 0;
+      m = catalog[0].average[i].offset;
+      for (j = 0; j < catalog[0].average[i].Nm; j++, m++) {
+	if ((Ncode = photcodes.hashcode[catalog[0].measure[m].photcode]) < 0) continue;
+	if (photcodes.code[Ncode].equiv != Photcode) continue;
+	if (photcodes.code[Ncode].type != PHOT_DEP) continue;
+	if (catalog[0].measure[m].flags & BAD_MEASURE) continue;
+	Mag = iPhotRel (&catalog[0].measure[m], &catalog[0].average[i],  &catalog[0].secfilt[i*PhotNsec]);
+	if (fabs(Mag - stats.mean) > Nsigma*stats.sigma) continue;
+	list[N]  = Mag;
+	dlist[N] = sqrt (SQ(DMGAIN*catalog[0].measure[m].dM) + DMSYS);
+	N++;
+      }
+      if (N > 2) liststats (list, dlist, N, &stats);
+
+      /* flag bad measures */
+      m = catalog[0].average[i].offset;
+      for (j = 0; j < catalog[0].average[i].Nm; j++, m++) {
+	if ((Ncode = photcodes.hashcode[catalog[0].measure[m].photcode]) < 0) continue;
+	if (photcodes.code[Ncode].equiv != Photcode) continue;
+	if (photcodes.code[Ncode].type != PHOT_DEP) continue;
+	Mag = iPhotRel (&catalog[0].measure[m], &catalog[0].average[i],  &catalog[0].secfilt[i*PhotNsec]);
+	if (fabs(Mag - stats.mean) > Nsigma*stats.sigma) catalog[0].measure[m].flags |= FLAG_SIGCLIP;
+      }
+    }      
+  
+    BAD_MEASURE = FLAG_AREA | FLAG_MINST | FLAG_DMCAL | FLAG_SIGCLIP;
+    /* flag variables */
+    for (i = 0; i < catalog[0].Naverage; i++) {
+      /* extract good measures, calculate chisq */
+      N = 0;
+      m = catalog[0].average[i].offset;
+      for (j = 0; j < catalog[0].average[i].Nm; j++, m++) {
+	if ((Ncode = photcodes.hashcode[catalog[0].measure[m].photcode]) < 0) continue;
+	if (photcodes.code[Ncode].equiv != Photcode) continue;
+	if (photcodes.code[Ncode].type != PHOT_DEP) continue;
+	if (catalog[0].measure[m].flags & BAD_MEASURE) continue;
+	list[N] = iPhotRel (&catalog[0].measure[m], &catalog[0].average[i],  &catalog[0].secfilt[i*PhotNsec]);
+	dlist[N] = sqrt (SQ(DMGAIN*catalog[0].measure[m].dM) + DMSYS);
+	N++;
+      }
+      liststats (list, dlist, N, &stats);
+      if (stats.chisq < CHISQ_MAX) continue;
+
+      /* flag bad measures */
+      m = catalog[0].average[i].offset;
+      for (j = 0; j < catalog[0].average[i].Nm; j++, m++) {
+	if ((Ncode = photcodes.hashcode[catalog[0].measure[m].photcode]) < 0) continue;
+	if (photcodes.code[Ncode].equiv != Photcode) continue;
+	if (photcodes.code[Ncode].type != PHOT_DEP) continue;
+	catalog[0].measure[m].flags |= FLAG_CHISQ;
+      }
+    }	
+
+    /* flag too few valid measures */
+    BAD_MEASURE = FLAG_AREA | FLAG_MINST | FLAG_DMCAL | FLAG_SIGCLIP | FLAG_CHISQ;
+    for (i = 0; i < catalog[0].Naverage; i++) {
+      N = 0;
+      m = catalog[0].average[i].offset;
+      for (j = 0; j < catalog[0].average[i].Nm; j++, m++) {
+	if ((Ncode = photcodes.hashcode[catalog[0].measure[m].photcode]) < 0) continue;
+	if (photcodes.code[Ncode].equiv != Photcode) continue;
+	if (photcodes.code[Ncode].type != PHOT_DEP) continue;
+	if (catalog[0].measure[m].flags & BAD_MEASURE) continue;
+	N++;
+      }
+      if (N == 0) continue; // only flag objects with valid measurements, but too few
+      if (N >= NMEAS_MIN) continue;
+      m = catalog[0].average[i].offset;
+      for (j = 0; j < catalog[0].average[i].Nm; j++, m++) {
+	if ((Ncode = photcodes.hashcode[catalog[0].measure[m].photcode]) < 0) continue;
+	if (photcodes.code[Ncode].equiv != Photcode) continue;
+	if (photcodes.code[Ncode].type != PHOT_DEP) continue;
+	catalog[0].measure[m].flags |= FLAG_TOOFEW;
+      }
+    }	
+  }
+}
+
+
+/* notes
+   
+   flag by area: uses an inefficient search for the image.  
+   images should be sorted by time and we should use bisection
+   
+   number of loops over Nmeasure: 9
+
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/get_mags.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/get_mags.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/get_mags.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "photdbc.h"
+
+void get_mags (Catalog *catalog) {
+
+  int i, j, m, n, N;
+  int Nlist, Nsec, Nsecfilt, Ncode;
+  short int *Mval;
+  int Photcode, BAD_MEASURE;
+  double *list, *dlist;
+  StatType stats;
+
+  if (VERBOSE) fprintf (stderr, "re-calculating photometry\n");
+
+  BAD_MEASURE = FLAG_AREA | FLAG_MINST | FLAG_SIGCLIP | FLAG_CHISQ | FLAG_TOOFEW;
+  Nsecfilt = catalog[0].Nsecfilt;
+
+  /* allocate a list for temp storage of mag values */
+  Nlist = 0;
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    Nlist = MAX (Nlist, catalog[0].average[i].Nm);
+    for (j = 0; j < Nsecfilt; j++) {
+      catalog[0].secfilt[i*Nsecfilt+j].M  = NAN;
+      catalog[0].secfilt[i*Nsecfilt+j].dM = NAN;
+      catalog[0].secfilt[i*Nsecfilt+j].Xm = NAN_S_SHORT;
+    }
+  }
+  ALLOCATE (list, double, Nlist);
+  ALLOCATE (dlist, double, Nlist);
+  initstats ("WT_MEAN");
+
+  /* check on photcode */
+  for (n = 0; n < photcodes.Ncode; n++) {
+    if (photcodes.code[n].type == PHOT_DEP) continue;
+    if (photcodes.code[n].type == PHOT_REF) continue;
+    Photcode = photcodes.code[n].code;
+    Nsec = photcodes.hashNsec[Photcode];
+
+    /* find average magnitudes */
+    for (i = 0; i < catalog[0].Naverage; i++) {
+      N = 0;
+      m = catalog[0].average[i].offset;
+      for (j = 0; j < catalog[0].average[i].Nm; j++, m++) {
+	if ((Ncode = photcodes.hashcode[catalog[0].measure[m].photcode]) < 0) continue;
+	if (photcodes.code[Ncode].equiv != Photcode) continue;
+	if (photcodes.code[Ncode].type != PHOT_DEP) continue;
+	if (catalog[0].measure[m].flags & BAD_MEASURE) continue;
+	list[N] = PhotRel (&catalog[0].measure[m], &catalog[0].average[i],  &catalog[0].secfilt[i*Nsecfilt]);
+	dlist[N] = sqrt (SQ(DMGAIN*catalog[0].measure[m].dM) + DMSYS);
+	N++;
+      }
+      liststats (list, dlist, N, &stats);
+      if (N < 1) continue;
+    
+      Mval = (Nsec == -1) ? &catalog[0].average[i].M : &catalog[0].secfilt[i*Nsecfilt+Nsec].M;
+      *Mval = stats.mean;
+      Mval = (Nsec == -1) ? &catalog[0].average[i].dM : &catalog[0].secfilt[i*Nsecfilt+Nsec].dM;
+      *Mval = stats.sigma;
+      Mval = (Nsec == -1) ? &catalog[0].average[i].Xm : &catalog[0].secfilt[i*Nsecfilt+Nsec].Xm;
+      *Mval = 100.0*log10(stats.chisq);
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/initialize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/initialize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/initialize.c	(revision 22322)
@@ -0,0 +1,35 @@
+# include "photdbc.h"
+
+void initialize (int argc, char **argv) {
+
+  /* are these set correctly? */
+  ConfigInit (&argc, argv);
+  args (argc, argv);
+
+  if (SHOW_PARAMS) {
+    fprintf (stderr, "current parameter settings:\n");
+    fprintf (stderr, "VERBOSE:                %d\n", VERBOSE);
+    fprintf (stderr, "JOIN_RADIUS:           %lf\n", JOIN_RADIUS);
+    fprintf (stderr, "UNIQ_RADIUS:           %lf\n", UNIQ_RADIUS);
+						     
+    fprintf (stderr, "XMIN:                  %lf\n", XMIN);
+    fprintf (stderr, "XMAX:                  %lf\n", XMAX);
+    fprintf (stderr, "YMIN:                  %lf\n", YMIN);
+    fprintf (stderr, "YMAX:                  %lf\n", YMAX);
+    fprintf (stderr, "MMIN:                  %lf\n", MMIN);
+    fprintf (stderr, "MMAX:                  %lf\n", MMAX);
+    fprintf (stderr, "DMCAL_MIN:             %lf\n", DMCAL_MIN);
+    fprintf (stderr, "DMSYS:                 %lf\n", DMSYS);
+						     
+    fprintf (stderr, "CHISQ_MAX:             %lf\n", CHISQ_MAX);
+    fprintf (stderr, "NMEAS_MIN:             %d\n",  NMEAS_MIN);
+
+    fprintf (stderr, "IMAGE_CATALOG          %s\n",  ImageCat);
+    fprintf (stderr, "GSCFILE                %s\n",  GSCFILE);
+    fprintf (stderr, "CATDIR                 %s\n",  CATDIR);
+    fprintf (stderr, "PHOTCODE_FILE          %s\n",  PhotCodeFile);
+
+    exit (0);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/join_stars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/join_stars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/join_stars.c	(revision 22322)
@@ -0,0 +1,243 @@
+# include "photdbc.h"
+
+void join_stars (Catalog *catalog) {
+
+  int i, j, k, m, M, Ni, Nj, first_j;
+  int Naves, Nmeas, Ncurr;
+  int Naverage, Nmeasure, *found, *index;
+  double *X, *Y, dX, dY, dR, RADIUS2;
+  double Sr, Sd, Rmid, Dmid;
+  int basecode, baseNsec, Nsecfilt, Nfirst;
+
+  Average *naverage, *average;
+  Measure *nmeasure, *measure;
+  SecFilt *nsecfilt, *secfilt;
+  Mpointer *mpointer;
+  Coords tcoords;
+
+  if (VERBOSE) fprintf (stderr, "joining overlapping stars\n");
+  if (VERBOSE) fprintf (stderr, "require base star to have i-band\n");
+
+  basecode = GetPhotcodeCodebyName ("i");
+  baseNsec = GetPhotcodeNsec (basecode);
+  Nsecfilt = catalog[0].Nsecfilt;
+
+  average = catalog[0].average;
+  measure = catalog[0].measure;
+  secfilt = catalog[0].secfilt;
+
+  Naverage = catalog[0].Naverage;
+  Nmeasure = catalog[0].Nmeasure;
+  RADIUS2 = JOIN_RADIUS*JOIN_RADIUS;
+
+  /* reference for coords is this region */
+  Naves = 0;
+  Rmid = Dmid = 0;
+  for (i = 0; i < Naverage; i++) {
+    // XXX we should be vigilant against R,D becoming nan : this must be due to pm...
+    if (isnan(average[i].R) || isnan(average[i].D)) {
+      average[i].R = 0.0;
+      average[i].D = 0.0;
+      continue;
+    }
+    Rmid += average[i].R;
+    Dmid += average[i].D;
+    Naves ++;
+  }
+  Rmid /= Naverage;
+  Dmid /= Naverage;
+
+  /* coordinate system for projection */
+  tcoords.crval1 = Rmid;
+  tcoords.crval2 = Dmid;
+  tcoords.crpix1 = tcoords.crpix2 = 0.0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1  = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2  = tcoords.pc2_1 = 0.0;
+  tcoords.Npolyterms = 0;
+  strcpy (tcoords.ctype, "RA---TAN");
+  
+  /* project & sort coordinates in local linear frame */
+  ALLOCATE (X, double, Naverage);
+  ALLOCATE (Y, double, Naverage);
+  ALLOCATE (index, int, Naverage);
+  for (i = 0; i < Naverage; i++) {
+    index[i] = i;
+    RD_to_XY (&X[i], &Y[i], average[i].R, average[i].D, &tcoords);
+  }
+  sort_coords_index (X, Y, index, Naverage);
+  
+  /* flags to mark if a star has been handled */
+  ALLOCATE (found, int, Naverage);
+  for (i = 0; i < Naverage; i++) found[i] = FALSE;
+
+  ALLOCATE (naverage, Average, Naverage);
+  ALLOCATE (nsecfilt, SecFilt, Naverage*Nsecfilt);
+  ALLOCATE (mpointer, Mpointer, Nmeasure);
+  for (i = 0; i < Nmeasure; i++) mpointer[i].averef = -1;
+
+  Naves =  0; // counter for new averages
+  Nmeas =  0; // counter for new measures
+  for (i = j = 0; (i < Naverage) && (j < Naverage); ) {
+    
+    Ni = index[i];
+    Nj = index[j];
+
+    // if ((average[Ni].R > 131.259) && (average[Ni].R < 131.267) && (average[Ni].D > 20.440) && (average[Ni].D < 20.450)) {
+    // fprintf (stderr, "outer: %f, %f - %f, %f (%f, %f) == (%f, %f)\n", average[Ni].R, average[Ni].D, average[Nj].R, average[Nj].D, 
+    // 3600.0*(average[Ni].R - average[Nj].R), 3600.0*(average[Ni].D - average[Nj].D), X[i] - X[j], Y[i] - Y[j]);
+    // }
+
+    // require base star to meet certain conditions:
+    if (isnan(secfilt[Ni*Nsecfilt + baseNsec].M)) {
+      i++;
+      continue;
+    }
+
+    /* a new star, add it to naverage[] */
+    if (!found[i]) { 	
+      naverage[Naves]               = average[Ni];
+      naverage[Naves].measureOffset = Nmeas;
+
+      for (k = 0; k < Nsecfilt; k++) {
+	nsecfilt[Naves*Nsecfilt + k] = secfilt[Ni*Nsecfilt + k];
+      }
+
+      for (k = 0; k < average[Ni].Nmeasure; k++) {
+	m = average[Ni].measureOffset + k;
+	mpointer[Nmeas].measure = m;
+	mpointer[Nmeas].averef  = Naves;
+	mpointer[Nmeas].R       = average[Ni].R - measure[m].dR / 3600.0;
+	mpointer[Nmeas].D       = average[Ni].D - measure[m].dD / 3600.0;
+	Nmeas ++;
+      }
+      Ncurr = Naves;
+      found[i] = TRUE;
+      Naves ++;
+    }
+
+    if (found[j]) { j++; continue; }  // don't duplicate
+    if (j == i)   { j++; continue; }  // don't auto-correlate
+
+    dX = X[i] - X[j];
+    if (dX <= -JOIN_RADIUS) { /* X[j] is too large */
+      while (found[i] && (i < Naverage)) i++;
+      continue;
+    }
+    if (dX >= +JOIN_RADIUS) { /* X[i] is too large */
+      j++;
+      continue;
+    }
+
+    first_j = j;
+    for (; (dX > -2*JOIN_RADIUS) && (j < Naverage); j++) {
+      Nj = index[j];
+
+      // if ((average[Ni].R > 131.259) && (average[Ni].R < 131.267) && (average[Ni].D > 20.440) && (average[Ni].D < 20.450)) {
+      // fprintf (stderr, "inner: %f, %f - %f, %f (%f, %f) == (%f, %f)\n", average[Ni].R, average[Ni].D, average[Nj].R, average[Nj].D, 
+      // 3600.0*(average[Ni].R - average[Nj].R), 3600.0*(average[Ni].D - average[Nj].D), X[i] - X[j], Y[i] - Y[j]);
+      // }
+
+      if (found[j]) continue;
+      dX = X[i] - X[j];
+      dY = Y[i] - Y[j];
+      dR = dX*dX + dY*dY;
+      if (dR < RADIUS2) {  /* matched star, join to first */
+
+	// if ((average[Ni].R > 131.259) && (average[Ni].R < 131.267) && (average[Ni].D > 20.440) && (average[Ni].D < 20.450)) {
+	// fprintf (stderr, "match: %f, %f - %f, %f\n", average[Ni].R, average[Ni].D, average[Nj].R, average[Nj].D);
+	// }
+
+	/* define pointers for new measures */
+	for (k = 0; k < average[Nj].Nmeasure; k++) {
+	  m = average[Nj].measureOffset + k;
+	  mpointer[Nmeas].measure = m;
+	  mpointer[Nmeas].averef  = Ncurr;
+	  mpointer[Nmeas].R       = average[Nj].R - measure[m].dR / 3600.0;
+	  mpointer[Nmeas].D       = average[Nj].D - measure[m].dD / 3600.0;
+	  Nmeas ++;
+	}
+	Nfirst = average[Nj].Nmeasure;
+	naverage[Ncurr].Nmeasure += average[Nj].Nmeasure;
+	found[j] = TRUE;
+
+# if 0
+	/* recalculate naverage[Ncurr].RA,DEC */
+	/* this must be done here to keep the average position consistent
+	   for the next star found */
+	for (Sr = Sd = k = 0; k < naverage[Ncurr].Nmeasure; k++) {
+	  m = naverage[Ncurr].measureOffset + k;
+	  Sr += mpointer[m].R;
+	  Sd += mpointer[m].D;
+	}
+	Sr = Sr / naverage[Ncurr].Nmeasure;
+	Sd = Sd / naverage[Ncurr].Nmeasure;
+	naverage[Ncurr].R = Sr;
+	naverage[Ncurr].D = Sd;
+
+	/* update original measurement offsets for new detections */
+	for (k = Nfirst; k < naverage[Ncurr].Nmeasure; k++) {
+	  m = naverage[Ncurr].measureOffset + k;
+	  M = mpointer[m].measure;
+	  measure[M].dR = 3600.0*(Sr - mpointer[m].R);
+	  measure[M].dD = 3600.0*(Sd - mpointer[m].D);
+	}
+
+	/* update current reference star position */
+	RD_to_XY (&X[i], &Y[i], Sr, Sd, &tcoords);
+# else
+	/* update original measurement offsets for new detections */
+	for (k = Nfirst; k < naverage[Ncurr].Nmeasure; k++) {
+	  m = naverage[Ncurr].measureOffset + k;
+	  M = mpointer[m].measure;
+	  measure[M].dR = 3600.0*(naverage[Ncurr].R - mpointer[m].R);
+	  measure[M].dD = 3600.0*(naverage[Ncurr].D - mpointer[m].D);
+	}
+# endif
+      }
+    }
+    while (found[i] && (i < Naverage)) i++;
+    j = first_j;
+  }
+
+  if (Nmeas != Nmeasure) {
+    fprintf (stderr, "failure to match %d measures (%d of %d matched)\n", Nmeasure - Nmeas, Nmeas, Nmeasure);
+  }
+  
+  /* create a new Measure table in the appropriate sequence */
+  ALLOCATE (nmeasure, Measure, Nmeas);
+  for (i = 0; i < Nmeas; i++) {
+    nmeasure[i]        = measure[mpointer[i].measure];
+    nmeasure[i].averef = mpointer[i].averef;
+  }
+
+  /* create an empty SecFilt table */
+  // ALLOCATE (nsecfilt, SecFilt, Naves*catalog[0].Nsecfilt);
+  // bzero (nsecfilt, Naves*catalog[0].Nsecfilt*sizeof(SecFilt));
+
+  free (catalog[0].average);
+  free (catalog[0].measure);
+  free (catalog[0].secfilt);
+
+  catalog[0].average = naverage;
+  catalog[0].measure = nmeasure;
+  catalog[0].secfilt = nsecfilt;
+  
+  // allow output catalog to retain fewer measures
+  catalog[0].Naverage = Naves;
+  catalog[0].Nmeasure = Nmeas;
+  catalog[0].Nsecf_mem = Naves*catalog[0].Nsecfilt;
+  
+  return;
+}
+
+/* notes:
+   merge averages of stars within RADIUS to single stars 
+   updates the average RA and DEC fields
+   creates a new Average table 'naverage' 
+   updates the Measure table to the new sequence
+   generates a new SecFilt table with empty values 
+   does NOT update magnitudes 
+   output measure table is the same size as the input measure table
+   (measures are just moved around).  thus, catalog[0].Nmeasure does not change.
+*/   
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/liststats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/liststats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/liststats.c	(revision 22322)
@@ -0,0 +1,114 @@
+# include "photdbc.h"
+
+enum {M_MEAN, M_MEDIAN, M_WT_MEAN, M_INNER_MEAN, 
+      M_INNER_WTMEAN, M_CHI_INNER_MEAN, M_CHI_INNER_WTMEAN};
+
+static int statmode;
+
+void initstats (char *mode) {
+
+  statmode = -1;
+  if (!strcmp (mode, "MEAN")) statmode = M_MEAN;
+  if (!strcmp (mode, "MEDIAN")) statmode = M_MEDIAN;
+  if (!strcmp (mode, "WT_MEAN")) statmode = M_WT_MEAN;
+  if (!strcmp (mode, "INNER_MEAN")) statmode = M_INNER_MEAN;
+  if (!strcmp (mode, "INNER_WTMEAN")) statmode = M_INNER_WTMEAN;
+  if (!strcmp (mode, "CHI_INNER_MEAN")) statmode = M_CHI_INNER_MEAN;
+  if (!strcmp (mode, "CHI_INNER_WTMEAN")) statmode = M_CHI_INNER_WTMEAN;
+
+  if (statmode == -1) {
+    fprintf (stderr, "ERROR: invalid stats mode: %s\n", mode);
+    exit (1);
+  }
+}
+
+int liststats (double *value, double *dvalue, int N, StatType *stats) {
+  
+  int i, ks, ke, Nm;
+  double Mo, dMo, M, dM, X2, dS, *chi;
+
+  stats[0].Nmeas = N;
+  stats[0].mean  = 0;
+  if (N < 2) return (FALSE);
+
+  dsortpair (value, dvalue, N);
+  stats[0].median = value[(int)(0.5*N)];
+  stats[0].min    = value[0];
+  stats[0].max    = value[N-1];
+
+  switch (statmode) {
+  case M_MEDIAN:
+    ks = 0;
+    ke = N;
+    Mo = stats[0].median;
+    Nm = N;
+    goto chisq;
+    break;
+  case M_MEAN:
+  case M_WT_MEAN:
+    ks = 0;
+    ke = N;
+    break;
+  case M_INNER_MEAN:
+  case M_INNER_WTMEAN:
+  case M_CHI_INNER_MEAN:
+  case M_CHI_INNER_WTMEAN:
+    ks = 0.25*N + 0.50;
+    ke = 0.75*N + 0.25;
+    if (N <= 3) {
+      ks = 0;
+      ke = N;
+    }
+    break;
+  }    
+
+  if ((statmode == M_CHI_INNER_MEAN) || (statmode == M_CHI_INNER_WTMEAN)) {
+    ALLOCATE (chi, double, N);
+    for (i = 0; i < N; i++) {
+      chi[i] = (value[i] - stats[0].median) / dvalue[i];
+    }
+    dsortthree (chi, value, dvalue, N);
+    free (chi);
+  }
+
+  /* calculating the per-star offset based on the weighted average */
+  M = dM = Nm = 0;
+  if ((statmode == M_WT_MEAN) || (statmode == M_INNER_WTMEAN) || (statmode == M_CHI_INNER_WTMEAN)) {
+    for (i = ks; i < ke; i++) {
+      M   += value[i] / SQ (dvalue[i]);
+      dM  += 1.0 / SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / dM;
+    dMo = sqrt (1.0 / dM);
+  } else {
+    for (i = ks; i < ke; i++) {
+      M   += value[i];
+      dM  += SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / (double) Nm;
+    dMo = sqrt (dM / (double) Nm);
+  }
+
+ chisq:
+  /* find sigma and chisq */
+  X2 = dS = 0;
+  for (i = ks; i < ke; i++) {
+    M  = SQ (value[i] - Mo);
+    dM = SQ (dvalue[i]);
+    X2 += M / dM;
+    dS += M;
+  }
+  X2 = X2 / Nm;
+  dS = sqrt (dS / Nm);
+
+  stats[0].mean  = Mo;
+  stats[0].Nmeas = Nm;
+  stats[0].chisq = X2;
+  stats[0].sigma = dS;
+  stats[0].error = dMo;
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/make_subcatalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/make_subcatalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/make_subcatalog.c	(revision 22322)
@@ -0,0 +1,111 @@
+# include "photdbc.h"
+
+// copy a catalog to a new subcatalog, applying some filters
+// the supplied subcatalog must already be locked, opened, and created
+int make_subcatalog (Catalog *subcatalog, Catalog *catalog) {
+  
+  int i, j, offset;
+  int NAVERAGE, NMEASURE, Naverage, Nmeasure, Nm, Nsecfilt;
+  double mag, minMag;
+
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  assert (catalog[0].Nsecfilt == Nsecfilt);
+
+  /* we are moving only the subset of measurements from catalog[0] to subcatalog[0] */
+  NAVERAGE = 50;
+  NMEASURE = 1000;
+  Nmeasure = Naverage = 0;
+  REALLOCATE (subcatalog[0].average, Average, NAVERAGE);
+  REALLOCATE (subcatalog[0].secfilt, SecFilt, NAVERAGE*Nsecfilt);
+  REALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
+
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    // perform exclusions based on average properties
+
+    // exclude stars with too few measurements
+    if (NMEAS_MIN && (catalog[0].average[i].Nmeasure < NMEAS_MIN)) continue; 
+
+    /* assign average and secfilt values */
+    subcatalog[0].average[Naverage] = catalog[0].average[i];
+    subcatalog[0].average[Naverage].measureOffset = Nmeasure;
+    for (j = 0; j < Nsecfilt; j++) {
+      subcatalog[0].secfilt[Nsecfilt*Naverage+j] = catalog[0].secfilt[Nsecfilt*i+j];
+    }
+
+    minMag = 32;
+    Nm = 0;
+    for (j = 0; j < catalog[0].average[i].Nmeasure; j++) {
+
+      offset = catalog[0].average[i].measureOffset + j;
+
+      # if 0
+      if (DropPhotcode) {
+	ecode = GetPhotcodeEquivCodebyCode (catalog[0].measure[offset].photcode);
+	if (ecode == photcode[0].code) continue;
+      }
+      if (DropNonStellar && catalog[0].measure[offset].dophot != 1) continue;
+      # endif
+
+      /* exclude measurements by measurement error */
+      if (SIGMA_MAX && (catalog[0].measure[offset].dM > SIGMA_MAX)) continue;
+
+      /* select measurements by mag limit */
+      if (ExcludeByInstMag) {
+	mag = PhotInst (&catalog[0].measure[offset]);
+	if (mag < INST_MAG_MIN) continue;
+	if (mag > INST_MAG_MAX) continue;
+      }
+
+      if (ExcludeByMaxMinMag) {
+	mag = PhotSys (&catalog[0].measure[offset], &catalog[0].average[i], &catalog[0].secfilt[i*Nsecfilt]);
+	minMag = MIN (minMag, mag);
+      }
+
+      subcatalog[0].measure[Nmeasure]        = catalog[0].measure[offset];
+      subcatalog[0].measure[Nmeasure].averef = Naverage;
+      Nmeasure ++;
+      Nm ++;
+      if (Nmeasure == NMEASURE) {
+	NMEASURE += 1000;
+	REALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
+      }
+    }
+
+    // exclude faint objects
+    if (ExcludeByMaxMinMag && (minMag > MAX_MIN_MAG)) {
+      Nmeasure -= Nm;
+      continue; 
+    }
+
+    // after measurement exclusion, exclude stars with too few measurements
+    if (Nm < NMEAS_MIN) {
+      Nmeasure -= Nm;
+      continue; 
+    }
+    subcatalog[0].average[Naverage].Nmissing = 0;
+    subcatalog[0].average[Naverage].Nmeasure = Nm;
+    Naverage ++;
+    if (Naverage == NAVERAGE) {
+      NAVERAGE += 50;
+      REALLOCATE (subcatalog[0].average, Average, NAVERAGE);
+      REALLOCATE (subcatalog[0].secfilt, SecFilt, NAVERAGE*Nsecfilt);
+    }
+  }
+  REALLOCATE (subcatalog[0].average, Average, MAX (Naverage, 1));
+  REALLOCATE (subcatalog[0].measure, Measure, MAX (Nmeasure, 1));
+  REALLOCATE (subcatalog[0].secfilt, SecFilt, Nsecfilt*MAX (Naverage, 1));
+  subcatalog[0].Naverage = Naverage;
+  subcatalog[0].Nmeasure = Nmeasure;
+  subcatalog[0].Nsecfilt = Nsecfilt;
+  subcatalog[0].Nsecf_mem = Naverage * Nsecfilt;
+
+  // XXX for now, don't copy the missing entries (these should be re-computed)
+  ALLOCATE (subcatalog[0].missing, Missing, 1);
+  subcatalog[0].Nmissing = 0;
+
+  if (VERBOSE) {
+    fprintf (stderr, "%d: using %d stars (%d measures) for catalog\n", i, 
+	     subcatalog[0].Naverage, subcatalog[0].Nmeasure);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/misc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/misc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/misc.c	(revision 22322)
@@ -0,0 +1,16 @@
+# include "photdbc.h"
+
+void sort_time (unsigned int *X, int *Y, int N) {
+
+# define SWAPFUNC(A,B){ float tmp; \
+  unsigned int utmp = X[A]; X[A] = X[B]; X[B] = utmp; \
+  int          itmp = Y[A]; Y[A] = Y[B]; Y[B] = itmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/overlap_funcs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/overlap_funcs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/overlap_funcs.c	(revision 22322)
@@ -0,0 +1,78 @@
+# include "photdbc.h"
+
+/* check if line between points 0 and 1 of x1
+   crosses line between points 0 and 1 of x2 */
+int edge_check (double *x1, double *y1, double *x2, double *y2) {
+
+  double theta1, theta2;
+  double Theta1, Theta2;
+
+  theta1 = opening_angle (x1[0], y1[0], x2[0], y2[0], x1[1], y1[1]); 
+  theta2 = opening_angle (x1[0], y1[0], x2[0], y2[0], x2[1], y2[1]); 
+
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  Theta1 = theta1;
+  Theta2 = theta2;
+  theta1 = opening_angle (x2[0], y2[0], x1[1], y1[1], x2[1], y2[1]); 
+  theta2 = opening_angle (x2[0], y2[0], x1[1], y1[1], x1[0], y1[0]); 
+  
+ 
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+
+/* check if point x1,y1 is in box formed by x2[0-4] */
+int corner_check (double *x1, double *y1, double *x2, double *y2) {
+
+  int i;
+  double theta;
+
+  theta = 0;
+
+  for (i = 0; i < 4; i++) {
+    theta += opening_angle (x2[i], y2[i], x1[0], y1[0], x2[i+1], y2[i+1]); 
+  }
+  if (fabs(theta) > 6) {
+    return (TRUE);
+  } else {
+    return (FALSE);
+  }
+}
+
+/* returns the opening angle between the three points (2 is in middle) 
+   in range -pi to pi */
+
+double opening_angle (double x1, double y1, double x2, double y2, double x3, double y3) {
+
+  double dx1, dy1, dx2, dy2, ct, st, theta;
+
+  dx1 = x1 - x2;
+  dy1 = y1 - y2;
+  
+  dx2 = x3 - x2;
+  dy2 = y3 - y2;
+  
+  ct = (dx1*dx2 + dy1*dy2);
+  st = (dx1*dy2 - dx2*dy1);
+
+  theta = atan2 (st, ct);
+
+  return (theta);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/photdbc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/photdbc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/photdbc.c	(revision 22322)
@@ -0,0 +1,79 @@
+# include "photdbc.h"
+
+int main (int argc, char **argv) {
+
+  int i;
+  char *skyfile;
+  Catalog incatalog;
+  Catalog outcatalog;
+  SkyTable *sky;
+  SkyList *skylist;
+
+  /* get configuration info, args, lockfile */
+  initialize (argc, argv);
+
+  // load and copy the image table
+  copy_images (argv[1]);
+
+  // the output catalog needs to inherit the SKY_DEPTH of the input catalog
+  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  skylist = SkyListByPatch (sky, -1, &REGION);
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    if (i % 100 == 0) fprintf (stderr, "%s\n", skylist[0].regions[i][0].name);
+
+    // set the parameters which guide catalog open/load/create
+    incatalog.filename = skylist[0].filename[i];
+    incatalog.Nsecfilt = GetPhotcodeNsecfilt ();
+    incatalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&incatalog, skylist[0].regions[i], VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", incatalog.filename);
+      exit (2);
+    }
+    // skip empty input catalogs
+    if (!incatalog.Naves_disk) {
+      dvo_catalog_unlock (&incatalog);
+      dvo_catalog_free (&incatalog);
+      continue;
+    }
+
+    // create output catalog filename
+    outcatalog.filename = strsubs (incatalog.filename, CATDIR, argv[1]);
+    if (outcatalog.filename == NULL) Shutdown ("error with input catalog name");
+
+    // define outcatalog open parameters
+    outcatalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    outcatalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    outcatalog.Nsecfilt  = incatalog.Nsecfilt;                 // inherit from the incatalog
+    outcatalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+
+    // output catalogs always represent the same skyregions as the input catalogs
+    if (!dvo_catalog_open (&outcatalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", outcatalog.filename);
+      exit (2);
+    }
+
+    /* limit number of measures based on selections */
+    make_subcatalog (&outcatalog, &incatalog);
+	
+    // XXX add other filters here:
+    join_stars (&outcatalog);
+    // unique_measures (catalog);
+    // flag_measures (&db, catalog);
+    // get_mags (catalog);
+
+    dvo_catalog_save (&outcatalog, VERBOSE);
+    dvo_catalog_unlock (&outcatalog);
+    dvo_catalog_free (&outcatalog);
+
+    dvo_catalog_unlock (&incatalog);
+    dvo_catalog_free (&incatalog);
+  }
+
+  ALLOCATE (skyfile, char, strlen(argv[1]) + strlen("/SkyTable.fits") + 1);
+  sprintf (skyfile, "%s/SkyTable.fits", argv[1]);
+  SkyTableSave (sky, skyfile);
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/unique_measures.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/unique_measures.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/photdbc/src/unique_measures.c	(revision 22322)
@@ -0,0 +1,110 @@
+# include "photdbc.h"
+
+void unique_measures (Catalog *catalog) {
+
+  int i, j, k, m, N;
+  int No, Nm, Nvalid, Nlist;
+  int *ilist;
+  unsigned int *tlist;
+  Measure *mlist, *nmeasure;
+  double dR, dD, R, RADIUS2;
+  Average *p;
+
+  if (VERBOSE) fprintf (stderr, "removing duplicate measurements\n");
+
+  /* allocate a list for temp storage of measure values */
+  Nlist = 0;
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    Nlist = MAX (Nlist, catalog[0].average[i].Nm);
+  }
+  ALLOCATE (ilist, int, Nlist);
+  ALLOCATE (tlist, unsigned int, Nlist);
+  ALLOCATE (mlist, Measure, Nlist);
+
+  RADIUS2 = SQ(UNIQ_RADIUS);
+  
+  p = catalog[0].average;
+  for (i = 0; i < catalog[0].Naverage; i++) {
+
+    if (catalog[0].average != p) {
+      fprintf (stderr, "error\n");
+    }
+
+    /* extract list of measurements */
+    N = 0;
+    m = catalog[0].average[i].offset;
+    for (j = 0; j < catalog[0].average[i].Nm; j++, m++) {
+      ilist[N] = m;
+      tlist[N] = catalog[0].measure[m].t;
+      N++;
+    }
+
+    /* sort the images by time */
+    sort_time (tlist, ilist, N);
+
+    /* look for duplicates */
+    for (j = k = 0; (j < N) && (k < N); j++) {
+      if (j == k) {
+	k++;
+	continue;
+      }
+      if (tlist[j] != tlist[k]) {
+	j++;
+	k = j + 1;
+	continue;
+      }
+      if (catalog[0].measure[ilist[j]].flags & FLAG_DUPMEAS) {
+	j++;
+	k = j + 1;
+	continue;
+      }
+      
+      dR = (catalog[0].measure[ilist[j]].dR - catalog[0].measure[ilist[k]].dR);
+      dD = (catalog[0].measure[ilist[j]].dD - catalog[0].measure[ilist[k]].dD);
+      R = SQ(dR) + SQ(dD);
+      
+      /* dR, dD in arcsec, RADIUS is 0.1 arcsec */
+      if (R < RADIUS2) {
+	/* matched pair */
+	catalog[0].measure[ilist[k]].flags |= FLAG_DUPMEAS;
+      }
+      k++;
+    }
+  }
+
+  Nvalid = 0;
+  for (i = 0; i < catalog[0].Nmeasure; i++) {
+    if (!(catalog[0].measure[i].flags & FLAG_DUPMEAS)) Nvalid ++;
+  }
+
+  No = 0;
+  ALLOCATE (nmeasure, Measure, MAX (Nvalid, 1));
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    /* copy valid measures to new table, update offset, Nm */
+    Nm = 0;
+    m = catalog[0].average[i].offset;
+    catalog[0].average[i].offset = No;
+    for (j = 0; j < catalog[0].average[i].Nm; j++, m++, No++) {
+      if (catalog[0].measure[m].flags & FLAG_DUPMEAS) continue;
+      nmeasure[No] = catalog[0].measure[m];
+      if (catalog[0].measure[m].averef != i) { 
+	fprintf (stderr, "average reference mismatch!!\n");
+      }
+      Nm ++;
+    }
+    catalog[0].average[i].Nm = Nm;
+  }
+
+  free (catalog[0].measure);
+  catalog[0].measure = nmeasure;
+  catalog[0].Nmeasure = No;
+
+}
+
+/*** XXX 
+     this function does not quite do the right thing.  it should remove duplicate measurements 
+     for a single average object.  unique measurements have a unique combination of time and photcode
+     (IS this true?  only for DET, not for REF...)
+***/
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/Makefile	(revision 22322)
@@ -0,0 +1,60 @@
+default: relastro
+help:
+	@echo "make options: relastro (default)"
+
+include ../../Makefile.System
+HOME    =       $(ROOT)/src/relastro
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+relastro: $(BIN)/relastro.$(ARCH)
+install: $(DESTBIN)/relastro
+
+RELASTRO = \
+$(SRC)/ConfigInit.$(ARCH).o	     \
+$(SRC)/FitChip.$(ARCH).o             \
+$(SRC)/FitMosaic.$(ARCH).o           \
+$(SRC)/FitPM.$(ARCH).o               \
+$(SRC)/FitPar.$(ARCH).o              \
+$(SRC)/FitPMandPar.$(ARCH).o         \
+$(SRC)/FitSimple.$(ARCH).o           \
+$(SRC)/ImageOps.$(ARCH).o	     \
+$(SRC)/MosaicOps.$(ARCH).o	     \
+$(SRC)/ParFactor.$(ARCH).o           \
+$(SRC)/SetSignals.$(ARCH).o 	     \
+$(SRC)/Shutdown.$(ARCH).o 	     \
+$(SRC)/UpdateChips.$(ARCH).o         \
+$(SRC)/UpdateMosaic.$(ARCH).o        \
+$(SRC)/UpdateObjects.$(ARCH).o       \
+$(SRC)/UpdateObjectOffsets.$(ARCH).o \
+$(SRC)/UpdateSimple.$(ARCH).o        \
+$(SRC)/UpdateMeasures.$(ARCH).o      \
+$(SRC)/GetAstromError.$(ARCH).o      \
+$(SRC)/args.$(ARCH).o		     \
+$(SRC)/bcatalog.$(ARCH).o	     \
+$(SRC)/dvo_astrom_ops.$(ARCH).o      \
+$(SRC)/fitpoly.$(ARCH).o             \
+$(SRC)/initialize.$(ARCH).o	     \
+$(SRC)/liststats.$(ARCH).o	     \
+$(SRC)/load_catalogs.$(ARCH).o	     \
+$(SRC)/load_images.$(ARCH).o	     \
+$(SRC)/mkpolyterm.$(ARCH).o          \
+$(SRC)/plot_scatter.$(ARCH).o	     \
+$(SRC)/plotstuff.$(ARCH).o	     \
+$(SRC)/select_images.$(ARCH).o	     \
+$(SRC)/relastro.$(ARCH).o	     \
+$(SRC)/relastro_objects.$(ARCH).o    \
+$(SRC)/save_catalogs.$(ARCH).o       \
+$(SRC)/write_coords.$(ARCH).o
+
+$(RELASTRO): $(INC)/relastro.h $(KAPA_INCS)
+$(BIN)/relastro.$(ARCH): $(RELASTRO) $(KAPA_LIBS)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/doc/notes.txt	(revision 22322)
@@ -0,0 +1,124 @@
+
+2008.03.01
+
+  relastro (and relphot for that matter) needs to have a somewhat
+  flexible error model for the detections.  Some data have an accurate
+  astrometric error; some have a reported error, but it is either
+  insufficient (eg, only from poisson error), or inaccurate.  A
+  complete error model would be something like this:
+
+  dR_total^2 =  dR_sys^2 + AS * dR_obs^2 + MS * dM_obs^2
+
+  dR_sys : systematicAstrometryError
+  AS     : astrometryErrorScale
+  MS     : astrometryErrorMagScale
+
+2007.11.12
+
+  relastro is working for the SIMPLE (single-chip) and CHIP
+  (mosaic-chip) modes.  it is not yet working for the MOSAIC mode.
+  The image table contains one mosaic entry and N chip entries for a
+  mosaic with N chips.  The mosaic entry has coord.ctype = DIS, while
+  the corresponding chips have coord.ctype = WRP (and matching
+  time/photcodes).
+
+  (this is fairly weak. we should just define the relationship when we
+  register the chips...)
+
+2007.10.06
+
+  relastro currently loads the set of detections, averages, and image,
+  and sets up arrays of pointers to define the relationships beteen
+  the detections, objects, and images.  This is probably an acceptable
+  scheme.  
+
+  However, the generation of those index arrays is probably very
+  inefficient.  Currently it is written for the old DVO tables in
+  which there was no real link between the detection and image.  We
+  are trying to move to a situation where each detection has a
+  reference to the image (imageID).  If this is unique, then we should
+  be able to make these links directly.
+
+  Outstanding issues:
+
+  * am I getting the correct set of matches for the mosaic images? 
+    ie, do getImageRaw & getImageRef return the correct list when the
+    image is a DIS mosaic image?
+
+  * is the operation in fit_apply_coords doing the correct thing?
+    compare this to psastro code.  This should not be a problem since
+    we are only fitting in the linear frames.  we are just fitting
+    simple polynomials.  We are not changing the reference coordinate
+    for the projection.
+
+  * measure the errors?
+
+  * apply the magnitude errors as weight?
+
+  * improve matching process with the index information available?
+
+  * add the ability to down-weight groups of detections by photcode?
+
+2007.02.11
+
+  relastro major modes:
+
+  - update the astrometry of objects in the images
+    - load objects within a region
+    - foreach object
+      - calculate average R,D
+      - update db tables (dR, dD)
+    - include external trends
+      - parallax factor
+      - atm trends
+
+  - update the astrometric parameters of images
+    - use the average R,D for objects, update image terms
+    * include external refs with adjustable weighting
+
+  - image parameter smoothing
+
+  - simultaneous fit of objects and images
+    - identify parameters to constrain
+    - limited number of internal objects?
+
+
+2006.04.08 : relastro contemplations
+
+relastro will perform the astrometry equivalent to relphot.  For
+region, it loads in the average objects, detections, and image
+parameters.  The goal is to determine improved astrometric positions
+of objects in the images and to determine improved astrometric
+parameters for the images.  Just like relphot, this is an iterative
+process in which the object postions are improved in one step, the
+image parameters are improved in the next, then back to the images.
+
+Relastro needs to deal with outlier objects in a robust way.  There
+are a few reasons why objects should have poor astrometric solutions,
+and these can be flagged up front: saturation and too many bad pixels.
+For detections which come from psphot, we can use the quality
+parameter to exclude objects which have excessively high bad pixel
+counts.  For detections from sextractor or other analysis tools,
+this information is missing.  In this case, we can use the distance
+from the image boundaries as a trigger.
+
+An open question to be addressed is the issue of external calibration
+sources.  Unlike photometry, external astrometric measurements do not
+need to have an unknown filter transformation applied.  They can thus
+be included as part of the solution automatically.  It is not clear,
+however, how to include *proper-motion* data in the analysis.  If I
+introduce, for example, USNO objects with proper motions, the
+information provided consists in practice of a velocity vector and
+errors.  How does this get folded into the fit?  One answer is to
+construct additional artificial datapoints for each entry with the
+proper errors and epochs.  Seems like something of a hack.  
+
+We may want to allow for different errors / weightings for the
+different external sources.
+
+After the initial iterations, we will can solve for proper-motion and
+parallax for (all?) objects
+
+- calculate the parallax normal vector for each image
+- calculate the atmospheric / orbital motion effects for each image
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/doc/parallax.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/doc/parallax.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/doc/parallax.pro	(revision 22322)
@@ -0,0 +1,348 @@
+
+macro sample
+  PMsim 15 30 20 10 $ANGLE
+  set dX = zero(X) + 10
+  set dY = zero(Y) + 10
+
+  style -c black
+  plot -x 2 -pt 2 -sz 2.0 X Y -dx dX -dy dY
+
+  # set x = (1000.0/$dS)*cos(t*$TP) + $vX*t
+  # set y = (1000.0/$dS)*sin(t*$TP)*dsin($ANGLE) + $vY*t
+  # plot -c grey60 -x 2 -sz 2.0 -pt 7 x y
+
+  create t 0 1.5 0.01
+  set x = (1000.0/$dS)*cos(t*$TP) + $vX*t
+  set y = (1000.0/$dS)*sin(t*$TP)*dsin($ANGLE) + $vY*t
+  plot -c grey60 -x 0 x y
+
+  # label -x "offset east (milliarcsec)"
+  # label -y "offset north (milliarcsec)"
+end
+
+macro PMsim
+  if ($0 != 6)
+    echo "USAGE: PMsim Do vX vY dP dT"
+    echo "Do : distance in parsec"
+    echo "vX, vY : velocity (milliarcsec/year)"
+    echo "dP : astrometric scatter (arcsec)"
+    echo "dT : ecliptic lattitude (degrees)"
+    break
+  end
+
+  $TP = 2*3.1416
+
+  $dS = $1
+  $vX = $2
+  $vY = $3
+  $dP = $4
+  $dT = $5
+
+  mkbase
+
+  set x = (1000.0/$dS)*cos(t*$TP) + $vX*t
+  set y = (1000.0/$dS)*sin(t*$TP)*dsin($dT) + $vY*t
+
+  gaussdev dx x[] 0.0 $dP
+  gaussdev dy y[] 0.0 $dP 
+
+  set X = x + dx
+  set Y = y + dy
+
+  if ($PLOT)
+    lim -200 200 -200 200; clear; box;
+    style -c black
+    plot -x 2 -pt 2 -sz 2.0 X Y
+    plot -x 0 X Y
+  end
+
+  PMfit X Y t $dT
+
+  if ($PLOT) 
+    label -x "offset east (milliarcsec)"
+    label -y "offset north (milliarcsec)"
+    section a 0 0 1 1
+    lim 0 1 0 1
+    sprintf line "input: D: %5.1f (pc), V: %5.1f, %5.1f (mas/yr), dS: %4.1f" $1 $2 $3 $4
+    textline -fn courier 14 0.15 0.95 "$line"
+    sprintf line "fit:    D: %5.1f (pc), V: %5.1f, %5.1f (mas/yr)" $dS $vX $vY
+    textline -fn courier 14 0.15 0.90 "$line"
+    section default
+  end
+end
+
+macro PMfit
+  if ($0 != 5)
+    echo "USAGE: PMfit X Y t (theta)"
+    echo "X,Y are offsets in milliarcsec"
+    echo "t is time in years"
+    break
+  end
+
+  $TP = 2*3.1416
+  set x = $1
+  set y = $2
+  set t = $3
+  set cs = cos(t*$TP)
+  set snx = sin(t*$TP)*dsin($4)
+
+  mcreate A 3 3
+  create B 0 3
+
+  # define A values:
+  set tmp = cs^2 + snx^2
+  vstat -q tmp
+  zap A 0 0 1 1 -v $TOTAL
+
+  set tmp = t*cs
+  vstat -q tmp
+  zap A 1 0 1 1 -v $TOTAL
+  zap A 0 1 1 1 -v $TOTAL
+
+  set tmp = t*snx
+  vstat -q tmp
+  zap A 2 0 1 1 -v $TOTAL
+  zap A 0 2 1 1 -v $TOTAL
+
+  set tmp = t^2
+  vstat -q tmp
+  zap A 1 1 1 1 -v $TOTAL
+  zap A 2 2 1 1 -v $TOTAL
+
+  zap A 2 1 1 1 -v 0
+  zap A 1 2 1 1 -v 0
+
+  # define B values:
+  set tmp = x*cs + y*snx
+  vstat -q tmp
+  B[0] = $TOTAL
+
+  set tmp = x*t
+  vstat -q tmp
+  B[1] = $TOTAL
+
+  set tmp = y*t
+  vstat -q tmp
+  B[2] = $TOTAL
+
+  gaussj A B
+
+  $dS = 1000/B[0]
+  $vX = B[1]
+  $vY = B[2]
+
+  if ($PLOT)
+    echo "Do: $dS"
+    echo "vX: $vX"
+    echo "vY: $vY"
+
+    set x = (1000.0/$dS)*cos(t*$TP) + $vX*t
+    set y = (1000.0/$dS)*sin(t*$TP)*dsin($4) + $vY*t
+    plot -c red -x 2 -sz 2.0 -pt 7 x y
+  end
+end
+
+macro PMstats
+  
+  if ($0 != 7)
+    echo "USAGE: PMsim Do vX vY dP dT (Niter)"
+    echo "Do : distance in parsec"
+    echo "vX, vY : velocity (milliarcsec/year)"
+    echo "dP : astrometric scatter (arcsec)"
+    echo "dT : ecliptic lattitude (degrees)"
+    break
+  end
+
+  delete -q vx vy ds
+  $PLOT = 0
+
+  for i 0 $6
+    PMsim $1 $2 $3 $4 $5
+    concat $dS ds
+    concat $vX vx
+    concat $vY vy
+  end
+
+  vstat ds
+  vstat vx
+  vstat vy
+
+  set gm = 1000/ds
+  vstat gm
+
+  clear -s -n 1
+  label -fn courier 14
+  # section a 0.0 0.0 0.5 1.0
+  lim {$2-30} {$2+30} {$3-30} {$3+30}; box
+  plot -x 2 -pt 2 -sz 0.5 vx vy
+  label -x "p.m. (east, mas/yr)"
+  label -y "p.m. (east, mas/yr)"
+
+  clear -s -n 2
+  # section b 0.5 0.0 0.5 1.0
+  histogram gm Ng {1000/$1 - 30.0} {1000/$1 + 30.0} 1.0
+  create dg {1000/$1 - 30.0} {1000/$1 + 30.0} 1.0
+  lim dg Ng; box
+  plot -x 1 dg Ng
+
+  label -x "parallax (mas)"
+  label -y "# of tests"
+
+end
+
+macro PMtrend
+
+  PMstats 10 5.0 5.0 10 90 100
+  echo 10
+  PMstats 10 5.0 5.0 {750/2.38/25} 90 100
+  echo {750/2.38/25} 
+  PMstats 10 5.0 5.0 {750/2.38/10} 90 100
+  echo {750/2.38/10} 
+  PMstats 10 5.0 5.0 {750/2.38/5} 90 100
+  echo {750/2.38/5} 
+end
+
+list dist
+  5
+  10
+  15
+  20
+  30
+  40
+  50
+  60
+  70
+  80
+end
+
+macro ParDetect
+
+  local i
+  delete -q xp yp dyp dym
+  
+  $ANGLE = 45
+
+  for i 0 $dist:n
+    PMstats $dist:$i 5.0 5.0 10 $ANGLE 100
+    concat $dist:$i xp
+    concat {1000/$MEAN} yp
+    concat {1000/($MEAN - $SIGMA) - 1000/$MEAN} dyp
+    concat {1000/$MEAN - 1000/($MEAN + $SIGMA)} dym
+  end
+
+  section a 0.0 0.0 0.5 1.0
+
+  lim xp -1.0 101; clear; box; 
+  plot -c black -x 2 xp yp -dy dym +dy dyp
+  
+  delete -q xp yp 
+  concat 0 xp
+  concat 100 xp
+  set yp = xp
+  plot -x 0 -c blue xp yp
+
+  set yp = zero(xp) + 62.5
+  plot -x 0 -c red -lt 1 xp yp
+  style -lt 0  
+  label -x "input dist (pc)" -y "output dist" -fn courier 14
+
+  $PLOT = 0
+  section b 0.5 0.0 0.5 1.0
+  lim -120 120 -120 120; box -labels 1001;
+
+  for i 0 5
+   sample
+  end
+
+  label -x "offset east (milliarcsec)"
+  label +y "offset north"
+  ps -name ParallaxDist.ps
+end
+
+macro fields
+
+  $Dx = 0.25*4800*8/3600
+  $Fo = 1.09
+
+  $dD = 2.65/1.09
+
+  $Nf = 0
+  for i {-30+$dD/2} 90 $dD
+    $Np = int(dcos($i)*1.09*360/2.65) + 1
+    $Nf = $Nf + $Np
+    fprintf "%5.1f %5.1f %4d %4d" $i {dcos($i)*1.09*360/2.65} $Np $Nf 
+  end
+end
+
+macro SNsim
+  $w  = 0.76
+  $m1 = 24.8
+  $mu = 20.4
+
+  lim 13.5 26.5 0.0 4.0; clear; box
+
+  $t = 1
+  SN
+
+  $t = 5
+  SN
+
+  $t = 30
+  SN
+
+  $t = 100
+  SN
+
+  set sn = 5 + zero (m)
+  set lsn = log(sn)
+  plot -lt 2 m lsn
+
+  set sn = 25 + zero (m)
+  set lsn = log(sn)
+  plot -lt 2 m lsn
+
+  set sn = 100 + zero (m)
+  set lsn = log(sn)
+  plot -lt 2 m lsn
+  style -lt 0
+end
+
+macro SN
+  create m 14 26 0.1
+  set sn = ten (-0.2*(2*m - $mu - $m1)) * sqrt($t/$w^2/3.1416)
+  set lsn = log(sn)
+  plot m lsn
+end
+
+# r: 30s @ 5sig = 22.65  (0.6")
+# i: 30s @ 5sig = 22.42  (0.6")
+# i: 30s @ 5sig = 22.27  (for 0.3 mag lower zp)
+
+macro mkbase
+  delete -q t
+  concat 0.0 t
+  concat {10.0/365.0} t
+  concat {20.0/365.0} t
+  concat { 6.0/52.0} t
+
+  concat { 3.0/12.0} t
+  concat { 6.0/12.0} t
+  concat {12.0/12.0} t
+  concat {18.0/12.0} t
+end
+
+
+macro parfactor
+
+  $RA = $1
+  $Dec = $2
+  $e = 23 + 27/60
+  
+  create s 0 360
+  set pR = dcos($e)*d
+
+  set pR =  +(dcos($e)*dsin(s)*dcos($RA) - dcos(s)*dsin($RA))
+  set pD =  -(dcos($e)*dsin(s)*dsin($RA) + dcos(s)*dcos($RA))*dsin($Dec) + dsin($e)*dsin(s)*dcos($Dec)
+
+  lim -1.1 1.1 -1.1 1.1; clear; box; plot pR pD
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/doc/relastro.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/doc/relastro.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/doc/relastro.txt	(revision 22322)
@@ -0,0 +1,18 @@
+
+Config (.ptolemyrc) variables used by relastro:
+
+  RELASTRO_SIGMA_LIM   : exclude measurements with dMag > SIGMA_LIM for image calibrations
+  PM_DT_MIN            : minimum temporal baseline needed to calculate a proper motion
+  PM_TOOFEW            : minimum number of valid detections to calculate a proper motion
+  POS_TOOFEW           : minimum number of valid detections to calculate an average position
+
+  CATDIR               : database directory
+
+  GSCFILE              : template sky layout definition table (used for CATDIRs created before this feature)
+  SKY_DEPTH            : define the output sky catalog size
+  SKY_TABLE            : specify an alternate name for the sky catalog table
+
+  PHOTCODE_FILE        : template photcode table (used for CATDIRs created before the photcode table was added to catdir)
+
+  CATMODE              : define the output catalog mode (mef,split,etc) -- this is ignored
+  CATFORMAT            : define the output catalog format (eg, PS1_DEV_1) -- this is ignored
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/include/relastro.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/include/relastro.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/include/relastro.h	(revision 22322)
@@ -0,0 +1,292 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <kapa.h>
+# include <signal.h>
+
+typedef enum {
+  MODE_SIMPLE,
+  MODE_CHIP,
+  MODE_MOSAIC,
+} CoordMode;
+
+typedef enum {ERROR_MODE_RA, ERROR_MODE_DEC, ERROR_MODE_POS} ErrorMode;
+
+typedef enum {FIT_NONE, FIT_AVERAGE, FIT_PM_ONLY, FIT_PAR_ONLY, FIT_PM_AND_PAR} FitMode;
+
+typedef enum {TARGET_NONE, TARGET_OBJECTS, TARGET_SIMPLE, TARGET_CHIPS, TARGET_MOSAICS} FitTarget;
+
+typedef struct {
+  double R, D;  /* Sky Coords    - degrees */
+  double P, Q;  /* Tangent Plane - pixels  */
+  double L, M;  /* Focal Plane   - pixels  */
+  double X, Y;  /* Chip Coords   - pixels  */
+  double Mag;
+  double dMag;
+  double dPos;
+  int mask;
+} StarData;
+
+// structure to hold coordinate fitting terms
+typedef struct {
+    int Npts;
+    int Nterms;
+    int Norder;
+    int Nsums;
+    int Nelems;
+    double **sum;
+    double **xsum;
+    double **ysum;
+    double **xfit;
+    double **yfit;
+} CoordFit;
+
+typedef struct {
+  double Ro, dRo;
+  double Do, dDo;
+  
+  double uR, duR;
+  double uD, duD;
+
+  double p, dp;
+
+  double chisq;
+  int Nfit;
+} PMFit;
+
+typedef struct {
+  unsigned int start;
+  unsigned int stop;
+  float Mcal;
+  float dMcal;
+  short Xm;
+  float secz;
+  char code;
+  Coords coords;
+} Mosaic; 
+
+typedef struct {
+  double median;
+  double mean;
+  double sigma;
+  double error;
+  double chisq;
+  double min;
+  double max;
+  double total;
+  int    Nmeas;
+} StatType;
+
+/* global variables set in parameter file */
+char   ImageCat[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char   SKY_TABLE[256];
+int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+
+double SIGMA_LIM;
+double MIN_ERROR;
+
+int    VERBOSE;
+
+int    RESET;
+int    UPDATE;
+int    PLOTSTUFF;
+int    SAVEPLOT;
+int    SHOW_PARAMS;
+char   STATMODE[32];
+int    POS_TOOFEW;
+int    PM_TOOFEW;
+double PM_DT_MIN;
+int    PLOTDELAY;
+int    CHIPORDER;
+
+char          *PHOTCODE_KEEP_LIST, *PHOTCODE_SKIP_LIST;
+int           NphotcodesKeep,      NphotcodesSkip;
+PhotCode     **photcodesKeep,     **photcodesSkip;
+
+int AreaSelect;
+double AreaXmin, AreaXmax, AreaYmin, AreaYmax;
+
+int ImagSelect, ImagMin, ImagMax;
+
+double  PlotMmin, PlotMmax, PlotdMmin, PlotdMmax;
+
+int DophotSelect, DophotValue;
+int PhotFlagSelect, PhotFlagPoor, PhotFlagBad;
+
+int TimeSelect;
+time_t TSTART, TSTOP;
+
+FitMode FIT_MODE;
+
+FitTarget FIT_TARGET;
+
+SkyRegion UserPatch;
+
+int DoUpdateObjects;
+int DoUpdateSimple;
+int DoUpdateChips;
+int DoUpdateMosaics;
+
+/*** relphot prototypes ***/
+void          ConfigInit          PROTO((int *argc, char **argv));
+void          GetConfig           PROTO((char *config, char *field, char *format, int N, void *ptr));
+char         *GetPhotnamebyCode   PROTO((PhotCodeData *photcodes, int code));
+void          InterpolateGrid     PROTO((float *buffer, int Nx, int Ny, Coords *ccd, Coords *gcoords));
+int          *SelectRefMosaic     PROTO((Mosaic **refmosaic, int *Nimage));
+int           args                PROTO((int argc, char **argv));
+int           bcatalog            PROTO((Catalog *subcatalog, Catalog *catalog));
+void          clean_images        PROTO(());
+void          clean_measures      PROTO((Catalog *catalog, int Ncatalog, int final));
+void          clean_mosaics       PROTO(());
+void          clean_stars         PROTO((Catalog *catalog, int Ncatalog));
+int           corner_check        PROTO((double *x1, double *y1, double *x2, double *y2));
+void          dumpGrid            PROTO(());
+void          dump_grid           PROTO(()); 
+int           edge_check          PROTO((double *x1, double *y1, double *x2, double *y2));
+void          findImages          PROTO((Catalog *catalog, int Ncatalog));
+int           findMosaics         PROTO((Catalog *catalog, int Ncatalog));
+Image        *find_images         PROTO((FITS_DB *db, GSCRegion *region, int Nregion, int *Nimage, int **LineNum));
+void set_db (FITS_DB *in);
+int Shutdown (char *format, ...);
+void TrapSignal (int sig);
+void SetProtect (int mode);
+int SetSignals ();
+
+GSCRegion    *find_regions        PROTO((Image *image, int Nimage, int *Nregions, GSCRegion *fullregion));
+void          freeGridBins        PROTO((int Ncatalog));
+void          freeImageBins       PROTO((int Ncatalog));
+void          freeMosaicBins      PROTO((int Ncatalog));
+void          free_catalogs       PROTO((Catalog *catalog, int Ncatalog));
+int           gcatalog            PROTO((Catalog *catalog, int FINAL));
+Coords       *getCoords           PROTO((int meas, int cat));
+float         getMcal             PROTO((int meas, int cat));
+float         getMgrid            PROTO((int meas, int cat));
+float         getMmos             PROTO((int meas, int cat));
+float         getMrel             PROTO((Catalog *catalog, int meas, int cat));
+GSCRegion    *get_regions         PROTO((double minRa, double maxRa, double minDec, double maxDec, int *Nregions));
+void          getfullregion       PROTO((Image *image, int Nimage, GSCRegion *fullregion));
+Image        *getimage            PROTO((int N));
+Image        *getimages           PROTO((int *N));
+void          global_stats        PROTO((Catalog *catalog, int Ncatalog));
+void          initGrid            PROTO((int dX, int dY));
+void          initGridBins        PROTO((Catalog *catalog, int Ncatalog));
+void          initImageBins       PROTO((Catalog *catalog, int Ncatalog));
+void          initImages          PROTO((Image *input, int N));
+void          initMosaicBins      PROTO((Catalog *catalog, int Ncatalog));
+void          initMosaicGrid      PROTO((Image *image, int Nimage));
+void          initMosaics         PROTO((Image *image, int Nimage));
+void          initMrel            PROTO((Catalog *catalog, int Ncatalog));
+void          initialize          PROTO((int argc, char **argv));
+void          initstats           PROTO((char *mode));
+int           liststats           PROTO((double *value, double *dvalue, int N, StatType *stats));
+Catalog      *load_catalogs       PROTO((SkyList *skylist, int *Ncatalog, int subselect));
+SkyList      *load_images         PROTO((FITS_DB *db, SkyRegion *region));
+Image        *select_images       PROTO((SkyList *skylist, Image *timage, int Ntimage, int **LineNumber, int *Nimage));
+
+void check_permissions (char *basefile);
+void lock_image_db (FITS_DB *db, char *filename);
+void unlock_image_db (FITS_DB *db);
+void create_image_db (FITS_DB *db);
+void save_catalogs (Catalog *catalog, int Ncatalog);
+
+int           main                PROTO((int argc, char **argv));
+void          mark_images         PROTO((Image *image, int Nimage, Image *timage, int Ntimage));
+void          matchImage          PROTO((Catalog *catalog, int meas, int cat));
+void          matchMosaics        PROTO((Catalog *catalog, int meas, int cat));
+GSCRegion    *name_region         PROTO((char *name, int *Nregions));
+double        opening_angle       PROTO((double x1, double y1, double x2, double y2, double x3, double y3));
+void          plot_chisq          PROTO((Catalog *catalog, int Ncatalog));
+void          plot_defaults       PROTO((Graphdata *graphdata));
+void          plot_grid           PROTO((Catalog *catalog));
+void          plot_images         PROTO(());
+void          plot_list           PROTO((Graphdata *graphdata, double *xlist, double *ylist, int N, char *label, char *file));
+void          plot_mosaic_fields  PROTO((Catalog *catalog));
+void          plot_mosaics        PROTO(());
+void          plot_scatter        PROTO((Catalog *catalog, int Ncatalog));
+void          plot_star_coords    PROTO((Catalog *catalog, int Ncatalog));
+void          plot_stars          PROTO((Catalog *catalog, int Ncatalog));
+void          reload_catalogs     PROTO((SkyList *skylist));
+int           setExclusions       PROTO((Catalog *catalog, int Ncatalog));
+void          setMcal             PROTO((Catalog *catalog, int Poor));
+void          setMcalFinal        PROTO(());
+int           setMcalOutput       PROTO((Catalog *catalog, int Ncatalog));
+void          setMgrid            PROTO((Catalog *catalog));
+int           setMmos             PROTO((Catalog *catalog, int Poor));
+int           setMrel             PROTO((Catalog *catalog, int Ncatalog));
+void          setMrelFinal        PROTO((Catalog *catalog));
+int           setMrelOutput       PROTO((Catalog *catalog, int Ncatalog, int mark));
+void          set_ZP              PROTO((double ZERO));
+int           setrefcode          PROTO((Image *image, int Nimage)); 
+void          skip_measurements   PROTO((Catalog *catalog, int pass));
+void          sortA               PROTO((double *X, int N));
+void          sortB               PROTO((double *X, double *Y, int N));
+void          sortC               PROTO((double *X, double *Y, double *F1, double *F2, int N));
+void          sortD               PROTO((double *X, double *Y, double *Z, int N));
+StatType      statsImageM         PROTO((Catalog *catalog));
+StatType      statsImageN         PROTO((Catalog *catalog));
+StatType      statsImageX         PROTO((Catalog *catalog));
+StatType      statsImagedM        PROTO((Catalog *catalog));
+StatType      statsMosaicM        PROTO((Catalog *catalog));
+StatType      statsMosaicN        PROTO((Catalog *catalog));
+StatType      statsMosaicX        PROTO((Catalog *catalog));
+StatType      statsMosaicdM       PROTO((Catalog *catalog));
+StatType      statsStarN          PROTO((Catalog *catalog, int Ncatalog));
+StatType      statsStarS          PROTO((Catalog *catalog, int Ncatalog));
+StatType      statsStarX          PROTO((Catalog *catalog, int Ncatalog));
+void          wcatalog            PROTO((Catalog *catalog));
+void          wimages             PROTO(());
+void          write_coords        PROTO((Header *header, Coords *coords));
+
+double **array_init (int Nx, int Ny);
+void array_free (double **array, int Nx);
+CoordFit *fit_init (int order);
+void fit_free (CoordFit *fit);
+void fit_add (CoordFit *fit, double x1, double y1, double x2, double y2, double wt);
+void fit_eval (CoordFit *fit);
+void fit_apply (CoordFit *fit, double *x2, double *y2, double x1, double y1);
+double **poly2d_dx (double **poly, int Nx, int Ny);
+double **poly2d_dy (double **poly, int Nx, int Ny);
+double **poly2d_copy (double **poly, int Nx, int Ny);
+double poly2d_eval (double **poly, int Nx, int Ny, double x, double y);
+CoordFit *fit_apply_coords (CoordFit *fit, Coords *coords);
+int CoordsGetCenter (CoordFit *fit, double tol, double *xo, double *yo);
+CoordFit *CoordsSetCenter (CoordFit *input, double Xo, double Yo);
+void FitChip (StarData *raw, StarData *ref, int Nmatch, Coords *coords);
+void FitMosaic (StarData *raw, StarData *ref, int Nmatch, Coords *coords);
+void FitSimple (StarData *raw, StarData *ref, int Nmatch, Coords *coords);
+void initObjectData (Catalog *catalog, int Ncatalog);
+int UpdateObjects (Catalog *catalog, int Ncatalog);
+int UpdateSimple (Catalog *catalog, int Ncatalog);
+int UpdateChips (Catalog *catalog, int Ncatalog);
+int UpdateMosaic (Catalog *catalog, int Ncatalog);
+int UpdateMeasures (Catalog *catalog, int Ncatalog);
+void fixImageRaw (Catalog *catalog, int Ncatalog, int im);
+
+int sun_ecliptic (double jd, double *lambda, double *beta, double *epsilon);
+int ParFactor (double *pR, double *pD, double R, double D, time_t T);
+int FitPM (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, int Npts);
+int FitPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *pR, double *pD, int Npts);
+int FitPMandPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, double *pR, double *pD, int Npts);
+
+Mosaic *getMosaicForImage (int N);
+
+StarData *getImageRef (Catalog *catalog, int Ncatalog, int im, int *Nstars, CoordMode mode);
+StarData *getImageRaw (Catalog *catalog, int Ncatalog, int im, int *Nstars, CoordMode mode);
+
+Mosaic *getmosaics (int *N);
+void initMosaics (Image *image, int Nimage);
+StarData *getMosaicRaw (Catalog *catalog, int Ncatalog, int mos, int *Nstars);
+StarData *getMosaicRef (Catalog *catalog, int Ncatalog, int mos, int *Nstars);
+Mosaic *getMosaicForImage (int im);
+
+double getMeanR (Measure *measure, Average *average, SecFilt *secfilt);
+double getMeanD (Measure *measure, Average *average, SecFilt *secfilt);
+int setMeanR (double ra_fit, Measure *measure, Average *average, SecFilt *secfilt);
+int setMeanD (double dec_fit, Measure *measure, Average *average, SecFilt *secfilt);
+
+float GetAstromError (Measure *measure, int mode);
+int relastro_objects ();
+int UpdateObjectOffsets (SkyList *skylist);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,82 @@
+# include "relastro.h"
+
+void ConfigInit (int *argc, char **argv) {
+
+  char  *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+  struct stat filestat;
+  int status;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  GetConfig (config, "RELASTRO_SIGMA_LIM",     "%lf", 0, &SIGMA_LIM);
+
+  // XXX these are used in relphot to identify poor stars / images -- define
+  // an equivalent concept here?
+  // GetConfig (config, "STAR_SCATTER",           "%lf", 0, &STAR_SCATTER);
+  // GetConfig (config, "IMAGE_SCATTER",          "%lf", 0, &IMAGE_SCATTER);
+  // GetConfig (config, "IMAGE_OFFSET",           "%lf", 0, &IMAGE_OFFSET);
+  // GetConfig (config, "STAR_CHISQ",             "%lf", 0, &STAR_CHISQ);
+
+  GetConfig (config, "PM_DT_MIN",              "%lf", 0, &PM_DT_MIN);
+  GetConfig (config, "PM_TOOFEW",              "%d",  0, &PM_TOOFEW);
+  GetConfig (config, "POS_TOOFEW",             "%d",  0, &POS_TOOFEW);
+
+  GetConfig (config, "GSCFILE",                "%s",  0, GSCFILE);
+  GetConfig (config, "CATDIR",                 "%s",  0, CATDIR);
+  ScanConfig(config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig(config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig(config, "PHOTCODE_FILE",          "%s",  0, MasterPhotcodeFile);
+
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = 2;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  // check for existence of CATDIR
+  status = stat (CATDIR, &filestat);
+  if (status == -1) {
+    fprintf (stderr, "directory %s does not exist, giving up\n", CATDIR);
+    exit (1);
+  } 
+
+  /* update master photcode table if not defined */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+  SetZeroPoint (25.0);
+
+  free (config);
+  free (file);
+
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitChip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitChip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitChip.c	(revision 22322)
@@ -0,0 +1,142 @@
+# include "relastro.h"
+
+// XXX make these user parameters
+# define FIT_CHIP_MAX_ERROR 0.05
+# define FIT_CHIP_NITER     3
+# define FIT_CHIP_NSIGMA    3.0
+
+// XXX we should test if the fit is sufficiently constrained across the chip, or if the
+// new positions deviate too much from the old positions
+
+// XXX add visualization tools: per chip residual plots
+
+// XXX save the fit[0].Npts value in the image table?
+
+// XXX save measurements of the fit quality (scatter, chisq) in the image table
+
+void FitChip (StarData *raw, StarData *ref, int Nmatch, Coords *coords) {
+
+  int i, Nscatter, Niter, skip;
+  CoordFit *fit;
+  double dL, dM, dR, dRmax, *values;
+
+  ALLOCATE (values, double, Nmatch);
+
+  for (Niter = 0; Niter < FIT_CHIP_NITER; Niter ++) {
+
+    // measure the scatter distribution (use only the bright end detections)
+    for (i = Nscatter = 0; i < Nmatch; i++) {
+      if (raw[i].mask) continue;
+      if (raw[i].dMag > FIT_CHIP_MAX_ERROR) continue;
+
+      dL = raw[i].L - ref[i].L;
+      dM = raw[i].M - ref[i].M;
+      dR = hypot (dL, dM);
+    
+      values[Nscatter] = dR;
+      Nscatter++;
+    }
+
+    // for a 2D Gaussian, 40% of the points are within 1 sigma; dRmax is ~ 3 sigma
+    dsort (values, Nscatter);
+    dRmax = FIT_CHIP_NSIGMA*values[(int)(0.40*Nscatter)];
+
+    // fit the requested order polynomial
+    if (CHIPORDER > 0) {
+      coords[0].Npolyterms = CHIPORDER;
+    }
+    fit = fit_init (coords[0].Npolyterms);
+
+    // generate the fit matches
+    for (i = 0; i < Nmatch; i++) {
+      if (raw[i].mask) continue;
+
+      // only keep objects within dRmax
+      dL = raw[i].L - ref[i].L;
+      dM = raw[i].M - ref[i].M;
+      dR = hypot (dL, dM);
+      if (dR > dRmax) continue;
+    
+      fit_add (fit, raw[i].X, raw[i].Y, ref[i].L, ref[i].M, raw[i].dPos);
+    }
+
+    // check if the fit has enough data points for the polynomial order
+    skip = FALSE;
+    switch (coords[0].Npolyterms) {
+      case 0:
+      case 1:
+	skip = (fit[0].Npts < 8);
+	break;
+      case 2:
+	skip = (fit[0].Npts < 11);
+	break;
+      case 3:
+	skip = (fit[0].Npts < 15);
+	break;
+      default:
+	fprintf (stderr, "invalid chip order %d\n", coords[0].Npolyterms);
+	abort ();
+    }
+    if (skip) {
+      fprintf (stderr, "insufficient measurements (%d) for requested order (%d)\n", fit[0].Npts, coords[0].Npolyterms);
+      fit_free (fit);
+      free (values);
+      return;
+    }
+
+    fprintf (stderr, "scatter limit: %f based on %d detections; using %d of %d for fit\n", dRmax, Nscatter, fit[0].Npts, Nmatch);
+
+    // measure the fit, update the coords & object coordinates
+    fit_eval (fit);
+    fit_apply_coords (fit, coords);
+    fit_free (fit);
+
+    for (i = 0; i < Nmatch; i++) {
+      XY_to_LM (&raw[i].L, &raw[i].M, raw[i].X, raw[i].Y, coords);
+    }
+  }
+
+  free (values);
+  return;
+}
+
+/* in the mosaic case, we have four coord systems of interest:
+   R,D : the sky
+   P,Q : the tangent plane
+   L,M : the focal plane
+   X,Y : the chip
+
+   R,D -> P,Q (projection)
+   P,Q -> L,M (polynomial transformation : DIS)
+   L,M -> X,Y (polynomial transformation : WRP)
+
+Some details about this function:
+
+- the initial value of the clipping radius is set based on the distribution of the dR
+  values for the bright sources.
+
+- NITER clipping passes are performed
+
+- the per-star astrometric errors are included in the fit.  The photcode table controls
+  whether only the reported positional errors are used, or if the magnitudes errors are
+  scaled to determine the error, and if there is a systematic floor for all sources.
+
+- input detections are pre-filtered on the basis of the photFlags, etc, in bcatalog
+
+- outlying detections are clipped in the iterative passes, but the clipped detections are
+  not recorded
+
+*/
+
+/* example using fit_apply() :
+
+  f = fopen ("test3.dat", "w");
+
+  // apply new coords to raw (X,Y -> L,M)
+  for (i = 0; i < Nmatch; i++) {
+    fprintf (f, "%f %f  %f %f  ", raw[i].X, raw[i].Y, raw[i].L, raw[i].M);
+    fit_apply (newfit, &L1, &M1, raw[i].X - coords[0].crpix1, raw[i].Y - coords[0].crpix2);
+    fprintf (f, "%f %f\n", L1, M1);
+  }
+  fclose (f);
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitMosaic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitMosaic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitMosaic.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "relastro.h"
+
+void FitMosaic (StarData *raw, StarData *ref, int Nmatch, Coords *coords) {
+
+  int i;
+  CoordFit *fit;
+  double dP, dQ, dR;
+
+  fit = fit_init (coords[0].Npolyterms);
+  for (i = 0; i < Nmatch; i++) {
+    if (raw[i].mask) continue;
+
+    // require radius of XXX arcsec
+    dP = raw[i].P - ref[i].P;
+    dQ = raw[i].Q - ref[i].Q;
+    dR = 3600.0 * hypot (dP, dQ);
+
+    // XXX the value needs to be set in a more intelligent way
+    if (dR > 0.15) continue;
+    
+    fit_add (fit, raw[i].L, raw[i].M, ref[i].P, ref[i].Q, 1.0);
+  }
+  if (fit[0].Npts == 0) {
+    fit_free (fit);
+    return;
+  }
+  fit_eval (fit);
+  fit_apply_coords (fit, coords);
+  fit_free (fit);
+
+  // apply new coords to raw (X,Y -> L,M)
+  for (i = 0; i < Nmatch; i++) {
+    XY_to_LM (&raw[i].P, &raw[i].Q, raw[i].L, raw[i].M, coords);
+  }
+}
+
+/* in the mosaic case, we have four coord systems of interest:
+   R,D : the sky
+   P,Q : the tangent plane
+   L,M : the focal plane
+   X,Y : the chip
+
+   R,D -> P,Q (projection)
+   P,Q -> L,M (polynomial transformation : DIS)
+   L,M -> X,Y (polynomial transformation : WRP)
+*/
+
+/* XXX I'm not using the errors at all : this could at least be done with the dMag values */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitPM.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitPM.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitPM.c	(revision 22322)
@@ -0,0 +1,87 @@
+# include "relastro.h"
+
+/* do we want an init function which does the alloc and a clear function to free? */
+int FitPM (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, int Npts) {
+
+  int i;
+
+  double **A, **B;
+  double wx, wy, Wx, Wy, Tx, Ty, Tx2, Ty2, Xs, Ys, XT, YT;
+  double chisq, Xf, Yf;
+
+  /* do I need to do this as 2 2x2 matrix equations? */
+  A = array_init (4, 4);
+  B = array_init (4, 1);
+
+  Wx = Wy = Tx = Ty = Tx2 = Ty2 = Xs = Ys = XT = YT = 0.0;
+  for (i = 0; i < Npts; i++) {
+    /* handle case where dX or dY = 0.0 */
+    wx = 1.0 / SQ(dX[i]);
+    wy = 1.0 / SQ(dY[i]);
+
+    Wx += wx;
+    Wy += wy;
+
+    Tx += T[i]*wx;
+    Ty += T[i]*wy;
+    
+    Tx2 += SQ(T[i])*wx;
+    Ty2 += SQ(T[i])*wy;
+    
+    Xs += X[i]*wx;
+    Ys += Y[i]*wy;
+
+    XT += X[i]*T[i]*wx;
+    YT += Y[i]*T[i]*wy;
+  }
+
+  A[0][0] = Wx;
+  A[0][1] = Tx;
+
+  A[1][0] = Tx;
+  A[1][1] = Tx2;
+
+  A[2][2] = Wy;
+  A[2][3] = Ty;
+
+  A[3][2] = Ty;
+  A[3][3] = Ty2;
+
+  B[0][0] = Xs;
+  B[1][0] = XT;
+  B[2][0] = Ys;
+  B[3][0] = YT;
+
+  dgaussjordan (A, B, 4, 1);
+
+  fit[0].Ro = B[0][0];
+  fit[0].uR = B[1][0];
+  fit[0].Do = B[2][0];
+  fit[0].uD = B[3][0];
+  fit[0].p  = 0.0;
+  
+  fit[0].dRo = sqrt(A[0][0]);
+  fit[0].duR = sqrt(A[1][1]);
+  fit[0].dDo = sqrt(A[2][2]);
+  fit[0].duD = sqrt(A[3][3]);
+  fit[0].dp  = 0.0;
+  
+  array_free (A, 4);
+  array_free (B, 4);
+
+  // add up the chi square for the fit
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+      Xf = fit[0].Ro + fit[0].uR*T[i];
+      Yf = fit[0].Do + fit[0].uD*T[i];
+      chisq += SQ(X[i] - Xf) / SQ(dX[i]);
+      chisq += SQ(Y[i] - Yf) / SQ(dY[i]);
+  }
+  fit[0].Nfit = Npts;
+
+  // the reduced chisq is divided by (Ndof = 2*Npts - 4)
+  fit[0].chisq = chisq / (2.0*Npts - 4.0);
+  return (TRUE);
+}
+
+// XXX this function should (optionally?) iterate and clip outlier detections
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitPMandPar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitPMandPar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitPMandPar.c	(revision 22322)
@@ -0,0 +1,98 @@
+# include "relastro.h"
+
+/* do we want an init function which does the alloc and a clear function to free? */
+int FitPMandPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, double *pR, double *pD, int Npts) {
+
+  int i;
+
+  double **A, **B;
+  double wx, wy, Wx, Wy, Tx, Ty, Tx2, Ty2, Xs, Ys, XT, YT;
+  double PR, PD, PRT, PDT, PRX, PDY, PR2, PD2;
+
+  A = array_init (5, 5);
+  B = array_init (5, 1);
+
+  PR = PD = PRT = PDT = PRX = PDY = PR2 = PD2 = 0.0;
+  Wx = Wy = Tx = Ty = Tx2 = Ty2 = Xs = Ys = XT = YT = 0.0;
+  for (i = 0; i < Npts; i++) {
+    /* handle case where dX or dY = 0.0 */
+    wx = 1.0 / SQ(dX[i]);
+    wy = 1.0 / SQ(dY[i]);
+
+    Wx += wx;
+    Wy += wy;
+
+    Tx += T[i]*wx;
+    Ty += T[i]*wy;
+    
+    Tx2 += SQ(T[i])*wx;
+    Ty2 += SQ(T[i])*wy;
+    
+    PR += pR[i]*wx;
+    PD += pD[i]*wy;
+    
+    PRT += pR[i]*T[i]*wx;
+    PDT += pD[i]*T[i]*wy;
+    
+    PRX += pR[i]*X[i]*wx;
+    PDY += pD[i]*Y[i]*wy;
+    
+    PR2 += SQ(pR[i])*wx;
+    PD2 += SQ(pD[i])*wy;
+
+    Xs += X[i]*wx;
+    Ys += Y[i]*wy;
+
+    XT += X[i]*T[i]*wx;
+    YT += Y[i]*T[i]*wy;
+  }
+
+  A[0][0] = Wx;
+  A[0][1] = Tx;
+  A[0][4] = PR;
+
+  A[1][0] = Tx;
+  A[1][1] = Tx2;
+  A[1][4] = PRT;
+
+  A[2][2] = Wy;
+  A[2][3] = Ty;
+  A[2][4] = PD;
+
+  A[3][2] = Ty;
+  A[3][3] = Ty2;
+  A[3][4] = PDT;
+
+  A[4][0] = PR;
+  A[4][1] = PRT;
+  A[4][2] = PD;
+  A[4][3] = PDT;
+  A[4][4] = PR2 + PD2;
+
+  B[0][0] = Xs;
+  B[1][0] = XT;
+  B[2][0] = Ys;
+  B[3][0] = YT;
+  B[4][0] = PRX + PDY;
+
+  dgaussjordan (A, B, 5, 1);
+
+  fit[0].Ro = B[0][0];
+  fit[0].uR = B[1][0];
+  fit[0].Do = B[2][0];
+  fit[0].uD = B[3][0];
+  fit[0].p  = B[4][0];
+  
+  fit[0].dRo = sqrt(A[0][0]);
+  fit[0].duR = sqrt(A[1][1]);
+  fit[0].dDo = sqrt(A[2][2]);
+  fit[0].duD = sqrt(A[3][3]);
+  fit[0].dp  = sqrt(A[4][4]);
+  
+  array_free (A, 5);
+  array_free (B, 5);
+
+  /* get the chisq from the matrix values */
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitPar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitPar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitPar.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "relastro.h"
+
+/* do we want an init function which does the alloc and a clear function to free? */
+int FitPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *pR, double *pD, int Npts) {
+
+  int i;
+
+  double **A, **B;
+  double wx, wy, Wx, Wy, Xs, Ys;
+  double PR, PD, PRX, PDY, PR2, PD2;
+
+  A = array_init (3, 3);
+  B = array_init (3, 1);
+
+  Wx = Wy = Xs = Ys = 0.0;
+  PR = PD = PRX = PDY = PR2 = PD2 = 0.0;
+  for (i = 0; i < Npts; i++) {
+    /* handle case where dX or dY = 0.0 */
+    wx = 1.0 / SQ(dX[i]);
+    wy = 1.0 / SQ(dY[i]);
+
+    Wx += wx;
+    Wy += wy;
+
+    PR += pR[i]*wx;
+    PD += pD[i]*wy;
+    
+    PRX += pR[i]*X[i]*wx;
+    PDY += pD[i]*Y[i]*wy;
+    
+    PR2 += SQ(pR[i])*wx;
+    PD2 += SQ(pD[i])*wy;
+
+    Xs += X[i]*wx;
+    Ys += Y[i]*wy;
+  }
+
+  A[0][0] = Wx;
+  A[0][2] = PR;
+
+  A[1][1] = Wy;
+  A[1][2] = PD;
+
+  A[2][0] = PR;
+  A[2][1] = PD;
+  A[2][2] = PR2 + PD2;
+
+  B[0][0] = Xs;
+  B[1][0] = Ys;
+  B[2][0] = PRX + PDY;
+
+  dgaussjordan (A, B, 3, 1);
+
+  fit[0].Ro = B[0][0];
+  fit[0].Do = B[1][0];
+  fit[0].p  = B[2][0];
+
+  fit[0].uR = 0.0;
+  fit[0].uD = 0.0;
+  
+  fit[0].dRo = sqrt(A[0][0]);
+  fit[0].dDo = sqrt(A[2][2]);
+  fit[0].dp  = sqrt(A[4][4]);
+  
+  fit[0].duR = 0.0;
+  fit[0].duD = 0.0;
+
+  array_free (A, 3);
+  array_free (B, 3);
+
+  /* get the chisq from the matrix values */
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitSimple.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitSimple.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/FitSimple.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "relastro.h"
+
+void FitSimple (StarData *raw, StarData *ref, int Nmatch, Coords *coords) {
+
+  int i;
+  CoordFit *fit;
+  double dP, dQ, dR;
+
+  fit = fit_init (coords[0].Npolyterms);
+  for (i = 0; i < Nmatch; i++) {
+    if (raw[i].mask) continue;
+
+    // require radius of XXX arcsec
+    dP = raw[i].P - ref[i].P;
+    dQ = raw[i].Q - ref[i].Q;
+    dR = 3600.0 * hypot (dP, dQ);
+
+    // XXX the value needs to be set in a more intelligent way
+    if (dR > 0.15) continue;
+    
+    fit_add (fit, raw[i].X, raw[i].Y, ref[i].P, ref[i].Q, 1.0);
+  }
+  if (fit[0].Npts == 0) {
+    fit_free (fit);
+    return;
+  }
+  fit_eval (fit);
+  fit_apply_coords (fit, coords);
+  fit_free (fit);
+
+  // apply new coords to raw (X,Y -> P,Q)
+  for (i = 0; i < Nmatch; i++) {
+    XY_to_LM (&raw[i].L, &raw[i].M, raw[i].X, raw[i].Y, coords);
+    raw[i].P = raw[i].L;
+    raw[i].Q = raw[i].M;
+  }
+}
+
+/* in the simple case, we only have three coord systems of interest:
+   R,D : the sky
+   P,Q : the tangent plane
+   X,Y : the chip
+
+   R,D -> P,Q (projection)
+   P,Q -> X,Y (polynomial transformation)
+
+   L,M is maintained, but is identical to P,Q
+*/
+
+/* XXX I'm not using the errors at all : this could at least be done with the dMag values */
+
+/* XXX See notes in FitChips.c */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/GetAstromError.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/GetAstromError.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/GetAstromError.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "relastro.h"
+
+float GetAstromError (Measure *measure, int mode) {
+
+  PhotCode *code;
+  float dPobs, dPsys, dPtotal, dM, AS, MS;
+
+  switch (mode) {
+    case ERROR_MODE_RA:
+      dPobs = measure[0].dXccd;  // need to redefine this as RAerr
+      break;
+    case ERROR_MODE_DEC:
+      dPobs = measure[0].dYccd;  // need to redefine this as RAerr
+      break;
+    case ERROR_MODE_POS:
+      dPobs = hypot (measure[0].dXccd, measure[0].dYccd);  // need to redefine this as RAerr
+      break;
+    default:
+      abort();
+  }
+
+  /* the astrometric errors are not being carried yet (but should be!) */
+  /* we use the photometric mag error as a weighting term */
+
+  code 	= GetPhotcodebyCode (measure[0].photcode);
+  AS   	= code[0].astromErrScale;
+  MS   	= code[0].astromErrMagScale;
+  dPsys = code[0].astromErrSys;
+  dM    = measure[0].dM;
+  
+  dPtotal = sqrt(SQ(dPsys) + AS*SQ(dPobs) + MS*SQ(dM));
+  dPtotal = MAX (dPtotal, MIN_ERROR);
+
+  return (dPtotal);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/ImageOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/ImageOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/ImageOps.c	(revision 22322)
@@ -0,0 +1,382 @@
+# include "relastro.h"
+
+static unsigned int *start;
+static unsigned int *stop;
+static int         **bin;
+
+static int         **clist;
+static int         **mlist;
+static int          *Nlist;
+static int          *NLIST;
+
+static Image        *image;
+static int          Nimage;
+
+Image *getimages (int *N) {
+  *N = Nimage;
+  return (image);
+}
+
+Image *getimage (int N) {
+  return (&image[N]);
+}
+
+void initImages (Image *input, int N) {
+
+  int i;
+
+  image = input;
+  Nimage = N;
+
+  ALLOCATE (start,   unsigned, Nimage);
+  ALLOCATE (stop,    unsigned, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    start[i] = image[i].tzero - MAX(0.01*image[i].trate*image[i].NY, 1);
+    stop[i]  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+  }
+}
+
+void initImageBins (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+
+  ALLOCATE (bin, int *, Ncatalog);
+  for (i = 0; i < Ncatalog; i++) {
+    ALLOCATE (bin[i], int, MAX (catalog[i].Nmeasure, 1));
+    for (j = 0; j < catalog[i].Nmeasure; j++) bin[i][j] = -1;
+  }
+
+  ALLOCATE (Nlist, int, Nimage);
+  ALLOCATE (NLIST, int, Nimage);
+  ALLOCATE (clist, int *, Nimage);
+  ALLOCATE (mlist, int *, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    Nlist[i] = 0;
+    NLIST[i] = 100;
+    ALLOCATE (clist[i], int, NLIST[i]);
+    ALLOCATE (mlist[i], int, NLIST[i]);
+  }
+}
+
+void freeImageBins (int Ncatalog) {
+
+  int i;
+
+  for (i = 0; i < Ncatalog; i++) {
+    free (bin[i]);
+  }
+  free (bin);
+  for (i = 0; i < Nimage; i++) {
+    free (clist[i]);
+    free (mlist[i]);
+  }
+  free (clist);
+  free (mlist);
+}
+
+/* match measurements to images */
+void findImages (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+  char *name;
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Nmeasure; j++) {
+      matchImage (catalog, j, i);
+    }
+  }
+
+  for (i = 0; VERBOSE && (i < Nimage); i++) {
+    name = GetPhotcodeNamebyCode (image[i].photcode);
+    fprintf (stderr, "image %d has %d measures (%s, %s)\n", i, Nlist[i], 
+	     ohana_sec_to_date(image[i].tzero), name);
+  } 
+}
+
+/* modify this function to use the measure->imageID field */
+void matchImage (Catalog *catalog, int meas, int cat) {
+
+  int i;
+  Measure *measure;
+  
+  measure = &catalog[cat].measure[meas];
+
+  /* find the image that supplied this measurement */
+  for (i = 0; i < Nimage; i++) {
+    if (image[0].photcode == -1) continue;
+    if (measure[0].photcode != image[i].photcode) continue;
+    if (measure[0].t < start[i]) continue;
+    if (measure[0].t > stop[i]) continue;
+    
+    // index for (catalog, measure) -> image
+    bin[cat][meas] = i;
+
+    // index for image, Nentry -> catalog
+    clist[i][Nlist[i]] = cat;
+
+    // index for image, Nentry -> measure
+    mlist[i][Nlist[i]] = meas;
+    Nlist[i] ++;
+
+    if (Nlist[i] == NLIST[i]) {
+      NLIST[i] += 100;
+      REALLOCATE (clist[i], int, NLIST[i]);
+      REALLOCATE (mlist[i], int, NLIST[i]);
+    }	
+    return;
+  }
+}
+
+Coords *getCoords (int meas, int cat) {
+
+  int i;
+
+  i = bin[cat][meas];
+  if (i == -1) return (NULL);
+  return (&image[i].coords);
+}
+
+void plot_images () {
+
+  int i, bin;
+  double *xlist, *Mlist, *dlist;
+  Graphdata graphdata;
+
+  ALLOCATE (xlist, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+  ALLOCATE (Mlist, double, Nimage);
+
+  /**** dMcal vs airmass ****/
+  for (i = 0; i < Nimage; i++) {
+    Mlist[i] = image[i].Mcal;
+    dlist[i] = image[i].dMcal;
+    xlist[i] = image[i].secz;
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = PlotdMmin; 
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, Mlist, Nimage, "airmass vs Mcal", "airmass.png");
+  plot_defaults (&graphdata);
+  plot_list (&graphdata, Mlist, dlist, Nimage, "Mcal vs dMcal", NULL);
+
+# define NBIN 200
+  REALLOCATE (xlist, double, NBIN);
+  REALLOCATE (Mlist, double, NBIN);
+
+  /**** dMcal histgram ****/
+  for (i = 0; i < NBIN; i++) xlist[i] = 0.00025*i;
+  bzero (Mlist, NBIN*sizeof(double));
+  for (i = 0; i < Nimage; i++) {
+    bin = image[i].dMcal / 0.00025;
+    bin = MAX (0, MIN (NBIN - 1, bin));
+    Mlist[bin] += 1.0;
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.style = 1;
+  plot_list (&graphdata, xlist, Mlist, NBIN, "dMcal hist", "dMcalhist.png");
+
+  free (dlist);
+  free (xlist);
+  free (Mlist);
+}
+
+// return StarData values for detections in the specified image, converting coordinates from the
+// chip positions: X,Y -> L,M -> P,Q -> R,D
+void fixImageRaw (Catalog *catalog, int Ncatalog, int im) {
+
+  int i, m, c, n;
+  double X, Y, L, M, P, Q, R, D, dR, dD;
+  
+  Mosaic *mosaic;
+  Coords *moscoords, *imcoords;
+  
+  moscoords = NULL;
+  mosaic = getMosaicForImage (im);
+  if (mosaic != NULL) {
+      moscoords = &mosaic[0].coords;
+  }
+  imcoords = &image[im].coords;
+
+  for (i = 0; i < Nlist[im]; i++) {
+    m = mlist[im][i];
+    c = clist[im][i];
+
+    X = catalog[c].measure[m].Xccd;
+    Y = catalog[c].measure[m].Yccd;
+    n = catalog[c].measure[m].averef;
+
+    if (moscoords == NULL) {
+      // this is a Simple image (not a mosaic)
+      // note that for a Simple image, L,M = P,Q
+      XY_to_LM (&L, &M, X, Y, imcoords);
+      LM_to_RD (&R, &D, L, M, imcoords);
+    } else {
+      XY_to_LM (&L, &M, X, Y, imcoords);
+      XY_to_LM (&P, &Q, L, M, moscoords);
+      LM_to_RD (&R, &D, P, Q, moscoords);
+    }
+
+    // new dR, dD : test
+    dR = 3600.0*(catalog[c].average[n].R - R);
+    dD = 3600.0*(catalog[c].average[n].D - D);
+    
+    if (fabs(catalog[c].measure[m].dR - dR) > 10.0) {
+      // XXXXX running into this still for last megacam exposure: wrong mosaic?
+      // ???? inconsistently hitting this????
+      fprintf (stderr, "!");
+      // abort ();
+    }
+    if (fabs(catalog[c].measure[m].dD - dD) > 10.0) {
+      fprintf (stderr, "*");
+      // abort ();
+    }
+
+    catalog[c].measure[m].dR = dR;
+    catalog[c].measure[m].dD = dD;
+
+    if (catalog[c].measure[m].dR > +180.0*3600.0) {
+      // average on high end of boundary, move star up
+      R += 360.0;
+      catalog[c].measure[m].dR = 3600.0*(catalog[c].average[n].R - R);
+    }
+    if (catalog[c].measure[m].dR < -180.0*3600.0) {
+      // average on low end of boundary, move star down
+      R -= 360.0;
+      catalog[c].measure[m].dR = 3600.0*(catalog[c].average[n].R - R);
+    }
+  }  
+  return;
+}
+
+// return StarData values for detections in the specified image, converting coordinates from the
+// chip positions: X,Y -> L,M -> P,Q -> R,D.  This function is used by the image fitting steps, for
+// which the detections have already been filtered when they were loaded (bcatalog)
+StarData *getImageRaw (Catalog *catalog, int Ncatalog, int im, int *Nstars, CoordMode mode) {
+
+  int i, m, c, n;
+  
+  Mosaic *mosaic;
+  Coords *moscoords;
+  StarData *raw;
+  
+  ALLOCATE (raw, StarData, Nlist[im]);
+
+  mosaic = NULL;
+  moscoords = NULL;
+  if (mode == MODE_MOSAIC) {
+      mosaic = getMosaicForImage (im);
+      if (mosaic == NULL) {
+	fprintf (stderr, "mosaic not found for image %s\n", image[im].name);
+	exit (1);
+      }
+      moscoords = &mosaic[0].coords;
+  }
+
+  for (i = 0; i < Nlist[im]; i++) {
+    m = mlist[im][i];
+    c = clist[im][i];
+
+    /* apply the current image transformation or use the current value of R+dR, D+dD? */
+    raw[i].X = catalog[c].measure[m].Xccd;
+    raw[i].Y = catalog[c].measure[m].Yccd;
+    
+    raw[i].Mag  = catalog[c].measure[m].M;
+    raw[i].dMag = catalog[c].measure[m].dM;
+    raw[i].dPos = GetAstromError (&catalog[c].measure[m], ERROR_MODE_POS);
+
+    n = catalog[c].measure[m].averef;
+
+    // an object with only one detection provides no information about the image calibration
+    raw[i].mask = FALSE;
+    if (catalog[c].average[n].Nmeasure < 2) {
+      raw[i].mask = TRUE;
+    }
+
+    switch (mode) {
+      case MODE_SIMPLE:
+	/* note that for a Simple image, L,M = P,Q */
+	XY_to_LM (&raw[i].L, &raw[i].M, raw[i].X, raw[i].Y, &image[im].coords);
+	raw[i].P = raw[i].L;
+	raw[i].Q = raw[i].M;
+	LM_to_RD (&raw[i].R, &raw[i].D, raw[i].P, raw[i].Q, &image[im].coords);
+	break;
+      case MODE_MOSAIC:
+	XY_to_LM (&raw[i].L, &raw[i].M, raw[i].X, raw[i].Y, &image[im].coords);
+	XY_to_LM (&raw[i].P, &raw[i].Q, raw[i].L, raw[i].M, moscoords);
+	LM_to_RD (&raw[i].R, &raw[i].D, raw[i].P, raw[i].Q, moscoords);
+	break;
+    default:
+      fprintf (stderr, "error: invalid mode in getImageRaw");
+      abort ();
+    }
+  }  
+
+  *Nstars = Nlist[im];
+  return (raw);
+}
+
+// return StarData values for averages positions in the specified image, converting coordinates from
+// the sky positions: R,D -> P,Q -> L,M -> X,Y
+
+StarData *getImageRef (Catalog *catalog, int Ncatalog, int im, int *Nstars, CoordMode mode) {
+
+  int i, m, c, n;
+
+  Mosaic *mosaic;
+  Coords *moscoords;
+  StarData *ref;
+  
+  ALLOCATE (ref, StarData, Nlist[im]);
+
+  mosaic = NULL;
+  moscoords = NULL;
+  if (mode == MODE_MOSAIC) {
+    mosaic = getMosaicForImage (im);
+    if (mosaic == NULL) {
+      fprintf (stderr, "mosaic not found for image %s\n", image[im].name);
+      exit (1);
+    }
+    moscoords = &mosaic[0].coords;
+  }
+
+  for (i = 0; i < Nlist[im]; i++) {
+    m = mlist[im][i];
+    c = clist[im][i];
+    n = catalog[c].measure[m].averef;
+
+    /* apply the current image transformation or use the current value of R+dR, D+dD? */
+    ref[i].R = catalog[c].average[n].R;
+    ref[i].D = catalog[c].average[n].D;
+    
+    ref[i].Mag  = catalog[c].measure[m].M;
+    ref[i].dMag = catalog[c].measure[m].dM;
+    ref[i].dPos = GetAstromError (&catalog[c].measure[m], ERROR_MODE_POS);
+
+    ref[i].mask = FALSE;
+
+    /* note that for a Simple image, L,M = P,Q */
+    switch (mode) {
+      case MODE_SIMPLE:
+      RD_to_LM (&ref[i].P, &ref[i].Q, ref[i].R, ref[i].D, &image[im].coords);
+      ref[i].L = ref[i].P;
+      ref[i].M = ref[i].Q;
+      LM_to_XY (&ref[i].X, &ref[i].Y, ref[i].L, ref[i].M, &image[im].coords);
+      break;
+      case MODE_MOSAIC:
+      RD_to_LM (&ref[i].P, &ref[i].Q, ref[i].R, ref[i].D, moscoords);
+      LM_to_XY (&ref[i].L, &ref[i].M, ref[i].P, ref[i].Q, moscoords);
+      LM_to_XY (&ref[i].X, &ref[i].Y, ref[i].L, ref[i].M, &image[im].coords);
+      break;
+      default:
+	fprintf (stderr, "invalid case");
+	abort();
+    }
+  }
+  
+  *Nstars = Nlist[im];
+  return (ref);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/MosaicOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/MosaicOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/MosaicOps.c	(revision 22322)
@@ -0,0 +1,186 @@
+# include "relastro.h"
+
+// array of mosaic definition structures
+static int    Nmosaic;
+static Mosaic *mosaic;
+
+// list of all images associated with a mosaic
+static int   *Nmosaic_own_images; // number of images for this mosaic
+static int   *Amosaic_own_images; // size of allocated array
+static int   **mosaic_own_images; // array of arrays: mosaic -> images
+
+// list of mosaic associated with each image  
+static int    Nmosaic_for_images; // number of images (for internal checks)
+static int    *mosaic_for_images; // array of: image -> mosaic
+
+Mosaic *getmosaics (int *N) {
+  *N = Nmosaic;
+  return (mosaic);
+}
+
+// find mosaic frames (unique time periods & photcode name matches mosaic) 
+void initMosaics (Image *image, int Nimage) {
+
+  int i, j, found, NMOSAIC;
+  unsigned int start, stop;
+
+  Nmosaic = 0;
+  NMOSAIC = 10;
+  ALLOCATE (mosaic, Mosaic, NMOSAIC);
+
+  ALLOCATE (Nmosaic_own_images, int, NMOSAIC);
+  ALLOCATE (Amosaic_own_images, int, NMOSAIC);
+  ALLOCATE (mosaic_own_images, int *, NMOSAIC);
+
+  /* find the mosaic images (coords.ctype = DIS); generate list of unique mosaics */
+  for (i = 0; i < Nimage; i++) {
+    if (strcmp(&image[i].coords.ctype[4], "-DIS")) continue;
+
+    /* set image time range */
+    start = image[i].tzero - MAX(0.01*image[i].trate*image[i].NY, 1);
+    stop  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+
+    /* a new mosaic, define ranges */
+    mosaic[Nmosaic].start = start;
+    mosaic[Nmosaic].stop  = stop;
+    mosaic[Nmosaic].Mcal  = 0.0;
+    mosaic[Nmosaic].dMcal = 0.0;
+    mosaic[Nmosaic].Xm    = 0.0;
+    mosaic[Nmosaic].code  = image[i].code;
+    mosaic[Nmosaic].secz  = image[i].secz;
+    mosaic[Nmosaic].coords = image[i].coords;
+
+    // init the mosaic_own_images array data
+    Nmosaic_own_images[Nmosaic] = 0;
+    Amosaic_own_images[Nmosaic] = 10;
+    ALLOCATE (mosaic_own_images[Nmosaic], int, Amosaic_own_images[Nmosaic]);
+
+    Nmosaic ++;
+    if (Nmosaic == NMOSAIC) {
+      NMOSAIC += 10;
+      REALLOCATE (mosaic, Mosaic, NMOSAIC);
+      REALLOCATE (mosaic_own_images, int *, NMOSAIC);
+      REALLOCATE (Nmosaic_own_images, int, NMOSAIC);
+      REALLOCATE (Amosaic_own_images, int, NMOSAIC);
+    }
+  }
+
+  // array to store image->mosaic index
+  Nmosaic_for_images = Nimage;
+  ALLOCATE (mosaic_for_images, int, Nmosaic_for_images);
+
+  /* now assign the WRP images to these mosaics */
+  for (i = 0; i < Nimage; i++) {
+    mosaic_for_images[i] = -1; // default value for no mosaic found
+
+    if (strcmp(&image[i].coords.ctype[4], "-WRP")) continue;
+
+    /* set image time range */
+    start = image[i].tzero - MAX(0.01*image[i].trate*image[i].NY, 1);
+    stop  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+
+    /* find existing mosaic with this time range */
+    found = FALSE;
+    for (j = 0; !found && (j < Nmosaic); j++) { 
+      if (stop  < mosaic[j].start) continue;
+      if (start > mosaic[j].stop)  continue;
+      found = TRUE;
+      break;
+    }
+    /* if no matching mosaic exists, skip this image */
+    if (!found) continue;
+
+    // mosaic corresponding to this image
+    mosaic_for_images[i] = j;
+
+    // add image to mosaic_own_image list 
+    mosaic_own_images[j][Nmosaic_own_images[j]] = i;
+    Nmosaic_own_images[j] ++;
+    if (Nmosaic_own_images[j] == Amosaic_own_images[j]) {
+      Amosaic_own_images[j] += 10;
+      REALLOCATE (mosaic_own_images[j], int, Amosaic_own_images[j]);
+    }
+    assert (Nmosaic_own_images[j] < Amosaic_own_images[j]);
+  }
+
+  return;
+}
+
+// return StarData values for detections in the specified image, converting coordinates from the
+// chip positions: X,Y -> L,M -> P,Q -> R,D
+StarData *getMosaicRaw (Catalog *catalog, int Ncatalog, int mos, int *Nstars) {
+
+  int i, j, im, Nraw, Nnew;
+  StarData *raw, *new;
+
+  Nraw = 0;
+  ALLOCATE (raw, StarData, 1);
+
+  // loop over the images owned by this mosaic
+  for (i = 0; i < Nmosaic_own_images[mos]; i++) {
+
+    im = mosaic_own_images[mos][i];
+    
+    // retrieve stars for this chip, applying chip and mosaic astrometry
+    // this function does the reverse-lookup for the mosaic corresponding to this image
+    new = getImageRaw (catalog, Ncatalog, im, &Nnew, MODE_MOSAIC);
+    
+    // merge new and raw
+    REALLOCATE (raw, StarData, Nraw + Nnew);
+    for (j = 0; j < Nnew; j++) {
+      raw[Nraw+j] = new[j];
+    }
+    Nraw += Nnew;
+
+    free (new);
+  }
+
+  *Nstars = Nraw;
+  return (raw);
+}
+
+// return StarData values for averages positions in the specified image, converting coordinates from
+// the sky positions: R,D -> P,Q -> L,M -> X,Y
+StarData *getMosaicRef (Catalog *catalog, int Ncatalog, int mos, int *Nstars) {
+
+  int i, j, im, Nref, Nnew;
+  StarData *ref, *new;
+  
+  Nref = 0;
+  ALLOCATE (ref, StarData, 1);
+
+  for (i = 0; i < Nmosaic_own_images[mos]; i++) {
+
+    im = mosaic_own_images[mos][i];
+    
+    // retrieve stars for this chip, applying chip and mosaic astrometry
+    // this function does the reverse-lookup for the mosaic corresponding to this image
+    new = getImageRef (catalog, Ncatalog, im, &Nnew, MODE_MOSAIC);
+    
+    // merge new and ref
+    REALLOCATE (ref, StarData, Nref + Nnew);
+    for (j = 0; j < Nnew; j++) {
+      ref[Nref+j] = new[j];
+    }
+    Nref += Nnew;
+
+    free (new);
+  }
+
+  *Nstars = Nref;
+  return (ref);
+}
+
+Mosaic *getMosaicForImage (int im) {
+
+  int mos;
+
+  if (im < 0) abort();
+  if (im >= Nmosaic_for_images) abort();
+
+  // search for the mosaic that 
+  mos = mosaic_for_images[im];
+  if (mos < 0) return NULL;
+
+  return &mosaic[mos];
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/ParFactor.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/ParFactor.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/ParFactor.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "relastro.h"
+
+# if (0)
+/* Low precision formulae for the sun, from Almanac p. C24 (1990) */
+/* ra and dec are returned as decimal hours and decimal degrees. */
+void lpsun (double jd, double *ra, double *dec) {
+
+  double n, L, g, lambda,epsilon,alpha,delta,x,y,z;
+
+  n = jd - J2000;
+  L = 280.460 + 0.9856474 * n;
+  g = (357.528 + 0.9856003 * n)/DEG_IN_RADIAN;
+  lambda = (L + 1.915 * sin(g) + 0.020 * sin(2. * g))/DEG_IN_RADIAN;
+  epsilon = (23.439 - 0.0000004 * n)/DEG_IN_RADIAN;
+
+  // this is the conversion from ecliptic to celestial coords
+  x = cos(lambda);
+  y = cos(epsilon)*sin(lambda);
+  z = sin(epsilon)*sin(lambda);
+
+  *ra = (atan_circ(x,y))*HRS_IN_RADIAN;
+  *dec = (asin(z))*DEG_IN_RADIAN;
+}
+# endif
+
+/* code borrowed from Skycalc : fix this stuff XXX */
+/* Low precision formulae for the sun, from Almanac p. C24 (1990) */
+int sun_ecliptic (double jd, double *lambda, double *beta, double *epsilon) {
+
+  double n, L, g;
+
+# define J2000 2451545.       /* Julian date at standard epoch */
+
+  n = jd - J2000;
+  L = 280.460 + 0.9856474 * n;
+  g = (357.528 + 0.9856003 * n)*RAD_DEG;
+  *lambda = L + 1.915 * sin(g) + 0.020 * sin(2. * g); // longitude in degrees
+  *beta = 0.0;					  // approx latitude
+  *epsilon = (23.439 - 0.0000004 * n);		  // obliquity of ecliptic in degrees
+  return TRUE;
+}
+
+/* given RA, DEC, Time, calculate the parallax factor */
+int ParFactor (double *pR, double *pD, double R, double D, time_t T) {
+
+  double jd;
+  double L, B, E, e, s, r, d;
+
+  /* given a time T in UNIX seconds, determine the solar longitude S */
+
+  jd = ohana_sec_to_jd (T);
+  sun_ecliptic (jd, &L, &B, &E);
+
+  e = E * RAD_DEG;
+  s = L * RAD_DEG;
+  r = R * RAD_DEG;
+  d = D * RAD_DEG;
+  
+  *pR =  +(cos(e)*sin(s)*cos(r) - cos(s)*sin(r));
+  *pD =  -(cos(e)*sin(s)*sin(r) + cos(s)*cos(r))*sin(d) + sin(e)*sin(s)*cos(d);
+  return TRUE;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/SetSignals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/SetSignals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/SetSignals.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "relastro.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect this signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore */
+    case SIGCONT:    /* continue - maintain this action */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? */
+    case SIGURG:     /* socket signal, ignore this */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/Shutdown.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/Shutdown.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/Shutdown.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "relastro.h"
+
+static FITS_DB *db;
+
+void set_db (FITS_DB *in) {
+  db = in;
+}
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format));
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  SetProtect (TRUE);
+  gfits_db_close (db);
+  fprintf (stderr, "ERROR: addstar halted\n");
+  exit (1);
+}
+
+
+/* XXX this is probably not needed anymore : just protect the write statements */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateChips.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateChips.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateChips.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "relastro.h"
+
+int UpdateChips (Catalog *catalog, int Ncatalog) {
+
+  /* we can measure new image parameters for each non-mosaic chip independently */
+  int i, Nimage, Nraw, Nref;
+  Image *image;
+  StarData *raw, *ref;
+
+  image = getimages (&Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+
+    /* skip all except WRP images */
+    if (strcmp(&image[i].coords.ctype[4], "-WRP")) continue;
+
+    /* convert measure coordinates to raw entries */
+    raw = getImageRaw (catalog, Ncatalog, i, &Nraw, MODE_MOSAIC);
+
+    /* convert average coordinates to ref entries */
+    ref = getImageRef (catalog, Ncatalog, i, &Nref, MODE_MOSAIC);
+
+    // note that Nraw & Nref must be equal: if not, we made a programming error in one of these two functions.
+    assert (Nraw == Nref);
+
+    // FitChip does iterative, clipped fitting
+    fprintf (stderr, "image %d : Nstars: %d\n", i, Nraw);
+    FitChip (raw, ref, Nraw, &image[i].coords);
+
+    free (raw);
+    free (ref);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateMeasures.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateMeasures.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateMeasures.c	(revision 22322)
@@ -0,0 +1,21 @@
+# include "relastro.h"
+
+int UpdateMeasures (Catalog *catalog, int Ncatalog) {
+
+  int i, Nimage;
+  Image *image;
+
+  image = getimages (&Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+
+    /* skip DIS images (since they have no associated detections) */
+    if (!strcmp(&image[i].coords.ctype[4], "-DIS")) continue;
+
+    /* convert measure coordinates to raw entries */
+    fixImageRaw (catalog, Ncatalog, i);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateMosaic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateMosaic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateMosaic.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "relastro.h"
+
+int UpdateMosaic (Catalog *catalog, int Ncatalog) {
+
+  /* we can measure new image parameters for each mosaic independently */
+  int i, Nmosaic, Nstars;
+  Mosaic *mosaic;
+  StarData *raw, *ref;
+
+  mosaic = getmosaics (&Nmosaic);
+
+  for (i = 0; i < Nmosaic; i++) {
+
+    /* convert measure coordinates to raw entries */
+    raw = getMosaicRaw (catalog, Ncatalog, i, &Nstars);
+
+    /* convert average coordinates to ref entries */
+    ref = getMosaicRef (catalog, Ncatalog, i, &Nstars);
+
+    // XXX : I'll need to supply these back to the image[] entry
+    FitMosaic (raw, ref, Nstars, &mosaic[i].coords);
+
+    free (raw);
+    free (ref);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateObjectOffsets.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateObjectOffsets.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateObjectOffsets.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "relastro.h"
+
+int UpdateObjectOffsets (SkyList *skylist) {
+
+  int i;
+  Catalog catalog;
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set up the basic catalog info
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (!catalog.Naves_disk) {
+      if (VERBOSE) fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // match measurements with images
+    initImageBins (&catalog, 1);
+    findImages (&catalog, 1);
+
+    // update the detection coordinates using the new image parameters
+    UpdateMeasures (&catalog, 1);
+
+    freeImageBins (1);
+
+    // write the updated detections to disk
+    save_catalogs (&catalog, 1);
+  }
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateObjects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateObjects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateObjects.c	(revision 22322)
@@ -0,0 +1,330 @@
+# include "relastro.h"
+
+static int Nmax;
+static double *X, *dX;
+static double *Y, *dY;
+static double *R, *dR;
+static double *D, *dD;
+static double *pX;
+static double *pY;
+static double *T;
+static double *dT;
+
+void initObjectData (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+  
+  Nmax = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
+    }
+  }
+
+  ALLOCATE (R, double, MAX (1, Nmax));
+  ALLOCATE (D, double, MAX (1, Nmax));
+  ALLOCATE (T, double, MAX (1, Nmax));
+  ALLOCATE (X, double, MAX (1, Nmax));
+  ALLOCATE (Y, double, MAX (1, Nmax));
+
+  ALLOCATE (dR, double, MAX (1, Nmax));
+  ALLOCATE (dD, double, MAX (1, Nmax));
+  ALLOCATE (dT, double, MAX (1, Nmax));
+  ALLOCATE (dX, double, MAX (1, Nmax));
+  ALLOCATE (dY, double, MAX (1, Nmax));
+
+  ALLOCATE (pX, double, MAX (1, Nmax));
+  ALLOCATE (pY, double, MAX (1, Nmax));
+}  
+
+int UpdateObjects (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, N, Nsecfilt, found, kp;
+  StatType statsR, statsD;
+  Coords coords;
+  PMFit fit;
+  time_t To;
+  int mode, Nave, Npm, Npar, Nskip;
+  double Tmin, Tmax;
+  float mag;
+  int mask;
+  PhotCode *code;
+
+  initObjectData (catalog, Ncatalog);
+
+  /* project coordinates to a plane centered on the object with units of arcsec */
+  coords.crval1 = 0;
+  coords.crval2 = 0;
+  coords.crpix1 = 0;
+  coords.crpix2 = 0;
+  coords.cdelt1 = coords.cdelt2 = 1.0 / 3600.0;
+  coords.pc1_1  = coords.pc2_2 = 1.0;
+  coords.pc1_2  = coords.pc2_1 = 0.0;
+  coords.Npolyterms = 1;
+  strcpy (coords.ctype, "RA---SIN");
+
+  // use J2000 as a reference time
+  To = ohana_date_to_sec ("2000/01/01");
+  Nave = Npar = Npm = 0;
+
+  // XXX in the future, use catalog[0].Nsecfilt only?  allow catalogs to have variable Nsecfilt?
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  assert (catalog[0].Nsecfilt == Nsecfilt);
+
+  for (i = 0; i < Ncatalog; i++) {
+
+    if (VERBOSE) fprintf (stderr, "astrometrize catalog %d : %d ave, %d meas\n", i, catalog[i].Naverage, catalog[i].Nmeasure);
+
+    Nskip = 0;
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      /* calculate the average value of R,D for a single star */
+
+      // skip objects which are known to be problematic
+      // XXX include this code or not?
+      # if (0)
+      if (catalog[i].average[j].code & STAR_BAD) {
+	Nskip ++;
+	continue;  
+      }
+      # endif
+
+      N = 0;
+      m = catalog[i].average[j].measureOffset;
+
+      Tmin = Tmax = (catalog[i].measure[m].t - To) / (86400*365.25);
+      mode = FIT_MODE;
+
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+
+	// exclude measurements by previous outlier detection
+	// XXX include this code or not?
+	# if (0)
+	if (catalog[i].measure[m].dbFlags & MEAS_BAD) { 
+	  catalog[i].measure[m].dbFlags |= ID_MEAS_SKIP_ASTROM;
+	  continue;
+	}
+	# endif
+
+	/* exclude measurements by quality */
+	if (PhotFlagSelect) {
+	  if (PhotFlagBad) {
+	    mask = PhotFlagBad;
+	  } else {
+	    code = GetPhotcodebyCode (catalog[i].measure[m].photcode);
+	    mask = code[0].astromBadMask;
+	  }
+	  if (mask & catalog[i].measure[m].photFlags) {
+	    catalog[i].measure[m].dbFlags |= ID_MEAS_SKIP_ASTROM;
+	    continue;
+	  }
+	}
+	
+	/* exclude measurements by mag limit */
+	if (ImagSelect) {
+	  mag = PhotInst (&catalog[i].measure[m]);
+	  if (mag < ImagMin) {
+	    catalog[i].measure[m].dbFlags |= ID_MEAS_SKIP_ASTROM;
+	    continue;
+	  }
+	  if (mag > ImagMax) {
+	    catalog[i].measure[m].dbFlags |= ID_MEAS_SKIP_ASTROM;
+	    continue;
+	  }
+	}
+
+	/* select or exclude measurements by photcode, or equiv photcode, if specified */
+	if (NphotcodesKeep > 0) {
+	  found = FALSE;
+	  for (kp = 0; (kp < NphotcodesKeep) && !found; kp++) {
+	    if (photcodesKeep[kp][0].code == catalog[i].measure[m].photcode) found = TRUE;
+	    if (photcodesKeep[kp][0].code == GetPhotcodeEquivCodebyCode(catalog[i].measure[m].photcode)) found = TRUE;
+	  }
+	  if (!found) continue;
+	}
+	if (NphotcodesSkip > 0) {
+	  found = FALSE;
+	  for (kp = 0; (kp < NphotcodesSkip) && !found; kp++) {
+	    if (photcodesSkip[kp][0].code == catalog[i].measure[m].photcode) found = TRUE;
+	    if (photcodesSkip[kp][0].code == GetPhotcodeEquivCodebyCode(catalog[i].measure[m].photcode)) found = TRUE;
+	  }
+	  if (found) continue;
+	}
+
+	R[N] = getMeanR (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*Nsecfilt]);
+	D[N] = getMeanD (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*Nsecfilt]);
+	T[N] = (catalog[i].measure[m].t - To) / (86400*365.25) ; // time relative to J2000 in years
+
+	Tmin = MIN(Tmin, T[N]);
+	Tmax = MAX(Tmax, T[N]);
+
+	dR[N] = GetAstromError (&catalog[i].measure[m], ERROR_MODE_RA);
+	dD[N] = GetAstromError (&catalog[i].measure[m], ERROR_MODE_DEC);
+	dT[N] = catalog[i].measure[m].dt;
+
+	N++;
+      }
+
+      // if we have too few good detections for the desired fit, or too limited a baseline, use a
+      // fit with fewer parameters.  XXX if we have too few parameters for even the average
+      // position, consider including the lower-quality detections
+
+      catalog[i].average[j].code &= ~ID_STAR_FEW;
+
+      // XXX add the parallax factor range as a criterion as well
+      if ((Tmax - Tmin) < PM_DT_MIN) mode = FIT_AVERAGE;
+      if ((mode == FIT_PM_ONLY) && (N < PM_TOOFEW)) mode = FIT_AVERAGE;
+
+      // too few measurements for average position (require 2 values)
+      if (N < 2) {
+	// XXX need to define PHOTOM and ASTROM object flags
+	// catalog[i].average[j].code |= ID_STAR_FEW;
+	continue;
+      }
+
+      /* we need to do the fit in a locally linear space; choose a ref coordinate */
+      coords.crval1 = R[0];
+      coords.crval2 = D[0];
+      
+      /* project all of the R,D coordinates to a plane centered on this coordinate */
+      for (k = 0; k < N; k++) {
+	RD_to_XY (&X[k], &Y[k], R[k], D[k], &coords);
+	dX[k] =  dR[k];
+	dY[k] =  dD[k];
+	// fprintf (stderr, "%d %f %f %f  %f %f\n", k, T[k], R[k], D[k], X[k], Y[k]);
+      }	  
+
+      /* fit the model components as needed */
+      switch (mode) {
+	case FIT_AVERAGE:
+	  liststats (R, dR, N, &statsR);
+	  liststats (D, dD, N, &statsD);
+
+	  fit.Ro = statsR.mean;
+	  fit.dRo = 3600.0*statsR.sigma;
+
+	  fit.Do = statsD.mean;
+	  fit.dDo = 3600.0*statsD.sigma;
+
+	  fit.chisq = 0.5*(statsR.chisq + statsD.chisq);
+	  fit.Nfit = N;
+
+	  fit.uR = fit.duR = 0.0;
+	  fit.uD = fit.duD = 0.0;
+	  fit.p  = fit.dp  = 0.0;
+
+	  Nave ++;
+	  break;
+
+	case FIT_PM_ONLY:
+	  FitPM (&fit, X, dX, Y, dY, T, N);
+	  // fprintf (stderr, "fitted:  %f - %f : %f %f : %f %f : %f\n", Tmin, Tmax, fit.Ro, fit.Do, fit.uR, fit.uD, fit.p);
+	  // project Ro, Do back to RA,DEC
+	  XY_to_RD (&fit.Ro, &fit.Do, fit.Ro, fit.Do, &coords);
+	  // fprintf (stderr, "project: %f %f : %f %f : %f\n", fit.Ro, fit.Do, fit.uR, fit.uD, fit.p);
+	  // continue;
+
+	  fit.p  = fit.dp  = 0.0;
+
+	  Npm ++;
+	  break;
+
+	case FIT_PAR_ONLY:
+	  fprintf (stderr, "programming error at %s, %d", __FILE__, __LINE__);
+	  exit (2);
+
+	  for (k = 0; k < N; k++) {
+	    ParFactor (&pX[k], &pY[k], R[k], D[k], T[k]);
+	  }
+	  FitPar (&fit, X, dX, Y, dY, pX, pY, N);
+
+	  // project Ro, Do back to RA,DEC
+	  XY_to_RD (&fit.Ro, &fit.Do, fit.Ro, fit.Do, &coords);
+
+	  fit.uR = fit.duR = 0.0;
+	  fit.uD = fit.duD = 0.0;
+
+	  Npar ++;
+	  break;
+
+	case FIT_PM_AND_PAR:
+	  fprintf (stderr, "programming error at %s, %d", __FILE__, __LINE__);
+	  exit (2);
+
+	  for (k = 0; k < N; k++) {
+	    ParFactor (&pX[k], &pY[k], R[k], D[k], T[k]);
+	  }
+	  FitPMandPar (&fit, X, dX, Y, dY, T, pX, pY, N);
+	  XY_to_RD (&fit.Ro, &fit.Do, fit.Ro, fit.Do, &coords);
+	  Npar ++;
+	  break;
+
+	default:
+	  fprintf (stderr, "programming error at %s, %d", __FILE__, __LINE__);
+	  exit (2);
+      }	  
+
+      if (0 && (j < 100)) {
+	fprintf (stderr, "%f %f -> %f %f (%f,%f)\n",
+		 catalog[i].average[j].R, 
+		 catalog[i].average[j].D, 
+		 fit.Ro, fit.Do, 
+		 3600*(catalog[i].average[j].R - fit.Ro), 
+		 3600*(catalog[i].average[j].D - fit.Do));
+      }
+
+      // the measure fields must be updated before the average fields
+      m = catalog[i].average[j].measureOffset;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	// XXX why was this here?? if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	setMeanR (fit.Ro, &catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*Nsecfilt]);
+	setMeanD (fit.Do, &catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*Nsecfilt]);
+      }      
+
+      catalog[i].average[j].R  	= fit.Ro; // RA in degrees
+      catalog[i].average[j].D  	= fit.Do; // DEC in degrees
+      catalog[i].average[j].dR 	= fit.dRo; // RA scatter in arcsec
+      catalog[i].average[j].dD 	= fit.dDo; // DEC scatter in arcsec
+
+      catalog[i].average[j].uR  = fit.uR; // RA proper motion in arcsec/year
+      catalog[i].average[j].uD  = fit.uD; // DEC proper motion in arcsec/year
+      catalog[i].average[j].duR = fit.duR; // RA proper motion error in arcsec/year
+      catalog[i].average[j].duD = fit.duD; // DEC proper motion error in arcsec/year
+
+      catalog[i].average[j].P   = fit.p; // parallax in arcsec
+      catalog[i].average[j].dP  = fit.dp; // parallax error in arcsec
+
+      catalog[i].average[j].Xp  = (fit.Nfit > 1) ? 100.0*log10(fit.chisq) : NAN_S_SHORT;
+    }
+
+    if (VERBOSE) fprintf (stderr, "catalog %d : %d ave, %d pm, %d par : Nskip % d\n", i, Nave, Npm, Npar, Nskip);
+  }
+
+  if (VERBOSE) fprintf (stderr, "fitted %d objects (%d ave, %d pm, %d par)\n", Nave + Npm + Npar, Nave, Npm, Npar);
+  return (TRUE);
+}
+
+/* fitting proper-motion and parallax:
+
+given a source at position r,d, at a time t, we need to calculate a vector (pr,pd)
+
+let x,y be the coordinate in the linearized frame with y parallel to DEC lines
+
+L,B are the ecliptic longitude and latitude of the object, 
+dL and dB are the offsets in the L and B directions
+
+dL = sin(t - topp)
+dB = cos(t - topp)*sin(B)
+
+these need to be rotated to the R,D frame to yield pR,pD.  Then, the equation of motion
+for the source in the x,y frame is:
+
+x = Ro + uR * (t - to) + p * pR 
+y = Do + uD * (t - to) + p * pD
+
+the unknowns in these equations are Ro, uR, Do, uD, and p
+
+XXX think through the concepts for the pole a bit better.  all objects near the pole 
+move the same way with the same phase.  choose a projection center and define dL,dB relative 
+to that center point coordinate system?
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateSimple.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateSimple.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/UpdateSimple.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include "relastro.h"
+
+int UpdateSimple (Catalog *catalog, int Ncatalog) {
+
+  /* we can measure new image parameters for each non-mosaic chip independently */
+  int i, Nimage, Nstars;
+  Image *image;
+  StarData *raw, *ref;
+
+  image = getimages (&Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+
+    /* skip WRP and DIS images */
+    if (!strcmp(&image[i].coords.ctype[4], "-WRP")) continue;
+    if (!strcmp(&image[i].coords.ctype[4], "-DIS")) continue;
+
+    /* convert measure coordinates to raw entries */
+    raw = getImageRaw (catalog, Ncatalog, i, &Nstars, MODE_SIMPLE);
+
+    /* convert average coordinates to ref entries */
+    ref = getImageRef (catalog, Ncatalog, i, &Nstars, MODE_SIMPLE);
+
+    FitSimple (raw, ref, Nstars, &image[i].coords);
+
+    free (raw);
+    free (ref);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/args.c	(revision 22322)
@@ -0,0 +1,270 @@
+# include "relastro.h"
+void usage (void);
+
+int args (int argc, char **argv) {
+
+  int N;
+  double trange;
+
+  /* possible operations */
+  FIT_TARGET = TARGET_NONE;
+  FIT_MODE = FIT_AVERAGE;
+  if ((N = get_argument (argc, argv, "-update-objects"))) {
+    remove_argument (N, &argc, argv);
+    FIT_TARGET = TARGET_OBJECTS;
+
+    // check for object fitting modes (not valid for images)
+    if ((N = get_argument (argc, argv, "-pm"))) {
+	remove_argument (N, &argc, argv);
+	FIT_MODE = FIT_PM_ONLY;
+    }
+    if ((N = get_argument (argc, argv, "-par"))) {
+	remove_argument (N, &argc, argv);
+	FIT_MODE = FIT_PAR_ONLY;
+    }
+    if ((N = get_argument (argc, argv, "-pmpar"))) {
+	remove_argument (N, &argc, argv);
+	FIT_MODE = FIT_PM_AND_PAR;
+    }
+  }
+  if ((N = get_argument (argc, argv, "-update-simple"))) {
+    remove_argument (N, &argc, argv);
+    FIT_TARGET = TARGET_SIMPLE;
+  }
+  if ((N = get_argument (argc, argv, "-update-chips"))) {
+    remove_argument (N, &argc, argv);
+    FIT_TARGET = TARGET_CHIPS;
+  }
+  if ((N = get_argument (argc, argv, "-update-mosaics"))) {
+    remove_argument (N, &argc, argv);
+    FIT_TARGET = TARGET_MOSAICS;
+  }
+  if (FIT_TARGET == TARGET_NONE) usage();
+
+  /* specify portion of the sky : allow default of all sky? */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  } else {
+    usage ();
+  }
+
+  /* define time */
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &TSTART)) { 
+      fprintf (stderr, "ERROR: syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      if (!ohana_str_to_time (argv[N], &TSTOP)) { 
+	fprintf (stderr, "ERROR: syntax error\n");
+	return (FALSE);
+      }
+    } else {
+      if (trange < 0) {
+	trange = fabs (trange);
+	TSTOP = TSTART;
+	TSTART -= trange;
+      } else {
+	TSTOP = TSTART + trange;
+      }
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+
+  PHOTCODE_KEEP_LIST = NULL;
+  if ((N = get_argument (argc, argv, "+photcode"))) {
+    remove_argument (N, &argc, argv);
+    PHOTCODE_KEEP_LIST = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  PHOTCODE_SKIP_LIST = NULL;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    PHOTCODE_SKIP_LIST = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  CHIPORDER = 0;
+  if ((N = get_argument (argc, argv, "-chiporder"))) {
+    remove_argument (N, &argc, argv);
+    CHIPORDER = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  SAVEPLOT = FALSE;
+  PLOTSTUFF = FALSE;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    PLOTSTUFF = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOTDELAY = 500000;
+  if ((N = get_argument (argc, argv, "-plotdelay"))) {
+    remove_argument (N, &argc, argv);
+    PLOTDELAY = 1e6*atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  // by default, require > 10pts to clip
+  strcpy (STATMODE, "CHI_INNER_80_WTMEAN");
+  if ((N = get_argument (argc, argv, "-statmode"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (STATMODE, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  RESET = FALSE;
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    RESET = TRUE;
+  }
+
+  UPDATE = FALSE;
+  if ((N = get_argument (argc, argv, "-update"))) {
+    remove_argument (N, &argc, argv);
+    UPDATE = TRUE;
+  }
+
+  SHOW_PARAMS = FALSE;
+  if ((N = get_argument (argc, argv, "-params"))) {
+    remove_argument (N, &argc, argv);
+    SHOW_PARAMS = TRUE;
+  }
+
+  /* XXX update these for relevant plots */
+  PlotMmin = 10.0; PlotMmax = 20.0; PlotdMmin = -1.0; PlotdMmax = 1.0;
+  if ((N = get_argument (argc, argv, "-plrange"))) {
+    remove_argument (N, &argc, argv);
+    PlotMmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotMmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotdMmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotdMmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* XXX update this */
+  MIN_ERROR = 0.001;
+  if ((N = get_argument (argc, argv, "-minerror"))) {
+    remove_argument (N, &argc, argv);
+    MIN_ERROR = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    /* require MIN_ERROR > 0 */
+  }  
+
+  AreaSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-area"))) {
+    remove_argument (N, &argc, argv);
+    AreaXmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaXmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaSelect = TRUE;
+  }
+
+  ImagSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-instmag"))) {
+    remove_argument (N, &argc, argv);
+    ImagMin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagMax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagSelect = TRUE;
+  }
+  
+  // for now, make the default to ignore the photflags
+  // XXX make it true by default instead?
+  PhotFlagSelect = FALSE;
+  if ((N = get_argument (argc, argv, "+photflags"))) {
+    remove_argument (N, &argc, argv);
+    PhotFlagSelect = TRUE;
+  }
+
+  PhotFlagBad = 0;
+  if ((N = get_argument (argc, argv, "-photflagbad"))) {
+    remove_argument (N, &argc, argv);
+    PhotFlagBad = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  PhotFlagPoor = 0;
+  if ((N = get_argument (argc, argv, "-photflagpoor"))) {
+    remove_argument (N, &argc, argv);
+    PhotFlagPoor = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* XXX drop this? */
+  DophotSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-dophot"))) {
+    remove_argument (N, &argc, argv);
+    DophotValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    DophotSelect = TRUE;
+  }
+
+  if (argc != 1) usage ();
+  return TRUE;
+}
+
+void usage () {
+  fprintf (stderr, "ERROR: USAGE: relastro -region RA RA DEC DEC\n");
+  fprintf (stderr, "  working options: \n");
+  fprintf (stderr, "  -update-objects\n");
+  fprintf (stderr, "    -pm\n");
+  fprintf (stderr, "    -par\n");
+  fprintf (stderr, "    -pmpar\n");
+  fprintf (stderr, "  -update-simple\n");
+  fprintf (stderr, "  -update-chips\n");
+  fprintf (stderr, "  -update-mosaics\n");
+  fprintf (stderr, "  -time (start)(stop)\n");
+  fprintf (stderr, "  +photcode (code)[,code,code...]\n");
+  fprintf (stderr, "  -photcode (code)[,code,code...]\n");
+  fprintf (stderr, "  -plot\n");
+  fprintf (stderr, "  -plotdelay (seconds)\n");
+  fprintf (stderr, "  -statmode (mode)\n");
+  fprintf (stderr, "  -reset");
+  fprintf (stderr, "  -update : apply new fit to database\n");
+  fprintf (stderr, "  -params\n");
+  fprintf (stderr, "  -plrange\n");
+  fprintf (stderr, "  -minerror\n");
+  fprintf (stderr, "  -area\n");
+  fprintf (stderr, "  -area Xmin Xmax Ymin Ymax\n");
+  fprintf (stderr, "  -instmag min max\n\n");
+  fprintf (stderr, "  +photflags\n");
+  fprintf (stderr, "  -photflagbad\n");
+  fprintf (stderr, "  -photflagpoor\n");
+  fprintf (stderr, "  -v\n");
+  fprintf (stderr, "  \n");
+  exit (2);
+} 
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/bcatalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/bcatalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/bcatalog.c	(revision 22322)
@@ -0,0 +1,144 @@
+# include "relastro.h"
+
+int bcatalog (Catalog *subcatalog, Catalog *catalog) {
+  
+  int i, j, k, offset, found;
+  int NAVERAGE, NMEASURE, Naverage, Nmeasure, Nm, Nsecfilt;
+  float mag;
+  int mask;
+  PhotCode *code;
+
+  // XXX in the future, use catalog[0].Nsecfilt only?  allow catalogs to have variable Nsecfilt?
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  assert (catalog[0].Nsecfilt == Nsecfilt);
+
+  /* we are moving only the subset of measurements from catalog[0] to subcatalog[0] */
+  NAVERAGE = 50;
+  NMEASURE = 1000;
+  ALLOCATE (subcatalog[0].average, Average, NAVERAGE);
+  ALLOCATE (subcatalog[0].secfilt, SecFilt, NAVERAGE*Nsecfilt);
+  ALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
+  Nmeasure = Naverage = 0;
+
+  /* exclude stars not in range or with too few measurements */
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    if (catalog[0].average[i].Nmeasure < 2) continue; 
+
+    /* start with all stars good */
+    subcatalog[0].average[Naverage] = catalog[0].average[i];
+    subcatalog[0].average[Naverage].measureOffset = Nmeasure;
+    for (j = 0; j < Nsecfilt; j++) {
+      subcatalog[0].secfilt[Nsecfilt*Naverage+j] = catalog[0].secfilt[Nsecfilt*i+j];
+    }
+
+    if (RESET) {
+      // XXX reset the ra,dec coords?
+      // XXX define astrometry vs photometry average.dbFlags
+      // XXX put photometry flags in secfilt (ie, per average mag?)
+      subcatalog[0].average[Naverage].code &= ~ID_STAR_FEW;
+      subcatalog[0].average[Naverage].code &= ~ID_STAR_POOR;
+    }
+
+    Nm = 0;
+    for (j = 0; j < catalog[0].average[i].Nmeasure; j++) {
+
+      offset = catalog[0].average[i].measureOffset + j;
+
+      /* select measurements by photcode, or equiv photcode, if specified */
+      if (NphotcodesKeep > 0) {
+	found = FALSE;
+	for (k = 0; (k < NphotcodesKeep) && !found; k++) {
+	  if (photcodesKeep[k][0].code == catalog[0].measure[offset].photcode) found = TRUE;
+	  if (photcodesKeep[k][0].code == GetPhotcodeEquivCodebyCode(catalog[0].measure[offset].photcode)) found = TRUE;
+	}
+	if (!found) continue;
+      }
+      if (NphotcodesSkip > 0) {
+	found = FALSE;
+	for (k = 0; (k < NphotcodesSkip) && !found; k++) {
+	  if (photcodesSkip[k][0].code == catalog[0].measure[offset].photcode) found = TRUE;
+	  if (photcodesSkip[k][0].code == GetPhotcodeEquivCodebyCode(catalog[0].measure[offset].photcode)) found = TRUE;
+	}
+	if (found) continue;
+      }
+
+      /* select measurements by time */
+      if (TimeSelect) {
+	if (catalog[0].measure[offset].t < TSTART) continue;
+	if (catalog[0].measure[offset].t > TSTOP) continue;
+      }
+
+      /* select measurements by quality */
+      if (DophotSelect && (catalog[0].measure[offset].dophot != DophotValue)) continue;
+
+      /* select measurements by quality */
+      if (PhotFlagSelect) {
+	if (PhotFlagBad) {
+	  mask = PhotFlagBad;
+	} else {
+	  code = GetPhotcodebyCode (catalog[0].measure[offset].photcode);
+	  mask = code[0].astromBadMask;
+	}
+	if (mask & catalog[0].measure[offset].photFlags) continue;
+      }
+
+      /* select measurements by measurement error */
+      if ((SIGMA_LIM > 0) && (catalog[0].measure[offset].dM > SIGMA_LIM)) continue;
+
+      /* select measurements by mag limit */
+      if (ImagSelect) {
+	mag = PhotInst (&catalog[0].measure[offset]);
+	if (mag < ImagMin) continue;
+	if (mag > ImagMax) continue;
+      }
+
+      // re-assess on each run of relastro if a measurement should be used
+
+      // NOCAL : this flag is used to mark measurements which must be ignored for this
+      // analysis.  an example would be time ranges or photcodes which are ignored.  NOCAL
+      // is internal only; it is not written out.
+
+      // SKIP : this flag is used to mark measurements currently ignored, but may be
+      // allowed before the analysis is complete.  for example, a star with measurements
+      // only near the edge of images will have this flag set for most of the analysis,
+      // but before the final average properties are calculated, these measurements may be
+      // allowed.
+
+      subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_SKIP_ASTROM;
+      subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_NOCAL;
+      subcatalog[0].measure[Nmeasure]          = catalog[0].measure[offset];
+      subcatalog[0].measure[Nmeasure].averef   = Naverage;
+      if (RESET) { 
+	subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_POOR_ASTROM;
+	subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_AREA;
+      }
+      Nmeasure ++;
+      Nm ++;
+      if (Nmeasure == NMEASURE) {
+	NMEASURE += 1000;
+	REALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
+      }
+    }
+    subcatalog[0].average[Naverage].Nmeasure = Nm;
+    Naverage ++;
+    if (Naverage == NAVERAGE) {
+      NAVERAGE += 50;
+      REALLOCATE (subcatalog[0].average, Average, NAVERAGE);
+      REALLOCATE (subcatalog[0].secfilt, SecFilt, NAVERAGE*Nsecfilt);
+    }
+  }
+  REALLOCATE (subcatalog[0].average, Average, MAX (Naverage, 1));
+  REALLOCATE (subcatalog[0].measure, Measure, MAX (Nmeasure, 1));
+  REALLOCATE (subcatalog[0].secfilt, SecFilt, Nsecfilt*MAX (Naverage, 1));
+  subcatalog[0].Naverage = Naverage;
+  subcatalog[0].Nmeasure = Nmeasure;
+  subcatalog[0].Nsecfilt = catalog[0].Nsecfilt;
+  subcatalog[0].Nsecf_mem = Naverage * Nsecfilt;
+  assert (Nsecfilt == catalog[0].Nsecfilt);
+
+  if (VERBOSE) {
+    fprintf (stderr, "%d: using %d stars (%d measures) for catalog\n", i, 
+	     subcatalog[0].Naverage, subcatalog[0].Nmeasure);
+   }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/dvo_astrom_ops.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/dvo_astrom_ops.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/dvo_astrom_ops.c	(revision 22322)
@@ -0,0 +1,91 @@
+# include "relastro.h"
+
+double getMeanR (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  double ra;
+
+  /* the measure carries the instantaneous mean position at the epoch t */ 
+  ra = average[0].R - measure[0].dR / 3600.0;
+
+  /* possible corrections to mean ra:
+
+  - proper-motion and parallax
+  - abberation
+  - precession and nutation, etc
+  - refraction
+  - DCR
+
+  */
+
+  return (ra);
+}
+
+double getMeanD (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  double dec;
+
+  /* the measure carries the instantaneous mean position at the epoch t */ 
+  dec = average[0].D - measure[0].dD / 3600.0;
+
+  /* possible corrections to mean ra:
+
+  - proper-motion and parallax
+  - abberation
+  - precession and nutation, etc
+  - refraction
+  - DCR
+
+  */
+
+  return (dec);
+}
+
+int setMeanR (double ra_fit, Measure *measure, Average *average, SecFilt *secfilt) {
+
+  /* math to get from new fitted position to new measure offset
+     ra_obs = average[0].R - measure[0].dR / 3600.0;
+     measure[0].dR = (ra_fit - ra_obs) * 3600.0;
+     measure[0].dR = (ra_fit - average[0].R + measure[0].dR/3600) * 3600.0
+     measure[0].dR = (ra_fit - average[0].R) * 3600.0 + measure[0].dR;
+  */
+
+  /* the measure carries the instantaneous mean position at the epoch t */ 
+  measure[0].dR += (ra_fit - average[0].R) * 3600.0;
+
+  /* possible corrections to mean ra:
+
+  - proper-motion and parallax
+  - abberation
+  - precession and nutation, etc
+  - refraction
+  - DCR
+
+  */
+
+  return (TRUE);
+}
+
+int setMeanD (double dec_fit, Measure *measure, Average *average, SecFilt *secfilt) {
+
+  /* math to get from new fitted position to new measure offset
+     dec_obs = average[0].D - measure[0].dD / 3600.0;
+     measure[0].dD = (dec_fit - dec_obs) * 3600.0;
+     measure[0].dD = (dec_fit - average[0].D + measure[0].dD/3600) * 3600.0
+     measure[0].dD = (dec_fit - average[0].D) * 3600.0 + measure[0].dD;
+  */
+
+  /* the measure carries the instantaneous mean position at the epoch t */ 
+  measure[0].dD += (dec_fit - average[0].D) * 3600.0;
+
+  /* possible corrections to mean ra:
+
+  - proper-motion and parallax
+  - abberation
+  - precession and nutation, etc
+  - refraction
+  - DCR
+
+  */
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/fitpoly.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/fitpoly.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/fitpoly.c	(revision 22322)
@@ -0,0 +1,334 @@
+# include "relastro.h"
+
+/* these functions support simultaneous 2D fits to
+   x2 = \sum a_i,j x1^i y1^j 
+   y2 = \sum b_i,j x1^i y1^j 
+
+   the order of the fit (largest coefficient) is fixed to a single
+   value for both x1,y1 terms and for x2,y2 fits
+
+   the code is currently confusing because we limit to i+j <= order.
+   this could be cleaner if we used masks and allowed i <= order, j <= order
+*/
+
+double **array_init (int Nx, int Ny) {
+
+  int i;
+  double **array;
+
+  ALLOCATE (array, double *, Nx);
+  for (i = 0; i < Nx; i++) {
+    ALLOCATE (array[i], double, Ny);
+    memset (array[i], 0, Ny*sizeof(double));
+  }    
+  return (array);
+}
+
+void array_free (double **array, int Nx) {
+
+  int i;
+
+  for (i = 0; i < Nx; i++) {
+    free (array[i]);
+  }    
+  free (array);
+}
+
+// XXX define a fit structure and drop the file static variables?
+CoordFit *fit_init (int order) {
+
+  CoordFit *fit;
+
+  ALLOCATE (fit, CoordFit, 1);
+
+  fit[0].Npts   = 0;
+  fit[0].Norder = order;
+  fit[0].Nterms = order + 1;
+  fit[0].Nsums  = 2*order + 1;
+  fit[0].Nelems = SQ(order + 1);
+
+  /* summing arrays for fit solution */
+
+  // xsum[i][j] holds \sum (x2 wt x1^i y1^j)
+  // ysum[i][j] holds \sum (y2 wt x1^i y1^j)
+  fit[0].xsum = array_init (fit[0].Nterms, fit[0].Nterms);
+  fit[0].ysum = array_init (fit[0].Nterms, fit[0].Nterms);
+
+  // xfit[i][j] holds x2 coeff for x1^i y1^j
+  // yfit[i][j] holds y2 coeff for x1^i y1^j
+  fit[0].xfit = array_init (fit[0].Nterms, fit[0].Nterms);
+  fit[0].yfit = array_init (fit[0].Nterms, fit[0].Nterms);
+
+  // sum[i][j] holds \sum (wt x1^i y1^j)
+  fit[0].sum = array_init (fit[0].Nsums, fit[0].Nsums);
+
+  return (fit);
+}
+
+void fit_free (CoordFit *fit) {
+
+  array_free (fit[0].xfit, fit[0].Nterms);
+  array_free (fit[0].yfit, fit[0].Nterms);
+
+  array_free (fit[0].sum, fit[0].Nsums);
+  array_free (fit[0].xsum, fit[0].Nterms);
+  array_free (fit[0].ysum, fit[0].Nterms);
+  
+  free (fit);
+}
+  
+// XXX use implicit masks as below or explicit masks (with function to set?)
+// XXX eg, add a global mask to this file and 
+void fit_add (CoordFit *fit, double x1, double y1, double x2, double y2, double wt) {
+
+  int ix, iy;
+  double xterm, yterm, term;
+
+  xterm = 1;
+  for (ix = 0; ix < fit[0].Nsums; ix++) {
+    yterm = 1;
+    for (iy = 0; iy < fit[0].Nsums; iy++) {
+      term = xterm*yterm*wt;
+      fit[0].sum[ix][iy] += term;
+      if ((iy < fit[0].Nterms) && (ix < fit[0].Nterms)) {
+	fit[0].xsum[ix][iy] += x2*term;
+	fit[0].ysum[ix][iy] += y2*term;
+      }
+      yterm *= y1;
+    }
+    xterm *= x1;
+  }
+  fit[0].Npts ++;
+}
+
+/* convert the xsum,ysum,sum terms into vector,matrix and solve */
+void fit_eval (CoordFit *fit) {
+
+  int i, j, ix, iy, jx, jy;
+  double **matrix, **vector;
+
+  if (fit[0].Npts == 0) {
+    fprintf (stderr, "warning: no valid pts\n");
+  }
+
+  // matrix, vector hold the final linear system
+  matrix = array_init (fit[0].Nelems, fit[0].Nelems);
+  vector = array_init (fit[0].Nelems, 2);
+
+  /* remap the xsum,ysum terms into the vector */
+  for (i = 0; i < fit[0].Nelems; i++) {
+    ix = i % fit[0].Nterms;
+    iy = i / fit[0].Nterms;
+    vector[i][0] = fit[0].xsum[ix][iy];
+    vector[i][1] = fit[0].ysum[ix][iy];
+
+    for (j = 0; j < fit[0].Nelems; j++) {
+      jx = j % fit[0].Nterms;
+      jy = j / fit[0].Nterms;
+      matrix[i][j] = fit[0].sum[ix+jx][iy+jy];
+    }
+
+    // mask the terms not represented by the Coords terms
+    if (ix + iy > fit[0].Norder) {
+      for (j = 0; j < fit[0].Nelems; j++) {
+	matrix[i][j] = 0.0;
+      }
+      vector[i][0] = 0.0;
+      vector[i][1] = 0.0;
+      matrix[i][i] = 1.0;
+    }      
+  }
+
+  for (i = 0; i < fit[0].Nelems; i++) {
+    ix = i % fit[0].Nterms;
+    iy = i / fit[0].Nterms;
+    // fprintf (stderr, "x2 : x^%dy^%d: %10.4g    y2 : x^%dy^%d: %10.4g \n", 
+    // ix, iy, vector[i][0], ix, iy, vector[i][1]);
+  }	
+
+  dgaussjordan (matrix, vector, fit[0].Nelems, 2); 
+
+  for (i = 0; i < fit[0].Nelems; i++) {
+    ix = i % fit[0].Nterms;
+    iy = i / fit[0].Nterms;
+    // fprintf (stderr, "x2 : x^%dy^%d: %10.4g    y2 : x^%dy^%d: %10.4g \n", 
+    // ix, iy, vector[i][0], ix, iy, vector[i][1]);
+  }	
+
+  /* remap the vector terms into xfit,yfit */
+  for (i = 0; i < fit[0].Nelems; i++) {
+    ix = i % fit[0].Nterms;
+    iy = i / fit[0].Nterms;
+    fit[0].xfit[ix][iy] = vector[i][0];
+    fit[0].yfit[ix][iy] = vector[i][1];
+  }
+
+  array_free (matrix, fit[0].Nelems);
+  array_free (vector, fit[0].Nelems);
+}
+
+void fit_apply (CoordFit *fit, double *x2, double *y2, double x1, double y1) {
+  
+  int ix, iy;
+  double xterm, yterm, term;
+  double x, y;
+
+  x = 0.0;
+  y = 0.0;
+
+  xterm = 1;
+  for (ix = 0; ix < fit[0].Nterms; ix++) {
+    yterm = 1;
+    for (iy = 0; iy < fit[0].Nterms; iy++) {
+      term = xterm*yterm;
+      x += fit[0].xfit[ix][iy]*term;
+      y += fit[0].yfit[ix][iy]*term;
+      yterm *= y1;
+    }
+    xterm *= x1;
+  }
+  *x2 = x;
+  *y2 = y;
+}
+
+// Nx, Ny is the number of terms in x and in y
+double **poly2d_dx (double **poly, int Nx, int Ny) {
+
+  int i, j, Nxout, Nyout;
+  double **dpoly;
+
+  Nxout = Nx - 1;
+  Nyout = Ny;
+
+  // poly[i][j] holds coeff for x1^i y1^j
+  dpoly = array_init (Nxout, Nyout);
+
+  for (i = 0; i < Nxout; i++) {
+    for (j = 0; j < Nyout; j++) {
+      dpoly[i][j] = poly[i+1][j] * (i+1);
+    }
+  }
+  return dpoly;
+}
+
+// Nx, Ny is the number of terms in x and in y
+double **poly2d_dy (double **poly, int Nx, int Ny) {
+
+  int i, j, Nxout, Nyout;
+  double **dpoly;
+
+  Nxout = Nx;
+  Nyout = Ny - 1;
+
+  // poly[i][j] holds coeff for x1^i y1^j
+  dpoly = array_init (Nxout, Nyout);
+
+  for (i = 0; i < Nxout; i++) {
+    for (j = 0; j < Nyout; j++) {
+      dpoly[i][j] = poly[i][j+1] * (j+1);
+    }
+  }
+  return dpoly;
+}
+
+// Nx, Ny is the number of terms in x and in y
+double **poly2d_copy (double **poly, int Nx, int Ny) {
+
+  int i, j;
+  double **out;
+
+  // poly[i][j] holds coeff for x1^i y1^j
+  out = array_init (Nx, Ny);
+
+  for (i = 0; i < Nx; i++) {
+    for (j = 0; j < Ny; j++) {
+      out[i][j] = poly[i][j];
+    }
+  }
+  return out;
+}
+
+double poly2d_eval (double **poly, int Nx, int Ny, double x, double y) {
+
+  int i, j;
+  double xsum, ysum, sum;
+
+  sum = 0;
+  xsum = ysum = 1.0;
+
+  for (i = 0; i < Nx; i++) {
+    ysum = xsum;
+    for (j = 0; j < Ny; j++) {
+      sum += poly[i][j] * ysum;
+      ysum *= y;
+    }
+    xsum *= x;
+  }
+  return (sum);
+}
+
+/* linear portion of fit : NORDER is 1 */
+/* this should only apply to the polynomial, not the projection terms */
+/* compare with psastro supporting code */
+CoordFit *fit_apply_coords (CoordFit *fit, Coords *coords) {
+
+  double Xo, Yo, R1, R2;
+  CoordFit *modfit;
+
+  /* I have L,M = fit(X,Y). set corresponding terms for coords */
+
+  // L = a[0][0] + a[1][0]x^1 y^0 + a[0][1] x^0 y^1 + ...
+  // L = pc1_1*cd1*(x - cp1) + pc1_2*cd2*(y - cp2) + ...
+
+  CoordsGetCenter (fit, 0.001, &Xo, &Yo);
+  coords[0].crpix1 = Xo;
+  coords[0].crpix2 = Yo;
+  
+  // resulting fit should have zero constant terms
+  modfit = CoordsSetCenter (fit, Xo, Yo);
+
+  /* we do not modify crval1,2: these are kept at the default values */
+
+  // set cdelt1, cdelt2
+  coords[0].cdelt1 = hypot (modfit[0].xfit[1][0], modfit[0].xfit[0][1]);
+  coords[0].cdelt2 = hypot (modfit[0].yfit[1][0], modfit[0].yfit[0][1]);
+  R1 = 1 / coords[0].cdelt1;
+  R2 = 1 / coords[0].cdelt2;
+
+  // set pc1_1, pc1_2, pc2_1, pc2_2 (cd1,cd2 = 1.0)
+  coords[0].pc1_1 = modfit[0].xfit[1][0] * R1;
+  coords[0].pc1_2 = modfit[0].xfit[0][1] * R2;
+  coords[0].pc2_1 = modfit[0].yfit[1][0] * R1;
+  coords[0].pc2_2 = modfit[0].yfit[0][1] * R2;
+
+  // set the polyterm elements 
+  if (coords->Npolyterms > 1) {
+    coords[0].polyterms[0][0] = modfit[0].xfit[2][0]*R1*R1;
+    coords[0].polyterms[1][0] = modfit[0].xfit[1][1]*R1*R2;
+    coords[0].polyterms[2][0] = modfit[0].xfit[0][2]*R2*R2;
+
+    coords[0].polyterms[0][1] = modfit[0].yfit[2][0]*R1*R1;
+    coords[0].polyterms[1][1] = modfit[0].yfit[1][1]*R1*R2;
+    coords[0].polyterms[2][1] = modfit[0].yfit[0][2]*R2*R2;
+  }
+
+  // I need to validate Norder
+  if (coords->Npolyterms > 2) {
+    coords[0].polyterms[3][0] = modfit[0].xfit[3][0]*R1*R1*R1;
+    coords[0].polyterms[4][0] = modfit[0].xfit[2][1]*R1*R1*R2;
+    coords[0].polyterms[5][0] = modfit[0].xfit[1][2]*R1*R2*R2;
+    coords[0].polyterms[6][0] = modfit[0].xfit[0][3]*R2*R2*R2;
+
+    coords[0].polyterms[3][1] = modfit[0].yfit[3][0]*R1*R1*R1;
+    coords[0].polyterms[4][1] = modfit[0].yfit[2][1]*R1*R1*R2;
+    coords[0].polyterms[5][1] = modfit[0].yfit[1][2]*R1*R2*R2;
+    coords[0].polyterms[6][1] = modfit[0].yfit[0][3]*R2*R2*R2;
+  }
+
+  fit_free (modfit);
+  /* keep the order and type from initial values */
+  
+  // XXX if desired in the future, return modfit (and free above)
+  return (NULL);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/initialize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/initialize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/initialize.c	(revision 22322)
@@ -0,0 +1,78 @@
+# include "relastro.h"
+
+void initialize (int argc, char **argv) {
+
+  int NPHOTCODES;
+  char *codename, *ptr, *list;
+
+  ConfigInit (&argc, argv);
+  args (argc, argv);
+
+  /* build a list of accepted photcodes. these will be used by bcatalog to accept or
+     reject loaded data */
+
+  NphotcodesKeep = 0;
+  photcodesKeep = NULL;
+  if (PHOTCODE_KEEP_LIST != NULL) {
+    NPHOTCODES = 10;
+    ALLOCATE (photcodesKeep, PhotCode *, NPHOTCODES);
+
+    /* parse the comma-separated list of photcodesKeep */
+    list = PHOTCODE_KEEP_LIST;
+    while ((codename = strtok_r (list, ",", &ptr)) != NULL) {
+      list = NULL; // pass NULL on successive strtok_r calls
+      fprintf (stderr, "PHOTCODE_LIST: %s\n", PHOTCODE_KEEP_LIST);
+      fprintf (stderr, "codename: %s\n", codename);
+      if ((photcodesKeep[NphotcodesKeep] = GetPhotcodebyName (codename)) == NULL) {
+	fprintf (stderr, "ERROR: photcode %s not found in photcode table\n", codename);
+	exit (1);
+      }
+      NphotcodesKeep ++;
+      CHECK_REALLOCATE (photcodesKeep, PhotCode *, NPHOTCODES, NphotcodesKeep, 10);
+    }
+  }
+
+  NphotcodesSkip = 0;
+  photcodesSkip = NULL;
+  if (PHOTCODE_SKIP_LIST != NULL) {
+    NPHOTCODES = 10;
+    ALLOCATE (photcodesSkip, PhotCode *, NPHOTCODES);
+
+    /* parse the comma-separated list of photcodesSkip */
+    list = PHOTCODE_SKIP_LIST;
+    while ((codename = strtok_r (list, ",", &ptr)) != NULL) {
+      list = NULL; // pass NULL on successive strtok_r calls
+      fprintf (stderr, "PHOTCODE_LIST: %s\n", PHOTCODE_SKIP_LIST);
+      fprintf (stderr, "codename: %s\n", codename);
+      if ((photcodesSkip[NphotcodesSkip] = GetPhotcodebyName (codename)) == NULL) {
+	fprintf (stderr, "ERROR: photcode %s not found in photcode table\n", codename);
+	exit (1);
+      }
+      NphotcodesSkip ++;
+      CHECK_REALLOCATE (photcodesSkip, PhotCode *, NPHOTCODES, NphotcodesSkip, 10);
+    }
+  }
+
+  initstats (STATMODE);
+
+  // IMAGE_BAD = ID_IMAGE_POOR | ID_IMAGE_FEW | ID_IMAGE_SKIP;
+  // STAR_BAD  = ID_STAR_POOR | ID_STAR_FEW;
+  // MEAS_BAD  = ID_MEAS_NOCAL | ID_MEAS_POOR_ASTROM | ID_MEAS_SKIP_ASTROM | ID_MEAS_AREA;
+
+  /* XXX drop irrelevant entries */
+  if (SHOW_PARAMS) {
+    fprintf (stderr, "current parameter settings:\n");
+    if (TimeSelect) {
+      fprintf (stderr, "TimeSelect: TRUE (%s - %s)\n", ohana_sec_to_date (TSTART), ohana_sec_to_date (TSTOP));
+    } else {
+      fprintf (stderr, "TimeSelect: FALSE\n");
+    }
+    fprintf (stderr, "VERBOSE: %d, PLOTSTUFF: %d\n", VERBOSE, PLOTSTUFF);
+
+    fprintf (stderr, "IMAGE_CATALOG          %s\n",  ImageCat);
+    fprintf (stderr, "GSCFILE                %s\n",  GSCFILE);
+    fprintf (stderr, "CATDIR                 %s\n",  CATDIR);
+    exit (0);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/liststats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/liststats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/liststats.c	(revision 22322)
@@ -0,0 +1,149 @@
+# include "relastro.h"
+
+enum {M_MEDIAN, 
+      M_MEAN, M_WTMEAN, 
+      M_INNER_MEAN, M_INNER_WTMEAN, 
+      M_INNER_80_MEAN, M_INNER_80_WTMEAN, 
+      M_CHI_INNER_MEAN, M_CHI_INNER_WTMEAN, 
+      M_CHI_INNER_80_MEAN, M_CHI_INNER_80_WTMEAN};
+
+static int statmode;
+
+void initstats (char *mode) {
+
+  statmode = -1;
+  if (!strcmp (mode, "MEAN")) statmode = M_MEAN;
+  if (!strcmp (mode, "MEDIAN")) statmode = M_MEDIAN;
+  if (!strcmp (mode, "WTMEAN")) statmode = M_WTMEAN;
+  if (!strcmp (mode, "INNER_MEAN")) statmode = M_INNER_MEAN;
+  if (!strcmp (mode, "INNER_WTMEAN")) statmode = M_INNER_WTMEAN;
+  if (!strcmp (mode, "INNER_80_MEAN")) statmode = M_INNER_80_MEAN;
+  if (!strcmp (mode, "INNER_80_WTMEAN")) statmode = M_INNER_80_WTMEAN;
+  if (!strcmp (mode, "CHI_INNER_MEAN")) statmode = M_CHI_INNER_MEAN;
+  if (!strcmp (mode, "CHI_INNER_WTMEAN")) statmode = M_CHI_INNER_WTMEAN;
+  if (!strcmp (mode, "CHI_INNER_80_MEAN")) statmode = M_CHI_INNER_80_MEAN;
+  if (!strcmp (mode, "CHI_INNER_80_WTMEAN")) statmode = M_CHI_INNER_80_WTMEAN;
+
+  if (statmode == -1) {
+    fprintf (stderr, "ERROR: invalid stats mode: %s\n", mode);
+    exit (1);
+  }
+}
+
+int liststats (double *value, double *dvalue, int N, StatType *stats) {
+  
+  int i, ks, ke, Nm;
+  double Mo, dMo, M, dM, X2, dS, *chi;
+
+  ke = ks = dMo = 0;
+
+  stats[0].Nmeas = N;
+  stats[0].mean  = 0;
+  stats[0].sigma = 0;
+  stats[0].error = 0;
+  stats[0].chisq = 0;
+  if (N < 1) return (FALSE);
+
+  dsortpair (value, dvalue, N);
+  if (N % 2) {
+    stats[0].median = value[(int)(0.5*N)];
+  } else {
+    stats[0].median = 0.5*(value[N/2] + value[N/2 - 1]);
+  }
+  stats[0].min    = value[0];
+  stats[0].max    = value[N-1];
+
+  switch (statmode) {
+  case M_MEDIAN:
+    ks = 0;
+    ke = N;
+    Mo = stats[0].median;
+    Nm = N;
+    goto chisq;
+    break;
+  case M_MEAN:
+  case M_WTMEAN:
+    ks = 0;
+    ke = N;
+    break;
+  case M_INNER_MEAN:
+  case M_INNER_WTMEAN:
+  case M_CHI_INNER_MEAN:
+  case M_CHI_INNER_WTMEAN:
+    ks = 0.25*N + 0.50;
+    ke = 0.75*N + 0.25;
+    if (N <= 3) {
+      ks = 0;
+      ke = N;
+    }
+    break;
+  case M_INNER_80_MEAN:
+  case M_INNER_80_WTMEAN:
+  case M_CHI_INNER_80_MEAN:
+  case M_CHI_INNER_80_WTMEAN:
+    ks = 0.1*N + 0.1;
+    ke = 0.8*N + 0.1;
+    if (N <= 10) {
+      ks = 0;
+      ke = N;
+    }
+    break;
+  }    
+
+  if ((statmode == M_CHI_INNER_MEAN) || 
+      (statmode == M_CHI_INNER_WTMEAN) || 
+      (statmode == M_CHI_INNER_80_MEAN) || 
+      (statmode == M_CHI_INNER_80_WTMEAN)) {
+    ALLOCATE (chi, double, N);
+    for (i = 0; i < N; i++) {
+      chi[i] = (value[i] - stats[0].median) / dvalue[i];
+    }
+    dsortthree (chi, value, dvalue, N);
+    free (chi);
+  }
+
+  /* calculating the per-star offset based on the weighted average */
+  M = dM = Nm = 0;
+  if ((statmode == M_WTMEAN) || 
+      (statmode == M_INNER_WTMEAN) || 
+      (statmode == M_INNER_80_WTMEAN) || 
+      (statmode == M_CHI_INNER_WTMEAN) || 
+      (statmode == M_CHI_INNER_80_WTMEAN)) {
+    for (i = ks; i < ke; i++) {
+      M   += value[i] / SQ (dvalue[i]);
+      dM  += 1.0 / SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / dM;
+    dMo = sqrt (1.0 / dM);
+  } else {
+    for (i = ks; i < ke; i++) {
+      M   += value[i];
+      dM  += SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / (double) Nm;
+    dMo = sqrt (dM / (double) Nm);
+  }
+
+ chisq:
+  /* find sigma and chisq */
+  X2 = dS = 0;
+  for (i = ks; i < ke; i++) {
+    M  = SQ (value[i] - Mo);
+    dM = SQ (dvalue[i]);
+    X2 += M / dM;
+    dS += M;
+  }
+  X2 = X2 / Nm;
+  dS = sqrt (dS / Nm);
+
+  stats[0].mean  = Mo;
+  stats[0].Nmeas = Nm;
+  stats[0].chisq = X2;
+  stats[0].sigma = dS;
+  stats[0].error = dMo;
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/load_catalogs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/load_catalogs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/load_catalogs.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "relastro.h"
+
+Catalog *load_catalogs (SkyList *skylist, int *Ncatalog, int subselect) {
+
+  int i, j, k, m, Nstar;
+  Catalog *catalog, *pcatalog, tcatalog;
+
+  if (VERBOSE) fprintf (stderr, "loading catalog data\n");
+
+  ALLOCATE (catalog, Catalog, skylist[0].Nregions);
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // we only allow output if we do not use a subset.  in this case,
+    // the output parameters are correctly set for catalog[i] via pcatalog
+    pcatalog = subselect ? &tcatalog : &catalog[i];
+
+    // set up the basic catalog info
+    pcatalog[0].filename  = skylist[0].filename[i];
+    pcatalog[0].catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    pcatalog[0].catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    pcatalog[0].catflags  = LOAD_AVES | LOAD_MEAS | LOAD_SECF; // don't need to load all data at this point
+    pcatalog[0].Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    if (!dvo_catalog_open (pcatalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", pcatalog[0].filename);
+      exit (1);
+    }
+    if (VERBOSE && !pcatalog[0].Naves_disk) fprintf (stderr, "no data in %s, skipping\n", pcatalog[0].filename);
+
+    // select only the brighter stars
+    if (subselect) {
+      bcatalog (&catalog[i], &tcatalog);
+      dvo_catalog_unlock (&tcatalog);
+      dvo_catalog_free (&tcatalog);
+    } else {
+      if (RESET) {
+	for (j = 0; j < catalog[i].Naverage; j++) {
+	  catalog[i].average[j].code = 0;
+	  m = catalog[i].average[j].measureOffset;
+	  for (k = 0; k < catalog[i].average[j].Nmeasure; k++) {
+	    catalog[i].measure[m+k].dbFlags = 0;
+	  }
+	}
+      }
+    }
+  }
+
+  // XXX keep this test?
+  Nstar = 0;
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    Nstar += catalog[i].Naverage;
+  }
+  if (Nstar < 2) { 
+    fprintf (stderr, "warning: insufficient stars %d\n", Nstar);
+  }
+
+  // XXX consider only returning the populated catalogs
+  *Ncatalog = skylist[0].Nregions;
+  return (catalog);
+}
+
+/* this function loads all relevant catalog files for the first pass.  it currently loads the data
+   read only (SOFT lock) since it assumes the image table has been locked. if we go to the new
+   addstar locking paradigm, in which the images and catalogs are updated independently, then we may
+   need to use an XCLD lock here.  
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/load_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/load_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/load_images.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "relastro.h"
+
+SkyList *load_images (FITS_DB *db, SkyRegion *region) {
+
+  Image     *image, *subset;
+  int        Nimage, Nsubset;
+  int       *LineNumber;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // determine the populated SkyRegions overlapping the requested area
+  skylist = SkyListByPatch (sky, -1, region);
+
+  // convert database table to internal structure
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  // select the images which overlap the selected sky regions
+  subset = select_images (skylist, image, Nimage, &LineNumber, &Nsubset);
+
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, LineNumber, Nsubset);
+
+  initImages (subset, Nsubset);
+  initMosaics (subset, Nsubset);
+  
+  /* unlock, if we can (else, unlocked below) */
+  if (!UPDATE) dvo_image_unlock (db); 
+
+  return (skylist);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/mkpolyterm.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/mkpolyterm.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/mkpolyterm.c	(revision 22322)
@@ -0,0 +1,166 @@
+# include "relastro.h"
+
+double factorial (int N) {
+
+  int i;
+  double F;
+
+  F = 1;
+  for (i = N; i > 1; i--) {
+    F *= i;
+  }
+  return F;
+}
+
+// XXX use a separate structure for the map (x2,y2 = f,g(x1,y1)) ?
+
+// given a 2D transformation -- L(x,y),M(x,y) -- find the coordinates x,y
+// for which L,M = 0,0. tol is the allowed error on x,y.
+int CoordsGetCenter (CoordFit *fit, double tol, double *xo, double *yo) {
+
+  int i, Nx, Ny;
+  double R, Xo, Yo, dPos, dPosRef;
+  double **XdX, **XdY, **YdX, **YdY, **alpha, **beta;
+  double **xfit, **yfit;
+
+  xfit = fit[0].xfit;
+  yfit = fit[0].yfit;
+
+  // solve for Xo,Yo where L,M(Xo,Yo) = 0,0
+  // start with linear solution for Xo,Yo:
+  R  = (xfit[1][0]*yfit[0][1] - xfit[0][1]*yfit[1][0]);
+  Xo = (yfit[0][0]*xfit[0][1] - xfit[0][0]*yfit[0][1])/R;
+  Yo = (xfit[0][0]*yfit[1][0] - yfit[0][0]*xfit[1][0])/R;
+
+  Nx = fit[0].Nterms;
+  Ny = fit[0].Nterms;
+
+  // iterate to actual solution: requires small non-linear terms
+  if (fit[0].Norder > 1) {
+    XdX = poly2d_dx (xfit, Nx, Ny);
+    XdY = poly2d_dy (xfit, Nx, Ny);
+    YdX = poly2d_dx (yfit, Nx, Ny);
+    YdY = poly2d_dy (yfit, Nx, Ny);
+
+    alpha = array_init (2, 2);
+    beta  = array_init (2, 1);
+
+    /* this loop uses the Newton-Raphson method to solve for Xo,Yo
+     * it needs the high order terms to be small 
+     * Xo,Yo are in pixels;
+     */
+    dPos = tol + 1;
+    dPosRef = dPos;
+    for (i = 0; (dPos > tol) && (i < 20); i++) {
+      // NOTE: order for alpha is: [y][x]
+      alpha[0][0] = poly2d_eval (XdX, Nx-1, Ny,   Xo, Yo);
+      alpha[1][0] = poly2d_eval (XdY, Nx,   Ny-1, Xo, Yo);
+      alpha[0][1] = poly2d_eval (YdX, Nx-1, Ny,   Xo, Yo);
+      alpha[1][1] = poly2d_eval (YdY, Nx,   Ny-1, Xo, Yo);
+
+      beta[0][0] = poly2d_eval (xfit, Nx, Ny, Xo, Yo);
+      beta[1][0] = poly2d_eval (yfit, Nx, Ny, Xo, Yo);
+
+      dgaussjordan (alpha, beta, 2, 1);
+
+      Xo -= beta[0][0];
+      Yo -= beta[1][0];
+      dPos = hypot(beta[0][0], beta[1][0]);
+      if (i == 0) {
+	dPosRef = dPos;
+      }
+    }
+    if (dPos > dPosRef) {
+      fprintf (stderr, "*** warning : non-convergence in model conversion (mkpolyterm.c;73) *** \n");
+    }
+    array_free (alpha, 2);
+    array_free (beta, 2);
+    array_free (XdX, Nx - 1);
+    array_free (XdY, Nx);
+    array_free (YdX, Nx - 1);
+    array_free (YdY, Nx);
+  }
+  *xo = Xo;
+  *yo = Yo;
+  return TRUE;
+}
+
+// convert a transformation L(x,y) to L'(x-xo,y-yo)
+CoordFit *CoordsSetCenter (CoordFit *input, double Xo, double Yo) {
+
+  int i, j, Nx, Ny;
+  double **xPx, **yPx, **xPy, **yPy, **tmp;
+
+  CoordFit *output;
+  output = fit_init (input->Norder);
+
+  /* given two equivalent polynomial representations L(x,y) = \sum_i \sum_j A_{i,j} x^i y^j
+   * we can transform L(x,y) into L'(x-xo,y-yo) by taking the derivatives of both sides and 
+   * noting that the constant term in each is the coefficient in the case of L(x,y) and is the 
+   * value of L'(-xo,-yo) in the second case.
+   */
+
+  Nx = input->Nterms;
+  Ny = input->Nterms;
+
+  xPx = poly2d_copy (input->xfit, Nx, Ny);
+  yPx = poly2d_copy (input->yfit, Nx, Ny);
+
+  for (i = 0; i < input->Nterms; i++) {
+    xPy = poly2d_copy (xPx, Nx, Ny);
+    yPy = poly2d_copy (yPx, Nx, Ny);
+    for (j = 0; j < input->Nterms; j++) {
+      output->xfit[i][j] = poly2d_eval (xPy, Nx, Ny, Xo, Yo) / factorial(i) / factorial(j);
+      output->yfit[i][j] = poly2d_eval (yPy, Nx, Ny, Xo, Yo) / factorial(i) / factorial(j);
+
+      // take the next derivative wrt y, catch output (is NULL on last pass)
+      if (Ny > 0) {
+	tmp = poly2d_dy(xPy, Nx, Ny);
+	array_free (xPy, Nx);
+	xPy = tmp;
+
+	tmp = poly2d_dy(yPy, Nx, Ny);
+	array_free (yPy, Nx);
+	yPy = tmp;
+      } else {
+	array_free (xPy, Nx);
+	array_free (yPy, Nx);
+      }
+
+      Ny --;
+    }
+    Ny = input->Nterms;
+    // take the next derivative wrt x, catch output (is NULL on last pass)
+    if (Nx > 0) {
+      tmp = poly2d_dx(xPx, Nx, Ny);
+      array_free (xPx, Nx);
+      xPx = tmp;
+      tmp = poly2d_dx(yPx, Nx, Ny);
+      array_free (yPx, Nx);
+      yPx = tmp;
+      Nx --;
+    } else {
+      array_free (xPx, Nx);
+      array_free (yPx, Nx);
+    }
+  }
+  return output;
+}
+
+/*
+  Coords uses a rigid sequence for the coefficients:
+
+  x^0 y^0 : crpix1,2 (but note this is applied before higher order terms)
+
+  x^1 y^0 : pc1_1, pc2_1
+  x^0 y^1 : pc1_2, pc2_2
+
+  x^2 y^0 : polyterm[0][0,1]
+  x^1 y^1 : polyterm[1][0,1]
+  x^0 y^2 : polyterm[2][0,1]
+
+  x^3 y^0 : polyterm[3][0,1]
+  x^2 y^1 : polyterm[4][0,1]
+  x^1 y^2 : polyterm[5][0,1]
+  x^0 y^3 : polyterm[6][0,1]
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/plot_scatter.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/plot_scatter.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/plot_scatter.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "relastro.h"
+   
+void plot_scatter (Catalog *catalog, int Ncatalog) {
+
+# if (0)   
+  int i, j, k, m, N, Ntot, Nsecfilt;
+  float Mrel, Mcal, Mmos, Mgrid;
+  double *xlist, *ylist, *ilist;
+  Graphdata graphdata;
+
+  // XXX in the future, use catalog[0].Nsecfilt only?  allow catalogs to have variable Nsecfilt?
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  assert (catalog[0].Nsecfilt == Nsecfilt);
+
+  Ntot = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Ntot += catalog[i].average[j].Nmeasure;
+    }
+  }
+  ALLOCATE (xlist, double, Ntot);
+  ALLOCATE (ylist, double, Ntot);
+  ALLOCATE (ilist, double, Ntot);
+
+  N = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+      m = catalog[i].average[j].measureOffset;
+
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	if (catalog[i].measure[m].flags & MEAS_BAD) continue;
+
+	Mrel = catalog[i].secfilt[Nsecfilt*j+PhotSec].M;
+	xlist[N] = Mrel;
+	ylist[N] = PhotSys  (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*Nsecfilt]) - Mcal - Mmos - Mgrid - Mrel;
+	ilist[N] = PhotInst (&catalog[i].measure[m]);
+	N++;
+      }
+    }
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.xmin = PlotMmin;
+  graphdata.xmax = PlotMmax;
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, ylist, N, "mag vs dmag", "Mag.png");
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, ilist, ylist, N, "imag vs dmag", "iMag.png");
+  free (xlist);
+  free (ylist);
+  free (ilist);
+
+# endif
+}
+
+/* XXX this should become astrometrically relevant */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/plotstuff.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/plotstuff.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/plotstuff.c	(revision 22322)
@@ -0,0 +1,148 @@
+# include "relastro.h"
+# include <signal.h>
+
+static int Xgraph[5] = {0,0,0,0,0};
+static int active;
+
+enum {black, white, red, orange, yellow, green, blue, indigo, violet};
+
+/*
+static union { unsigned char c[4]; float f; } f_undef = { {0xff, 0xff, 0xff, 0xfe} };
+# define fUNDEF (f_undef.f)
+*/
+
+static union { unsigned char c[8]; float d; } d_undef = { {0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00} };
+# define dUNDEF (d_undef.d)
+
+void XDead () {
+  signal (SIGPIPE, XDead);
+  fprintf (stderr, "kapa is dead, must restart\n");
+  Xgraph[active] = -1;
+}
+
+int open_graph (int N) {
+
+  char name[100];
+
+  sprintf (name, "gastro [%d]", N);
+  Xgraph[N] = KapaOpen ("kapa", name);
+
+  if (Xgraph[N] < 0) {
+    fprintf (stderr, "error starting kapa\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+void DonePlotting (Graphdata *graphmode, int N) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaBox (Xgraph[N], graphmode);
+  return;
+}
+
+void JpegPlot (Graphdata *graphmode, int N, char *filename) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaPNG (Xgraph[N], filename);
+  return;
+}
+
+void PSPlot (Graphdata *graphmode, int N, char *filename) {
+
+  if (Xgraph[N] == 0) return;
+
+  KiiPS (Xgraph[N], filename, TRUE, KAPA_PS_NEWPLOT, "default");
+  return;
+}
+
+void PrepPlotting (int Npts, Graphdata *graphmode, int N) {
+
+  if (Npts < 1) return;
+
+  if (Xgraph[N] < 1) if (!open_graph(N)) return;
+
+  KapaClearSections (Xgraph[N]);
+
+  KapaPrepPlot (Xgraph[N], Npts, graphmode);
+  return;
+}
+
+void PlotLabel (char *string, int N) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaSendLabel (Xgraph[N], string, 2);
+}
+
+void PlotVector (int Npts, double *vect, int mode, int N, char *type) {
+
+  float *values;
+  int i;
+
+  if (Npts < 1) return;
+
+  ALLOCATE (values, float, Npts);
+  for (i = 0; i < Npts; i++) {
+    values[i] = vect[i];
+  }
+
+  KapaPlotVector (Xgraph[N], Npts, values, type);
+  free (values);
+  return;
+}
+
+void plot_list (Graphdata *graphdata, double *xlist, double *ylist, int N, char *label, char *file) {
+
+  int i;
+  StatType stats;
+  
+  stats.min = stats.max = xlist[0];
+  for (i = 0; i < N; i++) {
+    stats.min = MIN (stats.min, xlist[i]);
+    stats.max = MAX (stats.max, xlist[i]);
+  }
+  if (graphdata[0].xmin == dUNDEF) graphdata[0].xmin = 1.05*stats.min - 0.05*stats.max;
+  if (graphdata[0].xmax == dUNDEF) graphdata[0].xmax = 1.05*stats.max - 0.05*stats.min;
+
+  stats.min = stats.max = ylist[0];
+  for (i = 0; i < N; i++) {
+    stats.min = MIN (stats.min, ylist[i]);
+    stats.max = MAX (stats.max, ylist[i]);
+  }
+  if (graphdata[0].ymin == dUNDEF) graphdata[0].ymin = 1.05*stats.min - 0.05*stats.max;
+  if (graphdata[0].ymax == dUNDEF) graphdata[0].ymax = 1.05*stats.max - 0.05*stats.min;
+
+  PrepPlotting (N, graphdata, 0);
+  PlotVector (N, xlist, 0, 0, "x");
+  PlotVector (N, ylist, 1, 0, "y");
+  if (label != NULL) PlotLabel (label, 0);
+  DonePlotting (graphdata, 0);
+
+  if ((file != NULL) && SAVEPLOT) JpegPlot (graphdata, 0, file);
+  if (PLOTDELAY > 500000) {
+    fprintf (stdout, "press return\n"); 
+    fscanf (stdin, "%*c");
+  } else {
+    usleep (PLOTDELAY);
+  }
+}
+
+void plot_defaults (Graphdata *graphdata) {
+
+  graphdata[0].style = 2;
+  graphdata[0].ptype = 2;
+  graphdata[0].ltype = 0;
+  graphdata[0].etype = 0;
+  graphdata[0].color = black;
+  graphdata[0].lweight = 0;
+  graphdata[0].size = 0.5;
+
+  graphdata[0].xmin = dUNDEF;
+  graphdata[0].xmax = dUNDEF;
+  graphdata[0].ymin = dUNDEF;
+  graphdata[0].ymax = dUNDEF;
+   
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/relastro.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/relastro.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/relastro.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include "relastro.h"
+
+int main (int argc, char **argv) {
+
+  int status, Ncatalog;
+  Catalog *catalog;
+  FITS_DB db;
+
+  SkyList *skylist = NULL;
+
+  /* get configuration info, args */
+  initialize (argc, argv);
+
+  /* the object analysis is a separate process iterating over catalogs */
+  if (FIT_TARGET == TARGET_OBJECTS) {
+    relastro_objects ();
+    exit (0);
+  }
+
+  /* register database handle with shutdown procedure */
+  set_db (&db);
+
+  /* lock and load the image db table */
+  status = dvo_image_lock (&db, ImageCat, 60.0, (UPDATE ? LCK_XCLD : LCK_SOFT));
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+  if (db.dbstate == LCK_EMPTY) Shutdown ("ERROR: No images in catalog %s (1)", db.filename);
+  if (!dvo_image_load (&db, VERBOSE, FALSE)) Shutdown ("can't read image catalog %s", db.filename);
+
+  /* load regions and images based on specified sky patch (default depth) */
+  skylist = load_images (&db, &UserPatch);
+
+  /* load catalog data from region files : subselect high-quality measurements */
+  catalog = load_catalogs (skylist, &Ncatalog, TRUE);
+
+  /* match measurements with images */
+  initImageBins (catalog, Ncatalog);
+  findImages (catalog, Ncatalog);
+
+  if (PLOTSTUFF) {
+    // plot_star_coords (catalog, Ncatalog);
+    // plot_mosaic_fields (catalog);
+  }
+
+  /* major modes */
+  switch (FIT_TARGET) {
+    case TARGET_SIMPLE:
+      UpdateSimple (catalog, Ncatalog);
+      break;
+
+    case TARGET_CHIPS:
+      UpdateChips (catalog, Ncatalog);
+      break;
+
+    case TARGET_MOSAICS:
+      UpdateMosaic (catalog, Ncatalog);
+      break;
+
+    default:
+      fprintf (stderr, "programming error at %s:%d", __FILE__, __LINE__);
+      exit (2);
+  }
+
+  if (!UPDATE) exit (0);
+
+  // free the image / measurement pointers
+  freeImageBins (1);
+
+  // iterate over catalogs to make detection coordinates consistant
+  UpdateObjectOffsets (skylist);
+
+  // save the updated image parameters
+  dvo_image_update (&db, VERBOSE);
+  dvo_image_unlock (&db); 
+
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/relastro_objects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/relastro_objects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/relastro_objects.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "relastro.h"
+
+int relastro_objects () {
+
+  int i, j, k, m;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+  Catalog catalog;
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // determine the populated SkyRegions overlapping the requested area (default depth)
+  skylist = SkyListByPatch (sky, -1, &UserPatch);
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set up the basic catalog info
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (!catalog.Naves_disk) {
+      if (VERBOSE) fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // XXX consider what gets reset (only ASTROM flags)
+    if (RESET) {
+      for (j = 0; j < catalog.Naverage; j++) {
+	catalog.average[j].code = 0;
+	m = catalog.average[j].measureOffset;
+	for (k = 0; k < catalog.average[j].Nmeasure; k++) {
+	  catalog.measure[m+k].dbFlags = 0;
+	}
+      }
+    }
+
+    UpdateObjects (&catalog, 1);
+
+    if (!UPDATE) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+    
+    save_catalogs (&catalog, 1);
+  }
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/save_catalogs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/save_catalogs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/save_catalogs.c	(revision 22322)
@@ -0,0 +1,15 @@
+# include "relastro.h"
+
+void save_catalogs (Catalog *catalog, int Ncatalog) {
+
+  int i;
+
+  /* load data from each region file */
+  for (i = 0; i < Ncatalog; i++) {
+
+    if (VERBOSE) fprintf (stderr, "saving catalog %s\n", catalog[i].filename);
+    dvo_catalog_save (&catalog[i], VERBOSE); 
+    dvo_catalog_unlock (&catalog[i]);
+    dvo_catalog_free (&catalog[i]);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/select_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/select_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/select_images.c	(revision 22322)
@@ -0,0 +1,254 @@
+# include "relastro.h"
+
+/* this function returns a list of all images which overlap the given SkyList (set of
+   SkyRegions).  All images in the image catalog are tested once, so there is no check that an
+   image already has been included.  LineNum stores the locations in the Image database of the
+   list of images */
+
+typedef struct {
+  double Xc[5];
+  double Yc[5];
+  double Rc;
+  double Dc;
+} SkyRegionCoords;
+
+Image *select_images (SkyList *skylist, Image *timage, int Ntimage, int **LineNumber, int *Nimage) {
+  
+  Image *image;
+  int i, j, k, m, found, nimage, NIMAGE;
+  int InRange;
+  double Ri[5], Di[5], Xi[5], Yi[5], dx, dy;
+  int *line_number;
+  Coords tcoords;
+  SkyRegionCoords *skycoords;
+  
+  if (skylist[0].Nregions < 1) {
+    *Nimage = 0;
+    *LineNumber = NULL;
+    if (VERBOSE) fprintf (stderr, "no matching sky regions\n");
+    return NULL;
+  }
+
+  // the comparison is made in the catalog local projection. below we set crval1,2
+  tcoords.crpix1 = tcoords.crpix2 = 0.0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1  = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2  = tcoords.pc2_1 = 0.0;
+  strcpy (tcoords.ctype, "RA---TAN");
+
+  ALLOCATE (skycoords, SkyRegionCoords, skylist[0].Nregions);
+
+  /* compare with each region file */
+  for (i = 0; i < skylist[0].Nregions; i++) { 
+
+    /* we make positional comparisons in the projection of catalog */
+    skycoords[i].Rc = 0.5*(skylist[0].regions[i][0].Rmax + skylist[0].regions[i][0].Rmin);
+    skycoords[i].Dc = 0.5*(skylist[0].regions[i][0].Dmax + skylist[0].regions[i][0].Dmin);
+    tcoords.crval1 = skycoords[i].Rc;
+    tcoords.crval2 = skycoords[i].Dc;
+
+    /* define catalog corners */
+    RD_to_XY (&skycoords[i].Xc[0], &skycoords[i].Yc[0], skylist[0].regions[i][0].Rmin, skylist[0].regions[i][0].Dmin, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[1], &skycoords[i].Yc[1], skylist[0].regions[i][0].Rmax, skylist[0].regions[i][0].Dmin, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[2], &skycoords[i].Yc[2], skylist[0].regions[i][0].Rmax, skylist[0].regions[i][0].Dmax, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[3], &skycoords[i].Yc[3], skylist[0].regions[i][0].Rmin, skylist[0].regions[i][0].Dmax, &tcoords);
+    skycoords[i].Xc[4] = skycoords[i].Xc[0];    
+    skycoords[i].Yc[4] = skycoords[i].Yc[0];    
+
+    dx = 0.02*(skycoords[i].Xc[2] - skycoords[i].Xc[0]);
+    dy = 0.02*(skycoords[i].Yc[2] - skycoords[i].Yc[0]);
+    skycoords[i].Xc[0] -= dx; skycoords[i].Yc[0] -= dy;
+    skycoords[i].Xc[1] += dx; skycoords[i].Yc[1] -= dy;
+    skycoords[i].Xc[2] += dx; skycoords[i].Yc[2] += dy;
+    skycoords[i].Xc[3] -= dx; skycoords[i].Yc[3] += dy;
+    skycoords[i].Xc[4] -= dx; skycoords[i].Yc[4] -= dy;
+  }
+
+  if (VERBOSE) fprintf (stderr, "finding images\n");
+  BuildChipMatch (timage, Ntimage);
+
+  nimage = 0;
+  NIMAGE = 100;
+  ALLOCATE (image, Image, NIMAGE);
+  ALLOCATE (line_number, int, NIMAGE);
+  
+  // go through the complete list of images, selecting ones which overlap any region
+  for (i = 0; i < Ntimage; i++) {
+      
+    /* select images by photcode, or equiv photcode, if specified */
+    if (NphotcodesKeep > 0) {
+      found = FALSE;
+      for (k = 0; (k < NphotcodesKeep) && !found; k++) {
+	if (photcodesKeep[k][0].code == timage[i].photcode) found = TRUE;
+	if (photcodesKeep[k][0].code == GetPhotcodeEquivCodebyCode(timage[i].photcode)) found = TRUE;
+      }
+      if (!found) continue;
+    }
+    if (NphotcodesSkip > 0) {
+      found = FALSE;
+      for (k = 0; (k < NphotcodesSkip) && !found; k++) {
+	if (photcodesSkip[k][0].code == timage[i].photcode) found = TRUE;
+	if (photcodesSkip[k][0].code == GetPhotcodeEquivCodebyCode(timage[i].photcode)) found = TRUE;
+      }
+      if (found) continue;
+    }
+
+    /* exclude images by time */
+    if (TimeSelect) {
+      if (timage[i].tzero < TSTART) continue;
+      if (timage[i].tzero > TSTOP) continue;
+    }
+    
+    if (!FindMosaicForImage (timage, Ntimage, i)) continue;
+
+    /* define image corners */
+    Xi[0] = 0;            Yi[0] = 0;
+    Xi[1] = timage[i].NX; Yi[1] = 0;
+    Xi[2] = timage[i].NX; Yi[2] = timage[i].NY;
+    Xi[3] = 0;            Yi[3] = timage[i].NY;
+    Xi[4] = 0;            Yi[4] = 0;
+    found = FALSE;
+
+    /* transform to ra,dec */
+    for (j = 0; j < 5; j++) {
+      XY_to_RD (&Ri[j], &Di[j], Xi[j], Yi[j], &timage[i].coords);
+    }
+
+    /* compare with each region file */
+    for (m = 0; (m < skylist[0].Nregions) && !found; m++) { 
+
+      /* we make positional comparisons in the projection of catalog */
+      tcoords.crval1 = skycoords[m].Rc;
+      tcoords.crval2 = skycoords[m].Dc;
+
+      /* transform to ra,dec */
+      InRange = TRUE;
+      for (j = 0; (j < 5) && InRange; j++) {
+	InRange = RD_to_XY (&Xi[j], &Yi[j], Ri[j], Di[j], &tcoords);
+      }
+      if (!InRange) continue;
+
+      /* check if image corner inside catalog */
+      for (j = 0; (j < 4) && !found; j++) {
+	found = corner_check (&Xi[j], &Yi[j], &skycoords[m].Xc[0], &skycoords[m].Yc[0]);
+      }
+      /* check if catalog corner inside image */
+      for (j = 0; (j < 4) && !found; j++) {
+	found = corner_check (&skycoords[m].Xc[j], &skycoords[m].Yc[j], &Xi[0], &Yi[0]);
+      }
+      /* check if edges cross */
+      for (j = 0; (j < 4) && !found; j++) {
+	for (k = 0; (k < 4) && !found; k++) {
+	  found = edge_check (&Xi[j], &Yi[j], &skycoords[m].Xc[k], &skycoords[m].Yc[k]);
+	}
+      }
+      if (!found) continue;
+
+      image[nimage] = timage[i]; 
+      /* always allow 'few' images to succeed, if possible */
+      if (image[nimage].code & ID_IMAGE_FEW) { 
+	image[nimage].code &= ~(ID_IMAGE_FEW | ID_IMAGE_POOR);
+      }
+      if (RESET) {
+	// XXX do we need this in relastro?
+	assignMcal (&image[nimage], (double *) NULL, -1);
+	image[nimage].dMcal = NAN;
+	image[nimage].code &= ~ID_IMAGE_POOR;
+      }
+      line_number[nimage] = i;
+      nimage ++;
+      if (nimage == NIMAGE) {
+	NIMAGE += 100;
+	REALLOCATE (image, Image, NIMAGE);
+	REALLOCATE (line_number, int, NIMAGE);
+      }
+    }
+  }
+      
+  if (VERBOSE) fprintf (stderr, "found %d images\n", nimage);
+
+  REALLOCATE (image, Image, MAX (nimage, 1));
+  REALLOCATE (line_number, int, MAX (nimage, 1));
+  free (skycoords);
+
+  *Nimage  = nimage;
+  *LineNumber = line_number;
+  return (image);
+}
+
+/* check if line between points 0 and 1 of x1
+   crosses line between points 0 and 1 of x2 */
+int edge_check (double *x1, double *y1, double *x2, double *y2) {
+
+  double theta1, theta2;
+  double Theta1, Theta2;
+
+  theta1 = opening_angle (x1[0], y1[0], x2[0], y2[0], x1[1], y1[1]); 
+  theta2 = opening_angle (x1[0], y1[0], x2[0], y2[0], x2[1], y2[1]); 
+
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  Theta1 = theta1;
+  Theta2 = theta2;
+  theta1 = opening_angle (x2[0], y2[0], x1[1], y1[1], x2[1], y2[1]); 
+  theta2 = opening_angle (x2[0], y2[0], x1[1], y1[1], x1[0], y1[0]); 
+  
+ 
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+
+/* check if point x1,y1 is in box formed by x2[0-4] */
+int corner_check (double *x1, double *y1, double *x2, double *y2) {
+
+  int i;
+  double theta;
+
+  theta = 0;
+
+  for (i = 0; i < 4; i++) {
+    theta += opening_angle (x2[i], y2[i], x1[0], y1[0], x2[i+1], y2[i+1]); 
+  }
+  if (fabs(theta) > 6) {
+    return (TRUE);
+  } else {
+    return (FALSE);
+  }
+}
+
+/* returns the opening angle between the three points (2 is in middle) 
+   in range -pi to pi */
+
+double opening_angle (double x1, double y1, double x2, double y2, double x3, double y3) {
+
+  double dx1, dy1, dx2, dy2, ct, st, theta;
+
+  dx1 = x1 - x2;
+  dy1 = y1 - y2;
+  
+  dx2 = x3 - x2;
+  dy2 = y3 - y2;
+  
+  ct = (dx1*dx2 + dy1*dy2);
+  st = (dx1*dy2 - dx2*dy1);
+
+  theta = atan2 (st, ct);
+
+  return (theta);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/write_coords.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/write_coords.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relastro/src/write_coords.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "relastro.h"
+
+# define CD_COORDS 1
+
+void write_coords (Header *header, Coords *coords) {
+
+  gfits_modify (header, "CTYPE1",   "%s",  1, "RA---TAN");
+  gfits_modify (header, "CTYPE2",   "%s",  1, "DEC--TAN");
+
+  gfits_modify (header, "CRVAL1",   "%lf", 1, coords[0].crval1);
+  gfits_modify (header, "CRVAL2",   "%lf", 1, coords[0].crval2);  
+
+  gfits_modify (header, "CRPIX1",   "%lf", 1, coords[0].crpix1);
+  gfits_modify (header, "CRPIX2",   "%lf", 1, coords[0].crpix2);
+
+# if (CD_COORDS)  
+  gfits_modify (header, "CD1_1",    "%le", 1, coords[0].pc1_1 * coords[0].cdelt1);
+  gfits_modify (header, "CD2_1",    "%le", 1, coords[0].pc2_1 * coords[0].cdelt1);
+  gfits_modify (header, "CD1_2",    "%le", 1, coords[0].pc1_2 * coords[0].cdelt2);
+  gfits_modify (header, "CD2_2",    "%le", 1, coords[0].pc2_2 * coords[0].cdelt2);
+# else
+  gfits_modify (header, "CDELT1",   "%le", 1, coords[0].cdelt1); 
+  gfits_modify (header, "CDELT2",   "%le", 1, coords[0].cdelt2);
+  gfits_modify (header, "PC001001", "%le", 1, coords[0].pc1_1);
+  gfits_modify (header, "PC001002", "%le", 1, coords[0].pc1_2);
+  gfits_modify (header, "PC002001", "%le", 1, coords[0].pc2_1);
+  gfits_modify (header, "PC002002", "%le", 1, coords[0].pc2_2);
+# endif
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/Makefile	(revision 22322)
@@ -0,0 +1,48 @@
+default: relphot
+help:
+	@echo "make options: relphot (default)"
+
+include ../../Makefile.System
+HOME    =       $(ROOT)/src/relphot
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+relphot: $(BIN)/relphot.$(ARCH)
+install: $(DESTBIN)/relphot
+
+RELPHOT = \
+$(SRC)/ConfigInit.$(ARCH).o	 \
+$(SRC)/GridOps.$(ARCH).o	 \
+$(SRC)/ImageOps.$(ARCH).o	 \
+$(SRC)/MosaicOps.$(ARCH).o	 \
+$(SRC)/SetSignals.$(ARCH).o 	 \
+$(SRC)/Shutdown.$(ARCH).o 	 \
+$(SRC)/StarOps.$(ARCH).o	 \
+$(SRC)/args.$(ARCH).o		 \
+$(SRC)/bcatalog.$(ARCH).o	 \
+$(SRC)/global_stats.$(ARCH).o	 \
+$(SRC)/initialize.$(ARCH).o	 \
+$(SRC)/liststats.$(ARCH).o	 \
+$(SRC)/load_catalogs.$(ARCH).o	 \
+$(SRC)/load_images.$(ARCH).o	 \
+$(SRC)/plot_scatter.$(ARCH).o	 \
+$(SRC)/plotstuff.$(ARCH).o	 \
+$(SRC)/reload_catalogs.$(ARCH).o \
+$(SRC)/relphot.$(ARCH).o	 \
+$(SRC)/relphot_objects.$(ARCH).o	 \
+$(SRC)/select_images.$(ARCH).o	 \
+$(SRC)/setExclusions.$(ARCH).o 	 \
+$(SRC)/setMrelFinal.$(ARCH).o 	 \
+$(SRC)/write_coords.$(ARCH).o
+
+$(RELPHOT): $(INC)/relphot.h
+$(BIN)/relphot.$(ARCH): $(RELPHOT)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,22 @@
+
+- relphot-1-4:
+  * converted to gfits APIs (forces libfits 1.6)
+  * converted to new DVO APIs (forces libdvo 1.3)
+  * allow seletion by RA/DEC limits
+  * removed old, unused code
+  * added time range to time selections
+
+- relphot-1-3:
+  * dropped IMAGE_CATALOG from config
+  * convert to use libkapa
+  * dropped _PS from average.R,D,M,dM
+  * convert to dvo_image_lock,unlock
+
+- relphot-1-2: released 2005.10.20
+  * substantial changes to sync with addstar v1.3
+    (added mode/format support, new dvo load functions, etc).
+  * various cleanups (functions moved to libohana)
+  * changes to use new libohana (v1.5) / libfits (v1.4)
+
+- relphot-1-1
+  * minor cleanup, loneos.h -> dvo.h
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/doc/notes.txt	(revision 22322)
@@ -0,0 +1,135 @@
+
+2006.10.17
+
+  I am working on a partial upgrade of relphot to handle more general
+  problems than I have treated in the past.  In particular, I would
+  like to be able to have it assign the average magnitudes, regardless
+  of whether any or all images and/or objects are well-treated for
+  relative photometry.  In fact, I would like it to be able to
+  determine average magnitudes even for sources which have only
+  externally supplied data, and thus have no matching images.  I would
+  eventually like to have more flexibility about the filtering which
+  is performed on the measurements to determine the average
+  magnitudes.  Some of this work will require a bit of a more careful
+  treatment of the process.  
+
+  At the moment, we can consider the relphot process to consist of the
+  following steps:
+
+  1) load the complete catalog data
+  2) select a subset of the sources:
+     - appropriate photcode (equiv == selected photcode)
+     - in time range, if specified
+     - appropriate dophot type, if specified
+     - bright enough:
+       - mag < MAG_LIM	
+       - dMag > SIGMA_LIM
+       - ImagMin < iMag < ImagMax
+     - other restrictions
+       - within valid image region for chip
+  3) iterate to a solution for Mcal (image offsets)
+     - mark specific images as bad
+     - mark specific sources as bad
+     - mark specific measurements as bad
+     - include desired restrictions on Mcal (eg, all chips of mosaic
+       matched)
+     - only keep the image offsets
+
+  4) if the results are to be kept, recalculate the average magnitudes
+     using the image exclusions and Mcal values determined above 
+
+2006.05.03
+
+  I am upgrading relphot to accept as input an arbitrary region (using
+  the standard ohana SkyRegion concept of a RA & DEC bounded patch on
+  the sky).  This modification has a few implications.  First, the
+  analysis is in fact performed on the populated SkyRegions which
+  overlap the requested area on the sky.
+
+- select different calibration options
+- plot various terms
+
+- rationalize Mmos & Mcal (sum(Mcal) -> Mmos)
+
+- options:
+
+  -update:  update phot database 
+  -mosaic:  apply per-mosaic corrections
+  -grid:    apply X,Y-based offset
+  -chips:   apply per-chip corrections
+  -trange:  select images in time range
+  
+
+find_images: selects images in region, filtered by photcode, time range
+
+bcatalog:    selects stars & measurments, filtered by mag limit, photcode, time range
+
+image:  a single ccd
+mosaic: a collection of associate ccds (mosaiclink[image])
+grid:   x,y magnitude correction grid across mosaic 
+fit:    2-d fit of mag offsets across field
+
+
+
+m = Mrel + Mcal + Mmos + Mxy + Mgrid
+
+Mrel  (average)
+Mcal  (image)
+Mmos  (mosaic)
+Mxy   (Xi, Yi) (Xi,Yi per image ??)
+Mgrid (Xm, Ym)
+
+
+ measure -> average           catalog[i].measure[j].averef 
+ average -> measure           catalog[i].average[j].offset, catalog[i].average[j].Nm
+
+ measure -> image             image:  image[cat, meas]
+ image   -> measure           image:  catlist[image], measlist[image]
+
+ measure -> mosaic            mosaic: mosaic[cat, meas]
+ mosaic  -> measure	      mosaic: catlist[mosaic], measlist[mosaic]
+
+ measure -> gridbin           grid:   bin[cat, meas]
+ gridbin -> measure           grid:   catlist[bin], measlist[bin]
+
+ measure -> Xi, Yi -> Mxy     
+
+
+getMmos:
+
+ for (i = 0; i < Nmosaic; i++) {
+
+   for (j = 0; j < Nmeasure[i]; j++) {
+      cat  = catlist[i][j]
+      meas = measlist[i][j]
+
+      M = catalog[cat].measure[meas].M
+      Mcal  = applyMcal  (cat, meas)
+      Mrel  = applyMrel  (cat, meas)
+      Mxy   = applyMxy   (cat, meas)
+      Mgrid = applyMgrid (cat, meas)
+
+      list[n] = M - Mcal - Mrel - Mxy - Mgrid
+    }
+  }
+
+
+
+excluding data points:
+
+star:   N < 2
+image:  
+
+
+--
+
+MAG_LIM      : keep stars brighter than MAG_LIM (Mcat)
+SIGMA_LIM    : keep stars with error < SIGMA_LIM
+STAR_SCATTER : exclude stars with dM > STAR_SCATTER
+IMAGE_SCATTER, NIMAGE_SCATTER : define image scatter limits
+
+
+
+load_images - exclude images 
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/include/relphot.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/include/relphot.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/include/relphot.h	(revision 22322)
@@ -0,0 +1,216 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <kapa.h>
+# include <signal.h>
+
+/* # define GRID_V1 */
+# define GRID_V2
+# define NO_IMAGE -100
+
+# if (0)
+typedef struct {
+  double xmin, xmax, ymin, ymax;
+  int style, ptype, ltype, etype, color;
+  double lweight, size;
+} Graphdata;
+# endif
+
+typedef struct {
+  unsigned int start;
+  unsigned int stop;
+  float Mcal;
+  float dMcal;
+  short Xm;
+  float secz;
+  char code;
+  Coords coords;
+} Mosaic; 
+
+typedef struct {
+  double median;
+  double mean;
+  double sigma;
+  double error;
+  double chisq;
+  double min;
+  double max;
+  double total;
+  int    Nmeas;
+} StatType;
+
+/* global variables set in parameter file */
+char   ImageCat[256];
+char   ImageTemplate[256];
+char   CatTemplate[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char   CameraConfig[256];
+char   SKY_TABLE[256];
+int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+
+double MAG_LIM;
+double SIGMA_LIM;
+double IMAGE_SCATTER;
+double IMAGE_OFFSET;
+double STAR_SCATTER;
+double STAR_CHISQ;
+double MIN_ERROR;
+
+int    VERBOSE;
+
+int    NLOOP;
+int    RESET;
+int    UPDATE;
+int    PLOTSTUFF;
+int    SAVEPLOT;
+int    SHOW_PARAMS;
+char   MOSAICNAME[256];
+char   STATMODE[32];
+int    STAR_BAD;
+int    MEAS_BAD;
+int    STAR_TOOFEW;
+int    IMAGE_TOOFEW;
+double IMAGE_GOOD_FRACTION;
+int    IMAGE_BAD;
+int    FREEZE_IMAGES;
+int    USE_GRID;
+int    PLOTDELAY;
+int    UpdateAverages;
+
+int    RELPHOT_GRID_X;
+int    RELPHOT_GRID_Y;
+int    RELPHOT_GRID_BINNING;
+
+PhotCode      *photcode;
+int            PhotNsec;
+int            PhotSec;
+
+int AreaSelect;
+double AreaXmin, AreaXmax, AreaYmin, AreaYmax;
+
+int ImagSelect, ImagMin, ImagMax;
+
+int DophotSelect, DophotValue;
+
+double  PlotMmin, PlotMmax, PlotdMmin, PlotdMmax;
+enum {black, white, red, orange, yellow, green, blue, indigo, violet};
+
+int TimeSelect;
+time_t TSTART, TSTOP;
+
+SkyRegion UserPatch;
+int UserPatchSelect;
+
+# ifdef GRID_V1
+int setGridMeasure (int meas, int cat, double X, double Y);
+# endif
+
+# ifdef GRID_V2
+int setGridMeasure (int meas, int cat, double X, double Y, int ccdnum);
+# endif
+
+/*** relphot prototypes ***/
+void          ConfigInit          PROTO((int *argc, char **argv));
+void          GetConfig           PROTO((char *config, char *field, char *format, int N, void *ptr));
+char         *GetPhotnamebyCode   PROTO((PhotCodeData *photcodes, int code));
+void          InterpolateGrid     PROTO((float *buffer, int Nx, int Ny, Coords *ccd, Coords *gcoords));
+int          *SelectRefMosaic     PROTO((Mosaic **refmosaic, int *Nimage));
+int           args                PROTO((int argc, char **argv));
+int           bcatalog            PROTO((Catalog *subcatalog, Catalog *catalog));
+void          clean_images        PROTO(());
+void          clean_measures      PROTO((Catalog *catalog, int Ncatalog, int final));
+void          clean_mosaics       PROTO(());
+void          clean_stars         PROTO((Catalog *catalog, int Ncatalog));
+int           corner_check        PROTO((double *x1, double *y1, double *x2, double *y2));
+void          dumpGrid            PROTO(());
+void          dump_grid           PROTO(()); 
+int           edge_check          PROTO((double *x1, double *y1, double *x2, double *y2));
+void          findImages          PROTO((Catalog *catalog, int Ncatalog));
+int           findMosaics         PROTO((Catalog *catalog, int Ncatalog));
+
+void set_db (FITS_DB *in);
+int Shutdown (char *format, ...);
+void TrapSignal (int sig);
+void SetProtect (int mode);
+int SetSignals ();
+
+void          freeGridBins        PROTO((int Ncatalog));
+void          freeImageBins       PROTO((int Ncatalog));
+void          freeMosaicBins      PROTO((int Ncatalog));
+void          free_catalogs       PROTO((Catalog *catalog, int Ncatalog));
+int           gcatalog            PROTO((Catalog *catalog, int FINAL));
+Coords       *getCoords           PROTO((int meas, int cat));
+int           getImageEntry       PROTO((int meas, int cat));
+float         getMcal             PROTO((int meas, int cat));
+float         getMgrid            PROTO((int meas, int cat));
+float         getMmos             PROTO((int meas, int cat));
+float         getMrel             PROTO((Catalog *catalog, int meas, int cat));
+Image        *getimage            PROTO((int N));
+Image        *getimages           PROTO((int *N));
+void          global_stats        PROTO((Catalog *catalog, int Ncatalog));
+void          initGrid            PROTO((int dX, int dY));
+void          initGridBins        PROTO((Catalog *catalog, int Ncatalog));
+void          initImageBins       PROTO((Catalog *catalog, int Ncatalog));
+void          initImages          PROTO((Image *input, int N));
+void          initMosaicBins      PROTO((Catalog *catalog, int Ncatalog));
+void          initMosaicGrid      PROTO((Image *image, int Nimage));
+void          initMosaics         PROTO((Image *image, int Nimage));
+void          initMrel            PROTO((Catalog *catalog, int Ncatalog));
+void          initialize          PROTO((int argc, char **argv));
+void          initstats           PROTO((char *mode));
+int           liststats           PROTO((double *value, double *dvalue, int N, StatType *stats));
+Catalog       *load_catalogs      PROTO((SkyList *skylist, int *Ncatalog));
+SkyList      *load_images         PROTO((FITS_DB *db, char *regionName, SkyRegion *region, int RegionSelect));
+Image         *select_images      PROTO((SkyList *skylist, Image *timage, int Ntimage, int **LineNumber, int *Nimage));
+
+int           main                PROTO((int argc, char **argv));
+void          mark_images         PROTO((Image *image, int Nimage, Image *timage, int Ntimage));
+void          matchImage          PROTO((Catalog *catalog, int meas, int cat));
+void          matchMosaics        PROTO((Catalog *catalog, int meas, int cat));
+double        opening_angle       PROTO((double x1, double y1, double x2, double y2, double x3, double y3));
+void          plot_chisq          PROTO((Catalog *catalog, int Ncatalog));
+void          plot_defaults       PROTO((Graphdata *graphdata));
+void          plot_grid           PROTO((Catalog *catalog));
+void          plot_images         PROTO(());
+void          plot_list           PROTO((Graphdata *graphdata, double *xlist, double *ylist, int N, char *label, char *file));
+void          plot_mosaic_fields  PROTO((Catalog *catalog));
+void          plot_mosaics        PROTO(());
+void          plot_scatter        PROTO((Catalog *catalog, int Ncatalog));
+void          plot_star_coords    PROTO((Catalog *catalog, int Ncatalog));
+void          plot_stars          PROTO((Catalog *catalog, int Ncatalog));
+void          reload_catalogs     PROTO((SkyList *skylist));
+int           reload_images       PROTO((FITS_DB *db));
+int           setExclusions       PROTO((Catalog *catalog, int Ncatalog));
+void          setMcal             PROTO((Catalog *catalog, int Poor));
+void          setMcalFinal        PROTO(());
+int           setMcalOutput       PROTO((Catalog *catalog, int Ncatalog));
+void          setMgrid            PROTO((Catalog *catalog));
+int           setMmos             PROTO((Catalog *catalog, int Poor));
+int           setMrel             PROTO((Catalog *catalog, int Ncatalog));
+void          setMrelFinal        PROTO((Catalog *catalog));
+int           setMrelOutput       PROTO((Catalog *catalog, int Ncatalog, int mark));
+int           setMave             PROTO((Catalog *catalog, int Ncatalog));
+void          set_ZP              PROTO((double ZERO));
+int           setrefcode          PROTO((Image *image, int Nimage)); 
+void          skip_measurements   PROTO((Catalog *catalog, int pass));
+void          sortA               PROTO((double *X, int N));
+void          sortB               PROTO((double *X, double *Y, int N));
+void          sortC               PROTO((double *X, double *Y, double *F1, double *F2, int N));
+void          sortD               PROTO((double *X, double *Y, double *Z, int N));
+StatType      statsImageM         PROTO((Catalog *catalog));
+StatType      statsImageN         PROTO((Catalog *catalog));
+StatType      statsImageX         PROTO((Catalog *catalog));
+StatType      statsImagedM        PROTO((Catalog *catalog));
+StatType      statsMosaicM        PROTO((Catalog *catalog));
+StatType      statsMosaicN        PROTO((Catalog *catalog));
+StatType      statsMosaicX        PROTO((Catalog *catalog));
+StatType      statsMosaicdM       PROTO((Catalog *catalog));
+StatType      statsStarN          PROTO((Catalog *catalog, int Ncatalog));
+StatType      statsStarS          PROTO((Catalog *catalog, int Ncatalog));
+StatType      statsStarX          PROTO((Catalog *catalog, int Ncatalog));
+void          wcatalog            PROTO((Catalog *catalog));
+void          wimages             PROTO(());
+void          write_coords        PROTO((Header *header, Coords *coords));
+int relphot_objects ();
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,79 @@
+# include "relphot.h"
+
+void ConfigInit (int *argc, char **argv) {
+
+  double ZERO_POINT;
+  char  *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  GetConfig (config, "MAG_LIM",                "%lf", 0, &MAG_LIM);
+  GetConfig (config, "SIGMA_LIM",              "%lf", 0, &SIGMA_LIM);
+  GetConfig (config, "STAR_SCATTER",           "%lf", 0, &STAR_SCATTER);
+  GetConfig (config, "IMAGE_SCATTER",          "%lf", 0, &IMAGE_SCATTER);
+  GetConfig (config, "IMAGE_OFFSET",           "%lf", 0, &IMAGE_OFFSET);
+
+  GetConfig (config, "STAR_CHISQ",             "%lf", 0, &STAR_CHISQ);
+  GetConfig (config, "STAR_TOOFEW",            "%d",  0, &STAR_TOOFEW);
+  GetConfig (config, "IMAGE_TOOFEW",           "%d",  0, &IMAGE_TOOFEW);
+  GetConfig (config, "IMAGE_GOOD_FRACTION",    "%lf", 0, &IMAGE_GOOD_FRACTION);
+
+  GetConfig (config, "GSCFILE",                "%s",  0, GSCFILE);
+  GetConfig (config, "CATDIR",                 "%s",  0, CATDIR);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig (config, "PHOTCODE_FILE",         	"%s",  0, MasterPhotcodeFile);
+
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = 2;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+
+  GetConfig (config, "ZERO_PT",                "%lf", 0, &ZERO_POINT);
+
+  GetConfig (config, "RELPHOT_GRID_X",         "%d",  0, &RELPHOT_GRID_X);
+  GetConfig (config, "RELPHOT_GRID_Y",         "%d",  0, &RELPHOT_GRID_Y);
+  GetConfig (config, "RELPHOT_GRID_BINNING",   "%d",  0, &RELPHOT_GRID_BINNING);
+  GetConfig (config, "CAMERA_CONFIG",          "%s",  0, CameraConfig);
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+  SetZeroPoint (ZERO_POINT);
+
+  free (config);
+  free (file);
+
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/GridOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/GridOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/GridOps.c	(revision 22322)
@@ -0,0 +1,583 @@
+# include "relphot.h"
+
+static int     Ngrid;
+static float   *gridM;
+static float   *gridS;
+static int     *gridN;
+static int      gridX;
+static int      gridY;
+
+static int    **bin;
+static int    **Xmeas;
+static int    **Ymeas;
+
+static int    **clist;
+static int    **mlist;
+static int     *Nlist;
+static int     *NLIST;
+
+static struct {
+  int Nchip;
+  int Mx, My;  /* mosaic size in chips */
+  int Nx, Ny;  /* chip size in pixels */
+  int *Fx, *Fy;  /* chip flip */
+  int *Ox, *Oy;  /* chip offset */
+  char **ccdname;
+} camera;    
+
+# ifdef GRID_V1
+void initGrid (int dX, int dY) {
+
+  int i;
+
+  /* define mosaic 2d correction grid */
+  gridX = dX / RELPHOT_GRID_BINNING + 1;
+  gridY = dY / RELPHOT_GRID_BINNING + 1;
+  Ngrid = gridX * gridY;
+
+  ALLOCATE (gridM, float, Ngrid);
+  ALLOCATE (gridS, float, Ngrid);
+  ALLOCATE (gridN, int,   Ngrid);
+  bzero (gridM, Ngrid*sizeof(float));
+  bzero (gridS, Ngrid*sizeof(float));
+  bzero (gridN, Ngrid*sizeof(int));
+
+}
+# endif
+
+# ifdef GRID_V2
+void initGrid (int dX, int dY) {
+
+  int i;
+  char field[64], *config, line[256];
+
+  /* load camera config file */
+  config = LoadConfigFile (CameraConfig);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find camera config file %s\n", CameraConfig);
+    exit (1);
+  }
+
+  /* load basic mosaic parameters */
+  ScanConfig (config, "NCCD", "%d", 1, &camera.Nchip);
+  ScanConfig (config, "MOSAIC_X", "%d", 1, &camera.Mx);
+  ScanConfig (config, "MOSAIC_Y", "%d", 1, &camera.My);
+  ScanConfig (config, "NAXIS1", "%d", 1, &camera.Nx);
+  ScanConfig (config, "NAXIS2", "%d", 1, &camera.Ny);
+
+  ALLOCATE (camera.Fx, int, camera.Nchip);
+  ALLOCATE (camera.Fy, int, camera.Nchip);
+  ALLOCATE (camera.Ox, int, camera.Nchip);
+  ALLOCATE (camera.Oy, int, camera.Nchip);
+  ALLOCATE (camera.ccdname, char *, camera.Nchip);
+
+  /* load per-chip parameters */
+  for (i = 0; i < camera.Nchip; i++) {
+    ALLOCATE (camera.ccdname[i], char, 256);
+    sprintf (field, "CCD.%d", i);
+    ScanConfig (config, field, "%s", 1, line);
+    sscanf (line, "%s %d %d %d %d", camera.ccdname[i], &camera.Ox[i], &camera.Oy[i], &camera.Fx[i], &camera.Fy[i]);
+  }
+  free (config);
+
+  /* define mosaic 2d correction grid: 
+   * GRID_X is the number of grid pixels per chip in the X direction */
+  gridX = RELPHOT_GRID_X * camera.Mx;
+  gridY = RELPHOT_GRID_Y * camera.My;
+  Ngrid = gridX * gridY;
+
+  ALLOCATE (gridM, float, Ngrid);
+  ALLOCATE (gridS, float, Ngrid);
+  ALLOCATE (gridN, int,   Ngrid);
+  bzero (gridM, Ngrid*sizeof(float));
+  bzero (gridS, Ngrid*sizeof(float));
+  bzero (gridN, Ngrid*sizeof(int));
+
+}
+# endif
+
+void initGridBins (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+
+  if (!USE_GRID) return;
+
+  /* define cat,meas -> grid pointers */
+  ALLOCATE (bin, int *, Ncatalog);
+  ALLOCATE (Xmeas, int *, Ncatalog);
+  ALLOCATE (Ymeas, int *, Ncatalog);
+  for (i = 0; i < Ncatalog; i++) {
+    ALLOCATE (bin[i], int, MAX (catalog[i].Nmeasure, 1));
+    ALLOCATE (Xmeas[i], int, MAX (catalog[i].Nmeasure, 1));
+    ALLOCATE (Ymeas[i], int, MAX (catalog[i].Nmeasure, 1));
+    for (j = 0; j < catalog[i].Nmeasure; j++) bin[i][j] = -1;
+  }
+
+  /* define grid -> cat,meas pointers */
+  ALLOCATE (Nlist, int, Ngrid);
+  ALLOCATE (NLIST, int, Ngrid);
+  ALLOCATE (clist, int *, Ngrid);
+  ALLOCATE (mlist, int *, Ngrid);
+
+  for (i = 0; i < Ngrid; i++) {
+    Nlist[i] = 0;
+    NLIST[i] = 100;
+    ALLOCATE (clist[i], int, NLIST[i]);
+    ALLOCATE (mlist[i], int, NLIST[i]);
+  }
+}
+
+void freeGridBins (int Ncatalog) {
+
+  int i;
+
+  if (!USE_GRID) return;
+
+  /* define cat,meas -> grid pointers */
+  for (i = 0; i < Ncatalog; i++) {
+    free (bin[i]);
+    free (Xmeas[i]);
+    free (Ymeas[i]);
+  }
+  free (bin);
+  free (Xmeas);
+  free (Ymeas);
+
+  /* define grid -> cat,meas pointers */
+  for (i = 0; i < Ngrid; i++) {
+    free (clist[i]);
+    free (mlist[i]);
+  }
+  free (Nlist);
+  free (NLIST);
+  free (clist);
+  free (mlist);
+}
+
+# ifdef GRID_V1
+int setGridMeasure (int meas, int cat, double X, double Y) {
+
+  int ix, iy, i;
+
+  ix = X / RELPHOT_GRID_BINNING;
+  iy = Y / RELPHOT_GRID_BINNING;
+  if (ix < 0) goto escape;
+  if (iy < 0) goto escape;
+  if (ix >= gridX) goto escape;
+  if (iy >= gridY) goto escape;
+
+  i = ix + iy*gridX;
+
+  bin[cat][meas] = i;
+  Xmeas[cat][meas] = X;
+  Ymeas[cat][meas] = Y;
+  clist[i][Nlist[i]] = cat;
+  mlist[i][Nlist[i]] = meas;
+
+  Nlist[i] ++;
+  if (Nlist[i] == NLIST[i]) {
+    NLIST[i] += 100;
+    REALLOCATE (clist[i], int, NLIST[i]);
+    REALLOCATE (mlist[i], int, NLIST[i]);
+  }	
+  return (TRUE);
+
+escape:
+  fprintf (stderr, "error: star out of grid\n");
+  exit (1);
+}
+# endif
+
+# ifdef GRID_V2
+int setGridMeasure (int meas, int cat, double X, double Y, int ccdnum) {
+
+  int ix, iy, Cx, Cy, i;
+  double x, y;
+
+  /* X, Y are chip coords on chip ccdnum */
+
+  /* normalize X & Y */
+  x = X;
+  if (camera.Fx[ccdnum]) x = camera.Nx - X;
+  y = Y;
+  if (camera.Fy[ccdnum]) y = camera.Ny - Y;
+
+  /* grid coords on the chip */
+  Cx = MIN (MAX ((x / camera.Nx) * RELPHOT_GRID_X, 0), RELPHOT_GRID_X - 1);
+  Cy = MIN (MAX ((y / camera.Ny) * RELPHOT_GRID_Y, 0), RELPHOT_GRID_Y - 1);
+  
+  /* coordinates in the grid */
+  ix = Cx + camera.Ox[ccdnum]*RELPHOT_GRID_X;
+  iy = Cy + camera.Oy[ccdnum]*RELPHOT_GRID_Y;
+
+  i = ix + iy*gridX;
+
+  bin[cat][meas] = i;
+  Xmeas[cat][meas] = x + camera.Ox[ccdnum]*camera.Nx;
+  Ymeas[cat][meas] = Y + camera.Oy[ccdnum]*camera.Ny;
+  clist[i][Nlist[i]] = cat;
+  mlist[i][Nlist[i]] = meas;
+
+  Nlist[i] ++;
+  if (Nlist[i] == NLIST[i]) {
+    NLIST[i] += 100;
+    REALLOCATE (clist[i], int, NLIST[i]);
+    REALLOCATE (mlist[i], int, NLIST[i]);
+  }	
+  return (TRUE);
+
+  fprintf (stderr, "error: star out of grid\n");
+  exit (1);
+}
+# endif
+
+float getMgrid (int meas, int cat) {
+
+  int i;
+  float value;
+
+  if (!USE_GRID) return (0);
+  i = bin[cat][meas];
+  if (i == -1) return (NAN);
+
+  value = gridM[i];
+  return (value);
+}
+
+/* determine Mgrid values for all grid bins */
+void setMgrid (Catalog *catalog) {
+
+  int i, j, m, c, n, N, Nmax;
+  double *list, *dlist;
+  float Msys, Mrel, Mcal, Mmos;
+  StatType stats;
+  
+  if (!USE_GRID) return;
+
+  Nmax = Nlist[0];
+  for (i = 0; i < Ngrid; i++) {
+    Nmax = MAX (Nmax, Nlist[i]);
+  }
+  ALLOCATE (list, double, Nmax);
+  ALLOCATE (dlist, double, Nmax);
+
+  for (i = 0; i < Ngrid; i++) {
+
+    N = 0;
+    for (j = 0; j < Nlist[i]; j++) {
+      
+      m = mlist[i][j];
+      c = clist[i][j];
+      
+      if (catalog[c].measure[m].dbFlags & MEAS_BAD) continue;
+      Mcal = getMcal  (m, c);
+      if (isnan(Mcal)) continue;
+      Mmos = getMmos  (m, c);
+      if (isnan(Mmos)) continue;
+      Mrel  = getMrel  (catalog, m, c);
+      if (isnan(Mrel)) continue;
+      
+      n = catalog[c].measure[m].averef;
+      Msys = PhotSys (&catalog[c].measure[m], &catalog[c].average[n], &catalog[c].secfilt[n*PhotNsec]);
+      list[N] = Msys - Mrel - Mcal - Mmos;
+      dlist[N] = MAX (catalog[c].measure[m].dM, MIN_ERROR);
+      N++;
+    }
+
+    liststats (list, dlist, N, &stats);
+    gridM[i] = stats.mean;
+    gridS[i] = stats.sigma;
+    gridN[i] = N;
+  }
+  free (list);
+  free (dlist);
+}
+
+void plot_grid (Catalog *catalog) {
+
+  int i, j, m, c, n, N, Narea;
+  float Msys, Mrel, Mcal, Mmos;
+  double *xlist, *Mlist, *dlist, *ylist;
+  Graphdata graphdata;
+
+  if (!USE_GRID) return;
+
+  N = 0;
+  for (i = 0; i < Ngrid; i++) 
+    N += Nlist[i];
+
+  ALLOCATE (xlist, double, N);
+  ALLOCATE (ylist, double, N);
+  ALLOCATE (Mlist, double, N);
+  ALLOCATE (dlist, double, N);
+
+  Narea = 0;
+  N = 0;
+  for (i = 0; i < Ngrid; i++) {
+    for (j = 0; j < Nlist[i]; j++) {
+      
+      m = mlist[i][j];
+      c = clist[i][j];
+      
+      if (catalog[c].measure[m].dbFlags & MEAS_BAD) {
+	Narea ++;
+	continue;
+      }
+      Mcal  = getMcal  (m, c);
+      if (isnan(Mcal)) continue;
+      Mmos  = getMmos  (m, c);
+      if (isnan(Mmos)) continue;
+      Mrel  = getMrel  (catalog, m, c);
+      if (isnan(Mrel)) continue;
+
+      n = catalog[c].measure[m].averef;
+      Msys = PhotSys (&catalog[c].measure[m], &catalog[c].average[n], &catalog[c].secfilt[n*PhotNsec]);
+
+      xlist[N] = Xmeas[c][m];
+      ylist[N] = Ymeas[c][m];
+      Mlist[N] = Msys - Mrel - Mcal - Mmos;
+      dlist[N] = Msys - Mrel - Mcal - Mmos - gridM[i];
+      N++;
+    }
+  }
+
+  fprintf (stderr, "skipped %d meas for area\n", Narea);
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, Mlist, N, "X vs dM raw", "XdM.png");
+  plot_list (&graphdata, xlist, dlist, N, "X vs dM corrected", "XdMf.png");
+  plot_list (&graphdata, ylist, dlist, N, "Y vs dM corrected", "YdMf.png");
+
+  plot_defaults (&graphdata);
+  plot_list (&graphdata, xlist, ylist, N, "X vs Y", "XY.png");
+
+  free (ylist);
+  free (xlist);
+  free (Mlist);
+  free (dlist);
+
+}
+
+void dump_grid () { 
+
+  int i, j, Nimage;
+  int *imlist;
+  FILE *f;
+  Header header, theader;
+  Matrix matrix;
+  Mosaic *refmosaic;
+    
+  /* select reference mosaic image */
+  imlist = SelectRefMosaic (&refmosaic, &Nimage);
+
+  /* we are writing to this file */
+  f = fopen ("mosaic.fits", "w");
+  if (f == (FILE *) NULL) { 
+    fprintf (stderr, "cannot open %s for output\n", "mosaic.fits");
+    return;
+  }
+
+  /* create empty phu */
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_modify (&header, "NEXTEND", "%d", 1, Nimage + 3);
+  gfits_modify (&header, "FILTER", "%s", 1, photcode[0].name);
+  gfits_modify (&header, "COMMENT", "%S", 1, "Mosaic Photometry Grid Analysis");
+  gfits_fwrite_header (f, &header);
+  gfits_fwrite_matrix (f, &matrix);
+  gfits_free_matrix (&matrix);
+
+  /* save grid mag values */
+  gfits_init_header (&theader);
+  theader.Naxes = 2;
+  theader.Naxis[0] = gridX;
+  theader.Naxis[1] = gridY;
+  theader.bitpix   = -32;
+  gfits_create_Theader (&theader, "IMAGE");
+  gfits_modify (&theader, "FILTER", "%s", 1, photcode[0].name);
+  gfits_modify (&theader, "EXTNAME", "%s", 1, "MAG_OFFSET");
+  gfits_create_matrix  (&theader, &matrix);
+  for (i = 0; i < gridX; i++) {
+    for (j = 0; j < gridY; j++) {
+      gfits_set_matrix_value (&matrix, i, j, (double) gridM[i + j*gridX]);
+    }
+  }
+  write_coords (&theader, &refmosaic[0].coords);
+  gfits_fwrite_header (f, &theader);
+  gfits_fwrite_matrix (f, &matrix);
+  gfits_free_matrix (&matrix);
+
+  /* save grid Nmeas values */
+  gfits_modify (&theader, "EXTNAME", "%s", 1, "NMEAS");
+  gfits_modify (&theader, "FILTER", "%s", 1, photcode[0].name);
+  gfits_create_matrix  (&theader, &matrix);
+  for (i = 0; i < gridX; i++) {
+    for (j = 0; j < gridY; j++) {
+      gfits_set_matrix_value (&matrix, i, j, (double) gridN[i + j*gridX]);
+    }
+  }
+  write_coords (&theader, &refmosaic[0].coords);
+  gfits_fwrite_header (f, &theader);
+  gfits_fwrite_matrix (f, &matrix);
+  gfits_free_matrix (&matrix);
+
+  /* save grid sigma values */
+  gfits_modify (&theader, "EXTNAME", "%s", 1, "SIGMA");
+  gfits_modify (&theader, "FILTER", "%s", 1, photcode[0].name);
+  gfits_create_matrix  (&theader, &matrix);
+  for (i = 0; i < gridX; i++) {
+    for (j = 0; j < gridY; j++) {
+      gfits_set_matrix_value (&matrix, i, j, (double) gridS[i + j*gridX]);
+    }
+  }
+  write_coords (&theader, &refmosaic[0].coords);
+  gfits_fwrite_header (f, &theader);
+  gfits_fwrite_matrix (f, &matrix);
+  gfits_free_matrix (&matrix);
+
+# ifdef GRID_V1
+  /* calculate pixel values for each CCD pixel, write out CCD images */
+  /* grid pixels are in RA,DEC coords, transform to image and interpolate */
+  for (i = 0; i < Nimage; i++) {
+    image = getimage (imlist[i]);
+    pname = GetPhotcodeNamebyCode (image[0].photcode);
+
+    /* this is kind of bogus... */
+    /* pname is CAMERA.FILTER.CCD, grab the CCD */
+    p = strrchr (pname, '.');
+    if (p == (char *) NULL) {
+      fprintf (stderr, "error parsing photcode %s\n", pname);
+      exit (2);
+    }
+    p++;
+    sprintf (ccdname, "ccd%s", p);
+
+    gfits_modify (&theader, "EXTNAME", "%s", 1, ccdname);
+    gfits_modify (&theader, "FILTER", "%s", 1, photcode[0].name);
+    gfits_modify (&theader, "PHOTCODE", "%s", 1, pname);
+    gfits_modify (&theader, "NX", "%d", 1, image[i].NX);
+    gfits_modify (&theader, "NY", "%d", 1, image[i].NY);
+    write_coords (&theader, &image[0].coords);
+
+    Nx = 2 * image[i].NX / RELPHOT_GRID_BINNING;
+    Ny = 2 * image[i].NY / RELPHOT_GRID_BINNING;
+    theader.Naxis[0] = Nx;
+    theader.Naxis[1] = Ny;
+    gfits_modify (&theader, "NAXIS1", "%d", 1, Nx);
+    gfits_modify (&theader, "NAXIS2", "%d", 1, Ny);
+    gfits_create_matrix  (&theader, &matrix);
+
+    InterpolateGrid ((float *)matrix.buffer, Nx, Ny, &image[0].coords, &refmosaic[0].coords);
+    gfits_fwrite_header (f, &theader);
+    gfits_fwrite_matrix (f, &matrix);
+    gfits_free_matrix (&matrix);
+  }
+# endif
+
+# ifdef GRID_V2
+  /* calculate value for each CCD pixel, write out CCD images */
+  /* grid pixels are tied to detector pixels, but are flipped to match focal plane */
+  for (i = 0; i < camera.Nchip; i++) {
+    int ix, iy, x, y, X, Y, bin;
+
+    gfits_modify (&theader, "EXTNAME", "%s", 1, camera.ccdname[i]);
+    gfits_modify (&theader, "FILTER", "%s", 1, photcode[0].name);
+    gfits_modify (&theader, "NX", "%d", 1, camera.Nx);
+    gfits_modify (&theader, "NY", "%d", 1, camera.Ny);
+      
+    theader.Naxis[0] = RELPHOT_GRID_X;
+    theader.Naxis[1] = RELPHOT_GRID_Y;
+    gfits_modify (&theader, "NAXIS1", "%d", 1, RELPHOT_GRID_X);
+    gfits_modify (&theader, "NAXIS2", "%d", 1, RELPHOT_GRID_Y);
+    gfits_create_matrix  (&theader, &matrix);
+
+    for (Y = 0; Y < RELPHOT_GRID_Y; Y++) {
+      for (X = 0; X < RELPHOT_GRID_X; X++) {
+	      
+	/* normalize X & Y */
+	x = X;
+	if (camera.Fx[i]) x = RELPHOT_GRID_X - X - 1;
+	y = Y;
+	if (camera.Fy[i]) y = RELPHOT_GRID_Y - Y - 1;
+	      
+	/* coordinates in the grid */
+	ix = x + camera.Ox[i]*RELPHOT_GRID_X;
+	iy = y + camera.Oy[i]*RELPHOT_GRID_Y;
+	      
+	bin = ix + iy*gridX;
+	gfits_set_matrix_value (&matrix, X, Y, (double) gridM[bin]);
+      }
+    }
+    gfits_fwrite_header (f, &theader);
+    gfits_fwrite_matrix (f, &matrix);
+    gfits_free_matrix (&matrix);
+  }
+# endif
+
+}
+
+void InterpolateGrid (float *buffer, int Nx, int Ny, Coords *ccd, Coords *gcoords) {
+
+  int i, j;
+  double x, y, r, d, X, Y, dx, dy;
+  double V00, V01, V10, V11;
+  double wV00, wV01, wV10, wV11;
+  double dV00, dV01, dV10, dV11;
+  double v1, v2, value;
+  int ix, iy, N;
+
+  for (i = 0; i < Nx; i++) {
+    for (j = 0; j < Ny; j++) {
+      x = i * RELPHOT_GRID_BINNING / 2;
+      y = j * RELPHOT_GRID_BINNING / 2;
+      XY_to_RD (&r, &d, x, y, ccd);
+      RD_to_XY (&X, &Y, r, d, gcoords);
+
+      X = X / RELPHOT_GRID_BINNING;
+      Y = Y / RELPHOT_GRID_BINNING;
+
+      ix = (int) X;
+      dx = X - ix;
+      iy = (int) Y;
+      dy = Y - iy;
+
+      if (ix < 0) continue;
+      if (iy < 0) continue;
+      if (ix >= gridX) continue;
+      if (iy >= gridY) continue;
+
+      N = ix + iy*gridX;
+      V00 = gridM[N];
+      V10 = gridM[N + 1];
+      V01 = gridM[N + gridX];
+      V11 = gridM[N + gridX + 1];
+
+      dV00 = gridS[N];
+      dV10 = gridS[N + 1];
+      dV01 = gridS[N + gridX];
+      dV11 = gridS[N + gridX + 1];
+
+      wV00 = (dV00 == 0) ? 0.0 : 1 / SQ(dV00);
+      wV01 = (dV01 == 0) ? 0.0 : 1 / SQ(dV01);
+      wV10 = (dV10 == 0) ? 0.0 : 1 / SQ(dV10);
+      wV11 = (dV11 == 0) ? 0.0 : 1 / SQ(dV11);
+
+      v1 = wV00*V00*(1 + dx*dy - dx - dy) +
+	wV10*V10*(dx - dx*dy) +
+	wV01*V01*(dy - dx*dy) +
+	wV11*V11*(dx*dy);
+
+      v2 = wV00*(1 + dx*dy - dx - dy) +
+	wV10*(dx - dx*dy) +
+	wV01*(dy - dx*dy) +
+	wV11*(dx*dy);
+
+      value = v1 / v2;
+      buffer[j*Nx + i] = value;
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/ImageOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/ImageOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/ImageOps.c	(revision 22322)
@@ -0,0 +1,480 @@
+# include "relphot.h"
+
+static unsigned int *start;
+static unsigned int *stop;
+static int         **bin;
+
+static int         **clist;
+static int         **mlist;
+static int          *Nlist;
+static int          *NLIST;
+
+static Image        *image;
+static int          Nimage;
+
+void initImages (Image *input, int N) {
+
+  int i;
+
+  image = input;
+  Nimage = N;
+
+  ALLOCATE (start,   unsigned, Nimage);
+  ALLOCATE (stop,    unsigned, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    start[i] = image[i].tzero - MAX(0.01*image[i].trate*image[i].NY, 1);
+    stop[i]  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+  }
+}
+
+void initImageBins (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+
+  ALLOCATE (bin, int *, Ncatalog);
+  for (i = 0; i < Ncatalog; i++) {
+    ALLOCATE (bin[i], int, MAX (catalog[i].Nmeasure, 1));
+    for (j = 0; j < catalog[i].Nmeasure; j++) bin[i][j] = -1;
+  }
+
+  ALLOCATE (Nlist, int, Nimage);
+  ALLOCATE (NLIST, int, Nimage);
+  ALLOCATE (clist, int *, Nimage);
+  ALLOCATE (mlist, int *, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    Nlist[i] = 0;
+    NLIST[i] = 100;
+    ALLOCATE (clist[i], int, NLIST[i]);
+    ALLOCATE (mlist[i], int, NLIST[i]);
+  }
+}
+
+void freeImageBins (int Ncatalog) {
+
+  int i;
+
+  for (i = 0; i < Ncatalog; i++) {
+    free (bin[i]);
+  }
+  free (bin);
+  for (i = 0; i < Nimage; i++) {
+    free (clist[i]);
+    free (mlist[i]);
+  }
+  free (clist);
+  free (mlist);
+}
+
+/* select all image equivalent to the current photcode */
+void findImages (Catalog *catalog, int Ncatalog) {
+
+  int i, j, ecode;
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Nmeasure; j++) {
+      ecode = GetPhotcodeEquivCodebyCode (catalog[i].measure[j].photcode);
+      if (photcode[0].code != ecode) continue;
+      matchImage (catalog, j, i);
+    }
+  }
+}
+
+/* XXX convert this to use the values of measure->Xccd,Yccd */
+/* XXX need to apply the conversion for ELIXIR and LONEOS formats */
+void matchImage (Catalog *catalog, int meas, int cat) {
+
+  int i, ave, ccdnum;
+  double ra, dec, X, Y;
+  char *pname, *filter, *p, base[256];
+  Measure *measure;
+  
+  measure = &catalog[cat].measure[meas];
+  for (i = 0; i < Nimage; i++) {
+    if (image[0].photcode == -1) continue;
+    if (measure[0].photcode != image[i].photcode) continue;
+    if (measure[0].t < start[i]) continue;
+    if (measure[0].t > stop[i]) continue;
+    
+# ifdef GRID_V2 /* this section is added to support GridOps.v2.c */
+    if (USE_GRID) {
+
+      /* identify the ccd on the basis of the photcode name */
+      pname = GetPhotcodeNamebyCode (image[i].photcode);
+      filter = photcode[0].name;
+      sprintf (base, "%s.%s.", MOSAICNAME, filter);
+      if (strncmp (pname, base, strlen (base))) continue;
+      p = pname + strlen(base);
+      if (*p == 0) continue;
+      ccdnum = atoi (p);
+      /* some ambiguity here between seq number and id number */
+
+      /* add this measurement to the grid cell for this chip */
+      ave = measure[0].averef;
+      ra  = catalog[cat].average[ave].R - measure[0].dR / 3600.0;
+      dec = catalog[cat].average[ave].D - measure[0].dD / 3600.0;
+       
+      /* X,Y always positive-definite in range 0,0 - dX, dY */
+      RD_to_XY (&X, &Y, ra, dec, &image[i].coords);
+      setGridMeasure (meas, cat, X, Y, ccdnum);
+    }
+# endif
+
+    bin[cat][meas] = i;
+
+    clist[i][Nlist[i]] = cat;
+    mlist[i][Nlist[i]] = meas;
+    Nlist[i] ++;
+
+    if (Nlist[i] == NLIST[i]) {
+      NLIST[i] += 100;
+      REALLOCATE (clist[i], int, NLIST[i]);
+      REALLOCATE (mlist[i], int, NLIST[i]);
+    }	
+    return;
+  }
+  /*  fprintf (stderr, "can't find source image for this measurement: %d (%d)\n", measure[0].t, measure[0].photcode); */
+}
+
+int getImageEntry (int meas, int cat) {
+
+  int i;
+
+  i = bin[cat][meas];
+  return (i);
+}
+
+float getMcal (int meas, int cat) {
+
+  int i;
+  float value;
+
+  i = bin[cat][meas];
+  if (i == -1) return (NAN);
+
+  if (image[i].code & IMAGE_BAD) return (NAN);  
+  value = image[i].Mcal;
+  return (value);
+}
+
+Coords *getCoords (int meas, int cat) {
+
+  int i;
+
+  i = bin[cat][meas];
+  if (i == -1) return (NULL);
+  return (&image[i].coords);
+}
+
+/* determine Mcal values for all images */
+void setMcal (Catalog *catalog, int PoorImages) {
+
+  int i, j, m, c, n, N, Nmax, mark, bad;
+  float Msys, Mrel, Mmos, Mgrid;
+  double *list, *dlist;
+  StatType stats;
+
+  if (FREEZE_IMAGES) return;
+
+  if (PoorImages) {
+    IMAGE_BAD = STAR_BAD = MEAS_BAD = 0;
+  }
+
+  Nmax = 0;
+  for (i = 0; i < Nimage; i++) {
+    Nmax = MAX (Nmax, Nlist[i]);
+  }
+  ALLOCATE (list, double, Nmax);
+  ALLOCATE (dlist, double, Nmax);
+
+  for (i = 0; i < Nimage; i++) {
+    
+    /* on PoorImages run, skip good images */
+    if (PoorImages) {
+      bad = image[i].code & (ID_IMAGE_FEW | ID_IMAGE_POOR | ID_IMAGE_SKIP);
+      if (!bad) continue;
+    }      
+
+    N = 0;
+    for (j = 0; j < Nlist[i]; j++) {
+      
+      m = mlist[i][j];
+      c = clist[i][j];
+      
+      if (catalog[c].measure[m].dbFlags & MEAS_BAD) continue;
+      Mmos  = getMmos  (m, c);
+      if (isnan(Mmos)) continue;
+      Mgrid = getMgrid (m, c);
+      if (isnan(Mgrid)) continue;
+      Mrel  = getMrel  (catalog, m, c);
+      if (isnan(Mrel)) continue;
+      
+      n = catalog[c].measure[m].averef;
+      Msys = PhotSys (&catalog[c].measure[m], &catalog[c].average[n], &catalog[c].secfilt[n*PhotNsec]);
+      list[N] = Msys - Mrel - Mmos - Mgrid;
+      dlist[N] = MAX (catalog[c].measure[m].dM, MIN_ERROR);
+      N++;
+    }
+    /* Nlist[i] is all measurements, N is good measurements */
+
+    /* too few good measurements or too many bad measurements */
+    if (!PoorImages) {
+      mark = (N < IMAGE_TOOFEW) || (N < IMAGE_GOOD_FRACTION*Nlist[i]);
+      if (mark) {
+	image[i].code |= ID_IMAGE_FEW;
+      } else {
+	image[i].code &= ~ID_IMAGE_FEW;
+      }      
+    }
+    
+    liststats (list, dlist, N, &stats);
+    image[i].Mcal  = stats.mean;
+    image[i].dMcal = stats.sigma;
+    image[i].Xm    = 100.0*log10(stats.chisq);
+  }
+  free (list);
+  free (dlist);
+  if (PoorImages) {
+    IMAGE_BAD = ID_IMAGE_POOR | ID_IMAGE_FEW | ID_IMAGE_SKIP;
+    STAR_BAD  = ID_STAR_POOR | ID_STAR_FEW;
+    MEAS_BAD  = ID_MEAS_NOCAL | ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM | ID_MEAS_AREA;
+  }
+  return;
+}
+
+/* mark image if: abs(Mcal) too large, dMcal too large */
+void clean_images () {
+
+  int i, N, mark, Nmark;
+  double *mlist, *slist, *dlist;
+  double MaxOffset, MaxScatter, MedOffset;
+  StatType stats;
+
+  if (FREEZE_IMAGES) return;
+
+  if (VERBOSE) fprintf (stderr, "marking poor images\n");
+
+  ALLOCATE (mlist, double, Nimage);
+  ALLOCATE (slist, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+
+  for (i = N = 0; i < Nimage; i++) {
+    if (image[i].code & IMAGE_BAD) continue;
+    mlist[N] = fabs (image[i].Mcal);
+    slist[N] = image[i].dMcal;
+    dlist[N] = 1;
+    N++;
+  }
+  initstats ("MEAN");
+  liststats (mlist, dlist, N, &stats);
+  MaxOffset = MAX (IMAGE_OFFSET, 3*stats.sigma);
+  MedOffset = stats.median;
+  liststats (slist, dlist, N, &stats);
+  MaxScatter = MAX (IMAGE_SCATTER, 2*stats.median);
+  fprintf (stderr, "Mrel: %f, dMrel: %f, Max Scatter: %f, Max Offset: %f\n", MedOffset, stats.median, MaxScatter, MaxOffset);
+  
+  Nmark = 0;
+  for (i = 0; i < Nimage; i++) {
+    mark = FALSE;
+    image[i].code &= ~ID_IMAGE_POOR;
+    mark = (image[i].dMcal > MaxScatter) || (fabs(image[i].Mcal - MedOffset) > MaxOffset);
+    if (mark) { 
+      Nmark ++;
+      image[i].code |= ID_IMAGE_POOR;
+    } else {
+      image[i].code &= ~ID_IMAGE_POOR;
+    }
+  }
+
+  fprintf (stderr, "%d images marked poor\n", Nmark);
+  initstats (STATMODE);
+  free (mlist);
+  free (slist);
+  free (dlist);
+}
+
+void plot_images () {
+
+  int i, bin;
+  double *xlist, *Mlist, *dlist;
+  Graphdata graphdata;
+
+  if (FREEZE_IMAGES) return;
+
+  ALLOCATE (xlist, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+  ALLOCATE (Mlist, double, Nimage);
+
+  /**** dMcal vs airmass ****/
+  for (i = 0; i < Nimage; i++) {
+    Mlist[i] = image[i].Mcal;
+    dlist[i] = image[i].dMcal;
+    xlist[i] = image[i].secz;
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = PlotdMmin; 
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, Mlist, Nimage, "airmass vs Mcal", "airmass.png");
+  plot_defaults (&graphdata);
+  plot_list (&graphdata, Mlist, dlist, Nimage, "Mcal vs dMcal", NULL);
+
+# define NBIN 200
+  REALLOCATE (xlist, double, NBIN);
+  REALLOCATE (Mlist, double, NBIN);
+
+  /**** dMcal histgram ****/
+  for (i = 0; i < NBIN; i++) xlist[i] = 0.00025*i;
+  bzero (Mlist, NBIN*sizeof(double));
+  for (i = 0; i < Nimage; i++) {
+    bin = image[i].dMcal / 0.00025;
+    bin = MAX (0, MIN (NBIN - 1, bin));
+    Mlist[bin] += 1.0;
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.style = 1;
+  plot_list (&graphdata, xlist, Mlist, NBIN, "dMcal hist", "dMcalhist.png");
+
+  free (dlist);
+  free (xlist);
+  free (Mlist);
+}
+
+StatType statsImageN (Catalog *catalog) {
+
+  int i, j, m, c, n, N;
+  double *list, *dlist;
+  float Mcal, Mmos, Mgrid;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (FREEZE_IMAGES) return (stats);
+
+  ALLOCATE (list, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+
+  n = 0;
+  for (i = 0; i < Nimage; i++) {
+    if (image[i].code & IMAGE_BAD)  continue;
+
+    N = 0;
+    for (j = 0; j < Nlist[i]; j++) {
+
+      m = mlist[i][j];
+      c = clist[i][j];
+
+      Mcal  = getMcal  (m, c);
+      if (isnan(Mcal)) continue;
+      Mmos  = getMmos  (m, c);
+      if (isnan(Mmos)) continue;
+      Mgrid = getMgrid (m, c);
+      if (isnan(Mgrid)) continue;
+      N++;
+    }
+    list[n] = N;
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsImageX (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (FREEZE_IMAGES) return (stats);
+
+  ALLOCATE (list, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+
+  n = 0;
+  for (i = 0; i < Nimage; i++) {
+
+    if (image[i].code & IMAGE_BAD)  continue;
+
+    list[n] = pow (10.0, 0.01*image[i].Xm);
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsImageM (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (FREEZE_IMAGES) return (stats);
+
+  ALLOCATE (list, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+
+  n = 0;
+  for (i = 0; i < Nimage; i++) {
+
+    if (image[i].code & IMAGE_BAD)  continue;
+
+    list[n] = image[i].Mcal;
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsImagedM (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (FREEZE_IMAGES) return (stats);
+
+  ALLOCATE (list, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+
+  n = 0;
+  for (i = 0; i < Nimage; i++) {
+
+    if (image[i].code & IMAGE_BAD)  continue;
+
+    list[n] = image[i].dMcal;
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+Image *getimages (int *N) {
+
+  *N = Nimage;
+  return (image);
+}
+
+Image *getimage (int N) {
+  return (&image[N]);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/MosaicOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/MosaicOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/MosaicOps.c	(revision 22322)
@@ -0,0 +1,681 @@
+# include "relphot.h"
+
+Image *getimages (int *N);
+
+static int    Nmosaic;
+static Mosaic *mosaic;
+
+static int    *Nimlist;
+static int   **imlist; /* mosaic -> image[] */
+static int   **bin;    /* catalog, measure -> mosaic */
+
+static int   **clist;  /* mosaic -> catalog[] */
+static int   **mlist;  /* mosiac -> measure[] */
+static int    *Nlist;
+static int    *NLIST;
+
+/* find mosaic frames (unique time periods & photcode name matches mosaic) */
+void initMosaics (Image *image, int Nimage) {
+
+  int i, j, status, found, NMOSAIC, *NIMLIST;
+  unsigned int start, stop;
+  char *pname;
+
+  if (!MOSAICNAME[0]) return;
+
+  Nmosaic = 0;
+  NMOSAIC = 10;
+  ALLOCATE (mosaic, Mosaic, NMOSAIC);
+
+  ALLOCATE (imlist, int *, NMOSAIC);
+  ALLOCATE (Nimlist, int, NMOSAIC);
+  ALLOCATE (NIMLIST, int, NMOSAIC);
+
+  /* generate list of unique mosaics */
+  for (i = 0; i < Nimage; i++) {
+
+    /* select valid mosaic images by photcode */
+    pname = GetPhotcodeNamebyCode (image[i].photcode);
+    status = strncmp (pname, MOSAICNAME, strlen (MOSAICNAME));
+    if (status) continue;
+
+    /* set image time range */
+    start = image[i].tzero - MAX(0.01*image[i].trate*image[i].NY, 1);
+    stop  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+
+    /* find existing mosaic with this time range */
+    found = FALSE;
+    for (j = 0; !found && (j < Nmosaic); j++) { 
+      if (stop  < mosaic[j].start) continue;
+      if (start > mosaic[j].stop)  continue;
+      found = TRUE;
+
+      /* add image to mosaic image list */
+      imlist[j][Nimlist[j]] = i;
+      Nimlist[j] ++;
+      if (Nimlist[j] == NIMLIST[j]) {
+	NIMLIST[j] += 10;
+	REALLOCATE (imlist[j], int, NIMLIST[j]);
+      }
+
+    }
+    if (found) continue;
+    
+    /* a new mosaic, define ranges */
+    mosaic[Nmosaic].start = start;
+    mosaic[Nmosaic].stop  = stop;
+    mosaic[Nmosaic].Mcal  = 0.0;
+    mosaic[Nmosaic].dMcal = 0.0;
+    mosaic[Nmosaic].Xm    = 0.0;
+    mosaic[Nmosaic].code  = image[i].code;
+    mosaic[Nmosaic].secz  = image[i].secz;
+
+    /* add image to mosaic image list */
+    NIMLIST[Nmosaic] = 10;
+    Nimlist[Nmosaic] = 1;
+    ALLOCATE (imlist[Nmosaic], int, NIMLIST[Nmosaic]);
+    imlist[Nmosaic][0] = i;
+
+    Nmosaic ++;
+    if (Nmosaic == NMOSAIC) {
+      NMOSAIC += 10;
+      REALLOCATE (mosaic, Mosaic, NMOSAIC);
+      REALLOCATE (imlist, int *, NMOSAIC);
+      REALLOCATE (Nimlist, int, NMOSAIC);
+      REALLOCATE (NIMLIST, int, NMOSAIC);
+    }
+  }
+
+  initMosaicGrid (image, Nimage);
+  return;
+}
+
+void initMosaicGrid (Image *image, int Nimage) {
+
+  /* find max dR, dD range for all mosaics */
+  /* define mosaic.coords to cover dR, dD */
+  /* send results to initGridBins */
+
+  int i, j, m, NX, NY;
+  int dXmax, dYmax;
+  double dS, dX, dY;
+  double R, D, Rmin, Rmax, Dmin, Dmax;
+  double Mcal, dMcal, Xm;
+
+  dXmax = dYmax = 0.0;
+  for (i = 0; i < Nmosaic; i++) {
+    Dmin = Rmin =  360.0;
+    Dmax = Rmax = -360.0;
+    dS = 0.0;
+    Mcal = dMcal = Xm = 0;
+    for (j = 0; j < Nimlist[i]; j++) {
+      m = imlist[i][j];
+      NX = image[m].NX;
+      NY = image[m].NY;
+      dS += hypot (image[m].coords.cdelt1*image[m].coords.pc1_1, image[m].coords.cdelt1*image[m].coords.pc2_1);
+      XY_to_RD (&R, &D, 0.0, 0.0, &image[m].coords);
+      Rmin = MIN (Rmin, R);
+      Rmax = MAX (Rmax, R);
+      Dmin = MIN (Dmin, D);
+      Dmax = MAX (Dmax, D);
+      XY_to_RD (&R, &D, (double) NX, 0.0, &image[m].coords);
+      Rmin = MIN (Rmin, R);
+      Rmax = MAX (Rmax, R);
+      Dmin = MIN (Dmin, D);
+      Dmax = MAX (Dmax, D);
+      XY_to_RD (&R, &D, (double) NX, (double) NY, &image[m].coords);
+      Rmin = MIN (Rmin, R);
+      Rmax = MAX (Rmax, R);
+      Dmin = MIN (Dmin, D);
+      Dmax = MAX (Dmax, D);
+      XY_to_RD (&R, &D, 0.0, (double) NY, &image[m].coords);
+      Rmin = MIN (Rmin, R);
+      Rmax = MAX (Rmax, R);
+      Dmin = MIN (Dmin, D);
+      Dmax = MAX (Dmax, D);
+      Mcal  += image[m].Mcal;
+      dMcal += image[m].dMcal;
+      Xm    += image[m].Xm;
+      /* we are using mosaic.Mcal, not image.Mcal. reset image.Mcal */
+      image[m].Mcal  = 0.0;
+      image[m].dMcal = NAN;
+      image[m].Xm    = NAN_S_SHORT;
+    }
+    dS /= Nimlist[i];
+    strcpy (mosaic[i].coords.ctype, "RA---TAN");
+    mosaic[i].coords.crval1 = Rmin;
+    mosaic[i].coords.crval2 = Dmin;
+    mosaic[i].coords.crpix1 = 0.0;
+    mosaic[i].coords.crpix2 = 0.0;
+    mosaic[i].coords.cdelt1 = dS;
+    mosaic[i].coords.cdelt2 = dS;
+    mosaic[i].coords.pc1_1  = 1.0;
+    mosaic[i].coords.pc2_2  = 1.0;
+    mosaic[i].coords.pc1_2  = 0.0;
+    mosaic[i].coords.pc2_1  = 0.0;
+    RD_to_XY (&dX, &dY, Rmax, Dmax, &mosaic[i].coords);
+
+    mosaic[i].Mcal  = Mcal / Nimlist[i];
+    mosaic[i].dMcal = dMcal / Nimlist[i];
+    mosaic[i].Xm    = Xm / Nimlist[i];
+  }
+  if (!USE_GRID) return;
+
+  dXmax = MAX (dXmax, dX);
+  dYmax = MAX (dYmax, dY);
+  initGrid (dXmax, dYmax);
+  return;
+}
+
+void setMcalFinal () {
+
+  int i, j, im, Nimage;
+  Image *image;
+
+  if (!MOSAICNAME[0]) return;
+
+  image = getimages (&Nimage);
+
+  for (i = 0; i < Nmosaic; i++) {
+    for (j = 0; j < Nimlist[i]; j++) {
+      im = imlist[i][j];
+      image[im].Mcal = mosaic[i].Mcal;
+      image[im].dMcal = mosaic[i].dMcal;
+      image[im].Xm = mosaic[i].Xm;
+      image[im].code |= (mosaic[i].code & ID_IMAGE_FEW);
+      image[im].code |= (mosaic[i].code & ID_IMAGE_POOR);
+    }
+  }      
+}
+
+void initMosaicBins (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+
+  /* measure -> mosaic */
+  if (!MOSAICNAME[0]) return;
+  ALLOCATE (bin, int *, Ncatalog);
+  for (i = 0; i < Ncatalog; i++) {
+    ALLOCATE (bin[i], int, MAX (catalog[i].Nmeasure, 1));
+    for (j = 0; j < catalog[i].Nmeasure; j++) bin[i][j] = -1;
+  }
+
+  /* mosaic -> measure */
+  ALLOCATE (Nlist, int, Nmosaic);
+  ALLOCATE (NLIST, int, Nmosaic);
+  ALLOCATE (clist, int *, Nmosaic);
+  ALLOCATE (mlist, int *, Nmosaic);
+
+  for (i = 0; i < Nmosaic; i++) {
+    Nlist[i] = 0;
+    NLIST[i] = 100;
+    ALLOCATE (clist[i], int, NLIST[i]);
+    ALLOCATE (mlist[i], int, NLIST[i]);
+  }
+}
+
+void freeMosaicBins (int Ncatalog) {
+
+  int i;
+
+  /* measure -> mosaic */
+  if (!MOSAICNAME[0]) return;
+  for (i = 0; i < Ncatalog; i++) {
+    free (bin[i]);
+  }
+  free (bin);
+
+  /* mosaic -> measure */
+  for (i = 0; i < Nmosaic; i++) {
+    free (clist[i]);
+    free (mlist[i]);
+  }
+  free (Nlist);
+  free (NLIST);
+  free (clist);
+  free (mlist);
+}
+
+int findMosaics (Catalog *catalog, int Ncatalog) {
+  
+  int i, j, ecode;
+
+  if (!MOSAICNAME[0]) return (FALSE);
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Nmeasure; j++) {
+      if (TimeSelect) {
+	if (catalog[i].measure[j].t < TSTART) continue;
+	if (catalog[i].measure[j].t > TSTOP) continue;
+      }
+      ecode = GetPhotcodeEquivCodebyCode (catalog[i].measure[j].photcode);
+      if (photcode[0].code != ecode) continue;
+      matchMosaics (catalog, j, i);
+    }
+  }
+  return (TRUE);
+}
+
+void matchMosaics (Catalog *catalog, int meas, int cat) {
+
+  int i;
+
+  for (i = 0; i < Nmosaic; i++) {
+    if (catalog[cat].measure[meas].t < mosaic[i].start) continue;
+    if (catalog[cat].measure[meas].t > mosaic[i].stop) continue;
+    
+# ifdef GRID_V1
+    if (USE_GRID) {
+      ave = catalog[cat].measure[meas].averef;
+      ra  = catalog[cat].average[ave].R - catalog[cat].measure[meas].dR / 3600.0;
+      dec = catalog[cat].average[ave].D - catalog[cat].measure[meas].dD / 3600.0;
+
+      /* X,Y always positive-definite in range 0,0 - dX, dY */
+      RD_to_XY (&X, &Y, ra, dec, &mosaic[i].coords);
+      setGridMeasure (meas, cat, X, Y);
+    }
+# endif
+
+    bin[cat][meas] = i;
+
+    clist[i][Nlist[i]] = cat;
+    mlist[i][Nlist[i]] = meas;
+    Nlist[i] ++;
+    
+    if (Nlist[i] == NLIST[i]) {
+      NLIST[i] += 100;
+      REALLOCATE (clist[i], int, NLIST[i]);
+      REALLOCATE (mlist[i], int, NLIST[i]);
+    }	
+    return;
+  }
+  fprintf (stderr, "missed measurement\n");
+  return;
+}
+
+float getMmos (int meas, int cat) {
+
+  int i;
+  float value;
+
+  if (!MOSAICNAME[0]) return (0);
+  i = bin[cat][meas];
+  if (i == -1) return (NAN);
+
+  if (mosaic[i].code & IMAGE_BAD) return (NAN);  
+  value = mosaic[i].Mcal;
+  return (value);
+}
+
+int setMmos (Catalog *catalog, int PoorImages) {
+
+  int i, j, m, c, n, N, Nmax, mark, bad;
+  float Msys, Mrel, Mcal, Mgrid;
+  double *list, *dlist, *Mlist, *dMlist;
+  StatType stats;
+  Image *image;
+
+  if (!MOSAICNAME[0]) return (FALSE);
+
+  image = getimages (&N);
+
+  if (PoorImages) {
+    IMAGE_BAD = 0;
+  }
+
+  Nmax = 0;
+  for (i = 0; i < Nmosaic; i++) {
+    Nmax = MAX (Nmax, Nlist[i]);
+  }
+  ALLOCATE (list, double, Nmax);
+  ALLOCATE (dlist, double, Nmax);
+  ALLOCATE (Mlist, double, Nmax);
+  ALLOCATE (dMlist, double, Nmax);
+
+  for (i = 0; i < Nmosaic; i++) {
+    
+    /* on PoorImages run, skip good images */
+    if (PoorImages) {
+      bad = mosaic[i].code & (ID_IMAGE_FEW | ID_IMAGE_POOR | ID_IMAGE_SKIP);
+      if (!bad) continue;
+    }      
+
+    N = 0;
+    for (j = 0; j < Nlist[i]; j++) {
+      
+      m = mlist[i][j];
+      c = clist[i][j];
+      
+      if (catalog[c].measure[m].dbFlags & MEAS_BAD) continue;
+      Mcal  = getMcal  (m, c);
+      if (isnan(Mcal)) continue;
+      Mgrid = getMgrid (m, c);
+      if (isnan(Mgrid)) continue;
+      Mrel  = getMrel  (catalog, m, c);
+      if (isnan(Mrel)) continue;
+      
+      n = catalog[c].measure[m].averef;
+      Msys = PhotSys (&catalog[c].measure[m], &catalog[c].average[n], &catalog[c].secfilt[n*PhotNsec]);
+      list[N]  = Msys - Mrel - Mcal - Mgrid;
+      dlist[N] = MAX (catalog[c].measure[m].dM, MIN_ERROR);
+      Mlist[N] = Msys;
+      dMlist[N] = list[N];
+      N++;
+    }
+    /* Nlist[i] is all measurements, N is good measurements */
+
+    /* too few good measurements or too many bad measurements (skip in PoorImages run) */
+
+    if (!PoorImages) {
+      mark = (N < IMAGE_TOOFEW) || (N < IMAGE_GOOD_FRACTION*Nlist[i]);
+      if (mark) {
+	fprintf (stderr, "marked image %s (%d), (%d < %d) || (%d < %f*%d)\n", image[imlist[i][0]].name, i, N, IMAGE_TOOFEW, N, IMAGE_GOOD_FRACTION, Nlist[i]);
+	mosaic[i].code |= ID_IMAGE_FEW;
+      } else {
+	mosaic[i].code &= ~ID_IMAGE_FEW;
+      }
+    }
+    liststats (list, dlist, N, &stats);
+    if (PoorImages) fprintf (stderr, "Mmos: %f %f %d %d\n", stats.mean, stats.sigma, stats.Nmeas, N);
+    mosaic[i].Mcal  = stats.mean;
+    mosaic[i].dMcal = stats.sigma;
+    mosaic[i].Xm    = 100.0*log10(stats.chisq);
+  }
+  free (list);
+  free (dlist);
+  free (Mlist);
+  free (dMlist);
+
+  if (PoorImages) {
+    IMAGE_BAD = ID_IMAGE_POOR | ID_IMAGE_FEW | ID_IMAGE_SKIP;
+    STAR_BAD  = ID_STAR_POOR | ID_STAR_FEW;
+    MEAS_BAD  = ID_MEAS_NOCAL | ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM | ID_MEAS_AREA;
+  }
+  return (TRUE);
+}
+  
+StatType statsMosaicM (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (!MOSAICNAME[0]) return (stats);
+
+  ALLOCATE (list, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+
+  n = 0;
+  for (i = 0; i < Nmosaic; i++) {
+    if (mosaic[i].code & IMAGE_BAD) continue;
+    list[n] = mosaic[i].Mcal;
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsMosaicdM (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (!MOSAICNAME[0]) return (stats);
+
+  ALLOCATE (list, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+
+  n = 0;
+  for (i = 0; i < Nmosaic; i++) {
+
+    if (mosaic[i].code & IMAGE_BAD) continue;
+    list[n] = mosaic[i].dMcal;
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsMosaicN (Catalog *catalog) {
+
+  int i, j, m, c, n, N;
+  double *list, *dlist;
+  float Mcal, Mgrid, Mrel;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (!MOSAICNAME[0]) return (stats);
+
+  ALLOCATE (list, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+
+  n = 0;
+  for (i = 0; i < Nmosaic; i++) {
+    if (mosaic[i].code & IMAGE_BAD)  continue;
+
+    N = 0;
+    for (j = 0; j < Nlist[i]; j++) {
+
+      m = mlist[i][j];
+      c = clist[i][j];
+
+      Mcal = getMcal  (m, c);
+      if (isnan(Mcal)) continue;
+      Mgrid = getMgrid (m, c);
+      if (isnan(Mgrid)) continue;
+      Mrel = getMrel  (catalog, m, c);
+      if (isnan(Mrel)) continue;
+      N++;
+    }
+    list[n] = N;
+    dlist[n] = 1;
+    n++;
+  }
+  fprintf (stderr, "Nmosaic: %d, n: %d\n", Nmosaic, n);
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsMosaicX (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (!MOSAICNAME[0]) return (stats);
+
+  ALLOCATE (list, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+
+  n = 0;
+  for (i = 0; i < Nmosaic; i++) {
+
+    if (mosaic[i].code & IMAGE_BAD) continue;
+    list[n] = pow(10.0, 0.01*mosaic[i].Xm);
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+/* mark mosaic if: abs(Mcal - <Mcal>) too large, dMcal too large */
+void clean_mosaics () {
+
+  int i, N, mark, Nmark;
+  double *mlist, *slist, *dlist;
+  double MaxOffset, MedOffset, MaxScatter;
+  StatType stats;
+
+  if (!MOSAICNAME[0]) return;
+
+  if (VERBOSE) fprintf (stderr, "marking poor mosaics\n");
+
+  ALLOCATE (mlist, double, Nmosaic);
+  ALLOCATE (slist, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+
+  for (i = N = 0; i < Nmosaic; i++) {
+    if (mosaic[i].code & IMAGE_BAD) continue;
+    mlist[N] = mosaic[i].Mcal;
+    slist[N] = mosaic[i].dMcal;
+    dlist[N] = 1;
+    N++;
+  }
+  initstats ("MEAN");
+  liststats (mlist, dlist, N, &stats);
+  MaxOffset = MAX (IMAGE_OFFSET, 2*stats.sigma);
+  MedOffset = stats.median;
+  liststats (slist, dlist, N, &stats);
+  MaxScatter = MAX (IMAGE_SCATTER, 2*stats.median);
+  fprintf (stderr, "Mrel: %f, dMrel: %f, Max Scatter: %f, Max Offset: %f\n", MedOffset, stats.median, MaxScatter, MaxOffset);
+  
+  Nmark = 0;
+  for (i = 0; i < Nmosaic; i++) {
+    mark = FALSE;
+    mark = (mosaic[i].dMcal > MaxScatter) || (fabs(mosaic[i].Mcal - MedOffset) > MaxOffset);
+    if (mark) { 
+      Nmark ++;
+      mosaic[i].code |= ID_IMAGE_POOR;
+    } else {
+      mosaic[i].code &= ~ID_IMAGE_POOR;
+    }
+  }
+
+  fprintf (stderr, "%d mosaics marked poor\n", Nmark);
+  initstats (STATMODE);
+  free (mlist);
+  free (slist);
+  free (dlist);
+}
+
+void plot_mosaic_fields (Catalog *catalog) {
+
+  int i, j, m, c, N, ave, Nimage;
+  double *xlist, *ylist;
+  double Xmin, Xmax, Ymin, Ymax;
+  char string[64];
+  Image *image;
+  Graphdata graphdata;
+
+  if (!MOSAICNAME[0]) return;
+
+  image = getimages (&Nimage);
+
+  N = 0;
+  for (i = 0; i < Nmosaic; i++) 
+    N = MAX (N, Nlist[i]);
+
+  ALLOCATE (xlist, double, N);
+  ALLOCATE (ylist, double, N);
+
+  for (i = 0; i < Nmosaic; i++) {
+    N = 0;
+    Xmin = Ymin = +360.0;
+    Xmax = Ymax = -360.0;
+    for (j = 0; j < Nlist[i]; j++) {
+      
+      m = mlist[i][j];
+      c = clist[i][j];
+      
+      if (catalog[c].measure[m].dbFlags & (ID_MEAS_AREA | ID_MEAS_NOCAL)) continue;
+
+      ave = catalog[c].measure[m].averef;
+      xlist[N] = catalog[c].average[ave].R - catalog[c].measure[m].dR / 3600.0;
+      ylist[N] = catalog[c].average[ave].D - catalog[c].measure[m].dD / 3600.0;
+      N++;
+    }
+  
+    sprintf (string, "Mosaic %d", i);
+    plot_defaults (&graphdata);
+    plot_list (&graphdata, xlist, ylist, N, string, NULL);
+  }
+
+  free (ylist);
+  free (xlist);
+}
+
+void plot_mosaics () {
+
+  int i, bin;
+  double *xlist, *Mlist, *dlist;
+  Graphdata graphdata;
+
+  if (!MOSAICNAME[0]) return;
+
+  ALLOCATE (xlist, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+  ALLOCATE (Mlist, double, Nmosaic);
+
+  for (i = 0; i < Nmosaic; i++) {
+    Mlist[i] = mosaic[i].Mcal;
+    dlist[i] = mosaic[i].dMcal;
+    xlist[i] = mosaic[i].secz;
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.xmin = 0.95;
+  graphdata.xmax = 2.50;
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, Mlist, Nmosaic, "airmass vs Mcal", "airmass.png");
+  plot_defaults (&graphdata);
+  graphdata.size = 1.5;
+  graphdata.ptype = 7;
+  plot_list (&graphdata, Mlist, dlist, Nmosaic, "Mcal vs dMcal", "MdM.png");
+
+# define NBIN 200
+  REALLOCATE (xlist, double, NBIN);
+  REALLOCATE (Mlist, double, NBIN);
+
+  /**** dMcal histgram ****/
+  for (i = 0; i < NBIN; i++) xlist[i] = 0.00005*i;
+  bzero (Mlist, NBIN*sizeof(double));
+  for (i = 0; i < Nmosaic; i++) {
+    bin = mosaic[i].dMcal / 0.00005;
+    bin = MAX (0, MIN (NBIN - 1, bin));
+    Mlist[bin] += 1.0;
+  }
+  plot_defaults (&graphdata);
+  graphdata.style = 1;
+  plot_list (&graphdata, xlist, Mlist, NBIN, "dMcal hist", "dMcalhist.png");
+
+  free (dlist);
+  free (xlist);
+  free (Mlist);
+}
+
+int *SelectRefMosaic (Mosaic **refmosaic, int *Nimage) {
+
+  int i, Imax, Nmax;
+
+  Imax = 0;
+  Nmax = Nimlist[0];
+  for (i = 0; i < Nmosaic; i++) {
+    if (Nimlist[i] > Nmax) {
+      Imax = i;
+      Nmax = Nimlist[i];
+    }
+  }
+
+  *refmosaic = &mosaic[Imax];
+  *Nimage = Nmax;
+  return (imlist[Imax]);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/SetSignals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/SetSignals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/SetSignals.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "relphot.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect this signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore */
+    case SIGCONT:    /* continue - maintain this action */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? */
+    case SIGURG:     /* socket signal, ignore this */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/Shutdown.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/Shutdown.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/Shutdown.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "relphot.h"
+
+static FITS_DB *db;
+
+void set_db (FITS_DB *in) {
+  db = in;
+}
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format));
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  SetProtect (TRUE);
+  gfits_db_close (db);
+  fprintf (stderr, "ERROR: addstar halted\n");
+  exit (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/StarOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/StarOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/StarOps.c	(revision 22322)
@@ -0,0 +1,611 @@
+# include "relphot.h"
+
+static int Nmax;
+static double *list;
+static double *dlist;
+
+void initMrel (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+  
+  Nmax = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
+    }
+  }
+
+  ALLOCATE (list, double, MAX (1, Nmax));
+  ALLOCATE (dlist, double, MAX (1, Nmax));
+}  
+
+float getMrel (Catalog *catalog, int meas, int cat) {
+
+  int ave;
+  float value;
+
+  ave = catalog[cat].measure[meas].averef;
+  if (catalog[cat].average[ave].code & STAR_BAD) return (NAN);  
+ 
+  value = catalog[cat].secfilt[PhotNsec*ave+PhotSec].M;
+  return (value);
+}
+
+int setMrel (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, N;
+  float Msys, Mcal, Mmos, Mgrid;
+  StatType stats;
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+      m = catalog[i].average[j].measureOffset;
+
+      N = 0;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	// XXX allow REF stars (no Image Entry) to be included in the calculation this
+	// should be optionally set, and should allow for REF stars to be downweighted by
+	// more than their reported errors.  how such info is carried is unclear...
+	if (getImageEntry (m, i) < 0) {
+	  Mcal = Mmos = Mgrid = 0;
+	} else {
+	  Mcal  = getMcal  (m, i);
+	  if (isnan(Mcal)) continue;
+	  Mmos  = getMmos  (m, i);
+	  if (isnan(Mmos)) continue;
+	  Mgrid = getMgrid (m, i);
+	  if (isnan(Mgrid)) continue;
+	}
+
+	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
+	list[N] = Msys - Mcal - Mmos - Mgrid;
+	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
+	N++;
+      }
+      if (N < STAR_TOOFEW) { /* too few measurements */
+	catalog[i].average[j].code |= ID_STAR_FEW;
+      } else {
+	catalog[i].average[j].code &= ~ID_STAR_FEW;
+      }	
+
+      liststats (list, dlist, N, &stats);
+
+      catalog[i].secfilt[PhotNsec*j+PhotSec].M  = stats.mean;
+      catalog[i].secfilt[PhotNsec*j+PhotSec].dM = stats.sigma;
+      catalog[i].secfilt[PhotNsec*j+PhotSec].Xm = (stats.Nmeas > 1) ? 100.0*log10(stats.chisq) : NAN_S_SHORT;
+    }
+  }
+  return (TRUE);
+}
+
+int setMrelOutput (Catalog *catalog, int Ncatalog, int mark) {
+
+  int i, j, k, m, N, Nmax;
+  float Msys, Mcal, Mmos, Mgrid;
+  double *list, *dlist;
+  StatType stats;
+
+  /* Nmeasure is now different, need to reallocate */
+  Nmax = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
+    }
+  }
+  ALLOCATE (list, double, MAX (1, Nmax));
+  ALLOCATE (dlist, double, MAX (1, Nmax));
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* skip stars already calibrated */
+      if (catalog[i].found[j]) continue;  
+
+      N = 0;
+      m = catalog[i].average[j].measureOffset;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	// XXX allow REF stars (no Image Entry) to be included in the calculation this
+	// should be optionally set, and should allow for REF stars to be downweighted by
+	// more than their reported errors.  how such info is carried is unclear...
+	if (getImageEntry (m, i) < 0) {
+	  Mcal = Mmos = Mgrid = 0;
+	} else {
+	  Mcal  = getMcal  (m, i);
+	  if (isnan(Mcal)) continue;
+	  Mmos  = getMmos  (m, i);
+	  if (isnan(Mmos)) continue;
+	  Mgrid = getMgrid (m, i);
+	  if (isnan(Mgrid)) continue;
+	}
+
+	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
+	list[N] = Msys - Mcal - Mmos - Mgrid;
+	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
+	N++;
+      }
+      if (N < 1) continue;
+
+      liststats (list, dlist, N, &stats);
+      if (mark) catalog[i].found[j] = TRUE;
+
+      /* use sigma or error in dM for output? */
+      catalog[i].secfilt[PhotNsec*j+PhotSec].M  = stats.mean;
+      catalog[i].secfilt[PhotNsec*j+PhotSec].dM = MAX (stats.error, stats.sigma);
+      catalog[i].secfilt[PhotNsec*j+PhotSec].Xm = (stats.Nmeas > 1) ? 100.0*log10(stats.chisq) : NAN_S_SHORT;
+    }
+  }
+
+  free (list);
+  free (dlist);
+  return (TRUE);
+}
+
+// for each average object, set the average mags based on existing equiv photometry
+int setMave (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, Ns, Nsecfilt, N, Nmax, Nc;
+  float Msys;
+  double *list, *dlist;
+  StatType stats;
+  PhotCode *code;
+
+  // pre-allocate a list for stats purposes
+  Nmax = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
+    }
+  }
+  ALLOCATE (list, double, MAX (1, Nmax));
+  ALLOCATE (dlist, double, MAX (1, Nmax));
+
+  Nsecfilt = catalog[0].Nsecfilt;
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      for (Ns = 0; Ns < Nsecfilt; Ns++) {
+
+	code = GetPhotcodebyNsec (Ns);
+	Nc = code[0].code;
+	
+	N = 0;
+	m = catalog[i].average[j].measureOffset;
+	for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	  if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	  if (GetPhotcodeEquivCodebyCode (catalog[i].measure[m].photcode) != Nc) continue;
+
+	  Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
+	  if (isnan(Msys)) continue;
+
+	  list[N] = Msys - catalog[i].measure[m].Mcal;
+	  dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
+	  N++;
+	}
+	if (N < 1) continue;
+
+	liststats (list, dlist, N, &stats);
+
+	/* use sigma or error in dM for output? */
+	catalog[i].secfilt[Nsecfilt*j+Ns].M  = stats.mean;
+	catalog[i].secfilt[Nsecfilt*j+Ns].dM = MAX (stats.error, stats.sigma);
+	catalog[i].secfilt[Nsecfilt*j+Ns].Xm = (stats.Nmeas > 1) ? 100.0*log10(stats.chisq) : NAN_S_SHORT;
+	catalog[i].secfilt[Nsecfilt*j+Ns].Ncode = N;
+	catalog[i].secfilt[Nsecfilt*j+Ns].Nused = stats.Nmeas;
+      }
+    }
+  }
+
+  free (list);
+  free (dlist);
+  return (TRUE);
+}
+
+/* set measure.Mcal for all measures except ID_MEAS_NOCAL and ID_IMAGE_NOCAL */
+int setMcalOutput (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m;
+  float Mcal, Mmos, Mgrid;
+
+  MEAS_BAD = ID_MEAS_NOCAL;
+  IMAGE_BAD = ID_IMAGE_NOCAL;
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      m = catalog[i].average[j].measureOffset;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	Mcal  = getMcal  (m, i);
+	if (isnan(Mcal)) continue;
+	Mmos  = getMmos  (m, i);
+	if (isnan(Mmos)) continue;
+	Mgrid = getMgrid (m, i);
+	if (isnan(Mgrid)) continue;
+	catalog[i].measure[m].Mcal = Mcal + Mmos + Mgrid;
+      }
+    }
+  }
+  return (TRUE);
+}
+
+void clean_stars (Catalog *catalog, int Ncatalog) {
+
+  int i, j, Ndel, Nave, Ntot, mark;
+  float dM, Xm;
+  double Chisq, MaxScatter, MaxChisq;
+  double *xlist, *slist, *dlist;
+  StatType stats;
+
+  if (VERBOSE) fprintf (stderr, "marking poor stars\n");
+
+  /* find Xm median -> ChiSq lim must be > median */
+  for (i = Ntot = 0; i < Ncatalog; i++) {
+    Ntot += catalog[i].Naverage; 
+  }
+  ALLOCATE (xlist, double, Ntot);
+  ALLOCATE (slist, double, Ntot);
+  ALLOCATE (dlist, double, Ntot);
+  for (i = Ntot = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      if (catalog[i].average[j].code & STAR_BAD) continue;
+      Xm = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
+      if (Xm == -1) continue;
+      Chisq = pow (10.0, 0.01*Xm);
+      xlist[Ntot] = Chisq;
+      slist[Ntot] = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
+      dlist[Ntot] = 1;
+      Ntot ++;
+    }
+  }
+  
+  initstats ("MEAN");
+  liststats (xlist, dlist, Ntot, &stats);
+  MaxChisq = MAX (STAR_CHISQ, 2*stats.median);
+  liststats (slist, dlist, Ntot, &stats);
+  MaxScatter = MAX (STAR_SCATTER, 2*stats.median);
+  fprintf (stderr, "Max Scatter: %f, Max Chisq: %f\n", MaxScatter, MaxChisq);
+
+  Ndel = Nave = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      dM = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
+      Xm = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
+      Chisq = pow (10.0, 0.01*Xm);
+      mark = (dM > MaxScatter) || (Xm == NAN_S_SHORT) || (Chisq > MaxChisq);
+      if (mark) {
+	catalog[i].average[j].code |= ID_STAR_POOR;
+	Ndel ++;
+      } else {
+	catalog[i].average[j].code &= ~ID_STAR_POOR;
+      }
+      Nave ++;
+    }
+  }
+  fprintf (stderr, "%d stars marked variable, %d total\n", Ndel, Nave);
+  initstats (STATMODE);
+  free (xlist);
+  free (slist);
+  free (dlist);
+}
+
+void clean_measures (Catalog *catalog, int Ncatalog, int final) {
+
+  int i, j, k, m, N, Ndel, Nave, Nmax, image_bad, TOOFEW;
+  int *ilist;
+  double *tlist, *list, *dlist, Ns;
+  float Msys, Mcal, Mmos, Mgrid;
+  StatType stats;
+
+  if (VERBOSE) fprintf (stderr, "marking poor measures\n");
+  /* Nmeasure is now different, need to reallocate */
+  Nmax = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
+    }
+  }
+  ALLOCATE (list, double, MAX (1, Nmax));
+  ALLOCATE (dlist, double, MAX (1, Nmax));
+  ALLOCATE (ilist, int, Nmax);
+  ALLOCATE (tlist, double, Nmax);
+  
+  /* it makes no sense to mark 3-sigma outliers with <5 measurements */
+  TOOFEW = MAX (5, STAR_TOOFEW);
+
+  Ns = 3;
+  Ndel = Nave = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* skip bad stars to prevent them from becoming good (on inner sample) */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+
+      /* on final processing, skip stars already measured */
+      if (final && catalog[i].found[j]) continue;  
+
+      /* accumulate list of valid measurements */
+      m = catalog[i].average[j].measureOffset;
+      N = 0;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	/* if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue; */
+	Mcal  = getMcal  (m, i);
+	if (isnan(Mcal)) continue;
+	Mmos  = getMmos  (m, i);
+	if (isnan(Mmos)) continue;
+	Mgrid = getMgrid (m, i);
+	if (isnan(Mgrid)) continue;
+
+	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
+	list[N] = Msys - Mcal - Mmos - Mgrid;
+	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
+	N++;
+      }
+      if (N < TOOFEW) continue;
+
+      /* 3-sigma clip based on stats of inner 50% */
+      initstats ("INNER_MEAN");
+      liststats (list, dlist, N, &stats);
+      stats.sigma = MAX (MIN_ERROR, stats.sigma); /* if measurements agree too well, sigma -> 0.0 */
+      for (k = m = 0; k < N; k++) {
+	if (fabs (list[k] - stats.median) < Ns*stats.sigma) {
+	  list[m] = list[k];
+	  m++;
+	}
+      }
+      initstats ("MEAN");
+      liststats (list, dlist, m, &stats);
+      stats.sigma = MAX (MIN_ERROR, stats.sigma);
+
+      /* apply to list of all relevant measurements, including IMAGE_POOR & IMAGE_FEW */
+      image_bad = IMAGE_BAD;
+      IMAGE_BAD = ID_IMAGE_NOCAL;
+      m = catalog[i].average[j].measureOffset;
+      N = 0;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	/* if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue; */
+	Mcal  = getMcal  (m, i);
+	if (isnan(Mcal)) continue;
+	Mmos  = getMmos  (m, i);
+	if (isnan(Mmos)) continue;
+	Mgrid = getMgrid (m, i);
+	if (isnan(Mgrid)) continue;
+
+	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
+	list[N] = Msys - Mcal - Mmos - Mgrid;
+	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
+	ilist[N] = m;
+	N++;
+	Nave ++;
+      }
+      if (N < TOOFEW) continue;
+
+      /* mark bad measures */
+      for (k = 0; k < N; k++) {
+	if (fabs (list[k] - stats.median) > Ns*stats.sigma) {
+	  catalog[i].measure[ilist[k]].dbFlags |= ID_MEAS_POOR_PHOTOM;
+	  Ndel ++;
+	}
+      }
+      IMAGE_BAD = image_bad;
+    }
+  }
+  initstats (STATMODE);
+  if (VERBOSE) fprintf (stderr, "%d measures marked poor, %d total\n", Ndel, Nave);
+  free (ilist);
+  free (tlist);
+}
+
+StatType statsStarN (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, n, N, Ntot;
+  double *list, *dlist;
+  float Mcal, Mmos, Mgrid;
+  StatType stats;
+
+  Ntot = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    Ntot += catalog[i].Naverage; 
+  }
+
+  ALLOCATE (list, double, Ntot);
+  ALLOCATE (dlist, double, Ntot);
+
+  n = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+      m = catalog[i].average[j].measureOffset;
+
+      N = 0;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	Mcal = getMcal  (m, i);
+	if (isnan(Mcal)) continue;
+	Mmos = getMmos  (m, i);
+	if (isnan(Mmos)) continue;
+	Mgrid = getMgrid (m, i);
+	if (isnan(Mgrid)) continue;
+	N++;
+      }
+      
+      list[n] = N;
+      dlist[n] = 1;
+      n++;
+    }
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsStarX (Catalog *catalog, int Ncatalog) {
+
+  int i, j, n, Ntot, Xm;
+  double *list, *dlist;
+  StatType stats;
+
+  Ntot = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    Ntot += catalog[i].Naverage; 
+  }
+
+  ALLOCATE (list, double, Ntot);
+  ALLOCATE (dlist, double, Ntot);
+
+  n = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+
+      Xm = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
+      if (Xm == NAN_S_SHORT) continue;
+      list[n] = pow (10.0, 0.01*Xm);
+      dlist[n] = 1;
+      n++;
+    }
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsStarS (Catalog *catalog, int Ncatalog) {
+
+  int i, j, n, Ntot;
+  double *list, *dlist;
+  float dM;
+  StatType stats;
+
+  Ntot = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    Ntot += catalog[i].Naverage; 
+  }
+
+  ALLOCATE (list, double, Ntot);
+  ALLOCATE (dlist, double, Ntot);
+
+  n = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+
+      dM = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
+      list[n] = dM;
+      dlist[n] = 1;
+      n++;
+    }
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+void plot_stars (Catalog *catalog, int Ncatalog) {
+
+  int i, j, bin;
+  float dMrel;
+  double *xlist, *Mlist;
+  Graphdata graphdata;
+
+# define NBIN 200
+  ALLOCATE (xlist, double, NBIN);
+  ALLOCATE (Mlist, double, NBIN);
+
+  for (i = 0; i < NBIN; i++) xlist[i] = 0.00025*i;
+  bzero (Mlist, NBIN*sizeof(double));
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+      dMrel = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
+      bin = dMrel / 0.00025;
+      bin = MAX (0, MIN (NBIN-1, bin));
+      Mlist[bin] += 1.0;
+    }
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.style = 1;
+  plot_list (&graphdata, xlist, Mlist, NBIN, "dMrel hist", "dMhist.png");
+
+  free (xlist);
+  free (Mlist);
+}
+
+void plot_chisq (Catalog *catalog, int Ncatalog) {
+
+  int i, j, N, Ntotal, value;
+  double *xlist, *ylist;
+  Graphdata graphdata;
+
+  Ntotal = 0;
+  for (i = 0; i < Ncatalog; i++) Ntotal += catalog[i].Naverage;
+
+  ALLOCATE (xlist, double, Ntotal);
+  ALLOCATE (ylist, double, Ntotal);
+
+  N = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      if (catalog[i].average[j].code & STAR_BAD) continue;
+      xlist[N] = catalog[i].secfilt[PhotNsec*j+PhotSec].M;
+      value    = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
+      if (value == NAN_S_SHORT) continue;
+      ylist[N] = 0.01*value;
+      N++;
+    }
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = -3.0;
+  plot_list (&graphdata, xlist, ylist, N, "chisq", NULL);
+  free (xlist);
+  free (ylist);
+}
+
+void plot_star_coords (Catalog *catalog, int Ncatalog) {
+
+  int i, j, N;
+  double *xlist, *ylist;
+  double Xmin, Ymin, Xmax, Ymax;
+  Graphdata graphdata;
+
+  N = 0; 
+  for (i = 0; i < Ncatalog; i++) {
+    N += catalog[i].Naverage;
+  }
+  ALLOCATE (xlist, double, N);
+  ALLOCATE (ylist, double, N);
+
+  N = 0;
+  Xmin = Ymin = +360.0;
+  Xmax = Ymax = -360.0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      xlist[N] = catalog[i].average[j].R;
+      ylist[N] = catalog[i].average[j].D;
+      N++;
+    }
+  }
+  plot_defaults (&graphdata);
+  plot_list (&graphdata, xlist, ylist, N, "coords", NULL);
+
+  free (xlist);
+  free (ylist);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/args.c	(revision 22322)
@@ -0,0 +1,217 @@
+# include "relphot.h"
+void usage (void);
+
+int args (int argc, char **argv) {
+
+  int N;
+  double trange;
+
+  /* define time */
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &TSTART)) { 
+      fprintf (stderr, "ERROR: syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      if (!ohana_str_to_time (argv[N], &TSTOP)) { 
+	fprintf (stderr, "ERROR: syntax error\n");
+	return (FALSE);
+      }
+    } else {
+      if (trange < 0) {
+	trange = fabs (trange);
+	TSTOP = TSTART;
+	TSTART -= trange;
+      } else {
+	TSTOP = TSTART + trange;
+      }
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+
+  /* specify portion of the sky */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  UserPatchSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatchSelect = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOTSTUFF = FALSE;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    PLOTSTUFF = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOTDELAY = 500000;
+  if ((N = get_argument (argc, argv, "-plotdelay"))) {
+    remove_argument (N, &argc, argv);
+    PLOTDELAY = 1e6*atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  strcpy (STATMODE, "CHI_INNER_WTMEAN");
+  if ((N = get_argument (argc, argv, "-statmode"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (STATMODE, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  NLOOP = 8;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    NLOOP = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  RESET = FALSE;
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    RESET = TRUE;
+  }
+
+  UPDATE = FALSE;
+  if ((N = get_argument (argc, argv, "-update"))) {
+    remove_argument (N, &argc, argv);
+    UPDATE = TRUE;
+  }
+
+  SHOW_PARAMS = FALSE;
+  if ((N = get_argument (argc, argv, "-params"))) {
+    remove_argument (N, &argc, argv);
+    SHOW_PARAMS = TRUE;
+  }
+
+  PlotMmin = 10.0; PlotMmax = 20.0; PlotdMmin = -1.0; PlotdMmax = 1.0;
+  if ((N = get_argument (argc, argv, "-plrange"))) {
+    remove_argument (N, &argc, argv);
+    PlotMmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotMmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotdMmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotdMmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* group images by mosaic, find Mmos */
+  MOSAICNAME[0] = 0;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (MOSAICNAME, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  FREEZE_IMAGES = FALSE;
+  if ((N = get_argument (argc, argv, "-imfreeze"))) {
+    remove_argument (N, &argc, argv);
+    FREEZE_IMAGES = TRUE;
+  }
+
+  USE_GRID = FALSE;
+  if ((N = get_argument (argc, argv, "-grid"))) {
+    remove_argument (N, &argc, argv);
+    USE_GRID = TRUE;
+    if (!MOSAICNAME[0]) {
+      fprintf (stderr, "-grid is only valid with -mosaic\n");
+      exit (2);
+    }
+  }
+
+  MIN_ERROR = 0.001;
+  if ((N = get_argument (argc, argv, "-minerror"))) {
+    remove_argument (N, &argc, argv);
+    MIN_ERROR = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    /* require MIN_ERROR > 0 */
+  }  
+
+  AreaSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-area"))) {
+    remove_argument (N, &argc, argv);
+    AreaXmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaXmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaSelect = TRUE;
+  }
+
+  ImagSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-instmag"))) {
+    remove_argument (N, &argc, argv);
+    ImagMin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagMax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagSelect = TRUE;
+  }
+
+  DophotSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-dophot"))) {
+    remove_argument (N, &argc, argv);
+    DophotValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    DophotSelect = TRUE;
+  }
+
+  UpdateAverages = FALSE;
+  if ((N = get_argument (argc, argv, "-averages"))) {
+    remove_argument (N, &argc, argv);
+    UpdateAverages = TRUE;
+  }
+
+  if (UpdateAverages && (argc == 1)) return TRUE;
+  if (UserPatchSelect && (argc == 2)) return TRUE;
+  if (argc != 3) usage ();
+
+  return TRUE;
+}
+
+void usage () {
+  fprintf (stderr, "ERROR: USAGE: relphot (region) (photcode)\n");
+  fprintf (stderr, "       or:    relphot (photcode) -region RA RA DEC DEC\n");
+  fprintf (stderr, "       or:    relphot -averages -region RA RA DEC DEC\n");
+  fprintf (stderr, "  options: \n");
+  fprintf (stderr, "  -time (start) (stop)\n");
+  fprintf (stderr, "  -v\n");
+  fprintf (stderr, "  -plot\n");
+  fprintf (stderr, "  -plotdelay (seconds)\n");
+  fprintf (stderr, "  -statmode (mode)\n");
+  fprintf (stderr, "  -n (nloop)\n");
+  fprintf (stderr, "  -reset\n");
+  fprintf (stderr, "  -update\n");
+  fprintf (stderr, "  -params\n");
+  fprintf (stderr, "  -mosaic (mosaic)\n");
+  fprintf (stderr, "  -imfreeze\n");
+  fprintf (stderr, "  -grid\n");
+  fprintf (stderr, "  -area Xmin Xmax Ymin Ymax\n");
+  fprintf (stderr, "  -instmag min max\n");
+  fprintf (stderr, "  \n");
+  exit (2);
+} 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/bcatalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/bcatalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/bcatalog.c	(revision 22322)
@@ -0,0 +1,120 @@
+# include "relphot.h"
+
+int bcatalog (Catalog *subcatalog, Catalog *catalog) {
+  
+  int i, j, offset, ecode;
+  int NAVERAGE, NMEASURE, Naverage, Nmeasure, Nm;
+  float mag;
+
+  // XXX PhotNsec as a global is a bad idea; either get it from catalog
+  // or get it from:
+  // Nsecfilt = GetPhotcodeNsecfilt ();
+  // assert (catalog[0].Nsecfilt == Nsecfilt);
+
+  /* we are moving only the subset of measurements from catalog[0] to subcatalog[0] */
+  NAVERAGE = 50;
+  NMEASURE = 1000;
+  ALLOCATE (subcatalog[0].average, Average, NAVERAGE);
+  ALLOCATE (subcatalog[0].secfilt, SecFilt, NAVERAGE*PhotNsec);
+  ALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
+  Nmeasure = Naverage = 0;
+
+  /* exclude stars not in range or with too few measurements */
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    // if (catalog[0].average[i].Nmeasure < 2) continue; 
+
+    /* start with all stars good */
+    subcatalog[0].average[Naverage] = catalog[0].average[i];
+    subcatalog[0].average[Naverage].measureOffset = Nmeasure;
+    for (j = 0; j < PhotNsec; j++) {
+      subcatalog[0].secfilt[PhotNsec*Naverage+j] = catalog[0].secfilt[PhotNsec*i+j];
+    }
+
+    if (RESET) {
+      subcatalog[0].secfilt[PhotNsec*Naverage+PhotSec].M  = NAN;
+      subcatalog[0].secfilt[PhotNsec*Naverage+PhotSec].dM = NAN;
+      subcatalog[0].average[Naverage].code &= ~ID_STAR_FEW;
+      subcatalog[0].average[Naverage].code &= ~ID_STAR_POOR;
+    }
+
+    Nm = 0;
+    for (j = 0; j < catalog[0].average[i].Nmeasure; j++) {
+
+      offset = catalog[0].average[i].measureOffset + j;
+
+      /* select measurements by photcode */
+      ecode = GetPhotcodeEquivCodebyCode (catalog[0].measure[offset].photcode);
+      if (ecode != photcode[0].code) continue;
+
+      /* select measurements by time */
+      if (TimeSelect) {
+	if (catalog[0].measure[offset].t < TSTART) continue;
+	if (catalog[0].measure[offset].t > TSTOP) continue;
+      }
+
+      /* select measurements by quality */
+      // XXX ignore this criterion for REF measurements?
+      if (DophotSelect && (catalog[0].measure[offset].dophot != DophotValue)) continue;
+
+      /* select measurements by mag limit */
+      mag = PhotCat (&catalog[0].measure[offset]);
+      if (mag > MAG_LIM) continue;
+
+      /* select measurements by measurement error */
+      if ((SIGMA_LIM > 0) && (catalog[0].measure[offset].dM > SIGMA_LIM)) continue;
+
+      /* select measurements by mag limit */
+      if (ImagSelect) {
+	mag = PhotInst (&catalog[0].measure[offset]);
+	if (mag < ImagMin) continue;
+	if (mag > ImagMax) continue;
+      }
+
+      subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_SKIP_PHOTOM;
+      subcatalog[0].measure[Nmeasure]        = catalog[0].measure[offset];
+      subcatalog[0].measure[Nmeasure].averef = Naverage;
+      if (RESET) { 
+	subcatalog[0].measure[Nmeasure].Mcal = 0;
+	subcatalog[0].measure[Nmeasure].dbFlags &= 0xff00;
+	subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_POOR_PHOTOM;
+	subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_AREA;
+	subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_NOCAL;
+      }
+      Nmeasure ++;
+      Nm ++;
+      if (Nmeasure == NMEASURE) {
+	NMEASURE += 1000;
+	REALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
+      }
+    }
+
+    // XXXX test : what checks do I need to make elsewhere to avoid problems here?
+    # if 0
+    if (Nm < 2) { /* enough measurements in band? */
+      Nmeasure -= Nm;
+      continue; 
+    }
+    # endif
+    subcatalog[0].average[Naverage].Nmeasure = Nm;
+    Naverage ++;
+    if (Naverage == NAVERAGE) {
+      NAVERAGE += 50;
+      REALLOCATE (subcatalog[0].average, Average, NAVERAGE);
+      REALLOCATE (subcatalog[0].secfilt, SecFilt, NAVERAGE*PhotNsec);
+    }
+  }
+  REALLOCATE (subcatalog[0].average, Average, MAX (Naverage, 1));
+  REALLOCATE (subcatalog[0].measure, Measure, MAX (Nmeasure, 1));
+  REALLOCATE (subcatalog[0].secfilt, SecFilt, PhotNsec*MAX (Naverage, 1));
+  subcatalog[0].Naverage = Naverage;
+  subcatalog[0].Nmeasure = Nmeasure;
+  subcatalog[0].Nsecfilt = catalog[0].Nsecfilt;
+  subcatalog[0].Nsecf_mem = Naverage * catalog[0].Nsecfilt;
+  assert (PhotNsec == catalog[0].Nsecfilt);
+
+  if (VERBOSE) {
+    fprintf (stderr, "%d: using %d stars (%d measures) for catalog\n", i, 
+	     subcatalog[0].Naverage, subcatalog[0].Nmeasure);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/global_stats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/global_stats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/global_stats.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "relphot.h"
+
+void global_stats (Catalog *catalog, int Ncatalog) {
+
+  StatType stN, stX, stS, imN, imX, imM, imD, msM, msX, msN, msD;
+
+  initstats ("MEAN");
+
+  stN = statsStarN (catalog, Ncatalog);
+  stX = statsStarX (catalog, Ncatalog);
+  stS = statsStarS (catalog, Ncatalog);
+
+  imN = statsImageN (catalog);
+  imX = statsImageX (catalog);
+  imM = statsImageM (catalog);
+  imD = statsImagedM (catalog);
+
+  msN = statsMosaicN (catalog);
+  msM = statsMosaicM (catalog);
+  msD = statsMosaicdM (catalog);
+  msX = statsMosaicX (catalog);
+
+  fprintf (stderr, "STATS            median     mean    sigma      min      max   Nmeas\n");
+  fprintf (stderr, "meas / image:   %7.0f  %7.0f  %7.0f  %7.0f  %7.0f  %6d\n",   imN.median, imN.mean, imN.sigma, imN.min, imN.max, imN.Nmeas);
+  fprintf (stderr, "meas / mosaic:  %7.0f  %7.0f  %7.0f  %7.0f  %7.0f  %6d\n",   msN.median, msN.mean, msN.sigma, msN.min, msN.max, msN.Nmeas);
+  fprintf (stderr, "meas / star:    %7.0f  %7.1f  %7.1f  %7.0f  %7.0f  %6d\n",   stN.median, stN.mean, stN.sigma, stN.min, stN.max, stN.Nmeas);
+
+  fprintf (stderr, "chisq image:    %7.1f  %7.1f  %7.1f  %7.1f  %7.1f  %6d\n",   imX.median, imX.mean, imX.sigma, imX.min, imX.max, imX.Nmeas);
+  fprintf (stderr, "chisq mosaic:   %7.1f  %7.1f  %7.1f  %7.1f  %7.1f  %6d\n",   msX.median, msX.mean, msX.sigma, msX.min, msX.max, msX.Nmeas);
+  fprintf (stderr, "chisq star:     %7.1f  %7.1f  %7.1f  %7.1f  %7.1f  %6d\n",   stX.median, stX.mean, stX.sigma, stX.min, stX.max, stX.Nmeas);
+
+  fprintf (stderr, "Mcal image:     %7.4f  %7.4f  %7.4f  %7.4f  %7.4f  %6d\n",   imM.median, imM.mean, imM.sigma, imM.min, imM.max, imM.Nmeas);
+  fprintf (stderr, "dMcal image:    %7.4f  %7.4f  %7.4f  %7.4f  %7.4f  %6d\n",   imD.median, imD.mean, imD.sigma, imD.min, imD.max, imD.Nmeas);
+
+  fprintf (stderr, "Mcal mosaic:    %7.4f  %7.4f  %7.4f  %7.4f  %7.4f  %6d\n",   msM.median, msM.mean, msM.sigma, msM.min, msM.max, msM.Nmeas);
+  fprintf (stderr, "dMcal mosaic:   %7.4f  %7.4f  %7.4f  %7.4f  %7.4f  %6d\n",   msD.median, msD.mean, msD.sigma, msD.min, msD.max, msD.Nmeas);
+
+  fprintf (stderr, "dMrel star:     %7.4f  %7.4f  %7.4f  %7.4f  %7.4f  %6d\n\n", stS.median, stS.mean, stS.sigma, stS.min, stS.max, stS.Nmeas);
+
+  initstats (STATMODE);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/initialize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/initialize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/initialize.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "relphot.h"
+
+void initialize (int argc, char **argv) {
+
+  int N;
+
+  ConfigInit (&argc, argv);
+  args (argc, argv);
+
+  if (!UpdateAverages) {
+
+    N = UserPatchSelect ? 1 : 2;
+
+    if ((photcode = GetPhotcodebyName (argv[N])) == NULL) {
+      fprintf (stderr, "ERROR: photcode %s not found in photcode table\n", argv[N]);
+      exit (1);
+    }
+    if (photcode[0].type != PHOT_SEC) {
+      fprintf (stderr, "photcode %s is not a primary or secondary filter\n", argv[N]);
+      exit (1);
+    }
+    PhotSec = GetPhotcodeNsec (photcode[0].code);
+  }
+
+  PhotNsec = GetPhotcodeNsecfilt ();
+
+  initstats (STATMODE);
+
+  IMAGE_BAD = ID_IMAGE_POOR | ID_IMAGE_FEW | ID_IMAGE_SKIP;
+  STAR_BAD  = ID_STAR_POOR | ID_STAR_FEW;
+  MEAS_BAD  = ID_MEAS_NOCAL | ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM | ID_MEAS_AREA;
+
+  if (SHOW_PARAMS) {
+    fprintf (stderr, "current parameter settings:\n");
+    if (TimeSelect) {
+      fprintf (stderr, "TimeSelect: TRUE (%s - %s)\n", ohana_sec_to_date (TSTART), ohana_sec_to_date (TSTOP));
+    } else {
+      fprintf (stderr, "TimeSelect: FALSE\n");
+    }
+    fprintf (stderr, "VERBOSE: %d, PLOTSTUFF: %d\n", VERBOSE, PLOTSTUFF);
+    fprintf (stderr, "GRID_X: %d, GRID_Y: %d, BINNING: %d == Nmx: %d, Nmy: %d\n", 
+	     RELPHOT_GRID_X, 
+	     RELPHOT_GRID_Y, 
+	     RELPHOT_GRID_BINNING, 
+	     (int)(RELPHOT_GRID_X/RELPHOT_GRID_BINNING), (int)(RELPHOT_GRID_Y/RELPHOT_GRID_BINNING));
+
+    fprintf (stderr, "MAG_LIM                %lf\n", MAG_LIM);
+    fprintf (stderr, "STAR_SCATTER           %lf\n", STAR_SCATTER);
+    fprintf (stderr, "IMAGE_SCATTER          %lf\n", IMAGE_SCATTER);
+    fprintf (stderr, "IMAGE_OFFSET           %lf\n", IMAGE_OFFSET);
+    fprintf (stderr, "IMAGE_CATALOG          %s\n",  ImageCat);
+    fprintf (stderr, "GSCFILE                %s\n",  GSCFILE);
+    fprintf (stderr, "CATDIR                 %s\n",  CATDIR);
+    exit (0);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/liststats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/liststats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/liststats.c	(revision 22322)
@@ -0,0 +1,119 @@
+# include "relphot.h"
+
+enum {M_MEAN, M_MEDIAN, M_WT_MEAN, M_INNER_MEAN, 
+      M_INNER_WTMEAN, M_CHI_INNER_MEAN, M_CHI_INNER_WTMEAN};
+
+static int statmode;
+
+void initstats (char *mode) {
+
+  statmode = -1;
+  if (!strcmp (mode, "MEAN")) statmode = M_MEAN;
+  if (!strcmp (mode, "MEDIAN")) statmode = M_MEDIAN;
+  if (!strcmp (mode, "WT_MEAN")) statmode = M_WT_MEAN;
+  if (!strcmp (mode, "INNER_MEAN")) statmode = M_INNER_MEAN;
+  if (!strcmp (mode, "INNER_WTMEAN")) statmode = M_INNER_WTMEAN;
+  if (!strcmp (mode, "CHI_INNER_MEAN")) statmode = M_CHI_INNER_MEAN;
+  if (!strcmp (mode, "CHI_INNER_WTMEAN")) statmode = M_CHI_INNER_WTMEAN;
+
+  if (statmode == -1) {
+    fprintf (stderr, "ERROR: invalid stats mode: %s\n", mode);
+    exit (1);
+  }
+}
+
+int liststats (double *value, double *dvalue, int N, StatType *stats) {
+  
+  int i, ks, ke, Nm;
+  double Mo, dMo, M, dM, X2, dS, *chi;
+
+  ke = ks = dMo = 0;
+
+  stats[0].Nmeas = N;
+  stats[0].mean  = 0;
+  stats[0].sigma = 0;
+  stats[0].error = 0;
+  stats[0].chisq = 0;
+  if (N < 1) return (FALSE);
+
+  dsortpair (value, dvalue, N);
+  stats[0].median = value[(int)(0.5*N)];
+  stats[0].min    = value[0];
+  stats[0].max    = value[N-1];
+
+  switch (statmode) {
+  case M_MEDIAN:
+    ks = 0;
+    ke = N;
+    Mo = stats[0].median;
+    Nm = N;
+    goto chisq;
+    break;
+  case M_MEAN:
+  case M_WT_MEAN:
+    ks = 0;
+    ke = N;
+    break;
+  case M_INNER_MEAN:
+  case M_INNER_WTMEAN:
+  case M_CHI_INNER_MEAN:
+  case M_CHI_INNER_WTMEAN:
+    ks = 0.25*N + 0.50;
+    ke = 0.75*N + 0.25;
+    if (N <= 3) {
+      ks = 0;
+      ke = N;
+    }
+    break;
+  }    
+
+  if ((statmode == M_CHI_INNER_MEAN) || (statmode == M_CHI_INNER_WTMEAN)) {
+    ALLOCATE (chi, double, N);
+    for (i = 0; i < N; i++) {
+      chi[i] = (value[i] - stats[0].median) / dvalue[i];
+    }
+    dsortthree (chi, value, dvalue, N);
+    free (chi);
+  }
+
+  /* calculating the per-star offset based on the weighted average */
+  M = dM = Nm = 0;
+  if ((statmode == M_WT_MEAN) || (statmode == M_INNER_WTMEAN) || (statmode == M_CHI_INNER_WTMEAN)) {
+    for (i = ks; i < ke; i++) {
+      M   += value[i] / SQ (dvalue[i]);
+      dM  += 1.0 / SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / dM;
+    dMo = sqrt (1.0 / dM);
+  } else {
+    for (i = ks; i < ke; i++) {
+      M   += value[i];
+      dM  += SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / (double) Nm;
+    dMo = sqrt (dM / (double) Nm);
+  }
+
+ chisq:
+  /* find sigma and chisq */
+  X2 = dS = 0;
+  for (i = ks; i < ke; i++) {
+    M  = SQ (value[i] - Mo);
+    dM = SQ (dvalue[i]);
+    X2 += M / dM;
+    dS += M;
+  }
+  X2 = X2 / Nm;
+  dS = sqrt (dS / Nm);
+
+  stats[0].mean  = Mo;
+  stats[0].Nmeas = Nm;
+  stats[0].chisq = X2;
+  stats[0].sigma = dS;
+  stats[0].error = dMo;
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/load_catalogs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/load_catalogs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/load_catalogs.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "relphot.h"
+
+Catalog *load_catalogs (SkyList *skylist, int *Ncatalog) {
+
+  int i, Nstar;
+  Catalog *catalog, tcatalog;
+
+  if (VERBOSE) fprintf (stderr, "loading catalog data\n");
+
+  ALLOCATE (catalog, Catalog, skylist[0].Nregions);
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set up the basic catalog info
+    tcatalog.filename = skylist[0].filename[i];
+    tcatalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    tcatalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    tcatalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_SECF;    // don't need to load all data at this point
+    tcatalog.Nsecfilt  = GetPhotcodeNsecfilt ();               // set the desired number in case we need to create the catalog
+
+    if (!dvo_catalog_open (&tcatalog, skylist[0].regions[i], VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", tcatalog.filename);
+      exit (1);
+    }
+    if (VERBOSE && !tcatalog.Naves_disk) fprintf (stderr, "no data in %s, skipping\n", tcatalog.filename);
+
+    // select only the brighter stars
+    bcatalog (&catalog[i], &tcatalog);
+    dvo_catalog_unlock (&tcatalog);
+    dvo_catalog_free (&tcatalog);
+  }
+
+  // XXX keep this test?
+  Nstar = 0;
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    Nstar += catalog[i].Naverage;
+  }
+  if (Nstar < 2) { 
+    fprintf (stderr, "warning: insufficient stars %d\n", Nstar);
+  }
+
+  // XXX consider only returning the populated catalogs
+  *Ncatalog = skylist[0].Nregions;
+  return (catalog);
+}
+
+/* this function loads all relevant catalog files for the first pass.  it currently loads the data
+   read only (SOFT lock) since it assumes the image table has been locked. if we go to the new
+   addstar locking paradigm, in which the images and catalogs are updated independently, then we may
+   need to use an XCLD lock here.  
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/load_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/load_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/load_images.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "relphot.h"
+
+SkyList *load_images (FITS_DB *db, char *regionName, SkyRegion *region, int RegionSelect) {
+
+  Image     *image, *subset;
+  int        Nimage, Nsubset, Nchar;
+  int       *LineNumber;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // determine the populated SkyRegions overlapping the requested area
+  if (RegionSelect) {
+    skylist = SkyListByPatch (sky, -1, region);
+  } else {
+    Nchar = strlen(regionName);
+    if (!strcmp (&regionName[Nchar-4], ".cpt")) regionName[Nchar-4] = 0;
+    skylist = SkyListByName (sky, regionName);
+  }
+
+  // convert database table to internal structure
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  // select the images which overlap the selected sky regions
+  subset = select_images (skylist, image, Nimage, &LineNumber, &Nsubset);
+
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, LineNumber, Nsubset);
+
+  initImages (subset, Nsubset);
+  initMosaics (subset, Nsubset);
+  
+  return (skylist);
+}
+
+int reload_images (FITS_DB *db) {
+
+  Image     *image;
+  int        Nimage, Nx, i;
+  VTable    *vtable;
+
+  image = getimages (&Nimage);
+
+  vtable = &db[0].vtable;
+
+  gfits_scan (vtable[0].header, "NAXIS1", "%d", 1, &Nx);
+  for (i = 0; i < Nimage; i++) {
+    memcpy (vtable[0].buffer[i], &image[i], Nx);
+  }
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/plot_scatter.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/plot_scatter.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/plot_scatter.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "relphot.h"
+   
+void plot_scatter (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, N, Ntot;
+  float Mrel, Mcal, Mmos, Mgrid;
+  double *xlist, *ylist, *ilist;
+  Graphdata graphdata;
+
+  Ntot = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Ntot += catalog[i].average[j].Nmeasure;
+    }
+  }
+  ALLOCATE (xlist, double, Ntot);
+  ALLOCATE (ylist, double, Ntot);
+  ALLOCATE (ilist, double, Ntot);
+
+  N = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+      m = catalog[i].average[j].measureOffset;
+
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	Mcal = getMcal  (m, i);
+	if (isnan(Mcal)) continue;
+	Mmos = getMmos  (m, i);
+	if (isnan(Mmos)) continue;
+	Mgrid = getMgrid (m, i);
+	if (isnan(Mgrid)) continue;
+
+	Mrel = catalog[i].secfilt[PhotNsec*j+PhotSec].M;
+	xlist[N] = Mrel;
+	ylist[N] = PhotSys  (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]) - Mcal - Mmos - Mgrid - Mrel;
+	ilist[N] = PhotInst (&catalog[i].measure[m]);
+	N++;
+      }
+    }
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.xmin = PlotMmin;
+  graphdata.xmax = PlotMmax;
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, ylist, N, "mag vs dmag", "Mag.png");
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, ilist, ylist, N, "imag vs dmag", "iMag.png");
+  free (xlist);
+  free (ylist);
+  free (ilist);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/plotstuff.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/plotstuff.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/plotstuff.c	(revision 22322)
@@ -0,0 +1,149 @@
+# include "relphot.h"
+# include <signal.h>
+
+static int Xgraph[5] = {0,0,0,0,0};
+static int active;
+
+/*
+static union { unsigned char c[4]; float f; } f_undef = { {0xff, 0xff, 0xff, 0xfe} };
+# define fUNDEF (f_undef.f)
+*/
+
+static union { unsigned char c[8]; float d; } d_undef = { {0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00} };
+# define dUNDEF (d_undef.d)
+
+void XDead () {
+  signal (SIGPIPE, XDead);
+  fprintf (stderr, "kapa is dead, must restart\n");
+  Xgraph[active] = -1;
+}
+
+int open_graph (int N) {
+
+  char name[100];
+
+  sprintf (name, "gastro [%d]", N);
+
+  // XXX need to allow for KapaOpenNamed socket instead
+  Xgraph[N] = KapaOpen ("kapa", name);
+
+  if (Xgraph[N] < 0) {
+    fprintf (stderr, "error starting kapa\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+void DonePlotting (Graphdata *graphmode, int N) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaBox (Xgraph[N], graphmode);
+  return;
+}
+
+void JpegPlot (Graphdata *graphmode, int N, char *filename) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaPNG (Xgraph[N], filename);
+  return;
+}
+
+void PSPlot (Graphdata *graphmode, int N, char *filename) {
+
+  if (Xgraph[N] == 0) return;
+
+  KiiPS (Xgraph[N], filename, TRUE, KAPA_PS_NEWPLOT, "default");
+  return;
+}
+
+void PrepPlotting (int Npts, Graphdata *graphmode, int N) {
+
+  if (Npts < 1) return;
+
+  if (Xgraph[N] < 1) if (!open_graph(N)) return;
+
+  KapaClearSections (Xgraph[N]);
+
+  KapaPrepPlot (Xgraph[N], Npts, graphmode);
+  return;
+}
+
+void PlotLabel (char *string, int N) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaSendLabel (Xgraph[N], string, 2);
+}
+
+void PlotVector (int Npts, double *vect, int mode, int N, char *type) {
+
+  float *values;
+  int i;
+
+  if (Npts < 1) return;
+
+  ALLOCATE (values, float, Npts);
+  for (i = 0; i < Npts; i++) {
+    values[i] = vect[i];
+  }
+
+  KapaPlotVector (Xgraph[N], Npts, values, type);
+  free (values);
+  return;
+}
+
+void plot_list (Graphdata *graphdata, double *xlist, double *ylist, int N, char *label, char *file) {
+
+  int i;
+  StatType stats;
+  
+  stats.min = stats.max = xlist[0];
+  for (i = 0; i < N; i++) {
+    stats.min = MIN (stats.min, xlist[i]);
+    stats.max = MAX (stats.max, xlist[i]);
+  }
+  if (graphdata[0].xmin == dUNDEF) graphdata[0].xmin = 1.05*stats.min - 0.05*stats.max;
+  if (graphdata[0].xmax == dUNDEF) graphdata[0].xmax = 1.05*stats.max - 0.05*stats.min;
+
+  stats.min = stats.max = ylist[0];
+  for (i = 0; i < N; i++) {
+    stats.min = MIN (stats.min, ylist[i]);
+    stats.max = MAX (stats.max, ylist[i]);
+  }
+  if (graphdata[0].ymin == dUNDEF) graphdata[0].ymin = 1.05*stats.min - 0.05*stats.max;
+  if (graphdata[0].ymax == dUNDEF) graphdata[0].ymax = 1.05*stats.max - 0.05*stats.min;
+
+  PrepPlotting (N, graphdata, 0);
+  PlotVector (N, xlist, 0, 0, "x");
+  PlotVector (N, ylist, 1, 0, "y");
+  if (label != NULL) PlotLabel (label, 0);
+  DonePlotting (graphdata, 0);
+
+  if ((file != NULL) && SAVEPLOT) JpegPlot (graphdata, 0, file);
+  if (PLOTDELAY > 500000) {
+    fprintf (stdout, "press return\n"); 
+    fscanf (stdin, "%*c");
+  } else {
+    usleep (PLOTDELAY);
+  }
+}
+
+void plot_defaults (Graphdata *graphdata) {
+
+  KapaInitGraph (graphdata);
+  graphdata[0].style = 2;
+  graphdata[0].ptype = 2;
+  graphdata[0].ltype = 0;
+  graphdata[0].etype = 0;
+  graphdata[0].color = black;
+  graphdata[0].lweight = 0;
+  graphdata[0].size = 0.5;
+
+  graphdata[0].xmin = dUNDEF;
+  graphdata[0].xmax = dUNDEF;
+  graphdata[0].ymin = dUNDEF;
+  graphdata[0].ymax = dUNDEF;
+   
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/reload_catalogs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/reload_catalogs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/reload_catalogs.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "relphot.h"
+
+void reload_catalogs (SkyList *skylist) {
+
+  int i;
+  int status;
+  struct stat filestat;
+  Catalog catalog;
+
+  if (VERBOSE) fprintf (stderr, "re-loading catalog data\n");
+
+  /* load data from each region file */
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    catalog.filename = skylist[0].filename[i];
+
+    // only update existing db tables
+    status = stat (catalog.filename, &filestat);
+    if ((status == -1) && (errno == ENOENT)) {
+      if (VERBOSE) fprintf (stderr, "no file %s, skipping\n", catalog.filename);
+      continue;
+    }
+
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();               // set the desired number in case we need to create the catalog
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (VERBOSE && (catalog.Naves_disk == 0)) {
+	fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+	dvo_catalog_unlock (&catalog);
+	dvo_catalog_free (&catalog);
+	continue;
+    }
+
+    initImageBins  (&catalog, 1);
+    initMosaicBins (&catalog, 1);
+    initGridBins   (&catalog, 1);
+
+    findImages (&catalog, 1);
+    findMosaics (&catalog, 1);
+
+    setMrelFinal (&catalog);
+    dvo_catalog_save (&catalog, VERBOSE); 
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+
+    freeImageBins (1);
+    freeMosaicBins (1);
+    freeGridBins (1);
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/relphot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/relphot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/relphot.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include "relphot.h"
+
+int main (int argc, char **argv) {
+
+  int i, status, Ncatalog;
+  Catalog *catalog;
+  FITS_DB db;
+
+  SkyList *skylist = NULL;
+
+  /* get configuration info, args */
+  initialize (argc, argv);
+
+  /* the object analysis is a separate process iterating over catalogs */
+  if (UpdateAverages) {
+    relphot_objects ();
+    exit (0);
+  }
+
+  /* register database handle with shutdown procedure */
+  set_db (&db);
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+
+  /* lock and load the image db table */
+  status = dvo_image_lock (&db, ImageCat, 60.0, (UPDATE ? LCK_XCLD : LCK_SOFT));
+  if (!status && UPDATE) {
+    fprintf (stderr, "error\n");
+    Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+  }
+  // if the file is missing, db.dbstate will have a value of either:
+  // LCK_EMPTY (if UPDATE) or LCK_MISSING (if !UPDATE)
+  if ((db.dbstate == LCK_EMPTY) || (db.dbstate == LCK_MISSING)) {
+    // XXX get ZERO_POINT from config
+    dvo_image_create (&db, 25.0);
+    // Shutdown ("ERROR: No images in catalog %s (1)", db.filename);
+  } else {
+    if (!dvo_image_load (&db, VERBOSE, FALSE)) Shutdown ("can't read image catalog %s", db.filename);
+  }
+
+  /* load regions and images based on specified sky patch */
+  // XXX need to mimic old-style load by passing patch name
+  // XXX need to reduce number of global variables in use.
+  // XXX this is fairly lame: argv[1] is photcode if UserPatchSelect is true
+  skylist = load_images (&db, argv[1], &UserPatch, UserPatchSelect);
+
+  /* unlock, if we can (else, unlocked below) */
+  if (!UPDATE) dvo_image_unlock (&db); 
+
+  /* load catalog data from region files */
+  catalog = load_catalogs (skylist, &Ncatalog);
+  
+  /* add in a loop over the catalogs calling dvo_catalog_chipcoords */
+
+  /* match measurements with images, mosaics */
+  initImageBins  (catalog, Ncatalog);
+  initMosaicBins (catalog, Ncatalog);
+  initGridBins   (catalog, Ncatalog);
+  initMrel (catalog, Ncatalog);
+
+  findImages (catalog, Ncatalog);
+  findMosaics (catalog, Ncatalog);  /* also sets Grid values */
+  SAVEPLOT = FALSE;
+
+  setExclusions (catalog, Ncatalog);
+
+  if (PLOTSTUFF) {
+    plot_star_coords (catalog, Ncatalog);
+    plot_mosaic_fields (catalog);
+  }
+
+  /* determine fit values */
+  for (i = 0; i < NLOOP; i++) {
+    setMrel  (catalog, Ncatalog);
+    setMcal  (catalog, FALSE);
+    setMmos  (catalog, FALSE);
+    setMgrid (catalog);
+    
+    if (PLOTSTUFF) {
+      plot_scatter (catalog, Ncatalog); 
+      plot_grid (catalog); 
+      plot_mosaics ();
+      plot_images ();
+      plot_stars (catalog, Ncatalog);
+      plot_chisq (catalog, Ncatalog);
+    }
+    if ((i == 1) || (i == 5) || (i ==  9) || (i == 13)) clean_measures (catalog, Ncatalog, FALSE); 
+    if ((i == 2) || (i == 6) || (i == 10) || (i == 14)) clean_stars (catalog, Ncatalog);
+    if ((i == 4) || (i == 8) || (i == 12) || (i == 16)) clean_mosaics (catalog, Ncatalog);
+    if ((i == 4) || (i == 8) || (i == 12) || (i == 16)) clean_images ();
+    global_stats (catalog, Ncatalog);
+  }
+
+  SAVEPLOT = TRUE;
+  plot_scatter (catalog, Ncatalog); 
+  plot_grid (catalog); 
+  plot_mosaics ();
+  plot_images ();
+  plot_stars (catalog, Ncatalog);
+  plot_chisq (catalog, Ncatalog);
+
+  if (USE_GRID) dump_grid ();
+  if (!UPDATE) exit (0);
+
+  /* set Mcal & Mmos for bad images */
+  setMcal  (catalog, TRUE);
+  setMmos  (catalog, TRUE);
+
+  /* at this point, we have correct cal coeffs in the image/mosaic structures */
+  for (i = 0; i < Ncatalog; i++) dvo_catalog_free (&catalog[i]);
+  freeImageBins (Ncatalog);
+  freeMosaicBins (Ncatalog);
+  freeGridBins (Ncatalog);
+
+  /* load catalog data from region files, update Mrel include all data */
+  reload_catalogs (skylist);
+  setMcalFinal ();
+
+  reload_images (&db);
+  
+  dvo_image_update (&db, VERBOSE);
+  dvo_image_unlock (&db); 
+
+  exit (0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/relphot_objects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/relphot_objects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/relphot_objects.c	(revision 22322)
@@ -0,0 +1,65 @@
+# include "relphot.h"
+
+int relphot_objects () {
+
+  int i, j, k, m;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+  Catalog catalog;
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // determine the populated SkyRegions overlapping the requested area (default depth)
+  skylist = SkyListByPatch (sky, -1, &UserPatch);
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set up the basic catalog info
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (!catalog.Naves_disk) {
+      if (VERBOSE) fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // XXX consider what gets reset (only PHOTOM flags)
+    if (RESET) {
+      for (j = 0; j < catalog.Naverage; j++) {
+	catalog.average[j].code = 0;
+	m = catalog.average[j].measureOffset;
+	for (k = 0; k < catalog.average[j].Nmeasure; k++) {
+	  catalog.measure[m+k].dbFlags = 0;
+	}
+      }
+    }
+
+    setMave (&catalog, 1);
+
+    if (!UPDATE) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+    
+    if (VERBOSE) fprintf (stderr, "saving catalog %s\n", catalog.filename);
+    dvo_catalog_save (&catalog, VERBOSE); 
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/select_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/select_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/select_images.c	(revision 22322)
@@ -0,0 +1,239 @@
+# include "relphot.h"
+
+/* this function returns a list of all images which overlap the given SkyList (set of
+   SkyRegions).  All images in the image catalog are tested once, so there is no check that an
+   image already has been included.  LineNum stores the locations in the Image database of the
+   list of images */
+
+typedef struct {
+  double Xc[5];
+  double Yc[5];
+  double Rc;
+  double Dc;
+} SkyRegionCoords;
+
+Image *select_images (SkyList *skylist, Image *timage, int Ntimage, int **LineNumber, int *Nimage) {
+  
+  Image *image;
+  int i, j, k, m, found, nimage, NIMAGE;
+  int InRange, ecode;
+  double Ri[5], Di[5], Xi[5], Yi[5], dx, dy;
+  int *line_number;
+  Coords tcoords;
+  SkyRegionCoords *skycoords;
+  
+  if (skylist[0].Nregions < 1) {
+    *Nimage = 0;
+    *LineNumber = NULL;
+    if (VERBOSE) fprintf (stderr, "no matching sky regions\n");
+    return NULL;
+  }
+
+  // the comparison is made in the catalog local projection. below we set crval1,2
+  tcoords.crpix1 = tcoords.crpix2 = 0.0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1  = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2  = tcoords.pc2_1 = 0.0;
+  strcpy (tcoords.ctype, "RA---TAN");
+
+  ALLOCATE (skycoords, SkyRegionCoords, skylist[0].Nregions);
+
+  /* compare with each region file */
+  for (i = 0; i < skylist[0].Nregions; i++) { 
+
+    /* we make positional comparisons in the projection of catalog */
+    skycoords[i].Rc = 0.5*(skylist[0].regions[i][0].Rmax + skylist[0].regions[i][0].Rmin);
+    skycoords[i].Dc = 0.5*(skylist[0].regions[i][0].Dmax + skylist[0].regions[i][0].Dmin);
+    tcoords.crval1 = skycoords[i].Rc;
+    tcoords.crval2 = skycoords[i].Dc;
+
+    /* define catalog corners */
+    RD_to_XY (&skycoords[i].Xc[0], &skycoords[i].Yc[0], skylist[0].regions[i][0].Rmin, skylist[0].regions[i][0].Dmin, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[1], &skycoords[i].Yc[1], skylist[0].regions[i][0].Rmax, skylist[0].regions[i][0].Dmin, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[2], &skycoords[i].Yc[2], skylist[0].regions[i][0].Rmax, skylist[0].regions[i][0].Dmax, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[3], &skycoords[i].Yc[3], skylist[0].regions[i][0].Rmin, skylist[0].regions[i][0].Dmax, &tcoords);
+    skycoords[i].Xc[4] = skycoords[i].Xc[0];    
+    skycoords[i].Yc[4] = skycoords[i].Yc[0];    
+
+    dx = 0.02*(skycoords[i].Xc[2] - skycoords[i].Xc[0]);
+    dy = 0.02*(skycoords[i].Yc[2] - skycoords[i].Yc[0]);
+    skycoords[i].Xc[0] -= dx; skycoords[i].Yc[0] -= dy;
+    skycoords[i].Xc[1] += dx; skycoords[i].Yc[1] -= dy;
+    skycoords[i].Xc[2] += dx; skycoords[i].Yc[2] += dy;
+    skycoords[i].Xc[3] -= dx; skycoords[i].Yc[3] += dy;
+    skycoords[i].Xc[4] -= dx; skycoords[i].Yc[4] -= dy;
+  }
+
+  if (VERBOSE) fprintf (stderr, "finding images\n");
+  BuildChipMatch (timage, Ntimage);
+
+  nimage = 0;
+  NIMAGE = 100;
+  ALLOCATE (image, Image, NIMAGE);
+  ALLOCATE (line_number, int, NIMAGE);
+  
+  // go through the complete list of images, selecting ones which overlap any region
+  for (i = 0; i < Ntimage; i++) {
+      
+    /* exclude images by photcode */
+    ecode = GetPhotcodeEquivCodebyCode (timage[i].photcode);
+    if (ecode != photcode[0].code) continue;
+
+    /* exclude images by time */
+    if (TimeSelect) {
+      if (timage[i].tzero < TSTART) continue;
+      if (timage[i].tzero > TSTOP) continue;
+    }
+    
+    if (!FindMosaicForImage (timage, Ntimage, i)) continue;
+
+    /* define image corners */
+    Xi[0] = 0;            Yi[0] = 0;
+    Xi[1] = timage[i].NX; Yi[1] = 0;
+    Xi[2] = timage[i].NX; Yi[2] = timage[i].NY;
+    Xi[3] = 0;            Yi[3] = timage[i].NY;
+    Xi[4] = 0;            Yi[4] = 0;
+    found = FALSE;
+
+    /* transform to ra,dec */
+    for (j = 0; j < 5; j++) {
+      XY_to_RD (&Ri[j], &Di[j], Xi[j], Yi[j], &timage[i].coords);
+    }
+
+    /* compare with each region file */
+    for (m = 0; (m < skylist[0].Nregions) && !found; m++) { 
+
+      /* we make positional comparisons in the projection of catalog */
+      tcoords.crval1 = skycoords[m].Rc;
+      tcoords.crval2 = skycoords[m].Dc;
+
+      /* transform to ra,dec */
+      InRange = TRUE;
+      for (j = 0; (j < 5) && InRange; j++) {
+	InRange = RD_to_XY (&Xi[j], &Yi[j], Ri[j], Di[j], &tcoords);
+      }
+      if (!InRange) continue;
+
+      /* check if image corner inside catalog */
+      for (j = 0; (j < 4) && !found; j++) {
+	found = corner_check (&Xi[j], &Yi[j], &skycoords[m].Xc[0], &skycoords[m].Yc[0]);
+      }
+      /* check if catalog corner inside image */
+      for (j = 0; (j < 4) && !found; j++) {
+	found = corner_check (&skycoords[m].Xc[j], &skycoords[m].Yc[j], &Xi[0], &Yi[0]);
+      }
+      /* check if edges cross */
+      for (j = 0; (j < 4) && !found; j++) {
+	for (k = 0; (k < 4) && !found; k++) {
+	  found = edge_check (&Xi[j], &Yi[j], &skycoords[m].Xc[k], &skycoords[m].Yc[k]);
+	}
+      }
+      if (!found) continue;
+
+      image[nimage] = timage[i]; 
+      /* always allow 'few' images to succeed, if possible */
+      if (image[nimage].code & ID_IMAGE_FEW) { 
+	image[nimage].code &= ~(ID_IMAGE_FEW | ID_IMAGE_POOR);
+      }
+      if (RESET) {
+	assignMcal (&image[nimage], (double *) NULL, -1);
+	image[nimage].dMcal = NAN;
+	image[nimage].code &= ~ID_IMAGE_POOR;
+      }
+      line_number[nimage] = i;
+      nimage ++;
+      if (nimage == NIMAGE) {
+	NIMAGE += 100;
+	REALLOCATE (image, Image, NIMAGE);
+	REALLOCATE (line_number, int, NIMAGE);
+      }
+    }
+  }
+      
+  if (VERBOSE) fprintf (stderr, "found %d images\n", nimage);
+
+  REALLOCATE (image, Image, MAX (nimage, 1));
+  REALLOCATE (line_number, int, MAX (nimage, 1));
+  free (skycoords);
+
+  *Nimage  = nimage;
+  *LineNumber = line_number;
+  return (image);
+}
+
+/* check if line between points 0 and 1 of x1
+   crosses line between points 0 and 1 of x2 */
+int edge_check (double *x1, double *y1, double *x2, double *y2) {
+
+  double theta1, theta2;
+  double Theta1, Theta2;
+
+  theta1 = opening_angle (x1[0], y1[0], x2[0], y2[0], x1[1], y1[1]); 
+  theta2 = opening_angle (x1[0], y1[0], x2[0], y2[0], x2[1], y2[1]); 
+
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  Theta1 = theta1;
+  Theta2 = theta2;
+  theta1 = opening_angle (x2[0], y2[0], x1[1], y1[1], x2[1], y2[1]); 
+  theta2 = opening_angle (x2[0], y2[0], x1[1], y1[1], x1[0], y1[0]); 
+  
+ 
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+
+/* check if point x1,y1 is in box formed by x2[0-4] */
+int corner_check (double *x1, double *y1, double *x2, double *y2) {
+
+  int i;
+  double theta;
+
+  theta = 0;
+
+  for (i = 0; i < 4; i++) {
+    theta += opening_angle (x2[i], y2[i], x1[0], y1[0], x2[i+1], y2[i+1]); 
+  }
+  if (fabs(theta) > 6) {
+    return (TRUE);
+  } else {
+    return (FALSE);
+  }
+}
+
+/* returns the opening angle between the three points (2 is in middle) 
+   in range -pi to pi */
+
+double opening_angle (double x1, double y1, double x2, double y2, double x3, double y3) {
+
+  double dx1, dy1, dx2, dy2, ct, st, theta;
+
+  dx1 = x1 - x2;
+  dy1 = y1 - y2;
+  
+  dx2 = x3 - x2;
+  dy2 = y3 - y2;
+  
+  ct = (dx1*dx2 + dy1*dy2);
+  st = (dx1*dy2 - dx2*dy1);
+
+  theta = atan2 (st, ct);
+
+  return (theta);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/setExclusions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/setExclusions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/setExclusions.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "relphot.h"
+
+int setExclusions (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, Narea, Nnocal, Ngood, ecode;
+  Coords *coords;
+  double r, d, x, y;
+
+  Ngood = Nnocal = Narea = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      m = catalog[i].average[j].measureOffset;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+
+	/* select measurements by photcode */
+	ecode = GetPhotcodeEquivCodebyCode (catalog[i].measure[m].photcode);
+	if (ecode != photcode[0].code) goto mark_nocal;
+	
+	/* select measurements by time */
+	if (TimeSelect) {
+	  if (catalog[i].measure[m].t < TSTART) goto mark_nocal;
+	  if (catalog[i].measure[m].t > TSTOP) goto mark_nocal;
+	}
+
+	/* select measurements by mag limit */
+	if (AreaSelect) {
+	  r = catalog[i].average[j].R + catalog[i].measure[m].dR / 3600.0;
+	  d = catalog[i].average[j].D + catalog[i].measure[m].dD / 3600.0;
+	  if ((coords = getCoords (m, i)) == NULL) goto markbad;
+	  RD_to_XY (&x, &y, r, d, coords);
+	  if (x < AreaXmin) goto markbad;
+	  if (x > AreaXmax) goto markbad;
+	  if (y < AreaYmin) goto markbad;
+	  if (y > AreaYmax) goto markbad;
+	}
+	Ngood ++;
+	continue;
+
+      markbad:
+	catalog[i].measure[m].dbFlags |= ID_MEAS_AREA;
+	Narea ++;
+	continue;
+	
+      mark_nocal:
+	catalog[i].measure[m].dbFlags |= ID_MEAS_NOCAL;
+	Nnocal ++;
+	continue;
+      }
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "%d measurements marked by area\n", Narea);
+  if (VERBOSE) fprintf (stderr, "%d measurements marked nocal\n", Nnocal);
+  if (VERBOSE) fprintf (stderr, "%d measurements kept for analysis\n", Ngood);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/setMrelFinal.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/setMrelFinal.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/setMrelFinal.c	(revision 22322)
@@ -0,0 +1,150 @@
+# include "relphot.h"
+
+void setMrelFinal (Catalog *catalog) {
+
+  int i, j, m, ecode;
+
+  /* if we reset the catalog, reset all the current measurements */
+  if (RESET) {
+
+    for (i = 0; i < catalog[0].Naverage; i++) {
+      catalog[0].secfilt[PhotNsec*i+PhotSec].M  = NAN;
+      catalog[0].secfilt[PhotNsec*i+PhotSec].dM = NAN;
+      catalog[0].secfilt[PhotNsec*i+PhotSec].Xm = NAN_S_SHORT;
+
+      m = catalog[0].average[i].measureOffset;
+      for (j = 0; j < catalog[0].average[i].Nmeasure; j++, m++) {
+	
+	/* select measurements by photcode */
+	ecode = GetPhotcodeEquivCodebyCode (catalog[0].measure[m].photcode);
+	if (ecode != photcode[0].code) continue;
+	
+	/* select measurements by time */
+	if (TimeSelect) {
+	  if (catalog[0].measure[m].t < TSTART) continue;
+	  if (catalog[0].measure[m].t > TSTOP) continue;
+	}
+	
+	catalog[0].measure[m].Mcal = 0;
+	catalog[0].measure[m].dbFlags &= 0xff00;
+	catalog[0].measure[m].dbFlags &= ~ID_MEAS_POOR_PHOTOM;
+	catalog[0].measure[m].dbFlags &= ~ID_MEAS_SKIP_PHOTOM;
+	catalog[0].measure[m].dbFlags &= ~ID_MEAS_AREA;
+	catalog[0].measure[m].dbFlags &= ~ID_MEAS_NOCAL;
+      }
+    }
+  }
+
+  setExclusions (catalog, 1);  /* mark by area */
+
+  /* set catalog[0].found[i] = FALSE */
+  ALLOCATE (catalog[0].found, int, MAX (1, catalog[0].Naverage));
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    catalog[0].found[i] = FALSE;
+  }
+
+  for (i = 0; i < 5; i++) {
+    skip_measurements (catalog, i);       /* set ID_MEAS_SKIP for measures to be skipped */
+    setMrelOutput  (catalog, 1, FALSE);   /* set Mrel using all measures not skipped */
+    clean_measures (catalog, 1, TRUE);    /* mark outliers ID_MEAS_POOR_PHOTOM */
+    setMrelOutput  (catalog, 1, TRUE);    /* set Mrel using remaining measures */
+  }
+  setMcalOutput (catalog, 1);
+
+  /* clear ID_STAR_POOR, ID_STAR_FEW, ID_MEAS_NOCAL values before writing ??? */
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    catalog[0].average[i].code &= ~ID_STAR_FEW;
+    catalog[0].average[i].code &= ~ID_STAR_POOR;
+    m = catalog[0].average[i].measureOffset;
+    for (j = 0; j < catalog[0].average[i].Nmeasure; j++, m++) {
+      catalog[0].measure[m].dbFlags &= ~ID_MEAS_NOCAL;
+    }
+  }
+}
+
+/* ID_MEAS_SKIP marks measurements which were not used to calculate Mrel */
+void skip_measurements (Catalog *catalog, int pass) {
+
+  int i, k, m, ecode, d1, d2;
+  int Ntot, Ntry, Nkeep, Nskip;
+  float mag;
+
+  Ntot = Ntry = Nskip = Nkeep = 0;
+
+  /* allow measures from images marked POOR and FEW */
+  if (pass >= 3) IMAGE_BAD = ID_IMAGE_NOCAL;
+  
+  /* allow measures marked as outliers (POOR) and off image region (AREA) */
+  if (pass >= 3) MEAS_BAD = ID_MEAS_NOCAL | ID_MEAS_SKIP_PHOTOM;
+
+  /* mark measures which should be ignored on second pass */
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    Ntot += catalog[0].average[i].Nmeasure;
+    if (catalog[0].found[i]) continue;
+
+    m = catalog[0].average[i].measureOffset;
+    for (k = 0; k < catalog[0].average[i].Nmeasure; k++, m++) {
+      Ntry++;
+
+      /* clear SKIP for all measures at first */
+      catalog[0].measure[m].dbFlags &= ~ID_MEAS_SKIP_PHOTOM;
+
+      /** never use these measurements (wrong photcode, bad time range) */
+      /* skipped via NOCAL, don't mark as skipped */
+      ecode = GetPhotcodeEquivCodebyCode (catalog[0].measure[m].photcode);
+      if (ecode != photcode[0].code) continue;
+
+      /* skip measurements by time range */
+      if (TimeSelect) {
+	if (catalog[0].measure[m].t < TSTART) goto skip;
+	if (catalog[0].measure[m].t > TSTOP) goto skip;
+      }
+
+      /* skip measurements with sat. dophot values */
+      if ((pass < 4) && (catalog[0].measure[m].dophot == 10)) goto skip;
+
+      /* skip measurements from BAD images and mosaics */
+      /* do NOT skip measurements without a matching image */
+      if (isnan(getMcal (m, 0))) goto skip;
+      if (isnan(getMmos (m, 0))) goto skip;
+
+      /* skip measurements by inst mag limit */
+      if ((pass < 4) && ImagSelect) {
+	mag = PhotInst (&catalog[0].measure[m]);
+	if (mag < ImagMin) goto skip;
+	if (mag > ImagMax) goto skip;
+      }
+
+      d1 = (catalog[0].measure[m].dophot == 1);
+      d2 = (catalog[0].measure[m].dophot == 2);
+
+      /* skip measurements by measurement error */
+      if ((pass < 2) && !(d1 || d2)) goto skip;
+
+      /* if ((pass < 1) && (catalog[0].measure[m].dM > SIGMA_LIM)) goto skip; */
+      if ((pass < 1) && !d1) goto skip;
+      Nkeep ++;
+
+      continue;
+
+    skip:
+      catalog[0].measure[m].dbFlags |= ID_MEAS_SKIP_PHOTOM;
+      Nskip ++;
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "pass %d, Ntot: %d, Ntry: %d, Nskip: %d, Nkeep: %d\n",
+			pass, Ntot, Ntry, Nskip, Nkeep);
+}
+
+/* dophot types:
+
+1 - star
+2 - galaxy
+3 - star
+4 - weak
+5 - edge
+7 - weak
+9 - weak
+10 - sat
+*/
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/sort.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/sort.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/sort.c	(revision 22322)
@@ -0,0 +1,182 @@
+# include "relphot.h"
+
+void sortA (double *X, int N) {
+
+  int l,j,ir,i;
+  double tX;
+  
+  if (N < 2) return;
+
+  l = N >> 1;
+  ir = N - 1;
+  for (;;) {
+    if (l > 0) {
+      l--;
+      tX = X[l];
+    }
+    else {
+      tX = X[ir];
+      X[ir] = X[0];
+      if (--ir == 0) {
+	X[0] = tX;
+	return;
+      }
+    }
+    i = l;
+    j = (l << 1) + 1;
+    while (j <= ir) {
+      if (j < ir && X[j] < X[j+1]) j++;
+      if (tX < X[j]) {
+	X[i] = X[j];
+	j += (i=j) + 1;
+      }
+      else j = ir + 1;
+    }
+    X[i] = tX;
+  }
+}
+
+void sortB (double *X, double *Y, int N) {
+
+  int l,j,ir,i;
+  double tX, tY;
+  
+  if (N < 2) return;
+
+  l = N >> 1;
+  ir = N - 1;
+  for (;;) {
+    if (l > 0) {
+      l--;
+      tX = X[l];
+      tY = Y[l];
+    }
+    else {
+      tX = X[ir];
+      X[ir] = X[0];
+      tY = Y[ir];
+      Y[ir] = Y[0];
+      if (--ir == 0) {
+	X[0] = tX;
+	Y[0] = tY;
+	return;
+      }
+    }
+    i = l;
+    j = (l << 1) + 1;
+    while (j <= ir) {
+      if (j < ir && X[j] < X[j+1]) j++;
+      if (tX < X[j]) {
+	X[i] = X[j];
+	Y[i] = Y[j];
+	j += (i=j) + 1;
+      }
+      else j = ir + 1;
+    }
+    X[i] = tX;
+    Y[i] = tY;
+  }
+}
+
+void sortC (double *X, double *Y, double *F1, double *F2, int N) {
+
+  int l,j,ir,i;
+  double tX, tY;
+  double t1, t2;
+  
+  if (N < 2) return;
+
+  l = N >> 1;
+  ir = N - 1;
+  for (;;) {
+    if (l > 0) {
+      l--;
+      tX = X[l];
+      tY = Y[l];
+      t1 = F1[l];
+      t2 = F2[l];
+    }
+    else {
+      tX = X[ir];
+      X[ir] = X[0];
+      tY = Y[ir];
+      Y[ir] = Y[0];
+      t1 = F1[ir];
+      F1[ir] = F1[0];
+      t2 = F2[ir];
+      F2[ir] = F2[0];
+      if (--ir == 0) {
+	X[0] = tX;
+	Y[0] = tY;
+	F1[0] = t1;
+	F2[0] = t2;
+	return;
+      }
+    }
+    i = l;
+    j = (l << 1) + 1;
+    while (j <= ir) {
+      if (j < ir && X[j] < X[j+1]) j++;
+      if (tX < X[j]) {
+	X[i] = X[j];
+	Y[i] = Y[j];
+	F1[i] = F1[j];
+	F2[i] = F2[j];
+	j += (i=j) + 1;
+      }
+      else j = ir + 1;
+    }
+    X[i] = tX;
+    Y[i] = tY;
+    F1[i] = t1;
+    F2[i] = t2;
+  }
+}
+
+void sortD (double *X, double *Y, double *Z, int N) {
+
+  int l,j,ir,i;
+  double tX, tY, tZ;
+  
+  if (N < 2) return;
+
+  l = N >> 1;
+  ir = N - 1;
+  for (;;) {
+    if (l > 0) {
+      l--;
+      tX = X[l];
+      tY = Y[l];
+      tZ = Z[l];
+    }
+    else {
+      tX = X[ir];
+      X[ir] = X[0];
+      tY = Y[ir];
+      Y[ir] = Y[0];
+      tZ = Z[ir];
+      Z[ir] = Z[0];
+      if (--ir == 0) {
+	X[0] = tX;
+	Y[0] = tY;
+	Z[0] = tZ;
+	return;
+      }
+    }
+    i = l;
+    j = (l << 1) + 1;
+    while (j <= ir) {
+      if (j < ir && X[j] < X[j+1]) j++;
+      if (tX < X[j]) {
+	X[i] = X[j];
+	Y[i] = Y[j];
+	Z[i] = Z[j];
+	j += (i=j) + 1;
+      }
+      else j = ir + 1;
+    }
+    X[i] = tX;
+    Y[i] = tY;
+    Z[i] = tZ;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/write_coords.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/write_coords.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/relphot/src/write_coords.c	(revision 22322)
@@ -0,0 +1,29 @@
+# include "relphot.h"
+
+# define CD_COORDS 1
+
+void write_coords (Header *header, Coords *coords) {
+
+  gfits_modify (header, "CTYPE1",   "%s",  1, "RA---TAN");
+  gfits_modify (header, "CTYPE2",   "%s",  1, "DEC--TAN");
+
+  gfits_modify (header, "CRVAL1",   "%lf", 1, coords[0].crval1);
+  gfits_modify (header, "CRVAL2",   "%lf", 1, coords[0].crval2);  
+
+  gfits_modify (header, "CRPIX1",   "%lf", 1, coords[0].crpix1);
+  gfits_modify (header, "CRPIX2",   "%lf", 1, coords[0].crpix2);
+
+# if (CD_COORDS)  
+  gfits_modify (header, "CD1_1",    "%le", 1, coords[0].pc1_1 * coords[0].cdelt1);
+  gfits_modify (header, "CD2_1",    "%le", 1, coords[0].pc2_1 * coords[0].cdelt1);
+  gfits_modify (header, "CD1_2",    "%le", 1, coords[0].pc1_2 * coords[0].cdelt2);
+  gfits_modify (header, "CD2_2",    "%le", 1, coords[0].pc2_2 * coords[0].cdelt2);
+# else
+  gfits_modify (header, "CDELT1",   "%le", 1, coords[0].cdelt1); 
+  gfits_modify (header, "CDELT2",   "%le", 1, coords[0].cdelt2);
+  gfits_modify (header, "PC001001", "%le", 1, coords[0].pc1_1);
+  gfits_modify (header, "PC001002", "%le", 1, coords[0].pc1_2);
+  gfits_modify (header, "PC002001", "%le", 1, coords[0].pc2_1);
+  gfits_modify (header, "PC002002", "%le", 1, coords[0].pc2_2);
+# endif
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/Makefile	(revision 22322)
@@ -0,0 +1,42 @@
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/shell
+
+shell:
+
+default: install
+
+DADS = \
+dads.cleanup
+
+GENERAL = \
+block checkdisks dumptape \
+fits2jpeg gopsfphot gosexphot \
+setpath scattercorrection \
+vncstart vnccheck gpc.cell.zoom
+
+IMSTATS = \
+irsex sexstats
+
+MKDETREND = \
+detstats rm.elixir
+
+REALTIME = \
+el_plots elixir.fork elixir.launch \
+getfocus getseeing gettemps mkjpeg
+
+SKYPROBE = \
+sp_command sp_phot \
+sp_plots sp_sexphot
+
+CODE = $(DADS) $(GENERAL) $(IMSTATS) $(MKDETREND) $(POSTRUN) $(PTOLEMY) $(REALTIME) $(SKYPROBE)
+
+install:
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	for file in $(CODE); do \
+	(chmod +x src/$$file && cd $(DESTBIN) && rm -f $$file && ln -s $(HOME)/src/$$file .); \
+	done
+
+# utilities #################################################
+clean:	
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,10 @@
+
+- shell 1.2
+  * fixed to work with mana 1.0 changes (only one cmd line input file)
+  * replace addspphot with addstar -skyprobe
+
+shell-1-1:
+  - changed sp_phot to use unified addstar, not addspphot
+
+shell-1-0:
+  - initial release
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/flips.notes
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/flips.notes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/flips.notes	(revision 22322)
@@ -0,0 +1,51 @@
+
+Some of the Elixir scripts call Flips programs.  Here is a list of the
+programs in Flips being used by Elixir:
+
+fr.defringe   : flipsrun (imcombred)
+fr.gtfringe   : flipsrun (imstat)
+defringe      : flipsrun (imcombred)
+doflips       : flatccd
+doflips       : darkccd
+flatten.flips : redccd 
+fr.mkrough    : flipsrun (imcombred)
+fr.smooth     : flipsrun (imfilter)
+gtfringe      : flipsrun (imstat)
+
+noni: file *
+RCS:            directory
+block:          C shell script text
+checkdisks:     C shell script text
+dads.cleanup:   C shell script text
+dads.remef:     C shell script text  - to be deprecated?
+dads.split:     C shell script text  - to be deprecated
+defringe:       C shell script text  - perl
+detstats:       C shell script text  - (mkdetrend)
+doflips:        C shell script text  - perl/detflips
+dumptape:       C shell script text
+el_plots:       C shell script text
+elixir.fork:    C shell script text
+elixir.launch:  C shell script text
+fits2jpeg:      C shell script text
+flatten.flips:  C shell script text  - perl
+flatten.subaru: C shell script text  - old
+fr.defringe:    C shell script text  - perl
+fr.mkrough:     C shell script text  - perl/detflips
+fr.smooth:      C shell script text  - perl TBD
+getfocus:       C shell script text
+getfringe:      bash shell script    - perl
+getseeing:      C shell script text
+gettemps:       C shell script text
+gopsfphot:      C shell script text
+gosexphot:      C shell script text
+irsex:          C shell script text
+mk.ptlist:      C shell script text
+mkjpeg:         C shell script text
+pt.export:      C shell script text
+rm.elixir:      C shell script text
+setpath:        C shell script text
+sexstats:       C shell script text
+sp_command:     C shell script text
+sp_phot:        C shell script text
+sp_plots:       C shell script text
+sp_sexphot:     C shell script text
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/notes.txt	(revision 22322)
@@ -0,0 +1,113 @@
+block			general
+checkdisks		CFHT specific
+dads.cleanup		
+dads.remef		
+dads.split		
+defringe		N CCDs, CFH12K Coord file
+detrend			Not Used?
+detstats		OK (check mana scripts)
+doflips			OK
+dumptape		
+elixir.fork		
+elixir.grab		
+elixir.report		
+fits2jpeg		
+fixdetrend		
+flatten.flips		OK
+flatten.mana		OK	
+focusstats		
+fr.ckfile		Not Used
+fr.defringe		N CCDs (check mana scripts)
+fr.mkimage		OK
+fr.mkrough		OK
+fr.mkstats		(check scripts)
+fr.smooth		Scale is CCD dependent
+fr.stats		OK
+getfocus		
+getseeing		
+gettemps		
+gopsfphot		
+gosexphot		
+gtfringe		(sh) OK
+imstats			CCD size specific -- should use DETSEC
+merge.lists		OK
+mk2d			Not Used
+mkjpeg			(realtime) N CCD specific
+normalize		OK
+normalize.invert	-
+phot.by.night		
+pt.export		(ptolemy export process)
+rm.elixir		(drop & convert to the exit status method)
+setpath			unused?
+sexstats		OK
+skyprobephot		
+split.mef		OK
+spphot			
+
+-
+
+cfht.names		a
+checkconfig		CFHT specific
+ckastrom		
+ckdetrend		
+ckphotom		
+ckrunid			
+ckvalid			
+clear.rawdir		
+create.imlists		nut used
+create.subset		OK
+dads.detrend		
+dads.jpeg		
+dads.keywords		
+darktime.cfht		CFH12K specific -- these curves must be measured for a new device!
+fixlists		
+fl.split2mef		not used?
+fr.detrend		Nccds from gt_names -- more general encapsulation
+fr.gtstats		Nccds
+fr.medbin		Nccds
+fr.medbin.spline	Nccds
+fr.mklists		OK
+fr.select		
+fr.subset		
+get.standards		
+grab.keywords		
+images.names		
+imclean.cfht		
+imselect		
+imstatqso		
+merge.lists		
+mkdetrend		
+mkfringe		
+mkptolemy.names		
+mktrans			
+mktreport		
+mosaic.merge		
+mosaic.stats		
+pt.run			
+seeingstats		
+validate		
+
+shell scripts that use awk -v (convert to perl):
+defringe
+fr.defringe
+fr.smooth
+gtfringe   (convert from sh anyway)
+
+shell scripts which use CFH12K dependent things:
+
+defringe		N CCDs, CFH12K Coord file
+detstats		(check mana scripts)
+fr.ckfile		Nccds
+fr.defringe		N CCDs (check mana scripts)
+fr.mkimage		(check scripts)
+fr.mkstats		(check scripts)
+fr.smooth		Scale is CCD dependent
+fr.stats		(check scripts)
+imstats			CCD size specific -- should use DETSEC
+normalize		OK
+split.mef		CCD size specific
+
+(watch out for header keywords -- make a complete dictionary)
+
+shell -> perl
+split.mef
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/programs.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/programs.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/doc/programs.txt	(revision 22322)
@@ -0,0 +1,61 @@
+block			: x
+checkdisks		: x
+ckastro			: x
+dads.cleanup		: x
+dads.remef		: x
+dads.split		: +
+doflips			: x
+dumptape		: x
+elixir.fork		: x
+fits2jpeg		: x
+flatten.flips		: +
+flatten.subaru		: x (fake)
+gettemps		: x
+gopsfphot		: x
+gosexphot		: +
+imstats			: +
+merge.lists		: x
+mk2d			: x
+normalize		: x
+normalize.invert	: x
+phot.by.night		: x
+pt.export		: x
+rm.elixir		: x
+setpath			: x
+sexstats		: x
+detstats		: x
+
+getfocus		: [which chips should be camera-dependent]
+getseeing		: [which chip should be camera-dependent]
+mkjpeg			: [scale and ccd list should be camera-dependend]
+
+mk.ptlist		: - [ccds in list need to be ID, not N - add option to imsearch?]
+el_plots		: - (in script mana/elixir)
+
+skyprobe should have its own minimal config file
+
+skyprobephot		: [cleanup up config reference]
+sp_expose		: x
+split.mef		: x
+spphot			: [abstract RAWDIR, etc?]
+sp_plots		: [abstract plot directory?]
+
+mkfringe stuff:
+
+fr.fitstats		: +
+fr.imstats		: +
+fr.mkimage		: 
+fr.mkrough		: 
+fr.mkstats		: 
+fr.smooth		: 
+fr.stats		: 
+getfringe		: 
+defringe		: - (not very clean; might work)
+fr.defringe		: - (some major issues..)
+
+
+already checked:
+
+mode.imstats
+mode.sextract
+mode.flips
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/block
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/block	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/block	(revision 22322)
@@ -0,0 +1,40 @@
+#!/bin/csh -f
+# wait for the file to exist
+
+set file=""
+set timeout=10;
+while ("$1" != "") 
+ switch ($1)
+  case -t: 
+   shift;
+   if ("$1" == "") goto escape
+   set timeout=$1;
+   breaksw;
+  case -*: 
+   goto escape
+   breaksw;
+  default:
+   if ("$file" != "") goto escape
+   set file=$1;
+   breaksw;
+ endsw
+ shift
+end
+
+if ("$file" == "") goto escape
+
+set i=0
+while ($i < $timeout)
+ if (-r $file) then
+  echo "block success: $i seconds $file"
+  exit 0;
+ endif
+ sleep 1
+ @ i++
+end
+echo "block failure: $i seconds $file";
+exit 1;
+
+escape:
+ echo "USAGE: block file [-t timeout]";
+ exit 2;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/checkdisks
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/checkdisks	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/checkdisks	(revision 22322)
@@ -0,0 +1,9 @@
+#!/bin/csh -f
+
+set disks="/data/elixir /data/elixir1 /data/elixir2 /data/elixir3 /h/skyprobe /data/ulu /data/hau /data/naupaka /data/mamane /data/naio /data/koa /data/kiawe /data/noni"
+
+foreach f ($disks)
+    set uspace = `sdf -a $f -M`
+    set aspace = `sdf -c $f -M`
+    echo $f $uspace $aspace | awk '{printf "%-20s %8.3f of %8.3f GB\n", $1, $2/1000, $3/1000}'
+end
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/dads.cleanup
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/dads.cleanup	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/dads.cleanup	(revision 22322)
@@ -0,0 +1,17 @@
+#!/bin/csh -f
+
+if ($#argv != 1) then
+ echo "USAGE: dads.remef (outbase)"
+ exit;
+endif
+
+set outdir=$1
+
+rm -f $outdir/*.raw
+rm -f $outdir/*.det
+rm -f $outdir/*.def
+
+# generate binned-by-12 image
+mosmc $outdir/*.fits $outdir.B12.fits 12
+
+echo "SUCCESS"
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/detstats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/detstats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/detstats	(revision 22322)
@@ -0,0 +1,86 @@
+#!/bin/csh
+
+if ($#argv != 7) then
+ echo "USAGE: detstats (list) (detimage) (type) (medbin) (tenbin) (stats) (imbin)"
+ exit;
+endif
+
+set nline = `wc -l $1 | awk '{print $1}'`
+
+if ($nline == 0) then
+ echo "no files in source list, skipping"
+ echo "ERROR"
+ exit;
+endif
+
+# find the appropriate script file
+set confdir=`gconfig -q CONFDIR`
+set script="$confdir/mana/flips.pro"
+
+# set up temp files
+set temp=`mktemp /tmp/detstats.XXXXXX`
+rm -f $temp
+rm -f $4
+rm -f $5
+rm -f $6
+rm -f $7
+
+# choose appropriate operation
+switch ($3) 
+  case flat:
+  case FLAT:
+    goto doflat;
+    breaksw;
+  case dark:
+  case DARK:
+  case bias:
+  case BIAS:
+    goto dodark;
+    breaksw;
+  default:
+    echo "unknown type $3";
+    echo ERROR;
+    exit;
+endsw
+
+doflat:
+    
+# flatstats (list) (detimage) (medlist) (tenlist) (statlist) 
+cat > $temp << END
+input $script
+flatstats $1 $2 $4 $5 $6 $7
+END
+
+mana --only $temp
+
+rm -f $temp
+
+echo "SUCCESS"
+exit; 
+
+dodark:
+    
+# darkstats (list) (detimage) (medlist) (tenlist) (statlist) 
+cat > $temp << END
+input $script
+darkstats $1 $2 $4 $5 $6 $7
+END
+
+mana --only $temp
+
+rm -f $temp
+
+echo "SUCCESS"
+exit; 
+
+
+# we are performing statistics on the detrended images:
+# input is a list of images for a given ccd, along with the
+# appropriate detrend image
+# 
+# for each image in the list, detrend the image, measure statistics,
+# create a binned-by-10 image, create a median-binned image, write
+# them out.  to be used by the mosaicing step
+#
+# need to create many output files: 1 medbin & 1 jpeg for each entry in list.
+# these need to be merged by a later program...       
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/dumptape
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/dumptape	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/dumptape	(revision 22322)
@@ -0,0 +1,81 @@
+#!/bin/csh
+
+# possible machines:
+# druid /dev/dlt
+
+# set dumpdir=/data/elixir2/eugene/archive/raw
+set dumpdir=.
+set host=`hostname | awk -F. '{print $1}'`
+set tape=""
+
+if ("$host" == "druid") then
+ set tape=/dev/dlt
+endif
+if ("$host" == "hoku") then
+ set tape=/dev/rmt/0mn
+endif
+if ("$host" == "kapu") then
+ set tape=/dev/rmt/0mn
+endif
+
+if ("$tape" == "") then
+ echo "ERROR: must use dumptape on druid, hoku, or kapu"
+ exit 1
+endif
+
+mt -f $tape stat
+
+echo "type 'y' to continue"
+
+set n=$<
+
+if ("$n" == "y") then
+ echo "continuing"
+else
+ echo "stopping"
+ exit 
+endif
+
+cd $dumpdir
+set dname=`date +%m%d%H`
+echo $dname
+
+set temp1=/tmp/dumptape.1.$$
+set temp2=extracted.$host.$dname.list
+set temp3=deleted.$host.$dname.list
+
+rm -f temp1
+
+set i=1
+
+while ($i < 32)
+ echo $i
+ tar xvf $tape >>& $temp1
+ if ($status) goto finish
+ mt -f $tape fsf
+ if ($status) goto finish
+ @ i++
+end
+
+finish:
+
+mt -f $tape rewind
+mt -f $tape offline
+
+mail eugene << END
+ done with tape on $host
+END
+ 
+grep fits $temp1 | awk '{s=index($0,"fits")}{n=substr($0,s-8,7)}{printf "%s.fits\n", n}' | fields INSTRUME | awk '($2=="CFH12K"){print $0}' > $temp2
+grep fits $temp1 | awk '{s=index($0,"fits")}{n=substr($0,s-8,7)}{printf "%s.fits\n", n}' | fields INSTRUME | awk '($2!="CFH12K"){print $0}' > $temp3
+
+foreach f (`awk '{print $1}' $temp3`) 
+ rm -f $f
+end
+
+# foreach f (`awk '{print $1}' $temp2`) 
+#  imsort $f -C config info here on this line
+# end
+
+rm -f $temp1
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/el_plots
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/el_plots	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/el_plots	(revision 22322)
@@ -0,0 +1,63 @@
+#!/bin/csh -f
+
+if (($#argv != 1) && ($#argv != 2)) goto usage
+
+setenv PATH /bin:/usr/bin:/usr/bin/X11:/apps/elixir/bin:/usr/local/bin
+
+# check that camera is ok:
+set camera=`gconfig CAMERA.CURRENT`
+set datdir=`gconfig DATDIR`
+if ($camera == "cfh12k") goto valid;
+if ($camera == "cfhtir") goto valid;
+if ($camera == "megacam") goto valid;
+if ($camera == "meganorth") goto valid;
+
+if ("$1" == "init") then
+  echo "current camera $camera, not supported by el_plots"
+endif
+exit 0;
+
+valid:
+# find the appropriate script file
+set confdir=`gconfig CONFDIR`
+set script="$confdir/mana/$camera.plots"
+set xhost=`gconfig XHOST`
+set xdisp=`gconfig XDISP`
+setenv DISPLAY $xdisp
+
+if ("$1" == "init") goto init;
+if ("$1" == "plot") goto plot;
+if ("$1" == "done") goto done;
+goto usage;
+
+init:
+ # test named Xserver, or start vnc on specified machine
+ xdpyinfo -display $xdisp >& /dev/null
+ if ($status) then
+   # X server is not running. start it now
+   echo "X server is not running. start it now"
+   exit 1;
+ endif
+ echo "X server $xdisp is running"
+ exit 0;
+
+plot: 
+ echo $script
+ echo "doplots; exit 0" | dvo -D CAMERA $camera --norc $script
+ exit 0;
+
+
+done:
+ if ("$2" == "") then
+  echo "USAGE: el_plots done (date)"
+  exit 2;
+ endif 
+
+ cp $datdir/plot/seeing_night.png /apps/www/www.cfht.hawaii.edu/Instruments/Elixir/seeing/archive/seeing_$2.png
+ exit 0;
+
+usage:
+ echo "USAGE: el_plots init"
+ echo "USAGE: el_plots plot"
+ echo "USAGE: el_plots done (date)"
+ exit 2;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/elixir.fork
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/elixir.fork	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/elixir.fork	(revision 22322)
@@ -0,0 +1,112 @@
+#!/bin/csh -f
+
+# this script is called by the CFH12K 'go' command and is run on the
+# acquisition machine.  It is run as user cfh12k, or who ever else
+# might be running the camera.  It assumes the user does not have any
+# the elixir path (/apps/elixir/bin at CFHT) define.
+
+# all needed elixir functions should be in the path of cfh12k (and megacam)
+setenv PATH /bin:/usr/bin:/apps/elixir/bin
+umask 0
+
+# file must exist when elixir.fork is called
+if ($#argv != 1) then
+ echo "USAGE: elixir.fork (filename)"
+ echo "(filename) must be the file for MEF, the top directory for SPLIT"
+ exit;
+endif
+
+# convert filename to (dir) (base) (mode):
+set answer=`/apps/elixir/bin/cfht.names $1`
+if ($status) exit 1
+
+set list=($answer)
+set fdir=$list[1]
+set name=$list[2]
+set mode=$list[3]
+
+# set logdir for various logging
+# set logdir=`gconfig LOGDIR`
+# if ($status) then 
+#  echo "error with elixir configurations: missing LOGDIR, using /tmp" 
+# endif
+
+set logdir = "$HOME/elixir"
+if (! -e $logdir) then 
+  mkdir $logdir
+  if ($status) then 
+    echo "error with elixir configurations: missing LOGDIR, using /tmp" 
+    set logdir = "/tmp"
+  endif
+endif
+
+# get name for a specific image, get image type
+# the definition of the name is system dependent
+if ("$mode" == "on") then
+ set file=$fdir/$name.fits
+else 
+ set file=$fdir/$name/$name\00.fits
+endif
+
+# abstract the OBSTYPE keyword? 
+set type=`echo $file | fields OBSTYPE | awk '{print $2}' | tr '[:lower:]' '[:upper:]'`
+
+# name for summit processing machine
+set remote=wekiu
+
+# realtime functions to perform
+set focus=0
+set seeing=0
+set jpeg=0
+switch ($type)
+    case FOCUS:
+	set focus=1
+	breaksw;
+    case DARK:
+    case BIAS:
+    case FLAT:
+	breaksw;
+    case OBJECT:
+	set jpeg=1
+	set seeing=1
+	# send to the seeing stats
+	breaksw;
+    default:
+	breaksw;
+endsw
+
+if (-r ~/.elixir.override) then
+ echo "NOTE: using values from .elixir.override" >> $logdir/fork.log
+ source ~/.elixir.override
+endif
+
+set comp = ""
+if ($focus) set comp = "getfocus $comp"
+if ($seeing) set comp = "getseeing $comp"
+if ($jpeg) set comp = "mkjpeg $comp"
+if ("$comp" == "") set comp = "nothing" 
+
+echo "$fdir $name $mode : type $type : running $comp" >> $logdir/fork.log
+# echo "$fdir $name $mode : type $type : running $comp"
+
+if ($seeing) then 
+    # getseeing - measure seeing stats for image
+    rsh -n $remote /apps/elixir/bin/getseeing $fdir $name $mode >>& $logdir/getseeing.log &
+endif
+
+if ($focus) then 
+    # getfocus - measure focus frame
+    rsh -n $remote /apps/elixir/bin/getfocus $fdir $name $mode >>& $logdir/getfocus.log &
+endif
+
+if ($jpeg) then 
+    # mkjpeg - create binned images
+    rsh -n $remote /apps/elixir/bin/mkjpeg $fdir $name $mode >>& $logdir/mkjpeg.log &
+endif
+
+exit 0;
+
+# note on script launches:
+# script >>& logfile.log &
+# launches the script immediately, ignores the exit status,
+# saves all output into logfile.log
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/elixir.launch
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/elixir.launch	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/elixir.launch	(revision 22322)
@@ -0,0 +1,124 @@
+#!/bin/csh -f
+
+if ($#argv != 1) then
+ echo "USAGE: elixir.launch (system)"
+ exit 2
+endif
+
+set run    = `gconfig RUNID.CURRENT`;
+set camera = `gconfig CAMERA.CURRENT`;
+set logdir = `gconfig LOGDIR`;
+set datdir = `gconfig DATDIR`;
+
+if ($1 == "mkdetrend") then
+    switch ($camera)
+     case cfh12k:
+     case megacam:
+     case meganorth:
+      echo "starting mkdetrend"
+      mkdetrend auto run -run $run -camera $camera >>& $logdir/mkdetrend.log &
+      breaksw;
+
+     default:
+      breaksw;
+    endsw
+
+    exit 0;
+endif
+
+if ($1 == "imstats") then
+    switch ($camera)
+     case cfh12k:
+     case megacam:
+     case meganorth:
+      echo "starting elixir imstats"
+      elixir -restart -D mode imstats -D CAMERA $camera -D RUNID $run >>& $logdir/imstats.log &
+      breaksw;
+
+     case cfhtir:
+      echo "starting elixir irstats"
+      elixir -restart -D mode irstats -D CAMERA $camera -D RUNID $run >>& $logdir/imstats.log &
+      breaksw;
+
+     default:
+      echo "stopping elixir imstats"
+      elixir -stop -D mode imstats
+      breaksw;
+    endsw
+    exit 0;
+endif
+
+if ($1 == "imstatreg") then
+    switch ($camera)
+     case cfhtir:
+     case cfh12k:
+     case megacam:
+     case meganorth:
+      echo "checking for existing imstatreg -daemon"
+      imstatreg -D CAMERA $camera -D RUNID $run -daemon -status
+      if ($status) then
+	echo "no imstatreg -daemon, starting..."
+	imstatreg -D CAMERA $camera -D RUNID $run -daemon -v
+	if ($status) then
+	    echo "error starting imstatreg -daemon"
+	endif
+      endif
+      breaksw;
+
+     default:
+      echo "no imstatreg -daemon needed for $camera"
+      breaksw;
+    endsw
+    exit 0;
+endif
+
+if ($1 == "cadc") then
+    switch ($camera)
+     case cfh12k:
+     case megacam:
+     case meganorth:
+      set date = `date -u +%Y%m%d+%H:%M:%S`
+
+      # extract CADC table, using all undistributed, object images from the past few days
+      imsearch -D CAMERA $camera -D RUNID $run -type object -dist f -trange now -4d -cadctable $datdir/cadc/mcmd.raw.$date.fits
+      if ($status) then
+	echo "error extracting cadc table"
+	exit 1;
+      endif
+      # mark the images above distributed
+      imsearch -D CAMERA $camera -D RUNID $run -type object -dist f -trange now -4d -modify dist t
+
+      # send the file to the CADC ftp location
+      cp $datdir/cadc/mcmd.raw.$date.fits /h/ftp/pub/fits_hdrs/
+
+      # update the values to the QSO database
+      cadc2qso $datdir/cadc/mcmd.raw.$date.fits
+
+      breaksw;
+
+     default:
+      echo "no CADC table needed for $camera"
+      breaksw;
+    endsw
+    exit 0;
+endif
+
+if ($1 == "sextract") then
+    switch ($camera)
+     case cfh12k:
+     case megacam:
+     case meganorth:
+      echo "starting elixir sextract"
+      elixir -restart -D mode sextract -D CAMERA $camera -D RUNID $run >>& $logdir/sextract.log &
+      breaksw;
+
+     default:
+      echo "stopping elixir sextract"
+      elixir -stop -D mode sextract
+      breaksw;
+    endsw
+    exit 0;
+endif
+
+echo "command $1 not found"
+exit 1
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/fits2jpeg
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/fits2jpeg	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/fits2jpeg	(revision 22322)
@@ -0,0 +1,68 @@
+#!/bin/csh -f
+# convert a FITS image to jpeg, scaling based on data
+
+set sys=`uname -s` 
+switch ($sys)
+ case IRIX64:
+   setenv ARCH irix;
+   breaksw;
+ case SunOS:
+   set ver=`uname -r | awk '{print substr($1,1,1)}'`;
+   if ($ver == 5) then
+     setenv ARCH sol
+   else 
+     setenv ARCH sun4
+   endif
+   breaksw;
+ case Linux:
+   setenv ARCH linux;
+   breaksw;
+ case HP-UX:
+    setenv ARCH hp;
+    breaksw;
+ default:
+   echo "unknown architecture";
+   setenv ARCH unknown;
+   breaksw;
+endsw
+
+setenv PATH /bin:/usr/bin:/usr/bin/X11:/data/milo/eugene/elixir/ohana/bin/{$ARCH}
+
+if ($#argv != 2) then
+ echo "USAGE: fits2jpeg (fits) (jpeg)"
+ exit;
+endif
+
+set temp=`mktemp /tmp/fitsjpg.XXXXXX`
+rm -f $temp
+
+xset q >& /dev/null
+if ($status) then
+ echo "can't access X server"
+ exit 1
+endif
+
+cat > $temp << END
+ \$KII = kii -nomap -private
+ rd a $1
+ keyword a NAXIS1 nx
+ keyword a NAXIS2 ny
+ rotate a flipy
+ stats a - - - -
+ tv a {\$MEDIAN-0.5*\$SIGMA} {2.5*\$SIGMA} 
+ resize \$nx \$ny
+ center {0.5*\$nx} {0.5*\$ny} 
+ jpeg -name $2
+ exit 0
+END
+
+(mana --only $temp) >& /dev/null
+if ($status) then
+ echo "problem running mana
+ rm -f $temp
+ exit 1
+endif
+
+rm -f $temp
+exit 0
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/flatten.subaru
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/flatten.subaru	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/flatten.subaru	(revision 22322)
@@ -0,0 +1,43 @@
+#!/bin/csh -f
+
+# this script performs the detrending operation using mana
+# for image arithmetic.  detrending images are stored in the
+# detrend database.  flats are defined so that the science image
+# is divided by the flatfield image, and the normalization is such 
+# that the entire mosaic is normalized by this process
+
+if ($#argv < 2) then
+ echo "USAGE: flatten (in) (out)"
+ exit 2
+endif
+
+set temp=`mktemp /tmp/flatten.XXXXXX`
+
+# create mana script
+echo "fixing the astrometry keywords"
+
+echo "macro go"                    > $temp
+echo "rd a $1"                    >> $temp
+echo "keyword a PC001001 -wf 1.0" >> $temp
+echo "keyword a PC001002 -wf 0.0" >> $temp
+echo "keyword a PC002001 -wf 0.0" >> $temp
+echo "keyword a PC002002 -wf 1.0" >> $temp
+echo "wd a $2"                    >> $temp
+echo "exit 0"                     >> $temp
+echo "end"                        >> $temp
+
+echo "go"                         >> $temp
+echo "exit 1"                     >> $temp
+
+mana --only $temp
+set mana=$status
+rm -f $temp
+
+if ($mana) then
+ echo "ERROR: flatten failed"
+else
+ echo "SUCCESS: flatten succeeded"
+endif
+
+exit $mana
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/getfocus
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/getfocus	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/getfocus	(revision 22322)
@@ -0,0 +1,195 @@
+#!/bin/csh -f
+# load the file, determine bias and sky, then extract a subraster
+# write out the subraster 
+
+setenv PATH /bin:/usr/bin:/usr/local/bin:/usr/bin/X11:/apps/elixir/bin
+
+set tmpout=""
+set args=""
+while ("$1" != "") 
+ switch ($1)
+  case -output: 
+   shift
+   set tmpout=$1
+   breaksw;
+  case -*: 
+   goto usage
+   breaksw;
+  default:
+   set args=($args $1);
+   breaksw;
+ endsw
+ shift
+end
+
+if ($#args != 3) goto usage
+
+# kill off other processes with this name, and their children
+set procname=getfocus
+set list=`/bin/ps -eo pid,user,comm | awk -v n=$procname -v m=$$ '($3==n)&&($1!=m){print $1}'`
+foreach pid ($list)
+ echo "kill $pid"
+ set children=`/bin/ps -eo pid,ppid | awk -v p=$pid '($2==p){print $1}'`
+ kill $pid
+ foreach child ($children)
+   echo "kill $child"
+   kill $child
+ end
+end
+
+set fdir=$args[1]
+set name=$args[2]
+set mode=$args[3]
+
+# set script=/apps/elixir/config/mana/focus.pro
+set datdir  = `gconfig DATDIR`
+set confdir = `gconfig CONFDIR`
+set ccds    = `gconfig FOCUS-CCDS-LIST`
+set ccdn    = `gconfig FOCUS-CCDN-LIST`
+
+# sextractor needs full paths to:
+set sexconf = "$confdir/sextract/focus.sex"
+set sexpars = "$confdir/sextract/focus.param"
+set sexfilt = "$confdir/sextract/default.conv"
+set sexnnw  = "$confdir/sextract/default.nnw"
+
+set script  = "$confdir/mana/focus.pro"
+set output1 = "$datdir/plots/focus.unit.gif"
+set output2 = "$datdir/plots/$name.focus.gif"
+if ("$tmpout" != "") then 
+ set output1=$tmpout.ppm
+ set output2=$tmpout.gif
+endif
+
+# test named Xserver, or start vnc on specified machine
+set xhost = `gconfig XHOST.SUMMIT`
+set xdisp = `gconfig XDISP.SUMMIT`
+xdpyinfo -display $xdisp >& /dev/null
+if ($status) then
+  # X server is not running. start it now
+  echo "X server is not running. start it now"
+  exit 1;
+endif
+setenv DISPLAY $xdisp
+
+set temp=`mktemp /tmp/focus.XXXXXX`
+rm -f $temp
+
+if ("$mode" == "on") goto mef
+if ("$mode" == "off") goto split
+goto usage
+
+### SPLIT mode
+split:
+
+# make sure all four files exist
+foreach ccd ($ccdn)
+ block $fdir/$name/$name$ccd.fits -t 60 || goto failure;
+end
+
+# run sextractor on each chip
+foreach ccd ($ccdn)
+ sex $fdir/$name/$name$ccd.fits \
+    -c $sexconf \
+    -PARAMETERS_NAME $sexpars \
+    -FILTER_NAME     $sexfilt \
+    -STARNNW_NAME    $sexnnw \
+    -CATALOG_NAME    $temp.$ccd; \
+    mv $temp.$ccd $temp.sdat.$ccd &
+end
+
+# wait for results, extract stellar objects
+foreach ccd ($ccdn)
+ block $temp.sdat.$ccd -t 60 || goto failure;
+ awk '($6 > -14) && ($6 < -8) && ($4== 0){print $0}' $temp.sdat.$ccd | sort -k 6n | head -500 | sort -k 2n > $temp.$ccd.sdat
+ rm -f $temp.sdat.$ccd
+end
+# result i in $temp.$ccd.sdat ($ccd from $ccdn)
+
+# create mana script & run it
+rm -f $temp.pro
+set ccd = $ccdn[1]
+echo "input $script" >> $temp.pro
+echo "plotfocus $temp $temp.ppm $name $fdir/$name/$name$ccd.fits" >> $temp.pro
+mana --norc --only $temp.pro
+if ($status) goto failure
+
+# save resulting image for posterity
+ppmtogif < $temp.ppm > $output1
+cp $output1 $output2
+
+# cleanup temp files
+rm -f $temp $temp.sdat $temp.pro $temp.ppm
+foreach ccd ($ccdn)
+ rm -f $temp.$ccd.sdat $temp.$ccd.fits
+end
+exit 0;
+
+### MEF mode
+mef:
+ 
+# wait for file to appear
+block $fdir/$name.fits -t 60 || goto failure;
+
+# extract section of each image to $temp.$ccd.fits ($ccd in $ccdn)
+rm -f $temp.pro
+set i = 1
+while ($i <= $#ccds)
+ echo $i $ccds
+ set ccdnum = $ccdn[$i]
+ set ccdnam = $ccds[$i]
+ echo "input $script" >> $temp.pro
+ echo "split $fdir/$name.fits $ccdnam $temp.$ccdnum.fits" >> $temp.pro
+ @ i++
+end
+mana --norc --only $temp.pro
+
+# run sextractor on each chip
+foreach ccd ($ccdn)
+ sex $temp.$ccd.fits \
+    -c $sexconf \
+    -PARAMETERS_NAME $sexpars \
+    -FILTER_NAME     $sexfilt \
+    -STARNNW_NAME    $sexnnw \
+    -CATALOG_NAME    $temp.$ccd; \
+    mv $temp.$ccd $temp.sdat.$ccd &
+end
+
+# wait for results, extract stellar objects
+foreach ccd ($ccdn)
+ block $temp.sdat.$ccd -t 60 || goto failure;
+ awk '($6 > -14) && ($6 < -8) && ($4 == 0){print $0}' $temp.sdat.$ccd | sort -k 6n | head -500 | sort -k 2n > $temp.$ccd.sdat
+ rm -f $temp.sdat.$ccd
+end
+
+# create mana script & run it
+rm -f $temp.pro
+set ccd = $ccdn[1]
+echo "input $script" >> $temp.pro
+echo "plotfocus $temp $temp.ppm $name $temp.$ccd.fits" >> $temp.pro
+mana --norc --only $temp.pro
+if ($status) goto failure
+
+# save resulting image for posterity
+ppmtogif < $temp.ppm > $output1
+cp $output1 $output2
+
+# cleanup temp files
+rm -f $temp $temp.sdat $temp.pro $temp.ppm
+foreach ccd ($ccdn)
+ rm -f $temp.$ccd.sdat $temp.$ccd.fits
+end
+exit 0;
+
+failure: 
+ echo "****** error with focus ******"
+ rm -f $temp $temp.sdat $temp.pro
+ foreach ccd ($ccdn)
+  rm -f $temp.$ccd.sdat $temp.$ccd.fits
+ end
+ exit 1;
+
+usage:
+ echo "USAGE: getfocus (fdir) (file) (mode)"
+ echo "(mode) may be 'on' for mef, 'off' for split"
+ exit 2;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/getfocus.org
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/getfocus.org	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/getfocus.org	(revision 22322)
@@ -0,0 +1,192 @@
+#!/bin/csh -f
+# load the file, determine bias and sky, then extract a subraster
+# write out the subraster 
+
+setenv PATH /bin:/usr/bin:/usr/local/bin:/usr/bin/X11:/apps/elixir/bin
+
+set tmpout=""
+set args=""
+while ("$1" != "") 
+ switch ($1)
+  case -output: 
+   shift
+   set tmpout=$1
+   breaksw;
+  case -*: 
+   goto usage
+   breaksw;
+  default:
+   set args=($args $1);
+   breaksw;
+ endsw
+ shift
+end
+
+if ($#args != 3) goto usage
+
+# kill off other processes with this name, and their children
+set procname=getfocus
+set list=`/bin/ps -eo pid,user,comm | awk -v n=$procname -v m=$$ '($3==n)&&($1!=m){print $1}'`
+foreach pid ($list)
+ echo "kill $pid"
+ set children=`/bin/ps -eo pid,ppid | awk -v p=$pid '($2==p){print $1}'`
+ kill $pid
+ foreach child ($children)
+   echo "kill $child"
+   kill $child
+ end
+end
+
+set fdir=$args[1]
+set name=$args[2]
+set mode=$args[3]
+
+# set script=/apps/elixir/config/mana/focus.pro
+set datdir  = `gconfig DATDIR`
+set confdir = `gconfig CONFDIR`
+set ccds    = `gconfig FOCUS-CCDS-LIST`
+set ccdn    = `gconfig FOCUS-CCDN-LIST`
+
+# sextractor needs full paths to:
+set sexconf = "$confdir/sextract/focus.sex"
+set sexpars = "$confdir/sextract/focus.param"
+set sexfilt = "$confdir/sextract/default.conv"
+set sexnnw  = "$confdir/sextract/default.nnw"
+
+set script  = "$confdir/mana/focus.pro"
+set output1 = "$datdir/plots/focus.unit.gif"
+set output2 = "$datdir/plots/$name.focus.gif"
+if ("$tmpout" != "") then 
+ set output1=$tmpout.ppm
+ set output2=$tmpout.gif
+endif
+
+# test named Xserver, or start vnc on specified machine
+set xhost = `gconfig XHOST.SUMMIT`
+set xdisp = `gconfig XDISP.SUMMIT`
+xdpyinfo -display $xdisp >& /dev/null
+if ($status) then
+  # X server is not running. start it now
+  echo "X server is not running. start it now"
+  exit 1;
+endif
+setenv DISPLAY $xdisp
+
+set temp=`mktemp /tmp/focus.XXXXXX`
+rm -f $temp
+
+if ("$mode" == "on") goto mef
+if ("$mode" == "off") goto split
+goto usage
+
+### SPLIT mode
+split:
+
+# make sure all four files exist
+foreach ccd ($ccdn)
+ block $fdir/$name/$name$ccd.fits -t 60 || goto failure;
+end
+
+# run sextractor on each chip
+foreach ccd ($ccdn)
+ sex $fdir/$name/$name$ccd.fits \
+    -c $sexconf \
+    -PARAMETERS_NAME $sexpars \
+    -FILTER_NAME     $sexfilt \
+    -STARNNW_NAME    $sexnnw \
+    -CATALOG_NAME    $temp.$ccd; \
+    mv $temp.$ccd $temp.sdat.$ccd &
+end
+
+# wait for results, extract stellar objects
+foreach ccd ($ccdn)
+ block $temp.sdat.$ccd -t 60 || goto failure;
+ awk '($6 > -14) && ($6 < -8){print $0}' $temp.sdat.$ccd | sort -k 6n | head -300 | sort -k 2n > $temp.$ccd.sdat
+ rm -f $temp.sdat.$ccd
+end
+# result i in $temp.$ccd.sdat ($ccd from $ccdn)
+
+# create mana script & run it
+rm -f $temp.pro
+set ccd = $ccdn[1]
+echo "plotfocus $temp $temp.ppm $name $fdir/$name/$name$ccd.fits" >> $temp.pro
+mana --norc --only $script $temp.pro
+if ($status) goto failure
+
+# save resulting image for posterity
+ppmtogif < $temp.ppm > $output1
+cp $output1 $output2
+
+# cleanup temp files
+rm -f $temp $temp.sdat $temp.pro $temp.ppm
+foreach ccd ($ccdn)
+ rm -f $temp.$ccd.sdat $temp.$ccd.fits
+end
+exit 0;
+
+### MEF mode
+mef:
+ 
+# wait for file to appear
+block $fdir/$name.fits -t 60 || goto failure;
+
+# extract section of each image to $temp.$ccd.fits ($ccd in $ccdn)
+rm -f $temp.pro
+set i = 1
+while ($i <= $#ccds)
+ echo $i $ccds
+ set ccdnum = $ccdn[$i]
+ set ccdnam = $ccds[$i]
+ echo "split $fdir/$name.fits $ccdnam $temp.$ccdnum.fits" >> $temp.pro
+ @ i++
+end
+mana --norc --only $script $temp.pro
+
+# run sextractor on each chip
+foreach ccd ($ccdn)
+ sex $temp.$ccd.fits \
+    -c $sexconf \
+    -PARAMETERS_NAME $sexpars \
+    -FILTER_NAME     $sexfilt \
+    -STARNNW_NAME    $sexnnw \
+    -CATALOG_NAME    $temp.$ccd; \
+    mv $temp.$ccd $temp.sdat.$ccd &
+end
+
+# wait for results, extract stellar objects
+foreach ccd ($ccdn)
+ block $temp.sdat.$ccd -t 60 || goto failure;
+ awk '($6 > -14) && ($6 < -8){print $0}' $temp.sdat.$ccd | sort -k 6n | head -300 | sort -k 2n > $temp.$ccd.sdat
+ rm -f $temp.sdat.$ccd
+end
+
+# create mana script & run it
+rm -f $temp.pro
+set ccd = $ccdn[1]
+echo "plotfocus $temp $temp.ppm $name $temp.$ccd.fits" >> $temp.pro
+mana --norc --only $script $temp.pro
+if ($status) goto failure
+
+# save resulting image for posterity
+ppmtogif < $temp.ppm > $output1
+cp $output1 $output2
+
+# cleanup temp files
+rm -f $temp $temp.sdat $temp.pro $temp.ppm
+foreach ccd ($ccdn)
+ rm -f $temp.$ccd.sdat $temp.$ccd.fits
+end
+exit 0;
+
+failure: 
+ echo "****** error with focus ******"
+ rm -f $temp $temp.sdat $temp.pro
+ foreach ccd ($ccdn)
+  rm -f $temp.$ccd.sdat $temp.$ccd.fits
+ end
+ exit 1;
+
+usage:
+ echo "USAGE: getfocus (fdir) (file) (mode)"
+ echo "(mode) may be 'on' for mef, 'off' for split"
+ exit 2;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/getseeing
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/getseeing	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/getseeing	(revision 22322)
@@ -0,0 +1,119 @@
+#!/bin/csh -f
+# measure the seeing on a subraster of a specific CCD
+# write the results to a file for the TCL display
+
+setenv PATH /bin:/usr/bin:/usr/local/bin:/usr/bin/X11:/apps/elixir/bin
+
+set screen=0
+if (($#argv == 4) && ("$4" == "-screen")) then
+ set screen=1
+ set argv=($1 $2 $3)
+endif
+
+if ($#argv != 3) then
+ echo "USAGE: getseeing (fdir) (file) (mode) [-screen]"
+ echo "(mode) may be 'on' for mef, 'off' for split"
+ exit;
+endif
+
+# kill off other processes with this name, and their children
+set procname=getseeing
+set list=`/bin/ps -eo pid,user,comm | awk -v n=$procname -v m=$$ '($3==n)&&($1!=m){print $1}'`
+foreach pid ($list)
+ echo "kill $pid"
+ set children=`/bin/ps -eo pid,ppid | awk -v p=$pid '($2==p){print $1}'`
+ kill $pid
+ foreach child ($children)
+   echo "kill $child"
+   kill $child
+ end
+end
+
+# set up references & defaults
+set fdir=$1
+set name=$2
+set mode=$3
+
+set pixscale = `gconfig ASEC_PIX`
+set ccd      = `gconfig SEEING_REF_CCD`
+set amp      = `gconfig SEEING_REF_AMP`
+set datdir   = `gconfig DATDIR`
+set confdir  = `gconfig CONFDIR`
+
+# sextractor needs full paths to:
+set sexconf = "$confdir/sextract/seeing.sex"
+set sexpars = "$confdir/sextract/seeing.param"
+set sexfilt = "$confdir/sextract/default.conv"
+set sexnnw  = "$confdir/sextract/default.nnw"
+
+set script  = "$confdir/mana/seeing.pro"
+set output  = "$datdir/plots/seeing.dat"
+
+# MEF / SPLIT mode
+if ("$mode" == "on") then
+ set mef=1
+ set file=$fdir/$name.fits
+else
+ set mef=0
+ set file=$fdir/$name/$name$ccd.fits
+endif
+
+# wait for file to exist
+block $file -t 60 || exit 1;
+
+# create mana script 
+set temp=`mktemp /tmp/seeing.XXXXXX`
+rm -f $temp $temp.fits $temp.stats $temp.pro $temp.cat
+if ($mef) then
+ set Nextend = `echo $file | fields NEXTEND | awk '{print $2}'` 
+ set Nccds   = `echo $file | fields NCCDS | awk '{print $2}'` 
+ set tccd = $ccd
+ if ($Nextend != $Nccds) set tccd = $amp
+ # use amp for un-spliced data, ccd for spliced
+ echo "input $script" > $temp.pro
+ echo "subraster.mef   $file $tccd $temp.fits $temp.stats" >> $temp.pro
+else
+ echo "input $script" > $temp.pro
+ echo "subraster.split $file $ccd $temp.fits $temp.stats" >> $temp.pro
+endif
+
+# run mana
+mana --only $temp.pro || goto failure;
+
+# run sextractor
+sex $temp.fits \
+    -c $sexconf \
+    -PARAMETERS_NAME $sexpars \
+    -FILTER_NAME     $sexfilt \
+    -STARNNW_NAME    $sexnnw \
+    -CATALOG_NAME    $temp.cat
+
+set fwhm   = `seeingstats $temp.cat | awk -v scale=$pixscale '{print $1*scale}'`
+set filter = `echo $temp.fits | fields FILTER`
+
+# send output (screen || $output & QSO)
+if ($screen) then
+ echo $name $fwhm $filter
+else
+ echo $name $fwhm $filter > $output
+ imstatqso $temp.fits $temp.stats $temp.cat
+endif 
+
+# cleanup 
+rm -f $temp $temp.fits $temp.stats $temp.pro $temp.cat
+exit 0;
+
+# failure:
+failure:
+ rm -f $temp $temp.fits $temp.stats $temp.pro $temp.cat
+ echo $name died X.XX 0.0 0.0 X > $output
+ exit 1;
+ 
+
+
+# old method of sending data to QSO db:
+#set obs=`echo $name | awk '{n=substr($1,1,6)}{print n}'`
+#/cfht/bin/update_xexpe.sh -U qso_elixir -P op1eliw --obsid $obs --iq $fwhm
+
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gettemps
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gettemps	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gettemps	(revision 22322)
@@ -0,0 +1,45 @@
+#!/bin/csh -f
+
+# check command-line arguments
+while ("$1" != "") 
+ switch ($1)
+  case -h:
+  case -help:
+  default:  
+   echo "USAGE: gettemps"
+   exit 1
+   breaksw;
+ endsw
+ shift
+end
+
+# find the appropriate script file
+set file=`gconfig TEMPERATURE_LOG`
+if ($status) then 
+ echo "configuration error: TEMPERATURE_LOG"
+ exit 1;
+endif
+
+set temp=`mktemp /tmp/teltemps.XXXXXX`
+set date=`date +%Y/%m/%d`
+cfhtlog -f $date -t "$date 23:59" -p 2,36,43,45 > $temp
+
+# mv is atomic - other processes won't miss the file
+mv -f $temp $file
+chmod 666 $file
+
+exit 0;
+
+# using dl_query gives a consistent output format regardless of 
+# whether it is recent or old DataLogger data
+
+# dl_now -p 4  | awk '(NR==2){print $0}' >  /tmp/teltemps.$$
+
+# data logger probe choices:
+#  2 surface temp, primary mirror west (silver)           deg celsius
+# 36 air temp, dome top ws side                           deg celsius
+# 43 air temp, 6' above 5th floor                         deg celsius
+# 45 air temp, 2" above 5th floor                         deg celsius
+
+# the complete list of data logger probes is found in:
+# /data/logger/www/probe.list
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gopsfphot
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gopsfphot	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gopsfphot	(revision 22322)
@@ -0,0 +1,46 @@
+#!/bin/csh -f
+# load the file, determine bias and sky, then extract a subraster
+# write out the subraster 
+
+set sexconf=/data/milo/eugene/elixir/config/sextract
+
+if ($#argv != 4) then
+ echo "USAGE: gopsfphot file.fits file.px file.st file.psf"
+ exit;
+endif
+
+set infile = $1
+set pxfile = $2
+set stfile = $3
+set psfile = $4
+
+# find the appropriate script file
+set confdir=`gconfig -q CONFDIR`
+
+# sextractor needs full paths to:
+set sexconf = "$confdir/sextract/gosexphot.sex"
+set sexpars = "$confdir/sextract/gosexphot.param"
+set sexfilt = "$confdir/sextract/default.conv"
+set sexnnw  = "$confdir/sextract/default.nnw"
+
+set psfconf = "$confdir/sextract/gopsfphot.psf"
+set psfpars = "$confdir/sextract/psf.param"
+
+set pexconf = "$confdir/sextract/gopsfphot.psfex"
+
+sex $infile \
+    -c $psfconf \
+    -PARAMETERS_NAME $psfpars \
+    -FILTER_NAME     $sexfilt \
+    -STARNNW_NAME    $sexnnw \
+    -CATALOG_NAME    $stfile
+
+psfex $stfile -c $pexconf -PSF_NAME $psfile
+
+sex $infile \
+    -c $sexconf \
+    -PARAMETERS_NAME $sexpars \
+    -FILTER_NAME     $sexfilt \
+    -STARNNW_NAME    $sexnnw \
+    -PSF_NAME        $psfile \
+    -CATALOG_NAME    $pxfile
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gosexphot
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gosexphot	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gosexphot	(revision 22322)
@@ -0,0 +1,49 @@
+#!/bin/csh -f
+# load the file, determine bias and sky, then extract a subraster
+# write out the subraster 
+
+if (($#argv != 2) && ($#argv != 3)) then
+ echo "USAGE: gosexphot file.fits file.sx [-shallow]"
+ exit;
+endif
+
+set infile = $1
+set outfile = $2
+
+# find the appropriate script file
+set confdir=`gconfig CONFDIR`
+
+set name = "gosexphot.sex"
+set param = "gosexphot.param"
+
+if ($#argv == 3) then
+    if ("$3" == "-shallow") then
+        set name  = "gosexshallow.sex"
+    endif
+    if ("$3" == "-stds") then
+        set name  = "gosexstds.sex"
+	set param = "gosexstds.param"
+    endif
+    if ("$3" == "-deep") then
+        set name  = "gosexdeep.sex"
+    endif
+endif
+
+# sextractor needs full paths to:
+set sexconf = "$confdir/sextract/$name"
+set sexpars = "$confdir/sextract/gosexphot.param"
+set sexfilt = "$confdir/sextract/default.conv"
+set sexnnw  = "$confdir/sextract/default.nnw"
+
+sex $infile \
+    -c $sexconf \
+    -PARAMETERS_NAME $sexpars \
+    -FILTER_NAME     $sexfilt \
+    -STARNNW_NAME    $sexnnw \
+    -CATALOG_NAME    $outfile
+
+if ($status) then 
+  echo "ERROR: problem with sextractor"
+else
+  echo "SUCCESS"
+endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gpc.cell.zoom
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gpc.cell.zoom	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/gpc.cell.zoom	(revision 22322)
@@ -0,0 +1,124 @@
+#!/usr/bin/env mana
+
+if (($argv:n != 1) && ($argv:n != 2)) 
+  echo "USAGE: gpc.cell.zoom (gpcdir) [output]"
+  exit 2
+end
+
+macro cell-zoom 
+  $CCDKEYWORD = EXTNAME
+  $gpcdir = $argv:0
+  $basename = `basename $gpcdir`
+
+  $output = $basename.grid.fits
+  if ($argv:n == 2)
+    $output = $argv:1
+  end
+
+  # we assume the files have names of the form gpcdir/gpcdirXY.fits
+  list name -x "ls $gpcdir/$basename??.fits"
+
+  if ($name:n == 0)
+    echo "No fits files found in $gpcdir"
+    exit 1
+  end
+
+  # judge the full mosaic size by a single sample cell
+  rd cell $name:0 -x 0
+  keyword cell NAXIS1 Nx
+  keyword cell NAXIS2 Ny
+
+  mcreate mosaic {8*$Nx} {8*$Ny}
+
+  for ix 0 8
+    for iy 0 8
+      sprintf filename "%s/%s%d%d.fits" $gpcdir $basename $ix $iy
+      file $filename found
+      if (not($found)) continue
+
+      # try to read the center cell:
+      #  3,3 if (x < 4) && (y < 4)
+      #  3,4 if (x < 4) && (y > 3)
+      #  3,4 if (x > 3) && (y < 4)
+      #  3,3 if (x > 3) && (y > 3)
+      if (($ix < 4) && ($iy < 4))
+        $xo = 3; $yo = 3
+      end
+      if (($ix < 4) && ($iy > 3))
+        $xo = 3; $yo = 4
+      end
+      if (($ix > 3) && ($iy < 4))
+        $xo = 3; $yo = 4
+      end
+      if (($ix > 3) && ($iy > 3))
+        $xo = 3; $yo = 3
+      end
+
+      # avoid the calcite cells & another poor cell
+      if (($ix == 7) && ($iy == 6))
+        $xo = 2; $yo = 2
+      end
+      if (($ix == 1) && ($iy == 7))
+        $xo = 2; $yo = 5
+      end
+      if (($ix == 4) && ($iy == 4))
+        $xo = 3; $yo = 4
+      end
+
+      break -auto off
+      sprintf cell "xy%d%d" $xo $yo
+      rd cell $filename -n $cell
+      if (not($STATUS))
+        echo "failed to find desired cell, just using first in file"
+        rd cell $filename -x 0
+      end
+      break -auto on
+
+      stats -q cell
+
+      set cell = cell - $MEDIAN
+
+      keyword cell LTM1_1 dx
+      keyword cell LTM2_2 dy
+      if ($ix < 4)
+        $dx = $dx * -1
+        $dy = $dy * -1
+      end
+
+      if (($dx == -1) && ($dy == -1)) 
+        rotate cell upsize
+      end
+      if (($dx == -1) && ($dy == +1)) 
+        rotate cell flipx
+      end
+      if (($dx == +1) && ($dy == -1)) 
+        rotate cell flipy
+      end
+
+      keyword cell NAXIS1 nx
+      keyword cell NAXIS2 ny
+      if (($nx != $Nx) || ($ny != $Ny))
+        echo "cell has different size"
+        $nx = $nx << $Nx
+        $ny = $ny << $Ny
+      end
+      extract cell mosaic 0 0 $nx $ny {$ix*$Nx} {$iy*$Ny} {8*$Nx} {8*$Ny}
+    end
+  end
+
+  # apply the header from the 00 chip:
+  sprintf filename "%s/%s11.fits" $gpcdir $basename
+  header mosaic -w $filename
+
+  keyword mosaic NAXIS  -wd 2
+  keyword mosaic NAXIS1 -wd {8*$Nx}
+  keyword mosaic NAXIS2 -wd {8*$Ny}
+
+  wd mosaic $output -bitpix -32 -bzero 0.0 -bscale 1.0
+  exit 0
+end
+
+cell-zoom 
+
+echo "error in gpc.cell.zoom"
+exit 2
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/irsex
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/irsex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/irsex	(revision 22322)
@@ -0,0 +1,55 @@
+#!/bin/csh -f
+# measure stars (fluxes, fwhm, etc) 
+
+if ($#argv != 2) then
+ echo "USAGE: irsex infile file.sdat"
+ exit;
+endif
+
+set infile = $1
+set outfile = $2
+
+if (! -e $infile) then
+ echo "ERROR: file $infile is missing"
+ exit 1
+endif
+
+# determine image type
+set imageword = `gconfig IMAGETYPE-KEYWORD`
+set type = `echo $1 | fields $imageword | awk '{print $2}'`
+
+# find the sextractor param files (can tune for cfhtir if needed)
+set confdir = `gconfig CONFDIR`
+set sexconf = "$confdir/sextract/cfhtir.sex"
+set sexpars = "$confdir/sextract/cfhtir.param"
+set sexfilt = "$confdir/sextract/default.conv"
+set sexnnw  = "$confdir/sextract/default.nnw"
+
+switch ($type)
+  case object:
+  case Object:
+  case OBJECT:
+     sex $infile \
+	-c $sexconf \
+	-PARAMETERS_NAME $sexpars \
+	-FILTER_NAME     $sexfilt \
+	-STARNNW_NAME    $sexnnw \
+	-CATALOG_NAME    $outfile
+     set stat=$status
+     breaksw;
+
+   default:
+      echo '0' > $2;
+      echo "image type $type, skipping FWHM";
+      set stat = 0
+      breaksw;
+endsw
+
+if ($stat) then
+  echo "ERROR ($stat): problem with sextractor"
+else
+  echo SUCCESS;
+endif
+
+exit $stat
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/mkjpeg
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/mkjpeg	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/mkjpeg	(revision 22322)
@@ -0,0 +1,90 @@
+#!/bin/csh -f
+# create a binned-by-10 mosaic in jpeg format
+# write the results to a file for the TCL display
+
+setenv PATH /bin:/usr/bin:/usr/bin/X11:/apps/elixir/bin:/usr/local/bin
+
+if ($#argv != 3) then
+ echo "USAGE: mkjpeg (fdir) (file) (mode)"
+ echo "(mode) may be 'on' for mef, 'off' for split"
+ exit;
+endif
+
+# this is stupid, but simple: wait a few seconds to let 'getseeing' win
+sleep 5
+
+# kill off other processes with this name, and their children
+set procname=mkjpeg
+set list=`/bin/ps -eo pid,user,comm | awk -v n=$procname -v m=$$ '($3==n)&&($1!=m){print $1}'`
+foreach pid ($list)
+ echo "kill $pid"
+ set children=`/bin/ps -eo pid,ppid | awk -v p=$pid '($2==p){print $1}'`
+ kill $pid
+ foreach child ($children)
+   echo "kill $child"
+   kill $child
+ end
+end
+
+set fdir=$1
+set name=$2
+set mode=$3
+
+# find the appropriate script file
+set confdir = `gconfig CONFDIR`
+set datdir  = `gconfig DATDIR`
+
+set confdir  = "/data/elixir2/elixir/config"
+set script   = "$confdir/mana/mkjpeg.pro"
+set output   = "$datdir/plots/current.b10.gif"
+set outzoom  = "$datdir/plots/current.zoom.gif"
+set outshort = "$datdir/plots/lastshort.zoom.gif"
+
+# test named Xserver, or start vnc on specified machine
+set xhost   = `gconfig XHOST.SUMMIT`
+set xdisp   = `gconfig XDISP.SUMMIT`
+xdpyinfo -display $xdisp >& /dev/null
+if ($status) then
+  # X server is not running. start it now
+  echo "X server is not running. start it now on $xhost"
+  exit 1;
+endif
+setenv DISPLAY $xdisp
+
+set temp=`mktemp /tmp/mkjpeg.XXXXXX`
+rm -f $temp $temp.jpg
+
+if ("$mode" == "on") then
+ set mef=1
+else 
+ set mef=0
+endif
+
+echo "input $script" > $temp
+echo "init.mosaic" >> $temp
+foreach ccd (00 01 02 03 04 05 06 07 08 09 10 11)
+ if ($mef) then
+  set file=$fdir/$name.fits
+  block $file -t 60 || exit 1;
+  echo "load.mosaic $file $ccd MEF" >> $temp
+ else
+  set file=$fdir/$name/$name$ccd.fits
+  block $file -t 60 || exit 1;
+  echo "load.mosaic $fdir/$name/$name$ccd.fits $ccd SPLIT" >> $temp
+ endif
+end
+echo "jpeg.mosaic $temp.jpg $temp.zoom.jpg" >> $temp
+
+mana --norc --only $temp
+# ppmquant 256 $temp.jpg | ppmtogif > $output
+djpeg -gif < $temp.jpg > $output
+djpeg -gif < $temp.zoom.jpg > $outzoom
+
+set short = `echo $file | fields EXPTIME | awk '{s = 0}($2 < 30){s = 1}{print s}'`
+echo "short: $short"
+if ($short) then
+ cp -f $outzoom $outshort
+endif
+
+rm -f $temp $temp.jpg $temp.zoom.jpg
+exit 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/rm.elixir
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/rm.elixir	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/rm.elixir	(revision 22322)
@@ -0,0 +1,12 @@
+#!/bin/csh -f 
+
+# USAGE: rm.elixir *
+# deletes files in arg list
+# echo "SUCCESS" at end (or ERROR if failure)
+
+while ("$1" != "") 
+ /bin/rm $1
+ shift
+end
+
+echo SUCCESS
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/scattercorrection
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/scattercorrection	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/scattercorrection	(revision 22322)
@@ -0,0 +1,53 @@
+#!/bin/csh -f
+
+# USAGE: scattercorrection (input) (output) (ccd) (xcol) (ycol) (mcol) (nhead)
+if ($#argv != 6) then
+  echo "USAGE: scattercorrection (input) (output) (ccd) (xcol) (ycol) (mcol)"
+  exit 2
+endif
+
+set INPUT  = $1
+set OUTPUT = $2
+set CCD    = `echo $3 | awk '{printf "%02d", $1}'`
+set XCOL   = $4
+set YCOL   = $5
+set MCOL   = $6
+
+rm -f $OUTPUT
+rm -f tmp.cat
+
+mana << END
+
+macro fixcat
+ data $INPUT
+ read x $XCOL y $YCOL m $MCOL
+ set x = x * (x > 5)       + 5 * (x <= 5)
+ set x = x * (x < 2050) + 2050 * (x >= 2050)
+ set y = y * (y > 20)     + 20 * (y <= 20)
+ set y = y * (y < 4096) + 4096 * (y >= 4096)
+ rd a 2002B.scatter.Z.$CCD.00.fits 
+ for i 0 x[]
+  stats -q a {x[\$i]-5} {y[\$i]-5} 11 11
+  \$dmag = -2.5*log(\$MEDIAN)
+  m[\$i] = m[\$i] - \$dmag
+  sprintf line "%7.4f" m[\$i]
+  output tmp.cat
+  echo "\$line" 
+  output stdout
+ end
+ exit 0
+end
+
+fixcat
+exit 1
+
+END
+
+if ($status) then
+ echo "problem running mana on $INPUT"
+ exit 1
+endif
+
+grep '#' $INPUT > tmp.head
+grep -v '#' $INPUT | paste - tmp.cat > tmp.body
+cat tmp.head tmp.body > $OUTPUT
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/setpath
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/setpath	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/setpath	(revision 22322)
@@ -0,0 +1,30 @@
+#!/bin/csh -f
+
+set sys=`uname -s` 
+switch ($sys)
+ case IRIX64:
+   setenv ARCH irix;
+   breaksw;
+ case SunOS:
+   set ver=`uname -r | awk '{print substr($1,1,1)}'`;
+   if ($ver == 5) then
+     setenv ARCH sol
+   else 
+     setenv ARCH sun4
+   endif
+   breaksw;
+ case Linux:
+   setenv ARCH linux;
+   breaksw;
+ case HP-UX:
+    setenv ARCH hp;
+    breaksw;
+ default:
+   echo "unknown architecture";
+   setenv ARCH unknown;
+   breaksw;
+endsw
+
+setenv PATH /bin:/usr/bin:/data/milo/eugene/elixir/ohana/bin/{$ARCH}
+
+echo $PATH
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sexstats
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sexstats	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sexstats	(revision 22322)
@@ -0,0 +1,55 @@
+#!/bin/csh -f
+# measure stars (fluxes, fwhm, etc) 
+
+if ($#argv != 2) then
+ echo "USAGE: sexstats infile file.sdat"
+ exit;
+endif
+
+set infile = $1
+set outfile = $2
+
+if (! -e $infile) then
+ echo "ERROR: file $infile is missing"
+ exit 1
+endif
+
+# determine image type
+set imageword = `gconfig IMAGETYPE-KEYWORD`
+set type = `echo $1 | fields $imageword | awk '{print $2}'`
+
+# find the sextractor param files (can tune for cfhtir if needed)
+set confdir = `gconfig CONFDIR`
+set sexconf = "$confdir/sextract/shallow.sex"
+set sexpars = "$confdir/sextract/shallow.param"
+set sexfilt = "$confdir/sextract/default.conv"
+set sexnnw  = "$confdir/sextract/default.nnw"
+
+switch ($type)
+  case object:
+  case Object:
+  case OBJECT:
+     sex $infile \
+	-c $sexconf \
+	-PARAMETERS_NAME $sexpars \
+	-FILTER_NAME     $sexfilt \
+	-STARNNW_NAME    $sexnnw \
+	-CATALOG_NAME    $outfile
+     set stat=$status
+     breaksw;
+
+   default:
+      echo '0' > $2;
+      echo "image type $type, skipping FWHM";
+      set stat = 0
+      breaksw;
+endsw
+
+if ($stat) then
+  echo "ERROR ($stat): problem with sextractor"
+else
+  echo SUCCESS;
+endif
+
+exit $stat
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_command
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_command	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_command	(revision 22322)
@@ -0,0 +1,274 @@
+#!/bin/csh -f
+
+set IOPENER   = "root@128.171.83.9"
+set template  = "/h/skyprobe/data/Refs/template.fits"
+set dummyfits = "/h/skyprobe/data/Refs/dummy.fits"
+set dlogfile  = "/h/skyprobe/log/sp_expose.log"
+set photlog   = "/h/skyprobe/log/sp_phot.log"
+set process   = 1
+
+set args=""
+while ($#argv) 
+ switch ($1)
+  case -iopener: 
+   shift
+   set IOPENER=$1
+   breaksw;
+  case -noprocess: 
+   set process = 0
+   breaksw;
+  case -h: 
+  case --help: 
+   goto usage;
+   breaksw;
+  default:
+   set args=($args $1);
+   breaksw;
+ endsw
+ shift
+end
+
+if ($args[1] == "") goto usage;
+if ($args[1] == "init")   goto init;
+if ($args[1] == "cool")   goto cool;
+if ($args[1] == "warm")   goto warm;
+if ($args[1] == "dark")   goto dark;
+if ($args[1] == "test")   goto test;
+if ($args[1] == "expose") goto expose;
+goto usage;
+
+######
+
+init:
+ if ($#args != 2) goto usage
+ if (! -d $args[2]) then
+  mkdir -p $args[2]
+ endif
+ echo "" > $dlogfile
+ echo "" > $photlog
+ exit 0;
+
+cool: 
+ if ($#args != 2) goto usage
+ ssh -n $IOPENER sp_ccdcontrol cool 1 $args[2]
+ exit 0;
+
+warm: 
+ if ($#args != 1) goto usage
+ ssh -n $IOPENER sp_ccdcontrol cool 2 0
+ exit 0;
+
+dark:
+ if ($#args != 3) goto usage
+ set exptime = $args[2]
+ set output  = $args[3]
+ ssh -n $IOPENER sp_ccdcontrol expose -etime $exptime -shuttermode 2 > $output
+ exit 0;
+
+test:
+
+ echo ""
+ echo "starting skyprobe test suite"
+ echo "this will take about 2 minutes"
+ echo ""
+
+ # is computer user & name valid?
+ set machine = `echo $IOPENER | tr '@' ' '`
+ if ($#machine != 2) then
+   echo "invalid name for iopener $IOPENER"
+   exit 1;
+ endif
+
+ # is computer alive?
+ ping -c 2 -w 4 $machine[2] >& /dev/null
+ if ($status) then    
+   echo "can't contact iopener $machine[2]"
+   exit 1;
+ endif
+ echo "iopener is OK"
+ echo ""
+
+ # cool camera & wait for 60 seconds
+ echo "cooling camera & waiting for 1 minute"
+ ssh -n $IOPENER sp_ccdcontrol cool 1 -20
+ sleep 60
+ echo ""
+
+ # take a dark frame
+ echo "taking 1 sec dark frame sample.dark.fits"
+ set exptime = 1.0
+ set output  = sample.dark.fits
+ ssh -n $IOPENER sp_ccdcontrol expose -etime $exptime -shuttermode 2 > $output
+ echo ""
+
+ # is DETTEMP in range?
+ set temps = `echo sample.dark.fits | fields DETTEM CAMTEM`
+ if ($status) then
+   echo "header information incorrect for sample.dark.fits"
+   exit 1
+ endif
+ echo "temperatures: camera: $temps[2], outside: $temps[3]"
+ set t = `echo $temps[2] | awk '($1 > 10){print 1}'`
+ if ($t) then
+  echo "WARNING: camera temperature is high!"
+ else
+  echo "camera temperature OK"
+ endif
+ echo ""
+
+ # measure dark frame stats
+ mana --norc << EOF >& /dev/null
+  macro go
+   rd a sample.dark.fits
+   stats a - - - -
+   exec echo \$MEAN \$MEDIAN \$SIGMA > sample.dark.dat
+   exit 0
+  end
+  go
+  exit 1
+EOF
+ if ($status) then 
+  echo "error measuring dark flux"
+  exit 1
+ endif
+
+ # check dark frame stats
+ set flux = `cat sample.dark.dat`
+ echo "dark flux: $flux[2]"
+ set t = `echo $flux[2] | awk '($1 > 10000){print 1}'`
+ if ($t) then
+  echo "WARNING: dark frame is bright!"
+ endif
+ echo ""
+ 
+ # take a light frame
+ echo "taking 0.1 sec light frame sample.light.fits"
+ set exptime = 0.1
+ set output  = sample.light.fits
+ ssh -n $IOPENER sp_ccdcontrol expose -etime $exptime -shuttermode 1 > $output
+ echo ""
+
+printout:
+
+ # test named Xserver, or start vnc on specified machine
+ set xhost = `gconfig XHOST.SUMMIT`
+ set xdisp = `gconfig XDISP.SUMMIT`
+ xdpyinfo -display $xdisp >& /dev/null
+ if ($status) then
+   # X server is not running. start it now
+   echo "X server is not running on $xdisp. start it now"
+   exit 1;
+ endif
+ setenv DISPLAY $xdisp
+ echo $xdisp
+
+ # delete old PS file
+ date +%Y.%m.%d | awk '{printf "TEXT 10 20 date:%s\n", $1}' > date.reg
+
+ mana --norc << EOF >& /dev/null
+  macro go
+   rd a sample.light.fits
+   stats a - - - -
+   exec echo \$MEAN \$MEDIAN \$SIGMA > sample.light.dat
+   keyword a NAXIS1 nx
+   keyword a NAXIS2 ny
+   tv a {\$MEDIAN*1.1} {-0.2*\$MEDIAN}
+   resize \$nx \$ny
+   load red date.reg
+   exec sleep 2
+   ps -name sample.light.ps
+   exit 0
+  end
+  go
+  exit 1
+EOF
+ if ($status) then 
+  echo "error measuring light flux"
+  exit 1
+ endif
+
+ # check light frame stats
+ set flux = `cat sample.light.dat`
+ echo "light flux: $flux[2]"
+ echo "approx. expected counts: "
+ echo "     100 for dark dome"
+ echo "   15000 for dome lights on"
+ echo "   30000 for dome open daytime"
+ echo ""
+
+ if (! -e sample.light.ps) then 
+   echo "sample.light.ps not created, cannot print out image"
+ else
+   lpr -Psps sample.light.ps
+   echo "image is being printed on SPS"
+ endif
+
+ rm -f date.reg
+ rm -r sample.dark.dat
+ rm -r sample.light.dat
+
+ # test tcsh & loggerh
+ # cp -f $template $dummyfits
+ # setenv FFTEMPLATE $dummyfits
+ # /cfht/bin/tcsh -B >>& $logfile
+ # /cfht/bin/loggerh -E >>& $logfile
+
+ exit 0; 
+
+expose:
+ if ($#args != 3) goto usage
+ set exptime  = $args[2]
+ set output = $args[3]
+
+ # setup logfile (default or /dev/null)
+ if ((-e $dlogfile) && (! -w $dlogfile)) then
+  set logfile = /dev/null
+ else
+  set logfile = $dlogfile
+ endif
+
+ # pre-exposure:
+ # get current RA & DEC, etc
+ # cp -f $template $dummyfits
+ # setenv FFTEMPLATE $dummyfits
+ # /cfht/bin/tcsh -B >>& $logfile
+ # at this point, we could check the TCS status and wait until dome & tel
+ # are not moving.  a future addition
+    
+ # take exposure:
+ if ($process) then
+  (ssh -n $IOPENER sp_ccdcontrol expose -etime $exptime -shuttermode 1 > $output) >>& $logfile
+ else
+   ssh -n $IOPENER sp_ccdcontrol expose -etime $exptime -shuttermode 1 > $output
+ endif
+
+ # post-exposure 
+ if ($process) then 
+  # set target for tcsh & loggerh
+  setenv FFTEMPLATE $output
+
+  # update RA & DEC in image header
+  /cfht/bin/tcsh -B >>& $logfile
+  /cfht/bin/loggerh -E >>& $logfile
+
+  # todo:
+  # read RA & DEC from $dummyfits, check for offset, set flag
+  # check exit status for functions above. if they fail, exit 1
+
+  # currently skipping the animation jpegs
+  # makejpegs $output &
+  sp_phot $output >>& $photlog & 
+ endif
+  
+ exit 0;
+
+######
+
+usage:
+  echo "USAGE: sp_command init (path)"
+  echo "USAGE: sp_command cool (temp)"
+  echo "USAGE: sp_command warm"
+  echo "USAGE: sp_command test"
+  echo "USAGE: sp_command dark (exptime) (filename)"
+  echo "USAGE: sp_command expose (exptime) (filename)"
+exit 2;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_phot
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_phot	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_phot	(revision 22322)
@@ -0,0 +1,148 @@
+#!/bin/csh -f
+
+# USAGE: sp_phot (filename.fits) 
+
+# this script has a few goals:
+# 1) make a copy of the unreduced image in RAWDIR
+# 2) run skyproberedccd, produce a reduced image
+# 3) run skyprobephot (sextractor) on image
+# 4) run imclean on result, place smp file in SMPDIR
+# 5) run gastro on smp file 
+# 6) run addstar on smp to add to CATDIR
+
+# run this process on 'druid' instead of 'ohia'?
+
+set DBMACHINE = `gconfig DBMACHINE`
+set PROCNAME  = sp_phot
+set MAXTIME   = 300
+
+set args=""
+set setdate=1
+set testrun=0
+while ($#argv)
+ switch ($1)
+  case -date: 
+   set setdate=0;
+   shift;
+   set DATE=$1;
+   breaksw;
+  default:
+   set args=($args $1);
+   breaksw;
+ endsw
+ shift
+end
+
+if ("$args[1]" == "") then
+  echo "USAGE: sp_phot (filename.fits) [-date YYYYMMDD]";
+  echo "USAGE: sp_phot (test)";
+  exit 2;
+endif
+
+if ($args[1] == "test") set testrun = 1
+
+# kill off other processes with this name, and their children
+# but only if they've run for more than $MAXTIME
+set list=`/bin/ps -eo pid,user,comm | awk -v n=$PROCNAME -v m=$$ '($3==n)&&($1!=m){print $1}'`
+foreach pid ($list)
+  set etime=`/bin/ps -eo pid,etime | awk -v n=$pid '($1==n){{m=substr($2,1,2)}{s=substr($2,4,2)}{print m*60+s}}'`
+  if ($etime < $MAXTIME) continue
+  echo "kill $pid ($etime)"
+  set children=`/bin/ps -eo pid,ppid | awk -v p=$pid '($2==p){print $1}'`
+  kill $pid
+  foreach child ($children)
+    echo "kill $child (child)"
+    kill $child
+  end
+end
+
+if ($setdate) then
+  set DATE=`date -u +%Y%m%d`
+endif
+
+set spdir = `gconfig SKYPROBE_DIR`
+if ($?) then 
+  echo "problem with config system: missing SKYPROBE_DIR"
+  exit 1;
+endif
+set RAWDIR = "$spdir/rawdir/$DATE"
+set SMPDIR = "$spdir/smpdir/$DATE"
+set TYCHO  = "$spdir/tycho" 
+
+# check that directories exist
+if (! -d $RAWDIR) then
+  mkdir -p $RAWDIR
+  if ($status) then
+    echo "can't create save directory $RAWDIR";
+    exit 1;
+  endif
+endif
+if (! -d $SMPDIR) then
+  mkdir -p $SMPDIR
+  if ($status) then
+    echo "can't create save directory $SMPDIR";
+    exit 1;
+  endif
+endif
+
+set raw=$args[1]
+if ($testrun) then
+ set raw = /h/skyprobe/data/Refs/sample.fits
+endif
+
+set root=`echo $raw | awk -F/ '{print $NF}' | sed s/.fits//`
+
+cp $raw $RAWDIR/
+if ($status) then
+  echo "warning: can't make copy of image $raw"
+endif
+
+skyproberedccd $raw $SMPDIR/$root.flt
+if ($status) goto cleanup;
+
+# skyprobephot $SMPDIR/$root.flt $SMPDIR/$root.sx
+sp_sexphot $SMPDIR/$root.flt $SMPDIR/$root.sx
+if ($status) goto cleanup;
+
+# need to have a generic method of introducing the correct photcode
+# based on both camera and filter
+imclean -sex -p SKYPROBE.V $SMPDIR/$root.flt $SMPDIR/$root.sx $SMPDIR/$root.smp 
+if ($status) goto cleanup;
+
+# use the tycho data only to do astrometry
+# rsh -n $DBMACHINE gastro -D CATDIR $TYCHO $SMPDIR/$root.smp
+echo gastro -D CATDIR $TYCHO $SMPDIR/$root.smp
+gastro -D CATDIR $TYCHO $SMPDIR/$root.smp
+# if ($status) goto cleanup;
+
+# rsh -n $DBMACHINE addstar -skyprobe -image -cal TYCHO_V TYCHO_B $SMPDIR/$root.smp 
+addstar -skyprobe -image -cal $SMPDIR/$root.smp 
+# if ($status) goto cleanup;
+
+if ($testrun) then
+ set N = `imphotsearch -name sample | wc -l`
+ imphotsearch -name sample
+ if ($status) then
+   echo "failure to find image in db"
+   exit 2;
+ endif
+ if ($N == 0) then
+   echo "failure to write image to db"
+   exit 1;
+ endif
+ echo delstar $SMPDIR/$root.smp 
+ delstar $SMPDIR/$root.smp 
+ if ($status) then
+   echo "failure to remove test image from db"
+   exit 2;
+ endif
+endif
+
+rm -f $SMPDIR/$root.flt 
+rm -f $SMPDIR/$root.sx
+exit 0;
+
+cleanup:
+rm -f $SMPDIR/$root.flt 
+rm -f $SMPDIR/$root.sx
+exit 1;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_plots
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_plots	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_plots	(revision 22322)
@@ -0,0 +1,57 @@
+#!/bin/csh -f
+
+if (($#argv != 1) && ($#argv != 2)) goto usage
+
+setenv PATH /bin:/usr/bin:/usr/bin/X11:/apps/elixir/bin:/usr/local/bin
+
+# find the appropriate script file
+set confdir=`gconfig CONFDIR`
+set datdir=`gconfig DATDIR`
+set script="$confdir/mana/skyprobe"
+set xhost=`gconfig XHOST.SUMMIT`
+set xdisp=`gconfig XDISP.SUMMIT`
+setenv DISPLAY $xdisp
+
+if ("$1" == "init")   goto init;
+if ("$1" == "plot")   goto plot;
+if ("$1" == "mkplot") goto mkplot;
+if ("$1" == "done")   goto done;
+goto usage;
+
+init:
+ # test named Xserver, or start vnc on specified machine
+ xdpyinfo -display $xdisp >& /dev/null
+ if ($status) then
+   # X server is not running. start it now
+   echo "X server is not running. start it now"
+   exit 1;
+ endif
+ echo "X server $xdisp is running"
+ exit 0;
+
+plot: 
+ echo "init; doplots; exit 0" | dvo -C skyprobe --norc $script
+ exit 0;
+
+
+mkplot: 
+ if ($#argv != 2) goto usage
+ echo "init; ntplot $2; exit 0" | status -C skyprobe --norc $script
+ exit 0;
+
+
+done:
+ if ("$2" == "") then
+  echo "USAGE: sp_plots done (date)"
+  exit 2;
+ endif 
+
+ cp $datdir/plots/skyprobe_night.png /apps/www/www.cfht.hawaii.edu/Instruments/Skyprobe/archive/mcal_$2.png
+ exit 0;
+
+usage:
+ echo "USAGE: sp_plots init"
+ echo "USAGE: sp_plots plot"
+ echo "USAGE: sp_plots mkplot (yyyy/mm/dd)"
+ echo "USAGE: sp_plots done (yyyymmdd))"
+ exit 2;
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_sexphot
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_sexphot	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/sp_sexphot	(revision 22322)
@@ -0,0 +1,32 @@
+#!/bin/csh -f
+# load the file, determine bias and sky, then extract a subraster
+# write out the subraster 
+
+if ($#argv != 2) then
+ echo "USAGE: skyprobephot file.fits file.sx"
+ exit 2;
+endif
+
+set infile = $1
+set outfile = $2
+
+# sex $1 -c $script -CATALOG_NAME $2
+# exit $status
+
+# find the appropriate script file
+set confdir=`gconfig CONFDIR`
+
+# sextractor needs full paths to:
+set sexconf = "$confdir/sextract/skyprobe.sex"
+set sexpars = "$confdir/sextract/skyprobe.param"
+set sexfilt = "$confdir/sextract/default.conv"
+set sexnnw  = "$confdir/sextract/default.nnw"
+
+sex $infile \
+    -c $sexconf \
+    -PARAMETERS_NAME $sexpars \
+    -FILTER_NAME     $sexfilt \
+    -STARNNW_NAME    $sexnnw \
+    -CATALOG_NAME    $outfile
+
+exit $status
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/vnccheck
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/vnccheck	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/vnccheck	(revision 22322)
@@ -0,0 +1,15 @@
+#!/bin/csh -f 
+
+# test if the vnc is alive.  if not, restart it.
+
+if ($#argv != 0) then
+ echo "USAGE: vnccheck" 
+ exit 2
+endif
+
+set disp = `hostname`:1
+
+xdpyinfo -display $disp >& /dev/null
+if ($status) then
+  vncstart
+endif
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/vncstart
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/vncstart	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/shell/src/vncstart	(revision 22322)
@@ -0,0 +1,12 @@
+#!/bin/csh -f
+
+setenv PATH {$PATH}:/apps/elixir/bin
+
+vncserver -kill :1
+
+# remove lock files from Xvnc 
+rm -f /tmp/.X1-lock
+rm -f /tmp/.X11-unix/X1
+
+# start Xvnc as user 'cfh12k' (so users can restart as needed)
+vncserver -depth 16 :1
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/Makefile	(revision 22322)
@@ -0,0 +1,76 @@
+default: install
+help:
+	@echo "make options: install libskycalc all programs clean dist"
+
+include ../../Makefile.System
+HOME	=	$(ROOT)/src/skycalc
+BIN	= 	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	= 	$(HOME)/doc
+INC	=	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS) -lskycalc -lohana
+
+install: $(DESTINC)/skycalc.h $(DESTLIB)/libskycalc.a
+libskycalc: $(DESTLIB)/libskycalc.a $(DESTLIB)/libskycalc.$(DLLTYPE)
+
+all: libskycalc dusktime moondata sundata
+programs: all
+
+INCS = $(DESTINC)/skycalc.h
+
+MYINCS = $(INCS) $(INC)/skycalc_internal.h
+
+OBJS = 	\
+$(SRC)/time.$(ARCH).o     \
+$(SRC)/geometry.$(ARCH).o \
+$(SRC)/astro.$(ARCH).o    \
+$(SRC)/sun.$(ARCH).o      \
+$(SRC)/moon.$(ARCH).o
+
+$(OBJS): $(MYINCS)
+
+$(LIB)/libskycalc.$(ARCH).a: $(OBJS)
+$(LIB)/libskycalc.$(ARCH).$(DLLTYPE): $(OBJS)
+
+$(DESTLIB)/libskycalc.a:  $(LIB)/libskycalc.$(ARCH).a
+$(DESTLIB)/libskycalc.$(DLLTYPE): $(LIB)/libskycalc.$(ARCH).$(DLLTYPE)
+
+dusktime : $(BIN)/dusktime.$(ARCH)
+moondata : $(BIN)/moondata.$(ARCH)
+sundata  : $(BIN)/sundata.$(ARCH)
+sunmoon  : $(BIN)/sunmoon.$(ARCH)
+
+DUSKTIME = $(SRC)/dusktime.$(ARCH).o
+MOONDATA = $(SRC)/moondata.$(ARCH).o
+SUNDATA  = $(SRC)/sundata.$(ARCH).o
+SUNMOON  = $(SRC)/sunmoon.$(ARCH).o
+
+$(DUSKTIME) : $(MYINCS)
+$(MOONDATA) : $(MYINCS)
+$(SUNDATA)  : $(MYINCS)
+$(SUNMOON)  : $(MYINCS)
+
+$(BIN)/dusktime.$(ARCH) : $(DUSKTIME)
+$(BIN)/moondata.$(ARCH) : $(MOONDATA)
+$(BIN)/sundata.$(ARCH)  : $(SUNDATA)
+$(BIN)/sunmoon.$(ARCH)  : $(SUNMOON)
+
+INSTALL = dusktime moondata sundata sunmoon
+
+# dependancy rules for binary code #########################
+$(INSTALL): % : $(BIN)/%.$(ARCH)
+
+%.clean :
+	rm -f $(BIN)/$*.$(ARCH)
+
+%.install:
+	make $(DESTBIN)/$*
+
+install:
+	for i in $(INSTALL); do make $$i.install || exit; done
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/README	(revision 22322)
@@ -0,0 +1,9 @@
+
+This directory contains the Ohana version of libskycalc, a small
+collection of astronomical almanac functions.  This library is derived
+from the skycalc progrm provided to the community by John Thorstensen
+(Dartmouth College).  See the file doc/Thorstensen.txt for his
+discussion of skycalc, including the legal release.  
+
+Skycalc, and thus this code, are released for non-commercial use only,
+and are not explicitly covered by the GPL.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/doc/Thorstensen.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/doc/Thorstensen.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/doc/Thorstensen.txt	(revision 22322)
@@ -0,0 +1,101 @@
+
+	John Thorstensen, Dartmouth College.
+
+   This program computes many quantities frequently needed by
+   the observational astronomer.  It is written as a completely
+   self-contained program in standard c, so it should be
+   very transportable; the only issue I know of that really affects
+   portability is the adequacy of the double-precision floating
+   point accuracy on the machine.  Experience shows that c compilers
+   on various systems have idiosyncracies, though, so be sure
+   to check carefully.
+
+   This is intended as an observatory utility program; I assume the
+   user is familiar with astronomical coordinates and nomenclature.
+   While the code should be very transportable, I also
+   assume it will be installed by a conscientious person who
+   will run critical tests before it is released at a new site.
+   Experience shows that some c compilers generate unforseen errors
+   when the code is ported, so the output should be checked meticulously
+   against data from other sites.
+
+   The first part (the almanac) lists the phenomena for a single night (sunset,
+   twilight, moonrise, mooset, etc.) in civil clock time.
+   The rise-set and twilight times given are good
+   to a minute or two; the moon ephemeris used for rise/set is good to
+   +- 0.3 degrees or so; it's from the Astronomical Almanac's
+   low precision formulae, (with topocentric corrections included).
+   The resulting moon rise/set times are generally good to better than
+   two minutes.  The moon coordinates for midnight and in the 'calculator
+   mode' are from a more accurate routine and are generally better than
+   1 arcmin.  The elevation of an observatory above its effective
+   horizon can be specified; if it is non-zero, rise/set times are
+   corrected approximately for depression of the horizon.
+
+   After displaying the phenomena for one night, the program goes
+   into a 'calculator mode', in which one can -
+
+	- enter RA, dec, proper motion, epoch, date, time,
+	     new site parameters, etc. ...
+
+	- compute and display circumstances of observation for the
+	   current parameters, including precessed coordinates,
+	   airmass, interference from moon or twilight, parallactic
+	   angle, etc; the program also gives calendar date in
+	   both UT and local, Julian date, and barycentric corrections.
+
+	- compute and display a table of airmasses (etc) at
+	   hourly intervals through the night.  This is very useful
+	   at the telescope.  Also, if one has a modest number of
+	   objects, it may be convenient (using system utilities)
+	   to redirect the output and print a hard copy of these
+	   tables for ready reference.
+
+	- compute and display galactic and ecliptic coordinates.
+
+	- compute and display rough (of order 0.1 degree, but often
+	  much better) positions of the major planets.
+
+	- display the almanac for the current night.
+
+    The program is self-contained.  It was developed on a VMS system,
+   but should adapt easily to any system with a c compiler.  It has
+   been ported to, and tested on, several popular workstations.
+
+	** BUT CAUTION ... **
+   Because many of the routines take a double-precision floating point
+   Julian Date as their time argument, one must be sure that the machine
+   and compiler carry sufficient mantissa to reach the desired accuracy.
+   On VAX/VMS, the time resolution is of order 0.01 second.  This has also
+   proven true on Sun and IBM workstations.
+
+LEGALITIES:
+
+   I make no guarantee as to the accuracy, reliability, or
+   appropriateness of this program, though I have found it to be
+   reasonably accurate and quite useful to the working astronomer.
+
+   The program is COPYRIGHT 1993 BY JOHN THORSTENSEN.
+   Permission is hereby granted for non-profit scientific or educational use.
+   For-profit use (e. g., by astrologers!) must be through negotiated
+   license.  The author requests that observatories and astronomy
+   departments which install this as a utility notify the author
+   by paper mail, just so I know how widely it is used.
+
+   Credits:
+    * The julian date and sidereal time routines were
+    originally coded in PL/I by  Steve Maker of Dartmouth College.
+    They were based on routines in the old American Ephemeris.
+    * The conversion from julian date to calendar date is adapted
+    from Numerical Recipes in c, by Press et al. (Cambridge University
+    Press). I highly recommend this excellent, very useful book.
+
+
+    APOLOGIES/DISCLAIMER:
+    I am aware that the code here does not always conform to
+    the best programming practices.  Not every possible error condition
+    is anticipated, and no guarantee is given that this is bug-free.
+    Nonetheless, most of this code has been shaken down at several
+    hundred sites for several years, and I have never received any
+    actual bug reports.  Many users have found this program
+    to be useful.
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/doc/skycalc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/doc/skycalc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/doc/skycalc.c	(revision 22322)
@@ -0,0 +1,6606 @@
+/* SKY CALCULATOR PROGRAM
+
+	John Thorstensen, Dartmouth College.
+
+   This program computes many quantities frequently needed by
+   the observational astronomer.  It is written as a completely
+   self-contained program in standard c, so it should be
+   very transportable; the only issue I know of that really affects
+   portability is the adequacy of the double-precision floating
+   point accuracy on the machine.  Experience shows that c compilers
+   on various systems have idiosyncracies, though, so be sure
+   to check carefully.
+
+   This is intended as an observatory utility program; I assume the
+   user is familiar with astronomical coordinates and nomenclature.
+   While the code should be very transportable, I also
+   assume it will be installed by a conscientious person who
+   will run critical tests before it is released at a new site.
+   Experience shows that some c compilers generate unforseen errors
+   when the code is ported, so the output should be checked meticulously
+   against data from other sites.
+
+   The first part (the almanac) lists the phenomena for a single night (sunset,
+   twilight, moonrise, mooset, etc.) in civil clock time.
+   The rise-set and twilight times given are good
+   to a minute or two; the moon ephemeris used for rise/set is good to
+   +- 0.3 degrees or so; it's from the Astronomical Almanac's
+   low precision formulae, (with topocentric corrections included).
+   The resulting moon rise/set times are generally good to better than
+   two minutes.  The moon coordinates for midnight and in the 'calculator
+   mode' are from a more accurate routine and are generally better than
+   1 arcmin.  The elevation of an observatory above its effective
+   horizon can be specified; if it is non-zero, rise/set times are
+   corrected approximately for depression of the horizon.
+
+   After displaying the phenomena for one night, the program goes
+   into a 'calculator mode', in which one can -
+
+	- enter RA, dec, proper motion, epoch, date, time,
+	     new site parameters, etc. ...
+
+	- compute and display circumstances of observation for the
+	   current parameters, including precessed coordinates,
+	   airmass, interference from moon or twilight, parallactic
+	   angle, etc; the program also gives calendar date in
+	   both UT and local, Julian date, and barycentric corrections.
+
+	- compute and display a table of airmasses (etc) at
+	   hourly intervals through the night.  This is very useful
+	   at the telescope.  Also, if one has a modest number of
+	   objects, it may be convenient (using system utilities)
+	   to redirect the output and print a hard copy of these
+	   tables for ready reference.
+
+	- compute and display galactic and ecliptic coordinates.
+
+	- compute and display rough (of order 0.1 degree, but often
+	  much better) positions of the major planets.
+
+	- display the almanac for the current night.
+
+    The program is self-contained.  It was developed on a VMS system,
+   but should adapt easily to any system with a c compiler.  It has
+   been ported to, and tested on, several popular workstations.
+
+	** BUT CAUTION ... **
+   Because many of the routines take a double-precision floating point
+   Julian Date as their time argument, one must be sure that the machine
+   and compiler carry sufficient mantissa to reach the desired accuracy.
+   On VAX/VMS, the time resolution is of order 0.01 second.  This has also
+   proven true on Sun and IBM workstations.
+
+LEGALITIES:
+
+   I make no guarantee as to the accuracy, reliability, or
+   appropriateness of this program, though I have found it to be
+   reasonably accurate and quite useful to the working astronomer.
+
+   The program is COPYRIGHT 1993 BY JOHN THORSTENSEN.
+   Permission is hereby granted for non-profit scientific or educational use.
+   For-profit use (e. g., by astrologers!) must be through negotiated
+   license.  The author requests that observatories and astronomy
+   departments which install this as a utility notify the author
+   by paper mail, just so I know how widely it is used.
+
+   Credits:
+    * The julian date and sidereal time routines were
+    originally coded in PL/I by  Steve Maker of Dartmouth College.
+    They were based on routines in the old American Ephemeris.
+    * The conversion from julian date to calendar date is adapted
+    from Numerical Recipes in c, by Press et al. (Cambridge University
+    Press). I highly recommend this excellent, very useful book.
+
+
+    APOLOGIES/DISCLAIMER:
+    I am aware that the code here does not always conform to
+    the best programming practices.  Not every possible error condition
+    is anticipated, and no guarantee is given that this is bug-free.
+    Nonetheless, most of this code has been shaken down at several
+    hundred sites for several years, and I have never received any
+    actual bug reports.  Many users have found this program
+    to be useful.
+
+    CHANGES SINCE THE ORIGINAL DISTRIBUTION ....
+
+	The program as listed here is for the most part similar to that
+	posted on the IRAF bulletin board in 1990.  Some changes
+	include:
+
+	01 In the original code, many functions returned structures, which
+	   some c implementations do not like.  These have been eliminated.
+
+	02 The original main() was extremely cumbersome; much of it has
+	   been broken into smaller (but still large) functions.
+
+	03 The hourly airmass includes a column for the altitude of the
+	   sun, which is printed if it is greater than -18 degrees.
+
+	04 The planets are included (see above).  As part of this, the
+	   circumstances calculator issues a warning when one is within
+	   three degrees of a major planet.  This warning is now also
+	   included in the hourly-airmass table.
+
+	05 The changeover from standard to daylight time has been rationalized.
+	   Input times between 2 and 3 AM on the night when DST starts (which
+	   are skipped over and  hence don't exist) are now disallowed; input
+	   times between 1 and 2 AM on the night when DST ends (which are
+	   ambiguous) are interpreted as standard times.  Warnings are printed
+	   in both the almanac and calculator mode when one is near to the
+	   changeover.
+
+	06 a much more accurate moon calculation has been added; it's used
+	   when the moon's coordinates are given explicitly, but not for
+	   the rise/set times, which iterate and for which a lower precision
+	   is adequate.
+
+	07 It's possible now to set the observatory elevation; in a second
+	   revision there are now two separate elevation parameters specified.
+	   The elevation above the horizon used only in rise/set calculations
+	   and adjusts rise/set times assuming the parameter is the elevation
+	   above flat surroundings (e. g., an ocean).  The true elevation above
+	   sea level is used (together with an ellipsoidal earth figure) in
+	   determining the observatory's geocentric coordinates for use in
+	   the topocentric correction of the moon's position and in the
+	   calculation of the diurnal rotation part of the barycentric velocity
+	   correction.  These refinements are quite small.
+
+	08 The moon's altitude above the horizon is now printed in the
+	   hourly airmass calculation; in the header line, its illuminated
+	   fraction and angular separation from the object are included,
+	   as computed for local midnight.
+
+	09 The helio/barycentric corrections have been revised and improved.
+	   The same routines used for planetary positions are used to
+	   compute the offset from heliocentric to solar-system
+	   barycentric positions and velocities.  The earth's position
+	   (and the sun's position as well) have been improved somewhat
+	   as well.
+
+	10 The printed day and date are always based on the same truncation
+	   of the julian date argument, so they should now always agree
+	   arbitrarily close to midnight.
+
+	11 A new convention has been adopted by which the default is that the
+	   date specified is the evening date for the whole night.  This way,
+	   calculating an almanac for the night of July 3/4 and then specifying
+	   a time after midnight gives the circumstances for the *morning of
+	   July 4*.  Typing 'n' toggles between this interpretation and a
+	   literal interpretation of the date.
+
+	12 The planetary proximity warning is now included in the hourly airmass
+	   table.
+
+	13 A routine has been added which calculates how far the moon is from
+	   the nearest cardinal phase (to coin a phrase) and prints a
+	   description.  This information is now included in both the almanac
+	   and the calculator mode.
+
+	14 The output formats have been changed slightly; it's hoped this
+	   will enhance comprehensibility.
+
+	15 A substantial revision affecting the user interface took place
+	   in September of 1993.  A command 'a' has been added to the
+	   'calculator' menu, which simply prints the almanac (rise, set,
+	   and so on) for the current night.  I'd always found that it was
+	   easy to get disoriented using the '=' command -- too much
+	   information about the moment, not enough about the time
+	   context.  Making the almanac info *conveniently* available
+	   in the calculator mode helps your get oriented.
+
+	   When the 'a' almanac is printed, space is saved over the
+	   almanac printed on entry, because there does not need
+	   to be a banner introducing the calculator mode.  Therefore some
+	   extra information is included with the 'a' almanac; this includes
+	   the length of the night from sunset to sunrise, the number of
+	   hours the sun is below -18 degrees altitude, and the number of hours
+	   moon is down after twilight.  In addition, moonrise and moonset
+	   are printed in the order in which they occur, and the occasional
+	   non-convergence of the rise/set algorithms at high latitude are
+	   signalled more forcefully to the user.
+
+	16 I found this 'a' command to be convenient in practice, and never
+	   liked the previous structure of having to 'quit' the calculator
+	   mode to see almanac information for a different night.  D'Anne
+	   Thompson of NOAO also pointed out how hokey this was, especially the
+	   use of a negative date to exit. So, I simply removed the outer
+	   'almanac' loop and added a 'Q' to the main menu for 'quit'.  The
+	   use of upper case -- for this one command only --  should guard
+	   against accidental exit.
+
+	17 The menu has been revised to be a little more readable.
+
+	18 More error checking was added in Nov. 1993, especially for numbers.
+	   If the user gives an illegal entry (such as a number which isn't
+	   legal), the rest of the command line is thrown away (to avoid
+	   having scanf simply chomp through it) and the user is prompted
+	   as to what to do next.  This seems to have stopped all situations
+	   in which execution could run away.  Also, typing repeated carriage
+	   returns with nothing on the line -- which a frustrated novice
+	   user may do because of the lack of any prompts -- causes a
+	   little notice to be printed to draw attention to the help and menu
+	   texts.
+
+	19 I found in practice that, although the input parameters and
+	   conditions are deducible *in principle* from such things as the
+	   'a' and '=' output, it takes too much digging to find them.  So
+	   I instituted an 'l' command to 'look' at the current parameter
+	   values.  To make room for this I put the 'Cautions and legalities'
+	   into the 'w' (inner workings) help text.  This looks as though
+	   it will be be very helpful to the user.
+
+	20 The more accurate moon calculation is used for moonrise and
+	   moonset; the execution time penalty appears to be trivial.
+	   Low precision moon is still used for the summary moon information
+	   printed along with the hourly airmass table.
+
+	21 A calculation of the expected portion of the night-sky
+	   brightness due to moonlight has been added.  This is based on
+	   Krisciunas and Schaefer's analytic fits (PASP, 1991).  Obviously,
+	   it's only an estimate which will vary considerably depending on
+	   atmospheric conditions.
+
+	22 A very crude estimator of the zenith sky brightness in twilight
+	   has been added.
+
+	23 A topocentric correction has been added for the sun, in anticipation
+	   of adding eclipse prediction.
+
+	24 The code now checks for eclipses of the sun and moon, by making
+	   very direct use of the predicted positions.  If an eclipse is
+	   predicted, a notice is printed in print_circumstances; also, a
+	   disclaimer is printed for the lunar sky brightness if a lunar
+	   eclipse is predicted to be under way.
+
+	25 In the driver of the main calculator loop, a provision has been
+	   added for getting characters out of a buffer rather than reading
+	   them directly off the command line.  This allows one to type any
+	   valid command character (except Q for quit) directly after a number
+	   in an argument without generating a complaint from the program
+	   (see note 18).  This had been an annoying flaw.
+
+	26 In 1993 December/1994 January, the code was transplanted
+	   to a PC and compiled under Borland Turbo C++, with strict
+	   ANSI rules.  The code was cut into 9 parts -- 8 subroutine
+	   files, the main program, and an incude file containing
+	   global variables and function prototypes.
+
+	27 An "extra goodies" feature has been added -- at present it
+	   computes geocentric times of a repeating phenomenon as
+	   viewed from a site.  This can be used for relatively little-used
+           commands to save space on the main menu.
+
+	28 The sun and moon are now included in the "major planets"
+	   printout.  This allows one to find their celestial positions
+	   even when they are not visible from the selected site.
+
+	29 A MAJOR new feature was added in February 1994, which computes
+           the observability of an object at new and full moon over a
+           range of dates.  The galactic/ecliptic coordinate converter
+           was moved to the extra goodies menu to make room for this.
+
+	30 Inclusion of a season-long timescale means that it's not
+           always necessary to specify a date on entry to the program.
+           Accordingly, the program immediately starts up in what used
+           to be called "calculator" mode -- only the site is prompted
+           for.  It is thought that the site will be relevant to nearly
+           all users.
+
+	31 Because the user is not led by the hand as much as before, the
+           startup messages were completely revised to direct new users
+           toward a short `guided tour' designed to show the program's
+	   command structure and capabilities very quickly.  Tests on
+	   volunteers showed that users instinctively go for anything
+	   called the `menu', despite the fact that that's a slow way to
+	   learn, so all mention of the menu option is removed from the
+	   startup sequence; they'll catch on soon enough.
+
+	32 Code has been added to automatically set the time and
+           date to the present on startup.  A menu option 'T' has been
+           added to set the time and date to the present plus a settable
+           offset.  This should be very useful while observing.
+
+	33 Because Sun machines apparently do not understand ANSI-standard
+           function declarations, the code has been revised back to K&R
+           style.  It's also been put back in a monolithic block for
+           simplicity in distribution.
+
+	34 The startup has been simplified still further, in that the
+           coordinates are set automatically to the zenith on startup.
+	   An 'xZ' menu item sets to the zenith for the currently specified
+           time and date (not necessarily the real time and date.)
+
+	35 Another MAJOR new capability was added in early 1994 --
+           the ability to read in a list of objects and set the current
+	   coordinates to an object on the list.  The list can be sorted
+           in a number of ways using information about the site, date
+           and time.
+
+	35 Calculator-like commands were added to the extra goodies menu
+           to do precessions and to convert Julian dates to calendar
+           dates.  An option to set the date and time to correspond to
+           a given julian date was also added.
+
+	36 Another substantial new capability was added Aug 94 -- one can
+           toggle open a log file (always named "skyclg") and keep
+           a record of the output.  This is done simply by replacing
+           most occurrences of "printf" with "oprintf", which mimics
+           printf but writes to a log file as well if it is open.
+	   This appears to slow down execution somewhat.
+
+	37 12-degree twilight has been added to the almanac.  While the
+	   awkward "goto" statements have been retained, the statement
+           labels have been revised to make them a little clearer.
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* a couple of the system-dependent magic numbers are defined here */
+
+#define SYS_CLOCK_OK 1    /* 1 means ANSI-standard time libraries do work,
+   2 means they don't.  This is used by compiler switches in file 5 and
+   the main program.  */
+
+#define LOG_FILES_OK 1  /* 1 means that log files are enabled.
+			Any other value means they're not.  */
+
+#define MAX_OBJECTS 500
+#define MINSHORT -32767   /* min, max short integers and double precision */
+#define MAXSHORT 32767
+#define MAXDOUBLE 1.0e38
+#define MINDOUBLE -1.0e38
+#define BUFSIZE 150
+
+/* some (not all) physical, mathematical, and astronomical constants
+   used are defined here. */
+
+#define  PI                3.14159265358979
+#define  ARCSEC_IN_RADIAN  206264.8062471
+#define  DEG_IN_RADIAN     57.2957795130823
+#define  HRS_IN_RADIAN     3.819718634205
+#define  KMS_AUDAY         1731.45683633   /* km per sec in 1 AU/day */
+#define  SS_MASS           1.00134198      /* solar system mass in solar units */
+#define  J2000             2451545.        /* Julian date at standard epoch */
+#define  SEC_IN_DAY        86400.
+#define  FLATTEN           0.003352813   /* flattening of earth, 1/298.257 */
+#define  EQUAT_RAD         6378137.    /* equatorial radius of earth, meters */
+#define  ASTRO_UNIT        1.4959787066e11 /* 1 AU in meters */
+#define  RSUN              6.96000e8  /* IAU 1976 recom. solar radius, meters */
+#define  RMOON             1.738e6    /* IAU 1976 recom. lunar radius, meters */
+#define  PLANET_TOL        3.          /* flag if nearer than 3 degrees
+						to a major planet ... */
+#define  KZEN              0.172       /* zenith extinction, mag, for use
+				     in lunar sky brightness calculations. */
+#define FIRSTJD            2415387.  /* 1901 Jan 1 -- calendrical limit */
+#define LASTJD             2488070.  /* 2099 Dec 31 */
+
+/* MAGIC NUMBERS which might depend on how accurately double-
+   precision floating point is handled on your machine ... */
+
+#define  EARTH_DIFF        0.05            /* used in numerical
+   differentiation to find earth velocity -- this value gives
+   about 8 digits of numerical accuracy on the VAX, but is
+   about 3 orders of magnitude larger than the value where roundoff
+   errors become apparent. */
+
+#define  MIDN_TOL          0.00001         /* this is no longer
+   used -- it was formerly
+   how close (in days) a julian date has to be to midnight
+   before a warning flag is printed for the reader.  VAX
+   double precision renders a Julian date considerably
+   more accurately than this.  The day and date are now based
+   on the same rounding of the julian date, so they should
+   always agree. */
+
+/*  FUNCTION PROTOTYPES and type definitions ....
+    These are used in breaking the code into function libraries.
+    They work properly on a strictly ANSI compiler, so they
+    apparently comply with the ANSI standard format.  */
+
+struct coord
+   {
+     short sign;  /* carry sign explicitly since -0 not neg. */
+     double hh;
+     double mm;
+     double ss;
+   };
+
+struct date_time
+   {
+	short y;
+	short mo;
+	short d;
+	short h;
+	short mn;
+	float s;
+   };
+
+FILE *sclogfl = NULL;
+
+void oprntf(char *fmt, ...)
+
+/* This routine should look almost exactly like printf in terms of its
+   arguments (format list, then a variable number of arguments
+   to be formatted and printed).  It is designed to behave just
+   like printf (though perhaps not all format types are supported yet)
+   EXCEPT that IF the globally-defined file pointer "sclogfl" is
+   defined, IT ALSO WRITES TO THAT FILE using fprintf.  The skeleton
+   for this came from Kernighan and Ritchie, 2nd edition, page 156 --
+   their "minprintf" example.  I modified it to include the
+   entire format string (e.g., %8.2f, %7d) and to write to the
+   file as well as standard output.  */
+
+{
+	va_list ap;        /* see K&R for explanation of these macros */
+	char *p, *sval;
+	char outform[10];  /* an item's output format, e.g. %8.2f */
+	char strout[150];
+	int ival, i;
+	short shval;
+	char cval;
+	double dval;
+
+	va_start(ap,fmt);
+	for (p = fmt; *p; p++) {
+		if (*p != '%') {
+			putchar(*p);
+/* overkill to put in these preprocessor flags, perhaps. */
+#if LOG_FILES_OK == 1
+			if(sclogfl != NULL) fputc(*p,sclogfl);
+#endif
+			continue;
+		}
+		i = 0;
+		outform[i] = '%';
+		p++;
+		while(*p != 'd' && *p != 'f' && *p != 's' && *p != 'c' && 
+		    *p != 'h') {
+			outform[++i] = *p++;
+		}
+		switch (*p) {
+		case 'd':
+			ival = va_arg(ap, int);
+			outform[++i] = *p;
+			outform[++i] = '\0';
+			printf(outform, ival);
+#if LOG_FILES_OK == 1
+			if(sclogfl != NULL)
+				fprintf(sclogfl,outform,ival);
+#endif
+			break;
+		case 'h':    /* signals short argument ... */
+			shval = va_arg(ap, short);
+			outform[++i] = 'd';
+			outform[++i] = '\0';
+			++p;  /* skip the 'd' in '%hd' */
+			printf(outform, shval);
+#if LOG_FILES_OK == 1
+			if(sclogfl != NULL)
+				fprintf(sclogfl,outform,shval);
+#endif
+			break;
+		case 'c':
+			/* cval = va_arg(ap, char); */
+			cval = va_arg(ap, int);
+			outform[++i] = *p;
+			outform[++i] = '\0';
+			printf(outform, cval);
+#if LOG_FILES_OK == 1
+			if(sclogfl != NULL)
+				fprintf(sclogfl,outform,cval);
+#endif
+			break;
+		case 'f':
+			dval = va_arg(ap, double);
+			outform[++i] = *p;
+			outform[++i] = '\0';
+			printf(outform, dval);
+#if LOG_FILES_OK == 1
+			if(sclogfl != NULL)
+				fprintf(sclogfl,outform,dval);
+#endif
+			break;
+		case 's':
+			outform[++i] = *p;
+			outform[++i] = '\0';
+			i = 0;
+			for (sval = va_arg(ap, char *); *sval; sval++) {
+				strout[i++] = *sval;
+			}
+			strout[i] = '\0';
+			printf(outform,strout);
+#if LOG_FILES_OK == 1
+			if(sclogfl != NULL) fprintf(sclogfl,outform,strout);
+#endif
+			break;
+		default:
+	                ;
+		}
+	}
+	va_end(ap);
+}
+
+/* elements of K&R hp calculator, basis of commands */
+
+char buf[BUFSIZE];
+int bufp=0;
+
+
+char getch() /* get a (possibly pushed back) character */
+{
+	return((bufp > 0) ? buf[--bufp] : getchar());
+}
+
+void ungetch(c) /* push character back on input */
+	int c;
+{
+	if(bufp > BUFSIZE)
+		printf("Ungetch -- too many characters.\n");
+	else
+		buf[bufp++] = c;
+}
+
+/* some functions for getting well-tested input. */
+
+int legal_num_part(c)
+	char c;
+
+{
+	if((c != '.') && (c != '-') && (c < '0' || c > '9'))
+		return(-1);  /* not a legal number part */
+	else return(0);
+}
+
+int legal_int_part(c)
+	char c;
+
+{
+	if((c != '-') && (c < '0' || c > '9'))
+		return(-1);  /* not a legal number part */
+	else return(0);
+}
+
+int legal_command_char(c)
+	char c;
+{
+	/* Allows more sophisticated argument checking by seeing if
+	      a character appended to an argument is actually a
+	      legal commmand. */
+	switch(c) {
+	  case '?': return(1);
+		break;
+	  case 'i': return(1);
+		break;
+	  case 'f': return(1);
+		break;
+	  case 'w': return(1);
+		break;
+	  case 'r': return(1);
+		break;
+	  case 'd': return(1);
+		break;
+	  case 'y': return(1);
+		break;
+	  case 't': return(1);
+		break;
+	  case 'T': return(1);
+		break;
+	  case 'n': return(1);
+		break;
+	  case 'g': return(1);
+		break;
+	  case 'e': return(1);
+		break;
+	  case 'p': return(1);
+		break;
+	  case 's': return(1);
+		break;
+	  case 'l': return(1);
+		break;
+	  case '=': return(1);
+		break;
+	  case 'a': return(1);
+		break;
+	  case 'h': return(1);
+		break;
+	  case 'o': return(1);
+		break;
+	  case 'm': return(1);
+		break;
+	  case 'c': return(1);
+		break;
+	  case 'x': return(1);
+		break;
+	  /* let's not allow 'Q' here! */
+	  default: return(0);
+	}
+}
+
+int parsedouble(s,d)
+
+	char *s;
+	double *d;
+
+	/* return values 0 = ok, with number, 1 = found a valid command,
+	   but no number, and -1 = an error of some sort (unexpected char)*/
+
+{
+   short i=0, legal = 0;
+
+   while((*(s+i) != '\0') && (legal == 0)) {
+	if(legal_num_part(*(s+i)) == 0) i++;
+	else if(legal_command_char(*(s+i)) == 1) {
+		/* to allow command to follow argument without blanks */
+		ungetch(s[i]);
+		*(s+i) = '\0';  /* will terminate on next pass. */
+	}
+	else legal = -1;
+   }
+
+   if(legal == 0) {
+	if(i > 0) {
+		sscanf(s,"%lf",d);
+		return(0);
+	}
+	else if (i == 0) { /* ran into a command character -- no input */
+		*d = 0.;
+		return(1);  /* ok, actually */
+	}
+   }
+   else {
+	printf("%s is not a legal number!! Try again!\n",s);
+	return(-1);
+   }
+}
+
+
+int getdouble(d,least,most,errprompt)
+
+	double *d,least,most;
+	char *errprompt;
+
+
+{
+    char s[30], buf[200], c;
+    short success = -1, ndiscard = 0;
+
+    scanf("%s",s);
+    while(success < 0) {
+	success = parsedouble(s,d);
+	if((success == 0) && ((*d < least) || (*d > most))) {
+	   printf("%g is out of range; allowed %g to %g -- \n",
+			*d,least,most);
+	   success = -1;
+	}
+	if(success < 0) {
+	   /* if there's error on input, clean out the rest of the line */
+	   ndiscard = 0;
+	   while((c = getchar()) != '\n')  {
+		buf[ndiscard] = c;
+		ndiscard++;
+	   }
+	   if(ndiscard > 0) {
+		buf[ndiscard] = '\0';  /* terminate the string */
+		printf("Rest of input (%s) has been discarded.\n",buf);
+	   }
+	   printf("%s",errprompt);
+	   printf("\nTry again:");
+	   scanf("%s",s);
+	}
+    }
+    return((int) success);
+}
+
+int parseshort(s,d)
+
+	char *s;
+	short *d;
+
+{
+   short i=0, legal = 0;
+
+   while((*(s+i) != '\0') && (legal == 0)) {
+	if(legal_int_part(*(s+i)) == 0) i++;
+	else if(legal_command_char(*(s+i)) == 1) {
+		/* to allow command to follow argument without blanks */
+		ungetch(s[i]);
+		*(s+i) = '\0';  /* will terminate on next pass. */
+	}
+	else legal = -1;
+   }
+
+   if(legal == 0) {
+	if(i > 0) {
+		sscanf(s,"%hd",d);
+		return(0);
+	}
+	else if (i == 0) { /* ran into a command character -- no input */
+		*d = 0.;
+		return(1);  /* didn't get a number, but something else legal */
+	}
+   }
+   else {
+	printf("%s is not a legal integer number!! Try again!\n",s);
+	return(-1);
+   }
+}
+
+int getshort(d,least,most,errprompt)
+
+	short *d,least,most;
+	char *errprompt;
+
+{
+    char s[30];
+    short success = -1, ndiscard = 0;
+    char c, buf[200];
+
+    scanf("%s",s);
+    while(success < 0) {
+	success = parseshort(s,d);
+	if((success == 0) && ((*d < least) || (*d > most))) {
+	   printf("%d is out of range; allowed %d to %d -- try again.\n",
+			*d,least,most);
+	   success = -1;
+	}
+	if(success < 0) {
+	   /* if there's error on input, clean out the rest of the line */
+	   ndiscard = 0;
+	   while((c = getchar()) != '\n')  {
+		buf[ndiscard] = c;
+		ndiscard++;
+	   }
+	   if(ndiscard > 0) {
+		buf[ndiscard] = '\0';  /* cap the string */
+		printf("Rest of input (%s) has been discarded.\n",buf);
+	   }
+	   printf("%s",errprompt);
+	   printf("Try again:");
+	   scanf("%s",s);
+	}
+    }
+    return( (int) success);
+}
+
+
+double bab_to_dec(bab)
+
+	struct coord bab;
+
+   /* converts a "babylonian" (sexigesimal) structure into
+      double-precision floating point ("decimal") number. */
+   {
+   double x;
+   x = bab.sign * (bab.hh + bab.mm / 60. + bab.ss / 3600.);
+   return(x);
+   }
+
+void dec_to_bab (deci,bab)
+
+	double deci;
+	struct coord *bab;
+
+   /* function for converting decimal to babylonian hh mm ss.ss */
+
+{
+   int hr_int, min_int;
+
+   if (deci >= 0.) bab->sign = 1;
+   else {
+      bab->sign = -1;
+      deci = -1. * deci;
+   }
+   hr_int = deci;   /* use conversion conventions to truncate */
+   bab->hh = hr_int;
+   min_int = 60. * (deci - bab->hh);
+   bab->mm = min_int;
+   bab->ss = 3600. * (deci - bab->hh - bab->mm / 60.);
+}
+
+short get_line(s)
+
+	char *s;
+
+/* gets a line terminated by end-of-line and returns number of characters. */
+{
+	char c;
+	short i = 0;
+
+	c = getchar(); /* get the first character */
+	/* chew through until you hit non white space */
+	while((c == '\n') || (c == ' ') || (c == '\t')) c = getchar();
+
+	s[i]=c;
+	i++;
+
+	/* keep going til the next newline */
+	while((c=getchar()) != '\n') {
+		s[i]=c;
+		i++;
+	}
+	s[i]='\0';  /* terminate with null */
+	return(i);
+}
+
+double get_coord()
+
+/* Reads a string from the terminal and converts it into
+   a double-precision coordinate.  This is trickier than
+   it appeared at first, since a -00 tests as non-negative;
+   the sign has to be picked out and handled explicitly. */
+/* Prompt for input in the calling routine.*/
+{
+   short sign;
+   double hrs, mins, secs;
+   char hh_string[6];  /* string with the first coord (hh) */
+   char hh1[1];
+   char errprompt[80];
+   short i = 0;
+   int end_in = 0;
+
+   /* read and handle the hour (or degree) part with sign */
+
+   scanf("%s",hh_string);
+   hh1[0] = hh_string[i];
+
+   while(hh1[0] == ' ') {
+       /* discard leading blanks */
+       i++;
+       hh1[0] = hh_string[i];
+   }
+
+   if(hh1[0] == '-') sign = -1;
+
+     else sign = 1;
+
+   if((end_in = parsedouble(hh_string,&hrs)) < 0) {
+	printf("Didn't parse correctly -- set parameter to zero!!\n");
+	return(0.);
+   }
+
+   if(sign == -1) hrs = -1. * hrs;
+
+   /* read in the minutes and seconds normally */
+   if(end_in == 0)
+       	end_in = getdouble(&mins,0.,60.,
+	  "Give minutes again, then seconds; no further prompts.\n");
+   else return(sign * hrs);
+   if(end_in == 0) end_in = getdouble(&secs,0.,60.,
+     "Give seconds again, no further prompts.\n");
+   else if(end_in == 1) secs = 0.;
+   return(sign * (hrs + mins / 60. + secs / 3600.));
+}
+
+
+void put_coords(deci, precision)
+
+	double deci;
+	short precision;
+
+/* prints out a struct coord in a nice format; precision
+   is a code for how accurate you want it.  The options are:
+     precision = 0;   minutes rounded to the nearest minute
+     precision = 1;   minutes rounded to the nearest tenth.
+     precision = 2;   seconds rounded to the nearest second
+     precision = 3;   seconds given to the tenth
+     precision = 4;   seconds given to the hundredth
+   The program assumes that the line is ready for the coord
+   to be printed and does NOT deliver a new line at the end
+   of the output. */
+
+{
+
+   double minutes;  /* for rounding off if necess. */
+   struct coord out_coord, coords;
+   char out_string[20];  /* for checking for nasty 60's */
+
+   dec_to_bab(deci,&coords);  /* internally convert to coords*/
+
+   if(precision == 0) {   /* round to nearest minute */
+      minutes = coords.mm + coords.ss / 60.;
+	   /* check to be sure minutes aren't 60 */
+      sprintf(out_string,"%.0f %02.0f",coords.hh,minutes);
+      sscanf(out_string,"%lf %lf",&out_coord.hh,&out_coord.mm);
+      if(fabs(out_coord.mm - 60.) < 1.0e-7) {
+	 out_coord.mm = 0.;
+	 out_coord.hh = out_coord.hh + 1.;
+      }
+      if(out_coord.hh < 100.) oprntf(" ");  /* put in leading blanks explicitly
+	  for 'h' option below. */
+      if(out_coord.hh < 10.) oprntf(" ");
+      if(coords.sign == -1) oprntf("-");
+	else oprntf(" ");   /* preserves alignment */
+      oprntf("%.0f %02.0f",out_coord.hh,out_coord.mm);
+   }
+
+   else if(precision == 1) {    /* keep nearest tenth of a minute */
+      minutes = coords.mm + coords.ss / 60.;
+	   /* check to be sure minutes are not 60 */
+      sprintf(out_string,"%.0f %04.1f",coords.hh,minutes);
+      sscanf(out_string,"%lf %lf",&out_coord.hh, &out_coord.mm);
+      if(fabs(out_coord.mm - 60.) < 1.0e-7) {
+	 out_coord.mm = 0.;
+	 out_coord.hh = out_coord.hh + 1.;
+      }
+      if(out_coord.hh < 10.) oprntf(" ");
+      if(coords.sign == -1) oprntf("-");
+	else oprntf(" ");   /* preserves alignment */
+      oprntf("%.0f %04.1f", out_coord.hh, out_coord.mm);
+   }
+   else if(precision == 2) {
+	  /* check to be sure seconds are not 60 */
+      sprintf(out_string,"%.0f %02.0f %02.0f",coords.hh,coords.mm,coords.ss);
+      sscanf(out_string,"%lf %lf %lf",&out_coord.hh,&out_coord.mm,
+	   &out_coord.ss);
+      if(fabs(out_coord.ss - 60.) < 1.0e-7) {
+	  out_coord.mm = out_coord.mm + 1.;
+	  out_coord.ss = 0.;
+	  if(fabs(out_coord.mm - 60.) < 1.0e-7) {
+	      out_coord.hh = out_coord.hh + 1.;
+	      out_coord.mm = 0.;
+	  }
+      }
+      if(out_coord.hh < 10.) oprntf(" ");
+      if(coords.sign == -1) oprntf("-");
+	 else oprntf(" ");   /* preserves alignment */
+      oprntf("%.0f %02.0f %02.0f",out_coord.hh,out_coord.mm,out_coord.ss);
+   }
+   else if(precision == 3) {
+	  /* the usual shuffle to check for 60's */
+      sprintf(out_string,"%.0f %02.0f %04.1f",coords.hh, coords.mm, coords.ss);
+      sscanf(out_string,"%lf %lf %lf",&out_coord.hh,&out_coord.mm,
+	   &out_coord.ss);
+      if(fabs(out_coord.ss - 60.) < 1.0e-7) {
+	  out_coord.mm = out_coord.mm + 1.;
+	  out_coord.ss = 0.;
+	  if(fabs(out_coord.mm - 60.) < 1.0e-7) {
+	     out_coord.hh = out_coord.hh + 1.;
+	     out_coord.mm = 0.;
+	  }
+      }
+      if(out_coord.hh < 10.) oprntf(" ");
+      if(coords.sign == -1) oprntf("-");
+	 else oprntf(" ");   /* preserves alignment */
+      oprntf("%.0f %02.0f %04.1f",out_coord.hh,out_coord.mm,out_coord.ss);
+   }
+   else {
+      sprintf(out_string,"%.0f %02.0f %05.2f",coords.hh,coords.mm,coords.ss);
+      sscanf(out_string,"%lf %lf %lf",&out_coord.hh,&out_coord.mm,
+	   &out_coord.ss);
+      if(fabs(out_coord.ss - 60.) < 1.0e-6) {
+	 out_coord.mm = out_coord.mm + 1.;
+	 out_coord.ss = 0.;
+	 if(fabs(out_coord.mm - 60.) < 1.0e-6) {
+	    out_coord.hh = out_coord.hh + 1.;
+	    out_coord.mm = 0.;
+	 }
+      }
+      if(out_coord.hh < 10.) oprntf(" ");
+      if(coords.sign == -1) oprntf("-");
+	 else oprntf(" ");   /* preserves alignment */
+      oprntf("%.0f %02.0f %05.2f",out_coord.hh, out_coord.mm, out_coord.ss);
+   }
+}
+
+void load_site(longit,lat,stdz,use_dst,
+	zone_name,zabr,elevsea,elev,horiz,site_name)
+
+	double *longit,*lat;
+   	double *stdz;
+	short *use_dst;
+   	char *zone_name, *zabr;
+	double *elevsea;
+   	double *elev,*horiz;
+	char *site_name;
+
+/* sets the site-specific quantities; these are
+		longit     = W longitude in decimal hours
+		lat        = N latitude in decimal degrees
+		stdz       = standard time zone offset, hours
+		elevsea    = elevation above sea level (for absolute location)
+		elev       = observatory elevation above horizon, meters
+		horiz      = (derived) added zenith distance for rise/set due
+				to elevation
+		use_dst    = 0 don't use it
+			     1 use USA convention
+			     2 use Spanish convention
+			     < 0 Southern hemisphere (reserved, unimplimented)
+		zone_name  = name of time zone, e. g. Eastern
+		zabr       = single-character abbreviation of time zone
+		site_name  = name of site.  */
+
+{
+	short nch;
+	char obs_code[3];  /* need only one char, but why not? */
+	char errprompt[50];
+
+	printf("*SELECT SITE* - Enter single-character code:\n");
+	printf("   n .. NEW SITE, prompts for all parameters.\n");
+	printf("   x .. exit without change (current: %s)\n",site_name);
+	printf("   k .. Kitt Peak [MDM Obs.]\n");
+	printf("   s .. Shattuck Observatory, Dartmouth College, Hanover NH\n");
+	printf("   e .. European Southern Obs, La Silla\n");
+	printf("   a .. Anglo-Australian Telelescope, Siding Spring\n");
+	printf("   h .. Mt. Hopkins, AZ (MMT, FLWO)\n");
+	printf("   p .. Palomar Observatory\n");
+	printf("   t .. Cerro Tololo \n");
+	printf("   r .. Roque de los Muchachos, La Palma, Canary Is.\n");
+	printf("   b .. Black Moshannon Obs., Penn State U.\n");
+	printf("   d .. Dominion Astrophysical Obs., Victoria, BC\n");
+	printf("   o .. McDonald Observatory, Mt. Locke, Texas\n");
+	printf("   m .. Mauna Kea, Hawaii\n");
+	printf("   l .. Lick Observatory\n");
+	printf("Your answer --> ");
+	scanf("%s",obs_code);
+	if(obs_code[0] == 'x') {
+		printf("No action taken.  Current site = %s.\n",site_name);
+		return;
+	}
+	if(obs_code[0] == 'k') {
+		strcpy(site_name,"Kitt Peak [MDM Obs.]");
+		strcpy(zone_name, "Mountain");
+		*zabr = 'M';
+		*use_dst = 0;
+		*longit = 7.44111; /* decimal hours */
+		*lat = 31.9533;    /* decimal degrees */
+		*stdz = 7.;
+		*elevsea = 1925.;  /* for MDM observatory, strictly */
+		*elev = 700.;  /* approximate -- to match KPNO tables */
+	}
+	else if (obs_code[0] == 's') {
+		strcpy(site_name, "Shattuck Observatory");
+		strcpy(zone_name,"Eastern");
+		*zabr = 'E';
+		*use_dst = 1;
+		*longit = 4.81889;
+		*lat = 43.7033;
+		*stdz = 5.;
+		*elevsea = 183.;
+		*elev = 0.;  /* below surrouding horizon */
+	}
+	else if (obs_code[0] == 'e') {
+		strcpy(site_name, "ESO, Cerro La Silla");
+		strcpy(zone_name, "Chilean");
+		*zabr = 'C';
+		*use_dst = -1;
+		*longit = 4.7153;
+		*lat = -29.257;
+		*stdz = 4.;
+		*elevsea = 2347.;
+		*elev = 2347.; /* for ocean horizon, not Andes! */
+		printf("\n\n** Will use daylght time, Chilean date conventions. \n\n");
+	}
+	else if (obs_code[0] == 'p') {
+		strcpy(site_name, "Palomar Observatory");
+		strcpy(zone_name, "Pacific");
+		*zabr = 'P';
+		*use_dst = 1;
+		*longit = 7.79089;
+		*lat = 33.35667;
+		*elevsea = 1706.;
+		*elev = 1706.;  /* not clear if it's appropriate ... */
+		*stdz = 8.;
+	}
+	else if (obs_code[0] == 't') {
+		strcpy(site_name, "Cerro Tololo");
+		strcpy(zone_name, "Chilean");
+		*zabr = 'C';
+		*use_dst = -1;
+		*longit = 4.721;
+		*lat = -30.165;
+		*stdz = 4.;
+		*elevsea = 2215.;
+		*elev = 2215.; /* for ocean horizon, not Andes! */
+		printf("\n\n** Will use daylght time, Chilean date conventions. \n\n");
+	}
+	else if (obs_code[0] == 'h') {
+		strcpy(site_name, "Mount Hopkins, Arizona");
+		strcpy(zone_name, "Mountain");
+		*zabr = 'M';
+		*use_dst = 0;
+		*longit = 7.39233;
+		*lat = 31.6883;
+		*elevsea = 2608.;
+		*elev = 500.;  /* approximate elevation above horizon mtns */
+		*stdz = 7.;
+	}
+/*      else if (obs_code[0] == 'c') {
+		strcpy(site_name,"Harvard College Observatory");
+		strcpy(zone_name,"Eastern");
+		*zabr = 'E';
+		*use_dst = 1;
+		*longit = 4.742;
+		*lat = 42.38;
+		*elevsea = 0.;  /* small, anyhow *?
+		*elev = 0.;
+		*stdz = 5.;
+	}                                      --- COMMENTED OUT */
+        else if (obs_code[0] == 'o') {
+		strcpy(site_name,"McDonald Observatory");
+		strcpy(zone_name,"Central");
+		*zabr = 'C';
+		*use_dst = 1;
+		*longit = 6.93478;
+                *lat = 30.6717;
+                *elevsea = 2075;
+                *elev = 1000.;  /* who knows? */
+                *stdz = 6.;
+        }
+	else if (obs_code[0] == 'a') {
+		strcpy(site_name, "Anglo-Australian Tel., Siding Spring");
+		strcpy(zone_name, "Australian");
+		*zabr = 'A';
+		*use_dst = -2;
+		*longit = -9.937739;
+		*lat = -31.277039;
+		*elevsea = 1149.;
+		*elev = 670.;
+		*stdz = -10.;
+	}
+	else if (obs_code[0] == 'b') {
+		strcpy(site_name, "Black Moshannon Observatory");
+		strcpy(zone_name, "Eastern");
+		*zabr = 'E';
+		*use_dst = 1;
+		*longit = 5.20033;
+		*lat = 40.92167;
+		*elevsea = 738.;
+		*elev = 0.;  /* not set */
+		*stdz = 5.;
+	}
+	else if (obs_code[0] == 'd') {
+		strcpy(site_name, "DAO, Victoria, BC");
+		strcpy(zone_name, "Pacific");
+		*zabr = 'P';
+		*use_dst = 1;
+		printf("\n\nWARNING: United States conventions for DST assumed.\n\n");
+		*longit = 8.22778;
+		*lat = 48.52;
+		*elevsea = 74.;
+		*elev = 74.;  /* not that it makes much difference */
+		*stdz = 8.;
+	}
+	else if (obs_code[0] == 'm') {
+		strcpy(site_name, "Mauna Kea, Hawaii");
+		strcpy(zone_name, "Hawaiian");
+		*zabr = 'H';
+		*use_dst = 0;
+		*longit = 10.36478;
+		*lat = 19.8267;
+		*elevsea = 4215.;
+		*elev = 4215.;  /* yow! */
+		*stdz = 10.;
+	}
+	else if (obs_code[0] == 'l') {
+		strcpy(site_name, "Lick Observatory");
+		strcpy(zone_name, "Pacific");
+		*zabr = 'P';
+		*use_dst = 1;
+		*longit = 8.10911;
+		*lat = 37.3433;
+		*elevsea = 1290.;
+		*elev = 1290.; /* for those nice Pacific sunsets */
+		*stdz = 8.;
+	}
+	else if (obs_code[0] == 'r') {
+		strcpy(site_name, "Roque de los Muchachos");
+		strcpy(zone_name, "pseudo-Greenwich");
+		*zabr = 'G';
+		*use_dst = 2;
+		*longit = 1.192;
+		*lat = 28.75833;
+		*elevsea = 2326.;
+		*elev = 2326.;
+		*stdz = 0.;
+	}
+	else if (obs_code[0] == 'n') {
+		printf("Enter new site parameters; the prompts give current values.\n");
+		printf("(Even if current value is correct you must re-enter explicitly.)\n");
+		printf("WEST longitude, (HOURS min sec); current value ");
+		put_coords(*longit,3);
+		printf(": ");
+		*longit = get_coord();
+		printf("Latitude, (d m s); current value ");
+		put_coords(*lat,2);
+		printf(": ");
+		*lat = get_coord();
+		printf("Actual elevation (meters) above sea level,");
+		printf(" currently %5.0f:",*elevsea);
+		strcpy(errprompt,"Because of error,");
+		    /* situation is uncomplicated, so simple errprompt */
+		getdouble(elevsea,-1000.,100000.,errprompt);
+		printf("Effective elevation, meters (for rise/set),");
+		printf(" currently %5.0f:",*elev);
+		getdouble(elev,-1000.,20000.,errprompt); /* limits of approx. ... */
+		printf("Site name (< 30 char): ");
+		nch=get_line(site_name);
+		printf("Std time zone, hours W; currently %3.0f :",*stdz);
+		getdouble(stdz,-13.,13.,errprompt);
+		printf("Time zone name, e. g., Central: ");
+		nch = get_line(zone_name);
+		printf("Single-character zone abbreviation, currently %c : ",*zabr);
+		scanf("%c",zabr);
+		printf("Type daylight savings time option --- \n");
+		printf("   0  ... don't use it, \n");
+		printf("   1  ... use United States convention for clock change.\n");
+		printf("   2  ... use Spanish (Continental?) convention.\n");
+		printf("  -1  ... use Chilean convention.\n");
+		printf("  -2  ... use Australian convention.\n");
+		printf("(Other options would require new code). Answer: --> ");
+		getshort(use_dst,-100,100,errprompt);
+	}
+		else {
+		printf("UNKNOWN SITE '%c' -- left as %s. Note input is case-sensitive.\n",
+			      obs_code[0],site_name);
+	}
+	/* now compute derived quantity "horiz" = depression of horizon.*/
+	*horiz = sqrt(2. * *elev / 6378140.) * DEG_IN_RADIAN;
+}
+
+
+double atan_circ(x,y)
+
+	double x,y;
+
+{
+	/* returns radian angle 0 to 2pi for coords x, y --
+	   get that quadrant right !! */
+
+	double theta;
+
+	if(x == 0.) {
+		if(y > 0.) theta = PI / 2.;
+		else if(y < 0.) theta = 3.* PI / 2.;
+		else theta = 0.;   /* x and y zero */
+	}
+	else theta = atan(y/x);
+	if(x < 0.) theta = theta + PI;
+	if(theta < 0.) theta = theta + 2.* PI;
+	return(theta);
+}
+
+void min_max_alt(lat,dec,min,max)
+
+	double lat,dec,*min,*max;
+
+{
+	/* computes minimum and maximum altitude for a given dec and
+	    latitude. */
+
+	double x;
+	lat = lat / DEG_IN_RADIAN; /* pass by value! */
+	dec = dec / DEG_IN_RADIAN;
+	x = cos(dec)*cos(lat) + sin(dec)*sin(lat);
+	if(fabs(x) <= 1.) {
+		*max = asin(x) * DEG_IN_RADIAN;
+	}
+	else oprntf("Error in min_max_alt -- arcsin(>1)\n");
+	x = sin(dec)*sin(lat) - cos(dec)*cos(lat);
+	if(fabs(x) <= 1.) {
+		*min = asin(x) * DEG_IN_RADIAN;
+	}
+	else oprntf("Error in min_max_alt -- arcsin(>1)\n");
+}
+
+double altit(dec,ha,lat,az)
+
+	double dec,ha,lat,*az;
+
+/* returns altitude(degr) for dec, ha, lat (decimal degr, hr, degr);
+    also computes and returns azimuth through pointer argument. */
+{
+	double x,y,z;
+	dec = dec / DEG_IN_RADIAN;
+	ha = ha / HRS_IN_RADIAN;
+	lat = lat / DEG_IN_RADIAN;  /* thank heavens for pass-by-value */
+	x = DEG_IN_RADIAN * asin(cos(dec)*cos(ha)*cos(lat) + sin(dec)*sin(lat));
+	y =  sin(dec)*cos(lat) - cos(dec)*cos(ha)*sin(lat); /* due N comp. */
+	z =  -1. * cos(dec)*sin(ha); /* due east comp. */
+	*az = atan_circ(y,z) * DEG_IN_RADIAN;
+	return(x);
+}
+
+double secant_z(alt)
+	double alt;
+{
+	/* Computes the secant of z, assuming the object is not
+           too low to the horizon; returns 100. if the object is
+           low but above the horizon, -100. if the object is just
+           below the horizon. */
+
+	double secz;
+	if(alt != 0) secz = 1. / sin(alt / DEG_IN_RADIAN);
+	else secz = 100.;
+	if(secz > 100.) secz = 100.;
+	if(secz < -100.) secz = -100.;
+	return(secz);
+}
+
+double ha_alt(dec,lat,alt)
+
+	double dec,lat,alt;
+
+{
+	/* returns hour angle at which object at dec is at altitude alt.
+	   If object is never at this altitude, signals with special
+	   return values 1000 (always higher) and -1000 (always lower). */
+
+	double x,coalt,min,max;
+
+	min_max_alt(lat,dec,&min,&max);
+	if(alt < min)
+		return(1000.);  /* flag value - always higher than asked */
+	if(alt > max)
+		return(-1000.); /* flag for object always lower than asked */
+	dec = (0.5*PI) - dec / DEG_IN_RADIAN;
+	lat = (0.5*PI) - lat / DEG_IN_RADIAN;
+	coalt = (0.5*PI) - alt / DEG_IN_RADIAN;
+	x = (cos(coalt) - cos(dec)*cos(lat)) / (sin(dec)*sin(lat));
+	if(fabs(x) <= 1.) return(acos(x) * HRS_IN_RADIAN);
+	else {
+		oprntf("Error in ha_alt ... acos(>1).\n");
+		return(1000.);
+	}
+}
+
+double subtend(ra1,dec1,ra2,dec2)
+
+	double ra1,dec1,ra2,dec2;
+
+  /*args in dec hrs and dec degrees */
+
+{
+	/* angle subtended by two positions in the sky --
+	   return value is in radians.  Hybrid algorithm works down
+	   to zero separation except very near the poles. */
+
+	double x1, y1, z1, x2, y2, z2;
+	double theta;
+
+	ra1 = ra1 / HRS_IN_RADIAN;
+	dec1 = dec1 / DEG_IN_RADIAN;
+	ra2 = ra2 / HRS_IN_RADIAN;
+	dec2 = dec2 / DEG_IN_RADIAN;
+	x1 = cos(ra1)*cos(dec1);
+	y1 = sin(ra1)*cos(dec1);
+	z1 = sin(dec1);
+	x2 = cos(ra2)*cos(dec2);
+	y2 = sin(ra2)*cos(dec2);
+	z2 = sin(dec2);
+	theta = acos(x1*x2+y1*y2+z1*z2);
+     /* use flat Pythagorean approximation if the angle is very small
+	*and* you're not close to the pole; avoids roundoff in arccos. */
+	if(theta < 1.0e-5) {  /* seldom the case, so don't combine test */
+		if(fabs(dec1) < (PI/2. - 0.001) &&
+		    fabs(dec2) < (PI/2. - 0.001))    {
+			/* recycled variables here... */
+			x1 = (ra2 - ra1) * cos((dec1+dec2)/2.);
+			x2 = dec2 - dec1;
+			theta = sqrt(x1*x1 + x2*x2);
+		}
+	}
+	return(theta);
+}
+
+int get_pm(dec, mura, mudec)
+
+double dec, *mura, *mudec;
+
+{
+	/* This gets the proper motions.  New routine
+	(2/94) assumes that primary PM convention will
+	be rate of change of right ascension in seconds
+	of time per year.  Either one can be entered here,
+	but the value passed out is seconds of time per year at the
+        equator (i.e., rate of change of RA itself). */
+
+	char pmtype[3];
+        int status;
+
+	printf("Note -- two different conventions for RA proper motion.\n");
+	printf("Enter RA p.m., either as delta RA(sec) or arcsec per yr.:");
+	scanf("%lf",mura);
+        if(*mura != 0.) {
+	  printf("Type s if this is RA change in time sec per yr,\n");
+	  printf("or a if this is motion in arcsec per yr:");
+	  scanf("%s",pmtype);
+        }
+	else pmtype[0] = 's';  /* if pm is zero, it doesn't matter. */
+
+	if(pmtype[0] == 's') status = 0;
+	else if (pmtype[0] == 'a') {
+		*mura = *mura /(15. * cos(dec / DEG_IN_RADIAN));
+		printf("Equivalent to %8.5f sec of time per yr. at current dec.\n",
+		     *mura);
+                               printf("(Will only be correct at this dec.)\n");
+                status = 0;
+	}
+	else {
+		printf("UNRECOGNIZED PM TYPE ... defaults to sec/yr\n");
+		status = -1;
+        }
+	printf("Give declination PM in arcsec/yr:");
+	scanf("%lf",mudec);
+	printf("(Note: Proper motion correction only ");
+	printf("by mu * delta t; inaccurate near pole.)\n");
+	printf("Don't forget to reset for new object.\n");
+
+        return(status);  /* 0 = success */
+}
+
+
+int get_date(date)
+
+	struct date_time *date;
+{
+
+	int valid_date = 0;
+
+	while(valid_date == 0) {
+	 	getshort(&(date->y),-10,2100,
+           	   "Give year again, then month and day.\n");
+		if(date->y <= 0) return(-1);
+
+	/* scan for mo and day here, *then* error check. */
+		getshort(&(date->mo),1,12,
+		   "Give month again (as number 1-12), then day.\n");
+		getshort(&(date->d),0,32,"Give day again.\n");
+		/* a lot of this error checking is redundant with the
+		    checks in the new getshort routine.... */
+		if(date->y < 100)  {
+			date->y = date->y + 1900;
+			printf("(Your answer assumed to mean %d)\n",date->y);
+		}
+		else if((date->y <= 1900 ) | (date->y >= 2100)){
+			printf("Date out of range: only 1901 -> 2099 allowed.\n");
+			printf("Try it again!\n");
+		}
+		/* might be nice to allow weird input dates, but calendrical
+		   routines will not necessarily handle these right ... */
+		else if((date->d < 0) || (date->d > 32))
+			printf("day-of-month %d not allowed -- try again!\n",
+				date->d);
+		else if((date->mo < 1) || (date->mo > 12))
+			printf("month %d doesn't exist -- try again!\n",
+				date->mo);
+		else {
+			valid_date = 1;
+			return(0);  /* success */
+		}
+	}
+}
+
+int get_time(date)
+
+	struct date_time *date;
+
+{
+
+	struct coord ttime;
+
+	dec_to_bab(get_coord(),&ttime);
+	date->h = (short) ttime.hh;
+		/* awkward, because h and m of date are short. */
+	date->mn = (short) ttime.mm;
+	date->s = ttime.ss;
+	return(0);
+}
+
+double date_to_jd(date)
+
+	struct date_time date;
+
+/* Converts a date (structure) into a julian date.
+   Only good for 1900 -- 2100. */
+
+{
+	short yr1=0, mo1=1;
+	long jdzpt = 1720982, jdint, inter;
+	double jd,jdfrac;
+
+
+	if((date.y <= 1900) | (date.y >= 2100)) {
+		printf("Date out of range.  1900 - 2100 only.\n");
+		return(0.);
+	}
+
+	if(date.mo <= 2) {
+		yr1 = -1;
+		mo1 = 13;
+	}
+
+	jdint = 365.25*(date.y+yr1);  /* truncates */
+	inter = 30.6001*(date.mo+mo1);
+	jdint = jdint+inter+date.d+jdzpt;
+	jd = jdint;
+	jdfrac=date.h/24.+date.mn/1440.+date.s/SEC_IN_DAY;
+	if(jdfrac < 0.5) {
+		jdint--;
+		jdfrac=jdfrac+0.5;
+	}
+	else jdfrac=jdfrac-0.5;
+	jd=jdint+jdfrac;
+	return(jd);
+}
+
+short day_of_week(jd)
+	double jd;
+{
+	/* returns day of week for a jd, 0 = Mon, 6 = Sun. */
+
+	double x,y;
+	long i;
+	short d;
+
+	jd = jd+0.5;
+	i = jd; /* truncate */
+	x = i/7.+0.01;
+	d = 7.*(x - (long) x);   /* truncate */
+	return(d);
+}
+
+
+void caldat(jdin,date,dow)
+
+	double jdin;
+	struct date_time *date;
+	short *dow;
+
+#define IGREG 2299161
+
+{
+	/* Returns date and time for a given julian date;
+	   also returns day-of-week coded 0 (Mon) through 6 (Sun).
+	   Adapted from Press, Flannery, Teukolsky, &
+	   Vetterling, Numerical Recipes in C, (Cambridge
+	   University Press), 1st edn, p. 12. */
+
+	int mm, id, iyyy;  /* their notation */
+	long ja, jdint, jalpha, jb, jc, jd, je;
+	float jdfrac;
+	double x;
+
+	jdin = jdin + 0.5;  /* adjust for 1/2 day */
+	jdint = jdin;
+	x = jdint/7.+0.01;
+	*dow = 7.*(x - (long) x);   /* truncate for day of week */
+	jdfrac = jdin - jdint;
+	date->h = jdfrac * 24; /* truncate */
+	date->mn = (jdfrac - ((float) date->h)/24.) * 1440.;
+	date->s = (jdfrac - ((float) date->h)/24. -
+			((float) date->mn)/1440.) * SEC_IN_DAY;
+
+	if(jdint > IGREG) {
+		jalpha=((float) (jdint-1867216)-0.25)/36524.25;
+		ja=jdint+1+jalpha-(long)(0.25*jalpha);
+	}
+	else
+		ja=jdint;
+	jb=ja+1524;
+	jc=6680.0+((float) (jb-2439870)-122.1)/365.25;
+	jd=365*jc+(0.25*jc);
+	je=(jb-jd)/30.6001;
+	id=jb-jd-(int) (30.6001*je);
+	mm=je-1;
+	if(mm > 12) mm -= 12;
+	iyyy=jc-4715;
+	if(mm > 2) --iyyy;
+	if (iyyy <= 0) --iyyy;
+	date->y = iyyy;
+	date->mo = mm;
+	date->d = id;
+}
+
+
+void print_day(d)
+	short d;
+
+{
+	/* prints day of week given number 0=Mon,6=Sun */
+	char *days = "MonTueWedThuFriSatSun";
+	char day_out[4];
+
+	day_out[0] = *(days+3*d);
+	day_out[1] = *(days+3*d+1);
+	day_out[2] = *(days+3*d+2);
+	day_out[3] = '\0';  /* terminate with null char */
+
+	oprntf("%s",day_out);
+}
+
+
+void print_all(jdin)
+
+	double jdin;
+{
+	/* given a julian date,
+	prints a year, month, day, hour, minute, second */
+
+	struct date_time date;
+        int ytemp, dtemp; /* compiler bug workaround ... SUN
+         and silicon graphics */
+	char *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
+	char mo_out[4];
+	double out_time;
+	short dow;
+
+	caldat(jdin,&date,&dow);
+
+	print_day(dow);
+	oprntf(", ");
+
+	mo_out[0] = *(months + 3*(date.mo - 1));
+	mo_out[1] = *(months + 3*(date.mo - 1) + 1);
+	mo_out[2] = *(months + 3*(date.mo - 1) + 2);
+	mo_out[3] = '\0';
+
+	/* going through the rigamarole to avoid 60's */
+
+	out_time = date.h + date.mn / 60. + date.s / 3600.;
+
+	ytemp = (int) date.y;
+	dtemp = (int) date.d;
+	oprntf("%d %s %2d, time ",
+		ytemp,mo_out,dtemp);
+	put_coords(out_time,3);
+}
+
+void print_current(date,night_date,enter_ut)
+        struct date_time date;
+	short night_date, enter_ut;
+{
+	/* just prints out the date & time and a little statement
+           of whether time is "local" or "ut".  Functionalized to
+           compactify some later code. */
+
+	double jd;
+
+	jd = date_to_jd(date);
+        if((night_date == 1) && (date.h < 12)) jd = jd + 1.0;
+	print_all(jd);
+	if(enter_ut == 0) oprntf(" local time.");
+	else oprntf(" Universal time.");
+}
+
+void print_calendar(jdin,dow)
+
+	double jdin;
+	short *dow;
+
+{
+	/* given a julian date prints a year, month, day.
+	   Returns day of week (0 - 6) for optional printing ---
+	   idea is to do one roundoff only to eliminate possibility
+	   of day/date disagreement. */
+
+	struct date_time date;
+	char *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
+	char mo_out[4];
+	int ytemp, dtemp;  /* compiler bug workaround -- SUN and
+           Silicon Graphics machines */
+
+	caldat(jdin,&date,dow);
+	mo_out[0] = *(months + 3*(date.mo - 1));
+	mo_out[1] = *(months + 3*(date.mo - 1) + 1);
+	mo_out[2] = *(months + 3*(date.mo - 1) + 2);
+	mo_out[3] = '\0';
+        ytemp = (int) date.y;
+        dtemp = (int) date.d;
+	oprntf("%d %s %d",ytemp,mo_out,dtemp);
+}
+
+void print_time(jdin,prec)
+
+	double jdin;
+	short prec;
+
+{
+	/* given a julian day, prints time only;
+	   special precision of "-1" prints only hours!  */
+	struct date_time date;
+	double temptime;
+	short dow;
+
+	caldat(jdin,&date,&dow);
+
+	temptime = date.h + date.mn/60. + date.s/3600.;
+
+	if(prec >= 0) put_coords(temptime,prec);
+	else if(date.mn < 30) oprntf("%2.0hd hr",date.h);
+	else oprntf("%2.0hd hr",(date.h+1)); /* round it up */
+}
+
+double frac_part(x)
+
+	double x;
+{
+	long i;
+	i = x;
+	x = x - i;
+	return(x);
+}
+
+
+double lst(jd,longit)
+
+	double jd,longit;
+
+{
+	/* returns the local MEAN sidereal time (dec hrs) at julian date jd
+	   at west longitude long (decimal hours).  Follows
+	   definitions in 1992 Astronomical Almanac, pp. B7 and L2.
+	   Expression for GMST at 0h ut referenced to Aoki et al, A&A 105,
+	   p.359, 1982.  On workstations, accuracy (numerical only!)
+	   is about a millisecond in the 1990s. */
+
+	double t, ut, jdmid, jdint, jdfrac, sid_g, sid;
+	long jdin, sid_int;
+
+	jdin = jd;         /* fossil code from earlier package which
+			split jd into integer and fractional parts ... */
+	jdint = jdin;
+	jdfrac = jd - jdint;
+	if(jdfrac < 0.5) {
+		jdmid = jdint - 0.5;
+		ut = jdfrac + 0.5;
+	}
+	else {
+		jdmid = jdint + 0.5;
+		ut = jdfrac - 0.5;
+	}
+	t = (jdmid - J2000)/36525;
+	sid_g = (24110.54841+8640184.812866*t+0.093104*t*t-6.2e-6*t*t*t)/SEC_IN_DAY;
+	sid_int = sid_g;
+	sid_g = sid_g - (double) sid_int;
+	sid_g = sid_g + 1.0027379093 * ut - longit/24.;
+	sid_int = sid_g;
+	sid_g = (sid_g - (double) sid_int) * 24.;
+	if(sid_g < 0.) sid_g = sid_g + 24.;
+	return(sid_g);
+}
+
+double adj_time(x)
+	double x;
+
+{
+	/* adjusts a time (decimal hours) to be between -12 and 12,
+	   generally used for hour angles.  */
+
+	if(fabs(x) < 100000.) {  /* too inefficient for this! */
+		while(x > 12.) {
+			x = x - 24.;
+		}
+		while(x < -12.) {
+			x = x + 24.;
+		}
+	}
+	else oprntf("Out of bounds in adj_time!\n");
+	return(x);
+}
+
+void lpmoon(jd,lat,sid,ra,dec,dist)
+
+	double jd,lat,sid,*ra,*dec,*dist;
+
+/* implements "low precision" moon algorithms from
+   Astronomical Almanac (p. D46 in 1992 version).  Does
+   apply the topocentric correction.
+Units are as follows
+jd,lat, sid;   decimal hours
+*ra, *dec,   decimal hours, degrees
+	*dist;      earth radii */
+{
+
+	double T, lambda, beta, pie, l, m, n, x, y, z, alpha, delta,
+		rad_lat, rad_lst, distance, topo_dist;
+	char dummy[40];  /* to fix compiler bug on IBM system */
+
+	T = (jd - J2000) / 36525.;  /* jul cent. since J2000.0 */
+
+	lambda = 218.32 + 481267.883 * T
+	   + 6.29 * sin((134.9 + 477198.85 * T) / DEG_IN_RADIAN)
+	   - 1.27 * sin((259.2 - 413335.38 * T) / DEG_IN_RADIAN)
+	   + 0.66 * sin((235.7 + 890534.23 * T) / DEG_IN_RADIAN)
+	   + 0.21 * sin((269.9 + 954397.70 * T) / DEG_IN_RADIAN)
+	   - 0.19 * sin((357.5 + 35999.05 * T) / DEG_IN_RADIAN)
+	   - 0.11 * sin((186.6 + 966404.05 * T) / DEG_IN_RADIAN);
+	lambda = lambda / DEG_IN_RADIAN;
+	beta = 5.13 * sin((93.3 + 483202.03 * T) / DEG_IN_RADIAN)
+	   + 0.28 * sin((228.2 + 960400.87 * T) / DEG_IN_RADIAN)
+	   - 0.28 * sin((318.3 + 6003.18 * T) / DEG_IN_RADIAN)
+	   - 0.17 * sin((217.6 - 407332.20 * T) / DEG_IN_RADIAN);
+	beta = beta / DEG_IN_RADIAN;
+	pie = 0.9508
+	   + 0.0518 * cos((134.9 + 477198.85 * T) / DEG_IN_RADIAN)
+	   + 0.0095 * cos((259.2 - 413335.38 * T) / DEG_IN_RADIAN)
+	   + 0.0078 * cos((235.7 + 890534.23 * T) / DEG_IN_RADIAN)
+	   + 0.0028 * cos((269.9 + 954397.70 * T) / DEG_IN_RADIAN);
+	pie = pie / DEG_IN_RADIAN;
+	distance = 1 / sin(pie);
+
+	l = cos(beta) * cos(lambda);
+	m = 0.9175 * cos(beta) * sin(lambda) - 0.3978 * sin(beta);
+	n = 0.3978 * cos(beta) * sin(lambda) + 0.9175 * sin(beta);
+
+	x = l * distance;
+	y = m * distance;
+	z = n * distance;  /* for topocentric correction */
+	/* lat isn't passed right on some IBM systems unless you do this
+	   or something like it! */
+	sprintf(dummy,"%f",lat);
+	rad_lat = lat / DEG_IN_RADIAN;
+	rad_lst = sid / HRS_IN_RADIAN;
+	x = x - cos(rad_lat) * cos(rad_lst);
+	y = y - cos(rad_lat) * sin(rad_lst);
+	z = z - sin(rad_lat);
+
+
+	topo_dist = sqrt(x * x + y * y + z * z);
+
+	l = x / topo_dist;
+	m = y / topo_dist;
+	n = z / topo_dist;
+
+	alpha = atan_circ(l,m);
+	delta = asin(n);
+	*ra = alpha * HRS_IN_RADIAN;
+	*dec = delta * DEG_IN_RADIAN;
+	*dist = topo_dist;
+}
+
+
+void lpsun(jd,ra,dec)
+
+	double jd, *ra, *dec;
+
+/* Low precision formulae for the sun, from Almanac p. C24 (1990) */
+/* ra and dec are returned as decimal hours and decimal degrees. */
+
+{
+	double n, L, g, lambda,epsilon,alpha,delta,x,y,z;
+
+	n = jd - J2000;
+	L = 280.460 + 0.9856474 * n;
+	g = (357.528 + 0.9856003 * n)/DEG_IN_RADIAN;
+	lambda = (L + 1.915 * sin(g) + 0.020 * sin(2. * g))/DEG_IN_RADIAN;
+	epsilon = (23.439 - 0.0000004 * n)/DEG_IN_RADIAN;
+
+	x = cos(lambda);
+	y = cos(epsilon) * sin(lambda);
+	z = sin(epsilon)*sin(lambda);
+
+	*ra = (atan_circ(x,y))*HRS_IN_RADIAN;
+	*dec = (asin(z))*DEG_IN_RADIAN;
+}
+
+void eclrot(jd, x, y, z)
+
+	double jd, *x, *y, *z;
+
+/* rotates ecliptic rectangular coords x, y, z to
+   equatorial (all assumed of date.) */
+
+{
+	double incl;
+	double xpr,ypr,zpr;
+	double T;
+
+	T = (jd - J2000) / 36525;  /* centuries since J2000 */
+
+	incl = (23.439291 + T * (-0.0130042 - 0.00000016 * T))/DEG_IN_RADIAN;
+		/* 1992 Astron Almanac, p. B18, dropping the
+		   cubic term, which is 2 milli-arcsec! */
+	ypr = cos(incl) * *y - sin(incl) * *z;
+	zpr = sin(incl) * *y + cos(incl) * *z;
+	*y = ypr;
+	*z = zpr;
+	/* x remains the same. */
+}
+
+double circulo(x)
+	double x;
+{
+	/* assuming x is an angle in degrees, returns
+	   modulo 360 degrees. */
+
+	int n;
+
+	n = (int)(x / 360.);
+	return(x - 360. * n);
+}
+
+
+void geocent(geolong,geolat,height,x_geo,y_geo,z_geo)
+
+	double geolong, geolat, height, *x_geo, *y_geo, *z_geo;
+
+/* computes the geocentric coordinates from the geodetic
+(standard map-type) longitude, latitude, and height.
+These are assumed to be in decimal hours, decimal degrees, and
+meters respectively.  Notation generally follows 1992 Astr Almanac,
+p. K11 */
+
+
+{
+
+	double denom, C_geo, S_geo;
+
+	geolat = geolat / DEG_IN_RADIAN;
+	geolong = geolong / HRS_IN_RADIAN;
+	denom = (1. - FLATTEN) * sin(geolat);
+	denom = cos(geolat) * cos(geolat) + denom*denom;
+	C_geo = 1. / sqrt(denom);
+	S_geo = (1. - FLATTEN) * (1. - FLATTEN) * C_geo;
+	C_geo = C_geo + height / EQUAT_RAD;  /* deviation from almanac
+		       notation -- include height here. */
+	S_geo = S_geo + height / EQUAT_RAD;
+	*x_geo = C_geo * cos(geolat) * cos(geolong);
+	*y_geo = C_geo * cos(geolat) * sin(geolong);
+	*z_geo = S_geo * sin(geolat);
+}
+
+
+double etcorr(jd)
+
+double jd;
+
+{
+
+	/* Given a julian date in 1900-2100, returns the correction
+           delta t which is:
+		TDT - UT (after 1983 and before 1993)
+		ET - UT (before 1983)
+		an extrapolated guess  (after 1993).
+
+	For dates in the past (<= 1993) the value is linearly
+        interpolated on 5-year intervals; for dates after the present,
+        an extrapolation is used, because the true value of delta t
+	cannot be predicted precisely.  Note that TDT is essentially the
+	modern version of ephemeris time with a slightly cleaner
+	definition.
+
+	Where the algorithm shifts there is an approximately 0.1 second
+        discontinuity.  Also, the 5-year linear interpolation scheme can
+        lead to errors as large as 0.5 seconds in some cases, though
+ 	usually rather smaller. */
+
+	double jd1900 = 2415019.5;
+	double dates[20] = {1900,1905,1910,1915,1920,1925,1930,1935,1940,1945,
+		    1950,1955,1960,1965,1970,1975,1980,1985,1990,1993};
+	double delts[20]={-2.72,3.86,10.46,17.20,21.16,23.62,24.02,23.93,24.33,26.77,
+		  29.15,31.07,33.15,35.73,40.18,45.48,50.54,54.34,56.86,59.12};
+	double year, delt;
+	short i;
+
+	year = 1900. + (jd - 2415019.5) / 365.25;
+
+	if(year < 1993.0 && year >= 1900.) {
+		i = (year - 1900) / 5;
+		delt = delts[i] +
+		 ((delts[i+1] - delts[i])/(dates[i+1] - dates[i])) * (year - dates[i]);
+	}
+
+	else if (year > 1993. && year < 2100.)
+		delt = 33.15 + (2.164e-3) * (jd - 2436935.4);  /* rough extrapolation */
+
+	else if (year < 1900) {
+		oprntf("etcorr ... no ephemeris time data for < 1900.\n");
+       		delt = 0.;
+	}
+
+	else if (year >= 2100.) {
+		oprntf("etcorr .. very long extrapolation in delta T - inaccurate.\n");
+		delt = 180.; /* who knows? */
+	}
+
+	return(delt);
+}
+
+
+void accumoon(jd,geolat,lst,elevsea,geora,geodec,geodist,
+     topora,topodec,topodist)
+
+	double jd,geolat,lst,elevsea;
+     	double *geora,*geodec,*geodist,*topora,*topodec,*topodist;
+
+  /* jd, dec. degr., dec. hrs., meters */
+/* More accurate (but more elaborate and slower) lunar
+   ephemeris, from Jean Meeus' *Astronomical Formulae For Calculators*,
+   pub. Willman-Bell.  Includes all the terms given there. */
+
+{
+/*      double *eclatit,*eclongit, *pie,*ra,*dec,*dist; geocent quantities,
+		formerly handed out but not in this version */
+	double pie, dist;  /* horiz parallax */
+	double Lpr,M,Mpr,D,F,Om,T,Tsq,Tcb;
+	double e,lambda,B,beta,om1,om2;
+	double sinx, x, y, z, l, m, n;
+	double x_geo, y_geo, z_geo;  /* geocentric position of *observer* */
+
+	jd = jd + etcorr(jd)/SEC_IN_DAY;   /* approximate correction to ephemeris time */
+	T = (jd - 2415020.) / 36525.;   /* this based around 1900 ... */
+	Tsq = T * T;
+	Tcb = Tsq * T;
+
+	Lpr = 270.434164 + 481267.8831 * T - 0.001133 * Tsq
+			+ 0.0000019 * Tcb;
+	M = 358.475833 + 35999.0498*T - 0.000150*Tsq
+			- 0.0000033*Tcb;
+	Mpr = 296.104608 + 477198.8491*T + 0.009192*Tsq
+			+ 0.0000144*Tcb;
+	D = 350.737486 + 445267.1142*T - 0.001436 * Tsq
+			+ 0.0000019*Tcb;
+	F = 11.250889 + 483202.0251*T -0.003211 * Tsq
+			- 0.0000003*Tcb;
+	Om = 259.183275 - 1934.1420*T + 0.002078*Tsq
+			+ 0.0000022*Tcb;
+
+	Lpr = circulo(Lpr);
+	Mpr = circulo(Mpr);
+	M = circulo(M);
+	D = circulo(D);
+	F = circulo(F);
+	Om = circulo(Om);
+
+
+	sinx =  sin((51.2 + 20.2 * T)/DEG_IN_RADIAN);
+	Lpr = Lpr + 0.000233 * sinx;
+	M = M - 0.001778 * sinx;
+	Mpr = Mpr + 0.000817 * sinx;
+	D = D + 0.002011 * sinx;
+
+	sinx = 0.003964 * sin((346.560+132.870*T -0.0091731*Tsq)/DEG_IN_RADIAN);
+
+	Lpr = Lpr + sinx;
+	Mpr = Mpr + sinx;
+	D = D + sinx;
+	F = F + sinx;
+
+	sinx = sin(Om/DEG_IN_RADIAN);
+	Lpr = Lpr + 0.001964 * sinx;
+	Mpr = Mpr + 0.002541 * sinx;
+	D = D + 0.001964 * sinx;
+	F = F - 0.024691 * sinx;
+	F = F - 0.004328 * sin((Om + 275.05 -2.30*T)/DEG_IN_RADIAN);
+
+	e = 1 - 0.002495 * T - 0.00000752 * Tsq;
+
+	M = M / DEG_IN_RADIAN;   /* these will all be arguments ... */
+	Mpr = Mpr / DEG_IN_RADIAN;
+	D = D / DEG_IN_RADIAN;
+	F = F / DEG_IN_RADIAN;
+
+	lambda = Lpr + 6.288750 * sin(Mpr)
+		+ 1.274018 * sin(2*D - Mpr)
+		+ 0.658309 * sin(2*D)
+		+ 0.213616 * sin(2*Mpr)
+		- e * 0.185596 * sin(M)
+		- 0.114336 * sin(2*F)
+		+ 0.058793 * sin(2*D - 2*Mpr)
+		+ e * 0.057212 * sin(2*D - M - Mpr)
+		+ 0.053320 * sin(2*D + Mpr)
+		+ e * 0.045874 * sin(2*D - M)
+		+ e * 0.041024 * sin(Mpr - M)
+		- 0.034718 * sin(D)
+		- e * 0.030465 * sin(M+Mpr)
+		+ 0.015326 * sin(2*D - 2*F)
+		- 0.012528 * sin(2*F + Mpr)
+		- 0.010980 * sin(2*F - Mpr)
+		+ 0.010674 * sin(4*D - Mpr)
+		+ 0.010034 * sin(3*Mpr)
+		+ 0.008548 * sin(4*D - 2*Mpr)
+		- e * 0.007910 * sin(M - Mpr + 2*D)
+		- e * 0.006783 * sin(2*D + M)
+		+ 0.005162 * sin(Mpr - D);
+
+		/* And furthermore.....*/
+
+	lambda = lambda + e * 0.005000 * sin(M + D)
+		+ e * 0.004049 * sin(Mpr - M + 2*D)
+		+ 0.003996 * sin(2*Mpr + 2*D)
+		+ 0.003862 * sin(4*D)
+		+ 0.003665 * sin(2*D - 3*Mpr)
+		+ e * 0.002695 * sin(2*Mpr - M)
+		+ 0.002602 * sin(Mpr - 2*F - 2*D)
+		+ e * 0.002396 * sin(2*D - M - 2*Mpr)
+		- 0.002349 * sin(Mpr + D)
+		+ e * e * 0.002249 * sin(2*D - 2*M)
+		- e * 0.002125 * sin(2*Mpr + M)
+		- e * e * 0.002079 * sin(2*M)
+		+ e * e * 0.002059 * sin(2*D - Mpr - 2*M)
+		- 0.001773 * sin(Mpr + 2*D - 2*F)
+		- 0.001595 * sin(2*F + 2*D)
+		+ e * 0.001220 * sin(4*D - M - Mpr)
+		- 0.001110 * sin(2*Mpr + 2*F)
+		+ 0.000892 * sin(Mpr - 3*D)
+		- e * 0.000811 * sin(M + Mpr + 2*D)
+		+ e * 0.000761 * sin(4*D - M - 2*Mpr)
+		+ e * e * 0.000717 * sin(Mpr - 2*M)
+		+ e * e * 0.000704 * sin(Mpr - 2 * M - 2*D)
+		+ e * 0.000693 * sin(M - 2*Mpr + 2*D)
+		+ e * 0.000598 * sin(2*D - M - 2*F)
+		+ 0.000550 * sin(Mpr + 4*D)
+		+ 0.000538 * sin(4*Mpr)
+		+ e * 0.000521 * sin(4*D - M)
+		+ 0.000486 * sin(2*Mpr - D);
+
+/*              *eclongit = lambda;  */
+
+	B = 5.128189 * sin(F)
+		+ 0.280606 * sin(Mpr + F)
+		+ 0.277693 * sin(Mpr - F)
+		+ 0.173238 * sin(2*D - F)
+		+ 0.055413 * sin(2*D + F - Mpr)
+		+ 0.046272 * sin(2*D - F - Mpr)
+		+ 0.032573 * sin(2*D + F)
+		+ 0.017198 * sin(2*Mpr + F)
+		+ 0.009267 * sin(2*D + Mpr - F)
+		+ 0.008823 * sin(2*Mpr - F)
+		+ e * 0.008247 * sin(2*D - M - F)
+		+ 0.004323 * sin(2*D - F - 2*Mpr)
+		+ 0.004200 * sin(2*D + F + Mpr)
+		+ e * 0.003372 * sin(F - M - 2*D)
+		+ 0.002472 * sin(2*D + F - M - Mpr)
+		+ e * 0.002222 * sin(2*D + F - M)
+		+ e * 0.002072 * sin(2*D - F - M - Mpr)
+		+ e * 0.001877 * sin(F - M + Mpr)
+		+ 0.001828 * sin(4*D - F - Mpr)
+		- e * 0.001803 * sin(F + M)
+		- 0.001750 * sin(3*F)
+		+ e * 0.001570 * sin(Mpr - M - F)
+		- 0.001487 * sin(F + D)
+		- e * 0.001481 * sin(F + M + Mpr)
+		+ e * 0.001417 * sin(F - M - Mpr)
+		+ e * 0.001350 * sin(F - M)
+		+ 0.001330 * sin(F - D)
+		+ 0.001106 * sin(F + 3*Mpr)
+		+ 0.001020 * sin(4*D - F)
+		+ 0.000833 * sin(F + 4*D - Mpr);
+     /* not only that, but */
+	B = B + 0.000781 * sin(Mpr - 3*F)
+		+ 0.000670 * sin(F + 4*D - 2*Mpr)
+		+ 0.000606 * sin(2*D - 3*F)
+		+ 0.000597 * sin(2*D + 2*Mpr - F)
+		+ e * 0.000492 * sin(2*D + Mpr - M - F)
+		+ 0.000450 * sin(2*Mpr - F - 2*D)
+		+ 0.000439 * sin(3*Mpr - F)
+		+ 0.000423 * sin(F + 2*D + 2*Mpr)
+		+ 0.000422 * sin(2*D - F - 3*Mpr)
+		- e * 0.000367 * sin(M + F + 2*D - Mpr)
+		- e * 0.000353 * sin(M + F + 2*D)
+		+ 0.000331 * sin(F + 4*D)
+		+ e * 0.000317 * sin(2*D + F - M + Mpr)
+		+ e * e * 0.000306 * sin(2*D - 2*M - F)
+		- 0.000283 * sin(Mpr + 3*F);
+
+	om1 = 0.0004664 * cos(Om/DEG_IN_RADIAN);
+	om2 = 0.0000754 * cos((Om + 275.05 - 2.30*T)/DEG_IN_RADIAN);
+
+	beta = B * (1. - om1 - om2);
+/*      *eclatit = beta; */
+
+	pie = 0.950724
+		+ 0.051818 * cos(Mpr)
+		+ 0.009531 * cos(2*D - Mpr)
+		+ 0.007843 * cos(2*D)
+		+ 0.002824 * cos(2*Mpr)
+		+ 0.000857 * cos(2*D + Mpr)
+		+ e * 0.000533 * cos(2*D - M)
+		+ e * 0.000401 * cos(2*D - M - Mpr)
+		+ e * 0.000320 * cos(Mpr - M)
+		- 0.000271 * cos(D)
+		- e * 0.000264 * cos(M + Mpr)
+		- 0.000198 * cos(2*F - Mpr)
+		+ 0.000173 * cos(3*Mpr)
+		+ 0.000167 * cos(4*D - Mpr)
+		- e * 0.000111 * cos(M)
+		+ 0.000103 * cos(4*D - 2*Mpr)
+		- 0.000084 * cos(2*Mpr - 2*D)
+		- e * 0.000083 * cos(2*D + M)
+		+ 0.000079 * cos(2*D + 2*Mpr)
+		+ 0.000072 * cos(4*D)
+		+ e * 0.000064 * cos(2*D - M + Mpr)
+		- e * 0.000063 * cos(2*D + M - Mpr)
+		+ e * 0.000041 * cos(M + D)
+		+ e * 0.000035 * cos(2*Mpr - M)
+		- 0.000033 * cos(3*Mpr - 2*D)
+		- 0.000030 * cos(Mpr + D)
+		- 0.000029 * cos(2*F - 2*D)
+		- e * 0.000029 * cos(2*Mpr + M)
+		+ e * e * 0.000026 * cos(2*D - 2*M)
+		- 0.000023 * cos(2*F - 2*D + Mpr)
+		+ e * 0.000019 * cos(4*D - M - Mpr);
+
+	beta = beta/DEG_IN_RADIAN;
+	lambda = lambda/DEG_IN_RADIAN;
+	l = cos(lambda) * cos(beta);
+	m = sin(lambda) * cos(beta);
+	n = sin(beta);
+	eclrot(jd,&l,&m,&n);
+
+	dist = 1/sin((pie)/DEG_IN_RADIAN);
+	x = l * dist;
+	y = m * dist;
+	z = n * dist;
+
+	*geora = atan_circ(l,m) * HRS_IN_RADIAN;
+	*geodec = asin(n) * DEG_IN_RADIAN;
+	*geodist = dist;
+
+	geocent(lst,geolat,elevsea,&x_geo,&y_geo,&z_geo);
+
+	x = x - x_geo;  /* topocentric correction using elliptical earth fig. */
+	y = y - y_geo;
+	z = z - z_geo;
+
+	*topodist = sqrt(x*x + y*y + z*z);
+
+	l = x / (*topodist);
+	m = y / (*topodist);
+	n = z / (*topodist);
+
+	*topora = atan_circ(l,m) * HRS_IN_RADIAN;
+	*topodec = asin(n) * DEG_IN_RADIAN;
+
+}
+
+void flmoon(n,nph,jdout)
+
+	int n,nph;
+	double *jdout;
+
+/* Gives jd (+- 2 min) of phase nph on lunation n; replaces
+less accurate Numerical Recipes routine.  This routine
+implements formulae found in Jean Meeus' *Astronomical Formulae
+for Calculators*, 2nd edition, Willman-Bell.  A very useful
+book!! */
+
+/* n, nph lunation and phase; nph = 0 new, 1 1st, 2 full, 3 last
+ *jdout   jd of requested phase */
+
+{
+	double jd, cor;
+	double M, Mpr, F;
+	double T;
+	double lun;
+
+	lun = (double) n + (double) nph / 4.;
+	T = lun / 1236.85;
+	jd = 2415020.75933 + 29.53058868 * lun
+		+ 0.0001178 * T * T
+		- 0.000000155 * T * T * T
+		+ 0.00033 * sin((166.56 + 132.87 * T - 0.009173 * T * T)/DEG_IN_RADIAN);
+	M = 359.2242 + 29.10535608 * lun - 0.0000333 * T * T - 0.00000347 * T * T * T;
+	M = M / DEG_IN_RADIAN;
+	Mpr = 306.0253 + 385.81691806 * lun + 0.0107306 * T * T + 0.00001236 * T * T * T;
+	Mpr = Mpr / DEG_IN_RADIAN;
+	F = 21.2964 + 390.67050646 * lun - 0.0016528 * T * T - 0.00000239 * T * T * T;
+	F = F / DEG_IN_RADIAN;
+	if((nph == 0) || (nph == 2)) {/* new or full */
+		cor =   (0.1734 - 0.000393*T) * sin(M)
+			+ 0.0021 * sin(2*M)
+			- 0.4068 * sin(Mpr)
+			+ 0.0161 * sin(2*Mpr)
+			- 0.0004 * sin(3*Mpr)
+			+ 0.0104 * sin(2*F)
+			- 0.0051 * sin(M + Mpr)
+			- 0.0074 * sin(M - Mpr)
+			+ 0.0004 * sin(2*F+M)
+			- 0.0004 * sin(2*F-M)
+			- 0.0006 * sin(2*F+Mpr)
+			+ 0.0010 * sin(2*F-Mpr)
+			+ 0.0005 * sin(M+2*Mpr);
+		jd = jd + cor;
+	}
+	else {
+		cor = (0.1721 - 0.0004*T) * sin(M)
+			+ 0.0021 * sin(2 * M)
+			- 0.6280 * sin(Mpr)
+			+ 0.0089 * sin(2 * Mpr)
+			- 0.0004 * sin(3 * Mpr)
+			+ 0.0079 * sin(2*F)
+			- 0.0119 * sin(M + Mpr)
+			- 0.0047 * sin(M - Mpr)
+			+ 0.0003 * sin(2 * F + M)
+			- 0.0004 * sin(2 * F - M)
+			- 0.0006 * sin(2 * F + Mpr)
+			+ 0.0021 * sin(2 * F - Mpr)
+			+ 0.0003 * sin(M + 2 * Mpr)
+			+ 0.0004 * sin(M - 2 * Mpr)
+			- 0.0003 * sin(2*M + Mpr);
+		if(nph == 1) cor = cor + 0.0028 -
+				0.0004 * cos(M) + 0.0003 * cos(Mpr);
+		if(nph == 3) cor = cor - 0.0028 +
+				0.0004 * cos(M) - 0.0003 * cos(Mpr);
+		jd = jd + cor;
+
+	}
+	*jdout = jd;
+}
+
+float lun_age(jd, nlun)
+
+	double jd;
+	int *nlun;
+
+{
+	/* compute age in days of moon since last new,
+	   and lunation of last new moon. */
+
+	int n; /* appropriate lunation */
+	int nlast;
+	double newjd, lastnewjd;
+	short kount=0;
+	float x;
+
+	nlast = (jd - 2415020.5) / 29.5307 - 1;
+
+	flmoon(nlast,0,&lastnewjd);
+	nlast++;
+	flmoon(nlast,0,&newjd);
+	while((newjd < jd) && (kount < 40)) {
+		lastnewjd = newjd;
+		nlast++;
+		flmoon(nlast,0,&newjd);
+	}
+ 	if(kount > 35) {
+		oprntf("Didn't find phase in lun_age!\n");
+		x = -10.;
+                *nlun = 0;
+	}
+	else {
+	  x = jd - lastnewjd;
+	  *nlun = nlast - 1;
+        }
+
+	return(x);
+}
+
+void print_phase(jd)
+	double jd;
+
+{
+	/* prints a verbal description of moon phase, given the
+	   julian date.  */
+
+	int n; /* appropriate lunation */
+	int nlast, noctiles;
+	double newjd, lastnewjd;
+	double fqjd, fljd, lqjd;  /* jds of first, full, and last in this lun.*/
+	short kount=0;
+	float x;
+
+	nlast = (jd - 2415020.5) / 29.5307 - 1;  /* find current lunation */
+
+	flmoon(nlast,0,&lastnewjd);
+	nlast++;
+	flmoon(nlast,0,&newjd);
+	while((newjd < jd) && (kount < 40)) {
+		lastnewjd = newjd;
+		nlast++;
+		flmoon(nlast,0,&newjd);
+	}
+	if(kount > 35) {  /* oops ... didn't find it ... */
+		oprntf("Didn't find phase in print_phase!\n");
+		x = -10.;
+	}
+	else {     /* found lunation ok */
+		x = jd - lastnewjd;
+		nlast--;
+		noctiles = x / 3.69134;  /* 3.69134 = 1/8 month; truncate. */
+		if(noctiles == 0) oprntf("%3.1f days since new moon",x);
+		else if (noctiles <= 2) {  /* nearest first quarter */
+			flmoon(nlast,1,&fqjd);
+			x = jd - fqjd;
+			if(x < 0.)
+			  oprntf("%3.1f days before first quarter",(-1.*x));
+			else
+			  oprntf("%3.1f days since first quarter",x);
+		}
+		else if (noctiles <= 4) {  /* nearest full */
+			flmoon(nlast,2,&fljd);
+			x = jd - fljd;
+			if(x < 0.)
+			  oprntf("%3.1f days until full moon",(-1.*x));
+			else
+			  oprntf("%3.1f days after full moon",x);
+		}
+		else if (noctiles <= 6) {  /* nearest last quarter */
+			flmoon(nlast,3,&lqjd);
+			x = jd - lqjd;
+			if(x < 0.)
+			  oprntf("%3.1f days before last quarter",(-1.*x));
+			else
+			  oprntf("%3.1f days after last quarter",x);
+		}
+		else oprntf("%3.1f days before new moon",(newjd - jd));
+	}
+}
+
+double lunskybright(alpha,rho,kzen,altmoon,alt, moondist)
+
+	double alpha,rho,kzen,altmoon,alt,moondist;
+
+/* Evaluates predicted LUNAR part of sky brightness, in
+   V magnitudes per square arcsecond, following K. Krisciunas
+   and B. E. Schaeffer (1991) PASP 103, 1033.
+
+   alpha = separation of sun and moon as seen from earth,
+   converted internally to its supplement,
+   rho = separation of moon and object,
+   kzen = zenith extinction coefficient,
+   altmoon = altitude of moon above horizon,
+   alt = altitude of object above horizon
+   moondist = distance to moon, in earth radii
+
+   all are in decimal degrees. */
+
+{
+
+    double istar,Xzm,Xo,Z,Zmoon,Bmoon,fofrho,rho_rad,test;
+
+    rho_rad = rho/DEG_IN_RADIAN;
+    alpha = (180. - alpha);
+    Zmoon = (90. - altmoon)/DEG_IN_RADIAN;
+    Z = (90. - alt)/DEG_IN_RADIAN;
+    moondist = moondist/(60.27);  /* divide by mean distance */
+
+    istar = -0.4*(3.84 + 0.026*fabs(alpha) + 4.0e-9*pow(alpha,4.)); /*eqn 20*/
+    istar =  pow(10.,istar)/(moondist * moondist);
+    if(fabs(alpha) < 7.)   /* crude accounting for opposition effect */
+	istar = istar * (1.35 - 0.05 * fabs(istar));
+	/* 35 per cent brighter at full, effect tapering linearly to
+	   zero at 7 degrees away from full. mentioned peripherally in
+	   Krisciunas and Scheafer, p. 1035. */
+    fofrho = 229087. * (1.06 + cos(rho_rad)*cos(rho_rad));
+    if(fabs(rho) > 10.)
+       fofrho=fofrho+pow(10.,(6.15 - rho/40.));            /* eqn 21 */
+    else if (fabs(rho) > 0.25)
+       fofrho= fofrho+ 6.2e7 / (rho*rho);   /* eqn 19 */
+    else fofrho = fofrho+9.9e8;  /*for 1/4 degree -- radius of moon! */
+    Xzm = sqrt(1.0 - 0.96*sin(Zmoon)*sin(Zmoon));
+    if(Xzm != 0.) Xzm = 1./Xzm;
+	  else Xzm = 10000.;
+    Xo = sqrt(1.0 - 0.96*sin(Z)*sin(Z));
+    if(Xo != 0.) Xo = 1./Xo;
+	  else Xo = 10000.;
+    Bmoon = fofrho * istar * pow(10.,(-0.4*kzen*Xzm))
+	  * (1. - pow(10.,(-0.4*kzen*Xo)));   /* nanoLamberts */
+    if(Bmoon > 0.001)
+      return(22.50 - 1.08574 * log(Bmoon/34.08)); /* V mag per sq arcs-eqn 1 */
+    else return(99.);
+}
+
+void accusun(jd,lst,geolat,ra,dec,dist,topora,topodec,x,y,z)
+
+	double jd,lst,geolat,*ra,*dec,*dist,*topora,*topodec;
+ 	double *x, *y, *z;
+{
+      /*  implemenataion of Jean Meeus' more accurate solar
+	  ephemeris.  For ultimate use in helio correction! From
+	  Astronomical Formulae for Calculators, pp. 79 ff.  This
+	  gives sun's position wrt *mean* equinox of date, not
+	  *apparent*.  Accuracy is << 1 arcmin.  Positions given are
+	  geocentric ... parallax due to observer's position on earth is
+	  ignored. This is up to 8 arcsec; routine is usually a little
+	  better than that.
+          // -- topocentric correction *is* included now. -- //
+	  Light travel time is apparently taken into
+	  account for the ra and dec, but I don't know if aberration is
+	  and I don't know if distance is simlarly antedated.
+
+	  x, y, and z are heliocentric equatorial coordinates of the
+	  EARTH, referred to mean equator and equinox of date. */
+
+	double L, T, Tsq, Tcb;
+	double M, e, Cent, nu, sunlong;
+	double Lrad, Mrad, nurad, R;
+	double A, B, C, D, E, H;
+	double xtop, ytop, ztop, topodist, l, m, n, xgeo, ygeo, zgeo;
+
+	jd = jd + etcorr(jd)/SEC_IN_DAY;  /* might as well do it right .... */
+	T = (jd - 2415020.) / 36525.;  /* 1900 --- this is an oldish theory*/
+	Tsq = T*T;
+	Tcb = T*Tsq;
+	L = 279.69668 + 36000.76892*T + 0.0003025*Tsq;
+	M = 358.47583 + 35999.04975*T - 0.000150*Tsq - 0.0000033*Tcb;
+	e = 0.01675104 - 0.0000418*T - 0.000000126*Tsq;
+
+	L = circulo(L);
+	M = circulo(M);
+/*      printf("raw L, M: %15.8f, %15.8f\n",L,M); */
+
+	A = 153.23 + 22518.7541 * T;  /* A, B due to Venus */
+	B = 216.57 + 45037.5082 * T;
+	C = 312.69 + 32964.3577 * T;  /* C due to Jupiter */
+		/* D -- rough correction from earth-moon
+			barycenter to center of earth. */
+	D = 350.74 + 445267.1142*T - 0.00144*Tsq;
+	E = 231.19 + 20.20*T;    /* "inequality of long period .. */
+	H = 353.40 + 65928.7155*T;  /* Jupiter. */
+
+	A = circulo(A) / DEG_IN_RADIAN;
+	B = circulo(B) / DEG_IN_RADIAN;
+	C = circulo(C) / DEG_IN_RADIAN;
+	D = circulo(D) / DEG_IN_RADIAN;
+	E = circulo(E) / DEG_IN_RADIAN;
+	H = circulo(H) / DEG_IN_RADIAN;
+
+	L = L + 0.00134 * cos(A)
+	      + 0.00154 * cos(B)
+	      + 0.00200 * cos(C)
+	      + 0.00179 * sin(D)
+	      + 0.00178 * sin(E);
+
+	Lrad = L/DEG_IN_RADIAN;
+	Mrad = M/DEG_IN_RADIAN;
+
+	Cent = (1.919460 - 0.004789*T -0.000014*Tsq)*sin(Mrad)
+	     + (0.020094 - 0.000100*T) * sin(2.0*Mrad)
+	     + 0.000293 * sin(3.0*Mrad);
+	sunlong = L + Cent;
+
+
+	nu = M + Cent;
+	nurad = nu / DEG_IN_RADIAN;
+
+	R = (1.0000002 * (1 - e*e)) / (1. + e * cos(nurad));
+	R = R + 0.00000543 * sin(A)
+	      + 0.00001575 * sin(B)
+	      + 0.00001627 * sin(C)
+	      + 0.00003076 * cos(D)
+	      + 0.00000927 * sin(H);
+/*      printf("solar longitude: %10.5f  Radius vector %10.7f\n",sunlong,R);
+	printf("eccentricity %10.7f  eqn of center %10.5f\n",e,Cent);   */
+
+	sunlong = sunlong/DEG_IN_RADIAN;
+
+	*dist = R;
+	*x = cos(sunlong);  /* geocentric */
+	*y = sin(sunlong);
+	*z = 0.;
+	eclrot(jd, x, y, z);
+
+/*      --- code to include topocentric correction for sun .... */
+
+	geocent(lst,geolat,0.,&xgeo,&ygeo,&zgeo);
+
+	xtop = *x - xgeo*EQUAT_RAD/ASTRO_UNIT;
+	ytop = *y - ygeo*EQUAT_RAD/ASTRO_UNIT;
+	ztop = *z - zgeo*EQUAT_RAD/ASTRO_UNIT;
+
+	topodist = sqrt(xtop*xtop + ytop*ytop + ztop*ztop);
+
+	l = xtop / (topodist);
+	m = ytop / (topodist);
+	n = ztop / (topodist);
+
+	*topora = atan_circ(l,m) * HRS_IN_RADIAN;
+	*topodec = asin(n) * DEG_IN_RADIAN;
+
+	*ra = atan_circ(*x,*y) * HRS_IN_RADIAN;
+	*dec = asin(*z) * DEG_IN_RADIAN;
+
+	*x = *x * R * -1;  /* heliocentric */
+	*y = *y * R * -1;
+	*z = *z * R * -1;
+
+}
+
+double jd_moon_alt(alt,jdguess,lat,longit,elevsea)
+
+	double alt,jdguess,lat,longit,elevsea;
+
+{
+	/* returns jd at which moon is at a given
+	altitude, given jdguess as a starting point. In current version
+	uses high-precision moon -- execution time does not seem to be
+	excessive on modern hardware.  If it's a problem on your machine,
+	you can replace calls to 'accumoon' with 'lpmoon' and remove
+	the 'elevsea' argument. */
+
+	double jdout;
+	double deriv, err, del = 0.002;
+	double ra,dec,dist,geora,geodec,geodist,sid,ha,alt2,alt3,az;
+	short i = 0;
+
+	/* first guess */
+
+	sid=lst(jdguess,longit);
+	accumoon(jdguess,lat,sid,elevsea,&geora,&geodec,&geodist,
+				&ra,&dec,&dist);
+	ha = lst(jdguess,longit) - ra;
+	alt2 = altit(dec,ha,lat,&az);
+	jdguess = jdguess + del;
+	sid = lst(jdguess,longit);
+	accumoon(jdguess,lat,sid,elevsea,&geora,&geodec,&geodist,
+				&ra,&dec,&dist);
+	alt3 = altit(dec,(sid - ra),lat,&az);
+	err = alt3 - alt;
+	deriv = (alt3 - alt2) / del;
+	while((fabs(err) > 0.1) && (i < 10)) {
+		jdguess = jdguess - err/deriv;
+		sid=lst(jdguess,longit);
+		accumoon(jdguess,lat,sid,elevsea,&geora,&geodec,&geodist,
+				&ra,&dec,&dist);
+		alt3 = altit(dec,(sid - ra),lat,&az);
+		err = alt3 - alt;
+		i++;
+		if(i == 9) oprntf("Moonrise or -set calculation not converging!!...\n");
+	}
+	if(i >= 9) jdguess = -1000.;
+	jdout = jdguess;
+	return(jdout);
+}
+
+double jd_sun_alt(alt,jdguess,lat,longit)
+
+	double alt,jdguess,lat,longit;
+
+{
+	/* returns jd at which sun is at a given
+	altitude, given jdguess as a starting point. Uses
+	low-precision sun, which is plenty good enough. */
+
+	double jdout;
+	double deriv, err, del = 0.002;
+	double ra,dec,ha,alt2,alt3,az;
+	short i = 0;
+
+	/* first guess */
+
+	lpsun(jdguess,&ra,&dec);
+	ha = lst(jdguess,longit) - ra;
+	alt2 = altit(dec,ha,lat,&az);
+	jdguess = jdguess + del;
+	lpsun(jdguess,&ra,&dec);
+	alt3 = altit(dec,(lst(jdguess,longit) - ra),lat,&az);
+	err = alt3 - alt;
+	deriv = (alt3 - alt2) / del;
+	while((fabs(err) > 0.1) && (i < 10)) {
+		jdguess = jdguess - err/deriv;
+		lpsun(jdguess,&ra,&dec);
+		alt3 = altit(dec,(lst(jdguess,longit) - ra),lat,&az);
+		err = alt3 - alt;
+		i++;
+		if(i == 9) oprntf("Sunrise, set, or twilight calculation not converging!\n");
+	}
+	if(i >= 9) jdguess = -1000.;
+	jdout = jdguess;
+	return(jdout);
+}
+
+float ztwilight(alt)
+	double alt;
+{
+
+/* evaluates a polynomial expansion for the approximate brightening
+   in magnitudes of the zenith in twilight compared to its
+   value at full night, as function of altitude of the sun (in degrees).
+   To get this expression I looked in Meinel, A.,
+   & Meinel, M., "Sunsets, Twilight, & Evening Skies", Cambridge U.
+   Press, 1983; there's a graph on p. 38 showing the decline of
+   zenith twilight.  I read points off this graph and fit them with a
+   polynomial; I don't even know what band there data are for! */
+/* Comparison with Ashburn, E. V. 1952, JGR, v.57, p.85 shows that this
+   is a good fit to his B-band measurements.  */
+
+	float y, val;
+
+	y = (-1.* alt - 9.0) / 9.0;  /* my polynomial's argument...*/
+	val = ((2.0635175 * y + 1.246602) * y - 9.4084495)*y + 6.132725;
+	return(val);
+}
+
+
+void find_dst_bounds(yr,stdz,use_dst,jdb,jde)
+
+	short yr;
+	double stdz;
+	short use_dst;
+  	double *jdb,*jde;
+
+{
+	/* finds jd's at which daylight savings time begins
+	    and ends.  The parameter use_dst allows for a number
+	    of conventions, namely:
+		0 = don't use it at all (standard time all the time)
+		1 = use USA convention (1st Sun in April to
+		     last Sun in Oct after 1986; last Sun in April before)
+		2 = use Spanish convention (for Canary Islands)
+		-1 = use Chilean convention (CTIO).
+		-2 = Australian convention (for AAT).
+	    Negative numbers denote sites in the southern hemisphere,
+	    where jdb and jde are beginning and end of STANDARD time for
+	    the year.
+	    It's assumed that the time changes at 2AM local time; so
+	    when clock is set ahead, time jumps suddenly from 2 to 3,
+	    and when time is set back, the hour from 1 to 2 AM local
+	    time is repeated.  This could be changed in code if need be. */
+
+	struct date_time trial;
+
+	if((use_dst == 1) || (use_dst == 0)) {
+	    /* USA Convention, and including no DST to be defensive */
+	    /* Note that this ignores various wrinkles such as the
+		brief Nixon administration flirtation with year-round DST,
+		the extended DST of WW II, and so on. */
+		trial.y = yr;
+		trial.mo = 4;
+		if(yr >= 1986) trial.d = 1;
+		else trial.d = 30;
+		trial.h = 2;
+		trial.mn = 0;
+		trial.s = 0;
+
+		/* Find first Sunday in April for 1986 on ... */
+		if(yr >= 1986)
+			while(day_of_week(date_to_jd(trial)) != 6)
+				trial.d++;
+
+		/* Find last Sunday in April for pre-1986 .... */
+		else while(day_of_week(date_to_jd(trial)) != 6)
+				trial.d--;
+
+		*jdb = date_to_jd(trial) + stdz/24.;
+
+		/* Find last Sunday in October ... */
+		trial.mo = 10;
+		trial.d = 31;
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d--;
+		}
+		*jde = date_to_jd(trial) + (stdz - 1.)/24.;
+	}
+	else if (use_dst == 2) {  /* Spanish, for Canaries */
+		trial.y = yr;
+		trial.mo = 3;
+		trial.d = 31;
+		trial.h = 2;
+		trial.mn = 0;
+		trial.s = 0;
+
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d--;
+		}
+		*jdb = date_to_jd(trial) + stdz/24.;
+		trial.mo = 9;
+		trial.d = 30;
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d--;
+		}
+		*jde = date_to_jd(trial) + (stdz - 1.)/24.;
+	}
+	else if (use_dst == -1) {  /* Chilean, for CTIO, etc.  */
+	   /* off daylight 2nd Sun in March, onto daylight 2nd Sun in October */
+		trial.y = yr;
+		trial.mo = 3;
+		trial.d = 8;  /* earliest possible 2nd Sunday */
+		trial.h = 2;
+		trial.mn = 0;
+		trial.s = 0;
+
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d++;
+		}
+		*jdb = date_to_jd(trial) + (stdz - 1.)/24.;
+			/* note jdb is beginning of STANDARD time in south,
+				hence use stdz - 1. */
+		trial.mo = 10;
+		trial.d = 8;
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d++;
+		}
+		*jde = date_to_jd(trial) + stdz /24.;
+	}
+	else if (use_dst == -2) {  /* For Anglo-Australian Telescope  */
+	   /* off daylight 1st Sun in March, onto daylight last Sun in October */
+		trial.y = yr;
+		trial.mo = 3;
+		trial.d = 1;  /* earliest possible 1st Sunday */
+		trial.h = 2;
+		trial.mn = 0;
+		trial.s = 0;
+
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d++;
+		}
+		*jdb = date_to_jd(trial) + (stdz - 1.)/24.;
+			/* note jdb is beginning of STANDARD time in south,
+				hence use stdz - 1. */
+		trial.mo = 10;
+		trial.d = 31;
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d--;
+		}
+		*jde = date_to_jd(trial) + stdz /24.;
+	}
+}
+
+
+double zone(use_dst,stdz,jd,jdb,jde)
+
+	short use_dst;
+	double stdz,jd,jdb,jde;
+
+{
+	/* Returns zone time offset when standard time zone is stdz,
+	   when daylight time begins (for the year) on jdb, and ends
+	   (for the year) on jde.  This is parochial to the northern
+	   hemisphere.  */
+	/* Extension -- specifying a negative value of use_dst reverses
+	   the logic for the Southern hemisphere; then DST is assumed for
+	   the Southern hemisphere summer (which is the end and beginning
+	   of the year. */
+
+	if(use_dst == 0) return(stdz);
+	else if((jd > jdb) && (jd < jde) && (use_dst > 0)) return(stdz-1.);
+	   /* next line .. use_dst < 0 .. for Southern Hemisphere sites. */
+	else if(((jd < jdb) || (jd > jde)) && (use_dst < 0)) return(stdz-1.);
+	else return(stdz);
+}
+
+double true_jd(date, use_dst, enter_ut, night_date, stdz)
+
+/* takes the values in the date-time structure, the standard time
+   zone (in hours west), the prevailing conventions for date and
+   time entry, and returns the value of the true julian date. */
+
+	struct date_time date;
+	short use_dst, enter_ut, night_date;
+	double stdz;
+{
+	double jd, jdb, jde, test;
+
+	if(enter_ut == 0) {
+           find_dst_bounds(date.y,stdz,use_dst,&jdb,&jde);
+	   jd = date_to_jd(date);
+	   if((night_date == 1)  && (date.h < 12)) jd = jd + 1.;
+	   if(use_dst != 0)  {  /* check at time changes */
+		test = jd + stdz/24. - jdb;
+		if((test > 0.) && (test < 0.041666666))   {
+			/* 0.0416 = 1 hr; nonexistent time */
+			oprntf("Error in true_jd -- nonexistent input time during std->dst change.\n");
+			oprntf("Specify as 1 hour later!\n");
+			return(-1.); /* signal of nonexistent time */
+		}
+		test = jd + stdz/24. - jde;
+		if((test > 0.) && (test < 0.041666666))   {
+			oprntf("WARNING ... ambiguous input time during dst->std change!\n");
+		}
+	   }
+	   jd = jd + zone(use_dst,stdz,(jd+stdz/24.),jdb,jde)/24.;
+			/* effect should be to default to standard time. */
+        }
+	else jd = date_to_jd(date);
+
+	return(jd);
+}
+
+
+void print_tz(jd,use,jdb,jde,zabr)
+
+	double jd;
+	short use;
+	double jdb,jde;
+	char zabr;
+
+{
+	/* prints correct time abbreviation, given zabr as the
+	   single character abbreviation for the time zone,
+	   "D" or "S" depending on daylight or standard (dst
+	    begins at jdb, ends at jde) and current jd. */
+
+	oprntf(" %c", zabr);
+	if((jd > jdb) && (jd < jde) && (use > 0)) oprntf("D");
+	  else if(((jd < jdb) || (jd > jde)) && (use < 0)) oprntf("D");
+	  else oprntf("S");
+	oprntf("T");
+}
+
+void xyz_cel(x, y, z, r, d)
+
+	double x, y, z, *r, *d;
+
+     /* Cartesian coordinate triplet */
+
+{
+   /* converts a coordinate triplet back to a standard ra and dec */
+
+   double mod;    /* modulus */
+   double xy;     /* component in xy plane */
+   short sign;    /* for determining quadrant */
+   double radian_ra, radian_dec;
+
+   /* this taken directly from pl1 routine - no acos or asin available there,
+       as it is in c. Easier just to copy, though */
+
+   mod = sqrt(x*x + y*y + z*z);
+   x = x / mod;
+   y = y / mod;
+   z = z / mod;   /* normalize 'em explicitly first. */
+
+   xy = sqrt(x*x + y*y);
+
+   if(xy < 1.0e-10) {
+      radian_ra = 0.;  /* too close to pole */
+      radian_dec = PI / 2.;
+      if(z < 0.) radian_dec = radian_dec * -1.;
+   }
+   else {
+      if(fabs(z/xy) < 3.) radian_dec = atan(z / xy);
+	 else if (z >= 0.) radian_dec = PI / 2. - atan(xy / z);
+	 else radian_dec = -1. * PI / 2. - atan(xy / z);
+      if(fabs(x) > 1.0e-10) {
+	 if(fabs(y / x) < 3.) radian_ra = atan(y / x);
+	 else if ((x * y ) >= 0.) radian_ra = PI / 2. - atan(x/y);
+	 else radian_ra = -1. *  PI / 2. - atan(x / y);
+      }
+      else {
+	 radian_ra = PI / 2.;
+	 if((x * y)<= 0.) radian_ra = radian_ra * -1.;
+      }
+      if(x <0.) radian_ra = radian_ra + PI ;
+      if(radian_ra < 0.) radian_ra = radian_ra + 2. * PI ;
+   }
+
+   *r = radian_ra * HRS_IN_RADIAN;
+   *d = radian_dec * DEG_IN_RADIAN;
+
+}
+
+void precrot(rorig, dorig, orig_epoch, final_epoch, rf, df)
+
+	double rorig, dorig, orig_epoch, final_epoch, *rf, *df;
+
+/*  orig_epoch, rorig, dorig  years, decimal hours, decimal degr.
+    final_epoch;
+    *rf, *df final ra and dec */
+
+   /* Takes a coordinate pair and precesses it using matrix procedures
+      as outlined in Taff's Computational Spherical Astronomy book.
+      This is the so-called 'rigorous' method which should give very
+      accurate answers all over the sky over an interval of several
+      centuries.  Naked eye accuracy holds to ancient times, too.
+      Precession constants used are the new IAU1976 -- the 'J2000'
+      system. */
+
+{
+   double ti, tf, zeta, z, theta;  /* all as per  Taff */
+   double cosz, coszeta, costheta, sinz, sinzeta, sintheta;  /* ftns */
+   double p11, p12, p13, p21, p22, p23, p31, p32, p33;
+      /* elements of the rotation matrix */
+   double radian_ra, radian_dec;
+   double orig_x, orig_y, orig_z;
+   double fin_x, fin_y, fin_z;   /* original and final unit ectors */
+
+   ti = (orig_epoch - 2000.) / 100.;
+   tf = (final_epoch - 2000. - 100. * ti) / 100.;
+
+   zeta = (2306.2181 + 1.39656 * ti + 0.000139 * ti * ti) * tf +
+    (0.30188 - 0.000344 * ti) * tf * tf + 0.017998 * tf * tf * tf;
+   z = zeta + (0.79280 + 0.000410 * ti) * tf * tf + 0.000205 * tf * tf * tf;
+   theta = (2004.3109 - 0.8533 * ti - 0.000217 * ti * ti) * tf
+     - (0.42665 + 0.000217 * ti) * tf * tf - 0.041833 * tf * tf * tf;
+
+   /* convert to radians */
+
+   zeta = zeta / ARCSEC_IN_RADIAN;
+   z = z / ARCSEC_IN_RADIAN;
+   theta = theta / ARCSEC_IN_RADIAN;
+
+   /* compute the necessary trig functions for speed and simplicity */
+
+   cosz = cos(z);
+   coszeta = cos(zeta);
+   costheta = cos(theta);
+   sinz = sin(z);
+   sinzeta = sin(zeta);
+   sintheta = sin(theta);
+
+   /* compute the elements of the precession matrix */
+
+   p11 = coszeta * cosz * costheta - sinzeta * sinz;
+   p12 = -1. * sinzeta * cosz * costheta - coszeta * sinz;
+   p13 = -1. * cosz * sintheta;
+
+   p21 = coszeta * sinz * costheta + sinzeta * cosz;
+   p22 = -1. * sinzeta * sinz * costheta + coszeta * cosz;
+   p23 = -1. * sinz * sintheta;
+
+   p31 = coszeta * sintheta;
+   p32 = -1. * sinzeta * sintheta;
+   p33 = costheta;
+
+   /* transform original coordinates */
+
+   radian_ra = rorig / HRS_IN_RADIAN;
+   radian_dec = dorig / DEG_IN_RADIAN;
+
+   orig_x = cos(radian_dec) * cos(radian_ra);
+   orig_y = cos(radian_dec) *sin(radian_ra);
+   orig_z = sin(radian_dec);
+      /* (hard coded matrix multiplication ...) */
+   fin_x = p11 * orig_x + p12 * orig_y + p13 * orig_z;
+   fin_y = p21 * orig_x + p22 * orig_y + p23 * orig_z;
+   fin_z = p31 * orig_x + p32 * orig_y + p33 * orig_z;
+
+   /* convert back to spherical polar coords */
+
+   xyz_cel(fin_x, fin_y, fin_z, rf, df);
+
+}
+
+void mass_precess() {
+
+    double rorig = 1., dorig, orig_epoch, final_epoch, rf, df,
+            mura = 0., mudec = 0., dt;
+    short do_proper = 0;
+
+    printf("Mass precession.  The '=' command does precessions in a pinch, but\n");
+    printf("the present command is convenient for doing several (all with same\n");
+    printf("starting and ending epochs).  This routine does not affect parameters\n");
+    printf("in the rest of the program.\n\n");
+    printf("Type 1 if you need proper motions (They're a pain!), or 0:");
+    scanf("%hd",&do_proper);
+    if(do_proper == 1) {
+	printf("\nA proper motion correction will be included -- it's a simple\n");
+	printf("linear correction (adds mu * dt to coordinate).  Proper motion\n");
+	printf("itself is NOT rigorously transformed (as in B1950->J2000, which\n");
+	printf("involves a small change of inertial reference frame!)\n\n");
+    }
+    printf("Give epoch to precess from:");
+    scanf("%lf",&orig_epoch);
+    printf("Give epoch to precess to:");
+    scanf("%lf",&final_epoch);
+    dt = final_epoch - orig_epoch;
+    while(rorig >= 0.) {
+	printf("\nGive RA for %7.2f (h m s, -1 0 0 to exit):",orig_epoch);
+        rorig = get_coord();
+        if(rorig < 0.) {
+		printf("Exiting precession routine.  Type '?' if you want a menu.\n");
+		return;
+        }
+        printf("Give dec (d m s):");
+        dorig = get_coord();
+	if(do_proper == 1) get_pm(dorig,&mura,&mudec);
+        precrot(rorig,dorig,orig_epoch,final_epoch,&rf,&df);
+	rf = rf + mura * dt / 3600.;
+        df = df + mudec * dt / 3600.;
+        oprntf("\n\n %7.2f : RA = ",orig_epoch);
+        put_coords(rorig,4);
+        oprntf(", dec = ");
+        put_coords(dorig,3);
+        oprntf("\n %7.2f : RA = ",final_epoch);
+        put_coords(rf,4);
+        oprntf(", dec = ");
+        put_coords(df,3);
+        if(do_proper == 1) oprntf("\n RA p.m. = %8.4f sec/yr, dec = %8.3f arcsec/yr",mura,mudec);
+	oprntf("\n");
+    }
+}
+
+void galact(ra,dec,epoch,glong,glat)
+
+	double ra,dec,epoch,*glong,*glat;
+
+{
+	/* Homebrew algorithm for 3-d Euler rotation into galactic.
+	   Perfectly rigorous, and with reasonably accurate input
+	   numbers derived from original IAU definition of galactic
+	   pole (12 49, +27.4, 1950) and zero of long (at PA 123 deg
+	   from pole.) */
+
+	double  p11= -0.066988739415,
+		p12= -0.872755765853,
+		p13= -0.483538914631,
+		p21=  0.492728466047,
+		p22= -0.450346958025,
+		p23=  0.744584633299,
+		p31= -0.867600811168,
+		p32= -0.188374601707,
+		p33=  0.460199784759;  /* derived from Euler angles of
+		theta   265.610844031 deg (rotates x axis to RA of galact center),
+		phi     28.9167903483 deg (rotates x axis to point at galact cent),
+		omega   58.2813466094 deg (rotates z axis to point at galact pole) */
+
+	double r1950,d1950,
+		x0,y0,z0,x1,y1,z1,
+		check;
+
+/*   EXCISED CODE .... creates matrix from Euler angles. Resurrect if
+     necessary to create new Euler angles for better precision.
+     Program evolved by running and calculating angles,then initializing
+     them to the values they will always have, thus saving space and time.
+
+	cosphi = cos(phi); and so on
+	p11 = cosphi * costhet;
+	p12 = cosphi * sinthet;
+	p13 = -1. * sinphi;
+	p21 = sinom*sinphi*costhet - sinthet*cosom;
+	p22 = cosom*costhet + sinthet*sinphi*sinom;
+	p23 = sinom*cosphi;
+	p31 = sinom*sinthet + cosom*sinphi*costhet;
+	p32 = cosom*sinphi*sinthet - costhet*sinom;
+	p33 = cosom * cosphi;
+
+	printf("%15.10f %15.10f %15.10f\n",p11,p12,p13);
+	printf("%15.10f %15.10f %15.10f\n",p21,p22,p23);
+	printf("%15.10f %15.10f %15.10f\n",p31,p32,p33);
+
+	check = p11*(p22*p33-p32*p23) - p12*(p21*p33-p31*p23) +
+		p13*(p21*p32-p22*p31);
+	printf("Check: %lf\n",check);  check determinant .. ok
+
+    END OF EXCISED CODE..... */
+
+	/* precess to 1950 */
+	precrot(ra,dec,epoch,1950.,&r1950,&d1950);
+	r1950 = r1950 / HRS_IN_RADIAN;
+	d1950 = d1950 / DEG_IN_RADIAN;
+
+	/* form direction cosines */
+	x0 = cos(r1950) * cos(d1950);
+	y0 = sin(r1950) * cos(d1950);
+	z0 = sin(d1950);
+
+	/* rotate 'em */
+	x1 = p11*x0 + p12*y0 + p13*z0;
+	y1 = p21*x0 + p22*y0 + p23*z0;
+	z1 = p31*x0 + p32*y0 + p33*z0;
+
+	/* translate to spherical polars for Galactic coords. */
+	*glong = atan_circ(x1,y1)*DEG_IN_RADIAN;
+	*glat = asin(z1)*DEG_IN_RADIAN;
+}
+
+void eclipt(ra,dec,epoch,jd,curep,eclong,eclat)
+
+           double ra,dec,epoch,jd,*curep,*eclong,*eclat;
+
+ /* ra in decimal hrs, other coords in dec. deg. */
+
+/* converts ra and dec to ecliptic coords -- precesses to current
+   epoch first (and hands current epoch back for printing.)  */
+
+{
+	double incl, T;
+	double racur, decur;
+	double x0,y0,z0,x1,y1,z1;
+
+	T = (jd - J2000)/36525.;  /* centuries since J2000 */
+	*curep = 2000. + (jd - J2000) / 365.25;
+
+	incl = (23.439291 + T * (-0.0130042 - 0.00000016 * T))/DEG_IN_RADIAN;
+		/* 1992 Astron Almanac, p. B18, dropping the
+		   cubic term, which is 2 milli-arcsec! */
+
+	precrot(ra,dec,epoch,*curep,&racur,&decur);
+	racur = racur / HRS_IN_RADIAN;
+	decur = decur / DEG_IN_RADIAN;
+
+	x0=cos(decur)*cos(racur);
+	y0=cos(decur)*sin(racur);
+	z0=sin(decur);
+
+	x1=x0;  /* doesn't change */
+	y1 = cos(incl)*y0 + sin(incl)*z0;
+	z1 = -1 * sin(incl)*y0 + cos(incl)*z0;
+	*eclong = atan_circ(x1,y1) * DEG_IN_RADIAN;
+	*eclat = asin(z1) * DEG_IN_RADIAN;
+}
+
+double parang(ha,dec,lat)
+
+	double ha,dec,lat;
+
+ /* decimal hours, degrees, and degrees. */
+
+{
+	/* finds the parallactic angle.  This is a little
+	   complicated (see Filippenko PASP 94, 715 (1982) */
+
+	double colat,codec,hacrit,sineta,denom;
+
+	ha = ha / HRS_IN_RADIAN;
+	dec = dec / DEG_IN_RADIAN;
+	lat = lat / DEG_IN_RADIAN;
+
+	/* Filippenko eqn 10 follows -- guarded against division by zero
+             at the exact zenith .... */
+	denom =
+	   sqrt(1.-pow((sin(lat)*sin(dec)+cos(lat)*cos(dec)*cos(ha)),2.));
+	if(denom != 0.)
+	    sineta = sin(ha)*cos(lat)/denom;
+	else sineta = 0.;
+
+	if(lat >= 0.) {
+		/* northern hemisphere case */
+
+		/* If you're south of zenith, no problem. */
+
+		if(dec<lat) return (asin(sineta)*DEG_IN_RADIAN);
+
+		else {
+			/* find critical hour angle -- where parallactic
+				angle becomes 90 deg.  After that,
+				take another root of expression. */
+			colat = PI /2. - lat;
+			codec = PI /2. - dec;
+			hacrit = 1.-pow(cos(colat),2.)/pow(cos(codec),2.);
+			hacrit = sqrt(hacrit)/sin(colat);
+			if(fabs(hacrit) <= 1.00) hacrit = asin(hacrit);
+			else oprntf("Error in parang..asin(>1)\n");
+			if(fabs(ha) > fabs(hacrit))
+				return(asin(sineta)*DEG_IN_RADIAN);
+				/* comes out ok at large hour angle */
+			else if (ha > 0)
+				return((PI - asin(sineta))*DEG_IN_RADIAN);
+			else return((-1.* PI - asin(sineta))*DEG_IN_RADIAN);
+		}
+	}
+	else {  /* Southern hemisphere case follows */
+		/* If you're north of zenith, no problem. */
+		if(dec>lat) {
+			if(ha >= 0)
+				return ((PI - asin(sineta))*DEG_IN_RADIAN);
+			else return(-1*(PI + asin(sineta)) * DEG_IN_RADIAN);
+		}
+		else {
+			/* find critical hour angle -- where parallactic
+				angle becomes 90 deg.  After that,
+				take another root of expression. */
+			colat = -1*PI/2. - lat;
+			codec = PI/2. - dec;
+			hacrit = 1.-pow(cos(colat),2.)/pow(cos(codec),2.);
+			hacrit = sqrt(hacrit)/sin(colat);
+			if(fabs(hacrit) <= 1.00) hacrit = asin(hacrit);
+			else oprntf("Error in parang..asin(>1)\n");
+			if(fabs(ha) > fabs(hacrit)) {
+				if(ha >= 0)
+				    return((PI - asin(sineta))*DEG_IN_RADIAN);
+				else return(-1. * (PI + asin(sineta))*DEG_IN_RADIAN);
+			}
+			else return(asin(sineta)*DEG_IN_RADIAN);
+		}
+	}
+}
+
+/* Planetary part, added 1992 August.  The intention of this is
+   to compute low-precision planetary positions for general info
+   and to inform user if observation might be interfered with by
+   a planet -- a rarity, but it happens.  Also designed to make
+   handy low-precision planet positions available for casual
+   planning purposes.  Do not try to point blindly right at the
+   middle of a planetary disk with these routines!  */
+
+double jd_el;     /* ************** */
+
+/* elements of planetary orbits */
+struct elements {
+	char name[9];
+	double incl;
+	double Omega;
+	double omega;
+	double a;
+	double daily;
+	double ecc;
+	double L_0;
+	double mass;
+};
+
+struct elements el[10];
+
+void comp_el(jd)
+
+	double jd;
+{
+
+   double T, Tsq, Tcb, d;
+   double ups, P, Q, S, V, W, G, H, zeta, psi; /* Meeus p. 110 ff. */
+   double sinQ,sinZeta,cosQ,cosZeta,sinV,cosV,
+	sin2Zeta,cos2Zeta;
+
+   jd_el = jd;   /* true, but not necessarily; set explicitly */
+   d = jd - 2415020.;
+   T = d / 36525.;
+   Tsq = T * T;
+   Tcb = Tsq * T;
+
+/* computes and loads mean elements for planets.  */
+
+/* Mercury, Venus, and Mars from Explanatory Suppl., p. 113 */
+
+   strcpy(el[1].name,"Mercury");
+   el[1].incl = 7.002880 + 1.8608e-3 * T - 1.83e-5 * Tsq;
+   el[1].Omega = 47.14594 + 1.185208 * T + 1.74e-4 * Tsq;
+   el[1].omega = 75.899697 + 1.55549 * T + 2.95e-4 * Tsq;
+   el[1].a = .3870986;
+   el[1].daily = 4.0923388;
+   el[1].ecc = 0.20561421 + 0.00002046 * T;
+   el[1].L_0 = 178.179078 + 4.0923770233 * d  +
+	 0.0000226 * pow((3.6525 * T),2.);
+
+   strcpy(el[2].name,"Venus  ");
+   el[2].incl = 3.39363 + 1.00583e-03 * T - 9.722e-7 * Tsq;
+   el[2].Omega = 75.7796472 + 0.89985 * T + 4.1e-4 * Tsq;
+   el[2].omega = 130.16383 + 1.4080 * T + 9.764e-4 * Tsq;
+   el[2].a = .723325;
+   el[2].daily = 1.60213049;
+   el[2].ecc = 0.00682069 - 0.00004774 * T;
+   el[2].L_0 = 342.767053 + 1.6021687039 * 36525 * T +
+	 0.000023212 * pow((3.6525 * T),2.);
+
+/* Earth from old Nautical Almanac .... */
+
+   strcpy(el[5].name,"Earth  ");
+   el[3].ecc = 0.01675104 - 0.00004180*T + 0.000000126*Tsq;
+   el[3].incl = 0.0;
+   el[3].Omega = 0.0;
+   el[3].omega = 101.22083 + 0.0000470684*d + 0.000453*Tsq + 0.000003*Tcb;
+   el[3].a = 1.0000007;;
+   el[3].daily = 0.985599;
+   el[3].L_0 = 358.47583 + 0.9856002670*d - 0.000150*Tsq - 0.000003*Tcb +
+	    el[3].omega;
+
+   strcpy(el[4].name,"Mars   ");
+   el[4].incl = 1.85033 - 6.75e-04 * T - 1.833e-5 * Tsq;
+   el[4].Omega = 48.786442 + .770992 * T + 1.39e-6 * Tsq;
+   el[4].omega = 334.218203 + 1.840758 * T + 1.299e-4 * Tsq;
+   el[4].a = 1.5236915;
+   el[4].daily = 0.5240329502 + 1.285e-9 * T;
+   el[4].ecc = 0.09331290 - 0.000092064 * T - 0.000000077 * Tsq;
+   el[4].L_0 = 293.747628 + 0.5240711638 * d  +
+	 0.000023287 * pow((3.6525 * T),2.);
+
+/* Outer planets from Jean Meeus, Astronomical Formulae for
+   Calculators, 3rd edition, Willman-Bell; p. 100. */
+
+   strcpy(el[5].name,"Jupiter");
+   el[5].incl = 1.308736 - 0.0056961 * T + 0.0000039 * Tsq;
+   el[5].Omega = 99.443414 + 1.0105300 * T + 0.0003522 * Tsq
+		- 0.00000851 * Tcb;
+   el[5].omega = 12.720972 + 1.6099617 * T + 1.05627e-3 * Tsq
+	- 3.43e-6 * Tcb;
+   el[5].a = 5.202561;
+   el[5].daily = 0.08312941782;
+   el[5].ecc = .04833475  + 1.64180e-4 * T - 4.676e-7*Tsq -
+	1.7e-9 * Tcb;
+   el[5].L_0 = 238.049257 + 3036.301986 * T + 0.0003347 * Tsq -
+	1.65e-6 * Tcb;
+
+   /* The outer planets have such large mutual interactions that
+      even fair accuracy requires lots of perturbations --- here
+      are some of the larger ones, from Meeus' book. */
+
+   ups = 0.2*T + 0.1;
+   P = (237.47555 + 3034.9061 * T) / DEG_IN_RADIAN;
+   Q = (265.91650 + 1222.1139 * T) / DEG_IN_RADIAN;
+   S = (243.51721 + 428.4677 * T) / DEG_IN_RADIAN;
+   V = 5*Q - 2*P;
+   W = 2*P - 6*Q + 3*S;
+   zeta = Q - P;
+   psi = S - Q;
+   sinQ = sin(Q);
+   cosQ = cos(Q);
+   sinV = sin(V);
+   cosV = cos(V);
+   sinZeta = sin(zeta);
+   cosZeta = cos(zeta);
+   sin2Zeta = sin(2*zeta);
+   cos2Zeta = cos(2*zeta);
+
+   el[5].L_0 = el[5].L_0
+	+ (0.331364 - 0.010281*ups - 0.004692*ups*ups)*sinV
+	+ (0.003228 - 0.064436*ups + 0.002075*ups*ups)*cosV
+	- (0.003083 + 0.000275*ups - 0.000489*ups*ups)*sin(2*V)
+	+ 0.002472 * sin(W) + 0.013619 * sinZeta + 0.018472 * sin2Zeta
+	+ 0.006717 * sin(3*zeta)
+	+ (0.007275  - 0.001253*ups) * sinZeta * sinQ
+	+ 0.006417 * sin2Zeta * sinQ
+	- (0.033839 + 0.001253 * ups) * cosZeta * sinQ
+	- (0.035681 + 0.001208 * ups) * sinZeta * sinQ;
+	/* only part of the terms, the ones first on the list and
+	   selected larger-amplitude terms from farther down. */
+
+   el[5].ecc = el[5].ecc + 1e-7 * (
+	  (3606 + 130 * ups - 43 * ups*ups) * sinV
+	+ (1289 - 580 * ups) * cosV - 6764 * sinZeta * sinQ
+	- 1110 * sin2Zeta * sin(Q)
+	+ (1284 + 116 * ups) * cosZeta * sinQ
+	+ (1460 + 130 * ups) * sinZeta * cosQ
+	+ 6074 * cosZeta * cosQ);
+
+   el[5].omega = el[5].omega
+	+ (0.007192 - 0.003147 * ups) * sinV
+	+ ( 0.000197*ups*ups - 0.00675*ups - 0.020428) * cosV
+	+ 0.034036 * cosZeta * sinQ + 0.037761 * sinZeta * cosQ;
+
+   el[5].a = el[5].a + 1.0e-6 * (
+	205 * cosZeta - 263 * cosV + 693 * cos2Zeta + 312 * sin(3*zeta)
+	+ 147 * cos(4*zeta) + 299 * sinZeta * sinQ
+	+ 181 * cos2Zeta * sinQ + 181 * cos2Zeta * sinQ
+	+ 204 * sin2Zeta * cosQ + 111 * sin(3*zeta) * cosQ
+	- 337 * cosZeta * cosQ - 111 * cos2Zeta * cosQ
+	);
+
+   strcpy(el[6].name,"Saturn ");
+   el[6].incl = 2.492519 - 0.00034550*T - 7.28e-7*Tsq;
+   el[6].Omega = 112.790414 + 0.8731951*T - 0.00015218*Tsq - 5.31e-6*Tcb ;
+   el[6].omega = 91.098214 + 1.9584158*T + 8.2636e-4*Tsq;
+   el[6].a = 9.554747;
+   el[6].daily = 0.0334978749897;
+   el[6].ecc = 0.05589232 - 3.4550e-4 * T - 7.28e-7*Tsq;
+   el[6].L_0 = 266.564377 + 1223.509884*T + 0.0003245*Tsq - 5.8e-6*Tcb
+	+ (0.018150*ups - 0.814181 + 0.016714 * ups*ups) * sinV
+	+ (0.160906*ups - 0.010497 - 0.004100 * ups*ups) * cosV
+	+ 0.007581 * sin(2*V) - 0.007986 * sin(W)
+	- 0.148811 * sinZeta - 0.040786*sin2Zeta
+	- 0.015208 * sin(3*zeta) - 0.006339 * sin(4*zeta)
+	- 0.006244 * sinQ
+	+ (0.008931 + 0.002728 * ups) * sinZeta * sinQ
+	- 0.016500 * sin2Zeta * sinQ
+	- 0.005775 * sin(3*zeta) * sinQ
+	+ (0.081344 + 0.003206 * ups) * cosZeta * sinQ
+	+ 0.015019 * cos2Zeta * sinQ
+	+ (0.085581 + 0.002494 * ups) * sinZeta * cosQ
+	+ (0.025328 - 0.003117 * ups) * cosZeta * cosQ
+	+ 0.014394 * cos2Zeta * cosQ;   /* truncated here -- no
+		      terms larger than 0.01 degrees, but errors may
+		      accumulate beyond this.... */
+   el[6].ecc = el[6].ecc + 1.0e-7 * (
+	  (2458 * ups - 7927.) * sinV + (13381. + 1226. * ups) * cosV
+	+ 12415. * sinQ + 26599. * cosZeta * sinQ
+	- 4687. * cos2Zeta * sinQ - 12696. * sinZeta * cosQ
+	- 4200. * sin2Zeta * cosQ +(2211. - 286*ups) * sinZeta*sin(2*Q)
+	- 2208. * sin2Zeta * sin(2*Q)
+	- 2780. * cosZeta * sin(2*Q) + 2022. * cos2Zeta*sin(2*Q)
+	- 2842. * sinZeta * cos(2*Q) - 1594. * cosZeta * cos(2*Q)
+	+ 2162. * cos2Zeta*cos(2*Q) );  /* terms with amplitudes
+	    > 2000e-7;  some secular variation ignored. */
+   el[6].omega = el[6].omega
+	+ (0.077108 + 0.007186 * ups - 0.001533 * ups*ups) * sinV
+	+ (0.045803 - 0.014766 * ups - 0.000536 * ups*ups) * cosV
+	- 0.075825 * sinZeta * sinQ - 0.024839 * sin2Zeta*sinQ
+	- 0.072582 * cosQ - 0.150383 * cosZeta * cosQ +
+	0.026897 * cos2Zeta * cosQ;  /* all terms with amplitudes
+	    greater than 0.02 degrees -- lots of others! */
+   el[6].a = el[6].a + 1.0e-6 * (
+	2933. * cosV + 33629. * cosZeta - 3081. * cos2Zeta
+	- 1423. * cos(3*zeta) + 1098. * sinQ - 2812. * sinZeta * sinQ
+	+ 2138. * cosZeta * sinQ  + 2206. * sinZeta * cosQ
+	- 1590. * sin2Zeta*cosQ + 2885. * cosZeta * cosQ
+	+ 2172. * cos2Zeta * cosQ);  /* terms with amplitudes greater
+	   than 1000 x 1e-6 */
+
+   strcpy(el[7].name,"Uranus ");
+   el[7].incl = 0.772464 + 0.0006253*T + 0.0000395*Tsq;
+   el[7].Omega = 73.477111 + 0.4986678*T + 0.0013117*Tsq;
+   el[7].omega = 171.548692 + 1.4844328*T + 2.37e-4*Tsq - 6.1e-7*Tcb;
+   el[7].a = 19.21814;
+   el[7].daily = 1.1769022484e-2;
+   el[7].ecc = 0.0463444 - 2.658e-5 * T;
+   el[7].L_0 = 244.197470 + 429.863546*T + 0.000316*Tsq - 6e-7*Tcb;
+   /* stick in a little bit of perturbation -- this one really gets
+      yanked around.... after Meeus p. 116*/
+   G = (83.76922 + 218.4901 * T)/DEG_IN_RADIAN;
+   H = 2*G - S;
+   el[7].L_0 = el[7].L_0 + (0.864319 - 0.001583 * ups) * sin(H)
+	+ (0.082222 - 0.006833 * ups) * cos(H)
+	+ 0.036017 * sin(2*H);
+   el[7].omega = el[7].omega + 0.120303 * sin(H)
+	+ (0.019472 - 0.000947 * ups) * cos(H)
+	+ 0.006197 * sin(2*H);
+   el[7].ecc = el[7].ecc + 1.0e-7 * (
+	20981. * cos(H) - 3349. * sin(H) + 1311. * cos(2*H));
+   el[7].a = el[7].a - 0.003825 * cos(H);
+
+   /* other corrections to "true longitude" are ignored. */
+
+   strcpy(el[8].name,"Neptune");
+   el[8].incl = 1.779242 - 9.5436e-3 * T - 9.1e-6*Tsq;
+   el[8].Omega = 130.681389 + 1.0989350 * T + 2.4987e-4*Tsq - 4.718e-6*Tcb;
+   el[8].omega = 46.727364 + 1.4245744*T + 3.9082e-3*Tsq - 6.05e-7*Tcb;
+   el[8].a = 30.10957;
+   el[8].daily = 6.020148227e-3;
+   el[8].ecc = 0.00899704 + 6.33e-6 * T;
+   el[8].L_0 = 84.457994 + 219.885914*T + 0.0003205*Tsq - 6e-7*Tcb;
+   el[8].L_0 = el[8].L_0
+	- (0.589833 - 0.001089 * ups) * sin(H)
+	- (0.056094 - 0.004658 * ups) * cos(H)
+	- 0.024286 * sin(2*H);
+   el[8].omega = el[8].omega + 0.024039 * sin(H)
+	- 0.025303 * cos(H);
+   el[8].ecc = el[8].ecc + 1.0e-7 * (
+	4389. * sin(H) + 1129. * sin(2.*H)
+	+ 4262. * cos(H) + 1089. * cos(2.*H));
+   el[8].a = el[8].a + 8.189e-3 * cos(H);
+
+/* crummy -- osculating elements a la Sept 15 1992 */
+
+   d = jd - 2448880.5;  /* 1992 Sep 15 */
+   T = d / 36525.;
+   strcpy(el[9].name,"Pluto  ");
+   el[9].incl = 17.1426;
+   el[9].Omega = 110.180;
+   el[9].omega = 223.782;
+   el[9].a = 39.7465;
+   el[9].daily = 0.00393329;
+   el[9].ecc = 0.253834;
+   el[9].L_0 = 228.1027 + 0.00393329 * d;
+/*   printf("inc Om om : %f %f %f\n",el[9].incl,el[9].Omega,el[9].omega);
+   printf("a  dail ecc: %f %f %f\n",el[9].a,el[9].daily,el[9].ecc);
+   printf("L_0 %f\n",el[9].L_0);
+*/
+   el[1].mass = 1.660137e-7;  /* in units of sun's mass, IAU 1976 */
+   el[2].mass = 2.447840e-6;  /* from 1992 *Astron Almanac*, p. K7 */
+   el[3].mass = 3.040433e-6;  /* earth + moon */
+   el[4].mass = 3.227149e-7;
+   el[5].mass = 9.547907e-4;
+   el[6].mass = 2.858776e-4;
+   el[7].mass = 4.355401e-5;
+   el[8].mass = 5.177591e-5;
+   el[9].mass = 7.69e-9;  /* Pluto+Charon -- ? */
+
+}
+
+void planetxyz(p, jd, x, y, z)
+
+	int p;
+	double jd, *x, *y, *z;
+
+/* produces ecliptic x,y,z coordinates for planet number 'p'
+   at date jd. */
+
+{
+	double M, omnotil, nu, r;
+	double e, LL, Om, om, nuu, ii;
+
+/* see 1992 Astronomical Almanac, p. E 4 for these formulae. */
+
+	ii = el[p].incl/DEG_IN_RADIAN;
+	e = el[p].ecc;
+
+	LL = (el[p].daily * (jd - jd_el) + el[p].L_0) / DEG_IN_RADIAN;
+	Om = el[p].Omega / DEG_IN_RADIAN;
+	om = el[p].omega / DEG_IN_RADIAN;
+
+	M = LL - om;
+	omnotil = om - Om;
+	nu = M + (2.*e - 0.25 * pow(e,3.)) * sin(M) +
+	     1.25 * e * e * sin(2 * M) +
+	     1.08333333 * pow(e,3.) * sin(3 * M);
+	r = el[p].a * (1. - e*e) / (1 + e * cos(nu));
+
+	*x = r *
+	     (cos(nu + omnotil) * cos(Om) - sin(nu +  omnotil) *
+		cos(ii) * sin(Om));
+	*y = r *
+	     (cos(nu +  omnotil) * sin(Om) + sin(nu +  omnotil) *
+		cos(ii) * cos(Om));
+	*z = r * sin(nu +  omnotil) * sin(ii);
+}
+
+
+void planetvel(p, jd, vx, vy, vz)
+	int p;
+	double jd, *vx, *vy, *vz;
+{
+/* numerically evaluates planet velocity by brute-force
+numerical differentiation. Very unsophisticated algorithm. */
+
+	double dt; /* timestep */
+	double x1,y1,z1,x2,y2,z2,r1,d1,r2,d2,ep1;
+
+	dt = 0.1 / el[p].daily; /* time for mean motion of 0.1 degree */
+	planetxyz(p, (jd - dt), &x1, &y1, &z1);
+	planetxyz(p, (jd + dt), &x2, &y2, &z2);
+	*vx = 0.5 * (x2 - x1) / dt;
+	*vy = 0.5 * (y2 - y1) / dt;
+	*vz = 0.5 * (z2 - z1) / dt;
+	/* answer should be in ecliptic coordinates, in AU per day.*/
+}
+
+void xyz2000(jd,x,y,z)
+	double jd, x, y, z;
+
+/* simply transforms a vector x, y, and z to 2000 coordinates
+   and prints -- for use in diagnostics. */
+{
+	double r1, d1, ep1, r2, d2, mod;
+
+	mod = sqrt(x*x + y*y + z*z);
+	xyz_cel(x,y,z,&r1,&d1);
+	ep1 = 2000. + (jd - J2000)/365.25;
+	precrot(r1,d1,ep1,2000.,&r2,&d2);
+	x = mod * cos(r2/HRS_IN_RADIAN) * cos(d2/DEG_IN_RADIAN);
+	y = mod * sin(r2/HRS_IN_RADIAN) * cos(d2/DEG_IN_RADIAN);
+	z = mod * sin(d2/DEG_IN_RADIAN);
+	printf("%f to 2000 -->  %f %f %f \n",ep1,x,y,z);
+
+}
+
+void earthview(x, y, z, i, ra, dec)
+
+	double *x, *y, *z;
+	int i;
+	double *ra, *dec;
+
+/* given computed planet positions for planets 1-10, computes
+   ra and dec of i-th planet as viewed from earth (3rd) */
+
+{
+	double dx, dy, dz;
+
+	dx = x[i] - x[3];
+	dy = y[i] - y[3];
+	dz = z[i] - z[3];
+
+
+	xyz_cel(dx,dy,dz,ra,dec);
+
+}
+
+void pposns(jd,lat,sid,print_option,planra,plandec)
+
+	double jd,lat,sid;
+	short print_option;
+	double *planra, *plandec;
+
+/* computes and optionally prints positions for all the planets. */
+/*  print_option 1 = print positions, 0 = silent */
+
+{
+	int i;
+	double x[10], y[10], z[10], ha, alt, az, secz;
+	double rasun, decsun, distsun, topora,topodec;
+	double georamoon,geodecmoon,geodistmoon,toporamoon,topodecmoon,
+	      topodistmoon;
+
+	accusun(jd,0.,0.,&rasun,&decsun,&distsun,&topora,&topodec,x+3,y+3,z+3);
+/*      planetxyz(3,jd,x+3,y+3,z+3);   get the earth first (EarthFirst!?)
+	eclrot(jd,x+3,y+3,z+3);  */
+
+	accumoon(jd,lat,sid,0.,&georamoon,&geodecmoon,&geodistmoon,
+			 &toporamoon,&topodecmoon,&topodistmoon);
+
+	if(print_option == 1) {  /* set up table header */
+		oprntf("\n\nPlanetary positions (epoch of date), accuracy about 0.1 deg:\n");
+		oprntf("\n             RA       dec       HA");
+		oprntf("       sec.z     alt   az\n\n");
+
+	/* Throw in the sun and moon here ... */
+		oprntf("Sun    : ");
+		put_coords(topora,1);
+		oprntf("  ");
+		put_coords(topodec,0);
+		ha = adj_time(sid - topora);
+		oprntf("  ");
+		put_coords(ha,0);
+		alt = altit(topodec,ha,lat,&az);
+		secz = secant_z(alt);
+ 		if(fabs(secz) < 100.) oprntf("   %8.2f  ",secz);
+		else oprntf("  (near horiz)");
+		oprntf(" %5.1f  %5.1f\n",alt,az);
+		oprntf("Moon   : ",el[i].name);
+		put_coords(toporamoon,1);
+		oprntf("  ");
+		put_coords(topodecmoon,0);
+		ha = adj_time(sid - toporamoon);
+		oprntf("  ");
+		put_coords(ha,0);
+		alt=altit(topodecmoon,ha,lat,&az);
+		secz = secant_z(alt);
+ 		if(fabs(secz) < 100.) oprntf("   %8.2f  ",secz);
+		else oprntf("  (near horiz)");
+		oprntf(" %5.1f  %5.1f\n\n",alt,az);
+        }
+	for(i = 1; i <= 9; i++) {
+		if(i == 3) goto SKIP;  /* skip the earth */
+		planetxyz(i,jd,x+i,y+i,z+i);
+		eclrot(jd,x+i,y+i,z+i);
+		earthview(x,y,z,i,planra+i,plandec+i);
+		if(print_option == 1) {
+			oprntf("%s: ",el[i].name);
+			put_coords(planra[i],1);
+			oprntf("  ");
+			put_coords(plandec[i],0);
+			ha = adj_time(sid - planra[i]);
+			oprntf("  ");
+			put_coords(ha,0);
+			alt=altit(plandec[i],ha,lat,&az);
+			secz = secant_z(alt);
+ 			if(fabs(secz) < 100.) oprntf("   %8.2f  ",secz);
+			else oprntf("  (near horiz)");
+			oprntf(" %5.1f  %5.1f",alt,az);
+			if(i == 9) oprntf(" <-(least accurate)\n");
+			else oprntf("\n");
+		}
+		SKIP: ;
+	}
+	if(print_option == 1) printf("Type command, or ? for menu:");
+}
+
+void barycor(jd,x,y,z,xdot,ydot,zdot)
+
+	double jd,*x,*y,*z;
+	double *xdot,*ydot,*zdot;
+
+/* This routine takes the position
+   x,y,z and velocity xdot,ydot,zdot, assumed heliocentric,
+   and corrects them to the solar system barycenter taking into
+   account the nine major planets.  Routine evolved by inserting
+   planetary data (given above) into an earlier, very crude
+   barycentric correction.  */
+
+{
+
+	int p;
+	double xp, yp, zp, xvp, yvp, zvp;
+	double xo, yo, zo;  /* for diagn */
+
+	double xc=0.,yc=0.,zc=0.,xvc=0.,yvc=0.,zvc=0.;
+
+	comp_el(jd);
+
+	for(p=1;p<=9;p++) { /* sum contributions of the planets */
+		planetxyz(p,jd,&xp,&yp,&zp);
+		xc = xc + el[p].mass * xp;  /* mass is fraction of solar mass */
+		yc = yc + el[p].mass * yp;
+		zc = zc + el[p].mass * zc;
+		planetvel(p,jd,&xvp,&yvp,&zvp);
+		xvc = xvc + el[p].mass * xvp;
+		yvc = yvc + el[p].mass * yvp;
+		zvc = zvc + el[p].mass * zvc;
+	/* diagnostic commented out ..... nice place to check planets if needed
+		printf("%d :",p);
+		xo = xp;
+		yo = yp;
+		zo = zp;
+		eclrot(jd,&xo,&yo,&zo);
+		xyz2000(jd,xo,yo,zo);
+		printf("    ");
+		xo = xvp;
+		yo = yvp;
+		zo = zvp;
+		eclrot(jd,&xo,&yo,&zo);
+		xyz2000(jd,xo,yo,zo);    */
+	}
+	/* normalize properly and rotate corrections to equatorial coords */
+	xc = xc / SS_MASS;
+	yc = yc / SS_MASS;
+	zc = zc / SS_MASS;     /* might as well do it right ... */
+
+	eclrot(jd, &xc, &yc, &zc);
+
+/*      printf("posn corrn:");   diagnostic commented out
+	xyz2000(jd,xc,yc,zc);
+	printf(" vel corrn:");
+	xyz2000(jd,(1.0e9 * xvc),(1.0e9 * yvc),(1.0e9 * zvc));  */
+
+	xvc = xvc * KMS_AUDAY / SS_MASS;
+	yvc = yvc * KMS_AUDAY / SS_MASS;
+	zvc = zvc * KMS_AUDAY / SS_MASS;
+	eclrot(jd, &xvc, &yvc, &zvc);
+
+	/* add them in */
+	*x = *x - xc;  /* these are in AU -- */
+	*y = *y - yc;
+	*z = *z - zc;
+/*      xyz2000(jd,*x,*y,*z);   */
+	*xdot = *xdot - xvc;
+	*ydot = *ydot - yvc;
+	*zdot = *zdot - zvc;
+
+/* diagnostic -- trash variables
+	-- scale for direct comparison with almanac
+	xp = 1.0e9 * *xdot / KMS_AUDAY;
+	yp = 1.0e9 * *ydot / KMS_AUDAY;
+	zp = 1.0e9 * *zdot / KMS_AUDAY;
+	xyz2000(jd,xp,yp,zp);
+					*/
+}
+
+void helcor(jd,ra,dec,ha,lat,elevsea,tcor,vcor)
+
+	double jd,ra,dec,ha;
+  	double lat,elevsea,*tcor,*vcor;
+
+/* finds heliocentric correction for given jd, ra, dec, ha, and lat.
+   tcor is time correction in seconds, vcor velocity in km/s, to
+   be added to the observed values.
+   Input ra and dec assumed to be at current epoch */
+
+{
+	double M, ec, E, obliq, omeg, x, y, z, xdot, ydot, zdot;
+	double xobj,yobj,zobj;
+	double ras, decs, dists, jd1, jd2, x1, x2, y1, y2, z1, z2;
+	double topora, topodec;
+	double x_geo, y_geo, z_geo;  /* geocentric coords of observatory */
+	double diff;
+	double a=499.0047837;  /* light travel time for 1 AU, sec  */
+
+	dec=dec/DEG_IN_RADIAN; /* pass by value! */
+	ra = ra/HRS_IN_RADIAN;
+	ha = ha/HRS_IN_RADIAN;
+
+	xobj = cos(ra) * cos(dec);
+	yobj = sin(ra) * cos(dec);
+	zobj = sin(dec);
+
+/* diagnostic -- temporarily trash jd1
+	jd1 = jd1 + etcorr(jd1) / SEC_IN_DAY;
+	printf("TDT: %20f\n",jd1);  */
+
+	jd1 = jd - EARTH_DIFF;
+	jd2 = jd + EARTH_DIFF;
+
+	accusun(jd1,0.,0.,&ras,&decs,&dists,&topora,&topodec,&x1,&y1,&z1);
+	accusun(jd2,0.,0.,&ras,&decs,&dists,&topora,&topodec,&x2,&y2,&z2);
+	accusun(jd,0.,0.,&ras,&decs,&dists,&topora,&topodec,&x,&y,&z);
+
+/*      printf("ra dec distance:");  diagnostic -- commented out
+	put_coords(ras,3);
+	printf(" ");
+	put_coords(decs,2);
+	printf(" %f\n",dists);   */
+
+	xdot = KMS_AUDAY*(x2 - x1)/(2.*EARTH_DIFF);  /* numerical differentiation */
+	ydot = KMS_AUDAY*(y2 - y1)/(2.*EARTH_DIFF);  /* crude but accurate */
+	zdot = KMS_AUDAY*(z2 - z1)/(2.*EARTH_DIFF);
+
+/*      printf("Helio earth:");  diagnostic -- commmented out
+	xyz2000(jd,x,y,z);
+	xyz2000(jd,xdot,ydot,zdot);   */
+
+	barycor(jd,&x,&y,&z,&xdot,&ydot,&zdot);
+	*tcor = a * (x*xobj + y*yobj + z*zobj);
+	*vcor = xdot * xobj + ydot * yobj + zdot * zobj;
+/* correct diurnal rotation for elliptical earth including obs. elevation */
+	geocent(0., lat, elevsea, &x_geo, &y_geo, &z_geo);
+/* longitude set to zero arbitrarily so that x_geo = perp. distance to axis */
+	*vcor = *vcor - 0.4651011 * x_geo * sin(ha) * cos(dec);
+/* 0.4651011 = 6378.137 km radius * 2 pi / (86164.1 sec per sidereal day) */
+/* could add time-of-flight across earth's radius here -- but rest of
+   theory is not good to 0.02 seconds anyway. */
+}
+
+/* A couple of eclipse predictors.... */
+
+float overlap(r1, r2, sepn)
+
+	double r1, r2, sepn;
+{
+
+
+/* for two circles of radii r1 and r2,
+   computes the overlap area in terms of the area of r1
+   if their centers are separated by
+   sepn. */
+
+	float result, a, rlarge, rsmall;
+
+	if(sepn > r1+r2) return(0.); /* They don't overlap */
+	if((r2 < 0.) || (r1 < 0.)) return(-1.);  /* oops -- r's > 0.*/
+	if(r2 > r1) {
+		rlarge = r2;
+		rsmall = r1;
+	}
+	else {
+		rlarge = r1;
+		rsmall = r2;
+	}
+	if(sepn < (rlarge - rsmall)) { /* small circle completely contained */
+		if(r2 > r1) return(1.);  /* r1 is completely covered */
+		else return((r2*r2) / (r1*r1)); /* ratio of areas */
+	}
+	a = (rlarge*rlarge + sepn*sepn - rsmall*rsmall) / (2.*sepn);
+	   /* a is the distance along axis of centers to where circles cross */
+	result = PI * rlarge * rlarge / 4.
+	     - a * sqrt(rlarge * rlarge - a*a) / 2.
+	     - rlarge * rlarge * asin(a/rlarge) / 2;
+	a = sepn - a;
+	result = result + PI * rsmall * rsmall / 4.
+	     - a * sqrt(rsmall * rsmall - a*a) / 2.
+	     - rsmall * rsmall * asin(a/rsmall) / 2.;
+	result = result * 2.;
+	return(result / (PI*r1*r1)); /* normalize to circle 1's area. */
+}
+
+void solecl(sun_moon,distmoon,distsun)
+
+	double sun_moon,distmoon,distsun;
+
+
+{
+	double ang_sun, ang_moon; /* angular sizes */
+	float magnitude; /* fraction of sun covered */
+
+	ang_moon = DEG_IN_RADIAN * asin(RMOON / (distmoon * EQUAT_RAD));
+	ang_sun = DEG_IN_RADIAN * asin(RSUN / (distsun * ASTRO_UNIT));
+
+	if(sun_moon >= (ang_sun + ang_moon)) return ;
+
+	else if(ang_sun >= ang_moon) {                   /* annular */
+		magnitude = overlap(ang_sun,ang_moon,sun_moon);
+		if(sun_moon > (ang_sun - ang_moon))
+			oprntf("PARTIAL ECLIPSE OF THE SUN, %4.2f covered.\n",
+				magnitude);
+		else oprntf("ANNULAR ECLIPSE OF THE SUN, %4.2f covered.\n",
+				magnitude);
+	}
+	else {
+		magnitude = overlap(ang_sun,ang_moon,sun_moon);
+		if(sun_moon > (ang_moon - ang_sun))
+			oprntf("PARTIAL ECLIPSE OF THE SUN, %4.2f covered.\n",
+				magnitude);
+		else oprntf("TOTAL ECLIPSE OF THE SUN!\n");
+	}
+}
+
+short lunecl(georamoon,geodecmoon,geodistmoon,rasun,decsun,distsun)
+
+	double georamoon,geodecmoon,geodistmoon,rasun,decsun,distsun;
+
+
+{
+	/* quickie lunar eclipse predictor -- makes a number of
+	   minor assumptions, e. g. small angle approximations, plus
+	   projects phenomena onto a plane at distance = geocentric
+	   distance of moon . */
+
+	double ang_sun, ang_moon; /* angular sizes */
+	double radius_um, radius_penum;
+	double ra_shadow, dec_shadow;
+	double lun_to_shadow;  /* angular separation of centerline of shadow
+		  from center of moon ... */
+	float magnitude;  /* portion covered */
+
+	ang_sun = asin(RSUN / (distsun * ASTRO_UNIT));  /* radians */
+
+	ra_shadow = rasun + 12.;
+	if(ra_shadow > 24.) ra_shadow = ra_shadow - 24.; /* not strictly
+	   necessary, perhaps, but wise */
+	dec_shadow = -1. * decsun;
+	radius_um = (1./geodistmoon - ang_sun) * DEG_IN_RADIAN;
+	radius_penum = (1./geodistmoon + ang_sun) * DEG_IN_RADIAN;
+
+
+	ang_moon = DEG_IN_RADIAN * asin(RMOON / (geodistmoon * EQUAT_RAD));
+	lun_to_shadow = DEG_IN_RADIAN *
+		subtend(georamoon,geodecmoon,ra_shadow,dec_shadow);
+	if(lun_to_shadow > (radius_penum + ang_moon)) return (0);
+	else if(lun_to_shadow >= (radius_um + ang_moon))  {
+		if(lun_to_shadow >= (radius_penum - ang_moon)) {
+			oprntf("PARTIAL PENUMBRAL (BRIGHT) ECLIPSE OF THE MOON.\n");
+			return (1);
+		}
+		else  {
+			oprntf("PENUMBRAL (BRIGHT) ECLIPSE OF THE MOON.\n");
+			return (2);
+		}
+	}
+	else if (lun_to_shadow >= (radius_um - ang_moon)) {
+		magnitude = overlap(ang_moon,radius_um,lun_to_shadow);
+		oprntf("PARTIAL UMBRAL (DARK) ECLIPSE OF THE MOON, %4.2f covered.\n",
+		   magnitude);
+		return(3);
+	}
+	else {
+		oprntf("TOTAL ECLIPSE OF THE MOON!\n");
+		return(4);
+	}
+}
+
+void planet_alert(jd,ra,dec,tolerance)
+
+	double jd,ra,dec,tolerance;
+
+/* given a jd, ra, and dec, this computes rough positions
+   for all the planets, and alerts the user if any of them
+   are within less than a settable tolerance of the ra and dec. */
+
+{
+	double pra[10],pdec[10], angle;
+	int i;
+
+	comp_el(jd);
+	pposns(jd,0.,0.,0,pra,pdec);
+	for(i = 1; i<=9 ; i++) {
+		if(i == 3) goto SKIP;
+		angle = subtend(pra[i],pdec[i],ra,dec) * DEG_IN_RADIAN;
+		if(angle < tolerance) {
+			oprntf("-- CAUTION -- proximity to %s -- low-precision calculation shows\n ",
+				el[i].name);
+			oprntf("this direction as %5.2f deg away from %s ---\n",
+				angle,el[i].name);
+		}
+		SKIP: ;
+	}
+}
+
+short setup_time_place(date,longit,lat,stdz,use_dst,zone_name,
+        zabr,site_name,enter_ut,night_date,jdut,jdlocal,jdb,jde,sid,
+	curepoch)
+
+struct date_time date;
+double lat, longit, stdz, *jdut, *jdlocal, *jdb, *jde, *sid, *curepoch;
+short use_dst, enter_ut, night_date;
+char zabr;
+char *site_name;
+char *zone_name;
+
+/* This takes the date (which contains the time), and the site parameters,
+   and prints out a banner giving the various dates and times; also
+   computes and returns various jd's, the sidereal time, and the epoch.
+   Returns negative number to signal error if date is out of range of
+   validity of algorithms, or if you specify a bad time during daylight-time
+   change; returns zero if successful.  */
+
+{
+
+	double jd, jdloc, jdtest;
+	if((date.y <= 1900) | (date.y >= 2100)) {
+		oprntf("Date out of range - 1901 -> 2099\n");
+		return(-1);
+	}
+	find_dst_bounds(date.y,stdz,use_dst,jdb,jde);
+	oprntf("\nW Long (hms):");
+	put_coords(longit,3);
+	oprntf(", lat (dms):");
+	put_coords(lat,2);
+	oprntf(", std time zone %3.0f hr W\n",stdz);
+	oprntf("\n");
+
+        /* Establish the correct time from the input values ...
+             This is done more compactly in the true_jd function, but
+             the present code prints more warnings, etc, so leave it.*/
+
+	if(enter_ut==0) {
+		/* "night date" convention -- adjust date up by one if local
+		      time is in morning. */
+		if((night_date == 1) && (date.h < 12))
+		      date.d = date.d + 1;  /* pass-by-value, I hope */
+		jdloc = date_to_jd(date); /* local */
+		jdtest = jdloc + stdz/24.; /* almost */
+		if(use_dst != 0) {
+		   if((fabs(jdtest-*jdb)<0.5) | (fabs(jdtest-*jde)<0.5)) {
+		   oprntf("** be careful -- near standard and daylight change. ** \n");
+		   }
+		}
+		if(use_dst > 0) {  /* handle exceptions during dst -- std change */
+			if((jdtest - *jdb > 0.)
+				&& (jdtest - *jdb < 0.041666667)) {
+				/* this time doesn't really exist */
+			  oprntf("Nonexistent input time during change to DST!\n");
+			  oprntf("Please respecify time as 1 hour later.\n");
+			  return(-1);  /* return error flag. */
+			}
+			else if((jdtest - *jde >= 0.0) &&
+				(jdtest - *jde < 0.04166667)) {
+			  oprntf("\nCAUTION - DST --> STD -- ambiguous input time defaults to STD!\n");
+			  oprntf("Use 'g' option to specify UT unambiguously.\n");
+			  jd = jdloc + stdz / 24.;
+			}
+			else jd = jdloc +
+				zone(use_dst,stdz,jdtest,*jdb,*jde) / 24.;
+		}
+		else if(use_dst < 0) {  /* south -- reverse jdb & jde */
+			if((jdtest - *jde > 0.)
+				&& (jdtest - *jde < 0.041666667)) {
+				/* this time doesn't really exist */
+			  oprntf("Nonexistent input time during change to DST!\n");
+			  oprntf("Please respecify time as 1 hour later.\n");
+			  return(-1);  /* return error flag. */
+			}
+			else if((jdtest - *jdb >= 0.0) &&
+				(jdtest - *jdb < 0.04166667)) {
+			  oprntf("\nCAUTION - DST --> STD -- ambiguous input time defaults to STD!\n");
+			  oprntf("Use 'g' option to specify UT unambiguously.\n");
+			  jd = jdloc + stdz / 24.;
+			}
+			else jd = jdloc +
+				zone(use_dst,stdz,jdtest,*jdb,*jde) / 24.;
+		}
+		else jd = jdtest;  /* not using dst ... no problem. */
+		oprntf("Local Date and time: ");
+		print_all(jdloc);
+		oprntf(" ");
+		print_tz(jd,use_dst,*jdb,*jde,zabr);
+/*              if(fabs(frac_part(jdloc) - 0.5) < MIDN_TOL)
+		   oprntf("\n(Watch for possible day error near midnight.)");
+ .... this really shouldn't happen now. */
+		if(use_dst != 0) {
+		}
+
+		oprntf("\n   UT Date and time: ");
+		print_all(jd);
+		if(fabs(frac_part(jd) - 0.5) < 0.00001)
+		   oprntf("\n(Watch for possible day error near midnight.)");
+	}
+	else {  /* times entered are ut already */
+
+		jd = date_to_jd(date);
+		oprntf("   UT Date and time: ");
+		print_all(jd);
+		if(fabs(frac_part(jd) - 0.5) < 0.00001)
+		   oprntf("\n(Watch for possible day error near midnight.)");
+		jdloc = jd - zone(use_dst,stdz,jd,*jdb,*jde)/24.;
+		if(use_dst != 0) {
+		   if((fabs(jd-*jdb)<0.05) | (fabs(jd-*jde)<0.05))
+			oprntf("\nCAUTION..dst/std time changing!");
+		}
+		oprntf("\nlocal Date and time: ");
+		print_all(jdloc);
+		oprntf(" ");
+		print_tz(jd,use_dst,*jdb,*jde,zabr);
+		if(fabs(frac_part(jdloc) - 0.5) < 0.00001)
+		   oprntf("\n(Watch for possible day error near midnight.)");
+
+	}
+	if(use_dst != 0) {
+		if(stdz==zone(use_dst,stdz,jd,*jdb,*jde))
+			oprntf("\nDST assumed not in effect\n");
+		else oprntf("\nDST assumed in effect.\n");
+	}
+	oprntf("\nJulian date: %lf",jd);
+	*sid=lst(jd,longit);
+	oprntf("   LMST: ");
+	put_coords(*sid,3);
+	*jdut = jd;
+	*jdlocal = jdloc;
+	*curepoch = 2000.+(jd-J2000)/365.25;
+	return(0);
+}
+
+void print_tonight(date,lat,longit,elevsea,elev,horiz,site_name,stdz,
+	zone_name,zabr,use_dst,jdb,jde,short_long)
+
+struct date_time date;
+double lat, longit, elevsea, elev, horiz, stdz, *jdb, *jde;
+char *site_name, *zone_name, zabr;
+short use_dst, short_long;     /* short_long is a fossil argument which
+                    allows a slightly shorter version to be printed. */
+
+/* Given site and time information, prints a summary of
+   the important phenomena for a single night.
+
+   The coding in this routine is extremely tortuous ... I even use
+   the dreaded goto statement!  It's inelegant, but (a) the logic is
+   indeed somewhat complicated and (b) it works.  */
+
+{
+
+	double jd, jdmid, jdcent, stmid, ramoon, decmoon, distmoon;
+	double geora, geodec, geodist;  /* geocent for moon, not used here.*/
+	double locjdb, locjde;
+	double rasun, decsun, min_alt, max_alt;
+	double hasunset, jdsunset, jdsunrise, sid;
+	double hamoonrise, hamoonset, tmoonrise, tmoonset,
+		jdmoonrise, jdmoonset;
+	double hatwilight, jdtwilight, jdetw, jdmtw;
+	double ill_frac;
+	short dow; /* day of week */
+	float set_to_rise, twi_to_twi, hrs,
+		   moonless_hrs, moony_hrs, moon_print;
+
+	find_dst_bounds(date.y,stdz,use_dst,jdb,jde);
+	locjdb = *jdb-stdz/24.;
+	locjde = *jde-(stdz-1)/24.;
+	date.h = 18;  /* local afternoon */
+	date.mn = 0;
+	date.s = 0;  /* afternoon */
+
+	oprntf("\nAlmanac for %s:\nlong. ",site_name);
+	put_coords(longit,2);
+	oprntf(" (h.m.s) W, lat. ");
+	put_coords(lat,1);
+	oprntf(" (d.m), elev. %5.0f m\n",elevsea);
+	jd = date_to_jd(date); /* not really jd; local equivalent */
+	if(use_dst > 0) {
+		oprntf("%s Daylight Savings Time assumed from 2 AM on\n",zone_name);
+		print_calendar(locjdb,&dow);
+		oprntf(" to 2 AM on ");
+		print_calendar(locjde,&dow);
+		oprntf("; standard zone = %3.0f hrs W",stdz);
+		if((fabs(jd - locjdb) < 0.45) || (fabs(jd - locjde) < 0.45))
+			oprntf("\n   ** TIME CHANGE IS TONIGHT! **");
+	}
+	else if (use_dst < 0) {
+		oprntf("%s Daylight Savings Time used before 2 AM \n",zone_name);
+		print_calendar(*jdb-stdz/24.,&dow);
+		oprntf(" and after 2 AM ");
+		print_calendar(*jde-(stdz-1)/24.,&dow);
+		oprntf("; standard zone = %3.0f hrs W",stdz);
+		if((fabs(jd - locjdb) < 0.45) || (fabs(jd - locjde) < 0.45))
+			oprntf("\n   ** TIME CHANGE IS TONIGHT! **");
+	}
+	else oprntf("%s Standard Time (%3.0f hrs W) in use all year.",zone_name,stdz);
+
+	oprntf("\n\n");
+
+	oprntf("For the night of: ");
+	print_day(day_of_week(jd));
+	oprntf(", ");
+	print_calendar(jd,&dow); /* translate back e.g. 11/31-12/1 */
+	jd = jd + .5;  /* local morning */
+	oprntf(" ---> ");
+	print_day(day_of_week(jd));
+	oprntf(", ");
+	print_calendar(jd,&dow);
+	oprntf("\n");
+	jd = jd - 0.25;  /* local midnight */
+	jdmid = jd + zone(use_dst,stdz,jd,*jdb,*jde) / 24.;
+					/* corresponding ut */
+	oprntf("Local midnight = ");
+	print_calendar(jdmid,&dow);
+	oprntf(", ");
+	print_time(jdmid,-1); /* just the hours! */
+	oprntf(" UT, or JD %11.3f\n",jdmid);
+	oprntf("Local Mean Sidereal Time at midnight = ");
+	stmid = lst(jdmid,longit);
+	put_coords(stmid,3);
+	oprntf("\n\n");
+
+	accumoon(jdmid,lat,stmid,elevsea,
+	   &geora,&geodec,&geodist,&ramoon,&decmoon,&distmoon);
+	lpsun(jdmid,&rasun,&decsun);
+	hasunset = ha_alt(decsun,lat,-(0.83+horiz));
+	if(hasunset > 900.) {  /* flag for never sets */
+		oprntf("Sun up all night!\n");
+		set_to_rise = 0.;
+		twi_to_twi = 0.;
+		jdcent = -1.;
+		goto DO_MOON_ETC;  /* aargh! Ugly flow of control!! But
+                   twilight certainly irrelevant if sun up all night, so
+		   skip it. */
+	}
+	if(hasunset < -900.) {
+		oprntf("Sun down all day!\n");
+		set_to_rise = 24.;
+		jdcent = -1.;
+		goto CHECK_TWI18;  /* More ugly flow  -
+			checks for twilight even if sun down. */
+	}
+	jdsunset = jdmid + adj_time(rasun+hasunset-stmid)/24.;
+		/* initial guess */
+	jdsunset = jd_sun_alt(-(0.83+horiz),jdsunset,lat,longit);
+	if(jdsunset > 0.) {
+		oprntf("Sunset (%5.0f m horizon): ",elev);
+		print_time((jdsunset-zone(use_dst,stdz,jdsunset,*jdb,*jde)/24.),0);
+		print_tz(jdsunset,use_dst,*jdb,*jde,zabr);
+	}
+	else oprntf("Sunset not correctly computed; ");
+	jdsunrise = jdmid + adj_time(rasun-hasunset-stmid)/24.;
+	jdsunrise = jd_sun_alt(-(0.83+horiz),jdsunrise,lat,longit);
+	if(jdsunrise > 0.) {
+		oprntf("; Sunrise: ");
+		print_time((jdsunrise-zone(use_dst,stdz,jdsunrise,*jdb,*jde)/24.),0);
+		print_tz(jdsunrise,use_dst,*jdb,*jde,zabr);
+	}
+	if((jdsunrise > 0.) && (jdsunset > 0.)) {
+		set_to_rise = (jdsunrise - jdsunset) * 24.;
+		jdcent = (jdsunrise + jdsunset) / 2.;
+	}
+	else {
+		oprntf(" Sunrise not correctly computed.");
+		jdcent = -1.;
+	}
+
+	/* This block checks for and prints out 18-degree twilight
+           as appropriate. */
+
+	CHECK_TWI18: hatwilight = ha_alt(decsun,lat,-18.);
+	if(hatwilight < -900.) {
+		oprntf("\nFull darkness all day (sun below -18 deg).\n");
+		twi_to_twi = 24.;
+		goto DO_MOON_ETC;  /* certainly no 12-degree twilight */
+	}
+	if(hatwilight > 900.) {
+		oprntf("\nSun higher than 18-degree twilight all night.\n");
+		twi_to_twi = 0.;
+		goto CHECK_TWI12;  /* but maybe 12-degree twilight occurs ...*/
+	}
+
+	/* compute & print evening twilight and LST at eve. twilight */
+	jdtwilight = jdmid + adj_time(rasun+hatwilight-stmid)/24.;  /* rough */
+	jdtwilight = jd_sun_alt(-18.,jdtwilight,lat,longit);  /* accurate */
+	jdetw = jdtwilight;
+	if(jdtwilight > 0.) {
+		oprntf("\nEvening twilight: ");
+		print_time((jdtwilight-zone(use_dst,stdz,jdtwilight,*jdb,*jde)/24.),0);
+		sid = lst(jdtwilight,longit);
+		print_tz(jdtwilight,use_dst,*jdb,*jde,zabr);
+		oprntf(";  LMST at evening twilight: ");
+		put_coords(sid,0);
+		oprntf("\n");
+	}
+	else oprntf("Evening twilight incorrectly computed.\n");
+
+        /* Now do morning twilight */
+	jdtwilight = jdmid + adj_time(rasun-hatwilight-stmid)/24.;
+	jdtwilight = jd_sun_alt(-18.,jdtwilight,lat,longit);
+	jdmtw = jdtwilight;
+	if(jdtwilight > 0.) {
+		oprntf("Morning twilight: ");
+		print_time((jdtwilight-zone(use_dst,stdz,jdtwilight,*jdb,*jde)/24.),0);
+		sid = lst(jdtwilight,longit);
+		print_tz(jdtwilight,use_dst,*jdb,*jde,zabr);
+		oprntf(";  LMST at morning twilight: ");
+		put_coords(sid,0);
+	}
+	else oprntf("Morning twilight incorrectly computed.");
+	if((jdetw > 0.) && (jdmtw > 0.)) twi_to_twi = 24. * (jdmtw - jdetw);
+
+        /* Now do same for 12-degree twilight */
+	CHECK_TWI12: hatwilight = ha_alt(decsun,lat,-12.);
+	if(hatwilight < -900.) {
+		oprntf("\nSun always below 12-degree twilight...\n");
+		goto DO_MOON_ETC;
+	}
+	if(hatwilight > 900.) {
+		oprntf("\nSun always above 12-degree twilight...\n");
+		goto DO_MOON_ETC;
+	}
+	jdtwilight = jdmid + adj_time(rasun+hatwilight-stmid)/24.;
+	jdtwilight = jd_sun_alt(-12.,jdtwilight,lat,longit);
+	if(jdtwilight > 0.) {
+		oprntf("\n12-degr twilight:");
+		print_time((jdtwilight-zone(use_dst,stdz,jdtwilight,*jdb,*jde)/24.),0);
+		print_tz(jdtwilight,use_dst,*jdb,*jde,zabr);
+	}
+	else oprntf("Evening 12-degree twilight incorrectly computed.\n");
+
+	jdtwilight = jdmid + adj_time(rasun-hatwilight-stmid)/24.;
+	jdtwilight = jd_sun_alt(-12.,jdtwilight,lat,longit);
+	if(jdtwilight > 0.) {
+		oprntf(" -->");
+		print_time((jdtwilight-zone(use_dst,stdz,jdtwilight,*jdb,*jde)/24.),0);
+		print_tz(jdtwilight,use_dst,*jdb,*jde,zabr);
+		oprntf("; ");
+	}
+	else oprntf("Morning 12-degree twilight incorrectly computed.");
+
+        DO_MOON_ETC:
+        if(jdcent > 0.) {
+		oprntf("night center: ");
+		print_time((jdcent-zone(use_dst,stdz,jdcent,*jdb,*jde)/24.),0);
+		print_tz(jdcent,use_dst,*jdb,*jde,zabr);
+	}
+	oprntf("\n\n");
+	min_max_alt(lat,decmoon,&min_alt,&max_alt);  /* rough check -- occurs? */
+	if(max_alt < -(0.83+horiz)) {
+		oprntf("Moon's midnight position does not rise.\n");
+		jdmoonrise = -1.;
+		goto MORE_MOON;
+	}
+	if(min_alt > -(0.83+horiz)) {
+		oprntf("Moon's midnight position does not set.\n");
+		jdmoonrise = 1.;
+		goto MORE_MOON;
+	}
+
+	/* compute moonrise and set if they're likely to occur */
+
+	hamoonset = ha_alt(decmoon,lat,-(0.83+horiz)); /* rough approx. */
+	tmoonrise = adj_time(ramoon-hamoonset-stmid);
+	tmoonset = adj_time(ramoon+hamoonset-stmid);
+	jdmoonrise = jdmid + tmoonrise / 24.;
+	jdmoonrise = jd_moon_alt(-(0.83+horiz),jdmoonrise,lat,longit,elevsea);
+	jdmoonset = jdmid + tmoonset / 24.;
+	jdmoonset = jd_moon_alt(-(0.83+horiz),jdmoonset,lat,longit,elevsea);
+	if(fabs(set_to_rise) > 10.) moon_print = 0.65*set_to_rise;
+		 else moon_print = 6.5;
+
+	/* more lousy flow of control, but it's nice to see the event which happens
+	   first printed first .... */
+
+	if(jdmoonset < jdmoonrise) goto PRINT_MOONSET;  /* OUCH!! */
+	PRINT_MOONRISE: if((jdmoonrise > 0.) && (fabs(tmoonrise) < moon_print)) {
+	  /* print it if computed correctly and more-or-less at night */
+		oprntf("Moonrise: ");
+		print_time((jdmoonrise-zone(use_dst,stdz,jdmoonrise,*jdb,*jde)/24.),0);
+		print_tz(jdmoonrise,use_dst,*jdb,*jde,zabr);
+		oprntf("   ");
+	}
+	else if (jdmoonrise < 0.) oprntf("Moonrise incorrectly computed. ");
+	if(jdmoonset < jdmoonrise) goto MORE_MOON;
+	PRINT_MOONSET: if((jdmoonset > 0.) && (fabs(tmoonset) < moon_print)) {
+		oprntf("Moonset : ");
+		print_time((jdmoonset-zone(use_dst,stdz,jdmoonset,*jdb,*jde)/24.),0);
+		print_tz(jdmoonset,use_dst,*jdb,*jde,zabr);
+		oprntf("   ");
+	}
+	else if (jdmoonset < 0.) oprntf("Moonset incorrectly computed. ");
+	if(jdmoonset < jdmoonrise) goto PRINT_MOONRISE;
+
+     MORE_MOON: ill_frac=0.5*(1.-cos(subtend(ramoon,decmoon,rasun,decsun)));
+	oprntf("\nMoon at civil midnight: ");
+	oprntf("illuminated fraction %5.3f\n",ill_frac);
+	print_phase(jdmid);
+	oprntf(", RA and dec: ");
+	put_coords(ramoon,2);
+	oprntf(", ");
+	put_coords(decmoon,1);
+	oprntf("\n\n");
+
+     /* print more information if desired */
+     if(short_long == 2) {  /* wacky indenting here ... */
+	oprntf("The sun is down for %4.1f hr; %4.1f hr from eve->morn 18 deg twilight.\n",
+		set_to_rise,twi_to_twi);
+	if((jdmoonrise > 100.) && (jdmoonset > 100.) &&
+	   (twi_to_twi > 0.) && (twi_to_twi < 24.)) {
+	  /* that is, non-pathological */
+		if((jdmoonrise > jdetw) && (jdmoonrise < jdmtw)) /* rises at night */
+			oprntf("%4.1f dark hours after end of twilight and before moonrise.\n",
+			  (24.*(jdmoonrise - jdetw)));
+		if((jdmoonset > jdetw) && (jdmoonset < jdmtw)) /* sets at night */
+			oprntf("%4.1f dark hours after moonset and before beginning of twilight.\n",
+			  (24.*(jdmtw - jdmoonset)));
+		if((jdmoonrise < jdetw) && (jdmoonset > jdmtw))
+			oprntf("Bright all night (moon up from evening to morning twilight).\n");
+		if((jdmoonrise > jdmtw) && (jdmoonset < jdetw))
+			oprntf("Dark all night (moon down from evening to morning twilight).\n");
+	}
+     }  /* closes the wacky indent. */
+}
+
+void print_circumstances(objra,objdec,objepoch,jd,curep,
+	mura_arcs,mura_sec,mudec,sid,lat,elevsea,horiz)
+
+double objra,objdec,objepoch,curep,mura_arcs,mura_sec,mudec,lat,horiz;
+double jd,sid,elevsea;
+
+/* Given object, site, and time information, prints the circumstances
+   of an observation.  The heart of the "calculator" mode. */
+
+{
+	double objra_adj,objdec_adj,curra,curdec,ha,alt,az,secz,par;
+	float ill_frac;
+	double ramoon,decmoon,distmoon,ang_moon,
+		georamoon, geodecmoon, geodistmoon,
+		rasun,decsun,distsun,x,y,z,
+		toporasun,topodecsun,
+		moon_alt,sun_alt,obj_moon,obj_lunlimb,sun_moon,Vmoon,
+		obj_alt,eclong,eclat,tcor,vcor;
+	short luneclcode = 0;
+
+	oprntf("\n\nStd epoch--> RA:");
+	put_coords(objra,3);
+	oprntf(", dec:");
+	put_coords(objdec,2);
+	oprntf(", ep %7.2f\n",objepoch);
+	if((mura_sec != 0.) | (mura_arcs != 0.) |(mudec != 0.)) {
+		objra_adj = objra + (curep-objepoch)* mura_sec/3600.;
+		objdec_adj = objdec + (curep-objepoch)*mudec/3600.;
+		oprntf("Adj for p.m: RA:");
+		put_coords(objra_adj,3);
+		oprntf(", dec:");
+		put_coords(objdec_adj,2);
+		oprntf(", epoch %7.2f, equinox %7.2f\n", curep,objepoch);
+		oprntf("(Annual proper motions:");
+		mura_arcs = mura_sec * 15. *
+		     cos(objdec / DEG_IN_RADIAN);
+      		oprntf(" RA: %8.4f sec //%7.3f arcsec, ",mura_sec,mura_arcs);
+		oprntf("dec: %7.3f)\n",mudec);
+		precrot(objra_adj,objdec_adj,objepoch,curep,&curra,&curdec);
+	}
+	else precrot(objra,objdec,objepoch,curep,&curra,&curdec);
+	oprntf("Current  --> RA:");
+	put_coords(curra,3);
+	oprntf(", dec:");
+	put_coords(curdec,2);
+	oprntf(", ep %7.2f\n",curep);
+	ha = adj_time(sid - curra);
+	oprntf("HA: ");
+	put_coords(ha,2);
+	alt=altit(curdec,ha,lat,&az);
+	obj_alt = alt;
+	/* test size of sec z to avoid overflowing space provided */
+	secz = secant_z(alt);
+ 	if(fabs(secz) < 100.) oprntf("; sec.z = %8.3f",secz);
+	else oprntf(" Obj very near horizon.");
+	if(secz > 3.) oprntf(" -- Large airmass!\n");
+	else if(secz < 0.) oprntf(" -- BELOW HORIZON.\n");
+	else oprntf("\n");
+	oprntf("altitude %6.2f, azimuth %6.2f, ",alt,az);
+	par = parang(ha,curdec,lat);
+	oprntf("parallactic angle %4.1f",par);
+	/* also give +- 180 ..... */
+	if((par <= 180.) && (par > 0.)) oprntf("  [%4.1f]\n\n",par - 180.);
+	 else oprntf("  [%4.1f]\n\n",par + 180.);
+	accumoon(jd,lat,sid,elevsea,&georamoon,&geodecmoon,&geodistmoon,
+			&ramoon,&decmoon,&distmoon);
+	accusun(jd,sid,lat,&rasun,&decsun,&distsun,
+		&toporasun,&topodecsun,&x,&y,&z);
+	alt=altit(topodecsun,(sid-toporasun),lat,&az);
+	sun_moon = subtend(ramoon,decmoon,toporasun,topodecsun);
+	ill_frac= 0.5*(1.-cos(sun_moon)); /* ever so slightly inaccurate ...
+	   basis of ancient Greek limit on moon/sun distance ratio! */
+	sun_moon = sun_moon * DEG_IN_RADIAN;
+/*      printf("geocentric moon: ");
+	put_coords(georamoon,4);
+	printf("  ");
+	put_coords(geodecmoon,3);
+	printf(" %f \n",geodistmoon);  */
+	sun_alt = alt;
+	if(alt < -18.) oprntf("The sun is down; there is no twilight.\n");
+	else {
+		if (alt < -(0.83+horiz))
+			oprntf("In twilight, sun alt %4.1f, az %5.1f ",alt,az);
+		else oprntf("The sun is up, alt %4.1f, az %4.1f",alt,az);
+		oprntf("; Sun at ");
+		put_coords(toporasun,3);
+		oprntf(", ");
+		put_coords(topodecsun,2);
+		oprntf("\n");
+		if(sun_moon < 1.5) solecl(sun_moon,distmoon,distsun);
+			/* check for solar eclipse if it's close */
+		if (alt < -(0.83+horiz))
+		  oprntf("Clear zenith twilight (blue) approx %4.1f  mag over dark night sky.\n",
+				ztwilight(alt));
+	}
+	moon_alt=altit(decmoon,(sid-ramoon),lat,&az);
+	if(moon_alt > -2.) {
+		oprntf("Moon :");
+		put_coords(ramoon,2);
+		oprntf(",");
+		put_coords(decmoon,1);
+		oprntf(", alt %5.1f, az %5.1f;",moon_alt,az);
+		oprntf("%6.3f illum.\n",ill_frac);
+		print_phase(jd);
+		obj_moon = DEG_IN_RADIAN * subtend(ramoon,decmoon,curra,curdec);
+		if(fabs(obj_moon) > 10.) {
+		  oprntf(".  Object is %5.1f degr. from moon.\n",obj_moon);
+		}
+		else  {
+			ang_moon = DEG_IN_RADIAN * asin(RMOON / (distmoon * EQUAT_RAD));
+			if((obj_lunlimb = obj_moon - ang_moon) > 0.)
+			  oprntf(" ** NOTE ** Object %4.1f degr. from lunar limb!\n",obj_lunlimb);
+			else oprntf(" ** NOTE ** You're looking AT the moon!\n");
+		}
+		if(sun_moon > 176.)
+			luneclcode = lunecl(georamoon,geodecmoon,geodistmoon,
+			rasun,decsun,distsun);
+		if((moon_alt > 0.) && (obj_alt > 0.5) && (sun_alt < -9.)) {
+		  /*if it makes sense to estimate a lunar sky brightness */
+		  Vmoon =
+		     lunskybright(sun_moon,obj_moon,KZEN,moon_alt,
+				obj_alt,distmoon);
+
+		     oprntf("Lunar part of sky bright. = %5.1f V mag/sq.arcsec (estimated).\n",Vmoon);
+		     if(luneclcode != 0)
+			oprntf(" NOT including effect of LUNAR ECLIPSE ...!\n");
+		}
+	}
+	else {
+		print_phase(jd);
+		oprntf(".  The moon is down.\n");
+	}
+	eclipt(objra,objdec,objepoch,jd,&curep,&eclong,&eclat);
+	if(fabs(eclat)<10.) {
+		oprntf("Ecliptic latitude %4.1f; ",eclat);
+		oprntf("watch for low-flying minor planets.\n");
+	}
+	planet_alert(jd,curra,curdec,PLANET_TOL);
+	/* tolerance set to 3 degrees earlier. */
+	helcor(jd,curra,curdec,ha,lat,elevsea,&tcor,&vcor);
+	oprntf("Barycentric corrections: add %6.1f sec, %5.2f",tcor,vcor);
+	oprntf(" km/sec to observed values.\n");
+	oprntf("Barycentric Julian date = %14.6f\n",(jd+tcor/SEC_IN_DAY));
+	printf("\nType command, 'f' for fast tour, '?' for a menu:");
+}
+
+void hourly_airmass(date,stdz,lat,longit,horiz,use_dst,objra,objdec,
+  objepoch, mura_sec,mura_arcs,mudec)
+
+/* Given a slew of information, prints a table of hourly airmass, etc.
+   for use in scheduling observations.  Also prints sun and moon
+   altitude when these are relevant.  Precesses coordinates as well. */
+
+struct date_time date;
+double stdz,lat,longit,horiz,objra,objdec,objepoch,mura_sec,mura_arcs,mudec;
+short use_dst;
+
+{
+
+	double jd, jdb, jde, jdmid, curra, curdec, curep;
+	double sid, ha, alt, az, secz, par, rasun, decsun, ramoon,
+		decmoon,distmoon;
+ 	double ill_frac,sepn;
+        long int jdclong;
+	int nch;
+	short i, hr_span, dow;
+	char obj_name[40], errprompt[80];
+        double hasset, jdsset, jdsrise, jdcent;
+
+	if((date.y <= 1900) | (date.y >= 2100)) {
+		printf("Date out of range - 1901 -> 2099\n");
+		return;
+	}
+	printf("Name of object:");
+	nch = get_line(obj_name);
+
+	find_dst_bounds(date.y,stdz,use_dst,&jdb,&jde);
+	date.h = 24; /* local midn */
+	date.mn = 0;
+	date.s = 0;
+	jdmid = date_to_jd(date) + stdz/24.;
+	/* first approx.-imperfect near time change */
+	jdmid = date_to_jd(date) +
+		zone(use_dst,stdz,jdmid,jdb,jde)/24.;
+	curep = 2000.+(jdmid - J2000)/365.25;
+	oprntf("\n\n*** Hourly airmass for %s ***\n\n",obj_name);
+	if((use_dst != 0) &&
+		((fabs(jdmid - jdb) < 0.5) || (fabs(jdmid - jde) < 0.5)))
+		oprntf("*** NOTE STD/DAYLIGHT TIME CHANGE TONIGHT ***\n");
+	oprntf("Epoch %7.2f: RA ",objepoch);
+	put_coords(objra,3);
+	oprntf(", dec ");
+	put_coords(objdec,2);
+	oprntf("\n");
+	oprntf("Epoch %7.2f: RA ",curep);
+	precrot(objra,objdec,objepoch,curep,&curra,&curdec);
+	put_coords(curra,3);
+	oprntf(", dec ");
+	put_coords(curdec,2);
+	if((mura_sec != 0.) | (mura_arcs != 0.) | (mudec != 0))
+	   oprntf("\n Caution .. proper motion ignored\n\n");
+	else oprntf("\n\n");
+	oprntf("At midnight: UT date ");
+	print_calendar(jdmid,&dow);
+	lpsun(jdmid,&rasun,&decsun);
+	sid=lst(jdmid,longit);
+	lpmoon(jdmid,lat,sid,&ramoon,&decmoon,&distmoon); /* close enuf */
+	ill_frac=0.5*(1.-cos(subtend(ramoon,decmoon,rasun,decsun)));
+	sepn = DEG_IN_RADIAN * subtend(ramoon,decmoon,curra,curdec);
+	oprntf(", Moon %4.2f illum, %3.0f degr from obj\n",ill_frac,sepn);
+	planet_alert(jdmid,curra,curdec,PLANET_TOL);   /* better know about it .... */
+	oprntf("\n  Local      UT      LMST");
+	oprntf("      HA     secz   par.angl. SunAlt MoonAlt\n\n");
+
+        /* figure out how much to print ... */
+
+        hasset = ha_alt(decsun,lat,-0.83);
+        jdsset = jd_sun_alt(-0.83,(jdmid - (12. - hasset) / 24.),lat,longit);
+        jdsrise = jd_sun_alt(-0.83,(jdmid + (12. - hasset) / 24.),lat,longit);
+        jdcent = (jdsset + jdsrise) / 2.;   /* center of night .. not local mid */
+        hr_span = (short) (12. * (jdsrise - jdsset) + 0.5);
+ /*       printf("jdsset %f jdsrise %f jdcent %f hr_span %d\n",
+                jdsset,jdsrise,jdcent,hr_span);   ... diagnostic */
+        jdclong = (long) ((24. * jdcent) + 0.5);  /* round to nearest hour */
+        jdcent = jdclong / 24. + 0.00001;  /* add a hair to prevent "24 00"
+              rounding ugliness in time table. */
+ /*       printf("jdcent .... %f\n",jdcent);  ... diagnostic */
+	for(i=(-1 * hr_span);i<=hr_span;i++) {
+		jd = jdcent + i/24.;
+		sid=lst(jd,longit);
+		lpsun(jd,&rasun,&decsun);
+		if(altit(decsun,(sid-rasun),lat,&az) > 0.) goto SKIP;
+		print_time((jd-zone(use_dst,stdz,jd,jdb,jde)/24.),0);
+		oprntf("  ");
+		print_time(jd,0);
+		oprntf("  ");
+	     /*	sid=lst(jd,longit); */
+		put_coords(sid,0);
+		oprntf("  ");
+		ha = adj_time(sid - curra);
+		put_coords(ha,0);
+		oprntf("  ");
+		alt=altit(curdec,ha,lat,&az);
+	        if(alt < -(horiz)) oprntf(" (down)");
+		else if(alt < 1.0) oprntf("(v.low)");
+		else {
+		     secz=secant_z(alt);
+      		     oprntf(" %6.3f",secz);
+		}
+		par = parang(ha,curdec,lat);
+		oprntf("  %6.1f ",par);
+		/* lpsun(jd,&rasun,&decsun); */
+		alt=altit(decsun,(sid-rasun),lat,&az);
+		if(alt < -18.) oprntf("    ... ");
+		   else oprntf("   %5.1f",alt);
+		lpmoon(jd,lat,sid,&ramoon,&decmoon,&distmoon); /* close enuf */
+		alt=altit(decmoon,(sid-ramoon),lat,&az);
+		if(alt < -2.)  oprntf("    ... \n");
+		   else oprntf("   %5.1f\n",alt);
+                SKIP: ;
+	}
+}
+
+void print_params(date,enter_ut,night_date,stdz,lat,longit,site_name,
+    elevsea,elev,use_dst,objra,objdec,objepoch,mura_sec,mura_arcs,
+    mudec)
+
+    struct date_time date;
+    short enter_ut;
+    short night_date;
+    double stdz;
+    double lat;
+    double longit;
+    char *site_name;
+    double elevsea;
+    double elev;
+    short use_dst;
+    double objra;
+    double objdec;
+    double objepoch;
+    double mura_sec;
+    double mura_arcs;
+    double mudec;
+
+/* This simply prints a nicely formatted list of the *input* parameters
+   without doing any computations.  Helpful for the perplexed user, and
+   for checking things. */
+{
+	short dow;
+	double jd;
+
+	jd = date_to_jd(date);
+	oprntf("\nCurrent INPUT parameter values:\n\n");
+	oprntf("      DATE: ");
+	print_calendar(jd,&dow);
+	oprntf("\n      TIME:");
+	print_time(jd,3);
+	if(night_date == 1)
+		oprntf("\nNIGHT_DATE:  ON    -- date applies all evening & next morning.\n");
+	else oprntf("\nNIGHT_DATE:  OFF   -- date is taken literally.\n");
+	if(enter_ut == 1)
+		oprntf("  UT_INPUT:  ON    -- input times taken to be UT.\n");
+	else oprntf("  UT_INPUT:  OFF   -- input times taken to be local.\n");
+	if(use_dst == 0)
+		oprntf("   USE_DST:   0    -- Standard time in use all year.\n");
+	else oprntf("   USE_DST: %3d    -- Daylight savings used part of year.\n",
+			use_dst);
+	oprntf("\n");
+	oprntf("            RA: ");
+	put_coords(objra,4);
+	oprntf("\n           DEC: ");
+	put_coords(objdec,3);
+	oprntf("\n   INPUT EPOCH:   %8.2f\n",objepoch);
+	if((mura_sec == 0.) && (mura_arcs == 0.) && (mudec == 0.))
+		oprntf("PROPER MOTIONS:  OFF\n");
+	else    {
+          mura_arcs = mura_sec * 15. * cos(objdec / DEG_IN_RADIAN);
+	  oprntf("PROPER MOTIONS: RA(sec)=%6.3f, RA(arc)=%6.2f, DEC=%6.2f\n",
+		mura_sec,mura_arcs,mudec);
+        }
+	oprntf("\nSITE: %s\n",site_name);
+	oprntf("      E.longit. = ");
+	put_coords(-15. * longit,1);
+	oprntf(", latit. = ");
+	put_coords(lat,1);
+	oprntf(" (degrees)\n");
+	oprntf("      Standard zone = %3.0f hrs ",fabs(stdz));
+	if(stdz >= 0.) oprntf(" West\n");
+	  else oprntf(" East\n");
+	oprntf("      Elevation above horizon = %4.0f m, True elevation = %4.0f m\n",
+		elev,elevsea);
+}
+
+void print_menu()
+	{
+	if(sclogfl != NULL) fprintf(sclogfl,"\n\n *** Menu Choices *** \n");
+	oprntf("Circumstance calculator, type '=' for output.\n");
+	oprntf("Commands are SINGLE (lower-case!) CHARACTERS as follows:\n");
+	oprntf(" ? .. prints this menu; other information options are:\n");
+	oprntf("i,f . 'i' prints brief Instructions and examples, 'f' fast tour\n");
+	oprntf(" w .. prints info on internal Workings, accuracy & LEGALITIES\n");
+	oprntf("TO SET PARAMETERS & OPTIONS, use these (follow the formats!):\n");
+	oprntf(" r .. enter object RA, in hr min sec,  example: r 3 12 12.43\n");
+	oprntf(" d .. enter object Dec in deg min sec, example: d -0 18 0\n");
+	oprntf(" y .. enter date, starting with Year   example: y 1994 10 12\n");
+#if SYS_CLOCK_OK == 1
+     	oprntf("t,T: t = enter time, e.g.: t 22 18 02 [see 'g' and 'n']; T = right now+\n");
+#else
+     	oprntf(" t .. enter time, example: t 22 18 02 ; see 'g' and 'n' below\n");
+#endif
+     	oprntf(" n .. *toggles* whether date is used as 'evening' (default) or literal\n");
+	oprntf(" g .. *toggles* whether time is used as Greenwich or local\n");
+	oprntf(" e .. enter Epoch used to interpret input coords (default = 1950)\n");
+	oprntf(" p .. enter object Proper motions (complicated, follow prompts).\n");
+	oprntf(" s .. change Site (again, follow prompts).\n");
+	oprntf(" l .. Look at current parameter values (no computation).\n");
+	oprntf("TO CALCULATE AND SEE RESULTS, use these commands: \n");
+	oprntf(" = .. type out circumstances for specified instant of time, ra, dec, etc.\n");
+	oprntf(" a .. type out night's Almanac for specified (evening) date\n");
+	oprntf(" h .. type out Hourly airmass table for specified date, ra, dec\n");
+        oprntf(" o .. tabulate Observability at 2-week intervals (at full&new moon)\n");
+	oprntf(" m .. Major planets -- print 0.1 deg positions for specified instant\n");
+        oprntf(" x .. eXtra goodies: log file, object files, other calculators; try x? \n");
+	oprntf(" Q .. QUIT .. STOPS PROGRAM. ---> ");
+}
+
+void print_tutorial()
+{
+	if(sclogfl != NULL) fprintf(sclogfl,"\n  *** Fast Guided Tour listing ***\n\n");
+	oprntf("FAST GUIDED TOUR: (type 'f' to see this again).\n\n");
+	oprntf("To explore this program quickly, try the following (in order):\n\n");
+	oprntf("--> Specify an evening date (e.g., 1995 March 22) and then display\n");
+	oprntf("    the almanac for that night by typing \n");
+	oprntf("        y 1995 3 22   a \n");
+	oprntf("--> Specify an ra and dec and then make an hourly airmass table\n");
+	oprntf("    by typing, for instance \n");
+	oprntf("        r 15 28 27  d 12 13 14  h \n");
+	oprntf("    (Notice how the date you specified earlier is still in effect.)\n");
+	oprntf("--> Specify a time (e.g., 4 50 00 AM local time) and display the\n");
+	oprntf("    circumstances at that instant by typing\n");
+	oprntf("        t 4 50 0  =\n");
+	oprntf("--> Type 'o' and follow the prompts to show observability\n");
+	oprntf("    through a season (tabulated at new & full moon).\n");
+	oprntf("--> Type 'l' to list the current input parameters and options.\n");
+	oprntf("--> Type 'm' to list planetary positions.\n");
+#if SYS_CLOCK_OK == 1
+ 	oprntf("--> Type 'T' to set time & date to right now (+ settable offset).\n");
+        oprntf("--> Type 'x?' for eXtra goodies menu; log file, object lists etc..\n");
+	oprntf("--> Read the help texts; '?' prints a short menu, 'i' gives\n");
+#else
+	oprntf("--> Read the help texts; '?' prints a short menu, 'i' gives\n");
+#endif
+	oprntf("    more complete information, 'w' tells you about inner workings.\n");
+	oprntf("    An upper-case 'Q' exits the program.\n");
+	oprntf(" Give a command: ");
+}
+
+void print_examples() {
+
+	char cdum;
+
+	if(sclogfl != NULL) fprintf(sclogfl,"\n\n *** On-Line Documentation listing ***");
+	oprntf("\n\nMost parameters are entered as a single character followed\n");
+	oprntf("by a value; you then type an EQUALS SIGN to compute circumstances\n");
+	oprntf("for current site, time, and celestial position. FOR EXAMPLE:\n");
+	oprntf("You observe an object at RA 19 02 23.3, dec -5 18 17\n");
+	oprntf("epoch 2000., on 1993 June 18, at 21 32 18 local time, from\n");
+	oprntf("the site you've selected.  To calculate circumstances, type\n\n");
+	oprntf("r 19 02 23.3 d -5 18 17 e 2000 y 1993 6 18 t 21 32 18 =\n\n");
+	oprntf("Note: coords, dates and times are entered as TRIPLETS OF NUMBERS\n");
+	oprntf("separated by blanks (not colons), and date format is y m d.\n");
+	oprntf("Commands are CASE SENSITIVE (R is not r)!  Carriage returns\n");
+	oprntf("can go almost anywhere. Parameters remain in effect until you\n");
+	oprntf("change them, so in this example typing\n");
+	oprntf("t 23 32 18 =  \n");
+	oprntf("would give circumstances for the same object two hours later.\n\n");
+
+	oprntf("Typing 'a' prints an almanac for the currently specified date -- be\n");
+	oprntf("careful you have the correct night if you've used 'n' or 'g'. (below).\n\n");
+
+	oprntf("(Hit return to continue listing...)");
+	scanf("%c",&cdum);
+	scanf("%c",&cdum);  /* first one eats fossil input.. */
+
+	oprntf("\n\nTwo commands invoke options for the interpretation of times and dates.\n");
+        oprntf("[N. B. -- This issue is a little confusing, but important .... ]\n");
+	oprntf("By default, the program interprets dates as 'evening', so late PM and\n");
+	oprntf("early AM times refer to the same night.  Typing 'n' (for 'night date')\n");
+	oprntf("TOGGLES bewteen this and literal dates.  Also by default, input\n");
+	oprntf("times are in local (zone) time, but input times can be interpreted as UT;\n");
+	oprntf("typing 'g' TOGGLES between these.  Invoking UT mode disables night dates.\n");
+	oprntf("Note that 'g', 'n' (and the epoch option 'e') do not cause calculation, but\n");
+	oprntf("only affect interpretation of the relevant parameters at compute time.\n\n");
+
+	oprntf("Typing 'l' (look) causes all the input parameters to be typed out without\n");
+        oprntf("any computation, which is especially helpful if you're lost by 'g' or 'n'.\n\n");
+
+	oprntf("Typing 'h' creates a table of airmass, etc. at hourly intervals through\n");
+	oprntf("the night.  You may wish to redirect output (using system) to print.\n\n");
+
+	oprntf("Typing 'o' computes observability of an object for each dark\n");
+	oprntf("and bright run through a range of dates.\n\n");
+
+	oprntf("(Hit return to continue listing...)");
+	scanf("%c",&cdum);
+
+	oprntf("\nTyping 'm' displays rough (see 'w' option) positions of major planets.\n\n");
+
+	oprntf("To change observing sites, type 's' and answer the prompts.\n\n");
+
+#if SYS_CLOCK_OK == 1
+	oprntf("Typing 'T' sets BOTH the TIME and DATE to 'now' using the system clock.\n");
+	oprntf("You can also specify a number of minutes into the future to set.  The status\n");
+	oprntf("of the 'n' and 'g' options are properly taken into account.\n\n");
+#endif
+	oprntf("To enable an approximate proper motion correction, type 'p' and\n");
+	oprntf("answer the prompts *carefully*.  Re-enter as zero to turn off.\n\n");
+	oprntf("Typing 'x' invokes many useful commands -- log file, object files, etc!\n\n");
+	oprntf("Type command, Q (upper case) to stop, or ? for a menu:");
+}
+
+void print_accuracy()
+
+{
+
+	char cdum;
+
+        if(sclogfl != NULL) fprintf(sclogfl,"\n\n");
+	oprntf("ACCURACY INFO:\n\n");
+	oprntf("The distinctions between UTC, UT1, TDT (etc.) are ignored\n");
+	oprntf("except that a rough correction to TDT is used for the moon.\n");
+	oprntf("The solar ephemeris used is good to a few arcsec.  Moon positions\n");
+	oprntf("positions are topocentric and +- about 30 arcsec, hence solar\n");
+	oprntf("eclipse paths are +- 50 km and +- 1 min. All rise/set times are\n");
+	oprntf("computed to about +-1 min; non-level horizon, site elevation,\n");
+	oprntf("and refraction uncertainties are often larger than this.\n\n");
+	oprntf("The lunar sky brightness model assumes ideal atmospheric conditions;\n");
+	oprntf("true lunar contributions to sky will vary widely.  To compare\n");
+	oprntf("a dark site has about V=21.5 mag/sq.arcsec (variable)! Twilight\n");
+	oprntf("brightness prediction is for blue, and only very approximate.\n\n");
+	oprntf("The planetary calculations are truncated, but the error should\n");
+	oprntf("seldom exceed 0.1 degree; MV are best(1'), MJSU ok, Pluto worst.\n");
+	oprntf("\nNote that the local sidereal time given is Mean, not true,\n");
+	oprntf("and that it assumes the input is true UT, not UTC (< 1 second)\n");
+	oprntf("(Hit return to continue listing...)");
+	scanf("%c",&cdum);
+	scanf("%c",&cdum);  /* first one usually eats fossil input.. */
+	oprntf("\nDaylight savings time, if selected, is established using a\n");
+	oprntf("site-specific convention (e.g., USA).  Beware of ambiguities\n");
+	oprntf("and nonexistent times when the clock is reset.  If necessary,\n");
+	oprntf("use the 'g' option and enter times and dates as Greenwich (UT),\n");
+	oprntf("or disable DST in site params (see discussion under 'i').\n\n");
+	oprntf("The *precession* routine used is a 'rigorous'");
+	oprntf(" rotation matrix routine,\ntaken from L. Taff's Computational");
+	oprntf(" Spherical Astronomy.\nIt uses IAU1976 constants, is good to < 1 arcsec in 50 years,\n");
+	oprntf("and has no troubles near the pole.  Proper motion corrections\n");
+	oprntf("are done crudely as x = x0 + mu * dt; this is inaccurate near\n");
+	oprntf("the poles.  Use another routine if sub-arcsec accuracy is critical.\n");
+	oprntf("Apparent place (with nutation, aberration) is NOT computed.\n");
+	oprntf("(Hit return to continue listing...)");
+	scanf("%c",&cdum);
+	oprntf("\n\nThe parallactic angle follows Filippenko (1982, PASP 94, 715).\n");
+	oprntf("\nThe barycentric ('heliocentric') corrections are computed using\n");
+	oprntf("an elliptical earth orbit with a few periodic perturbations\n");
+	oprntf("including lunar recoil.  The helio-to-barycentric transformation\n");
+	oprntf("uses the same algorithms as the planetary postions.  Overall max error:\n");
+	oprntf("< 0.2 sec and < 0.005 km/s.  Velocity corrn. includes earth rotation.\n");
+	oprntf("\nThe galactic coordinate routine is rigorously accurate, and\n");
+	oprntf("precesses to 1950 before transforming.  The ecliptic coord.\n");
+	oprntf("routine is for coordinates of date and is good to < 1 arcsec.\n\n");
+	oprntf("These routines are **not necessarily correct** at times very far from\n");
+	oprntf("the present (1990s).  The program rejects input outside 1900-2100.\n");
+	oprntf("\nWhen porting to a new machine, run tests to ensure\n");
+	oprntf("correctness and accuracy.  Experience shows that compiler\npeculiarities arise ");
+	oprntf("surprisingly often.\n");
+	oprntf("(Hit return to continue listing...)");
+	scanf("%c",&cdum);
+	oprntf("CAUTIONS, LEGALITIES:\n");
+	oprntf("Many routines take a time argument which is a double-precision\n");
+	oprntf("floating-point julian date; on most workstations, this gives\n");
+	oprntf("time resolution of < 0.1 second.  When porting\n");
+	oprntf("to another machine or compiler, test that the accuracy is\n");
+	oprntf("sufficient.  \n\n");
+	oprntf("I cannot guarantee that this program is bug-free, and caution\n");
+   /* although at this time I really don't know of any bugs ... ! */
+	oprntf("that not all routines are thoroughly precise and rigorous.\n");
+	oprntf("The user of this program is responsible for interpreting \n");
+	oprntf("results correctly.  I disavow any legal liability for damages\n");
+	oprntf("caused by use of this program.\n\n");
+	oprntf("Program copyright 1993, John Thorstensen, Dartmouth College\n");
+	oprntf("Permission hereby granted for scientific/educational use.\n");
+	oprntf("Commercial users must license.  Please communicate problems or\n");
+	oprntf("suggestions to the author, John Thorstensen, Dept. Physics\n");
+	oprntf("and Astronomy, Dartmouth College, Hanover NH 03755\n");
+	oprntf("John.Thorstensen@Dartmouth.edu\n");
+	oprntf("\nType command, or ? to see menu again.:");
+}
+
+void print_legalities()
+
+{
+	printf("\nThis has been superseded -- this shouldn't print out.\n");
+}
+
+
+void ephemgen(ra, dec, ep, lat, longit)
+	double ra, dec, ep, lat, longit;
+
+{
+
+/* Prompts for elements of an ephemeris, then
+   generates a printout of the *geocentric*
+   ephemeris of an object.  Uses current values of ra and dec;
+   ignores observatory parameters, which are much more important
+   for velocity than for time.  Not *strictly* correct in that
+   earth's position in helio-to-geo conversion is computed (strictly
+   speaking) for heliocentric jd, while ephemeris is for (presumably)
+   geocentric jd.  This makes a difference of at most 30 km/s  over c,
+   or about 1 in 10**4, for the heliocentric correction.  Pretty trivial.
+
+   Observatory paramaters are used in determining observability of
+   phenomenon.
+*/
+
+  double t0=0., per; /* ephemeris -- t0 + per * cycle count */
+  int ibeg, iend, cycles;
+  double curra, curdec, curep;
+  double max_secz, min_alt, max_alt, min_ok_alt, sun_alt, max_ok_sun, secz;
+  double sid, ha, altitude, az, rasun, decsun, hasun;
+  double jd, jdstart, jdend, tcor, vcor;
+  struct date_time tempdate;
+  int valid_date = 0;
+  short dow;
+
+  printf("Prints geocentric times of repeating phenom, e.g. stellar eclipses.\n");
+  printf("Not suitable for very short periods (seconds) -- too many events.\n");
+  printf("You may optionally specify that only events observable from your site\n");
+  printf("be printed.\n\n");
+  printf("Give heliocentric period, days; negative exits:");
+  scanf("%lf",&per);
+  if(per < 0.) return;
+  printf("Give heliocentric jd of time zero, *all* the digits!:");
+  scanf("%lf",&t0);
+  if(t0 < 2000000.) {
+	printf("Exiting -- JD *has* to be greater than 2 million!\n");
+	return;
+  }
+  printf("Give starting UT date for listing, yyyy mm dd, neg year exits:");
+  if((valid_date = get_date(&tempdate)) < 0) return;
+/*  printf("Give starting ut, h m s:");
+  get_time(&tempdate);   Too much of pain to enter .. force to ut = 0 */
+  tempdate.h = 0.;
+  tempdate.mn = 0.;
+  tempdate.s = 0.;
+  jdstart = date_to_jd(tempdate);
+  printf("Give ending date for listing:");
+  get_date(&tempdate);
+/*  printf("Give ending time:");
+  get_time(&tempdate);  */
+  jdend = date_to_jd(tempdate);
+  printf("Give maximum airmass for listing, negative to print all:");
+  scanf("%lf",&max_secz);
+  if(max_secz > 0.) min_ok_alt = 90. - DEG_IN_RADIAN * acos(1/max_secz);
+  else min_ok_alt = -100.;
+  curep = 2000. + (jdstart - J2000)/365.25;    /* precess to check observ. */
+  precrot(ra,dec,ep,curep,&curra,&curdec);
+  min_max_alt(lat,curdec,&min_alt,&max_alt);
+  if(max_alt < min_ok_alt) {
+  oprntf("Object never rises this high -- max alt %5.1f deg -- returning!\n",
+           max_alt);
+	return;
+  }
+
+  printf("Give maximum allowable sun altitude to print, 90 to print all:");
+  scanf("%lf",&max_ok_sun);
+
+  oprntf("\nObject RA = ");
+  put_coords(ra,3);
+  oprntf(", dec = ");
+  put_coords(dec,2);
+  oprntf(" epoch %6.1f\n",ep);
+  oprntf("Site long = ");
+  put_coords(longit,3);
+  oprntf("W (hr.mn.sec), lat = ");
+  put_coords(lat,2);
+  oprntf("N \n");
+  oprntf("\nEphemeris: %lf + %14.8f E (Heliocentric)\n\n",t0, per);
+  oprntf("Listing of events in interval from ");
+  print_calendar(jdstart,&dow);
+  oprntf(" UT to ");
+  print_calendar(jdend,&dow);
+  oprntf(" UT.\n");
+  if(min_ok_alt > 0.) oprntf("Only events at sec(z) < %5.2f are shown.\n",
+        max_secz);
+  if(max_ok_sun < 89.)
+	oprntf("Only events with sun altit. < %6.1f degrees are shown.\n\n",
+	max_ok_sun);
+  oprntf("First columns show cycle number, GEOCENTRIC jd, date and time (UT);\n");
+  oprntf("Last show HA, sec(z), 'ngt' if night, sun alt if twilit, 'day' if day.\n\n");
+  ibeg = (jdstart - t0) / per;  /* truncate */
+  iend = (jdend - t0) / per;    /* truncate again */
+
+  for(cycles = ibeg; cycles <= iend; cycles++) {
+ 	jd = t0 + (double) cycles * per;
+	curep = 2000. + (jd - J2000)/365.25;    /* precess to current */
+        precrot(ra,dec,ep,curep,&curra,&curdec);
+	helcor(jd, curra, curdec, 0., 0., 0., &tcor, &vcor);
+/*      oprntf("Raw = %12.4f, Correction: %f\n",jd,tcor);   diagnostic */
+        jd = jd - tcor / SEC_IN_DAY;  /* subtract to convert to geo .*/
+        sid = lst(jd,longit);
+        ha = adj_time(sid - curra);
+        altitude = altit(curdec,ha,lat,&az);
+	secz = secant_z(altitude);
+        if(altitude >= min_ok_alt) {
+           lpsun(jd,&rasun,&decsun);  /* lpsun plenty good enough */
+	   hasun = lst(jd,longit) - rasun;
+	   sun_alt = altit(decsun,hasun,lat,&az);
+           if(sun_alt < max_ok_sun) {
+              oprntf("# %d  JD(geo) %12.4f = ",cycles,jd);
+	      print_calendar(jd,&dow);
+	      oprntf(", ");
+              print_time(jd,3);
+	      oprntf(" UT");
+	      put_coords(ha,0);
+	      if((secz < 10.) && (secz > 0.)) oprntf(" %5.2f",secz);
+              else if (secz > 0.) oprntf(" v.low");
+	      else oprntf(" down!");
+	      if(sun_alt <= -18.) oprntf(" ngt");
+	      else if(sun_alt <= -0.53) oprntf(" %3.0f",sun_alt);
+	      else oprntf(" day");
+	      oprntf("\n");
+	   }
+       }
+  }
+  oprntf("Listing done.  Type ? if you need a menu.\n");
+}
+
+#define ALT_3  19.47  /* 19.47 degrees altitude => sec z = 3 */
+#define ALT_2  30.
+#define ALT_15 41.81
+#define SID_RATE 1.0027379093  /* sidereal / solar rate */
+
+double hrs_up(jdup, jddown, jdeve, jdmorn)
+
+	double jdup, jddown, jdeve, jdmorn;
+{
+
+   /* If an object comes up past a given point at jdup,
+      and goes down at jddown, and evening and morning
+      are at jdeve and jdmorn, computes how long
+      object is up *and* it's dark.  ... Written as
+      function 'cause it's done three times below. */
+
+   double jdup2, jddown0;  /* for the next ... or previous ...
+                            time around */
+
+   if(jdup < jdeve) {
+     if(jddown >= jdmorn)   /* up all night */
+	return ((jdmorn - jdeve) * 24.);
+     else if(jddown >= jdeve)  {
+	/* careful here ... circumpolar objects can come back *up*
+	   a second time before morning.  jdup and jddown are
+	   the ones immediately preceding and following the upper
+	   culmination nearest the center of the night, so "jdup"
+	   can be on the previous night rather than the one we want. */
+       jdup2 = jdup + 1.0/SID_RATE;
+       if(jdup2 > jdmorn)  /* the usual case ... doesn't rise again */
+	   return ((jddown - jdeve) * 24.);
+       else return(((jddown - jdeve) + (jdmorn - jdup2)) * 24.);
+     }
+     else return(0.);
+   }
+   else if(jddown > jdmorn) {
+     if(jdup >= jdmorn) return(0.);
+     else {
+	/* again, a circumpolar object can be up at evening twilight
+	    and come 'round again in the morning ... */
+        jddown0 = jddown - 1.0/SID_RATE;
+	if(jddown0 < jdeve) return((jdmorn - jdup) * 24.);
+        else return(((jddown0 - jdeve) + (jdmorn - jdup)) * 24.);
+     }
+   }
+   else return((jddown - jdup)*24.);  /* up & down the same night ...
+      might happen a second time in pathological cases, but this will
+      be extremely rare except at very high latitudes.  */
+}
+
+void print_air(secz,prec)
+    	double secz;
+	short prec;
+{
+   if((secz > 0.) && (secz < 100.)) {
+	if(prec == 0) oprntf(" %5.1f ",secz);
+	         else oprntf(" %5.2f ",secz);
+   }
+   else if(secz > 0.) oprntf(" v.low ");
+   else if(secz < 0.) oprntf("  down ");
+}
+
+void print_ha_air(ha,secz,prec1,prec2)
+	double ha, secz;
+	short prec1, prec2;
+{
+ 	put_coords(ha,prec1);
+	oprntf(" ");
+	print_air(secz,prec2);
+}
+
+void obs_season(ra, dec, epoch, lat, longit)
+
+   double ra, dec, epoch, lat, longit;
+
+/* prints a table of observability through an observing
+   season.  The idea is to help the observer come up
+   with an accurately computed "range of acceptable
+   dates", to quote NOAO proposal forms ... */
+
+{
+   int valid_date, nlun, nph, nch;
+   char obj_name[40];
+   short dow;
+   double sun_twi;
+   double jdstart, jdend, jd, jdtrunc, jdevedate;
+   double jdmid, jdcent, jdeve, jdmorn, midnfrac;
+   double steve,stmid,stcent,stmorn;
+   double hatwi, hasun, hacent, haeve, hamorn;
+   double seczcent, seczeve, seczmorn;
+   double dt15,dt2,dt3;
+   double jdtrans, jd3_1, jd3_2, jd2_1, jd2_2,
+	  jd15_1, jd15_2, rasun, decsun;
+   double hrs_3, hrs_2, hrs_15;
+   double min_alt, max_alt, altitude, az;
+   double curep, curra, curdec;
+   struct date_time tempdate;
+
+   printf("This types out a summary of the observability of your object\n");
+   printf("through the observing season.  Observability is summarized\n");
+   printf("at new & full moon through a range of dates you specify.\n");
+   printf("(a 24-line display holds about a 6-month range.)\n\n");
+   if(fabs(lat) > 70.) {
+	oprntf("NOTE: The site's geographical latitude is so high that there's\n");
+	oprntf("a possibility that 'darkness' will last all day (depending\n");
+        oprntf("on how you define the end of twilight below.)  In this case\n");
+	oprntf("the HA and sec z at 'twilight' will be for +-12 hours from\n");
+	oprntf("sun's lower culmination.\n\n");
+   }
+
+   printf("Give approx. starting date, yyyy mm dd, neg year exits:");
+
+  if((valid_date = get_date(&tempdate)) < 0) return;
+  tempdate.h =  0.;
+  tempdate.mn = 0.;
+  tempdate.s = 0.;
+  jdstart = date_to_jd(tempdate);
+  printf("Give ending date for listing:");
+  if((valid_date = get_date(&tempdate)) < 0) return;
+  jdend = date_to_jd(tempdate);
+  printf("You must now specify the altitude of the sun which defines twilight.\n");
+  printf("Type -18. , or give a non-standard choice:");
+  scanf("%lf",&sun_twi);
+
+  printf("Name of object:");     /* for labeling redirected output */
+  nch = get_line(obj_name);
+
+  jd = jdstart - lun_age(jdstart,&nlun);
+  nph = 0;  /* jd is adjusted to last previous new moon */
+
+  jdcent = (jd + jdend) / 2.;  /* temporary use of var */
+  curep = 2000. + (jdcent - J2000)/365.25;    /* precess to check observ. */
+  precrot(ra,dec,epoch,curep,&curra,&curdec);
+  min_max_alt(lat,curdec,&min_alt,&max_alt);
+
+  oprntf("\n          *** Seasonal Observability of %s ***\n",obj_name);
+  oprntf("\n     RA & dec: ");
+  put_coords(ra,3);
+  oprntf(", ");
+  put_coords(dec,2);
+  oprntf(", epoch %6.1f\n", epoch);
+  /*  printf("\nObject RA&dec: ");
+  put_coords(curra,3);
+  oprntf(", ");
+  put_coords(curdec,2);
+  oprntf(", epoch %6.1f\n", curep);  */
+
+  oprntf("Site long&lat: ");
+  put_coords(longit,3);
+  oprntf(" (h.m.s) West, ");
+  put_coords(lat,2);
+  oprntf(" North.\n\n");
+
+
+  if(max_alt < ALT_15)
+    oprntf("NOTE -- This object is always at sec.z > %5.2f\n",
+	1./cos((90. - max_alt) / DEG_IN_RADIAN));
+  if(min_alt > ALT_3)
+    oprntf("NOTE -- This object is always at sec.z < %5.2f\n",
+	1./cos((90. - min_alt) / DEG_IN_RADIAN));
+
+  oprntf("Shown: local eve. date, moon phase, hr ang and sec.z at (1) eve. twilight,\n");
+  oprntf("(2) natural center of night, and (3) morning twilight; then comes number of\n");
+  oprntf("nighttime hours during which object is at sec.z less than 3, 2, and 1.5.\n");
+  oprntf("Night (and twilight) is defined by sun altitude < %4.1f degrees.\n\n",sun_twi);
+
+  oprntf(" Date (eve) moon      eve            cent           morn");
+  oprntf("     night hrs@sec.z:\n");
+  oprntf("                   HA  sec.z      HA  sec.z      HA  sec.z");
+  oprntf("     <3   <2   <1.5\n");
+
+
+  while(jd <= jdend) {
+       if(nph == 0) nph = 2;
+       else if(nph == 2) {
+	  nlun++;
+	  nph = 0;
+       }
+       flmoon(nlun,nph,&jd);
+
+       /* Take care to compute for the date nearest full or new ...
+	  people may use this as a lunar calendar, which it sort of
+	  isn't. */
+
+       midnfrac = 0.5 + longit/24.;  /* rough fractional part of jd
+		       for local midnight ... */
+       jdtrunc = (double)((long) jd);
+       jdmid = jdtrunc + midnfrac;
+       if((jd - jdmid) > 0.5) jdmid = jdmid + 1.;
+       else if((jd - jdmid) < -0.5) jdmid = jdmid - 1.;
+
+       lpsun(jdmid,&rasun,&decsun);
+       hasun = adj_time(lst(jdmid,longit) - rasun); /* at rough midn. */
+       if(hasun > 0.) jdcent = jdmid + (12. - hasun) / 24.;
+       else jdcent = jdmid - (hasun + 12.) / 24.;
+	  /* jdcent is very close to sun's lower culmination
+	      -- natural center of night */
+
+       hatwi = ha_alt(decsun,lat,sun_twi);
+       if(hatwi > 100.) {
+	      jdeve = -1.;    /* signal -- no twilight */
+              jdmorn = -1.;
+       }
+
+       else {
+	  if(hatwi < -100.) {  /* always night */
+	        jdeve = jdcent - 0.5; /* sensible, anyway  */
+	        jdmorn = jdcent + 0.5;
+          }
+          else {
+		jdmorn = jd_sun_alt(sun_twi,(jdcent +(12.-hatwi)/24.),
+	           lat,longit);
+          	jdeve = jd_sun_alt(sun_twi,(jdcent - (12.-hatwi)/24.),
+	           lat,longit);
+          }
+          haeve = adj_time(lst(jdeve,longit) - curra);
+          altitude = altit(curdec,haeve,lat,&az);
+	  seczeve = secant_z(altitude);
+
+          hamorn = adj_time(lst(jdmorn,longit) - curra);
+          altitude = altit(curdec,hamorn,lat,&az);
+	  seczmorn = secant_z(altitude);
+       }
+
+       hacent = adj_time(lst(jdcent,longit) - curra);
+       altitude = altit(curdec,hacent,lat,&az);
+       seczcent = secant_z(altitude);
+
+       jdtrans = jdcent - hacent / (SID_RATE * 24.);
+	  /* this will be the transit nearest midnight */
+       if((min_alt < ALT_3) && (max_alt > ALT_3)) {
+	 /* if it makes sense to compute the times when
+	    this object passes three airmasses ... */
+	  dt3 = ha_alt(curdec,lat,ALT_3) / (SID_RATE * 24.);
+	  jd3_1 = jdtrans - dt3;
+	  jd3_2 = jdtrans + dt3;
+       }
+       else {
+	  jd3_1 = 0.;  jd3_2 = 0.;
+       }
+        if((min_alt < ALT_2) && (max_alt > ALT_2)) {
+	 /* if it makes sense to compute the times when
+	    this object passes two airmasses ... */
+	  dt2 = ha_alt(curdec,lat,ALT_2) / (SID_RATE * 24.);
+	  jd2_1 = jdtrans - dt2;
+	  jd2_2 = jdtrans + dt2;
+       }
+       else {
+	  jd2_1 = 0.;  jd2_2 = 0.;
+       }
+       if((min_alt < ALT_15) && (max_alt > ALT_15)) {
+	 /* if it makes sense to compute the times when
+	    this object passes 1.5 airmasses ... */
+	  dt15 = ha_alt(curdec,lat,ALT_15) / (SID_RATE * 24.);
+	  jd15_1 = jdtrans - dt15;
+	  jd15_2 = jdtrans + dt15;
+       }
+       else {
+	  jd15_1 = 0.;  jd15_2 = 0.;
+       }
+
+   /* Now based on times of twilight and times at which object
+      passes the airmass points, figure out how long the object
+      is up at night ... */
+
+      if(jdeve > 0.) {  /* if twilight occurs ... */
+         if(jd2_1 != 0.)
+	   hrs_2 = hrs_up(jd2_1, jd2_2,jdeve,jdmorn);
+         else if(min_alt > ALT_2)
+	   hrs_2 = 24. * (jdmorn - jdeve);  /* always up ... */
+         else hrs_2 = 0.;                   /* never up ... */
+
+         if(jd3_1 != 0.)
+	   hrs_3 = hrs_up(jd3_1, jd3_2,jdeve,jdmorn);
+         else if(min_alt > ALT_3)
+	   hrs_3 = 24. * (jdmorn - jdeve);
+         else hrs_3 = 0.;
+
+         if(jd15_1 != 0.)
+           hrs_15 = hrs_up(jd15_1, jd15_2,jdeve,jdmorn);
+         else if(min_alt > ALT_15)
+	   hrs_15 = 24. * (jdmorn - jdeve);
+         else hrs_15 = 0.;
+      }
+      else if(jdeve > -500.) {  /* twilight all night */
+	  hrs_2 = 0.;
+	  hrs_3 = 0.;
+	  hrs_15 = 0.;
+      }
+
+
+       /* now print the table itself.  Take pains to
+	  get the correct *evening* date .... */
+
+       jdevedate = jdcent - longit/24. - 0.25;
+       planet_alert(jdcent,curra,curdec,PLANET_TOL);
+         /* better know about it ... */
+
+       print_calendar(jdevedate,&dow);
+
+       /* space table correctly -- what a pain ! */
+       caldat(jdevedate,&tempdate,&dow);
+       if(tempdate.d < 10) oprntf(" ");
+
+       if(nph == 0) oprntf("   N");
+       else oprntf("   F");
+       if(jdeve > 0.) print_ha_air(haeve,seczeve,0,0);
+       else oprntf(" twi.all.nght! ");
+       print_ha_air(hacent,seczcent,0,0);
+       if(jdmorn > 0.) print_ha_air(hamorn,seczmorn,0,0);
+       else oprntf(" twi.all.nght! ");
+
+       oprntf(" %4.1f  %4.1f  %4.1f \n",hrs_3,hrs_2,hrs_15);
+
+   }
+   printf("Listing done.  'f' gives tutorial, '?' prints a menu.\n");
+}
+
+#if SYS_CLOCK_OK == 1
+#include <time.h>
+#endif
+
+#if SYS_CLOCK_OK == 1
+
+int get_sys_date(date, use_dst, enter_ut, night_date, stdz, toffset)
+
+	struct date_time *date;
+	short use_dst, enter_ut, night_date;
+        double stdz, toffset;
+{
+	/* Reads the system clock; loads up the date structure
+           to conform to the prevailing conventions for the interpretation
+           of times.  Optionally adds "toffset" minutes to the system
+           clock, as in x minutes in the future. */
+
+	time_t t, *tp;
+	struct tm *stm;
+        double jd, jdb, jde;
+        short dow;
+
+	tp = &t;  /* have to initialize pointer variable for it to
+                     serve as an argument. */
+
+	t = time(tp);
+	if(t == -1) {
+		oprntf("SYSTEM TIME UNAVAILABLE!  Date & time left alone.\n");
+		return(-1);
+        }
+	stm = localtime(&t);
+	date->y = (short) (stm->tm_year + 1900);
+	date->mo = (short) (stm->tm_mon + 1);
+	date->d = (short) (stm->tm_mday);
+	date->h = (short) (stm->tm_hour);
+	date->mn = (short) (stm->tm_min);
+	date->s = (float) (stm->tm_sec);
+
+	if(toffset != 0.) {
+	   jd = date_to_jd(*date);
+	   jd = jd + toffset / 1440.;
+	   caldat(jd,date,&dow);
+        }
+
+        if(enter_ut == 1)  { /* adjust if needed */
+           find_dst_bounds(date->y,stdz,use_dst,&jdb,&jde);
+	   jd = date_to_jd(*date);
+	   jd = jd + zone(use_dst,stdz,jd,jdb,jde)/24.;
+           caldat(jd,date,&dow);
+        }
+	oprntf("\nDATE AND TIME SET AUTOMATICALLY USING SYSTEM CLOCK ... to\n");
+	print_all(date_to_jd(*date));
+	if(enter_ut == 0) oprntf(" local time\n");
+        else oprntf(" Universal time\n");
+      	if((night_date == 1) && (date->h < 12)) {
+		date->d = date->d - 1;
+		oprntf("(night-date is on, so internal value of date set to last evening)\n");
+	}
+	oprntf("(NOTE: time does NOT automatically update; 'T' reads clock again.)\n");
+        return(0); /* success */
+}
+
+#endif     /* previous routine depends on sys clock */
+
+void indexx(n,arrin,indx)
+
+/* Sort routine from Press et al., "Numerical Recipes in C",
+  1st edition, Cambridge University Press. */
+
+int n,indx[];
+float arrin[];
+{
+	int l,j,ir,indxt,i;
+	float q;
+
+	for (j=1;j<=n;j++) indx[j]=j;
+	l=(n >> 1) + 1;
+	ir=n;
+	for (;;) {
+		if (l > 1)
+			q=arrin[(indxt=indx[--l])];
+		else {
+			q=arrin[(indxt=indx[ir])];
+			indx[ir]=indx[1];
+			if (--ir == 1) {
+				indx[1]=indxt;
+				return;
+			}
+		}
+		i=l;
+		j=l << 1;
+		while (j <= ir) {
+			if (j < ir && arrin[indx[j]] < arrin[indx[j+1]]) j++;
+			if (q < arrin[indx[j]]) {
+				indx[i]=indx[j];
+				j += (i=j);
+			}
+			else j=ir+1;
+		}
+		indx[i]=indxt;
+	}
+}
+
+struct objct {
+	char name[20];
+	double ra;
+	double dec;
+	float ep;
+	float xtra;  /* mag, whatever */
+};
+
+struct objct objs[MAX_OBJECTS];
+int nobjects;
+
+int read_obj_list()
+
+/* Reads a list of objects from a file.  Here's the rules:
+     -- each line of file  must be formatted as follows:
+
+name   rahr ramn rasec   decdeg decmin decsec   epoch   [optional number]
+
+    -- the name cannot have any spaces in it.
+    -- if the declination is negative, the first character of the
+         declination degrees must be a minus sign, and there can
+         be no blank space after the minus sign (e. g., -0 18 22 is ok;
+	 - 12 13 14 is NOT ok).
+    -- other than that, it doesn't matter how you space the lines.
+
+    I chose this format because it's the standard format for
+    pointing files at my home institution.  If you'd like to code
+    another format, please be my guest!  */
+
+{
+        FILE *inf;
+	char fname[60], resp[10];
+	char buf[200];
+	char decstr[10];
+	double rah, ram, ras, ded, dem, des, ept;
+        int i, nitems;
+	float xtr;
+
+	printf("\nThis reads from a file of objects.  Format is as follows,\n\n");
+	printf("name_no_blanks<20char   hr mn sec  deg mn sec  epoch  [opt._user_float]\n\n");
+	printf("with exactly 1 object per line, blanks between fields, otherwise free-form.\n");
+        printf("Anything after the optional user-defined floating pt number is ignored.\n");
+	printf("Error checking is unsophisticated; maximum of %d objects.\n\n",
+		(MAX_OBJECTS-1));
+	printf("Give name of file of objects (or QUIT):");
+	scanf("%s",fname);
+
+	if(strcmp(fname,"QUIT") == 0) {
+		printf("Quitting ... did not attempt to open a file.\n");
+		return(-1);
+	}
+
+	inf = fopen(fname,"r");
+
+	if(inf == NULL) {
+		printf("\nFILE DID NOT OPEN!  ... I quit.  Try again if you want.\n");
+		return(-1);
+	}
+	else printf("\nopened ... \n");
+
+	if(nobjects != 0) {
+		printf("\nYou have %d objects already!\n",nobjects);
+	        printf("Type a to append, or r to replace:");
+		scanf("%s",resp);
+		if(resp[0] == 'r') nobjects = 0;
+	}
+        /*  on first pass be sure xtra's have a value, just in case. */
+        else for(i = 1; i < MAX_OBJECTS; i++) objs[i].xtra = 0.0;
+
+	while((fgets(buf,200,inf) != NULL) && (nobjects < MAX_OBJECTS - 1)) {
+		nobjects++;   /* this will be 1-indexed */
+		nitems = sscanf(buf,"%s %lf %lf %lf  %s %lf %lf  %lf %f",
+			objs[nobjects].name,&rah,&ram,&ras,decstr,&dem,&des,
+				&ept,&xtr);
+		if(nitems >= 8) {  /* a little error checking here ... */
+	             objs[nobjects].ra = rah + ram/60. + ras/3600.;
+		     sscanf(decstr,"%lf",&ded);   /* careful with "-0" */
+		     if(decstr[0] == '-') {
+			if(ded <= 0.)
+				ded = ded * -1.;
+			objs[nobjects].dec =
+					-1.* (ded + dem/60. + des/3600.);
+		     }
+	             else objs[nobjects].dec = ded + dem/60. + des/3600.;
+                     objs[nobjects].ep = ept;
+		     if(nitems == 9) objs[nobjects].xtra = xtr;
+		     else if(nitems == 8) objs[nobjects].xtra = 99.9;
+                }
+	        else {
+		     printf("Ignoring bad line: %s",buf);
+		     nobjects--;
+	        }
+        }
+	printf("\n .... %d objects read from file.\n",nobjects);
+	if(nobjects == MAX_OBJECTS - 1)
+	  printf("** WARNING ** AT MAX NUMBER OF OBJECTS. You may have missed some.\n");
+	fclose(inf);
+	return(0);  /* success */
+}
+
+int find_by_name(ra, dec, epoch, date, use_dst, enter_ut, night_date, stdz,
+		lat, longit)
+	double *ra, *dec, epoch, stdz, lat, longit;
+	struct date_time date;
+	short use_dst, enter_ut, night_date;
+
+{
+
+	/* finds object by name in list, and sets ra and dec to
+           those coords if found.  Precesses to current value of
+           epoch. */
+
+	char objname[20];
+	int i, found = 1;
+	double jd, curep, curra, curdec, sid, ha, alt, az, secz, precra, precdec;
+
+	if(nobjects == 0) {
+		printf("No objects!\n");
+		return;
+	}
+
+	jd = true_jd(date, use_dst, enter_ut, night_date, stdz);
+
+	if(jd < 0.) {
+		printf(" ... exiting because of time error!  Try again.\n");
+		return(-1);
+	}
+
+	sid = lst(jd, longit);
+	curep = 2000. + (jd - J2000) / 365.25;
+
+	printf("RA and dec will be set to list object you name.\n");
+	printf("Give exact object name:");
+	scanf("%s",objname);
+
+	i = 1;
+	while((i <= nobjects) &&
+		((found = strcmp(objs[i].name,objname)) != 0)) i++;
+        if(found == 0) {
+		if(objs[i].ep != epoch) {
+		        precrot(objs[i].ra,objs[i].dec,objs[i].ep,
+                                epoch,&precra,&precdec);
+		}
+		else {
+			precra = objs[i].ra;
+			precdec = objs[i].dec;
+		}
+		*ra = precra;
+		*dec = precdec;
+		printf("\nObject found -- name, coords, epoch, user#, HA, airmass --- \n\n");
+		printf("%s  ",objs[i].name);
+		put_coords(objs[i].ra,3);
+		printf("  ");
+		put_coords(objs[i].dec,2);
+		printf("  %6.1f  %5.2f ",objs[i].ep,objs[i].xtra);
+               	precrot(objs[i].ra,objs[i].dec,
+				objs[i].ep,curep,&curra,&curdec);
+    		ha = adj_time(sid - curra);
+ 		alt=altit(curdec,ha,lat,&az);
+		secz = secant_z(alt);
+		print_ha_air(ha,secz,0,1);
+		printf("\n\n COORDINATES ARE NOW SET TO THIS OBJECT.\n");
+		if(objs[i].ep != epoch)
+		printf("(RA & dec have been precessed to %6.1f, your standard input epoch.)\n",
+			epoch);
+		return(0);
+	}
+	else {
+		printf("Not found in %d entries.\n",i);
+		return(-1);
+	}
+}
+
+void type_list(date, use_dst, enter_ut, night_date, stdz,
+		lat, longit)
+     	double stdz, lat, longit;
+	struct date_time date;
+	short use_dst, enter_ut, night_date;
+
+
+{
+	int i;
+	double jd, curep, curra, curdec, sid, ha, alt, az, secz;
+	short nstart = 1, nend, ok;
+	char errprompt[40];
+
+	if(nobjects == 0) {
+		printf("No objects!\n");
+		return;
+	}
+
+	jd = true_jd(date, use_dst, enter_ut, night_date, stdz);
+
+	if(jd < 0.) {
+		printf(" ... exiting because of time error!  Try again.\n");
+		return;
+	}
+
+	sid = lst(jd, longit);
+	curep = 2000. + (jd - J2000) / 365.25;
+
+	printf("(Listing will show name, coords, epoch, user#, HA and airmass.)\n");
+
+	strcpy(errprompt,"ERROR IN INPUT ... ");
+	oprntf("%d objects in list.\n",nobjects);
+/*	while((nobjects > 0) && (nstart > 0)) { used to loop -- too tricky. */
+	   /* get out the heavy input checking artillery to avoid
+                running away here ... */
+	  /*      printf("First and last (numbers) to list, -1 exits:"); */
+	        printf("First and last (numbers) to list:");
+		ok = getshort(&nstart,-1,(short)nobjects,errprompt);
+		/* if(nstart < 0) break; */
+		ok = getshort(&nend,nstart,(short)nobjects,errprompt);
+		if(nend > nobjects) nend = nobjects;
+		if(nstart > nend) nstart = nend;
+		oprntf("\n\n");
+		print_current(date,night_date,enter_ut);
+		oprntf("\n");
+                for(i = nstart; i <= nend; i++) {
+ 			oprntf("%20s ",objs[i].name);
+			put_coords(objs[i].ra,3);
+			oprntf("  ");
+			put_coords(objs[i].dec,2);
+			oprntf("   %6.1f  %5.3f ",objs[i].ep,objs[i].xtra);
+               		precrot(objs[i].ra,objs[i].dec,
+				objs[i].ep,curep,&curra,&curdec);
+    			ha = adj_time(sid - curra);
+			alt=altit(curdec,ha,lat,&az);
+			secz = secant_z(alt);
+			print_ha_air(ha,secz,0,1);
+			oprntf("\n");
+		}
+		oprntf("\n");
+/*	}*/
+}
+
+int find_nearest(ra, dec, epoch, date, use_dst, enter_ut, night_date, stdz,
+		lat, longit)
+
+	double *ra, *dec, epoch, stdz, lat, longit;
+	struct date_time date;
+	short use_dst, enter_ut, night_date;
+
+/* given ra,dec, and epoch, sorts items in list with
+   respect to arc distance away, and queries user
+   whether to accept.  */
+
+{
+ 	int i, ind[MAX_OBJECTS];
+	double precra, precdec, jd, sid, ha, alt, az,
+		secz, seczob, curra, curdec, curep, hacrit, altcrit, aircrit;
+	float arcs[MAX_OBJECTS];
+	char resp[10];
+	int found = 0;
+        short sortopt,nprnt;
+
+	if(nobjects == 0) {
+		printf("No objects!\n");
+		return(-1);
+	}
+
+	jd = true_jd(date, use_dst, enter_ut, night_date, stdz);
+
+	if(jd < 0.) {
+		printf(" ... exiting because of time error!  Try again.\n");
+		return(-1);
+	}
+
+	printf("Select:\n");
+	printf("  1 .. to rank objects by arc dist from current coords;\n");
+	printf("  2 .. to rank objects by absolute value of hour angle:\n");
+	printf("  3 .. sort by proximity in airmass to present coords:\n");
+	printf("  4 .. sort by imminence of setting (reaching critical airmass):\n");
+        printf("  5 .. sort by user-supplied optional number.\n");
+	printf("  Type 1, 2, 3, 4, or 5 ---> ");
+	scanf("%hd",&sortopt);
+
+	if(sortopt == 4) {
+		printf("Give critical airmass in west:");
+	        scanf("%lf",&aircrit);
+		if(aircrit < 1.) {
+			oprntf("Airmass must be > 1. ... exiting!\n");
+			return(-1);
+		}
+		altcrit = DEG_IN_RADIAN * asin(1.0 / aircrit);
+	}
+	sid = lst(jd, longit);
+	curep = 2000. + (jd - J2000) / 365.25;
+
+	 /* compute present airmass for option 3 */
+        if(sortopt == 3) {
+	    precrot(*ra,*dec,epoch,
+                         curep,&curra,&curdec);
+	    ha = adj_time(sid - curra);
+	    alt=altit(curdec,ha,lat,&az);
+	    seczob = secant_z(alt);
+        }
+
+	for(i = 1; i <= nobjects; i++) {
+		if(sortopt == 1) {   /* sort by arc distance */
+		   if(objs[i].ep != epoch)
+ 	  		precrot(objs[i].ra,objs[i].dec,objs[i].ep,
+                                epoch,&precra,&precdec);
+ 		   else {
+			precra = objs[i].ra;
+			precdec = objs[i].dec;
+		   }
+	           arcs[i] = subtend(*ra,*dec,precra,precdec);
+		}
+		else if (sortopt == 2) {  /* sort by hour angle */
+		   precrot(objs[i].ra,objs[i].dec,objs[i].ep,
+				curep,&curra,&curdec);
+	           arcs[i] = fabs(sid - curra);
+		}
+		else if (sortopt == 3) {  /* sort by difference of airmass */
+		   precrot(objs[i].ra,objs[i].dec,objs[i].ep,
+				curep,&curra,&curdec);
+    		   ha = adj_time(sid - curra);
+		   alt=altit(curdec,ha,lat,&az);
+	    	   arcs[i] = fabs(secant_z(alt) - seczob);
+		}
+                else if (sortopt == 4) {  /* sort by proximity to critical airmass */
+		   precrot(objs[i].ra,objs[i].dec,objs[i].ep,
+				curep,&curra,&curdec);
+		   hacrit = ha_alt(curdec,lat,altcrit);
+                   if(fabs(hacrit) > 24.) arcs[i] = 100.;
+    		   else {
+	         	ha = adj_time(sid - curra);
+                        arcs[i] = hacrit - ha;
+		        if(arcs[i] < 0.) arcs[i] = 100.;
+		   }
+               }
+               else   /* sort by user-supplied extra number */
+		   arcs[i] = (double) objs[i].xtra;
+	}
+
+	indexx(nobjects,arcs,ind);
+
+	printf("If you now select an object, RA & dec will be set to its coords.\n\n");
+	if(sclogfl != NULL) fprintf(sclogfl,"\n\n *** Sorted object listing *** \n");
+	oprntf("Listed for each: Name, ra, dec, epoch, user-defined #,\n");
+	if(sortopt == 1) oprntf("arclength to coords (deg), ");
+        if(sortopt == 4) oprntf("minutes til crit secz, ");
+	oprntf("HA, secz, computed for the following date & time:\n\n");
+	print_current(date,night_date,enter_ut);
+	oprntf("\n\n");
+	if((sortopt != 2) && (sortopt != 4)) { /* print relevant info */
+	    oprntf("Current coords: ");
+	    put_coords(*ra,3);
+	    oprntf("  ");
+            put_coords(*dec,2);
+	    oprntf("  %6.1f ",epoch);
+	    precrot(*ra,*dec,epoch,
+                                curep,&curra,&curdec);
+	    ha = adj_time(sid - curra);
+	    alt=altit(curdec,ha,lat,&az);
+	    seczob = secant_z(alt);
+	    print_ha_air(ha,seczob,0,1);
+	    oprntf("\n\n");
+	}
+
+	i = 1;
+	while(found == 0) {
+	    for(nprnt=1;nprnt<=10;nprnt++) {
+		precrot(objs[ind[i]].ra,objs[ind[i]].dec,
+				objs[ind[i]].ep,
+                                   curep,&curra,&curdec);
+    		ha = adj_time(sid - curra);
+		alt=altit(curdec,ha,lat,&az);
+
+ 		oprntf("%2d %13s",i,objs[ind[i]].name);
+		put_coords(objs[ind[i]].ra,3);
+		oprntf(" ");
+		put_coords(objs[ind[i]].dec,2);
+		oprntf(" %6.1f %6.2f ",objs[ind[i]].ep, objs[ind[i]].xtra);
+		if(sortopt == 1) oprntf(" %6.3f",arcs[ind[i]] * DEG_IN_RADIAN);
+                if(sortopt == 4) oprntf(" %5.0f",arcs[ind[i]] * 60.);
+		secz = secant_z(alt);
+		print_ha_air(ha,secz,0,1);
+                oprntf("\n");
+                if(nprnt == 5) oprntf("\n");
+		i++;
+	        if(i > nobjects) break;
+            }
+	    printf("Type number to select an object, m to see more, q to quit:");
+
+ 	    scanf("%s",resp);
+ 	    if(resp[0] == 'q') {
+			oprntf("Abandoning search.\n");
+			return(found = -1);
+	    }
+	    else if((resp[0] == 'm') || (resp[0] == 'M')) {
+		if(i > nobjects) {
+			oprntf("Sorry -- that's all you have!\n");
+			oprntf("Search abandoned.\n");
+	        	return(found = -1);
+                }
+	    }
+	    else if(isdigit(resp[0]) != 0) {
+		sscanf(resp,"%d",&i);
+		if((i < 0) || (i > nobjects)) {
+		     	oprntf("BAD OBJECT INDEX -- %d -- start over!\n",i);
+			return(-1);
+		}
+ 		if(objs[ind[i]].ep != epoch)
+			     precrot(objs[ind[i]].ra,objs[ind[i]].dec,
+				objs[ind[i]].ep,
+                                   epoch,&precra,&precdec);
+		else {
+			precra = objs[ind[i]].ra;
+			precdec = objs[ind[i]].dec;
+		}
+                *ra = precra;
+		*dec = precdec;
+		oprntf("\n%s  ",objs[ind[i]].name);
+		put_coords(objs[ind[i]].ra,3);
+		oprntf("  ");
+		put_coords(objs[ind[i]].dec,2);
+		oprntf("  %6.1f  %5.2f ",objs[ind[i]].ep,objs[ind[i]].xtra);
+               	precrot(objs[ind[i]].ra,objs[ind[i]].dec,
+				objs[ind[i]].ep,curep,&curra,&curdec);
+    		ha = adj_time(sid - curra);
+ 		alt=altit(curdec,ha,lat,&az);
+		secz = secant_z(alt);
+		print_ha_air(ha,secz,0,1);
+		oprntf("\n\n COORDINATES ARE NOW SET TO THIS OBJECT.\n");
+		if(objs[ind[i]].ep != epoch)
+   		oprntf("(RA & dec have been precessed to %6.1f, your current standard epoch.)\n",
+				epoch);
+		return(found = 1);
+	    }
+	    else {
+		printf("Unrecognized response ... continuing ..\n");
+		if(i > nobjects) {
+			printf("That's all the objects .. abandoning search.\n");
+			return(found = -1);
+		}
+            }
+       }
+}
+
+void set_zenith(date, use_dst, enter_ut, night_date, stdz, lat,
+	  longit, epoch, ra, dec)
+
+struct date_time date;
+short use_dst, enter_ut, night_date;
+double stdz, lat, longit, epoch, *ra, *dec;
+
+/* sets RA and dec to zenith as defined by present time and date;
+   coords are set to actual zenith but in currently used epoch.  */
+
+{
+	double jd, curep;
+
+	jd = true_jd(date, use_dst, enter_ut, night_date, stdz);
+
+	if(jd < 0.) return;  /* nonexistent time. */
+
+	*ra = lst(jd, longit);
+
+        *dec = lat;
+
+	curep = 2000. + (jd - J2000) / 365.25;
+
+	precrot(*ra,*dec,curep,epoch,ra,dec);
+
+	oprntf("\nCOORDINATES SET TO ZENITH: ");
+	put_coords(*ra,3);
+	oprntf(", ");
+	put_coords(*dec,2);
+	oprntf(", epoch %6.1f\n",epoch);
+}
+
+
+void printephase(date, use_dst, enter_ut, night_date, stdz, lat,
+	  longit, epoch, ra, dec)
+
+/* prints phase of a repeating phenomenon at this instant. */
+
+struct date_time date;
+short use_dst, enter_ut, night_date;
+double stdz, lat, longit, epoch, ra, dec;
+
+{
+ 	double P=0., T0=0.;
+	double phase, jd, hjd, tcor, vcor, curep, curra, curdec;
+
+	while(P == 0.) {
+		printf("Give period (days) of repeating phenom.:");
+		getdouble(&P,1.0e-5,1.0e6,"Give period again:");
+	}
+	while(T0 == 0.) {
+		printf("Give epoch T-0 (Julian date) :");
+		getdouble(&T0,1500000.,3.0e6,"Give epoch again:");
+	}
+
+	jd = true_jd(date, use_dst, enter_ut, night_date, stdz);
+
+	if(jd < 0.) {
+		printf("nonexistent time.\n");
+	        return;
+	}
+
+ 	oprntf("\n\nRA & dec: ");
+  	put_coords(ra,3);
+  	oprntf(", ");
+  	put_coords(dec,2);
+  	oprntf(", epoch %6.1f\n", epoch);
+	oprntf("Ephemeris: %lf + %14.8f E (Heliocentric)\n",T0, P);
+	oprntf("Julian date %f --> UT ",jd);
+	print_all(jd);
+	oprntf("\n\n");
+
+	curep = 2000. + (jd - J2000) / 365.25;
+
+	precrot(ra,dec,epoch,curep,&curra,&curdec);
+
+	helcor(jd,curra,curdec,0.,0.,0.,&tcor,&vcor);
+		/* throwing away vcor .... tcor doesn't need ha, lat,
+			or elevation. */
+	hjd = jd + tcor/86400.;
+
+	phase = (jd - T0) / P;
+	oprntf("If input time already helio, phase is %f\n",phase);
+	if(phase < 0.) {
+		phase = phase - (int) phase + 1;
+		oprntf(" ... i.e., %f\n",phase);
+	}
+	phase = (hjd - T0) / P;
+	oprntf("Making helio correction for current coords, phase is %f\n",
+		phase);
+	if(phase < 0.) {
+		phase = phase - (int) phase + 1;
+		oprntf(" ... i.e., %f\n",phase);
+	}
+	oprntf("(Helio correction is %f seconds, helio J.D. is %lf)\n",
+		tcor,hjd);
+
+}
+
+int set_to_jd(date, use_dst, enter_ut, night_date, stdz, jd)
+
+	struct date_time *date;
+	short use_dst, enter_ut, night_date;
+        double stdz, jd;
+{
+	/* Takes a jd and loads up the date structure
+           to conform to the prevailing conventions for the interpretation
+           of times. */
+
+        short dow;
+        double jdb, jde, jdloc;
+
+	if((jd < FIRSTJD) || (jd > LASTJD)) {
+		oprntf("JD out of range .. allowed %f to %f\n",
+			FIRSTJD,LASTJD);
+		oprntf("(Corresponds to 1901 --> 2099 calendrical limits.\n");
+		oprntf("No action taken ... try again if you want.\n");
+		return(-1);
+	}
+
+	caldat(jd,date,&dow);   /* first get year (at least) */
+
+        if(enter_ut != 1)  { /* adjust if needed */
+           find_dst_bounds(date->y,stdz,use_dst,&jdb,&jde);
+	   jd = date_to_jd(*date);
+	   jdloc = jd - zone(use_dst,stdz,jd,jdb,jde)/24.;
+	   if(use_dst > 0) {  /* north ... daylight savings */
+	      if((jde - jd) < 0.041666667 && (jde - jd) > 0.) {
+ 		oprntf("\n\n IN AMBIGUOUS DST/STD HOUR -- TIME WILL NOT BE CORRECT \n");
+	        oprntf("\n   ... use 'g' to set UT input, then do over.\n\n");
+	      }
+	   }
+	   else if(use_dst < 0) {  /* south ... daylight savings */
+	      if((jdb - jd) < 0.041666667 && (jdb - jd) > 0.) {
+ 		oprntf("\n\n IN AMBIGUOUS DST/STD HOUR -- TIME WILL NOT BE CORRECT \n");
+	        oprntf("\n   ... use 'g' to set UT input, then do over.\n\n");
+	      }
+	   }
+           caldat(jdloc,date,&dow);
+        }
+	oprntf("\nDATE AND TIME SET FROM JD ... to\n");
+	print_all(date_to_jd(*date));
+	if(enter_ut == 0) oprntf(" local time\n");
+        else oprntf(" Universal time\n");
+      	if((night_date == 1) && (date->h < 12)) {
+		date->d = date->d - 1;
+		oprntf("(night-date is on, so internal value of date set to last evening)\n");
+	}
+        return(0); /* success */
+}
+
+
+main()
+
+{
+	struct date_time date,dateback;
+	struct coord ttime;
+	double jd, jdmid, jdc;
+	double jdb, jde, test;  /* jd of begin and end of dst */
+	double sid, sss;
+	double Dt; /* ephemeris time correction */
+	short option, trying;
+	short done = 0, optdone = 0, valid_date = 0, nreturns=0, nxret = 0;
+	short day, yearday, dst=0, dow;
+	int cc, end_in;  /* control character for circumstances loop */
+	int cx;    /* control character for extra goodies ... */
+        char cxstr[3];
+	double objra=0., objdec=0., objepoch=1950.,dectest;
+	double curra, curdec, curep, obj_moon;
+	double pra[10],pdec[10];
+	double alt, az, ha, secz, jdloc;
+	double rasun,decsun,ramoon,decmoon,distmoon;
+	short enter_ut = 0; /* are times to be entered as UT? */
+	short night_date = 1; /* interperet current date as evening or true? */
+	char str[80]; /* dummy string */
+	char errprompt[80];
+	short nch;
+	double glong, glat, eclong, eclat, par;
+	int status;
+	double mura_sec=0.,mura_arcs=0.,mudec=0.;  /* proper motions */
+	double objra_adj, objdec_adj;           /* equinox of std epoch,
+			adjusted for proper motion only */
+	short hr_span,i;  /* for table of hour angles */
+	double tcor, vcor; /* time and velocity helio corrections */
+	char obs_code;
+	double minoffset; /* minutes offset from system clock ... */
+
+	/* all the site-specific quantities are here:
+		longit     = W longitude in decimal hours
+		lat        = N latitude in decimal degrees
+		stdz       = standard time zone offset, hours
+		use_dst    = 1 for USA DST, 2 for Spanish, negative for south,
+				 0 to use standard time year round
+		zone_name  = name of time zone, e. g. Eastern
+		zabr       = single-character abbreviation of time zone
+		site_name  = name of site.
+	*/
+
+	/* Kitt peak, MDM observatory, is initialized here as a default.
+	   User later gets to choose from a menu of possible sites -
+	   they're all hard-coded in the routine 'load_site'. */
+
+	char site_name[45];  /* initialized later with strcpy for portability */
+	char zabr = 'M';
+	char zone_name[25]; /* this too */
+	short use_dst = 0;
+	double longit = 7.44111;
+	double elevsea = 1925.;  /* for MDM, strictly */
+	double elev = 500.; /* well, sorta -- height above horizon */
+	double horiz = 0.7174;
+	double lat = 31.9533;
+	double stdz = 7.;
+
+	/* and off we go.... */
+
+	strcpy(site_name,"Kitt Peak");
+	strcpy(zone_name,"Mountain");
+
+	oprntf("\nAstronomical calculator program, by John Thorstensen.\n\n");
+
+	load_site(&longit,&lat,&stdz,&use_dst,zone_name,&zabr,
+			&elevsea,&elev,&horiz,site_name);
+	oprntf("You have selected %s\n",site_name);
+        oprntf("(This can be changed later using the 's' [site] command.\n\n");
+
+#if SYS_CLOCK_OK == 1
+
+        if(get_sys_date(&date,use_dst,enter_ut,night_date,stdz,0.) != 0) {
+	  date.y = 2000;  /* have to have a default date.*/
+	  date.mo = 1;
+	  date.d = 1;
+	  date.h = 0.;
+	  date.mn = 0.;
+	  date.s = 0.;
+	  oprntf("SYSTEM CLOCK didn't read. Time & date set arbitrarily to\n");
+	  print_all(date_to_jd(date));
+	  oprntf("\n");
+        }
+
+        else set_zenith(date, use_dst, enter_ut, night_date, stdz, lat,
+	  longit, objepoch, &objra, &objdec);
+
+#else
+       	  date.y = 2000;  /* have to have a default date.*/
+	  date.mo = 1;
+	  date.d = 1;
+	  date.h = 0.;
+	  date.mn = 0.;
+	  date.s = 0.;
+	  oprntf("SYSTEM CLOCK options turned off, so \n ");
+	  oprntf("time and date set arbitrarily to:\n");
+	  print_all(date_to_jd(date));
+	  oprntf("\n\n");
+#endif
+
+        oprntf("\nREADY TO COMPUTE.  Use simple commands to set the date, time, RA\n");
+        oprntf("dec, and so on; then use other commands to compute and display\n");
+        oprntf("observability information.\n\n");
+        oprntf("NEW or RUSTY USERS: type 'f' (and return) for FAST guided tour.\n");
+
+	while((cc = getch()) != 'Q')    switch(cc) {
+		case '?':    /* print a menu */
+			print_menu();
+			nreturns=0;
+			break;
+                case 'f':    /* print a short tutorial */
+                        print_tutorial();
+			nreturns=0;
+			break;
+		case 'r':   /* enter the object's right ascension */
+			objra = get_coord();
+			nreturns=0;
+			break;
+		case 'd':   /* enter the object's declination */
+			/* filter declination in put for 'date' input! */
+			dectest = get_coord();
+			if(fabs(dectest) <= 90.) {
+				objdec = dectest;
+			}
+			else {
+			    oprntf("REJECTED 'd' INPUT - DECLINATION MUST BE < 90.\n");
+			    oprntf("if you want DATE, Use 'y' (yyyy mm dd)\n");
+			}
+			nreturns=0;
+			break;
+		case 'p':   /* enter the object's proper motions -- */
+
+                	status = get_pm(objdec,&mura_sec,&mudec);
+			nreturns=0;
+			break;
+		case 'e':   /* enter the input epoch */
+			getdouble(&objepoch,-10000.,10000.,
+				"Give input epoch again...\n"); /* liberal lims*/
+                        if(objepoch < -5000.) {
+				objepoch = 2000.+
+	                            (true_jd(date, use_dst, enter_ut, night_date, stdz)
+				     -J2000)/365.25;
+				printf("LARGE NEGATIVE EPOCH --- causes input epoch to be set to current!\n");
+				printf("set to Julian epoch %9.4f\n",objepoch);
+                        }
+			break;
+		case 't':   /* enter the time ... hours min sec */
+			get_time(&date);
+			nreturns=0;
+			break;
+		case 'T':   /* read system clock -- set date & time to that. */
+			;
+#if SYS_CLOCK_OK == 1
+			printf("Set to how many minutes into the future? :");
+			scanf("%lf",&minoffset);
+			get_sys_date(&date,use_dst,enter_ut,night_date,
+				stdz,minoffset);
+#else
+			printf("Sorry -- system clock options are disabled, probably because of an\n");
+			printf("incompatibility between your system and the standard time library\n");
+			printf("functions used in the program.  Turning them on would require fixing\n");
+			printf("the problem in source code and recompiling.\n");
+#endif
+			nreturns=0;
+                        break;
+		case 'g':   /* toggle whether times are entered as
+				      Greenwich or local */
+			if(enter_ut == 1) enter_ut = 0;
+			else {
+				enter_ut = 1;
+				night_date = 0;
+			}
+			if(enter_ut == 1)
+				oprntf("Dates and times entered are now UT.\n");
+else oprntf("Dates & times entered are local, dates are literal (not evening).\n");
+			oprntf("TIME IS CHANGED to %d %02d %02d, %02d %02d %02.0f",
+				date.y,date.mo,date.d,date.h,date.mn,date.s);
+			if(enter_ut == 1) oprntf(" UNIVERSAL time.\n");
+				else oprntf(" LOCAL time.\n");
+			nreturns=0;
+			break;
+		case 'n': /* toggle whether the current date is to
+				  be interpreted as the evening date (for
+				  all night) or the true date .... */
+			if(enter_ut == 1) {
+oprntf("You're entering times as UT, so 'evening date' makes no sense!....\n");
+oprntf("No action taken on 'n', first use 'g' first to enable local time input.\n");
+			}
+			else if(night_date == 1) {
+			   night_date = 0;
+oprntf("The date in effect will now be interpreted literally, not as evening.\n");
+			}
+			else {
+			   night_date = 1;
+oprntf("The date in effect will now be interpreted as the evening date.\n");
+			}
+			nreturns=0;
+			break;
+		case 'a':
+			if(sclogfl != NULL) fprintf(sclogfl,"\n\n"); /* space it */
+			oprntf("*** Almanac for the currently specified date ***");
+			if(night_date != 1) {
+oprntf(", but CAUTION!!\nThe 'night date' option is off, so be especially careful\n");
+oprntf("this is the correct night.  See 'g' and 'n'...");
+			}
+			else oprntf(":");
+			print_tonight(date,lat,longit,elevsea,elev,horiz,site_name,stdz,
+			   zone_name,zabr,use_dst,&jdb,&jde,2);
+			printf("\nType command, 'f' for fast tour, or '?' for menu:");
+			nreturns=0;
+			break;
+		case 'y':   /* enter the date, yyyy mm dd */
+			get_date(&date);
+			nreturns=0;
+			break;
+
+	/* The site parameters must all be changed at the same time; hence
+	   user is forced to change them all. */
+
+		case 's':  /* change the site parameters */
+			load_site(&longit,&lat,&stdz,&use_dst,
+			   zone_name,&zabr,&elevsea,&elev,
+			   &horiz,site_name);
+			oprntf("New site = %s\n",site_name);
+			printf("(Give command, or ? for menu.)\n");
+			nreturns=0;
+			break;
+		case '=':  /* PRINT CIRCUMSTANCES for current params */
+			if(sclogfl != NULL) fprintf(sclogfl,"\n\n*** Instantaneous Circumstances ***\n");
+			if(setup_time_place(date,longit,lat,stdz,
+			    use_dst,zone_name,zabr, site_name,enter_ut,
+			    night_date,&jd,&jdloc,&jdb,&jde,&sid,
+			    &curep) < 0) break;
+			print_circumstances(objra,objdec,objepoch,jd,
+			    curep,mura_arcs,mura_sec,mudec,
+				   sid,lat,elevsea,horiz);
+			nreturns=0;
+			break;
+		case 'm':  /* print positions of major planets */
+			if(setup_time_place(date,longit,lat,stdz,
+				use_dst,zone_name,zabr, site_name,enter_ut,night_date,
+				&jd,&jdloc,&jdb,&jde,&sid,&curep) < 0)
+				   break;
+			comp_el(jd);
+			pposns(jd,lat,sid,1,pra,pdec);
+			nreturns=0;
+			break;
+		case 'h':  /* print an hourly airmass table */
+			hourly_airmass(date,stdz,lat,longit,horiz,
+			   use_dst,objra,objdec,objepoch, mura_sec,
+			   mura_arcs,mudec);
+			nreturns=0;
+			break;
+		case 'o':
+			if(sclogfl != NULL) fprintf(sclogfl,"\n");
+		        obs_season(objra,objdec,objepoch,
+			     lat,longit);
+			nreturns=0;
+                        break;
+		case 'c':  /* print galactic and ecliptic coordinates */
+			galact(objra,objdec,objepoch,&glat,&glong);
+			oprntf("Galactic: l = %5.2f, b = %5.2f\n",
+				glat,glong);
+			eclipt(objra,objdec,objepoch,date_to_jd(date),
+				&curep,&eclong,&eclat);
+			oprntf("Ecliptic (equinox %7.2f): long = %5.2f, lat = %5.2f\n",
+				curep, eclong, eclat);
+			nreturns=0;
+			break;
+		case 'l':
+			print_params(date,enter_ut,night_date,
+				stdz,lat,longit,site_name,elevsea,elev,use_dst,
+				objra,objdec,objepoch,mura_sec,mura_arcs,mudec);
+			nreturns=0;
+			break;
+		case 'i':  /* print a short tutorial */
+			print_examples();
+			nreturns=0;
+			break;
+		case 'w':  /* print information about algorithms, acc. */
+			print_accuracy();
+			nreturns=0;
+			break;
+		case 'x':
+			nxret = 0;
+    /*			printf("(Give xtra goodies subcommand, ? for menu)\n");
+			scanf("%s",cxstr);    */
+		        while(isspace(cx = getch()) != 0) {
+			    nxret++;
+			    if(nxret == 3) {
+				printf("Give an extra goodies command, or ? for menu!\n");
+				nxret = 0;
+			    }
+			}
+            /*          cx = cxstr[0];   */
+			switch(cx)  {
+                           case '?':
+			oprntf("Extra goodies commands are:\n");
+			oprntf("  x? ... print extra goodies menu.\n");
+			oprntf("  xc ... give galactic and ecliptic coords.\n");
+			oprntf("  xd ... give rough value of delta T = TDT - UT.\n");
+                        oprntf("  xv ... list geocentric times of repeating phenom (Variable star)\n");
+			oprntf("  xf ... give phase of repeating phenom.\n");
+                        oprntf("  xb ... precess a bunch of coords the same way.\n");
+			oprntf("  xj ... calculate calendar dates given julian dates.\n");
+			oprntf("  xJ ... *set* date and time values from Julian date.\n");
+			oprntf("  xZ ... *set* RA and dec to Zenith\n");
+#if LOG_FILES_OK == 1
+			oprntf("LOG-FILE COMMAND:\n");
+			oprntf("  xL ... toggles log file open or closed\n");
+#endif
+			oprntf("COMMANDS FOR FILES OF OBJECTS:\n");
+			oprntf("  xR ... read objects from a file, format: name h m s d m s epoch\n");
+			oprntf("  xl ... type out (part of) object list.\n");
+			oprntf("  xN ... find object by name, set to its coords\n");
+			oprntf("  xS ... sort and select object by a rank, set to coords.\n");
+			oprntf("(Note that capital letters affect more than one quantity, eg. RA and dec)\n");
+			oprntf("  xx ... null command, returns to main level.\n");
+			   break;
+                           case 'v':
+			      if(sclogfl != NULL)
+				fprintf(sclogfl,"\n\n  *** Ephemeris predictions ***\n\n");
+			      ephemgen(objra,objdec,objepoch,lat,longit);
+			   break;
+			   case 'f':
+                              printephase(date, use_dst, enter_ut,
+				night_date, stdz, lat,
+	  			longit, objepoch, objra, objdec);
+			   break;
+			   case 'b':
+                              mass_precess();
+			   break;
+			   case 'c':
+			      oprntf("Equatorial: RA = ");
+			      put_coords(objra,3);
+			      oprntf(", dec = ");
+			      put_coords(objdec,2);
+			      oprntf(" (epoch %6.1f)\n",
+                                  objepoch);
+			      galact(objra,objdec,objepoch,&glat,&glong);
+			      oprntf("  Galactic: l = %5.2f, b = %5.2f\n",
+				glat,glong);
+			      eclipt(objra,objdec,objepoch,date_to_jd(date),
+		           		&curep,&eclong,&eclat);
+			      oprntf("Ecliptic (equinox %7.2f): long = %5.2f, lat = %5.2f\n",
+				curep, eclong, eclat);
+			   break;
+			   case 'd':
+			      jd = true_jd(date, use_dst, enter_ut, night_date, stdz);
+       	                      Dt = etcorr(jd);
+			      oprntf("Delta t = TDT - UT = %5.1f seconds\n",Dt);
+			      oprntf("JD %f (UT) --> ",jd);
+			      jd = jd + Dt / SEC_IN_DAY;
+			      oprntf(" %f (TDT)\n",jd);
+			      if(date.y > 1993)
+				oprntf("(Value is an extrapolated guess ... only computable after the fact.)\n");
+			      else oprntf("+- 0.5 sec, based on 5-year linear interpolations.\n");
+		              if(date.y < 1983) oprntf("Before 1983, TDT was preceded by ephemeris time (ET).\n");
+			   break;
+			   case 'j':
+			      jdc = 1;
+			      oprntf("jd to calendar conversion. \n");
+			      while(jdc > 0.) {
+				  printf("Give jd to convert, negative to exit:");
+				  getdouble(&jdc,-1000000.,3000000.,
+	  			    "Give JD to convert, negative to exit");
+			   	  oprntf("%f -- > ",jdc);
+			          print_all(jdc);
+			          oprntf("\n");
+			      }
+			       oprntf("(Value of date in main program is unaffected.)\n");
+	                   break;
+			   case 'J':
+ 			      oprntf("Sets date and time from an input JD.\n");
+			      printf("Give JD to set to, negative value for no action: ");
+				  getdouble(&jdc,-1000000.,LASTJD,
+	  			    "Give jd to set to, negative for no action");
+			      set_to_jd(&date, use_dst, enter_ut,
+					night_date, stdz, jdc);
+	                   break;
+#if LOG_FILES_OK == 1
+			   case 'L':
+				if(sclogfl == NULL) {
+				    trying = 1;
+				    while(sclogfl == NULL && trying == 1) {
+				    	printf("Give filename for log file, type NONE to cancel:");
+				    	scanf("%s",str);
+					if(strcmp(str,"NONE") == 0)
+						trying = 0;
+					else {
+				    		sclogfl = fopen(str,"a");
+					}
+				    }
+				    if(sclogfl != NULL)
+					printf("log file %s is OPEN in append mode.\n",str);
+				    else printf("LOG FILE NOT OPENED.\n");
+				}
+			        else {
+				    fclose(sclogfl);
+				    sclogfl = NULL;  /* reset it explicitly */
+				    printf("Log file has been CLOSED.\n");
+				}
+				break;
+#endif
+			   case 'R':
+				read_obj_list();
+				break;
+			   case 'l':
+				type_list(date,use_dst,enter_ut,night_date,
+				    stdz,lat,longit);
+     				break;
+			   case 'N':
+				find_by_name(&objra,&objdec,objepoch,date,
+				  use_dst,enter_ut,night_date,stdz,lat,longit);
+				break;
+			   case 'S':
+				find_nearest(&objra,&objdec,objepoch,date,
+                                  use_dst,enter_ut,night_date,stdz,lat,longit);
+				break;
+			   case 'Z':
+                                set_zenith(date,use_dst,enter_ut,night_date,
+ 					stdz,lat,longit,objepoch,&objra,
+					&objdec);
+				break;
+			   case 't':  /* test */
+
+	test = true_jd(date, use_dst, enter_ut, night_date, stdz);
+	printf(".... true_jd gives --> %f\n",test);
+	break;
+			   case ' ':  ;
+			   break;
+			   case '\n': ;
+    			   break;
+			   default: oprntf("Unrecognized character %c ... no action.\n",cx);
+                           break;
+                        }      /* end of 'extra goodies' menu. */
+			nreturns=0;
+			printf("\n(eXtra goodies doesn't loop.)\n");
+			printf("Back in main commands, 'Q' quits, '?' menu, 'f' fast tour.\n");
+			break;
+		case '\n': /* ignore carriage returns */
+			nreturns++;  /* but guide the user if they keep
+			       hitting returns .... */
+			if(nreturns == 3) {
+			  printf("You're repeating carriage returns. There are no prompts.\n");
+			  printf("Type 'f' for fast tour, 'i' for instructions, ? for a menu.\n");
+			  nreturns = 0;
+			}
+			break;
+		case ' ':  /* ignore blank spaces */
+			break;
+		case 'q':  /* prompt if user's trying to quit */
+			printf("Type an UPPER CASE Q to quit.\n");
+			break;
+		default:   /* complain if unrecognizable */
+			printf("Unknown command, %c\n",cc);
+	}       /* closing switch loop */
+	BLUNDER:; /* DUMMY STATEMENT */
+	oprntf("Suggestions or comments --> john.thorstensen@dartmouth.edu\n");
+	oprntf("Goodbye.\n");
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/doc/skycalc.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/doc/skycalc.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/doc/skycalc.txt	(revision 22322)
@@ -0,0 +1,92 @@
+
+functions in time.c:
+
+int    get_sys_date (struct SC_date_time *date)
+       set SC date & time from system clock
+
+double date_to_jd (struct SC_date_time date)
+       convert JD to SC date & time
+
+void   jd_to_date (double jdin, struct SC_date_time *date)
+       convert SC date & time to JD
+
+double lst (double jd, double longit)
+       convert JD & longitude to LST
+
+double adj_time (double x)
+       force time domain to be -12h and 12h 
+
+
+functions in geometry.c:
+
+void   xyz_cel (double x, double y, double z, double *r, double *d)
+       converts a coordinate triplet back to a standard ra and dec
+
+double atan_circ (double x, double y)
+       returns radian angle 0 to 2pi for coords x, y -- get that quadrant right !! */
+
+double altit (double dec, double ha, double lat, double *az)
+       returns altitude (degrees) for dec, ha, lat (degree)
+       also computes and returns azimuth through pointer argument.
+
+double ha_alt (double dec, double lat, double alt)
+       returns hour angle at which object at dec is at altitude alt.
+       If object is never at this altitude, signals with special
+       return values 1000 (always higher) and -1000 (always lower).
+
+void   min_max_alt (double lat, double dec, double *min, double *max)
+       computes minimum and maximum altitude for a given dec and latitude.
+
+double circulo (double x)
+       force domain to be 0 - 360 degrees
+
+
+functions in astro.c:
+
+void   precrot (double rorig, double dorig, double orig_epoch, double final_epoch, double *rf, double *df) {
+       accurate precession method from rorig, dorig, orig_epoch 
+
+void   geocent (double geolong, double geolat, double height, double x_geo, double y_geo, double z_geo)
+       return geocentric coordinate from geodetic data
+
+void   eclrot(double jd, double x, double y, double z)
+       rotates ecliptic rectangular coords x, y, z to equatorial
+
+double etcorr (double jd)
+       return correction to jd for shifts in ET or TDT
+
+void   set_zenith (struct SC_date_time date, double lat, double longit, double epoch, double *ra, double *dec)
+       return zenith coords for given date, time, epoch, position 
+
+
+functions is sun.c:
+
+void   lpsun (double jd, double ra, double dec)
+       return ra & dec for sun on given JD (low precision)
+
+double jd_sun_alt (double alt, double jdguess, double lat, double longit)
+       return JD for sun at given altitude, start with jdguess
+
+double sunset_tonight (struct SC_date_time date, double lat, double longit, double elev)
+       return JD for sunset before closest midnight to date & time
+
+double sunrise_tonight (struct SC_date_time date, double lat, double longit, double elev)
+       return JD for sunrise after closest midnight to date & time
+
+
+functions in moon.c:
+
+void   accumoon (double jd, double geolat, double lst, double elevsea, 
+       double *geora, double *geodec, double *geodist, 
+       double *topora, double *topodec, double *topodist)
+       calculate position of moon given jd, lat, etc.
+
+double jd_moon_alt (double alt, double jdguess, double lat, double longit, double elevsea)
+       return JD for moon at given altitude, start with jdguess
+
+double moonset_tonight (struct SC_date_time date, double lat, double longit, double elevsea, double elev) {
+       Given site position, return Moonset for closest midnight
+
+double moonrise_tonight (struct SC_date_time date, double lat, double longit, double elevsea, double elev) {
+       Given site position, return Moonrise for closest midnight
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/include/skycalc.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/include/skycalc.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/include/skycalc.h	(revision 22322)
@@ -0,0 +1,28 @@
+
+/* This is the header file for the Ohana version of 'libskycalc'.  This library is based on the
+ * code provided to the community by John Thorstensen.  See the discussion in the README file
+ * and in the file doc/Thorstensen.txt
+ */
+
+/* header for skycalc library function calls */
+
+struct SC_coord {
+     short sign;  /* carry sign explicitly since -0 not neg. */
+     double hh;
+     double mm;
+     double ss;
+}; 
+
+struct SC_date_time {
+  short y;
+  short mo;
+  short d;
+  short h;
+  short mn;
+  float s;
+};
+
+double sunset_tonight (struct SC_date_time date, double lat, double longit, double elev);
+double sunrise_tonight (struct SC_date_time date, double lat, double longit, double elev);
+double moonset_tonight (struct SC_date_time date, double lat, double longit, double elevsea, double elev);
+double moonrise_tonight (struct SC_date_time date, double lat, double longit, double elevsea, double elev);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/include/skycalc_internal.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/include/skycalc_internal.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/include/skycalc_internal.h	(revision 22322)
@@ -0,0 +1,77 @@
+/* header for use by the skycalc library files and stand-alone programs, not needed for external calls */
+
+# include <stdio.h>
+# include <math.h>
+# include <stdlib.h>
+# include <time.h>
+# include <skycalc.h>
+
+/* some (not all) physical, mathematical, and astronomical constants
+   used are defined here. */
+
+#define  PI                3.14159265358979
+#define  ARCSEC_IN_RADIAN  206264.8062471
+#define  DEG_IN_RADIAN     57.2957795130823
+#define  HRS_IN_RADIAN     3.819718634205
+#define  KMS_AUDAY         1731.45683633   /* km per sec in 1 AU/day */
+#define  SS_MASS           1.00134198      /* solar system mass in solar units */
+#define  J2000             2451545.        /* Julian date at standard epoch */
+#define  SEC_IN_DAY        86400.
+#define  FLATTEN           0.003352813   /* flattening of earth, 1/298.257 */
+#define  EQUAT_RAD         6378137.    /* equatorial radius of earth, meters */
+#define  ASTRO_UNIT        1.4959787066e11 /* 1 AU in meters */
+#define  RSUN              6.96000e8  /* IAU 1976 recom. solar radius, meters */
+#define  RMOON             1.738e6    /* IAU 1976 recom. lunar radius, meters */
+#define  PLANET_TOL        3.          /* flag if nearer than 3 degrees
+						to a major planet ... */
+
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+/** prototypes of private functions used by the library **/
+
+/* in time.c */
+int    SC_get_sys_date (struct SC_date_time *date);
+double SC_date_to_jd (struct SC_date_time date);
+void   SC_jd_to_date (double jdin, struct SC_date_time *date);
+double SC_lst (double jd, double longit);
+double SC_adj_time (double x);
+
+/* in geometry.c */
+void   SC_xyz_cel (double x, double y, double z, double *r, double *d);
+double SC_atan_circ (double x, double y);
+double SC_altit (double dec, double ha, double lat, double *az);
+double SC_ha_alt (double dec, double lat, double alt);
+void   SC_min_max_alt (double lat, double dec, double *min, double *max);
+double SC_circulo (double x);
+
+/* in astro.c */
+void   SC_precrot (double rorig, double dorig, double orig_epoch, double final_epoch, double *rf, double *df);
+void   SC_geocent (double geolong, double geolat, double height, double *x_geo, double *y_geo, double *z_geo);
+void   SC_eclrot(double jd, double *x, double *y, double *z);
+double SC_etcorr (double jd);
+void   SC_set_zenith (struct SC_date_time date, double lat, double longit, double epoch, double *ra, double *dec);
+
+/* in sun.c */
+void   SC_lpsun (double jd, double *ra, double *dec);
+double SC_jd_sun_alt (double alt, double jdguess, double lat, double longit);
+double SC_sunset_tonight (struct SC_date_time date, double lat, double longit, double elev);
+double SC_sunrise_tonight (struct SC_date_time date, double lat, double longit, double elev);
+
+/* in moon.c */
+void   SC_lpmoon(double jd, double lat, double sid, double* ra, double* dec, double* dist);
+void   SC_accumoon (double jd, double geolat, double lst, double elevsea, double *geora, double *geodec, double *geodist, double *topora, double *topodec, double *topodist);
+double SC_jd_moon_alt (double alt, double jdguess, double lat, double longit, double elevsea);
+double SC_moonset_tonight (struct SC_date_time date, double lat, double longit, double elevsea, double elev);
+double SC_moonrise_tonight (struct SC_date_time date, double lat, double longit, double elevsea, double elev);
+
+// are these defined in here or in libohana?
+// int dms_to_ddd (double *Value, char *string);
+// int str_to_radec (double *ra, double *dec, char *str1, char *str2);
+// int chk_time (char *line);
+// double sec_to_jd (time_t second);
+// time_t jd_to_sec (double jd);
+// char *sec_to_date (time_t second);
+// time_t date_to_sec (char *date);
+// int str_to_time (char *line, time_t *second);
+// int str_to_dtime (char *line, double *second);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/astro.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/astro.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/astro.c	(revision 22322)
@@ -0,0 +1,192 @@
+# include <skycalc_internal.h>
+
+/* Takes a coordinate pair and precesses it using matrix procedures
+   as outlined in Taff's Computational Spherical Astronomy book.
+   This is the so-called 'rigorous' method which should give very
+   accurate answers all over the sky over an interval of several
+   centuries.  Naked eye accuracy holds to ancient times, too.
+   Precession constants used are the new IAU1976 -- the 'J2000'
+   system. Angles in degrees, epochs in years */
+
+void SC_precrot (double rorig, double dorig, double orig_epoch, double final_epoch, double *rf, double *df) {
+  
+  double ti, tf, zeta, z, theta;  /* all as per  Taff */
+  double cosz, coszeta, costheta, sinz, sinzeta, sintheta;  /* ftns */
+  double p11, p12, p13, p21, p22, p23, p31, p32, p33;
+  /* elements of the rotation matrix */
+  double radian_ra, radian_dec;
+  double orig_x, orig_y, orig_z;
+  double fin_x, fin_y, fin_z;   /* original and final unit ectors */
+
+  ti = (orig_epoch - 2000.) / 100.;
+  tf = (final_epoch - 2000. - 100. * ti) / 100.;
+
+  zeta = (2306.2181 + 1.39656 * ti + 0.000139 * ti * ti) * tf +
+    (0.30188 - 0.000344 * ti) * tf * tf + 0.017998 * tf * tf * tf;
+  z = zeta + (0.79280 + 0.000410 * ti) * tf * tf + 0.000205 * tf * tf * tf;
+  theta = (2004.3109 - 0.8533 * ti - 0.000217 * ti * ti) * tf
+    - (0.42665 + 0.000217 * ti) * tf * tf - 0.041833 * tf * tf * tf;
+
+  /* convert to radians */
+
+  zeta = zeta / ARCSEC_IN_RADIAN;
+  z = z / ARCSEC_IN_RADIAN;
+  theta = theta / ARCSEC_IN_RADIAN;
+
+  /* compute the necessary trig functions for speed and simplicity */
+
+  cosz = cos(z);
+  coszeta = cos(zeta);
+  costheta = cos(theta);
+  sinz = sin(z);
+  sinzeta = sin(zeta);
+  sintheta = sin(theta);
+
+  /* compute the elements of the precession matrix */
+
+  p11 = coszeta * cosz * costheta - sinzeta * sinz;
+  p12 = -1. * sinzeta * cosz * costheta - coszeta * sinz;
+  p13 = -1. * cosz * sintheta;
+
+  p21 = coszeta * sinz * costheta + sinzeta * cosz;
+  p22 = -1. * sinzeta * sinz * costheta + coszeta * cosz;
+  p23 = -1. * sinz * sintheta;
+
+  p31 = coszeta * sintheta;
+  p32 = -1. * sinzeta * sintheta;
+  p33 = costheta;
+
+  /* transform original coordinates */
+
+  radian_ra = rorig / HRS_IN_RADIAN;
+  radian_dec = dorig / DEG_IN_RADIAN;
+
+  orig_x = cos(radian_dec) * cos(radian_ra);
+  orig_y = cos(radian_dec) *sin(radian_ra);
+  orig_z = sin(radian_dec);
+  /* (hard coded matrix multiplication ...) */
+  fin_x = p11 * orig_x + p12 * orig_y + p13 * orig_z;
+  fin_y = p21 * orig_x + p22 * orig_y + p23 * orig_z;
+  fin_z = p31 * orig_x + p32 * orig_y + p33 * orig_z;
+
+  /* convert back to spherical polar coords */
+
+  SC_xyz_cel(fin_x, fin_y, fin_z, rf, df);
+
+}
+
+
+/* computes the geocentric coordinates from the geodetic
+   (standard map-type) longitude, latitude, and height.
+   These are assumed to be in decimal hours, decimal degrees, and
+   meters respectively.  Notation generally follows 1992 Astr Almanac,
+   p. K11 */
+
+void SC_geocent (double geolong, double geolat, double height, double *x_geo, double *y_geo, double *z_geo) {
+
+  double denom, C_geo, S_geo;
+
+  geolat = geolat / DEG_IN_RADIAN;
+  geolong = geolong / HRS_IN_RADIAN;
+  denom = (1. - FLATTEN) * sin(geolat);
+  denom = cos(geolat) * cos(geolat) + denom*denom;
+  C_geo = 1. / sqrt(denom);
+  S_geo = (1. - FLATTEN) * (1. - FLATTEN) * C_geo;
+  C_geo = C_geo + height / EQUAT_RAD;  /* deviation from almanac
+					  notation -- include height here. */
+  S_geo = S_geo + height / EQUAT_RAD;
+  *x_geo = C_geo * cos(geolat) * cos(geolong);
+  *y_geo = C_geo * cos(geolat) * sin(geolong);
+  *z_geo = S_geo * sin(geolat);
+}
+
+/* rotates ecliptic rectangular coords x, y, z to
+   equatorial (all assumed of date.) */
+void SC_eclrot(double jd, double *x, double *y, double *z) {
+
+  double incl;
+  double xpr,ypr,zpr;
+  double T;
+
+  T = (jd - J2000) / 36525;  /* centuries since J2000 */
+
+  incl = (23.439291 + T * (-0.0130042 - 0.00000016 * T))/DEG_IN_RADIAN;
+  /* 1992 Astron Almanac, p. B18, dropping the
+     cubic term, which is 2 milli-arcsec! */
+  ypr = cos(incl) * *y - sin(incl) * *z;
+  zpr = sin(incl) * *y + cos(incl) * *z;
+  *y = ypr;
+  *z = zpr;
+  /* x remains the same. */
+}
+
+/* Given a julian date in 1900-2100, returns the correction
+     delta t which is:
+     TDT - UT (after 1983 and before 1993)
+     ET - UT (before 1983)
+     an extrapolated guess  (after 1993).
+
+     For dates in the past (<= 1993) the value is linearly
+     interpolated on 5-year intervals; for dates after the present,
+     an extrapolation is used, because the true value of delta t
+     cannot be predicted precisely.  Note that TDT is essentially the
+     modern version of ephemeris time with a slightly cleaner
+     definition.
+
+     Where the algorithm shifts there is an approximately 0.1 second
+     discontinuity.  Also, the 5-year linear interpolation scheme can
+     lead to errors as large as 0.5 seconds in some cases, though
+     usually rather smaller. */
+
+double SC_etcorr (double jd) {
+
+  double jd1900 = 2415019.5;
+  double dates[20] = {1900,1905,1910,1915,1920,1925,1930,1935,1940,1945,
+		      1950,1955,1960,1965,1970,1975,1980,1985,1990,1993};
+  double delts[20]={-2.72,3.86,10.46,17.20,21.16,23.62,24.02,23.93,24.33,26.77,
+		    29.15,31.07,33.15,35.73,40.18,45.48,50.54,54.34,56.86,59.12};
+  double year, delt;
+  short i;
+
+  year = 1900. + (jd - 2415019.5) / 365.25;
+
+  if(year < 1993.0 && year >= 1900.) {
+    i = (year - 1900) / 5;
+    delt = delts[i] +
+      ((delts[i+1] - delts[i])/(dates[i+1] - dates[i])) * (year - dates[i]);
+  }
+
+  else if (year > 1993. && year < 2100.)
+    delt = 33.15 + (2.164e-3) * (jd - 2436935.4);  /* rough extrapolation */
+
+  else if (year < 1900) {
+    delt = 0.;
+  }
+
+  else if (year >= 2100.) {
+    delt = 180.; /* who knows? */
+  }
+
+  return (delt);
+
+}
+
+/* sets RA and DEC at zenith as defined by given time and date  */
+void SC_set_zenith (struct SC_date_time date, double lat, double longit, double epoch, double *ra, double *dec)
+{
+  double jd, current_epoch;
+
+  jd = SC_date_to_jd (date);
+
+  if (jd < 0.) return;  /* nonexistent time. */
+
+  *ra = SC_lst (jd, longit);
+
+  *dec = lat;
+
+  current_epoch = 2000. + (jd - J2000) / 365.25;
+
+  SC_precrot (*ra,*dec,current_epoch,epoch,ra,dec);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/dusktime.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/dusktime.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/dusktime.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include <skycalc_internal.h>
+# include <ohana.h>
+# define VERBOSE 0
+
+// XXX is this set for MKO??
+void SC_set_site (double *longit, double *lat, double *elevsea, double *elev) {
+
+  *longit = 10.36478; /*  W longitude in decimal hours */                     
+  *lat = 19.8267;     /*  N latitude in decimal degrees */                    
+  *elevsea = 4215.;   /* elevation above sea level (for absolute location) */ 
+  *elev = 4215.;      /* observatory elevation above horizon, meters */       
+
+}
+
+main (int argc, char **argv) {
+
+  struct SC_date_time date, tmpdate;
+  double longit, lat, elevsea, elev;
+  double jdnow, jdset, jdrise, d1, d2;
+  time_t tzero;
+  struct tm *stm;
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: dusktime (date)\n");
+    exit (1);
+  }
+
+  if (!ohana_str_to_time (argv[1], &tzero)) { 
+    fprintf (stderr, "syntax error\n");
+    exit (1);
+  }
+  stm = gmtime (&tzero);
+  date.y  = (short) (stm->tm_year + 1900);
+  date.mo = (short) (stm->tm_mon + 1);
+  date.d  = (short) (stm->tm_mday);
+  date.h  = (short) (stm->tm_hour);
+  date.mn = (short) (stm->tm_min);
+  date.s  = (float) (stm->tm_sec);
+
+  if (VERBOSE) fprintf (stderr, "%4d/%02d/%02d %02d:%02d:%02f\n", date.y, date.mo, date.d, date.h, date.mn, date.s);
+
+  SC_set_site (&longit, &lat, &elevsea, &elev);
+
+  jdnow  = SC_date_to_jd (date);
+  jdset  = SC_sunset_tonight (date, lat, longit, elev);
+  jdrise = SC_sunrise_tonight (date, lat, longit, elev);
+
+
+  d1 = fabs (jdnow - jdset);
+  d2 = fabs (jdnow - jdrise);
+
+  if (d1 < d2) {
+    fprintf (stdout, "set %f  %f\n", jdset, 24*60*(jdnow - jdset));
+    jdnow = jdset;
+  } else {
+    fprintf (stdout, "rise %f  %f\n", jdrise, 24*60*(jdrise - jdnow));
+    jdnow = jdrise;
+  }    
+
+  if (VERBOSE) {
+    jd_to_date (jdnow, &tmpdate);
+    fprintf (stderr, "%4d/%02d/%02d %02d:%02d:%02f\n", tmpdate.y, tmpdate.mo, tmpdate.d, tmpdate.h, tmpdate.mn, tmpdate.s);
+  }
+  exit (0);
+}
+
+  /* set_zenith (date, lat, longit, objepoch, &objra, &objdec); */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/geometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/geometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/geometry.c	(revision 22322)
@@ -0,0 +1,136 @@
+# include <skycalc_internal.h>
+
+/* converts a coordinate triplet back to a standard ra and dec */
+void SC_xyz_cel (double x, double y, double z, double *r, double *d) {
+
+   /* converts a coordinate triplet back to a standard ra and dec */
+
+   double mod;    /* modulus */
+   double xy;     /* component in xy plane */
+   short sign;    /* for determining quadrant */
+   double radian_ra, radian_dec;
+
+   /* this taken directly from pl1 routine - no acos or asin available there,
+       as it is in c. Easier just to copy, though */
+
+   mod = sqrt(x*x + y*y + z*z);
+   x = x / mod;
+   y = y / mod;
+   z = z / mod;   /* normalize 'em explicitly first. */
+
+   xy = sqrt(x*x + y*y);
+
+   if(xy < 1.0e-10) {
+      radian_ra = 0.;  /* too close to pole */
+      radian_dec = PI / 2.;
+      if(z < 0.) radian_dec = radian_dec * -1.;
+   }
+   else {
+      if(fabs(z/xy) < 3.) radian_dec = atan(z / xy);
+	 else if (z >= 0.) radian_dec = PI / 2. - atan(xy / z);
+	 else radian_dec = -1. * PI / 2. - atan(xy / z);
+      if(fabs(x) > 1.0e-10) {
+	 if(fabs(y / x) < 3.) radian_ra = atan(y / x);
+	 else if ((x * y ) >= 0.) radian_ra = PI / 2. - atan(x/y);
+	 else radian_ra = -1. *  PI / 2. - atan(x / y);
+      }
+      else {
+	 radian_ra = PI / 2.;
+	 if((x * y)<= 0.) radian_ra = radian_ra * -1.;
+      }
+      if(x <0.) radian_ra = radian_ra + PI ;
+      if(radian_ra < 0.) radian_ra = radian_ra + 2. * PI ;
+   }
+
+   *r = radian_ra * HRS_IN_RADIAN;
+   *d = radian_dec * DEG_IN_RADIAN;
+
+}
+
+/* returns radian angle 0 to 2pi for coords x, y -- get that quadrant nright !! */
+/* XXX : reimplements atan2() */
+double SC_atan_circ (double x, double y) {
+  
+  double theta;
+  
+  if(x == 0.) {
+    if(y > 0.) theta = PI / 2.;
+    else if(y < 0.) theta = 3.* PI / 2.;
+    else theta = 0.;   /* x and y zero */
+  }
+  else theta = atan(y/x);
+  if(x < 0.) theta = theta + PI;
+  if(theta < 0.) theta = theta + 2.* PI;
+  return(theta);
+}
+
+/* returns altitude(degr) for dec, ha, lat (decimal degr, hr, degr);
+    also computes and returns azimuth through pointer argument. */
+double SC_altit (double dec, double ha, double lat, double *az) {
+
+  double x,y,z;
+
+  dec = dec / DEG_IN_RADIAN;
+  ha = ha / HRS_IN_RADIAN;
+  lat = lat / DEG_IN_RADIAN;
+  x = DEG_IN_RADIAN * asin(cos(dec)*cos(ha)*cos(lat) + sin(dec)*sin(lat));
+  y =  sin(dec)*cos(lat) - cos(dec)*cos(ha)*sin(lat); /* due N comp. */
+  z =  -1. * cos(dec)*sin(ha); /* due east comp. */
+  *az = SC_atan_circ(y,z) * DEG_IN_RADIAN;
+  return(x);
+}
+
+/* returns hour angle at which object at dec is at altitude alt.
+   If object is never at this altitude, signals with special
+   return values 1000 (always higher) and -1000 (always lower). */
+double SC_ha_alt (double dec, double lat, double alt) {
+
+  double x,coalt,min,max;
+  
+  SC_min_max_alt(lat,dec,&min,&max);
+  if(alt < min)
+    return(1000.);  /* flag value - always higher than asked */
+  if(alt > max)
+    return(-1000.); /* flag for object always lower than asked */
+  dec = (0.5*PI) - dec / DEG_IN_RADIAN;
+  lat = (0.5*PI) - lat / DEG_IN_RADIAN;
+  coalt = (0.5*PI) - alt / DEG_IN_RADIAN;
+  x = (cos(coalt) - cos(dec)*cos(lat)) / (sin(dec)*sin(lat));
+  if (fabs(x) <= 1.) return(acos(x) * HRS_IN_RADIAN);
+  else {
+    printf  ("Error in ha_alt ... acos(>1).\n");
+    return (1000.);
+  }
+}
+
+/* computes minimum and maximum altitude for a given dec and latitude. */
+void SC_min_max_alt (double lat, double dec, double *min, double *max) {
+
+  double x;
+
+  lat = lat / DEG_IN_RADIAN; /* pass by value! */
+  dec = dec / DEG_IN_RADIAN;
+  x = cos(dec)*cos(lat) + sin(dec)*sin(lat);
+  if (fabs(x) <= 1.) {
+    *max = asin(x) * DEG_IN_RADIAN;
+  }
+  else printf ("Error in min_max_alt -- arcsin(>1)\n");
+
+  x = sin(dec)*sin(lat) - cos(dec)*cos(lat);
+  if(fabs(x) <= 1.) {
+    *min = asin(x) * DEG_IN_RADIAN;
+  }
+  else printf ("Error in min_max_alt -- arcsin(>1)\n");
+}
+
+/* force domain to be 0 - 360 degrees */
+double SC_circulo (double x) {
+  
+  /* fails for negative angles! */
+
+  int n;
+  
+  n = (int)(x / 360.);
+  return(x - 360. * n);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/misc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/misc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/misc.c	(revision 22322)
@@ -0,0 +1,314 @@
+# include <ohana.h>
+# include <skycalc_internal.h>
+
+/***** convert [-]00:00:00 to 0.0000 ****/
+int dms_to_ddd (double *Value, char *string) {
+  
+  int valid, neg, status;
+  double tmp, value;
+  char *p1, *p2, *px;
+
+  valid = FALSE; 
+  neg = FALSE;
+  stripwhite (string);
+  p1 = string;
+  px = string + strlen(string);
+
+  if (string[0] == '-') { 
+    valid = TRUE; 
+    neg = TRUE;
+    p1 = &string[1];
+  }
+  if (string[0] == '+') { 
+    valid = TRUE; 
+    neg = FALSE;
+    p1 = &string[1];
+  }
+  if (isdigit(string[0])) { 
+    valid = TRUE;
+    p1 = &string[0];
+  }
+  if (!valid) { return (FALSE); }
+
+  status = 1;
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) return (FALSE); /* entry not a number: +fred */
+  value = tmp;
+  if (p2 == px) goto escape;    /* entry only number: +1.0 */ 
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;    /* entry not a number: +1:fred */
+  status = 2;
+  value += tmp / 60.0;
+  if (p2 == px) goto escape;    /* entry only number: +1:1 */
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;    /* entry not a number: +1:1:fred */
+  value += tmp / 3600.0;
+
+ escape:
+  if (neg) {
+    value *= -1;
+  }
+  *Value = value;
+
+  return (status);
+}
+
+/**********/
+int str_to_radec (double *ra, double *dec, char *str1, char *str2) {
+
+  double Ra, Dec;
+
+  *ra = *dec = 0;
+  switch (dms_to_ddd (&Ra, str1)) {
+  case 0:
+    fprintf (stderr, "syntax error in RA\n");
+    return (FALSE);
+  case 1:
+    break;
+  case 2:
+    Ra = Ra * 15;
+    break;
+  }
+  switch (dms_to_ddd (&Dec, str2)) {
+  case 0:
+    fprintf (stderr, "syntax error in DEC\n");
+    return (FALSE);
+  case 1:
+  case 2:
+    break;
+  }
+  *ra = Ra;
+  *dec = Dec;
+  return (TRUE);
+}
+
+# define FORMAT_DAYS    1
+# define FORMAT_HOURS   2
+# define FORMAT_MINUTES 3
+# define FORMAT_SECONDS 4
+# define FORMAT_JD      5
+# define FORMAT_DATE    6
+
+/**********/
+int chk_time (char *line) {
+
+  char *p1, *p2;
+  double tmp;
+  int mode;
+
+  p1 = line;
+  tmp = strtod (p1, &p2);
+  if (p2 == p1 + strlen (p1) - 1) {
+    if (*p2 == 'd') {
+      mode = FORMAT_DAYS;
+    }
+    if (*p2 == 'h') {
+      mode = FORMAT_HOURS;
+    }
+    if (*p2 == 'm') {
+      mode = FORMAT_MINUTES;
+    }
+    if (*p2 == 's') {
+      mode = FORMAT_SECONDS;
+    }
+    if (*p2 == 'j') {
+      mode = FORMAT_JD;
+    }
+  } else { 
+    mode = FORMAT_DATE;
+  }
+  return (mode);
+}
+
+/**********/
+int str_to_time (char *line, time_t *second) {
+  
+  struct timeval now;
+  double jd;
+
+  if (!strcasecmp (line, "NOW")) {
+    gettimeofday (&now, (struct timezone *) NULL);
+    *second = now.tv_sec;
+    return (TRUE);
+  }
+    
+  if (!strcasecmp (line, "TODAY")) {
+    gettimeofday (&now, (struct timezone *) NULL);
+    *second = 86400 * ((int)(now.tv_sec / 86400));
+    return (TRUE);
+  }
+    
+  switch (chk_time (line)) {
+  case 0:
+    return (FALSE);
+  case FORMAT_DAYS:
+    *second = strtod (line, 0) * 86400.0;
+    return (TRUE);
+  case FORMAT_HOURS:
+    *second = strtod (line, 0) * 3600.0;
+    return (TRUE);
+  case FORMAT_MINUTES:
+    *second = strtod (line, 0) * 60.0;
+    return (TRUE);
+  case FORMAT_SECONDS:
+    *second = strtod (line, 0);
+    return (TRUE);
+  case FORMAT_JD:
+    jd = strtod (line, 0);
+    *second = jd_to_sec (jd);
+    return (TRUE);
+  case FORMAT_DATE:
+    *second = date_to_sec (line);
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+/**********/
+int str_to_dtime (char *line, double *second) {
+  
+  switch (chk_time (line)) {
+  case 0:
+  case FORMAT_JD:
+  case FORMAT_DATE:
+    return (FALSE);
+  case FORMAT_DAYS:
+    *second = strtod (line, 0) * 86400.0;
+    return (TRUE);
+  case FORMAT_HOURS:
+    *second = strtod (line, 0) * 3600.0;
+    return (TRUE);
+  case FORMAT_MINUTES:
+    *second = strtod (line, 0) * 60.0;
+    return (TRUE);
+  case FORMAT_SECONDS:
+    *second = strtod (line, 0);
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+/**********/
+double sec_to_jd (time_t second) {
+
+  double jd;
+  
+  jd = second/86400.0 + 2440587.5;
+  return (jd);
+}
+
+/**********/
+time_t jd_to_sec (double jd) {
+
+  time_t second;
+
+  second = (jd - 2440587.5)*86400;
+  return (second);
+}
+
+/**********/
+char *sec_to_date (time_t second) {
+  
+  struct tm *gmt;
+  char *line;
+  
+  ALLOCATE (line, char, 64);
+  gmt   = gmtime (&second);
+  sprintf (line, "%4d/%02d/%02d,%02d:%02d:%02d", 1900+gmt[0].tm_year, gmt[0].tm_mon+1, gmt[0].tm_mday, gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+  return (line);
+
+}
+
+/***** date in format yyyy/mm/dd,hh:mm:ss *****/
+time_t date_to_sec (char *date) {
+  
+  time_t second;
+  double tmp, jd;
+  struct tm now;
+  char *p1, *p2, *px;
+  
+  p1 = date;
+  px = date + strlen(date);
+  bzero (&now, sizeof(now));
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  now.tm_year = tmp;
+  if (now.tm_year > 1000) now.tm_year -= 1900;
+  if (now.tm_year <   50) now.tm_year += 100;
+  if (p2 == px) goto escape;  
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  now.tm_mon = tmp - 1; /* mon runs from 0 - 11 */
+  if (p2 == px) goto escape;  
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  now.tm_mday = tmp;
+  if (p2 == px) goto escape;  
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  p1 = p2 + 1;
+  now.tm_hour = tmp;
+  if (p2 == px) goto escape;  
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  now.tm_min = tmp;
+  if (p2 == px) goto escape;  
+  p1 = p2 + 1;
+
+  tmp = strtod (p1, &p2);
+  if (p2 == p1) goto escape;
+  now.tm_sec = tmp;
+  if (p2 == px) goto escape;  
+  p1 = p2 + 1;
+
+ escape:
+  jd = now.tm_mday - 32075 + (int)(1461*(1900 + now.tm_year + 4800 + (int)(((now.tm_mon+1)-14)/12))/4)
+    + (int)(367*((now.tm_mon+1) - 2 - (int)(((now.tm_mon+1) - 14)/12)*12)/12)
+    - (int)(3*(int)((1900 + now.tm_year + 4900 + (int)(((now.tm_mon+1) - 14)/12))/100)/4) - 0.5;
+  
+  second = (jd - 2440587.5)*86400 + 3600.0*now.tm_hour + now.tm_min*60.0 + now.tm_sec;
+
+  return (second);
+}
+
+
+/* times may be in forms as:
+   20040200450s (N seconds since 1970.0)
+   2440900.232j (julian date)
+   99/02/23,03:22:18 (date string)
+   (separators may be anything except space, +, -)
+   99:02:15:12:23:30
+   99:02:15:12h23m30s
+   */
+
+
+/* fseek with timeout - 0.5 sec */
+int Fseek (FILE *f, long offset, int whence) {
+
+  int status, k;
+
+  status = fseek (f, offset, whence);
+  if (status == -1) {
+    int k;
+    /* fprintf (stderr, "problem seeking position: %d\n", errno); */
+    for (k = 0; (k < 10) && ((status = fseek (f, 0, SEEK_SET)) == -1); k++) usleep (50000);
+    if (status == -1) {
+      /* fprintf (stderr, "ERROR: serious problem seeking position: %d\n", errno); */
+      /* clear lock at this point? */
+      return (0);
+    }
+  }
+  return (1);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/moon.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/moon.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/moon.c	(revision 22322)
@@ -0,0 +1,465 @@
+# include <skycalc_internal.h>
+
+void SC_lpmoon(double jd, double lat, double sid,
+       double* ra, double* dec, double* dist) {
+
+
+/* implements "low precision" moon algorithms from
+   Astronomical Almanac (p. D46 in 1992 version).  Does
+   apply the topocentric correction. 
+   Units are as follows
+   jd,lat, sid;   decimal hours 
+   *ra, *dec,   decimal hours, degrees 
+   *dist;      earth radii */
+
+
+    double T, lambda, beta, pie, l, m, n, x, y, z, alpha, delta,
+        rad_lat, rad_lst, distance, topo_dist;
+    char dummy[40];  /* to fix compiler bug on IBM system */
+
+    T = (jd - J2000) / 36525.;  /* jul cent. since J2000.0 */
+
+    lambda = 218.32 + 481267.883 * T 
+        + 6.29 * sin((134.9 + 477198.85 * T) / DEG_IN_RADIAN)
+        - 1.27 * sin((259.2 - 413335.38 * T) / DEG_IN_RADIAN)
+        + 0.66 * sin((235.7 + 890534.23 * T) / DEG_IN_RADIAN)
+        + 0.21 * sin((269.9 + 954397.70 * T) / DEG_IN_RADIAN)
+        - 0.19 * sin((357.5 + 35999.05 * T) / DEG_IN_RADIAN)
+        - 0.11 * sin((186.6 + 966404.05 * T) / DEG_IN_RADIAN);
+    lambda = lambda / DEG_IN_RADIAN;
+    beta = 5.13 * sin((93.3 + 483202.03 * T) / DEG_IN_RADIAN)
+        + 0.28 * sin((228.2 + 960400.87 * T) / DEG_IN_RADIAN)
+        - 0.28 * sin((318.3 + 6003.18 * T) / DEG_IN_RADIAN)
+        - 0.17 * sin((217.6 - 407332.20 * T) / DEG_IN_RADIAN);
+    beta = beta / DEG_IN_RADIAN;
+    pie = 0.9508 
+        + 0.0518 * cos((134.9 + 477198.85 * T) / DEG_IN_RADIAN)
+        + 0.0095 * cos((259.2 - 413335.38 * T) / DEG_IN_RADIAN)
+        + 0.0078 * cos((235.7 + 890534.23 * T) / DEG_IN_RADIAN)
+        + 0.0028 * cos((269.9 + 954397.70 * T) / DEG_IN_RADIAN);
+    pie = pie / DEG_IN_RADIAN;
+    distance = 1 / sin(pie);
+
+    l = cos(beta) * cos(lambda);
+    m = 0.9175 * cos(beta) * sin(lambda) - 0.3978 * sin(beta);
+    n = 0.3978 * cos(beta) * sin(lambda) + 0.9175 * sin(beta);
+
+    x = l * distance; 
+    y = m * distance; 
+    z = n * distance;  /* for topocentric correction */
+
+
+    /* lat isn't passed right on some IBM systems unless you do this
+       or something like it! */
+    sprintf(dummy,"%f",lat);
+
+    rad_lat = lat / DEG_IN_RADIAN;
+    rad_lst = sid / HRS_IN_RADIAN;
+
+    x = x - cos(rad_lat) * cos(rad_lst);
+    y = y - cos(rad_lat) * sin(rad_lst);
+    z = z - sin(rad_lat);
+
+
+    topo_dist = sqrt(x * x + y * y + z * z);
+
+    l = x / topo_dist; 
+    m = y / topo_dist; 
+    n = z / topo_dist;
+
+    alpha = SC_atan_circ(l,m);
+    delta = asin(n);
+
+    *ra = alpha * HRS_IN_RADIAN;
+
+    *dec = delta * DEG_IN_RADIAN;
+    
+    *dist = topo_dist;
+
+}
+
+/* More accurate (but more elaborate and slower) lunar
+   ephemeris, from Jean Meeus' *Astronomical Formulae For Calculators*,
+   pub. Willman-Bell.  Includes all the terms given there. */
+
+void SC_accumoon (jd,geolat,lst,elevsea,geora,geodec,geodist,topora,topodec,topodist)
+     double jd,geolat,lst,elevsea;
+     double *geora,*geodec,*geodist,*topora,*topodec,*topodist;
+{
+  double pie, dist;  /* horiz parallax */
+  double Lpr,M,Mpr,D,F,Om,T,Tsq,Tcb;
+  double e,lambda,B,beta,om1,om2;
+  double sinx, x, y, z, l, m, n;
+  double x_geo, y_geo, z_geo;  /* geocentric position of *observer* */
+
+  jd = jd + SC_etcorr(jd)/SEC_IN_DAY;   /* approximate correction to ephemeris time */
+  T = (jd - 2415020.) / 36525.;   /* this based around 1900 ... */
+  Tsq = T * T;
+  Tcb = Tsq * T;
+
+  Lpr = 270.434164 + 481267.8831 * T - 0.001133 * Tsq
+    + 0.0000019 * Tcb;
+  M = 358.475833 + 35999.0498*T - 0.000150*Tsq
+    - 0.0000033*Tcb;
+  Mpr = 296.104608 + 477198.8491*T + 0.009192*Tsq
+    + 0.0000144*Tcb;
+  D = 350.737486 + 445267.1142*T - 0.001436 * Tsq
+    + 0.0000019*Tcb;
+  F = 11.250889 + 483202.0251*T -0.003211 * Tsq
+    - 0.0000003*Tcb;
+  Om = 259.183275 - 1934.1420*T + 0.002078*Tsq
+    + 0.0000022*Tcb;
+
+  Lpr = SC_circulo(Lpr);
+  Mpr = SC_circulo(Mpr);
+  M = SC_circulo(M);
+  D = SC_circulo(D);
+  F = SC_circulo(F);
+  Om = SC_circulo(Om);
+
+
+  sinx =  sin((51.2 + 20.2 * T)/DEG_IN_RADIAN);
+  Lpr = Lpr + 0.000233 * sinx;
+  M = M - 0.001778 * sinx;
+  Mpr = Mpr + 0.000817 * sinx;
+  D = D + 0.002011 * sinx;
+
+  sinx = 0.003964 * sin((346.560+132.870*T -0.0091731*Tsq)/DEG_IN_RADIAN);
+
+  Lpr = Lpr + sinx;
+  Mpr = Mpr + sinx;
+  D = D + sinx;
+  F = F + sinx;
+
+  sinx = sin(Om/DEG_IN_RADIAN);
+  Lpr = Lpr + 0.001964 * sinx;
+  Mpr = Mpr + 0.002541 * sinx;
+  D = D + 0.001964 * sinx;
+  F = F - 0.024691 * sinx;
+  F = F - 0.004328 * sin((Om + 275.05 -2.30*T)/DEG_IN_RADIAN);
+
+  e = 1 - 0.002495 * T - 0.00000752 * Tsq;
+
+  M = M / DEG_IN_RADIAN;   /* these will all be arguments ... */
+  Mpr = Mpr / DEG_IN_RADIAN;
+  D = D / DEG_IN_RADIAN;
+  F = F / DEG_IN_RADIAN;
+
+  lambda = Lpr + 6.288750 * sin(Mpr)
+    + 1.274018 * sin(2*D - Mpr)
+    + 0.658309 * sin(2*D)
+    + 0.213616 * sin(2*Mpr)
+    - e * 0.185596 * sin(M)
+    - 0.114336 * sin(2*F)
+    + 0.058793 * sin(2*D - 2*Mpr)
+    + e * 0.057212 * sin(2*D - M - Mpr)
+    + 0.053320 * sin(2*D + Mpr)
+    + e * 0.045874 * sin(2*D - M)
+    + e * 0.041024 * sin(Mpr - M)
+    - 0.034718 * sin(D)
+    - e * 0.030465 * sin(M+Mpr)
+    + 0.015326 * sin(2*D - 2*F)
+    - 0.012528 * sin(2*F + Mpr)
+    - 0.010980 * sin(2*F - Mpr)
+    + 0.010674 * sin(4*D - Mpr)
+    + 0.010034 * sin(3*Mpr)
+    + 0.008548 * sin(4*D - 2*Mpr)
+    - e * 0.007910 * sin(M - Mpr + 2*D)
+    - e * 0.006783 * sin(2*D + M)
+    + 0.005162 * sin(Mpr - D);
+
+		/* And furthermore.....*/
+
+  lambda = lambda + e * 0.005000 * sin(M + D)
+    + e * 0.004049 * sin(Mpr - M + 2*D)
+    + 0.003996 * sin(2*Mpr + 2*D)
+    + 0.003862 * sin(4*D)
+    + 0.003665 * sin(2*D - 3*Mpr)
+    + e * 0.002695 * sin(2*Mpr - M)
+    + 0.002602 * sin(Mpr - 2*F - 2*D)
+    + e * 0.002396 * sin(2*D - M - 2*Mpr)
+    - 0.002349 * sin(Mpr + D)
+    + e * e * 0.002249 * sin(2*D - 2*M)
+    - e * 0.002125 * sin(2*Mpr + M)
+    - e * e * 0.002079 * sin(2*M)
+    + e * e * 0.002059 * sin(2*D - Mpr - 2*M)
+    - 0.001773 * sin(Mpr + 2*D - 2*F)
+    - 0.001595 * sin(2*F + 2*D)
+    + e * 0.001220 * sin(4*D - M - Mpr)
+    - 0.001110 * sin(2*Mpr + 2*F)
+    + 0.000892 * sin(Mpr - 3*D)
+    - e * 0.000811 * sin(M + Mpr + 2*D)
+    + e * 0.000761 * sin(4*D - M - 2*Mpr)
+    + e * e * 0.000717 * sin(Mpr - 2*M)
+    + e * e * 0.000704 * sin(Mpr - 2 * M - 2*D)
+    + e * 0.000693 * sin(M - 2*Mpr + 2*D)
+    + e * 0.000598 * sin(2*D - M - 2*F)
+    + 0.000550 * sin(Mpr + 4*D)
+    + 0.000538 * sin(4*Mpr)
+    + e * 0.000521 * sin(4*D - M)
+    + 0.000486 * sin(2*Mpr - D);
+
+/*              *eclongit = lambda;  */
+
+  B = 5.128189 * sin(F)
+    + 0.280606 * sin(Mpr + F)
+    + 0.277693 * sin(Mpr - F)
+    + 0.173238 * sin(2*D - F)
+    + 0.055413 * sin(2*D + F - Mpr)
+    + 0.046272 * sin(2*D - F - Mpr)
+    + 0.032573 * sin(2*D + F)
+    + 0.017198 * sin(2*Mpr + F)
+    + 0.009267 * sin(2*D + Mpr - F)
+    + 0.008823 * sin(2*Mpr - F)
+    + e * 0.008247 * sin(2*D - M - F)
+    + 0.004323 * sin(2*D - F - 2*Mpr)
+    + 0.004200 * sin(2*D + F + Mpr)
+    + e * 0.003372 * sin(F - M - 2*D)
+    + 0.002472 * sin(2*D + F - M - Mpr)
+    + e * 0.002222 * sin(2*D + F - M)
+    + e * 0.002072 * sin(2*D - F - M - Mpr)
+    + e * 0.001877 * sin(F - M + Mpr)
+    + 0.001828 * sin(4*D - F - Mpr)
+    - e * 0.001803 * sin(F + M)
+    - 0.001750 * sin(3*F)
+    + e * 0.001570 * sin(Mpr - M - F)
+    - 0.001487 * sin(F + D)
+    - e * 0.001481 * sin(F + M + Mpr)
+    + e * 0.001417 * sin(F - M - Mpr)
+    + e * 0.001350 * sin(F - M)
+    + 0.001330 * sin(F - D)
+    + 0.001106 * sin(F + 3*Mpr)
+    + 0.001020 * sin(4*D - F)
+    + 0.000833 * sin(F + 4*D - Mpr);
+  /* not only that, but */
+  B = B + 0.000781 * sin(Mpr - 3*F)
+    + 0.000670 * sin(F + 4*D - 2*Mpr)
+    + 0.000606 * sin(2*D - 3*F)
+    + 0.000597 * sin(2*D + 2*Mpr - F)
+    + e * 0.000492 * sin(2*D + Mpr - M - F)
+    + 0.000450 * sin(2*Mpr - F - 2*D)
+    + 0.000439 * sin(3*Mpr - F)
+    + 0.000423 * sin(F + 2*D + 2*Mpr)
+    + 0.000422 * sin(2*D - F - 3*Mpr)
+    - e * 0.000367 * sin(M + F + 2*D - Mpr)
+    - e * 0.000353 * sin(M + F + 2*D)
+    + 0.000331 * sin(F + 4*D)
+    + e * 0.000317 * sin(2*D + F - M + Mpr)
+    + e * e * 0.000306 * sin(2*D - 2*M - F)
+    - 0.000283 * sin(Mpr + 3*F);
+
+  om1 = 0.0004664 * cos(Om/DEG_IN_RADIAN);
+  om2 = 0.0000754 * cos((Om + 275.05 - 2.30*T)/DEG_IN_RADIAN);
+
+  beta = B * (1. - om1 - om2);
+  /*      *eclatit = beta; */
+
+  pie = 0.950724
+    + 0.051818 * cos(Mpr)
+    + 0.009531 * cos(2*D - Mpr)
+    + 0.007843 * cos(2*D)
+    + 0.002824 * cos(2*Mpr)
+    + 0.000857 * cos(2*D + Mpr)
+    + e * 0.000533 * cos(2*D - M)
+    + e * 0.000401 * cos(2*D - M - Mpr)
+    + e * 0.000320 * cos(Mpr - M)
+    - 0.000271 * cos(D)
+    - e * 0.000264 * cos(M + Mpr)
+    - 0.000198 * cos(2*F - Mpr)
+    + 0.000173 * cos(3*Mpr)
+    + 0.000167 * cos(4*D - Mpr)
+    - e * 0.000111 * cos(M)
+    + 0.000103 * cos(4*D - 2*Mpr)
+    - 0.000084 * cos(2*Mpr - 2*D)
+    - e * 0.000083 * cos(2*D + M)
+    + 0.000079 * cos(2*D + 2*Mpr)
+    + 0.000072 * cos(4*D)
+    + e * 0.000064 * cos(2*D - M + Mpr)
+    - e * 0.000063 * cos(2*D + M - Mpr)
+    + e * 0.000041 * cos(M + D)
+    + e * 0.000035 * cos(2*Mpr - M)
+    - 0.000033 * cos(3*Mpr - 2*D)
+    - 0.000030 * cos(Mpr + D)
+    - 0.000029 * cos(2*F - 2*D)
+    - e * 0.000029 * cos(2*Mpr + M)
+    + e * e * 0.000026 * cos(2*D - 2*M)
+    - 0.000023 * cos(2*F - 2*D + Mpr)
+    + e * 0.000019 * cos(4*D - M - Mpr);
+
+  beta = beta/DEG_IN_RADIAN;
+  lambda = lambda/DEG_IN_RADIAN;
+  l = cos(lambda) * cos(beta);
+  m = sin(lambda) * cos(beta);
+  n = sin(beta);
+  SC_eclrot(jd,&l,&m,&n);
+
+  dist = 1/sin((pie)/DEG_IN_RADIAN);
+  x = l * dist;
+  y = m * dist;
+  z = n * dist;
+
+  *geora = SC_atan_circ(l,m) * HRS_IN_RADIAN;
+  *geodec = asin(n) * DEG_IN_RADIAN;
+  *geodist = dist;
+
+  SC_geocent (lst, geolat, elevsea, &x_geo, &y_geo, &z_geo);
+
+  x = x - x_geo;  /* topocentric correction using elliptical earth fig. */
+  y = y - y_geo;
+  z = z - z_geo;
+
+  *topodist = sqrt(x*x + y*y + z*z);
+
+  l = x / (*topodist);
+  m = y / (*topodist);
+  n = z / (*topodist);
+
+  *topora = SC_atan_circ(l,m) * HRS_IN_RADIAN;
+  *topodec = asin(n) * DEG_IN_RADIAN;
+
+}
+
+/* returns jd at which moon is at a given
+   altitude, given jdguess as a starting point. In current version
+   uses high-precision moon -- execution time does not seem to be
+   excessive on modern hardware.  If it's a problem on your machine,
+   you can replace calls to 'accumoon' with 'lpmoon' and remove
+   the 'elevsea' argument. */
+double SC_jd_moon_alt (double alt, double jdguess, double lat, double longit, double elevsea) {
+
+  double jdout;
+  double deriv, err, del = 0.002;
+  double ra,dec,dist,geora,geodec,geodist,sid,ha,alt2,alt3,az;
+  short i = 0;
+
+  /* first guess */
+
+  sid=SC_lst(jdguess,longit);
+  SC_accumoon(jdguess,lat,sid,elevsea,&geora,&geodec,&geodist,
+	   &ra,&dec,&dist);
+  ha = SC_lst(jdguess,longit) - ra;
+  alt2 = SC_altit(dec,ha,lat,&az);
+  jdguess = jdguess + del;
+  sid = SC_lst(jdguess,longit);
+  SC_accumoon(jdguess,lat,sid,elevsea,&geora,&geodec,&geodist,
+	   &ra,&dec,&dist);
+  alt3 = SC_altit(dec,(sid - ra),lat,&az);
+  err = alt3 - alt;
+  deriv = (alt3 - alt2) / del;
+  while((fabs(err) > 0.1) && (i < 10)) {
+    jdguess = jdguess - err/deriv;
+    sid=SC_lst(jdguess,longit);
+    SC_accumoon(jdguess,lat,sid,elevsea,&geora,&geodec,&geodist,
+	     &ra,&dec,&dist);
+    alt3 = SC_altit(dec,(sid - ra),lat,&az);
+    err = alt3 - alt;
+    i++;
+  }
+  if(i >= 9) jdguess = -1000.;
+  jdout = jdguess;
+  return(jdout);
+}
+
+/* Given site position, return Moonset for closest midnight */
+double SC_moonset_tonight (struct SC_date_time date, double lat, double longit, double elevsea, double elev) {
+
+  double jd, jdmid, stmid;
+  double min_alt, max_alt;
+  double geora, geodec, geodist;  /* geocent for moon, not used here.*/
+  double ramoon, decmoon, distmoon;
+  double hamoonrise, hamoonset, tmoonrise, tmoonset, jdmoonrise, jdmoonset;
+  short dow; /* day of week */
+  struct SC_date_time date_midnight, tmpdate;
+  double dt, lst0, lst1, djd, horiz;
+
+  horiz = sqrt (2. * elev / 6378140.) * DEG_IN_RADIAN;
+
+  /* find offset in hours from longit to greenwich */
+  jd = SC_date_to_jd (date);  /* true jd now */
+  lst0 = SC_lst (jd, 0.0);    /* lst at long = 0 */
+  lst1 = SC_lst (jd, longit); /* local lst now */
+  dt = lst0 - lst1;
+  if (dt < 0) dt += 24;
+	
+  /* midnight at greenwich */
+  date_midnight = date;
+  date_midnight.h = 0;
+  date_midnight.mn = 0;
+  date_midnight.s = 0;
+	
+  /* find jd for local midnight, select the *closest* midnight */
+  jdmid = SC_date_to_jd (date_midnight) - dt / 24.0;
+  djd = jd - jdmid;
+  if (djd < -0.5) jdmid -= 1.0;
+  if (djd >  0.5) jdmid += 1.0;
+  stmid = SC_lst (jdmid,longit); 
+
+  SC_accumoon (jdmid,lat,stmid,elevsea,&geora,&geodec,&geodist,&ramoon,&decmoon,&distmoon);
+
+  SC_min_max_alt (lat,decmoon,&min_alt,&max_alt);  /* rough check -- occurs? */
+  if (max_alt < -(0.83+horiz)) return (-1);
+  if (min_alt > -(0.83+horiz)) return (-1);
+
+  /* compute moonrise and set if they're likely to occur */
+
+  hamoonset = SC_ha_alt(decmoon,lat,-(0.83+horiz)); /* rough approx. */
+
+  tmoonset = SC_adj_time(ramoon+hamoonset-stmid);
+  jdmoonset = jdmid + tmoonset / 24.;
+  jdmoonset = SC_jd_moon_alt(-(0.83+horiz),jdmoonset,lat,longit,elevsea);
+
+  return (jdmoonset);
+
+}
+
+/* Given site position, return Moonrise for closest midnight */
+double SC_moonrise_tonight (struct SC_date_time date, double lat, double longit, double elevsea, double elev) {
+
+  double jd, jdmid, stmid;
+  double min_alt, max_alt;
+  double geora, geodec, geodist;  /* geocent for moon, not used here.*/
+  double ramoon, decmoon, distmoon;
+  double hamoonrise, hamoonset, tmoonrise, tmoonset, jdmoonrise, jdmoonset;
+  short dow; /* day of week */
+  struct SC_date_time date_midnight, tmpdate;
+  double dt, lst0, lst1, djd, horiz;
+
+  horiz = sqrt (2. * elev / 6378140.) * DEG_IN_RADIAN;
+
+  /* find offset in hours from longit to greenwich */
+  jd = SC_date_to_jd (date);  /* true jd now */
+  lst0 = SC_lst (jd, 0.0);    /* lst at long = 0 */
+  lst1 = SC_lst (jd, longit); /* local lst now */
+  dt = lst0 - lst1;
+  if (dt < 0) dt += 24;
+	
+  /* midnight at greenwich */
+  date_midnight = date;
+  date_midnight.h = 0;
+  date_midnight.mn = 0;
+  date_midnight.s = 0;
+	
+  /* find jd for local midnight, select the *closest* midnight */
+  jdmid = SC_date_to_jd (date_midnight) - dt / 24.0;
+  djd = jd - jdmid;
+  if (djd < -0.5) jdmid -= 1.0;
+  if (djd >  0.5) jdmid += 1.0;
+  stmid = SC_lst (jdmid,longit); 
+
+  SC_accumoon (jdmid,lat,stmid,elevsea,&geora,&geodec,&geodist,&ramoon,&decmoon,&distmoon);
+
+  SC_min_max_alt (lat,decmoon,&min_alt,&max_alt);  /* rough check -- occurs? */
+  if (max_alt < -(0.83+horiz)) return (-1);
+  if (min_alt > -(0.83+horiz)) return (-1);
+
+  /* compute moonrise and set if they're likely to occur */
+
+  hamoonset = SC_ha_alt(decmoon,lat,-(0.83+horiz)); /* rough approx. */
+
+  tmoonrise = SC_adj_time(ramoon-hamoonset-stmid);
+  jdmoonrise = jdmid + tmoonrise / 24.;
+  jdmoonrise = SC_jd_moon_alt(-(0.83+horiz),jdmoonrise,lat,longit,elevsea);
+
+  return (jdmoonrise);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/moon_interference.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/moon_interference.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/moon_interference.c	(revision 22322)
@@ -0,0 +1,875 @@
+/*
+   This program segment is extracted from the skycalc program written
+   by John Thorstensen from Dartmouth College.  For question please
+   contact: John.Thorstensen@Dartmouth.edu
+   This is one long and ugly, but accurate program.  Ideally I should
+   have split this into separate modules, but this may be replaced by
+   Bernt's algorithm anyway... so "let's wait and see what happens"   
+   -Rosemary Alles 04/18/2001
+   */
+
+#include <stdio.h>
+#include <math.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+
+/* some (not all) physical, mathematical, and astronomical constants
+   used are defined here. */
+#define  DEG_IN_RADIAN     57.2957795130823
+#define  HRS_IN_RADIAN     3.819718634205
+#define  EQUAT_RAD         6378137.    /* equatorial radius of earth, meters */
+#define  TWOPI             6.28318530717959
+#define  J2000             2451545.    /* Julian date at standard epoch */
+#define  SEC_IN_DAY        86400.
+
+struct coord {
+    short sign;  /* carry sign explicitly since -0 not neg. */
+    double hh;
+    double mm;
+    double ss;
+};
+
+struct date_time {
+    short y;
+    short mo;
+    short d;
+    short h;
+    short mn;
+    float s;
+};
+
+/* Gloabals */
+int update_on = 0; 
+double update_delta = 0.;
+
+/* Prototypes */
+void load_site(double* longit, double* lat, double* stdz,
+               short* use_dst, char* zone_name, char* zabr,
+               double* elevsea, double* elev, double* horiz,
+               char* site_name);
+double zone(short use_dst, double stdz,
+            double jd, double jdb, double jde);
+void caldat(double jdin, struct date_time* date, short *dow);
+short day_of_week(double jd);
+double day_of_year(double jd);
+void find_dst_bounds(short yr, double stdz, short use_dst,
+                     double *jdb, double* jde);
+int get_sys_date(struct date_time *date, short use_dst, short enter_ut,
+                 short night_date, double stdz, double toffset, double* jd);
+double lst(double jd, double longit);
+double date_to_jd(struct date_time date);
+void lpmoon(double jd, double lat, double sid,
+            double* ra, double* dec, double* dist);
+void put_coords(double deci, int precision, int showsign);
+double get_coord(void);
+void dec_to_bab (double deci, struct coord* bab);
+double atan_circ(double x, double y);
+double adj_time(double x);
+
+    
+void
+load_site(double* longit, double* lat, double* stdz,
+          short* use_dst, char* zone_name, char* zabr,
+          double* elevsea, double* elev, double* horiz,
+          char* site_name) {
+              
+/* sets the site-specific quantities; these are
+   longit     = W longitude in decimal hours
+   lat        = N latitude in decimal degrees
+   stdz       = standard time zone offset, hours
+   elevsea    = elevation above sea level (for absolute location)
+   elev       = observatory elevation above horizon, meters
+   horiz      = (derived) added zenith distance for rise/set due
+                to elevation
+   use_dst    = 0 don't use it
+                1 use USA convention
+                2 use Spanish convention
+                < 0 Southern hemisphere (reserved, unimplimented)
+   zone_name  = name of time zone, e. g. Eastern
+   zabr       = single-character abbreviation of time zone
+   site_name  = name of site.  */
+    
+    strcpy(site_name, "Mauna Kea, Hawaii");
+    strcpy(zone_name, "Hawaiian");
+    *zabr = 'H';
+    *use_dst = 0;
+    *longit = 10.36478;
+    *lat = 19.8267;
+    *elevsea = 4215.;
+    *elev = 4215.;  /* yow! */
+    *stdz = 10.;
+
+    /* now compute derived quantity "horiz" = depression of horizon.*/
+    *horiz = sqrt(2. * *elev / EQUAT_RAD) * DEG_IN_RADIAN;   
+}
+
+double
+zone(short use_dst, double stdz,
+     double jd, double jdb, double jde) {
+    
+    /* Returns zone time offset when standard time zone is stdz,
+       when daylight time begins (for the year) on jdb, and ends
+       (for the year) on jde.  This is parochial to the northern
+       hemisphere.  */
+    /* Extension -- specifying a negative value of use_dst reverses
+       the logic for the Southern hemisphere; then DST is assumed for
+       the Southern hemisphere summer (which is the end and beginning
+       of the year. */
+    
+    if(use_dst == 0) return(stdz);
+    else if((jd > jdb) && (jd < jde) && (use_dst > 0)) return(stdz-1.);
+    /* next line .. use_dst < 0 .. for Southern Hemisphere sites. */
+    else if(((jd < jdb) || (jd > jde)) && (use_dst < 0)) return(stdz-1.);
+    else return(stdz);
+}
+
+void
+caldat(double jdin, struct date_time* date, short *dow) {
+
+    /* from Jean Meeus, Astronomical Formulae for Calculators,
+       published by Willman-Bell Inc.
+       Avoids a copyrighted routine from Numerical Recipes.
+       Tested and works properly from the beginning of the 
+       Gregorian calendar era (1583) to beyond 3000 AD. */
+
+    double jdtmp;
+    long alpha;
+    long Z;
+    long A, B, C, D, E;
+    double F; 
+    double x;   /* for day-of-week calculation */
+
+    jdtmp = jdin + 0.5;
+
+    Z = (long) jdtmp;
+
+    x = Z/7.+0.01;
+    *dow = 7.*(x - (long) x);   /* truncate for day of week */
+
+    F = jdtmp - Z;
+
+    if(Z < 2299161) A = Z;
+    else {
+        alpha = (long) ((Z - 1867216.25) / 36524.25);
+        A = Z + 1 + alpha - (long) (alpha / 4);
+    }
+
+    B = A + 1524;
+    C = ((B - 122.1) / 365.25);
+    D =  (365.25 * C);\
+    E =  ((B - D) / 30.6001);
+
+    date->d = B - D - (long)(30.6001 * E);
+    if(E < 13.5) date->mo = E - 1;
+    else date->mo = E - 13;
+    if(date->mo  > 2.5)  date->y = C - 4716;
+    else date->y = C - 4715;
+	
+    date->h = F * 24.;  /* truncate */
+    date->mn = (F - ((float) date->h)/24.) * 1440.;
+    date->s = (F - ((float) date->h)/24. -
+               ((float) date->mn)/1440.) * 86400;
+	
+}
+
+short
+day_of_week(double jd) { 
+
+    /* returns day of week for a jd, 0 = Mon, 6 = Sun. */
+    
+    double x;
+    long i;
+    short d;
+    
+    jd = jd+0.5;
+    i = jd; /* truncate */
+    x = i/7.+0.01; 
+    d = 7.*(x - (long) x);   /* truncate */
+    return(d);
+}
+
+double
+day_of_year(double jd) {
+    
+    double jdjan0;
+    struct date_time date;
+    short dow;
+    
+    caldat(jd,&date,&dow);
+    /* find jd of "jan 0" = Dec 31 of previous year */
+    date.y = date.y - 1;
+    date.mo = 12;
+    date.d = 31;
+    date.h = 0;
+    date.mn = 0;
+    date.s = 0.;
+    jdjan0 = date_to_jd(date);
+    return(jd - jdjan0);
+}
+
+void
+find_dst_bounds(short yr, double stdz, short use_dst,
+                double *jdb, double* jde) {
+    
+	/* finds jd's at which daylight savings time begins 
+	    and ends.  The parameter use_dst allows for a number
+	    of conventions, namely:
+		0 = don't use it at all (standard time all the time)
+		1 = use USA convention (1st Sun in April to
+		     last Sun in Oct after 1986; last Sun in April before)
+		2 = use Spanish convention (for Canary Islands)
+		-1 = use Chilean convention (CTIO).
+		-2 = Australian convention (for AAT).
+	    Negative numbers denote sites in the southern hemisphere,
+	    where jdb and jde are beginning and end of STANDARD time for
+	    the year. 
+	    It's assumed that the time changes at 2AM local time; so
+	    when clock is set ahead, time jumps suddenly from 2 to 3,
+	    and when time is set back, the hour from 1 to 2 AM local 
+	    time is repeated.  This could be changed in code if need be. */
+
+	struct date_time trial;
+
+	if((use_dst == 1) || (use_dst == 0)) { 
+	    /* USA Convention, and including no DST to be defensive */
+	    /* Note that this ignores various wrinkles such as the
+		brief Nixon administration flirtation with year-round DST,
+		the extended DST of WW II, and so on. */
+		trial.y = yr;
+		trial.mo = 4;
+		if(yr >= 1986) trial.d = 1;
+		else trial.d = 30; 
+		trial.h = 2;
+		trial.mn = 0;
+		trial.s = 0;
+
+		/* Find first Sunday in April for 1986 on ... */
+		if(yr >= 1986) 
+			while(day_of_week(date_to_jd(trial)) != 6) 
+				trial.d++;
+			
+		/* Find last Sunday in April for pre-1986 .... */
+		else while(day_of_week(date_to_jd(trial)) != 6) 
+				trial.d--;
+
+		*jdb = date_to_jd(trial) + stdz/24.;    
+
+		/* Find last Sunday in October ... */
+		trial.mo = 10;
+		trial.d = 31;
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d--;
+		}
+		*jde = date_to_jd(trial) + (stdz - 1.)/24.;             
+	}
+	else if (use_dst == 2) {  /* Spanish, for Canaries */
+		trial.y = yr;
+		trial.mo = 3;
+		trial.d = 31; 
+		trial.h = 2;
+		trial.mn = 0;
+		trial.s = 0;
+
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d--;
+		}
+		*jdb = date_to_jd(trial) + stdz/24.;    
+		trial.mo = 9;
+		trial.d = 30;
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d--;
+		}
+		*jde = date_to_jd(trial) + (stdz - 1.)/24.;             
+	}               
+	else if (use_dst == -1) {  /* Chilean, for CTIO, etc.  */
+	   /* off daylight 2nd Sun in March, onto daylight 2nd Sun in October */
+		trial.y = yr;
+		trial.mo = 3;
+		trial.d = 8;  /* earliest possible 2nd Sunday */
+		trial.h = 2;
+		trial.mn = 0;
+		trial.s = 0;
+
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d++;
+		}
+		*jdb = date_to_jd(trial) + (stdz - 1.)/24.;
+			/* note jdb is beginning of STANDARD time in south,
+				hence use stdz - 1. */  
+		trial.mo = 10;
+		trial.d = 8;
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d++;
+		}
+		*jde = date_to_jd(trial) + stdz /24.;           
+	}                       
+	else if (use_dst == -2) {  /* For Anglo-Australian Telescope  */
+	   /* off daylight 1st Sun in March, onto daylight last Sun in October */
+		trial.y = yr;
+		trial.mo = 3;
+		trial.d = 1;  /* earliest possible 1st Sunday */
+		trial.h = 2;
+		trial.mn = 0;
+		trial.s = 0;
+
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d++;
+		}
+		*jdb = date_to_jd(trial) + (stdz - 1.)/24.;
+			/* note jdb is beginning of STANDARD time in south,
+				hence use stdz - 1. */  
+		trial.mo = 10;
+		trial.d = 31;
+		while(day_of_week(date_to_jd(trial)) != 6) {
+			trial.d--;
+		}
+		*jde = date_to_jd(trial) + stdz /24.;           
+	}               
+}
+
+int
+get_sys_date(struct date_time *date, short use_dst, short enter_ut,
+             short night_date, double stdz, double toffset, double* jd) {
+
+    /* Reads the system clock; loads up the date structure
+       to conform to the prevailing conventions for the interpretation
+       of times.  Optionally adds "toffset" minutes to the system
+       clock, as in x minutes in the future. */
+
+    time_t t, *tp;
+    struct tm *stm;
+    double jdb, jde;
+    short dow;
+
+    tp = &t;  /* have to initialize pointer variable for it to
+                 serve as an argument. */
+
+    t = time(tp);
+    if(t == -1) {
+        printf("error: system time unavailable during calculation of moon interference\n");
+        return(-1);
+    }
+    stm = localtime(&t);
+    date->y = (short) (stm->tm_year + 1900);
+    date->mo = (short) (stm->tm_mon + 1);
+    date->d = (short) (stm->tm_mday);
+    date->h = (short) (stm->tm_hour);
+    date->mn = (short) (stm->tm_min);
+    date->s = (float) (stm->tm_sec);
+
+    if(toffset != 0.) {
+        *jd = date_to_jd(*date);
+        *jd = *jd + toffset / 1440.;
+        caldat(*jd,date,&dow);
+    }
+
+    if(enter_ut == 1)  { /* adjust if needed */
+        find_dst_bounds(date->y,stdz,use_dst,&jdb,&jde);
+        *jd = date_to_jd(*date);
+        *jd = *jd + zone(use_dst,stdz,*jd,jdb,jde)/24.;
+        caldat(*jd,date,&dow);
+    }
+    if((night_date == 1) && (date->h < 12)) {
+        date->d = date->d - 1;
+    }
+
+    return(0); /* success */
+}
+
+double
+lst(double jd, double longit) {
+
+    /* returns the local MEAN sidereal time (dec hrs) at julian date jd
+       at west longitude long (decimal hours).  Follows
+       definitions in 1992 Astronomical Almanac, pp. B7 and L2. 
+       Expression for GMST at 0h ut referenced to Aoki et al, A&A 105,
+       p.359, 1982.  On workstations, accuracy (numerical only!)
+       is about a millisecond in the 1990s. */
+
+    double t, ut, jdmid, jdint, jdfrac, sid_g;
+    long jdin, sid_int;
+
+    jdin = jd;         /* fossil code from earlier package which 
+                          split jd into integer and fractional parts ... */
+    jdint = jdin;
+    jdfrac = jd - jdint;
+    if(jdfrac < 0.5) {
+        jdmid = jdint - 0.5;
+        ut = jdfrac + 0.5;
+    }
+    else {
+        jdmid = jdint + 0.5;
+        ut = jdfrac - 0.5;
+    }
+    t = (jdmid - J2000)/36525;
+    sid_g = (24110.54841+8640184.812866*t+0.093104*t*t-6.2e-6*t*t*t)/SEC_IN_DAY;
+    sid_int = sid_g;
+    sid_g = sid_g - (double) sid_int;
+    sid_g = sid_g + 1.0027379093 * ut - longit/24.;
+    sid_int = sid_g;
+    sid_g = (sid_g - (double) sid_int) * 24.;
+    if(sid_g < 0.) sid_g = sid_g + 24.;
+    return(sid_g);
+}
+
+
+double
+date_to_jd(struct date_time date) {
+    
+/* From Meeus' Astronomical Formulae for Calculators.  The two JD
+   conversion routines routines were replaced 1998 November 29 to
+   avoid inclusion of copyrighted "Numerical Recipes" code.  A test
+   of 1 million random JDs between 1585 and 3200 AD gave the same
+   conversions as the NR routines. */
+
+
+    double jd;
+    int y, m;
+    long A, B;
+
+    if(date.mo <= 2) {
+        y = date.y - 1;
+        m = date.mo + 12;
+    }
+    else {
+        y = date.y;
+        m = date.mo;
+    }
+
+    A = (long) (y / 100.);
+    B = 2 - A + (long) (A / 4.);
+
+    jd = (long) (365.25 * y) + (long) (30.6001 * (m + 1)) + date.d + 
+        1720994.5;
+
+    jd += date.h / 24. + date.mn / 1440. + date.s / 86400.;
+
+    if(date.y > 1583) return(jd + B);  
+    else return(jd);
+    /* Not quite right, since Gregorian calendar first
+       adopted around Oct 1582.  But fine for modern. */
+}
+
+void
+lpmoon(double jd, double lat, double sid,
+       double* ra, double* dec, double* dist) {
+
+
+/* implements "low precision" moon algorithms from
+   Astronomical Almanac (p. D46 in 1992 version).  Does
+   apply the topocentric correction. 
+   Units are as follows
+   jd,lat, sid;   decimal hours 
+   *ra, *dec,   decimal hours, degrees 
+   *dist;      earth radii */
+
+
+    double T, lambda, beta, pie, l, m, n, x, y, z, alpha, delta,
+        rad_lat, rad_lst, distance, topo_dist;
+    char dummy[40];  /* to fix compiler bug on IBM system */
+
+    T = (jd - J2000) / 36525.;  /* jul cent. since J2000.0 */
+
+    lambda = 218.32 + 481267.883 * T 
+        + 6.29 * sin((134.9 + 477198.85 * T) / DEG_IN_RADIAN)
+        - 1.27 * sin((259.2 - 413335.38 * T) / DEG_IN_RADIAN)
+        + 0.66 * sin((235.7 + 890534.23 * T) / DEG_IN_RADIAN)
+        + 0.21 * sin((269.9 + 954397.70 * T) / DEG_IN_RADIAN)
+        - 0.19 * sin((357.5 + 35999.05 * T) / DEG_IN_RADIAN)
+        - 0.11 * sin((186.6 + 966404.05 * T) / DEG_IN_RADIAN);
+    lambda = lambda / DEG_IN_RADIAN;
+    beta = 5.13 * sin((93.3 + 483202.03 * T) / DEG_IN_RADIAN)
+        + 0.28 * sin((228.2 + 960400.87 * T) / DEG_IN_RADIAN)
+        - 0.28 * sin((318.3 + 6003.18 * T) / DEG_IN_RADIAN)
+        - 0.17 * sin((217.6 - 407332.20 * T) / DEG_IN_RADIAN);
+    beta = beta / DEG_IN_RADIAN;
+    pie = 0.9508 
+        + 0.0518 * cos((134.9 + 477198.85 * T) / DEG_IN_RADIAN)
+        + 0.0095 * cos((259.2 - 413335.38 * T) / DEG_IN_RADIAN)
+        + 0.0078 * cos((235.7 + 890534.23 * T) / DEG_IN_RADIAN)
+        + 0.0028 * cos((269.9 + 954397.70 * T) / DEG_IN_RADIAN);
+    pie = pie / DEG_IN_RADIAN;
+    distance = 1 / sin(pie);
+
+    l = cos(beta) * cos(lambda);
+    m = 0.9175 * cos(beta) * sin(lambda) - 0.3978 * sin(beta);
+    n = 0.3978 * cos(beta) * sin(lambda) + 0.9175 * sin(beta);
+
+    x = l * distance; 
+    y = m * distance; 
+    z = n * distance;  /* for topocentric correction */
+
+
+    /* lat isn't passed right on some IBM systems unless you do this
+       or something like it! */
+    sprintf(dummy,"%f",lat);
+
+    rad_lat = lat / DEG_IN_RADIAN;
+    rad_lst = sid / HRS_IN_RADIAN;
+
+    x = x - cos(rad_lat) * cos(rad_lst);
+    y = y - cos(rad_lat) * sin(rad_lst);
+    z = z - sin(rad_lat);
+
+
+    topo_dist = sqrt(x * x + y * y + z * z);
+
+    l = x / topo_dist; 
+    m = y / topo_dist; 
+    n = z / topo_dist;
+
+    alpha = atan_circ(l,m);
+    delta = asin(n);
+
+    *ra = alpha * HRS_IN_RADIAN;
+
+    *dec = delta * DEG_IN_RADIAN;
+    
+    *dist = topo_dist;
+
+}
+
+
+void
+put_coords(double deci, int precision, int showsign) {
+
+
+/* prints out a struct coord in a nice format; precision
+   is a code for how accurate you want it.  The options are:
+     precision = 0;   minutes rounded to the nearest minute
+     precision = 1;   minutes rounded to the nearest tenth.
+     precision = 2;   seconds rounded to the nearest second
+     precision = 3;   seconds given to the tenth
+     precision = 4;   seconds given to the hundredth
+   The program assumes that the line is ready for the coord
+   to be printed and does NOT deliver a new line at the end
+   of the output. */
+
+   
+   double minutes;  /* for rounding off if necess. */
+   struct coord out_coord, coords;
+   char out_string[20];  /* for checking for nasty 60's */
+
+   dec_to_bab(deci,&coords);  /* internally convert to coords*/
+
+   if(coords.sign == -1) printf("-");
+	else printf(" "); /* to preserve alignment */
+
+   if(precision == 0) {   /* round to nearest minute */
+      minutes = coords.mm + coords.ss / 60.;
+           /* check to be sure minutes aren't 60 */
+      sprintf(out_string,"%.0f %02.0f",coords.hh,minutes);
+      sscanf(out_string,"%lf %lf",&out_coord.hh,&out_coord.mm);
+      if(fabs(out_coord.mm - 60.) < 1.0e-7) {
+         out_coord.mm = 0.;
+         out_coord.hh = out_coord.hh + 1.;
+      }
+      printf("%2.0f:%02.0f",out_coord.hh,out_coord.mm);
+   }
+
+   else if(precision == 1) {    /* keep nearest tenth of a minute */
+      minutes = coords.mm + coords.ss / 60.;
+           /* check to be sure minutes are not 60 */
+      sprintf(out_string,"%.0f %04.1f",coords.hh,minutes);
+      sscanf(out_string,"%lf %lf",&out_coord.hh, &out_coord.mm);
+      if(fabs(out_coord.mm - 60.) < 1.0e-7) {
+         out_coord.mm = 0.;
+         out_coord.hh = out_coord.hh + 1.;
+      }
+      printf("%2.0f:%04.1f", out_coord.hh, out_coord.mm);
+   }
+   else if(precision == 2) {
+          /* check to be sure seconds are not 60 */
+      sprintf(out_string,"%.0f %02.0f %02.0f",coords.hh,coords.mm,coords.ss);
+      sscanf(out_string,"%lf %lf %lf",&out_coord.hh,&out_coord.mm,
+           &out_coord.ss);
+      if(fabs(out_coord.ss - 60.) < 1.0e-7) {
+          out_coord.mm = out_coord.mm + 1.;
+          out_coord.ss = 0.;
+          if(fabs(out_coord.mm - 60.) < 1.0e-7) {
+              out_coord.hh = out_coord.hh + 1.;
+              out_coord.mm = 0.;
+          }
+      }
+      printf("%2.0f:%02.0f:%02.0f",out_coord.hh,out_coord.mm,out_coord.ss);
+   }
+   else if(precision == 3) {
+          /* the usual shuffle to check for 60's */
+      sprintf(out_string,"%.0f %02.0f %04.1f",coords.hh, coords.mm, coords.ss);
+      sscanf(out_string,"%lf %lf %lf",&out_coord.hh,&out_coord.mm,
+           &out_coord.ss);
+      if(fabs(out_coord.ss - 60.) < 1.0e-7) {
+          out_coord.mm = out_coord.mm + 1.;
+          out_coord.ss = 0.;
+          if(fabs(out_coord.mm - 60.) < 1.0e-7) {
+             out_coord.hh = out_coord.hh + 1.;
+             out_coord.mm = 0.;
+          }
+      }
+      printf("%2.0f:%02.0f:%04.1f",out_coord.hh,out_coord.mm,out_coord.ss);
+   }
+   else {
+      sprintf(out_string,"%.0f %02.0f %05.2f",coords.hh,coords.mm,coords.ss);
+      sscanf(out_string,"%lf %lf %lf",&out_coord.hh,&out_coord.mm,
+           &out_coord.ss);
+      if(fabs(out_coord.ss - 60.) < 1.0e-6) {
+         out_coord.mm = out_coord.mm + 1.;
+         out_coord.ss = 0.;
+         if(fabs(out_coord.mm - 60.) < 1.0e-6) {
+            out_coord.hh = out_coord.hh + 1.;
+            out_coord.mm = 0.;
+         }
+      }
+      printf("%2.0f:%02.0f:%05.2f",out_coord.hh, out_coord.mm, out_coord.ss);
+   }
+}
+
+
+double
+get_coord(void) {
+
+/* Reads a string from the terminal and converts it into
+   a double-precision coordinate.  This is trickier than 
+   it appeared at first, since a -00 tests as non-negative; 
+   the sign has to be picked out and handled explicitly. */
+/* Prompt for input in the calling routine.*/
+
+   short sign;
+   double hrs, mins, secs;
+   char hh_string[6];  /* string with the first coord (hh) */
+   char hh1[1];
+   short i = 0;
+
+   /* read and handle the hour (or degree) part with sign */
+
+   scanf("%s",hh_string);
+   hh1[0] = hh_string[i];
+
+   while(hh1[0] == ' ') {
+       /* discard leading blanks */
+       i++;
+       hh1[0] = hh_string[i];
+   }
+
+   if(hh1[0] == '-') sign = -1;
+
+     else sign = 1;
+
+   sscanf(hh_string,"%lf", &hrs);
+   if(sign == -1) hrs = -1. * hrs;
+
+   /* read in the minutes and seconds normally */
+   scanf("%lf %lf",&mins,&secs);
+
+   return(sign * (hrs + mins / 60. + secs / 3600.));
+}
+
+void
+dec_to_bab (double deci, struct coord* bab) {
+
+    /* function for converting decimal to babylonian hh mm ss.ss */
+    int hr_int, min_int;
+    
+    if (deci >= 0.) bab->sign = 1; 
+    else {
+        bab->sign = -1;
+        deci = -1. * deci;
+    }
+    hr_int = deci;   /* use conversion conventions to truncate */
+    bab->hh = hr_int;
+    min_int = 60. * (deci - bab->hh);
+    bab->mm = min_int;
+    bab->ss = 3600. * (deci - bab->hh - bab->mm / 60.);
+}
+
+double
+atan_circ(double x, double y) {
+    
+    /* returns radian angle 0 to 2pi for coords x, y --
+       get that quadrant right !! */
+    
+    double theta;
+    
+    if((x == 0.) && (y == 0.)) return(0.);  /* guard ... */
+    
+    theta = atan2(y,x);  /* turns out there is such a thing in math.h */
+    while(theta < 0.) theta += TWOPI;
+    return(theta);
+}
+
+
+double
+adj_time(double x) {
+
+    /* adjusts a time (decimal hours) to be between -12 and 12, 
+       generally used for hour angles.  */
+    
+    if(fabs(x) < 100000.) {  /* too inefficient for this! */
+        while(x > 12.) {
+            x = x - 24.;
+        }
+        while(x < -12.) {
+            x = x + 24.;
+        }
+    }
+
+    else printf("warning: Out of bounds in adj_time in moon interference routine!\n");
+
+    return(x);
+}
+
+double
+altit(double dec, double ha, double lat,
+      double* az, double *parang) {
+            
+/*
+  returns altitude(degr) for dec, ha, lat (decimal degr, hr, degr); 
+  also computes and returns azimuth through pointer argument,
+  and as an extra added bonus returns parallactic angle (decimal degr)
+  through another pointer argument.
+  */
+
+    double x,y,z;
+    double sinp, cosp;  /* sin and cos of parallactic angle */
+    double cosdec, sindec, cosha, sinha, coslat, sinlat;
+    /* time-savers ... */
+    
+    dec = dec / DEG_IN_RADIAN;
+    ha = ha / HRS_IN_RADIAN;
+    lat = lat / DEG_IN_RADIAN;  /* thank heavens for pass-by-value */
+    cosdec = cos(dec); sindec = sin(dec);
+    cosha = cos(ha); sinha = sin(ha);
+    coslat = cos(lat); sinlat = sin(lat);
+    x = DEG_IN_RADIAN * asin(cosdec*cosha*coslat + sindec*sinlat);
+    y =  sindec*coslat - cosdec*cosha*sinlat; /* due N comp. */
+    z =  -1. * cosdec*sinha; /* due east comp. */
+    *az = atan2(z,y);   
+    
+    /* as it turns out, having knowledge of the altitude and 
+       azimuth makes the spherical trig of the parallactic angle
+       less ambiguous ... so do it here!  Method uses the 
+       "astronomical triangle" connecting celestial pole, object,
+       and zenith ... now know all the other sides and angles,
+       so we can crush it ... */
+    
+    if(cosdec != 0.) { /* protect divide by zero ... */ 
+        sinp = -1. * sin(*az) * coslat / cosdec;
+        /* spherical law of sines .. note cosdec = sin of codec,
+           coslat = sin of colat .... */
+        cosp = -1. * cos(*az) * cosha - sin(*az) * sinha * sinlat;
+        /* spherical law of cosines ... also transformed to local
+           available variables. */
+	   *parang = atan2(sinp,cosp) * DEG_IN_RADIAN;
+           /* let the library function find the quadrant ... */
+    }
+    else { /* you're on the pole */
+        if(lat >= 0.) *parang = 180.;
+        else *parang = 0.;
+    }
+    
+    *az *= DEG_IN_RADIAN;  /* done with taking trig functions of it ... */ 
+    while(*az < 0.) *az += 360.;  /* force 0 -> 360 */
+    while(*az >= 360.) *az -= 360.;
+    
+    return(x);
+}
+
+
+int
+main(void) {
+
+    /* Site specific parameters */
+    double longit, lat;
+    double stdz;
+    short use_dst;
+    char zabr;
+    double elevsea;
+    double elev, horiz;
+
+    char site_name[45];  /* initialized later with
+                            strcpy for portability */
+    char zone_name[25];  /* this too */
+
+    struct date_time date;
+    double jd, sid;    
+
+    /* Position of moon */    
+    double ra, dec, dist;    
+    double ha, az, par, alt;    
+    
+    /* Misc stuff */
+    short enter_ut = 1;
+    short night_date = 0;
+    
+    /* Load site specific information */
+    load_site(&longit,&lat,&stdz,&use_dst,zone_name,&zabr,
+          &elevsea,&elev,&horiz,site_name);
+
+    /* Get system date and calculate julian date */
+    if (get_sys_date(&date, use_dst, enter_ut, night_date, stdz,
+                     update_delta, &jd) != 0) { 
+        printf("error: Can't get system date! \n");
+        return(-1);
+    }
+
+    /* Calcualte local sidereal time */
+    sid = lst(jd, longit);
+
+    /* Calculate position of moon */
+    lpmoon(jd, lat, sid, &ra, &dec, &dist);
+
+    ha = adj_time(sid - ra);
+    
+    alt=altit(dec, ha, lat, &az, &par);
+
+    /* Debug section
+      printf("moon ra = ");    
+      put_coords(ra, 2, 1);    
+      printf("\n"); 
+      printf("moon dec = ");  
+      put_coords(dec, 2, 1); 
+      printf("\n");
+      printf("moon ha = "); 
+      put_coords(ha, 2, 1); 
+      printf("\n");
+      printf("moon altitude = %.2f\n", alt); 
+      printf("moon zenith-distance = %.2f\n", 90 - alt);    
+      */
+      
+    /* Calculation of where to move */
+    /* If zenith distance is within 40 degrees then we move.
+       In order to do so the following logic will be applied:
+
+       1) Is zenith distance within 40 degrees (inclusive)
+       2) If so:
+             if dec > 20 then
+                move dec by -40
+             else
+                move dec by +40        
+          
+    */                      
+
+    if (fabs(90.0 - alt) <= 40) {
+        double adj_dec;        
+        /* Zenith distance within 40 degress */        
+        if (dec > lat) {
+            adj_dec = dec - 40.0;                              
+        }
+        else {
+            adj_dec = dec + 40.0;                     
+        }
+        printf("TRUE ");
+        put_coords(adj_dec, 2, 1);
+        printf("\n");    
+    }
+    else {
+        printf("FALSE\n");        
+    }
+    
+    return(0);    
+    
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/moondata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/moondata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/moondata.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include <skycalc_internal.h>
+# include <ohana.h>
+# define VERBOSE 0
+
+void SC_set_site (double *longit, double *lat, double *elevsea, double *elev) {
+
+  *longit = 10.36478; /*  W longitude in decimal hours */                     
+  *lat = 19.8267;     /*  N latitude in decimal degrees */                    
+  *elevsea = 4215.;   /* elevation above sea level (for absolute location) */ 
+  *elev = 4215.;      /* observatory elevation above horizon, meters */       
+
+}
+
+main (int argc, char **argv) {
+
+  struct SC_date_time date, tmpdate;
+  double longit, lat, elevsea, elev;
+  double jdnow, ra, dec, sid;
+  double geora, geodec, geodist, dist;
+  double RAo, DECo, abx, aby, abz, cs, theta;
+  double Rsun, Dsun, days;
+  time_t tzero;
+  struct tm *stm;
+
+  if (argc != 4) {
+    fprintf (stderr, "USAGE: moondata (date) (ra) (dec) [ra & dec in dec. deg.]\n");
+    exit (1);
+  }
+
+  RAo = atof (argv[2]);
+  DECo = atof (argv[3]);
+
+  if (!ohana_str_to_time (argv[1], &tzero)) { 
+    fprintf (stderr, "syntax error\n");
+    exit (1);
+  }
+  stm = gmtime (&tzero);
+  date.y  = (short) (stm->tm_year + 1900);
+  date.mo = (short) (stm->tm_mon + 1);
+  date.d  = (short) (stm->tm_mday);
+  date.h  = (short) (stm->tm_hour);
+  date.mn = (short) (stm->tm_min);
+  date.s  = (float) (stm->tm_sec);
+
+  /*
+  fprintf (stderr, "%4d/%02d/%02d %02d:%02d:%02f\n", date.y, date.mo, date.d, date.h, date.mn, date.s);
+  */
+
+  SC_set_site (&longit, &lat, &elevsea, &elev);
+  jdnow  = SC_date_to_jd (date);
+  /* Calcualte local sidereal time */
+  sid = SC_lst(jdnow, longit);
+  SC_lpsun (jdnow, &Rsun, &Dsun);
+
+  SC_accumoon(jdnow,lat,sid,elevsea,&geora,&geodec,&geodist,&ra,&dec,&dist);
+
+  /*  fprintf (stdout, "moon @ %f %f\n", 15*ra, dec); */
+
+  abx = cos(RAo*RAD_DEG)*cos(DECo*RAD_DEG)*cos(15*ra*RAD_DEG)*cos(dec*RAD_DEG);
+  aby = sin(RAo*RAD_DEG)*cos(DECo*RAD_DEG)*sin(15*ra*RAD_DEG)*cos(dec*RAD_DEG);
+  abz = sin(DECo*RAD_DEG)*sin(dec*RAD_DEG);
+
+  cs = abx + aby + abz;
+  
+  theta = DEG_RAD * acos (cs);
+
+  days = (Rsun - ra - 12)/24.0;
+  while (days < -0.5) days += 1.0;
+  while (days >  0.5) days -= 1.0;
+  days *= 29.5;
+
+  fprintf (stdout, "moon @ %f %f dist %f %f days from full\n", 15*ra, dec, theta, days);
+  exit (0);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/skylib.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/skylib.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/skylib.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include <stdio.h>
+# include <math.h>
+# include <skycalc.h>
+
+void set_site (double *longit, double *lat, double *elevsea, double *elev) {
+
+  *longit = 10.36478; /*  W longitude in decimal hours */                     
+  *lat = 19.8267;     /*  N latitude in decimal degrees */                    
+  *elevsea = 4215.;   /* elevation above sea level (for absolute location) */ 
+  *elev = 4215.;      /* observatory elevation above horizon, meters */       
+
+}
+
+main()
+
+{
+
+  struct SC_date_time date, tmpdate;
+  double longit, lat, elevsea, elev;
+  double jd;
+
+  SC_set_site (&longit, &lat, &elevsea, &elev);
+  SC_get_sys_date (&date);
+
+  jd = SC_sunset_tonight (date, lat, longit, elev);
+  fprintf (stderr, "Sunset (%5.0f m horizon): %f\n", elev, jd);
+  SC_jd_to_date (jd, &tmpdate);
+  fprintf (stderr, "%4d/%02d/%02d %02d:%02d:%02f\n", tmpdate.y, tmpdate.mo, tmpdate.d, tmpdate.h, tmpdate.mn, tmpdate.s);
+
+  jd = SC_sunrise_tonight (date, lat, longit, elev);
+  fprintf (stderr, "Sunrise (%5.0f m horizon): %f\n", elev, jd);
+  SC_jd_to_date (jd, &tmpdate);
+  fprintf (stderr, "%4d/%02d/%02d %02d:%02d:%02f\n", tmpdate.y, tmpdate.mo, tmpdate.d, tmpdate.h, tmpdate.mn, tmpdate.s);
+  
+
+  jd = SC_moonset_tonight (date, lat, longit, elevsea, elev);
+  fprintf (stderr, "Moonset (%5.0f m horizon): %f\n", elev, jd);
+  SC_jd_to_date (jd, &tmpdate);
+  fprintf (stderr, "%4d/%02d/%02d %02d:%02d:%02f\n", tmpdate.y, tmpdate.mo, tmpdate.d, tmpdate.h, tmpdate.mn, tmpdate.s);
+
+  jd = SC_moonrise_tonight (date, lat, longit, elevsea, elev);
+  fprintf (stderr, "Moonrise (%5.0f m horizon): %f\n", elev, jd);
+  SC_jd_to_date (jd, &tmpdate);
+  fprintf (stderr, "%4d/%02d/%02d %02d:%02d:%02f\n", tmpdate.y, tmpdate.mo, tmpdate.d, tmpdate.h, tmpdate.mn, tmpdate.s);
+
+}
+
+  /* set_zenith (date, lat, longit, objepoch, &objra, &objdec); */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/sun.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/sun.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/sun.c	(revision 22322)
@@ -0,0 +1,157 @@
+# include <skycalc_internal.h>
+
+/* Low precision formulae for the sun, from Almanac p. C24 (1990) */
+/* ra and dec are returned as decimal hours and decimal degrees. */
+void SC_lpsun (double jd, double *ra, double *dec) {
+
+  double n, L, g, lambda,epsilon,alpha,delta,x,y,z;
+
+  n = jd - J2000;
+  L = 280.460 + 0.9856474 * n;
+  g = (357.528 + 0.9856003 * n)/DEG_IN_RADIAN;
+  lambda = (L + 1.915 * sin(g) + 0.020 * sin(2. * g))/DEG_IN_RADIAN;
+  epsilon = (23.439 - 0.0000004 * n)/DEG_IN_RADIAN;
+
+  x = cos(lambda);
+  y = cos(epsilon) * sin(lambda);
+  z = sin(epsilon)*sin(lambda);
+
+  *ra = (SC_atan_circ(x,y))*HRS_IN_RADIAN;
+  *dec = (asin(z))*DEG_IN_RADIAN;
+}
+
+/* returns jd at which sun is at a given
+   altitude, given jdguess as a starting point. Uses
+   low-precision sun, which is plenty good enough. */
+double SC_jd_sun_alt (double alt, double jdguess, double lat, double longit) {
+
+  double jdout;
+  double deriv, err, del = 0.002;
+  double ra,dec,ha,alt2,alt3,az;
+  short i = 0;
+  
+  /* first guess */
+  
+  SC_lpsun (jdguess, &ra, &dec);
+  ha = SC_lst (jdguess,longit) - ra;
+  alt2 = SC_altit (dec,ha,lat,&az);
+  jdguess = jdguess + del;
+  SC_lpsun (jdguess,&ra,&dec);
+  alt3 = SC_altit(dec,(SC_lst(jdguess,longit) - ra),lat,&az);
+  err = alt3 - alt;
+  deriv = (alt3 - alt2) / del;
+  while((fabs(err) > 0.1) && (i < 10)) {
+    jdguess = jdguess - err/deriv;
+    SC_lpsun(jdguess,&ra,&dec);
+    alt3 = SC_altit(dec,(SC_lst(jdguess,longit) - ra),lat,&az);
+    err = alt3 - alt;
+    i++;
+    if(i == 9) printf ("Sunrise, set, or twilight calculation not converging!\n");
+  }
+  if(i >= 9) jdguess = -1000.;
+  jdout = jdguess;
+  return(jdout);
+}
+
+/* Given site position, prints Sun info for the given night.
+/* dates are all in UT now */
+double SC_sunset_tonight (struct SC_date_time date, double lat, double longit, double elev) {
+
+  double jd, jdmid0, jdmid, stmid;
+  double rasun, decsun, min_alt, max_alt, horiz;
+  double hasunset, jdsunset, jdsunrise, sid;
+  double dt, lst0, lst1, djd;
+  struct SC_date_time date_midnight, tmpdate;
+  short dow;
+
+  horiz = sqrt (2. * elev / 6378140.) * DEG_IN_RADIAN;
+
+  /* find offset in hours from longit to greenwich */
+  jd = SC_date_to_jd (date);  /* true jd now */
+  lst0 = SC_lst (jd, 0.0);    /* lst at long = 0 */
+  lst1 = SC_lst (jd, longit); /* local lst now */
+  dt = lst0 - lst1;
+  if (dt < 0) dt += 24;
+	
+  /* midnight at greenwich */
+  date_midnight = date;
+  date_midnight.h = 0;
+  date_midnight.mn = 0;
+  date_midnight.s = 0;
+	
+  /* find jd for local midnight, select the *closest* midnight */
+  jdmid0 = SC_date_to_jd (date_midnight);
+  jdmid = jdmid0 + dt / 24.0;
+  djd = jd - jdmid;
+  if (djd < -0.5) jdmid -= 1.0;
+  if (djd >  0.5) jdmid += 1.0;
+  stmid = SC_lst (jdmid,longit); 
+
+  /* sunset / sunrise hour angle */
+  SC_lpsun (jdmid, &rasun, &decsun);
+  hasunset = SC_ha_alt (decsun, lat, -(0.83+horiz));
+  if(hasunset > 900.) {  /* flag for never sets */
+    return (-1);
+  }
+  if(hasunset < -900.) {
+    return (-1);
+  }
+
+  /* find sunset time */
+  jdsunset = jdmid + SC_adj_time(rasun+hasunset-stmid)/24.;
+  jdsunset = SC_jd_sun_alt (-(0.83+horiz),jdsunset,lat,longit);
+
+  return (jdsunset);
+
+}
+
+/* Given site position, prints Sun info for the given night.
+/* dates are all in UT now */
+double SC_sunrise_tonight (struct SC_date_time date, double lat, double longit, double elev) {
+
+  double jd, jdmid, stmid;
+  double rasun, decsun, min_alt, max_alt, horiz;
+  double hasunset, jdsunset, jdsunrise, sid;
+  double dt, lst0, lst1, djd;
+  struct SC_date_time date_midnight, tmpdate;
+  short dow;
+
+  horiz = sqrt (2. * elev / 6378140.) * DEG_IN_RADIAN;
+
+  /* find offset in hours from longit to greenwich */
+  jd = SC_date_to_jd (date);  /* true jd now */
+  lst0 = SC_lst (jd, 0.0);    /* lst at long = 0 */
+  lst1 = SC_lst (jd, longit); /* local lst now */
+  dt = lst0 - lst1;
+  if (dt < 0) dt += 24;
+	
+  /* midnight at greenwich */
+  date_midnight = date;
+  date_midnight.h = 0;
+  date_midnight.mn = 0;
+  date_midnight.s = 0;
+	
+  /* find jd for local midnight, select the *closest* midnight */
+  jdmid = SC_date_to_jd (date_midnight) - dt / 24.0;
+  djd = jd - jdmid;
+  if (djd < -0.5) jdmid -= 1.0;
+  if (djd >  0.5) jdmid += 1.0;
+  stmid = SC_lst (jdmid,longit); 
+
+  /* sunset / sunrise hour angle */
+  SC_lpsun (jdmid, &rasun, &decsun);
+  hasunset = SC_ha_alt (decsun, lat, -(0.83+horiz));
+  if(hasunset > 900.) {  /* flag for never sets */
+    return (-1);
+  }
+  if(hasunset < -900.) {
+    fprintf (stderr, "Sun down all day!\n");
+    return (-1);
+  }
+
+  /* find sunrise time */
+  jdsunrise = jdmid + SC_adj_time(rasun-hasunset-stmid)/24.;
+  jdsunrise = SC_jd_sun_alt(-(0.83+horiz),jdsunrise,lat,longit);
+
+  return (jdsunrise);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/sundata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/sundata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/sundata.c	(revision 22322)
@@ -0,0 +1,77 @@
+# include <skycalc_internal.h>
+# include <ohana.h>
+# define VERBOSE 0
+
+void SC_set_site (double *longit, double *lat, double *elevsea, double *elev) {
+
+  *longit = 10.36478; /*  W longitude in decimal hours */                     
+  *lat = 19.8267;     /*  N latitude in decimal degrees */                    
+  *elevsea = 4215.;   /* elevation above sea level (for absolute location) */ 
+  *elev = 4215.;      /* observatory elevation above horizon, meters */       
+
+}
+
+main (int argc, char **argv) {
+
+  struct SC_date_time date, tmpdate;
+  double longit, lat, elevsea, elev;
+  double jdnow, ra, dec, sid, alt, az, sind, sinh, cosh;
+  double geora, geodec, geodist, dist;
+  double RAo, DECo, abx, aby, abz, cs, theta;
+  double Rsun, Dsun, days, Hsun;
+  time_t tzero;
+  struct tm *stm;
+
+  if (argc != 4) {
+    fprintf (stderr, "USAGE: sundata (date) (ra) (dec) [ra & dec in dec. deg.]\n");
+    exit (1);
+  }
+
+  RAo = atof (argv[2]);
+  DECo = atof (argv[3]);
+
+  if (!ohana_str_to_time (argv[1], &tzero)) { 
+    fprintf (stderr, "syntax error\n");
+    exit (1);
+  }
+  stm = gmtime (&tzero);
+  date.y  = (short) (stm->tm_year + 1900);
+  date.mo = (short) (stm->tm_mon + 1);
+  date.d  = (short) (stm->tm_mday);
+  date.h  = (short) (stm->tm_hour);
+  date.mn = (short) (stm->tm_min);
+  date.s  = (float) (stm->tm_sec);
+
+  if (VERBOSE) fprintf (stderr, "%4d/%02d/%02d %02d:%02d:%02f\n", date.y, date.mo, date.d, date.h, date.mn, date.s);
+
+  SC_set_site (&longit, &lat, &elevsea, &elev);
+  jdnow  = SC_date_to_jd (date);
+
+  /* Calculate local sidereal time */
+  sid = SC_lst(jdnow, longit);
+  SC_lpsun (jdnow, &Rsun, &Dsun);
+
+  if (VERBOSE) fprintf (stderr, "jdnow: %lf, sid: %lf\n", jdnow, sid);
+
+  /* dot product of unit vectors of (RAo,DECo) & (Rsun,Dsun) */
+  abx = dCOS(RAo)*dCOS(DECo)*dCOS(15*Rsun)*dCOS(Dsun);
+  aby = dSIN(RAo)*dCOS(DECo)*dSIN(15*Rsun)*dCOS(Dsun);
+  abz = dSIN(DECo)*dSIN(Dsun);
+  cs = abx + aby + abz;
+  theta = DEG_RAD * acos (cs);
+
+  /***** get sun altitude *****/
+  Hsun = 15.0*(sid - Rsun);
+ 
+  sind = dSIN (Dsun) * dSIN (lat) + dCOS (Dsun) * dCOS (Hsun) * dCOS (lat);
+  alt  = DEG_RAD * asin (sind);
+
+  sinh = - dCOS (Dsun) * dSIN (Hsun);
+  cosh =   dSIN (Dsun) * dCOS (lat) - dCOS (Dsun) * dCOS (Hsun) * dSIN (lat);
+  az = DEG_RAD * atan2 (sinh, cosh);
+
+  fprintf (stdout, "sun @ %f %f dist %f altaz: %f %f\n", 15*Rsun, Dsun, theta, alt, az);
+  exit (0);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/sunmoon.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/sunmoon.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/sunmoon.c	(revision 22322)
@@ -0,0 +1,123 @@
+# include <skycalc_internal.h>
+# include <ohana.h>
+# define VERBOSE 0
+
+# define MKO_LONGITUDE 10.36478 /*  W longitude in decimal hours */                     
+# define MKO_LATITUDE  19.82670 /*  N latitude in decimal degrees */                    
+# define MKO_ELEVATION   4215.0 /* elevation above sea level (for absolute location) */ 
+
+double angular_separation (double ra, double dec, double RA, double DEC);
+
+main (int argc, char **argv) {
+
+  int N;
+  struct SC_date_time date, tmpdate;
+  double longitude, latitude, elevation;
+  double jdnow, sid;
+  double moon_ra, moon_dec, moon_angle, moon_alt, moon_az, moon_ha, moon_dist;
+  double sun_ra, sun_dec, sun_angle, sun_alt, sun_az, sun_ha;
+  double geo_ra, geo_dec, geo_dist;
+  double RAo, DECo;
+  double phase;
+  time_t tzero;
+  struct tm *stm;
+
+  longitude = MKO_LONGITUDE; // MKO longitude in decimal hours
+  if ((N = get_argument (argc, argv, "-longitude"))) {
+    remove_argument (N, &argc, argv);
+    longitude = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  latitude = MKO_LATITUDE; // MKO latitude in decimal degrees
+  if ((N = get_argument (argc, argv, "-latitude"))) {
+    remove_argument (N, &argc, argv);
+    latitude = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  elevation = MKO_ELEVATION; // MKO elevation in meters
+  if ((N = get_argument (argc, argv, "-elevation"))) {
+    remove_argument (N, &argc, argv);
+    elevation = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    fprintf (stderr, "USAGE: sunmoon [-longitude long] [-latitude lat] [-elevation elev] (date) (ra) (dec)\n");
+    fprintf (stderr, " longitude : W long in decimal HOURS\n");
+    fprintf (stderr, " latitude  : N lat in decimal DEGREES\n");
+    fprintf (stderr, " elevation : meters above sea level\n");
+    fprintf (stderr, " ra & dec  : decimal degrees\n");
+    fprintf (stderr, " date      : YYYY/MM/DD,HH:MM:SS.SSS -- least significant elements are optional\n");
+    exit (1);
+  }
+
+  RAo = atof (argv[2]);
+  DECo = atof (argv[3]);
+
+  if (!ohana_str_to_time (argv[1], &tzero)) { 
+    fprintf (stderr, "syntax error\n");
+    exit (1);
+  }
+  stm = gmtime (&tzero);
+  date.y  = (short) (stm->tm_year + 1900);
+  date.mo = (short) (stm->tm_mon + 1);
+  date.d  = (short) (stm->tm_mday);
+  date.h  = (short) (stm->tm_hour);
+  date.mn = (short) (stm->tm_min);
+  date.s  = (float) (stm->tm_sec);
+
+  if (VERBOSE) fprintf (stderr, "%4d/%02d/%02d %02d:%02d:%02f\n", date.y, date.mo, date.d, date.h, date.mn, date.s);
+
+  // get JD
+  jdnow  = SC_date_to_jd (date);
+
+  // Calculate local sidereal time
+  sid = SC_lst(jdnow, longitude);
+
+  if (VERBOSE) fprintf (stderr, "jdnow: %lf, sid: %lf\n", jdnow, sid);
+
+  // get the sun coordinates
+  SC_lpsun (jdnow, &sun_ra, &sun_dec);
+  sun_ha = (sid - sun_ra);
+  sun_ra *= 15.0;
+  sun_alt = SC_altit (sun_dec, sun_ha, latitude, &sun_az);
+  sun_angle = angular_separation (sun_ra, sun_dec, RAo, DECo);
+
+  if (VERBOSE) fprintf (stderr, "sun @ ra,dec = %f %f : alt, az = %f %f\n", sun_ra, sun_dec, sun_alt, sun_az);
+
+  // get the moon coordintes
+  SC_accumoon (jdnow, latitude, sid, elevation, &geo_ra, &geo_dec, &geo_dist, &moon_ra, &moon_dec, &moon_dist);
+  moon_ha = (sid - moon_ra);
+  moon_ra *= 15.0; // convert to degrees
+  moon_alt = SC_altit (moon_dec, moon_ha, latitude, &moon_az);
+  moon_angle = angular_separation (moon_ra, moon_dec, RAo, DECo);
+
+  if (VERBOSE) fprintf (stderr, "moon @ ra,dec = %f %f : alt, az = %f %f\n", moon_ra, moon_dec, moon_alt, moon_az);
+
+  phase = (moon_ra - sun_ra)/360.0;
+  while (phase <  0.0) phase += 1.0;
+  while (phase >  1.0) phase -= 1.0;
+
+  if (VERBOSE) fprintf (stderr, "sun @ %f %f dist %f\n", sun_ra, sun_dec, sun_angle);
+  if (VERBOSE) fprintf (stderr, "moon @ %f %f angle %f phase %f\n", moon_ra, moon_dec, moon_angle, phase);
+
+  fprintf (stdout, "-sun_alt %f -sun_angle %f -moon_alt %f -moon_angle %f -moon_phase %f\n", sun_alt, sun_angle, moon_alt, moon_angle, phase);
+  exit (0);
+}
+
+double angular_separation (double ra, double dec, double RA, double DEC) {
+
+  double abx, aby, abz, cs, theta;
+
+  abx = cos(RA*RAD_DEG)*cos(DEC*RAD_DEG)*cos(ra*RAD_DEG)*cos(dec*RAD_DEG);
+  aby = sin(RA*RAD_DEG)*cos(DEC*RAD_DEG)*sin(ra*RAD_DEG)*cos(dec*RAD_DEG);
+  abz = sin(DEC*RAD_DEG)*sin(dec*RAD_DEG);
+
+  cs = abx + aby + abz;
+  
+  theta = DEG_RAD * acos (cs);
+
+  return theta;
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/time.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/time.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skycalc/src/time.c	(revision 22322)
@@ -0,0 +1,151 @@
+# include <skycalc_internal.h>
+
+/* fill date structure with current date & time (UT) */
+int SC_get_sys_date (struct SC_date_time *date) {
+
+  time_t t;
+  struct tm *stm;
+  double jd, jdb, jde;
+  short dow;
+  
+  t = time (0);
+  if (t == -1) return (-1);
+
+  stm = gmtime (&t);
+  date->y = (short) (stm->tm_year + 1900);
+  date->mo = (short) (stm->tm_mon + 1);
+  date->d = (short) (stm->tm_mday);
+  date->h = (short) (stm->tm_hour);
+  date->mn = (short) (stm->tm_min);
+  date->s = (float) (stm->tm_sec);
+
+  return (0); /* success */
+
+}
+
+/* convert a UT date to JD ( 1900 -- 2100?) */
+double SC_date_to_jd (struct SC_date_time date) {
+  
+  short yr1=0;
+  short mo1=1;
+  long jdzpt = 1720982;
+  long jdint, inter;
+  double jd, jdfrac;
+  
+  if ((date.y <= 1900) | (date.y >= 2100)) return (0.0);
+  
+  if (date.mo <= 2) {
+    yr1 = -1;
+    mo1 = 13;
+  }
+  
+  jdint = 365.25*(date.y+yr1);  /* truncates */
+  inter = 30.6001*(date.mo+mo1);
+  jdint = jdint+inter+date.d+jdzpt;
+  jd = jdint;
+  jdfrac=date.h/24.+date.mn/1440.+date.s/SEC_IN_DAY;
+  if (jdfrac < 0.5) {
+    jdint--;
+    jdfrac=jdfrac+0.5;
+  }
+  else jdfrac=jdfrac-0.5;
+  jd = jdint+jdfrac;
+  return (jd);
+}
+
+/* convert JD to a UT date & time */
+/* XXX can we drop this and use the unix time functions? */
+void SC_jd_to_date (double jdin, struct SC_date_time *date) {
+  
+#define IGREG 2299161
+
+  /* Adapted from Press, Flannery, Teukolsky, &
+     Vetterling, Numerical Recipes in C, (Cambridge
+     University Press), 1st edn, p. 12. */
+  
+  int mm, id, iyyy;  /* their notation */
+  long ja, jdint, jalpha, jb, jc, jd, je;
+  float jdfrac;
+  double x;
+
+  jdin = jdin + 0.5;  /* adjust for 1/2 day */
+  jdint = jdin;
+  x = jdint/7.+0.01;
+  jdfrac = jdin - jdint;
+  date->h = jdfrac * 24; /* truncate */
+  date->mn = (jdfrac - ((float) date->h)/24.) * 1440.;
+  date->s = (jdfrac - ((float) date->h)/24. -
+	     ((float) date->mn)/1440.) * SEC_IN_DAY;
+
+  if(jdint > IGREG) {
+    jalpha=((float) (jdint-1867216)-0.25)/36524.25;
+    ja=jdint+1+jalpha-(long)(0.25*jalpha);
+  }
+  else
+    ja=jdint;
+  jb=ja+1524;
+  jc=6680.0+((float) (jb-2439870)-122.1)/365.25;
+  jd=365*jc+(0.25*jc);
+  je=(jb-jd)/30.6001;
+  id=jb-jd-(int) (30.6001*je);
+  mm=je-1;
+  if(mm > 12) mm -= 12;
+  iyyy=jc-4715;
+  if(mm > 2) --iyyy;
+  if (iyyy <= 0) --iyyy;
+  date->y = iyyy;
+  date->mo = mm;
+  date->d = id;
+
+}
+
+
+/* returns the local MEAN sidereal time (dec hrs) at julian date jd
+   at west longitude long (decimal hours).  Follows
+   definitions in 1992 Astronomical Almanac, pp. B7 and L2.
+   Expression for GMST at 0h ut referenced to Aoki et al, A&A 105,
+   p.359, 1982.  On workstations, accuracy (numerical only!)
+   is about a millisecond in the 1990s. */
+double SC_lst (double jd, double longit) {
+  
+  double t, ut, jdmid, jdint, jdfrac, sid_g, sid;
+  long sid_int;
+  
+  jdint = (int) jd;
+  jdfrac = jd - jdint;
+
+  if (jdfrac < 0.5) {
+    jdmid = jdint - 0.5;
+    ut = jdfrac + 0.5;
+  }
+  else {
+    jdmid = jdint + 0.5;
+    ut = jdfrac - 0.5;
+  }
+  t = (jdmid - J2000)/36525;
+  sid_g = (24110.54841+8640184.812866*t+0.093104*t*t-6.2e-6*t*t*t)/SEC_IN_DAY;
+  sid_int = sid_g;
+  sid_g = sid_g - (double) sid_int;
+  sid_g = sid_g + 1.0027379093 * ut - longit/24.;
+  sid_int = sid_g;
+  sid_g = (sid_g - (double) sid_int) * 24.;
+  if (sid_g < 0.) sid_g = sid_g + 24.;
+  return (sid_g);
+}
+
+/* force time domain to be -12h and 12h  */
+double SC_adj_time (double x) {
+  
+  /* ridiculously inefficient - use modulo and fractions.. */
+  if(fabs(x) < 100000.) {  
+    while(x > 12.) {
+      x = x - 24.;
+    }
+    while(x < -12.) {
+      x = x + 24.;
+    }
+  }
+  else printf ("Out of bounds in adj_time!\n");
+  return(x);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/skyprobe/skyproberedccd
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/skyprobe/skyproberedccd	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/skyprobe/skyproberedccd	(revision 22322)
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+echo " FLIPS - skyproberedccd (imred)"
+if [ $# -ne 2 ] ; then
+  echo " Process: single SkyProbe CCD image reduction with Dark & Flat-Field"
+  echo " Syntax:  skyproberedccd FILE_IN FILE_OUT"
+  exit 1
+fi
+
+# Check if INPUT FITS file exists
+if [ ! -f $1 ]; then
+   echo "FITS file '$1' (FILE_IN = 1st argument) does not exist"
+   exit 1
+fi
+
+# Create temporary parameter file name
+TEMP="/tmp/redccd$$"
+
+# Prepare parameter file
+cat ~skyprobe/.skyprobeimred.param.CCD | sed "s|INFILE|$1|"   | sed "s|OUTFILE|$2|" > $TEMP
+
+# Set a default size for the buffer height
+echo "BUFFER"                       >> $TEMP
+echo "        height          240"  >> $TEMP
+echo "        end"                  >> $TEMP
+# CPU type: for swap or not
+echo "CPU"                          >> $TEMP
+echo "        swap            1"    >> $TEMP
+echo "        end"                  >> $TEMP
+# Timing info flag
+echo "TIMING"                       >> $TEMP
+echo "        timing          1"    >> $TEMP
+echo "        end"                  >> $TEMP
+# End of the parfile
+echo "END"                          >> $TEMP
+
+# Combine files
+imred_lx $TEMP
+
+# Remove temporary file
+rm -f $TEMP
+rm -f imred-stat.dat
+
+exit 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/Makefile	(revision 22322)
@@ -0,0 +1,20 @@
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/tcl
+
+tcl:
+
+default: install
+
+CODE =  edisp
+
+install:
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	for file in $(CODE); do \
+	(cd $(DESTBIN); rm -f $$file; ln -s $(HOME)/src/$$file .); \
+	done
+
+# utilities #################################################
+clean:	
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/src/edisp
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/src/edisp	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/src/edisp	(revision 22322)
@@ -0,0 +1,217 @@
+#!/usr/bin/env wish
+
+# define three components to the window:
+
+# text line with data value
+# image region with image
+# quit button
+
+if [catch {exec /apps/elixir/bin/gconfig DATDIR} datdir] { 
+    set picpath "ERROR" 
+} else {
+    set picpath "$datdir/plots"
+}
+
+set imname "(empty)"
+set name "(empty)"
+set pid [pid]
+set index 0
+
+proc mk_layout { } {
+
+    global file lasttime picpath
+
+    set lasttime 0
+    if {$picpath == "ERROR"} {
+	set file "ERROR"
+    } else {
+	set file "$picpath/empty.gif"
+    }
+    wm withdraw .
+
+    set w [toplevel .t]
+    wm title .t "user display"
+    
+    # top level containers
+    frame $w.menubar -relief raised -borderwidth 2
+    frame $w.picture -relief raised -borderwidth 2
+    frame $w.dataline -relief raised -borderwidth 2
+
+    pack $w.menubar -fill x
+    pack $w.picture -fill x
+    pack $w.dataline -fill x
+
+    mk_menubar $w.menubar
+    mk_picture $w.picture
+    mk_dataline $w.dataline
+
+    update
+}
+
+proc do_quit {} {
+    
+    exit 
+
+}
+
+proc mk_picture {w} {
+
+    global file
+
+    if {$file == "ERROR"} {
+	label $w.name -text "Config Error: missing DATDIR"
+	pack  $w.name -side top -fill both
+    } else {
+	image create photo picture -file $file
+	label $w.name -image picture
+	pack  $w.name -side top -fill both
+    }
+}
+
+proc mk_menubar {w} {
+
+    global file picpath cycle
+
+    menubutton $w.file -text "File" -menu $w.file.menu
+    pack $w.file -side left
+
+    menu $w.file.menu -tearoff 0
+    $w.file.menu add command -label "Quit" -command { do_quit }
+    bind all <Alt-q> { do_quit }
+    bind all <Control-q> { do_quit }
+    
+    menubutton $w.plot -text "Plots" -menu $w.plot.menu
+    pack $w.plot -side left
+
+    label $w.label -text "$file"
+    pack $w.label -side right
+
+    menu $w.plot.menu -tearoff 0
+    $w.plot.menu add command -label "Cycle"           -command { set cycle 1 ; cycleImages }
+    $w.plot.menu add command -label "biases"          -command { set cycle 0 ; set file "$picpath/biases.gif"; refresh_now;  }
+    $w.plot.menu add command -label "skyprobe recent" -command { set cycle 0 ; set file "$picpath/skyprobe_recent.gif"; refresh_now;  }
+    $w.plot.menu add command -label "skyprobe night"  -command { set cycle 0 ; set file "$picpath/skyprobe_night.gif"; refresh_now;  }
+    $w.plot.menu add command -label "sky vs time"     -command { set cycle 0 ; set file "$picpath/sky.time.gif"; refresh_now;  }
+    $w.plot.menu add command -label "sky vs airmass"  -command { set cycle 0 ; set file "$picpath/sky.airmass.gif"; refresh_now;  }
+    $w.plot.menu add command -label "Recent Seeing"    -command { set cycle 0 ; set file "$picpath/seeing_recent.gif"; refresh_now;  }
+    $w.plot.menu add command -label "Night Seeing"    -command { set cycle 0 ; set file "$picpath/seeing_night.gif"; refresh_now;  }
+    $w.plot.menu add command -label "FWHM vs airmass" -command { set cycle 0 ; set file "$picpath/fwhm.airmass.gif"; refresh_now;  }
+    $w.plot.menu add command -label "field (small)"   -command { set cycle 0 ; set file "$picpath/field.small.gif"; refresh_now;  }
+    $w.plot.menu add command -label "field (large)"   -command { set cycle 0 ; set file "$picpath/field.large.gif"; refresh_now;  }
+    $w.plot.menu add command -label "temperatures"    -command { set cycle 0 ; set file "$picpath/temps.time.gif"; refresh_now;  }
+    $w.plot.menu add command -label "last image (10x10)"   -command { set cycle 0 ; set file "$picpath/current.b10.gif"; refresh_now;  }
+    $w.plot.menu add command -label "last image (zoom)"     -command { set cycle 0 ; set file "$picpath/current.zoom.gif"; refresh_now;  }
+    $w.plot.menu add command -label "last short image" -command { set cycle 0 ; set file "$picpath/lastshort.zoom.gif"; refresh_now;  }
+    $w.plot.menu add command -label "focus (unit)"     -command { set cycle 0 ; set file "$picpath/focus.unit.gif"; refresh_now;  }
+
+}
+
+proc mk_dataline {w} {
+
+    global fwhm imname name file
+    set time [clock format [clock seconds] -format "%H:%M:%S"]
+
+    label $w.label -text "name: $name  |  FHWM: 0.0  |  FILTER: X  |  time: $time            | image: $imname"
+    pack $w.label -fill x
+
+}
+
+proc cycleImages { } {
+
+    global file cycle picpath index
+
+    set images { skyprobe_recent.gif sky.time.gif seeing_recent.gif biases.gif temps.time.gif current.b10.gif current.zoom.gif lastshort.zoom.gif }
+
+
+    if { $cycle == 0 } { return }
+
+    set file "$picpath/[lindex $images $index]"
+    refresh_now
+
+    incr index
+    if { $index == [llength $images] } {
+	set index 0
+    }
+
+    after 30000 {
+	cycleImages
+    }
+}
+
+proc do_refresh { } {
+
+    global picpath imname name file
+
+    after 1000 {
+	
+	set time [clock format [clock seconds] -format "%H:%M:%S"]
+	set name "(empty)"
+	set fwhm 0.0
+	set filter "X"
+
+	if {! [catch {open $picpath/seeing.dat r} data] } {
+	    gets $data line
+	    close $data
+	    set list [split $line]
+	    if {[llength $list] == 4} {
+		set name [lindex $list 0]
+		catch { set fwhm [format "%5.2f" [lindex $list 1]] } status
+		set filter [lindex $list 3]
+            }
+	}
+	
+	.t.dataline.label configure -text "name: $name  |  FHWM: $fwhm  |  FILTER: $filter  |  time: $time           | image: $imname"
+	.t.menubar.label configure -text "file: $file"
+	update
+	do_refresh 
+    }
+}
+
+proc do_refresh_pic { } {
+
+    global file lasttime imname name pid
+
+    after 2000 {
+	
+	# get file status & time
+	if {! [catch {file stat $file stats}] } {
+	    set newtime $stats(mtime)
+	    if {$newtime > $lasttime} {
+		set tmpfile "$file.$pid"
+		catch { exec ln -f $file $tmpfile }
+		catch { picture read $tmpfile -shrink } 
+		catch { exec rm -f $tmpfile }
+		set imname $name
+		set lasttime $newtime
+		update
+	    }
+	}
+	do_refresh_pic
+    }
+}
+
+proc bgerror {result} {
+	puts "edisp error: $result"
+}
+
+proc refresh_now { } {
+
+    global file lasttime imname name pid
+
+    if {[file exists $file]} {
+	set tmpfile "$file.$pid"
+	catch { exec ln -f $file $tmpfile }
+	catch { picture read $tmpfile -shrink }
+	catch { exec rm -f $tmpfile }
+
+	file stat $file stats
+	set imname $name
+	set lasttime $stats(mtime)
+	update
+	
+    }
+}
+
+mk_layout
+do_refresh
+do_refresh_pic
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/src/edisp-1.0
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/src/edisp-1.0	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/src/edisp-1.0	(revision 22322)
@@ -0,0 +1,175 @@
+#!/usr/bin/env wish
+
+# define three components to the window:
+
+# text line with data value
+# image region with image
+# quit button
+
+set picpath "/data/milo/elixir/plots"
+set imname "(empty)"
+set name "(empty)"
+set pid [pid]
+
+proc mk_layout { } {
+
+    global file lasttime picpath
+
+    set lasttime 0
+    set file "$picpath/empty.gif"
+    wm withdraw .
+
+    set w [toplevel .t]
+    wm title .t "user display"
+    
+    # top level containers
+    frame $w.menubar -relief raised -borderwidth 2
+    frame $w.picture -relief raised -borderwidth 2
+    frame $w.dataline -relief raised -borderwidth 2
+
+    pack $w.menubar -fill x
+    pack $w.picture -fill x
+    pack $w.dataline -fill x
+
+    mk_menubar $w.menubar
+    mk_picture $w.picture
+    mk_dataline $w.dataline
+
+    update
+}
+
+proc do_quit {} {
+    
+    exit 
+
+}
+
+proc mk_picture {w} {
+
+    global file
+
+    image create photo picture -file $file
+
+    label $w.name -image picture
+    pack  $w.name -side top -fill both
+}
+
+proc mk_menubar {w} {
+
+    global file picpath
+
+    menubutton $w.file -text "File" -menu $w.file.menu
+    pack $w.file -side left
+
+    menu $w.file.menu -tearoff 0
+    $w.file.menu add command -label "Quit" -command { do_quit }
+    bind all <Alt-q> { do_quit }
+    bind all <Control-q> { do_quit }
+    
+    menubutton $w.plot -text "Plots" -menu $w.plot.menu
+    pack $w.plot -side left
+
+    menu $w.plot.menu -tearoff 0
+    $w.plot.menu add command -label "biases"          -command { set file "$picpath/biases.gif"; refresh_now;  }
+    $w.plot.menu add command -label "skyprobe recent" -command { set file "$picpath/skyprobe_recent.gif"; refresh_now;  }
+    $w.plot.menu add command -label "skyprobe night"  -command { set file "$picpath/skyprobe_night.gif"; refresh_now;  }
+    $w.plot.menu add command -label "sky vs time"     -command { set file "$picpath/sky.time.gif"; refresh_now;  }
+    $w.plot.menu add command -label "sky vs airmass"  -command { set file "$picpath/sky.airmass.gif"; refresh_now;  }
+    $w.plot.menu add command -label "FWHM vs time"    -command { set file "$picpath/fwhm.time.gif"; refresh_now;  }
+    $w.plot.menu add command -label "FWHM vs airmass" -command { set file "$picpath/fwhm.airmass.gif"; refresh_now;  }
+    $w.plot.menu add command -label "field (small)"   -command { set file "$picpath/field.small.gif"; refresh_now;  }
+    $w.plot.menu add command -label "field (large)"   -command { set file "$picpath/field.large.gif"; refresh_now;  }
+    $w.plot.menu add command -label "temperatures"    -command { set file "$picpath/temps.time.gif"; refresh_now;  }
+    $w.plot.menu add command -label "last image (10x10)"   -command { set file "$picpath/current.b10.gif"; refresh_now;  }
+    $w.plot.menu add command -label "last image (zoom)"     -command { set file "$picpath/current.zoom.gif"; refresh_now;  }
+    $w.plot.menu add command -label "last short image" -command { set file "$picpath/lastshort.zoom.gif"; refresh_now;  }
+    $w.plot.menu add command -label "focus (unit)"     -command { set file "$picpath/focus.unit.gif"; refresh_now;  }
+
+}
+
+proc mk_dataline {w} {
+
+    global fwhm imname name
+    set time [clock format [clock seconds] -format "%H:%M:%S"]
+
+    label $w.label -text "name: $name  |  FHWM: 0.0  |  FILTER: X  |  time: $time            | image: $imname"
+    pack $w.label -side left
+    
+}
+
+proc do_refresh { } {
+
+    global picpath imname name
+
+    after 1000 {
+	
+	set time [clock format [clock seconds] -format "%H:%M:%S"]
+	set name "(empty)"
+	set fwhm 0.0
+	set filter "X"
+
+	if {! [catch {open $picpath/seeing.dat r} data] } {
+	    gets $data line
+	    close $data
+	    set list [split $line]
+	    if {[llength $list] == 4} {
+		set name [lindex $list 0]
+		catch { set fwhm [format "%5.2f" [lindex $list 1]] } status
+		set filter [lindex $list 3]
+            }
+	}
+	
+	.t.dataline.label configure -text "name: $name  |  FHWM: $fwhm  |  FILTER: $filter  |  time: $time           | image: $imname"
+	update
+	do_refresh 
+    }
+}
+
+proc do_refresh_pic { } {
+
+    global file lasttime imname name pid
+
+    after 2000 {
+	
+	# get file status & time
+	if {! [catch {file stat $file stats}] } {
+	    set newtime $stats(mtime)
+	    if {$newtime > $lasttime} {
+		set tmpfile "$file.$pid"
+		catch { exec ln -f $file $tmpfile }
+		catch { picture read $tmpfile -shrink } 
+		catch { exec rm -f $tmpfile }
+		set imname $name
+		set lasttime $newtime
+		update
+	    }
+	}
+	do_refresh_pic
+    }
+}
+
+proc bgerror {result} {
+	puts "edisp error: $result"
+}
+
+proc refresh_now { } {
+
+    global file lasttime imname name pid
+
+    if {[file exists $file]} {
+	set tmpfile "$file.$pid"
+	catch { exec ln -f $file $tmpfile }
+	catch { picture read $tmpfile -shrink }
+	catch { exec rm -f $tmpfile }
+
+	file stat $file stats
+	set imname $name
+	set lasttime $stats(mtime)
+	update
+	
+    }
+}
+
+mk_layout
+do_refresh
+do_refresh_pic
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/src/edisp.ppm
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/src/edisp.ppm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tcl/src/edisp.ppm	(revision 22322)
@@ -0,0 +1,169 @@
+#!/usr/bin/env wish
+
+# define three components to the window:
+
+# text line with data value
+# image region with image
+# quit button
+
+set picpath "/data/milo/cfh12k/plots"
+set imname "(empty)"
+set name "(empty)"
+
+proc mk_layout { } {
+
+    global file lasttime picpath
+
+    set lasttime 0
+    set file "$picpath/empty.gif"
+    wm withdraw .
+
+    set w [toplevel .t]
+    wm title .t "user display"
+    
+    # top level containers
+    frame $w.menubar -relief raised -borderwidth 2
+    frame $w.picture -relief raised -borderwidth 2
+    frame $w.dataline -relief raised -borderwidth 2
+
+    pack $w.menubar -fill x
+    pack $w.picture -fill x
+    pack $w.dataline -fill x
+
+    mk_menubar $w.menubar
+    mk_picture $w.picture
+    mk_dataline $w.dataline
+
+    update
+}
+
+proc do_quit {} {
+    
+    exit 
+
+}
+
+proc mk_picture {w} {
+
+    global file
+
+    image create photo picture -file $file
+
+    label $w.name -image picture
+    pack  $w.name -side top -fill both
+}
+
+proc mk_menubar {w} {
+
+    global file picpath
+
+    menubutton $w.file -text "File" -menu $w.file.menu
+    pack $w.file -side left
+
+    menu $w.file.menu -tearoff 0
+    $w.file.menu add command -label "Quit" -command { do_quit }
+    bind all <Alt-q> { do_quit }
+    
+    menubutton $w.plot -text "Plots" -menu $w.plot.menu
+    pack $w.plot -side left
+
+    menu $w.plot.menu -tearoff 0
+    $w.plot.menu add command -label "biases"          -command { set file "$picpath/biases.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "skyprobe recent" -command { set file "$picpath/skyprobe_recent.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "skyprobe night"  -command { set file "$picpath/skyprobe_night.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "sky vs time"     -command { set file "$picpath/sky.time.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "sky vs airmass"  -command { set file "$picpath/sky.airmass.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "FWHM vs time"    -command { set file "$picpath/fwhm.time.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "FWHM vs airmass" -command { set file "$picpath/fwhm.airmass.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "field (small)"   -command { set file "$picpath/field.small.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "field (large)"   -command { set file "$picpath/field.large.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "temperatures"    -command { set file "$picpath/temps.time.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "image (10x10)"   -command { set file "$picpath/current.b10.ppm"; refresh_now;  }
+    $w.plot.menu add command -label "focus (unit)"    -command { set file "$picpath/focus.unit.ppm"; refresh_now;  }
+
+}
+
+proc mk_dataline {w} {
+
+    global fwhm imname name
+    set time [clock format [clock seconds] -format "%H:%M:%S"]
+
+    label $w.label -text "name: $name  |  FHWM: 0.0  |  FILTER: X  |  time: $time            | image: $imname"
+    pack $w.label -side left
+    
+}
+
+proc do_refresh { } {
+
+    global picpath imname name
+
+    after 1000 {
+	
+	set time [clock format [clock seconds] -format "%H:%M:%S"]
+
+	set name "empty"
+	set fwhm 0.0
+	set filter "X"
+
+	if {! [catch "open $picpath/seeing.dat r" data] } {
+	    gets $data line
+	    close $data
+	    set list [split $line]
+	    set name [lindex $list 0]
+	    set fwhm [format "%5.2f" [lindex $list 1]]
+	    set filter [lindex $list 3]
+	}
+
+	.t.dataline.label configure -text "name: $name  |  FHWM: $fwhm  |  FILTER: $filter  |  time: $time           | image: $imname"
+	update
+	
+	do_refresh 
+    }
+}
+
+proc do_refresh_pic { } {
+
+    global file lasttime imname name
+
+    after 2000 {
+	
+	if {! [catch "file stat $file stats"] } {
+	    set newtime $stats(mtime)
+	    if {$newtime > $lasttime} {
+		set tmpfile "$file.tmp"
+		catch "exec ln -f $file $tmpfile"
+		catch "picture read $tmpfile -shrink"
+		catch "exec rm $tmpfile"
+		set imname $name
+		set lasttime $newtime
+		update
+	    }
+	}
+	do_refresh_pic
+    }
+}
+
+proc refresh_now { } {
+
+    global file lasttime imname name
+
+    picture read $file -shrink;
+
+    if {[file exists $file]} {
+	set tmpfile "$file.tmp"
+	catch "exec ln -f $file $tmpfile"
+	catch "picture read $tmpfile -shrink"
+	catch "exec rm -f $tmpfile"
+
+	file stat $file stats
+	set lasttime $stats(mtime)
+	set imname $name
+	
+	update
+	
+    }
+}
+
+mk_layout
+do_refresh
+do_refresh_pic
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/Makefile	(revision 22322)
@@ -0,0 +1,82 @@
+default: tools
+help:
+	@echo "make options: tools (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/tools
+SRC	=	$(HOME)/src
+LIB	= 	$(HOME)/lib
+MAN	=	$(HOME)/doc
+BIN	=	$(HOME)/bin
+INC	=	$(HOME)/include
+# include ../../Makefile.Common
+
+DVO_LIBS  = $(DESTLIB)/libdvo.a $(DESTLIB)/libFITS.a $(DESTLIB)/libohana.a
+DVO_INCS  = $(DESTINC)/dvo.h $(DESTINC)/gfitsio.h $(DESTINC)/ohana.h $(DESTINC)/autocode.h 
+
+PROGRAMS = gconfig fhead ftable fields list_astro glockfile \
+radec mktemp precess csystem fits_insert \
+medianfilter mefhead ckfits
+
+all tools: $(PROGRAMS)
+
+BASE_CFLAGS   =	$(CFLAGS)
+BASE_CPPFLAGS =	$(CPPFLAGS) -I$(INC) -I$(DESTINC) $(INCDIRS) -D$(ARCH)
+BASE_LDFLAGS  = $(LDFLAGS) -L$(LIB) -L$(DESTLIB) $(LIBDIRS) $(LIBFLAGS)
+
+FULL_CFLAGS   =	$(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+# these are all programs which just depend on a single c file: foo : foo.c
+# we use a special set of rules in this directory which expect this simplification
+
+%.$(ARCH).o : %.c $(DVO_INCS)
+	$(CC) $(FULL_CFLAGS) $(FULL_CPPFLAGS) -c $< -o $@
+	@echo "compiled $<"
+	@echo ""
+
+$(BIN)/%.$(ARCH) : $(SRC)/%.$(ARCH).o $(DVO_LIBS)
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	$(CC) $(FULL_CFLAGS) -o $@ $^ $(FULL_LDFLAGS)
+	@echo "compiled $*"
+	@echo ""
+
+$(DESTBIN)/%: $(BIN)/%.$(ARCH)  $(DVO_LIBS)
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	rm -f $(DESTBIN)/$*
+	cp $(BIN)/$*.$(ARCH) $(DESTBIN)/$*
+	@echo "installed $*"
+	@echo ""
+
+$(PROGRAMS) : % : $(BIN)/%.$(ARCH)
+
+%.clean:
+	rm -f $(SRC)/$*.$(ARCH).o
+	rm -f $(BIN)/$*.$(ARCH)
+
+%.install:
+	make $(DESTBIN)/$*
+
+install:
+	for i in $(PROGRAMS); do make $$i.install || exit; done
+
+
+clean:
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
+
+dist: clean
+	rm -rf $(BIN)
+	rm -rf $(LIB)
+
+# $@ : target filename
+# $* : matched word (%)
+# $< : first prereq
+# $^ : all prereqs
+
+.PRECIOUS: %.$(ARCH).o
+.PRECIOUS: $(BIN)/%.$(ARCH)
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/ckfits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/ckfits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/ckfits.c	(revision 22322)
@@ -0,0 +1,58 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+int main (int argc, char **argv) {
+
+  int i, Nbytes, nbytes, Ndata, Ntotal, nskip, status;
+  Header header;
+  FILE *f;
+  char *buffer;
+
+  if (argc != 2) {
+    fprintf (stdout, "USAGE: ckfits (filename)\n");
+    exit (2);
+  }
+
+  status = gfits_read_header (argv[1], &header);
+  if (!status) { 
+    fprintf (stdout, "%s: header\n", argv[1]);
+    exit (1);
+  }
+
+  Ntotal = gfits_data_size (&header);
+
+  Ndata = abs(header.bitpix / 8);
+  for (i = 0; i < header.Naxes; i++) Ndata *= header.Naxis[i];
+
+  nbytes = Ntotal - Ndata;
+  nskip = Ndata + header.size;
+
+  f = fopen (argv[1], "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stdout, "%s: open\n", argv[1]);
+    exit (1);
+  }
+
+  ALLOCATE (buffer, char, MAX (nbytes, 1));
+  fseek (f, nskip, SEEK_SET);
+  Nbytes = fread (buffer, 1, nbytes, f);
+  fclose (f);
+
+  if (Nbytes != nbytes) {
+    fprintf (stdout, "%s: short\n", argv[1]);
+    exit (1);
+  }
+
+  for (i = 0; i < nbytes; i++) {
+    if (buffer[i]) {
+      fprintf (stdout, "%s: padding\n", argv[1]);
+      exit (1);
+    }
+  }
+
+  fprintf (stdout, "%s: ok\n", argv[1]);
+  exit (0);
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/csystem.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/csystem.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/csystem.c	(revision 22322)
@@ -0,0 +1,98 @@
+# include <ohana.h>
+
+/* csystem: convert between celestial, galactic, ecliptic, and
+   hoziron systems */
+
+int main (int argc, char **argv) {
+
+  /* USAGE: csystem [C/G/E/H] [C/G/E/H] [epoch] */
+  double x, y, X, Y, Xo, xo, phi, T;
+  double sin_x, sin_y, cos_x, cos_y;
+  
+  if (((argc == 3) && ((argv[1][0] == 'E') || (argv[2][0] == 'E'))) || 
+      ((argc == 4) && ((argv[1][0] != 'E') && (argv[2][0] != 'E'))) ||
+      ((argc != 3) && (argc != 4))) {
+    fprintf (stderr, "USAGE: csystems [C/G/E/H] [C/G/E/H] [epoch]\n");
+    exit (2);
+  }
+
+  switch (argv[1][0]) {
+  case 'C':
+    switch (argv[2][0]) {
+    case 'C': 
+      phi = Xo = xo = 0.0;
+      break;
+    case 'G':
+      phi = -62.6*RAD_DEG;
+      Xo = 282.25;
+      xo = 33;
+      break;
+    case 'E':
+      T = (atof (argv[3]) - 1900) / 100.0;
+      phi = -1*(23.452294 - 0.013013*T - 0.000001639*T*T + 0.000000503*T*T*T);
+      phi *= RAD_DEG;
+      Xo = xo = 0.0;
+      break;
+    }
+    break;
+  case 'E':
+    switch (argv[2][0]) {
+    case 'C': 
+      T = (atof (argv[3]) - 1900) / 100.0;
+      phi = 23.452294 - 0.013013*T - 0.000001639*T*T + 0.000000503*T*T*T;
+      phi *= RAD_DEG;
+      Xo = xo = 0.0;
+      break;
+    case 'G':
+      fprintf (stderr, "error: not working!\n");
+      exit (1);
+      phi = -62.6*RAD_DEG;
+      Xo = 282.25;
+      xo = 33;
+      break;
+    case 'E':
+      phi = Xo = xo = 0.0;
+      break;
+    }
+    break;
+  case 'G':
+    switch (argv[2][0]) {
+    case 'C': 
+      phi = 62.6*RAD_DEG;
+      Xo = 33;
+      xo = 282.25;
+      break;
+    case 'G':
+      phi = Xo = xo = 0.0;
+      break;
+    case 'E':
+      fprintf (stderr, "error: not working!\n");
+      exit (1);
+      phi = -1*(23.452294 - 0.013013*T - 0.000001639*T*T + 0.000000503*T*T*T);
+      Xo = xo = 0.0;
+      break;
+    }
+  }
+ 
+  Xo *= RAD_DEG;
+  for (; fscanf (stdin, "%lf %lf", &X, &Y) == 2;) {
+    
+    X *= RAD_DEG;
+    Y *= RAD_DEG;
+
+    sin_y = cos(Y)*sin(X - Xo)*sin(phi) + sin(Y)*cos(phi);
+    cos_y = sqrt (1 - sin_y*sin_y);
+    sin_x = (cos(Y)*sin(X - Xo)*cos(phi) - sin(Y)*sin(phi)) /  cos_y;
+    cos_x = cos(Y)*cos(X - Xo) / cos_y;
+/*    fprintf (stderr, "%f %f   %f %f   %f %f\n", X, Y, sin_x, cos_x, sin_y, cos_y); */
+    
+    x = (DEG_RAD * atan2 (sin_x, cos_x) + xo + 360);
+    
+    while (x >= 360.0)
+      x -= 360;
+    y = DEG_RAD * atan2 (sin_y, cos_y);
+    
+    fprintf (stdout, "%10.6f %10.6f\n", x, y);
+  }
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/fhead.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/fhead.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/fhead.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+int main (int argc, char **argv) {
+
+  int N, Extend, Nextend, status;
+  int i, j, nbytes;
+  Header head;
+  char *p;
+
+  Extend = FALSE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    Extend = TRUE;
+    remove_argument (N, &argc, argv);
+    Nextend = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  for (i = 1; i < argc; i++) {
+    if (argc != 2) 
+      fprintf (stdout, "------> %s <------\n", argv[i]);
+    
+    if (Extend) {
+      status = gfits_read_Xheader (argv[i], &head, Nextend);
+    } else {
+      status = gfits_read_header (argv[i], &head);
+    }      
+
+    if (!status) continue;
+
+    for (j = 79; j < head.size; j+= 80) {
+      head.buffer[j] = 10;
+    }
+
+    p = gfits_header_field (&head, "END", 1);
+    nbytes = p - head.buffer;
+    fwrite (head.buffer, nbytes, 1, stdout);
+    gfits_free_header (&head);
+
+  }
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/fields.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/fields.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/fields.c	(revision 22322)
@@ -0,0 +1,139 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# include <sys/types.h>
+# include <regex.h>
+
+int print_fields (char *filename, char *extname, Header *header, int argc, char **argv);
+
+int usage () {
+
+  fprintf (stderr, "USAGE: fields [options] [KEYWORD] [KEYWORD]...\n");
+  fprintf (stderr, " reads filenames from stdin, writes header values to stdout\n");
+  fprintf (stderr, " options:\n");
+  fprintf (stderr, " -x (extnum)  : PHU is -1, followed by 0,1,2...\n");
+  fprintf (stderr, " -n (extname) : name may include a regex for EXTNAME values\n");
+
+  exit (2);
+}
+
+int main (int argc, char **argv) {
+
+  FILE *f;
+  Header header;
+  char filename[1000], *CCDKeyword, *Extname, extname[80];
+  int N, Nbytes, Extnum, Nextend, status, GotFile, GotField, GotExtension;
+
+  regex_t preg;
+
+  if (get_argument (argc, argv, "-h")) usage();
+  if (get_argument (argc, argv, "--help")) usage();
+
+  CCDKeyword = NULL;
+  if ((N = get_argument (argc, argv, "-keyword"))) {
+    remove_argument (N, &argc, argv);
+    CCDKeyword = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (CCDKeyword == NULL) {
+    CCDKeyword = strcreate ("EXTNAME");
+  }
+
+  Extnum = FALSE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    Extnum = TRUE;
+    remove_argument (N, &argc, argv);
+    Nextend = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Extname = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    Extname = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    regcomp (&preg, Extname, REG_EXTENDED);
+  }
+
+  GotFile  = TRUE; 
+  GotField = TRUE;
+
+  while (fscanf (stdin, "%s", filename) != EOF) {
+    if (!Extnum && !Extname) {
+      GotFile &= gfits_read_header (filename, &header);
+      GotField &= print_fields (filename, NULL, &header, argc, argv);
+      continue;
+    }
+    if (Extnum) {
+      GotFile  &= gfits_read_Xheader (filename, &header, Nextend);
+      GotField &= print_fields (filename, NULL, &header, argc, argv);
+      continue;
+    } 
+
+    if (Extname) {
+      /* keep reading headers, only parse fields for matching headers */
+      Nextend = 0;
+      GotExtension = FALSE;
+      f = fopen (filename, "r");
+      if (f == NULL) {
+	GotFile = FALSE;
+	continue;
+      }
+      while (gfits_fread_header (f, &header)) {
+	/* extract the EXTNAME (or other CCDKeyword) for this component (set to PHU for 0th component) */
+	status = gfits_scan (&header, CCDKeyword, "%s", 1, extname);
+	if (!status) {
+	  if (Nextend == 0) {
+	    strcpy (extname, "PHU");
+	  } else {
+	    strcpy (extname, "UNKNOWN");
+	  }
+	}
+	if (!regexec (&preg, extname, 0, NULL, 0)) {
+	  GotField &= print_fields (filename, extname, &header, argc, argv);
+	  GotExtension = TRUE;
+	}   
+    
+	Nbytes = gfits_data_size (&header);
+	fseek (f, Nbytes, SEEK_CUR);
+	Nextend ++;
+
+	GotFile = gfits_read_Xheader (filename, &header, Nextend);
+	continue;
+      } 
+      fclose (f);
+      if (Nextend == 0) {
+	GotFile = FALSE;
+      }
+    }
+  }
+
+  if (Extname) regfree (&preg);
+
+  if (!GotFile) exit (1);
+  if (!GotField) exit (2);
+  if (!GotExtension) exit (3);
+  exit (0);
+}
+
+int print_fields (char *filename, char *extname, Header *header, int argc, char **argv) {
+
+  int i, GotField;
+  char buffer[1000];
+
+  GotField = TRUE;
+
+  if (extname) {
+    fprintf (stdout, "%s[%s]  ", filename, extname);
+  } else {
+    fprintf (stdout, "%s  ", filename);
+  }
+  for (i = 1; i < argc; i++) {
+    bzero (buffer, 1000);
+    GotField &= gfits_scan (header, argv[i], "%s", 1, buffer);
+    stripwhite (buffer);
+    fprintf (stdout, "%s  ", buffer);
+  }
+  fprintf (stdout, "\n");
+  gfits_free_header (header);
+  return (GotField);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/findexec.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/findexec.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/findexec.c	(revision 22322)
@@ -0,0 +1,121 @@
+# include <stdio.h>
+# include <strings.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <stdlib.h>
+
+# define TRUE 1
+# define FALSE 0
+
+# ifndef ALLOCATE
+# define ALLOCATE(X,T,S)  \
+  X=(T *)malloc((unsigned) ((S)*sizeof(T)));\
+  if(X==NULL) \
+    { \
+      fprintf(stderr,"failed to malloc X\n");\
+        exit(0);\
+    } 
+# define REALLOCATE(X,T,S) \
+  X=(T *)realloc(X,(unsigned) ((S)*sizeof(T))); \
+  if(X==NULL) \
+    { \
+       fprintf(stderr,"failed to realloc X\n"); \
+       exit(0); \
+    }
+# endif /* ALLOCATE */
+
+int _check_permissions (char *filename) {
+  
+  FILE *f;
+  struct stat filestat;
+  uid_t fuid, uid;
+  gid_t fgid, gid;
+  int status;
+
+  uid = getuid();
+  gid = getgid();
+
+  /* check permission to exec file */
+  status = stat (filename, &filestat);
+  if (status == 0) { /* file exists, are permissions OK? */
+    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR) && (filestat.st_mode & S_IXUSR)) ||
+	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP) && (filestat.st_mode & S_IXGRP)) || 
+	(                            (filestat.st_mode & S_IROTH) && (filestat.st_mode & S_IXOTH))) {
+      return (TRUE);
+    } else {
+      fprintf (stderr, "can't exec %s\n", filename);
+      return (FALSE);
+    }
+  }
+  fprintf (stderr, "no such file %s\n", filename);
+  return (FALSE);
+}
+
+char *pathname (char *name) {
+ 
+  char *c, *path;
+
+  ALLOCATE (path, char, strlen(name) + 1);
+  strcpy (path, name);
+  c = strrchr (path, '/');
+  if (c == (char *) NULL) {
+    strcpy (path, ".");
+  } else {
+    *c = 0;
+  }
+  
+  return (path);
+  
+}
+
+char *findexec (int argc, char **argv) {
+
+  int i, N, done, status;
+  char *c, *e, *dir, path[1024], name[1024];
+  struct stat state;
+
+  if (argv[0][0] == '/') {
+    status = _check_permissions (argv[0]);
+    if (status) {
+      realpath (argv[0], path);
+      dir = pathname (path);
+      return (dir);
+    }
+  }
+
+  N = 0;
+  for (i = argc+1; argv[i] != (char *) NULL; i++) {
+    if (!strncmp (argv[i], "PATH", 4)) {
+      N = i;
+      break;
+    }
+  }
+
+  if (N) {
+    c = &argv[N][5];
+    e = strchr (c, ':');
+    done = FALSE;
+    i = 0;
+    while (!done && (i < 50)) {
+      if (e == (char *) NULL) {
+	done = TRUE;
+	bzero (path, 256);
+	strncpy (path, c, strlen(c));
+      } else {
+	bzero (path, 256);
+	strncpy (path, c, e-c);
+	c = e+1;
+	e = strchr (c, ':');
+      }
+      sprintf (name, "%s/%s", path, argv[0]);
+      status = _check_permissions (name);
+
+      if (status) {
+	realpath (name, path);
+	dir = pathname (path);
+	return (dir);
+      }
+    }
+    i++;
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/fits_insert.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/fits_insert.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/fits_insert.c	(revision 22322)
@@ -0,0 +1,187 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+static char reserved[] =  "COMMENT  Reserved space.  This line can be used to add a new FITS card.         ";
+static char blankline[] = "                                                                                ";
+
+int main (int argc, char **argv) {
+
+  int i, N, status, EXTNUM, Nbytes, skip, Delete, DeleteStart, DeleteStop;
+  int Nreserved, start_size;
+  char *p, keyword[16], line[256];
+  FILE *f;
+  Header header;
+  int COMMENT, Cnumber;
+  char *Cline;
+
+  /* check for command line options */
+  COMMENT = FALSE;
+  if ((N = get_argument (argc, argv, "-comment"))) {
+    COMMENT = TRUE;
+    remove_argument (N, &argc, argv);
+    Cnumber = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Cline = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* check for delete options */
+  Delete = FALSE;
+  if ((N = get_argument (argc, argv, "-delete"))) {
+    Delete = TRUE;
+    remove_argument (N, &argc, argv);
+    DeleteStart = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    DeleteStop = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* check for command line options */
+  EXTNUM = -1; /* -1 is primary header */
+  if ((N = get_argument (argc, argv, "-X"))) {
+    remove_argument (N, &argc, argv);
+    EXTNUM = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: gfits_insert (image.fits) (header.hdx) [-X N] [-comment N line] [-delete N]\n");
+    exit (2);
+  }
+  
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    fprintf (stderr, "can't open fits file %s\n", argv[1]);
+    exit (1);
+  }
+
+  /* load header from image file */
+  Nbytes = gfits_fread_Xheader (f, &header, EXTNUM);
+  if (!Nbytes) {
+      fprintf (stderr, "can't read extension %d\n", EXTNUM);
+      exit (1);
+  }
+  start_size = header.size;
+  skip = Nbytes - header.size;
+
+  /* open header data file */
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open header data file %s\n", argv[2]);
+    exit (1);
+  }
+
+  /* wipe out COMMENT N and replace with given line */ 
+  if (COMMENT) {
+
+    /* create a pristine FITS line */
+    snprintf (line, 81, "COMMENT  %-71s", Cline);
+
+    p = gfits_header_field (&header, "COMMENT", Cnumber);
+    if (p != (char *) NULL) {
+      strncpy (p, line, 80);
+    }
+  }
+
+  /* wipe out lines from DeleteStart to < DeleteStop */ 
+  if (Delete) {
+    for (i = DeleteStart; i < DeleteStop; i++) {
+      p = gfits_header_lineno (&header, i);
+      if (p != (char *) NULL) {
+	memset (p, ' ', 80);
+      }
+    }
+  }
+
+  /* run through the lines in the header data file */
+  while (scan_line (f, line) != EOF) {
+    
+    /* fill in end of line with blanks, force end at 80 */
+    for (N = strlen (line); N < 80; N++) {
+      line[N] = ' ';
+    }
+    line[80] = 0;
+    bzero (keyword, 10);
+    strncpy (keyword, line, 8);
+    
+    /* replace existing keywords, unless this is a COMMENT or HISTORY field */
+    if (strncmp (keyword, "COMMENT ", 8) && strncmp (keyword, "HISTORY ", 8)) {
+      p = gfits_header_field (&header, keyword, 1);
+      if (p != (char *) NULL) {
+	strncpy (p, line, 80);
+	continue;
+      }
+    }
+
+    /* check that the line does not already exist */
+    p = (char *) NULL;
+    for (i = 0; (i < header.size) && (p == (char *) NULL) ; i+= FT_LINE_LENGTH) {
+      if (!strncmp (&header.buffer[i], line, 80)) {
+	p = &header.buffer[i];
+      }
+    }
+    if (p != (char *) NULL) continue;
+
+    /* find first line with the reserved line */
+    p = (char *) NULL;
+    Nreserved = strlen (reserved);
+    for (i = 0; (i < header.size) && (p == (char *) NULL) ; i+= FT_LINE_LENGTH) {
+      if (!strncmp (&header.buffer[i], "END     ", 8)) break;
+      if (!strncmp (&header.buffer[i], reserved, Nreserved)) {
+	p = &header.buffer[i];
+      }
+      if (!strncmp (&header.buffer[i], blankline, Nreserved)) {
+	p = &header.buffer[i];
+      }
+    }
+    if (p == (char *) NULL) {
+      fprintf (stdout, "no more reserved spaces, trying for extra space in block\n");
+      p = gfits_header_field (&header, "END", 1);
+      if (p == (char *) NULL) {
+	fprintf (stderr, "header is missing END\n");
+	exit (1);
+      }
+      if (p - header.buffer + 80 == header.size) {
+	fprintf (stderr, "no free space in block, can't insert keyword\n");
+	exit (1);
+      }
+      strncpy (p+80, "END", 3);
+      for (i = 3; i < 80; i++) { p[80+i] = ' '; }
+    }
+    /* insert the new line here */
+    strncpy (p, line, 80);
+  }
+  if (fclose (f)) {
+    fprintf (stderr, "error reading new keywords\n");
+    exit (1);
+  }
+
+  /* now write the new header on top of the old one. 
+     check first that the header size has not changed.
+  */
+
+  if (header.size != start_size) {
+    fprintf (stderr, "header changed size: should not happen!\n");
+    exit (1);
+  }
+
+  f = fopen (argv[1], "r+");
+  if (f == NULL) {
+    fprintf (stderr, "can't open file for update %s\n", argv[1]);
+    exit (1);
+  }
+
+  fseek (f, skip, SEEK_SET);
+  status = fwrite (header.buffer, 1, header.size, f);
+  if (status != header.size) {
+    fprintf (stderr, "failed to write data to image header\n");
+    exit (1);
+  }
+  if (fclose (f)) {
+    fprintf (stderr, "error writing data to disk\n");
+    exit (1);
+  }
+
+  exit (0);
+}
+    
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/ftable.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/ftable.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/ftable.c	(revision 22322)
@@ -0,0 +1,406 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+char *print_table_row (char *row, Header *header);
+FILE *load_extension (char *file, int Nextend, char *Extname, Header *header);
+void print_column (FTable *table, int Column, char *Colname);
+void usage();
+void list_extnames (char *file);
+void print_layout (Header *header);
+int   ByteSwap (char *ptr, int size, int nitems, char *type);
+int Binary;
+
+int main (int argc, char **argv) {
+
+  int i, N, Nx, Ny, Nbytes, Nread;
+  int Nextend, Column, Row, ListExtname, Layout;
+  char *Extname, *Colname, *line, ttype[80];
+  FTable table;
+  Header header;
+  FILE *f;
+
+  if (get_argument (argc, argv, "-h")) usage ();
+  if (get_argument (argc, argv, "--help")) usage ();
+
+  Nextend = 0;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    Nextend = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Extname = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    if (Nextend) usage ();
+    remove_argument (N, &argc, argv);
+    Extname = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Row = 0;
+  if ((N = get_argument (argc, argv, "-row"))) {
+    remove_argument (N, &argc, argv);
+    Row = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Column = 0;
+  if ((N = get_argument (argc, argv, "-ncolumn"))) {
+    remove_argument (N, &argc, argv);
+    Column = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Colname = 0;
+  if ((N = get_argument (argc, argv, "-column"))) {
+    if (Column) usage ();
+    remove_argument (N, &argc, argv);
+    Colname = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  ListExtname = FALSE;
+  if ((N = get_argument (argc, argv, "-list"))) {
+    remove_argument (N, &argc, argv);
+    ListExtname = TRUE;
+  }
+
+  Layout = FALSE;
+  if ((N = get_argument (argc, argv, "-layout"))) {
+    remove_argument (N, &argc, argv);
+    Layout = TRUE;
+  }
+
+  if (argc != 2) usage ();
+
+  if (ListExtname) list_extnames (argv[1]);
+
+  /* load header */
+  table.header = &header;
+  f = load_extension (argv[1], Nextend, Extname, table.header);
+
+  if (Layout) print_layout (table.header);
+
+  Binary = FALSE;
+  gfits_scan (table.header, "XTENSION", "%s", 1, ttype);
+  if (!strcmp (ttype, "BINTABLE")) Binary = TRUE;
+
+  /* load table data array */
+  Nbytes = gfits_data_size (table.header);
+  ALLOCATE (table.buffer, char, Nbytes);
+  Nread = fread (table.buffer, sizeof (char), Nbytes, f);
+  if (Nread != Nbytes) {
+    fprintf (stderr, "failed to read all table data\n");
+    exit (1);
+  }
+  table.size = Nbytes;
+
+  gfits_scan (table.header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (table.header, "NAXIS2",  "%d", 1, &Ny);
+
+  /* print a column */
+  if (Column || (Colname != (char *) NULL)) print_column (&table, Column, Colname);
+
+  /* print a row */
+  if (Row) {
+    line = print_table_row (&table.buffer[Nx*Row], table.header);
+    fprintf (stdout, "%s\n", line);
+    free (line);
+    exit (0);
+  }
+
+  /* print complete table */
+  for (i = 0; i < Ny; i++) {
+    line = print_table_row (&table.buffer[Nx*i], table.header);
+    fprintf (stdout, "%s\n", line);
+    free (line);
+  }    
+  exit (0);
+}
+
+/* print an ASCII table to a row with single spaces separating value */
+char *print_table_row (char *row, Header *header) {
+  
+  int i, j, Nx, Nfields, Nbytes, Nvals, Oout, Oin;
+  char field[16], type[16], format[16], *line;
+
+  gfits_scan (header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+
+  /* assume we have one space per byte column */
+  ALLOCATE (line, char, 2*Nx + 1);
+
+  Oin = Oout = 0;
+  for (i = 1; i <= Nfields; i++) {
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (header, field, "%s", 1, format); /* get field format */
+    gfits_table_format (format, type, &Nvals, &Nbytes);    /* convert to c-style */
+    memcpy (&line[Oout], &row[Oin], Nvals*Nbytes);
+    for (j = 0; j < Nvals*Nbytes; j++) if (line[Oout+j] == 0) line[Oout+j] = ' ';
+    line[Oout+Nvals*Nbytes] = ' ';
+    Oout += Nvals*Nbytes + 1;
+    Oin += Nvals*Nbytes;
+  }
+
+  return (line);
+}
+
+void usage () {
+    fprintf (stderr, "USAGE: [-h] [-N N] (table.fits)\n");
+    fprintf (stderr, " -x N:       operate on extension N (0 is default)\n");
+    fprintf (stderr, " -n EXTNAME: operate on named extension (incompatible with -x)\n");
+    fprintf (stderr, " -row N:     print row number N\n");
+    fprintf (stderr, " -column n:  print column named n\n");
+    fprintf (stderr, " -ncolumn N: print column number N\n");
+    fprintf (stderr, " -list:      print extension names\n");
+    exit (2);
+}
+
+void list_extnames (char *file) {
+
+  FILE *f;
+  int i, Naxis, Nelem, Ncomp, extend, Nbytes, status;
+  char extname[82], exttype[82], axisname[32];
+  Header header;
+
+  f = fopen (file, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open file %s\n", file);
+    exit (1);
+  }
+
+  /* 
+     if (!gfits_fread_header (f, &header)) {
+     fprintf (stderr, "can't read header from %s\n", file);
+     exit (1);
+     }
+     Nbytes = gfits_data_size (&header);
+     fseek (f, Nbytes, SEEK_CUR);
+  */
+
+  fprintf (stdout, "%-30s %-15s NAXIS NAXIS(i)...\n", "extname", "datatype"); 
+
+  Ncomp = 0;
+  while (gfits_fread_header (f, &header)) {
+    /* extract the EXTNAME for this component (set to PHU for 0th component) */
+    status = gfits_scan (&header, "EXTNAME", "%s", 1, extname);
+    if (!status) {
+      if (Ncomp == 0) {
+	strcpy (extname, "PHU");
+      } else {
+	strcpy (extname, "UNKNOWN");
+      }
+    }
+    fprintf (stdout, "%-30s ", extname);
+
+    /* extract the datatype for this component (IMAGE for 0th component) */
+    if (Ncomp == 0) {
+      strcpy (exttype, "IMAGE");
+    } else {
+      status = gfits_scan (&header, "XTENSION", "%s", 1, exttype);
+      if (!status) {
+	strcpy (exttype, "UNKNOWN");
+      }
+    }
+    fprintf (stdout, "%-15s ", exttype);
+
+    /* extract the rank of the component */
+    status = gfits_scan (&header, "NAXIS",  "%d", 1, &Naxis);
+    if (!status) {
+      fprintf (stderr, "component %d is missing Naxis!\n", Ncomp);
+      Ncomp ++;
+      continue;
+    }
+    fprintf (stdout, " %4d", Naxis);
+
+    /* extract the individual axes */
+    for (i = 0; i < Naxis; i++) {
+      sprintf (axisname, "NAXIS%d", i+1);
+      status = gfits_scan (&header, axisname,  "%d", 1, &Nelem);
+      if (!status) {
+	fprintf (stderr, "missing %s\n", axisname);
+      }
+      fprintf (stdout, " %7d", Nelem);
+    }
+    fprintf (stdout, "\n");
+
+    /* are extensions identified? (we will scan for them anyway) */
+    if (Ncomp == 0) {
+      extend = FALSE;
+      gfits_scan (&header, "EXTEND", "%t", 1, &extend);
+      if (!extend) {
+	fprintf (stderr, "no extensions listed in file\n");
+      }
+    }
+
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+
+    Ncomp ++;
+  }
+  fclose (f);
+  exit (0);
+}
+
+FILE *load_extension (char *file, int Nextend, char *Extname, Header *header) {
+
+  int i, extend, Nbytes;
+  char extname[82];
+  FILE *f;
+
+  /* open file */
+  f = fopen (file, "r");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open file %s\n", file);
+    exit (1);
+  }
+
+  /* read PHU */
+  if (!gfits_fread_header (f, header)) {
+    fprintf (stderr, "can't read header from %s\n", file);
+    exit (1);
+  }
+
+  /* check for existence of extensions */
+  extend = FALSE;
+  gfits_scan (header, "EXTEND", "%t", 1, &extend);
+  if (!extend) {
+    fprintf (stderr, "no extensions listed in file\n");
+  }
+
+  /* skip first data array */
+  Nbytes = gfits_data_size (header);
+  fseek (f, Nbytes, SEEK_CUR);
+
+  /* search for extension of interest */
+  for (i = 0; gfits_fread_Theader (f, header); i++) {
+    if ((Extname == NULL) && (Nextend == i)) return (f);
+
+    gfits_scan (header, "EXTNAME", "%s", 1, extname);
+    if ((Extname != NULL) && (!strcmp (Extname, extname))) return (f);
+
+    Nbytes = gfits_data_size (header);
+    fseek (f, Nbytes, SEEK_CUR);
+    gfits_free_header (header);
+  }
+  fclose (f);
+
+  fprintf (stderr, "failed to load extension of interest\n");
+  exit (1);
+}
+
+void print_column (FTable *table, int Column, char *Colname) {
+  
+  int i, j, Nfields, Nstart, Nv, Nb, Nx, Ny;
+  Header *header;
+  char format[16], field[16], type[16], *line, *data;
+
+  header =  table[0].header;
+  data   =  table[0].buffer;
+
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+  gfits_scan (header, "NAXIS1",  "%d", 1, &Nx);
+  gfits_scan (header, "NAXIS2",  "%d", 1, &Ny);
+
+  if (Colname != (char *) NULL) {
+    /* find matching column entry */
+    for (i = 1; i <= Nfields; i++) {
+      sprintf (field, "TTYPE%d", i);
+      gfits_scan (header, field, "%s", 1, type);
+      if (!strcmp (type, Colname)) {
+	Column = i;
+	break;
+      }
+    }
+    if (!Column) {
+      fprintf (stderr, "column %s not found\n", Colname);
+      exit (1);
+    }
+  } else {
+    /* check if Column is in range */
+    if (Column > Nfields) {
+      fprintf (stderr, "-ncolumn %d too large, only %d columns\n", Column, Nfields);
+      exit (1);
+    }
+  }
+
+  /* scan columns to find insert point */
+  Nstart = 0;
+  for (i = 1; i < Column; i++) {
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (header, field, "%s", 1, format);
+    if (Binary) 
+      gfits_table_format (format, type, &Nv, &Nb);
+    else 
+      gfits_bintable_format (format, type, &Nv, &Nb);
+    Nstart += Nv*Nb;
+  }
+
+  sprintf (field, "TFORM%d", Column);
+  gfits_scan (header, field, "%s", 1, format);
+  if (Binary) 
+    gfits_bintable_format (format, type, &Nv, &Nb);    /* convert to c-style */
+  else 
+    gfits_table_format (format, type, &Nv, &Nb);    /* convert to c-style */
+
+  ALLOCATE (line, char, Nv*Nb + 1);
+
+  if (Binary) {
+    if (!gfits_get_bintable_column_type (header, Colname, type, &Nv)) return;
+    // if (!strcmp (type, "char")) return;
+    if (!gfits_get_bintable_column (header, table, Colname, (void **)&data)) return;
+    // this results in an array of Ny*Nv entries
+
+    for (i = 0; i < Ny; i++) {
+      if (!strcmp (type, "char")) {
+	memcpy (line, &data[i*Nv*Nb], Nv*Nb);
+	fprintf (stdout, "%s\n", line);
+      } else {
+	for (j = 0; j < Nv; j++) {
+	  if (!strcmp (type, "int")) {
+	    memcpy (line, &data[i*Nv*Nb + Nb*j], Nb);
+	    fprintf (stdout, "%d ", *(int *)line);
+	  }
+	  if (!strcmp (type, "float")) {
+	    memcpy (line, &data[i*Nv*Nb + Nb*j], Nb);
+	    fprintf (stdout, "%e ", *(float *)line);
+	  }
+	}
+	fprintf (stdout, "\n");
+      }
+    }
+  } else {
+    for (i = 0; i < Ny; i++) {
+      memcpy (line, &data[i*Nx + Nstart], Nv*Nb);
+      sprintf (format, "%%%ds\n", Nv*Nb);
+      fprintf (stdout, format, line);
+    }
+  }
+  exit (0);
+}
+
+void print_layout (Header *header) {
+
+  int i, Nfields;
+  char field[16], type[80], comment[80], format[80], unit[80], null[80], nval[80];
+
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+
+  for (i = 1; i <= Nfields; i++) {
+    sprintf (field, "TTYPE%d", i);
+    gfits_scan (header, field, "%s", 1, type);
+    gfits_scan (header, field, "%C", 1, comment);
+    sprintf (field, "TFORM%d", i);
+    gfits_scan (header, field, "%s", 1, format);
+    sprintf (field, "TUNIT%d", i);
+    gfits_scan (header, field, "%s", 1, unit);
+    sprintf (field, "TNULL%d", i);
+    if (!gfits_scan (header, field, "%s", 1, null)) strcpy (null, "none");
+    sprintf (field, "TNVAL%d", i);
+    if (!gfits_scan (header, field, "%s", 1, nval)) strcpy (nval, "none");
+
+    fprintf (stdout, "%-18s %-32s %-18s %-6s %-8s %-8s\n", 
+	  type, comment, unit, format, null, nval); 
+  }
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/gconfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/gconfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/gconfig.c	(revision 22322)
@@ -0,0 +1,66 @@
+# include "ohana.h"
+void usage ();
+
+int main (int argc, char **argv) {
+
+  char *config, *file;
+  char word[256];
+  int i, N, VERBOSE, status;
+
+  if ((N = get_argument (argc, argv, "-h"))) { usage (); }
+  if ((N = get_argument (argc, argv, "-help"))) { usage (); }
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (&argc, argv, "ptolemy");
+
+  /* dump raw config file (no interpolation of input files */
+  if ((N = get_argument (argc, argv, "-raw"))) {
+    config = LoadRawConfigFile (file, TRUE);
+    fwrite (config, 1, strlen(config), stdout);
+    exit (0);
+  }
+
+  /* load complete config info from file(s) */
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-q"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = FALSE;
+  }
+
+  /* if no keywords, dump entire config file */
+  if (argc == 1) {
+    fwrite (config, 1, strlen(config), stdout);
+    exit (0);
+  }
+
+  status = 0;
+  for (i = 1; i < argc; i++) {
+    if (ScanConfig (config, argv[i], "%s", 0,  word) == (char *) NULL) {
+      strcpy (word, "not found");
+      status = 1;
+    }
+    if (VERBOSE) {
+      fprintf (stdout, "%s %s\n", argv[i], word);
+    } else {
+      fprintf (stdout, "%s\n", word);
+    }
+  }
+  exit (status);
+}
+
+void usage () {
+  fprintf (stderr, "gconfig: print elixir config information\n");
+  fprintf (stderr, " USAGE: gconfig [keywords...] [-c file] [-C config] [-D keyword value]\n");
+  exit (2);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/glockfile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/glockfile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/glockfile.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include <ohana.h>
+
+int main (int argc, char **argv) {
+
+  char *filename;
+  double timeout;
+  int holdtime, state, type;
+  FILE *f;
+
+  if (argc != 4) {
+    fprintf (stderr, "USAGE: glock (filename) (type) (holdtime)\n");
+    exit (1);
+  }
+
+  filename = argv[1];
+  timeout = 30.0;
+
+  holdtime = atoi (argv[3]);
+
+  type = 0;
+  if (!strcasecmp (argv[2], "hard")) {
+    type = LCK_HARD;
+  }
+  if (!strcasecmp (argv[2], "soft")) {
+    type = LCK_SOFT;
+  }
+  if (!strcasecmp (argv[2], "xcld")) {
+    type = LCK_XCLD;
+  }
+
+  f = fsetlockfile (filename, timeout, type, &state);
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't lock file %s\n", filename);
+    exit (1);
+  }
+
+  fprintf (stderr, "file is locked\n");
+  sleep (holdtime);
+  fclearlockfile (filename, f, type, &state);
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/list_astro.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/list_astro.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/list_astro.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include <ohana.h>
+
+int main (int argc, char **argv) {
+
+  float *ra1, *ra2, *dec1, *dec2;
+  float x, y, x2, y2, xy, R, D, Rx, Ry, Dx, Dy, N;
+  float x3, x2y, x4, Rx2;
+  float DEC, RA, DX, DY, RX, RY, d2RA, d2DEC, dRA, dDEC;
+  float Sx2, Sy2, Sxy, Srx, Sry, Sdx, Sdy;
+  int i, Npairs, NPAIR;
+
+  NPAIR = 100;
+  ALLOCATE (ra1, float, NPAIR);
+  ALLOCATE (ra2, float, NPAIR);
+  ALLOCATE (dec1, float, NPAIR);
+  ALLOCATE (dec2, float, NPAIR);
+
+/*
+  ALLOCATE (a, double *, 3);
+  ALLOCATE (a[0], double, 3);
+  ALLOCATE (a[1], double, 3);
+  ALLOCATE (a[2], double, 3);
+  ALLOCATE (a[3], double, 4);
+  ALLOCATE (b, double *, 3);
+  ALLOCATE (b[0], double, 1);
+  ALLOCATE (b[1], double, 1);
+  ALLOCATE (b[2], double, 1);
+  ALLOCATE (b[3], double, 1);
+*/
+
+  if (argc > 1) {
+    fprintf (stderr, "USAGE: list_astro x\nTakes list from the stdin in the");
+    fprintf (stderr, " format:\n  X Y RA DEC\nReturns:\n  RA, RX, RY, dRA\n  DEC, DX, DY, dDEC\n");
+    exit(0);
+  }
+    
+  for (i = 0; fscanf (stdin, "%f %f %f %f", &ra1[i], &dec1[i], &ra2[i], &dec2[i]) != EOF; i++) {
+    if (i == NPAIR - 1) {
+      NPAIR += 50;
+      REALLOCATE (ra1, float, NPAIR);
+      REALLOCATE (ra2, float, NPAIR);
+      REALLOCATE (dec1, float, NPAIR);
+      REALLOCATE (dec2, float, NPAIR);
+    }
+  }
+
+  
+  
+  N = x = y = x2 = y2 = xy = R = D = Rx = Ry = Dx = Dy = 0;
+  Npairs = i;
+  for (i = 0; i < Npairs; i++) {
+    x  += ra1[i];
+    y  += dec1[i];
+    x2 += ra1[i]*ra1[i];
+    y2 += dec1[i]*dec1[i];
+    xy += ra1[i]*dec1[i];
+    x3 += ra1[i]*ra1[i]*ra1[i];
+    x2y += ra1[i]*ra1[i]*dec1[i];
+    x4 += ra1[i]*ra1[i]*ra1[i]*ra1[i];
+    R  += ra2[i];
+    D  += dec2[i];
+    Rx += ra2[i]*ra1[i];
+    Ry += ra2[i]*dec1[i];
+    Rx2 += ra2[i]*ra1[i]*ra1[i];
+    Dx += dec2[i]*ra1[i];
+    Dy += dec2[i]*dec1[i];
+    N  += 1.0;
+  }
+
+/*
+  a[0][0] = N;
+  a[0][1] = a[1][0] = x;
+  a[0][2] = a[2][0] = y;
+  a[0][3] = a[3][0] = x2;
+  a[1][1] = x2;
+  a[1][2] = a[2][1] = xy;
+  a[1][3] = a[3][1] = x3;
+  a[2][2] = y2;
+  a[2][3] = a[3][2] = x2y;
+  a[3][3] = x4;
+  b[0][0] = R;
+  b[1][0] = Rx;
+  b[2][0] = Ry;
+  b[1][0] = Rx2;
+*/
+
+  Sx2 = x2 - x*x/N;
+  Sy2 = y2 - y*y/N;
+  Sxy = xy - x*y/N;
+  Srx = Rx - R*x/N;
+  Sry = Ry - R*y/N;
+  Sdx = Dx - D*x/N;
+  Sdy = Dy - D*y/N;
+  
+  RX = (Srx*Sy2 - Sry*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  RY = (Sry*Sx2 - Srx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  RA = R/N - RX*x/N - RY*y/N;
+
+  DX  = (Sdx*Sy2 - Sdy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  DY  = (Sdy*Sx2 - Sdx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  DEC = D/N - DX*x/N - DY*y/N;
+
+  d2RA = d2DEC = N = 0;
+  N = x = y = x2 = y2 = xy = R = D = Rx = Ry = Dx = Dy = 0;
+  for (i = 0; i < Npairs; i++) {
+    d2RA += pow((ra2[i] - RA - RX*ra1[i] - RY*dec1[i]), 2.0);
+    d2DEC += pow((dec2[i] - DEC - DX*ra1[i] - DY*dec1[i]), 2.0);
+    N += 1.0;
+  }
+
+  dRA  = 3600.0*sqrt(d2RA / (N - 3.0));
+  dDEC = 3600.0*sqrt(d2DEC / (N - 3.0));
+
+  fprintf (stdout, " %12.9f %12.9e %12.9e %12.9f\n", RA, RX, RY, dRA);
+  fprintf (stdout, " %12.9f %12.9e %12.9e %12.9f\n", DEC, DX, DY, dDEC);
+  exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/medianfilter.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/medianfilter.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/medianfilter.c	(revision 22322)
@@ -0,0 +1,133 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+# define D_NFILES 100
+
+int main (int argc, char **argv) {
+
+  int i, j, k, n, Npixels, Npixin, Npixrd, Npixlast, N;
+  int Npass, NFILES, Nfiles, Nvalid;
+
+  char **filename;
+  Header head, *tmphead;
+  Matrix out,  *tmpmatr;
+  float *list, *v, *O;
+  float fmin, fmax, Nval, sum, MinValid;
+
+  if ((N = get_argument (argc, argv, "-unsign"))) {
+    remove_argument (N, &argc, argv);
+    gfits_set_unsign_mode (TRUE);
+  }
+
+  MinValid = 1.0;
+  if ((N = get_argument (argc, argv, "-bad"))) {
+    remove_argument (N, &argc, argv);
+    MinValid = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 4) {
+    fprintf (stderr, "USAGE: minmaxfilter outfile fmin fmax\n");
+    exit (0);
+  }
+  fmin = atof (argv[2]); 
+  fmax = atof (argv[3]); 
+
+  /* read in the filenames */
+  NFILES = D_NFILES;
+  ALLOCATE (filename, char *, NFILES);
+  ALLOCATE (filename[0], char, 1024);
+  for (i = 0; fscanf (stdin, "%s", filename[i]) != EOF; i++) {
+    if (i == NFILES - 1) {
+      NFILES += D_NFILES;
+      REALLOCATE (filename, char *, NFILES);
+    }
+    ALLOCATE (filename[i+1], char, 1024);
+  }
+  Nfiles = i;
+  REALLOCATE (filename, char *, Nfiles);
+
+  /* load a header, setup output header, matrix */
+  gfits_read_header (filename[0], &head);
+  gfits_create_matrix (&head, &out);
+  gfits_convert_format (&head, &out, -32, 1.0, 0.0, FALSE);
+
+  /* find size of temporary images */
+  Npixels = head.Naxis[0]*head.Naxis[1];
+  Npixin = Npixels / Nfiles;
+  Npixlast = Npixels - Npixin*Nfiles;
+  if (Npixlast == 0) {
+    Npass = Nfiles;
+    Npixlast = Npixin;
+    fprintf (stderr, "operating on %d pixels per pass\n", Npixin);
+  } else {
+    Npass = Nfiles + 1;
+    fprintf (stderr, "operating on %d pixels per pass, %d in last\n", Npixin, Npixlast);
+  }
+  
+  ALLOCATE (tmphead, Header, Nfiles);
+  ALLOCATE (tmpmatr, Matrix, Nfiles);
+  ALLOCATE (list, float, Nfiles);
+
+  /*
+  Nval = 0;
+  for (k = fmin*Nfiles; k < fmax*Nfiles; k++) {
+    Nval += 1.0;
+  }
+  */
+
+  O = (float *) out.buffer;
+  for (n = 0; n < Npass; n++) {
+    fprintf (stderr, "pass %d\n", n);
+    Npixrd = (n == Npass - 1) ? Npixlast : Npixin;
+    for (i = 0; i < Nfiles; i++) {
+      fprintf (stderr, ".");
+      if (!gfits_read_header  (filename[i], &tmphead[i])) {
+	fprintf (stderr, "trouble reading file %s\n", filename[i]);
+	exit (1);
+      }
+      gfits_read_portion (filename[i], &tmpmatr[i], n*Npixin, Npixrd);
+      tmphead[i].Naxis[0] = 1;
+      tmphead[i].Naxis[1] = Npixrd;
+      gfits_convert_format (&tmphead[i], &tmpmatr[i], -32, 1.0, 0.0, FALSE);
+    }
+    
+    fprintf (stderr, "starting sorts\n");
+    for (j = 0; j < Npixrd; j++, O++) {
+      Nvalid = 0;
+      for (k = 0; k < Nfiles; k++) {
+	v = (float *)tmpmatr[k].buffer;
+	if (v[j] < MinValid) continue;
+	if (isnan(v[j])) continue;
+	if (isinf(v[j])) continue;
+	list[k] = v[j];
+	Nvalid ++;
+      }
+      if (Nvalid == 0) {
+	*O = 0.0;
+	continue;
+      }
+      fsort (list, Nvalid);
+      sum = 0;
+      Nval = 0;
+      for (k = fmin*Nvalid; k < fmax*Nvalid; k++) {
+	sum += list[k];
+	Nval ++;
+      }
+      *O = (sum / Nval);
+    }
+
+    for (i = 0; i < Nfiles; i++) {
+      fprintf (stderr, ",");
+      gfits_free_header (&tmphead[i]);
+      gfits_free_matrix (&tmpmatr[i]);
+    }
+    
+  }
+
+  gfits_write_header (argv[1], &head);
+  gfits_write_matrix (argv[1], &out);
+
+  return (0);
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/mefhead.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/mefhead.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/mefhead.c	(revision 22322)
@@ -0,0 +1,98 @@
+# include <ohana.h>
+# include <gfitsio.h>
+
+int main (int argc, char **argv) {
+
+  struct stat filestat;
+  int NEXTEND, Nextend, Nmatrix, status;
+  Header header;
+  FILE *f, *g;
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: mefhead (input) (output)\n");
+    exit (2);
+  }
+
+  /* exit if file exists */
+  status = stat (argv[2], &filestat);
+  if (status != -1) {
+    fprintf (stderr, "error: output file exists\n");
+    exit (1);
+  } 
+  
+  /* open stream for input */
+  g = fopen (argv[1], "r");
+  if (g == (FILE *) NULL) {
+    fprintf (stderr, "error: can't open input file\n");
+    exit (1);
+  } 
+
+  /* open stream for output */
+  f = fopen (argv[2], "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "error: can't create output file\n");
+    exit (1);
+  } 
+
+  /* read PHU */
+  status = gfits_fread_header (g, &header);
+  if (!status) {
+    fprintf (stderr, "error: can't read primary header unit\n");
+    exit (1);
+  }
+  /* skip matrix */
+  Nmatrix = gfits_data_size (&header);
+  fseek (g, Nmatrix, SEEK_CUR);
+
+  /* write PHU */
+  gfits_modify (&header, "NAXIS", "%d", 1, 0);
+  status = gfits_fwrite_header (f, &header);
+  if (!status) {
+    fprintf (stderr, "error: can't write primary header unit\n");
+    exit (1);
+  }
+
+  /* check if NEXTEND is accurate */
+  gfits_scan (&header, "NEXTEND", "%d", 1, &NEXTEND);
+
+  Nextend = 0;
+  while (1) {
+    
+    /* read header */
+    status = gfits_fread_header (g, &header);
+    if (!status) break;
+
+    /* skip matrix */
+    Nmatrix = gfits_data_size (&header);
+    fseek (g, Nmatrix, SEEK_CUR);
+    
+    /* write header */
+    gfits_modify (&header, "NAXIS", "%d", 1, 0);
+    status = gfits_fwrite_header (f, &header);
+    if (!status) {
+      fprintf (stderr, "error: can't write header unit %d\n", Nextend);
+      exit (1);
+    }
+    Nextend ++;
+    gfits_free_header (&header);
+  }
+
+  if (Nextend != NEXTEND) { 
+    fprintf (stderr, "warning: mismatch in NEXTEND\n");
+  }
+
+  fclose (f);
+  fclose (g);
+  exit (0);
+
+}
+
+
+
+/* mefhead (input) (output) 
+   convert (input) MEF image file to (output) MEF header file:
+   read all headers
+   convert all lines NAXIS=2 to NAXIS=0 
+   write only headers
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/mktemp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/mktemp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/mktemp.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include <stdio.h>
+# include <stdlib.h>
+
+int main (int argc, char **argv) {
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: %s (template)\n", argv[0]);
+    exit (1);
+  }
+
+  if (mkstemp (argv[1]) == -1) exit (1);
+
+  fprintf (stdout, "%s\n", argv[1]);
+
+  exit (0);
+}
+ 
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/precess.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/precess.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/precess.c	(revision 22322)
@@ -0,0 +1,150 @@
+# include <ohana.h>
+
+double BtoJ (double in_epoch);
+double get_epoch (char *in_epoch, char mode);
+
+int main (int argc, char **argv) {
+
+  double T, in_epoch, out_epoch;
+  double A, D, RA, DEC, zeta, z, theta;
+  double SA, CA, SD, CD;
+  int Julian, Besselian;
+
+  Besselian = Julian = 0;
+  Besselian = get_argument (argc, argv, "B");
+  Julian    = get_argument (argc, argv, "J");
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE:  precess in_epoch out_epoch\n");
+    fprintf (stderr, "   you may use B for B1950.0 or J for J2000.0\n");
+    fprintf (stderr, "   Enter values in degrees\n");
+    exit (2);
+  }
+
+  if (!Julian && !Besselian) { /* assume Julian! */
+    in_epoch  = get_epoch (argv[1], 'J');
+    out_epoch = get_epoch (argv[2], 'J');
+  }
+
+  if ((Julian == 1) && !Besselian) {
+    in_epoch  = 2000.0;
+    out_epoch = get_epoch(argv[2], 'J');
+  }
+
+  if ((Julian == 2) && !Besselian) {
+    in_epoch  = get_epoch(argv[1], 'J');
+    out_epoch = 2000.0;
+  }
+
+  if ((Besselian == 1) && !Julian) {
+    in_epoch  = BtoJ(1950.0); 
+    out_epoch = get_epoch(argv[2], 'B'); 
+  }
+
+  if ((Besselian == 2) && !Julian) {
+    in_epoch  = get_epoch(argv[1], 'B'); 
+    out_epoch = BtoJ(1950.0); 
+  }
+  
+  if (Julian && Besselian) {
+    if (Julian > Besselian) {
+      in_epoch  = BtoJ(1950.0); 
+      out_epoch = 2000.0;
+    }
+    else {
+      in_epoch  = 2000.0;
+      out_epoch = BtoJ(1950.0); 
+    }
+  }
+
+  fprintf (stderr, "converting from J%f to J%f\n", in_epoch, out_epoch);
+
+  T = (out_epoch - in_epoch) / 100.0;
+  
+  zeta  = RAD_DEG*(0.6406161*T + 0.0000839*T*T + 0.0000050*T*T*T);
+  theta = RAD_DEG*(0.5567530*T - 0.0001185*T*T - 0.0000116*T*T*T);
+  z     =          0.6406161*T + 0.0003041*T*T + 0.0000051*T*T*T;
+
+  while (fscanf (stdin, "%lf %lf", &A, &D) != EOF) {
+    SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+    CD = sqrt (1 - SD*SD);
+    SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+    CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+
+    DEC = DEG_RAD*asin(SD);
+    RA  = DEG_RAD*atan2(SA, CA) + z;
+
+    if (RA < 0)
+      RA += 360;
+    fprintf (stdout, "%f %f\n", RA, DEC);
+  }
+  exit (0);
+}  
+    
+
+double get_epoch (char *in_epoch, char mode) {
+
+  int done;
+  double epoch;
+
+  done = FALSE;
+  if (in_epoch[0] == 'B') {
+    epoch = BtoJ(atof(&in_epoch[1]));
+    done = TRUE;
+  }
+
+  if (in_epoch[0] == 'J') {
+    epoch = atof(&in_epoch[1]);
+    done = TRUE;
+  }
+
+  if (!done && (mode == 'B')) {
+    epoch = BtoJ(atof(in_epoch));
+    done = TRUE;
+  }
+    
+  if (!done && (mode == 'J')) {
+    epoch = atof(in_epoch);
+    done = TRUE;
+  }
+
+  if (!done) {
+    fprintf (stderr, "error finding epoch %s\n", in_epoch);
+    exit (1);
+  }
+  
+  return (epoch);
+}
+
+double BtoJ (double in_epoch) {
+
+  double JD, out_epoch;
+
+  JD = (in_epoch - 1900.0)*365.242198781 + 2415020.31352;
+  out_epoch = 2000.0 + (JD - 2451545.0)/365.25;
+
+  return (out_epoch);
+}
+
+/*
+  time =  (in_epoch - out_epoch) / 100.0;
+
+  fprintf (stderr, "precessing from %f to %f\n", in_epoch, out_epoch);
+  sinE = sin (epsilon*deg_to_rad);
+  cosE = cos (epsilon*deg_to_rad);
+  M = 1.281232*time + 0.0003879*(time*time);
+  N = 0.5567*time - 0.0001185*(time*time);
+
+  while (fscanf (stdin, "%lf %lf", &RA, &Dec) != EOF) {
+    RA = RA - M - N*sin(RA*deg_to_rad)*tan(Dec*deg_to_rad);
+    Dec = Dec - N*cos (RA*deg_to_rad);
+
+    Dec = Dec + time*thetaD*sinE*cos(RA*deg_to_rad);
+    RA = RA + time*thetaD*(cosE + sinE*sin(RA*deg_to_rad)*tan(Dec*deg_to_rad));
+
+    fprintf (stdout, "%f %f\n", RA, Dec);
+  }
+
+*/
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/radec.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/radec.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/tools/src/radec.c	(revision 22322)
@@ -0,0 +1,102 @@
+# include <ohana.h>
+
+int help ();
+int _hms_to_deg (double *h, double *d, char *string, char sep, int Nin);
+
+int main (int argc, char **argv) {
+
+  char line[1000];
+  char sep;
+  double ra, dec;
+  int status, h, m, flag;
+  double s, hh;
+
+  sep = ' ';
+  if (get_argument (argc, argv, "-hms")) {
+    while (scan_line (stdin, line) != EOF) {
+      status = _hms_to_deg (&ra, &dec, line, sep, 3);
+      if (status)
+	fprintf (stdout, "%10.6f %10.6f\n", ra, dec);
+    }
+    exit (0);
+  }
+    
+  if (get_argument (argc, argv, "-hh")) {
+    while (scan_line (stdin, line) != EOF) {
+      dparse (&hh, 1, line);
+      hh /= 15.0;  /* convert from degrees to hours */
+      flag = SIGN(hh);
+      hh *= flag;
+      h = hh;
+      m = 60.000001*(hh - h);
+      s = 3600*(hh - h - m / 60.0);
+      if (flag > 0)
+	fprintf (stdout, " %02d%c%02d%c%06.3f  ", h, sep, m, sep, s);
+      else
+	fprintf (stdout, "-%02d%c%02d%c%06.3f  ", h, sep, m, sep, s);
+      dparse (&hh, 2, line);
+      flag = SIGN(hh);
+      hh *= flag;
+      h = hh;
+      m = 60.000001*(hh - h);
+      s = 3600*(hh - h - m / 60.0);
+      if (flag > 0)
+	fprintf (stdout, " %02d%c%02d%c%06.3f\n", h, sep, m, sep, s);
+      else
+	fprintf (stdout, "-%02d%c%02d%c%06.3f\n", h, sep, m, sep, s);
+    }
+    exit (0);
+  }
+
+  fprintf (stderr, "USAGE: %s [-hh/-hms] \n", argv[0]);
+  fprintf (stderr, "  -hh:  convert from decimal ra,dec to hours, min sec\n");
+  fprintf (stderr, "  -hms: convert from hours, min sec to decimal ra,dec\n");
+  exit (2);
+}
+
+/**********/
+int _hms_to_deg (double *h, double *d, char *string, char sep, int Nin) {
+  
+  char *c;
+  int i, flag_d, flag_h, Nfields;
+  double tmp;
+  
+  *d = *h = 0;
+  stripwhite (string);
+  for (Nfields = 2, i = 0; i < 2*(Nin - 1); i++) {
+    if ((c = strchr (string, sep)) != NULL) {
+      Nfields ++;
+      *c = ' ';
+    }
+  }
+  if (Nfields != 2*Nin) {
+    fprintf (stderr, "warning -- line with too few entries, skipping:  %d != %d\n", Nfields, Nin);
+    return (FALSE);
+  }
+
+  Nfields /= 2;
+
+  flag_h = dparse (h, 1, string);
+  flag_d = dparse (d, Nfields + 1, string);
+  *h *= flag_h;
+  *d *= flag_d;
+
+  if (Nfields > 1) {
+    dparse (&tmp, 2, string);
+    *h += tmp/60.0;
+    dparse (&tmp, Nfields + 2, string);
+    *d += tmp/60.0;
+  }
+
+  if (Nfields > 2) {
+    dparse (&tmp, 3, string);
+    *h += tmp/3600.0;
+    dparse (&tmp, Nfields + 3, string);
+    *d += tmp/3600.0;
+  }
+  
+  *h *= 15*flag_h;
+  *d *= flag_d;
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/Makefile	(revision 22322)
@@ -0,0 +1,40 @@
+default: uniphot
+help:
+	@echo "make options: uniphot (default)"
+
+include ../../Makefile.System
+HOME    =       $(ROOT)/src/uniphot
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+uniphot: $(BIN)/uniphot.$(ARCH)
+install: $(DESTBIN)/uniphot
+
+UNIPHOT = \
+$(SRC)/uniphot.$(ARCH).o	    \
+$(SRC)/initialize.$(ARCH).o	    \
+$(SRC)/ConfigInit.$(ARCH).o	    \
+$(SRC)/args.$(ARCH).o		    \
+$(SRC)/liststats.$(ARCH).o	    \
+$(SRC)/load_images.$(ARCH).o	    \
+$(SRC)/subset_images.$(ARCH).o	    \
+$(SRC)/find_image_tgroups.$(ARCH).o \
+$(SRC)/find_image_sgroups.$(ARCH).o \
+$(SRC)/fit_groups.$(ARCH).o	    \
+$(SRC)/update.$(ARCH).o		    \
+$(SRC)/update_catalog.$(ARCH).o	    \
+$(SRC)/SetSignals.$(ARCH).o	    \
+$(SRC)/Shutdown.$(ARCH).o	    \
+$(SRC)/sort.$(ARCH).o
+
+$(UNIPHOT): $(INC)/uniphot.h
+$(BIN)/uniphot.$(ARCH): $(UNIPHOT)
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/bin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/bin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/bin/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/doc/ChangeLog.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/doc/ChangeLog.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/doc/ChangeLog.txt	(revision 22322)
@@ -0,0 +1,24 @@
+
+- uniphot-1-4:
+  * converted to gfits APIs (forces libfits 1.6)
+  * converted to new DVO APIs (forces libdvo 1.3)
+  * removed old, unused code
+
+- uniphot-1-3:
+  * dropped IMAGE_CATALOG from config
+  * convert to dvo_image_lock,unlock
+  * dropped _PS from average.R,D,M,dM
+
+- uniphot-1-2:
+
+  * various changes to use mode/format concepts
+  * mods to use new dvo load functions / libdvo
+  * minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+- uniphot-1-1:
+
+  * changes from loneos.h to dvo.h (libohana)
+
+- uniphot-1-0:
+
+  * initial release
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/doc/notes.txt	(revision 22322)
@@ -0,0 +1,14 @@
+
+<h2> uniphot </h2>
+
+<ul>
+<li> load images (select on photcode, time range)
+<li> create time groups
+<li> create space groups
+<li> solve for:
+     tgroup[i].M = ave(image[tgroup].Mcal - image[tgroup].sgroup.Mcal)
+     sgroup[i].M = ave(image[sgroup].Mcal - image[tgroup].tgroup.Mcal)
+
+     image.tgroup -> tgroup
+     group.Nimage, image
+     
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/include/uniphot.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/include/uniphot.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/include/uniphot.h	(revision 22322)
@@ -0,0 +1,106 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+
+# ifdef ANSI
+#   define F_SETFL      4   
+#   define O_NONBLOCK   0200000  
+#   define AF_UNIX      1          
+#   define SOCK_STREAM  1          
+#   define ENOENT       2       /* No such file or directory    */
+# endif /* ANSI */
+
+typedef struct {
+  double median;
+  double mean;
+  double sigma;
+  double error;
+  double chisq;
+  double min;
+  double max;
+  double total;
+  int    Nmeas;
+} StatType;
+
+typedef struct {
+  void *tgroup;
+  void *sgroup;
+} ImageLink;
+
+typedef struct {
+  char label[64];
+  float M;
+  float dM;
+  float dMsub;
+  double v1, v2;
+  Image **image;
+  ImageLink **imlink;
+  int Nimage, Ngood;
+} Group;
+
+/* global variables set in parameter file */
+char         ImageCat[256];
+char         CATDIR[256];
+char         CATMODE[16];    /* raw, mef, split, mysql */
+char         CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char         STATMODE[64];
+int          VERBOSE;
+int          NLOOP;
+int          TimeSelect;
+int          VERBOSE;
+int          UPDATE;
+int          IMAGE_BAD;
+double       RADIUS;
+double       TRANGE;
+time_t 	     TSTART;
+time_t 	     TSTOP;
+PhotCode    *photcode;
+
+enum {black, white, red, orange, yellow, green, blue, indigo, violet};
+
+typedef struct {
+  double xmin, xmax, ymin, ymax;
+  int style, ptype, ltype, etype, color;
+  double lweight, size;
+} Graphdata;
+
+/***** prototypes ****/
+void          ConfigInit         PROTO((int *argc, char **argv));
+void          DonePlotting       PROTO((Graphdata *graphmode, int N));
+void          JpegPlot           PROTO((Graphdata *graphmode, int N, char *filename));
+void          PSPlot             PROTO((Graphdata *graphmode, int N, char *filename));
+void          PlotLabel          PROTO((char *string, int N));
+void          PlotVector         PROTO((int Npts, double *vect, int mode, int N));
+void          PrepPlotting       PROTO((int Npts, Graphdata *graphmode, int N));
+void          XDead              PROTO(());
+int           args               PROTO((int argc, char **argv));
+void          dumpresult         PROTO(());
+Group        *find_image_sgroups PROTO((FITS_DB *db, ImageLink **imlink, int *Nsgroup));
+Group        *find_image_tgroups PROTO((FITS_DB *db, ImageLink **imlink, int *Ntgroup));
+void          fit_sgroup         PROTO((Group *sgroup, int Nsgroup));
+void          fit_tgroup         PROTO((Group *tgroup, int Ntgroup));
+int           gcatalog           PROTO((Catalog *catalog));
+void          initialize         PROTO((int argc, char **argv));
+void          initstats          PROTO((char *mode));
+int           liststats          PROTO((double *value, double *dvalue, int N, StatType *stats));
+int           load_images        PROTO((FITS_DB *db));
+int           main               PROTO((int argc, char **argv));
+int           open_graph         PROTO((int N));
+void          sort               PROTO((unsigned int *X, int N));
+void          sortB              PROTO((double *X, double *Y, int N));
+void          sortD              PROTO((double *X, double *Y, double *Z, int N));
+void          update_dvo_catalog PROTO((Catalog *catalog, Group *sgroup, int warn));
+void          wcatalog           PROTO((Catalog *catalog));
+void          wimages            PROTO((Image *image, int Nimage));
+void check_permissions (char *basefile);
+void lock_image_db (FITS_DB *db, char *filename);
+void unlock_image_db (FITS_DB *db);
+void create_image_db (FITS_DB *db);
+void set_db (FITS_DB *in);
+int Shutdown (char *format, ...);
+void TrapSignal (int sig);
+void SetProtect (int mode);
+int SetSignals ();
+int subset_images (FITS_DB *db);
+void update (FITS_DB *db, Group *sgroup, int Nsgroup);
+void sort_time (unsigned int *value, int N);
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/ConfigInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/ConfigInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/ConfigInit.c	(revision 22322)
@@ -0,0 +1,43 @@
+# include "uniphot.h"
+
+void ConfigInit (int *argc, char **argv) {
+
+  double ZERO_POINT;
+  char  *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  ScanConfig (config, "CATDIR",                 "%s",  0, CATDIR);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig (config, "PHOTCODE_FILE",         	"%s",  0, MasterPhotcodeFile);
+
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  ScanConfig (config, "ZERO_PT",                "%lf", 0, &ZERO_POINT);
+  SetZeroPoint (ZERO_POINT);
+
+  free (config);
+  free (file);
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/SetSignals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/SetSignals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/SetSignals.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "uniphot.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect this signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore */
+    case SIGCONT:    /* continue - maintain this action */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? */
+    case SIGURG:     /* socket signal, ignore this */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/Shutdown.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/Shutdown.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/Shutdown.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "uniphot.h"
+
+static FITS_DB *db;
+
+void set_db (FITS_DB *in) {
+  db = in;
+}
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format));
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  SetProtect (TRUE);
+  gfits_db_close (db);
+  fprintf (stderr, "ERROR: addstar halted\n");
+  exit (1);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/args.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/args.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/args.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "uniphot.h"
+
+int args (int argc, char **argv) {
+
+  int N;
+
+  /* define time */
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &TSTART)) { 
+      fprintf (stderr, "ERROR: syntax error\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &TSTOP)) { 
+      fprintf (stderr, "ERROR: syntax error\n");
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  NLOOP = 8;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    NLOOP = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  strcpy (STATMODE, "MEAN");
+  if ((N = get_argument (argc, argv, "-statmode"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (STATMODE, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  UPDATE = FALSE;
+  if ((N = get_argument (argc, argv, "-update"))) {
+    remove_argument (N, &argc, argv);
+    UPDATE = TRUE;
+  }
+
+  /* max separation for unique space group, in degrees */
+  RADIUS = 2.0;
+  if ((N = get_argument (argc, argv, "-radius"))) {
+    remove_argument (N, &argc, argv);
+    RADIUS = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* max separation for unique time group, in days -> seconds */
+  TRANGE = 86400*7.0;
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    TRANGE = 86400*atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    fprintf (stderr, "ERROR: USAGE: uniphot (photcode) [options]\n");
+    exit (2);
+  } 
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/dumpresult.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/dumpresult.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/dumpresult.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "uniphot.h"
+
+void dumpresult () {
+  short Mcal, Mgrp, Mset;
+  FILE *f;
+  char outfile[64];
+  Group *tgrp;
+
+  for (i = 0; i < Nsgroup; i++) {
+    sprintf (outfile, "test.%02d.dat", i);
+    f = fopen (outfile, "w");
+    for (j = 0; j < sgroup[i].Nimage; j++) {
+      if (sgroup[i].image[j][0].code & IMAGE_BAD) continue;
+      tgrp = (Group *) sgroup[i].imlink[j][0].tgroup;
+      Mcal = sgroup[i].image[j][0].Mcal;
+      Mset = sgroup[i].M;
+      Mgrp = tgrp[0].M;
+      fprintf (f, "%7.4f %7.4f %7.4f %7.4f   %10.6f %10.6f  %f %s\n", 
+	       0.001*Mcal, 0.001*Mgrp, 0.001*Mset, 0.001*sgroup[i].image[j][0].dMcal, 
+	       sgroup[i].image[j][0].coords.crval1, sgroup[i].image[j][0].coords.crval2, (sgroup[i].image[j][0].tzero-915148800)/86400.0, tgrp[0].label);
+    }
+    fclose (f);
+  }
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/find_image_sgroups.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/find_image_sgroups.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/find_image_sgroups.c	(revision 22322)
@@ -0,0 +1,85 @@
+# include "uniphot.h"
+
+Group *find_image_sgroups (FITS_DB *db, ImageLink **Imlink, int *Nsgroup) {
+
+  int i, j, Nimage, Ngroup, Nentry, NENTRY;
+  double r, d, x, y, radius;
+  Group *group;
+  Coords coords;
+  Image *image;
+  ImageLink *imlink;
+
+  imlink = *Imlink;
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  coords.crpix1 = coords.crpix2 = 0.0;
+  coords.cdelt1 = coords.cdelt2 = 1.0; /* scale is degrees, radius in degrees */
+  coords.pc1_1 = coords.pc2_2 = 1.0;
+  coords.pc1_2 = coords.pc2_1 = 0.0;
+  strcpy (coords.ctype, "RA---TAN");
+  
+  Ngroup = 0;
+  ALLOCATE (group, Group, Nimage);
+
+  /* set imlink.sgroups = NULL as a marker */
+  for (i = 0; i < Nimage; i++) imlink[i].sgroup = NULL;
+
+  for (i = 0; i < Nimage; i++) {
+    if (imlink[i].sgroup != NULL) continue;
+    if (image[i].code & ID_IMAGE_NOCAL) continue;
+
+    /* new sgroup, set ref coords */
+    XY_to_RD (&r, &d, 0.5*image[i].NX, 0.5*image[i].NX, &image[i].coords);
+    coords.crval1 = r;
+    coords.crval2 = d;
+
+    /* init sgroup structure */
+    Nentry = 0;
+    NENTRY = 100;
+    ALLOCATE (group[Ngroup].image, Image *, NENTRY);
+    ALLOCATE (group[Ngroup].imlink, ImageLink *, NENTRY);
+    group[Ngroup].M = 0;
+    group[Ngroup].dM = 0;
+    snprintf (group[Ngroup].label, 64, "%10.6f - %10.6f", r, d);
+    group[Ngroup].v1 = r;
+    group[Ngroup].v2 = d;
+
+    /* link this image to sgroup */
+    group[Ngroup].image[Nentry] = &image[i];
+    group[Ngroup].imlink[Nentry] = &imlink[i];
+    imlink[i].sgroup = &group[Ngroup];
+    Nentry ++;
+
+    for (j = 0; j < Nimage; j++) {
+      if (image[j].code & ID_IMAGE_NOCAL) continue;
+      if (imlink[j].sgroup != NULL) continue;
+
+      /* project image center to local coords, check radius */
+      XY_to_RD (&r, &d, 0.5*image[j].NX, 0.5*image[j].NX, &image[j].coords);
+      if (!RD_to_XY (&x, &y, r, d, &coords)) continue; 
+      /* RD_to_XY returns FALSE if opposite hemispheres */
+      radius = hypot (x, y);
+
+      if (radius > RADIUS) continue;
+
+      /* image in sgroup, add to entry */
+      group[Ngroup].image[Nentry] = &image[j];
+      group[Ngroup].imlink[Nentry] = &imlink[j];
+      imlink[j].sgroup = &group[Ngroup];
+      Nentry ++;
+      if (Nentry == NENTRY) {
+	NENTRY += 100;
+	REALLOCATE (group[Ngroup].image, Image *, NENTRY);
+	REALLOCATE (group[Ngroup].imlink, ImageLink *, NENTRY);
+      }
+    }
+    group[Ngroup].Nimage = Nentry;
+    Ngroup ++;
+  }
+  *Nsgroup = Ngroup;
+  return (group);
+}
+
+  /* this is a bit weak: since we use pointers, we can't
+     reallocate group after the pointers are assigned.
+     therefore, we allocate the max possible groups */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/find_image_tgroups.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/find_image_tgroups.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/find_image_tgroups.c	(revision 22322)
@@ -0,0 +1,84 @@
+# include "uniphot.h"
+
+Group *find_image_tgroups (FITS_DB *db, ImageLink **Imlink, int *Ntgroup) {
+
+  char *start, *stop;
+  int i, j, Nimage, Ngroup, NGROUP, Nentry, NENTRY;
+  unsigned int *time, *tmin, *tmax;
+  Group *group;
+  Image *image;
+  ImageLink *imlink;
+
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+  ALLOCATE (imlink, ImageLink, Nimage);
+
+  /* sort time list (use only valid images?) */
+  ALLOCATE (time, unsigned int, Nimage);
+  for (i = 0; i < Nimage; i++) {
+    time[i] = image[i].tzero;
+  }
+  sort_time (time, Nimage);
+
+  /* find groups with dt < TRANGE */
+  Ngroup = 0;
+  NGROUP = 100;
+  ALLOCATE (tmin, unsigned int, NGROUP);
+  ALLOCATE (tmax, unsigned int, NGROUP);
+  tmin[Ngroup] = time[0];
+
+  /* generate tgroups */
+  for (i = 0; i < Nimage - 1; i++) {
+    if (time[i+1] - time[i] < TRANGE) continue;
+    
+    tmax[Ngroup] = time[i];
+    
+    Ngroup ++;
+    if (Ngroup == NGROUP) {
+      NGROUP += 100;
+      REALLOCATE (tmin, unsigned int, NGROUP);
+      REALLOCATE (tmax, unsigned int, NGROUP);
+    }
+    tmin[Ngroup] = time[i + 1];
+  }
+  tmax[Ngroup] = time[Nimage - 1];
+  Ngroup ++;
+  ALLOCATE (group, Group, Ngroup);
+
+  /* assign images to groups */
+  for (i = 0; i < Ngroup; i++) {
+    Nentry = 0;
+    NENTRY = 100;
+    ALLOCATE (group[i].image, Image *, NENTRY);
+    ALLOCATE (group[i].imlink, ImageLink *, NENTRY);
+    group[i].M = 0;
+    group[i].dM = 0;
+
+    start = ohana_sec_to_date (tmin[i]);
+    stop = ohana_sec_to_date (tmax[i]);
+    snprintf (group[i].label, 64, "%s - %s", start, stop);
+    free (start);
+    free (stop);
+
+    for (j = 0; j < Nimage; j++) {
+      if (image[j].tzero < tmin[i]) continue;
+      if (image[j].tzero > tmax[i]) continue;
+      if (image[j].code & ID_IMAGE_NOCAL) continue;
+      
+      group[i].image[Nentry] = &image[j];
+      group[i].imlink[Nentry] = &imlink[j];
+      imlink[j].tgroup = &group[i];
+      Nentry ++;
+      if (Nentry == NENTRY) {
+	NENTRY += 100;
+	REALLOCATE (group[i].image, Image *, NENTRY);
+	REALLOCATE (group[i].imlink, ImageLink *, NENTRY);
+      }
+    }
+    group[i].Nimage = Nentry;
+  }
+  *Imlink = imlink;
+  *Ntgroup = Ngroup;
+  return (group);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/fit_groups.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/fit_groups.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/fit_groups.c	(revision 22322)
@@ -0,0 +1,76 @@
+# include "uniphot.h"
+
+void fit_tgroup (Group *tgroup, int Ntgroup) {
+
+  int i, j, Nlist;
+  float Mcal, Mgrp;
+  double *mlist, *dlist;
+  StatType stats;
+  Group *sgroup;
+
+  Nlist = tgroup[0].Nimage;
+  for (i = 0; i < Ntgroup; i++) { Nlist = MAX (Nlist, tgroup[i].Nimage); }
+  ALLOCATE (mlist, double, Nlist);
+  ALLOCATE (dlist, double, Nlist);
+
+  for (i = 0; i < Ntgroup; i++) {
+    for (j = Nlist = 0; j < tgroup[i].Nimage; j++) {
+      if (tgroup[i].image[j][0].code & IMAGE_BAD) continue;
+      sgroup = (Group *) tgroup[i].imlink[j][0].sgroup;
+      Mcal = tgroup[i].image[j][0].Mcal;
+      Mgrp = sgroup[0].M;
+      mlist[Nlist] = (Mcal - Mgrp);
+      dlist[Nlist] = tgroup[i].image[j][0].dMcal;
+      Nlist ++;
+    }
+    liststats (mlist, dlist, Nlist, &stats);
+    tgroup[i].M  = stats.mean;
+    tgroup[i].dMsub = stats.sigma;
+    tgroup[i].Ngood = stats.Nmeas;
+    
+    initstats ("MEAN");
+    liststats (mlist, dlist, Nlist, &stats);
+    tgroup[i].dM = stats.sigma;
+    initstats (STATMODE);
+  }
+  free (mlist);
+  free (dlist);
+}
+
+void fit_sgroup (Group *sgroup, int Nsgroup) {
+
+  int i, j, Nlist;
+  float Mcal, Mgrp;
+  double *mlist, *dlist;
+  StatType stats;
+  Group *tgroup;
+  
+  Nlist = sgroup[0].Nimage;
+  for (i = 0; i < Nsgroup; i++) { Nlist = MAX (Nlist, sgroup[i].Nimage); }
+  ALLOCATE (mlist, double, Nlist);
+  ALLOCATE (dlist, double, Nlist);
+  Nlist = 0;
+
+  for (i = 0; i < Nsgroup; i++) {
+    for (j = Nlist = 0; j < sgroup[i].Nimage; j++) {
+      if (sgroup[i].image[j][0].code & IMAGE_BAD) continue;
+      tgroup = (Group *) sgroup[i].imlink[j][0].tgroup;
+      Mcal = sgroup[i].image[j][0].Mcal;
+      Mgrp = tgroup[0].M;
+      mlist[Nlist] = (Mcal - Mgrp);
+      dlist[Nlist] = sgroup[i].image[j][0].dMcal;
+      Nlist ++;
+    }
+    liststats (mlist, dlist, Nlist, &stats);
+    sgroup[i].M  = stats.mean;
+    sgroup[i].dMsub = stats.sigma;
+    sgroup[i].Ngood = stats.Nmeas;
+
+    initstats ("MEAN");
+    liststats (mlist, dlist, Nlist, &stats);
+    sgroup[i].dM = stats.sigma;
+    initstats (STATMODE);
+  }
+  free (mlist);
+  free (dlist);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/initialize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/initialize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/initialize.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "uniphot.h"
+
+void initialize (int argc, char **argv) {
+
+  /* are these set correctly? */
+  ConfigInit (&argc, argv);
+  args (argc, argv);
+
+  if ((photcode = GetPhotcodebyName (argv[1])) == NULL) {
+    fprintf (stderr, "ERROR: photcode not found in photcode table\n");
+    exit (1);
+  }
+  if ((photcode[0].type == PHOT_DEP) && (photcode[0].type == PHOT_REF)) {
+    fprintf (stderr, "photcode must be primary or secondary type\n");
+    exit (1);
+  }
+
+  IMAGE_BAD = ID_IMAGE_NOCAL | ID_IMAGE_POOR | ID_IMAGE_SKIP | ID_IMAGE_FEW;
+
+  initstats (STATMODE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/liststats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/liststats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/liststats.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include "uniphot.h"
+
+enum {M_MEAN, M_MEDIAN, M_WT_MEAN, M_INNER_MEAN, 
+      M_INNER_WTMEAN, M_CHI_INNER_MEAN, M_CHI_INNER_WTMEAN};
+
+static int statmode;
+
+void initstats (char *mode) {
+
+  statmode = -1;
+  if (!strcmp (mode, "MEAN")) statmode = M_MEAN;
+  if (!strcmp (mode, "MEDIAN")) statmode = M_MEDIAN;
+  if (!strcmp (mode, "WT_MEAN")) statmode = M_WT_MEAN;
+  if (!strcmp (mode, "INNER_MEAN")) statmode = M_INNER_MEAN;
+  if (!strcmp (mode, "INNER_WTMEAN")) statmode = M_INNER_WTMEAN;
+  if (!strcmp (mode, "CHI_INNER_MEAN")) statmode = M_CHI_INNER_MEAN;
+  if (!strcmp (mode, "CHI_INNER_WTMEAN")) statmode = M_CHI_INNER_WTMEAN;
+
+  if (statmode == -1) {
+    fprintf (stderr, "ERROR: invalid stats mode: %s\n", mode);
+    exit (1);
+  }
+}
+
+int liststats (double *value, double *dvalue, int N, StatType *stats) {
+  
+  int i, ks, ke, Nm;
+  double Mo, dMo, M, dM, X2, dS, *chi;
+
+  ke = ks = dMo = 0;
+
+  stats[0].Nmeas = N;
+  stats[0].mean  = 0;
+  stats[0].sigma = 0;
+  if (N < 2) return (FALSE);
+
+  dsortpair (value, dvalue, N);
+  stats[0].median = value[(int)(0.5*N)];
+  stats[0].min    = value[0];
+  stats[0].max    = value[N-1];
+
+  switch (statmode) {
+  case M_MEDIAN:
+    ks = 0;
+    ke = N;
+    Mo = stats[0].median;
+    Nm = N;
+    goto chisq;
+    break;
+  case M_MEAN:
+  case M_WT_MEAN:
+    ks = 0;
+    ke = N;
+    break;
+  case M_INNER_MEAN:
+  case M_INNER_WTMEAN:
+  case M_CHI_INNER_MEAN:
+  case M_CHI_INNER_WTMEAN:
+    ks = 0.25*N + 0.50;
+    ke = 0.75*N + 0.25;
+    if (N <= 3) {
+      ks = 0;
+      ke = N;
+    }
+    break;
+  }    
+
+  if ((statmode == M_CHI_INNER_MEAN) || (statmode == M_CHI_INNER_WTMEAN)) {
+    ALLOCATE (chi, double, N);
+    for (i = 0; i < N; i++) {
+      chi[i] = (value[i] - stats[0].median) / dvalue[i];
+    }
+    dsortthree (chi, value, dvalue, N);
+    free (chi);
+  }
+
+  /* calculating the per-star offset based on the weighted average */
+  M = dM = Nm = 0;
+  if ((statmode == M_WT_MEAN) || (statmode == M_INNER_WTMEAN) || (statmode == M_CHI_INNER_WTMEAN)) {
+    for (i = ks; i < ke; i++) {
+      M   += value[i] / SQ (dvalue[i]);
+      dM  += 1.0 / SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / dM;
+    dMo = sqrt (1.0 / dM);
+  } else {
+    for (i = ks; i < ke; i++) {
+      M   += value[i];
+      dM  += SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / (double) Nm;
+    dMo = sqrt (dM / (double) Nm);
+  }
+
+ chisq:
+  /* find sigma and chisq */
+  X2 = dS = 0;
+  for (i = ks; i < ke; i++) {
+    M  = SQ (value[i] - Mo);
+    dM = SQ (dvalue[i]);
+    X2 += M / dM;
+    dS += M;
+  }
+  X2 = X2 / Nm;
+  dS = sqrt (dS / Nm);
+
+  stats[0].mean  = Mo;
+  stats[0].Nmeas = Nm;
+  stats[0].chisq = X2;
+  stats[0].sigma = dS;
+  stats[0].error = dMo;
+
+  return (TRUE);
+}
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/load_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/load_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/load_images.c	(revision 22322)
@@ -0,0 +1,11 @@
+# include "uniphot.h"
+
+int load_images (FITS_DB *db) {
+
+  if (VERBOSE) fprintf (stderr, "finding images\n");
+
+  /* read entire db table */
+  if (!dvo_image_load (db, VERBOSE, FALSE)) Shutdown ("can't read image catalog %s", db[0].filename);
+
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/plotstuff.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/plotstuff.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/plotstuff.c	(revision 22322)
@@ -0,0 +1,119 @@
+# include "uniphot.h"
+# include <signal.h>
+
+static int Xgraph[5] = {0,0,0,0,0};
+static int active;
+
+void XDead () {
+  signal (SIGPIPE, XDead);
+  fprintf (stderr, "kapa is dead, must restart\n");
+  Xgraph[active] = -1;
+}
+
+int open_graph (int N) {
+
+  char name[100];
+  
+  active = N;
+
+  sprintf (name, "gastro [%d]", N);
+  Xgraph[N] = KapaOpen ("kapa", name);
+
+  if (Xgraph[N] < 0) {
+    fprintf (stderr, "error starting kapa\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+void DonePlotting (Graphdata *graphmode, int N) {
+
+  if (Xgraph[N] == 0) return;
+  KapaBox (Xgraph[N], graphmode);
+  return;
+}
+
+void PrepPlotting (int Npts, Graphdata *graphmode, int N) {
+
+  active = N;
+  if (Npts < 1) return;
+
+  KapaPrepPlot (Xgraph[N], Npts, graphmode);
+}
+
+void PlotVector (int Npts, float *vect, int mode, int N) {
+
+  int Nbytes;
+
+  if (Npts < 1) return;
+  active = N;
+  KapaPlotVector (Xgraph[N], Npts, vect);
+}
+
+void PlotLabel (char *string, int N) {
+
+  char buffer[32];
+
+  if (Xgraph[N] == 0) return;
+
+  KapaSendLabel (Xgraph[N[, string, 4);
+}
+
+void PlotReset (int N) {
+
+  char buffer[128];
+  int i;
+
+  /* test Xgraph[N], flush junk from pipe */
+  signal (SIGPIPE, XDead);
+  fcntl (Xgraph[N], F_SETFL,  O_NONBLOCK); 
+  for (i = 0; (read (Xgraph[N], buffer, 64) > 0) && (i < 20); i++);
+  fcntl (Xgraph[N], F_SETFL, !O_NONBLOCK); 
+  
+  if (Xgraph[N] < 1) if (!open_graph(N)) return;
+  KapaClearSections (Xgraph[N]);
+}
+
+void JpegPlot (Graphdata *graphmode, int N, char *filename) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaPNG (Xgraph[N], filename);
+  return;
+}
+
+void PSPlot (Graphdata *graphmode, int N, char *filename) {
+
+  if (Xgraph[N] == 0) return;
+
+  KiiPS (Xgraph[N], filename, TRUE, KAPA_PS_NEWPLOT, "default");
+  return;
+}
+
+/* include these lines to plot a pair of vectors: 
+
+   typedef struct {
+   double xmin, xmax, ymin, ymax;
+   int style, ptype, ltype, etype, color;
+   double lweight, size;
+   } Graphdata;
+   Graphdata graphdata;
+   
+   graphdata.xmin = -200;
+   graphdata.xmax = 4200;
+   graphdata.ymin = -500;
+   graphdata.ymax = 500;
+   graphdata.style = 2;
+   graphdata.ptype = 2;
+   graphdata.ltype = 0;
+   graphdata.etype = 0;
+   graphdata.color = 0;
+   graphdata.lweight = 0;
+   graphdata.size = 0.5;
+   
+   PrepPlotting (N, &graphdata, n);
+   PlotVector (N, Y, 0, n);
+   PlotVector (N, dM, 1, n);
+   DonePlotting (&graphdata, n);
+   
+ */
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/sort.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/sort.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/sort.c	(revision 22322)
@@ -0,0 +1,13 @@
+# include "uniphot.h"
+
+void sort_time (unsigned int *value, int N) {
+
+# define SWAPFUNC(A,B){ unsigned int tmp = value[A]; value[A] = value[B]; value[B] = tmp; }
+# define COMPARE(A,B)(value[A] < value[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/subset_images.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/subset_images.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/subset_images.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "uniphot.h"
+
+int subset_images (FITS_DB *db) {
+
+  int    i, Nimage, equiv;
+  int    Nkeep, *keep;
+  Image *image;
+
+  /* use a vtable to keep the images to be calibrated */
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  Nkeep = 0;
+  ALLOCATE (keep, int, Nimage);
+
+  /* mark images to be calibrated */
+  for (i = 0; i < Nimage; i++) {
+      
+    image[i].code |= ID_IMAGE_NOCAL;
+
+    /* select images by photcode */
+    equiv = GetPhotcodeEquivCodebyCode (image[i].photcode);
+    if (equiv != photcode[0].code) continue;
+
+    /* select images by time */
+    if (TimeSelect) {
+      if (image[i].tzero < TSTART) continue;
+      if (image[i].tzero > TSTOP) continue;
+    }
+    image[i].code &= ~ID_IMAGE_NOCAL;
+    keep[Nkeep] = i;
+    Nkeep ++;
+  }
+
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, keep, Nkeep);
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/uniphot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/uniphot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/uniphot.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "uniphot.h"
+
+int main (int argc, char **argv) {
+
+  int i, Ntgroup, Nsgroup, status;
+  Group *tgroup, *sgroup;
+  ImageLink *imlinks;
+  FITS_DB db;
+
+  /* get configuration info, args, lockfile */
+  initialize (argc, argv);
+
+  set_db (&db);
+  status = dvo_image_lock (&db, ImageCat, 60.0, (UPDATE ? LCK_XCLD : LCK_SOFT));
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+  if (db.dbstate == LCK_EMPTY) Shutdown ("ERROR: No images in catalog %s (1)", db.filename);
+  if (!UPDATE) dvo_image_unlock (&db); 
+
+  /* load images */
+  load_images (&db);
+  
+  /* filter image list by selection */
+  subset_images (&db);
+
+  tgroup = find_image_tgroups (&db, &imlinks, &Ntgroup);
+  sgroup = find_image_sgroups (&db, &imlinks, &Nsgroup);
+
+  /* determine fit values */
+  for (i = 0; i < NLOOP; i++) {
+
+    fit_tgroup (tgroup, Ntgroup);
+    fit_sgroup (sgroup, Nsgroup); 
+
+  }    
+
+  fprintf (stdout, "# uniphot results for filter %s\n", photcode[0].name);
+  fprintf (stdout, "# STATMODE: %s\n", STATMODE);
+  fprintf (stdout, "# NLOOP: %d\n", NLOOP);
+  fprintf (stdout, "# time groups : %d\n", Ntgroup);
+  for (i = 0; i < Ntgroup; i++) {
+    fprintf (stdout, "%s %5d %5d %7.4f  %7.4f %7.4f\n", tgroup[i].label, 
+	     tgroup[i].Nimage, tgroup[i].Ngood, 0.001*tgroup[i].M, 0.001*tgroup[i].dM, 0.001*tgroup[i].dMsub);
+  }
+  fprintf (stdout, "\n");
+
+  fprintf (stdout, "# space groups : %d\n", Nsgroup);
+  for (i = 0; i < Nsgroup; i++) {
+    fprintf (stdout, "%s %5d %5d %7.4f  %7.4f %7.4f\n", sgroup[i].label, 
+	     sgroup[i].Nimage, sgroup[i].Ngood, 0.001*sgroup[i].M, 0.001*sgroup[i].dM, 0.001*sgroup[i].dMsub);
+  }
+  if (!UPDATE) exit (0);
+
+  update (&db, sgroup, Nsgroup);
+  dvo_image_unlock (&db); 
+
+  exit (0);
+}
+
+/* add a mode to check / force consistency between image.Mcal and
+   measure.Mcal.
+
+   use the method in delstar to match image with measure (gregions (image), etc)
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/update.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/update.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/update.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "uniphot.h"
+# include <glob.h>
+
+void update (FITS_DB *db, Group *sgroup, int Nsgroup) {
+
+  int i, j, status, Nmin, Nimage;
+  char line[256];
+  glob_t pglob;
+  double Rmin, Rmax, Dmin, Dmax, x, y, radius;
+  Image *image;
+  Catalog catalog;
+  Coords coords;
+
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  /* clear the NOCAL flags */
+  for (i = 0; i < Nimage; i++) {
+    image[i].code &= ~ID_IMAGE_NOCAL;
+  }
+
+  /* apply calculated space-group offset to image Mcal values */
+  for (i = 0; i < Nsgroup; i++) {
+    for (j = 0; j < sgroup[i].Nimage; j++) {
+      sgroup[i].image[j][0].Mcal -= sgroup[i].M;
+    }
+  }
+
+  /** write image table **/
+  dvo_image_update (db, VERBOSE);
+
+  // XXX this process uses the existence of the file to perform the update
+  // XXX convert this to an examination of the SkyTable
+  /** update catalog tables **/
+  pglob.gl_offs = 0;
+  sprintf (line, "%s/*/*.cpt", CATDIR);
+  status = glob (line, 0, NULL, &pglob);
+
+  coords.crpix1 = coords.crpix2 = 0.0;
+  coords.cdelt1 = coords.cdelt2 = 1.0; /* scale is degrees, radius in degrees */
+  coords.pc1_1 = coords.pc2_2 = 1.0;
+  coords.pc1_2 = coords.pc2_1 = 0.0;
+  strcpy (coords.ctype, "RA---TAN");
+  
+  for (i = 0; i < pglob.gl_pathc; i++) {
+    catalog.filename = pglob.gl_pathv[i];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, VERBOSE, "a")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    if (!catalog.Naves_disk) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    gfits_scan (&catalog.header, "RA0", "%lf",  1, &Rmin);
+    gfits_scan (&catalog.header, "RA1", "%lf",  1, &Rmax);
+    gfits_scan (&catalog.header, "DEC0", "%lf", 1, &Dmin);
+    gfits_scan (&catalog.header, "DEC1", "%lf", 1, &Dmax);
+    while (Rmin <   0.0) { Rmin += 360.0; }
+    while (Rmin > 360.0) { Rmin -= 360.0; }
+    while (Rmax <   0.0) { Rmax += 360.0; }
+    while (Rmax > 360.0) { Rmax -= 360.0; }
+
+    coords.crval1 = 0.5*(Rmin + Rmax);
+    coords.crval2 = 0.5*(Dmin + Dmax);
+
+    Nmin = 0;
+    Rmin = 1000;
+    /* primitive version: match catalog with closest sgroup */
+    for (j = 0; j < Nsgroup; j++) {
+      if (!RD_to_XY (&x, &y, sgroup[j].v1, sgroup[j].v2, &coords)) continue;
+      radius = hypot (x, y);
+      if ((j == 0) || (radius < Rmin)) {
+	Rmin = radius;
+	Nmin = j;
+      }
+    }
+
+    fprintf (stderr, "catalog: %s sgroup: %d %s %f\n", catalog.filename, Nmin, sgroup[Nmin].label, Rmin);
+    update_dvo_catalog (&catalog, &sgroup[Nmin], (Rmin > 2*RADIUS)); 
+    dvo_catalog_save (&catalog, VERBOSE);
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+}      
+
+/* loop over all average 
+
+   if (source equiv photcode)
+
+   loop over sgroups
+
+   if (radius < RADIUS) 
+      
+   average.M (+/-) source.M
+*/
+
+/* loop over all measure 
+
+   if (source equiv photcode)
+
+   loop over sgroups
+
+   if (radius < RADIUS) 
+      
+   measure.Mcal (+/-) source.M
+*/
+
+/* alternative (slower, more robust)
+
+   loop over all average
+   loop over all average.Nm
+   select measure with source equiv photcode
+   match measure to image, get Mcal & offset
+   if any offset for average[i] is different, give error
+   apply offset to average.M
+*/
Index: /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/update_catalog.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/update_catalog.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/src/uniphot/src/update_catalog.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "uniphot.h"
+
+void update_dvo_catalog (Catalog *catalog, Group *sgroup, int warn) {
+
+  int i, j, m, found;
+  int Nsec, Nsecfilt;
+  PhotCode *code;
+
+  Nsec = GetPhotcodeNsec (photcode[0].code);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  found = 0;    
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    
+    if (!isnan(catalog[0].secfilt[i*Nsecfilt+Nsec].M)) {
+      catalog[0].secfilt[i*Nsecfilt+Nsec].M += sgroup[0].M;
+    }
+
+    m = catalog[0].average[i].measureOffset;
+    for (j = 0; j < catalog[0].average[i].Nmeasure; j++, m++) {
+      code = GetPhotcodebyCode (catalog[0].measure[m].photcode);
+      if (code == NULL) continue;
+      if (code[0].type != PHOT_DEP) continue;
+      if (code[0].equiv != photcode[0].code) continue;
+      catalog[0].measure[m].Mcal -= sgroup[0].M;
+      found ++;
+    }
+  }
+
+  if (found) {
+    fprintf (stderr, "found %d matches\n", found);
+    if (warn) fprintf (stderr, "warning: updated values, perhaps out of range?\n");
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/README	(revision 22322)
@@ -0,0 +1,13 @@
+
+This directory contains DVO/addstar tests.  DVO/addstar has a large
+number of modes and options which must be tested.  Each directory
+defines tests for a major addstar mode.  The tests are also divided
+into those using the stand-alone version of addstar (TEST.sa) and
+those using the client/server version (TEST.cs).  There are also a set
+of tests using the unthreaded client/server setup (TEST.cu).  
+
+refcat:  add stars to a database from the reference catalog
+reflist: add stars to a database from an ascii list
+image: addstar to a database from an image
+fakeimage: add simulated images to the database
+
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+catdir.*
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/Makefile	(revision 22322)
@@ -0,0 +1,245 @@
+
+DVOCHECK = ./dvocheck
+
+all: megacam asca
+
+asca: asca.raw asca.mef asca.split
+
+asca.raw: asca.loneos.raw asca.elixir.raw asca.pmtest.raw asca.panstarrs.raw
+asca.mef: asca.loneos.mef asca.elixir.mef asca.pmtest.mef asca.panstarrs.mef
+asca.split: asca.loneos.split asca.elixir.split asca.pmtest.split asca.panstarrs.split
+
+######
+asca.L1:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	$(DVOCHECK) catdir.asca.L1.raw asca
+#
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	$(DVOCHECK) catdir.asca.L1.mef asca
+#
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	$(DVOCHECK) catdir.asca.L1.split asca
+#
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.update -D CATFORMAT ELIXIR -D CATMODE SPLIT -update -missed -closest
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.update -D CATFORMAT ELIXIR -D CATMODE SPLIT -update -missed -closest
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L1.update -D CATFORMAT ELIXIR -D CATMODE SPLIT -update -missed -closest
+	addstar -resort -D CATDIR catdir.asca.L1.update
+	$(DVOCHECK) catdir.asca.L1.update asca
+
+######
+asca.L2:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	$(DVOCHECK) catdir.asca.L2.raw asca
+#
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	$(DVOCHECK) catdir.asca.L2.mef asca
+#
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	$(DVOCHECK) catdir.asca.L2.split asca
+#
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.update -D CATFORMAT ELIXIR -D CATMODE SPLIT -update -missed -closest
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.update -D CATFORMAT ELIXIR -D CATMODE SPLIT -update -missed -closest
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 2 -D CATDIR catdir.asca.L2.update -D CATFORMAT ELIXIR -D CATMODE SPLIT -update -missed -closest
+	addstar -resort -D CATDIR catdir.asca.L2.update
+	$(DVOCHECK) catdir.asca.L2.update asca
+
+######
+asca.L3:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	$(DVOCHECK) catdir.asca.L3.raw asca
+#
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	$(DVOCHECK) catdir.asca.L3.mef asca
+#
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	$(DVOCHECK) catdir.asca.L3.split asca
+#
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.update -D CATFORMAT ELIXIR -D CATMODE SPLIT -update -missed -closest
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.update -D CATFORMAT ELIXIR -D CATMODE SPLIT -update -missed -closest
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D SKY_DEPTH 3 -D CATDIR catdir.asca.L3.update -D CATFORMAT ELIXIR -D CATMODE SPLIT -update -missed -closest
+	addstar -resort -D CATDIR catdir.asca.L3.update
+	$(DVOCHECK) catdir.asca.L3.update asca
+
+######
+asca.loneos.raw:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.loneos.raw -D CATFORMAT LONEOS -D CATMODE RAW
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.loneos.raw -D CATFORMAT LONEOS -D CATMODE RAW
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.loneos.raw -D CATFORMAT LONEOS -D CATMODE RAW
+	$(DVOCHECK) catdir.asca.loneos.raw asca
+
+asca.elixir.raw:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.elixir.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.elixir.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.elixir.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	$(DVOCHECK) catdir.asca.elixir.raw asca
+
+asca.pmtest.raw:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.pmtest.raw -D CATFORMAT PMTEST -D CATMODE RAW
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.pmtest.raw -D CATFORMAT PMTEST -D CATMODE RAW
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.pmtest.raw -D CATFORMAT PMTEST -D CATMODE RAW
+	$(DVOCHECK) catdir.asca.pmtest.raw asca
+
+asca.panstarrs.raw:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.panstarrs.raw -D CATFORMAT PANSTARRS -D CATMODE RAW
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.panstarrs.raw -D CATFORMAT PANSTARRS -D CATMODE RAW
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.panstarrs.raw -D CATFORMAT PANSTARRS -D CATMODE RAW
+	$(DVOCHECK) catdir.asca.panstarrs.raw asca
+
+#######
+asca.loneos.mef:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.loneos.mef -D CATFORMAT LONEOS -D CATMODE MEF
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.loneos.mef -D CATFORMAT LONEOS -D CATMODE MEF
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.loneos.mef -D CATFORMAT LONEOS -D CATMODE MEF
+	$(DVOCHECK) catdir.asca.loneos.mef asca
+
+asca.elixir.mef:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.elixir.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.elixir.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.elixir.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	$(DVOCHECK) catdir.asca.elixir.mef asca
+
+asca.pmtest.mef:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.pmtest.mef -D CATFORMAT PMTEST -D CATMODE MEF
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.pmtest.mef -D CATFORMAT PMTEST -D CATMODE MEF
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.pmtest.mef -D CATFORMAT PMTEST -D CATMODE MEF
+	$(DVOCHECK) catdir.asca.pmtest.mef asca
+
+asca.panstarrs.mef:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.panstarrs.mef -D CATFORMAT PANSTARRS -D CATMODE MEF
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.panstarrs.mef -D CATFORMAT PANSTARRS -D CATMODE MEF
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.panstarrs.mef -D CATFORMAT PANSTARRS -D CATMODE MEF
+	$(DVOCHECK) catdir.asca.panstarrs.mef asca
+
+#######
+asca.loneos.split:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.loneos.split -D CATFORMAT LONEOS -D CATMODE SPLIT
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.loneos.split -D CATFORMAT LONEOS -D CATMODE SPLIT
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.loneos.split -D CATFORMAT LONEOS -D CATMODE SPLIT
+	$(DVOCHECK) catdir.asca.loneos.split asca
+
+asca.elixir.split:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.elixir.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.elixir.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.elixir.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	$(DVOCHECK) catdir.asca.elixir.split asca
+
+asca.pmtest.split:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.pmtest.split -D CATFORMAT PMTEST -D CATMODE SPLIT
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.pmtest.split -D CATFORMAT PMTEST -D CATMODE SPLIT
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.pmtest.split -D CATFORMAT PMTEST -D CATMODE SPLIT
+	$(DVOCHECK) catdir.asca.pmtest.split asca
+
+asca.panstarrs.split:
+	addstar asca/to_r20060121ut005757s32340.smp -p ASCA_RED -D CATDIR catdir.asca.panstarrs.split -D CATFORMAT PANSTARRS -D CATMODE SPLIT
+	addstar asca/to_r20060121ut015747s35940.smp -p ASCA_RED -D CATDIR catdir.asca.panstarrs.split -D CATFORMAT PANSTARRS -D CATMODE SPLIT
+	addstar asca/to_r20060121ut025738s39540.smp -p ASCA_RED -D CATDIR catdir.asca.panstarrs.split -D CATFORMAT PANSTARRS -D CATMODE SPLIT
+	$(DVOCHECK) catdir.asca.panstarrs.split asca
+
+# megacam images:
+# add in both FITS and RAW versions of output data
+# 'best' and 'ap25' are from Sextractor / imclean
+# 'ps10' is from psphot direct
+
+megacam: megacam.raw megacam.mef megacam.split
+
+megacam.raw: megacam.loneos.raw megacam.elixir.raw megacam.pmtest.raw megacam.panstarrs.raw
+megacam.mef: megacam.loneos.mef megacam.elixir.mef megacam.pmtest.mef megacam.panstarrs.mef
+megacam.split: megacam.loneos.split megacam.elixir.split megacam.pmtest.split megacam.panstarrs.split
+
+#######
+megacam.loneos.raw:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.loneos.raw -D CATFORMAT LONEOS -D CATMODE RAW
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.loneos.raw -D CATFORMAT LONEOS -D CATMODE RAW
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.loneos.raw -D CATFORMAT LONEOS -D CATMODE RAW
+	$(DVOCHECK) catdir.megacam.loneos.raw megacam
+
+megacam.elixir.raw:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.elixir.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.elixir.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.elixir.raw -D CATFORMAT ELIXIR -D CATMODE RAW
+	$(DVOCHECK) catdir.megacam.elixir.raw megacam
+
+megacam.pmtest.raw:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.pmtest.raw -D CATFORMAT PMTEST -D CATMODE RAW
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.pmtest.raw -D CATFORMAT PMTEST -D CATMODE RAW
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.pmtest.raw -D CATFORMAT PMTEST -D CATMODE RAW
+	$(DVOCHECK) catdir.megacam.pmtest.raw megacam
+
+megacam.panstarrs.raw:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.panstarrs.raw -D CATFORMAT PANSTARRS -D CATMODE RAW
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.panstarrs.raw -D CATFORMAT PANSTARRS -D CATMODE RAW
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.panstarrs.raw -D CATFORMAT PANSTARRS -D CATMODE RAW
+	$(DVOCHECK) catdir.megacam.panstarrs.raw megacam
+
+#######
+megacam.loneos.mef:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.loneos.mef -D CATFORMAT LONEOS -D CATMODE MEF
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.loneos.mef -D CATFORMAT LONEOS -D CATMODE MEF
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.loneos.mef -D CATFORMAT LONEOS -D CATMODE MEF
+	$(DVOCHECK) catdir.megacam.loneos.mef megacam
+
+megacam.elixir.mef:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.elixir.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.elixir.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.elixir.mef -D CATFORMAT ELIXIR -D CATMODE MEF
+	$(DVOCHECK) catdir.megacam.elixir.mef megacam
+
+megacam.pmtest.mef:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.pmtest.mef -D CATFORMAT PMTEST -D CATMODE MEF
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.pmtest.mef -D CATFORMAT PMTEST -D CATMODE MEF
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.pmtest.mef -D CATFORMAT PMTEST -D CATMODE MEF
+	$(DVOCHECK) catdir.megacam.pmtest.mef megacam
+
+megacam.panstarrs.mef:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.panstarrs.mef -D CATFORMAT PANSTARRS -D CATMODE MEF
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.panstarrs.mef -D CATFORMAT PANSTARRS -D CATMODE MEF
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.panstarrs.mef -D CATFORMAT PANSTARRS -D CATMODE MEF
+	$(DVOCHECK) catdir.megacam.panstarrs.mef megacam
+
+#######
+megacam.loneos.split:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.loneos.split -D CATFORMAT LONEOS -D CATMODE SPLIT
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.loneos.split -D CATFORMAT LONEOS -D CATMODE SPLIT
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.loneos.split -D CATFORMAT LONEOS -D CATMODE SPLIT
+	$(DVOCHECK) catdir.megacam.loneos.split megacam
+
+megacam.elixir.split:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.elixir.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.elixir.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.elixir.split -D CATFORMAT ELIXIR -D CATMODE SPLIT
+	$(DVOCHECK) catdir.megacam.elixir.split megacam
+
+megacam.pmtest.split:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.pmtest.split -D CATFORMAT PMTEST -D CATMODE SPLIT
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.pmtest.split -D CATFORMAT PMTEST -D CATMODE SPLIT
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.pmtest.split -D CATFORMAT PMTEST -D CATMODE SPLIT
+	$(DVOCHECK) catdir.megacam.pmtest.split megacam
+
+megacam.panstarrs.split:
+	addstar megacam/720357o13.best.smp -D CATDIR catdir.megacam.panstarrs.split -D CATFORMAT PANSTARRS -D CATMODE SPLIT
+	addstar megacam/720357o13.ap25.smp -D CATDIR catdir.megacam.panstarrs.split -D CATFORMAT PANSTARRS -D CATMODE SPLIT
+	addstar megacam/720357o13.ps10.cmp -D CATDIR catdir.megacam.panstarrs.split -D CATFORMAT PANSTARRS -D CATMODE SPLIT
+	$(DVOCHECK) catdir.megacam.panstarrs.split megacam
+
+#######
+
+clean:
+	rm -rf catdir.*
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/dvocheck
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/dvocheck	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/dvocheck	(revision 22322)
@@ -0,0 +1,29 @@
+#!/usr/bin/env dvo
+
+if ($argv:n != 2) 
+  echo "USAGE: validate (catdir) (mode)"
+  echo $argv:n
+  exit 2
+end
+
+catdir $argv:0
+$KAPA = kapa -nomap
+
+if ("$argv:1" == "megacam") 
+  region 300.0 10.18 0.3
+end
+
+if ("$argv:1" == "asca")
+  resize -g 800 420
+  usleep 200000
+  region 60 0 90 ait
+end
+
+mextract ra
+if (ra[] == 0)
+  echo "no stars found for $1"
+  exit 1
+end
+
+echo "found ra[] stars"
+exit 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/photcode.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/photcode.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/photcode.txt	(revision 22322)
@@ -0,0 +1,75 @@
+# this file contains the list of valid photometry codes.
+# there should be one code for each combination of CCD, filter, and
+# telescope (and possibly additional ones if filter transmission
+# reduces with time).  The code on each line is a unique number, while
+# the name is a representative name.  The type defines how the
+# photometry is treated.  There can only be one primary photcode.  There
+# should only be a few secondary photcodes: these are the calibrated
+# values for stars.  The dependent photcodes are those which are
+# equivalent to a primary or secondary photcode, and are equated after
+# photometric solutions.  Reference photcodes are for anything without
+# a dynamic solution: the reference values are provided externally.
+
+# a bogosity in scatfit: it assumes primary photcodes are those which
+# N % 50 == 0
+
+# I discovered that the standards star analysis and the photreg
+# process are inconsistent.  the standard star analysis was using the
+# per-chip zero-points while photreg was using the per-filter
+# zero-points.  these should have been the same, but were not.  See
+# cfh12.photcode.original for the version with this error.  This file
+# now contains consistent entries
+
+# code name		type  zero    airmass  offset   c1   c2   slope  <color> equivalence
+#  1    B		pri   26.017  -0.15     -       1023 1024 0.016     -      1
+#  2    V		sec   26.220  -0.12     -       1023 1024 0.008     -      2
+#  3    R		sec   26.190  -0.09     -       1024 1025 0.028     -      3
+#  4    I		sec   26.185  -0.04     -       1025 1027 0.107     -      4
+
+#  1    B		pri   26.017  -0.15     -       1003 1004 0.016     -      1
+#  2    V		sec   26.220  -0.12     -       1003 1004 0.008     -      2
+#  3    R		sec   26.190  -0.09     -       1004 1005 0.028     -      3
+#  4    I		sec   26.185  -0.04     -       1005 1007 0.107     -      4
+
+  1    B		pri   26.000  -0.15     -       1003 1004 0.016     -      1
+  2    V		sec   26.150  -0.12     -       1003 1004 0.008     -      2
+  3    R		sec   26.190  -0.09     -       1003 1004 0.028     -      3
+  4    I		sec   26.135  -0.04     -       1003 1004 0.107     -      4
+  5    Z		sec   25.830  -0.03     -       -    -    -         -      5
+
+  1000 USNO_BLUE	ref   -     -        -       -  -   -      -        -
+  1001 USNO_RED		ref   -     -        -       -  -   -      -        -
+
+    50 ASCA_RED    	dep  10.000 0.0      -       -  -   -      -        3
+    51 ASCA_BLUE    	dep  10.000 0.0      -       -  -   -      -        3
+
+  1002 U_L92		ref   -     -        -       -  -   -      -        -
+  1003 B_L92		ref   -     -        -       -  -   -      -        -
+  1004 V_L92		ref   -     -        -       -  -   -      -        -
+  1005 R_L92		ref   -     -        -       -  -   -      -        -
+  1007 I_L92		ref   -     -        -       -  -   -      -        -
+
+  1022 U_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1023 B_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1024 V_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1025 R_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1027 I_L92_HQ		ref   -     -        -       -  -   -      -        -
+
+  1012 U_STET		ref   -     -        -       -  -   -      -        -
+  1013 B_STET		ref   -     -        -       -  -   -      -        -
+  1014 V_STET		ref   -     -        -       -  -   -      -        -
+  1015 R_STET		ref   -     -        -       -  -   -      -        -
+  1017 I_STET		ref   -     -        -       -  -   -      -        -
+
+  1100 GSC_M    	ref   -     -        -       -  -   -      -        -
+  1101 SAO		ref   -     -        -       -  -   -      -        -
+
+  1200 TYCHO_B		ref   -     -        -       -  -   -      -        -
+  1201 TYCHO_V		ref   -     -        -       -  -   -      -        -
+
+  2011 2MASS_J          ref   -     -        -       -  -   -      -        -
+  2012 2MASS_H          ref   -     -        -       -  -   -      -        -
+  2013 2MASS_K          ref   -     -        -       -  -   -      -        -
+
+  100  CFH12K.B.00	dep   26.000 -0.15     -       -  -   -      -        1
+  101  MEGACAM.r.13	dep   26.000 -0.15     -       -  -   -      -        1
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/ptolemy.rc
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/ptolemy.rc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/image.sa/ptolemy.rc	(revision 22322)
@@ -0,0 +1,23 @@
+# .ptolemyrc
+ 
+REFSDIR                 ../../refs
+
+# asca camera settings
+UT-KEYWORD              UTSHUT
+DATE-KEYWORD            DATE
+DATE-MODE               yyyy-mm-dd
+
+# matching radius (arcseconds)
+# RADIUS  1.0  (old name, deprecated)
+ADDSTAR_RADIUS  1.0
+
+GSCFILE $REFSDIR/GSCregions.tbl
+CATDIR catdir
+PHOTCODE_FILE photcode.txt
+
+USNO_A_DIR /data/elixir/srcdir/refs/usno
+USNO_B_DIR /data/elixir/srcdir/refs/usnob
+
+ZERO_PT 25.0
+
+GSCDIR /data/elixir/srcdir/refs/gsc
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+catdir.*
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/Makefile	(revision 22322)
@@ -0,0 +1,91 @@
+
+REG = -region 0 1 0 1
+
+usno: usno.photcodes usno.formats usno.depths
+usno.depths:  usno.depths.raw  usno.depths.mef  usno.depths.split
+usno.formats: usno.formats.raw usno.formats.mef usno.formats.split
+
+gsc:
+	addstar -v -cat GSC $(REG) -D CATDIR catdir.gsc
+	dvocheck catdir.gsc gsc
+
+2mass:
+	addstar -cat 2MASS $(REG) -D CATDIR catdir.2mass -photcode 2MASS_J
+	addstar -cat 2MASS $(REG) -D CATDIR catdir.2mass -photcode 2MASS_H
+	addstar -cat 2MASS $(REG) -D CATDIR catdir.2mass -photcode 2MASS_K
+	dvocheck catdir.2mass 2mass
+
+tycho:
+	addstar -cat TYCHO $(REG) -D CATDIR catdir.tycho -photcode TYCHO_B
+	addstar -cat TYCHO $(REG) -D CATDIR catdir.tycho -photcode TYCHO_V
+	dvocheck catdir.tycho tycho
+
+usnob:
+	addstar -cat USNOB $(REG) -D CATDIR catdir.usnob -photcode USNO_RED
+	addstar -cat USNOB $(REG) -D CATDIR catdir.usnob -photcode USNO_BLUE
+	dvocheck catdir.usnob usnob
+
+usno.photcodes:
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno -photcode USNO_RED
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno -photcode USNO_BLUE
+	dvocheck catdir.usno usno
+
+usno.formats.raw:
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.raw.loneos    -photcode USNO_BLUE -D CATFORMAT LONEOS
+	dvocheck catdir.usno.raw.loneos usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.raw.elixir    -photcode USNO_BLUE -D CATFORMAT ELIXIR
+	dvocheck catdir.usno.raw.elixir usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.raw.pmtest    -photcode USNO_BLUE -D CATFORMAT PMTEST
+	dvocheck catdir.usno.raw.pmtest usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.raw.panstarrs -photcode USNO_BLUE -D CATFORMAT PANSTARRS
+	dvocheck catdir.usno.raw.panstarrs usno
+
+usno.formats.mef:
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.mef.loneos    -photcode USNO_BLUE -D CATMODE MEF -D CATFORMAT LONEOS 
+	dvocheck catdir.usno.mef.loneos usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.mef.elixir    -photcode USNO_BLUE -D CATMODE MEF -D CATFORMAT ELIXIR
+	dvocheck catdir.usno.mef.elixir usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.mef.pmtest    -photcode USNO_BLUE -D CATMODE MEF -D CATFORMAT PMTEST
+	dvocheck catdir.usno.mef.pmtest usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.mef.panstarrs -photcode USNO_BLUE -D CATMODE MEF -D CATFORMAT PANSTARRS
+	dvocheck catdir.usno.mef.panstarrs usno
+
+usno.formats.split:
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.split.loneos    -photcode USNO_BLUE -D CATMODE SPLIT -D CATFORMAT LONEOS
+	dvocheck catdir.usno.split.loneos usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.split.elixir    -photcode USNO_BLUE -D CATMODE SPLIT -D CATFORMAT ELIXIR
+	dvocheck catdir.usno.split.elixir usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.split.pmtest    -photcode USNO_BLUE -D CATMODE SPLIT -D CATFORMAT PMTEST
+	dvocheck catdir.usno.split.pmtest usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.split.panstarrs -photcode USNO_BLUE -D CATMODE SPLIT -D CATFORMAT PANSTARRS
+	dvocheck catdir.usno.split.panstarrs usno
+
+usno.depths.raw:
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.raw.L2 -photcode USNO_BLUE -D SKY_DEPTH 2 -D CATMODE RAW
+	dvocheck catdir.usno.raw.L2 usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.raw.L3 -photcode USNO_BLUE -D SKY_DEPTH 3 -D CATMODE RAW
+	dvocheck catdir.usno.raw.L3 usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.raw.L4 -photcode USNO_BLUE -D SKY_DEPTH 4 -D CATMODE RAW
+	dvocheck catdir.usno.raw.L4 usno
+
+usno.depths.mef:
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.mef.L2 -photcode USNO_BLUE -D SKY_DEPTH 2 -D CATMODE MEF
+	dvocheck catdir.usno.mef.L2 usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.mef.L3 -photcode USNO_BLUE -D SKY_DEPTH 3 -D CATMODE MEF
+	dvocheck catdir.usno.mef.L3 usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.mef.L4 -photcode USNO_BLUE -D SKY_DEPTH 4 -D CATMODE MEF
+	dvocheck catdir.usno.mef.L4 usno
+
+usno.depths.split:
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.split.L2 -photcode USNO_BLUE -D SKY_DEPTH 2 -D CATMODE SPLIT
+	dvocheck catdir.usno.split.L2 usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.split.L3 -photcode USNO_BLUE -D SKY_DEPTH 3 -D CATMODE SPLIT
+	dvocheck catdir.usno.split.L3 usno
+	addstar -cat USNO $(REG) -D CATDIR catdir.usno.split.L4 -photcode USNO_BLUE -D SKY_DEPTH 4 -D CATMODE SPLIT
+	dvocheck catdir.usno.split.L4 usno
+
+
+#####
+
+clean:
+	rm -rf catdir.*
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/dvocheck
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/dvocheck	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/dvocheck	(revision 22322)
@@ -0,0 +1,29 @@
+#!/usr/bin/env dvo
+
+if ($argv:n != 2) 
+  echo "USAGE: validate (catdir) (mode)"
+  echo $argv:n
+  exit 2
+end
+
+catdir $argv:0
+$KAPA = kapa -nomap
+
+if ("$argv:1" == "gsc") 
+  region 0.5 0.5 0.5
+end
+
+if ("$argv:1" == "asca")
+  resize -g 800 420
+  usleep 200000
+  region 60 0 90 ait
+end
+
+mextract ra
+if (ra[] == 0)
+  echo "no stars found for $1"
+  exit 1
+end
+
+echo "found ra[] stars"
+exit 0
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/photcode.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/photcode.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/photcode.txt	(revision 22322)
@@ -0,0 +1,225 @@
+# this file contains the list of valid photometry codes.
+# there should be one code for each combination of CCD, filter, and
+# telescope (and possibly additional ones if filter transmission
+# reduces with time).  The code on each line is a unique number, while
+# the name is a representative name.  The type defines how the
+# photometry is treated.  There can only be one primary photcode.  There
+# should only be a few secondary photcodes: these are the calibrated
+# values for stars.  The dependent photcodes are those which are
+# equivalent to a primary or secondary photcode, and are equated after
+# photometric solutions.  Reference photcodes are for anything without
+# a dynamic solution: the reference values are provided externally.
+
+# a bogosity in scatfit: it assumes primary photcodes are those which
+# N % 50 == 0
+
+# I discovered that the standards star analysis and the photreg
+# process are inconsistent.  the standard star analysis was using the
+# per-chip zero-points while photreg was using the per-filter
+# zero-points.  these should have been the same, but were not.  See
+# cfh12.photcode.original for the version with this error.  This file
+# now contains consistent entries
+
+# code name		type  zero    airmass  offset   c1   c2   slope  <color> equivalence
+#  1    B		pri   26.017  -0.15     -       1023 1024 0.016     -      1
+#  2    V		sec   26.220  -0.12     -       1023 1024 0.008     -      2
+#  3    R		sec   26.190  -0.09     -       1024 1025 0.028     -      3
+#  4    I		sec   26.185  -0.04     -       1025 1027 0.107     -      4
+
+#  1    B		pri   26.017  -0.15     -       1003 1004 0.016     -      1
+#  2    V		sec   26.220  -0.12     -       1003 1004 0.008     -      2
+#  3    R		sec   26.190  -0.09     -       1004 1005 0.028     -      3
+#  4    I		sec   26.185  -0.04     -       1005 1007 0.107     -      4
+
+  1    B		pri   26.000  -0.15     -       1003 1004 0.016     -      1
+  2    V		sec   26.150  -0.12     -       1003 1004 0.008     -      2
+  3    R		sec   26.190  -0.09     -       1003 1004 0.028     -      3
+  4    I		sec   26.135  -0.04     -       1003 1004 0.107     -      4
+  5    Z		sec   25.830  -0.03     -       -    -    -         -      5
+
+  1000 USNO_BLUE	ref   -     -        -       -  -   -      -        -
+  1001 USNO_RED		ref   -     -        -       -  -   -      -        -
+
+    50 ASCA_RED    	dep  10.000 0.0      -       -  -   -      -        3
+
+  1002 U_L92		ref   -     -        -       -  -   -      -        -
+  1003 B_L92		ref   -     -        -       -  -   -      -        -
+  1004 V_L92		ref   -     -        -       -  -   -      -        -
+  1005 R_L92		ref   -     -        -       -  -   -      -        -
+  1007 I_L92		ref   -     -        -       -  -   -      -        -
+
+  1022 U_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1023 B_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1024 V_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1025 R_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1027 I_L92_HQ		ref   -     -        -       -  -   -      -        -
+
+  1012 U_STET		ref   -     -        -       -  -   -      -        -
+  1013 B_STET		ref   -     -        -       -  -   -      -        -
+  1014 V_STET		ref   -     -        -       -  -   -      -        -
+  1015 R_STET		ref   -     -        -       -  -   -      -        -
+  1017 I_STET		ref   -     -        -       -  -   -      -        -
+
+  1100 GSC_M    	ref   -     -        -       -  -   -      -        -
+  1101 SAO		ref   -     -        -       -  -   -      -        -
+
+  1200 TYCHO_B		ref   -     -        -       -  -   -      -        -
+  1201 TYCHO_V		ref   -     -        -       -  -   -      -        -
+
+  2011 2MASS_J          ref   -     -        -       -  -   -      -        -
+  2012 2MASS_H          ref   -     -        -       -  -   -      -        -
+  2013 2MASS_K          ref   -     -        -       -  -   -      -        -
+
+  100  CFH12K.B.00	dep   26.000 -0.15     -       -  -   -      -        1
+  101  CFH12K.B.01	dep   26.000 -0.15     -       -  -   -      -        1
+  102  CFH12K.B.02	dep   26.000 -0.15     -       -  -   -      -        1
+  103  CFH12K.B.03	dep   26.000 -0.15     -       -  -   -      -        1
+  104  CFH12K.B.04	dep   26.000 -0.15     -       -  -   -      -        1
+  105  CFH12K.B.05	dep   26.000 -0.15     -       -  -   -      -        1
+  106  CFH12K.B.06	dep   26.000 -0.15     -       -  -   -      -        1
+  107  CFH12K.B.07	dep   26.000 -0.15     -       -  -   -      -        1
+  108  CFH12K.B.08	dep   26.000 -0.15     -       -  -   -      -        1
+  109  CFH12K.B.09	dep   26.000 -0.15     -       -  -   -      -        1
+  110  CFH12K.B.10	dep   26.000 -0.15     -       -  -   -      -        1
+  111  CFH12K.B.11	dep   26.000 -0.15     -       -  -   -      -        1
+
+  200  CFH12K.V.00	dep   26.150 -0.12     -       -  -   -      -        2
+  201  CFH12K.V.01	dep   26.150 -0.12     -       -  -   -      -        2
+  202  CFH12K.V.02	dep   26.150 -0.12     -       -  -   -      -        2
+  203  CFH12K.V.03	dep   26.150 -0.12     -       -  -   -      -        2
+  204  CFH12K.V.04	dep   26.150 -0.12     -       -  -   -      -        2
+  205  CFH12K.V.05	dep   26.150 -0.12     -       -  -   -      -        2
+  206  CFH12K.V.06	dep   26.150 -0.12     -       -  -   -      -        2
+  207  CFH12K.V.07	dep   26.150 -0.12     -       -  -   -      -        2
+  208  CFH12K.V.08	dep   26.150 -0.12     -       -  -   -      -        2
+  209  CFH12K.V.09	dep   26.150 -0.12     -       -  -   -      -        2
+  210  CFH12K.V.10	dep   26.150 -0.12     -       -  -   -      -        2
+  211  CFH12K.V.11	dep   26.150 -0.12     -       -  -   -      -        2
+
+# was 26.190
+  300  CFH12K.R.00	dep   26.190 -0.09     -       -  -   -      -        3
+  301  CFH12K.R.01	dep   26.190 -0.09     -       -  -   -      -        3
+  302  CFH12K.R.02	dep   26.190 -0.09     -       -  -   -      -        3
+  303  CFH12K.R.03	dep   26.190 -0.09     -       -  -   -      -        3
+  304  CFH12K.R.04	dep   26.190 -0.09     -       -  -   -      -        3
+  305  CFH12K.R.05	dep   26.190 -0.09     -       -  -   -      -        3
+  306  CFH12K.R.06	dep   26.190 -0.09     -       -  -   -      -        3
+  307  CFH12K.R.07	dep   26.190 -0.09     -       -  -   -      -        3
+  308  CFH12K.R.08	dep   26.190 -0.09     -       -  -   -      -        3
+  309  CFH12K.R.09	dep   26.190 -0.09     -       -  -   -      -        3
+  310  CFH12K.R.10	dep   26.190 -0.09     -       -  -   -      -        3
+  311  CFH12K.R.11	dep   26.190 -0.09     -       -  -   -      -        3
+
+  400  CFH12K.I.00	dep   26.135 -0.04     -       -  -   -      -        4
+  401  CFH12K.I.01	dep   26.135 -0.04     -       -  -   -      -        4
+  402  CFH12K.I.02	dep   26.135 -0.04     -       -  -   -      -        4
+  403  CFH12K.I.03	dep   26.135 -0.04     -       -  -   -      -        4
+  404  CFH12K.I.04	dep   26.135 -0.04     -       -  -   -      -        4
+  405  CFH12K.I.05	dep   26.135 -0.04     -       -  -   -      -        4
+  406  CFH12K.I.06	dep   26.135 -0.04     -       -  -   -      -        4
+  407  CFH12K.I.07	dep   26.135 -0.04     -       -  -   -      -        4
+  408  CFH12K.I.08	dep   26.135 -0.04     -       -  -   -      -        4
+  409  CFH12K.I.09	dep   26.135 -0.04     -       -  -   -      -        4
+  410  CFH12K.I.10	dep   26.135 -0.04     -       -  -   -      -        4
+  411  CFH12K.I.11	dep   26.135 -0.04     -       -  -   -      -        4
+
+### This needs to be fixed: I am setting all filters which are not
+###   BVRI to point at photcode 5 (= Z) as a reference.  This is
+###   needed because the 
+
+  500  CFH12K.Z.00	dep   25.83 -0.03     -       -  -   -      -        5
+  501  CFH12K.Z.01	dep   25.83 -0.03     -       -  -   -      -        5
+  502  CFH12K.Z.02	dep   25.83 -0.03     -       -  -   -      -        5
+  503  CFH12K.Z.03	dep   25.83 -0.03     -       -  -   -      -        5
+  504  CFH12K.Z.04	dep   25.83 -0.03     -       -  -   -      -        5
+  505  CFH12K.Z.05	dep   25.83 -0.03     -       -  -   -      -        5
+  506  CFH12K.Z.06	dep   25.83 -0.03     -       -  -   -      -        5
+  507  CFH12K.Z.07	dep   25.83 -0.03     -       -  -   -      -        5
+  508  CFH12K.Z.08	dep   25.83 -0.03     -       -  -   -      -        5
+  509  CFH12K.Z.09	dep   25.83 -0.03     -       -  -   -      -        5
+  510  CFH12K.Z.10	dep   25.83 -0.03     -       -  -   -      -        5
+  511  CFH12K.Z.11	dep   25.83 -0.03     -       -  -   -      -        5
+
+  600  CFH12K.Ha.00	dep   25.000 0.04     -       -  -   -      -       5
+  601  CFH12K.Ha.01	dep   25.000 0.04     -       -  -   -      -       5 
+  602  CFH12K.Ha.02	dep   25.000 0.04     -       -  -   -      -       5 
+  603  CFH12K.Ha.03	dep   25.000 0.04     -       -  -   -      -       5 
+  604  CFH12K.Ha.04	dep   25.000 0.04     -       -  -   -      -       5 
+  605  CFH12K.Ha.05	dep   25.000 0.04     -       -  -   -      -       5 
+  606  CFH12K.Ha.06	dep   25.000 0.04     -       -  -   -      -       5 
+  607  CFH12K.Ha.07	dep   25.000 0.04     -       -  -   -      -       5 
+  608  CFH12K.Ha.08	dep   25.000 0.04     -       -  -   -      -       5 
+  609  CFH12K.Ha.09	dep   25.000 0.04     -       -  -   -      -       5 
+  610  CFH12K.Ha.10	dep   25.000 0.04     -       -  -   -      -       5 
+  611  CFH12K.Ha.11	dep   25.000 0.04     -       -  -   -      -       5 
+									     
+  700  CFH12K.HaOff.00	dep   25.000 0.04     -       -  -   -      -       5
+  701  CFH12K.HaOff.01	dep   25.000 0.04     -       -  -   -      -       5 
+  702  CFH12K.HaOff.02	dep   25.000 0.04     -       -  -   -      -       5 
+  703  CFH12K.HaOff.03	dep   25.000 0.04     -       -  -   -      -       5 
+  704  CFH12K.HaOff.04	dep   25.000 0.04     -       -  -   -      -       5 
+  705  CFH12K.HaOff.05	dep   25.000 0.04     -       -  -   -      -       5 
+  706  CFH12K.HaOff.06	dep   25.000 0.04     -       -  -   -      -       5 
+  707  CFH12K.HaOff.07	dep   25.000 0.04     -       -  -   -      -       5 
+  708  CFH12K.HaOff.08	dep   25.000 0.04     -       -  -   -      -       5 
+  709  CFH12K.HaOff.09	dep   25.000 0.04     -       -  -   -      -       5 
+  710  CFH12K.HaOff.10	dep   25.000 0.04     -       -  -   -      -       5 
+  711  CFH12K.HaOff.11	dep   25.000 0.04     -       -  -   -      -       5 
+
+  800  CFH12K.CN.00	dep   25.000 0.04     -       -  -   -      -       5
+  801  CFH12K.CN.01	dep   25.000 0.04     -       -  -   -      -       5 
+  802  CFH12K.CN.02	dep   25.000 0.04     -       -  -   -      -       5 
+  803  CFH12K.CN.03	dep   25.000 0.04     -       -  -   -      -       5 
+  804  CFH12K.CN.04	dep   25.000 0.04     -       -  -   -      -       5 
+  805  CFH12K.CN.05	dep   25.000 0.04     -       -  -   -      -       5 
+  806  CFH12K.CN.06	dep   25.000 0.04     -       -  -   -      -       5 
+  807  CFH12K.CN.07	dep   25.000 0.04     -       -  -   -      -       5 
+  808  CFH12K.CN.08	dep   25.000 0.04     -       -  -   -      -       5 
+  809  CFH12K.CN.09	dep   25.000 0.04     -       -  -   -      -       5 
+  810  CFH12K.CN.10	dep   25.000 0.04     -       -  -   -      -       5 
+  811  CFH12K.CN.11	dep   25.000 0.04     -       -  -   -      -       5 
+
+  900  CFH12K.TiO.00	dep   25.000 0.04     -       -  -   -      -       5
+  901  CFH12K.TiO.01	dep   25.000 0.04     -       -  -   -      -       5 
+  902  CFH12K.TiO.02	dep   25.000 0.04     -       -  -   -      -       5 
+  903  CFH12K.TiO.03	dep   25.000 0.04     -       -  -   -      -       5 
+  904  CFH12K.TiO.04	dep   25.000 0.04     -       -  -   -      -       5 
+  905  CFH12K.TiO.05	dep   25.000 0.04     -       -  -   -      -       5 
+  906  CFH12K.TiO.06	dep   25.000 0.04     -       -  -   -      -       5 
+  907  CFH12K.TiO.07	dep   25.000 0.04     -       -  -   -      -       5 
+  908  CFH12K.TiO.08	dep   25.000 0.04     -       -  -   -      -       5 
+  909  CFH12K.TiO.09	dep   25.000 0.04     -       -  -   -      -       5 
+  910  CFH12K.TiO.10	dep   25.000 0.04     -       -  -   -      -       5 
+  911  CFH12K.TiO.11	dep   25.000 0.04     -       -  -   -      -       5 
+
+  950  CFH12K.NB920.00	dep   25.000 0.04     -       -  -   -      -       5
+  951  CFH12K.NB920.01	dep   25.000 0.04     -       -  -   -      -       5 
+  952  CFH12K.NB920.02	dep   25.000 0.04     -       -  -   -      -       5 
+  953  CFH12K.NB920.03	dep   25.000 0.04     -       -  -   -      -       5 
+  954  CFH12K.NB920.04	dep   25.000 0.04     -       -  -   -      -       5 
+  955  CFH12K.NB920.05	dep   25.000 0.04     -       -  -   -      -       5 
+  956  CFH12K.NB920.06	dep   25.000 0.04     -       -  -   -      -       5 
+  957  CFH12K.NB920.07	dep   25.000 0.04     -       -  -   -      -       5 
+  958  CFH12K.NB920.08	dep   25.000 0.04     -       -  -   -      -       5 
+  959  CFH12K.NB920.09	dep   25.000 0.04     -       -  -   -      -       5 
+  960  CFH12K.NB920.10	dep   25.000 0.04     -       -  -   -      -       5 
+  961  CFH12K.NB920.11	dep   25.000 0.04     -       -  -   -      -       5 
+
+  970  CFH12K.B2F.00	dep   25.000 0.04     -       -  -   -      -       5
+  971  CFH12K.B2F.01	dep   25.000 0.04     -       -  -   -      -       5 
+  972  CFH12K.B2F.02	dep   25.000 0.04     -       -  -   -      -       5 
+  973  CFH12K.B2F.03	dep   25.000 0.04     -       -  -   -      -       5 
+  974  CFH12K.B2F.04	dep   25.000 0.04     -       -  -   -      -       5 
+  975  CFH12K.B2F.05	dep   25.000 0.04     -       -  -   -      -       5 
+  976  CFH12K.B2F.06	dep   25.000 0.04     -       -  -   -      -       5 
+  977  CFH12K.B2F.07	dep   25.000 0.04     -       -  -   -      -       5 
+  978  CFH12K.B2F.08	dep   25.000 0.04     -       -  -   -      -       5 
+  979  CFH12K.B2F.09	dep   25.000 0.04     -       -  -   -      -       5 
+  980  CFH12K.B2F.10	dep   25.000 0.04     -       -  -   -      -       5 
+  981  CFH12K.B2F.11	dep   25.000 0.04     -       -  -   -      -       5 
+
+  2000 U_BESS		dep   25.0  0.10     -       -  -   -      -        5
+  2001 B_BESS		dep   25.0  0.10     -       -  -   -      -        1
+  2002 V_BESS		dep   25.0  0.10     -       -  -   -      -        2
+  2003 R_BESS		dep   25.0  0.10     -       -  -   -      -        3
+  2004 I_BESS		dep   25.0  0.10     -       -  -   -      -        4
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/ptolemy.rc
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/ptolemy.rc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/addstar/refcat.sa/ptolemy.rc	(revision 22322)
@@ -0,0 +1,25 @@
+# .ptolemyrc
+ 
+REFDIR ../../refs
+
+# asca camera settings
+UT-KEYWORD              UTSHUT
+DATE-KEYWORD            DATE
+DATE-MODE               yyyy-mm-dd
+
+# matching radius (arcseconds)
+# RADIUS  1.0  (old name, deprecated)
+ADDSTAR_RADIUS  1.0
+
+GSCFILE $REFDIR/GSCregions.tbl
+CATDIR catdir
+PHOTCODE_FILE photcode.txt
+
+USNO_A_DIR /data/elixir/srcdir/refs/usno
+USNO_B_DIR /data/elixir/srcdir/refs/usnob
+
+ZERO_PT 25.0
+
+GSCDIR         /data/elixir/srcdir/refs/gsc
+2MASS_DIR_AS   /data/elixir/srcdir/refs/2mass.allsky
+2MASS_DIR_DR2  /data/elixir/srcdir/refs/2mass.dr2
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/photdbc/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/photdbc/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/photdbc/Makefile	(revision 22322)
@@ -0,0 +1,24 @@
+
+clean:
+	rm -rf catdir.output.copy
+	rm -rf catdir.output.clip
+	rm -rf catdir.output.clip2
+
+empty: clean
+	rm -rf catdir.input
+	rm -rf catdir.in2
+
+init:
+	rm -rf catdir.input
+	addstar -D CATDIR catdir.input -cat usno -p USNO_RED -region 0 1 0 1
+	addstar -D CATDIR catdir.in2 -cat usno -p USNO_RED -region 0 1 0 1
+	addstar -D CATDIR catdir.in2 -cat usno -p USNO_BLUE -region 0 1 0 1
+
+copy:
+	photdbc -D CATDIR catdir.input catdir.output.copy -region 0 1 0 1
+
+clip:
+	photdbc -D CATDIR catdir.input catdir.output.clip -region 0 1 0 1 -maxminmag 14.0
+
+clip2:
+	photdbc -D CATDIR catdir.in2 catdir.output.clip2 -region 0 1 0 1 -maxminmag 14.0
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/photdbc/photcode.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/photdbc/photcode.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/photdbc/photcode.txt	(revision 22322)
@@ -0,0 +1,75 @@
+# this file contains the list of valid photometry codes.
+# there should be one code for each combination of CCD, filter, and
+# telescope (and possibly additional ones if filter transmission
+# reduces with time).  The code on each line is a unique number, while
+# the name is a representative name.  The type defines how the
+# photometry is treated.  There can only be one primary photcode.  There
+# should only be a few secondary photcodes: these are the calibrated
+# values for stars.  The dependent photcodes are those which are
+# equivalent to a primary or secondary photcode, and are equated after
+# photometric solutions.  Reference photcodes are for anything without
+# a dynamic solution: the reference values are provided externally.
+
+# a bogosity in scatfit: it assumes primary photcodes are those which
+# N % 50 == 0
+
+# I discovered that the standards star analysis and the photreg
+# process are inconsistent.  the standard star analysis was using the
+# per-chip zero-points while photreg was using the per-filter
+# zero-points.  these should have been the same, but were not.  See
+# cfh12.photcode.original for the version with this error.  This file
+# now contains consistent entries
+
+# code name		type  zero    airmass  offset   c1   c2   slope  <color> equivalence
+#  1    B		pri   26.017  -0.15     -       1023 1024 0.016     -      1
+#  2    V		sec   26.220  -0.12     -       1023 1024 0.008     -      2
+#  3    R		sec   26.190  -0.09     -       1024 1025 0.028     -      3
+#  4    I		sec   26.185  -0.04     -       1025 1027 0.107     -      4
+
+#  1    B		pri   26.017  -0.15     -       1003 1004 0.016     -      1
+#  2    V		sec   26.220  -0.12     -       1003 1004 0.008     -      2
+#  3    R		sec   26.190  -0.09     -       1004 1005 0.028     -      3
+#  4    I		sec   26.185  -0.04     -       1005 1007 0.107     -      4
+
+  1    B		pri   26.000  -0.15     -       1003 1004 0.016     -      1
+  2    V		sec   26.150  -0.12     -       1003 1004 0.008     -      2
+  3    R		sec   26.190  -0.09     -       1003 1004 0.028     -      3
+  4    I		sec   26.135  -0.04     -       1003 1004 0.107     -      4
+  5    Z		sec   25.830  -0.03     -       -    -    -         -      5
+
+  1000 USNO_BLUE	ref   -     -        -       -  -   -      -        -
+  1001 USNO_RED		ref   -     -        -       -  -   -      -        -
+
+    50 ASCA_RED    	dep  10.000 0.0      -       -  -   -      -        3
+    51 ASCA_BLUE    	dep  10.000 0.0      -       -  -   -      -        3
+
+  1002 U_L92		ref   -     -        -       -  -   -      -        -
+  1003 B_L92		ref   -     -        -       -  -   -      -        -
+  1004 V_L92		ref   -     -        -       -  -   -      -        -
+  1005 R_L92		ref   -     -        -       -  -   -      -        -
+  1007 I_L92		ref   -     -        -       -  -   -      -        -
+
+  1022 U_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1023 B_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1024 V_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1025 R_L92_HQ		ref   -     -        -       -  -   -      -        -
+  1027 I_L92_HQ		ref   -     -        -       -  -   -      -        -
+
+  1012 U_STET		ref   -     -        -       -  -   -      -        -
+  1013 B_STET		ref   -     -        -       -  -   -      -        -
+  1014 V_STET		ref   -     -        -       -  -   -      -        -
+  1015 R_STET		ref   -     -        -       -  -   -      -        -
+  1017 I_STET		ref   -     -        -       -  -   -      -        -
+
+  1100 GSC_M    	ref   -     -        -       -  -   -      -        -
+  1101 SAO		ref   -     -        -       -  -   -      -        -
+
+  1200 TYCHO_B		ref   -     -        -       -  -   -      -        -
+  1201 TYCHO_V		ref   -     -        -       -  -   -      -        -
+
+  2011 2MASS_J          ref   -     -        -       -  -   -      -        -
+  2012 2MASS_H          ref   -     -        -       -  -   -      -        -
+  2013 2MASS_K          ref   -     -        -       -  -   -      -        -
+
+  100  CFH12K.B.00	dep   26.000 -0.15     -       -  -   -      -        1
+  101  MEGACAM.r.13	dep   26.000 -0.15     -       -  -   -      -        1
Index: /tags/sj_tags/sj_root_20080929/Ohana/test/photdbc/ptolemy.rc
===================================================================
--- /tags/sj_tags/sj_root_20080929/Ohana/test/photdbc/ptolemy.rc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/Ohana/test/photdbc/ptolemy.rc	(revision 22322)
@@ -0,0 +1,23 @@
+# .ptolemyrc
+ 
+REFSDIR                 ../../refs
+
+# asca camera settings
+UT-KEYWORD              UTSHUT
+DATE-KEYWORD            DATE
+DATE-MODE               yyyy-mm-dd
+
+# matching radius (arcseconds)
+# RADIUS  1.0  (old name, deprecated)
+ADDSTAR_RADIUS  1.0
+
+GSCFILE ../refs/GSCregions.tbl
+CATDIR catdir
+PHOTCODE_FILE photcode.txt
+
+USNO_A_DIR /data/elixir/srcdir/refs/usno
+USNO_B_DIR /data/elixir/srcdir/refs/usnob
+
+ZERO_PT 25.0
+
+GSCDIR /data/elixir/srcdir/refs/gsc
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Config/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Config/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Config/.cvsignore	(revision 22322)
@@ -0,0 +1,5 @@
+Build
+_build
+blib
+META.yml
+Makefile.PL
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Config/Build.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Config/Build.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Config/Build.PL	(revision 22322)
@@ -0,0 +1,25 @@
+use Module::Build;
+# See perldoc Module::Build for details of how this works
+
+Module::Build->new(
+    module_name         => 'PS::IPP::Config',
+    dist_version_from   => 'lib/PS/IPP/Config.pm',
+    author              => 'Paul Price <price@ifa.hawaii.edu> and Joshua Hoblitt <jhoblitt@cpan.org>',
+    license             => 'gpl',
+    create_makefile_pl  => 'passthrough',
+    requires            =>  {
+        'Carp'                      => 0,
+        'Class::Accessor::Fast'     => '0.19',
+        'File::Spec'                => '0.87',
+        'Getopt::Long'              => '2.33',
+	'IPC::Cmd'                  => '0.36',
+        'PS::IPP::Metadata::Config' => '1.00',
+        'Statistics::Descriptive'   => '2.6',
+        'URI'                       => 0,
+    },
+    recommends          => {
+        'Test::Distribution'        => '1.22',
+    },
+    dist_abstract => 'Configuration module for Pan-STARRS IPP.',
+
+)->create_build_script;
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Config/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Config/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Config/LICENSE	(revision 22322)
@@ -0,0 +1,260 @@
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Config/MANIFEST
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Config/MANIFEST	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Config/MANIFEST	(revision 22322)
@@ -0,0 +1,7 @@
+Build.PL
+LICENSE
+lib/PS/IPP/Config.pm
+lib/PS/IPP/Metadata/List.pm
+lib/PS/IPP/Metadata/Stats.pm
+t/00_distribution.t
+MANIFEST
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Config/lib/PS/IPP/Config.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Config/lib/PS/IPP/Config.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Config/lib/PS/IPP/Config.pm	(revision 22322)
@@ -0,0 +1,1292 @@
+# Copyright (c) 2006  Paul Price, Joshua Hoblitt
+#
+# $Id: Config.pm,v 1.92 2008-09-13 02:22:10 eugene Exp $
+
+package PS::IPP::Config;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = '1.01';
+
+use Carp qw( carp );
+use File::Basename qw( basename dirname );
+# use File::Spec 3.19;
+use File::Spec 0.87;
+use File::Temp qw( tempfile );
+
+use PS::IPP::Metadata::Config 1.00;
+use Getopt::Long 2.33 qw( GetOptions :config gnu_getopt pass_through ); # Set pass_through so we don't kill @ARGV
+use IPC::Cmd 0.36 qw( can_run run );
+
+use base qw( Class::Accessor::Fast Exporter );
+__PACKAGE__->mk_accessors( qw( path ) );
+
+our @EXPORT_OK = qw(
+                    $PS_EXIT_SUCCESS
+                    $PS_EXIT_UNKNOWN_ERROR
+                    $PS_EXIT_SYS_ERROR
+                    $PS_EXIT_CONFIG_ERROR
+                    $PS_EXIT_PROG_ERROR
+                    $PS_EXIT_DATA_ERROR
+                    $PS_EXIT_TIMEOUT_ERROR
+                    metadataLookupStr
+                    metadataLookupMD
+                    metadataLookupBool
+                    caturi
+                    file_scheme
+                    );
+our %EXPORT_TAGS = (standard => [@EXPORT_OK]);
+
+our $PS_EXIT_SUCCESS = 0;
+our $PS_EXIT_UNKNOWN_ERROR = 1;
+our $PS_EXIT_SYS_ERROR = 2;
+our $PS_EXIT_CONFIG_ERROR = 3;
+our $PS_EXIT_PROG_ERROR = 4;
+our $PS_EXIT_DATA_ERROR = 5;
+our $PS_EXIT_TIMEOUT_ERROR = 6;
+
+our $parser = PS::IPP::Metadata::Config->new; # Metadata parser
+$parser->overwrite(0);                # Turn off overwrite
+
+#$::RD_TRACE = 1;
+#$::RD_HINT = 1;
+#use Data::Dumper;
+
+# Given a parsed metadata, parse it into an array of hashes
+sub new {
+    my $class = shift;                # Class name
+    my $camera = shift;                # Camera name
+
+    unless (defined $class) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    # Get location of ipprc file
+    my $name;                        # Name of ipprc file
+    GetOptions( 'site=s' => \$name );
+    $name = $ENV{IPPRC} if not defined $name;
+    if (not defined $name and defined $ENV{HOME}) {
+        $name = $ENV{HOME} . '/.ipprc';
+    }
+
+    my $file;                        # File handle
+    unless (open $file, $name) {
+        carp "Unable to open ipprc file $name: $!";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+    my @contents = <$file>;        # Contents of the ipprc file
+    close $file;
+
+    my $mdc = $parser->parse( join '', @contents); # The parsed metadata config
+
+    my $path = _interpolate_env(metadataLookupStr($mdc, 'PATH')); # The path
+    unless (defined $path) {
+        carp "PATH is not set in $name\n";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    # Interpolate environment variables in the work directory
+
+    my $self = {
+        path => $path,                # Path
+        camera => undef,        # Camera configuration
+        filerules => undef,        # File rules from the camera configuration
+        rejection => undef,        # Rejection data
+        reductionClasses => undef, # Reduction class definitions
+        _userConfig => $mdc,        # The top-level user configuration
+        _siteConfig => undef,        # The site configuration (from _userConfig)
+        _systemConfig => undef        # The system configuration (from _userConfig)
+        };
+    bless $self, $class;
+
+    $self->load_site();
+    $self->load_system();
+
+    $self->define_camera($camera) if defined $camera;
+
+    return $self;
+}
+
+# Find a configuration file, which might be anywhere in the PATH
+sub _find_config
+{
+    my $self = shift;                # Configuration object
+    my $filename = shift;        # File name to find
+
+    my @path = split /:/, $self->{path}; # List of paths to try (I need this: ,/ for emacs fonts...)
+    my $found = 0;                        # Found it?
+    my $realfile;
+    my $tries = 0;
+    while (not $found) {
+        ## try a second time
+        foreach my $path (@path) {
+            $realfile = File::Spec->rel2abs( $filename, $path );
+            if ($tries) {
+                print "re-trying ($tries): filename: $filename, path: $path -> ";
+                print "test: $realfile\n";
+            }
+            if (-f $realfile) {
+                $found = 1;
+                return $realfile;
+            }
+        }
+        if (not $found) {
+            $tries ++;
+            if ($tries > 4) {
+                unless ($found) {
+                    carp "Unable to find camera configuration file $filename\n";
+                    exit($PS_EXIT_CONFIG_ERROR);
+                }
+            }
+            ## try again after a moment?
+            ## system ("df");
+            select(undef, undef, undef, 0.25);
+        }
+    }
+
+    carp "Unable to find configuration file: $filename";
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Load the site config file
+sub load_site
+{
+    my $self = shift;                # Configuration object
+
+    my $i = 0;
+
+    unless (defined $self) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $filename = metadataLookupStr($self->{_userConfig}, 'SITE'); # Site config file
+    unless (defined $filename) {
+        carp "Unable to find site configuration file\n";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $realfile = $self->_find_config($filename); # Resolved filename, after hunting the PATH
+
+    # Read the file
+    my $file;                        # File handle
+    unless (open $file, $realfile) {
+        carp "Unable to open site configuration file $realfile: $!";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my @contents = <$file>;
+    close $file;
+    $self->{_siteConfig} = $parser->parse( join '', @contents); # The parsed metadata config
+
+    unless (defined $self->{_siteConfig}) {
+        carp "Failure to parse the site configuration file $realfile";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    return $self;
+}
+
+# Load the system config file
+sub load_system
+{
+    my $self = shift;                # Configuration object
+
+    unless (defined $self) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $filename = metadataLookupStr($self->{_userConfig}, 'SYSTEM'); # System config file
+    unless (defined $filename) {
+        carp "Unable to find system configuration file\n";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $realfile = $self->_find_config($filename); # Resolved filename, after hunting the PATH
+
+    # Read the file
+    my $file;                        # File handle
+    unless (open $file, $realfile) {
+        carp "Unable to open system configuration file $realfile: $!";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+    my @contents = <$file>;
+    close $file;
+    $self->{_systemConfig} = $parser->parse( join '', @contents); # The parsed metadata config
+
+    unless (defined $self->{_systemConfig}) {
+        carp "Failure to define system configuration";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    # XXX why isn't just $self being returned here? -JH
+    return $self;
+}
+
+# Define a camera to use
+sub define_camera
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # Camera name
+
+    unless (defined $self and defined $name) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $camera_list = metadataLookupMD($self->{_systemConfig}, 'CAMERAS'); # List of cameras
+    my $filename = metadataLookupStr($camera_list, $name); # Filename of camera configuration
+    unless (defined $filename) {
+        carp "Unable to find configuration file for camera $name\n";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $realfile = $self->_find_config($filename); # Resolved filename, after hunting the PATH
+
+    # Read the file
+    my $file;                        # File handle
+    unless (open $file, $realfile) {
+        carp "Unable to open camera configuration file $realfile: $!";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+    my @contents = <$file>;
+    close $file;
+    $self->{camera} = $parser->parse( join '', @contents); # The parsed metadata config
+    $self->{cameraName} = $name;
+
+    unless (defined $self->{camera}) {
+        carp "Failure to define camera";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    # XXX why isn't just $self being returned here? -JH
+    return defined $self->{camera};
+}
+
+# Return the scheme used for a filename
+sub file_scheme
+{
+    my $name = shift;                # Filename for which to get the scheme
+    my ($scheme) = $name =~ /^(path|neb|file):/; # The scheme, e.g., file://, path://
+    # $scheme may be undef if the input doesn't contain one of the above recognised schemes
+    return $scheme;
+}
+
+
+# Concatenate elements of a URI
+sub caturi
+{
+    my $base = shift;                # Base name (might be "foo /foo path://SOMETHING" or "neb://SOMETHING")
+    my @segments = @_;                # Path segments
+
+    carp "base is not inited" if not defined $base;
+    my $scheme = file_scheme($base); # The scheme, e.g., file://, path://
+    $base =~ s|^$scheme:/*|| if defined $scheme;
+
+    my $root = '';
+    if ($base =~ m|^/|) { $root = '/'; }
+
+    unshift @segments, $base if $base =~ /\S+/;
+
+    # Remove leading and trailing slashes
+    foreach my $segment ( @segments ) {
+        $segment =~ s|^/*||;
+        $segment =~ s|/*$||;
+    }
+
+    # Join everything together
+    my $joined = join('/', @segments);
+    if (defined $scheme) {
+        $joined = lc($scheme) . "://" . $joined;
+    } else {
+        $joined = $root . $joined;
+    }
+
+    return $joined;
+}
+
+# select a datapath from config.site
+sub datapath
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # datapath name
+
+    unless (defined $self and defined $name) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $path_list = metadataLookupMD($self->{_siteConfig}, 'DATAPATH'); # List of paths
+    my $pathname = metadataLookupStr($path_list, $name); # Path of interest
+    unless (defined $pathname) {
+        carp "Unable to find datapath $name\n" ;
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    return $pathname;
+}
+
+# Start up Nebulous
+sub _neb_start
+{
+    my $self = shift;                # Configuration object
+
+    eval {
+        require Nebulous::Client;
+    };
+    if ($@) {
+        carp "Can't find Nebulous::Client.";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    return 1 if defined $self->{nebulous}; # Already started
+
+    my $server = metadataLookupStr( $self->{_siteConfig}, 'NEB_SERVER' ); # Nebulous server
+    unless (defined $server) {
+        carp "Unable to find NEB_SERVER in camera configuration file.";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $neb = Nebulous::Client->new( proxy => $server );
+    unless (defined $neb) {
+        carp "Unable to find NEB_SERVER in camera configuration file.";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    $self->{nebulous} = $neb;
+
+    return 1;
+}
+
+# Resolve a URI to a file name
+sub file_resolve
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # File name to check
+    my $create_if_doesnt_exist = shift;
+
+    my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
+
+    if ($scheme) {
+        $scheme = lc($scheme);
+	# print "scheme: $scheme\n";
+
+        if ($scheme eq 'neb') {
+            $self->_neb_start();
+            my $neb = $self->{nebulous}; # Nebulous handle
+            if ($create_if_doesnt_exist) {
+                unless ($neb->stat( $name )) {
+		    # print "entry $name not found, creating...\n";
+                    my $uri = $neb->create( $name );
+                    unless(defined $uri) {
+                        carp "unable to instantiate $name.";
+                        exit($PS_EXIT_DATA_ERROR);
+                    }
+                    my $path = URI->new( $uri )->path;
+		    # print "created path: $path\n";
+                    return $path;
+                }
+            }
+	    my $path = $neb->find( $name );
+	    if (not defined $path) { 
+		carp "neb entry $name not found, not created\n"; 
+		exit($PS_EXIT_DATA_ERROR);
+	    } 
+	    # print "found path: $path\n";
+            return $path;
+        }
+
+        if ($scheme eq 'path' or $scheme eq 'file') {
+            # guaranteed to have a scheme (path:// or file://)
+            $name = $self->convert_filename_absolute( $name );
+	    # print "resolved path to $name\n";
+        }
+    }
+
+    if ($create_if_doesnt_exist && ! -e $name) {
+        # make sure the file's parent directory exists
+        my $dir = dirname($name);
+        if (! -e $dir) {
+            my $rc = system "mkdir -p $dir";
+            die "failed to create directory for $name" unless (!$rc);
+        } elsif (! -d $dir ) {
+            die "parent for $name exists and is not a directory";
+        }
+
+        open F, ">$name" or die "failed to create $name";
+        close F;
+	# print "created target $name\n";
+    }
+
+    return $name;
+}
+
+# Create and open file
+sub file_create_open
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # File name to check
+
+    $self->file_prepare( $name );
+
+    my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
+    if (defined $scheme) {
+        $scheme = lc($scheme);
+        if ($scheme eq 'neb') {
+            $self->_neb_start();
+            return $self->{nebulous}->open_create( $name );
+        }
+        if ($scheme eq 'path' or $scheme eq 'file') {
+            # guaranteed to have a scheme (path:// or file://)
+            $name = $self->convert_filename_absolute( $name );
+        }
+    }
+
+    if (-f $name) {
+        carp "Unable to create file $name --- file exists.";
+        exit($PS_EXIT_SYS_ERROR);
+    }
+
+    my $fh;
+    unless (open $fh, '>', $name) {
+        carp "Unable to create file $name --- $!";
+        exit($PS_EXIT_SYS_ERROR);
+    }
+    return $fh;
+}
+
+# Create and open file for appending
+sub file_create_append
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # File name to check
+
+    $self->file_prepare( $name );
+
+    my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
+    if (defined $scheme) {
+        $scheme = lc($scheme);
+        if ($scheme eq 'neb') {
+            $self->_neb_start();
+            return $self->{nebulous}->open_create( $name );
+        }
+        if ($scheme eq 'path' or $scheme eq 'file') {
+            # guaranteed to have a scheme (path:// or file://)
+            $name = $self->convert_filename_absolute( $name );
+        }
+    }
+
+    my $fh;
+    unless (open $fh, '>>', $name) {
+        carp "Unable to create file $name --- $!";
+        exit($PS_EXIT_SYS_ERROR);
+    }
+    return $fh;
+}
+
+# Create a file (intended principally to abstract Nebulous files)
+sub file_create
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # File name to check
+
+    $self->file_prepare( $name );
+
+    my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
+    if (defined $scheme and lc($scheme) eq 'neb') {
+        $self->_neb_start();
+        $name = $self->{nebulous}->create( $name );
+    }
+
+    return $self->file_resolve($name);
+}
+
+# Check if a file exists
+sub file_exists
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # File name to check
+
+    my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
+    if (defined $scheme and lc($scheme) eq 'neb') {
+        $self->_neb_start();
+        return (defined $self->{nebulous}->find_instances( $name ) ? 1 : 0);
+    }
+
+    return (-f $self->file_resolve($name));
+}
+
+# Copy a file
+sub file_copy
+{
+    my $self = shift;                # Configuration object
+    my $source = shift;                # Name of source file
+    my $target = shift;                # Name of target file
+
+    $self->file_prepare( $target );
+
+    my $scheme = file_scheme($target); # The scheme, e.g., file://, path://
+    if (defined $scheme and lc($scheme) eq 'neb') {
+        $self->_neb_start();
+        $target = $self->{nebulous}->create( $target );
+    }
+    $target = $self->file_resolve( $target );
+    $source = $self->file_resolve( $source );
+
+    system("cp $source $target") == 0 or (carp "Can't copy file $source to $target." and
+                                          exit($PS_EXIT_DATA_ERROR));
+    return 1;
+}
+
+# create a (possibly) temporary file with name (basename $_[0])
+# if $preserve create a file named basename$_[0] in current directory
+
+sub create_temp_file
+{
+    my $self     = shift;
+    my $pathname = shift;
+    my $preserve = shift;
+
+    die "pathname must be defined" unless ($pathname);
+
+    my $fileRef;
+    my $fileName;
+
+    # we only use in the last part of the path
+    my $base = basename($pathname);
+
+    if ($preserve) {
+        # we want to keep the file just create it in the current directory 
+        $fileName = "./$base";
+        open $fileRef, ">$fileName" or die "can't open $fileName for output";
+    } else {
+        #  we really want a tempfile, so put it in /tmp
+        ($fileRef, $fileName) = tempfile("/tmp/$base.XXXX", UNLINK => 1);
+    }
+
+    return ($fileRef, $fileName);
+}
+
+# redirect stderr and stdout streams to a file
+sub redirect_output
+{
+    my $self = shift;
+    my $name = shift;
+
+    die "need name" unless $name;
+
+    my $filename = $self->file_resolve($name, 1);
+
+    die "cannot resolve $name" unless $filename;
+
+    if (! open(STDOUT, ">>$filename") ) {
+        # try to work around the 'nfs glitch' by waiting and retrying the open a couple of times
+        print STDERR "failed to redirect stdout to $filename re-trying\n";
+        my $max_tries = 10;
+        my $try = 2;
+        while (! open STDOUT, ">>$filename" ) {
+            if ($try == $max_tries) {
+                die "failed to redirect stdout to $filename after trying $max_tries times";
+            }
+            sleep 5;
+            $try++;
+        }
+        print STDERR "   redirect stdout to $filename succeded on try $try\n";
+    }
+    open STDERR, ">>$filename" or die "failed to redirect stderr to $filename";
+}
+
+# Strip the filename (the last element of the URI), returning the scheme and directory part
+sub _strip_filename
+{
+    my $name = shift;                # File name of interest
+    $name =~ s|/[\w.-]*?$||;
+    return $name;
+}
+
+# Prepare to receive a new file --- create the directory, if appropriate.  Return the appropriate filename
+# Does not register anything with Nebulous
+sub file_prepare
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # File name for which to prepare
+    my $workdir = shift;        # Working directory
+    my $template = shift;        # Template filename from which to get working directory if 
+
+    if (defined $workdir) {
+        $name = caturi( $workdir, $name );
+    } elsif (defined $template) {
+        # Take directory from template and apply to name
+        my $dir = _strip_filename( $template );
+        $name = caturi( $dir, $name );
+    }
+
+    my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
+    return $name if defined $scheme and lc($scheme) eq 'neb'; # Nothing to be done: Nebulous handles it all
+
+    # Might need to create a directory
+    # not guaranteed to have a scheme (path:// or file://) - might be /PATH/foobar
+    # a relative path (PATH/foobar) is invalid here
+    my $resolved = $self->convert_filename_absolute( $name );
+    my ( $vol, $dirs, $file ) = File::Spec->splitpath( $resolved );
+    unless (-d $dirs) {
+        system("mkdir -p $dirs") == 0 or (carp "Can't create directory $dirs" and exit($PS_EXIT_DATA_ERROR));
+    }
+
+    return $name;
+}
+
+# Prepare to receive a new file --- create the directory, if appropriate.  Return the appropriate filename
+# Does not register anything with Nebulous
+sub outroot_prepare
+{
+    my $self = shift;                # Configuration object
+    my $outroot = shift;        # Working directory
+
+    # outroot is a directory + fragement of a filename
+
+    my $scheme = file_scheme($outroot); # The scheme, e.g., file://, path://
+    return 1 if defined $scheme and lc($scheme) eq 'neb'; # Nothing to be done: Nebulous handles it all
+
+    # Might need to create a directory
+    # not guaranteed to have a scheme (path:// or file://) - might be /PATH/foobar
+    # a relative path (PATH/foobar) is invalid here
+    my $resolved = $self->convert_filename_absolute( $outroot );
+    my ( $vol, $dirs, $file ) = File::Spec->splitpath( $resolved );
+    unless (-d $dirs) {
+        system("mkdir -p $dirs") == 0 or (carp "Can't create directory $dirs" and exit($PS_EXIT_DATA_ERROR));
+    }
+
+    return 1;
+}
+
+# Convert a relative filename (e.g., "path://PATH/file") to an absolute (e.g., "/path/to/file")
+# note that file://PATH/file resolves to /PATH/file, regardless of the number of leading slashes
+sub convert_filename_absolute
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # raw name
+
+    unless (defined $self and defined $name) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    $name =~ s|/$||; # drop tailing slashes (foobar/ to foobar)
+    my $scheme = file_scheme($name); # The scheme, e.g., file, path
+
+    ## if this is already an absolute path (/PATH/file), just return the path
+    unless (defined $scheme) { 
+        if ($name =~ m|^/|) { return $name; }
+        # without a leading slash, this is an error
+        carp "Relative file name provided: relative paths are not permitted.";
+        exit($PS_EXIT_SYS_ERROR);
+    }
+
+    $name =~ s|^$scheme:/*||;
+
+    if (lc($scheme) eq 'file') {
+        # the above strips of the leading slash; replace it for file:// 
+        $name = '/' . $name;
+        return $name;
+    }
+
+    if (lc($scheme) eq 'path') {
+        my @dirs = split('/', $name);
+        my $pathName = shift @dirs;
+        my $path = $self->datapath( $pathName );
+        $path =~ s|/*$||;
+        return File::Spec->catfile( $path, @dirs );
+    }
+
+    # looks like we cannot reach here without an invalid scheme.  
+    # programming error?
+    # return $name;
+
+    carp "Programming error";
+    exit($PS_EXIT_PROG_ERROR);
+}
+
+# Convert a relative filename (e.g., "/path/to/file") to an absolute (e.g., "path://PATH/file")
+sub convert_filename_relative
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # raw name
+    
+    unless (defined $self and defined $name) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+    
+    # First, check to see if it's already in a relative form
+    my $scheme = file_scheme($name); # The scheme, e.g., file, path
+    if (defined $scheme) {
+        $scheme = lc($scheme);
+        if ($scheme eq 'path' or $scheme eq 'file') {
+            # We may as well search for a 'better' path
+            # guaranteed to have a scheme (path:// or file://)
+            $name = $self->convert_filename_absolute( $name );
+        } elsif ($scheme eq 'neb') {
+            # No chance of changing anything --- move along
+            return $name;
+        }
+    }
+    
+    $name = File::Spec->canonpath( $name); # Clean up
+    my @dirs = File::Spec->splitdir( $name );
+    
+    my $path_list = metadataLookupMD($self->{_siteConfig}, 'DATAPATH'); # List of paths
+    my $best_path;
+    my $best_name;
+    my $best_score;
+  CFR_PATH_CHECK: foreach my $path_item (@$path_list) {
+      my $path_name = $path_item->{name}; # Name of the path
+      my $path = File::Spec->canonpath( $path_item->{value} ); # The path
+      $path =~ s|/*$||;
+      my @path_dirs = File::Spec->splitdir( $path );
+      
+      # Check if the path is suitable
+      next if scalar @path_dirs > scalar @dirs;
+      for (my $i = 0; $i < scalar @path_dirs; $i++) {
+          next CFR_PATH_CHECK if $path_dirs[$i] ne $dirs[$i];
+      }
+      # It is suitable; see if it is 'best' (minimizes the number of directories)
+      my $score = scalar @path_dirs;
+      if (not defined $best_score or $score > $best_score) {
+          $best_path = $path;
+          $best_score = $score;
+          $best_name = $path_name;
+      }
+  }
+    
+    $name =~ s|^/||;
+    $name =~ s|/$||;
+    if (defined $best_score) {
+        $best_path =~ s|^/||;
+        $best_path =~ s|/$||;
+        $name =~ s|^$best_path|$best_name|;
+        return 'path://' . $name;
+    }
+    return 'file://' . $name;
+}
+
+# Return a rejection limit from the camera configuration
+sub rejection
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # Name of limit
+    my $type = lc(shift);        # Type of frame
+    my $filter = shift;                # Filter name, for extra qualification; optional
+
+    unless (defined $self and defined $name and defined $type) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    unless (defined $self->{rejection}) {
+        my $camera = $self->{cameraName}; # Camera name
+        unless (defined $camera) {
+            carp "Camera has not yet been defined.\n";
+            return undef;
+        }
+        
+        # rejections are saved as a recipe: REJECTIONS
+        my @rejContents = `ppConfigDump -dump-recipe REJECTIONS -camera $camera -`;
+        
+        # load from resulting psMetadataConfig
+        $self->{rejection} = $parser->parse( join '', @rejContents); # The rejection metadata
+        unless (defined $self->{rejection}) {
+            carp "Unable to parse REJECTION recipe for $camera.";
+            exit($PS_EXIT_CONFIG_ERROR);
+        }
+    }
+
+    my $rejection = $self->{rejection};
+
+    # Find the appropriate entry
+  TYPES: foreach my $item (@$rejection) {
+      if (lc($item->{name}) eq $type) {
+          unless ($item->{class} eq "metadata") {
+              carp "$name within REJECTIONS is not of type METADATA";
+              exit($PS_EXIT_PROG_ERROR);
+          }
+          my $limits = $item->{value}; # List of rejection limits
+
+          # Check the FILTER first
+          foreach my $limit (@$limits) {
+              if ($limit->{name} eq 'FILTER') {
+                  if ($limit->{value} eq '*' or
+                      (defined $filter and
+                       $limit->{value} eq $filter)) {
+                      last;
+                  }
+                  next TYPES; # Doesn't match --- look at the next one
+              }
+          }
+          
+          foreach my $limit (@$limits) {
+              return $limit->{value} if $limit->{name} eq $name;
+          }
+          carp "Unable to find limit $name in REJECTIONS list for $type.\n";
+          return undef;
+      }
+  }
+
+    if (not defined $filter) {
+        carp "Unable to find type $type with no FILTER in REJECTIONS list.\n";
+    } else {
+        carp "Unable to find type $type with FILTER $filter in REJECTIONS list.\n";
+    }
+    return undef;
+}
+
+# Return a filename from the camera configuration FILERULES
+sub filename
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # Name of the file
+    my $output = shift;                # For replacing {OUTPUT}
+    my $component = shift;        # For replacing {CHIP.NAME} and {CELL.NAME}
+
+    unless (defined $self and defined $name and defined $output) {
+        carp "Programming error: required inputs left undefined";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $filerules;                # File rules
+    if (defined $self->{filerules}) {
+        $filerules = $self->{filerules};
+    } else {
+        # Get the file rules from the camera configuration
+
+        my $camera = $self->{camera}; # Camera configuration
+        unless (defined $camera) {
+            carp "Camera has not yet been defined.\n";
+            return undef;
+        }
+        
+        $filerules = metadataLookup($camera, 'FILERULES');        # File rules
+        unless (defined $filerules) {
+            carp "Can't find FILERULES list in camera configuration.\n";
+            return undef;
+        }
+        
+        if ($filerules->{class} eq "scalar" and $filerules->{type} eq "STR") {
+            # Allow indirection to a file
+            my $filename = $self->_find_config($filerules->{value}); # Resolved filename
+            # Read the file
+            my $file;                # File handle
+            unless (open $file, $filename) {
+                carp "Unable to open filerules file $filename: $!";
+                exit($PS_EXIT_CONFIG_ERROR);
+            }
+            my @contents = <$file>;
+            close $file;
+            $filerules = $parser->parse( join '', @contents);
+        } elsif ($filerules->{class} eq "metadata") {
+            $filerules = $filerules->{value};
+        } else {
+            carp "Can't interpret FILERULES in camera configuration.\n";
+            return undef;
+        }
+        $self->{filerules} = $filerules;
+    }
+
+    my $file = _mdLookupMDwithRedirect($filerules, $name); # The file of interest
+    unless (defined $file) {
+        carp "Can't find $name within FILERULES in camera configuration.\n";
+        return undef;
+    }
+
+    my $filename = metadataLookupStr($file, "FILENAME.RULE"); # File name
+    unless (defined $filename) {
+        carp "Can't find FILENAME.RULE for $name within FILERULES in camera configuration.\n";
+        return undef;
+    }
+
+    $filename =~ s/\{OUTPUT\}/$output/;
+    if ($filename =~ /\{CHIP\.NAME\}/) {
+        unless (defined $component) {
+            carp "Programming error";
+            exit($PS_EXIT_PROG_ERROR);
+        }
+        $filename =~ s/\{CHIP\.NAME\}/$component/;
+        $filename =~ s/\{CELL\.NAME\}/$component/;
+    }
+
+    return $filename;
+}   
+
+# Return an EXTNAME From the EXTNAME.RULE table in the camera configuration
+sub extname_rule
+{
+    my $self = shift;                # Configuration object
+    my $name = shift;                # Name of the EXTNAME to build
+    my $component = shift;        # For replacing {CHIP.NAME}
+
+    unless (defined $self and defined $name) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $camera = $self->{camera}; # Camera configuration
+    unless (defined $camera) {
+        carp "Camera has not yet been defined.\n";
+        return undef;
+    }
+
+    my $extname_rules = metadataLookupMD($camera, 'EXTNAME.RULES');        # File rules
+    unless (defined $extname_rules) {
+        carp "Can't find EXTNAME.RULES in camera configuration.\n";
+        return undef;
+    }
+
+    my $extname = metadataLookupStr($extname_rules, $name); # File name
+    unless (defined $extname) {
+        carp "Can't find $name within EXTNAME.RULES in camera configuration.\n";
+        return undef;
+    }
+
+    if ($extname =~ /\{CHIP\.NAME\}/) {
+        unless (defined $component) {
+            carp "Programming error";
+            exit($PS_EXIT_PROG_ERROR);
+        }
+        $extname =~ s/\{CHIP\.NAME\}/$component/;
+    }
+
+    return $extname;
+}   
+
+# Return catdir for tessellation, from TESSELLATIONS within the site configuration
+sub tessellation_catdir
+{
+    my $self = shift;                # Configuration object
+    my $tess_id = shift;        # Tessellation identifier
+
+    unless (defined $self and defined $self->{_siteConfig} and defined $tess_id) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $tessellations = metadataLookupMD($self->{_siteConfig}, 'TESSELLATIONS'); # Tessellations
+    unless (defined $tessellations) {
+        carp "Can't find TESSELLATIONS in site configuration.\n";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $catdir = metadataLookupStr($tessellations, $tess_id);
+    unless (defined $catdir) {
+        print "Can't find tessellation identifier $tess_id in TESSELLATIONS in site configuration, assuming it is a filename.\n";
+        return $tess_id;
+    }
+
+    ### Because DVO doesn't use psModules, it doesn't understand Nebulous --- check
+    my $scheme = file_scheme($catdir); # The scheme, e.g., file, path, neb
+    if (defined $scheme and lc($scheme) eq 'neb') {
+        carp "Tessellation $tess_id refers to a Nebulous path: $catdir\n";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    return $catdir
+}
+
+
+# Return catdir for the dvo db, from DVO.CATDIRS within the site configuration
+sub dvo_catdir
+{
+    my $self = shift;        # Configuration object
+    my $dvodb = shift;        # DVO db identifier
+
+    unless (defined $self and defined $self->{_siteConfig} and defined $dvodb) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $catdirs = metadataLookupMD($self->{_siteConfig}, 'DVO.CATDIRS'); # Tessellations
+    unless (defined $catdirs) {
+        carp "Can't find DVO.CATDIRS in site configuration.\n";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $catdir = metadataLookupStr($catdirs, $dvodb);
+    unless (defined $catdir) {
+        print "Can't find dvodb identifier $dvodb in DVO.CATDIR in site configuration, assuming filename\n";
+        return $dvodb;
+    }
+
+    ### Because DVO doesn't use psModules, it doesn't understand Nebulous --- check
+    my $scheme = file_scheme($catdir); # The scheme, e.g., file, path, neb
+    if (defined $scheme and lc($scheme) eq 'neb') {
+        carp "DVO catdir $dvodb refers to a Nebulous path: $catdir\n";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    return $catdir
+}
+
+# Return catdir for the psastro reference, from PSASTRO.CATDIRS within the site configuration
+sub psastro_catdir
+{
+    my $self = shift;        # Configuration object
+    my $dvodb = shift;        # DVO db identifier
+
+    unless (defined $self and defined $self->{_siteConfig} and defined $dvodb) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $catdirs = metadataLookupMD($self->{_siteConfig}, 'PSASTRO.CATDIRS'); # Tessellations
+    unless (defined $catdirs) {
+        carp "Can't find PSASTRO.CATDIRS in site configuration.\n";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $catdir = metadataLookupStr($catdirs, $dvodb);
+    unless (defined $catdir) {
+        print "Can't find dvodb identifier $dvodb in PSASTRO.CATDIR in site configuration, assuming filename.\n";
+        return $dvodb;
+    }
+
+    ### Because DVO doesn't use psModules, it doesn't understand Nebulous --- check
+    my $scheme = file_scheme($catdir); # The scheme, e.g., file, path, neb
+    if (defined $scheme and lc($scheme) eq 'neb') {
+        carp "PSASTRO catdir $dvodb refers to a Nebulous path: $catdir\n";
+        exit($PS_EXIT_CONFIG_ERROR);
+    }
+
+    return $catdir
+}
+
+# Return the DVO.CAMERADIR in the camera configuration
+sub dvo_cameradir
+{
+    my $self = shift;                # Configuration object
+
+    unless (defined $self) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $camera = $self->{camera}; # Camera configuration
+    unless (defined $camera) {
+        carp "Camera has not yet been defined.\n";
+        return undef;
+    }
+
+    my $dir = metadataLookupStr($camera, "DVO.CAMERADIR"); # Directory of interest
+    unless (defined $dir) {
+        carp "Can't find DVO.CAMERADIR in camera configuration.\n";
+        return undef;
+    }
+
+    return $dir;
+}
+
+# Given a reduction class name (and a previously defined camera) and a
+# symbolic name for the recipe, return the actual name of the recipe.
+sub reduction
+{
+    my $self = shift;                # Configuration object
+    my $reduction = shift;        # Reduction class
+    my $name = shift;                # Symbolic name of recipe
+
+    unless (defined $self and defined $reduction and defined $name) {
+        carp "Programming error --- inputs undefined";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    my $reductionClasses;
+    if (defined $self->{reductionClasses}) {
+        $reductionClasses = $self->{reductionClasses};
+    } else  {
+        # load and save the reductionClasses table
+
+        # need to have a valid camear
+        my $camera = $self->{camera}; # Camera configuration
+        unless (defined $camera) {
+            carp "Camera has not yet been defined.\n";
+            return undef;
+        }
+
+        $reductionClasses = metadataLookup($camera, 'REDUCTION');        # File rules
+
+        unless (defined $reductionClasses) {
+            carp "Can't find REDUCTION list in camera configuration.\n";
+            return undef;
+        }
+        
+        if ($reductionClasses->{class} eq "scalar" and $reductionClasses->{type} eq "STR") {
+            # Allow indirection to a file
+            my $filename = $self->_find_config($reductionClasses->{value}); # Resolved filename
+            # Read the file
+            my $file;                # File handle
+            unless (open $file, $filename) {
+                carp "Unable to open reductionClasses file $filename: $!";
+                exit($PS_EXIT_CONFIG_ERROR);
+            }
+            my @contents = <$file>;
+            close $file;
+            $reductionClasses = $parser->parse( join '', @contents);
+        } elsif ($reductionClasses->{class} eq "metadata") {
+            $reductionClasses = $reductionClasses->{value};
+        } else {
+            carp "Can't interpret REDUCTIONS in camera configuration.\n";
+            return undef;
+        }
+        $self->{reductionClasses} = $reductionClasses;
+    }
+
+    my $class = metadataLookupMD($reductionClasses, $reduction) or # Class of interest
+        (carp "Can't find $reduction in REDUCTION in camera configuration.\n" and
+         exit($PS_EXIT_CONFIG_ERROR));
+
+    my $actual = metadataLookupStr($class, $name) or # The actual recipe name of interest
+        (carp "Can't find $name in $class in REDUCTION in camera configuration.\n" and
+         exit($PS_EXIT_CONFIG_ERROR));
+
+    return $actual;
+}
+
+sub skycell_file
+{
+    my $self = shift;                # Configuration object
+    my $tess_id = shift;        # Tessellation identifier
+    my $skycell_id = shift;        # Skycell identifier
+    my $outname = shift;        # Output name
+    my $verbose = shift;        # Verbose?
+
+    unless (defined $self and defined $tess_id and defined $skycell_id and defined $outname) {
+        carp "Input parameters not defined";
+        return 0;
+    }
+
+    my $dvoImageExtract = can_run('dvoImageExtract') or die "Can't find dvoImageExtract";
+    
+    my $tess_dir = $self->tessellation_catdir( $tess_id ); # Tessellation catdir for DVO
+    unless (defined $tess_dir) {
+        carp "Can't get list of tessellations.";
+        return 0;
+    }
+    $tess_dir = $self->convert_filename_absolute( $tess_dir );
+
+    unless ($self->file_exists( $outname )) {
+        my $outnameResolved = $self->file_create( $outname ); # Resolved filename, for Nebulous
+        my $command = "$dvoImageExtract -D CATDIR $tess_dir $skycell_id -o $outnameResolved";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        die "Unable to perform dvoImageExtract for $tess_id $skycell_id\n" unless ($success and $self->file_exists( $outname ));
+    }
+
+    return 1;
+}
+
+# Interpolate environment variables in a directory (or colon-delimited list of directories)
+sub _interpolate_env
+{
+    my $dir = shift;                # Directory of interest
+
+    return undef unless defined $dir;
+
+    while ($dir =~ /\$\{?(\w*)[\}\/:]?/) {
+        my $name = $1;                # Environment variable name
+        my $value = $ENV{$name}; # Environment variable value
+        unless (defined $value) {
+            carp "Unable to find environment variable $name";
+            exit($PS_EXIT_SYS_ERROR);
+        }
+        $dir =~ s/\$\{?$name\}?/$value\//;
+    }
+    return $dir;
+}
+
+sub metadataLookup
+{
+    my $mdc = shift;                # Metadata config to look up
+    my $name = shift;                # Name of item to look up
+
+    unless (defined $mdc and defined $name) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    # Iterate through the array of hashes
+    foreach my $item (@$mdc) {
+        if ($item->{name} eq $name) {
+            return $item;
+        }
+    }
+
+    carp "Unable to find $name within metadata.\n";
+    return undef;
+}
+   
+
+# Lookup the metadata, checking the type is STR
+sub metadataLookupStr
+{
+    my $item = metadataLookup(@_);
+    return undef if not defined $item;
+    my $name = shift;                # Name of item
+    carp "$name within metadata is not of type STR.\n" unless $item->{type} eq "STR";
+    return $item->{value};
+}
+
+# Lookup the metadata, checking the type is MD
+sub metadataLookupMD
+{
+    my $item = metadataLookup(@_);
+    return undef if not defined $item;
+    my $name = shift;                # Name of item
+    carp "$name within metadata is not of type METADATA.\n" unless $item->{class} eq "metadata";
+    return $item->{value};
+}
+
+# Lookup the metadata, checking the type is BOOL
+sub metadataLookupBool
+{
+    my $item = metadataLookup(@_);
+    return undef if not defined $item;
+    my $name = shift;                # Name of item
+    carp "$name within metadata is not of type BOOL.\n" unless $item->{type} eq "BOOL";
+    return $item->{value};
+}
+
+# Lookup the metadata, checking the type is MD; may be redirection
+sub _mdLookupMDwithRedirect
+{
+    my $mdc = shift;                # Metadata config to look up
+    my $name = shift;                # Name of item to look ip
+
+    unless (defined $mdc and defined $name) {
+        carp "Programming error";
+        exit($PS_EXIT_PROG_ERROR);
+    }
+
+    # Iterate through the array of hashes
+    foreach my $item (@$mdc) {
+        if ($item->{name} eq $name) {
+            if ($item->{class} eq "metadata") {
+                return $item->{value};
+            }
+            if ($item->{class} eq "scalar" and $item->{type} eq "STR") {
+                # Redirect
+                return _mdLookupMDwithRedirect($mdc, $item->{value});
+            }
+            carp "$name within metadata is not of type METADATA or STR.\n";
+            return undef;
+        }
+    }
+
+    carp "Unable to find $name within metadata.\n";
+    return undef;
+}
+
+
+1;
+
+__END__;
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Config/lib/PS/IPP/Metadata/List.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Config/lib/PS/IPP/Metadata/List.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Config/lib/PS/IPP/Metadata/List.pm	(revision 22322)
@@ -0,0 +1,46 @@
+# Copyright (c) 2006  Paul Price, Joshua Hoblitt
+#
+# $Id: List.pm,v 1.1 2006-08-12 04:26:12 price Exp $
+
+package PS::IPP::Metadata::List;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = '0.01';
+
+use Carp qw( carp );
+
+# Allow exporting of function
+use base qw( Exporter );
+our @EXPORT_OK = qw( parse_md_list );
+
+#$::RD_TRACE = 1;
+#$::RD_HINT = 1;
+#use Data::Dumper;
+
+# Given a parsed metadata, parse it into an array of hashes
+sub parse_md_list {
+    my $md = shift;		# Parsed metadata, from PS::IPP::Metadata::Config
+
+    my @array;			# The array of hashes, to be returned
+    foreach my $mdItem (@$md) {
+	if ($mdItem->{class} ne "metadata") {
+	    carp "MD element ", $mdItem->{name}, " isn't of type METADATA --- ignored.\n";
+	    next;
+	}
+	my %hash;		# Hash element
+	my $mdComponents = $mdItem->{value}; # Components of the metadata
+	foreach my $data (@$mdComponents) {
+	    $hash{$data->{name}} = $data->{value};
+	}
+	push @array, \%hash;
+    }
+
+    return \@array;
+}
+
+1;
+
+__END__;
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Config/lib/PS/IPP/Metadata/Stats.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Config/lib/PS/IPP/Metadata/Stats.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Config/lib/PS/IPP/Metadata/Stats.pm	(revision 22322)
@@ -0,0 +1,399 @@
+# Copyright (c) 2006  Paul Price, Joshua Hoblitt
+#
+# $Id: Stats.pm,v 1.33 2008-08-06 02:35:40 price Exp $
+
+package PS::IPP::Metadata::Stats;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = '0.02';
+
+use Carp qw( carp );
+use Statistics::Descriptive;
+
+use PS::IPP::Config qw(
+    $PS_EXIT_SUCCESS
+    $PS_EXIT_UNKNOWN_ERROR
+    $PS_EXIT_SYS_ERROR
+    $PS_EXIT_CONFIG_ERROR
+    $PS_EXIT_PROG_ERROR
+    $PS_EXIT_DATA_ERROR
+    $PS_EXIT_TIMEOUT_ERROR
+    );
+
+use base qw( Class::Accessor::Fast );
+__PACKAGE__->mk_accessors( qw(
+                               results
+                               ) );
+
+#$::RD_TRACE = 1;
+#$::RD_HINT = 1;
+use Data::Dumper;
+
+sub new {
+    my $class = shift;          # Class name
+    my $entries = shift;        # Array of values to extract
+
+    my $self = {
+        entries => $entries, # Array of values that should be constant through the FPA
+        data => {}              # The data
+    };
+
+    # add a hash for storage
+    foreach my $entry (@$entries) {
+        $entry->{data} = [];
+    }
+
+    # Bless and return object
+    bless $self, $class;
+    return $self;
+}
+
+sub cmdflags {
+
+    my $self = shift;                # Where we'll put the information
+
+    # apply the needed calculation to the data based on the type
+
+    my $cmdflags;
+    my $entries = $self->{entries};
+
+    foreach my $entry (@$entries) {
+        my $value = $entry->{value};
+        my $flag = $entry->{flag};
+        next unless defined $value and defined $flag;
+        if ($value =~ m|\S+\s+\S+|) {
+            # protect values with whitespace
+            $cmdflags .= " $flag '$value'";
+        } else {
+            $cmdflags .= " $flag $value";
+        }
+    }
+
+    return $cmdflags;
+}
+
+# Given a parsed metadata from ppStats, assemble summary statistics
+sub parse {
+    my $self = shift;                # Where we'll put the information
+    my $md = shift;                # Parsed metadata, from PS::IPP::Metadata::Config
+
+    $self->_parse_metadata_stats($md);
+
+    # apply the needed calculation to the data based on the type
+
+    my $entries = $self->{entries};
+
+    foreach my $entry (@$entries) {
+        my $type = $entry->{type};
+        my $data = $entry->{data};
+
+        if ($type eq "constant") {
+            if (not defined $entry->{value}) {
+                $self->_null_for_type ($entry);
+            }
+            next;
+        }
+
+        if ($type eq "mean") {
+            if (scalar @$data > 0) {
+                # Get statistics on the value
+                my $stats = Statistics::Descriptive::Sparse->new(); # Statistics calculator
+                $stats->add_data(@$data);
+                $entry->{value} = $stats->mean();
+            } else {
+                $self->_null_for_type ($entry);
+            }
+            next;
+        }
+
+        # calculate a 3-sigma clipped mean (if Nvalues > 8, reject > 3sigma outliers)
+        if ($type eq "clipmean") {
+            if (scalar @$data > 0) {
+                # Get statistics on the value
+                my $stats = Statistics::Descriptive::Sparse->new(); # Statistics calculator
+                $stats->add_data(@$data);
+                my $rawMean = $stats->mean();
+                my $rawStdev = $stats->standard_deviation();
+
+                # clip, if we can
+                if (scalar @$data > 8) {
+                    my $rawLimit = 3.0*$rawStdev;
+                    my @clipdata = ();
+                    foreach my $value (@$data){
+                        my $delta = abs($value - $rawMean);
+                        if ($delta > $rawLimit) { next; }
+                        push @clipdata, $value;
+                    }
+                    my $clipstats = Statistics::Descriptive::Sparse->new(); # Statistics calculator
+                    $clipstats->add_data(@clipdata);
+                    $entry->{value} = $clipstats->mean();
+                } else {
+                    $entry->{value} = $stats->mean();
+                }
+            } else {
+                $self->_null_for_type ($entry);
+            }
+            next;
+        }
+
+        if ($type eq "stdev") {
+            if (scalar @$data > 1) {
+                # Get statistics on the value
+                my $stats = Statistics::Descriptive::Sparse->new(); # Statistics calculator
+                $stats->add_data(@$data);
+                $entry->{value} = $stats->standard_deviation();
+                next;
+            }
+            if (scalar @$data == 1) {
+                $entry->{value} = 0.0;
+                next;
+            }
+            $self->_null_for_type ($entry);
+            next;
+        }
+
+        # calculate a 3-sigma clipped stdev (if Nvalues > 8, reject > 3sigma outliers)
+        if ($type eq "clipstdev") {
+            if (scalar @$data > 0) {
+                # Get statistics on the value
+                my $stats = Statistics::Descriptive::Sparse->new(); # Statistics calculator
+                $stats->add_data(@$data);
+                my $rawMean = $stats->mean();
+                my $rawStdev = $stats->standard_deviation();
+
+                # clip, if we can
+                if (scalar @$data > 8) {
+                    my $rawLimit = 3.0*$rawStdev;
+                    my @clipdata = ();
+                    foreach my $value (@$data){
+                        my $delta = abs($value - $rawMean);
+                        if ($delta > $rawLimit) { next; }
+                        push @clipdata, $value;
+                    }
+                    my $clipstats = Statistics::Descriptive::Sparse->new(); # Statistics calculator
+                    $clipstats->add_data(@clipdata);
+                    $entry->{value} = $clipstats->standard_deviation();
+                } else {
+                    $entry->{value} = $stats->standard_deviation();
+                }
+            } else {
+                $self->_null_for_type ($entry);
+            }
+            next;
+        }
+
+        if ($type eq "rms") {
+            if (scalar @$data > 0) {
+                # Get statistics on the value
+                # for rmsStats, we pushed the value^2 on the data array
+                my $stats = Statistics::Descriptive::Sparse->new(); # Statistics calculator
+                $stats->add_data(@$data);
+                $entry->{value} = sqrt($stats->mean());
+            } else {
+                $self->_null_for_type ($entry);
+            }
+            next;
+        }
+
+        if ($type eq "sum") {
+            if (scalar @$data > 0) {
+                # Get statistics on the value
+                my $stats = Statistics::Descriptive::Sparse->new(); # Statistics calculator
+                $stats->add_data(@$data);
+                $entry->{value} = $stats->sum();
+            } else {
+                $self->_null_for_type ($entry);
+            }
+            next;
+        }
+
+        if ($type eq "max") {
+            if (scalar @$data > 0) {
+                # Get statistics on the value
+                my $stats = Statistics::Descriptive::Sparse->new(); # Statistics calculator
+                $stats->add_data(@$data);
+                $entry->{value} = $stats->max();
+            } else {
+                $self->_null_for_type ($entry);
+            }
+            next;
+        }
+
+        if ($type eq "min") {
+            if (scalar @$data > 0) {
+                # Get statistics on the value
+                my $stats = Statistics::Descriptive::Sparse->new(); # Statistics calculator
+                $stats->add_data(@$data);
+                $entry->{value} = $stats->min();
+            } else {
+                $self->_null_for_type ($entry);
+            }
+            next;
+        }
+
+    }
+
+    return $self;
+}
+
+# Return 1 if a metadata item value is effectively NULL
+sub _is_null_for_type
+{
+    my ($item) = @_;            # Metadata item
+
+    return 1 unless defined $item->{type} and defined $item->{value};
+
+    if (($item->{type} eq "F32" or $item->{type} eq "F64") and lc($item->{value} eq 'nan')) {
+        return 1;
+    }
+    if ($item->{type} eq "STR" and lc($item->{value}) eq 'null') {
+        return 1;
+    }
+    return 0;
+}
+
+sub _null_for_type {
+
+    my ($self, $entry) = @_;
+
+    if ($entry->{dtype} eq "float") {
+        $entry->{value} = 'NAN';
+        return;
+    }
+    if ($entry->{dtype} eq "int") {
+        $entry->{value} = '0';
+        return;
+    }
+    if ($entry->{dtype} eq "string") {
+        $entry->{value} = 'NULL';
+        return;
+    }
+    $entry->{value} = 'NAN';
+    return;
+}
+
+sub _parse_metadata_stats
+{
+    my ($self, $md) = @_;
+
+    # descend through the fpa
+    foreach my $entry (@$md) {
+        # recurse on nested metadata
+        if ($entry->{class} eq 'metadata') {
+            $self->_parse_metadata_stats($entry->{value});
+        }
+        #if (defined $entry->{value}) {
+        #    print STDERR "found $entry->{name} : $entry->{value}\n";
+        #}
+        $self->_check_values($entry);
+   }
+
+    return $self;
+}
+
+# Private function to check a metadata item for things we're interested in
+sub _check_values {
+    my $self = shift;
+    my $mdItem = shift;                # Metadata item to check
+
+    my $name = $mdItem->{name};        # Name of the item
+    my $value = $mdItem->{value}; # Value of the item
+
+    if ( _is_null_for_type($mdItem) and $mdItem->{class} eq 'scalar' ) {
+        # print "Stats: Ignoring NULL entry $name\n";
+        return;
+    }
+
+    my $entries = $self->{entries};        # The data
+
+    # we may request the value more than once (different stats on the same value)
+    foreach my $entry (@$entries) {
+        if ($entry->{name} eq $name) {
+
+            my $type = $entry->{type}; # Type of the item: constant or variable
+            my $data = $entry->{data}; # Array containing all the values
+
+            if ($type eq 'constant') {
+                if (defined $entry->{value} && ($entry->{value} ne $value)) {
+                    carp "Warning: different value for ", $name, " found: ", $entry->{value}, " (old) vs ", $value, " (new).  Ignoring new value.\n";
+                    next;
+                }
+                $entry->{value} = $value;
+                next;
+            }
+
+            if ($type eq 'mean') {
+                push @$data, $value;
+                next;
+            }
+
+            if ($type eq 'clipmean') {
+                push @$data, $value;
+                next;
+            }
+
+            if ($type eq 'stdev') {
+                push @$data, $value;
+                next;
+            }
+
+            if ($type eq 'clipstdev') {
+                push @$data, $value;
+                next;
+            }
+
+            if ($type eq 'rms') {
+                push @$data, ($value**2);
+                next;
+            }
+
+            if ($type eq 'sum') {
+                push @$data, $value;
+                next;
+            }
+
+            if ($type eq 'max' or $type eq 'min') {
+                push @$data, $value;
+                next;
+            }
+
+            carp "Unrecognised type (", $type, ") for value of ", $name, "\n";
+            exit($PS_EXIT_PROG_ERROR);
+        }
+    }
+
+    return;
+}
+
+sub value_for_flag {
+
+    my $self = shift;
+    my $flag = shift;
+
+    my $entries = $self->{entries};
+
+    foreach my $entry (@$entries) {
+        if ($flag eq $entry->{flag}) {
+            return $entry->{value};
+        }
+    }
+    return 'NULL'; #
+}
+
+
+# Return the data structure for a particular value, or the entire hash
+sub data {
+    my $self = shift;
+    my $name = shift;                # Name of data to return
+
+    return $self->{data} if (not defined $name);
+    return $self->{data}->{$name};
+}
+
+
+1;
+
+__END__;
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Config/t/00_distribution.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Config/t/00_distribution.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Config/t/00_distribution.t	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 00_distribution.t,v 1.1 2006-09-06 01:59:09 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib ./t );
+
+use Test::More;
+
+# example taken from Test::Distribution Pod
+
+BEGIN {
+    eval {
+        require Test::Distribution;
+    };
+    if($@) {
+        plan skip_all => 'Test::Distribution not installed';
+    } else {
+        import Test::Distribution; # not => qw( podcover );
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Config/test/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Config/test/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Config/test/test.pl	(revision 22322)
@@ -0,0 +1,36 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use PS::IPP::Config qw( caturi );
+
+print ( caturi("path://PATH", "file") . "\n");
+print ( caturi("path://PATH/", "file") . "\n");
+print ( caturi("path://PATH/something", "file") . "\n");
+print ( caturi("path://PATH/something/", "file") . "\n");
+print ( caturi("neb://", "file") . "\n");
+print ( caturi("neb://PATH", "file") . "\n");
+print ( caturi("neb://PATH/", "file") . "\n");
+print ( caturi("neb://PATH/something", "file") . "\n");
+print ( caturi( "/PATH", "file") . "\n");
+print ( caturi("/PATH/", "file") . "\n");
+print ( caturi( "/PATH/something", "file") . "\n");
+print ( caturi("/PATH/something/", "file") . "\n");
+
+print "\n";
+
+my $ipprc = PS::IPP::Config->new();
+
+print ( $ipprc->convert_filename_absolute("path://SIMTEST") . "\n");
+print ( $ipprc->convert_filename_absolute("path://SIMTEST/") . "\n");
+print ( $ipprc->convert_filename_absolute("path://SIMTEST/something") . "\n");
+print ( $ipprc->convert_filename_absolute("path://SIMTEST/something/") . "\n");
+
+print "\n";
+
+print ( $ipprc->convert_filename_relative("/data/mithrandir.2/price/temp") . "\n");
+print ( $ipprc->convert_filename_relative("/data/mithrandir.2/price/temp/") . "\n");
+print ( $ipprc->convert_filename_relative("/data/mithrandir.2/price/temp/something") . "\n");
+print ( $ipprc->convert_filename_relative("/data/mithrandir.2/price/temp/something/") . "\n");
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Build
+META.yml
+Makefile
+Makefile.PL
+_build
+blib
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/Build.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/Build.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/Build.PL	(revision 22322)
@@ -0,0 +1,25 @@
+use Module::Build;
+# See perldoc Module::Build for details of how this works
+
+Module::Build->new(
+    module_name         => 'PS::IPP::MetaDB',
+    dist_version_from   => 'lib/PS/IPP/MetaDB.pm',
+    author              => 'Joshua Hoblitt <jhoblitt@cpan.org>',
+    license             => 'gpl',
+    create_makefile_pl  => 'passthrough',
+    requires        => {
+        'PS::IPP::PSFTP'    => 0,
+    },
+    build_requires  => {
+        'Class::DBI::mysql' => 0,
+        'Text::CSV'         => 0,
+    },
+    recommends      => {
+        'Test::Pod'         => '1.00',
+    },
+    script_files    => [qw(
+        scripts/mdb-add-new
+        scripts/mdb-find-new
+        scripts/mdb-new2newed
+    )],
+)->create_build_script;
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/Changes
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/Changes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/Changes	(revision 22322)
@@ -0,0 +1,6 @@
+Revision history for Perl module PS::IPP::MetaDB
+
+0.01 Tue Sep 20 12:20:39 2005
+    - original version; created by ExtUtils::ModuleMaker 0.41
+
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/LICENSE	(revision 22322)
@@ -0,0 +1,260 @@
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/MANIFEST
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/MANIFEST	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/MANIFEST	(revision 22322)
@@ -0,0 +1,15 @@
+Build.PL
+Changes
+LICENSE
+MANIFEST
+META.yml
+Makefile.PL
+README
+Todo
+lib/PS/IPP/MetaDB.pm
+scripts/mdb-add-new
+scripts/mdb-find-new
+scripts/mdb-new2newed
+t/00_distribution.t
+t/01_load.t
+t/02_mdb-add-new.t
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/README	(revision 22322)
@@ -0,0 +1,15 @@
+pod2text PS::IPP::MetaDB.pm > README
+
+If this is still here it means the programmer was too lazy to create the readme file.
+
+You can create it now by using the command shown above from this directory.
+
+At the very least you should be able to use this set of instructions
+to install the module...
+
+perl Build.PL
+./Build
+./Build test
+./Build install
+
+If you are on a windows box you should use 'nmake' rather than 'make'.
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/Todo
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/Todo	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/Todo	(revision 22322)
@@ -0,0 +1,5 @@
+TODO list for Perl module PS::IPP::MetaDB
+
+- Nothing yet
+
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/lib/PS/IPP/MetaDB.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/lib/PS/IPP/MetaDB.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/lib/PS/IPP/MetaDB.pm	(revision 22322)
@@ -0,0 +1,614 @@
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: MetaDB.pm,v 1.14 2006-01-21 02:39:05 jhoblitt Exp $
+
+package PS::IPP::MetaDB;
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION ); 
+$VERSION = '0.01';
+
+use base qw( Class::DBI::mysql );
+
+use Carp qw( croak );
+
+sub import {
+    my $class = shift;
+
+    $class->init( @_ ) if @_;
+}
+
+my @child_inits;
+
+sub init {
+    my $class = shift;
+    my %p = @_;
+    
+    croak "dsn is required" unless exists $p{dsn};
+
+    $class->set_db('Main', $p{dsn}, $p{user}, $p{passwd});
+
+    foreach my $init (@child_inits) {
+        &$init;
+    }
+}
+
+sub register_init {
+    my $class = shift;
+    push @child_inits, shift;
+}
+
+# lifted from Class::DBI docs (modified)
+sub do_transaction {
+    my $class = shift;
+    my ( $code ) = @_;
+
+    # Turn off AutoCommit for this scope.
+    # A commit will occur at the exit of this block automatically,
+    # when the local AutoCommit goes out of scope.
+    local $class->db_Main->{ AutoCommit };
+
+    # Execute the required code inside the transaction.
+    eval { $code->() };
+    if ( $@ ) {
+        my $commit_error = $@;
+        eval { $class->dbi_rollback }; # might also die!
+        croak $commit_error;
+    }
+}
+
+sub add_fetch {
+    my $class = shift;
+    my %p = @_;
+
+    PS::IPP::MetaDB::Image::Fetch->create({
+        exp_id      => $p{exp_id},
+        class       => $p{class},
+        class_id    => $p{class_id},
+        camera      => $p{camera},
+        stamp       => $p{stamp},
+        file_id     => $p{file_id},
+        size        => $p{size},
+        md5         => $p{md5},
+        url         => $p{url},
+    });
+}
+
+sub fetch_fields
+{
+    return PS::IPP::MetaDB::Image::Raw->fields;
+}
+
+sub find_fetch {
+    my $class = shift;
+    my %p = @_;
+
+    my @rows;
+    if (exists $p{exp_id}) {
+        @rows = PS::IPP::MetaDB::Image::Fetch ->search(
+            exp_id => $p{exp_id},
+            { order_by => 'class_id' },
+        );
+    } else {
+        @rows = PS::IPP::MetaDB::Image::Fetch->retrieve_all;
+    }
+
+    my @fields = fetch_fields();
+    my @result;
+    foreach my $obj (@rows) {
+        my %hash;
+        @hash{@fields} = $obj->get(@fields);
+        push(@result, \%hash);
+    }
+
+    return @result;
+}
+
+sub fetch2raw {
+    my $class = shift;
+    my %p = @_;
+
+    $class->do_transaction(sub { $class->_fetch2raw(%p) });
+}
+
+sub _fetch2raw {
+    my $class = shift;
+    my %p = @_;
+
+    my $fetch = PS::IPP::MetaDB::Image::fetch->search(
+        exp_id      => $p{exp_id},
+        class       => $p{class},
+        class_id    => $p{class_id},
+    );
+
+    PS::IPP::MetaDB::Image::NotifyReceived->create({
+        file_id => $fetch->file_id,
+    });
+
+    PS::IPP::MetaDB::Image::Raw->create({
+        exp_id      => $fetch->exp_id,
+        class       => $fetch->class,
+        class_id    => $fetch->class_id,
+        camera      => $fetch->camera,
+        # stamp should auto-update
+        file_id     => $fetch->file_id,
+        size        => $fetch->size,
+        md5         => $fetch->md5,
+        # new storage url
+        url         => $p{url},
+    });
+
+    $fetch->delete;
+}
+
+
+#package PS::IPP::MetaDB::Image::Prepared;
+#package PS::IPP::MetaDB::Image::Reduced;
+#package PS::IPP::MetaDB::Image::Analyzed;
+#package PS::IPP::MetaDB::Image::Combined;
+
+=pod
+
+=head2 Temporal Queues
+
+These queues all describe work that I<needs> to be performed but hasn't
+happened yet.
+
+=over 4
+
+=item * image_fetch
+
+This table contains a list of new data waiting to be downloaded from the
+summit.  The location of fetched data files is written into the C<raw> table.
+
+Table description:
+
+=over 4
+
+=item * exp_id
+
+A unique exposure ID #.
+
+=item * class
+
+The 'type' of data this is.
+
+=item * class_id
+
+A unique identifier for data of the same class in the same exposure.
+
+=item * camera
+
+Camera that this data was acquired from.
+
+=item * stamp
+
+Date/Time stamp of file when the file became available.
+
+=item * file_id
+
+OTIS's ID string for the file, i.e. it's filename.
+
+=item * size
+
+The size of the file in bytes.
+
+=item * md5
+
+The md5 checksum of the file in hexadecimal form (ASCII).
+
+=item * url
+
+URL to retrieve data from.
+
+=back
+
+=cut
+
+package PS::IPP::MetaDB::Image::Fetch;
+
+use base qw( PS::IPP::MetaDB );
+
+__PACKAGE__->register_init(\&init);
+
+sub init
+{
+    __PACKAGE__->table("image_fetch");
+    __PACKAGE__->create_table(q{
+        exp_id      BIGINT          NOT NULL PRIMARY KEY,
+        class       VARCHAR(255),
+        class_id    BIGINT          NOT NULL,
+        camera      VARCHAR(255),
+        stamp       DATETIME,
+        file_id     VARCHAR(255),
+        size        INT,
+        md5         VARCHAR(32),
+        url         VARCHAR(255)    NOT NULL
+    });
+    __PACKAGE__->set_up_table;
+}
+
+sub fields
+{
+    return qw( exp_id class class_id camera stamp file_id size md5 url );
+}
+
+=item * notify_received
+
+XXX no longer needed?
+
+This is a queue of pending file received notifications that need to be send to
+OTIS.  Once a notification is successfully sent to OTIS the entry is removed
+from this queue.
+
+=over 4
+
+=item * file_id
+
+OTIS's ID string for the file, i.e. it's filename.
+
+=back
+
+=cut
+
+package PS::IPP::MetaDB::Image::NotifyReceived;
+
+use base qw( PS::IPP::MetaDB );
+
+__PACKAGE__->register_init(\&init);
+
+sub init
+{
+    __PACKAGE__->table("notify_received");
+    __PACKAGE__->create_table(q{
+        file_id      BIGINT          NOT NULL PRIMARY KEY,
+    });
+    __PACKAGE__->set_up_table;
+}
+
+=item * metadata_fetch
+
+This table contains a list of new metadata waiting to be downloaded from the
+summit.  XXX Are these files written to disk or just parsed in memory?
+
+Table description:
+
+=over 4
+
+=item * stamp
+
+Date/Time stamp of file when the file became available.
+
+=item * file_id
+
+OTIS's ID string for the file, i.e. it's filename.
+
+=item * size
+
+The size of the file in bytes.
+
+=item * md5
+
+The md5 checksum of the file in hexadecimal form (ASCII).
+
+=item * url
+
+URL to retrieve data from.
+
+=back
+
+=cut
+
+package PS::IPP::MetaDB::Metadata::Fetch;
+
+use base qw( PS::IPP::MetaDB );
+
+__PACKAGE__->register_init(\&init);
+
+sub init
+{
+    __PACKAGE__->table("metadata_fetch");
+    __PACKAGE__->create_table(q{
+        stamp       DATETIME,
+        file_id     VARCHAR(255)    NOT NULL PRIMARY KEY,
+        size        INT,
+        md5         VARCHAR(32),
+        url         VARCHAR(255)    NOT NULL
+    });
+    __PACKAGE__->set_up_table;
+}
+
+=item * exposure_prepare
+
+Phase 1: A list of exposure IDs to extra metadata from.
+
+=over 4
+
+=item * exp_id
+
+exposure ID #, keyed against the C<raw> table.
+
+=back
+
+=cut
+
+package PS::IPP::MetaDB::Exposure::Prepare;
+
+use base qw( PS::IPP::MetaDB );
+
+__PACKAGE__->register_init(\&init);
+
+sub init
+{
+    __PACKAGE__->table("exposure_prepare");
+    __PACKAGE__->create_table(q{
+        exp_id      BIGINT          NOT NULL PRIMARY KEY,
+    });
+    __PACKAGE__->set_up_table;
+}
+
+=item * image_reduce
+
+Phase 2: A list of exposure components to reduce.
+
+=over 4
+
+=item * exp_id
+
+exposure ID #, keyed against the C<prepared> table.
+
+=item * class_id
+
+class ID #, keyed against the C<prepared> table.
+
+=item * prepare_version
+
+version #, keyed against the C<prepared> table.
+
+=back
+
+=item * exposure_mosaic
+
+Phase 3: A list of exposures to process.
+
+=over 4
+
+=item * exp_id
+
+exposure ID #, keyed against the C<reduced> table.
+
+=item * exp_config
+
+Class ID #, keyed against the C<exposure_config> table.
+
+=back
+
+=item * combine
+
+=over 4
+
+=item * run_id???
+
+=back
+
+=back
+
+=head2 Metadata
+
+These tables describe data in other tables.
+
+=over 4
+
+=item * exposure
+
+A list of ALL exposure ID #s.
+
+=over 4
+
+=item * exp_id
+
+exposure ID #.
+
+=item * camera
+
+Name of the camera the generated the exposure.
+
+=item * type
+
+The type of exposure taken.
+
+=item * n_components
+
+The number of file that make of the complete exposure.
+
+=back
+
+=item * exposure_component
+
+A list of the files that together make up a complete exposure.
+
+=over 4
+
+=item * exp_id
+
+exposure ID #, keyed against the C<exposure> table.
+
+=item * class_id
+
+class ID #.
+
+=item * class
+
+The type of component this is
+
+=back
+
+=item * exposure_config
+
+This table provides a way to configure which 'version' of components of an
+'exposure' to use.
+
+=over 4
+
+=item * exp_config
+
+exposure config ID #.
+
+=item * class_id
+
+class ID #, keyed against the C<exposure_component> table.
+
+=item * version
+
+version ID #, keyed against the C<exposure_component> table.
+
+=item * n_components
+
+=back
+
+=item * exposure_reduced
+
+A list of version ID #'s assigned after reducing data.  This table is used to
+make sure that all components of an exposure get the same version ID assigned
+to them.
+
+=over 4
+
+=item * exp_id
+
+exposure ID #, keyed on the C<exposure> table.
+
+=item * reduced_version
+
+Version ID # used.
+
+=back
+
+=back
+
+=head2 File Storage
+
+These tables describe data actually stored on disk.
+
+=over 4
+
+=item * raw 
+
+Note that there is only one 'version' of the original data.
+
+=over 4
+
+=item * exp_id
+
+exposure ID #, keyed on the C<exposure_component> table.
+
+=item * class
+
+The 'type' of data this is.
+
+=item * class_id
+
+class ID #, keyed on the C<exposure_componet> table.
+
+=item * url
+
+url to the storage location of the file.
+
+=back
+
+=cut
+
+package PS::IPP::MetaDB::Image::Raw;
+
+use base qw( PS::IPP::MetaDB );
+
+__PACKAGE__->register_init(\&init);
+
+sub init
+{
+    __PACKAGE__->table("raw");
+    __PACKAGE__->create_table(q{
+        exp_id      BIGINT          NOT NULL PRIMARY KEY,
+        class       VARCHAR(256)    NOT NULL,
+        class_id    BIGINT          NOT NULL,
+        camera      VARCHAR(255),
+        stamp       DATETIME,
+        file_id     VARCHAR(255),
+        size        INT,
+        md5         VARCHAR(32),
+        url         VARCHAR(255)    NOT NULL
+    });
+    __PACKAGE__->set_up_table;
+}
+
+
+=item * prepared
+
+Data that has been through Phase: 0.
+
+=over 4
+
+=item * exp_id
+
+exposure ID #, keyed on the C<exposure_component> table.
+
+=item * class_id
+
+class ID #, keyed on the C<exposure_componet> table.
+
+=item * prepare_version
+
+prepare step version ID #
+
+
+=item * url
+
+url to the storage location of the file.
+
+=back
+
+=item * reduced
+
+Data that's been through Phase: 2.
+
+=over 4
+
+=item * exp_id
+
+exposure ID #, keyed on the C<exposure_component> table.
+
+=item * class_id
+
+class ID #, keyed on the C<exposure_componet> table.
+
+=item * prepare_version
+
+prepare step version ID #
+
+=item * reduce_version
+
+reduce step version ID #
+
+=item * url
+
+url to the storage location of the file.
+
+=back
+
+=item * mosaiced
+
+???
+
+=item * combined
+
+???
+
+=back
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-add-new
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-add-new	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-add-new	(revision 22322)
@@ -0,0 +1,163 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: mdb-add-new,v 1.6 2005-09-30 03:32:21 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use PS::IPP::MetaDB;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($exp_id, $class, $class_id, $camera, $stamp, $file_id, $size, $md5, $url);
+my ($dsn, $dbuser, $dbpass);
+
+GetOptions(
+    'dsn=s'     => \$dsn,
+    'user=s'    => \$dbuser,
+    'pass=s'    => \$dbpass,
+    'exp_id=s'  => \$exp_id,
+    'class=s'   => \$class,
+    'class_id=s'=> \$class_id,
+    'camera=s'  => \$camera,
+    'stamp=s'   => \$stamp,
+    'file_id=s' => \$file_id,
+    'size=s'    => \$size,
+    'md5=s'     => \$md5,
+    'url=s'     => \$url,
+) || pod2usage( 2 );
+
+$dsn    = $ENV{'MDB_DSN'} unless $dsn;
+$dbuser = $ENV{'MDB_USER'} unless $dbuser;
+$dbpass = $ENV{'MDB_PASS'} unless $dbpass;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --dsn --user", -exitval => 3 )
+    unless defined $dsn and defined $dbuser;
+pod2usage(
+    -msg => "Required options: --exp_id --class --class_id --url",
+    -exitval => 4,
+) unless defined $exp_id and defined $class 
+    and defined $class_id and defined $url;
+
+PS::IPP::MetaDB->init(
+    dsn     => $dsn,
+    user    => $dbuser,
+    passwd  => $dbpass,
+);
+
+PS::IPP::MetaDB->add_new(
+    exp_id      => $exp_id,
+    class       => $class,
+    class_id    => $class_id,
+    camera      => $camera,
+    stamp       => $stamp,
+    file_id     => $file_id,
+    size        => $size,
+    md5         => $md5,
+    url         => $url,
+);
+
+__END__
+
+=pod
+
+=head1 NAME
+
+mdb-add-new - register a new file as available for download
+
+=head1 SYNOPSIS
+
+    mdb-add-new [--db <database>] [--user <username>] [--pass <password>]
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --db|-d <database>
+
+Name of database (C<namespace>) to create tables in.
+
+Optional if the appropriate environment variable is set.
+
+=item * --user|-u <username>
+
+Username to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --pass|-p <password>
+
+Password to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<MDB_DB>
+
+Equivalent to --db|-d
+
+=item * C<MDB_USER>
+
+Equivalent to --user|-u
+
+=item * C<MDB_PASS>
+
+Equivalent to --pass|-p
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<PS::IPP::MetaDB>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-add-new-from-psftp
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-add-new-from-psftp	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-add-new-from-psftp	(revision 22322)
@@ -0,0 +1,168 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: mdb-add-new-from-psftp,v 1.2 2005-10-14 23:28:06 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use PS::IPP::MetaDB;
+use PS::IPP::PSFTP;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($base_url, $recipient);
+my ($dsn, $dbuser, $dbpass);
+
+GetOptions(
+    'dsn=s'         => \$dsn,
+    'user=s'        => \$dbuser,
+    'pass=s'        => \$dbpass,
+    'base_url=s'    => \$base_url,
+    'recipient=s'   => \$recipient,
+) || pod2usage( 2 );
+
+$dsn    = $ENV{'MDB_DSN'} unless $dsn;
+$dbuser = $ENV{'MDB_USER'} unless $dbuser;
+$dbpass = $ENV{'MDB_PASS'} unless $dbpass;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --dsn --user", -exitval => 3 )
+    unless defined $dsn and defined $dbuser;
+pod2usage(
+    -msg => "Required options: --base_url --recipient",
+    -exitval => 4,
+) unless defined $base_url and defined $recipient;
+
+my $psftp = PS::IPP::PSFTP->new(
+    base_url    => $base_url,
+    recipient   => $recipient,
+);
+
+# do some sort of error checking
+my $filelist = $psftp->files_available;
+
+PS::IPP::MetaDB->init(
+    dsn     => $dsn,
+    user    => $dbuser,
+    passwd  => $dbpass,
+);
+
+foreach my $file (@$filelist) {
+    next unless $file->type eq 'expsoure';
+
+    PS::IPP::MetaDB->add_new(
+        exp_id      => $file->attributes->{exp_id},
+        class       => $file->attributes->{class},
+        class_id    => $file->attributes->{class_id},
+        camera      => $file->attributes->{camera},
+        stamp       => $file->stamp,
+        file_id     => $file->file_id,
+        size        => $file->size,
+        md5         => $file->md5,
+        url         => $file->url,
+    );
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+mdb-add-new - register a new file as available for download
+
+=head1 SYNOPSIS
+
+    mdb-add-new [--db <database>] [--user <username>] [--pass <password>]
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --db|-d <database>
+
+Name of database (C<namespace>) to create tables in.
+
+Optional if the appropriate environment variable is set.
+
+=item * --user|-u <username>
+
+Username to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --pass|-p <password>
+
+Password to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<MDB_DB>
+
+Equivalent to --db|-d
+
+=item * C<MDB_USER>
+
+Equivalent to --user|-u
+
+=item * C<MDB_PASS>
+
+Equivalent to --pass|-p
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<PS::IPP::MetaDB>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-find-new
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-find-new	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-find-new	(revision 22322)
@@ -0,0 +1,161 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: mdb-find-new,v 1.4 2005-09-30 21:46:55 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use PS::IPP::MetaDB;
+use Text::CSV;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($exp_id, $class, $class_id, $url);
+my ($dsn, $dbuser, $dbpass);
+
+GetOptions(
+    'dsn=s'     => \$dsn,
+    'user=s'    => \$dbuser,
+    'pass=s'    => \$dbpass,
+    'exp_id=s'  => \$exp_id,
+    'class=s'   => \$class,
+    'class_id=s'=> \$class_id,
+) || pod2usage( 2 );
+
+$dsn    = $ENV{'MDB_DSN'} unless $dsn;
+$dbuser = $ENV{'MDB_USER'} unless $dbuser;
+$dbpass = $ENV{'MDB_PASS'} unless $dbpass;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --dsn --user", -exitval => 3 )
+    unless defined $dsn and defined $dbuser;
+
+PS::IPP::MetaDB->init(
+    dsn     => $dsn,
+    user    => $dbuser,
+    passwd  => $dbpass,
+);
+
+my @rows;
+if (defined $exp_id) {
+    @rows = PS::IPP::MetaDB->find_new(
+        exp_id      => $exp_id,
+        class       => $class,
+        class_id    => $class_id,
+    );
+} else {
+    @rows = PS::IPP::MetaDB->find_new();
+}
+
+my @fields = PS::IPP::MetaDB->new_fields; 
+my $csv = Text::CSV->new();
+
+print "# ", join(", ", @fields), "\n" if scalar @rows;
+foreach my $hashref (@rows) {
+    $csv->combine(@$hashref{@fields});
+    print $csv->string, "\n";
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+mdb-find-new - list new files avaiable for download
+
+=head1 SYNOPSIS
+
+    mdb-find-new [--db <database>] [--user <username>] [--pass <password>]
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --db|-d <database>
+
+Name of database (C<namespace>) to create tables in.
+
+Optional if the appropriate environment variable is set.
+
+=item * --user|-u <username>
+
+Username to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --pass|-p <password>
+
+Password to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<MDB_DB>
+
+Equivalent to --db|-d
+
+=item * C<MDB_USER>
+
+Equivalent to --user|-u
+
+=item * C<MDB_PASS>
+
+Equivalent to --pass|-p
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<PS::IPP::MetaDB>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-new2newed
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-new2newed	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/scripts/mdb-new2newed	(revision 22322)
@@ -0,0 +1,150 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: mdb-new2newed,v 1.4 2005-10-14 23:46:56 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use PS::IPP::MetaDB;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($exp_id, $class, $class_id, $url);
+my ($dsn, $dbuser, $dbpass);
+
+GetOptions(
+    'dsn=s'     => \$dsn,
+    'user=s'    => \$dbuser,
+    'pass=s'    => \$dbpass,
+    'exp_id=s'  => \$exp_id,
+    'class=s'   => \$class,
+    'class_id=s'=> \$class_id,
+    'url=s'     => \$url,
+) || pod2usage( 2 );
+
+$db     = $ENV{'MDB_DB'} unless $db;
+$dbuser = $ENV{'MDB_USER'} unless $dbuser;
+$dbpass = $ENV{'MDB_PASS'} unless $dbpass;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --dsn --user", -exitval => 2 )
+    unless defined $dsn and defined $dbuser;
+pod2usage( -msg => "Required options: --exp_id --class --class_id --url", -exitval => 2)
+    unless defined $exp_id and defined $class and defined $class_id and defined $url;
+
+PS::IPP::MetaDB->init(
+    dsn     => $dsn,
+    user    => $dbuser,
+    passwd  => $dbpass,
+);
+
+PS::IPP::MetaDB->new2newed(
+    exp_id      => $exp_id,
+    class       => $class,
+    class_id    => $class_id,
+    url         => $url,
+);
+
+__END__
+
+=pod
+
+=head1 NAME
+
+mdb-new2newed - register a file as successfully downloaded
+
+=head1 SYNOPSIS
+
+    mdb-new2newed [--db <database>] [--user <username>] [--pass <password>]
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --db|-d <database>
+
+Name of database (C<namespace>) to create tables in.
+
+Optional if the appropriate environment variable is set.
+
+=item * --user|-u <username>
+
+Username to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --pass|-p <password>
+
+Password to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<MDB_DB>
+
+Equivalent to --db|-d
+
+=item * C<MDB_USER>
+
+Equivalent to --user|-u
+
+=item * C<MDB_PASS>
+
+Equivalent to --pass|-p
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<PS::IPP::MetaDB>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/00_distribution.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/00_distribution.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/00_distribution.t	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 00_distribution.t,v 1.1 2005-10-14 21:33:44 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib ./t );
+
+use Test::More;
+
+# example taken from Test::Distribution Pod
+
+BEGIN {
+    eval {
+        require Test::Distribution;
+    };
+    if($@) {
+        plan skip_all => 'Test::Distribution not installed';
+    } else {
+        import Test::Distribution not => qw( podcover );
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/01_load.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/01_load.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/01_load.t	(revision 22322)
@@ -0,0 +1,7 @@
+# -*- perl -*-
+
+# t/001_load.t - check module loading and create testing directory
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'PS::IPP::MetaDB' ); }
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/02_mdb-add-new.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/02_mdb-add-new.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/02_mdb-add-new.t	(revision 22322)
@@ -0,0 +1,104 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 02_mdb-add-new.t,v 1.1 2005-09-30 03:29:17 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use Test::Cmd;
+use Test::More tests => 29;
+
+use Env qw( MDB_DSN MDB_USER MDB_PASS );
+
+local $MDB_DSN;
+local $MDB_USER;
+local $MDB_PASS;
+
+my $test = Test::Cmd->new(prog => 'scripts/mdb-add-new', workdir => '');
+isa_ok($test, 'Test::Cmd');
+
+{
+    $test->run(args => '');
+    missing_args(3, "Required options: --dsn --user");
+}
+
+{
+    local $MDB_DSN  = "foo";
+
+    $test->run(args => '');
+    missing_args(3, "Required options: --dsn --user");
+}
+
+{
+    local $MDB_USER = "foo";
+
+    $test->run(args => '');
+    missing_args(3, "Required options: --dsn --user");
+}
+
+{
+    local $MDB_PASS = "foo";
+
+    $test->run(args => '');
+    missing_args(3, "Required options: --dsn --user");
+}
+
+{
+    local $MDB_DSN  = 'foo';
+    local $MDB_USER = 'bar';
+
+    $test->run(args => '');
+    missing_args(4, "Required options: --exp_id --class --class_id --url");
+}
+
+$test->run(args => '--dsn foo');
+missing_args(3, "Required options: --dsn --user");
+
+$test->run(args => '--user foo');
+missing_args(3, "Required options: --dsn --user");
+
+$test->run(args => '--pass foo');
+missing_args(3, "Required options: --dsn --user");
+
+$test->run(args => '--dsn foo --user bar');
+missing_args(4, "Required options: --exp_id --class --class_id --url");
+
+local $MDB_DSN  = "DBI:mysql:test";
+local $MDB_USER = "test";
+
+$test->run(args => '--exp_id 1');
+missing_args(4, "Required options: --exp_id --class --class_id --url");
+
+$test->run(args => '--exp_id 1 --class 2');
+missing_args(4, "Required options: --exp_id --class --class_id --url");
+
+$test->run(args => '--exp_id 1 --class 2 --class_id 3');
+missing_args(4, "Required options: --exp_id --class --class_id --url");
+
+reset_db();
+$test->run(args => '--exp_id 1 --class 2 --class_id 3 --url http://example/');
+is($? >> 8, 0, "all manditory options");
+is($test->stderr, "", "no output to stderr");
+
+reset_db();
+$test->run(args => '--exp_id 1 --class 2 --class_id 3 --camera tc3 --stamp 2005-09-29T17:27:00Z --file_id foo.fits --size 42 --md5 768d2bcf7deba710d75a71c53470a182 --url http://example/');
+is($? >> 8, 0, "all manditory + optional options");
+is($test->stderr, "", "no output to stderr");
+
+END { reset_db() }
+
+sub missing_args
+{
+    my ($exit, $errstr) = @_;
+
+    is($? >> 8, $exit, "error code is: $exit");
+    like($test->stderr, qr/$errstr/, "error string is: $errstr");
+}
+
+sub reset_db
+{
+    use PS::IPP::MetaDB dsn => "DBI:mysql:test", user => "test";
+    PS::IPP::MetaDB::Image::New->drop_table();
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/03_mdb-find-new.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/03_mdb-find-new.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-MetaDB/t/03_mdb-find-new.t	(revision 22322)
@@ -0,0 +1,87 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 03_mdb-find-new.t,v 1.2 2005-10-14 21:33:44 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use Test::Cmd;
+use Test::More tests => 21;
+
+use Env qw( MDB_DSN MDB_USER MDB_PASS );
+
+local $MDB_DSN;
+local $MDB_USER;
+local $MDB_PASS;
+
+my $test = Test::Cmd->new(prog => 'scripts/mdb-find-new', workdir => '');
+isa_ok($test, 'Test::Cmd');
+
+{
+    $test->run(args => '');
+    missing_args(3, "Required options: --dsn --user");
+}
+
+{
+    local $MDB_DSN  = "foo";
+
+    $test->run(args => '');
+    missing_args(3, "Required options: --dsn --user");
+}
+
+{
+    local $MDB_USER = "foo";
+
+    $test->run(args => '');
+    missing_args(3, "Required options: --dsn --user");
+}
+
+{
+    local $MDB_PASS = "foo";
+
+    $test->run(args => '');
+    missing_args(3, "Required options: --dsn --user");
+}
+
+$test->run(args => '--dsn foo');
+missing_args(3, "Required options: --dsn --user");
+
+$test->run(args => '--user foo');
+missing_args(3, "Required options: --dsn --user");
+
+$test->run(args => '--pass foo');
+missing_args(3, "Required options: --dsn --user");
+
+$MDB_DSN  = "DBI:mysql:test";
+$MDB_USER = "test";
+
+$test->run(args => '');
+is($? >> 8, 0, "no options");
+is($test->stderr, "", "no output to stderr");
+
+$test->run(args => '--dsn $MDB_DSN --user $MDB_USER');
+is($? >> 8, 0, "no options");
+is($test->stderr, "", "no output to stderr");
+
+$test->run(args => '--exp_id 1 --class 2 --class_id 3');
+is($? >> 8, 0, "all manditory options");
+is($test->stderr, "", "no output to stderr");
+
+END { reset_db() }
+
+sub missing_args
+{
+    my ($exit, $errstr) = @_;
+
+    is($? >> 8, $exit, "error code is: $exit");
+    like($test->stderr, qr/$errstr/, "error string is: $errstr");
+}
+
+sub reset_db
+{
+        use PS::IPP::MetaDB dsn => "DBI:mysql:test", user => "test";
+            PS::IPP::MetaDB::Image::New->drop_table();
+}
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Build
+META.yml
+Makefile
+Makefile.PL
+_build
+blib
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/Build.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/Build.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/Build.PL	(revision 22322)
@@ -0,0 +1,43 @@
+use Module::Build;
+# See perldoc Module::Build for details of how this works
+
+my $class = Module::Build->subclass(code => <<'EOF');
+use File::Copy;
+use File::stat;
+
+my $grammar = "config_grammar.txt";
+my $parser  = "lib/PS/IPP/Metadata/Parser.pm";
+
+sub ACTION_code {
+    my $self = shift;
+
+    if (!-e $parser || (stat($grammar)->mtime > stat($parser)->mtime)) {
+        system("perl -MParse::RecDescent - $grammar PS::IPP::Metadata::Parser") == 0
+            or die "Parse::RecDecent code gen failed: $?";
+        move("Parser.pm", $parser) or die "move failed: $!";
+    }
+
+    $self->SUPER::ACTION_code(@_);
+}
+EOF
+
+$class->new(
+    module_name         => 'PS::IPP::Metadata::Config',
+    dist_version_from   => 'lib/PS/IPP/Metadata/Config.pm',
+    author              => 'Joshua Hoblitt <jhoblitt@cpan.org>',
+    license             => 'gpl',
+    create_makefile_pl  => 'passthrough',
+    requires            =>  {
+        'Carp'                      => 0,
+        'Class::Accessor::Fast'     => '0.19',
+        'Params::Validate'          => '0.72',
+        'DateTime::Format::ISO8601' => '0.0402',
+        'Parse::RecDescent'         => '1.94',
+    },
+    recommends          => {
+        'Test::Distribution'    => '1.22',
+    },
+    script_files        => [qw(
+        scripts/mdc-dump
+    )],
+)->create_build_script;
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/Changes
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/Changes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/Changes	(revision 22322)
@@ -0,0 +1,61 @@
+Revision history for Perl module PS::IPP::Metadata::Config
+
+1.01 Fri Nov  9 14:50:58 HST 2007
+    - report an error with line # when eof not parsed
+    - remove mdc-dump --file option flag
+
+1.00 Fri Feb  2 21:51:20 HST 2007
+    - version bump only
+
+0.11 Thu Feb  1 15:42:28 HST 2007
+    - add t/00_distribution.t
+
+0.10 Fri Nov 24 17:11:13 HST 2006
+    - fix handling of NULL strings
+    - merge the 'time' production into the 'scalar' production for a ~20% speed boost 
+    - remove support for "epoch format" times
+    - additional metadata structure tests
+    - merge metadata_name & metadata_end into metdata
+    - merge empty metdata case into main metadata production
+    - error check vector
+    - change type & vtype into regex alternations instead of rule alternations
+
+0.09 Thu Oct 26 16:16:28 HST 2006
+    - fix a bug in Build.PL where a rebuilt grammar was not installed
+    - fix time tests that were dependent on MULTI times being broken
+    - fix MULTI time
+    - no longer require the 'Z' and the end of ISO8601 times
+    - implement NULL time support
+
+0.08 Wed Oct 11 16:09:26 HST 2006
+    - add support for empty METADATAs
+
+0.07 Tue Oct 10 17:09:28 HST 2006
+    - fix MULTI parsing of TYPEs
+    - minor test update
+
+0.06 Tue Oct  3 15:25:44 HST 2006
+    - fix Build.PL script to not throw and error when $parser does not exist
+    - fix the trailing white space left in string values when followed by a
+      comment
+
+0.05 Mon Sep 25 12:06:22 HST 2006
+    - fix a bug in the manifest
+    - fix Build.PL so the parser is only regenerated when the grammar has been
+      changed
+
+0.04 Mon Sep 25 11:50:20 HST 2006
+    - add fractional second support to the ISO8601 format
+
+0.03 Fri Sep 22 14:27:14 HST 2006
+    - add float nan/inf support
+
+0.02 Thu Aug 24 17:08:14 HST 2006
+    - split grammar out of PS::IPP::Metadata::Config into grammar_config.txt
+    - precompile the parser as PS::IPP::Metadata::Pasrser
+    - change PS::IPP::Metadata::Config to use PS::IPP::Metadata::Pasrser
+
+0.01 Tue Feb 22 15:46:35 2005
+	- original version; created by ExtUtils::ModuleMaker 0.32
+
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/LICENSE	(revision 22322)
@@ -0,0 +1,260 @@
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/MANIFEST
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/MANIFEST	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/MANIFEST	(revision 22322)
@@ -0,0 +1,21 @@
+Build.PL
+Changes
+LICENSE
+MANIFEST
+README
+Todo
+config_grammar.txt
+docs/sdrs_grammar.txt
+lib/PS/IPP/Metadata/Config.pm
+lib/PS/IPP/Metadata/Config.pod
+lib/PS/IPP/Metadata/Parser.pm
+scripts/mdc-dump
+t/00_distribution.t
+t/01_load.t
+t/02_examples.t
+t/03_metadata.t
+t/04_type.t
+t/05_time.t
+t/06_multi.t
+t/07_floats.t
+t/08_strings.t
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/README	(revision 22322)
@@ -0,0 +1,16 @@
+pod2text PS::IPP::Metadata::Config.pm > README
+
+If this is still here it means the programmer was too lazy to create the readme file.
+
+You can create it now by using the command shown above from this directory.
+
+At the very least you should be able to use this set of instructions
+to install the module...
+
+perl Build.PL
+./Build
+./Build test
+./Build install
+
+
+If you are on a windows box you should use 'nmake' rather than 'make'.
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/Todo
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/Todo	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/Todo	(revision 22322)
@@ -0,0 +1,5 @@
+TODO list for Perl module PS::IPP::Metadata::Config
+
+- Nothing yet
+
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/config_grammar.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/config_grammar.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/config_grammar.txt	(revision 22322)
@@ -0,0 +1,355 @@
+# Copyright (c) 2005  Joshua Hoblitt
+#
+# $Id: config_grammar.txt,v 1.17 2008-02-19 23:35:38 jhoblitt Exp $
+
+{
+    use strict;
+
+    use Carp qw( carp );
+    use DateTime::Format::ISO8601;
+
+    our @scope_stack;
+}
+
+startrule: grammar <commit> eofile
+    { $item{grammar} }
+    | <error: of metadata file : could not parse beyond this point>
+
+grammar:
+    statement(s)
+        {
+            $thisparser->{local} = pop @scope_stack;
+            [ grep $_->{class} !~ /comment_line/, @{$item{'statement(s)'}} ];
+        }
+
+statement:
+    scalar
+    | comment_line
+    | metadata
+    | typedef
+    | typedef_declare
+    | multi_declare
+    | vector
+    | <error:unmatched statement: $text>
+
+scalar:
+    <skip:'[ \t\r]*'> name type <matchrule:$item{type}> comment(?) "\n"
+        {
+            $thisparser->{local}{name}{ $item{name} }++
+                unless defined $thisparser->{local}{name}{ $item{name} };
+
+            $return = {
+                class   => 'scalar',
+                name    => $item{name},
+                type    => $item{type},
+                value   => $item[4],
+            };
+
+            $return->{comment} = $item{'comment(?)'}[0]
+                if $item{'comment(?)'}[0];
+
+            if ( defined $thisparser->{local}{name}{ $item{name} } ) {
+                $return->{multi}++
+                    if $thisparser->{local}{name}{ $item{name} } =~ /MULTI/;
+            } else {
+                $thisparser->{local}{name}{ $item{name} }++;
+            }
+        }
+
+vector:
+     <skip:'[ \t\r]*'> vname <commit> vtype vvalue[ $item{vtype} ] comment(?) "\n"
+        {
+            $thisparser->{local}{name}{ $item{vname} }++
+                unless defined $thisparser->{local}{name}{ $item{vname} };
+
+            $return = {
+                class   => 'vector',
+                name    => $item{vname},
+                type    => $item{vtype},
+                value   => $item{vvalue},
+                comment => $item{comment}
+            };
+
+            $return->{comment} = $item{'comment(?)'}[0]
+                if $item{'comment(?)'}[0];
+        }
+    | <error?:redefinition of MULTI> <reject>
+
+multi_declare:
+    <skip:'[ \t\r]*'> name /MULTI/i <commit> comment(?) "\n"
+        {
+            if ( ! defined $thisparser->{local}{name}{ $item{name} } ) {
+                $thisparser->{local}{name}{ $item{name} } = "MULTI";
+                $return = { class   => 'comment_line' };
+            } else {
+                $return = undef;
+            }
+        }
+    | <error?:redefinition of MULTI> <reject>
+
+# can't comit after the metadat_name here as it seems to break parsing empty
+# metadatas
+metadata:
+    <skip:'[ \t\r]*'> name /METADATA/i comment(?) "\n"
+    <commit>
+    { push @scope_stack, $thisparser->{local}; $thisparser->{local} = {}; 1; }
+    grammar(?)
+    <skip:'[ \t\r]*'> /END/i comment(?) "\n"
+
+        {
+            if (defined(@{$item{'grammar(?)'}})) {
+                $return = {
+                    class   => 'metadata',
+                    name    => $item{name},
+                    value   => @{$item{'grammar(?)'}},
+                };
+            } else {
+                $return = {
+                    class   => 'metadata',
+                    name    => $item{name},
+                    value   => [],
+                };
+            }
+
+            $return->{comment} = $item[4][0]
+                if defined $item[4][0];
+
+            if ( defined $thisparser->{local}{name}{ $item{name} } ) {
+                $return->{multi}++
+                    if $thisparser->{local}{name}{ $item{name} } =~ /MULTI/;
+            } else {
+                $thisparser->{local}{name}{ $item{name} }++;
+            }
+        } 
+    | <error?:bad METADATA syntax: $text> <reject>
+
+comment_line:
+    <skip:'[ \t\r]*'> comment(?) "\n"
+        {{ class   => 'comment_line' }}
+
+typedef_declare:
+    <skip:'[ \t\r]*'> 'TYPE' <commit> name name(s) comment(?) "\n"
+        {
+            if (defined $thisparser->{local}{typedef}{ $item{name} }) {
+                carp "refinition of TYPE $item{name}";    
+                $return = undef;
+            } else {
+
+                my $type = {
+                    class   => 'typedef_declare',
+                    name    => $item{name},
+                    fields  => $item{'name(s)'},
+                    length  => scalar @{ $item{'name(s)'} },
+                };
+
+                $thisparser->{local}{typedef}{ $item{name} } = $type;
+
+                # don't return anything in the parse tree
+                $return = { class => 'comment_line' };
+            }
+        }
+    | <error?:redefinition of TYPE> <reject>
+
+typedef:
+    <skip:'[ \t\r]*'> name typedef_type <commit> word(s) comment(?) "\n"
+        {
+            my $type = $thisparser->{local}{typedef}{ $item{typedef_type} };
+
+            unless ( scalar @{ $item{'word(s)'} } == $type->{length} ) {
+                my $expect = $type->{length};
+                my $got = scalar @{ $item{'word(s)'} };
+                carp "\"$item{name} $item{typedef_type}\""
+                    . " does not have enough fields, epected: $expect, got: $got";
+                $return = undef;
+            } else {
+                my @md;
+                my $i = 0;
+                foreach my $name ( @{ $type->{fields} } ) {
+                    push @md, {
+                        name    => $name,
+                        class   => 'scalar',
+                        type    => 'STR',
+                        value   => $item{'word(s)'}[$i],
+                    };
+
+                    $i++;
+                }
+
+                $return = {
+                    class   => 'metadata',
+                    name    => $item{name},
+                    value   => \@md,
+                };
+
+                if ( defined $thisparser->{local}{name}{ $item{name} } ) {
+                    $return->{multi}++
+                        if $thisparser->{local}{name}{ $item{name} } =~ /MULTI/;
+                } else {
+                    $thisparser->{local}{name}{ $item{name} }++;
+                }
+            }
+
+        }
+    | <error?:'TYPE' does not have enough parameters: $text> <reject>
+
+
+typedef_type: name
+        {
+            if ( ! defined $thisparser->{local}{typedef}{ $item{name} } ) {
+                $return = undef;
+            } else {
+                $return = $item{name};
+            }
+        }
+
+comment:
+    '#' to_end_of_line
+        { $item{to_end_of_line} }
+
+vname:
+    '@' name
+        { $item{name} }
+
+name:
+    /[\w][.\w-]*/i
+        {
+            if ( $item[1] =~ /^(METADATA|END|TYPE)$/i ) {
+                $return = undef;
+            } else {
+                $return = $item[1];
+            }
+        }
+
+# 'STR' seems to be the most common type, trying to match it first is a simple
+# optimization.
+type: 
+    vtype
+    | /(STR
+    | STRING
+    | UTC
+    | UT1
+    | TAI
+    | TT)/x
+
+vtype: 
+    /(S8
+    | S16
+    | S32
+    | S64
+    | U8 
+    | U16
+    | U32
+    | U64
+    | F32
+    | F64
+    | C32
+    | C64
+    | BOOL)/x
+
+S8: int
+S16: int
+S32: int
+S64: int
+U8 : int
+U16: int
+U32: int
+U64: int
+F32: float
+F64: float
+C32: float
+C64: float
+BOOL: bool
+STR: string
+STRING: string
+
+vvalue:
+    _vvalue[%arg](s)
+        { [ map { @$_ } @{$item[1]} ] }
+
+# backtracking optimization
+_vvalue:
+    <matchrule:$arg[0]> vector_sep(?)
+        { [ $item[1] ] }
+
+vector_sep:
+    /,|\s+/
+
+int:
+    # $RE{num}{int} from Regexp::Common::number
+    /(?:(?:[+-]?)(?:[0-9]+))/
+
+    # based on $RE{num}{real} from Regexp::Common::number
+float:
+    /(?:(?i)(?:[+-]?)(?:(?=[0-9]|[.])(?:[0-9]*)(?:(?:[.])(?:[0-9]{0,}))?)(?:(?:[Ee])(?:(?:[+-]?)(?:[0-9]+))|))/
+    | inf
+    | nan
+
+inf:
+    /[+-]?inf(?:inity)?/i
+        {
+            if ($item[1] =~ /^-/) {
+                $return = "-inf";
+            } else {
+                $return = "+inf";
+            }
+        }
+nan:
+    /[+-]?nan(?:\(\S*?\))?/i
+        { $return = "nan" }
+
+bool:
+    /(true|false|t|f)/i
+        { $item[1] =~ /t/i ? 1 : 0 }
+
+string:
+    /[^#\n\s]+[^#\n]*/
+        # trim tailing the trailing white that's left when the string is
+        # followed by a comment (#)
+        { 
+            $item[1] =~ s/\s*$//;
+            $return = $item[1];
+        }
+    | NULL
+
+word:
+    /(?:[^#\s\n]+)/
+
+to_end_of_line:
+    /[^\n]*/
+        {
+            my $comment = $item[1];
+            # remove leading whitespace
+            $comment =~ s/^\s+//;
+            # remove trailing whitespace
+            $comment =~ s/\s+$//;
+
+            $return = $comment;
+        }
+
+UTC:
+    iso8601
+    | NULL
+
+UT1:
+    iso8601
+    | NULL
+
+TAI:
+    iso8601
+    | NULL
+
+TT:
+    iso8601
+    | NULL
+
+iso8601:
+    # based on code from DateTime::Format::ISO8601
+    / \d{4} -?? \d\d -?? \d\d T?? \d\d :?? \d\d :?? \d\d (?:[\.,] (\d+))? Z?/x
+        { DateTime::Format::ISO8601->parse_datetime( $item[1] ) }
+
+NULL:
+    /NULL/
+    { $return = "NULL" }
+
+eofile:
+    /^\z/
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/docs/sdrs_grammar.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/docs/sdrs_grammar.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/docs/sdrs_grammar.txt	(revision 22322)
@@ -0,0 +1,144 @@
+startrule: statement(s) /\z/
+
+statement: scalar | vector | multi_declare | comment
+
+comment: '#' end_of_line
+
+scalar: name type value comment | name type value 
+
+vector: vname vtype vvalue comment | vname vtype vvalue 
+
+multi_declare: name '*' end_of_line
+
+name: /[a-z][.\w]*/i
+
+vname: /\@[a-z][.\w]*/i
+
+type: 
+    vtype | 'STR' | 'STRING'
+
+vtype: 
+    'S8'  |
+    'S16' |
+    'S32' |
+    'S64' |
+    'U8'  |
+    'U16' |
+    'U32' |
+    'U64' |
+    'F32' |
+    'F64' |
+    'C32' |
+    'C64' |
+    'BOOL'
+
+value: vector_value | string
+
+vvalue: vector_value vector_sep vvalue | vector_value vector_sep(?)
+
+vector_sep: ','
+
+vector_value: float | int | bool
+
+int: integer_constant
+
+float: floating_constant
+
+bool: /[tf]\s+?/i
+
+string:
+    /\S[^#\n]*/
+
+end_of_line: /[^\n]*/
+
+### based on syntax found in "C A Reference Manual"
+
+# integer constants
+
+integer_constant:
+    decimal_constant integer_suffix(?) |
+    octal_constant integer_suffix(?) |
+    hexadecimal_constant integer_suffix(?)
+
+decimal_constant:
+    digit(2..) |
+    nonzero_digit
+
+octal_constant:
+    '0' octal_digit(s) |
+    '0'
+   
+hexadecimal_constant:
+    hex_prefix hex_digit_sequence
+
+digit:
+    /[0-9]/
+
+nonzero_digit:
+    /[1-9]/
+
+octal_digit:
+    /[0-7]/
+
+hex_digit:
+    /0_9a-f/i
+
+integer_suffix:
+    long_suffix unsigned_suffix(?) |
+    long_long_suffix unsigned_suffix(?) |
+    unsigned_suffix long_suffix(?) |
+    unsigned_suffix long_long_suffix(?)
+
+long_suffix:
+    /l/i
+
+long_long_suffix:
+    /ll/i
+
+unsigned_suffix:
+    /u/i
+
+# floating-point constants
+
+floating_constant:
+    decimal_floating_constant |
+    hexadecimal_floating_constant
+
+decimal_floating_constant:
+    digit_sequence exponent floating_suffix(?) |
+    dotted_digits exponent(?) floating_suffix(?)
+
+digit_sequence:
+    digit(s)
+
+dotted_digits:
+    digit_sequence '.' digit_sequence  |
+    '.' digit_sequence |
+    digit_sequence '.'
+
+exponent:
+    /e/i sign_part digit_sequence
+
+sign_part:
+    /[+-]/
+
+floating_suffix:
+    /[fl]/i
+
+hexadecimal_floating_constant:
+    hex_prefix dotted_hex_digits binary_exponent floating_suffix(?) |
+    hex_prefix hex_digit_sequence binary_exponent floating_suffix(?)
+
+hex_prefix:
+    /0x/i
+
+dotted_hex_digits:
+    hex_digit_sequence '.' hex_digit_sequence |
+    '.' hex_digit_sequence |
+    hex_digit_sequence '.'
+
+hex_digit_sequence:
+    hex_digit(s) 
+
+binary_exponent:
+    /p/i sign_part(?) digit_sequence
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+Parser.pm
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/Config.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/Config.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/Config.pm	(revision 22322)
@@ -0,0 +1,137 @@
+# Copyright (c) 2005  Joshua Hoblitt
+#
+# $Id: Config.pm,v 1.32 2007-11-10 00:50:19 jhoblitt Exp $
+
+package PS::IPP::Metadata::Config;
+
+use strict;
+use warnings FATAL => qw( all );
+
+our $VERSION = '1.01';
+
+use Carp qw( carp );
+use PS::IPP::Metadata::Parser;
+
+use base qw( Class::Accessor::Fast );
+__PACKAGE__->mk_accessors( qw( overwrite ) );
+
+#$::RD_TRACE = 1;
+#$::RD_HINT = 1;
+#use Data::Dumper;
+
+sub new {
+    my $class = shift;
+
+    my $self = { _parser => PS::IPP::Metadata::Parser->new };
+
+    bless $self, $class;
+
+    $self->overwrite( undef );
+
+    return $self;
+}
+
+sub parse {
+    my ( $self, $metadata ) = @_;
+
+    return undef if not defined $metadata;
+    return undef if $metadata =~ /^\s*$/;
+
+    # remove any local data from a prevous run
+    # this is slightly faster then using Storable::dclone to clone a new parser
+    delete $self->{_parser}{local};
+
+    my $tree = $self->{_parser}->startrule( $metadata );
+
+    return undef unless defined $tree;
+
+    # look for duplicate names, there should be none after processing the
+    # multi-symbols
+    $self->_merge_duplicates( $tree ) or return undef;
+
+    # translate NULL values into perl undefs (Parse::RecDescent can't return
+    # undefs, args)
+    $self->_fix_nulls( $tree ) or return undef;
+
+    #print Dumper($tree);
+
+    return $tree;
+}
+
+sub _merge_duplicates {
+    my ( $self, $tree ) = @_;
+
+    # encountered names
+    my %names;
+
+    # Iteratate through the parse tree looking for duplicate declarations and
+    # resolving them by discarding elements according to the value of
+    # 'overwrite'.
+    for (my $i = 0; $i < @{$tree}; $i++) {
+        my $elem = $tree->[$i];
+
+        # stop if the prevous pass removed the last element and called redo
+        last unless defined $elem;
+                
+        # recurse through nested metadata
+        if ( $elem->{class} eq "metadata" ) {
+            $self->_merge_duplicates( $elem->{value} );
+        }
+
+        # ignore elements with the "multi" flag
+        if ( defined $elem->{multi} ) {
+            delete $elem->{multi};
+            next;
+        } 
+
+        if ( defined $names{ $elem->{name} } ) {
+            if ( $self->{overwrite} ) {
+                # remove the previous occurance
+                carp "duplicate variable name: ", $elem->{name}
+                    , ", removed previous occurance\n";
+                splice @{$tree}, $names{ $elem->{name} }, 1;
+            } else {
+                # remove the current occurance
+                carp "duplicate variable name: ", $elem->{name}
+                    , ", removed\n";
+                splice @{$tree}, $i, 1;
+            }
+
+            # the list just got shorter by one element so we don't want to
+            # increment the cursor
+            redo;
+        }
+
+        # add element name and location to record of previously seen names
+        $names{ $elem->{name} } = $i;
+    }
+
+    return 1;
+}
+
+sub _fix_nulls {
+    my ( $self, $tree ) = @_;
+
+    # Iteratate through the parse tree looking for values of NULL and
+    # translating them into Perl undefs
+    for (my $i = 0; $i < @{$tree}; $i++) {
+        my $elem = $tree->[$i];
+
+        # recurse through nested metadata
+        if ( $elem->{class} eq "metadata" ) {
+            $self->_fix_nulls( $elem->{value} );
+        }
+
+        # force stringification of $elem->{value} -- specifically because you
+        # can't compare a DateTime object to a string value
+        if ("$elem->{value}" eq "NULL") {
+            $elem->{value} = undef;
+        }
+    }
+
+    return 1;
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/Config.pod
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/Config.pod	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/Config.pod	(revision 22322)
@@ -0,0 +1,137 @@
+=pod
+
+=head1 NAME
+
+PS::IPP::Metadata::Config - Parses the psMetadata config language
+
+=head1 SYNOPSIS
+
+    use PS::IPP::Metadata::Config;
+
+    my $mdparser = PS::IPP::Metadata::Config->new;
+    $mdparser->overwrite( 1 );
+
+    my $config = $mdparser->parse( $example );
+
+=head1 DESCRIPTION
+
+Parses the psMetadata configuration language into an AoH parse tree.
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * new
+
+Accepts no parameters and returns a L<PS::IPP::Metadata::Config> object.
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * overwrite
+
+Controls the behavior of the parser when encountering keys that have been
+defined more then once.  A true value instructs the parser to discard all but
+the latest declaration and a false value will preserve the first key parsed.
+Accepts 0, 1, or undef.
+
+Defaults to undef.
+
+=item * parse
+
+Accepts a string.  Returns an AoH structure on success or undef on failure.
+
+The format of the returned structure is as follows:
+
+    [
+        {
+            'comment'   => 'This is a comment',
+            'value'     => '123456789',
+            'name'      => 'Bob',
+            'type'      => 'U64',
+            'class'     => 'scalar'
+        },
+        {
+            'comment'   => 'These are prime numbers',
+            'value'     => [
+                            '2',
+                            '3'
+                        ],
+            'name'      => '@primes',
+            'type'      => 'U8',
+            'class'     => 'vector'
+        },
+        ...
+    ]
+
+=back
+
+=head1 DEVELOPER NOTES
+
+Currently, "multiple symbols" are translated into "STR" vectors as the
+appropriate behavior isn't clear in the psLib SDRS.
+
+The parser does not have complete support for all of C99's floating point
+constants or any support for complex numbers.
+
+None of the "extended" config language syntax, as defined in the SDRS, is
+supported.
+
+=head1 REFERENCES
+
+=over 4
+
+=item PSDC-430-007
+
+Pan-STARRS PS-1 IPP Library Supplementary Design Requirements Specification
+(SDRS): Section 5.3.4, "Configuration Files".
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the LICENSE file included with
+this module, or in the L<perlgpl> Pod as supplied with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/scripts/mdc-dump
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/scripts/mdc-dump	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/scripts/mdc-dump	(revision 22322)
@@ -0,0 +1,147 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: mdc-dump,v 1.3 2007-11-10 00:40:08 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use Data::Dumper qw( Dumper );
+use PS::IPP::Metadata::Config;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ( $file, $overwrite, $verbose );
+GetOptions(
+    'overwrite' => \$overwrite,
+    'verbose'   => \$verbose,
+) || pod2usage( 2 );
+
+$file = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+# open STDIN if no file was specified
+$file = '-' unless $file;
+
+if (defined $verbose) {
+    $::RD_TRACE = 1;
+    $::RD_HINT = 1;
+}
+my $md_parser = PS::IPP::Metadata::Config->new;
+
+if ( $overwrite ) {
+   $md_parser->overwrite( 1 );
+} else {
+   $md_parser->overwrite( 0 );
+}
+
+open( my $md, $file ) or die "can't open file: $!";
+
+my $data = $md_parser->parse( do { local $/; <$md> } );
+
+close( $md );
+
+if ( $data ) {
+    print Dumper( $data );
+} else {
+    warn "Invalid Syntax, Parse failed.\n";
+    exit 1;
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+mddump.pl - validate psMetadataConfig files
+
+=head1 SYNOPSIS
+
+    mddump.pl [--overwrite] [--verbose] <filename>
+
+    or
+
+    cat <filename> | mddump.pl [--overwrite] [--verbose]
+
+    or
+
+    mddump.pl [--help | -h | -? | --version]
+
+=head1 DESCRIPTION
+
+Parses psMetadataConfig formatted text either from a file or STDIN and prints a
+Perl data structure that represents the metadata.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * <filename>
+
+Accepts the name of a file to use as input.  If this option is omitted the
+STDIN will be read from.
+
+=item * --overwrite | -o
+
+A flag that turns L<PS::IPP::Metadata::Config>'s overwrite behavior on.
+Overwrite is always off unless this flag is specified.
+
+=item * --verbose | -v
+
+A flag that turns L<Parse::RecDescent>'s C<RD_TRACE> and C<RD_HINT> tracing
+flags.  verbose is always off unless this flag is specified.
+
+=item * --help | -h | -?
+
+Prints usage information.
+
+=item * --version
+
+Prints the version.
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2004  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<PS::IPP::Metadata::Config>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/00_distribution.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/00_distribution.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/00_distribution.t	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 00_distribution.t,v 1.1 2007-01-30 01:30:48 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib ./t );
+
+use Test::More;
+
+# example taken from Test::Distribution Pod
+
+BEGIN {
+    eval {
+        require Test::Distribution;
+    };
+    if($@) {
+        plan skip_all => 'Test::Distribution not installed';
+    } else {
+        import Test::Distribution not => qw( podcover );
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/01_load.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/01_load.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/01_load.t	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 01_load.t,v 1.1.1.1 2005-03-01 03:38:45 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib );
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'PS::IPP::Metadata::Config' ); }
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/02_examples.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/02_examples.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/02_examples.t	(revision 22322)
@@ -0,0 +1,385 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 02_examples.t,v 1.5 2006-07-12 02:49:53 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib );
+
+#$::RD_TRACE = 1;
+
+use Test::More tests => 9;
+use PS::IPP::Metadata::Config;
+
+my $config_parser = PS::IPP::Metadata::Config->new;
+
+{
+    my $config = $config_parser->parse(undef);
+    is($config, undef, "parsing undef returns undef");
+}
+
+{
+    my $config = $config_parser->parse('');
+    is($config, undef, "parsing an empty string returns undef");
+}
+
+{
+    my $config = $config_parser->parse("\n\n\n\n\n");
+    is($config, undef, "parsing an empty string returns undef");
+}
+
+{
+my $example = q{
+Double     F64     1.23456789      # This is a comment
+Float    F32 0.98765#This is a comment too
+String  STR This is the string that forms the value #comment
+
+ # This is a comment line and is to be ignored
+boolean     BOOL    T # The value of `boolean' is `true'
+ 
+@primes U8  2,3 5 7,11,13 17 #   These are prime numbers
+
+comment MULTI # The rest of this line is ignored, but `comment' is set to be non-unique
+comment STR This
+comment STR     is
+comment STR       a
+comment STR        non-unique
+comment STR                  key
+Float F64 1.23456 # This generates a warning, and, if `overwrite' is `false', is ignored
+};
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "SDRS example parsed");
+
+    my $tree = [
+        {
+            name    => 'Double',
+            class   => 'scalar',
+            type    => 'F64',
+            value   => '1.23456789',
+            comment => 'This is a comment',
+        },
+        {
+            name    => 'Float',
+            class   => 'scalar',
+            type    => 'F32',
+            value   => '0.98765',
+            comment => 'This is a comment too',
+        },
+        {
+            name    => 'String',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'This is the string that forms the value',
+            comment => 'comment',
+        },
+        {
+            name    => 'boolean',
+            class   => 'scalar',
+            type    => 'BOOL',
+            value   => 1,
+            comment => q{The value of `boolean' is `true'},
+        },
+        {
+            name    => 'primes',
+            class   => 'vector',
+            type    => 'U8',
+            value   => [qw( 2 3 5 7 11 13 17 )],
+            comment => 'These are prime numbers',
+        },
+        {
+            name    => 'comment',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'This',
+        },
+        {
+            name    => 'comment',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'is',
+        },
+        {
+            name    => 'comment',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'a',
+        },
+        {
+            name    => 'comment',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'non-unique',
+        },
+        {
+            name    => 'comment',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'key',
+        },
+    ];
+
+    is_deeply( $config, $tree, "SDRS example structure, no overwrite" );
+}
+
+{
+my $example = q{
+Double     F64     1.23456789      # This is a comment
+Float    F32 0.98765#This is a comment too
+String  STR This is the string that forms the value #comment
+
+ # This is a comment line and is to be ignored
+boolean     BOOL    T # The value of `boolean' is `true'
+ 
+@primes U8  2,3 5 7,11,13 17 #   These are prime numbers
+
+comment MULTI # The rest of this line is ignored, but `comment' is set to be non-unique
+comment STR This
+comment STR     is
+comment STR       a
+comment STR        non-unique
+comment STR                  key
+Float F64 1.23456 # This generates a warning, and, if `overwrite' is `false', is ignored
+};
+    $config_parser->overwrite( 1 );
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "SDRS example parsed");
+
+    my $tree = [
+        {
+            name    => 'Double',
+            class   => 'scalar',
+            type    => 'F64',
+            value   => '1.23456789',
+            comment => 'This is a comment',
+        },
+        {
+            name    => 'String',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'This is the string that forms the value',
+            comment => 'comment',
+        },
+        {
+            name    => 'boolean',
+            class   => 'scalar',
+            type    => 'BOOL',
+            value   => 1,
+            comment => q{The value of `boolean' is `true'},
+        },
+        {
+            name    => 'primes',
+            class   => 'vector',
+            type    => 'U8',
+            value   => [qw( 2 3 5 7 11 13 17 )],
+            comment => 'These are prime numbers',
+        },
+        {
+            name    => 'comment',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'This',
+        },
+        {
+            name    => 'comment',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'is',
+        },
+        {
+            name    => 'comment',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'a',
+        },
+        {
+            name    => 'comment',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'non-unique',
+        },
+        {
+            name    => 'comment',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'key',
+        },
+        {
+            name    => 'Float',
+            class   => 'scalar',
+            type    => 'F64',
+            value   => '1.23456',
+            comment => q{This generates a warning, and, if `overwrite' is `false', is ignored},
+        },
+    ];
+
+    is_deeply( $config, $tree, "SDRS example structure, with overwrite" );
+}
+
+{
+my $example = q{
+# these are examples of camera definition variables:
+# skyprobe
+NCELL       S32    1
+NCHIP       S32    1
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC     
+CELL.00     STR    %f00.%x     PHU      [0,0:0,0]   CHIP.00   [0,0:0,0] 
+                   
+# megacam-raw      
+NCELL       S32    72
+NCHIP       S32    36
+#                  FILENAME    EXTNAME  DATASEC     CHIP      BIASSEC     
+CELL.00     STR    %f.%x       AMP00    [0,0:0,0]   CHIP.00   BIASSEC
+CELL.01     STR    %f.%x       AMP01    [0,0:0,0]   CHIP.00   [2100,2110:0,4096]   
+CELL.02     STR    %f.%x       AMP02    [0,0:0,0]   CHIP.01   [0,0:0,0]   
+CELL.03     STR    %f.%x       AMP03    [0,0:0,0]   CHIP.01   [0,0:0,0]   
+
+# megacam-splice
+NCELL       S32    72
+NCHIP       S32    36
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC   TRIMSEC
+CELL.00     STR    %f.%x       CCD00    ASEC-00     CHIP.00   BSEC-00   DSEC-00
+CELL.01     STR    %f.%x       CCD00    ASEC-01     CHIP.00   BSEC-01   DSEC-01
+CELL.02     STR    %f.%x       CCD01    ASEC-00     CHIP.01   BSEC-00   DSEC-00
+CELL.03     STR    %f.%x       CCD01    ASEC-01     CHIP.01   BSEC-01   DSEC-01
+
+# cfh12k-split
+NCELL       S32    12
+NCHIP       S32    12
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC     
+CELL.00     STR    %f/%f00.%x  PHU      [0,0:0,0]   CHIP.00   [0,0:0,0]   
+CELL.01     STR    %f/%f01.%x  PHU      [0,0:0,0]   CHIP.01   [0,0:0,0]   
+CELL.02     STR    %f/%f02.%x  PHU      [0,0:0,0]   CHIP.02   [0,0:0,0]   
+
+# cfh12k-mef
+NCELL       S32    12
+NCHIP       S32    12
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC     
+CELL.00     STR    %f.%x       CHIP00   [0,0:0,0]   CHIP.00   [0,0:0,0]   
+CELL.01     STR    %f.%x       CHIP01   [0,0:0,0]   CHIP.01   [0,0:0,0]   
+CELL.02     STR    %f.%x       CHIP02   [0,0:0,0]   CHIP.02   [0,0:0,0]   
+
+#- REGION can be defined by a header keyword in IRAF format or by a explicit IRAF format 
+#- what is default for NAXIS1,2 for IRAF format?
+#- Nreadout is always NAXIS3?
+
+# recipe file:
+# this makes the assumption that, for a given camera, all chips &
+# cells have the same recipe.  this is probably a good start, but may
+# not cut it in general. eg, it is already clear that for 
+
+# recipe file must be a function of time and camera.
+# 
+
+# BIAS:
+BIAS.IMAGE                 STR    NONE
+BIAS.IMAGE  		   STR    FILE:bias.fits
+BIAS.IMAGE  		   STR    DB:BEST
+BIAS.IMAGE  		   STR    DB:CLOSE
+
+BIAS.OVERSCAN 		   STR    HEADER:BIASSEC
+BIAS.OVERSCAN 		   STR    RECIPE:[0,0:0,0]
+BIAS.OVERSCAN 		   STR    NONE
+
+BIAS.OVERSCAN.STATS 	   STR    MEDIAN
+BIAS.OVERSCAN.STATS 	   STR    MEAN
+
+BIAS.OVERSCAN.FIT          STR    SPLINE
+BIAS.OVERSCAN.FIT.NPTS     S32    5
+
+BIAS.OVERSCAN.FIT          STR    POLYNOMIAL
+BIAS.OVERSCAN.FIT.ORDER    S32    3
+BIAS.OVERSCAN.FIT.NBIN     S32    5
+};
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "GENE example");
+}
+
+{
+my $example = q{
+# The raw MegaCam data comes off the telescope with each of the chips stored in extensions of a MEF file.
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	MegaCam
+	EXTEND		BOOL	T
+	NEXTEND		S32	72
+END
+
+# How to read this data
+PHU		STR	FPA	# The FITS file represents an entire FPA
+EXTENSIONS	STR	CELLS	# The extensions represent cells
+EXTENSION_KEY	STR	EXTNAME	# You get the extensions by looking at the EXTNAME header
+IDENTIFIER	STR	OBSID	# We identify the observation by the observation Id in the header
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	TYPE	CELL	CHIP	CELLTYPE
+	# Extension name, chip
+	amp00	CELL	ccd00	science
+	amp01	CELL	ccd00	science
+	amp02	CELL	ccd01	science
+	amp03	CELL	ccd01	science
+	guide	CELL	guide	guide		# A guide CCD thrown in, just for fun
+END
+
+# Specify the cell data
+CELLS	METADATA
+	science	METADATA	# A science CCD
+		TYPE	LOCATION	SOURCE	VALUE
+		BIASSEC	LOCATION	VALUE	[1:10,1:4096];[1035:1084,1:4096]
+		TRIMSEC	LOCATION	VALUE	[11:1034,1:4096]
+	#	BIASSEC	LOCATION	HEADER	BIASSEC
+	#	TRIMSEC	LOCATION	HEADER	TRIMSEC
+	END
+	guide	METADATA	# A guide CCD
+		TYPE	LOCATION	SOURCE	VALUE
+		BIASSEC	LOCATION	VALUE	[1:10,1:1024];[1035:1084,1:1024]
+		TRIMSEC	LOCATION	VALUE	[11:1034,1:1024]
+	#	BIASSEC	LOCATION	HEADER	BIASSEC
+	#	TRIMSEC	LOCATION	HEADER	TRIMSEC
+	END
+		
+END
+
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	AIRMASS         STR             AIRMASS
+	EXPTIME         STR             EXPOSURE
+	DARKTIME        STR             EXPOSURE # No specific darktime header; use exposure time
+	FILTER          STR             FILTER
+	DATE-OBS        STR             DATE-OBS
+	TIME-OBS        STR             TIME-OBS
+	POSANGLE        STR             POSANG
+	RA              STR             OBJ-RA
+	DEC             STR             OBJ-DEC
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	RADECSYS	STR		ICRS
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+	GAIN            dbEntry         Camera          gain            chipId,cellId	CHIP,CELL
+	READNOISE       dbEntry         Camera          readNoise       chipId,cellId	CHIP,CELL
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+
+END
+};
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "Paul/megacam example");
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/03_metadata.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/03_metadata.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/03_metadata.t	(revision 22322)
@@ -0,0 +1,439 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 03_metadata.t,v 1.6 2006-11-25 01:15:47 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib );
+
+#$::RD_TRACE = 1;
+
+use Test::More tests => 13;
+use PS::IPP::Metadata::Config;
+
+my $config_parser = PS::IPP::Metadata::Config->new;
+
+{
+my $example = q{
+CELL      METADATA
+ EXTNAME   STR   CCD00
+ BIASSEC   STR   BSEC-00
+ CHIP      STR   CHIP.00
+ NCELL     S32   24
+END
+};
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "SDRS example parsed");
+
+    my $tree = [
+        {
+            name    => 'CELL',
+            class   => 'metadata',
+            value   => [
+                {
+                    name    => 'EXTNAME',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'CCD00'
+                },
+                {
+                    name    => 'BIASSEC',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'BSEC-00'
+                },
+                {
+                    name    => 'CHIP',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'CHIP.00'
+                },
+                {
+                    name    => 'NCELL',
+                    class   => 'scalar',
+                    type    => 'S32',
+                    value   => 24,
+                },
+            ],
+        }
+    ];
+
+    is_deeply( $config, $tree, "SDRS example structure");
+}
+
+{
+my $example = q{
+CELL      METADATA          # foo
+ EXTNAME   STR   CCD00      # bar
+ BIASSEC   STR   BSEC-00    # baz 
+ CHIP      STR   CHIP.00    # zab
+ NCELL     S32   24         # rab
+END                         # oof
+};
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "SDRS example + comments parsed");
+
+    my $tree = [
+        {
+            name    => 'CELL',
+            class   => 'metadata',
+            value   => [
+                {
+                    name    => 'EXTNAME',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'CCD00',
+                    comment => 'bar',
+                },
+                {
+                    name    => 'BIASSEC',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'BSEC-00',
+                    comment => 'baz',
+                },
+                {
+                    name    => 'CHIP',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'CHIP.00',
+                    comment => 'zab',
+                },
+                {
+                    name    => 'NCELL',
+                    class   => 'scalar',
+                    type    => 'S32',
+                    value   => 24,
+                    comment => 'rab',
+                },
+            ],
+            comment => 'foo',
+        }
+    ];
+
+    is_deeply( $config, $tree, "SDRS example + comments structure");
+}
+
+{
+my $example = q{
+CELL      METADATA
+    FOO METADATA
+        BAR     STR BAZ
+        PING    STR PONG
+    END
+    
+    EXTNAME   STR   CCD00
+    BIASSEC   STR   BSEC-00
+    CHIP      STR   CHIP.00
+    NCELL     S32   24
+END
+};
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "nested metadata parsed");
+
+    my $tree = [
+        {
+            name    => 'CELL',
+            class   => 'metadata',
+            value   => [
+                {
+                    name    => 'FOO',
+                    class   => 'metadata',
+                    value   => [
+                        {
+                            name    => 'BAR',
+                            class   => 'scalar',
+                            type    => 'STR',
+                            value   => 'BAZ',
+                        },
+                        {
+                            name    => 'PING',
+                            class   => 'scalar',
+                            type    => 'STR',
+                            value   => 'PONG',
+                        },
+                    ],
+                },
+                {
+                    name    => 'EXTNAME',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'CCD00',
+                },
+                {
+                    name    => 'BIASSEC',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'BSEC-00',
+                },
+                {
+                    name    => 'CHIP',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'CHIP.00',
+                },
+                {
+                    name    => 'NCELL',
+                    class   => 'scalar',
+                    type    => 'S32',
+                    value   => 24,
+                },
+            ],
+        }
+    ];
+
+    is_deeply( $config, $tree, "nested metadata structure");
+}
+
+{
+my $example = q{
+FOO1 METADATA
+    FOO2 METADATA
+        FOO3 METADATA
+            FOO4 METADATA
+                FOO5 METADATA
+                    FOO6 METADATA
+                        BAR     STR BAZ
+                        PING    STR PONG
+                    END
+                    BAR     STR BAZ
+                    PING    STR PONG
+                END
+                BAR     STR BAZ
+                PING    STR PONG
+            END
+            BAR     STR BAZ
+            PING    STR PONG
+        END
+        BAR     STR BAZ
+        PING    STR PONG
+    END
+    BAR     STR BAZ
+    PING    STR PONG
+END
+};
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "deeply nested metadata");
+
+    my $tree = [
+        {
+            name    => 'FOO1',
+            class   => 'metadata',
+            value   => [
+                {
+                    name    => 'FOO2',
+                    class   => 'metadata',
+                    value   => [
+                        {
+                            name    => 'FOO3',
+                            class   => 'metadata',
+                            value   => [
+                                {
+                                    name    => 'FOO4',
+                                    class   => 'metadata',
+                                    value   => [
+                                        {
+                                            name    => 'FOO5',
+                                            class   => 'metadata',
+                                            value   => [
+                                                {
+                                                    name    => 'FOO6',
+                                                    class   => 'metadata',
+                                                    value   => [
+                                                        {
+                                                            name    => 'BAR',
+                                                            class   => 'scalar',
+                                                            type    => 'STR',
+                                                            value   => 'BAZ',
+                                                        },
+                                                        {
+                                                            name    => 'PING',
+                                                            class   => 'scalar',
+                                                            type    => 'STR',
+                                                            value   => 'PONG',
+                                                        },
+                                                    ],
+                                                },
+                                                {
+                                                    name    => 'BAR',
+                                                    class   => 'scalar',
+                                                    type    => 'STR',
+                                                    value   => 'BAZ',
+                                                },
+                                                {
+                                                    name    => 'PING',
+                                                    class   => 'scalar',
+                                                    type    => 'STR',
+                                                    value   => 'PONG',
+                                                },
+                                            ],
+                                        },
+                                        {
+                                            name    => 'BAR',
+                                            class   => 'scalar',
+                                            type    => 'STR',
+                                            value   => 'BAZ',
+                                        },
+                                        {
+                                            name    => 'PING',
+                                            class   => 'scalar',
+                                            type    => 'STR',
+                                            value   => 'PONG',
+                                        },
+                                    ],
+                                },
+                                {
+                                    name    => 'BAR',
+                                    class   => 'scalar',
+                                    type    => 'STR',
+                                    value   => 'BAZ',
+                                },
+                                {
+                                    name    => 'PING',
+                                    class   => 'scalar',
+                                    type    => 'STR',
+                                    value   => 'PONG',
+                                },
+                            ],
+                        },
+                        {
+                            name    => 'BAR',
+                            class   => 'scalar',
+                            type    => 'STR',
+                            value   => 'BAZ',
+                        },
+                        {
+                            name    => 'PING',
+                            class   => 'scalar',
+                            type    => 'STR',
+                            value   => 'PONG',
+                        },
+                    ],
+                },
+                {
+                    name    => 'BAR',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'BAZ',
+                },
+                {
+                    name    => 'PING',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'PONG',
+                },
+            ],
+        },
+    ];
+
+    is_deeply( $config, $tree, "nested metadata structure");
+}
+
+{
+my $example = q{
+FOO1 METADATA
+    BAR     STR BAZ
+    PING    STR PONG
+    FOO2 METADATA
+        BAR     STR BAZ
+        PING    STR PONG
+        FOO3 METADATA
+            BAR     STR BAZ
+            PING    STR PONG
+            FOO4 METADATA
+                BAR     STR BAZ
+                PING    STR PONG
+                FOO5 METADATA
+                    BAR     STR BAZ
+                    PING    STR PONG
+                    FOO6 METADATA
+                        BAR     STR BAZ
+                        PING    STR PONG
+                    END
+                END
+            END
+        END
+    END
+END
+};
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "deeply nested metadata");
+}
+
+{
+my $example = q{
+FOO1 METADATA
+    BAR     STR BAZ
+    FOO2 METADATA
+        BAR     STR BAZ
+        FOO3 METADATA
+            BAR     STR BAZ
+            FOO4 METADATA
+                BAR     STR BAZ
+                FOO5 METADATA
+                    BAR     STR BAZ
+                    FOO6 METADATA
+                        BAR     STR BAZ
+                        PING    STR PONG
+                    END
+                    PING    STR PONG
+                END
+                PING    STR PONG
+            END
+            PING    STR PONG
+        END
+        PING    STR PONG
+    END
+    PING    STR PONG
+END
+};
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "deeply nested metadata");
+}
+
+{
+my $example = q{
+FOO1 METADATA
+    FOO2 METADATA
+        BAR     STR BAZ
+        PING    STR PONG
+    END
+    FOO3 METADATA
+        BAR     STR BAZ
+        PING    STR PONG
+    END
+END
+};
+    my $config = $config_parser->parse( $example );
+
+    ok( defined( $config ), "two metadata at the same depth");
+}
+
+{
+my $example = q{
+foo METADATA
+END
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+
+    ok( defined( $config ), "empty METADATA");
+
+    my $tree = [
+        {
+            name    => 'foo',
+            class   => 'metadata',
+            value   => [],
+        },
+    ];
+
+    is_deeply( $config, $tree, "empty METADATA structure");
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/04_type.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/04_type.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/04_type.t	(revision 22322)
@@ -0,0 +1,123 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 04_type.t,v 1.1 2005-03-23 01:53:55 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib );
+
+#$::RD_TRACE = 1;
+
+use Test::More tests => 9;
+use PS::IPP::Metadata::Config;
+
+my $config_parser = PS::IPP::Metadata::Config->new;
+
+{
+my $example = q{
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+};
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "basic TYPE");
+}
+
+{
+my $example = q{
+TYPE      CELL   EXTNAME   BIASSEC  CHIP    # comment
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00 # foo
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00 #
+};
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "TYPE with comments");
+}
+
+{
+my $example = q{
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+FOO METADATA
+    TYPE      CELL   EXTNAME   BIASSEC
+    CELL.00   CELL   CCD00     BSEC-00
+    CELL.01   CELL   CCD01     BSEC-01
+    FOO METADATA
+        TYPE      CELL   EXTNAME
+        CELL.00   CELL   CCD00
+        CELL.01   CELL   CCD01
+    END
+END
+};
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "TYPE not visible in lower scopes");
+}
+
+{
+my $example = q{
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+FOO METADATA
+    CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+END
+};
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "TYPE not in scope");
+}
+
+{
+my $example = q{
+FOO METADATA
+    TYPE      CELL   EXTNAME   BIASSEC  CHIP
+END
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+};
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "TYPE not in scope");
+}
+
+{
+my $example = q{
+FOO METADATA
+    TYPE      CELL   EXTNAME   BIASSEC  CHIP
+END
+BAR METADATA
+    CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+END
+};
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "TYPE not in scope");
+}
+
+
+{
+my $example = q{
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+CELL.00   CELL   CCD00     BSEC-00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+};
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "TYPE with missing parameters");
+}
+
+{
+my $example = q{
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+};
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "TYPE redefinition");
+}
+
+{
+my $example = q{
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+};
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "missing TYPE declaration");
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/05_time.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/05_time.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/05_time.t	(revision 22322)
@@ -0,0 +1,283 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 05_time.t,v 1.5 2006-11-22 23:54:50 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib );
+
+#$::RD_TRACE = 1;
+
+use Test::More tests => 15;
+use PS::IPP::Metadata::Config;
+
+my $config_parser = PS::IPP::Metadata::Config->new;
+
+{
+my $example =<<END;
+mytime  MULTI
+mytime  UTC     NULL
+mytime  UT1     NULL
+mytime  TAI     NULL
+mytime  TT      NULL
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "NULL times parsed");
+
+    my $tree = [
+        {
+            name    => 'mytime',
+            class   => 'scalar',
+            type    => 'UTC',
+            value   => undef,
+        },
+        {
+            name    => 'mytime',
+            class   => 'scalar',
+            type    => 'UT1',
+            value   => undef,
+        },
+        {
+            name    => 'mytime',
+            class   => 'scalar',
+            type    => 'TAI',
+            value   => undef,
+        },
+        {
+            name    => 'mytime',
+            class   => 'scalar',
+            type    => 'TT',
+            value   => undef,
+        },
+    ];
+
+    is_deeply( $config, $tree, "NULL times structure" );
+}
+
+{
+my $example =<<END;
+mytime  MULTI
+mytime  UTC     NULL    # comment
+mytime  UT1     NULL    # comment
+mytime  TAI     NULL    # comment
+mytime  TT      NULL    # comment
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "NULL times with comments parsed");
+
+    my $tree = [
+        {
+            name    => 'mytime',
+            class   => 'scalar',
+            type    => 'UTC',
+            value   => undef,
+            comment => 'comment',
+        },
+        {
+            name    => 'mytime',
+            class   => 'scalar',
+            type    => 'UT1',
+            value   => undef,
+            comment => 'comment',
+        },
+        {
+            name    => 'mytime',
+            class   => 'scalar',
+            type    => 'TAI',
+            value   => undef,
+            comment => 'comment',
+        },
+        {
+            name    => 'mytime',
+            class   => 'scalar',
+            type    => 'TT',
+            value   => undef,
+            comment => 'comment',
+        },
+    ];
+
+    is_deeply( $config, $tree, "NULL times with comments structure" );
+}
+
+{
+my $example =<<END;
+recently    MULTI
+recently    UTC     2005-03-18T16:05:00
+recently    UT1     2005-03-18T16:05:00
+recently    TAI     2005-03-18T16:05:00
+recently    TT      2005-03-18T16:05:00
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "basic ISO8601 parsed");
+
+    my $tree = [
+        {
+            name    => 'recently',
+            class   => 'scalar',
+            type    => 'UTC',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'scalar',
+            type    => 'UT1',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'scalar',
+            type    => 'TAI',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'scalar',
+            type    => 'TT',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+    ];
+
+    is_deeply( $config, $tree, "basic ISO8601 structure" );
+}
+
+{
+my $example =<<END;
+recently    MULTI
+recently    UTC     2005-03-18T16:05:00Z
+recently    UT1     2005-03-18T16:05:00Z
+recently    TAI     2005-03-18T16:05:00Z
+recently    TT      2005-03-18T16:05:00Z
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "basic ISO8601 w/Z parsed");
+
+    my $tree = [
+        {
+            name    => 'recently',
+            class   => 'scalar',
+            type    => 'UTC',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'scalar',
+            type    => 'UT1',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'scalar',
+            type    => 'TAI',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'scalar',
+            type    => 'TT',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+    ];
+
+    is_deeply( $config, $tree, "basic ISO8601 w/Z structure" );
+}
+
+{
+my $example =<<END;
+recently    MULTI
+recently    UTC     2005-03-18T16:05:00.000001Z
+recently    UT1     2005-03-18T16:05:00.000001Z
+recently    TAI     2005-03-18T16:05:00.000001Z
+recently    TT      2005-03-18T16:05:00.000001Z
+END
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "basic IS8601 with fractional seconds parsed");
+}
+
+{
+my $example =<<END;
+recently    MULTI
+recently    UTC     2005-03-18T16:05:00Z    # foo
+recently    UT1     2005-03-18T16:05:00Z    # bar
+recently    TAI     2005-03-18T16:05:00Z    # baz
+recently    TT      2005-03-18T16:05:00Z    #
+END
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "ISO8601 with comments parsed");
+}
+
+{
+my $example =<<END;
+recently    MULTI
+recently    UTC     2005-03-18T16:05:00.000001Z    # foo
+recently    UT1     2005-03-18T16:05:00.000001Z    # bar
+recently    TAI     2005-03-18T16:05:00.000001Z    # baz
+recently    TT      2005-03-18T16:05:00.000001Z    #
+END
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "ISO8601 with comments and fractional seconds parsed");
+}
+
+#diag("epoch time format maybe deprecated");
+#
+#{
+#my $example =<<END;
+#recently    MULTI
+#recently    UTC     123456, 5000, 1
+#recently    UT1     123456, 5000
+#recently    TAI     123456, 5000
+#recently    TT      123456, 5000
+#END
+#    my $config = $config_parser->parse( $example );
+#    ok( defined( $config ), "basic epoch time parsed");
+#}
+#
+#{
+#my $example =<<END;
+#recently    MULTI
+#recently    UTC     123456, 5000, 1         # foo
+#recently    UT1     123456, 5000            # bar
+#recently    TAI     123456, 5000            # baz
+#recently    TT      123456, 5000            #
+#END
+#    my $config = $config_parser->parse( $example );
+#    ok( defined( $config ), "epoch time with comments parsed");
+#}
+
+{
+my $example =<<END;
+broken      UTC     2005-03-18 16:05:00
+END
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "bad format");
+}
+
+{
+my $example =<<END;
+broken      UT1     2005-03-18 16:05:00
+END
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "bad format");
+}
+
+{
+my $example =<<END;
+broken      TAI     2005-03-18 16:05:00
+END
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "bad format");
+}
+
+{
+my $example =<<END;
+broken      TT      2005-03-18 16:05:00
+END
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "bad format");
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/06_multi.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/06_multi.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/06_multi.t	(revision 22322)
@@ -0,0 +1,346 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 06_multi.t,v 1.8 2006-10-11 02:49:05 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib );
+
+#$::RD_TRACE = 1;
+
+use Test::More tests => 15;
+use PS::IPP::Metadata::Config;
+
+{
+my $example = q{
+foo MULTI
+foo S8      -1
+foo STR     bar baz
+foo BOOL    T
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+    ok( defined( $config ), "basic MULTI");
+}
+
+{
+my $example = q{
+foo MULTI               # foo
+foo S8      -1          # bar
+foo STR     bar baz     # baz
+foo BOOL    T           #
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+    ok( defined( $config ), "MULTI with comments");
+}
+
+{
+my $example = q{
+foo MULTI
+foo S8      -1
+foo STR     bar baz
+foo BOOL    T
+bar METADATA
+    foo MULTI
+    foo S8      -1
+    foo STR     bar baz
+    foo BOOL    T
+END
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+    ok( defined( $config ), "MULTI not visible in lower scopes");
+}
+
+{
+my $example = q{
+foo MULTI
+foo METADATA
+    bar BOOL    T
+END
+foo METADATA
+    bar BOOL    T
+END
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+
+    my $tree = [
+        {
+            name    => 'foo',
+            class   => 'metadata',
+            value   => [{
+                            name    => 'bar',
+                            class   => 'scalar',
+                            type    => 'BOOL',
+                            value   => 1,
+                        }],
+        },
+        {
+            name    => 'foo',
+            class   => 'metadata',
+            value   => [{
+                            name    => 'bar',
+                            class   => 'scalar',
+                            type    => 'BOOL',
+                            value   => 1,
+                        }],
+        },
+    ];
+
+    is_deeply( $config, $tree, "MULTI METADATA structure, declared" );
+}
+
+{
+my $example = q{
+foo METADATA
+    bar BOOL    T
+END
+foo METADATA
+    bar BOOL    T
+END
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+
+    my $tree = [
+        {
+            name    => 'foo',
+            class   => 'metadata',
+            value   => [{
+                            name    => 'bar',
+                            class   => 'scalar',
+                            type    => 'BOOL',
+                            value   => 1,
+                        }],
+        },
+    ];
+
+    is_deeply( $config, $tree, "MULTI METADATA structure, not declared" );
+}
+
+{
+my $example = q{
+foo MULTI
+TYPE bar a b c
+foo  bar x y z
+foo  bar x y z
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+    ok( defined( $config ), "MULTI TYPE");
+
+    my $tree = [
+        {
+            name    => 'foo',
+            class   => 'metadata',
+            value   => [
+                {
+                    name    => 'a',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'x',
+                },
+                {
+                    name    => 'b',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'y',
+                },
+                {
+                    name    => 'c',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'z',
+                },
+            ],
+        },
+        {
+            name    => 'foo',
+            class   => 'metadata',
+            value   => [
+                {
+                    name    => 'a',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'x',
+                },
+                {
+                    name    => 'b',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'y',
+                },
+                {
+                    name    => 'c',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'z',
+                },
+            ],
+        },
+    ];
+
+    is_deeply( $config, $tree, "MULTI TYPE structure, not declared" );
+}
+
+{
+my $example = q{
+TYPE bar a b c
+foo MULTI
+foo  bar x y z
+foo  bar x y z
+};
+
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+    ok( defined( $config ), "MULTI TYPE");
+
+    my $tree = [
+        {
+            name    => 'foo',
+            class   => 'metadata',
+            value   => [
+                {
+                    name    => 'a',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'x',
+                },
+                {
+                    name    => 'b',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'y',
+                },
+                {
+                    name    => 'c',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'z',
+                },
+            ],
+        },
+        {
+            name    => 'foo',
+            class   => 'metadata',
+            value   => [
+                {
+                    name    => 'a',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'x',
+                },
+                {
+                    name    => 'b',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'y',
+                },
+                {
+                    name    => 'c',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'z',
+                },
+            ],
+        },
+    ];
+
+    is_deeply( $config, $tree, "MULTI TYPE structure, not declared" );
+}
+
+{
+my $example = q{
+TYPE bar a b c
+foo  bar x y z
+foo  bar x y z
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+
+    my $tree = [
+        {
+            name    => 'foo',
+            class   => 'metadata',
+            value   => [
+                {
+                    name    => 'a',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'x',
+                },
+                {
+                    name    => 'b',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'y',
+                },
+                {
+                    name    => 'c',
+                    class   => 'scalar',
+                    type    => 'STR',
+                    value   => 'z',
+                },
+            ],
+        },
+    ];
+
+    is_deeply( $config, $tree, "MULTI TYPE structure, not declared" );
+}
+
+{
+my $example = q{
+foo MULTI
+bar METADATA
+    foo S8      -1
+    foo STR     bar baz
+END
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+    # ok because of overwrite
+    ok( defined( $config ), "MULTI not in scope");
+}
+
+{
+my $example = q{
+bar METADATA
+    foo MULTI
+END
+foo S8      -1
+foo STR     bar baz
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+    # ok because of overwrite
+    ok( defined( $config ), "MULTI not in scope");
+}
+
+{
+my $example = q{
+bar METADATA
+    foo MULTI
+END
+baz METADATA
+    foo S8      -1
+END
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+    # ok because of overwrite
+    ok( defined( $config ), "MULTI not in scope");
+}
+
+{
+my $example = q{
+foo MULTI
+foo MULTI
+foo S8      -1
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+    ok( !defined( $config ), "MULTI redeclaration");
+}
+
+{
+my $example = q{
+foo S8      -1
+foo STR     bar baz
+};
+    my $config = PS::IPP::Metadata::Config->new->parse( $example );
+    # ok because of overwrite
+    ok( defined( $config ), "missing MULTI declaration");
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/07_floats.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/07_floats.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/07_floats.t	(revision 22322)
@@ -0,0 +1,209 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 07_floats.t,v 1.1 2006-09-23 00:26:50 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib );
+
+#$::RD_TRACE = 1;
+
+use Test::More tests => 12;
+use PS::IPP::Metadata::Config;
+
+my $config_parser = PS::IPP::Metadata::Config->new;
+
+{
+my $example =<<END;
+mynan   MULTI
+mynan   F32     nan
+mynan   F32     NaN
+mynan   F32     nAn
+mynan   F32     NAN
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "F32 nans parsed");
+
+    my $tree = [
+        {
+            name    => 'mynan',
+            class   => 'scalar',
+            type    => 'F32',
+            value   => 'nan',
+        },
+        {
+            name    => 'mynan',
+            class   => 'scalar',
+            type    => 'F32',
+            value   => 'nan',
+        },
+        {
+            name    => 'mynan',
+            class   => 'scalar',
+            type    => 'F32',
+            value   => 'nan',
+        },
+        {
+            name    => 'mynan',
+            class   => 'scalar',
+            type    => 'F32',
+            value   => 'nan',
+        },
+    ];
+
+    is_deeply( $config, $tree, "F32 nans structure" );
+}
+
+{
+my $example =<<END;
+mynan   MULTI
+mynan   F64     nan
+mynan   F64     NaN
+mynan   F64     nAn
+mynan   F64     NAN
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "F64 nans parsed");
+
+    my $tree = [
+        {
+            name    => 'mynan',
+            class   => 'scalar',
+            type    => 'F64',
+            value   => 'nan',
+        },
+        {
+            name    => 'mynan',
+            class   => 'scalar',
+            type    => 'F64',
+            value   => 'nan',
+        },
+        {
+            name    => 'mynan',
+            class   => 'scalar',
+            type    => 'F64',
+            value   => 'nan',
+        },
+        {
+            name    => 'mynan',
+            class   => 'scalar',
+            type    => 'F64',
+            value   => 'nan',
+        },
+    ];
+
+    is_deeply( $config, $tree, "F64 nans structure" );
+}
+
+my $inftree = [
+    # inf
+    {
+        name    => 'myinf',
+        class   => 'scalar',
+        type    => 'F32',
+        value   => '+inf',
+    },
+    {
+        name    => 'myinf',
+        class   => 'scalar',
+        type    => 'F32',
+        value   => '+inf',
+    },
+    {
+        name    => 'myinf',
+        class   => 'scalar',
+        type    => 'F32',
+        value   => '-inf',
+    },
+    {
+        name    => 'myinf',
+        class   => 'scalar',
+        type    => 'F32',
+        value   => '+inf',
+    },
+    {
+        name    => 'myinf',
+        class   => 'scalar',
+        type    => 'F32',
+        value   => '+inf',
+    },
+    {
+        name    => 'myinf',
+        class   => 'scalar',
+        type    => 'F32',
+        value   => '-inf',
+    },
+];
+
+{
+my $example =<<END;
+myinf   MULTI
+myinf   F32     inf
+myinf   F32     +inf
+myinf   F32     -inf
+myinf   F32     infinity
+myinf   F32     +infinity
+myinf   F32     -infinity
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "F32 infs parsed");
+
+    is_deeply( $config, $inftree, "F32 infs structure" );
+}
+
+{
+my $example =<<END;
+myinf   MULTI
+myinf   F32     InF
+myinf   F32     +InF
+myinf   F32     -InF
+myinf   F32     InFinity
+myinf   F32     +InFinity
+myinf   F32     -InFinity
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "F32 InFs parsed");
+
+    is_deeply( $config, $inftree, "F32 InFs structure" );
+}
+
+{
+my $example =<<END;
+myinf   MULTI
+myinf   F32     iNf
+myinf   F32     +iNf
+myinf   F32     -iNf
+myinf   F32     iNfinity
+myinf   F32     +iNfinity
+myinf   F32     -iNfinity
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "F32 iNfs parsed");
+
+    is_deeply( $config, $inftree, "F32 iNfs structure" );
+}
+
+{
+my $example =<<END;
+myinf   MULTI
+myinf   F32     INF
+myinf   F32     +INF
+myinf   F32     -INF
+myinf   F32     INFINITY
+myinf   F32     +INFINITY
+myinf   F32     -INFINITY
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "F32 INFs parsed");
+
+    is_deeply( $config, $inftree, "F32 INFs structure" );
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/08_strings.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/08_strings.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-Metadata-Config/t/08_strings.t	(revision 22322)
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: 08_strings.t,v 1.3 2007-07-06 02:27:32 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib );
+
+#$::RD_TRACE = 1;
+
+use Test::More tests => 6;
+use PS::IPP::Metadata::Config;
+
+my $config_parser = PS::IPP::Metadata::Config->new;
+
+{
+my $example =<<END;
+mystr   MULTI
+mystr   STR     somevalue
+mystr   STR     somevaluebeforeacomment             # some comment
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "strs parsed");
+
+    my $tree = [
+        {
+            name    => 'mystr',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'somevalue',
+        },
+        {
+            name    => 'mystr',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => 'somevaluebeforeacomment',
+            comment => 'some comment',
+        },
+    ];
+
+    is_deeply( $config, $tree, "str structure" );
+}
+
+{
+my $example =<<END;
+mystr   MULTI
+mystr   STR     NULL 
+mystr   STR     NULL    # some comment
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "NULL strs parsed");
+
+    my $tree = [
+        {
+            name    => 'mystr',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => undef,
+        },
+        {
+            name    => 'mystr',
+            class   => 'scalar',
+            type    => 'STR',
+            value   => undef,
+            comment => 'some comment',
+        },
+    ];
+
+    is_deeply( $config, $tree, "NULL str structure" );
+}
+
+{
+my $example =<<END;
+mystr   STR     # some comment
+END
+
+    my $config = $config_parser->parse( $example );
+    ok(!defined( $config ), "STR without value");
+
+    is_deeply( $config, undef, "NULL str structure" );
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Build
+META.yml
+Makefile
+Makefile.PL
+_build
+blib
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/Build.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/Build.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/Build.PL	(revision 22322)
@@ -0,0 +1,26 @@
+use Module::Build;
+# See perldoc Module::Build for details of how this works
+
+Module::Build->new(
+    module_name         => 'PS::IPP::PSFTP',
+    dist_version_from   => 'lib/PS/IPP/PSFTP.pm',
+    author              => 'Joshua Hoblitt <jhoblitt@cpan.org>',
+    license             => 'gpl',
+    create_makefile_pl  => 'passthrough',
+    requires            => {
+        'Carp'                  => 0,
+        'Data::Validate::URI'   => '0.01',
+        'LWP::UserAgent'        => 0,
+        'Params::Validate'      => '0.77',
+        'Regexp::Common'        => '2.113',
+    },
+    build_requires      => {
+        'Test::Warn'            => '0.08',
+    },
+    recommends          => {
+        'Test::Distribution'    => '1.22',
+    },
+    script_files        => [qw{
+        scripts/psftp-get
+    )],
+)->create_build_script;
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/Changes
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/Changes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/Changes	(revision 22322)
@@ -0,0 +1,6 @@
+Revision history for Perl module PS::IPP::PSFTP
+
+0.01 Wed Oct  5 14:00:26 2005
+    - original version; created by ExtUtils::ModuleMaker 0.41
+
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/LICENSE	(revision 22322)
@@ -0,0 +1,260 @@
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/MANIFEST
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/MANIFEST	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/MANIFEST	(revision 22322)
@@ -0,0 +1,17 @@
+Build.PL
+Changes
+LICENSE
+MANIFEST
+META.yml
+Makefile.PL
+README
+Todo
+lib/PS/IPP/PSFTP.pm
+lib/PS/IPP/PSFTP/Parser.pm
+lib/PS/IPP/PSFTP/Parser.pod
+lib/PS/IPP/PSFTP/Server.pm
+lib/PS/IPP/PSFTP/Server/HTTP.pm
+scripts/psftp-get
+t/00_distribution.t
+t/01_load.t
+t/02_parse.t
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/README	(revision 22322)
@@ -0,0 +1,15 @@
+pod2text PS::IPP::PSFTP.pm > README
+
+If this is still here it means the programmer was too lazy to create the readme file.
+
+You can create it now by using the command shown above from this directory.
+
+At the very least you should be able to use this set of instructions
+to install the module...
+
+perl Build.PL
+./Build
+./Build test
+./Build install
+
+If you are on a windows box you should use 'nmake' rather than 'make'.
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/Todo
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/Todo	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/Todo	(revision 22322)
@@ -0,0 +1,5 @@
+TODO list for Perl module PS::IPP::PSFTP
+
+- Nothing yet
+
+
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP.pm	(revision 22322)
@@ -0,0 +1,218 @@
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: PSFTP.pm,v 1.10 2005-10-15 00:57:32 jhoblitt Exp $
+
+package PS::IPP::PSFTP;
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Carp qw( carp );
+use Data::Validate::URI qw( is_uri );
+use Digest::MD5::File qw( file_md5_hex );
+use File::stat;
+use LWP::UserAgent;
+use Params::Validate qw( validate SCALAR UNDEF );
+use POSIX;
+use PS::IPP::PSFTP::Parser;
+use Regexp::Common qw( number );
+
+sub new
+{
+    my $class = shift;
+    
+    my %p = validate(@_,
+        {
+            base_url    => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid url' => sub { is_uri( $_[0] ) },
+                },
+            },
+            recipient   => {
+                type        => SCALAR,
+                regex       => qr/\S+/, # string with atleast 1 non WS char
+            },
+        },
+    );
+
+    my $self = bless(\%p, ref ($class) || $class);
+
+    return $self;
+}
+
+sub files_available
+{
+    my $self = shift;
+
+    validate(@_, {});
+
+    my $url = $self->{base_url}
+              . "/files_available"
+              . "?recipient="
+              . $self->{recipient};
+
+    my $ua = LWP::UserAgent->new;
+    my $response = $ua->get( $url );
+
+    if (! $response->is_success) {
+        carp $response->status_line;
+        return undef;
+    }
+
+    my $parser = PS::IPP::PSFTP::Parser->new;
+    my $records = $parser->parse($response->content);
+
+    return $records ? $records : undef;
+}
+
+sub file_received
+{
+    my $self = shift;
+
+    my %p = validate(@_,
+        {
+            id          => {
+                type        => SCALAR,
+                regex       => qr/\S+/, # string with atleast 1 non WS char
+            },
+            discarded   => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is 0 or 1' => sub { $_[0] == 0 or $_[0] == 1 },
+                },
+            },
+        },
+    );
+
+    my $url = $self->{base_url}
+              . "/file_received"
+              . "?recipient="
+              . $self->{recipient}
+              . "&id=" . $p{id}
+              . "&discarded=" . $p{discarded};
+
+    my $ua = LWP::UserAgent->new;
+    my $response = $ua->get( $url );
+
+    if (! $response->is_success) {
+        carp $response->status_line;
+        return undef;
+    }
+
+    return 1;
+}
+
+sub get_file_by_record
+{
+    my $self = shift;
+
+    my %p = validate(@_,
+        {
+            record      => {
+                isa         => 'PS::IPP::PSFTP::Record',
+            },
+            filename    => {
+                type        => SCALAR,
+                regex       => qr/\S+/, # string with atleast 1 non WS char
+                callbacks   => {
+                    'write access' => sub {
+                        POSIX::access($_[0], &POSIX::W_OK);
+                    },
+                },
+            },
+        },
+    );
+
+    return $self->get_file(
+        url         => $p{record}->url,
+        size        => $p{record}->size,
+        md5         => $p{record}->md5,
+        filename    => $p{filename},
+    );
+}
+
+sub get_file
+{
+    my $self = shift;
+
+    my %p = validate(@_,
+        {
+            url         => {
+                type        => SCALAR,
+                callbacks   => {
+                    'is valid url' => sub { is_uri( $_[0] ) },
+                },
+            },
+            size        => {
+                type        => SCALAR | UNDEF,
+                callbacks   => {
+                    'is int' => sub {
+                        return 1 unless defined $_[0];
+                        $_[0] =~ qr/^$RE{num}{int}$/;
+                     },
+                },
+                optional    => 1,
+            },
+            md5         => {
+                type        => SCALAR | UNDEF,
+                callbacks   => {
+                    'is hex' => sub {
+                        return 1 unless defined $_[0];
+                        $_[0] =~ qr/^$RE{num}{real}{-base => 16}$/;
+                     },
+                },
+                optional    => 1,
+            },
+            filename    => {
+                type        => SCALAR,
+                regex       => qr/\S+/, # string with atleast 1 non WS char
+                callbacks   => {
+                    'write access' => sub {
+                        POSIX::access($_[0], &POSIX::W_OK);
+                    },
+                },
+            },
+        },
+    );
+
+    my $ua = LWP::UserAgent->new;
+    my $request = HTTP::Request->new(GET => $p{url});
+    my $response = $ua->request($request, $p{filename});
+
+    if (! $response->is_success) {
+        carp $response->status_line;
+        return undef;
+    }
+
+    if (defined $p{size}) {
+        my $size = stat($p{filename})->size;
+        if (! $p{size} == $size) {
+            unlink $p{filename};
+            carp "url: ", $p{url},
+                 " - expected size: ", $p{size},
+                 " got: ", $size;
+            return undef;
+        }
+    }
+
+    if (defined $p{md5}) {
+        my $md5 = file_md5_hex($p{filename});
+        if (! $p{md5} eq $md5) {
+            unlink $p{filename};
+            carp "url: ", $p{url},
+                 " - expected md5: ", $p{md5},
+                 " got: ", $md5;
+            return undef;
+        }
+    }
+
+    return 1;
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Parser.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Parser.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Parser.pm	(revision 22322)
@@ -0,0 +1,118 @@
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: Parser.pm,v 1.4 2005-10-14 23:31:39 jhoblitt Exp $
+
+package PS::IPP::PSFTP::Parser;
+
+use strict;
+
+use Carp qw( carp );
+use Params::Validate qw( validate_pos SCALAR );
+
+use vars qw( @FIELDS @REQUIRED_FIELDS );
+
+@FIELDS = qw( date time file_id type attributes size md5 url );
+@REQUIRED_FIELDS = qw( date time file_id type size md5 url );
+
+sub new
+{
+    my $class = shift;
+
+    validate_pos(@_);
+
+    my $self = bless {}, ref ($class) || $class;
+
+    return $self;
+}
+
+sub parse
+{
+    my $self = shift;
+
+    my ($doc) = validate_pos(@_,
+        {
+            type    => SCALAR,
+            regex   => qr/\S+/, # string with atleast 1 non WS char
+        }
+    );
+
+    # pack data into an HoH
+    my @data;
+    foreach my $line (split /\n/, $doc) {
+        # convert string to an array of lines
+        my %record;
+        @record{@FIELDS} = map { $_ ? $_ : undef } split /\|/, $line;
+
+        # check for the proper number of fields (null or not)
+        unless (scalar keys %record == scalar @FIELDS) {
+            carp "record: $line does not have the proper number of fields\n";
+            return undef;
+        }
+
+        # check that all required fields are defined
+        foreach my $key (@REQUIRED_FIELDS) {
+           unless (defined $record{$key}) {
+                carp "record: $line is missing required field: $key\n";
+                return undef;
+           }
+        }
+
+        # convert attributes into a hash
+        if (defined $record{attributes}) {
+            my @pairs = split /:/, $record{attributes};
+            
+            my %attr;
+            foreach my $pair (@pairs) {
+                my ($key, $value) = split /=/, $pair;
+
+                unless (defined $key) {
+                    carp "record: $line has an attribute: $pair "
+                       . " with no key";
+                    return undef;
+                }
+
+                unless (defined $value) {
+                    carp "record: $line has an attribute: $pair"
+                       . " with no value";
+                    return undef;
+                }
+
+                # pack pairs into a hash
+                $attr{$key} = $value;
+            }
+        
+            # replace attributes string with a hashref
+            if (%attr) {
+                $record{attributes} = \%attr;
+            } else {
+                delete $record{attributes};
+            }
+        }
+
+        # pack date & time into stamp
+        $record{stamp} = $record{date} . "T" . $record{time};
+        delete $record{date};
+        delete $record{time};
+
+        my $obj = bless \%record, "PS::IPP::PSFTP::Record";
+
+        # pack as a FIFO
+        unshift @data, $obj;
+    }
+
+    return @data ? \@data : undef;
+}
+
+package PS::IPP::PSFTP::Record;
+
+use base qw( Class::Accessor::Fast );
+
+use vars qw( @FIELDS );
+
+@FIELDS = qw( stamp file_id type attributes size md5 url );
+
+__PACKAGE__->mk_accessors( @FIELDS );
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Parser.pod
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Parser.pod	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Parser.pod	(revision 22322)
@@ -0,0 +1,15 @@
+=pod
+
+=head1 name
+
+=head1 description
+
+=over 4
+
+=item * new
+
+=item * parse
+
+=back
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Server.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Server.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Server.pm	(revision 22322)
@@ -0,0 +1,85 @@
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: Server.pm,v 1.2 2005-10-12 02:11:10 jhoblitt Exp $
+
+package PS::IPP::PSFTP::Server;
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use CGI;
+
+sub run
+{
+    my ($class, $cgi) = @_;
+
+    $cgi ||= CGI->new;
+
+    my $mode = $cgi->url(-path_info=> 1, -relative=> 1);
+
+    if ($mode =~ /files_available/) {
+        $class->files_available($cgi);
+    } elsif ($mode =~ /file_received/) {
+        $class->file_received($cgi);
+    } else {
+        $class->error($cgi, "invalid mode");
+    }
+}
+
+sub files_available
+{
+    my ($class, $cgi) = @_;
+
+    my $recipient = $cgi->param('recipient')
+        or $class->error($cgi, "recipient not specified");
+
+    print $cgi->header('text/plain'),
+          $class->list_files($recipient);
+}
+
+sub file_received
+{
+    my ($class, $cgi) = @_;
+
+    my $recipient = $cgi->param('recipient')
+        or $class->error($cgi, "recipient not specified");
+    my $id = $cgi->param('id')
+        or $class->error($cgi, "id not specified");
+    my $discarded = $cgi->param('discarded')
+        or $class->error($cgi, "discarded not specified");
+
+    $class->set_received($recipient, $id, $discarded);
+
+    print $cgi->header(-status=> 200),
+}
+
+sub error
+{
+    my ($class, $cgi, $message) = @_;
+
+    print $cgi->header(-status=> 400),
+          $message, "\n",
+          $cgi->end_html();
+}
+
+sub get_available
+{
+    my ($class, $recipient) = @_;
+
+    return <<END;
+2005-08-15|12:00:01|telescope_2005-08-15T12:00:01|metadata||8640|c462d570deb517d9bd92e5bb6a21cc86|http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-15T12:00:01
+2005-08-15|12:10:01|telescope_2005-08-15T12:10:01|metadata||8640|c462d570deb517d9bd92e5bb6a21cc86|http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-15T12:10:01
+2005-08-15|12:20:01|telescope_2005-08-15T12:20:01|metadata||8640|c462d570deb517d9bd92e5bb6a21cc86|http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-15T12:20:01
+END
+}
+
+sub set_received
+{
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Server/HTTP.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Server/HTTP.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/lib/PS/IPP/PSFTP/Server/HTTP.pm	(revision 22322)
@@ -0,0 +1,27 @@
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: HTTP.pm,v 1.2 2005-10-14 21:29:35 jhoblitt Exp $
+
+package PS::IPP::PSFTP::Server::HTTP;
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use PS::IPP::PSFTP::Server;
+
+use base qw( HTTP::Server::Simple::CGI );
+   
+sub handle_request {
+    my ($self, $cgi) = @_;
+
+    print "HTTP/1.0 200 OK\r\n"; 
+
+    PS::IPP::PSFTP::Server->run($cgi);
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/scripts/psftp-get
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/scripts/psftp-get	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/scripts/psftp-get	(revision 22322)
@@ -0,0 +1,120 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: psftp-get,v 1.1 2005-10-15 01:27:33 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use PS::IPP::PSFTP;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($url, $size, $md5, $filename);
+
+GetOptions(
+    'url|u=s'       => \$url,
+    'size|s=s'      => \$size,
+    'md5|m=s'       => \$md5,
+    'filename|f=s'  => \$filename,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --url --filename",
+    -exitval => 3,
+) unless defined $url and defined $filename;
+
+my $status = PS::IPP::PSFTP->get_file(
+    url         => $url,
+    size        => $size,
+    md5         => $md5,
+    filename    => $filename,
+);
+
+unless ($status) {
+    exit(4);
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+psftp-get - download a file from a PSFTP server
+
+=head1 SYNOPSIS
+
+    psftp-get --url <url> --filename <filename> [--size <nbytes>] [--md5 <hex>]
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --url|-u <url>
+
+The URL of the file to be retrieved.
+
+=item * --filename|-f <filename>
+
+The name to use when writing the file to disk.
+
+=item * --size|-s <nbytes>
+
+The size of the file in bytes.
+
+Optional, the size check is skipped if no value is specified. 
+
+=item * --md5|-m <hex>
+
+The md5 checksum of the file in hex.
+
+Optional, the md5check is skipped if no value is specified. 
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<PS::IPP::PSFTP>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/00_distribution.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/00_distribution.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/00_distribution.t	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 00_distribution.t,v 1.2 2005-10-12 02:49:07 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib ./t );
+
+use Test::More;
+
+# example taken from Test::Distribution Pod
+
+BEGIN {
+    eval {
+        require Test::Distribution;
+    };
+    if($@) {
+        plan skip_all => 'Test::Distribution not installed';
+    } else {
+        import Test::Distribution not => qw( podcover );
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/01_load.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/01_load.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/01_load.t	(revision 22322)
@@ -0,0 +1,10 @@
+# -*- perl -*-
+
+# t/001_load.t - check module loading and create testing directory
+
+use Test::More tests => 4;
+
+BEGIN { use_ok( 'PS::IPP::PSFTP' ); }
+BEGIN { use_ok( 'PS::IPP::PSFTP::Parser' ); }
+BEGIN { use_ok( 'PS::IPP::PSFTP::Server' ); }
+BEGIN { use_ok( 'PS::IPP::PSFTP::Server::HTTP' ); }
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/02_parse.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/02_parse.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/02_parse.t	(revision 22322)
@@ -0,0 +1,102 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 02_parse.t,v 1.5 2005-10-14 23:30:48 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 33;
+
+use PS::IPP::PSFTP::Parser;
+use Test::Warn;
+
+my $example1 =<<END;
+2005-08-06|19:30:01|telescope_2005-08-06T19:30:01|metadata||8640|c462d570deb517d9bd92e5bb6a21cc86|http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-06T19:30:01
+2005-08-06|19:40:01|telescope_2005-08-06T19:40:01|metadata||8640|c462d570deb517d9bd92e5bb6a21cc86|http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-06T19:40:01
+2005-08-06|19:50:01|telescope_2005-08-06T19:50:01|metadata||8640|c462d570deb517d9bd92e5bb6a21cc86|http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-06T19:50:01
+END
+
+my $parser = PS::IPP::PSFTP::Parser->new;
+
+{
+my $results = $parser->parse($example1);
+my @items = @{ $results };
+
+is(scalar @items, 3, "correct number of item returned");
+
+{
+    my $item = pop @items;
+    is($item->stamp,   "2005-08-06T19:30:01",           "correct time stamp");
+    is($item->file_id, "telescope_2005-08-06T19:30:01", "correct file id");
+    is($item->type,    "metadata",                      "correct type");
+    is($item->size,    8640,                            "correct size");
+    is($item->md5,     "c462d570deb517d9bd92e5bb6a21cc86", "correct md5");
+    is($item->url,
+        "http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-06T19:30:01", "correct url");
+    is($item->attributes, undef, "no attributes");
+}
+
+{
+    my $item = pop @items;
+    is($item->stamp,   "2005-08-06T19:40:01",           "correct time stamp");
+    is($item->file_id, "telescope_2005-08-06T19:40:01", "correct file id");
+    is($item->type,    "metadata",                      "correct type");
+    is($item->size,    8640,                            "correct size");
+    is($item->md5,     "c462d570deb517d9bd92e5bb6a21cc86", "correct md5");
+    is($item->url,     "http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-06T19:40:01", "correct url");
+    is($item->attributes, undef, "no attributes");
+}
+
+{
+    my $item = pop @items;
+    is($item->stamp,   "2005-08-06T19:50:01",           "correct time stamp");
+    is($item->file_id, "telescope_2005-08-06T19:50:01", "correct file id");
+    is($item->type,    "metadata",                      "correct type");
+    is($item->size,    8640,                            "correct size");
+    is($item->md5,     "c462d570deb517d9bd92e5bb6a21cc86", "correct md5");
+    is($item->url,     "http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-06T19:50:01", "correct url");
+    is($item->attributes, undef, "no attributes");
+}
+}
+
+my $example2 =<<END;
+2005-08-06|19:30:01|telescope_2005-08-06T19:30:01|metadata|foo=bar:baz=baz|8640|c462d570deb517d9bd92e5bb6a21cc86|http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-06T19:30:01
+END
+
+{
+my $results = $parser->parse($example2);
+my @items = @{ $results };
+
+is(scalar @items, 1, "correct number of item returned");
+
+{
+    my $item = pop @items;
+    is($item->stamp,   "2005-08-06T19:30:01",           "correct time stamp");
+    is($item->file_id, "telescope_2005-08-06T19:30:01", "correct file id");
+    is($item->type,    "metadata",                      "correct type");
+    is($item->size,    8640,                            "correct size");
+    is($item->md5,     "c462d570deb517d9bd92e5bb6a21cc86", "correct md5");
+    is($item->url,     "http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-06T19:30:01", "correct url");
+    eq_hash($item->attributes, { foo => 'bar', baz => 'baz' }, "no attributes");
+}
+}
+
+warning_like {
+    my $results = $parser->parse(<<END);
+2005-08-06|19:30:01|telescope_2005-08-06T19:30:01||foo=bar:baz=baz|8640|c462d570deb517d9bd92e5bb6a21cc86|http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-06T19:30:01
+END
+    is($results, undef, "field not allowed to be null");
+} qr/record: .*? is missing required field/,
+    "warning for null required field";
+
+warning_like {
+my $results = $parser->parse(<<END);
+2005-08-06|19:30:01|telescope_2005-08-06T19:30:01|foo=bar:baz=baz|8640|c462d570deb517d9bd92e5bb6a21cc86|http://flaxen.ifa.hawaii.edu/otis/data/metadata/telescope_2005-08-06T19:30:01
+END
+    is($results, undef, "not enough fields");
+} qr/record: .*? is missing required field/,
+    "warning for missing required field";
Index: /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/03_psftp.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/03_psftp.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PS-IPP-PSFTP/t/03_psftp.t	(revision 22322)
@@ -0,0 +1,36 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 03_psftp.t,v 1.2 2005-10-11 00:09:13 jhoblitt Exp $
+
+use strict;
+use warnings;
+
+use lib qw( ./lib ./t );
+
+use Test::More tests => 3;
+
+use PS::IPP::PSFTP;
+
+{
+    my $psftp = PS::IPP::PSFTP->new(
+        base_url    => 'http://example.com',
+        recipient   => 'ipp',
+    );
+    isa_ok($psftp, 'PS::IPP::PSFTP');
+}
+
+eval {
+    my $psftp = PS::IPP::PSFTP->new(
+        base_url    => 'http://example.com',
+    );
+};
+like($@, qr/Mandatory parameter 'recipient' missing/, "no recipient param");
+
+eval {
+    my $psftp = PS::IPP::PSFTP->new(
+        recipient   => 'ipp',
+    );
+};
+like($@, qr/Mandatory parameter 'base_url' missing/, "no recipient param");
Index: /tags/sj_tags/sj_root_20080929/PStamp/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/PStamp/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PStamp/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Build
+META.yml
+Makefile
+Makefile.PL
+_build
+blib
Index: /tags/sj_tags/sj_root_20080929/PStamp/Build.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/PStamp/Build.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PStamp/Build.PL	(revision 22322)
@@ -0,0 +1,15 @@
+use Module::Build;
+# See perldoc Module::Build for details of how this works
+
+Module::Build->new(
+    module_name         => 'PStamp',
+    dist_version_from   => 'lib/PStamp.pm',
+    author              => 'Bill Sweeney',
+    license             => 'gpl',
+    create_makefile_pl  => 'passthrough',
+    requires            => {
+        'IPC::Cmd'              => '0.36',
+        'PS::IPP::Config'       => '1.01',
+        'PS::IPP::Metadata::List' => '0.01',
+    },
+)->create_build_script;
Index: /tags/sj_tags/sj_root_20080929/PStamp/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/PStamp/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PStamp/LICENSE	(revision 22322)
@@ -0,0 +1,260 @@
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
Index: /tags/sj_tags/sj_root_20080929/PStamp/MANIFEST
===================================================================
--- /tags/sj_tags/sj_root_20080929/PStamp/MANIFEST	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PStamp/MANIFEST	(revision 22322)
@@ -0,0 +1,6 @@
+Build.PL
+LICENSE
+MANIFEST
+lib/PStamp.pm
+lib/PStamp/Job.pm
+lib/PStamp/RequestFile.pm
Index: /tags/sj_tags/sj_root_20080929/PStamp/lib/PStamp.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PStamp/lib/PStamp.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PStamp/lib/PStamp.pm	(revision 22322)
@@ -0,0 +1,86 @@
+
+package PStamp;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '1.0';
+
+=pod
+
+=head1 NAME
+
+PStamp - Perl Module of Postage Stamp Server functions
+
+=head1 SYNOPSIS
+
+    use PStamp;
+
+    # equivalent to:
+
+    use Pstamp::RequestFile;
+
+=head1 DESCRIPTION
+
+This is a convenience module so that that don't have to individualy load (C<use
+...;>) all of the common PStamp inteface modules.  Please see the POD of the
+individual modules for usage information. 
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+None.
+
+=head1 EXAMPLE PROGRAM
+
+=cut
+
+use PStamp::RequestFile;
+
+=head1 CREDITS
+
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Bill Sweeney
+
+=head1 COPYRIGHT
+
+Copyright (C) 2008  Bill Sweeney.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the LICENSE file included with
+this module, or in the L<perlgpl> Pod as supplied with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<PStamp::RequestFile>
+
+=cut
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/PStamp/lib/PStamp/Job.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PStamp/lib/PStamp/Job.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PStamp/lib/PStamp/Job.pm	(revision 22322)
@@ -0,0 +1,413 @@
+###
+###     PStamp/Job.pm
+###     subroutines and constants related to Postage Stamp Jobs
+###
+
+package PStamp::Job;
+
+use strict;
+use warnings;
+
+our $VERSION = '1.0';
+
+use base qw( Exporter );
+
+our @EXPORT_OK = qw( 
+                    locate_images
+                    resolve_project
+                    );
+our %EXPORT_TAGS = (standard => [@EXPORT_OK]);
+
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config qw( :standard );
+
+### my @images = locate_images($image_db, $req_type, $img_type, $id, $lookup_class_id,
+###            $mjd_min, $mjd_max, $filter);
+
+sub locate_images {
+    my $ipprc    = shift;   # required
+    my $image_db = shift;   # required
+    my $req_type = shift;   # required
+    my $img_type = shift;   # required
+    my $id       = shift;   # required unless req_type eq bycoord
+    my $class_id = shift;
+    my $x        = shift;
+    my $y        = shift;
+    my $mjd_min  = shift;
+    my $mjd_max  = shift;
+    my $filter   = shift;
+    my $verbose  = shift;
+
+    # we die in response to bad data in request files
+    die "Unknown req_type: $req_type" if ($req_type ne "byid") and ($req_type ne "byexp")
+                                        and ($req_type ne "bycoord");
+    if ($req_type eq "bycoord") {
+
+        # run
+        # regtool -dbname $image_db -processedimfile -time_begin $mjd_min -time_end = $mjd_max -filter $filter
+        #
+        my $results = lookup_bycoord($ipprc, $image_db, $x, $y, $mjd_min, $mjd_max, $filter, $verbose);
+
+        $req_type = "byexp";
+        $id = $results->{exp_name};
+    }
+
+    my $results = lookup($ipprc, $image_db, $req_type, $img_type, $id, $class_id, $verbose);
+
+    return $results;
+}
+
+sub lookup {
+    my $ipprc    = shift;
+    my $image_db = shift;
+    my $req_type = shift;
+    my $img_type = shift;
+    my $id       = shift;
+    my $class_id = shift;
+    my $verbose = shift;
+
+    my $missing_tools;
+    my $regtool = can_run('regtool') or (warn "Can't find regtool" and $missing_tools = 1);
+    my $chiptool = can_run('chiptool') or (warn "Can't find chiptool" and $missing_tools = 1);
+    my $warptool = can_run('warptool') or (warn "Can't find warptool" and $missing_tools = 1);
+    my $difftool = can_run('difftool') or (warn "Can't find difftool" and $missing_tools = 1);
+    my $stacktool = can_run('stacktool') or (warn "Can't find stacktool" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+    my $command;
+    my $id_opt;     # option for the lookup
+    my $mask_name;
+    my $weight_name;
+    my $base_name;
+    my $want_astrom;
+    my $use_class_id;
+
+    # special class_id value "null" means all 
+    if ($class_id eq "null") {
+        $class_id = undef;
+    }
+
+    if ($img_type eq "raw") {
+        $command = "$regtool -processedimfile -dbname $image_db";
+        $id_opt = "-exp_id";
+        $command .= " -class_id $class_id" if $class_id;
+        $want_astrom = 0;
+        $use_class_id = 1;
+    } elsif ($img_type eq "chip") {
+        $command = "$chiptool -processedimfile -dbname $image_db";
+        $command .= " -class_id $class_id" if $class_id;
+        $id_opt = "-chip_id";
+        $mask_name    = "PPIMAGE.CHIP.MASK";
+        $weight_name  = "PPIMAGE.CHIP.WEIGHT";
+        $base_name    = "path_base"; # name of the field for the chiptool output
+        $want_astrom  = 1;
+        $use_class_id = 1;
+    } elsif ($img_type eq "warp") {
+        $command = "$warptool -warped -dbname $image_db";
+        $id_opt = "-warp_id";
+        $mask_name    = "PSWARP.OUTPUT.MASK";
+        $weight_name  = "PSWARP.OUTPUT.WEIGHT";
+        $base_name    = "path_base"; # name of the field for the warptool output
+    } elsif ($img_type eq "diff") {
+        $command = "$difftool -diffskyfile -dbname $image_db";
+        $id_opt = "-diff_id";
+        $mask_name   = "PPSUB.OUTPUT.MASK";
+        $weight_name = "PPSUB.OUTPUT.WEIGHT";
+        $base_name   = "path_base";
+    } elsif ($img_type eq "stack") {
+        $command = "$stacktool -sumskyfile -dbname $image_db";
+        $id_opt = "-stack_id";
+
+        $mask_name   = "PPSTACK.OUTPUT.MASK";
+        $weight_name = "PPSTACK.OUTPUT.WEIGHT";
+        $base_name   = "path_base";
+    } else {
+        die "Unknown img_type supplied: $img_type";
+    }
+
+    if ($req_type eq "byid") {
+        $command .= " $id_opt $id";
+    } elsif ($req_type eq "byexp") {
+        $command .= " -exp_name $id";
+    } else {
+        die "Unknown req_type supplied: $req_type";
+    }
+
+    # run the tool and parse the output
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+    unless ($success) {
+        # not sure if we should die here
+        print STDERR @$stderr_buf;
+        return undef;
+    }
+
+    my $buf = join "", @$stdout_buf;
+    if (!$buf) {
+        return;
+    }
+
+    my $mdcParser = PS::IPP::Metadata::Config->new;
+    my $images = parse_md_fast($mdcParser, $buf)
+        or die ("Unable to parse metadata config doc");
+
+    my $output = [];
+
+    my $camera;
+    foreach my $image (@$images) {
+        my $base;
+        if ($base_name) {
+            $base = $image->{$base_name};
+            # if base is undef then the output pruducts for thjis image are incomplete
+            # This may only happen for warps.
+            next if !$base;
+        }
+        if (!$camera) {
+            # This assumes that all images have the same camera
+            $ipprc->define_camera($image->{camera});
+            $camera = $image->{camera};
+        }
+        my $out = {};
+        $out->{exp_id} = $image->{exp_id};
+        $out->{image}  = $image->{uri};
+        $out->{state}  = $image->{state}; # state is undef for rawExp, but that's ok
+        $class_id = $image->{class_id} if $use_class_id;
+
+
+        # find the mask and weight images
+        if ($base) {
+            $out->{mask}   = $ipprc->filename($mask_name,   $base, $class_id) if $mask_name;
+            $out->{weight} = $ipprc->filename($weight_name, $base, $class_id) if $weight_name;
+        }
+        $out->{camera} = $camera;
+
+#XXX gpc1 and mops data that I've been looking at was made with astrometry in chip processing
+#so the filerule doesn't match
+if ($camera ne "SIMTEST") {
+    $want_astrom = 0;
+}
+       
+        $out->{astrom} = find_astrometry($ipprc, $image_db, $image, $verbose) if $want_astrom;
+
+        push @$output, $out;
+    }
+
+    return $output;
+}
+
+sub lookup_bycoord {
+    my $ipprc    = shift;
+    my $image_db = shift;
+    my $x        = shift;
+    my $y        = shift;
+    my $mjd_min  = shift;
+    my $mjd_max  = shift;
+    my $filter   = shift;
+    my $verbose  = shift;
+
+    my $missing_tools;
+    my $regtool = can_run('regtool') or (warn "Can't find regtool" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    # XXX TODO:
+    # Full lookups by coordinate require looking at the DVO database. However, 
+    # dateobs and filter which is all that MOPS has asked for.
+
+    my $args;
+    if ($mjd_min) {
+        my $dateobs_min = mjd_to_dateobs($mjd_min);
+        $args .= " -dateobs_begin $dateobs_min";
+    }
+    if ($mjd_max) {
+        my $dateobs_max = mjd_to_dateobs($mjd_max);
+        $args .= " -dateobs_end $dateobs_max";
+    }
+    if ($filter) {
+        $args .= " -filter $filter";
+    }
+
+    if (!$args) {
+        # avoid returning every exposure in the DB
+        print STDERR "no query arguments provided for bycoord\n";
+        return undef;
+    }
+
+    # XXX TODO: This query doesn't work Something appears to be out of
+    # sync about the times I'm passing and what regtool and or the DB expect
+    # the query I used in the C version of locateimages used
+    # WHERE dateobs >= FROM_UNIXTIME(%ld) AND dateobs <= FROM_UNIXTIME(%ld + exp_time)
+
+    my $command = "$regtool -processedexp -dbname $image_db $args";
+
+    # run the tool and parse the output
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+    unless ($success) {
+        # not sure if we should die here
+        print STDERR @$stderr_buf;
+        return undef;
+    }
+    my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+    my $output = join "", @$stdout_buf;
+    if (!$output) {
+        print STDERR "no output returned from $command\n" if $verbose;
+        return undef;
+    }
+    my $images = parse_md_fast($mdcParser, $output) or die ("Unable to parse metadata config doc");
+
+    return $images;
+}
+
+# find the astrometry file for a given exposure
+# return undef if no completed camRun exists
+sub find_astrometry {
+    my $ipprc = shift;
+    my $image_db = shift;
+    my $image = shift;      # hashref to output of the lookup tool
+    my $verbose = shift;
+
+    my $missing_tools;
+    my $camtool = can_run("camtool") or (warn "Can't find camtool" and $missing_tools = 1);
+    my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $command = "$camtool -dbname $image_db -processedexp -exp_id $image->{exp_id}";
+    # run the tool and parse the output
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+    unless ($success) {
+        print STDERR @$stderr_buf;
+        return undef;
+    }
+
+    my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+    my $output = join "", @$stdout_buf;
+    if (!$output) {
+        print STDERR "no output returned from $command\n" if $verbose;
+        return undef;
+    }
+    my $camruns = parse_md_fast($mdcParser, $output);
+    if (!$camruns) {
+        return undef;
+    }
+
+    # If there are multiple cam runs for this exposure, take the last one
+    # assuming that it has the best astrometry 
+    my $camdata = pop @$camruns;
+    if (!$camdata) {
+        # no cam runs for this exposure id therefore best astrometry is whatever is in the header
+        return undef;
+    }
+    my $camRoot = $camdata->{path_base};
+    my $camera = $image->{camera};
+    my $astromSource;
+    {
+       my $command = "$ppConfigDump -camera $camera -dump-recipe PSWARP -";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            die("Unable to perform ppConfigDump: $error_code");
+        }
+        my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+            die("Unable to parse metadata config doc");
+        $astromSource = metadataLookupStr($metadata, 'ASTROM.SOURCE');
+    }
+
+    my $astromFile = $ipprc->filename($astromSource, $camRoot);
+
+    return $astromFile;
+}
+
+# splits meta data config input stream into single units to work around the pathalogically
+# slow parser. This is similar to and adapted from code in various ippScripts.
+sub parse_md_fast {
+    my $mdcParser = shift;
+    my $input = shift;
+    my $output = ();
+
+    my @whole = split /\n/, $input;
+    my @single = ();
+
+    my $n;
+    while ( ($n = @whole) > 0) {
+        my $value = shift @whole;
+        push @single, $value;
+        if ($value =~ /^\s*END\s*$/) {
+	    push @single, "\n";
+
+            my $list = parse_md_list( $mdcParser->parse( join("\n", @single ) ) ) or
+                print STDERR "Unable to parse metdata config doc" and return undef;
+#            my $num = @$list;
+#            print STDERR "list has $num elments\n";
+            push @$output, $list->[0];
+
+            @single = ();
+        }
+    }
+    return $output;
+}
+
+sub mjd_to_dateobs {
+    my $mjd = shift;
+
+    my $ticks = ($mjd - 40587.0) * 86400;
+
+    my ($sec, $min, $hr, $day, $mon, $year) = gmtime($ticks);
+
+    return sprintf "'%4d-%02d-%02d %02d:%02d:%02d'", $year+1900, $mon+1, $day, $hr, $min, $sec;
+}
+
+# resolve_project()
+# get project specific information
+sub resolve_project {
+    my $ipprc = shift;
+    my $project_name = shift;
+    my $dbname = shift;
+    die "project is not defined" if !$project_name;
+
+    my $verbose = 1;
+
+    my $missing_tools;
+    my $pstamptool = can_run("pstamptool") or (warn "Can't find pstamptool" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $command = "$pstamptool -project -name $project_name";
+    $command .= " -dbname $dbname" if defined $dbname;
+    # run the tool and parse the output
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+    unless ($success) {
+        print STDERR @$stderr_buf;
+        return undef;
+    }
+
+    my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+    my $output = join "", @$stdout_buf;
+    if (!$output) {
+        print STDERR "no output returned from $command\n" if $verbose;
+        return undef;
+    }
+    my $proj_hash = parse_md_fast($mdcParser, $output);
+
+
+    return $proj_hash->[0];
+}
+1;
Index: /tags/sj_tags/sj_root_20080929/PStamp/lib/PStamp/RequestFile.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/PStamp/lib/PStamp/RequestFile.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/PStamp/lib/PStamp/RequestFile.pm	(revision 22322)
@@ -0,0 +1,129 @@
+#!/bin/env perl
+
+###
+###     PStamp/RequestFile.pm
+###     subroutines and constants related to Postage Stamp Request Files
+###
+
+package PStamp::RequestFile;
+
+use strict;
+use warnings;
+
+our $VERSION = '1.0';
+
+use base qw( Exporter );
+
+our @EXPORT_OK = qw( 
+                    read_request_file
+                    $PSTAMP_CENTER_IN_PIXELS
+                    $PSTAMP_RANGE_IN_PIXELS
+                    $PSTAMP_SELECT_IMAGE
+                    $PSTAMP_SELECT_MASK
+                    $PSTAMP_SELECT_WEIGHT
+                    );
+our %EXPORT_TAGS = (standard => [@EXPORT_OK]);
+
+
+our $PSTAMP_CENTER_IN_PIXELS = 1;
+our $PSTAMP_RANGE_IN_PIXELS  = 2;
+
+our $PSTAMP_SELECT_IMAGE     = 1;
+our $PSTAMP_SELECT_MASK      = 2;
+our $PSTAMP_SELECT_WEIGHT    = 4;
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config qw( :standard );
+
+sub read_request_file {
+    my $request_file_name = shift;
+    die "need request file name\n" unless $request_file_name;
+
+    my $verbose = shift;
+
+    my $ipprc = PS::IPP::Config->new(); # IPP Configuration
+
+    my $missing_tools;
+
+    my $pstampdump  = can_run('pstampdump') or (warn "Can't find pstampdump" and $missing_tools = 1);
+    my $fields  = can_run('fields') or (warn "Can't find fields" and $missing_tools = 1);
+
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    # Parser for metadata config files
+    my $mdcParser = PS::IPP::Metadata::Config->new;
+
+    #
+    # get the data from the extension header
+    #
+    my $fields_output;
+    {
+        my $command = "echo $request_file_name | $fields -x 0 EXTNAME EXTVER REQ_NAME";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        $fields_output = join "", @$stdout_buf;
+    }
+    my (undef, $extname, $extver, $req_name) = split " ", $fields_output;
+
+    # make sure the file contains what we are expecting
+
+    die "$request_file_name is not a PS1_PS_REQUEST" 
+                    if !$extname or ($extname ne "PS1_PS_REQUEST");
+    die "REQ_NAME not found in $request_file_name"  if (!$req_name);
+    die "wrong EXTVER $extver found in $request_file_name" if ($extver ne "1");
+
+    my %header;
+    $header{REQ_NAME} = $req_name;
+    $header{EXTVER}   = $extver;
+    $header{EXTNAME}  = $extname;
+
+    #
+    # now convert the request table to an array of hashes
+    #
+
+    my $rows;
+    {
+        my $command = "$pstampdump $request_file_name";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            print STDERR @$stderr_buf;
+        }
+        my $table =  $mdcParser->parse(join "", @$stdout_buf) or
+            die("Unable to parse metdata config doc");
+
+        $rows = parse_md_list($table);
+    }
+
+    my %req_specs;
+    foreach my $row (@$rows) {
+        my $rownum   = $row->{ROWNUM};
+        $req_specs{$rownum} = $row;
+
+        my $job_type = $row->{JOB_TYPE};
+        my $project  = $row->{PROJECT};
+        my $req_type = $row->{REQ_TYPE};
+        my $img_type = $row->{IMG_TYPE};
+        my $id       = $row->{ID};
+        my $class_id = $row->{CLASS_ID};
+        my $stamp_name   = $row->{STAMP_NAME};
+        my $filter   = $row->{REQFILT};
+        my $mjd_min = $row->{MJD_MIN};
+        my $mjd_max = $row->{MJD_MAX};
+        my $x = $row->{CENTER_X};
+        my $y = $row->{CENTER_Y};
+        my $w = $row->{WIDTH};
+        my $h = $row->{HEIGHT};
+        my $coord_mask = $row->{COORD_MASK};
+        my $option_mask= $row->{OPTION_MASK};
+
+        print "$rownum $req_type $img_type $id\n" if $verbose;
+    }
+    return (\%header, \%req_specs);
+}
+1;
Index: /tags/sj_tags/sj_root_20080929/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/README	(revision 22322)
@@ -0,0 +1,4 @@
+
+Welcome to the IPP Software Tree.  To build the full IPP suite, enter
+the directory 'psconfig' and follow the instructions in 'INSTALL'.
+
Index: /tags/sj_tags/sj_root_20080929/archive/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/README	(revision 22322)
@@ -0,0 +1,15 @@
+
+This directory contains early development version of IPP code.  These
+can eventually be removed with 'cvs rm', but for now they may still be
+used in some cases.  The examples are interesting in any case.
+
+pslib : the IfA's original prototype code for pslib.  since about
+Cycle 3, we have been working with the MHPCC version of the tree.
+This directory contains some interesting example code.
+
+modules : analogous for the psModules code
+
+psdb : Josh's development psDB code.
+
+utils : some unused, potentially useful scripts
+
Index: /tags/sj_tags/sj_root_20080929/archive/modules/config/camera-config.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/modules/config/camera-config.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/modules/config/camera-config.txt	(revision 22322)
@@ -0,0 +1,120 @@
+
+# rules to identify camera: 
+
+CFH12K.MEF	METADATA
+  TELESCOP	STR   CFHT
+  INSTRUME	STR   CFH12K
+  EXTEND	BOOL  T
+  NEXTEND	S32   12
+END
+
+CFH12K.SPLIT	METADATA
+  TELESCOP	STR   CFHT
+  INSTRUME	STR   CFH12K
+  EXTEND	BOOL  F
+END
+
+MEGACAM.RAW	METADATA
+  TELESCOP	STR   CFHT
+  INSTRUME	STR   MEGACAM
+  EXTEND	BOOL  T
+  NEXTEND	S32   72
+END
+
+MEGACAM.RAW	METADATA
+  TELESCOP	STR   CFHT
+  INSTRUME	STR   MEGACAM
+  EXTEND	BOOL  T
+  NEXTEND	S32   36
+END
+
+# camera definitions:
+TYPE            CELL   FILENAME    EXT.KEY  READOUT       CCDBIN1     CCDBIN2     DATASEC     BIASSEC     CHIP  
+
+MEGACAM.RAW     METADATA    
+  NCELL         S32    72
+  CELL.FMT      STR    CELL.%02d
+  EXT.TYPE      STR    CELL
+  EXT.KEY       STR    EXTNAME
+  PHU           STR    FPA
+  #
+  #                    FILENAME    EXT.KEY  READOUT       CCDBIN1     CCDBIN2     DATASEC     BIASSEC     CHIP  
+  CELL.00       CELL   %f.%x       amp00    CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.00
+  CELL.01       CELL   %f.%x       amp01    CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.00
+  CELL.02       CELL   %f.%x       amp02    CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.01
+  CELL.03       CELL   %f.%x       amp03    CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.01
+  ...
+END
+
+MEGACAM.SPLICE  METADATA      
+  NCELL         S32    36
+  CELL.FMT      STR    CELL.%02d
+  EXT.TYPE      STR    CHIP
+  EXT.KEY       STR    EXTNAME
+  PHU           STR    FPA
+  #
+  #                    FILENAME    EXT.KEY  READOUT       CCDBIN1     CCDBIN2     DATASEC     BIASSEC     CHIP  
+  CELL.00       CELL   %f.%x       ccd00    HD:ASECA      HD:CCDBIN1  HD:CCDBIN2  HD:DSECA    HD:BSECA    CHIP.00
+  CELL.01       CELL   %f.%x       ccd00    HD:ASECB      HD:CCDBIN1  HD:CCDBIN2  HD:DSECB    HD:BSECB    CHIP.00
+  CELL.02       CELL   %f.%x       ccd01    HD:ASECA      HD:CCDBIN1  HD:CCDBIN2  HD:DSECA    HD:BSECA    CHIP.01
+  CELL.03       CELL   %f.%x       ccd01    HD:ASECB      HD:CCDBIN1  HD:CCDBIN2  HD:DSECB    HD:BSECB    CHIP.01
+  ...
+END
+
+CFH12K.SPLIT    METADATA
+  NCELL         S32    12
+  CELL.FMT      STR    CELL.%02d
+  EXT.TYPE      STR    CELL
+  EXT.KEY       STR    EXTNAME
+  PHU           STR    NONE
+  #
+  #                    FILENAME    EXT.KEY  READOUT       CCDBIN1     CCDBIN2     DATASEC     BIASSEC     CHIP   
+  CELL.00       CELL   %f/%f00.%x  chip00   CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.00
+  CELL.01     	CELL   %f/%f01.%x  chip01   CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.01
+  CELL.02     	CELL   %f/%f02.%x  chip02   CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.02
+  ...
+END
+
+CFH12K.MEF      METADATA
+  NCELL         S32    12
+  CELL.FMT      STR    CELL.%02d
+  EXT.TYPE      STR    CELL
+  EXT.KEY       STR    EXTNAME
+  PHU		STR    FPA
+  #
+  #                    FILENAME    EXT.KEY  READOUT       CCDBIN1     CCDBIN2     DATASEC     BIASSEC     CHIP   
+  CELL.00       STR    %f.%x  	   chip00   CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.00
+  CELL.01     	STR    %f.%x  	   chip01   CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.01
+  CELL.02     	STR    %f.%x  	   chip02   CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.02
+  ...
+END
+
+GPC.RAW         METADATA    
+  NCELL         S32    4096
+  CELL.FMT      STR    CELL.%04d
+  EXT.TYPE      STR    CELL
+  EXT.KEY       STR    EXTNAME
+  PHU		STR    CHIP
+  #
+  #                    FILENAME    EXT.KEY  READOUT       CCDBIN1     CCDBIN2     DATASEC     BIASSEC     CHIP  
+  CELL.0000     CELL   %f.%x       amp00    CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.00
+  CELL.0001     CELL   %f.%x       amp01    CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.00
+  CELL.0002     CELL   %f.%x       amp02    CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.00
+  CELL.0003     CELL   %f.%x       amp03    CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.00
+  ...
+  CELL.0065     CELL   %f.%x       amp03    CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.01
+  CELL.0066     CELL   %f.%x       amp03    CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.01
+  ...
+END
+
+SKYPROBE        METADATA
+  NCELL		S32    1
+  CELL.FMT      STR    CELL.%02d
+  EXT.TYPE      STR    CELL
+  EXT.KEY       STR    DETECTOR
+  PHU		STR    NONE
+  #
+  #             CELL   FILENAME    EXT.KEY  READOUT       CCDBIN1     CCDBIN2     DATASEC     BIASSEC     CHIP  
+  CELL.00       CELL   %f.%x        -       CF:[0:0,0:0]  HD:CCDBIN1  HD:CCDBIN2  HD:DATASEC  HD:BIASSEC  CHIP.00
+  CELL.00       CELL   KEY="SBIG ST-7 Dual CCD Camera"
+END
Index: /tags/sj_tags/sj_root_20080929/archive/modules/config/phase2-config.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/modules/config/phase2-config.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/modules/config/phase2-config.txt	(revision 22322)
@@ -0,0 +1,134 @@
+
+# these are examples of camera definition variables:
+# skyprobe
+NCELL       S32    1
+NCHIP       S32    1
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC     
+CELL.00     STR    %f00.%x     PHU      [0,0:0,0]   CHIP.00   [0,0:0,0] 
+                   
+# megacam-raw      
+NCELL       S32    72
+NCHIP       S32    36
+#                  FILENAME    EXTNAME  DATASEC     CHIP      BIASSEC     
+CELL.00     STR    %f.%x       AMP00    [0,0:0,0]   CHIP.00   BIASSEC
+CELL.01     STR    %f.%x       AMP01    [0,0:0,0]   CHIP.00   [2100,2110:0,4096]   
+CELL.02     STR    %f.%x       AMP02    [0,0:0,0]   CHIP.01   [0,0:0,0]   
+CELL.03     STR    %f.%x       AMP03    [0,0:0,0]   CHIP.01   [0,0:0,0]   
+
+# megacam-splice
+NCELL       S32    72
+NCHIP       S32    36
+
+TYPE        CELL   FILENAME:STR  EXTNAME  REGION          BIASSEC   TRIMSEC  CHIP      
+CELL.00     CELL   %f.%x         CCD00    HEADER:ASEC-00  BSEC-00   DSEC-00  CHIP.00   
+CELL.01     CELL   %f.%x         CCD00    HEADER:ASEC-01  BSEC-01   DSEC-01  CHIP.00   
+CELL.02     CELL   %f.%x         CCD01    HEADER:ASEC-00  BSEC-00   DSEC-00  CHIP.01   
+CELL.03     CELL   %f.%x         CCD01    HEADER:ASEC-01  BSEC-01   DSEC-01  "chip  foo"
+CELL.03     CELL   DEFAULT=CELL.00 FILENAME=%f.%x
+
+FPA:
+ CHIP.00     
+  CELL.00     CELL   %f.%x         CCD00    ASEC-00     BSEC-00   DSEC-00  CHIP.00   
+  CELL.01     CELL   %f.%x         CCD00    ASEC-01     BSEC-01   DSEC-01  CHIP.00   
+ CHIP.01
+  CELL.02     CELL   %f.%x         CCD01    ASEC-00     BSEC-00   DSEC-00  CHIP.01   
+  CELL.03     CELL   %f.%x         CCD01    ASEC-01     BSEC-01   DSEC-01  "chip  foo"
+
+
+# gpc
+NCELL       S32    4
+NCHIP       S32    2
+#                  FILENAME    EXTNAME  DATASEC      CHIP      BIASSEC     
+CHIP.00.CELL.00     STR    %f/%f00.%x  CELL00   "[0,0:0,0]"  CHIP.00   RECIPE:[0,0:0,0]   
+CHIP.00.CELL.01     STR    %f/%f00.%x  CELL01   "DATASEC"    CHIP.00   RECIPE:[0,0:0,0]   
+#
+
+TYPE     CHIP    NCELL 
+TYPE     CELL    FILENAME:STR  EXTNAME  REGION          BIASSEC   TRIMSEC  CHIP      
+
+CHIP.01  CHIP    2
+# TYPE                CELL   FILENAME:STR  EXTNAME  REGION          BIASSEC   TRIMSEC  CHIP      
+  CHIP.01.CELL.00     CELL   %f/%f01.%x    CELL02   RECIPE:[0,0:0,0] CHIP.01   [0,0:0,0]   
+  CHIP.01.CELL.01     CELL   %f/%f01.%x    CELL03   RECIPE:[0,0:0,0] CHIP.01   [0,0:0,0]   
+
+CHIP.00     CHIP   
+
+
+
+f12345
+CHIP37
+
+
+
+# cfh12k-split
+NCELL       S32    12
+NCHIP       S32    12
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC     
+CELL.00     STR    %f/%f00.%x  PHU      [0,0:0,0]   CHIP.00   [0,0:0,0]   
+CELL.01     STR    %f/%f01.%x  PHU      [0,0:0,0]   CHIP.01   [0,0:0,0]   
+CELL.02     STR    %f/%f02.%x  PHU      [0,0:0,0]   CHIP.02   [0,0:0,0]   
+
+# cfh12k-mef
+NCELL       S32    12
+NCHIP       S32    12
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC     
+CELL.00     STR    %f.%x       CHIP00   [0,0:0,0]   CHIP.00   [0,0:0,0]   
+CELL.01     STR    %f.%x       CHIP01   [0,0:0,0]   CHIP.01   [0,0:0,0]   
+CELL.02     STR    %f.%x       CHIP02   [0,0:0,0]   CHIP.02   [0,0:0,0]   
+
+- REGION can be defined by a header keyword in IRAF format or by a explicit IRAF format 
+- what is default for NAXIS1,2 for IRAF format?
+- Nreadout is always NAXIS3?
+
+# recipe file:
+# this makes the assumption that, for a given camera, all chips &
+# cells have the same recipe.  this is probably a good start, but may
+# not cut it in general. eg, it is already clear that for 
+
+# recipe file must be a function of time and camera.
+# 
+
+# BIAS:
+BIAS.IMAGE                 STR    NONE
+BIAS.IMAGE  		   STR    FILE:bias.fits
+BIAS.IMAGE  		   STR    DB:BEST
+BIAS.IMAGE  		   STR    DB:CLOSE
+
+BIAS.OVERSCAN 		   STR    HEADER:BIASSEC
+BIAS.OVERSCAN 		   STR    RECIPE:[0,0:0,0]
+BIAS.OVERSCAN 		   STR    NONE
+
+BIAS.OVERSCAN.STATS 	   STR    MEDIAN
+BIAS.OVERSCAN.STATS 	   STR    MEAN
+
+BIAS.OVERSCAN.FIT          STR    SPLINE
+BIAS.OVERSCAN.FIT.NPTS     S32    5
+
+BIAS.OVERSCAN.FIT          STR    POLYNOMIAL
+BIAS.OVERSCAN.FIT.ORDER    S32    3
+BIAS.OVERSCAN.FIT.NBIN     S32    5
+
+bias image: 
+     none
+     filename
+     database: best
+     database: acceptable match
+
+overscan:
+     from header: BIASSEC
+     use region ['x0,x1:y0:y1']
+     statistics: median
+     direction PM_OVERSCAN_COLUMNS
+     fit: POLYNOMIAL
+     order: 3
+
+bias
+dark
+non-linearity
+flat
+mask
+trim
+fringe (no large-scale structure?)
+
+detect objects
+astrometrize objects
Index: /tags/sj_tags/sj_root_20080929/archive/modules/include/phase2.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/modules/include/phase2.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/modules/include/phase2.h	(revision 22322)
@@ -0,0 +1,204 @@
+#if !defined(PHASE2_H)
+#define PHASE2_H
+
+/** TO BE DEFINED:
+ *  1. Is it the responsibility of the phase2Whatever function to get the appropriate metadata, or should
+ *  it be supplied by the caller?
+ *  2. We want to carry masks around with each image.  Should these be separate psImages, or should we
+ *  incorporate the mask into the definition of psImage?
+ *  3. In some cases, we don't yet know the best algorithm.  How do we make these APIs robust against
+ *  variation of algorithms and input parameters?  For example, the number of parameters may vary.
+ *
+ *  Possible answers:
+ *  1. It's the responsibility of the caller to supply all relevant information.  This means that the
+ *  caller has to worry about getting the current best choice parameters, not the function itself; the
+ *  function can just be dumb, and do what it's told.
+ *  2. Masks will be provided as a member (psImage *mask) of psReadout.
+ *  3. Provide an "int algorithmNumber" plus "void *parameters" which will point to a struct containing the
+ *  parameters for the task.
+ *
+ */
+
+/************************************************************************************************************/
+
+/* Bias/overscan subtraction and trim */
+
+/** Overscan axis */
+typedef enum {
+    PM_OVERSCAN_NONE,			///< No overscan subtraction
+    PM_OVERSCAN_ROWS,			///< Subtract rows
+    PM_OVERSCAN_COLUMNS, 		///< Subtract columns
+    PM_OVERSCAN_ALL			///< Subtract the statistic of all pixels in overscan region
+} pmOverscanAxis;
+
+/** Fit types */
+typedef enum {
+    PM_FIT_NONE,			///< No fit
+    PM_FIT_POLYNOMIAL,			///< Fit polynomial
+    PM_FIT_SPLINE			///< Fit cubic splines
+} pmFit;
+
+/** Subtracts an overscan and bias from the input image. */
+psReadout *pmSubtractBias(psReadout *in, ///< Input image to be de-biased, and output
+			  void *fitSpec, ///< Polynomial/Spline/Some other function(?) specification,
+			                 ///< Also outputted
+			  const psList *overscans, ///< Linked list of subimages of overscans
+			  pmOverscanAxis overscanAxis, ///< Overscan axis
+			  const psStats *stat, ///< Statistic to use
+			  int nBin, ///< Number of bins for overscan vector before fitting
+			  pmFit fit, ///< How to fit the bias
+			  const psReadout *bias ///< Bias (or dark) image to subtract
+    );
+
+/************************************************************************************************************/
+
+/* Correction for non-linearity */
+
+/** Non-linearity correction via polynomial */
+psReadout *pmNonLinearityPolynomial(psReadout *in, ///< Input image to be corrected, and output
+				    const psPolynomial1D *coeff ///< Polynomial with which to correct
+    );
+
+/** Non-linearity correction via lookup table */
+psReadout *pmNonLinearityLookup(psReadout *in, ///< Input image to be corrected, and output
+				const psVector *inFlux, ///< Input flux values
+				const psVector *outFlux ///< Output flux values
+    );
+
+/************************************************************************************************************/
+
+/** Flat-fields the image. */
+psReadout *pmFlatField(psReadout *in, ///< Input image to be flat-fielded, and output
+		       const psReadout *flat ///< Flat-field image
+    );
+
+/************************************************************************************************************/
+
+/* Masking */
+
+/** Mask values */
+typedef enum {
+    PM_MASK_TRAP       = 0x0001,	///< The pixel is a charge trap
+    PM_MASK_BADCOL     = 0x0002,	///< The pixel is a bad column
+    PM_MASK_SAT        = 0x0004,	///< The pixel is saturated
+    PM_MASK_FLAT       = 0x0008,	///< The pixel is non-positive in the flat-field
+    PM_MASK_CR_MORPH   = 0x000F,	///< The pixel is determined to be a cosmic ray, from morphology
+} pmMaskValue;
+
+/** Returns an image that has the bad pixels masked.  Also masks saturated pixels */
+psReadout *pmMaskBadPixels(psReadout *in, ///< Image to be masked, and output
+			   const psImage *mask, ///< Bad pixel mask to apply
+			   unsigned int maskVal, ///< Mask the pixels for which mask & maskVal > 0.
+			   float sat, ///< Saturation level: pixels brighter than this level are masked
+			   unsigned int growVal, ///< Grow bad pixels for which mask & growVal > 0.
+			   int grow ///< Radius to grow the bad pixels
+    );
+
+/************************************************************************************************************/
+/************************************************************************************************************/
+/************************************************************************************************************/
+
+/* Below functions are still under development */
+
+/************************************************************************************************************/
+/************************************************************************************************************/
+/************************************************************************************************************/
+
+/* Reading in an image */
+
+/** Generate an FPA structure on the basis of the configuration file */
+psFPA *psFPAGenerate(FILE *config,	///< Configuration file, already open
+		     const char *fpaName ///< FPA name to generate for
+		     );
+
+/** Read all pixel data into a generated FPA structure */
+psFPA *psFPAReadAll(psFPA *in		///< FPA to read into, and output
+		    );
+
+/** Read only a particular chip */
+psFPA *psFPAReadChip(psFPA *in,		///< FPA to read into, and output
+		     const char *chipName ///< Chip name to read
+		     );
+
+/** Read only a particular cell into a chip that has been set up with psFPAGenerate */
+psChip *psChipReadCell(psChip *in,	///< Chip to read into, and output
+		       const char *cellName ///< Cell name to read
+		       );
+
+/** Read only a particular readout into a cell that has been set up with psFPAGenerate */
+psCell *psCellReadReadout(psCell *in,	///< Cell to read into, and output
+			  const char *readoutName ///< Readout name to read
+			  );
+
+/************************************************************************************************************/
+
+/** Masks Cosmic Rays on the input image on the basis of morphology. */
+psReadout *psPhase2MaskCRs(psReadout *in, ///< Input image to be masked, and output
+			   int algorithm, ///< Algorithm number to use
+			   const void *params ///< Parameters for algorithm
+			   );
+
+/** Masks optical defects in the input image */
+psChip *psPhase2MaskOpticalDefects(psChip *in, ///< Image to be masked (with astrometry), and output
+				   const psDlist *catalog, ///< Catalog stars nearby: a list of psObjects
+				   int grow ///< Number of pixels to grow
+				   );
+
+/************************************************************************************************************/
+
+/** Simple sky subtraction */
+psReadout *psPhase2SubtractSky(psReadout *in, ///< Input image to be sky-subtracted, and output
+			       void *fitSpec, ///< Polynomial/Spline specification, returns coeffcients
+			       psFit fit, ///< Fit type
+			       int binFactor, ///< Binning factor to use on image before solving for polynomial
+			       psStats *stats, ///< Statistics to use in binning
+			       float clipSD ///< Standard deviations to clip at after binning.
+			       );
+
+/************************************************************************************************************/
+
+/* Object finding */
+
+/** Measures the PSF on the input image.  Returns the PSF */
+psImage *psPhase2MeasurePSF(const psReadout *in, ///< Input image for which to measure the PSF
+			    int algorithm, ///< Algorithm number to use
+			    const void *params ///< Parameters for algorithm
+			    );
+
+/** Find and measure objects on the input image.  Fills in the "objects" member of the psReadout. */
+/** THIS NEEDS WORK. */
+psReadout *psPhase2FindObjects(psReadout *in, ///< Input image on which to find objects, and output
+			       const psImage *psf, ///< PSF to use to find objects
+			       const psVector *levels ///< Threshold levels (std dev.s)
+			       );
+
+/************************************************************************************************************/
+
+/* Astrometry */
+
+/** Corrects astrometry on the input chip */
+psChip *psPhase2Astrometry(psChip *in,	///< Input chip for which to do astrometry, and output
+			   const psDlist *catalog, ///< Catalog stars on the chip: a list of psObjects
+			   int nClips,	///< Number of clipping iterations
+			   float clipLevel ///< Level at which to clip
+			   );
+
+/************************************************************************************************************/
+
+/* Postage stamps */
+
+/** Specification of a postage stamp: location on the sky */
+typedef struct {
+    psSphereCoord *center;		///< Centre of postage stamp
+    psSphereCoord *size;		///< Size of postage stamp
+} psPostageStampSpec;
+
+/** Return postage stamps of a set of regions */
+psImageArray *psPhase2PostageStamps(const psChip *in, ///< Chip from which to form postage stamps
+				    const psDlist *regions ///< Regions to postage-stampise: a list of
+				                           ///< psPostageStampSpec
+				    );
+
+/************************************************************************************************************/
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/modules/include/phase4stuff.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/modules/include/phase4stuff.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/modules/include/phase4stuff.h	(revision 22322)
@@ -0,0 +1,369 @@
+// A list of pixel coordinates
+typedef struct {
+    psVector *x;			// x coordinate
+    psVector *y;			// y coordinate
+} psPixels;
+
+// Convert from a list of pixels to a U8 mask image
+psImage *psPixelsToMask(psImage *out,// Output mask image, or NULL
+			const psPixels *pixels, // Pixels to convert
+			const psRegion *region,	// Range of values; also specifies size of output image
+			unsigned int maskVal // Value to give mask
+			);
+
+// Convert from a U8 mask image to a list of pixels
+psPixels *psMaskToPixels(psPixels *out,	// Output pixels, or NULL
+			 const psImage *mask, // Mask image to convert
+			 unsigned int maskVal // Values to convert
+			 );
+
+// Concatenate a list of pixels onto another list
+psPixels *psPixelsConcatenate(psPixels *out, // Output pixels, or NULL
+			      const psPixels *pixels // Pixels to add to list
+			      );
+
+// Return combined image
+psImage *pmCombineImages(psImage *combined, // Combined image
+			 psArray **rejected, // Array of rejection masks
+			 const psArray *images, // Array of input images
+			 const psArray *errors, // Array of input error images
+			 const psArray *masks,// Array of input masks
+			 unsigned int maskVal, // Mask value
+			 const psArray *pixels,	// Pixels to combine
+			 int numIter,	// Number of rejection iterations
+			 float sigmaClip, // Number of standard deviations at which to reject
+			 const psStats *stats // Statistics to use in the combination
+			 );
+
+// Return the derivative of the transformation at the specified coordinates
+psPlane *psPlaneTransformDeriv(psPlane *out, // Output value, or NULL
+			       const psPlaneTransform *map, // Transformation to apply
+			       const psPlane *in // Coordinates at which to evaluate the derivative
+			       );
+
+// Transform a list of pixels from one coordinate frame to another.
+// Include overlaps (need to account for derivatives).
+psPixels *psPixelsTransform(psPixels *out, // Output pixels, or NULL
+			    const psPixels *in, // Input pixels
+			    const psPlaneTransform *inToOut // Transformation map
+			    );
+
+// Transform an image --- different from current version, nothing major
+psImage *psImageTransform(psImage *output, // Output image, or NULL
+			  psPixels **blankPixels, // List of blank pixels
+			  const psImage *input,	// Input image
+			  const psImage *inputMask, // Input image mask, or NULL
+			  int inputMaskVal, // Mask value
+			  double exposedValue, // Value for "exposed" pixels in the transformation
+                          const psPlaneTransform *outToIn, // Transformation
+			  const psRegion *region, // Region of output to consider (defines image size)
+			  const psPixels *pixels, // Pixels in output image to transform, or NULL for all
+			  psImageInterpolateMode mode // Mode of interpolation
+			  );
+
+// Return an array of psPixels that are rejected
+psArray *pmRejectPixels(const psArray *images, // Array of input images
+			const psArray *pixels, // These are the pixels which were rejected in the combination
+			const psPlaneTransform *inToOut, // Transformation from input to output system
+			const psPlaneTransform *outToIn, // Transformation from output to input system
+			float rejThreshold, // Rejection threshold
+			float gradLimit // Gradient limit
+			);
+
+
+// The image combination looks something like this:
+psImage *imageCombination(const psArray *inputs, // Input detector images
+			  psArray *inputMask,	// Input mask images
+			  const psArray *inputsErr, // Input error images
+			  const psArray *skyToDetector, // Maps from sky coordinates to detector coordinates
+			  const psRegion *combineRegion, // Sky coordinate pixels to combine
+			  int numIter,	// Number of iterations
+			  float rejThreshold, // Threshold for rejection
+			  float gradLimit // Limit for gradient
+	      )
+{
+    psArray *transformed = psArrayAlloc(nImages); // Array of transformed images
+    psArray *transformedErr = psArrayAlloc(nImages); // Array of transformed error images
+    psArray *transformedMask = psArrayAlloc(nImages); // Array of masks for transformed images
+
+    for (int i = 0; i < nImages; i++) {
+	psPixels *blanks = NULL;	// List of blank pixels
+	transformed->data[i] = psImageTransform(NULL, &blanks, inputs->data[i],
+						inputMask->data[i], inputMaskVal, NAN, skyToDetector,
+						combineRegion, NULL, PS_INTERPOLATE_BILINEAR);
+	transformedErr->data[i] = psImageTransform(NULL, NULL, inputsErr->data[i], inputMask->data[i],
+						   inputMaskVal, NAN, skyToDetector, combineRegion, NULL,
+						   PS_INTERPOLATE_BILINEAR_VARIANCE);
+	psImage *skyImage = transformed->data[i]; // Dereference the transformed image
+	psRegion *blankRegion = psRegionAlloc(0, 0, skyImage->numCols, skyImage->numRows); // Size of
+											   // transformed
+											   // image
+	transformedMask->data[i] = psPixelsToMask(NULL, blanks, blankRegion, PS_MASK_BLANK);
+	psFree(blankRegion);
+	psFree(blanks);
+    }
+
+    psArray *rejected = NULL;		// Array of rejected pixel lists
+    psStats *combineStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // Statistic to use in doing the combination
+    psImage *combined = pmCombineImages(NULL, &rejected, transformed, transformedErr, transformedMask, 0,
+					NULL, numIter, sigmaClip, combineStats); // Combined image
+    psArray *bad = pmRejectPixels(inputs, rejected, NULL, skyToDetector, rejThreshold, gradLimit); // Bad pix
+    psPixels *combinePixels = NULL;	// Pixels to combine
+    for (int i = 0; i < nImages; i++) {
+	psPixels *badSource = psPixelsTransform(NULL, bad->data[i], skyToDetector); // Bad pixels on the input
+	psImage *badMask = psPixelsToMask(NULL, badSource, PS_MASK_COSMICRAY); // Mask image for the input
+	(void)psBinaryOp(inputMask->data[i], inputMask->data[i], "|", badMask);	// Put CRs into original mask
+	psFree(badSource);
+	psFree(badMask);
+
+	combinePixels = psPixelsConcatenate(redo, bad->data[i]);
+
+	// Update transformed image
+	psPixels *blanks = NULL;	// List of blank pixels
+	transformed->data[i] = psImageTransform(transformed->data[i], &blanks, inputs->data[i],
+						inputMask->data[i], inputMaskVal | PS_MASK_COSMICRAY, NAN,
+						skyToDetector, combineRegion, bad->data[i],
+						PS_INTERPOLATE_BILINEAR);
+	transformedErr->data[i] = psImageTransform(transformedErr->data[i], NULL, inputsErr->data[i],
+						   inputMask->data[i], inputMaskVal | PS_MASK_COSMICRAY,
+						   NAN, skyToDetector, combineRegion, bad->data[i],
+						   PS_INTERPOLATE_BILINEAR_VARIANCE);
+	psImage *skyImage = transformed->data[i]; // Dereference the transformed image
+	psRegion *blankRegion = psRegionAlloc(0, 0, skyImage->numCols, skyImage->numRows); // Size of
+											   // transformed
+											   // image
+	transformedMask->data[i] = psPixelsToMask(transformedMask->data[i], blanks, blankRegion,
+						  PS_MASK_BLANK);
+	psFree(blankRegion);
+	psFree(blanks);
+    }
+    psFree(bad);
+
+    // Combine with no rejection
+    combined = pmCombineImages(combined, NULL, transformed, transformedErr, transformedMask,
+			       PS_MASK_BLANK, combinePixels, 0, 0.0, combineStats);
+    psFree(combineStats);
+    psFree(combinePixels);
+    psFree(transformed);
+    psFree(transformedErr);
+    psFree(transformedMask);
+
+    return combined;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Image subtraction APIs                                                                                   //
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+typedef enum {
+    PM_STAMP_USED,			// Use this stamp
+    PM_STAMP_RESET,			// This stamp has been rejected --- reset
+    PM_STAMP_RECALC,			// Having been reset, this stamp needs to be recalculated
+    PM_STAMP_NONE			// No stamp in this region
+} pmStampStatus;
+
+typedef struct {
+    int x, y;				// Position
+    psImage *matrix;			// Associated matrix
+    psVector *vector;			// Associated vector
+    psStampStatus status;		// Status of stamp
+} pmStamp;
+
+// There's no reason why we can't do *both* ISIS and POIS --- it only takes a couple of changes.
+// Let's leave in the flexibility.
+
+typedef enum {
+    PM_SUBTRACTION_KERNEL_POIS,		// POIS kernel --- delta functions
+    PM_SUBTRACTION_KERNEL_ISIS		// ISIS kernel --- gaussians modified by polynomials
+} pmSubtractionKernelType;
+
+typedef struct {
+    pmSubtractionKernelType type;	// Type of kernel --- allowing the use of multiple kernels
+    int u, v;				// Offset (for POIS) or polynomial order (for ISIS)
+    int xOrder, yOrder;			// Spatial polynomial order (for all)
+    float sigma;			// Width of Gaussian (for ISIS)
+    psImage *preCalc;			// Pre-calculated kernel (to accelerate ISIS; don't use for POIS)
+} pmSubtractionKernel;
+
+// Create the kernel basis functions
+psArray *pmSubtractionKernelsGeneratePOIS(int size, // Half-size of kernel (full size is 2*size+1)
+					  int spatialOrder // Maximum spatial order
+					  );
+psArray *pmSubtractionKernelsGenerateISIS(const psVector *sigmas, // Gaussian widths
+					  const psVector *orders, // Polynomial orders for the gaussians
+					  int size, // Half-size of kernel (full size is 2*size+1)
+					  int spatialOrder	// Maximum spatial order
+					  );
+
+psImage *psImageGrowMask(psImage *out,	// Output mask, or NULL
+			 const psImage *in, // Input mask
+			 unsigned int maskVal, // Value to identify
+			 unsigned int growRadius, // Radius by which to grow
+			 unsigned int growValue	// Value to OR with grown pixels
+			 );
+
+// Return an array of stamps
+psArary *pmSubtractionFindStamps(psArray *stamps, // Output stamps, or NULL
+				 const psImage *image, // Image for which to find stamps
+				 const psImage *mask, // Mask
+				 unsigned int maskVal, // Value for mask
+				 float threshold, // Threshold for stamps in the image
+				 int xNum, int yNum, // Number of stamps in x and y
+				 int border // Border around image to ignore (should be size of kernel)
+				 );
+
+// Calculate the matrix equations for each kernel basis function
+int pmSubtractionCalculateEquation(psArray *stamps, // The stamps for which to calculate the equation
+				   const psImage *reference, // Reference image
+				   const psImage *input, // Input image
+				   const psArray *kernels, // The kernel basis functions
+				   int footprint // Half-size of region over which to calculate equation
+				   );
+
+// Solve the matrix equation (sum the stamps, solve)
+psVector *pmSubtractionSolveEquation(psVector *solution,	// Solution vector, or NULL
+				     const psArray *stamps // Array of stamps
+				     );
+
+// Convolve an image by the kernel
+psImage *pmSubtractionConvolveImage(psImage *out,// Output image, or NULL
+				    const psImage *in, // Input image, to be convolved
+				    const psImage *mask, // Mask image
+				    const psVector *solution, // The solution vector (the coefficients)
+				    const psArray *kernels, // The kernel parameters
+				    int footprint // Size of region over which spatial variation is small (to optimise)
+				    );
+
+// Reject stamps, based on the solution; return TRUE if rejection performed
+bool pmSubtractionRejectStamps(psArray *stamps, // Array of stamps to check for rejection
+			       psImage *mask, // Mask image
+			       unsigned int badStampMaskVal, // Value to use in mask for bad stamp
+			       int footprint, // Region to mask if stamp is bad
+			       float sigmaRej, // Number of RMS deviations above zero at which to reject
+			       const psImage *refImage, // Reference image
+			       const psImage *inImage, // Input image
+			       const psVector *solution, // Solution vector
+			       const psArray *kernelParams // Array of kernel parameters
+			       );
+
+// Create a kernel image from the solution vector
+psImage *pmSubtractionKernelImage(psImage *out,	// Output image, or NULL
+				  const psVector *solution, // Vector containing the kernel elements
+				  const psArray *kernelParams, // The kernel parameters
+				  float x, float y // The relative (-1 to 1) x position for which to get the
+						   // kernel
+				  );
+
+
+// The whole image subtraction process looks something like this:
+psImage *subtractImages(psImage **subtractionMask, // Mask for subtraction (output)
+			const psImage *reference, // Reference image
+			const psImage *refMask, // Mask for reference image
+			const psImage *input,	// Input image
+			const psImage *inMask,// Mask for input image
+			unsigned int maskVal, // Value to be masked
+			pmSubtractionKernelType kernelType, // Type of kernel
+			int kernelHalfSize, // Half the kernel size (full size is 2*kernelHalfSize + 1)
+			const psVector *sigmas, // Sigmas for ISIS Gaussians
+			const psVector *polyOrders, // Polynomial orders for ISIS Gaussians
+			int spatialOrder, // Maximum spatial order
+			float stampThreshold, // Threshold for stamps
+			int nStampsX, int nStampsY, // Number of stamps in x and y
+			int stampSize,	// Half size of stamp footprint
+			int numIter,	// Number of iterations on the stamps
+			float sigmaRej	// Rejection threshold
+			)
+{
+    // Mask around bad pixels in the reference image.  There are two cases to worry about:
+    // 1. Bad pixels within the kernel, which will affect the subtracted image
+    // 2. Bad pixels within the stamp, which affects the calculation of the kernel
+    psImage *subMask = psImageGrowMask(NULL, refMask, maskVal, kernelHalfSize, PS_MASK_NEAR_BAD);
+    (void)psImageGrowMask(subMask, refMask, maskVal, stampSize, PS_MASK_BAD_STAMP);
+    // Add in the mask for the input image.  Don't need to grow this, since it isn't convolved.
+    (void)psBinaryOp(subMask, subMask, "|", inMask);
+
+    // Generate kernel basis functions
+    psArray *kernels = NULL;		// Array of kernel basis functions
+    switch (kernelType) {
+      case PM_SUBTRACTION_KERNEL_POIS:
+	// Create the kernel basis functions
+	kernels = pmSubtractionKernelsGeneratePOIS(kernelHalfSize, spatialOrder);
+	break;
+      case PM_SUBTRACTION_KERNEL_ISIS:
+	kernels = pmSubtractionKernelsGenerateISIS(sigmas, polyOrders, kernelHalfSize, spatialOrder);
+	break;
+      default:
+	barf();
+    }
+
+    psArray *stamps = NULL;		// Array of stamps
+    psVector *kernelCoeffs = NULL;	// Coefficients for the kernels
+    bool rejected = true;		// Did we reject a stamp in the last iteration?
+
+    // Iterate for a solution
+    for (int iter = 0; iter < numIter && rejected; iter++) {
+
+	// Find stamps
+	stamps = pmSubtractionFindStamps(stamps, reference, subMask, maskVal | PS_MASK_BAD_STAMP,
+					 stampThreshold, nStampsX, nStampsY, stampSize, kernelHalfSize);
+
+	// Generate and solve matrix equations
+	(void)pmSubtractionCalculateEquation(stamps, reference, input, kernels, stampSize);
+	kernelCoeffs = pmSubtractionSolveEquation(kernelCoeffs, stamps);
+
+	// Reject bad stamps
+	rejected = pmSubtractionRejectStamps(stamps, subMask, PS_MASK_BAD_STAMP, stampSize, sigmaRej,
+					     reference, input, kernelCoeffs, kernels);
+    }
+
+    // Convolve the reference image
+    psImage *referenceConvolved = pmSubtractionConvolveImage(NULL, reference, subMask, kernelCoeffs, kernels);
+    // Subtract
+    psImage *subtracted = (psImage*)psBinaryOp(NULL, input, "-", referenceConvolved);
+
+    // What does the kernel look like?
+    psImage *kernelImage = pmSubtractionKernelImage(NULL, kernelCoeffs, kernels, 0.0, 0.0);
+    // Check/save kernel image, print statistics....
+
+    psFree(referenceConvolved);
+    psFree(stamps);
+    psFree(kernels);
+    psFree(kernelCoeffs);
+
+    *subtractionMask = subMask;
+    return subtracted;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// This stuff is still under development.  We don't need it immediately, but need to think about it for the //
+// long run.                                                                                                //
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+// A tangent plane on the sky, which may be a weird shape (triangle, hexagon, silly healpix thing).
+typedef struct {
+    double ra, dec;			// RA and Dec of tangent point
+    double plateScale;			// Plate scale (arcsec/pixel), identical in x and y
+    psArray *rows;			// The data, stored as an array of vectors
+    psVector *offsets;			// Integer offset for row
+} psSkyPlane;
+
+psImage *psSkyPlaneToImage(psImage *out,	// Output image, or NULL
+			   psImage **mask, // Output mask image, or NULL
+			   const psSkyPlane *sky // Input sky
+			   );
+
+psSkyPlane *psImageToSkyPlane(psSkyPlane *out, // Output sky, or NULL
+			      const psImage *image, // Input image
+			      const psImage *mask	// Mask image, or NULL
+			      );
+
+// Return sky planes within a region on the sky.
+// Could be broken into two logical steps: identify sky planes, then read/copy/retrieve
+psArray *psSkyPlaneRetrieve(double ra, double dec, // Coordinates around which to search
+			    int raSize, int decSize // Size around which to search
+			    );
+
+// Rough
+astrometry *psImageSetWCSFromTP(psImage *image, double ra, double dec, double plateScale);
Index: /tags/sj_tags/sj_root_20080929/archive/modules/src/pmAstrom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/modules/src/pmAstrom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/modules/src/pmAstrom.c	(revision 22322)
@@ -0,0 +1,295 @@
+# include "pmAstrom.h"
+
+// input: st1, st2 are psArray of psPlane? pmAstromObj
+
+static double maxOffset;  // maximum allowed offset between lists, in raw pixels
+static double Scale;      // grid pixel scale
+static double Offset;     // deltas to pixels
+
+bool p_pmAstromGridBin (int *pOut, int *qOut, double dP, int dQ) {
+
+    if (fabs(dP) > maxOffset) return false;
+    if (fabs(dQ) > maxOffset) return false;
+
+    *pOut = dP / Scale + Offset;
+    *qOut = dQ / Scale + Offset;
+    return true;
+}
+
+/* Illustration of the grid bins
+ dP        bin
+-35:-25 -> 0     bin = dP / Scale + Offset
+-25:-15 -> 1     Scale = 10
+-15:-05 -> 2     Offset = 3.5
+-05:+05 -> 3     nPix = 3 (maxOffset = 35 = (nPix + 0.5)*dPix
++05:+15 -> 4     dPix = 10
++15:+25 -> 5
++25:+35 -> 6
+
+maxOffsetRequest = 30
+nPix = (int) (maxOffset / dPix + 0.5);
+maxOffset = (nPix + 0.5)*Scale;
+*/
+
+psArray *pmAstromGridMatch (psArray *raw, psArray *ref, psMetadata *options) {
+
+    pmAstromGridMatchStat minStat, newStat
+
+    // find center of the st2 field (focal-plane coords)
+    pMin = 1e10; pMax = -1e10;
+    qMin = 1e10; qMax = -1e10;
+    for (int i = 0; i < st2->n; i++) {
+	ob2 = (pmAstromObj *)st2->data[i];
+	pMin = PS_MIN (ob2->P, pMin);
+	pMax = PS_MAX (ob2->P, pMax);
+	qMin = PS_MIN (ob2->Q, qMin);
+	qMax = PS_MAX (ob2->Q, qMax);
+    }
+    pCenter = 0.5*(pMin + pMax);
+    qCenter = 0.5*(qMin + qMax);
+
+    minStat.minMetric = 1e10;
+    for (angle = opt->minAngle; angle <= opt->maxAngle; angle += opt->delAngle) {
+	st2r = pmAstromRotateObj (st2, angle, pCenter, qCenter);
+	newStat = pmAstromGridMatchAngle (st1, st2r, opt);
+	newStat.angle = angle;
+	if (newStat.minMetric < minStat.minMetric) {
+	    minStat = newStat;
+	}
+    }
+    return (minStat);
+}
+
+// match the two lists using the binned delta-delta max
+pmAstromGridMatchStats pmAstromGridMatchAngle (psArray *st1, psArray *st2, pmAstromOpt *opt) {
+
+    // input lists must be projected to common focal plane
+    pmAstromGridMatchStats matchStats;
+    
+    // set the static scaling factors
+    nPixHalf = (int)(opt->gridOffset / opt->gridScale + 0.5);
+    nPix     = 2*nPixHalf + 1;
+
+    maxOffpix = opt->gridScale * (nPix + 0.5);
+    Offset    = maxOffpix / opt->gridScale; 
+    Scale     = opt->gridScale;
+
+    // ** can we assume the allocated image is init-ed?
+    gridNP = psImageAlloc (nPix, nPix, PS_TYPE_S32);
+    gridDP = psImageAlloc (nPix, nPix, PS_TYPE_F32);
+    gridDQ = psImageAlloc (nPix, nPix, PS_TYPE_F32);
+    gridD2 = psImageAlloc (nPix, nPix, PS_TYPE_F32);
+    psImageInit (gridNP);
+    psImageInit (gridDP);
+    psImageInit (gridDQ);
+    psImageInit (gridD2);
+
+    // short-cut names for grid images
+    NP = gridNP->data.F32;
+    DP = gridDP->data.F32;
+    DQ = gridDQ->data.F32;
+    D2 = gridD2->data.F32;
+
+    // accumulate grids for focal plane (L,M) matches
+    for (int i = 0; i < st1->n; i++) {
+	ob1 = (pmAstromObj *)st1->data[i];
+	for (int j = 0; j < st2->n; j++) {
+	    ob2 = (pmAstromObj *)st2->data[i];
+	    dP = ob1->P - ob2->P;
+	    if (fabs(dP) > maxOffset) continue;
+
+	    dQ = ob1->Q - ob2->Q;
+	    if (fabs(dQ) > maxOffset) continue;
+
+	    // find bin coordinates for this delta-delta
+	    p_pmAstromGridBin (&iP, &iQ, dP, dQ);
+		
+	    // accumulate dP2, dQ2? 
+	    NP[iQ][iP] ++;
+	    DP[iQ][iP] += dP;
+	    DQ[iQ][iP] += dQ;
+	    D2[iQ][iP] += PS_SQR(dP) + PS_SQR(dQ);
+	}
+    }
+
+    // find the max pixel
+    stats = psStatsAlloc (PS_STAT_MAX);
+    stats = psImageStats (stats, gridNP);
+
+    // only check bins with at least 1/2 of max bin
+    minNpt = 0.5*stats->max;
+
+    // find the 'best' bin
+    minMetric = minVar = 1e10;
+    minP = minQ = -1;
+    for (int j = 0; j < gridNP->nY; j++) {
+	for (int i = 0; i < gridNP->nX; i++) {
+
+	    if (NP[j][i] < minNpts) continue;
+
+	    // this metric emphasize a narrow peak with lots of sources over one with few.
+	    var = fabs((D2[j][i]/NP[j][i]) - PS_SQR(DP[j][i]/NP[j][i]) - PS_SQR(DQ[j][i]/NP[j][i]));
+	    metric = var / PS_SQR(PS_SQR(NP[j][i]));
+	    
+	    if (metric < minMetric) {
+		minMetric = metric;
+		minVar    = var;
+		minP      = i;
+		minQ      = j;
+	    }
+	}
+    }
+
+    // convert the bin to delta-delta
+    matchStats.dP        = DP[minQ][minP] / NP[minQ][minP];
+    matchStats.dQ        = DQ[minQ][minP] / NP[minQ][minP];
+    matchStats.minMetric = minMetric;
+    matchStats.minVar    = minVar;
+    matchStats.nMatch    = NP[minQ][minP];
+
+    return (matchStats);
+}
+
+pmAstromMatch *pmAstromRadiusMatch (psArray *st1, psArray *st2, pmAstromOpt *opt) {
+
+    // assume the lists are sorted, or sort in place?
+
+    // sort st1 by P
+    // sort st2 by P
+
+    int i = 0;
+    int j = 0;
+
+    double RADIUS = opt->matchRadius;
+    double RADIUS_SQR = PS_SQR (RADIUS);
+
+    while ((i < st1->n) && (j < st2->n)) {
+
+	dP = ((pmAstromObj *)st1->data[i])->P - ((pmAstromObj *)st2->data[i])->P;
+	if (dP < -RADIUS) {
+	    i++;
+	    continue;
+	}
+	if (dP > +RADIUS) {
+	    j++;
+	    continue;
+	}
+
+	jStart = j;
+	while ((dX > -RADIUS) && (j < st2->n)) {
+	    
+	    dP = ((pmAstromObj *)st1->data[i])->P - ((pmAstromObj *)st2->data[i])->P;
+	    dQ = ((pmAstromObj *)st1->data[i])->Q - ((pmAstromObj *)st2->data[i])->Q;
+	    dR = dP*dP + dQ*dQ;
+
+	    if (dR > RADIUS_SQR) {
+		j++;
+		continue;
+	    }
+	    
+	    // XXX got a match, add it to list
+	    j++;
+	}
+	j = jStart;
+	i++;
+    }
+    return (match);
+}
+
+pmAstromMatchedListFit (psArray *st1, psArray *st2, psArray *match, pmAstromOpt *opt) {
+
+  psVector *X = psVectorAlloc (match->n);
+  psVector *Y = psVectorAlloc (match->n);
+  psVector *x = psVectorAlloc (match->n);
+  psVector *y = psVectorAlloc (match->n);
+  
+  // take the matched stars, first fit 
+  for (i = 0; i < match->n; i++) {
+
+    pair = match->data[i];
+    ob1 = st1->data[pair->i1];
+    ob2 = st2->data[pair->i2];
+
+    X->data.F64[i] = ob1->P;
+    Y->data.F64[i] = ob1->Q;
+
+    x->data.F64[i] = ob2->P;
+    y->data.F64[i] = ob2->Q;
+
+    // use one or the other...
+    psPolynomial2D *xt = psVectorFitPolynomial2D (NULL, stats, NULL, 0, X, NULL, x, y)
+    psPolynomial2D *xt = psVectorClipFitPolynomial2D (NULL, stats, NULL, 0, X, NULL, x, y)
+
+    psPolynomial2D *yt = psVectorFitPolynomial2D (NULL, stats, NULL, 0, Y, NULL, x, y)
+    psPolynomial2D *yt = psVectorClipFitPolynomial2D (NULL, stats, NULL, 0, Y, NULL, x, y)
+
+      }
+
+}
+
+// rotate the focal-plane coordinates about the center coordinate
+// angle specified in radians
+psArray *pmAstromRotateObj (psArray *old, psPlane *center, double angle) {
+
+    pmAstromObj *newObj;
+
+    psArray *new = psArrayAlloc (old->n);
+
+    cs = cos(angle);
+    sn = sin(angle);
+    xCenter = center.x;
+    yCenter = center.y;
+    
+    for (int i = 0; i < old->n; i++) {
+
+	oldObj = (pmAstromObj *)old->data[i];
+	newObj = pmAstromObjCopy (oldObj);
+
+	P = oldObj->FP.x - xCenter;
+	Q = oldObj->FP.y - yCenter;
+	
+	newObj->FP.x = P*cs + Q*sn;
+	newObj->FP.y = Q*cs - P*sn;
+	
+	new->data[i] = newObj;
+    }
+    return (new);
+}
+
+pmAstromObj *pmAstromObjCopy (pmAstromObj *old) {
+
+  pmAstromObj *obj = pmAstromObjAlloc ();
+
+  obj[0] = old[0];
+
+  return (obj);
+}
+
+static void pmAstromObjFree (pmAstromObj *obj) {
+
+  if (obj == NULL) return;
+  return;
+}
+
+pmAstromObj *pmAstromObjAlloc (void) {
+
+  pmAstromObj *obj = psAlloc (sizeof(pmAstromObj));
+  psMemSetDeallocator(line, (psFreeFunc) pmAstromObjFree);
+
+  obj->pix.x = 0;
+  obj->pix.y = 0;
+
+  obj->FP.x = 0;
+  obj->FP.y = 0;
+
+  obj->TP.x = 0;
+  obj->TP.y = 0;
+
+  obj->sky.x = 0;
+  obj->sky.y = 0;
+
+  obj->mag = 0;
+
+  return (obj);
+}
+
Index: /tags/sj_tags/sj_root_20080929/archive/modules/src/pmAstrom.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/modules/src/pmAstrom.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/modules/src/pmAstrom.h	(revision 22322)
@@ -0,0 +1,36 @@
+# include <stdio.h>
+# include <strings.h>  // for strcasecmp
+# include <unistd.h>   // for unlink
+# include <pslib.h>
+
+psArray *pmAstromGridMatch (psArray *s1, psArray *s2, pmAstromGridMatchOpt *opt);
+psArray *pmAstromRotateObj (psArray *old, psPlane center, double angle);
+
+typedef struct {
+  psPlane pix;
+  psPlane FP;
+  psPlane TP;
+  psSphere sky;
+  double mag;
+} pmAstromObj;
+
+typedef struct {
+  int i1;
+  int i2;
+} pmAstromMatch;
+
+typedef struct {
+    double minAngle;
+    double maxAngle;
+    double delAngle;
+} pmAstromOpt;
+
+typedef struct {
+    double angle;
+    double dP;
+    double dQ;
+    double minMetric;
+    double minVar;
+    int    nMatch;
+} pmAstromGridMatchStats;
+
Index: /tags/sj_tags/sj_root_20080929/archive/modules/src/pmFPAfromHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/modules/src/pmFPAfromHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/modules/src/pmFPAfromHeader.c	(revision 22322)
@@ -0,0 +1,385 @@
+
+/*** still need to use the values from the header to define the offset values.
+     make this a separate function (still divided into Chip/Cell Base versions
+     where does PHU go?
+***/
+
+psFPA *pmFPAfromHeader (psMetadata *headers, psMetadata *camera) {
+
+    char *extType = NULL;
+    psFPA *fpa = NULL;
+
+    extType = psMetadataLookup(camera, "EXT.TYPE");
+
+    if (!strcasecmp (extType, "cell")) {
+	fpa = pmFPAfromHeader_CellBase (headers, camera);
+	return (fpa);
+    } 
+    if (!strcasecmp (extType, "chip")) {
+	fpa = pmFPAfromHeader_ChipBase (headers, camera);
+	return (fpa);
+    } 
+    fprintf (stderr, "unknown EXT.TYPE value %s\n", extType);
+    return (NULL);
+}
+
+/** what do we do with the PHU?  **/
+
+psFPA *pmFPAfromHeader_CellBase (psMetadata *headers, psMetadata *camera) {
+
+    char *extID   = NULL;
+    char *cellID  = NULL;
+
+    int   nCellMD = psMetadataLookupS32(camera, "NCELL");       // number of cells in camera definition file 
+    char *extKey  = psMetadataLookupPtr(camera, "EXT.KEY");     // identifier for a cell
+    char *cellFmt = psMetadataLookupPtr(camera, "CELL.FORMAT"); // format used to define the CELL keywords
+
+    psHash *chipsByID  = psHashAlloc ();		// this will store the list of cells for this chip
+    psHash *cellsByExt = psHashAlloc ();		// this will store the list of cells for this chip
+
+    psMetadata *cellMD     = NULL;
+    psMetadata *header     = NULL;
+    psListIterator *cursor = NULL;
+
+    /* construct a hash of the EXT.KEY entries (one per cell) */
+    cellsByExt = psHashAlloc ();
+    for (int i = 0; i < nCellMD; i++) {
+	sprintf (cell, cellFormat, i);
+	cellMD = psMetadataLookupPtr(camera, cell);
+	cellID = psMetadataLookupPtr(cellMD, "EXT.KEY");
+	psHashAdd (cellsByExt, cellID, cellMD);
+    }
+
+    /* examine all headers, try to find them in the CAMERA.CELL definition list */
+    cursor = psListIteratorAlloc(headers->list, PS_LIST_HEAD);
+    while ((header = (psMetadata *)psListGetNext(cursor)) != NULL) {
+	extID   = psMetadataLookupPtr(header, extKey);    // look up the EXTNAME keyword (or equiv) 
+	cellMD  = psHashLookup (cellsByExt, extID);
+	if (cellMD == NULL) {
+	    continue;  			/* extension not a valid cell */
+	}
+
+	Nreadout = MAX (psMetadataLookupS32 (header, "NAXIS3"), 1);
+	cell = psCellAlloc (Nreadout);
+	cell->metadata = psMetadataAdd (cell->metadata, PS_LIST_TAIL, "HEADER", PS_META_META, "header", header);
+	cell->metadata = psMetadataAdd (cell->metadata, PS_LIST_TAIL, "CELL", PS_META_META, "cell defs", cellMD);
+
+	chipID = psMetadataLookupPtr(cellMD, "CHIP");  // look for the chip value in this cell MD
+	cellArray = psHashLookup (chipsByID, chipID);
+	if (list == NULL) {
+	    cellArray = psArrayAlloc (10);
+	    status = psHashAdd(chipsByID, chipID, list);
+	}
+	status = psArrayAdd (cellArray, cell, 10);
+    }
+    psFree (cursor);
+
+    chipArray = psArrayFromHash (chipsByID);
+
+    fpa = psFPAAlloc ();
+    fpa->chips = psArrayAlloc(chipArray->n);
+    for (int i = 0; i < chipArray->n; i++) {
+	fpa->chips->data[i] = psChipAlloc ();
+	chip = fpa->chips->data[i];
+	chip->cells = chipArray->data[i];
+    }
+    fpa->metadata = psMetadataAdd (fpa->metadata, PS_LIST_TAIL, "CAMERA", PS_META_META, "camera definition data", camera);
+
+    pmFPA_DefineOffsets_CellBase (fpa);
+    return (fpa);
+}
+
+psFPA *pmFPAfromHeader_ChipBase (psMetadata *headers, psMetadata *camera) {
+
+    char *extID   = NULL;
+    char *cellID  = NULL;
+
+    int   nCellMD = psMetadataLookupS32(camera, "NCELL");
+    char *extKey  = psMetadataLookupPtr(camera, "EXT.KEY");
+    char *cellFmt = psMetadataLookupPtr(camera, "CELL.FMT");
+
+    psHash *chipsByID  = psHashAlloc ();		// this will store the list of cells for this chip
+    psHash *cellsByExt = psHashAlloc ();		// this will store the list of cells for this chip
+
+    psMetadata *cellMD     = NULL;
+    psMetadata *header     = NULL;
+    psListIterator *cursor = NULL;
+
+    /* construct a hash of the EXT.KEY entries (one per chip) */
+    chipsByExt = psHashAlloc ();
+    for (int i = 0; i < nCellMD; i++) {
+	char cell[64];
+	sprintf (cell, cellFormat, i);
+	cellMD = psMetadataLookupPtr(camera, cell);
+	chipID = psMetadataLookupPtr(cellMD, "EXT.KEY");
+	cells  = psHashLookup (chipsByExt, chipID);
+	if (cells == NULL) {
+	    cells = psListAlloc ();
+	    psHashAdd (chipsByExt, cellID, cells);
+	}
+	psListAdd (cells, PS_LIST_TAIL, cellMD);
+    }
+
+    chipArray = psArrayAlloc (10);
+    chipArray->n = 0;
+
+    /* examine all headers, try to find them in the CAMERA.CELL definition list */
+    cursor = psListIteratorAlloc(headers->list, PS_LIST_HEAD);
+    while ((header = (psMetadata *)psListGetNext(cursor)) != NULL) {
+	extID = psMetadataLookupPtr(header, extKey);
+	cells = psHashLookup(chipsByExt, extID);
+	if (cells == NULL) {
+	    continue;  			/* extension not a valid cell */
+	}
+
+	cellArray = psArrayFromList (cells);
+	Nreadout = MAX (psMetadataLookupS32 (header, "NAXIS3"), 1);
+
+	chip = psChipAlloc ();
+	chipArray = psArrayAdd (chipArray, chip, 10);
+
+	chip->cells = psArrayAlloc(cellArray->n);
+	for (i = 0; i < cellArray->n; i++) {
+	    chip->cells->data[i] = psCellAlloc (Nreadout);
+	    cell = chip->cells->data[i];
+	    cell->metadata = psMetadataAdd (cell->metadata, PS_LIST_TAIL, "CELL", PS_META_META, "cell defs", cellArray->data[i]);
+	}	    
+	chip->metadata = psMetadataAdd (chip->metadata, PS_LIST_TAIL, "HEADER", PS_META_META, "header", header);
+    }
+    psFree (cursor);
+    pmFPA_DefineOffsets_CellBase (fpa);
+    return (fpa);
+}
+
+/* define the values for fpa->chip.x0,y0, etc */
+bool pmFPADefineOffsets_CellBase (psFPA fpa) {
+
+    for (i = 0; i < fpa->chips->n; i++) {
+	chip = fpa->chips->data[i];
+	chip.x0 = 0;
+	chip.y0 = 0;
+	
+	for (j = 0; j < chip->cells->n; j++) {
+	    cell = chip->cells->data[i];
+
+	    header  = psMetadataLookupPtr(cell->metadata, "HEADER");
+	    cellMD  = psMetadataLookupPtr(cell->metadata, "CELL.MD");
+	    detsec  = pmRegionFromConfig(cellMD, header, "DETSEC");
+	    datasec = pmRegionFromConfig(cellMD, header, "DATASEC");
+
+	    xBin = pmConfigLookupS32(cellMD, header, "CCDBIN1");
+	    yBin = pmConfigLookupS32(cellMD, header, "CCDBIN2");
+
+	    cell.xP = (datasec.x0 > datasec.x1) ? -1 : 1;
+	    cell.yP = (datasec.y0 > datasec.y1) ? -1 : 1;
+	    
+	    cell.x0 = f(datasec,detsec);
+	    cell.y0 = f(datasec,detsec);
+	}
+    }
+}
+
+/* define the values for fpa->chip.x0,y0, etc */
+bool pmFPADefineOffsets_ChipBase (psFPA fpa) {
+
+    for (i = 0; i < fpa->chips->n; i++) {
+	chip = fpa->chips->data[i];
+	chip.x0 = 0;
+	chip.y0 = 0;
+	
+	/** need to get the cell IDs from AMPLIST **/
+	header = psMetadataLookupPtr(chip->metadata, "HEADER");
+
+	for (j = 0; j < chip->cells->n; j++) {
+	    cell = chip->cells->data[i];
+
+	    cellMD  = psMetadataLookupPtr(cell->metadata, "CELL.MD");
+	    detsec  = pmConfigFindRegion(cellMD, header, "DETSEC");
+	    datasec = pmConfigFindRegion(cellMD, header, "DATASEC");
+
+	    xBin = pmConfigLookupS32(cellMD, header, "CCDBIN1");
+	    yBin = pmConfigLookupS32(cellMD, header, "CCDBIN2");
+
+	    cell.xP = (datasec.x0 > datasec.x1) ? -1 : 1;
+	    cell.yP = (datasec.y0 > datasec.y1) ? -1 : 1;
+	    
+	    cell.x0 = f(datasec,detsec);
+	    cell.y0 = f(datasec,detsec);
+	}
+    }
+}
+
+psReadout *pmReadoutLoad (psReadout *input, psFits *f, psCell *cell, int plane) {
+
+    psReadout *readout;
+    psRegion *region;
+
+    readout = input;
+    if (readout == NULL) {
+	readout = psReadoutAlloc();
+    }
+
+    region = psRegionFromConfig (cell, header, "READOUT");
+    readout->image = psFitsReadImageSection(NULL, f, region, plane);
+    // does this read the header again?  if not, it needs the header
+
+    return (readout);
+}
+
+psRegion *pmConfigLookupRegion (psMetadata *config, psMetadata *header, char *name) {
+
+    char *source, *string;
+    psRegion *region;
+
+    source = psMetadataLookupPtr (config, name);
+
+    if (!strcasecmp (source, "NONE")) {
+	region = psRegionAlloc (0, 0, 0, 0); // the NULL section
+	return (region);
+    }
+
+    if (!strncasecmp (source, "HD:", 7)) {
+	char *keyword;
+    
+	keyword = &source[7]; 
+	string = psMetadataLookupPtr (readout[0].metadata, keyword);
+	region = psRegionFromString (string);
+	psFree (string);
+	psFree (source);
+	return (region);
+    }
+
+    if (!strncasecmp (source, "CF:", 7)) {
+	string = &source[7]; 
+	region = psRegionFromString (string);
+	psFree (source);
+	return (region);
+    }
+
+    psError ("invalid section");
+    return (NULL);
+
+}
+
+psS32 pmConfigLookupS32 (psMetadata *config, psMetadata *header, char *name) {
+
+    char *source, *string;
+    psS32 value;
+
+    source = psMetadataLookupPtr (config, name);
+
+    if (!strcasecmp (source, "NONE")) {
+	psFree (source);
+	return (0);
+    }
+
+    if (!strncasecmp (source, "HD:", 7)) {
+	char *keyword;
+    
+	keyword = &source[7]; 
+	value = psMetadataLookupS32 (readout[0].metadata, keyword);
+	psFree (source);
+	return (value);
+    }
+
+    if (!strncasecmp (source, "CF:", 7)) {
+	value = atoi (&source[7]);
+	return (value);
+    }
+
+    psError ("invalid key");
+    return (0);
+
+}
+
+psF64 pmConfigLookupF64 (psMetadata *config, psMetadata *header, char *name) {
+
+    char *source, *string;
+    psF64 value;
+
+    source = psMetadataLookupPtr (config, name);
+
+    if (!strcasecmp (source, "NONE")) {
+	psFree (source);
+	return (0);
+    }
+
+    if (!strncasecmp (source, "HD:", 7)) {
+	char *keyword;
+    
+	keyword = &source[7]; 
+	value = psMetadataLookupF64 (readout[0].metadata, keyword);
+	psFree (source);
+	return (value);
+    }
+
+    if (!strncasecmp (source, "CF:", 7)) {
+	value = atof (&source[7]);
+	return (value);
+    }
+
+    psError ("invalid key");
+    return (0);
+
+}
+
+void *psMetdataLookupPtr (psMetadata *md, char *key) {
+
+    void *value = NULL;
+    psMetadataItem *item = psMetadataLookup (md, key);
+
+    if (item == NULL) {
+	return (NULL);
+    }
+
+    value = item->data.V;
+    psFree (item);
+
+    return (value);
+}
+
+psS32 psMetdataLookupS32 (psMetadata *md, char *key) {
+
+    psS32 value;
+    psMetadataItem *item = psMetadataLookup (md, key);
+
+    if (item == NULL) {
+	return (0.0);
+    }
+
+    value = item->data.S32;
+    psFree (item);
+
+    return (value);
+}
+
+psF64 psMetdataLookupF64 (psMetadata *md, char *key) {
+
+    psF64 value;
+    psMetadataItem *item = psMetadataLookup (md, key);
+
+    if (item == NULL) {
+	return (0.0);
+    }
+
+    value = item->data.F64;
+    psFree (item);
+
+    return (value);
+}
+
+psArray psArrayAdd (psArray *array, void *value, int delta) {
+
+    if (delta < 1) {
+	delta = 5;
+    }
+
+    N = array->n;
+    if (N >= array->nAlloc) {
+	array = psArrayRealloc (array, array->nAlloc + delta);
+    }
+  
+    array->data[N] = value;
+    return (array);
+}
+
Index: /tags/sj_tags/sj_root_20080929/archive/modules/src/pmFringe.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/modules/src/pmFringe.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/modules/src/pmFringe.c	(revision 22322)
@@ -0,0 +1,53 @@
+# include "pslib.h"
+
+typedef struct {
+    double xMin;
+    double yMin;
+    double xMax;
+    double yMax;
+    double delta;
+    double midValue;
+} pmFringePoint;
+
+stats *pmFringeStats (psImage *image, psArray *fringePoints, psMetadata *config) {
+
+    psRegion region;
+
+    double RADIUS = psMetadataLookupR32 (&status, myHeader, "FRINGE_SQUARE_RADIUS");
+
+    psStats *stats = psStatsAlloc (PS_STATS_ROBUST_MEDIAN | PS_STATS_ROBUST_STDEV);
+
+    // clip out the stars?
+
+    psVector *delta = psVectorAlloc (fringePoints->n, PS_DATA_F32);
+
+    for (i = 0; i < fringePoints->n; i++) {
+
+	pmFringePoint *point = fringePoints->data[i];
+	
+	region = psRegionForSquare (point->xMin, point->yMin, RADIUS);
+	region = psRegionForImage (image, region);
+	subimage = psImageSubset (image, region);
+	stats = psImageStats (stats, subimage, NULL, 0);
+	psImageFree (subimage);
+
+	min = stats->robustMedian;
+
+	region = psRegionForSquare (point->xMax, point->yMax, RADIUS);
+	region = psRegionForImage (image, region);
+	subimage = psImageSubset (image, region);
+	stats = psImageStats (stats, subimage, NULL, 0);
+	psImageFree (subimage);
+
+	max = stats->robustMedian;
+
+	// check for NaN, ignore 
+	point->delta = max - min;
+	point->midValue = 0.5*(min + max);
+
+	delta->data.F32[i] = point->delta;
+    }
+
+    stats = psVectorStats (stats, delta, NULL, NULL, 0);
+    return (value);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/psdb/makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/psdb/makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/psdb/makefile	(revision 22322)
@@ -0,0 +1,29 @@
+SHELL=/bin/sh
+CFG=`which mysql_config`
+CFLAGS=-g -O0 -pipe -fpic -Wall -pedantic -std=c99
+MYSQL_LIBS:=$(shell $(CFG) --libs)
+MYSQL_FLAGS:=$(shell $(CFG) --cflags)
+LIBS=-lpslib
+
+test_objects=test.o psleak.o
+objects=psDB.o
+
+all: libpsdb.so
+
+test: test-bin
+	LD_LIBRARY_PATH=. ./test
+     
+test-bin: $(test_objects) libpsdb.so
+	$(CC) $(CFLAGS) -o test $(test_objects) -L./ -lpsdb
+
+$(test-objects): %.o : %.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+libpsdb.so: $(objects)
+	$(CC) $(CFLAGS) -shared -o libpsdb.so $(objects) $(LIBS) $(MYSQL_FLAGS) $(MYSQL_LIBS)
+
+$(objects): %.o : %.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+clean:
+	$(RM) *.so *.o core test
Index: /tags/sj_tags/sj_root_20080929/archive/psdb/psDB.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/psdb/psDB.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/psdb/psDB.c	(revision 22322)
@@ -0,0 +1,1654 @@
+/** @file  psDB.c
+ *
+ *  @brief database functions
+ *
+ *  This file contains functions that perform basic database operations.  MySQL
+ *  4.1.2 or newer is required.
+ *
+ *  @author Joshua Hoblitt
+ *
+ *  @version $Revision: 1.34 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 03:22:06 $
+ *
+ *  Copyright 2005 Joshua Hoblitt, University of Hawaii
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <math.h>
+#include <mysql/mysql.h>
+#include <mysql/mysql_com.h> // enum_field_types
+
+#include "psDB.h"
+#include "pslib.h"
+
+typedef struct
+{
+    enum enum_field_types type;
+    bool            isUnsigned;
+} mysqlType;
+
+// database utility functions
+static bool     psDBRunQuery(psDB *dbh, const char *query);
+static inline bool psDBPackRow(MYSQL_BIND *bind, psMetadata *values, psU32 paramCount);
+
+// SQL generation functions
+static char    *psDBGenerateCreateTableSQL(const char *tableName, psMetadata *where);
+static char    *psDBGenerateSelectRowSQL(const char *tableName, const char *col, psMetadata *where, psU64 limit);
+static char    *psDBGenerateInsertRowSQL(const char *tableName, psMetadata *row);
+static char    *psDBGenerateUpdateRowSQL(const char *tableName, psMetadata *where, psMetadata *values);
+static char    *psDBGenerateDeleteRowSQL(const char *tableName, psMetadata *where);
+static char    *psDBGenerateWhereSQL(psMetadata *where);
+static char    *psDBGenerateSetSQL(psMetadata *set);
+
+// lookup table functions
+static psElemType psDBMySQLToPType(enum enum_field_types type, unsigned int flags);
+static char    *psDBPTypeToSQL(psElemType pType);
+static mysqlType *psDBPTypeToMySQL(psElemType pType);
+
+static psHash  *psDBGetPTypeToSQLTable(void);
+static void     psDBPTypeToSQLTableCleanup(void);
+
+static psHash  *psDBGetSQLToPTypeTable(void);
+static void     psDBSQLToPTypeTableCleanup(void);
+
+static psHash  *psDBGetMySQLToSQLTable(void);
+static void     psDBMySQLToSQLTableCleanup(void);
+
+static psHash  *psDBGetPTypeToMySQLTable(void);
+static void     psDBPTypeToMySQLTableCleanup(void);
+
+static psPtr    psDBMySQLTypeAlloc(enum enum_field_types type, bool isUnsigned);
+static void     psDBAddToLookupTable(psHash *lookupTable, psU32 type, const char *string);
+static void     psDBAddVoidToLookupTable(psHash *lookupTable, psU32 type, psPtr value);
+
+// pType utility functions
+static psPtr    psDBGetPTypeNaN(psElemType pType);
+static bool     psDBIsPTypeNaN(psElemType pType, psPtr data);
+
+// string utility functions
+static char    *psDBIntToString(psU64 n);
+static ssize_t  psStringAppend(char **dest, const char *format, ...);
+static ssize_t  psStringPrepend(char **dest, const char *format, ...);
+
+
+// public functions
+/*****************************************************************************/
+
+psDB *psDBInit(const char *host, const char *user, const char *passwd, const char *dbname)
+{
+    MYSQL           *mysql;
+    psDB            *dbh;
+
+    mysql = mysql_init(NULL);
+    if (!mysql) {
+         psAbort(__func__, "mysql_init(), out of memory.");
+    }
+
+    if (!mysql_real_connect(mysql, host, user, passwd, dbname, 0, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to connect to database.  Error: %s", mysql_error(mysql));
+
+        mysql_close(mysql);
+
+        return NULL;
+    }
+
+    dbh = psAlloc(sizeof(psDB));
+
+    dbh->mysql = mysql;
+
+    return dbh;
+}
+
+void psDBCleanup(psDB *dbh)
+{
+    mysql_close(dbh->mysql);
+    psFree(dbh);
+
+    // these lookup tables should probably be moved into psDB to prevent them
+    // from being flushed in the case that a database handle is closed while
+    // one or more are still open
+    psDBMySQLToSQLTableCleanup();
+    psDBPTypeToSQLTableCleanup();
+    psDBSQLToPTypeTableCleanup();
+    psDBPTypeToMySQLTableCleanup();
+}
+
+bool psDBCreate(psDB *dbh, const char *dbname)
+{
+    char            *query = NULL;
+    bool            status;
+    
+    psStringAppend(&query, "CREATE DATABASE %s", dbname);
+
+    // the MySQL C API notes that mysql_create_db() is deprecated
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to create new database.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+bool psDBChange(psDB *dbh, const char *dbname)
+{
+    if (mysql_select_db(dbh->mysql, dbname) != 0) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to change database.  Error: %s", mysql_error(dbh->mysql));
+           
+        return false;
+    }
+
+    return true;
+}
+
+bool psDBDrop(psDB *dbh, const char *dbname)
+{
+    char            *query = NULL;
+    bool            status;
+    
+    psStringAppend(&query, "DROP DATABASE %s", dbname);
+    
+    // the MySQL C API notes that mysql_drop_db() is deprecated
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to drop database.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+bool psDBCreateTable(psDB *dbh, const char *tableName, psMetadata *md)
+{
+    char            *query;
+    bool            status;
+
+    query = psDBGenerateCreateTableSQL(tableName, md);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return NULL;
+    }
+
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to create table.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+bool psDBDropTable(psDB *dbh, const char *tableName)
+{
+    char            *query = NULL;
+    bool            status;
+
+    psStringAppend(&query, "DROP TABLE %s", tableName);
+
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to drop table.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+psArray *psDBSelectColumn(psDB *dbh, const char *tableName, const char *col, psU64 limit)
+{
+    MYSQL_RES       *result;            // complete db result set
+    MYSQL_ROW       row;                // single row of db result set
+    char            *query;             // SQL query
+    psU64           rowCount;           // number of rows in db result set
+    psU64           dataSize;           // size of field
+    unsigned int    fieldCount;         // number of fields in db result set
+    psArray         *column = NULL;     // return array
+    psPtr           data;               // copy of result field
+
+    query = psDBGenerateSelectRowSQL(tableName, col, NULL, limit);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return NULL;
+    }
+
+    if (!psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Query execution failed.");
+
+        psFree(query);
+
+        return NULL;
+    }
+
+    psFree(query);
+
+    result = mysql_store_result(dbh->mysql);
+    if (!result) {
+        // no result set
+        fieldCount = mysql_field_count(dbh->mysql);
+
+        // if field count is zero the query returned no data.  If it's non-zero
+        // then something bad has happened.
+        if (fieldCount != 0) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Query returned no data.  Error: %s", mysql_error(dbh->mysql));
+
+            return NULL;
+        }
+    }
+
+    rowCount = (psU64)mysql_num_rows(result);
+
+    // pre-allocate enough elements to hold the complete result set
+    // then reset n to 0 so elements are added from the beginning of
+    // the array
+    column = psArrayAlloc(rowCount);
+    column->n = 0;
+
+    while ((row = mysql_fetch_row(result))) {
+        // get the first element of lengths array that is part of the
+        // result set
+        dataSize = (psU64)*mysql_fetch_lengths(result);
+
+        // represent NULL as an empty string
+        if (row[0] == NULL) {
+            data = psStringCopy("");
+        } else {
+            data = psStringNCopy(row[0], dataSize);
+        }
+
+        // add field to return array
+        psArrayAdd(column, 0, data);
+    }
+
+    mysql_free_result(result);
+
+    return column;
+}
+
+// dest = assign too, source = source string psArray, conv = conversion function,
+// type = type to cast to, pType = psElemType
+#define PS_STR_ARRAY_TO_PTYPE(dest, source, conv, type, pType) \
+{ \
+    psPtr           myNaN; \
+    int             i; \
+\
+    for (i = 0; i < source->n; i++) { \
+        if (strlen(source->data[i])) { \
+            dest[i] = (type)conv(source->data[i]); \
+        } else { \
+            myNaN = psDBGetPTypeNaN(pType); \
+            dest[i] = *(type *)myNaN; \
+            psFree(myNaN); \
+        } \
+    } \
+}
+
+psVector *psDBSelectColumnNum(psDB *dbh, const char *tableName, const char *col, psElemType pType, const psU64 limit)
+{
+    psArray         *stringColumn;      // source psArray
+    psVector        *column;            // dest psVector
+
+    stringColumn = psDBSelectColumn(dbh, tableName, col, limit);
+    if (!stringColumn) {
+        // could be an error or the result set was just empty
+        return NULL;
+    }
+
+    column = psVectorAlloc(stringColumn->n, pType);
+
+    // conversion functions are a portability issue
+    switch (pType) {
+        case PS_TYPE_S8:
+            PS_STR_ARRAY_TO_PTYPE(column->data.S8, stringColumn, atoi, psS8, PS_TYPE_S8);
+            break;
+        case PS_TYPE_S16:
+            PS_STR_ARRAY_TO_PTYPE(column->data.S16, stringColumn, atoi, psS16, PS_TYPE_S16);
+            break;
+        case PS_TYPE_S32:
+            PS_STR_ARRAY_TO_PTYPE(column->data.S32, stringColumn, atoi, psS32, PS_TYPE_S32);
+            break;
+        case PS_TYPE_S64:
+            PS_STR_ARRAY_TO_PTYPE(column->data.S64, stringColumn, atoll, psS64, PS_TYPE_S64);
+            break;
+        case PS_TYPE_U8:
+            PS_STR_ARRAY_TO_PTYPE(column->data.U8, stringColumn, atoi, psU8, PS_TYPE_U8);
+            break;
+        case PS_TYPE_U16:
+            PS_STR_ARRAY_TO_PTYPE(column->data.U16, stringColumn, atoi, psU16, PS_TYPE_U16);
+            break;
+        case PS_TYPE_U32:
+            PS_STR_ARRAY_TO_PTYPE(column->data.U32, stringColumn, atoi, psU32, PS_TYPE_U32);
+            break;
+        case PS_TYPE_U64:
+            PS_STR_ARRAY_TO_PTYPE(column->data.U64, stringColumn, atoll, psU64, PS_TYPE_U64);
+            break;
+        case PS_TYPE_F32:
+            PS_STR_ARRAY_TO_PTYPE(column->data.F32, stringColumn, atof, psF32, PS_TYPE_F32);
+            break;
+        case PS_TYPE_F64:
+            PS_STR_ARRAY_TO_PTYPE(column->data.F64, stringColumn, atof, psF64, PS_TYPE_F64);
+            break;
+        case PS_TYPE_C32:
+            // this is a bogus SQL type
+            PS_STR_ARRAY_TO_PTYPE(column->data.C32, stringColumn, atof, psC32, PS_TYPE_C32);
+            break;
+        case PS_TYPE_C64:
+            // this is a bogus SQL type
+            PS_STR_ARRAY_TO_PTYPE(column->data.C64, stringColumn, atof, psC64, PS_TYPE_C64);
+            break;
+        case PS_TYPE_BOOL:
+            // valid for psVector?
+            break;
+        case PS_TYPE_PTR:
+            // valid for psVector?
+            break;
+    }
+
+    psFree(stringColumn);
+
+    return column;
+}
+
+psArray *psDBSelectRows(psDB *dbh, const char *tableName, psMetadata *where, const psU64 limit)
+{
+    MYSQL_RES       *result;            // complete db result set
+    MYSQL_ROW       row;                // single row of db result set
+    MYSQL_FIELD     *field;             // field type info
+    char            *query;             // SQL query
+    psU64           rowCount;           // number of rows in db result set
+    psU64           fieldCount;         // number of fields in db result set
+    psU64           *fieldLength;       // field sizes
+    psArray         *resultSet;         // return array
+    int             i;                  // field index
+    psMetadata      *md;                // a row
+    psU32           pType;              // psElemType of a field
+    psU32           type;               // psMetadataType of a a field
+    psPtr           data;               // copy of result field
+
+    query = psDBGenerateSelectRowSQL(tableName, NULL, where, limit);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return NULL;
+    }
+
+    if (!psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Query execution failed.");
+
+        psFree(query);
+
+        return NULL;
+    }
+
+    psFree(query);
+
+    result = mysql_store_result(dbh->mysql);
+    if (!result) {
+        // no result set
+        fieldCount = mysql_field_count(dbh->mysql);
+
+        // if field count is zero the query should have returned no data.  If
+        // it's non-zero then something bad has happened.
+        if (fieldCount != 0) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Query returned no data.  Error: %s", mysql_error(dbh->mysql));
+
+            return NULL;
+        }
+    }
+
+    rowCount = (psU64)mysql_num_rows(result);
+
+    // pre-allocate enough elements to hold the complete result set
+    // then reset n to 0 so elements are added from the beginning of
+    // the array
+    resultSet = psArrayAlloc(rowCount);
+    resultSet->n = 0;
+
+    field = mysql_fetch_fields(result);
+    fieldCount = mysql_num_fields(result);
+
+    while ((row = mysql_fetch_row(result))) {
+        // allocate new psMetadata to represent a row
+        md = psMetadataAlloc();
+
+        fieldLength = (psU64 *)mysql_fetch_lengths(result);
+
+        for (i = 0; i < fieldCount; i++) {
+            // lookup MySQL column type
+            pType = psDBMySQLToPType(field[i].type, field[i].flags);
+
+            // copy field data and convert NULLs to the appropriate NaN value
+            if (pType == PS_TYPE_PTR) {
+                type = PS_META_STR;
+                data = fieldLength[i] ? psStringNCopy(row[i], fieldLength[i])
+                                      : psDBGetPTypeNaN(pType);
+            } else {
+                type = PS_META_PRIMITIVE;
+                if (fieldLength[i]) {
+                    data = psAlloc(fieldLength[i]);
+                    memcpy(data, row[i], fieldLength[i]);
+                } else {
+                    data = psDBGetPTypeNaN(pType);
+                }
+            }
+
+            // add new field to row
+            psMetadataAdd(md, i, field[i].name, pType, type, NULL, data);
+
+            psFree(data);
+        }
+
+        // add row to result set
+        psArrayAdd(resultSet, 0, md);
+    }
+
+    mysql_free_result(result);
+
+    return resultSet;
+}
+
+bool psDBInsertOneRow(psDB *dbh, const char *tableName, psMetadata *row)
+{
+    psArray         *rowSet;            // psArray of row to insert
+
+    // this is to prevent row from being free'd when rowSet is free'd. this
+    // will have to be removed when psArrayAdd is modified to do this for you.
+    psMemIncrRefCounter(row);
+
+    rowSet = psArrayAlloc(1);
+    rowSet->n = 0;
+    psArrayAdd(rowSet, 0, row);
+
+    if (!psDBInsertRows(dbh, tableName, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "Insert failed.");
+
+        psFree(rowSet);
+
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool psDBInsertRows(psDB *dbh, const char *tableName, psArray *rowSet)
+{
+    psMetadata      *row;               // row of data
+    char            *query;             // SQL query
+    MYSQL_STMT      *stmt;              // prepared db statement
+    MYSQL_BIND      *bind;              // field values to insert
+    psU32           paramCount;         // number of placeholders in query
+    psU64           j;                  // row index
+
+    // we are assuming that all rows in the set have an identical with reguard
+    // to field count and type
+    row = rowSet->data[0];
+
+    query = psDBGenerateInsertRowSQL(tableName, row);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return NULL;
+    }
+
+    stmt = mysql_stmt_init(dbh->mysql);
+    if (!stmt) {
+        psAbort(__func__, "mysql_stmt_init(), out of memory.");
+    }
+
+    if (mysql_stmt_prepare(stmt, query, (unsigned long)strlen(query))) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to prepare query.  Error: %s", mysql_stmt_error(stmt));
+
+        mysql_stmt_close(stmt);
+        psFree(query);
+
+        return false;
+    }
+
+    psFree(query);
+
+    // how many place holders are in our query
+    paramCount = (psU32)mysql_stmt_param_count(stmt);
+
+    // structure larger enough to hold one field of data per place holder
+    bind = psAlloc(sizeof(MYSQL_BIND) * paramCount);
+
+    // loop over rows
+    for (j = 0; j < rowSet->n; j++) {
+        row = rowSet->data[j];
+
+        // reset bind for each row
+        memset(bind, 0, sizeof(MYSQL_BIND) * paramCount);
+
+        if (!psDBPackRow(bind, row, paramCount)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to pack params into bind structure.");
+
+            mysql_rollback(dbh->mysql);
+
+            psFree(bind);
+            mysql_stmt_close(stmt);
+        }
+
+        if (mysql_stmt_bind_param(stmt, bind)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to bind params.  Error: %s", mysql_stmt_error(stmt));
+
+            mysql_rollback(dbh->mysql);
+
+            psFree(bind);
+            mysql_stmt_close(stmt);
+
+            return false;
+        }
+
+        if (mysql_stmt_execute(stmt)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to execute prepared statement.  Error: %s",
+                mysql_stmt_error(stmt));
+
+            mysql_rollback(dbh->mysql);
+
+            psFree(bind);
+            mysql_stmt_close(stmt);
+
+            return false;
+        }
+    } // end loop over rows
+
+    // point of no return
+    mysql_commit(dbh->mysql);
+
+    psFree(bind);
+    mysql_stmt_close(stmt);
+
+    return true;
+}
+
+psArray *psDBDumpRows(psDB *dbh, const char *tableName)
+{
+    return psDBSelectRows(dbh, tableName, NULL, 0);
+}
+
+psMetadata *psDBDumpCols(psDB *dbh, const char *tableName)
+{
+    MYSQL_RES       *result;
+    MYSQL_FIELD     *field;
+    psU32           fieldCount;
+    psMetadata      *table;
+    psU32           pType;
+    psU32           i;
+    psPtr           column;
+
+    // find column types
+    result = mysql_list_fields(dbh->mysql, tableName, NULL);
+    if (!result) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Failed to retrieve column types."); 
+    }
+
+    field = mysql_fetch_fields(result);
+    fieldCount = mysql_num_fields(result);
+
+    table = psMetadataAlloc();
+
+    // fetch each column and load into psMetadata
+    for (i =0; i < fieldCount; i++) {
+        // find ptype of column
+        pType = psDBMySQLToPType(field[i].type, field[i].flags);
+        
+        // if the ptype is PS_TYPE_PTR assume that it's a string and fetch the
+        // column as an psArray of strings; otherwise fetch the column as a
+        // psVector.  
+        if (pType == PS_TYPE_PTR) {
+            // PS_META_UNKNOWN -> PS_META_ARRAY ?
+            column = psDBSelectColumn(dbh, tableName, field[i].name, 0);
+            psMetadataAdd(table, 0, field[i].name, PS_TYPE_PTR, PS_META_UNKNOWN, "psArray", column);
+            // FIXME
+            //psFree(column);
+        } else {
+            column = psDBSelectColumnNum(dbh, tableName, field[i].name, pType, 0);
+            psMetadataAdd(table, 0, field[i].name, PS_TYPE_PTR, PS_META_VEC, "psVector", column);
+            // FIXME
+            //psFree(column);
+        }
+    }
+    
+    return table;
+}
+
+psS64 psDBUpdateRows(psDB *dbh, const char *tableName, psMetadata *where, psMetadata *values)
+{
+    char            *query;
+    MYSQL_STMT      *stmt;              // prepared db statement
+    psU32           paramCount;         // number of placeholders in query
+    MYSQL_BIND      *bind;              // field values to insert
+    psU64           rowsAffected;       // number of rows affected by query
+
+    query = psDBGenerateUpdateRowSQL(tableName, where, values);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return -1;
+    }
+
+    stmt = mysql_stmt_init(dbh->mysql);
+    if (!stmt) {
+        psAbort(__func__, "mysql_stmt_init(), out of memory.");
+    }
+
+    if (mysql_stmt_prepare(stmt, query, (unsigned long)strlen(query))) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to prepare query.  Error: %s", mysql_stmt_error(stmt));
+
+        mysql_stmt_close(stmt);
+        psFree(query);
+
+        return -1;
+    }
+
+    psFree(query);
+
+    // how many place holders are in our query
+    paramCount = (psU32)mysql_stmt_param_count(stmt);
+
+    // structure large enough to hold one field of data per place holder
+    bind = psAlloc(sizeof(MYSQL_BIND) * paramCount);
+
+    // init bind
+    memset(bind, 0, sizeof(MYSQL_BIND) * paramCount);
+
+    if (!psDBPackRow(bind, values, paramCount)) {
+        psFree(bind);
+        mysql_stmt_close(stmt);
+
+        return -1;
+    }
+
+    if (mysql_stmt_bind_param(stmt, bind)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to bind params.  Error: %s", mysql_stmt_error(stmt));
+
+        mysql_rollback(dbh->mysql);
+
+        psFree(bind);
+        mysql_stmt_close(stmt);
+
+        return -1;
+    }
+
+    if (mysql_stmt_execute(stmt)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to execute prepared statement.  Error: %s",
+            mysql_stmt_error(stmt));
+
+        mysql_rollback(dbh->mysql);
+
+        psFree(bind);
+        mysql_stmt_close(stmt);
+
+        return -1;
+    }
+
+    psFree(bind);
+
+    // point of no return
+    mysql_commit(dbh->mysql);
+
+    rowsAffected = (psU64)mysql_stmt_affected_rows(stmt);
+
+    mysql_stmt_close(stmt);
+
+    return rowsAffected;
+}
+
+psS64 psDBDeleteRows(psDB *dbh, const char *tableName, psMetadata *where)
+{
+    char            *query;
+
+    query = psDBGenerateDeleteRowSQL(tableName, where);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return -1;
+    }
+
+    if (!psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Delete failed.");
+
+        mysql_rollback(dbh->mysql);
+
+        psFree(query);
+
+        return -1;
+    }
+
+    psFree(query);
+
+    if (!where) {
+        // we truncated the table so mysql_affected_rows() won't work
+        return 1;
+    }
+
+    // point of no return
+    mysql_commit(dbh->mysql);
+
+    return (psS64)mysql_affected_rows(dbh->mysql);
+}
+
+// database utility functions
+/*****************************************************************************/
+
+static bool psDBRunQuery(psDB *dbh, const char *query)
+{
+    if (mysql_real_query(dbh->mysql, query, (unsigned long)strlen(query)) !=0) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to execute query.  Error: %s\n", mysql_error(dbh->mysql));
+           
+        return false;
+    }
+
+    return true;
+}
+
+static inline bool psDBPackRow(MYSQL_BIND *bind, psMetadata *values, psU32 paramCount)
+{
+    psListIterator  *cursor;            // row iterator
+    psMetadataItem  *item;              // field in row
+    mysqlType       *mType;             // type tmp variable
+    static bool     isNull = true;      // used in a MYSQL_BIND to indicate NULL,
+                                        // this will be used outside of this func
+    psU32           i;                  // field index
+
+    // check size of values == paramCount ?
+
+    cursor = psListIteratorAlloc(values->list, 0);
+
+    // loop over fields
+    i = 0;
+    while ((item = psListGetNext(cursor))) {
+        // lookup pType -> mysql type
+        mType = psDBPTypeToMySQL(item->pType);
+
+        bind[i].buffer_type = mType->type;
+        bind[i].is_unsigned = mType->isUnsigned;
+
+        psFree(mType);
+
+        // input data length is determined by the MYSQL_TYPE_* unless it's a string
+        if (item->type == PS_META_PRIMITIVE) {
+            switch (item->pType) {
+                case PS_TYPE_S32:
+                    bind[i].length  = 0;
+                    bind[i].buffer  = &item->data.S32;
+                    bind[i].is_null = psDBIsPTypeNaN(item->pType, &item->data.S32) 
+                                    ? (my_bool *)&isNull
+                                    : NULL;
+                    break;
+                case PS_TYPE_F32:
+                    bind[i].length  = 0;
+                    bind[i].buffer  = &item->data.F32;
+                    bind[i].is_null = psDBIsPTypeNaN(item->pType, &item->data.F32) 
+                                    ? (my_bool *)&isNull
+                                    : NULL;
+                    break;
+                case PS_TYPE_F64:
+                    bind[i].length  = 0;
+                    bind[i].buffer  = &item->data.F64;
+                    bind[i].is_null = psDBIsPTypeNaN(item->pType, &item->data.F64) 
+                                    ? (my_bool *)&isNull
+                                    : NULL;
+                    break;
+                default:
+                    psError(PS_ERR_BAD_PARAMETER_TYPE , true,
+            "FIXME: Only PS_META_PRIMITIVE values of PS_TYPE_S32, PS_TYPE_F32, and PS_TYPE_F64 supported.");
+                    break;
+            }
+        // convert NaNs to NULL and set the buffer_length for strings
+        } else if ((item->type == PS_META_STR) && (item->pType == PS_TYPE_PTR)) {
+            bind[i].buffer_length = (unsigned long)strlen((char *)item->data.V);
+            bind[i].length  = &bind[i].buffer_length;
+            bind[i].buffer  = item->data.V;
+            bind[i].is_null = *(char *)item->data.V == '\0' 
+                            ? (my_bool *)&isNull
+                            : NULL;
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE , true,
+                "Only types of PS_META_PRIMITIVE and PS_META_STR are supported.");
+
+            psFree(cursor);
+
+            return false;
+        }
+
+        // increment field index
+        i++;
+    }
+
+    psFree(cursor);
+
+    return true;
+}
+
+
+// SQL generation functions
+/*****************************************************************************/
+
+static char *psDBGenerateCreateTableSQL(const char *tableName, psMetadata *table)
+{
+    char            *query = NULL;      // complete query
+    psMetadataItem  *item;              // column description
+    psListIterator  *cursor;            // column iterator
+    char            *colType;           // type lookup table
+
+    if (!table) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "table param may not be null.");
+
+        return NULL;
+    }
+
+    psStringAppend(&query, "CREATE TABLE %s (", tableName);
+
+    cursor = psListIteratorAlloc(table->list, 0);
+
+    // find column name and type
+    while ((item = psListGetNext(cursor))) {
+        if (item->type == PS_META_PRIMITIVE) {
+            // + column name + _ + column type
+            colType = psDBPTypeToSQL(item->pType);
+            psStringAppend(&query, "%s %s", item->name, colType);
+            psFree(colType);
+        } else if ((item->type == PS_META_STR) && (item->pType == PS_TYPE_PTR)) {
+            // + column name + _ + varchar( + length + )
+            psStringAppend(&query, "%s VARCHAR(%s)", item->name, item->data.V);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Only types of PS_META_PRIMITIVE and PS_META_STR are supported.");
+
+            psFree(query);
+            psFree(cursor);
+
+            return NULL;
+        }
+
+        // add a , after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    psListIteratorSet(cursor, 0);
+
+    // find database indexes
+    while ((item = psListGetNext(cursor))) {
+        if ((strncmp(item->comment, "Primary Key", strlen("Primary Key"))) == 0) {
+            psStringAppend(&query, ", PRIMARY KEY(%s)", item->name);
+        } else if ((strncmp(item->comment, "Key", strlen("Key"))) == 0) {
+            psStringAppend(&query, ", KEY(%s)", item->name);
+        }
+    }
+
+    psFree(cursor);
+
+    // end column types + table type
+    psStringAppend(&query, ") ENGINE=innodb");
+
+    return query;
+}
+
+static char *psDBGenerateSelectRowSQL(const char *tableName, const char *col, psMetadata *where, const psU64 limit)
+{
+    char            *query = NULL;
+    char            *whereSQL;
+    char            *limitString;
+
+    // select all columns if col is NULL
+    if (col) {
+        psStringAppend(&query, "SELECT %s FROM %s", col, tableName);
+    } else {
+        psStringAppend(&query, "SELECT * FROM %s", tableName);
+    }
+
+    // select all rows if where is NULL
+    if (where) {
+        whereSQL = psDBGenerateWhereSQL(where);
+        if (!whereSQL) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+            psFree(query);
+
+            return NULL;
+        }
+        psStringAppend(&query, " %s", whereSQL);
+        psFree(whereSQL);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        limitString = psDBIntToString(limit);
+        psStringAppend(&query, " LIMIT %s", limitString);
+        psFree(limitString);
+    }
+
+    return query;
+}
+
+static char *psDBGenerateInsertRowSQL(const char *tableName, psMetadata *row)
+{
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    psStringAppend(&query, "INSERT INTO %s (", tableName);
+
+    cursor = psListIteratorAlloc(row->list, 0);
+
+    // get field names
+    while ((item = psListGetNext(cursor))) {
+        psStringAppend(&query, item->name);
+
+        // + , + _ between every field name
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    // end of field names
+    psStringAppend(&query, ") VALUES (");
+
+    psListIteratorSet(cursor, 0);
+
+    // create value place holders
+    while ((item = psListGetNext(cursor))) {
+        psStringAppend(&query, "?");
+
+        // + ", " between every place holder
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    psFree(cursor);
+
+    // end of values
+    psStringAppend(&query, ")");
+
+    return query;
+}
+
+static char *psDBGenerateUpdateRowSQL(const char *tableName, psMetadata *where, psMetadata *values)
+{
+    char            *query = NULL;
+    char            *setSQL;
+    char            *whereSQL;
+
+    if ((!values) || (!where)) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "values and where params may not be NULL.");
+
+        return NULL;
+    }
+
+    setSQL = psDBGenerateSetSQL(values);
+    if (!setSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+        return NULL;
+    }
+
+    whereSQL = psDBGenerateWhereSQL(where);
+    if (!whereSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+        return NULL;
+    }
+
+    psStringAppend(&query, "UPDATE %s %s %s", tableName, setSQL, whereSQL);
+    psFree(setSQL);
+    psFree(whereSQL);
+
+    return query;
+}
+
+static char *psDBGenerateDeleteRowSQL(const char *tableName, psMetadata *where)
+{
+    char            *query = NULL;
+    char            *whereSQL;
+
+    // delete all rows if where is NULL
+    if (!where) {
+        psStringAppend(&query, "TRUNCATE TABLE %s", tableName);
+
+        return query;
+    }
+    
+    whereSQL = psDBGenerateWhereSQL(where);
+    if (!whereSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+        return NULL;
+    }
+
+    psStringAppend(&query, "DELETE FROM %s %s", tableName, whereSQL);
+
+    return query;
+}
+
+static char *psDBGenerateWhereSQL(psMetadata *where)
+{
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    if (!where) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "where param may not be NULL.");
+
+        return NULL;
+    }
+
+    query = psStringCopy("WHERE ");
+
+    cursor = psListIteratorAlloc(where->list, 0);
+
+    // find column name and match pattern
+    while ((item = psListGetNext(cursor))) {
+        // item->data must be a string
+        if (item->type == PS_META_STR) {
+            // + column name + _ + like + _ + ' + value + '
+            if (*(char *)item->data.V == '\0') {
+                psStringAppend(&query, "%s IS NULL", item->name);
+            } else {
+                psStringAppend(&query, "%s LIKE '%s'", item->name, item->data.V);
+            }
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Only a type of PS_META_STR is supported.");
+
+            psFree(cursor);
+            psFree(query);
+
+            return NULL;
+        }
+
+        // + " and " after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, " AND ");
+        }
+    }
+
+    psFree(cursor);
+
+    return query;
+}
+
+static char *psDBGenerateSetSQL(psMetadata *set)
+{
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    if (!set) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "set param may not be NULL.");
+
+        return NULL;
+    }
+
+    query = psStringCopy("SET ");
+
+    cursor = psListIteratorAlloc(set->list, 0);
+
+    // find column name 
+    while ((item = psListGetNext(cursor))) {
+        // + column name + _ + = + _ + ?
+        psStringAppend(&query, "%s = ?", item->name);
+
+        // + ", " after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ",  ");
+        }
+    }
+
+    psFree(cursor);
+
+    return query;
+}
+
+
+// lookup table functions
+/*****************************************************************************/
+
+static psElemType psDBMySQLToPType(enum enum_field_types type, unsigned int flags)
+{
+    psHash          *mysqlToSQLTable;   // type lookup table
+    psHash          *sqlToPSTable;      // type lookup table
+    char            *key;               // hash tmp value
+    char            *value;             // hash tmp value
+    char            *sqlType;           // copy of lookup table result
+    psU32           pType;              // psElemType of a field
+
+    mysqlToSQLTable = psDBGetMySQLToSQLTable();
+    sqlToPSTable    = psDBGetSQLToPTypeTable();
+
+    // lookup MySQL column type
+    key     = psDBIntToString((psU64)type); 
+    sqlType = psHashLookup(mysqlToSQLTable, key);
+    psFree(key);
+    psFree(mysqlToSQLTable);
+
+    if (!sqlType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        psFree(sqlToPSTable);
+
+        return -1;
+    }
+
+    // MySQL column types can not be directly translated to PS
+    // types as the the mysql types do not tell you if the value is
+    // signed or unsigned.  The result is this ugly conversion from
+    // mysql -> ascii -> ptype
+    if (flags & UNSIGNED_FLAG) {
+        psStringPrepend(&sqlType, "UNSIGNED ");
+    }
+
+    // convert MySQL type to PS type
+    value = psHashLookup(sqlToPSTable, sqlType);
+    psFree(sqlToPSTable);
+
+    if (!value) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return -1;
+    }
+
+    pType = (psU32)atol(value);
+
+    return pType;
+}
+
+static char *psDBPTypeToSQL(psElemType pType)
+{
+    psHash          *pTypeToSQLTable;   // type lookup table
+    char            *key;               // hash tmp value
+    char            *sqlType;             // hash tmp value
+
+    pTypeToSQLTable = psDBGetPTypeToSQLTable();
+
+    key = psDBIntToString((psU64)pType);
+    sqlType = psHashLookup(pTypeToSQLTable, key);
+    psFree(key);
+    psFree(pTypeToSQLTable);
+
+    if (!sqlType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return NULL;
+    }
+
+    psMemIncrRefCounter(sqlType);
+
+    return sqlType;
+}
+
+static mysqlType *psDBPTypeToMySQL(psElemType pType)
+{
+    psHash          *pTypeToMySQLTable; // type lookup table
+    char            *key;               // hash tmp value
+    mysqlType       *mType;             // mysqlType struct to return
+    
+    pTypeToMySQLTable = psDBGetPTypeToMySQLTable();
+
+    key = psDBIntToString((psU64)pType);
+    mType = psHashLookup(pTypeToMySQLTable, key);
+    psFree(key);
+    psFree(pTypeToMySQLTable);
+
+    if (!mType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return NULL;
+    }
+
+    psMemIncrRefCounter(mType);
+
+    return mType;
+}
+
+static psHash *psDBGetPTypeToSQLTable(void)
+{
+    static psHash   *lookupTable = NULL;
+
+    if (!lookupTable) {
+        lookupTable = psHashAlloc(14);
+
+        // no support for CHAR, TEXT or GLOB
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S8,  "TINYINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S16, "SMALLINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S32, "INT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S64, "BIGINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U8,  "UNSIGNED TINYINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U16, "UNSIGNED SMALLINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U32, "UNSIGNED INT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U64, "UNSIGNED BIGINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_F32, "FLOAT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_F64, "DOUBLE");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_C32, "PS_TYPE_C32 is not supported");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_C64, "PS_TYPE_C64 is not supported");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_BOOL,"BOOLEAN");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_PTR, "VARCHAR");
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBPTypeToSQLTableCleanup(void) 
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetPTypeToSQLTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psHash *psDBGetSQLToPTypeTable(void)
+{
+    static psHash   *lookupTable = NULL;
+    psHash          *psToSQLTable;
+    psList          *list;
+    psListIterator  *cursor;
+    char            *key;
+    char            *value;
+
+    if (!lookupTable) {
+        // invert the PSToSQL table
+        psToSQLTable = psDBGetPTypeToSQLTable();
+        lookupTable = psHashAlloc(psToSQLTable->nbucket);
+
+        list = psHashKeyList(psToSQLTable);
+        cursor = psListIteratorAlloc(list, 0); 
+
+        while ((key = psListGetNext(cursor))) {
+            value = psHashLookup(psToSQLTable, key);
+            // switch key and value
+            psHashAdd(lookupTable, value, key);
+        }
+
+        // DECIMAL does not exist in the pType to SQL table
+        value = psDBIntToString((psU64)PS_TYPE_PTR);
+        psHashAdd(lookupTable, "DECIMAL", value);
+        psFree(value);
+
+        psFree(cursor);
+        psFree(list);
+        psFree(psToSQLTable);
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBSQLToPTypeTableCleanup(void) 
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetSQLToPTypeTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psHash *psDBGetMySQLToSQLTable(void)
+{
+    static psHash   *lookupTable = NULL;
+
+    if (!lookupTable) {
+        lookupTable = psHashAlloc(20);
+
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_TINY,      "TINYINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_SHORT,     "SMALLINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_LONG,      "INT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_INT24,     "MEDIUMINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_LONGLONG,  "BIGINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DECIMAL,   "DECIMAL");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_FLOAT,     "FLOAT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DOUBLE,    "DOUBLE");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_TIMESTAMP, "TIMESTAMP");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DATE,      "DATE");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_TIME,      "TIME");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DATETIME,  "DATETIME");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_YEAR,      "YEAR");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_STRING,    "CHAR");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_VAR_STRING,"VARCHAR");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_BLOB,      "BLOB");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_SET,       "SET");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_ENUM,      "ENUM");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_NULL,      "NULL-type");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_CHAR,      "TINYINT");
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBMySQLToSQLTableCleanup(void)
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetMySQLToSQLTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psHash *psDBGetPTypeToMySQLTable(void)
+{
+    static psHash   *lookupTable = NULL;
+
+    if (!lookupTable) {
+        lookupTable = psHashAlloc(14);
+
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S8,     psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S16,    psDBMySQLTypeAlloc(MYSQL_TYPE_SHORT,      false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S32,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONG,       false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S64,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONGLONG,   false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U8,     psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U16,    psDBMySQLTypeAlloc(MYSQL_TYPE_SHORT,      true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U32,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONG,       true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U64,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONGLONG,   true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_F32,    psDBMySQLTypeAlloc(MYSQL_TYPE_FLOAT,      false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_F64,    psDBMySQLTypeAlloc(MYSQL_TYPE_DOUBLE,     false));
+        // bogus type
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_C32,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        // bogus type
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_C64,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_BOOL,   psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_PTR,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBPTypeToMySQLTableCleanup(void)
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetPTypeToMySQLTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psPtr psDBMySQLTypeAlloc(enum enum_field_types type, bool isUnsigned)
+{
+    mysqlType       *mType;
+
+    mType = psAlloc(sizeof(mysqlType));
+    mType->type       = type;
+    mType->isUnsigned = isUnsigned;
+
+    return mType;
+}
+
+static void psDBAddToLookupTable(psHash *lookupTable, psU32 type, const char *string)
+{
+    char            *key;
+    char            *value;
+
+    key = psDBIntToString((psU64)type);
+    value = psStringCopy(string);
+
+    psHashAdd(lookupTable, key, value);
+
+    psFree(key);
+    psFree(value);
+}
+
+static void psDBAddVoidToLookupTable(psHash *lookupTable, psU32 type, psPtr value)
+{
+    char            *key;
+
+    key = psDBIntToString((psU64)type);
+
+    psHashAdd(lookupTable, key, value);
+
+    // destructive of value parameter
+    psFree(value);
+    psFree(key);
+}
+
+
+// pType utility functions
+/*****************************************************************************/
+
+#define PS_NAN_ALLOC(dest, type, nan) \
+    dest = psAlloc(sizeof(type)); \
+    *(type *)dest = nan;
+
+static psPtr psDBGetPTypeNaN(psElemType pType)
+{
+    psPtr           myNaN;
+
+    switch (pType) {
+        case PS_TYPE_S8:
+            PS_NAN_ALLOC(myNaN, psS8, PS_MAX_S8);
+            break;
+        case PS_TYPE_S16:
+            PS_NAN_ALLOC(myNaN, psS16, PS_MAX_S16);
+            break;
+        case PS_TYPE_S32:
+            PS_NAN_ALLOC(myNaN, psS32, PS_MAX_S32);
+            break;
+        case PS_TYPE_S64:
+            PS_NAN_ALLOC(myNaN, psS64, PS_MAX_S64);
+            break;
+        case PS_TYPE_U8:
+            PS_NAN_ALLOC(myNaN, psU8, PS_MAX_U8);
+            break;
+        case PS_TYPE_U16:
+            PS_NAN_ALLOC(myNaN, psU16, PS_MAX_U16);
+            break;
+        case PS_TYPE_U32:
+            PS_NAN_ALLOC(myNaN, psU32, PS_MAX_U32);
+            break;
+        case PS_TYPE_U64:
+            PS_NAN_ALLOC(myNaN, psU64, PS_MAX_U64);
+            break;
+        case PS_TYPE_F32:
+            PS_NAN_ALLOC(myNaN, psF32, NAN);
+            break;
+        case PS_TYPE_F64:
+            PS_NAN_ALLOC(myNaN, psF64, NAN);
+            break;
+        case PS_TYPE_C32:
+            // this is a bogus SQL type
+            PS_NAN_ALLOC(myNaN, psC32, NAN);
+            break;
+        case PS_TYPE_C64:
+            // this is a bogus SQL type
+            PS_NAN_ALLOC(myNaN, psC64, NAN);
+            break;
+        case PS_TYPE_BOOL:
+            // what is NaN for a bool?
+            break;
+        case PS_TYPE_PTR:
+            PS_NAN_ALLOC(myNaN, char, '\0');
+            break;
+    }
+
+    return myNaN;
+}
+
+#define PS_IS_NAN(type, data, nan) *(type *)data == nan
+
+static bool psDBIsPTypeNaN(psElemType pType, psPtr data)
+{
+    bool    isNaN;
+
+    switch (pType) {
+        case PS_TYPE_S8:
+            isNaN = PS_IS_NAN(psS8, data, PS_MAX_S8);
+            break;
+        case PS_TYPE_S16:
+            isNaN = PS_IS_NAN(psS16, data, PS_MAX_S16);
+            break;
+        case PS_TYPE_S32:
+            isNaN = PS_IS_NAN(psS32, data, PS_MAX_S32);
+            break;
+        case PS_TYPE_S64:
+            isNaN = PS_IS_NAN(psS64, data, PS_MAX_S64);
+            break;
+        case PS_TYPE_U8:
+            isNaN = PS_IS_NAN(psU8, data, PS_MAX_U8);
+            break;
+        case PS_TYPE_U16:
+            isNaN = PS_IS_NAN(psU16, data, PS_MAX_U16);
+            break;
+        case PS_TYPE_U32:
+            isNaN = PS_IS_NAN(psU32, data, PS_MAX_U32);
+            break;
+        case PS_TYPE_U64:
+            isNaN = PS_IS_NAN(psU64, data, PS_MAX_U64);
+            break;
+        case PS_TYPE_F32:
+            isNaN = PS_IS_NAN(psF32, data, NAN);
+            break;
+        case PS_TYPE_F64:
+            isNaN = PS_IS_NAN(psF64, data, NAN);
+            break;
+        case PS_TYPE_C32:
+            // this is a bogus SQL type
+            isNaN = PS_IS_NAN(psC32, data, NAN);
+            break;
+        case PS_TYPE_C64:
+            // this is a bogus SQL type
+            isNaN = PS_IS_NAN(psC64, data, NAN);
+            break;
+        case PS_TYPE_BOOL:
+            // what is NaN for a bool?
+            break;
+        case PS_TYPE_PTR:
+            isNaN = PS_IS_NAN(char, data, '\0');
+            break;
+    }
+
+    return isNaN;
+}
+
+
+// string utility functions
+/*****************************************************************************/
+
+static char *psDBIntToString(psU64 n)
+{
+    char            *string;
+    size_t          length;
+
+    // length of string + \0
+    // if n is 0, length is 1 char + \0
+    length = n ? (size_t)log10((double)n) + 1
+               : 2;
+    string = psAlloc(length);
+    sprintf(string, "%li", n);
+
+    return string;
+}
+
+static ssize_t psStringAppend(char **dest, const char *format, ...)
+{
+    va_list         args;
+    size_t          length;             // complete string length (sans \0)
+    size_t          oldLength;          // original string length (sans \0)
+    ssize_t         tailLength;         // length of string to append
+
+    if (!*dest) {
+        *dest = psStringCopy("");
+        oldLength = 0;
+    } else {
+        // size of existing string
+        oldLength = strlen(*dest);
+    }
+
+    // find the size of the string to append
+    va_start(args, format);
+    // C99 guarentees vsnprintf() to work as expected with size = 0
+    tailLength = vsnprintf(*dest, 0, format, args);
+    va_end(args);
+
+    // if the new tail is zero length, return the length of the old string.  if
+    // it's a format error, return the error code.
+    if (tailLength < 1) {
+        return tailLength == 0 ? oldLength : tailLength;
+    }
+
+    // new string length (sans \0)
+    length = oldLength + tailLength;
+
+    // realloc string to string + tail + \0
+    *dest = psRealloc(*dest, length + 1);
+
+    // append tail + \0
+    va_start(args, format);
+    vsnprintf(*dest + oldLength, tailLength + 1, format, args);
+    va_end(args);
+
+    return length;
+}
+
+static ssize_t psStringPrepend(char **dest, const char *format, ...)
+{
+    va_list         args;
+    size_t          length;             // complete string length (sans \0)
+    ssize_t         headLength;         // length of string to prepend
+    char            *oldDest;           // copy of original string
+
+    if (!*dest) {
+        // makes the string backup and concatination pointless 
+        *dest = psStringCopy("");
+        length = 0;
+    } else {
+        // size of existing string
+        length = strlen(*dest);
+    }
+
+    // find the size of the string to prepend
+    va_start(args, format);
+    // C99 guarentees vsnprintf() to work as expected with size = 0
+    headLength = vsnprintf(*dest, 0, format, args);
+    va_end(args);
+
+    // if the new head is zero length, return the length of the old string.  if
+    // it's a format error, return the error code.
+    if (headLength < 1) {
+        return headLength == 0 ? length : headLength;
+    }
+
+    // backup original string
+    oldDest = psStringCopy(*dest);
+
+    // new string length (sans \0)
+    length += headLength;
+
+    // realloc string to head + string + \0
+    *dest = psRealloc(*dest, length + 1);
+
+    // copy the new head to the beginning of string
+    va_start(args, format);
+    vsnprintf(*dest, length + 1, format, args);
+    va_end(args);
+
+    // append the original string
+    strncat(*dest, oldDest, length + 1);
+
+    psFree(oldDest);
+
+    return length;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/psdb/psDB.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/psdb/psDB.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/psdb/psDB.h	(revision 22322)
@@ -0,0 +1,261 @@
+/** @file  psDB.h
+ *
+ *  @brief database types and functions
+ *
+ *  This file defines the abstract database type and functions that
+ *  perform basic database operations.
+ *
+ *  @ingroup DataBase
+ *
+ *  @author Joshua Hoblitt
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-16 00:33:32 $
+ *
+ *  Copyright 2005 Joshua Hoblitt, University of Hawaii
+ */
+
+#ifndef PS_DB_H
+#define PS_DB_H 1
+
+#include <mysql/mysql.h>
+#include <pslib.h>
+
+/// @addtogroup DataBase
+/// @{
+
+/** Database handle
+ *
+ *  An opaque object representing a database connection.
+ *
+ */
+typedef struct {
+    MYSQL *mysql;   ///< MySQL database handle
+} psDB;
+
+/** Opens a new database connection
+ *
+ *  @return A new psDB object if the database connection is successful or NULL on
+ *  failure.
+ */
+psDB *psDBInit(
+    const char *host,                   ///< Database server hostname
+    const char *user,                   ///< Database username
+    const char *passwd,                 ///< Database password
+    const char *dbname                  ///< Database namespace
+);
+
+/** Closes a database connection
+ */
+void psDBCleanup(
+    psDB *dbh                           ///< Database handle
+);
+
+/** Creates a new database namespace
+ *
+ * @return true on success
+ */
+bool psDBCreate(
+    psDB *dbh,                          ///< Database handle
+    const char *dbname                  ///< New database namespace
+);
+
+/** Changes the current database namespace
+ *
+ * @return true on success
+ */
+bool psDBChange(
+    psDB *dbh,                          ///< Database handle
+    const char *dbname                  ///< Database namespace
+);
+
+/** Drops a database namespace
+ *
+ * @return true on success
+ */
+bool psDBDrop(
+    psDB *dbh,                          ///< Database handle
+    const char *dbname                  ///< Database namespace
+);
+
+/** Creates a new database table
+ *
+ * This function generates and executes the SQL needed to create a table named
+ * "tableName", with the column names and data types as described in "md".  Each
+ * data item in the psMetadata collection represents a single table field.  The
+ * name of the field is given by the name of the psMetadataItem and the data
+ * type is give by the psMetadataItem.type and psMetadataItem.ptype entries.  A
+ * lookup table should be used to convert from PSLib types into MySQL
+ * compatible SQL data types.  For example, a PS_META_STR would map to an SQL99
+ * varchar.  If the value of type is PS_META_STR then the psMetadataItem.data
+ * element is set to a string with the length for the field written as a text
+ * string.  The value of the psMetadataItem.data element is unused for the
+ * PS_META_PRIMITIVE types.  Other psMetadata types beyond PS_META_STR and
+ * PS_META_PRIMITIVE are not allowed in a table definition.  
+ *
+ * Database indexes can be specified setting the "comment" field to "Primary
+ * Key" or "Key".  Comments are otherwise ignored.
+ *
+ * @return true on success
+ */
+bool psDBCreateTable(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    psMetadata *md                      ///< Column names, types, and indexes
+);
+
+/** Deletes a database table
+ *
+ * @return true on success
+*/
+bool psDBDropTable(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName               ///< Table name
+);
+
+/** Selects a column from a table
+ *
+ * This function generates and executes the SQL needed to select an entire
+ * column from a table or up to "limit" rows from it.  If "limit" is 0, the
+ * entire range is returned.
+ *
+ * @return A psArray of strings or NULL on failure
+ */
+psArray *psDBSelectColumn(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const char *col,                    ///< Column name
+    const psU64 limit                   ///< Maximum number of elements to return
+);
+
+/** Selects a column from a table and casts it to a given type
+ *
+ * This function generates and executes the SQL needed to select an entire
+ * column from a table or up to "limit" rows from it.  If "limit" is 0, the
+ * entire range is returned.  The data in the column is cast to to "pType".
+ *
+ * @return A psVector or NULL on failure
+ */
+psVector *psDBSelectColumnNum(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const char *col,                    ///< Column name
+    psElemType pType,                   ///< Resulting psVector type
+    const psU64 limit                   ///< Maximum number of elements to return
+);
+
+/** Selects a set of rows from a table
+ *
+ * This function returns rows from the specified table which match the
+ * restrictions given by "where".  The restrictions are specified as field /
+ * value pairs.  The psMetadata collection "where" must consist of valid
+ * database fields.  The selected rows are returned as a psArray of psMetadata
+ * values, one per row.
+ *
+ * Currently, the "where" specification only supports the PS_META_STR type.
+ * The string value can be a SQL match pattern, e.g. "%foo%", or an empty
+ * string, e.g. "", to match NULL field values.
+ *
+ * @return A psArray of psMetadata or NULL on failure
+ */
+psArray *psDBSelectRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    psMetadata *where,                  ///< Row match criteria
+    const psU64 limit                   ///< Maximum number of elements to return
+);
+
+/** Insert a single row into a table
+ *
+ * This function inserts the data from "row" into "tableName".
+ *
+ * The "row" specification uses the psMetadataItem name as the column name.
+ * The field values maybe specified in any order.  psMetadata types beyond
+ * PS_META_STR and PS_META_PRIMITIVE are not supported.  If fields are
+ * specified in "row" that do not exist in "tableName", the insert will fail.
+ *
+ * @return true on success
+ */
+bool psDBInsertOneRow(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    psMetadata *row                     ///< Row description
+);
+
+/** Insert a set of rows into a table
+ *
+ * This function inserts the data from "rowSet" into "tableName".
+ *
+ * "rowSet" is a psArray of psMetadata containing row specifications identical to
+ * those used in psDBInsertOneRow().
+ *
+ * @return true on success
+ */
+bool psDBInsertRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    psArray *rowSet                     ///< Set of rows to insert
+);
+
+/** Retrieves all rows from a table
+ *
+ * This function fetches all rows as an psArray of psMetadata.  The rows are in
+ * the same psMetadata format as used in psDBInsertOneRow() & psDBInsertRows().
+ *
+ * @return A psArray of psMetadata or NULL on failure
+ */
+psArray *psDBDumpRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName               ///< Table name
+);
+
+/** Retrieves all columns from a table
+ *
+ * This function fetches all columns, as either a psVector or a psArray
+ * depending on whether or not the column is numeric, and return them in a
+ * psMetadata structure where psMetadataItem.name contains the column's name.
+ *
+ * @return A psMetadata containing either a psArrays or psVector per column
+ */
+psMetadata *psDBDumpCols(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName               ///< Table name
+);
+
+/** Updates the field values, as specified, in a table
+ *
+ * This function updates the fields contained in "values" in the row(s) that
+ * have a field with the value indicated by "where".  Where "where" is in the
+ * same format as used in psDBSelectRows().
+ *
+ * The "values" specification uses the same format as the row specification
+ * used in psDBInsertOneRow(), etc.
+ *
+ * @return The number of rows modified or a negative value on error
+ */
+psS64 psDBUpdateRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    psMetadata *where,                  ///< Row match criteria
+    psMetadata *values                  ///< new field values
+);
+
+/** Deletes rows, as specified, in a table
+ *
+ * Delete the rows that are matched by "where" using the same semantics for
+ * "where" as in psDBUpdateRow().
+ *
+ * If "where" is NULL, all rows in the table will be removed and regardless of
+ * the number of rows that were dropped, only 1 will be returned on success.
+ *
+ * @return The number of rows removed or a negative value on error
+ */
+psS64 psDBDeleteRows(
+    psDB *dbh,                          ///< Database handle
+     const char *tableName,             ///< Table name
+     psMetadata *where                  ///< Row match criteria
+);
+
+/// @}
+
+#endif // PS_DB_H
Index: /tags/sj_tags/sj_root_20080929/archive/psdb/psleak.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/psdb/psleak.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/psdb/psleak.c	(revision 22322)
@@ -0,0 +1,61 @@
+#include <pslib.h>
+#include "psleak.h"
+
+#define LEAKS "leaks.dat"       // File to which to write leaks data
+
+
+psMemoryId stacMemPrint(const psMemBlock *ptr)
+{
+    psLogMsg("stac.memoryPrint", PS_LOG_INFO,
+         "Memory block %d:\n"
+         "\tFile %s, line %d, size %d\n"
+         "\tPosts: %x %x %x\n",
+         ptr->id, ptr->file, ptr->lineno, ptr->userMemorySize, ptr->startblock, ptr->endblock,
+         *(void**)((int8_t *)(ptr + 1) + ptr->userMemorySize));
+    return 0;
+}
+
+
+void stacMemoryProblem(const psMemBlock* ptr, ///< the pointer to the problematic memory block.
+               const char *file, ///< the file in which the problem originated
+               psS32 lineno ///< the line number in which the problem originated
+    )
+{
+    psLogMsg("stac.checkMemory.corruption", PS_LOG_WARN,
+         "Memory corruption detected in memBlock %d\n"
+         "\tFile %s, line %d, size %d\n"
+         "\tPosts: %x %x %x\n",
+         ptr->id, file, lineno, ptr->userMemorySize, ptr->startblock, ptr->endblock,
+         (ptr + 1 + ptr->userMemorySize));
+}
+
+
+
+void stacCheckMemory(void)
+{
+    psMemBlock **leaks = NULL;      // List of leaks
+    FILE *leakFile;         // File to write leaks to
+
+    psTrace("stac.checkMemory", 1, "Checking for memory problems....\n");
+
+    (void)psMemProblemCallbackSet(stacMemoryProblem); // Set callback for corruption
+
+    if ((leakFile = fopen(LEAKS, "w")) == NULL) {
+    //psError("stac.checkMemory", "Unable to open leaks file, %s\n",LEAKS);
+    return;
+    }
+
+    int nLeaks = psMemCheckLeaks(0, &leaks, leakFile, false); // Number of leaks
+    psTrace("stac.checkMemory", 1, "%d leaks found.\n", nLeaks);
+    for (int i = 0; i < nLeaks; i++) {
+    psLogMsg("stac.checkMemory.leaks", PS_LOG_WARN,
+         "Memory leak detection: memBlock %d\n"
+         "\tFile %s, line %d, size %d\n",
+         leaks[i]->id, leaks[i]->file, leaks[i]->lineno, leaks[i]->userMemorySize);
+    }
+
+    int nCorrupted = psMemCheckCorruption(false); // Number of corrupted
+    psTrace("stac.checkMemory", 1, "%d memory blocks corrupted.\n", nCorrupted);
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/archive/psdb/psleak.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/psdb/psleak.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/psdb/psleak.h	(revision 22322)
@@ -0,0 +1,15 @@
+#include <pslib.h>
+
+// Check memory
+void stacCheckMemory(void);
+
+// Print out the problem
+void stacMemoryProblem(const psMemBlock* ptr, ///< the pointer to the problematic memory block.
+               const char *file, ///< the file in which the problem originated
+               psS32 lineno ///< the line number in which the problem originated
+    );
+
+// Print out a memblock when it's allocated --- this function used as a callback
+psMemoryId stacMemPrint(const psMemBlock *ptr);
+
+
Index: /tags/sj_tags/sj_root_20080929/archive/psdb/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/psdb/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/psdb/test.c	(revision 22322)
@@ -0,0 +1,322 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "psDB.h"
+#include "psleak.h"
+
+#define TEST_SETUP\
+    int testsRun    = 0;\
+    int testsPassed = 0;
+
+#define TEST_SUMMARY\
+    printf("\n\n### TEST SUMMARY ###\n\n    %d of %d tests succeeded\n\n", \
+        testsPassed, testsRun);
+
+#define OK(test, name) \
+    testsRun++; \
+    printf("Test #%d: %s... ", testsRun, name); \
+    if (test) { \
+        testsPassed++; \
+        printf("OK\n"); \
+    } else { \
+        printf("FAILED\n"); \
+    }
+
+int main (int argv, char **argc)
+{ 
+    TEST_SETUP;
+
+    psMemAllocateCallbackSet(stacMemPrint);
+    //psMemAllocateCallbackSetID(224);
+    psMemFreeCallbackSet(stacMemPrint);
+    psMemProblemCallbackSet(stacMemoryProblem);
+    atexit(stacCheckMemory);
+
+    psDB            *dbh;
+
+    // open db
+    dbh = psDBInit("localhost", "test", NULL, "test");
+    OK(dbh, "psDBInit() - connect");
+
+    {
+        psDB        *foo;
+
+        foo = psDBInit("tsohlacol", "tset", NULL, "tset");
+        OK(!foo, "psDBInit(), bad DSN");
+    }
+
+    // the next 3 tests will fail as the MySQL test account has insufficent
+    // permissions
+    OK(psDBCreate(dbh, "foobar"), "psDBCreate()");
+    OK(psDBChange(dbh, "foobar"), "psDBChange()");
+    OK(psDBDrop(dbh, "foobar"), "psDBDrop()");
+
+    // psDBCreateTable() & psDBDropTable()
+    {
+        psMetadata      *md;
+
+        md = psMetadataAlloc();
+        psMetadataAdd(md, 0, "foo", PS_TYPE_S32, PS_META_PRIMITIVE, "Primary Key"); 
+        psMetadataAdd(md, 1, "bar", PS_TYPE_BOOL,PS_META_PRIMITIVE, "Key"); 
+        psMetadataAdd(md, 2, "baz", PS_TYPE_F64, PS_META_PRIMITIVE, NULL);
+        psMetadataAdd(md, 3, "boing", PS_TYPE_PTR, PS_META_STR, NULL, "60");
+
+        OK(psDBCreateTable(dbh, "bar", md), "psDBCreateTable() - create table");
+
+        psFree(md);
+    }
+
+    OK(psDBDropTable(dbh, "bar"), "psDBDropTable() - drop table");
+
+    {
+        psMetadata      *md;
+
+        md = psMetadataAlloc();
+        psMetadataAdd(md, 0, "foo", PS_TYPE_S32, PS_META_PRIMITIVE, "Primary Key"); 
+
+        psDBCreateTable(dbh, "bar", md);
+
+        OK(!psDBCreateTable(dbh, "bar", md), "psDBCreateTable() - table already exists");
+
+        psFree(md);
+
+        psDBDropTable(dbh, "bar");
+    }
+
+    OK(!psDBDropTable(dbh, "fubar"), "psDBDropTable() - non-existant table");
+
+    // setup yak table for later tests
+    {
+        psMetadata      *md;
+
+        md = psMetadataAlloc();
+        psMetadataAdd(md, 0, "hair", PS_TYPE_F32, PS_META_PRIMITIVE, NULL);
+
+        psDBCreateTable(dbh, "yak", md);
+
+        psFree(md);
+    }
+
+    // setup horse table for later tests
+    {
+        psMetadata      *md;
+
+        md = psMetadataAlloc();
+        psMetadataAdd(md, 0, "color", PS_TYPE_PTR, PS_META_STR, NULL, "100");
+
+        psDBCreateTable(dbh, "horse", md);
+
+        psFree(md);
+    }
+
+    // psDBInsertOneRow()
+    {
+        psMetadata      *md;
+
+        md = psMetadataAlloc();
+        psMetadataAdd(md, 0, "hair", PS_TYPE_F32, PS_META_PRIMITIVE, NULL, 10e3);
+
+        OK(psDBInsertOneRow(dbh, "yak", md),"psDBInsertOneRow() - number");
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+
+        md = psMetadataAlloc();
+        psMetadataAdd(md, 0, "hair", PS_TYPE_F32, PS_META_PRIMITIVE, NULL, NAN);
+
+        OK(psDBInsertOneRow(dbh, "yak", md),"psDBInsertOneRow() - nan");
+
+        psFree(md);
+    }
+
+    // psDBInsertRows()
+    {
+        psArray         *rowSet;
+        psMetadata      *row;
+
+        rowSet = psArrayAlloc(3);
+        rowSet->n = 0;
+
+        row = psMetadataAlloc();
+        psMetadataAdd(row, 0, "color", PS_TYPE_PTR, PS_META_STR, NULL, "brown"); 
+        psArrayAdd(rowSet, 0, row);
+        // FIXME
+        // psFree(row);
+
+        row = psMetadataAlloc();
+        psMetadataAdd(row, 0, "color", PS_TYPE_PTR, PS_META_STR, NULL, ""); 
+        psArrayAdd(rowSet, 0, row);
+        // FIXME
+        // psFree(row);
+
+        row = psMetadataAlloc();
+        psMetadataAdd(row, 0, "color", PS_TYPE_PTR, PS_META_STR, NULL, "pink"); 
+        psArrayAdd(rowSet, 0, row);
+        // FIXME
+        // psFree(row);
+
+        OK(psDBInsertRows(dbh, "horse", rowSet), "psDBInsertRows() - multi-row insert");
+
+        psFree(rowSet);
+    }
+
+    // psDBSelectColumn()
+    {
+        psArray         *column;
+        int             i;
+
+        column = psDBSelectColumn(dbh, "horse", "color", 42);
+        OK(column, "psDBSelectColumn() - select");
+        OK(column->n == 3, "psDBSelectColumn() - number of elements");
+
+        for (i = 0; i < column->n; i++) {
+            printf("\thorse color is: %s\n", (char *)column->data[i]);
+        }
+
+        psFree(column);
+    }
+
+    // psDBSelectColumnNum()
+    {
+        psVector        *column;
+        int             i;
+
+        column = psDBSelectColumnNum(dbh, "yak", "hair", PS_TYPE_F32, 42);
+        OK(column, "psDBSelectColumnNum() - select");
+        OK(column->n == 2, "psDBSelectColumnNum() - number of elements");
+
+        for (i = 0; i < column->n; i++) {
+            printf("\tyak hair count is: %f\n", column->data.F32[i]);
+            if (isnan(column->data.F32[i])){
+                printf("\t\tNO yak hair!!! (it's a nan)\n");
+            }
+        }
+
+        psFree(column);
+    }
+
+    // psDBSelectRows()
+    {
+        psMetadata      *md;
+        psMetadataItem  *item;
+        psArray         *resultSet;
+        psListIterator  *cursor;
+        int             i;
+
+        md = psMetadataAlloc();
+        psMetadataAdd(md, 0, "color", PS_TYPE_PTR, PS_META_STR, NULL, "brown"); 
+
+        resultSet = psDBSelectRows(dbh, "horse", md, 0);
+        OK(resultSet, "psDBSelectRows() - select");
+        OK(resultSet->n == 1, "psDBSelectRows() - number of rows");
+
+        psFree(md);
+        for (i = 0; i < resultSet->n; i++) {
+            md = (psMetadata *)resultSet->data[0];
+
+            cursor = psListIteratorAlloc(md->list, 0);
+
+            while ((item = psListGetNext(cursor))) {
+                printf("\tcolum name is: %s, value is: %s\n", item->name, (char *)item->data.V);
+            }
+
+            psFree(cursor);
+        }
+        psFree(resultSet);
+
+    }
+
+
+    // psDBDumpRows()
+    {
+        psArray         *resultSet;
+
+        resultSet = psDBDumpRows(dbh, "horse");
+        OK(resultSet, "psDBDumpRows() - dump");
+        OK(resultSet->n == 3, "psDBDumpRows() - number of rows");
+        psFree(resultSet);
+    }
+
+    // psDBDumpCols()
+    {
+        psMetadata      *columns;
+        psMetadataItem  *item;
+        psListIterator  *cursor;
+
+        columns = psDBDumpCols(dbh, "horse");
+        OK(columns, "psDBDumpCols() - dump");
+        OK(columns->list->size == 1, "psDBDumpCols() - number of columns");
+
+        cursor = psListIteratorAlloc(columns->list, 0);
+
+        while ((item = psListGetNext(cursor))) {
+            printf("\tcolumn name is: %s\n", item->name);
+        }
+
+        psFree(cursor);
+        psFree(columns);
+    }
+
+    // psDBUpdateRows()
+    {
+        psMetadata      *where;
+        psMetadata      *value;
+        psU64           rowsAffected;
+        psArray         *resultSet;
+        int             i;
+        psListIterator  *cursor;
+        psMetadata      *md;
+        psMetadataItem  *item;
+
+        where = psMetadataAlloc();
+        psMetadataAdd(where, 0, "color", PS_TYPE_PTR, PS_META_STR, NULL, "pink"); 
+
+        value = psMetadataAlloc();
+        psMetadataAdd(value, 0, "color", PS_TYPE_PTR, PS_META_STR, NULL, "HOT pink"); 
+
+        rowsAffected = psDBUpdateRows(dbh, "horse", where, value);
+        psFree(where);
+        psFree(value);
+
+        OK(rowsAffected, "psDBUpdateRows() - pink -> HOT pink");
+
+        resultSet = psDBSelectRows(dbh, "horse", value, 0);
+
+        for (i = 0; i < resultSet->n; i++) {
+            md = (psMetadata *)resultSet->data[0];
+
+            cursor = psListIteratorAlloc(md->list, 0);
+
+            while ((item = psListGetNext(cursor))) {
+                if (strcmp((char *)item->data.V, "HOT pink") == 0) {
+                    OK(1, "psDBUpdateRows() - psDBSelectRows() found HOT pink");
+                    printf("\thorse color is: %s\n", (char *)item->data.V);
+                }
+            }
+
+            psFree(cursor);
+        }
+
+        psFree(resultSet);
+    }
+
+    // psDBDeleteRows()
+    OK(psDBDeleteRows(dbh, "yak", NULL), "psDBDeleteRows()");
+    
+    // cleanup other tests
+    psDBDropTable(dbh, "horse");
+    psDBDropTable(dbh, "yak");
+
+    // close db
+    psDBCleanup(dbh);
+
+    TEST_SUMMARY;
+
+    //psErrorStackPrint(stderr, "### Error stack dump ###\n");
+
+    exit(0);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.log *.dvi *.aux *.toc *.log *.out
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/Makefile	(revision 22322)
@@ -0,0 +1,33 @@
+# $Id: Makefile,v 1.8 2004-04-21 14:22:03 rhl Exp $
+SHELL = /bin/sh
+DIRS = bin include lib src ups
+
+.PHONY : all clean
+all clean :
+	@for f in $(DIRS); do \
+		if [ ! -d $$f ]; then \
+			echo No such directory: $$f >&2; \
+		else \
+			(cd $$f; $(MAKE) $(MFLAGS) $@); \
+		fi \
+	done
+
+clean : topClean
+.PHONY : topClean
+topClean:
+	$(RM) *~ core* TAGS
+
+.PHONY : tags
+tags:
+	etags `find . ! -name \*#\* \( -name \*.[ch] -o -name \*.cpp \) -print`
+#
+# Assumes that Utilities is checked out one level up
+#
+check :
+	../Utilities/bin/check-namespace -d `find . ! -name \*#\* -name \*.[ha] -print`
+
+doxygen:
+	(cd include; doxygen)
+
+publish:
+	rsync -e ssh -auv ./ poiserver0:PSLib/
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/bin/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/bin/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/bin/Makefile	(revision 22322)
@@ -0,0 +1,11 @@
+all :
+	@: no default actions
+
+install :
+	cp Makefile makeErrorCodes $(PSLIB_DIR)/bin
+
+clean:
+	$(RM) *~ core* *.a
+
+distclean: clean
+	@: nothing extra to do
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/bin/makeErrorCodes
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/bin/makeErrorCodes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/bin/makeErrorCodes	(revision 22322)
@@ -0,0 +1,178 @@
+#!/usr/bin/perl -w
+#
+# Read a file and generate the proper C to define a set of enums and their descriptions
+#
+$cfile = $hfile = undef;	# Output filenames (.{c,h})
+$prefix = "ps";			# Prefix for enums
+$verbose = 0;			# be chatty?
+#
+while ($ARGV[0] =~ /^-/) {
+   if ($ARGV[0] eq "-?") {
+      &usage();
+      exit(0);
+   } elsif ($ARGV[0] =~ /^-[chp]$/) {
+      $arg = $ARGV[1];
+      if (!$arg) {
+	 warn "$ARGV[0] expects an argument\n";
+      } else {
+	 if ($ARGV[0] eq "-c") {
+	    $cfile = "$arg";
+	 } elsif ($ARGV[0] eq "-h") {
+	    $hfile = "$arg";
+	 } elsif ($ARGV[0] eq "-p") {
+	    $prefix = "$arg";
+	 } else {
+	    die "Impossible condition: $ARGV[0]\n";
+	 }
+      }
+      shift(@ARGV);
+   } elsif($ARGV[0] eq "-v") {
+      $verbose++;
+   } else {
+      warn "Unknown argument $ARGV[0]\n";
+   }
+
+   shift(@ARGV);
+}
+
+$prefix = lc("$prefix");	# e.g. ps
+$PREFIX = uc("$prefix");	#      PS
+
+
+if (!defined($cfile) && !defined($hfile)) {
+   warn "Surely you want some output? Please specify -c and/or -h\n";
+   &usage();
+   exit 1;
+}
+
+if (!@ARGV) {
+   warn "Please specify some input files!\n";
+   &usage();
+   exit 1;
+}
+#
+# OK, read files
+#
+$input_files = join(" ", @ARGV);
+foreach $file (@ARGV) {
+   if ($verbose > 1) {
+      warn "   $file\n";
+   }
+
+   open(FD, $file) or die "Failed to open $file:$! \n";
+   while (<FD>) {
+      chomp;
+      
+      s/(^|[^\\])\#.*/$1/;	# strip comments
+      s/\\\#/\#/g;		# unprotect #
+
+      if (!$_) {
+	 next;
+      }
+      
+      #print "$_\n";
+
+      ($name, undef, $val, $descrip) = /^\s*([^ \t]+)\s*(=\s*(\d+))?\s*,?\s*(.*)/;
+
+      push(@names,$name);
+      $values{$name} = defined($val) ? $val : undef;
+      $descrips{$name} = $descrip;
+   }
+}
+
+close(FD);
+
+#
+# Generate .c file
+#
+if (defined($cfile)) {
+   open(FD, ">$cfile") or die("Cannot open $cfile for write\n");
+   
+   print FD<<"EOT";
+/*
+ * This file was machine generated from $input_files;
+ * please do not modify it
+ */
+#include "psError.h"
+#include "${prefix}ErrorCodes.h"
+
+void ${prefix}ErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+EOT
+
+    foreach $name (@names) {
+       printf FD "        { ${PREFIX}_ERR_%s, ", $name;
+       if (defined($descrips{$name})) {
+	  printf FD "\"%s\"", $descrips{$name};
+       } else {
+	  printf FD "NULL";
+       }
+       printf FD " },\n";
+   }
+
+
+$names = @names;
+print FD <<"EOT";
+    };
+    static int nerror = $names;		// number of values in enum
+
+    p_psErrorRegister(errors, nerror);
+    nerror = 0;			                // don't register more than once
+}
+EOT
+}
+#
+# Generate .h file
+#
+if (defined($hfile)) {
+   open(FD, ">$hfile") or die("Cannot open $hfile for write\n");
+   
+   print FD <<"EOT";
+#if !defined(${PREFIX}_ERROR_CODES_H)
+#define ${PREFIX}_ERROR_CODES_H
+/*
+ * This file was machine generated from $input_files;
+ * please do not modify it
+ */
+typedef enum {
+EOT
+
+   foreach $name (@names) {
+      printf FD "    ${PREFIX}_ERR_%s", $name;
+      if (defined($values{$name})) {
+	 printf FD " = %d", $values{$name};
+      }
+      printf FD ",\n";
+   }
+	 
+   print FD <<"EOT";
+} ${prefix}ErrorCode;
+
+void ${prefix}ErrorRegister(void);
+
+#endif
+EOT
+   close(FD);
+}
+    
+###############################################################################
+
+sub usage
+{
+      warn "\
+Usage: makeErrorClasses [options] file [file ...]
+
+Read a file containing lines of the format
+   NAME [ = val], description
+and generate the proper C to define a set of enums and their descriptions.
+Comments start with a # and extend to the end of the line
+
+Options:
+        -?		Print this message
+        -c file.c       Generate file.c
+        -h file.h       Generate file.h
+	-p prefix	Change enum prefix from ps/PS to \"prefix/PREFIX\"
+	-v		Be chatty (repeat for even more output)
+";
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+psErrorCodes.h
+dox
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/Doxyfile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/Doxyfile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/Doxyfile	(revision 22322)
@@ -0,0 +1,993 @@
+# Doxyfile 1.2.18
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = IPP
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = PSLib
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = dox
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, 
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en 
+# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, 
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these class will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH        = 
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower case letters. If set to YES upper case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments  will behave just like the Qt-style comments (thus requiring an 
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# reimplements.
+
+INHERIT_DOCS           = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consist of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = .
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS          = 
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.
+
+INPUT_FILTER           = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output dir.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non empty doxygen will try to run 
+# the html help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, 
+# or Internet explorer 4.0+). Note that for large projects the tree generation 
+# can take a very long time. In such cases it is better to disable this feature. 
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = YES
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimised for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assigments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_XML           = NO
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermedate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
+
+# The CGI_NAME tag should be the name of the CGI script that 
+# starts the search engine (doxysearch) with the correct parameters. 
+# A script with this name will be generated by doxygen.
+
+CGI_NAME               = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the 
+# cgi binaries are located. See the documentation of your http daemon for 
+# details.
+
+CGI_URL                = 
+
+# The DOC_URL tag should be the absolute URL to the directory where the 
+# documentation is located. If left blank the absolute path to the 
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL                = 
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the 
+# documentation is located. If left blank the directory on the local machine 
+# will be used.
+
+DOC_ABSPATH            = 
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary 
+# is installed.
+
+BIN_ABSPATH            = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to 
+# documentation generated for other projects. This allows doxysearch to search 
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS          = 
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/Makefile	(revision 22322)
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.5 2004-05-04 02:29:18 rhl Exp $
+
+PSLIB_DIR = ..
+
+all :
+	@: no default actions
+
+psErrorCodes.h : $(PSLIB_DIR)/src/Utils/psErrorCodes.dat
+	$(PSLIB_DIR)/bin/makeErrorCodes -h psErrorCodes.h $(PSLIB_DIR)/src/Utils/psErrorCodes.dat
+
+clean:
+	$(RM) *~ core* TAGS psErrorCodes.h .#*
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psArray.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psArray.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psArray.h	(revision 22322)
@@ -0,0 +1,35 @@
+#if !defined(PS_ARRAY_H)
+#define PS_ARRAY_H
+
+/** \file psArray.h
+ *  \brief Support for void* arrays
+ *  \ingroup DataGroup
+ */
+
+/** An array of pointers to void */
+typedef struct {
+    const int n;                        ///< size of array 
+    const int nalloc;                   ///< allocated data block
+    void **data;                        ///< pointer to data block
+} psArray;
+
+/** Functions **************************************************************/
+/** \addtogroup DataGroup Data Utilities
+ *  \{
+ */
+
+/** Constructor */
+psArray *psArrayAlloc(int nalloc	///< Number to allocate
+    );
+/** Reallocator */
+psArray *psArrayRealloc(const psArray *array, ///< Array to stretch
+			int nalloc	///< New number to be allocated
+    );
+
+/** Sort an array */
+psArray *psArraySort(psArray *array,	///< Array to sort
+		     int (*compare)(const void **a, const void **b) ///< Comparison function
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psAstrom.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psAstrom.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psAstrom.h	(revision 22322)
@@ -0,0 +1,372 @@
+# if !defined(PS_ASTROM_H)
+# define PS_ASTROM_H
+
+/** \file psAstrom.h
+ *  \brief Astrometric transformations
+ *  \ingroup AstroGroup
+ */
+
+/* Astrometric transformations for a focal plane array.
+ * There are five coordinate frames that we need to worry about:
+ * 1. Cell:          x,y in pixels (raw coordinates; lower-left pixel center is 0.5,0.5 TBR)
+ * 2. Chip:          X,Y in pixels
+ * 3. FPA:           P,Q in microns 
+ * 4. Tangent plane: L,M in arcsec (TBR - angles or linear?) from the telescope boresight.
+ * 5. Sky frame:     R,D in degrees (TBR - radians?)
+ *
+ * 1 <--> 2: 2 2-D polynomials: (X,Y) = f(x,y)
+ * 2 <--> 3: 2 2-D polynomials: (P.Q) = f(X,Y)
+ * 3 <--> 4: 2 4-D polynomials: (L,M) = f(P,Q,m,c) (m & c are magnitude and color of object)
+ * 4 <--> 5: SLALib transformation using exposure parameters (psExposure or psGrommit)
+ */
+
+/** Observatory data */
+typedef struct {
+    const char *name;			///< Name of observatory
+    const double latitude;		///< Latitude of observatory, east positive
+    const double longitude;		///< Longitude of observatory
+    const double height;		///< Height of observatory
+    const double tlr;			///< Tropospheric Lapse Rate
+} psObservatory;
+
+psObservatory *
+psObservatoryAlloc(const char *name,	///< Name of observatory
+		   double latitude,	///< Latitude of observatory, east positive
+		   double longitude,	///< Longitude of observatory
+		   double height,	///< Height of observatory
+		   double tlr		///< Tropospheric Lapse Rate
+		   );
+
+/** Information needed (by SLALIB) to convert Apparent to Observed Position */
+typedef struct {
+    const double latitude;		///< geodetic latitude (radians)
+    const double sinLat, cosLat;	///< sine and cosine of geodetic latitude
+    const double abberationMag;		///< magnitude of diurnal aberration vector
+    const double height;		///< height (HM)
+    const double temperature;		///< ambient temperature (TDK)
+    const double pressure;		///< pressure (PMB)
+    const double humidity;		///< relative humidity (RH)
+    const double wavelength;		///< wavelength (WL)
+    const double lapseRate;		///< lapse rate (TLR)
+    const double refractA, refractB;	///< refraction constants A and B (radians)
+    const double longitudeOffset;	///< longitude + eqn of equinoxes + ``sidereal UT'' (radians)
+    const double siderealTime;		///< local apparent sidereal time (radians)
+} psGrommit;
+
+/** Constructor */
+psGrommit *
+psGrommitAlloc(const psExposure *exp	///< Exposure details from which to derive grommit
+    );
+
+/** Exposure information from the telescope */
+typedef struct {
+    const double ra, dec;		///< Telescope boresight
+    const double ha;			///< Hour angle
+    const double zd;			///< Zenith distance
+    const double az;			///< Azimuth
+    const psTime *time;			///< Time of observation
+    const float rotAngle;		///< Rotator position angle
+    const float temp;			///< Air temperature, for estimating refraction
+    const float pressure;		///< Air pressure, for calculating refraction
+    const float humidity;		///< Relative humidity, for calculating refraction
+    const float exptime;		///< Exposure time
+    const float wavelength;		///< Wavelength of observation
+    const psObservatory *observatory;	///< Observatory data: longitude, latitude, height etc.
+    /* Derived quantities */
+    const psTime *lst;			///< Local Sidereal Time
+    const float posAngle;		///< Position angle
+    const float parallactic;		///< Parallactic angle
+    const float airmass;		///< Airmass, calculated from zenith distance
+    const float pf;			///< Parallactic factor
+    const char *cameraName;		///< name of camera which provided exposure
+    const char *telescopeName;		///< name of telescope which provided exposure
+} psExposure;
+
+/** Constructor */
+psExposure *
+psExposureAlloc(double ra, double dec,  ///< Telescope boresight
+                double ha,              ///< Hour angle
+                double zd,              ///< Zenith distance
+                double az,              ///< Azimuth
+		const psTime *time,	///< Time of observation
+                float rotAngle,         ///< Rotator position angle
+                float temp,             ///< Temperature
+                float pressure,         ///< Pressure
+                float humidity,         ///< Relative humidity
+                float exptime,		///< Exposure time
+		float wavelength,	///< Wavelength of observation
+		const psObservatory *observatory ///< Observatory data
+		);
+
+/** The fixed pattern residual offsets.  These are specified via a coarse grid of x and y offsets. */
+typedef struct {
+    int nX, nY;				///< Number of elements in x and y
+    double x0, y0;			///< Position of the lower-left corner of the grid on the focal plane
+    double xScale, yScale;		///< Scale of the grid
+    double **x, **y;			///< The grid of offsets in x and y
+} psFixedPattern;
+
+/** Constructor */
+psFixedPattern *
+psFixedPatternAlloc(double x0, double y0, ///< Position of the lower-left corner of the grid on the focal plane
+		    double xScale, double yScale, ///< Scale of the grid
+		    const psImage *x,	///< Grid of offsets in x
+		    const psImage *y	///< Grid of offsets in y
+		    );
+
+
+/** a Focal plane array: a collection of chips.  Not all chips in a camera need to be listed in an instance of
+ *  psFPA.
+ */
+typedef struct {
+    int nChips;                         ///< Number of Cells assigned
+    int nAlloc;                         ///< Number of Cells available
+    struct psChip *chips;		///< Chips in the Focal Plane Array
+
+    psMetadata *md;			///< FPA-level metadata 
+    psPlaneDistort *TPtoFP;		///< Transformation term from 
+    psPlaneDistort *FPtoTP;		///< Transformation term from 
+    psFixedPattern *pattern;		///< Fixed pattern residual offsets
+    const psExposure *exp;		///< information about this exposure
+    const psGrommit *grommit;		///< Information for conversion from TP to Sky.
+    psPhotSystem colorPlus, colorMinus; ///< Colour reference
+    float rmsX, rmsY;                   ///< Dispersion in astrometric solution
+    float chi2;                         ///< chi^2 of astrometric solution
+} psFPA;
+
+/** Constructor: makes an empty FPA */
+psFPA *
+psFPAAlloc(int nAlloc,			///< Number of Cells to allocate
+	   const psExposure *exp	///< information about this exposure
+	   );
+
+
+/** a Chip: a collection of cells.  Not all valid cells in a chip need to be listed in an
+ *  instance of psChip.
+ */
+typedef struct {
+    int nCells;                         ///< Number of Cells assigned
+    struct psCell *cells;		///< Cells in the Chip
+
+    psMetadata *md;			///< Chip-level metadata
+    psPlaneTransform *chipToFPA;	///< Transformations from chip coordinates to FPA coordinates
+    psPlaneTransform *FPAtoChip;	///< Transformations from FPA coordinates to chip
+
+    struct psFPA *parentFPA;		///< FPA which contains this chip
+} psChip;
+
+/** Constructor */
+psChip *
+psChipAlloc(int nCells,			///< Number of Cells assigned
+	    psFPA *parentFPA		///< FPA which contains this chip
+	    );
+
+
+/** a Cell: a collection of readouts.
+ */
+typedef struct {
+    int nReadouts;			///< number of readouts in this cell realization; each may have its
+					///< own image, objects and overscan.
+    struct psReadout *readouts;		///< Readouts from the cell
+    psMetadata *md;			///< Cell-level metadata
+    psPlaneTransform *cellToChip;	///< Transformations from cell coordinates to chip coordinates
+    psPlaneTransform *cellToFPA;	///< Transformations from cell coordinates to FPA coordinates
+    psPlaneTransform *toTP;		///< Quick and Dirty transformations from cell coordinates to sky
+    struct psChip *parentChip;		///< chip which contains this cell
+} psCell;
+
+/** Constructor */
+psCell *
+psCellAlloc(int nReadouts,		///< number of readouts in this cell
+	    struct psChip *parentChip	///< chip which contains this cell
+	    );
+
+
+/** a Readout: a collection of pixels */
+typedef struct {
+    const int x0, y0;			///< Offset from the lower-left corner of the physical device
+    const int xBin, yBin;		///< Image binning
+    psImage *image;                     ///< imaging area of cell 
+    psDlist *objects;			///< objects derived from cell
+    psMetadata *md;			///< Readout-level metadata
+} psReadout;
+    
+
+/** Constructor */
+psReadout *
+psReadoutAlloc(int x0, int y0,		///< Offset from the lower-left corner of the physical device
+	       const psImage *image	///< imaging area of cell
+	       );
+
+
+/** Functions **************************************************************/
+/** \addtogroup AstroGroup Astronomy-Specific Utilities
+ *  \{
+ */
+
+/*** Calculating and applying astrometric solutions ***/
+
+/** Return the cell in FPA which contains the given FPA coordinates */
+psCell *
+psCellInFPA(const psPlane *coord,	///< Coordinate in FPA
+	    const psFPA *fpa)		///< FPA description
+;
+
+
+/** returns Chip in FPA which contains the given FPA coordinate */
+psChip *
+psChipInFPA(const psPlane *coord,	///< coordinate in FPA
+	    const psFPA *fpa) 		///< FPA description
+;
+
+/** returns Cell in Chip which contains the given chip coordinate */
+psCell *
+psCellInChip(const psPlane *coord,	///< coordinate in chip
+	     const psChip *chip)	///< chip description
+;
+
+/************ conversions low -> high ****************/
+/** converts the specified Cell coord to the coord on the parent Chip */
+psPlane *
+psCoordCelltoChip (psPlane *out,	///< Coordinates to return, or NULL
+		   const psPlane *in,	///< input Cell coordinate
+		   const psCell *cell) 	///< Cell description
+;
+
+/** converts the specified Chip coord to the coord on the parent FPA */
+psPlane *
+psCoordChiptoFPA (psPlane *out,		///< Coordinates to return, or NULL
+		  const psPlane *in,	///< input Chip coordinate
+		  const psChip *chip) 	///< Chip description
+;
+
+/** Convert focal plane coords to tangent plane coordinates */
+psPlane *
+psCoordFPAToTP(psPlane *out,		///< Coordinates to return, or NULL
+	       const psPlane *in,	///< input FPA coordinate
+	       float color,		///< Color of source, using reference defined in the fpa
+	       float mag,		///< Magnitude of source
+	       const psFPA *fpa)	///< FPA description
+;
+
+/** Convert tangent plane coords to (RA,Dec) */
+psSphere *
+psCoordTPtoSky(psSphere *out,		///< Coordinates to return, or NULL
+	       const psPlane *in,	///< input TP coordinate
+	       const psGrommit *grommit) ///< Grommit for fast conversion
+;
+
+/** Convert Cell coords to FPA coordinates */
+psPlane *
+psCoordCellToFPA(psPlane *out,		///< Coordinates to return, or NULL
+		 const psPlane *in,	///< Input cell coordinates
+		 const psCell *cell)	///< Cell description
+;
+
+/** Convert cell and cell coordinate to (RA,Dec) */
+psSphere *
+psCoordCellToSky(psSphere *out,		///< Coordinates to return, or NULL
+		 const psPlane *in,	///< cell coordinates to transform
+		 float color,		///< Color of source, using reference defined in the fpa
+		 float mag,		///< Magnitude of source
+		 const psCell *cell)	///< Cell to get coordinates for
+;
+
+/** Convert cell and cell coordinate to (RA,Dec).  Don't need color,mag here because it's quick and dirty. */
+psSphere *
+psCoordCellToSkyQuick(psSphere *out,	///< Coordinates to return, or NULL
+		      const psPlane *in, ///< cell coordinates to transform
+		      const psCell *cell) ///< Cell to get coordinates for
+;
+
+
+/************ conversions low -> high ****************/
+/** Convert (RA,Dec) to tangent plane coords */
+psPlane *
+psCoordSkyToTP(psPlane *out,		///< Coordinates to return, or NULL
+	       const psSphere *in,	///< input Sky coordinate
+	       const psGrommit *grommit) ///< Grommit for fast conversion
+;
+
+/** Convert tangent plane coords to focal plane coordinates */
+psPlane *
+psCoordTPtoFPA(psPlane *out,		///< Coordinates to return, or NULL
+	       const psPlane *in,	///< input TP coordinate
+	       float color,		///< Color of source, using reference defined in the fpa
+	       float mag,		///< Magnitude of source
+	       const psFPA *fpa)	///< FPA description
+;
+
+/** converts the specified FPA coord to the coord on the given Chip */
+psPlane *
+psCoordFPAtoChip (psPlane *out,		///< Coordinates to return, or NULL
+		  const psPlane *in,	///< input FPA coordinate
+		  const psChip *chip)	///< Chip of interest
+;
+
+/** converts the specified Chip coord to the coord on the given Cell */
+psPlane *
+psCoordChiptoCell (psPlane *out,	///< Coordinates to return, or NULL
+		   const psPlane *in,	///< input Chip coordinate
+		   const psCell *cell) 	///< Cell of interest
+;
+
+/** Convert (RA,Dec) to cell and cell coordinates */
+psPlane *
+psCoordSkyToCell(psPlane *out,		///< Coordinates to return
+		 const psSphere *in,	///< Input coordinates
+		 float color,		///< Color of source, using reference defined in the fpa
+		 float mag,		///< Magnitude of source
+		 const psCell *cell)	///< Cell of interest
+;
+
+/** Quick and dirty cell to (RA,Dec) --- employs cellToSky transformation */
+psSphere *
+psCoordSkyToCellQD(psPlane *out,	///< Coordinates to return
+		   const psSphere *in,	///< Input coordinates
+		   const psCell *cell)	///< Cell of interest
+;
+
+/**** other utilities ****/
+
+/** Get the airmass for a given position and sidereal time */
+float
+psGetAirmass(const psSphere *coord,	///< Position on the sky
+             psTime *lst,		///< Local sidereal time
+	     float height)		///< Height above sea level
+;
+
+/** Get the parallactic angle for a given position and sidereal time */
+float
+psGetParallactic(const psSphere *coord, ///< Position on the sky
+                 double siderealTime)	///< Sidereal time
+;
+
+/** Estimate atmospheric refraction, along the parallactic */
+float
+psGetRefraction(float colour,           ///< Colour of object
+		psPhotSystem colorPlus,	///< Colour reference
+		psPhotSystem colorMinus, ///< Colour reference
+                const psExposure *exp)	///< Telescope pointing information, for airmass, temp and pressure
+;
+
+/** Calculate the parallax factor */
+psSphere *
+psGetParallaxFactor(const psExposure *exp) ///< Exposure details
+;
+
+/* \} */ // End of AstroGroup Functions
+
+#endif
+
+/*
+  example images:
+
+  GPC:          1 Chip per file, 1 Cell per extension 
+  Megacam:      1 FPA per file, 1 Chip per extension, 2 Cell per extension (DSEC) 
+  CFH12K (MEF): 1 FPA per file, 1 Chip per extension, 1 Cell per extension
+  CFHT-IR:      1 FPA per file, 1 Chip per extension, 4 Cell per extension (NAXIS = 3)
+  WIRCAM:       1 FPA per file, 1 Chip per extension, 1 Cell per extension (?)
+   
+
+*/
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psBitset.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psBitset.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psBitset.h	(revision 22322)
@@ -0,0 +1,54 @@
+# if !defined (PS_BITSET_H)
+# define PS_BITSET_H
+
+/** \file psBitset.h
+ *  \brief Bitset manipulations and operations.
+ *  \ingroup MathGroup
+ */
+
+/** A bitset of arbitrary length. */
+typedef struct {
+    int n;				///< Number of chars that form the bitset
+    char *bits;				///< The bits
+} psBitset;
+
+/** Functions **************************************************************/
+/** \addtogroup MathGroup Math Utilities
+ *  \{
+ */
+
+/** Constructor */
+psBitset *psBitsetAlloc(int n)		///< Number of bits required
+;
+
+/************************************************************************************************************/
+
+/** Set a bitset */
+psBitset *
+psBitsetSet(psBitset *myBits,		///< Input bitset
+	    int bit)			///< Bit to set
+;
+
+/** Check a bitset.  Returns true or false */
+bool
+psBitsetTest(const psBitset *checkBits, ///< bitset to check
+	     int bit)			///< Bit to check
+;
+
+/** apply the given operator to two bitsets */
+psBitset *
+psBitsetOp(psBitset *outBits,		///< Output bitset or NULL
+	   const psBitset *inBits1,	///< Input bitset 1
+	   char *operator,		///< bitset operator (AND, OR, XOR)
+	   const psBitset *inBits2)	///< Input bitset 2
+;
+
+/** Apply unary NOT to a bitset */
+psBitset *
+psBitsetNot(psBitset *out,		///< Output bitset, or NULL
+	    psBitset *in)		///< Input bitset to be NOT-ed
+;
+
+/* \} */ // End of MathGroup Functions
+
+#endif 
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psConvolve.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psConvolve.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psConvolve.h	(revision 22322)
@@ -0,0 +1,52 @@
+#if !defined(PS_CONVOLVE_H)
+#define PS_CONVOLVE_H
+
+/** \file psConvolve.h
+ *  \brief Convolution functions
+ *  \ingroup MathGroup
+ */
+
+
+/**** Convolution ****/
+
+/** A convolution kernel */
+typedef struct {
+    psImage *data;                      ///< Kernel data, in the form of an image
+    int xMin, yMin;                     ///< Most negative indices
+    int xMax, yMax;                     ///< Most positive indices
+    float **kernelRows;                 ///< Pointer to the rows of the kernel data
+    float **kernel;                     ///< Pointer to the kernel data
+} psKernel;
+
+
+
+/** Functions **************************************************************/
+/** \addtogroup MathGroup Math Utilities
+ *  \{
+ */
+
+
+/** Constructor */
+psKernel *psKernelAlloc(int xMin,	///< Size of the kernel in the negative x direction
+			int xMax,	///< Size of the kernel in the positive x direction
+			int yMin,	///< Size of the kernel in the negative y direction
+			int yMax	///< Size of the kernel in the positive y direction
+			);
+
+/** Returns the kernel of OT shifts made during the course of the exposure. */
+psKernel *psKernelGenerate(const psVector *xShifts, ///< List of OT shifts in x; integers
+			   const psVector *yShifts ///< List of OT shifts in y; integers;
+						   ///< xshifts->n == yShifts->n
+    );
+
+/** Returns an image that is the result of convolving the input image with the specified kernel. */
+psImage *psImageConvolve(psImage *out,	///< Output image, or NULL
+			 const psImage *in, ///< Input image to be convolved
+			 const psKernel *kernel, ///< Kernel by which to convolve
+			 bool direct	///< Do direct convolution?  If not, use FFT
+    );
+
+
+/* \} */ // End of MathGroup Functions
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psDateTime.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psDateTime.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psDateTime.h	(revision 22322)
@@ -0,0 +1,122 @@
+#if !defined(PS_DATE_TIME_H)
+#define PS_DATE_TIME_H
+
+#include "taia.h"
+
+/** \file psDateTime.h
+ *  \brief Dates and Times
+ *  \ingroup AstroGroup
+ *  
+ *  The functions in this file define a set of conversions between different time systems and representations.
+ *  All convert to and from the PSLib internal time data value (TAI - TBR).  Available time representations
+ *  include: TAI (taia type), UTC (?), MJD (double?), ISO ISO point-in-time (YYYY-MM-DD,HH:MM:SS.SSS),
+ */
+
+/** \addtogroup AstroGroup Astronomy-Specific Utilities
+ *  \{
+ */
+
+/** psTime is the time structure we will use throughout.  This needs to be revised.  If we end up wrapping
+    libTAI, then we'll probably just use their time struct.  But until then, we need this temporary
+    definition. */
+typedef struct {
+  struct taia t;
+} psTime;
+
+
+/** Get current MJD, for a timestamp \ingroup AstroGroup */
+double
+psGetMJD(void);
+
+/** Get current sidereal time at longitude \ingroup AstroGroup */
+double
+psGetSidereal(float mjd,		///< MJD
+	      float longitude)		///< Longitude
+;
+
+/***** convert from psTime *****/
+/** Convert psTime to ISOTime (Human-readable date/time string YYYY/MM/DD,HH:MM:SS.SSS) */
+char *
+psTimeToISOTime (psTime time)		///< Input psTime time
+;
+
+/** Convert psTime to UTC */
+double
+psTimeToUTC (psTime time)		///< Input psTime time
+;
+
+/** Convert psTime to MJD */
+double
+psTimeToMJD (psTime time)		///< Input psTime time
+;
+
+/** Convert psTime to JD */
+double
+psTimeToJD (psTime time)		///< Input psTime time
+;
+
+/** Convert psTime to timeval (struct timeval) */
+struct timeval *
+psTimeToTimeval (psTime time)		///< Input psTime time
+;
+
+/** Convert psTime to broken-down time (struct tm) */
+struct tm *				
+psTimeToTm (psTime time)		///< Input psTime time
+;
+
+/** Convert psTime to lunation number */
+float
+psTimeToLunation (psTime time)		///< Input psTime time
+;
+
+/***** convert to psTime *****/
+/** Convert ISOTime (Human-readable date/time string YYYY/MM/DD,HH:MM:SS.SSS) to psTime \ingroup AstroGroup */
+psTime *
+psISOTimeToTime (char *input)		///< Input ISOTime time
+;
+
+/** Convert UTC to psTime */
+psTime *
+psUTCToTime (double input)		///< Input UTC time
+;
+
+/** Convert MJD to psTime */
+psTime *
+psMJDToTime (double input)		///< Input MJD time
+;
+
+/** Convert JD to psTime */
+psTime *
+psJDToTime (double input)		///< Input JD time
+;
+
+/** Convert timeval (struct timeval) to psTime */
+psTime *
+psTimevalToTime (struct timeval *input)	///< Input timeval time
+;
+
+/** Convert broken-down time (struct tm) to psTime */
+psTime *
+psTMToTime (struct tm *input)		///< Input tm time
+;
+
+/** Convert lunation number to psTime */
+psTime *
+psLunationToTime (float lunation)	///< Input lunation
+;
+
+/* \} end of AstroGroup */
+
+#endif
+
+/* date/time representations of interest:
+
+  TAI - 
+  UTC - long? float? double?
+  MJD - double?
+  ISO - yyyy-mm-dd hh:mm:ss +oooo
+  point-in-time YYYY/MM/DD,HH:MM:SS.SSS
+  timeval
+  
+*/
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psError.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psError.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psError.h	(revision 22322)
@@ -0,0 +1,61 @@
+#if !defined(PS_ERROR_H)
+#define PS_ERROR_H
+#include <stdio.h>
+#include <errno.h>
+/*
+ * Classes of errors that Pan-STARRS uses; more or less the equivalent of the values of errno
+ *
+ * This initialisation is machine-generated from psError.dat
+ */
+#include <stdarg.h>
+#include "psErrorCodes.h"
+
+typedef struct {
+    psErrorCode code;			///< An error code
+    const char *descrip;		///< the associated description
+} psErrorDescription;
+
+typedef struct {
+    char *name;				///< category of code that caused the error
+    psErrorCode code;			///< class of error (equivalent to errno)
+    char *msg;				///< the message associated with the error
+} psErr;
+
+/// Prints an error message and doesn't abort; returns code
+int psError(const char *name,		///< Category of code that caused the error
+	    psErrorCode code,		///< class of error (equivalent to errno)
+	    bool new,	                ///< is this a new error?
+	    const char *fmt,		///< Format
+	    ...)			///< Extra arguments to use format
+;
+
+void p_psErrorRegister(const psErrorDescription *errors, ///< register a set of errors
+		       int nerror)	///< number of errors
+;
+
+///< return specified error (or an "error" with code PS_ERR_NONE)
+const psErr *psErrorGet(int which);	
+
+///< return last error (or an "error" with code PS_ERR_NONE)
+const psErr *psErrorLast(void);		
+
+///< Clear the error stack
+void psErrorClear(void);		
+
+///< return the string associated with the error code
+const char *psErrorCodeString(psErrorCode code);	
+
+///< print the errorstack to this file descriptor
+void psErrorStackPrint(FILE *fd,	///< write to this file descriptor
+		       const char *fmt, ///< format for any header information; may be NULL
+		       ...)		///< arguments for format
+;	
+
+///< print the errorstack to this file descriptor (stdargs version)
+void psErrorStackPrintV(FILE *fd,	///< write to this file descriptor
+			const char *fmt,///< format for any header information; may be NULL
+			va_list ap)	///< arguments for format
+;
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psFFT.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psFFT.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psFFT.h	(revision 22322)
@@ -0,0 +1,112 @@
+#if !defined(PS_FFT_H)
+#define PS_FFT_H
+
+/* #include <fftw3.h> */
+
+/** \file psFFT.h
+ *  \brief Fourier Transform functions
+ *  \ingroup MathGroup
+ */
+
+/** Functions **************************************************************/
+/** \addtogroup MathGroup Math Utilities
+ *  \{
+ */
+
+
+/** Specify direction of FFT, and if the result is real or not */
+typedef enum {
+    PS_FFT_FORWARD = 0,                 ///< psImageFFT/psVectorFFT should perform a forward FFT.
+    PS_FFT_REVERSE = 1,                 ///< psImageFFT/psVectorFFT should perform a reverse FFT.
+    PS_FFT_REAL_RESULT = 2		///< psImageFFT/psVectorFFT should return a real image. This is valid
+					///< for only reverse FFT, i.e., the psImageFFT/psVectorFFT flag
+					///< parameter should appear as PS_FFT_REVERSE+PS_FFT_REAL_RESULT.
+} psFFTFlags;
+
+
+/**** Vector FFT & Complex functions ****/
+
+/** FFT a vector. in: psF32 or psC32, out: psC32 */
+psVector *
+psVectorFFT(psVector *out,		///< Vector for output (or NULL)
+	    const psVector *vector,	///< Vector to transform
+	    psFFTFlags dir)		///< FFT direction
+;
+
+/** Get the real part of a vector */
+psVector *
+psVectorReal(psVector *out,		///< Vector for output (or NULL)
+	     const psVector *in)	///< Vector to get the real part of
+;
+
+/** Get the imaginary part of a vector */
+psVector *
+psVectorImaginary(psVector *out,	///< Vector for output (or NULL)
+		  const psVector *in)	///< Vector to get the imaginary part of
+;
+
+/** Construct a complex vector from real & imaginary parts */
+psVector *
+psVectorComplex(psVector *out,		///< Vector for output (or NULL)
+		const psVector *real,	///< real part of vector
+		const psVector *imag)	///< imaginary part of vector
+;
+
+/** Get the complex conjugate of an vector of complex floating-point numbers */
+psVector *
+psVectorConjugate(psVector *out,	///< Vector for output (or NULL)
+		  const psVector *in)	///< Vector to get the complex conjugate of
+;
+
+/** Calculate power spectrum of a vector of floating-point numbers */
+psVector *
+psVectorPowerSpectrum(psVector *out,	///< Vector for output (or NULL)
+		      const psVector *vector) ///< Vector to obtain power spectrum of
+;
+
+/**** Image FFT & Complex functions ****/
+
+/** FFT an image.  in: psF32 or psC32, out: psC32 */
+psImage *
+psImageFFT(psImage *out,		///< Image for output (or NULL)
+	   const psImage *image,	///< image to transform (if forward, may be real)
+	   psFFTFlags dir)		///< FFT direction
+;
+
+/** Get the real part of an image */
+psImage *
+psImageReal(psImage *out,		///< Image for output (or NULL)
+	    const psImage *in)		///< Image to get the real part of
+;
+
+/** Get the imaginary part of an image */
+psImage *
+psImageImaginary(psImage *out,		///< Image for output (or NULL)
+		 const psImage *in)	///< Image to get the imaginary part of
+;
+
+/** Construct a complex image from real & imaginary parts */
+psImage *
+psImageComplex(psImage *out,		///< Image for output (or NULL)
+	       const psImage *real,	///< real part of image
+	       const psImage *imag)	///< imaginary part of image
+;
+
+/** Get the complex conjugate of an image */
+psImage *
+psImageConjugate(psImage *out,		///< Image for output (or NULL)
+		 const psImage *in)	///< Image to get the complex conjugate of
+;
+
+
+/** Calculate power spectrum of an image */
+psImage *
+psImagePowerSpectrum(psImage *out,	///< Image for output (or NULL)
+		     const psImage *image) ///< Image to obtain power spectrum of
+;
+
+/* \} */ // End of MathGroup Functions
+
+#endif
+
+
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psFunctions.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psFunctions.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psFunctions.h	(revision 22322)
@@ -0,0 +1,335 @@
+#if !defined(PS_FUNCTIONS_H)
+#define PS_FUNCTIONS_H
+
+/** \file psFunctions.h
+ *  \brief Standard Mathematical Functions. 
+ *  \ingroup MathGroup
+ */
+
+/** Evaluate a non-normalized Gaussian with the given mean and sigma at the given coordianate.  Note that this
+ *  is not a Gaussian deviate.  The evaluated Gaussian is:
+ *  \f[ 1/(\sqrt(2\pi)\sigma) exp(-\frac{(x-mean)^2}{2\sigma^2}) \f]
+ */
+float
+psGaussian(float x,			///< Value at which to evaluate
+	   float mean,			///< Mean for the Gaussian
+	   float sigma,			///< Standard deviation for the Gaussian
+	   bool normal)			///< normalized Gaussian?
+;
+
+psVector *
+psGaussianDev(float mean,		///< Mean for the Gaussian
+	      float sigma,		///< Standard deviation for the Gaussian
+	      int Npts)			///< Number of points generated
+;
+
+/************************************************************************************************************/
+
+/** Type of polynomial */
+typedef enum {
+    PS_POLYNOMIAL_ORD,			///< Ordinary polynomial
+    PS_POLYNOMIAL_CHEB			///< Chebyshev polynomial
+} psPolynomialType;
+
+/************************************************************************************************************/
+
+/** one-dimensional polynomial */
+typedef struct {
+    psPolynomialType type;		///< Polynomial type
+    int n;				///< Number of terms
+    float *restrict coeff;		///< Coefficients
+    float *restrict coeffErr;		///< Error in coefficients
+    bool *restrict mask;		///< Coefficient mask
+} psPolynomial1D;
+
+/** Two-dimensional polynomial */
+typedef struct {
+    psPolynomialType type;		///< Polynomial type
+    int nX, nY;				///< Number of terms in x and y
+    float *restrict *restrict coeff;	///< Coefficients
+    float *restrict *restrict coeffErr;	///< Error in coefficients
+    bool *restrict *restrict mask;	///< Coefficients mask
+} psPolynomial2D;
+
+/** Three-dimensional polynomial */
+typedef struct {
+    psPolynomialType type;		///< Polynomial type
+    int nX, nY, nZ;			///< Number of terms in x, y and z
+    float *restrict *restrict *restrict coeff; ///< Coefficients
+    float *restrict *restrict *restrict coeffErr; ///< Error in coefficients
+    bool *restrict *restrict *restrict mask; ///< Coefficients mask
+} psPolynomial3D;
+
+/** Four-dimensional polynomial */
+typedef struct {
+    psPolynomialType type;		///< Polynomial type
+    int nW, nX, nY, nZ;			///< Number of terms in w, x, y and z
+    float *restrict *restrict *restrict *restrict coeff; ///< Coefficients
+    float *restrict *restrict *restrict *restrict coeffErr; ///< Error in coefficients
+    bool *restrict *restrict *restrict *restrict mask; ///< Coefficients mask
+} psPolynomial4D;
+
+
+/** Functions **************************************************************/
+/** \addtogroup MathGroup Math Utilities
+ *  \{
+ */
+
+/** Constructor */
+psPolynomial1D *psPolynomial1DAlloc(int n ///< Number of terms
+				    psPolynomialType type ///< Polynomial type
+				    );
+/** Constructor */
+psPolynomial2D *psPolynomial2DAlloc(int nX, int nY ///< Number of terms in x and y
+				    psPolynomialType type ///< Polynomial type
+				    );
+/** Constructor */
+psPolynomial3D *psPolynomial3DAlloc(int nX, int nY, int nZ ///< Number of terms in x, y and z
+				    psPolynomialType type ///< Polynomial type
+				    );
+/** Constructor */
+psPolynomial4D *psPolynomial4DAlloc(int nW, int nX, int nY, int nZ ///< Number of terms in w, x, y and z
+				    psPolynomialType type ///< Polynomial type
+				    );
+
+/** Destructor */
+void psPolynomial1DFree(psPolynomial1D *restrict myPoly ///< Polynomial to destroy
+    );
+     
+/** Destructor */
+void psPolynomial2DFree(psPolynomial2D *restrict myPoly ///< Polynomial to destroy
+    );
+/** Destructor */
+void psPolynomial3DFree(psPolynomial3D *restrict myPoly ///< Polynomial to destroy
+    );
+/** Destructor */
+void psPolynomial4DFree(psPolynomial4D *restrict myPoly ///< Polynomial to destroy
+    );
+
+/** Evaluate 1D polynomial */
+float
+psPolynomial1DEval(float x,		///< Value at which to evaluate
+		   const psPolynomial1D *myPoly ///< Coefficients for the polynomial
+		   );
+
+/** Evaluate 2D polynomial */
+float
+psPolynomial2DEval(float x,		///< Value x at which to evaluate
+		   float y,		///< Value y at which to evaluate
+		   const psPolynomial2D *myPoly ///< Coefficients for the polynomial
+		   );
+
+/** Evaluate 3D polynomial */
+float
+psPolynomial3DEval(float x,		///< Value x at which to evaluate
+		   float y,		///< Value y at which to evaluate
+		   float z,		///< Value z at which to evaluate
+		   const psPolynomial3D *myPoly ///< Coefficients for the polynomial
+		   );
+
+/** Evaluate 4D polynomial */
+float
+psPolynomial4DEval(float w,		///< Value w at which to evaluate
+		   float x,		///< Value x at which to evaluate
+		   float y,		///< Value y at which to evaluate
+		   float z,		///< Value z at which to evaluate
+		   const psPolynomial4D *myPoly ///< Coefficients for the polynomial
+		   );
+
+/** Evaluate 1D polynomial: vector input */
+psVector *
+psPolynomial1DEvalVector(const psVector *x, ///< Value at which to evaluate
+			 const psPolynomial1D *myPoly ///< Coefficients for the polynomial
+			 );
+
+/** Evaluate 2D polynomial: vector input */
+psVector *
+psPolynomial2DEvalVector(const psVector *x, ///< Value x at which to evaluate
+			 const psVector *y, ///< Value y at which to evaluate
+			 const psPolynomial2D *myPoly ///< Coefficients for the polynomial
+			 );
+
+/** Evaluate 3D polynomial: vector input */
+psVector *
+psPolynomial3DEvalVector(const psVector *x, ///< Value x at which to evaluate
+			 const psVector *y, ///< Value y at which to evaluate
+			 const psVector *z, ///< Value z at which to evaluate
+			 const psPolynomial3D *myPoly ///< Coefficients for the polynomial
+			 );
+
+/** Evaluate 4D polynomial: vector input */
+psVector *
+psPolynomial4DEvalVector(const psVector *w, ///< Value w at which to evaluate
+			 const psVector *x, ///< Value x at which to evaluate
+			 const psVector *y, ///< Value y at which to evaluate
+			 const psVector *z, ///< Value z at which to evaluate
+			 const psPolynomial4D *myPoly ///< Coefficients for the polynomial
+			 );
+
+
+/* \} */ // End of MathGroup Functions
+
+/************************************************************************************************************/
+
+/* Double-precision polynomials, mainly for use in astrometry */
+
+/** Double-precision one-dimensional polynomial */
+typedef struct {
+    psPolynomialType type;		///< Polynomial type
+    int n;				///< Number of terms
+    double *restrict coeff;		///< Coefficients
+    double *restrict coeffErr;		///< Error in coefficients
+    bool *restrict mask;		///< Coefficient mask
+} psDPolynomial1D;
+
+/** Double-precision two-dimensional polynomial */
+typedef struct {
+    psPolynomialType type;		///< Polynomial type
+    int nX, nY;				///< Number of terms in x and y
+    double *restrict *restrict coeff;	///< Coefficients
+    double *restrict *restrict coeffErr; ///< Error in coefficients
+    bool *restrict *restrict mask;	///< Coefficients mask
+} psDPolynomial2D;
+
+/** Double-precision three-dimensional polynomial */
+typedef struct {
+    psPolynomialType type;		///< Polynomial type
+    int nX, nY, nZ;			///< Number of terms in x, y and z
+    double *restrict *restrict *restrict coeff;	///< Coefficients
+    double *restrict *restrict *restrict coeffErr; ///< Error in coefficients
+    bool *restrict *restrict *restrict mask; ///< Coefficient mask
+} psDPolynomial3D;
+
+/** Double-precision four-dimensional polynomial */
+typedef struct {
+    psPolynomialType type;		///< Polynomial type
+    int nW, nX, nY, nZ;			///< Number of terms in w, x, y and z
+    double *restrict *restrict *restrict *restrict coeff; ///< Coefficients
+    double *restrict *restrict *restrict *restrict coeffErr; ///< Error in coefficients
+    bool *restrict *restrict *restrict *restrict mask; ///< Coefficients mask
+} psDPolynomial4D;
+
+/** Functions **************************************************************/
+/** \addtogroup MathGroup Math Utilities
+ *  \{
+ */
+
+/** Constructor */
+psDPolynomial1D *psDPolynomial1DAlloc(int n, ///< Number of terms
+				      psPolynomialType type ///< Polynomial type
+				      );
+/** Constructor */
+psDPolynomial2D *psDPolynomial2DAlloc(int nX, int nY ///< Number of terms in x and y
+				      psPolynomialType type ///< Polynomial type
+				      );
+/** Constructor */
+psDPolynomial3D *psDPolynomial3DAlloc(int nX, int nY, int nZ ///< Number of terms in x, y and z
+				      psPolynomialType type ///< Polynomial type
+				      );
+/** Constructor */
+psDPolynomial4D *psDPolynomial4DAlloc(int nW, int nX, int nY, int nZ ///< Number of terms in w, x, y and z
+				      psPolynomialType type ///< Polynomial type
+				      );
+
+/** Evaluate 1D polynomial (double precision) */
+double
+psDPolynomial1DEval(double x,		///< Value at which to evaluate
+		    const psDPolynomial1D *myPoly ///< Coefficients for the polynomial
+		    );
+
+/** Evaluate 2D polynomial (double precision) */
+double
+psDPolynomial2DEval(double x,		///< Value x at which to evaluate
+		    double y,		///< Value y at which to evaluate
+		    const psDPolynomial2D *myPoly ///< Coefficients for the polynomial
+		    );
+
+/** Evaluate 3D polynomial (double precision) */
+double
+psDPolynomial3DEval(double x,		///< Value x at which to evaluate
+		    double y,		///< Value y at which to evaluate
+		    double z,		///< Value z at which to evaluate
+		    const psDPolynomial3D *myPoly ///< Coefficients for the polynomial
+		    );
+
+/** Evaluate 4D polynomial (double precision) */
+double
+psDPolynomial4DEval(double w,		///< Value w at which to evaluate
+		    double x,		///< Value x at which to evaluate
+		    double y,		///< Value y at which to evaluate
+		    double z,		///< Value z at which to evaluate
+		    const psDPolynomial4D *myPoly ///< Coefficients for the polynomial
+		    );
+
+/** Evaluate 1D polynomial: vector input */
+psVector *
+psDPolynomial1DEvalVector(const psVector *x, ///< Value at which to evaluate
+			  const psDPolynomial1D *myPoly ///< Coefficients for the polynomial
+			  );
+
+/** Evaluate 2D polynomial: vector input */
+psVector *
+psDPolynomial2DEvalVector(const psVector *x, ///< Value x at which to evaluate
+			  const psVector *y, ///< Value y at which to evaluate
+			  const psDPolynomial2D *myPoly ///< Coefficients for the polynomial
+			  );
+
+/** Evaluate 3D polynomial: vector input */
+psVector *
+psDPolynomial3DEvalVector(const psVector *x, ///< Value x at which to evaluate
+			  const psVector *y, ///< Value y at which to evaluate
+			  const psVector *z, ///< Value z at which to evaluate
+			  const psDPolynomial3D *myPoly ///< Coefficients for the polynomial
+			  );
+
+/** Evaluate 4D polynomial: vector input */
+psVector *
+psDPolynomial4DEvalVector(const psVector *w, ///< Value w at which to evaluate
+			  const psVector *x, ///< Value x at which to evaluate
+			  const psVector *y, ///< Value y at which to evaluate
+			  const psVector *z, ///< Value z at which to evaluate
+			  const psDPolynomial4D *myPoly ///< Coefficients for the polynomial
+			  );
+
+/* \} */ // End of MathGroup Functions
+
+/************************************************************************************************************/
+
+/* Splines */
+
+/** A 1D spline */
+typedef struct {
+    int n;				///< Number of spline pieces
+    psPolynomial1D **spline;		///< Array of n pointers to splines
+    float *domains;			///< The boundaries between each spline piece.  Size is n+1.
+} psSpline1D;
+
+/** Functions **************************************************************/
+/** \addtogroup MathGroup Math Utilities
+ *  \{
+ */
+
+/** Constructors */
+psSpline1D *psSpline1DAlloc(int n,	///< Number of spline pieces
+			    int order,	///< Order of spline pieces
+			    float min,	///< Minimum data value
+			    float max	///< Maximum data value
+			    );
+psSpline1D *psSpline1DAllocGeneric(const psVector *bounds ///< Boundaries between each spline piece.  Number
+							  ///< of spline pieces can hence be inferred.
+				   int order	///< Order of spline pieces
+				   );
+/** Destructor */
+void psSpline1DFree(psSpline1D *mySpline ///< Spline to destroy
+		    );
+/** Evaluator */
+float psSpline1DEval(float x,		///< Coordinate at which to evaluate
+		     const psSpline1D *spline ///< Spline to evaluate
+		     );
+
+/** Evaluator: vector version */
+psVector *psSpline1DEvalVector(const psVector *x, ///< Coordinates at which to evaluate
+			       const psSpline1D *spline ///< Spline to evaluate
+			       );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psHash.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psHash.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psHash.h	(revision 22322)
@@ -0,0 +1,53 @@
+#if !defined(PS_HASH_H)
+#define PS_HASH_H
+
+/** \file psHash.h
+ *  \brief Hash Table manipulation
+ *  \ingroup DataGroup
+ */
+
+/** A hash table */
+typedef struct {
+    int nbucket;                        ///< number of buckets
+    psHashBucket **buckets;             ///< the buckets themselves
+} psHash;
+
+/** The hash buckets, which compose the hash */
+typedef struct psHashBucket {
+    char *key;                          ///< key for this item of data
+    void *data;                         ///< the data itself
+    struct psHashBucket *next;          ///< list of other possible keys
+} psHashBucket;
+
+/** Functions **************************************************************/
+/** \addtogroup DataGroup General Data Container Utilities  
+ *  \{
+ */
+
+/** Allocate hash buckets in table. */
+psHash *psHashAlloc(void);
+
+/** Add entry to table. */
+bool psHashAdd(psHash *table,		///< table to insert in
+	       const char *key,		///< key to use
+	       void *data		///< data to insert
+    );
+
+/** Lookup key in table. */
+void *psHashLookup(psHash *table,	///< table to lookup key in
+		   const char *key	///< key to lookup
+    );
+
+/** Remove key from table. */
+bool psHashRemove(psHash *table,	///< table to lookup key in
+		  const char *key	///< key to lookup
+    );
+
+/** Get list of keys */
+psList *psHashKeyList(psHash *table	///< table for which to get keys
+    );
+
+
+/* \} */ // End of DataGroup Functions
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psImage.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psImage.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psImage.h	(revision 22322)
@@ -0,0 +1,289 @@
+# ifndef PS_IMAGE_H
+# define PS_IMAGE_H
+
+/** \file psImage.h
+ *  \brief Basic image definitions and operations.
+ *  \ingroup AstroGroup
+ */
+
+/// basic image data structure.
+typedef struct psImage {
+    psType type; 			///< image data type and dimension
+    const int ncols, nrows;		///< size of image 
+    const int x0, y0;			///< data region relative to parent 
+    union {
+        psS8  **S8;                     ///< Pointers to char data
+        psS16 **S16;                    ///< Pointers to short-integer data
+        psS32 **S32;                    ///< Pointers to integer data
+        psS64 **S64;                    ///< Pointers to long-integer data
+        psU8  **U8;                     ///< Pointers to unsigned-char data
+        psU16 **U16;                    ///< Pointers to unsigned-short-integer data
+        psU32 **U32;                    ///< Pointers to unsigned-integer data
+        psU64 **U64;                    ///< Pointers to unsigned-long-integer data
+        psF32 **F32;                    ///< Pointers to floating-point data
+        psF64 **F64;                    ///< Pointers to double-precision data
+        psC32 **C32;                    ///< Pointers to complex floating-point data
+        psC64 **C64;                    ///< Pointers to complex floating-point data
+    } data;
+    const struct psImage *parent;	///< parent, if a subimage 
+    psArray *children;			///< children of this region
+} psImage;
+
+/** Functions **************************************************************/
+/** \addtogroup AstroGroup Astronomy-Specific Utilities
+ *  \{
+ */
+
+/*** Image structure manipulation ***/
+
+/// Create an image of the specified size and type.
+psImage *
+psImageAlloc (int width,		///< image width (ncols)
+	      int height,		///< image height (nrows)
+	      psElemType type)		///< image data type 
+;
+
+/// Create a subimage of the specified area.
+psImage *
+psImageSubset(const psImage *image,	///< parent image 
+	      int nx,			///< subimage width (<= image.ncols - col0)  
+	      int ny,			///< subimage height (<= image.nrows - row0)  
+	      int x0,			///< subimage starting col (0 <= col0 < ncols)   
+	      int y0)			///< subimage starting row (0 <= row0 < nrows)   
+;
+
+/// Create a subimage of the area specified by an image section, [x1:x2,y1:y2]
+psImage *
+psImageSubsection(const psImage *image,	///< Parent image
+		  const char *section	///< Image section, of the form [x1:x2,y1:y2]
+    );
+
+
+/// Create a copy of the specified image.
+psImage *
+psImageCopy(psImage *output, 		///< target structure for output image data
+	    const psImage *input,	///< copy this image 
+	    psElemType type)		///< element type of output image
+;
+
+
+/// Trim an image, in-place in memory
+psImage *
+psImageTrim(psImage *image,		///< Input image, and output
+	    int x0, int x1,		///< Keep x0 to x1
+	    int y0, int y1		///< Keep y0 to y1
+    );
+	    
+/*** various image pixel extractions ***/
+
+/** Direction in which to cut */
+typedef enum {
+    PS_CUT_X_POS,                     ///< Cut in positive x direction
+    PS_CUT_X_NEG,                     ///< Cut in negative x direction
+    PS_CUT_Y_POS,                     ///< Cut in positive y direction
+    PS_CUT_Y_NEG                      ///< Cut in negative y direction
+} psImageCutDirection;
+
+/// Extract pixels from rectlinear region to a vector.
+psVector *
+psImageSlice(psVector *out,		///< Vector to output, or NULL
+	     const psImage *input,	///< Input image
+	     const psImage *mask,	///< Ignore those pixels where mask & maskVal == 1.  May be NULL
+	     unsigned int maskVal,	///< Value in mask to ignore
+	     unsigned int x,            ///< starting x coord of region to slice
+	     unsigned int y,            ///< starting y coord of region to slice
+	     unsigned int nx,           ///< length of region in x
+	     unsigned int ny,           ///< length of region in y
+	     psImageCutDirection direction, ///< Direction in which to cut
+	     const psStats *stats       ///< defines statistics used to find output values
+    );
+
+/// Extract pixels along a line to a vector.
+psVector *
+psImageCut(psVector *out,		///< Vector to output, or NULL
+	   const psImage *input,	///< extract cut from this image
+	   const psImage *mask,		///< Ignore those pixels where mask & maskVal == 1.  May be NULL
+	   unsigned int maskVal,	///< Value in mask to ignore
+	   float xs, 			///< starting x coord of cut
+	   float ys, 			///< starting y coord of cut
+	   float xe, 			///< ending x coord of cut
+	   float ye, 			///< ending y coord of cut
+	   unsigned int nSamples,	///< Number of samples along the slice
+	   psImageInterpolateMode mode	///< defines statistics used to find output values
+	   );
+
+/// Extract radial annulii data to a vector.
+psVector *
+psImageRadialCut(psVector *out,		///< Vector to output, or NULL
+		 const psImage *input,	///< extract profile from this image
+		 const psImage *mask,	///< Ignore those pixels where mask & maskVal == 1.  May be NULL
+		 unsigned int maskVal,	///< Value in mask to ignore
+		 float x, 		///< center x coord of annulii
+		 float y, 		///< center y coord of annulii
+		 const psVector *radii, ///< Radii of the annuli
+		 const psStats *stats)	///< defines statistics used to find output values
+;
+
+/*** various image geometry manipulation ***/
+
+/** How to do interpolation */
+typedef enum {
+    PS_INTERPOLATE_FLAT, 
+    PS_INTERPOLATE_BILINEAR, 
+    PS_INTERPOLATE_BICUBIC, 
+    PS_INTERPOLATE_SINC 
+} psImageInterpolateMode mode;
+
+/// Rebin image to new scale.
+psImage *
+psImageRebin(psImage *out,		///< Image to output, or NULL
+	     const psImage *in,		///< rebin this image
+	     const psImage *restrict mask, ///< Ignore those pixels where mask & maskVal == 1.  May be NULL
+	     unsigned int maskVal,	///< Value in mask to ignore
+	     int scale, 		///< rebinning scale
+	     const psStats *stats)	///< defines statistics used to find output values
+;
+
+/** Resample image to new scale */
+psImage *
+psImageResample(psImage *out,		///< Image to output, or NULL
+		const psImage *in,	///< resample this image
+		int scale,		///< Factor by which to resample
+		psImageInterpolateMode mode ///< Interpolation method
+    );
+
+/// Rotate image by given angle.
+psImage *
+psImageRotate(psImage *out,		///< Image to output, or NULL
+	      const psImage *in,	///< rotate this image
+	      float angle		///< rotate by this amount anti-clockwise (degrees)
+	      psC64 exposed,		///< Set exposed pixels to this value
+	      psImageInterpolateMode mode ///< Interpolation method
+    );
+
+/// Shift image by an arbitrary number of pixels in either direction.
+/// Drop edge pixels, and set newly exposed pixels to 'exposed'.
+psImage *
+psImageShift(psImage *out,		///< Image to output, or NULL
+	     const psImage *in,		///< shift this image
+	     float dx,			///< shift by this amount in x
+	     float dy,			///< shift by this amount in y
+	     psC64 exposed,		///< set exposed pixels to this value
+	     psImageInterpolateMode mode ///< Interpolation method
+);
+
+/// Roll image by an integer number of pixels in either direction.
+/// Edge pixels wrap to the other side (no values are lost).
+psImage *
+psImageRoll(psImage *out,		///< Image to output, or NULL
+	    const psImage *in,	///< roll this image
+	    int dx, 			///< roll this amount in x
+	    int dy)			///< roll this amount in y
+;
+
+/// Determine statistics for image (or subimage).
+psStats *
+psImageGetStats(psStats *stats,		///< defines statistics to be calculated & target
+		const psImage *in, 	///< image (or subimage) to calculate stats
+		const psImage *mask,	///< Ignore those pixels where mask & maskVal == 1.  May be NULL
+		unsigned int maskVal	///< Value in mask to ignore
+    );
+
+/// Construct a histogram from an image (or subimage).
+psHistogram *
+psImageHistogram(psHistogram *hist,	///< input histogram description & target
+		 const psImage *in	///< determine histogram of this image
+		 const psImage *mask,	///< Ignore those pixels where mask & maskVal == 1.  May be NULL
+		 unsigned int maskVal	///< Value in mask to ignore
+    );
+
+/// Fit a 2-D polynomial surface to an image.
+psPolynomial2D *
+psImageFitPolynomial(psPolynomial2D *coeffs, ///< coefficient structure carries in desired terms & target
+		     const psImage *input) ///< image to fit
+;
+
+/// Evaluate a 2-D polynomial surface to image pixels.
+psImage *
+psImageEvalPolynomial(psImage *input,	///< image to produce
+		      const psPolynomial2D *coeffs) ///< coefficient structure carries in desired terms
+;
+
+/** Perform interpolation of image pixel values */
+psF64
+psImagePixelInterpolate(const psImage *input, ///< Image on which to interpolate
+			float x, float y, ///< Fractional pixel coordinates at which to interpolate
+			psC64 unexposedValue, ///< Value for pixels that don't have an input value
+			psImageInterpolateMode mode ///< Interpolation method
+    );
+
+
+/*** image input/output routines ***/
+/// Read an image or subimage from a named file.
+psImage *
+psImageReadSection(psImage *output, 	///< place data in this structure for output 
+		   int x0, 		///< starting x coord of region
+		   int y0, 		///< starting y coord of region
+		   int nx, 		///< x size of region (-1 for full range)
+		   int ny, 		///< y size of region (-1 for full range)
+		   int z, 		///< plane of interest
+		   const char *extname,	///< MEF extension name ("PHU" for primary header)
+		   int extnum,		///< MEF extension number (-1 for "PHU", 0 : Nextend - 1)
+		   const char *filename) ///< file to read data from
+;
+
+/// Write an image section to named file (which may exist).
+psImage *
+psImageWriteSection(const psImage *input, ///< image to write out
+		    int x, 		///< starting x coord of region		   
+		    int y, 		///< starting y coord of region		   
+		    int z, 		///< plane of interest			   
+		    const char *extname, ///< MEF extension name ("PHU" for primary header)
+		    int extnum, 	///< MEF extension number (-1 for "PHU", 0 : Nextend - 1)
+		    const char *filename) ///< file to write data to		   
+;
+
+/*** basic pixel manipulations ***/
+/// Clip image values outside of range to given values.  Return number of clipped pixels.
+int
+psImageClip(psImage *input, 		///< Image to be clipped, and output
+	    double min,			///< clip pixels with values < min
+	    double vmin, 		///< set min-clipped pixels to vmin
+	    double max,			///< clip pixels with values > max
+	    double vmax)		///< set max-clipped pixels to vmax
+;
+
+/** Clipping for complex data */
+int
+psImageClipComplexRegion(psImage *input, ///< Image to be clipped, and output
+			 complex double min, ///< clip pixels with values < min
+			 complex double vmin, ///< set min-clipped pixels to vmin
+			 complex double max, ///< clip pixels with values > max
+			 complex double vmax ///< set max-clipped pixels to vmax
+    );
+
+/// Clip NaN image pixels to given value.  Return number of clipped pixels.
+int
+psImageClipNaN(psImage *input,		///< clip this image & target
+	       float value)		///< set nan pixels to this value
+;
+
+/// Overlay subregion of image with another image.  Return number of pixels replaced.
+int 
+psImageOverlaySection(psImage *image,	///< input image & target
+		      const psImage *overlay, ///< image to overlay 
+		      int x0,		///< x offset of overlay subimage 
+		      int y0,		///< y offset of overlay subimage 
+		      const char *op)	///< overlay operation 
+;
+
+/* \} */ // End of AstroGroup Functions
+
+# endif
+/* image overlay operations 
+   PS_OVERLAY_EQUALS   = '=', 
+   PS_OVERLAY_ADD      = '+', 
+   PS_OVERLAY_SUBTRACT = '-', 
+   PS_OVERLAY_MULTIPLY = '*', 
+   PS_OVERLAY_DIVIDE   = '/', 
+*/
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psLib.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psLib.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psLib.h	(revision 22322)
@@ -0,0 +1,59 @@
+# if !defined (PS_LIB_H)
+# define PS_LIB_H
+
+#include <complex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+/** \file psLib.h
+ *  \brief Master header file for PSLib
+ */
+
+/** @defgroup SystemGroup System Utilities */
+/** @defgroup DataGroup General Data Container Utilities */
+/** @defgroup MathGroup Math Utilities */
+/** @defgroup AstroGroup Astronomy-Specific Utilities */
+
+/** @ingroup SystemGroup */
+# include <psMemory.h>
+# include <psTrace.h>
+# include <psLogMsg.h>
+# include <psMisc.h>
+# include <psError.h>
+
+/** @ingroup DataGroup */
+# include <psType.h>
+# include <psVector.h>
+# include <psHash.h>
+# include <psDlist.h>
+
+/* These are here to ensure things compile --- ordering is important.  ): */
+/* They are also listed later to keep things together */
+/** @ingroup MathGroup */
+# include <psStats.h>
+# include <psFunctions.h>
+/** @ingroup AstroGroup */
+# include <psMetadata.h>
+# include <psImage.h>
+
+/** @ingroup MathGroup */
+# include <psBitset.h>
+# include <psFFT.h>
+# include <psFunctions.h>
+# include <psMath.h>
+# include <psMatrix.h>
+# include <psMinimize.h>
+# include <psStats.h>
+# include <psSort.h>
+
+/** @ingroup AstroGroup */
+# include <psDateTime.h>
+# include <psPosition.h>
+# include <psMetadata.h>
+# include <psImage.h>
+# include <psPhotom.h>
+# include <psAstrom.h>
+/* # include <psObject.h> */
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psList.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psList.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psList.h	(revision 22322)
@@ -0,0 +1,94 @@
+#if !defined(PS_LIST_H)
+#define PS_LIST_H
+
+/** \file psList.h
+ *  \brief Support for doubly linked lists
+ *  \ingroup DataGroup
+ */
+
+/** Doubly-linked list element */
+typedef struct psListElem {
+   struct psListElem *prev;		///< previous link in list
+   struct psListElem *next;		///< next link in list
+   void *data;				///< real data item
+} psListElem;
+
+/** Doubly-linked list */
+typedef struct {
+   unsigned int size;			///< number of elements on list
+   psListElem *head;			///< first element on list (may be NULL)
+   psListElem *tail;			///< last element on list (may be NULL)
+   psListElem *iter;			///< iteration cursor
+   unsigned int iterIndex;		///< the numeric position of the iteration cursor in the list
+   pthread_mutex_t lock;		///< mutex to lock a node during changes
+} psList;
+
+/** Special values of index into list */
+enum {
+   PS_LIST_HEAD = 0,			///< at head
+   PS_LIST_TAIL = -1,			///< at tail
+   PS_LIST_UNKNOWN = -2,		///< unknown position
+   PS_LIST_PREV = -3,			///< previous element
+   PS_LIST_NEXT = -4			///< next element
+};
+
+/** Functions **************************************************************/
+/** \addtogroup DataGroup General Data Container Utilities  
+ *  \{
+ */
+
+/** Constructor */
+psList *psListAlloc(void *data)		///< initial data item; may be NULL
+;
+
+/**** List maintainence functions ****/
+
+/** Add to list */
+bool *psListAdd(psList *list,		///< list to add to (may be NULL)
+		int location,		///< index, PS_LIST_HEAD, or PS_LIST_TAIL
+		void *data)		///< data item to add
+;
+
+/** Remove from a list */
+bool *psListRemove(psList *list,	///< list to remove element from
+		   int location,	///< index of item, or PS_LIST_UNKNOWN, PS_LIST_NEXT, PS_LIST_PREV
+		   void *data)		///< data item to remove
+;
+
+/** Retrieve from a list */
+void *psListGet(const psList *list,	///< list to retrieve element from
+		int location)		///< index of item, or PS_LIST_NEXT, PS_LIST_PREV, PS_LIST_UNKNOWN
+;
+
+/** Convert doubly-linked list to an array of (void *) */
+psArray *psListToArray(psList *list)	///< List to convert
+;
+
+/** Convert array to a doubly-linked list */
+psList *psArrayToList(psArray *array) ///< array of (void *) to convert
+;
+
+/** Sort a list. */
+psList *psListSort(psList *list,	///< List to sort
+		   int (*compare)(const void **a, const void **b) // Function to compare two list elements
+    );
+
+/** Set the iterator */
+void psListSetIterator(psList *list,	///< list to retrieve element from
+		       int iterator,	///< the desired iterator
+		       int location)	///< index, PS_LIST_HEAD, or PS_LIST_TAIL
+;
+
+/** Get next element relative to iter */
+void *psListGetNext(psList *list,	///< list to retrieve element from
+		    int iterator)	///< the desired iterator
+;
+
+/** Get prev element relative to iter */
+void *psListGetPrevious(psList *list,	///< list to retrieve element from
+			int iterator)	///< the desired iterator
+;
+
+/* \} */ // End of DataGroup Functions
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psLogMsg.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psLogMsg.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psLogMsg.h	(revision 22322)
@@ -0,0 +1,44 @@
+#if !defined(PS_LOG_MSG_H)
+#define PS_LOG_MSG_H
+
+/** \file psLogMsg.h
+ *  \brief log messaging facilities
+ *  \ingroup SystemGroup
+ */
+
+#include <stdarg.h>
+
+/** Functions **************************************************************/
+/** \addtogroup SystemGroup System Utilities 
+ *  \{
+ */
+
+/// Sets the log destination 
+bool psLogSetDestination(const char *dest);	
+
+/// Sets the log level
+int psLogSetLevel(int level);		
+
+/// sets the log format
+void psLogSetFormat(const char *fmt);	
+
+/// Logs a message
+void psLogMsg(const char *name,		///< name of the log source
+	      int myLevel, 		///< severity level of this log message 
+	      const char *fmt, ...)	///< printf-style format command
+; 
+
+/// Logs a message from varargs
+void psLogMsgV(const char *name,	///< name of the log source
+	       int myLevel,		///< severity level of this log message 
+	       const char *fmt, 	///< printf-style format command
+	       va_list ap)		///< varargs argument list
+; 
+
+/* \} */ // End of SystemGroup Functions
+
+enum { PS_LOG_ABORT = 0, PS_LOG_ERROR, PS_LOG_WARN, PS_LOG_INFO }; ///< Status codes for log messages
+
+enum { PS_LOG_NONE = 0, PS_LOG_TO_STDERR = -1, PS_LOG_TO_STDOUT = -2 }; ///< Destinations for log messages
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMath.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMath.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMath.h	(revision 22322)
@@ -0,0 +1,143 @@
+#if !defined (PS_MATH_H)
+#define PS_MATH_H
+
+/** \file psMath.h
+ *  \brief math operators between PSLib data types
+ *  \ingroup MathGroup
+ */
+
+/** private structure used to pass constant values into the math operators. */
+typedef struct {
+    psType type;			///< data type information
+    union {			       
+        psS8   S8;                      ///< bye value entry
+        psS16 S16;                      ///< short int value entry
+        psU8   U8;                      ///< unsigned byte value entry
+        psU16 U16;                      ///< unsigned short int value entry
+        psF32 F32;                      ///< float value entry
+        psF64 F64;                      ///< double value entry
+        psC32 C32;                      ///< complex value entry
+        psC64 C64;                      ///< double complex value entry
+    } data;
+} p_psScalar;
+
+/** Functions **************************************************************/
+/** \addtogroup MathGroup Math Utilities
+ *  \{
+ */
+
+/** Scalar constructor */
+psScalar *psScalarAlloc(psC64 value,	///< Value to set
+			psElemType dataType ///< Data type
+    );
+
+/** Copy a scalar */
+psScalar *psScalarCopy(psScalar *value	///< Scalar to copy
+    );
+
+/** Perform a binary operation on two data items (psImage, psVector, psScalar). */
+psType *
+psBinaryOp (void *out,			///< destination (may be NULL) 
+	    void *in1,			///< first input
+	    char *op,			///< operator 
+	    void *in2)			///< second input
+;
+
+/** Perform a unary operation on two data items (psImage, psVector, psScalar). */
+psType *
+psUnaryOp (void *out,			///< destination (may be NULL) 
+	   void *in,			///< input
+	   char *op)			///< operator 
+;
+
+/* \} */ // End of MathGroup Functions
+
+/** the functions defined here can operate on the basic PSLib data types: psImage, psVector, XXX.  These data
+ *  types include embedded information about their type in the structure.  Constant values are handled by
+ *  wrapping the value in the psScalar function, which returns a private scalar data type, p_ps_Scalar, which
+ *  is always freed by the function.  The return value type can be determined by casting the return to psType
+ *  and checking the type entry.  
+ */
+
+/* examples of usage:
+
+psImage A, B, C;
+psVector X, Y;
+
+calculate C = sin(A^2)
+
+B = psBinaryOp (NULL, A, "^", psScalar(2));
+C = psUnaryOp (NULL, B, "sin");
+psFree (B);
+
+calculate Y = X^2
+
+Y = psBinaryOp (NULL, Y, "^", psScalar(2));
+
+Y = psBinaryOp (NULL, A, "^", X);
+
+Y = psBinaryOp (NULL, A, "^", psVectorTranspose(X));
+
+psFree (B);
+
+*/
+
+#endif
+
+/** allowed operators for the different image functions 
+
+    PS_BINARY_ADD        = "+", 
+    PS_BINARY_SUBTRACT   = "-", 
+    PS_BINARY_MULTIPLY   = "*", 
+    PS_BINARY_DIVIDE     = "/", 
+    PS_BINARY_POWER      = "^", 
+    PS_BINARY_MIN, 
+    PS_BINARY_MAX, 
+    PS_BINARY_LESSTHAN   = "<", 
+    PS_BINARY_MORETHAN   = ">", 
+    PS_BINARY_ISEQUAL    = "==", 
+    PS_BINARY_NOTEQUAL   = "!=", 
+    PS_BINARY_LESSOREQ   = "<=", 
+    PS_BINARY_MOREOREQ   = ">=", 
+    PS_BINARY_AND        = "&&", 
+    PS_BINARY_OR         = "||", 
+    PS_BINARY_BITAND     = "&", 
+    PS_BINARY_BITOR      = "|", 
+    PS_BINARY_XOR        = "~", 
+    PS_BINARY_ATAN2      = "atan2", 
+    PS_BINARY_MODULO     = "%"
+
+    -- all valid for complex types
+
+
+    PS_UNARY_IS     = "=", 
+    PS_UNARY_ABS,
+    PS_UNARY_INT,
+    PS_UNARY_EXP,
+    PS_UNARY_TEN,
+    PS_UNARY_LOG,
+    PS_UNARY_LN,
+    PS_UNARY_SQRT,
+    PS_UNARY_SIN,
+    PS_UNARY_COS,
+    PS_UNARY_TAN,
+    PS_UNARY_DSIN,
+    PS_UNARY_DCOS,
+    PS_UNARY_DTAN,
+    PS_UNARY_ASIN,
+    PS_UNARY_ACOS,
+    PS_UNARY_ATAN,
+    PS_UNARY_DASIN,
+    PS_UNARY_DACOS,
+    PS_UNARY_DATAN,
+    PS_UNARY_RND,
+    PS_UNARY_NOT,
+    PS_UNARY_NEGATE = "-",
+    PS_UNARY_INCR,
+    PS_UNARY_DECR,
+    PS_UNARY_ISINF,
+    PS_UNARY_ISNAN
+    PS_UNARY_MOD,   // specific to complex data types
+    PS_UNARY_CONJ,  // specific to complex data types
+    PS_UNARY_ARG,   // specific to complex data types
+**/
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMatrix.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMatrix.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMatrix.h	(revision 22322)
@@ -0,0 +1,80 @@
+#if !defined(PS_MATRIX_H)
+#define PS_MATRIX_H
+
+/** \file psMatrix.h
+ *  \brief matrix math operators
+ *  \ingroup MathGroup
+ */
+
+/** Functions **************************************************************/
+/** \addtogroup MathGroup Math Utilities
+ *  \{
+ */
+
+
+/* Linear Algebra */
+
+/** LU Decomposition of a matrix */
+psImage *
+psMatrixLUD(psImage *out,		///< Matrix to return, or NULL
+	    psVector *perm, 		///< Permutation vector
+	    const psImage *in)		///< Matrix to decompose
+;
+
+/** LU Solution.  Solves for and returns x in the equation Ax = b */
+psVector *
+psMatrixLUSolve(psVector *out,		///< Vector to return, or NULL
+		const psImage *LU,	///< LU-decomposed matrix
+		const psVector *RHS,	///< right-hand-side of the equation
+		const psVector *perm	///< Permutation vector
+;
+
+/** Invert matrix.  Not using restrict, to allow inversion to be done in-place */
+psImage *
+psMatrixInvert(psImage *out,		///< Matrix to return, or NULL
+	       const psImage *myMatrix, ///< Matrix to be inverted
+	       float *determinant)	///< Determinant to return, or NULL
+;
+
+/** Matrix determinant */
+float
+psMatrixDeterminant(const psImage *myMatrix) ///< Matrix to get determinant for
+;
+
+/** Matrix operation: addition, subtraction, multiplication */
+psImage *
+psMatrixMultiply(psImage *out,		///< Matrix to return, or NULL
+		 const psImage *in1,	///< Matrix 1
+		 const psImage *in2)	///< Matrix 2
+;
+
+/** Transpose Matrix */
+psImage *
+psMatrixTranspose(psImage *out,		///< Matrix to return, or NULL
+		  const psImage *in)	///< Matrix to transpose
+;
+
+/** Eigenvectors of a matrix */
+psVector *
+psMatrixEigenvectors(psImage *myMatrix)	///< Matrix to get eigenvectors for
+;
+
+/***********************************************************************************************************/
+
+/* Conversions */
+
+/** Convert matrix to vector.  Intended for a 1-d matrix. */
+psVector *
+psMatrixToVector(psVector *out,		///< Vector to return, or NULL
+		 const psImage *in)	///< Matrix to convert
+;
+
+/** Convert vector to matrix. */
+psImage *
+psVectorToMatrix(psImage *out,		///< Matrix to return, or NULL
+		 const psVector *in)	///< Vector to convert
+;
+
+/* \} */ // End of MathGroup Functions
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMemory.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMemory.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMemory.h	(revision 22322)
@@ -0,0 +1,163 @@
+#if !defined(PS_MEMORY_H)
+#define PS_MEMORY_H
+
+#include <stdio.h> 			// needed for FILE
+/** \file psMemory.h
+ *  \brief Memory alloc/free routines
+ *  \ingroup SystemGroup
+ */
+
+// Structures ***********************************************************
+
+/** A function that frees an allocated pointer */
+typedef void (*psFreeFcn)(void* ptr);
+
+/** Memory ID number */
+typedef unsigned long psMemoryId;
+
+/** Reference counter number */
+typedef unsigned long psReferenceCount;
+
+/** Book-keeping data for storage allocator.
+ *  N.b. sizeof(psMemBlock) must be chosen such that if ptr is a pointer
+ *  returned by malloc, then ((char *)ptr + sizeof(psMemBlock)) is properly
+ *  aligned for all storage types.
+ */
+typedef struct {
+    const void* startblock;             ///< initialised to p_psMEMMAGIC
+    struct psMemBlock* previousBlock;   ///< previous block in allocation list
+    struct psMemBlock* nextBlock;       ///< next block allocation list
+    psFreeFcn freeFcn;                  ///< deallocator.  If NULL, use generic deallocation.
+    size_t  userMemorySize;             ///< the size of the user-portion of the memory block
+    const psMemoryId id;                ///< a unique ID for this allocation
+    const char* file;                   ///< set from __FILE__ in e.g. p_psAlloc
+    const int lineno;                   ///< set from __LINE__ in e.g. p_psAlloc
+    pthread_mutex_t   refCounterMutex;  ///< mutex to ensure exclusive access to reference counter
+    psReferenceCount refCounter;        ///< how many times pointer is referenced
+    const void* endblock;               ///< initialised to p_psMEMMAGIC
+} psMemBlock;
+
+/// prototype of a basic callback used by memory functions 
+typedef psMemoryId (*psMemAllocateCallback)(const psMemBlock *ptr);
+
+/// prototype of memory free callback used by memory functions 
+typedef psMemoryId (*psMemFreeCallback)(const psMemBlock *ptr);
+
+/// prototype of a callback used in error conditions 
+typedef void (*psMemProblemCallback)(const psMemBlock *ptr, const char *file, int lineno);
+
+/// prototype of a callback used when memory runs out 
+typedef void *(*psMemExhaustedCallback)(size_t size);
+
+/** Functions **************************************************************/
+/** \addtogroup SystemGroup System Utilities
+ *  \{
+ */
+
+/** Set the fcn pointer to the appropriate ps*Free function.  If NULL, then a generic free functionality is
+ * used (i.e., the old psFree functionality).
+ */
+void p_psMemSetDeallocator(void* ptr, psFreeFcn func);
+
+/** retrieves the function pointer to the higher-level free function to be used.  If NULL, then the
+ * traditional psFree functionality shall be used.
+ */
+freeFcn p_psMemGetDeallocator(void* ptr);
+
+/// Memory allocation. Underlying private function called by macro psAlloc. 
+void *p_psAlloc(size_t size,		///< Size required
+		const char *file,	///< File of call
+		int lineno)		///< Line number of call
+;
+
+/// Memory re-allocation.  Underlying private function called by macro psRealloc. 
+void *p_psRealloc(void *ptr,		///< Pointer to re-allocate
+		  size_t size,		///< Size required
+		  const char *file,	///< File of call
+		  int lineno)		///< Line number of call
+;
+
+/// Free memory.  Underlying private function called by macro psFree. 
+void p_psFree(void *ptr,		///< Pointer to free
+	      const char *file,		///< File of call
+	      int lineno)		///< Line number of call
+;
+
+/// Check for memory leaks 
+int psMemCheckLeaks(psMemoryId id0,	///< don't list blocks with id < id0
+		    psMemBlock ***arr,	///< pointer to array of pointers to leaked blocks, or NULL
+		    FILE *fd)		///< print list of leaks to fd (or NULL)
+;
+
+/// Check for memory corruption 
+int psMemCheckCorruption(bool abort_on_error) ///< Abort on detecting corruption?
+;
+
+/// Return reference counter 
+psReferenceCount psMemGetRefCounter(void *vptr)	///< Pointer to get refCounter for
+;
+
+/// Increment reference counter and return the pointer 
+void *psMemIncrRefCounter(void *vptr)	///< Pointer to increment refCounter, and return
+;
+
+/// Decrement reference counter and return the pointer 
+void *psMemDecrRefCounter(void *vptr)	///< Pointer to decrement refCounter, and return
+;
+
+/// Set callback for problems 
+psMemProblemCallback psMemProblemCallbackSet(psMemProblemCallback func) ///< Function to run
+;
+
+/// Set callback for out-of-memory 
+psMemExhaustedCallback psMemExhaustedCallbackSet(psMemExhaustedCallback func) ///< Function to run
+;
+
+/// Set call back for when a particular memory block is allocated 
+psMemAllocateCallback psMemAllocateCallbackSet(psMemAllocateCallback func) ///< Function to run
+;
+
+/// Set call back for when a particular memory block is freed 
+psMemFreeCallback psMemFreeCallbackSet(psMemFreeCallback func) ///< Function to run
+;
+
+/** Default callback functions */
+psMemExhaustedCallback psMemExhaustedCallbackDefault;
+psMemProblemCallback psMemProblemCallbackDefault;
+psMemAllocateCallback psMemAllocateCallbackDefault;
+psMemFreeCallback psMemFreeCallbackDefault;
+
+/// get next memory ID 
+psMemoryId psMemGetId(void)
+;
+
+/// set p_psMemAllocateID to id 
+psMemoryId psMemAllocateCallbackSetID(psMemoryId id) ///< ID to set
+;
+
+/// set p_psMemFreeID to id 
+psMemoryId psMemFreeCallbackSetID(psMemoryId id) ///< ID to set
+;
+
+/* \} */ // End of SystemGroup Functions
+
+/// Memory allocation. psAlloc sends file and line number to p_psAlloc. 
+#define psAlloc(size) p_psAlloc(size, __FILE__, __LINE__)
+
+/// Memory re-allocation.  psRealloc sends file and line number to p_psRealloc. 
+#define psRealloc(ptr, size) p_psRealloc(ptr, size, __FILE__, __LINE__)
+
+/// Free memory.  psFree sends file and line number to p_psFree. 
+#define psFree(size) p_psFree(size, __FILE__, __LINE__)
+
+/*
+ * Ensure that any program using malloc/realloc/free will fail to compile
+ */
+#if !defined(PS_ALLOW_MALLOC)
+#  define malloc(S) for
+#  define realloc(P,S) for
+#  define calloc(S) for
+#  define free(P) for
+#endif
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMetadata.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMetadata.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMetadata.h	(revision 22322)
@@ -0,0 +1,141 @@
+#if !defined(PS_META_DATA_H)
+#define PS_META_DATA_H 1
+
+/** \file psMetadata.h
+ *  \brief Metadata handling structures and functions
+ *  \ingroup AstroGroup
+ */
+
+/** Possible types of metadata. */
+typedef enum {				///< type of val is:
+    PS_META_ITEM_SET = 0,		///< NULL; metadata is in psMetadataType.items
+    PS_META_S32,			///< int (.i)
+    PS_META_F32,			///< float (.f)
+    PS_META_F64,			///< double (.d)
+    PS_META_STR,			///< string (.v)
+    PS_META_IMG,			///< image (.v)
+    PS_META_JPEG,			///< JPEG (.v)
+    PS_META_PNG,			///< PNG (.v)
+    PS_META_ASTROM,			///< astrometric coefficients (.v)
+    PS_META_UNKNOWN,			///< other (.v)
+    PS_META_NTYPE			///< Number of types; must be last
+} psMetadataType;
+
+/** A struct to define a single item of metadata */
+typedef struct {
+    const int id;			///< unique ID for this item
+    const char *name;			///< Name of item
+    psMetadataType type;		///< type of this item
+    const union {
+	psS32 S32;			///< integer value
+	psF32 F32;			///< floating value
+	psF64 F64;			///< double value
+	void *void;			///< other type
+    } data;				///< value of metadata
+    char *comment;			///< optional comment ("", not NULL)
+    psDlist *items;			///< list of psMetadataItems with the same name
+} psMetadataItem;
+
+/** A set of metadata */
+typedef struct {
+    psDlist *list;			///< list of psMetadataItem
+    psHash *table;			///< hash table of the same metadata
+} psMetadata;
+
+/** Functions **************************************************************/
+/** \addtogroup AstroGroup Astronomy-Specific Utilities
+ *  \{
+ */
+
+/** Constructor */
+psMetadataItem *psMetadataItemAlloc(const char *name, ///< name of new item of metadata (sprintf format)
+				    psMetadataType type, ///< type of this piece of metadata
+				    const char *comment, ///< comment associated with item
+				    ...)	///< arguments for name and data
+;
+
+psMetadataItem *psMetadataItemAllocV(const char *name, ///< name of new item of metadata (sprintf format)
+				    psMetadataType type, ///< type of this piece of metadata
+				    const char *comment, ///< comment associated with item
+				    va_list ap)	///< arguments for name and data
+;
+
+/** Constructor */
+psMetadata *psMetadataAlloc(void)   ///< make a new set of metadata
+;
+
+/**** Utilities **********************************************************************/
+
+/// Add item to the end of the metadata
+bool *psMetadataAddItem(psMetadata *md, ///< metadata to add to
+			int location,	///< Where to add item
+			psMetadataItem *item) ///< Metatdata item to add
+;
+
+/// Add item to the end of the metadata.  Combines psMetadataItemAlloc and psMetadataAppendItem
+bool *psMetadataAdd(psMetadata *md,	///< Metadata to add to
+		    int location,	///< Where to add item
+		    const char *name,	///< name of new item of metadata (sprintf format)
+		    int format,		///< type of this piece of metadata + flags
+		    const char *comment, ///< comment associated with item
+		    ...)		///< possible arguments for name format
+;
+
+/// delete item from the metadata
+bool *psMetadataRemove(psMetadata *md,	///< metadata to delete from
+		       int location, 	///< Where to remove
+		       const char *key	///< Key to delete
+    );
+
+/// find the metadata with the specified key
+psMetadataItem *psMetadataLookup(const psMetadata *md, ///< metadata to look up
+				 const char *key ///< Key to find
+    );
+
+/// retrieve the metadata on the basis of entry position
+psMetadataItem *psMetadataGet(const psMetadata *md, ///< metadata to look up
+			      int location ///< entry position
+    );
+
+/// reset the iterator to the start of the list
+void psMetadataSetIterator(psMetadata *md, ///< metadata to set iterator for
+			   int iterator, ///< Which iterator to set
+			   int location	///< Where to set iterator
+			   );
+
+/// get the next item in the sequence
+psMetadataItem *psMetadataGetNext(psMetadata *md, ///< metadata to get from
+				  const char *match, ///< String to match
+				  int iterator ///< Which iterator to use
+    );
+
+/// get the next item in the sequence
+psMetadataItem *psMetadataGetPrevious(psMetadata *md, ///< metadata to get from
+				      const char *match, ///< String to match
+				      int iterator ///< Which iterator to use
+    );
+
+/// print metadata item to the specified stream
+void psMetadataItemPrint(FILE *fd,	///< file descriptor to write to
+			 const char *format, ///< Format of output
+			 const psMetadataItem *md ///< item of metadata to print
+    );
+
+/// Read only header from image file.
+psMetadata *
+psMetadataReadHeader(psMetadata *out,	///< read data to this structure
+		     const char *ext,	///< MEF extension name ("PHU" for primary header)
+		     int extnum,	///< MEF extension number (-1 for "PHU", 0 : Nextend - 1)
+		     const char *file)	///< file to read from
+;
+
+/// Read only header from image file descriptor.
+psMetadata *
+psMetadataFReadHeader(psMetadata *out,	///< read data to this structure
+		      const char *ext,	///< MEF extension name ("PHU" for primary header)
+		      int extnum,	///< MEF extension number (-1 for "PHU", 0 : Nextend - 1)
+		      FILE *f)		///< file descriptor to read from
+;
+
+/* \} */ // End of AstroGroup Functions
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMinimize.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMinimize.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMinimize.h	(revision 22322)
@@ -0,0 +1,101 @@
+#if !defined(PS_MINIMIZE_H)
+#define PS_MINIMIZE_H
+
+/** \file psMinimize.h
+ *  \brief minimization operations
+ *  \ingroup MathGroup
+ */
+
+/** Functions **************************************************************/
+/** \addtogroup MathGroup Math Utilities
+ *  \{
+ */
+
+
+/** Specification and return structure for a minimization */
+typedef struct {
+    const int maxIter;			///< Maximum number of iterations
+    const float tol;			///< Tolerance to reach
+    float value;			///< Value after minimization
+    int iter;				///< Actual number of iterations performed
+    float lastDelta;			///< Last change before quitting
+} psMinimization;
+
+
+psMinimization *psMinimizationAlloc(int maxIter, ///< Maximum number of iterations
+				    float tol ///< Tolerance to reach
+    );
+
+/** Function format for LM minimization: requires derivatives */
+typedef float (*psMinimizeLMFunc)(
+    psVector *deriv,			///< Derivatives to return
+    const psVector *params,		///< Parameters for which to evaluate
+    const psArray *coords		///< Array of vectors of coordinates at which to evaluate
+    );
+
+/** Minimize a function using the LM method: requires derivatives.  Returns false if there were errors. */
+bool
+psMinimizeLM(psMinimization *min,	///< Minimization specification, and output
+	     psMatrix *covar,		///< Covariance matrix to return
+	     psVector *params,		///< Best guess input parameters, and output
+	     const psVector *paramMask,	///< Parameter mask: 1 = fit for parameter, 0 = hold constant.
+	     const psArray *coords,	///< Coordinates at which to evaluate function
+	     psMinimizeLMFunc func	///< Function to minimize
+    );
+
+/** Pre-defined functions for LM minimization */
+psMinimizeLMFunc psMinimizeLMChi2Gauss1D;
+psMinimizeLMFunc psMinimizeLMChi2Gauss2D;
+
+/** Function format for Powell minimization: does not require derivatives */
+typedef float (*psMinimizePowellFunc)(
+    const psVector *params,		///< Parameters for which to evaluate
+    const psArray *coords		///< Array of vectors of coordinates at which to evaluate
+    );
+
+/** Minimize a function using Powell: doesn't require derivatives.  Returns false if there were errors. */
+bool
+psMinimizePowell(psMinimization *min,	///< Minimization specification, and output
+		 psVector *params,	///< Best guess input parameters, and output
+		 const psVector *paramMask, ///< Parameter mask: 1 = fit for parameter, 0 = hold constant.
+		 const psArray *coords, ///< Coordinates at which to evaluate function
+		 psMinimizePowellFunc func ///< Function to minimize
+    );
+
+/** Model function format for Powell Chi2 minimization */
+typedef psVector* (*psMinimizeChi2PowellFunc)(
+    const psVector *params,		///< Parameters for which to evaluate
+    const psArray *coords		///< Array of vectors of coordinates at which to evaluate
+    );
+
+/** Minimize Chi^2 for a data set given a model: using Powell method.  Returns false if there were errors. */
+bool
+psMinimizeChi2Powell(psMinimization *min,	///< Minimization specification, and output
+		     psVector *params,	///< Best guess input parameters, and output
+		     const psVector *paramMask, ///< Parameter mask: 1 = fit for parameter, 0 = hold constant.
+		     const psArray *coords, ///< Coordinates at which to evaluate function
+		     const psVector *value, ///< Measured values at coordinates
+		     const psVector *error, ///< Errors in measured values at coordinates
+		     psMinimizePowellChi2Func model ///< Model to fit
+    );
+
+/************************************************************************************************************/
+
+/** Derive a polynomial fit by chi^2 minimisation --- can be done analytically */
+psPolynomial1D *
+psVectorFitPolynomial1D(psPolynomial1D *myPoly, ///< Polynomial to fit
+			const psVector *x, ///< Ordinates (or NULL to just use the indices)
+			const psVector *y, ///< Coordinates
+			const psVector *yErr) ///< Errors in coordinates, or NULL
+;
+
+psSpline1D *
+psVectorFitSpline1D(psSpline1D *mySpline, ///< Spline to fit
+		    const psVector *x,	///< Ordinates (or NULL to just use the indices)
+		    const psVector *y,	///< Coordinates
+		    const psVector *yErr ///< Errors in coordinates, or NULL
+    );
+
+/* \} */ // End of MathGroup Functions
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMisc.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMisc.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psMisc.h	(revision 22322)
@@ -0,0 +1,47 @@
+#if !defined(PS_MISC_H)
+#define PS_MISC_H
+
+/** \file psMisc.h
+ *  \brief miscellaneous system utilities 
+ *  \ingroup SystemGroup
+ */
+
+/** Functions **************************************************************/
+/** \addtogroup SystemGroup System Utilities
+ *  \{
+ */
+
+/// Prints an error message and aborts
+void psAbort(const char *name,		///< Category of code that caused the abort
+	     const char *fmt,		///< Format
+	     ...			///< Extra arguments to use format
+	     );
+
+/// Allocates and returns a copy of a string
+char *psStringCopy(const char *str	///< string to copy
+    );
+
+/// Allocates nChar and returns a copy of the string or segment
+char *psStringNCopy(const char *str,	///< string to copy
+		    int nChar		///< Number of characters (including \0 )
+    );
+
+/* \} */ // End of SystemGroup Functions
+/*
+ * Concatenate two macro arguments
+ */
+#define P_PS_CONCAT(A, B) A ## B	      //!< Expands to AB
+#define PS_CONCAT(A, B) A ## B                //!< Also Expands to AB
+#define PS_CONCAT2(A, B) PS_CONCAT(A, B)      //!< Also expands to AB
+#define PS_CONCAT3(A, B, C) A ## B ## C	      //!< Expands to ABC
+#define PS_CONCAT4(A, B, C, D) A ## B ## C ## D	//!< Expands to ABCD
+  
+#define PS_STRING(S) #S			///< converts argument to string
+
+/// Magic values for errors
+enum {
+    PS_NO_VALUE = -111,			///< Corresponding value not yet measured
+    PS_NO_ERROR = -222			///< Corresponding value has no error
+};
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psObject.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psObject.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psObject.h	(revision 22322)
@@ -0,0 +1,180 @@
+#if !defined(PS_OBJECT_H)
+#define PS_OBJECT_H
+
+/** \file psObject.h
+ *  \brief Definition and manipulation of astronomical objects 
+ *  \ingroup AstroGroup
+ */
+
+/***********************************************************************************************************/
+
+/** Object definition, to handle both objects we detect, and catalogues */
+typedef struct {
+    psCoord *cell;			///< Centre position on Cell, with associated error
+    psCoord *sky;			///< Position on the sky, with associated error
+    float mag, magErr;			///< Magnitude and associated error
+    float isoMag, isoMagErr;		///< Isophotal magnitude and associated error
+    float fwhm, fwhmErr;		///< FWHM and associated error
+    float ellipA, ellipB;		///< Elliptical semi-major (A) and semi-minor (B) axes
+    float ellipAngle;			///< Ellipse position angle
+    float sky, skyErr;			///< Local sky level, and associated error
+    float colour, colourErr;		///< Colour and associated error, if known
+    psPhotSysrem colorPlus, colorMinus;	///< Colour reference
+    psBitset *quality;			///< Bit mask for quality information
+    psCell *parentCell;			///< Cell this measurement came from
+} psObject;
+
+/** Constructor */
+psObject *
+psObjectAlloc(void);
+
+/** Destructor */
+void
+psObjectFree(psObject *restrict myObject);
+
+/***********************************************************************************************************/
+
+/** An assembly of objects */
+typedef struct {
+    psType type;			///< Type of data.  THIS STRUCT ELEMENT MUST BE FIRST IN THE STRUCT!
+    int size;				///< Total number of elements available
+    int n;				///< Number of elements in use
+    psObject *arr;			///< The array data
+} psObjectArray;
+
+/** Constructor */
+psObjectArray *psObjectArrayAlloc(int s, ///< Total number of elements to make available
+				  int n	///< Number of elements that will be used
+    );
+/** Reallocator */
+psObjectArray *psObjectArrayRealloc(psObjectArray *myArray, ///< Array to reallocate
+				    int s ///< Total number of elements to make available
+    );
+/** Destructor */
+void psObjectArrayFree(psObjectArray *restrict myArray ///< Array to free
+    );
+
+/***********************************************************************************************************/
+
+/** Associates objects on an image with the image */
+typedef struct {
+    const struct psImage *image;	///< Image that produced the objects, which has metadata we need
+    psObjectArray *objects;		///< The objects
+} psImageObjects;
+
+/** Constructor */
+psImageObjects *
+psImageObjectsAlloc(const struct psImage *image, ///< Image that produced the objects, with needed metadata
+		    psObjectArray *objects ///< The objects
+    );
+
+/** Destructor */
+void
+psImageObjectsFree(psImageObjects *restrict myImageObjects ///< Object to destroy
+		  );
+
+/***********************************************************************************************************/
+
+/** Objects from a catalogue */
+typedef struct {
+    psCatalogue catalogue;		///< Source catalogue from which objects come
+    psObjectArray *object;		///< The objects
+} psCatalogueObjects;
+
+/** Constructor */
+psCatalogueObjects *
+psCatalogueObjectsAlloc(enum psCatalogue *catalogue, ///< Source catalogue
+			psObjectArray *objects ///< The objects
+    );
+
+/** Destructor */
+void
+psCatalogueObjectsFree(psCatalogueObjects *restrict myCatalogueObjects ///< Object to destroy
+		      );
+
+/***********************************************************************************************************/
+
+/** A "super" object --- an object with multiple detections in different images */
+typedef struct {
+    const struct  psImageArray *images;	///< Images that provided the different measurements
+    const psObjectArray *objects;	///< Individual object measurements
+
+    /* Derived quantities */
+    psSkyPos *meanSkyPos;		///< Mean position on the sky
+    double pmRA, pmDec;			///< Proper motion in RA and Dec
+    double pmRAErr, pmDecErr;		///< Errors in proper motion
+    float posChi2;			///< chi^2 for position
+} psSuperObject;
+
+/** Constructor */
+psSuperObject *
+psSuperObjectAlloc(const struct psImageArray *images, ///< The images with the measurements
+		   const psObjectArray *objects ///< Object measurements
+    );
+
+/** Destructor */
+void
+psSuperObjectFree(psSuperObject *restrict mySuperObject ///< Object to destroy
+		 );
+
+
+/***********************************************************************************************************/
+
+/* Correlation */
+
+
+/** Correlate two OTA object arrays.
+ * Returns an array with the indices of the object in the second bunch that matched the corresponding object
+ * in the first bunch, or -1 if none that match.
+ */
+psIntArray *
+psCorrelateObjects(const psObjectArray *restrict myObjects1, ///< First bunch of objects
+		   const psObjectArray *restrict myObjects2, ///< Second bunch of objects
+		   const psOTADescription *myOTA1, ///< OTA description for first bunch of objects, or NULL for
+						   ///< sky
+		   const psOTADescription *myOTA2 ///< OTA description for second bunch of objects, or NULL for
+						  ///< sky
+		   );
+
+
+/** Get matched lists from a correlated index array Returns a status number, and matched1 and matched2
+ * are manipulated to contain matches
+ */
+int
+psGetCorrelatedMatches(const psIntArray matches, ///< Index array specifying matches */
+		       const psObjectArray *restrict myObjects1, ///< First bunch of objects
+		       const psObjectArray *restrict myObjects2, ///< Second bunch of objects
+		       psObjectArray *matched1, ///< Matched objects in first bunch
+		       psObjectArray *matched2 ///< Matched objects in second bunch
+		       );
+
+/***********************************************************************************************************/
+
+/* Object selection */
+
+/** Get objects within a particular magnitude range */
+psObjectArray *
+psSelectObjectMag(psObjectArray *restrict myArray, ///< Bunch of objects to select from
+		  float magLower,	///< Lower bound for magnitude
+		  float magUpper	///< Upper bound for magnitude
+		  );
+
+/** Get objects within a radius on the sky */
+psObjectArray *
+psSelectObjectSkyDist(psObjectArray *restrict myArray, ///< Bunch of objects to select from
+		      psSkyPos *skyPos,	///< Position on the sky
+		      float radius,	///< Radius of search
+		      int circleOrSquare ///< Circle = 1, Square = 2
+		      );
+
+/** Get objects within a particular colour range */
+psObjectArray *
+psSelectObjectColour(psObjectArray *restrict myArray, ///< Bunch of objects to select from
+		     float magLower,	///< Lower bound for colour
+		     float magUpper,	///< Upper bound for colour
+		     psColourRef colourRef ///< Only select those with this colour reference
+		     );
+
+
+#endif
+
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psPhotom.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psPhotom.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psPhotom.h	(revision 22322)
@@ -0,0 +1,50 @@
+#if !defined(PS_PHOTOM_H)
+#define PS_PHOTOM_H
+
+/** \file psPhotom.h
+ *  \brief Photometric transformations
+ *  \ingroup AstroGroup
+ */
+
+/** photometry system transformation types */
+typedef enum {
+    PS_PHOT_SYS,
+    PS_PHOT_REF
+} psPhotTransformType;
+
+/** photometry system definition */
+typedef struct {
+    const int ID;			///< ID number for this photometric system
+    const char *name;			///< Name of photometric system
+    const char *camera;			///< Camera for photometric system
+    const char *filter;			///< Filter used for photometric system
+    const char *detector;		///< Detector used for photometric system
+} psPhotSystem;
+
+/** photometry transformations */
+typedef struct {
+    psPhotSystem src;			///< Source photometric system
+    psPhotSystem dst;			///< Destination photometric system
+    psPhotSystem pP, pM;		///< Primary colour reference
+    psPhotSystem sP, sM;		///< Secondary colour reference
+    float pA, sA;			///< Colour offset for primary and secondary references
+    psPolynomial3D transform;		///< Transformation from source to destination
+} psPhotTransform;
+
+# endif
+
+/**** examples & explanations 
+ *
+ * examples of psPhotSystem values:
+ * data from Megacam, g' filter, CCD 01:
+ *
+ * magnitude conversions
+ * instrumental mag -> rough mag -> system mag -> reference mag
+ *  
+ * Mruf = -2.5*log(DN) + 2.5*log(exptime) + PS_ZERO_POINT
+ * Minst = Mruf - 2.5*log(exptime) - PS_ZERO_POINT
+ * Msys = Mruf + C_w + K_w (airmass - 1) + SUM (X_{w,i} (color - color_ref)^i)
+ * Mref = Msys + C_w + SUM (X_{w,i} (color - color_ref)^i)
+ *  
+ */
+
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psPosition.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psPosition.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psPosition.h	(revision 22322)
@@ -0,0 +1,219 @@
+#if !defined(PS_POSITION_H)
+#define PS_POSITION_H
+
+/** \file psPosition.h
+ *  \brief Definitions of objects in 2-D space 
+ *  \ingroup AstroGroup
+ */
+
+/** Structures *********************/
+
+/** projection types */
+typedef enum {                          ///< type of val is:
+    PS_PROJ_TAN,                        ///< Tangent projection
+    PS_PROJ_SIN,                        ///< Sine projection
+    PS_PROJ_AIT,                        ///< Aitoff projection
+    PS_PROJ_PAR,                        ///< Par projection
+    PS_PROJ_NTYPE                       ///< Number of types; must be last
+} psProjectionType;
+
+/** A point in 2-D space, with errors. */
+typedef struct {
+    double x;				///< x position
+    double y;				///< y position
+    double xErr;			///< Error in x position
+    double yErr;			///< Error in y position
+} psPlane;
+
+/** A point on the surface of a sphere, with errors */
+typedef struct {
+    double r;				///< RA
+    double d;				///< Dec
+    double rErr;			///< Error in RA
+    double dErr;			///< Error in Dec
+} psSphere;
+
+/** A polynomial transformation between coordinate frames.  This may be a linear relationship, or may
+ *  represent a higher-order transformation.
+ */
+typedef struct {
+    psDPolynomial2D *x;			///< Transformation for x
+    psDPolynomial2D *y;			///< Transformation for y
+} psPlaneTransform;
+
+/** The optical distortion terms.  The lowest two terms are the x and y axis of the target system.  The higher
+ *  two terms represent magnitude and color terms.
+ */
+typedef struct {
+    psDPolynomial4D *x;			///< Transformation for x
+    psDPolynomial4D *y;			///< Transformation for y
+} psPlaneDistort;
+
+/** General spherical transformation */
+typedef struct {
+  double sinNPlon;                       ///< sin of North Pole longitude
+  double cosNPlon;                       ///< cos of North Pole longitude
+  double sinNPlat;                       ///< sin of North Pole lattitude
+  double cosNPlat;                       ///< cos of North Pole lattitude
+  double sinZP;	                         ///< sin of First PT of Ares lon
+  double cosZP;	                         ///< cos of First PT of Ares lon
+} psSphereTransform;
+
+/** Spherical <-> Linear projections */
+typedef struct {
+    double R, D;                         ///< coordinates of projection center
+    double Xs, Ys;                       ///< plate-scale in X and Y directions
+    psProjectionType type;               ///< projection type
+} psProjection;
+
+typedef enum {
+    PS_SPHERICAL;			///< Offset on a sphere
+    PS_LINEAR;				///< Linear offset
+} psSphereOffsetMode;
+
+typedef enum {
+    PS_ARCSEC;				///< Arcseconds
+    PS_ARCMIN;				///< Arcminutes
+    PS_DEGREE;				///< Degrees
+    PS_RADIAN;				///< Radians
+} psSphereOffsetUnit;
+
+/** Functions **************************************************************/
+/** \addtogroup AstroGroup Astronomy-Specific Utilities
+ *  \{
+ */
+
+/** apply the coordinate transformation to the given coordinate */
+psPlane *psPlaneTransformApply (psPlane *out, ///< Output coordinates, or NULL
+				const psPlaneTransform *transform, ///< coordinate transformation
+				const psPlane *coords) ///< input coordiate
+;
+
+/** apply the optical distortion to the given coordinate, magnitude, color */
+psPlane *psPlaneDistortApply (psPlane *out, ///< Output coordinates, or NULL
+			      const psPlaneDistort *distort, ///< optical distortion pattern
+			      const psPlane *coords, ///< input coordinate
+			      float mag, ///< magnitude of object
+			      float color) ///< color of object
+;
+
+/* Celestial coordinate conversions */
+
+/** Constructor */
+psSphereTransform *
+psSphereTransformAlloc(double NPlon, ///< Longitude in input system of north pole
+		       double NPlat, ///< Latitude in input system of north pole
+		       double ZP) ///< Zero point (e.g, the first point of Ares)
+;
+
+/** Apply general spherical transformation */
+psSphere *
+psSphereTransformApply(psSphere *out,	///< Position to output, or NULL
+		       const psSphereTransform *transform ///< transformation to use
+		       const psSphere *coord) ///< Coordinates to convert
+;
+
+/** Return transformation structure to convert ICRS to Ecliptic */
+psSphereTransform *psSphereTransformICRSToEcliptic(void);
+
+/** Return transformation structure to convert Ecliptic to ICRS */
+psSphereTransform *psSphereTransformEclipticToICRS(void);
+
+/** Return transformation structure to convert ICRS to Galactic */
+psSphereTransform *psSphereTransformICRSToGalactic(void);
+
+/** Return transformation structure to convert Galactic to ICRS */
+psSphereTransform *psSphereTransformGalacticToICRS(void);
+
+/***********************************************************************************************************/
+
+
+/** Project spherical system onto a plane */
+psPlane *
+psProject(const psSphere *coord, ///< Spherical coordinates to project
+	  const psProjection *projection) ///< Projection to use
+;
+
+/** Deproject plane onto spherical system */
+psSphere *
+psDeproject(const psPlane *coord, ///< Plane coordinates to deproject
+	    const psProjection *projection) ///< Projection to use
+;
+
+/** Get offset (RA,Dec) on the sky between two positions position1 and position2 may not be identical */
+psSphere *
+psSphereGetOffset(const psSphere *position1, ///< Position 1
+		  const psSphere *position2, ///< Position 2
+		  psSphereOffsetMode mode, ///< Mode of offset
+		  psSphereOffsetUnit unit ///< Units for offset
+;
+
+/** Apply an offset to a position */
+psSphere *
+psSphereSetOffset(const psSphere *position, ///< Position
+		  const psSphere *offset, ///< Offset
+		  psSphereOffsetMode mode, ///< Mode of offset
+		  psSphereOffsetUnit unit ///< Units of offset
+    );
+
+/************************************************************************************************************/
+
+/* Positions of well-known objects */
+
+/** These are not up to date: we don't need them immediately */
+
+/** Get Sun Position */
+psSphere *
+psSunGetPos(psTime *time)		///< Time for which to get position
+;
+
+/** Get Sun Rise time */
+psSphere *
+psSunGetRise(psTime *twi15,		///< corresponding 15 deg twilight
+	     psTime *twi18,		///< corresponding 18 deg twilight
+	     const psTime *time)	///< get rise closest to this time
+;
+
+/** Get Sun Set time */
+psSphere *
+psSunGetSet(psTime *twi15,		///< corresponding 15 deg twilight
+	    psTime *twi18,		///< corresponding 18 deg twilight
+	    const psTime time)		///< get set closest to this time
+;
+
+/** Get Length of closest night in hours */
+float
+psNightLength(const psTime *time)	///< Time that specifies the night of interest
+;
+
+/** Get Moon Position */
+psSphere *
+psMoonGetPos(const psTime *time)	///< Time for which to get position
+;
+
+/** Get Moon Rise time and return position */
+psSphere *
+psMoonGetRise(psTime *time)		///< get rise closest to this time
+;
+
+/** Get Moon Set time and return position */
+psSphere *
+psMoonGetSet(psTime *time)		///< get set closest to this time, and output
+;
+
+/** Get Moon phase */
+float
+psMoonGetPhase(const psTime *time)	///< Get phase for this time
+;
+
+/** Get Planet positions */
+psSphere *
+psPlanetGetPos(const char *solarSystemObject, ///< Named S.S. object
+	       const psTime *time)	///< Get position for this time
+;
+
+/***********************************************************************************************************/
+
+/* \} */ // End of AstroGroup Functions
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psSort.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psSort.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psSort.h	(revision 22322)
@@ -0,0 +1,28 @@
+#if !defined(PS_SORT_H)
+#define PS_SORT_H
+
+/** \file psSort.h
+ *  \brief general sorting operations
+ *  \ingroup MathGroup
+ */
+
+/** Functions **************************************************************/
+/** \addtogroup MathGroup Math Utilities
+ *  \{
+ */
+
+/** Sort an array. Inputs not restrict-ed to allow sort in place */
+psVector *
+psSort(psVector *out,			///< Sorted array to return. May be NULL
+       const psVector *in)		///< Array to sort
+;
+
+/** Sort an array, along with some other stuff.  Returns an index array */
+psVector *
+psSortIndex(psVector *out,		///< Output index array (may be NULL)
+	    const psVector *in)		///< Array to sort
+;
+
+/* \} */ // End of MathGroup Functions
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psStats.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psStats.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psStats.h	(revision 22322)
@@ -0,0 +1,99 @@
+#if !defined(PS_STATS_H)
+#define PS_STATS_H
+
+/** \file psStats.h
+ *  \brief basic statistical operations
+ *  \ingroup MathGroup
+ */
+
+/** statistics which may be calculated */
+typedef enum {
+    PS_STAT_SAMPLE_MEAN           = 0x000001,
+    PS_STAT_SAMPLE_MEDIAN         = 0x000002,
+    PS_STAT_SAMPLE_STDEV          = 0x000004,
+    PS_STAT_SAMPLE_QUARTILE       = 0x000008, 
+    PS_STAT_ROBUST_MEAN           = 0x000010,
+    PS_STAT_ROBUST_MEDIAN         = 0x000020,
+    PS_STAT_ROBUST_MODE           = 0x000040,
+    PS_STAT_ROBUST_STDEV          = 0x000080,
+    PS_STAT_ROBUST_QUARTILE       = 0x000100, 
+    PS_STAT_CLIPPED_MEAN          = 0x000200,
+    PS_STAT_CLIPPED_STDEV         = 0x000400,
+    PS_STAT_MAX                   = 0x000800,     
+    PS_STAT_MIN                   = 0x001000,
+    PS_STAT_USE_RANGE             = 0x002000,
+    PS_STAT_USE_BINSIZE           = 0x004000,
+} psStatsOptions;
+
+/** generic statistics structure */
+typedef struct {
+    double sampleMean;                  ///< formal mean of sample
+    double sampleMedian;                ///< formal median of sample
+    double sampleStdev;                 ///< standard deviation of sample
+    double sampleUQ;                    ///< upper quartile of sample
+    double sampleLQ;                    ///< lower quartile of sample
+    double robustMean;                  ///< robust mean of data
+    double robustMedian;                ///< robust median of data
+    double robustMode;                  ///< Robust mode of data
+    double robustStdev;                 ///< robust standard deviation of data
+    double robustUQ;                    ///< robust upper quartile
+    double robustLQ;                    ///< robust lower quartile
+    int    robustN50;                   ///< Number of points UQ-LQ
+    int    robustNfit;                  ///< Number of points in Gauss. fit
+    double clippedMean;                 ///< Nsigma clipped mean
+    double clippedStdev;                ///< standard deviation after clipping
+    int    clippedNvalues;              ///< number of data points used for clipped mean
+    double clipSigma;                   ///< Nsigma used for clipping; user input
+    int    clipIter;                    ///< Number of clipping iterations; user input
+    double min;                         ///< minimum data value in data; input range
+    double max;                         ///< maximum data value in data; input range
+    double binsize;		        ///< binsize for robust fit (input/output)
+    psStatsOptions options;             ///< bitmask of calculated values
+} psStats;
+
+
+/** Do Statistics on a vector.  Returns a status value. \ingroup MathGroup */
+psStats *
+psVectorStats(psStats *stats,		///< stats structure defines stats to be calculated, how and result
+	      const psVector *in,	///< Array to be analysed
+	      const psVector *mask,	///< Ignore elements where (maskArray & maskVal) != 0.  May be NULL
+	      unsigned int maskVal)	///< Only mask elements with one of these bits set in maskArray
+;
+
+/** Constructor */
+psStats *
+psStatsAlloc(psStatsOptions options)	///< Statistics to measure
+;
+
+/***********************************************************************************************************/
+
+/** Histograms  */
+typedef struct {
+    const psVector *bounds;		///< Bounds for the bins
+    psVector *nums;			///< Number in each of the bins
+    int minNum, maxNum;			///< Number below the minimum and above the maximum
+    bool uniform;			///< Is it a uniform distribution?
+} psHistogram;
+
+/** Constructor \ingroup MathGroup */
+psHistogram *
+psHistogramAlloc(float lower,		///< Lower limit for the bins
+		 float upper,		///< Upper limit for the bins
+		 int n)			///< Number of the bins
+;
+
+/** Generic constructor \ingroup MathGroup */
+psHistogram *
+psHistogramAllocGeneric(const psVector *bounds) ///< Bounds for the bins
+;
+
+/** Calculate a histogram \ingroup MathGroup **/
+psHistogram *
+psVectorHistogram(psHistogram *out, 	///< Input and output histogram
+		  const psVector *in	///< Array to analyse
+		  const psVector *mask,	///< Ignore elements where (maskArray & maskVal) != 0.  May be NULL
+		  unsigned int maskVal	///< Only mask elements with one of these bits set in maskArray
+    );
+
+#endif
+
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psTime.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psTime.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psTime.h	(revision 22322)
@@ -0,0 +1,113 @@
+#if !defined (PS_TIME_H)
+#define PS_TIME_H
+
+/** Defines time type */
+typedef enum {
+    PS_TIME_TAI,                    ///< seconds since 1970-01-01T00:00:00Z (Gregorian)
+    PS_TIME_UTC,                    ///< seconds since 1970-01-01T00:00:00Z (Gregorian)
+} psTimeType;
+
+/** The time */
+typedef struct {
+    psS64            sec;           ///< seconds, negative values represent dates before 1970
+    psU32            usec;          ///< microseconds
+    psTimeType       type;          ///< type of time
+} psTime;
+
+
+/** Get the current system time */
+psTime *psTimeGetTime(psTimeType type	///< Type of time
+		      );
+
+/** Convert between time types */
+psTime *psTimeConvert(psTime *time,	///< Time to convert, and output
+		      psTimeType type	///< Type to convert to
+    );
+
+/** Convert to local sidereal time */
+psTime *psTimeToLST(psTime *time,	///< Time to convert, and output
+		    double longitude	///< Latitude for LST
+    );
+
+/** Convert from local sidereal time */
+psTime *psTimeFromLST(psTime *time,	///< Time to convert, and output
+		      double longitude	///< Latitude for LST
+    );
+
+/** Get UT1 - UTC */
+double psTimeGetUT1Delta(const psTime *time	///< Time for which to return UT1-UTC difference
+    );
+
+/** Return pole coordinates */
+psSphere *psTimeGetPoleCoords(const psTime *time ///< Time for which to return pole coordinates
+    );
+
+/** Return Julian Date */
+psF64 psTimeToJD(const psTime *time);
+
+/** Return Modified Julian Date */
+psF64 psTimeToMJD(const psTime *time);
+
+/** Return ISO time string */
+char *psTimeToISOTime(const psTime *time);
+
+/** Return a timeval structure */
+timeval *psTimeToTimeval(const psTime *time);
+
+/** Convert Julian Date to psTime */
+psTime *psTimeFromJD(psF64 input);
+
+/** Convert Modified Julian Date to psTime */
+psTime *psTimeFromMJD(psF64 input);
+
+/** Convert ISO time string to psTime */
+psTime *psTimeFromISO(const char *input);
+
+/** Convert a timeval structure to psTime */
+psTime *psTimeFromTimeval(const timeval *input);
+
+/** Add two times */
+psTime *psTimeAdd(const psTime *tai1,	///< Time to which to add
+		  psF64 delta		///< Number of TAI seconds to add
+    );
+
+/** Subtract two times, TAI seconds */
+psF64 psTimeSub(const psTime *time1, const psTime *time2);
+
+/** Get absolute time difference between two times */
+psF64 psTimeDelta(const psTime *time1, const psTime *time2);
+
+// A table of UT1-UTC and polar coordinates
+typedef struct {
+    const char *filename;		// Filename of time table
+    const psF64 validFrom, validTo;	// MJDs of validity of time table
+    bool loaded;			// Has the table been loaded?
+    psVector *mjd;			// MJD data, type is psF64
+    psVector *dut;			// delta UT = UT1 - UTC data
+    psVector *xp, *yp;			// Polar motion data
+} psTimeTable;
+
+// Constructor --- don't load
+psTimeTable *psTimeTableAlloc(const char *filename, // Filename of time table
+			      psF64 validFrom,	// The time table is valid from this MJD
+			      psF64 validTo // The time table is valid to this MJD
+			      );
+
+// Load a time table, return true for success
+bool psTimeTableLoad(psTimeTable *table	// Time table to load
+		     );
+
+// Interpolate on a table, return true if given MJD is within the given range
+bool psTimeTableInterpolate(const psTimeTable *table, // Table to interpolate on
+			    double *xp,	// Output x_p, or NULL
+			    double *yp,	// Output y_p, or NULL
+			    double *dut,// Output UT1-UTC, or NULL
+			    psF64 mjd	// MJD at which to interpolate
+			    );
+
+// Time tables
+static psArray *timeTables;
+// Time configuration
+static psMetadata *timeConfig;
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psTrace.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psTrace.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psTrace.h	(revision 22322)
@@ -0,0 +1,52 @@
+#if !defined(PS_TRACE_H)
+#define PS_TRACE_H 1
+
+/** \file psTrace.h
+ *  \brief basic run-time trace facilities
+ *  \ingroup SystemGroup
+ */
+
+/** Functions **************************************************************/
+/** \addtogroup SystemGroup System Utilities 
+ *  \{
+ */
+
+/// Send a trace message
+void p_psTrace(const char *facil, 	///< facilty of interest
+	       int myLevel,		///< desired trace level
+	       ...)			///< trace message arguments 
+;
+
+/// Set trace level 
+int psTraceSetLevel(const char *facil,	///< facilty of interest
+		    int level)		///< desired trace level
+;
+
+/// Get the trace level 
+int psTraceGetLevel(const char *facil)	///< facilty of interest
+;
+
+/// turn off all tracing, and free trace's allocated memory 
+void psTraceReset(void)
+;
+
+/// print trace levels 
+void psTracePrintLevels(void)
+;
+
+/** Set destination for tracing. */
+void psTraceSetDestination(FILE *fp	// Open file pointer to write to
+    );
+
+/* \} */ // End of SystemGroup Functions
+
+//#define PS_NO_TRACE 1			///< to turn off all tracing
+
+#if defined(PS_NO_TRACE)
+#  define psTrace(facil, level, ...)	/* do nothing */
+#else
+#  define psTrace(facil, level, ...) \
+          p_psTrace(facil, level, __VA_ARGS__)
+#endif
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psType.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psType.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psType.h	(revision 22322)
@@ -0,0 +1,62 @@
+#if !defined(PS_TYPES_H)
+#define PS_TYPES_H
+
+/** \file psTypes.h
+ *  \brief Support for different types
+ *  \ingroup DataGroup
+ */
+
+/** Types of the elements of vectors, images, etc. */
+typedef enum {
+    PS_TYPE_S8,				///< Character
+    PS_TYPE_S16,			///< Short integer
+    PS_TYPE_S32,			///< Integer
+    PS_TYPE_S64,			///< Long integer
+    PS_TYPE_U8,				///< Unsigned character
+    PS_TYPE_U16,			///< Unsigned short integer
+    PS_TYPE_U32,			///< Unsigned integer
+    PS_TYPE_U64,			///< Unsigned long integer
+    PS_TYPE_F32,			///< Floating point
+    PS_TYPE_F64,			///< Double-precision floating point
+    PS_TYPE_C32,			///< Complex numbers consisting of floating point
+    PS_TYPE_C64,                        ///< Complex numbers consisting of doubles
+    PS_TYPE_OTHER			///< Something else that's not supported for arithmetic
+} psElemType;
+
+/** Dimensions of a data type */
+typedef enum {
+    PS_DIMEN_SCALAR,			///< Scalar
+    PS_DIMEN_VECTOR,			///< A vector
+    PS_DIMEN_TRANSV,			///< A transposed vector
+    PS_DIMEN_IMAGE,			///< An image
+    PS_DIMEN_OTHER			///< Something else that's not supported for arithmetic
+} psDimen;
+
+/** The type of a data type */
+typedef struct {
+    psDimen dimen;			///< The dimensionality
+    psElemType type;			///< The type
+} psType;
+
+/************************************************************************************************************/
+
+/**
+ * ps- typedefs. These should NOT be used generally, but only where the number of bits in a type must be
+ * guaranteed, or for use in macros to preserve the standard naming conventions.
+ */
+typedef float          psF32;		///< BITPIX -32 (float)
+typedef double         psF64;		///< BITPIX -64 (double)
+typedef complex float  psC32;
+typedef complex double psC64;
+
+typedef int8_t         psS8;
+typedef int16_t        psS16;
+typedef int32_t        psS32;
+typedef int64_t        psS64;
+
+typedef uint8_t        psU8;		///< equivalent to BITPIX 8   
+typedef uint16_t       psU16;		///< equivalent to BITPIX 16  
+typedef uint32_t       psU32;		///< equivalent to BITPIX 32  
+typedef uint64_t       psU64;		
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/include/psVector.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/include/psVector.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/include/psVector.h	(revision 22322)
@@ -0,0 +1,51 @@
+# ifndef PS_VECTOR_H
+# define PS_VECTOR_H
+
+/** \file psVector.h
+ *  \brief Basic vector definitions and operations.
+ *  \ingroup DataGroup
+ */
+
+/// basic vector data structure.
+typedef struct {
+    psType type; 			///< vector data type and dimension
+    const int n;			///< size of vector 
+    const int nalloc;			///< allocated data block
+    union {
+        psS8  *S8;                      ///< Pointers to byte data
+        psS16 *S16;                     ///< Pointers to short-integer data
+        psS32 *S32;                     ///< Pointers to integer data
+        psS64 *S64;                     ///< Pointers to long-integer data
+        psU8  *U18;                     ///< Pointers to unsigned-byte data
+        psU16 *U16;                     ///< Pointers to unsigned-short-integer data
+        psU32 *U32;                     ///< Pointers to unsigned-integer data
+        psU64 *U64;                     ///< Pointers to unsigned-long-integer data
+        psF32 *F32;                     ///< Pointers to floating-point data
+        psF64 *F64;                     ///< Pointers to double-precision data
+        psC32 *C32;                     ///< Pointers to complex floating-point data
+        psC64 *C64;                     ///< Pointers to complex floating-point data
+    } data;
+} psVector;
+
+/** Functions **************************************************************/
+/** \addtogroup DataGroup Astronomy-Specific Utilities
+ *  \{
+ */
+
+/*** Vector structure manipulation ***/
+
+/// Create a vector of the specified size and type.
+psVector *
+psVectorAlloc(int nalloc,		///< vector length 
+	      psElemType type)		///< vector data type 
+;
+
+/// Extend a vector 
+psVector *
+psVectorRealloc(const psVector *vector, ///< vector to reallocate
+		int nalloc)		///< required length 
+;
+
+/* \} */ // End of DataGroup Functions
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/lib/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/lib/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/lib/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+libUtils.dylib
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/lib/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/lib/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/lib/Makefile	(revision 22322)
@@ -0,0 +1,17 @@
+all :
+	@: no default actions
+
+install :
+	cp Makefile *.a $(PSLIB_DIR)/lib
+	@if [ X$(RANLIB) != X ]; then\
+		for f in *.a; do \
+			echo $(RANLIB) $(PSLIB_DIR)/lib/$$f; \
+			$(RANLIB) $(PSLIB_DIR)/lib/$$f; \
+		done; \
+	fi
+
+clean:
+	$(RM) *~ core* *.a *.so *.dylib
+
+distclean: clean
+	@: nothing extra to do
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Makefile	(revision 22322)
@@ -0,0 +1,36 @@
+CC = cc -std=c99
+CFLAGS = -Wall -g $(INCLUDES)
+
+INCLUDES =
+#
+# N.b. Utils must come first:
+#
+DIRS = Utils Metadata
+
+.PHONY : all
+all :
+	@for d in $(DIRS); do \
+	    echo "cd $$d; $(MAKE)"; \
+	    (cd $$d; $(MAKE)); \
+	done
+#
+# `Our' .h files
+#
+HFILES = psArray.h psDlist.h psHash.h psLogMsg.h psMemory.h psMisc.h psTrace.h
+
+check :
+	../Utilities/bin/check-namespace `find Utils Metadata .. -name *.[ao] -print` \
+		`find . -name \*.h -print` \
+		`echo $(HFILES) | perl -pe 's:(^| ): ../PSLib/include/:g;'`
+#
+clean :
+	$(RM) $(PROGS) *.o *.a *.log *.dvi *.aux *.toc *.log *.out *~ core TAGS
+	@for d in $(DIRS); do \
+	    echo "cd $$d; $(MAKE) clean"; \
+	    (cd $$d; $(MAKE) clean); \
+	done
+empty : clean
+	@for d in $(DIRS); do \
+	    echo "cd $$d; $(MAKE) empty"; \
+	    (cd $$d; $(MAKE) empty); \
+	done
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+tst_metadata
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/Makefile	(revision 22322)
@@ -0,0 +1,33 @@
+CC = cc -std=c99
+
+CFLAGS = -Wall -g $(INCLUDES)
+
+PSLIB_DIR = ../../
+
+INCLUDES = -I. -I$(PSLIB_DIR)/include -I$(FFTW_DIR)/include
+LIBS = -L$(PSLIB_DIR)/lib -lUtils
+
+.PHONY : all
+all : metadata.o
+	@: nothing else to do
+#
+# Test code
+#
+TST_PROGS = tst_metadata
+test : $(TST_PROGS)
+	for p in $(TST_PROGS); do \
+		echo $$p; \
+		$$p; \
+	done
+
+tst_metadata : tst_metadata.o metadata.o 
+	$(CC) -o tst_metadata tst_metadata.o metadata.o $(LIBS)
+#
+.PHONY :_tags
+_tags :;
+tags : _tags
+	etags *.[ch]
+#
+clean :
+	$(RM) $(TST_PROGS) *.o *.a *.log *.dvi *.aux *.toc *.log *.out *~ core TAGS
+empty : clean
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/metadata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/metadata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/metadata.c	(revision 22322)
@@ -0,0 +1,396 @@
+/*
+ * A strawman implementation of the Pan-STARRS metadata system
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include "psLib.h"
+#include "psMetadata.h"
+
+/*****************************************************************************/
+/*
+ * Handle sets of metadata
+ */
+psMetadata *psMetadataAlloc(void)	// make a new set of metadata
+{
+    psMetadata *ms = psAlloc(sizeof(psMetadata));
+
+    ms->list = psDlistAlloc(NULL);	// list of metadata
+    ms->table = psHashAlloc();		// hash table
+
+    return ms;
+}
+
+void psMetadataFree(psMetadata *ms) // destroy a set of metadata
+{
+    if (ms == NULL) {
+	return;
+    } else if (psMemGetRefCounter(ms) > 1) {
+	psMemDecrRefCounter(ms);
+	return;
+    }
+    
+    psDlistFree(ms->list, (void (*)(void *))NULL);
+    psHashFree(ms->table, (void (*)(void *))psMetadataItemFree);
+
+    psFree(ms);
+}
+
+/*****************************************************************************/
+/*
+ * Return a properly filled-out psMetadataItem
+ *
+ * N.b. For types PS_META_FLOAT, PS_META_INT, and PS_META_STR, a COPY
+ * of the data will be kept. In all other cases, only a POINTER
+ * to the data will be kept.
+ */
+psMetadataItem *psMetadataItemAlloc(
+    const char *name,			// name of new item of metadata (may be an sprintf format)
+    int format,				// type of this piece of metadata + flags
+    const char *comment,		// comment associated with item; may be NULL
+    ...)				// data + possible arguments for name format
+{
+    va_list ap;
+    va_start(ap, comment);
+
+    psMetadataItem *mi = psMetadataItemAllocV(name, format, comment, ap);
+
+    va_end(ap);
+
+    return mi;
+}
+
+psMetadataItem *psMetadataItemAllocV(
+    const char *name,			// name of new item of metadata (may be an sprintf format)
+    int format,				// type of this piece of metadata + flags
+    const char *comment,		// comment associated with item; may be NULL
+    va_list ap)				// possible arguments for name format
+{
+    const psMetadataType type = format & PS_META_TYPE_MASK; // the type part of format
+    psMetadataFlags flags = format & ~PS_META_TYPE_MASK;    // the flags part of format
+    static int id = 0;			// unique ID for an item of metadata
+    psMetadataItem *ms = psAlloc(sizeof(psMetadataItem));
+
+    *(int *)&ms->id = id++;		// need to cast away const
+
+    assert ((PS_META_NTYPE & PS_META_TYPE_MASK) == PS_META_NTYPE); // we have enough type names available
+    if (flags & PS_META_NON_UNIQUE) {
+	if (flags & PS_META_UNIQUE) {
+	    psTrace("metadata.name", 1,
+		    "Keyword \"%s\" was specified as both UNIQUE and NON_UNIQUE; adopting UNIQUE\n", name);
+	    flags &= ~PS_META_NON_UNIQUE;
+	}
+    } else {
+	flags |= PS_META_UNIQUE;
+    }
+    
+    ms->type = type;
+    ms->flags = flags;
+    ms->items = NULL;
+
+    assert (type >= 0 && type < PS_META_NTYPE);
+    switch (type) {
+      case PS_META_ITEM_SET:
+	ms->data.V = NULL;
+	ms->items = psDlistAlloc(NULL);
+	break;
+
+      case PS_META_F32:			// psF32 promoted to psF64 through ...
+      case PS_META_F64:
+	ms->data.F64 = va_arg (ap, psF64);
+	break;
+	
+      case PS_META_S32:
+	ms->data.S32 = va_arg (ap, psS32);
+	break;
+	
+      case PS_META_STR:
+	ms->data.V = psStringCopy(va_arg(ap, char *));
+	break;
+	
+      case PS_META_IMG:
+      case PS_META_JPEG:
+      case PS_META_PNG:
+      case PS_META_ASTROM:
+      case PS_META_UNKNOWN:
+	ms->data.V = va_arg(ap, void *);
+	break;
+
+      case PS_META_NTYPE:
+	psAbort(__FILE__, "You cannot get here (%d)\n", __LINE__);
+	break;				// NOTREACHED
+    }
+
+    assert (name != NULL);
+    if (strchr(name, '%') != NULL) {
+	static char tmp[201];		// should be enough; we won't overflow even if it isn't
+
+	if(vsnprintf(tmp, sizeof(tmp), name, ap) > sizeof(tmp) - 1) {
+	    psTrace("metadata.name", 1, "Keyword \"%s\" was truncated to \"%s\" upon expansion\n", name, tmp);
+	}
+
+	name = tmp;	    
+    }
+    ms->name = psStringCopy(name);
+
+    ms->comment = psStringCopy(comment == NULL ? "" : comment);
+
+    return ms;
+}
+
+void psMetadataItemFree(psMetadataItem *ms) // piece of metadata to destroy
+{
+    if(ms == NULL) {
+	return;
+    }
+
+    psFree(ms->name);
+    psFree(ms->comment);
+
+    assert (ms->type >= 0 && ms->type < PS_META_NTYPE);
+    switch (ms->type) {
+      case PS_META_ITEM_SET:
+	psDlistFree(ms->items, (void (*)(void *))NULL); // they're still in the hash table
+	ms->items = NULL;
+	break;
+	
+      case PS_META_F32:
+      case PS_META_F64:
+      case PS_META_S32:
+	break;				// nothing to do
+	
+      case PS_META_STR:
+	psFree(ms->data.V);
+	break;
+	
+      case PS_META_IMG:
+      case PS_META_JPEG:
+      case PS_META_PNG:
+      case PS_META_ASTROM:
+      case PS_META_UNKNOWN:
+	break;				// we don't own the memory
+	
+      case PS_META_NTYPE:
+	psAbort(__FILE__, "You cannot get here (%d)\n", __LINE__);
+	break;				// NOTREACHED
+    }
+
+    psFree(ms);
+    
+}
+
+/*****************************************************************************/
+/*
+ * Add an new psMetadataItem to a psMetadata
+ */
+psMetadataItem *psMetadataAppendItem(psMetadata *restrict ms, psMetadataItem *restrict item)
+{
+    psMetadataItem *meta = NULL;	// an item of metadata
+    assert (ms != NULL && ms->list != NULL && ms->table != NULL);
+
+    // Have we already seen this item?
+    meta = psHashLookup(ms->table, item->name);
+    
+    if (item->flags & PS_META_NON_UNIQUE) {
+	psMetadataItem *head = meta;
+	if (head == NULL) {		// this is the first item of this name
+	    head = psMetadataAppend(ms, item->name, PS_META_ITEM_SET, NULL);
+	}	    
+
+	psDlistSetIterator(head->items, PS_DLIST_TAIL, 0);
+	meta = psDlistGetPrev(head->items, 0);
+
+	int i = 0;			// index of this instance of name
+	if (meta != NULL) {		// no items of this name
+	    char *ptr = strrchr(meta->name, '.');
+	    assert (ptr != NULL);
+	    i = atoi(ptr + 1) + 1;	// next value
+	}
+
+	char newname[strlen(item->name) + 2 + 1];
+	sprintf(newname, "%s.%d", item->name, i);
+
+	assert (psHashLookup(ms->table, newname) == NULL); // name isn't in use
+
+	psFree(item->name);
+	item->name = psStringCopy(newname);
+
+	psDlistAppend(head->items, item);
+	psHashInsert(ms->table, item->name, item, (void (*)(void *))psMetadataItemFree);
+
+	return item;
+    } else if (meta != NULL) {
+	psError(__func__, PS_ERR_UNKNOWN, 1,
+		"Key \"%s\" is already present in the metaDataSet 0x%x", item->name, ms);
+	psMetadataItemFree(item);
+
+	return NULL;
+    }
+
+    psDlistAppend(ms->list, item);
+    psHashInsert(ms->table, item->name, item, (void (*)(void *))psMetadataItemFree);
+
+    return item;
+}
+
+/************************************************************************************************************/
+/*
+ * Convenience wrapper for psMetadataAppendItem()
+ */
+psMetadataItem *psMetadataAppend(psMetadata *restrict md, ///< Metadata to add to
+    const char *name,			///< name of new item of metadata
+    int format,				///< type of this piece of metadata + flags
+    const char *comment,		///< comment associated with item
+    ...)				///< possible arguments for name format
+{
+    va_list ap;
+    va_start(ap, comment);
+
+    psMetadataItem *mi = psMetadataItemAllocV(name, format, comment, ap);
+
+    va_end(ap);
+
+    return psMetadataAppendItem(md, mi);
+}
+
+/*****************************************************************************/
+/*
+ * Remove and return an item of metadata from a set
+ */
+psMetadataItem *psMetadataRemove(psMetadata *restrict ms, const char *restrict key)
+{
+    assert (ms != NULL && ms->list != NULL && ms->table != NULL);
+
+    psMetadataItem *meta = psHashRemove(ms->table, key);
+
+    if (meta == NULL) {
+	return NULL;
+    } else {
+	if (meta->flags & PS_META_NON_UNIQUE) {	// it's on someone's ->items list; find that someone
+	    char baseKey[strlen(key) + 1];	// Name without the .nnn tacked on
+	    strcpy(baseKey, key);
+
+	    char *ptr = strrchr(baseKey, '.');
+	    assert (ptr != NULL);
+	    *ptr = '\0';
+
+	    psMetadataItem *base = psHashLookup(ms->table, baseKey); // here's the non-qualified metadata
+	    assert (base != NULL);
+
+	    return psDlistRemove(base->items, meta, PS_DLIST_UNKNOWN);
+	} else {
+	    return psDlistRemove(ms->list, meta, PS_DLIST_UNKNOWN);
+	}
+    }
+}
+
+/*****************************************************************************/
+/*
+ * Iterators for the psDlist representation of the metadata
+ */
+void psMetadataSetIterator(psMetadata *ms)
+{
+    assert (ms != NULL);
+    psDlistSetIterator(ms->list, PS_DLIST_HEAD, 0);
+}
+
+psMetadataItem *psMetadataGetNext(psMetadata *restrict ms,
+				  const char *restrict match,
+				  int which)
+{
+    assert (ms != NULL);
+    psMetadataItem *meta = psDlistGetNext(ms->list, 0);
+
+    if (match == NULL || match[0] == '\0' || meta == NULL) {
+	return meta;
+    } else {
+	if (strncmp(meta->name, match, strlen(match)) == 0) { // a match
+	    return meta;
+	} else {
+	    return psMetadataGetNext(ms, match, which);
+	}
+    }
+}
+
+/*****************************************************************************/
+/*
+ * Access using the hash table
+ */
+psMetadataItem *psMetadataLookup(const psMetadata *restrict ms,
+				 const char *restrict key)
+{
+    assert (ms != NULL);
+
+    return psHashLookup(ms->table, key);
+}
+
+/*****************************************************************************/
+void psMetadataItemPrint(FILE *fd,		// file descriptor to write to
+			 const psMetadataItem *restrict ms, // item of metadata to print
+			 const char *prefix)	   // print this at the beginning of each line
+{
+    if(ms == NULL) {
+	return;
+    }
+
+    fprintf(fd, "%sName:     %-40s (ID: %d)\n", prefix, ms->name, ms->id);
+    if (*ms->comment != '\0') {
+	fprintf(fd, "%sComment:  %s\n", prefix, ms->comment);
+    }
+    fprintf(fd, "%sValue:    ", prefix);
+
+    assert (ms->type >= 0 && ms->type < PS_META_NTYPE);
+    fprintf(fd, "%s", prefix);
+    switch (ms->type) {
+      case PS_META_ITEM_SET:
+	{				// gcc 3.3 doesn't allow a declaration here
+	    psMetadataItem *meta = NULL;
+
+	    fprintf(fd, "\n");
+	    psDlistSetIterator(ms->items, PS_DLIST_HEAD, 0);
+	    while ((meta = psDlistGetNext(ms->items, 0)) != NULL) {
+		psMetadataItemPrint(fd, meta, "\t");
+	    }
+	}
+	
+	break;
+	
+      case PS_META_F64:
+      case PS_META_F32:
+	fprintf(fd, "%g\n", ms->data.F32);
+	break;
+	
+      case PS_META_S32:
+	fprintf(fd, "%d\n", ms->data.S32);
+	break;
+	
+      case PS_META_STR:
+	fprintf(fd, "%s\n", (char *)ms->data.V);
+	break;
+	
+      case PS_META_IMG:
+	fprintf(fd, "(IMG)\n");
+	break;
+	
+      case PS_META_JPEG:
+	fprintf(fd, "(JPG)\n");
+	break;
+	
+      case PS_META_PNG:
+	fprintf(fd, "(PNG)\n");
+	break;
+	
+      case PS_META_ASTROM:
+	fprintf(fd, "(ASTROM)\n");
+	break;
+	
+      case PS_META_UNKNOWN:
+	fprintf(fd, "(UNKNOWN)\n");
+	break;
+	
+      case PS_META_NTYPE:
+	psAbort(__FILE__, "You cannot get here (%d)\n", __LINE__);
+	break;				// NOTREACHED
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/psAstrom.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/psAstrom.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/psAstrom.h	(revision 22322)
@@ -0,0 +1,10 @@
+#if !defined(PS_ASTROM_H)
+#define PS_ASTROM_H
+/*
+ * This is not a full implementation...
+ */
+typedef struct {
+   const int id;
+} psAstrom;
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/psImage.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/psImage.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/psImage.h	(revision 22322)
@@ -0,0 +1,10 @@
+#if !defined(PS_IMAGE_H)
+#define PS_IMAGE_H
+/*
+ * This is not a full implementation...
+ */
+typedef struct {
+   const int id;
+} psImage;
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/tst_metadata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/tst_metadata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Metadata/tst_metadata.c	(revision 22322)
@@ -0,0 +1,71 @@
+/*****************************************************************************/
+/*
+ * Test code
+ */
+#include <stdlib.h>
+//#include <stdio.h>
+//#include <assert.h>
+//#include <string.h>
+#include <math.h>
+#include "psLib.h"
+#include "psMetadata.h"
+
+int main(void)
+{
+    psSetTraceLevel("metadata", 1);	// turn on tracing
+    psSetLogFormat("NM");		// suppress some of the psLogMsg verbosity
+
+    psMetadata *ms = psMetadataAlloc(); // set of metadata
+    psMetadataItem *meta = NULL;	    // used to traverse a set of metadata
+
+    for (int i = 0; i < 10; i += 5) {
+	float sqrti = sqrt(i);
+	psMetadataAppend(ms, PS_META_INT, &i, NULL, "const.%d", i);
+	psMetadataAppend(ms, PS_META_FLOAT, &sqrti, "square root", "const.sqrt%d", i);
+    }
+    psMetadataAppend(ms, PS_META_STR | PS_META_NON_UNIQUE, "Bonjour", "French", "lang.hello");
+    psMetadataAppend(ms, PS_META_STR | PS_META_NON_UNIQUE, "Aloha", "Hawaiian", "lang.hello");
+    psMetadataAppend(ms, PS_META_STR | PS_META_NON_UNIQUE, "Good Morning", "English", "lang.hello");
+    /*
+     * Print all metadata starting "lang"
+     */
+    psMetadataItemFree(psMetadataRemove(ms, "lang.hello.0"));
+
+    psMetadataSetIterator(ms);
+    while ((meta = psMetadataGetNext(ms, "lang")) != NULL) {
+	psMetadataItemPrint(stdout, meta, "");
+    }
+    /*
+     * Remove a key
+     */
+    psMetadataItemFree(psMetadataRemove(ms, "lang.hello"));
+    /*
+     * Print all metadata
+     */
+    fprintf(stdout, "------\n");
+    psMetadataSetIterator(ms);
+    while ((meta = psMetadataGetNext(ms, NULL)) != NULL) {
+	psMetadataItemPrint(stdout, meta, "");
+    }
+    /*
+     * Look up a key by name
+     */
+    fprintf(stdout, "------\n");
+    fprintf(stdout, "Looking up by name:\n");
+    meta = psMetadataLookup(ms, "const.5");
+    if (meta != NULL) {
+	psMetadataItemPrint(stdout, meta, "");
+    }
+    /*
+     * Clean up
+     */
+    psMetadataFree(ms);
+    /*
+     * Check for memory leaks
+     */
+    fprintf(stderr,"Checking for memory leaks:\n");
+    psTraceReset();
+    psMemCheckLeaks(0, NULL, stderr);
+    
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+tst_trace tst_logmsg tst_dlist tst_memory tst_array tst_hash tst_error
+psErrorCodes.c
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/.log
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/.log	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/.log	(revision 22322)
@@ -0,0 +1,34 @@
+This is pdfTeX, Version 3.14159-1.10a-devel (Web2C 7.3.8) (format=pdflatex 2003.1.31)  2 MAR 2004 11:42
+**utils.tex
+(/usr/local/tetex/share/texmf/tex/latex/tools/.tex{/usr/local/tetex/share/texmf
+.local/pdftex/config/pdftex.cfg
+Warning: pdftex (file /usr/local/tetex/share/texmf.local/pdftex/config/pdftex.c
+fg): invalid line in config file: `pdf_minorversion 4'
+}
+LaTeX2e <2001/06/01>
+Babel <v3.7h> and hyphenation patterns for american, british, nohyphenation, lo
+aded.
+File ignored)
+*
+! Interruption.
+<*> 
+    
+? 
+! Emergency stop.
+<*> 
+    
+End of file on the terminal!
+
+ 
+Here is how much of TeX's memory you used:
+ 6 strings out of 60983
+ 162 string characters out of 1189764
+ 44491 words of memory out of 2500001
+ 3092 multiletter control sequences out of 10000+50000
+ 3640 words of font info for 14 fonts, out of 500000 for 1000
+ 22 hyphenation exceptions out of 1000
+ 5i,0n,1p,89b,7s stack positions out of 1500i,500n,5000p,200000b,5000s
+ 0 PDF objects out of 300000
+ 0 named destinations out of 131072
+ 0 words of extra memory for PDF output out of 65536
+No pages of output.
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/Makefile	(revision 22322)
@@ -0,0 +1,58 @@
+CC = cc -std=c99
+CFLAGS = -Wall -g -I$(PSLIB_DIR)/include -I$(FFTW_DIR)/include
+DYLIB = dylib
+DYLIB_LDFLAGS = -undefined suppress -flat_namespace -dynamiclib -shared
+
+PSLIB_DIR = ../..
+# array.o 
+OBJS = dlist.o error.o hash.o logmsg.o memory.o misc.o trace.o vector.o
+
+.PHONY : all
+all : $(PSLIB_DIR)/lib/libUtils.a libUtils.$(DYLIB)
+
+libUtils.$(DYLIB) : $(OBJS)
+	$(LD) -o libUtils.$(DYLIB) $(DYLIB_LDFLAGS) $(OBJS)
+	mv libUtils.$(DYLIB) $(PSLIB_DIR)/lib
+
+$(PSLIB_DIR)/lib/libUtils.a : $(OBJS)
+	ar r $(PSLIB_DIR)/lib/libUtils.a $?
+	ranlib $(PSLIB_DIR)/lib/libUtils.a
+#
+# Error codes
+#
+psErrorCodes.c : $(PSLIB_DIR)/src/Utils/psErrorCodes.dat
+	$(PSLIB_DIR)/bin/makeErrorCodes -c psErrorCodes.c -h $(PSLIB_DIR)/include/psErrorCodes.h \
+			$(PSLIB_DIR)/src/Utils/psErrorCodes.dat
+$(OBJS): psErrorCodes.c
+#
+# Test code
+#
+TST_PROGS = tst_array tst_dlist tst_error tst_hash tst_logmsg tst_memory tst_trace
+test : $(TST_PROGS)
+	for p in $(TST_PROGS); do \
+		echo $$p; \
+		$$p; \
+	done
+tst_array : $(PSLIB_DIR)/lib/libUtils.a tst_array.o
+	$(CC) $(CFLAGS) -o tst_array tst_array.o $(PSLIB_DIR)/lib/libUtils.a
+tst_dlist : $(PSLIB_DIR)/lib/libUtils.a tst_dlist.o
+	$(CC) $(CFLAGS) -o tst_dlist tst_dlist.o $(PSLIB_DIR)/lib/libUtils.a
+tst_error : $(PSLIB_DIR)/lib/libUtils.a tst_error.o
+	$(CC) $(CFLAGS) -o tst_error tst_error.o $(PSLIB_DIR)/lib/libUtils.a
+tst_hash : $(PSLIB_DIR)/lib/libUtils.a tst_hash.o
+	$(CC) $(CFLAGS) -o tst_hash tst_hash.o $(PSLIB_DIR)/lib/libUtils.a
+tst_logmsg : $(PSLIB_DIR)/lib/libUtils.a tst_logmsg.o
+	$(CC) $(CFLAGS) -o tst_logmsg tst_logmsg.o $(PSLIB_DIR)/lib/libUtils.a
+tst_memory : $(PSLIB_DIR)/lib/libUtils.a tst_memory.o
+	$(CC) $(CFLAGS) -o tst_memory tst_memory.o $(PSLIB_DIR)/lib/libUtils.a
+tst_trace : $(PSLIB_DIR)/lib/libUtils.a tst_trace.o
+	$(CC) $(CFLAGS) -o tst_trace tst_trace.o $(PSLIB_DIR)/lib/libUtils.a
+#
+#
+_tags :;
+tags : _tags
+	etags *.[ch]
+#
+clean :
+	$(RM) $(TST_PROGS) *.o *.a *.log *.dvi *.aux *.toc *.log *.out *~ core TAGS psErrorCodes.c
+empty : clean
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/Private/p_psMemory.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/Private/p_psMemory.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/Private/p_psMemory.h	(revision 22322)
@@ -0,0 +1,17 @@
+#if !defined(P_PS_MEMORY_H)
+#define P_PS_MEMORY_H
+
+#define P_PS_MEMMAGIC (void *)0xdeadbeef // Magic number in psMemBlock header
+
+/*****************************************************************************/
+/*
+ * When to call the allocate/free callbacks.  Set using
+ * psMemSetAllocateID/psMemSetFreeID, but it can be
+ * convenient to see these directly from the debugger
+ */
+extern long p_psMemAllocateID;		// notify user this block is allocated
+extern long p_psMemFreeID;		// notify user this block is freed
+
+/*****************************************************************************/
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/array.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/array.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/array.c	(revision 22322)
@@ -0,0 +1,38 @@
+/*
+ * Arrays.  Almost all the array support is in fact in psArray.h,
+ * but we directly support arrays of (void *) here, so as to be
+ * able to free each element properly
+ */
+#include "psLib.h"
+#include "psArray.h"
+
+typedef void *psVoidPtr;
+
+P_PS_CREATE_ARRAY_TYPE(static, my_, psVoidPtr);
+
+psVoidPtrArray *psVoidPtrArrayAlloc(int n)
+{
+    return my_psVoidPtrArrayAlloc(n);
+}
+
+psVoidPtrArray *psVoidPtrArrayRealloc(psVoidPtrArray *arr, int n)
+{
+    return my_psVoidPtrArrayRealloc(arr, n);
+}
+
+void psVoidPtrArrayFree(psVoidPtrArray *arr,
+			void (*elemFree)(void *)) // destructor for data on array
+{
+    if (arr == NULL) {
+	return;
+    }
+	
+    for (int i = 0; i < arr->n; i++) {
+	if (elemFree == NULL) {
+	    psMemDecrRefCounter(arr->arr[i]);
+	} else {
+	    elemFree(psMemDecrRefCounter(arr->arr[i]));
+	}
+    }
+    my_psVoidPtrArrayFree(arr);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/dlist.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/dlist.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/dlist.c	(revision 22322)
@@ -0,0 +1,347 @@
+/*
+ * Minimal support for doubly linked lists
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "psLib.h"
+
+static psDlistElem *dlistElemAlloc(void)
+{
+    return(psAlloc(sizeof(psDlistElem)));
+}
+
+static void dlistElemFree(psDlistElem *elem)
+{
+    psFree(elem);
+}
+
+/*****************************************************************************/
+
+psDlist *psDlistAlloc(void *data)	// initial data item; may be NULL
+{
+    psDlist *list = psAlloc(sizeof(psDlist));
+
+    list->n = 0;
+    list->head = list->tail = NULL;
+    list->iter = NULL;
+    if (data != NULL) {
+	psDlistAdd(list, data, PS_DLIST_TAIL);
+    }
+
+    return list;
+}
+
+void psDlistFree(psDlist *list,		// list to destroy
+		 void (*elemFree)(void *)) // destructor for data on list
+{
+    if (list == NULL) {
+	return;
+    }
+
+    psDlistElem *ptr = list->head;
+    while (ptr != NULL) {
+	psDlistElem *next = ptr->next;
+
+	if (elemFree == NULL) {
+	    psMemDecrRefCounter(ptr->data);
+	} else {
+	    elemFree(psMemDecrRefCounter(ptr->data));
+	}
+	dlistElemFree(ptr);
+	
+	ptr = next;
+    }
+
+    psFree(list);
+}
+
+/*****************************************************************************/
+/*
+ * Add an element to a list
+ */
+psDlist *psDlistAdd(psDlist *list,	// list to add to; may be NULL
+		    void *data,		// data to add
+		    int where)		// where to add data. PS_DLIST_HEAD,
+					// PS_DLIST_TAIL, or an integer
+{
+    psDlistElem *elem = dlistElemAlloc();
+
+    if (list == NULL) {
+	list = psDlistAlloc(NULL);
+    }
+
+    if (where > list->n) {		// XXX need better diagnostic here
+	fprintf(stderr, "Invalid index %d (only %d elements)\n",
+		where, list->n);
+	dlistElemFree(elem);		// cleanup
+	
+	return list;
+    }
+
+    if (where == PS_DLIST_HEAD) {	// easy
+	elem->prev = NULL;
+	elem->next = list->head;
+	elem->data = psMemIncrRefCounter(data);
+
+	if (list->head != NULL) {
+	    list->head->prev = elem;
+	}
+
+	list->head = elem;
+	if (list->tail == NULL) {
+	    list->tail = elem;
+	}
+	list->n++;
+    } else if (where == PS_DLIST_TAIL) { // also easy
+	elem->prev = list->tail;
+	elem->next = NULL;
+	elem->data = psMemIncrRefCounter(data);
+
+	if (list->tail != NULL) {
+	    list->tail->next = elem;
+	}
+
+	list->tail = elem;
+	if (list->head == NULL) {
+	    list->head = elem;
+	}
+	list->n++;
+    } else {				// XXX
+	fprintf(stderr, "Insertion into psDlists is not yet supported\n");
+	return psDlistAdd(list, data, PS_DLIST_TAIL);
+    }
+
+    return list;
+}
+
+/*****************************************************************************/
+
+psDlist *psDlistAppend(psDlist *list,	// list to add to; may be NULL
+		       void *data)	// data to add at end
+{
+    return psDlistAdd(list, data, PS_DLIST_TAIL);
+}
+
+/*****************************************************************************/
+/*
+ * Remove an element from a list
+ */
+void *psDlistRemove(psDlist *list,	// list to remove element from
+		    void *data,		// data item to remove (or NULL)
+		    int which)		// index of item, if known.
+					// PS_DLIST_UNKNOWN, or PS_DLIST_HEAD,
+					// or PS_DLIST_TAIL
+{
+    psDlistElem *elem = NULL;		// element to remove
+    assert (list != NULL);
+
+    if (data != NULL) {
+	assert (which == PS_DLIST_UNKNOWN);
+
+	int i = 0;			// index
+	for (psDlistElem *ptr = list->head; ptr != NULL; ptr = ptr->next, i++) {
+	    if (ptr->data == data) {
+		return psDlistRemove(list, NULL, i);
+	    }
+	}
+
+	psTrace("utils.dlist.remove", 5, "Failed to find 0x%x on psDlist 0x%x", data, list);
+	
+	return NULL;
+    }
+
+    if (which == PS_DLIST_HEAD || which == 0) {
+	if (list->head == NULL) {
+	    return NULL;
+	} else {
+	    elem = list->head;
+	    
+	    if (list->head->next != NULL) {
+		list->head->next->prev = NULL;
+	    }
+	    list->head = list->head->next;
+	    list->n--;
+
+	    if (list->head == NULL) {
+		list->tail = NULL;
+	    }
+	}
+    } else if (which == PS_DLIST_TAIL || which == list->n - 1) {
+	if (list->tail == NULL) {
+	    return NULL;
+	} else {
+	    elem = list->tail;
+	    if (list->tail->prev != NULL) {
+		list->tail->prev->next = NULL;
+	    }
+	    list->tail = list->tail->prev;
+	    list->n--;
+
+	    if (list->tail == NULL) {
+		list->head = NULL;
+	    }
+	}
+    } else if (which != PS_DLIST_UNKNOWN) { // an index in the middle of the list
+	assert (which != 0 && which != list->n - 1);
+	
+	if (which < 0 || which >= list->n) { // out of bounds
+	    psError(__func__, PS_ERR_BAD_INDEX, 1, "Index %d is not in range [0..%d] for list 0x%x", which, list->n, list);
+	    return NULL;
+	}
+	
+	int i = 0;			// index
+	for (psDlistElem *ptr = list->head; ptr != NULL; ptr = ptr->next, i++) {
+	    if (i == which) {
+		ptr->prev->next = ptr->next;
+		ptr->next->prev = ptr->prev;
+
+		elem = ptr;		
+	    }
+	}
+    }
+    /*
+     * OK, delete list element and return the data
+     */
+    assert (elem != NULL);
+    data = elem->data;
+    dlistElemFree(elem);
+
+    return psMemDecrRefCounter(data);
+}
+
+/*****************************************************************************/
+/*
+ * Retrieve an element from the list, or iterate over list.
+ *
+ * If which is psDlist{Next,Prev} return the next/previous
+ * data and move the iteration cursor 
+ *
+ * If which is psDlist{Head,Tail} initialise the iteration pointer
+ * and return the head/tail
+ *
+ * Otherwise, return the desired element
+ */
+#define ITER_INIT_HEAD ((void *)1)	// next iteration should return head
+#define ITER_INIT_TAIL ((void *)2)	// next iteration should return tail
+
+void *psDlistGet(
+    const psDlist *list,		// list to retrieve element from
+    int which)				// index of item, or PS_DLIST_NEXT,
+					// or PS_DLIST_PREV
+{
+    assert (list != NULL);
+
+    switch (which) {
+      case PS_DLIST_HEAD:		// easy
+	((psDlist *)list)->iter = ITER_INIT_HEAD;
+	return (list->head == NULL) ? NULL : list->head->data;
+      case PS_DLIST_TAIL:		// also easy
+	((psDlist *)list)->iter = ITER_INIT_TAIL;
+	return (list->tail == NULL) ? NULL : list->tail->data;
+      case PS_DLIST_PREV:		// use iterator
+      case PS_DLIST_NEXT:
+	if (list->iter == NULL) {
+	    return NULL;
+	} else if (list->iter == ITER_INIT_HEAD) {
+	    ((psDlist *)list)->iter = list->head;
+	} else if (list->iter == ITER_INIT_TAIL) {
+	    ((psDlist *)list)->iter = list->tail;
+	} else if (which == PS_DLIST_PREV) {
+	    ((psDlist *)list)->iter = list->iter->prev;
+	} else if (which == PS_DLIST_NEXT) {
+	    ((psDlist *)list)->iter = list->iter->next;
+	} else {
+	    fprintf(stderr,"You cannot get here\n");
+	    abort();
+	}
+
+	if (list->iter == NULL) {
+	    return NULL;
+	}
+	return list->iter->data;	// XXX what if data is NULL?
+
+	break;
+      default:				// XXX
+	fprintf(stderr,
+		"Retrieval from psDlists by index is not yet supported\n");
+	return NULL;
+    }
+}
+
+/*****************************************************************************/
+/*
+ * Some wrappers for those iteration calls using psDlistGet.
+ *
+ * First a call to set the iteration pointer to the head of the list
+ */
+void psDlistSetIterator(psDlist *list,	// the list in question
+			int where,	// where on the list should I start?
+			int which)	// @notused@ the desired iterator
+{
+    if (where != PS_DLIST_HEAD && where != PS_DLIST_TAIL) {
+	psError(__func__, PS_ERR_UNKNOWN, 1, "Invalid where argument: %d; assuming PS_DLIST_HEAD", where);
+	where = PS_DLIST_HEAD;
+    }
+    
+    psDlistGet(list, where);	// initialise iterator at head/tail
+}
+
+/*
+ * and now return the previous/next element of the list
+ */
+void *psDlistGetNext(psDlist *list,	// the list in question
+		     int which)		// @notused@ the desired iterator
+{
+    return psDlistGet(list, PS_DLIST_NEXT);
+}
+
+void *psDlistGetPrev(psDlist *list,	// the list in question
+		     int which)		// @notused@ the desired iterator
+{
+    return psDlistGet(list, PS_DLIST_PREV);
+}
+
+/*****************************************************************************/
+/*
+ * Convert a psDlist to/from a psVoidPtrArray
+ */
+psVector *psDlistToArray(psDlist *restrict dlist)
+{
+    if (dlist == NULL) {
+	return NULL;
+    }
+    
+    psVector *restrict arr = psVectorAlloc(dlist->n, PS_TYPE_OTHER);
+
+    psDlistElem *ptr = dlist->head;
+    for (int i = 0, n = dlist->n; i < n; i++) {
+	arr->data.V[i] = ptr->data;
+	ptr->data = NULL;
+	ptr = ptr->next;
+    }
+
+    psDlistFree(dlist, NULL);
+
+    return arr;    
+}
+
+psDlist *psArrayToDlist(psVector *arr)
+{
+    psDlist *list = psDlistAlloc(NULL);	// list of elements
+
+    assert (arr != NULL);
+    if (arr->n > 0) {
+       assert (arr->type.dimen == PS_DIMEN_VECTOR && arr->type.type == PS_TYPE_OTHER);
+    }
+    
+    for (int i = 0, n = arr->n; i < n; i++) {
+	psDlistAppend(list,
+		      psMemDecrRefCounter(arr->data.V[i])); // it\'s already Incr
+	arr->data.V[i] = NULL;
+    }
+
+    psVectorFree(arr, NULL);
+
+    return list;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/error.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/error.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/error.c	(revision 22322)
@@ -0,0 +1,254 @@
+/*
+ * Support a Pan-STARRS error stack
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "psLogMsg.h"
+#include "psMemory.h"
+#include "psMisc.h"
+#include "psError.h"
+#include "psLib.h"
+
+/************************************************************************************************************/
+/*
+ * Description strings for errors
+ */
+#include "psErrorCodes.h"
+#include "psErrorCodes.c"
+
+static int nerrorString = 0;		// number of known error messages
+static char **errorStringList = NULL;	// error strings indexed by errorCode
+
+/************************************************************************************************************/
+/*
+ * Here's the error stack
+ */
+typedef struct errStack {
+    psErr error;
+    struct errStack *next;		// pointer to next message on the stack
+} errStack;
+
+static errStack *errorStack = NULL;	// the error stack itself, last message pushed is FIRST
+int nerror = 0;				// number of errors
+
+/************************************************************************************************************/
+/*
+ * Push an element onto the error stack
+ */
+void errStackPush(const char *name,		// name of error
+		  psErrorCode code,		// class of error
+		  const char *fmt,		// format for error message
+		  va_list ap)			// optional arguments for error
+{
+    errStack *err = psAlloc(sizeof(errStack));
+
+    err->next = errorStack;
+    errorStack = err;
+    nerror++;
+
+    err->error.name = psStringCopy(name);
+    err->error.code = code;
+
+    char buff[256];
+    int n = vsnprintf(buff, sizeof(buff), fmt, ap);
+    err->error.msg = strcpy(psAlloc(n + 1), buff);
+}
+/*
+ * Free an errStack element; don't worry about fixing the ->next pointer (that's why it's Del not Free)
+ */
+void errStackDel(errStack *err)
+{
+    if (err == NULL) {
+	return;
+    }
+
+    psFree(err->error.name);
+    psFree(err->error.msg);
+    psFree(err);
+}
+
+/************************************************************************************************************/
+/*
+ * Push an error onto the errorstack and print an error message
+ */
+int psError(const char *name,		// category of code that caused the error
+	    psErrorCode code,		// code of error (equivalent to errno)
+	    int new,			// is this a new error?
+	    const char *fmt, ...)	// format and possible extra arguments
+{
+    if (code == PS_ERR_NONE || new) { // free old error stack
+	errStack *eptr = errorStack;
+
+	while (eptr != NULL) {
+	    errStack *next = eptr->next;
+	    errStackDel(eptr);
+	    eptr = next;
+	}
+
+	errorStack = NULL;
+	nerror = 0;
+    }
+
+    if (code != PS_ERR_NONE) {
+	va_list ap;
+	va_start(ap, fmt);
+	errStackPush(name, code, fmt, ap);
+	va_end(ap);
+	
+	va_start(ap, fmt);
+	psLogMsgV(name, PS_LOG_ERROR, fmt, ap);
+	va_end(ap);
+    }
+
+    return code;
+}
+
+/************************************************************************************************************/
+/*
+ * Clear the error stack
+ */
+void psErrorClear(void)
+{
+    psError("", PS_ERR_NONE, 1, "");
+}
+
+/************************************************************************************************************/
+/*
+ * Return the last error; if there is none, return one with code "PS_ERR_NONE"
+ */
+const psErr *psErrorLast(void)
+{
+    return psErrorGet(0);
+}
+
+/*
+ * Return specified error (0: last error, 1: previous error, and so on)
+ */
+const psErr *psErrorGet(int which)
+{
+    static psErr noError = { "", PS_ERR_NONE, "" };
+
+    errStack *eptr = errorStack;
+    for (int i = 0; i < which && eptr != NULL; i++) {
+	eptr = eptr->next;
+    }
+
+    return (eptr == NULL) ? &noError : &eptr->error;
+}
+
+/************************************************************************************************************/
+/*
+ * Register a set of psErrorDescription
+ */
+void p_psErrorRegister(const psErrorDescription *errors,       // errors to register
+		       int nerror)			       // number of errors
+{
+    if (nerror <= 0) {			// no new errors
+	return;
+    }
+
+    int codeMax = errors[nerror - 1].code; // largest new error code
+
+    if (codeMax > nerrorString) {
+	errorStringList = psRealloc(errorStringList, (codeMax + 1)*sizeof(char *));
+
+	for (int i = nerrorString; i <= codeMax; i++) {
+	    errorStringList[i] = NULL;
+	}
+	nerrorString = codeMax + 1;
+    }
+
+    for (int i = 0; i < nerror; i++) {
+	if (errorStringList[errors[i].code] != NULL && // that value is already registered differently
+	    strcmp(errorStringList[errors[i].code], errors[i].descrip) != 0) {
+	    psLogMsg("utils.error", PS_LOG_WARN,
+		     "Attempt to register error code %d as \"%s\" that is already registered as \"%s\"",
+		     errors[i].code, errors[i].descrip, errorStringList[errors[i].code]);
+	}
+
+	errorStringList[errors[i].code] = (char *)errors[i].descrip;
+    }
+}
+
+/************************************************************************************************************/
+/*
+ * Return the string associated with an error code
+ */
+const char *psErrorCodeString(psErrorCode code)
+{
+    if (errorStringList == NULL) {	// need to build the errorStringList
+	psErrorRegister();
+    }
+
+    if (code < PS_ERR_BASE) {
+	return strerror(code);
+    } else if (code >= nerrorString) {
+	static char buff[30];
+	sprintf(buff, "(Unknown code %d)", code);
+	return buff;
+    } else {
+	return errorStringList[code];
+    }
+}
+
+/************************************************************************************************************/
+/*
+ * Print the header then error stack to the provided file descriptor
+ */
+void psErrorStackPrint(FILE *fd,	// write to this file descriptor
+		       const char *fmt,	// format for any header information; may be NULL
+		       ...)		// arguments for format
+{
+    va_list ap;
+    /*
+     * Print header?
+     */
+    if (fmt != NULL) {
+	va_start(ap, fmt);
+    }
+
+    psErrorStackPrintV(fd, fmt, ap);
+
+    if (fmt != NULL) {
+	va_end(ap);
+    }
+}
+
+void psErrorStackPrintV(FILE *fd,	// write to this file descriptor
+			const char *fmt,// format for any header information; may be NULL
+			va_list ap)	// arguments for format
+{
+    if (errorStringList == NULL) {	// need to build the errorStringList
+	psErrorRegister();
+    }
+
+    if (errorStack == NULL) {
+	return;
+    }
+    /*
+     * Print header?
+     */
+    if (fmt != NULL) {
+	vfprintf(fd, fmt, ap);
+	if (strchr(fmt, '\n') == NULL) {
+	    fprintf(fd, "\n");
+	}
+    }
+    /*
+     * Print error stack
+     */
+    const errStack *stack[nerror];	// we need to reverse the list
+    errStack *eptr = errorStack;
+    for (int i = 0; i < nerror; i++) {
+	stack[nerror - i - 1] = eptr;
+	eptr = eptr->next;
+    }
+    
+    for (int i = 0; i < nerror; i++) {
+	int code = stack[i]->error.code;
+	fprintf(fd, "%*s%-*s %-30s %s\n",
+		i, "", 30 - i, stack[i]->error.name, psErrorCodeString(code), stack[i]->error.msg);
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/hash.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/hash.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/hash.c	(revision 22322)
@@ -0,0 +1,228 @@
+/*
+ * An interface to hash tables for Pan-STARRS
+ *
+ * Collisions are handled by simple linked lists
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "psLib.h"
+
+/*****************************************************************************/
+/*
+ * A bucket that holds an item of data
+ */
+typedef struct HashBucket {
+    char *key;				// key for this item of data
+    void *data;				// the data itself
+    struct HashBucket *next;		// list of other possible keys
+} HashBucket;
+
+/*
+ * An entire hash table
+ */
+struct HashTable {
+    int nbucket;			// number of buckets
+    HashBucket **buckets;		// the buckets themselves
+};
+
+/*****************************************************************************/
+/*
+ * Con/Destruct buckets
+ */
+static HashBucket *hashBucketAlloc(const char *key,
+				   void *data,
+				   HashBucket *next)
+{
+    HashBucket *bucket = psAlloc(sizeof(HashBucket));
+
+    bucket->key = psStringCopy(key);
+    bucket->data = psMemIncrRefCounter(data);
+    bucket->next = next;
+
+    return bucket;
+}
+
+static void hashBucketFree(
+    HashBucket *bucket,			// bucket to free
+    void (*itemFree)(void *item))	// how to free hashed data;
+					// or NULL
+{
+    if (bucket == NULL) {
+	return;
+    }
+
+    psFree(bucket->key);
+    psMemDecrRefCounter(bucket->data);
+    
+    if (itemFree != NULL) {
+	itemFree(bucket->data);
+    }
+    
+    psFree(bucket);
+}
+
+/*****************************************************************************/
+/*
+ * Con/Destruct psHash tables
+ */
+psHash *psHashAlloc(void)
+{
+    int nbucket = 128;			// XXX initial number of buckets
+    psHash *table = psAlloc(sizeof(psHash));
+    table->buckets = psAlloc(nbucket*sizeof(HashBucket *));
+    table->nbucket = nbucket;
+
+    psTrace("utils.hash", 1, "Creating %d-element hash table\n", nbucket);
+    
+    for (int i = 0; i < nbucket; i++) {
+	table->buckets[i] = NULL;
+    }
+
+    return table;
+}
+
+void psHashFree(psHash *table,		// hash table to be freed
+		void (*itemFree)(void *item)) // how to free hashed data; or NULL
+{
+    if (table == NULL) {
+	return;
+    }
+/*
+ * Release data interned on the list, and delete the buckets
+ */
+    for (int i = 0; i < table->nbucket; i++) {
+	if (table->buckets[i] != NULL) {
+	    HashBucket *ptr = table->buckets[i];
+	    while (ptr != NULL) {
+		HashBucket *tmp = ptr->next;
+		hashBucketFree(ptr, itemFree);
+		ptr = tmp;
+	    }
+	}
+    }
+
+    psFree(table->buckets);
+    psFree(table);
+}
+
+/*****************************************************************************/
+/*
+ * Here's the routine to do the work of insertion/retrieval/removal
+ *
+ * We need itemFree if the key is already in use and we're inserting
+ *
+ * N.b. this is NOT a good hash function! See Knuth for some better ones
+ */
+static void *doHashWork(
+    psHash *table,			// table to insert in
+    const char *key,			// key to use
+    void *data,				// data to insert,
+					// or (if NULL) retrieve/remove
+    int remove,				// remove the item from the list?
+    void (*itemFree)(void *item))	// how to free hashed data
+					// or NULL
+{
+    long int hash = 1;
+
+    for (int i = 0, len = strlen(key); i < len; i++) {
+	hash = (hash << 1) ^ key[i];
+    }
+
+    hash &= (table->nbucket - 1);
+
+    HashBucket *ptr = table->buckets[hash];
+    /*
+     * We've found the correct hash bucket, now we need to know what to do
+     */
+    if (data == NULL) {			// retrieve/remove
+	if (remove) {
+	    HashBucket *optr = ptr;
+	    while (ptr != NULL) {
+		if (strcmp(key, ptr->key) == 0) { // found it!
+		    void *data = ptr->data;
+		    optr->next = ptr->next;
+
+		    if (ptr == table->buckets[hash]) {
+			table->buckets[hash] = ptr->next;
+		    }
+
+		    psFree(ptr->key);
+		    psFree(ptr);
+
+		    return psMemDecrRefCounter(data); // return data
+		}
+
+		optr = ptr;
+		ptr = ptr->next;
+	    }
+	} else {
+	    while (ptr != NULL) {
+		if (strcmp(key, ptr->key) == 0) { // found it!
+		    return ptr->data;
+		}
+		
+		ptr = ptr->next;
+	    }
+	}
+
+	return NULL;			// not in hash
+    } else {				// insert
+	while (ptr != NULL) {
+	    if (strcmp(key, ptr->key) == 0) { // found it!
+		if (itemFree == NULL) {
+		    return NULL;	// can't insert
+		}			
+
+		psTrace("utils.hash.insert", 3, "Replacing data for %s\n",
+			key);
+
+		itemFree(psMemDecrRefCounter(ptr->data));
+		ptr->data = psMemIncrRefCounter(data);
+
+		return data;
+	    }
+
+	    ptr = ptr->next;
+	}
+	/*
+	 * Not found, so insert at the front of the list
+	 */
+	table->buckets[hash] = hashBucketAlloc(key, data, table->buckets[hash]);
+
+	return data;
+    }
+}    
+
+/*****************************************************************************/
+/*
+ * Insert a value into a hash table
+ */
+void *psHashInsert(psHash *table,	// table to insert in
+		   const char *key,	// key to use
+		   void *data,		// data to insert
+		   void (*itemFree)(void *item)) // how to free hashed data;
+					// or NULL
+{
+    return doHashWork(table, key, data, 0, itemFree);
+}
+
+/*****************************************************************************/
+/*
+ * Lookup a value in a hash table
+ */
+void *psHashLookup(psHash *table,	// table to lookup key in
+		   const char *key)	// key to lookup
+{
+    return doHashWork(table, key, NULL, 0, NULL);
+}
+
+/*****************************************************************************/
+/*
+ * Remove and return a value from a hash table
+ */
+void *psHashRemove(psHash *table,	// table to lookup key in
+		   const char *key)	// key to lookup
+{
+    return doHashWork(table, key, NULL, 1, NULL);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/logmsg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/logmsg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/logmsg.c	(revision 22322)
@@ -0,0 +1,228 @@
+/*
+ * A simple implementation of a logging facility for Pan-STARRS
+ */
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <unistd.h>
+#include "psLib.h"
+
+/*****************************************************************************/
+
+static int logDest = PS_LOG_TO_STDERR;	// where to log messages
+static int logLevel = PS_LOG_INFO;	// log all messages at this or above
+
+/*****************************************************************************/
+/*
+ * Set the current log level and return old level
+ */
+int psLogSetLevel(int level)		// new level
+{
+    int oldLevel = logLevel;
+
+    if (level < 0 || level > 9) {
+	psLogMsg("utils.logmsg", PS_LOG_WARN,
+		 "Attempt to set invalid logMsg level: %d", level);
+	level = (level < 0) ? 0 : 9;
+    }
+
+    logLevel = level;
+
+    return oldLevel;
+}
+
+/*****************************************************************************/
+
+int psLogSetDestination(int dest)	// new destination
+{
+    int old = logDest;
+
+    switch (dest) {
+      case PS_LOG_NONE:
+      case PS_LOG_TO_STDOUT:
+      case PS_LOG_TO_STDERR:
+	logDest = dest;
+	break;
+
+      default:
+	fprintf(stderr,"Unknown logDestination: %d (ignored)\n", dest);
+	break;
+    }
+
+    return old;
+}
+
+/*****************************************************************************/
+/*
+ * Set the format of psLogMsg output.
+ *
+ * More precisely, provide a string consisting of the letters H (host), L (level), M (message),
+ * N (name), and T (time).  The default is "HLMNT"
+ */
+static int log_time = 1, log_host = 1, log_level = 1, log_name = 1, log_msg = 1;
+
+void psLogSetFormat(const char *fmt)
+{
+    int nlog_time, nlog_host, nlog_level, nlog_name, nlog_msg; // new values of log_time etc.
+
+    nlog_host = nlog_level = nlog_msg = nlog_name = nlog_time = 0; // don\'t log anything
+    for (const char *ptr = fmt; *ptr != '\0'; ptr++) {
+	switch (*ptr) {
+	  case 'H': case 'h':
+	    nlog_host = 1;
+	    break;
+	  case 'L': case 'l':
+	    nlog_level = 1;
+	    break;
+	  case 'M': case 'm':
+	    nlog_msg = 1;
+	    break;
+	  case 'N': case 'n':
+	    nlog_name = 1;
+	    break;
+	  case 'T': case 't':
+	    nlog_time = 1;
+	    break;
+	  default:
+	    psError(__func__, PS_ERR_UNKNOWN, 1, "Unknown logging keyword %c", *ptr);
+	    break;
+	}
+    }
+
+    if (!nlog_msg) {
+	psTrace("utils.logMsg", 1, "You must at least log error messages (You chose \"%s\")", fmt);
+    }
+
+    log_host = nlog_host;
+    log_level = nlog_level;
+    log_msg = nlog_msg;
+    log_name = nlog_name;
+    log_time = nlog_time;
+}
+
+
+/*****************************************************************************/
+#if !defined(HOST_NAME_MAX)		// should be in limits.h
+#  define HOST_NAME_MAX 256
+#endif
+
+void psLogMsgV(const char *name, int level, const char *fmt, va_list ap)
+{
+    static int first = 1;
+    static char hostname[HOST_NAME_MAX + 1];
+     
+    if (level > logLevel || logDest == PS_LOG_NONE) {
+	return;
+    }
+
+    if (first) {
+	first = 0;
+	gethostname(hostname, HOST_NAME_MAX);
+    }
+    
+    char clevel;			// letter-name for level
+    switch (level) {
+      case PS_LOG_ABORT:
+	clevel = 'A';
+	break;
+	
+      case PS_LOG_ERROR:
+	clevel = 'E';
+	break;
+	
+      case PS_LOG_WARN:
+	clevel = 'W';
+	break;
+	
+      case PS_LOG_INFO:
+	clevel = 'I';
+	break;
+
+      case 4:
+      case 5:
+      case 6:
+      case 7:
+      case 8:
+      case 9:
+	clevel = level + '0';
+	break;
+
+      default:
+	psTrace("utils.logMsg", 2, "Invalid logMsg level: %d (%s)\n",
+		level, fmt);
+	level = (level < 0) ? 0 : 9;
+	break;
+    }
+
+    char head[HOST_NAME_MAX + 40];	// yes, this is long enough
+    char *head_ptr = head;		// where we\'ve got to in head
+    time_t clock = time(NULL);
+    struct tm *utc = gmtime(&clock);
+
+    if (log_time) {
+	sprintf(head_ptr, "%4d:%02d:%02d %02d:%02d:%02dZ",
+		utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday,
+		utc->tm_hour, utc->tm_min, utc->tm_sec);
+	head_ptr += strlen(head_ptr);
+    }
+    if (log_host) {
+	if (head_ptr > head) {
+	    *head_ptr++ = '|';
+	}
+	sprintf(head_ptr, "%-20s", hostname);
+	head_ptr += strlen(head_ptr);
+    }
+    if (log_level) {
+	if (head_ptr > head) {
+	    *head_ptr++ = '|';
+	}
+	sprintf(head_ptr, "%c", clevel);
+	head_ptr += strlen(head_ptr);
+    }
+    if (log_name) {
+	if (head_ptr > head) {
+	    *head_ptr++ = '|';
+	}
+	sprintf(head_ptr, "%-15s", name);
+	head_ptr += strlen(head_ptr);
+    }
+    if (head_ptr > head) {
+	*head_ptr++ = '|';
+    }
+    *head_ptr = '\0';
+    
+    switch (logDest) {
+      case PS_LOG_TO_STDOUT:
+	puts(head);
+	vprintf(fmt, ap);
+	if (fmt[strlen(fmt) - 1] != '\n') {
+	    putc('\n', stdout);
+	}
+	break;
+
+      case PS_LOG_TO_STDERR:
+	fputs(head, stderr);
+	vfprintf(stderr, fmt, ap);
+	if (fmt[strlen(fmt) - 1] != '\n') {
+	    putc('\n', stderr);
+	}
+	break;
+
+      default:
+	fprintf(stderr, "psLogMsg: You cannot get here (%s:%d)\n",
+		__FILE__, __LINE__);
+	abort();
+    }
+}
+
+void psLogMsg(const char *name, int level, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+
+    psLogVMsg(name, level, fmt, ap);
+    va_end(ap);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/memory.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/memory.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/memory.c	(revision 22322)
@@ -0,0 +1,436 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#define PS_ALLOW_MALLOC			// we're allowed to call malloc()
+#include "psLib.h"
+#include "Private/p_psMemory.h"
+
+#undef psAlloc				// don't get the __FILE__ versions
+#undef psRealloc
+#undef psFree
+
+static int bad_memblock(const psMemBlock *m, int quiet);
+/*
+ * This strawman allocation package uses an array to track calls to psAlloc;
+ * not nearly good enough for production work!
+ */
+static int nMemBlock = 0;		// size of memBlocks
+static psMemBlock **memBlocks = NULL;
+
+/*****************************************************************************/
+/*
+ * Unique ID for allocated blocks
+ */
+static unsigned long memid = 0;
+
+/*****************************************************************************/
+
+void *psAlloc(size_t size)
+{
+    return p_psAlloc(size, "(unknown)", 0);
+}
+
+void *psRealloc(void *ptr, size_t size)
+{
+    return p_psRealloc(ptr, size, "(unknown)", 0);
+}
+
+void psFree(void *ptr)
+{
+    p_psFree(ptr, "(unknown)", 0);
+}
+
+/*****************************************************************************/
+/*
+ * Callbacks
+ */
+/*
+ * First the I-can't-get-the-memory callback
+ */
+static void *memExhaustedCallback0(size_t size)
+{
+    return NULL;
+}
+static psMemExhaustedCallback memExhaustedCallback = memExhaustedCallback0;
+
+psMemExhaustedCallback psMemExhaustedCallbackSet(psMemExhaustedCallback func)
+{
+    psMemExhaustedCallback old = memExhaustedCallback;
+
+    memExhaustedCallback = (func != NULL) ? func : memExhaustedCallback0;
+
+    return old;
+}
+/*
+ * then the I-have-detected-a-problem callback
+ */
+static void memProblemCallback0(const psMemBlock *ptr,
+			  const char *file, int lineno)
+{
+    if (bad_memblock(ptr, 0)) {
+	fprintf(stderr, "Memory corruption or an attempt to manipulate memory "
+		"not allocated with psAlloc at %s", file);
+	if (lineno > 0) {
+	    fprintf(stderr, ":%d", lineno);
+	}
+	fprintf(stderr, "\n");
+    } else if (ptr->refCounter <= 0) {
+	psError(__func__, PS_ERR_BADFREE, 1,
+		"Block %ld allocated at %s:%d freed more than once at %s:%d\n",
+		ptr->id, ptr->file, ptr->lineno, file, lineno);
+    } else if (ptr->refCounter > 1) {
+	psError(__func__,  PS_ERR_BADFREE, 1,
+		"Block %ld allocated at %s:%d freed while still referenced at %s:%d\n",
+		ptr->id, ptr->file, ptr->lineno, file, lineno);
+    }
+
+    if (lineno > 0) {
+	psAbort(__func__, "Detected a problem in the memory system at %s:%d", file, lineno);
+    }
+}
+static psMemProblemCallback memProblemCallback = memProblemCallback0;
+
+psMemProblemCallback psMemProblemCallbackSet(psMemProblemCallback func)
+{
+    psMemProblemCallback old = memProblemCallback;
+
+    memProblemCallback = (func != NULL) ? func : memProblemCallback0;
+
+    return old;
+}
+/*
+ * And now the I-want-to-be-informed callbacks
+ *
+ * Call the callbacks when these IDs are allocated/freed
+ */
+long p_psMemAllocateID = 0;		// notify user this block is allocated
+long p_psMemFreeID = 0;			// notify user this block is freed
+
+long psMemAllocateIDSet(long id)	// set p_psMemAllocateID to id
+{
+    long old = p_psMemAllocateID;
+    p_psMemAllocateID = id;
+
+    return old;
+}
+
+long psMemFreeIDSet(long id)		// set p_psMemFreeID to id
+{
+    long old = p_psMemFreeID;
+    p_psMemFreeID = id;
+
+    return old;
+}
+
+/*
+ * Default callback for both allocate and free. Note that the
+ * value of p_psMemAllocateID/p_psMemFreeID is incremented
+ * by the return value (so returning 0 means that the callback
+ * isn't resignalled)
+ */
+static long memAllocateCallback0(const psMemBlock *ptr)
+{
+    static int incr = 0;		// "p_psMemAllocateID += incr"
+
+    assert (ptr != NULL);		// prevent compiler warnings
+    
+    return incr;
+}
+
+static long memFreeCallback0(const psMemBlock *ptr)
+{
+    static int incr = 0;		// "p_psMemFreeID += incr"
+
+    assert (ptr != NULL);		// prevent compiler warnings
+    
+    return incr;
+}
+
+/*
+ * The default callbacks, and the routines to change them
+ */
+static psMemAllocateCallback memAllocateCallback = memAllocateCallback0;
+static psMemFreeCallback memFreeCallback = memFreeCallback0;
+
+psMemAllocateCallback psMemAllocateCallbackSet(psMemAllocateCallback func)
+{
+    psMemAllocateCallback old = memAllocateCallback;
+
+    memAllocateCallback = (func != NULL) ? func : memAllocateCallback0;
+
+    return old;
+}
+
+psMemFreeCallback psMemFreeCallbackSet(psMemFreeCallback func)
+{
+    psMemFreeCallback old = memFreeCallback;
+
+    memFreeCallback = (func != NULL) ? func : memFreeCallback0;
+
+    return old;
+}
+
+/*****************************************************************************/
+/*
+ * Return memory ID counter for next block to be allocated
+ */
+int psMemGetId(void)
+{
+    return memid + 1;
+}
+
+/*****************************************************************************/
+/*
+ * Routines to check the consistency of the allocated and/or free memory arena
+ *
+ * N.b. If the block wasn't allocated by psAlloc, it will appear corrupted
+ */
+#define ALIGNED(P) ((void *)((long)(P) & ~03) == (P))
+
+static int
+bad_memblock(const psMemBlock *m,	// block to check
+	     int quiet)			// be quiet?
+{
+    if (m == NULL) {
+	if (!quiet) {
+	    psError(__func__,  PS_ERR_MEMORY_CORRUPTION, 1,
+		    "psMemCheckCorruption: NULL memory block\n");
+	}
+	return(1);
+    }
+    
+    if (!ALIGNED(m)) {
+	if (!quiet) {
+	    psError(__func__,  PS_ERR_MEMORY_CORRUPTION, 1,
+		    "psMemCheckCorruption: non-aligned memory block\n");
+	}
+	return(1);
+    }
+    
+    if (m->startblock != P_PS_MEMMAGIC || m->endblock != P_PS_MEMMAGIC) {
+	if (!quiet) {
+	    psError(__func__, PS_ERR_MEMORY_CORRUPTION, 1,
+		    "psMemCheckCorruption: memory block %ld is corrupted\n", m->id);
+	}
+	return(1);
+    }
+
+    return(0);
+}
+
+int psMemCheckCorruption(int abort_on_error)
+{
+    int nbad = 0;			// number of bad blocks
+
+    if (memBlocks == NULL) {
+	return 0;			// no memory is allocated to be corrupted
+    }
+
+    for (int i = 1; i <= memid; i++) {
+	if (memBlocks[i] != NULL) {
+	    if (bad_memblock(memBlocks[i], 1)) {
+		nbad++;
+
+		memProblemCallback(memBlocks[i], "(psMemCheckCorruption)", 0);
+		
+		if (abort_on_error) {
+		    psAbort(__func__, "Detected memory corruption");
+		}
+	    }
+	}
+    }
+
+    return nbad;
+}
+
+/*****************************************************************************/
+
+void *p_psAlloc(size_t size, const char *file, int lineno)
+{
+    psMemBlock *ptr = malloc(sizeof(psMemBlock) + size);
+
+    if (ptr == NULL) {
+	ptr = memExhaustedCallback(size);
+	if (ptr == NULL) {
+	    psAbort(__func__, "Failed to allocate %ld bytes at %s:%d",
+		    size, file, lineno);
+	}
+    }
+
+    *(unsigned long *)&ptr->id = ++memid;
+    ptr->file = file;
+    *(int *)&ptr->lineno = lineno;
+    *(void **)&ptr->startblock = *(void **)&ptr->endblock = P_PS_MEMMAGIC;
+    ptr->endpost = &ptr->endblock;	// should point just beyond allocated memory XXX
+
+    ptr->refCounter = 1;		// one user so far
+    /*
+     * Did the user ask to be informed about this allocation?
+     */
+    
+    if (ptr->id == p_psMemAllocateID && p_psMemAllocateID > 0) {
+	p_psMemAllocateID += memAllocateCallback(ptr);	
+    }
+    /*
+     * Register the allocation.  This is not a production quality algorithm!
+     */
+    if (memid >= nMemBlock) {
+	nMemBlock = (nMemBlock == 0) ? 10000 : 2*nMemBlock;
+	memBlocks = realloc(memBlocks, nMemBlock*sizeof(psMemBlock *)); // NOT psRealloc
+    }
+    memBlocks[ptr->id] = ptr;
+    /*
+     * And return the user the memory that they allocated
+     */
+    return ptr + 1;			// usr memory
+}
+
+void *p_psRealloc(void *vptr, size_t size, const char *file, int lineno)
+{
+    if (vptr == NULL) {
+	return p_psAlloc(size, file, lineno);
+    } else {
+	psMemBlock *ptr = ((psMemBlock *)vptr) - 1;
+	ptr = (psMemBlock *)realloc(ptr, sizeof(psMemBlock) + size);
+	memBlocks[ptr->id] = ptr;
+	
+	return ptr + 1;			// usr memory
+    }
+}
+
+void p_psFree(void *vptr, const char *file, int lineno)
+{
+    if (vptr == NULL) {
+	return;
+    }
+
+    psMemBlock *ptr = ((psMemBlock *)vptr) - 1;
+    /*
+     * Did the user ask to be informed about this deallocation?
+     */
+    if (ptr->id == p_psMemFreeID && p_psMemFreeID > 0) {
+	p_psMemFreeID += memFreeCallback(ptr);	
+    }
+
+    if (bad_memblock(ptr, 0)) {
+	memProblemCallback(ptr, file, lineno); // we may not own this block; don't free it
+    } else {
+	if (ptr->refCounter != 1) {
+	    memProblemCallback(ptr, file, lineno);
+	}
+	ptr->refCounter--;
+#if 1
+	memBlocks[ptr->id] = NULL;	// XXX sub-optimal! Can't check
+					// freed blocks for corruption/double frees
+	free(ptr);
+#endif
+    }
+}
+
+/*****************************************************************************/
+/*
+ * Check for memory leaks. Not production quality code
+ */
+int psMemCheckLeaks(
+    int id0,				// don't list blocks with id < id0
+    psMemBlock ***arr,			// pointer to array of pointers to
+					// leaked blocks, or NULL
+    FILE *fd)				// print list of leaks to fd (or NULL)
+{
+    int nleak = 0;
+
+    if (memBlocks == NULL) {		// nothing was allocated
+	return 0;
+    }
+
+    for (int i = id0; i <= memid; i++) {
+	if (memBlocks[i] != NULL) {
+	    if (memBlocks[i]->refCounter > 0) {
+		nleak++;
+
+		if (fd != NULL) {
+		    if (nleak == 1) {
+			fprintf(fd, "   %20s:line ID\n", "file");
+		    }
+
+		    fprintf(fd, "   %20s:%-4d %ld\n",
+			    memBlocks[i]->file, memBlocks[i]->lineno,
+			    memBlocks[i]->id);
+		}
+	    }
+	}
+    }
+
+    if (nleak == 0 || arr == NULL) {
+	return nleak;
+    }
+
+    assert (*arr == NULL);		// Don't generate a memory leak!
+
+    *arr = p_psAlloc(nleak*sizeof(psMemBlock), __FILE__, __LINE__);
+    for (int i = id0, j = 0; i < memid; i++) {
+	if (memBlocks[i] != NULL) {
+	    if (memBlocks[i]->refCounter > 0) {
+		(*arr)[j++] = memBlocks[i];
+		if (j == nleak) {	// found them all
+		    break;
+		}
+	    }
+	}
+    }
+
+    return nleak;
+}
+
+/*****************************************************************************/
+/*
+ * Reference counting APIs
+ */
+int psMemGetRefCounter(void *vptr)	// return refCounter
+{
+    if (vptr == NULL) {
+	return 0;
+    }
+
+    psMemBlock *ptr = ((psMemBlock *)vptr) - 1;
+
+    if (bad_memblock(ptr, 0)) {
+	memProblemCallback(ptr, "(psMemGetRefCounter)", -1);
+    }
+
+    return ptr->refCounter;
+}
+
+void *psMemIncrRefCounter(void *vptr)	// increment and return refCounter
+{
+    if (vptr == NULL) {
+	return vptr;
+    }
+		 
+    psMemBlock *ptr = ((psMemBlock *)vptr) - 1;
+
+    if (bad_memblock(ptr, 0)) {
+	memProblemCallback(ptr, "(psMemIncrRefCounter)", -1);
+    }
+
+    ptr->refCounter++;
+
+    return vptr;
+}
+
+void *psMemDecrRefCounter(void *vptr)	// decrement and return refCounter
+{
+    if (vptr == NULL) {
+	return vptr;
+    }
+
+    psMemBlock *ptr = ((psMemBlock *)vptr) - 1;
+
+    if (bad_memblock(ptr, 0)) {
+	memProblemCallback(ptr, "(psMemDecrRefCounter)", -1);
+    }
+
+    ptr->refCounter--;
+
+    return vptr;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/misc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/misc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/misc.c	(revision 22322)
@@ -0,0 +1,35 @@
+/*
+ * A set of miscellaneous utilities for Pan-STARRS
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include "psLib.h"
+
+/*****************************************************************************/
+/*
+ * Return a copy of a string
+ */
+char *psStringCopy(const char *str)
+{
+    return strcpy(psAlloc(strlen(str) + 1), str);
+}
+
+/*****************************************************************************/
+/*
+ * Send a psLogFatal message and abort
+ */
+void psAbort(const char *name, const char *fmt, ...)
+{
+    va_list ap;
+    
+    va_start(ap, fmt);
+    psErrorStackPrint(stderr, "Error traceback:");
+    va_end(ap);
+
+    va_start(ap, fmt);
+    psLogMsgV(name, PS_LOG_ABORT, fmt, ap);
+    va_end(ap);
+    
+    abort();
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/psArray.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/psArray.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/psArray.h	(revision 22322)
@@ -0,0 +1,113 @@
+#if !defined(PS_ARRAY_H)
+#define PS_ARRAY_H
+
+/**
+ * Declare TYPEArray
+ */
+#define PS_DECLARE_ARRAY_TYPE(TYPE) \
+    typedef struct { \
+	psType type;			/* Type of data.  THIS STRUCT ELEMENT MUST BE FIRST IN THE STRUCT */\
+	int nalloc;			/* Total number of elements available */\
+	int n;				/* Number of elements in use */\
+	TYPE *arr;			/* the actual data */ \
+    } PS_CONCAT(TYPE, Array); \
+    \
+PS_CONCAT(TYPE, Array) *PS_CONCAT3(TYPE,Array,Alloc)(int n); \
+PS_CONCAT(TYPE, Array) *PS_CONCAT3(TYPE,Array,Realloc)(PS_CONCAT(TYPE, Array) *arr, int n); \
+void PS_CONCAT3(TYPE,Array,Free)(PS_CONCAT(TYPE, Array) *arr)
+
+/*****************************************************************************/
+/**
+ * Generate the code TYPEArray's constructors/destructors
+ */
+#define PS_CREATE_ARRAY_TYPE(TYPE) \
+    P_PS_CREATE_ARRAY_TYPE(,,TYPE)
+
+#define P_PS_CREATE_ARRAY_TYPE(QUAL,PREFIX,TYPE) \
+\
+QUAL PS_CONCAT(TYPE, Array) *PS_CONCAT4(PREFIX,TYPE,Array,Alloc)(int n) /* initial dimension */ \
+{ \
+    PS_CONCAT(TYPE, Array) *arr = psAlloc(sizeof(PS_CONCAT(TYPE, Array))); \
+    \
+    arr->nalloc = arr->n = n; \
+    \
+    if (n == 0) { \
+	arr->arr = NULL; \
+    } else { \
+	arr->arr = psAlloc(n*sizeof(TYPE)); \
+    } \
+    \
+    return arr; \
+} \
+\
+QUAL PS_CONCAT(TYPE, Array) *PS_CONCAT4(PREFIX,TYPE,Array,Realloc)(PS_CONCAT(TYPE, Array) *arr, int n) \
+{ \
+    if (arr == NULL) { \
+	return PS_CONCAT4(PREFIX,TYPE,Array,Alloc)(n); \
+    } \
+    \
+    if (n <= arr->nalloc) { \
+	if (arr->n < n) { \
+	    arr->n = n; \
+	} \
+    } else { \
+        arr->arr = psRealloc(arr->arr, n*sizeof(TYPE)); \
+	arr->nalloc = n; \
+    } \
+    \
+    return arr; \
+} \
+\
+QUAL void PS_CONCAT4(PREFIX,TYPE,Array,Free)(PS_CONCAT(TYPE,Array) *arr) \
+{ \
+    if (arr == NULL) { \
+	return; \
+    } \
+    \
+    psFree(arr->arr); \
+    psFree(arr); \
+}
+
+/*****************************************************************************/
+/**
+ * Declare array of pointers
+ */
+#define PS_DECLARE_ARRAY_PTR_TYPE(TYPE) \
+    typedef TYPE *P_PS_CONCAT(TYPE, Ptr); \
+    PS_DECLARE_ARRAY_TYPE(PS_CONCAT(TYPE, Ptr))
+
+/**
+ * Create constructors/destructors for array of pointers
+ */
+#define PS_CREATE_ARRAY_PTR_TYPE(TYPE); \
+    P_PS_CREATE_ARRAY_TYPE(static, my_, P_PS_CONCAT(TYPE, Ptr)) \
+    \
+    PS_CONCAT(TYPE, PtrArray) *PS_CONCAT3(TYPE,PtrArray,Alloc)(int n) \
+    { \
+	PS_CONCAT(TYPE, PtrArray) *arr = PS_CONCAT4(my_,TYPE,PtrArray,Alloc)(n); \
+	for (int i = 0; i < arr->n; i++) { \
+	    arr->arr[i] = psMemIncrRefCounter(PS_CONCAT(TYPE, Alloc)());\
+	} \
+        \
+	return arr; \
+    } \
+    \
+    PS_CONCAT(TYPE, PtrArray) *PS_CONCAT3(TYPE,PtrArray,Realloc)(PS_CONCAT(TYPE, PtrArray) *arr, int n) \
+    { \
+	for (int i = n; i < arr->n; i++) { \
+	    PS_CONCAT(TYPE, Free)(psMemDecrRefCounter(arr->arr[i]));\
+	} \
+        \
+	return PS_CONCAT4(my_,TYPE,PtrArray,Realloc)(arr, n); \
+    } \
+    \
+    void PS_CONCAT3(TYPE,PtrArray,Free)(PS_CONCAT2(TYPE,PtrArray) *arr) \
+    { \
+	if (arr == NULL) { return; } \
+	for (int i = 0; i < arr->nalloc; i++) { \
+	    PS_CONCAT(TYPE, Free)(psMemDecrRefCounter(arr->arr[i])); \
+	} \
+	PS_CONCAT4(my_, TYPE, PtrArray, Free)(arr); \
+    }
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/psErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/psErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/psErrorCodes.dat	(revision 22322)
@@ -0,0 +1,11 @@
+#
+# This file is used to generate psErrorClasses.h
+#
+NONE = 0		no error
+BASE = 256		first value we use; should avoid errno conflicts
+UNKNOWN			unknown error
+IO			I/O error
+BADFREE			bad argument to psFree()
+MEMORY_CORRUPTION	memory corruption detected
+MEMORY_EXHAUSTED	unable to allocate requested memory
+BAD_INDEX		index is out of range
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/trace.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/trace.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/trace.c	(revision 22322)
@@ -0,0 +1,326 @@
+/*
+ * A simple implementation of a tracing facility for Pan-STARRS
+ *
+ * Tracing is controlled on a per "component" basis, where a "component" is a
+ * name of the form aaa.bbb.ccc where aaa is the Most significant part; for
+ * example, the utilities library might be called "utils", the doubly-linked
+ * list "utils.dlist", and the code to destroy a list "utils.dlist.del" 
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "psLib.h"
+
+/*****************************************************************************/
+/*
+ * A component is a string of the form aaa.bbb.ccc, and may itself
+ * contain further subcomponents. The Component structure doesn't
+ * in fact contain it's full name, but only the last part.
+ */
+typedef struct Component {
+    const char *name;			// last part of name of this component
+    int level;				// trace level for this component
+    int dimen;				// dimension of subcomp
+    int n;				// number of subcomponents
+    struct Component **subcomp;		// next level of subcomponents
+} Component;
+
+static int highest_level = 0;		// highest level requested
+/*
+ * tracelevel cache
+ */
+#define CACHE_NAME_LEN 50
+#define NO_CACHE "\a"			// no name is cached
+
+#define UNKNOWN_LEVEL -9999		// we don\'t know this name\'s level
+
+static char cachedName[CACHE_NAME_LEN + 1] = NO_CACHE; // last name looked up
+static int cachedLevel;			// level of last looked up name
+
+/*****************************************************************************/
+
+static Component *componentAlloc(const char *name,
+				 int level)
+{
+    Component *comp = psAlloc(sizeof(Component));
+
+    comp->name = psStringCopy(name);
+    comp->level = level;
+    comp->dimen = comp->n = 0;
+    comp->subcomp = NULL;
+
+    return comp;
+}
+
+static void componentFree(Component *comp)
+{
+    if (comp == NULL) { return; }
+
+    if (comp->subcomp != NULL) {
+	for (int i = 0; i < comp->n; i++) {
+	    componentFree(comp->subcomp[i]);
+	}
+	psFree(comp->subcomp);
+    }
+
+    psFree((char *)comp->name);
+    psFree(comp);
+}
+
+/*****************************************************************************/
+/*
+ * The root of the trace component tree
+ */
+static Component *croot = NULL;
+
+static void initTrace(void) {
+    if (croot == NULL) {
+	croot = componentAlloc("", UNKNOWN_LEVEL);
+    }
+}
+
+/*****************************************************************************/
+/*
+ * Free the trace tree information
+ */
+void psTraceReset(void)
+{
+    componentFree(croot);
+    croot = NULL;
+}
+
+/*****************************************************************************/
+/*
+ * Add a new component to the tree
+ */
+static void componentAdd(Component *comp,
+			 const char *aname,
+			 int level)
+{
+    char name[strlen(aname) + 1];	// need a writeable copy
+    strcpy(name, aname);
+
+    char *cpt0;				// first component of name
+    char *rest = name;			// rest of name
+    cpt0 = strsep(&rest, ".");
+    /*
+     * Does cpt0 match this level?
+     */
+    if (strcmp(comp->name, cpt0) == 0) { // a match
+	if (rest == NULL) {
+	    comp->level = level;
+	} else {
+	    componentAdd(comp, rest, level);
+	}
+	
+	return;
+    }
+    /*
+     * Look for a match for cpt0 in this level's subcomps
+     */
+    for (int i = 0; i < comp->n; i++) {
+	if (strcmp(comp->subcomp[i]->name, cpt0) == 0) { // a match
+	    if (rest == NULL) {
+		comp->subcomp[i]->level = level;
+	    } else {
+		componentAdd(comp->subcomp[i], rest, level);
+	    }
+
+	    return;
+	}
+    }
+    /*
+     * No match; add cpt0 to this level. Ensure that subcomp is sorted
+     */
+    if (comp->n >= comp->dimen - 1) {
+	comp->dimen += 5;
+	comp->subcomp = psRealloc(comp->subcomp, comp->dimen*sizeof(Component *));
+    }
+
+    Component *fcpt0 = componentAlloc(cpt0, UNKNOWN_LEVEL);
+    int i;
+    for (i = 0; i < comp->n; i++) {
+	if (strcmp(cpt0, comp->subcomp[i]->name) > 0) {
+	    for (int j = comp->n; j > i; j--) {
+		comp->subcomp[j] = comp->subcomp[j - 1];
+	    }
+	    break;
+	}
+    }
+    comp->subcomp[i] = fcpt0;
+    comp->n++;
+
+    if (rest == NULL) {
+	fcpt0->level = level;
+    } else {
+	componentAdd(fcpt0, rest, level);
+    }
+}
+
+/*****************************************************************************/
+/*
+ * Find the highest level present in the tree
+ */
+static void setHighestLevel(const Component *comp)
+{
+    if (comp->level > highest_level) {
+	highest_level = comp->level;
+    }
+    
+    for (int i = 0; i < comp->n; i++) {
+	setHighestLevel(comp->subcomp[i]);
+    }
+}
+
+/*****************************************************************************/
+
+int psTraceSetLevel(const char *comp,	// component of interest
+		    int level)		// desired trace level
+{
+    if (croot == NULL) {
+	initTrace();
+    }
+
+    strncpy(cachedName, NO_CACHE, CACHE_NAME_LEN); // invalidate cache
+    
+    componentAdd(croot, comp, level);
+
+    if (level > highest_level) {
+	highest_level = level;
+    } else {
+	highest_level = UNKNOWN_LEVEL;
+	setHighestLevel(croot);
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+/*
+ * Return a trace level given a name
+ */
+static int doTraceGetLevel(const Component *comp, // end of component to search
+			   const char *aname) // name to find
+{
+    char name[strlen(aname) + 1];	// need a writeable copy
+    strcpy(name, aname);
+
+    char *cpt0;				// first component of name
+    char *rest = name;			// rest of name
+    cpt0 = strsep(&rest, ".");
+    /*
+     * Look for a match for cpt0 in this level's subcomps
+     */
+    for (int i = 0; i < comp->n; i++) {
+	if (strcmp(comp->subcomp[i]->name, cpt0) == 0) { // a match
+	    if (rest == NULL) {		// we\'re there
+		int level = comp->subcomp[i]->level;
+
+		return (level == UNKNOWN_LEVEL) ? comp->level : level;
+	    } else {
+		return doTraceGetLevel(comp->subcomp[i], rest);
+	    }
+	}
+    }
+    /*
+     * No match. This is as far as she goes
+     */
+    return comp->level;
+}
+
+int psTraceGetLevel(const char *name)	// component of interest
+{
+    if (croot == NULL) {
+	initTrace();
+    }
+
+    if (strcmp(name, cachedName) == 0) {
+	return cachedLevel;
+    }
+
+    int level = doTraceGetLevel(croot, name);
+
+    strncpy(cachedName, name, CACHE_NAME_LEN);
+    cachedLevel = level;
+
+    return level;
+}
+
+/*****************************************************************************/
+/*
+ * Print a tree of trace levels
+ */
+static void doTracePrintLevels(const Component *comp, int depth)
+{
+    if (comp->name[0] == '\0') {
+	printf("%*s%-*s %d\n", depth, "", 20 - depth,
+	       "(root)", (comp->level == UNKNOWN_LEVEL) ? 0 : comp->level);
+    } else {
+	if (comp->level == UNKNOWN_LEVEL) {
+	    printf("%*s%-*s %s\n", depth, "", 20 - depth,
+		   comp->name, ".");
+	} else {
+	    printf("%*s%-*s %d\n", depth, "", 20 - depth,
+		   comp->name, comp->level);
+	}
+    }
+    
+    for (int i = 0; i < comp->n; i++) {
+       doTracePrintLevels(comp->subcomp[i], depth + 1);
+    }
+}
+
+void psTracePrintLevels(void)
+{
+    if (croot == NULL) {
+	initTrace();
+    }
+
+    doTracePrintLevels(croot, 0);
+}
+
+/*****************************************************************************/
+/*
+ * Change where we're writing to; close previous file descriptor
+ * if it isn't stdout/stderr
+ */
+static FILE *traceFD = NULL;
+
+void psTraceSetDestination(FILE *fp)
+{
+   if (traceFD != stdout && traceFD != stderr) {
+      fclose(traceFD);
+   }
+
+   traceFD = fp;
+}
+
+
+/*****************************************************************************/
+/*
+ * Actually generate the trace message. Note that psTrace dealt with
+ * prepending appropriate indentation
+ */
+void p_psTrace(const char *comp,	// component being traced
+	       int level,		// desired trace level
+	       ...)			// arguments
+{
+    if (level <= highest_level && psTraceGetLevel(comp) >= level) {
+        if (traceFD == NULL) {		// not initialised, or fopen failed
+	   traceFD = stdout;
+	}
+
+	va_list ap;
+	va_start(ap, level);
+	for (int i = 0; i < level; i++) {
+	    fputc(' ', traceFD);
+	}
+	char *fmt = va_arg(ap, char *);
+	vfprintf(traceFD, fmt, ap);
+	if (strchr(fmt, '\n') == NULL) {
+	    fputc('\n', traceFD);
+	}
+	va_end(ap);
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_array.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_array.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_array.c	(revision 22322)
@@ -0,0 +1,53 @@
+#include "psLib.h"
+#include "psArray.h"
+
+typedef struct {
+    int x, y;
+} psXY;
+
+psXY *psXYAlloc(void)
+{
+    return psAlloc(sizeof(psXY));
+}
+
+void psXYFree(psXY *xy)
+{
+    psFree(xy);
+}
+
+PS_DECLARE_ARRAY_TYPE(psXY);
+PS_CREATE_ARRAY_TYPE(psXY);
+
+PS_DECLARE_ARRAY_PTR_TYPE(psXY);
+PS_CREATE_ARRAY_PTR_TYPE(psXY);
+
+int main(void)
+{
+    psXYArray *t = psXYArrayAlloc(10);
+    psXYPtrArray *pt = psXYPtrArrayAlloc(10);
+
+    for (int i = 0; i < t->n; i++) {
+	t->arr[i].x = i;
+	pt->arr[i]->y = 10*i;
+    }
+
+    t = psXYArrayRealloc(t, 5);
+    t = psXYArrayRealloc(t, 8);
+
+    for (int i = 0; i < t->n; i++) {
+	printf("%d %d  ", t->arr[i].x, pt->arr[i]->y);
+    }
+    printf("\n");
+    
+    psXYArrayFree(t);
+
+    psXY *xy = psMemDecrRefCounter(pt->arr[0]);
+    pt->arr[0] = NULL;
+    psXYFree(xy);    
+    
+    psXYPtrArrayFree(pt);
+
+    psMemCheckLeaks(0, NULL, stderr);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_dlist.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_dlist.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_dlist.c	(revision 22322)
@@ -0,0 +1,80 @@
+//#include <stdlib.h>
+#include <stdio.h>
+#include "psLib.h"
+
+
+typedef struct { int i; } X;
+static X *xAlloc(int i) { X *x = psAlloc(sizeof(X)); x->i = i; return x; }
+static void xFree(X *x) { psFree(x); }
+
+int main(void)
+{
+    psDlist *list = psDlistAlloc(NULL);	// list of metadata
+
+    psDlistAppend(list, xAlloc(1));
+    psDlistAppend(list, xAlloc(2));
+    psDlistAppend(list, xAlloc(3));
+    /*
+     * Print that data
+     */
+    for (psDlistElem *ptr = list->head; ptr != NULL; ptr = ptr->next) {
+	printf("%d ", ((X *)(ptr->data))->i);
+    }
+    printf("\n");
+    /*
+     * Print it again
+     */
+    (void)psDlistGet(list, PS_DLIST_TAIL); // initialise iterator
+    X *x = NULL;
+    while ((x = psDlistGet(list, PS_DLIST_PREV)) != NULL) {
+	printf("%d ", x->i);
+    }
+    printf("\n");
+#if 1
+    /*
+     * Convert to an array
+     */
+    psVector *arr = psDlistToArray(list);
+    list = NULL;			// it\'s been freed
+
+    for (int i = 0; i < arr->n; i++) {
+	printf("%d ", ((X *)(arr->arr.arr_v[i]))->i);
+    }
+    printf("\n");
+#if 1
+    /*
+     * Convert back to a list
+     */
+    list = psArrayToDlist(arr);
+    arr = NULL;				// it\'s been freed
+
+    (void)psDlistGet(list, PS_DLIST_TAIL); // initialise iterator
+    while ((x = psDlistGet(list, PS_DLIST_PREV)) != NULL) {
+	printf("%d ", x->i);
+    }
+    printf("\n");
+#endif
+    psVectorFree(arr, (void (*)(void *))xFree);
+#endif
+
+#if 1
+    list = psArrayToDlist(psDlistToArray(list));
+
+    (void)psDlistGet(list, PS_DLIST_TAIL); // initialise iterator
+    while ((x = psDlistGet(list, PS_DLIST_PREV)) != NULL) {
+	printf("%d ", x->i);
+    }
+    printf("\n");
+#endif
+    /*
+     * Clean up
+     */
+    psDlistFree(list, (void (*)(void *))xFree);
+    /*
+     * Check for memory leaks
+     */
+    fprintf(stderr,"Checking for memory leaks:\n");
+    psMemCheckLeaks(0, NULL, stderr);
+    
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_error.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_error.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_error.c	(revision 22322)
@@ -0,0 +1,54 @@
+#include "psLib.h"
+
+static int primary(int i)
+{
+    if (i != 0) {			// let's pretend it's an I/O error
+	return psError("tst.error.primary", PS_ERR_IO, 1, "Primary error");
+    }
+
+    return 0;
+}
+
+static int middle(void)
+{
+    if (primary(1) != 0) {
+	return psError("tst.error.middle", PS_ERR_UNKNOWN, 0, "Secondary error");
+    }
+
+    return 0;
+}
+
+static int toplevel(void)
+{
+    if (middle() != 0) {
+	return psError("tst.error", PS_ERR_UNKNOWN, 0, "Toplevel error");
+    }
+
+    return 0;
+}
+
+int main(void)
+{
+    psTraceSetLevel("", 10);		// turn on all tracing
+    psLogSetDestination(PS_LOG_NONE);	// turn off error logging
+
+    if (toplevel() != 0) {
+	psErrorStackPrint(stdout, "Traceback:\n");
+
+	if (psErrorLast()->code == PS_ERR_UNKNOWN) {
+	    fprintf(stderr, "Last error is of unknown type\n");
+	}
+	if (psErrorGet(2)->code == PS_ERR_IO) {
+	    fprintf(stderr, "Third oldest error is of type IO\n");
+	}
+    }
+
+    psErrorClear();
+    psErrorStackPrint(stdout, "Traceback:\n");
+
+    if (psErrorLast()->code == PS_ERR_NONE) {
+	fprintf(stderr, "No errors. Hurrah\n");
+    }
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_hash.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_hash.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_hash.c	(revision 22322)
@@ -0,0 +1,56 @@
+/*
+ * An interface to hash tables for Pan-STARRS
+ *
+ * Collisions are handled by simple linked lists
+ */
+#include <stdio.h>
+#include "psLib.h"
+
+typedef struct { char *name; } ID;
+static ID *IdAlloc(const char *name)
+{
+    ID *id = psAlloc(sizeof(ID));
+    id->name = psStringCopy(name);
+
+    return id;
+}
+
+static void IdFree(ID *id)
+{
+    if (psMemGetRefCounter(id) > 1) {
+	psMemDecrRefCounter(id);
+	return;
+    }
+
+    psFree(id->name);
+    psFree(id);
+}
+
+int main(void)
+{
+    psTraceSetLevel("utils.hash", 3);
+    long memId0 = psMemGetId();
+
+    psHash *hash = psHashAlloc();
+
+    psHashInsert(hash, "Lynda", IdAlloc("Lee"), (void (*)(void *))IdFree);
+    psHashInsert(hash, "Lynda", IdAlloc("Lee"), (void (*)(void *))IdFree);
+    psHashInsert(hash, "Robert", IdAlloc("Lupton"), (void (*)(void *))IdFree);
+    psHashInsert(hash, "Robert", IdAlloc("the Good"), (void (*)(void *))IdFree);
+
+    char *names[] = {"Robert", "Lynda", "Rowan", NULL};
+    for (char **name = names; *name != NULL; name++) {
+	ID *id = psHashLookup(hash, *name);
+	printf("%s's surname is:\t%s\n", *name,
+	       ((id == NULL) ? "(unknown)" : id->name));
+    }
+
+    psHashFree(hash, (void (*)(void *))IdFree);
+    /*
+     * Check for memory leaks
+     */
+    fprintf(stderr,"Checking for memory leaks:\n");
+    psMemCheckLeaks(memId0, NULL, stderr);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_logmsg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_logmsg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_logmsg.c	(revision 22322)
@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include "psLib.h"
+
+int main(void)
+{
+    psTraceSetLevel("utils.logMsg", 1);
+    
+    psLogSetDestination(PS_LOG_TO_STDERR);
+    psLogSetLevel(PS_LOG_INFO);
+    psLogSetFormat("NM");
+    
+    psLogMsg("init", PS_LOG_INFO, "Hello World\n");
+    fprintf(stderr,"    Expect an abort():\n");
+    psAbort("fini", "I'm in Hawai`i");
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_memory.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_memory.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_memory.c	(revision 22322)
@@ -0,0 +1,163 @@
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "psLib.h"
+
+/*****************************************************************************/
+/*
+ * My callbacks
+ */
+static long my_MemAllocateCB(const psMemBlock *ptr)
+{
+    static int incr = 0;		// "p_psMemAllocateID += incr"
+
+    assert (ptr != NULL);		// prevent compiler warnings
+    
+    fprintf(stderr, "Allocating block %ld (%s:%d)\n",
+	    ptr->id, ptr->file, ptr->lineno);
+    
+    incr = 2;				// See every other allocation
+
+    return incr;
+}
+
+static void *my_MemExhaustedCB(size_t size)
+{
+    psError(__func__, ENOMEM, 1, "Bye bye sweet world\n");
+    
+    return NULL;
+}
+
+static psMemProblemCB default_MemProblemCB = NULL;
+
+static void my_MemProblemCB(const psMemBlock *ptr,
+			    const char *file, int lineno)
+{
+    fprintf(stderr, "I foresee trouble:\n");
+
+    default_MemProblemCB(ptr, file, lineno);
+}
+    
+/*****************************************************************************/
+
+int main(void)
+{
+    /*
+     * Survive any aborts that are generated
+     * This doesn't seem to work on mac os/x 10.2.8
+     */
+    if (signal(SIGABRT, SIG_IGN) == SIG_ERR) {
+	perror("Installing a SIGABRT handler");
+    }
+    /*
+     * Install my callbacks
+     */
+    (void)psMemAllocateCBSet(my_MemAllocateCB);
+    default_MemProblemCB = psMemProblemCBSet(my_MemProblemCB);
+    (void)psMemExhaustedCBSet(my_MemExhaustedCB);
+    /*
+     * Request callback on first allocation
+     */
+    (void)psMemAllocateIDSet(1);
+    /*
+     * Start allocating
+     */
+    char *str = psAlloc(10);
+    double *x = psAlloc(1);
+    int *i = psAlloc(1);
+    *i = 1;
+    *x = 1.234;
+
+    psFree(i);
+    
+    str = psRealloc(str, 20);
+    psFree(str);
+#if 0
+    /*
+     * An illegal double-free
+     */
+    psFree(str);
+#endif
+
+#if 0
+    /*
+     * Free memory not allocated with psAlloc
+     */
+    psFree(psAlloc(1000) + 500);
+#endif
+
+#if 1
+    /*
+     * Ask for more memory than we can get
+     */
+    psAlloc((size_t)-1 - sizeof(psMemBlock));
+#endif
+
+#if 0
+    /*
+     * Increment refCounter on non-malloced pointer
+     */
+    psMemGetRefCounter(malloc(1));
+#endif
+
+#if 0
+    /*
+     * Mess with the refCounter
+     */
+    (void)psMemIncrRefCounter(x);
+    (void)psMemIncrRefCounter(x);
+    (void)psMemDecrRefCounter(x);	// now == 2
+#endif
+
+#if 0
+    /*
+     * A memory leak
+     */
+    {
+	char *str2 = psAlloc(20);
+	
+	sprintf(str2, "XXX");
+    }
+#else
+    psFree(x);
+#endif
+
+#if 1
+    /*
+     * Corrupt some memory
+     */
+    char *c = psAlloc(1);
+    c[-4] = 0;
+#endif
+
+    /*************************************************************************/
+    /*
+     * Check for memory corruption
+     */
+    if(psMemCheckCorruption(1)) {
+	psError(__func__, PS_ERR_MEMORY_CORRUPTION, 1, "Detected corrupted memory\n\n");
+    }
+    /*
+     * Check for memory leaks
+     */
+    psMemBlock **leaks = NULL;
+    int nleak = psMemCheckLeaks(0, &leaks, NULL);
+
+    if (nleak > 0) {
+	fprintf(stderr, "Memory is leaking (%d blocks):\n", nleak);
+	fprintf(stderr, "   %20s:line ID\n", "file");
+	for (int i = 0; i < nleak; i++) {
+	    fprintf(stderr, "   %20s:%-4d %ld\n",
+		    leaks[i]->file, leaks[i]->lineno, leaks[i]->id);
+	}
+	psFree(leaks);
+    }
+
+    if ((nleak = psMemCheckLeaks(0, NULL, NULL)) > 0) {
+	fprintf(stderr, "Memory is still leaking (%d blocks):\n", nleak);
+	psMemCheckLeaks(0, NULL, stderr);
+    }
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_trace.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_trace.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/tst_trace.c	(revision 22322)
@@ -0,0 +1,64 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+#include "psLib.h"
+
+static long my_MemAllocateCB(const psMemBlock *ptr)
+{
+    static int n = 1;			// "p_psMemAllocateID += n"
+
+    assert (ptr != NULL);		// prevent compiler warnings
+    
+    if(psMemCheckCorruption(0)) {
+	psError(__func__, PS_ERR_MEMORY_CORRUPTION, PS_NEW_ERROR, "Detected corrupted memory\n");
+    }
+    
+    return n;				// see every nth allocation
+}
+
+int main(void)
+{
+    psLogSetFormat("NM");		// suppress some of the psLogMsg verbosity
+
+    (void)psMemAllocateCBSet(my_MemAllocateCB);
+    //(void)psMemFreeCBSet(my_MemAllocateCB);
+    psMemAllocateIDSet(1);
+    psMemFreeIDSet(14);
+    
+#if 0
+    psTraceSetLevel("", 10);
+#else
+    psTraceSetLevel("utils.dlist.add.head", 9);
+    psTraceSetLevel("utils.dlist.add", 3);
+    psTraceSetLevel("utils.dlist.remove", 4);
+    psTraceSetLevel("coadd", 2);
+    psTraceSetLevel("coadd.CR.remove.morphology", 5);
+    psTraceSetLevel("utils.hash", 2);
+    psTraceSetLevel("utils.dlist.add", 9);
+    psTraceSetLevel("utils", 1);
+#endif
+    
+    psTracePrintLevels();
+    printf("\n");
+
+    psTrace("utils.dlist.remove", 2, "Removing psDList key \"%s\"\n", "my_key");
+    psTrace("utils", 2, "Initialising utilities library\n");
+    psTrace("", 2, "This is turned on by trace component \"\"");
+    psTrace("utils.dlist", 2, "Initialising psDList\n");
+    psTrace("utils.dlist.remove", 4, "Removing psDList key \"%s\" (value: \"%d\")\n", "my_key", 12345);
+    psTrace("utils.hash.remove", 4, "Removing hash key \"%s\" (value: \"%d\")\n", "my_key", 12345);
+    psTrace("utils.dlist.add", 1, "Adding psDList key \"%s\"\n", "your_key");
+    psTrace("utils.dlist.find", 2, "Looking up psDList key \"%s\"\n", "some_key");
+    psTrace("coadd.CR.remove", 4, "Removing CRs\n");
+    psTrace("coadd.CR.remove.morphology", 4, "CRs are not fuzzy\n");
+
+    psTraceReset();
+    fprintf(stderr,"\n");
+    fprintf(stderr,"Checking for memory leaks:\n");
+    psMemCheckLeaks(0, NULL, stderr);
+    
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/vector.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/vector.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/Utils/vector.c	(revision 22322)
@@ -0,0 +1,45 @@
+/*
+ * We only support psVector's of type PS_TYPE_OTHER (i.e. (void *)) for now
+ */
+#include <assert.h>
+#include "psLib.h"
+
+psVector *
+psVectorAlloc (int nalloc,		///< vector length 
+	       psElemType type)		///< vector data type 
+{
+   assert (type == PS_TYPE_OTHER);
+
+   psVector *vec = psAlloc(sizeof(psVector));
+   vec->type.type = type;
+   vec->type.dimen = PS_DIMEN_VECTOR;
+
+   *(int *)&vec->n = *(int *)&vec->nalloc = nalloc;
+   vec->data.V = psAlloc(nalloc*sizeof(void **));
+
+   return vec;
+}
+
+void
+psVectorFree(psVector *vec,
+	     void (*elemFree)(void *))  ///< destructor for array data
+	     
+{
+   if (psMemGetRefCounter(vec) != 1) {
+      psMemDecrRefCounter(vec);
+      return;
+   }
+   
+   assert (vec->type.dimen == PS_DIMEN_VECTOR && vec->type.type == PS_TYPE_OTHER);
+
+   if (elemFree != NULL && vec->nalloc > 0) {
+      assert (vec->data.V != NULL);	/* choose any member of the union */
+      const int n = vec->nalloc;
+      for (int i = 0; i < n; i++) {
+	 elemFree(vec->data.V[i]);
+      }
+   }
+
+   psFree(vec->data.V);
+   psFree(vec);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/src/psArray.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/src/psArray.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/src/psArray.h	(revision 22322)
@@ -0,0 +1,130 @@
+#if !defined(PS_ARRAY_H)
+#define PS_ARRAY_H
+
+/**
+ * Concatenate two macro arguments
+ */
+#define P_PS_CONCAT(A, B) A ## B	//!< Expands to AB
+#define PS_CONCAT(A, B) A ## B		//!< Also Expands to AB
+#define PS_CONCAT2(A, B) PS_CONCAT(A, B) //!< Also expands to AB
+#define PS_CONCAT3(A, B, C) A ## B ## C //!< Expands to ABC
+#define PS_CONCAT4(A, B, C, D) A ## B ## C ## D //!< Expands to ABCD
+
+#include <stdlib.h>
+/**
+ * Declare TYPEArray
+ */
+#define PS_DECLARE_ARRAY_TYPE(TYPE) \
+    typedef struct { \
+	int size;			/* number of allocated elements */ \
+	int n;				/* number of elements in use */ \
+	\
+	TYPE *arr;			/* the actual data */ \
+    } PS_CONCAT(TYPE, Array); \
+    \
+PS_CONCAT(TYPE, Array) *PS_CONCAT3(TYPE,Array,Alloc)(int n, int s); \
+PS_CONCAT(TYPE, Array) *PS_CONCAT3(TYPE,Array,Realloc)(PS_CONCAT(TYPE, Array) *arr, int n); \
+void PS_CONCAT3(TYPE,Array,Free)(PS_CONCAT(TYPE, Array) *arr)
+
+/*****************************************************************************/
+/**
+ * Generate the code TYPEArray's constructors/destructors
+ */
+#define PS_CREATE_ARRAY_TYPE(TYPE) \
+    P_PS_CREATE_ARRAY_TYPE(,,TYPE)
+
+#define P_PS_CREATE_ARRAY_TYPE(QUAL,PREFIX,TYPE) \
+\
+QUAL PS_CONCAT(TYPE, Array) *PS_CONCAT4(PREFIX,TYPE,Array,Alloc)(int n, /* initial dimension */\
+                                                   int s) /* initial number of elements */ \
+{ \
+    PS_CONCAT(TYPE, Array) *arr = psAlloc(sizeof(PS_CONCAT(TYPE, Array))); \
+    \
+    if (n > s) { \
+	psLogMsg(PS_STRING(TYPE) "ArrayAlloc", PS_LOG_ERROR, "Too few elements requested in array (%d < %d)\n", s, n); \
+	s = n; \
+    } \
+    \
+    arr->size = s; \
+    arr->n = n; \
+    \
+    if (s == 0) { \
+	arr->arr = NULL; \
+    } else { \
+	arr->arr = psAlloc(s*sizeof(TYPE)); \
+    } \
+    \
+    return arr; \
+} \
+\
+QUAL PS_CONCAT(TYPE, Array) *PS_CONCAT4(PREFIX,TYPE,Array,Realloc)(PS_CONCAT(TYPE, Array) *arr, int n) \
+{ \
+    if (arr == NULL) { \
+	return PS_CONCAT4(PREFIX,TYPE,Array,Alloc)(n, n); \
+    } \
+    \
+    if (n <= arr->size) { \
+	if (arr->n < n) { \
+	    arr->n = n; \
+	} \
+    } else { \
+        arr->arr = psRealloc(arr->arr, n*sizeof(TYPE)); \
+	arr->size = n; \
+    } \
+    \
+    return arr; \
+} \
+\
+QUAL void PS_CONCAT4(PREFIX,TYPE,Array,Free)(PS_CONCAT(TYPE,Array) *arr) \
+{ \
+    if (arr == NULL) { \
+	return; \
+    } \
+    \
+    psFree(arr->arr); \
+    psFree(arr); \
+}
+
+/*****************************************************************************/
+/**
+ * Declare array of pointers
+ */
+#define PS_DECLARE_ARRAY_PTR_TYPE(TYPE) \
+    typedef TYPE *P_PS_CONCAT(TYPE, Ptr); \
+    PS_DECLARE_ARRAY_TYPE(PS_CONCAT(TYPE, Ptr))
+
+/**
+ * Create constructors/destructors for array of pointers
+ */
+#define PS_CREATE_ARRAY_PTR_TYPE(TYPE); \
+    P_PS_CREATE_ARRAY_TYPE(static, my_, P_PS_CONCAT(TYPE, Ptr)) \
+    \
+    PS_CONCAT(TYPE, PtrArray) *PS_CONCAT3(TYPE,PtrArray,Alloc)(int n, int s) \
+    { \
+	PS_CONCAT(TYPE, PtrArray) *arr = PS_CONCAT4(my_,TYPE,PtrArray,Alloc)(n, s); \
+	for (int i = 0; i < arr->n; i++) { \
+	    arr->arr[i] = psMemIncrRefCounter,(PS_CONCAT(TYPE, Alloc)());\
+	} \
+        \
+	return arr; \
+    } \
+    \
+    PS_CONCAT(TYPE, PtrArray) *PS_CONCAT3(TYPE,PtrArray,Realloc)(PS_CONCAT(TYPE, PtrArray) *arr, int n) \
+    { \
+	for (int i = n; i < arr->n; i++) { \
+	    PS_CONCAT(TYPE, Free)(psMemDecrRefCounter(arr->arr[i]));\
+	} \
+        \
+	return PS_CONCAT4(my_,TYPE,PtrArray,Realloc)(arr, n); \
+    } \
+    \
+    void PS_CONCAT3(TYPE,PtrArray,Free)(PS_CONCAT2(TYPE,PtrArray) *arr) \
+    { \
+	if (arr == NULL) { return; } \
+	for (int i = 0; i < arr->size; i++) { \
+	    PS_CONCAT(TYPE, Free)(psMemDecrRefCounter(arr->arr[i])); \
+	} \
+	PS_CONCAT4(my_, TYPE, PtrArray, Free)(arr); \
+    }
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/testing/pap_rel1.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/testing/pap_rel1.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/testing/pap_rel1.txt	(revision 22322)
@@ -0,0 +1,239 @@
+psMemory.c:
+pthread_mutex_t   memBlockListMutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t   memIdMutex = PTHREAD_MUTEX_INITIALIZER;
+
+Either these should be declared "static", or there should be namespace
+restriction.  Since these are not used outside psMemory.c, probably
+the former.
+
+
+#define ALIGNED(P) ((void *)((long)(P) & ~03) == (P))
+This is not machine-independent.
+
+
+/*
+ * Check for memory leaks. Not production quality code
+ */
+int psMemCheckLeaks(
+.....
+The comment, I hope, is not appropriate.
+
+
+Coding standard violations: e.g, curly brackets:
+    if (vptr == NULL)
+    {
+        return NULL;
+    }
+Due to a bug in astyle.
+
+
+"Slightly illegal" according to RHL:
+#define PS_FREE (void*)1
+
+
+On nkfb0:
+Test driver: tst_psMemory Failed (Return value 256).
+Test driver: tst_psStats07 Failed (Return value 34304).
+
+On poiserver0:
+--- Executing test driver tst_psFunc01
+        Failed - STDOUT difference
+76,105c76,105
+< Gaussian Deviate [0] is 30.267838
+< Gaussian Deviate [1] is 29.823797
+< Gaussian Deviate [2] is 33.348816
+< Gaussian Deviate [3] is 31.467281
+< Gaussian Deviate [4] is 31.995049
+< Gaussian Deviate [5] is 27.444996
+< Gaussian Deviate [6] is 25.206570
+< Gaussian Deviate [7] is 28.641439
+< Gaussian Deviate [8] is 29.921818
+< Gaussian Deviate [9] is 31.787111
+< Gaussian Deviate [10] is 29.964705
+< Gaussian Deviate [11] is 27.406887
+< Gaussian Deviate [12] is 28.664040
+< Gaussian Deviate [13] is 30.363417
+< Gaussian Deviate [14] is 31.662102
+< Gaussian Deviate [15] is 28.903503
+< Gaussian Deviate [16] is 28.723936
+< Gaussian Deviate [17] is 30.014177
+< Gaussian Deviate [18] is 28.660292
+< Gaussian Deviate [19] is 28.344475
+< Gaussian Deviate [20] is 31.225000
+< Gaussian Deviate [21] is 29.264824
+< Gaussian Deviate [22] is 29.421013
+< Gaussian Deviate [23] is 28.166250
+< Gaussian Deviate [24] is 28.832390
+< Gaussian Deviate [25] is 30.129230
+< Gaussian Deviate [26] is 30.093487
+< Gaussian Deviate [27] is 28.498547
+< Gaussian Deviate [28] is 29.711535
+< Gaussian Deviate [29] is 32.951122
+---
+> Gaussian Deviate [0] is 27.723047
+> Gaussian Deviate [1] is 31.874863
+> Gaussian Deviate [2] is 32.605770
+> Gaussian Deviate [3] is 31.492830
+> Gaussian Deviate [4] is 32.045048
+> Gaussian Deviate [5] is 31.126467
+> Gaussian Deviate [6] is 29.192963
+> Gaussian Deviate [7] is 32.562622
+> Gaussian Deviate [8] is 29.928261
+> Gaussian Deviate [9] is 26.550817
+> Gaussian Deviate [10] is 30.568424
+> Gaussian Deviate [11] is 33.030228
+> Gaussian Deviate [12] is 33.261463
+> Gaussian Deviate [13] is 26.076645
+> Gaussian Deviate [14] is 32.264557
+> Gaussian Deviate [15] is 26.738295
+> Gaussian Deviate [16] is 29.086100
+> Gaussian Deviate [17] is 31.104702
+> Gaussian Deviate [18] is 27.956509
+> Gaussian Deviate [19] is 31.351622
+> Gaussian Deviate [20] is 28.200375
+> Gaussian Deviate [21] is 28.058838
+> Gaussian Deviate [22] is 31.720587
+> Gaussian Deviate [23] is 28.963566
+> Gaussian Deviate [24] is 32.100430
+> Gaussian Deviate [25] is 28.235241
+> Gaussian Deviate [26] is 30.268490
+> Gaussian Deviate [27] is 30.099007
+> Gaussian Deviate [28] is 32.430271
+> Gaussian Deviate [29] is 26.015800
+ 
+
+On mithrandir:
+All 62 Test Drivers Passed with 553 Testpoints.
+
+
+
+
+Problem for tst_psMemory was in the test:
+In TPOutOfMemory, want to use the line:
+	mem1 = (int*) psAlloc(LONG_MAX);
+It basically comes down to different machines using different memory
+sizes.
+
+The tst_psStats07 failure seems to be due to memory problems:
+2004:06:17 02:29:23Z|nkfb0               |E|       p_psFree|psMemCheckCorruption: memory block 14 is corrupted (buffer overflow)
+2004:06:17 02:29:23Z|nkfb0               |A|memProblemCallb|Detected a problem in the memory system at psVector.c:162
+
+I suspect that the problem is somewhere in the histogram code ---
+possibly due to freeing vector->data.V when vector->data.S32 was
+allocated.
+Memory block 14 has:
+Start: deadbeef
+End: deadbeef
+Endpost: 63f100
+File: psVector.c
+Line: 77
+
+RdS says this test is not ready.
+
+
+This code is handy:
+psMemoryId printMem(const psMemBlock *ptr)
+{
+        const void* startblock = ptr->startblock;
+        //struct psMemBlock* previousBlock = ptr->previousBlock;
+        //struct psMemBlock* nextBlock = ptr->nextBlock;
+        size_t  userMemorySize = ptr->userMemorySize;
+        //const psMemoryId id = ptr->id;
+        const char* file = ptr->file;
+        const int lineno = ptr->lineno;
+        //psReferenceCount refCounter = ptr->refCounter;
+        const void* endblock = ptr->endblock;
+                                                                                
+        fprintf(stderr,"Start: %lx\nEnd: %lx\nEndpost: %lx\nFile: %s\nLine: %d\n",(long)startblock,(long)endblock,(long)(ptr+1+userMemorySize),file,lineno);
+        return 0;
+}
+
+
+
+I suspect the problem on poiserver0, with tst_psFunc01 is due to the
+use of a different RNG.  No, actually, a different version of GSL
+(1.1.1 vs 1.4).
+
+
+In psTrace.c, there is a globally-visible struct "Component".
+
+
+=============================================================================================================
+
+Notes:
+
+psAbort02 produces:
+2004:06:18 20:46:57Z|mithrandir          |A|       __LINE__|NO_VALUES
+Note the __LINE__ which wasn't replaced?  Not a big deal.
+
+
+Is the psError functionality implemented as specified?  There is no
+psErrorStackPrint, and psError simply does a psLogMsg and then
+continues.  SVD says it's the old specification.
+
+
+psHash: now store the free function when inserting, but also have to specify when calling psHashFree.
+psHash01 does the allocate, insert and free.
+
+
+
+psBitSet: SVD p21 says it checks that a set of a non-multile of 8 bits can be allocated,
+but this is not done:
+price@mithrandir:/home/mithrandir/price/psLib/test/collections>grep psBitSetAlloc *.c
+tst_psBitSet_01.c:    psBitSet* bs = psBitSetAlloc(24);
+tst_psBitSet_02.c:    psBitSet* bs1 = psBitSetAlloc(24);
+tst_psBitSet_02.c:    psBitSet* bs2 = psBitSetAlloc(24);
+tst_psBitSet_02.c:    psBitSet* outbs = psBitSetAlloc(24);
+tst_psBitSet_03.c:    psBitSet* bs1 = psBitSetAlloc(24);
+tst_psBitSet_03.c:    psBitSet* bs2 = psBitSetAlloc(24);
+tst_psBitSet_03.c:    psBitSet* outbs = psBitSetAlloc(24);
+tst_psBitSet_04.c:    psBitSet* bs1 = psBitSetAlloc(24);
+tst_psBitSet_04.c:    psBitSet* bs2 = psBitSetAlloc(24);
+tst_psBitSet_04.c:    psBitSet* outbs = psBitSetAlloc(24);
+tst_psBitSet_05.c:    psBitSet* bs1 = psBitSetAlloc(24);
+tst_psBitSet_05.c:    psBitSet* bs2 = psBitSetAlloc(40);
+tst_psBitSet_05.c:    psBitSet* outbs = psBitSetAlloc(24);
+tst_psBitSet_06.c:    psBitSet* bs1 = psBitSetAlloc(24);
+tst_psBitSet_06.c:    psBitSet* bs2 = psBitSetAlloc(24);
+tst_psBitSet_06.c:    psBitSet* outbs = psBitSetAlloc(24);
+tst_psBitSet_06.c:    psBitSetAlloc(-4);
+
+psBitSet02: SVD says 0xFF AND 0x55, but program is in effect testing 0xFF AND 0xFF,
+which is not particularly rigorous.  Similarly with psBitSet0[34].
+
+psLogMsg00:
+First test which checks that the levels are working doesn't use psLogMsg but psVLogMsg.
+        myLogMsg(__func__, i, "Hello World!  My level is %d %f %s\n", i,
+                 (float) i, "beep beep");
+instead of psLog(...).
+
+psTrace: is inheritance of a trace level being tested?  It doesn't appear to be in
+tst_psTrace02.
+
+tst_psList has no checking for corruption or memory leaks.
+Also, it doesn't seem to test for the instance of freeing the last item in a list.
+
+In SVD, testing of sample mean, median, stdev contains "allow for
+calculated value to be within a delta" --- no!  Sample statistics are
+to be exact.
+
+psStats06: Testing against values that can quickly and easily be evaluated (standard deviation), but
+yet, programmer includes the following comment:
+    // NOTE: These values were calculated by running the function on the data.
+    // A: They must be changed if we adjust the number of data points.
+    // B: We don't really know that they are correct.
+    float realStdevNoMask   = 4.472136;
+    float realStdevWithMask = 2.160247;
+
+tst_psMatrix02: Transpose fails for non-square matrices (deliberately!).
+
+tst_psMatrix03: Test C generates:
+	Error: Resulting image is not PS_DIMEN_VECTOR
+But this is in the validation, so it passes.... is it an error or not?
+Surely the output from the LU solver should be a vector!
+
+
+tst_psImage, testpoint 548 doesn't check for memory leaks.
+
+Need to look over the polynomial source code.
+
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/testing/pap_rel2.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/testing/pap_rel2.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/testing/pap_rel2.txt	(revision 22322)
@@ -0,0 +1,1968 @@
+Bug 131: there was a problem with the test harness that caused 35 of
+81 tests to fail.  It was a regex problem in the Perl code.
+
+psMatrixVectorArithmetic:
+// Conversion for degrees to radians
+#define D2R 0.01745329252111111  /* PI/180 */
+// Conversion for radians to degrees
+#define R2D 57.29577950924861   /* 180.0/PI */
+
+Instead of using M_PI etc.
+
+
+Running gcov:
+
+=============================================================================================================
+
+ 57.89% of 133 source lines executed in file psImage.c
+
+L65:
+         467        if (image == NULL) {
+      ######            psAbort(__func__, " : Line %d - Failed to allocate memory", __LINE__);
+		    }
+L72:
+         467        if (image->data.V == NULL) {
+      ######            psAbort(__func__, " : Line %d - Failed to allocate memory", __LINE__);
+		    }
+L77:
+         467        if (image->data.V[0] == NULL) {
+      ######            psAbort(__func__, " : Line %d - Failed to allocate memory", __LINE__);
+		    }
+L100:
+         455        if (image == NULL) {
+      ######            return;
+		    }
+
+***L104:
+         455        if (image->type.type == PS_TYPE_PTR) {
+		        // 2-D array of pointers -- must
+		        // dereference
+      ######            unsigned int oldNumRows = image->numRows;
+      ######            unsigned int oldNumCols = image->numCols;
+      ######            psPTR* rowPtr;
+		
+      ######            for (unsigned int row = 0; row < oldNumRows; row++) {
+      ######                rowPtr = image->data.PTR[row];
+      ######                for (unsigned int col = 0; col < oldNumCols; col++) {
+      ######                    psMemDecrRefCounter(rowPtr[col]);
+		            }
+		        }
+		    }
+L143:
+         171        if (old->type.dimen != PS_DIMEN_IMAGE) {
+         187            psError(__func__, "Can not realloc image because input is not an image.");
+      ######            return NULL;
+		    }
+***L148:
+         171        if (old->type.type == PS_TYPE_PTR) {
+		        // 2-D array of pointers -- must
+		        // dereference
+      ######            unsigned int oldNumRows = old->numRows;
+      ######            unsigned int oldNumCols = old->numCols;
+      ######            psPTR* rowPtr;
+		
+      ######            for (unsigned int row = 0; row < oldNumRows; row++) {
+      ######                rowPtr = old->data.PTR[row];
+      ######                for (unsigned int col = 0; col < oldNumCols; col++) {
+      ######                    psMemDecrRefCounter(rowPtr[col]);
+      ######                    rowPtr[col] = NULL;
+		            }
+		        }
+		    }
+L191:
+         456        if (image == NULL) {
+      ######            return numFreed;
+		    }
+L222:
+         162        if (input == NULL) {
+         162            psError(__func__, "Image can not be NULL.");
+      ######            return unexposedValue;
+		    }
+***L240:
+         162        switch (input->type.type) {
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(U8);
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(U16);
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(U32);
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(U64);
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(S8);
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(S16);
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(S32);
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(S64);
+         162            PSIMAGE_PIXEL_INTERPOLATE_CASE(F32);
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(F64);
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(C32);
+      ######            PSIMAGE_PIXEL_INTERPOLATE_CASE(C64);
+		    default:
+      ######            psError(__func__, "Unsupported image datatype (%d)", input->type.type);
+		    }
+
+=============================================================================================================
+
+ 83.25% of 209 source lines executed in file psImageExtraction.c
+
+L51:
+          13        if (image->type.dimen != PS_DIMEN_IMAGE) {
+      ######            psError(__func__, "Can not subset image because input image is not an image.");
+      ######            return NULL;
+		    }
+L117:
+          69        if (input == output) {
+      ######            psError(__func__,
+		                "Can not copy image because given input and output "
+		                "parameter reference the same psImage struct.");
+      ######            psFree(output);
+      ######            return NULL;
+		    }
+L125:
+          69        if (input->type.dimen != PS_DIMEN_IMAGE) {
+      ######            psError(__func__, "Can not copy image because input image is not actually an image.");
+      ######            psFree(output);
+      ######            return NULL;
+		    }
+L137:
+          69        if (inDatatype == PS_TYPE_PTR || type == PS_TYPE_PTR) {
+      ######            psError(__func__, "Can not copy image to/from a void* matrix");
+      ######            psFree(output);
+      ######            return NULL;
+		    }
+***L201:
+          57        switch (type) {
+		    case PS_TYPE_S8:
+           7            PSIMAGE_COPY_CASE(output, S8);
+           7            break;
+		    case PS_TYPE_S16:
+           7            PSIMAGE_COPY_CASE(output, S16);
+           7            break;
+		    case PS_TYPE_S32:
+           7            PSIMAGE_COPY_CASE(output, S32);
+      ######            break;
+		    case PS_TYPE_S64:
+      ######            PSIMAGE_COPY_CASE(output, S64);
+           7            break;
+		    case PS_TYPE_U8:
+           7            PSIMAGE_COPY_CASE(output, U8);
+           7            break;
+		    case PS_TYPE_U16:
+           7            PSIMAGE_COPY_CASE(output, U16);
+           7            break;
+		    case PS_TYPE_U32:
+           7            PSIMAGE_COPY_CASE(output, U32);
+      ######            break;
+		    case PS_TYPE_U64:
+      ######            PSIMAGE_COPY_CASE(output, U64);
+           7            break;
+		    case PS_TYPE_F32:
+           7            PSIMAGE_COPY_CASE(output, F32);
+           7            break;
+		    case PS_TYPE_F64:
+           7            PSIMAGE_COPY_CASE(output, F64);
+           1            break;
+		    case PS_TYPE_C32:
+           1            PSIMAGE_COPY_CASE(output, C32);
+      ######            break;
+		    case PS_TYPE_C64:
+      ######            PSIMAGE_COPY_CASE(output, C64);
+          57            break;
+		    default:
+          57            break;
+		    }
+          57        return output;
+		}
+L297:
+          23        if (mask != NULL) {
+          23            if (inRows != mask->numRows || inCols != mask->numCols) {
+      ######                psError(__func__,
+		                    "The mask and image dimensions did not match (%dx%d vs %dx%d)",
+		                    mask->numCols, mask->numRows, in->numCols, in->numRows);
+      ######                psFree(out);
+		        }
+          23            if (mask->type.type != PS_TYPE_MASK) {
+      ######                psError(__func__, "The mask datatype (%d) must be %s.", mask->type.type, PS_TYPE_MASK_NAME);
+      ######                psFree(out);
+		        }
+		    }
+L386:
+           8            switch (type) {
+      ######                PSIMAGE_CUT_VERTICAL(U8);
+      ######                PSIMAGE_CUT_VERTICAL(U16);
+      ######                PSIMAGE_CUT_VERTICAL(U32);
+      ######                PSIMAGE_CUT_VERTICAL(U64);
+      ######                PSIMAGE_CUT_VERTICAL(S8);
+      ######                PSIMAGE_CUT_VERTICAL(S16);
+      ######                PSIMAGE_CUT_VERTICAL(S32);
+      ######                PSIMAGE_CUT_VERTICAL(S64);
+           8                PSIMAGE_CUT_VERTICAL(F32);
+      ######                PSIMAGE_CUT_VERTICAL(F64);
+      ######                PSIMAGE_CUT_VERTICAL(C32);
+      ######                PSIMAGE_CUT_VERTICAL(C64);
+		        default:
+      ######                psError(__func__, "Unsupported datatype (%d)", type);
+      ######                psFree(out);
+      ######                out = NULL;
+		        }
+
+=============================================================================================================
+
+ 43.93% of 239 source lines executed in file psImageIO.c
+
+LOTS
+
+=============================================================================================================
+
+ 61.74% of 345 source lines executed in file psImageManip.c
+
+***L110:
+      ######            psImageClipCase(S64, "psS64")
+***L114:
+      ######            psImageClipCase(U64, "psU64")
+L120:
+		    default:
+      ######            psError(__func__, "psImageClip does not support the given datatype (%d)", input->type.type);
+***L156:
+      ######            psImageClipNaNCase(C32)
+      ######            psImageClipNaNCase(C64)
+L181:
+          42        if (op == NULL) {
+      ######            psError(__func__, "Operation can not be NULL.");
+      ######            return 1;
+		    }
+L188:
+          42        if (type != overlay->type.type) {
+      ######            psError(__func__, "Image and overlay datatypes must match. (%d vs %d)", type, overlay->type.type);
+      ######            return 2;
+		    }
+L199:
+          42        if (row0 < 0 || col0 < 0 || row0 >= imageNumRows || col0 >= imageNumCols) {
+      ######            psError(__func__,
+		                "Overlay origin of (%d,%d) is outside of the image dimensions (%d x %d).",
+		                col0, row0, imageNumCols, imageNumRows);
+      ######            return 3;
+		    }
+***L249:
+           5            psImageOverlayCase(U8);
+           5            psImageOverlayCase(U16);
+      ######            psImageOverlayCase(U32);
+      ######            psImageOverlayCase(U64);
+           5            psImageOverlayCase(S8);
+           5            psImageOverlayCase(S16);
+      ######            psImageOverlayCase(S32);
+      ######            psImageOverlayCase(S64);
+           6            psImageOverlayCase(F32);
+           5            psImageOverlayCase(F64);
+           5            psImageOverlayCase(C32);
+           5            psImageOverlayCase(C64);
+L262:
+		    default:
+      ######            psError(__func__, "Can not operate on type %d.", type);
+		    }
+L335:
+		    default:
+      ######            psError(__func__, "psImageClip does not support the given datatype (%d)", input->type.type);
+		    }
+***L421:
+           2        switch (in->type.type) {
+      ######            PS_IMAGE_REBIN_CASE(U8);
+      ######            PS_IMAGE_REBIN_CASE(U16);
+      ######            PS_IMAGE_REBIN_CASE(U32);
+      ######            PS_IMAGE_REBIN_CASE(U64);
+      ######            PS_IMAGE_REBIN_CASE(S8);
+      ######            PS_IMAGE_REBIN_CASE(S16);
+      ######            PS_IMAGE_REBIN_CASE(S32);
+      ######            PS_IMAGE_REBIN_CASE(S64);
+           2            PS_IMAGE_REBIN_CASE(F32);
+      ######            PS_IMAGE_REBIN_CASE(F64);
+      ######            PS_IMAGE_REBIN_CASE(C32);
+      ######            PS_IMAGE_REBIN_CASE(C64);
+		    default:
+      ######            psError(__func__, "Input image type not supported.");
+      ######            psFree(out);
+      ######            out = NULL;
+		    }
+
+***psImageResample
+
+***L588:
+           2            switch (type) {
+      ######                PSIMAGE_ROTATE_LEFT_90(U8);
+      ######                PSIMAGE_ROTATE_LEFT_90(U16);
+      ######                PSIMAGE_ROTATE_LEFT_90(U32);
+      ######                PSIMAGE_ROTATE_LEFT_90(U64);
+      ######                PSIMAGE_ROTATE_LEFT_90(S8);
+           1                PSIMAGE_ROTATE_LEFT_90(S16);
+      ######                PSIMAGE_ROTATE_LEFT_90(S32);
+      ######                PSIMAGE_ROTATE_LEFT_90(S64);
+           1                PSIMAGE_ROTATE_LEFT_90(F32);
+      ######                PSIMAGE_ROTATE_LEFT_90(F64);
+      ######                PSIMAGE_ROTATE_LEFT_90(C32);
+      ######                PSIMAGE_ROTATE_LEFT_90(C64);
+L601:
+		        default:
+      ######                psError(__func__, "Unsupported type (%d)", type);
+      ######                psFree(out);
+      ######                return NULL;
+		        }
+***L628:
+           4            switch (type) {
+      ######                PSIMAGE_ROTATE_180_CASE(U8);
+      ######                PSIMAGE_ROTATE_180_CASE(U16);
+      ######                PSIMAGE_ROTATE_180_CASE(U32);
+      ######                PSIMAGE_ROTATE_180_CASE(U64);
+      ######                PSIMAGE_ROTATE_180_CASE(S8);
+           2                PSIMAGE_ROTATE_180_CASE(S16);
+      ######                PSIMAGE_ROTATE_180_CASE(S32);
+      ######                PSIMAGE_ROTATE_180_CASE(S64);
+           2                PSIMAGE_ROTATE_180_CASE(F32);
+      ######                PSIMAGE_ROTATE_180_CASE(F64);
+      ######                PSIMAGE_ROTATE_180_CASE(C32);
+      ######                PSIMAGE_ROTATE_180_CASE(C64);
+L641:
+		        default:
+      ######                psError(__func__, "Unsupported type (%d)", type);
+      ######                psFree(out);
+      ######                return NULL;
+		        }
+
+***L667:
+           2            switch (type) {
+      ######                PSIMAGE_ROTATE_RIGHT_90(U8);
+      ######                PSIMAGE_ROTATE_RIGHT_90(U16);
+      ######                PSIMAGE_ROTATE_RIGHT_90(U32);
+      ######                PSIMAGE_ROTATE_RIGHT_90(U64);
+      ######                PSIMAGE_ROTATE_RIGHT_90(S8);
+           1                PSIMAGE_ROTATE_RIGHT_90(S16);
+      ######                PSIMAGE_ROTATE_RIGHT_90(S32);
+      ######                PSIMAGE_ROTATE_RIGHT_90(S64);
+           1                PSIMAGE_ROTATE_RIGHT_90(F32);
+      ######                PSIMAGE_ROTATE_RIGHT_90(F64);
+      ######                PSIMAGE_ROTATE_RIGHT_90(C32);
+      ######                PSIMAGE_ROTATE_RIGHT_90(C64);
+L680:
+		        default:
+      ######                psError(__func__, "Unsupported type (%d)", type);
+      ######                psFree(out);
+      ######                return NULL;
+		        }
+
+***L797:
+      ######                PSIMAGE_ROTATE_ARBITRARY_CASE(BILINEAR);
+L798:
+		        default:
+      ######                psError(__func__, "Unsupported interpolation mode (%d)", mode);
+      ######                psFree(out);
+      ######                out = NULL;
+		        }
+
+psImageShift
+
+=============================================================================================================
+
+ 96.07% of 178 source lines executed in file psImageStats.c
+
+L71:
+           6            if (mask->type.type != PS_TYPE_MASK) {
+      ######                psError(__func__, "Expected the mask image type not found (type=%x)", mask->type.type);
+      ######                psFree(junkData);
+      ######                return NULL;
+		        }
+L118:
+           6            if (mask->type.type != PS_TYPE_MASK) {
+          15                psError(__func__, "Expected the mask image type not found (type=%x)", mask->type.type);
+      ######                psFree(junkData);
+      ######                return NULL;
+		        }
+L257:
+           1        if (coeffs->nY > coeffs->nX) {
+      ######            maxChebyPoly = coeffs->nY;
+		    }
+L365:
+           1        if (coeffs->nY > coeffs->nX) {
+      ######            maxChebyPoly = coeffs->nY;
+		    }
+
+=============================================================================================================
+
+  0.00% of 6 source lines executed in file psAbort.c
+
+gcov claims these haven't been exercised, but I'm sure that they have.
+
+=============================================================================================================
+
+100.00% of 4 source lines executed in file psError.c
+
+=============================================================================================================
+
+ 90.98% of 133 source lines executed in file psLogMsg.c
+
+L69:
+          43        if ((level < MIN_LOG_LEVEL) || (level > MAX_LOG_LEVEL)) {
+      ######            psLogMsg("logmsg", PS_LOG_WARN, "Attempt to set invalid logMsg level: %d", level);
+      ######            level = (level < MIN_LOG_LEVEL) ? MIN_LOG_LEVEL : MAX_LOG_LEVEL;
+		    }
+L116:
+           1                if (logDest != NULL && logDest != stderr && logDest != stdout) {
+      ######                    fclose(logDest);
+		            }
+L123:
+           1                if (logDest != NULL && logDest != stderr && logDest != stdout) {
+      ######                    fclose(logDest);
+		            }
+L129:
+      ######            psError(__func__, "The location, %s, for protocol 'dest' is invalid.", location);
+      ######            return 1;
+L138:
+           1            if (logDest != NULL && logDest != stderr && logDest != stdout) {
+      ######                fclose(logDest);
+		        }
+L145:
+      ######        psError(__func__, "Do not know how to handle the protocol '%s'.", protocol);
+      ######        return false;
+L288:
+		    default:
+      ######            psTrace("utils.logMsg", 2, "Invalid logMsg level: %d (%s)\n", level, fmt);
+      ######            level = (level < 0) ? 0 : 9;
+         395            break;
+		    }
+
+
+=============================================================================================================
+
+ 76.72% of 262 source lines executed in file psMemory.c
+
+static void *memExhaustedCallbackDefault
+psMemExhaustedCallback psMemExhaustedCallbackSet
+static void memProblemCallbackDefault
+L117:
+           2        if (func != NULL) {
+           2            memProblemCallback = func;
+		    } else {
+      ######            memProblemCallback = memProblemCallbackDefault;
+		    }
+
+static psMemoryId memAllocateCallbackDefault
+static psMemoryId memFreeCallbackDefault
+
+L182:
+           1        if (func != NULL) {
+           1            memAllocateCallback = func;
+		    } else {
+      ######            memAllocateCallback = memAllocateCallbackDefault;
+		    }
+L195:
+           1        if (func != NULL) {
+           1            memFreeCallback = func;
+		    } else {
+      ######            memFreeCallback = memFreeCallbackDefault;
+		    }
+L229:
+       47465        if (m == NULL) {
+      ######            psError(funcName, "Memory Corruption: NULL memory block found.");
+      ######            return 1;
+		    }
+L244:
+       47464        if (*(void **)((int8_t *) (m + 1) + m->userMemorySize) != P_PS_MEMMAGIC) {
+      ######            psError(funcName, "Memory Corruption: memory block %ld is corrupted (buffer overflow)", m->id);
+      ######            return 1;
+		    }
+L265 (possibly gcov wrong):
+           1                if (abort_on_error) {
+		                // release the lock on the memblock list
+      ######                    pthread_mutex_unlock(&memBlockListMutex);
+      ######                    psAbort(__func__, "Detected memory corruption");
+      ######                    return nbad;
+		            }
+L315 (something funky with gcov):
+        1814            if (ptr == NULL) {
+      ######                ptr = memExhaustedCallback(size);
+      ######                if (ptr == NULL) {
+       30851                    psAbort(__func__, "Failed to allocate %u bytes at %s:%d", size, file, lineno);
+		            }
+		        }
+L368(gcov):
+         521            if (checkMemBlock(ptr, __func__) != 0) {
+      ######                memProblemCallback(ptr, file, lineno);
+      ######                psAbort(file, "Realloc detected a memory corruption (id %ld @ %s:%d).",
+		                    ptr->id, ptr->file, ptr->lineno);
+		        }
+L477:
+         160        if (checkMemBlock(ptr, __func__) != 0) {
+      ######            memProblemCallback(ptr, __func__, __LINE__);
+		    }
+L499:
+         756        if (checkMemBlock(ptr, __func__)) {
+      ######            memProblemCallback(ptr, file, lineno);
+		    }
+L519:
+       62686        if (checkMemBlock(ptr, __func__) != 0) {
+      ######            memProblemCallback(ptr, file, lineno);
+      ######            return NULL;
+		    }
+L611:
+       29356        if (checkMemBlock(ptr, __func__) != 0) {
+      ######            memProblemCallback(ptr, __func__, __LINE__);
+		    }
+
+psFreeFcn p_psMemGetDeallocator
+
+=============================================================================================================
+
+100.00% of 11 source lines executed in file psString.c
+
+=============================================================================================================
+
+ 84.92% of 126 source lines executed in file psTrace.c
+
+static void componentFree
+void psTraceFree
+L158:
+          55        if (addNodeName[0] != '.') {
+      ######            printf("ERROR: failed to add %s to the root component tree.\n", addNodeName);
+      ######            exit(1);
+		    }
+L309:
+          11        if (comp->name[0] == '\0') {
+      ######            printf("%*s%-*s %d\n", depth, "", 20 - depth,
+		               "(root)", (comp->level == PS_UNKNOWN_TRACE_LEVEL) ? 0 : comp->level);
+		    } else {
+          11            if (comp->level == PS_UNKNOWN_TRACE_LEVEL) {
+      ######                printf("%*s%-*s %s\n", depth, "", 20 - depth, comp->name, ".");
+		        } else {
+
+
+=============================================================================================================
+
+ 83.33% of 42 source lines executed in file psArray.c
+
+L71:
+           2        if (in == NULL) {
+      ######            psError(__func__, "Null input vector\n");
+      ######            return NULL;
+
+=============================================================================================================
+
+ 90.40% of 125 source lines executed in file psBitSet.c
+
+L109:
+          26        if (newObj == NULL) {
+      ######            psAbort(__func__, " : Line %d - Failed to allocate memory", __LINE__);
+		    }
+L118:
+          26        if (newObj->bits == NULL) {
+      ######            psAbort(__func__, " : Line %d - Failed to allocate memory", __LINE__);
+		    }
+L195:
+           9        if (operator == NULL) {
+      ######            psError(__func__, " : Line %d - Null input operator\n", __LINE__);
+      ######            return outBitSet;
+		    }
+L200:
+           9        if (inBitSet2 == NULL) {
+      ######            psError(__func__, " : Line %d - Null psBitSet for inBitSet2 argument", __LINE__);
+      ######            return outBitSet;
+		    }
+L257:
+           3        if (n == 0) {
+      ######            psError(__func__, " : Line %d - No elements in inBitSet", __LINE__);
+      ######            return outBitSet;
+		    }
+L265:
+           3        if (inBitSet->n != outBitSet->n) {
+      ######            psError(__func__, " : Line %d - psBitSet sizes not the same", __LINE__);
+      ######            return outBitSet;
+		    }
+
+=============================================================================================================
+
+ 12.50% of 40 source lines executed in file psCompare.c
+
+Need to do all the macros.
+
+=============================================================================================================
+
+ 87.18% of 117 source lines executed in file psHash.c
+
+L96
+          55        if (data == NULL) {
+		        // NOTE: Should we flag a warning message?
+      ######            bucket->data = NULL;
+		    } else {
+          55            bucket->data = psMemIncrRefCounter(data);
+		    }
+L246:
+         132        if ((hash < 0) || (hash >= table->nbucket)) {
+      ######            psAbort(__func__, "Internal hash function out of range (%d)", hash);
+		    }
+***L304:
+          65            while (ptr != NULL) {
+          10                if (strcmp(key, ptr->key) == 0) {
+		                // We have found this key in the hash table.
+		
+      ######                    psTrace("utils.hash.insert", 3, "Replacing data for %s\n", key);
+		
+		                // NOTE: I have changed this behavior from the originally
+		                // supplied code.  Formerly, if itemFree was NULL, then
+		                // the new data was not inserted into the hash table.
+		
+      ######                    psFree(ptr->data);
+		
+      ######                    ptr->data = psMemIncrRefCounter(data);
+      ######                    return data;
+		            }
+          10                ptr = ptr->next;
+		        }
+L402:
+          14        if (data != NULL) {
+          14            retVal = true;
+		    } else {
+      ######            retVal = false;
+		    }
+
+=============================================================================================================
+
+ 98.21% of 224 source lines executed in file psList.c
+
+L137:
+          19            if (position == NULL) {
+      ######                psError(__func__, "%s failed to move cursor to specified location (%d)", __func__, where);
+      ######                position = list->head;         // since we no list->size != 0, this must be non-NULL
+		        }
+
+=============================================================================================================
+
+ 36.96% of 46 source lines executed in file psScalar.c
+
+psScalar* psScalarCopy
+void psScalarFree
+
+All the different cases.
+
+=============================================================================================================
+
+ 67.08% of 161 source lines executed in file psVector.c
+
+L147:
+          13        if (nalloc < 1) {
+          42            psError(__func__, "Invalid value for nalloc (%d)\n", nalloc);
+      ######            psFree(in);
+      ######            return NULL;
+		    }
+**L181:
+        6622        if (outVector == NULL) {
+      ######            outVector = psVectorAlloc(inN, inType);
+      ######            outVector->n = inVector->n;
+		    }
+L201:
+        6620        if (inN == 0) {
+      ######            psError(__func__, " : Line %d - No elements in use for input vector\n", __LINE__);
+      ######            return outVector;
+		    }
+		
+        6620        if (outN == 0) {
+      ######            psError(__func__, " : Line %d - No elements in use for output vector\n", __LINE__);
+      ######            return outVector;
+		    }
+***L214:
+        6620        switch (inType) {
+		    case PS_TYPE_U8:
+      ######            qsort(outVec, inN, elSize, psCompareU8);
+      ######            break;
+		    case PS_TYPE_U16:
+      ######            qsort(outVec, inN, elSize, psCompareU16);
+      ######            break;
+		    case PS_TYPE_U32:
+      ######            qsort(outVec, inN, elSize, psCompareU32);
+      ######            break;
+		    case PS_TYPE_U64:
+      ######            qsort(outVec, inN, elSize, psCompareU64);
+      ######            break;
+		    case PS_TYPE_S8:
+      ######            qsort(outVec, inN, elSize, psCompareS8);
+      ######            break;
+		    case PS_TYPE_S16:
+      ######            qsort(outVec, inN, elSize, psCompareS16);
+      ######            break;
+		    case PS_TYPE_S32:
+      ######            qsort(outVec, inN, elSize, psCompareS32);
+      ######            break;
+		    case PS_TYPE_S64:
+      ######            qsort(outVec, inN, elSize, psCompareS64);
+      ######            break;
+		    case PS_TYPE_F32:
+        6620            qsort(outVec, inN, elSize, psCompareF32);
+        6620            break;
+		    case PS_TYPE_F64:
+      ######            qsort(outVec, inN, elSize, psCompareF64);
+      ######            break;
+		    default:
+      ######            psError(__func__, " : Line %d - Invalid psType\n", __LINE__);
+		    }
+L292:
+           1        if (inN != outN) {
+      ######            psError(__func__, " : Line %d - Input and output vector sizes are not equal: in=%d out=%d\n",
+		                __LINE__, inN, outN);
+      ######            return outVector;
+		    }
+		
+           1        if (outVector->type.type != PS_TYPE_U32) {
+      ######            psError(__func__, " : Line %d - Output vector is not of type U32: out=%d\n",
+		                __LINE__, outVector->type.type);
+      ######            return outVector;
+		    }
+***L309:
+           1        switch (inType) {
+		    case PS_TYPE_U8:
+      ######            SORT_INDICES(U8);
+      ######            break;
+		    case PS_TYPE_U16:
+      ######            SORT_INDICES(U16);
+      ######            break;
+		    case PS_TYPE_U32:
+      ######            SORT_INDICES(U32);
+      ######            break;
+		    case PS_TYPE_U64:
+      ######            SORT_INDICES(U64);
+      ######            break;
+		    case PS_TYPE_S8:
+      ######            SORT_INDICES(S8);
+      ######            break;
+		    case PS_TYPE_S16:
+      ######            SORT_INDICES(S16);
+      ######            break;
+		    case PS_TYPE_S32:
+      ######            SORT_INDICES(S32);
+      ######            break;
+		    case PS_TYPE_S64:
+      ######            SORT_INDICES(S64);
+           1            break;
+		    case PS_TYPE_F32:
+           1            SORT_INDICES(F32);
+      ######            break;
+		    case PS_TYPE_F64:
+      ######            SORT_INDICES(F64);
+      ######            break;
+		    default:
+      ######            psError(__func__, " : Line %d - Invalid psType\n", __LINE__);
+		    }
+
+
+=============================================================================================================
+
+ 63.49% of 430 source lines executed in file psFFT.c
+
+L38:
+           2        if (in == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L45 (gcov?):
+           2        if ((type != PS_TYPE_F32) && (type != PS_TYPE_C32)) {
+           2            psError(__func__, "Input image must be a 32-bit float or complex image (type=%d)", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+		
+           2        if (type != PS_TYPE_C32 && direction == PS_FFT_REVERSE) {
+      ######            psError(__func__, "Input image must be complex image for reverse FFT (type=%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		
+		    }
+		
+           2        if (type != PS_TYPE_F32 && direction == PS_FFT_FORWARD) {
+      ######            psError(__func__, "Input image must be real image for forward FFT (type=%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L80:
+           2        if (plan == NULL) {
+      ######            psError(__func__, "Failed to create FFTW plan.");
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L101:
+           1        if (in == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+***L131:
+      ######        } else if (type == PS_TYPE_C64) {
+      ######            psF64* outRow;
+      ######            psC64* inRow;
+		
+      ######            out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64);
+      ######            for (unsigned int row = 0; row < numRows; row++) {
+      ######                outRow = out->data.F64[row];
+      ######                inRow = in->data.C64[row];
+		
+      ######                for (unsigned int col = 0; col < numCols; col++) {
+      ######                    outRow[col] = creal(inRow[col]);
+		            }
+		        }
+		    } else {
+      ######            psError(__func__, "Can not extract real component from given image type (%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L159:
+           1        if (in == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L169 (gcov?):
+           1        if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+		        // Warn user, as this is probably not expected
+           1            psLogMsg(__func__, PS_LOG_WARN, "Imaginary portion of a non-Complex type called for. "
+		                 "A zero image was returned.");
+      ######            out = psImageRecycle(out, numCols, numRows, type);
+      ######            memset(out->data.V[0], 0, PSELEMTYPE_SIZEOF(type) * numCols * numRows);
+      ######            return out;
+		    }
+***L191:
+      ######        } else if (type == PS_TYPE_C64) {
+      ######            psF64* outRow;
+      ######            psC64* inRow;
+		
+      ######            out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64);
+      ######            for (unsigned int row = 0; row < numRows; row++) {
+      ######                outRow = out->data.F64[row];
+      ######                inRow = in->data.C64[row];
+		
+      ######                for (unsigned int col = 0; col < numCols; col++) {
+      ######                    outRow[col] = cimag(inRow[col]);
+		            }
+		        }
+		    } else {
+      ######            psError(__func__, "Can not extract imaginary component from given image type (%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L219:
+           3        if (real == NULL || imag == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L240:
+           1        if (PS_IS_PSELEMTYPE_COMPLEX(type)) {
+      ######            psError(__func__, "The inputs to psImageComplex can not be complex.");
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L246:
+           1        if (type != PS_TYPE_F32 && type != PS_TYPE_F64) {
+      ######            psError(__func__, "The input type to psImageComplex must be a floating point.");
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+***L268:
+      ######        } else if (type == PS_TYPE_F64) {
+      ######            psC64* outRow;
+      ######            psF64* realRow;
+      ######            psF64* imagRow;
+		
+      ######            out = psImageRecycle(out, numCols, numRows, PS_TYPE_C64);
+      ######            for (unsigned int row = 0; row < numRows; row++) {
+      ######                outRow = out->data.C64[row];
+      ######                realRow = real->data.F64[row];
+      ######                imagRow = imag->data.F64[row];
+		
+      ######                for (unsigned int col = 0; col < numCols; col++) {
+      ######                    outRow[col] = realRow[col] + I * imagRow[col];
+		            }
+		        }
+		    } else {
+      ######            psError(__func__, "Can not merge real and imaginary portions for given image type (%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L298:
+           1        if (in == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+***L328:
+      ######        } else if (type == PS_TYPE_C64) {
+      ######            psC64* outRow;
+      ######            psC64* inRow;
+		
+      ######            out = psImageRecycle(out, numCols, numRows, PS_TYPE_C64);
+      ######            for (unsigned int row = 0; row < numRows; row++) {
+      ######                outRow = out->data.C64[row];
+      ######                inRow = in->data.C64[row];
+		
+      ######                for (unsigned int col = 0; col < numCols; col++) {
+      ######                    outRow[col] = creal(inRow[col]) - I * cimag(inRow[col]);
+		            }
+		        }
+		    } else {
+      ######            psError(__func__, "Can not compute complex conjugate for given image type (%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L357:
+           1        if (in == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L368 (gcov?):
+           1        if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+           1            psError(__func__, "Power Spectrum for non-complex inputs is not implemented.");
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+***L391:
+      ######        } else if (type == PS_TYPE_C64) {
+      ######            psF64* outRow;
+      ######            psC64* inRow;
+      ######            psF64 real;
+      ######            psF64 imag;
+		
+      ######            out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64);
+      ######            for (unsigned int row = 0; row < numRows; row++) {
+      ######                outRow = out->data.F64[row];
+      ######                inRow = in->data.C64[row];
+		
+      ######                for (unsigned int col = 0; col < numCols; col++) {
+      ######                    real = crealf(inRow[col]);
+      ######                    imag = cimagf(inRow[col]);
+      ######                    outRow[col] = real * real + imag * imag / numElementsSquared;
+		            }
+		        }
+		    } else {
+      ######            psError(__func__, "Can not power spectrum for given image type (%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L427:
+           2        if (in == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L434:
+           2        if ((type != PS_TYPE_F32) && (type != PS_TYPE_C32)) {
+           2            psError(__func__, "Input image must be a 32-bit float or complex image (type=%d)", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L440:
+           2        if ((type != PS_TYPE_C32) && (direction == PS_FFT_REVERSE)) {
+      ######            psError(__func__, "Input image must be complex image for reverse FFT (type=%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		
+		    }
+L447:
+           2        if ((type != PS_TYPE_F32) && (direction == PS_FFT_FORWARD)) {
+      ######            psError(__func__, "Input image must be real image for forward FFT (type=%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L486:
+           2        if (plan == NULL) {
+      ######            psError(__func__, "Failed to create FFTW plan.");
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L505:
+           1        if (in == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+***L514:
+           1        if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+		        // Warn user, as this is probably not expected
+           1            psLogMsg(__func__, PS_LOG_WARN, "Real portion of a non-Complex type called called for. "
+		                 "Just a vector copy was performed.");
+      ######            out = psVectorRecycle(out, numElements, type);
+      ######            out->n = numElements;
+      ######            memcpy(out->data.V, in->data.V, numElements * PSELEMTYPE_SIZEOF(type));
+      ######            return out;
+		    }
+L535:
+		    } else {
+      ######            psError(__func__, "Can not extract real component from given vector type (%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L549:
+           1        if (in == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+***L558:
+           1        if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+		        // Warn user, as this is probably not expected
+           1            psLogMsg(__func__, PS_LOG_WARN, "Imaginary portion of a non-Complex type called for. "
+		                 "A zeroed vector was returned.");
+      ######            out = psVectorRecycle(out, numElements, type);
+      ######            out->n = numElements;
+      ######            memset(out->data.V, 0, PSELEMTYPE_SIZEOF(type) * numElements);
+      ######            return out;
+		    }
+L579:
+		    } else {
+      ######            psError(__func__, "Can not extract imaginary component from given vector type (%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L593:
+           3        if (real == NULL || imag == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L611:
+           2        if (PS_IS_PSELEMTYPE_COMPLEX(type)) {
+      ######            psError(__func__, "The inputs to psVectorComplex can not be complex.");
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L629:
+		    } else {
+      ######            psError(__func__, "Can not merge real and imaginary portions for given vector type (%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L643:
+           1        if (in == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+***L652:
+           1        if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+		        // Warn user, as this is probably not expected
+           1            psLogMsg(__func__, PS_LOG_WARN, "Complex Conjugate of a non-Complex type called for. "
+		                 "Vector copy was performed instead.");
+		
+      ######            out = psVectorRecycle(out, numElements, type);
+      ######            out->n = numElements;
+      ######            memcpy(out->data.V, in->data.V, PSELEMTYPE_SIZEOF(type) * numElements);
+      ######            return out;
+		    }
+L674:
+		    } else {
+      ######            psError(__func__, "Can not compute complex conjugate for given vector type (%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L691:
+           1        if (in == NULL) {
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L703 (gcov?):
+           1        if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+           1            psError(__func__, "Power Spectrum for non-complex inputs is not implemented.");
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+L733:
+		    } else {
+      ######            psError(__func__, "Can not power spectrum for given vector type (%d).", type);
+      ######            psFree(out);
+      ######            return NULL;
+		    }
+
+=============================================================================================================
+
+ 99.32% of 438 source lines executed in file psFunctions.c
+
+L372:
+     1056799        if (myPoly->n == 0) {
+      ######            return (1.0);
+		    }
+L376:
+     1056799        if (NULL == myPoly->coeff) {
+      ######            psAbort(__func__, "psPolynomial1DEval(): myPoly->coeff is NULL\n");
+		    }
+L682:
+           1        if (myPoly->n == 0) {
+      ######            return (1.0);
+		    }
+
+=============================================================================================================
+
+ 94.57% of 258 source lines executed in file psMatrix.c
+
+L507:
+           1            if (outVector->n == 0) {
+      ######                outVector->n = inImage->numRows;
+		        }
+L511:
+           1            if (outVector->n != inImage->numRows) {
+      ######                psError(__func__, "Image and vector sizes differ: (%d vs %d).", inImage->numRows, outVector->n);
+      ######                return outVector;
+		        }
+L521:
+           1            if (outVector->n == 0) {
+      ######                outVector->n = inImage->numCols;
+		        }
+L525:
+           1            if (outVector->n != inImage->numCols) {
+      ######                psError(__func__, "Image and vector sizes differ: (%d vs %d).", inImage->numCols, outVector->n);
+      ######                return outVector;
+		        }
+L550:
+           1            if (outImage->numCols > 1) {
+      ######                psError(__func__, "Image has more than 1 column: numCols = %d.", outImage->numCols);
+      ######                return outImage;
+           1            } else if (outImage->numRows != inVector->n) {
+      ######                psError(__func__, "Image and vector sizes differ: (%d vs %d).", outImage->numRows, inVector->n);
+      ######                return outImage;
+		        }
+L565:
+           1            if (outImage->numRows > 1) {
+      ######                psError(__func__, "Image has more than 1 row: numRows = %d.", outImage->numRows);
+      ######                return outImage;
+           1            } else if (outImage->numCols != inVector->n) {
+      ######                psError(__func__, "Image and vector sizes differ: (%d vs %d).", outImage->numCols, inVector->n);
+      ######                return outImage;
+		        }
+
+=============================================================================================================
+
+ 80.30% of 132 source lines executed in file psMatrixVectorArithmetic.c
+
+L451:
+          58        if (dim1 == PS_DIMEN_OTHER || dim2 == PS_DIMEN_OTHER || dimOut == PS_DIMEN_OTHER) {
+      ######            psError(__func__, ": Line %d - PS_DIMEN_OTHER not allowed for arguments: (%d, %d, %d)", __LINE__,
+		                dim1, dim2, dimOut);
+      ######            return out;
+		    }
+L457:
+          58        if (dim1 == PS_DIMEN_VECTOR) {
+           1            if (((psVector* ) in1)->n == 0) {
+      ######                psError(__func__, ": Line %d - Vector contains zero elements");
+		        }
+          57        } else if (dim1 == PS_DIMEN_IMAGE) {
+          56            if (((psImage* ) in1)->numCols == 0 || ((psImage* ) in1)->numRows == 0) {
+      ######                psError(__func__, ": Line %d - Image contains zero length row or cols");
+		        }
+		    }
+L467:
+          58        if (dim2 == PS_DIMEN_VECTOR) {
+          17            if (((psVector* ) in2)->n == 0) {
+      ######                psError(__func__, ": Line %d - Vector contains zero elements");
+		        }
+          41        } else if (dim2 == PS_DIMEN_IMAGE) {
+          24            if (((psImage* ) in2)->numCols == 0 || ((psImage* ) in2)->numRows == 0) {
+      ######                psError(__func__, ": Line %d - Image contains zero length row or cols");
+		        }
+		    }
+***L477:
+          58        if (dim1 == PS_DIMEN_SCALAR) {
+      ######            if (dim2 == PS_DIMEN_SCALAR) {
+      ######                BINARY_OP(SCALAR, SCALAR, out, psType1, op, psType2);       // scalar op scalar
+      ######            } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+      ######                BINARY_OP(SCALAR, VECTOR, out, psType1, op, psType2);       // scalar op vector
+      ######            } else if (dim2 == PS_DIMEN_IMAGE) {
+      ######                BINARY_OP(SCALAR, IMAGE, out, psType1, op, psType2);        // scalar op image
+		        } else {
+      ######                psError(__func__, ": Line %d - Invalid dimensionality for in2 arg: %d", __LINE__, dim2);
+		        }
+          58        } else if (dim1 == PS_DIMEN_VECTOR || dim1 == PS_DIMEN_TRANSV) {
+           2            if (dim2 == PS_DIMEN_SCALAR) {
+      ######                BINARY_OP(VECTOR, SCALAR, out, psType1, op, psType2);       // vector op scalar
+           2            } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+      ######                BINARY_OP(VECTOR, VECTOR, out, psType1, op, psType2);       // vector op vector
+           2            } else if (dim2 == PS_DIMEN_IMAGE) {
+           2                BINARY_OP(VECTOR, IMAGE, out, psType1, op, psType2);        // vector op image
+		        } else {
+      ######                psError(__func__, ": Line %d - Invalid dimensionality for in2 arg: %d", __LINE__, dim2);
+L504:
+		        } else {
+      ######                psError(__func__, ": Line %d - Invalid dimensionality for in2 arg: %d", __LINE__, dim2);
+		        }
+		    } else {
+      ######            psError(__func__, ": Line %d - Invalid dimensionality for in1 arg: %d", __LINE__, dim1);
+		    }
+L757:
+         138        if (dimIn == PS_DIMEN_OTHER || dimOut == PS_DIMEN_OTHER) {
+      ######            psError(__func__, ": Line %d - PS_DIMEN_OTHER not allowed for arguments: (%d, %d)", __LINE__,
+		                dimIn, dimOut);
+      ######            return out;
+		    }
+L763:
+         138        if (dimIn == PS_DIMEN_VECTOR) {
+          68            if (((psVector* ) in)->n == 0) {
+      ######                psError(__func__, ": Line %d - Vector contains zero elements");
+		        }
+          70        } else if (dimIn == PS_DIMEN_IMAGE) {
+          70            if (((psImage* ) in)->numCols == 0 || ((psImage* ) in)->numRows == 0) {
+      ######                psError(__func__, ": Line %d - Image contains zero length row or cols");
+		        }
+		    }
+L773:
+         138        if (dimOut == PS_DIMEN_VECTOR) {
+          68            if (((psVector* ) out)->n == 0) {
+      ######                psError(__func__, ": Line %d - Vector contains zero elements");
+		        }
+          70        } else if (dimOut == PS_DIMEN_IMAGE) {
+          70            if (((psImage* ) out)->numCols == 0 || ((psImage* ) out)->numRows == 0) {
+      ######                psError(__func__, ": Line %d - Image contains zero length row or cols");
+		        }
+		    }
+***L783:
+         138        if (dimIn == PS_DIMEN_SCALAR) {
+      ######            UNARY_OP(SCALAR, out, psTypeIn, op);    // scalar
+L789:
+		    } else {
+      ######            psError(__func__, ": Line %d - Invalid dimensionality for in arg: %d", __LINE__, dimIn);
+		    }
+
+=============================================================================================================
+
+ 74.93% of 371 source lines executed in file psMinimize.c
+
+IGNORED: Subject to change.  Will test in next release.
+
+=============================================================================================================
+
+ 69.71% of 746 source lines executed in file psStats.c
+
+L130:
+		    case PS_STAT_SAMPLE_STDEV:
+      ######            *value = stats->sampleStdev;
+      ######            return true;
+		
+		    case PS_STAT_ROBUST_MEAN:
+      ######            *value = stats->robustMean;
+      ######            return true;
+		
+		    case PS_STAT_ROBUST_MEDIAN:
+      ######            *value = stats->robustMedian;
+      ######            return true;
+		
+		    case PS_STAT_ROBUST_MODE:
+      ######            *value = stats->robustMode;
+      ######            return true;
+		
+		    case PS_STAT_ROBUST_STDEV:
+      ######            *value = stats->robustStdev;
+      ######            return true;
+		
+		    case PS_STAT_CLIPPED_MEAN:
+      ######            *value = stats->clippedMean;
+      ######            return true;
+		
+		    case PS_STAT_CLIPPED_STDEV:
+      ######            *value = stats->clippedStdev;
+      ######            return true;
+		
+		    case PS_STAT_MAX:
+          37            *value = stats->max;
+          37            return true;
+		
+		    case PS_STAT_MIN:
+      ######            *value = stats->min;
+      ######            return true;
+
+void p_psVectorPrint unused, but comment says it's for debugging: fair enough.
+
+***L231:
+          36        if (stats->options & PS_STAT_USE_RANGE) {
+      ######            rangeMin = stats->min;
+      ######            rangeMax = stats->max;
+      ######            if (maskVector != NULL) {
+      ######                for (i = 0; i < myVector->n; i++) {
+		                // Check if the data is with the specified range
+      ######                    if (!(maskVal & maskVector->data.U8[i]) &&
+		                        (rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        mean += myVector->data.F32[i];
+      ######                        count++;
+		                }
+		            }
+      ######                mean /= (float)count;
+		        } else {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if ((rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        mean += myVector->data.F32[i];
+      ######                        count++;
+		                }
+		            }
+      ######                mean /= (float)count;
+		        }
+***L293:
+          39        if (stats->options & PS_STAT_USE_RANGE) {
+      ######            rangeMin = stats->min;
+      ######            rangeMax = stats->max;
+      ######            if (maskVector != NULL) {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if (!(maskVal & maskVector->data.U8[i])) {
+      ######                        if ((myVector->data.F32[i] > max) &&
+		                            (rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                            max = myVector->data.F32[i];
+		                    }
+		                }
+		            }
+		        } else {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if ((myVector->data.F32[i] > max) &&
+		                        (rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        max = myVector->data.F32[i];
+		                }
+		            }
+		        }
+		    } else {
+***L353:
+           3        if (stats->options & PS_STAT_USE_RANGE) {
+      ######            rangeMin = stats->min;
+      ######            rangeMax = stats->max;
+      ######            if (maskVector != NULL) {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if (!(maskVal & maskVector->data.U8[i])) {
+      ######                        if ((myVector->data.F32[i] < min) &&
+		                            (rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                            min = myVector->data.F32[i];
+		                    }
+		                }
+		            }
+		        } else {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if ((myVector->data.F32[i] < min) &&
+		                        (rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        min = myVector->data.F32[i];
+		                }
+		            }
+		        }
+		    } else {
+***L414:
+        6617        if (stats->options & PS_STAT_USE_RANGE) {
+      ######            rangeMin = stats->min;
+      ######            rangeMax = stats->max;
+      ######            if (maskVector != NULL) {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if (!(maskVal & maskVector->data.U8[i]) &&
+		                        (rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        numData++;
+		                }
+		            }
+		        } else {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if ((rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        numData++;
+		                }
+		            }
+		        }
+		    } else {
+***L502:
+        6613        if (stats->options & PS_STAT_USE_RANGE) {
+      ######            rangeMin = stats->min;
+      ######            rangeMax = stats->max;
+		
+		        // Store all non-masked data points within the min/max range
+		        // into the temporary vectors.
+      ######            count = 0;
+      ######            if (maskVector != NULL) {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if (!(maskVal & maskVector->data.U8[i]) &&
+		                        (rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        unsortedVector->data.F32[count++] = maskVector->data.F32[i];
+		                }
+		            }
+		        } else {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if ((rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        unsortedVector->data.F32[count++] = myVector->data.F32[i];
+		                }
+		            }
+		        }
+		    } else {
+***L596:
+		        } else {
+		            /* If sigma equals zero (all pixels have the same value) the above code will divide by zero.
+		             * Therefore, we don't need to smooth the data. */
+      ######                for (i = 0; i < robustHistogram->nums->n; i++) {
+      ######                    smooth->data.F32[i] = (float)robustHistogram->nums->data.S32[i];
+		            }
+      ######                return (smooth);
+		        }
+***L652:
+           4        if (stats->options & PS_STAT_USE_RANGE) {
+      ######            rangeMin = stats->min;
+      ######            rangeMax = stats->max;
+		        // Store all non-masked data points within the min/max range
+		        // into the temporary vectors.
+      ######            count = 0;
+      ######            if (maskVector != NULL) {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if (!(maskVal & maskVector->data.U8[i]) &&
+		                        (rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        unsortedVector->data.F32[count++] = myVector->data.F32[i];
+		                }
+		            }
+		        } else {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if ((rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        unsortedVector->data.F32[count++] = myVector->data.F32[i];
+		                }
+		            }
+		        }
+		    } else {
+***L732:
+        3605        if (stats->options & PS_STAT_USE_RANGE) {
+      ######            if (maskVector != NULL) {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if (!(maskVal & maskVector->data.U8[i]) &&
+		                        (rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        diff = myVector->data.F32[i] - mean;
+      ######                        sumSquares += (diff * diff);
+      ######                        sumDiffs += diff;
+      ######                        countInt++;
+		                }
+		            }
+		        } else {
+      ######                for (i = 0; i < myVector->n; i++) {
+      ######                    if ((rangeMin <= myVector->data.F32[i]) && (myVector->data.F32[i] <= rangeMax)) {
+      ######                        diff = myVector->data.F32[i] - mean;
+      ######                        sumSquares += (diff * diff);
+      ######                        sumDiffs += diff;
+      ######                        countInt++;
+		                }
+		            }
+      ######                countInt = myVector->n;
+		        }
+		    } else {
+L813:
+           3        if (!((CLIPPED_SIGMA_LB <= stats->clipSigma) && (stats->clipSigma <= CLIPPED_SIGMA_UB))) {
+      ######            psAbort(__func__, "Unallowed value for clipSigma (%f).\n", stats->clipSigma);
+		    }
+
+void p_psNormalizeVector, float p_psGaussian, float p_psGaussianDeriv,
+float p_psQuadratic, float p_psQuadraticDeriv unexercised, but are
+private (should be "static").
+
+L1009:
+          30            if (fabs(midpoint - oldMidpoint) <= FLT_EPSILON) {
+      ######                return (midpoint);
+		        }
+L1016:
+          30            if (fabs(f - getThisValue) <= FLT_EPSILON) {
+      ######                return (midpoint);
+		        }
+
+float p_psFitQuadratic not exercised, comment says "unused".
+
+*L1150:
+           1        if (fabs(binSize) <= FLT_EPSILON) {
+      ######            if (stats->options & PS_STAT_ROBUST_MEAN) {
+      ######                stats->robustMean = stats->clippedMean;
+		        }
+      ######            if (stats->options & PS_STAT_ROBUST_MEDIAN) {
+      ######                stats->robustMedian = stats->clippedMean;
+		        }
+      ######            if (stats->options & PS_STAT_ROBUST_MODE) {
+      ######                stats->robustMode = stats->clippedMean;
+		        }
+      ######            if (stats->options & PS_STAT_ROBUST_STDEV) {
+      ######                stats->robustStdev = 0.0;
+		        }
+      ######            if (stats->options & PS_STAT_ROBUST_QUARTILE) {
+      ######                stats->robustUQ = stats->clippedMean;
+      ######                stats->robustLQ = stats->clippedMean;
+		        }
+		        // XXX: Set these to the number of unmasked data points?
+      ######            stats->robustNfit = 0.0;
+      ######            stats->robustN50 = 0.0;
+      ######            psFree(tmpStats);
+      ######            return;
+		    }
+L1338:
+           1                if (!((y->data.F64[0] <= cumulativeMedian) && (cumulativeMedian <= y->data.F64[2]))) {
+      ######                    printf("((%f), %f, %f)\n", cumulativeMedian, y->data.F64[0], y->data.F64[2]);
+      ######                    psAbort(__func__, "p_psVectorRobustStats(1): midpoint not within y-range\n");
+		            }
+*L1354:
+		        } else {
+		            // If the mode is the first/last histogram bin, then simply use
+		            // the midpoint of that bin.
+      ######                stats->robustMedian = 0.5 * (robustHistogram->bounds->data.F32[maxBinNum + 1] +
+		                                         robustHistogram->bounds->data.F32[maxBinNum]);
+		        }
+L1384:
+           1                if (!((y->data.F64[0] <= (countFloat / 4.0)) && ((countFloat / 4.0) <= y->data.F64[2]))) {
+      ######                    psAbort(__func__, "p_psVectorRobustStats(2): midpoint not within y-range\n");
+		            }
+*L1395:
+		        } else {
+		            // If the LQ is the first/last histogram bin, then simply use
+		            // the midpoint of that bin.
+      ######                stats->robustLQ = 0.5 * (robustHistogram->bounds->data.F32[LQBinNum + 1] +
+		                                     robustHistogram->bounds->data.F32[LQBinNum]);
+		        }
+L1417:
+           1                if (!((y->data.F64[0] <= (3.0 * countFloat / 4.0)) &&
+		                    ((3.0 * countFloat / 4.0) <= y->data.F64[2]))) {
+      ######                    psAbort(__func__, "p_psVectorRobustStats(3): midpoint not within y-range\n");
+		            }
+*L1429:
+		        } else {
+		            // If the UQ is the first/last histogram bin, then simply use
+		            // the midpoint of that bin.
+      ######                stats->robustUQ = 0.5 * (robustHistogram->bounds->data.F32[UQBinNum + 1] +
+		                                     robustHistogram->bounds->data.F32[UQBinNum]);
+		        }
+L1557:
+          28        if (n == 0) {
+      ######            return (NULL);
+		    }
+*****Allocator returning NULL.
+
+L1613:
+           4        if (bounds == NULL) {
+		        // psAbort(__func__, "psHistogram requested with NULL bounds");
+      ######            return (NULL);
+		    }
+L1618:
+           4        if (bounds->n <= 1) {
+		        // psAbort(__func__, "psHistogram requested with NULL bounds");
+      ######            return (NULL);
+		    }
+L1623:
+           4        if (bounds->type.type != PS_TYPE_F32) {
+		        // psAbort(__func__, "psHistogram request a bound which is not type F32");
+      ######            return (NULL);
+		    }
+L1691:
+          22        if (out->nums->type.type != PS_TYPE_U32) {
+      ######            psAbort(__func__, "Only data type PS_TYPE_U32 for output.nums member.");
+		    }
+L1699:
+          21        if (in->type.type != PS_TYPE_F32) {
+      ######            psAbort(__func__, "Only data type PS_TYPE_F32 is currently supported (0x%x).", in->type.type);
+		    }
+L1703:
+          21        if (mask != NULL) {
+          10            if (in->n != mask->n) {
+      ######                psAbort(__func__, "Vector data and vector mask are of different sizes.");
+		        }
+          10            if (mask->type.type != PS_TYPE_U8) {
+      ######                psAbort(__func__, "Vector mask must be type PS_TYPE_U8");
+		        }
+		    }
+***L1720:
+       69415                if (in->data.F32[i] < out->bounds->data.F32[0]) {
+      ######                    out->minNum++;
+		                // Check if this pixel is above the maximum value, and if so
+		                // count it, then skip it.
+       69415                } else if (in->data.F32[i] > out->bounds->data.F32[numBins]) {
+      ######                    out->maxNum++;
+		            } else {
+L1736:
+       69415                        if (binNum >= out->nums->n) {
+      ######                            binNum = out->nums->n - 1;
+		                    }
+***L1744:
+		                } else {
+		                    // NOTE: This is slow.  Put a smarter algorithm here to
+		                    // find the correct bin number (bin search, probably)
+      ######                        for (j = 0; j < (out->bounds->n) - 1; j++) {
+      ######                            if ((out->bounds->data.S32[j] <= in->data.F32[i]) &&
+		                                (in->data.F32[i] <= out->bounds->data.S32[j + 1])) {
+      ######                                (out->nums->data.S32[j])++;
+		                        }
+		                    }
+		                }
+*L1774: Looks to be temporary
+        3091        if (in->type.type == PS_TYPE_S32) {
+      ######            tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+      ######            for (i = 0; i < in->n; i++) {
+      ######                tmp->data.F32[i] = (float)in->data.S32[i];
+		        }
+        3091        } else if (in->type.type == PS_TYPE_U32) {
+      ######            tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+      ######            for (i = 0; i < in->n; i++) {
+      ######                tmp->data.F32[i] = (float)in->data.U32[i];
+		        }
+        3091        } else if (in->type.type == PS_TYPE_F64) {
+      ######            tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+      ######            for (i = 0; i < in->n; i++) {
+      ######                tmp->data.F32[i] = (float)in->data.F64[i];
+		        }
+        3091        } else if (in->type.type == PS_TYPE_F32) {
+L1884:
+        3091        if (mustFreeTmp == 1) {
+      ######            psFree(inF32);
+		    }
+
+
+=============================================================================================================
+
+  0.00% of 289 source lines executed in file psAstrometry.c
+  0.00% of 212 source lines executed in file psCoord.c
+
+These probably not part of the release.
+
+=============================================================================================================
+
+ 75.88% of 369 source lines executed in file psMetadata.c
+
+L137 (gcov?):
+          52        if (metadataItem == NULL) {
+      ######            psAbort(__func__, "Failed to allocate memory");
+		    }
+L213 (gcov?):
+           8        if (list == NULL) {
+      ######            psAbort(__func__, "Failed to allocate list");
+		    }
+L218 (gcov?):
+           8        if (table == NULL) {
+      ######            psAbort(__func__, "Failed to allocate table");
+		    }
+L250:
+          42        if(mdTable == NULL) {
+      ######            psError( __func__, "Null metadata table not allowed" );
+      ######            return false;
+		    }
+L256:
+          42        if(mdList == NULL) {
+      ######            psError( __func__, "Null metadata list not allowed");
+      ######            return false;
+		    }
+L262:
+          42        if(key == NULL) {
+      ######            psError(__func__, "Null key item not allowed");
+      ######            return false;
+		    }
+L274:
+           3                if(!psListAdd(existingEntry->items, metadataItem, PS_LIST_TAIL)) {
+      ######                    psError( __func__, "Couldn't add metadata item to items list. Name: %s",
+		                         metadataItem->name );
+      ######                    return false;
+		            }
+L284:
+           4                if(!psListAdd(newFolderEntry->items, existingEntry, PS_LIST_TAIL)) {
+      ######                    psError( __func__, "Couldn't add metadata item to items list. Name: %s", key);
+      ######                    psFree(newFolderEntry);
+      ######                    return false;
+		            }
+L290:
+           4                if(!psListAdd(newFolderEntry->items, metadataItem, PS_LIST_TAIL)) {
+      ######                    psError( __func__, "Couldn't add metadata item to items list. Name: %s", key);
+      ######                    psFree(newFolderEntry);
+      ######                    return false;
+		            }
+L296:
+           4                if(!psHashRemove(mdTable, key)) {
+      ######                    psError( __func__, "Couldn't remove metadata item from metadata table. Name: %s", key);
+      ######                    psFree(newFolderEntry);
+      ######                    return false;
+		            }
+L302:
+           4                if(!psHashAdd(mdTable, key, newFolderEntry)) {
+      ######                    psError( __func__, "Couldn't add metadata item to metadata table. Name: %s", key);
+      ######                    psFree(newFolderEntry);
+      ######                    return false;
+		            }
+L313:
+           1                if(!psListAdd(metadataItem->items, existingEntry, PS_LIST_TAIL)) {
+      ######                    psError( __func__, "Couldn't add metadata item to items list. Name: %s", key);
+      ######                    return false;
+		            }
+L318:
+           1                if(!psHashRemove(mdTable, key)) {
+      ######                    psError( __func__, "Couldn't remove metadata item from metadata table. Name: %s", key);
+      ######                    return false;
+		            }
+L323:
+           1                if(!psHashAdd(mdTable, key, metadataItem)) {
+      ######                    psError( __func__, "Couldn't add metadata item to metadata table. Name: %s", key);
+      ######                    return false;
+		            }
+L331:
+          34            if(!psHashAdd(mdTable, key, metadataItem)) {
+      ######                psError( __func__, "Couldn't add metadata item to metadata table. Name: %s", key);
+      ######                return false;
+		        }
+L338:
+          42        if(!psListAdd( mdList, metadataItem, where )) {
+      ######            psError( __func__, "Couldn't add metadata item to metadata collection list. Name: %s",
+		                 metadataItem->name );
+      ######            return false;
+		    }
+L363 (gcov):
+          25        if (!psMetadataAddItem(md, where, metadataItem)) {
+          25            psError(__func__, "Couldn't add metadata item to metadata collection list. Name: %s",
+		                metadataItem->name);
+      ######            psFree(metadataItem);
+      ######            return false;
+		    }
+L387:
+           9        if (mdList == NULL) {
+      ######            psError(__func__, "Null metadata list not allowed");
+      ######            return false;
+		    }
+L393:
+           9        if (mdTable == NULL) {
+      ######            psError(__func__, "Null metadata table not allowed");
+      ######            return false;
+		    }
+L414:
+           2                    if (!psListRemove(mdList, entryChild, PS_LIST_UNKNOWN)) {
+      ######                        psError(__func__, "Couldn't remove metadata item from list. Name: %s", key);
+      ######                        return false;
+		                }
+L423:
+           4                if (!psListRemove(mdList, entry, PS_LIST_UNKNOWN)) {
+      ######                    psError(__func__, "Couldn't remove metadata item from list. Name: %s", key);
+      ######                    return false;
+		            }
+L429:
+           5            if (!psHashRemove(mdTable, key)) {
+      ######                psError(__func__, "Couldn't remove metadata item from table. Name: %s", key);
+      ######                return false;
+		        }
+L443:
+           2            if (key == NULL) {
+      ######                psError(__func__, "Null key name not allowed. Index: %d", where);
+      ######                return false;
+		        }
+L466:
+           3        if (mdTable == NULL) {
+      ######            psError(__func__, "Null metadata table not allowed");
+      ######            return NULL;
+		    }
+L497:
+           2        if (mdList == NULL) {
+      ######            psError(__func__, "Null metadata list not allowed");
+      ######            return NULL;
+		    }
+L521:
+           1        if (mdList == NULL) {
+      ######            psError(__func__, "Null metadata list not allowed");
+      ######            return false;
+		    }
+L543:
+           2        if (mdList == NULL) {
+      ######            psError(__func__, "Null metadata list not allowed");
+      ######            return NULL;
+		    }
+L564:
+      ######        if (entry == NULL) {
+      ######            psError(__func__, "Couldn't find metadata item");
+		    }
+**L591:
+           1            if(match != NULL) {
+      ######                if (!strncmp(match, entry->name, strlen(match))) {
+		
+		                // Match found
+      ######                    return entry;
+		            }
+      ######                entry = psListGetPrevious(mdList);
+		        } else {
+L604:
+      ######        if (entry == NULL) {
+      ######            psError(__func__, "Couldn't find metadata item", match);
+		    }
+**L632:
+           1        switch (type) {
+		    case PS_META_BOOL:
+      ######            fprintf(fd, format, metadataItem->data.B);
+      ######            break;
+		    case PS_META_S32:
+           1            fprintf(fd,format, metadataItem->data.S32);
+           1            break;
+		    case PS_META_F32:
+      ######            fprintf(fd, format, metadataItem->data.F32);
+      ######            break;
+		    case PS_META_F64:
+      ######            fprintf(fd, format, metadataItem->data.F64);
+      ######            break;
+		    case PS_META_STR:
+      ######            fprintf(fd, format, metadataItem->data.V);
+      ######            break;
+		    case PS_META_ITEM_SET:
+		    case PS_META_IMG:
+		    case PS_META_JPEG:
+		    case PS_META_PNG:
+		    case PS_META_ASTROM:
+		    case PS_META_UNKNOWN:
+		    default:
+      ######            psError(__func__, " Invalid psMetadataType to print: %d", type);
+		    }
+L683:
+           5        } else if (extName && extNum) {
+      ######            psError(__func__, "Both extName and extNum arguments should not have non zero values.");
+      ######            return NULL;
+		    }
+L703:
+           3        if (fits_get_hdrpos(fd, &numKeys, &keyNum, &status) != 0) {
+      ######            FITS_ERROR("FITS error while reading key %d: %s", keyNum);
+		    }
+L708:
+          25            if (fits_read_keyn(fd, i, keyName, keyValue, keyComment, &status) != 0) {
+      ######                FITS_ERROR("FITS error while reading key %d: %s", keyNum);
+		        }
+L713:
+           7                if (status != VALUE_UNDEFINED) {
+      ######                    FITS_ERROR("FITS error while determining key %d type: %s", keyNum);
+		            } else {
+*L730:
+		        case 'F':
+      ######                metadataItemType = PS_META_F64;
+      ######                success =
+		                psMetadataAdd(output, PS_LIST_TAIL, keyName, metadataItemType, keyComment,
+		                              atof(keyValue));
+      ######                break;
+L749:
+		        default:
+      ######                psError(__func__, "Invalid psMetadataType: %c", keyType);
+      ######                return output;
+		        }
+L754:
+          25            if (!success) {
+      ######                psError(__func__, "Failed to add metadata item. Name: %s", keyName);
+      ######                return output;
+		        }
+
+=============================================================================================================
+
+ 98.09% of 209 source lines executed in file psTime.c
+
+L236:
+           1        if (!strftime(tempString, MAX_TIME_STRING_LENGTH, "%Y/%m/%d,%H:%M:%S", tmTime)) {
+      ######            psError(__func__, " : Line %d - Failed strftime conversion", __LINE__);
+		    }
+L240:
+           1        if (snprintf(timeString, MAX_TIME_STRING_LENGTH, "%s.%3.3d", tempString, ms) < 0) {
+      ######            psError(__func__, " : Line %d - Failed snprintf conversion", __LINE__);
+		    }
+
+=============================================================================================================
+
+Compiling on nkfb0 was annoying: must have taken several hours.
+
+There were a few problems:
+
+* Installing SLALIB was difficult, because they want to use programs
+(like uudecode, pax) that people don't use anyone, so they weren't
+installed.
+
+* It seems that some of the files aren't coming out of CVS.  Perhaps I
+was trying to install over the top of an old copy which caused CVS to
+do some funky stuff.  I just tried it on my own machine, and
+psFFT.[ch] has disappeared from the release.  After some digging
+around, it seems that psFFT.[ch] have been renamed topsVectorFFT.[ch],
+which was done in the repository instead of the(orthodox but, I'll
+admit, annoying) CVS dance with "remove old" and"add new".  This had
+the effect of breaking the rel2 release, since allthe other files have
+#include "psFFT.h" --- the name of the file (evenif updated on the
+mainline) isn't updated in the release because thefiles are tagged,
+and because psFFT.h no longer exists, the release hasbeen broken.
+
+* In order to link in SLALib (written in Fortran), it was necessary to
+add "-DFORTRAN_DOUBLE_UNDERSCORE_SUFFIX" to CFLAGS in the
+Makefile.Globals, and to edit "slalib.h", which was using the illegal
+"#elseif" instead of "#elif" (--> bug 141).  I discovered that I
+needed the double underscore suffix by doing "nm slalib.a" and noting
+that the functions of interest had no prefix underscores, but a double
+suffix on this system.
+
+=============================================================================================================
+
+Attempting to put in a test for PS_TYPE_PTR in psImage has been
+annoying.  I put in a test in tst_psImage.c similar to the other
+lines, which causes the test to fail with a core dump somewhere in
+psMemory: it can't access the refCounter.  I can't trace through the
+program, I think because gdb won't trace through a shared library.
+==> BUG #176.
+
+
+Many instances of multiple types not being tested.  Each usage of a
+#define MACRO should be tested.
+
+Here are the major code segments that aren't being exercised, which
+should be:
+
+psImage.c:
+L240 psImagePixelInterpolate on U8,U16,U32,U64,S8,S16,S32,S64,F64,C32,C64
+
+psImageExtraction.c:
+L201 psImageCopy on S16,U64,C64
+L386 psImageSlice on U8,U16,U32,U64,S8,S16,S32,S64,F64,C32,C64
+
+psImageIO.c:
+L142 psImageReadSection on U8,S8,U16,U32,S32,S64,F64
+L229 psImageWriteSection on U8,S8,U16,U32,S32,S64,F64
+
+psImageManip.c:
+L110 psImageClip on S64,U64
+L156 psImageClipNaN on C32,C64
+L249 psImageOverlaySection on U32,U64,S32,S64
+L421 psImageRebin on U8,U16,U32,U64,S8,S16,S32,S64,F64,C32,C64
+psImageResample Not tested at all
+L588 psImageRotate for 90, 180, 270 on U8,U16,U32,U64,S8,S32,S64,F64,C32,C64
+L797 psImageRotate using bilinear
+
+psHash.c:
+L304 Replacing a key
+
+psScalar.c:
+psScalarCopy
+psScalarFree
+
+psVector.c:
+L214 psVectorSort on U8,U16,U32,U64,S8,S16,S32,S64,F64,C32,C64
+L309 psVectorSortIndex on U8,U16,U32,U64,S8,S16,S32,S64,F64,C32,C64
+
+psFFT.c:
+L131 psImageReal on C64
+L191 psImageImaginary on C64
+L268 psImageComplex on F64
+L328 psImageConjugate on C64
+L391 psImagePowerSpectrum on C64
+L514 psVectorReal on real vector
+L558 psVectorImaginary on real vector
+L652 psVectorConjugate on real vector
+
+psMatrixVectorArithmetic.c:
+L477 First operand is scalar; or second operand is scalar or vector
+L783 Unary operator on a scalar
+
+psStats.c:
+L231 Vector sample mean with PS_STAT_USE_RANGE
+L293 Vector max with PS_STAT_USE_RANGE
+L353 Vector min with PS_STAT_USE_RANGE
+L414 Vector nValues PS_STAT_USE_RANGE
+L502 Vector sample median with PS_STAT_USE_RANGE
+L596 Smoothing a histogram when the vector is single-valued
+L652 Vector quartiles with PS_STAT_USE_RANGE
+L732 Vector SD with PS_STAT_USE_RANGE
+L1354 Robust mode in first/last bin
+L1395 LQ in first/last bin
+L1429 UQ in first/last/bin
+L1720 minNum, maxNum
+L1744 Non-uniform histogram bins
+
+=============================================================================================================
+
+To get psLogMsg output to stderr, you need to specify:
+        psLogSetDestination("dest:stderr");
+Similarly for stdout.  I can see where they got it from (the SDRS
+entry could easily be misinterpreted), but I don't think this is the
+behaviour we expected.
+
+psTrace:
+
+* Each "facility" must commence with a dot: e.g.,
+".IPP.debias.overscan", which I think is really annoying because it's
+easy to forget.
+
+* You must set every level for each node on the trace tree explicitly if
+you intend it to be used.  For example, the call
+        psTraceSetLevel(".IPP.debias.overscan", 5)
+will produce the following setup:
+.                    0
+ IPP                 5
+ debias              5
+ overscan            5
+Note how each of those is in the same level, so that the hierarchy
+hasn't been built up?  Of course, this is a bug because it does
+something completely unexpected, but I would appreciate confirmation
+of my expectation that a 4-deep heirarchy (".", ".A", ".A.B",
+".A.B.C") should be formed from simply specifying a level for
+".A.B.C".
+
+* I would have thought that the property of inheritance that we
+specified implied that if I hadn't explicitly set the level for
+".A.B", but I have set the level for ".A", then printing a trace
+message on component ".A.B" would depend on comparing the level to the
+set level of ".A"; but in the implementation, it would be ignored
+completely.
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/ups/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/ups/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/ups/Makefile	(revision 22322)
@@ -0,0 +1,12 @@
+all :
+	@: no default actions
+
+install :
+	cp Makefile $(PSLIB_DIR)/ups
+	eups_expandtable actUtils.table $(PSLIB_DIR)/ups
+
+clean:
+	$(RM) *~ core*
+
+distclean: clean
+	@: nothing extra to do
Index: /tags/sj_tags/sj_root_20080929/archive/pslib/ups/pslib.table
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/pslib/ups/pslib.table	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/pslib/ups/pslib.table	(revision 22322)
@@ -0,0 +1,5 @@
+setupRequired("fftw")
+pathAppend(LD_LIBRARY_PATH, ${UPS_PROD_DIR}/lib)
+
+Flavor=Darwin
+pathAppend(DYLD_LIBRARY_PATH, ${UPS_PROD_DIR}/lib)
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/Makefile	(revision 22322)
@@ -0,0 +1,100 @@
+default: psphot
+help:
+	@echo "USAGE: make psphot"
+
+CC      =       gcc -g -Wall
+SRC     =       src
+BIN     =       bin
+
+DESTBIN =       /home/eugene/src/bin/$(ARCH)
+
+LPSLIB  :=      $(shell pslib-config --libs)
+IPSLIB  :=      $(shell pslib-config --cflags)
+
+INCS	= 	$(IPSLIB)
+LIBS	= 	-lpsmodule $(LPSLIB)
+CFLAGS	=	$(INCS) -std=c99
+LFLAGS	=	$(LIBS)
+
+PSPHOT = \
+$(SRC)/psphot.$(ARCH).o \
+$(SRC)/pspsf.$(ARCH).o \
+$(SRC)/psphot-utils.$(ARCH).o \
+$(SRC)/psPolynomials.$(ARCH).o \
+$(SRC)/psUtils.$(ARCH).o
+
+psphot: $(BIN)/psphot.$(ARCH)
+
+$(BIN)/psphot.$(ARCH) : $(PSPHOT)
+
+# $(PSPHOT): $(INC)/psphot.h
+
+PSTEST = \
+$(SRC)/pstest.$(ARCH).o
+
+pstest: $(BIN)/pstest.$(ARCH)
+
+$(BIN)/pstest.$(ARCH) : $(PSTEST)
+
+MODELTEST = \
+$(SRC)/modeltest.$(ARCH).o \
+$(SRC)/psUtils.$(ARCH).o
+
+modeltest: $(BIN)/modeltest.$(ARCH)
+
+$(BIN)/modeltest.$(ARCH) : $(MODELTEST)
+
+POLYTEST = \
+$(SRC)/polytest.$(ARCH).o \
+$(SRC)/psUtils.$(ARCH).o
+
+polytest: $(BIN)/polytest.$(ARCH)
+
+$(BIN)/polytest.$(ARCH) : $(POLYTEST)
+
+POLY2DTEST = \
+$(SRC)/poly2dtest.$(ARCH).o \
+$(SRC)/psUtils.$(ARCH).o
+
+poly2dtest: $(BIN)/poly2dtest.$(ARCH)
+
+$(BIN)/poly2dtest.$(ARCH) : $(POLY2DTEST)
+
+INSTALL = psphot
+
+# dependancy rules for binary code #########################
+.PRECIOUS: %.$(ARCH).o
+.PRECIOUS: $(BIN)/%.$(ARCH)
+
+%.$(ARCH).o : %.c
+	$(CC) $(CFLAGS) -c $< -o $@
+
+$(BIN)/%.$(ARCH) : $(SRC)/%.$(ARCH).o
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	$(CC) $^ -o $@ $(LFLAGS)
+	@echo "done with $@"
+
+$(DESTBIN)/%: $(BIN)/%.$(ARCH)
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	rm -f $(DESTBIN)/$*
+	cp $(BIN)/$*.$(ARCH) $(DESTBIN)/$*
+
+$(INSTALL): % : $(BIN)/%.$(ARCH)
+
+%.clean :
+	rm -f $(BIN)/$*.$(ARCH)
+	rm -f $(SRC)/*.$(ARCH).o
+
+%.install:
+	make $(DESTBIN)/$*
+
+# utilities #################################################
+
+install:
+	for i in $(INSTALL); do make $$i.install; done
+
+clean:	
+	rm -f $(BIN)/*.$(ARCH)
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/scripts/phase2.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/scripts/phase2.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/scripts/phase2.pl	(revision 22322)
@@ -0,0 +1,65 @@
+#!/usr/bin/env perl
+
+sub usage {
+    # input is foo.fits
+    # outputs are outroot.im.fits (image) outroot.ob.fits (object list), outroot.log, etc
+    print STDERR "USAGE: phase2 (input) (outroot)\n";
+    exit 2;
+}
+
+if (@ARGV != 4) { &usage; }
+
+# base file needs to be hardwired, from environment, or based on program name
+$base   = psMetadataParseConfig (NULL, basefile, FALSE);  
+
+# probably defined in the base file?
+$site   = psMetadataParseConfig (NULL, sitefile, FALSE);  
+
+# this should be optionally specified as part of the command-line options
+# alternatively, this should be derived from the input file header data 
+$camera = psMetadataParseConfig (NULL, camerafile, FALSE);
+
+# we need a way to define an optional alternative recipe
+$recipe = psMetadataParseConfig (NULL, recipefile, FALSE);  # defined in the camera file
+
+# load only the headers from the given file input a list of headers
+@headers = psFITSReadHeaderSet ($input);
+
+# check the validity of the given set of headers based on expectactions for this camera
+# ** where does camera come from?
+# ** how do we do this if the camera is derived from the header info?
+$status = pmCameraValidateHeaders ($headers, $camera);
+
+# this gets a bit tenuous: what do we do with the collection?  
+
+# construct a complete, empty (ie, no pixel data) FPA structure for this camera / header set
+# we need to construct the output container.  is it big enough to fit in memory?  assume so?
+$FPA = pmFPAfromHeader ($headers, $camera);
+
+### now we work on individual readouts:
+
+# we have the FPA, already validated
+# as part of the FPA, we have access to a specified READOUT and we know the corresponding EXTNAME
+
+# load in the complete readout:
+$image    = psImageReadSection (NULL, 0, 0, 0, 0, 0, $extname, $Nreadout, $input);
+
+# load in the complete corresponding bias image (this should be done once per cell)
+# in this form, we are implying that the bias image is guaranteed to have the corresponding EXTNAME
+$bias     = psImageReadSection (NULL, 0, 0, 0, 0, 0, $extname, 0, $biasname);
+
+# overscan options:
+# - overscan is defined by the recipe (BIAS.OVERSCAN has form [Xs,Xe:Ys:Ye])
+# - overscan is defined by the header (BIAS.OVERSCAN has value BIASSEC)
+# - overscan is not used (BIAS.OVERSCAN has value NONE)
+
+# bias section from header:
+$ = psMetadataLookup ($readout->metadata, "BIASSEC");
+$overscan = psImageSubsection ($image, $metaitem->data.void);
+
+psListAdd ($overlist, $overscan, PS_LIST_HEAD);
+
+$bias = psImageReadSection (NULL, 0, 0, 0, 0, 0, $extname, -1, $biasname);
+
+$image_b = pmSubtractBias ($image, NULL, $overlist, PM_OVERSCAN_COLUMNS, $stats, 0, PM_FIT_NONE, $bias);
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/Makefile	(revision 22322)
@@ -0,0 +1,30 @@
+SHELL = /bin/sh
+CC = gcc
+CFLAGS += -O0 -g -std=c99 -Werror -I/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/include/ -D_GNU_SOURCE
+PSLIB += -L/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/lib/ -lpslib -lgsl -lgslcblas -lfftw3f -lsla -lcfitsio -lm -lxml2 -lmysqlclient
+LDFLAGS += $(PSLIB)
+
+BUILD_OBJS = test_pmFPAfromHeader.o pmCameraFromHeader.o pmFPAFromHeader.o psAdditionals.o papStuff.o
+BUILD_TARGET = test_pmFPAfromHeader
+
+READ_OBJS = test_pmFPARead.o pmCameraFromHeader.o pmFPARead.o papFocalPlane.o psAdditionals.o papStuff.o \
+		pmFPAConstruct.o pmFPAMorph.o pmFPAWrite.o
+READ_TARGET = test_pmFPARead
+
+.PHONY: tags clean empty
+
+.c.o:
+		$(CC) -c $(CFLAGS) $(OPTFLAGS) $<
+
+$(READ_TARGET):	$(READ_OBJS)
+		$(CC) $(CFLAGS) -o $@ $(READ_OBJS) $(LDFLAGS) $(OPTFLAGS)
+
+clean:
+		-$(RM) *.o gmon.* profile.txt
+
+empty:		clean
+		-$(RM) $(TARGET) TAGS
+
+# Tags for emacs
+tags:
+		etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/arraytest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/arraytest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/arraytest.c	(revision 22322)
@@ -0,0 +1,59 @@
+# include <pslib.h>
+
+// test psMinimizeLMChi2
+int main (int argc, char **argv) {
+
+    int i;
+    psArray  *x;
+    psVector  *y;
+    static char *data = "test line";
+
+    // create data array
+    x = psArrayAlloc (100);
+    x->n = 0;
+    for (i = 0; i < 500; i++) {
+      psArrayAdd (x, 100, psStringCopy (data));
+    }
+
+    // create data vector
+    y = psVectorAlloc (100);
+    y->n = 0;
+    for (i = 0; i < y->n; i++) {
+      n = y->n;
+      y->data.F32[n] = 2*i;
+      psVectorExtend (y, 100, 1);
+      // increments n by 1, extends length if needed by 100
+    }
+    exit (0);
+}
+
+static psHash *timers = NULL;
+
+bool psTimerStart (char *name) {
+
+    psTime *start;
+
+    if (timers == NULL) timers = psHashAlloc (16);
+
+    start = psTimeGetTime (PS_TIME_UTC);
+
+    psHashAdd (timers, name, start);
+
+    return (TRUE);
+}
+
+psF64 psTimerMark (char *name) {
+
+    psTime *start;
+    psTime *mark;
+    psF64   delta;
+
+    if (timers == NULL) return (0);
+
+    start = psHashLookup (timers, name);
+    if (start == NULL) return (0);
+
+    mark = psTimeGetTime (PS_TIME_UTC);
+    delta = psTimeDelta (mark, start);
+    return (delta);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/concepts.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/concepts.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/concepts.txt	(revision 22322)
@@ -0,0 +1,115 @@
+We need to be able to do two things:
+
+1. Given an identifier (root name, observation number, whatever), read
+   in the whole FPA (e.g., for phase 1 and 3).  This means that we
+   need to be able to identify the files for each chip, and the
+   extensions.
+
+2. Given a file, identify what it is: observation number + chip/cell.
+   This is so that we can identify the filter, airmass, etc.  We can't
+   expect that these will always be specified in the chip level FITS
+   headers, but might be stored in a database under the observation
+   number.
+
+That is, we need to be able to identify the components given the
+whole, and we need to identify the whole given the component.
+
+
+
+Here is the list of IPP concepts, each of which must be specified somewhere.
+
+FPA-level:
+FPA.AIRMASS		Airmass at which the observation is made (boresight)
+FPA.FILTER		Filter used
+FPA.POSANGLE		Position angle
+FPA.RA			Right Ascension of boresight
+FPA.DEC			Declination of boresight
+FPA.RADECSYS		System of RA,Dec (e.g., J2000 or ICRS)
+FPA.NAME		An identifier (e.g., observation number) for the FPA instance
+FPA.DATE		UT date (YYYY-MM-DD)
+FPA.TIME		UT time (HH:MM:SS.sss)
+FPA.DATETIME		UT date and time (YYYY-MM-DDTHH:MM:SS.ss)
+FPA.MJD			Modified Julian Date
+
+Chip-level:
+CHIP.NAME		The name of the chip (unique within the FPA) --- set at read
+CHIP.FPA		FPA identifier
+
+Cell/Readout-level:
+CELL.NAME		The name of the cell (unique within the parent chip) --- set at read
+CELL.CHIP		Chip identifier --- might be set at read
+CELL.DATE		Date of observation start
+CELL.TIME		Time of observation start
+CELL.EXPOSURE		Exposure time of image
+CELL.DARKTIME		Dark time for image
+CELL.BIASSEC		Overscan region(s)
+CELL.TRIMSEC		Trim region
+CELL.GAIN		CCD gain (e/ADU)
+CELL.READNOISE		CCD read noise (e)
+CELL.SATURATION		CCD saturation point (ADU)
+CELL.BAD		CCD bad pixel point (ADU)
+
+CELL.XBIN		Binning in x
+CELL.YBIN		Binning in y
+CELL.XPARITY		Direction of readout in x
+CELL.YPARITY		Direction of readout in y
+
+There is no reason why concepts higher up can't be specified lower
+down (multiple times, at the risk of introducing the potential for
+value to become out of sync).  And there is no reason why (most)
+concepts lower down can't be specified higher up, and simply be
+inherited.  The exception to this is the FPA.NAME (observation
+number), which allows an FPA to be identified given a cell, etc.
+
+There are so many ways of specifying dates and times.  We probably
+want a high-level function, psCellGetTimeObs() to sort through all of the
+possibilities.
+
+Similarly with binning: CCDSUM is a common header, or the bin factors
+might be in separate headers.
+
+
+Retrieval
+=========
+
+Each of the concepts can be retrieved in the following ways:
+
+1. FITS header translation: the camera configuration contains a
+   "TRANSLATION" entry, which gives the concept along with the FITS
+   header equivalent.
+
+2. Database: the camera configuration "DATABASE" entry contains
+   information on how to poll the database for the required concept.
+   The DATABASE entry contains metadatas specified using the TYPE:
+
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+
+   An example DATABASE entry is:
+
+	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+
+   This says, in the database, you basically do: "SELECT gain IN
+   Camera where chipId == CHIP.NAME and cellId == CELL.NAME" (where I
+   have, no doubt, taken liberty with the SQL query).  In this case,
+   CHIP.NAME and CELL.NAME are looked up before the query is executed,
+   using means other than a DB query (otherwise we could get stuck in
+   an infinite loop).
+
+3. Defaults: the camera configuration "DEFAULTS" entry contains
+   default values.  These default values may simply be specified, or
+   they may be dependent upon another value.  In the latter case, an
+   additional keyword, "CONCEPT_DEPEND", where "CONCEPT" is the
+   concept of interest, and "DEPEND" is literal, is specified with the
+   value being the concept on which it is dependent.  Then, the
+   concept is a METADATA type, with the elements being values of the
+   other concept on which it is dependent.  An example is helpful:
+
+	CELL.YPARITY_DEPEND	STR	CHIP.NAME
+	CELL.YPARITY	METADATA
+		ccd00	S32	-1
+		ccd35	S32	1
+	END
+
+   This says that the CELL.YPARITY is dependent upon the CHIP.NAME:
+   ccd00 has negative parity, and ccd35 has positive parity.
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/gpc1_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/gpc1_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/gpc1_raw.config	(revision 22322)
@@ -0,0 +1,207 @@
+# The raw GPC data comes off the telescope with each of the chips stored in separate files
+
+# How to identify this type
+RULE	METADATA
+#	TELESCOP	STR	PS1
+#	DETECTOR	STR	GPC1
+	EXTEND		BOOL	T
+	NEXTEND		S32	64
+	NAMPS		S32	64
+END
+
+# How to read this data
+PHU		STR	CHIP	# The FITS file represents a single chip
+EXTENSIONS	STR	CELL	# The extensions represent cells
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, type
+	xy00	STR	pitch10u
+	xy01	STR	pitch10u
+	xy02	STR	pitch10u
+	xy03	STR	pitch10u
+	xy04	STR	pitch10u
+	xy05	STR	pitch10u
+	xy06	STR	pitch10u
+	xy07	STR	pitch10u
+	xy10	STR	pitch10u
+	xy11	STR	pitch10u
+	xy12	STR	pitch10u
+	xy13	STR	pitch10u
+	xy14	STR	pitch10u
+	xy15	STR	pitch10u
+	xy16	STR	pitch10u
+	xy17	STR	pitch10u
+	xy20	STR	pitch10u
+	xy21	STR	pitch10u
+	xy22	STR	pitch10u
+	xy23	STR	pitch10u
+	xy24	STR	pitch10u
+	xy25	STR	pitch10u
+	xy26	STR	pitch10u
+	xy27	STR	pitch10u
+	xy30	STR	pitch10u
+	xy31	STR	pitch10u
+	xy32	STR	pitch10u
+	xy33	STR	pitch10u
+	xy34	STR	pitch10u
+	xy35	STR	pitch10u
+	xy36	STR	pitch10u
+	xy37	STR	pitch10u
+	xy40	STR	pitch10u
+	xy41	STR	pitch10u
+	xy42	STR	pitch10u
+	xy43	STR	pitch10u
+	xy44	STR	pitch10u
+	xy45	STR	pitch10u
+	xy46	STR	pitch10u
+	xy47	STR	pitch10u
+	xy50	STR	pitch10u
+	xy51	STR	pitch10u
+	xy52	STR	pitch10u
+	xy53	STR	pitch10u
+	xy54	STR	pitch10u
+	xy55	STR	pitch10u
+	xy56	STR	pitch10u
+	xy57	STR	pitch10u
+	xy60	STR	pitch10u
+	xy61	STR	pitch10u
+	xy62	STR	pitch10u
+	xy63	STR	pitch10u
+	xy64	STR	pitch10u
+	xy65	STR	pitch10u
+	xy66	STR	pitch10u
+	xy67	STR	pitch10u
+	xy70	STR	pitch10u
+	xy71	STR	pitch10u
+	xy72	STR	pitch10u
+	xy73	STR	pitch10u
+	xy74	STR	pitch10u
+	xy75	STR	pitch10u
+	xy76	STR	pitch10u
+	xy77	STR	pitch10u
+END
+
+# Specify the cell data
+CELLS	METADATA
+	pitch10u	METADATA
+		CELL.BIASSEC	STR	VALUE:[575:606,1:594]
+		CELL.TRIMSEC	STR	VALUE:[1:574,1:594]
+	#	CELL.BIASSEC	STR	HEADER:BIASSEC
+	#	CELL.TRIMSEC	STR	HEADER:DATASEC
+	END
+
+	# This is just in here for fun
+	pitch12u	METADATA
+		CELL.BIASSEC	STR	VALUE:[1:10,1:512];[523:574,1:512]
+		CELL.TRIMSEC	STR	VALUE:[11:522,1:512]
+	#	CELL.BIASSEC	STR	HEADER:BIASSEC
+	#	CELL.TRIMSEC	STR	HEADER:TRIMSEC
+	END
+END
+
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	CELL.BIN	STR	CCDSUM
+	CELL.SATURATION	STR	SATURATE
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.AIRMASS	F32	0.0
+	FPA.FILTER	STR	NONE
+	FPA.POSANGLE	F32	0.0
+	FPA.RA		STR	0:0:0
+	FPA.DEC		STR	0:0:0
+	FPA.RADECSYS	STR	ICRS
+	FPA.NAME	S32	0
+	FPA.MJD		F32	12345.6789
+	CELL.EXPOSURE	F32	0.0
+	CELL.DARKTIME	F32	0.0
+	CELL.GAIN	F32	1.0
+	CELL.READNOISE	F32	0.0
+	CELL.BAD	S32	0
+	CELL.BIN	S32	1
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.TEST_DEPEND	STR	CELL.NAME
+	CELL.TEST	METADATA
+		xy00	S32	00
+		xy01	S32	01
+		xy02	S32	02
+		xy03	S32	03
+		xy04	S32	04
+		xy05	S32	05
+		xy06	S32	06
+		xy07	S32	07
+		xy10	S32	10
+		xy11	S32	11
+		xy12	S32	12
+		xy13	S32	13
+		xy14	S32	14
+		xy15	S32	15
+		xy16	S32	16
+		xy17	S32	17
+		xy20	S32	20
+		xy21	S32	21
+		xy22	S32	22
+		xy23	S32	23
+		xy24	S32	24
+		xy25	S32	25
+		xy26	S32	26
+		xy27	S32	27
+		xy30	S32	30
+		xy31	S32	31
+		xy32	S32	32
+		xy33	S32	33
+		xy34	S32	34
+		xy35	S32	35
+		xy36	S32	36
+		xy37	S32	37
+		xy40	S32	40
+		xy41	S32	41
+		xy42	S32	42
+		xy43	S32	43
+		xy44	S32	44
+		xy45	S32	45
+		xy46	S32	46
+		xy47	S32	47
+		xy50	S32	50
+		xy51	S32	51
+		xy52	S32	52
+		xy53	S32	53
+		xy54	S32	54
+		xy55	S32	55
+		xy56	S32	56
+		xy57	S32	57
+		xy60	S32	60
+		xy61	S32	61
+		xy62	S32	62
+		xy63	S32	63
+		xy64	S32	64
+		xy65	S32	65
+		xy66	S32	66
+		xy67	S32	67
+		xy70	S32	70
+		xy71	S32	71
+		xy72	S32	72
+		xy73	S32	73
+		xy74	S32	74
+		xy75	S32	75
+		xy76	S32	76
+		xy77	S32	77
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP,CELL
+	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP,CELL
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+
+END
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/instruments.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/instruments.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/instruments.txt	(revision 22322)
@@ -0,0 +1,34 @@
+Each instrument has its own configuration file, which specifies how to
+identify the instrument based on FITS headers and how to read the data
+in.  The former is specified in a sub-metadata section labelled
+"RULE", and is fairly straight-forward: you simply specify what FITS
+keywords that instrument can be expected to have, along with the value
+that it should have.  "TELESCOP" and "INSTRUME" are useful, if present,
+for this purpose.
+
+Specifying how the data is to be read in is a bit more complicated.
+This is specified by several keywords.  The two most important are
+"PHU" and "EXTENSIONS".  "PHU" specifies what the FITS file
+represents, and may have one of two values: "FPA" indicates that
+the file contains an entire focal plane array; "CHIP" indicates that
+the file contains a single chip.
+
+"EXTENSIONS" specifies what each of the extensions represents, and may
+be one of the following values, with the proviso that it cannot
+specify a higher level than that specified by "PHU": "CHIP" indicates
+that each extension is a chip; "CELL" indicates that each extension is
+a cell; "NONE" indicates that there are no extensions.
+
+The other two important keywords are "CONTENTS" and "CELLS".  The
+contents of "CONTENTS" varies according to the values of "PHU" and
+"EXTENSIONS", as shown in the below table, but "CELLS" contains
+metadata with values for each cell name or cell type.
+
+PHU	EXTENSIONS	EXAMPLE		CONTENTS
+===	==========	=======		========
+FPA	CHIP		megacam_splice	For each chip, a list of cell types that comprise the chip.
+FPA	CELL		megacam_raw	For each extension, the chip and cell type, separated by a colon.
+FPA	NONE		lris_blue	For each chip, a list of cell types that comprise the chip
+CHIP	CELL		gpc1_raw	For each extension, the cell type
+CHIP	NONE		lris_red	The cells that comprise the chip
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/ipprc.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/ipprc.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/ipprc.config	(revision 22322)
@@ -0,0 +1,17 @@
+### Example .ipprc file
+
+
+### Database configuration
+DBSERVER	STR	ippdb.ifa.hawaii.edu	# Database host name (for psDBInit)
+DBUSER		STR	ipp			# Database user name (for psDBInit)
+DBPASSWORD	STR	password		# Database password (for psDBInit)
+
+### Setups for each camera system
+INSTRUMENTS	METADATA
+	MEGACAM_RAW	STR	megacam_raw.config
+	MEGACAM_SPLICE	STR	megacam_splice.config
+	GPC1_RAW	STR	gpc1_raw.config
+	LRIS_BLUE	STR	lris_blue.config
+	LRIS_RED	STR	lris_red.config
+END
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/lris_blue.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/lris_blue.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/lris_blue.config	(revision 22322)
@@ -0,0 +1,89 @@
+# The Low Resolution Imager and Spectrograph (LRIS) blue side
+
+# We have no choice but to hard-code the various regions, because Keck
+# only stores them as:
+# WINDOW  = '1,0,0,2048,4096'
+# PREPIX  =                   51
+# POSTPIX =                   80
+# BINNING = '1,1     '
+# AMPPSIZE= '[1:1024,1:4096]'
+
+# I don't know how we would get the IPP to react to changes in the
+# windowing on the fly --- we have no mechanism for setting the region
+# sizes on the basis of the above keywords.  Therefore, we hard-code
+# the regions and assert on our assumptions in the RULE.
+
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	Keck I
+	INSTRUME	STR	LRISBLUE
+	AMPLIST		STR	1,4,0,0
+	WINDOW		STR	1,0,0,2048,4096
+	PREPIX		S32	51
+	POSTPIX		S32	80
+	BINNING		STR	1,1
+	AMPPSIZE	STR	[1:1024,1:4096]
+	NAXIS1		S32	4620
+	NAXIS2		S32	4096
+END
+
+# How to read this data
+PHU		STR	FPA	# The FITS file represents an entire FPA
+EXTENSIONS	STR	NONE	# There are no extensions
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	LeftChip	STR	amp1 amp2
+	RightChip	STR	amp3 amp4
+END
+
+# Specify the cell data
+CELLS	METADATA
+	amp1		METADATA
+		CELL.BIASSEC	STR	VALUE:[1:51,1:4096];[4301:4380,1:4096]
+		CELL.TRIMSEC	STR	VALUE:[205:1228,1:4096]
+		CELL.GAIN	STR	VALUE:1.2
+		CELL.READNOISE	STR	VALUE:5.6
+	END
+
+	amp2	METADATA
+		CELL.BIASSEC	STR	VALUE:[52:102,1:4096];[4381:4460,1:4096]
+		CELL.TRIMSEC	STR	VALUE:[1229:2252,1:4096]
+		CELL.GAIN	STR	VALUE:1.3
+		CELL.READNOISE	STR	VALUE:6.7
+	END
+
+	amp3		METADATA
+		CELL.BIASSEC	STR	VALUE:[103:153,1:4096];[4461:4540,1:4096]
+		CELL.TRIMSEC	STR	VALUE:[2253:3276,1:4096]
+		CELL.GAIN	STR	VALUE:1.4
+		CELL.READNOISE	STR	VALUE:7.8
+	END
+
+	amp4	METADATA
+		CELL.BIASSEC	STR	VALUE:[154:204,1:4096];[4541:4620,1:4096]
+		CELL.TRIMSEC	STR	VALUE:[3277:4300,1:4096]
+		CELL.GAIN	STR	VALUE:1.5
+		CELL.READNOISE	STR	VALUE:8.9
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.FILTER	STR	BLUFILT
+	FPA.POSANGLE	STR	ROTPOSN
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	CELL.EXPOSURE	STR	EXPOSURE
+	CELL.DARKTIME	STR	EXPOSURE	// No special darktime header; use exposure time
+	CELL.DATE	STR	DATE		// NOTE: There are TWO keywords called "DATE" (creation, exp)!
+	CELL.TIME	STR	UT
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.RADECSYS	STR	ICRS
+END
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/lris_red.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/lris_red.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/lris_red.config	(revision 22322)
@@ -0,0 +1,73 @@
+# The Low Resolution Imager and Spectrograph (LRIS) red side
+
+# We have no choice but to hard-code the various regions, because Keck
+# only stores them as:
+# WINDOW  = '0,0,0,2048,2048'
+# PREPIX  =                   20
+# POSTPIX =                   80
+# BINNING = '1,1     '
+# AMPPSIZE= '[1:1024,1:4096]'
+
+# I don't know how we would get the IPP to react to changes in the
+# windowing on the fly --- we have no mechanism for setting the region
+# sizes on the basis of the above keywords.  Therefore, we hard-code
+# the regions and assert on our assumptions in the RULE.
+
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	Keck I
+	INSTRUME	STR	LRIS
+	AMPLIST		STR	2,1,0,0
+	WINDOW		STR	0,0,0,2048,2048
+	PREPIX		S32	20
+	POSTPIX		S32	80
+	BINNING		STR	1, 1
+	CCDPSIZE	STR	[1:2048,1:2048]
+	NAXIS1		S32	2248
+	NAXIS2		S32	2048
+	IMTYPE		STR	TWOAMPTOP
+END
+
+# How to read this data
+PHU		STR	CHIP	# The FITS file represents a single chip
+EXTENSIONS	STR	NONE	# There are no extensions
+
+# What's in the FITS file?
+CONTENTS	STR	LeftSide RightSide
+
+# Specify the cell data
+CELLS	METADATA
+	LeftSide	METADATA
+		CELL.BIASSEC	STR	VALUE:[1:20,1:2048];[2089:2168,1:2048]
+		CELL.TRIMSEC	STR	VALUE:[41:1064,1:2048]
+		CELL.GAIN	STR	VALUE:1.2
+		CELL.READNOISE	STR	VALUE:5.6
+	END
+
+	RightSide	METADATA
+		CELL.BIASSEC	STR	VALUE:[21:40,1:2048];[2169:2248,1:2048]
+		CELL.TRIMSEC	STR	VALUE:[1065:2088,1:2048]
+		CELL.GAIN	STR	VALUE:1.3
+		CELL.READNOISE	STR	VALUE:6.5
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	POSANG
+	FPA.RA		STR	OBJ-RA
+	FPA.DEC		STR	OBJ-DEC
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.DATE	STR	DATE-OBS
+	CELL.TIME	STR	TIME-OBS
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.RADECSYS	STR	ICRS
+END
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/megacam_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/megacam_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/megacam_raw.config	(revision 22322)
@@ -0,0 +1,183 @@
+# The raw MegaCam data comes off the telescope with each of the chips stored in extensions of a MEF file.
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	MegaCam
+	EXTEND		BOOL	T
+	NEXTEND		S32	72
+END
+
+# How to read this data
+PHU		STR	FPA	# The FITS file represents an entire FPA
+EXTENSIONS	STR	CELL	# The extensions represent cells
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:type
+	amp00	STR	ccd00:left
+	amp01	STR	ccd00:right
+	amp02	STR	ccd01:left
+	amp03	STR	ccd01:right
+	amp04	STR	ccd02:left
+	amp05	STR	ccd02:right
+	amp06	STR	ccd03:left
+	amp07	STR	ccd03:right
+	amp08	STR	ccd04:left
+	amp09	STR	ccd04:right
+	amp10	STR	ccd05:left
+	amp11	STR	ccd05:right
+	amp12	STR	ccd06:left
+	amp13	STR	ccd06:right
+	amp14	STR	ccd07:left
+	amp15	STR	ccd07:right
+	amp16	STR	ccd08:left
+	amp17	STR	ccd08:right
+	amp18	STR	ccd09:left
+	amp19	STR	ccd09:right
+	amp20	STR	ccd10:left
+	amp21	STR	ccd10:right
+	amp22	STR	ccd11:left
+	amp23	STR	ccd11:right
+	amp24	STR	ccd12:left
+	amp25	STR	ccd12:right
+	amp26	STR	ccd13:left
+	amp27	STR	ccd13:right
+	amp28	STR	ccd14:left
+	amp29	STR	ccd14:right
+	amp30	STR	ccd15:left
+	amp31	STR	ccd15:right
+	amp32	STR	ccd16:left
+	amp33	STR	ccd16:right
+	amp34	STR	ccd17:left
+	amp35	STR	ccd17:right
+	amp36	STR	ccd18:left
+	amp37	STR	ccd18:right
+	amp38	STR	ccd19:left
+	amp39	STR	ccd19:right
+	amp40	STR	ccd20:left
+	amp41	STR	ccd20:right
+	amp42	STR	ccd21:left
+	amp43	STR	ccd21:right
+	amp44	STR	ccd22:left
+	amp45	STR	ccd22:right
+	amp46	STR	ccd23:left
+	amp47	STR	ccd23:right
+	amp48	STR	ccd24:left
+	amp49	STR	ccd24:right
+	amp50	STR	ccd25:left
+	amp51	STR	ccd25:right
+	amp52	STR	ccd26:left
+	amp53	STR	ccd26:right
+	amp54	STR	ccd27:left
+	amp55	STR	ccd27:right
+	amp56	STR	ccd28:left
+	amp57	STR	ccd28:right
+	amp58	STR	ccd29:left
+	amp59	STR	ccd29:right
+	amp60	STR	ccd30:left
+	amp61	STR	ccd30:right
+	amp62	STR	ccd31:left
+	amp63	STR	ccd31:right
+	amp64	STR	ccd32:left
+	amp65	STR	ccd32:right
+	amp66	STR	ccd33:left
+	amp67	STR	ccd33:right
+	amp68	STR	ccd34:left
+	amp69	STR	ccd34:right
+	amp70	STR	ccd35:left
+	amp71	STR	ccd35:right
+END
+
+# Specify the cell data
+CELLS	METADATA
+	left	METADATA	# Left amplifier
+		CELL.BIASSEC	STR	HEADER:BIASSEC
+		CELL.TRIMSEC	STR	HEADER:DATASEC
+		CELL.XPARITY	S32	1	# We could have specified this as a DEFAULT, but this works
+	END
+	right	METADATA	# Right amplifier
+		CELL.BIASSEC	STR	HEADER:BIASSEC
+		CELL.TRIMSEC	STR	HEADER:DATASEC
+		CELL.XPARITY	S32	-1	# This cell is read out in the opposite direction
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.NAME	STR	EXPNUM
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+	FPA.MJD		STR	MJD-OBS
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.XBIN	STR	CCDBIN1
+	CELL.YBIN	STR	CCDBIN2
+	CELL.GAIN	STR	GAIN
+	CELL.READNOISE	STR	RDNOISE
+	CELL.SATURATION	STR	SATURATE
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	CELL.READDIR		S32	1		# Cell is read in x direction
+	CELL.BAD		S32	0
+#	CELL.YPARITY	S32	1
+# This doesn't work, yet.
+	CELL.YPARITY_DEPEND	STR	CHIP.NAME
+	CELL.YPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+
+END
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/megacam_splice.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/megacam_splice.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/megacam_splice.config	(revision 22322)
@@ -0,0 +1,107 @@
+# The spliced MecaCam data is stored in single extensions for each chip
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	MegaCam
+	EXTEND		BOOL	T
+	NEXTEND		S32	36
+END
+
+# How to read this data
+PHU		STR	FPA	# The FITS file represents an entire FPA
+EXTENSIONS	STR	CHIP	# The extensions represent chips
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, components
+	ccd00		STR	left right
+	ccd01		STR	left right
+	ccd02		STR	left right
+	ccd03		STR	left right
+	ccd04		STR	left right
+	ccd05		STR	left right
+	ccd06		STR	left right
+	ccd07		STR	left right
+	ccd08		STR	left right
+	ccd09		STR	left right
+	ccd10		STR	left right
+	ccd11		STR	left right
+	ccd12		STR	left right
+	ccd13		STR	left right
+	ccd14		STR	left right
+	ccd15		STR	left right
+	ccd16		STR	left right
+	ccd17		STR	left right
+	ccd18		STR	left right
+	ccd19		STR	left right
+	ccd20		STR	left right
+	ccd21		STR	left right
+	ccd22		STR	left right
+	ccd23		STR	left right
+	ccd24		STR	left right
+	ccd25		STR	left right
+	ccd26		STR	left right
+	ccd27		STR	left right
+	ccd28		STR	left right
+	ccd29		STR	left right
+	ccd30		STR	left right
+	ccd31		STR	left right
+	ccd32		STR	left right
+	ccd33		STR	left right
+	ccd34		STR	left right
+	ccd35		STR	left right
+END
+
+# Specify the cells
+CELLS		METADATA
+	left		METADATA
+		CELL.BIASSEC	STR	HEADER:BSECA
+		CELL.TRIMSEC	STR	HEADER:TSECA
+	END
+
+	right		METADATA
+		CELL.BIASSEC	STR	HEADER:BSECB
+		CELL.TRIMSEC	STR	HEADER:TSECB
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+        FPA.NAME        STR     EXPNUM
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTER      STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+        FPA.MJD         STR     MJD-OBS
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+        CELL.GAIN       STR     GAIN
+        CELL.READNOISE  STR     RDNOISE
+        CELL.SATURATION STR     SATURATE
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.BAD                S32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+END
+
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+
+END		
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/modeltest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/modeltest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/modeltest.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include <pslib.h>
+# include <pmObjects.h>
+
+/* pars: x, sx, sky, I, */
+psF64 fgaussOD (psVector *dpar, const psVector *par, const psVector *x) {
+
+    double X, S, Z, R, f;
+
+    X = x->data.F32[0] - par->data.F32[0];
+    S = 1.0 / (par->data.F32[1]*par->data.F32[1]);
+    Z = -0.5*X*X*S;
+    R = exp (Z);
+    f = par->data.F32[2]*R + par->data.F32[3];
+
+    if (dpar != NULL) {
+	dpar->data.F32[0] = par->data.F32[2]*R*X*S;
+	dpar->data.F32[1] = dpar->data.F32[0]*X/par->data.F32[1];
+	dpar->data.F32[2] = R;
+	dpar->data.F32[3] = 1;
+    }
+    return (f);
+}
+
+/* pars: x, sx, */
+psF64 fgaussODs (psVector *dpar, const psVector *par, const psVector *x) {
+
+    double X, S, Z, R, f;
+
+    X = x->data.F32[0] - par->data.F32[0];
+    S = 1.0 / (par->data.F32[1]*par->data.F32[1]);
+    Z = -0.5*X*X*S;
+    f = exp (Z);
+
+    if (dpar != NULL) {
+	dpar->data.F32[0] = f*X*S;
+	dpar->data.F32[1] = dpar->data.F32[0]*X/par->data.F32[1];
+    }
+    return (f);
+}
+
+// test psMinimizeLMChi2
+int main_test (int argc, char **argv) {
+
+    int i;
+    bool rc;
+    psF32 dx;
+    psArray  *x;
+    psVector *y;
+    psVector *v;
+    psVector *p;
+
+    psTraceSetLevel (".", 3);
+
+    psMinimization *myMin = psMinimizationAlloc (20, 0.001); // (iterations, tolerance)
+
+    // create data vector
+    x = psArrayAlloc (100);
+    y = psVectorAlloc (100, PS_TYPE_F32);
+    for (i = 0; i < x[0].n; i++) {
+	v = psVectorAlloc (1, PS_TYPE_F32);
+	v[0].data.F32[0] = i;
+	x[0].data[i] = (psPtr) v;
+	dx = i - 30;
+	y[0].data.F32[i] = exp (-(dx*dx)/(2*3*3));
+	// xo = 30.0, sigma = 3.0
+    }
+    p = psVectorAlloc (2, PS_TYPE_F32);
+    p[0].data.F32[0] = 25.0;
+    p[0].data.F32[1] = 5.0;
+
+    rc = psMinimizeLMChi2(myMin, NULL, p, NULL, x, y, NULL, fgaussODs);
+  
+    for (i = 0; i < p[0].n; i++) {
+	fprintf (stderr, "par %d: %f\n", i, p[0].data.F32[i]);
+    }
+
+    exit (0);
+}
+
+// test psMinimizeLMChi2
+int main (int argc, char **argv) {
+
+    int i;
+    bool rc;
+    psF32 dx;
+    psArray  *x;
+    psVector *y;
+    psVector *v;
+    psVector *p;
+    psVector *mask;
+
+    psTraceArguments (&argc, argv);
+
+    psMinimization *myMin = psMinimizationAlloc (20, 0.1); // (iterations, tolerance)
+
+    // create data vector
+    x = psArrayAlloc (100);
+    y = psVectorAlloc (100, PS_TYPE_F32);
+    for (i = 0; i < x[0].n; i++) {
+	v = psVectorAlloc (1, PS_TYPE_F32);
+	v[0].data.F32[0] = i;
+	x[0].data[i] = (psPtr) v;
+	dx = i - 30;
+	y[0].data.F32[i] = 5.0*exp (-(dx*dx)/(2*3*3)) + 4.0;
+	// sigma = 3.0, xo = 30.0, A = 5.0, B = 4.0
+    }
+    p = psVectorAlloc (4, PS_TYPE_F32);
+    p[0].data.F32[0] = 25.0;
+    p[0].data.F32[1] = 5.0;
+    p[0].data.F32[2] = 8.0;
+    p[0].data.F32[3] = 2.0;
+
+    mask = psVectorAlloc (4, PS_TYPE_U8);
+    mask->data.U8[0] = 0;
+    mask->data.U8[1] = 0;
+    mask->data.U8[2] = 0;
+    mask->data.U8[3] = 0;
+
+    rc = psMinimizeLMChi2(myMin, NULL, p, mask, x, y, NULL, fgaussOD);
+  
+    for (i = 0; i < p[0].n; i++) {
+	fprintf (stderr, "par %d: %f\n", i, p[0].data.F32[i]);
+    }
+
+    exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/papFocalPlane.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/papFocalPlane.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/papFocalPlane.c	(revision 22322)
@@ -0,0 +1,526 @@
+#include <stdio.h>
+#include <string.h>
+#include "pslib.h"
+#include "papFocalPlane.h"
+#include "psAdditionals.h"
+#include "pmFPARead.h"
+
+// I'm sure this code, especially the DB lookup, leaks memory something chronic....
+
+
+// Look for a value already cached
+static psMetadataItem *getValueFromCache(const papFPA *fpa, // The FPA that contains the chip
+					 const papChip *chip, // The chip that contains the cell
+					 const papCell *cell, // The cell
+					 const char *valueName // Name of value
+    )
+{
+    // Search the cell
+    psMetadataItem *item = psMetadataLookup(cell->values, valueName); // Item from the lookup
+    if (item) {
+	return item;
+    }
+    // Search the chip
+    item = psMetadataLookup(chip->values, valueName);
+    if (item) {
+	return item;
+    }
+    // Search the FPA
+    item = psMetadataLookup(fpa->values, valueName);
+    return item;			// item is either NULL or points to what was desired
+}
+
+
+// Put a value that we've found into the cache
+static void setValueInCache(papFPA *fpa, // The FPA that contains the chip
+			    papChip *chip, // The chip that contains the cell
+			    papCell *cell, // The cell
+			    const char *valueName, // Name of value
+			    const psMetadataItem *value	// Item containing the value
+    )
+{
+    // We need to create a new psMetadataItem.  I don't think we can't simply hack the existing one, since
+    // that could conceivably cause memory leaks
+    psMetadataItem *newItem = psMetadataItemAlloc(valueName, value->type, value->comment, value->data.V);
+
+    if (fpa && strncmp(valueName, "FPA", 3) == 0) {
+	psMetadataAddItem(fpa->values, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+    }
+    if (chip && strncmp(valueName, "CHIP", 4) == 0) {
+	psMetadataAddItem(chip->values, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+    }
+    if (cell && strncmp(valueName, "CELL", 4) == 0) {
+	psMetadataAddItem(cell->values, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+    }
+
+}
+
+
+static psMetadataItem *getValueFromHeader(papFPA *fpa, // The FPA that contains the chip
+					  papChip *chip, // The chip that contains the cell
+					  papCell *cell, // The cell
+					  const char *valueName // Name of value
+    )
+{
+    bool mdStatus = true;		// Status of MD lookup
+    psMetadata *translation = psMetadataLookupMD(&mdStatus, fpa->camera, "TRANSLATION"); // FITS translation
+    if (! mdStatus) {
+	psError(PS_ERR_IO, false, "Unable to find TRANSLATION in camera configuration.\n");
+	return NULL;
+    }
+
+    // Look for how to translate the concept into a FITS header name
+    const char *header = psMetadataLookupString(&mdStatus, translation, valueName);
+    if (mdStatus && strlen(header) > 0) {
+	// We have a FITS header to look up --- search each level
+	if (cell->header) {
+	    psMetadataItem *cellItem = psMetadataLookup(cell->header, header);
+	    if (cellItem) {
+		// XXX: Need to clean up before returning
+		setValueInCache(NULL, NULL, cell, valueName, cellItem);
+		return cellItem;
+	    }
+	}
+
+	if (chip->header) {
+	    psMetadataItem *chipItem = psMetadataLookup(chip->header, header);
+	    if (chipItem) {
+		// XXX: Need to clean up before returning
+		setValueInCache(NULL, chip, cell, valueName, chipItem);
+		return chipItem;
+	    }
+	}
+
+	if (fpa->header) {
+	    psMetadataItem *fpaItem = psMetadataLookup(fpa->header, header);
+	    if (fpaItem) {
+		// XXX: Need to clean up before returning
+		setValueInCache(fpa, chip, cell, valueName, fpaItem);
+		return fpaItem;
+	    }
+	}
+    }
+
+    // No header value
+    return NULL;
+}
+
+
+// Look for a default
+static psMetadataItem *getValueFromDefault(papFPA *fpa, // The FPA that contains the chip
+					   papChip *chip, // The chip that contains the cell
+					   papCell *cell, // The cell
+					   const char *valueName // Name of value
+    )
+{
+    bool mdOK = true;			// Status of MD lookup
+    psMetadata *defaults = psMetadataLookupMD(&mdOK, fpa->camera, "DEFAULTS");
+    if (! mdOK) {
+	psError(PS_ERR_IO, false, "Unable to find DEFAULTS in camera configuration.\n");
+	return NULL;
+    }
+
+    psMetadataItem *defItem = psMetadataLookup(defaults, valueName);
+    if (defItem) {
+	if (defItem->type == PS_META_META) {
+	    // A dependent default
+	    psTrace(__func__, 7, "Evaluating dependent default....\n");
+	    psMetadata *dependents = defItem->data.V; // The list of dependents
+	    // Find out what it depends on
+	    psString dependName = psStringCopy(valueName);
+	    psStringAppend(&dependName, "_DEPEND");
+	    psString dependsOn = psMetadataLookupString(&mdOK, defaults, dependName);
+	    if (! mdOK) {
+		psError(PS_ERR_IO, false, "Unable to find %s in camera configuration for dependent default"
+			" --- ignored\n", dependName);
+		// XXX: Need to clean up before returning
+		return NULL;
+	    }
+	    psFree(dependName);
+	    // Find the value of the dependent concept
+	    psMetadataItem *depItem = getValueFromCache(fpa, chip, cell, dependsOn);
+	    if (! depItem) {
+		depItem = getValueFromHeader(fpa, chip, cell, dependsOn);
+	    }
+	    if (! depItem) {
+		psError(PS_ERR_IO, true, "Unable to find value for %s (required for %s)\n", dependsOn,
+			valueName);
+		return NULL;
+	    }
+	    if (depItem->type != PS_META_STR) {
+		psError(PS_ERR_IO, true, "Value of %s is not of type string, as required for dependency"
+			" --- ignored.\n", dependsOn);
+	    }
+
+	    defItem = psMetadataLookup(dependents, depItem->data.V);	// This is now what we were after
+	}
+	setValueInCache(fpa, chip, cell, valueName, defItem);
+    }
+
+    // XXX: Need to clean up before returning
+    return defItem;			// defItem is either NULL or points to what was desired
+}
+
+
+// Look for a database lookup
+static psMetadataItem *getValueFromDB(papFPA *fpa, // The FPA that contains the chip
+				      papChip *chip, // The chip that contains the cell
+				      papCell *cell, // The cell
+				      const char *valueName // Name of value
+    )
+{
+    if (! fpa->db) {
+	// No database initialised
+	return NULL;
+    }
+
+    bool mdStatus = true;		// Status of MD lookup
+    psMetadata *database = psMetadataLookupMD(&mdStatus, fpa->camera, "DATABASE");
+    if (! mdStatus) {
+	// No error, because not everyone needs to use the DB
+	return NULL;
+    }
+
+    psMetadata *dbLookup = psMetadataLookupMD(&mdStatus, database, valueName);
+    if (dbLookup) {
+	const char *tableName = psMetadataLookupString(&mdStatus, dbLookup, "TABLE"); // Name of the table
+	const char *colName = psMetadataLookupString(&mdStatus, dbLookup, "COLUMN"); // Name of the column
+	const char *givenCols = psMetadataLookupString(&mdStatus, dbLookup, "GIVENDBCOL");	// Name of "where" columns
+	const char *givenPS = psMetadataLookupString(&mdStatus, dbLookup, "GIVENPS"); // Values for "where" columns
+	
+	// Now, need to get the "given"s
+	if (strlen(givenCols) || strlen(givenPS)) {
+	    psList *cols = papSplit(givenCols, ",;"); // List of column names
+	    psList *values = papSplit(givenPS, ",;"); // List of value names for the columns
+	    psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
+	    if (cols->size != values->size) {
+		psLogMsg(__func__, PS_LOG_WARN, "The GIVENDBCOL and GIVENPS entries for %s do not have "
+			 "the same number of entries --- ignored.\n", valueName);
+	    } else {
+		// Iterators for the lists
+		psListIterator *colsIter = psListIteratorAlloc(cols, PS_LIST_HEAD, false);
+		psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
+		char *column = NULL;	// Name of the column
+		while (column = psListGetAndIncrement(colsIter)) {
+		    const char *name = psListGetAndIncrement(valuesIter); // Name for the value
+		    if (!strlen(column) || !strlen(name)) {
+			psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
+				 " empty --- ignored.\n", valueName);
+		    } else {
+			// Search for the value name
+			psMetadataItem *item = getValueFromCache(fpa, chip, cell, name);
+			if (! item) {
+			    item = getValueFromHeader(fpa, chip, cell, name);
+			}
+			if (! item) {
+			    item = getValueFromDefault(fpa, chip, cell, name);
+			}
+			if (! item) {
+			    psLogMsg(__func__, PS_LOG_ERROR, "Unable to find the value name %s for DB "
+				     " lookup on %s --- ignored.\n", name, valueName);
+			} else {
+			    // We need to create a new psMetadataItem.  I don't think we can't simply hack
+			    // the existing one, since that could conceivably cause memory leaks
+			    psMetadataItem *newItem = psMetadataItemAlloc(valueName, item->type,
+									  item->comment, item->data.V);
+			    psMetadataAddItem(selection, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+			    psFree(newItem);
+			}
+		    }
+		} // Iterating through the columns
+		
+		psArray *dbResult = psDBSelectRows(fpa->db, tableName, selection, 2); // Lookup result
+		// Note that we use limit=2 in order to test if there are multiple rows returned
+		
+		psMetadataItem *result = NULL; // The final result of the DB lookup
+		if (dbResult->n == 0) {
+		    psLogMsg(__func__, PS_LOG_WARN, "Unable to find any rows in DB for %s --- ignored\n", 
+			     valueName);
+		} else {
+		    if (dbResult-> n > 1) {
+			psLogMsg(__func__, PS_LOG_WARN, "Multiple rows returned in DB lookup for %s --- "
+				 " using the first one only.\n", valueName);
+		    }
+		    result = (psMetadataItem*)dbResult->data[0];
+		}
+		// XXX: Need to clean up before returning
+		return result;
+	    }
+	}
+    } // Doing the "given"s.
+
+    // Shouldn't get here
+    return NULL;
+}
+
+psMetadataItem *psCellGetValue(papCell *cell, // The cell
+			       const char *valueName // Name of value
+			       )
+{
+    psTrace(__func__, 3, "Trying to retrieve %s...\n", valueName);
+
+    papChip *chip = cell->parent;	// The chip
+    papFPA *fpa = chip->parent;		// The FPA
+
+    // Try cache, headers, database, defaults in order
+    psTrace(__func__, 5, "Trying caches....\n");
+    psMetadataItem *item = getValueFromCache(fpa, chip, cell, valueName);
+    if (! item) {
+	psTrace(__func__, 5, "Trying headers....\n");
+	item = getValueFromHeader(fpa, chip, cell, valueName);
+    }
+    if (! item) {
+	psTrace(__func__, 5, "Trying database....\n");
+	item = getValueFromDB(fpa, chip, cell, valueName);
+    }
+    if (! item) {
+	psTrace(__func__, 5, "Trying defaults....\n");
+	item = getValueFromDefault(fpa, chip, cell, valueName);
+    }
+
+    return item;			// item is either NULL or points to what was desired
+}
+
+float psCellGetValueF32(papCell *cell, // The cell
+			const char *valueName // Name of value
+			)
+{
+    float value = NAN;			// Result
+    psMetadataItem *item = psCellGetValue(cell, valueName); // The item
+    if (! item) {
+	psError(PS_ERR_IO, true, "Unable to find value %s in cell.\n", valueName);
+    } else if (item->type != PS_META_F32) {
+	psError(PS_ERR_IO, true, "Metadata item type for %s is not F32.\n", valueName);
+    } else {
+	value = item->data.F32;
+    }
+
+    return value;
+}
+
+int psCellGetValueS32(papCell *cell, // The cell
+		      const char *valueName // Name of value
+    )
+{
+    int value = 0;			// Result
+    psMetadataItem *item = psCellGetValue(cell, valueName); // The item
+    if (! item) {
+	psError(PS_ERR_IO, true, "Unable to find value %s.\n", valueName);
+    } else if (item->type != PS_META_S32) {
+	psError(PS_ERR_IO, true, "Metadata item type for %s is not S32.\n", valueName);
+    } else {
+	value = item->data.S32;
+    }
+
+    return value;
+}
+
+
+double psCellGetValueF64(papCell *cell, // The cell
+			 const char *valueName // Name of value
+			 )
+{
+    double value = NAN;			// Result
+    psMetadataItem *item = psCellGetValue(cell, valueName); // The item
+    if (! item) {
+	psError(PS_ERR_IO, true, "Unable to find value %s.\n", valueName);
+    } else if (item->type != PS_META_F64) {
+	psError(PS_ERR_IO, true, "Metadata item type for %s is not F64.\n", valueName);
+    } else {
+	value = item->data.F64;
+    }
+
+    return value;
+}
+
+
+psString psCellGetValueString(papCell *cell, // The cell
+			      const char *valueName // Name of value
+    )
+{
+    psString value = NULL;		// Result
+    psMetadataItem *item = psCellGetValue(cell, valueName); // The item
+    if (! item) {
+	psError(PS_ERR_IO, true, "Unable to find value %s.\n", valueName);
+    } else if (item->type != PS_META_STR) {
+	psError(PS_ERR_IO, true, "Metadata item type for %s is not STR.\n", valueName);
+    } else {
+	value = item->data.V;
+    }
+
+    return value;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Allocators
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+papFPA *papFPAAlloc(const psMetadata *camera, // Camera configuration
+		    psDB *db		// Database
+    )
+{
+    papFPA *fpa = psAlloc(sizeof(papFPA));// The FPA
+    psMemSetDeallocator(fpa, (psFreeFcn)p_papFPAFree);
+
+    // Fill in the components
+    fpa->fromTangentPlane = NULL;
+    fpa->toTangentPlane = NULL;
+    fpa->projection = NULL;
+
+    fpa->values = psMetadataAlloc();
+    fpa->camera = psMemIncrRefCounter((psPtr)camera);
+    fpa->db = db;
+    fpa->chips = psArrayAlloc(0);
+
+    fpa->extname = NULL;
+    fpa->pixels = NULL;
+    fpa->header = NULL;
+
+    return fpa;
+}
+
+void p_papFPAFree(papFPA *fpa)
+{
+    psFree(fpa->fromTangentPlane);
+    psFree(fpa->toTangentPlane);
+    psFree(fpa->projection);
+
+    psFree(fpa->values);
+    psFree((psPtr)fpa->camera);
+    psFree(fpa->db);
+    psFree(fpa->chips);
+
+    psFree(fpa->pixels);
+    psFree(fpa->header);
+}
+
+papChip *papChipAlloc(papFPA *fpa,	// FPA to which the chip belongs
+		      psString name	// Chip name
+    )
+{
+    papChip *chip = psAlloc(sizeof(papChip)); // The chip
+    psMemSetDeallocator(chip, (psFreeFcn)p_papChipFree);
+
+    // Push onto the array of chips
+    fpa->chips = psArrayAdd(fpa->chips, 0, chip);
+
+    // Fill in the components
+    *(int*)&chip->col0 = 0;		// Good enough for now
+    *(int*)&chip->row0 = 0;		// Good enough for now
+
+    chip->toFPA = NULL;
+    chip->fromFPA = NULL;
+
+    chip->values = psMetadataAlloc();
+    chip->parent = psMemIncrRefCounter(fpa);
+    chip->cells = psArrayAlloc(0);
+
+    chip->extname = NULL;
+    chip->pixels = NULL;
+    chip->header = NULL;
+
+    psMetadataAddStr(chip->values, PS_LIST_HEAD, "CHIP.NAME", "Chip name added at papChipAlloc", name);
+
+    return chip;
+}
+
+void p_papChipFree(papChip *chip)
+{
+    psFree(chip->toFPA);
+    psFree(chip->fromFPA);
+
+    psFree(chip->values);
+    psFree(chip->cells);
+    psFree(chip->parent);
+
+    psFree(chip->pixels);
+    psFree(chip->header);
+}
+
+papCell *papCellAlloc(papChip *chip,	// Chip to which the cell belongs
+		      psString name	// Name of cell
+    )
+{
+    papCell *cell = psAlloc(sizeof(papCell)); // The cell
+    psMemSetDeallocator(cell, (psFreeFcn)p_papCellFree);
+
+    // Push onto the array of chips
+    chip->cells = psArrayAdd(chip->cells, 0, cell);
+
+    // Fill in components
+    *(int*)&cell->col0 = 0;		// Good enough for now
+    *(int*)&cell->row0 = 0;		// Good enough for now
+
+    cell->toChip = NULL;
+    cell->fromChip = NULL;
+    cell->toFPA = NULL;
+    cell->toTP = NULL;
+    cell->toSky = NULL;
+
+    cell->values = psMetadataAlloc();
+    cell->readouts = psArrayAlloc(0);
+    cell->parent = psMemIncrRefCounter(chip);
+
+    cell->extname = NULL;
+    cell->pixels = NULL;
+    cell->header = NULL;
+
+    psMetadataAddStr(cell->values, PS_LIST_HEAD, "CELL.NAME", "Cell name added at papCellAlloc", name);
+
+    return cell;
+}
+
+void p_papCellFree(papCell *cell)
+{
+    psFree(cell->toChip);
+    psFree(cell->fromChip);
+    psFree(cell->toFPA);
+    psFree(cell->toTP);
+    psFree(cell->toSky);
+
+    psFree(cell->values);
+    psFree(cell->readouts);
+    psFree(cell->parent);
+
+    psFree(cell->pixels);
+    psFree(cell->header);
+}
+
+papReadout *papReadoutAlloc(papCell *cell, // Cell to which the readout belongs
+			    int readoutNum, // Number of the readout
+			    psImage *image, // The pixels
+			    psList *overscans, // The overscan images
+			    int col0, int row0, int colParity, int rowParity, int colBin, int rowBin // Data
+    )
+{
+    papReadout *readout = psAlloc(sizeof(papReadout));
+    psMemSetDeallocator(readout, (psFreeFcn)p_papReadoutFree);
+    psArray *readouts = cell->readouts;
+    if (readoutNum >= readouts->nalloc) {
+	readouts = psArrayRealloc(readouts, readoutNum);
+	cell->readouts = readouts;
+    }
+    readouts->data[readoutNum] = readout;
+    
+    // Set the components
+    readout->image = image;
+    readout->overscans = overscans;
+    
+    readout->values = psMetadataAlloc();
+    
+    *(int*)&readout->col0 = col0;
+    *(int*)&readout->row0 = row0;
+    *(int*)&readout->colParity = colParity;
+    *(int*)&readout->rowParity = rowParity;
+    *(int*)&readout->colBins = colBin;
+    *(int*)&readout->rowBins = rowBin;
+
+    return readout;
+}
+
+void p_papReadoutFree(papReadout *readout)
+{
+    psFree(readout->image);
+    psFree(readout->overscans);
+    psFree(readout->values);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/papFocalPlane.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/papFocalPlane.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/papFocalPlane.h	(revision 22322)
@@ -0,0 +1,124 @@
+#ifndef PAP_FOCAL_PLANE_H
+#define PAP_FOCAL_PLANE_H
+
+#include "pslib.h"
+#include "papStuff.h"
+
+// Temporary metadata types
+#define PS_META_CHIP PS_META_UNKNOWN
+#define PS_META_CELL PS_META_UNKNOWN
+
+typedef struct {
+    // Astrometric transformations
+    psPlaneDistort* fromTangentPlane;	// Transformation from tangent plane to focal plane
+    psPlaneDistort* toTangentPlane;	// Transformation from focal plane to tangent plane
+    psProjection *projection;		// Projection from tangent plane to sky
+    // Information
+    psMetadata *values;			// Important values (cached)
+    const psMetadata *camera;		// Camera configuration
+    psDB *db;				// Database
+    psArray *chips;			// The chips
+    // FITS data
+    const char *extname;		// Extension name, if it corresponds to this level
+    psArray *pixels;			// The pixel data, if it corresponds to this level
+    psMetadata *header;			// The FITS header, if it corresponds to this level
+} papFPA;
+
+typedef struct {
+    // Offset specifying position on focal plane
+    const int col0;			// Offset from the left of FPA
+    const int row0;			// Offset from the bottom of FPA
+    // Astrometric transformations
+    psPlaneTransform* toFPA;		// Transformation from chip to FPA coordinates
+    psPlaneTransform* fromFPA;		// Transformation from FPA to chip coordinates
+    // Information
+    psMetadata *values;			// Important values (cached)
+    psArray *cells;			// The cells (referred to by name)
+    papFPA *parent;			// Parent FPA
+    // FITS data
+    const char *extname;		// Extension name, if it corresponds to this level
+    psArray *pixels;			// The pixel data, if it corresponds to this level
+    psMetadata *header;			// The FITS header, if it corresponds to this level
+} papChip;
+
+typedef struct {
+    // Offset specifying position on chip
+    const int col0;			// Offset from the left of chip
+    const int row0;			// Offset from the bottom of chip
+    // Astrometric transformations
+    psPlaneTransform* toChip;		// Transformations from cell to chip coordinates
+    psPlaneTransform* fromChip;		// Transformations from cell to chip coordinates
+    psPlaneTransform* toFPA;		// Transformations from cell to FPA coordinates
+    psPlaneTransform* toTP;		// Transformations from cell to FPA coordinates
+    psPlaneTransform* toSky;		// Transformations from cell to tangent plane coordinates
+    // Information
+    psMetadata *values;			// Important values (cached)
+    psArray *readouts;			// The readouts (referred to by number)
+    papChip *parent;			// Parent chip
+    // FITS data
+    const char *extname;		// Extension name, if it corresponds to this level
+    psArray *pixels;			// The pixel data, if it corresponds to this level
+    psMetadata *header;			// The FITS header, if it corresponds to this level
+} papCell;
+
+
+typedef struct {
+    // Details for position on the cell
+    const int col0;			// Offset from the left of cell.
+    const int row0;			// Offset from the bottom of cell.
+    const int colParity;		// Readout Direction X
+    const int rowParity;		// Readout Direction Y
+    const unsigned int colBins;		// Amount of binning in x-dimension
+    const unsigned int rowBins;		// Amount of binning in y-dimension
+    // Information
+    psImage *image;			// The pixels
+    psList *overscans;			// Array of subimages containing the overscan region(s)
+    psMetadata *values;			// readout-level metadata
+} papReadout;
+
+
+// Look for a particular value for a given cell (referred to by FPA+chip+cell)
+psMetadataItem *psCellGetValue(papCell *cell, // The cell
+			       const char *valueName // Name of value
+    );
+
+// Type-specific functions provided as a convenience to the user
+float psCellGetValueF32(papCell *cell, // The cell
+			const char *valueName // Name of value
+			);
+int psCellGetValueS32(papCell *cell, // The cell
+		      const char *valueName // Name of value
+		      );
+double psCellGetValueF64(papCell *cell, // The cell
+			 const char *valueName // Name of value
+			 );
+psString psCellGetValueString(papCell *cell, // The cell
+			      const char *valueName // Name of value
+			      );
+
+
+// Allocators and deallocators
+papFPA *papFPAAlloc(const psMetadata *camera, // Camera configuration
+		    psDB *db		// Database
+    );
+void p_papFPAFree(papFPA *fpa);
+
+papChip *papChipAlloc(papFPA *fpa,	// FPA to which the chip belongs
+		      psString name	// Name of chip
+    );
+void p_papChipFree(papChip *chip);
+
+papCell *papCellAlloc(papChip *chip,	// Chip to which the cell belongs
+		      psString name	// Name of cell
+    );
+void p_papCellFree(papCell *cell);
+
+papReadout *papReadoutAlloc(papCell *cell,	// Cell to which the readout belongs
+			    int readoutNum, // Number of the readout
+			    psImage *image, // The pixels
+			    psList *overscans, // The overscan images
+			    int col0, int row0, int colParity, int rowParity, int colBin, int rowBin // Data
+    );
+void p_papReadoutFree(papReadout *readout);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/papPhase2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/papPhase2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/papPhase2.c	(revision 22322)
@@ -0,0 +1,599 @@
+#include <stdio.h>
+#include "pslib.h"
+#include "papmodule.h"
+
+// Phase 2 needs to:
+// * Read configurations
+// * Read in the images
+// * Flag bad pixels
+// * Non-linearity correction
+// * Bias/dark subtraction (MHPCC)
+// * Flat-field (MHPCC)
+// * Fringe subtraction (Not for now)
+// * Source identification and photometry (IPP prototype)
+// * Astrometry (IPP prototype soon)
+// * Write calibrated image
+// * Write source catalogue, astrometry, etc.
+ 
+// Currently neglecting different input types (F32, S32, U16, etc)
+
+// Would like to fold in Nebulous at some stage as well.
+
+// Currently neglect to convolve the reference frames with the orthogonal transfer kernel
+
+// phase2 INPUT.fits OUTPUT.fits -bias BIAS.fits -dark DARK.fits -flat FLAT.fits -chip CHIP
+// 
+// Most are self-explanatory.  "-chip" says "only work on this particular chip".
+
+void help(psMetadata *arguments,	// Command-line arguments
+	  const char *programName	// Name of the program = argv[0]
+    )
+{
+    printf("Pan-STARRS Phase 2 processing\n\n");
+    printf("Usage: %s INPUT.fits OUTPUT.fits\n\n", programName);
+    psArgumentHelp(arguments);
+}
+
+
+
+int main(int argc, char *argv[])
+{
+    // Parse the configurations
+    psMetadata *site = NULL;            // Site configuration
+    psMetadata *camera = NULL;          // Camera configuration
+    psMetadata *recipe = NULL;          // Recipe configuration
+    if (! pmConfigRead(&site, &camera, &recipe, &argc, argv, "moduleName")) {
+        psErrorStackPrint("Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // Parse other command-line arguments
+    psMetadata *arguments = psMetadataAlloc(); // The arguments, with default values
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "bias", "Name of the bias image", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "dark", "Name of the dark image", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "flat", "Name of the flat-field image", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "mask", "Name of the mask image", NULL);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "chip", "Chip number to process (if positive)", -1);
+    if (! psArgumentParse(arguments, &argc, argv) || argc != 2) {
+	help(arguments, argv[0]);
+	exit(EXIT_FAILURE);
+    }
+    const char *inputName = argv[1];	// Name of input image
+    const char *outputName = argv[2];	// Name of output image
+    const char *biasName = psMetadataLookupString(NULL, arguments, "bias"); // Name of bias image
+    const char *darkName = psMetadataLookupString(NULL, arguments, "dark"); // Name of dark image
+    const char *flatName = psMetadataLookupString(NULL, arguments, "flat"); // Name of flat-field image
+    const char *maskName = psMetadataLookupString(NULL, arguments, "mask"); // Name of mask image
+    const int chipNum = psMetadataLookupS32(NULL, arguments, "chip"); // Chip number to work on
+
+    // Open the input
+    psFits *inputFile = psFitsOpen(inputName, "r"); // File handle for FITS file
+    if (! inputFile) {
+	psErrorStackPrint("Can't open input image: %s\n", inputName);
+        exit(EXIT_FAILURE);
+    }
+    psMetadata *header = psFitsReadHeader(NULL, inputFile); // FITS header
+    psDB *database = pmConfigDB(site);	// Database handle
+
+    // Open the output and output mask
+    // We do it here so that we don't process the whole lot and then find out we can't open the output file
+    psFits *outputFile = psFitsOpen(outputName, "w");
+    if (! outputFile) {
+	psErrorStackPrint("Can't open output image: %s\n", outputName);
+	exit(EXIT_FAILURE);
+    }
+    psString outputMaskName = psStringCopy(outputName);
+    (void)psStringAppend(outputMaskName, ".mask");
+    psFits *outputMaskFile = psFits(outputMaskName, "w");
+    if (! outputMaskFile) {
+	psErrorStackPrint("Can't open output mask image: %s\n", outputMaskName);
+	exit(EXIT_FAILURE);
+    }
+    psFree(outputMaskName);
+	
+    // Get camera configuration from header if not already defined
+    if (! camera) {
+	camera = pmConfigCameraFromHeader(site, header);
+	if (! camera) {
+	    psErrorStackPrint("Can't find camera configuration!\n");
+	    exit(EXIT_FAILURE);
+	}
+    } else if (! pmConfigValidateCamera(camera, inputHeader)) {
+	psError("phase2", true, "%s does not seem to be from the camera.\n", inputName);
+	exit(EXIT_FAILURE);
+    }
+    if (! recipe && !(recipe = pmConfigRecipeFromCamera(camera, "moduleName"))) {
+        psErrorStackPrint("Can't find recipe configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // Construct camera in preparation for reading
+    pmFPA *input = pmFPAConstruct(camera, db);
+    pmFPA *mask = pmFPAConstruct(camera, db);
+    pmFPA *bias = pmFPAConstruct(camera, db);
+    pmFPA *dark = pmFPAConstruct(camera, db);
+    pmFPA *flat = pmFPAConstruct(camera, db);
+
+    // Set various tasks
+    bool doMask = false;		// Mask bad pixles
+    bool doNonLin = false;		// Non-linearity correction
+    bool doBias = false;		// Bias subtraction
+    bool doDark = false;		// Dark subtraction
+    bool doOverscan = false;		// Overscan subtraction
+    bool doFlat = false;		// Flat-field normalisation
+    bool doFringe = false;		// Fringe subtraction
+    bool doSource = false;		// Source identification and photometry
+    bool doAstrom = false;		// Astrometry
+    pmOverscanAxis overscanMode = PM_OVERSCAN_NONE; // Axis for overscan
+    pmFit overscanFit = PM_FIT_NONE;	// Fit type for overscan
+    int overscanBins = 1;		// Number of pixels per bin for overscan
+    psStats *overscanStats = NULL;	// Statistics for overscan
+
+    if (psMetadataLookupBool(NULL, recipe, "MASK")) {
+	if (maskName) {
+	    doMask = true;
+	} else {
+	    psLogMsg("phase2", PS_LOG_WARN, "Masking is desired, but no mask was supplied --- no masking "
+		     "will be performed.\n");
+	}
+    }
+    if (psMetadataLookupBool(NULL, recipe, "NONLIN")) {
+	doNonLin = true;
+    }
+    if (psMetadataLookupBool(NULL, recipe, "BIAS")) {
+	if (biasName) {
+	    doBias = true;
+	} else {
+	    psLogMsg("phase2", PS_LOG_WARN, "Bias subtraction is desired, but no bias was supplied --- "
+		     "no bias subtraction will be performed.\n");
+	}
+    }
+    if (psMetadataLookupBool(NULL, recipe, "DARK")) {
+	if (darkName) {
+	    doDark = true;
+	} else {
+	    psLogMsg("phase2", PS_LOG_WARN, "Dark subtraction is desired, but no dark was supplied --- "
+		     "no dark subtraction will be performed.\n");
+	}
+    }
+    if (psMetadataLookupBool(NULL, recipe, "OVERSCAN")) {
+	doOverscan = true;
+	psString mode = psMetadataLookupString(NULL, recipe, "OVERSCAN.MODE");
+	if (strcasecmp(mode, "INDIVIDUAL") == 0) {
+	    overscanMode = PM_OVERSCAN_ROWS;
+	    // By "ROWS", I mean either rows or columns --- will check the READDIR for each chip
+	} else if (strcasecmp(mode, "ALL") == 0) {
+	    overscanMode = PM_OVERSCAN_ALL;
+	} else if (strcasecmp(mode, "NONE") != 0) {
+	    psLogMsg("phase2", PS_LOG_WARN, "OVERSCAN.MODE (%s) is not one of NONE, INDIVIDUAL, or ALL:"
+		     " assuming NONE.\n", mode);
+	}
+	psFree(mode);
+	psString fit = psMetadataLookupString(NULL, recipe, "OVERSCAN.FIT");
+	if (strcasecmp(fit, "POLYNOMIAL") == 0) {
+	    overscanFit = PM_FIT_POLYNOMIAL;
+	} else if (strcasecmp(fit, "SPLINE") == 0) {
+	    overscanFit = PM_FIT_SPLINE;
+	} else if (strcasecmp(fit, "NONE") != 0) {
+	    psLogMsg("phase2", PS_LOG_WARN, "OVERSCAN.FIT (%s) is not one of NONE, POLYNOMIAL, or SPLINE:"
+		     " assuming NONE.\n", fit);
+	}
+	psFree(fit);
+	overscanBins = psMetadataLookupS32(NULL, recipe, "OVERSCAN.BIN");
+	if (overscanBins <= 0) {
+	    psErrorStackPrint("OVERSCAN.BIN (%d) is non-positive --- assuming 1.\n", overscanBins);
+	    overscanBins = 1;
+	}
+	psString stat = psMetadataLookupString(NULL, recipe, "OVERSCAN.STAT");
+	if (strcasecmp(stat, "MEAN")) {
+	    overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+	} else if (strcasecmp(stat, "MEDIAN")) {
+	    overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+	} else {
+	    psErrorStackPrint("OVERSCAN.STAT (%s) is not one of MEAN, MEDIAN: assuming MEAN\n", stat);
+	    overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);	    
+	}
+    }
+    if (psMetadataLookupBool(NULL, recipe, "FLAT")) {
+	if (flatName) {
+	    doFlat = true;
+	} else {
+	    psLogMsg("phase2", PS_LOG_WARN, "Flat-fielding is desired, but no flat was supplied --- "
+		     "no flat-fielding will be performed.\n");
+	}
+    }
+	 
+    // Chip selection
+    if (chipNum >= 0) {
+	if (! pmFPASelectChip(input, chipNum) || ! pmFPASelectChip(bias, chipNum) ||
+	    ! pmFPASelectChip(dark, chipNum) || ! pmFPASelectChip(flat, chipNum) ||
+	    ! pmFPASelectChip(mask, chipNum)) {
+	    psErrorStackPrint("Chip number %d doesn't exist in camera.\n", chipNum);
+	    exit(EXIT_FAILURE);
+	}
+	psLogMsg("phase2", PS_LOG_INFO, "Operating only on chip %d\n", chipNum);
+    }
+
+    // Read in the input pixels
+    if (! pmFPARead(input, inputFile)) {
+	psErrorStackPrint("Unable to populate camera from FITS file: %s\n", inputName);
+	exit(EXIT_FAILURE);
+    }
+    psFitsClose(inputFile);
+
+    // Load the calibration frames, if required
+    if (biasName) {
+	psFits *biasFile = psFitsOpen(biasName, "r"); // File handle for bias
+	psMetadata *biasHeader = psFitsReadHeader(NULL, biasFile); // FITS header for bias
+	if (! pmConfigValidateCamera(camera, biasHeader)) {
+	    psError("phase2", true, "Bias (%s) does not seem to be from the same camera.\n", biasName);
+	    exit(EXIT_FAILURE);
+	}
+	psFree(biasHeader);
+	if (! pmFPARead(bias, biasFile)) {
+	    psErrorStackPrint("Unable to populate bias camera from fits FITS: %s\n", biasName);
+	    exit(EXIT_FAILURE);
+	}
+	psFitsClose(biasFile);
+    }
+
+    if (darkName) {
+	psFits *darkFile = psFitsOpen(darkName, "r"); // File handle for dark
+	psMetadata *darkHeader = psFitsReadHeader(NULL, darkFile); // FITS header for dark
+	if (! pmConfigValidateCamera(camera, darkHeader)) {
+	    psError("phase2", true, "Dark (%s) does not seem to be from the same camera.\n", darkName);
+	    exit(EXIT_FAILURE);
+	}
+	psFree(darkHeader);
+	if (! pmFPARead(dark, darkFile)) {
+	    psErrorStackPrint("Unable to populate dark camera from fits FITS: %s\n", darkName);
+	    exit(EXIT_FAILURE);
+	}
+	psFitsClose(darkFile);
+    }
+
+    if (maskName) {
+	psFits *maskFile = psFitsOpen(maskName, "r"); // File handle for mask
+	psMetadata *maskHeader = psFitsReadHeader(NULL, maskFile); // FITS header for mask
+	if (! pmConfigValidateCamera(camera, maskHeader)) {
+	    psError("phase2", true, "Mask (%s) does not seem to be from the same camera.\n", maskName);
+	    exit(EXIT_FAILURE);
+	}
+	psFree(maskHeader);
+	if (! pmFPARead(mask, maskFile)) {
+	    psErrorStackPrint("Unable to populate mask camera from fits FITS: %s\n", maskName);
+	    exit(EXIT_FAILURE);
+	}
+	psFitsClose(maskFile);
+    }
+    
+    if (flatName) {
+	psFits *flatFile = psFitsOpen(flatName, "r"); // File handle for flat
+	psMetadata *flatHeader = psFitsReadHeader(NULL, flatFile); // FITS header for flat
+	if (! pmConfigValidateCamera(camera, flatHeader)) {
+	    psError("phase2", true, "Flat (%s) does not seem to be from the same camera.\n", flatName);
+	    exit(EXIT_FAILURE);
+	}
+	psFree(flatHeader);
+	if (! pmFPARead(flat, flatFile)) {
+	    psErrorStackPrint("Unable to populate flat camera from fits FITS: %s\n", flatName);
+	    exit(EXIT_FAILURE);
+	}
+	psFitsClose(flatFile);
+    }
+    
+    psArray *inputChips = inputs->chips; // Array of chips in input image
+    psArray *biasChips = bias->chips;	// Array of chips in bias image
+    psArray *darkChips = dark->chips;	// Array of chips in dark image
+    psArray *maskChips = mask->chips;	// Array of chips in mask image
+    psArray *flatChips = flat->chips;	// Array of chips in flat image
+    for (int i = 0; i < inputChips->n; i++) {
+	pmChip *inputChip = inputChips->data[i]; // Chip of interest in input image
+	pmChip *biasChip = biasChips->data[i]; // Chip of interest in bias image
+	pmChip *darkChip = darkChips->data[i]; // Chip of interest in dark image
+	pmChip *maskChip = maskChips->data[i]; // Chip of interest in mask image
+	pmChip *flatChip = flatChips->data[i]; // Chip of interest in flat image
+
+	if (! inputChip->valid) {
+	    continue;
+	}
+
+	psArray *inputCells = inputChip->cells; // Array of cells in input image
+	psArray *biasCells = biasChip->cells; // Array of cells in bias image
+	psArray *darkCells = darkChip->cells; // Array of cells in dark image
+	psArray *maskCells = maskChip->cells; // Array of cells in mask image
+	psArray *flatCells = flatChip->cells; // Array of cells in flat image
+
+	for (int j = 0; j < inputCells->n; j++) {
+	    pmCell *inputCell = inputCells->data[i]; // Cell of interest in input image
+	    pmCell *biasCell = biasCells->data[i]; // Cell of interest in bias image
+	    pmCell *darkCell = darkCells->data[i]; // Cell of interest in dark imag
+	    pmCell *maskCell = maskCells->data[i]; // Cell of interest in mask image
+	    pmCell *flatCell = flatCells->data[i]; // Cell of interest in flat image
+
+	    if (! inputCell->valid) {
+		continue;
+	    }
+
+	    psArray *inputReadouts = inputCell->readouts; // Array of readouts in input image
+	    if (biasCell->readouts->n != 1) {
+		psLogMsg("phase2", PS_LOG_WARN, "Bias contains multiple readouts: only the first will be"
+			 " used.");
+	    }
+	    if (darkCell->readouts->n != 1) {
+		psLogMsg("phase2", PS_LOG_WARN, "Dark contains multiple readouts: only the first will be"
+			 " used.");
+	    }
+	    if (maskCell->readouts->n != 1) {
+		psLogMsg("phase2", PS_LOG_WARN, "Mask contains multiple readouts: only the first will be"
+			 " used.");
+	    }
+	    if (flatCell->readouts->n != 1) {
+		psLogMsg("phase2", PS_LOG_WARN, "Flat contains multiple readouts: only the first will be"
+			 " used.");
+	    }
+
+	    psImage *biasImage = NULL;	// Bias pixels
+	    psImage *darkImage = NULL;	// Dark pixels
+	    float darkTime = 1.0;	// Dark time for dark image
+
+	    if (biasName) {
+		pmReadout *biasReadout = biasCell->readouts->data[0]; // Readout of interest in bias image
+		biasImage = biasReadout->image;
+	    }
+	    if (darkName) {
+		pmReadout *darkReadout = darkCell->readouts->data[0]; // Readout of interest in dark image
+		darkImage = darkReadout->image;
+		darkTime = pmReadoutGetDarkTime(darkReadout);
+		if (darkTime <= 0.0) {
+		    psErrorStackPrint("DARKTIME for dark image (%f) is non-positive.\n", darkTime);
+		    exit(EXIT_FAILURE);
+		}
+	    }
+	    if (maskName) {
+		pmReadout *maskReadout = maskCell->readouts->data[0]; // Readout of interest in mask image
+	    }
+	    if (flatName) {
+		pmReadout *flatReadout = flatCell->readouts->data[0]; // Readout of interest in mask image
+	    }
+
+	    for (int k = 0; k < inputReadouts->n; k++) {
+		pmReadout *inputReadout = inputReadouts->data[k]; // Readout of interest in input image
+		psImage *inputImage = inputReadout->image; // The actual image
+		psList *inputBias = inputReadout->bias; // List of overscan bias regions
+
+		// Mask bad pixels
+		if (doMask) {
+		    // Need to change this later to grow the mask by the size of the convolution kernel
+		    (void)pmMaskBadPixels(inputReadout, maskReadout,
+					  PS_MASK_TRAP | PS_MASK_BADCOL | PS_MASK_SAT, saturation,
+					  PS_MASK_TRAP, 0);
+		}
+
+		// Non-linearity correction
+		if (doNonLin) {
+		    psMetadataItem *dataItem = psMetadataLookup(recipe, "NONLIN.DATA");
+		    if (! dataItem) {
+			psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but unable to "
+				 "find NONLIN.DATA in recipe --- ignored.\n");
+		    } else {
+			switch (dataItem->type) {
+			  case PS_META_VECTOR:
+			    // These are the polynomial coefficients
+			    psVector *coeff = dataItem->data.V; // The coefficient vector
+			    if (coeff->type.type != PS_TYPE_F64) {
+				psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64); // F64 version
+				psFree(coeff);
+				coeff = temp;
+			    }
+			    psPolynomial1D *correction = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD,
+									     coeff->n + 1);
+			    for (int i = 0; i < coeff->n; i++) {
+				correction->coeff[i] = coeff->data.F64[i];
+			    }
+			    (void)pmNonLinearityPolynomial(inputReadout, correction);
+			    psFree(coeffCopy);
+			    psFree(coeff);
+			    psFree(correction);
+			    break;
+			  case PS_META_STR:
+			    // This is a filename
+			    psString name = dataItem->data.V;	// Filename
+			    psLookupTable *table = psLookupTableAlloc(name, "%f %f", 0);
+			    if (psLookupTableRead(table) <= 0) {
+				psErrorStackPrint("Unable to read non-linearity correction file %s --- "
+						  "ignored\n", tableName);
+			    } else {
+				(void)pmNonLinearityLookup(inputReadout, table);
+			    }
+			    psFree(table);
+			    break;
+			  case PS_META_META:
+			    // This is a menu
+			    psMetadata *options = dataItem->data.V; // Options with concept values as keys
+			    bool mdok = false; // Success of MD lookup
+			    psString concept = psMetadataLookupStr(&mdok, recipe, "NONLIN.SOURCE");
+			    if (! mdok || ! concept) {
+				psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but "
+					 "unable to find NONLIN.SOURCE in recipe --- ignored.\n");
+			    } else {
+				psMetadataItem *conceptValueItem = pmCellGetConcept(inputCell, concept);
+				if (! conceptValueItem) {
+				    psLogMsg("phase2", PS_LOG_WARN, "Unable to find value of concept %s "
+					     "for non-linearity correction --- ignored.\n", concept);
+				} else if (conceptValueItem->type != PS_META_STR) {
+				    psLogMsg("phase2", PS_LOG_WARN, "Type for concept %s isn't STRING, as"
+					     " expected for non-linearity correction --- ignored.\n",
+					     concept);
+				} else {
+				    psString conceptValue = conceptValueItem->data.V;
+				    // Get the value of the concept
+				    psMetadataItem *optionItem = psMetadataLookup(options, conceptValue);
+				    if (!optionItem) {
+					psLogMsg("phase2", PS_LOG_WARN, "Unable to find %s in NONLIN.DATA"
+						 " --- ignored.\n", conceptValue);
+				    } else if (optionItem->type == PS_META_VECTOR) {
+					// These are the polynomial coefficients
+					psVector *coeff = optionItem->data.V; // The coefficient vector
+					if (coeff->type.type != PS_TYPE_F64) {
+					    psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64);
+					    psFree(coeff);
+					    coeff = temp;
+					}
+					// Polynomial correction
+					psPolynomial1D *correction = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD,
+											 coeff->n + 1);
+					for (int i = 0; i < coeff->n; i++) {
+					    correction->coeff[i] = coeff->data.F64[i];
+					}
+					(void)pmNonLinearityPolynomial(inputReadout, correction);
+					psFree(coeffCopy);
+					psFree(coeff);
+					psFree(correction);
+				    } else if (optionItem->type == PS_META_STR) {
+					// This is a filename
+					psString tableName = optionItem->data.V; // The filename
+					psLookupTable *table = psLookupTableAlloc(tableName, "%f %f", 0);
+					if ((int numLines = psLookupTableRead(table)) <= 0) {
+					    psErrorStackPrint("Unable to read non-linearity correction "
+							      "file %s --- ignored\n", tableName);
+					} else {
+					    (void)pmNonLinearityLookup(inputReadout, table);
+					}
+					psFree(table);
+				    } else {
+					psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction "
+						 "desired but unable to interpret NONLIN.DATA for %s"
+						 " --- ignored\n", conceptValue);
+				    }
+				}
+			    }
+			    break;
+			  default:
+			    psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but "
+				     "NONLIN.DATA is of invalid type --- ignored.\n");
+			}
+		    }
+		} // Non-linearity correction
+
+		// Bias, dark and overscan subtraction are all merged.
+
+		// Changed the definition of pmSubtractBias to do the dark subtraction as well, but
+		// it's not in there yet.  Once it is, we can get rid of "pedestal".
+
+		psImage *pedestal = NULL;	// Pedestal image (bias + scaled dark)
+		if (doDark) {
+		    float inputTime = pmReadoutGetDarkTime(inputReadout); // Dark time for input image
+		    if (inputTime <= 0) {
+			psErrorStackPrint("DARKTIME for input image (%f) is non-positive.\n", inputTime);
+			exit(EXIT_FAILURE);
+		    }
+
+		    pedestal = psBinaryOp(NULL, darkImage, "*",
+					  psScalarAlloc(inputTime * darkTime, PS_TYPE_F32));
+		}
+		if (doBias) {
+		    if (pedestal) {
+			if (biasImage->numRows != darkImage->numRows ||
+			    biasImage->numCols != darkImage->numCols) {
+			    psError("phase2", true, "Bias and dark images have different dimensions: "
+				    "%dx%d vs %dx%d\n", biasImage->numCols, biasImage->numRows,
+				    darkImage->numCols, darkImage->numRows);
+			    exit(EXIT_FAILURE);
+			}
+			(void)psBinaryOp(pedestal, pedestal, "+", biasImage);
+		    } else {
+			pedestal = psMemIncrRefCounter(biasImage);
+		    }
+		}
+
+		if (overscanMode == PM_OVERSCAN_ROWS || overscanMode == PM_OVERSCAN_COLUMNS) {
+		    // Need to get the read direction
+		    int readdir = pmCellGetReaddir(inputCell); // Read direction
+		    if (readdir == 1) {
+			overscanMode = PM_OVERSCAN_ROWS;
+		    } else if (readdir == 2) {
+			overscanMode = PM_OVERSCAN_COLUMNS;
+		    } else {
+			psErrorStackPrint("CELL.READDIR (%d) is not 1 or 2 --- assuming 1.\n", readdir);
+			overscanMode = PM_OVERSCAN_ROWS;
+		    }
+		}
+
+		void *overscanResult = NULL; // Result of overscan fit
+		(void)pmSubtractBias(inputImage, overscanResult, inputBias, overscanMode, overscanStat,
+				     overscanBin, overscanFit, pedestal);
+
+		// Output overscan fit results, if required
+		if (doOverscan) {
+		    switch (overscanFit) {
+		      case PM_FIT_POLYNOMIAL:
+			psPolynomial1D *poly = (psPolynomial1D*)overscanResult;	// The polynomial
+			psString coeffs = NULL;	// String containing the coefficients
+			for (int i = 0; i < poly->n; i++) {
+			    psStringAppend(&coeffs, "%.2f ", poly->coeffs[i]);
+			}
+			psLogMsg("phase2", PS_LOG_INFO, "Overscan polynomial coefficients:\n%s\n",
+				 coeffs);
+			psFree(coeffs);
+			break;
+		      case PM_FIT_SPLINE:
+			psSpline1D *spline = (psSpline1D*)overscanResult; // The spline
+			psString coeffs = NULL;	// String containing the coefficients
+			for (int i = 0; i < spline->n; i++) {
+			    psPolynomial1D *poly = spline->spline[i]; // i-th polynomial
+			    psStringAppend(&coeffs, "%d: ", i);
+			    for (int j = 0; j < poly->n; j++) {
+				psStringAppend(&coeffs, "%.2f ", poly->coeffs[i]);
+			    }
+			    psStringAppend(&coeffs, "\n");
+			}
+			psLogMsg("phase2", PS_LOG_INFO, "Overscan spline coefficients:\n%s\n", coeffs);
+			psFree(coeffs);
+			break;
+		      case PM_FIT_NONE:
+			break;
+		      default:
+			psAbort("Should never get here!!!\n");
+		    }
+		}
+
+		// Flat-field correction
+		if (doFlat) {
+		    (void)pmFlatField(inputReadout, maskReadout, flatReadout);
+		}
+
+		// Fringe subtraction
+		if (doFringe) {
+#if 0
+		    // Placeholder
+		    pmReadout *pmSubtractSky(pmReadout *in, psPolynomial2D *poly, psImage *mask,
+					     psU8 maskVal, int binFactor, psStats *stats, float clipSD);
+#endif
+		}
+
+		// Source identification and photometry
+		if (doSource) {
+		}
+
+		// Astrometry
+		if (doAstrom) {
+		}
+
+	    } // Iterating over readouts
+	} // Iterating over cells
+    } // Iterating over chips
+
+    // Write the output
+    pmFPAWrite(outputFile, input);
+    pmFPAWriteMask(outputMaskFile, input);
+    psFitsClose(outputFile);
+    psFitsClose(outputMaskFile);
+
+    psFree(input);
+    psFree(mask);
+    psFree(bias);
+    psFree(dark);
+    psFree(flat);
+
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/papStuff.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/papStuff.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/papStuff.c	(revision 22322)
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <string.h>
+#include "pslib.h"
+#include "papStuff.h"
+
+// Split string on given characters
+psList *papSplit(const char *string, const char *splitters)
+{
+    psList *values = psListAlloc(NULL);	// The list of values to return
+    unsigned int length = strlen(string); // The length of the string
+    unsigned int numSplitters = strlen(splitters); // Number of characters that might split
+    unsigned int start = 0;		// The position of the start of a word
+    for (int i = 1; i < length; i++) {
+	bool split = false;		// Is this character a splitter?
+	for (int j = 0; j < numSplitters && ! split; j++) {
+	    if (string[i] == splitters[j]) {
+		split = true;
+	    }
+	}
+	if (split) {
+	    if (i == start) {
+		// Some idiot put in two spaces, or two commas or something
+		start++;
+	    } else {
+		// We're at the end of the word
+		psString word = psStringNCopy(&string[start], i - start);
+		(void)psListAdd(values, PS_LIST_TAIL, word);
+		start = i + 1;
+	    }
+	}
+    }
+    if (start < length) {
+	// Copy the last word
+	psString word = psStringNCopy(&string[start], length - start);
+	(void)psListAdd(values, PS_LIST_TAIL, word);
+    }
+
+    return values;
+}
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/papStuff.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/papStuff.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/papStuff.h	(revision 22322)
@@ -0,0 +1,12 @@
+#ifndef PAP_STUFF_H
+#define PAP_STUFF_H
+
+#include "pslib.h"
+
+// psString is a psLib version of a char*
+typedef char* psString;
+
+// Split string on given characters
+psList *papSplit(const char *string, const char *splitters);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/papmodule.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/papmodule.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/papmodule.h	(revision 22322)
@@ -0,0 +1,10 @@
+// Load various include files
+
+#include "pmCameraFromHeader.h"
+#include "pmFPAConstruct.h"
+#include "pmFPARead.h"
+#include "papStuff.h"
+#include "psAdditionals.h"
+#include "papFocalPlane.h"
+#include "pmFPAMorph.h"
+#include "pmFPAWrite.h"
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2-alt.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2-alt.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2-alt.c	(revision 22322)
@@ -0,0 +1,307 @@
+# include <pslib.h>
+
+main (int argc, char **argv) {
+
+    psArray *headers;
+    psMetadata *base, *site, *camera, *recipe;
+
+    pmConfigLoadBasic (&site, &base);
+
+    fits = psFitsAlloc (input);
+    headers = psFitsReadHeaderSet (fits);
+
+    pmConfigLoadCamera (&camera, site, headers->data[0]);
+    pmConfigLoadRecipe (&recipe, camera, "PHASE2");
+
+    // check the validity of the given set of headers based on expectactions for this camera
+    pmCameraValidateHeaders (headers, camera);
+
+    // construct a complete, empty (ie, no pixel data) FPA structure for this camera / header set
+    FPA = pmFPAfromHeader (headers, camera);
+  
+    /* ideally, the sequence of cells will be the same as the sequence of extensions */
+    for (i = 0; i < FPA[0].chips.n; i++) {
+	chip = FPA[0].chips.data[i];
+	for (j = 0; j < chip[0].cells.n; j++) {
+	    cell = chip[0].cells.data[j];
+
+	    cellMD  = psMetadataLookupPtr (cell->metadata, "CELL");
+	    Nextend = psMetadataLookupS32 (cellMD, "EXT.NUMBER");
+	    f = psFitsMoveExtNum(f, extNum, FALSE); // this should not move if we are already there
+
+	    for (k = 0; k < cell[0].readouts.n; k++) {
+		readout = cell[0].readouts[k];
+
+		// load the specific readout pixels.  put this in a function?  
+		cell[0].readouts[k] = pmReadoutLoad (cell[0].readouts[k], fits, cell, k);
+		bias_subtract_readout (readout, NULL, recipe);
+		
+	    }
+	}
+    }	
+}
+
+// bias subtract the given readout according to the recipe (bias image may be NULL)
+// no mask needed yet
+bias_subtract_readout (psReadout *readout, psImage *bias, psMetadata *recipe) {
+  
+    char *biassec;
+    psImage *overscan;
+    psList *overscanlist;
+    pmOverscanAxis direction;
+    psRectangle biassec;
+
+    // determine overscan option
+    biassec = pmConfigLookupRegion (recipe, readout[0].metadata, "BIAS.OVERSCAN"); 
+    overscan = psImageSubsection (readout[0].image, biassec);
+
+    psListAdd (overscanlist, overscan, PS_LIST_HEAD);             // list of overscans (allocate list first)
+
+    // the direction is implied by noting the long dimension of the biassec
+    direction = DirectionFromOverscan (biassec);  // weak, but do we want a function to deal with this or add it inline?
+
+    // the type of statistic is defined by the recipe: BIAS.OVERSCAN.STATS
+    statname = psMetadataLookup (recipe, "BIAS.OVERSCAN.STATS");  // string defining overscan stats
+    stats = DefineStats (statname);
+
+    // identify the type of fit requested and relevant parameters
+    // the type of fit is defined by the recipe: BIAS.OVERSCAN.STATS
+    fitname = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT");  // string defining overscan stats
+
+    if (!strcmp (fitname, "NONE")) {
+	fitType = PM_FIT_NONE;
+	fitSpec = NULL;
+	nBin = 0;
+    }
+
+    if (!strcmp (fitname, "SPLINE")) {
+	fitType = PM_FIT_SPLINE;
+	nBin = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NBIN");
+	nPts = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NPTS");
+	order = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.ORDER");  // only 1 or 3 is allowed for 'order'
+	fitSpec = psSpline1DAlloc (nPts, 3, 0, Npix);                  // Npix is either NAXIS1 or NAXIS2 (Nx or Ny);
+    }
+
+    if (!strcmp (fitname, "POLYNOMIAL")) {
+	fitType = PM_FIT_POLYNOMIAL;
+	nBin = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NBIN");
+	nPts = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NPTS");
+	order = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.ORDER");
+	fitSpec = psPolynomial1DAlloc (nPts, PS_POLYNOMIAL_CHEB);
+    }
+
+    readout[0].image = pmSubtractBias (readout[0].image, fitSpec, overscanlist, direction, stats, nBin, PM_FIT_NONE, bias);
+
+}
+
+// subtract dark image using the appropriate scaling factor for this exposure time
+// no mask needed yet
+dark_subtract_readout (psReadout *readout, psImage *dark, psMetadata *recipe) {
+  
+    char *biassec;
+    psImage *overscan;
+    psList *overscanlist;
+    pmOverscanAxis direction;
+
+    if (dark == NULL) {
+	return;
+    }
+    
+    // need to get a lookup table from the database, apply exptime for readout and dark
+    factor_source = psMetadataLookup (recipe, "DARK.FACTOR"); // use the recipe to determine the dark scaling
+    factor = getScalingFactor (readout, dark, factor_source); 
+
+    // out = in - dark * factor
+    tmpdark = psBinaryOp (NULL, dark, "*", factor);
+    readout[0].image = psBinaryOp (readout[0].image, readout[0].image, "-", tmpdark);
+
+    psFree (tmpdark);
+}
+
+// this corrects the non-linear response, but probably should use data determined at the cell level
+// we need to set a mask value for pixels which have were out of range (TBD)
+nonlinearity_correct_readout (psReadout *readout, psReadout *mask, psMetadata *recipe) {
+  
+    char *method;
+    psPolynomial1D correction;
+    psVector inFlux, outFlux;
+
+    method = psMetadataLookup (recipe, "NONLINEAR.METHOD");
+
+    // use a polynomial; get coeffs from recipe
+    if (!strcmp (method, "POLYNOMIAL")) {
+	correction.n = psMetadataLookup (recipe, "NONLINEAR.ORDER"); 
+	correction.coeffs = psMetadataLookup (recipe, "NONLINEAR.COEFFS"); 
+	pmNonLinearityPolynomial (readout, correction);
+    }
+
+    if (!strcmp (method, "LOOKUP")) {
+	lookup_table = DetrendDatabaseLookup (time, cell, camera, etc?);
+	inFlux = psFITSTableRead (lookup_table, "IN_FLUX"); 
+	outFlux = psFITSTableRead (lookup_table, "OUT_FLUX"); 
+	pmNonLinearityLookup (readout, inFlux, outFlux);
+    }
+}
+
+// trim the image and mask based on the requested region
+trim_readout (psReadout *readout, psReadout *mask, psMetadata *recipe) {
+  
+    datasec = pmRecipeDefineSection (recipe, readout[0].metadata, "TRIM.DATASEC"); 
+
+    psImageTrim (readout[0].image, datasec);
+    psImageTrim (mask[0].image, datasec);
+
+}
+
+// apply the bad pixel mask to the image mask
+mask_readout (psReadout *readout, psImage *mask, psImage *badpix, psKernel *kernel, psMetadata *recipe) {
+  
+}
+
+psImage *load_bias_image (psMetadata *recipe, psMetadata *header, char *extname) {
+
+    // load in the complete corresponding bias image (this should be done once per cell)
+    // we are implying that the bias image is guaranteed to have the corresponding EXTNAME
+    bias_source = psMetadataLookup (recipe, "BIAS.IMAGE");
+    biasname = NULL;
+    if (!strncmp (bias_source, "FILE:", 5)) {
+	biasname = &bias_source[5];
+    }
+    if (!strcmp (bias_source, "DB:BEST")) {
+	biasname = DetrendDatabaseLookup ("best", header);   // these APIs need to be seriously fleshed out!!
+    }
+    if (!strcmp (bias_source, "DB:CLOSE")) {
+	biasname = DetrendDatabaseLookup ("close", header);   // these APIs need to be seriously fleshed out!!
+    }
+    if (biasname != NULL) {
+	bias     = psImageReadSection (NULL, 0, 0, 0, 0, 0, extname, 0, biasname);
+    }
+
+    return (bias);
+}
+
+psImage *load_dark_image (psMetadata *recipe, psMetadata *header, char *extname) {
+
+    // load in the complete corresponding bias image (this should be done once per cell)
+    // we are implying that the bias image is guaranteed to have the corresponding EXTNAME
+    dark_source = psMetadataLookup (recipe, "DARK.IMAGE");
+    darkname = NULL;
+    if (!strncmp (dark_source, "FILE:", 5)) {
+	darkname = &dark_source[5];
+    }
+    if (!strcmp (dark_source, "DB:BEST")) {
+	darkname = DetrendDatabaseLookup ("best", header);   // these APIs need to be seriously fleshed out!!
+    }
+    if (!strcmp (dark_source, "DB:CLOSE")) {
+	darkname = DetrendDatabaseLookup ("close", header);   // these APIs need to be seriously fleshed out!!
+    }
+    if (darkname != NULL) {
+	dark     = psImageReadSection (NULL, 0, 0, 0, 0, 0, extname, 0, darkname);
+    }
+
+    return (dark);
+}
+
+bool pmConfigLoadSite (psMetadata **site, int *argc, char **argv) {
+
+    int N;
+    char *sitefile;
+
+    if ((N = psArgsGet (argc, argv, "-site"))) {
+	if (N == argc - 1) {
+	    psError ("syntax error: -site (file)");
+	    return (FALSE);
+	}
+	psArgsRemove (&argc, argv, N);
+	sitefile = psStringCopy (argv[N]);
+	psArgsRemove (&argc, argv, N);
+    }
+
+    if (sitefile == NULL) {
+	sitefile = psStringCopy ("site.cnf");
+    }
+    *site = psMetadataParseConfig (NULL, sitefile, FALSE);  
+    return (TRUE);
+}
+
+bool pmConfigLoadCamera (psMetadata **camera, psMetadata *site, psMetadata *header) {
+  
+    int N;
+    char *cameraname = NULL;
+    char *camerafile = NULL;
+
+    if (site == NULL) return (FALSE);
+
+    // try to determine camera from command-line arguments 
+    if ((N = psArgsGet (argc, argv, "-camera"))) {
+	if (N == argc - 1) {
+	    psError ("syntax error: -camera (file)");
+	    return (FALSE);
+	}
+	psArgsRemove (&argc, argv, N);
+	cameraname = psStringCopy (argv[N]);
+	psArgsRemove (&argc, argv, N);
+    }
+
+    // try to determine camera from header 
+    if ((cameraname == NULL) && (header != NULL)) {
+	cameraname = pmCameraFromHeader (header);
+    }
+
+    // try to determine camera from general configuration information
+    if (cameraname == NULL) {
+	cameraname = psMetadataLookupPtr (site, "CAMERA");
+    }
+
+    // if there is no source for camera info, fail
+    if (cameraname == NULL) {
+	return (FALSE);
+    }
+
+    // find appropriate camera definition file
+    camerafile = psMetadataLookup (site, "CAMERA.%s", cameraname);
+
+    // load the camera details for the specific camera
+    *camera = psMetadataParseConfig (NULL, camerafile, FALSE);
+
+    return (TRUE);
+}
+
+bool pmConfigLoadRecipe (psMetadata **recipe, psMetadata *camera, int *argc, char **argv, char *script) {
+
+    int N;
+    char *recipename = NULL;
+    char *recipefile = NULL;
+
+    if (camera == NULL) return (FALSE);
+
+    // try to determine camera from command-line arguments 
+    if ((N = psArgsGet (argc, argv, "-recipe"))) {
+	if (N == argc - 1) {
+	    psError ("syntax error: -recipe (file)");
+	    return (FALSE);
+	}
+	psArgsRemove (&argc, argv, N);
+	recipefile = psStringCopy (argv[N]);
+	psArgsRemove (&argc, argv, N);
+    }
+
+    // recipefile is defined in the camera metadata, if not specified on command line
+    if (recipefile == NULL) {
+	recipename = psAlloc (sizeof(script) + 8);
+	sprintf (recipename, "RECIPE.%s", script);
+
+	// option 1: recipe is in the camera definition 
+	recipefile = psMetadataLookup (camera, recipename);
+	psFree (recipename);
+
+	// option 2: recipe is in the metadata database (equiv to a detrend file)
+	// this option requires a different API (header data, etc)
+	// recipefile = DetrendDatabaseLookup (recipename, site, camera, header);
+    }
+
+    *recipe = psMetadataParseConfig (NULL, recipefile, FALSE);  
+    psFree (recipefile);
+
+    return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2.c	(revision 22322)
@@ -0,0 +1,364 @@
+# include <pslib.h>
+
+// ignoring things like allocation, error tests, etc
+
+main (int argc, char **argv) {
+
+  psArray headers;
+  psMetadata *base, *site, *camera, *recipe;
+
+  // the set of code below (up to END CONFIG) is generic boilerplate which can be used for most analysis stages
+  // the specific issues are defined by the recipefile loaded for this analysis
+  // I've written this keeping the external metadata separated (base, site, camera, recipe),
+  // but we could put all of this in a single metdata collection, called eg config
+
+  // USAGE: phase2 (input) (outroot) [-camera (camera)] [-recipe (recipe)]
+  input = argv[1];
+  outroot = argv[2];
+
+  fits = psFitsAlloc (input);
+
+  // load only the headers from the given file
+  headers = psFitsReadHeaderSet (fits);
+ 
+  // identify camera (may be specified on command line)
+  if (cameraname == NULL) {
+    cameraname = pmCameraFromHeader (psListGet (headers->list, 0));
+  }
+  camerafile = psMetadataLookup (site, "CAMERA.%s", cameraname);
+
+  // load the camera details for the specific camera
+  camera = psMetadataParseConfig (NULL, camerafile, FALSE);
+
+  // recipefile is defined in the camera metadata, if not specified on command line
+  if (recipefile == NULL) {
+    recipefile = psMetadataLookup (camera, "RECIPE.PHASE2");
+    recipefile = DetrendDatabaseLookup ("recipe", camera, header);   // an alternative
+    // make this a database lookup & a function of date
+  }
+  recipe = psMetadataParseConfig (NULL, recipefile, FALSE);  
+
+  // END CONFIG loading code
+
+  // check the validity of the given set of headers based on expectactions for this camera
+  status = pmCameraValidateHeaders (headers, camera);
+
+  // construct a complete, empty (ie, no pixel data) FPA structure for this camera / header set
+  // we need to construct the output container.  is it big enough to fit in memory?  assume so?
+  FPA = pmFPAfromHeader (headers, camera);
+  
+  // I'm pretending the extname value for each cell in part of psCell
+  // Do we need to construct a list of the extname values associated with each readout?
+  // Is EXTNAME invarient?  (probably not; does not always exist)
+
+  /* This will work, but it is not efficient: 
+     we should work with the extensions in sequence */
+  for (i = 0; i < FPA[0].chips.n; i++) {
+    chip = FPA[0].chips.data[i];
+
+    if (Base == CELL) {
+      f = psFitsMoveExtName(f, extname);
+    }
+    
+    for (j = 0; j < chip[0].cells.n; j++) {
+      cell = chip[0].cells.data[j];
+
+      if (Base == CELL) {
+	f = psFitsMoveExtName(f, extname);
+      }
+
+      /* these need to work with the psFits pointer correctly 
+      bias = load_bias_image (recipe, cell[0].metadata, cell[0].extname);
+      dark = load_dark_image (recipe, cell[0].metadata, cell[0].extname);
+      flat = load_flat_image (recipe, cell[0].metadata, cell[0].extname);
+      badpix = load_badpix_image (recipe, cell[0].metadata, cell[0].extname);
+      fringe = load_fringe_image (recipe, cell[0].metadata, cell[0].extname);
+      */
+
+      for (k = 0; k < cell[0].readouts.n; k++) {
+	readout = cell[0].readouts[k];
+
+	// load the specific readout pixels.  put this in a function?  
+	status = pmReadoutLoad (fits, cell, k);
+
+	kernel = make_kernel ();
+	mask = make_mask ();
+
+	bias_subtract_readout (readout, bias, recipe);
+	dark_subtract_readout (readout, dark, recipe);
+	nonlinear_correct_readout (readout, mask, recipe);
+
+	trim_readout (readout, mask, recipe);
+	mask_readout (readout, mask, badpix, kernel, recipe);
+
+	// where do we convolve the flat with the kernel?
+	pmFlatField (readout, mask, flat);
+
+	defringe_readout (readout, mask, fringe, recipe);
+      }
+    }
+  }	
+}
+
+// bias subtract the given readout according to the recipe (bias image may be NULL)
+// no mask needed yet
+bias_subtract_readout (psReadout *readout, psImage *bias, psMetadata *recipe) {
+  
+  char *biassec;
+  psImage *overscan;
+  psList *overscanlist;
+  pmOverscanAxis direction;
+  psRectangle biassec;
+
+  // determine overscan option
+  biassec = pmRecipeDefineSection (recipe, readout[0].metadata, "BIAS.OVERSCAN"); 
+  overscan = psImageSubsection (readout[0].image, biassec);
+
+  psListAdd (overscanlist, overscan, PS_LIST_HEAD);             // list of overscans (allocate list first)
+
+  // the direction is implied by noting the long dimension of the biassec
+  direction = DirectionFromOverscan (biassec);  // weak, but do we want a function to deal with this or add it inline?
+
+  // the type of statistic is defined by the recipe: BIAS.OVERSCAN.STATS
+  statname = psMetadataLookup (recipe, "BIAS.OVERSCAN.STATS");  // string defining overscan stats
+  stats = DefineStats (statname);
+
+  // identify the type of fit requested and relevant parameters
+  // the type of fit is defined by the recipe: BIAS.OVERSCAN.STATS
+  fitname = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT");  // string defining overscan stats
+
+  if (!strcmp (fitname, "NONE")) {
+    fitType = PM_FIT_NONE;
+    fitSpec = NULL;
+    nBin = 0;
+  }
+
+  if (!strcmp (fitname, "SPLINE")) {
+    fitType = PM_FIT_SPLINE;
+    nBin = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NBIN");
+    nPts = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NPTS");
+    order = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.ORDER");  // only 1 or 3 is allowed for 'order'
+    fitSpec = psSpline1DAlloc (nPts, 3, 0, Npix);                  // Npix is either NAXIS1 or NAXIS2 (Nx or Ny);
+ }
+
+  if (!strcmp (fitname, "POLYNOMIAL")) {
+    fitType = PM_FIT_POLYNOMIAL;
+    nBin = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NBIN");
+    nPts = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NPTS");
+    order = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.ORDER");
+    fitSpec = psPolynomial1DAlloc (nPts, PS_POLYNOMIAL_CHEB);
+  }
+
+  readout[0].image = pmSubtractBias (readout[0].image, fitSpec, overscanlist, direction, stats, nBin, PM_FIT_NONE, bias);
+
+  // add these to the readout[0].metadata? 
+  // or to the chip / cell metadata?
+
+}
+
+// subtract dark image using the appropriate scaling factor for this exposure time
+// no mask needed yet
+dark_subtract_readout (psReadout *readout, psImage *dark, psMetadata *recipe) {
+  
+  char *biassec;
+  psImage *overscan;
+  psList *overscanlist;
+  pmOverscanAxis direction;
+
+  if (dark == NULL) {
+    return;
+  }
+    
+  // need to get a lookup table from the database, apply exptime for readout and dark
+  factor_source = psMetadataLookup (recipe, "DARK.FACTOR"); // use the recipe to determine the dark scaling
+  factor = getScalingFactor (readout, dark, factor_source); 
+
+  // out = in - dark * factor
+  tmpdark = psBinaryOp (NULL, dark, "*", factor);
+  readout[0].image = psBinaryOp (readout[0].image, readout[0].image, "-", tmpdark);
+
+  psFree (tmpdark);
+}
+
+// this corrects the non-linear response, but probably should use data determined at the cell level
+// we need to set a mask value for pixels which have were out of range (TBD)
+nonlinearity_correct_readout (psReadout *readout, psReadout *mask, psMetadata *recipe) {
+  
+  char *method;
+  psPolynomial1D correction;
+  psVector inFlux, outFlux;
+
+  method = psMetadataLookup (recipe, "NONLINEAR.METHOD");
+
+  // use a polynomial; get coeffs from recipe
+  if (!strcmp (method, "POLYNOMIAL")) {
+    correction.n = psMetadataLookup (recipe, "NONLINEAR.ORDER"); 
+    correction.coeffs = psMetadataLookup (recipe, "NONLINEAR.COEFFS"); 
+    pmNonLinearityPolynomial (readout, correction);
+  }
+
+  if (!strcmp (method, "LOOKUP")) {
+    lookup_table = DetrendDatabaseLookup (time, cell, camera, etc?);
+    inFlux = psFITSTableRead (lookup_table, "IN_FLUX"); 
+    outFlux = psFITSTableRead (lookup_table, "OUT_FLUX"); 
+    pmNonLinearityLookup (readout, inFlux, outFlux);
+  }
+}
+
+// trim the image and mask based on the requested region
+trim_readout (psReadout *readout, psReadout *mask, psMetadata *recipe) {
+  
+  datasec = pmRecipeDefineSection (recipe, readout[0].metadata, "TRIM.DATASEC"); 
+
+  psImageTrim (readout[0].image, datasec);
+  psImageTrim (mask[0].image, datasec);
+
+}
+
+// apply the bad pixel mask to the image mask
+mask_readout (psReadout *readout, psImage *mask, psImage *badpix, psKernel *kernel, psMetadata *recipe) {
+  
+}
+
+psImage *load_bias_image (psMetadata *recipe, psMetadata *header, char *extname) {
+
+  // load in the complete corresponding bias image (this should be done once per cell)
+  // we are implying that the bias image is guaranteed to have the corresponding EXTNAME
+  bias_source = psMetadataLookup (recipe, "BIAS.IMAGE");
+  biasname = NULL;
+  if (!strncmp (bias_source, "FILE:", 5)) {
+    biasname = &bias_source[5];
+  }
+  if (!strcmp (bias_source, "DB:BEST")) {
+    biasname = DetrendDatabaseLookup ("best", header);   // these APIs need to be seriously fleshed out!!
+  }
+  if (!strcmp (bias_source, "DB:CLOSE")) {
+    biasname = DetrendDatabaseLookup ("close", header);   // these APIs need to be seriously fleshed out!!
+  }
+  if (biasname != NULL) {
+    bias     = psImageReadSection (NULL, 0, 0, 0, 0, 0, extname, 0, biasname);
+  }
+
+  return (bias);
+}
+
+psImage *load_dark_image (psMetadata *recipe, psMetadata *header, char *extname) {
+
+  // load in the complete corresponding bias image (this should be done once per cell)
+  // we are implying that the bias image is guaranteed to have the corresponding EXTNAME
+  dark_source = psMetadataLookup (recipe, "DARK.IMAGE");
+  darkname = NULL;
+  if (!strncmp (dark_source, "FILE:", 5)) {
+    darkname = &dark_source[5];
+  }
+  if (!strcmp (dark_source, "DB:BEST")) {
+    darkname = DetrendDatabaseLookup ("best", header);   // these APIs need to be seriously fleshed out!!
+  }
+  if (!strcmp (dark_source, "DB:CLOSE")) {
+    darkname = DetrendDatabaseLookup ("close", header);   // these APIs need to be seriously fleshed out!!
+  }
+  if (darkname != NULL) {
+    dark     = psImageReadSection (NULL, 0, 0, 0, 0, 0, extname, 0, darkname);
+  }
+
+  return (dark);
+}
+
+psRectangle *pmRecipeDefineSection (psMetadata *recipe, psMetadata *header, char *name) {
+
+  char *source, *section;
+  psRectangle *Section;
+
+  source = psMetadataLookup (recipe, name);
+
+  if (!strcasecmp (source, "NONE")) {
+    Section = psRectangleAlloc (0, 0, 0, 0); // the NULL section
+    return (Section);
+  }
+
+  if (!strncasecmp (source, "HEADER:", 7)) {
+    char *keyword;
+    
+    keyword = &source[7]; 
+    section = psMetadataLookup (readout[0].metadata, keyword);
+    Section = pmSectiontoRectangle (section);
+    psFree (section);
+    psFree (source);
+    return (Section);
+  }
+
+  if (!strncasecmp (source, "RECIPE:", 7)) {
+    section = &source[7]; 
+    Section = pmSectiontoRectangle (section);
+    psFree (source);
+    return (Section);
+  }
+
+  psError ("invalid section");
+  return (NULL);
+
+}
+
+
+bool pmConfigLoadBasic (psMetadata **site, psMetadata **base) {
+
+  // base file needs to be hardwired, from environment, or based on program name
+  base   = psMetadataParseConfig (NULL, basefile, FALSE);  
+
+  // load site-specific information (such as database servers, etc)
+  sitefile = psMetadataLookup (base, "SITE");
+  site   = psMetadataParseConfig (NULL, sitefile, FALSE);  
+
+}
+
+bool pmConfigLoadCamera (psMetadata **camera, psMetadata *site, psMetadata *header) {
+  
+  if (site == NULL) return (FALSE);
+
+  // identify camera (may be specified on command line)
+  cameraname = getargs ("-camera");  // pseudo-code, not a valid API
+
+  // if there is no source for camera info, fail
+  if ((cameraname == NULL) && (header == NULL)) {
+    return (FALSE);
+  }
+
+  // try to determine camera from header 
+  if (cameraname == NULL) {
+    cameraname = pmCameraFromHeader (header);
+  }
+
+  // find appropriate camera definition file
+  camerafile = psMetadataLookup (site, "CAMERA.%s", cameraname);
+
+  // load the camera details for the specific camera
+  *camera = psMetadataParseConfig (NULL, camerafile, FALSE);
+
+  return (TRUE);
+}
+
+bool pmConfigLoadRecipe (psMetadata **recipe, psMetadata *camera, char *script) {
+
+  char recipename[64];
+
+  if (site   == NULL) return (FALSE);
+  if (camera == NULL) return (FALSE);
+
+  // define command-line options
+  recipefile = getargs ("-recipe");  // pseudo-code, not a valid API
+
+  // recipefile is defined in the camera metadata, if not specified on command line
+  if (recipefile == NULL) {
+    sprintf (recipename, "RECIPE.%s", script);
+
+    // option 1: recipe is in the camera definition 
+    recipefile = psMetadataLookup (camera, recipename);
+
+    // option 2: recipe is in the metadata database (equiv to a detrend file)
+    // this option requires a different API (header data, etc)
+    recipefile = DetrendDatabaseLookup (recipename, site, camera, header);
+  }
+
+  *recipe = psMetadataParseConfig (NULL, recipefile, FALSE);  
+  return (TRUE);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2.config	(revision 22322)
@@ -0,0 +1,27 @@
+### Phase 2 recipe configuration file
+
+# List of tasks to perform
+MASK		BOOL	TRUE		# Mask bad pixels
+NONLIN		BOOL	TRUE		# Non-linearity correction
+BIAS		BOOL	TRUE		# Bias subtraction
+DARK		BOOL	TRUE		# Dark subtraction
+OVERSCAN	BOOL	TRUE		# Overscan subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+SOURCE		BOOL	FALSE		# Source identification and photometry
+ASTROM		BOOL	FALSE		# Astrometry
+
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+# @NONLIN.SOURCE	STR	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		METADATA		# Source of non-linearity data
+	ccd00		STR	nonlin00.dat	# A lookup table 
+	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+END
+
+# Overscan subtraction
+OVERSCAN.MODE		STR	INDIVIDUAL	# NONE | INDIVIDUAL | ALL
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.BIN		S32	4		# Number of pixels per bin
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/.cvsignore	(revision 22322)
@@ -0,0 +1,16 @@
+Makefile.in
+aclocal.m4
+autom4te.cache
+configure
+Makefile
+config.log
+config.status
+compile
+config.guess
+config.sub
+depcomp
+install-sh
+ltmain.sh
+missing
+stamp-h1
+.deps
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/Makefile.am	(revision 22322)
@@ -0,0 +1,52 @@
+bin_PROGRAMS = phase2
+
+phase2_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(phase2_CFLAGS)
+phase2_LDADD = $(PSLIB_LIBS) $(PSMODULE_LIBS)
+phase2_SOURCES = \
+	papPhase2.c
+#	papStuff.c \
+#	pmChipMosaic.c \
+#	pmConfig.c \
+#	pmFPA.c \
+#	pmFPAConceptsGet.c \
+#	pmFPAConceptsSet.c \
+#	pmFPAConstruct.c \
+#	pmFPARead.c \
+#	pmFPAWrite.c \
+#	pmFlatField.c \
+#	pmMaskBadPixels.c \
+#	pmNonLinear.c \
+#	pmSubtractBias.c \
+#	psAdditionals.c
+
+noinst_HEADERS =
+#	papStuff.h \
+#	pmChipMosaic.h \
+#	pmConfig.h \
+#	pmFPA.h \
+#	pmFPAConceptsGet.h \
+#	pmFPAConceptsSet.h \
+#	pmFPAConstruct.h \
+#	pmFPARead.h \
+#	pmFPAWrite.h \
+#	pmFlatField.h \
+#	pmFlatFieldErrors.h \
+#	pmMaskBadPixels.h \
+#	pmMaskBadPixelsErrors.h \
+#	pmNonLinear.h \
+#	pmSubtractBias.h \
+#	psAdditionals.h
+
+EXTRA_DIST = \
+	autogen.sh \
+	gpc1_raw.config \
+	ipprc.config \
+	megacam_raw.config \
+	phase2.config \
+	nonlin.dat
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=phase2
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL=aclocal
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+#($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+#        DIE=1
+#}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+#($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+#        DIE=1
+#}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+#$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+#$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/configure.ac	(revision 22322)
@@ -0,0 +1,28 @@
+AC_PREREQ(2.61)
+
+AC_INIT([phase2], [0.0.2], [price@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([phase2.config])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+dnl AM_CONFIG_HEADER([config.h])
+AM_MAINTAINER_MODE
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+dnl AC_PROG_LIBTOOL
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 0.9.0]) 
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 0.0.0]) 
+
+CFLAGS="${CFLAGS} -DTESTING"
+dnl is this the best was to setup recursive CFLAGS?
+dnl phase2_CFLAGS="-Werror -std=c99 "
+phase2_CFLAGS=" "
+AC_SUBST([phase2_CFLAGS])
+
+AC_CONFIG_FILES([
+  Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/gpc1_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/gpc1_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/gpc1_raw.config	(revision 22322)
@@ -0,0 +1,172 @@
+# The raw GPC data comes off the telescope with each of the chips stored in separate files
+
+# How to identify this type
+RULE	METADATA
+#	TELESCOP	STR	PS1
+#	DETECTOR	STR	GPC1
+	EXTEND		BOOL	T
+	NEXTEND		S32	64
+	NAMPS		S32	64
+END
+
+# How to read this data
+PHU		STR	CHIP	# The FITS file represents a single chip
+EXTENSIONS	STR	CELL	# The extensions represent cells
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, type
+	xy00	STR	pitch10u
+	xy01	STR	pitch10u
+	xy02	STR	pitch10u
+	xy03	STR	pitch10u
+	xy04	STR	pitch10u
+	xy05	STR	pitch10u
+	xy06	STR	pitch10u
+	xy07	STR	pitch10u
+	xy10	STR	pitch10u
+	xy11	STR	pitch10u
+	xy12	STR	pitch10u
+	xy13	STR	pitch10u
+	xy14	STR	pitch10u
+	xy15	STR	pitch10u
+	xy16	STR	pitch10u
+	xy17	STR	pitch10u
+	xy20	STR	pitch10u
+	xy21	STR	pitch10u
+	xy22	STR	pitch10u
+	xy23	STR	pitch10u
+	xy24	STR	pitch10u
+	xy25	STR	pitch10u
+	xy26	STR	pitch10u
+	xy27	STR	pitch10u
+	xy30	STR	pitch10u
+	xy31	STR	pitch10u
+	xy32	STR	pitch10u
+	xy33	STR	pitch10u
+	xy34	STR	pitch10u
+	xy35	STR	pitch10u
+	xy36	STR	pitch10u
+	xy37	STR	pitch10u
+	xy40	STR	pitch10u
+	xy41	STR	pitch10u
+	xy42	STR	pitch10u
+	xy43	STR	pitch10u
+	xy44	STR	pitch10u
+	xy45	STR	pitch10u
+	xy46	STR	pitch10u
+	xy47	STR	pitch10u
+	xy50	STR	pitch10u
+	xy51	STR	pitch10u
+	xy52	STR	pitch10u
+	xy53	STR	pitch10u
+	xy54	STR	pitch10u
+	xy55	STR	pitch10u
+	xy56	STR	pitch10u
+	xy57	STR	pitch10u
+	xy60	STR	pitch10u
+	xy61	STR	pitch10u
+	xy62	STR	pitch10u
+	xy63	STR	pitch10u
+	xy64	STR	pitch10u
+	xy65	STR	pitch10u
+	xy66	STR	pitch10u
+	xy67	STR	pitch10u
+	xy70	STR	pitch10u
+	xy71	STR	pitch10u
+	xy72	STR	pitch10u
+	xy73	STR	pitch10u
+	xy74	STR	pitch10u
+	xy75	STR	pitch10u
+	xy76	STR	pitch10u
+	xy77	STR	pitch10u
+END
+
+# Specify the cell data
+CELLS	METADATA
+	pitch10u	METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[575:606,1:594]
+		CELL.TRIMSEC		STR	[1:574,1:594]
+	#	CELL.BIASSEC		STR	BIASSEC
+	#	CELL.TRIMSEC		STR	DATASEC
+	END
+
+	# This is just in here for fun
+	pitch12u	METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[1:10,1:512];[523:574,1:512]
+		CELL.TRIMSEC		STR	[11:522,1:512]
+	#	CELL.BIASSEC		STR	BIASSEC
+	#	CELL.TRIMSEC		STR	TRIMSEC
+	END
+END
+
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	CELL.XBIN	STR	CCDSUM
+	CELL.YBIN	STR	CCDSUM
+	CELL.X0		STR	IMNPIX1
+	CELL.Y0		STR	IMNPIX2
+	CELL.XPARITY	STR	LTM1_1
+	CELL.YPARITY	STR	LTM2_2
+	CELL.SATURATION	STR	SATURATE
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.AIRMASS	F32	0.0
+	FPA.FILTER	STR	NONE
+	FPA.POSANGLE	F32	0.0
+	FPA.RA		STR	0:0:0
+	FPA.DEC		STR	0:0:0
+	FPA.RADECSYS	STR	ICRS
+	FPA.NAME	S32	12345
+	CELL.EXPOSURE	F32	0.0
+	CELL.DARKTIME	F32	0.0
+	CELL.GAIN	F32	1.0
+	CELL.READNOISE	F32	0.0
+	CELL.READDIR	S32	2
+	CELL.BAD	S32	0
+	CELL.TIMESYS	STR	UTC
+	CELL.TIME	STR	2005-11-23T12:34:56.78
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP,CELL
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP,CELL
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	CELL.TIME	STR	ISO
+	CELL.BINNING	STR	TOGETHER
+	CELL.X0		STR	FORTRAN
+	CELl.Y0		STR	FORTRAN
+END
+ 
+# Recipe options
+RECIPES		METADATA
+	PHASE2		STR	phase2.config		# Phase 2 recipe details
+END
+ 
+# How to get the supplementary stuff: mask and weight
+SUPPLEMENTARY	METADATA
+	MASK.SOURCE	STR	FILE		# Source type for mask: EXT | FILE
+	MASK.NAME	STR	%a_mask.fits	# Name for mask extension or filename
+	WEIGHT.SOURCE	STR	FILE		# Source type for weight: EXT | FILE
+	WEIGHT.NAME	STR	%a_weight.fits	# Name for weight extension or filename
+END
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/ipprc.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/ipprc.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/ipprc.config	(revision 22322)
@@ -0,0 +1,35 @@
+### Example .ipprc file
+
+### Database configuration
+DBSERVER	STR	ippdb.ifa.hawaii.edu	# Database host name (for psDBInit)
+DBUSER		STR	ipp			# Database user name (for psDBInit)
+DBPASSWORD	STR	password		# Database password (for psDBInit)
+
+### Setups for each camera system
+CAMERAS		METADATA
+	MEGACAM_RAW	STR	megacam_raw.config
+	GPC1_RAW	STR	gpc1_raw.config
+	MEGACAM_SPLICE	STR	megacam_splice.config
+#	LRIS_BLUE	STR	lris_blue.config
+#	LRIS_RED	STR	lris_red.config
+END
+
+### psLib setup
+#TIME		STR	/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/etc/pslib/psTime.config	# Time configuration file
+LOGLEVEL	S32	3			# Logging level; 3=INFO
+LOGFORMAT	STR	THLNM			# Log format
+LOGDEST	STR	STDOUT				# Log destination
+TRACE		METADATA			# Trace levels
+	pap			S32	10
+	pmFPAPrint		S32	10
+	pmFPAWrite		S32	10
+#	pmConfigRead		S32	10
+#	pmFPAWriteMask		S32	10
+#	pmFPAWriteWeight	S32	10
+	pmChipMosaic		S32	10
+	pmFPAMorph		S32	10
+	spliceCells		S32	10
+	spliceImage		S32	10
+	setConceptItem		S32	10
+#	pap_psMetadataCopy	S32	9
+END
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/megacam_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/megacam_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/megacam_raw.config	(revision 22322)
@@ -0,0 +1,175 @@
+# The raw MegaCam data comes off the telescope with each of the chips stored in extensions of a MEF file.
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	MegaCam
+	EXTEND		BOOL	T
+	NEXTEND		S32	72
+END
+
+# How to read this data
+PHU		STR	FPA	# The FITS file represents an entire FPA
+EXTENSIONS	STR	CELL	# The extensions represent cells
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:type
+	amp00	STR	ccd00:left
+	amp01	STR	ccd00:right
+	amp02	STR	ccd01:left
+	amp03	STR	ccd01:right
+	amp04	STR	ccd02:left
+	amp05	STR	ccd02:right
+	amp06	STR	ccd03:left
+	amp07	STR	ccd03:right
+	amp08	STR	ccd04:left
+	amp09	STR	ccd04:right
+	amp10	STR	ccd05:left
+	amp11	STR	ccd05:right
+	amp12	STR	ccd06:left
+	amp13	STR	ccd06:right
+	amp14	STR	ccd07:left
+	amp15	STR	ccd07:right
+	amp16	STR	ccd08:left
+	amp17	STR	ccd08:right
+	amp18	STR	ccd09:left
+	amp19	STR	ccd09:right
+	amp20	STR	ccd10:left
+	amp21	STR	ccd10:right
+	amp22	STR	ccd11:left
+	amp23	STR	ccd11:right
+	amp24	STR	ccd12:left
+	amp25	STR	ccd12:right
+	amp26	STR	ccd13:left
+	amp27	STR	ccd13:right
+	amp28	STR	ccd14:left
+	amp29	STR	ccd14:right
+	amp30	STR	ccd15:left
+	amp31	STR	ccd15:right
+	amp32	STR	ccd16:left
+	amp33	STR	ccd16:right
+	amp34	STR	ccd17:left
+	amp35	STR	ccd17:right
+	amp36	STR	ccd18:left
+	amp37	STR	ccd18:right
+	amp38	STR	ccd19:left
+	amp39	STR	ccd19:right
+	amp40	STR	ccd20:left
+	amp41	STR	ccd20:right
+	amp42	STR	ccd21:left
+	amp43	STR	ccd21:right
+	amp44	STR	ccd22:left
+	amp45	STR	ccd22:right
+	amp46	STR	ccd23:left
+	amp47	STR	ccd23:right
+	amp48	STR	ccd24:left
+	amp49	STR	ccd24:right
+	amp50	STR	ccd25:left
+	amp51	STR	ccd25:right
+	amp52	STR	ccd26:left
+	amp53	STR	ccd26:right
+	amp54	STR	ccd27:left
+	amp55	STR	ccd27:right
+	amp56	STR	ccd28:left
+	amp57	STR	ccd28:right
+	amp58	STR	ccd29:left
+	amp59	STR	ccd29:right
+	amp60	STR	ccd30:left
+	amp61	STR	ccd30:right
+	amp62	STR	ccd31:left
+	amp63	STR	ccd31:right
+	amp64	STR	ccd32:left
+	amp65	STR	ccd32:right
+	amp66	STR	ccd33:left
+	amp67	STR	ccd33:right
+	amp68	STR	ccd34:left
+	amp69	STR	ccd34:right
+	amp70	STR	ccd35:left
+	amp71	STR	ccd35:right
+END
+
+# Specify the cell data
+CELLS	METADATA
+	left	METADATA	# Left amplifier
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XPARITY		S32	1 # We could have specified this as a DEFAULT, but this works
+		CELL.X0			S32	1
+		CELL.Y0			S32	1
+	END
+	right	METADATA	# Right amplifier
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XPARITY		S32	-1 # This cell is read out in the opposite direction
+		CELL.X0			S32	2048
+		CELL.Y0			S32	1
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.NAME		STR	EXPNUM
+	FPA.AIRMASS		STR	AIRMASS
+	FPA.FILTER		STR	FILTER
+	FPA.POSANGLE		STR	ROTANGLE
+	FPA.RA			STR	RA
+	FPA.DEC			STR	DEC
+	FPA.RADECSYS		STR	RADECSYS
+	CELL.EXPOSURE		STR	EXPTIME
+	CELL.DARKTIME		STR	DARKTIME
+	CELL.GAIN		STR	GAIN
+	CELL.READNOISE		STR	RDNOISE
+	CELL.SATURATION		STR	SATURATE
+	CELL.TIME		STR	MJD-OBS
+	CELL.XBIN		STR	CCDBIN1
+	CELL.YBIN		STR	CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	CELL.READDIR		S32	1		# Cell is read in x direction
+	CELL.BAD		S32	0
+	CELL.TIMESYS		STR	UTC
+	CELL.YPARITY		S32	1
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	CELL.TIME	STR	MJD
+#	CELL.BINNING	STR	SEPARATE
+	CELL.X0		STR	FORTRAN
+	CELL.Y0		STR	FORTRAN
+END
+
+# Recipe options
+RECIPES		METADATA
+	PHASE2		STR	phase2.config		# Phase 2 recipe details
+END
+
+
+# How to get the supplementary stuff: mask and weight
+SUPPLEMENTARY	METADATA
+	MASK.SOURCE	STR	FILE		# Source type for mask: EXT | FILE
+	MASK.NAME	STR	%a_mask.fits	# Name for mask extension or filename
+	WEIGHT.SOURCE	STR	FILE		# Source type for weight: EXT | FILE
+	WEIGHT.NAME	STR	%a_weight.fits	# Name for weight extension or filename
+END
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/nonlin.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/nonlin.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/nonlin.dat	(revision 22322)
@@ -0,0 +1,106 @@
+# Non-linearity correction lookup table
+# Col 1: Input value
+# Col 2: Corrected value
+0	0
+100	1
+200	2
+300	3
+400	4
+500	5
+600	6
+700	7
+800	8
+900	9
+1000	10
+1100	11
+1200	12
+1300	13
+1400	14
+1500	15
+1600	16
+1700	17
+1800	18
+1900	19
+2000	20
+2100	21
+2200	22
+2300	23
+2400	24
+2500	25
+2600	26
+2700	27
+2800	28
+2900	29
+3000	30
+3100	31
+3200	32
+3300	33
+3400	34
+3500	35
+3600	36
+3700	37
+3800	38
+3900	39
+4000	40
+4100	41
+4200	42
+4300	43
+4400	44
+4500	45
+4600	46
+4700	47
+4800	48
+4900	49
+5000	50
+5100	51
+5200	52
+5300	53
+5400	54
+5500	55
+5600	56
+5700	57
+5800	58
+5900	59
+6000	60
+6100	61
+6200	62
+6300	63
+6400	64
+6500	65
+6600	66
+6700	67
+6800	68
+6900	69
+7000	70
+7100	71
+7200	72
+7300	73
+7400	74
+7500	75
+7600	76
+7700	77
+7800	78
+7900	79
+8000	80
+8100	81
+8200	82
+8300	83
+8400	84
+8500	85
+8600	86
+8700	87
+8800	88
+8900	89
+9000	90
+9100	91
+9200	92
+9300	93
+9400	94
+9500	95
+9600	96
+9700	97
+9800	98
+9900	99
+10000	100
+10001	100
+1e6	100
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/papPhase2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/papPhase2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/papPhase2.c	(revision 22322)
@@ -0,0 +1,858 @@
+#include <stdio.h>
+#include <strings.h>
+
+#include "pslib.h"
+
+#include "psAdditionals.h"
+
+#include "pmAstrometry.h"
+#include "pmReadout.h"
+#include "pmConfig.h"
+#include "pmFPAConstruct.h"
+#include "pmFPARead.h"
+#include "pmFPAConceptsGet.h"
+#include "pmFPAWrite.h"
+
+#include "pmFlatField.h"
+#include "pmMaskBadPixels.h"
+#include "pmNonLinear.h"
+#include "pmSubtractBias.h"
+#include "pmChipMosaic.h"
+//#include "pmFPAMorph.h"
+
+// Phase 2 needs to:
+// * Read configurations
+// * Read in the images
+// * Flag bad pixels
+// * Non-linearity correction
+// * Bias/dark subtraction (MHPCC)
+// * Flat-field (MHPCC)
+// * Fringe subtraction (Not for now)
+// * Source identification and photometry (IPP prototype)
+// * Astrometry (IPP prototype soon)
+// * Write calibrated image
+// * Write source catalogue, astrometry, etc.
+
+// Currently neglecting different input types (F32, S32, U16, etc)
+
+// Would like to fold in Nebulous at some stage as well.
+
+// Currently neglect to convolve the reference frames with the orthogonal transfer kernel
+
+// phase2 INPUT.fits OUTPUT.fits -bias BIAS.fits -dark DARK.fits -flat FLAT.fits -chip CHIP
+//
+// Most are self-explanatory.  "-chip" says "only work on this particular chip".
+
+
+#define RECIPE "PHASE2"                 // Name of the recipe to use
+
+static psMemId memPrintAlloc(const psMemBlock *mb)
+{
+    printf("Allocated memory block %ld (%ld):\n"
+           "\tFile %s, line %d, size %d\n"
+           "\tPosts: %x %x %x\n",
+           mb->id, mb->refCounter, mb->file, mb->lineno, mb->userMemorySize, mb->startblock, mb->endblock,
+           *(void**)((int8_t *)(mb + 1) + mb->userMemorySize));
+    return 0;
+}
+static psMemId memPrintFree(const psMemBlock *mb)
+{
+    printf("Freed memory block %ld (%ld):\n"
+           "\tFile %s, line %d, size %d\n"
+           "\tPosts: %x %x %x\n",
+           mb->id, mb->refCounter, mb->file, mb->lineno, mb->userMemorySize, mb->startblock, mb->endblock,
+           *(void**)((int8_t *)(mb + 1) + mb->userMemorySize));
+    return 0;
+}
+static void memPrint(const psPtr ptr)
+{
+    psMemBlock *mb = ((psMemBlock*)ptr) - 1;
+    printf("Memory block %ld (%ld):\n"
+           "\tFile %s, line %d, size %d\n"
+           "\tPosts: %x %x %x\n",
+           mb->id, mb->refCounter, mb->file, mb->lineno, mb->userMemorySize, mb->startblock, mb->endblock,
+           *(void**)((int8_t *)(mb + 1) + mb->userMemorySize));
+}
+
+int main(int argc, char *argv[])
+{
+#if 0
+    // Hunting memory leaks
+    psMemAllocCallbackSetID(22367);
+    psMemFreeCallbackSetID(22367);
+    psMemAllocCallbackSet(memPrintAlloc);
+    psMemFreeCallbackSet(memPrintFree);
+#endif
+
+    psTimerStart("phase2");
+
+    // Parse the configurations
+    psMetadata *site = NULL;            // Site configuration
+    psMetadata *camera = NULL;          // Camera configuration
+    psMetadata *recipe = NULL;          // Recipe configuration
+    if (! pmConfigRead(&site, &camera, &recipe, &argc, argv, RECIPE)) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // Parse other command-line arguments
+    psMetadata *arguments = psMetadataAlloc(); // The arguments, with default values
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-bias", 0, "Name of the bias image", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dark", 0, "Name of the dark image", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-flat", 0, "Name of the flat-field image", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-mask", 0, "Name of the mask image", "");
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-chip", 0, "Chip number to process (if positive)", -1);
+    if (! psArgumentParse(arguments, &argc, argv) || argc != 3) {
+        printf("\nPan-STARRS Phase 2 processing\n\n");
+        printf("Usage: %s INPUT.fits OUTPUT.fits\n\n", argv[0]);
+        psArgumentHelp(arguments);
+        psFree(arguments);
+        exit(EXIT_FAILURE);
+    }
+    const char *inputName = argv[1];    // Name of input image
+    const char *outputName = argv[2];   // Name of output image
+    const char *biasName = psMetadataLookupStr(NULL, arguments, "-bias"); // Name of bias image
+    const char *darkName = psMetadataLookupStr(NULL, arguments, "-dark"); // Name of dark image
+    const char *flatName = psMetadataLookupStr(NULL, arguments, "-flat"); // Name of flat-field image
+    const char *maskName = psMetadataLookupStr(NULL, arguments, "-mask"); // Name of mask image
+    const int chipNum = psMetadataLookupS32(NULL, arguments, "-chip"); // Chip number to work on
+    printf("Input: %s\nOutput: %s\n", inputName, outputName);
+    printf("Bias: %s\n", biasName);
+    printf("Dark: %s\n", darkName);
+    printf("Flat: %s\n", flatName);
+    printf("Mask: %s\n", maskName);
+    printf("Chip: %d\n", chipNum);
+
+    // Open the input
+    psFits *inputFile = psFitsOpen(inputName, "r"); // File handle for FITS file
+    if (! inputFile) {
+        psErrorStackPrint(stderr, "Can't open input image: %s\n", inputName);
+        exit(EXIT_FAILURE);
+    }
+    psMetadata *header = psFitsReadHeader(NULL, inputFile); // FITS header
+#if PRODUCTION
+    psDB *database = pmConfigDB(site);  // Database handle
+#else
+    psDB *database = NULL;              // Database handle
+#endif
+
+    // Open the output and output mask
+    // We do it here so that we don't process the whole lot and then find out we can't open the output file
+    psFits *outputFile = psFitsOpen(outputName, "w");
+    if (! outputFile) {
+        psErrorStackPrint(stderr, "Can't open output image: %s\n", outputName);
+        exit(EXIT_FAILURE);
+    }
+
+    // Get camera configuration from header if not already defined
+    if (! camera) {
+        camera = pmConfigCameraFromHeader(site, header);
+        if (! camera) {
+            psErrorStackPrint(stderr, "Can't find camera configuration!\n");
+            exit(EXIT_FAILURE);
+        }
+   } else if (! pmConfigValidateCamera(camera, header)) {
+        psError(PS_ERR_IO, true, "%s does not seem to be from the camera.\n", inputName);
+        exit(EXIT_FAILURE);
+    }
+    if (! recipe && !(recipe = pmConfigRecipeFromCamera(camera, RECIPE))) {
+        psErrorStackPrint(stderr, "Can't find recipe configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // Construct camera in preparation for reading
+    pmFPA *input = pmFPAConstruct(camera);
+    pmFPA *mask = pmFPAConstruct(camera);
+    pmFPA *bias = pmFPAConstruct(camera);
+    pmFPA *dark = pmFPAConstruct(camera);
+    pmFPA *flat = pmFPAConstruct(camera);
+
+    // Set various tasks
+    bool doMask = false;                // Mask bad pixles
+    bool doNonLin = false;              // Non-linearity correction
+    bool doBias = false;                // Bias subtraction
+    bool doDark = false;                // Dark subtraction
+    bool doOverscan = false;            // Overscan subtraction
+    bool doFlat = false;                // Flat-field normalisation
+    bool doFringe = false;              // Fringe subtraction
+    bool doSource = false;              // Source identification and photometry
+    bool doAstrom = false;              // Astrometry
+    pmOverscanAxis overscanMode = PM_OVERSCAN_NONE; // Axis for overscan
+    pmFit overscanFitType = PM_FIT_NONE; // Fit type for overscan
+    int overscanBins = 1;               // Number of pixels per bin for overscan
+    psStats *overscanStats = NULL;      // Statistics for overscan
+    void *overscanFit = NULL;           // Overscan fit (polynomial or spline)
+
+    if (psMetadataLookupBool(NULL, recipe, "MASK")) {
+        if (strlen(maskName) > 0) {
+            doMask = true;
+        } else {
+            psLogMsg("phase2", PS_LOG_WARN, "Masking is desired, but no mask was supplied --- no masking "
+                     "will be performed.\n");
+        }
+    }
+    if (psMetadataLookupBool(NULL, recipe, "NONLIN")) {
+        doNonLin = true;
+    }
+    if (psMetadataLookupBool(NULL, recipe, "BIAS")) {
+        if (strlen(biasName) > 0) {
+            doBias = true;
+        } else {
+            psLogMsg("phase2", PS_LOG_WARN, "Bias subtraction is desired, but no bias was supplied --- "
+                     "no bias subtraction will be performed.\n");
+        }
+    }
+    if (psMetadataLookupBool(NULL, recipe, "DARK")) {
+        if (strlen(darkName) > 0) {
+            doDark = true;
+        } else {
+            psLogMsg("phase2", PS_LOG_WARN, "Dark subtraction is desired, but no dark was supplied --- "
+                     "no dark subtraction will be performed.\n");
+        }
+    }
+    if (psMetadataLookupBool(NULL, recipe, "OVERSCAN")) {
+        doOverscan = true;
+        psString mode = psMetadataLookupStr(NULL, recipe, "OVERSCAN.MODE");
+        if (strcasecmp(mode, "INDIVIDUAL") == 0) {
+            overscanMode = PM_OVERSCAN_ROWS;
+            // By "ROWS", I mean either rows or columns --- will check the READDIR for each chip
+        } else if (strcasecmp(mode, "ALL") == 0) {
+            overscanMode = PM_OVERSCAN_ALL;
+        } else if (strcasecmp(mode, "NONE") != 0) {
+            psLogMsg("phase2", PS_LOG_WARN, "OVERSCAN.MODE (%s) is not one of NONE, INDIVIDUAL, or ALL:"
+                     " assuming NONE.\n", mode);
+        }
+        psString fit = psMetadataLookupStr(NULL, recipe, "OVERSCAN.FIT");
+        if (strcasecmp(fit, "POLYNOMIAL") == 0) {
+            overscanFitType = PM_FIT_POLYNOMIAL;
+            int order = psMetadataLookupS32(NULL, recipe, "OVERSCAN.ORDER"); // Order of polynomial fit
+            overscanFit = psPolynomial1DAlloc(order, PS_POLYNOMIAL_ORD);
+        } else if (strcasecmp(fit, "SPLINE") == 0) {
+            overscanFitType = PM_FIT_SPLINE;
+            int order = psMetadataLookupS32(NULL, recipe, "OVERSCAN.ORDER"); // Order of polynomial fit
+            overscanFit = NULL;
+//          overscanFit = psSpline1DAlloc();
+        } else if (strcasecmp(fit, "NONE") != 0) {
+            psLogMsg("phase2", PS_LOG_WARN, "OVERSCAN.FIT (%s) is not one of NONE, POLYNOMIAL, or SPLINE:"
+                     " assuming NONE.\n", fit);
+        }
+        overscanBins = psMetadataLookupS32(NULL, recipe, "OVERSCAN.BIN");
+        if (overscanBins <= 0) {
+            psErrorStackPrint(stderr, "OVERSCAN.BIN (%d) is non-positive --- assuming 1.\n", overscanBins);
+            overscanBins = 1;
+        }
+        psString stat = psMetadataLookupStr(NULL, recipe, "OVERSCAN.STAT");
+        if (strcasecmp(stat, "MEAN")) {
+            overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        } else if (strcasecmp(stat, "MEDIAN")) {
+            overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+        } else {
+            psErrorStackPrint(stderr, "OVERSCAN.STAT (%s) is not one of MEAN, MEDIAN: assuming MEAN\n", stat);
+            overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        }
+    }
+
+    if (psMetadataLookupBool(NULL, recipe, "FLAT")) {
+        if (strlen(flatName) > 0) {
+            doFlat = true;
+        } else {
+            psLogMsg("phase2", PS_LOG_WARN, "Flat-fielding is desired, but no flat was supplied --- "
+                     "no flat-fielding will be performed.\n");
+        }
+    }
+
+    // Chip selection
+    if (chipNum >= 0) {
+        if (! pmFPASelectChip(input, chipNum) || ! pmFPASelectChip(bias, chipNum) ||
+            ! pmFPASelectChip(dark, chipNum) || ! pmFPASelectChip(flat, chipNum) ||
+            ! pmFPASelectChip(mask, chipNum)) {
+            psErrorStackPrint(stderr, "Chip number %d doesn't exist in camera.\n", chipNum);
+            exit(EXIT_FAILURE);
+        }
+        psLogMsg("phase2", PS_LOG_INFO, "Operating only on chip %d\n", chipNum);
+    }
+
+    psLogMsg("phase2", PS_LOG_INFO, "Setup completed after %f sec.\n", psTimerMark("phase2"));
+
+    // Read in the input pixels
+    psLogMsg("phase2", PS_LOG_INFO, "Opening input image: %s\n", inputName);
+    if (! pmFPARead(input, inputFile, header, database)) {
+        psErrorStackPrint(stderr, "Unable to populate camera from FITS file: %s\n", inputName);
+        exit(EXIT_FAILURE);
+    }
+    psFitsClose(inputFile);
+
+#if 0
+    pmFPAReadMask(input, inputFile);
+    pmFPAReadWeight(input, inputFile);
+//#else
+    {
+        // Generate mask and weight frame
+        p_pmHDU *hdu = NULL;
+        if (input->hdu) {
+            hdu = input->hdu;
+        }
+        psArray *chips = input->chips;
+        for (int chipNum = 0; chipNum < chips->n; chipNum++) {
+            pmChip *chip = chips->data[chipNum];
+            if (chip->valid) {
+                if (chip->hdu) {
+                    hdu = chip->hdu;
+                }
+                psArray *cells = chip->cells;
+                for (int cellNum = 0; cellNum < cells->n; cellNum++) {
+                    pmCell *cell = cells->data[cellNum];
+                    if (cell->valid) {
+                        if (cell->hdu) {
+                            hdu = cell->hdu;
+                        }
+
+                        hdu->masks = psArrayAlloc(hdu->images->n);
+                        hdu->weights = psArrayAlloc(hdu->images->n);
+                        psArray *readouts = cell->readouts;
+                        for (int readNum = 0; readNum < hdu->images->n; readNum++) {
+                            psImage *image = hdu->images->data[readNum];
+                            psImage *mask = psImageAlloc(image->numCols, image->numRows, PS_TYPE_U8);
+                            psImage *weight = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
+                            pmReadout *readout = readouts->data[readNum];
+                            for (int j = 0; j < image->numRows; j++) {
+                                for (int i = 0; i < image->numCols; i++) {
+                                    mask->data.U8[j][i] = j + i;
+                                    weight->data.F32[j][i] = sqrtf(image->data.F32[j][i]);
+                                }
+                            }
+                            hdu->masks->data[readNum] = mask;
+                            hdu->weights->data[readNum] = weight;
+                            readout->mask = psMemIncrRefCounter(mask);
+                            readout->weight = psMemIncrRefCounter(weight);
+                        }
+                    }
+                }
+            }
+        }
+    }
+#endif
+
+#if 0
+    pmFPAPrint(input);
+#endif
+
+    // Load the calibration frames, if required
+    if (doBias) {
+        psFits *biasFile = psFitsOpen(biasName, "r"); // File handle for bias
+        psMetadata *biasHeader = psFitsReadHeader(NULL, biasFile); // FITS header for bias
+        if (! pmConfigValidateCamera(camera, biasHeader)) {
+            psError(PS_ERR_IO, true, "Bias (%s) does not seem to be from the same camera.\n", biasName);
+            exit(EXIT_FAILURE);
+        }
+        psLogMsg("phase2", PS_LOG_INFO, "Opening bias image: %s\n", biasName);
+        if (! pmFPARead(bias, biasFile, biasHeader, database)) {
+            psErrorStackPrint(stderr, "Unable to populate bias camera from fits FITS: %s\n", biasName);
+            exit(EXIT_FAILURE);
+        }
+        psFree(biasHeader);
+        psFitsClose(biasFile);
+    }
+
+    if (doDark) {
+        psFits *darkFile = psFitsOpen(darkName, "r"); // File handle for dark
+        psMetadata *darkHeader = psFitsReadHeader(NULL, darkFile); // FITS header for dark
+        if (! pmConfigValidateCamera(camera, darkHeader)) {
+            psError(PS_ERR_IO, true, "Dark (%s) does not seem to be from the same camera.\n", darkName);
+            exit(EXIT_FAILURE);
+        }
+        psLogMsg("phase2", PS_LOG_INFO, "Opening dark image: %s\n", darkName);
+        if (! pmFPARead(dark, darkFile, darkHeader, database)) {
+            psErrorStackPrint(stderr, "Unable to populate dark camera from fits FITS: %s\n", darkName);
+            exit(EXIT_FAILURE);
+        }
+        psFree(darkHeader);
+        psFitsClose(darkFile);
+    }
+
+    if (doMask) {
+        psFits *maskFile = psFitsOpen(maskName, "r"); // File handle for mask
+        psMetadata *maskHeader = psFitsReadHeader(NULL, maskFile); // FITS header for mask
+        if (! pmConfigValidateCamera(camera, maskHeader)) {
+            psError(PS_ERR_IO, true, "Mask (%s) does not seem to be from the same camera.\n", maskName);
+            exit(EXIT_FAILURE);
+        }
+        psLogMsg("phase2", PS_LOG_INFO, "Opening mask image: %s\n", maskName);
+        if (! pmFPARead(mask, maskFile, maskHeader, database)) {
+            psErrorStackPrint(stderr, "Unable to populate mask camera from fits FITS: %s\n", maskName);
+            exit(EXIT_FAILURE);
+        }
+        psFree(maskHeader);
+        psFitsClose(maskFile);
+    }
+
+    if (doFlat) {
+        psFits *flatFile = psFitsOpen(flatName, "r"); // File handle for flat
+        if (! flatFile) {
+            psErrorStackPrint(stderr, "Can't open flat image: %s\n", flatName);
+            exit(EXIT_FAILURE);
+        }
+        psMetadata *flatHeader = psFitsReadHeader(NULL, flatFile); // FITS header for flat
+        if (! pmConfigValidateCamera(camera, flatHeader)) {
+            psError(PS_ERR_IO, true, "Flat (%s) does not seem to be from the same camera.\n", flatName);
+            exit(EXIT_FAILURE);
+        }
+        psLogMsg("phase2", PS_LOG_INFO, "Opening flat image: %s\n", flatName);
+        if (! pmFPARead(flat, flatFile, flatHeader, database)) {
+            psErrorStackPrint(stderr, "Unable to populate flat camera from fits FITS: %s\n", flatName);
+            exit(EXIT_FAILURE);
+        }
+        psFree(flatHeader);
+        psFitsClose(flatFile);
+    }
+
+    psLogMsg("phase2", PS_LOG_INFO, "Input completed after %f sec.\n", psTimerMark("phase2"));
+
+    psArray *inputChips = input->chips; // Array of chips in input image
+    psArray *biasChips = bias->chips;   // Array of chips in bias image
+    psArray *darkChips = dark->chips;   // Array of chips in dark image
+    psArray *maskChips = mask->chips;   // Array of chips in mask image
+    psArray *flatChips = flat->chips;   // Array of chips in flat image
+    for (int i = 0; i < inputChips->n; i++) {
+        pmChip *inputChip = inputChips->data[i]; // Chip of interest in input image
+        pmChip *biasChip = biasChips->data[i]; // Chip of interest in bias image
+        pmChip *darkChip = darkChips->data[i]; // Chip of interest in dark image
+        pmChip *maskChip = maskChips->data[i]; // Chip of interest in mask image
+        pmChip *flatChip = flatChips->data[i]; // Chip of interest in flat image
+
+        if (! inputChip->valid) {
+            continue;
+        }
+
+        psArray *inputCells = inputChip->cells; // Array of cells in input image
+        psArray *biasCells = biasChip->cells; // Array of cells in bias image
+        psArray *darkCells = darkChip->cells; // Array of cells in dark image
+        psArray *maskCells = maskChip->cells; // Array of cells in mask image
+        psArray *flatCells = flatChip->cells; // Array of cells in flat image
+
+        for (int j = 0; j < inputCells->n; j++) {
+            pmCell *inputCell = inputCells->data[j]; // Cell of interest in input image
+            pmCell *biasCell = biasCells->data[j]; // Cell of interest in bias image
+            pmCell *darkCell = darkCells->data[j]; // Cell of interest in dark imag
+            pmCell *maskCell = maskCells->data[j]; // Cell of interest in mask image
+            pmCell *flatCell = flatCells->data[j]; // Cell of interest in flat image
+
+            if (! inputCell->valid) {
+                continue;
+            }
+
+            psArray *inputReadouts = inputCell->readouts; // Array of readouts in input image
+            if (doBias && biasCell->readouts->n > 1) {
+                psLogMsg("phase2", PS_LOG_WARN, "Bias contains multiple readouts: only the first will be"
+                         " used.");
+            }
+            if (doDark && darkCell->readouts->n > 1) {
+                psLogMsg("phase2", PS_LOG_WARN, "Dark contains multiple readouts: only the first will be"
+                         " used.");
+            }
+            if (doMask && maskCell->readouts->n > 1) {
+                psLogMsg("phase2", PS_LOG_WARN, "Mask contains multiple readouts: only the first will be"
+                         " used.");
+            }
+            if (doFlat && flatCell->readouts->n > 1) {
+                psLogMsg("phase2", PS_LOG_WARN, "Flat contains multiple readouts: only the first will be"
+                         " used.");
+            }
+
+            pmReadout *biasReadout = NULL; // Bias readout
+            pmReadout *maskReadout = NULL; // Mask readout
+            pmReadout *flatReadout = NULL; // Flat readout
+            psImage *biasImage = NULL;  // Bias pixels
+            psImage *darkImage = NULL;  // Dark pixels
+            float darkTime = 1.0;       // Dark time for dark image
+            psImage *maskImage = NULL;  // Mask pixels
+
+            if (doBias) {
+                biasReadout = biasCell->readouts->data[0]; // Readout of interest in bias image
+                biasImage = biasReadout->image;
+            }
+            if (doDark) {
+                pmReadout *darkReadout = darkCell->readouts->data[0]; // Readout of interest in dark image
+                darkImage = darkReadout->image;
+                darkTime = psMetadataLookupF32(NULL, darkCell->concepts, "CELL.DARKTIME");
+                if (darkTime <= 0.0) {
+                    psErrorStackPrint(stderr, "DARKTIME for dark image (%f) is non-positive.\n", darkTime);
+                    exit(EXIT_FAILURE);
+                }
+            }
+            if (doMask) {
+                maskReadout = maskCell->readouts->data[0]; // Readout of interest in mask image
+                maskImage = maskReadout->image;
+            }
+            if (doFlat) {
+                flatReadout = flatCell->readouts->data[0]; // Readout of interest in mask image
+            }
+
+            for (int k = 0; k < inputReadouts->n; k++) {
+                pmReadout *inputReadout = inputReadouts->data[k]; // Readout of interest in input image
+                psImage *inputImage = inputReadout->image; // The actual image
+
+                // Mask bad pixels
+                if (doMask) {
+                    float saturation = psMetadataLookupF32(NULL, inputCell->concepts, "CELL.SATURATION");
+
+                    // Need to change this later to grow the mask by the size of the convolution kernel
+                    (void)pmMaskBadPixels(inputReadout, maskImage,
+                                          PM_MASK_TRAP | PM_MASK_BADCOL | PM_MASK_SAT, saturation,
+                                          PM_MASK_TRAP, 0);
+                }
+
+                // Non-linearity correction
+                if (doNonLin) {
+                    psMetadataItem *dataItem = psMetadataLookup(recipe, "NONLIN.DATA");
+                    if (! dataItem) {
+                        psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but unable to "
+                                 "find NONLIN.DATA in recipe --- ignored.\n");
+                    } else {
+                        switch (dataItem->type) {
+                          case PS_DATA_VECTOR:
+                            {
+                                // These are the polynomial coefficients
+                                psVector *coeff = dataItem->data.V; // The coefficient vector
+                                if (coeff->type.type != PS_TYPE_F64) {
+                                    psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64); // F64 version
+                                    coeff = temp;
+                                }
+                                psPolynomial1D *correction = psPolynomial1DAlloc(coeff->n - 1,
+                                                                                 PS_POLYNOMIAL_ORD);
+                                psFree(correction->coeff);
+                                correction->coeff = psMemIncrRefCounter(coeff->data.F64);
+                                (void)pmNonLinearityPolynomial(inputReadout, correction);
+                                psFree(coeff);
+                                psFree(correction);
+                            }
+                            break;
+                          case PS_DATA_STRING:
+                            {
+                                // This is a filename
+                                psString name = dataItem->data.V;       // Filename
+                                psLookupTable *table = psLookupTableAlloc(name, "%f %f", 0);
+                                if (psLookupTableRead(table) <= 0) {
+                                    psErrorStackPrint(stderr, "Unable to read non-linearity correction file "
+                                                      "%s --- ignored\n", name);
+                                } else {
+#ifdef PRODUCTION
+                                    (void)pmNonLinearityLookup(inputReadout, table);
+#else
+                                    psVector *influx = table->values->data[0];
+                                    psVector *outflux = table->values->data[1];
+                                    (void)pmNonLinearityLookup(inputReadout, table->values->data[0],
+                                                               table->values->data[1]);
+#endif
+                                }
+                                psFree(table);
+                            }
+                            break;
+                          case PS_DATA_METADATA:
+                            {
+                                // This is a menu
+                                psMetadata *options = dataItem->data.V; // Options with concept values as keys
+                                bool mdok = false; // Success of MD lookup
+                                psString concept = psMetadataLookupStr(&mdok, recipe, "NONLIN.SOURCE");
+                                if (! mdok || ! concept) {
+                                    psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but "
+                                             "unable to find NONLIN.SOURCE in recipe --- ignored.\n");
+                                } else {
+                                    psMetadataItem *conceptValueItem = pmCellGetConcept(inputCell, concept);
+                                    if (! conceptValueItem) {
+                                        psLogMsg("phase2", PS_LOG_WARN, "Unable to find value of concept %s "
+                                                 "for non-linearity correction --- ignored.\n", concept);
+                                    } else if (conceptValueItem->type != PS_DATA_STRING) {
+                                        psLogMsg("phase2", PS_LOG_WARN, "Type for concept %s isn't STRING, as"
+                                                 " expected for non-linearity correction --- ignored.\n",
+                                                 concept);
+                                    } else {
+                                        psString conceptValue = conceptValueItem->data.V;
+                                        // Get the value of the concept
+                                        psMetadataItem *optionItem = psMetadataLookup(options, conceptValue);
+                                        if (!optionItem) {
+                                            psLogMsg("phase2", PS_LOG_WARN, "Unable to find %s in NONLIN.DATA"
+                                                     " --- ignored.\n", conceptValue);
+                                        } else if (optionItem->type == PS_DATA_VECTOR) {
+                                            // These are the polynomial coefficients
+                                            psVector *coeff = optionItem->data.V; // The coefficient vector
+                                            if (coeff->type.type != PS_TYPE_F64) {
+                                                psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64);
+                                                coeff = temp;
+                                            }
+                                            // Polynomial correction
+                                            psPolynomial1D *correction = psPolynomial1DAlloc(coeff->n - 1,
+                                                                                             PS_POLYNOMIAL_ORD);
+                                            psFree(correction->coeff);
+                                            correction->coeff = psMemIncrRefCounter(coeff->data.F64);
+                                            (void)pmNonLinearityPolynomial(inputReadout, correction);
+                                            psFree(coeff);
+                                            psFree(correction);
+                                        } else if (optionItem->type == PS_DATA_STRING) {
+                                            // This is a filename
+                                            psString tableName = optionItem->data.V; // The filename
+                                            psLookupTable *table = psLookupTableAlloc(tableName, "%f %f", 0);
+                                            int numLines = 0; // Number of lines read from table
+                                            if ((numLines = psLookupTableRead(table)) <= 0) {
+                                                psErrorStackPrint(stderr, "Unable to read non-linearity "
+                                                                  "correction file %s --- ignored\n",
+                                                                  tableName);
+                                            } else {
+#ifdef PRODUCTION
+                                                (void)pmNonLinearityLookup(inputReadout, table);
+#else
+                                                printf("XXX: Non-linearity correction from lookup table not "
+                                                       "yet implemented.\n");
+#endif
+                                            }
+                                            psFree(table);
+                                        } else {
+                                            psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction "
+                                                     "desired but unable to interpret NONLIN.DATA for %s"
+                                                     " --- ignored\n", conceptValue);
+                                        }
+                                    }
+                                }
+                            }
+                            break;
+                          default:
+                            psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but "
+                                     "NONLIN.DATA is of invalid type --- ignored.\n");
+                        }
+                    }
+                } // Non-linearity correction
+
+                // Bias, dark and overscan subtraction are all merged.
+
+                // Changed the definition of pmSubtractBias to do the dark subtraction as well, but
+                // it's not in there yet.  Once it is, we can get rid of "pedestal".
+
+#ifdef PRODUCTION
+                psImage *pedestal = NULL;       // Pedestal image (bias + scaled dark)
+                if (doDark) {
+                    // Dark time for input image
+                    float inputTime = psMetadataLookupF32(NULL, inputCell->concepts, "CELL.DARKTIME");
+                    if (inputTime <= 0) {
+                        psErrorStackPrint(stderr, "DARKTIME for input image (%f) is non-positive.\n",
+                                          inputTime);
+                        exit(EXIT_FAILURE);
+                    }
+
+                    pedestal = (psImage*)psBinaryOp(NULL, darkImage, "*",
+                                                    psScalarAlloc(inputTime * darkTime, PS_TYPE_F32));
+                }
+                if (doBias) {
+                    if (pedestal) {
+                        if (biasImage->numRows != darkImage->numRows ||
+                            biasImage->numCols != darkImage->numCols) {
+                            psError(PS_ERR_IO, true, "Bias and dark images have different dimensions: "
+                                    "%dx%d vs %dx%d\n", biasImage->numCols, biasImage->numRows,
+                                    darkImage->numCols, darkImage->numRows);
+                            exit(EXIT_FAILURE);
+                        }
+                        (void)psBinaryOp(pedestal, pedestal, "+", biasImage);
+                    } else {
+                        pedestal = psMemIncrRefCounter(biasImage);
+                    }
+                }
+#endif
+                if (overscanMode == PM_OVERSCAN_ROWS || overscanMode == PM_OVERSCAN_COLUMNS) {
+                    // Need to get the read direction
+                    int readdir = psMetadataLookupS32(NULL, inputCell->concepts, "CELL.READDIR");
+                    if (readdir == 1) {
+// psmodule-0.8.0 has confused PM_OVERSCAN_ROWS and PM_OVERSCAN_COLUMNS; bug 608.
+#ifdef PRODUCTION
+                        overscanMode = PM_OVERSCAN_ROWS;
+#else
+                        overscanMode = PM_OVERSCAN_COLUMNS;
+#endif
+                    } else if (readdir == 2) {
+#ifdef PRODUCTION
+                        overscanMode = PM_OVERSCAN_COLUMNS;
+#else
+                        overscanMode = PM_OVERSCAN_ROWS;
+#endif
+                    } else {
+                        psErrorStackPrint(stderr, "CELL.READDIR (%d) is not 1 or 2 --- assuming 1.\n",
+                                          readdir);
+                        overscanMode = PM_OVERSCAN_ROWS;
+                    }
+                }
+
+                if (doBias || doOverscan || doDark) {
+                    psList *inputOverscans = pmReadoutGetBias(inputReadout); // List of overscan bias regions
+#ifdef PRODUCTION
+                    (void)pmSubtractBias(inputReadout, overscanFit, inputOverscans, overscanMode,
+                                         overscanStats, overscanBins, overscanFitType, pedestal);
+#else
+                    (void)pmSubtractBias(inputReadout, overscanFit, inputOverscans, overscanMode,
+                                         overscanStats, overscanBins, overscanFitType, biasReadout);
+#endif
+                    psFree(inputOverscans);
+
+                    // Output overscan fit results, if required
+                    if (doOverscan) {
+                        if (! overscanFit) {
+                            psLogMsg("phase2", PS_LOG_WARN, "No fit generated!\n");
+                        } else {
+                            switch (overscanFitType) {
+                              case PM_FIT_POLYNOMIAL:
+                                {
+                                    psPolynomial1D *poly = (psPolynomial1D*)overscanFit; // The polynomial
+                                    psString coeffs = NULL;     // String containing the coefficients
+                                    for (int i = 0; i < poly->nX; i++) {
+                                        psStringAppend(&coeffs, "%e ", poly->coeff[i]);
+                                    }
+                                    psLogMsg("phase2", PS_LOG_INFO, "Overscan polynomial coefficients:\n%s\n",
+                                             coeffs);
+                                    psFree(coeffs);
+                                }
+                                break;
+                              case PM_FIT_SPLINE:
+                                {
+                                    psSpline1D *spline = (psSpline1D*)overscanFit; // The spline
+                                    psString coeffs = NULL;     // String containing the coefficients
+                                    for (int i = 0; i < spline->n; i++) {
+                                        psPolynomial1D *poly = spline->spline[i]; // i-th polynomial
+                                        psStringAppend(&coeffs, "%d: ", i);
+                                        for (int j = 0; j < poly->nX; j++) {
+                                            psStringAppend(&coeffs, "%e ", poly->coeff[i]);
+                                        }
+                                        psStringAppend(&coeffs, "\n");
+                                    }
+                                    psLogMsg("phase2", PS_LOG_INFO, "Overscan spline coefficients:\n%s\n",
+                                             coeffs);
+                                    psFree(coeffs);
+                                }
+                                break;
+                              case PM_FIT_NONE:
+                                break;
+                              default:
+                                psAbort(__func__, "Should never get here!!!\n");
+                            }
+                        }
+                    }
+                }
+
+                // XXX: Temporary: until the other functions are altered to do this themselves.
+                // Trim, so that flat, fringe etc computations are faster.
+#if 0
+#ifndef PRODUCTION
+                psRegion *trimRegion = psMetadataLookupPtr(NULL, inputCell->concepts, "CELL.TRIMSEC");
+                psImage *trimmed = psImageSubset(inputReadout->image, *trimRegion);
+                psFree(inputReadout->image);
+                inputReadout->image = trimmed;
+#endif
+#endif
+                // Flat-field correction
+                if (doFlat) {
+                    psLogMsg("phase2", PS_LOG_INFO, "Performing flat field.\n");
+#ifndef PRODUCTION
+                    psImage *dummyImage = psImageAlloc(inputReadout->image->numCols,
+                                                       inputReadout->image->numRows,
+                                                       PS_TYPE_U8);
+                    pmReadout *dummyMask = pmReadoutAlloc(NULL);
+                    dummyMask->image = dummyImage;
+                    (void)pmFlatField(inputReadout, dummyMask, flatReadout);
+                    psFree(dummyMask);
+                    psFree(dummyImage);
+#endif
+                }
+
+                // Fringe subtraction
+                if (doFringe) {
+#if 0
+                    // Placeholder
+                    pmReadout *pmSubtractSky(pmReadout *in, psPolynomial2D *poly, psImage *mask,
+                                             psU8 maskVal, int binFactor, psStats *stats, float clipSD);
+#endif
+                }
+
+                // Source identification and photometry
+                if (doSource) {
+                }
+
+                // Astrometry
+                if (doAstrom) {
+                }
+
+
+            } // Iterating over readouts
+        } // Iterating over cells
+
+#if 0
+        {
+            // Testing pmChipMosaic
+            psImage *mosaic = pmChipMosaic(inputChip, 1, 1); // Mosaic of chip
+            psFits *mosaicFile = psFitsOpen("mosaic.fits", "w"); // FITS file to which to write
+            psFitsWriteImage(mosaicFile, NULL, mosaic, 0);
+            psFitsClose(mosaicFile);
+            psFree(mosaic);
+        }
+#endif
+
+    } // Iterating over chips
+
+    psLogMsg("phase2", PS_LOG_INFO, "Processing completed after %f sec.\n", psTimerMark("phase2"));
+
+
+#if 0
+    {
+        // Testing pmFPAMorph
+        int nBad = 0;
+        psMetadata *newCamera = psMetadataConfigParse(NULL, &nBad, "megacam_splice.config", true);
+        pmFPA *newFPA = pmFPAConstruct(newCamera);
+        pmFPAMorph(newFPA, input, true, 0, 0);
+        //pmFPAPrint(newFPA);
+        psFits *newFile = psFitsOpen("morph.fits", "w");
+        pmFPAWrite(newFile, newFPA, database);
+        psFitsClose(newFile);
+        psFree(newFPA);
+    }
+#endif
+
+#if 1
+    // Write the output
+    pmFPAWrite(outputFile, input, database);
+    pmFPAWriteMask(input, outputFile);
+    pmFPAWriteWeight(input, outputFile);
+#endif
+
+    psLogMsg("phase2", PS_LOG_INFO, "Output completed after %f sec.\n", psTimerMark("phase2"));
+
+    psFitsClose(outputFile);
+
+    psFree(arguments);
+    psFree(site);
+    psFree(header);
+    psFree(camera);
+    psFree(recipe);
+    psFree(input);
+    psFree(mask);
+    psFree(bias);
+    psFree(dark);
+    psFree(flat);
+    psFree(overscanFit);
+    psFree(overscanStats);
+
+    psLogMsg("phase2", PS_LOG_INFO, "Cleanup completed after %f sec.\n", psTimerMark("phase2"));
+    psTimerStop();
+
+#if 0
+    psMemCheckCorruption(true);
+    psMemBlock **leaks = NULL;          // List of leaks
+    int nLeaks = psMemCheckLeaks(0, &leaks, NULL, false); // Number of leaks
+    printf("%d leaks found.\n", nLeaks);
+#if 1
+    for (int i = 0; i < nLeaks; i++) {
+        psMemBlock *mb = leaks[i];
+        printf("Memory leak %ld (%ld):\n"
+               "\tFile %s, line %d, size %d\n"
+               "\tPosts: %x %x %x\n",
+               mb->id, mb->refCounter, mb->file, mb->lineno, mb->userMemorySize, mb->startblock, mb->endblock,
+               *(void**)((int8_t *)(mb + 1) + mb->userMemorySize));
+    }
+#endif
+#endif
+
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/papStuff.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/papStuff.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/papStuff.c	(revision 22322)
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <strings.h>
+
+#include "pslib.h"
+#include "psAdditionals.h"
+
+#include "papStuff.h"
+
+
+static void memPrint(const psPtr ptr)
+{
+    psMemBlock *mb = ((psMemBlock*)ptr) - 1;
+    printf("Memory block %lld (%lld):\n"
+	   "\tFile %s, line %d, size %d\n"
+	   "\tPosts: %x %x %x\n",
+	   mb->id, mb->refCounter, mb->file, mb->lineno, mb->userMemorySize, mb->startblock, mb->endblock,
+	   *(void**)((int8_t *)(mb + 1) + mb->userMemorySize));
+}
+
+// Split string on given characters
+psList *papSplit(const char *string, const char *splitters)
+{
+    psList *values = psListAlloc(NULL);	// The list of values to return
+    unsigned int length = strlen(string); // The length of the string
+    unsigned int numSplitters = strlen(splitters); // Number of characters that might split
+    unsigned int start = 0;		// The position of the start of a word
+    for (int i = 1; i < length; i++) {
+	bool split = false;		// Is this character a splitter?
+	for (int j = 0; j < numSplitters && ! split; j++) {
+	    if (string[i] == splitters[j]) {
+		split = true;
+	    }
+	}
+	if (split) {
+	    if (i == start) {
+		// Some idiot put in two spaces, or two commas or something
+		start++;
+	    } else {
+		// We're at the end of the word
+		psString word = psStringNCopy(&string[start], i - start);
+		(void)psListAdd(values, PS_LIST_TAIL, word);
+		start = i + 1;
+		psFree(word);
+	    }
+	}
+    }
+    if (start < length) {
+	// Copy the last word
+	psString word = psStringNCopy(&string[start], length - start);
+	(void)psListAdd(values, PS_LIST_TAIL, word);
+	psFree(word);
+    }
+
+    return values;
+}
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/papStuff.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/papStuff.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/papStuff.h	(revision 22322)
@@ -0,0 +1,9 @@
+#ifndef PAP_STUFF_H
+#define PAP_STUFF_H
+
+#include "pslib.h"
+
+// Split string on given characters
+psList *papSplit(const char *string, const char *splitters);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/phase2.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/phase2.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/phase2.config	(revision 22322)
@@ -0,0 +1,65 @@
+### Phase 2 recipe configuration file
+
+# List of tasks to perform
+MASK		BOOL	FALSE		# Mask bad pixels
+NONLIN		BOOL	TRUE		# Non-linearity correction
+BIAS		BOOL	TRUE		# Bias subtraction
+DARK		BOOL	FALSE		# Dark subtraction
+OVERSCAN	BOOL	TRUE		# Overscan subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+SOURCE		BOOL	FALSE		# Source identification and photometry
+ASTROM		BOOL	FALSE		# Astrometry
+
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+#@NONLIN.DATA		F32	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		STR	nonlin.dat	# Filename for lookup table
+#NONLIN.DATA		METADATA		# Source of non-linearity data
+#	ccd00		STR	nonlin00.dat	# A lookup table 
+#	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd02		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd03		F32	1.2345 		# A polynomial
+#	@ccd04		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd05		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd06		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd07		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd08		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd09		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd11		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd12		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd13		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd14		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd15		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd16		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd17		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd18		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd19		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd21		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd22		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd23		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd24		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd25		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd26		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd27		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd28		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd29		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd30		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd31		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd32		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd33		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd34		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd35		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd36		F32	0.0 1.001 0.001	# A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.MODE		STR	INDIVIDUAL	# NONE | INDIVIDUAL | ALL
+#OVERSCAN.FIT		STR	SPLINE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	5		# Order of polynomial fit
+OVERSCAN.BIN		S32	0		# Number of pixels per bin
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/pmFPAMorph.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/pmFPAMorph.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/pmFPAMorph.c	(revision 22322)
@@ -0,0 +1,727 @@
+#include <stdio.h>
+#include <strings.h>
+#include <assert.h>
+#include "pslib.h"
+
+#include "papStuff.h"
+#include "pmFPA.h"
+#include "pmFPAMorph.h"
+
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
+
+// Flip the chip in x
+static psImage *xFlipImage(psImage *image, // Image to be flipped
+                           int size     // Size of image in flip dimension
+    )
+{
+    psImage *flipped = psImageAlloc(size, image->numRows, image->type.type); // Flipped image
+    switch (image->type.type) {
+      case PS_TYPE_U16:
+        for (int y = 0; y < image->numRows; y++) {
+            for (int x = 0; x < image->numCols; x++) {
+                flipped->data.U16[y][size - x - 1] = image->data.U16[y][x];
+            }
+        }
+        break;
+      case PS_TYPE_F32:
+        for (int y = 0; y < image->numRows; y++) {
+            for (int x = 0; x < image->numCols; x++) {
+                flipped->data.F32[y][size - x - 1] = image->data.F32[y][x];
+            }
+        }
+        break;
+      default:
+        psError(PS_ERR_IO, true, "Image type %x not yet supported for flip.\n", image->type.type);
+    }
+
+    return flipped;
+}
+
+// Flip the chip in y
+static psImage *yFlipImage(psImage *image, // Image to be flipped
+                           int size     // Size of image in flip dimension
+    )
+{
+    psImage *flipped = psImageAlloc(image->numCols, size, image->type.type); // Flipped image
+    switch (image->type.type) {
+      case PS_TYPE_U16:
+        for (int y = 0; y < image->numRows; y++) {
+            for (int x = 0; x < image->numCols; x++) {
+                flipped->data.U16[size - y - 1][x] = image->data.U16[y][x];
+            }
+        }
+        break;
+      default:
+        psError(PS_ERR_IO, true, "Image type %x not yet supported for flip.\n", image->type.type);
+    }
+
+    return flipped;
+}
+
+
+#if 0
+// Return a list of the region strings and the method for interpreting them
+static psList *getRegions(psString *method, // Method for evaluating the regions
+                          psString methodValues // The METHOD:VALUES string
+    )
+{
+    char *values = strchr(methodValues, ':'); // The VALUES
+    *method = psStringNCopy(methodValues, strlen(methodValues) - strlen(values)); // The METHOD
+    psList *regionStrings = NULL;       // List of the region strings
+    if (strncmp(*method, "HEADER", 6) == 0 || strncmp(*method, "HD", 2) == 0) {
+        regionStrings = papSplit(values, ", ");
+    } else if (strncmp(*method, "VALUE", 5) == 0 || strncmp(*method, "VAL", 3) == 0) {
+        regionStrings = papSplit(values, "; ");
+    }
+
+    return regionStrings;
+}
+#endif
+
+
+// Splice an image into another image
+static psRegion *spliceImage(psImage *target, // Target image onto which to splice
+                             int *x0, int *y0, // Starting coordinates to splice to
+                             psImage *source, // Source image from which to splice
+                             const psRegion *from, // Region to splice from
+                             int readdir, // Read direction
+                             bool xFlip, bool yFlip // Flip x and y axes?
+                        )
+{
+    psImage *toSplice = psImageSubset(source, *from); // Subimage, to splice into target
+    if (xFlip) {
+        int size = 0;           // Size of flipped image
+        if (readdir == 1) {
+            size = toSplice->numCols;
+        } else if (readdir == 2) {
+            size = target->numCols;
+        }
+        psImage *temp = xFlipImage(toSplice, size);
+        psFree(toSplice);
+        toSplice = temp;
+    }
+    if (yFlip) {
+        int size = 0;           // Size of flipped image
+        if (readdir == 1) {
+            size = target->numRows;
+        } else if (readdir == 2) {
+            size = toSplice->numRows;
+        }
+        psImage *temp = yFlipImage(toSplice, size);
+        psFree(toSplice);
+        toSplice = temp;
+    }
+
+    psTrace(__func__, 5, "Splicing %dx%d image in at %d,%d\n", toSplice->numCols, toSplice->numRows,
+            *x0, *y0);
+    (void)psImageOverlaySection(target, toSplice, *x0, *y0, "=");
+
+    if (readdir == 1) {
+        *x0 += toSplice->numCols;
+    } else if (readdir == 2) {
+        *y0 += toSplice->numRows;
+    }
+
+    // Return the region where we pasted the image
+    psRegion *outRegion = psAlloc(sizeof(psRegion));
+    // Addding 1 to get FITS standard
+    outRegion->x0 = *x0 + 1;
+    outRegion->x1 = *x0 + toSplice->numCols;
+    outRegion->y0 = *y0 + 1;
+    outRegion->y1 = *y0 + toSplice->numRows;
+
+    psFree(toSplice);
+    return outRegion;
+}
+
+// Splice bias regions (overscans)
+static bool spliceBias(psImage *splice, // Image to which to splice
+                       int *x0, int *y0, // Starting position for splice
+                       const pmCell *in, // Input cell
+                       pmCell *out,     // Output cell
+                       int xFlip, int yFlip, // Flip the image?
+                       int readNum,     // Number of readout
+                       int readdir      // Read direction
+                       )
+{
+    psArray *readouts = in->readouts; // The readouts;
+    pmReadout *readout = readouts->data[readNum]; // The readout of interest
+    psList *inBiassecs = psMetadataLookupPtr(NULL, in->concepts, "CELL.BIASSEC"); // List of input biassecs
+    psListIterator *inBiassecsIter = psListIteratorAlloc(inBiassecs, PS_LIST_HEAD, false);
+    psRegion *inBiassec = NULL; // BIASSEC from list
+    psList *outBiassecs = psListAlloc(NULL); // List of biassecs for output
+    while (inBiassec = psListGetAndIncrement(inBiassecsIter)) {
+        psRegion *outBiassec = spliceImage(splice, x0, y0, readout->image, inBiassec, readdir, xFlip,
+                                           yFlip);
+        psListAdd(outBiassecs, PS_LIST_TAIL, outBiassec);
+    }
+    psFree(inBiassecsIter);
+    psMetadataAdd(out->concepts, PS_LIST_TAIL, "CELL.BIASSEC", PS_DATA_LIST | PS_META_REPLACE,
+                  "BIASSEC from pmFPAMorph", outBiassecs);
+
+    return true;
+}
+
+psImage *p_pmImageMosaic(const psArray *source, // Images to splice in
+                         const psVector *xFlip, const psVector *yFlip, // Need to flip x and y?
+                         const psVector *xBinSource, const psVector *yBinSource, // Binning in x and y of
+                                                                                 // source images
+                         int xBinTarget, int yBinTarget, // Binning in x and y of target images
+                         const psVector *x0, const psVector *y0 // Offsets for source images on target
+                         );
+
+// Splice a list of cells together
+// We need to do the following:
+// 1. Copy the contents of the cells (i.e., the readouts) over from the in to the out
+// 2. Splice together everything in the bucket for the image that we will write out
+static psArray *spliceCells(psList *outCells, // List of target cells (required for parity info)
+                            psList *inCells, // List of cells to splice together
+                            bool posDep // Position dependent placement of overscans?
+    )
+{
+    assert(outCells);
+    assert(inCells);
+
+    psTrace(__func__, 5, "Splicing together all cells in the bucket.\n");
+
+    if (inCells->n != outCells->n) {
+        psError(PS_ERR_IO, true, "Sizes of input (%d) and output (%d) lists of cells don't match.\n",
+                inCells->n, outCells->n);
+        return NULL;
+    }
+
+    int numCells = inCells->n;          // Number of cells
+    int readdir = 0;                    // Read direction for CCD; currently unknown
+    int numReadouts = 0;                // Number of readouts in a cell
+    psArray *spliced = NULL;            // Array of spliced readouts
+    psElemType type = 0;                // Image type (U16, S32, F32, etc)
+
+    psVector *xFlip = psVectorAlloc(numCells, PS_TYPE_U8); // Do we need to flip in x?
+    psVector *yFlip = psVectorAlloc(numCells, PS_TYPE_U8); // Do we need to flip in y?
+    psVector *xBin = psVectorAlloc(numCells, PS_TYPE_S32); // Binning in x
+    psVector *yBin = psVectorAlloc(numCells, PS_TYPE_S32); // Binning in y
+    psVector *x0 = psVectorAlloc(numCells, PS_TYPE_S32); // Offset in x
+    psVector *y0 = psVectorAlloc(numCells, PS_TYPE_S32); // Offset in y
+    int xBinOut = 0;                    // Binning in x for output
+    int yBinOut = 0;                    // Binning in y for output
+
+    // We go through the cells to make sure everything's kosher, and to get the sizes.
+    psListIterator *inCellsIter = psListIteratorAlloc(inCells, PS_LIST_HEAD, false); // Iterator for cells
+    psListIterator *outCellsIter = psListIteratorAlloc(outCells, PS_LIST_HEAD, false); // Iterator for cells
+    pmCell *inCell = NULL;              // Cell from list of input cells
+    pmCell *outCell = NULL;             // Cell from list of output cells
+    int cellNum = -1;                   // Cell number
+    while (inCell = psListGetAndIncrement(inCellsIter) && outCell = psListGetAndIncrement(outCellsIter)) {
+        cellNum++;
+        psArray *inReadouts = inCell->readouts;   // The readouts comprising the cell
+
+        // Get and check the read direction
+        int cellReaddir = psMetadataLookupS32(NULL, inCell->concepts, "CELL.READDIR");
+        psTrace(__func__, 10, "Checking read direction for cell %d: %d\n", cellNum, cellReaddir);
+        if (cellNum == 0) {
+            // First run through; set it.
+            readdir = cellReaddir;
+        } else if (readdir != cellReaddir) {
+            psError(PS_ERR_IO, true, "Trying to splice cells with different read directions (%d vs %d). I "
+                    "don't know how to do that!\n", readdir, cellReaddir);
+            // XXX Clean up before returning
+            return NULL;
+        }
+
+        // Get and check the number of readouts
+        if (! spliced) {
+            numReadouts = inReadouts->n;
+            spliced = psArrayAlloc(numReadouts);
+            xSize = psVectorAlloc(numReadouts, PS_TYPE_S32);
+            ySize = psVectorAlloc(numReadouts, PS_TYPE_S32);
+            for (int i = 0; i < numReadouts; i++) {
+                xSize->data.S32[i] = 0;
+                ySize->data.S32[i] = 0;
+            }
+        } else if (inReadouts->n != numReadouts) {
+            psError(PS_ERR_IO, true, "Trying to splice cells with different number of reads (%d vs %d)\n",
+                    numReadouts, readouts->n);
+            // XXX Clean up before returning
+            return NULL;
+        }
+
+        // Get and check the binning --- we can't splice to cells that have different binnings, because
+        // the spliced image can't be defined.  Hence all the "to" cells must have the same binning.
+        xBinIn = psMetadataLookupS32(NULL, inCell->concepts, "CELL.XBIN");
+        yBinIn = psMetadataLookupS32(NULL, inCell->concepts, "CELL.YBIN");
+        int xBinCheck = psMetadataLookupS32(NULL, outCell->concepts, "CELL.XBIN");
+        int yBinCheck = psMetadataLookupS32(NULL, outCell->concepts, "CELL.YBIN");
+        if (xBinOut == 0) {
+            xBinOut = xBinCheck;
+        } else if (xBinCheck != xBinOut) {
+            psError(PS_ERR_IO, true, "Trying to splice to cells with different x binnings (%d vs %d)\n",
+                    xBinOut, xBinCheck);
+            // XXX Clean up before returning
+            return NULL;
+        }
+        if (yBinOut == 0) {
+            yBinOut = yBinCheck;
+        } else if (yBinCheck != yBinOut) {
+            psError(PS_ERR_IO, true, "Trying to splice to cells with different y binnings (%d vs %d)\n",
+                    yBinOut, yBinCheck);
+            // XXX Clean up before returning
+            return NULL;
+        }
+
+        // Copy the readouts over
+        psArray *outReadouts = psArrayAlloc(inReadouts->n); // Array of readouts for the output cell
+        for (int i = 0; i < inReadouts->n; i++) {
+            outReadouts->data[i] = psMemIncrRefCounter(inReadouts->data[i]);
+        }
+
+        // Get the offsets
+        x0->data.S32[i] = psMetadataLookupS32(NULL, inCell->concepts, "CELL.X0");
+        y0->data.S32[i] = psMetadataLookupS32(NULL, inCell->concepts, "CELL.Y0");
+
+        // Get and check the parity
+        int xParityIn = psMetadataLookupS32(NULL, inCell->concepts, "CELL.XPARITY");
+        int yParityIn = psMetadataLookupS32(NULL, inCell->concepts, "CELL.YPARITY");
+        int xParityOut = psMetadataLookupS32(NULL, outCell->concepts, "CELL.XPARITY");
+        int yParityOut = psMetadataLookupS32(NULL, outCell->concepts, "CELL.YPARITY");
+        if (xParityIn == 0 || xParityOut == 0 || yParityIn == 0 || yParityOut == 0) {
+            psError(PS_ERR_IO, false, "Unable to determine parity of cell!\n");
+            // XXX Clean up before returning
+            return NULL;
+        }
+        if (xParityIn != xParityOut) {
+            for (int i = 0; i < outReadouts->n; i++) {
+                psImage *image = outReadouts->data[i];
+                outReadouts->data[i] = xFlipImage(image);
+                psFree(image);
+            }
+            // Correct CELL.X0 for the parity shift
+            psMetadataItem *x0item = psMetadataLookup(inCell->concepts, "CELL.X0");
+            x0item->data.S32 -= xSize->data.S32[cellNum];
+        }
+        if (yParityIn != yParityOut) {
+            for (int i = 0; i < outReadouts->n; i++) {
+                psImage *image = outReadouts->data[i];
+                outReadouts->data[i] = xFlipImage(image);
+                psFree(image);
+            }
+            // Correct CELL.Y 0 for the parity shift
+            psMetadataItem *y0item = psMetadataLookup(inCell->concepts, "CELL.Y0");
+            y0item->data.S32 -= ySize->data.S32[cellNum];
+        }
+
+        //////////////////////////////////////////////////////////////////////////////////////////////////////
+        // DO I WANT TO DO THIS????
+        // Wouldn't it be better to splice all the images together first, and then divvy up the components?
+        //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+        // Manipulate the images, if required
+        for (int i = 0; i < outReadouts->n; i++) {
+            psImage *image = manipulate(outReadouts
+
+                         psImage *image = psMemIncrRefCounter(outReadouts->data[i]);
+                         if (xParityIn != xParityOut) {
+                             psImage *image = xFlipImage(image);
+                         }
+
+
+        // THIS ISN'T SO SIMPLE: we need to SET the output x0,y0 based on where the image actually gets pasted
+        // Get the offsets
+        x0->data.S32[i] = psMetadataLookupS32(NULL, outCell->concepts, "CELL.X0");
+        y0->data.S32[i] = psMetadataLookupS32(NULL, outCell->concepts, "CELL.Y0");
+
+
+
+
+// Splice a list of cells together
+static psArray *spliceCells(psList *outCells, // List of target cells (required for parity info)
+                            psList *inCells, // List of cells to splice together
+                            bool posDep // Position dependent placement of overscans?
+    )
+{
+    assert(outCells);
+    assert(inCells);
+
+    psTrace(__func__, 5, "Splicing together all cells in the bucket.\n");
+
+    if (inCells->n != outCells->n) {
+        psError(PS_ERR_IO, true, "Sizes of input (%d) and output (%d) lists of cells don't match.\n",
+                inCells->n, outCells->n);
+        return NULL;
+    }
+
+    int numCells = inCells->n;          // Number of cells
+    int readdir = 0;                    // Read direction for CCD; currently unknown
+    int numReadouts = 0;                // Number of readouts in a cell
+    psArray *spliced = NULL;            // Array of spliced readouts
+    psElemType type = 0;                // Image type (U16, S32, F32, etc)
+
+    psVector *xSize = NULL;             // Size of spliced image in x
+    psVector *ySize = NULL;             // Size of spliced image in y
+
+    psVector *xFlip = psVectorAlloc(numCells, PS_TYPE_U8); // Do we need to flip in x?
+    psVector *yFlip = psVectorAlloc(numCells, PS_TYPE_U8); // Do we need to flip in y?
+
+
+    // We go through the cells to make sure everything's kosher, and to get the sizes.
+    psListIterator *inCellsIter = psListIteratorAlloc(inCells, PS_LIST_HEAD, false); // Iterator for cells
+    psListIterator *outCellsIter = psListIteratorAlloc(outCells, PS_LIST_HEAD, false); // Iterator for cells
+    pmCell *inCell = NULL;              // Cell from list of input cells
+    int cellNum = 0;                    // Cell number;
+    while (inCell = psListGetAndIncrement(inCellsIter)) {
+        psArray *readouts = inCell->readouts;   // The readouts comprising the cell
+
+        // Get and check the read direction
+        int cellReaddir = psMetadataLookupS32(NULL, inCell->concepts, "CELL.READDIR");
+        psTrace(__func__, 10, "Checking read direction for cell %d: %d\n", cellNum, cellReaddir);
+        if (cellNum == 0) {
+            // First run through; set it.
+            readdir = cellReaddir;
+        } else if (readdir != cellReaddir) {
+            psError(PS_ERR_IO, true, "Trying to splice cells with different read directions (%d vs %d). I "
+                    "don't know how to do that!\n", readdir, cellReaddir);
+            // XXX Clean up before returning
+            return NULL;
+        }
+
+        // Get and check the number of readouts
+        if (! spliced) {
+            numReadouts = readouts->n;
+            spliced = psArrayAlloc(numReadouts);
+            xSize = psVectorAlloc(numReadouts, PS_TYPE_S32);
+            ySize = psVectorAlloc(numReadouts, PS_TYPE_S32);
+            for (int i = 0; i < numReadouts; i++) {
+                xSize->data.S32[i] = 0;
+                ySize->data.S32[i] = 0;
+            }
+        } else if (readouts->n != numReadouts) {
+            psError(PS_ERR_IO, true, "Trying to splice cells with different number of reads (%d vs %d)\n",
+                    numReadouts, readouts->n);
+            // XXX Clean up before returning
+            return NULL;
+        }
+
+        // Get and check the cell parities
+        pmCell *outCell = psListGetAndIncrement(outCellsIter);  // Corresponding output cell
+        int xParityIn = psMetadataLookupS32(NULL, inCell->concepts, "CELL.XPARITY");
+        int yParityIn = psMetadataLookupS32(NULL, inCell->concepts, "CELL.YPARITY");
+        int xParityOut = psMetadataLookupS32(NULL, outCell->concepts, "CELL.XPARITY");
+        int yParityOut = psMetadataLookupS32(NULL, outCell->concepts, "CELL.YPARITY");
+        if (xParityIn == 0 || xParityOut == 0 || yParityIn == 0 || yParityOut == 0) {
+            psError(PS_ERR_IO, false, "Unable to determine parity of cell!\n");
+            // XXX Clean up before returning
+            return NULL;
+        }
+
+        if (yParityIn == yParityOut) {
+            yFlip->data.U8[cellNum] = 0;
+        } else {
+            psTrace(__func__, 7, "Need to flip y.\n");
+            yFlip->data.U8[cellNum] = 1;
+        }
+        if (xParityIn == xParityOut) {
+            xFlip->data.U8[cellNum] = 0;
+        } else {
+            psTrace(__func__, 7, "Need to flip x.\n");
+            xFlip->data.U8[cellNum] = 1;
+        }
+        cellNum++;
+
+        // Calculate the sizes of the spliced images
+        for (int i = 0; i < numReadouts; i++) {
+            pmReadout *readout = readouts->data[i]; // The readout of interest
+            psImage *image = readout->image; // The image pixels
+
+            // Check image type
+            if (type == 0) {
+                type = image->type.type;
+            } else if (image->type.type != type) {
+                psError(PS_ERR_IO, true, "Trying to splice readouts with different image types (%d vs %d)\n",
+                        type, image->type.type);
+                // XXX Clean up before returning
+                return NULL;
+            }
+
+            psRegion *trimsec = psMetadataLookupPtr(NULL, inCell->concepts, "CELL.TRIMSEC"); // Trim section
+            psList *biassecs = psMetadataLookupPtr(NULL, inCell->concepts, "CELL.BIASSEC"); // Bias section
+            psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
+            psRegion *biassec = NULL;   // Bias section from list iteration
+
+            // Get the size of the spliced image
+            if (readdir == 1) {
+                psTrace(__func__, 7, "TRIMSEC is %.0fx%.0f\n", trimsec->x1 - trimsec->x0,
+                        trimsec->y1 - trimsec->y0);
+                xSize->data.S32[i] += trimsec->x1 - trimsec->x0;
+                ySize->data.S32[i] = MAX(ySize->data.S32[i], trimsec->y1 - trimsec->y0);
+                while (biassec = psListGetAndIncrement(biassecsIter)) {
+                    psTrace(__func__, 9, "BIASSEC is %.0fx%.0f\n", biassec->x1 - biassec->x0,
+                        biassec->y1 - biassec->y0);
+                    xSize->data.S32[i] += biassec->x1 - biassec->x0;
+                    ySize->data.S32[i] = MAX(ySize->data.S32[i], biassec->y1 - biassec->y0);
+                }
+            } else if (readdir == 2) {
+                ySize->data.S32[i] += trimsec->y1 - trimsec->y0;
+                xSize->data.S32[i] = MAX(xSize->data.S32[i], trimsec->x1 - trimsec->x0);
+                while (biassec = psListGetAndIncrement(biassecsIter)) {
+                    ySize->data.S32[i] += biassec->y1 - biassec->y0;
+                    xSize->data.S32[i] = MAX(ySize->data.S32[i], biassec->x1 - biassec->x0);
+                }
+            } else {
+                psError(PS_ERR_IO, true, "Invalid read direction: %d\n", readdir);
+                // XXX Clean up before returning
+                return NULL;
+            }
+            psFree(biassecsIter);
+        }
+    }
+    psFree(inCellsIter);
+    psFree(outCellsIter);
+
+
+    // Make sure all the readouts have the same size
+    int numRows = ySize->data.S32[0];   // Number of rows for spliced image
+    int numCols = xSize->data.S32[0];   // Number of columns for spliced image
+    for (int i = 1; i < numReadouts; i++) {
+        if (xSize->data.S32[i] != numCols || ySize->data.S32[i] != numRows) {
+            psError(PS_ERR_IO, true, "Spliced readouts would have different sizes: %dx%d vs %dx%d\n",
+                    numCols, numRows, xSize->data.S32[i], ySize->data.S32[i]);
+        }
+    }
+
+    psTrace(__func__, 5, "Spliced image has dimensions %dx%d\n", numCols, numRows);
+
+    // Now we have done the requisite checks, and know the sizes; we just go through and splice together
+    for (int i = 0; i < numReadouts; i++) {
+        psImage *splice = psImageAlloc(numCols, numRows, type); // The spliced image
+        int x0 = 0, y0 = 0;             // Position at which the splice begins
+
+        // Position-dependent overscans first
+        if (posDep) {
+            psTrace(__func__, 8, "Doing first position-dependent biases...\n");
+            for (int cellNum = 0; cellNum < inCells->n / 2; cellNum++) {
+                psTrace(__func__, 9, "Cell %d...\n", cellNum);
+                pmCell *inCell = psListGet(inCells, cellNum); // Input cell of interest
+                pmCell *outCell = psListGet(outCells, cellNum); // Output cell of interest
+                spliceBias(splice, &x0, &y0, inCell, outCell, xFlip->data.U8[i], yFlip->data.U8[i], i,
+                           readdir);
+            }
+        }
+
+        // Then the images
+        psListIterator *inCellsIter = psListIteratorAlloc(inCells, PS_LIST_HEAD, false);// Iterator for cells
+        pmCell *inCell = NULL;          // Cell from the list of cells
+        psListIterator *outCellsIter = psListIteratorAlloc(outCells, PS_LIST_HEAD, false); // Iterator
+        int cellNum = 0;                // Cell number
+        while (inCell = psListGetAndIncrement(inCellsIter)) {
+            psArray *readouts = inCell->readouts; // The readouts comprising the cell
+            pmReadout *readout = readouts->data[i]; // The specific readout of interest
+            pmCell *outCell = psListGetAndIncrement(outCellsIter); // Output cell
+            psRegion *inTrimsec = psMetadataLookupPtr(NULL, inCell->concepts, "CELL.TRIMSEC"); // TRIMSEC
+            psRegion *outTrimsec = spliceImage(splice, &x0, &y0, readout->image, inTrimsec, readdir,
+                                               xFlip->data.U8[cellNum], yFlip->data.U8[cellNum]);
+
+            // Update the output TRIMSEC
+            psMetadataAdd(outCell->concepts, PS_LIST_TAIL, "CELL.TRIMSEC", PS_DATA_UNKNOWN | PS_META_REPLACE,
+                             "TRIMSEC from pmFPAMorph", outTrimsec);
+        }
+        psFree(inCellsIter);
+        psFree(outCellsIter);
+
+        // Then the biases
+        if (posDep) {
+            // Need to only do half the biases
+            psTrace(__func__, 8, "Doing second position-dependent biases...\n");
+            for (int cellNum = inCells->n / 2; cellNum < inCells->n; cellNum++) {
+                psTrace(__func__, 9, "Cell %d...\n", cellNum);
+                pmCell *inCell = psListGet(inCells, cellNum); // Input cell of interest
+                pmCell *outCell = psListGet(outCells, cellNum); // Output cell of interest
+                spliceBias(splice, &x0, &y0, inCell, outCell, xFlip->data.U8[i], yFlip->data.U8[i], i,
+                           readdir);
+            }
+        } else {
+            // Need to do all the biases
+            for (int cellNum = 0; cellNum < inCells->n; cellNum++) {
+                pmCell *inCell = psListGet(inCells, cellNum); // Input cell of interest
+                pmCell *outCell = psListGet(outCells, cellNum); // Output cell of interest
+                spliceBias(splice, &x0, &y0, inCell, outCell, xFlip->data.U8[i], yFlip->data.U8[i], i,
+                           readdir);
+            }
+        }
+
+        spliced->data[i] = splice;
+
+    } // Iterating over readouts
+
+    return spliced;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+bool pmFPAMorph(pmFPA *toFPA,           // FPA structure to which to morph
+                pmFPA *fromFPA,         // FPA structure from which to morph
+                bool positionDependent, // Is the position of the overscan dependent on the position?
+                int chipNum,            // Chip number, in case the scopes are different
+                int cellNum             // Cell number, in case the scopes are different
+    )
+{
+    psList *targetCells = psListAlloc(NULL); // List of target cells
+    psList *sourceCells = psListAlloc(NULL); // List of source cells
+
+    psArray *toChips = toFPA->chips;    // Array of chips
+    psArray *fromChips = fromFPA->chips;// Array of chips
+
+    psTrace(__func__, 1, "Copying FPA concepts over...\n");
+    pap_psMetadataCopy(toFPA->concepts, fromFPA->concepts);
+    pap_psMetadataCopy(toFPA->phu, fromFPA->phu);
+
+    for (int i = 0; i < toChips->n; i++) {
+        pmChip *toChip = toChips->data[i];
+        pmChip *fromChip = NULL;
+        // Select the correct chip
+        if (toChips->n == 1 && fromChips->n > chipNum) {
+            fromChip = fromChips->data[chipNum];
+        } else if (fromChips->n == 1 && toChips->n > chipNum) {
+            fromChip = fromChips->data[0];
+        } else if (toChips->n == fromChips->n) {
+            fromChip = fromChips->data[i];
+        } else {
+            psError(PS_ERR_IO, true, "Unable to discern intended chip.\n");
+            return false;
+        }
+
+        if (! fromChip->valid) {
+            toChip->valid = false;
+            continue;
+        }
+
+        psTrace(__func__, 2, "Copying chip %d concepts over...\n", i);
+        pap_psMetadataCopy(toChip->concepts, fromChip->concepts);
+
+        psArray *toCells = toChip->cells; // Array of cells
+        psArray *fromCells = fromChip->cells; // Array of cells
+
+        for (int j = 0; j < toCells->n; j++) {
+            pmCell *toCell = toCells->data[j];
+            pmCell *fromCell = NULL;
+            // Select the correct cell
+            if (toCells->n == 1 && fromCells->n > cellNum) {
+                fromCell = fromCells->data[cellNum];
+            } else if (fromCells->n == 1 && toCells->n > cellNum) {
+                fromCell = fromCells->data[0];
+            } else if (toCells->n == fromCells->n) {
+                fromCell = fromCells->data[j];
+            } else {
+                psError(PS_ERR_IO, true, "Unable to discern intended cell.\n");
+                return false;
+            }
+
+            if (! fromCell->valid) {
+                toCell->valid = false;
+                continue;
+            }
+
+#ifdef TESTING
+            int xParityIn = psMetadataLookupS32(NULL, fromCell->concepts, "CELL.XPARITY");
+            int yParityIn = psMetadataLookupS32(NULL, fromCell->concepts, "CELL.YPARITY");
+            int xParityOut = psMetadataLookupS32(NULL, toCell->concepts, "CELL.XPARITY");
+            int yParityOut = psMetadataLookupS32(NULL, toCell->concepts, "CELL.YPARITY");
+            psTrace(__func__, 3, "Chip %d, cell %d: in (%d,%d) out (%d,%d)\n", i, j, xParityIn, yParityIn,
+                    xParityOut, yParityOut);
+#endif
+
+#ifdef TESTING
+            psMetadataPrint(toCell->concepts, 7);
+#endif
+
+            // Copy the concepts over
+
+            // Need to be a little tricky here --- some concepts are already set from the camera configuration
+            // file (e.g., CELL.NAME), and we want to preserve these, not replace it with the old value.
+            psTrace(__func__, 3, "Copying cell %d concepts over...\n", j);
+            psMetadata *newConcepts = pap_psMetadataCopy(NULL, fromCell->concepts);
+            pap_psMetadataCopy(newConcepts, toCell->concepts); // This preserves any already existing concepts
+            psFree(toCell->concepts);
+            toCell->concepts = newConcepts;
+
+            // Now, we need to check if the following concepts for the target cell are defined statically:
+            // CELL.XPARITY, CELL.YPARITY, CELL.XBIN, CELL.YBIN, CELL.X0, CELL.Y0
+            // 1. If they are specified by the header, we can do what we want (because we can set the header).
+            // 2. If they are not specified by the header, we use those values.
+            const char *checkConcepts[4] = { "CELL.XPARITY",
+                                             "CELL.YPARITY",
+                                             "CELL.XBIN",
+                                             "CELL.YBIN",
+                                             "CELL.X0",
+                                             "CELL.Y0" }; // Concepts that we need to check
+            bool mdok = false;          // Result of MD lookup
+            psMetadata *translation = psMetadataLookupMD(&mdok, toFPA->camera, "TRANSLATION"); // Header
+                                        // translation table
+            if (!mdok || ! translation) {
+                psError(PS_ERR_IO, true, "Unable to find TRANSLATION in camera configuration!\n");
+                return false;
+            }
+            for (int c = 0; c < 4; c++) {
+                psString headerName = psMetadataLookupString(&mdok, translation, checkConcepts[c]); // Name of
+                                        // header holding the concept, or NULL
+                if (!mdok || strlen(headerName) <= 0) {
+                    p_pmCellIngestConcept(toFPA, toChip, toCell, db, checkConcepts[c]);
+                }
+            }
+
+#ifdef TESTING
+            psMetadataPrint(toCell->concepts, 7);
+#endif
+
+            psTrace(__func__, 5, "Putting chip %d, cell %d in the bucket.\n", i, j);
+            psListAdd(targetCells, PS_LIST_TAIL, toCell);
+            psListAdd(sourceCells, PS_LIST_TAIL, fromCell);
+
+            // Copy the cell contents over
+            toCell->readouts = psMemIncrRefCounter(fromCell->readouts);
+
+            if (toCell->hdu && strlen(toCell->hdu->extname) > 0) {
+                // Splice the component cells
+                p_pmHDU *hdu = toCell->hdu;
+                if (! hdu->header) {
+                    hdu->header = psMetadataAlloc();
+                }
+                hdu->images = spliceCells(targetCells, sourceCells, positionDependent);
+                // Purge the lists
+                while (psListRemove(targetCells, PS_LIST_TAIL));
+                while (psListRemove(sourceCells, PS_LIST_TAIL));
+            }
+
+        }
+
+        if (toChip->hdu && strlen(toChip->hdu->extname) > 0) {
+            // Splice the component cells
+            p_pmHDU *hdu = toChip->hdu;
+            if (! hdu->header) {
+                hdu->header = psMetadataAlloc();
+            }
+            hdu->images = spliceCells(targetCells, sourceCells, positionDependent);
+            // Purge the lists
+            while (psListRemove(targetCells, PS_LIST_TAIL));
+            while (psListRemove(sourceCells, PS_LIST_TAIL));
+        }
+
+    }
+
+    if (toFPA->hdu && strlen(toFPA->hdu->extname) > 0) {
+        // Splice the component cells
+        p_pmHDU *hdu = toFPA->hdu;
+        if (! hdu->header) {
+            hdu->header = psMetadataAlloc();
+        }
+        hdu->images = spliceCells(targetCells, sourceCells, positionDependent);
+        // Purge the lists
+        while (psListRemove(targetCells, PS_LIST_TAIL));
+        while (psListRemove(sourceCells, PS_LIST_TAIL));
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/pmFPAMorph.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/pmFPAMorph.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/pmFPAMorph.h	(revision 22322)
@@ -0,0 +1,13 @@
+#ifndef PM_FPA_MORPH_H
+#define PM_FPA_MORPH_H
+
+// Morph one focal plane representation into another
+bool pmFPAMorph(pmFPA *toFPA,		// FPA structure to which to morph
+		pmFPA *fromFPA,		// FPA structure from which to morph
+		bool positionDependent, // Is the position of the overscan dependent on the position?
+		int chipNum,		// Chip number, in case the scopes are different
+		int cellNum		// Cell number, in case the scopes are different
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/psAdditionals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/psAdditionals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/psAdditionals.c	(revision 22322)
@@ -0,0 +1,567 @@
+#include <stdio.h>
+#include <strings.h>
+#include "pslib.h"
+#include "psAdditionals.h"
+
+
+psMetadata *pap_psMetadataCopy(psMetadata *out,
+			       const psMetadata *in)
+{
+    PS_ASSERT_PTR_NON_NULL(in,NULL);
+    if (out ==  NULL) {
+        out = psMetadataAlloc();
+    }
+    psMetadataItem *inItem = NULL;
+    psMetadataIterator *iter = psMetadataIteratorAlloc(*(psMetadata**)&in, PS_LIST_HEAD, NULL);
+    unsigned long numPointers = 0;	// Number of pointers we were forced to copy
+    while (inItem = psMetadataGetAndIncrement(iter)) {
+	psMetadataItem *outItem = NULL;
+
+	// Need to look for MULTI, which won't be picked up using the iterator.
+	psMetadataItem *multiCheckItem = psMetadataLookup(in, inItem->name);
+	unsigned int flag = PS_META_REPLACE; // Flag to indicate MULTI; otherwise, replace
+	if (multiCheckItem->type == PS_DATA_METADATA_MULTI) {
+	    psTrace(__func__, 10, "MULTI: %s (%s)\n", inItem->name, inItem->comment);
+	    flag = PS_DATA_METADATA_MULTI;
+	}
+
+	psTrace(__func__, 5, "Copying %s (%s)...\n", inItem->name, inItem->comment);
+
+#define PS_METADATA_COPY_CASE(NAME,TYPE) \
+          case PS_TYPE_##NAME: \
+            if (! psMetadataAdd(out, PS_LIST_TAIL, inItem->name, PS_TYPE_##NAME | flag, inItem->comment, \
+			        inItem->data.TYPE)) { \
+	        psErrorStackPrint(stderr, "Error copying %s (%s) in the metadata\n", inItem->name, \
+                                  inItem->comment); \
+            } \
+            break;
+
+	switch (inItem->type) {
+	    // Numerical types
+	    PS_METADATA_COPY_CASE(BOOL,B);
+	    PS_METADATA_COPY_CASE(S8,S8);
+	    PS_METADATA_COPY_CASE(S16,S16);
+	    PS_METADATA_COPY_CASE(S32,S32);
+	    PS_METADATA_COPY_CASE(U8,U8);
+	    PS_METADATA_COPY_CASE(U16,U16);
+	    PS_METADATA_COPY_CASE(U32,U32);
+	    PS_METADATA_COPY_CASE(F32,F32);
+	    PS_METADATA_COPY_CASE(F64,F64);
+
+	    // String: relying on the fact that this will copy the string, not point at it.
+	  case PS_DATA_STRING:
+            psMetadataAdd(out, PS_LIST_TAIL, inItem->name, PS_DATA_STRING | flag, inItem->comment,
+			  inItem->data.V);
+            break;
+
+	    // Metadata: copy the next level and stuff that in too
+	  case PS_DATA_METADATA:
+	    {
+		psMetadata *metadata = pap_psMetadataCopy(NULL, inItem->data.md);
+		psMetadataAdd(out, PS_LIST_TAIL, inItem->name, PS_DATA_METADATA | flag, inItem->comment,
+			      metadata);
+		break;
+	    }
+	    // Other kinds of pointers
+	  default:
+	    numPointers++;
+	    psTrace(__func__, 10, "Copying a pointer in the metadata: %x\n", inItem->type);
+	    psMetadataAdd(out, PS_LIST_TAIL, inItem->name, inItem->type | flag, inItem->comment,
+			  inItem->data.V);
+	    break;
+	}
+    }
+    psFree(iter);
+
+    if (numPointers > 0) {
+	psLogMsg(__func__, PS_LOG_WARN, "Forced to copy %d pointers when copying metadata.  Updating the "
+		 "copied psMetadata will affect the original!\n", numPointers);
+    }
+
+    return out;
+}
+
+
+psMetadata *psMetadataLookupMD(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    psMetadata *value = NULL;		// The value to return
+    if (!item) {
+	// The given key isn't in the metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+	}
+    } else if (item->type != PS_DATA_METADATA) {
+	// The value at the key isn't metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_DATA_METADATA, as expected.\n");
+	}
+	value = NULL;
+    } else {
+	// We have the requested metadata
+	if (status) {
+	    *status = true;
+	}
+	value = item->data.md; // The requested metadata
+    }
+    return value;
+}
+
+
+char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    char *value = NULL;			// The value to return
+    if (!item) {
+	// The given key isn't in the metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+	}
+    } else if (item->type != PS_DATA_STRING) {
+	// The value at the key isn't of the desired type
+	if (status) {
+	    *status = false;
+	} else {
+	    psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_DATA_STRING, as expected.\n");
+	}
+	value = NULL;
+    } else {
+	// We have the requested metadata
+	if (status) {
+	    *status = true;
+	}
+	value = item->data.V; // The requested metadata
+    }
+    return value;
+}
+
+
+void psMetadataPrint(psMetadata *md, int level)
+{
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);	// Iterator
+    psMetadataItem *item = NULL;	// Item from metadata
+    while (item = psMetadataGetAndIncrement(iter)) {
+	// Indent...
+	for (int i = 0; i < level; i++) {
+	    printf(" ");
+	}
+	printf("%s", item->name);
+	if (item->comment && strlen(item->comment) > 0) {
+	    printf(" (%s)", item->comment);
+	}
+	printf(": ");
+	switch (item->type) {
+	  case PS_DATA_STRING:
+	    printf("%s", item->data.V);
+	    break;
+	  case PS_DATA_BOOL:
+	    if (item->data.B) {
+		printf("True");
+	    } else {
+		printf("False");
+	    }
+	    break;
+	  case PS_DATA_S32:
+	    printf("%d", item->data.S32);
+	    break;
+	  case PS_DATA_F32:
+	    printf("%f", item->data.F32);
+	    break;
+	  case PS_DATA_F64:
+	    printf("%f", item->data.F64);
+	    break;
+	  case PS_DATA_METADATA:
+	    printf("\n");
+	    psMetadataPrint(item->data.V, level + 1);
+	    break;
+	  default:
+	    printf("\n");
+	    psError(PS_ERR_IO, false, "Non-printable metadata type: %x\n", item->type);
+	}
+	printf("\n");
+    }
+    psFree(iter);
+
+    return;
+}
+
+
+// Set verbosity level
+int psArgumentVerbosity(int *argc, char **argv)
+{
+    int logLevel = 2;			// Default log level
+    int argnum = 0;			// Argument number
+
+    // set in order, so that -vvv overrides -vv overrides -v
+    if (argnum = psArgumentGet(*argc, argv, "-v")) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 3;
+    }
+    if (argnum = psArgumentGet(*argc, argv, "-vv")) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 4;
+    }
+    if (argnum = psArgumentGet(*argc, argv, "-vvv")) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 5;
+    }
+    psLogSetLevel (logLevel);		// XXX: This function should return an error if the log level is invalid
+
+    if (argnum = psArgumentGet(*argc, argv, "-logfmt")) {
+        if (*argc < argnum + 2) {
+            psError(PS_ERR_IO, true, "-logfmt switch specified without a format.");
+        } else {
+	    psArgumentRemove(argnum, argc, argv);
+	    psLogSetFormat(argv[argnum]); // XXX EAM : this function should return an error if the log format is invalid
+	    psArgumentRemove(argnum, argc, argv);
+	}
+    }
+
+    // Now the trace stuff
+    // argument format is: -trace (facil) (level)
+    while (argnum = psArgumentGet(*argc, argv, "-trace")) {
+        if (*argc < argnum + 3) {
+            psError(PS_ERR_IO, true, "-trace switch specified without facility and level.");
+        }
+        psArgumentRemove(argnum, argc, argv);
+        psTraceSetLevel(argv[argnum], atoi(argv[argnum+1])); // XXX: This function should return an error if the trace level is invalid
+        psArgumentRemove(argnum, argc, argv);
+        psArgumentRemove(argnum, argc, argv);
+    }
+    if ((argnum = psArgumentGet(*argc, argv, "-trace-levels"))) {
+        psTracePrintLevels();
+        exit(2);
+    }
+
+    return logLevel;
+}
+ 
+// Find the location of the specified argument
+int psArgumentGet(int argc, char **argv, const char *arg)
+{
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], arg))
+            return i;
+    }
+   
+    return 0;
+}
+
+// Remove the specified argument (by location)
+bool psArgumentRemove(int argnum, int *argc, char **argv)
+{
+    if (argnum > 0) {
+        (*argc)--;
+        for (int i = argnum; i < *argc; i++) {
+            argv[i] = argv[i+1];
+        }
+    } else {
+	return false;
+    }
+    
+    return true;
+}
+
+
+static psMetadataItem *argumentRead(psMetadataItem *item, // Item to read into
+				    int argnum,	// Argument number
+				    int *argc,	// Number of arguments in total
+				    char **argv	// The arguments
+				    )
+{
+    psMetadataItem *newItem = NULL;
+    switch(item->type) {
+	// Only doing a representative set of types
+      case PS_DATA_S32:
+	newItem = psMetadataItemAlloc(item->name, item->type, item->comment, atoi(argv[argnum]));
+	psArgumentRemove(argnum, argc, argv);
+	break;
+      case PS_DATA_F32:
+	newItem = psMetadataItemAlloc(item->name, item->type, item->comment, atof(argv[argnum]));
+	psArgumentRemove(argnum, argc, argv);
+	break;
+      case PS_DATA_BOOL:
+	// Turn option on; no optional argument to remove
+	newItem = psMetadataItemAlloc(item->name, item->type, item->comment, true);
+	break;
+	// XXX: Include the other numerical types
+      case PS_DATA_STRING:
+	{
+	    //psString string = psStringCopy(argv[argnum]);	// Get the argument into PS memory management
+	    //psFree(string);
+	    newItem = psMetadataItemAlloc(item->name, item->type, item->comment, argv[argnum]);
+	    psArgumentRemove(argnum, argc, argv);
+	}
+	break;
+      default:
+	psError(PS_ERR_IO, true, "Argument type (%x) is not supported --- argument %s ignored\n",
+		item->type, item->name);
+	psFree(newItem);
+	return NULL;
+    }
+
+    return newItem;
+}
+
+
+// XXX: There is a memory leak in the MULTI section.  I think it might have something to do with reference
+// counting between lists and MD, in the second section of the code (copy newArgs into arguments), but I'm not
+// entirely sure.
+bool psArgumentParse(psMetadata *arguments, int *argc, char **argv)
+{
+    // We need to do a bit of mucking around in order to preserve the arguments metadata until the last
+    // minute --- if there is a bad argument, we need to return the old "arguments", since they contain
+    // the default values, which we probably want to output in a "help" message (we don't want to print
+    // the changed values and have the user think that they are default values).
+
+    psMetadata *newArgs = psMetadataAlloc(); // Place to read arguments into
+    psList *changed = psListAlloc(NULL);// List of keys that have changed
+
+    for (int i = 1; i < *argc; i++) {
+	psTrace(__func__, 7, "Looking at %s\n", argv[i]);
+	psMetadataItem *argItem = psMetadataLookup(arguments, argv[i]);
+	if (argItem) {
+	    psArgumentRemove(i, argc, argv); // Remove the switch
+	    if (argItem->type != PS_DATA_METADATA_MULTI) {
+		if (argItem->type != PS_DATA_BOOL && *argc < i + 1) {
+		    psError(PS_ERR_IO, true, "Required argument for %s is missing.\n", argItem->name);
+		    // XXX: Cleanup before returning
+		    psFree(newArgs);
+		    return false;
+		}
+		psMetadataItem *newItem = argumentRead(argItem, i, argc, argv);
+		psMetadataAddItem(newArgs, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+		psFree(newItem);
+	    } else {
+		// Go through the MULTI
+		psList *multi = argItem->data.V; // The list of MULTI psMetadataItems
+		if (*argc < i + multi->n) {
+		    psError(PS_ERR_IO, true, "Not enough arguments for %s.\n", argItem->name);
+		    // Remove the arguments --- they will be ignored
+		    for (int j = i; i < *argc; i++) {
+			psArgumentRemove(i, argc, argv);
+		    }
+		    // XXX: Cleanup before returning
+		    psFree(newArgs);
+		    return false;
+		}
+
+		// Remove any prior existence in the newArgs --- this is important because we specify
+		// adding the new items as DUPLICATE_OK, so if some idiot specifies it twice, we'd end
+		// up with two copies of everything.
+		psMetadataItem *checkItem = psMetadataLookup(newArgs, argItem->name);
+		if (checkItem) {
+		    (void)psMetadataRemove(newArgs, 0, argItem->name);
+		    (void)psListRemoveData(changed, argItem->name);
+		}
+
+		psListIterator *multiIter = psListIteratorAlloc(multi, PS_LIST_HEAD, true);
+		psMetadataItem *nextItem = NULL; // Item from list
+		while (nextItem = psListGetAndIncrement(multiIter)) {
+		    psMetadataItem *newItem = argumentRead(nextItem, i, argc, argv);
+		    psMetadataAddItem(newArgs, newItem, PS_LIST_TAIL, PS_META_DUPLICATE_OK);
+		    //psFree(newItem);
+		}
+		psFree(multiIter);
+	    }
+
+	    // Some book-keeping
+	    //	    psString name = psStringCopy(argItem->name);
+	    psListAdd(changed, PS_LIST_TAIL, argItem->name);
+	    i--;
+
+	} else if (strncmp(argv[i], "-", 1) == 0 || strncmp(argv[i], "+", 1) == 0) {
+	    // Someone's specified a bad option
+	    psError(PS_ERR_IO, true, "Unknown option: %s\n", argv[i]);
+	    psFree(newArgs);
+	    return false;
+	}
+    }
+
+    // All the arguments are good, so now we can copy the newArgs over
+    psListIterator *changedIter = psListIteratorAlloc(changed, PS_LIST_HEAD, false); // Iterator
+    psString name = NULL;		// Item from iteration
+    while (name = psListGetAndIncrement(changedIter)) {
+	printf("Updating %s\n", name);
+	psMetadataItem *oldItem = psMetadataLookup(arguments, name);
+	psMetadataItem *newItem = psMetadataLookup(newArgs, name);
+	if (oldItem->type != newItem->type) {
+	    psAbort(__func__, "Shouldn't reach here!\n");
+	}
+	switch (oldItem->type) {
+	    // Only doing a representative set of types
+	  case PS_DATA_S32:
+	    oldItem->data.S32 = newItem->data.S32;
+	    break;
+	  case PS_DATA_F32:
+	    oldItem->data.F32 = newItem->data.F32;
+	    break;
+	  case PS_DATA_BOOL:
+	    oldItem->data.B = newItem->data.B;
+	    break;
+	    // XXX: Include the other numerical types
+	  case PS_DATA_STRING:
+	    psFree(oldItem->data.V);
+	    oldItem->data.V = psMemIncrRefCounter(newItem->data.V);
+	    break;
+	  case PS_DATA_METADATA_MULTI:
+	    {
+		psList *newMulti = psMemIncrRefCounter(newItem->data.V);	// The new list of MULTI
+		psList *oldMulti = oldItem->data.V;	// The old list of MULTI
+		psListIterator *newMultiIter = psListIteratorAlloc(newMulti, PS_LIST_HEAD, false);
+		psListIterator *oldMultiIter = psListIteratorAlloc(oldMulti, PS_LIST_HEAD, true);
+		psMetadataItem *newMultiItem = NULL; // Item from iterator
+		while (newMultiItem = psListGetAndIncrement(newMultiIter)) {
+		    psMetadataItem *oldMultiItem = psListGetAndIncrement(oldMultiIter);
+		    if (! oldMultiItem) {
+			psAbort(__func__, 
+				"Something went very wrong here!  The lists SHOULD be of the same length!\n");
+		    }
+		    switch (oldMultiItem->type) {
+			// Only doing a representative set of types
+		      case PS_DATA_S32:
+			oldItem->data.S32 = newItem->data.S32;
+			break;
+		      case PS_DATA_F32:
+			oldItem->data.F32 = newItem->data.F32;
+			break;
+			// XXX: Include the other numerical types
+		      case PS_DATA_STRING:
+			psFree(oldItem->data.V);
+			oldItem->data.V = psMemIncrRefCounter(newItem->data.V);
+			break;
+		      default:
+			psAbort(__func__, "Should never ever get here, ever.\n");
+		    }
+		    psFree(oldMultiItem);
+		    psFree(newMultiItem);
+		}
+		psFree(newMultiIter);
+		psFree(oldMultiIter);
+	    }
+	    break;
+	  default:
+	    psAbort(__func__, "Should never ever ever get here.\n");
+	}
+    }
+    psFree(changedIter);
+    psFree(changed);
+
+    // Now, blow away the newArgs and we're done.
+    psFree(newArgs);
+    return true;
+}
+
+
+static int argLength(psMetadataItem *arg)
+{
+    switch (arg->type) {
+	// Only doing a representative set of types
+      case PS_DATA_S32:
+	return arg->data.S32 >= 0 ? (int)log10f((float)arg->data.S32) + 1 :
+	    (int)log10f(-(float)arg->data.S32) + 2;
+	// XXX: Other numerical types
+      case PS_DATA_F32:
+	return arg->data.F32 >= 0 ? 12 : 13; // -d.dddddde?dd
+      case PS_DATA_F64:
+	return arg->data.F64 >= 0 ? 12 : 13; // -d.dddddde?dd
+      case PS_DATA_BOOL:
+	return arg->data.B ? 4 : 5;
+      case PS_DATA_STRING:
+	return strlen(arg->data.V);
+      default:
+	psAbort(__func__, "Argument type (%x) is not supported.\n", arg->type);
+    }
+ 
+    return 0;
+}
+
+#define NUM_SPACES 4			// Number of spaces between
+
+void psArgumentHelp(psMetadata *arguments)
+{
+    printf("Optional arguments, with default values:\n");
+    psMetadataIterator *argIter = psMetadataIteratorAlloc(arguments, PS_LIST_HEAD, NULL);
+    psMetadataItem *argItem = NULL;	// Item from iterator
+    int maxName = 4;			// Maximum length of a name
+    int maxValue = 4;			// Maximum length of a value
+
+    // First pass to get the sizes
+    while (argItem = psMetadataGetAndIncrement(argIter)) {
+	if (strlen(argItem->name) > maxName) {
+	    maxName = strlen(argItem->name);
+	}
+	int valLength = argLength(argItem);
+	if (valLength > maxValue) {
+	    maxValue = valLength;
+	}
+    }
+
+    // Second pass to print
+    psMetadataIteratorSet(argIter, PS_LIST_HEAD);
+    psString lastName = NULL;		// Last name we printed
+    while (argItem = psMetadataGetAndIncrement(argIter)) {
+	// Initial indent
+	for (int i = 0; i < NUM_SPACES; i++) {
+	    printf(" ");
+	}
+
+	// Print the name if required
+	int position = 0;	// Number of spaces in
+	if (! lastName || strcmp(lastName, argItem->name) != 0) {
+	    // A new name
+	    printf("%s", argItem->name);
+	    position += strlen(argItem->name);
+	    lastName = argItem->name;
+	}
+	for (int i = position; i < maxName + NUM_SPACES; i++) {
+	    printf(" ");
+	}
+
+	// Print the value
+	printf("(");
+	switch (argItem->type) {
+	    // Only doing a representative set of types
+	  case PS_DATA_S32:
+	    printf("%d", argItem->data.S32);
+	    break;
+	    // XXX: Other numerical types
+	  case PS_DATA_F32:
+	    printf("%.6e", argItem->data.F32);
+	    break;
+	  case PS_DATA_F64:
+	    printf("%.6e", argItem->data.F64);
+	    break;
+	  case PS_DATA_BOOL:
+	    if (argItem->data.B) {
+		printf("TRUE");
+	    } else {
+		printf("FALSE");
+	    }
+	    break;
+	  case PS_DATA_STRING:
+	    printf("%s", argItem->data.V);
+	    break;
+	  default:
+	    psAbort(__func__, "Argument type (%x) is not supported.\n", argItem->type);
+	}
+	printf(")");
+	for (int i = argLength(argItem); i < maxValue + NUM_SPACES; i++) {
+	    printf(" ");
+	}
+
+	// Print the comment
+	if (argItem->comment) {
+	    printf("%s", argItem->comment);
+	}
+	printf("\n");
+    }
+
+    psFree(argIter);
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/psAdditionals.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/psAdditionals.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/psAdditionals.h	(revision 22322)
@@ -0,0 +1,30 @@
+#ifndef PS_ADDITIONALS_H
+#define PS_ADDITIONALS_H
+
+#include "pslib.h"
+
+// Deep copy of metadata
+psMetadata *pap_psMetadataCopy(psMetadata *out, const psMetadata *in);
+
+// Get a value from the metadata that we believe should be metadata.
+psMetadata *psMetadataLookupMD(bool *status, const psMetadata *md, const char *key);
+
+// Get a value from the metadata that we believe should be a string
+char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key);
+
+#if 0
+pmChip *psMetadataLookupChip(bool *status, const psMetadata *md, const char *key);
+pmCell *psMetadataLookupCell(bool *status, const psMetadata *md, const char *key);
+#endif
+
+// Print out the metadata
+void psMetadataPrint(psMetadata *md, int level);
+
+// Argument handling
+int psArgumentVerbosity(int *argc, char **argv);
+int psArgumentGet(int argc, char **argv, const char *arg);
+bool psArgumentRemove(int argnum, int *argc, char **argv);
+bool psArgumentParse(psMetadata *arguments, int *argc, char **argv);
+void psArgumentHelp(psMetadata *arguments);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/readouts.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/readouts.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/readouts.txt	(revision 22322)
@@ -0,0 +1,41 @@
+Behaviour of readouts:
+
+
+typedef struct {
+	const char *extname;
+	psMetadata *header;
+	psArray *images;
+	psArray *masks;
+	psArray *weights;
+} pmPixelData;
+
+typedef struct {
+	...
+	psMetadata *concepts;
+	pmCell *parent;
+	psImage *image;
+	psImage *mask;
+	psImage *weight;
+} pmReadout;
+
+
+Procedures:
+
+a. Read: set pmReadout->image = pmPixelData->images->data[i],
+   pmReadout->mask = pmPixelData->masks->data[i], pmReadout->weight =
+   pmPixelData->weights->data[i]
+
+b. Mask bad pixels: Copy the provided mask into the input image mask.
+
+c. Bias/overscan subtraction: Read the CELL.BIASSEC, and apply to the
+   CELL.TRIMSEC.
+
+d. Trim: Cut the CELL.TRIMSEC, and paste into pmReadout->image,
+   pmReadout->mask, pmReadout->weight.
+
+e. Flat-fielding: Pretty straight-forward.  The FF just needs to be of
+   the same format as the input image.
+
+f. Splice: If images are untrimmed, then have to pluck out the
+   CELL.TRIMSEC and CELL.BIASSEC, and need to worry about the overscan
+   regions.
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/testing.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/testing.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase2/testing.txt	(revision 22322)
@@ -0,0 +1,65 @@
+Overscans:
+
+Modes: NONE | INDIVIDUAL | ALL
+
+* NONE produces warnings that there is no overscan subtraction:
+  "Proceeding to full fram subtraction."  This is probably
+  unnecessary.  The second warning is "bias frame is NULL.  Returning
+  original image."  This is in the spec --- good.
+
+* INDIVIDUAL works well.  The exponential ramp at the bottom of the
+  megacam image is removed. It leaves horizontal streaks, but I've
+  seen these before, and I figure they're part of the instrument, not
+  our software.
+
+* ALL also works.  The bias is removed, but the ramp remains, which is
+  exactly what I expected.
+
+Bin: An integer.  Using INDIVIDUAL mode.
+
+* 1 removes the ramp
+* 0 removes the ramp
+* Large values (50,100) have the effect of smoothing out the
+  horizontal streaks.
+
+Stat: MEAN | MEDIAN
+
+* Do a similar job (not identical).  Slightly less horizontal
+  structure with median.
+
+Fit: NONE | POLYNOMIAL | SPLINE
+
+* NONE is fine.
+
+* POLYNOMIAL produces a SEGV when I try to print out the resultant
+  polynomial.  I realise I haven't told it the polynomial order
+  (should create a psPolynomial1D to feed in to specify the order),
+  but I would have expected an error message.  Fixing this up, the
+  polynomial fitting works fairly well.
+
+* SPLINE doesn't seem to work --- it won't give back a spline for me
+  to print out the results of the fit.  Filed a bug.
+
+
+Flat: it works, but I'm concerned it's doing the entire image area,
+instead of just the appropriate area (CELL.TRIMSEC).  I added a trim
+to the phase 2, and it runs faster now.  The program still outputs the
+overscan, beacause it writes the pixels it read it.  The trim only
+affects where the computations are done.  Flat-fielding the image by
+itself (w/o overscan) results in ones everywhere, except the overscan.
+ 
+Full-frame bias subtraction: Works, but it acts on the entire image.
+Fixed the definition so that this is only done on the region specified
+by CELL.TRIMSEC.  That way, the overscan of the input image isn't
+affected by the full-frame subtraction.
+
+Non-linearity: This is really slow, or there's something really weird
+going on.  It used a heap of memory.  OK, found the problem: I had an
+extraneous free that caused corruption of the number of elements in
+the vector of coefficients.  Again, it wants to act on the entire
+image, so adding to the spec that it should only act on the
+CELL.TRIMSEC (assuming the non-linearity is from the CCD itself, not
+the amplifier).  It's fairly slow, but that might be because of the
+multiple trace messages in the polynomial evaluation.  Got the
+individual correction parameters working.  Now need to do the lookup
+table.
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase4.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase4.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/phase4.c	(revision 22322)
@@ -0,0 +1,212 @@
+#include <stdio.h>
+#include <pslib.h>
+
+// Configuration for Phase 4
+typedef struct {
+    // Input parameters
+    int nInputs;			// Number of input files
+    const char *camera;			// Name of the camera configuration file
+    const psList *inNames;		// Input file names
+    unsigned long skyId;		// Sky tangent plane identification number
+    // Output parameters
+    const char *combined;		// Combined image filename
+    const char *subtracted;		// Subtracted image filename
+    const char *variables;		// Output variable candidates filename
+    const char *recipeFile;		// Recipe filename
+} phase4Config;
+
+
+// A tangent plane (for combined image and static sky)
+// Assume we're dealing with a rectangle only for now.
+typedef struct {
+    long int id;			// Identification number --- key in MDDB
+    double ra0, dec0;			// Tangent point --- from MDDB
+    double sizeRA, sizeDec;		// Size in RA and Dec --- from MDDB
+    double scale;			// Pixel scale --- from MDDB
+    char *name;				// File name --- from MDDB or image server
+    psImage *pixels;			// The pixels that comprise the tangent plane
+    psImage *mask;			// Mask image
+    psSomeCollectionOfObjects *objects;	// Objects in the image
+} psTangentPlane;
+
+void main (int argc, char **argv)
+{
+    // Bury the command line parsing into some function that just works
+    phase4Config *config = parseCommandLine(argc, argv); // Parse the command line
+
+    // Read the recipe metadata
+    psMetadata *recipe = psMetadataParseConfig(NULL, config->recipeFile, false);
+
+    // Don't particularly care about the camera and site details --- Phase 4 doesn't care where
+    // the data comes from, but it does need the following for each image:
+    // * Gain and read noise for noise model --- from the camera configuration
+    // * Background value for noise model (assume constant over the region of interest) --- from image MD
+    // * Photometric calibration for relative and absolute scalings --- from image MD
+    // * Astrometric calibration to map to the sky --- from file specified in image MD
+    // We need to be told which camera; assume all data is from the same camera (easy to generalise
+    // if this is not true).
+
+    // Read the camera configuration
+    psMetadata *camera = psMetadataParseConfig(NULL, config->camera, false);
+    // What are the keywords for gain, readnoise, photometric calibration, background?
+    // This basically treats the "camera" configuration as a translation table.
+    const char *gainHeader = psMetadataLookup(camera, "GAIN");
+    const char *readnoiseHeader = psMetadataLookup(camera, "READNOISE");
+    const char *photcalHeader = psMetadataLookup(camera, "PHOTCAL");
+    const char *backgroundHeader = psMetadataLookup(camera, "BACKGRND");
+    const char *astromHeader = psMetadataLookup(camera, "ASTROM");
+
+    // Initialise the various arrays/vectors we need
+    psArray *headers = psArrayAlloc(config->nInputs); // The image headers
+    psArray *inputs = psArrayAlloc(config->nInputs); // The images
+    psArray *astrometry = psArrayAlloc(config->nInputs); // The astrometry data for each image
+    psVector *gain = psVectorAlloc(config->nInputs, PS_TYPE_F32); // Gains
+    psVector *readnoise = psVectorAlloc(config->nInputs, PS_TYPE_F32); // Read noises
+    psVector *photcal = psVectorAlloc(config->nInputs, PS_TYPE_F32); // Photometric calibrations
+    psVector *background = psVectorAlloc(config->nInputs, PS_TYPE_F32); // Background flux values
+
+    // This function queries the MDDB to get sizeRA,sizeDec,ra0,dec0,scale,name for the tangent plane of
+    // interest
+    psTangentPlane *staticSky = psTangentPlaneFromDB(config->sky);
+
+    // Read in each of the inputs
+    for (psListSetIterator(config->inNames, PS_LIST_HEAD), // Set the iterator and get the first one
+	     char *inName = psListGetNext(input),
+	     int i = 0;
+	 inName != NULL;		// Keep going until nothing left in the list
+	 inName = psListGetNext(input),	// Get the next one on the list
+	     i++) {
+
+	// Read the header
+	psMetadata *header = psFITSReadHeader(inName);
+	// Put it in the array of headers for future reference
+	headers->data[i] = header;
+
+	// Check the validity of the given header based on expectactions for this camera
+	status = pmCameraValidateHeaders(headers, camera);
+	// Need to act if status is bad --- generate a warning and ignore this image
+
+	// Get the gain, readnoise, photometric calibration, background
+	// I think this is not strictly correct --- doesn't psMetadataLookup return a void* ?
+	gain->data.F32[i] = (psF32) psMetadataLookup(header, gainHeader);
+	readnoise->data.F32[i] = (psF32) psMetadataLookup(header, readnoiseHeader);
+	photcal->data.F32[i] = (psF32) psMetadataLookup(header, photcalHeader);
+	background->data.F32[i] = (psF32) psMetadataLookup(header, backgroundHeader);
+
+	// Get the name of the astrometry file (XML) and generate the FPA structure from it (no pixels)
+	const char *astromFile = psMetadataLookup(header, astromHeader);
+	psFPA *fpa = psFPAFromAstrometry(astromFile);
+	astrometry->data[i] = fpa;
+
+	// Iterate over the FPA structure
+	for (int chipnum = 0, int nChips = fpa->chips->n; chipnum < nChips; chipnum++) {
+	    psChip *chip = fpa->chips->data[chipnum]; // The chip of interest
+	    for (int cellnum = 0, int nCells = chip->cells->n; cellnum < nCells; cellnum++) {
+		psCell *cell = chip->cells->data[cellnum]; // The cell of interest
+
+		// QUESTION: Do we want to bother about the cells that were used for fast guiding?
+		// Assume that we care --- may as well.
+		
+		// QUESTION: Are multiple readouts stacked in phase 4 or earlier?
+		// Assume that they should be stacked here.
+
+		// QUESTION: How do we know the names of the chip, cell, readout in the FITS file?
+		// Assume that each of psFPA, psChip, psCell, psReadout contains a "const char *source"
+		// which contains some sort of URI, e.g.:
+		// FPA: fpa12345_12.fits --- This particular FPA contains only a single OTA
+		// Chip: fpa12345_12.fits --- The OTA is entirely contained in the FITS file
+		// Cell: fpa12345_12.fits|cell34 --- The cell has extension name "cell34"
+		// Readout: fpa12345_12.fits|cell34|56 --- The readout is the 56th slice in the 3rd axis
+		// Assume further that, given the "source", there exists a module that loads the pixels.
+
+
+		// If there are multiple readouts, then stack them
+		if (cell->readouts->n > 1) {
+		    psList *readouts = psListAlloc(NULL); // List of readouts for stacking
+
+		    // Read the images in
+		    for (int readoutNum = 0; readoutNum < cell->readouts->n; readoutNum++) {
+			(void)psReadoutRead(cell->readouts->data[readoutNum]); // Read the readout into memory
+			psListAdd(readouts, PS_LIST_TAIL, cell->readouts->data[readoutNum]); // Put on list
+		    }
+
+		    // QUESTION: Do we want to bother about combining these "properly", or just sum them?
+		    // Assume that we just sum them, which saves measuring the zeros and scales.  CR rejection
+		    // can be done when we combine multiple images (consecutive or from multiple telescopes).
+
+		    // Perhaps we want a pmReadoutStack module, which would convert an array of readouts from
+		    // fast guiding to a single pseudo-readout.
+
+		    // Just do a simple mean
+		    psStats *combineStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // Statistics for combination
+		    pmCombineParams *combine = pmCombineParamsAlloc(combineStats, 0, 0.0, 0.0, 0); // How to
+												   // combine
+		    psImage *stack = pmReadoutCombine(NULL, readouts, combine, 0.0, 1.0, false, 1.0, 0.0);
+		    // And now multiply by the number to get the sum
+		    (void)psImageBinaryOp(stack, stack, "*", psScalarAlloc(cell->readouts->n, PS_TYPE_U16));
+
+		    // Probably also have to multiply by some exposure time ratio to scale with the rest of
+		    // the focal plane --- neglected here.
+
+		    // Clean up the temporary steps
+		    psFree(readouts);
+		    psFree(combine);
+		    psFree(combineStats);
+
+		    // Stuff the stack back into the cell --- probably should play with the metadata, but I'm
+		    // going to ignore that for now.
+
+		    // Clean out the old structure
+		    for (int readoutNum = 0; readoutNum < cell->readouts->n; readoutNum++) {
+			psFree(cell->readouts->data[readoutNum]);
+		    }
+		    // Resize and stuff the stack in.
+		    psArrayRealloc(cell->readouts, 1);
+		    cell->readouts->data[0] = stack;
+		} else {
+		    // Otherwise, just read the cell in
+		    cell->readouts->data[0] = psReadoutRead(cell->readouts->data[0]);
+		} // Reading in readouts
+	    } // Cells
+	} // Chips
+    } // FPAs
+
+    // Now all the data is in memory.  Combine the multiple images
+    psTangentPlane *combined = pmFPACombine(NULL, staticSky, inputs, gain, readnoise, photcal, background);
+
+    // Write the combined image out
+    psTangentPlaneWriteFITS(combined, config->combined);
+
+    // Run the object detection/measurement --- this is not well thought out yet
+    (void)psDetectAndMeasureObjects(combined, otherParameters);
+
+    // Load the static sky into the structure already defined
+    (void)psTangentPlaneRead(staticSky);
+
+    // Get the seeing ratio --- involves matching stars and measuring the mean ratio of FWHM
+    float seeingRatio = pmSeeingRatio(combined, staticSky, otherParameters);
+
+    // Do the subtraction --- involves measuring the convolution kernel, applying the kernel and subtracting
+    psTangentPlane *subtracted = NULL;
+    if (seeingRatio < 1.0) {
+	subtracted = pmImageSubtraction(NULL, combined, staticSky, otherParameters);
+    } else {
+	subtracted = pmImageSubtraction(NULL, staticSky, combined, otherParameters);
+    }
+
+    // Write out the subtracted image
+    psTangentPlaneWriteFITS(subtraction, config->subtracted);
+    
+    // Find and measure objects on the subtracted image
+    (void)psDetectAndMeasureObjects(subtracted, otherParameters);
+
+    // Filter the list of candidate variable sources
+    psList *variables = pmFilterVariables(subtracted, combined, staticSky, otherParameters);
+
+    // Output the list of variable sources
+    (void)pmOutputVariables(variables, config->variables);
+
+    // All done.
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmCameraFromHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmCameraFromHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmCameraFromHeader.c	(revision 22322)
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <string.h>
+#include "pslib.h"
+#include "papmodule.h"
+
+// Work out what camera we have, based on the FITS header and a set of rules specified in the IPP
+// configuration; return the camera configuration
+psMetadata *pmCameraFromHeader(const psMetadata *header, // The FITS header
+			       const psMetadata *ipprc // The IPP configuration
+    )
+{
+    bool mdStatus = false;		// Metadata lookup status
+    psMetadata *instruments = psMetadataLookupMD(&mdStatus, ipprc, "INSTRUMENTS");
+    if (mdStatus == false) {
+	psError(PS_ERR_IO, false, "Unable to find INSTRUMENTS in the configuration.\n");
+	return NULL;
+    }
+
+    psMetadata *winner = NULL;		// The instrument configuration whose rule first matches the supplied
+					// header
+
+    // Iterate over the instruments
+    psMetadataIterator *iterator = psMetadataIteratorAlloc(instruments, PS_LIST_HEAD, NULL); // MD Iterator
+    psMetadataItem *instrumentItem = NULL; // Item from the metadata
+    while (instrumentItem = psMetadataGetAndIncrement(iterator)) {
+	// Open the instrument information
+	psTrace(__func__, 3, "Inspecting instrument %s (%s)\n", instrumentItem->name,
+		instrumentItem->comment);
+	psMetadata *instrument = NULL;	// The instrument metadata
+	if (instrumentItem->type == PS_META_META) {
+	    instrument = instrumentItem->data.md;
+	} else if (instrumentItem->type == PS_META_STR) {
+	    psTrace(__func__, 5, "Reading instrument configuration for %s...\n", instrumentItem->name);
+	    int badLines = 0;		// Number of bad lines in reading instrument configuration
+	    instrument = psMetadataConfigParse(NULL, &badLines, instrumentItem->data.V, true);
+	    if (badLines > 0) {
+		psLogMsg(__func__, PS_LOG_WARN, "%d bad lines encountered while reading camera"
+			 "configuration %s\n", badLines, instrumentItem->name);
+	    }
+	}
+
+	if (! instrument) {
+	    psLogMsg(__func__, PS_LOG_WARN, "Unable to interpret instrument configuration for %s (%s)\n",
+		     instrumentItem->name, instrumentItem->comment);
+	} else {
+	    // Read the rule for that instrument
+	    psMetadata *rule = psMetadataLookupMD(&mdStatus, instrument, "RULE");
+	    if (! mdStatus) {
+		psLogMsg(__func__, PS_LOG_WARN, "Unable to read rule for instrument %s (%s)\n",
+			 instrumentItem->name, instrumentItem->comment);
+	    } else {
+		// Apply the rules
+		psMetadataIterator *ruleIter = psMetadataIteratorAlloc(rule, PS_LIST_HEAD, NULL); // Rule
+											          // iterator
+		bool match = true;	// The header matches the rule until we determine otherwise
+		psMetadataItem *ruleItem = NULL; // Item from the metadata
+		while ((ruleItem = psMetadataGetAndIncrement(ruleIter)) && match) {
+		    // Check for the existence of the rule
+		    psMetadataItem *headerItem = psMetadataLookup((psMetadata*)header, ruleItem->name);
+		    if (! headerItem || headerItem->type != ruleItem->type) {
+			match = false;
+		    } else {
+			// Check to see if the rule works
+			switch (ruleItem->type) {
+			  case PS_META_STR:
+			    psTrace(__func__, 8, "Matching %s: '%s' vs '%s'\n", ruleItem->name,
+				    ruleItem->data.V, headerItem->data.V);
+			    match = (strncmp(ruleItem->data.V, headerItem->data.V,
+					     strlen(ruleItem->data.V)) == 0) ? true : false;
+			    break;
+			  case PS_TYPE_S32:
+			  case PS_TYPE_BOOL:
+			    psTrace(__func__, 8, "Matching %s: %d vs %d\n", ruleItem->name,
+				    ruleItem->data.S32, headerItem->data.S32);
+			    if (ruleItem->data.S32 != headerItem->data.S32) {
+				match = false;
+			    }
+			    break;
+			  case PS_TYPE_F32:
+			    psTrace(__func__, 8, "Matching %s: %f vs %f\n", ruleItem->name,
+				    ruleItem->data.F32, headerItem->data.F32);
+			    if (ruleItem->data.F32 != headerItem->data.F32) {
+				match = false;
+			    }
+			    break;
+			  case PS_TYPE_F64:
+			    psTrace(__func__, 8, "Matching %s: %g vs %g\n", ruleItem->name,
+				    ruleItem->data.F64, headerItem->data.F64);
+			    if (ruleItem->data.F64 != headerItem->data.F64) {
+				match = false;
+			    }
+			    break;
+			  default:
+			    psLogMsg(__func__, PS_LOG_WARN, "Ignoring invalid type in metadata: %x\n",
+				     ruleItem->type);
+			}
+		    }
+		} // Iterating through the rules for an instrument
+
+		if (match) {
+		    if (! winner) {
+			// This is the first match
+			winner = instrument;
+			psLogMsg(__func__, PS_LOG_INFO, "FITS header matches instrument %s\n",
+				 instrumentItem->name);
+		    } else {
+			// We have a duplicate match
+			psLogMsg(__func__, PS_LOG_WARN,
+				 "Additional instrument found that matches the rules: %s\n",
+				 instrumentItem->name);
+		    }
+		}
+	    }
+
+	} // Done inspecting the instrument
+		    
+    } // Done looking at all instruments
+    psFree(iterator);
+
+    if (! winner) {
+	psError(PS_ERR_IO, true, "Unable to find an instrument that matches input FITS header!\n");
+    }
+
+    return winner;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmCameraFromHeader.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmCameraFromHeader.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmCameraFromHeader.h	(revision 22322)
@@ -0,0 +1,10 @@
+#ifndef PM_CAMERA_FROM_HEADER_H
+#define PM_CAMERA_FROM_HEADER_H
+
+// Work out what camera we have, based on the FITS header and a set of rules specified in the IPP
+// configuration; return the camera configuration
+psMetadata *pmCameraFromHeader(const psMetadata *header, // The FITS header
+			       const psMetadata *ipprc // The IPP configuration
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAConstruct.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAConstruct.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAConstruct.c	(revision 22322)
@@ -0,0 +1,315 @@
+#include <stdio.h>
+#include <string.h>
+#include "pslib.h"
+#include "papmodule.h"
+#include "papStuff.h"
+#include "papFocalPlane.h"
+#include "pmFPAConstruct.h"
+
+// NOTE: Need to deal with header inheritance
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Copy metadata elements into another metadata
+static void metadataCopy(psMetadata *to, // Metadata to which to copy
+			 psMetadata *from // Metdata from which to copy
+    )
+{
+    psMetadataIterator *mdIter = psMetadataIteratorAlloc(from, PS_LIST_HEAD, NULL); // Iterator for the MD
+    psMetadataItem *item = NULL;	// Item from the metadata
+    while (item = psMetadataGetAndIncrement(mdIter)) {
+	psTrace(__func__, 9, "Adding %s to metadata...\n", item->name);
+	if (! psMetadataAddItem(to, item, PS_LIST_TAIL, PS_META_REPLACE)) {
+	    psLogMsg(__func__, PS_LOG_WARN, "Unable to add item (%s: %s) to metadata: ignored\n", item->name,
+		     item->comment);
+	}
+    }
+    psFree(mdIter);
+}
+
+// Read data for a particular cell from the camera configuration
+static psMetadata *getCellData(const psMetadata *camera, // The camera configuration
+			       const char *cellName // The name of the cell
+    )
+{
+    bool status = true;			// Result of MD lookup
+    psMetadata *cells = psMetadataLookupMD(&status, camera, "CELLS"); // The CELLS
+    if (! status) {
+	psError(PS_ERR_IO, false, "Unable to determine CELLS of camera.\n");
+	return NULL;
+    }
+
+    psMetadata *cellData = psMetadataLookupMD(&status, cells, cellName); // The data for the particular cell
+    if (! status) {
+	psError(PS_ERR_IO, false, "Unable to find specs for cell %s: ignored\n", cellName);
+	return NULL;
+    } else {
+	return cellData;
+    }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+papFPA *pmFPAConstruct(const psMetadata *camera, // The camera configuration
+		       psDB *db		// Database handle
+    )
+{
+    papFPA *fpa = papFPAAlloc(camera, db); // The FPA to fill out
+    bool mdStatus = true;		// Status from metadata lookups
+    const char *phuType = psMetadataLookupString(&mdStatus, camera, "PHU"); // What is the PHU?
+    const char *extType = psMetadataLookupString(&mdStatus, camera, "EXTENSIONS"); // What's in the extensions?
+    if (strncmp(phuType, "FPA", 3) == 0) {
+	// The FITS file contains an entire FPA
+
+	psMetadata *contents = psMetadataLookupMD(&mdStatus, camera, "CONTENTS"); // The CONTENTS
+	if (! mdStatus) {
+	    psError(PS_ERR_IO, false, "Unable to determine CONTENTS of camera.\n");
+	    psFree(fpa);
+	    return NULL;
+	}
+
+	// Set up iteration over the contents
+	psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
+	psMetadataItem *contentItem = NULL; // Item from the metadata
+
+	if (strncmp(extType, "CHIP", 4) == 0) {
+	    // Extensions are chips; Content contains a list of cells
+	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
+		psString extName = contentItem->name; // The name of the extension
+		papChip *chip = papChipAlloc(fpa, extName); // The chip
+		chip->extname = extName;// Mark chip to receive FITS data
+		if (contentItem->type != PS_META_STR) {
+		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
+			     contentItem->type);
+		    continue;
+		}
+
+		const char *content = contentItem->data.V; // The content of the extension
+		psTrace(__func__, 7, "Content of %s is: %s\n", extName, content);
+		psList *cellNames = papSplit(content, " ,"); // A list of the component cells
+		psListIterator *cellNamesIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, NULL);
+		char *cellName = NULL; // The name of a cell
+		while (cellName = psListGetAndIncrement(cellNamesIter)) {
+		    // Get the cell data
+		    papCell *cell = papCellAlloc(chip, cellName); // The cell
+		    psMetadata *cellData = getCellData(camera, cellName);
+		    metadataCopy(cell->values, cellData);
+		}
+		psFree(cellNamesIter);
+		psFree(cellNames);
+	    }
+
+	} else if (strncmp(extType, "CELL", 4) == 0) {
+	    // Extensions are cells; Content contains a chip name and cell type
+	    psMetadata *chips = psMetadataAlloc(); // Given a chip name, holds the chip number
+	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
+		psString extName = contentItem->name; // The name of the extension
+		psTrace(__func__, 1, "Getting %s....\n", extName);
+
+		if (contentItem->type != PS_META_STR) {
+		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
+			     contentItem->type);
+		    continue;
+		}
+		const char *content = contentItem->data.V; // The content of the extension
+		psList *contents = papSplit(content, ": "); // Split the name from the type
+		if (contents->size != 2) {
+		    psLogMsg(__func__, PS_LOG_WARN, "Unable to read contents of %s: ignored.\n", extName);
+		} else {
+		    psString chipName = psListGet(contents, 0); // The name of the chip
+		    psString cellType = psListGet(contents, 1); // The type of cell
+		    psTrace(__func__, 7, "Extension is cell of type %s, from chip %s\n", cellType,
+			    chipName);
+		    
+		    papChip *chip = psMetadataLookupChip(&mdStatus, chips, chipName); // The chip
+		    if (! mdStatus && ! chip) {
+			chip = papChipAlloc(fpa, chipName);
+			psMetadataAdd(chips, PS_LIST_TAIL, chipName, PS_META_CHIP, "", chip);
+		    }
+		    // The cell
+		    psArray *images = NULL;
+		    psMetadata *header = NULL;
+		    papCell *cell = papCellAlloc(chip, extName); // The cell
+		    cell->extname = extName; // Mark cell to receive FITS data
+		    psMetadata *cellData = getCellData(camera, cellType);
+		    metadataCopy(cell->values, cellData);
+		}
+	    }
+	    psFree(chips);
+
+	} else if (strncmp(extType, "NONE", 4) == 0) {
+	    // No extensions; Content contains metadata, each entry is a chip with its component cells
+	    fpa->extname = "PHU";
+	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
+		psString chipName = contentItem->name; // The name of the chip
+
+		if (contentItem->type != PS_META_STR) {
+		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
+			     contentItem->type);
+		    continue;
+		}
+		psString content = contentItem->data.V; // The content of the extension
+		psTrace(__func__, 5, "Component cells are: %s\n", content);
+		papChip *chip = papChipAlloc(fpa, chipName); // The chip
+		psList *cellNames = papSplit(content, ", "); // Split the list of cells
+		psListIterator *cellNamesIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false);
+		char *cellName = NULL; // Name of the cell
+		while (cellName = psListGetAndIncrement(cellNamesIter)) {
+		    psTrace(__func__, 7, "Processing cell %s....\n", cellName);
+		    papCell *cell = papCellAlloc(chip, cellName); // The cell
+		    psMetadata *cellData = getCellData(camera, cellName);
+		    metadataCopy(cell->values, cellData);
+		}
+		psFree(cellNamesIter);
+	    }
+
+	} else {
+	    psError(PS_ERR_IO, false, "EXTENSIONS in camera definition is not CHIP, CELL or NONE.\n");
+	    psFree(fpa);
+	    return NULL;
+	} // Type of extension
+
+	psFree(contentsIter);
+
+    } else if (strncmp(phuType, "CHIP", 4) == 0) {
+	// The FITS file contains a single chip only
+	psString chipName = psStringCopy("CHIP"); // Name for chip
+	papChip *chip = papChipAlloc(fpa, chipName); // The chip
+
+	if (strncmp(extType, "NONE", 4) == 0) {
+	    // There are no extensions --- only the PHU
+	    chip->extname = "PHU";
+
+	    const char *contents = psMetadataLookupString(&mdStatus, camera, "CONTENTS");
+	    if (! mdStatus) {
+		psError(PS_ERR_IO, false, "Unable to determine CONTENTS of camera.\n");
+		psFree(fpa);
+		return NULL;
+	    }
+	    psList *cellNames = papSplit(contents, " ,"); // Names of cells
+	    psListIterator *cellIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false); // Iterator
+	    psString cellName = NULL;
+	    while (cellName = psListGetAndIncrement(cellIter)) {
+		papCell *cell = papCellAlloc(chip, cellName); // The cell
+		psMetadata *cellData = getCellData(camera, cellName);
+		metadataCopy(cell->values, cellData);
+	    }
+	    psFree(cellIter);
+
+	} else if (strncmp(extType, "CELL", 4) == 0) {
+	    // Extensions are cells
+	    psMetadata *contents = psMetadataLookupMD(&mdStatus, camera, "CONTENTS"); // The CONTENTS
+	    if (! mdStatus) {
+		psError(PS_ERR_IO, false, "Unable to determine CONTENTS of camera.\n");
+		psFree(fpa);
+		return NULL;
+	    }
+	    
+	    if (strncmp(extType, "CELL", 4) != 0) {
+		psLogMsg(__func__, PS_LOG_WARN, "EXTENSIONS in camera definition is %s, but PHU is CHIP.\n"
+			 "EXTENSIONS assumed to be CELL.\n", extType);
+	    }
+
+	    // Iterate through the contents
+	    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
+	    psMetadataItem *contentItem = NULL; // Item from metadata
+	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
+		psString extName = contentItem->name; // The name of the extension
+		// Content is a cell type
+		if (contentItem->type != PS_META_STR) {
+		    psLogMsg(__func__, PS_LOG_WARN,
+			     "CONTENT metadata for extension %s is not of type string, but %x --- ignored\n",
+			     extName, contentItem->type);
+		    continue;
+		}
+		const char *cellType = contentItem->data.V; // The type of cell
+		psTrace(__func__, 5, "Cell type is %s\n", cellType);
+		psArray *images = NULL;
+		psMetadata *header = NULL;
+		papCell *cell = papCellAlloc(chip, extName); // The cell
+		cell->extname = extName; // Mark cell to receive FITS data
+		psMetadata *cellData = getCellData(camera, cellType);
+		metadataCopy(cell->values, cellData);
+
+	    } // Iterating through contents
+	    psFree(contentsIter);
+
+	} else {
+	    psError(PS_ERR_IO, false, "EXTENSIONS in camera definition is neither CELL or NONE.\n");
+	    psFree(fpa);
+	    return NULL;
+	}
+
+    } else {
+	psError(PS_ERR_IO, true,
+		"The PHU type specified in the camera configuration (%s) is not FPA or CHIP.\n",
+		phuType);
+	psFree(fpa);
+	return NULL;
+    }
+
+    return fpa;
+}
+
+// Print out the focal plane structure
+void pmFPAPrint(papFPA *fpa		// FPA to print
+    )
+{
+    psTrace(__func__, 0, "FPA:\n");
+    if (fpa->extname) {
+	psTrace(__func__, 1, "---> FPA is extension %s.\n", fpa->extname);
+	if (! fpa->pixels) {
+	    psTrace(__func__, 1, "---> NO PIXELS for extension %s\n", fpa->extname);
+	}
+    }
+
+    psArray *chips = fpa->chips;	// Array of chips
+    // Iterate over the FPA
+    for (int i = 0; i < chips->n; i++) {
+	psTrace(__func__, 1, "Chip: %d\n", i);
+	papChip *chip = chips->data[i]; // The chip
+	if (chip->extname) {
+	    psTrace(__func__, 2, "---> Chip is extension %s.\n", chip->extname);
+	    if (! chip->pixels) {
+		psTrace(__func__, 2, "---> NO PIXELS for extension %s\n", chip->extname);
+	    }
+	}
+	// Iterate over the chip
+	psArray *cells = chip->cells;	// Array of cells
+	for (int j = 0; j < cells->n; j++) {
+	    psTrace(__func__, 2, "Cell: %d\n", j);
+	    papCell *cell = cells->data[j]; // The cell
+	    if (cell->extname) {
+		psTrace(__func__, 3, "---> Cell is extension %s.\n", cell->extname);
+		if (! cell->pixels) {
+		    psTrace(__func__, 3, "---> NO PIXELS for extension %s\n", cell->extname);
+		}
+	    }
+	    psMetadataPrint(cell->values, 3);
+	    psTrace(__func__, 3, "Readouts:\n");
+	    psArray *readouts = cell->readouts;	// Array of readouts
+	    for (int k = 0; k < readouts->n; k++) {
+		papReadout *readout = readouts->data[k]; // The readout
+		psImage *image = readout->image; // The image
+		psTrace(__func__, 4, "Image: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 + 
+			image->numCols, image->row0, image->row0 + image->numRows, image->numCols,
+			image->numRows);
+		psList *overscans = readout->overscans;	// The list of overscans
+		psListIterator *overscansIter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false);
+		while (image = psListGetAndIncrement(overscansIter)) {
+		    psTrace(__func__, 4, "Overscan: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 + 
+			    image->numCols, image->row0, image->row0 + image->numRows, image->numCols,
+			    image->numRows);
+		}
+		psFree(overscansIter);
+
+	    } // Iterating over cell
+	} // Iterating over chip
+    } // Iterating over FPA
+
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAConstruct.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAConstruct.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAConstruct.h	(revision 22322)
@@ -0,0 +1,17 @@
+#ifndef PM_FPA_CONSTRUCT_H
+#define PM_FPA_CONSTRUCT_H
+
+#include "pslib.h"
+#include "papFocalPlane.h"
+
+// Read the contents of a FITS file (format specified by the camera configuration) into memory
+papFPA *pmFPAConstruct(const psMetadata *camera, // The camera configuration
+		       psDB *db		// Database handle
+    );
+
+// Print out the FPA
+void pmFPAPrint(papFPA *fpa		// FPA to print
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAFromHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAFromHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAFromHeader.c	(revision 22322)
@@ -0,0 +1,306 @@
+#include <stdio.h>
+#include <string.h>
+#include "pslib.h"
+#include "psmodule.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Copy a metadata
+static psMetadata *metadataCopy(psMetadata *md)
+{
+    psMetadata *new = psMetadataAlloc();// The metadata to return
+    psMetadataIterator *mdIter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL); // Iterator for the MD
+    psMetadataItem *item = NULL;	// Item from the metadata
+    while (item = psMetadataGetAndIncrement(mdIter)) {
+	if (! psMetadataAddItem(new, item, PS_LIST_TAIL, PS_META_REPLACE)) {
+	    psLogMsg(__func__, PS_LOG_WARN, "Unable to add item (%s: %s) to metadata: ignored\n", item->name,
+		     item->comment);
+	}
+    }
+    psFree(mdIter);
+
+    return new;
+}
+
+// Read data for a particular cell from the camera configuration
+static psMetadata *getCellData(const psMetadata *camera, // The camera configuration
+			       const char *cellName // The name of the cell
+    )
+{
+    bool status = true;			// Result of MD lookup
+    psMetadata *cells = psMetadataLookupMD(&status, camera, "CELLS"); // The CELLS
+    if (! status) {
+	psError(PS_ERR_IO, false, "Unable to determine CELLS of camera.\n");
+	return NULL;
+    }
+
+    psMetadata *cellData = psMetadataLookupMD(&status, cells, cellName); // The data for the particular cell
+    if (! status) {
+	psError(PS_ERR_IO, false, "Unable to find specs for cell %s: ignored\n", cellName);
+	return NULL;
+    } else {
+	psMetadata *newCell = metadataCopy(cellData); // A copy of the cell, to be returned
+	return newCell;
+    }
+}
+	
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+psMetadata *pmFPAfromHeader(const psMetadata *camera // The camera configuration
+    )
+{
+ 
+#if 0
+    psMetadata *headers = psFitsReadHeaderSet(fitsFile); // The headers of every extension
+    if (! headers) {
+	psError(PS_ERR_BAD_DATA, false, "Unable to read FITS headers.\n");
+	return NULL;
+    }
+#endif
+
+    // Generate a build tree
+    psMetadata *fpa = psMetadataAlloc(); // The FPA to fill out
+
+    bool mdStatus = true;		// Status from metadata lookups
+    const char *phuType = psMetadataLookupString(&mdStatus, camera, "PHU"); // What is the PHU?
+    const char *extType = psMetadataLookupString(&mdStatus, camera, "EXTENSIONS"); // What's in the extensions?
+    if (strcmp(phuType, "FPA") == 0) {
+	// The FITS file contains an entire FPA
+
+	psMetadata *contents = psMetadataLookupMD(&mdStatus, camera, "CONTENTS"); // The CONTENTS
+	if (! mdStatus) {
+	    printf("Get here.\n");
+	    psError(PS_ERR_IO, false, "Unable to determine CONTENTS of camera.\n");
+	    psFree(fpa);
+	    return NULL;
+	}
+
+	if (strcmp(extType, "CHIP") == 0) {
+	    // Extensions are chips; Content contains a list of cells
+	    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
+	    psMetadataItem *contentItem = NULL; // Item from the metadata
+	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
+		const char *extName = contentItem->name; // The name of the extension
+
+		if (contentItem->type != PS_META_STR) {
+		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
+			     contentItem->type);
+		} else {
+		    const char *content = contentItem->data.V; // The content of the extension
+		    psMetadata *chip = psMetadataAlloc(); // The chip
+		    psList *cellNames = papSplit(content, " ,"); // A list of the component cells
+		    psListIterator *cellNamesIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, NULL);
+		    char *cellName = NULL; // The name of a cell
+		    while (cellName = psListGetAndIncrement(cellNamesIter)) {
+			// Get the cell data
+			psMetadata *cell = getCellData(camera, cellName);
+			psMetadataAdd(cell, PS_LIST_HEAD, "EXTENSION", PS_META_STR, "", extName);
+			// Add the cell to the chip
+			psMetadataAdd(chip, PS_LIST_TAIL, cellName, PS_META_META, "", cell);
+		    }
+		    psFree(cellNamesIter);
+		    psFree(cellNames);
+		    // Add the chip onto the FPA
+		    psMetadataAdd(fpa, PS_LIST_TAIL, extName, PS_META_META, "", chip);
+		}
+	    }
+	    psFree(contentsIter);
+
+	} else if (strcmp(extType, "CELL") == 0) {
+	    // Extensions are cells; Content contains a chip name and cell type
+	    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
+	    psMetadataItem *contentItem = NULL; // Item from the metadata
+	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
+		const char *extName = contentItem->name; // The name of the extension
+
+		if (contentItem->type != PS_META_STR) {
+		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
+			     contentItem->type);
+		} else {
+		    const char *content = contentItem->data.V; // The content of the extension
+		    psList *contents = papSplit(content, ": "); // Split the name from the type
+		    if (contents->size != 2) {
+			psLogMsg(__func__, PS_LOG_WARN, "Unable to read contents of %s: ignored.\n", extName);
+		    } else {
+			// The name of the chip
+			const char *chipName = psListGet(contents, 0);
+			// The type of cell
+			const char *cellType = psListGet(contents, 1);
+
+			psMetadata *chip = psMetadataLookupMD(&mdStatus, fpa, chipName); // The chip
+			if (! mdStatus && ! chip) {
+			    chip = psMetadataAlloc();
+			    psMetadataAdd(fpa, PS_LIST_TAIL, chipName, PS_META_META, "", chip);
+			}
+			// The cell
+			psMetadata *cell = getCellData(camera, cellType);
+			psMetadataAdd(cell, PS_LIST_HEAD, "EXTENSION", PS_META_STR, "", extName);
+			// Add the cell to the chip
+			psMetadataAdd(chip, PS_LIST_TAIL, extName, PS_META_META, "", cell);
+		    }
+		}
+	    }
+	    psFree(contentsIter);
+	} else if (strcmp(extType, "NONE") == 0) {
+	    // No extensions; Content contains metadata, each entry is a chip with its component cells
+	    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
+	    psMetadataItem *contentItem = NULL; // Item from the metadata
+	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
+		const char *chipName = contentItem->name; // The name of the chip
+
+		if (contentItem->type != PS_META_STR) {
+		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
+			     contentItem->type);
+		} else {
+		    const char *content = contentItem->data.V; // The content of the extension
+		    psMetadata *chip = psMetadataAlloc(); // The chip
+		    psList *cellNames = papSplit(content, ", "); // Split the list of cells
+		    psListIterator *cellNamesIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false);
+		    char *cellName = NULL; // Name of the cell
+		    while (cellName = psListGetAndIncrement(cellNamesIter)) {
+			psMetadata *cell = getCellData(camera, cellName);
+			psMetadataAdd(cell, PS_LIST_HEAD, "EXTENSION", PS_META_STR, "", "PHU");
+			psMetadataAdd(chip, PS_LIST_TAIL, cellName, PS_META_META, "", cell);
+		    }
+		    psFree(cellNamesIter);
+		    // Add the chip onto the FPA
+		    psMetadataAdd(fpa, PS_LIST_TAIL, chipName, PS_META_META, "", chip);
+		}
+	    }
+	    psFree(contentsIter);
+	} else {
+	    psError(PS_ERR_IO, false, "EXTENSIONS in camera definition is not CHIP, CELL or NONE.\n");
+	    psFree(fpa);
+	    return NULL;
+	} // Type of extension
+
+    } else if (strcmp(phuType, "CHIP") == 0) {
+	// The FITS file contains a single chip only
+
+	psMetadata *chip = psMetadataAlloc(); // The chip
+	psMetadataAdd(fpa, PS_LIST_TAIL, "CHIP", PS_META_META, "", chip);
+	
+	if (strcmp(extType, "NONE") == 0) {
+	    // There are no extensions --- only the PHU
+	    const char *contents = psMetadataLookupString(&mdStatus, camera, "CONTENTS");
+	    if (! mdStatus) {
+		psError(PS_ERR_IO, false, "Unable to determine CONTENTS of camera.\n");
+		psFree(fpa);
+		return NULL;
+	    }
+	    psList *cellNames = papSplit(contents, " ,"); // Names of cells
+	    psListIterator *cellIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false); // Iterator
+	    const char *cellName = NULL;
+	    while (cellName = psListGetAndIncrement(cellIter)) {
+		psMetadata *cell = getCellData(camera, cellName);
+		psMetadataAdd(cell, PS_LIST_HEAD, "EXTENSION", PS_META_STR, "", "PHU");
+		// Add the cell to the chip
+		psMetadataAdd(chip, PS_LIST_TAIL, cellName, PS_META_META, "", cell);
+	    }
+	    psFree(cellIter);
+	} else if (strcmp(extType, "CELL") == 0) {
+	    // Extensions are cells
+
+	    psMetadata *contents = psMetadataLookupMD(&mdStatus, camera, "CONTENTS"); // The CONTENTS
+	    if (! mdStatus) {
+		psError(PS_ERR_IO, false, "Unable to determine CONTENTS of camera.\n");
+		psFree(fpa);
+		return NULL;
+	    }
+	    
+	    if (strcmp(extType, "CELL") != 0) {
+		psLogMsg(__func__, PS_LOG_WARN, "EXTENSIONS in camera definition is %s, but PHU is CHIP.\n"
+			 "EXTENSIONS assumed to be CELL.\n", extType);
+	    }
+
+	    // Iterate through the contents
+	    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
+	    psMetadataItem *contentItem = NULL; // Item from metadata
+	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
+		const char *extName = contentItem->name; // The name of the extension
+		
+		// Content is a cell type
+		if (contentItem->type != PS_META_STR) {
+		    psLogMsg(__func__, PS_LOG_WARN,
+			     "CONTENT metadata for extension %s is not of type string, but %x --- ignored\n",
+			     extName, contentItem->type);
+		} else {
+		    const char *cellType = contentItem->data.V; // The type of cell
+		    
+		    // The cell
+		    psMetadata *cell = getCellData(camera, cellType);
+		    psMetadataAdd(cell, PS_LIST_HEAD, "EXTENSION", PS_META_STR, "", extName);
+		    // Add the cell to the chip
+		    psMetadataAdd(chip, PS_LIST_TAIL, extName, PS_META_META, "", cell);
+		}
+	    } // Iterating through contents
+	    psFree(contentsIter);
+
+	} else {
+	    psError(PS_ERR_IO, false, "EXTENSIONS in camera definition is neither CELL or NONE.\n");
+	    psFree(fpa);
+	    return NULL;
+	}
+
+    } else {
+	psError(PS_ERR_IO, true,
+		"The PHU type specified in the camera configuration (%s) is not FPA or CHIP.\n",
+		phuType);
+	psFree(fpa);
+	return NULL;
+    }
+
+    
+    // Print out the build tree
+    psMetadataIterator *fpaIter = psMetadataIteratorAlloc(fpa, PS_LIST_HEAD, NULL); // Iterator for FPA
+    psMetadataItem *fpaItem = NULL;	// Item from metadata
+    psTrace("pmFPAfromHeader", 0, "FPA:\n");
+    while (fpaItem = psMetadataGetAndIncrement(fpaIter)) {
+	if (fpaItem->type != PS_META_META) {
+	    psError(PS_ERR_IO, false, "FPA metadata does not contain chip metadata.\n");
+	} else {
+	    psTrace("pmFPAfromHeader", 1, "Chip: %s\n", fpaItem->name);
+	    psMetadata *chip = fpaItem->data.V;	// The chip
+	    psMetadataIterator *chipIter = psMetadataIteratorAlloc(chip, PS_LIST_HEAD, NULL); // Iterator for
+											      // chip
+	    psMetadataItem *chipItem = NULL; // Item from metadata
+	    while (chipItem = psMetadataGetAndIncrement(chipIter)) {
+		if (chipItem->type != PS_META_META) {
+		    psError(PS_ERR_IO, false, "Chip metadata does not contain cell metadata.\n");
+		} else {
+		    psTrace("pmFPAfromHeader", 2, "Cell: %s\n", chipItem->name);
+		    psMetadata *cell = chipItem->data.V; // The cell
+		    psMetadataIterator *cellIter = psMetadataIteratorAlloc(cell, PS_LIST_HEAD, NULL); // Iterator
+												  // for cell
+		    psMetadataItem *cellItem = NULL; // Item from metadata
+		    while (cellItem = psMetadataGetAndIncrement(cellIter)) {
+			switch(cellItem->type) {
+			  case PS_META_STR:
+			    psTrace("pmFPAfromHeader", 3, "%s: %s\n", cellItem->name, cellItem->data.V);
+			    break;
+			  case PS_META_F32:
+			    psTrace("pmFPAfromHeader", 3, "%s: %f\n", cellItem->name, cellItem->data.F32);
+			    break;
+			  case PS_META_META:
+			    psTrace("pmFPAfromHeader", 3, "%s:\n", cellItem->name);
+			    psMetadataPrint(cellItem->data.V, 4);
+			    break;
+			  default:
+			    psError(PS_ERR_IO, false, "Unknown type for cell (%x).\n", cellItem->type);
+			}
+		    } // Iterating through cell
+		    psFree(cellIter);
+		}
+	    } // Iterating through chip
+	    psFree(chipIter);
+	}
+    } // Iterating through FPA
+    psFree(fpaIter);
+
+    return fpa;
+
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAFromHeader.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAFromHeader.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAFromHeader.h	(revision 22322)
@@ -0,0 +1,8 @@
+#ifndef PM_FPA_FROM_HEADER_H
+#define PM_FPA_FROM_HEADER_H
+
+// Read in an FPA
+psMetadata *pmFPAfromHeader(const psMetadata *camera // The camera configuration
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAMorph.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAMorph.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAMorph.c	(revision 22322)
@@ -0,0 +1,760 @@
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "pslib.h"
+#include "papmodule.h"
+
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
+
+// Flip the chip in x
+static psImage *xFlipImage(psImage *image, // Image to be flipped
+			   int size	// Size of image in flip dimension
+    )
+{
+    psImage *flipped = psImageAlloc(size, image->numRows, image->type.type); // Flipped image
+    switch (image->type.type) {
+      case PS_TYPE_U16:
+	for (int y = 0; y < image->numRows; y++) {
+	    for (int x = 0; x < image->numCols; x++) {
+		flipped->data.U16[y][size - x - 1] = image->data.U16[y][x];
+	    }
+	}
+	break;
+      default:
+	psError(PS_ERR_IO, true, "Image type %x not yet supported for flip.\n", image->type.type);
+    }
+
+    return flipped;
+}
+
+// Flip the chip in y
+static psImage *yFlipImage(psImage *image, // Image to be flipped
+			   int size	// Size of image in flip dimension
+    )
+{
+    psImage *flipped = psImageAlloc(image->numCols, size, image->type.type); // Flipped image
+    switch (image->type.type) {
+      case PS_TYPE_U16:
+	for (int y = 0; y < image->numRows; y++) {
+	    for (int x = 0; x < image->numCols; x++) {
+		flipped->data.U16[size - y - 1][x] = image->data.U16[y][x];
+	    }
+	}
+	break;
+      default:
+	psError(PS_ERR_IO, true, "Image type %x not yet supported for flip.\n", image->type.type);
+    }
+
+    return flipped;
+}
+
+// Return a list of the region strings and the method for interpreting them
+static psList *getRegions(psString *method, // Method for evaluating the regions
+			  psString methodValues // The METHOD:VALUES string
+    )
+{
+    char *values = strchr(methodValues, ':'); // The VALUES
+    *method = psStringNCopy(methodValues, strlen(methodValues) - strlen(values)); // The METHOD
+    psList *regionStrings = NULL;	// List of the region strings
+    if (strncmp(*method, "HEADER", 6) == 0 || strncmp(*method, "HD", 2) == 0) {
+	regionStrings = papSplit(values, ", ");
+    } else if (strncmp(*method, "VALUE", 5) == 0 || strncmp(*method, "VAL", 3) == 0) {
+	regionStrings = papSplit(values, "; ");
+    }
+
+    return regionStrings;
+}
+
+
+static psArray *spliceCells(psMetadata *header,	// Output header
+			    psList *outCells, // List of target cells (required for parity info)
+			    psList *inCells // List of cells to splice together
+			    )
+{
+    assert(header);
+    assert(outCells);
+    assert(inCells);
+
+    psTrace(__func__, 5, "Splicing together all cells in the bucket.\n");
+
+    if (inCells->size != outCells->size) {
+	psError(PS_ERR_IO, true, "Sizes of input (%d) and output (%d) lists of cells don't match.\n",
+		inCells->size, outCells->size);
+	return NULL;
+    }
+
+    int numCells = inCells->size;	// Number of cells
+    int readdir = 0;			// Read direction for CCD; currently unknown
+    int numReadouts = 0;		// Number of readouts in a cell
+    psArray *spliced = NULL;		// Array of spliced readouts
+    psElemType type = 0;		// Image type (U16, S32, F32, etc)
+
+    psVector *xSize = NULL;		// Size of spliced image in x
+    psVector *ySize = NULL;		// Size of spliced image in y
+
+    psVector *xFlip = psVectorAlloc(numCells, PS_TYPE_U8); // Do we need to flip in x?
+    psVector *yFlip = psVectorAlloc(numCells, PS_TYPE_U8); // Do we need to flip in y?
+
+    psListIterator *inCellsIter = psListIteratorAlloc(inCells, PS_LIST_HEAD, false); // Iterator for cells
+    papCell *inCell = NULL;		// Cell from list of input cells
+    psListIterator *outCellsIter = psListIteratorAlloc(outCells, PS_LIST_HEAD, false); // Iterator for cells
+    int cellNum = 0;			// Cell number; 
+    while (inCell = psListGetAndIncrement(inCellsIter)) {
+	psArray *readouts = inCell->readouts;	// The readouts comprising the cell
+
+	// Check the read direction
+	int cellReaddir = psCellGetValueS32(inCell, "CELL.READDIR"); // Read direction for cell
+	if (readdir == 0) {
+	    readdir = cellReaddir;
+	} else if (readdir != cellReaddir) {
+	    psError(PS_ERR_IO, true, "Trying to splice cells with different read directions (%d vs %d)\n",
+		    readdir, cellReaddir);
+	    // XXX Clean up before returning
+	    return NULL;
+	}
+	// Check the number of readouts
+	if (! spliced) {
+	    numReadouts = readouts->n;
+	    spliced = psArrayAlloc(numReadouts);
+	    xSize = psVectorAlloc(numReadouts, PS_TYPE_S32);
+	    ySize = psVectorAlloc(numReadouts, PS_TYPE_S32);
+	    for (int i = 0; i < numReadouts; i++) {
+		xSize->data.S32[i] = 0;
+		ySize->data.S32[i] = 0;
+	    }
+	} else if (readouts->n != numReadouts) {
+	    psError(PS_ERR_IO, true, "Trying to splice cells with different number of reads (%d vs %d)\n",
+		    numReadouts, readouts->n);
+	    // XXX Clean up before returning
+	    return NULL;
+	}
+
+	// Get the cell parities
+	papCell *outCell = psListGetAndIncrement(outCellsIter);	// Corresponding output cell
+	int xParityIn = psCellGetValueS32(inCell, "CELL.XPARITY");
+	int yParityIn = psCellGetValueS32(inCell, "CELL.YPARITY");
+	int xParityOut = psCellGetValueS32(outCell, "CELL.XPARITY");
+	int yParityOut = psCellGetValueS32(outCell, "CELL.YPARITY");
+	if (xParityIn == 0 || xParityOut == 0 || yParityIn == 0 || yParityOut == 0) {
+	    psError(PS_ERR_IO, false, "Unable to determine parity of cell!\n");
+	    // XXX Clean up before returning
+	    return NULL;
+	}
+
+	if (yParityIn == yParityOut) {
+	    yFlip->data.U8[cellNum] = 0;
+	} else {
+	    psTrace(__func__, 7, "Need to flip y.\n");
+	    yFlip->data.U8[cellNum] = 1;
+	}
+	if (xParityIn == xParityOut) {
+	    xFlip->data.U8[cellNum] = 0;
+	} else {
+	    psTrace(__func__, 7, "Need to flip x.\n");
+	    xFlip->data.U8[cellNum] = 1;
+	}
+	cellNum++;
+
+	// Calculate the sizes of the spliced images
+	for (int i = 0; i < numReadouts; i++) {
+	    papReadout *readout = readouts->data[i]; // The readout of interest
+	    psImage *image = readout->image; // The image pixels
+	    psList *overscans = readout->overscans; // The overscan regions
+
+	    // Check image type
+	    if (type == 0) {
+		type = image->type.type;
+	    } else if (image->type.type != type) {
+		psError(PS_ERR_IO, true, "Trying to splice readouts with different image types (%d vs %d)\n",
+			type, image->type.type);
+		// XXX Clean up before returning
+		return NULL;
+	    }
+
+	    // Get the size of the spliced image
+	    if (readdir == 1) {
+		psTrace(__func__, 9, "Image size: %dx%d\n", image->numCols, image->numRows);
+		xSize->data.S32[i] += image->numCols;
+		ySize->data.S32[i] = MAX(ySize->data.S32[i], image->numRows);
+	    } else if (readdir == 2) {
+		psTrace(__func__, 9, "Image size: %dx%d\n", image->numCols, image->numRows);
+		ySize->data.S32[i] += image->numRows;
+		xSize->data.S32[i] = MAX(xSize->data.S32[i], image->numCols);
+	    } else {
+		psError(PS_ERR_IO, true, "Invalid read direction: %d\n", readdir);
+		// XXX Clean up before returning
+		return NULL;
+	    }
+
+	    psListIterator *overscansIter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false);
+	    psImage *overscan = NULL;	// Overscan image
+	    while (overscan = psListGetAndIncrement(overscansIter)) {
+
+		// Check image type
+		if (type == 0) {
+		    type = image->type.type;
+		} else if (image->type.type != type) {
+		    psError(PS_ERR_IO, true, "Trying to splice readouts with different image types "
+			    "(%d vs %d)\n", type, image->type.type);
+		    // XXX Clean up before returning
+		    return NULL;
+		}
+
+		// Get the size of the spliced image
+		if (readdir == 1) {
+		    psTrace(__func__, 9, "Overscan size: %dx%d\n", image->numCols, image->numRows);
+		    xSize->data.S32[i] += overscan->numCols;
+		    ySize->data.S32[i] = MAX(ySize->data.S32[i], overscan->numRows);
+		} else if (readdir == 2) {
+		    psTrace(__func__, 9, "Overscan size: %dx%d\n", image->numCols, image->numRows);
+		    ySize->data.S32[i] += overscan->numRows;
+		    xSize->data.S32[i] = MAX(xSize->data.S32[i], overscan->numCols);
+		}
+	    }
+
+	}
+    }
+    psFree(inCellsIter);
+    psFree(outCellsIter);
+
+    // Make sure all the readouts have the same size
+    int numRows = ySize->data.S32[0];	// Number of rows for spliced image
+    int numCols = xSize->data.S32[0];	// Number of columns for spliced image
+    for (int i = 1; i < numReadouts; i++) {
+	if (xSize->data.S32[i] != numCols || ySize->data.S32[i] != numRows) {
+	    psError(PS_ERR_IO, true, "Spliced readouts would have different sizes: %dx%d vs %dx%d\n",
+		    numCols, numRows, xSize->data.S32[i], ySize->data.S32[i]);
+	}
+    }
+
+    psTrace(__func__, 5, "Spliced image has dimensions %dx%d\n", numCols, numRows);
+
+    // Now we have done the requisite checks, and know the sizes; we just go through and splice together
+    for (int i = 0; i < numReadouts; i++) {
+	psImage *splice = psImageAlloc(numCols, numRows, type); // The spliced image
+
+	psListIterator *inCellsIter = psListIteratorAlloc(inCells, PS_LIST_HEAD, false);// Iterator for cells
+	papCell *inCell = NULL;		// Cell from the list of cells
+	psListIterator *outCellsIter = psListIteratorAlloc(outCells, PS_LIST_HEAD, false); // Iterator
+	int cellNum = 0;		// Cell number
+	int x0 = 0, y0 = 0;		// Position at which the splice begins
+	while (inCell = psListGetAndIncrement(inCellsIter)) {
+	    psArray *readouts = inCell->readouts; // The readouts comprising the cell
+	    papReadout *readout = readouts->data[i]; // The specific readout of interest
+	    psImage *image = readout->image; // The image pixels
+	    psList *overscans = readout->overscans; // The overscan regions
+	    papCell *outCell = psListGetAndIncrement(outCellsIter); // Output cell
+	    
+	    psImage *toSplice = psMemIncrRefCounter(image); // Image to splice in
+	    if (xFlip->data.U8[cellNum]) {
+		int size = 0;		// Size of flipped image
+		if (readdir == 1) {
+		    size = toSplice->numCols;
+		} else if (readdir == 2) {
+		    size = numCols;
+		}
+		psImage *temp = xFlipImage(toSplice, size);
+		psFree(toSplice);
+		toSplice = temp;
+	    }
+	    if (yFlip->data.U8[cellNum]) {
+		int size = 0;		// Size of flipped image
+		if (readdir == 1) {
+		    size = numRows;
+		} else if (readdir == 2) {
+		    size = toSplice->numRows;
+		}
+		psImage *temp = yFlipImage(toSplice, size);
+		psFree(toSplice);
+		toSplice = temp;
+	    }
+
+	    (void)psImageOverlaySection(splice, toSplice, x0, y0, "=");
+
+	    if (readdir == 1) {
+		x0 += toSplice->numCols;
+	    } else if (readdir == 2) {
+		y0 += toSplice->numRows;
+	    }
+
+	    // Update the TRIMSEC for the output cell
+	    psString trimsecString = psCellGetValueString(outCell, "CELL.TRIMSEC");
+	    psString trimsecMethod = NULL; // Method of determining trimsec
+	    psList *trimsecs = getRegions(&trimsecMethod, trimsecString); // List of trimsecs
+	    if (strncmp(trimsecMethod, "HEADER", 6) != 0 && strncmp(trimsecMethod, "HD", 2) != 0) {
+		psError(PS_ERR_IO, true, "Can only splice cells if the CELL.TRIMSEC (= %s) is determined "
+			"from the header.\n", trimsecString);
+		// XXX Clean up before returning
+		return NULL;
+	    }
+	    if (trimsecs->size != 1) {
+		psError(PS_ERR_IO, true, "Multiple headers specified for CELL.TRIMSEC: %s\n", trimsecString);
+		// XXX Clean up before returning
+		return NULL;
+	    }
+	    psString trimsecName = trimsecs->head->data; // Grab header for TRIMSEC off the list
+	    psFree(trimsecs);
+
+	    // TRIMSEC region; add 1 to get FITS standard
+	    psRegion trimsecRegion = {x0 + 1, x0 + toSplice->numCols, y0 + 1, y0 + toSplice->numRows};
+	    psString trimsecValue = psRegionToString(trimsecRegion);
+	    psMetadataAddStr(header, PS_LIST_TAIL, trimsecName, "TRIMSEC from pmFPAMorph", trimsecValue);
+	    // XXX Does psMetadataAddStr overwrite a previous value?
+
+	    // Repeat the above for each overscan (flip, overlay, update BIASSEC)
+	    // Prepare the BIASSEC
+	    psString biassecString = psCellGetValueString(outCell, "CELL.BIASSEC");
+	    psString biassecMethod = NULL; // Method of determining biassec
+	    psList *biassecs = getRegions(&biassecMethod, biassecString); // List of biassecs
+	    if (strncmp(biassecMethod, "HEADER", 6) != 0 && strncmp(biassecMethod, "HD", 2) != 0) {
+		psError(PS_ERR_IO, true, "Can only splice cells if the CELL.BIASSEC (= %s) is determined "
+			"from the header.\n", biassecString);
+		// XXX Clean up before returning
+		return NULL;
+	    }
+	    if (biassecs->size != overscans->size);
+
+	    psListIterator *overscansIter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false);
+	    psImage *overscan = NULL;	// Overscan from list
+	    psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false);
+	    psString biassecName = NULL;// FITS header keyword for biassec
+	    while ((overscan = psListGetAndIncrement(overscansIter)) &&
+		   (biassecName = psListGetAndIncrement(biassecsIter))) {
+		// Splice the overscan on
+		psImage *toSplice = psMemIncrRefCounter(overscan); // Image to splice in
+		if (xFlip->data.U8[cellNum]) {
+		    int size = 0;	// Size of flipped image
+		    if (readdir == 1) {
+			size = toSplice->numCols;
+		    } else if (readdir == 2) {
+			size = numCols;
+		    }
+		    psImage *temp = xFlipImage(toSplice, size);
+		    psFree(toSplice);
+		    toSplice = temp;
+		}
+		if (yFlip->data.U8[cellNum]) {
+		    int size = 0;	// Size of flipped image
+		    if (readdir == 1) {
+			size = numRows;
+		    } else if (readdir == 2) {
+			size = toSplice->numRows;
+		    }
+		    psImage *temp = yFlipImage(toSplice, size);
+		    psFree(toSplice);
+		    toSplice = temp;
+		}
+		
+		(void)psImageOverlaySection(splice, toSplice, x0, y0, "=");
+
+		if (readdir == 1) {
+		    x0 += toSplice->numCols;
+		} else if (readdir == 2) {
+		    y0 += toSplice->numRows;
+		}
+
+		// Update the header
+		psRegion biassecRegion = {x0 + 1, x0 + toSplice->numCols, y0 + 1, y0 + toSplice->numRows};
+		psString biassecValue = psRegionToString(biassecRegion);
+		psMetadataAddStr(header, PS_LIST_TAIL, biassecName, "BIASSEC from pmFPAMorph", biassecValue);
+	    }
+	    psFree(overscansIter);
+	    psFree(biassecsIter);
+	    psFree(biassecs);
+	} // Iterating over input cells
+
+	spliced->data[i] = splice;
+    } // Iterating over readouts
+
+    return spliced;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+bool pmFPAMorph(papFPA *toFPA,		// FPA structure to which to morph
+		papFPA *fromFPA,	// FPA structure from which to morph
+		int chipNum,		// Chip number, in case the scopes are different
+		int cellNum		// Cell number, in case the scopes are different
+    )
+{
+    psList *targetCells = psListAlloc(NULL); // List of target cells
+    psList *sourceCells = psListAlloc(NULL); // List of source cells
+
+    psImage *targetPixels = NULL;	// The target pixels
+    psMetadata *targetHeader = NULL;	// The target headers
+
+    psArray *toChips = toFPA->chips;	// Array of chips
+    psArray *fromChips = fromFPA->chips;// Array of chips    
+
+    // First, copy over the low-level representations.
+    for (int i = 0; i < toChips->n; i++) {
+	papChip *toChip = toChips->data[i];
+	papChip *fromChip = NULL;
+	// Select the correct chip
+	if (toChips->n == 1 && fromChips->n > chipNum) {
+	    fromChip = fromChips->data[chipNum];
+	} else if (fromChips->n == 1 && toChips->n > chipNum) {
+	    fromChip = fromChips->data[0];
+	} else if (toChips->n == fromChips->n) {
+	    fromChip = fromChips->data[i];
+	} else {
+	    psError(PS_ERR_IO, true, "Unable to discern intended chip.\n");
+	    return false;
+	}
+	
+	psArray *toCells = toChip->cells; // Array of cells
+	psArray *fromCells = fromChip->cells; // Array of cells
+
+	for (int j = 0; j < toCells->n; j++) {
+	    papCell *toCell = toCells->data[j];
+	    papCell *fromCell = NULL;
+	    // Select the correct cell
+	    if (toCells->n == 1 && fromCells->n > cellNum) {
+		fromCell = fromCells->data[cellNum];
+	    } else if (fromCells->n == 1 && toCells->n > cellNum) {
+		fromCell = fromCells->data[0];
+	    } else if (toCells->n == fromCells->n) {
+		fromCell = fromCells->data[j];
+	    } else {
+		psError(PS_ERR_IO, true, "Unable to discern intended cell.\n");
+		return false;
+	    }
+
+	    psTrace(__func__, 5, "Putting chip %d, cell %d in the bucket.\n", i, j);
+	    psListAdd(targetCells, PS_LIST_TAIL, toCell);
+	    psListAdd(sourceCells, PS_LIST_TAIL, fromCell);
+
+	    int xParityIn = psCellGetValueS32(fromCell, "CELL.XPARITY");
+	    int yParityIn = psCellGetValueS32(fromCell, "CELL.YPARITY");
+	    int xParityOut = psCellGetValueS32(toCell, "CELL.XPARITY");
+	    int yParityOut = psCellGetValueS32(toCell, "CELL.YPARITY");
+	    psTrace(__func__, 3, "Chip %d, cell %d: in (%d,%d) out (%d,%d)\n", i, j, xParityIn, yParityIn,
+		    xParityOut, yParityOut);
+
+	    // Copy the cell contents over
+	    toCell->readouts = psMemIncrRefCounter(fromCell->readouts);
+	    
+	    if (toCell->extname) {
+		// Splice the component cells
+		if (! toCell->header) {
+		    toCell->header = psMetadataAlloc();
+		}
+		toCell->pixels = spliceCells(toCell->header, targetCells, sourceCells);
+		// Purge the lists
+		while (psListRemove(targetCells, PS_LIST_TAIL));
+		while (psListRemove(sourceCells, PS_LIST_TAIL));
+	    }
+
+	}
+
+	if (toChip->extname) {
+	    // Splice the component cells
+	    if (! toChip->header) {
+		toChip->header = psMetadataAlloc();
+	    }
+	    toChip->pixels = spliceCells(toChip->header, targetCells, sourceCells);
+	    // Purge the lists
+	    while (psListRemove(targetCells, PS_LIST_TAIL));
+	    while (psListRemove(sourceCells, PS_LIST_TAIL));
+	}
+
+    }
+
+    return true;
+}
+
+
+
+
+
+
+
+#if 0
+
+
+
+
+	    // Change parity if necessary
+	    int toXParity = psCellGetValueS32(toCell, "CELL.XPARITY");
+	    int toYParity = psCellGetValueS32(toCell, "CELL.YPARITY");
+	    int fromXParity = psCellGetValueS32(toCell, "CELL.XPARITY");
+	    int fromYParity = psCellGetValueS32(toCell, "CELL.YPARITY");
+
+	    if (toXParity != fromXParity) {
+		for (int i = 0; i < readouts->n; i++) {
+		    papReadout *readout = readouts->data[i]; // The readout to be flipped
+
+		    // Flip the image
+		    psImage *temp = readout->image; // Temporary image
+		    readout->image = xFlipImage(temp);
+		    psFree(temp);
+
+		    // Flip each of the bias regions
+		    psListIterator *overscansIter = psListIteratorAlloc(readout->overscans, PS_LIST_HEAD,
+									false);	// Iterator for overscans
+		    psList *overscans = psListAlloc(void); // List of flipped overscans
+		    psImage *overscan = NULL; // Overscan to flip
+		    while (overscan = psListGetAndIncrement(overscansIter)) {
+			psImage *flipped = xFlipImage(overscan);
+			psListAdd(overscans, PS_LIST_TAIL, flipped);
+		    }
+		    // Replace the list with the list of flipped images
+		    psFree(readout->overscans);
+		    readout->overscans = overscans;
+		}
+
+	    }
+
+	    if (toYParity != fromYParity) {
+		for (int i = 0; i < readouts->n; i++) {
+		    papReadout *readout = readouts->data[i]; // The readout to be flipped
+
+		    // Flip the image
+		    psImage *temp = readout->image; // Temporary image
+		    readout->image = yFlipImage(temp);
+		    psFree(temp);
+
+		    // Flip each of the bias regions
+		    psListIterator *overscansIter = psListIteratorAlloc(readout->overscans, PS_LIST_HEAD,
+									false);	// Iterator for overscans
+		    psList *overscans = psListAlloc(void); // List of flipped overscans
+		    psImage *overscan = NULL; // Overscan to flip
+		    while (overscan = psListGetAndIncrement(overscansIter)) {
+			psImage *flipped = yFlipImage(overscan);
+			psListAdd(overscans, PS_LIST_TAIL, flipped);
+		    }
+		    // Replace the list with the list of flipped images
+		    psFree(readout->overscans);
+		    readout->overscans = overscans;
+		}
+
+	    }
+	    
+
+
+
+
+
+
+
+
+
+
+
+
+   psArray *pixels = NULL;		// Pixels to copy from lower levels
+    psMetadata *header = NULL;		// Header to copy from lower levels
+
+    psArray *toChips = toFPA->chips;	// Array of chips
+    psArray *fromChips = fromFPA->chips; // Array of chips
+
+    if (toFPA->extname) {
+	// Need to stick pixels in at the FPA level by splicing stuff at lower levels
+	toFPA->pixels = pixels;
+	toFPA->header = header;
+    } else {
+	for (int i = 0; i < toChips->n; i++) {
+	    papChip *toChip = toChips->data[i];
+	    papChip *fromChip = NULL;
+	    // Select the correct chip
+	    if (toChips->n == 1 && fromChips->n > chipNum) {
+		fromChip = fromChips->data[chipNum];
+	    } else if (fromChips->n == 1 && toChips->n > chipNum) {
+		fromChip = fromChips->data[0];
+	    } else if (toChips->n == fromChips->n) {
+		fromChip = fromChips->data[i];
+	    } else {
+		psError(PS_ERR_IO, true, "Unable to discern intended chip.\n");
+		return false;
+	    }
+	
+	    psArray *toCells = toChip->cells; // Array of cells
+	    psArray *fromCells = fromChip->cells; // Array of cells
+
+	    if (toChip->extname) {
+		// Need to stick pixels in at the chip level by splicing stuff at lower levels
+		toChip->pixels = pixels;
+		toChip->header = header;
+	    } else {
+
+		psList *toBeSpliced = psListAlloc(void); // List of cells to be spliced
+		int numReadouts = 0;	// Number of readouts in each cell
+		psVector *columns = psVectorAlloc(fromCells->n, PS_TYPE_U32); // Number of columns in cells
+		psVector *rows = psVectorAlloc(fromCells->n, PS_TYPE_U32); // Number of rows in cells
+
+		int splicedCols = 0;	// Number of columns in spliced image
+		int splicedRows = 0;	// Number of rows in spliced image
+		bool scanIsRows = true;	// Is the scan done in rows (true) or columns (false)
+		int numScans = 0;	// Number of scans (either in rows or columns)
+
+		for (int j = 0; j < toCells->n; j++) {
+		    
+		    papCell *toCell = toCells->data[i];
+		    papCell *fromCell = NULL;
+		    // Select the correct cell
+		    if (toCells->n == 1 && fromCells->n > cellNum) {
+			fromCell = fromCells->data[cellNum];
+		    } else if (fromCells->n == 1 && toCells->n > cellNum) {
+			fromCell = fromCells->data[0];
+		    } else if (toCells->n == fromCells->n) {
+			fromCell = fromCells->data[i];
+		    } else {
+			psError(PS_ERR_IO, true, "Unable to discern intended cell.\n");
+			return false;
+		    }
+
+		    if (toCell->extname) {
+			// Need to stick pixels in at the cell level
+			// Since the levels match, we can just stuff them in if the dimensions match
+			// Actually, don't worry about checking the dimensions: if the bias sections
+			// are hard-wired, then we will later throw an error if the dimensions don't match,
+			// and if they are based on a header, we will fix the header.
+			if (checkDimensions(toCell, fromCell)) {
+			    toCell->readouts = fromCell->readouts;
+			} else {
+			    psError(PS_ERR_IO, true, "Cell size mismatch (chip %d, cell %d)\n", i, j);
+			    continue;
+			}
+		    } else {
+			// Put it on the list to be spliced together
+
+			// Check the number of readouts corresponds
+			if (numReadouts == 0) {
+			    numReadouts = fromCell->readouts->n;
+			} else if (fromCell->readouts->n != numReadouts) {
+			    psError(PS_ERR_IO, true, "Determined that cells have %d readouts, but chip %d, "
+				    "cell %d has %d readouts --- ignored.\n", numReadouts, i, j,
+				    fromCell->readouts->n);
+			    continue;
+			}
+
+			papReadout *readout = fromCell->readouts->data[0]; // The first readout
+
+			columns->data.U32[k] = readout->image->numCols; // Number of columns in image
+			rows->data.U32[k] = readout->image->numRows; // Number of rows in image
+
+
+
+			if (numScans != 0) {
+			    // Work out the scan direction
+			    if (splicedCols
+
+
+			psVector *biasRows = psVectorAlloc(numReadouts, PS_TYPE_U32); // Number of bias rows
+			psVector *biasCols = psVectorAlloc(numReadouts, PS_TYPE_U32); // Number of bias columns
+
+			psList *overscans = readout->overscans; // List of overscans
+			psListIterator *overscanIter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false);
+			psImage *overscan = NULL; // Overscan image section
+			int k = 0;	// Overscan number
+			while (overscan = psListGetAndIncrement(overscanIter)) {
+			    if (k == 0) {
+				// On first overscan, decide which is the scan direction
+				if (overscan->numRows == imageRows) {
+				    // Scan done in columns
+				    scanIsRows = false;
+				} else if (overscan->numCols == imageCols) {
+				    // Scan done in rows
+				    scanIsRows = true;
+				} else {
+				    psLogMsg(__func__, PS_LOG_WARN, "Unable to determine relation between "
+					     "overscans and image for chip %d, cell %d.\n", i, j);
+				    // Assume that the scan is done in rows (it usually is)
+				}
+			    }
+
+			    if (scanIsRows) {
+				splicedRows += overscan->numCols;
+			    } else {
+				splicedCols += overscan->numRows;
+			    }
+
+			    k++;
+			}
+
+			// Should check that all readouts have the same size
+			
+			psListAdd(toBeSpliced, PS_LIST_TAIL, fromCell);
+		    }
+		}
+
+		// Having gone through all the cells in the lowest level, we can now
+		// splice the cells together
+		psListIterator *spliceIter = psListIteratorAlloc(toBeSpliced, PS_LIST_HEAD, true);
+		
+		while (
+		    
+
+}
+
+
+
+// Check the image regions
+static bool checkRegions(papCell *toCell, // Destination cell
+			 papCell *fromCell, // Source cell
+			 psMetadata *toHeader, // Destination header
+			 psMetadata *fromHeader	// Source header
+    )
+{
+    // Cases:
+    // 1. Destination regions are specified by headers:
+    //    In this case, set the Destination headers appropriately.
+    // 2. Destination regions are specified by values:
+    //    Barf.
+
+    // Note that none of this accounts for negative indices in the region, as is generally
+    // allowed in psLib.  This could be alleviated by using the psRegionForImage() function
+    // on the fromImage.
+
+    psString toTrimString = psCellGetValueString(toCell, "CELL.TRIMSEC"); // TRIMSEC from the TO
+    psString fromTrimString = psCellGetValueString(fromCell, "CELL.TRIMSEC"); // TRIMSEC from the FROM
+    psString toBiasString = psCellGetValueString(toCell, "CELL.BIASSEC"); // BIASSEC from the TO
+    psString fromTrimString = psCellGetValueString(fromCell, "CELL.BIASSEC"); // BIASSEC from the FROM
+
+    psString fromTrim = NULL;	// The actual TRIMSEC region in string format from the FROM
+
+    if (strncmp(toTrimString, "HEADER:", 7) == 0 || strncmp(toTrimString, "HD:", 3) {
+	// We don't want to bother with all the checking, so we don't allow this
+	return false;
+    }
+
+    if (strncmp(fromTrimString, "HEADER:", 7) == 0 || strncmp(fromTrimString, "HD:", 3) {
+	char *keyword = strchr(fromTrimString, ':') + 1;
+	bool mdOK = false;	// Result of MD lookup
+	fromTrim = psMetadataLookupString(&mdOK, fromHeader, keyword);
+	if (! mdOK) {
+	    panic();
+	}
+    } else if (strncmp(fromTrimString, "VALUE:", 6) == 0 || strncmp(fromTrimString, "VAL:", 4) {
+	char *value = strchr(fromTrimString, ':') + 1;
+	fromTrim = psStringCopy(value);
+    }
+	       
+	       if (strncmp(toTrimString, "HEADER:", 7) == 0 || strncmp(toTrimString, "HD:", 3) {
+		   char *keyword = strchr(toTrimString, ':') + 1;
+		   psMetadataAdd(toHeader, PS_LIST_TAIL, keyword, PS_META_STR | PS_META_REPLACE,
+				 "Trimsec region from pmFPAMorph", fromTrim);
+	       } else if (strncmp(toTrimString, "VALUE:", 6) == 0 || strncmp(toTrimString, "VAL:", 4) {
+		   char *toTrim = strchr(toTrimString, ':') + 1;
+		   psRegion *toRegion = psRegionFromString(toTrim);
+		   psRegion *fromRegion = psRegionFromString(fromTrim);
+		   if (toRegion.x1 - toRegion.x0 != fromRegion.x1 - fromRegion.x0 ||
+		       toRegion.y1 - toRegion.y0 != fromRegion.y1 - fromRegion.y0) {
+		       psError(PS_ERR_IO, true, "Cell size of output is already specified, but does not match "
+			       "that of the input (chip %d, cell%d: %dx%d vs %dx%d\n", i, j,
+			       toRegion.x1 - toRegion.x0, toRegion.y1 - toRegion.y0,
+			       fromRegion.x1 - fromRegion.x0, fromRegion.y1 - fromRegion.y0);
+		       return false;
+		   }
+	       }
+		    
+
+
+			  }
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAMorph.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAMorph.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAMorph.h	(revision 22322)
@@ -0,0 +1,12 @@
+#ifndef PM_FPA_MORPH_H
+#define PM_FPA_MORPH_H
+
+// Morph one focal plane representation into another
+bool pmFPAMorph(papFPA *toFPA,		// FPA structure to which to morph
+		papFPA *fromFPA,	// FPA structure from which to morph
+		int chipNum,		// Chip number, in case the scopes are different
+		int cellNum		// Cell number, in case the scopes are different
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPARead.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPARead.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPARead.c	(revision 22322)
@@ -0,0 +1,291 @@
+#include <stdio.h>
+#include <string.h>
+#include "pslib.h"
+#include "papmodule.h"
+#include "papStuff.h"
+#include "papFocalPlane.h"
+#include "pmFPARead.h"
+
+// NOTE: Need to deal with header inheritance
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Read a FITS extension into a chip
+static bool readExtension(psArray **images, // Array of images in which to read pixels
+			  psMetadata **header, // Metadata in which to read header
+			  psFits *fits,	// The FITS file from which to read
+			  const char *extName // The name of the extension
+    )
+{
+    psTrace(__func__, 7, "Moving to extension %s...\n", extName);
+    if (strncmp(extName, "PHU", 3) == 0) {
+	if (!psFitsMoveExtNum(fits, 0, false)) {
+	    psError(PS_ERR_IO, false, "Unable to find PHU in FITS file!\n");
+	    return false;
+	}
+    } else if (! psFitsMoveExtName(fits, extName)) {
+	psError(PS_ERR_IO, false, "Unable to find extension %s in FITS file!\n", extName);
+	return false;
+    }
+    psTrace(__func__, 7, "Reading header....\n");
+    *header = psFitsReadHeader(*header, fits);
+    if (! *header) {
+	psError(PS_ERR_IO, false, "Unable to read FITS header!\n");
+	return false;
+    }
+
+    psTrace(__func__, 7, "Checking NAXIS....\n");
+    bool mdStatus = false;
+    int nAxis = psMetadataLookupS32(&mdStatus, *header, "NAXIS");
+    if (!mdStatus) {
+	psLogMsg(__func__, PS_LOG_WARN, "There is no NAXIS keyword in the FITS header of extension %s!\n",
+		 extName);
+    }
+    if (nAxis != 2 && nAxis != 3) {
+	psLogMsg(__func__, PS_LOG_WARN, "Image is not 2- or 3-dimensional --- reading into a single image "
+		 "anyway.\n");
+    }
+    psTrace(__func__, 9, "NAXIS = %d\n", nAxis);
+
+    int numPlanes = 1;			// Number of planes
+    if (nAxis == 3) {
+	numPlanes = psMetadataLookupS32(&mdStatus, *header, "NAXIS3");
+	if (!mdStatus) {
+	    psError(PS_ERR_IO, false, "Unable to read NAXIS3 for 3-dimensional image!\n");
+	    return false;
+	}
+    }
+    psRegion region = {0, 0, 0, 0};	// Region to read is everything
+
+    // Read each plane into the array
+    psTrace(__func__, 7, "Reading %d planes into array....\n", numPlanes);
+    *images = psArrayAlloc(numPlanes); // Array of images
+    for (int i = 0; i < numPlanes; i++) {
+	psTrace(__func__, 9, "Reading plane %d\n", i);
+	psMemCheckCorruption(true);
+	(*images)->data[i] = psFitsReadImage(NULL, fits, region, i);
+	psTrace(__func__, 10, "Done\n");
+        if (! (*images)->data[i]) {
+	    psError(PS_ERR_IO, false, "Unable to read image for extension %s, plane %d\n", extName, i);
+	    return false;
+	}
+    }
+
+    return true;
+}
+
+// Read a list of regions (e.g., [x0:x1,y0:y1];[x2:x3,y2:y3]...), extract subimages and put them on a list
+static int readMultipleRegions(psList *list, // List to which to add images
+			       const char *string, // String from which to read regions.
+			       psImage *image // Image from which to get the region
+    )
+{
+    int numFound = 0;			// Number of regions found
+    char *stringCopy = psStringCopy(string); // Copy of the string (to preserve it intact)
+    char *startOfRegion = stringCopy; // The string containing all the regions yet to be parsed
+    while (startOfRegion) {
+	psRegion region = psRegionFromString(startOfRegion); // The region
+	if (isnan(region.x0) || isnan(region.x1) || isnan(region.y0) || isnan(region.y1)) {
+	    psLogMsg(__func__, PS_LOG_WARN, "Unable to read region (%s) --- ignored.\nOriginal string was "
+		     "%s.", startOfRegion, string);
+	} else {
+	    psTrace(__func__, 9, "region found: %f,%f --> %f,%f\n", region.x0, region.y0, region.x1,
+		    region.y1);
+	    numFound++;
+	    // Convert from FITS standard to PS standard
+	    region.x0 -= 1;
+	    region.y0 -= 1;
+	    // We don't touch the x1 and y1 values because they aren't included by psImageSubset.
+	    psImage *subImage = psImageSubset(image, region);
+	    psListAdd(list, PS_LIST_TAIL, subImage);
+	}
+
+	startOfRegion = strchr(startOfRegion, ';');
+	if (startOfRegion) {
+	    // Eat the semi-colon
+	    startOfRegion += 1;
+	}
+    }
+    psFree(stringCopy);
+
+    return numFound;
+}
+
+
+// Return a list of portions
+static psList *portions(psImage *image,	// The image to portion up
+			const char *methodValue, // How to determine the portions
+			psMetadata *header // FITS header from the file
+    )
+{
+    psList *subImages = psListAlloc(NULL); // The list of subimages, to be returned
+
+    psTrace(__func__, 7, "Parsing %s to get subimages....\n", methodValue);
+    // Parse the methodValue string into the METHOD:VALUE (e.g., HEADER:TRIMSEC, or VALUE:[1:100,1:100])
+    char *value = strchr(methodValue, ':') + 1; // Find the first colon, which is the split character
+    if (! value) {
+	psError(PS_ERR_IO, true, "Unable to separate string (%s) into METHOD:VALUE\n", methodValue);
+	return false;
+    }
+    psTrace(__func__, 7, "Value is %s\n", value);
+
+    if (strncmp(methodValue, "HEADER:", 7) == 0 || strncmp(methodValue, "HD:", 3) == 0) {
+	// Read the portions from the header
+	psList *keywords = papSplit(value, ",;"); // List of headers to look at
+	psListIterator *keywordsIter = psListIteratorAlloc(keywords, PS_LIST_HEAD, false); // Iterator
+	psString keyword = NULL;	// The header keyword
+	bool mdStatus = false;		// Status for MD lookup
+	while (keyword = psListGetAndIncrement(keywordsIter)) {
+	    psString regionString = psMetadataLookupString(&mdStatus, header, keyword);
+	    if (! mdStatus) {
+		psLogMsg(__func__, PS_LOG_WARN, "Unable to find header %s to determine image region --- "
+			 "ignored.\n", keyword);
+	    } else {
+		// We have the region: [x0:x1,y0:y1]
+		psRegion region = psRegionFromString(regionString);
+		// Convert from FITS standard to PS standard
+		region.x0 -= 1;
+		region.y0 -= 1;
+		// We don't touch the x1 and y1 values because they aren't included by psImageSubset.
+		psImage *subImage = psImageSubset(image, region);
+		psListAdd(subImages, PS_LIST_TAIL, subImage);
+	    }
+	}
+    } else if (strncmp(methodValue, "VALUE:", 6) == 0 || strncmp(methodValue, "VAL:", 4) == 0) {
+	// Read the portions right here
+	if (readMultipleRegions(subImages, value, image) == 0) {
+	    psLogMsg(__func__, PS_LOG_WARN, "No value specified: %s --- ignored.\n", methodValue);
+	}
+    } else {
+	// Try to interpret the methodValue as a "value" only (ie, no "HEADER:" or "VALUE:")
+	// Otherwise, we give up
+	if (readMultipleRegions(subImages, methodValue, image) == 0) {
+	    psLogMsg(__func__, PS_LOG_WARN, "Can't parse %s into a region --- ignored.\n", methodValue);
+	}
+    }
+
+    psTrace(__func__, 7, "%d regions found.\n", subImages->size);
+
+    return subImages;
+}
+
+// Portion out an image into the cell
+static bool portionCell(papCell *cell,	// The cell that gets its bits
+			psArray *images, // Array of images from which the readouts are assigned
+			psMetadata *header // FITS header from the file
+    )
+{
+    bool mdStatus = false;		// Status of MD lookup
+
+    // How do we get the trim section?
+    psString trimsecString = psMetadataLookupString(&mdStatus, cell->values, "CELL.TRIMSEC");
+    if (! mdStatus) {
+	psError(PS_ERR_IO, false, "Unable to find CELL.TRIMSEC specified --- unable to read cell.\n");
+	return false;
+    }
+
+    // How do we get the bias section(s)?
+    psString biassecString = psMetadataLookupString(&mdStatus, cell->values, "CELL.BIASSEC");
+    if (! mdStatus) {
+	psError(PS_ERR_IO, false, "Unable to find CELL.BIASSEC specified --- unable to read cell.\n");
+	return false;
+    }
+
+    // Iterate over each of the image planes
+    psArray *readouts = psArrayAlloc(images->n);
+    for (int i = 0; i < images->n; i++) {
+	// Get the TRIMSEC region
+	psList *trimsecs = portions(images->data[i], trimsecString, header);
+	if (trimsecs->size == 0) {
+	    psError(PS_ERR_IO, true, "No TRIMSEC region found for image.  Specified: %s\n", trimsecString);
+	    readouts->data[i] = NULL;
+	} else {
+	    // There can be only ONE!
+	    if (trimsecs->size > 1) {
+		psLogMsg(__func__, PS_LOG_WARN, "Multiple TRIMSECs were specified --- using only the first."
+			 "\n");
+	    }
+	    psImage *trimsec = psListGet(trimsecs, PS_LIST_HEAD);
+	    psFree(trimsecs);
+
+	    // Get the BIASSEC regions
+	    psList *biassecs = portions(images->data[i], biassecString, header);
+	    if (biassecs->size == 0) {
+		psLogMsg(__func__, PS_LOG_WARN, "No BIASSEC region found for image.  Specified: %s\n",
+			 biassecString);
+	    }
+
+	    papReadout *readout = papReadoutAlloc(cell, i, trimsec, biassecs, 0,0,0,0,1,1);
+	    readouts->data[i] = readout;
+	}
+
+    }
+
+    // Stuff the array of readouts into the cell
+    cell->readouts = readouts;
+
+    return true;
+
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmFPARead(papFPA *fpa,		// FPA to read into
+	       psFits *fits		// FITS file from which to read
+    )
+{
+    psArray *pixels = NULL;		// Current pixels from FITS file (array of images)
+    psMetadata *header = NULL;		// Current header from FITS file
+
+    psTrace(__func__, 1, "Working on FPA...\n");
+    if (fpa->extname) {
+	psTrace(__func__, 3, "Reading pixels from extension %s into FPA.\n", fpa->extname);
+	if (! readExtension(&fpa->pixels, &fpa->header, fits, fpa->extname)) {
+	    psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n", fpa->extname);
+	    return false;
+	}
+	pixels = fpa->pixels;
+	header = fpa->header;
+    }
+
+    psArray *chips = fpa->chips;	// Array of chips
+    // Iterate over the FPA
+    for (int i = 0; i < chips->n; i++) {
+	papChip *chip = chips->data[i]; // The chip
+	psTrace(__func__, 2, "Working on chip %d...\n", i);
+	if (chip->extname) {
+	    psTrace(__func__, 3, "Reading pixels from extension %s into chip %d.\n", chip->extname, i);
+	    if (! readExtension(&chip->pixels, &chip->header, fits, chip->extname)) {
+		psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n", chip->extname);
+		return false;
+	    }
+	    pixels = chip->pixels;
+	    header = chip->header;
+	}
+	// Iterate over the chip
+	psArray *cells = chip->cells;	// Array of cells
+	for (int j = 0; j < cells->n; j++) {
+	    papCell *cell = cells->data[j]; // The cell
+	    psTrace(__func__, 3, "Working on cell %d...\n", j);
+	    if (cell->extname) {
+		psTrace(__func__, 5, "Reading pixels from extension %s into cell %d.\n", cell->extname, j);
+		if (! readExtension(&cell->pixels, &cell->header, fits, cell->extname)) {
+		    psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n", cell->extname);
+		    return false;
+		}
+		pixels = cell->pixels;
+		header = cell->header;
+	    }
+
+	    psTrace(__func__, 5, "Allocating readouts and extracting overscans etc for chip %d cell %d...\n",
+		    i, j);
+	    portionCell(cell, pixels, header);
+	}
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPARead.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPARead.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPARead.h	(revision 22322)
@@ -0,0 +1,10 @@
+#ifndef PM_FPA_READ_H
+#define PM_FPA_READ_H
+
+#include "papFocalPlane.h"
+
+bool pmFPARead(papFPA *fpa,		// FPA to read into
+	       psFits *fits		// FITS file from which to read
+	       );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAWrite.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAWrite.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAWrite.c	(revision 22322)
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include "pslib.h"
+#include "papmodule.h"
+
+static bool writePixels(psFits *fits,	// FITS handle
+			psMetadata *header, // FITS header
+			const psArray *pixels,// Array of images
+			const char *extname // Extension name
+			)
+{
+    // Check NAXIS 3
+    if (pixels->n > 1) {
+	psMetadataAdd(header, PS_LIST_TAIL, "NAXIS3", PS_META_S32 | PS_META_REPLACE, "Added by pmFPAWrite",
+		      pixels->n);
+    }
+
+    (void)psFitsMoveExtName(fits, extname); // Try to move, even if it fails
+    for (int i = 0; i < pixels->n; i++) {
+	if (! psFitsWriteImage(fits, header, pixels->data[i], i)) {
+	    psError(PS_ERR_IO, false, "Unable to write plane %d to %s.\n", i, extname);
+	    return false;
+	}
+    }
+    psFitsSetExtName(fits, extname);
+
+    return true;
+}
+
+
+
+bool pmFPAWrite(psFits *fits,		// FITS file to which to write
+		papFPA *fpa 		// FPA to write
+		)
+{
+    psArray *chips = fpa->chips;	// Array of chips
+
+    for (int i = 0; i < chips->n; i++) {
+	papChip *chip = chips->data[i];	// Chip of interest
+	psArray *cells = chip->cells;	// Array of cells
+
+	if (chip->extname) {
+	    if (! writePixels(fits, chip->header, chip->pixels, chip->extname)) {
+		return false;
+	    }
+	}
+
+	for (int j = 0; j < cells->n; j++) {
+	    papCell *cell = cells->data[j]; // Cell of interest
+
+	    if (cell->extname) {
+		if (!writePixels(fits, cell->header, cell->pixels, cell->extname)) {
+		    return false;
+		}
+	    }
+	}
+    }
+    
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAWrite.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAWrite.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAWrite.h	(revision 22322)
@@ -0,0 +1,8 @@
+#ifndef PM_FPA_WRITE_H
+#define PM_FPA_WRITE_H
+
+bool pmFPAWrite(psFits *fits,		// FITS file to which to write
+		papFPA *fpa 		// FPA to write
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAio.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAio.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/pmFPAio.c	(revision 22322)
@@ -0,0 +1,20 @@
+# include <pslib.h>
+
+main (int argc, char **argv) {
+
+  psArray headers;
+  psMetadata *base, *site, *camera, *recipe;
+
+  pmConfigLoadBasic (&site, &base);
+
+  fits = psFitsAlloc (input);
+  header = psFitsReadHeader (fits);
+  pmConfigLoadCamera (&camera, site, header);
+
+  pmConfigLoadRecipe (&recipe, camera, "PHASE2");
+
+  // we've loaded the PHU, determined the camera (and recipe).
+
+  fpa = psFPAAlloc ();
+
+  // is this an image? 
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/poly2dtest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/poly2dtest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/poly2dtest.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include "psphot.h"
+# include <stdlib.h>
+double drand48(void);
+
+int main (int argc, char **argv) {
+
+  int i, j, N;
+  psPolynomial2D *poly;
+
+  srand48(0);
+
+  // *** test 2D fit without errors  
+  { 
+    psVector *x  = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *y  = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *z  = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *dz = NULL;
+
+    N = 0;
+    for (i = 0; i < 50; i++) {
+      for (j = 0; j < 20; j++, N++) {
+	x->data.F64[N] = i;
+	y->data.F64[N] = j;
+	z->data.F64[N] = 3 + 5*i - 2*j;
+      }
+    }
+  
+    // use the code in psUtils.c
+    poly = Polynomial2DAlloc (1, 1, PS_POLYNOMIAL_ORD);
+    poly = VectorFitPolynomial2DOrd_EAM (poly, x, y, z, dz);
+
+    for (i = 0; i < poly->nX + 1; i++) {
+      for (j = 0; j < poly->nY + 1; j++) {
+	fprintf (stderr, "x^%d y^%d : %6.3f +/- %6.3f\n", i, j, poly->coeff[i][j], poly->coeffErr[i][j]);
+      }
+    }
+  }
+
+
+  // *** test 2D fit with errors  
+  { 
+    psVector *x  = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *y  = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *z  = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *dz = psVectorAlloc (1000, PS_TYPE_F64);
+
+    N = 0;
+    for (i = 0; i < 50; i++) {
+      for (j = 0; j < 20; j++, N++) {
+	x->data.F64[N] = i;
+	y->data.F64[N] = j;
+	dz->data.F64[N] = 1*(drand48() - 0.5);
+	z->data.F64[N] = 3 + 5*i - 2*j + dz->data.F64[N];
+      }
+    }
+  
+    // use the code in psUtils.c
+    poly = Polynomial2DAlloc (2, 2, PS_POLYNOMIAL_ORD);
+    poly = VectorFitPolynomial2DOrd_EAM (poly, x, y, z, dz);
+
+    for (i = 0; i < poly->nX; i++) {
+      for (j = 0; j < poly->nY; j++) {
+	fprintf (stderr, "x^%d y^%d : %f +/- %f\n", i, j, poly->coeff[i][j], poly->coeffErr[i][j]);
+      }
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/polytest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/polytest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/polytest.c	(revision 22322)
@@ -0,0 +1,73 @@
+# include "psphot.h"
+# include <stdlib.h>
+double drand48(void);
+
+int main (int argc, char **argv) {
+
+  int i;
+  psPolynomial1D *poly;
+
+  // *** test 1D fit without errors  
+  { 
+    psVector *x  = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *y  = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *dy = NULL;
+    psVector *dy2 = NULL;
+
+    srand48(0);
+
+    for (i = 0; i < 1000; i++) {
+      x->data.F64[i] = i;
+      y->data.F64[i] = 3 + 5*i - 2*i*i +0.4*i*i*i;
+    }
+  
+    // use the code in psUtils.c
+    poly = psPolynomial1DAlloc (4, PS_POLYNOMIAL_ORD);
+    poly = VectorFitPolynomial1DOrd_EAM (poly, x, y, dy);
+
+    for (i = 0; i < poly->n; i++) {
+      fprintf (stderr, "x^%d : %f +/- %f\n", i, poly->coeff[i], poly->coeffErr[i]);
+    }
+  
+    // use the code in psMinimize.c
+    poly = psPolynomial1DAlloc (4, PS_POLYNOMIAL_ORD);
+    poly = VectorFitPolynomial1DOrd (poly, x, y, dy2);
+
+    for (i = 0; i < poly->n; i++) {
+      fprintf (stderr, "x^%d : %f +/- %f\n", i, poly->coeff[i], poly->coeffErr[i]);
+    }
+  }
+
+  // *** test 1D fit with errors  
+  {
+    psVector *x  = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *y  = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *dy = psVectorAlloc (1000, PS_TYPE_F64);
+    psVector *dy2 = psVectorAlloc (1000, PS_TYPE_F64);
+
+    srand48(0);
+
+    for (i = 0; i < 1000; i++) {
+      x->data.F64[i] = i;
+      dy->data.F64[i] = 10*(drand48() - 0.5);
+      dy2->data.F64[i] = PS_SQR(dy->data.F64[i]);
+      y->data.F64[i] = 3 + 5*i - 2*i*i +0.4*i*i*i + dy->data.F64[i];
+    }
+  
+    // use the code in psUtils.c
+    poly = psPolynomial1DAlloc (4, PS_POLYNOMIAL_ORD);
+    poly = VectorFitPolynomial1DOrd_EAM (poly, x, y, dy);
+
+    for (i = 0; i < poly->n; i++) {
+      fprintf (stderr, "x^%d : %f +/- %f\n", i, poly->coeff[i], poly->coeffErr[i]);
+    }
+
+    // use the code in psMinimize.c
+    poly = psPolynomial1DAlloc (4, PS_POLYNOMIAL_ORD);
+    poly = VectorFitPolynomial1DOrd (poly, x, y, dy2);
+
+    for (i = 0; i < poly->n; i++) {
+      fprintf (stderr, "x^%d : %f +/- %f\n", i, poly->coeff[i], poly->coeffErr[i]);
+    }
+  }
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/psAdditionals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/psAdditionals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/psAdditionals.c	(revision 22322)
@@ -0,0 +1,172 @@
+#include <stdio.h>
+#include <string.h>
+#include "pslib.h"
+#include "papmodule.h"
+
+
+psMetadata *psMetadataLookupMD(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    psMetadata *value = NULL;		// The value to return
+    if (!item) {
+	// The given key isn't in the metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+	}
+    } else if (item->type != PS_META_META) {
+	// The value at the key isn't metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_META_META, as expected.\n");
+	}
+	value = NULL;
+    } else {
+	// We have the requested metadata
+	if (status) {
+	    *status = true;
+	}
+	value = item->data.md; // The requested metadata
+    }
+    return value;
+}
+
+
+char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    char *value = NULL;			// The value to return
+    if (!item) {
+	// The given key isn't in the metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+	}
+    } else if (item->type != PS_META_STR) {
+	// The value at the key isn't of the desired type
+	if (status) {
+	    *status = false;
+	} else {
+	    psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_META_STR, as expected.\n");
+	}
+	value = NULL;
+    } else {
+	// We have the requested metadata
+	if (status) {
+	    *status = true;
+	}
+	value = item->data.V; // The requested metadata
+    }
+    return value;
+}
+
+papChip *psMetadataLookupChip(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    papChip *value = NULL;		// The value to return
+    if (!item) {
+	// The given key isn't in the metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+	}
+    } else if (item->type != PS_META_CHIP) {
+	// The value at the key isn't of the desired type
+	if (status) {
+	    *status = false;
+	} else {
+	    psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_META_CHIP, as expected.\n");
+	}
+	value = NULL;
+    } else {
+	// We have the requested metadata
+	if (status) {
+	    *status = true;
+	}
+	value = item->data.V; // The requested metadata
+    }
+    return value;
+}
+
+papCell *psMetadataLookupCell(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    papCell *value = NULL;		// The value to return
+    if (!item) {
+	// The given key isn't in the metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+	}
+    } else if (item->type != PS_META_CELL) {
+	// The value at the key isn't of the desired type
+	if (status) {
+	    *status = false;
+	} else {
+	    psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_META_CHIP, as expected.\n");
+	}
+	value = NULL;
+    } else {
+	// We have the requested metadata
+	if (status) {
+	    *status = true;
+	}
+	value = item->data.V; // The requested metadata
+    }
+    return value;
+}
+
+void psMetadataPrint(psMetadata *md, int level)
+{
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);	// Iterator
+    psMetadataItem *item = NULL;	// Item from metadata
+    while (item = psMetadataGetAndIncrement(iter)) {
+	// Indent...
+	for (int i = 0; i < level; i++) {
+	    printf(" ");
+	}
+	printf("%s", item->name);
+	if (item->comment && strlen(item->comment) > 0) {
+	    printf(" (%s)", item->comment);
+	}
+	printf(": ");
+	switch (item->type) {
+	  case PS_META_STR:
+	    printf("%s", item->data.V);
+	    break;
+	  case PS_META_BOOL:
+	    if (item->data.B) {
+		printf("True");
+	    } else {
+		printf("False");
+	    }
+	    break;
+	  case PS_META_S32:
+	    printf("%d", item->data.S32);
+	    break;
+	  case PS_META_F32:
+	    printf("%f", item->data.F32);
+	    break;
+	  case PS_META_F64:
+	    printf("%f", item->data.F64);
+	    break;
+	  case PS_META_META:
+	    printf("\n");
+	    psMetadataPrint(item->data.V, level + 1);
+	    break;
+	  default:
+	    psError(PS_ERR_IO, false, "Non-printable metadata type: %x\n", item->type);
+	}
+	printf("\n");
+    }
+    psFree(iter);
+
+    return;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/psAdditionals.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/psAdditionals.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/psAdditionals.h	(revision 22322)
@@ -0,0 +1,19 @@
+#ifndef PS_ADDITIONALS_H
+#define PS_ADDITIONALS_H
+
+#include "pslib.h"
+
+// Get a value from the metadata that we believe should be metadata.
+psMetadata *psMetadataLookupMD(bool *status, const psMetadata *md, const char *key);
+
+// Get a value from the metadata that we believe should be a string
+char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key);
+
+papChip *psMetadataLookupChip(bool *status, const psMetadata *md, const char *key);
+papCell *psMetadataLookupCell(bool *status, const psMetadata *md, const char *key);
+
+
+// Print out the metadata
+void psMetadataPrint(psMetadata *md, int level);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/test_pmFPARead.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/test_pmFPARead.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/test_pmFPARead.c	(revision 22322)
@@ -0,0 +1,131 @@
+#include <stdio.h>
+#include "pslib.h"
+#include "papmodule.h"
+#include "papFocalPlane.h"
+
+psMemoryId memPrint(const psMemBlock *ptr)
+{
+    fprintf(stderr, "File: %s\n", ptr->file);
+    fprintf(stderr, "Memory block %qd:\n"
+	    "\tFile %s, line %d, size %d\n"
+	    "\tRefCounter: %qd\n"
+	    "\tPosts: %x %x %x\n",
+	    ptr->id, ptr->file, ptr->lineno, ptr->userMemorySize, ptr->refCounter,
+	    ptr->startblock, ptr->endblock, *(psPtr *)((int8_t *) (ptr + 1) + ptr->userMemorySize));
+    return 0;
+}
+ 
+int main(int argc, char *argv[])
+{
+#if 0
+    psMemAllocateCallbackSet(memPrint);
+    psMemAllocateCallbackSetID(921);
+    psMemFreeCallbackSet(memPrint);
+    psMemFreeCallbackSetID(921);
+#endif
+
+    (void)psTraceSetLevel(".", 0);
+    (void)psTraceSetLevel("readExtension", 10);
+    (void)psTraceSetLevel("readMultipleRegions", 0);
+    (void)psTraceSetLevel("portions", 0);
+    (void)psTraceSetLevel("pmFPAPrint", 10);
+    (void)psTraceSetLevel("pmFPAfromHeader", 10);
+    (void)psTraceSetLevel("pmCameraFromHeader", 10);
+    (void)psTraceSetLevel("pmFPAMorph", 10);
+    (void)psTraceSetLevel("spliceCells", 10);
+
+
+    if (argc != 3) {
+	printf("Usage: %s IPP_CONFIG IMAGE\n", argv[0]);
+	exit(EXIT_FAILURE);
+    }
+    const char *ipprcName = argv[1];
+    const char *imageName = argv[2];
+
+    int badLines = 0;			// Number of bad lines in camera configuration
+    psMetadata *ipprc = psMetadataConfigParse(NULL, &badLines, ipprcName, true); // Read IPP config file
+    if (badLines > 0) {
+	psLogMsg(__func__, PS_LOG_WARN, "%d bad lines encountered while reading IPP configuration %s\n",
+		 badLines, ipprcName);
+    }
+
+    psFits *fits = psFitsAlloc(imageName);
+    psMetadata *header = psFitsReadHeader(NULL, fits);
+
+    psMetadata *camera = pmCameraFromHeader(header, ipprc);
+
+    papFPA *fpa = pmFPAConstruct(camera, NULL);
+
+    // Cut off a lot of the chips so I can fit things in memory
+    {
+	psArray *chips = fpa->chips;
+	papChip *chip = chips->data[0];
+	psFree(chips);
+	chips = psArrayAlloc(1);
+	chips->data[0] = chip;
+    }
+
+    (void)pmFPARead(fpa, fits);
+    (void)pmFPAPrint(fpa);
+
+    // Morph to a splice image
+    psMetadata *newCamera = psMetadataConfigParse(NULL, &badLines, "megacam_splice.config", true);
+    papFPA *newfpa = pmFPAConstruct(newCamera, NULL);
+
+    // Cut off a lot of the chips so I can fit things in memory
+    {
+	psArray *chips = newfpa->chips;
+	papChip *chip = chips->data[0];
+	psFree(chips);
+	chips = psArrayAlloc(1);
+	chips->data[0] = chip;
+    }
+
+    pmFPAPrint(newfpa);
+    pmFPAMorph(newfpa, fpa, 0, 0);
+    pmFPAPrint(newfpa);
+
+#if 0
+    // Write out
+    psFits *newfits = psFitsAlloc("test.fits");
+    pmFPAWrite(fits, newfpa);
+    psFree(newfits);
+#endif
+
+#if 1
+    psArray *chips = newfpa->chips;
+    for (int i = 0; i < chips->n; i++) {
+	papChip *chip = chips->data[i];	// Chip of interest
+	psArray *cells = chip->cells;	// Array of cells
+
+	if (chip->extname) {
+	    psFits *fp = psFitsAlloc(chip->extname);
+	    psFitsWriteImage(fp, chip->header, chip->pixels->data[i], i);
+	    psFree(fp);
+	}
+
+	for (int j = 0; j < cells->n; j++) {
+	    papCell *cell = cells->data[j]; // Cell of interest
+
+	    if (cell->extname) {
+		psFits *fp = psFitsAlloc(cell->extname);
+		psFitsWriteImage(fp, cell->header, cell->pixels->data[i], i);
+		psFree(fp);
+	    }
+	}
+    }
+#endif
+
+    psFree(newfpa);
+    psFree(newCamera);
+
+    // Tidy up
+    psMemCheckCorruption(true);
+
+    psFree(camera);
+    psFree(fits);
+
+    psFree(fpa);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/scripts/src/test_pmFPAfromHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/scripts/src/test_pmFPAfromHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/scripts/src/test_pmFPAfromHeader.c	(revision 22322)
@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include "pslib.h"
+#include "psmodule.h"
+
+int main(int argc, char *argv[])
+{
+    if (argc != 2) {
+	printf("Usage: %s CONFIG\n", argv[0]);
+	exit(EXIT_FAILURE);
+    }
+    const char *configName = argv[1];
+	
+    int badLines = 0;			// Number of bad lines in camera configuration
+
+    (void)psTraceSetLevel("pmFPAfromHeader", 10);
+
+    psMetadata *camera = psMetadataParseConfig(NULL, &badLines, configName, true); // Camera config
+
+    if (badLines > 0) {
+	printf("%d bad lines encountered while reading %s\n", badLines, configName);
+    }
+
+    //psMetadataPrint(camera, 0);
+   
+    psMetadata *fpa = pmFPAfromHeader(camera);
+
+    psFree(camera);
+    psFree(fpa);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/archive/utils/check-namespace
===================================================================
--- /tags/sj_tags/sj_root_20080929/archive/utils/check-namespace	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/archive/utils/check-namespace	(revision 22322)
@@ -0,0 +1,309 @@
+#!/usr/bin/perl -w
+#
+# Check that a set of .h files and .o and .a files satisfy the Pan-STARRS
+# rules about namespaces.
+#
+# This is a real hack; it should be rewritten to parse the .h files correctly. XXX
+#
+# Robert Lupton (rhl@astro.princeton.edu)  February 2004
+#
+# Parse arguments
+#
+$nm_opts = "";
+$prefix = "ps";
+$pprefix = "p_$prefix";
+$verbose = 0;
+
+if (!@ARGV) {
+   warn "Please specify some files to check! E.g. *.h *.o\n";
+   push @ARGV, "-h";
+}
+
+while ($ARGV[0] =~ /^-/) {
+   if ($ARGV[0] eq "-h" || $ARGV[0] eq "-?") {
+      warn "\
+Usage: check-namespace [options] files.h files.o
+
+Check that a set of .h and .o (and/or .a) files obey the pan-STARRS
+naming convention, namely that iff a symbol is externally visible, it
+should have a name starting \"ps\" or \"p_ps\", and be declared in a
+.h file.
+
+E.g. check-namespace *.o *.h
+
+The treatment of comments and typedefs is sleazy and non-robust.
+
+Options:
+	-h, -?		Print this message
+	-d		Don't warn about symbols defined in .h files but not found by nm
+	-p prefix	Change prefix from ps to \"prefix\"
+	-v		Be chatty (repeat for even more output)
+";
+      exit(0);
+   } elsif($ARGV[0] eq "-p") {
+      shift(@ARGV);
+      $arg = $ARGV[0];
+      if(!$arg) {
+	 warn "-p expects an argument\n";
+      } else {
+	 $prefix = "$arg";
+	 $pprefix = "p_$prefix";
+      }
+   } elsif($ARGV[0] eq "-d") {
+      $allow_missing_definitions = 1;      
+   } elsif($ARGV[0] eq "-v") {
+      $verbose++;
+   } else {
+      warn "Unknown argument $ARGV[0]\n";
+   }
+
+   shift(@ARGV);
+}
+#
+$uprefix = uc($prefix);
+$upprefix = uc($pprefix);
+#
+# Get list of files to process
+#
+foreach (@ARGV) {
+   if(/\.h$/) {
+      push(@hfiles, $_);
+   } elsif(/\.[ao]$/) {
+      push(@ofiles, $_);
+   } else {
+      warn "I don't know what to do with $_; ignoring\n";
+   }
+}
+#
+# Read all the .h files looking for suitable symbols
+#
+foreach $file (@hfiles) {
+   $in_comment = 0;		# in a multiline comment?
+   $in_typedef = 0;		# parsing a typedef?
+   
+   if ($verbose > 1) {
+      warn "   $file\n";
+   }
+
+   open(FD, $file) or die "Failed to open $file:$! \n";
+   while (<FD>) {
+      chomp;
+      # Deal with comments, at least approximately
+      if($in_comment) {
+	 while (!m|\*/|) {
+	    $_ = <FD>;
+	 }
+	 $in_comment = 0;
+      }
+
+      s|//.*||;
+      if(m|(.*)/\*(.*)|) {
+	 $first = $1; $rest = $2;
+	 if ($rest =~ m|\*/(.*)|) {
+	    $_ = "$first /**/ $1";
+	 } else {
+	    $_ = $first;
+	    $in_comment = 1;
+	 }
+      }
+      #
+      # OK, how about typedefs?
+      #
+      if (/typedef.*\W\(\*(($pprefix|$prefix)[a-zA-Z_0-9]+)\)/o) {
+	 $name = $1;
+ 
+	 $typedefs{$name} = $file;
+	 $in_typedef--;
+      } elsif (/typedef\s+struct/) {
+	 if(/([a-zA-Z_0-9]+)\s*;/) {
+	    $name = $1;
+	    $typedefs{$name} = $file;
+	    next;
+	 }
+	 $in_typedef = 1;
+      } elsif ($in_typedef) {
+	 if (/\{/) {
+	    $in_typedef++;
+	 } elsif ($in_typedef > 1 && /\}/) {
+	    $in_typedef--;
+	 }
+      }
+
+      if ($in_typedef) {
+	 if (/\}\s*(($prefix|$pprefix)[a-zA-Z_0-9]+)\s*;/) {
+	    $in_typedef--;
+	    if ($in_typedef == 0) {
+	       $name = $1;
+	       $typedefs{$name} = $file;
+	    }
+	 }
+      }
+      #
+      # And #defines?
+      #
+      if (/^\s*\#\s*define\s+([a-zA-Z_0-9]+)(\()?/) {
+	 $name = $1;
+	 $func_macro = (defined($2)) ? 1 : 0;
+
+	 $defines{$name} = $file;
+
+	 if((!$func_macro && $name !~ /^($uprefix|$upprefix)/o) ||
+	    ($func_macro && $name !~ /^($prefix|$pprefix)/io)) {
+	    printf STDERR
+		"\#define %s (%s) does not have a permitted prefix\n",
+		$name, $file;
+	 }
+      }
+      #
+      # OK, how about enums? Their members look like external names,
+      # and should obey the same rules
+      #
+      if (s/((typedef\s+)?enum)(\s*{)/$3/) {
+	 $typedef = $1;
+	 $is_enum_typedef = ($typedef =~ /typedef/) ? 1 : 0;
+
+	 while (!/\}/) {
+	    $line = <FD>;
+	    chomp;
+	    s|//.*||;
+	    s|/\*[^\*]*\*/||;
+
+	    $_ .= " $line";
+	 }
+
+	 if ($is_enum_typedef) {
+	    if ($line =~ /\}\s*([a-zA-Z_0-9]+)/) {
+	       $name = $1;
+	       $typedefs{$name} = $file;
+	    }
+	 }
+
+	 s/^\s*\{\s*//;
+	 while (s/([a-zA-Z_0-9]+)(\s*=\s*-?[a-zA-Z_0-9]+)?(\s*,\s*)?\s*//) {
+	    if(/^\}/) {
+	       last;
+	    }
+	    
+	    $name = $1;
+
+	    if ($name eq "enum") {
+	       next;
+	    }
+
+	    #$enums{$1} = $file;
+	    
+	    if($name !~ /^($uprefix|$upprefix)/o) {
+	       printf STDERR
+		   "enum element %s (%s) does not have a permitted prefix\n",
+		   $name, $file;
+	    }
+	 }
+      }
+      #
+      # Include filenames can look like external names too
+      #
+      if (/^\s*\#\s*include\s/) {
+	 next;
+      }
+      # Look for possible external symbols
+      #
+      while(s/(^|\W)(($pprefix|$prefix)[a-zA-Z_0-9]+)//o) {
+	 $name = $2;
+
+	 $declarations{$name} = $file;
+      }
+   }
+   close(FD);
+}
+
+if($verbose) {
+   warn "Typedefs found in header files:\n";
+   foreach (sort keys %typedefs) {
+      printf STDERR "%-30s %s\n", $_, $typedefs{$_};
+   }
+
+   warn "External symbols found in header files:\n";
+   foreach (sort keys %declarations) {
+      printf STDERR "%-30s %s\n", $_, $declarations{$_};
+   }
+}
+#
+# OK, now run nm on the .o and .a files and see if there are problems
+# 
+foreach $file (@ofiles) {
+   if ($verbose) {
+      warn "   $file\n";
+   }
+
+   open(FD, "nm $nm_opts $file|") or die "Failed to open $file:$! \n";
+   while (<FD>) {
+      if (/^\s*$/) {
+	 next;
+      }
+
+      if (/^(lib[A-Za-z0-9]+\.a\(\w+\.o\))/) {
+	 $file = $1;
+	 next;
+      }
+      
+      if (/^\s+U /) {
+	 next;			# an undefined symbol
+      }
+
+      my($addr, $type, $name) = split;
+
+      if (!defined($name)) {
+	 next;
+      }
+      $name =~ s/^_//;
+
+      if ($name eq "main") {
+	 next;
+      }
+      if ($name =~ /\./) {
+	 next;			# an internal name to the compiler
+      }
+
+      if ($type eq "b") { $type = "d"; } # don't distinguish BSS, Common, and Data
+      if ($type eq "B") { $type = "D"; }
+      if ($type eq "C") { $type = "D"; }
+
+      if ($type !~ /[DT]/i) {	# Data or Text section
+	 die "Unknown symbol type in $file: $_\n";
+      }
+
+      if ($verbose > 1) {
+	 printf "%-20s %-40s %s\n", $file, $name, $type;
+      }
+
+      if ($type =~ /[dt]/) {
+	 if($name =~ /^($prefix|$pprefix)/o) {
+	    warn "Non-global symbol $name in $file is in reserved namespace ($prefix|$pprefix)\n";
+	 }
+      } elsif ($type =~ /[DT]/) {
+	 if ($declarations{$name}) {
+	    $used_declarations{$name}++;
+	 } elsif ($name !~ /^($pprefix|$prefix)/) {
+	    if($name !~ /^($prefix|$pprefix)/io) {
+	       printf STDERR
+		   "global symbol %s (%s) does not have a permitted prefix\n",
+		   $name, $file;
+	    }
+	 } else {
+	    printf STDERR "External symbol %s (%s) appears in no .h file\n",
+	    $name, $file;
+	 }
+      }
+   }
+   close(FD);
+}
+#
+# Now check for unused declarations
+#
+if (!$allow_missing_definitions) {
+   foreach $name (sort keys %declarations) {
+      if (!$used_declarations{$name} && !$typedefs{$name} && !$defines{$name}) {
+	 warn "$name was declared in $declarations{$name} but not defined\n";
+      }
+   }
+}
Index: /tags/sj_tags/sj_root_20080929/arclog/.arclogrc
===================================================================
--- /tags/sj_tags/sj_root_20080929/arclog/.arclogrc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/arclog/.arclogrc	(revision 22322)
@@ -0,0 +1,6 @@
+---
+hosts:
+event_filters:
+  - Raid Powered On
+  - HTTP Log In
+logdb: .arclog_db
Index: /tags/sj_tags/sj_root_20080929/arclog/arclog.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/arclog/arclog.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/arclog/arclog.pl	(revision 22322)
@@ -0,0 +1,263 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Config::YAML;
+use HTML::TreeBuilder;
+use HTTP::Request;
+use LWP::UserAgent;
+use LWP::ConnCache;
+use DateTime::Format::Strptime;
+use DBI;
+use Email::Send;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($email);
+
+GetOptions(
+    'email|s=s'    => \$email,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+my $filter_records = 1;
+
+my $rcfile = "$ENV{HOME}/.arclogrc";
+$rcfile = File::Spec->canonpath($rcfile);
+
+my $c = read_rcfile($rcfile);
+my $myhosts = $c->get_hosts;
+die "no hosts configured" unless $myhosts;
+my $logdb = $c->get_logdb;
+die "no log file configured" unless $logdb;
+my $dbh = DBI->connect("dbi:SQLite:dbname=$logdb","","");
+die "can not open logdb" unless $dbh;
+
+$dbh->do("CREATE TABLE IF NOT EXISTS log(record TEXT, PRIMARY KEY(record))")
+    or die "db error";
+
+my $errors;
+my $output;
+###
+if (defined $email) {
+    close(STDOUT);
+    close(STDERR);
+    open(STDOUT, '>', \$output) or die "Can't open STDOUT: $!";
+    open(STDERR, '>', \$errors) or die "Can't open STDERR: $!";
+}
+
+# HTTP authentication realm
+my $realm = "Raid Console";
+my $user = "admin"; # unconfigurable on the areca card as far as I know
+my $http_port = "81";
+
+# collection of fetched HTML log pages
+my %log_pages;
+
+# iterate over the list of hosts and passwords
+foreach my $host (keys %$myhosts) {
+    my $hostport = "$host:$http_port";
+    my $ua = LWP::UserAgent->new;
+    $ua->timeout(10);
+    $ua->conn_cache(LWP::ConnCache->new());
+    $ua->credentials( "$hostport", $realm, $user => $myhosts->{$host});
+
+    # first query: access the doc root to setup HTTP digest auth and grab
+    # exclusive control of the HTTP admin interace
+    my $res = $ua->request(HTTP::Request->new(GET => "http://$hostport/"));
+    unless ($res->is_success) {
+        warn "can't access $hostport: ", $res->status_line;
+        next;
+    }
+    
+    # second query: access the log page
+    my $req = HTTP::Request->new(GET => "http://$hostport/evt0.htm");
+
+    # if the referer is not set the access attempt with fail
+    $req->referer("http://$hostport/menu.htm");
+    # we must copy the auth credentals from the first query attempt as LWP
+    # seems to always do a non digest access attempt first, which fails, and
+    # then does the HTTP digest.  In the case of the arcea card this first non
+    # authed query resets the state of the card and we have to go back and
+    # request '/' again.
+    $req->header( Authorization => $res->request->header('Authorization'));
+    $res= $ua->request($req);
+    unless ($res->is_success) {
+        die "can't access $hostport: ", $res->status_line;
+    }
+
+    $log_pages{$host} = $res->content;    
+}
+
+my $strptime = DateTime::Format::Strptime->new(
+    pattern => '%Y-%m-%d %H:%M:%S'
+);
+
+my @records;
+foreach my $host (keys %log_pages) {
+    my $page = $log_pages{$host};
+    my $root = HTML::TreeBuilder->new->parse($page);
+    my $table = $root->look_down('_tag', 'table');
+    foreach my $row ($table->descendants) {
+        next unless $row->tag eq "tr";
+        my $row_data = parse_row($row);
+        if (defined $row_data) {
+            # add the host name to the hash
+            $row_data->{host} = $host;
+            push @records, $row_data;
+        }
+    }
+}
+
+# figure out the last record that we saw
+# since the order in the sqllite db isn't guarenteed and we can't sort in the
+# db, we have to do this by hand
+#
+# futher, since it doesn't take 0 time to poll the cards -- and as it testing
+# it was determined that some cards weren't properly synced to an NTP server.
+# We should figure out the last record time for each host.
+my %latest_times;
+{
+    my $strptime = DateTime::Format::Strptime->new(
+        pattern => '%Y-%m-%dT%H:%M:%S'
+    );
+
+    # read in the entire database
+    my $query = $dbh->prepare("SELECT * FROM log")
+    or die "database error: $!";
+    $query->execute or die "database error: $!";
+    my $records = $query->fetchall_arrayref;
+
+    # sort it for the latest timestamp
+    foreach my $rec (@$records) {
+        my ($host, $time, $device, $event, $elapse_time, $errors)
+            = split(/\|/, $rec->[0]);
+
+        my $dt = $strptime->parse_datetime($time);
+
+        if (not exists $latest_times{$host}) {
+            $latest_times{$host} = $dt;
+        } elsif ($dt > $latest_times{$host}) {
+            $latest_times{$host} = $dt;
+        }
+
+    }
+}
+
+use Data::Dumper;
+#print Dumper(\@records);
+# sort by time
+# insert all records
+@records = sort {
+    unless (defined $a->{time} && defined $b->{time}) {
+        print Dumper($a);
+        print Dumper($b);
+    }
+    $a->{time} cmp $b->{time}
+} @records;
+my $query = $dbh->prepare("INSERT OR IGNORE INTO log VALUES(?)")
+        or die "database error: $!";
+
+my @myfilters;
+foreach my $filter (@{$c->get_event_filters}) {
+    push @myfilters, qr/$filter/;
+}
+
+RECORDS: foreach my $rec (@records) {
+    no warnings qw( uninitialized );
+
+    my ($host, $time, $device, $event, $elapse_time, $errors)
+        = @$rec{qw( host time device event elapse_time errors )};
+    # pack record
+    $query->execute("$host|$time|$device|$event|$elapse_time|$errors")
+        or die "database error: $!";
+
+    # do not print filtered records
+    if ($filter_records) {
+        foreach my $filter (@myfilters) {
+            next RECORDS if $event =~ /$filter/;
+        }
+    }
+    
+    if ($time > $latest_times{$host}) {
+        print "$host $time $device $event $elapse_time $errors\n";
+    }
+
+    use warnings;
+}
+
+my $message =<<END;
+To: $email
+From: jhoblitt\@ifa.hawaii.edu
+Subject: output from arclog.pl
+
+$errors
+$output
+END
+
+my $sender = Email::Send->new({mailer => 'SMTP'});
+$sender->mailer_args([Host => 'hale.ifa.hawaii.edu']);
+$sender->send($message);
+
+
+sub parse_row
+{
+    my $row = shift;
+    # log table format in firmware "V1.43 2007-4-17"
+    #<tr>
+    #<th width="15%" height="23">Time</th>
+    #<th width="23%" height="23">Device</th>
+    #<th width="28%" height="23">Event Type</th>
+    #<th width="19%" height="23">Elapse Time</th>
+    #<th width="15%" height="23">Errors</th>
+    #</tr>
+    my @fields;
+    foreach my $child ($row->descendants) {
+        return if $child->tag eq "th";
+        my $text = $child->as_text;
+        # translate HTML "&nbsp" into undef
+        $text = undef if $text eq chr(160);
+        push @fields, $text;
+    }
+    my %row_data;
+    @row_data{qw( time device event elapse_time errors )} = @fields;
+
+    # ipp008 has been returning valid rows with completely empty fields
+    return unless defined $row_data{time};
+
+    # parse time string into a DateTime object for easy sorting later
+    my $dt = $strptime->parse_datetime($row_data{time});
+
+    unless (defined $dt) {
+#        warn "can't parse time string $row_data{time}";
+        return;
+    }
+
+    $row_data{time} = $dt;
+
+    return \%row_data;
+}
+
+
+sub read_rcfile
+{
+    my $rcfile = shift;
+
+    return unless defined $rcfile;
+
+    if (!-f $rcfile) {
+        open(my $fh, '>', $rcfile) or die "can't open file: $!";
+        close($fh) or die "can't close file:$!";
+    }
+
+    return Config::YAML->new(
+            config => $rcfile,
+            output => $rcfile,
+    );
+}
Index: /tags/sj_tags/sj_root_20080929/arclog/arclog_readdb.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/arclog/arclog_readdb.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/arclog/arclog_readdb.pl	(revision 22322)
@@ -0,0 +1,92 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Config::YAML;
+use HTML::TreeBuilder;
+use HTTP::Request;
+use LWP::UserAgent;
+use LWP::ConnCache;
+use DateTime::Format::Strptime;
+use DBI;
+
+#use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+# accept no options
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+my $filter_records = 1;
+
+my $rcfile = "$ENV{HOME}/.arclogrc";
+$rcfile = File::Spec->canonpath($rcfile);
+
+my $c = read_rcfile($rcfile);
+my $logdb = $c->get_logdb;
+die "no log file configured" unless $logdb;
+my $dbh = DBI->connect("dbi:SQLite:dbname=$logdb","","");
+die "can not open logdb" unless $dbh;
+
+my $query = $dbh->prepare("SELECT * FROM log")
+    or die "database error: $!";
+$query->execute or die "database error: $!";
+my $rows = $query->fetchall_arrayref(undef);
+
+#$records = [ $records->[-1] ];
+
+my @myfilters;
+foreach my $filter (@{$c->get_event_filters}) {
+    push @myfilters, qr/$filter/;
+}
+
+my $strptime = DateTime::Format::Strptime->new(
+    pattern => '%Y-%m-%dT%H:%M:%S'
+);
+
+# unpack records
+my @records;
+foreach my $row (@$rows) {
+    my %rec;
+    @rec{qw( host time device event elapse_time errors )} 
+        = split(/\|/, $row->[0]);
+    $rec{time} = $strptime->parse_datetime($rec{time});
+    push @records, \%rec;
+}
+
+# sort by time
+@records = sort { $a->{time} cmp $b->{time} } @records;
+
+RECORDS: foreach my $rec (@records) {
+    my ($host, $time, $device, $event, $elapse_time, $errors)
+        = @$rec{qw( host time device event elapse_time errors )};
+
+    # do not print filtered records
+    if ($filter_records) {
+        foreach my $filter (@myfilters) {
+            next RECORDS if $event =~ /$filter/;
+        }
+    }
+
+    no warnings qw( uninitialized );
+    print "$host $time $device $event $elapse_time $errors\n";
+    use warnings;
+}
+
+sub read_rcfile
+{
+    my $rcfile = shift;
+
+    return unless defined $rcfile;
+
+    if (!-f $rcfile) {
+        open(my $fh, '>', $rcfile) or die "can't open file: $!";
+        close($fh) or die "can't close file:$!";
+    }
+
+    return Config::YAML->new(
+            config => $rcfile,
+            output => $rcfile,
+    );
+}
Index: /tags/sj_tags/sj_root_20080929/dbconfig/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+ippdb.m4
+ippdb.mdc
Index: /tags/sj_tags/sj_root_20080929/dbconfig/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/Makefile	(revision 22322)
@@ -0,0 +1,26 @@
+SHELL=/bin/sh
+GLUEFORGE=`which glueforge`
+
+all: ippdb.mdc
+
+ippdb.mdc : ipp.m4 *.md
+	m4 ipp.m4 > ippdb.mdc
+
+## please leave the output target as ippdb.src and
+## move to ippdb by hand for cvs import
+install: ippdb.mdc
+	rm -rf ../ippdb.src
+	$(GLUEFORGE) -i ippdb.mdc --output ../ippdb.src
+	chmod +x ../ippdb.src/autogen.sh
+
+src: ippdb.mdc
+	$(GLUEFORGE) -i ippdb.mdc --output ../ippdb
+	chmod +x ../ippdb/autogen.sh
+
+foo: src
+	$(MAKE) -C ../ippdb install
+
+build : ippdb ippdb.mdc
+
+clean:
+	@rm -f *~
Index: /tags/sj_tags/sj_root_20080929/dbconfig/calibration.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/calibration.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/calibration.md	(revision 22322)
@@ -0,0 +1,15 @@
+
+# table of the DVO databases which get calibrated
+calDB METADATA
+    cal_id      S64         0       # Primary Key AUTO_INCREMENT
+    dvodb       STR         64
+    state       STR         64
+END
+
+calRun METADATA
+    cal_id      S64         0       # Primary Key AUTO_INCREMENT
+    region      STR         64      # success or failure
+    last_step   STR         64      # fkey(cal_id) ref calDB(cal_id)
+    state       STR         64      # 
+END
+
Index: /tags/sj_tags/sj_root_20080929/dbconfig/cam.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/cam.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/cam.md	(revision 22322)
@@ -0,0 +1,50 @@
+camRun METADATA
+    cam_id      S64         0       # Primary Key AUTO_INCREMENT
+    chip_id     S64         0       # Key INDEX(cam_id, chip_id) fkey(chip_id) ref chipRun(chip_id)
+    state       STR         64      # key
+    workdir     STR         255 
+    workdir_state STR       64      # key
+    label       STR         64      # key
+    reduction   STR         64
+    expgroup    STR         64      # key
+    dvodb       STR         255
+    tess_id     STR         64
+    end_stage   STR         64      # Key
+END
+
+camProcessedExp METADATA
+# the camPendingExp row gets deleted so we can not put a fkey on cam_id
+    cam_id         S64      0       # Primary Key
+    uri            STR      255     # fkey(cam_id) ref camRun(cam_id)
+
+    bg             F32      0.0
+    bg_stdev       F32      0.0
+    bg_mean_stdev  F32      0.0
+    bias	   F32	    0.0
+    bias_stdev     F32	    0.0
+    fringe_0       F32      0.0
+    fringe_1       F32      0.0
+    fringe_2       F32      0.0
+    sigma_ra       F32      0.0
+    sigma_dec      F32      0.0
+    ap_resid       F32	    0.0
+    ap_resid_stdev F32	    0.0
+    zp_mean        F32      0.0
+    zp_stdev       F32      0.0
+    fwhm_major     F32      0.0
+    fwhm_minor     F32      0.0
+    dtime_detrend  F32      0.0
+    dtime_photom   F32      0.0
+    dtime_astrom   F32      0.0
+    hostname       STR      64
+    n_stars        S32      0
+    n_extended     S32      0
+    n_cr           S32      0
+    n_astrom       S32      0
+    path_base      STR      255
+    fault          S16      0       # Key NOT NULL
+END
+
+camMask METADATA
+    label       STR         64      # Primary Key
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/changes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/changes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/changes.txt	(revision 22322)
@@ -0,0 +1,536 @@
+-- How to manually change the database between versions via mySQL commands.
+-- 
+-- This file is generated manually, and may not be complete.
+
+
+-- Version 1.1.20 --> 1.1.21:
+
+alter table camProcessedExp drop nastro;
+alter table camProcessedExp add (fwhm double, fwhm_range double, n_stars int(11), n_extended int(11), n_cr int(11), n_astrom int(11));
+alter table chipProcessedImfile add column (bias double, bias_stdev double, fringe_0 double, fringe_1 double, fringe_2 double, sigma_ra double, sigma_dec double, ap_resid double, ap_resid_stdev double, fwhm double, fwhm_range double, n_stars int(11), n_extended int(11), n_cr int(11), n_astrom int(11));
+alter table warpSkyfile add column path_base varchar(255) NULL DEFAULT NULL after uri;
+alter table diffSkyfile add column path_base varchar(255) NULL DEFAULT NULL after uri;
+alter table stackSumSkyfile add column path_base varchar(255) NULL DEFAULT NULL after uri;
+
+
+-- Version 1.1.22 --> 1.1.23
+-- Adding support for reduction classes (which are used to specify recipes for parts of the pipeline).
+# Adding 'reduction' to detRun
+# Renaming 'recipe' in {chip,cam}{Pending,Processed}Exp to 'reduction'
+
+alter table detRun add column reduction varchar(64) NULL DEFAULT NULL after exp_type;
+alter table chipPendingExp change column recipe reduction varchar(64);
+alter table chipProcessedExp change column recipe reduction varchar(64);
+alter table camPendingExp change column recipe reduction varchar(64);
+alter table camProcessedExp change column recipe reduction varchar(64);
+
+-- Version ??? --> 1.1.29
+
+alter table warpSkyfile add column ignored tinyint after good_frac;
+
+--
+-- The following set of changes were applied to update the gpc1 database on 2008/02/07
+--
+
+alter table calDB change column catdir dvodb varchar(64);
+
+alter table camProcessedExp change column fwhm fwhm_major float;
+alter table camProcessedExp change column fwhm_range fwhm_minor float;
+
+-- In order to keep the column order correct this is split up below
+--alter table camProcessedExp add column ( bias float, bias_stdev float, fringe_0 float, fringe_1 float, fringe_2 float, ap_resid float, ap_resid_stdev float, dtime_detrend float, dtime_photom float, dtime_astrom float, hostname varchar(64) );
+
+alter table camProcessedExp add column bias float after bg_mean_stdev;
+alter table camProcessedExp add column bias_stdev float after bias;
+alter table camProcessedExp add column fringe_0 float after bias_stdev;
+alter table camProcessedExp add column fringe_1 float after fringe_0;
+alter table camProcessedExp add column fringe_2 float after fringe_1;
+alter table camProcessedExp add column ap_resid float after sigma_dec;
+alter table camProcessedExp add column ap_resid_stdev float after ap_resid;
+alter table camProcessedExp add column dtime_detrend float after fwhm_minor;
+alter table camProcessedExp add column dtime_photom float after dtime_detrend;
+alter table camProcessedExp add column dtime_astrom float after dtime_photom;
+alter table camProcessedExp add column hostname varchar(64) after dtime_astrom;
+
+
+alter table chipProcessedImfile change column fwhm fwhm_major float;
+alter table chipProcessedImfile change column fwhm_range fwhm_minor float;
+
+-- In order to keep the column order correct this is split up below
+-- alter table chipProcessedImfile add column ( zp_mean float, zp_stdev float, dtime_detrend float, dtime_photom float, dtime_astrom float, hostname varchar(64) );
+
+alter table chipProcessedImfile add column zp_mean float after ap_resid_stdev;
+alter table chipProcessedImfile add column zp_stdev float after zp_mean;
+alter table chipProcessedImfile add column dtime_detrend float after fwhm_minor;
+alter table chipProcessedImfile add column dtime_photom float after dtime_detrend;
+alter table chipProcessedImfile add column dtime_astrom float after dtime_photom;
+alter table chipProcessedImfile add column hostname varchar(64) after dtime_astrom;
+
+
+-- In order to keep the column order correct this is split up below
+-- alter table diffSkyfile add column ( dtime_diff float, hostname varchar(64) );
+alter table diffSkyfile add column dtime_diff float after bg_stdev;
+alter table diffSkyfile add column hostname varchar(64) after dtime_diff;
+alter table diffSkyfile change column good_frac good_frac float;
+
+alter table flatcorrRun add column filter varchar(64) after dvodb;
+
+alter table stackSumSkyfile change column good_frac good_frac float;
+
+-- alter table stackSumSkyfile add column ( dtime_stack float, hostname varchar(64) );
+alter table stackSumSkyfile add column dtime_stack float after bg_stdev;
+alter table stackSumSkyfile add column hostname varchar(64) after dtime_stack;
+
+#alter table warpSkyfile add column ( dtime_warp float, hostname varchar(64) );
+alter table warpSkyfile add column dtime_warp float after bg_stdev;
+alter table warpSkyfile add column hostname varchar(64) after dtime_warp;
+
+alter table warpSkyfile change column good_frac good_frac float;
+
+alter table newExp add column dvodb varchar(255) after reduction;
+alter table newExp add column tess_id varchar(64) after dvodb;
+alter table rawExp add column dvodb varchar(255) after reduction;
+alter table rawExp add column tess_id varchar(64) after dvodb;
+alter table chipRun add column tess_id varchar(64) after dvodb;
+alter table camRun add column tess_id varchar(64) after dvodb;
+alter table warpRun add column tess_id varchar(64) after dvodb;
+
+alter table camRun add column end_stage varchar(64) after tess_id;
+alter table chipRun add column end_stage varchar(64) after tess_id;
+alter table warpRun add column end_stage varchar(64) after tess_id;
+alter table newExp add column end_stage varchar(64) after tess_id;
+alter table rawExp add column end_stage varchar(64) after tess_id;
+alter table camRun add key (end_stage);
+alter table chipRun add key (end_stage);
+alter table warpRun add key (end_stage);
+alter table newExp add key (end_stage);
+alter table rawExp add key (end_stage);
+
+alter table warpRun add column workdir_state varchar(64) after workdir;
+alter table warpRun add key (workdir_state);
+
+alter table pzDoneImfile add column fault smallint(6);
+
+-- end of changes to 1.1.29
+
+alter table newExp add column label varchar(64) after end_stage;
+
+-- 1.1.30
+
+drop table pzDoneExp;
+drop table pzDoneImfile;
+drop table pzPendingExp;
+drop table pzPendingImfile;
+alter table flatcorrRun add column region VARCHAR(255) after stats;
+
+-- 1.1.31
+
+ALTER TABLE warpRun ADD COLUMN cam_id BIGINT AFTER warp_id;
+ALTER TABLE warpRun ADD KEY(cam_id);
+ALTER TABLE warpRun ADD KEY(warp_id, cam_id);
+ALTER TABLE warpRun ADD CONSTRAINT FOREIGN KEY(cam_id) REFERENCES
+camRun(cam_id);
+ALTER TABLE warpRun ADD COLUMN magiced TINYINT AFTER registered;
+ALTER TABLE warpSkyCellMap DROP FOREIGN KEY warpSkyCellMap_ibfk_1;
+UPDATE warpRun JOIN warpInputExp USING(warp_id) SET warpRun.cam_id =
+warpInputExp.cam_id WHERE warpRun.warp_id = warpInputExp.warp_id;
+ALTER TABLE warpSkyCellMap ADD CONSTRAINT FOREIGN KEY(warp_id, cam_id)
+REFERENCES warpRun(warp_id, cam_id);
+DROP TABLE warpInputExp;
+
+-- 1.1.32
+ALTER TABLE warpRun ADD COLUMN label VARCHAR(64) AFTER workdir_state;
+ALTER TABLE warpRun ADD KEY(label);
+CREATE TABLE warpMask (label VARCHAR(64), PRIMARY KEY(label)) ENGINE=innodb;
+
+-- 1.1.33
+ALTER TABLE summitExp ADD COLUMN fault smallint(6) NOT NULL AFTER imfiles;
+ALTER TABLE summitExp ADD KEY(fault);
+
+-- 1.1.34
+ALTER TABLE rawImfile ADD KEY(exp_name);
+
+-- 1.1.35
+ALTER TABLE rawImfile ADD INDEX UNQIUE(exp_id, tmp_class_id);
+
+-- 1.1.36
+ALTER TABLE warpSkyfile ADD COLUMN (xmin INT, xmax INT, ymin INT, ymax INT);
+ALTER TABLE diffSkyfile ADD COLUMN (stamps_num INT, stamps_rms FLOAT, sources INT);
+ALTER TABLE rawImfile ADD COLUMN hostname VARCHAR(64) AFTER object;
+ALTER TABLE rawExp ADD COLUMN hostname VARCHAR(64) AFTER object;
+
+-- 1.1.37
+ALTER TABLE pzDataStore ADD COLUMN epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER uri;
+ALTER TABLE summitExp ADD COLUMN epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER fault;
+ALTER TABLE summitImfile ADD COLUMN epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER uri;
+ALTER TABLE pzDownloadExp ADD COLUMN epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER state;
+ALTER TABLE pzDownloadImfile ADD COLUMN epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER fault;
+ALTER TABLE newExp ADD COLUMN epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER label;
+ALTER TABLE newImfile ADD COLUMN epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER uri;
+ALTER TABLE rawExp ADD COLUMN epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER fault;
+ALTER TABLE rawImfile ADD COLUMN epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER fault;
+
+-- 1.1.38
+
+-- convert from utf8 -> latin1 for performance/space gains
+SET FOREIGN_KEY_CHECKS=0;
+ALTER TABLE calDB CONVERT TO CHARACTER SET latin1;
+ALTER TABLE calRun CONVERT TO CHARACTER SET latin1;
+ALTER TABLE camMask CONVERT TO CHARACTER SET latin1;
+ALTER TABLE camProcessedExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE camRun CONVERT TO CHARACTER SET latin1;
+ALTER TABLE chipMask CONVERT TO CHARACTER SET latin1;
+ALTER TABLE chipProcessedImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE chipRun CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detInputExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detNormalizedExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detNormalizedImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detNormalizedStatImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detProcessedExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detProcessedImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detRegisteredImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detResidExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detResidImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detRun CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detRunSummary CONVERT TO CHARACTER SET latin1;
+ALTER TABLE detStackedImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE diffInputSkyfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE diffRun CONVERT TO CHARACTER SET latin1;
+ALTER TABLE diffSkyfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE fakeMask CONVERT TO CHARACTER SET latin1;
+ALTER TABLE fakeProcessedImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE fakeRun CONVERT TO CHARACTER SET latin1;
+ALTER TABLE flatcorrExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE flatcorrRun CONVERT TO CHARACTER SET latin1;
+ALTER TABLE guidePendingExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE magicInputSkyfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE magicMask CONVERT TO CHARACTER SET latin1;
+ALTER TABLE magicNodeResult CONVERT TO CHARACTER SET latin1;
+ALTER TABLE magicRun CONVERT TO CHARACTER SET latin1;
+ALTER TABLE magicSkyfileMask CONVERT TO CHARACTER SET latin1;
+ALTER TABLE magicTree CONVERT TO CHARACTER SET latin1;
+ALTER TABLE newExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE newImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE pstampDataStore CONVERT TO CHARACTER SET latin1;
+ALTER TABLE pstampJob CONVERT TO CHARACTER SET latin1;
+ALTER TABLE pstampRequest CONVERT TO CHARACTER SET latin1;
+ALTER TABLE pzDataStore CONVERT TO CHARACTER SET latin1;
+ALTER TABLE pzDownloadExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE pzDownloadImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE rawExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE rawImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE stackInputSkyfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE stackRun CONVERT TO CHARACTER SET latin1;
+ALTER TABLE stackSumSkyfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE summitExp CONVERT TO CHARACTER SET latin1;
+ALTER TABLE summitImfile CONVERT TO CHARACTER SET latin1;
+ALTER TABLE warpMask CONVERT TO CHARACTER SET latin1;
+ALTER TABLE warpRun CONVERT TO CHARACTER SET latin1;
+ALTER TABLE warpSkyCellMap CONVERT TO CHARACTER SET latin1;
+ALTER TABLE warpSkyfile CONVERT TO CHARACTER SET latin1;
+SET FOREIGN_KEY_CHECKS=1;
+
+-- first use of fake* tables
+-- drop and recreate any fake* tables created prior to this point
+
+DROP TABLE IF EXISTS fakeRun;
+CREATE TABLE fakeRun (
+    fake_id BIGINT AUTO_INCREMENT,
+    cam_id BIGINT,
+    state VARCHAR(64),
+    workdir VARCHAR(255),
+    label VARCHAR(64),
+    reduction VARCHAR(64),
+    expgroup VARCHAR(64),
+    dvodb VARCHAR(255),
+    tess_id VARCHAR(64),
+    end_stage VARCHAR(64),
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(fake_id),
+    KEY(cam_id),
+    KEY(state),
+    KEY(label),
+    KEY(expgroup),
+    KEY(end_stage),
+    INDEX(fake_id, cam_id),
+    FOREIGN KEY (cam_id)
+        REFERENCES camRun(cam_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+DROP TABLE IF EXISTS fakeProcessedImfile;
+CREATE TABLE fakeProcessedImfile (
+    fake_id BIGINT AUTO_INCREMENT,
+    exp_id BIGINT(20),
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    dtime_fake FLOAT,
+    hostname VARCHAR(64),
+    path_base VARCHAR(255),
+    fault SMALLINT NOT NULL,
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(fake_id, exp_id, class_id),
+    KEY(fault),
+    FOREIGN KEY (fake_id)
+        REFERENCES fakeRun(fake_id),
+    FOREIGN KEY(exp_id, class_id)
+        REFERENCES rawImfile(exp_id, class_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+DROP TABLE IF EXISTS fakeMask;
+CREATE TABLE fakeMask (
+    label VARCHAR(64),
+    PRIMARY KEY(label))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+-- re-normalize camProcessedExp table
+-- unrelated to other changes
+ALTER TABLE camProcessedExp DROP FOREIGN KEY camProcessedExp_ibfk_1;
+ALTER TABLE camProcessedExp DROP COLUMN chip_id;
+ALTER TABLE camProcessedExp ADD FOREIGN KEY (cam_id) REFERENCES camRun (cam_id);
+
+-- insert fakeRun betwen camRun and warpRun
+-- populate fakeRun/fakeProcessedImfile
+-- then heal the foreign key references
+
+CREATE TEMPORARY TABLE warpCamMap (
+    warp_id BIGINT,
+    cam_id  BIGINT,
+    PRIMARY KEY(warp_id),
+    KEY(cam_id)
+) ENGINE=MEMORY;
+
+INSERT INTO warpCamMap SELECT warp_id, cam_id FROM warpRun;
+
+ALTER TABLE warpRun drop FOREIGN KEY warpRun_ibfk_1;
+ALTER TABLE warpSkyCellMap DROP FOREIGN KEY warpSkyCellMap_ibfk_1;
+
+ALTER TABLE warpRun change COLUMN cam_id fake_id BIGINT(20) DEFAULT NULL;
+ALTER TABLE warpSkyCellMap DROP COLUMN cam_id;
+
+SET FOREIGN_KEY_CHECKS=0;
+ALTER TABLE warpRun ADD FOREIGN KEY (fake_id) REFERENCES fakeRun (fake_id);
+SET FOREIGN_KEY_CHECKS=1;
+ALTER TABLE warpSkyCellMap ADD FOREIGN KEY (warp_id) REFERENCES warpRun(warp_id);
+
+INSERT INTO fakeRun
+SELECT
+    NULL,
+    camRun.cam_id,
+    'stop',
+    workdir,
+    label,
+    reduction,
+    expgroup,
+    dvodb, 
+    tess_id, 
+    end_stage, 
+    NULL 
+FROM camRun
+JOIN warpCamMap
+    using(cam_id);
+
+INSERT INTO fakeProcessedImfile
+SELECT
+    fake_id,
+    chipRun.exp_id,
+    class_id,
+    uri,
+    0,
+    hostname,
+    path_base, 
+    fault, 
+    NULL
+FROM fakeRun
+JOIN camRun
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN chipProcessedImfile
+    USING(chip_id);
+
+UPDATE warpRun, fakeRun, warpCamMap
+SET warpRun.fake_id = fakeRun.fake_id
+WHERE warpRun.warp_id = warpCamMap.warp_id
+    AND warpCamMap.cam_id = fakeRun.fake_id;
+
+DROP TABLE warpCamMap;
+
+
+-- new values for chipRun.state
+
+update chipRun set state = 'new' where state = 'run';
+update chipRun set state = 'full' where state = 'stop';
+
+-- from eam_branch_20080806
+-- add 'data_state' to detrend tables
+
+alter table detProcessedImfile      add column data_state varchar(64) after path_base;
+alter table detProcessedExp         add column data_state varchar(64) after path_base;
+alter table detStackedImfile        add column data_state varchar(64) after user_5;
+alter table detNormalizedStatImfile add column data_state varchar(64) after norm;
+alter table detNormalizedImfile     add column data_state varchar(64) after path_base;
+alter table detNormalizedExp 	    add column data_state varchar(64) after path_base;
+alter table detResidImfile 	    add column data_state varchar(64) after path_base;
+alter table detResidExp 	    add column data_state varchar(64) after path_base;
+alter table detRunSummary 	    add column data_state varchar(64) after iteration;
+alter table detRegisteredImfile     add column data_state varchar(64) after path_base;
+
+update camRun set state = 'new' where state = 'run';
+update camRun set state = 'full' where state = 'stop';
+
+update fakeRun set state = 'new' where state = 'run';
+update fakeRun set state = 'full' where state = 'stop';
+
+update warpRun set state = 'new' where state = 'run';
+update warpRun set state = 'full' where state = 'stop';
+
+update stackRun set state = 'new' where state = 'run';
+update stackRun set state = 'full' where state = 'stop';
+
+update diffRun set state = 'new' where state = 'run';
+update diffRun set state = 'full' where state = 'stop';
+
+-- not sure when 'filter' was added to stackRun, but it is needed now:
+
+alter table stackRun add column filter varchar(64) after tess_id;
+
+-- changes to the flatcorr tables : no valid flatcorr tables have been created to date expect my tests
+-- this sql just drops and recreates the flatcorrRun with the correct layout
+
+delete from flatcorrExp;
+delete from flatcorrRun;
+
+drop table flatcorrExp;
+drop table flatcorrRun;
+
+CREATE TABLE flatcorrRun (
+        corr_id BIGINT AUTO_INCREMENT,
+        dvodb VARCHAR(64),
+        filter VARCHAR(64),
+        state VARCHAR(64),
+        workdir VARCHAR(255),
+        label VARCHAR(64),
+        reduction VARCHAR(64),
+        region VARCHAR(64),
+        hostname VARCHAR(64),
+        fault SMALLINT NOT NULL,
+        PRIMARY KEY(corr_id),
+        KEY(corr_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE flatcorrChipLink (
+        corr_id BIGINT,
+        chip_id BIGINT,
+        PRIMARY KEY(corr_id, chip_id),
+        FOREIGN KEY (corr_id)  REFERENCES  flatcorrRun(corr_id),
+        FOREIGN KEY (chip_id)  REFERENCES  chipRun(chip_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE flatcorrCamLink (
+        corr_id BIGINT,
+        chip_id BIGINT,
+        cam_id BIGINT,
+        PRIMARY KEY(corr_id, chip_id, cam_id),
+        FOREIGN KEY (corr_id)  REFERENCES  flatcorrRun(corr_id),
+        FOREIGN KEY (chip_id)  REFERENCES  chipRun(chip_id),
+        FOREIGN KEY (cam_id)  REFERENCES  camRun(cam_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+-- I initially defined flatcorrCamLin without the chip_id entry.  this
+-- command corrects for that, but should not be needed widely.
+-- ALTER TABLE flatcorrCamLink ADD COLUMN chip_id BIGINT AFTER corr_id;
+-- alter table flatcorrRun add column hostname varchar(64) after region;
+-- alter table flatcorrRun add column fault smallint after hostname;
+-- alter table flatcorrRun add column reduction varchar(64) after stats;
+
+-- changes to the chip tables to support cleanup and reprocessing
+
+ALTER TABLE chipProcessedImfile ADD COLUMN data_state VARCHAR(64) AFTER class_id;
+UPDATE chipProcessedImfile SET data_state = 'full';
+
+-- I have added 'label' and 'reduction' entries to stack and diff
+
+ALTER TABLE stackRun ADD COLUMN label VARCHAR(64) AFTER workdir;
+ALTER TABLE stackRun ADD COLUMN reduction VARCHAR(64) AFTER label;
+
+ALTER TABLE stackSumSkyfile add column data_state varchar(64) after path_base;
+UPDATE stackSumSkyfile SET data_state = 'full';
+
+alter table diffRun add column label varchar(64) after workdir;
+alter table diffRun add column reduction varchar(64) after label;
+
+ALTER TABLE diffSkyfile add column data_state varchar(64) after path_base;
+UPDATE diffSkyfile SET data_state = 'full';
+
+ALTER TABLE warpSkyfile add column data_state varchar(64) after path_base;
+UPDATE warpSkyfile SET data_state = 'full';
+
+ALTER TABLE fakeProcessedImfile add column data_state varchar(64) after path_base;
+update fakeProcessedImfile set data_state ='full';
+
+-- adding ref_det_id and ref_iter to detRun and detResidImfile
+
+ALTER TABLE detResidImfile add column ref_det_id bigint after iteration;
+ALTER TABLE detResidImfile add column ref_iter int after ref_det_id;
+
+ALTER TABLE detRun change column parent ref_det_id bigint;
+ALTER TABLE detRun add column ref_iter int after ref_det_id;
+
+-- populate ref_det_id and ref_iter 
+UPDATE detResidImfile SET ref_det_id = det_id, ref_iter = iteration;
+
+-- Next we drop the old constraint and add the new one
+ALTER TABLE detResidImfile DROP FOREIGN KEY detResidImfile_ibfk_3;
+ALTER TABLE detResidImfile add FOREIGN KEY  (ref_det_id, ref_iter)
+    REFERENCES  detNormalizedExp(det_id, iteration);
+
+--- Additions for stack/diff QA by PAP.
+ALTER TABLE diffSkyfile ADD COLUMN stamps_mean FLOAT AFTER stamps_num;
+ALTER TABLE diffSkyfile ADD COLUMN dtime_match FLOAT AFTER dtime_diff;
+ALTER TABLE diffSkyfile ADD COLUMN dtime_phot FLOAT AFTER dtime_match;
+
+ALTER TABLE stackSumSkyfile ADD COLUMN dtime_match_mean FLOAT AFTER dtime_stack;
+ALTER TABLE stackSumSkyfile ADD COLUMN dtime_match_stdev FLOAT AFTER dtime_match_mean;
+ALTER TABLE stackSumSkyfile ADD COLUMN dtime_initial FLOAT AFTER dtime_match_stdev;
+ALTER TABLE stackSumSkyfile ADD COLUMN dtime_reject FLOAT AFTER dtime_initial;
+ALTER TABLE stackSumSkyfile ADD COLUMN dtime_final FLOAT AFTER dtime_reject;
+ALTER TABLE stackSumSkyfile ADD COLUMN dtime_phot FLOAT AFTER dtime_final;
+ALTER TABLE stackSumSkyfile ADD COLUMN match_mean FLOAT AFTER dtime_phot;
+ALTER TABLE stackSumSkyfile ADD COLUMN match_stdev FLOAT AFTER match_mean;
+ALTER TABLE stackSumSkyfile ADD COLUMN match_rms FLOAT AFTER match_stdev;
+ALTER TABLE stackSumSkyfile ADD COLUMN stamps_mean FLOAT AFTER match_rms;
+ALTER TABLE stackSumSkyfile ADD COLUMN stamps_stdev FLOAT AFTER stamps_mean;
+ALTER TABLE stackSumSkyfile ADD COLUMN stamps_min INT AFTER stamps_stdev;
+ALTER TABLE stackSumSkyfile ADD COLUMN reject_images INT AFTER stamps_min;
+ALTER TABLE stackSumSkyfile ADD COLUMN reject_pix_mean FLOAT AFTER reject_images;
+ALTER TABLE stackSumSkyfile ADD COLUMN reject_pix_stdev FLOAT AFTER reject_pix_mean;
+ALTER TABLE stackSumSkyfile ADD COLUMN sources INT AFTER reject_pix_stdev;
+ALTER TABLE stackSumSkyfile DROP COLUMN data_state;
+ALTER TABLE diffSkyfile DROP COLUMN data_state;
+
+-- Adding the new solar-system info (sun & moon data)
+ALTER TABLE rawExp CHANGE COLUMN solang sun_angle FLOAT;
+ALTER TABLE rawExp ADD COLUMN sun_alt    FLOAT AFTER sun_angle;
+ALTER TABLE rawExp ADD COLUMN moon_angle FLOAT AFTER sun_alt;
+ALTER TABLE rawExp ADD COLUMN moon_alt   FLOAT AFTER moon_angle;
+ALTER TABLE rawExp ADD COLUMN moon_phase FLOAT AFTER moon_alt;
+
+ALTER TABLE rawImfile ADD COLUMN sun_angle  FLOAT AFTER object;
+ALTER TABLE rawImfile ADD COLUMN sun_alt    FLOAT AFTER sun_angle;
+ALTER TABLE rawImfile ADD COLUMN moon_angle FLOAT AFTER sun_alt;
+ALTER TABLE rawImfile ADD COLUMN moon_alt   FLOAT AFTER moon_angle;
+ALTER TABLE rawImfile ADD COLUMN moon_phase FLOAT AFTER moon_alt;
+
+CREATE TABLE pstampProject (
+        proj_id BIGINT AUTO_INCREMENT,
+        state VARCHAR(64),
+        dbname VARCHAR(64),
+        dvodb VARCHAR(64),
+        camera VARCHAR(64),
+        telescope VARCHAR(64),
+        need_magic TINYINT,
+        PRIMARY KEY(proj_id),
+        KEY(proj_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+
Index: /tags/sj_tags/sj_root_20080929/dbconfig/chip.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/chip.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/chip.md	(revision 22322)
@@ -0,0 +1,51 @@
+chipRun METADATA
+    chip_id     S64         0       # Primary Key AUTO_INCREMENT
+    exp_id      S64         64      # Key INDEX(chip_id, exp_id) fkey (exp_id) ref rawExp(exp_id)
+    state       STR         64      # Key
+    workdir     STR         255 
+    workdir_state STR       64      # Key
+    label       STR         64      # Key
+    reduction   STR         64      # Reduction class
+    expgroup    STR         64      # Key
+    dvodb       STR         255
+    tess_id     STR         64
+    end_stage   STR         64      # Key
+END
+
+chipProcessedImfile METADATA
+    chip_id         S64     0       # Primary Key fkey (chip_id, exp_id) ref chipRun(chip_id, exp_id)
+    exp_id          S64     64      # Primary Key fkey (exp_id, class_id) ref rawImfile(exp_id, class_id)
+    class_id        STR     64      # Primary Key
+    data_state      STR     64      # Key
+    uri             STR     255
+    bg              F32     0.0
+    bg_stdev        F32     0.0
+    bg_mean_stdev   F32     0.0
+    bias	    F32	    0.0
+    bias_stdev      F32	    0.0
+    fringe_0        F32     0.0
+    fringe_1        F32     0.0
+    fringe_2        F32     0.0
+    sigma_ra        F32     0.0
+    sigma_dec       F32     0.0
+    ap_resid        F32	    0.0
+    ap_resid_stdev  F32	    0.0
+    zp_mean         F32     0.0
+    zp_stdev        F32     0.0
+    fwhm_major      F32     0.0
+    fwhm_minor      F32     0.0
+    dtime_detrend   F32     0.0
+    dtime_photom    F32     0.0
+    dtime_astrom    F32     0.0
+    hostname        STR     64
+    n_stars         S32	    0
+    n_extended      S32	    0
+    n_cr            S32	    0
+    n_astrom        S32     0
+    path_base       STR     255
+    fault           S16     0       # Key NOT NULL
+END
+
+chipMask METADATA
+    label       STR         64      # Primary Key
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/config.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/config.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/config.md	(revision 22322)
@@ -0,0 +1,5 @@
+glueforge METADATA
+    pkg_name        STR     ippdb
+    pkg_namespace   STR     ippdb
+    pkg_version     STR     1.1.39
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/det.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/det.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/det.md	(revision 22322)
@@ -0,0 +1,276 @@
+detRun METADATA
+    det_id      S64         0       # Primary Key AUTO_INCREMENT
+    iteration   S32         0       # Key INDEX(det_id, iteration)
+    det_type    STR         64      # Key
+    mode        STR         64      # Key
+    state       STR         64      # Key
+    filelevel   STR         64
+    workdir     STR         255     # destination for output files
+    camera      STR         64
+    telescope   STR         64
+    exp_type    STR         64      # XXX this should be dropped
+    reduction   STR         64      # Reduction clas
+    filter      STR         64
+    airmass_min F32         0.0
+    airmass_max F32         0.0
+    exp_time_min F32        0.0
+    exp_time_max F32        0.0
+    ccd_temp_min F32        0.0
+    ccd_temp_max F32        0.0
+    posang_min  F64         0.0 
+    posang_max  F64         0.0 
+    registered  TAI         0001-01-01T00:00:00Z
+    time_begin  TAI         0001-01-01T00:00:00Z
+    time_end    TAI         0001-01-01T00:00:00Z
+    use_begin   TAI         0001-01-01T00:00:00Z
+    use_end     TAI         0001-01-01T00:00:00Z
+    solang_min  F32         0.0
+    solang_max  F32         0.0
+    label       STR         64      # key
+    ref_det_id  S64         0	    # reference for 'verify' and 'correction' analysis
+    ref_iter    S32         0	    # reference for 'verify' and 'correction' analysis
+END
+
+detInputExp METADATA
+    det_id      S64         0       # Primary Key fkey(det_id) ref detRun(det_id)
+    iteration   S32         0       # Primary Key fkey(exp_id) ref rawExp(exp_id)
+    exp_id     S64         64       # Primary Key INDEX(det_id, exp_id)
+    include     BOOL        f       # INDEX(det_id, iteration)
+END
+
+detProcessedImfile METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, exp_id) ref detInputExp(det_id, exp_id) 
+    exp_id     S64         64       # Primary Key fkey(exp_id, class_id) ref rawImfile(exp_id, class_id)
+    class_id    STR         64      # Primary Key INDEX(det_id, class_id)
+    uri         STR         255     # INDEX(det_id, exp_id)
+    recipe      STR         64
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    fringe_0    F64         0.0
+    fringe_1    F64         0.0
+    fringe_2    F64         0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    path_base   STR         255
+    data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
+    fault       S16         0       # Key NOT NULL
+END
+
+detProcessedExp METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, exp_id) ref detInputExp(det_id, exp_id)
+    exp_id     S64         64       # Primary Key fkey(det_id, exp_id) ref detProcessedImfile(det_id, exp_id)
+    recipe      STR         64
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    fringe_0    F64         0.0
+    fringe_1    F64         0.0
+    fringe_2    F64         0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    path_base   STR         255
+    data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
+    fault       S16         0       # Key NOT NULL
+END
+
+#
+# detStackedImfile does not depend on detProcessedExp. detProcesedExp is purely
+# FYI values and can be calculated in parellel
+#
+detStackedImfile METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, iteration) ref detInputExp(det_id, iteration)
+    iteration   S32         0       # Primary Key fkey(det_id, class_id) ref detProcessedImfile(det_id, class_id)
+    class_id    STR         64      # Primary Key
+    uri         STR         255
+    recipe      STR         64
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+#   XXX does it make sense to 'clean' the stacked imfiled?
+    data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
+    fault       S16         0       # Key NOT NULL
+END
+
+detNormalizedStatImfile METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, iteration) ref detInputExp(det_id, iteration)
+    iteration   S32         0       # Primary Key fkey(det_id, iteration, class_id) ref detStackedImfile(det_id, iteration, class_id)
+    class_id    STR         64      # Primary Key
+    norm        F32         0.0
+    data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
+    fault       S16         0       # Key NOT NULL
+END
+
+## XXX this must match in fields with detRegisteredImfile
+detNormalizedImfile METADATA
+    det_id      S64         0       # Primary Key fkey(det_id) ref detInputExp(det_id)
+    iteration   S32         0       # Primary Key fkey(det_id, iteration, class_id) ref detNormalizedStatImfile(det_id, iteration, class_id)
+    class_id    STR         64      # Primary Key INDEX(det_id, iteration)
+    uri         STR         255
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    path_base   STR         255
+    data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
+    fault       S16         0       # Key NOT NULL
+END
+
+detNormalizedExp METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, iteration) ref detInputExp(det_id, iteration)
+    iteration   S32         0       # Primary Key fkey(det_id, iteration) ref detNormalizedImfile(det_id, iteration)
+    recipe      STR         64
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    path_base   STR         255
+    data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
+    fault       S16         0       # Key NOT NULL
+END
+
+#detMasterFrame METADATA
+#    det_id      S64         0       # Primary Key
+#    iteration   S32         0       # Primary Key
+#    comment     STR         255
+#END
+#
+## drop?
+#detMasterImfile METADATA
+#    det_id      S64         0       # Primary Key
+#    class_id    STR         64      # Primary Key
+#    uri         STR         255
+#    recipe      STR         64
+#END
+
+detResidImfile METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, iteration, exp_id) ref detInputExp(det_id, iteration, exp_id)
+    iteration   S32         0       # Primary Key fkey(det_id, exp_id, class_id) ref detProcessedImfile(det_id, exp_id, class_id)
+    ref_det_id  S64         0	    # detrend master actually applied (same as above for 'master', but not for 'verify')
+    ref_iter    S32         0	    # detrend master actually applied (same as above for 'master', but not for 'verify')
+    exp_id      S64         64      # Primary Key fkey(det_id, iteration) ref detNormalizedExp(det_id, iteration)
+    class_id    STR         64      # Primary Key INDEX(det_id, iteration, exp_id)
+    uri         STR         255
+    recipe      STR         64
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    bg_skewness F64         0.0
+    bg_kurtosis F64         0.0
+    bin_stdev   F64         0.0
+    fringe_0    F64         0.0
+    fringe_1    F64         0.0
+    fringe_2    F64         0.0
+    fringe_resid_0  F64     0.0
+    fringe_resid_1  F64     0.0
+    fringe_resid_2  F64     0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    path_base   STR         255
+    data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
+    fault       S16         0       # Key NOT NULL
+END
+
+detResidExp METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, iteration, exp_id) ref detInputExp(det_id, iteration, exp_id)
+    iteration   S32         0       # Primary Key fkey(det_id, iteration, exp_id) ref detResidImfile(det_id, iteration, exp_id)
+    exp_id     S64         64       # Primary Key INDEX(det_id, iteration)
+    recipe      STR         64
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    bg_skewness F64         0.0
+    bg_kurtosis F64         0.0
+    bin_stdev   F64         0.0
+    fringe_0    F64         0.0
+    fringe_1    F64         0.0
+    fringe_2    F64         0.0
+    fringe_resid_0  F64     0.0
+    fringe_resid_1  F64     0.0
+    fringe_resid_2  F64     0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    path_base   STR         255
+    data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
+    accept      BOOL        f
+    fault       S16         0       # Key NOT NULL
+END
+
+# XXX this probably should have been defined at the start of a new iteration, not at the end.
+detRunSummary METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, iteration) ref detInputExp(det_id, iteration)
+    iteration   S32         0       # Primary Key fkey(det_id, iteration) ref detResidExp(det_id, iteration)
+    data_state  STR         64      # full, goto_cleaned, cleaned, goto_full, goto_purged, purged
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    accept      BOOL        f
+    fault       S16         0       # Key NOT NULL
+END
+
+#
+# Note: This table needs to stay more or less identical to detNormalizedImfile.
+# It only exists as a seperate entity so they it can have different fkeys
+#
+detRegisteredImfile METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, iteration) ref detRun(det_id, iteration)
+    iteration   S32         0       # Primary Key
+    class_id    STR         64      # Primary Key
+    uri         STR         255
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    path_base   STR         255
+    data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
+    fault       S16         0       # Key NOT NULL
+END
+
+detCorrectedExp METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, exp_id) ref detInputExp(det_id, exp_id) 
+    exp_id      S64         64      # Primary Key fkey(exp_id, class_id) ref rawImfile(exp_id, class_id)
+    uri         STR         255     # INDEX(det_id, exp_id)
+    corr_id     S64         0
+    corr_type   STR         64
+    recipe      STR         64
+    path_base   STR         255
+    fault       S16         0       # Key NOT NULL
+END
+
+detCorrectedImfile METADATA
+    det_id      S64         0       # Primary Key fkey(det_id, exp_id) ref detInputExp(det_id, exp_id) 
+    exp_id     S64         64       # Primary Key fkey(exp_id, class_id) ref rawImfile(exp_id, class_id)
+    class_id    STR         64      # Primary Key INDEX(det_id, class_id)
+    uri         STR         255     # INDEX(det_id, exp_id)
+    path_base   STR         255
+    fault       S16         0       # Key NOT NULL
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/diff.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/diff.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/diff.md	(revision 22322)
@@ -0,0 +1,47 @@
+# $Id: diff.md,v 1.12 2008-09-23 19:58:27 bills Exp $
+
+diffRun METADATA
+    diff_id     S64         0       # Primary Key AUTO_INCREMENT
+    state       STR         64      # Key
+    label       STR         64      # Key
+    reduction   STR         64      # Reduction class
+    workdir     STR         255
+    dvodb       STR         255
+    registered  TAI         NULL
+    skycell_id  STR         64      # Key
+    tess_id     STR         64      # Key
+END
+
+#
+# Diff Sky Cells Mode
+#
+
+# only ever 2 per run - one template / one not
+diffInputSkyfile METADATA
+    diff_id     S64         0       # Primary Key fkey(diff_id) ref diffRun(diff_id)
+    template    BOOL        f       # Primary Key
+    stack_id    S64         0       # fkey(stack_id) ref stackSumSkyfile(stack_id) 
+    warp_id     S64         0       # fkey(warp_id, skycell_id, tess_id) ref warpSkyfile(warp_id, skycell_id, tess_id)
+    skycell_id  STR         64      # Key
+    tess_id     STR         64      # Key
+# either a input or a template
+    kind        STR         64      # Key 
+END
+
+diffSkyfile METADATA
+    diff_id     S64         0       # Primary Key fkey(diff_id) ref diffRun(diff_id)
+    uri         STR         255
+    path_base   STR         255
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    stamps_num  S32         0
+    stamps_mean F32         0.0
+    stamps_rms  F32         0.0
+    sources     S32         0
+    dtime_diff  F32         0.0
+    dtime_match F32         0.0
+    dtime_phot  F32         0.0
+    hostname    STR         64
+    good_frac   F32         0.0     # Key
+    fault       S16         0       # Key
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/dimm.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/dimm.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/dimm.md	(revision 22322)
@@ -0,0 +1,21 @@
+#              Table 13: DIMM Measurements Table
+# Column Name Datatype Description
+# Time          date/time   The time the DIMM observation was taken.
+# sigmax        float Raw dispersion in x.
+# sigmay        float Raw dispersion in y.
+# FWHM          float  Dervied seeing full width at half maximum.
+# RA            float  The coordinates of the measured star.
+# DEC           float  The coordinates of the measured star.
+# Exposure time float  The exposure time of the DIMM observation.
+# Telescope ID  string source of the DIMM data
+
+dimm METADATA
+#    time        DATETIME    2006-01-11T00:00:00
+    sigmax      F32         0.0
+    sigmay      F32         0.0
+    fwhm        F32         0.0
+    ra          F64         0.0
+    decl        F64         0.0
+    expttime    F32         0.0
+    telescope_id    STR     255
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/dome.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/dome.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/dome.md	(revision 22322)
@@ -0,0 +1,16 @@
+#                     Table 15: Dome Status Table
+# Column Name   Datatype Description
+# Time          date/time     The time for which the dome status is valid.
+# Azimuth       float         The azimuth of the dome.
+# Open status   boolean       Whether the dome is open or not.
+# Lights status boolean       Whether lights are on in the dome or not.
+# Track status  boolean       Whether dome is tracking telescope or not.
+
+dome METADATA
+#    time        DATETIME    2006-01-11T00:00:00
+    az          F32         0.0
+    open        BOOL        t
+    light       BOOL        t
+    # XXX is it possible for the dome slit to not track the telescope? ;)
+    track       BOOL        t
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/fake.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/fake.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/fake.md	(revision 22322)
@@ -0,0 +1,30 @@
+fakeRun METADATA
+    fake_id     S64         0       # Primary Key AUTO_INCREMENT
+    cam_id      S64         0       # Key INDEX(fake_id, cam_id) fkey (cam_id) ref camRun(cam_id)
+    state       STR         64      # Key
+    workdir     STR         255 
+    label       STR         64      # Key
+    reduction   STR         64      # Reduction class
+    expgroup    STR         64      # Key
+    dvodb       STR         255
+    tess_id     STR         64
+    end_stage   STR         64      # Key
+    epoch       UTC         0001-01-01T00:00:00Z
+END
+
+fakeProcessedImfile METADATA
+    fake_id         S64     0       # Primary Key fkey (fake_id) ref fakeRun(fake_id)
+    exp_id          S64     64      # Primary Key fkey (exp_id, class_id) ref rawImfile(exp_id, class_id)
+    class_id        STR     64      # Primary Key
+    uri             STR     255
+    dtime_fake      F32     0.0
+    hostname        STR     64
+    path_base       STR     255
+    data_state      STR     64      # Key
+    fault           S16     0       # Key NOT NULL
+    epoch           UTC         0001-01-01T00:00:00Z
+END
+
+fakeMask METADATA
+    label       STR         64      # Primary Key
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/flatcorr.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/flatcorr.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/flatcorr.md	(revision 22322)
@@ -0,0 +1,28 @@
+
+# table of flat-field correction runs
+flatcorrRun METADATA
+    corr_id     S64         0       # Primary Key AUTO_INCREMENT	
+    dvodb       STR         64
+    filter      STR         64
+    state       STR         64
+    workdir     STR         255 
+    label       STR         64
+    reduction   STR         64
+    region      STR         64
+    hostname    STR         64
+    fault       S16         0       # Key NOT NULL
+END
+
+# table of Exposure-level data used for each flat-field corrction run
+flatcorrChipLink METADATA
+    corr_id     S64         0       # Primary Key fkey(corr_id) ref flatcorrRun(corr_id)
+    chip_id     S64         0       # Primary Key fkey(chip_id) ref chipRun(chip_id)
+END
+
+# table of Exposure-level data used for each flat-field corrction run
+flatcorrCamLink METADATA
+    corr_id     S64         0       # Primary Key fkey(corr_id) ref flatcorrRun(corr_id)
+    chip_id     S64         0       # Primary Key fkey(chip_id) ref chipRun(chip_id)
+    cam_id      S64         0       # Primary Key fkey(chip_id) ref chipRun(chip_id)
+END
+
Index: /tags/sj_tags/sj_root_20080929/dbconfig/guide.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/guide.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/guide.md	(revision 22322)
@@ -0,0 +1,5 @@
+guidePendingExp METADATA
+    guide_id    S64         0       # Primary Key AUTO_INCREMENT
+    exp_id     S64         64      # Key
+    recipe      STR         64
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/ipp.m4
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/ipp.m4	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/ipp.m4	(revision 22322)
@@ -0,0 +1,23 @@
+include(config.md)
+dnl include(weather.md)
+dnl include(skyp_transparency.md)
+dnl include(skyp_absorption.md)
+dnl include(skyp_emission.md)
+dnl include(dimm.md)
+dnl include(skyp_ir.md)
+dnl include(dome.md)
+dnl include(telescope.md)
+dnl include(skycell.md)
+include(tasks.md)
+include(guide.md)
+include(chip.md)
+include(cam.md)
+include(fake.md)
+include(warp.md)
+include(diff.md)
+include(stack.md)
+include(det.md)
+include(magic.md)
+include(calibration.md)
+include(flatcorr.md)
+include(pstamp.md)
Index: /tags/sj_tags/sj_root_20080929/dbconfig/magic.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/magic.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/magic.md	(revision 22322)
@@ -0,0 +1,47 @@
+# $Id: magic.md,v 1.10 2008-07-23 01:52:30 price Exp $
+
+### Fault in magicRun indicates that the processing tree failed
+magicRun METADATA
+    magic_id    S64         0       # Primary Key AUTO_INCREMENT
+    exp_id      S64         0       # Key
+    state       STR         64      # Key
+    workdir     STR         255
+    workdir_state STR       255     # Key
+    label       STR         64      # key
+    dvodb       STR         255
+    registered  TAI         NULL
+    fault       S16         0       # Key
+END
+
+magicInputSkyfile METADATA
+    magic_id    S64         0       # Primary Key fkey(magic_id) ref magicRun(magic_id)
+    diff_id     S64         0       # Primary Key fkey(diff_id) ref diffRun(diff_id)
+    node        STR         64      #
+END
+
+magicTree METADATA
+    magic_id    S64         0       # Key fkey(magic_id) ref magicRun(magic_id)
+    node        STR         64      # Key INDEX(magic_id, node)
+    dep         STR         64      # Key
+END
+
+magicNodeResult METADATA
+    magic_id    S64         0       # Primary Key fkey(magic_id) ref magicRun(magic_id)
+    node        STR         64      # Primary Key fkey(magic_id, node) ref magicTree(magic_id, node)
+    uri         STR         255
+    fault       S16         0       # Key
+END
+
+magicMask METADATA
+    magic_id    S64         0       # Primary Key fkey(magic_id) ref magicRun(magic_id)
+    uri         STR         255
+    streaks     S32         0
+    fault       S16         0       # Key
+END
+
+### I don't think we need this if magic produces mask descriptions. -- PAP.
+#magicSkyfileMask METADATA
+#    magic_id    S64         0       # Primary Key fkey(magic_id) ref magicRun(magic_id)
+#    diff_id     S64         0       # Primary Key fkey(magic_id, diff_id) ref magicInputSkyfile(magic_id, diff_id)
+#    uri         STR         255     #  fkey(magic_id) ref magicMask(magic_id)
+#END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/notes.txt	(revision 22322)
@@ -0,0 +1,41 @@
+
+2007.06.08 : EAM
+
+ * when changing the database schema:
+   * increment the pkg_version number on dbconfig/config.md
+   * increment the ippdb version number in ippTools/configure.ac (to match)
+   * increment the ippTools version number in ippTools/configure.ac
+   * build ippbd ('make src' in dbconfig)
+   * check in dbconfig, ippdb, and ippTools
+
+2007.06.04 : EAM
+
+I am adding additional fields which we will need for selecting the
+input detrend images:
+
+rawExp : 
+ user stat 1
+ user stat 2
+ user stat 3
+ user stat 4
+ user stat 5
+
+detProcessedImfile, detProcessedExp:
+ fringe amplitude
+ user stat 1
+ user stat 2
+ user stat 3
+ user stat 4
+ user stat 5
+
+detResidImfile, detResidExp:
+ binned stdev
+ fringe_0
+ fringe_1
+ fringe_2
+ user stat 1
+ user stat 2
+ user stat 3
+ user stat 4
+ user stat 5
+
Index: /tags/sj_tags/sj_root_20080929/dbconfig/pstamp.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/pstamp.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/pstamp.md	(revision 22322)
@@ -0,0 +1,42 @@
+pstampDataStore METADATA
+    ds_id       S64         0       # Primary Key AUTO_INCREMENT
+    state       STR         64
+    lastFileset STR         64
+    outProduct  STR         64
+    uri         STR         255
+END
+
+pstampProject METADATA
+    proj_id     S64         0       # Primary Key AUTO_INCREMENT
+    name        STR         64      # UNIQUE
+    state       STR         64
+    dbname      STR         64
+    dvodb       STR         64
+    camera      STR         64
+    telescope   STR         64
+    need_magic  BOOL        f
+END
+
+pstampRequest   METADATA
+    req_id      S64         0       # Primary Key AUTO_INCREMENT
+    ds_id       S64         0
+    state       STR         64
+    name        STR         64      # UNIQUE
+    reqType     STR         16
+    outProduct  STR         64
+    uri         STR         255
+    fault       S32         0
+END
+
+pstampJob       METADATA
+    job_id      S64         0       # Primary Key AUTO_INCREMENT
+    req_id      S64         0       # Primary Key fkey(req_id) ref pstampRequest(req_id)
+    rownum      STR         64
+    state       STR         64
+    jobType     STR         16
+    fault       S32         0
+    uri         STR         255
+    exp_id      S64         0
+    outputBase  STR         255
+    args        STR         511
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/skycell.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/skycell.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/skycell.md	(revision 22322)
@@ -0,0 +1,25 @@
+# $Id: skycell.md,v 1.4 2007-07-06 01:16:08 jhoblitt Exp $
+
+skyCell METADATA
+    skycell_id  STR         64      # Primary Key
+    tess_id     STR         64      # Primary Key
+    ra1         F64         0.0
+    decl1       F64         0.0
+    ra2         F64         0.0
+    decl2       F64         0.0
+    ra3         F64         0.0
+    decl3       F64         0.0
+    ra4         F64         0.0
+    decl4       F64         0.0
+END
+
+skyCellMap METADATA
+    skycell_id  STR         64      # Primary Key
+    tess_id     STR         64      # Primary Key
+    exp_id     S64         64      # Primary Key
+    p3_version  S32         0       # Primary Key
+# class is not yet consistently carried through pXtools
+#    class       STR         64      # Primary Key
+    class_id    STR         64      # Primary Key
+END
+
Index: /tags/sj_tags/sj_root_20080929/dbconfig/skyp_absorption.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/skyp_absorption.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/skyp_absorption.md	(revision 22322)
@@ -0,0 +1,25 @@
+#                Table 11: Skyprobe Line Absorption Table (sample entries)
+# Column Name         Datatype Description
+# Time                date/time   The time the LRProbe observation was taken.
+# Disperser ID        string      ID of the dispersing element
+# Atm Component 1 float           The strength of the 1st atmospheric component.
+# Atm Component 2 float           The strength of the 2nd atmospheric component.
+# Atm Component 3 float           The strength of the 3rd atmospheric component.
+# Disperser ID        string      ID of the dispersing element
+# Number of stars     int         Number of stars used to measure the absorptions.
+# Astrometry          coords      The astrometry used on the LRProbe image.
+# Exposure time       float       The exposure time of the LRProbe image.
+# Sky brightness      float       The measured sky (surface) brightness, in physical units.
+
+skyp_absorption METADATA
+#    time        DATETIME    2006-01-10T00:00:00
+    disperser_id    STR     255
+    atmcomp1    F32         0.0
+    atmcomp2    F32         0.0
+    atmcomp3    F32         0.0
+    nstars      S32         0
+    ra          F64         0.0
+    decl        F64         0.0
+    exptime     F32         0.0
+    sky_bright  F64         0.0
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/skyp_emission.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/skyp_emission.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/skyp_emission.md	(revision 22322)
@@ -0,0 +1,20 @@
+#             Table 12: Skyprobe Line Emission Table (sample entries)
+# Column Name         Datatype Description
+# Time                date/time   The time the LRProbe observation was taken.
+# Disperser ID        string      ID of the dispersing element
+# Atm Component 1 float           The strength of the 1st atmospheric component.
+# Atm Component 2 float           The strength of the 2nd atmospheric component.
+# Atm Component 3 float           The strength of the 3rd atmospheric component.
+# Continuum           float       The strength of the continuum emission.
+# Disperser ID        string      ID of the dispersing element
+# Exposure time       float       The exposure time of the LRProbe image.
+
+skyp_emission METADATA
+#    time        DATETIME    2006-01-11T00:00:00
+    disperser_id    STR     255
+    atmcomp1    F32         0.0
+    atmcomp2    F32         0.0
+    atmcomp3    F32         0.0
+    continuum   F32         0.0
+    exptime     F32         0.0
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/skyp_ir.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/skyp_ir.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/skyp_ir.md	(revision 22322)
@@ -0,0 +1,18 @@
+#               Table 14: Near IR Wide-field Camera Results Table
+# Column Name Datatype Description
+# Time            date/time    The time the NIR observation was taken.
+# Sky brightness float         The sky (surface) brightness in the NIR observation.
+# Sky variance    float        The variance in the sky (surface) brightness.
+# Astrometry      coords       The astrometry used on the NIR image.
+# FOV X           float        field width
+# FOV Y           float        field height
+
+skyp_ir METADATA
+#    time        DATETIME    2006-01-11T00:00:00
+    sky_bright  F64         0.0 
+    sky_var     F64         0.0 
+    ra          F64         0.0
+    decl        F64         0.0
+    fov_x       F32         0.0
+    fov_y       F32         0.0
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/skyp_transparency.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/skyp_transparency.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/skyp_transparency.md	(revision 22322)
@@ -0,0 +1,21 @@
+#                Table 10: SkyProbe Transparency Table (sample entries)
+# Column Name Datatype Description
+# Time              date/time   The time the SkyProbe image was taken.
+# Filter            string      Filter used for SkyProbe image.
+# Transparency      float       The derived transparency.
+# Number of stars int           The number of stars used to measure the transparency.
+# Astrometry        coords      The astrometry used on the SkyProbe image.
+# Exposure time     float       The exposure time of the SkyProbe image.
+# Sky brightness    float       The measured sky (surface) brightness, counts / second
+
+skyp_transparency METADATA
+#    time        DATETIME    2006-01-10T00:00:00    # Primary Key
+    filter      STR         255
+    trans       F64         0.0
+    nstars      S32         0
+#    astrom
+    ra          F64         0.0
+    decl        F64         0.0
+    exptime     F32         0.0
+    sky_bright  F64         0.0
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/stack.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/stack.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/stack.md	(revision 22322)
@@ -0,0 +1,47 @@
+# $Id: stack.md,v 1.13 2008-09-23 19:58:27 bills Exp $
+
+stackRun METADATA
+    stack_id    S64         0       # Primary Key AUTO_INCREMENT
+    state       STR         64      # Key
+    workdir     STR         255
+    label       STR         64      # Key
+    reduction   STR         64      # Reduction class
+    dvodb       STR         255
+    registered  TAI         NULL
+    skycell_id  STR         64      # Key
+    tess_id     STR         64      # Key
+    filter      STR         64
+END
+
+stackInputSkyfile METADATA
+    stack_id    S64         0       # Primary Key fkey(stack_id) ref stackRun(stack_id)
+    warp_id     S64         0       # Primary Key fkey(warp_id) ref warpSkyfile(warp_id)
+END
+
+stackSumSkyfile METADATA
+    stack_id    S64         0       # Primary Key fkey(stack_id) ref stackRun(stack_id)
+    uri         STR         255
+    path_base   STR         255
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    dtime_stack F32         0.0 # Key
+    dtime_match_mean F32    0.0 # Key
+    dtime_match_stdev F32   0.0 # Key
+    dtime_initial F32       0.0 # Key
+    dtime_reject F32        0.0 # Key
+    dtime_final F32         0.0 # Key
+    dtime_phot  F32         0.0 # Key
+    match_mean  F32         0.0
+    match_stdev F32         0.0
+    match_rms   F32         0.0
+    stamps_mean F32         0.0
+    stamps_stdev F32        0.0
+    stamps_min  S32         0
+    reject_images S32       0
+    reject_pix_mean F32     0.0
+    reject_pix_stdev F32    0.0
+    sources     S32         0
+    hostname    STR         64
+    good_frac   F32         0.0     # Key
+    fault       S16         0       # Key
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/tasks.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/tasks.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/tasks.md	(revision 22322)
@@ -0,0 +1,235 @@
+# $Id: tasks.md,v 1.155 2008-09-23 20:12:05 eugene Exp $
+
+# this table records all exposure ID ever seen from the summit
+# exp_name == fileset
+
+# note that dec is a MySQL reserved word
+# note that use is a MySQL reserved word
+# note that exp is a MySQL reserved word
+
+# for use with this stored procedure to generate exp_ids under Mysql 5+
+#
+#CREATE FUNCTION genTag (exp_name varchar(64)) RETURNS VARCHAR(64)
+#BEGIN
+#    UPDATE expTagCounter SET counter = LAST_INSERT_ID(counter + 1);
+#    RETURN CONCAT(exp_name, '.', LAST_INSERT_ID());
+#END
+#
+#expTagCounter METADATA
+#   counter     U64         0 
+#END
+
+pzDataStore METADATA
+    camera      STR         64      # Primary Key
+    telescope   STR         64      # Primary Key
+    uri         STR         255
+    epoch       UTC         0001-01-01T00:00:00Z
+END
+
+# list of source exposures -- updated as exposures are seen
+# summitExp.imfiles is updated as filesets are queried default value should be
+# -1 as NULL or 0 might be a valid value (empty fileset)
+summitExp METADATA
+    exp_name    STR         64      # Primary Key
+    camera      STR         64      # Primary Key
+    telescope   STR         64      # Primary Key
+    dateobs     UTC         NULL
+    exp_type    STR         64
+    uri         STR         255
+    imfiles     S32         0
+    fault       S16         0       # Key NOT NULL
+    epoch       UTC         0001-01-01T00:00:00Z
+END
+
+# class == type of file
+# class_id == type set id
+# list of source images -- updated as exposures/filesets are queried
+summitImfile METADATA
+    exp_name    STR         64      # Primary Key fkey(exp_name, camera, telescope) ref summitExp(exp_name, camera, telescope)
+    camera      STR         64      # Primary Key
+    telescope   STR         64      # Primary Key
+    file_id     STR         64      # Key
+    bytes       S32         0
+    md5sum      STR         32
+    class       STR         64      # Primary Key
+    class_id    STR         64      # Primary Key
+    uri         STR         255
+    epoch       UTC         0001-01-01T00:00:00Z
+END
+
+# list of exposures that have had their imfiles/files registered (but not
+# downloaded) 
+pzDownloadExp METADATA
+    exp_name    STR         64      # Primary Key fkey(exp_name, camera, telescope) ref summitExp(exp_name, camera, telescope)
+    camera      STR         64      # Primary Key
+    telescope   STR         64      # Primary Key
+    state       STR         64      # Key 
+    epoch       UTC         0001-01-01T00:00:00Z
+END
+
+pzDownloadImfile METADATA
+    exp_name    STR         64      # Primary Key fkey(exp_name, camera, telescope) ref pzDownloadExp(exp_name, camera, telescope)
+    camera      STR         64      # Primary Key fkey(exp_name, camera, telescope, class, class_id) ref summitImfile(exp_name, camera, telescope, class, class_id)
+    telescope   STR         64      # Primary Key
+    class       STR         64      # Primary Key
+    class_id    STR         64      # Primary Key
+    uri         STR         255
+    fault       S16         0       # Key NOT NULL
+    epoch       UTC         0001-01-01T00:00:00Z
+END
+
+newExp METADATA
+    exp_id      S64         0       # Primary Key AUTO_INCREMENT
+    tmp_exp_name STR        64      # Key
+    tmp_camera    STR       64      # Key
+    tmp_telescope STR       64      # Key
+    state       STR         64      # Key
+    workdir     STR         255     # destination for output files
+    workdir_state STR       64      # key
+    reduction   STR         64      # Reduction class
+    dvodb       STR         255
+    tess_id     STR         64
+    end_stage   STR         64      # Key
+    label       STR         64      # Key
+    epoch       UTC         0001-01-01T00:00:00Z
+END
+
+# class needs to be carried here so it can go into rawImfile and be normalized
+# from there
+newImfile METADATA
+    exp_id      S64         64      # Primary Key fkey(exp_id) ref newExp(exp_id)
+    tmp_class_id STR        64      # Primary Key
+    uri         STR         255
+    epoch       UTC         0001-01-01T00:00:00Z
+END
+
+# paired with rawImfile
+rawExp METADATA
+    exp_id      S64         64      # Primary Key fkey(exp_id) ref newExp(exp_id)
+    exp_name    STR         64      # Key
+    camera      STR         64
+    telescope   STR         64
+    dateobs     UTC         0001-01-01T00:00:00Z
+    exp_tag     STR         255
+    exp_type    STR         64
+    filelevel   STR         64
+    workdir     STR         255     # destination for output files
+    reduction   STR         64      # Reduction class
+    dvodb       STR         255
+    tess_id     STR         64
+    end_stage   STR         64      # Key
+    filter      STR         64
+    comment     STR         80
+    airmass     F32         0.0
+    ra          F64         0.0
+    decl        F64         0.0
+    exp_time    F32         0.0
+    sat_pixel_frac F32      0.0
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    alt         F64         0.0
+    az          F64         0.0
+    ccd_temp    F32         0.0
+    posang      F64         0.0 
+    m1_x        F32         0.0
+    m1_y        F32         0.0
+    m1_z        F32         0.0
+    m1_tip      F32         0.0
+    m1_tilt     F32         0.0
+    m2_x        F32         0.0
+    m2_y        F32         0.0
+    m2_z        F32         0.0
+    m2_tip      F32         0.0
+    m2_tilt     F32         0.0
+    env_temperature F32     0.0
+    env_humidity    F32     0.0
+    env_wind_speed  F32     0.0
+    env_wind_dir    F32     0.0
+    teltemp_m1      F32     0.0
+    teltemp_m1cell  F32     0.0
+    teltemp_m2      F32     0.0
+    teltemp_spider  F32     0.0
+    teltemp_truss   F32     0.0
+    teltemp_extra   F32     0.0
+    pon_time        F32     0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    object      STR         64
+    sun_angle   F32         0.0
+    sun_alt     F32         0.0
+    moon_angle  F32         0.0
+    moon_alt    F32         0.0
+    moon_phase  F32         0.0
+    hostname    STR         64
+    fault       S16         0       # Key NOT NULL
+    epoch       UTC         0001-01-01T00:00:00Z
+END
+
+rawImfile METADATA
+    exp_id      S64         64      # Primary Key fkey(exp_id, tmp_class_id) ref newImfile(exp_id, tmp_class_id)
+    exp_name    STR         64      # UINDEX(exp_id, tmp_class_id)
+    camera      STR         64
+    telescope   STR         64
+    dateobs     UTC         0001-01-01T00:00:00Z
+    tmp_class_id    STR     64      # Key
+    class_id    STR         64      # Primary Key
+    uri         STR         255
+    exp_type    STR         64
+# This field is used to set the per exp filelevel. Thus the values for all of
+# the imfiles in an exposure need to be sanity checked to make sure that this
+# value is in argeement.
+    filelevel   STR         64 
+    filter      STR         64
+    comment     STR         80
+    airmass     F32         0.0
+    ra          F64         0.0
+    decl        F64         0.0
+    exp_time    F32         0.0
+    sat_pixel_frac F32      0.0
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    alt         F64         0.0
+    az          F64         0.0
+    ccd_temp    F32         0.0
+    posang      F64         0.0 
+    m1_x        F32         0.0
+    m1_y        F32         0.0
+    m1_z        F32         0.0
+    m1_tip      F32         0.0
+    m1_tilt     F32         0.0
+    m2_x        F32         0.0
+    m2_y        F32         0.0
+    m2_z        F32         0.0
+    m2_tip      F32         0.0
+    m2_tilt     F32         0.0
+    env_temperature F32     0.0
+    env_humidity    F32     0.0
+    env_wind_speed  F32     0.0
+    env_wind_dir    F32     0.0
+    teltemp_m1      F32     0.0
+    teltemp_m1cell  F32     0.0
+    teltemp_m2      F32     0.0
+    teltemp_spider  F32     0.0
+    teltemp_truss   F32     0.0
+    teltemp_extra   F32     0.0
+    pon_time        F32     0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    object      STR         64
+    sun_angle   F32         0.0
+    sun_alt     F32         0.0
+    moon_angle  F32         0.0
+    moon_alt    F32         0.0
+    moon_phase  F32         0.0
+    hostname    STR         64
+    fault       S16         0       # Key NOT NULL
+    epoch       UTC         0001-01-01T00:00:00Z
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/telescope.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/telescope.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/telescope.md	(revision 22322)
@@ -0,0 +1,18 @@
+#                      Table 16: Telescope Status
+# Column Name  Datatype Description
+# Time         date/time   The time for which the telescope status is valid.
+# Guide status enum        The status of the guiding.
+# Altitude     float       The telescope altitude.
+# Azimuth      float       The telescope azimuth.
+# RA           float The telescope Right Ascension (ICRS ~ J2000).
+# Dec          float The telescope Declination (ICRS ~ J2000).
+
+telescope METADATA
+#    time        DATETIME    2006-01-11T00:00:00 # Primary Key
+# XXX there is currently no way to declare an enum - use str or int instead?
+    guide       STR         255
+    alt         F32         0.0
+    az          F32         0.0
+    ra          F64         0.0
+    decl        F64         0.0
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/warp.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/warp.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/warp.md	(revision 22322)
@@ -0,0 +1,57 @@
+# $Id: warp.md,v 1.18 2008-09-05 00:19:01 eugene Exp $
+
+#
+# We have at least 3 different run types
+# a) single epoch differing
+# b) magic run operates on "complete" exposures
+# c) multiple epoch differing/stacking operations
+#
+
+# define a new warprun for a single skycell
+warpRun METADATA
+    warp_id     S64         0       # Primary Key AUTO_INCREMENT
+    fake_id      S64         0       # Key INDEX(warp_id, fake_id) fkey(fake_id) ref camProcessedExp(fake_id)
+    mode        STR         64      # Key
+    state       STR         64      # Key
+    workdir     STR         255
+    workdir_state STR       64      # Key
+    label       STR         64      # key
+    dvodb       STR         255
+    tess_id     STR         64
+    end_stage   STR         64      # Key
+    registered  TAI         NULL
+# if magic is T then look for the exp_id in the magic output tables
+    magiced     BOOL        f       # Key
+END
+
+warpSkyCellMap METADATA
+    warp_id     S64         0       # Primary Key fkey(warp_id) ref warpRun(warp_id)
+    skycell_id  STR         64      # Primary Key
+    tess_id     STR         64      # Primary Key
+    class_id    STR         64      # Primary Key
+    fault       S16         0       # Key
+END
+
+warpSkyfile METADATA
+    warp_id     S64         0       # Primary Key fkey(warp_id, skycell_id, tess_id) ref warpSkyCellMap(warp_id, skycell_id, tess_id)
+    skycell_id  STR         64      # Primary Key
+    tess_id     STR         64      # Primary Key
+    uri         STR         255
+    path_base   STR         255
+    data_state  STR         64 # Key
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    dtime_warp  F32         0.0
+    hostname    STR         64
+    good_frac   F32         0.0     # Key
+    xmin        S32         0
+    xmax        S32         0
+    ymin        S32         0
+    ymax        S32         0
+    ignored     BOOL        f       # Key
+    fault       S16         0       # Key
+END
+
+warpMask METADATA
+    label       STR         64      # Primary Key
+END
Index: /tags/sj_tags/sj_root_20080929/dbconfig/weather.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/dbconfig/weather.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dbconfig/weather.md	(revision 22322)
@@ -0,0 +1,19 @@
+#             Table 9: Weather Table: some sample weather points
+# Column Name Datatype Description
+# Time            date/time    The time the weather information was measured.
+# Temperature 01 float         The external temperature
+# Temperature 02 float         The temperature at top of the dome
+# Temperature 03 float         The temperature on the primary mirror
+# Humidity        float        The relative humidity.
+# Pressure        float        The (external) atmospheric pressure.
+
+weather METADATA
+#    time        DATETIME    2006-01-10T00:00:00 # Primary Key
+    temp01      F32         0.0
+    humi01      F32         0.0
+    temp02      F32         0.0
+    humi02      F32         0.0
+    temp03      F32         0.0
+    humi03      F32         0.0
+    pressure    F32         0.0
+END
Index: /tags/sj_tags/sj_root_20080929/doc/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.log *.dvi *.aux *.toc *.log *.out
Index: /tags/sj_tags/sj_root_20080929/doc/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/Makefile	(revision 22322)
@@ -0,0 +1,14 @@
+# Makefile for all IPP main engineering documents
+
+DIR = pslib modules design hardware dvo ipptools pantasks psphot misc manual
+
+all:
+	for i in $(DIR); do (cd $$i; make all || exit); done
+
+clean:
+	@rm -f *~ #* .*~
+	for i in $(DIR); do (cd $$i; make clean || exit); done
+
+dist:
+	@rm -f *~ #* .*~
+	for i in $(DIR); do (cd $$i; make dist || exit); done
Index: /tags/sj_tags/sj_root_20080929/doc/config/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/config/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/config/.cvsignore	(revision 22322)
@@ -0,0 +1,8 @@
+config.out
+config.pdf
+*.aux
+*.dvi
+*.lof
+*.log
+*.tbd
+*.toc
Index: /tags/sj_tags/sj_root_20080929/doc/config/ChangeLog.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/config/ChangeLog.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/config/ChangeLog.tex	(revision 22322)
@@ -0,0 +1,3 @@
+%%% $Id: ChangeLog.tex,v 1.1 2006-10-18 04:19:54 price Exp $
+
+\subsection{Changes from version DR to the present}
Index: /tags/sj_tags/sj_root_20080929/doc/config/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/config/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/config/Makefile	(revision 22322)
@@ -0,0 +1,29 @@
+# $Id: Makefile,v 1.2 2006-10-20 03:52:13 price Exp $
+
+PDFLATEX = env TEXINPUTS=../../latex/inputs:$(TEXINPUTS):.: pdflatex
+PSLATEX  = env TEXINPUTS=../../latex/inputs:$(TEXINPUTS):.: latex
+
+help:
+	@echo "USAGE: make (target)"
+	@echo "  targets: all"
+
+all : config.pdf
+
+CONFIG_FILES = \
+	config.tex
+
+config.pdf: $(CONFIG_FILES)
+
+%.pdf: %.tex
+	$(PDFLATEX) $*.tex 
+	$(PDFLATEX) $*.tex 
+	@rm -f $*.ps $*.dvi $*.aux $*.log $*.tbr $*.tbd $*.tpm  $*.lof $*.toc body.tmp head.tmp
+
+clean :
+	$(RM) *.log *.dvi *.aux *.toc *.tbd *.tbr *.tpm *.lof *.out *~ core body.tmp head.tmp
+
+dist : clean
+	$(RM) *.pdf
+
+empty : clean
+
Index: /tags/sj_tags/sj_root_20080929/doc/config/config.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/config/config.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/config/config.tex	(revision 22322)
@@ -0,0 +1,1856 @@
+%%% $Id: config.tex,v 1.7 2007-02-03 00:11:45 eugene Exp $
+\documentclass[panstarrs,spec]{panstarrs}
+
+% Basic document variables
+\title{Pan-STARRS Image Processing Pipeline}
+\subtitle{Configuration Guide}
+\shorttitle{IPP CG}
+\author{Paul Price}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-???}
+
+\begin{document}
+\maketitle
+\sloppy
+
+% -- Revision History --
+% provide explicit values for the old versions
+% use '\theversion' for the current version (set above)
+% use \hline between each table row
+\RevisionsStart
+% version  Date            Description
+DR & 2006 Oct 17 & Draft \\ \hline
+\RevisionsEnd
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\DocumentsInternal
+PSDC-430-005  &   Pan-STARRS PS-1 IPP Software Requirements Specification \\ \hline
+PSDC-430-011  &   Pan-STARRS PS-1 IPP System/Subsystem Design Description \\ \hline
+PSDC-430-012  &   Pan-STARRS PS-1 IPP Modules SDRS \\ \hline
+\DocumentsExternal
+\DocumentsEnd
+
+\tableofcontents
+\pagebreak 
+
+\listoffigures
+\pagebreak 
+\pagenumbering{arabic}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Introduction}
+
+This document describes the configuration files used for the
+Pan-STARRS Image Processing Pipeline (IPP).  Configuration files are
+used to provide parameters to the IPP programs that can be ingested at
+run-time, and easily changed (``configured'') as circumstances
+require.
+
+PSLib defines a \code{psMetadata} structure which can carry labeled
+data of arbitrary types.  The associated functions implemented in
+PSLib consist of tools to manipulate and extract data from
+\code{psMetadata} collections.  A particular application of the
+\code{psMetadata} structure within PSLib is to carry the data from a
+FITS header.  Other general-purpose information is also carried with
+the structure.  Functions are available to read/write a
+\code{psMetadata} collection from/to a text-based configuration file
+using a human-readable syntax.  We therefore use these ``metadata
+configuration'' (MDC) files as the basis for our configuration files.
+When referring to entries in an MDC file, we use the convention in
+this document that \code{NAME(TYPE)} refers to the item called
+\code{NAME}, with type \code{TYPE}.
+
+The IPP uses configuration parameters on four levels:
+\begin{itemize}
+\item Options for the particular site installation of the
+  pipeline: the {\it site};
+\item Options specifying the instrument setup: the {\it camera};
+\item Options specifying the format of the FITS file: the {\it
+  format}; and
+\item Options specifying the particular parameter choices that affect
+  the details of an analysis: the {\it recipe}.
+\end{itemize}
+Note that these are arranged in an hierarchical order, with the site
+configuration being the most general, and the recipe configuration the
+most specific.  For example, not all sites will have to deal with all
+cameras, and different cameras may require different recipes at
+different times according to their particular quirks, analysis
+experimentations, or their evolution.  Once the camera configuration
+is known under a particular circumstance, the appropriate recipe may
+be selected.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Site configuration}
+
+\subsection{Location}
+
+The location for the site configuration file itself is configurable.
+The filename can be specified by the following:
+\begin{enumerate}
+\item The \code{-site} option\footnote{\code{-site} is used for C
+programs.  For Perl programs, we use the \code{Getopt::Long} module
+which requires us to use \code{--site}} on the command line if
+provided;
+\item The environment variable \code{PS_SITE}, if defined; or
+\item \code{$HOME/.ipprc} otherwise. %$ --- Balancing the former dollar sign for Emacs
+\end{enumerate}
+
+\subsection{Contents}
+
+The site configuration carries information particular to the site,
+that is, to the global setup of the IPP.  Its responsibility is to
+configure the directories, database, cameras, recipes, and psLib.
+
+\subsubsection{Directories}
+
+The \code{PATH(STR)} entry gives a colon-delimited list of paths that
+are to be searched for configuration files.  This allows the user to
+neglect a long leading path when specifying filenames within the site
+and camera configuration files.
+
+The \code{DATAPATH(METADATA)} entry contains a series of symbolic
+links (of tyoe \code{STR}) to data directories.  This allows data to
+be moved to a different system (with different directory structure)
+without having to search and replace all paths within the database;
+and also to juggle multiple projects using the same configuration
+file.  In the Perl components of the IPP, we use \code{path://DIR} to
+mean ``look up \code{DIR} in the \code{DATAPATH} to get the
+directory''.
+
+The \code{DATAPATH(METADATA)} entry supplies a list of directories
+which may be used as aliases.  Filenames which have the form
+\code{path://PATH/remainder} will be converted to a UNIX path by
+stripping the \code{path://} component and replacing PATH with its
+value from the DATAPATH list.  This allows the database to be moved to
+a different system (with different directory structure) without having
+to search and replace all paths.
+
+\subsubsection{Database setup}
+
+The following four entries give the required information for
+\code{psDBInit()} to establish a connection with the database.
+
+\code{DBSERVER(STR)} specifies the database host name.
+
+\code{DBNAME(STR)} specifies the database name.
+
+\code{DBUSER(STR)} specifies the database user name.
+
+\code{DBPASSWORD(STR)} specifies the database password.
+\tbd{DBPASSWORD is an insecure method of storing what might be
+sensitive information.  This is to be revised in the future.}
+
+\subsubsection{Cameras}
+
+The \code{CAMERAS(METADATA)} item contains a list of cameras, with
+their corresponding camera configuration files (of type \code{STR}).
+The order is significant, since this is the order in which cameras
+will be compared with FITS headers (see
+\code{pmConfigCameraFromHeader}) --- the first matching camera found
+will be used.  For this reason, it is useful to leave a simple
+catch-all camera configuration at the end, as a fall-back option.
+
+\subsubsection{Recipes}
+
+Recipe configuration files that are common for all cameras may be
+listed in the \code{RECIPES(METADATA)}.  The list consists of the
+recipe symbolic names with corresponding filename (of type
+\code{STR}).
+
+\subsubsection{psLib}
+
+\code{TIME(STR)} specifies the location of the time configuration file
+for psLib.  This is not too important, since the original installation
+location is known by psLib.
+
+\code{LOGLEVEL(S32)} specifies the logging level for \code{psLogMsg}.
+
+\code{LOGFORMAT(STR)} specifies the log format (see
+\code{psLogSetFormat}).
+
+\code{LOGDEST(STR)} specifies the log destination (see
+\code{psMessageDestination} for acceptable formats).  We recommend
+setting this to \code{STDERR} to avoid problems associated with
+higher-level programs attempting to parse unintended input from
+\code{stdout}.
+
+\code{TRACEFORMAT(STR)} specifies the trace format (see
+\code{psTraceSetFormat}).
+
+\code{TRACEDEST(STR)} specifies the trace destination (see
+\code{psMessageDestination} for acceptable formats).  We recommend
+setting this to \code{STDERR} to avoid problems associated with
+higher-level programs attempting to parse unintended input from
+\code{stdout}.
+
+\code{TRACE(METADATA)} gives a list of trace facilities and their
+accompanying levels (of type \code{S32}); see \code{psTraceSetLevel}.
+We recommend including at least \code{err}, with level \code{10},
+since this will print all error messages.  Other useful traces to set
+are \code{psModules.camera} for camera (and especially
+\code{pmFPAfile}) operations; and \code{psLib.db} for database
+lookups.
+
+\subsection{Example}
+
+\begin{verbatim}
+### Example .ipprc file
+
+PATH            STR     .:/my/home/.ipp # Default search path for configuration files
+
+# Place your data directories here and refer to as path://PATH/remainder
+DATAPATH	METADATA
+	HERE	STR	/data/my/host/
+	THERE	STR	/data/other/host/
+END
+
+### Database configuration
+DBSERVER        STR     localhost               # Database host name (for psDBInit)
+DBNAME          STR     my_database             # Database name (for psDBInit)
+DBUSER          STR     my_name                 # Database user name (for psDBInit)
+DBPASSWORD      STR     my_password             # Database password (for psDBInit)
+
+### Setups for each camera system
+CAMERAS         METADATA
+	CTIO_MOSAIC2	STR	ctio_mosaic2/camera.config	# CTIO MOSAIC2 camera, for ESSENCE
+        MEGACAM         STR     megacam/camera.config           # Megacam, on CFHT
+        GPC1            STR     gpc1/camera.config              # Pan-STARRS GigaPixel Camera 1
+        ISP             STR     isp/camera.config               # Pan-STARRS Imaging Sky Probe
+        SIMPLE          STR     simple/camera.config            # Simple single-chip camera
+END
+
+### psLib setup
+TIME            STR     pslib/psTime.config     # Time configuration file
+LOGLEVEL        S32     9                       # Logging level; 3=INFO
+LOGFORMAT       STR     THLNM                   # Log format
+LOGDEST         STR     STDERR                  # Log destination
+TRACEDEST       STR     STDERR                  # Trace destination
+TRACEFORMAT     STR     THLNM                   # Trace format
+TRACE           METADATA                        # Trace levels
+        err                 S32     10
+#       psLib.db            S32     10
+#       psModules.camera    S32     10
+END
+
+RECIPES         METADATA                # Site-level recipes
+	PPIMAGE		STR		recipes/ppImage.config  # Image reduction
+	PPMERGE		STR		recipes/ppMerge.config	# Image combination
+ 	PPSTATS		STR		recipes/ppStats.config	# Image statistics
+	PPSTATS_PHASE0	STR		recipes/ppStats_phase0.config	# Image statistics for Phase 0
+	PSPHOT		STR     	recipes/psphot.config	# Photometry
+	PSASTRO		STR		recipes/psastro.config	# Astrometry
+END
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Camera configuration}
+
+The Focal Plane hierarchy (\code{pmFPA, pmChip, pmCell, pmReadout}) is
+explained in more detail in the psModules SDRS (PSDC-430-012).  The
+top level, an FPA, contains one or more chips, which correspond to a
+contiguous piece of silicon.  A chip contains one or more cells, which
+correspond to a single amplifier.  A cell contains one or more
+readouts, which correspond to individual reads of the detector.
+
+The purpose of the camera configuration is to define the contents of
+the Focal Plane hierarchy, and to define parameters that are
+particular to the camera.
+
+\subsection{Location}
+
+The camera configuration may be specified by the \code{-camera} option
+on the command line.  Failing that, locations for all known camera
+configuration files are specified within the site configuration, under
+the \code{CAMERAS(METADATA)}, which lists cameras by name, with their
+corresponding configuration file.  Note that the \code{PATH(STR)} in
+the site configuration defines the search path for these files.
+
+\subsection{Contents}
+
+The camera configuration specifies information particular to the data
+from a particular camera.  Note that the data from a camera may be
+stored in different formats (e.g., one amplifier per extension, vs all
+amplifiers spliced together in the PHU).  The camera configuration
+contains the formats, the camera description, filter translation
+table, observation type translation table, recipes, rejection levels
+and file rules.
+
+\subsubsection{Formats}
+
+\code{FORMATS(METADATA)} contains a list of formats for the camera,
+with their corresponding camera format configuration files (of type
+\code{STR}).  The order is significant, since this is the order in
+which formats will be compared with FITS headers (see
+\code{pmConfigCameraFormatFromHeader}) --- the first matching format
+found will be used.
+
+\subsubsection{Camera description}
+
+\code{FPA(METADATA)} contains a list of chips that comprise the focal
+plane array.  The corresponding values, of type \code{STR} are a
+whitespace-delimited list of cells that comprise the chip.  These chip
+and cell names are symbolic names, that needn't match any particular
+detail.  The chip names must be unique within the FPA, and the cell
+names must be unique within the chip.
+
+\subsubsection{Filter translation table}
+
+\code{FILTER.ID(METADATA)} contains a list of filter names (generally
+those found within the FITS header; e.g., \code{r.MP9601}), with an
+abstract name to describe the filter (e.g., \code{r}), of type
+\code{STR}.  This allows multiple descriptions of the same filter that
+may exist in the FITS headers to be resolved as the same thing.
+
+\subsubsection{Observation type translation table}
+
+\code{OBSTYPE.TABLE(METADATA)} contains a list of observation types
+(generally those found within the FITS header; e.g., \code{ZERO}),
+with an abstract name to describe the observation type (e.g.,
+\code{BIAS}).  This allows multiple descriptions of the same
+observation type that may exist in the FITS headers to be resolved as
+the same thing (e.g., \code{BIAS}, \code{ZERO} and \code{PEDESTAL} can
+all be set to \code{BIAS}).
+
+\subsubsection{Recipes}
+
+Recipe configuration files that are common for the camera may be
+listed in the \code{RECIPES(METADATA)}.  The list consists of the
+recipe symbolic names with corresponding filename (of type
+\code{STR}).
+
+\subsubsection{Rejection levels}
+
+\code{REJECTION(METADATA)} contains a list of rejection levels, by
+detrend type, for use in the detrend creation steps (see
+\code{detrend_reject_imfile.pl} and \code{detrend_reject_exp.pl}).
+For each detrend type, the following sub-components of a
+\code{METADATA}, each of type \code{STR}, are expected to be defined:
+\begin{itemize}
+\item \code{FILTER}: provides additional specificity for the rejection
+  limits, allowing multiple limits for a single detrend type to be
+  defined, with the search for the appropriate value being made with
+  an additional qualifier.  This is useful, for example, for flats,
+  where some filters require looser rejection levels than for others.
+  The use of this keyword needn't be restricted to wavelength filters,
+  however.  Note that detrend types listed more than once must be
+  declared as \code{MULTI}.  If the \code{FILTER} is \code{*} (or
+  absent), then it will only match if no filter is provided for the
+  search.
+\item \code{EXPECTED}: the expected value for the mean in residual
+  images.
+\item \code{IMFILE.MEAN}: rejection level for the mean at the imfile
+  level, after removing the expected value and taking the absolute
+  value.
+\item \code{IMFILE.STDEV}: rejection level for the standard deviation
+  at the imfile level.
+\item \code{EXP.MEAN}: rejection level for the mean at the exposure
+  level, after removing the expected value and taking the absolute
+  value.
+\item \code{EXP.STDEV}: rejection level for the standard deviation at
+  the exposure level.
+\item \code{EXP.MEANSTDEV}: rejection level for the standard deviation
+  of the mean at the exposure level.
+\item \code{ENSEMBLE.MEAN}: rejection level for an exposure within an
+  ensemble of exposures, comparing the exposure to the mean; in terms
+  of standard deviations.
+\item \code{ENSEMBLE.STDEV}: rejection level for an exposure within an
+  ensemble of exposures, comparing the variance to the mean variance;
+  in terms of standard deviations.
+\item \code{ENSEMBLE.MEANSTDEV}: rejection level for an exposure
+  within an ensemble of exposures, comparing the standard deviation of
+  the mean to the mean standard deviation of the mean; in terms of
+  standard deviations.  \tbd{Confusing enough?}
+\item \code{IMFILE.SN}: rejection level for the signal-to-noise at the
+  imfile level.
+\item \code{EXP.SN}: rejection level for the signal-to-noise at the
+  exposure level.
+\end{itemize}
+Apart from \code{FILTER}, values that are set to zero are ignored.
+
+Because the above values must be duplicated multiple times, it may
+be useful to define a type:
+\begin{verbatim}
+TYPE    LIMITS  FILTER  EXPECTED	IMFILE.MEAN	IMFILE.STDEV	EXP.MEAN	EXP.STDEV	EXP.MEANSTDEV	ENSEMBLE.MEAN	ENSEMBLE.STDEV	ENSEMBLE.MEANSTDEV	IMFILE.SN	EXP.SN
+\end{verbatim}
+
+\subsubsection{File rules}
+
+\tbd{EAM to check and supplement this description.}
+
+The file rules are one of the most important aspects of the camera
+configuration, and one of the easiest to get wrong.  When setting up a
+new camera configuration and getting errors (or worse, segmentation
+faults), check the file rules first.  Try turning up the
+\code{psModules.camera} trace level to see what's going on.
+
+\code{FILERULES(METADATA)} lists the different types of files used in
+the image processing, which specify how and when a file is read in and
+written out.  The files usually are of two or three components,
+separated by a period (not for any particular reason except that's
+what's been adopted); the first part specifies the program the file
+will be used in, the second and third parts identify its role.  For
+example, \code{PPIMAGE.INPUT} specifies the input file for
+\code{ppImage}; \code{PPIMAGE.OUTPUT.MASK} specifies the output mask
+file from \code{ppImage}.
+
+\subsubsubsection{Replacements}
+
+Throughout the file rules, a syntax for defining strings from
+variables is used: curly brackets \verb|{}| around an abstract name
+are replaced by the program to obtain the proper value.  Supported
+abstract names are:
+\begin{itemize}
+\item \verb|{OUTPUT}| --- replaced with the output file root;
+\item \verb|{CHIP.NAME}| --- replaced with the chip name;
+\item \verb|{CHIP.N}| --- replaced with the chip number (printed \code{%02d});
+\item \verb|{CELL.NAME}| --- replaced with the cell name;
+\item \verb|{CELL.N}| --- replaced with the chip number (printed \code{%02d});
+\item \verb|{EXTNAME}| --- replaced with the extension name;
+\item \verb|{FILTER}| --- replaced with the filter name (without
+  applying the \code{FILTER.ID} translation table);
+\item \verb|{FILTER.ID}| --- replaced with the filter identifier (after
+  applying the \code{FILTER.ID} translation table);
+\item \verb|{CAMERA}| --- replaced with the instrument name (from \code{FPA.INSTRUMENT});
+\item \verb|{INSTRUMENT}| --- replaced with the instrument name (from \code{FPA.INSTRUMENT});
+\item \verb|{DETECTOR}| --- replaced with the detector name (from \code{FPA.DETECTOR}); and
+\item \verb|{TELESCOPE}| --- replaced with the telescope name (from \code{FPA.TELESCOPE}).
+\end{itemize}
+
+(More could potentially be added.  If one you greatly desire is
+missing, please ask!)
+
+
+\subsubsubsection{Redirections}
+
+Entries with type \code{STR} are treated as symbolic links to another
+line.  For example, specifying:
+\begin{verbatim}
+  PPIMAGE.OUTPUT   STR   PPIMAGE.OUTPUT.SPLIT
+\end{verbatim}
+means that the program will look up \code{PPIMAGE.OUTPUT.SPLIT} in the
+place of \code{PPIMAGE.OUTPUT}.  This allows a quick replacement if a
+different output format is desired (e.g., \code{PPIMAGE.OUTPUT.MEF}
+instead of \code{PPIMAGE.OUTPUT.SPLIT}).
+
+\subsubsubsection{File types}
+\label{sec:camera-filerules-types}
+
+Both the input and output file rules use file types.  The currently
+supported file types are:
+\begin{itemize}
+\item \code{IMAGE} --- image data in FITS image format (treated as \code{F32});
+\item \code{MASK} --- mask data in FITS image format (treated as \code{U8});
+\item \code{WEIGHT} --- weight data in FITS image format (treated as \code{F32})
+\item \code{FRINGE} --- fringe image with fringe tables (one for each
+  cell) in FITS image format (image treated as \code{F32});
+\item \code{JPEG} --- image data in JPEG format (output only);
+\item \code{CMP} --- object data in CMP format;
+\item \code{CMF} --- object data in CMF format;
+\item \code{RAW} --- object data in RAW format;
+\item \code{SX} --- object data in SX format; and
+\item \code{OBJ} --- object data in OBJ format.
+\end{itemize}
+
+\tbd{EAM to fill in details on the object formats.}
+
+
+\subsubsubsection{Inputs}
+
+It is useful to make the following \code{TYPE} declaration, which can
+be used for all input files:
+\begin{verbatim}
+TYPE   INPUT   FILENAME.RULE   FILENAME.XTRA   EXTNAME.RULE   EXTNAME.XTRA   DATA.LEVEL   FILE.TYPE
+\end{verbatim}
+The components are:
+\begin{itemize}
+\item \code{FILENAME.RULE} --- this specifies the rule for
+  constructing the filename.  Options for doing so are:
+  \begin{itemize}
+  \item A simple filename, perhaps using the replacement syntax
+    defined above;
+  \item \code{@DETDB} to look up the appropriate file using the
+    detrend database (see \S\ref{sec:detrend-database}); or
+  \item \code{@FILES} to indicate that the input file(s) will be
+    specified on the command-line of the program.
+  \end{itemize}
+\item \code{FILENAME.XTRA} --- \tbd{PAP is not entirely sure what this
+  is for; it may be unnecessary.}
+\item \code{EXTNAME.RULE} --- This defines the extension name.
+  \tbd{Is this true?  PAP thinks the camera format does that; this may
+    be unnecessary, or it may have to be tied into the camera format.}
+\item \code{EXTNAME.XTRA} --- \tbd{PAP is not entirely sure what this
+  is for; it may be unnecessary.}
+\item \code{DATA.LEVEL} --- the level of the hierarchy at which the
+  data is to be opened for reading.  This should correspond to the
+  level of the extension in the FITS file, or higher.  There are some
+  checks against the camera format that this is sensical, but don't
+  bet your life on it just yet.  This is an important setting to check
+  if you're having problems.
+\item \code{FILE.TYPE} --- the type of file, from the above list
+  (\S\ref{sec:camera-filerules-types}).
+\end{itemize}
+
+\subsubsubsection{Outputs}
+
+It is useful to make the following \code{TYPE} declaration, which
+can be used for all output files:
+\begin{verbatim}
+TYPE   OUTPUT   FILENAME.RULE   FILENAME.XTRA   EXTNAME.RULE   EXTNAME.XTRA   FILE.LEVEL   DATA.LEVEL   FILE.TYPE   FILE.SAVE   FILE.FORMAT
+\end{verbatim}
+The components are:
+\begin{itemize}
+\item \code{FILENAME.RULE} --- this specifies the rule for
+  constructing the filename.  You most likely want to include
+  \verb|{OUTPUT}| somewhere here; Pan-STARRS convention is that
+  it goes at the front.
+\item \code{FILENAME.XTRA} --- \tbd{PAP is not entirely sure what this
+  is for; it may be unnecessary.}
+\item \code{EXTNAME.RULE} --- This defines the extension name.
+  \tbd{Is this true?  PAP thinks the camera format does that; this may
+  be unnecessary, or it may have to be ties into the camera format.}
+\item \code{EXTNAME.XTRA} --- \tbd{PAP is not entirely sure what this
+  is for; it may be unnecessary.}
+\item \code{FILE.LEVEL} --- the level of the hierarchy at which a file
+  should be opened and the PHU written.  This should correspond to the
+  level of the PHU.  There are some checks against the camera format
+  that this is sensical, but don't bet your life on it just yet.  This
+  is an important setting to check if you're having problems.
+\item \code{DATA.LEVEL} --- the level of the hierarchy at which an
+  extension should be written.  This should correspond to the level of
+  the extensions in the FITS file, or higher.  There are some checks
+  against the camera format that this is sensical, but don't bet your
+  life on it just yet.  This is an important setting to check if
+  you're having problems.
+\item \code{FILE.TYPE} --- the type of file, from the above list
+  (\S\ref{sec:camera-filerules-types}).
+\item \code{FILE.SAVE} --- whether this type of file should be saved
+  (\code{TRUE}) or not (\code{FALSE}).
+\item \code{FILE.FORMAT} --- if the file format is to be changed, this
+  is the name of the file format (from the \code{FORMATS} metadata).
+  Otherwise, it is \code{NONE}.
+\end{itemize}
+
+
+\subsection{Example}
+
+\begin{verbatim}
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# Camera configuration file for mcShort: describes the camera
+
+# File formats that we know about
+FORMATS         METADATA
+        RAW     STR     mcshort/format_raw.config
+        SPLICE  STR     mcshort/format_spliced.config
+        SPLIT   STR     mcshort/format_split.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        ccd12   STR     LeftAmp RightAmp
+        ccd13   STR     LeftAmp RightAmp
+        ccd14   STR     LeftAmp RightAmp
+        ccd21   STR     LeftAmp RightAmp
+        ccd22   STR     LeftAmp RightAmp
+        ccd23   STR     LeftAmp RightAmp
+END
+
+
+# Lookup table to go from FPA.FILTER to abstract name for the filter
+FILTER.ID       METADATA
+   u.MP9301     STR u
+   g.MP9401     STR g
+   r.MP9601     STR r
+   i.MP9701     STR i
+   z.MP9801     STR z
+   Zp           STR z
+   Zprime       STR z
+   Ha.MP7605    STR Ha
+   Halpha       STR Ha
+   Haalpha.on   STR Ha
+   HaOFF.MP7604 STR HaOff
+END
+
+# Lookup table to go from FPA.OBSTYPE values to abstract name for the exposure type
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES		METADATA
+	# Other recipes
+        PSPHOT          STR     megacam/psphot.config           # psphot details
+        PSASTRO         STR     megacam/psastro.config          # psastro details
+	PPSTATS		STR	megacam/ppStats.config		# ppStats recipe
+	PPIMAGE         STR     megacam/ppImage.config		# ppImage recipe
+END
+
+# Rejection levels for detrend creation
+REJECTION	METADATA
+	TYPE	LIMITS	FILTER	EXPECTED	IMFILE.MEAN	IMFILE.STDEV	EXP.MEAN	EXP.STDEV	EXP.MEANSTDEV	ENSEMBLE.MEAN	ENSEMBLE.STDEV	ENSEMBLE.MEANSTDEV	IMFILE.SN	EXP.SN
+	FLAT	MULTI
+
+	BIAS	LIMITS	*	0		1		5		0.5		3		0.5		3		3		0			0		0
+	DARK	LIMITS	*	0		1		5		0.5		3		0.5		3		3		0			0		0
+	FLAT	LIMITS	*	0		0		0		0		0		0		0		0		3			0		0
+#	FLAT	LIMITS	u	0		0		0		0		0		0		0		0		3			0		0
+#	FLAT	LIMITS	g	0		0		0		0		0		0		0		0		3			0		0
+#	FLAT	LIMITS	r	0		0		0		0		0		0		0		0		3			0		0
+#	FLAT	LIMITS	i	0		0		0		0		0		0		0		0		3			0		0
+#	FLAT	LIMITS	z	0		0		0		0		0		0		0		0		3			0		0
+	FRINGE	LIMITS	*	0		0		0		0		0		0		0		0		0			0		0
+
+# FILTER is an additional qualifier, and may be "*" (or absent!), in which case it matches everything
+# EXPECTED is the expected mean value
+# IMFILE.MEAN is the maximum permitted mean value for an imfile, relative to the standard deviation
+# IMFILE.STDEV is the maximum permitted standard deviation for an imfile
+# EXP.MEAN is the maximum permitted mean value for an exposure, relative to the standard deviation
+# EXP.STDEV is the maximum permitted standard deviation for an exposure
+# EXP.MEANSTDEV is the maximum permitted mean standard deviation for an exposure relative to the mean
+# ENSEMBLE.MEAN is the maximum permitted mean for an ensemble of exposures
+# ENSEMBLE.STDEV is the maximum permitted standard deviation for an ensemble of exposures
+# ENSEMBLE.MEANSTDEV is the maximum permitted mean standard deviation for an ensemble of exposures
+# IMFILE.SN is the minimum permitted signal-to-noise for an imfile
+# EXP.SN is the minimum permitted signal-to-noise for an exposure
+# These values (all except FILTER) may be zero, in which case no clipping is applied.
+
+END
+
+
+FILERULES METADATA
+   ### Redirections
+   PSASTRO.INPUT      STR PSASTRO.INPUT.CMP
+   PSASTRO.OUTPUT     STR PSASTRO.OUTPUT.CMP
+   PSPHOT.OUTPUT      STR PSPHOT.OUTPUT.CMF
+
+   ### input file definitions
+   TYPE               INPUT FILENAME.RULE                 FILENAME.XTRA EXTNAME.RULE EXTNAME.XTRA DATA.LEVEL FILE.TYPE 
+   PPIMAGE.INPUT      INPUT @FILES                        {CHIP.NAME}   {CELL.NAME}  NONE         CHIP       IMAGE
+   PPARITH.INPUT      INPUT @FILES                        {CHIP.NAME}   {CELL.NAME}  NONE         CHIP       IMAGE
+
+   ### use these entries to get the detrend images from specific files
+   PPIMAGE.MASK       INPUT mask.mef.fits                 {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       IMAGE     
+   PPIMAGE.BIAS       INPUT MegaCam.bias.1.0.{CHIP.NAME}.fits      {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       IMAGE     
+   PPIMAGE.DARK       INPUT MegaCam.dark.2.0.{CHIP.NAME}.fits      {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       IMAGE     
+   PPIMAGE.FLAT       INPUT MegaCam.flat.3.0.{CHIP.NAME}.fits      {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       IMAGE     
+
+   ### use these entries to get the detrend images from the database
+   #PPIMAGE.MASK       INPUT @DETDB                       {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       IMAGE     
+   #PPIMAGE.BIAS       INPUT @DETDB                       fpa           fpa          NONE         CHIP       IMAGE     
+   #PPIMAGE.DARK       INPUT @DETDB                       {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       IMAGE     
+   #PPIMAGE.FLAT       INPUT @DETDB                       {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       IMAGE     
+
+   PSPHOT.INPUT       INPUT @FILES                        {CHIP.NAME}   {CELL.NAME}  NONE         CHIP       IMAGE     
+
+   PSASTRO.INPUT.CMP  INPUT @FILES                        NONE          NONE         PHU          CHIP       CMP       
+   PSASTRO.INPUT.CMF  INPUT @FILES                        NONE          SMPDATA      PHU          CHIP       CMF       
+
+   ### output file definitions
+   TYPE                OUTPUT FILENAME.RULE                 FILENAME.XTRA EXTNAME.RULE EXTNAME.XTRA FILE.LEVEL DATA.LEVEL FILE.TYPE FILE.SAVE FILE.FORMAT
+   PPIMAGE.OUTPUT      OUTPUT {OUTPUT}.{CHIP.NAME}.fits     {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       CHIP       IMAGE     TRUE      NONE
+   PPIMAGE.BIN1        OUTPUT {OUTPUT}.{CHIP.NAME}.b1.fits  {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       CHIP       IMAGE     TRUE      MCSHORT_CHIP:MOSAIC
+   PPIMAGE.BIN2        OUTPUT {OUTPUT}.{CHIP.NAME}.b2.fits  {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       CHIP       IMAGE     TRUE      MCSHORT_CHIP:MOSAIC
+
+   PPIMAGE.OUTPUT.CHIP OUTPUT {OUTPUT}.{CHIP.NAME}.chip.fits {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       CHIP       IMAGE     TRUE      MCSHORT_CHIP:MOSAIC
+   PPIMAGE.OUTPUT.FPA1 OUTPUT {OUTPUT}.b1.fits               NONE          NONE         NONE         FPA        FPA        IMAGE     TRUE      MCSHORT_FPA:MOSAIC
+   PPIMAGE.OUTPUT.FPA2 OUTPUT {OUTPUT}.b2.fits               NONE          NONE         NONE         FPA        FPA        IMAGE     TRUE      MCSHORT_FPA:MOSAIC
+
+   PPIMAGE.JPEG1       OUTPUT {OUTPUT}.b1.jpg               -greyscale    RANGE        -5:20        FPA        FPA        JPEG      TRUE      MCSHORT_FPA:MOSAIC
+   PPIMAGE.JPEG2       OUTPUT {OUTPUT}.b2.jpg               -greyscale    FRACTION     0.50:2.00    FPA        FPA        JPEG      TRUE      MCSHORT_FPA:MOSAIC
+
+   PSPHOT.RESID        OUTPUT {OUTPUT}.res.fits             NONE          {CELL.NAME}  {CELL.NAME}  CHIP       CHIP       IMAGE     TRUE      SPLICE
+   PSPHOT.BACKGND      OUTPUT {OUTPUT}.bck.fits             NONE          {CELL.NAME}  {CELL.NAME}  CHIP       CHIP       IMAGE     TRUE      SPLICE
+   PSPHOT.BACKSUB      OUTPUT {OUTPUT}.sub.fits             NONE          {CELL.NAME}  {CELL.NAME}  CHIP       CHIP       IMAGE     TRUE      SPLICE
+   PSPHOT.BACKMDL      OUTPUT {OUTPUT}.mdl.fits             NONE          {CELL.NAME}  {CELL.NAME}  CHIP       CHIP       IMAGE     TRUE      SPLICE
+
+   PSPHOT.OUTPUT.RAW   OUTPUT {OUTPUT}.{CHIP.NAME}          NONE          NONE         PHU          CHIP       CHIP       RAW       TRUE      NONE
+   PSPHOT.OUTPUT.SX    OUTPUT {OUTPUT}.sx                   NONE          NONE         PHU          CHIP       CHIP       SX        TRUE      NONE
+   PSPHOT.OUTPUT.OBJ   OUTPUT {OUTPUT}.obj                  NONE          NONE         PHU          CHIP       CHIP       OBJ       TRUE      NONE
+   PSPHOT.OUTPUT.CMF   OUTPUT {OUTPUT}.cmf                  NONE          SMPDATA      PHU          CHIP       CHIP       CMF       TRUE      NONE
+   PSPHOT.OUTPUT.CMP   OUTPUT {OUTPUT}.{CHIP.NAME}.cmp      NONE          NONE         PHU          CHIP       CHIP       CMP       TRUE      NONE
+
+   PSASTRO.OUTPUT.CMP  OUTPUT {OUTPUT}.{CHIP.NAME}.smp      NONE          NONE         PHU          CHIP       CHIP       CMP       TRUE      NONE
+
+   PPARITH.OUTPUT      OUTPUT {OUTPUT}                      {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       CHIP       IMAGE     TRUE      NONE
+
+END
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Camera format configuration}
+
+The FITS (Flexible Image Transport System) format is a standard in the
+astronomical community for storing astronomical images.  A FITS file
+consists of an arbitrary number of coupled human readable \code{ASCII}
+header segments and binary data segments.  The headers describe the
+format and layout of the data segments.  The first of these groups is
+traditionally called the ``primary header unit'' (PHU) and the rest
+are referred to as ``extensions''.  The header segments may contain
+extensive documentary information related to the interpretation of the
+data.  Although the FITS format defines a standard representation of
+the data, the header metadata is not so consistently defined within
+the astronomical community.  Also, the flexibility of the data format
+means that it is possible to construct a variety of different
+representations for the same fundamental collection of data.
+
+The purpose of the camera format file is to define how FITS files are
+to be read into the Focal Plane hierarchy, and how the ``concepts''
+are to be ingested.
+
+\subsection{Location}
+
+The camera formats for a particular camera are listed in the
+\code{FORMATS} metadata of the camera configuration file.  Note that
+the \code{PATH} in the site configuration defines the search
+paths for these files.
+
+\subsection{Contents}
+
+The camera format specifies how a FITS file from a particular camera
+is to be read.  Different formats may be defined for a single camera
+(e.g., one amplifier per extension, or all amplifiers spliced together
+in the PHU, or anything in between).  The camera format configuration
+file contains the rules for recognising the format, how to read the
+file, the contents of a FITS file, data appropriate to different types
+of cells, information on how to determine the concepts from the
+headers, default values, or database, and expected formats for certain
+concepts.
+
+\subsubsection{Rules for recognising}
+
+\code{RULE(METADATA)} contains a list of FITS headers with expected
+values (of the appropriate type) for this particular combination of
+the camera and format.  It is often useful to include \code{TELESCOP}
+and \code{DETECTOR}, if possible, along with any other headers that
+uniquely identify the camera and format.  Note that all of the headers
+must match exactly (modulo leading and trailing spaces for strings),
+including the data type and value, for the rule to match, and that the
+first format's rule to match is accepted.  If a rule doesn't match the
+header, try adjusting the types (especially for numerical types ---
+use S32 for integers, F32 and F64 for floats).
+
+\subsubsection{How to read the file}
+
+Within the FITS data representation, there are various choices which
+can and have been made for the placement of the pixels in the file.
+In the simplest case, the camera consists of a single chip consisting
+of a single cell always read with a single readout.  In this case, the
+image data is generally written as part of the primary header unit.
+However, in a more complex case with multiple chips and multiple
+cells, the data may be organized in several ways.  The data may be
+distributed into multiple files or in multiple FITS data extensions
+within a single file..  A single camera image may be written as a
+collection of files for individual chips with separate extensions for
+each cell (CFH12K.split, GPC).  Another camera may write a single file
+with multiple extensions for each cell (Megacam.raw), or multiple
+extensions per chip, with each cell representing portions of the chip
+image (Megacam.splice, CFHT-IR).
+
+In all of these representations, there are only two basic distinctions
+in how the pixel data is stored: what level in the hierarchy the
+entire FITS file corresponds to (FPA, chip, or cell), and what level
+the extensions correspond to (chip, cell or no extensions at all).
+Knowing these, and having a list of the contents of each extension, we
+can construct the Focal Plane hierarchy.
+
+\code{FILE(METADATA)} contains information on how to read the FITS
+file for this format.  The contents are:
+\begin{itemize}
+\item \code{PHU(STR)} identifies the class of the file --- what level
+  in the focal plane hierarchy the primary header unit (PHU) of this
+  file belongs.  Legal values are \code{FPA}, \code{CHIP} or
+  \code{CELL}.
+\item \code{EXTENSIONS(STR)} identifies what level in the focal plane
+  hierarchy the extensions belong.  Legal values are \code{CHIP},
+  \code{CELL} or \code{NONE} (if there are no extensions).
+\item \code{FPA.NAME(STR)} specifies a PHU header keyword for a unique
+  identifier for the FPA.  This is usually an exposure number, or
+  similar.  The purpose is to identify the FPA, so that only files
+  with the same value of \code{FPA.NAME} can be admitted to the same
+  FPA structure.
+\item \code{CHIP.NAME(STR)} (only required if \code{PHU} is \code{CHIP} or
+  \code{CELL}) specifies a PHU header keyword that identifies the name
+  of the chip.  The purpose is to identify to which chip in the
+  hierarchy the file belongs.
+\item \code{CELL.NAME(STR)} (only required if \code{PHU} is \code{CELL})
+  specifies a PHU header keyword that identifies the name of the cell
+  within the chip.  The purpose is to identify to which cell in the
+  hierarchy the file belongs.
+\item \code{CONTENT(STR)} (only required if \code{EXTENSIONS} is
+  \code{NONE} and \code{PHU} is \code{CHIP} or \code{CELL}) specifies
+  a key to the \code{CONTENTS} menu (see below).  The purpose is to
+  identify the contents of the file (in terms of its FPA hierarchy
+  components).  The string has concepts interpolated, where these are
+  enclosed in curly brackets (currently \code{CHIP.NAME} and
+  \code{CELL.NAME} only; \tbd{future concepts may be permitted in the
+  future if there exists sufficient demand}.  This allows such a
+  construct as \verb|{CHIP.NAME}_{CELL.NAME}| to identify a
+  combination of chip and cell.
+\end{itemize}
+
+\subsubsection{File contents}
+
+The exact meaning of the \code{CONTENTS} (as well as the type) depends
+on the value of \code{PHU} and \code{EXTENSIONS} in the \code{FILE}
+metadata.  In each case, we rely on the use of \code{chip:cell:type}
+triplets to identify the contents.  These are used to identify the
+contents of an extension: the chip and cell to which a component
+belongs, and the type of the cell (see \S\ref{sec:cell_data} for cell
+types), with the symbolic names separated by colons.  Where an
+extension contains more than one cell, the triplets are listed one
+after the other, separated by whitespace.
+
+\begin{itemize}
+\item If \code{PHU} is \code{FPA} and \code{EXTENSIONS} is
+  \code{NONE}, then \code{CONTENTS} is of type \code{STR}, and
+  contains a string of \code{chip:cell:type} triplets.
+\item If \code{PHU} is \code{CHIP} or \code{CELL} and
+  \code{EXTENSIONS} is \code{NONE}, then \code{CONTENTS} is of type
+  \code{METADATA}, and contains a menu of possible contents.  Each
+  menu item is of type \code{STR}, and consists of a string of
+  \code{chip:cell:type} triplets.  The menu key is provided by the
+  interpolated \code{CONTENT} value within the \code{FILE} metadata.
+\item In all other cases, \code{CONTENTS} is of type \code{METADATA},
+  and contains a list of extension names within the file, with the
+  values of type \code{STR} consisting of a string of
+  \code{chip:cell:type} triplets.
+\end{itemize}
+
+\subsubsection{Cell data}
+\label{sec:cell_data}
+
+\code{CELLS(METADATA)} contains a list of cell types, with concepts
+particular to those types.  Each type, which corresponds to a type
+specified in the \code{CONTENTS}, is of type \code{METADATA}.  The
+contents of these metadata are values for concepts that are particular
+to that cell type (e.g., left amplifier vs right amplifier).  Usually
+\code{CELL.TRIMSEC(STR)} and \code{CELL.BIASSEC(STR)} will be listed
+here, since these differ according to the cell type.  Since there is
+ambiguity in what the values here refer to (if the concept is of type
+\code{STR}, then the value could be a header name or the actual value
+to use), we also require an additional entry with \code{.SOURCE}
+suffixed to the concept name, with the value (of type \code{STR})
+being \code{VALUE} to indicate that the concept is specified by value,
+or \code{HEADER} to indicate that the concept is specified in the
+header of the given name.
+
+[It might be thought that there is no need to provide the ability to
+look up headers here, since it is provided below.  However, the header
+name may vary depending on the cell type.  For example, the Megacam
+spliced format uses \code{TSECA} and \code{TSECB} to specify the trim
+sections for the left and right amplifiers, respectively.]
+
+\subsubsection{Concepts from headers}
+
+\code{TRANSLATION(METADATA)} contains a list of concepts that have
+their values ingested from the FITS headers.  Each concept name should
+have type \code{STR}, with the value being the header name from which
+the concept is ingested.  No distinction is made between the PHU and
+extension headers, but inheritance (look at the PHU if it's not in the
+extension header) should be the normal behaviour.  Multiple header
+keywords (separated by whitespace) may be given for certain concepts:
+\begin{itemize}
+\item \code{FPA.TIME} and \code{CELL.TIME} to specify the date and
+  time (in that order) are contained in separate header keywords.
+\item \code{CELL.BIASSEC} to specify multiple bias regions (e.g., a
+  prescan and an overscan).
+\end{itemize}
+
+\tbd{TRANSLATION is a poor name (it's supposed to be a header
+translation table); HEADERS would be better.}
+
+\subsubsection{Concepts from default values}
+
+\code{DEFAULTS(METADATA)} contains a list of concepts with their
+default values (of the appropriate types).  A concept may have type
+\code{METADATA}, in which case the metadata acts as a menu.  The menu
+key is determined from an additional entry in the \code{DEFAULTS},
+formed from the concept name suffixed with \code{.DEPEND}, which must
+be of type \code{STR} and contain a concept name.  The value of this
+extra concept determines the menu key.  This allows dependence on the
+chip (e.g., depending on \code{CHIP.NAME}) or cell (\code{CELL.NAME}),
+which is useful for setting things such as \code{CHIP.X0} when it is
+not contained in the header.
+
+\subsubsection{Concepts from database}
+
+\tbd{Database lookup for concepts has never been tested.  In fact, the
+current implementation probably doesn't even match this description.}
+
+\code{DATABASE(METADATA)} contains a list of concepts whose values are
+determined from database lookup.  Each concept is of type
+\code{METADATA}.  Each concept metadata must contain the entries
+\code{TABLE(STR)} and \code{COLUMN(STR)}, which specify the database
+table to use, and the column within that table.  Additional entries
+provide the \code{WHERE} part of the database query.
+
+
+\subsubsection{Formats for concepts}
+
+\code{FORMATS(METADATA)} contains a list of concepts that require
+additional information in order to parse.  Each concept name contains
+a value of type \code{STR} which is a list of options for parsing the
+concept.
+
+Concepts which require formats:
+\begin{itemize}
+\item \code{FPA.RA} and \code{FPA.DEC}: the format specifies the units
+  --- \code{HOURS}, \code{DEGREES} or \code{RADIANS}.  \code{FPA.RA}
+  defaults to \code{HOURS}, and \code{FPA.DEC} defaults to
+  \code{DEGREES}.
+\item \code{FPA.TIME} and \code{CELL.TIME}: \code{USA} indicates that
+  the date format is mm-dd-yyyy; \code{BACKWARDS} indicates that the
+  date format is dd-mm-yyyy; \code{PRE2000} indicates that a two-digit
+  date is used (1900 years is added if the year is less than 100);
+  \code{MJD} indicates the date is a modified julian date; \code{JD}
+  indicates the date is a julian date.
+\item \code{CELL.X0}, \code{CELL.Y0}, \code{CHIP.X0} and
+  \code{CHIP.Y0}: \code{FORTRAN} indicates that the corner lower
+  left-hand pixel corresponds to coordinates (1,1); if missing,
+  assumes that the corner is at (0,0).
+\end{itemize}
+
+\subsubsection{Default concepts}
+
+Default concepts that should be included in each camera format file,
+either in the \code{CELLS}, \code{TRANSLATION}, \code{DEFAULTS} or
+\code{DATABASE}:
+\begin{itemize}
+\item \code{FPA.TELESCOPE}: Telescope used
+\item \code{FPA.INSTRUMENT}: Instrument used
+\item \code{FPA.DETECTOR}: Detector used
+\item \code{FPA.CAMERA}: Camera used; \tbd{To be deprecated?}
+\item \code{FPA.FOCUS}: Telescope focus
+\item \code{FPA.AIRMASS}: Airmass at boresight
+\item \code{FPA.FILTER}: Filter used
+\item \code{FPA.FILTERID}: Filter identifier (parsed through the
+  \code{FILTER.ID} translation table in the camera configuration).
+\item \code{FPA.POSANGLE}: Position angle of instrument
+\item \code{FPA.RADECSYS}: Celestial coordinate system
+\item \code{FPA.RA}: Right Ascension of boresight
+\item \code{FPA.DEC}: Declination of boresight
+\item \code{FPA.OBSTYPE}: Type of observation
+\item \code{FPA.OBJECT}: Object of observation
+\item \code{FPA.ALT}: Altitude of telescope
+\item \code{FPA.AZ}: Azimuth of telescope
+\item \code{FPA.TIMESYS}: Time system
+\item \code{FPA.TIME}: Time of exposure
+\item \code{FPA.TEMP}: Temperature of the focal plane
+\item \code{FPA.EXPOSURE}: Exposure time for the focal plane
+\item \code{CHIP.XPARITY}: Orientation in x compared to the rest of the FPA
+\item \code{CHIP.YPARITY}: Orientation in y compared to the rest of the FPA
+\item \code{CHIP.X0}: Position of (0,0) on the FPA
+\item \code{CHIP.Y0}: Position of (0,0) on the FPA
+\item \code{CHIP.TEMP}: Temperature of chip
+\item \code{CELL.GAIN}: CCD gain (e/count)
+\item \code{CELL.READNOISE}: CCD read noise (e)
+\item \code{CELL.SATURATION}: Saturation level (counts)
+\item \code{CELL.BAD}: Bad level (counts)
+\item \code{CELL.XPARITY}: Orientation in x compared to the rest of the chip
+\item \code{CELL.YPARITY}: Orientation in y compared to the rest of the chip
+\item \code{CELL.READDIR}: Read direction, rows=1, cols=2
+\item \code{CELL.EXPOSURE}: Exposure time (sec)
+\item \code{CELL.DARKTIME}: Time since flush (sec)
+\item \code{CELL.TRIMSEC}: Trim section
+\item \code{CELL.BIASSEC}: Bias sections
+\item \code{CELL.XBIN}: Binning in x
+\item \code{CELL.YBIN}: Binning in y
+\item \code{CELL.TIMESYS}: Time system
+\item \code{CELL.TIME}: Time of exposure
+\item \code{CELL.X0}: Position of (0,0) on the chip
+\item \code{CELL.Y0}: Position of (0,0) on the chip
+\end{itemize}
+
+In addition, \code{FPA.NAME}, \code{CHIP.NAME} and \code{CELL.NAME}
+are included automatically, based on the \code{FILE} and
+\code{CONTENTS} metadatas.
+
+\subsection{Examples}
+
+\subsubsection{Megacam (short) raw}
+
+\begin{verbatim}
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The raw MegaCam data comes off the telescope with each of the chips stored in extensions of a MEF file.
+
+# How to identify this type
+RULE    METADATA
+        TELESCOP        STR     CFHT 3.6m
+        DETECTOR        STR     MegaCam
+        EXTEND          BOOL    T
+        NEXTEND         S32     72
+END
+
+# How to read this data
+FILE    METADATA
+        PHU             STR     FPA     # The FITS file represents an entire FPA
+        EXTENSIONS      STR     CELL    # The extensions represent cells
+        FPA.NAME        STR     EXPNUM  # A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS        METADATA
+        # Extension name, chip:cell:type
+        amp24           STR     ccd12:LeftAmp:left
+        amp25           STR     ccd12:RightAmp:right
+        amp26           STR     ccd13:LeftAmp:left
+        amp27           STR     ccd13:RightAmp:right
+        amp28           STR     ccd14:LeftAmp:left
+        amp29           STR     ccd14:RightAmp:right
+        amp42           STR     ccd21:LeftAmp:left
+        amp43           STR     ccd21:RightAmp:right
+        amp44           STR     ccd22:LeftAmp:left
+        amp45           STR     ccd22:RightAmp:right
+        amp46           STR     ccd23:LeftAmp:left
+        amp47           STR     ccd23:RightAmp:right
+END
+
+# Specify the cell data
+CELLS   METADATA
+        left    METADATA        # Left amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+                CELL.BIASSEC            STR     BIASSEC
+                CELL.TRIMSEC            STR     DATASEC
+                CELL.XPARITY            S32     1 # We could have specified this as a DEFAULT, but this works
+                CELL.X0                 S32     1
+        END
+        right   METADATA        # Right amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+                CELL.BIASSEC            STR     BIASSEC
+                CELL.TRIMSEC            STR     DATASEC
+                CELL.XPARITY            S32     -1 # This cell is read out in the opposite direction
+                CELL.X0                 S32     2048
+        END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION     METADATA
+        FPA.NAME                STR     EXPNUM
+        FPA.AIRMASS             STR     AIRMASS
+        FPA.FILTER              STR     FILTER
+        FPA.POSANGLE            STR     ROTANGLE
+        FPA.RA                  STR     RA
+        FPA.DEC                 STR     DEC
+        FPA.RADECSYS            STR     RADECSYS
+        FPA.OBSTYPE             STR     OBSTYPE
+        FPA.OBJECT              STR     CMMTOBS
+        FPA.TIME                STR     MJD-OBS
+        FPA.TIMESYS             STR     TIMESYS
+        FPA.ALT                 STR     TELALT
+        FPA.AZ                  STR     TELAZ
+        CHIP.TEMP               STR     DETTEM
+        CELL.EXPOSURE           STR     EXPTIME
+        CELL.DARKTIME           STR     DARKTIME
+        CELL.GAIN               STR     GAIN
+        CELL.READNOISE          STR     RDNOISE
+        CELL.SATURATION         STR     SATURATE
+        CELL.TIME               STR     MJD-OBS
+        CELL.TIMESYS            STR     TIMESYS
+        CELL.XBIN               STR     CCDBIN1
+        CELL.YBIN               STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+        CELL.READDIR            S32     1               # Cell is read in x direction
+        CELL.BAD                S32     0
+        CELL.YPARITY            S32     1
+        CELL.Y0                 S32     1
+
+        CHIP.X0.DEPEND          STR     CHIP.NAME
+        CHIP.X0         METADATA
+                ccd12   S32     6144
+                ccd13   S32     8192
+                ccd14   S32     10240
+                ccd21   S32     6144
+                ccd22   S32     8192
+                ccd23   S32     10240
+        END
+        CHIP.Y0.DEPEND          STR     CHIP.NAME
+        CHIP.Y0         METADATA
+                ccd12   S32     13835
+                ccd13   S32     13835
+                ccd14   S32     13835
+                ccd21   S32     4612
+                ccd22   S32     4612
+                ccd23   S32     4612
+        END
+        CHIP.XPARITY.DEPEND     STR     CHIP.NAME
+        CHIP.XPARITY    METADATA
+                ccd12   S32     1
+                ccd13   S32     1
+                ccd14   S32     1
+                ccd21   S32     1
+                ccd22   S32     1
+                ccd23   S32     1
+        END
+        CHIP.YPARITY.DEPEND     STR     CHIP.NAME
+        CHIP.YPARITY    METADATA
+                ccd12   S32     -1
+                ccd13   S32     -1
+                ccd14   S32     -1
+                ccd21   S32     1
+                ccd22   S32     1
+                ccd23   S32     1
+        END
+END
+
+# How to translate PS concepts into database lookups
+DATABASE        METADATA
+        TYPE            dbLookup        TABLE           COLUMN          chipId          cellId
+#       CHIP.TEMP       METADATA
+#               TABLE   STR     Cryostat
+#               COLUMN  STR     temp
+#               chipId  STR     {CHIP.NAME}
+#               time    STR     {CELL.TIME}
+#       END
+#       CELL.GAIN       dbLookup        Camera          gain            CHIP.NAME       CELL.NAME
+#       CELL.READNOISE  dbLookup        Camera          readNoise       CHIP.NAME       CELL.NAME
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS         METADATA
+        FPA.RA          STR     HOURS
+        FPA.DEC         STR     DEGREES
+        FPA.TIME        STR     MJD
+        CELL.TIME       STR     MJD
+        CELL.X0         STR     FORTRAN
+        CELL.Y0         STR     FORTRAN
+END
+\end{verbatim}
+
+\subsubsection{Megacam (short) split}
+
+\begin{verbatim}
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The spliced MecaCam data is stored in single extensions for each chip
+
+# How to recognise this type
+RULE    METADATA
+        TELESCOP        STR     CFHT 3.6m
+        DETECTOR        STR     MegaCam
+        # No particular distinguishing features apart from these, so we list this format last
+        # in the camera configuration file.
+END
+
+FILE    METADATA
+        # How to read this data
+        PHU             STR     CHIP    # The FITS file represents an entire FPA
+        EXTENSIONS      STR     NONE    # The extensions represent chips
+        FPA.NAME        STR     EXPNUM  # A PHU keyword for unique identifier
+        CHIP.NAME       STR     EXTNAME # An extension keyword for unique identifie
+        CONTENT         STR     {CHIP.NAME} # Key to the CONTENTS menu
+END
+
+# What's in the FITS file?
+CONTENTS        METADATA
+        # Extension name, chip:cell:type
+        ccd12           STR     ccd12:LeftAmp:left ccd12:RightAmp:right
+        ccd13           STR     ccd13:LeftAmp:left ccd13:RightAmp:right
+        ccd14           STR     ccd14:LeftAmp:left ccd14:RightAmp:right
+        ccd21           STR     ccd21:LeftAmp:left ccd21:RightAmp:right
+        ccd22           STR     ccd22:LeftAmp:left ccd22:RightAmp:right
+        ccd23           STR     ccd23:LeftAmp:left ccd23:RightAmp:right
+END
+
+# Specify the cells
+CELLS           METADATA
+        left            METADATA
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+                CELL.BIASSEC            STR     BSECA
+                CELL.TRIMSEC            STR     TSECA
+                CELL.X0                 S32     0
+                CELL.GAIN.SOURCE        STR     HEADER
+                CELL.GAIN               STR     GAINA
+        END
+
+        right           METADATA
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+                CELL.BIASSEC            STR     BSECB
+                CELL.TRIMSEC            STR     TSECB
+                CELL.X0                 S32     1024
+                CELL.GAIN.SOURCE        STR     HEADER
+                CELL.GAIN               STR     GAINB
+        END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION     METADATA
+        FPA.NAME        STR     EXPNUM
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTER      STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+        FPA.OBSTYPE     STR     OBSTYPE
+        FPA.OBJECT      STR     CMMTOBS
+        FPA.TIME        STR     MJD-OBS
+        FPA.TIMESYS     STR     TIMESYS
+        FPA.ALT         STR     TELALT
+        FPA.AZ          STR     TELAZ
+        CHIP.TEMP       STR     DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.READNOISE  STR     RDNOISE
+        CELL.SATURATION STR     SATURATE
+        CELL.TIME       STR     MJD-OBS
+        CELL.TIMESYS    STR     TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+        CELL.READDIR            S32     1               # Cell is read in x direction
+        CELL.BAD                S32     0
+        CELL.XPARITY            S32     1
+        CELL.YPARITY            S32     1
+        CELL.Y0                 S32     0
+#       PPMERGE.SCALE           F32     1.0
+#       PPMERGE.ZERO            F32     0.0
+        CHIP.X0.DEPEND          STR     CHIP.NAME
+        CHIP.X0         METADATA
+                ccd12   S32     0
+                ccd13   S32     2048
+                ccd14   S32     4096
+                ccd21   S32     0
+                ccd22   S32     2048
+                ccd23   S32     4096
+        END
+        CHIP.Y0.DEPEND          STR     CHIP.NAME
+        CHIP.Y0         METADATA
+                ccd12   S32     9223
+                ccd13   S32     9223
+                ccd14   S32     9223
+                ccd21   S32     0
+                ccd22   S32     0
+                ccd23   S32     0
+        END
+        CHIP.XPARITY.DEPEND     STR     CHIP.NAME
+        CHIP.XPARITY    METADATA
+                ccd12   S32     1
+                ccd13   S32     1
+                ccd14   S32     1
+                ccd21   S32     1
+                ccd22   S32     1
+                ccd23   S32     1
+        END
+        CHIP.YPARITY.DEPEND     STR     CHIP.NAME
+        CHIP.YPARITY    METADATA
+                ccd12   S32     -1
+                ccd13   S32     -1
+                ccd14   S32     -1
+                ccd21   S32     1
+                ccd22   S32     1
+                ccd23   S32     1
+        END
+END
+
+
+# How to translation PS concepts into database lookups
+DATABASE        METADATA
+# None
+END             
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS         METADATA
+        FPA.RA          STR     HOURS
+        FPA.DEC         STR     DEGREES
+        FPA.TIME        STR     MJD
+        CELL.TIME       STR     MJD
+END
+\end{verbatim}
+
+\subsubsection{Imaging Sky Probe}
+
+\begin{verbatim}
+# Pan-STARRS Imaging Sky Probe
+
+# How to identify this type
+RULE    METADATA
+        SIMPLE          BOOL    TRUE
+        NAXIS           S32     2
+        TELESCOP        STR     ISP-1 
+        INSTRUME        STR     ISP-Apogee
+        DETECTOR        STR     ISP-Apogee-01
+        ISPCAMER        STR     Apogee U42
+END
+
+# How to read this data
+FILE    METADATA
+        PHU             STR     FPA     # The FITS file represents an entire FPA
+        EXTENSIONS      STR     NONE    # There are no extensions
+        FPA.NAME        STR     SEQID   # A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS        STR     Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS   METADATA
+        amplifier       METADATA
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC            STR     TRIMSEC
+                CELL.BIASSEC            STR     BIASSEC
+        END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION     METADATA
+        FPA.OBSTYPE     STR     OBSTYPE
+        FPA.OBJECT      STR     OBSTYPE
+        FPA.FILTER      STR     FILTNAME
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+        FPA.ALT         STR     ALT
+        FPA.AZ          STR     AZ
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.TIME        STR     MJD-OBS
+        CHIP.TEMP       STR     CCDTEMP
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.TIME       STR     MJD-OBS
+        CELL.GAIN       STR     GAIN
+        CELL.READNOISE  STR     RDNOISE
+        CELL.XBIN       STR     XBIN
+        CELL.YBIN       STR     YBIN
+#       CELL.SATURATION STR     SATURATE        ### Currently set to 0 ???
+        CELL.BAD        STR     BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+        FPA.TIMESYS     STR     UTC
+        CELL.SATURATION F32     65535
+        CELL.READDIR    S32     1
+        CELL.TIMESYS    STR     UTC
+        CHIP.XPARITY    S32     1
+        CHIP.YPARITY    S32     1
+        CHIP.X0         S32     0
+        CHIP.Y0         S32     0
+        CELL.XPARITY    S32     1
+        CELL.YPARITY    S32     1
+        CELL.X0         S32     0
+        CELL.Y0         S32     0
+END
+
+FORMATS         METADATA
+        FPA.RA          STR     HOURS
+        FPA.DEC         STR     DEGREES
+        FPA.TIME        STR     MJD
+        CELL.TIME       STR     MJD
+END
+
+# PS Concepts to get from the database
+DATABASE        METADATA
+# None.
+END
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Recipes}
+
+\subsection{Locations}
+
+Recipes may be specified in a number of locations.  The recipe files
+are loaded in a sequence, with each new file supplementing the recipes
+already defined.  First, the site-wide list of recipes is loaded.
+Next, if a camera can be identified, the camera-specific recipes are
+loaded.  In both locations, the recipes are identified as named files
+under the \code{RECIPES} metadata.  Note that the \code{PATH(STR)} in
+the site configuration defines the search paths for these files.
+Finally, they may be specified on the command line with the
+\code{-recipe} option, giving a symbolic name and a filename or
+another symbolic name to link to.  In addition, individual recipe
+values may be specified on the command line with one of several
+command-line options.
+
+\subsubsection{Recipe combination}
+
+A single recipes are defined at multiple levels (site, camera, and
+command-line), so it's important to know how these are loaded.  The
+site configuration recipes serve as the default recipes.  Once the
+particular camera is known, the values contained within its recipes
+(provided either as a filename or as a symbolic link; see below for
+symbolic links) override those defined in the site configuration
+(unless the value has been declared as \code{MULTI}, in which case it
+supplements).  This is useful because recipes often depend on the
+camera from which the data being processed originated; for example,
+not all cameras require a dark to be subtracted.
+
+Finally, the command line can be used to provide further refinement.
+A recipe can be defined on the command line using \code{-recipe
+RECIPE_NAME filename.config} to specify a file containing the recipe,
+or \code{-recipe RECIPE_NAME ALTERNATE_RECIPE_NAME} to specify an
+symbolic link from which to inget values for the original recipe.
+
+Symbolic links offer the ability to override the default recipe values
+by specifying a name, rather than a filename.  A symbolic link can
+refer to a recipe of a different name that has already been defined,
+or it can refer to a \code{METADATA} within the recipe of that same
+name.
+
+A few examples are useful here.  Say the site configuration contains:
+\begin{verbatim}
+RECIPES    METADATA
+    RECIPE         STR    recipe_default.config
+    RECIPE_EXOTIC  STR    recipe_exotic.config
+END
+\end{verbatim}
+
+The camera configuration has:
+\begin{verbatim}
+RECIPES    METADATA
+    RECIPE         STR    recipe_camera.config
+END
+\end{verbatim}
+
+\code{recipe_default.config} has:
+\begin{verbatim}
+VALUE    STR    Default
+
+RECIPE_DULL    METADATA
+    VALUE    STR    Dull
+END
+\end{verbatim}
+
+
+\code{recipe_exotic.config} has:
+\begin{verbatim}
+VALUE    STR    Exotic
+\end{verbatim}
+
+And \code{recipe_camera.config} has:
+\begin{verbatim}
+VALUE    STR    Camera
+\end{verbatim}
+
+Then:
+\begin{itemize}
+\item If the recipe is examined without knowing the camera,
+  \code{VALUE} will be \code{Default}.
+\item If the recipe is examined once the camera is known, \code{VALUE}
+  will be \code{Camera}.
+\item If the command-line contains \code{-recipe RECIPE recipe_exotic.config},
+  \code{VALUE} will be \code{Exotic}.
+\item If the command-line contains \code{-recipe RECIPE RECIPE_EXOTIC},
+  \code{VALUE} will be \code{Exotic}.
+\item If the command-line contains \code{-recipe RECIPE RECIPE_DULL},
+  \code{VALUE} will be \code{Dull}.
+\end{itemize}
+
+The priority for recipe sources is:
+\begin{enumerate}
+\item Site configuration
+\item Camera configuration
+\item Command-line recipes
+\item Command-line options
+\end{enumerate}
+If multiple recipes have the same name, higher priority entries
+over-write the values specified in the lower-priority entries.  Values
+which are not defined in the higher-priority entries are inherited
+from the lower-priority entries.  This allows the user to override any
+recipe values using the command-line, and to specify default values in
+the site configuration, while also having camera-specific values in
+the camera configurations.  A good practice is for the higher-priority
+recipes files to only supply the entries which are different from the
+default values.  
+
+\begin{verbatim}
+loading sequence is:
+
+* in pmConfigRead->pmConfigReadRecipes(config, PM_RECIPE_SOURCE_SITE | PM_RECIPE_SOURCE_CAMERA):
+
+  config->site:RECIPES:NAME=FILE(STR) -> config->recipes:NAME(MD)
+
+  config->camera:RECIPES:NAME=FILE(STR) -> config->recipes:NAME(MD)
+  (if camera is specified)
+
+* in pmConfigRead->pmConfigLoadRecipeArguments (config):
+
+  config->argv:-recipe:NAME=FILE(STR) -> config->arguments:RECIPES:NAME(MD)
+
+  config->argv:-recipe:NAME=REF(STR) -> config->recipesSource:NAME=REF(STR)
+
+* in pmConfigRead->pmConfigLoadRecipeOptions (config, "-D"):
+
+  config->argv:-D:NAME:KEY=VALUE(STR) -> config->arguments:OPTIONS:NAME(MD)
+
+** file is loaded / camera is identified **
+
+* in pmConfigCameraFormatFromHeader->pmConfigReadRecipes(config, PM_RECIPE_SOURCE_CAMERA | PM_RECIPE_SOURCE_CL):
+
+  config->camera:RECIPES:NAME=FILE(STR) -> config->recipes:NAME(MD)
+
+** at this point, all recipes are loaded as MD in either config->recipes, config->arguments:RECIPES, or config->arguments:OPTIONS
+
+  config->arguments:RECIPES:NAME(MD) -> config->recipes:NAME(MD)
+
+** resolve the symbolic names:
+
+  config->recipesSource:NAME=REF(STR):
+  * if REF is in config->recipes, interpolate: config->recipes:REF(MD) -> config->recipes:NAME(MD)
+  * else if REF is in config->recipes:NAME, interpolate: config->recipes:NAME:REF(MD) -> config->recipes:NAME(MD)
+
+** apply the OPTIONS:
+
+  config->arguments:OPTIONS:NAME(MD) -> config->recipes:NAME(MD)
+
+--
+
+Examples:
+program -recipe PPIMAGE ppImage.config -recipe PPIMAGE PPIMAGE_BIAS
+\end{verbatim}
+
+\subsection{Contents}
+
+The contents of the recipe files depends on the particular recipe.
+
+\subsubsection{PPIMAGE}
+
+The \code{PPIMAGE} recipe contains options for \code{ppImage}:
+\begin{itemize}
+\item \code{MASK(BOOL)} indicates if bad pixels are to be masked.
+\item \code{MASK.VALUE(U8)} specifies a bitmask (matching the bad
+  pixel mask) for pixels to mask in the input image.
+\item \code{NONLIN(BOOL)} indicates if the non-linearity correction is
+  to be performed.
+\item \code{OVERSCAN(BOOL)} indicates if the overscan correction is to be performed.
+\item \code{BIAS(BOOL)} indicates if the bias correction is to be performed.
+\item \code{DARK(BOOL)} indicates if the dark correction is to be performed.
+\item \code{SHUTTER(BOOL)} indicates if the shutter correction is to be performed.
+\item \code{FLAT(BOOL)} indicates if the flat-field correction is to be performed.
+\item \code{FRINGE(BOOL)} indicates if the fringe correction is to be performed.
+\item \code{PHOTOM(BOOL)} indicates if the photometry is to be performed.
+\item \code{ASTROM.CHIP(BOOL)} indicates if the astrometry is to be performed on a chip level.
+\item \code{ASTROM.MOSAIC(BOOL)} indicates if the astrometry is to be performed on a mosaic (FPA) level.
+\item \code{BASE.FITS(BOOL)} indicates if the base detrended image is to be saved.
+\item \code{CHIP.FITS(BOOL)} indicates if the chip mosaicked image is to be saved.
+\item \code{FPA1.FITS(BOOL)} indicates if the FPA mosaicked image with first level binning is to be saved.
+\item \code{FPA2.FITS(BOOL)} indicates if the FPA mosaicked image with second level binning is to be saved.
+\item \code{BIN1.FITS(BOOL)} indicates if the chip mosaicked image with first level binning is to be saved.
+\item \code{BIN2.FITS(BOOL)} indicates if the chip mosaicked image with second level binning is to be saved.
+\item \code{BIN1.JPEG(BOOL)} indicates if the JPEG image with first level binning is to be saved.
+\item \code{BIN2.JPEG(BOOL)} indicates if the JPEG image with second level binning is to be saved.
+\item \code{NONLIN.DATA} may be:
+  \begin{itemize}
+  \item A vector of type \code{F32}, in which case it provides the
+    (ordinary) polynomial coefficients for the non-linear correction.
+  \item Of type \code{STR}, in which case it provides a filename with
+    the lookup table (consisting of two columns of values, the first
+    the input flux and the second the corresponding corrected flux).
+  \item Of type \code{METADATA}, in which case it is a menu, with menu
+    items with types and values according to one of the other two
+    options.  The menu key is provided by \code{NONLIN.SOURCE(STR}),
+    which gives a concept name to look up (\code{CHIP.NAME} would be a
+    good choice).
+  \end{itemize}
+  \tbd{Non-linearity correction is implemented but not tested.}
+\item \code{OVERSCAN.SINGLE(BOOL)} indicates if the entire overscan is
+  to be reduced to a single value.
+\item \code{OVERSCAN.FIT(STR)} indicates the type of fit that is to be
+  performed to the overscan (if \code{OVERSCAN.SINGLE} is
+  \code{FALSE}): \code{NONE}, \code{POLYNOMIAL} or \code{SPLINE}.
+\item \code{OVERSCAN.ORDER(S32)} gives the order of the polynomial fit
+  (or number of spline pieces).
+\item \code{OVERSCAN.STAT(STR)} gives the statistic to apply to the
+  overscan: \code{MEAN} or \code{MEDIAN}. \tbd{Would like to change
+  this to allow the full range of statistics.}
+\item \code{FRINGE.ITER(S32)} specifies the number of rejection iterations for fringe solution.
+\item \code{FRINGE.REJ(F32)} specifies the rejection threshold (in standard deviations) for fringe solution.
+\item \code{FRINGE.KEEP(F32)} specifies the minimum fraction of points to keep in the fringe solution.
+\item \code{BIN1.XBIN(S32)} gives the level 1 binning in x
+\item \code{BIN2.YBIN(S32)} gives the level 1 binning in y
+\item \code{BIN2.XBIN(S32)} gives the level 2 binning in x
+\item \code{BIN2.YBIN(S32)} gives the level 2 binning in y:
+\item \code{PHOTCODE.RULE(STR)} gives a rule for producing a
+  photometry code, with values in curly brackets interpolated in the
+  same manner as the file rules in the camera configuration.
+\item \code{PPIMAGE.JPEG1(METADATA)} and \code{PPIMAGE.JPEG2(METADATA)} give parameters for JPEG scaling, and contains:
+  \begin{itemize}
+  \item \code{COLORMAP(STR)} specifies the colormap to use:
+    \code{greyscale}, \code{-greyscale} (inverse greyscale),
+    \code{rainbow}, \code{heat}.
+  \item \code{SCALE.MODE(STR)} specifies how the scaling is performed: \code{RANGE} or \code{FRACTION}.
+  \item \code{SCALE.MIN(F32)} specifies the minimum scale.
+  \item \code{SCALE.MAX(F32)} specifies the maximum scale.
+  \end{itemize}
+\item \code{DETREND.CONSTRAINTS(METADATA)} contains constraints for
+  the detrend lookup as a function of type.  Each type is a
+  \code{METADATA} with the constraints to be used.  Supported
+  constraint names are: \code{FILTER}, \code{EXPTIME} and
+  \code{AIRMASS}.  Each is of type \code{STR} and the value is a
+  string specifying a concept that will provide the constraint value.
+  \label{sec:detrend-database}
+\end{itemize}
+
+\subsubsubsection{Example}
+
+\begin{verbatim}
+### ppImage recipe configuration file
+
+# List of tasks to perform
+MASK            BOOL    FALSE           # Mask bad pixels
+MASK.VALUE      U8      0xff            # Only mask pixels matching this bitmask
+NONLIN          BOOL    FALSE           # Non-linearity correction
+OVERSCAN        BOOL    TRUE            # Overscan subtraction
+BIAS            BOOL    TRUE            # Bias subtraction
+DARK            BOOL    TRUE            # Dark subtraction
+FLAT            BOOL    TRUE            # Flat-field normalisation
+FRINGE          BOOL    FALSE           # Fringe subtraction
+PHOTOM          BOOL    FALSE           # Source identification and photometry
+ASTROM.CHIP     BOOL    FALSE           # Astrometry on chip
+ASTROM.MOSAIC   BOOL    FALSE           # Astrometry on mosaic
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+FPA1.FITS       BOOL    TRUE            # Save 1st binned fpa image? 
+FPA2.FITS       BOOL    TRUE            # Save 2nd binned fpa image? 
+BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+BIN1.JPEG       BOOL    TRUE            # Save 1st binned jpeg?
+BIN2.JPEG       BOOL    FALSE           # Save 2nd binned jpeg?
+
+# Non-linearity correction
+NONLIN.SOURCE           STR     CHIP.NAME       # How to determine the source
+#@NONLIN.DATA           F32     0.0 1.001 0.001 # A polynomial
+#NONLIN.DATA            STR     nonlin.dat      # Filename for lookup table
+NONLIN.DATA             METADATA                # Source of non-linearity data
+        ccd00           STR     nonlin00.dat    # A lookup table 
+        @ccd01          F32     0.0 1.001 0.001 # A polynomial
+        @ccd02          F32     1.2345          # A polynomial
+END
+
+# Overscan subtraction
+OVERSCAN.SINGLE         BOOL    FALSE           # Reduce overscan to a single value?
+#OVERSCAN.FIT           STR     SPLINE          # NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT            STR     POLYNOMIAL      # NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER          S32     5               # Order of polynomial fit
+OVERSCAN.STAT           STR     MEAN            # MEAN | MEDIAN
+
+# Fringe subtraction options
+FRINGE.ITER	S32	10		# Number of rejection iterations for fringe solution
+FRINGE.REJ	F32	2.0		# Rejection threshold for fringe solution
+FRINGE.KEEP	F32	0.5		# Minimum fraction to keep in fringe solution
+
+# binned output image options
+BIN1.XBIN               S32     8
+BIN1.YBIN               S32     8
+BIN2.XBIN               S32     64
+BIN2.YBIN               S32     64
+
+PPIMAGE.JPEG1  METADATA
+  COLORMAP      STR     -greyscale
+  SCALE.MODE    STR     RANGE
+  SCALE.MIN     F32     -5.0
+  SCALE.MAX     F32     20.0
+END
+
+PPIMAGE.JPEG2  METADATA
+  COLORMAP      STR     greyscale
+  SCALE.MODE    STR     FRACTION
+  SCALE.MIN     STR     0.50
+  SCALE.MAX     STR     2.00
+END
+
+PHOTCODE.RULE           STR     {CAMERA}.{FILTER.ID}.{CHIP.N}
+  
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  # NONE
+  END
+  DARK METADATA
+    EXPTIME STR FPA.EXPTIME
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTERID
+    AIRMASS  STR FPA.AIRMASS
+  END
+  SHUTTER METADATA
+  # NONE
+  END	
+END
+\end{verbatim}
+
+\subsubsection{PPMERGE}
+
+The \code{PPMERGE} recipe contains options for \code{ppMerge}:
+\begin{itemize}
+\item \code{ROWS(S32)} gives the number of rows to be read at once (a
+  number larger than the physical size will read all rows).
+\item \code{ELECTRONS(F32)} gives the minimum number of electrons for
+  useful signal. \tbd{Don't think this is implemented yet.}
+\item \code{SAMPLE(S32)} specifies a sampling frequency for
+  determining the background level.
+\item \code{REJ(F32)} specifies a rejection threshold, in standard
+  deviations.
+\item \code{ITER(S32)} specifies the number of rejection iterations.
+\item \code{FRACHIGH(F32)} gives the fraction of high pixels to reject immediately.
+\item \code{FRACLOW(F32)} gives the fraction of low pixels to reject immediately.
+\item \code{NKEEP(S32)} gives the minimum number of pixels in the stack to keep.
+\item \code{MASKVAL(S32)} gives the mask value for input data.
+\item \code{COMBINE(STR)} gives the statistic to use for combination.
+\item \code{MEAN(STR)} gives the statistic to use to measure the mean.
+\item \code{STDEV(STR)} gives the statistic to use to measure the standard deviation.
+\item \code{WEIGHTS(BOOL)} specifies whether image (Poisson) weights should be used in the combination.
+\item For combining a fringe:
+  \begin{itemize}
+  \item \code{FRINGE.NUM(S32)} specifies the number of fringe regions for fringe combination.
+  \item \code{FRINGE.SIZE(S32)} specifies the half-size of the fringe regions.
+  \item \code{FRINGE.XSMOOTH(S32)} specifies the number of smoothing regions in x.
+  \item \code{FRINGE.YSMOOTH(S32)} specifies the number of smoothing regions in y.
+  \end{itemize}
+\item For generating a shutter correction:
+  \begin{itemize}
+  \item \code{SHUTTER.SIZE(S32)} specifies the size for shutter measurement regions.
+  \item \code{SHUTTER.ITER(S32)} specifies the number of iterations for performing the shutter measurement.
+  \item \code{SHUTTER.REJECT(F32)} specifies the rejection limit for shutter measurement.
+  \end{itemize}
+\item For generating a bad pixel mask:
+  \begin{itemize}
+  \item \code{MASK.SUSPECT(F32)} specifies the threshold for suspect pixels (in standard deviations).
+  \item \code{MASK.BAD(F32)} specifies the threshold for bad pixels
+    (in standard deviations); if negative, assume it's something like
+    a Poisson distribution.
+  \end{itemize}
+\end{itemize}
+
+Mean statistics specified by a string (for \code{COMBINE},
+\code{MEAN}) may be one of \code{MEAN}, \code{MEDIAN}, \code{ROBUST},
+\code{FITTED} or \code{CLIPPED}.  The standard deviation statistic
+(\code{STDEV}) may be one of \code{STDEV}, \code{ROBUST_STDEV},
+\code{FITTED_STDEV}, or \code{CLIPPED_STDEV}.
+
+
+\subsubsubsection{Example}
+
+\begin{verbatim}
+# Recipe configuration for ppMerge
+ 
+ROWS            S32     512		# Number of rows to read at once
+ELECTRONS       F32     100.0           # Minimum number of electrons for useful signal
+SAMPLE          S32     100             # Sampling factor for measuring the background
+REJ		F32	3.0		# Rejection threshold (sigma)
+ITER		S32	1		# 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
+NKEEP		S32	5		# Minimum number of pixels in stack to keep
+FRINGE.NUM	S32	10000		# Number of fringe regions
+FRINGE.SIZE	S32	5		# Half-size of fringe regions
+FRINGE.XSMOOTH	S32	5		# Number of smoothing regions in x
+FRINGE.YSMOOTH	S32	11		# Number of smoothing regions in y
+SHUTTER.SIZE	S32	128		# Size for shutter measurement regions
+SHUTTER.ITER	S32	1		# Number of iterations for shutter measurement
+SHUTTER.REJECT	F32	2		# Rejection limit for shutter measurement
+MASK.SUSPECT	F32	5.0		# Threshold for suspect pixels (sigma)
+MASK.BAD	F32	-4.0		# Threshold for bad pixels (sigma)
+MASKVAL		S32	0xff		# Mask value for input data
+COMBINE		STR	CLIPPED		# Statistic to use for combination
+MEAN		STR	ROBUST_MEDIAN	# Statistic to use to measure the mean
+STDEV		STR	ROBUST_STDEV	# Statistic to use to measure the stdev
+WEIGHTS		BOOL	FALSE		# Use image weights?
+\end{verbatim}
+
+
+\subsubsection{PPSTATS}
+
+The \code{PPSTATS} recipe contains options for \code{ppStats} or its
+library used within another program:
+\begin{itemize}
+\item \code{SAMPLE(F32)} specifies the fraction of the cell to sample
+  (for statistical measurements).
+\item \code{MASKVAL(U8)} specifies a mask value to use for the
+  statistics.
+\item \code{HEADER(STR)} specifies headers (may be listed, separated
+  by whitespace) to print.  Multiple \code{HEADER} entries may exist,
+  if it is declared \code{MULTI}.
+\item \code{CONCEPT(STR)} specifies concepts (may be listed, separated
+  by whitespace) to print.  Multiple \code{CONCEPT} entries may exist,
+  if it is declared \code{MULTI}.
+\item \code{STAT(STR)} specifies statistics (may be listed, separated
+  by whitespace) to print.  Multiple \code{STAT} entries may exist, if
+  it is declared \code{MULTI}.  Acceptable statistics names are those
+  parsed by \code{psStatsOptionFromString}.
+\end{itemize}
+
+\subsubsubsection{Example}
+
+\begin{verbatim}
+### ppStats recipe for Phase 0 with MegaCam
+
+# Options governing statistics
+SAMPLE          F32     0.1     # Fraction of cell to sample
+MASKVAL         U8      0xff    # Mask value to use for statistics
+
+# Define the outputs as MULTI
+HEADER          MULTI
+CONCEPT         MULTI
+STAT            MULTI
+
+# Values to return
+HEADER          STR     OBSERVER        # Observer name
+CONCEPT         STR     FPA.OBJECT      # Object name
+CONCEPT         STR     FPA.OBSTYPE     # Observation type
+CONCEPT         STR     FPA.FILTER      # Filter
+CONCEPT         STR     FPA.RA FPA.DEC  # Telescope pointing
+CONCEPT         STR     FPA.AIRMASS     # Airmass
+CONCEPT         STR     FPA.ALT FPA.AZ  # Telescopy alt/az
+CONCEPT         STR     FPA.POSANGLE    # Rotator angle
+CONCEPT         STR     CHIP.TEMP       # Detector temperature
+CONCEPT         STR     CELL.EXPOSURE   # Exposure time
+CONCEPT         STR     CELL.TIME       # Time of exposure
+STAT            STR     ROBUST_MEDIAN   # Background estimator
+STAT            STR     ROBUST_STDEV    # Background standard deviation estimator
+\end{verbatim}
+
+\subsubsection{PSPHOT}
+
+\tbd{EAM to fill this in.}
+
+\subsubsection{PSASTRO}
+
+\tbd{EAM to fill this in.}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Revision Change Log}
+%\input{ChangeLog.tex}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%\bibliographystyle{plain}
+%\bibliography{panstarrs}
+
+\end{document}
+
Index: /tags/sj_tags/sj_root_20080929/doc/design/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+*.log *.dvi *.aux *.toc *.log *.out *.lof *.tbr *.tbd *.ent *.pdf
+ippSRSout.tex ippSRStrace.tex
Index: /tags/sj_tags/sj_root_20080929/doc/design/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/Makefile	(revision 22322)
@@ -0,0 +1,44 @@
+# $Id: Makefile,v 1.18 2006-08-04 12:55:02 eugene Exp $
+
+PDFLATEX = env TEXINPUTS=../../latex/inputs:$(TEXINPUTS):.: pdflatex
+PSLATEX  = env TEXINPUTS=../../latex/inputs:$(TEXINPUTS):.: latex
+
+help:
+	@echo "USAGE: make (target)"
+	@echo "  targets: srs ssdd scd all cdrresp pars comm"
+
+scd: ippSCD.pdf
+srs: ippSRS.pdf
+ssdd: ippSSDD.pdf
+cdrresp: ippCDRresponse.pdf
+pars: ippParameters.pdf
+comm: ippCommission.pdf
+
+scd-draft: ippSCDdraft.pdf
+
+all : srs ssdd scd
+
+ippSRS.pdf : ippSRS.tex
+	./trace.pl ippSRS.tex ippSRSout.tex ippSRStrace.tex
+	make ippSRSout.pdf
+	mv ippSRSout.pdf ippSRS.pdf
+	rm -f ippSRSout.tex ippSRStrace.tex
+
+%.pdf: %.tex
+	$(PSLATEX) $*.tex || exit
+	$(PSLATEX) $*.tex || exit
+	dvips -z -t letter -o $*.ps $*.dvi || exit
+	ps2pdf $*.ps $*.pdf || exit
+	thumbpdf --modes=dvips $*.pdf || exit
+	$(PSLATEX) $*.tex  || exit
+	dvips -z -t letter -o $*.ps $*.dvi || exit
+	ps2pdf $*.ps $*.pdf || exit
+	@rm -f $*.ps $*.dvi $*.aux $*.log $*.tbr $*.tbd $*.lof $*.toc $*.tpm body.tmp head.tmp
+
+clean :
+	$(RM) *.log *.dvi *.aux *.toc *.tbd *.tbr *.lof *.out *.tpm *~ core body.tmp head.tmp
+
+dist : clean
+	$(RM) *.pdf
+
+empty : clean
Index: /tags/sj_tags/sj_root_20080929/doc/design/glossary.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/glossary.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/glossary.tex	(revision 22322)
@@ -0,0 +1,113 @@
+{\Large \bf Acronyms}
+
+{\footnotesize
+\begin{tabular}{|p{50pt}|p{400pt}|} \hline 
+CAN  & Controller Area Network \\ \hline
+CCD  & Charge Coupled Device \\ \hline
+CFHT & Canada-France-Hawaii Telescope \\ \hline
+DC   & Data Collection - Database or other data storage container. \\ \hline
+DML  & Device Meta-Language \\ \hline
+DMT  & Dark Matter Telescope \\ \hline
+FOM  & Figure of Merit \\ \hline
+FOV  & Field of View \\ \hline
+FWHM & Full-Width at Half-Maximum \\ \hline
+GPC  & Giga-Pixel Camera \\ \hline
+GS   & Guide Star \\ \hline
+GUI  & Graphical User Interface  \\ \hline
+IAU  & International Astronomical Union \\ \hline
+IOD  & Initial Orbit Determination \\ \hline
+IPP  & Image Processing Pipeline \\ \hline
+KBO  & Kuiper Belt Object \\ \hline
+LAN  & Local Area Network \\ \hline
+LSN  & Local Solar Neighborhood \\ \hline
+LSS  & Large-Scale Structure \\ \hline
+LSST & Large Synoptic Survey Telescope \\ \hline
+MBA  & Main Belt Asteroid \\ \hline
+MDS  & Medium-Deep Survey  \\ \hline
+MOID & Minimum Orbital Intersection Distance - The minimum distance between two orbits. \\ \hline
+MOPS & Moving Object Processing System \\ \hline
+MPC  & Minor Planet Center (of the IAU) \\ \hline
+NEO  & Near Earth Object - An asteroid or comet with perihelion < 1.3AU. \\ \hline
+OBS  & Observation Seqencer \\ \hline
+ODA  & OTIS Data Archive \\ \hline
+OOF  & OTIS Observe File \\ \hline
+OOT  & OTIS Observation Tool \\ \hline
+OTA  & Orthogonal Transfer Array \\ \hline
+OTF  & Optical Transfer Function \\ \hline
+OTIS & Observatory and Telescope System \\ \hline
+OWS  & OTIS Weather Server \\ \hline
+PHO  & Potentially Hazardous Object \\ \hline
+PSDC & Pan-STARRS Document Control \\ \hline
+PSF  & Point Spread Function \\ \hline
+PSPS & Published Science Products System \\ \hline
+PTS  & Pan-STARRS Telescope Scheduler \\ \hline
+Pan-STARRS & Panoramic Survey Telescope and Rapid Response System  \\ \hline
+RFI  & Radio Frequency Interference \\ \hline
+SCD  & System Concept Definition \\ \hline
+SDSS & Sloan Digital Sky Survey  \\ \hline
+SGS  & Science Goals Statement \\ \hline
+SNe  & Supernovae \\ \hline
+SRS  & Software Requirements Specification \\ \hline
+SSS  & Solar System Survey \\ \hline
+TAC  & Time Allocation Committee \\ \hline
+TBD  & To Be Determined  \\ \hline
+TBR  & To Be Reviewed \\ \hline
+TCS  &  Telescope Control System \\ \hline
+TLA  & Three Letter Acronym \\ \hline
+\end{tabular}}
+
+\newpage
+{\Large \bf Acronyms (con't)}
+
+{\footnotesize
+\begin{tabular}{|p{50pt}|p{400pt}|} \hline 
+TLR  & Top Level Requirements \\ \hline
+TNO  & Trans-Neptunian Object \\ \hline
+TTI  & Transient Time Interval \\ \hline
+UDS  & Ultra-Deep Survey  \\ \hline
+UDS  & Ultra-Deep Survey  \\ \hline
+UET  & Unit Exposure Time \\ \hline
+WFS  & Wavefront Sensor   \\ \hline
+WFSS & Wavefront Sensor Star  \\ \hline
+WL   & Weak Lensing \\ \hline
+- & - \\ \hline
+RA   & right ascension \\ \hline
+DEC  & Declination \\ \hline
+GLAT & Galactic Latitude \\ \hline
+GLON & Galactic Longitude \\ \hline
+ELAT & Ecliptic Latitude \\ \hline
+ELON & Ecliptic Longitude \\ \hline
+\end{tabular}}
+
+{\Large \bf Glossary}
+
+{\footnotesize
+\begin{tabular}{|p{50pt}|p{400pt}|} \hline 
+Subaru  & National Astronomical Observatory of Japan's 8.3m telescope \\ \hline
+cadence & \\ \hline
+Phase 1 & IPP image processing preparation stage \\ \hline
+Phase 2 & IPP image reduction stage \\ \hline
+Phase 3 & IPP exposure analysis stage \\ \hline
+Phase 4 & IPP image combination stage \\ \hline
+Detection   & Identification of a source (real or not) in an image \\ \hline
+Observation & In MOPS, a detection that corresponds to a real Solar System object \\ \hline
+Designation & The identifying label assigned to newly identified Solar System objects \\ \hline
+Orbit Identification & The identification of two separately determined orbits as representing the same object. \\ \hline
+Attribution & The identification of a detection with a known orbit. \\ \hline
+Linkage    & The identification of sets of detections that allow an orbit determination for a Solar System object \\ \hline
+Autonomous & operates at night without human intervention 
+             for a minimum of three nights out of seven.  
+             Daytime summit calibration and maintainance carried out
+             four consecutive days a week (Monday-Thursday).   \\ \hline
+Robotic & operates at night without human intervention 
+         for a minimum of three nights out of seven.  
+         Only four days summit maintainence per week necessary,
+         human intervention in calibration not required.  \\ \hline
+Remote & operates without human intervention at the summit at night. \\ \hline
+Transient Time Interval & Time interval between two successive images of the same footprint in order to distinguish between stationary and non-stationary transient detections. \\ \hline
+Trans-Neptunian Object & An asteroid or comet that spends most of its time outside Neptune's orbit.  Include classical  \\ \hline
+Sweet Spots &  \\ \hline
+Potentially Hazardous Object & An asteroid or comet with MOID<0.05AU with Earth's orbit  \\ \hline
+Observing Efficiency & ratio of shutter open time to total time in a night excluding weather loss \\ \hline
+\end{tabular}
+}
Index: /tags/sj_tags/sj_root_20080929/doc/design/ippCDRresponse.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/ippCDRresponse.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/ippCDRresponse.tex	(revision 22322)
@@ -0,0 +1,760 @@
+\documentclass[panstarrs]{panstarrs}
+
+% basic document variables
+\title{Response to the IPP CDR Committee Report}
+\subtitle{}
+\shorttitle{IPP CDD Report Response}
+\author{Eugene A. Magnier}
+\audience{PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-440-004}
+
+% allow paragraphs to be listed in TOC for now 
+\setcounter{tocdepth}{3} 
+
+\begin{document}
+\maketitle
+
+% -- Revision History --
+%\RevisionsStart
+% version     Date         Description
+%00     & 2006.08.13 & Initial Release \\ \hline
+%\RevisionsEnd
+
+\tableofcontents
+\pagebreak
+\pagenumbering{arabic}
+
+\section{Document Overview}
+
+The IPP team appreciates the care, detail, and effort which went into
+the IPP CDR Committee Report.  The guidance and suggestions provided
+by this review are extremely valuable and will help us to refine the
+IPP design and improve the overall reliability and usability.
+
+This document is meant to address concerns raised by the CDR report
+and offer a fuller explanation of certain issues.  Some of the
+questions raised are addressed in separate documents (IPP-related
+ICDs, Updates to the IPP SSDD, the IPP Commissioning Plan, and the IPP
+Analysis Parameters Guide).  In particular, the CDR Review Committee
+listed the following Action Items and requested a report on items 3-10
+from the IPP team.
+
+\begin{enumerate}
+\item Work with other Pan-STARRS project members to complete a system
+level requirements specification.  Not having such a document at this
+late stage of PS1 development is a significant risk, and it is
+incumbent upon all relevant parties to address this as soon as
+possible.  (Note: Although recommending system level actions is
+strictly outside the scope of the charge to this committee, it is
+obvious that additional system level and operational requirements need
+to be specified/clarified in the immediate future.)
+
+\item Currently, no requirements on completeness and reliability have
+been specified for the PS1 Design Reference Mission. The project
+should develop internal goals for at least completeness to help IPP
+development set limits on how hard to work on difficult objects.
+
+\item Identify more explicitly the operational requirements of the IPP
+(what is the IPP required to do as part of the complete PS1
+system?). In particular, compare the functionality of certain IPP
+components relative to the PSPS.  Then ensure that any functional
+redundancy is intentionally present to increase system robustness, and
+does not represent a needless duplication of effort.
+
+\item Major version deliveries of the IPP should be defined in the
+development schedule.  Each major version should be associated with
+specific tasks and added functionality.  It is a good thing to have
+major milestones not just to track progress, but also to help focus
+the development work by the team.  Making these major deliveries can
+also give a sense of accomplishment to the team, helping with morale.
+
+\item All the external and internal interfaces to/within the IPP
+should be specified in detail.  For the external interfaces, this
+includes documenting the requirements in Interface Requirements
+Specifications (IRSs) or as part of the PS1 System/Subsystem
+Specification (SSS). The interface implementation should be presented
+in Interface Control Documents (ICDs).  For future reference and
+project reviews maintain a matrix showing the status of each, and
+describe the ICD maintenance and update process.
+
+\item Present the plan for finalizing the analysis algorithms.  The
+plan should include a complete list of analysis algorithms and
+functions, the status/completeness of each, what data is required to
+complete those not yet fully implemented or designed, and (if known)
+what choices are available to be traded.
+
+\item Establish more clearly the data validation process within the
+PS1 operations plan and budget.
+
+\item Provide the IPP commissioning plan.
+
+\item Provide additional information as to how the proposed hardware
+configuration allows IPP to meet its requirements.
+
+\item Explain more fully design philosophy and benefits of maintaining
+CFHT compatibility.
+\end{enumerate}
+
+\section{Action Item 3: Operational Requirements of the IPP}
+
+This question appears to be principally concerned with perceived
+overlap between the IPP DVO system and the PSPS Object Data Manager
+(ODM).  While there is superficial similarity between the systems, it
+is our belief that the development of two complementary systems is not
+only worthwhile, but extremely important for a variety of reasons.
+
+The IPP operational requirements include the analysis of images to
+produce the P2, P4$\Delta$, and P4$\Sigma$ detections, the Static Sky
+images, the Astrometric and Photometric Reference catalog, and the
+astrometric and photometric calibration of the data products (images
+and detections).  These requirements are highly coupled: in order to
+produce the Static Sky at the level required, the IPP must perform
+astrometric and photometric calibrations of the data stream at a high
+level of accuracy.  In order to perform these calibrations, the IPP
+must construct an improved astrometric and photometric reference
+catalog to supplant the existing references, at least within the PS1
+pass-bands.  
+
+The DVO system is designed to meet the twin requirements of performing
+the astrometric and photometric calibrations and to perform the
+construction of the astrometric and photometric reference catalog.
+The IPP DVO system and the PSPS thus have overlapping capabilities in
+terms of linking detections together into objects on the sky and in
+terms of tracking the relationships between the detections, images,
+and objects, along with the photometric and astrometric calibration
+information.  There are several motivations for defining a separate
+entity within the IPP specifically for this task:
+
+\begin{itemize}
+\item The PSPS system is foreseen as the definitive view on the object
+  database problem, while the IPP requires the ability to manipulate
+  and update the astrometric and photometric calibrations.  
+\item The PSPS system requires a high-level of sophistication in the
+  scientific queries which may be performed.  While critical, this
+  sophistication will result in a much longer development time line for
+  the PSPS.  
+\item By performing the analysis of the astrometric and photometric
+  calibration within the DVO system, and avoiding these requirements
+  within the PSPS, the PSPS design may be focused on the database
+  management issues of supplying the data to users.
+\item The IPP requirements for object databasing are fairly limited in
+  terms of the ability to perform detection/object matching.  This
+  trade-off allows the IPP DVO system to operate at a much higher
+  throughput than is possible for the PSPS, and perform more
+  re-processing operations.
+\end{itemize}
+
+It is useful to ask if the PSPS could have been ready within the
+necessary time frame, would the IPP use that instead of the DVO system
+for support of photometric and astrometric calibrations.  The answer
+would be 'yes' only if the PSPS requirements were modified to allow it
+to perform the same operations as required by the IPP DVO.  Would it
+be worthwhile to the project to merge the functionality of the two
+subsystems after they have both matured?  My opinion, and I believe,
+that of the project management, is that this choice would result in
+development of a system which would be substantially over-engineered
+for its role in either of the two subsystems, with concomitant
+increase in the cost of development and support.  By dividing the
+tasks served by these two systems in a logical way, the project as a
+whole is better served.  
+
+\section{Action Item 4: Major version deliveries of the IPP}
+
+\subsection{IPP Release 1 (Aug 30, 2006) : Phase 1-3, Detrend Creation, Infrastructure, ISP Support}
+
+\begin{itemize}
+\item {\bf Release Contents} Analysis programs for Phases 1-3 and the
+  Detrend Creation analysis, PanTasks (the controller/scheduler),
+  PanTasks scripts for Phase 1-3 and Detrend Creation, the Metadata
+  Database tools (ippTools) needed to track the process flow,
+  Nebulous, and DVO.
+\item {\bf Release Capabilities} Detrend image creation (bias, dark,
+  flat, fringe), image detrending (excluding convolved guide kernels),
+  single-image positive object detection and classification,
+  astrometric calibration, object detection databasing, basic
+  zero-point analysis.  Real-time ISP transparency measurements.
+\end{itemize}
+
+\subsection{IPP Release 2 (Oct 30, 2006) : Phase 4, static sky pixel definitions}
+
+\begin{itemize}
+\item {\bf Release Contents} Phase 4 analysis programs, Phase 4
+  PanTasks scripts, Static Sky pixel layout, interface to Magic,
+  psphot and ppImage upgrades.
+\item {\bf Release Capabilities} Image warping, Image differencing,
+  Image stacking, object modeling for difference images (loading PSF
+  from an external source, fitting positive and negative sources),
+  analysis of the OTA guide kernel, detrend images convolved with the
+  guiding kernel. 
+\end{itemize}
+
+\subsection{IPP Release 3 (Dec 30, 2006) : Static Sky, AP Catalog tools, output interfaces}
+
+\begin{itemize}
+\item {\bf Release Contents} Updates to psphot; DVO crawler tools
+  (uniphot, relphot, relastro); external interfaces.
+\item {\bf Release Capabilities} Static sky version of the photometry
+  analysis, with more-complete characterization of galaxy parameters
+  and the simultaneous multi-filter photometry; tools to perform the
+  astrometric and photometric calibration of the all-sky survey data;
+  the interfaces to provide data to the MOPS, PSPS, and other science
+  clients as defined.
+\end{itemize}
+
+\subsection{IPP Release 4 (Mar 30, 2007) : Code Freeze for ORR and the Survey Start}
+
+\begin{itemize}
+\item {\bf Release Contents} Updates to programs identified in the
+  commissioning.
+\item {\bf Release Capabilities} Improvements determined on the basis
+  of the commissioning with GPC-1 (possibly new PSPhot PSF models,
+  temperature dependent selects of input detrend data as needed, etc).
+\end{itemize}
+
+\section{Action Item 5: Internal \& External Interfaces}
+
+We have worked with the other PS1 teams to define complete ICDs
+between all of the interacting subsystems.  The IPP interfaces with 4
+existing subsystems, and will interact with other as-yet-undefined
+science clients.  We have completed the definitions of the ICDs for
+the Camera-IPP (PSDC-940-003), OTIS-IPP (PSDC-940-004), IPP-MOPS
+(PSDC-940-005), and IPP-PSPS (PSDC-940-006).  These are now part of
+the PSDC document tree.
+
+We have clarified the internal interfaces of the IPP in the SSDD.  We
+have improved the discussion of the types of interfaces, created a
+table listing the major interfaces, and identified the data format for
+each of the interfaces.  The detailed contents of those interfaces are
+specified in the software design description of the relevant software
+components.  For example, the SDD for PSPhot defines the formats of
+output object data tables, while the SDD for DVO/addstar references
+this document.
+
+\section{Action Item 6 : Plan for Finalizing the Analysis Algorithms}
+
+\begin{table}[ht]
+\begin{center}
+\caption{IPP Analysis Algorithms\label{algorithms}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Analysis}	       	 & {\bf IPP program}      & {\bf Status}  & {\bf Data Needed} \\
+\hline
+Bias Creation 	       	 & \code{ppMerge} 	  & completed     & raw bias images (range of conditions) \\
+Dark Creation 	       	 & \code{ppMerge} 	  & completed     & raw dark images (range of conditions) \\
+Flat Creation 	       	 & \code{ppMerge} 	  & completed     & raw flat images (dome and sky flats) \\
+Fringe Creation        	 & \code{ppMerge} 	  & 90\%          & raw fringe image (dome and night-sky images) \\
+\hline   
+Overscan Correction    	 & \code{ppImage} 	  & completed     & raw bias images (range of conditions) \\
+OTA Convolution	       	 & \code{ppImage} 	  & partial       & raw science images \\
+Non-Linear Correction  	 & \code{ppImage} 	  & partial       & correction functions (from camera group) \\
+Shutter Correction     	 & \code{ppImage} 	  & missing       & correction functions (from camera group) \\
+Domeflats or skyflats?   & \code{ppImage} 	  & completed     & photometry dither images \\
+Domefringe or skyfringes? & \code{ppImage} 	  & completed     & night-time images (range of sky conditions) \\
+\hline   
+PSF model	       	 & \code{psphot}  	  & completed$^1$ & science images (range of seeing) \\
+Sky model grid size    	 & \code{psphot}  	  & completed     & science images (range of seeing) \\
+Other PSPhot parameters  & \code{psphot}  	  & completed     & science images (range of seeing) \\
+\hline
+Astrometry search radius & \code{psastro} 	  & completed     & telescope pointing measurements \\
+Astrometry model order   & \code{psastro} 	  & completed     & science images (range of focus) \\
+\hline
+Interpolation kernel     & \code{stac}            & partial       & science images \\
+Difference image kernel  & \code{poisub}          & partial       & science images \\
+Static Sky cells         & \code{stac}            & partial       & simulations \\
+Static Sky combination   & \code{stac}            & partial       & science images \\
+\hline
+\multicolumn{4}{l}{$^1$Only analytical models are implemented. If more
+complex models are needed they will have to be implemented.}
+\end{tabular}
+\end{center}
+\end{table}
+
+The CDR Review Committee requests a plan for finalizing the analysis
+algorithms within the IPP.  There are three stages to freezing the
+analysis algorithms within the IPP:
+\begin{itemize}
+\item Algorithm conceptual design.  
+\item Development of the software used to implement the algorithm.
+\item Definition of parameters which modify the details of an
+  algorithm.
+\end{itemize}.
+
+The IPP SSDD defines the conceptual details of nearly all of the
+analysis algorithms in Sections 5, 6, and 7.  Of these descriptions,
+only Sections 5.5.5, describing the stacking of the Static Sky, and
+Section 7.2, defining the Static Sky photometry analysis, are
+insufficient in detail for the analysis to be well understood.  The
+discussion of the analysis used to generate the astrometric and
+photometric reference catalog are included in Section 4 on the design
+of DVO, and are somewhat sparse.  A new version of the SSDD with these
+three sections updated will be posted by Aug 30.
+
+Table~\ref{algorithms} lists the IPP algorithms, the relevant program
+in the IPP tree, the development state of the program with respect to
+the identified analysis, and the data needed to set the algorithm
+parameters.  In a separate document (`How to Define IPP Analysis
+Parameters'), we present a detailed discussion of the choices to be
+made and guidelines on making those choices.
+
+There are a handful of decisions which need to be made which have an
+important impact on the way the analysis is performed.  Most of these
+will have a major impact on the results, but have little or no impact
+on the IPP {\em design}.  The one exception is the question of exactly
+how the object analysis is performed on the Static Sky, as there are
+several possible choices here.  The major analysis issues to address
+are:
+
+\begin{itemize}
+\item {\bf Skyflats or Domeflats?}  We will need to choose between
+  skyflats and domeflats. This choice will require a comparison of the
+  photometric accuracy achieved by both methods.  Also important in
+  making this decision are the questions of how efficiently raw flats
+  may be obtained using both methods, how stable the two versions are,
+  and what kind of operations load they represent.  These are
+  questions beyond the scope of IPP and are largely the responsibility
+  of OTIS.  It is also an open question at this time if the planned
+  calibration screen will be available at the start of the Survey.
+  This is an operational requirement/decision that does not affect the
+  IPP design, though feedback from the IPP will be critical in making
+  the decision.
+
+\item {\bf Skyfringe or Domefringe?} This decision is similar to the
+  question of domeflats vs skyflats.  Here the operational concerns
+  for the domefringe images are more acute.  An important trade which
+  must be made is how many raw fringe images are needed to generate
+  the fringe images to be used by IPP.  It is also critical that the
+  number of input fringe images required by IPP for any Phase 2
+  analysis be minimized since it is impossible to afford the I/O load
+  demanded by a large number of input fringe images.  A related
+  question is that of how to sub-select the night-time fringe images
+  for best effect, if sky fringe images are used.  Based on experience
+  from CFHT/Megacam, it may be possible to use fringe images selected
+  on the basis of the time of night, but this must be tested for
+  Haleakala.  An alternative strategy, in which master fringe images
+  are combined based on their consistency with the science image, has
+  also been implemented for the IPP.  The decision between these
+  options will be guided by further testing of Megacam images and real
+  Haleakala data in a range of conditions.  It seems unlikely at this
+  time that a spectral skyprobe will be available for the start of
+  PS1, so we cannot rely on such a device to guide our choices.
+
+\item {\bf Static Sky Cell definition} What is the layout of the
+  Static Sky cells, and how does this depend on the different surveys?
+  In particular, what are the projection centers and the size of
+  individual files?  This question can be answered before we begin
+  collecting data by testing several of the competing options.  The
+  software is not tied to any specific choice of static sky cell
+  arrangement.  The simulations required to perform this decision are
+  being deferred until the IPP Release 1 can be completed.
+
+\item {\bf How is the image difference PSF-matching kernel
+  represented?}  The software currently implements the Alard-Lupton
+  kernel (multiple Gaussians) and the POIS kernel (array of delta
+  functions).  The software is easily modified to accommodate other
+  equivalent kernels such as Airy or Bessel-function decompositions.
+  It will be necessary to perform tests with real GPC-1 images to
+  determine which of these kernels achieves the best results.
+
+\item {\bf What interpolation kernel is used for image warping?}  The
+  choice of interpolation kernel has two important impacts on the
+  resulting data product.  First, there is a trade-off between the
+  accuracy of the kernel and the processing time required for the
+  analysis.  Second, there is the question of aliasing of
+  high-frequency features.  The first trade-off can be easily
+  performed with speed tests of the algorithms on the test hardware.
+  The second part of this question requires examples of the range of
+  PSFs and noise properties in real PS1 images.
+
+\item {\bf How are the input images to the static sky combined?}  Once
+  images are warped, there are several choices to be made in how the
+  data from multiple images are combined into a single static sky
+  image.  How do we select the input images as their PSFs change?  How
+  are images with different PSFs weighted?  What smoothing kernel (if
+  any) is applied to the input images before combination?  The 3$\pi$
+  survey will necessarily require the combination of pixels from a
+  range of field angles into the static sky.  These will have a range
+  of image qualities.  We will need to set the cuts to trade-off
+  between degradation of the final image quality versus degradation of
+  the signal-to-noise in the final image.  In general, our guidance
+  for the Static Sky is to maximize our ability to measure accurate
+  morphology on the images (to meet the Weak Lensing requirements) and
+  to provide the best reference for the image differencing algorithms.
+  Astrometric and photometric accuracy of the static sky measurements
+  is not as critical a driving requirement.
+
+\item {\bf How are the Static Sky images processed?}.  This particular
+  issue is critical to finalizing several aspects of the IPP design.
+  First, is it possible to meet the requirements for the Static Sky
+  (weak lensing accuracy and difference image accuracy) with a single
+  combination?  Is it necessary to degrade all images to a single,
+  common seeing (for a stable PSF across the image for difference
+  image)?  Is it possible to measure the weak lensing parameters
+  sufficiently accurately from the stacked image with knowledge of the
+  PSF?  To what level of detail is the PSF model required?  Is it
+  necessary to perform the weak lensing analysis (and other galaxy
+  shape parameter measurements) from the individual images rather than
+  from the Static Sky?
+
+\end{itemize}
+
+\section{Action Item 7: Data Validation Process}
+
+Throughout the IPP, the analysis steps have been defined with data
+validation as an integral step.  As the images are processed through
+the detrend creation stage, the Phase 1-3, and Phase 4, the analysis
+performed includes tests of the quality of the input images.  These
+measurements are included in the stream of metadata produced by the
+IPP (e.g., Tables 23, 25, 37 IPP SSDD).  In the discussion below, we
+identify the data validation measurements performed for the different
+steps.  The functional flow of these different analysis steps can be
+seen in IPP SSDD Figures 22-30.  The use of the Q/A measurements is
+not summarized very clearly within the text of version 00; this will
+be updated in the new SSDD release.  The types of Q/A measurements are
+also discussed below.
+
+Within the IPP, the analysis stages use these measurements to mark
+input images as accepted or rejected.  These assessments are passed
+back to the OTIS subsystem, along with the image statistics measured
+for the input images.  OTIS has the option of setting more stringent
+filters on the input images and re-observing images on the basis of
+the IPP feedback, even if the IPP accepted images which OTIS
+re-observes.  There is also a system-wide plan in place to use
+feedback from the IPP and from OTIS to guide the project's choices for
+survey strategies and science goals.
+
+\subsection{Detrend Images}
+
+The input detrend images have their pixel count levels measured for
+each chip.  Input images which have counts or fluxes outside of a
+defined range will be flagged and excluded from any detrend analysis.
+For example, the flat-field images should never use input images which
+are saturated, nor should the dark image analysis use input images
+with flux levels wildly outside of the nominal range.  Both conditions
+are evidence that the data were incorrectly obtained.
+
+In addition to raw pixel values, the input detrend images are
+confronted with the resulting master detrend images.  In general, the
+effect corrected by the master detrend image should adequately correct
+each of the input raw detrend images.  The residual scatter of the
+detrended raw images should be small.  As part of the detrend creation
+process, input images with excessive residual scatter are
+automatically rejected from the input stack.  These images are also
+flagged in the database.  
+
+The master detrend images are also evaluated by using the statistics
+of the (surviving) input residual images.  The global statistics of
+the residuals from the input set of images can be used to judge if the
+master detrend images has sufficient signal-to-noise or consistency to
+meet the requirements of the detrending process.
+
+\subsection{Science Images : Single-Image phases}
+
+As the science images are processed, a number of measurements are
+performed to tests the quality of these input images.  I discuss here
+the measurements which will be performed.
+
+\begin{itemize}
+\item median sky background : this can be used to trigger on images
+  taken in very poor conditions (eg, too early, dome lights on, too
+  close to the moon).
+\item sky background statistics after detrending : this can be used to
+  detect images taken under poor conditions (close to the moon, many
+  bright stars).  In the early stages, until we have a model for the
+  static sky surface brightness in each of the PS filters, these
+  measurements will only provide a reference and cannot be used as a
+  rigid identification of good/bad images. 
+\item fringe residual statistics : after the fringe correction has
+  been performed, a measurement of the variations correlated the
+  fringe pattern can be used to measure the quality of the fringe
+  correction.  Images which fall outside of the nominal range may be
+  indicative of sky emission line conditions not expected in the
+  initial master fringe sample, and may indicate a way towards further
+  improvements to the fringe correction process.
+\item image quality statistics : the PSF as a function of field
+  position can be used to detect images taken in degraded focus or
+  seeing conditions.  Images outside defined ranges can be excluded
+  from further analysis.
+\item Astrometric calibration \& scatter : failure of the astrometric
+  analysis to converge, or elevated scatter of the astrometric
+  solution are both evidence of images taken in poor conditions.
+  These may include telescope mis-pointing, tracking failures, or
+  focus failures.
+\item Photometric calibration \& scatter : Excessive variations of the
+  zero-point as a function of mosaic position, generally elevated
+  scatter, and substantial photometric offsets are all evidence of
+  problems with the observing conditions.  These may be the presence
+  of clouds and/or haze, degradation of the optics, and/or extreme
+  image-quality problems.
+\end{itemize}
+
+
+\subsection{Science Images : Image Stacking and Image Difference Phases}
+
+During the image stacking (P4$\Sigma$) and image differencing
+(P4$\Delta$) stages, data validation is based on measurements of
+consistency of the input images with the output stack and on the
+number of peaks detected in the difference image.  Images which have
+passed the single-image quality tests should in general succeed on the
+image stacking and difference stages.  However, large deviations can
+be expected in the event of unusual features within the images.  For
+example, uncorrected background variations from image to image will
+result in large deviations between the components of an image stack,
+and will also result in large numbers of difference image detections.
+Similarly, bright stars with larger-than-expected halos or saturation
+regions will result in excess difference image detections.
+
+\section{Action Item 8 : IPP Commissioning Plan}
+
+The IPP commissioning plan is supplied in the accompanying document,
+IPP Commissioning Plan.
+
+\section{Action Item 9 : Hardware Configuration and Requirements}
+
+The proposed IPP hardware configuration is driven by three major
+requirements: storage, processing power, I/O bandwidth.  We have
+performed a detailed analysis of these issues, which we have presented
+in Section 12 of the SSDD.  We concluded in our analysis that the
+storage component of the requirements completely dominated our
+hardware design considerations:  by specifying reasonable hardware to
+meet the storage requirements, we were supplying more than enough
+hardware to meet the processing and I/O requirements. 
+
+That analysis was based largely on prototype tests of our processing
+algorithms and has been made out-of-date by changes to the survey
+plans.  We present here new numbers for the processing time line based
+on the current baseline software on our existing baseline cluster
+hardware.
+
+% For PS1, there is a significant processing challenge in the first 6 -
+% 9 months when only a fraction of the IPP storage hardware will be
+% available.  This period is further complicated by the budgetary
+% constraints placed on the IPP to limit the hardware purchase to a bare
+% minimum.  An important area for clarification by the project is the
+% processing requirement in the beginning of the project.  If the IPP is
+% required to perform a complete Static Sky analysis on every image as
+% it becomes available, then the total hardware required in the first
+% 6-9 months for processing must be increased.  If it is only necessary
+% to stack sets of, for example, 4 images as they are available, then
+% the requirements are somewhat reduced.  A trade-off must be made by
+% the project to choose between these options.
+
+The data storage requirements are determined from the design reference
+mission.  The current plan for the design reference mission has
+increased somewhat the number of images obtained by the survey.  The
+current plan consists of an All-Sky Survey re-visiting each point in
+the sky 60 times over the 3 years (12 visits per filter).  There is
+also a Medium Deep survey which is expected to generate roughly 75000
+images per year.  Combining these two, we find that the total number
+of raw image data is roughly 1.7PB (555,000 images).  In addition, we
+have a requirement for Static Sky storage (assuming 0.2 arcsec pixels)
+of roughly 300TB, and miscellaneous additional storage of nearly 100
+TB.  Our hardware purchase plan has a minimum total storage of 2.4PB,
+giving us a margin of about 10\%.  Our plan is to purchase the
+hardware in 5 stages of 16 computers each, for a total system cost of
+roughly \$1.25M (each purchase set is expected to cost roughly
+\$250k).  Making reasonable estimates of disk capacity growth, we
+expect these 80 machines to meet the storage capacity requirements.
+Currently, we are able to buy 16.5TB per machine.  In the worst case
+that the hard disks stopped increasing in storage capacity (or we were
+required to buy all of the machines up front), we would require
+roughly 145 machines, increasing the cost of the cluster to a total of
+roughly \$2.2M.  We judge this to be a very low risk as hard disk
+capacities continue to grow.
+
+We have performed timing test of the current versions of the IPP tools
+for the different stages of the analysis using MegaCam images.  Four
+factors are critical in understanding if the cluster will be capable
+of performing the processing needed: 1) processing power of the
+cluster, 2) speed of the dominant analysis steps, 3) total number of
+object for which a full non-linear model fit is performed, 4) total
+number of images for which the warping and image differencing is
+performed.
+
+Current CPU development roadmaps project a slow rise in the clock
+speeds of the CPU cores, but increasing numbers of cores per socket.
+By the end of this year, quad-core processors are expected.  If we
+stagger the purchase of the computers as planned (5 sets of 16
+machines), and make reasonable estimates for the number of cores
+available, we expect the final cluster configuration to have between
+400 and 800 cores.  We will use 600 as an estimate.  Note that it is
+possible to supplement the processing power of the cluster by buying
+1U boxes with processors but minimal storage.  Each of these boxes
+cost roughly 15\% of a storage node and add an equal number of
+processor cores.  Such an option can be taken at any time to
+supplement the raw processing power, though it is not needed in our
+current development plan.
+
+There are two potentially dominant analysis steps in the process: the
+warping and PSF-matching for a single image (ie, the Phase 4 steps)
+and the non-linear model fitting performed on the brighter sources.
+The processing time for the warping and PSF-matching is essentially
+constant for each image.  On our demonstration hardware, this step
+takes roughly 16 seconds for a Megacam chip (single core), equivalent
+to roughly 38 seconds on a full GPC-1 chip.  Most other steps of the
+analysis require a constant amount of time per image, and contribute
+only a few seconds relative to the 38 seconds.  We use 50 seconds per
+chip per core to judge the total processing power for the portion
+which scales by the number of images.
+
+A useful statistic to judge the capability of the processing system is
+the time required to re-process all images at the end of the survey.
+Given the total number of images above (555,000), the per-image
+analysis portion of the processing would require a total of $\sim 1.8
+\times 10^9$ CPU core-seconds, or about 35 days on the 600 cores.
+
+The non-linear fitting speed is a bit more uncertain: the speed will
+depend on the number of parameters in the model and the number of
+pixels needed for most objects.  We have run a variety of example
+Megacam images and measured the speed per object for a several
+possible combinations of models and object footprints.  We find that
+the speed ranges from 2 - 10 milliseconds per object.  This number can
+be confronted with the total number of objects in images for which the
+non-linear fitting is performed.  This number depends on both our
+choice of a threshold for the non-linear fitting and the actual sky
+density of sources at that depth.  Our current proposal is to perform
+the non-linear fitting for all objects above a S/N limit of 20.  The
+total number of sources is dominated by the sources in the Galactic
+Plane.  As a rough estimate of the total number of sources above our
+threshold, we have examined the 2MASS catalog in the Galactic Plane
+anti-center region.  We have used the 2MASS and USNO-B colors to
+predict the Pan-STARRS magnitudes of stars, then extrapolated the
+source counts to our magnitudes limits.  We find 50,000 objects per
+square degree above our threshold in this region.  If we make the
+conservative assumption that every image required the non-linear
+fitting for this density of objects, and that each object requies
+10ms, this analysis would require a total of $\sim 2.0 \times 10^9$
+CPU core-seconds, or about 39 days on the 600 cores.
+
+In conclusion, given the assumptions above, the processing power of
+the proposed hardware will be sufficient to allow for a complete
+re-processing of all survey data within less than three months after
+the survey is completed.  We recognize, however, that there are still
+some large potential uncertainties in this analysis, particularly in
+the number of sources for which the analysis is performed.  To get a
+better understanding of the sky density of sources, we are extending
+our analysis of the 2MASS and USNO-B source densities across the full
+sky.  However, given the fact that the processing power of the cluster
+can be extended significantly without a large fractional cost, and the
+fact that the re-processing time above is well below our requirement,
+we do not believe there is a large risk in the hardware capabilities
+of the IPP design.
+
+\section{Action Item 10 : CFHT compatibility}
+
+The concern that the IPP is driven by CFHT compatibility issues is
+understandable, but easily addressed.  The IPP design is not locked
+into any specific design choices of the CFHT Elixir system.  The IPP
+uses one major subsystem, DVO, which is also used by the Elixir
+system.  It also is using code shared by certain Elixir systems in the
+IPP scheduler/controller system (PanTasks).
+
+The IPP is using the DVO system developed at CFHT for the object
+databasing.  This is a natural decision because the DVO system 1)
+existed, 2) was extensively tested, and 3) met most of the basic
+requirements of the IPP for object databasing.  Some effort has been
+needed to make DVO completely suitable for its role within the IPP.
+Regardless of what object databasing system was chosen, however, a
+certain level of effort would have been required.  In the case of DVO,
+we were clear what effort was required, and it was judged to be
+reasonable.
+
+Of that effort, only the ability to support older table formats was
+required to maintain compatibility with the existing CFHT DVO
+databases.  In fact, this is a feature which we would have added even
+if we did not want to maintain compatibility with CFHT's DVO
+installation.  We have found in our experience with the Elixir system
+that having a rigidly defined schema hindered the usability and
+extensibility of the DVO system.  The fixed tables made it difficult
+to add new elements to the database, and required multiple versions to
+support previously defined tables.  The new design allows us to be
+more flexible about changes without fear that this will break database
+instances which already exist.  One of the best ways we have found to
+test the DVO object databasing system is to engage students in science
+projects using DVO.  These projects explore the user interface and
+highlight problems and areas for possible improvement.  Such projects
+would not be possible if the users feared that their DVO instances
+would be unusable in the future because of lack of backwards
+compatibility.
+
+The PanTasks system used the existing Opihi command-line interface
+system which is also used by elements of DVO among other programs.
+The choice to use this existing framework made the implementation of
+the PanTasks system quick and efficient.
+
+The IPP owes a substantial intellectual heritage to the CFHT Elixir
+pipeline.  What the IPP gains by maintaining personal relationships
+with the CFHT Elixir team is access to an unparalleled set of test
+data and ongoing experience with a live running system facing the same
+concerns the IPP will soon face.  For example, only by virtue of this
+relationship are we able to test the IPP flat-field correction scheme
+of a large set of real images obtained by the CFHT engineering staff
+over several years.  We also gain by discussions with our Elixir
+colleagues about details of the analysis and possible sources of errors
+observed in the CFHT dataset.  The only cost to the IPP is in
+preventing excessive forking of the DVO databasing system, something
+we would attempt to avoid in any case.
+
+\end{document}
+
+-----------------------------------------------------
+
+* things we need to do within IPP
+
+  - clarify the rules of MAGIC
+    - do we run a version MAGIC on P2 images?
+    - do we use the imclean algorithm to remove detection lines?
+  - better list of all data products with details
+  - better list of all metadata database tables
+    - identify the Q/A elements
+  - explain authority chain:
+    - image headers have the primary authority
+    - non-image tables from the summit have the primary authority
+    - metadata database table elements derived from 
+
+  - completeness
+    - static sky images + weight maps specify if a region has been
+  observed at all
+    - DVO image query specifies which images (generally) overlapped an
+  area.
+    - queries via IPP mask tool specify if a location could have been
+  observed (PSF * mask).
+    - queries via IPP object tool could be used to force an object
+  measurement on a given location (PSF or aperture).
+    - IPP is willing to support up to 10 / min / machine (~200 - 800
+  per minute) during the daytime.  one request may specify many
+  objects per image (up to 100?)
+
+  - clarify DVO / MySQL relationship.  
+    - DVO needs to use the FITS tables internally for speed
+    - DVO will export the FITS tables to MySQL for external use
+
+  - better definition / discussion of the Phase 4 and Static Sky
+    components.
+
+  - commissioning plan
+  - deliverables and schedule
+
+
+---
+
+hardware test points:
+
+ppImage (no psphot) on Megacam : 40.650u 31.489s 2:46.41 43.3%   0+0k 0+0io 30pf+0w
+
+  * using alala I/O with 50 MB/sec read (and write?)
+  * total I/O was 4*708 MB (read) + 2778MB (write) = 112 sec
+
+  * processing time = 40 sec (actual) | 54 sec (clock)
+  * Npix = 353Mpix
+  * speed = 0.15 sec / Mpix
+
+  ** for 1 OTA : 3.5 sec processing time + 2-3 sec I/O (200 MB/sec)
+  ** for 1 ISP : 0.7 sec processing time + I/O
+
+psphot : 45 sec
+
Index: /tags/sj_tags/sj_root_20080929/doc/design/ippCommission.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/ippCommission.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/ippCommission.tex	(revision 22322)
@@ -0,0 +1,281 @@
+\documentclass[panstarrs,spec]{panstarrs}
+
+\title{IPP Commissioning Plan for PS-1} % put in your title
+%\subtitle{Specification}
+\author{Eugene Magnier}
+\audience{Pan-STARRS Science}
+\shorttitle{AP Survey Requirements}
+\group{Pan-STARRS IPP}
+\project{Pan-STARRS IPP}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-xxx-xxx}
+
+\newcommand\ugriz{$u^\prime g^\prime r^\prime i^\prime z^\prime$}
+\newcommand\grizy{$g r i z y$}
+
+\begin{document}
+\maketitle
+
+\section{Overview}
+
+The Pan-STARRS Image Processing Pipeline will undergo extensive
+commissioning tests in the period September - December, 2006.  With
+the staged arrival of the telescope, the first camera (TC-3) and
+optics, and the main camera (GPC), the commissioning will take place
+in a series of steps.  Initially, basic operating functionality will
+be demonstrated along with analysis throughput, without specific focus
+on achieving the final astrometric and photometric precision goals.
+Eventually, with the arrival of the GPC, the complete system
+requirements will be used as the commissioning criteria.
+
+\section{Pipeline / Infrastructure tests with Megacam data}
+
+Before actual camera hardware is delivered, the IPP will begin
+commissioning tests related to the infrastructure and interfaces.
+These commissioning tests may be performed with only a few IPP cluster
+nodes in the IfA computer room before hardware is deployed to Maui.
+Below, we list the commissioning tasks required to demonstrate the
+working infrastructure of the IPP.
+
+\begin{itemize}
+\item {\bf PanTasks / ippTools / Analysis Programs interactions :
+  Detrend Creation} The complete IPP pipelines for Phase 0 and the
+  Detrend Creation steps should be installed and set up on the test
+  cluster.  A complete megacam dataset must be run through the
+  pipeline.  This dataset will consist of raw bias, dark, flat-field,
+  and fringe images.  This commissioning task will demonstrate the
+  creation of master detrend images from raw detrend images.
+
+\item {\bf PanTasks / ippTools / Analysis Programs interactions :
+  Phase 2} The complete IPP pipelines for Phase 2 should be installed
+  and set up on the test cluster.  A complete megacam dataset must be
+  run through the pipeline.  This dataset will consist of science
+  images along with the master detrend images generated by the
+  previous task.  This commissioning task will demonstrate the
+  application of master detrend images to the raw science images and
+  the analysis up to object detection and photometry. (Phase 1 - guide
+  star analysis - is irrelevant to the Megacam test data).
+
+\item {\bf PanTasks / ippTools / Analysis Programs interactions :
+  Phase 3} The complete IPP pipelines for Phase 3 should be installed
+  and set up on the test cluster.  A complete megacam dataset must be
+  run through the pipeline.  This dataset will consist of science
+  images along with the master detrend images generated by the
+  previous task.  This commissioning task will extend the previous
+  test to include astrometric calibration relative to the installed
+  DVO database.  It will also demonstrate the operation of the
+  photometric calibration relative to the DVO database, though the
+  reference data will not be appropriate to judge the photometric
+  results at this stage.
+
+\item {\bf Camera - IPP Image transfer} Test GPC-1 images will be
+  injected into the test IPP system using the Data Store mechanism.
+
+\item {\bf Otis - IPP Images} Test ISP images will be injected into
+  the test IPP system using the Data Store mechanism.
+
+\item {\bf Otis - IPP Metadata} Test metadata tables will be passed to
+the test IPP system using the Data Store mechanism.
+\end{itemize}
+
+\section{Camera in lab : Detrend Image Tests}
+
+Before either camera is mounted on the telescope, as set of Camera-IPP
+commissioning tasks may be performed using biases, darks, and
+(depending on camera lab options) rough flat-field tests.  These
+initial commissioning tests are performed before a bad pixel mask is
+generated.  The tests are conditional on the bad pixels not
+overwhelming the statistics.  The same tests should be re-performed
+after the bad pixel mask has been built with data obtained on the
+telescope.  When the mask is available, the success criteria can be
+set to more stringent limits.
+
+\subsection{Bias construction / application}
+
+Obtain a stack of ($N > 10$) bias images.  Build a master bias and
+generate residual images (automatically part of the detrend creation
+system).  Examine the detrend creation Q/A statistics: residuals per
+chip, residuals per frame, number of 5 sigma outlier pixels.
+
+\subsection{Dark construction / application}
+
+Obtain a stack of ($N > 10$) dark images for each of a range of
+exposure times (15, 30, 60, 120, 240 seconds).  Build master darks and
+generate residual images (automatically part of the detrend creation
+system).  Examine the detrend creation Q/A statistics: residuals per
+chip, residuals per frame, number of 5 sigma outlier pixels.  Model
+the trend of average dark current vs exposure time.
+
+\subsection{Rough Flat-field construction / application}
+
+Obtain a stack of ($N > 10$) flat images for each of the filters with
+a range of count levels.  Build master flats and generate residual
+images (automatically part of the detrend creation system).  Examine
+the detrend creation Q/A statistics: residuals per chip, residuals per
+frame, number of 5 sigma outlier pixels.  Examine the amplitude of the
+flat-field residual as a function of count level (accuracy of shutter
+ballistics model, non-linearity).
+
+\section{Camera on telescope : Detrend Image Tests}
+
+\subsection{Bad Pixel Mask Construction}
+
+Supply a series of flat-field images to the mask creation
+tool. Examine the statistics of the flagged pixels, and examine the
+coverage by eye.
+
+\subsection{Basic Detrend Construction}
+
+Rerun the tests above (bias, dark, flat) using data obtained on the
+telescope, and applying the mask created above.
+
+\subsection{Fringe master construction / application}
+
+Obtain a stack of many potential input fringe images: night-time
+images in the appropriate filters over a range of airmass and time of
+night.  Build master fringe images and generate residual images
+(automatically part of the detrend creation system).  Examine the
+detrend creation Q/A statistics: residuals per chip, residuals per
+frame, number of 5 sigma outlier pixels.  Examine the amplitude of the
+fringe residual as a function of count level, airmass, etc.
+
+\subsection{Non-linearity}
+
+Measure the non-linearity by obtaining flat-field images with a range
+of exposure times and flux levels.  Measure the non-linearity by
+observing star fields with bright stars with a range of exposure times
+from short to long exposures.
+
+\subsection{additional detrend characterization tests}
+
+\begin{itemize}
+
+\item bias levels as a function of detector \& dome temperature
+
+\item dark level as a function of dome light level
+
+\item flat-field consistency as a function of illumination level:
+   obtain a sequence of input flats for each filter over the entire
+   twilight period.
+
+\item flat-field consistency as a function of internal vs external
+   illumination: obtain a sequence of input flats with dome shutters
+   opened different amounts
+
+\item flat-field consistency as a function of time: repeat the
+   flat-field construction process over N (> 5) photometric nights
+
+\item flat-field consistency as a function of solar angle: obtain a
+   series of flat-field sequences at a range of Alt/Az positions (Az =
+   0, 90, 180, 270; Alt = 45, 60, 80).
+
+\end{itemize}
+
+\subsection{Fringe construction tests}
+
+\subsection{Fringe consistency tests}
+
+\begin{itemize}
+\item fringe as a function of time-of-night
+
+\item fringe as a function of airmass
+
+\item fringe as a function of lunation
+
+\item fringe evolution twilight-to-nighttime
+\end{itemize}
+
+\subsection{Skyflat vs Domeflat comparison}
+
+Build master detrend images using both dome and sky flats.  a 
+
+input data: stack of N (5 - 10) skyflat images
+
+process: build a master flat, generate residual images (automatically
+part of the detrend creation system)
+
+test: residuals per chip, residuals per frame < limit, number of
+5 sigma outlier pixels.
+
+\section{Astrometry and Photometry}
+
+\subsection{Phase 2 / Phase 3 Astrometry solution tests}
+
+\begin{itemize}
+\item observe a high-quality astrometry field
+
+\item astrometry convergence for a range of stellar densities
+
+\item astrometry accuracy: perform a series of dithers and demonstrate
+   the astrometry solution
+\end{itemize}
+
+\subsection{Build the Flat Correction Frames}
+
+\begin{itemize}
+\item dither sequence in dense stellar field
+
+\item demonstrate photometric consistency within the dither pattern
+\end{itemize}
+
+\subsection{Photometry Calibration (Zero-Point) Checks}
+
+measure zero-points (using photflat) on megacam standards fields
+
+\section{Phase 4 Tests}
+
+\section{AP Survey Preparation Tests / Research}
+
+\subsection{Skyprobe Pre-AP Survey}
+
+Skyprobe observations in all 5 filters over 6x8 patch takes 30 minutes
+(45 seconds per point, one sweep in all filters).  The observable sky
+in one night is $\sim 25,000$ square degrees (ie 15\% less than 3pi),
+taking roughly 4500 pointings using the base overlaps.  The entire
+observable sky can be observed in 47 hours, or about 5 x 9 hour
+nights.
+
+In the pre-TC-3 / TC-3 era, we can perform a complete sweep of the
+observable sky every month. \note{dark half of the lunation only?}
+Between Feb-Jun, we can obtain 5 complete passes, including at least 3
+on even the portions which are in conjunction with the sun at some
+point in these 5 months.  The cost would be 25 photometric nights
+between Feb-Jun (16\% of the available nights).  This dataset will
+allow us to:
+
+\begin{itemize}
+\item build a complete skyprobe photometry dataset for the full 3pi.
+
+\item test the static sky construction process before GPC arrives.
+
+\item study the transparency stability of the sky at Haleakala.
+\end{itemize}
+
+\section{Transparency / Airmass Tests}
+
+we need to understand the stability of the atmosphere on Haleakala.
+Two effects which must be distinguished are variations in the airmass
+slope and the stochastic variations in the absolute transparency.
+
+we can follow a single spot on the sky (eg, Dec = 0 or 35) from the
+beginning of the night to the end of the night, for 9 - 11 full hours
+of transit.  we can obtain a 30-second image continuously (expose,
+readout, expose).  We need to do this on a photometric night (if the
+weather deteriorates, break and do some other tests).  More than one
+of these would be nice, at least one per filter.  the resulting
+photometry will illustrate the transparency of the sky versus the
+consistency of the airmass slope (thus, more than one night would be
+good, esp for g').  The measurement does not need the best possible
+flat-field image: keeping the position stationary reduces the
+sensitivity to the flat-fielding.
+
+To measure the stability of the atmosphere at high zenith angles, we
+can sit on the north pole field for several hours on a photometric
+night, performing 30 second continuous exposures.
+
+These two experiments would cost 5-10 photometric nights.  They can
+also be used to demonstrate the astrometric stability across a range
+of image qualities (high-to-low airmass).
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/design/ippParameters.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/ippParameters.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/ippParameters.tex	(revision 22322)
@@ -0,0 +1,298 @@
+\documentclass[panstarrs]{panstarrs}
+
+% basic document variables
+\title{How to Define IPP Analysis Parameters}
+\subtitle{A User's Guide}
+\shorttitle{Defining IPP Parameters}
+\author{Eugene A. Magnier}
+\audience{IPP Users}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{XX}
+\docnumber{PSDC-XXX-XXX}
+
+% allow paragraphs to be listed in TOC for now 
+\setcounter{tocdepth}{3} 
+
+\begin{document}
+\maketitle
+
+% -- Revision History --
+\RevisionsStart
+% version     Date         Description
+DR.01     & 2006.07.28 & First Draft \\ \hline
+\RevisionsEnd
+
+\tableofcontents
+\pagebreak
+
+% \listoffigures
+% \pagebreak
+\pagenumbering{arabic}
+
+\section{Overview}
+
+This Pan-STARRS IPP is a complex software system with many components.
+It is very flexible, and may be configured to handle data from a wide
+variety of sources.  The analysis programs have user-defined
+parameters which need to be set appropriately for a given set of data.
+Each camera, and in some cases each chip, may require slightly
+different settings.  This document outlines the process by which these
+parameters may be set and guidelines for which parts of the analysis
+programs may require tweaks.
+
+This document does not cover the setup of the IPP infrastructure.  The
+way in which the analysis programs are interrelated and how to define
+the tables used to track the data through the IPP is discussed in a
+separated document (TBD).
+
+The IPP performs a wide range of tasks, and consists of several
+top-level programs.  Some of these programs (eg, ppImage) are used in
+different contexts, and may require different parameter settings
+depending on the context.  The recipe system allows these contexts to
+be well-defined; please consult the relevant software design
+descriptions for information on managing different recipes.  The
+analysis program relevant to this document are listed in
+Table~\ref{programs}.  Notice that certain programs (i.e.,
+\code{psphot} and \code{psastro}) are available as stand-alone
+programs and also a library calls within other programs.  The recipes
+defined for one of these contexts may also be used by the other.
+
+\begin{table}[ht]
+\begin{center}
+\caption{IPP Analysis Programs\label{programs}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf IPP program} & {\bf Status}  & {\bf Function} \\
+\hline
+\code{ppMerge} 	  & completed     & Combine images for detrend creation (no warping) \\
+\code{ppImage} 	  & completed     & General single-image analysis program \\
+\code{ppStats} 	  & completed     & Report image statistics (from header and pixels) \\
+\code{ppNorm} 	  & completed     & Perform normalization on detrend images \\
+\code{psphot} 	  & completed     & Perform object detection and charaterization \\
+\code{psastro} 	  & completed     & Perform astrometric calibration \\
+\code{stac} 	  & prototype     & Warp and combine science images \\
+\code{poisub} 	  & prototype     & Perform image differencing \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsection{Detrend Creation}
+
+The Detrend Creation portion of the IPP depends heavily on
+\code{ppMerge} to build stacks of individual images.  In this section,
+we discuss the issues which must be examined for each of the types of
+detrend data to be created. 
+
+\subsubsection{Bias Creation}
+
+The bias creation tool (\code{ppMerge}) combines raw bias images into
+a master stack.  The tool may use several possible statistics to
+determine the value of an output pixel: mean and median, clipped
+versions, and robust version.  The value of the clipping parameters,
+if used must be set.  Decisions are primarily based on constructing
+bias images from a set of raw biases and applying them back to the raw
+bias imaes.  For any camera, following decisions must be taken:
+
+\begin{itemize}
+\item Is a bias image needed?  It can be skipped if a dark image is
+  used and the bias is the same for all dark exposures.  It may also
+  be skipped if the overscan subtraction is sufficient and
+  sufficiently stable. 
+\item Which combination statistic must be used?  The available options
+  may be tried and the best one selected.  
+\item Is it necessary to select the bias image on the basis of other
+  camera parameters (detector temperature? readout mode?).  Note that
+  these type of selections may be made by the detrend creation and
+  application tools, but they are not provided by default: they will
+  only be added to the tool if needed or if time permits before hand.
+\item Is it necessary to reject raw bias images from the stack on the
+  basis of other camera parameters (dome open/closed? detector
+  temperature?).  Note that these type of selections may be made by
+  the detrend creation tool, but they are not provided by default:
+  they will only be added to the tool if needed or if time permits
+  before hand.
+\end{itemize}
+
+\subsection{Dark Creation}
+
+The dark creation tool (\code{ppMerge}) combines raw dark images into
+a master stack.  The tool may use several possible statistics to
+determine the value of an output pixel: mean and median, clipped
+versions, and robust version.  The value of the clipping parameters,
+if used must be set.  Decisions are primarily based on constructing
+dark images from a set of raw darks and applying them back to the raw
+dark images.  For any camera, following decisions must be taken:
+
+\begin{itemize}
+\item Is a dark image needed?  It can be skipped if there is
+  insufficient dark current.
+\item Which combination statistic must be used?  The available options
+  may be tried and the best one selected.  
+\item Is it necessary to select the dark image on the basis of other
+  camera parameters (detector temperature? readout mode?).  Note that
+  these type of selections may be made by the detrend creation and
+  application tools, but they are not provided by default: they will
+  only be added to the tool if needed or if time permits before hand.
+\item Is it necessary to reject raw dark images from the stack on the
+  basis of other camera parameters (dome open/closed? detector
+  temperature?).  Note that these type of selections may be made by
+  the detrend creation tool, but they are not provided by default:
+  they will only be added to the tool if needed or if time permits
+  before hand.
+\item What is the scaling of a dark image by dark exposure time?  Is
+  it necessary to have well-matched darks, or can a set of exposure
+  times be chosen and interpolation used between these exposure times?
+\end{itemize}
+
+\subsection{Flat Creation}
+
+The flat creation tool (\code{ppMerge}) combines raw flat images into
+a master stack.  The tool may use several possible statistics to
+determine the value of an output pixel: mean and median, clipped
+versions, and robust version.  The value of the clipping parameters,
+if used must be set.  Decisions are primarily based on constructing
+flat images from a set of raw flats and applying them back to the raw
+flat images.  For any camera, following decisions must be taken:
+
+\begin{itemize}
+\item Are skyflats or domeflats better?  Both must be generated and
+  photometric analysis of observations of stars used to select between
+  these options.  Both the spatial consistency and the source
+  stability should be considered in making the decision.  Thus the
+  residual images should be used to judge the flat-field quality as
+  well. 
+\item Which combination statistic must be used?  The available options
+  may be tried and the best one selected.  
+\item For skyflats, what restrictions should be placed on the input
+  image?  (eg, time-from-sunset, sky flux level, mean image counts).
+\item Is it necessary to reject raw flat images from the stack on the
+  basis of other camera parameters (dome open/closed? detector
+  temperature?).  Note that these type of selections may be made by
+  the detrend creation tool, but they are not provided by default:
+  they will only be added to the tool if needed or if time permits
+  before hand.
+\end{itemize}
+
+\subsection{Fringe Creation}
+
+The fringe creation tool (\code{ppMerge}) combines raw fringe images into
+a master stack.  The tool may use several possible statistics to
+determine the value of an output pixel: mean and median, clipped
+versions, and robust version.  The value of the clipping parameters,
+if used must be set.  Decisions are primarily based on constructing
+fringe images from a set of raw fringes and applying them back to the raw
+fringe images.  For any camera, following decisions must be taken:
+
+\begin{itemize}
+\item Are skyfringes or domefringes better?  Both must be generated and
+  photometric analysis of observations of stars used to select between
+  these options.  Both the spatial consistency and the source
+  stability should be considered in making the decision.  Thus the
+  residual images should be used to judge the fringe-field quality as
+  well. 
+\item if skyfringes are used, how many reference images are needed?
+\item Which combination statistic must be used?  The available options
+  may be tried and the best one selected.  
+\item For skyfringes, what restrictions should be placed on the input
+  image?  (eg, time-of-night, sky flux level, mean image counts).
+\item Is it necessary to reject raw fringe images from the stack on
+  the basis of other camera parameters (dome open/closed? detector
+  temperature?).  Time relative to sunset is a default option, but
+  other parameters must be added on an as-needed basis.
+\end{itemize}
+
+\section{Detrend Analysis Recipes}
+
+A critical operation performed by the IPP is the application of the
+master detrend images to the individual science images.  This is
+performed by \code{ppImage} and is the core of the Phase 2 analysis
+stage.  Many of the issues important for the application of the
+detrend images have already been addressed in the detrend creation
+section above.  In this section, we discuss the steps of the analysis
+which may require inputs beyond those already discussed.
+
+\subsection{Overscan correction}
+
+For science images, we can correct the basic electronic offset (bias)
+by measuring the value of the overscan region.  The overscan may be
+used to determine a single value, a row-by-row vector, a spline fit, or a
+polynomial fit.  Clipping ranges must also be chosen.  These decisions
+are made by performing the overscan correction on bias images with
+different options and determining which results in a better
+correction.  
+
+\subsection{OTA Convolution of Detrend Images}
+
+For the OTA guided images, it is necessary to convolve the OTA kernel
+with some of the detrend images.  It is necessary to decide which of
+the images should be convolved.  The likely candidates are the
+flat-field and the fringe images.  The bias and dark images do not
+need to be convolved.  The flat images do.  The fringe images are less
+clear.  If the fringe patterns consist of lower spatial frequencies
+than the OTA kernel, then they may not need to be convolved.  The
+convolution method (direct vs fft) must be tested as well; the choice
+will depend on which is faster, which in-turn will depend on the
+typical size of the convolution kerne.  Night-time science images are
+needed to make this judgement.
+
+\subsection{Non-Linear Correction}
+
+The non-linearity of the devices must be measured.  This is the
+responsibility of the Camera team.  The resulting function or lookup
+table must supplied to the IPP and will be applied to the data.  The
+choice to make here is how the non-linear correction is represented:
+lookup table or interpolation function?
+
+\section{Single-Image Photometry}
+
+\subsection{PSF model function}
+
+The PSF model function must be selected from the available models, or
+a new model which better represents the available data supplied.  The
+current design of PSPhot supplies a range of analytical models.  The
+choice of which model to use, and the depends on the optical
+parameters of the camera
+
+\subsection{Sky Model Grid Scale} 
+
+PSPhot constructs a global model for the large-scale variations in the
+background to subtract before performing source detection and
+characterization.  This model is built by measuring the median for
+image pixels within large superpixels.  The size of the superpixels is
+configurable and must be chosen based on the maximum allowed size of
+the PSF and on the size of the smallest features which should be
+subtracted. 
+
+\subsection{Other PSPhot parameters}
+
+A variety of minor PSPhot parameters must be tuned to meet both the
+speed and the photometric accuracy requirements.  For example, the S/N
+per pixel of the pixels included in a source fit, or the maximum
+number of stars included in the measurement of the PSF model.  A
+number of images from the camera representing the expected or allowed
+range of seeing are required to choose these parameters.
+
+\section{Astrometry}
+
+\subsection{Initial Search Radius}
+
+The astrometry analysis requires a search radius within which it will
+search for matches between the reference stars and the observed
+stars.  The size of this search radius will depend on the
+repeatability of the telescope pointing.  The search radius should be
+as small as possible yet still guarantee that the match will be made
+for all images.
+
+\subsection{Order of Fitting Functions}
+
+The order of the polynomials used to represent the astrometry model
+needs to be set based on the observed data.  The smallest order which
+achieves the astrometric accuracy requirements should be used.  This
+value will depend on the real properties of the distortion in the
+images.  
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/design/ippSCD.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/ippSCD.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/ippSCD.tex	(revision 22322)
@@ -0,0 +1,3548 @@
+%%% $Id: ippSCD.tex,v 1.2 2006-02-16 18:57:50 eugene Exp $
+\documentclass[panstarrs]{panstarrs}
+
+% basic document variables
+\title{Pan-STARRS PS-1 Image Processing Pipeline}
+\subtitle{System Concept Design}
+\shorttitle{IPP SCD}
+\author{Eugene A. Magnier, Paul A. Price, Josh Hoblitt}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{01}
+\docnumber{PSDC-430-011}
+
+% allow paragraphs to be listed in TOC for now 
+\setcounter{tocdepth}{4} 
+
+\begin{document}
+\maketitle
+
+% -- Revision History --
+\RevisionsStart
+% version     Date         Description
+DR.01     & 2004.01.01 & First draft  \\ \hline
+DR.02     & 2004.03.05 & Second draft \\ \hline
+DR.03     & 2004.03.25 & Section reorganization \\ \hline
+DR.04     & 2004.04.13 & Most sections fleshed out \\ \hline
+DR.05     & 2004.04.29 & Reorganization for consistency \\ \hline
+DR.06     & 2004.10.21 & Major revision in prep of PDR \\ \hline \hline
+00        & 2004.11.04 & Incorporation of PDR Comments \\ \hline
+01        & 2006.01.22 & Name change (SSDD to SCD) before CDR \\ \hline
+\RevisionsEnd
+
+\inserttbd
+\inserttbr
+\pagebreak 
+
+\tableofcontents
+\pagebreak
+
+\listoffigures
+\pagebreak
+\pagenumbering{arabic}
+
+\section{Scope}
+
+\subsection{Identification}
+
+This document establishes Software Design Requirements for the
+Panoramic Survey Telescope and Rapid Response System (Pan-STARRS)
+Image Processing Pipeline (IPP) for the prototype telescope PS-1, and
+is a System-level controlled specification/design description document
+in the official Pan-STARRS engineering specification tree.
+
+\subsection{System Overview}
+
+The Institute for Astronomy at the University of Hawaii is developing
+a large optical synoptic survey telescope system, the Panoramic Survey
+Telescope and Rapid Response System (Pan-STARRS). The science goals,
+priorities, top-level concept of operations with associated
+operational requirements, and system performance drivers with
+associated system performance requirements are described in the
+Pan-STARRS Science Goals Statement (SGS).  As described in this
+document, The system conceptual design for Pan-STARRS utilizes an
+array of four 1.8m telescopes each with a 7 degree$^2$ field of view,
+giving the system an \'etendue larger than all existing survey
+instruments combined (defined as the product of the collecting area
+$A$ multiplied by the field-of-view solid angle $\Omega$).  Each
+telescope will be equipped with a 1 billion pixel CCD camera with low
+noise and rapid read-out, and the data will be reduced in near real
+time to produce both cumulative static sky and difference images from
+which transient, moving, and variable objects can be
+detected. Pan-STARRS will be able to survey up to $\approx 6,000$
+degree$^{2}$ per night to a detection limit of approximately 24$^{th}$
+magnitude.  This unique combination of sensitivity and sky coverage
+will open up many new possibilities in time domain astronomy including
+a major goal of surveying the Potentially Hazardous Object (PHO)
+population down to a diameter of $\approx 300$ meters.  In addition,
+the Pan-STARRS data will be used to investigate a broad range of
+astronomical problems of extreme current interest concerning the Solar
+System, the Galaxy, and the Cosmos at large.  A prototype single
+telescope system, PS-1, is being developed as a preliminary step
+before construction of the complete four telescope system.
+
+\begin{tabular}{ll}
+Project sponsor:&	AFRL, United States Air Force \\
+Acquirer:       &	University of Hawaii Institute for Astronomy \\
+User: 		&	Astronomical community \\
+Developer:      &	University of Hawaii Institute for Astronomy, participating \\
+                &       institutions, and associated subcontractors	
+\end{tabular}
+
+\subsection{Document Overview}
+
+The Pan-STARRS IPP System Concept Definition (SCD) contains the
+complete design concepts of the Pan-STARRS PS-1 IPP in order to
+achieve the requirements specified by the Pan-STARRS PS-1 IPP Software
+Requirements Specification (SRS; PSDC-430-005).  The requirements flow
+begun in the SGS and full Pan-STARRS SCD and continued in the SRS is
+used to guide the design presented here.
+
+\subsection{Requirements Definitions}
+
+The Pan-STARRS document naming scheme is PSDC-NNN-MMM-VV, where the VV
+entry specifies the document version number.  Where documents are
+identified without the version number, the latest official version in
+that series is implied.  
+
+Open issues (TBDs) in this document are marked {\bf \color{red} in
+bold red}.
+
+Quantities which should be reviewed (TBRs) are marked {\bf
+\color{blue} in bold blue}.
+
+\subsubsection{``Shall''}  When used in this specification, the word
+``shall'' refers to an explicit requirement of a system component or
+the complete system.  
+
+\subsubsection{``Should''}  When used in this specification, the word
+``should'' refers to a desired characteristic of a system component or
+the complete system.
+
+\subsubsection{``Will''}  When used in this specification, the word
+``will'' provides information about a characteristic of a related
+system component or a complete related system.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\DocumentsInternalSection
+PSDC-230-001  &   PS-1 Design Reference Mission \\ \hline
+PSDC-230-002  &   PS-1 System Concept Definition \\ \hline
+PSDC-400-006  &   The Pan-STARRS IPP Computational Challenge \\ \hline
+PSDC-430-004  &   Pan-STARRS IPP C Code Conventions \\ \hline
+PSDC-430-005  &   Pan-STARRS IPP PS-1 Software Requirements Specification \\ \hline
+PSDC-430-006  &   Pan-STARRS IPP Algorithm Design Document \\ \hline
+PSDC-430-007  &   Pan-STARRS IPP PSLib Supplementary Design Requirements Specification \\ \hline
+PSDC-430-010  &   Pan-STARRS IPP Perl Code Conventions \\ \hline
+PSDC-430-012  &   Pan-STARRS IPP Modules Supplementary Design Requirements Specification \\ \hline
+PSDC-430-014  &   Pan-STARRS IPP PS-1 Cluster Support \\ \hline
+\DocumentsExternalSection
+Posix Standard & Open Group Based Specifications Issue 6, IEEE Std 1003.1, 2003 \\
+\DocumentsEnd
+
+\section{Subsystem Overview}
+
+The Pan-STARRS Image Processing Pipeline (IPP) performs the image
+processing and data analysis tasks needed to enable the scientific use
+of the images obtained by the Pan-STARRS telescopes.  The primary
+goals of the IPP are to process the science images from the Pan-STARRS
+telescopes and make the results available to other systems within
+Pan-STARRS.  It also is responsible for combining all of the science
+images in a given filter into a single representation of the
+non-variable component of the night sky defined as the ``Static Sky''.
+To achieve these goals, the IPP also performs other analysis functions
+to generate the calibrations needed in the science image processing
+and to occasionally use the derived data to generate improved
+astrometric and photometric reference catalogs.  It also provides the
+infrastructure needed to store the incoming data and the resulting
+data products.
+
+The IPP inherits lessons learned, and in some cases code and prototype
+code, from several other astronomy image analysis systems, including
+Imcat (Kaiser), the Sloan Digital Sky Survey (REF), the Elixir system
+(Magnier \& Cuillandre), and Vista (Tonry).  Imcat and Vista have a
+large number of robust image processing functions.  SDSS has
+demonstrated a working analysis pipeline and large-scale database
+system for a dedicated project.  The Elixir system has demonstrated an
+automatic image processing system and an object database system for
+operational usage.
+
+The users of the IPP output are all systems internal to the Pan-STARRS
+project.  They consist of: 1) the Preferred Science Clients, which
+receive specified data products on short timescales.  2) the Moving
+Object Processing System (MOPS), which is one of the Preferred Science
+Clients, but has the distinction of being a component funded by
+Pan-STARRS.  It will receive the detections of non-stationary
+transient objects.  3) the Published Science Products Subsystem
+(PSPS), which will receive all data products of interest to the
+community external to the Pan-STARRS data processing systems, and will
+act as the long-term archive and publishing clearinghouse.
+
+The IPP receives data from two Pan-STARRS subsystems: the Camera, from
+which it receives the large volume of image data, and OTIS
+(Observatory, Telescope and Infrastructure Subsystem), from which it
+receives metadata describing the images and the environmental
+conditions.  The primary IPP hardware system on which the software
+operates will probably not be located at the summit.  Instead, because
+of thermal, power, and space constraints, the hardware will likely be
+located in a facility off the mountain.  A subset of processing tasks
+may eventually be assigned to machines at the summit if justified by
+the savings in data transfer time and cost.
+
+The Pan-STARRS camera produces images consisting of multiple chips
+(Orthogonal Transfer Arrays or OTAs), each consisting of multiple
+cells (continuous set of pixels).  The baseline design for the
+Pan-STARRS camera contains 64 chips each with 64 cells.
+
+This document defines the design requirements of the IPP for the PS-1
+prototype telescope.  Much of the IPP design for PS-4 will be
+identical to or closely based on the PS-1 implementation.  The
+software organization and the infrastructure systems are expected to
+be identical, with minor improvements in details.  The type of
+analysis steps to be performed will be nearly identical, with some
+additional details added for PS-4 to improve the accuracy.
+
+Although generally very similar, in terms of the IPP PS-1 differs from
+the complete PS-4 system in several specific ways.  First, with only
+one telescope and camera, the data throughput rate is substantially
+reduced to a maximum of 1 64-OTA image per 40 seconds rather than 4.
+Since PS-1 is a prototype for testing the Pan-STARRS hardware and
+software subsystems, the observing strategy is not a fixed quantity.
+The PS-1 Design Reference Mission (PSDC-230-001) provides some
+guidelines for the types of observing tests which will probably be
+performed, including possibly starting an Astrometric and Photometric
+Survey which will eventually cover the entire $3\pi$ steradians of the
+sky accessible to PS-4.  As a prototype, it is expected that much of
+the data collected by PS-1 will be processed multiple times to test
+and tune the analysis steps.  Compare with PS-4, this difference in
+approach has implications for the storage required by PS-1: rather
+than delete images soon after they have been used, raw images from
+demonstration observations must be stored for at least the first two
+years of PS-1 operations.  The PS-1 Design Reference Mission is used
+as an upper limit for these storage requirements to drive the hardware
+design.
+
+\subsection{System Design Decisions}
+
+Since Pan-STARRS is a survey project, all data from the telescopes
+will be uniformly analyzed by the Pan-STARRS Image Processing Pipeline
+(IPP), and the appropriate resulting data products made available to
+internal and external science analysis systems as they become
+available.  The processing performed by the IPP on the science images
+will consist of detrending and object detection for the individual
+images, combination of multiple overlapping images and further object
+detection, subtraction of a reference (static-sky) image and detection
+of residual objects, update of the static sky images, and detailed
+object analysis of the static sky images.  In addition, the IPP will
+produce improved astrometric and photometric reference catalogs on an
+as-needed basis.  The output data products from the IPP consist of the
+calibration images, reduced images from the individual telescopes,
+combined images, difference images, the static sky image, object
+photometry, and reference astrometry and photometry.
+
+The requirements for the IPP, as identified in the PS-1 IPP SRS
+(PSDC-430-005) fall into several broad categories: data analysis
+precision, throughput, system reliability, flexibility, testability,
+and traceability.  The details of the analysis tasks are specified in
+order to achieve the precision.  The architectural design as discussed
+below is motivated by the need for reliability and flexibility.  The
+hardware organization and the distributed/parallel processing model is
+motivated by the throughput requirements.  The need for flexibility
+and testability drives the software organization.  The need for simple
+testing procedures drives both the software organization and the
+separation of the system architecture into different infrastructure
+elements.
+
+\subsection{Analysis Tasks and Stages} 
+
+Specific programs are required to perform the processing steps listed
+above.  These can be divided into well-defined analysis stages, each
+of which operates on a particular unit of data, such as a single OTA
+image or a collection of astronomical objects.  Analysis tasks
+representing the different analysis stages are performed on the IPP
+computer cluster.  Note the distinction between the generic analysis
+{\em stage} and a specific analysis {\em task}.  An analysis stage
+represents a type of analysis which is performed, such as the basic
+image calibration and object detection analysis.  An analysis task is
+a particular realization of an analysis stage, e.g., the analysis of
+OTA number 61 from exposure 654321 to produce a specific set of output
+data products.  The analysis stages are discussed in detail in
+Section~\ref{sec:AnalysisStages}.
+
+A particular stage may process individual images, collections of
+images, or derived data products.  Because of the nature of the image
+data, many of the analysis stages can be run in parallel if needed to
+increase the processing throughput.  For example, the analysis of a
+chip in one image does not depend on the results from another chip.
+
+\subsection{Architectural Components}
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/IPPoverview}}
+\caption{ \label{fig:overview} IPP System Overview}
+\end{center}
+\end{figure}
+
+In order to achieve the required functionality, the IPP provides an
+infrastructure within which the Analysis Stages described above are
+executed.  In order to facilitate the subsystem testing, the IPP
+software infrastructure has been divided into a number of
+clearly-defined architectural software units as follows:
+
+\begin{itemize}
+
+\item {\bf Image Server:} This component is a large data store for all
+  images used by the IPP, including the raw images from the telescope,
+  the master calibration images, the reference static-sky images, and
+  any temporary image data products produced by the IPP.  The Image
+  Server accepts the incoming data and stores it until it is no longer
+  needed by other portions of the IPP.  The Image Server is not
+  restricted to imaging data: it is capable of storing any large data
+  files which are not well-suited for inclusion in a more structured
+  relational database, and for which access needs to be widely
+  available beyond the individual process which created the file.
+
+\item {\bf Metadata Database:} This component stores the data which is
+  not directly related to images or astronomical objects, but which is
+  needed to perform the IPP analyses.  The metadata may include the
+  summary weather information for each night, or details about the
+  filters, camera, telescopes, etc.  Note that the IPP Metadata
+  Database is not required to retain all archival engineering data
+  from all of Pan-STARRS; other Pan-STARRS subsystems use their own
+  internal databases to store engineering metadata and only the
+  necessary subset is transferred to the IPP Metadata Database.
+
+\item {\bf Astrometry \& Photometry Database (AP DB):} This component
+  stores and manipulates astronomical objects detected in various
+  images, as identified above, including individual measurements of
+  objects on the images, the summary information about those objects,
+  and reference object data.  It also provides mechanisms for users to
+  query and manipulate the objects and detections.
+
+\item {\bf IPP Controller:} In order to achieve the required
+  processing throughput for the IPP analysis stages, it is necessary
+  to use distributed computing processes on a large number of
+  computers.  The IPP Controller manages the collection of analysis
+  tasks performed on these machines.
+
+\item {\bf IPP Scheduler:} This component is a decision-making
+  mechanism which guides the operation of the IPP.  It evaluates the
+  currently available collection of data, identifies the necessary
+  analysis, and assigns the analysis tasks to the IPP Controller.
+
+\end{itemize}
+
+The relationship between these software units is shown in
+Figure~\ref{fig:overview}.  This figure also shows the interactions
+between the IPP and other Pan-STARRS systems.  The architectural
+components are discussed in detail in Section~\ref{sec:ArchComponents}.
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/IPPhardware}}
+\caption{ \label{fig:hardware} IPP Hardware Organization}
+\end{center}
+\end{figure}
+
+\subsection{IPP Hardware Organization}
+
+The IPP will utilize substantial computer resources, both in terms of
+computational power and in terms of data storage and network
+bandwidth.  The IPP requires relatively large amounts of data storage
+space, primarily for the image data.  Image data is organized in two
+categories.  First, there is the per-OTA data -- data associated with
+specific OTAs, including the raw images, the calibration images, and
+temporary processed images at various stages.  Second, there is the
+data associated with the static sky imagery, which is in turn
+organized into smaller sky-cell units.  In addition to image data,
+there are the somewhat smaller data entities of the Metadata Database
+and AP Database.
+
+The computer hardware is organized into nodes which provide both data
+storage and computational resources.  The data storage nodes are
+divided into three classes: those which deal with the per-OTA image
+data, those that provide the storage for the static sky images, and
+those that provide the storage for the other data systems, the
+Metadata Database and the AP Database.  In addition, the computational
+tasks related to the individual images take place on the per-OTA
+storage nodes and the processing of stacks of images takes place on
+the static sky storage nodes.
+
+Figure~\ref{fig:hardware} presents the basic concept for the hardware
+organization for the IPP.  This diagram shows the two types of compute
+nodes: (1) OTA-level processing and storage nodes and (2) Static Sky
+processing and storage nodes.  Also shown are two switches which
+divide the network into OTA and Static-Sky portions.  In such an
+organization, the inter-switch communication must meet the throughput
+needs between these network portions (though a single switch may also
+be used if its backplane capacity is sufficient).  The additional data
+systems (Metadata Database and AP Database) are also shown.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{System Design : Architectural Components}
+\label{sec:ArchComponents}
+
+\subsection{IPP Image Server}
+
+\subsubsection{Corresponding Requirements}
+
+The Image Server must meet the requirements specified in Section 3.4.1
+of the Pan-STARRS PS-1 IPP SRS (PSDC-430-005).  The specified design
+is chosen to meet requirements 3.4.1.3, and 3.4.1.5.  The other three
+requirements (3.4.1.1, 3.4.1.2, and 3.4.1.4) depend on the volume and
+capabilities of the hardware, and are addressed in
+Section~\ref{sec:Hardware}.
+
+\subsubsection{Image Server Overview}
+
+The IPP Image Server is a repository for all images and other large
+data files required by the IPP.  Along with the storage hardware, it
+provides tools for managing the distribution of these large data files
+and for accessing the files.  Data files stored by the IPP Image
+Server include the raw images, the calibration images, intermediate
+processing stage images as needed, final processed images, difference
+images, image subsections, and any large non-imaging data files needed
+by the IPP.  The IPP Image Server must retain the files for as long as
+they are needed by the IPP.
+
+The IPP Image Server is a parallel storage system.  It stores data
+across a collection of computer nodes, each with their own data
+storage resources.  Any single file is stored on only a single
+computer and storage device.  In order to achieve the data throughput
+requirements, the IPP Image Server may distribute the images across
+the processor nodes in an organized fashion, i.e., associating
+specific machines with specific detectors.  It is not the
+responsibility of the IPP Image Server to determine which computer
+should be associated with a specific data concept (Chip / region of
+sky), but it must enable the association of a particular file with a
+particular machine.
+
+There are three data concepts relevant to the IPP Image Server:
+\begin{itemize}
+\item {\bf Storage object:} This represents a single, unique data
+  entity in the Image Server.
+
+\item {\bf Instance:} A single copy of the storage object in the Image
+  Server.  In general, a given storage object may have several instances
+  in the Image Server, normally on different computer nodes.
+
+\item {\bf File ID:} This is the identifier of a particular storage
+  object in the Image Server.  The file ID is simply a unique string,
+  equivalent to the filename in a UNIX file system.
+\end{itemize}
+
+The Image Server provides file pointers (in C), handles (in Perl or
+Python), or file names corresponding to the instances of the storage
+objects.  The Image Server provides the data organization but does not
+define a file system; it assumes the existence of an appropriate file
+system which makes the files visible as local files.  This
+may be done over many machines with a network file system such as NFS
+or GFS.
+
+The IPP Image Server provides the storage and access mechanisms, but
+it does not include any logic or information about the data.  The
+Image Server does not, e.g., monitor the age of images and delete them
+on some schedule.
+
+As shown in Figure~\ref{fig:ImageServer}, the IPP Image Server
+consists of the following components:
+
+\begin{itemize}
+\item Image Server storage hardware 
+\item Image Server database 
+\item Image Server daemon
+\item Image Server client APIs
+\item Image Server maintenance tools (not shown)
+\end{itemize}
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/ImageServer}}
+\caption{The components of the IPP Image Server.}
+\label{fig:ImageServer}
+\end{center}
+\end{figure}
+
+\subsubsection{IPP Image Server Client APIs}
+
+Clients interact with the IPP Image Server via a small number of C
+APIs.  Bindings are also provided for Perl and Python and UNIX shell
+commands in some cases.  The client commands are:
+
+\begin{itemize}
+\item {\tt new object}: create a new storage object in the Image
+  Server.  This function takes as input the file ID and returns a
+  C-style file pointer or a Perl file handle to the instance of the
+  storage object.  The arguments to the function include an optional
+  node name on which the new storage object must be located.  If this
+  target is not given, the Image Server places the new storage object
+  on an appropriate machine from the pool, though the details need to
+  be specified.
+
+\item {\tt open object}: open an instance of an existing storage
+  object, as identified by the file ID.  This function may also
+  specify the node on which the object should be opened (if an
+  instance of the object is not stored on that node, the function
+  returns an error).  On success, the function returns a file pointer.
+
+\item {\tt find object}: return a list of filenames in the UNIX name
+  space associated with the storage object identified by the given
+  file ID.  Since there are in general multiple instances for a given
+  storage object, this function returns the collection of all
+  available instances.  These may be freely opened by the client
+  server using the standard \code{fopen} functions.
+
+\item {\tt stat object}: returns status information about the
+  specified storage object, including the number of instances of the object.
+
+\item {\tt replicate object}:a new instance of the given storage
+  object.  The target node may be optionally specified, otherwise an
+  appropriate node is selected.
+
+\item {\tt cull object}: removes one of the instances of the storage
+  object.  The input parameters may optionally specify the target
+  machine to delete.
+
+\item {\tt delete object}: deletes all instances of the storage object
+  and sets the storage object status to {\tt deleted}.  
+\end{itemize}
+
+\subsubsection{IPP Image Server Daemon}
+
+The Image Server client requests are mediated via the Image Server
+daemon.  Communication between the clients and the server is via SOAP
+implementing the commands above.  The identity of the machine on which
+Image Server daemon runs is part of the Image Server configuration
+information.
+
+\subsubsection{IPP Image Server Database}
+
+The IPP Image Server daemon uses a database to store the information
+about the data storage objects, their instances, and the available
+hardware resources.  A {\tt mysql} database engine is used to manage
+the database table.  The database tables defined for the Image Server
+are listed in Table~\ref{tab:ImageServerTables}, and their contents are
+listed in Appendix~\ref{sec:ImageServerTableContents}.  This database
+engine need not be the same one used for other IPP subsystems.
+%
+\begin{table}[ht]
+\begin{center}
+\caption{Image Server Database Tables\label{tab:ImageServerTables}}
+\begin{tabular}{ll}
+\hline
+\hline
+{\bf Table Name} & {\bf Description} \\
+\hline
+\code{storage_object}  & all storage objects known to Image Server \\
+\code{instance}        & all instances of all storage objects \\
+\code{volume}          & data storage devices known to Image Server \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsubsection{IPP Image Server Storage Hardware}
+
+The IPP Image Server manages data across a collection of computers and
+possibly on multiple storage devices on those computer nodes.  The
+Image Server maintains a table of the available data volumes.  The
+Image Server tracks information about each volume such as the total
+capacity, the current capacity, the association between computer and
+data volume.
+
+\subsubsection{IPP Image Server Maintenance Tools}
+
+The IPP Image Server provides a collection of administration tools
+which allow for maintenance.  These are operations which may be
+automatically scheduled by the IPP or which may be initiated by a
+human via a command-shell interface.  The maintenance functions
+include migrating data between nodes to re-balance the available space
+(this would only occur for instances which have not been placed on a
+specific node by the client API).  Other functions include checking
+for file corruption, which involves sweeping all files on a data
+volume and comparing the calculated file checksum to the currently
+recorded value.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Metadata Database}
+\label{sec:Metadata}
+
+\subsubsection{Corresponding Requirements}
+
+The Metadata Database must meet the requirements specified in Section
+3.4.2 of the Pan-STARRS PS-1 IPP SRS (PSDC-430-005).  The specified
+design is chosen to meet requirements 3.4.2.1, 3.4.2.2, 3.4.2.3,
+3.4.2.4, 3.4.2.5.
+
+\subsubsection{Overview}
+
+The IPP Metadata Database acts as a repository for non-pixel data
+needed by the IPP subsystems.  This includes the image metadata, the
+environmental data, system configuration data and system reference
+data.  The Metadata Database is required to save the non-ephemeral
+data for the lifetime of the project for future reference and
+additional analysis.  The Metadata Database may be used in close
+coupling with the analysis pipelines to store temporary data either
+within or between stages of the analysis.  In this scenario, the
+analysis pipeline will interact directly with the database.  However,
+database latency may make this scenario impractical, in which case the
+database may be used for long-term storage only.  In this scenario,
+the data produced by analysis pipelines which is destined for the
+Metadata Database may be collected and inserted by a separate,
+dedicated process.  Metadata which is large in volume or poorly
+structured may also be stored in an appropriate container file (FITS
+Table, FITS Header, XML File) in the Image Server with the Metadata DB
+providing pointers to these files.
+
+The IPP Metadata Database is a simple database system, consisting of a
+number of simple tables without extensive inter-table links.  The
+\code{mysql} database engine will be used to drive the database.
+
+\begin{table}[hb]
+\begin{center}
+\caption{Metadata Database Tables\label{tab:MetadataDBTables}}
+\begin{tabular}{ll}
+\hline
+\hline
+{\bf Table Name}           & {\bf Description} \\
+\hline
+Weather                    & Details on the weather, including internal temperatures. \\
+SkyProbe Transparency      & Analysis of SkyProbe B \& V data. \\
+SkyProbe Absorption        & Analysis of SkyProbe A data. \\
+SkyProbe Emission          & Analysis of SkyProbe E data. \\
+DIMM                       & Summary of DIMM data analysis. \\
+NIR                        & Summary statistics from NIR camera. \\
+Dome Status                & The time history of the dome status. \\
+Telescope Status           & The time history of the telescope status. \\
+Raw FPAs                   & Information about the raw FPA exposures. \\
+Pending Science Chips      & Science images to be processed and status. \\
+Processed Science Chips    & Science images which have been migrated to the processed state. \\
+Observation Group          & Details about a group of associated observations. \\
+Observation Frame          & Major frame information. \\
+Science Processing stats   & Details on processed cells. \\
+Chip / Sky overlaps        & List of overlaps between sky cells and detectors. \\
+Processed Sky-Cell stats   & Details of the sky cell processing. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsubsection{Metadata Tables}
+
+Table~\ref{tab:MetadataDBTables} lists the Metadata tables identified to
+date for the Metadata Database.  The contents of these tables are
+outlined in Appendix~\ref{sec:MetadataTableContents}, with examples for
+the data entries and their data types in many cases.  Additional
+tables will be added as necessary as the data analysis scripts are
+fleshed out in detail.  The Metadata Database, with a flat data
+organization, is flexible enough to add additional information as it
+is identified.
+
+\subsubsection{Metadata Queries}
+
+The IPP provides simple queries to the Metadata Database tables using
+auto-coded APIs.  These queries return a single row or a collection of
+rows based on the primary key.  The format of the API is identical for
+all Metadata tables.  New tables and APIs can be added to the IPP
+system by adding to the auto-code table description files.  The
+auto-code API includes read and write access permissions to be set
+for each table independently. See Appendix~\ref{sec:AutocodeIO} for
+further information.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{AP Database}
+
+\subsubsection{Corresponding Requirements}
+
+The AP Database must meet the requirements specified in Section 3.4.3
+of the Pan-STARRS PS-1 IPP SRS (PSDC-430-005).  The specified design
+is chosen to meet requirements 3.4.3.1 and 3.4.3.2.  In order to meet
+the throughput requirements, the AP Database will be distributed
+across 10 Nodes independent of the Image Server Nodes.  An alternative
+organization of the database which will be studied will have the AP
+Database co-located with the Image Server Phase 4 Nodes.
+
+\subsubsection{Overview}
+
+The AP (Astrometry \& Photometry) Database is a CSCI which stores data
+related to astronomical objects derived from various sources with a
+variety of associations.  The AP Database deals with two related
+concepts: {\em objects} and {\em detections}.  The {\em objects} are
+descriptions of astronomical objects while the {\em detections} are
+the specific measurements of those objects, typically measured from
+astronomical images.  A collection of {\em detections} may be used to
+derive average quantities which describe a particular {\em object}.  A
+third class of measurement to be considered are those supplied by
+external references.  Such measurements may be treated as {\em
+detections}, with the caveat that access to the raw measurements and
+metadata are usually unavailable: the reported measurements and errors
+must be accepted as they are reported.
+
+The AP Database stores the collections of detections which were
+derived from specific images from any of the analysis stages.  It
+provides a mechanism to determine the image from which a specific
+detection was derived, and in conjunction with the Image Server locate
+the corresponding data file.  The AP Database also makes it possible
+to extract all detections derived from a specific image and to
+determine quantities such as the pixel coordinates of the detection on
+the image.
+
+The AP Database also has the capability to associate multiple
+detections of a specific object.  Several major classes of objects
+will be present, each of which must be handled correctly.
+
+First, the most distant stars, compact galaxies, and QSOs will have
+nearly fixed locations relative to other distant stars, with only
+small deviations for individual measurements.  The association between
+multiple detections of such objects is made on the basis of their
+coincident positions.  The AP Database determines the average position
+of the object and the deviations of the individual detections from
+that average on the basis of the ensemble of individual detection.
+
+Second, solar system objects do not have a fixed location.  Detections
+of such objects are linked by their orbits, and depend on both the
+position and the time of the image.  The AP Database does not attempt
+to make this link; this is the role of the MOPS system.  However, it
+has the ability to accept identifications made externally with
+specified detections and to return the identifier of the moving object
+associated with the specific detections.  These associations also
+include descriptive information such as the offset of the detection
+from the predicted location of the detection based on the orbit.  This
+functionality is required to allow the AP Database to ignore known
+moving object detections from other types of queries.
+
+Third, objects in the general vicinity of the solar system fall in
+between these first two classes of objects.  Their proper motion and
+parallax response is significant enough ($>0.2$ arcsec in 1 year) that
+they are not well-described by an average location and a collection of
+offsets.  These objects are described by a distance and a proper
+motion vector.  The AP Database provides the association between the
+specific detections and an average object which includes finite
+parallax and proper motion.
+
+Fourth, many detections, especially in their initial states, will not
+be associated with a specific astronomical object of any of the above
+classes and are treated as orphans.  Most of these will be spurious
+(not representing real objects), some will be from solar system
+objects for which orbits are not yet determined, some will be from
+faint stars near the detection limits, and some will be from
+short-term transients which have only been detected once.  The AP
+Database maintains these detections until they have been associated
+with one of the objects above.  The AP Database provides mechanisms by
+which individual detections may be migrated back and forth between the
+orphan state and association with an astronomical object.
+
+For every object, and all orphaned detections, the AP Database also
+provides the capability to determine the images containing the
+location of the object but for which no detection was made.  The
+minimum set of information which must be carried for these
+non-detections is the image and the associated object or orphan.
+
+The AP Database also stores the relationships between various
+photometric systems and the evolution of that relationship.  It
+provides mechanisms to convert between the measured instrumental
+magnitude of a detection with a specific filter, detector, and
+telescope, and at a particular time and the implied magnitude in the
+average Pan-STARRS photometry system, given a determined set of
+calibrations.  It also provides the capability to convert magnitudes
+in one system to the magnitudes in another system; an example of such
+a conversion is between the average Pan-STARRS filter systems and the
+various reference systems appropriate for those filters.
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/APDB}}
+\caption{AP DB components}
+\label{fig:APDBComponents}
+\end{center}
+\end{figure}
+
+The AP Database provides interfaces to extract lists of objects and
+detections based on various query parameters.  It provides the
+capability to extract all detections associated with a specific
+object, all non-detections of that object, all non-detections of an
+orphan, and summary statistics from these collections.  It will also
+return all objects or detections within specified spatial regions
+including regions bounded by great circles (RA,DEC; GLAT,GLON;
+ELAT,ELON) and regions described by a location and a search radius.
+It will also return the image parameters associated with a specific
+detection including image coordinates of the detection, exposure time,
+time and date of the detection, etc.
+
+As shown in Figure~\ref{fig:APDBComponents}, the IPP AP Database
+consists of the following components:
+
+\begin{itemize}
+\item AP Database database tables
+\item AP Database database engine
+\item AP Database servers
+\item AP Database client APIs
+\end{itemize}
+
+\subsubsection{AP Database Tables}
+
+Table~\ref{tab:APDBTables} lists the tables used by the AP Database.  The
+contents of these tables are outlined in
+Appendix~\ref{sec:APDBTableContents}.  Below, the use of these tables by
+the AP Database software is discussed below.  Several of the tables
+are not just simple tables in the database but are instead table
+groups divided into many subtables, each of which represents a portion
+of the sky (a {\tt region}).  These subtables may also be distributed
+across different computers to distribute the processing load.
+
+\paragraph{Images Table Group}
+
+The {\tt Images} table group lists all of the images which provided
+the data in the AP Database.  These tables are subdivided by region on
+the sky.  In general, the images listed in this table correspond to
+the Chips.  This group of tables includes sufficient astrometric
+parameters to represent the coordinates of the detections to a
+sufficient accuracy.  Parallel to the Images table is the Mosaic
+table.  This table is very similar to the Images table, but defines
+the Mosaic which corresponds to a group of Images.  The parameters
+include the astrometric information needed to define the camera
+distortion.
+
+\paragraph{Image Overlaps Table Group}
+
+The specific subtable of {\tt Images} which contains a given image is
+the one which contains the center pixel of that image.  An additional
+table group, {\tt Image Overlaps} (with the same subtable organization
+as the {\tt Images} subtables), lists images which overlap that
+specific subtable.  Thus, given a particular coordinate, in order to
+find that images which overlap that coordinate, it is necessary to
+search the images in the {\tt Images} subtable which includes that
+coordinate, and all images in the {\tt ImageOverlaps} subtable for
+that coordinate.
+
+\begin{table}[hb]
+\begin{center}
+\caption{AP Database Tables\label{tab:APDBTables}}
+\begin{tabular}{ll}
+\hline
+\hline
+{\bf Table Name} & {\bf Description} \\
+\hline
+Images               & The images that have objects in the DB. \\
+Image Overlaps       & Image regions which are touched by specific images. \\
+Objects              & The objects --- average properties of multiple detections of the same object. \\
+Average Magnitudes   & Average photometry in multiple filters \\
+Solar System Objects & Identification of solar system objects \\
+Matched Detections   & Detections of sources in an image identified with an Object. \\
+Orphaned Detections  & Detections of sources in an image not identified with an Object. \\
+Non-detections       & Non-detections of objects in an image. \\
+Regions              & spatial distribution of tables \\
+Filters              & Filters understood by the system. \\
+Photcodes            & Transformations between different photometric systems \\
+Zero Points          & History of Zero-point \& Airmass terms \\
+Distortion Models    & History of Optical Distortion terms \\
+Database Hosts       & computers used to store the tables \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\paragraph{Objects Table Group}
+
+The {\tt Objects} table group (also divided by region) stores the
+average parameters for each astronomical object.  Certain details of
+this table have not yet been specified.  In particular, objects with
+significant parallax and/or proper motion may potentially be stored in
+a distinct table.  Solar system object identifications, to the extent
+average properties are maintained in the AP Database, will certainly
+be stored in a separate table.  
+
+\paragraph{Average Magnitudes Table Group}
+
+A related table, also divided into the same regions, is the {\tt
+Average Magnitudes} table.  In this table, there are multiple rows per
+object, one for each of the primary filters of interest for which
+photometric averaging is performed.  This organization makes the
+number of primary (averaged) filters a configurable value.
+
+\paragraph{Matched Detections Table Group}
+
+The {\tt Matched Detections} table stores all of the measurements of
+astronomical objects on specific images.  This table includes all
+detections associated with the average {\tt Objects}.  As discussed
+below, bright objects (above a configuration-specified signal-to-noise
+level) are defined object even if only one detection has been found at
+that position.  Faint orphaned objects are not added to this list or
+the list of objects.  The different types of detections (P2,
+P4$\Delta$, P4$\Sigma$) are distinguished by their photometry codes.
+(This is only valid if the AP Database does not store different
+quantities for these types of detections.)
+
+\paragraph{Orphaned Detections Table Group}
+
+The {\tt Orphaned Detections} table stores the detections which have
+not been correlated with an existing object.  This table is only
+populated for objects below a configuration-specified signal-to-noise
+limit (e.g., 5$\sigma$).  Bright orphaned detections are assigned an
+object and added to the {\tt Matched Detections} table.
+
+\paragraph{Non-detections Table Group}
+
+The {\tt Non-detections} table stores information about detection
+failures for each object.  If an image is added to the database which
+overlaps an object but the object is not detected, an entry is made in
+this table.  In practice, this table may store only the most recent
+non-detection and the total number, or a similar reduced set of
+non-detection statistics.
+
+\paragraph{Regions Table}
+
+The {\tt Regions} table is used to subdivide the tables of images,
+objects, and detections, etc, as discussed above.  The AP Database
+divides the sky into a hierarchy of regions (portions of the sky) each
+of which is in turn subdivided into smaller portions.  Since nearly
+all interactions with the AP Database performed by the IPP are limited
+in spatial coverage, subdividing the tables allows a specific
+interaction to search only a small subset of the data.  The table of
+images is the smallest of the three; the table of detections is likely
+to be the largest.  As a result, the {\tt Images} table group will be
+subdivided at a shallow hierarchical level, while the {\tt Objects}
+and {\tt Detections} are subdivided on deeper (more finely sampled)
+levels.  The {\tt Regions} table defines the boundaries of the sky
+regions and specifies if the region corresponds to an {\tt Images}
+table, an {\tt Objects} table, and/or a {\tt Detections} table.  It
+also specifies which regions in the next level of the hierarchy are
+contained by the region, and which parent region it belongs to.  In
+addition to improving the spatial access to the image, object, and
+detection data, the {\tt Regions} table allows for multiple computers
+to serve the database tables.  The region file specifies the machine
+which stores the specific table.  Figure~\ref{fig:APDBRegions}
+illustrates schematically the subdivision of the sky and the
+association between different levels of the hierarchy with different
+subtables.
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/APDBRegions}}
+\caption{AP DB Regions and Image / Object tables}
+\label{fig:APDBRegions}
+\end{center}
+\end{figure}
+
+\paragraph{Other Reference Tables}
+
+The {\tt Filters} table identifies all of the physical filters
+(specific pieces of glass) known to the system.  A related table, {\tt
+Photcodes}, defines relationships between photometry systems.  A
+photometry system may consist of a detector, telescope, and specific
+filter, or it may be a derived photometry system.  The {\tt Database
+Machines} table identifies all of the computers available to the AP
+Database.
+
+\subsubsection{AP Database servers}
+
+The AP Database functions on a group of computers, with portions of
+the tables stored on separate machines, as described above.  The
+association between a machine and the corresponding table or part of
+the sky is defined by the Region table.  Each machine has a
+corresponding AP Database server which runs on that machine to
+interact with the tables available on that machine.  Two possible
+interaction models are considered.  
+
+{\bf Option A:} A client chooses one of the machines and sends its
+query or data to that machine.  The server then uses the region table
+to determine which machines contain the relevant portion of the sky.
+Data to be added to the database is divided into corresponding region
+chunks and sent to the appropriate servers.  Queries are redirected to
+the appropriate server(s).  The original server may collect the
+results and return them to the original client.
+
+{\bf Option B:} The client downloads the region table and performs the
+division of the data into appropriate subsets.  The subsets are then
+sent to the corresponding servers by the client.  
+
+The differences between these models is small.  The first option may
+make the code more testable, placing all of the logic in the servers
+and making each server symmetric.  The smaller tables (ie, Region,
+Filters, etc) could either be downloaded from a single server or
+replicated to all AP DB servers.  For these reasons, Option A will be
+used for the PS-1 IPP..
+
+\subsubsection{AP Database engine}
+
+The backend database engine for the AP Database stores the tables and
+provides them to the servers on demand.  The AP Database will use a
+\code{mysql} database engine for this function.
+
+\subsubsection{AP DB Client operations}
+
+The AP Database client interactions consist of a collection of basic
+queries of the database, along with more complex operations to perform
+particular tasks.  The complex operations are listed below.
+
+\paragraph{Insert Image \& Detection Set (addstar)}
+
+One of the most basic operations needed by the AP Database is to
+insert a collection of detections derived from a specific image, and
+add the definition of that image to the database.  This operation is
+critical in terms of the processing throughput.  After the detections
+have been assigned to the appropriate regions, they are matched
+against all objects in the {\tt Objects} table.  Matches are performed
+only on the basis of positional coincidence, using a matching radius
+which may depend on the image astrometry errors, or may be a fixed
+distance.  Any matched detections are added to the {\tt Matched
+Detections} table.  Any unmatched detections brighter than the Faint
+Detection cut-off are specified as a new {\tt Object} and also added
+to the {\tt Matched Detections} table.  Any faint unmatched detections
+are added to the {\tt Orphaned Detections} table.  This division is
+important because it allows the automatic association of new
+detections with existing bright objects while limiting the I/O volume
+required to make the detections.  In general, there will be many fewer
+{\tt Objects} than {\tt Detections}, and there will be fewer bright
+orphans than faint orphans.
+
+\paragraph{Insert Reference Objects (addrefs)} 
+
+This operation is very similar to the previous one.  A collection of
+reference objects are added to the database as a collection of
+detections.  The reference photometry should in general be given its
+own photometry code.  The reference data is different from the image
+detection set because the associated image information is not
+included.  Thus, no corresponding images are added to the database.
+
+\paragraph{Determine Relative Photometry in region (relphot)}
+
+This operation uses the overlaps of images and multiple observations
+of the same objects to determine the relative photometry zero-points
+for a collection of images.  This is a task that wil be run much more
+infrequently than the object insertion tasks.
+
+\paragraph{Determine Consistent Photometry Zero Points (uniphot)}
+
+This operation uses the time history of relative photometry zero
+points for images and the spatial overlap information to determine a
+best set of image zero points which have a specific time scale for the
+atmospheric stability.
+
+\paragraph{Determine Distortion and Static Astrometry Model (mosastro)}
+
+This operation uses the reference and image detections to determine an
+optical distortion model for the camera and static astrometry model
+components.  The astrometry model includes: (1) field distortion
+introduced by the telescope optics, which is a smoothly-varying
+function of the field position relative to the center of the telescope
+boresite coordinates.  (2) focal plane geometry, which includes the
+chip positions and rotations in the focal relative to the boresite,
+along with chip-dependent plate-scale modifications needed to
+represent tilts or warps of the individual detectors relative to the
+ideal flat focal plane. .
+
+\begin{table}
+\begin{center}
+\caption{AP Detection Classes \& Object Parameters\label{tab:APdetections}}
+\begin{tabular}{lrrrr}
+\hline
+\hline
+Object Parameter & P2 & P4S & P4D & SS \\ 
+\hline
+PSF x,y, covar, $\alpha,\delta$               & + & + & + & + \\
+PSF mag, $\sigma_{\rm mag}$                   & + & + & + & + \\
+star/gal sep                                  & + & + & + & + \\
+$\sigma_x$, $\sigma_y$, $\theta$              & + & + & + & + \\
+local sky data                                & + & + & + & + \\
+Petrosian R, M, $R_{50}$, $R_{90}$            & - & + & - & + \\
+S\'ersic R, M, AB, $\phi$, $\nu$              & - & + & - & + \\
+W.L. $\gamma_1$, $\gamma_2$, pol. terms       & - & - & - & + \\
+exp. spaced aps., Poisson noise, variance     & - & - & - & + \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsubsection{Throughput}
+
+The AP Database design partly driven by the need to make the
+detection-object associations quickly and to processes the incoming
+detections at a sufficiently high rate to meet the throughput
+requirements.  For each upload of the object detections from a
+complete FPA, the AP Database must match roughly $1.4 \times 10^{6}$
+detections from an FPA with roughly $6.4 \times 10^{6}$ objects,
+including orphaned bright detections.  This corresponds to roughly 640
+MB, if each object uses 100 bytes for its descriptive informations
+(more than is currently specified in the Object table).  With a
+throughput of 100 MB/s for reads from a RAID, the AP Database can
+perform the data read in a fraction of a second if the data is
+distributed across 10 computers.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Controller}
+\label{sec:Controller}
+
+\subsubsection{Corresponding Requirements}
+
+The Controller must meet the requirements specified in Section 3.4.4
+of the Pan-STARRS PS-1 IPP SRS (PSDC-430-005).  The design must meet
+requirements 3.4.4.1 - 3.4.4.7.  In particular, the Controller / Node
+Agent architecture is chosen to control the I/O flow between the
+Controller and the individual processes so that blocking on the I/O
+from many remote processes does not saturate the Controller
+processing.
+
+\subsubsection{Overview}
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/Controller}}
+\caption{Schematic illustration of the Controller components}
+\label{fig:Controller}
+\end{center}
+\end{figure}
+
+The IPP uses a group of computers to store and process images and to
+manipulate collections of detections.  These computers perform any of
+a large number of analysis stages or other processing tasks without
+significant interprocess communication.  It is necessary to have a
+mechanism which initiates computing tasks on the different computers,
+which monitors the tasks as they are executed, which handles the
+output and the errors from these tasks, and which reacts to the
+failure of any of the computing nodes.  The system responsible for the
+tasks in the IPP is the IPP Controller.
+
+The IPP Controller interacts with the collection of computers under
+its management and with other subsystems in the IPP.  The IPP
+Controller receives a variety of inputs from other subsystems,
+described below, and initiates actions such as adding a new process to
+the queue of pending tasks.  The IPP Controller also provides
+information to other subsystems on demand about its processing history
+and current state.  Each physical computer may have multiple
+processors; since the IPP Controller is managing processing tasks, it
+treats each processor independently.  It is up to the system
+configuration if each computer needs to reserve one of its CPUs to
+manage background tasks or if the IPP Controller should attempt to
+send one task per CPU and let the operating system handle the I/O
+load.  The relationship between the different components of the
+Controller is illustrated in Figure~\ref{fig:Controller} and discussed
+below.
+
+\subsubsection{Nodes}
+
+The Controller maintains a table of available processing computers
+(`Nodes') and tracks the status of these Nodes.  Nodes managed by the
+IPP Controller are allowed to be in one of several states, and the IPP
+Controller must interact with it in an appropriate way for each of
+those states.  A Node may be {\tt alive}, {\tt dead} or {\tt off}.
+If the Node is {\tt alive}, it responds to commands from the IPP
+Controller and may be used for tasks subject to other constraints.  If
+it is {\tt dead}, the Node is not responsive and must not be used
+for executing tasks.  The IPP Controller must identify Nodes which
+have died (not responding) and occasionally test them to see if they
+are {\tt alive} again.  Nodes which are {\tt off} are not
+available for tasks and must not be tested.  Nodes may be set to
+the {\tt off} or {\tt dead} states by external subsystems; it is the
+responsibility of the IPP Controller to return a Node to the {\tt
+alive} state if possible.
+
+The IPP Controller must honor requests (normally from the users) to
+change the mode of any computing node on demand between {\tt off} and
+{\tt dead}.  This would normally be done after a Node has been
+rebooted and is released to the IPP Controller for its use.  It must
+also be able to change the list of allowed tasks as requested by
+external commands.
+
+Two example scenarios illustrate the transition between these states,
+and the basic concept of operations for the IPP Controller.  First,
+imagine a computer crashes.  At this point the IPP Controller should
+detect that the Node is no longer responsive and mark it as {\tt
+dead}.  It should occasionally try to re-establish communication with
+the Node, potentially with longer and longer delays between attempts.
+A human could be notified if the Node seems to remain {\tt dead} for a
+very long time.  In another scenario, a person needs to work on a
+Node.  They notify the IPP Controller that the machine is {\tt off},
+perhaps with a prior notification that the machine should be prepared
+to go off.  When work on the machine is complete, it should be placed
+in the {\tt dead} state.  Only when the person is done working and
+testing the machine, and tells the IPP Controller that the machine is
+now {\tt dead} can the IPP Controller attempt to re-start
+communications and re-new processing operations on that Node.
+
+\subsubsection{Node Agents}
+
+When the Controller starts, it attempts to launch a Node Agent on each
+of the available processing Nodes.  Nodes which are not responsive are
+marked as {\tt dead} so they may be re-tried.  A Node Agent runs on
+each of the individual nodes to execute the tasks as directed by the
+Controller.  The Node Agents communicate with the Controller via a
+socket connection.
+
+A Node Agent (which is only running on a Node in the {\tt alive}
+state) may be in one of four modes: {\tt idle}, {\tt busy}, {\tt
+done}, {\tt crash}.  A Node Agent which is {\tt busy} currently has a
+task assigned to it which is executing.  The IPP Controller may only
+assign one task to a Node at a time.  A Node Agent which is in the
+{\tt idle} state may have a task assigned to it.  When the Node Agent
+detects that a tasks has finished, it changes to either the {\tt done}
+or {\tt crash} states depending on the outcome of the process
+execution.  The IPP Controller must also respect a list of task
+restrictions which may require specific tasks to run on specific CPUs
+or exclude specific tasks from specific CPUs.
+
+A task being executed by the Node is run in the UNIX user space as a
+forked process.  The Node Agent must monitor the standard error and
+standard output of the executing task and save them in separate
+buffers.  If the process exits or dies, the Node Agent must detect
+this result and change state appropriately.  The Node Agent must
+respond to various commands from the Controller, as follows:
+
+\paragraph{Report status}
+
+The Node Agent returns its state ({\tt idle}, {\tt busy}, {\tt done},
+{\tt crash}) and the exit status of the current processing task, if
+available.  The reported exit state, if the process has completed
+without crashing, is the UNIX exit state reported by the task: 0--256
+with 0 indicating a successful completion.
+
+\paragraph{Report stdout}
+
+Send and flush the current stdout buffer.  The Node Agent will return
+the complete contents of the stdout buffer via a buffered write and
+flush the buffer when it is finished.  The Node Agent will not accept
+more data on the stdout buffer from the current processing task until
+the send is complete and the buffer is flushed.  The daemon must
+accept all of the buffer output.
+
+\paragraph{Report stderr}
+
+Identical to `report stdout', but for stderr.
+
+\paragraph{Kill task }
+
+The Node Agent should send a kill signal (\code{KILL} or \code{TERM})
+to the current processing task.  When the processing task has exited,
+the Node Agent should set its state to {\tt crash}.
+
+\paragraph{Clear task}
+
+The Node Agent should set its state {\tt idle}.  If a processing stage
+is currently running, it should be killed (\code{KILL} or \code{TERM})
+before the task is cleared.
+
+\paragraph{Start processing stage}
+
+The Node Agent forks a specified command.  The command should be a
+standard UNIX command without command line redirection or
+backgrounding.  The task is run with the same user ID as the Node
+Agent, which is also the same user ID as the Controller.
+
+\subsubsection{Tasks}
+
+The IPP Controller accepts tasks from other IPP subsystems.  The task
+requests include the specific command to be executed and are in the
+form of a UNIX command which could be performed on any of the
+computing nodes.  Any input or output data in the commands must be a
+valid resource regardless of the node on which the task is executed.
+Input and output data resources must be unique where necessary to
+avoid conflicts.  It is the responsibility of the task to wait for
+network lags (ie, NFS delays).  The IPP Controller gives each task a
+unique identifier, which is returned to the requesting entity.  The
+requestor may then use that ID to obtain status information on that
+task or to send control signals to the specific task.
+
+Task requests may specify a desired node for the task execution.  The
+IPP Controller attempts to honor the request if the node is {\tt
+alive}, but will execute it on another node if the requested one is
+{\tt dead} or {\tt off}.  Even if a node is {\tt alive}, the IPP
+Controller will choose another node if the specified task is not
+allowed on the requested node.  In all other cases, the IPP Controller
+waits until the currently executing processes, and processes with
+higher priority, are completed before executing the specified task on
+the requested node.
+
+Task requests may specify an urgency level.  The IPP Controller
+determines the priority of the task on the basis of both the urgency
+and the age of the request.  An executing task must be completed on a
+CPU before any new task is started on that CPU, regardless of
+priority.  The urgency levels range from 0 to 2.  Tasks with an
+urgency of 1 are scheduled whenever they reach the top of the stack.
+Tasks with an urgency of 2 are sent immediately to the top of the
+stack. Tasks assigned a priority of 0 are maintained in the queue and
+never executed.
+
+It may be useful for the Controller to distinguish between tasks
+dominated by I/O and tasks dominated by data processing.  It is
+possible that one of each of these types of tasks may be sent to the
+same node without significantly impacting the system performance.
+Alternatively, it may be necessary to limit a single machine with 2
+CPUs to only one of each of these types of tasks (i.e., one processor
+will be working on I/O while the other is working on processing).
+Such details will be studied by the IfA IPP Team.
+
+The IPP Controller monitors the output streams from the executing
+tasks and the exit status of the tasks.  Each task is associated with
+a log file, to which all output is written.  The status, including the
+exit status, of each task is maintained by the IPP Controller so that
+other subsystems may determine if specific tasks have started or
+completed.
+
+\subsubsection{Controller Interfaces}
+
+The IPP Controller must accept commands from other IPP subsystems.
+These commands include those which govern the processing of specified
+tasks, those which govern the behavior of specific computing nodes,
+and those which request information from the IPP Controller.  The IPP
+Controller must be able to halt the execution of a specified task,
+delete an unexecuted task from the task list, change the priority of
+tasks, and change the requested nodes for tasks.  The IPP Controller
+must also be able to stop the current execution of a task and push it
+to the end of the queue and also change its priority.
+
+The IPP Controller must respond to informational requests regarding the
+collection of machines and their states as well as the collection of
+tasks and their states.  The IPP Controller must monitor the execution
+times of the different tasks and provide summary statistics.  Finally,
+the IPP Controller must respond to three top-level commands: {\tt finish},
+{\tt stop} and {\tt abort}.  When {\tt finish} is requested, no more
+new tasks are accepted on the stack of task, and when all tasks in the
+stack have completed, the IPP Controller must exit.  When {\tt stop} is
+requested, the currently executing tasks must be completed at which
+point the IPP Controller must exit, but tasks remaining in the stack which
+have not been started are flushed.  When {\tt abort} is issued, the
+IPP Controller immediately kills all executing tasks and exits.
+
+The IPP Controller and the IPP Image Server have related needs for
+information from the combined storage-and-processing nodes regarding
+which nodes are available.  It is not yet clear if this information is
+best stored in a single location (either IPP Controller or IPP Image
+Server), which provides the information to other systems on demand, or
+if both systems should maintain the information.  Also, it may be
+necessary to distinguish nodes which are available for processing from
+those that are available to serve data as part of the IPP Image
+Server.
+
+The Controller maintains three tables of processing jobs: pending
+stages, active stages, and completed stages.  The pending stages are
+those which have not yet been performed.  The active stages are those
+currently being performed on one of the remote nodes.  The completed
+stages are those which have finished, either successfully or with an
+error state.  The Controller daemon monitors the collection of remote
+clients and sends them new pending stages when they become free.
+
+The IPP Controller provides a mechanism for users (either other
+programs or humans) to interact with it.  The user interface provides
+commands to check the current processing job queues, the tables of
+successful and failed jobs, to stop or delete jobs, etc.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Scheduler}
+
+\subsubsection{Corresponding Requirements}
+
+The Scheduler must meet the requirements specified in Section 3.4.5 of
+the Pan-STARRS PS-1 IPP SRS (PSDC-430-005).  The design must meet
+requirements 3.4.5.1 - 3.4.5.7.  In particular, the Task / Test
+division is chosen to prevent the Scheduler from blocking while an
+analysis process is performed.  Scheduling requirements will be met by
+defining appropriate Test periods for the different Tasks.
+
+\subsubsection{Overview}
+
+The IPP is responsible for a variety of analysis jobs: processing of
+the science images through several stages; routine assessment of the
+detrend (instrumental calibration) images used in processing the
+science images; construction of replacement detrend images when
+needed; generation of astrometric and photometric reference catalogs
+based on the collected dataset; and the performance of test analysis
+programs.  At any point, decisions need to be made about which of
+these tasks should be performed, based on an analysis of the contents
+of the metadata database, the requirements of the people monitoring
+the IPP, and the near-term observing plans.  The IPP Scheduler is the
+mechanism that assesses these various inputs to guide the decisions
+and initiate the actions.
+
+The IPP Scheduler acts as an interface between several components of
+the IPP and also between the IPP and external agents such as OTIS and
+the users who must monitor the behavior of the IPP.  The IPP Scheduler
+may be viewed as the central brain of the IPP.
+Figure~\ref{fig:Scheduler} illustrates the design of the IPP
+Scheduler.
+
+\subsubsection{Scheduler Tasks and Tests}
+
+The IPP Scheduler performs two types of actions.  'Tasks' are
+long-running programs which are executed by the Controller.  These are
+not only background tasks, but are distributed computing tasks.
+Examples of these include the science analysis tasks (eg, Phase 1, 2,
+3, 4), the Calibration construction tasks, and data copy tasks (such
+as copying images and metadata from the summit system).  'Tests' are
+short-running programs which are used to decide which tasks should be
+run.  Tests should be designed to return immediately ($< 100 ms$) and
+are not run in the background; the Scheduler will block until the test
+is complete.  The IPP Scheduler daemon, which runs continuously,
+performs tests (eg, queries of the IPP Metadata Database, queries of
+OTIS, checks of the IPP hardware status, etc).  Based on these tests,
+the daemon defines appropriate tasks and sends them to the Controller.
+When tasks are completed, their results may be used by the Scheduler
+to update the external systems (update the Metadata Database), or the
+tasks themselves may send their results directly to the Metadata
+Database or other subsystems.  Based on the successful completion (or
+not!) of the tasks, and the new state of entries in the Metadata
+Database, the Scheduler can define new tasks.
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/Scheduler}}
+\caption{ \label{fig:Scheduler} IPP Scheduler}
+\end{center}
+\end{figure}
+
+The IPP Scheduler sends tasks to the IPP Controller for execution.
+While the IPP Scheduler chooses the tasks to be performed, it is the
+IPP Controller's responsibility to manage the specific tasks executing
+on a given processing node.  This division of responsibilities allows
+the different functionalities of the IPP Scheduler and the IPP
+Controller to be isolated and encapsulated.  With this separation, the
+IPP Controller does not information about the details of the tasks it
+executes, while the IPP Scheduler does not need to monitor the
+computer hardware.
+
+Communication between the IPP Scheduler and the IPP Controller is
+bi-directional; the IPP Scheduler sends tasks to the IPP Controller,
+while the IPP Controller informs the IPP Scheduler of the outcome of
+those tasks.  For the PS-1 IPP, the IPP Scheduler and the IPP
+Controller are distinct, interacting software components.  The
+interface mechanisms are described in Section~\ref{sec:interfaces}.
+
+\subsubsection{Task Rules}
+
+The IPP Scheduler takes as input a collection of rules which define
+the dependency of tasks on certain tests.  The IPP Scheduler must
+choose between several types of analysis tasks based on those rules
+and on results of the tests.  The timescale on which different tasks
+(and their related tests) are executed may vary from 10s of seconds to
+hours, days, or even as long as a week.  The list of tasks which the
+IPP Scheduler must decide between, and the relevant timescale, follow:
+\begin{itemize}
+\item moving data from the Summit pixel server ($\sim 30$ second timescales)
+\item running the science analysis stages ($\sim 30$ second timescales)
+\item testing the validity of the current detrend images ($\sim$
+  nightly)
+\item constructing new detrend images ($\sim$ weekly)
+\end{itemize}
+The scheduler may be viewed as a complex state machine.  The goal is
+to design the scheduler so that rules may be specified independently
+from the engine which parses the rules to determine which specific jobs
+to send to the controller.
+
+\subsubsection{User Interface}
+
+The IPP Scheduler shall possess a user interface which allows a human
+operator, or other processes, to monitor the current state of the
+Scheduler.  Users have the option to specify that a particular task or
+set of tasks is of higher or lower urgency (as defined in
+Section~\ref{sec:Controller}) than the norm, or to schedule a
+particular tasks on a different timescale from the basic rule.
+
+The IPP Scheduler defines the operating state of the IPP and shares
+the same set of states:
+\begin{itemize}
+\item active state
+\item interactive state
+\item paused state
+\end{itemize}
+When the IPP Scheduler is in the {\em active state}, it performs the
+most appropriate of all possible tasks at a particular time.  When the
+IPP Scheduler is in the {\em interactive state}, it performs only a
+specific requested action regardless of the outcome of the decision
+trees.  In addition, in the interactive state, the IPP Scheduler must
+only perform the requested actions and not attempt to perform the
+other normally-required actions.  The only exception to this exclusion
+is that, in the interactive state, data is still copied from the
+summit system.  An additional IPP state is the {\em paused state},
+intended for tests or maintenance, in which case the IPP Scheduler
+does not perform even the data copy tasks.  Every task is performed on
+demand by the user.  A user command sets the IPP Scheduler in one of
+these three states, {\em active}, {\em interactive}, and {\em paused}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{System Design : Science Analysis Tasks and Stages}
+\label{sec:AnalysisStages}
+
+This section describes the design of the science analysis stages which
+perform the fundamental image analysis steps of the IPP.  The IPP
+science image processing stages perform analyses on the night-sky
+science images to extract the science data from these images.  These
+consist of: 
+\begin{itemize}
+\item Phase 1, the image processing preparation stage,
+\item Phase 2, the image reduction stage
+\item Phase 3, the exposure analysis stage
+\item Phase 4, the image combination stage.  
+\end{itemize}
+These analysis tasks must process the images in a timely manner so
+that the incoming data stream will not overload the IPP Image Server.
+The decision to execute a specific pipeline for a specific dataset is
+made by the Scheduler, which sends the information to the Controller.
+The Controller executes the pipeline for the data on an appropriate
+machine and monitors the success or failure of the processing stage.
+
+The analysis stages are written as UNIX commands, which may be
+executed by the IPP Controller, or may be executed individually by
+hand.  This option makes testing of the complete analysis system much
+easier because the individual analysis stages may be tested
+independently of each other and the IPP infrastructure.
+
+As part of this design model, the analysis stages have several methods
+for accepting and returning the input and output data and for defining
+optional choices in the analysis.  All of the analysis stages load an
+analysis recipe, which defines the details of that analysis.  The
+recipe includes the location of the data sources (from the metadata,
+from the image headers, from other external files, or supplied
+directly), which steps to employ, and how to assign optional
+parameters.  For example, in the discussion of the Phase 2 analysis
+below, the recipe file may specify {\em if} a bias subtraction should
+be applied, {\em where} to find the overscan region and {\em which}
+bias image, {\em if any}, to apply.  
+
+The recipe is loaded as part of the runtime configuration information
+loaded when the analysis script starts.  Four levels of runtime
+configuration information are defined.  The {\tt site} configuration
+defines values specific to the particular installation of the
+software.  For example, the name of the machine which hosts the
+Metadata Database or a default path for data files could be part of
+the {\tt site} configuration.  Multiple installations or versions of
+the IPP software would need to have separate {\tt site} configuration
+entries.  For example, a version of the IPP installed at the IfA would
+use a different computer for the Image Server from the live IPP
+installation running on the Pan-STARRS cluster.  The {\tt base}
+configuration defines general data sources which may be needed by any
+portion of the IPP.  The list of known telescopes or filters might be
+an example.  The {\tt camera} configuration consists of information
+which defines the parameters relevant to the cameras known by the IPP.
+For example, the default layout of the detectors or the names of
+specific header keyword values would be defined for each camera in a
+camera-specific configuration collection.  Finally, each analysis
+script loads its own recipe.  The location of this configuration
+information may be a collection of configuration files available on
+disk or some subset of the information may be stored in the Metadata
+Database.  The source of these configuration entries can be overridden
+when the script is executed, and individual configuration values may
+also be specified on the command line.  Examples of the recipe and
+other runtime configuration options are given in
+Appendix~\ref{sec:RuntimeConfig}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Phase 1: image processing preparation}
+
+The Phase 1 analysis stage is performed on each science exposure (each
+complete FPA image) to calculate basic astrometric data needed by the
+later stages.  Phase 1 uses the static (pre-determined) telescope
+distortion model and a table of nominal OTA positions and rotations,
+combined with the guide star pixel and celestial coordinates, to
+determine the correct telescope bore-sight, field rotation and
+magnification.  The guide star coordinates are loaded from the
+Metadata database.  These calculations are performed by comparing the
+observed guide star detector coordinates with the known astrometric
+positions of these same stars as reported by an external astrometric
+reference.  The accuracy of the resulting astrometric solution is
+expected to be $\sim 1$ arcsec across the field, sufficient in later
+stages to match the vast majority of astrometric reference stars with
+their detections with minimal effort.
+
+In some circumstances, science images may have no guide stars.  This
+may occur in the Pan-STARRS system if the detectors are not run in OTA
+mode, for example for short snapshot images.  This may also be the
+case if the IPP is being run on non-Pan-STARRS data.  In such a
+circumstance, the Phase 1 stage uses the provided boresight
+coordinates, exposure time, and camera zero-point to predict the pixel
+coordinates of known bright stars expected to be found on the
+detectors.  It then extracts a large box ($\sim$ 30 $\times$
+30\arcsec) around these locations and performs extremely basic object
+detection to determine the detector coordinates of those bright stars
+which are not saturated but which are significantly above the
+background level.  By targeting known locations in the image files,
+only a small amount of data will have to be read.
+
+If the image has invalid coordinates or no detectable bright stars,
+Phase 1 fails and reports a descriptive error.
+
+Given the above astrometric solution, the Phase 1 analysis stage
+constructs a table of the overlaps between the science image to be
+processed and the static sky images that must be constructed.  This
+table will be used to guide the processing of the static sky in Phase
+4.  The overlaps should be generously calculated so that small errors
+in astrometry at Phase 1 will not cause any valid static sky / science
+image pairs to be missed because of the astrometric error at this
+phase.  It is acceptable for a small number of invalid overlaps to be
+identified as these will be excluded in Phase 4.  Static Sky cells
+which do not have sufficient science image overlap ($< 5\%$) need not
+be processed because the few new measured pixels do not add
+significantly to the Static Sky.
+
+\subsubsection{Examples}
+
+Examples of Phase 1 as called from the command line, with different
+types of images:
+
+\begin{verbatim}
+Phase1 -file filename.fits [FPA is single fits file]
+Phase1 -list filename.list [FPA is collection of files]
+Phase1 -imdb ID            [FPA is single file in image server]
+Phase1 -FPA  ID            [FPA identifier from metadata db]
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Phase 2 : image reduction}
+
+\subsubsection{Overview}
+
+Phase 2 processing within the Pan-STARRS image processing pipeline is
+the detrend stage, where the images from the detector are processed to
+remove instrumental signatures.  This analysis is performed on
+individual chips, which can be identified as the data entity which has
+a single, continuous astrometric solution.
+
+Phase 2 consists of the following operations, some of which as noted
+may be skipped by the recipe:
+\begin{itemize}
+\item Load science image
+\item Identify appropriate detrend images
+\item Load detrend images
+\item Form OT kernel
+\item Convolve detrend images with the OT kernel
+\item Bias/dark/overscan subtraction
+\item Mask bad pixels
+\item Trim overscan
+\item Non-linearity correction
+\item Flat-field
+\item Mask diffraction spikes and optical ghosts
+\item Subtract sky
+\item Find and photometer objects in the image
+\item Identify CRs by morphology
+\item Determine PSF model
+\item Improve astrometry
+\item Extract Bright object postage stamps
+\end{itemize}
+The steps are explained in detail below.
+
+\subsubsection{Load Images}
+
+The Phase 2 analysis must load the science image to be analyzed into
+memory, as well as the corresponding metadata (either from the image
+header and/or from the IPP Metadata Database).  It must use the
+metadata for the image, along with information from the processing
+recipe, to determine the appropriate detrend images to be used for
+this analysis.  The Metadata Database stores the information necessary
+to associate a specific science image with one of the registered
+master detrend images for each type.  These images are also loaded by
+the Phase 2 analysis (note that the design of Phase 2 may perform the
+actual loading of pixels in blocks or groups to minimize the memory
+impact).
+
+\subsubsection{Form OT Kernel \& Convolve with Detrend Images}
+
+Science images which have been obtained with Orthogonal-Transfer
+Guiding have had their pixel response smoothed by the image correction
+motion.  For these images, some of the detrend images need to be
+convolved by the same OT kernel, so that they accurately represent the
+effective pixel response.  The detrend images which must be convolved
+include: the flat-field image, the high-spatial-frequency fringe
+images, and dark images, if they are used. The appropriate kernel for
+each cell of an OTA must be determined from the guide star history,
+extracted from the IPP Metadata Database or from the image header.  If
+the OT kernel is not available, but the image metadata notes that it
+should be, the convolution is skipped, with a warning.
+
+The convolution method depends on the size and structure of the OT
+kernel.  If the kernel is small ($< 5x5$ pixels), direct convolution
+may be employed.  If the kernel is large, but may be decomposed using
+Gaussians, then it may be convolved using a decomposition method.
+
+The module convolves each of the dark frame, flat-field, and the
+fringe frame(s) by the OT convolution kernel.  Specific flags in the
+static bad pixel mask are also grown by the outline of the OT
+convolution kernel (see Section~\ref{sec:masks}).
+
+\subsubsection{Bias Correction / Overscan Subtraction}
+
+The image bias must be subtracted. Since different detectors behave in
+different ways, several options for modeling the bias are available.
+The bias is measured from the image overscan region.  The bias
+subtraction method must be capable of subtracting a single constant
+from the complete image, or to subtract a 1-D bias which varies as a
+function along the overscan.  The function used to represent the
+overscan region may be a spline or a Chebychev polynomial derived from
+the data values along the overscan.  The values used to determine both
+the single constant or the inputs to the spline and polynomial fits
+are derived from groups of pixels on the basis of one of several
+statistics, including the sample and robust mean, median, and modes.
+In the case of a single constant, all of the overscan pixel values are
+used in the calculation of this statistic.  In the case of the 1-D
+functional representation, the input values to the fit must represent
+the coordinate along the overscan, with the statistic derived from the
+pixels in the perpendicular direction at each location.
+Sigma-clipping on the input data values must be an option.
+
+\subparagraph{Flag bad and saturated pixels}
+\label{sec:masks}
+
+A static bad pixel mask is used to identify pixels which are known to
+be bad in the camera.  This mask is then processed with the science
+image. Bad pixels which are charge traps are grown by the extent of
+the OT convolution kernel.  Bad pixels above a charge trap (i.e.\ bad
+columns) must not be grown, since they were not affected by pixel
+shifting, but only became bad at read-out.
+
+Pixels which are saturated in the A/D converter, or with a signal
+level at which the response is excessively non-linear, must also be
+masked, and this area must be grown by an additional pixel to mask
+excess charge spillover.
+
+The bad pixel mask is carried with the science images.  Different bits
+are set to identify different reasons for masking the pixel.  Flags
+are required for at least each of the following pixel attributes:
+
+\begin{itemize}
+\item The pixel is a charge trap;
+\item The pixel is a bad column;
+\item The pixel is saturated in the A/D converter;
+\item The pixel is non-positive in the flat-field;
+\item The pixel is part of a row that has excess noise; and
+\item The pixel is determined to be a cosmic ray, based on its
+morphology.
+\end{itemize}
+
+\subsubsection{Trim}
+
+The image is trimmed to remove the non-imaging pixels, such as the
+overscan and any pre-scan pixels, along with those pixels near the
+edges that have been compromised due to OT operation.  The definition
+of the imaging area of the detector is determined from the camera
+configuration data or from the metadata associated with the image,
+with the choice a user-configurable option.
+
+The input science and mask frames are additionally trimmed by the
+extent of the OT convolution kernel in each direction ($+x$, $-x$,
+$+y$, $-y$).  Within the PSLib image handling functions, the trim
+function is a virtual operation which simply marks the boundaries of
+the trimmed image but does not remove the corresponding memory.  This
+allows the later corrections to work with untrimmed correction images
+and still apply the correct pixels.  At the end of Phase 2, the only
+the trimmed portions of the output images are written out to disk.
+
+\subsubsection{Non-Linearity Correction}
+
+If required, the science image (after bias correction) must be
+corrected for the effects of non-linearity through a provided
+polynomial fit to the pixel data values or a numeric lookup table as a
+function of pixel flux.  The choice to apply the correction must be
+set by the analysis recipe.
+
+\subsubsection{Flat field Correction}
+
+The science image (after bias correction and non-linearity correction)
+must be corrected for sensitivity variations as a function of
+position, dividing by a flat-field image.  The mask is also modified
+for zero-valued pixels in the flat-field image.
+
+\subsubsection{Sky \& Fringe subtraction}
+
+After the science image has been flat-fielded, the flux contribution
+of the sky (from both continuum emission and the line emission that
+causes fringing) must be subtracted from the image.  The subtraction
+needs to remove background (technically, foreground) variations which
+are not celestial but generated in the atmosphere or by more localized
+scattering.  This background should include the contribution from the
+zodiacal light.  This background subtraction does not address the
+artifacts generated by bright stars: bleeding columns, ghosts, or
+other localized reflection effects.  This process also produces a
+super-binned image of the background map which may be used as a
+debugging diagnostic.
+
+This analysis step temporarily masks objects on the image using the
+astrometric solution from the boresight and fits for the sky
+background, consisting of a polynomial to model the continuum, and the
+fringe frame(s) to model the fringes from sky emission lines.  If the
+concentration of objects in the image is too high to reliably fit the
+sky background, the background solution from an exposure close in time
+and airmass to the current object image is used.  The output is the
+sky-subtracted object image.
+
+\subsubsection{Detect and Measure objects}
+
+After the image have been processed by the preceding steps, the Phase
+2 analysis performs a basic object detection analysis.  Objects on the
+flat-fielded object image are found, and general parameters are
+measured.  Object detection is performed at several stages by the IPP,
+with different object parameters measured in each case.
+Table~\ref{tab:APdetections} gives a list of the different detection
+stages and the object parameters measured for those stages.  For the
+Phase 2 analysis, the object parameters are: the object centroid and
+the position covariance matrix, the instrumental PSF magnitude and
+error, local background level and error, a measurement of the
+star-galaxy separation, and a measurement of the object shape
+($\sigma_x, \sigma_y, \theta$).  The detection threshold must be
+configurable, and be a function of the average background flux or the
+image noise map.  Minimal object classification must be performed to
+distinguish objects which are consistent with a single PSF, objects
+which are inconsistent, and objects which are saturated.  The
+resulting collection of detected objects are saved in the AP Database
+along with the relevant image metadata (\ie filter, exposure time,
+etc).  In addition, this process constructs a model of the
+point-spread-function (PSF) as a function of position in the image.
+This PSF model is saved as part of the image metadata.
+
+\subsubsection{Identify CRs by morphology}
+
+Charged particles in the detector frequently cause features which do
+not have the morphology of astronomical objects.  In a well-sampled
+image, these may be easily identified by the sharpness of the image.
+In a near critically-sampled image, these `cosmic rays' may be
+indistinguishable from stellar objects.  This analysis step makes
+morphological identification of cosmic rays if the imaging data is
+sufficiently well sampled.  The identified cosmic rays are masked with
+a configurable growth factor so that additional pixels beyond the
+detected pixels in the feature are also masked.
+
+\subsubsection{Perform Astrometry}
+
+The detected objects are matched with known astrometric reference
+objects, using reference object coordinates which have been adjusted
+for proper motion.  The matches are then used to improve the
+astrometric parameters for the individual OTAs.  The OTA astrometric
+parameters which are determined may include terms up to 3rd order in
+position, though the terms which are actually fitted are
+user-configurable.  The Cell astrometric parameters are not allowed to
+vary at this stage.  The fit must be robust, rejecting outlier matches
+(either stars with poorly determined proper motion or spurious
+matches).  The resulting astrometric solution is consistent across the
+OTA field to within 1.0 arcsec.
+
+\subsubsection{Perform Photometry}
+
+If possible (if a local photometry reference exists), the Phase 2
+analysis determines a photometry zero point for each image.  To do
+this, it extracts the appropriate reference objects (from the AP
+Database) and matches the stars between the two samples.  The
+zero-point is derived on the basis of a static atmospheric absorption
+model (eg, linear airmass slope).
+
+\subsubsection{Bright object postage stamps}
+
+The IPP must have the capability of extracting regions surrounding a
+specified subset of objects from the flattened images.  These postage
+stamp images must be saved for additional use by client science
+pipelines.  The identification of these objects must be on the basis
+of a set of rules applied to the object's magnitude and position.  The
+postage stamps are not restricted in shape to simple rectangles, but
+may represent more complex regions.  They are written to the Image
+Server.  The outputs are these postage stamps and pixel masks, which
+are sent to the IPP Pixel Server.
+
+%\begin{figure}
+%\begin{center}
+%\resizebox{6in}{!}{\includegraphics{pics/phase2}}
+%\caption{ \label{fig:phase2} Phase 2 dataflow - this diagram is old: update}
+%\end{center}
+%\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Phase 3 : exposure analysis}
+
+The Phase 3 system operates on the combined Phase 2 results from an
+FPA to determine improved solutions for the image calibrations and to
+provide the parameters needed by Phase 4.  The Phase 3 output is saved
+by the Metadata Database, and consists largely of improved values of
+the calibrations already determined by Phase 2.  The analysis
+performed by this pipeline consists of:
+
+\begin{itemize}
+\item improved astrometric solution based on comparison between
+  objects in the images and the astrometric reference.
+\item improved background model based on the full telescope field, or
+  fields.
+\item photometric solution based on comparison to photometric
+  standards
+\item FPA-wide PSF analysis
+\end{itemize}
+
+In the Phase 2 analysis, the astrometric solutions were determined
+independently for each chip.  These solutions are limited by the
+assumption of a static distortion and by the accuracy of the
+astrometric reference.  In the phase 3 analysis, the astrometric
+solutions of the complete FPA images are improved by combining the
+astrometry for all chips.  The astrometry model consists of a
+projection of the celestial coordinates to the telescope boresite
+center, followed by a rotation to the average rotation of the FPA and
+adjustment for the central plate scale.  The free parameters in this
+stage are the boresite coordinates ($R_o, D_o$), the field rotation
+($\theta_o$) and the plate scale ($\rho_o$), and are fitted in Phase
+1.  These tangent plane coordinates are then distorted by the optical
+distortion model, consisting of $N^{\rm th}$ order polynomials in two
+dimensions.  Finally, the focal plane coordinates are mapped to the
+individual chip coordinates, including the chip position and rotation,
+as well as possible higher order terms representing warping of the
+individual detectors.  A first pass at the chip positions is
+calculated in Phase 2, while the complete set of parameters is fitted
+as a whole during Phase 3.  The fitting process is determined in a
+robust and stable way by fitting the gradient of the distortion as a
+function of field position, removing the degeneracy of the distortion
+coefficients on the chip position parameters.
+
+In the Phase 2 analysis, the background is determined based only on
+the available sky in a single chip.  However, the background
+structures are normally correlated on the scale of the FPA, so an
+improved background solution can be determined by combining the
+information from many chips.  A high-order polynomial model of the
+background may be used and subtracted from all chips.  
+
+The Phase 3 photometric improvement is made by comparing the
+photometered objects from Phase 2 with the corresponding objects in a
+local reference catalog.  This analysis may only be performed if a
+local reference is available.  Note that improved relative photometry
+calculations may be performed in the absence of a reference catalog on
+the basis of image overlaps in the AP Database {\em after} the
+detections have been added to the Database.  Such a relative
+photometry analysis is outside the scope of Phase 3 and will likely be
+performed as an independent analysis process.  Given the presence of a
+local photometry reference, the zero point variations across the field
+may be measured, and possibly modeled.  If the zero-point variations
+are excessive, then the image is marked as non-photometric by the
+analysis.
+
+In the Phase 4 analysis, the $N$ FPA images are optimally combined to
+create a single image of the sky with bad-pixel and cosmic-ray
+rejection.  This combination requires the calculation of a set of PSF
+kernels to convert each of the input images to a single, common PSF.
+These PSF kernels are determined from the per-chip PSFs measured in
+Phase 2.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Phase 4 : image combination}
+
+\subsubsection{Overview}
+
+Phase 4 is the image combination stage, in which multiple images of
+the same portion of the sky are merged and confronted with the static
+sky image.  Phase 4 operates on the smallest data unit of the static
+sky, the sky cell, along with the associated pixels from a collection
+of images which have been processed through phases 1--3.  The size and
+exact representation of a static sky cell are yet to be determined.
+The working concept is that the static sky cells contain roughly the
+same number of pixels as an OTA (4k x 4k) and represent a portion of a
+local tangent plane projection.  In order to meet the image
+degradation requirements, the pixel scale of the static sky is planned
+to be 0.2\arcsec, somewhat smaller than the 0.3\arcsec\ raw image
+pixel scale.
+
+For each sky cell, the corresponding pixels are extracted from the
+exposures being processed and mapped to the projection of the sky
+cell. The pixels from the multiple input processed images are combined
+into a single, cleaned image.  Outlier pixels may be optionally
+rejected at this stage (optionally, since moving objects will be
+rejected in images obtained over a wide range of times).  This image
+is then confronted with the static sky cell data to produce a
+difference image.  Residual objects in the difference image above a
+threshold are detected and excised from the original cleaned image.
+The remaining pixels are added to the existing static sky image.
+Object detection must be performed on the difference ($P4\Delta$) and
+cleaned ($P4\Sigma$) images.
+
+\subsubsection{Image Warping and Matching}
+
+The analysis first maps the detector images to the sky cell using the
+specified linear transformations, then combines the images with strong
+rejection criteria and uses the combined sky cell image to identify
+artifacts in the original detector images.  It is desirable that the
+artifacts are masked in the detector plane (i.e.\ before mapping to
+the sky cell) so that they are not smeared out by the mapping;
+alternatively, the CR mask needs to be grown by an additional pixel.
+The mapped and masked detector images are then optimally combined
+using the specified weighting.  Both sets of combinations use the
+photometric calibration for the images to set the relative scales of
+the input images.  The limiting magnitude for the combined sky cell
+image should also be estimated.
+%
+
+\subsubsection{Static Sky Subtraction}
+
+The corresponding static sky image is subtracted from the combined
+image stack.  In this step, it is necessary to match the image kernel
+between the input image and the static sky image.  This will be done
+by solving for a best-fit image kernel which minimizes the difference
+image using a technique equivalent to the Allard-Lupton method.  One
+modification for the IPP is to represent the kernel as a combination
+of independent pixels rather than represent the components of the
+image difference kernel as a combination of Gaussians.  This method
+also automatically determines a photometric match between the static
+sky image and the input science image.
+
+\subsubsection{Object Detection and Measurement}
+
+Objects in the difference image are detected and a specific set of
+object parameters are measured from these detections.
+Table~\ref{tab:APdetections} gives a list of the different detection
+stages and the object parameters measured for those stages.  For the
+Phase 4 difference image (P4$\Delta$), the measured object parameters
+consist of: the object centroid and the position covariance matrix,
+the instrumental PSF magnitude and error, local background level and
+error, a measurement of the star-galaxy separation, and a measurement
+of the object shape ($\sigma_x, \sigma_y, \theta$).  The detection
+threshold must be configurable, and be a function of the average
+background flux or the image noise map.  Detections must be performed
+for both positive and negative (static sky only) sources.  Minimal
+object classification must be performed to distinguish objects which
+are consistent with a single PSF, objects which are inconsistent, and
+objects which are saturated.  The resulting collection of detected
+objects are saved along with the relevant image metadata (\ie filter,
+exposure time, etc).
+
+Objects in the cleaned, summed image are detected and a specific set
+of object parameters are measured from these detections.
+Table~\ref{tab:APdetections} gives a list of the different detection
+stages and the object parameters measured for those stages.  For the
+Phase 4 summed image (P4$\Sigma$), the measured object parameters
+consist of: the object centroid and the position covariance matrix,
+the instrumental PSF magnitude and error, local background level and
+error, a measurement of the star-galaxy separation, a measurement of
+the object shape ($\sigma_x, \sigma_y, \theta$), the Petrosian radius,
+magnitude, axis ratio, and angle; and the S\'ersic radius, magnitude, axis
+ratio, angle, and parameter $\nu$.  The detection threshold must be
+configurable, and be a function of the average background flux or the
+image noise map.  Minimal object classification must be performed to
+distinguish objects which are consistent with a single PSF, objects
+which are inconsistent, and objects which are saturated.  The
+resulting collection of detected objects are saved along with the
+relevant image metadata (\ie filter, exposure time, etc).  In this
+measurement, objects at known positions will also be measured even if
+they have not been detected.
+
+Objects which are detected in both of the Phase 4$\Sigma$ and Phase
+4$\Delta$ images are saved to the AP Database, along with the relevant
+image metadata (\ie filter, exposure time, etc).  In the process of
+adding these objects to the database, the transients which are
+correlated with previous detections of an object (and those which are
+not) will automatically be determined.  A subset of these transient
+objects are sent, along with their associated metadata, to the MOPS
+and other preferred science client pipelines.  
+
+\subsubsection{Static Sky Update}
+
+It is essential that the static sky image (which may have been
+painstakingly accumulated over many months) not be corrupted by adding
+in transient sources, or data that is of suspect quality (due, e.g.,
+to an error upstream in the processing).
+
+Object analysis of the static sky images is {\em not} a part of the
+Phase 4 analysis.  This processing is envisioned to take place
+relatively infrequently (perhaps weekly or even monthly) and is
+scheduled as a separate analysis task, probably run during the day at
+a time when the computing infrastructure is not under significant load.
+
+%\begin{figure}
+%\begin{center}
+%\resizebox{6in}{!}{\includegraphics{pics/phase4}}
+%\caption{ \label{fig:phase4} Phase 4 dataflow}
+%\end{center}
+%\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{System Design : Calibration Image Processing}
+
+The Calibration Analysis Stages construct calibrations from the
+relevant input data.  Some of these combine multiple raw input images
+together, after processing, to create a high-quality high-signal
+master calibration image.  Some of the calibrations are used to
+correct other calibrations.  Each of the calibration stages must also
+provide the tools to test the quality of the input data against
+existing master calibration data and to test the consistency of
+multiple measurements of the calibration.
+ 
+The Calibration analysis stages may be performed on whatever
+timescales are appropriate and necessary to maintain the quality and
+relevance of the calibration images.  The specific calibration data
+which must be constructed in the calibration analysis stages is listed
+below.
+
+The IPP must generate basic calibration images using the raw bias,
+dark, and flat-field (dome or twilight) images obtained by the
+telescope as the input.  The analysis of these images requires
+relatively simple stacking of the input set of images.  Outlier
+rejection, both of complete input images as well as pixels within the
+input stack, must be performed.  In addition, each type of image
+requires an appropriate normalization which may depend on the data
+levels in other detectors in the input set.  Each of these calibration
+stages must be able to determine from the input stack if the relevant
+calibration image needs to be updated and perform an initial test to
+see which input images are consistent and valid.
+
+\subsection{Bias Images}
+
+Bias images may be needed to correct for structure in the bias.  The
+IPP must have the capability of constructing a master bias image from
+a stack of raw bias frames.  The input bias images, representing
+offsets from the overscan level, are processed by subtracting the
+overscan, including 1D structure if needed.  
+
+The master bias frame construction uses outlier image and outlier
+pixel rejection to construct a single high-quality bias frame.  The
+statistic used to determine pixel values from the input stack can be
+set by the user to be one of the following: the sample mean, median,
+and mode, robust mean, median, and mode, and the clipped mean and
+median.  Testing of the input images consists of constructing residual
+images, in which the master bias is applied to the input images.
+These images may be included or excluded from an additional iteration
+of the stack on the basis of their pixel-to-pixel statistics.
+
+\subsection{Dark Images}
+
+Dark images may be needed to correct for structure in the dark
+current.  The IPP must have the capability of constructing a master
+dark image from a stack of raw dark frames.  The input dark images are
+first corrected for the bias using whatever method is appropriate for
+the science images.  Master dark frames depend on their exposure time.
+As such, the input dark frames must have a limited range of exposure
+times, and the output dark frame includes the exposure time as part of
+its associated metadata.  
+
+The master dark frame construction uses outlier image and outlier
+pixel rejection to construct a single high-quality dark frame.  The
+statistic used to determine pixel values from the input stack can be
+set by the user to be one of the following: the sample mean, median,
+and mode, robust mean, median, and mode, and the clipped mean and
+median.  Testing of the input images consists of constructing residual
+images, in which the master dark image is applied to the input images.
+These images may be included or excluded from an additional iteration
+of the stack on the basis of their pixel-to-pixel statistics.  A
+collection of master dark frames with a range of exposure times are
+used to determine the scaling of the dark frame as a function of
+exposure time.
+
+\subsection{On-Off Dark Images for Light Leaks}
+
+A type of image which may be necessary for calibrations will be pairs
+of images taken at night with the shutter closed with and without the
+dome shutter closed.  Such a pair of images can be used to determine
+any light-leak in the camera which may contribute additional flux
+across the mosaic.
+
+\subsection{Flat-Field Images}
+
+Master flat-field images must be constructed from a collection of
+input flat-field images.  The input flat-field images may be obtained
+from any of the standard sources: the dome, the twilight sky, and the
+night-time sky.  The choice of flat-field input image must be
+determined experimentally from observations during the commissioning
+phase of the telescope.  The IPP flat-field construction system must
+be capable of handling any of these sources.  
+
+An appropriate set of input images is selected on the basis of their
+flux levels, time of observations, and the observing conditions.  The
+input flat-field images are processed (bias and dark corrected if
+needed) and the resulting images are stacked.  The master flat-field
+construction uses image and pixel outlier rejection to construct a
+single high-quality master flat-field frame.  The statistic used to
+determine pixel values from the input stack can be set by the user to
+be one of the following: the sample mean, median, and mode, robust
+mean, median, and mode, and the clipped mean and median.  Testing of
+the input images consists of constructing residual images, in which
+the master flat-field image is applied to the input images.  These
+images may be included or excluded from an additional iteration of the
+stack on the basis of their pixel-to-pixel statistics.
+
+\subsection{Mask Images}
+
+Preliminary bad-pixel mask images are generated on the basis of
+comparison between raw flat-field images and a cleaned, stacked
+master.  The mask creation system accepts a collection of flat-field
+images and identifies pixels which are consistently poorly flattened.
+Pixels which are under-responsive are also identified as pixels to be
+masked.  
+
+\subsection{Sky \& Fringe Frames}
+
+Fringe-correction frames must be generated to remove the fringe
+pattern caused by thin-film interference in the top layers of CCDs,
+particularly in the redder passbands.  Fringe correction frames may be
+constructed on the basis of observations of the night-sky in the
+appropriate filters or on the basis of dome fringe lamp observations.
+The choice of the appropriate source will be determined experimentally
+on the basis of data obtained during the commissioning phase.  The IPP
+must be capable of handing either source.  The images are first
+flattened to remove the pixel-to-pixel sensitivity variations of the
+detector.  The combination of multiple input fringe frames may not be
+simply stacked since the amplitude of the fringe pattern varies
+independently of other variations in the image.  The amplitude of the
+fringe pattern in the input frames is measured and the images scaled
+to normalize the fringe amplitude to a consistent range (-1 to +1) for
+all input images before they are combined with one of the standard
+combination statistics (mean, median, mode, etc).  The quality of the
+input frames is tested by flattening the input image and applying the
+master fringe-frame.  The resulting residual image statistics are used
+to select or exclude specific input images.
+
+\subsection{Shutter Correction Map}
+
+Shutter correction map images may be generated based on the timing
+measurements of the shutter itself, or on the basis of dome-flat
+images of decreasing exposure times down to the shortest available
+exposures.
+
+\subsection{Low-k Sky Models}
+
+Large-scale background structure in images which is not caused by
+thin-film interference must also be detected and corrected.  Models of
+this background structure may be a necessary input to the correction
+procedure.  The IPP must have the capability of generating image
+models of the large-scale structure patterns observed with the
+telescope
+
+\subsection{Flat-Field Correction Frame}
+
+Flat-field images, whether constructed from the dome, twilight, or
+night-sky images, do not perfectly correct the detector response in a
+consistent fashion across the full field of the camera.  The IPP must
+have the capability of generating flat-field photometric correction
+frames on the basis of the measured photometry of objects which are
+moved to a variety of locations on the detector in a sequence of
+images.  The flat-field correction frames analysis stage makes use of
+targeted observations following a specified dither pattern, and
+extracts the photometered objects from the AP Database to determine
+the necessary photometric corrections.  The resulting image is applied
+to the master flat-field image.  Testing of the correction is
+performed by applying the correction to the basic master flat-field
+image, applying that flat-field image to the dithered photometry
+observations, and performing the object detections.  Comparison of the
+photometry of individual stars at different locations on the mosaic
+will demonstrate the consistency of the flat-field image.
+
+\subsection{Non-Linearity Correction}
+
+The IPP must have the capability of constructing a correction for
+non-linearity in the detectors.  These frames are constructed from
+exposures of a uniform source with a range of exposure times.  The
+non-linearity correction frames provide polynomial correction
+coefficients or a lookup table describing the correction.  There is
+likely to be a single non-linear correction for each OTA detector, or
+potentially for each Cell.  The IPP must handle these two cases.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{System Design : Miscellaneous Tasks}
+
+This section discusses additional operations which are performed by
+the IPP but which do not fall under the analysis of the science images
+or the creation of the calibration images.
+
+\subsection{Retrieval}
+
+The retrieval stage simply retrieves images from an external source
+(ordinarily OTIS at the Summit, but it could conceivably be some other
+external source) and store it in the Image Server.  
+
+\subsection{Static Sky Analysis}
+
+The IPP is responsible for performing object detection and analysis on
+the static sky.  This analysis is performed continuously (every day or
+week) on those portions of the sky within 15\degree\ of the sun.  In
+this analysis, the object measurement is much more detailed than those
+performed in the real-time analysis.  The currently envisioned
+parameters to be measured for every object are listed in
+Table~\ref{tab:APdetections}.  The parameters include the object centroid
+and the position covariance matrix, the instrumental PSF magnitude and
+error, local background level and error, a measurement of the
+star-galaxy separation, a measurement of the object shape ($\sigma_x,
+\sigma_y, \theta$), the Petrosian radius, magnitude, axis ratio, and
+angle; the S\'ersic radius, magnitude, axis ratio, angle, and
+parameter $\nu$, and a collection of annular aperture flux
+measurements, all of which are also measured for the P4$\Sigma$
+images.  In addition, the galaxy-shape parameters $Gamma_1 \&
+\Gamma_2$, along with the complete `polarization' terms are measured,
+as well as a collection of annular aperture flux and variance
+measurements.  Another unique feature of the static sky analysis is
+that the object detection may be performed simultaneously on all
+filters, in which case the locations and other parameters may be more
+strongly constrained by simultaneously fitting between all bands.  The
+analysis to be performed may be substantially more complex than the
+real-time analysis because of the relatively low data rate.  Instead
+of needing to process thousands of images per night ($\sim 350$Mpix
+per second), it is only necessary to process the complete sky in a
+year, or an average rate of $\sim$2 Mpix per second, or $< 1$\% of the
+object analysis in the other analysis stages.
+
+\subsection{AstroRef: Astrometric Reference Catalog creation}
+
+\tbd{needs to be fleshed out substantially}
+
+This processing stage shall use many observations over a given time
+period to fit a consistent global astrometric solution, resulting in a
+high quality and internally-consistent astrometric catalog that may be
+published.
+
+\subsection{PhotoRef: Photometric Reference Catalog creation}
+
+\tbd{needs to be fleshed out substantially}
+
+This processing stage shall use many observations over a given time
+period to fit a consistent global photometric solution, resulting in a
+high quality and internally-consistent photometric catalog that may be
+published.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Software Hierarchy}
+
+In order to facilitate testing and development, and to encourage
+flexibility, the IPP will be built in a layered fashion.  The lowest
+level functions will be written in C and collected together into a
+Pan-STARRS library.  These library functions will be used to write
+more complex modules.  The modules will be written in C but will make
+use of SWIG to make their functionality available within other
+languages.  In particular, the modules can be tied together with a
+program in C or through the use of a high-level language such as Perl,
+Python, or Tcl employing the SWIG interfaces.  For the high-level
+functions in the operational system, the IPP will make use of Perl as
+the scripting language to provide the required flow-control to tie the
+modules together.
+
+This approach satisfies the requirement that complicated low-level
+analysis steps run fast, while preserving flexibility for coding the
+high-level wrappers for which the speed requirements are not so
+stringent.
+
+\subsection{External Libraries}
+
+Pan-STARRS will employ several external libraries to save duplicating
+functionality that is already available.  These external libraries
+will be wrapped by the Pan-STARRS Library, insulating the project from the
+implementation details of the external libraries.  Examples of the
+external libraries are FFTW and SLALib.
+
+\subsection{Pan-STARRS Library}
+
+The Pan-STARRS Library will consist of C structures describing the
+basic data types needed by the IPP and C functions which perform the
+basic data manipulation operations.  Note that a subset of the library
+functions will be provided with SWIG interfaces as well to allow for
+their use in the creation of the processing stages.  Examples of the
+Pan-STARRS Library are Fourier transforms and transforming between
+pixel and celestial coordinates.  The details of the Pan-STARRS
+Library are specified in the document Pan-STARRS IPP PSLib
+Supplementary Design Requirements Specification (PSDC-430-007), which
+also addresses coding requirements detailed in the IPP PS-1 SRS
+(PSDC-430-005), Section 3.3.
+
+\subsection{IPP Modules}
+
+The IPP analysis stages are broken down into modules which represent
+specific functional operations.  The modules will be written in C
+using the Pan-STARRS Library functions and will be grouped into a
+Pan-STARRS Module Library.  The modules will be provided with SWIG
+interfaces to all public APIs for their use in processing stages.
+Examples of modules are overscan subtraction and image combination.
+Some modules (e.g.\ find objects on an image) will be used by multiple
+stages.  The details of the Pan-STARRS Modules are specified in the
+document Pan-STARRS IPP Modules Supplementary Design Requirements
+Specification (PSDC-430-012), which also addresses coding requirements
+detailed in the IPP PS-1 SRS (PSDC-430-005), Section 3.3.
+
+\subsection{IPP Stages}
+
+The major IPP processing tasks are organized into stages, which
+consist of multiple modules.  Each stage represents a collection of
+complex operations performed on a single data entity.  Each stage
+therefore represents the maximum amount of effort which can be
+performed in serial without interaction between parallel threads.  The
+stages will be written in Perl, linking the modules together.
+Examples of stages are Phase 2 (detrend images) and Phase 4 (combine
+images from multiple telescopes and search for transients).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Interfaces}
+\label{sec:interfaces}
+
+\subsection{Internal Interfaces}
+
+Internal interfaces consist of interactions between the analysis
+scripts and the IPP Metadata Database, Image Server or AP Database.
+There are also interfaces between the IPP Scheduler, Controller, and
+the Metadata Database.  
+
+The science and calibration image processing pipelines make requests
+for images from the Image Server, metadata from the Metadata Database,
+and push their results back onto the Image Server and Metadata
+Database.  The Scheduler specifies analysis tasks and sends them to
+the Controller, and determines the next action based on the contents
+of the Metadata Database.  The various subsystems specify the API for
+client / server interactions, and are discussed in their individual
+section.  Commands will be sent using either text-based commands, SOAP
+or an equivalent protocol.  The format of the exchanged data may be in
+one of several forms discussed below.
+
+FITS Images will be used to transport images between the components of
+the IPP.  Non-standard FITS images representing triangular collections
+of pixels may be used to store the static sky images.
+
+FITS Tables will be used to store and transport tabular data,
+especially large queries from database subsystems.  The Auto-coding
+technique discussed in Appendix~\ref{sec:AutocodeIO} is used to define many
+different table interactions.
+
+XML files will be used to store and transport data which is not
+well-suited to the rectangular form of FITS Tables.  Hierarchical data
+concepts and variable-length structures fall in this class.  Examples
+include mosaic astrometry description information and configuration
+information.
+
+SQL queries and C wrappers of SQL queries will be used as the direct
+interface to the databases.
+
+Within IPP and Pan-STARRS in general, process-to-process communication
+will be defined through auto-coded APIs which support a limited and
+validated communication protocol.  The APIs will be coded based on a
+table which defines the allowed command set and the grammar to be
+used.  This mechanism will allow a single code block to define
+inter-process communication methods for many Pan-STARRS subsystems,
+including, within the IPP, the Scheduler-Controller communications.
+
+\subsection{External Interfaces}
+
+This subsection describes the interfaces between the IPP and other
+Pan-STARRS systems and the external clients.  The interfaces are
+illustrated in Figure~\ref{fig:overview}.  
+
+\subsubsection{OTIS}
+
+The IPP Scheduler may query OTIS for a list of new images and
+metadata.  The locations of those images in the Summit Pixel Server is
+sent back as a table, and all metadata may be sent to the IPP as a
+collection of FITS Tables.  The IPP also may send quality assessment
+information for each FPA and major frame by writing out FITS tables
+and notifying OTIS of the presence of the new tables.  
+
+\subsubsection{Camera}
+
+Images are pulled from the Summit Pixel Server, part of the Camera
+team's purview.  The locations of the files are sent by OTIS.  IPP may
+grab these via {\tt http} commands or via {\tt NFS} or another network
+file exchange protocol.  The IPP notifies OTIS (and Camera) when each
+image has been received.
+
+\subsubsection{PSPS}
+
+Data will be sent to PSPS from the IPP as part of a daily or weekly
+analysis process on the Static Sky.  The data will be pushed from the
+IPP to PSPS when they are available.  The data to be transfered
+include:
+\begin{itemize}
+\item Static Sky images - to be transferred as FITS images or
+  FITS triangular image regions.
+\item Postage Stamps - to be transferred as FITS images.
+\item Metadata tables - to be transferred as FITS tables
+\item Detections \& Object associations - to be transferred as FITS tables.
+\end{itemize}
+
+\subsubsection{MOPS}
+
+Data will be sent to MOPS from the IPP as part of the Phase 4
+analysis.  The data will be pushed from the IPP to MOPS when they are
+available.  The data to be transfered include:
+\begin{itemize}
+\item Image Metadata tables - to be transferred as FITS tables
+\item Orphaned Detections - to be transferred as FITS tables
+\end{itemize}
+
+\subsubsection{Other Preferred Client Science Pipelines}
+
+These cannot be completely defined until the Clients are defined and
+their requirements are specified.  The expectation is that the data
+products will be the same as for the MOPS.  The data will be pushed
+from the IPP to the Client Science Pipeline when they are available.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Computer Hardware}
+\label{sec:Hardware}
+
+\subsection{PS-1 Cluster Design}
+  
+The PS-1 IPP computer system is designed as a cluster of 'fat bricks':
+computers with both processing power and large amounts of local disk
+storage.  These computers are large rack-mount boxes with space for
+10s of disks (24 and 36 disk cases are available) and a motherboard
+with two CPUs and two Gig-E ethernet ports.  One set of machines is
+specified for storage and processing of the individual OTAs up through
+Phase 2 (the `OTA nodes'), another set of machines are specified for
+storage of the Static Sky and processing of data from Phase 3 and
+Phase 4 (the `Sky nodes').  Other machines will be necessary to
+support the Metadata DB and the AP DB.  
+
+The IPP PS-1 SRS (PSDC-430-005) specifies the processing throughput
+requirements for the IPP.  Benchmark tests of the IPP processing
+algorithms have been used to drive the design needed to achieve the
+throughput requirements.  The details of this study are presented in
+the IPP Computational Challenge (PSDC-400-006), summarized here.  The
+analysis measures the processing time (excluding I/O) for both Phase 2
+and Phase 4 on an Intel Pentium 4 processor, and expresses the
+processing time in GHz-seconds, under the assumption that a machine
+with the same architecture and twice the processor speed will perform
+the same analysis in half the time.  This is probably a valid
+assumption within a limited range on hardware using the same
+architecture.  Independent tests show that 32-bit Pentium processors
+perform somewhat slower (up to a factor of 2) than equivalently rated
+64 bit Opteron processors.  This discrepancy makes the measured
+numbers somewhat conservative, and compensates for the simplified
+analysis performed.  The benchmarks show that the Phase 2 analysis
+takes 12000 GHz-seconds for a complete major frame (4 FPAs) while the
+Phase 4 analysis takes 7800 GHz-seconds for the same major frame.
+
+The total data I/O required for each processing node, both locally to
+disk and across the network to other machines, has also been measured.
+These numbers in turn depend on whether the data is optimally stored
+on the OTA nodes (raw images matched to their calibration images) or
+if the data are randomized across the storage nodes.  There are also
+differences in the analysis for the number of bits per pixel and the
+number of calibration images used in the processing.  For PS-1, the
+`minimal' data set is appropriate, resulting in a total Phase 2 I/O of
+21 GBs per major frame and a total Phase 4 I/O of 36 GBs.  The
+randomized numbers are used as a conservative estimate, under the
+assumption the network, not local disk access, is the dominant I/O
+bottleneck.
+
+The analysis assumes each CPU (rated at 2.2 GHz) is associated with
+one RAID array (maximum throughput 110 MB/sec) and one network
+controller (maximum throughput 70 MB/s). In this case, given the CPU
+load and I/O throughput above, Phase 2 will require a total of 190
+seconds of I/O and 5500 seconds of processing distributed across the
+cluster.  Likewise, the Phase 4 analysis will require a total of 330
+sec of I/O and 3500 seconds of processing.  Given the 160 seconds
+available per major frame, these numbers imply a total of 63
+processors are needed to keep up with the processing and I/O load.
+
+The other major driver on the IPP PS-1 cluster is the data storage
+requirements.  It is necessary to store the raw images from the entire
+AP Survey, the MOPS Verification Program (MVP) and the IPP
+Verification Program (IVP), and to have storage enough to represent
+the Static Sky by the end of the two year mission.  These storage
+requirements as a function of time are shown in
+Figure~\ref{fig:StorageProfile}.  Based on the PS-1 Design Reference
+Mission (PSDC-230-001), by the end of the second year, the total
+storage requirements for raw images and the Static Sky will be 850 TB,
+along with and an additional 55 TB needed for the AP DB storage
+
+To meet these requirements, the IPP cluster is designed to use fat
+bricks which will be capable of holding 24 disks each.  The 5U / 24
+disk rack mount computer cases are one of the highest density
+solutions currently available.  A 4U / 36 disk box is also available
+and will be considered.  The disk purchases will be staggered in three
+waves.  Before PS-1 goes on the sky, the first 1/3 of the disks (600
+disks total) will be purchased.  Since the lead time for disks is
+fairly short, the purchase will be made only when other portions of
+Pan-STARRS are clearly on a timeline to success.  After 9 months
+(tentatively 2006 September), the next 1/3 of the disks will
+purchased, and the remaining disks 9 months after that (tentatively
+2007 June).  Using conservative estimates of the available disk sizes
+at these purchase dates (400 GB, 600 GB, and 900 GB), and allocating 1
+of 12 disks to the RAID and 10\% of the volume to file system and
+binary Gigabyte overheads, the disk purchases outlined above result in
+a total volume after the last purchase of 950 TB.  This meets the
+requirements with 10\% spare excess.  The disk volume profile is also
+shown in Figure~\ref{fig:StorageProfile} and shows that the disk space
+will be available in the time it is required.
+
+The total number of computers to be purchased is 80.  This provides
+the 1800 disk slots and more than enough processors to meet the
+processing requirements.  This also leaves 5 live spare machines.
+
+There are two details which are not included in the analysis above:
+compression and replication.  Compression of the older raw data will
+reduce the volume requirements by a factor of roughly two.  However,
+replication of the data across the network is necessary to ensure the
+data against catastrophic failures on a single machine.  Replication
+doubles the total data space needed.  These two factors will tend to
+cancel each other, and are ignored in the calculations above.
+
+The IPP PS-1 clusters will have the following allocations of computers
+from this cluster:
+\begin{itemize}
+\item Phase 2 Nodes: 32
+\item Phase 4 Nodes: 30
+\item AP Database: 10
+\item Metadata Database: 1
+\item Image Server Database: 1
+\item Controller /  Scheduler: 1
+\end{itemize}
+This distribution meets the projections for computational power for
+each of these data systems, and leaves 5 computers as live spares for
+redundancy.
+
+\subsection{PS-1 Cluster Expected Reliability}
+
+With 80 computers and 1920 disks, component failures are inevitable.
+The cluster design and management must be chosen to minimize their
+impact on operations and data integrity.
+
+There are several factors which reduce the cluster's exposure to
+hardware failures.  First, the use of RAID controllers and RAID-5
+striping of the data will protect the data on a single RAID set
+against the failure of a single disk in the array.  Second,
+duplication of data across the cluster will protect against
+catastrophic failures of the array (loss of two disks, loss of the
+array controller card).  Finally, the flexibility of the distributed
+computing plan minimizes the impact the loss of individual machines
+has on operations by making changes in the data and processing
+assignments on the cluster a trivial matter.
+
+The components which are most likely to fail in the experience of our
+team are, in order: hard drives, RAID controllers, ram, power
+supplies, and other components.  The hard drive and RAID controller
+failure rates are by far the dominant concerns as they potentially
+affects the data integrity.
+
+Most sources (REFS: UCSD article, Samsung White Paper) currently imply
+hard disk failure rates (MTBF) in the range 400,000 hours and 500,000
+hours.  These are used as an upper limit, with the more historically
+conservative value of 100,000 hours used instead.  With 1920 disk,
+this MTBF implies a failure of one disk every 2.2 days.  Since the
+disks are in a RAID which reports the disk failures immediately and
+drops the array into degraded mode, these failures will not have a
+huge impact on the operations, and recovery time is only 10s of
+minutes.  This failure rate implies that the maintenance plan must
+include checks for hard disk failures on a daily basis, and should
+make use of email notification and early warning information (ie,
+SMART messages).  
+
+A catastrophic failure for the array would require two of the 12 disks
+to fail before the first failed disk is replaced.  Assuming that
+maintenance is poor and it is possible for a disk to take 1 week to
+be replaced, the probability of a catastrophe is 1.8\% each time the
+first disk fails.  Combined with the disk failure rate, RAID
+catastrophes are expected 6 times over the 2 year operation of PS-1.
+These numbers can be used as a guideline for the level of support
+needed to avoid these RAID failures.  Note that these 6 failures
+should not cause loss of data since the data is duplicated across the
+cluster, but they require over 1 day for recovery (as the entire array
+must be replicated across the network).
+
+A detailed IPP computer cluster commissioning and maintenance plan is
+specified in the document `Pan-STARRS PS-1 IPP Cluster Support'
+(PSDC-430-014).
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics[angle=-90]{pics/ps1_ipp_storage.ps}}
+\caption{ \label{fig:StorageProfile} Storage Profile}
+\end{center}
+\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\clearpage
+\appendix
+\section{Image Server Database Table Contents}
+\label{sec:ImageServerTableContents}
+
+Tables~\ref{tab:ImageServerTables:SO} - \ref{tab:ImageServerTables:VOL} list
+the basic contents of the Image Server database tables.  
+
+\begin{table}[bh]
+\begin{center}
+\caption{Storage Object Table Contents\label{tab:ImageServerTables:SO}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Description} \\
+\hline
+\code{so_id}      & integer        & internal storage object identifier \\
+\code{ext_id}     & string         & external storage object identifier (file ID) \\
+\code{comment}    & string         & user description of object \\
+\code{epoch}      & date/time      & last date of access \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Instance Table Contents\label{tab:ImageServerTables:INT}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Description} \\
+\hline
+\code{ins_id}     & integer        & internal instance identifier \\
+\code{so_id}      & integer        & key to storage object table \\
+\code{uri}        & string         & location in hardware collection \\
+\code{sha1sum}    & string         & checksum information \\
+\code{assigned_location} & boolean & is location user-specified? \\
+\code{epoch}      & date/time      & last date of access \\
+\code{atime}      & date/time      & last date of access \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Volume Table Contents\label{tab:ImageServerTables:VOL}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Description} \\
+\hline
+\code{vol_id}     & integer        & internal volume identifier \\
+\code{uri}        & string         & node name \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+\clearpage
+
+\section{Metadata Database Table Contents}
+\label{sec:MetadataTableContents}
+
+Tables~\ref{tab:WeatherTable} -- \ref{tab:overlaps} list the basic contents of
+each of the Metadata Database tables listed in Section~\ref{sec:Metadata}.
+
+\begin{table}[bh]
+\begin{center}
+\caption{Weather Table: some sample weather points\label{tab:WeatherTable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time             & date/time       & The time the weather information was measured. \\
+Temperature 01   & float           & The external temperature \\
+Temperature 02   & float           & The temperature at top of the dome \\
+Temperature 03   & float           & The temperature on the primary mirror \\
+Humidity         & float           & The relative humidity. \\
+Pressure         & float           & The (external) atmospheric pressure. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{SkyProbe Transparency Table (sample entries)\label{tab:SkyprobeBVTable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time             & date/time       & The time the SkyProbe image was taken. \\
+Filter           & string	   & Filter used for SkyProbe image. \\
+Transparency     & float	   & The derived transparency. \\
+Number of stars  & int		   & The number of stars used to measure the transparency. \\
+Astrometry       & coords	   & The astrometry used on the SkyProbe image. \\
+Exposure time    & float	   & The exposure time of the SkyProbe image. \\
+Sky brightness   & float	   & The measured sky (surface) brightness, counts / second \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Skyprobe Line Absorption Table (sample entries)\label{tab:SkyprobeATable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time             & date/time       & The time the LRProbe observation was taken. \\
+Disperser ID     & string          & ID of the dispersing element \\
+Atm Component 1  & float	   & The strength of the 1st atmospheric component. \\
+Atm Component 2  & float	   & The strength of the 2nd atmospheric component. \\
+Atm Component 3  & float	   & The strength of the 3rd atmospheric component. \\
+Disperser ID     & string          & ID of the dispersing element \\
+Number of stars  & int 	           & Number of stars used to measure the absorptions. \\
+Astrometry       & coords	   & The astrometry used on the LRProbe image. \\
+Exposure time    & float	   & The exposure time of the LRProbe image. \\
+Sky brightness   & float	   & The measured sky (surface) brightness, in physical units. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Skyprobe Line Emission Table (sample entries)\label{tab:SkyprobeETable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time             & date/time       & The time the LRProbe observation was taken. \\
+Disperser ID     & string          & ID of the dispersing element \\
+Atm Component 1  & float	   & The strength of the 1st atmospheric component. \\
+Atm Component 2  & float	   & The strength of the 2nd atmospheric component. \\
+Atm Component 3  & float	   & The strength of the 3rd atmospheric component. \\
+Continuum        & float	   & The strength of the continuum emission. \\
+Disperser ID     & string          & ID of the dispersing element \\
+Exposure time    & float	   & The exposure time of the LRProbe image. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{DIMM Measurements Table\label{tab:DimmTable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time             & date/time       & The time the DIMM observation was taken. \\
+$\sigma_x$       & float           & Raw dispersion in $x$. \\
+$\sigma_y$       & float	   & Raw dispersion in $y$. \\
+FWHM             & float	   & Dervied seeing full width at half maximum. \\
+RA               & float	   & The coordinates of the measured star. \\
+DEC              & float	   & The coordinates of the measured star. \\
+Exposure time    & float           & The exposure time of the DIMM observation. \\
+Telescope ID     & string          & source of the DIMM data \\
+\hline		 
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Near IR Wide-field Camera Results Table\label{tab:NIR-Table}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time           	 & date/time       & The time the NIR observation was taken. \\
+Sky brightness 	 & float           & The sky (surface) brightness in the NIR observation. \\
+Sky variance   	 & float	   & The variance in the sky (surface) brightness. \\
+Astrometry     	 & coords          & The astrometry used on the NIR image. \\
+FOV X            & float           & field width \\
+FOV Y            & float           & field height \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Dome Status Table\label{tab:DomeStatusTable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time          	 & date/time       & The time for which the dome status is valid. \\
+Azimuth       	 & float           & The azimuth of the dome. \\
+Open status   	 & boolean	   & Whether the dome is open or not. \\
+Lights status 	 & boolean	   & Whether lights are on in the dome or not. \\
+Track status 	 & boolean	   & Whether dome is tracking telescope or not. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Telescope Status\label{tab:TelescopeStatusTable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time         	 & date/time       & The time for which the telescope status is valid. \\
+Guide status 	 & enum            & The status of the guiding. \\
+Altitude     	 & float	   & The telescope altitude. \\
+Azimuth      	 & float	   & The telescope azimuth. \\
+RA  	     	 & float	   & The telescope Right Ascension (ICRS $\approx$ J2000). \\
+Dec 	     	 & float	   & The telescope Declination (ICRS $\approx$ J2000).\\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Raw FPA Images\label{tab:RawFPAs}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+ID               & string          & FPA image ID \\
+RA               & float	   & Coordinates of the boresight (i.e. telescope pointing). \\
+DEC              & float	   & Coordinates of the boresight (i.e. telescope pointing). \\
+Filter           & string	   & Filter used for the exposure. \\
+Image Type       & enum            & image exposure type \\
+Exposure time    & float	   & Exposure time for the image. \\
+Airmass          & float	   & Airmass at which the image was taken. \\
+ObsFrame ID      & int   	   & Observation frame identification number, ties FPAs into major frame \\
+ObsGroup ID      & int   	   & Observation group identification number, ties FPAs into observing group \\
+Observer         & string	   & The name of the observer, or the version of the telescope scheduler software. \\
+Program          & string	   & The observing program being executed. \\
+Nchips readout   & int   	   & Number of detector chips read out \\
+Camera           & string   	   & Identification of camera source \\
+Telescope        & string   	   & Telescope used for observation \\
+Astrometry       & coords	   & The astrometry used for the FPA. \\
+Chip Metadata    & string          & metadata resource file \\
+Cell Metadata    & string          & metadata resource file \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Pending Science Chips\label{tab:PendingChips}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+FPA ID           & string          & FPA image ID \\
+Chip ID          & string          & Chip identification number. \\
+Proc Status      & enum            & Current Processing Status. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Processed Science Chips\label{tab:ProcessedChips}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+FPA ID           & string          & FPA Image ID \\
+Chip ID          & string          & Chip identification number. \\
+Status           & enum            & Current Processing Status. \\
+Residual Stats   & float           & quality statistics. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Observation Group Information\label{tab:OBSGroup}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+ObsGroup ID      & string          & Identification number for the observation group. \\
+Number of images & string          & Number of images in the observation group. \\
+Type             & string          & Type of observation. \\
+Status           & string          & Status of the observation group. \\
+etc & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Observation Frame Information\label{tab:OBSFrame}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+ObsFrame ID      & string          & Identification number for the observation frame. \\
+Number of images & string          & Number of images in the observation group. \\
+Type             & string          & Type of observation. \\
+Status           & string          & Status of the observation group. \\
+etc & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Science Processing Stats\label{tab:PSStats}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Chip ID          & string	   & The chip identification number. \\
+State            & string	   & The state of the processing. \\
+ObsFrame ID      & string	   & The major frame the chip belongs to. \\
+ObsGroup ID      & string	   & The observation group the chip belongs to. \\
+P1 astrom        & string	   & The Phase 1 astrometry results file. \\
+P2 astrom        & string	   & The Phase 2 astrometry results file. \\
+P3 astrom        & string	   & The Phase 3 astrometry results file. \\
+N guide stars    & string	   & Number of guide stars used for the exposure. \\
+Astrometry stats & string	   & Summary statistics for astrometry (number of stars, $sigma_x$, $sigma_y$) \\
+Astrom catalog   & string	   & The reference catalog that was used for the astrometry. \\
+Bias method      & string	   & Method used to correct the bias. \\
+Bias stats       & string	   & Summary statistics for bias \\
+Flat-field image & string	   & The flat-field image that was applied. \\
+Kernel data      &       	   & A description of the OT kernel. \\
+Flat-field stats &       	   & Summary statistics for flat-field (sigma of sky). \\
+Mask image       & string	   & The mask image that was applied. \\
+Mask method      & string	   & The algorithm used to mask the bad pixels. \\
+Fringe images    & string	   & The fringe model images that were used. \\
+Fringe stats     &       	   & Summary statistics for fringes (fringe amplitude, sky sigma) \\
+Object stats     &       	   & Summary statistics for object detection (number of objects, depth, other input parameters). \\
+Photometry data  &       	   & photometry information: magnitude zero point and other corrections. \\
+Photometry stats &       	   & Summary statistics for the photometry (number of stars, $sigma_m$) \\
+Photom catalog   & string	   & The reference catalog that was used for the photometry. \\
+PSF stats        &       	   & Summary statistics of the PSF. \\
+Software ver     & string	   & Versions of each of the modules used in the processing. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Chip / Sky overlaps\label{tab:overlaps}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Chip ID     	 & string	   & The identification number of the chip. \\
+Sky Cell ID 	 & string	   & The identification number of the sky cell. \\
+State       	 & string	   & Processing state of overlap \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Processed Sky-Cell stats\label{tab:ProcessedSky}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Input Chips        & string 	   & Identification numbers of the chips used to produce the sky cell. \\
+PSF adjustments    & string 	   & Adjustments to the PSF. \\
+CR rejection stats & string 	   & Statistics from the CR rejection (number of CRs, distribution, limiting flux). \\
+Image comb params  & string 	   & Parameters used for the image combination. \\
+Diff image params  & string 	   & Parameters used for the image differencing. \\
+Average weight     & string 	   & The weight of the reference image \\
+P4D object stats   & string 	   & Summary statistics of the object detection  \\
+P4S object stats   & string 	   & Summary statistics of the object detection  \\
+Software versions  & string 	   & Software versions of modules used in the sky cell processing. \\
+Processing stats   & string 	   & Summary statistics of the processing (CPU time, etc). \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+\clearpage 
+
+\section{AP Database Table Contents}
+\label{sec:APDBTableContents}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Images\label{tab:images}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Image ID          & & \\ 
+time/date	  & & \\
+Exposure Time	  & & \\
+Nstars		  & & \\
+NX		  & & \\
+NY		  & & \\
+photcode	  & & \\
+Mcal		  & & \\
+Mcal error	  & & \\
+Mcal chisq	  & & \\
+Airmass           & & \\
+Astrometry	  & & \\
+PSF		  & & \\
+flags		  & & \\
+Camera		  & & \\
+\hline		  
+\end{tabular}	  
+\end{center}	  
+\end{table}	  
+		  
+\begin{table}[bh]
+\begin{center}
+\caption{Image Overlaps\label{tab:ImageOverlaps}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Image ID          & & \\
+Region Table	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Objects\label{tab:Objects}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline 
+ID                & & \\
+$\alpha$	  & & \\
+$\delta$	  & & \\
+$\mu_{\alpha}$	  & & \\
+$\mu_{\delta}$	  & & \\
+$\sigma_{\alpha}$ & & \\
+$\sigma_{\delta}$ & & \\
+$\chi^2$ position & & \\
+$N_{\rm det}$	  & & \\
+$N_{\rm miss}$	  & & \\
+flags		  & & \\
+\hline		  
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Average Magnitudes\label{tab:AveMags}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+object ID         & & \\
+$M_{\rm int}$	  & & \\
+$M_{\rm ext}$	  & & \\
+$\chi^2_{\rm mag}$& & \\
+$\sigma_{\rm mag}$& & \\
+photcode	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Solar System Objects\label{tab:SSObjs}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+SSO ID     	  & & \\
+$N_{\rm det}$	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Matched Detections\label{tab:Detections}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+$\alpha$          & & \\
+$\delta$	  & & \\
+$\sigma_{\alpha}$ & & \\
+$\sigma_{\delta}$ & & \\
+$M_{\rm inst}$	  & & \\
+$M_{\rm cal}$	  & & \\
+$\sigma_{\rm mag}$& & \\
+photcode	  & & \\
+type		  & & \\
+flags		  & & \\
+time/date	  & & \\
+airmass		  & & \\
+$\sigma_{x}$	  & & \\
+$\sigma_{y}$	  & & \\
+$\theta$	  & & \\
+object ID         & & \\
+exptime		  & & \\
+sky		  & & \\
+$\sigma_{\rm sky}$& & \\
+etc		  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Orphaned Detections\label{tab:Orphans}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+$\alpha$          & & \\
+$\delta$	  & & \\
+$\sigma_{\alpha}$ & & \\
+$\sigma_{\delta}$ & & \\
+$M_{\rm inst}$	  & & \\
+$M_{\rm cal}$	  & & \\
+$\sigma_{\rm mag}$& & \\
+photcode	  & & \\
+type		  & & \\
+flags		  & & \\
+time/date	  & & \\
+airmass		  & & \\
+$\sigma_{x}$	  & & \\
+$\sigma_{y}$	  & & \\
+$\theta$	  & & \\
+exptime		  & & \\
+sky		  & & \\
+$\sigma_{\rm sky}$& & \\
+etc		  & & \\
+\hline		  
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Non-detections\label{tab:NonDetects}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline  
+object ID          & & \\
+$N_{\rm non-det}$	   & & \\
+last time/date 	   & & \\
+last mag	   & & \\
+faintest time/date & & \\
+faintest mag	   & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Regions\label{tab:Regions}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+$\alpha_0$        & & \\
+$\alpha_1$	  & & \\
+$\delta_0$	  & & \\
+$\delta_1$	  & & \\
+Region ID	  & & \\
+Parent ID	  & & \\
+Nchildren	  & & \\
+Images		  & & \\
+Objects		  & & \\
+Detections 	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Filters\label{tab:Filters}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Filter ID         & & \\
+Filter name	  & & \\
+Photcode	  & & \\
+$\lambda_0$	  & & \\
+$\delta_\lambda$  & & \\
+$\epsilon$	  & & \\
+transmission curve& & \\
+time/date	  & & \\
+\hline		  
+\end{tabular}	  
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Photcodes\label{tab:Photcodes}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Photcode          & & \\
+Telescope	  & & \\
+Camera		  & & \\
+Detector	  & & \\
+Filter		  & & \\
+Nominal ZP	  & & \\
+airmass terms	  & & \\
+color terms	  & & \\
+Target		  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Zero Point History\label{tab:Zpts}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline 
+Photcode          & & \\
+start Time/date	  & & \\
+end Time/date	  & & \\
+Zero Points	  & & \\
+airmass		  & & \\
+color		  & & \\
+error		  & & \\
+N measurements	  & & \\
+N stars		  & & \\
+photom ref set    & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Distortion History\label{tab:Distortions}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Camera            & & \\
+Telescope	  & & \\
+distortion terms  & & \\
+time/date	  & & \\
+residuals / error & & \\
+N stars		  & & \\
+N images	  & & \\
+astrom ref set	  & & \\
+\hline		  
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Database Hosts\label{tab:APDBHosts}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+machine name	  & & \\
+machine ID	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Software Runtime Configuration Issues}
+\label{sec:RuntimeConfig}
+
+The IPP Software requires extensive runtime configuration information.
+This includes default parameters for analysis to be performed,
+descriptions of how a particular analysis is performed, locations of
+data sources, and so forth.  The IPP may store this information in the
+Metadata Database or in configuration files available to the user.
+Both methods are implemented in the current design.  In either method,
+the necessary parameters are identical.  This section discusses the
+contents of specific portions of the runtime configuration.
+
+\subsection{Camera Definition Information}
+
+Every camera which may be analysed by the IPP has differences in how
+the data is represented.  The IPP is built with the flexibility to
+handle data from many different cameras, not just the Pan-STARRS
+Gigapix cameras.  This is partly to allow testing of the analysis
+system on data from other telescopes, such as MegaPrime on CFHT and
+Suprime on Subaru, but also to allow us to adapt to changes in the
+design of the Gigapix cameras themselves.  It also means the IPP
+software may be used by astronomers for other analysis projects beyond
+the IPP.  
+
+Most cameras provide extensive descriptive information in the FITS
+image headers when the images are read out.  Typically, the location
+and orientations of the individual detectors are defined by keywords
+such as DATASEC and DETSEC.  Other variations on these words are used
+for cameras which place the pixels from multiple amplifiers in the
+same FITS data segment.  Other parameters, such as astrometric
+information or exposure times, are stored in headers as well.  It is
+possible to use these header keywords to guide the analysis software,
+but there are two difficulties.  
+
+First, it is very common for different keywords to be used by
+different cameras, sometimes even the same camera may use different
+keywords for the same information at different times (major readout
+software upgrades, for example, can be accompanied by keyword
+revisions).  In addition, within Pan-STARRS and the IPP, it is
+necessary to have the capability to refer to the Metadata database as
+the authoratative sources of some of these entries rather than the
+image headers.  Given this circumstance, it is at least necessary to
+define the appropriate source for a given data concept appropriate to
+data from a specific camera.
+
+The second problem arises when actually performing an analysis.  In
+many circumstances, the software needs to know what data to expect
+even when an appropriate camera image is not available.  This is
+particularly true for a camera which is composed of multiple chips and
+multiple amplifiers.  It is a frequent circumstance than some subset
+of the chips or amplifiers will either be unavailable or are invalid
+for one reason or another.  It is important for the software to have a
+guide for what data should be available from a perfect readout of the
+given camera so decisions can be made how to handle data which is not
+complete.  This is also important to validate that a particular
+dataset, which appears to be from a known camera, actually corresponds
+to that camera and has all of the necessary information where
+expected.
+
+In order to facilitate the operation of the IPP with a variety of
+cameras, and to allow the software the flexibility to change the
+camera defintion dynamically, the IPP includes a collection of
+software runtime configuration information which defines a given
+camera.  This information is represented below in the form of the
+PSLib Metadata Config file, but may be stored in the Metadata Database
+or in an alternate format as appropriate.
+
+The a single camera is represented as a Focal Plane Array (FPA),
+divided into Chips, divided into Cells.  For a single FPA, all imaging
+data is stored in a FITS file or a collection of FITS files.  Software
+needs to know where in a given file or set of files to find a
+particular Cell, what Cells to expect, what chips to expect, and the
+relationships between those entities, etc.
+
+A single camera configuration file (or dataset) represents the
+description of a complete FPA.  In the configuration file, any
+parameters which are specific to the complete FPA are placed on their
+own lines.  These include the definition of the keywords or database
+locations.  An incomplete example is given below.
+
+\begin{verbatim}
+NCELL       S32    NN
+NCHIP       S32    NN
+EXPTIME-SRC STR    HD:EXPTIME # need to specify PHU vs EXTNAME
+EXPTIME-KEY STR    EXPTIME  
+DATE-KEY    STR    DATE-OBS
+DATE-FMT    STR    YYYY/MM/DD
+
+TYPE        CELL   FILENAME           EXTNAME  CHIP      DATASEC       BIASSEC     
+CELL.nn     CELL   @ROOT@CELL         AMP00    CHIP.00   CF:[0,0:0,0]  HD:BIASSEC
+CELL.01     CELL   @ID/@ID@CELL.fits  AMP01    CHIP.00   DB:???
+\end{verbatim}
+
+\subsection{Analysis Recipe Information}
+
+In order to maintain flexibility in the analysis details, the IPP uses
+recipes to define how a particular analysis is implemented.  Each
+major analysis script (eg, Phase 2) has its own recipe configuration
+information, which may be stored in the Metadata Database or in the
+form of the PSLib Metadata Config file.  This configuration
+information includes all of the user configurable parameters.  Many of
+these may specify a specific value, or they may specify lookup methods
+(database locations, or header locations).  The specifies of each
+depends on the context.  Below is an example recipe file for the bias
+subtraction portion of Phase 2, giving several alternative options for
+certain entries.  Note that, for example, the overscan subtraction may
+be specified as using a particular region given in the recipe file, or
+on the basis of a particular header keyword.
+
+\begin{verbatim}
+# BIAS:
+BIAS.IMAGE                 STR    NONE
+BIAS.IMAGE  		   STR    FILE:bias.fits
+BIAS.IMAGE  		   STR    DB:BEST
+BIAS.IMAGE  		   STR    DB:CLOSE
+
+BIAS.OVERSCAN 		   STR    HD:BIASSEC
+BIAS.OVERSCAN 		   STR    CF:[0,16:0,2048]
+BIAS.OVERSCAN 		   STR    NONE
+
+BIAS.OVERSCAN.STATS 	   STR    MEDIAN
+BIAS.OVERSCAN.STATS 	   STR    MEAN
+
+BIAS.OVERSCAN.FIT          STR    SPLINE
+BIAS.OVERSCAN.FIT.NPTS     S32    5
+
+BIAS.OVERSCAN.FIT          STR    POLYNOMIAL
+BIAS.OVERSCAN.FIT.ORDER    S32    3
+BIAS.OVERSCAN.FIT.NBIN     S32    5
+\end{verbatim}
+
+\section{I/O Code Autogeneration}
+\label{sec:AutocodeIO}
+
+The IPP includes a number of data collections which have multiple
+representations.  A software tool will be used to automatically
+generate code to provide I/O APIs to read and write these data and to
+define the data structures used to carry them within a program.
+Within the IPP, examples of these different data entities include
+database tables (ie, in the Metadata Database), FITS Tables (to
+exchange bulk data), and XML (to exchange more complete datasets).
+
+I/O API Autocode template (example.def):
+\begin{verbatim}
+Name    Example
+Table   EXAMPLE
+EXTNAME EXAMPLE
+
+KEY     XVALUE
+
+# name  format   unit      comment
+XVALUE  F32      pixels    "x coordinate"
+BINNING S32      fraction  "binning factor"
+NAME    STR[32]  string    "description of entry"
+\end{verbatim}
+
+Running autocode on such a file would generate an output header and C
+files \code{example.h, example.c} with the following structure and APIs:
+
+\begin{verbatim}
+typedef struct {
+  psF32 XVALUE;    // x coordinate
+  psS32 BINNING;   // binning factor
+  char  NAME[32];  // description of entry
+} Example;
+
+psMetadata *psFITSTableInitExample ();
+psExample *psFITSTableLoadExample (char *filename, int *Nrows);
+bool psFITSTableSaveExample (char *filename);
+
+psMetadata *psDatabaseTableInitExample ();
+psExample *psDatabaseTableLoadExample (char *filename, int *Nrows);
+bool psDatabaseTableSaveExample (char *filename);
+psExample *psDatabaseTableLoadExampleRow (char *filename, psF32 XVALUE);
+\end{verbatim}
+
+%\bibliographystyle{plain}
+%\bibliography{panstarrs}
+
+\input{glossary.tex}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/design/ippSCDdraft.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/ippSCDdraft.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/ippSCDdraft.tex	(revision 22322)
@@ -0,0 +1,2854 @@
+% -*- latex -*- 
+%%% $Id: ippSCDdraft.tex,v 1.1 2004-11-30 23:16:57 eugene Exp $
+
+\providecommand{\setflag}{\newif \ifwhole \wholefalse}
+\setflag
+\ifwhole\else
+\documentclass[panstarrs,spec]{panstarrs}
+\setcounter{secnumdepth}{5}
+\begin{document}
+\section{Pan-STARRS IPP SCD Standalone Section}
+\newcommand\PSeten{$\gtrsim 40$\,m$^2$\,deg$^2$}
+\newcommand\PSiqdeg{$<$ 27\%}
+\newcommand\FRM[2]{\parbox[t]{#1}{\raggedright #2}}
+\newcommand\FRA{100pt}
+\newcommand\FRB{260pt}
+\newcommand\FRC{40pt}
+\newcommand\FRD{80pt}
+\newcommand\FRN[1]{\FRM{50pt}{#1}}
+\newcommand\FRS[1]{\FRM{190pt}{#1}}
+\newcommand\grizy{$grizy$}
+\fi 
+
+% \section{Image Processing Pipeline}
+
+\subsection{Subsystem Overview}
+\label{IPP}
+
+The Pan-STARRS Image Processing Pipeline (IPP) performs the image
+processing and data analysis tasks needed to enable the scientific use
+of the images obtained by the Pan-STARRS telescopes.  The primary
+goals of the IPP are to process the science images from the Pan-STARRS
+telescopes and make the results available to other systems within
+Pan-STARRS.  It also is responsible for combining all of the science
+images in a given filter into a single representation of the
+non-variable component of the night sky called the ``Static Sky''.  To
+achieve these goals, the IPP also performs other analysis functions to
+generate the calibrations needed in the science image processing and
+to occasionally use the derived data to generate improved astrometric
+and photometric reference catalogs.  It also provides the
+infrastructure needed to store the incoming data and the resulting
+data products.
+
+The IPP inherits lessons learned, and in some cases code and prototype
+code, from several other astronomy image analysis systems, including
+Imcat (Kaiser), the Sloan Digital Sky Survey (REF), the Elixir system
+(Magnier \& Cuillandre), and Vista (Tonry).  Imcat and Vista have a
+large number of robust image processing functions.  SDSS has
+demonstrated a working analysis pipeline and large-scale database
+system for a dedicated project.  The Elixir system has demonstrated an
+automatic image processing system and an object database system for
+operational usage.
+
+The users of the IPP output are all systems internal to the Pan-STARRS
+project.  They consist of the Transient Science Client, which will
+receive the detections of transient objects on short time-scales; the
+Moving Object Processing System (MOPS), which will receive the
+detections of non-stationary transient objects on day-to-week
+timescales; and the Published Science Products Subsystem (PSPS), which
+will receive all data products of interest to the outside world, and
+will act as the long-term archive and publishing clearinghouse.
+
+An important operational choice for the IPP is the decision not to
+attempt to save all raw data.  Once the IPP is running in a standard
+operational mode, data will be processed by the pipeline and deleted
+when it is no longer needed.  Raw images will only be saved for a
+short period to allow tests and quality assurance, and potentially to
+allow systems which study transient phenomena to return to recent data
+for closer inspection.  In general, during normal operations, raw
+science images will be deleted after $\sim$1 month.
+
+The primary IPP hardware system on which the software operates will
+not be located at the summit.  Instead, because of thermal, power, and
+space constraints, the hardware will likely be located in a facility
+off the mountain.  A subset of processing tasks may eventually be
+assigned to machines at the summit if justified by the savings in data
+transfer time and cost.
+
+\subsection{Subsystem Top-level Requirements}
+
+The IPP has the following top-level requirements derived from the
+system requirements above (Section~\ref{sys:TLR}):
+
+
+\begin{enumerate}
+\item For images obtained in photometric weather with normal detector
+  characteristics and providing appropriate flat-field images and
+  correction data have been obtained, the IPP shall produce reduced
+  science images for each full camera exposure with relative
+  photometric zero-point scatter less than 1\% ($1 \sigma$) across the
+  full field. 
+  \label{TLR:1}
+
+\item For images of reference fields calibrated for the IPP filter set
+  and obtained in photometric weather with normal detector
+  characteristics and providing appropriate flat-field images and
+  correction data have been obtained, the IPP shall determine and
+  track zero-points for these exposures with a 1$\sigma$ accuracy of
+  1\%.
+  \label{TLR:2}
+
+\item For images obtained under normal seeing conditions and optical
+  distortion, the IPP shall produce reduced science images for each
+  full camera exposure with an astrometric calibration providing $<
+  30$ milliarcsecond scatter (1$\sigma$) for sequential images of the
+  same location.
+  \label{TLR:4}
+
+\item For images obtained under normal seeing conditions and optical
+  distortion, the IPP shall produce reduced science images for each
+  full camera exposure with an astrometric calibration providing $<
+  100$ milliarcsecond scatter (1$\sigma$) relative to the ICRS
+  reference system.
+  \label{TLR:3}
+
+\item In photometric weather and under moon conditions listed in
+  Table~\ref{moonconditions}, the IPP shall produce reduced science
+  images for each full camera exposure which have background
+  variations of less than 1\% in regions free of large ($> 30$ pixels
+  diameter) astronomical structures.
+  \label{TLR:5}
+
+\item In photometric weather, the IPP shall produce reduced science
+  images for each full camera exposure which have background
+  deviations from the static sky in the same filter of less than 1\%
+  for the median in large ($> 30$ pixels diameter)
+  regions.
+  \label{TLR:5a}
+
+\item The IPP shall merge all $g$ filter science images into a static sky image.
+  \label{TLR:6}
+
+\item The IPP shall merge all $r$ filter science images into a static sky image.
+  \label{TLR:7}
+
+\item The IPP shall merge all $i$ filter science images into a static sky image.
+  \label{TLR:8}
+
+\item The IPP shall merge all $z$ filter science images into a static sky image.
+  \label{TLR:9}
+
+\item The IPP shall merge all $y$ filter science images into a static sky image.
+  \label{TLR:10}
+
+\item The IPP shall merge all $w$ filter science images into a static sky image.
+  \label{TLR:11}
+
+\item The IPP shall detect and classify objects on the individual processed science
+  images.
+  \label{TLR:12}
+
+\item The IPP shall detect and classify objects on the stacked groups
+  of science images.
+  \label{TLR:13}
+
+\item The IPP shall detect and classify objects on the static sky
+  image.
+  \label{TLR:14}
+
+\item The IPP shall detect transients with significance $>3\sigma$ in
+  the individual science images relative to the static sky
+  image.
+  \label{TLR:15}
+
+\item The IPP shall degrade the stacked image by no more than \tbr{10
+  milliarcseconds (FWHM added in quadrature)} over the theoretical
+  limit for the stack under infinite
+  sampling.
+  \label{TLR:16}
+
+\item The IPP shall perform the processing of science images to the
+  level of transient detection and static sky inclusion at a rate such
+  that exposures taken at an \tbr{average cadence of 40 seconds} do
+  not accumulate in the processing buffer (average throughput
+  requirement).
+  \label{TLR:17}
+
+\item The IPP shall limit the false alarm rate (FAR) to less than 5\%
+  for transient detections $> 5\sigma$ sent to the preferred client
+  science pipelines.\footnote{note difference with PS-4: 1\%}
+ \label{TLR:18}
+
+\item The IPP shall perform transient detection to a completeness of
+  99\% at the completeness for transient detections with a significant
+  $> 5\sigma$.
+
+\item The IPP shall publish the static sky images to the Pan-STARRS
+  Published Science Products Subsystem (PSPS) at a rate so the full
+  sky is transmitted once per year.
+  \label{TLR:19}
+
+\item The IPP shall publish the detected objects to the Pan-STARRS
+  Published Science Products Subsystem (PSPS) at a rate such that the
+  objects from the full sky are transmitted once per
+  year.
+  \label{TLR:20}
+
+\item The IPP shall send the IPP metadata and received OTIS metadata
+  to the Pan-STARRS Published Science Products Subsystem (PSPS)
+  weekly.
+  \label{TLR:21}
+
+\item The IPP shall provide access to preferred Pan-STARRS science clients to the
+  detected transient objects within \tbr{5 minutes}.
+  \label{TLR:22}
+
+\item The IPP shall provide sufficent storage volume for raw images from the AP and
+  IVP Surveys and the \grizy\ Static Sky.\footnote{note difference with
+  PS-4: 1 month of raw images}
+  \label{TLR:23}
+
+\item The IPP shall provide sufficient storage volume for all detections from the
+  AP, IVP, and MVP Surveys.\footnote{note difference with PS-4: 1 year
+  of detections}
+  \label{TLR:24}
+
+\item The IPP shall provide sufficient storage volume for 2 years of
+ metadata.\footnote{note difference with PS-4: 10 years of
+ metadata}
+  \label{TLR:25}
+\end{enumerate}
+
+\subsection{Subsystem Top Level Description}
+
+We now discuss three aspects of the IPP and their relationships: the
+IPP Analysis Stages, the Architectural Components which make up the
+IPP, and the Hardware System which provides the basic computer
+resources needed by the IPP.  We discuss these aspects of the IPP in
+general terms first, and in later sections, address the conceptual
+design issues for each of the three aspects of the IPP.
+
+\subsubsection{Analysis Tasks and Stages} 
+
+Specific programs are required to perform the processing steps listed
+above.  These can be divided into well-defined analysis stages, each
+of which operates on a particular unit of data, such as a single OTA
+image or a collection of astronomical objects.  Analysis tasks
+representing the different analysis stages are performed on the IPP
+computer cluster.  Note the distinction between the generic analysis
+{\em stage} and a specific analysis {\em task}.  An analysis stage
+represents a type of analysis which is performed, such as the basic
+image calibration and object detection analysis.  An analysis task is
+a particular realization of an analysis stage, e.g., the analysis of
+OTA number 61 from exposure 654321 to produce a specific set of output
+data products.  The analysis stages are discussed in detail in
+Section~\ref{IPP:AnalysisStages}.
+
+\subsubsection{Architectural Components}
+
+In order to achieve the required functionality, the IPP provides an
+infrastructure within which the Analysis Stages above are exectuted.
+We have divided the IPP software infrastructure into a number of
+clearly-defined architectural software units, listed as follows:
+
+\begin{enumerate}
+
+\item {\bf Image Server:} This component is a large data store for all
+  images used by the IPP, including the raw images from the telescope,
+  the master calibration images, the reference static-sky images, and
+  any temporary image data products produced by the IPP.  The Image
+  Server accepts the incoming data and stores it until it is no longer
+  needed by other portions of the IPP.  The Image Server is not
+  restricted to imaging data: it is capable of storing any large data
+  files which are not well-suited for inclusion in a more structured
+  relational database and for which access needs to be widely
+  available beyond the individual process which created the file.
+
+\item {\bf Astrometry \& Photometry Database (AP DB):} This component
+  stores and manipulates astronomical objects detected in various
+  images, as identified above, including individual measurements of
+  objects on the images, the summary information about those objects,
+  and reference object data.  It also provides mechanisms for users to
+  query and manipulate the objects and detections.
+
+\item {\bf Metadata Database:} This component stores the data which is
+  not directly related to images or astronomical objects, but which is
+  needed to perform the IPP analyses.  The metadata may include the
+  summary weather information for each night, or details about the
+  filters, camera, telescopes, etc.  
+
+\item {\bf IPP Controller:} In order to perform the analysis stages
+  required by the IPP, it is necessary to use distributed computing
+  processes on a large number of computers.  The IPP Controller
+  manages the collection of analysis tasks performed on these
+  machines.
+
+\item {\bf IPP Scheduler:} This component is a decision-making
+  mechanism which guides the operation of the IPP.  It evaluates the
+  currently available collection of data, identifies the necessary
+  analysis, and assigns the analysis tasks to the IPP Controller.
+
+\end{enumerate}
+
+The relationship between these software units is shown in
+Figure~\ref{overview}.  This figure also shows the interactions
+between the IPP and other Pan-STARRS systems.  The architectural
+components are discussed in detail in
+Section~\ref{IPP:ArchComponents}.
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/IPPoverview}}
+\caption{ \label{overview} IPP System Overview}
+\end{center}
+\end{figure}
+
+\subsubsection{IPP Hardware Organization}
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/IPPhardware}}
+\caption{ \label{hardware} IPP Hardware Organization}
+\end{center}
+\end{figure}
+
+The IPP needs substantial computer resources, both in terms of
+computational power and in terms of data storage and network
+bandwidth.  The IPP requires relatively large amounts of data storage
+space, primarily for the image data.  Image data is organized in two
+categories.  First, there is the per-OTA data -- data associated with
+specific OTAs, including the raw images, the calibration images, and
+temporary processed images at various stages.  Second, there is the
+data associated with the static sky imagery, which is in turn
+organized into smaller sky-cell units.  In addition to image data,
+there are the somewhat smaller data entities of the Metadata Database
+and AP Database.
+
+The computer hardware is organized into nodes which provide both data
+storage and computational resources.  The data storage nodes are
+divided into three classes: those which deal with the per-OTA image
+data, those that provide the storage for the static sky images, and
+those that provide the storage for the other data systems, the
+Metadata Database and the AP Database.  In addition, the computational
+tasks related to Phase 2 take place on the per-OTA storage nodes and
+the Phase 4 computation takes place on the static sky storage nodes.
+
+Figure~\ref{hardware} shows our basic concept for the hardware
+organization for the IPP.  This diagram shows the two types of compute
+nodes: OTA-level processing and storage nodes (dominated by Phase 2)
+and static sky processing and storage nodes (mostly Phase 4).  Also
+shown are two switches which divide the network into OTA and
+Static-Sky portions.  In such an organization, the interswitch
+communication must meet the throughput needs between these network
+portions.  The additional data systems (Metadata Database and AP
+Database) are also shown.
+
+\subsubsection{Data Products}
+
+The IPP data products are listed in Table~\ref{SYS:IPPProducts} and
+are discussed in detail in the sections below.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Subsystem Tasks and Functions}
+\label{IPP:Tasks}
+
+In order to achieve the top-level requirements listed above, the IPP
+performs the following tasks:
+
+\begin{enumerate}
+
+\item Accept raw images from OTIS.
+
+\item Accept metadata from OTIS.
+
+\item Produce high-quality calibration images from the raw calibration
+  images.  
+
+\item Pre-process the science images with the high-quality master
+  calibration images.  This analysis is called ``Phase 2'' and the
+  resulting images are called ``Phase 2'' or P2 images.
+
+\item Perform an image-wide improvement to the astrometric and
+  photometric calibrations and the sky background subtraction (``Phase
+  3'').
+
+\item Merge multiple pre-processed science images -- from multiple
+  telescopes or from sequential, dithered exposures -- into single,
+  cleaned, stacked images.  This analysis, and the next task,
+  constitute ``Phase 4''.  The resulting images are called ``Phase 4
+  Summed'' or P4$\Sigma$ images.
+
+\item Subtract a static sky image from the cleaned, stacked images to
+  produce an image of only the transient events.  The resulting images
+  are called ``Phase 4 Difference'' or P4$\Delta$ images.
+
+\item Excise the significant transients and outliers from the
+  pre-processed science images and merge the cleaned images into the
+  static sky image.
+
+\item Detect objects on the four types of images: pre-processed
+  images, the stacked image, the difference image, and the static sky
+  image.
+
+\item Determine astrometry of the detected objects relative to an
+  astrometric reference.
+
+\item Determine photometry of the detected objects relative to a
+  photometric reference.
+
+\item Provide the tools and data sources needed to construct a
+  high-quality astrometric reference catalog from the extracted
+  objects.
+
+\item Provide the tools and data sources needed to construct a
+  high-quality photometric reference catalog from the extracted
+  objects.
+
+\item Publish the static sky images to the Pan-STARRS PSPS after data
+validation.
+
+\item Publish the detected objects to the Pan-STARRS PSPS after data
+validation.
+
+\item Provide access to MOPS to the single-occurrence detections of
+transient objects on short time scales.
+
+\item Provide access to MOPS to the metadata specific to the images in
+which single-occurrence detections were found.
+
+\item Provide access to othe preferred Pan-STARRS science clients to
+  the measurements of the detected transient objects on short time
+  scales.
+
+\item Store the raw images for a period of time, depending on the
+  survey source of the data.  During normal operations, raw data is
+  stored for \tbr{1 month}.
+
+\item Store the detected objects for a period of time, depending on
+  the type of detection.  Transients from the P4$\Delta$ images may be
+  excised after \tbr{6 months}.
+
+\end{enumerate}
+
+\subsection{Operational Scenarios}
+
+In the normal operational state, the IPP continuously accepts data
+from the summit (to the Image Server) and schedules new analysis tasks
+for operation.  At the start of a night, it will need to choose
+between performing analysis on science images and analysis on
+calibration images.  It will also feed data to the MOPS and other
+preferred science clients (such as the Transient Science Client) in
+real time.  For the MOPS, this data feed occurs at the end of each
+night.  Other clients may require the data to be delivered more
+rapidly.  The IPP has a top-level requirement to be capable of
+delivering the transients to clients which need them within 5 minutes
+of the image being taken.
+
+As a continuous process, the IPP will perform detailed object analysis
+on the static sky images.  The logical time to schedule this analysis
+is to process those portions of the sky which are within $\sim 15$
+degrees of the sun (in RA), since these are regions which are
+guaranteed to be unobserved and unchanging for at least 2 months.
+
+Some of the IPP computational and data resources will also be used to
+construct the astrometric and photometric calibration catalogs.  The
+processing tasks involved in this effort will be assigned to the IPP
+cluster along with the standard, nightly processing tasks.  These
+analysis tasks will be initiated by a human.
+
+On a longer term timescale (possibly once a month when the system is
+running in an operational mode), some of the data products will be
+pushed to the PSPS.  In some cases (static sky images, most of the
+metadata), this publication will involve the simple copying of the
+data structures to an identical system on PSPS hardware.  In other
+cases (object detections), data will be sent to the PSPS which will be
+processed by the PSPS for incorporation in PSPS databases.  For the
+static sky publication, it is again logical to send those portions of
+the static sky which are within $\sim 15$ degrees of the sun in RA.
+
+The start-up process for the IPP as a whole may be viewed as a
+start-up procedure for each of the architectural systems
+independently.  The three data storage systems (Image Server, AP
+Database, and Metadata Database) need to check the existence and
+validity of the hardware and data it manages.  The IPP Controller
+needs to determine which computers are available before allowing
+processing.  The IPP Scheduler will need to check that all of the
+other systems are sufficiently operational before attempting to make
+decisions and submit jobs to the IPP Controller.
+
+\subsection{Conceptual Design}
+
+\subsubsection{Architectural Components}
+\label{IPP:ArchComponents}
+
+\paragraph{Image Server}
+\label{IPP:ImageServer}
+
+The IPP Image Server is a large data store for all images used by the
+IPP.  The Image Server stores all of the images needed by the IPP for
+the length of time they are required; total data volume is specified
+in detail in the separate report, `The Pan-STARRS Image Processing
+Pipeline Computational Challange' (PSDC-4xx-xx); the total data volume
+for PS-4 is approximately 1000 TB.
+
+The IPP Image Server stores science and calibration images as FITS
+files on disk Images of the Static Sky are stored as a variant of
+FITS, using a data representation yet to be determined to minimize the
+total data volume and pixel overlap and to minimize the losses from
+warping of the images.  The optimal representation of the Static Sky
+is a topic for study by the IfA IPP Team.  Two parameters which
+critically affect the total data volume requirements of the Image
+Server are 1) the pixel scale (arcseconds per pixel) and 2) the byte
+representation of the data.  Current working numbers for these are
+0.2\arcsec and 4 bytes per pixel (2 for the signal and 2 for the noise
+map).
+
+The IPP Image Server distributes images across a cluster of machines.
+Multiple copies of each image may be requested for redundancy or for
+improved throughput.  The image (or one of the copies) may be placed
+on a specified machine.  The specific machine on which a particular
+copy of an image resides may be determined via the user interface
+methods.
+
+The IPP Image Server maintains a record of all images currently
+available in the repository.  The Image Server is only responsible for
+tracking the location of the images, not for tracking metadata
+information such as the image summary statistics or the state of the
+image processing for the image.  These aspects are included in the
+Metadata Database discussed below.
+
+The IPP Image Server interfaces with other subsystems of the IPP.  It
+provides a mechanism by which other IPP subsystems may identify the
+image location (the computer on which it resides).  It also provides a
+mechanism to serve a specified image to an IPP or Pan-STARRS subsystem
+on request.  It also provides maintainence mechanisms for deletion,
+relocation, and duplication of images in the Image Server as
+necessary.
+
+The IPP Image Server is not limited to image data.  Any large file
+would be an appropriate object to store in the Image Server.  Raw
+images from the telescope are stored as separate OTA images, with
+multiple Cell images per file, as well as video sequences from the
+guide stars in the form of MEF extensions.
+
+%% IPP Image Server T & F
+
+Image Server tasks and functions:
+
+\begin{itemize}
+
+\item The IPP Image Server stores images on a distributed collection
+  of computer disks.  Individual instances of a file are only required
+  to be stored on a single machine (striping across computers is not a
+  requirement).
+
+\item The IPP Image Server attempts to store an image on a specific
+  machine if requested by the user.
+
+\item If such a request cannot be honored (ie, the machine is down),
+  the IPP Image Server selects an appropriate machine and notifies the
+  requesting agent of the new location.
+
+\item The IPP Image Server stores multiple copies of each image upon
+  request, the number of copies specified independently for each file
+  by the user.
+
+\item The IPP Image Server maintains a record of all image copies
+  currently available in the repository.  This record includes at
+  least the image name, location (which machine), the image size, and
+  the state of the image (available, locked,
+  deleted).
+
+\item The IPP Image Server locks images in the repository on request.
+  Both read (shared) and write (exclusive) locks are provided.  A read
+  lock prevents write access to the file; a write lock prevents both
+  read and write access.  Access prevention may be advisory rather
+  than rigorously enforced.
+
+\item The IPP Image Server return the image location (the computer or
+  computers on which it resides) upon request.
+
+\item The IPP Image Server provides a specified image upon request.
+
+\item The IPP Image Server deletes images in the repository on
+  request.
+\end{itemize}
+
+\paragraph{AP Database}
+\label{IPP:APDB}
+
+The purpose of the AP Database is:
+\begin{itemize}
+\item to enable the photometric calibration of images
+\item to enable the astrometric calibration of images
+\item to enable the construction of flat-field correction frames
+\item to enable the construction of a photometric calibration catalog
+\item to enable the construction of an astrometric calibration catalog
+\item to monitor the system photometry calibration parameters
+\item to monitor the system astrometry calibration parameters
+\item to perform the identification of single-occurance transients
+\end{itemize}
+
+The AP (Astrometry \& Photometry) Database is a mechanism to store
+data related to astronomical objects derived from various sources with
+a variety of associations.  The AP Database deals with two related
+concepts: {\em objects} and {\em detections}.  The objects are
+descriptions of astronomical objects while the detections are the
+specific measurements of those objects, typically measured from
+astronomical images.  A collection of {\em detections} may be used to
+derive average quantities which describe a particular {\em object}.  A
+third class of object information which must also be considered are
+those supplied by external references.  These may be treated as {\em
+detections}, with the caveat that access to the raw measurements and
+metadata are usually unavailable; the reported measurements and errors
+must be accepted as they are reported.
+
+The AP Database stores the collections of detections which were
+derived from specific images from any of the analysis stages.  It
+provides a mechanism to determine and (in conjunction with the Image
+Server) locate the image from which a specific detection was derived.
+The AP Database also makes it possible to extract all detections
+derived from a specific image and to determine quantities such as the
+coordinates of the detection in pixel coordinates on the image.
+
+The AP Database also has the capability to associate multiple
+detections of a specific object.  Several major classes of objects
+will be present, each of which must be handled correctly.
+
+First, the most distant stars, compact galaxies, and QSOs will have
+nearly fixed locations relative to other nearby stars, with only small
+deviations for individual measurements.  The association between
+multiple detections of such objects is made on the basis of their
+coincident positions.  The AP Database determines the average position
+of the object and the deviations of the individual detections from
+that average on the basis of the ensemble of individual detection.
+
+Second, solar system objects do not have a fixed location.  Detections
+of such objects are linked by their orbits, and depend on both the
+position and the time of the image.  The AP Database does not attempt
+to make this link, which is the role of the MOPS system.  However, it
+has the ability to accept identifications made externally with
+specified detections and to return the identifier of the moving object
+associated with the specific detections.  These associations also
+include descriptive information such as the offset of the detection
+from the predicted location of the detection based on the orbit.  This
+functionality is required to allow the AP Database to ignore known
+moving object detections from other types of queries.
+
+Third, stars in the general vicinity of the solar system fall in
+between these first two classes of objects.  Their proper motion and
+parallax response is significant enough ($>1$ arcsec in 1 year) that
+they are not well-described by an average location and a collection of
+offsets.  These objects are described by a distance and a proper
+motion vector.  The AP Database provides the association between the
+specific detections and an average object which includes finite
+parallax and proper motion.
+
+Fourth, many detections, especially in their initial states, will not
+be associated with a specific astronomical object of any of the above
+classes and are treated as orphans.  Most of these will be spurious
+(not represent real objects), some will be from solar system objects
+for which orbits are not yet determined, some will be from faint stars
+near the detection limits, some will be from short-term transients
+which have only been detected once.  The AP Database maintains these
+detections until they have been associated with one of the objects
+above.  The AP Database provides mechanisms by which individual
+detections may be migrated back and forth between the orphan state and
+association with an astronomical object.
+
+For every object, and all orphaned detections, the AP Database also
+provides the capability to determine the images which observed the
+location of the object but for which no detection was made.  The
+minimum set of information which must be carried for these
+non-detections is the image and the associated object or orphan.
+
+The AP Database also stores the relationships between various
+photometric systems and, in some cases, the evolution of that
+relationship.  It provides mechanisms to convert between the measured
+instrumental magnitude of a detection with a specific filter,
+detector, and telescope, and at a particular time and the implied
+magnitude in the average Pan-STARRS photometry system, given a
+determined set of calibrations.  It also provides the capability to
+convert magnitudes in one system to the magnitudes in another system;
+an example of such a conversion is between the average Pan-STARRS
+filter systems and the various reference systems appropriate for those
+filters.
+
+The AP Database provides interfaces to extract lists of objects and
+detections based on various query parameters.  It provides the
+capability to extract all detections associated with a specific
+object, all non-detections of that object, all non-detections of an
+orphan, and summary statistics from these collections.  It will also
+return all objects or detections within specified spatial regions
+including regions bounded by great circles (RA,DEC; GLAT,GLON;
+ELAT,ELON) and regions described by a location and a search radius.
+It will also return the image parameters associated with a specific
+detection including image coordinates of the detection, exposure time,
+time and date of the detection, etc.
+
+\begin{table}
+\begin{center}
+\caption{AP Detection Classes \& Object Parameters\label{APdetections}}
+\begin{tabular}{lrrrr}
+\hline
+\hline
+Object Parameter & P2 & P4S & P4D & SS \\ 
+\hline
+PSF x,y, covar, $\alpha,\delta$               & + & + & + & + \\
+PSF mag, $\sigma_{\rm mag}$                   & + & + & + & + \\
+star/gal sep                                  & + & + & + & + \\
+$\sigma_x$, $\sigma_y$, $\theta$              & + & + & + & + \\
+local sky data                                & + & + & + & + \\
+Petrosian R, M, $R_{50}$, $R_{90}$            & - & + & - & + \\
+S\'ersic R, M, AB, $\phi$, $\nu$              & - & + & - & + \\
+W.L. $\gamma_1$, $\gamma_2$, pol. terms       & - & - & - & + \\
+exp. spaced aps., Poisson noise, variance     & - & - & - & + \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+The tasks and functions of the AP Database include:
+
+\begin{itemize}
+\item The AP Database accepts and stores individual detections and
+  collections of detections along with information about the image
+  which provided the detections.
+
+\item Detections are saved as one of several detection classes (P2,
+  P4$\Sigma$, P4$\Delta$, SS) and the AP Database stores the
+  appropriate parameters, listed in Table~\ref{APdetections}, for each
+  class.
+
+\item The AP Database identifies the image which provided the
+  detection, or in the case of external references, an identifier
+  specific to the reference source.
+
+\item The AP Database groups detections into objects on the basis of
+  positional coincidence and measures average parameters of those
+  objects.
+
+\item The AP Database stores parallax and proper motion parameters for
+  a subset of the average objects.
+
+\item The AP Database stores image and filter calibration information
+  necessary to convert between instrumental magnitudes and calibrated
+  magnitudes in standard systems.
+
+\item The AP Database performs at least the follow queries, with
+  constraints on the output based on at least time ranges, magnitude
+  limits, error limits:
+
+ \begin{itemize}
+ \item given $(RA,DEC)$ and a Radius, return all objects and/or
+ detections in the region.
+
+ \item given $(RA,DEC)_0$ to $(RA,DEC)_1$, return all objects and/or
+   detections in the region.
+
+ \item given $(RA,DEC)$, return closest object.
+
+ \item given object ID, return all detections.
+
+ \item given detection, return source image data.
+
+ \item given detection, return object.
+
+ \item given $(RA,DEC)$, return all images overlapping coordinate.
+
+ \item given $(RA,DEC)$ and a Radius, return all images overlapping region.
+
+ \item given $(RA,DEC)_0$ to $(RA,DEC)_1$, return all images overlapping region.
+
+ \item given detection instrumental magnitude, return derived
+   magnitudes based on calibration information.
+
+ \item given a collection of detections in a filter, determine the
+   object average magnitude in that filter.
+
+ \item given a collection of objects and detections, determine the
+   individual image zero-points.
+
+ \item given a region, return all possible combinations of the object
+   or detection magnitudes $(M_1 - M_2)$.
+
+ \item given a list of $(RA,DEC)$ entries, return all nearest objects.
+
+ \item given a filter, telescope, or detector, return all calibration
+   terms and history.
+
+ \item given a detection, return all non-detections from images which
+   overlapped the detection coordinates.
+ \end{itemize}
+
+\item The AP Database shall accept detection IDs of moving objects and
+  label the detections with the identified object.
+\end{itemize}
+
+\paragraph{Metadata Database}
+\label{IPP:MetadataDB}
+
+The IPP requires a Metadata Database to store and provide access to
+metadata of various types and from various sources.  Metadata in the
+context of the IPP represents all data which is not included in the
+two data stores discussed above (Images and Detection/Objects).
+Metadata is generated at the telescope and during the various analysis
+stages
+
+The Metadata Database stores and provides metadata for all raw images,
+for processed images, for the calibration images (both raw and
+master), for the extracted object lists.  Metadata describing the
+environmental conditions at the telescope must also be stored and
+provided as needed.  The Metadata Database stores descriptive
+information about the raw images received from the summit and the
+current state of the data processing.  The Metadata Database also
+stores descriptive information for each of the static sky images
+currently available.
+
+If analysis results are exchanged via the Metadata Database, it must
+provide access to the queried data on timescales of $<2$ seconds to
+avoid slowing down the analysis systems.
+
+The IPP also requires a Configuration Database to store and provide
+access to information about the IPP itself.  Examples of data in the
+configuration database include the default parameters for the various
+analysis programs, the description of the computing environment, the
+process status information, etc.  Software configuration parameters
+are stored in and extracted from the Metadata Database.
+User-configurable software parameters are also stored in and extracted
+from the Metadata Database.
+
+%% Metadata DB T & F
+
+The Metadata Database tasks and functions:
+
+\begin{itemize}
+\item The Metadata Database stores the classes of data listed in
+  Table~\ref{metadata}.  Thus, the Metadata Database stores and serves
+  metadata for all raw images, for processed images, for the
+  calibration images (both raw and master), for the extracted object
+  lists.  Metadata describing the environmental conditions at the
+  telescope is also stored and provided as needed.  
+
+\item The Metadata Database responds to simple queries which return
+  the data in the categories listed in Table~\ref{metadata} based on
+  the primary data key and with basic constraints of time ranges and
+  other simple conditional constraints.
+
+\item The Metadata database stores the configuration information with
+  restricted access so that only specific people may change the
+  information (eg, science parameters available to the science team;
+  software configuration parameters available to the system
+  maintainers).
+\end{itemize}
+
+\paragraph{IPP Controller}
+
+The IPP uses a group of computers to store and process images and to
+manipulate collections of detections.  These computers perform any of
+a large number of analysis stages or other processing tasks without
+significant interprocess communication.  It is necessary to have a
+mechanism which initiates computing tasks on the different computers,
+which monitors the tasks as they are executed, which handles the
+output and the errors from these tasks, and which reacts to the
+failure of any of the computing nodes.  The system responsible for the
+tasks in the IPP is the IPP Controller.
+
+The IPP Controller interacts with the collection of computers under
+its management and with other subsystems in the IPP.  The IPP
+Controller receives a variety of inputs from other subsystems,
+described below, and initiates actions such as adding a new process to
+its queue.  The IPP Controller also provides information to other
+subsystems on demand about its processing history and current state.
+Each physical computer may have multiple processors; since the IPP
+Controller is managing processing tasks, it treats each processor
+independently.  It is up to the system configuration if each computer
+needs to reserve one of its CPUs to manage background tasks or if the
+IPP Controller should attempt to send one task per CPU and let the
+kernel handle the I/O load.
+
+Computers managed by the IPP Controller are allowed to be in one of
+several states, and the IPP Controller must interact with it in an
+appropriate way for each of those states.  A computer may be {\tt
+alive}, {\tt dead} or {\tt off}.  If the computer is {\tt alive}, it
+responds to commands from the IPP Controller and may be used for tasks
+subject to other constraints.  If it is {\tt dead}, the computer is
+not responsive and must not be used for executing tasks.  The IPP
+Controller must identify computers which have died and occasionally
+test them to see if they are {\tt alive} again.  Computers which are
+{\tt off} are not available for tests and must not be tested.
+Computers may be set to the {\tt off} or {\tt dead} states by external
+subsystems; it is the responsibility of the IPP Controller to return a
+computer to the {\tt alive} state if possible.  An example scenario: a
+computer crashes.  At this point the IPP Controller should detect that
+the computer is no longer responsive and mark it {\tt dead}.  It
+should occasionally try to re-establish communication with the
+computer, potentially with longer and longer delays between attempts.
+A human could be notified if the computer seems to remain {\tt dead}
+for a very long time.  In another circumstance, a person needs to work
+on a computer.  They should have the ability to notify the IPP
+Controller that the machine is off, perhaps with a prior notification
+that the machine should be prepared to go off.  Only when the person
+is done working and testing the machine, and tells the IPP Controller
+that the machine is now {\tt dead} can the IPP Controller attempt to
+re-start communications and processing on that computer.
+
+CPUs on computers which are in the {\tt alive} state may be in one of
+two modes: {\tt busy} and {\tt free}.  A CPU which is {\tt busy}
+currently has a task assigned to it.  The IPP Controller may only
+assign one task to one CPU at a time.  A CPU which is in the {\tt
+free} state may have tasks assigned to it.  The IPP Controller must
+also respect a list of task restrictions which may require specific
+tasks to run on specific CPUs or exclude specific tasks from specific
+CPUs.
+
+The IPP Controller accepts tasks from other IPP subsystems.  The task
+requests include the specific command to be executed and are in the
+form of a UNIX command which could be performed on any of the
+computing nodes.  Any input or output data structures in the commands
+must be a valid resource regardless of the node on which the task is
+executed.  Input and output data resources must be unique where
+necessary to avoid conflicts.  The IPP Controller gives each task a
+unique identifier, which is returned to the requesting agent.  The
+agent may then use that ID to obtain status information on that task
+or to send control signals to the specific task.
+
+Task requests may specify a desired node for the task execution.  The
+IPP Controller attempts to honor the request if the node is {\tt
+alive}, but will execute it on another node if the requested one is
+{\tt dead} or {\tt off}.  Even if a node is {\tt alive}, the IPP
+Controller will choose another node if the specified task is not
+allowed on the requested node.  In all other cases, the IPP Controller
+waits until the currently executing processes, and processes with
+higher priority, are completed before executing the specified task on
+the requested node.
+
+Task requests may specify an urgency level.  The IPP Controller
+determines the priority of the task on the basis of both the priority
+and the age of the request.  An executing task must be completed on a
+CPU before any new task is started on that CPU, regardless of
+priority.  Tasks may be assigned a priority of 0 in which case they
+are maintained in the queue and never executed.
+
+The IPP Controller monitors the output streams from the executing
+tasks and the exit status of the tasks.  Each task is associated with
+a log file, to which all output is written.  The status, including the
+exit status, of each task is maintained by the IPP Controller so that
+other subsystems may determine if specific tasks have started or
+completed.
+
+The IPP Controller must accept commands from other IPP subsystems.
+These commands include those which govern the processing of specified
+tasks, those which govern the behavior of specific computing nodes,
+and those which request information from the IPP Controller.  The IPP
+Controller must be able to halt the execution of a specified task,
+delete an unexecuted task from the task list, change the priority of
+tasks, and change the requested nodes for tasks.  The IPP Controller
+must also be able to stop the current execution of a task and push it
+to the end of the queue and also change its priority.
+
+The IPP Controller must honor requests (normally from the users) to
+change the mode of any computing node on demand between {\tt off} and
+{\tt dead}.  This would normally be done after a computer has been
+rebooted and is release to the IPP Controller for its use.  It must
+also be able to change the list of allowed tasks as requested by
+external commands.
+
+The IPP Controller must respond to informational requests regarding the
+collection of machines and their states as well as the collection of
+tasks and their states.  The IPP Controller must monitor the execution
+times of the different tasks and provide summary statistics.  Finally,
+the IPP Controller must respond to three top-level commands: {\tt finish},
+{\tt stop} and {\tt abort}.  When {\tt finish} is requested, no more
+new tasks are accepted on the stack of task, and when all tasks in the
+stack have completed, the IPP Controller must exit.  When {\tt stop} is
+requested, the currently executing tasks must be completed at which
+point the IPP Controller must exit, but tasks remaining in the stack which
+have not been started are flushed.  When {\tt abort} is issued, the
+IPP Controller immediately kills all executing tasks and exits.
+
+The IPP Controller and the IPP Image Server have related needs for
+information from the combined storage-and-processing nodes regarding
+which nodes are available.  It is not yet clear if this information is
+best stored in a single location (either IPP Controller or IPP Image
+Server), which provides the information to other systems on demand, or
+if both systems should maintain the information.  Also, it may be
+necessary to distinguish nodes which are available for processing from
+those that are available to serve data as part of the IPP Image
+Server.
+
+It may be useful for the Controller to distinguish between tasks
+dominated by I/O and tasks dominated by data processing.  It is
+possible that one of each of these types of tasks may be sent to the
+same node without significantly impacting the system performance.
+Alternatively, it may be necessary to limit a single machine with 2
+CPUs to only one of each of these types of tasks (i.e., one processor
+will be working on I/O while the other is working on processing).
+Such details will be studied by the IfA IPP Team.
+
+%% IPP Controller T & F
+
+ IPP Controller tasks and functions:
+
+\begin{itemize}
+
+\item On startup, the IPP Controller attempts to establish
+  communication with all of its computers and set their state to be
+  {\tt alive} or {\tt dead} based on the success of the
+  connection.
+
+\item The IPP Controller detects computers which crash or stop
+  responding and set their state to {\tt dead}.
+
+\item The IPP Controller attempts to re-establish communication with
+  {\tt dead} computers.
+
+\item The IPP Controller accepts tasks from external users and
+  systems, which may specify a desired CPU (node) and priority in
+  addition to the task command.
+
+\item The IPP Controller attempts to run pending tasks on the desired
+  node, if available (not {\tt dead} or {\tt off}).
+
+\item If the node is unavailable, the IPP Controller attempts to run
+  the task on another node.
+
+\item If the node is available, the IPP Controller attempts to run a
+  given task only if no higher-priority tasks are available and no
+  task is currently being executed.
+
+\item The IPP Controller monitors the output from the task and writes
+  it to an associated log destination.
+
+\item The IPP Controller monitors the execution status of each task
+  currently executing on a node and performs the following actions:
+
+  \begin{itemize}
+  \item identify the task as successful if it has a valid exit status.
+  \item identify the task as unsuccessful if it has an error exit status.
+  \item identify the task as unattempted if the computer crashed.
+  \end{itemize}
+
+\item The IPP Controller accepts and performs the following external
+  commands:
+  \begin{itemize}
+  \item add a task to the pending task list.
+  \item delete a specific task from the pending task list.
+  \item return the current status of a specific task.
+  \item return a list of all pending and non-pending tasks.
+  \item set a specified computer state to {\tt off} or {\tt dead}.
+  \item restrict a specified CPU to a class of tasks.
+  \item halt execution of a specified task.
+  \item set the IPP Controller state to {\tt finish}, {\tt abort}, or {\tt stop}.
+  \end{itemize}
+\end{itemize}
+
+\paragraph{IPP Scheduler}
+\label{IPP:IPPscheduler}
+
+The IPP is responsible for a variety of analysis tasks: processing of
+the science images through several stages; routine assessment of the
+detrend (instrumental calibration) images used in processing the
+science images; construction of replacement detrend images when
+needed; generation of astrometric and photometric reference catalogs
+based on the collected dataset; and the performance of test analysis
+programs.  At any point, decisions need to be made about which of
+these tasks should be performed, based on an analysis of the contents
+of the metadata database, the requirements of the people monitoring
+the IPP, and the near-term observing plans.  The IPP Scheduler is the
+mechanism that assesses these various inputs to guide the decisions
+and initiate the actions.
+
+The IPP Scheduler acts as an intermediary between several components
+of the IPP and also between the IPP and external agents such as OTIS
+and the users who must monitor the behavior of the IPP.
+
+The IPP Scheduler sends commands to the IPP Controller for execution.
+While the IPP Scheduler chooses the tasks to be performed, it is the
+IPP Controller's responsibility to manage the specific tasks executing
+on a given processing node.  Examples of these tasks include the
+process of copying or moving data from the Summit data systems to the
+IPP Image Server; image processing analysis stages performed on the
+science images by the appropriate processing nodes; and the analysis
+of the data in the AP Database.  This division of responsibilites
+allows us to isolate and encapsulate the functionality of the IPP
+Scheduler and the IPP Controller.  With this separation, the IPP
+Controller does not need to have any information about the details of
+the tasks which it executes, while the IPP Scheduler does not need to
+have detailed information about the available computer hardware.
+
+Communication between the IPP Scheduler and the IPP Controller is
+bi-directional; the IPP Scheduler sends tasks to the IPP Controller,
+while the IPP Controller informs the IPP Scheduler of the outcome of
+those tasks.  It is not specified whether the IPP Scheduler and IPP
+Controller are components of a single software system or interacting
+but distinct software components.
+
+The IPP Scheduler takes as input the current list of pending images,
+both science and calibration, and a description of the current
+observing plan or strategy on some time-scale.  The IPP Scheduler also
+takes input from humans who manage the IPP.
+
+The IPP Scheduler must choose between several types of analysis tasks
+based on the contents of those lists and on the requirements of the
+users.  The list of tasks which the IPP Scheduler must decide between
+includes:
+\begin{itemize}
+\item moving data from the Summit pixel server ($\sim 30$ second timescales)
+\item running the science analysis stages ($\sim 30$ second timescales)
+\item testing the validity of the current detrend images ($\sim$
+  nightly)
+\item constructing new detrend images ($\sim$ weekly)
+\item updating and improving the photometric and astrometric reference
+  catalogs ($\sim$ yearly).
+\end{itemize}
+
+The IPP Scheduler chooses between tasks which are relevant on several
+different time-scales.  The time-scales range from 2 times per minute
+to once or twice a year, as noted in the list above.  The IPP
+Scheduler must also make use of user input in managing such choices.
+Users have the option to specify that a particular task or set of
+tasks is of higher or lower priority than the norm.
+
+The IPP Scheduler maintains a set of rules that define the dependency
+of one type of analysis stage on other analysis products.  For
+example, the nightly science image processing depends on the existence
+of valid detrend images.  The IPP Scheduler must be able to recognize
+the dependency and initiate the required analysis needed to perform
+other analysis tasks.  The IPP Scheduler must have the ability to
+decide between postponing an analysis task until the required data are
+available or initiating the task using a lower-quality or less
+appropriate substitute.  For example, in normal circumstances, a
+science image must not be processed until the corresponding detrend
+frame has been produced.  However, if such a frame is unlikely to
+appear soon, and the pressure to process the science image is
+sufficiently high, then the frame could be processed with an older
+detrend frame of known lower quality.  The IPP Scheduler must have the
+ability to choose the best, if not ideal, reference data for a
+particular circumstance.  Note that these rules are defined for the
+IPP Scheduler in an abstract way as a relationship between analysis
+stages, rather than as a specific rule relating one task to another
+task.
+
+The IPP Scheduler defines the operating state of the IPP.  When the
+IPP is in the {\em automatic state}, the IPP Scheduler performs the
+most appropriate of all possible tasks at a particular time.  When the
+IPP is in the {\em interactive state}, the IPP Scheduler performs only
+the requested action regardless of the outcome of the decision trees.
+In addition, in the interactive state, the IPP Scheduler must only
+perform the requested actions and not attempt to perform the other
+normally-required actions.  The only exception to this exclusion is
+that, in the interactive state, data is still copied from the summit
+system.  An additional IPP state is the {\em paused state}, intended
+for tests or maintenance, in which case the IPP Scheduler does not
+perform even the data copy tasks.  Every task is performed on demand
+by the user.  The user command sets the IPP Scheduler in one of these
+three states, {\em automatic}, {\em interactive}, and {\em paused}.
+
+%% IPP Scheduler T & F
+
+The IPP Scheduler tasks and functions:
+
+\begin{itemize}
+\item The IPP Scheduler sends the analysis tasks which it initiates to
+  the IPP Controller.
+
+\item All analysis tasks sent by the IPP Scheduler include a complete
+  UNIX command with necessary arguments, the priority of the task, and
+  optionally the desired processing node.
+
+\item When the IPP Scheduler is placed in the {\em paused state}, it
+  only initiates User-requested tasks.
+
+\item When the IPP Scheduler is placed in the {\em interactive state},
+  it initiates User-requested tasks as well as data transfer tasks.
+
+\item When the IPP Scheduler is placed in the {\em automatic state},
+  it initiates the most appropriate task based on the inputs and
+  dependency rules.
+
+\item The IPP Scheduler sends the exit status of the analysis tasks to
+  the appropriate destination as defined by the task dependency table.
+\end{itemize}
+
+\subsubsection{Analysis Stages}
+\label{IPP:AnalysisStages}
+
+\begin{figure}
+\begin{center}
+\resizebox{6.4in}{!}{\includegraphics{pics/IPPstages}}
+\caption{ \label{stages} IPP Analysis Stages}
+\end{center}
+\end{figure}
+
+\paragraph{Overview}
+
+We now consider the collection of analysis tasks which must be
+performed by the IPP.  These tasks represent the core of the required
+IPP functionality; the architectural components discussed above can be
+viewed as primarily supporting infrastructure to enable the analysis
+tasks to be executed on the appropriate data and to store the results.
+The tasks are divided into well-defined analysis stages, each of which
+operates on a particular unit of data, such as a single OTA image or a
+collection of astronomical objets.
+
+Depending on the analysis stage, the basic data unit may be individual
+images, collections of images, or derived data products such as a
+collection of detections of astronomical objects.  Because of the
+granularity of these data units, a large number of analysis tasks
+representing the same analysis stage many be performed in parallel.
+This is particularly true because the analysis tasks from any
+particular stage do not depend on the results of another analysis task
+from that stage.  For example, the intial analysis of a chip from one
+image does not depend on the results from another chip.  The analysis
+stages are divided into three categories, and further subdivided as
+follows:
+
+\begin{enumerate}
+ \item {\bf Science Image Analysis} is performed on the night-sky
+ science images to extract the science data from these images.  The
+ science image analysis is divided into 4 analysis stages, which we
+ call Phases 1 - 4:
+
+ \begin{itemize}
+  \item {\bf Phase 1:} The image processing preparation stage, in
+  which basic astrometric analysis of the complete FPA image is
+  performed.
+
+  \item {\bf Phase 2:} The image reduction stage, in which the
+  individual detector images (OTAs) are processed as much as possible
+  without reference to other chips in the same FPA image or other
+  exposures.
+
+  \item {\bf Phase 3:} The exposure analysis stage, in which the
+  results of the multiple detectors are combined to improve the
+  calibrations for the complete FPA images. 
+
+  \item {\bf Phase 4:} The image combination stage, in which several
+  different exposures of the same part of the sky are combined to
+  produce high-quality difference and summed images.
+ \end{itemize}
+
+ \item {\bf Calibration Image Analysis} is required to generate the
+ calibration images used in the science image analysis.  There are
+ many different types of calibration images which must be produced,
+ some of which are derived from other calibration images.  The type of
+ calibration image is generated by its own analysis stage.  These
+ include the construction of simple bias, dark, and flat-field stacked
+ images, the generation of flat-field correction frames on the basis
+ of stellar photometry, and the construction of sky-model and
+ fringe-model images.  
+
+ \item {\bf Reference Catalog Creation} is required by the IPP to
+ generate improved astrometric and photometric reference catalogs on
+ the basis of Pan-STARRS observations.
+
+\end{enumerate}
+
+Figure~\ref{stages} shows the flow of data between the various IPP
+software subsystems and the different analysis stages.  The thick
+lines represent the flow of pixel data, the thin lines represent the
+flow of metadata and object data, and the grey lines represent the
+flow of commands.  The hatched systems represent external Pan-STARRS
+systems (OTIS, the Sky Server, the PSPS Object Database, the
+Moving/Transient Object Pipeline, and other Client Science Pipelines.
+
+The individual analysis stages are implemented as UNIX command-line
+programs.  Each command performs the action of the stage on a single
+quantum of data.  These analysis stages are built of lower-level
+C-functions which may be wrapped in a higher-level programming
+language such as Perl or Python.
+
+As discussed above (section~\ref{IPP:IPPscheduler}), the decision to
+execute a specific analysis stage for a specific dataset is made by
+the IPP Scheduler, which sends the infomation to the IPP Controller.
+The IPP Controller executes the analysis tasks constructed by the IPP
+Scheduler for that stage on an appropriate machine and monitors the
+success or failure of the job.
+
+An important design decision which must be addressed by the IfA IPP
+Team is the question of how the analysis stages determine
+configuration and reference data needed by the analysis.  In one
+scenario, it is always the responsibility of the low-level function to
+perform the necessary query of the reference databases.  In an
+alternative scenario, it is the responsibility of the scheduler to
+extract that information and send it as part of the processing
+command.
+
+\paragraph{Science Image Analysis}
+
+The Science Image analysis stages together represent the primary data
+analysis performed by the IPP.  The science image analysis which is
+performed by the IPP consists of:
+
+\begin{itemize} 
+\item detrending the images to remove the instrumental signature
+
+\item astrometric and photometric calibration of the individual images
+
+\item merging a collection of several images of the same portion of
+the sky obtained over a short period of time to remove image defects
+and gaps
+
+\item subtracting the appropriate reference static-sky image
+
+\item cleaning the image of any transients
+
+\item adding the cleaned image to the static sky
+
+\item object detection of images at specific stages
+\end{itemize}
+
+We have divided the analysis steps into four analysis stages, which we
+call Phases 1 - 4.  Each of these analysis stages deals with a single
+data unit.  
+
+\paragraph{Phase 1 : image processing preparation}
+
+The Phase 1 analysis stage is performed on each science exposure (each
+complete FPA image) to calculate basic astrometric data needed by the
+later stages.  Phase 1 uses the static (pre-determined) telescope
+distortion model and a table of nominal OTA positions and rotations,
+combined with the guide star pixel and celestial coordinates, to
+determine the correct telescope bore-sight, field rotation and
+magnification.  The guide star coordinates are loaded from the
+Metadata database.  These calculations are performed by comparing the
+observed guide star detector coodinates with the known astrometic
+positions of these same stars as reported by an external astrometric
+reference.  The accuracy of the resulting astrometric solution is
+expected to be \tbr{1 arcsec} across the field, sufficient in later
+stages to match the vast majority of astrometric reference stars with
+their detections with minimal effort.
+
+In some circumstances, science images may have no guide stars.  This
+may occur in the Pan-STARRS system if the detectors are not run in OTA
+mode, for example for short snapshot images.  This may also be the
+case if the IPP is being run on non-Pan-STARRS data.  In such a
+circumstance, the Phase 1 stage uses the provided boresight
+coordinates, exposure time, and camera zero-point to predict the pixel
+coordinates of known bright stars expected to be found on the
+detectors.  It then extracts a large box ($\sim$ 30 $\times$
+30\arcsec) around these locations and performs extremely basic object
+detection to determine the detector coordinates of those bright stars
+which are not saturated but which are significantly above the
+background level.  By targetting known locations in the image files,
+only a small amount of data will have to be read.
+
+If the image has invalid coordinates or no detectable bright stars,
+Phase 1 fails and reports a descriptive error.
+
+Given the above astrometric solution, the Phase 1 analysis stage
+constructs a table of the overlaps between the science image to be
+processed and the static sky images that must be constructed.  This
+table will be used to guide the processing of the static sky in Phase
+4.  The overlaps should be generously calculated so that small errors
+in astrometry at Phase 1 will not cause any valid static sky / science
+image pairs to be missed because of the astrometric error at this
+phase.  It is acceptable for a small number of invalid overlaps to be
+identified as these will be excluded in Phase 4.  Static Sky cells
+which do not have sufficient science image overlap \tbr{$< 5\%$} need
+not be processed because the few new measured pixels do not add
+significantly to the Static Sky.
+
+Phase 1 is the image processing preparation stage.  The analysis is
+performed on a complete FPA.  At the end of this analysis, the FPA is
+ready to be analysed in detail in Phase 2.  The Phase 1 tasks and
+functions are:
+
+\begin{itemize}
+
+\item Extract FPA guide stars to determine astrometry across the full FPA
+
+\item If no guide stars are available, phase 1 must measure the pixel
+  coordinates of known bright stars expected in the field from the
+  image data.
+
+\item The total number of stars and size of the bright-star
+  acquisition box shall be a user-configurable parameter in the range
+  20 - 250.
+
+\item Calculate the Image cell / Sky cell overlaps for each image.
+  Sky cells which do not have sufficient science image overlap $< 5\%$
+  are excluded from the overlap table.
+
+\end{itemize}
+
+\paragraph{Phase 2 : image reduction}
+\label{IPP:Phase2}
+
+The Phase~2 analysis is the detrend stage, in which the images from
+the detector are processed to remove instrumental signatures.  In
+addition, basic object detection is performed along with improved
+astrometric and photometric calibration.  In each step of the analysis
+process, an image mask and noise map must be constructed and updated
+when appropriate.  The following operations may be applied during the
+Phase~2 processing:
+
+\begin{enumerate}
+\item Convolve detrend images with the OT kernel, if appropriate
+\item Flag bad and saturated pixels
+\item Bias correction via overscan subtraction
+\item Dark
+\item Trim object image to remove overscan and edges corrupted by OT
+\item Correct for non-linearity
+\item Cross-talk
+\item Flat-field correction
+\item Sky \& Fringe subtraction
+\item Identify CRs
+\item Find objects in the image
+\item Model the PSF variations across the image
+\item Make postage stamps of bright objects.
+\end{enumerate}
+
+Of the calibration steps, some may be skipped if they do not
+contribute to an improved image.  The decision to apply or skip a
+particular step is determined by the Phase 2 recipe, which may specify
+exposure time or flux limit cutoffs for some of the steps.
+
+\subparagraph{Convolve detrend images with the OT kernel}
+
+Certain detrend images are convolved by the OT kernel, so that they
+accurately represent the detrend images appropriate for the object
+images, which have been shifted using OT.  The detrend images which
+must be convolved include: the flat-field and the
+high-spatial-frequency fringe images. The appropriate kernel for each
+cell of an OTA must be determined from the guide star history,
+extracted from the IPP Metadata Database\footnote{or image header}.
+If the OT kernel is not available, the convolution is skipped, with a
+warning.
+
+\subparagraph{Flag bad and saturated pixels}
+
+A static bad pixel mask is used to identify pixels which are known to
+be bad in the camera.  This mask is then processed with the science
+image. Bad pixels which are charge traps are grown by the extent of
+the OT convolution kernel.  Bad pixels above a charge trap (i.e.\ bad
+colums) must not be grown, since they were not affected by pixel
+shifting, but only became bad at read-out.
+
+Pixels which are saturated in the A/D converter, or with a signal
+level at which the response is excessively non-linear, must also be
+masked, and this area must be grown by an additional pixel to mask
+excess charge spillover.
+
+The bad pixel mask must be carried with the science images.  Different
+bits must be set to identify different reasons for masking the pixel.
+
+\subparagraph{Bias correction via overscan subtraction}
+
+The image bias must be subtracted. Since different detectors behave in
+different ways, several options for modelling the bias are available.
+The bias is measured from the image overscan region.  The bias
+subtraction method must be capable of subtracting a single constant
+from the complete image, or to subtract a 1-D bias which varies as a
+function along the overscan.  The function used to represent the
+overscan region may be a spline or a chebychev polynomial derived from
+the data values along the overscan.  The values used to determine both
+the single constant or the inputs to the spline and polynomial fits
+are derived from groups of pixels on the basis of one of several
+statistics, including the sample and robust mean, median, and modes.
+In the case of a single constant, all of the overscan pixel values are
+used in the calculation of this statistic.  In the case of the 1-D
+functional representation, the input values to the fit must represent
+the coordinate along the overscan, with the statistic derived from the
+pixels in the perpendicular direction at each location.  Sigma-clipping
+on the input data values must be an option.
+
+\subparagraph{Trim object image}
+
+The image is trimmed to remove the non-imaging pixels, such as the
+overscan and any pre-scan pixels, along with those pixels near the
+edges that have been compromised due to OT operation.  The definition
+of the imaging area of the detector is determined from the camera
+configuration data or from the metadata associated with the image,
+with the choice a user-configurable option.
+
+\subparagraph{Correct for non-linearity}
+
+If required, the object image (after bias correction) must be
+corrected for the effects of non-linearity through a provided
+polynomial fit to the pixel data values or a numeric lookup table as a
+function of pixel flux.  The choice to apply the correction must be
+set by the user.
+
+\subparagraph{Flat-field correction}
+
+The object image (after bias correction and non-linearity correction)
+must be corrected for sensitivity variations as a function of
+position, dividing by a flat-field image.  
+
+\subparagraph{Sky \& Fringe subtraction}
+
+After the science image has been flat-fielded, the flux contribution
+of the sky (from both continuum emission and the line emission that
+causes fringing) must be subtracted from the image.  The subtraction
+needs to remove background (technically, foreground) variations which
+are not celestial but generated in the atmosphere or by more localized
+scattering.  This background should include the contribution from the
+zodiacal light.  This background subtraction does not address the
+artifacts generated by bright stars: bleeding columns, ghosts, or
+other localized reflection effects.  This process also produces a
+superbinned image of the background map which may be used as a
+debugging diagnostic.
+
+\subparagraph{Identify `cosmic rays'}
+
+Charged particles in the detector frequently cause features which do
+not have the morphology of astronomical objects.  In a well-sampled
+image, these may be easily identified by the sharpness of the image.
+In a near critically-sampled image, these `cosmic rays' may be
+indistinguishable from stellar objects.  The IPP must have the
+capability of making the morphological identification of cosmic rays
+if the imaging data is sufficiently well sampled.  The identified
+cosmic rays should be masked with a configurable growth factor so that
+additional pixels beyond the detected pixels in the feature are also
+masked.
+
+\subparagraph{Find objects in the image}
+
+After the image have been processed by the preceeding steps, the Phase
+2 analysis performs a basic object detection analysis.  Objects on the
+flat-fielded object image are found, and general parameters are
+measured.  Object detection is performed at several stages by the IPP,
+with different object parameters measured in each case.
+Table~\ref{APdetections} gives a list of the different detection
+stages and the object parameters measured for those stages.  For the
+Phase 2 analysis, the object parameters are: the object centroid and
+the position covarience matrix, the instrumental PSF magnitude and
+error, local background level and error, a measurement of the
+star-galaxy separation, and a measurement of the object shape
+($\sigma_x, \sigma_y, \theta$).  The detection threshold must be
+configurable, and be a function of the average background flux or the
+image noise map.  Minimal object classification must be performed to
+distinguish objects which are consistent with a single PSF, objects
+which are inconsistent, and objects which are saturated.  The
+resulting collection of detected objects are saved in the AP Database
+along with the relevant image metadata (\ie filter, exposure time,
+etc).  In addition, this process constructs a model of the
+point-spread-function (PSF) as a function of position in the image.
+This PSF model is saved as part of the image metadata.
+
+\subparagraph{Astrometry}
+
+The astrometric parameters determined in Phase 1 have an accuracy of 1
+arcsec, sufficient to allow easy association between the newly
+detected objects and many reference objects in the image.  In Phase 2,
+the detected objects are matched with known astrometric reference
+objects, using reference object coordinates which have been adjusted
+for proper motion.  The matches are then used to improve the
+astrometric parameters for the individual OTAs.  The OTA astrometric
+parameters which are determined may include terms up to 3rd order in
+position, though the terms which are actually fitted are
+user-configurable.  The Cell astrometric parameters are not allowed to
+vary at this stage.  The fit must be robust, rejecting outlier matches
+(either stars with poorly determined proper motion or spurious
+matches).  The resulting astrometric solution is consistent across the
+OTA field to within \tbr{0.2 arcsec}.
+
+\subparagraph{Postage Stamps}
+
+The IPP must have the capability of extracting regions surrounding a
+specified subset of objects from the flattened images.  These postage
+stamp images must be saved for additional use by client science
+pipelines.  The identification of these objects must be on the basis
+of a set of rules applied to the object's magnitude and position.  The
+postage stamps are not restricted in shape to simple rectangles, but
+may represent more complex regions.  They are written the Image
+Server.
+
+%%
+
+Phase 2 is the detrend stage, in which each detector is separately
+processed to remove instrumental signatures.  The result of Phase 2 is
+an image with high-quality astrometric and photometric calibrations, a
+collection of objects detected in the image and characterized in a
+rudimentary way (star / non-stellar), and a measurement of the PSF
+across the detector.  
+
+The tasks and functions of Phase 2 are as follows:
+
+\begin{itemize}
+
+\item Convolve the flat-field and high-spatial-frequency fringe images
+  with the OT kernel.
+
+\item Mask ghosts of bright stars which introduce residual feature
+  more significant than 1\% of the background.
+
+\item Bias subtract the image.
+
+\item Correct each chip independently for non-linearity.
+
+\item Flat-field correct the image.
+
+\item Subtract a fit to the detector-dependent fringing pattern.
+
+\item Subtract a fit to the low-spatial frequency sky background.
+
+\item Identify `cosmic rays' on the basis of morphology.
+
+\item Perform (positive) object detection on the processed images,
+  down to a user-configured threshold, likely to be $\sim 20\sigma$.
+  The detection threshold may optionally be a function of the average
+  background flux or the local noise level.
+
+\item Measure the following object parameters:
+
+  \begin{itemize}
+  \item object centroid and position errors.
+  \item an extended object position ($x_g, y_g$).
+  \item instrumental PSF magnitude and error.
+  \item local background level and error.
+  \item second moments ($\sigma_{\rm min}, \sigma_{maj}$) of the object
+    and their covariance matrix.
+  \end{itemize}
+
+\item Perform minimal object classification to distinguish objects
+  which are consistent with a single PSF, objects which are
+  inconsistently large, objects which are inconsistently small, and
+  objects which are saturated.
+
+\item Match the detected objects with known astrometric reference
+  objects, including proper-motion compensation.
+
+\item Fit the reference and detected object coordinates to determine
+  astrometric parameters for the individual OTAs, including
+  polynomials of the coordinates up to 3rd order (user-specified
+  parameter).  The Cell astrometric parameters are not allowed to vary
+  in the fit, which uses outlier rejection to determine a robust
+  solution. 
+
+\item Extract subrasters (`postage stamps') surrounding a
+  user-specified list of coordinates from the flattened
+  images, to be saved in the IPP Image Server.
+
+\item measure the PSF variation as a function of detector position.
+
+\end{itemize}
+
+\paragraph{Phase 3 : exposure analysis}
+
+The Phase 3 analysis stage works with the results from a complete FPA
+obtained during Phase 2 to improve the photometric and astrometric
+calibrations.  
+
+Phase 3 uses the objects detected in Phase 2, matched with an
+appropriate reference catalog, to determine the image photometric zero
+point and zero-point variations across the field.  If zero-point
+variations are significant, the zero-point variations are modeled with
+a Chebychev polynomial correction of order 3 or less.  The complete
+FPA image must be categorized as photometric or not on the basis of
+the zero-point consistency, comparisons between the zero-point of the
+image and recent longer-term (week or month long) measurements of the
+zero-point, and the external indicators of photometricity.  In
+addition, statistics of the transparency are measured and saved as
+part of the related Metadata.
+
+Phase 3 also uses the objects detected in Phase 2, matched with an
+appropriate reference catalog, to determine improvements to the
+astrometric solutions.  The improved solution is determined by fitting
+a new distortion model appropriate to this image.  The resulting
+astrometric accuracy is limited by the astrometric reference
+catalog. (see Table~\ref{AstroRefs} below).
+
+Phase 3 also uses the individual measurements of the background and
+the superbinned background maps to generate an improved background map
+over the entire FPA.  The large-scale background correction is
+determined on the basis that the background should be smoothly varying
+between different chips (OTAs).
+
+Phase 3 also uses the individual chip models of the PSF variations to
+model the global PSF variations across the field.  There will be
+discontinuities at the chip boundaries due to charge diffusion and
+chip displacements along the optical axis, but there will also be an
+overlying trend due to the local coherence of atmospheric seeing
+variations. 
+
+%%%
+
+The Phase 3 analysis uses the objects detected in Phase 2 and external
+reference catalogs to determine improved photometric and astrometric
+calibrations for the FPA as a whole, and to improve the measurement of
+the PSF and sky variations across the field.  The Phase 3 tasks and
+functions are as follows:
+
+\begin{itemize}
+
+\item Phase 3 uses the objects detected in Phase 2, matched with a
+  user-specified reference photometry catalog, to determine the image
+  photometric zero point and zero-point variations across the field.
+
+\item If zero-point variations are significant ($> 0.01$ mag
+  peak-to-peak), the zero-point variations are modeled with a
+  polynomial correction of order 3 or less.
+
+\item The photometric nature of the FPA image is categorized on the
+  basis of the zero-point consistency, the transparency compared with
+  recent long-term measurements in the filter, and the external
+  indicators of photometricity.
+
+\item Phase 3 uses the objects detected in Phase 2, matched with an
+  appropriate astrometric reference catalog, to improve the distortion
+  model used for the image.  The resulting astrometric accuracy is
+  consistent across the field to 30 mas, and is limited by the
+  astrometric reference catalog, (eg, 100 - 250 mas for
+  USNO-B1.0).
+
+\item The Phase 3 analysis modifies the background correction of Phase
+  2 based on the full-field statistics to achieve an accuracy of 1\%
+  of the background.
+
+\end{itemize}
+
+\paragraph{Phase 4 : image combination}
+\label{IPP:Phase4}
+
+Phase 4 is the image combination stage, in which multiple images of
+the same portion of the sky are merged and confronted with the static
+sky image.  Phase 4 operates on the smallest data unit of the static
+sky, the sky cell, along with the associated pixels from a collection
+of images which have been processed through phases 1--3.  The size and
+exact representation of a static sky cell are yet to be determined.
+The working concept is that the static sky cells contain roughly the
+same number of pixels as an OTA (4k x 4k) and represent a portion of a
+local tangent plane projection.  As mentioned above
+(Section~\ref{IPP:ImageServer}), the pixel scale of the static sky is
+planned to be 0.2\arcsec, somewhat smaller than the 0.3\arcsec\ raw
+image pixel scale.
+
+For each sky cell, the corresponding pixels are extracted from the
+exposures being processed and mapped to the projection of the sky
+cell. The pixels from the multiple input processed images are combined
+into a single, cleaned image.  Outlier pixels may be optionally
+rejected at this stage (optionally, since moving objects will be
+rejected in images obtained over a wide range of times).  This image
+is then confronted with the static sky cell data to produce a
+difference image.  Residual objects in the difference image above a
+threshold are detected and excised from the original cleaned image.
+The remaining pixels are added to the existing static sky image.
+Object detection must be performed on the difference and cleaned
+images.
+
+Objects in the difference image are detected and a specific set of
+object parameters are measured from these detections.
+Table~\ref{APdetections} gives a list of the different detection
+stages and the object parameters measured for those stages.  For the
+Phase 4 difference image (P4$\Delta$), the measured object parameters
+consist of: the object centroid and the position covarience matrix,
+the instrumental PSF magnitude and error, local background level and
+error, a measurement of the star-galaxy separation, and a measurement
+of the object shape ($\sigma_x, \sigma_y, \theta$).  The detection
+threshold must be configurable, and be a function of the average
+background flux or the image noise map.  Minimal object classification
+must be performed to distinguish objects which are consistent with a
+single PSF, objects which are inconsistent, and objects which are
+saturated.  The resulting collection of detected objects are saved
+along with the relevant image metadata (\ie filter, exposure time,
+etc).
+
+Objects in the cleaned, summed image are detected and a specific set
+of object parameters are measured from these detections.
+Table~\ref{APdetections} gives a list of the different detection
+stages and the object parameters measured for those stages.  For the
+Phase 4 summed image (P4$\Sigma$), the measured object parameters
+consist of: the object centroid and the position covarience matrix,
+the instrumental PSF magnitude and error, local background level and
+error, a measurement of the star-galaxy separation, a measurement of
+the object shape ($\sigma_x, \sigma_y, \theta$), the Petrosian radius,
+magnitude, axis ratio, and angle; and the S\'ersic radius, magnitude, axis
+ratio, angle, and parameter $\nu$.  The detection threshold must be
+configurable, and be a function of the average background flux or the
+image noise map.  Minimal object classification must be performed to
+distinguish objects which are consistent with a single PSF, objects
+which are inconsistent, and objects which are saturated.  The
+resulting collection of detected objects are saved along with the
+relevant image metadata (\ie filter, exposure time, etc).  In this
+measurement, objects at known positions will also be measured even if
+they have not been detected.
+
+Objects which are detected in both of the Phase 4$\Sigma$ and Phase
+4$\Delta$ images are saved to the AP Database, along with the relevant
+image metadata (\ie filter, exposure time, etc).  In the process of
+adding these objects to the database, the transients which are
+correlated with previous detections of an object (and those which are
+not) will automatically be determined.  An independent process will
+query the AP Database for such transient objects of interest which are
+to be sent, along with their associated metadata, to the MOPS and
+other science client pipelines.  This step must be performed at least
+once per night.
+
+It is essential that the static sky image (which may have been
+painstakingly accumulated over many months) not be corrupted by adding
+in transient sources, or data that is of suspect quality (due, e.g.,
+to an error upstream in the processing).
+
+Object analysis of the static sky images is {\em not} a part of the
+Phase 4 analysis.  This processing is envisioned to take place
+relatively infrequently (perhaps weekly or even monthly) and is
+scheduled as a separate analysis task, probably run during the day at
+a time when the computing infrastructure is not under significant load.
+
+%% 
+
+Phase 4 is the image combination stage, in which multiple images of
+the same portion of the sky are merged and confronted with the static
+sky image.  The Phase 4 tasks and functions are as follows:
+
+\begin{itemize}
+
+\item The Phase 4 analysis determines the corresponding set of image
+  pixels for a given sky cell.
+
+\item These pixels are extracted from the input images, using the
+  astrometric information for each OTA and Cell to determine the exact
+  overlaps.
+
+\item The Phase 4 analysis skips any sky cells with fewer than 5\% of
+  their pixels overlapping the input images.
+
+\item Pixels which have been extracted from the input images are
+  geometrically warped to match the corresponding pixels in the sky
+  image.  This transformation is based on the measured astrometric
+  solution for the input images relative to the reference catalog used
+  to generate the static sky image.  The warping may use a
+  locally-linear astrometric solution to speed the processing.
+  
+\item Phase 4 determines the appropriate photometry scaling factors
+  needed to combine the images photometrically.
+
+\item When multiple images are combined, the group of input pixels
+  which contribute to an output pixel are examined and pixels from the
+  group of images which are inconsistent with the ensemble (by an
+  amount defined by the user-configurable parameters) are identified
+  and flagged, though this outlier rejection shall be performed
+  optionally.
+
+\item The resulting collection of pixels is used to construct a single
+  output image, cleaned of the outliers.
+
+\item The cleaned, combined image is PSF matched with the static sky
+  image.
+
+\item The static sky image is subtracted from the stacked, cleaned
+  image, resulting in the difference image (P4$\Delta$ image)
+
+\item The Phase 4 analysis performs object detection on the difference
+  images.  All objects in the difference image above a user-configured
+  signficance threshold are detected, including both positive and
+  negative flux objects.  The detection threshold may optionally be a
+  function of the average background flux or the local noise
+  level.  The likely significance threshold is $\sim 3\sigma$.
+
+\item P4$\Delta$ objects have the following object parameters
+  measured:
+  \begin{itemize}
+  \item object centroid and position errors.
+  \item instrumental PSF magnitude and error.
+  \item local background level and error.
+  \item streak L, $\phi$, $\sigma_L$, $\sigma_\phi$.
+  \item second moments ($\sigma_{\rm min}, \sigma_{maj}$) and their covariance matrix.
+  \end{itemize}
+
+\item Minimal object classification is performed to distinguish
+  objects which are consistent with a single PSF, objects which are
+  inconsistent, and objects which are saturated.
+
+\item The pixels belonging to variable sources are masked in the
+  input image.
+
+\item A new, cleaned image is constructed from the masked input images
+  (P4$\Sigma$ image)
+
+\item Object detection is performed on the cleaned, summed image to a
+  user-configured significance threshold ($\sim 7\sigma$).  Only
+  positive flux object are considered.  The detection threshold may
+  optionally be a function of the average background flux or the local
+  noise level.
+
+\item P4$\Sigma$ objects have the following object parameters
+  measured:
+  \begin{itemize}
+  \item object centroid and position errors.
+  \item an extended object position ($x_g, y_g$).
+  \item instrumental PSF magnitude and error.
+  \item local background level and error.
+  \item second moments ($\sigma_{\rm min}, \sigma_{maj}$) and their
+    covariance matrix.
+  \item the Petrosian radius, magnitude, axis ratio, and angle.
+  \item the S\'ersic radius, magnitude, axis ratio, angle, and parameter $\nu$.
+  \end{itemize}
+
+\item Minimal object classification is performed to distinguish
+  objects which are consistent with a single PSF, objects which are
+  inconsistent, and objects which are saturated.
+
+\item Before the image is added to the static sky, it must pass Q/A
+  tests:
+  \begin{itemize}
+  \item the measured photometry scatter for the image must be less
+      than \tbr{1\%}.
+
+  \item the measured astrometry scatter for the image must be less
+  than \tbr{30 mas}.
+  \end{itemize}
+
+\item The final, cleaned input image is added to the static sky so
+  that an incrementally-deeper static sky image may be
+  made.
+\end{itemize}
+
+\paragraph{Static Sky Analysis}
+\label{IPP:StaticSky} 
+
+The IPP is responsible for performing object detection and analysis on
+the static sky.  This analysis is performed continuously (every day or
+week) on those portions of the sky within 15\degree\ of the sun.  In
+this analysis, the object measurement is much more detailed than those
+performed in the real-time analysis.  The currently envisioned
+parameters to be measured for every object are listed in
+Table~\ref{APdetections}.  The parameters include the object centroid
+and the position covarience matrix, the instrumental PSF magnitude and
+error, local background level and error, a measurement of the
+star-galaxy separation, a measurement of the object shape ($\sigma_x,
+\sigma_y, \theta$), the Petrosian radius, magnitude, axis ratio, and
+angle; the S\'ersic radius, magnitude, axis ratio, angle, and
+parameter $\nu$, and a collection of annular aperture flux
+measurements, all of which are also measured for the P4$\Sigma$
+images.  In addition, the galaxy-shape parameters $Gamma_1 \&
+\Gamma_2$, along with the complete `polarization' terms are measured,
+as well as a collection of annular aperture flux and variance
+measurements.  Another unique feature of the static sky analysis is
+that the object detection may be performed simultaneously on all
+filters, in which case the locations and other parameters may be more
+strongly constrained by simultaneously fitting between all bands.  The
+analysis to be performed may be substantially more complex than the
+real-time analysis because of the relatively low data rate.  Instead
+of needed to process thousands of images per night ($\sim 350$Mpix per
+second), it is only necessary to process the complete sky in a year,
+or an average rate of $\sim$2 Mpix per second, or $< 1$\% of the
+object analysis in the other analysis stages.
+
+\paragraph{Calibration Stages}
+\label{IPP:mkcal}
+
+The Calibration Analysis Stages construct calibrations from the
+relevant input data.  Some of these combine multiple raw input images
+together, after processing, to create a high-quality high-signal
+master calibration image.  Some of the calibrations are used to
+correct other calibrations.  Each of the calibration stages must also
+provide the tools to test the quality of the input data against
+existing master calibration data and to test the consistency of
+multiple measurements of the calibration.
+ 
+The Calibration analysis stages may be performed on whatever
+timescales are appropriate and necessary to maintain the quality and
+relevance of the calibration images.  Below, we list the specific
+calibration data which must be constructed in the calibration analysis
+stages.  
+
+The IPP must generate basic calibration images using the raw bias,
+dark, and flat-field (dome or twilight) images obtained by the
+telescope as the input.  The analysis of these images requires
+relatively simple stacking of the input set of images.  Outlier
+rejection, both of complete input images as well as pixels within the
+input stack, must be performed.  In addition, each type of image
+requires an appropriate normalization which may depend on the data
+levels in other detectors in the input set.  Each of these calibration
+stages must be able to determine from the input stack if the relevant
+calibration image needs to be updated and perform an initial test to
+see which input images are consistent and valid.
+
+\subparagraph{Bias Images}
+
+Bias images may be needed to correct for structure in the bias.  The
+IPP must have the capability of constructing a master bias image from
+a stack of raw bias frames.  The input bias images, representing
+offsets from the overscan level, are processed by subtracting the
+overscan, including 1D structure if needed.  
+
+The master bias frame construction uses outlier image and outlier
+pixel rejection to construct a single high-quality bias frame.  The
+statistic used to determine pixel values from the input stack can be
+set by the user to be one of the following: the sample mean, median,
+and mode, robust mean, median, and mode, and the clipped mean and
+median.  Testing of the input images consists of constructing residual
+images, in which the master bias is applied to the input images.
+These images may be included or excluded from an additional iteration
+of the stack on the basis of their pixel-to-pixel statistics.
+
+\subparagraph{Dark Images}
+
+Dark images may be needed to correct for structure in the dark
+current.  The IPP must have the capability of constructing a master
+dark image from a stack of raw dark frames.  The input dark images are
+first corrected for the bias using whatever method is appropriate for
+the science images.  Master dark frames depend on their exposure time.
+As such, the input dark frames must have a limited range of exposure
+times, and the output dark frame includes the exposure time as part of
+its associated metadata.  
+
+The master dark frame construction uses outlier image and outlier
+pixel rejection to construct a single high-quality dark frame.  The
+statistic used to determine pixel values from the input stack can be
+set by the user to be one of the following: the sample mean, median,
+and mode, robust mean, median, and mode, and the clipped mean and
+median.  Testing of the input images consists of constructing residual
+images, in which the master dark image is applied to the input images.
+These images may be included or excluded from an additional iteration
+of the stack on the basis of their pixel-to-pixel statistics.  A
+collection of master dark frames with a range of exposure times are
+used to determine the scaling of the dark frame as a function of
+exposure time.
+
+\subparagraph{On-Off Dark Images for Light Leaks}
+
+A type of image which may be necessary for calibrations will be pairs
+of images taken at night with the shutter closed with and without the
+dome shutter closed.  Such a pair of images can be used to determine
+any light-leak in the camera which may contribute additional flux
+across the mosaic.
+
+\subparagraph{Flat-Field Images}
+
+Master flat-field images must be constructed from a collection of
+input flat-field images.  The input flat-field images may be obtained
+from any of the standard sources: the dome, the twilight sky, and the
+night-time sky.  The choice of flat-field input image must be
+determined experimentally from observations during the commissioning
+phase of the telescope.  The IPP flat-field construction system must
+be capable of handling any of these sources.  
+
+An appropriate set of input images is selected on the basis of their
+flux levels, time of observations, and the observing conditions.  The
+input flat-field images are processed (bias and dark corrected if
+needed) and the resulting images are stacked.  The master flat-field
+construction uses image and pixel outlier rejection to construct a
+single high-quality master flat-field frame.  The statistic used to
+determine pixel values from the input stack can be set by the user to
+be one of the following: the sample mean, median, and mode, robust
+mean, median, and mode, and the clipped mean and median.  Testing of
+the input images consists of constructing residual images, in which
+the master flat-field image is applied to the input images.  These
+images may be included or excluded from an additional iteration of the
+stack on the basis of their pixel-to-pixel statistics.
+
+\subparagraph{Mask Images}
+
+Preliminary bad-pixel mask images are generated on the basis of
+comparison between raw flat-field images and a cleaned, stacked
+master.  The mask creation system accepts a collection of flat-field
+images and identifies pixels which are consistently poorly flattened.
+Pixels which are under-responsive are also identified as pixels to be
+masked.  
+
+\subparagraph{Sky \& Fringe Frames}
+
+Fringe-correction frames must be generated to remove the fringe
+pattern caused by thin-film interference in the top layers of CCDs,
+particularly in the redder passbands.  Fringe correction frames may be
+constructed on the basis of observations of the night-sky in the
+appropriate filters or on the basis of dome fringe lamp observations.
+The choice of the appropriate source will be determined experimentally
+on the basis of data obtained during the commissioning phase.  The IPP
+must be capable of handing either source.  The images are first
+flattened to remove the pixel-to-pixel sensitivity variations of the
+detector.  The combination of multiple input fringe frames may not be
+simply stacked since the amplitude of the fringe pattern varies
+independently of other variations in the image.  The amplitude of the
+fringe pattern in the input frames is measured and the images scaled
+to normalize the fringe amplitude to a consistent range (-1 to +1) for
+all input images before they are combined with one of the standard
+combination statistics (mean, median, mode, etc).  The quality of the
+input frames is tested by flattening the input image and applying the
+master fringe-frame.  The resulting residual image statistics are used
+to select or exclude specific input images.
+
+\subparagraph{Shutter Correction Map}
+
+Shutter correction map images may be generated based on the timing
+measurements of the shutter itself, or on the basis of dome-flat
+images of decreasing exposure times down to the shortest available
+exposures.
+
+\subparagraph{Low-k Sky Models}
+
+Large-scale background structure in images which is not caused by
+thin-film interference must also be detected and corrected.  Models of
+this background structure may be a necessary input to the correction
+proceedure.  The IPP must have the capability of generating image
+models of the large-scale structure patterns observed with the
+telescope
+
+\subparagraph{Flat-Field Correction Frame}
+
+Flat-field images, whether constructed from the dome, twilight, or
+night-sky images, do not perfectly correct the detector response in a
+consistent fashion across the full field of the camera.  The IPP must
+have the capability of generating flat-field photometric correction
+frames on the basis of the measured photometry of objects which are
+moved to a variety of locations on the detector in a sequence of
+images.  The flat-field correction frames analysis stage makes use of
+targetted observations following a specified dither pattern, and
+extracts the photometered objects from the AP Database to determine
+the necessary photometric corrections.  The resulting image is applied
+to the master flat-field image.  Testing of the correction is
+performed by applying the correction to the basic master flat-field
+image, applying that flat-field image to the dithered photometry
+observations, and performing the object detections.  Comparion of the
+photometry of individual stars at different locations on the mosaic
+will demonstrate the consistency of the flat-field image.
+
+\subparagraph{Non-Linearity Correction}
+
+The IPP must have the capability of constructing a correction for
+non-linearity in the detectors.  These frames are constructed from
+exposures of a uniform source with a range of exposure times.  The
+non-linearity correction frames provide polynomial correction
+coefficients or a lookup table describing the correction.  There is
+likely to be a single non-linear correction for each OTA detector, or
+potentially for each Cell.  The IPP must handle these two cases.
+
+\paragraph{Reference Catalog Creation}
+
+One of the primary goals inital goals of Pan-STARRS is the creation of
+photometric and astrometric reference catalogs for the general
+community and for additional Pan-STARRS calibration.  This
+internally-generated reference catalog is necessary to achieve the
+photometry and astrometry goals set for the project.  The generation
+of these catalogs is inherently a research project, and will require
+human control and intervention.  The IPP will be required to provide
+the data access, manipulation and visualization tools needed to
+construct these reference catalogs and to assess their quality.  In
+this section, we discuss the tools needed for this effort.
+
+\subsubsection{Bias Image Creation}
+
+The Bias calibration stage constructs a master bias image from a
+collection of raw bias images.  The tasks and functions include:
+
+\begin{itemize}
+
+\item The Bias calibration stage corrects the input images based on
+  the overscan region, determined from either the header or from
+  metadata.
+
+\item The Bias calibration stage combines the input images using the
+  statistic specified by the user, selected from one of the following:
+  sample mean, median, and mode, robust mean, median, and mode, and
+  the clipped mean and median.
+
+\item The Bias calibration stage construct residual images, in which
+  the master bias is applied to the input images.
+
+\item Outlier residual images, those for which the residual bias and
+  variance in the bias image are excessive, are excluded from the
+  input image stack and the bias image reconstructed.
+\end{itemize}
+
+\subsubsection{Dark Image Creation}
+
+The Dark calibration stage shall construct a master dark image from a
+  collection of raw dark images.  The tasks and functions include:
+
+\begin{itemize}
+
+\item The Dark calibration stage raises an error if the input images
+  have exposure times which deviate by more than 2\%.
+
+\item The Dark calibration stage corrects the input dark images for
+  the bias.
+
+\item The Dark calibration stage combines the input images using the
+  statistic specified by the user, selected from one of the following:
+  sample mean, median, and mode, robust mean, median, and mode, and
+  the clipped mean and median.
+
+\item The Dark calibration stage constructs residual images, in which
+  the master dark is applied to the input images.
+
+\item Outlier residual images, those for which the residual level and
+  variance are excessive, are excluded from the input image stack and
+  the dark image reconstructed.
+\end{itemize}
+
+\subsubsection{Flat-field Image Creation}
+
+The Flat-field calibration stage constructs a master flat-field image
+from a collection of raw flat-field images.  The tasks and functions
+include:
+
+\begin{itemize}
+
+\item The Flat-field calibration stage accepts a group of images from
+  one of the following flat-field sources: dome, twilight,
+  night-sky.
+
+\item The flat-field calibration stage raises an error if the
+  input images in a single stack used more than one of the above
+  flat-field sources or multiple filters.
+
+\item The Flat-field calibration stage corrects the input flat-field
+  images for the bias and dark.
+
+\item The Flat-field calibration stage combines the input images using
+  the statistic specified by the user, selected from one of the
+  following: sample mean, median, and mode, robust mean, median, and
+  mode, and the clipped mean and median.
+
+\item The Flat-field calibration stage constructs residual images, in
+  which the master flat-field is applied to the input images.
+
+\item Outlier residual images, those for which the residual level and
+  variance are excessive ($> 0.1$\%, or 1.02 times the Poisson limit
+  of the flat-field image), are excluded from the input image stack
+  and the flat-field image reconstructed.
+\end{itemize}
+
+\subsubsection{Mask Image Creation}
+
+The Mask calibration stage constructs a bad-pixel mask from a stack of
+raw flat-field images and a master flat-field image.  The tasks and
+functions include:
+
+\begin{itemize}
+
+\item The Mask calibration stage masks the pixels which are
+  inconsistent in the input flats by more than 1\%, given sufficient
+  signal-to-noise in the input flats.
+
+\item The Mask calibration stage mask the pixels which are
+  consistently low or high in the input flats by more than a factor of
+  3 beyond the typical pixel.
+
+\item The Mask calibration stage masks the pixels identified in a
+  table of bad pixels generated externally to the calibration stage.
+
+\item The Mask calibration stage uses multiple bit values to identify
+  the different types of masked pixels.
+
+\item The Mask calibration stage raises an error if the input images
+  generate too many bad pixels in the mask.
+\end{itemize}
+
+\subsubsection{Fringe-frame Creation}
+
+The Fringe-frame Creation calibration stage constructs a master fringe
+frame from a stack of raw night-time sky images or from a stack of
+dome fringe frames.  The tasks and functions include:
+
+\begin{itemize}
+
+\item The Fringe-frame Creation calibration stage raises an error if
+  the input stack consists is images generated with more than one type
+  of fringe source or with multiple filters.
+
+\item The Fringe-frame Creation calibration stage flattens the input
+  images to remove the pixel-to-pixel sensitivity variations of the
+  detector.
+
+\item The Fringe-frame Creation calibration stage measures the fringe
+  amplitude on the input fringe images.
+
+\item The Fringe-frame Creation calibration stage scales the input
+  fringe images based on the fringe amplitude.
+
+\item The Fringe-frame Creation calibration stage combines the scaled
+  input images using the statistic specified by the user, selected
+  from one of the following: sample mean, median, and mode, robust
+  mean, median, and mode, and the clipped mean and median.
+
+\item The Fringe-frame Creation calibration stage constructs residual
+  images, in which the master fringe image is applied to the input
+  images, along with all necessary preceding calibration images.
+
+\item The Fringe-frame Creation calibration stage measures the
+  residual fringe amplitude on the residual images.
+\end{itemize}
+
+\subsubsection{Low-spatial-frequency Sky Models}
+
+The Sky Model calibration stage constructs a sky model image set from
+a stack of raw night-time sky images.
+
+\subsubsection{Flat-field correction Frame Creation}
+
+The Flat-field correction calibration stage constructs a flat-field
+correction image from dithered observations of a stellar field.  The
+tasks and functions include:
+
+\begin{itemize}
+
+\item The Flat-field correction calibration stage constructs a
+  flat-field correction image from dithered observations of a stellar
+  field.
+
+\item The Flat-field correction calibration stage constructs a
+  flat-field correction image for each filter and camera
+  independently.
+
+\item The Flat-field correction calibration stage constructs a
+  correction image which makes the photometry of multiple observations
+  of the same stellar source consistent at different locations in the
+  focal plane.
+
+\item The Flat-field correction calibration stage constructs corrected
+  flat-field images using the measured correction.
+
+\item The Flat-field correction calibration stage determines the
+  consistency of the corrected flat-field images using the dithered
+  stellar field observations flattened with the corrected flat-field
+  image.
+\end{itemize}
+
+\subsubsection{Non-linearity correction}
+
+The Non-linear correction calibration stage constructs a correction
+model for low-level non-linearity effects in the detector.  The tasks
+and functions include:
+
+\begin{itemize}
+
+\item The Non-linear correction calibration stage constructs a
+  non-linear correction from a collection of images of a constant
+  source with varying exposure times.
+
+\item The Non-linear correction calibration stage construct a
+  non-linear correction which linearizes the detector fluxes
+  $<0.5\%$.
+
+\item The Non-linear correction calibration stage determines the
+  saturation regime, in which the non-linear correction is no longer
+  consistent to $<0.5\%$.
+\end{itemize}
+
+\subsubsection{Telescope Astrometry Parameters}
+
+\begin{itemize}
+\item The IPP Calibration system constructs static models of the
+  telescope astrometry parameters (e.g., distortion, detector warps)
+  once per week.
+
+\item The IPP Calibration system constructs static models of the
+  telescope astrometry parameters (e.g., distortion, detector warps)
+  with an accuracy to produce astrometry consistent to 30
+  milliarcsec.
+
+\item The IPP Calibration system monitors changes in the telescope
+  astrometry parameters and issue a warning if the parameters change
+  by more than 2\%.
+\end{itemize}
+
+\subsubsection{Zero-Point Monitoring}
+
+The IPP Calibration system determines telescope filter and camera
+zero-points on a nightly basis with an accuracy sufficient to
+determine photometry in the native filter systems to 5 millimags.
+
+\subparagraph{Astrometry Reference Creation}
+
+\begin{table}
+\begin{center}
+\caption{Astrometric Reference Catalogs\label{AstroRefs}}
+\begin{tabular}{lrrrrl}
+\hline
+\hline
+Name       & scatter limit   & proper   & depth      & Nstars     & filters \\
+           & (milliarcsec)   & motion   &(mag)       & (millions) &         \\
+\hline
+Hipparcos  &   1             & 2        &  7.3       &    0.1     & {\em V}       \\ 
+Tycho2	   &  10             & 1        & 11.5       &    2.5     & {\em B,V}     \\ 
+UCAC-2     &  20             & 1        & 16.0       &   48.0     & {\em R}       \\ 
+USNO-A2.0  & 250             & N/A      & 19.0       &  526.2     & {\em B,R}     \\ 
+USNO-B1.0  & 200             & \tbr{20} & 21.0       & 1042.6     & {\em B,R}     \\ 
+2MASS	   &  70             & N/A      & \tbr{15.0} &  470.0     & {\em J,H,K}   \\ 
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+The existing astrometric reference catalogs are known to have
+limitations in accuracy as noted in Table~\ref{AstroRefs}.  The
+internal accuracy of the Pan-STARRS dataset can potentially be much
+higher than the external reference catalogs.  The IPP must have the
+capability of generating an astrometric reference on the basis of the
+observations obtained by the AP survey.  The IPP must provide the
+analysis tools needed to generate the master astrometric reference
+catalog.  Much of the required functionality is covered by the AP
+Database.
+
+The two basic, necessary ingredients for the construction of the
+Astrometric Reference Catalog are: the observed coordinates of stars
+and the existing astrometric reference catalogs.
+Table~\ref{AstroRefs} lists a subset of the reference catalogs which
+we will use at different stages in the analysis process, along with
+notes about their accuracy.
+
+These catalogs must be available and accessible to the AP Database.
+It is necessary to have the tools to convert the reference catalog
+object coordinates to all of the possible coordinate frames of
+relevance in the telescope and camera system, including:
+\begin{itemize}
+\item Catalog to mean positions
+\item Mean to apparent positions
+\item Apparent positions + pointing to arbitrary tangent plane coordinates
+\item Apparent positions + pointing to focal plane coordinates
+\item focal plane to specific detector (OTA)
+\item specific detector to detector cell
+\end{itemize}
+
+In addition to the reference catalogs, it will be necessary to
+determine and have available for the analysis system a variety of
+approximate calibration data, including the telescope and camera
+optical distortion models and the variation of the image PSF across
+the camera field, as a function of color.
+
+The other necessary ingredient in the astrometry reference creation is
+the observation of stars by Pan-STARRS.  The object detections are
+produced by several of the analysis stages discussed in the Science Image
+Analysis section.  The likely measurement of relevance to the
+astrometric reference catalog is the object extraction for the
+individual, detrended images (section~\ref{IPP:Phase2}).  The detected
+objects must be matched against the reference catalogs, and it must be
+possible to determine fit coefficients as a function of position
+alone, or with combinations of magnitude or color.  The fitting method
+must include robust outlier rejection.  It is also necessary to have
+information about the objects which are detected in the catalog, but
+not the science image or vice-versa, as well as an assessment of the
+centroiding errors for each object.  It must be possible to plot the
+fit residuals against a wide variety of parameters, including the
+object positions, magnitudes, colors, etc, and to make subset
+selections of the objects on the basis of these parameters.  .
+
+An alternative measurement of the stellar positions is derived from
+the guide stars, which are much brighter than the typical saturated
+stars.  It must be possible to compare the coordinates of the guide
+stars with the coordinates of the other stars in the image.  It must
+also be possible to perform the various fitting steps for the guide
+stars rather than for the normal image data.
+
+\subparagraph{Photometry Reference Creation}
+
+\begin{table}
+\begin{center}
+\caption{Photometric Reference Catalogs\label{PhotoRefs}}
+\begin{tabular}{lrrr}
+\hline
+\hline
+Name       & scatter  & depth & filters \\
+           & mmag     & mag   &         \\
+\hline
+SDSS       & 15       & 16    & {\em u,g,r,i,z} \\
+CFHT-LS    & \tbr{10} & 18    & {\em u,g,r,i,z} \\
+Landolt    & 10-20    & 15    & {\em U,B,V,R,I} \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+The IPP must provide the analysis tools needed to generate a master
+photometric reference catalog.  The tools needed for generation of the
+photometric reference catalogs are similar in essence to those used
+for the astrometric reference catalog.  It is necessary to confront
+the observed objects against the existing reference catalogs to
+determine the necessary calibrations.  Again, much of the required
+functionality is covered by the AP Database.  
+
+The necessary ingredients for the construction of the Photometric
+Reference Catalog are: the observed magnitudes of stars and the
+existing photometric reference catalogs.  An internally consistent
+magnitude system will be generated as the primary reference catalog.
+In addition, comparison of these magnitudes with the reference
+magnitudes will allow for the determination of color transformations
+and calibrated magnitudes in the reference system.
+Table~\ref{PhotoRefs} lists a variety of reference catalogs which may
+be used in the process.  These catalogs must be available and
+accessible to the AP Database.
+
+The other necessary ingredient in the photometry solution is the
+observation of stars with Pan-STARRS.  The photometry is determined by
+several of the analysis stages discussed in the Science Image Analysis
+section.  The likely measurement of relevance to the photometric
+reference catalog is the object extraction for the individual,
+detrended images (section~\ref{IPP:Phase2}).  It is necessary to have
+the tools to convert between different photometric systems, including:
+\begin{itemize}
+\item instrumental to nominal detector magnitude
+\item nominal detector magnitude to average filter system
+\item average filter system to reference photometry system
+\end{itemize}
+These transformations are based on a set of measured coefficients for
+the color and airmass dependency of the measurement.  In addition to
+these types of transformations, it is necessary to have the ability to
+measure and apply relative photometry corrections.  
+
+The detected objects must be matched against the reference catalogs,
+and it must be possible to determine fit coefficients as a function of
+airmass, magnitude, color and detector coordinates, or with
+combinations of the above.  The fitting method must include robust
+outlier rejection.  It is also necessary to perform exclusions on the
+basis of object locations, instrumental magnitudes, observed and
+reference errors, and time of the observations. It must be possible to
+plot the fit residuals against a wide variety of parameters, including
+the object positions, magnitudes, colors, etc, and to select a subset
+of the objects on the basis of these parameters.  It will likely be
+necessary to maintain individual color transformations for each
+detector and filter combination to a single internal system for each
+filter.
+
+An alternative measurement of the stellar photometry is derived from
+the guide stars, which are much brighter than the typical saturated
+stars.  It must be possible to relate the magnitudes of the guide
+stars with the magnitudes of the other stars in the image.  It must
+also be possible to perform the above fitting steps for the guide
+stars rather than for the normal image data.
+
+\subsubsection{Modules}
+
+In order to encapsulate functionality, the analysis stages are
+constructed of a sequence of steps.  The analysis stages consist of
+scripts in a high-level language, likely to be either Python or Perl,
+which executes a sequence of C-level functions.  The C-level functions
+called by the script are called {\em modules} and represent basic data
+analysis operations.
+
+\subsubsection{Pan-STARRS IPP Library}
+
+In order to facilitate testing and development, and to encourage
+flexibility, the IPP is built in a layered fashion.  The lowest level
+functions are written in C and collected together into a Pan-STARRS
+library, \code{PSLib}.
+
+The Pan-STARRS Data Library consists of C structures describing the
+basic data types needed by the IPP and C functions which perform the
+basic data manipulation operations.  The library is organized into
+four topics: System Utilities, Basic Data Collections, Data
+Manipulation, and Astronomy-Specific Functions.  The Modules are
+constructed using these low-level Library functions as needed.
+
+\subsection{Summary of Derived Requirements}
+
+\begin{enumerate}
+
+\item The IPP Image Server shall accept raw images from the summit at
+ a sustained rate of 1 exposure (4 FPAs or 8~GB) per \tbr{40 seconds}.
+\label{IPP:DeReq:1}
+
+\item The IPP Metadata Database shall accept metadata from the summit
+ at a sustained rate of \tbr{1 MB per 40 second}.
+\label{IPP:DeReq:2}
+
+\item The IPP Calibration Analysis shall produce master calibration
+ images from the raw calibration images in less \tbr{2 hours}.
+\label{IPP:DeReq:3}
+
+\item Master calibration images shall not introduce systematic
+ uncertainties in the photometry greater than \tbr{0.2\%}.
+\label{IPP:DeReq:4}
+
+\item The IPP Science Analysis shall pre-process the science images
+  with the master calibration images at a sustained rate of 1 exposure
+  per \tbr{40 seconds}.
+\label{IPP:DeReq:5}
+
+\item The IPP Science Analysis shall merge multiple pre-processed
+ science images into stacked images with corresponding signal-to-noise
+ maps at a sustained rate of 1 exposure per \tbr{40 seconds}.
+\label{IPP:DeReq:6}
+
+\item The IPP Science Analysis shall excise pixels from the input
+ images which are outliers for the ensemble of corresponding pixels
+ with an efficiency of $> 99$\%.
+\label{IPP:DeReq:7}
+
+\item The IPP Science Analysis shall merge the cleaned images into the
+ static sky image, and update the corresponding exposure (S/N) maps,
+ at a sustained rate of 1 exposure per \tbr{40 seconds}.
+\label{IPP:DeReq:8}
+
+\item The IPP Science Analysis shall detect and measure parameters of
+objects on the pre-processed science images.
+\label{IPP:DeReq:9}
+
+\item The IPP Science Analysis shall detect and measure parameters of
+objects on the stacked science images.
+\label{IPP:DeReq:10}
+
+\item The IPP Science Analysis shall detect and measure parameters of
+objects on the difference images.
+\label{IPP:DeReq:11}
+
+\item The IPP Science Analysis shall detect and measure parameters of
+objects on the static sky images.
+\label{IPP:DeReq:12}
+
+\item The IPP Science Analysis shall determine astrometry of the
+ detected objects relative to an external astrometric reference with
+ an accuracy of \tbr{750 mas} (for bright objects) in the
+ Commissioning phase of the telescope.
+\label{IPP:DeReq:13}
+
+\item The IPP Science Analysis shall determine astrometry of the
+ detected objects relative to an external astrometric reference with
+ an accuracy of \tbr{250 mas} (for bright objects) during the
+ construction of the Pan-STARRS Astrometric Reference Catalog.
+\label{IPP:DeReq:14}
+
+\item The IPP Science Analysis shall determine astrometry of the
+ detected objects relative to the Pan-STARRS Astrometric Reference
+ with an accuracy of \tbr{100 mas} (for bright objects) during normal
+ operations.
+\label{IPP:DeReq:15}
+
+\item The IPP Science Analysis shall determine photometry of the
+ detected objects within an internal photometric system with scatter
+ less than \tbr{25 millimags} (for bright objects) during the
+ Commissioning phase of the telescope in photometric weather.
+\label{IPP:DeReq:16}
+
+\item The IPP Science Analysis shall determine photometry of the
+ detected objects within an internal photometric system with scatter
+ less than \tbr{10 millimags} (for bright objects) during the
+ construction of the Pan-STARRS Photometric Reference Catalog in
+ photometric weather.
+\label{IPP:DeReq:17}
+
+\item The IPP Science Analysis shall determine photometry of the
+ detected objects within an internal photometric system with scatter
+ less than \tbr{5 millimags} (for bright objects) during normal
+ operations in photometric weather.
+\label{IPP:DeReq:18}
+
+\item The IPP Science Analysis shall determine photometry of the
+ detected objects in an external photometric system with scatter less
+ than \tbr{10 millimags} (for bright objects) during normal operations
+ in photometric weather.
+\label{IPP:DeReq:19}
+
+\item The IPP Reference Creation System shall produce an astrometric
+  reference catalog from the extracted objects within 6 months of the
+  end of the AP Survey.
+\label{IPP:DeReq:20}
+
+\item The IPP Reference Creation System shall produce an astrometric
+  reference catalog with an absolute accuracy of \tbr{100 mas} and a
+  local relative accuracy of \tbr{30 mas} for bright objects.
+\label{IPP:DeReq:21}
+
+\item The IPP Reference Creation System shall produce an astrometric
+  reference catalog with proper motions measurements for
+  non-solar-system objects with an accuracy of \tbr{20 mas / year} for
+  unsaturated, bright stars.
+\label{IPP:DeReq:22}
+
+\item The IPP Reference Creation System shall produce a photometric
+  reference catalog from the extracted point-source objects within 6
+  months of the end of the AP Survey.
+\label{IPP:DeReq:23}
+
+\item The IPP Reference Creation System shall produce a photometric
+  reference catalog with a consistency across the sky of \tbr{5
+  millimag}.
+\label{IPP:DeReq:24}
+
+\item The IPP Reference Creation System shall produce a photometric
+  reference catalog with an absolute calibration to the external
+  system (defined by \tbr{SDSS} and the CFHT Legacy Survey Standards)
+  with an accuracy of \tbr{10 millimag} (for bright objects).
+\label{IPP:DeReq:25}
+
+\item The IPP shall publish the static sky images to the Pan-STARRS
+PSPS on a time-scale of \tbr{6 month}.
+\label{IPP:DeReq:26}
+
+\item The IPP shall publish the detected objects to the Pan-STARRS
+PSPS on a time-scale of \tbr{1 month}.
+\label{IPP:DeReq:27}
+
+\item The IPP shall publish the IPP and OTIS metadata to the
+Pan-STARRS PSPS on a time-scale of \tbr{1 week}.
+\label{IPP:DeReq:28}
+
+\item The IPP shall provide to the MOPS subsystem the detected
+ single-occurrence transient objects \tbr{by the end of every night}.
+\label{IPP:DeReq:29a}
+
+\item The IPP shall provide to the MOPS subsystem the metadata
+appropriate to the images from which single-occurrence transient
+objects were detected \tbr{by the end of every night}.
+\label{IPP:DeReq:29b}
+
+\item The IPP shall provide to external Pan-STARRS clients the
+  detected objects within \tbr{5 minute} after the image is obtained.
+\label{IPP:DeReq:29c}
+
+\item The IPP shall store the raw images for a period of \tbr{1 month}.
+\label{IPP:DeReq:30}
+
+\item The IPP shall store the detected objects for a minimum of \tbr{1 year}.
+\label{IPP:DeReq:31}
+
+\item The IPP shall store the metadata for the lifetime of the project.
+\label{IPP:DeReq:32}
+\end{enumerate}
+
+\subsection{Internal Interfaces}
+
+The IPP has internal interfaces between several of the architectural
+components and between the architectural components and the analysis
+stages.
+
+\begin{enumerate}
+
+\item IPP Scheduler - IPP Controller.  The IPP Scheduler must
+send to the IPP Controller information about the tasks to be
+performed and must receive from the IPP Controller descriptions of the
+success or failure of these tasks.
+
+\item IPP Scheduler - Metadata DB.  The IPP Scheduler must query the
+Metadata DB to determine an appropriate course of action.  The IPP
+Scheduler must send result and status information to the Metadata DB.
+
+\item IPP Controller - Analysis Tasks.  The IPP Controller must
+initiate the Analysis Tasks and monitor their output and exit status.
+
+\item Analysis Tasks - Metadata DB.  The Analysis Tasks must be able
+to query the Metadata DB as needed to extract metadata needed for a
+given task.  The Analysis Tasks must be able to send results and
+updates to the Metadata DB.
+
+\item Analysis Tasks - Image Server.  The Analysis Tasks must be able
+to extract relevant images from the Image Server.  The Analysis Tasks
+must be able to send output images to the Image Server.
+
+\item Analysis Tasks - AP DB.  The Analysis Tasks must be able
+to extract information related to specific objects from the
+Astrometric and Photometric Database.  The Analysis Tasks must be able
+to send result detections to the AP Database. 
+\end{enumerate}
+
+\subsection{Requirements Trace Matrix}
+\begin{center}
+\footnotesize
+\begin{tabular}{|l|l|l|l|} 
+\hline 
+\multicolumn{2}{|c|}{\bf Subsystem Requirements} &  
+\multicolumn{2}{|c|}{\bf System/Subsystem Requirements} \\ \hline 
+\FRN{\bf Number} & 
+\FRS{\bf Caption} & 
+\FRN{\bf Number} & 
+\FRS{\bf Caption} \\ \hline
+
+\ref{IPP:Req:1}  & \FRS{Photometrically consistent images to 1\%} & \ref{OpsReq:D} & \FRS{Photometric precision of 0.01 mag} \\ \hline 
+\ref{IPP:Req:2}  & \FRS{Photometrically calibrated images to 1\%} & \ref{OpsReq:D} & \FRS{Photometric precision of 0.01 mag} \\ \hline 
+\ref{IPP:Req:3}  & \FRS{Absolute astrometry to 100 mas} & \ref{OpsReq:E} & \FRS{Absolute Astrometric precision of 100 mas} \\ \hline 
+\ref{IPP:Req:4}  & \FRS{Relative astrometry to 30 mas} & \ref{OpsReq:F} & \FRS{Relative Astrometric precision of 100 mas} \\ \hline 
+\ref{IPP:Req:5}  & \FRS{Background correction to 1\%} & \ref{OpsReq:G} & \FRS{remove foregrounds to $<$ 1\% of background} \\ \hline 
+\ref{IPP:Req:6}  & \FRS{Construct Static Sky for $g$ filter images} & \ref{OpsReq:J} & \FRS{construct static sky images} \\ \hline 
+\ref{IPP:Req:7}  & \FRS{Construct Static Sky for $r$ filter images} & \ref{OpsReq:J} & \FRS{construct static sky images} \\ \hline 
+\ref{IPP:Req:8}  & \FRS{Construct Static Sky for $i$ filter images} & \ref{OpsReq:J} & \FRS{construct static sky images} \\ \hline 
+\ref{IPP:Req:9}  & \FRS{Construct Static Sky for $z$ filter images} & \ref{OpsReq:J} & \FRS{construct static sky images} \\ \hline 
+\ref{IPP:Req:10} & \FRS{Construct Static Sky for $y$ filter images} & \ref{OpsReq:J} & \FRS{construct static sky images} \\ \hline 
+\ref{IPP:Req:11} & \FRS{Construct Static Sky for $w$ filter images} & \ref{OpsReq:J} & \FRS{construct static sky images} \\ \hline 
+\ref{IPP:Req:12} & \FRS{Detect \& classify objects on science images} & \ref{OpsReq:P} & \FRS{classify detected objects } \\ \hline 
+\ref{IPP:Req:13} & \FRS{Detect \& classify objects on science image stacks} & \ref{OpsReq:P} & \FRS{classify detected objects } \\ \hline 
+\ref{IPP:Req:14} & \FRS{Detect \& classify objects on static sky images} & \ref{OpsReq:P} & \FRS{classify detected objects } \\ \hline 
+\ref{IPP:Req:15} & \FRS{Detect \& classify transients} & \ref{OpsReq:P} & \FRS{classify detected objects} \\ \hline 
+\ref{IPP:Req:16} & \FRS{Degrade image size by $<$ 10 mas} & \ref{SysDeReq:2}, alloc. & \FRS{Degrade image size by \PSiqdeg\ of median seeing} \\ \hline 
+\ref{IPP:Req:17} & \FRS{Process images arriving at cadence of 40 seconds} & \ref{OpsReq:B} & \FRS{processing for transients within 5 min} \\ \hline 
+\ref{IPP:Req:18} & \FRS{Limit false alarm rate for transients} & \ref{OpsReq:M}   & \FRS{false alarm rate of $<1\%$} \\ \hline 
+\ref{IPP:Req:19} & \FRS{Publish static sky images to PSPS} & \ref{OpsReq:Q} & \FRS{Data Products shall be made available} \\ \hline 
+\ref{IPP:Req:20} & \FRS{Publish detected objects to PSPS} & \ref{OpsReq:Q} & \FRS{Data Products shall be made available} \\ \hline 
+\ref{IPP:Req:21} & \FRS{Publish metadata to PSPS} & \ref{OpsReq:Q} & \FRS{Data Products shall be made available} \\ \hline 
+\ref{IPP:Req:22} & \FRS{Provide access to preferred science clients}       & \ref{SysDeReq:9} & \FRS{allow interface to preferred science clients} \\ \hline 
+\ref{IPP:Req:23} & \FRS{Store raw images for 1 month} & allocated 	   & \FRS{} \\ \hline 
+\ref{IPP:Req:24} & \FRS{Store detected objects for 1 year} & allocated 	   & \FRS{} \\ \hline 
+\ref{IPP:Req:25} & \FRS{Store metadata for project lifetime} & allocated   & \FRS{} \\ \hline 
+\end{tabular}
+\end{center}
+
+\begin{center}
+\footnotesize
+\begin{tabular}{|l|l|l|l|} 
+\hline 
+\multicolumn{2}{|c|}{\bf Derived Subsystem Requirements} &  
+\multicolumn{2}{|c|}{\bf Top-level Subsystem Requirements} \\ \hline 
+\FRN{\bf Number} & 
+\FRS{\bf Caption} & 
+\FRN{\bf Number} & 
+\FRS{\bf Caption} \\ \hline
+
+\ref{IPP:DeReq:1}  & \FRS{Accept images from summit at rate...} & \ref{IPP:Req:17} & \FRS{Process images arriving at cadence of 40 seconds} \\ \hline 
+\ref{IPP:DeReq:2}  & \FRS{Accept metadata from summit at rate...} & \ref{IPP:Req:17} & \FRS{Process images arriving at cadence of 40 seconds} \\ \hline 
+\ref{IPP:DeReq:3}  & \FRS{Produce master calibration images in...} & allocated & \FRS{} \\ \hline 
+\ref{IPP:DeReq:4}  & \FRS{Master cal. image introduce less than $1\%$} & \ref{IPP:Req:1} & \FRS{Photometrically consistent images to 1\%} \\ \hline 
+\ref{IPP:DeReq:5}  & \FRS{Pre-process science image at rate...} & \ref{IPP:Req:17} & \FRS{Process images arriving at cadence of 40 seconds} \\ \hline 
+\ref{IPP:DeReq:6}  & \FRS{Merge images into stacked images at rate...} & \ref{IPP:Req:17} & \FRS{Process images arriving at cadence of 40 seconds} \\ \hline 
+\ref{IPP:DeReq:7}  & \FRS{Excise $>99\%$ of outlier pixels from stack} & \ref{IPP:Req:18} & \FRS{Limit false alarm rate for transients} \\ \hline 
+\ref{IPP:DeReq:8}  & \FRS{Merge cleaned images into static sky at rate...} & \ref{IPP:Req:17} & \FRS{Process images arriving at cadence of 40 seconds} \\ \hline 
+\ref{IPP:DeReq:9}  & \FRS{Measure objects on pre-processed images} & \ref{IPP:Req:12} & \FRS{Detect \& classify objects on science images} \\ \hline 
+\ref{IPP:DeReq:10} & \FRS{Measure objects on stacked images} & \ref{IPP:Req:13} & \FRS{Detect \& classify objects on science image stacks} \\ \hline 
+\ref{IPP:DeReq:11} & \FRS{Measure objects on difference images} & \ref{IPP:Req:15} & \FRS{Detect \& classify transients} \\ \hline 
+\ref{IPP:DeReq:12} & \FRS{Measure objects on static sky images} & \ref{IPP:Req:14} & \FRS{Detect \& classify objects on static sky images} \\ \hline 
+\ref{IPP:DeReq:13} & \FRS{astrometric accuracy for commissioning phase} & allocated & \FRS{} \\ \hline 
+\ref{IPP:DeReq:14} & \FRS{astrometric accuracy for reference catalog phase} & allocated & \FRS{} \\ \hline 
+\ref{IPP:DeReq:15} & \FRS{astrometric accuracy for normal operations} & \ref{IPP:Req:3} & \FRS{Absolute astrometry to 100 mas} \\ \hline 
+\ref{IPP:DeReq:16} & \FRS{photometric accuracy for commissioning phase} & allocated & \FRS{} \\ \hline 
+\ref{IPP:DeReq:17} & \FRS{photometric accuracy for reference catalog phase} & allocated & \FRS{} \\ \hline 
+\ref{IPP:DeReq:18} & \FRS{relative photometric accuracy for normal operations} & \ref{IPP:Req:2} & \FRS{Photometrically calibrated images to 1\%} \\ \hline 
+\ref{IPP:DeReq:19} & \FRS{absolute photometric accuracy for normal operations} & \ref{IPP:Req:2} & \FRS{Photometrically calibrated images to 1\%} \\ \hline 
+\ref{IPP:DeReq:20} & \FRS{astrometric reference within 6 mo} & \ref{IPP:Req:3}, allocated & \FRS{Absolute astrometry to 100 mas} \\ \hline 
+\ref{IPP:DeReq:21} & \FRS{astrometric reference astrometry accuracy} & \ref{IPP:Req:3} & \FRS{Absolute astrometry to 100 mas} \\ \hline 
+\ref{IPP:DeReq:22} & \FRS{astrometric reference proper motion accuracy} & \ref{IPP:Req:3} & \FRS{Absolute astrometry to 100 mas} \\ \hline 
+\ref{IPP:DeReq:23} & \FRS{photometric reference within 6 mo} & \ref{IPP:Req:2}, allocated & \FRS{Photometrically calibrated images to 1\%} \\ \hline 
+\ref{IPP:DeReq:24} & \FRS{photometric reference global consistency} & \ref{IPP:Req:2} & \FRS{Photometrically calibrated images to 1\%} \\ \hline 
+\ref{IPP:DeReq:25} & \FRS{photometric reference absolute accuracy} & \ref{IPP:Req:2} & \FRS{}Photometrically calibrated images to 1\% \\ \hline 
+\ref{IPP:DeReq:26} & \FRS{publish static sky images every 6 mo.} & \ref{IPP:Req:19} & \FRS{Publish static sky images to PSPS} \\ \hline 
+\ref{IPP:DeReq:27} & \FRS{publish detected objects every 1 mo.} & \ref{IPP:Req:20} & \FRS{Publish detected objects to PSPS} \\ \hline 
+\ref{IPP:DeReq:28} & \FRS{publish metadata every 1 week} & \ref{IPP:Req:21} & \FRS{Publish metadata to PSPS} \\ \hline 
+\ref{IPP:DeReq:29a} & \FRS{provide transients to MOPS rate...} & \ref{IPP:Req:22} & \FRS{Provide access to preferred science clients} \\ \hline 
+\ref{IPP:DeReq:29b} & \FRS{provide metadat to MOPS at rate...} & \ref{IPP:Req:22} & \FRS{Provide access to preferred science clients} \\ \hline 
+\ref{IPP:DeReq:29c} & \FRS{provide transients to other clients at rate...} & \ref{IPP:Req:22} & \FRS{Provide access to preferred science clients} \\ \hline 
+\ref{IPP:DeReq:30} & \FRS{store raw images for 1 month} & \ref{IPP:Req:23} & \FRS{Store raw images for 1 month} \\ \hline 
+\ref{IPP:DeReq:31} & \FRS{store detected objects for 1 year} & \ref{IPP:Req:24} & \FRS{Store detected objects for 1 year} \\ \hline 
+\ref{IPP:DeReq:32} & \FRS{store metadata for project lifetime} & \ref{IPP:Req:25} & \FRS{Store metadata for project lifetime} \\ \hline 
+\end{tabular}
+\end{center}
+
+\endnotesection
+
+\ifwhole\else
+
+\subsection{Notes}{
+
+There are several system concepts and goals which must be defined
+before the IPP section:
+
+\begin{enumerate}
+\item relationship between FPA image, OTA, and cell
+\item concept of a `major frame' (observationally defined group of
+  images). 
+\item distinction of a images grouped together for a science goal from
+  images grouped together for processing purposes.
+
+\item timing budget: need to specify total delay allowed from shutter
+  close to specific science result, and break down for the stages:
+  readout, OTIS-IPP communication, OTIS-IPP data transfer, IPP
+  processing Phase 2 and Phase 4, IPP-client science pipeline
+  communication.
+
+\item{discuss the astrometry and photometry errors introduced at each
+  stage of the analysis}
+
+\item mention future pre-processing at the summmit?
+
+\item does the Top-Level Description need a discussion of the basic goals of the image analysis?
+
+\end{enumerate}
+
+\subsection{Definitions}
+
+\subsection{External Interfaces}
+
+\end{document}
+\fi 
Index: /tags/sj_tags/sj_root_20080929/doc/design/ippSRS.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/ippSRS.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/ippSRS.tex	(revision 22322)
@@ -0,0 +1,1395 @@
+ %%% $Id: ippSRS.tex,v 1.13 2004-11-30 23:16:03 eugene Exp $
+\documentclass[panstarrs,spec]{panstarrs}
+
+% basic document variables
+\title{Pan-STARRS PS-1 Image Processing Pipeline}
+\subtitle{Software Requirements Specification}
+\shorttitle{IPP SRS}
+\author{Eugene A. Magnier, Paul A. Price, Josh Hoblitt}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{01}
+\docnumber{PSDC-430-005}
+
+\newcommand\FRM[2]{\parbox[t]{#1}{\raggedright #2}}
+\newcommand\FRA{100pt}
+\newcommand\FRB{260pt}
+\newcommand\FRC{40pt}
+\newcommand\FRD{80pt}
+\newcommand\FRN[1]{\FRM{50pt}{#1}}
+\newcommand\FRS[1]{\FRM{190pt}{#1}}
+\newcommand\VER[2]{\\ {\scriptsize QUALIFICATION METHOD: #1, TRACE: #2}}
+\newcommand\TASK{\\ {\scriptsize TASK}}
+\newcommand\grizy{$grizy$}
+
+% allow paragraphs to be listed in TOC for now 
+\setcounter{tocdepth}{3}
+
+\begin{document}
+\maketitle
+
+% -- Revision History --
+\RevisionsStart
+% version     Date         Description
+% DR.01 & 2004.01.01 & First draft  \\ \hline
+% DR.02 & 2004.03.10 & Second draft \\ \hline
+% DR.03 & 2004.04.13 & Most paragraphs fleshed out \\ \hline
+% DR.04 & 2004.04.27 & Basic text frozen for internal review \\ \hline
+% DR.05 & 2004.05.24 & Incorporating comments from internal review \\ \hline
+00      & 2004.08.06 & Revisions in prep of SRR \\ \hline
+01      & 2004.10.29 & Revisions based on SRR \\ \hline
+\RevisionsEnd
+
+\inserttbd
+
+\inserttbr
+\pagebreak 
+
+\tableofcontents
+\pagebreak 
+
+\listoffigures
+\pagebreak
+\pagenumbering{arabic}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Scope}
+
+\subsection{Identification}
+
+This document is the Software Requirements Specification (SRS) for the
+Panoramic Survey Telescope and Rapid Response System (Pan-STARRS)
+Image Processing Pipeline (IPP) for the prototype telescope PS-1, and
+is a System-level controlled specification/design description document
+in the official Pan-STARRS engineering specification tree.
+
+\subsection{System Overview}
+
+The Institute for Astronomy at the University of Hawaii is developing
+a large optical synoptic survey telescope system, the Panoramic Survey
+Telescope and Rapid Response System (Pan-STARRS). The science goals,
+priorities, top-level concept of operations with associated
+operational requirements, and system performance drivers with
+associated system performance requirements are described in the
+Pan-STARRS Science Goals Statement (SGS).  As described in this
+document, The system conceptual design for Pan-STARRS utilizes an
+array of four 1.8m telescopes each with a 7 degree$^2$ field of view,
+giving the system an \'etendue larger than all existing survey
+instruments combined (defined as the product of the collecting area
+$A$ multiplied by the field-of-view solid angle $\Omega$).  Each
+telescope will be equipped with a 1 billion pixel CCD camera with low
+noise and rapid read-out, and the data will be reduced in near real
+time to produce both cumulative static sky and difference images from
+which transient, moving, and variable objects can be
+detected. Pan-STARRS will be able to survey up to $\approx 6,000$
+degree$^{2}$ per night to a detection limit of approximately 24$^{th}$
+magnitude.  This unique combination of sensitivity and sky coverage
+will open up many new possibilities in time domain astronomy including
+a major goal of surveying the Potentially Hazardous Object (PHO)
+population down to a diameter of $\approx 300$ meters.  In addition,
+the Pan-STARRS data will be used to investigate a broad range of
+astronomical problems of extreme current interest concerning the Solar
+System, the Galaxy, and the Cosmos at large.  A prototype single
+telescope system, PS-1, is being developed as a preliminary step
+before construction of the complete four telescope system.
+
+\begin{tabular}{ll}
+Project sponsor:&	AFRL, United States Air Force \\
+Acquirer:       &	University of Hawaii Institute for Astronomy \\
+User: 		&	Astronomical community \\
+Developer:      &	University of Hawaii Institute for Astronomy, participating \\
+                &       institutions, and associated subcontractors	
+\end{tabular}
+
+\subsection{Document Overview}
+
+The Pan-STARRS IPP Software Requirements Specification contains the
+complete system requirements of the Pan-STARRS PS-1 IPP in order to
+achieve the top-level performance and operational requirements
+specified by the SCD.  The requirements flow begun in the SGS and
+continued in the SCD is further developed in this SRS to provide
+additional derived system and subsystem requirements.
+
+\subsection{Requirements Definitions}
+
+The Pan-STARRS document naming scheme is PSDC-NNN-MMM-VV, where the VV
+entry specifies the document version number.  Where documents are
+identified without the version number, the latest official version in
+that series is implied.  
+
+Open issues (TBDs) in this document are marked {\bf \color{red} in
+bold red}.
+
+Quantities which should be reviewed (TBRs) are marked {\bf
+\color{blue} in bold blue}.
+
+\subsubsection{``Shall''}  When used in this specification, the word
+``shall'' refers to an explicit requirement of a system component or
+the complete system.  
+
+\subsubsection{``Should''}  When used in this specification, the word
+``should'' refers to a desired characteristic of a system component or
+the complete system.
+
+\subsubsection{``Will''}  When used in this specification, the word
+``will'' provides information about a characteristic of a related
+system component or a complete related system.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\DocumentsInternalSection
+PSDC-230-001  &   PS-1 Design Reference Mission \\ \hline
+PSDC-230-002  &   PS-1 System Concept Definition \\ \hline
+PSDC-400-006  &   The Pan-STARRS IPP Computational Challenge \\ \hline
+PSDC-430-004  &   Pan-STARRS PS-1 IPP C Code Conventions \\ \hline
+PSDC-430-006  &   Pan-STARRS PS-1 IPP Algorithm Design Document \\ \hline
+PSDC-430-007  &   Pan-STARRS PS-1 IPP PSLib Supplementary Design Requirements Specification \\ \hline
+PSDC-430-010  &   Pan-STARRS PS-1 IPP Perl Code Conventions \\ \hline
+PSDC-430-011  &   Pan-STARRS PS-1 IPP System/Subsystem Design Description \\ \hline
+PSDC-430-012  &   Pan-STARRS PS-1 IPP Modules Supplementary Design Requirements Specification \\ \hline
+\DocumentsExternalSection
+Posix Standard & Open Group Based Specifications Issue 6, IEEE Std 1003.1, 2003 \\
+\DocumentsEnd
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Requirements} 
+
+\begin{table}
+\begin{center}
+\caption{Valid Moon Conditions for the 6 PS filters\label{moonconditions}}
+\begin{tabular}{lrrrr}
+\hline
+\hline
+filter & phase (days) & min. distance (degrees) \\
+\hline
+g &  6 & 60 \\
+r &  5 & 40 \\
+i &  4 & 30 \\
+z &  2 & 20 \\
+y &  1 & 10 \\
+w &  5 & 50 \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsection{Top-Level Requirements}
+\label{req:system-capabilities}
+
+The Pan-STARRS System Concept Definition (SCD) specifies the derived
+top-level requirements for the IPP, which we reproduce here (with
+numbering consistent within this document):
+
+\begin{enumerate}
+\item For images obtained in photometric weather with normal detector
+  characteristics and providing appropriate flat-field images and
+  correction data have been obtained, the IPP shall produce reduced
+  science images for each full camera exposure with relative
+  photometric zero-point scatter less than 1\% ($1 \sigma$) across the
+  full field. \VER{ANALYSIS}{SCD:3.2.2.5}
+  \label{TLR:1}
+
+\item For images of reference fields calibrated for the IPP filter set
+  and obtained in photometric weather with normal detector
+  characteristics and providing appropriate flat-field images and
+  correction data have been obtained, the IPP shall determine and
+  track zero-points for these exposures with a 1$\sigma$ accuracy of
+  1\%.\VER{ANALYSIS}{SCD:3.2.2.5}
+  \label{TLR:2}
+
+\item For images obtained under normal seeing conditions and optical
+  distortion, the IPP shall produce reduced science images for each
+  full camera exposure with an astrometric calibration providing $<
+  30$ milliarcsecond scatter (1$\sigma$) for sequential images of the
+  same location.\VER{ANALYSIS}{SCD:3.2.2.7}
+  \label{TLR:4}
+
+\item For images obtained under normal seeing conditions and optical
+  distortion, the IPP shall produce reduced science images for each
+  full camera exposure with an astrometric calibration providing $<
+  100$ milliarcsecond scatter (1$\sigma$) relative to the ICRS
+  reference system.\VER{ANALYSIS}{SCD:3.2.2.6}
+  \label{TLR:3}
+
+\item In photometric weather and under moon conditions listed in
+  Table~\ref{moonconditions}, the IPP shall produce reduced science
+  images for each full camera exposure which have background
+  variations of less than 1\% in regions free of large ($> 30$ pixels
+  diameter) astronomical structures.\VER{ANALYSIS}{SCD:3.5.12}
+  \label{TLR:5}
+
+\item In photometric weather, the IPP shall produce reduced science
+  images for each full camera exposure which have background
+  deviations from the static sky in the same filter of less than 1\%
+  for the median in large ($> 30$ pixels diameter)
+  regions.\VER{ANALYSIS}{SCD:3.5.12}
+  \label{TLR:5a}
+
+\item The IPP shall merge all $g$ filter science images into a static sky image.\VER{TASK}{SCD:3.2.2.10}
+  \label{TLR:6}
+
+\item The IPP shall merge all $r$ filter science images into a static sky image.\VER{TASK}{SCD:3.2.2.10}
+  \label{TLR:7}
+
+\item The IPP shall merge all $i$ filter science images into a static sky image.\VER{TASK}{SCD:3.2.2.10}
+  \label{TLR:8}
+
+\item The IPP shall merge all $z$ filter science images into a static sky image.\VER{TASK}{SCD:3.2.2.10}
+  \label{TLR:9}
+
+\item The IPP shall merge all $y$ filter science images into a static sky image.\VER{TASK}{SCD:3.2.2.10}
+  \label{TLR:10}
+
+\item The IPP shall merge all $w$ filter science images into a static sky image.\VER{TASK}{SCD:3.2.2.10}
+  \label{TLR:11}
+
+\item The IPP shall detect and classify objects on the individual processed science
+  images.\VER{TASK}{SCD:3.2.2.16}
+  \label{TLR:12}
+
+\item The IPP shall detect and classify objects on the stacked groups
+  of science images.\VER{TASK}{SCD:3.2.2.16}
+  \label{TLR:13}
+
+\item The IPP shall detect and classify objects on the static sky
+  image.\VER{TASK}{SCD:3.2.2.16}
+  \label{TLR:14}
+
+\item The IPP shall detect transients with significance $>3\sigma$ in
+  the individual science images relative to the static sky
+  image.\VER{ANALYSIS}{SCD:3.2.2.16}
+  \label{TLR:15}
+
+\item The IPP shall degrade the stacked image by no more than 150
+  milliarcseconds (FWHM added in quadrature) over the theoretical
+  limit for the stack under infinite
+  sampling.\VER{ANALYSIS}{SCD:3.5.2}
+  \label{TLR:16}
+
+\item The IPP shall perform the processing of science images to the
+  level of transient detection and static sky inclusion at a rate such
+  that exposures taken at an average cadence of 40 seconds do not
+  accumulate in the processing buffer (average throughput
+  requirement).\VER{TEST}{SCD:3.2.2.3}
+  \label{TLR:17}
+
+\item The IPP shall limit the false alarm rate (FAR) to less than 5\%
+  for transient detections $> 5\sigma$ sent to the preferred client
+  science pipelines.\footnote{note difference with PS-4: 1\%}
+  \VER{ANALYSIS}{SCD:3.2.2.13}
+ \label{TLR:18}
+
+\item The IPP shall perform transient detection to a completeness of
+  99\% at the completeness for transient detections with a significance
+  $> 5\sigma$.\VER{ANALYSIS}{SCD:xxx}
+
+\item The IPP shall publish the static sky images to the Pan-STARRS
+  Published Science Products Subsystem (PSPS) at a rate so the full
+  sky is transmitted once per year.\VER{TASK}{SCD:3.2.2.18}
+  \label{TLR:19}
+
+\item The IPP shall publish the detected objects to the Pan-STARRS
+  Published Science Products Subsystem (PSPS) at a rate such that the
+  objects from the full sky are transmitted once per
+  year.\VER{TASK}{SCD:3.2.2.18}
+  \label{TLR:20}
+
+\item The IPP shall send the IPP metadata and received OTIS metadata
+  to the Pan-STARRS Published Science Products Subsystem (PSPS)
+  weekly.\VER{TASK}{SCD:3.2.2.18}
+  \label{TLR:21}
+
+\item The IPP shall provide access to preferred Pan-STARRS science
+  clients to the detected transient objects within 15 minutes with at
+  least 85\% reliability.\VER{TEST}{SCD:3.5.10}
+  \label{TLR:22}
+
+\item The IPP shall provide sufficent storage volume for raw images from the AP and
+  IVP Surveys and the \grizy\ Static Sky.\footnote{note difference with
+  PS-4: 1 month of raw images} \VER{INSPECT}{allocated}
+  \label{TLR:23}
+
+\item The IPP shall provide sufficient storage volume for all detections from the
+  AP, IVP, and MVP Surveys.\footnote{note difference with PS-4: 1 year
+  of detections}\VER{INSPECT}{allocated}
+  \label{TLR:24}
+
+\item The IPP shall provide sufficient storage volume for 2 years of
+ metadata.\footnote{note difference with PS-4: 10 years of
+ metadata}\VER{INSPECT}{allocated}
+  \label{TLR:25}
+\end{enumerate}
+
+\subsection{Required States}
+
+The IPP has 3 operating states: active, paused, and interactive.  In active state, the IPP:
+
+\begin{itemize}
+\item Accepts images and metadata from the external sources (i.e., the summit)
+
+\item Automatically performs the complete set of image processing
+  tasks, including both calibration and science image
+  processing.
+
+\item Responds to requests for data from client science pipelines,
+possibly pre-registered classes of data requests.
+
+\item Responds to analysis priority requests issued by the IPP operators.
+\end{itemize}
+
+In paused state, the IPP refuses incoming data and metadata and data
+requests from the client science pipelines.
+
+The interactive state is intermediate between these two.  In
+interactive state, the IPP:
+
+\begin{itemize}
+\item Accepts incoming data and metadata from the external sources.
+\item Does {\em not} automatically process the data.
+\item Responds to user commands to perform portions of the data analysis.
+\end{itemize} 
+
+\subsection{Software Coding Requirements}
+
+\subsubsection{Languages}
+\label{req:languages}
+
+\begin{enumerate}
+\item Source code shall be in C. \VER{INSPECT}{allocated}
+\item All source code shall be tested with `gcc' version v2.95 or higher. \VER{INSPECT}{allocated}
+\item The tested compiler version shall be defined for the delivered software product. \VER{INSPECT}{allocated}
+\item Scripting language shall be Perl. \VER{INSPECT}{allocated}
+\end{enumerate}
+
+\subsubsection{Interfaces}
+\begin{enumerate}
+\item Access to low-level Library functions shall be provided via C APIs consisting of the function calls and the defined data structures and other data types. \VER{INSPECT}{allocated}
+\item Access to high-level functions shall be provided via C APIs. \VER{INSPECT}{allocated}
+\item Access to specified C functions in higher level languages shall employ SWIG. \VER{INSPECT}{allocated}
+\item Access to processing jobs shall be available via the UNIX shell. \VER{INSPECT}{allocated}
+\end{enumerate}
+
+\subsubsection{Coding Standards} 
+
+\begin{enumerate}
+\item The C code shall comply with ANSI Standard C99.   \VER{INSPECT}{allocated}
+\item Because the delivered code is required to run on UNIX machines, the delivered code shall be in compliance with the language-independent UNIX operating system standard POSIX (Open Group Based Specifications Issue 6, IEEE Std 1003.1, 2004).\VER{INSPECT}{allocated}
+\item Source code files shall use the UNIX line-break convention (line-feed only).  \VER{INSPECT}{allocated}
+\item C coding style shall adhere to the standard defined in the document `Pan-STARRS IPP C-coding standard' (PSDC-430-004).  \VER{INSPECT}{allocated}
+\item Perl coding shall follow the standard defined in the document `Pan-STARRS IPP Perl-coding standard' (PSDC-430-010).\VER{INSPECT}{allocated}
+\end{enumerate}
+
+\subsubsection{Naming Conventions}
+
+\begin{enumerate}
+\item Header files shall have names starting \code{ps} or \code{p_ps}
+for private interface definitions. The latter shall appear in a
+subdirectory \code{private} of whichever directory is being searched
+for the public header files.\VER{INSPECT}{allocated}
+
+\item Functions visible at global scope that are part of the public
+API shall have names beginning with \code{ps} and follow the naming
+conventions in the coding standard. \VER{INSPECT}{allocated} 
+
+\item Functions visible at global scope but which are not part of the
+public interface shall have names beginning with \code{p_ps}.\VER{INSPECT}{allocated}
+ 
+\item Functions that are local to a file shall \textit{not} start with
+\code{ps} or \code{p_ps}.\VER{INSPECT}{allocated}
+ 
+\item Variables visible at global scope which are part of the public
+API shall have names beginning with \code{ps}, and follow the naming
+conventions in the coding standard.  \VER{INSPECT}{allocated}
+
+\item Variables that are visible at global scope but which are not
+part of the public interface shall have names beginning with
+\code{p_ps}.\VER{INSPECT}{allocated}
+
+\item Variables that are local to a file shall \textit{not} start with
+\code{ps} (or \code{p_ps}).\VER{INSPECT}{allocated}
+
+\item The names of all enumerated types and C-preprocessor symbols
+(but not variables declared \code{const}) shall start with \code{PS_},
+in the case of public symbols, or \code{P_PS_}, for private symbols.
+The rest of the name shall be uppercase with words separated by
+underscores (\code{_}). An exception is the case of system utilities
+implemented as macros, in which case the names shall conform to the
+convention for function names.\VER{INSPECT}{allocated}
+
+\item When defining a function to convert from one type to another,
+the name shall be of the form \code{psOldToNew},
+e.g.\code{psEquatorialToEcliptic} (\emph{not}
+\code{psEquatorial2Ecliptic}).\VER{INSPECT}{allocated}
+\end{enumerate}
+
+\subsubsection{C Programming Guidelines}
+
+\begin{enumerate}
+\item Functions that assign to a variable shall list that argument
+\textit{first}, following the pattern of \code{strcpy}. \VER{INSPECT}{allocated}
+
+Type definitions should always be accompanied by prototypes for their
+constructors.  Corresponding destructors are private functions
+registered with the PSLib memory management system.
+
+\item The constructor name shall consist of the type name followed by
+\code{Alloc}; e.g. a type \code{psImage} would be created by a
+function \code{psImage *psImageAlloc();}.\VER{INSPECT}{allocated}
+  
+\item The constructor shall never return \code{NULL}, so code calling
+the constructor should not check the return value.\VER{INSPECT}{allocated}
+  
+\item The destructor shall not return a value.\VER{INSPECT}{allocated}
+  
+\item Constructors and Destructors shall use the memory reference
+  counter facilities of the PSLib memory management system.\VER{INSPECT}{allocated}
+\end{enumerate}
+
+\subsubsection{Commenting and Documentation}
+
+\begin{enumerate}
+\item Commenting of delivered C code shall follow the C coding
+  standards and provide tags for Doxygen interpretation of the
+  comments and program structures.\VER{INSPECT}{allocated}
+
+\item Commenting of delivered Perl code shall follow the Perl
+  coding standards.\VER{INSPECT}{allocated}
+
+\item Source code documentation shall be generated with Doxygen from
+  the in-line comments and shall be provided as HTML, Latex, and man
+  pages.  \VER{INSPECT}{allocated}
+
+\item User documentation includes the API usage for the modules and
+  library functions as well as user interface description for the
+  higher-level architectural systems.  User documentation shall be
+  delivered as PDF documents.\VER{INSPECT}{allocated}
+\end{enumerate}
+
+\subsubsection{Version Control}
+
+Source code version control shall be implemented with CVS.  \VER{INSPECT}{allocated}
+
+\subsubsection{CSCI Deliverable}
+
+All final source code generated for the IPP shall be delivered via CVS,
+including the test code.  CVS revision history shall be included and
+made available via CVS.\VER{INSPECT}{allocated}
+
+\subsubsection{Platform architectures and operating systems}
+
+Makefiles shall be provided with appropriate flags set so that all
+code compiles without warnings under `gcc -Wall' for the following
+platform architectures and operating systems:\VER{INSPECT}{allocated}
+
+\begin{itemize}
+\item x86/Linux
+\item PPC/OS-X
+\end{itemize}
+
+The requirement of compiling without warnings includes the allowance
+that the output may be filtered to exclude known, specified warnings,
+such as those caused by lex-generated code.  
+
+Although the code shall compile successfully under both listed
+operating systems, unit testing should only be performed for the
+x86/Linux combination.
+
+\subsubsection{Timing measurements}
+
+Timing requirements specified in this document shall be achieved on the
+deployed Pan-STARRS analysis computers.\VER{INSPECT}{allocated}
+
+\subsubsection{Software Configuration}
+
+The IPP software configuration management system shall follow the
+processes outlined by the Pan-STARRS IPP Software Configuration
+Management Place (PSDC-430-003).\VER{INSPECT}{allocated}
+
+\subsection{Architectural Components}
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/IPPoverview.ps}}
+\caption{ \label{overview} IPP System Overview}
+\end{center}
+\end{figure}
+
+As discussed in the Pan-STARRS System Concept Definition
+(PSDC-230-002), the IPP is organized into a number of clearly-defined
+software elements.  The SCD provides a detailed description of these
+subsystems.  In brief, the IPP consists of: a collection of science
+analysis programs which perform the stages of the data analysis; a set
+of architectural components which provide the infrastructure needed to
+run the analysis programs; and a collection of hardware on which all
+of the software elements exist and operate.
+
+The architectural components consist of:
+
+\begin{itemize}
+
+\item {\bf IPP Image Server:} This component is a large data store for all
+ images used by the IPP, including the raw images from the telescope,
+ the master calibration images, the reference static-sky images, and
+ any temporary image data products produced by the IPP.  The Image
+ Server may also store large data files which do not contain imaging
+ data.  The Image Server accepts the incoming data and stores it until
+ it is no longer needed by other portions of the IPP.
+
+\item {\bf IPP Metadata Database:} This component is used to store all
+ other data which are neither image files nor astronomical object
+ data.  The Metadata Database is the authoritative source for all
+ metadata data, including metadata which may be duplicated elsewhere,
+ such as in the headers of images in the image database.
+
+\item {\bf Astrometry \& Photometry Database (AP):} This component is
+ used to store and manipulate astronomical objects detected in images
+ processed by the IPP, including individual measurements of objects on
+ the images, the summary information about those objects, and
+ reference object data.  It includes descriptive information about the
+ images, filter, cameras, telescopes, and other aspects of the system
+ needed to interpret the object data.
+
+\item {\bf IPP Controller:} In order to perform the analysis stages
+ required by the IPP, it is necessary to use distributed computing
+ processes on a large number of computers.  The Controller is required
+ to manage the collection of analysis stages performed on these
+ machines.
+
+\item {\bf IPP Scheduler:} This component is a decision-making mechanism
+ required to guide the operation of the IPP: to evaluate the currently
+ available collection of data, to identify the necessary analysis, and
+ to assign the analysis tasks to the Controller.
+
+\end{itemize}
+
+The relationship between these software elements is shown in
+Figure~\ref{overview}.  This figure also shows the interactions
+between the IPP and other Pan-STARRS systems.  The following sections
+identify requirements of these five software elements.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsubsection{Image Server}
+
+%% IPP Image Server Requirements
+
+IPP Image Server has the following requirements:
+
+\begin{enumerate}
+\item The IPP Image Server shall accept raw images from the summit at
+ a sustained rate of 1 exposure (2~GB) per 40 seconds.
+ \VER{TEST}{TLR:17, TLR:23}
+
+\item The IPP Image Server nodes shall not be offline for more than 12 hours
+  consecutively or 36 hours per year.\VER{ANALYSIS}{TLR:17}
+
+\item The IPP Image Server shall provide a seekable, unix file-system
+  reference to the specified image.\VER{TEST}{allocated}
+
+\item The IPP Image Server shall provide a total data capacity of 400
+  TB after the first year of PS-1 operations and 750 TB after the
+  second year of operations.\VER{INSPECT}{}
+
+\item The IPP Image Server shall provide storage for a total of $2
+  \times 10^8$ data objects by the end of the second year of PS-1
+  operations.
+
+\end{enumerate}
+
+\subsubsection{Metadata Database}
+
+%% Metadata DB Requirements
+
+The Metadata Database has the following requirements:
+
+\begin{enumerate}
+\item The IPP Metadata Database shall accept metadata from the summit
+   at a nightly average rate of 1 MB per 40 second.\VER{TEST}{TLR:17,
+   TLR:21, TLR:25}
+
+\item The Metadata Database queries shall have a latency of $< 0.1$
+  seconds.\VER{TEST}{TLR:17}
+
+\item The Metadata Database shall be capable of at least 100 queries
+  per second.\VER{TEST}{TLR:17}
+
+\item The Metadata Database shall be capable of accepting a total data
+  volume after 2 years of operation of 280 GB. \VER{INSPECT}{TLR:25}
+
+\item The Metadata Database shall restrict write access of the
+  scientific parameters to a different group from the software and
+  hardware configuration parameters.\VER{TEST}{allocated}
+\end{enumerate}
+
+%% Table: Metadata data classes
+\begin{table}
+\begin{center}
+\caption{Metadata Classes\label{metadata}}
+\begin{tabular}{l}
+\hline
+\hline
+raw images \\
+pending images \\
+master detrend images \\
+processed images \\
+static sky images \\
+detrend residuals \\
+object detection statistics \\
+master detrend creation statistics \\
+astrometry residuals \\
+warping statistics \\
+processing timing \\
+software installation information \\
+software configuration information \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsubsection{AP Database}
+
+%% IPP AP DB Requirements
+The IPP AP Database has the following performance requirements:
+
+\begin{enumerate}
+\item The AP Database shall accept new detections at the rate
+  generated by the telescope from the Phase 2 and Phase 4 analysis.
+  Except within 10 degrees of the galactic plane, the AP Database
+  shall keep up with the incoming rates.  The expected rates are
+  listed in Table~\ref{APrates}, along with the total data volume
+  required for storage space over the PS-1 lifetime.\VER{TEST}{TLR:2,
+  TLR:3, TLR:22}
+
+\item The AP Database shall provide access to external Pan-STARRS
+  clients to the detected transient objects within 15 minutes after
+  the image is obtained with an 85\% reliability.\VER{TEST}{TLR:22}
+  \label{IPP:DeReq:29c}
+\end{enumerate}
+
+%%% Table: AP DB parameters 
+\begin{table}[hb]
+\begin{center}
+\caption{AP Detection Classes \& Object Parameters\label{APdetections}}
+\begin{tabular}{lrrrr}
+\hline
+\hline
+Object Parameter & P2 & P4$\Sigma$ & P4$\Delta$ & SS \\ 
+\hline
+PSF x,y, covar, $\alpha,\delta$               & + & + & + & + \\
+PSF mag, $\sigma_{\rm mag}$                   & + & + & + & + \\
+star/gal sep                                  & + & + & + & + \\
+$\sigma_x$, $\sigma_y$, $\theta$              & + & + & + & + \\
+local sky data                                & + & + & + & + \\
+Petrosian R, M, $R_{50}$, $R_{90}$            & - & + & - & + \\
+S\'ersic R, M, AB, $\phi$, $\nu$              & - & + & - & + \\
+W.L. $\gamma_1$, $\gamma_2$, pol. terms       & - & - & - & + \\
+exp. spaced aps., Poisson noise, variance     & - & - & - & + \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+%%% Table: AP DB Throughput 
+\begin{table}
+\begin{center}
+\caption{AP Data Volume and Throughput Requirements\label{APrates}}
+\begin{tabular}{lrrr}
+\hline
+\hline
+Quantity                    & P2                & P4$\Sigma$        & P4$\Delta$        \\
+\hline
+detection limit             & $20 \sigma$       & $5 \sigma$        & $3 \sigma$        \\
+depth (r')                  & 21.8              & 24.0              & 24.5              \\
+bytes star$^{-1}$           & 64                & 100               & 64                \\
+stars deg$^{-2}$ ($|b|>10$) & $2.0 \times 10^5$ & $8.0 \times 10^5$ & $2.0 \times 10^5$ \\
+stars FPA$^{-1}$ ($|b|>10$) & $1.4 \times 10^6$ & $5.6 \times 10^6$ & $1.4 \times 10^6$ \\
+stars sec$^{-1}$ ($|b|>10$) & $3.5 \times 10^4$ & $3.5 \times 10^4$ & $8.8 \times 10^3$ \\
+MB sec$^{-1}$               & 2.3               & 3.5               & 0.6               \\
+AP total TB                 & 7.7               & -                 & -                 \\               
+IVP total TB                & 13                & 20                & 3                 \\               
+MOPS total TB               & 4                 & 6                 & 1                 \\               
+PS-1 total TB               & 25                & 26                & 4                 \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsubsection{Controller}
+
+%% IPP Controller Requirements
+
+IPP Controller requirements:
+
+\begin{enumerate}
+
+\item The IPP Controller shall manage tasks on a cluster of up to 128
+  computers.\VER{TEST}{TLR:17}
+
+\item The IPP Controller shall limit command latency to $< 0.1$ seconds.\VER{TEST}{TLR:17}
+
+\item The IPP Controller shall be capable of performing up to 10 tasks per second.\VER{TEST}{TLR:17}
+
+\item The IPP Controller shall be capable of buffering up to a total of 64 MB of messages.\VER{TEST}{TLR:17}
+
+\item The IPP Controller shall be capable of executing up to 6 million tasks per month.\VER{TEST}{TLR:17}
+
+\item The IPP Controller shall be capable of interacting with up to 256 client processes.\VER{TEST}{TLR:17}
+
+\item The IPP Controller shall be capable of accepting up to 2 non-client (external) requests per second.\VER{TEST}{TLR:17}
+\end{enumerate}
+
+\subsubsection{Scheduler}
+
+%% IPP Scheduler Requirements
+
+The IPP Scheduler requirements:
+
+\begin{enumerate}
+\item The IPP Scheduler shall publish the static sky images to the
+  Pan-STARRS PSPS at a rate so that the full sky is transmitted once
+  per year.\VER{TEST}{TLR:19}
+
+\item The IPP Scheduler shall query the Databases on a regular basis
+  to check for new input information.  These queries shall take place
+  at least once every second.\VER{TEST}{TLR:17}
+
+\item The IPP Scheduler shall accept new User input in real-time:
+  within 0.1 seconds of the request.\VER{TEST}{TLR:17}
+
+\item The IPP Scheduler shall publish the detected objects to the
+  Pan-STARRS PSPS at a rate so that the objects from the full sky are
+  transmitted once per year.\VER{TEST}{TLR:20}
+
+\item The IPP Scheduler shall publish the IPP and OTIS metadata to the
+  Pan-STARRS PSPS on a time-scale of 1 week.\VER{TEST}{TLR:21}
+
+\item The IPP Scheduler shall send the detected single-occurance
+  transient objects to the MOPS subsystem within 5 minutes of the
+  image exposure time.\VER{TEST}{TLR:22}
+
+\item The IPP Scheduler shall send the metadata appropriate to the
+  images from which single-occurance transient objects were detected
+  to the MOPS subsystem within 5 minutes of the image exposure
+  time.\VER{TEST}{TLR:22}
+\end{enumerate}
+
+%%%%%% Analysis Stages
+
+%%%% Science Image Analysis Stages
+
+\subsection{Science Image Analysis Stages}
+
+We now consider the requirements of the science image analysis tasks
+which are performed by the IPP.  These tasks represent the core of the
+required IPP functionality; the architectural components discussed
+above can be viewed as primarily supporting infrastructure to enable
+the analysis tasks to be executed on the appropriate data and to store
+the results.
+
+The Science Image analysis stages together represent the basic data
+analysis required by the IPP.  Integral to our operational concept for
+the IPP is the division of the science image analysis into four
+phases, each of which represents a complete analysis on a particular
+unit of data.  The tasks and functions of these separate stages are
+discussed below.
+
+\subsubsection{General Science Image Analysis Requirements}
+There are several requirements which shall be met by the collection of
+science image analysis stages as a group.
+
+\begin{enumerate}
+\item The IPP Science Analysis shall pre-process the science images
+ with the master calibration images at a nightly average rate of 1
+ exposure (2~GB) per 40 seconds.\VER{TEST}{TLR:17}
+
+\item The IPP Science Analysis shall merge multiple pre-processed
+ science images into stacked images with corresponding signal-to-noise
+ maps at a nightly average rate of 1 exposure (2~GB) per 40
+ seconds.\VER{TEST}{TLR:17}
+
+\item The IPP Science Analysis shall excise pixels from the input
+ images which are outliers for the ensemble of corresponding pixels
+ with an efficiency of $> 99$\%.\VER{ANALYSIS}{TLR:18}
+
+\item The IPP Science Analysis shall merge the cleaned images into the
+ static sky image, and update the corresponding exposure (S/N) maps,
+ at a nightly average rate of 1 exposure (2~GB) per 40
+ seconds.\VER{TEST}{TLR:17}
+
+\item The maximum latency between the acquisition of an image and the
+  completion of the science image analysis is set by the science
+  requirements of the fast transient recovery programs.  The science
+  image analysis shall process images to the detection of transients
+  within 15 min of their acquisition with an 85\%
+  reliability.\VER{TEST}{TLR:22}
+
+\item The science image analysis stages shall processes up to 1000
+  science images per night.\VER{TEST}{TLR:17}
+
+\end{enumerate}
+
+%% Phase 1
+\subsubsection{Phase 1 : image processing preparation}
+
+The Phase 1 analysis stage is performed on each science exposure (each
+complete FPA image) to calculate basic astrometric data needed by the
+later stages.  The Phase 1 requirements are:
+
+\begin{enumerate}
+\item the Phase 1 analysis shall execute within 2 seconds for a
+  complete FPA image.\VER{TEST}{TLR:17, allocated}
+
+\item The Phase 1 analysis stage shall determine the astrometric
+  solution of the complete camera (FPA image) with an accuracy of 1
+  arcsec peak-to-peak deviation.\VER{TEST}{TLR:3}
+
+\item Bright-star extraction from the image data shall be performed in
+  less than 1 second.\VER{TEST}{TLR:17}
+  
+\item In order for blind astrometry of an image to succeed, it is
+  necessary that approximate image coordinates be known.  The Phase 1
+  analysis shall succeed despite initial coordinate errors as large as
+  20\arcsec.\VER{TEST}{TLR:3}
+  
+\end{enumerate}
+
+%% Phase 2
+\subsubsection{Phase 2 : image reduction}
+  
+Phase 2 is the detrend stage, in which each detector is separately
+processed to remove instrumental signatures.  The result of Phase 2 is
+an image with high-quality astrometric and photometric calibrations, a
+collection of objects detected in the image and characterized in a
+rudimentary way (star / non-stellar), and a measurement of the PSF
+across the detector.  The Phase 2 requirements are:
+
+\begin{enumerate}
+\item The complete Phase~2 analysis shall be performed in $< 38$
+  seconds for up to 4 complete FPA images at one
+  time. \VER{TEST}{TLR:17}
+
+\item The bias subtraction shall leave no residuals greater than 1 DN
+  peak-to-peak for images within the normal range of bias
+  variations.\VER{TEST}{TLR:1}
+
+\item The Phase 2 flat-field correction shall handle zero-valued
+  pixels in the flat-field image without raising floating point
+  exceptions, setting the corresponding bit value in the
+  mask.\VER{TEST}{TLR:1}
+
+\item The flat-fielded image shall have a consistent photometric
+  zero-point across the chip, and across the full FPA, with scatter $<
+  0.2\%$ and peak-to-peak deviations of $< 0.5\%$.\VER{ANALYSIS}{TLR:1}
+
+\item The residual after the background subtraction shall have an
+  average offset of 0 counts, excluding the signal from astronomical
+  features.\VER{ANALYSIS}{TLR:5}
+
+\item The background residuals shall have peak-to-peak variations of
+  less than 1\% of the input background
+  amplitude.\VER{ANALYSIS}{TLR:5}
+
+\item The background residuals shall have a scatter of less than 1\%
+  of the input background amplitude for apertures of less than 10
+  arcsec.\VER{ANALYSIS}{TLR:1}
+
+\item The Phase 2 analysis shall detect cosmic rays with flux $> 5\sigma$ by
+  morphology in single images with an efficiency of $> 95$\% for
+  images which are not undersampled.  \VER{TEST}{TLR:18}
+
+\end{enumerate}
+
+%% Phase 3
+\subsubsection{Phase 3 : exposure analysis}
+
+The Phase 3 analysis uses the objects detected in Phase 2 and external
+reference catalogs to determine improved photometric and astrometric
+calibrations for the FPA as a whole, and to improve the measurement of
+the PSF and sky variations across the field.  The Phase 3 requirements
+are:
+
+\begin{enumerate}
+
+\item The complete Phase~3 analysis shall be performed in $< 2$
+seconds for up to 4 complete FPA images at one time. \VER{TEST}{TLR:17}
+
+\item For images obtained under normal observing conditions, the
+  resulting astrometric solution shall have a residual scatter of $<
+  30$ milliarcseconds when calibrated with the AP Survey reference
+  catalog and $< 200$ milliarcseconds when calibrated with the USNO-B
+  catalog.\VER{ANALYSIS}{TLR:4}
+
+\item For images obtained under normal observing conditions, the
+  resulting astrometric solution shall have systematic errors relative
+  to ICRS of $< 100 milliarcseconds$.\VER{ANALYSIS}{TLR:3}
+
+\item For images obtained under photometric conditions or minimal
+  cirrus conditions ($< 0.1$ mag total extinction), the resulting
+  photometric calibration shall have a relative accuracy of 5
+  millimagnitudes.\VER{ANALYSIS}{TLR:1}
+
+\item For images obtained under photometric conditions or minimal
+  cirrus conditions ($< 0.1$ mag total extinction), the resulting
+  photometric calibration shall have an absolution photometric
+  accuracy of 10 millimagnitudes when calibrated relative to the AP
+  Survey reference catalog.\VER{ANALYSIS}{TLR:1}
+
+\item For images obtained under photometric conditions or minimal
+  cirrus conditions ($< 0.1$ mag total extinction) and under the moon
+  conditions listed in Table~\ref{moonconditions}, the resulting sky
+  background subtraction shall leave behind peak-to-peak residuals $<
+  1$\% of the input sky flux.\VER{ANALYSIS}{TLR:1}
+
+\end{enumerate}
+
+%% Phase 4
+\subsubsection{Phase 4 : image combination}
+
+Phase 4 is the image combination stage, in which multiple images of
+the same portion of the sky are merged and confronted with the static
+sky image.  The Phase 4 requirements are:
+
+\begin{enumerate}
+\item The Phase 4 analysis shall not miss any pixels in this match, and
+  it shall read no more than 20\% more pixels than necessary from the
+  input images.\VER{TEST}{TLR:17}
+
+\item The warped images shall maintain photometric consistency with
+  the input image to within 0.2\%.\VER{TEST}{TLR:1}
+
+\item The sky representation shall degrade the image quality by less
+  than 150 milliarcseconds added in quadrature to the input image
+  quality.\VER{TEST}{TLR:1}
+
+\item The complete Phase~4 analysis shall be performed in $< 38$
+  seconds for up to 4 complete FPA images at one
+  time. \VER{TEST}{TLR:17}
+
+\item The Phase 4 analysis shall have a transient detection
+  completeness of 99\% for detections with a significance $> 5\sigma$.
+
+\item The Phase 4 analysis shall have a false detection rate of $<
+  5\%$ for transients detections with a significance $> 5\sigma$.
+
+\end{enumerate}
+
+\subsection{Calibration Stages}
+\label{mkcal}
+
+\subsubsection{General Calibration Construction Requirements}
+
+The Calibration analysis stages construct the various types of
+calibration frames needed by the IPP.  Requirements for the
+calibration processing include the following:
+
+\begin{enumerate}
+\item The IPP Calibration Analysis shall produce master calibration
+  images from the raw calibration images in less 2
+  hours.\VER{TEST}{TLR:17, TLR:22}
+
+\item Master calibration images shall not introduce systematic
+ uncertainties in the photometry greater than 0.2\%.\VER{TEST}{TLR:1}
+
+\end{enumerate}
+
+The calibrations consist of the following types of data:
+
+\begin{itemize}
+\item Mask
+\item Bias 
+\item Dark
+\item Flat-field
+\item Fringe Pattern
+\item Low-spatial-frequency sky model 
+\item Flat-field correction image
+\item Non-linearity correction
+\item Telescope astrometry model
+\item Zero-point corrections
+\end{itemize}
+
+\subsection{Modules}
+
+In order to encapsulate functionality, the analysis stages are
+constructed of a sequence of steps.  The analysis stages consist of a
+high-level script which executes a sequence of C-level functions.  The
+C-level functions executed by the script are called {\em modules} and
+represent basic data analysis operations.
+
+The required set of Pan-STARRS modules and their functionality is
+specified in the document `Pan-STARRS Image Processing Pipeline Modules
+Supplementary Design Requirements' (PSDC-430-012), and details of
+specific algorithms are specified in the document `Pan-STARRS Image
+Processing Pipeline Algorithm Design Document' (PSDC-430-006).
+
+\subsection{Pan-STARRS IPP Library}
+
+In order to facilitate testing and development, and to encourage
+flexibility, the IPP will be built in a layered fashion.  The lowest
+level functions will be written in C and collected together into a
+Pan-STARRS library, \code{PSLib}.  
+
+The Pan-STARRS Data Library will consist of C structures describing
+the basic data types needed by the IPP and C functions which perform
+the basic data manipulation operations.  The library is organized into
+four topics: System Utilities, Basic Data Collections, Data
+Manipulation, and Astronomy-Specific Functions.
+
+The required functionality of the Pan-STARRS Data Library is specified
+by the document `Pan-STARRS Image Processing Pipeline Library,
+Supplementary Design Requirements' (PSDC-430-007), and details of
+specified algorithms are specified in the document `Pan-STARRS Image
+Processing Pipeline Algorithm Design Document' (the ADD;
+PSDC-430-006).
+
+\subsection{Data Sources and Formats}
+
+\subsubsection{Image Formats}
+
+\begin{enumerate}
+\item Certain IPP programs shall be able to read and write standard
+  FITS images.\VER{TEST}{allocated}
+
+\item Certain IPP programs shall be able to read and write files in
+  modified FITS format with Pan-STARRS definitions for non-square
+  pixel arrays.\VER{TEST}{allocated}
+\end{enumerate}
+
+\subsubsection{Table Formats}
+
+Certain IPP programs shall be able to read and write FITS tables.\VER{TEST}{allocated}
+
+\subsubsection{Other Data Formats}
+
+Certain IPP program shall be able to read and write XML files.\VER{TEST}{allocated}
+
+\subsubsection{External Catalogs}
+
+\begin{table}
+\begin{center}
+\caption{Astrometric Reference Catalogs\label{AstroRefs}}
+\begin{tabular}{lrrrrl}
+\hline
+\hline
+Name       & scatter limit   & proper   & depth      & Nstars     & filters \\
+           & (milliarcsec)   & motion   &(mag)       & (millions) &         \\
+\hline
+Hipparcos  &   1             & 2        &  7.3       &    0.1     & {\em V}       \\ 
+Tycho2	   &  10             & 1        & 11.5       &    2.5     & {\em B,V}     \\ 
+UCAC-2     &  20             & 1        & 16.0       &   48.0     & {\em R}       \\ 
+USNO-A2.0  & 250             & N/A      & 19.0       &  526.2     & {\em B,R}     \\ 
+USNO-B1.0  & 200             & 20       & 21.0       & 1042.6     & {\em B,R}     \\ 
+2MASS	   &  70             & N/A      & 16.0       &  470.0     & {\em J,H,K}   \\ 
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}
+\begin{center}
+\caption{Photometric Reference Catalogs\label{PhotoRefs}}
+\begin{tabular}{lrrr}
+\hline
+\hline
+Name       & scatter  & depth & filters \\
+           & mmag     & mag   &         \\
+\hline
+SDSS       & 15       & 16    & {\em u,g,r,i,z} \\
+CFHT-LS    & 10       & 18    & {\em u,g,r,i,z} \\
+Landolt    & 10-20    & 15    & {\em U,B,V,R,I} \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+The IPP AP Database shall be able to interact with the externally
+provided reference catalogs listed in Table~\ref{AstroRefs} and
+Table~\ref{PhotoRefs}.\VER{TEST}{TLR:1, TLR:3}
+
+\subsubsection{Static Sky Pixel Size}
+
+The IPP static sky shall have a pixel scale of
+0.2\arcsec.\VER{ANALYSIS}{TLR:16}
+
+\subsection{External Interfaces}
+
+The IPP shall interact with several Pan-STARRS systems and with the
+Client Science Pipelines, but it has no requirements to interact with
+external systems which are not associated with the Pan-STARRS project. 
+
+\begin{enumerate}
+
+\item The IPP shall receive Metadata data from OTIS.\TASK
+
+\item The IPP shall send image quality assessments to OTIS.\TASK
+
+\item The IPP shall receive raw images from the Camera System.\TASK
+
+\item The IPP shall send 3$\sigma$ transient non-orphaned detections
+  to MOPS.\TASK
+
+\item The IPP shall send all 3$\sigma$ transient detections to
+  Transient Science Client.\TASK
+
+\item The IPP shall send static sky images to Science Database after
+  they are released for publication.\TASK
+
+\item The IPP shall send object detections from Phase 2, Phase 4 (Sum
+  and Difference detections) and the static sky images to the
+  Published Science Products System after they are release for
+  publication.\TASK
+\end{enumerate}
+
+\subsection{Internal Interfaces}
+
+The IPP has internal interfaces between several of the architectural
+components and between the architectural components and the analysis
+stages.  
+
+\begin{enumerate}
+
+\item IPP Scheduler - IPP Controller.  The IPP Scheduler shall send to
+the IPP Controller information about the tasks to be performed and
+shall receive from the IPP Controller descriptions of the success or
+failure of these tasks.\TASK
+
+\item IPP Scheduler - Metadata DB.  The IPP Scheduler shall query the
+Metadata DB to determine an appropriate course of action.  The IPP
+Scheduler shall send result and status information to the Metadata
+DB.\TASK
+
+\item IPP Controller - Analysis Tasks.  The IPP Controller shall
+ initiate the Analysis Tasks and monitor their output and exit
+status.\TASK
+
+\item Analysis Tasks - Metadata DB.  The Analysis Tasks shall be able
+to query the Metadata DB as needed to extract metadata needed for a
+given task.  The Analysis Tasks shall be able to send results and
+updates to the Metadata DB.\TASK
+
+\item Analysis Tasks - Image Server.  The Analysis Tasks shall be able
+to extract relevant images from the Image Server.  The Analysis Tasks
+shall be able to send output images to the Image Server.\TASK
+
+\item Analysis Tasks - AP DB.  The Analysis Tasks shall be able to
+extract information related to specific objects from the Astrometric
+and Photometric Database.  The Analysis Tasks shall be able to send
+result detections to the AP Database.\TASK
+\end{enumerate}
+
+\subsection{Internal Data Requirements}
+
+The internal data requirements of the IPP are left as detailed design
+decisions, and are specified within the IPP Supplementary Design
+Requirements Documents for the IPP Modules and Library.
+
+\subsection{Computer Hardware}
+
+\subsubsection{Overview}
+
+This section discusses the IPP PS-1 hardware requirements.  The
+hardware requirements addressed in this section consist of:
+
+\begin{itemize}
+\item Total Disk Volume
+\item Total Processing Power
+\item Sustained Switch Bandwidth
+\item Sustained Node Network I/O
+\item Sustained Disk I/O
+\item Availabilty
+\end{itemize}
+
+The report, `The Pan-STARRS Image Processing Pipeline Computational
+Challenge' (PSDC-400-006) discusses the assumptions and measurements
+made to determine the IPP computing requirements, for both the PS-1
+configuration and the PS-4 configuration, under multiple assumptions
+regarding the data volume.  The requirements in this section are
+derived from that report, and follow the minimal data volume
+assumptions for PS-1.
+
+\begin{table}[b]
+\begin{center}
+\caption{Data Storage Requirements \label{storage}}
+\begin{tabular}{lr}
+\hline
+\hline
+Raw data           & 400 TB \\ 
+static sky         & 350 TB \\
+calibration frames & 2.8 TB \\
+metadata db        & 0.3 TB \\
+AP db              &  55 TB \\
+\hline
+total              & 810 TB \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsubsection{Data Storage Requirements}
+
+The Pan-STARRS IPP data storage requirements may be divided into five
+principal areas: raw image data, static sky image data, master
+calibration images, the metadata database, and the object database.
+Table~\ref{storage} summarizes the data storage requirements for these
+types of data.  
+
+\begin{enumerate}
+\item The IPP shall store all raw images from the first year from the
+  AP and IVP surveys.  This corresponds to 180,000 images, or 360 TB,
+  assuming 2 GB per image.  The IPP will require space for 400 TB of
+  raw imagery to store the data from these two survey components along
+  with raw calibration, test, and short-term storage of other raw
+  images not in the AP and IVP surveys.\VER{INSPECT}{TLR:23}
+
+\item The IPP shall store a single copy of the complete static sky in
+  all 6 filters.  With the assumed image sampling of 0.2 arcsec per
+  pixel, this corresponds to 9.7 Tpix per filter, or a total of 350 TB
+  for the 6 filters, with 4 bytes for the image pixels and 2 bytes for
+  the noise map pixesl.\VER{INSPECT}{TLR:6, TLR:11}
+
+\item The IPP shall store all detections from the AP, IVP, and MVP
+  Surveys.  These detections make up a total of 55 terabytes (see
+  Table~\ref{APrates}). \VER{INSPECT}{TLR:24}
+
+\item The IPP shall store all metadata and master calibration images
+  from two years of PS-1 operation.  The metadata is a fraction of a
+  terabyte, while the calibration frames (all master detrend frames)
+  represent at most 2 terabytes.  \VER{INSPECT}{TLR:25}
+
+\item The IPP shall have storage capacity for a total of 810 TB of
+  data by the end of PS-1.
+\end{enumerate}
+
+\subsubsection{CPU Requirements}
+
+\begin{enumerate}
+\item The IPP shall provide sufficient computing resources to process
+images obtained at a cadence of 1 image per 40 seconds.\VER{TEST}{TLR:17}
+
+\item The IPP shall perform the Phase 1 and Phase 2 analyses within an
+average time of 40 seconds per single Gigapixel camera image.  The
+Phase 2 analysis has been measured to require 3200 GHz-sec on a
+Pentium-4 machine.\VER{TEST}{TLR:17}
+
+\item The IPP shall perform the Phase 3 and Phase 4 analyses on a set
+of 4 input frames within an average time of 180 seconds.  The Phase 4
+analysis has been measured to require a total of 7800 GHz-sec on a
+Pentium-4 machine for a major frame of 4 input Gigapixel camera
+images.\VER{TEST}{TLR:17}
+\end{enumerate}
+
+\subsubsection{Network I/O Requirements}
+
+The switch I/O requirements are defined by the total number of bytes
+per second serviced by the network switch.  In the assumption that all
+Phase 2 processing is performed locally on the nodes which store the
+raw images and the corresponding detrend images, and that all Phase 4
+processing requires complete network distribution of both the initial
+and updated static sky images, the total I/O for a 160 second
+major-frame period is:
+\begin{itemize}
+\item 8 GB from summit to Phase 2 (4 images @ 2 GB each)
+\item 18 GB from Phase 2 to Phase 4 (3 bytes per pixel for image +
+  mask, 50\% image overhead)
+\item 9 GB from Static Sky to Phase 4 (2.25 static-sky pixels per
+  input image pixel, 4 bytes per pixel).
+\item 9 GB from Phase 4 to Static Sky 
+\end{itemize}
+for a total of 44 GB, of which 26 GB are used by the Phase 2 nodes and
+36 are used by the Phase 4 nodes.  The IPP shall be capable of
+sustaining this network load.\VER{TEST}{TLR:17}
+
+\paragraph{Phase 2 Disk I/O Requirements}
+
+For each major frame processed, the total I/O to and from disk for
+Phase 2 is:
+\begin{itemize}
+\item 8 GB raw image from summit to Phase 2 nodes (4 images @ 2 GB each)
+\item 8 GB raw image from Phase 2 disk to memory
+\item 40 GB detrend image from Phase 2 disk to memory
+\item 12 GB processed image from memory to Phase 2 disk (2 bytes image
+  + 1 byte mask).
+\item 18 GB processed image from Phase 2 disk to Phase 4
+\end{itemize}
+for a total of 86 GB Disk I/O for Phase 2 for a complete major frame.\VER{TEST}{TLR:17}
+
+\paragraph{Phase 4 Disk I/O Requirements}
+For each major frame processed, the total I/O to and from disk for
+Phase 4 is:
+\begin{itemize}
+\item  9 GB static image from Phase 4 disk to memory
+\item  9 GB static image from memory to Phase 4 disk
+\end{itemize}
+for a total of 18 GB I/O for Phase 4 for a complete major frame.\VER{TEST}{TLR:17}
+
+\subsubsection{Total Node Requirements}
+
+The I/O and CPU requirements above may be confronted with reasonable
+assumptions of bandwidth and CPU speeds to estimate the number of
+nodes required for the IPP.  Each CPU is matched with one network
+adapter and one disk array.  
+\begin{enumerate}
+\item The IPP requires at least 40 Phase 2 Nodes (OTA Nodes)\VER{TEST}{TLR:17}
+\item The IPP requires at least 5 TB for each Phase 2 node\VER{TEST}{TLR:17}
+\item The IPP requires at least 25 Phase 4 Nodes (Static Sky Nodes)\VER{TEST}{TLR:17}
+\item The IPP requires at least 14 TB for each Phase 4 node\VER{TEST}{TLR:17}
+\item The IPP requires at least 10 AP DB Nodes\VER{TEST}{TLR:17}
+\end{enumerate}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Test Verification}
+
+A testing regime shall be implemented to demonstrate the working state
+of the provided software.  Certain tests as specified shall be
+performed by MHPCC, with code release contingent on success.  Other
+specified tests will be performed by IfA to verify the validity of the
+implemented algorithms.  The tests include: software configuration
+tests, software integrity tests, basic unit tests, and detailed
+functional analysis.
+
+\subsection{Software Configuration Tests}
+
+MHPCC shall test the validity of the software configuration,
+specifically to check that the code can be compiled on the specified
+platforms and that the compilation produces no errors or warnings,
+except as noted and allowed.
+
+\subsection{Software Integrity Tests}
+\begin{enumerate}
+
+\item MHPCC shall test that the code does not produce memory leaks.
+
+\item MHPCC shall test that the code does not produce segmentation faults.
+\end{enumerate}
+
+\subsection{Basic Unit Tests}
+
+MHPCC shall perform basic unit tests with sample input data and known
+output results, including invalid input data to test error handling.
+These tests shall exercise the complete range of module options.
+
+\subsection{Detailed Functional Analysis}
+
+IfA shall perform detailed tests with a wide range of input data and
+compare the results with existing software system.
+
+\subsection{Test Verification Matrix}
+
+Test Verification Matrix information is supplied with each identified
+requirement in this document.
+
+\subsection{Trace Matrix}
+\input{ippSRStrace.tex}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\clearpage
+\appendix
+
+\bibliographystyle{plain}
+\bibliography{panstarrs}
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/design/ippSSDD.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/ippSSDD.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/ippSSDD.tex	(revision 22322)
@@ -0,0 +1,5919 @@
+\documentclass[panstarrs]{panstarrs}
+
+% basic document variables
+\title{Pan-STARRS PS-1 Image Processing Pipeline}
+\subtitle{System/Subsystem Design Description}
+\shorttitle{IPP SSDD}
+\author{Eugene A. Magnier, Paul A. Price, Josh Hoblitt}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{00}
+\docnumber{PSDC-430-014}
+
+% allow paragraphs to be listed in TOC for now 
+\setcounter{tocdepth}{3} 
+
+\begin{document}
+\maketitle
+
+% -- Revision History --
+\RevisionsStart
+% version     Date         Description
+DR.01     & 2006.01.19 & First draft release to CDR committee \\ \hline
+DR.02     & 2006.01.22 & Second draft release to CDR committee \\ \hline
+00        & 2006.04.26 & First version release after CDR report \\ \hline
+\RevisionsEnd
+
+\inserttbd
+\inserttbr
+\pagebreak 
+
+\tableofcontents
+\pagebreak
+
+\listoffigures
+\pagebreak
+\pagenumbering{arabic}
+
+\section{Scope}
+
+\subsection{Identification}
+
+This document establishes Software Design Requirements for the
+Panoramic Survey Telescope and Rapid Response System (Pan-STARRS)
+Image Processing Pipeline (IPP) for the prototype telescope PS-1, and
+is a System-level controlled specification/design description document
+in the official Pan-STARRS engineering specification tree.
+
+\subsection{System Overview}
+
+The Institute for Astronomy at the University of Hawaii is developing
+a large optical synoptic survey telescope system, the Panoramic Survey
+Telescope and Rapid Response System (Pan-STARRS). The science goals,
+priorities, top-level concept of operations with associated
+operational requirements, and system performance drivers with
+associated system performance requirements are described in the
+Pan-STARRS Science Goals Statement (SGS).  As described in this
+document, The system conceptual design for Pan-STARRS utilizes an
+array of four 1.8m telescopes each with a 7 degree$^2$ field of view,
+giving the system an \'etendue larger than all existing survey
+instruments combined (defined as the product of the collecting area
+$A$ multiplied by the field-of-view solid angle $\Omega$).  Each
+telescope will be equipped with a 1 billion pixel CCD camera with low
+noise and rapid read-out, and the data will be reduced in near real
+time to produce both cumulative static sky and difference images from
+which transient, moving, and variable objects can be
+detected. Pan-STARRS will be able to survey up to $\approx 6,000$
+degree$^{2}$ per night to a detection limit of approximately 24$^{th}$
+magnitude.  This unique combination of sensitivity and sky coverage
+will open up many new possibilities in time domain astronomy including
+a major goal of surveying the Potentially Hazardous Object (PHO)
+population down to a diameter of $\approx 300$ meters.  In addition,
+the Pan-STARRS data will be used to investigate a broad range of
+astronomical problems of extreme current interest concerning the Solar
+System, the Galaxy, and the Cosmos at large.  A prototype single
+telescope system, PS-1, is being developed as a preliminary step
+before construction of the complete four telescope system.
+
+\begin{tabular}{ll}
+Project sponsor:&	AFRL, United States Air Force \\
+Acquirer:       &	University of Hawaii Institute for Astronomy \\
+User: 		&	Astronomical community \\
+Developer:      &	University of Hawaii Institute for Astronomy, participating \\
+                &       institutions, and associated subcontractors	
+\end{tabular}
+
+\subsection{Document Overview}
+
+The Pan-STARRS IPP System/Subsystem Design Description (SSDD) contains
+the complete design description of the Pan-STARRS PS-1 IPP in order to
+achieve the requirements specified by the Pan-STARRS PS-1 IPP Software
+Requirements Specification (SRS; PSDC-430-005).  The requirements flow
+which began in the SGS and Pan-STARRS system SCD, and continued in the
+IPP SRS and the IPP SCD, is used to guide the design presented here.
+
+\subsection{Requirements Definitions}
+
+The Pan-STARRS document naming scheme is PSDC-NNN-MMM-VV, where the VV
+entry specifies the document version number.  Where documents are
+identified without the version number, the latest official version in
+that series is implied.  
+
+Open issues (TBDs) in this document are marked {\bf \color{red} in
+bold red}.
+
+Quantities which should be reviewed (TBRs) are marked {\bf
+\color{blue} in bold blue}.
+
+\subsubsection{``Shall''}  When used in this specification, the word
+``shall'' refers to an explicit requirement of a system component or
+the complete system.  
+
+\subsubsection{``Should''}  When used in this specification, the word
+``should'' refers to a desired characteristic of a system component or
+the complete system.
+
+\subsubsection{``Will''}  When used in this specification, the word
+``will'' provides information about a characteristic of a related
+system component or a complete related system.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\DocumentsInternalSection
+PSDC-230-001  &   PS-1 Design Reference Mission \\ \hline
+PSDC-230-002  &   PS-1 System Concept Definition \\ \hline
+PSDC-400-006  &   The Pan-STARRS IPP Computational Challenge \\ \hline
+PSDC-430-004  &   Pan-STARRS IPP C Code Conventions \\ \hline
+PSDC-430-005  &   Pan-STARRS IPP PS-1 Software Requirements Specification \\ \hline
+PSDC-430-006  &   Pan-STARRS IPP Algorithm Design Document \\ \hline
+PSDC-430-007  &   Pan-STARRS IPP PSLib Supplementary Design Requirements Specification \\ \hline
+PSDC-430-010  &   Pan-STARRS IPP Perl Code Conventions \\ \hline
+PSDC-430-011  &   Pan-STARRS IPP System Concept Definition \\ \hline
+PSDC-430-012  &   Pan-STARRS IPP Modules Supplementary Design Requirements Specification \\ \hline
+PSDC-430-014  &   Pan-STARRS IPP PS-1 Cluster Support \\ \hline
+\DocumentsExternalSection
+Posix Standard & Open Group Based Specifications Issue 6, IEEE Std 1003.1, 2003 \\
+\DocumentsEnd
+
+\section{Subsystem Overview}
+
+The Pan-STARRS Image Processing Pipeline (IPP) performs the image
+processing and data analysis tasks needed to enable the scientific use
+of the images obtained by the Pan-STARRS telescopes.  The primary
+goals of the IPP are to process the science images from the Pan-STARRS
+telescopes and make the results available to other systems within
+Pan-STARRS.  It also is responsible for combining all of the science
+images in a given filter into a single representation of the
+non-variable component of the night sky defined as the ``Static Sky''.
+To achieve these goals, the IPP also performs other analysis functions
+to generate the calibrations needed in the science image processing
+and to occasionally use the derived data to generate improved
+astrometric and photometric reference catalogs.  It also provides the
+infrastructure needed to store the incoming data and the resulting
+data products.
+
+The IPP inherits lessons learned, and in some cases code and prototype
+code, from several other astronomy image analysis systems, including
+Imcat (Kaiser), the Sloan Digital Sky Survey (REF), the Elixir system
+(Magnier \& Cuillandre), and Vista (Tonry).  Imcat and Vista have a
+large number of robust image processing functions.  SDSS has
+demonstrated a working analysis pipeline and large-scale database
+system for a dedicated project.  The Elixir system has demonstrated an
+automatic image processing system and an object database system for
+operational usage.
+
+The users of the IPP output are all systems internal to the Pan-STARRS
+project.  They consist of: 1) the Preferred Science Clients, which
+receive specified data products on short timescales.  2) the Moving
+Object Processing System (MOPS), which is one of the Preferred Science
+Clients, but has the distinction of being a component funded by
+Pan-STARRS.  It will receive the detections of non-stationary
+transient objects.  3) the Published Science Products Subsystem
+(PSPS), which will receive all data products of interest to the
+community external to the Pan-STARRS data processing systems, and will
+act as the long-term archive and publishing clearinghouse.
+
+The IPP receives data from two Pan-STARRS subsystems: the Camera, from
+which it receives the large volume of image data, and OTIS
+(Observatory, Telescope and Infrastructure Subsystem), from which it
+receives metadata describing the images and the environmental
+conditions.  The location of the IPP computing hardware will evolve
+over the course of the first year of the PS-1 project.  Initially,
+during the start of the commissioning phase, a subset of hardware will
+be located at the summit facility; when the network connection is
+established and the new facility is available, the IPP hardware will
+be relocated to the MHPCC computer room in Kihei.  A subset of
+processing tasks may eventually be re-assigned to machines at the
+summit if justified by the savings in data transfer time and cost, but
+this implementation is considered a possible future evolutionary
+path.  The details of the hardware deployment plan are discussed in
+the section on the IPP hardware.  
+
+The Pan-STARRS camera produces images consisting of multiple chips
+(Orthogonal Transfer Arrays or OTAs), each consisting of multiple
+cells (continuous set of pixels).  The baseline design for the
+Pan-STARRS camera contains 64 chips each with 64 cells.
+
+This document defines the design requirements of the IPP for the PS-1
+prototype telescope.  Much of the IPP design for PS-4 will be
+identical to or closely based on the PS-1 implementation.  The
+software organization and the infrastructure systems are expected to
+be identical, with minor improvements in details.  The type of
+analysis steps to be performed will be nearly identical, with some
+additional details added for PS-4 to improve the accuracy.
+
+Although generally very similar, in terms of the IPP PS-1 differs from
+the complete PS-4 system in several specific ways.  First, with only
+one telescope and camera, the data throughput rate is substantially
+reduced to a maximum of 1 64-OTA image per 40 seconds rather than 4.
+Since PS-1 is a prototype for testing the Pan-STARRS hardware and
+software subsystems, the observing strategy is not a fixed quantity.
+The PS-1 Design Reference Mission (PSDC-230-001) provides some
+guidelines for the types of observing tests which will probably be
+performed, including possibly starting an Astrometric and Photometric
+Survey which will eventually cover the entire $3\pi$ steradians of the
+sky accessible to PS-4.  As a prototype, it is expected that much of
+the data collected by PS-1 will be processed multiple times to test
+and tune the analysis steps.  Compare with PS-4, this difference in
+approach has implications for the storage required by PS-1: rather
+than delete images soon after they have been used, raw images from
+demonstration observations must be stored for at least the first two
+years of PS-1 operations.  The PS-1 Design Reference Mission is used
+as an upper limit for these storage requirements to drive the hardware
+design.
+
+\subsection{System Design Decisions}
+
+Since Pan-STARRS is a survey project, all data from the telescopes
+will be uniformly analyzed by the Pan-STARRS Image Processing Pipeline
+(IPP), and the appropriate resulting data products made available to
+internal and external science analysis systems as they become
+available.  The processing performed by the IPP on the science images
+will consist of detrending and object detection for the individual
+images, combination of multiple overlapping images and further object
+detection, subtraction of a reference (static-sky) image and detection
+of residual objects, update of the static sky images, and detailed
+object analysis of the static sky images.  In addition, the IPP will
+produce improved astrometric and photometric reference catalogs on an
+as-needed basis.  The output data products from the IPP consist of the
+calibration images, reduced images from the individual telescopes,
+combined images, difference images, the static sky image, object
+photometry, and reference astrometry and photometry.
+
+The requirements for the IPP, as identified in the PS-1 IPP SRS
+(PSDC-430-005) fall into several broad categories: data analysis
+precision, throughput, system reliability, flexibility, testability,
+and traceability.  The details of the analysis tasks are specified in
+order to achieve the precision.  The architectural design as discussed
+below is motivated by the need for reliability and flexibility.  The
+hardware organization and the distributed/parallel processing model is
+motivated by the throughput requirements.  The need for flexibility
+and testability drives the software organization.  The need for simple
+testing procedures drives both the software organization and the
+separation of the system architecture into different infrastructure
+elements.
+
+\subsection{Analysis Stages, Programs, and Libraries} 
+
+The processing steps listed above can be divided into well-defined
+analysis stages, each of which operates on a particular unit of data,
+such as a single OTA image or a collection of astronomical objects.
+The different analysis stages defined by the IPP are discussed in
+Sections~\ref{sec:AnalysisStages} - \ref{sec:MiscStages}.  The
+analysis defined by these analysis stages is further divided into
+specific programs which perform the particular operation.  Some
+analysis stages are primarily defined by a single programs; others
+require multiple programs and substages.  It is also possible for a
+single program to be involved in multiple analysis stages.  The major
+IPP analysis programs are discussed in some detail in
+Section~\ref{sec:AnalysisPrograms}.  Other important programs in the
+collection called \code{ippTools} are used to define the analysis
+stages and their interrelationships (Section~\ref{sec:ipptools}).  The
+IPP software hierarchy is further divided into \code{psModules}
+(representing complex analysis operations, often astronomical in
+nature) and \code{psLib}, the data and analysis foundation library
+(Section~\ref{sec:SoftwareHierarchy}).
+
+Analysis tasks representing the different analysis stages are
+performed on the IPP computer cluster.  Note the distinction between
+the generic analysis {\em stage} and a specific analysis {\em task}.
+An analysis stage represents a type of analysis which is performed,
+such as the basic image calibration and object detection analysis.  An
+analysis task is a particular realization of an analysis stage, e.g.,
+the analysis of OTA number 61 from exposure 654321 to produce a
+specific set of output data products.  The analysis stages are
+discussed in detail in Section~\ref{sec:AnalysisStages}.
+
+A particular stage may process individual images, collections of
+images, or derived data products.  Because of the nature of the image
+data, many of the analysis stages can be run in parallel if needed to
+increase the processing throughput.  For example, the analysis of a
+chip in one image does not depend on the results from another chip.
+
+\subsection{Architectural Components}
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/IPPoverview}}
+\caption{ \label{fig:overview} IPP System Overview}
+\end{center}
+\end{figure}
+
+In order to achieve the required functionality, the IPP provides an
+infrastructure within which the Analysis Stages described above are
+executed.  In order to facilitate the subsystem testing, the IPP
+software infrastructure has been divided into a number of
+clearly-defined architectural software units.  In the following
+summary, these elements are described in terms of the concept they are
+addressing; in later sections, this document will discuss the
+specifics of the implementations used by the IPP for PS-1.
+
+\begin{itemize}
+
+\item {\bf Image/File Server:} This component is a large data store
+  for all images and large used by the IPP, including the raw images
+  from the telescope, the master calibration images, the reference
+  static-sky images, and any temporary image data products produced by
+  the IPP.  The Image/File Server accepts data products and stores
+  them until they are no longer needed by other portions of the IPP.
+  It allows other IPP subsystems to refer to the data files with an
+  abstract identification without needing to worry about the details
+  of the physical location.  Conversely, it allows other entities to
+  determine or specify the physical locations if needed.  The
+  Image/File Server is capable of storing any large data files which
+  are not well-suited for inclusion in a more structured relational
+  database, and for which access needs to be widely available beyond
+  the individual process which created the file.  The IPP has
+  developed the software system called 'Nebulous' to perform this
+  function.
+
+\item {\bf Metadata Database:} This component stores the data which is
+  not directly related to images or astronomical objects, but which is
+  needed to perform the IPP analyses.  The metadata may include the
+  summary weather information for each night, or details about the
+  filters, camera, telescopes, etc.  Note that the IPP Metadata
+  Database is not required to retain all archival engineering data
+  from all of Pan-STARRS; other Pan-STARRS subsystems use their own
+  internal databases to store engineering metadata and only the
+  necessary subset is transferred to the IPP Metadata Database.  The
+  IPP uses the MySQL database engine, together with stand-alone
+  software tools to define, manage and examine the Metadata Database
+  tables.  These tools and other access functions are built using
+  autogenerated APIs supplied by the software called 'glueforge'
+
+\item {\bf Astrometry \& Photometry Database:} This component stores
+  and manipulates astronomical objects detected in various images, as
+  identified above, including individual measurements of objects on
+  the images, the summary information about those objects, and
+  reference object data.  It also provides mechanisms for users to
+  query and manipulate the objects and detections.  For this
+  component, the IPP has adopted the tool called 'DVO' from the Elixir
+  system and has made / is making necessary upgrades to meet the
+  requiements of the PS-1 system.
+
+\item {\bf IPP Controller:} In order to achieve the required
+  processing throughput for the IPP analysis stages, it is necessary
+  to use distributed computing processes on a large number of
+  computers.  The IPP Controller manages the collection of analysis
+  tasks performed on these machines.  The actual implementation of the
+  controller is called 'pcontrol', and is strongly coupled to the
+  Scheduler discussed below.
+
+\item {\bf IPP Scheduler:} This component is a decision-making
+  mechanism which guides the operation of the IPP.  It evaluates the
+  currently available collection of data, identifies the necessary
+  analysis, and assigns the analysis tasks to the IPP Controller.  The
+  actual implementation of the scheduler is built on top of the
+  controller in a system called 'PanTasks'.  
+
+\end{itemize}
+
+The relationship between these software units is shown in
+Figure~\ref{fig:overview}.  This figure also shows the interactions
+between the IPP and other Pan-STARRS systems.  The implementation of
+the architectural components is discussed in detail in
+Section~\ref{sec:ArchComponents}.
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/IPPhardware}}
+\caption{ \label{fig:hardware} IPP Hardware Organization}
+\end{center}
+\end{figure}
+
+\subsection{IPP Hardware Organization}
+
+The IPP will utilize substantial computer resources, both in terms of
+computational power and in terms of data storage and network
+bandwidth.  The IPP requires relatively large amounts of data storage
+space, primarily for the image data.  Image data is organized in two
+categories.  First, there is the per-OTA data -- data associated with
+specific OTAs, including the raw images, the calibration images, and
+temporary processed images at various stages.  Second, there is the
+data associated with the static sky imagery, which is in turn
+organized into smaller sky-cell units.  In addition to image data,
+there are the somewhat smaller data entities of the Metadata Database
+and AP Database.
+
+The computer hardware is organized into nodes which provide both data
+storage and computational resources.  The data storage nodes are
+divided into three classes: those which deal with the per-OTA image
+data, those that provide the storage for the static sky images, and
+those that provide the storage for the other data systems, the
+Metadata Database and the AP Database.  In addition, the computational
+tasks related to the individual images take place on the per-OTA
+storage nodes and the processing of stacks of images takes place on
+the static sky storage nodes.
+
+Figure~\ref{fig:hardware} presents the basic concept for the hardware
+organization for the IPP.  This diagram shows the two types of compute
+nodes: (1) OTA-level processing and storage nodes and (2) Static Sky
+processing and storage nodes.  Also shown are two switches which
+divide the network into OTA and Static-Sky portions.  In such an
+organization, the inter-switch communication must meet the throughput
+needs between these network portions (though a single switch may also
+be used if its backplane capacity is sufficient).  The additional data
+systems (Metadata Database and AP Database) are also shown.
+
+\tbd{re-draw to use a single switch}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{System Design : Architectural Components}
+\label{sec:ArchComponents}
+
+\subsection{Nebulous : the IPP Image/File Server}
+
+\subsubsection{Corresponding Requirements}
+
+The IPP Image/File Server must meet the requirements specified in
+Section 3.4.1 of the Pan-STARRS PS-1 IPP SRS (PSDC-430-005).  The
+specified design is chosen to meet requirements 3.4.1.3, and 3.4.1.5.
+The other three requirements (3.4.1.1, 3.4.1.2, and 3.4.1.4) depend on
+the volume and capabilities of the hardware, and are addressed in
+Section~\ref{sec:Hardware}.  
+
+\subsubsection{Image/File Server Overview}
+
+The IPP Image/File Server is a repository for all images and other
+large data files required by the IPP.  Along with the storage
+hardware, it provides tools for managing the distribution of these
+large data files and for accessing the files.  Data files stored by
+the IPP Image/File Server include the raw images, the calibration
+images, intermediate processing stage images as needed, final
+processed images, difference images, image subsections, and any large
+non-imaging data files needed by the IPP.  The IPP Image/File Server
+must retain the files for as long as they are needed by the IPP.
+
+The IPP team has developed the software system 'Nebulous' to meet the
+requirements of the Image/File Server.  Nebulous uses a MySql database
+engine to track the identities and locations of the files under its
+management.  Nebulous currently requires that all files it manages be
+available from a locally mounted file system; however, it does not
+place specific requirements upon the choice of this file system.
+Currently, the IPP is being implemented use NFS as the distributed
+file system, though other systems such as GFS or GSFS could be used in
+an equivalent fashion.  
+
+The decision to make use of a locally mounted file system is driven by
+two IPP-wide design decisions.  First, as a practical and technical
+consideration, in several locations throughout the IPP analysis, the
+efficiency can be increased significantly if processes have the
+ability to seek randomly in a file.  For example, when combining
+multiple partially-overlapping images, rather the be forced to read
+through an entire image data, the proccess can seek through a
+multi-component file to the pixels of interest.  The other driving
+motivation is more philosophical in nature: the IPP is designed to use
+components which operate as simple user commands in the UNIX
+environment.  By requiring Nebulous to work with files in the locally
+mounted file system, all UNIX programs will be able to interact with
+those files if needed.  This allows the IPP to include essentially any
+normal analysis program as part of the IPP system without difficulty.
+This philosophical choice is also present in the design of the IPP
+Scheduler / Controller system and in the design implementation of the
+data analysis programs. 
+
+Nebulous is a parallel storage system.  It stores data across a
+collection of computer nodes, each with their own data storage
+resources.  Any single file is stored on only a single computer and
+storage device.  In order to achieve the data throughput requirements,
+Nebulous will be used to distribute the images across the processor
+nodes in an organized fashion, i.e., associating specific machines
+with specific detectors.  It is not the responsibility of Nebulous to
+determine which computer should be associated with a specific data
+concept (Chip / region of sky), but it instead provides the hooks to
+enable the association of a particular file with a particular machine.
+
+There are three data concepts relevant to Nebulous:
+\begin{itemize}
+\item {\bf Storage object:} This represents a single, unique data
+  entity managed by Nebulous.
+
+\item {\bf Storage ID:} This is the identifier of a particular storage
+  object in Nebulous.  The Storage ID is the key used by any Nebulous
+  users to retrieve a specific file of interest.  The file ID is
+  simply a unique string, equivalent to the filename in a UNIX file
+  system.
+
+\item {\bf Instance (or Storage Object Instance):} A single copy of
+  the storage object in Nebulous.  In general, a given storage object
+  may have several instances in Nebulous, normally on different
+  computer nodes.
+
+\end{itemize}
+Upon request of a specific Storage ID, Nebulous provides file pointers
+(in C), handles (in Perl), or file names corresponding to the
+instances of the storage objects.  
+
+Nebulous provides the storage and access mechanisms, but it does not
+include any logic or information about the data.  It does not, e.g.,
+monitor the age of images and delete them on some schedule.  This
+functionality currently resides in the IPP Scheduler
+(Section~\ref{sec:pantasks}).
+
+As shown in Figure~\ref{fig:Nebulous}, Nebulous consists of the
+following principal components:
+\begin{itemize}
+\item Nebulous client(s)
+\item Nebulous server
+\item Nebulous database 
+\item Storage hardware 
+\end{itemize}
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/ImageServer}}
+\caption{The components of Nebulous, the IPP Image/File Server.}
+\label{fig:Nebulous}
+\end{center}
+\end{figure}
+
+\subsubsection{Nebulous Client APIs}
+
+Clients interact with the IPP Image Server via a small number of C
+APIs.  Bindings are also provided for Perl \tbd{and Python} and UNIX
+shell commands in some cases.  This document only gives an overview of
+the commands; for details on usage, please see the Nebulous user's
+guide.  The client commands are:
+
+\begin{itemize}
+\item {\tt create} : create a new storage object in the Image Server.
+  This function takes as input the requested Storage ID and returns a
+  C-style file pointer, Perl file handle, or file name corresponding
+  to the new instance of the storage object.  The arguments to the
+  function include an optional node name on which the new storage
+  object must be located.  If this target is not given, the Image
+  Server places the new storage object on an appropriate machine from
+  the pool.
+
+\item {\tt replicate} : a new instance of the given storage object is
+  created.  The target node may be optionally specified, otherwise an
+  appropriate node is selected.
+
+\item {\tt cull} : removes one of the instances of the storage object.
+  The input parameters may optionally specify the target machine from
+  which to delete the object.
+
+\item {\tt delete} : deletes all instances of the storage object and
+  sets the storage object status to {\tt deleted}.
+
+\item {\tt open} : open an instance of an existing storage object, as
+  identified by the storage ID.  This function may also specify the
+  node on which the object should be opened (if an instance of the
+  object is not stored on that node, the function returns an error).
+  On success, the function returns a file pointer.  If the object is
+  opened for 'write' access, all but one instance is deleted to ensure
+  consistency of the data.  
+
+\item {\tt find} : return a list of filenames in the UNIX name space
+  associated with the storage object identified by the given file ID.
+  Since there are in general multiple instances for a given storage
+  object, this function returns the collection of all available
+  instances.  These may be freely opened by the client server using
+  the standard \code{fopen} functions. 
+
+\item {\tt lock} : attempt to acquire a Nebulous lock on the storage object.
+
+\item {\tt unlock} : release a Nebulous lock from the storage object.
+
+\item {\tt stat} : returns status information about the specified
+  storage object, including the number of instances of the object.
+
+\item {\tt copy} : create a new storage object with one instance of
+  the corresponding object.
+
+\item {\tt move} : rename a storage object
+
+\item {\tt import} : copy an existing file object into Nebulous.  
+
+\end{itemize}
+
+\subsubsection{Nebulous Server}
+
+The Nebulous client requests are mediated via the Nebulous server.
+Communication between the clients and the server is via SOAP
+implementing the commands above.  The identity of the machine on which
+the Nebulous server runs is part of the Nebulous configuration
+information.
+
+The server is responsible for keeping track of storage objects, all
+instances of that object, and enforcing locking semantics.  Extensive
+logging and tracing support is provided for debug and to allow for
+statics generation and possible {\em hotspot} optimization.
+
+Nebulous uses a centralized server model.  This model was choosen
+because it allows efficient {\em pattern matching} of storage object
+names.  The current 'best' technique for a distributed metadata store
+is with distributed hash tables.  Unfortunately, no widely available
+DHT implementation allows efficient {\em pattern matching} of key
+names.
+
+\paragraph{House keeping}
+
+\subparagraph{Lock sweeping} 
+In the event that a Storage Object operation fails to complete
+successfully stale locks will have to be identified and removed from
+the IPP Pixel Data Server Database.  This should be done periodically
+by comparing the entries in the Lock table to the list of active nodes
+maintained by the IPP Controller.  It should also happen as soon as
+possible after a node goes offline (triggered by the IPP Controller
+marking a node as offline?).  A sweep must be /completed/ before an
+offline node can be marked on-line.
+
+Once a node is determined to be offline all entries in the Lock table
+set by that node should be identified.  The locks on the Storage
+Object Instances pointed to by those entries should then be rolled
+back and Lock Record entries themselves must be removed from the lock
+table.
+
+\subparagraph{Consistency sweeping} 
+Periodically the IPP Pixel Data Server meta-data and Storage Object
+will need to be checked for sanity.  This would be similar to running
+fsck on a modern filesystem.  Consistency sweeping should include Lock
+sweeping and should be considered a super-set.
+
+\subsubsection{Nebulous Database}
+
+The Nebulous Server uses a database to store the information about the
+data storage objects, their instances, and the available hardware
+resources.  A {\tt mysql} database engine is used to manage the
+database table.  The database tables defined for the Image Server are
+listed in Table~\ref{tab:ImageServerTables}, and their contents are
+listed in Appendix~\ref{sec:ImageServerTableContents}.  This database
+engine is not in general the same one used for other IPP subsystems;
+the full IPP hardware configuration will include independent machines
+for each of the major databasing systems (Nebulous, Metadata, DVO).
+In the earlier incarnations, the same hardware and database engine may
+be used.
+%
+\begin{table}[ht]
+\begin{center}
+\caption{Nebulous Database Tables\label{tab:ImageServerTables}}
+\begin{tabular}{ll}
+\hline
+\hline
+{\bf Table Name} & {\bf Description} \\
+\hline
+\code{storage_object}  & all storage objects known to Image Server \\
+\code{instance}        & all instances of all storage objects \\
+\code{volume}          & data storage devices known to Image Server \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsubsection{Nebulous Storage Hardware}
+
+Nebulous manages data across a collection of computers and as needed
+on multiple storage devices on those computer nodes.  Nebulous
+maintains a table of the available data volumes.  It tracks
+information about each volume such as the total capacity, the current
+capacity, the association between computer and data volume.  \tbd{Is
+Nebulous responsible for detecting unavailable hardware?  it is
+reponsible for changing allocations?  or is this a pantasks
+responsibility?}
+
+\subsubsection{Requirements Demonstrations}
+
+\tbd{summary of throughput tests : create / copy / delete objects per
+  second, etc}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Metadata Database}
+\label{sec:Metadata}
+
+\subsubsection{Corresponding Requirements}
+
+The Metadata Database must meet the requirements specified in Section
+3.4.2 of the Pan-STARRS PS-1 IPP SRS (PSDC-430-005).  The specified
+design is chosen to meet requirements 3.4.2.1, 3.4.2.2, 3.4.2.3,
+3.4.2.4, 3.4.2.5.
+
+\subsubsection{Overview}
+
+The IPP Metadata Database acts as a repository for most non-pixel data
+needed by the IPP subsystems.  This includes the image metadata, the
+environmental data, system configuration data and system reference
+data.  The Metadata Database is required to save the non-ephemeral
+data for the lifetime of the project for future reference and
+additional analysis.  The Metadata Database is also used in close
+coupling with the analysis pipelines to track the state of elements as
+they move through the processing system.  Metadata which is large in
+volume or poorly structured is stored in an appropriate container file
+(FITS Table, FITS Header, XML File) in Nebulous, with the Metadata DB
+maintaining the corresponding Nebulous storage IDs of these files.
+
+The IPP Metadata Database is a simple database system, consisting of a
+number of simple tables without extensive inter-table links.  The IPP
+uses the MySQL database engine for the the database.  To simplify the
+coding and management of the database, the IPP uses autocoded APIs
+constructed with the system called 'glueforge' to define and
+manipulate the Metadata Database tables.
+
+\begin{table}[hb]
+\begin{center}
+\caption{Metadata Database Tables\label{tab:MetadataDBTables}}
+\begin{tabular}{ll}
+\hline
+\hline
+{\bf Table Name}           & {\bf Description} \\
+\hline
+Weather                    & Details on the weather, including internal temperatures. \\
+SkyProbe Transparency      & Analysis of SkyProbe B \& V data. \\
+SkyProbe Absorption        & Analysis of SkyProbe A data. \\
+SkyProbe Emission          & Analysis of SkyProbe E data. \\
+DIMM                       & Summary of DIMM data analysis. \\
+NIR                        & Summary statistics from NIR camera. \\
+Dome Status                & The time history of the dome status. \\
+Telescope Status           & The time history of the telescope status. \\
+Raw FPAs                   & Information about the raw FPA exposures. \\
+Pending Science Chips      & Science images to be processed and status. \\
+Processed Science Chips    & Science images which have been migrated to the processed state. \\
+Observation Group          & Details about a group of associated observations. \\
+Observation Frame          & Major frame information. \\
+Science Processing stats   & Details on processed cells. \\
+Chip / Sky overlaps        & List of overlaps between sky cells and detectors. \\
+Processed Sky-Cell stats   & Details of the sky cell processing. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsubsection{Metadata Tables}
+
+Table~\ref{tab:MetadataDBTables} lists the Metadata tables identified to
+date for the Metadata Database.  The contents of these tables are
+outlined in Appendix~\ref{sec:MetadataTableContents}, with examples for
+the data entries and their data types in many cases.  Additional
+tables will be added as necessary as the data analysis scripts are
+fleshed out in detail.  The Metadata Database, with a flat data
+organization, is flexible enough to add additional information as it
+is identified.
+
+\subsubsection{Autocoded Metadata Queries}
+
+The IPP provides standardized interfaces to the Metadata tables using
+the 'glueforge' system.  Glueforge uses a standard table description
+file to construct a collection of standard interface functions with
+easily predicted names.  Given the description of a table (say Foo),
+Glueforge provides a C-struct which represents the elements of single
+row of the table (\code{FooRow}).  It also provides APIs to create a
+new Foo table (\code{FooCreateTable()}), to insert a row either
+supplying the elements of Foo (to \code{FooInsert()}) or by supplying
+a pointer to data of type Foo (to \code{FooInsertObject()}).  Simple
+queries may be constructed to select rows from the table
+(\code{FooSelectRow()}).  The same mechanism generates data I/O
+functions for writing FITS tables from a collection of the data
+elements.  
+
+This autocoding system for interacting with the Metadata database
+makes the software very flexible for changes to the structures of the
+Metadata database tables.  The programmer does not need to know
+anything about the details of a given table to interact with it,
+except when a specific element is needed.  Thus, new columns can be
+added to the tables, and only require a re-compilation for most
+portions of the IPP code.  Even migration to a new table schema for
+existing data becomes fairly trivial.  Such a migration only would
+require the definition of a conversion function from the old structure
+to the new structure.  The more general features of glueforge are
+discussed in the glueforge manual pages.  
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{DVO : the AP Database}
+
+\subsubsection{Corresponding Requirements}
+
+The AP Database must meet the requirements specified in Section 3.4.3
+of the Pan-STARRS PS-1 IPP SRS (PSDC-430-005).  The specified design
+is chosen to meet requirements 3.4.3.1 and 3.4.3.2.  The IPP is
+modifying the Elixir program 'DVO' to perform the role of the IPP AP
+Database.  
+
+\subsubsection{Overview}
+
+DVO, the Desktop Virtual Observatory, is a software system which
+stores data related to astronomical objects derived from various
+sources, and provides mechanisms to related multiple detections
+together as astronomical objects.  DVO deals with two related
+concepts: {\em objects} and {\em detections}.  The {\em objects} are
+descriptions of astronomical objects while the {\em detections} are
+the specific measurements of those objects, typically measured from
+astronomical images.  A collection of {\em detections} may be used to
+derive average quantities which describe a particular {\em object}.  A
+third class of measurement to be considered are those supplied by
+external references.  Such measurements may be treated as {\em
+detections}, with the caveat that access to the raw measurements and
+metadata are usually unavailable: the reported measurements and errors
+must be accepted as they are reported.
+
+DVO stores the collections of detections which were derived from
+specific images.  It provides a mechanism to determine the image from
+which a specific detection was derived, and in conjunction with the
+Image Server locate the corresponding data file.  DVO also makes it
+possible to extract all detections derived from a specific image and
+to determine quantities such as the pixel coordinates of the detection
+on the image.
+
+DVO also has the capability to associate multiple detections of a
+specific object.  Several major classes of objects will be present,
+each of which must be handled correctly.  DVO distinguished the
+following types of objects.
+
+{\bf Stars, compact galaxies, and QSOs} will have nearly fixed
+locations relative to other distant stars, with only small deviations
+for individual measurements.  The association between multiple
+detections of such objects is made on the basis of their coincident
+positions.  DVO determines the average position of the object and the
+deviations of the individual detections from that average on the basis
+of the ensemble of individual detection.
+
+{\bf Solar System Objects} do not have a fixed location.  Detections
+of such objects are linked by their orbits, and depend on both the
+position and the time of the image.  DVO does not attempt to make this
+link; this is the role of the MOPS system.  However, it has the
+ability to accept identifications made externally with specified
+detections and to return the identifier of the moving object
+associated with the specific detections.  These associations also
+include descriptive information such as the offset of the detection
+from the predicted location of the detection based on the orbit.  This
+functionality is required to allow DVO to ignore known moving object
+detections from other types of queries.
+
+{\bf High-proper-motion objects} in the general vicinity of the solar
+system fall in between these first two classes of objects.  Their
+proper motion and parallax response is significant enough ($>0.2$
+arcsec in 1 year) that they are not well-described by an average
+location and a collection of offsets.  These objects are better
+described by a distance and a proper motion vector.  DVO provides the
+association between the specific detections and an average object
+which includes finite parallax and proper motion.
+
+{\bf Orphaned detections} are not associated with a specific
+astronomical object of any of the above classes.  Most of these will
+be spurious (not representing real objects), some will be from solar
+system objects for which orbits are not yet determined, some will be
+from faint stars near the detection limits, and some will be from
+short-term transients which have only been detected once.  DVO
+maintains these detections until they have been associated with one of
+the objects above.  DVO provides mechanisms by which individual
+detections may be migrated back and forth between the orphan state and
+association with an astronomical object.
+
+DVO stores the information about the detection, the related objects,
+and the images which provided the measurements.  For every detection,
+DVO provides the mechanisms to link the detection back to the image
+which supplied it.  DVO also provides the capability to determine the
+images containing a specific location but for which no detection was
+made.  The minimum set of information which must be carried for these
+non-detections is the image and the associated object or orphan.
+
+DVO also stores the relationships between various
+photometric systems and the evolution of that relationship.  It
+provides mechanisms to convert between the measured instrumental
+magnitude of a detection with a specific filter, detector, and
+telescope, and at a particular time and the implied magnitude in the
+average Pan-STARRS photometry system, given a determined set of
+calibrations.  It also provides the capability to convert magnitudes
+in one system to the magnitudes in another system; an example of such
+a conversion is between the average Pan-STARRS filter systems and the
+various reference systems appropriate for those filters.
+
+\subsubsection{Photometric systems and the DVO Photcodes}
+
+One of the major roles of DVO is to relate different photometric
+measurements made with different instruments and detectors together.
+We may have observations made with the same basic filters, but using a
+number of different detectors.  We may have observations from
+different telescopes in similar filters.  We may have reference data
+related to some filter, but obtained and published by other observers.
+We would like to related these measurements together in optimal ways,
+making use of whatever information we have available.  DVO provides
+several mechanisms to enable these relationships.
+
+We identify three distinct types of photometry measurements within
+DVO:
+\begin{itemize}
+\item {\bf reference photometry}  These measurements are provided by
+  external observers.  For reference photometry, we do not have access
+  to very must information used to determine the magnitudes of the
+  objects of interest.  We have the reference magnitudes corresponding
+  to a type of filter, and presumably some information of the error on
+  the measurement.  We might possibly know the epoch of the
+  observations, but not necessarily.  
+\item {\bf detection photometry} This is our primary measurement of
+  interest: the photometry of objects measured from images which we
+  have processed.  More specifically, the detection photometry is an
+  instantaneous measurement from a specific image with well-known
+  properties, such as exposure time, airmass, instrument source, etc.  
+\item {\bf internal photometry} With the application of an appropriate
+  zero point and other calibration terms, any detection photometry can
+  be calibrated to represent a measurement in a well-known photometric
+  system.  The internal photometry measurements are calibrated to be
+  on a photometric system which represents a consistent system for a
+  particular telescope or collection of data, minimizing the
+  calibration transformations necsessary.
+\end{itemize}
+
+Defining the relationships between the different types of measurements
+is part of the process of photometric calibration.  DVO uses the
+concept of the 'photcode' to identify the source of the photometry,
+and to define the relationships between different photometry sources.
+A photcode identifies a photometric system: for the detection
+photometry measurments, each combination of telescope, camera, filter,
+and detector is associated with a unique photcode; there are also
+unique photcodes for the internal photometry systems and any distinct
+external reference source.  
+
+As a concrete example, consider the Pan-STARRS PS-1 system.  There
+will be three different cameras in use at different times: GPC-1,
+TC-3, and the SkyProbe camera.  There are at least 6 filter systems:
+{\it grizy} and {\it w}.  The SkyProbe camera has a single CCD, TC-3
+has 16 different detectors, and GPC-1 has up to 64 different devices.
+Each of these combinations is potentially a different photometric
+system, so a different photcode is defined for each combination.
+These photcodes would have names such as: GPC1.02.r (r filter with the
+GPC1 camera and OTA 02) or SP1.00.g (SkyProbe 1, g filter).  These
+($64 \times 6 + 16 \times 6 + 5 = 485$) photcodes are all identified
+as 'detection' photcodes, specifying that detection photometry is
+associated with them
+
+There are also 6 different internal photometric systems of interest,
+namely those associated with the 6 named filters, {\it grizy} and {\it
+w}. Each of these 6 systems is identified with an internal photcode.
+The internal photcodes are further distinguished as 'primary' or
+'secondary', which specifies how the DVO system stores average
+quantities related to these types of photcodes (see the discussion of
+the tables below).  
+
+Finally, there may be multiple external photometric systems of
+interest, some of which are related to the major internal photometry
+systems, some of which are not.  For example, the Pan-STARRS project
+may refer to photometry from the SDSS secondary standards, the SDSS
+data releases, Johnson photometry from Landolt (1992), observations
+from 2MASS in $JHK$, USNO-B observations, and so forth.  Each of these
+photometric systems is assoiciated with a different photcode; only
+some of these are relevant to the detection or internal photometry
+system.
+
+Within DVO, the detection and internal photcodes each define a
+relationships as well as a specific photometric system.  Associated
+with each of these photcodes are the parameters of the photometry
+transformation from the photometric system of the photcode to another
+photometric system.  For the detection photcodes, the parameters
+define the transformation to the equivalent internal photcode system.
+The currently-defined transformation parameters consist of the
+following photometry equation:
+%
+\[ 
+M_\lambda = m_\lambda + C_\lambda + K_\lambda (\mbox{airmass} - 1) + \sum_{i = 1}^{i < N}
+A_{\lambda,i} (\mbox{color}_\lambda - \mbox{color}_{o,\lambda})^i 
+\] 
+%
+where $C_r$ represents the zero-point of the transformation, $K_r$
+represents the slope of the airmass trend, $\mbox{airmass}$ is the
+airmass for a given measurement, $\mbox{color}$ is the color of the
+source of interest (as identified below), $\mbox{color}_r$ is the
+reference color for sources in this photometry system, and $A_{r,i}$
+is the coefficient of the $i$ power of the color difference.  Up to
+fourth order color terms are currently allowed.  For any photcode, the
+color is defined as the difference of the measurements in two other
+photcodes, usually two 'internal' photcodes.  The photcode information
+also specified the equivalent photcode to which the transformation corresponds.
+
+For the detection photcodes, the target of the transformation must be
+an internal photcode.  For the internal photcodes, the target of the
+transformation is an external reference photcode system.  This
+restriction implies that the internal photometry may only be
+transformed (and thus compared with) a single external reference.
+This is in fact the best practice as far as photometric calibration is
+concerned: the 'standard' observations from different references
+should always be treated as different photometric systems.  To allow
+for the relationship of the internal photometry to multiple sources of
+reference photometry, an additional set of photcodes are defined which
+identify 'alternative' transformations for the internal photcodes.
+
+It is important to note that not all of the photometry transformation
+parameters identified above are relevant for each of the three major
+types of photcode.  The detection photcodes will in general make use
+of all of these elements, though the order of the color transformation
+will hopefully be limited if the different devices are sufficiently
+similar.  For the transformation from the internal photcodes, which
+are derivative in some way of the detection photcodes, the airmass
+component is invalid: for a single measurement, the
+detection-to-internal transformation has already removed the airmass
+trend; for an averaged internal photometric measurement, no single
+airmass corresponds to the observations.  Finally, no transformation
+parameters are defined for the reference photcodes at this time.
+
+DVO provides methods by which these photometry transforamtions are
+automatically applied.  The specific measurements (detection
+photometry) are stored in the database tables as instrumental
+magnitudes, and any operation which examines these measurements must
+make use of the APIs to convert to an appropriate common system.  A
+further complication to note is that the photcodes defined above are
+static; they do not include any information about changes to the
+system sensitivity.  This information is carried externally to the
+photcode calibration information; the transformations defined by the
+photcodes must be considered the {\em starting point} for any
+photometric analysis.  An additional adjusment can be applied.  
+
+The detections from a specific image may all have a 'calibration'
+offset applied which bring the measured photometry into a common
+relative system.  This calibration offset is associated with the image
+and may be a function of position on the detector.  The tables which
+carry the individual measurements also include the calibration
+magnitude appropriate for each measurement to speed up the application
+of this offset.  In a well-calibrated collection of photometry, all of
+the detection measurements will have a measured calibration magnitude,
+yielding a collection of internal photometry measurements which are
+all consistent.  An additional piece of information is the zero-point
+history, which tracks the system-wide variations in the average
+sensitivity.  The zero-point history can be used to predict the
+calibration magnitudes for any observation which is not tied directly
+via relative photometry to the rest of the photometric observations.
+
+Putting all of these pieces together, the photometry APIs in DVO can
+be used to return any of the following types of photometric
+measurements:
+\begin{itemize}
+\item raw instrumental magnitudes for any detection
+
+\item 'catalog' magnitudes, applying only the airmass and static
+  zero-point calibrations to a detection magnitude; this is useful to
+  test the detector-color transformation.
+
+\item 'system' measurements, applying the complete static
+  transformation for a detection magnitude to the internal photometry
+  system; for photometric weather and no zero-point variations, this
+  would be a measurement in the internal photometry system.
+
+\item 'relative' magnitudes, applying the measured calibration offset
+  to the calibrated detection magnitude determined above; in a
+  well-calibrated system, this represents a consistent internal
+  photometry measurement.
+
+\item 'calibrated' magnitudes, correcting the measure detection
+  photometry by applying the transformation from the internal
+  magnitude system to the external reference magntiude system.
+
+\item 'average' magntiudes, the raw internal photometry magnitudes
+  (note the distinction between the 'average' quantities, which are
+  derived from a collection of detections an the 'relative' quantities
+  which represent an instantenous measurement in the same system).
+
+\item 'reference' magnitudes, in which the 'average' internal
+  photometry values are transformed to the refernce magnitude system.  
+\end{itemize}
+The complexity of these transformations is necessary to allow the
+examination of the trends of actual measurements with external
+parameters.
+
+\subsubsection{DVO Database Tables}
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/dvo.01.ps}}
+\caption{\label{fig:DVOtables} \small Data types managed by DVO}
+\end{center}
+\end{figure}
+
+Figure~\ref{fig:DVOtables} illustrates the data managed by DVO, and
+Table~\ref{tab:DVOtables} provides a complete listing.  The contents
+of these tables are outlined in Appendix~\ref{sec:DVOTableContents}.
+Below, the use of these tables by DVO software is discussed below.
+Several of the tables are not just simple tables in the database but
+are instead table groups divided into many subtables, each of which
+represents a portion of the sky (a {\tt region}).  These subtables may
+also be distributed across different computers to distribute the
+processing load.
+
+\paragraph{Sky Regions Table}
+
+The {\tt Regions} table is used to subdivide the tables of images,
+objects, and detections, etc, as discussed above.  DVO
+divides the sky into a hierarchy of regions (portions of the sky) each
+of which is in turn subdivided into smaller portions.  Since nearly
+all interactions with DVO performed by the IPP are limited
+in spatial coverage, subdividing the tables allows a specific
+interaction to search only a small subset of the data.  The table of
+images is the smallest of the three; the table of detections is likely
+to be the largest.  As a result, the {\tt Images} table group will be
+subdivided at a shallow hierarchical level, while the {\tt Objects}
+and {\tt Detections} are subdivided on deeper (more finely sampled)
+levels.  The {\tt Regions} table defines the boundaries of the sky
+regions and specifies if the region corresponds to an {\tt Images}
+table, an {\tt Objects} table, and/or a {\tt Detections} table.  It
+also specifies which regions in the next level of the hierarchy are
+contained by the region, and which parent region it belongs to.  In
+addition to improving the spatial access to the image, object, and
+detection data, the {\tt Regions} table allows for multiple computers
+to serve the database tables.  The region file specifies the machine
+which stores the specific table.  Figure~\ref{fig:DVOskyregions}
+illustrates schematically the subdivision of the sky and the
+association between different levels of the hierarchy with different
+subtables.
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/dvo.02.ps}}
+\caption{DVO Regions and Image / Object tables}
+\label{fig:DVOskyregions}
+\end{center}
+\end{figure}
+
+\paragraph{Images Table Group}
+
+The {\tt Images} table group lists all of the images which provided
+the data in DVO.  These tables are subdivided by region on
+the sky.  In general, the images listed in this table correspond to
+the Chips.  This group of tables includes sufficient astrometric
+parameters to represent the coordinates of the detections to a
+sufficient accuracy.  Parallel to the Images table is the Mosaic
+table.  This table is very similar to the Images table, but defines
+the Mosaic which corresponds to a group of Images.  The parameters
+include the astrometric information needed to define the camera
+distortion.
+
+\paragraph{Image Overlaps Table Group}
+
+The specific subtable of {\tt Images} which contains a given image is
+the one which contains the center pixel of that image.  An additional
+table group, {\tt Image Overlaps} (with the same subtable organization
+as the {\tt Images} subtables), lists images which overlap that
+specific subtable.  Thus, given a particular coordinate, in order to
+find that images which overlap that coordinate, it is necessary to
+search the images in the {\tt Images} subtable which includes that
+coordinate, and all images in the {\tt ImageOverlaps} subtable for
+that coordinate.
+
+\begin{table}[hb]
+\begin{center}
+\caption{DVO Database Tables\label{tab:DVOtables}}
+\begin{tabular}{ll}
+\hline
+\hline
+{\bf Table Name} & {\bf Description} \\
+\hline
+Images               & The images that have objects in the DB. \\
+Image Overlaps       & Image regions which are touched by specific images. \\
+Objects              & The objects --- average properties of multiple detections of the same object. \\
+Average Magnitudes   & Average photometry in multiple filters \\
+Solar System Objects & Identification of solar system objects \\
+Matched Detections   & Detections of sources in an image identified with an Object. \\
+Orphaned Detections  & Detections of sources in an image not identified with an Object. \\
+Non-detections       & Non-detections of objects in an image. \\
+SkyRegions           & spatial distribution of tables \\
+Filters              & Filters understood by the system. \\
+Photcodes            & Transformations between different photometric systems \\
+Zero Points          & History of Zero-point \& Airmass terms \\
+Distortion Models    & History of Optical Distortion terms \\
+Database Hosts       & computers used to store the tables \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsection{Objects Table Group}
+
+\begin{table}
+\begin{center}
+\caption{DBO Detection Classes \& Object Parameters\label{tab:APdetections}}
+\begin{tabular}{lrrrr}
+\hline
+\hline
+Object Parameter & P2 & P4S & P4D & SS \\ 
+\hline
+PSF x,y, covar, $\alpha,\delta$               & + & + & + & + \\
+PSF mag, $\sigma_{\rm mag}$                   & + & + & + & + \\
+star/gal sep                                  & + & + & + & + \\
+$\sigma_x$, $\sigma_y$, $\theta$              & + & + & + & + \\
+local sky data                                & + & + & + & + \\
+Petrosian R, M, $R_{50}$, $R_{90}$            & - & + & - & + \\
+S\'ersic R, M, AB, $\phi$, $\nu$              & - & + & - & + \\
+W.L. $\gamma_1$, $\gamma_2$, pol. terms       & - & - & - & + \\
+exp. spaced aps., Poisson noise, variance     & - & - & - & + \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+The {\tt Objects} table group (also divided by region) stores the
+average parameters for each astronomical object.  Certain details of
+this table have not yet been specified.  In particular, objects with
+significant parallax and/or proper motion may potentially be stored in
+a distinct table.  Solar system object identifications, to the extent
+average properties are maintained in DVO, will certainly
+be stored in a separate table.  
+
+\paragraph{Average Magnitudes Table Group}
+
+A related table, also divided into the same regions, is the {\tt
+Average Magnitudes} table.  In this table, there are multiple rows per
+object, one for each of the primary filters of interest for which
+photometric averaging is performed.  This organization makes the
+number of primary (averaged) filters a configurable value.
+
+\paragraph{Matched Detections Table Group}
+
+The {\tt Matched Detections} table stores all of the measurements of
+astronomical objects on specific images.  This table includes all
+detections associated with the average {\tt Objects}.  As discussed
+below, bright objects (above a configuration-specified signal-to-noise
+level) are defined object even if only one detection has been found at
+that position.  Faint orphaned objects are not added to this list or
+the list of objects.  The different types of detections (P2,
+P4$\Delta$, P4$\Sigma$) are distinguished by their photometry codes.
+(This is only valid if DVO does not store different
+quantities for these types of detections.)
+
+\paragraph{Orphaned Detections Table Group}
+
+The {\tt Orphaned Detections} table stores the detections which have
+not been correlated with an existing object.  This table is only
+populated for objects below a configuration-specified signal-to-noise
+limit (e.g., 5$\sigma$).  Bright orphaned detections are assigned an
+object and added to the {\tt Matched Detections} table.
+
+\paragraph{Non-detections Table Group}
+
+The {\tt Non-detections} table stores information about detection
+failures for each object.  If an image is added to the database which
+overlaps an object but the object is not detected, an entry is made in
+this table.  In practice, this table may store only the most recent
+non-detection and the total number, or a similar reduced set of
+non-detection statistics.
+
+\paragraph{Other Reference Tables}
+
+The {\tt Filters} table identifies all of the physical filters
+(specific pieces of glass) known to the system.  A related table, {\tt
+Photcodes}, defines relationships between photometry systems.  A
+photometry system may consist of a detector, telescope, and specific
+filter, or it may be a derived photometry system.  The {\tt Database
+Machines} table identifies all of the computers available to DVO.
+
+\subsubsection{Database Table I/O}
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/dvo.03.ps}}
+\caption{\label{fig:DVOformats} \small DVO Table I/O }
+\end{center}
+\end{figure}
+
+DVO allows for a flexible representation of its data on disk.  Data
+may be written to disk in one four possible mode: RAW, FITS MEF, FITS
+SPLIT, and MYSQL.  These modes define the overall organization of the
+data on disk.  In the RAW mode, the data is written to disk in a
+pseudo-FITS table format which consists of a simple FITS header
+describing the layout followed by the binary data in a block.  This
+storage mode is maintained for historical reasons.  There are also two
+types of FITS modes in which the data tables are written as valid FITS
+Binary Tables.  In the SPLIT format, every data table is written as a
+separate file, while in the MEF format, the object and detection
+tables are bundled together into a single FITS file with multiple
+table extensions.  The MEF format has the advantage of minimize the
+proliferation of files, while the SPLIT format is required to make use
+of the fastest read/write capabilities of DVO.  DVO makes use of these
+raw data formats as a throughput risk mitigation strategy.  As
+discussed below, this strategy has proven very successful.
+
+There are also multiple formats in which the data may be stored.  The
+different formats define which specific database table columns are
+stored and with what numerical format and precision.
+Figure~\ref{fig:DVOformat} illustrates the conversion process which
+DVO performs when loading in the data.  When DVO loads data from a
+file-based table (FITS or RAW), it first loads from the disk file into
+a data structure representing the external format in use.  The
+external structure is then converted into the internal format. The
+internal structure is always specified to be the superset of all
+external data formats.  This capability allows DVO to maintain
+backwards compatibility with data tables written with early versions.
+As DVO is extended and new elements are added to the tables, it is
+only necessary to define the methods to convert the new internal table
+into the external table.  In addition, DVO makes use of autocoded
+table manipulation and I/O APIs which are generated for each data
+structure based on a descriptive table.  This makes it easy to add new
+data types and input/output methods without significant re-coding.
+
+\tbd{DVO mysql table storage is not yet implemented}
+
+\subsubsection{addstar : Insert Image \& Detection Set}
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/dvo.04.ps}}
+\caption{\label{catalog} \small a figure }
+\end{center}
+\end{figure}
+
+One of the most basic operations needed by DVO is to insert a
+collection of detections derived from a specific image, and add the
+definition of that image to the database.  This operation is critical
+in terms of the processing throughput.  After the detections have been
+assigned to the appropriate regions, they are matched against all
+objects in the {\tt Objects} table.  Matches are performed only on the
+basis of positional coincidence, using a matching radius which may
+depend on the image astrometry errors, or may be a fixed distance.
+Any matched detections are added to the {\tt Matched Detections}
+table.  Any unmatched detections brighter than the Faint Detection
+cut-off are specified as a new {\tt Object} and also added to the {\tt
+Matched Detections} table.  Any faint unmatched detections are added
+to the {\tt Orphaned Detections} table.  This division is important
+because it allows the automatic association of new detections with
+existing bright objects while limiting the I/O volume required to make
+the detections.  In general, there will be many fewer {\tt Objects}
+than {\tt Detections}, and there will be fewer bright orphans than
+faint orphans.
+
+A wide range of options are available to addstar.  These can be used
+to modify the object matching rules, to reduce the number of tables
+which are updated, to specify the output data format, and so forth.  A
+few options modify the behavoir in substantial ways, as discussed in
+the two sections below.
+
+\tbd{flesh out discussion of the options}
+
+\paragraph{Insert Reference Objects} 
+
+\code{addstar -ref (filename)}
+
+This mode of addstar reads a text file and adds the listed objects to
+the database as a reference photcode type.  A collection of reference
+objects are added to the database as a collection of detections.  The
+reference photometry should in general be given its own photometry
+code.  The reference data is different from the image detection set
+because the associated image information is not included.  Thus, no
+corresponding images are added to the database.
+
+\paragraph{Insert Catalog Objects} 
+
+\code{addstar -cat (name) -region ra ra dec dec}
+
+In this mode, any of several all-sky or large-scale reference catalogs
+are used for the input sources.  The catalog objects are added to the
+database as reference objects.  The valid catalogs consist of 2MASS,
+USNO, GSC.  Tycho and USNO-B will be added shortly.  Specific
+photcode names are defined for each of these catalogs, and must be
+appropriately requested and defined in the photcode table.  The
+optional region restriction limits the insert to a subset of the sky.
+The user does not always want to add 50GB of 2MASS detections to any
+DVO database... 
+
+\paragraph{Addstar Client/Server Interactions}
+
+DVO currently uses stand-alone programs which are run from the command
+line (like addstar, or the programs listed below), or it works with
+the interactive DVO shell, which allows the user to query portions of
+the database.  These programs all interact with the database tables
+directly, making use of file locking to prevent conflicts.  
+
+Unlike the other DVO programs (currently), it is possible to run
+addstar as a client/server system.  In this configuration, the program
+\code{addstard} is launched to run in the background as a server.  It
+monitors a socket waiting for clients to contact it.  The client
+program, \code{addstarc} appears to the user identical to the
+stand-alone addstar.  However, rather than directly insert data into
+the database, \code{addstarc} contacts the addstar server and sends it
+the detections and associated image data (along with the information
+about the user options).  The daemons accepts the incoming data and
+then loads this data into the database, just as the stand-alone
+addstar does.
+
+The purpose of the addstar client/server design is three-fold.  First,
+the client can be used by processes to send data to the DVO database
+and then immediately exit.  The addstar loading process is one of the
+more time-critical functions within the IPP.  However, unlike the
+other portions of the IPP, the addstar processes must operate in
+serial, at least when they are updating the same portion of the sky
+(or the image table).  If the IPP analysis routines all needed to run
+the stand-alone addstar program, they would eventually block waiting
+for each addstar to complete, preventing other processing from
+continuing.  The addstar client / server model allows the processing
+node to invoke the addstar client, sending the data to the addstar
+server.  The addstar server will then be the entity that manages the
+serialization of the incoming data stream.  The addstar server has two
+threads which run in parallel.  One thread monitors the socket and
+accepts new data sets from addstar clients, adding the data to an
+internal queue.  The other thread pulls data off of the queue and
+updates the database with the data.  
+
+A second advantage of the client/server interaction is that only the
+new detections need to be sent across the network.  To update the
+database, addstar must load the average objects for the region from
+the database tables.  In the stand-alone mode, the addstar program
+loads this data via NFS across the network from whatever device stores
+the addstar tables.  In the client/server model, the addstar server
+always runs locally on the machine which holds the database tables.
+Thus, for the server, all database access is local disk access.
+
+The final advantage of the client/server model is that it enables the
+parallel database model, which is not yet implemented as of Jan
+2006. In this model, there are multiple addstar servers.  Each one has
+a fraction of the sky in the local tables.  The identification of
+which table is managed by this host/addstar server is stored in the
+SkyRegion table.  The addstar server simply accepts incoming
+detections from the addstar clients.  Any detections which it receives
+which fall within the boundaries of tables that it manages are updated
+as normal.  The server then identifies the other addstar servers which
+are responsible for the other detections.  It then sends these
+detections to those servers using the same socket communication used
+by the addstar clients.  The addstar server must also be ready accept
+detections from other addstar servers.  This relationship is
+completely parallel, and any addstar client may send its data to any
+addstar server, letting the servers hash out who owns what.  The only
+difficulty with this model is in handling sources near the boundaries
+of the tables.  Note that this issues exists whether those tables are
+distributed across multiple machines or not.
+
+Addstar uses the following strategy to handle detections on the table
+boundaries.  Detections are first added to each table completely
+ignoring the neighboring tables.  A detection which is close to the
+boundary may either be associated with an average object contained
+within the table, or not.  If it is, the detection is associated with
+that average object.  If not, a new average object is created at the
+location of the detection.  So far, this process is identical to the
+behavoir in the middle of the table.  One a longer time-scale, a
+process is run which mediates the table boundaries. In this analysis,
+the two neighboring tables are simultaneously examined.  The border
+region, in a strip wider than the correlation radius, is examined in
+detail.  If two objects within the border region fall within 2x the
+correlation radius of each other, their individual detections are
+re-examined.  These detections are re-added to a temporary table which
+encompases the overlap.  the resulting objects will in general have
+detections from either side of the boundary.  The average objects are
+kept within the table as normal, but the detections are allowed to
+migrate between the tables to stay with their object.  \tbd{this
+boundary cleanup process is not implemented to date}.
+
+\subsubsection{Relphot : Relative Photometry Analysis}
+
+This operation uses the overlaps of images and multiple observations
+of the same objects to determine the relative photometry zero-points
+for a collection of images.  This is a task that is run much more
+infrequently than the object insertion tasks.
+
+The relphot analysis is currently performed with a single Sky region
+as the starting point.  All images (or all chips from all mosaic
+iamges) which overlap the sky region are identified in the image
+table.  This set of images are considered set A.  Next, all skyregions
+which are overlapped by all of these images are selected.  Finally,
+all additional images which overlapped the new regions only are
+selected.  These are considered as image set B.  The image selections
+are also restricted to images of a single, user-selected photcode.  
+
+All of the objects and detections which are contributed by the images
+in sample A are extracted from the average and measure tables.  Only a
+subset of the detections for which the S/N is greater than a
+user-selected limit are kept.  Other restrictions, such as time range
+or instrumental magnitude ranges may also be specified.  The
+collection of average objects, their detections, and the images from
+which they were derived now define a system of photometry equations.
+In this system, every image has a calibration offset magnitude
+($M_{cal}$), every object has an average magnitude in a relative
+system ($M_{rel}$), and every detection of that object has a magnitude
+defined by the equation $M = M_{rel} + M_{cal}$.  The goal is to solve
+for the values of $M_{ref}$ and $M_{cal}$.  
+
+There are two points to note about this operation.  First, the system
+of equations is generally much too large to solve directly; we must
+use an iterative technique to converge on a solution. Second, it is
+important in the analysis to use robust averaging and identify
+detections, stars, or images which are deviant in some way.  These
+should be marked and given set weight in the solution.  These cases
+may represent poorly measured objects (perhaps detections on or near a
+bad column), variable stars, and images obtained in poor weather
+conditions.
+
+Relphot can also be used to determine the mosaic grid used to generate
+photometrically corrected flats (-grid option).
+
+\tbd{fill out this discussion in the analysis section on the
+astrometric and photometric reference catalog}.
+
+\subsubsection{Uniphot : Zero Point Analysis}
+
+This operation uses the time history of relative photometry zero
+points for images and the spatial overlap information to determine a
+best set of image zero points which have a specific time scale for the
+atmospheric stability.  This analysis is used after relative
+photometry has been determined for data in DVO.  This analysis
+currently is defined to unify the zero points of a collection of
+disjoint regions; additional modifications will be needed to
+simultaneously determine consistent zero points and relative
+photometry corrections for a collection of images distributed over a
+large range in space and time, but still with significant
+overlap. 
+
+\tbd{fill out this discussion in the analysis section on the
+astrometric and photometric reference catalog}.
+
+\subsubsection{relastro : Global Astrometry Analysis}
+
+This operation uses the reference and image detections to improve the
+astrometric reference catalog.  It determines an improved optical
+distortion model for the camera and static astrometry model
+components, and then applies the improved astrometric solutions to the
+observations to yield high-quality astrometry for the average object
+positions.  The astrometry model includes: (1) field distortion
+introduced by the telescope optics, which is a smoothly-varying
+function of the field position relative to the center of the telescope
+boresite coordinates.  (2) focal plane geometry, which includes the
+chip positions and rotations in the focal relative to the boresite,
+along with chip-dependent plate-scale modifications needed to
+represent tilts or warps of the individual detectors relative to the
+ideal flat focal plane.  
+
+\tbd{fill out this discussion in the analysis section on the
+astrometric and photometric reference catalog}.
+
+\subsubsection{DVO shell}
+
+The DVO Shell is a user-tool for examining the visualizing data stored
+by DVO. The DVO Shell uses the Opihi shell language structure (see
+also PanTasks, Section~\ref{pantasks}), which provides a rich data
+analysis language.  The shell language provides the user with
+capabilities to define new commands, set and manipulate scalar,
+vector, and image structures, and plot 2D graphics, including
+projections of the sky.  In addition, the DVO shell is aware of the
+DVO data tables and provides access mechanisms to these tables.  The
+following is a brief overview of these table access features.
+
+DVO provides several ways to access the information stored in the
+database. Several simple commands allow the user to extract 1-D
+information directly from one of the primary database tables. The
+fundamental such commands are:
+\begin{itemize}
+\item imextract
+\item avextract
+\item mextract
+\end{itemize}
+
+These commands allow the user to extract data from one of the columns
+represented by the image table(s), the average object tables, or the
+measurement tables.  The extraction places the resulting data into a
+vector data elements, which may be used to make plots or perform
+analysis.  The user may constrain the query with spatial selection, by
+photcode, by time ranges, and so forth.  Some examples:
+\begin{verbatim}
+   avextract all ra : select ra for all objects in displayed region
+   avextract all g  : select g magnitudes
+   avextract all mag -photcode r : select r magnitudes 
+   avextract all Xm -photcode r  : select chisq values for r average mags
+\end{verbatim}
+
+Beyond these basic vector extractions, the user may perform more
+complex extract operations such as color-based selections.  For
+example, color-color diagrams can be easily made by extracting the
+colors from the average or measurement tables and plotting the
+resulting vectors.  The \code{ccd} commands extracts a specified pair
+of colors for all objects with that color pair from the specified data
+region.  Similarly, the command \code{cmd} extracts a color and a
+magnitude into a pair of vectors.  Both commands may specify any of
+the different types of magnitudes (relative, calibrated, etc)
+discussed above.
+
+An additional class of DVO Shell commands perform more complex
+graphical operation.  For example, the command \code{images}, plots
+the images which the specified region on the plotting too.  Other
+commands allow the user to extract the images or the database tables
+which overlap specified locations.
+
+\begin{figure}
+%\resizebox{4.5in}{!}{\includegraphics{pics/polar}}
+\caption{\label{polar} \small Map of
+the sky in polar project, and images added to database. }
+\end{figure}
+
+\begin{figure}
+%\resizebox{4.5in}{!}{\includegraphics{pics/fullsky}}
+\caption{\label{allsky} \small Map of the entire sky, and images added to database. } 
+\end{figure}
+
+Some examples of using the DVO shell to perform visualization are
+given in Figures~\ref{polor} and \ref{allsky}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{PanTasks : the IPP Scheduler}
+\label{sec:pantasks}
+
+PanTasks is the IPP tool which manages the sequencing of data analysis
+steps and, with the related tool `PControl', distributes the data
+processing across a cluster of computers. \tbd{describe the 'opihi'
+shell-scripting system}
+
+The purpose of PanTasks is to manage the automatic construction and
+execution of inter-related (often repetative) operations.  PanTasks
+uses a set of rules to define UNIX commands, and their corresponding
+command-line arguments, to be performed on some regular, repeated
+basis.  The utility of PanTasks is that it can easily define and
+manage an analysis system which is completely state-based, as opposed
+to an event-driven system.
+
+Consider, for example, a telescope which obtains a collection of
+images over the course of a night. Every minute or two, it takes an
+image and writes the image to some disk. An event-driven analysis
+system would involve having the telescope initiate a process at the
+end of the exposure. This process would perform an analysis, write
+some output, then send trigger another process. This type of operation
+works very well for a simple set up with reliable hardware. Such a
+system becomes more difficult to maintain when hardware failures occur
+or when multiple systems need to interact with each other. When
+failures occur, the triggering information (the events) is easily
+lost, thus some mechanisms are needed to detect these failures and
+either re-send the trigger or send an alternative failure-mode
+trigger. Or, if two systems need to interact, one or the other system
+must block for results from the first. Stopping and restarting such an
+analysis system is very delicate since the appropriate triggers must
+be set up some how, eg by noticing which images have not succeeded and
+restarting them at the appropriate stage. All of these types of
+methods of handling complexity and failures are essentially
+state-based rules. PanTasks allows the easy definition of a totally
+state-based analysis system.
+
+In a state-based system, some mechanism examines the state of the
+system and decides which actions to perform based on the current
+state. In the illustration above, the mechanism could examine the
+images available (either by examining the disk or by examining the
+state of a data table) and decide to perform an operation based on
+what images are available. This makes it very easy to handle
+complexity and errors. If an analysis fails, the state either is not
+successfully updated or the error state is recorded, both situations
+being easy to detect and easy to handle. Restarting the system simply
+involves starting the state-monitoring mechanism. Combining results
+from multiple input sources simply involves watching for the multiple
+inputs to be available. PanTasks provides a mechanism to define state
+monitors, and to define the actions which are performed when those
+states occur. PanTasks action consist of initiating UNIX commands, where
+the arguments of those commands may depend on the results of the state
+tests.  
+
+\subsubsection{Tasks vs Jobs}
+
+The two basic units of PanTasks operation are the 'task' and the
+'job'.  A 'job' is simply a command the user would execute on a
+command line; it consists of a command along with optional command
+line arguments.  A 'task' is a generic description of a type of job in
+which the details of any specific piece of data are omitted.  The task
+defines the UNIX command which corresponds to the job, and it provides
+rules for determining the identity of data for the job.  The task also
+defines tests which are used to decide if the job may be executed.
+Finally, it defines a polling frequency in which PanTasks should
+attempt to construct a new job for the task.
+
+For example, we may want to regularly copy files from one location to
+another.  Perhaps the name of the next available file is available in
+a database table, and can be retrieved with the command 'nextFile'.
+Perhaps we wish to check for new files every 60 seconds.  Thus, the
+task is to copy files, while a specific job of this task might be of
+the form \code{cp newfile newpath}.  With PanTasks, we would define a
+copy task somewhat like the following:
+
+\begin{verbatim}
+  task copyfile
+    periods -exec 60.0
+
+    task.exec
+      $file = `nextFile`
+      if ($file == "none")
+        break
+      end
+      command cp $file $newpath
+    end
+
+    task.exit 0
+      queuepush copied $file
+    end
+
+    task.exit 1
+      queuepush failure $file
+    end
+  end
+\end{verbatim}
+
+In this simple example, the task is attempted every 60 seconds.  If
+there is no new file (output of 'nextFile' is 'none'), then no job
+results from this task.  If there is a new file, the copy command is
+performed.  This example is deceptively simple because one could
+easily imagine writing a stand-along program which performs the same
+thing.  The important advantages of using PanTasks for this type of
+operation are: 
+\begin{itemize}
+\item the output from one job can be used to spawn a number of other tasks.
+\item the success or failure state of each job can be used to spawn
+  other tasks.
+\item the details of the job and data test are kept separated from the
+  rules which connect tasks together.  
+\item the relationships between tasks are kept together in a single
+  location.
+\end{itemize}
+
+In addition to these organizational advantages, PanTasks also provides
+a direct connection to the tool for monitoring parallel jobs,
+pcontrol.  Thus the jobs spawned by PanTasks can be defined to run in
+the background locally or on any of the computers in the parallel
+processing cluster.
+
+\subsubsection{Parallel vs Local Job Processing}
+
+Jobs which are generated by PanTasks tasks may either be run locally
+(forked in the background on the same machine as PanTasks) or run on
+the IPP parallel process controller, pcontrol. The default is for the
+job to be run locally. If a job should be run on the parallel
+controller, this can be specified by including the command host
+(hostname) in the definition of a task. If the value of (hostname) is
+'anyhost', then pcontrol may select any of its host computers to run
+the job according to its own rules. If the value of (hostname) is one
+of the computers managed by pcontrol, then that machine will be
+selected for the job, if it is available. This amounts to a preference
+to use that machine, but pcontrol is allowed to substitute a different
+machine if it chooses. If the host command is given the option
+-required, then pcontrol is forced to use the named host, even if the
+machine is down, unknown, or otherwise unavailable. If the machine is
+not available, pcontrol will simply hold onto the job until the
+machine is available or the job is deleted. Note that PanTasks may
+delete jobs from pcontrol if they remain pending for too long.
+
+It is possible to interact directly with the parallel processor to
+examine the current status, halt the parallel processor, etc. Commands
+to the parallel processor are defined under the controller
+command. The following controller commands are available:
+
+\begin{itemize}
+
+\item controller host (command) (hostname): Manage the parallel
+  controller collection of hosts. This command can be used to add a
+  new host, the delete one of the existing hosts, to turn a host on or
+  off, and to check the status of a host
+
+  \begin{itemize}
+  \item controller host add (hostname): add a new host.
+
+  \item controller host delete (hostname): delete a host.
+
+  \item controller host on (hostname): tell pcontrol that the host is on.
+
+  \item controller host off (hostname): tell pcontrol that the host is off.
+
+  \item controller host retry (hostname): tell pcontrol to retry the host connection.
+
+  \item controller host check (hostname): check the current status of a host. 
+  \end{itemize}
+
+  \item controller exit: stop controller execution.
+
+  \item controller status: report controller current status.
+
+  \item controller check: check job or host status.
+
+  \item controller output: print accumulated messages from the controller. 
+\end{itemize}
+
+It is also possible to specify a host for a task which has not been
+identified to the controller. If such a host is required, the
+controller will simply keep the associated jobs in the pending state
+until such a machine exists. See the pcontrol section below for
+further discussion of the controller manipuation of jobs and hosts.
+
+\subsubsection{Task Restrictions}
+
+Tasks may have restrictions on when they create jobs and how
+frequently they create jobs. The task command trange is used to
+specify a valid or invalid time range for a task. A valid time range
+limits the task evaluation to that time period. An invalid time range
+excludes task evaluation from the time period. Any number of time
+range restrictions may be defined, and the union of all restrictions
+will define if a job may be created. By default, the time range is an
+inclusive time range: the task is evaluated only if the current time
+falls within the specified time range. Alternatively, if the -exclude
+flag is given, the time range is exclusive, in which case the task is
+not evaluated if the current time falls within this range.
+
+The time range may be given as a range of absolute dates as follows:
+
+\begin{verbatim}
+ trange YYYY/MM/DD,HH:MM:SS YYYY/MM/DD,HH:MM:SS 
+\end{verbatim}
+
+where the two dates specify the start and end of the time range. In
+either of these date representations, the least-significant elements
+of the date and time may be dropped, defaulting to 00 (in the case of
+hours, minutes, and seconds) or 01 (in the case of day and
+months). Rather than specifying an end date, it is also valid to
+specify a time interval from the starting date. The time interval is
+specified as a number followed by a unit indicated by a single letter:
+d (days), h (hours), m (minutes), s (seconds).
+
+The time range may also be specified as a repeated period of time,
+either as a time of day or a day and time of week. In the first case,
+the time range is specified as follows:
+
+ 
+\begin{verbatim}
+ trange HH:MM:SS HH:MM:SS
+ \end{verbatim}
+
+
+where again the least-significant elements may be dropped and default
+to 00. This type of restriction defines a time range which is valid
+every day. The alternative is to specify a time range within the week,
+in the following form:
+
+\begin{verbatim}
+ trange DAY@HH:MM:SS DAY@HH:MM:SS
+\end{verbatim}
+
+where the value of DAY may take on any of the three letter day-of-week
+names (Sun, Mon, Tue, etc). This restriction specifies a start and end
+time within a week which is evaluated for each week.
+
+Below are several examples of valid time range restrictions
+
+\begin{verbatim}
+ trange 2005/01/01 2005/12/31   (only run during 2005!)
+ trange 18:00 00:00             (only run from 6pm until midnight)
+ trange 00:00 06:00             (only run from midnight until 6am)
+ trange Mon@08:00 Fri@17:00     (only run between Mon morning and Fri afternoon)
+ trange -exclude 12:00 13:00    (skip 1 hour from noon)
+\end{verbatim}
+
+Note that the current definition of trange does not include time zone
+information. This means that all times are relative to UT. This should
+be addressed by adding a timezone environment variable to PanTasks and
+by allowing the trange to define a timezone offset.
+
+It is also possible to restrict the total number of jobs which are
+spawned for a given task. This is done with the nmax command, which is
+given as part of the task definition. Once a task has constructed nmax
+jobs, it stops task evaluation. It is possible to redefine the value
+of nmax at any time by redefining the task. Any time the task is
+redefined, the new values for any task concept will override the
+existing values for the task concept.
+
+\subsubsection{Inter-Task and Inter-Job Communications}
+
+There are several ways in which the results of jobs may be used to
+influence other jobs. These include:
+
+\begin{itemize}
+\item external communications
+\item job exit status
+\item job stdout/stderr parsing 
+\end{itemize}
+
+It is always possible for the interprocess communication to be
+performed externally: all jobs may simply write results to an external
+data source which is queried as part of the task evaluation. PanTasks
+may interact with UNIX programs using Opihi system interaction
+functions. These interaction methods include: the backticks for
+setting Opihi variables:
+
+\begin{verbatim}
+ $variable = `UNIX Command`
+\end{verbatim}
+
+The exec command (which executes a UNIX command) and the backticks
+both receive the UNIX command exit status, setting the variable
+\code{$STATUS}. It is also possible to set a variable list to the
+output of a UNIX command:
+
+\begin{verbatim}
+ list var -x "UNIX Command"
+\end{verbatim}
+
+In this last case, the values \code{$var:0 - $var:N-1} are set to the
+value of the stdout lines from the UNIX command, and the value
+\code{$var:n} is set to the number of output lines.
+% $
+
+Fine-grained control over the job exit status is available with the
+task.exit macro command. This allows a task to define an exit macro
+which is performed for different exit status conditions. The argument
+to the task.exit command is the exit status value which triggers the
+macro. This may consist of any valid numeric exit status value
+(0-255). It may also have the value crash, in which case the macro is
+executed if the program exited as a result of a signal (ie,
+segmentation fault, etc). Finally, if may have the value default, in
+which case, the macro is run if no other macro describes the exit
+status.
+
+Jobs may transmit their results back to PanTasks for further
+evaluation through the standard output and standard error
+streams. Whenever a job exits, the complete stdout and stderr streams
+from the job are pushed onto the PanTasks queues \code{stdout} and
+\code{stderr}. The job exit macros may then parse these queues, moving
+the results into other PanTasks / Opihi data containers (queues,
+variables, vectors, whatever is appropriate). Note that currently, the
+output data is simply pushed onto these output queues. It is currently
+the responsibility of the PanTasks programmer to use or dispose of the
+data in these queues. This may change in the future: the queues may be
+flushed for each job completion.
+
+\subsubsection{PanTasks Monitoring Loop}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.01.ps}
+\caption{\label{MonitorLoop} PanTasks Monitor Loop}
+\end{center}
+\end{figure}
+
+Figure~\ref{MonitorLoop} illustrates the operation of PanTasks.
+Currently, PanTasks is run as a stand-alone foreground process, not a
+server.  In this current operating mode, PanTasks accepts commands
+from the user (left side) via a GNU readline interface.  Readline
+provides a mechanism to schedule an background process which is
+executed on a regular basis, or in the interval after each keystroke.
+This background processing is represented as the loop on the right.
+In this loop, PanTasks checks on the status of pcontrol and on any
+locally spawned background jobs.  It also checks the list of tasks for
+tasks which are ready to be executed.  The tasks and the background
+jobs are kept in rotating queues.  Every time the loop is processed,
+PanTasks runs through a number of the background jobs and tasks,
+attempting to evaluate as many as possible, but stopping after a
+limited number of milliseconds.  This test stage cycles through the
+tasks and/or jobs in their queue, but stops if it examines all of the
+entries in the queue.  If this testing process runs out of time before
+the entire queue is searched, it leaves the sequence intact for the
+next pass.  The timeout is set to keep the keyboard small enough to
+keep the human interaction from being troublesome.
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.02.ps}
+\caption{\label{TaskLoop} PanTasks Task Check Loop}
+\end{center}
+\end{figure}
+
+Figure~\ref{TaskLoop} shows the interactions performed for each check
+of the list of tasks.  The task timers are examined to see if the
+specific task is ready to be executed.  If so, then the task exec
+macro is executed.  If this macro exit status is false, the loop goes
+to the next task.  If the task exec macro is true, the job command is
+constructed and the job is submitted to the correct location, either
+the controller (pcontrol) or to the queue of local, background jobs.
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.03.ps}
+\caption{\label{JobLoop} PanTasks Job Check Loop}
+\end{center}
+\end{figure}
+
+Figure~\ref{JobLoop} shows the interactions performed for each check
+of a single background job.  The job timer is checked to see if the
+job has timed out.  Next, the job run status is examined to see if the
+job has finished or not.  If the job has finished, the appropriate
+exit macro is executed and the job results are returned to the rest of
+the PanTasks data structures (ie, stderr and stdout queues are filled
+out).  Users should not define exit macros which perform long running
+jobs, or PanTasks will become quite sluggish to keyboard strokes.
+
+PanTasks also checks the current status of pcontrol on each loop.
+In this test, PanTasks requests from pcontrol a list of the jobs which
+have finished.  It then attempts to harvest the finished jobs one by
+one and place the results in the approrpiate locations.  This loop is
+also limited to a fixed amount of time; any pcontrol jobs which remain
+unharvested are left for the next pass by PanTasks.
+
+\subsubsection{Running the scheduler}
+
+Once a set of tasks has been defined, the scheduler can be
+started. The scheduler will run in the background, at regular
+intervals examining the collection of tasks and jobs. In these
+periods, the scheduler attempts to construct new jobs and checks on
+the status of jobs which may have finished, either locally or on the
+controller. To start the scheduler, give the command run. To stop the
+scheduler, given the command stop. The current status of the
+scheduler, controller, and any jobs which have been spawned are listed
+with the status command.
+
+It is also possible to kill or delete individual jobs by hand with the
+commands kill (jobID) or delete (jobID).  Other features
+
+\subsection{pcontrol : the PanTasks parallel controller}
+
+The IPP uses a group of computers to store and process images and to
+manipulate collections of detections. These computers perform any of a
+large number of analysis stages or other processing tasks without
+significant interprocess communication. It is necessary to have a
+mechanism which initiates computing tasks on the different computers,
+which monitors the tasks as they are executed, which handles the
+output and the errors from these tasks, and which reacts to the
+failure of any of the computing nodes. The system responsible for the
+tasks in the IPP is pcontrol.
+
+\subsubsection{Host States}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.04.ps}
+\caption{\label{HostStates} PanTasks Host States}
+\end{center}
+\end{figure}
+
+pcontrol maintains a table of available processing computers (hosts)
+and tracks their status. Hosts managed by pcontrol are allowed to be
+in one of several states: \code{off}, \code{down}, \code{idle},
+\code{busy}, and \code{done} (see Figure~\ref{HostStates}). There are
+also two virtual states: \code{new} and \code{delete}.  These states
+have the following meanings:
+
+If the host is \code{off}, it is known to pcontrol, but pcontrol does
+not have an active connection to the machine. Hosts which are
+\code{off} are not available for jobs, and pcontrol does not attempt to
+initiate a connection to them.  A pcontrol user may force a host to
+transition to the \code{off} state with the command host
+\code{off~(hostname)}. (Note that this command will set only one of
+the connections to the named host to \code{off}. If multiple
+connections to a machine have been defined, multiple \code{off}
+commands must be sent).
+
+When pcontrol is told to consider a machine on, the machine is moved
+from the \code{off} state to the \code{down} state. Pcontrol attempts
+to initiate a connection to the host. Connections are made by running
+a remote client on the host, using the specified connection
+method. The connection method may be ssh, rsh, or an equivalent remote
+shell connection. The choice is specified by the \code{COMMAND} Opihi
+variable. The remote connection starts a dedicated remote client which
+must accept the pcontrol client commands and respond
+appropriately. The provided remote client is called {\em pclient},
+though in principal other equivalent programs could be used by setting
+the Opihi variable \code{SHELL}.  This feature more generally allows a
+user to specify a path to the remote client, if it is not in the
+user's path.
+
+If the remote connection is successful, the connected host is moved by
+pcontrol from the \code{down} state to the \code{idle} state. If the
+connection is unsuccessful, pcontrol will try again after a certain
+period of time. If the connection continues to be unsuccessful, the
+retry period is doubled for each successiver connection attempt. If
+the user wants to force pcontrol to retry the connection to a machine
+(if, for example, the timeout is now very long, but the user knows the
+machine's ethernet cable has been re-inserted...), this can be
+achieved with the command host \code{retry (hostname)}. A host which
+is \code{down} is in the limbo state between \code{off} and
+\code{idle}.
+
+Once pcontrol has made a successful connection to the host, the host
+is in the \code{idle} state. At this point, it is ready to accept jobs
+from pcontrol for execution. Pcontrol periodically queries the hosts
+to check that they are still alive. If a host is discovered to be
+unresponsive, and particularly if the remote pipe connection has
+closed, then the machine is moved back to the \code{down} state.
+
+Hosts which are \code{idle} may accept a job from pcontrol. A job
+simply consists of a bare UNIX command, without redirection of
+standard input or standard output. The host will initiate the job, and
+pcontrol will place the host into the \code{busy} state. The remote
+client, pclient, runs the job in the background and will continue to
+accept input from pcontrol. pcontrol will continue to check the status
+of the host, and now also the status of the specific job. As before,
+if the connection breaks, pcontrol will migrate the host to the
+\code{down} state. Any job already initiated on a host which goes down
+will be returned to the pool of unexecuted jobs for later processing,
+so the job will not be lost.
+
+When the job exits, pclient tells pcontrol that the job is completed,
+and specifies the exit status. At this point, pcontrol will move the
+host from \code{busy} to \code{done} state. It will stay in this state
+until pcontrol can determine the ending conditions and reset the
+remote client. pcontrol requests the standard error and standard
+output from the job from pclient. pcontrol stores this data with its
+information about the completed job, and send a reset command to the
+remote client. Once these cleanup tasks are successfully completed,
+pcontrol will move the host to the \code{idle} state, ready for
+further jobs.
+
+Each physical computer may have multiple processors. pcontrol treats
+each processor independently. It is up to the system configuration if
+each computer needs to reserve one of its CPUs to manage background
+tasks or if pcontrol should attempt to send one task per CPU and let
+the operating system handle the I/O load. some of this behavior will
+probably be eventually more intelligent. For example, the commands
+which turn a host on or off should be able to do the same operation to
+all host connections for the same machine name.
+
+A machine may be completely removed from pcontrol's host tables with
+the command host delete (hostname).
+
+\subsubsection{Jobs}
+
+The pcontrol accepts new jobs with the command \code{job ...}, in
+which the ellipsis represents the command and arguments of a valid
+UNIX command. The commands are run under \code{sh}, and are executed
+in the user's home directory. Users should be wary of the conditions
+under which the remote jobs are run. If the nodes in question all
+cross-mount the same home directories, multiple jobs which interact
+with the same named file may produce unexpected results. The
+controller cannot enforce good behavior on the part of the remote
+jobs; it is the responsibility of the user to ensure that conflicts do
+not arise by, eg, always using unique output file names.
+
+Other issues may arise from the fact that pcontrol may be choosing any
+of the hosts to run the job. Typical failures arise if the user does
+not realize that specific jobs do not behave the same on all machines,
+or if a necessary resource (eg, some input data file) is only
+available or accessible from some of the hosts. It is also the
+responsibility of the task to wait for network lags (ie, NFS delays).
+
+pcontrol gives each task a unique internal identifier (Job ID)
+equivalent to the process ID used in UNIX. When a job is submitted to
+pcontrol, the command echoes back the Job ID. This ID may be used by
+other pcontrol commands to obtain information about or interact with
+the job.  For example, PanTasks uses the Job ID to examine specific
+jobs.
+
+A job may specify a specific host for the task execution. The host
+specified for a job may be required, or desired. In the first case,
+pcontrol, will only run the job on the specified host, waiting until
+it is available before attempting the job. In the second case,
+pcontrol will attempt to send the job to the specified host, but if
+the host is unavailable (how long? what conditions?), pcontrol will
+allow the job to be sent to an alternative host. pcontrol attempts to
+honor the requests for required and desired hosts, giving priority
+first to required-host jobs, then to the desired-host jobs, and
+finally to all other jobs. To specify a host for a job, the following
+commands are used:
+
+\begin{verbatim}
+ job -host (command and arguments...)
+ job +host (command and arguments...)
+\end{verbatim}
+
+
+The first case specifies a desired host, while the second specifies a
+required host. It is also possible to specify the special host name
+anyhost, which is equivalent to not specifying a host at all.
+
+Job priority / urgency levels are not implemented at this time.
+
+I/O vs CPU tasks are not currently distinguished by pcontrol
+
+pcontrol stores the stdout and stderr for each completed job. To
+retrieve these data from these streams, the user issues the commands
+stdout (JobID) and stderr (JobID). The result is a single line
+specifying the number of bytes to expect, followed by a dump of the
+buffers, followed by the prompt. It is the user's responsibility to
+relieve pcontrol of this data load by deleting jobs once they are no
+longer needed. Job deletion is performed with the command delete
+(JobID).
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.05.ps}
+\caption{\label{JobStates} pcontrol job states.  Transitions labeled Ux
+  are issued by the pcontrol user (including PanTasks).  Transitions
+  labeled Px are initiated by pcontrol.  Transitions labeled Tx are
+  the result of the job completion}
+\end{center}
+\end{figure}
+
+Jobs are moved between the following states by pcontrol (see
+Figure~\ref{JobStates}):
+\begin{itemize}
+\item pending: the job has not yet been executed.
+\item busy: the job is currently being executed.
+\item done: the job has completed, but the stdout/stderr has not been processed by pcontrol.
+\item exit: the job has completed with a valid exit status
+\item crash: the job has completed with a crash status (exit on signal). 
+\end{itemize}
+
+\subsubsection{Miscellaneous Commands}
+
+It is possible to check the status of a single host or job with the
+user command \code{check}.
+
+pcontrol continuously examines the stack of jobs, adjusting their
+state as needed and extracting their output when it is ready. These
+checks are performed in the background, with pcontrol ready to accept
+further commands from the user in the foreground. These checks are
+performed after every keystroke, and also after an inactivity
+timeout. The interrupt interval defaults to 1 second, but may be
+adjusted with the pulse command, which takes as an argument, the
+number of microseconds for the timeout.
+
+The pcontrol system status may be examined with the command
+status. This provides a dump of the job stacks and the host stacks.
+
+It is possible to list the jobs currently in a specific stack,
+corresponding to the list of jobs with a given state. This is done
+with the command jobstack (stackname). The valid stack names are
+pending, busy, exit, crash, and done. The result is a list of all jobs
+on the specified stack. This is useful to determine quickly which jobs
+have exited or crashed.
+
+A specific job may be killed with the command \code{kill
+(JobID)}. This command is only valid for a job in the busy state. Any
+job in the pending, exit, or crash state may be deleted with the
+\code{delete (JobID)} command. This is necessary to free the memory
+associated with the job and its output streams.  PanTasks
+automatically performs these job harvesting functions.
+
+The command \code{verbose (mode)} turns the verbosity of the pcontrol
+operations on or off.
+
+The pcontrol and Nebulous have related needs for information from the
+combined storage-and-processing nodes regarding which nodes are
+available. Currently, the two systems independently examine the
+hardware to judge the availability.  It is not yet clear if this
+information is best stored in a single location (either pcontrol or
+Nebulous), which provides the information to other systems on demand.
+The current implementation allows the IPP system as a whole to
+distinguih nodes which are available for processing from those that
+are available to serve data as part of Nebulous.
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.06.ps}
+\caption{\label{PControlLoop} PControl Job Monitor Loop}
+\end{center}
+\end{figure}
+
+Figure~\ref{PControlLoop} illustrated the pcontrol job monitor loop.
+This is very similar to the loop in PanTasks.  A background process
+launched by readline during idle periods cycles through the list of
+hosts (children) and migrates jobs to and from them as needed.
+
+\subsubsection{pclient}
+
+pclient is the remote process monitor for pcontrol, the parallel
+process controller.
+
+The program pclient is used to support the remote jobs which are run
+on the remote hosts by pcontrol. The concept of pclient is to act as a
+buffer between the job running on the remote host and pcontrol. The
+pcontrol design uses (by default) ssh connections initiated by
+pcontrol to the remote hosts. These connections execute the remote
+program of pclient. The use of a remote login process lets the UNIX
+system take care of the user authentication issues. In this case, the
+recommended practice is to set up ssh to allow the connection to the
+remote host without additional authentication using the appropriate
+authorized keys.  The security is maintained by the security of ssh
+logins to the computers used by PanTask and pcontrol.  
+
+It is convenient to keep a continuous connection to the remote
+hosts. This avoids incurring the overhead of authentication for each
+command which is executed, while keeping a high-quality user
+authentication process in place.
+
+pclient acts as a buffer between pcontrol and the remote background
+process, allowing the continuous connection to remain viable without
+samping pcontrol with output from the jobs.  
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.07.ps}
+\caption{\label{Pclient} Pclient monitor loop.}
+\end{center}
+\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{System Design : Science Analysis Tasks and Stages}
+\label{sec:AnalysisStages}
+
+This section describes the design of the science analysis stages which
+perform the fundamental image analysis steps of the IPP.  The IPP
+science image processing stages perform analyses on the night-sky
+science images to extract the science data from these images.  These
+consist of: 
+\begin{itemize}
+\item Phase 1, the image processing preparation stage,
+\item Phase 2, the image reduction stage
+\item Phase 3, the exposure analysis stage
+\item Phase 4, the image combination stage.  
+\end{itemize}
+These analysis tasks must process the images in a timely manner so
+that the incoming data stream will not overload the IPP Image Server.
+The decision to execute a specific pipeline for a specific dataset is
+made by the Scheduler, which sends the information to the Controller.
+The Controller executes the pipeline for the data on an appropriate
+machine and monitors the success or failure of the processing stage.
+
+The analysis stages are written using programs executed as UNIX
+commands.  These commands may be executed by the IPP Controller, or
+may be executed individually by hand.  This option makes testing of
+the complete analysis system much easier because the individual
+analysis stages may be tested independently of each other and the rest
+of the IPP infrastructure.  The analysis stages discussed in this
+section use two somewhat different types of programs.  One set of
+programs performs the heavy lifting of the data analysis: they examine
+the pixels in images and perform some statistical analysis of the
+pixel values or manipulate the data products in one way or another.
+Another set of programs are used to tie the analysis stages together.
+This latter set of programs examine the state of the Metadata database
+and select images for processing.  They are used by PanTasks, the IPP
+Scheduler to make decisions about which of the analysis programs to
+run on which data.  In this section, we discuss the details of the
+analysis stages in terms of the science analysis.  Below, in
+Section~\ref{AnalysisPrograms}, we discuss the major analysis programs
+and top-level analysis routines used by those program.  An important
+distinction to be noted here is that the same analysis programs with
+somewhat different options and configuration information can be
+employed by different analysis stages.  We exclude detailed discussion
+of the other connecting tools used to define the analysis stages.
+These tools and the detailed construction of the pipelines which make
+up the analysis stages are touched upon in Section~\ref{sec:PanTasks},
+which discusses the IPP Scheduler program, PanTasks.  They are
+discussed in more detail in Section~\ref{ippTools}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Phase 1: image processing preparation}
+
+The Phase 1 analysis stage is performed on each science exposure (each
+complete FPA image) to calculate basic astrometric data needed by the
+later stages.  Phase 1 uses the static (pre-determined) telescope
+distortion model and a table of nominal OTA positions and rotations,
+combined with the guide star pixel and celestial coordinates, to
+determine the correct telescope bore-sight, field rotation and
+magnification.  The guide star coordinates are loaded from the
+Metadata database.  These calculations are performed by comparing the
+observed guide star detector coordinates with the known astrometric
+positions of these same stars as reported by an external astrometric
+reference.  The accuracy of the resulting astrometric solution is
+expected to be $\sim 1$ arcsec across the field, sufficient in later
+stages to match the vast majority of astrometric reference stars with
+their detections with minimal effort.
+
+In some circumstances, science images may have no guide stars.  This
+may occur in the Pan-STARRS system if the detectors are not run in OTA
+mode, for example for short snapshot images.  This may also be the
+case if the IPP is being run on non-Pan-STARRS data.  In such a
+circumstance, the Phase 1 stage uses the provided boresight
+coordinates, exposure time, and camera zero-point to predict the pixel
+coordinates of known bright stars expected to be found on the
+detectors.  It then extracts a large box ($\sim$ 30 $\times$
+30\arcsec) around these locations and performs extremely basic object
+detection to determine the detector coordinates of those bright stars
+which are not saturated but which are significantly above the
+background level.  By targeting known locations in the image files,
+only a small amount of data will have to be read.
+
+If the image has invalid coordinates or no detectable bright stars,
+Phase 1 fails and reports a descriptive error.
+
+Given the above astrometric solution, the Phase 1 analysis stage
+constructs a table of the overlaps between the science image to be
+processed and the static sky images that must be constructed.  This
+table will be used to guide the processing of the static sky in Phase
+4.  The overlaps should be generously calculated so that small errors
+in astrometry at Phase 1 will not cause any valid static sky / science
+image pairs to be missed because of the astrometric error at this
+phase.  It is acceptable for a small number of invalid overlaps to be
+identified as these will be excluded in Phase 4.  Static Sky cells
+which do not have sufficient science image overlap ($< 5\%$) need not
+be processed because the few new measured pixels do not add
+significantly to the Static Sky.
+
+\subsubsection{Examples}
+
+Examples of Phase 1 as called from the command line, with different
+types of images:
+
+\begin{verbatim}
+Phase1 -file filename.fits [FPA is single fits file]
+Phase1 -list filename.list [FPA is collection of files]
+Phase1 -imdb ID            [FPA is single file in image server]
+Phase1 -FPA  ID            [FPA identifier from metadata db]
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Phase 2 : image reduction}
+
+\subsubsection{Overview}
+
+Phase 2 processing within the Pan-STARRS image processing pipeline is
+the detrend stage, where the images from the detector are processed to
+remove instrumental signatures.  This analysis is performed on
+individual chips, which can be identified as the data entity which has
+a single, continuous astrometric solution.
+
+Phase 2 consists of the following operations, some of which as noted
+may be skipped by the recipe:
+\begin{itemize}
+\item Load science image
+\item Identify appropriate detrend images
+\item Load detrend images
+\item Form OT kernel
+\item Convolve detrend images with the OT kernel
+\item Bias/dark/overscan subtraction
+\item Mask bad pixels
+\item Trim overscan
+\item Non-linearity correction
+\item Flat-field
+\item Mask diffraction spikes and optical ghosts
+\item Subtract sky
+\item Find and photometer objects in the image
+\item Identify CRs by morphology
+\item Determine PSF model
+\item Improve astrometry
+\item Extract Bright object postage stamps
+\end{itemize}
+The steps are explained in detail below.
+
+\subsubsection{Load Images}
+
+The Phase 2 analysis must load the science image to be analyzed into
+memory, as well as the corresponding metadata (either from the image
+header and/or from the IPP Metadata Database).  It must use the
+metadata for the image, along with information from the processing
+recipe, to determine the appropriate detrend images to be used for
+this analysis.  The Metadata Database stores the information necessary
+to associate a specific science image with one of the registered
+master detrend images for each type.  These images are also loaded by
+the Phase 2 analysis (note that the design of Phase 2 may perform the
+actual loading of pixels in blocks or groups to minimize the memory
+impact).
+
+\subsubsection{Form OT Kernel \& Convolve with Detrend Images}
+
+Science images which have been obtained with Orthogonal-Transfer
+Guiding have had their pixel response smoothed by the image correction
+motion.  For these images, some of the detrend images need to be
+convolved by the same OT kernel, so that they accurately represent the
+effective pixel response.  The detrend images which must be convolved
+include: the flat-field image, the high-spatial-frequency fringe
+images, and dark images, if they are used. The appropriate kernel for
+each cell of an OTA must be determined from the guide star history,
+extracted from the IPP Metadata Database or from the image header.  If
+the OT kernel is not available, but the image metadata notes that it
+should be, the convolution is skipped, with a warning.
+
+The convolution method depends on the size and structure of the OT
+kernel.  If the kernel is small ($< 5x5$ pixels), direct convolution
+may be employed.  If the kernel is large, but may be decomposed using
+Gaussians, then it may be convolved using a decomposition method.
+
+The module convolves each of the dark frame, flat-field, and the
+fringe frame(s) by the OT convolution kernel.  Specific flags in the
+static bad pixel mask are also grown by the outline of the OT
+convolution kernel (see Section~\ref{sec:masks}).
+
+\subsubsection{Bias Correction / Overscan Subtraction}
+
+The image bias must be subtracted. Since different detectors behave in
+different ways, several options for modeling the bias are available.
+The bias is measured from the image overscan region.  The bias
+subtraction method must be capable of subtracting a single constant
+from the complete image, or to subtract a 1-D bias which varies as a
+function along the overscan.  The function used to represent the
+overscan region may be a spline or a Chebychev polynomial derived from
+the data values along the overscan.  The values used to determine both
+the single constant or the inputs to the spline and polynomial fits
+are derived from groups of pixels on the basis of one of several
+statistics, including the sample and robust mean, median, and modes.
+In the case of a single constant, all of the overscan pixel values are
+used in the calculation of this statistic.  In the case of the 1-D
+functional representation, the input values to the fit must represent
+the coordinate along the overscan, with the statistic derived from the
+pixels in the perpendicular direction at each location.
+Sigma-clipping on the input data values must be an option.
+
+\subparagraph{Flag bad and saturated pixels}
+\label{sec:masks}
+
+A static bad pixel mask is used to identify pixels which are known to
+be bad in the camera.  This mask is then processed with the science
+image. Bad pixels which are charge traps are grown by the extent of
+the OT convolution kernel.  Bad pixels above a charge trap (i.e.\ bad
+columns) must not be grown, since they were not affected by pixel
+shifting, but only became bad at read-out.
+
+Pixels which are saturated in the A/D converter, or with a signal
+level at which the response is excessively non-linear, must also be
+masked, and this area must be grown by an additional pixel to mask
+excess charge spillover.
+
+The bad pixel mask is carried with the science images.  Different bits
+are set to identify different reasons for masking the pixel.  Flags
+are required for at least each of the following pixel attributes:
+
+\begin{itemize}
+\item The pixel is a charge trap;
+\item The pixel is a bad column;
+\item The pixel is saturated in the A/D converter;
+\item The pixel is non-positive in the flat-field;
+\item The pixel is part of a row that has excess noise; and
+\item The pixel is determined to be a cosmic ray, based on its
+morphology.
+\end{itemize}
+
+\subsubsection{Trim}
+
+The image is trimmed to remove the non-imaging pixels, such as the
+overscan and any pre-scan pixels, along with those pixels near the
+edges that have been compromised due to OT operation.  The definition
+of the imaging area of the detector is determined from the camera
+configuration data or from the metadata associated with the image,
+with the choice a user-configurable option.
+
+The input science and mask frames are additionally trimmed by the
+extent of the OT convolution kernel in each direction ($+x$, $-x$,
+$+y$, $-y$).  Within the PSLib image handling functions, the trim
+function is a virtual operation which simply marks the boundaries of
+the trimmed image but does not remove the corresponding memory.  This
+allows the later corrections to work with untrimmed correction images
+and still apply the correct pixels.  At the end of Phase 2, the only
+the trimmed portions of the output images are written out to disk.
+
+\subsubsection{Non-Linearity Correction}
+
+If required, the science image (after bias correction) must be
+corrected for the effects of non-linearity through a provided
+polynomial fit to the pixel data values or a numeric lookup table as a
+function of pixel flux.  The choice to apply the correction must be
+set by the analysis recipe.
+
+\subsubsection{Flat field Correction}
+
+The science image (after bias correction and non-linearity correction)
+must be corrected for sensitivity variations as a function of
+position, dividing by a flat-field image.  The mask is also modified
+for zero-valued pixels in the flat-field image.
+
+\subsubsection{Sky \& Fringe subtraction}
+
+After the science image has been flat-fielded, the flux contribution
+of the sky (from both continuum emission and the line emission that
+causes fringing) must be subtracted from the image.  The subtraction
+needs to remove background (technically, foreground) variations which
+are not celestial but generated in the atmosphere or by more localized
+scattering.  This background should include the contribution from the
+zodiacal light.  This background subtraction does not address the
+artifacts generated by bright stars: bleeding columns, ghosts, or
+other localized reflection effects.  This process also produces a
+super-binned image of the background map which may be used as a
+debugging diagnostic.
+
+This analysis step temporarily masks objects on the image using the
+astrometric solution from the boresight and fits for the sky
+background, consisting of a polynomial to model the continuum, and the
+fringe frame(s) to model the fringes from sky emission lines.  If the
+concentration of objects in the image is too high to reliably fit the
+sky background, the background solution from an exposure close in time
+and airmass to the current object image is used.  The output is the
+sky-subtracted object image.
+
+\subsubsection{Detect and Measure objects}
+
+After the image have been processed by the preceding steps, the Phase
+2 analysis performs a basic object detection analysis.  Objects on the
+flat-fielded object image are found, and general parameters are
+measured.  Object detection is performed at several stages by the IPP,
+with different object parameters measured in each case.
+Table~\ref{tab:APdetections} gives a list of the different detection
+stages and the object parameters measured for those stages.  For the
+Phase 2 analysis, the object parameters are: the object centroid and
+the position covariance matrix, the instrumental PSF magnitude and
+error, local background level and error, a measurement of the
+star-galaxy separation, and a measurement of the object shape
+($\sigma_x, \sigma_y, \theta$).  The detection threshold must be
+configurable, and be a function of the average background flux or the
+image noise map.  Minimal object classification must be performed to
+distinguish objects which are consistent with a single PSF, objects
+which are inconsistent, and objects which are saturated.  The
+resulting collection of detected objects are saved in the AP Database
+along with the relevant image metadata (\ie filter, exposure time,
+etc).  In addition, this process constructs a model of the
+point-spread-function (PSF) as a function of position in the image.
+This PSF model is saved as part of the image metadata.
+
+\subsubsection{Identify CRs by morphology}
+
+Charged particles in the detector frequently cause features which do
+not have the morphology of astronomical objects.  In a well-sampled
+image, these may be easily identified by the sharpness of the image.
+In a near critically-sampled image, these `cosmic rays' may be
+indistinguishable from stellar objects.  This analysis step makes
+morphological identification of cosmic rays if the imaging data is
+sufficiently well sampled.  The identified cosmic rays are masked with
+a configurable growth factor so that additional pixels beyond the
+detected pixels in the feature are also masked.
+
+\subsubsection{Perform Astrometry}
+
+The detected objects are matched with known astrometric reference
+objects, using reference object coordinates which have been adjusted
+for proper motion.  The matches are then used to improve the
+astrometric parameters for the individual OTAs.  The OTA astrometric
+parameters which are determined may include terms up to 3rd order in
+position, though the terms which are actually fitted are
+user-configurable.  The Cell astrometric parameters are not allowed to
+vary at this stage.  The fit must be robust, rejecting outlier matches
+(either stars with poorly determined proper motion or spurious
+matches).  The resulting astrometric solution is consistent across the
+OTA field to within 1.0 arcsec.
+
+\subsubsection{Perform Photometry}
+
+If possible (if a local photometry reference exists), the Phase 2
+analysis determines a photometry zero point for each image.  To do
+this, it extracts the appropriate reference objects (from the AP
+Database) and matches the stars between the two samples.  The
+zero-point is derived on the basis of a static atmospheric absorption
+model (eg, linear airmass slope).
+
+\subsubsection{Bright object postage stamps}
+
+The IPP must have the capability of extracting regions surrounding a
+specified subset of objects from the flattened images.  These postage
+stamp images must be saved for additional use by client science
+pipelines.  The identification of these objects must be on the basis
+of a set of rules applied to the object's magnitude and position.  The
+postage stamps are not restricted in shape to simple rectangles, but
+may represent more complex regions.  They are written to the Image
+Server.  The outputs are these postage stamps and pixel masks, which
+are sent to the IPP Pixel Server.
+
+%\begin{figure}
+%\begin{center}
+%\resizebox{6in}{!}{\includegraphics{pics/phase2}}
+%\caption{ \label{fig:phase2} Phase 2 dataflow - this diagram is old: update}
+%\end{center}
+%\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Phase 3 : exposure analysis}
+
+The Phase 3 system operates on the combined Phase 2 results from an
+FPA to determine improved solutions for the image calibrations and to
+provide the parameters needed by Phase 4.  The Phase 3 output is saved
+by the Metadata Database, and consists largely of improved values of
+the calibrations already determined by Phase 2.  The analysis
+performed by this pipeline consists of:
+
+\begin{itemize}
+\item improved astrometric solution based on comparison between
+  objects in the images and the astrometric reference.
+\item improved background model based on the full telescope field, or
+  fields.
+\item photometric solution based on comparison to photometric
+  standards
+\item FPA-wide PSF analysis
+\end{itemize}
+
+In the Phase 2 analysis, the astrometric solutions were determined
+independently for each chip.  These solutions are limited by the
+assumption of a static distortion and by the accuracy of the
+astrometric reference.  In the phase 3 analysis, the astrometric
+solutions of the complete FPA images are improved by combining the
+astrometry for all chips.  The astrometry model consists of a
+projection of the celestial coordinates to the telescope boresite
+center, followed by a rotation to the average rotation of the FPA and
+adjustment for the central plate scale.  The free parameters in this
+stage are the boresite coordinates ($R_o, D_o$), the field rotation
+($\theta_o$) and the plate scale ($\rho_o$), and are fitted in Phase
+1.  These tangent plane coordinates are then distorted by the optical
+distortion model, consisting of $N^{\rm th}$ order polynomials in two
+dimensions.  Finally, the focal plane coordinates are mapped to the
+individual chip coordinates, including the chip position and rotation,
+as well as possible higher order terms representing warping of the
+individual detectors.  A first pass at the chip positions is
+calculated in Phase 2, while the complete set of parameters is fitted
+as a whole during Phase 3.  The fitting process is determined in a
+robust and stable way by fitting the gradient of the distortion as a
+function of field position, removing the degeneracy of the distortion
+coefficients on the chip position parameters.
+
+In the Phase 2 analysis, the background is determined based only on
+the available sky in a single chip.  However, the background
+structures are normally correlated on the scale of the FPA, so an
+improved background solution can be determined by combining the
+information from many chips.  A high-order polynomial model of the
+background may be used and subtracted from all chips.  
+
+The Phase 3 photometric improvement is made by comparing the
+photometered objects from Phase 2 with the corresponding objects in a
+local reference catalog.  This analysis may only be performed if a
+local reference is available.  Note that improved relative photometry
+calculations may be performed in the absence of a reference catalog on
+the basis of image overlaps in the AP Database {\em after} the
+detections have been added to the Database.  Such a relative
+photometry analysis is outside the scope of Phase 3 and will likely be
+performed as an independent analysis process.  Given the presence of a
+local photometry reference, the zero point variations across the field
+may be measured, and possibly modeled.  If the zero-point variations
+are excessive, then the image is marked as non-photometric by the
+analysis.
+
+In the Phase 4 analysis, the $N$ FPA images are optimally combined to
+create a single image of the sky with bad-pixel and cosmic-ray
+rejection.  This combination requires the calculation of a set of PSF
+kernels to convert each of the input images to a single, common PSF.
+These PSF kernels are determined from the per-chip PSFs measured in
+Phase 2.
+
+\subsection{Summary of the Phase 1-3 Data Products}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Phase 4 : image combination}
+
+\subsubsection{Overview}
+
+Phase 4 is the image combination stage, in which multiple images of
+the same portion of the sky are merged and confronted with the static
+sky image.  Phase 4 operates on the smallest data unit of the static
+sky, the sky cell, along with the associated pixels from a collection
+of images which have been processed through phases 1--3.  The size and
+exact representation of a static sky cell are configurable parameters
+which may vary between surveys.  In some cases, it may be feasible to
+use a single large rectangular tangent plane image for the static sky
+and accept the distortion near the edges.  In other cases, the static
+sky cells may be more appropriate defined to represent a relatively
+small patch over which the distortion is known to be small.  The
+details of the static sky cell definition should not be a driver for
+the Phase 4 analysis.  In order to meet the image degradation
+requirements, the pixel scale of the static sky is planned to be
+0.2\arcsec, somewhat smaller than the 0.25\arcsec\ raw image pixel
+scale;  the choice of this scale must be determined with some caution.
+If the pixels are too large, the degredation will be excessive; too
+small, and the total volume of the static sky image data will be
+excessively costly.
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/phase4}}
+\caption{ \label{fig:phase2} Phase 2 dataflow - this diagram is old: update}
+\end{center}
+\end{figure}
+
+In the basic concept of the Phase 4 analysis, each sky cell is
+examined independently.  The corresponding pixels are extracted from
+the exposures being processed and mapped to the projection of the sky
+cell. The pixels from the multiple input processed images are combined
+into a single, cleaned image.  Outlier pixels may be optionally
+rejected at this stage (optionally, since moving objects will be
+rejected in images obtained over a wide range of times).  This image
+is then confronted with the static sky cell data to produce a
+difference image.  Residual objects in the difference image above a
+threshold are detected and excised from the original cleaned image.
+The remaining pixels are added to the existing static sky image.
+Object detection must be performed on the difference ($P4\Delta$) and
+cleaned ($P4\Sigma$) images.  This process is illustrated graphically
+in Figure~\ref{BasicP4}
+
+\subsubsection{Image Warping and Matching}
+
+The analysis first maps the detector images to the sky cell using the
+specified linear transformations, then combines the images with strong
+rejection criteria and uses the combined sky cell image to identify
+artifacts in the original detector images.  It is desirable that the
+artifacts are masked in the detector plane (i.e.\ before mapping to
+the sky cell) so that they are not smeared out by the mapping;
+alternatively, the CR mask needs to be grown by an additional pixel.
+The mapped and masked detector images are then optimally combined
+using the specified weighting.  Both sets of combinations use the
+photometric calibration for the images to set the relative scales of
+the input images.  The limiting magnitude for the combined sky cell
+image should also be estimated.
+%
+
+\subsubsection{Static Sky Subtraction}
+
+The corresponding static sky image is subtracted from the combined
+image stack.  In this step, it is necessary to match the image kernel
+between the input image and the static sky image.  This will be done
+by solving for a best-fit image kernel which minimizes the difference
+image using a technique equivalent to the Allard-Lupton method.  One
+modification for the IPP is to represent the kernel as a combination
+of independent pixels rather than represent the components of the
+image difference kernel as a combination of Gaussians.  This method
+also automatically determines a photometric match between the static
+sky image and the input science image.
+
+\subsubsection{Object Detection and Measurement}
+
+Objects in the difference image are detected and a specific set of
+object parameters are measured from these detections.
+Table~\ref{tab:APdetections} gives a list of the different detection
+stages and the object parameters measured for those stages.  For the
+Phase 4 difference image (P4$\Delta$), the measured object parameters
+consist of: the object centroid and the position covariance matrix,
+the instrumental PSF magnitude and error, local background level and
+error, a measurement of the star-galaxy separation, and a measurement
+of the object shape ($\sigma_x, \sigma_y, \theta$).  The detection
+threshold must be configurable, and be a function of the average
+background flux or the image noise map.  Detections must be performed
+for both positive and negative (static sky only) sources.  Minimal
+object classification must be performed to distinguish objects which
+are consistent with a single PSF, objects which are inconsistent, and
+objects which are saturated.  The resulting collection of detected
+objects are saved along with the relevant image metadata (\ie filter,
+exposure time, etc).
+
+Objects in the cleaned, summed image are detected and a specific set
+of object parameters are measured from these detections.
+Table~\ref{tab:APdetections} gives a list of the different detection
+stages and the object parameters measured for those stages.  For the
+Phase 4 summed image (P4$\Sigma$), the measured object parameters
+consist of: the object centroid and the position covariance matrix,
+the instrumental PSF magnitude and error, local background level and
+error, a measurement of the star-galaxy separation, a measurement of
+the object shape ($\sigma_x, \sigma_y, \theta$), the Petrosian radius,
+magnitude, axis ratio, and angle; and the S\'ersic radius, magnitude, axis
+ratio, angle, and parameter $\nu$.  The detection threshold must be
+configurable, and be a function of the average background flux or the
+image noise map.  Minimal object classification must be performed to
+distinguish objects which are consistent with a single PSF, objects
+which are inconsistent, and objects which are saturated.  The
+resulting collection of detected objects are saved along with the
+relevant image metadata (\ie filter, exposure time, etc).  In this
+measurement, objects at known positions will also be measured even if
+they have not been detected.
+
+Objects which are detected in both of the Phase 4$\Sigma$ and Phase
+4$\Delta$ images are saved to the AP Database, along with the relevant
+image metadata (\ie filter, exposure time, etc).  In the process of
+adding these objects to the database, the transients which are
+correlated with previous detections of an object (and those which are
+not) will automatically be determined.  A subset of these transient
+objects are sent, along with their associated metadata, to the MOPS
+and other preferred science client pipelines.  
+
+\subsubsection{Static Sky Update}
+
+It is essential that the static sky image (which may have been
+painstakingly accumulated over many months) not be corrupted by adding
+in transient sources, or data that is of suspect quality (due, e.g.,
+to an error upstream in the processing).
+
+\tbd{add discussion of the choices to be made in generating the
+  static sky image stacks: interpolation methods, selection of input
+  images by IQ, smoothing of input images by their PSF, weighting and
+  clipping of input pixels}
+
+Object analysis of the static sky images is {\em not} a part of the
+Phase 4 analysis.  This processing is envisioned to take place
+relatively infrequently (perhaps weekly or even monthly) and is
+scheduled as a separate analysis task, probably run during the day at
+a time when the computing infrastructure is not under significant load.
+
+\tbd{add discussion of the multiple image analysis and object
+  analysis without the static sky (ie, on all input images at once)}
+
+\subsubsection{Magic and Phase 4 Modifications}
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/phase4a}}
+\caption{ \label{fig:phase4a} Phase 4a}
+\end{center}
+\end{figure}
+
+The Pan-STARRS relationship with the U.S. Air Force has some
+implications for the data processing which place some interesting
+constraints on the IPP implementation.  The U.S. Air Force is funding
+the construction of PS-1, and we are thus subject to restrictions
+under which the Air Force must operate telescopes.  The Air Force has
+diplomatic and security concerns about publically releasing images in
+which artificial satelites are detected, particularly if those
+satelites are identified.  Historically, Air Force projects have been
+restricted from releasing images with identified satelites,
+considering the data to be of a sensitive nature.  
+
+The language which governs such Air Force projects is in the process
+of being modified so that images with identifable satelites can be
+treated under the classification of 'For Official Use Only'.  Even
+under such an arrangement, however, the Air Force requires that the
+satelites which appear in the PS-1 images be excised in such a way
+that their orbits cannot be reliably identified.  
+
+Satelite detected in the Pan-STARRS images will appear as long ($>
+100$ pixels) streaks.  The natural, and most effective, way to
+identify such streaks is to search for them in the images after they
+have been processed using the difference image processing in Phase 4.
+A team from Boeing has been contracted to develop a software module to
+idenfity streaks in these images.  They delivered the initial release
+of their software, which is currently called {\em Magic}, in the
+beginning of January 2006.  Further effort on that software will be
+required to confront it with the real PS-1 image parameters.  
+
+In order to mesh the operation of {\em Magic} with the IPP Phase 4
+analysis for PS-1, some modifications must be made to the operation
+sequence.  In the case of PS-1, unlike PS-4, the basic set of images
+which confronted with the Static Sky image are obtained in sequence,
+not essentially simultaneously.  This is important for Magic because
+the satelite streak in one image will not appear in the other three.
+Thus the Magic operation must be performed on intermediate difference
+images for individual exposures.  To facilitate this, the basic Phase
+4 described above is divided into three stages: Phase 4a, in which the
+individual difference frames are constructed; Magic; and Phase 4b, in
+which the results of Magic are used to filter the images before the
+final stack and difference images are generated. 
+
+Figure~\ref{phase4a} illustrates the operation of Phase 4a and Magic.
+The individual warped image (P4$_W$) is constructed and differenced
+against the corresponding Static Sky image.  In this analysis, poisub,
+which performs the kernel matching and difference image construction,
+is run with somewhat looser constraints on the image difference
+kernel.  The lower-accuracy difference kernel will make the difference
+image noisier in the vicinity of galaxies and bright stars.  Unlike
+the science transients, however, most of the pixels involved in a
+satelite streak are not in close proximity to another objects; for SNe
+and GRBs, the objects are preferentially located near host galaxies
+where it is most critical to have a high-quality subtraction.
+Allowing poisub a more relaxed image difference kernel results in a
+substantial improvement in the convolution speed.  The resulting
+difference image is called P4$\Delta$'.  
+
+{\em Magic} searches for streakes in the difference images by using a
+heirarchy of Huff transforms.  A single run of {\em Magic} expects to
+have access to all of the P4$\Delta$' images from a single FPA image.
+It starts with independent Huff transforms of the individual cells,
+which can be performed in parallel under the PanTasks paralleliztion
+scheme.  The results of these Huff transforms are then merged to yield
+the equivalent of a larger Huff-transformed image.  The heirarchy
+searches for significant streaks from the individual cells, the
+equivalent 2x2 cell sets, then 2x2 of those, etc, until the entire
+array has been searched.  Significant peaks detected from the Huff
+transforms are then used to restrict the pixels in the image space.
+These pixels are examined and streaks detected by requiring a certain
+fraction of the pixels along the purported streak to be lit.
+Filtering using the cross-streak PSF can also be applied to enhance
+the detection and minimize the false positives. Any streaks which are
+detected are then excisized.  In order to obfuscate the idenification
+of the satelite, this step masks the streak pixels, with a box
+somewhat wider than the streak (10-20 pixel wide), displaced by an
+unknown, random amount relative to the streak center-line.  This strip
+is extended to the ends of the FPA array.  Analysis of the expected
+density of streaks by the Boeing team, using known satelites as the
+input to the simulation, shows that we should expect to lose less than
+1\% of the pixels due to these long excised regions. 
+
+The result of the Magic process is a set of masks for each raw PS-1
+image.  Any images which are released beyond the IPP cluster must have
+these masks applied, or with manual inspection for visible streaks.
+\tbd{time-stamps? ok to release without the valid time? or only with }
+
+The final portion of the analysis, Phase 4b, is illustrated in
+Figure~\ref{phase4b}.  In this stage, the intermediate result images
+from Phase 4a can be used.  The P4$_W$ images are masked with the
+corresponding masks from Magic.  These images are then stacked as
+normal, and the resulting summed image is differenced with poisub,
+using a more stringent limit on the image difference kernel.
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/phase4b}}
+\caption{ \label{fig:phase4b} Phase 4b}
+\end{center}
+\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{System Design : Calibration Image Processing}
+
+The Calibration Analysis Stages construct calibrations from the
+relevant input data.  Some of these combine multiple raw input images
+together, after processing, to create a high-quality high-signal
+master calibration image.  Some of the calibrations are used to
+correct other calibrations.  Each of the calibration stages must also
+provide the tools to test the quality of the input data against
+existing master calibration data and to test the consistency of
+multiple measurements of the calibration.
+ 
+The Calibration analysis stages may be performed on whatever
+timescales are appropriate and necessary to maintain the quality and
+relevance of the calibration images.  The specific calibration data
+which must be constructed in the calibration analysis stages is listed
+below.
+
+The IPP must generate basic calibration images using the raw bias,
+dark, and flat-field (dome or twilight) images obtained by the
+telescope as the input.  The analysis of these images requires
+relatively simple stacking of the input set of images.  Outlier
+rejection, both of complete input images as well as pixels within the
+input stack, must be performed.  In addition, each type of image
+requires an appropriate normalization which may depend on the data
+levels in other detectors in the input set.  Each of these calibration
+stages must be able to determine from the input stack if the relevant
+calibration image needs to be updated and perform an initial test to
+see which input images are consistent and valid.
+
+\subsection{Bias Images}
+
+Bias images may be needed to correct for structure in the bias.  The
+IPP must have the capability of constructing a master bias image from
+a stack of raw bias frames.  The input bias images, representing
+offsets from the overscan level, are processed by subtracting the
+overscan, including 1D structure if needed.  
+
+The master bias frame construction uses outlier image and outlier
+pixel rejection to construct a single high-quality bias frame.  The
+statistic used to determine pixel values from the input stack can be
+set by the user to be one of the following: the sample mean, median,
+and mode, robust mean, median, and mode, and the clipped mean and
+median.  Testing of the input images consists of constructing residual
+images, in which the master bias is applied to the input images.
+These images may be included or excluded from an additional iteration
+of the stack on the basis of their pixel-to-pixel statistics.
+
+\subsection{Dark Images}
+
+Dark images may be needed to correct for structure in the dark
+current.  The IPP must have the capability of constructing a master
+dark image from a stack of raw dark frames.  The input dark images are
+first corrected for the bias using whatever method is appropriate for
+the science images.  Master dark frames depend on their exposure time.
+As such, the input dark frames must have a limited range of exposure
+times, and the output dark frame includes the exposure time as part of
+its associated metadata.  
+
+The master dark frame construction uses outlier image and outlier
+pixel rejection to construct a single high-quality dark frame.  The
+statistic used to determine pixel values from the input stack can be
+set by the user to be one of the following: the sample mean, median,
+and mode, robust mean, median, and mode, and the clipped mean and
+median.  Testing of the input images consists of constructing residual
+images, in which the master dark image is applied to the input images.
+These images may be included or excluded from an additional iteration
+of the stack on the basis of their pixel-to-pixel statistics.  A
+collection of master dark frames with a range of exposure times are
+used to determine the scaling of the dark frame as a function of
+exposure time.
+
+\subsection{On-Off Dark Images for Light Leaks}
+
+A type of image which may be necessary for calibrations will be pairs
+of images taken at night with the shutter closed with and without the
+dome shutter closed.  Such a pair of images can be used to determine
+any light-leak in the camera which may contribute additional flux
+across the mosaic.
+
+\subsection{Flat-Field Images}
+
+Master flat-field images must be constructed from a collection of
+input flat-field images.  The input flat-field images may be obtained
+from any of the standard sources: the dome, the twilight sky, and the
+night-time sky.  The choice of flat-field input image must be
+determined experimentally from observations during the commissioning
+phase of the telescope.  The IPP flat-field construction system must
+be capable of handling any of these sources.  
+
+An appropriate set of input images is selected on the basis of their
+flux levels, time of observations, and the observing conditions.  The
+input flat-field images are processed (bias and dark corrected if
+needed) and the resulting images are stacked.  The master flat-field
+construction uses image and pixel outlier rejection to construct a
+single high-quality master flat-field frame.  The statistic used to
+determine pixel values from the input stack can be set by the user to
+be one of the following: the sample mean, median, and mode, robust
+mean, median, and mode, and the clipped mean and median.  Testing of
+the input images consists of constructing residual images, in which
+the master flat-field image is applied to the input images.  These
+images may be included or excluded from an additional iteration of the
+stack on the basis of their pixel-to-pixel statistics.
+
+\subsection{Mask Images}
+
+Preliminary bad-pixel mask images are generated on the basis of
+comparison between raw flat-field images and a cleaned, stacked
+master.  The mask creation system accepts a collection of flat-field
+images and identifies pixels which are consistently poorly flattened.
+Pixels which are under-responsive are also identified as pixels to be
+masked.  
+
+\subsection{Sky \& Fringe Frames}
+
+Fringe-correction frames must be generated to remove the fringe
+pattern caused by thin-film interference in the top layers of CCDs,
+particularly in the redder passbands.  Fringe correction frames may be
+constructed on the basis of observations of the night-sky in the
+appropriate filters or on the basis of dome fringe lamp observations.
+The choice of the appropriate source will be determined experimentally
+on the basis of data obtained during the commissioning phase.  The IPP
+must be capable of handing either source.  The images are first
+flattened to remove the pixel-to-pixel sensitivity variations of the
+detector.  The combination of multiple input fringe frames may not be
+simply stacked since the amplitude of the fringe pattern varies
+independently of other variations in the image.  The amplitude of the
+fringe pattern in the input frames is measured and the images scaled
+to normalize the fringe amplitude to a consistent range (-1 to +1) for
+all input images before they are combined with one of the standard
+combination statistics (mean, median, mode, etc).  The quality of the
+input frames is tested by flattening the input image and applying the
+master fringe-frame.  The resulting residual image statistics are used
+to select or exclude specific input images.
+
+\subsection{Shutter Correction Map}
+
+Shutter correction map images may be generated based on the timing
+measurements of the shutter itself, or on the basis of dome-flat
+images of decreasing exposure times down to the shortest available
+exposures.
+
+\subsection{Low-k Sky Models}
+
+Large-scale background structure in images which is not caused by
+thin-film interference must also be detected and corrected.  Models of
+this background structure may be a necessary input to the correction
+procedure.  The IPP must have the capability of generating image
+models of the large-scale structure patterns observed with the
+telescope
+
+\subsection{Flat-Field Correction Frame}
+
+Flat-field images, whether constructed from the dome, twilight, or
+night-sky images, do not perfectly correct the detector response in a
+consistent fashion across the full field of the camera.  The IPP must
+have the capability of generating flat-field photometric correction
+frames on the basis of the measured photometry of objects which are
+moved to a variety of locations on the detector in a sequence of
+images.  The flat-field correction frames analysis stage makes use of
+targeted observations following a specified dither pattern, and
+extracts the photometered objects from the AP Database to determine
+the necessary photometric corrections.  The resulting image is applied
+to the master flat-field image.  Testing of the correction is
+performed by applying the correction to the basic master flat-field
+image, applying that flat-field image to the dithered photometry
+observations, and performing the object detections.  Comparison of the
+photometry of individual stars at different locations on the mosaic
+will demonstrate the consistency of the flat-field image.
+
+\subsection{Non-Linearity Correction}
+
+The IPP must have the capability of constructing a correction for
+non-linearity in the detectors.  These frames are constructed from
+exposures of a uniform source with a range of exposure times.  The
+non-linearity correction frames provide polynomial correction
+coefficients or a lookup table describing the correction.  There is
+likely to be a single non-linear correction for each OTA detector, or
+potentially for each Cell.  The IPP must handle these two cases.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{System Design : Miscellaneous Analysis Stages}
+\label{sec:MiscStages}
+
+This section discusses additional operations which are performed by
+the IPP but which do not fall under the analysis of the science images
+or the creation of the calibration images.
+
+\subsection{Retrieval}
+
+The retrieval stage simply retrieves images from an external source
+(ordinarily OTIS at the Summit, but it could conceivably be some other
+external source) and store it in the Image Server.  
+
+\subsection{Static Sky Analysis}
+
+The IPP is responsible for performing object detection and analysis on
+the static sky.  This analysis is performed continuously (every day or
+week) on those portions of the sky within 15\degree\ of the sun.  In
+this analysis, the object measurement is much more detailed than those
+performed in the real-time analysis.  The currently envisioned
+parameters to be measured for every object are listed in
+Table~\ref{tab:APdetections}.  The parameters include the object centroid
+and the position covariance matrix, the instrumental PSF magnitude and
+error, local background level and error, a measurement of the
+star-galaxy separation, a measurement of the object shape ($\sigma_x,
+\sigma_y, \theta$), the Petrosian radius, magnitude, axis ratio, and
+angle; the S\'ersic radius, magnitude, axis ratio, angle, and
+parameter $\nu$, and a collection of annular aperture flux
+measurements, all of which are also measured for the P4$\Sigma$
+images.  In addition, the galaxy-shape parameters $\Gamma_1 \&
+\Gamma_2$, along with the complete `polarization' terms are measured,
+as well as a collection of annular aperture flux and variance
+measurements.  Another unique feature of the static sky analysis is
+that the object detection may be performed simultaneously on all
+filters, in which case the locations and other parameters may be more
+strongly constrained by simultaneously fitting between all bands.  The
+analysis to be performed may be substantially more complex than the
+real-time analysis because of the relatively low data rate.  Instead
+of needing to process thousands of images per night ($\sim 350$Mpix
+per second), it is only necessary to process the complete sky in a
+year, or an average rate of $\sim$2 Mpix per second, or $< 1$\% of the
+object analysis in the other analysis stages.  These operations are
+all functions which will be performed within the PSPhot program using
+recipe options.
+
+\subsection{Astrometric and Photometric Reference Catalog}
+
+The IPP is responsible for generating the Astrometric and Photometric
+(AP) Reference Catalog.  The IPP provides several tools for performing
+this analysis.  The DVO programs \code{relphot}, \code{uniphot}, and
+\code{relastro} perform most of the operations required to generate
+the AP Reference Catalog.  These include the determination of the
+image zero-points, the identification of objects with significant
+variability, the detection of individual outlier measurements, the
+detection of objects with substantial astrometric error, the analysis
+of parallax and proper-motions, etc.  In addition, the DVO shell
+program will be used to generate the color transformations from the
+observed data and to perform other tests of the catalog quality.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Software Hierarchy}
+\label{sec:SoftwareHierarchy}
+
+In order to facilitate testing and development, and to encourage
+flexibility, the IPP is built in a layered fashion.  The lowest level
+functions will be written in C and collected together into a
+Pan-STARRS library.  These library functions will be used to write
+more complex modules.  The modules will be written in C but will make
+use of SWIG to make their functionality available within other
+languages.  In particular, the modules can be tied together with a
+program in C or through the use of a high-level language such as Perl,
+Python, or Tcl employing the SWIG interfaces.  For the high-level
+functions in the operational system, the IPP will make use of Perl as
+the scripting language to provide the required flow-control to tie the
+modules together.
+
+This approach satisfies the requirement that complicated low-level
+analysis steps run fast, while preserving flexibility for coding the
+high-level wrappers for which the speed requirements are not so
+stringent.
+
+\subsection{External Libraries}
+
+Pan-STARRS will employ several external libraries to save duplicating
+functionality that is already available.  These external libraries
+will be wrapped by the Pan-STARRS Library, insulating the project from the
+implementation details of the external libraries.  Examples of the
+external libraries are FFTW and SLALib.
+
+\subsection{Pan-STARRS Library}
+
+The Pan-STARRS Library will consist of C structures describing the
+basic data types needed by the IPP and C functions which perform the
+basic data manipulation operations.  Note that a subset of the library
+functions will be provided with SWIG interfaces as well to allow for
+their use in the creation of the processing stages.  Examples of the
+Pan-STARRS Library are Fourier transforms and transforming between
+pixel and celestial coordinates.  The details of the Pan-STARRS
+Library are specified in the document Pan-STARRS IPP PSLib
+Supplementary Design Requirements Specification (PSDC-430-007), which
+also addresses coding requirements detailed in the IPP PS-1 SRS
+(PSDC-430-005), Section 3.3.
+
+\subsection{IPP Modules}
+
+The IPP analysis stages are broken down into modules which represent
+specific functional operations.  The modules will be written in C
+using the Pan-STARRS Library functions and will be grouped into a
+Pan-STARRS Module Library.  The modules will be provided with SWIG
+interfaces to all public APIs for their use in processing stages.
+Examples of modules are overscan subtraction and image combination.
+Some modules (e.g.\ find objects on an image) will be used by multiple
+stages.  The details of the Pan-STARRS Modules are specified in the
+document Pan-STARRS IPP Modules Supplementary Design Requirements
+Specification (PSDC-430-012), which also addresses coding requirements
+detailed in the IPP PS-1 SRS (PSDC-430-005), Section 3.3.
+
+\subsection{IPP Analysis Programs}
+
+The major IPP processing tasks are organized into stages, which
+consist of multiple modules.  Each stage represents a collection of
+complex operations performed on a single data entity.  Each stage
+therefore represents the maximum amount of effort which can be
+performed in serial without interaction between parallel threads.  The
+stages will be written in Perl, linking the modules together.
+Examples of stages are Phase 2 (detrend images) and Phase 4 (combine
+images from multiple telescopes and search for transients).
+
+\section{Top-Level IPP Analysis Programs}
+\label{sec:AnalysisPrograms}
+
+The IPP uses a handful of high-level analysis routines which perform
+the bulk of the actual analysis effort.  These routines are used both
+as stand-alone programs and in some cases as library functions called
+by other stand-alone programs.  The 6 primary top-level analysis
+routines are:
+\begin{itemize}
+\item {\bf ppImage} : the complete single-image analysis program.
+\item {\bf ppMerge} : the basic image combinations program.
+\item {\bf psphot} : the photometry analysis routine.
+\item {\bf psastro} :  the astrometry analysis routine.
+\item {\bf stac} : the science image combination program.
+\item {\bf poisub} : the image difference program.
+\end{itemize}
+
+These programs are not mutually exclusive: psphot and psastro are both
+used by ppImage, while psastro is used as a stand-alone tool within
+the IPP, and psphot can be used as such.  
+
+\subsection{Software Configuration and  Camera Definition Information}
+
+Every camera which may be analysed by the IPP has differences in how
+the data is represented.  The IPP is built with the flexibility to
+handle data from many different cameras, not just the Pan-STARRS
+Gigapix cameras.  This is partly to allow testing of the analysis
+system on data from other telescopes, such as MegaPrime on CFHT and
+Suprime on Subaru, but also to allow us to adapt to changes in the
+design of the Gigapix cameras themselves.  It also means the IPP
+software may be used by astronomers for other analysis projects beyond
+the IPP.  
+
+Most cameras provide extensive descriptive information in the FITS
+image headers when the images are read out.  Typically, the location
+and orientations of the individual detectors are defined by keywords
+such as DATASEC and DETSEC.  Other variations on these words are used
+for cameras which place the pixels from multiple amplifiers in the
+same FITS data segment.  Other parameters, such as astrometric
+information or exposure times, are stored in headers as well.  It is
+possible to use these header keywords to guide the analysis software,
+but there are two difficulties.  
+
+First, it is very common for different keywords to be used by
+different cameras, sometimes even the same camera may use different
+keywords for the same information at different times (major readout
+software upgrades, for example, can be accompanied by keyword
+revisions).  In addition, within Pan-STARRS and the IPP, it is
+necessary to have the capability to refer to the Metadata database as
+the authoratative sources of some of these entries rather than the
+image headers.  Given this circumstance, it is at least necessary to
+define the appropriate source for a given data concept appropriate to
+data from a specific camera.
+
+The second problem arises when actually performing an analysis.  In
+many circumstances, the software needs to know what data to expect
+even when an appropriate camera image is not available.  This is
+particularly true for a camera which is composed of multiple chips and
+multiple amplifiers.  It is a frequent circumstance than some subset
+of the chips or amplifiers will either be unavailable or are invalid
+for one reason or another.  It is important for the software to have a
+guide for what data should be available from a perfect readout of the
+given camera so decisions can be made how to handle data which is not
+complete.  This is also important to validate that a particular
+dataset, which appears to be from a known camera, actually corresponds
+to that camera and has all of the necessary information where
+expected.
+
+As part of the flexible design model, all of these analysis programs
+use a common set of configuration files which define the details of
+how their analysis should be performed.  The configuration files a
+further divided into three main sets of configuration information:
+\begin{itemize}
+\item {\bf site configuration} this information defines the locations
+  of data resources and the other available configuration information.
+  For example, the site configuration would include the location of
+  Nebulous, the metadata database access information, the list of
+  available cameras, and so forth.
+\item {\bf camera configuration} this information describes
+  characteristics needed to interpret data from a specific camera.
+  This includes such details as where to extract particular metadata,
+  such as the filter name.  It also defines the camera layout and the
+  expected organization of the data files.  
+\item {\bf recipe} a particular analysis program may use one or
+  multiple recipes.  The recipe defines the value of optional
+  configuration information for that analysis program.  For example,
+  by using different recipes, ppImage can be made to perform a
+  complete Phase 2 image analysis, including detailed object detection
+  and astrometric calibration, or with a different recipe, ppImage may
+  be used to perform only bias subtraction (for example, as part of
+  the detrend image analysis).    Note that the details of a recipe in
+  general depend on the camera / telescope of interest.  As a result,
+  the identify of the recipe files which define different recipes are
+  included as part of the camera configuration information.
+\end{itemize}
+For all of the analysis programs, the source of the configuration
+files can be overridden when the program is executed, and individual
+configuration values may also be specified on the command line.  The
+details of the configuration file formats and the configuration
+variable names used by different programs and functions are discussed
+in the Modules SDRS document.
+
+\subsection{ppImage}
+
+This program is not only one of the work-horse programs of the IPP, it
+is also exemplary of the design of the top level analysis program.
+ppImage is used to perform the complete Phase 2 analysis on a single
+image data file.  This includes the complete detrending discussed
+above (bias, dark, flat, fringe, etc), as well as the object detection
+and classification, the astrometric calibration, and potentially the
+photometric calibration as well.  The object analysis and astrometry
+are in fact performed by ppImage using two of the other top-level
+routines discussed above, psphot and psastro; these components will be
+discussed independently and will mostly not be part of the ppImage
+discussion.
+
+The top-level design of ppImage is virtually identical to that of
+several other stand-alone analysis programs (eg, psphot, psastro,
+ppMerge).  The input to ppImage may be one of several options:
+\begin{itemize}
+\item a single FITS file by name (eg: /full/path/to/file.fits)
+\item a collection of FITS files by glob (eg: /full/path/to/file.*.fits)
+\item a single Nebulous storage ID (neb:nebID)
+\item a collection of Nebulous storage IDs (neb:nebID.regex)
+\end{itemize}
+The Nebulous versions can be viewed as simply utility versions to save
+the user from asking Nebulous for the corresponding file names.  In
+addition to the input file and any optional configuration information,
+ppImage also takes as part of the command-line arguments the root of
+the output file names; ppImage defines naming conventions to
+constructing the complete output files based on the recipe.  
+
+After parsing the command-line options, ppImage performs the following
+tasks:
+\begin{itemize}
+\item identify the camera associated with the file or files (all files
+  must be of the same camera). This analysis is performed by examining
+  the primary headers of the files and applying camera-identification
+  rules specified as part of the camera configuration information.  It
+  is also possible for the user to assert the camera which supplied
+  the image(s).
+
+\item construct a complete FPA structure representing the camera
+  (without pixel data) and associate the input filenames with the
+  different components of the FPA structure.  In this step, the camera
+  configuration information is used to identify the cell, chip, etc
+  which corresponds to each file, and the filenames are associated
+  with elements at the appropriate levels.  Since each input file has
+  already had its primary header read, this is also attached to the
+  appropriate FPA structure level.
+
+\item the complete FPA is then looped over, with nested loops for each
+  of the levels: FPA, Chip, Cell, and Readout.  At the appropriate
+  depth (depending on user options and the recipe), the pixels are
+  loaded from the corresponding file.  The recipe may, for example,
+  require the entire FPA image be read, or it may specify that each
+  cell be loaded one at a time.  
+
+\item the detrending analysis is peformed on each readout.  Note that
+  the detrend images needed for the analysis are defined by the recipe
+  and by querying the metadata database for the detrend image which
+  matches the science image in hand.  The details of this match may be
+  modified by the recipe as well, allowing for example, one recipe to
+  apply only the best available flat-field images, while another for
+  comparison only applies an earlier flat.  These types of options are
+  needed for testing, and also to perform, e.g., the flat-field
+  correction analysis.  Also note that the detrend images may be
+  loaded at a different level from the science images; it is likely
+  that a detrend image is only defined for each Cell, while the
+  science images may be loaded by Readout.  
+
+\item after each chip is processed, ppImage will optionally
+  reconfigure the resulting pixels into a single contiguous array.
+  This is the normal data source for the psphot function call used by
+  ppImage.  The reconfigured chip image on this stage may also be used
+  to generate thumbnail and rebinned sample images on the chip level.
+
+\item ppImage may also be used to reconfigure the pixels from a
+  complete FPA into a single pixel array.  For the GPC, this step
+  would not normally be performed on the full data array, but would be
+  used to generate rebinned sample images of the full mosaic (in
+  either FITS or JPEG formats).  This step is used by Phase 3 to
+  construct images for examining the state of the data processing or
+  by the detrend creation analysis to test the quality of the
+  residuals.  
+ 
+\item the final stage is to output, at an appropriate depth, the
+  chosen output data files and to send summary metadata to the
+  metadata database.  these output files may include FITS images, FITS
+  thumbnails (very small rebinned images), FITS samples, JPEG images,
+  object tables, and/or astrometry calibration files.
+\end{itemize}
+
+Except for the details of the analysis (detrending, etc), the detail
+of the processing steps are identical for psphot, psastro, and
+ppMerge, as discussed below.
+
+\subsection{ppMerge}
+
+This program performs the job of stacking multiple images in which the
+pixel grid is kept the same for all of the input images.  As part of
+the combination process, the input images may be scaled and shifted.
+This operation is the basis for the creation of all of the primary
+master detrend frames: bias, dark, flat, fringe.  For all of these
+cases, the output pixel values are determined by applying some global
+statistic to the collection of input pixels after the input pixels
+have been rescaled.  The combination statistics may be any of several
+standard options, including mean, median, sigma-clipping, etc.  The
+input files may be supplied to ppMerge using any of a command-line
+glob, a text-based list, or an XML list.  The glob method forces the
+zero and scaling to have values of 0 and 1, respectively.  The text
+file listing allows for zero and scaling values, but only a single
+value for each input file.  The XML-format list is needed if
+subdivisions within a file (eg, cells) require independent zero and
+scaling factors.  It is also necessary to specify more than one image
+file if, eg, a full FPA composed of separate Chip files is to be
+processed at once.  This analysis is performed using a very similar
+structure to ppImage: the input list of images is parsed and
+associated with the approrpiate elements of the FPA struture, though
+in this case, an array of FPA structures is used to carry the
+information.  The elements of the FPA are looped over, as with
+ppImage, and the analysis is performed on the lowest level, after the
+input data pixels are read and re-scaled.  The output is written out
+after the image have been looped over. 
+
+\subsection{psphot}
+
+The details of psphot are discussed in the psphot design document.
+Here we will just address some of the basic concepts.  PSPhot may be
+used as a stand-alone program, or it may be called as a function
+within another program (such as ppImage).  In the function form,
+psphot is passed a pmReadout structure which is populated with the
+image of interest; weight and mask elements may be supplied, or else
+constructed based on standard rules.  As a stand-alone program, psphot
+will use a structure similar to that used by ppImage to load in all of
+the image components and loop over all of the readouts, performing the
+analysis on readout-by-readout basis.  Note that for the images from
+the Static Sky or Phase 4, the readout structure is also used, though
+in that case, the readout is contained by a SkyCell, not a cell within
+an FPA heirarchy.
+
+PSPhot performs the complete object detection and analysis process,
+using the following basic outline:
+\begin{itemize}
+\item {\bf assess image} examine the image statistics; if needed and
+  desired, fit a model to the background and subtract it.
+\item {\bf detect peaks} search the image for peaks above a defined
+  threshold
+\item {\bf basic source stats} examine the peaks to determine the local
+  sky, centroid, first and second moments and other fast, simple
+  statistics.
+\item {\bf rough classification} based on the source moments,
+  determine the likely stellar sources and classify sources based on
+  their moments in relation to the stellar source moments, as well as
+  the saturation level, etc.
+\item {\bf basic deblend} identify peaks which are in the wings of
+  brighter sources, mark them for careful attention later.
+\item {\bf choose PSF} determine a PSF model from the bright possible
+  stellar sources.  PSPhot allows a wide range of possible analytical
+  PSF models, and may be set to automatically select the best one for
+  an image.  The parameters of a PSF are allowed to vary spatially.
+  Currently, pixel-based PSFs are not implemented, but can be added as
+  an upgrade.
+\item {\bf fit sources} the heart of PSPhot is the analysis of the
+  brighter sources in the image.  In this stage, each source (above a
+  certain threshold) is fitted with the PSF model, the source is
+  identified as PSF-like or not.  If not, extend source models and
+  double-PSF models are also attempted.  The best of the models is
+  kept, unless the fits fail complete, and the model is subtracted.
+\item {\bf photometry checks} curve-of-growth and aperture corrections
+  are measured and applied to the PSF photometry.
+\item {\bf output stage} as a stand-alone program, psphot will produce
+  output tables in various formats.
+\end{itemize}
+
+\subsection{psastro}
+
+This section discusses the design of psastro in general terms.  For
+more specific usage information, see the document on psastro.  Like
+psphot, psastro may be run as a stand-alone program or as a library.
+This routine performs astrometric calibration of a collection of
+sources from an image or a set of images.  As a stand-alone program,
+the input to psastro is an image file or set of images files
+representing data from single FPA.  Like ppImage, the input image or
+images may be specified as a single file, a glob, or Nebulous storage
+object IDs.  The heart of the psastro routine is wrapped with a loop
+similar to the one used for ppImage.  The primary difference between
+the two is that psastro expect a collection of files representing
+sources detected in an image, rather than the pixels from an image.
+Several standard source file formats, discussed in the psphot
+documentation may be read.  The sources which are loaded are added to
+the FPA heirarchy at the appropriate level, as part of the metadata
+collection associated with a given level.  As a library function,
+psastro is passed an assembled FPA structure with the sources already
+placed in the appropriate locations in the metadata containers.
+
+Psastro has three major modes, which are used in each of Phases 1, 2,
+and 3.
+
+In the conceptually most straightforeward mode, each readout contained
+in the incoming FPA structure is treated independently.  The metadata
+describing the approximate astrometry of the readout are used to guess
+the coordinates of the sources and to select likely reference sources
+from the selected reference catalog.  The observed sources are matched
+to the reference sources, using either a two-point grid search or
+optionally a triangle match.  Once an approximate match is found, a
+linear fit between detector coordinates an projected celestial
+coordinates is attempted.  The projected coordinate system may
+optionally make use of the default telescope distortion model, if it
+is known.  The radius of the match between observed and reference
+sources is reduced to improve the statistics of the match.  This
+anaysis mode is used in the Phase 2 processing.
+
+The second type of psastro analysis is used by Phase 3 to improve the
+solution determined in Phase 3.  In this analysis, psastro expects
+detections from a complete mosaic FPA of chips.  A complete mosaic
+solution is performed in which a single distortion model is used for
+the telescope optics and additional linear or higher order terms.
+This analysis breaks the degeneracy of the chip position / telescope
+distortion by fitting the local gradient of the distortion on chips to
+model the telescope distortion.  The result of this analysis is a
+complete mosaic astrometry model, consisting of (at the moment) 3rd
+order polynomials for the telescope distortion and up to 3rd order
+polynomials for each of the individual chips.
+
+In the third mode, a collection of detections from across a full
+mosaic are used, in conjunction with a model for the chip positions
+and the telescope / optical distortion, to determine a single
+low-order model for the complete FPA.  In this model, only the
+boresite coordinates, rotation, and X and Y plate scales are allowed
+to vary.  This analysis is used for the Phase 1 analysis, in
+conjunction with an implementation of psphot. 
+
+For any complete mosaic, there are three complementary representations
+of the astrometric solution which are used within the IPP.  The most
+basic description of the astrometry is the collection of header
+keywords which define the boresite center coordinates (RA, DEC), the
+location of the chip data arrays within the full mosaic (the IRAF
+DATASEC keywords).  More detailed astrometric information may be
+defined using the WCS keywords.  These unfortunately do not have a
+standard representation of higher-order terms.  Furthermore, the two
+competing systems which have been proposed define only a single
+transformation frame.  In the IPP, it is important to carry around
+more information which can be used to improve our astrometric
+solutions in the future.  Specifically, we would like to maintain at
+least the transformation for the telescope optics independent from the
+individual chip warps or tilts.  Even more, we would like to have a
+flexible astrometry definition format which can be extended in a
+flexible fashion.  We have defined a FITS table convention to carry
+all of the elements of the astrometric transformation of a full FPA.
+Within the table, transformations are generally defined to convert one
+layer (eg, focal plane) to another.  The form and the parameters of
+the conversion make up the columns of the table.  With this structure,
+it is possible to add arbitrary layers as needed.  The IPP, and
+portions of the project (particularly Otis), will share a common
+default astrometry model for the telescope.  This will be defined on
+the basis of measurements over the first weeks of observations.  The
+IPP will perform the astrometric analysis of individual images with
+psastro, and the results can be saved in this tabular format.  Over
+time, these result tables can be used to improve the astrometric model
+for the telescope, and to improve the astrometric reference catalog.
+
+\subsection{poisub}
+
+Poisub is the image difference analysis program.  \tbd{finish this
+discussion}.
+
+\subsection{stac}
+
+STAC is the program which warps and optimally combines images from the
+same region of the sky.  It consists of two major stages: the warping
+stage and the image combination stage with robust outlier rejection.
+\tbd{update / finish this discussion}
+
+\subsection{Command Sequences}
+
+It is useful in order to understand the analysis sequence to examine
+the complete series of processing steps involved in the analysis
+stages discussed above.  We first illustrate the Phase 1-3 sequence,
+giving the commands which start with a raw image available on disk and
+results in a collection of detrended chip images, a high-quality
+astrometric calibration, and a collection of object detections.  
+
+\subsubsection{Phase 1-3 Analysis Commands}
+
+In the example below, we imagine a GPC image available on disk with
+the exposure ID 654321.obj and chip IDs 00 through 88.  The IPP design
+does not mandate specific naming convensions for the exposure IDs and
+the chip IDs; these values are opaque strings supplied by the data
+source (eg, GPC).  Below, in Section~\ref{ipptools}, we discuss how
+the names and inputs are constructed, and how the relationships are
+tracked between an exposure and the data containers which make up the
+exposure.  Also, the details of directory naming and organization are
+just examples, though some nightly folder scheme is a likely option.
+For the moment, these are assumed to be known by the system
+
+Also discussed in Section~\ref{ipptools} it the concept of multiple
+analysis passes for a data element.  Within the IPP, any data may be
+processed multiple times; the system tracks each attempt to process a
+particular set of data, tracking the analysis versions numbers which
+increment sequentially for each new attempt.  In the sequence below,
+we are performing the first analysis attempt on the data, so the
+version numbers are all 0. 
+
+\begin{verbatim}
+Phase 1:
+  ppImage -recipe PHASE1 
+	  -inglob    file:/data/2006.11.01/7654321o/7654321o.??.fits 
+	  -out_astro file:/data/2006.11.01/7654321o/7654321o.XX.P1.00.ast.fits
+
+Phase 2:
+  ppImage -recipe PHASE2 
+	  -infile    file:/data/2006.11.01/7654321o/7654321o.24.fits
+	  -in_astro  file:/data/2006.11.01/7654321o/7654321o.XX.P1.00.ast.fits
+          -outfile   file:/data/2006.11.01/7654321o/7654321o.24.P2.00.img.fits
+          -outmask   file:/data/2006.11.01/7654321o/7654321o.24.P2.00.msk.fits
+          -outvar    file:/data/2006.11.01/7654321o/7654321o.24.P2.00.var.fits
+          -objects   file:/data/2006.11.01/7654321o/7654321o.24.P2.00.cmf.fits
+          -thumb     file:/data/2006.11.01/7654321o/7654321o.24.P2.00.thm.fits
+          -binned    file:/data/2006.11.01/7654321o/7654321o.24.P2.00.bin.fits
+
+Phase 3:
+  ppImage -recipe PHASE3
+          -inglob    file:/data/2006.11.01/7654321o/7654321o.??.P2.00.cmf.fits 
+	  -out_astro file:/data/2006.11.01/7654321o/7654321o.XX.P3.00.ast.fits
+	  -objects   file:/data/2006.11.01/7654321o/7654321o.XX.P3.00.cmf.fits
+
+  ppImage -recipe MOSAIC
+          -inglob   file:/data/2006.11.01/7654321o/7654321o.??.P2.00.thm.fits 
+          -outjpeg  file:/data/2006.11.01/7654321o/7654321o.XX.P3.00.thm.jpeg 
+
+  ppImage -recipe MOSAIC
+          -inglob   file:/data/2006.11.01/7654321o/7654321o.??.P2.00.bin.fits 
+          -outjpeg  file:/data/2006.11.01/7654321o/7654321o.XX.P3.00.bin.jpeg 
+\end{verbatim}
+
+In this example, the data are supplied by their file names in the UNIX
+file system.  We are invoking the capability of ppImage to accept a
+glob to supply a list of files.  In the Phase 1 stage, ppImage is used
+with the PHASE1 recipe to load data from a full mosaic (the files
+specified by the glob) and produce a single astrometry calibration
+file.  The \code{XX} is supplied for the full-mosaic output files just
+to make the output data products appear in a more easily readable
+fashion is a directory listing.  Also, note that we attach the
+\code{.fits} extension to all of these output files to make the data
+type more explicit to the reader.  Again, none of these convensions
+are required by the analysis programs.
+
+The Phase 2 analysis uses the Phase 1 astrometry to improve the
+astrometric starting guess.  The analysis for Phase 2 is illustrated
+for just a single chip (24), though presumably equivalent commands are
+executed for the other 63 chips.  The six output files selected in
+this example include the detrended image (\code{*.img.fits}), along
+with the corresponding mask (\code{*.msk.fits}) and variance images
+(\code{*.var.fits}) and the photometry results file
+(\code{*.cmf.fits}).  Note that the output object file contains the
+astrometric solution parameters from this analysis stage.  This
+process also constructs two smaller version output images: the binned
+image (\code{*.bin.fits}) and the thumbnail (\code{*.thm.fits}).  The
+binning scale for these images is specified in the recipe for the
+camera; the first of these for GPC would likely be binned 32x32, while
+the second would probably be binned 320x320.
+
+For the Phase 3 analysis stage, three actual analyses are illustrated.
+In the first, the photometry results files are identified by file glob
+and the result is an improved astrometric model for the camera and
+optics in our astrometry parameter table format.  The object files are
+also grouped into a single multi-extension file along with the
+astrometric calibration.
+
+In the second and third analysis examples, the collection of chip
+binned and thumbnail images are loaded by file glob, mosaic-ed
+together into a single image, and written to disk as a JPEG.  These
+images are used by the pipeline tracking tool, ippMonitor.  The binned
+image results in a full GPC image represented by 1200x1200 pixels; the
+thumbnail yields a 120x120 representation of the full GPC.
+
+The example above would be sufficient if we were processing a small
+number of images by hand or for test purposes.  However, the IPP is
+designed to be more flexible about the physical location of the data
+files that this illustration permits.  The use of nebulous allows us
+to use a similar naming scheme and yet place the actual data files on
+different hardware depending on the chip ID (among other
+possibilities).  To convert the filename version above to a version in
+which the files are stored on Nebulous simply requires changing the
+\code{file:} tag to \code{neb:}.  The analysis programs recognize this
+tag to indicate a file available from Nebulous, and make a request to
+Nebulous for the actual file names.  Nebulous can supply files based
+on a name match much like the file glob.  Nebulous also allows the
+storage object ID to include path-like elements, allowing a structured
+organization of the files within Nebulous (which does not reflect a
+{\em physical location} relationship).
+
+\subsubsection{Basic Detrend Creation Commands}
+
+In the following example, we examine the steps to produce master
+detrend images.  First, a few important points to note about this
+process.  The construction of a master detrend frame (bias, flat, etc)
+involves combining a number of individual frames of an appropriate
+type of exposure, possibly after some preparatory processing.  For
+example, in building a twilight flat-field image, 5 or 10 (or however
+many) raw flat-field images are first masked and bias corrected before
+being combined.  To build a night-time fringe-frame image, a
+collection of raw night-time images are bias, dark, and flat-corrected
+before they are combined.  In the combination, it may be necessary to
+apply some scaling and/or offset correction to the images to place
+them on a common footing.  For example, in the construction of a
+master flat-field image, the individual images must be normalized in a
+consistent fashion; in building a master fringe frame, the fringe
+amplitude must be used as part of the scaling applied to the input
+images.  In a mosaic camera, if individual chips are analysed
+independently, the resulting master chip images may require
+re-normalized to place the results on a common, consistent footing.
+
+Beyond the details of the analysis steps, there is the question of the
+choice of input images.  This choice is extremely dependent on the
+implementation for a particular camera, telescope, type of detrend
+image, etc.  The analysis {\em process} should not be designed to make
+strong assumptions about the selection of the input data.  In the IPP,
+the definition of the selection rules is part of the input
+configuration information and the scheduling rules, and can be
+considered outside of the discussion of the analysis commands.  The
+IPP provides a tool, part of the \code{dettools} suite, which examines
+the Metadata Database tables for raw images of the appropriate type to
+select input images based on selection options such as time range,
+filter, camera, chip, exposure type, airmass, exposure time, etc.  In
+the discussion below, we assume that some selection is made with
+\code{dettools}, resulting in a collection of input exposures and
+their corresponding chips.  These lists are placed in tables which are
+then provided as part of the input to the analysis programs below; the
+corresponding images used as part of these inputs are also saved in
+Metadata Database tables as discussed in Section~\ref{ipptools}.  In
+practice, the database tables provide the primary source; the list
+files are constructed from these tables and are simply intermediate
+data sources for the analysis programs.
+
+Another important distinction to clarify in the detrend processing is
+between the detrend {\em run}, the {\em version}, and the {\em
+iteration}.  These issues are discussed further in
+Section~\ref{ipptools}.  Briefly, though, there are the following
+concepts to keep in mind: The detrend {\em run} is a particular
+attempt to construct a master detrend image.  One run defines a
+collection of selection criteria for the initial set of input images.
+The resulting master detrend image is given an identifier, equivalent
+to the exposure ID.  If the same selection criteria are used multiple
+times (eg, for multiple experiments on the analysis recipe used to
+construct the image), the same detrend ID may be used for multiple
+detrend runs.  In this case, each new detrend run is given a different
+{\em version} number, equivalent to the version numbers used to track
+the science analysis passes.  For the detrend image construction, this
+concept must go one level further, however.  In order to produce a
+single validated master detrend image, it is necessary in general to
+produce multiple intermediate attempts.  The intermediate master
+frames are applied to the input images; the statistics of the residual
+images are then used to select a subset of the input images, rejecting
+poor quality or deviant images.  This processing is a form of
+image-level outlier rejection, and is particularly necessary for input
+images which result from observations of the sky (eg, twilight flats
+or night-time fringe frames); images obtained using stable calibration
+sources may not require this level of iterative processing.  This type
+of analysis can also be used to determine if a new master frame is
+needed (all input images internally consistent, but deviant from the
+current best master) or if conditions are unacceptable to produce a
+new master (all input images mutually inconsistent).  In order to
+track these multiple analysis passes, the IPP infrastructure assigns
+iteration numbers to the data products associated with a particular
+detrend run and version.
+
+One final point to address is the issue of the validity domain of a
+detrend image.  The end result of the detrend run is a master detrend
+image of a particular type, e.g., a master r' flat-field image for
+GPC-1.  As a result of the input selection criteria, the resulting
+master detrend frame will have a primary domain of validity, which
+consists of a particular camera, telescope, set of chips, and which
+may include a time range, filter, airmass range, etc.  The primary
+domain of validity defines those images which would be best processed
+with the particular master detrend image.  Beyond this primary domain
+of validity is a wider, relaxed domain of partial validity.
+
+Clearly, a SkyProbe flat-field image would be inapporiate in all
+context to be applied to a GPC image.  Likewise, a GPC-1 $r'$ flat would
+be inappropriate for a GPC-1 $g'$ science image.  However, in some
+circumstances, it is appropriate or desireable to apply a detrend
+frame to an image outside of its primary domain of validity.  For
+example, if a master flat-field image was produced using input images
+from a certain week, an image from a different week may be viewed as
+outside the primary domain; however, for some experiments or because a
+flat-field in the appropriate time range could not be produced, it may
+be acceptable to apply the out-of-date flat-field image to the science
+image.  In general, any image of the appropriate type, camera, filter
+(if a valid construct) and detector is in the partial domain of
+validity as a detrend image with those same values.  The detrend
+creation system assigns a primary domain of validity to the masters
+which it creates; it is the choice of the analysis routines to apply
+images from a more relaxed domain, if necessary or desired, or to
+require the primary domain and yield an error if it is not available.
+
+Below we give the series of analysis commands used to construct a
+master detrend frame.  In this example, we construct a master $r'$
+flat-field image for GPC1, using the detrend images for week 050.  In
+practice, we will likely build detrend frames on a nightly basis, but
+the choice of timescale will depend to some extent on the observing
+process and the stability of the system.  In this example, we
+construct a detrend ID using the camera, type, filter, and week
+number, though this choice is completely arbitrary.  We also
+illustrate the example for one of the input images with exposure ID
+7654321f and for chip 24.  In this example, this is the third time
+this image has been used for the analysis, thus the processing results
+for this frame/chip are given a version number of 02.
+
+\begin{verbatim}
+dettools -define [selection criteria] -detID GPC1.flat.r.w050.00
+
+ppImage -recipe MKDET.PROCESS
+        -infile   file:/data/2006.11.01/7654321f/7654321f.24.fits
+        -output   file:/data/2006.11.01/7654321f/7654321f.24.PC.02.img.fits
+
+ppMerge -recipe MKDET.STACK
+        -inlist   file:/data/detrend/w050/GPC1.flat.r.w050.v00.n00.c24.list
+        -output   file:/data/detrend/w050/GPC1.flat.r.w050.v00.n00.c24.fits
+
+ppNorm  -recipe MKDET.NORM
+        -inglob   file:/data/detrend/w050/GPC1.flat.r.w050.v00.n00.c??.fits
+
+ppImage -recipe MKDET.RESID
+        -infile   file:/data/2006.11.01/7654321f/7654321f.24.PC.02.img.fits
+        -output   file:/data/2006.11.01/7654321f/7654321f.24.RS.06.img.fits
+        -thumb    file:/data/2006.11.01/7654321f/7654321f.24.RS.06.thm.fits
+        -binned   file:/data/2006.11.01/7654321f/7654321f.24.RS.06.bin.fits
+
+ppImage -recipe MOSAIC
+        -inglob   file:/data/2006.11.01/7654321o/7654321o.??.RS.06.bin.fits
+        -outjpeg  file:/data/2006.11.01/7654321o/7654321o.XX.RS.06.bin.jpeg
+
+ppImage -recipe MOSAIC
+        -inglob   file:/data/2006.11.01/7654321o/7654321o.??.RS.06.thm.fits
+        -outjpeg  file:/data/2006.11.01/7654321o/7654321o.XX.RS.06.thm.jpeg
+
+ppImage -recipe MKDET.MOSAIC.
+        -inglob   file:/data/detrend/w050/GPC1.flat.r.w050.v00.n00.c??.fits
+        -outjpgt  file:/data/detrend/w050/GPC1.flat.r.w050.v00.n00.thm.jpeg
+        -outjpgb  file:/data/detrend/w050/GPC1.flat.r.w050.v00.n00.bin.jpeg
+
+\end{verbatim}
+
+Again, this data illustrates the use of files in the UNIX file system;
+the substitution of \code{neb:} for \code{file:} will inform the
+programs to retrieve the file names via Nebulous.  In this example, we
+use output names for the intermediate images which are equivalent to
+those used for the science processing; the version numbers used for
+these data products are sequential over all detrend runs which use
+those input frames.  Also, for the detrend image products, we have
+added the 'v', 'n', 'c' tags to clarify which of the two-digit numbers
+represents a version (v), an iteration (n), and a chip ID (c).  Note
+that this sequence of analysis steps makes heavy use of
+\code{ppImage}, with different choices of the recipe and the output
+options to change the behavior somewhat.  However, all of the uses of
+\code{ppImage} represented here are consistent with the primary
+responsibilities of \code{ppImage}: read in the file or mosaic image
+into the correct level of the image hierarchy, perform a detrend
+analysis (including rebinning in this category), re-structure the
+collection of image arrays as described by the recipe, write out the
+image in the desired format.
+
+\section{IPPTools}
+\label{sec:ipptools}
+
+PanTasks is the IPP tool which manages the sequencing of data analysis
+steps and, with the related tool `PControl', distributes the data
+processing across a cluster of computers. However, by itself, PanTasks
+does not determine the organization of data or the analysis sequencing
+for a particular pipeline.  This level of information is contained
+within specific PanTasks scripts.  To use the tasks defined by a set
+of PanTasks scripts, additional helper programs are needed.  This
+section discusses these programs and the PanTasks scripts used by the
+IPP.
+
+IPPTools is a collection of programs, Metadata Database table
+definitions, and PanTasks scripts used to define the actual data
+organization and the sequencing of operations by the IPP.  Within the
+IPP, the Metadata Database is used to store the analysis state, as
+well as result processing data points.  This section discusses the
+tasks needed to define each of the IPP analysis stages (Phase 1-4,
+detrend creation, etc) and examines the relevant MDDB tables.  
+
+\subsection{Persistent vs Ephemeral State in PanTasks}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.01.ps}
+\caption{\label{queues} PanTasks queues and MDDB tables}
+\end{center}
+\end{figure}
+
+The IPP, a fairly complex analysis system, uses PanTasks to select
+jobs, distribute them to the cluster, and harvest the results.  It
+uses the Metadata Database to record the results of a given analysis
+step, and to determine which jobs must be performed when.
+
+There are some subtleties in the interaction between PanTasks, the
+Metadata Database tables which store the system state, and the jobs
+which are currently being performed.  There is a choice to be made
+between rigorously maintaining the system state in the Metadata DB at
+all times or keeping an intermediate set of state tables.  Keeping the
+exact system state in the Metadata DB tables would require many extra
+queries to/from the database and may introduce additional latencies
+which are undesirable.  This is because any attempt by PanTasks to
+initiate a new job would require PanTasks to mark the corresponding
+data item in the Metadata DB (the item which acts as the trigger) with
+a `pending' state, and then mark it again as `done' when the job
+actually completes.  This also has the drawback that, if the system
+crashes (eg, hardware failure), some initial process would be required
+on start up to find all Metadata DB items which are in the `pending'
+state (examining all possible items which can be in such a state) and
+reset them to the `new' state.
+
+We implement an alternative in which PanTasks maintains an internal,
+ephemeral stack of the pending jobs, and only updates the system state
+entries in the Metadata DB when jobs are actually completed.  In this
+scenario, as far as the Metadata DB tables are concerned, data items
+transition only between a `new' and a `done' state.  Any jobs which
+are pending when the system crashes or the power is lost are simply
+dropped, and will be automatically re-constructed when the system
+restarts.  In this paradigm, no intermediate operation state is saved,
+and no partially completed job can be recovered.  Since the IPP is
+defined in terms of a fine granularity, with jobs lasting no more than
+30 - 120 seconds, crashes under this model will not have a large
+impact on the data processing.
+
+Figure~\ref{queues} illustrates this ephemeral vs persistent state
+information and the interrelation between the metadata tables and
+PanTasks.  The left-hand portion of the diagram illustrates the
+recommended interaction between the metadata database tables and
+PanTasks' internal queues.  Some table in the metadata database
+defines a list of data items which are to be processed by some
+analysis job.  PanTasks uses a two-step approach to define the
+analysis jobs based on this list.  First, one task queries the MDDB
+for a list of pending items, adds the returned items to an internal
+PanTasks queue.  The process of adding the elements to the queue is
+defined so that only unique items are added: already existing items
+are skipped.  The entries in the queue consist of the data items of
+interest and an internal temporary state.  At first, this would be
+`pending'.  A second tasks pops `pending' entries one-by-one from this
+internal queue, submits a job based on the entry, and sets the
+temporary state in the internal queue to `running'.  The internal
+state is needed to prevent PanTasks from re-submitting a job for the
+same data item before the first job is done or assessed.  Since the
+job make take an arbitrary amount of time, PanTasks requires a
+mechanism to remember which data items it has already submitted.  When
+the job eventually completes, the metadata database table is updated
+noting the completion.  This may be done either by the job itself or
+by PanTasks as part of the job exit rules.  In addition, the state of
+the entry in the queue can be set to either `done' or the entry can be
+simply removed from the queue.
+
+
+The purpose of this interaction is to maintain the temporary state
+information within non-persistent elements of PanTasks rather than
+using the metadata database tables to store this information.  This
+concept has two advantages.  First, PanTasks internal queues are in
+memory and relatively small, thus interfacing with them is quite fast
+for PanTasks -- this should reduce the system latency.  Second, by
+keeping this information non-persistent, the system responds correctly
+to stopping and restarting PanTasks.  Any jobs which have not been
+completed will not be marked in the database, and will be restarted
+naturally by PanTasks.  The alternative, of writing a temporary state
+marker in the database would require PanTasks, on startup, to
+initially clean all database tables of these temporary state markers.
+
+The right-hand portion of the diagram illustrates this process using
+the process of copying the images from the summit as an example.  The
+metadata database table of interest in this case is the list of
+pending images, with entries supplied by a job which queries the
+summit data systems.  The job which is actually performed is a remote
+copy of the image file from the location specified by the summit data
+system to the appropriate location within the IPP Image Server
+(Nebulous).  (As an alternative to the above, the `pending images'
+table may be part of the summit database system, and the `get images'
+command may query the summit directly.  In this scenario, the `copy
+image' command reports to the summit data system that an individual
+image file has been copied.)
+
+In the rest of this document, the use of PanTasks internal queues to
+manage the temporary data states is glossed over and assumed part of
+the tasks defined in the process.
+
+
+\subsection{IPP Pipelines Overview}
+
+\tbd{add the use of Q/A measurements from the IPP CDR Response
+document}
+
+The IPP as a whole performs all of the image analysis functions
+required by the Pan-STARRS telescopes, including images from the full
+Gigapixel camera (or cameras), the test camera TC-3, and the SkyProbe
+camera.  The IPP is designed to be very flexible, with instrument
+specific details isolated in configuration files associated with the
+different cameras known to the system.  As a result, the organization
+of the top level analysis infrastructure must be sufficiently general
+that a wide range of cameras can be accomodated.  We have a few
+general principles regarding constraints on the data to be processed
+which are used to guide the IPP design and developement:
+
+\begin{itemize}
+\item {\bf Camera Focal Plane Hierarchy} The IPP analysis programs
+  assume that the images to be processed are obtained by a camera
+  which can be represented by our Camera Focal-Plane Hierarchy of data
+  structures.  This hierarchy is discussed in detail in the Modules
+  SDRS, and defines a top-level {\em Focal-Plane Array (FPA)}, which
+  may contain 1 or more {\em Chips}, each of which may contain one or
+  more {\em Cells}.  An {\em FPA} is identified as having a single
+  optical system feeding photons to the detectors.  A {\em Chip} is
+  identified as a unit of data all deriving from a single detector
+  (piece of silicon), while a {\em Cell} is identified as a collection
+  of pixels read out as a continuous cartesian grid.  Finally, a
+  single collection of data from an {\em FPA} may include multiple
+  {\em Readouts} from any or all of the {\em Cells}.  
+
+\item {\bf Exposures vs Groups} The processing presumes that the data
+  is organized into {\em exposures} and exposure {\em groups}.  An
+  exposure represents the data from a single FPA, with the possible
+  subdivision of the exposure into multiple readouts for some or all
+  of the cells.  Exposure {\em Groups} are any group of exposures
+  which are related together in some way; the definition of the {\em
+  Groups} may be provided by the observers, or they may be derived
+  from the characteristics of the exposures.  The use of a particular
+  {\em group} depends on the context of that group.  A few examples of
+  exposure groups:
+
+  \begin{itemize}
+  \item a dithered sequence of exposures to be stacked for cosmetics
+  and improved signal-to-noise.
+  \item a twilight flat-field sequence.
+  \item all images of the same filter within a 10 degree region to be
+  used to construct an sample astrometric reference.  
+  \end{itemize}
+
+\item {\bf Image Files (imfiles) vs Exposures}  Any single exposure
+  may consist of a number of different data files.  The number of {\em
+  imfiles} for a given exposure will depend on the camera, as will the
+  data organization within those image files.  Also, a particular
+  camera will supply files corresponding to one of the particular
+  Focal-Plane Hierarchy elements.  The IPP analysis must be able to
+  interpret the incoming data correctly.
+\end{itemize}
+
+As discussed elsewhere, there are several major types of analysis
+performed by the IPP.  For the purposes of data organization and
+parallel processing efficiencies, we have identified the following
+divisions of the analysis tasks.  These will be discuss in much more
+detail below.
+
+\begin{itemize}
+\item {\bf Science Image Analysis} : This represents the analysis
+  performed on the images obtained by the telescope, and generally
+  performed in real-time, night-by-night.  The science image analysis
+  tasks are further subdivided as follows:
+
+  \begin{itemize}
+  \item {\bf Phase 1} : The full focal-plane array is examined quickly
+  to determine an initial astrometric calibration.  In this step, the
+  OTA guide stars may be used as the astrometric reference; if none
+  are available, predicted bright star positions are examined.  This
+  step is only used for mosaic images, and may be skipped if no guide
+  stars are available {\em and} the astrometric calibration for the
+  telescope / camera is reliable (better than 10 arcseconds).
+
+  \item {\bf Phase 2} : Each image file is analysed independently: the
+  image is detrended (bias, dark, flat, fringe, etc), sources are then
+  detected to a modest level, improved astrometric calibration is
+  performed.
+
+  \item {\bf Phase 3} : The collection of sources measured from all of
+  the image files for the camera are used to determine a global
+  astrometric, and possibly photometric, solution for the exposure.
+  This step is only required for mosaic cameras.
+
+  \item {\bf Phase 4.1} : An exposure group consisting of images
+  obtained in a specific region of the sky are merged together.  In
+  this step, the images are first warped to a common pixel grid, defined by
+  the static sky images.  The collection of images are then used to
+  construct a single, cleaned image by rejecting the outliers from the
+  source images in the stack.  The corresponding static sky pixels are
+  then used to construct a difference image from the resulting stack.
+
+  \item {\bf Magic} : In this step, the difference images are examined
+  to find the trailed images introduced by artificial satelites.
+  These so-called {\em streaks} are excised from the difference
+  images, as well as all of the source images which were used to
+  generate the difference images; the public data sources are updated
+  with the precise, correct time.  Note that this step requires that
+  separate difference images be generated for each of the input
+  images, a step which would be skipped if {\em magic} were avoided.
+  Also note that, until {\em magic} is performed, the publically
+  available time has a limited precision (probably $\sim 1$ minute
+  errors).  This step is only necessary in the operational IPP system
+  given the restrictions from the Air Force.
+
+  \item {\bf Phase 4.2} : After {\em magic} the final difference and
+  the final cleaned stacked image are produced and objects in both
+  images are detected.  The difference sources are used to mask the
+  extreme outliers in the cleaned stack, which is then used to update
+  the Static Sky images. 
+  \end{itemize}
+
+ \item {\bf Static Sky Image Analysis} : While the science image
+ analysis is performed as images are availablef, the static sky image
+ analysi occurs on a very different timescale.  In steady state, the
+ full static sky analysis will take place over the course of a full
+ year.  At any given time, the portion of the sky corresponding to the
+ location of the sun will be under-going the analysis.  In practice,
+ for PS-1, the static sky is produced in a somewhat different fashion
+ than in the steady-state model.  In PS-1, the different survey
+ strategies introduce very different update rates for the static sky.
+ At one extreme, the AP Survey will not have enough data for a
+ complete static sky analysis until nearly 22 months after the survey
+ begins.  At the other extreme, the deep survey, which observes a much
+ smaller portion of the sky, may best be analysed quite frequently.
+ These details are part of the science guidelines of the PS-1 surveys,
+ and are beyond the scope of this document.  Rather, the IPP Static
+ Sky Image Analysis must provide the capability of defining the static
+ sky analysis in a flexible and dynamic fashion.
+
+\item {\bf Basic Detrend Creation Analysis} : The analysis of most of
+  the detrend data is grouped together in a common analysis stage.
+  The differences between the analysis of the bias, dark, flat, and
+  fringe images is primarily one of how the input images are
+  pre-processed, what statistic is used to characterize a given input
+  image, how the input images are scaled before being combined, and
+  what normalization is applied to the resulting image.  All of these
+  types of detrend images can thus be processed with a single analysis
+  pipeline which is made aware of these minor differences.  This stage
+  is never the less fairly complex, and as a result is subdivided into
+  several compenents, as discussed below.
+
+\item {\bf Other analyses} There are a number of other tasks which the
+  IPP must perform that are not well-defined by the different analysis
+  types discussed above.  Some analysis tasks are not automatically
+  triggered, and are thus outside the scope of this document; these
+  are the tasks which are more properly considered as research
+  projects than analysis systems.  The other important automatic tasks
+  are:
+  \begin{itemize}
+    \item {\bf Summit Copy} : In this stage, the data source or data
+    sources are queried for new exposures and image files, which are
+    then copied to the IPP data area.  This stage also includes the
+    copying of other metadata which are not included in the image
+    files.
+    
+    \item {\bf Image Classification} : new images which are introduced
+    to the IPP are examined by this analysis stage and placed in the
+    appropriate table for processing.  This step includes a small
+    amount of accumulating statistics about the images.
+
+    \item {\bf Data File management} : a few tasks are necessary to
+    monitor and maintain the clustered storage system.  These tasks
+    include the automatic duplication and deletion of different types
+    of files from Nebulous, the file storage archive.  This also
+    includes automatic redistribution of machine assignments as
+    hardware is added or removed from the system.  This collection of
+    tasks also includes monitoring of system parameters to alert
+    people in case of dangerous hardware situations.
+
+    \item {\bf Irregular Calibration Data} certain types of
+    calibration information is extracted on different intervals from
+    the more regular detrend images.  These types of calibration data
+    include improved telescope pointing models, astrometric
+    calibrations, photometric calibrations, flat-field correction
+    frames.
+  \end{itemize}
+\end{itemize}
+
+\subsection{Tables, Tasks and Tools}
+
+The following sections discuss the database tables, the tasks within
+PanTasks, and the collection of programs used by PanTasks to examine
+and manipulate the state tables.  These later programs do not, in
+general, perform any in depth analysis; instead they perform actions
+such as selecting from one table images ready for analysis in a
+following processing step.  This collection of tools is grouped under
+the name of the {\tt ippTools}, and consists of a separate tool for
+each of the different major analysis steps.
+
+The {\tt ippTools} make use of {\em glueforge} to simplify the
+management of the database table schema.  Glueforge provides a single
+mechanism to generate a collection of C data structures, database
+tables, database access APIs, and I/O routines from a simple table
+description configuration file.  All APIs generated by {\em glueforge}
+for the same type of interaction have common naming schemes.  This
+technique has several important advantages.  It makes the writing of C
+database interactions very quick and easy.  It also makes it easy to
+modify the database schema without disrupting the software
+development.  Finally, it provides a simple, self-documenting source
+for data structure of multiple types which can be shared between
+programs or platforms.
+
+Within the following diagrams, we illustrate the database tables used
+to track the state of the IPP.  We also show the commands provided by
+{\tt ippTools} to connect the tables.  Finally, we show the IPP tasks
+which initiate the different analysis steps.  The following set of
+diagrams uses several consistent features.  The blue-and-grey
+rectangles define the metadata database tables.  The blue section
+contains the table name, while the grey section lists a minimal subset
+of the table columns.  The ellipses represent programs (or program
+portions in some cases) executed by PanTasks.  The blue filled
+ellipses represent the {\tt ippTools} commands which are executed
+locally on the computer hosting PanTasks.  The grey-blue ellipses
+represent the commands executed on the parallel cluster, monitored by
+{\tt pcontrol}.  The green ellipses represent commands executed by
+hand for testing and manual intervention.
+
+In most of the analysis tasks, we use a two-table approach to the data
+in order to avoid excessive latencies.  One table is used to track
+quantities which are still pending for a particular stage.  When the
+analysis is completed, these items are moved from the 'pending' tables
+to corresponding 'done' tables.  Although this introduces a somewhat
+higher number of tables and complexity, it will avoid the system from
+slowing down as the number of data items grows with time.  The pending
+tables are searched repeatedly by the {\tt ippTools} programs as they
+attempt to select new data of interest.  In contrast, the done tables
+are searched much less frequently.  
+
+\subsection{Summit Copy Tasks}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.02.ps}
+\caption{\label{pcopy} Summit Copy Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{pcopy} illustrates the MDDB tables used to copy data
+(images and metadata tables) from the summit.  The left-hand portion
+of the diagram shows the tables involved in copying images from the
+summit system.  The table of pending image files lists the URLs of the
+individual image files available for transfer, along with their
+associated exposure ID and the camera which generated the image.  Two
+other entries assist in interpreting the file: the class and the class
+ID.  The final entry in this table is the current copy state of the
+file, can have the value of `ready' or `copied'.
+
+The class defines the data grouping represented by this image file and
+may have values of: FPA, Chip, Cell.  This value indicates that the
+provided image file represents the specified portion of the camera
+FPA.  If the value is FPA, the file represents data from a complete
+FPA, though the file may contain pixel data in multiple extensions or
+other groupings to be identified later.  If the value is chip, the
+file contains only data for a single chip, presumably of multiple
+chips available, and equivalently for Cell.  Further discussion of the
+FPA image hierarchy is given in the IPP documents (eg, Modules SDRS).
+The class ID gives the identifier used to name the class level
+corresponding to this file.  This value is necessary to make decisions
+on how to copy the data based on the chip / cell before the data is
+available to IPP components.  Table~\ref{classes} lists likely values
+for the class and class ID for some common cameras.  The system
+described is sufficiently flexible to allow us to transfer the GPC
+images by cell if we eventually decide that is more efficient.
+
+The copy process copies the file from the given URL to the appropriate
+IPP node and adds an entry to the table of new image files, consisting
+of the same information as the pending image file table, though with a
+new value for the URL.  This URL may be an explicit filename, a
+reference to an entry in the image server, or a web address, or
+located on the image server (marked with file:, neb:, and http:,
+respectively).  (TBD: other possible file storage types?  perhaps the
+path could be abstracted without going to the level of the image
+server?  eg: ref:DIR0001/file0001.fits might be in a directory which
+is defined in a table of directories.) After an image file is
+successfully copied, the corresponding state in the `pending chip'
+table is updated from `ready' to `copied'.
+
+\begin{table}
+\begin{center}
+\caption{Camera and Data Classes\label{classes}}
+\begin{tabular}{llll}
+\hline
+\hline
+camera   & class  & classID \\
+\hline
+GPC	 & chip   & chip02 \\
+skyprobe & fpa 	  & sp01 \\
+Megacam  & fpa	  & MegacamSpliced \\
+Suprime	 & chip	  & chip0 \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+The right hand portion of this diagram illustrates the process of
+copying a metadata table.  The table of pending tables lists the URLs
+for the tables which are ready, a unique table ID for each table, and
+the table type.  The copy function copies the listed table and uploads
+the data to the IPP version of the same metadata database.  Two
+examples of metadata tables needed by the IPP for the basic image
+processing system are illustrated: the table of new exposures and the
+table of pending matches.  The first lists the exposures which are
+avilable from the summit system, and all represent entries which are
+available from the Image server.  the second represents the matches
+between exposure IDs and chips
+
+\subsection{Phase 0}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.03.ps}
+\caption{\label{phase0} Phase 0 Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{phase0} illustrates phase 0, in which the image files are
+categorised, examined for summary information and basic statistics,
+and moved to the later phase 'pending' tables to trigger further
+analysis.  The command {\tt p0search -pending} examines the `new
+imfiles' and 'new exposure' tables.  It selects images from this table
+which have not yet been examined (state is `new').  These are returned
+to PanTasks, which sends each image file to a separate analysis node
+running the {\tt p0search -update} command.  With this command, the
+file header is examined and relevant metadata is extracted (eg, RA,
+DEC, times, and so forth to be defined later).  The process may also
+select a portion of the image pixel data to determine a rough bias and
+background level.  These statistics, whether derived from the header
+or the pixel values, are placed along with image summary information
+in the `raw image files' table, and the state field of the `new image
+files' table is set to `ready'.
+
+The {\tt p0search -update} command is also responsible for moving the
+exposures to the tables used for triggering the analysis process.  If
+the image class is FPA, the image can be advanced without waiting for
+any other image files.  If the class is Chip or Cell, the process must
+also examine the `new exposure' table for this exposure ID.  The
+number of class files available for this exposure is listed in this
+table.  The process must the select all image files matching the
+exposure ID with state of `ready' and compare the number avalable to
+the number expected.  If the two match, then a new exposure is ready.
+Based on the image type (from the most recently examined image file
+header or new exp table?), the exposure is added to the `raw exposure'
+table for images of that type.  The allowed types are `detrend', (all
+bias, dark, flat images), `object', `focus'(??), etc.  The different
+tables represent different analysis modes.  This process also adds an
+entry to the exp ID / image file match.  This process also adds all
+science (OBJECT) exposures to the P1 exposure table (for mosaic data)
+or the P2 chip table (for single detector data).  These tables are
+used to trigger the Phase 1 and Phase 2 analysis stages.
+
+\subsection{Phase 1}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.04.ps}
+\caption{\label{phase1} Phase 1 Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{phase1} shows the tables involved in running the Phase 1
+analysis stage.  There are paths for exposures to enter the analysis
+automatically from the Phase 0 analysis (arrow on left) or to be added
+manually based on a selection from the raw exposure table.  Exposures
+to be analysed by Phase 1 are added to the P1 exposure table with the
+state `new'.  Exposures may be added multiple times for processing and
+reprocessing. The P1 done exposure table keeps a record of the old
+attempts for debugging and analysis.  Each time an exposure is added
+to the P1 exp table, it is given a new, unique version number,
+allowing the system as a whole to track different analysis attempts.
+This method is used in all of the image analysis stages (and
+extrapolated to iterations in the detrend analysis steps below).  The
+top portion of the diagram shows the use of the command {\tt p1search
+-define} to select and submit an exposure or a group of exposures,
+potentially selected on the basis of a query from the raw science
+exposure table.
+
+The P1 pending exposure table is examined by {\tt p1search -pending}
+to select the new exposures, which are sent to PanTasks.  PanTasks
+initiates a separate analysis job (p1astro) for each exposure, which
+are sent to the parallel processing nodes.  Within the analysis job,
+the chips (image files) associated with the exposure are select from
+the raw image file table.  The analysis examines the contents of these
+files, either extract the guide star information from the image files
+(GS table extension) or searches for and centroids the pixels on
+appropriate bright stars.  The analysis results in astrometric
+calibration terms which are written to the astrometric calibration
+file for this exposure.  The location of the astrometric calibration
+file and the statistics of the measurement are written back to the P1
+exposure table.  The images associated with exposures which are
+successfully processed by P1 are then added to the P2 image table,
+which is used to trigger the Phase 2 analysis.  This last step is
+performed by the command {\tt p1search -done}, which is executed
+regularly to search for completed Phase 1 jobs.
+
+\subsection{Phase 2}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.05.ps}
+\caption{\label{phase2} Phase 2 Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{phase2} shows the tables involved in running the P2
+analysis stage.  There are paths for images to enter the analysis
+automatically from the P1 analysis (arrow on left) or to be added
+manually based on a selection from the raw exposure and raw image file
+tables.  Image files to be analysed by Phase 2 are added to the P2
+pending imfiles table with the state `new'.  When images are added to
+this table, a single entry is also added to the P2 exposure table
+listing the P1 and P2 versions for this exposure.  These version
+numbers must be integers starting with 1.  If this image did not have
+a P1 analysis, the P1 version is set to 0.  Exposures may be added
+multiple times for processing and reprocessing. The P2 image table
+keeps a record of the old attempts for debugging and analysis.  As
+with P1, each time a collection of associated images from an exposure
+is added to the P2 image table, it is given a new, unique version
+number, allowing the system as a whole to track different analysis
+attempts.  Note that these version numbers are unique for each {\em
+exposure} processed by Phase 2, not just for any image file.  The top
+portion of the diagram illustrates the behavior of the commands {\tt
+p2search -define} and {\tt p2search -quick}.  The first may be used to
+re-submit the images for an exposure or a group of exposures,
+potentially selected on the basis of a query from the raw science
+exposure and raw image file tables.  The second version sends images
+files directly to PanTasks for processing; these entries will not be
+included in the processing tables, and is used only for testing
+purposes.
+
+The P2 pending image table is examined with the command {\tt p2search
+  -pending} to select the `new' images.  These images are used by
+PanTasks to generate P2 analysis jobs, running the analysis command
+{\tt ppImage}.  The P2 analysis uses the input url to find and load
+the image file.  The url may be a file on disk, an entry in the image
+server, Nebulous, etc.  The master detrend images matching the
+specific science image and the conditions are selected by examining
+the table of master detrend frames.  The specific detrend image files
+are selected by using the master detrend ID to select the matching the
+entries in the table of master detrend files.  After the analysis, the
+output image, mask, and FITS table of objects, including the
+astrometry calibration, are written back to the P2 image table, along
+with summary statistics from the P2 analysis.  The state is also
+updated (to `done').
+
+The completed images are examined by the command {\tt p2search -done},
+and when all image files for a single exposure are completed, this
+command migrates them to the P2 done table.  This process is also
+responsible for populating the P3 pending tables so exposures may be
+processing by Phase 3.
+
+\subsection{Phase 3}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.06.ps}
+\caption{\label{phase3} Phase 3 Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{phase3} illustrates the tables and commands involved in
+the Phase 3 analysis.  The P3 pending exposure table lists the
+exposure ID, the P3 analysis version, the P2 analysis version to be
+used as input to this P3 analysis, and the recipe to be used.  The
+command {\tt p3search -pending} extracts exposures from this table and
+provides them to PanTasks for processing.  PanTasks launches a Phase 3
+analysis (the command {\tt psastro}?) for each exposure.  In this
+analysis, the P2 exposure and image tables are used, in conjunction
+with the P2 version information, to select the P2 output measured
+objects and the astrometric calibrations from P2 and P1.  These
+measured objects are matched with the reference catalog objects, and
+calibrated astrometry {\em and eventually photometry} is produced for
+the full exposure.  The location of the resulting astometry
+calibration table is stored back in the P3 exposure table.  If the
+recipe file specifies, the 2-D photometric and background / fringe
+corrections may also be performed at this stage.  Since these analyses
+require reference data, the recipe may also be used to skip these
+analysis if such reference data is unavailable or unreliable.  At the
+end of Phase 3, the objects from the exposure are inserted into the
+photometry database (this is not shown).
+
+The astrometric calibration portion of Phase 3 is principally needed
+for a mosaic camera.  For single-chip cameras, Phase 3 may be used to
+perform the photometric calibration and simply pass the astrometric
+results along to the output file to be listed in the P3 exposure
+table.  In this way, later stages of the analysis (ie, Phase 4) can
+use the P3 exposure table as input for all cameras, even if all the
+funcionality of Phase 3 is not necessary for that camera.  This would
+be the case for the skyprobe camera, for example.
+
+\subsection{Phase 4}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.07.ps}
+\caption{\label{phase4} Phase 4 Tasks}
+\end{center}
+\end{figure}
+
+At the end of Phase 3, the images are ready for Phase 4.  The Phase 3
+diagram shows the output line adding the exposures to be processed by
+Phase 4 to a Phase 4 table.  However, this line is just for
+illustration purposes.  The rules for initiating a Phase 4 run are
+somewhat more complicated than those for running Phases 1-3.  Groups
+of exposures which have an appropriate overlap should be chosen for
+the Phase 4 analysis.  In the steady-state period of PS-4, it may be
+straightforward to choose the exposure groups: they would simply be
+the exposures obtained nearly simultaneously by the four separate
+cameras.  The circumstance for PS-1 will be much more complicated (and
+even PS-4 will probably be more complex than it seems at first
+glance).  For example, in PS-1, we will not have a static sky for most
+of the AP Survery.  In this circumstance, we cannot run P4, at least
+until after the complete AP Reference catalog is built, and
+potentially all exposures re-run through Phase 3.  It may be useful
+for the AP Survey data to split the Phase 4 analysis into two stages:
+image combination and image differencing.  It may even be the case
+that only the combination portion of Phase 4 is performed on the AP
+Survey data.
+
+More generally, the image groups selected for Phase 4 analysis may be
+chosen on the basis of a query of the AP Database (DVO) with some
+rules.  
+
+\tbd{Phase 4 run can be defined by selecting an observation group, a
+  set of exposures, or a set of rules related to a spatial region (eg,
+  region, time range, and filter}.
+
+\tbd{Phase 4 discussion (and diagram) needs more work}
+
+\subsection{Analysis Version and Recipes}
+
+Note that each of the stages P1-P4 refer to the processing version
+from the previous stage.  This allows the processing stage to request
+the correct version of the results from the previous stage, and makes
+it possible to run and re-run the analysis at any stage without
+deleting the earlier results.  As different analysis attempts are
+performed for a given image, the versions branch out.
+
+Also note that at every stage, the entries include a recipe
+identifier.  This is used to select the analysis recipe which should
+be used for this version.  By default, the recipe should be set to the
+current best recipe (use a default name for this?).  This feature
+allows the user to run test analyses with variations on the recipe
+without altering the analysis system.  For example, it is possible to
+use a different flat-field set by specifying alternate rules for the
+flat-field selection in a recipe file.  If it is necessary to run the
+P1-P3 analysis with the raw master flats, for example, the user simply
+defines that selection in the recipe file and submits the images of
+interest to P1 (or P2, etc), with the corresponding entry for the
+recipe.
+
+The recipe file may also be used to specify alternative analysis paths
+and desitinations.  For example, it is not necessary that all analysis
+stops with P4: the recipe file may be used to halt the analysis at P2
+or P3.  In addition, the recipe file may be used to specify an
+alternative destination for the output results.  For example, to
+generate the photometric flat-field correction frame from a collection
+of dithered images, the user may not want the photometry results in
+the main DVO database.  By using the recipe to set an alternative DVO
+database target, and by specifying the use of the raw master flat
+rather than the corrected one, the analysis of the dithered images is
+kept isolated from the other photometry database results.  The
+resulting photometry may be used to construct the new, corrected
+flat-field images, and the processing of the same images using the new
+flat-field images may be sent to the master DVO database.  
+
+\subsection{Basic Detrend Creation}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.08.ps}
+\caption{\label{detrend} Detrend Creation Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{detrend} illustrates the tables needed for the generic
+detrend construction process, using the flat-field construction as an
+example.  This diagram is somewhat more complex than the preceeding
+versions.  In this diagram, both single jobs and multiple jobs are
+represented by the process elements (the blue ellipses).  In some
+cases, more that one task will be needed to perform the function
+illustrated by a single process task.  The complexity of this diagram
+is enhanced by the need for multiple iterations and both single chip
+and full mosaic processing.  At the moment, the distinction between
+mosaic and single chip cameras is not specifically discussed.
+Finally, the triggers which initiate a specific detrend analysis are
+glossed over.
+
+The detrend analysis is initiated by choosing a type of detrend image
+to be constructed and by specifying the criteria which will be used to
+select the input raw detrend frames for the construction.  For
+example, these criteria could specify that all twilight flat images
+over a certain period of days, perhaps with restrictions on the flux
+levels or the time-from-sunset of the images.  The detrend analysis
+run is given an ID (det ID) which will also be used to identify the
+resulting master detrend frame.  
+
+Given the definition of a master detrend run, the input exposures are
+selected from the raw detrend exposure table, and written to the input
+detrend exposure table.  In the next step, the corresponding image
+files are selected from the table of raw image files.  Since there
+will be a different set of input raw images for each attempt at
+creating a master detrend image, and since any given attempt may use
+some of the same input images as any other attempt, a separate table
+of input raw images is constructed.  
+
+Each of the input raw images may be pre-processed before it may be
+used to construct the detrend frame.  For example, the input
+flat-field images should (probably) be dark- and bias-corrected before
+they are stacked.  The information about these input processed images
+is written to the input images table.  If no processing is needed,
+this step simply copies the appropriate information to the table, and
+points back to the raw image, rather than a processed version.  
+
+The input processed images are combined (stacked) to create a master
+detrend image for the particular data element defined by the image
+class (chip/cell/fpa).  At this stage, not all input images should
+necessarily be included in the stack.  If residual statistics have
+been measured for the input images (say, using a prior stack), then
+some of the input image may be excluded.  The table of residual images
+is used to guide this process.  The information describing the
+resulting master image is written to the master images table.  
+
+The statistics of the master detrend images must examined so that any
+necessary renormalizations may be performed.  For example, after
+stacking the individual flat images, the resulting stacks must be
+renomalized to account for the different ranges of input image fluxes.
+This analysis is least-squares solution in which an appropriate scale
+is determined for each input exposure and a separate gain is
+determined for each of the chips or cells in the camera.  This
+analysis can only performed after all image stacks (ie, for all chips)
+have been constructed.  The resulting information is written to the
+table of master detrend frames.  
+
+Once the master detrend is constructed, the master detrend images may
+be used to construct residual images for each of the input images.
+These residual statistics, as well as the locations of the residual
+images and other related data products (jpeg thumbnails?) are written
+to the residual image table.  Note the red arrow which by-passes the
+stack construction and merge steps and skips directly to the residual
+analysis.  In some cases, it may be useful to have the input images
+confronted with an existing detrend image, and the resulting residual
+values used to guide the rest of the process.  For example, in the
+flat-field analysis, applying an earlier flat can result in a very
+good first-pass rejection of poor input images.  The logic to make
+this leap must be part of PanTasks, since each of the individual
+blocks represent complete processing jobs.
+
+Finally, the residual statistics from the complete mosaic (all input
+images, all chips) are used to assess the quality of the newly
+constructed master detrend image, and to potentially modify the
+selection of input images.  This latter process is performed by
+marking the state of the residual images from this iteration.  The
+stacking process always examines the state information for the
+residual images from the previous iteration, if it exists, when
+constructing the master stack.  Once a master detrend frame has been
+judged of high enough quality, the state of the entry for the frame in
+the master detrend frames table is set to an appropriate value to tell
+the other routines that this image should be used as a master detrend.
+The exact choice of which master detrend frame is used for a given
+science image depends on the recipe along with information such as the
+time period used or the conditions used.
+
+Note that, although this discussion focuses on the construction of
+flat-field images, the same structure should be capable of
+constructing the biases, dark, fringes, etc.  In some cases, as noted
+above, the `process' stage is a null operation.
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.09.ps}
+\caption{\label{detprocess} Detrend Creation : Process Tasks}
+\end{center}
+\end{figure}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.10.ps}
+\caption{\label{detresid} Detrend Creation : Residual Tasks}
+\end{center}
+\end{figure}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.11.ps}
+\caption{\label{detstack} Detrend Creation : Stack and Norm}
+\end{center}
+\end{figure}
+
+\section{Interfaces}
+\label{sec:interfaces}
+
+\subsection{Internal Interfaces}
+
+Internal interfaces consist of interactions between the analysis
+scripts and the IPP Metadata Database, Image Server or AP Database.
+There are also interfaces between the IPP Scheduler, Controller, and
+the Metadata Database.  
+
+The science and calibration image processing pipelines make requests
+for images from the Image Server, metadata from the Metadata Database,
+and push their results back onto the Image Server and Metadata
+Database.  The Scheduler specifies analysis tasks and sends them to
+the Controller, and determines the next action based on the contents
+of the Metadata Database.  The various subsystems specify the API for
+client / server interactions, and are discussed in their individual
+section.  Commands will be sent using either text-based commands, SOAP
+or an equivalent protocol.  The format of the exchanged data may be in
+one of several forms discussed below.
+
+FITS Images will be used to transport images between the components of
+the IPP.  The Static Sky FITS Images will use standard rectangular
+FITS images and mask values to identify pixels which are included in a
+specific image.  Astrometric transformations will be stored in the
+image headers.
+
+FITS Tables will be used to store and transport tabular data,
+especially large queries from database subsystems.  The Auto-coding
+technique discussed in Appendix~\ref{sec:AutocodeIO} is used to define many
+different table interactions.
+
+SQL queries and C wrappers of SQL queries will be used as the direct
+interface to the databases.
+
+\begin{tabular}{lll}
+{\bf Interface}           & {\bf Data}          & {\bf Format} \\
+ippTools    : PanTasks    & image state info    & psMetadataConfig \\
+PanTasks    : ippTools    & image state updates & ippTools CLI, psMetadataConfig \\
+PanTasks    : ppImage     & image operations    & ppImage CLI, recipe files \\
+ppImage     : psphot      & detrend images      & pmReadout in pmFPA structure \\
+ppImage     : psastro     & detected objects    & pmSources in pmFPA structure \\
+DVO/getstar : psastro     & ref object coords   & FITS Tables \\
+psphot      : DVO/addstar & detection tables    & FITS Tables \\
+psastro     : DVO/addstar & astrometry          & FITS Header \\
+ippTools    : ppImage     & detrend images      & FITS Images, psMetadataConfig \\
+PanTasks    : ppMerge     & image operations    & ppMerge CLI, recipe files \\
+PanTasks    : ppStats     & image info       	& ppStats CLI, recipe files \\
+PanTasks    : ppNorm      & image results    	& ppNorm CLI, recipe files \\
+DVO         : relphot     & DVO database        & FITS Tables \\
+DVO         : relastro    & DVO database        & FITS Tables \\
+psastro     : stac        & astrometry          & FITS Header \\
+psphot      : poisub      & psf model           & psMetadataConfig \\
+psphot      : poisub      & detected stars      & FITS Table \\
+Static Sky  : stac        & astrometry          & FITS Header \\
+
+\end{tabular}
+
+\subsection{External Interfaces}
+
+This subsection describes the interfaces between the IPP and other
+Pan-STARRS systems and the external clients.  The interfaces are
+illustrated in Figure~\ref{fig:overview}.  The details of the IPP
+interfaces with other external subsystems are specified in the ICDs
+between those subsystems.
+
+\subsubsection{Camera}
+
+Images are pulled from the Summit Pixel Server, part of the Camera
+team's purview, via the Data Store mechanism.  The locations of the
+files are specified by the Data Store (see the Data Store SDD,
+PSDC-940-010).  IPP grabs these via {\tt http}.  This interface is
+described in detail by the Camera-IPP ICD (PSDC-940-003).
+
+\subsubsection{OTIS}
+
+The IPP Scheduler receives metadata from OTIS as a collection of FITS
+Tables.  The IPP sends quality assessment information for each FPA and
+major frame as FITS tables.  These metadata tables are exchanged in
+both directions using the Data Store mechanism (PSDC-940-010).  The
+interface is described in detail by the OTIS-IPP ICD (PSDC-940-004).
+
+\subsubsection{MOPS}
+
+Data will be sent to MOPS from the IPP as part of the Phase 4
+analysis.  The data to be transfered include:
+\begin{itemize}
+\item Image Metadata tables - to be transferred as FITS tables
+\item Transient Detections - to be transferred as FITS tables
+\end{itemize}
+This interface uses the Data Store mechanism (PSDC-940-010), and is
+described in detail by the IPP-MOPS ICD (PSDC-940-005).
+
+\subsubsection{PSPS}
+
+Data is sent to PSPS from the IPP on a regular basis.  The data to be
+transfered include:
+\begin{itemize}
+\item Static Sky images - to be transferred as FITS images or
+  FITS triangular image regions.
+\item Postage Stamps - to be transferred as FITS images.
+\item Metadata tables - to be transferred as FITS tables
+\item Detections \& Object associations - to be transferred as FITS tables.
+\end{itemize}
+This interface uses the Data Store mechanism (PSDC-940-010), and is
+described in detail by the IPP-PSPS ICD (PSDC-940-006).
+
+\subsubsection{Other Preferred Client Science Pipelines}
+
+These cannot be completely defined until the Clients are defined and
+their requirements are specified.  The expectation is that the data
+products will be the same as for the MOPS.  The data will be pushed
+from the IPP to the Client Science Pipeline when they are available.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Computer Hardware}
+\label{sec:Hardware}
+
+\subsection{PS-1 Cluster Design}
+  
+The PS-1 IPP computer system is designed as a cluster of 'fat bricks':
+computers with both processing power and large amounts of local disk
+storage.  These computers are large rack-mount boxes with space for
+10s of disks (24 and 36 disk cases are available) and a motherboard
+with two CPUs and two Gig-E ethernet ports.  One set of machines is
+specified for storage and processing of the individual OTAs up through
+Phase 2 (the `OTA nodes'), another set of machines are specified for
+storage of the Static Sky and processing of data from Phase 3 and
+Phase 4 (the `Sky nodes').  Other machines will be necessary to
+support the Metadata DB and the AP DB.  
+
+The IPP PS-1 SRS (PSDC-430-005) specifies the processing throughput
+requirements for the IPP.  Benchmark tests of the IPP processing
+algorithms have been used to drive the design needed to achieve the
+throughput requirements.  The details of this study are presented in
+the IPP Computational Challenge (PSDC-400-006), summarized here.  The
+analysis measures the processing time (excluding I/O) for both Phase 2
+and Phase 4 on an Intel Pentium 4 processor, and expresses the
+processing time in GHz-seconds, under the assumption that a machine
+with the same architecture and twice the processor speed will perform
+the same analysis in half the time.  This is probably a valid
+assumption within a limited range on hardware using the same
+architecture.  Independent tests show that 32-bit Pentium processors
+perform somewhat slower (up to a factor of 2) than equivalently rated
+64 bit Opteron processors.  This discrepancy makes the measured
+numbers somewhat conservative, and compensates for the simplified
+analysis performed.  The benchmarks show that the Phase 2 analysis
+takes 12000 GHz-seconds for a complete major frame (4 FPAs) while the
+Phase 4 analysis takes 7800 GHz-seconds for the same major frame.
+
+The total data I/O required for each processing node, both locally to
+disk and across the network to other machines, has also been measured.
+These numbers in turn depend on whether the data is optimally stored
+on the OTA nodes (raw images matched to their calibration images) or
+if the data are randomized across the storage nodes.  There are also
+differences in the analysis for the number of bits per pixel and the
+number of calibration images used in the processing.  For PS-1, the
+`minimal' data set is appropriate, resulting in a total Phase 2 I/O of
+21 GBs per major frame and a total Phase 4 I/O of 36 GBs.  The
+randomized numbers are used as a conservative estimate, under the
+assumption the network, not local disk access, is the dominant I/O
+bottleneck.
+
+The analysis assumes each CPU (rated at 2.2 GHz) is associated with
+one RAID array (maximum throughput 110 MB/sec) and one network
+controller (maximum throughput 70 MB/s). In this case, given the CPU
+load and I/O throughput above, Phase 2 will require a total of 190
+seconds of I/O and 5500 seconds of processing distributed across the
+cluster.  Likewise, the Phase 4 analysis will require a total of 330
+sec of I/O and 3500 seconds of processing.  Given the 160 seconds
+available per major frame, these numbers imply a total of 63
+processors are needed to keep up with the processing and I/O load.
+
+The other major driver on the IPP PS-1 cluster is the data storage
+requirements.  It is necessary to store the raw images from the entire
+AP Survey, the MOPS Verification Program (MVP) and the IPP
+Verification Program (IVP), and to have storage enough to represent
+the Static Sky by the end of the two year mission.  These storage
+requirements as a function of time are shown in
+Figure~\ref{fig:StorageProfile}.  Based on the PS-1 Design Reference
+Mission (PSDC-230-001), by the end of the second year, the total
+storage requirements for raw images and the Static Sky will be 850 TB,
+along with and an additional 55 TB needed for the AP DB storage
+
+To meet these requirements, the IPP cluster is designed to use fat
+bricks which will be capable of holding 24 disks each.  The 5U / 24
+disk rack mount computer cases are one of the highest density
+solutions currently available.  A 4U / 36 disk box is also available
+and will be considered.  The disk purchases will be staggered in three
+waves.  Before PS-1 goes on the sky, the first 1/3 of the disks (600
+disks total) will be purchased.  Since the lead time for disks is
+fairly short, the purchase will be made only when other portions of
+Pan-STARRS are clearly on a timeline to success.  After 9 months
+(tentatively 2006 September), the next 1/3 of the disks will
+purchased, and the remaining disks 9 months after that (tentatively
+2007 June).  Using conservative estimates of the available disk sizes
+at these purchase dates (400 GB, 600 GB, and 900 GB), and allocating 1
+of 12 disks to the RAID and 10\% of the volume to file system and
+binary Gigabyte overheads, the disk purchases outlined above result in
+a total volume after the last purchase of 950 TB.  This meets the
+requirements with 10\% spare excess.  The disk volume profile is also
+shown in Figure~\ref{fig:StorageProfile} and shows that the disk space
+will be available in the time it is required.
+
+The total number of computers to be purchased is 80.  This provides
+the 1800 disk slots and more than enough processors to meet the
+processing requirements.  This also leaves 5 live spare machines.
+
+There are two details which are not included in the analysis above:
+compression and replication.  Compression of the older raw data will
+reduce the volume requirements by a factor of roughly two.  However,
+replication of the data across the network is necessary to ensure the
+data against catastrophic failures on a single machine.  Replication
+doubles the total data space needed.  These two factors will tend to
+cancel each other, and are ignored in the calculations above.
+
+The IPP PS-1 clusters will have the following allocations of computers
+from this cluster:
+\begin{itemize}
+\item Phase 2 Nodes: 32
+\item Phase 4 Nodes: 30
+\item AP Database: 10
+\item Metadata Database: 1
+\item Image Server Database: 1
+\item Controller /  Scheduler: 1
+\end{itemize}
+This distribution meets the projections for computational power for
+each of these data systems, and leaves 5 computers as live spares for
+redundancy.
+
+\subsection{PS-1 Cluster Expected Reliability}
+
+With 80 computers and 1920 disks, component failures are inevitable.
+The cluster design and management must be chosen to minimize their
+impact on operations and data integrity.
+
+There are several factors which reduce the cluster's exposure to
+hardware failures.  First, the use of RAID controllers and RAID-5
+striping of the data will protect the data on a single RAID set
+against the failure of a single disk in the array.  Second,
+duplication of data across the cluster will protect against
+catastrophic failures of the array (loss of two disks, loss of the
+array controller card).  Finally, the flexibility of the distributed
+computing plan minimizes the impact the loss of individual machines
+has on operations by making changes in the data and processing
+assignments on the cluster a trivial matter.
+
+The components which are most likely to fail in the experience of our
+team are, in order: hard drives, RAID controllers, ram, power
+supplies, and other components.  The hard drive and RAID controller
+failure rates are by far the dominant concerns as they potentially
+affects the data integrity.
+
+Most sources (REFS: UCSD article, Samsung White Paper) currently imply
+hard disk failure rates (MTBF) in the range 400,000 hours and 500,000
+hours.  These are used as an upper limit, with the more historically
+conservative value of 100,000 hours used instead.  With 1920 disk,
+this MTBF implies a failure of one disk every 2.2 days.  Since the
+disks are in a RAID which reports the disk failures immediately and
+drops the array into degraded mode, these failures will not have a
+huge impact on the operations, and recovery time is only 10s of
+minutes.  This failure rate implies that the maintenance plan must
+include checks for hard disk failures on a daily basis, and should
+make use of email notification and early warning information (ie,
+SMART messages).  
+
+A catastrophic failure for the array would require two of the 12 disks
+to fail before the first failed disk is replaced.  Assuming that
+maintenance is poor and it is possible for a disk to take 1 week to
+be replaced, the probability of a catastrophe is 1.8\% each time the
+first disk fails.  Combined with the disk failure rate, RAID
+catastrophes are expected 6 times over the 2 year operation of PS-1.
+These numbers can be used as a guideline for the level of support
+needed to avoid these RAID failures.  Note that these 6 failures
+should not cause loss of data since the data is duplicated across the
+cluster, but they require over 1 day for recovery (as the entire array
+must be replicated across the network).
+
+A detailed IPP computer cluster commissioning and maintenance plan is
+specified in the document `Pan-STARRS PS-1 IPP Cluster Support'
+(PSDC-430-014).
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics[angle=-90]{pics/ps1_ipp_storage.ps}}
+\caption{ \label{fig:StorageProfile} Storage Profile}
+\end{center}
+\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\clearpage
+\appendix
+\section{Image Server Database Table Contents}
+\label{sec:ImageServerTableContents}
+
+Tables~\ref{tab:ImageServerTables:SO} - \ref{tab:ImageServerTables:VOL} list
+the basic contents of the Image Server database tables.  
+
+\begin{table}[bh]
+\begin{center}
+\caption{Storage Object Table Contents\label{tab:ImageServerTables:SO}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Description} \\
+\hline
+\code{so_id}      & integer        & internal storage object identifier \\
+\code{ext_id}     & string         & external storage object identifier (file ID) \\
+\code{comment}    & string         & user description of object \\
+\code{epoch}      & date/time      & last date of access \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Instance Table Contents\label{tab:ImageServerTables:INT}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Description} \\
+\hline
+\code{ins_id}     & integer        & internal instance identifier \\
+\code{so_id}      & integer        & key to storage object table \\
+\code{uri}        & string         & location in hardware collection \\
+\code{sha1sum}    & string         & checksum information \\
+\code{assigned_location} & boolean & is location user-specified? \\
+\code{epoch}      & date/time      & last date of access \\
+\code{atime}      & date/time      & last date of access \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Volume Table Contents\label{tab:ImageServerTables:VOL}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Description} \\
+\hline
+\code{vol_id}     & integer        & internal volume identifier \\
+\code{uri}        & string         & node name \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+\clearpage
+
+\section{Metadata Database Table Contents}
+\label{sec:MetadataTableContents}
+
+Tables~\ref{tab:WeatherTable} -- \ref{tab:overlaps} list the basic contents of
+each of the Metadata Database tables listed in Section~\ref{sec:Metadata}.
+
+\begin{table}[bh]
+\begin{center}
+\caption{Weather Table: some sample weather points\label{tab:WeatherTable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time             & date/time       & The time the weather information was measured. \\
+Temperature 01   & float           & The external temperature \\
+Temperature 02   & float           & The temperature at top of the dome \\
+Temperature 03   & float           & The temperature on the primary mirror \\
+Humidity         & float           & The relative humidity. \\
+Pressure         & float           & The (external) atmospheric pressure. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{SkyProbe Transparency Table (sample entries)\label{tab:SkyprobeBVTable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time             & date/time       & The time the SkyProbe image was taken. \\
+Filter           & string	   & Filter used for SkyProbe image. \\
+Transparency     & float	   & The derived transparency. \\
+Number of stars  & int		   & The number of stars used to measure the transparency. \\
+Astrometry       & coords	   & The astrometry used on the SkyProbe image. \\
+Exposure time    & float	   & The exposure time of the SkyProbe image. \\
+Sky brightness   & float	   & The measured sky (surface) brightness, counts / second \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Skyprobe Line Absorption Table (sample entries)\label{tab:SkyprobeATable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time             & date/time       & The time the LRProbe observation was taken. \\
+Disperser ID     & string          & ID of the dispersing element \\
+Atm Component 1  & float	   & The strength of the 1st atmospheric component. \\
+Atm Component 2  & float	   & The strength of the 2nd atmospheric component. \\
+Atm Component 3  & float	   & The strength of the 3rd atmospheric component. \\
+Disperser ID     & string          & ID of the dispersing element \\
+Number of stars  & int 	           & Number of stars used to measure the absorptions. \\
+Astrometry       & coords	   & The astrometry used on the LRProbe image. \\
+Exposure time    & float	   & The exposure time of the LRProbe image. \\
+Sky brightness   & float	   & The measured sky (surface) brightness, in physical units. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Skyprobe Line Emission Table (sample entries)\label{tab:SkyprobeETable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time             & date/time       & The time the LRProbe observation was taken. \\
+Disperser ID     & string          & ID of the dispersing element \\
+Atm Component 1  & float	   & The strength of the 1st atmospheric component. \\
+Atm Component 2  & float	   & The strength of the 2nd atmospheric component. \\
+Atm Component 3  & float	   & The strength of the 3rd atmospheric component. \\
+Continuum        & float	   & The strength of the continuum emission. \\
+Disperser ID     & string          & ID of the dispersing element \\
+Exposure time    & float	   & The exposure time of the LRProbe image. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{DIMM Measurements Table\label{tab:DimmTable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time             & date/time       & The time the DIMM observation was taken. \\
+$\sigma_x$       & float           & Raw dispersion in $x$. \\
+$\sigma_y$       & float	   & Raw dispersion in $y$. \\
+FWHM             & float	   & Dervied seeing full width at half maximum. \\
+RA               & float	   & The coordinates of the measured star. \\
+DEC              & float	   & The coordinates of the measured star. \\
+Exposure time    & float           & The exposure time of the DIMM observation. \\
+Telescope ID     & string          & source of the DIMM data \\
+\hline		 
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Near IR Wide-field Camera Results Table\label{tab:NIR-Table}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time           	 & date/time       & The time the NIR observation was taken. \\
+Sky brightness 	 & float           & The sky (surface) brightness in the NIR observation. \\
+Sky variance   	 & float	   & The variance in the sky (surface) brightness. \\
+Astrometry     	 & coords          & The astrometry used on the NIR image. \\
+FOV X            & float           & field width \\
+FOV Y            & float           & field height \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Dome Status Table\label{tab:DomeStatusTable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time          	 & date/time       & The time for which the dome status is valid. \\
+Azimuth       	 & float           & The azimuth of the dome. \\
+Open status   	 & boolean	   & Whether the dome is open or not. \\
+Lights status 	 & boolean	   & Whether lights are on in the dome or not. \\
+Track status 	 & boolean	   & Whether dome is tracking telescope or not. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Telescope Status\label{tab:TelescopeStatusTable}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Time         	 & date/time       & The time for which the telescope status is valid. \\
+Guide status 	 & enum            & The status of the guiding. \\
+Altitude     	 & float	   & The telescope altitude. \\
+Azimuth      	 & float	   & The telescope azimuth. \\
+RA  	     	 & float	   & The telescope Right Ascension (ICRS $\approx$ J2000). \\
+Dec 	     	 & float	   & The telescope Declination (ICRS $\approx$ J2000).\\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Raw FPA Images\label{tab:RawFPAs}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+ID               & string          & FPA image ID \\
+RA               & float	   & Coordinates of the boresight (i.e. telescope pointing). \\
+DEC              & float	   & Coordinates of the boresight (i.e. telescope pointing). \\
+Filter           & string	   & Filter used for the exposure. \\
+Image Type       & enum            & image exposure type \\
+Exposure time    & float	   & Exposure time for the image. \\
+Airmass          & float	   & Airmass at which the image was taken. \\
+ObsFrame ID      & int   	   & Observation frame identification number, ties FPAs into major frame \\
+ObsGroup ID      & int   	   & Observation group identification number, ties FPAs into observing group \\
+Observer         & string	   & The name of the observer, or the version of the telescope scheduler software. \\
+Program          & string	   & The observing program being executed. \\
+Nchips readout   & int   	   & Number of detector chips read out \\
+Camera           & string   	   & Identification of camera source \\
+Telescope        & string   	   & Telescope used for observation \\
+Astrometry       & coords	   & The astrometry used for the FPA. \\
+Chip Metadata    & string          & metadata resource file \\
+Cell Metadata    & string          & metadata resource file \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Pending Science Chips\label{tab:PendingChips}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+FPA ID           & string          & FPA image ID \\
+Chip ID          & string          & Chip identification number. \\
+Proc Status      & enum            & Current Processing Status. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Processed Science Chips\label{tab:ProcessedChips}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+FPA ID           & string          & FPA Image ID \\
+Chip ID          & string          & Chip identification number. \\
+Status           & enum            & Current Processing Status. \\
+Residual Stats   & float           & quality statistics. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Observation Group Information\label{tab:OBSGroup}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+ObsGroup ID      & string          & Identification number for the observation group. \\
+Number of images & string          & Number of images in the observation group. \\
+Type             & string          & Type of observation. \\
+Status           & string          & Status of the observation group. \\
+etc & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Observation Frame Information\label{tab:OBSFrame}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+ObsFrame ID      & string          & Identification number for the observation frame. \\
+Number of images & string          & Number of images in the observation group. \\
+Type             & string          & Type of observation. \\
+Status           & string          & Status of the observation group. \\
+etc & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Science Processing Stats\label{tab:PSStats}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Chip ID          & string	   & The chip identification number. \\
+State            & string	   & The state of the processing. \\
+ObsFrame ID      & string	   & The major frame the chip belongs to. \\
+ObsGroup ID      & string	   & The observation group the chip belongs to. \\
+P1 astrom        & string	   & The Phase 1 astrometry results file. \\
+P2 astrom        & string	   & The Phase 2 astrometry results file. \\
+P3 astrom        & string	   & The Phase 3 astrometry results file. \\
+N guide stars    & string	   & Number of guide stars used for the exposure. \\
+Astrometry stats & string	   & Summary statistics for astrometry (number of stars, $sigma_x$, $sigma_y$) \\
+Astrom catalog   & string	   & The reference catalog that was used for the astrometry. \\
+Bias method      & string	   & Method used to correct the bias. \\
+Bias stats       & string	   & Summary statistics for bias \\
+Flat-field image & string	   & The flat-field image that was applied. \\
+Kernel data      &       	   & A description of the OT kernel. \\
+Flat-field stats &       	   & Summary statistics for flat-field (sigma of sky). \\
+Mask image       & string	   & The mask image that was applied. \\
+Mask method      & string	   & The algorithm used to mask the bad pixels. \\
+Fringe images    & string	   & The fringe model images that were used. \\
+Fringe stats     &       	   & Summary statistics for fringes (fringe amplitude, sky sigma) \\
+Object stats     &       	   & Summary statistics for object detection (number of objects, depth, other input parameters). \\
+Photometry data  &       	   & photometry information: magnitude zero point and other corrections. \\
+Photometry stats &       	   & Summary statistics for the photometry (number of stars, $sigma_m$) \\
+Photom catalog   & string	   & The reference catalog that was used for the photometry. \\
+PSF stats        &       	   & Summary statistics of the PSF. \\
+Software ver     & string	   & Versions of each of the modules used in the processing. \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Chip / Sky overlaps\label{tab:overlaps}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Chip ID     	 & string	   & The identification number of the chip. \\
+Sky Cell ID 	 & string	   & The identification number of the sky cell. \\
+State       	 & string	   & Processing state of overlap \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Processed Sky-Cell stats\label{tab:ProcessedSky}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Input Chips        & string 	   & Identification numbers of the chips used to produce the sky cell. \\
+PSF adjustments    & string 	   & Adjustments to the PSF. \\
+CR rejection stats & string 	   & Statistics from the CR rejection (number of CRs, distribution, limiting flux). \\
+Image comb params  & string 	   & Parameters used for the image combination. \\
+Diff image params  & string 	   & Parameters used for the image differencing. \\
+Average weight     & string 	   & The weight of the reference image \\
+P4D object stats   & string 	   & Summary statistics of the object detection  \\
+P4S object stats   & string 	   & Summary statistics of the object detection  \\
+Software versions  & string 	   & Software versions of modules used in the sky cell processing. \\
+Processing stats   & string 	   & Summary statistics of the processing (CPU time, etc). \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+\clearpage 
+
+\section{AP Database Table Contents}
+\label{sec:DVOTableContents}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Images\label{tab:images}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Image ID          & & \\ 
+time/date	  & & \\
+Exposure Time	  & & \\
+Nstars		  & & \\
+NX		  & & \\
+NY		  & & \\
+photcode	  & & \\
+Mcal		  & & \\
+Mcal error	  & & \\
+Mcal chisq	  & & \\
+Airmass           & & \\
+Astrometry	  & & \\
+PSF		  & & \\
+flags		  & & \\
+Camera		  & & \\
+\hline		  
+\end{tabular}	  
+\end{center}	  
+\end{table}	  
+		  
+\begin{table}[bh]
+\begin{center}
+\caption{Image Overlaps\label{tab:ImageOverlaps}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Image ID          & & \\
+Region Table	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Objects\label{tab:Objects}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline 
+ID                & & \\
+$\alpha$	  & & \\
+$\delta$	  & & \\
+$\mu_{\alpha}$	  & & \\
+$\mu_{\delta}$	  & & \\
+$\sigma_{\alpha}$ & & \\
+$\sigma_{\delta}$ & & \\
+$\chi^2$ position & & \\
+$N_{\rm det}$	  & & \\
+$N_{\rm miss}$	  & & \\
+flags		  & & \\
+\hline		  
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Average Magnitudes\label{tab:AveMags}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+object ID         & & \\
+$M_{\rm int}$	  & & \\
+$M_{\rm ext}$	  & & \\
+$\chi^2_{\rm mag}$& & \\
+$\sigma_{\rm mag}$& & \\
+photcode	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Solar System Objects\label{tab:SSObjs}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+SSO ID     	  & & \\
+$N_{\rm det}$	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Matched Detections\label{tab:Detections}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+$\alpha$          & & \\
+$\delta$	  & & \\
+$\sigma_{\alpha}$ & & \\
+$\sigma_{\delta}$ & & \\
+$M_{\rm inst}$	  & & \\
+$M_{\rm cal}$	  & & \\
+$\sigma_{\rm mag}$& & \\
+photcode	  & & \\
+type		  & & \\
+flags		  & & \\
+time/date	  & & \\
+airmass		  & & \\
+$\sigma_{x}$	  & & \\
+$\sigma_{y}$	  & & \\
+$\theta$	  & & \\
+object ID         & & \\
+exptime		  & & \\
+sky		  & & \\
+$\sigma_{\rm sky}$& & \\
+etc		  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Orphaned Detections\label{tab:Orphans}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+$\alpha$          & & \\
+$\delta$	  & & \\
+$\sigma_{\alpha}$ & & \\
+$\sigma_{\delta}$ & & \\
+$M_{\rm inst}$	  & & \\
+$M_{\rm cal}$	  & & \\
+$\sigma_{\rm mag}$& & \\
+photcode	  & & \\
+type		  & & \\
+flags		  & & \\
+time/date	  & & \\
+airmass		  & & \\
+$\sigma_{x}$	  & & \\
+$\sigma_{y}$	  & & \\
+$\theta$	  & & \\
+exptime		  & & \\
+sky		  & & \\
+$\sigma_{\rm sky}$& & \\
+etc		  & & \\
+\hline		  
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Non-detections\label{tab:NonDetects}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline  
+object ID          & & \\
+$N_{\rm non-det}$	   & & \\
+last time/date 	   & & \\
+last mag	   & & \\
+faintest time/date & & \\
+faintest mag	   & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Regions\label{tab:Regions}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+$\alpha_0$        & & \\
+$\alpha_1$	  & & \\
+$\delta_0$	  & & \\
+$\delta_1$	  & & \\
+Region ID	  & & \\
+Parent ID	  & & \\
+Nchildren	  & & \\
+Images		  & & \\
+Objects		  & & \\
+Detections 	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Filters\label{tab:Filters}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Filter ID         & & \\
+Filter name	  & & \\
+Photcode	  & & \\
+$\lambda_0$	  & & \\
+$\delta_\lambda$  & & \\
+$\epsilon$	  & & \\
+transmission curve& & \\
+time/date	  & & \\
+\hline		  
+\end{tabular}	  
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Photcodes\label{tab:Photcodes}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Photcode          & & \\
+Telescope	  & & \\
+Camera		  & & \\
+Detector	  & & \\
+Filter		  & & \\
+Nominal ZP	  & & \\
+airmass terms	  & & \\
+color terms	  & & \\
+Target		  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Zero Point History\label{tab:Zpts}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline 
+Photcode          & & \\
+start Time/date	  & & \\
+end Time/date	  & & \\
+Zero Points	  & & \\
+airmass		  & & \\
+color		  & & \\
+error		  & & \\
+N measurements	  & & \\
+N stars		  & & \\
+photom ref set    & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Distortion History\label{tab:Distortions}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Camera            & & \\
+Telescope	  & & \\
+distortion terms  & & \\
+time/date	  & & \\
+residuals / error & & \\
+N stars		  & & \\
+N images	  & & \\
+astrom ref set	  & & \\
+\hline		  
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Database Hosts\label{tab:DVOHosts}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+machine name	  & & \\
+machine ID	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{PanTasks Example : Pcopy.pro}
+
+Below is an example script for psched which demonstrates the
+scheduling system. This parallel-copying script implements the
+Pan-STARRS image copying system, which requests images from the summit
+and copies them to the appropriate computer. The first task in the
+script queries an external system for new image names with the
+function new.images. In the case of Pan-STARRS, this would be a
+request from OTIS, the observatory controlling system. The second task
+initiates the individual image copies, with separate CCDs being copied
+to separate computers. This script uses the concept of having specific
+machines assigned to specific CCDs (as Pan-STARRS intends to
+operate). The association is determined by calling the external
+function chip.host, providing the identifier of the chip in
+question. This returns an appropriate host. The copy.image function
+copies the file and also sends a message to the summit system to
+inform it that the image has been successfully copied.
+
+\begin{verbatim}
+verbose on
+ queueinit newImages
+ exec echo 0 > new.last
+ exec cp -f raw.list new.list
+ 
+ controller host add po01
+ controller host add po02
+ controller host add po03
+ controller host add po04
+ 
+ # identify the images ready for copy 
+ # new entries are added to queue newImages
+ # need to compare the new list with the ones already being processed
+ task	       new.images
+   command      new.images
+   host         local
+ 
+   periods      -poll 1
+   periods      -exec 5
+   periods      -timeout 5
+ 
+   # success
+   task.exit    0
+     local i j Nstdout Nimages
+     # compare output with new.image queue
+     # keep only new entries
+     queuesize stdout -var Nstdout
+     for i 0 $Nstdout
+       queuepop stdout -var line
+       queuepush newImages -uniq -key 0 "$line"
+     end
+   end
+ 
+   # locked list
+   task.exit    1
+     echo       "new.images: exec failure"
+     $new.image.failure ++
+   end
+ 
+   # default exit status
+   task.exit    default
+     echo       "new.images: unknown exit status: $EXIT"
+     $new.image.failure ++
+   end
+ 
+   # operation times out?
+   task.exit    timeout
+     echo       "new.images: timeout"
+     $new.image.failure ++
+   end
+ end
+ 
+ # copy new images, sending job to desired host
+ task	       copy.images
+   periods      -poll 0.2
+   periods      -exec 1
+   periods      -timeout 5
+ 
+   task.exec
+     queuesize  newImages -var N
+     if ($N == 0) break
+     # if ($network == 0) break
+     # if ($filesystem == 1) break
+     
+     queuepop newImages -var line
+     list tmp -split $line
+     $filename   = $tmp:0
+     $chip       = $tmp:1
+     $state      = $tmp:2
+     if ($state == new) 
+       # copy this image
+       queuepush newImages -replace -key 0 "$filename $chip run"
+     else
+       # ignore this image
+       queuepush newImages -replace -key 0 "$filename $chip $state"
+       break
+     end
+     # echo $chip
+     $host = `chip.host $chip`
+     # echo $host
+     host $host
+     # echo "starting copy for $filename on $host..."
+     command copy.image $filename $chip
+   end
+ 
+   # can I have access to argc,argv?
+ 
+   # success
+   task.exit    0
+     echo "done copy..."
+     queuepop stdout -var line
+     list tmp -split $line
+     $filename   = $tmp:0
+     $chip       = $tmp:1
+     exec mark.image $filename
+     queuepush newImages -replace -key 0 "$filename $chip copy"
+   end
+ 
+   # default exit status
+   task.exit    default
+     echo       "new.images: unknown exit status: $EXIT"
+     $new.image.failure ++
+   end
+ 
+   # operation times out?
+   task.exit    timeout
+     echo       "new.images: timeout"
+     $new.image.failure ++
+   end
+ end
+ 
+\end{verbatim}
+
+\input{glossary.tex}
+
+\end{document}
+
+------
+
+* output data products
+* DVO
+* ipptools / ippMonitor?
+* analysis stages, versions and iterations
Index: /tags/sj_tags/sj_root_20080929/doc/design/phase4.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/phase4.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/phase4.txt	(revision 22322)
@@ -0,0 +1,39 @@
+
+output from ppImage is (in general) a multiple file, multiple
+extension set of FITS images:
+
+exp01.chipNN.p2.fits
+
+along with the output object files with the associated astrometry:
+
+exp01.chipNN.p2.cmp
+
+for phase3, output is a single MEF file per exposure with one chips
+per extension.  
+
+ISP:
+exp01.p2.fits (image data in PHU)
+exp01.p2.cmp (astrometry data in PHU, object in table, chip mode)
+
+Megacam:
+exp01.p2.fits (one chip per extension)
+exp01.p2.cmp (mosaic astrometry in PHU, chip astrometry per extension)
+
+GPC:
+exp01.chipNN.p2.fits (one chip per extension)
+exp01.chipNN.p2.cmp (astrometry in PHU, chip astrometry per extension)
+
+exp01.p3.cmp (mosaic astrometry in PHU, chip astrometry per extension)
+
+
+user interface to warp:
+
+warp skycell.fits -image exp01.p2.fits -header exp01.p3.cmp output.fits
+ - if -header is not supplied, assume image file contains astrometry
+ - skycell astrometry + NAXIS1,2 defines output grid
+ - output.fits is created (will be replaced if it exists)
+ - allow a subset of the input image to be warped? (specific
+ extensions)
+ - EXTNAME in input.fits is matched to EXTNAME in header.fits
+   - use the WRP / DIS astrometry if available
+
Index: /tags/sj_tags/sj_root_20080929/doc/design/pics/ps1_ipp_cluster_timeline.planner
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/pics/ps1_ipp_cluster_timeline.planner	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/pics/ps1_ipp_cluster_timeline.planner	(revision 22322)
@@ -0,0 +1,148 @@
+<?xml version="1.0"?>
+<project name="" company="" manager="" phase="" project-start="20040829T000000Z" mrproject-version="2" calendar="1">
+  <properties>
+    <property name="cost" type="cost" owner="resource" label="Cost" description="standard cost for a resource"/>
+  </properties>
+  <phases/>
+  <calendars>
+    <day-types>
+      <day-type id="0" name="Working" description="A default working day"/>
+      <day-type id="1" name="Nonworking" description="A default non working day"/>
+      <day-type id="2" name="Use base" description="Use day from base calendar"/>
+    </day-types>
+    <calendar id="1" name="Default">
+      <default-week mon="0" tue="0" wed="0" thu="0" fri="0" sat="1" sun="1"/>
+      <overridden-day-types>
+        <overridden-day-type id="0">
+          <interval start="0800" end="1200"/>
+          <interval start="1300" end="1700"/>
+        </overridden-day-type>
+      </overridden-day-types>
+      <days/>
+    </calendar>
+  </calendars>
+  <tasks>
+    <task id="1" name="Commissioning" note="" work="9532800" start="20040924T000000Z" end="20051230T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+      <task id="2" name="Switch ordered" note="" work="0" start="20040924T000000Z" end="20040924T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+        <constraint type="must-start-on" time="20040924T000000Z"/>
+      </task>
+      <task id="3" name="Arrives" note="" work="0" start="20050101T000000Z" end="20050101T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+        <constraint type="must-start-on" time="20050101T000000Z"/>
+        <predecessors>
+          <predecessor id="1" predecessor-id="2" type="FS"/>
+        </predecessors>
+      </task>
+      <task id="4" name="Nodes ordered" note="" work="0" start="20041201T000000Z" end="20041201T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+        <constraint type="must-start-on" time="20041201T000000Z"/>
+      </task>
+      <task id="5" name="Arrives" note="" work="0" start="20050301T000000Z" end="20050301T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+        <constraint type="must-start-on" time="20050301T000000Z"/>
+        <predecessors>
+          <predecessor id="1" predecessor-id="4" type="FS"/>
+        </predecessors>
+      </task>
+      <task id="6" name="Stage 1" note="" work="2044800" start="20050401T000000Z" end="20050708T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+        <predecessors>
+          <predecessor id="1" predecessor-id="3" type="FS"/>
+          <predecessor id="1" predecessor-id="5" type="FS"/>
+        </predecessors>
+        <task id="7" name="Step 1" note="" work="144000" start="20050401T000000Z" end="20050407T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+          <constraint type="must-start-on" time="20050401T000000Z"/>
+        </task>
+        <task id="8" name="Step 2" note="" work="1440000" start="20050501T000000Z" end="20050708T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+          <constraint type="must-start-on" time="20050501T000000Z"/>
+          <predecessors>
+            <predecessor id="1" predecessor-id="7" type="FS"/>
+          </predecessors>
+        </task>
+      </task>
+      <task id="9" name="Stage 2" note="" work="2736000" start="20050708T170000Z" end="20051118T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+        <predecessors>
+          <predecessor id="1" predecessor-id="6" type="FS"/>
+        </predecessors>
+        <task id="10" name="H/W maintenance" note="" work="2736000" start="20050708T170000Z" end="20051118T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work"/>
+      </task>
+      <task id="11" name="Stage 3" note="" work="2102400" start="20050921T000000Z" end="20051230T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+        <task id="12" name="1st disk lot" note="" work="1814400" start="20050921T000000Z" end="20051219T000000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+          <task id="13" name="Ordered" note="" work="0" start="20050921T000000Z" end="20050921T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+            <constraint type="start-no-earlier-than" time="20050921T000000Z"/>
+          </task>
+          <task id="14" name="Testing" note="" work="576000" start="20051121T000000Z" end="20051216T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+            <constraint type="must-start-on" time="20051121T000000Z"/>
+            <predecessors>
+              <predecessor id="1" predecessor-id="9" type="FS"/>
+              <predecessor id="1" predecessor-id="13" type="FS"/>
+            </predecessors>
+          </task>
+          <task id="15" name="Capacity available" note="" work="0" start="20051219T000000Z" end="20051219T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+            <constraint type="must-start-on" time="20051219T000000Z"/>
+            <predecessors>
+              <predecessor id="1" predecessor-id="14" type="FS"/>
+            </predecessors>
+          </task>
+        </task>
+        <task id="16" name="Step 2" note="" work="288000" start="20051219T000000Z" end="20051230T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+          <constraint type="must-start-on" time="20051219T000000Z"/>
+          <predecessors>
+            <predecessor id="1" predecessor-id="12" type="FS"/>
+          </predecessors>
+        </task>
+      </task>
+    </task>
+    <task id="17" name="Production" note="" work="15004800" start="20051230T170000Z" end="20080101T000000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+      <predecessors>
+        <predecessor id="1" predecessor-id="1" type="FS"/>
+      </predecessors>
+      <task id="18" name="H/W maintenance" note="" work="14976000" start="20051230T170000Z" end="20071228T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work"/>
+      <task id="19" name="First Light" note="&#10;" work="0" start="20060101T000000Z" end="20060101T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+        <constraint type="must-start-on" time="20060101T000000Z"/>
+      </task>
+      <task id="20" name="2nd disk lot" note="" work="1900800" start="20060601T000000Z" end="20060901T000000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+        <task id="21" name="Ordered" note="" work="0" start="20060601T000000Z" end="20060601T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+          <constraint type="must-start-on" time="20060601T000000Z"/>
+        </task>
+        <task id="22" name="Testing" note="" work="576000" start="20060801T000000Z" end="20060828T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+          <constraint type="must-start-on" time="20060801T000000Z"/>
+          <predecessors>
+            <predecessor id="1" predecessor-id="21" type="FS"/>
+          </predecessors>
+        </task>
+        <task id="23" name="Capacity available" note="" work="0" start="20060901T000000Z" end="20060901T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+          <constraint type="must-start-on" time="20060901T000000Z"/>
+          <predecessors>
+            <predecessor id="1" predecessor-id="22" type="FS"/>
+          </predecessors>
+        </task>
+      </task>
+      <task id="24" name="3rd disk lot" note="" work="1900800" start="20070301T000000Z" end="20070601T000000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+        <task id="25" name="Ordered" note="" work="0" start="20070301T000000Z" end="20070301T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+          <constraint type="must-start-on" time="20070301T000000Z"/>
+        </task>
+        <task id="26" name="Testing" note="" work="576000" start="20070501T000000Z" end="20070528T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+          <constraint type="must-start-on" time="20070501T000000Z"/>
+          <predecessors>
+            <predecessor id="1" predecessor-id="25" type="FS"/>
+          </predecessors>
+        </task>
+        <task id="27" name="Capacity available" note="" work="0" start="20070601T000000Z" end="20070601T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+          <constraint type="must-start-on" time="20070601T000000Z"/>
+          <predecessors>
+            <predecessor id="1" predecessor-id="26" type="FS"/>
+          </predecessors>
+        </task>
+      </task>
+      <task id="28" name="End of Ref. Mission" note="" work="0" start="20080101T000000Z" end="20080101T000000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
+        <constraint type="must-start-on" time="20080101T000000Z"/>
+      </task>
+    </task>
+    <task id="29" name="End of Life" note="" work="144000" start="20080101T000000Z" end="20080107T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
+      <predecessors>
+        <predecessor id="1" predecessor-id="17" type="FS"/>
+      </predecessors>
+      <task id="30" name="data migration" note="" work="144000" start="20080101T000000Z" end="20080107T170000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work"/>
+    </task>
+  </tasks>
+  <resource-groups/>
+  <resources/>
+  <allocations/>
+</project>
Index: /tags/sj_tags/sj_root_20080929/doc/design/trace.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/design/trace.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/design/trace.pl	(revision 22322)
@@ -0,0 +1,74 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 3) { die "USAGE: trace.pl (input) (output) (trace)\n"; }
+
+open (FILE, $ARGV[0]);
+@list = <FILE>;
+close (FILE);
+
+open (OUT, ">$ARGV[1]");
+open (TRC, ">$ARGV[2]");
+
+&header;
+$NL = 0;
+$NLMAX = 53;
+
+$Nv = 0;
+foreach $line (@list) {
+    if ($line =~ m|\\VER\{\S*\}\{.*\}|) {
+	($start, $value, $stop) = $line =~ m|(.*\\VER\{\S*\})\{(.*)\}(.*)|;
+	@words = split (" ", $value);
+	$N = @words;
+
+	print OUT "$start\{";
+	for ($i = 0; $i < @words; $i++) {
+	    $word = $words[$i];
+	    $last = chop ($word);
+	    if ($last ne ",") { $word = $word . $last; }
+	    if ($word =~ m|TLR|) { $word = "\\ref\{$word\}"; }
+	    print OUT "$word";
+	    if ($i < $N - 1) { print OUT ", "; }
+
+	    if ($Nv > 24) { 
+		$Nr = $Nv - 24; 
+		print TRC "\\ref{DRQ:$Nr} & & $word & \\\\ \\hline\n";
+		$NL ++;
+		if ($NL > $NLMAX) {
+		    print TRC "\\end{tabular}\n";
+		    print TRC "\\end{center}\n\n";
+		    &header;
+		    $NL = 0;
+		}
+	    } 	
+	}
+	print OUT "\}$end";
+	if ($Nv > 24) { 
+	    $Nr = $Nv - 24; 
+	    print OUT "\\label{DRQ:$Nr}\n";
+	} else {
+	    print OUT "\n";
+	}
+	$Nv ++;
+    } else {
+	print OUT $line;
+    }
+}
+print TRC "\\end{tabular}\n";
+print TRC "\\end{center}\n\n";
+
+close (OUT);
+close (TRC);
+
+
+sub header {
+print TRC "\\begin{center}\n";
+print TRC "\\footnotesize\n";
+print TRC "\\begin{tabular}{|l|l|l|l|}\n";
+print TRC "\\hline\n";
+print TRC "\\multicolumn{2}{|c|}{\\bf Derived Subsystem Requirements} &  \n";
+print TRC "\\multicolumn{2}{|c|}{\\bf Top-level Subsystem Requirements} \\\\ \\hline \n";
+print TRC "\\FRN{\\bf Number} & \n";
+print TRC "\\FRS{\\bf Caption} & \n";
+print TRC "\\FRN{\\bf Number} & \n";
+print TRC "\\FRS{\\bf Caption} \\\\ \\hline\n";
+}
Index: /tags/sj_tags/sj_root_20080929/doc/draft/errors.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/draft/errors.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/draft/errors.tex	(revision 22322)
@@ -0,0 +1,212 @@
+\documentclass[panstarrs]{panstarrs}
+
+\begin{document}
+  
+\section{Error Handling}
+\hlabel{errorStack}
+
+\begin{table}
+\begin{verbatim}
+typedef struct {
+    char *name;                         // category of code that caused the error
+    psErrorCode code;                   // class of error (equivalent to errno)
+    char *msg;                          // the message associated with the error
+} psErr;
+
+typedef enum {
+    PS_OLD_ERROR = 0,                   ///< This is an old error, and should append to the error stack
+    PS_NEW_ERROR = 1,                   ///< This is a new error and should clear the error stack
+} psErrorStatus;
+
+/// Prints an error message and doesn't abort; returns code
+int psError(const char *name,           ///< Category of code that caused the error
+            psErrorCode code,           ///< class of error (equivalent to errno)
+            psErrorStatus status,       ///< is this a new error?
+            const char *fmt,            ///< Format
+            ...                         ///< Extra arguments to use format
+    );
+
+const psErr *psGetError(int which);     // return specified error (or an "error" with code PS_ERR_NONE)
+const psErr *psLastError(void);         // return last error (or an "error" with code PS_ERR_NONE)
+
+void psErrorClear(void);                ///< Clear the error stack
+
+void psErrorStackPrint(FILE *fd, const char *fmt, ...); ///< print the errorstack to this file descriptor
+void psVErrorStackPrint(FILE *fd, const char *fmt, va_list va); ///< print the errorstack to this file descriptor
+const char *psErrorCodeString(psErrorCode code);        ///< return the string associated with an error code.
+
+\end{verbatim}
+\end{table}
+
+\PS{} errors shall be raised using the function \code{psError}. The \code{name} is of the
+form \code{aaa.bbb.ccc} and identifies the component raising the error.  The \code{psErrorCode} is
+an enumerated type which lists the possible textit{classes} of errors (e.g. \code{PS_ERR_IO})
+that \PS/ code can generate (see section \ref{psErrorCodes}). \code{status} specifies whether this is a
+new error, or whether this call to \code{psError} is in response to an error that has
+already resulted in a call to \code{psError}.
+The final required argument, \code{fmt}, is a \code{printf}-style
+format that is passed to \code{psLogMsg} with code \code{PS_LOG_ERROR}.
+
+The result of a call to \code{psError} shall be to push a \code{psErr} struct onto
+a stack; this stack is cleared if \code{psErrorStatus} is true, or by a call
+to \code{psErrorClear}.
+
+The last error reported is available from \code{psLastError}; if no errors are
+current, a non-\code{NULL} \code{psErr} shall be returned with code \code{PS_ERR_NONE}.
+Previous errors on the stack shall be returned by \code{psGetError} (a value of \code{0}
+passed to \code{psGetError} is equivalent to a call to \code{psLastError}).
+
+The routine \code{psErrorCodeString} returns the string associated with an error code.
+
+The entire error stack may be printed to an open file descriptor by calling \code{psErrorStackPrint}
+(or \code{psVErrorStackPrint});
+if and only if there are current errors, the printf-style string \code{fmt} is first printed
+to the file descriptor \code{fd}. In this printout, error codes shall be replaced by their
+string equivalents as defined in the next section.  Note that these are also available in
+the \code{psErr} structure. The successive lines of the traceback should be indented by
+an additional space (see example). \code{psVErrorStackPrint} shall not invoke \code{va_end}.
+
+Any \code{errorCode}s less then or equal to \code{PS_ERR_BASE} (see next section) shall be taken
+to be valid values of \code{errno}, and \code{psErrorStackPrint} shall print the value returned
+by \code{strerror} if such error codes are encountered.
+
+\subsection{Error Codes}
+\hlabel{psErrorCodes}
+
+The type \code{psErrorCode} is defined by an auxiliary file, conventionally named
+\file{psErrorCodes.dat}. This file shall consist of a number of lines, each
+of the form:
+\begin{verbatim}
+NAME [ = value ] , STRING
+\end{verbatim}
+where \code{[ = value]} is optional, and no spaces are significant except in the
+STRING.  Comments extend from \code{#} to the end of the line (except that a
+\code{\#} shall be replaced by \code{#} and not taken to start a comment). For example,
+\begin{verbatim}
+#
+# This file is used to generate psErrorClasses.h
+#
+NONE = 0,               not an error; must be 0
+BASE = 256,             first value we use; should avoid errno conflicts
+UNKNOWN,                unknown error
+IO,                     I/O error
+BADFREE,                bad argument to psFree()
+MEMORY_CORRUPTION,      memory corruption detected
+\end{verbatim}
+The values \code{NONE = 0} and {UNKNOWN} must be present.
+
+The \PS{} Makefiles shall
+generate two files, \file{psErrorCodes.h} and
+\file{psErrorCodes.c} from the input file \file{psErrorCodes.dat}.
+\file{psErrorCodes.h} shall define an enumerated type
+\code{psErrorCode} with elements \code{PS_ERR_NAME} and values as specified
+in \file{psErrorCodes.dat}, e.g.
+\begin{verbatim}
+#if !defined(PS_ERROR_CODES_H)
+#define PS_ERROR_CODES_H
+
+typedef enum {
+    PS_ERR_NONE = 0,
+    PS_ERR_BASE = 256,
+    PS_ERR_UNKNOWN,
+    PS_ERR_IO,
+    PS_ERR_BADFREE,
+    PS_ERR_MEMORY_CORRUPTION,
+    PS_ERR_N_ERR_CLASSES,
+} psErrorCode;
+
+#endif
+\end{verbatim}
+Any \code{errorCode}s less then or equal to \code{PS_ERR_BASE} shall be taken
+to be valid values of \code{errno}.
+
+The implementation may add extra fields (e.g. \code{PS_ERR_N_ERR_CLASSES}).
+
+The latter shall be of the form
+\begin{verbatim}
+static struct {
+    psErrorCode code;
+    char *descrip;
+} errorStrings[] = {
+    { PS_ERR_NONE, "not an error; must be 0"},
+    { PS_ERR_BASE, "first value we use; should avoid errno conflicts"},
+    { PS_ERR_UNKNOWN, "unknown error"},
+    { PS_ERR_IO, "I/O error"},
+    { PS_ERR_BADFREE, "bad argument to psFree()"},
+    { PS_ERR_MEMORY_CORRUPTION, "memory corruption detected"},
+    { PS_ERR_N_ERR_CLASSES, NULL},
+};
+\end{verbatim}
+
+\subsection{Example}
+
+The following output:
+\begin{verbatim}
+Traceback:
+tst.error.primary              I/O error                      Primary error
+ tst.error.middle              unknown error                  Secondary error
+  tst.error                    unknown error                  Toplevel error
+Last error is of unknown type
+Third oldest error is of type IO
+No errors. Hurrah
+\end{verbatim}
+
+produced by running this code:
+
+\begin{verbatim}
+#include "psLib.h"
+
+static int primary(int i)
+{
+    if (i != 0) {                       // let's pretend it's an I/O error
+        return psError("tst.error.primary", PS_ERR_IO, 1, "Primary error");
+    }
+
+    return 0;
+}
+
+static int middle(void)
+{
+    if (primary(1) != 0) {
+        return psError("tst.error.middle", PS_ERR_UNKNOWN, 0, "Secondary error");
+    }
+
+    return 0;
+}
+
+static int toplevel(void)
+{
+    if (middle() != 0) {
+        return psError("tst.error", PS_ERR_UNKNOWN, 0, "Toplevel error");
+    }
+
+    return 0;
+}
+
+int main(void)
+{
+    if (toplevel() != 0) {
+        psErrorStackPrint(stdout, "Traceback:\n");
+
+        if (psLastError()->code == PS_ERR_UNKNOWN) {
+            fprintf(stderr, "Last error is of unknown type\n");
+        }
+        if (psGetError(2)->code == PS_ERR_IO) {
+            fprintf(stderr, "Third oldest error is of type IO\n");
+        }
+    }
+
+    psErrorClear();
+    psErrorStackPrint(stdout, "Traceback:\n");
+
+    if (psLastError()->code == PS_ERR_NONE) {
+        fprintf(stderr, "No errors. Hurrah\n");
+    }
+
+    return 0;
+}
+\end{verbatim}
+
+%------------------------------------------------------------------------------
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/draft/metadata.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/draft/metadata.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/draft/metadata.tex	(revision 22322)
@@ -0,0 +1,247 @@
+\documentclass[panstarrs]{panstarrs}
+
+\title{The \PS{} Metadata API}
+\author{Robert Lupton, Gene Magnier}
+\group{}
+\organization{}
+\docnumber{PSDC-???}
+
+\fancyhead{}
+\fancyhead[L]{\PS{} Document}
+\fancyhead[R]{PSDC-???}
+\fancyhead[C]{Revision 0.1 -- Draft}
+\fancyfoot[L]{psMetaData}
+\fancyfoot[R]{\today}
+
+\begin{document}
+
+\pagenumbering{roman} \thispagestyle{empty} \maketitle
+
+\pagebreak \pagestyle{fancy}
+{ \Large \bf Revision History}
+\begin{center}
+\begin{tabular}{|p{1in}|p{1in}|p{4.5in}|}
+\hline
+{\bf Revision Number} & {\bf Release Date} & {\bf Description} \\
+\hline
+%0.1 & 2003/12/08 & First draft \\
+\hline
+\end{tabular}
+\end{center}
+
+{ \Large \bf Referenced Documents}
+\begin{center}
+\begin{tabular}{|p{1in}|p{2.75in}|p{2.75in}|}
+\hline
+{\bf PanSTARRS ID} & {\bf Title} & {\bf Author} \\
+\hline
+%%% References here.
+\hline
+\end{tabular}
+\end{center}
+
+\pagebreak
+\tableofcontents
+
+% \pagebreak
+% \listoffigures
+
+\pagebreak \pagenumbering{arabic}
+
+\section{Introduction}
+
+This document addresses the question of how \PS{}
+metadata should be represented in memory. We do not
+(yet) address how it should be represented on disk.
+
+\section{Metadata Representation}
+
+We propose that an item of metadata be represented as a C structure with at least the following
+fields:
+\begin{verbatim}
+/*
+ * A struct to define a single item of metadata
+ */
+typedef struct {
+    const int id;                       // unique ID for this item
+    
+    char *name;                         // Name of item
+    psMetaDataType type;                // type of this item
+    const union {
+        float f;                        // floating value
+        int i;                          // integer value
+        void *v;                        // other type
+    } val;                              // value of metadata
+    char *comment;                      // optional comment ("", not NULL)
+} psMetaDataItem;
+\end{verbatim}
+
+The \code{id} is a unique identifier for this item of metadata; experience
+shows that such tags are useful.
+
+The \code{psMetaDataType type} entry specifies the type of the data
+being represented; the possibilities are listed in section \ref{metadataTypes}.
+
+\subsection{Possible Types of Metadata}
+\label{metadataTypes}
+
+The possible types of metadata are identified by the enumerated type
+\code{psMetaDataType} (see also table \ref{tabMetaDataTypes}); the initial defined values are:
+\begin{verbatim}
+/*
+ * Possible types of metadata.  
+ */
+typedef enum {                          // type of val is:
+    psMetaFloat,                        // float (.f)
+    psMetaInt,                          // int (.i)
+    psMetaStr,                          // string (.v)
+    psMetaImg,                          // image (.v)
+    psMetaJPEG,                         // JPEG (.v)
+    psMetaPNG,                          // PNG (.v)
+    psMetaAstrom,                       // astrometric coefficients (.v)
+    psMetaUnknown,                      // other (.v)
+    psMetaNType                         // Number of types; must be last
+} psMetaDataType;
+\end{verbatim}
+
+\begin{table}
+\begin{tabular}{llll}
+\textbf{Value} & \textbf{Type} & \textbf{member of union} & \textbf{Comments}\\
+\hline
+psMetaFloat & float & f & value, not pointer, is stored \\
+psMetaInt & int & i &  value, not pointer, is stored \\
+psMetaStr & string & v &  value, not pointer to original, is stored \\
+psMetaImg & psImage & v &  \\
+psMetaJPEG & JPEG & v &  \\
+psMetaPNG & PNG & v &  \\
+psMetaAstrom & psAstrom & v &  \\
+psMetaUnknown & other & v &  \\
+psMetaNType & (none) & & The number of types defined
+\end{tabular}
+\begin{caption}{Supported Metadata Types}
+\label{tabMetaDataTypes}
+  Possible types of metadata
+\end{caption}
+\end{table}
+
+\section{Collections of Metadata}
+
+\begin{verbatim}
+typedef struct {
+    psDlist *list;			// list of psMetaDataItem
+    psHash *table;			// hash table of the same metadata
+} psMetaDataSet;
+\end{verbatim}
+
+The type \code{psMetaDataSet} is a container class for metadata. Note that there are
+in fact \emph{two} representations of the metadata (each \code{psMetaDataItem} appears
+on both).
+
+We are using the standard \PS{} doubly-linked list types \code{psDlist} and  \code{psHash}
+(see \href{file:utils#psDlist}{utils.pdf:psDlist} and \href{file:utils#psHash}{utils.pdf:psHash} for details).
+For example:
+\begin{verbatim}
+    for (int i = 0; i < 10; i += 5) {
+        float sqrti = sqrt(i);
+        psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_INT, &i, NULL, "const.%d", i));
+        psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_FLOAT, &sqrti, "square root", "const.sqrt%d", i));
+    }
+    psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_STR, "Bonjour", "French", "lang.hello"));
+    /*
+     * Remove a key
+     */
+    psMetaDataItemFree(psMetaDataRemove(ms, "lang.hello"));
+    /*
+     * Print all metadata
+     */
+    fprintf(stdout, "------\n");
+    psMetaDataSetIterator(ms);
+    while ((meta = psMetaDataGetNext(ms, NULL)) != NULL) {
+        psMetaDataItemPrint(stdout, meta, "");
+    }
+    /*
+     * Look up a key by name
+     */
+    fprintf(stdout, "------\n");
+    fprintf(stdout, "Looking up by name:\n");
+    meta = psMetaDataLookup(ms, "const.5");
+    if (meta != NULL) {
+        psMetaDataItemPrint(stdout, meta, "");
+    }
+\end{verbatim}
+
+\section{Naming convention for MetaData}
+
+The \code{psMetaDataItem} struct includes a name.  This name should be of
+the form \code{name1.name2.name3$\cdots$}, e.g.\hfil\break
+\null\qquad\qquad\code{IPP.phase1.ota12.biassec}.
+
+We shall set in place a system for assigning the top-level `domains'
+to responsible individuals, and for gathering a complete list of
+all metadata names in use throughout the project.
+
+\subsection{Support for Multiple Values for a Given Name}
+
+\begin{verbatim}
+    psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_STR | PS_META_NON_UNIQUE,
+                                           "Bonjour", "French", "lang.hello"));
+    psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_STR | PS_META_NON_UNIQUE,
+                                           "Aloha", "Hawaiian", "lang.hello"));
+    psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_STR | PS_META_NON_UNIQUE,
+                                           "Good Morning", "English", "lang.hello"));
+    /*
+     * Print all metadata starting "lang"
+     */
+    psMetaDataSetIterator(ms);
+    while ((meta = psMetaDataGetNext(ms, "lang")) != NULL) {
+        psMetaDataItemPrint(stdout, meta, "");
+    }
+\end{verbatim}
+
+It is an unfortunate fact that certain metadata keywords (such as \code{COMMENT} in a FITS header)
+may be repeated with different values.  The \code{psMetaDataAppend} routine is required
+to check that all metadata names are unique unless the type is qualified as \code{PS_META_NON_UNIQUE};
+in this case a unique integer will be added to each name that you specify. In this case,
+you may either delete individual element separately or as a complete set:
+\begin{verbatim}
+psMetaDataItemFree(psMetaDataRemove(ms, "lang.hello.0"));
+psMetaDataItemFree(psMetaDataRemove(ms, "lang.hello"));
+\end{verbatim}
+
+\appendix
+\section{MetaData APIs}
+
+\begin{verbatim}
+psMetaDataItem *psMetaDataItemAlloc(
+    psMetaDataType type,		// type of this piece of metadata
+    const void *val,			// value of new item
+    					// N.b. a pointer even if the item
+    					// is of type e.g. int
+    const char *comment,		// comment associated with item
+    const char *name,			// name of new item of metadata (may be an sprintf format)
+    ...);				// possible arguments for name format
+
+void psMetaDataItemFree(psMetaDataItem *ms); // piece of metadata to destroy
+
+psMetaDataSet *psMetaDataSetAlloc(void);	  // make a new set of metadata
+void psMetaDataSetDel(psMetaDataSet *ms); // destroy a set of metadata
+
+/*****************************************************************************/
+/*
+ * Utilities
+ */
+psMetaDataItem *psMetaDataAppend(psMetaDataSet *restrict ms, psMetaDataItem *restrict item);
+psMetaDataItem *psMetaDataRemove(psMetaDataSet *restrict ms, const char *restrict key);
+
+void psMetaDataSetIterator(psMetaDataSet *ms);
+psMetaDataItem *psMetaDataGetNext(psMetaDataSet *ms);
+psMetaDataItem *psMetaDataLookup(const psMetaDataSet *ms, const char *key);
+
+void psMetaDataItemPrint(FILE *fd,		// file descriptor to write to
+			 const psMetaDataItem *ms); // item of metadata to print
+\end{verbatim}
+
+\bibliographystyle{plain}
+\bibliography{panstarrs}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/draft/utils.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/draft/utils.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/draft/utils.tex	(revision 22322)
@@ -0,0 +1,1030 @@
+\documentclass[panstarrs]{panstarrs}
+
+\title{The \PS{} Utilities API}
+\author{Robert Lupton}
+\group{}
+\organization{}
+\docnumber{PSDC-???}
+
+\fancyhead{}
+\fancyhead[L]{\PS{} Document}
+\fancyhead[R]{PSDC-???}
+\fancyhead[C]{Revision 0.1 -- Draft}
+\fancyfoot[L]{\PS{} Utilities}
+\fancyfoot[R]{\today}
+
+\begin{document}
+
+\pagenumbering{roman} \thispagestyle{empty} \maketitle
+
+\pagebreak \pagestyle{fancy}
+{ \Large \bf Revision History}
+\begin{center}
+\begin{tabular}{|p{1in}|p{1in}|p{4.5in}|}
+\hline
+{\bf Revision Number} & {\bf Release Date} & {\bf Description} \\
+\hline
+%0.1 & 2003/12/08 & First draft \\
+\hline
+\end{tabular}
+\end{center}
+
+{ \Large \bf Referenced Documents}
+\begin{center}
+\begin{tabular}{|p{1in}|p{2.75in}|p{2.75in}|}
+\hline
+{\bf \PS{} ID} & {\bf Title} & {\bf Author} \\
+\hline
+%%% References here.
+\hline
+\end{tabular}
+\end{center}
+
+\pagebreak
+\tableofcontents
+
+% \pagebreak
+% \listoffigures
+
+\pagebreak \pagenumbering{arabic}
+
+\section{Memory Management}
+\hlabel{psMemBlock}
+
+\subsection{Introduction}
+
+The \PS{} software system will need a level of memory management
+placed between the operating system (malloc/free) and the high level
+routines (e.g. \code{psMetaDataAlloc}).
+
+This layer is in addition to the possibility that specific heavily
+used data types may need their own special-purpose memory managers;
+but as we have specified that all user-level objects be allocated via
+\code{typeAlloc/typeFree} functions, we will easily be able to
+implement such functionality without impacting on the facilities
+described here.
+
+All of the memory management APIs should be provided in a header file
+\file{psMemory.h} which is included by \file{psUtils.h}.
+
+\subsection{Rationale}
+
+We wish to insert our own layer of memory management for a number of
+reasons:
+
+\begin{itemize}
+\item
+  We wish to insulate ourselves from the details of the system-provided
+  \code{malloc}.  There is no guarantee that the goals of the system
+  architect align with those of the \PS{} processing
+
+\item
+  We need at least a wrapper layer which handles failed memory
+  requests without requiring the application programmer to check
+  every attempted allocation.
+
+\item
+  We need to provide a mechanism for tracking and fixing memory
+  leaks.  While it is possible to do this by linking with external
+  libraries (\tbd{reference}), it is convenient to do so within the
+  \PS{} framework.
+
+\item
+  Similarly, we wish to provide convenient hooks to detect and diagnose
+  memory corruption.
+
+\item
+  While debugging complex scientific code, it is very useful to be
+  able to trace a given data structure as it passed through the
+  processing pipeline.
+
+\item
+  There may be other features that we wish to add in the future (e.g.
+  associating a type with every allocation).
+\end{itemize}
+
+\subsection{A Minimal Specification}
+
+The previous section laid out a number of desiderata for a memory
+management system.  Rather than implement our own heap-manager at this
+stage of the project, I propose that we put in place a sufficient
+set of data structures and APIs, but initially implement only
+a simple subset (e.g. that we not implement a multi-bucket memory
+manager that takes in 100Mb chunks from the system and performs
+sophisticated defragmentation and garbage collection).
+
+Subject to agreement with the IfA, some of the functions proposed
+here may be omitted from the initial implementation; in particular
+the routines \code{psMemCheckCorruption} and \code{psMemCheckLeaks}
+may be hard to build efficiently in a simple implementation based
+upon adding extra fields to allocation requests.
+
+\subsubsection{Extra information to be saved by Memory Allocation Functions}
+
+It is required that a table of all allocated memory blocks be
+maintained by the \PS{} memory system.
+
+Each allocated block should preserve at least the information present
+in the following struct definition:
+\begin{verbatim}
+  typedef struct {
+      const unsigned long id;           // a unique ID for this allocation
+      const char *file;                 // set from __FILE__ in e.g. p_psAlloc
+      const int lineno;                 // set from __LINE__ in e.g. p_psAlloc
+      int refcntr;                      // how many times pointer is referenced
+      const void *magic;                // initialised to P_PS_MEMMAGIC
+  } psMemBlock;
+\end{verbatim}
+
+The value of \code{P_PS_MEMMAGIC} shall be \code{(void *)0xdeadbeef}%
+\footnote{Why this choice? Tradition, and because it's easy to notice
+  in a hex dump.}
+
+\subsubsection{APIs for using Memory Allocation Functions}
+
+\begin{table}
+\begin{verbatim}
+void *psAlloc(size_t size);
+void *psRealloc(void *ptr, size_t size);
+void psFree(void *ptr);
+
+void *p_psAlloc(size_t size, const char *file, int lineno);
+void *p_psRealloc(void *ptr, size_t size, const char *file, int lineno);
+void p_psFree(void *ptr, const char *file, int lineno);
+
+#define psAlloc(size) p_psAlloc(size, __FILE__, __LINE__)
+#define psRealloc(ptr, size) p_psRealloc(ptr, size, __FILE__, __LINE__)
+#define psFree(size) p_psFree(size, __FILE__, __LINE__)
+\end{verbatim}
+\begin{caption}{The APIs required to use the \PS{} memory allocation routines}
+  \hlabel{tabUsageAPI}
+  The APIs required to use the \PS{} memory allocation routines, and as
+  provided by \file{psMemory.h}.
+\end{caption}
+\end{table}
+
+The types and function prototypes for the part of the memory API
+concerned with allocating and freeing memory are given in table
+\ref{tabUsageAPI}).
+
+N.b.
+\begin{itemize}
+\item
+  The functions \code{psAlloc}, \code{psRealloc}, and
+  \code{psFree} are defined, and are required to be equivalent to
+  \code{p_psAlloc}, \code{p_psRealloc}, and \code{p_psFree}
+  with the final two arguments \code{"(unknown)"} and \code{0}.
+
+  In the descriptions that follow, I shall not distinguish between the
+  functions with and without the \code{p_} prefix.
+
+\item
+  Except as specified below, the functions \code{psAlloc},
+  \code{psRealloc}, and \code{psFree} have identical semantics
+  to the standard C library functins \code{malloc}, \code{realloc},
+  and \code{free}.
+  
+\item
+  The file \file{psMemory.h} shall take steps to ensure that
+  code calling the functions \code{malloc}, \code{calloc}, \code{realloc},
+  or \code{free} shall not compile (\eg{} \code{#define malloc(S) for})
+  unless the symbol \code{PS_ALLOC_MALLOC} is defined.
+
+\item
+  In all cases, application code will call
+  \code{p_\{psAlloc,psRealloc,psFree\}} via the macros defined above.
+  
+\item
+  The functions \code{psAlloc} and \code{psRealloc} shall never
+  return a \code{NULL} pointer. If they are unable to provide
+  the requested memory they should attempt to obtain the desired
+  memory by calling the routine registered by \code{psMemExhaustedSetCB} (see
+  subsubsection \ref{secMemAdvanced}), and if still unsuccessful,
+  call \code{abort()}.
+
+\item
+  The memory management routines shall maintain the field
+  \code{psMemBlock.refcntr}. It should be set to \code{1}
+  when a block is returned to the user. It is an error to
+  attempt to free a block with \code{psMemBlock.refcntr != 1}
+  (see subsubsection \ref{secMemRefcounter}).
+  
+\item
+  Where practical and efficient, the memory manager shall call
+  the routine registered using the \code{psMemProblemSetCB}
+  (see section \ref{secMemAdvanced})
+  whenever a corrupted block of memory is discovered. For example,
+  doubly-freed blocks can be detected by checking \code{psMemBlock.refcntr}.
+
+\item
+  There is no \code{psCalloc} function. Initialisation of data is
+  almost always more complex than setting all bytes to 0.
+\end{itemize}
+
+\subsubsection{APIs for Tracing and Debugging Memory}
+\hlabel{secMemAdvanced}
+
+\begin{table}
+\begin{verbatim}
+typedef int (*psMemCallback)(const psMemBlock *ptr);
+typedef void (*psMemProblemCallback)(const psMemBlock *ptr,
+                                     const char *file, int lineno);
+typedef void *(*psMemExhaustedCallback)(size_t size);
+
+psMemProblemCallback psMemProblemSetCB(psMemProblemCallback func);
+psMemExhaustedCallback psMemExhaustedSetCB(psMemExhaustedCallback func);
+
+psMemCallback psMemAllocateSetCB(psMemCallback func);
+psMemCallback psMemFreeSetCB(psMemCallback func);
+
+int psMemGetId(void);                   // get next memory ID
+
+long psMemSetAllocateID(long id);       // set p_psMemAllocateID to id
+long psMemSetFreeID(long id);           // set p_psMemFreeID to id
+
+int psMemCheckLeaks(
+    int id0,                            // don't list blocks with id < id0
+    psMemBlock ***arr,                  // pointer to array of pointers to
+                                        //leaked blocks, or NULL
+    FILE *fd);                          // print list of leaks to fd (or NULL)
+int psMemCheckCorruption(int abort_on_error);
+\end{verbatim}
+\begin{caption}{The APIs required to use the \PS{} memory allocation routines}
+  \hlabel{tabCallbackAPI}
+  The APIs required to use the callback and tracing facilities
+  in \PS{} memory allocation routines, as defined in \file{psMemory.h}.
+\end{caption}
+\end{table}
+
+The types and function prototypes for this part of the memory API
+are given in table \ref{tabCallbackAPI}.
+
+\subsubsection{Callback Routines}
+
+The four `\code{SetCB}' routines are:
+
+\begin{tabular}{ll|l}
+\textbf{type} &  \textbf{Name} & \textbf{Function of callback} \\
+psMemProblemCallback & psMemProblemSetCB &
+Called when a problem is detected with data being managed on the heap \\
+psMemExhaustedCallback & psMemExhaustedSetCB &
+Called when \code{psAlloc} is unable to satisfy a memory request. \\
+psMemCallback & psMemAllocateSetCB &
+Callback is called when the \code{psMemBlock} with a specified ID is
+allocated. \\
+psMemCallback & psMemFreeSetCB &
+Callback is called when the \code{psMemBlock} with a specified ID is
+freed. \\
+\end{tabular}
+
+N.b.
+\begin{itemize}
+\item
+  In all cases, the `\code{SetCB}' routine takes a pointer to the
+  desired callback function, and returns a pointer to the one that was
+  previously installed. If the function pointer is \code{NULL}, the
+  default callback function is reinstalled.
+
+\item
+  The routine installed by \code{psMemExhaustedSetCB} should return
+  a pointer to the desired memory or \code{NULL}; in the latter case
+  \code{psAlloc} will call \code{abort()}
+
+\item
+  \code{psMemGetId} returns the current value of \code{psMemBlock.id}.
+
+\item
+  The handler specified by \code{psMemAllocateSetCB} is called just
+  before the pointer with \code{psMemBlock.id} equal to
+  \code{p_psMemAllocateID} is returned to the user. The variable
+  \code{p_psMemAllocateID} should not be set directly in any
+  delivered code, \code{psMemSetAllocateID} should be used instead.
+
+\item
+  The handler specified by \code{psMemFreeSetCB} is called just
+  before the pointer with \code{psMemBlock.id} equal to
+  \code{p_psMemFreeID} is freed. The variable
+  \code{p_psMemFreeID} should not be set directly in any delivered
+  code, \code{psMemSetFreeID} should be used instead.
+
+\item
+  The routines \code{psMemSetFreeID} and \code{psMemSetAllocateID}
+  accept the desired ID value (see previous two items) and return
+  the old value to the user.
+
+\item
+  The return values of the handlers installed by \code{psMemAllocateSetCB}
+  and \code{psMemFreeSetCB} are used to increment the values of
+  \code{p_psMemAllocateID} and \code{p_psMemFreeID} respectively.
+
+  For example, the return value \code{0} implies that the value is unchanged;
+  if the value is \code{2} the callback will be called again when the
+  memory ID counter has increased by two.
+\end{itemize}
+
+\subsubsection{Memory Tracing and Corruption Routines}
+
+The routine \code{psMemCheckLeaks} may be used to check for memory
+leaks. The return value is the number of blocks that have been
+allocated but not freed.
+
+Only blocks with \code{psMemBlock.id} greater than \code{id0}
+are checked; this allows the user to ignore blocks allocated
+by initialisation routines.
+
+If the argument \code{arr} is non-\code{NULL}, then upon entering
+the call \code{**arr} should be \code{NULL}. Upon return it is set
+to an array of \code{psMemBlock *} pointers, one for each block
+allocated but not freed.  It is the caller's responsibility to free
+this array with \code{psFree}.
+
+If the argument \code{fd} is non\code{NULL}, a one-line summary
+of each block that has been allocated but not freed is written to that
+file descriptor.
+
+The routine \code{psMemCheckCorruption} checks the entire heap for
+corruption, calling the routine registered with
+\code{psMemProblemSetCB} for each block detected as being corrupted.
+The return value is the number of corrupted blocks detected. If the
+argument \code{abort_on_error} is true,
+\code{psMemCheckCorruption} shall call \code{abort()} as soon as
+memory corruption is detected.
+
+\subsubsection{Reference Counting}
+\hlabel{secMemRefcounter}
+
+The memory management routines include a field
+\code{psMemBlock.refcntr} which must equal \code{1} whenever
+a pointer is presented to \code{psFree}.
+
+The API for this field is:
+\begin{verbatim}
+int psMemGetRefCounter(void *vptr);        // return refcounter
+void *psMemIncrRefCounter(void *vptr);     // increment refcounter and return vptr
+void *psMemDecrRefCounter(void *vptr);     // decrement refcounter and return vptr
+\end{verbatim}
+
+The functions \code{psMemIncrRefCounter} and \code{psMemDecrRefCounter} shall
+return \code{NULL} if passed a \code{NULL} pointer.
+
+\subsubsubsection{Rationale}
+
+\begin{table}
+\begin{verbatim}
+typedef struct {
+    char *name;
+    int value;
+} psSimple;
+
+psSimple *psSimpleAlloc(const char *name, int val)
+{
+    psSimple *simp = psAlloc(sizeof(psSimple));
+    simp->name = strcpy(psAlloc(strlen(name) + 1), name);
+    simp->value = val;
+
+    return simp;
+}
+
+void psSimpleFree(psSimple *simp)
+{
+    if (simp == NULL) { return; }
+
+    if (psMemGetRefCounter(simp) > 1) {
+        (void)psMemDecrRefCounter(simp);
+        return;
+    }
+}
+\end{verbatim}
+\begin{caption}{An example of reference counting}
+  \hlabel{tabReferenceCounting}
+  An example of using reference counting
+\end{caption}
+\end{table}
+
+The \code{psMemBlock.refcounter} is clearly useful for detecting
+attempts to free memory that is already free.  A more complex
+application is for allowing pointers to complex data-objects (e.g.
+images) to appear in more than one data structure simultaneously
+(see table \ref{tabReferenceCounting}).
+
+Because of the use of the \code{refcounter} field, we can safely put items of
+this type onto many lists:
+\goodbreak
+\begin{verbatim}
+simp = psSimpleAlloc("RHL", 0);
+psDlistAppend(list1, psMemIncrRefCounter(simp));
+psDlistAppend(list2, psMemIncrRefCounter(simp));
+psSimpleFree(simp);
+\end{verbatim}
+
+(Note: in fact there is no need to explicitly increment the counter
+in this case, as the \code{psDlistAppend} (section \ref{psDlist})
+API specifies that it
+does it for you.)
+
+\section{Tracing and Logging}
+
+This document defines the \PS{} Tracing and Logging APIs; the former
+refers to debug information that we wish to be able to turn on and off
+without recompiling (although it will \emph{not} be available in
+production code); the latter means information about the processing
+that must be collected and saved, even in the production system.
+
+We envision that we will make extensive use of \code{psTrace} throughout
+the \PS{} code.
+
+\subsection{Tracing APIs}
+\hlabel{psTrace}
+
+\begin{table}
+  \begin{verbatim}
+#if defined(PS_NTRACE)
+#  define psTrace(facil, level, ...) /* do nothing */
+#else
+#  define psTrace(facil, level, ...) \
+          p_psTrace(facil, level, __VA_ARGS__)
+#endif
+
+void p_psTrace(const char *facil, int level, ...);
+
+int psSetTraceLevel(const char *facil,  // facilty of interest
+                    int level);         // desired trace level
+int psGetTraceLevel(const char *name);  // facilty of interest
+
+void psTraceReset(void);		// turn off all tracing, and free trace's allocated memory
+
+void psPrintTraceLevels(void);          // print trace levels
+  \end{verbatim}
+  \begin{caption}{The public API for the trace facility
+      from \file{psTrace.h}.}
+    \hlabel{tabTraceAPI}
+  \end{caption}
+\end{table}
+
+The public API for the trace facility (table \ref{tabTraceAPI})
+should be provided in a header file \file{psTrace.h} which
+is included by \file{psUtils.h}.
+
+\begin{itemize}
+  \item
+    Logging is provided by the function \code{psTrace},
+    which is actually a macro.  When the macro \code{PS_NTRACE}
+    is defined, all occurrences of  \code{psTrace} shall
+    be replaced by whitespace.
+
+  \item
+    The first argument to \code{psTrace} is a name of the form
+    \code{aaa.bbb.ccc}. The second is an integer,
+    \code{level}. The remaining arguments are a printf-style format
+    and a (possibly empty) set of values for that formatting
+    string. When this trace is active, \code{psTrace} shall generate
+    the requested output on \code{stdout}, preceded by \code{level}
+    spaces.
+
+  \item
+    The call \code{psSetTraceLevel(name, level)} shall set the trace
+    level for \code{name} to \code{level}.
+
+  \item
+    The call \code{psGetTraceLevel(name)} shall return the level
+    associated with \code{name}.  If \code{name} has not been
+    associated with a level, the routine shall remove the \textit{last}
+    element of \code{name} and attempt to look up its level; this
+    procedure shall be applied recursively until the name is reduced
+    to \code{""}; if this name has no level associated with
+    it, then the value \code{0} shall be returned (see examples
+    below).
+
+    \item
+      The call \code{psTrace(name, level, ...)}
+      shall print the message if and only if
+      \code{psGetTraceLevel(name)} returns a value greater than
+      or equal to \code{level}.
+
+    \item
+      \code{psPrintTraceLevels} shall print a listing of all
+      declared levels, displayed as a hierarchy with sub-components
+      sorted within each component (see examples below).
+
+      Note in particular that the root of the tree, the name \code{""}
+      should print as \code{(root)}, and nodes which have not
+      been assigned a value should list their level as \code{.}.
+
+    \item
+      All of the tracing facilities shall be SWIGed, with the exception
+      of \code{p_psTrace}.
+
+    \item
+      There is no requirement that \code{psTrace} be usable from
+      within the functions that implement \file{psTrace.h}
+      or \file{psMemory.h} systems.
+
+    \item
+      \code{psTraceReset} shall reset all tracing to the state that it
+      had at program initiation, including freeing any memory that the
+      tracing subsystem may have allocated.
+\end{itemize}
+
+\subsubsection{Examples of the use of Tracing Facilities}
+
+For example, after the commands:
+\begin{verbatim}
+    psSetTraceLevel("utils.dlist.add.head", 9);
+    psSetTraceLevel("utils.dlist.add", 3);
+    psSetTraceLevel("utils.dlist.remove", 4);
+    psSetTraceLevel("coadd", 2);
+    psSetTraceLevel("coadd.CR.remove.morphology", 5);
+    psSetTraceLevel("utils.hash", 2);
+    psSetTraceLevel("utils.dlist.add", 9);
+    psSetTraceLevel("utils", 1);
+\end{verbatim}
+the command \code{psPrintTraceLevels()} should print:
+\begin{verbatim}
+(root)               0
+ utils               1
+  hash               2
+  dlist              .
+   remove            4
+   add               9
+    head             9
+ coadd               2
+  CR                 .
+   remove            .
+    morphology       5
+\end{verbatim}
+where \code{.} means that the trace level should be inherited from its parent.
+
+After this set of \code{psSetTraceLevel} commands, and if
+\code{PS_NTRACE} is not defined, the following commands
+\begin{verbatim}
+    psTrace("utils.dlist.remove", 2, "Removing psDList key \"%s\"\n", "my_key");
+    psTrace("utils", 2, "Initialising utilities library\n");
+    psTrace("", 2, "This is turned on by trace component \"\"");
+    psTrace("utils.dlist", 2, "Initialising psDList\n");
+    psTrace("utils.dlist.remove", 4, "Removing psDList key \"%s\" (value: \"%d\")\n", "my_key", 12345);
+    psTrace("utils.hash.remove", 4, "Removing hash key \"%s\" (value: \"%d\")\n", "my_key", 12345);
+    psTrace("utils.dlist.add", 1, "Adding psDList key \"%s\"\n", "your_key");
+    psTrace("utils.dlist.find", 2, "Looking up psDList key \"%s\"\n", "some_key");
+    psTrace("coadd.CR.remove", 4, "Removing CRs\n");
+    psTrace("coadd.CR.remove.morphology", 4, "CRs are not fuzzy\n");
+\end{verbatim}
+should produce this output:
+\begin{verbatim}
+  Removing psDList key "my_key"
+    Removing psDList key "my_key" (value: "12345")
+ Adding psDList key "your_key"
+    CRs are not fuzzy
+\end{verbatim}
+
+Note that
+\begin{description}
+\item
+  \code{utils.dlist} messages are at level 1, inherited from \code{utils}, so the
+  \code{Initialising utilities library}, \code{Initialising psDList}, and
+  \code{Looking up psDList key} messages are \emph{not} printed (the traces are at level 2).
+
+\item
+  \code{utils.dlist.remove} messages are at level 4, and are printed.
+
+\item
+  \code{utils.hash} messages are at level 2, and are not printed (the traces are at level 4)
+
+\item
+  \code{coadd.CR.remove} is at level 2 (inherited from \code{coadd}) so \code{Removing CRs}
+  isn't printed.  \code{coadd.CR.remove.morphology} is at level 4, so \code{CRs are not fuzzy} is printed.
+\end{description}
+
+\subsection{Message Logging}
+\hlabel{psLogMsg}
+
+\begin{table}
+  \begin{verbatim}
+enum { PS_LOG_ABORT = 0, PS_LOG_ERROR, PS_LOG_WARN, PS_LOG_INFO };
+
+enum { PS_LOG_TO_STDERR, PS_LOG_TO_STDOUT };
+
+void psLogMsg(const char *name, int level, const char *fmt, ...);
+void p_psVLogMsg(const char *name, int level, const char *fmt, va_list ap);
+
+int psSetLogDestination(int dest);
+void psSetLogFormat(const char *fmt);
+
+int psSetLogLevel(int level);
+  \end{verbatim}
+  \begin{caption}{API for message logging}
+    \hlabel{tabLogMsgAPI}
+    The API for message logging.
+  \end{caption}
+\end{table}
+
+The public API for the logging facility (table \ref{tabLogMsgAPI})
+should be provided in a header file \file{psLogMsg.h} which
+is included by \file{psUtils.h}.
+
+The function \code{psSetLogLevel} may be used to set the current
+level of logging; the previous value is returned.
+The default value is \code{PS_LOG_INFO}. Valid values are 0---9
+inclusive (note that only the first four are required to have
+symbolic names).
+
+A call to \code{psLogMsg(name, level, msg, ...)} shall generate
+a log message if \code{level} is less than or equal to the
+value most recently set using \code{psSetLogLevel}. The function
+\code{p_psLogMsg} is identical, except that it expects a
+final \code{va_list} argument.
+
+The format of the log message shall be of the form:
+\begin{verbatim}
+YYYY:MM:DD hh:mm:ssZ|hostname|l|name            |msg
+\end{verbatim}
+\code{YYYY}, \code{MM}, \code{DD}, \code{hh}, \code{mm}, and \code{ss}
+are the year, month (Jan == 1), day of the month, hours (0--23),
+minutes, and seconds when the log message was received.  Note that the
+timestamp is in ISO order, and that the timezone is GMT (hence the
+\code{Z}).
+
+The \code{hostname} is returned by \code{gethostname}, \code{l} is a
+letter associated with the level (\code{A}, \code{E}, \code{W}, and
+\code{I} for \code{PS_LOG_ABORT}, \code{PS_LOG_ERROR}, \code{PS_LOG_WARN},
+and \code{PS_LOG_INFO} respectively. Other levels are represented
+numerically (\code{5} etc.). The other two field, \code{name} and
+\code{msg} are arguments to \code{psLogMsg}; note that \code{name} has
+a fixed width of 15 characters. If \code{msg} doesn't end in a newline,
+a single newline is emitted to terminate the message.
+
+An example message is:
+\begin{verbatim}
+2004:02:24 20:14:18Z|alibaba.IfA.Hawaii.Edu|I|utils          |Hello World
+\end{verbatim}
+
+Log messages are sent to the destination most recently set using
+\code{psSetLogDestination}.  The only two values that are initially
+defined are \code{PS_LOG_TO_STDERR} and \code{PS_LOG_TO_STDOUT} to
+write to \code{stderr} and \code{stdout} respectively.
+
+The fields included in the log message may be controlled using \code{psSetLogFormat} which
+expects a string consisting of the letters \code{H} (host), \code{L} (level), \code{M} (message),
+\code{N} (name), and \code{T} (time).  The default is \code{HLMNT}.
+
+\section{The Pan-STARRS \texttt{psDlist} doubly-linked list type}
+\hlabel{psDlist}
+
+Pan-STARRS supports doubly linked lists through a type \code{psDlist}.
+The type is defined in the header file \file{psDlist.h}, and consists
+of the following definitions:
+
+\begin{verbatim}
+typedef struct psDlistElem {
+    struct psDlistElem *prev;           // previous link in list
+    struct psDlistElem *next;           // next link in list
+    void *data;                         // real data item
+} psDlistElem;
+
+typedef struct {
+    int n;                              // number of elements on list
+    psDlistElem *head;                  // first element on list (may be NULL)
+    psDlistElem *tail;                  // last element on list (may be NULL)
+    psDlistElem *iter;                  // iteration cursor; private
+} psDlist;
+
+enum {                                  // Special values of index into list
+    psDlistHead = 0,                    // at head
+    psDlistTail = -1,                   // at tail
+    psDlistUnknown = -2,                // unknown position
+    psDlistPrev = -3,                   // previous element
+    psDlistNext = -4                    // next element
+};
+\end{verbatim}
+
+The API is:
+
+\begin{verbatim}
+psDlist *psDlistAlloc(void *data);        // initial data item; may be NULL
+
+void psDlistFree(psDlist *list,          // list to destroy
+                void (*elemFree)(void *)); // destructor for list data, or NULL
+
+/*
+ * List maintainence functions
+ */
+psDlist *psDlistAdd(psDlist *list,      // list to add to (may be NULL)
+                    void *data,         // data item to add
+                    int where);         // index, psDlistHead, or psDlistTail
+psDlist *psDlistAppend(psDlist *list,   // list to append to (may be NULL)
+                       void *data);     // data item to add
+void *psDlistRemove(psDlist *list,      // list to remove element from
+                    void *data,         // data item to remove
+                    int which);         // index of item, or psDlistUnknown,
+                                        // or psDlistNext, or psDlistPrev
+void *psDlistGet(const psDlist *list,   // list to retrieve element from
+                 int which);            // index of item, or psDlistNext,
+                                        // or psDlistPrev
+/*
+ * Conversions to/from arrays
+ */
+psVoidPtrArray *psDlistToArray(psDlist *dlist);
+psDlist *psArrayToDlist(psVoidPtrArray *arr);
+\end{verbatim}
+
+All data items placed onto lists (e.g. with \code{psDlistAdd})
+shall have their reference counters (section \ref{secMemRefcounter}) incremented.
+When elements
+are removed from a list with \code{psDlistRemove}, they shall
+have their reference counters decremented. The action of retrieving
+data from a list (with \code{psDlistGet}) shall not affect
+their reference counter.
+
+If \code{psDlistFree}'s argument \code{elemFree} is NULL, the
+list should be deleted, but not the elements on it (although their
+\code{refcounter}'s should be decremented).
+
+Iteration over all elements of the list is provided by the functions:
+\begin{verbatim}
+void psDlistSetIterator(psDlist *list, int where, int which);
+void *psDlistGetNext(psDlist *list, int which);
+void *psDlistGetPrev(psDlist *list, int which);
+\end{verbatim}
+in which the \code{where} argument must be \code{PS_DLIST_HEAD} or \code{PS_DLIST_TAIL},
+to start at the head or tail of the list, and \code{psDlistGetNext} and \code{psDlistGetPrev}
+return the next/previous element. The argument \code{which} identifies which of potentially
+many iteration cursors should be used; it must currently always be \code{0}.
+
+Explicit traversal of the list using the \code{psDlistElem}'s
+\code{prev} and \code{next} pointers is also supported.
+
+The routines to convert to and from \code{psVoidPtrArray}s,
+\code{psDlistToArray} and \code{psArrayToDlist} shall ensure that the
+objects on the arrays and lists have had their reference pointers
+correctly incremented (see section \ref{secArrayVoidPtr}) (\eg{} that
+\code{psArrayToDlist(psDlistToArray(list))} returns a properly-formed
+\code{psDlist}).
+
+\section{The \PS{} Array types}
+
+\begin{table}
+  \begin{verbatim}
+  \end{verbatim}
+  \begin{caption}{The array creation macros defined in \file{psArray.h}}
+    \hlabel{tabPsArray}
+  \end{caption}
+\end{table}
+
+\subsection{Arrays of Simple Types}
+
+Any \PS/ datatype \code{psType} may be associated with an array type
+\code{psTypeArray}:
+\begin{verbatim}
+typedef struct {
+  int size;
+  int n;
+  psType *arr;
+} psTypeArray;
+\end{verbatim}
+with associated constructors and a destructor:
+\begin{verbatim}
+psTypeArray *psTypeAlloc(int n, int size);
+psTypeArray *psTypeRealloc(psTypeArray *arr, int n);
+void psTypeFree(psTypeArray *arr);  
+\end{verbatim}
+
+The argument \code{n} is the dimension of the array; \code{size}
+is the number of elements allocated ($s \ge n$).
+
+This type and functions may be declared and defined using two macros
+from \file{psArray.h} (table \ref{tabPsArray}),
+\code{PS_DECLARE_ARRAY_TYPE(psType)} and
+\code{PS_CREATE_ARRAY_TYPE(psType)}.  The former defines the typedef
+and declares the prototypes (and is thus suitable for use in a
+header file); the latter generates the code for the three functions
+\code{psType(Alloc|Realloc|Free} (and should thus appear in exactly one
+source file for a given type).
+
+The \code{psType} should be a single word (e.g. \code{psXY}); in particular,
+there is no requirement to support a pointer type (\eg{} \code{psXY *});
+see next section.
+
+\subsection{Arrays of Pointer Types}
+
+The data type created with \code{PS_CREATE_ARRAY_TYPE} (\code{psType})
+contains an array of \code{psType}s not
+pointers to \code{psType}s; this means that the individual elements are
+not allocated using \code{psTypeAlloc}, are not correctly initialized,
+and shouldn't be individually deleted with \code{psTypeFree};
+
+If you wish to use arrays of pointers, use the macros
+\code{PS_DECLARE_ARRAY_PTR_TYPE(psType)} and
+\code{PS_CREATE_ARRAY_PTR_TYPE(psType)} (table \ref{tabPsArray}). These
+create types \code{typedef psType *psTypePtr} and \code{psTypePtrArray}:
+\begin{verbatim}
+typedef struct {
+  int size;
+  int n;
+  psTypePtr *arr;
+} psTypePtrArray;
+\end{verbatim}
+with associated constructors and a destructor:
+\begin{verbatim}
+psTypePtrArray *psTypePtrAlloc(int n, int size);
+psTypePtrArray *psTypePtrRealloc(psTypePtrArray *arr, int n);
+void psTypePtrArrayFree(psTypePtrArray *arr);  
+\end{verbatim}
+
+These constructors create arrays of \code{psType *} and call
+\code{psTypeAlloc} and \code{psTypeFree} to allocate and free the
+elements. As for the simple arrays, The former defines the typedef and
+declares the prototypes (and is thus suitable for use in a header
+file) and the latter generates the code for the three functions
+\code{psType(Alloc|Realloc|Free} (and should thus appear in exactly one
+source file for a given type).
+
+The objects pointed to by these types have had their \code{refCounter}s
+incremented (see \ref{secMemRefcounter}); to remove an element from the array you
+need to say something like:
+\begin{verbatim}
+  psTypePtrArray *pt = psTypePtrArrayAlloc(10, 10);
+  psType *xy = psMemDecrRefCounter(pt->arr[0]);
+  pt->arr[0] = NULL;
+\end{verbatim}
+
+\subsubsection{Arrays of \texttt{void *}}
+\hlabel{secArrayVoidPtr}
+
+Arrays of \code{void *} are different, as the need an explicitly-specified
+destructor.
+
+\file{psArray.h} shall specify a type \code{psVoidPtrArray} that
+behaves in all respects as if it had been created with:
+\begin{verbatim}
+typedef void *psVoidPtr;
+PS_DECLARE_ARRAY_TYPE(psVoidPtr);
+PS_CREATE_ARRAY_TYPE(psVoidPtr);
+\end{verbatim}
+except that its destructor is specified as:
+\begin{verbatim}
+void psVoidPtrArrayFree(psVoidPtrArray *arr,      // array to destroy
+                       void (*elemFree)(void *)); // destructor for array data
+\end{verbatim}
+
+The routine \code{psVoidPtrArrayFree} assumes that all pointers
+had their reference counters incremented
+when they were inserted onto the array.\footnote{%
+  \eg{} \code{va->arr[i] = psMemIncrRefCounter(ptr);}}
+
+If \code{psVoidPtrArrayFree}'s argument \code{elemFree} is NULL, the
+list should be deleted, but not the elements on it (although their
+\code{refcounter}'s should be decremented).
+
+\subsection{Examples of Array Types}
+
+The following is a complete C program that illustrates the use of
+\code{array}s.
+\begin{verbatim}
+#include "psUtils.h"
+
+typedef struct {
+    int x, y;
+} psXY;
+
+psXY *psXYAlloc(void)
+{
+    return psAlloc(sizeof(psXY));
+}
+
+void psXYFree(psXY *xy)
+{
+    psFree(xy);
+}
+
+PS_DECLARE_ARRAY_TYPE(psXY);
+PS_CREATE_ARRAY_TYPE(psXY);
+
+PS_DECLARE_ARRAY_PTR_TYPE(psXY);
+PS_CREATE_ARRAY_PTR_TYPE(psXY);
+
+int main(void)
+{
+    psXYArray *t = psXYArrayAlloc(10, 15);
+    psXYPtrArray *pt = psXYPtrArrayAlloc(10, 10);
+
+    for (int i = 0; i < t->n; i++) {
+	t->arr[i].x = i;
+	pt->arr[i]->y = 10*i;
+    }
+
+    t = psXYArrayRealloc(t, 5);
+    t = psXYArrayRealloc(t, 8);
+
+    for (int i = 0; i < t->n; i++) {
+	printf("%d %d  ", t->arr[i].x, pt->arr[i]->y);
+    }
+    printf("\n");
+    
+    psXYArrayFree(t);
+
+    psXY *xy = psMemDecrRefCounter(pt->arr[0]);
+    pt->arr[0] = NULL;
+    psXYFree(xy);    
+    
+    psXYPtrArrayFree(pt);
+
+    psMemCheckLeaks(0, NULL, stderr);
+
+    return 0;
+}
+\end{verbatim}
+
+\section{Hash Tables}
+\hlabel{psHash}
+
+\begin{table}
+  \begin{verbatim}
+typedef struct HashTable psHash;
+
+psHash *psHashAlloc(int nbucket);       // initial number of buckets
+void psHashFree(psHash *table,          // hash table to be freed
+               void (*itemFree)(void *item)); // how to free hashed data;
+                                        // or NULL
+
+void *psHashInsert(psHash *table,       // table to insert in
+                   const char *key,     // key to use
+                   void *data,          // data to insert
+                   void (*itemFree)(void *item)); // how to free hashed data;
+                                        // or NULL
+void *psHashLookup(psHash *table,       // table to lookup key in
+                   const char *key);    // key to lookup
+
+void *psHashRemove(psHash *table,       // table to lookup key in
+                   const char *key);    // key to lookup
+  \end{verbatim}
+  \begin{caption}{The public API for hash tables from \file{psHash.h}}
+    \hlabel{tabPsHash}
+  \end{caption}
+\end{table}
+
+The public API for the hash table (table \ref{tabPsHash})
+should be provided in a header file \file{psHash.h} which
+is included by \file{psUtils.h}.
+\footnote{
+  We choose not to use the posix function \code{hcreate},
+  \code{hdestroy}, and \code{hsearch} as they only support
+  a single hash table at any one time.}
+
+A hash table is an abstract type \code{psHash}.  The argument
+\code{nbucket} to \code{psHashAlloc} is a non-binding suggestion
+from the user for the initial size of the hash table.
+
+If the \code{itemFree} argument to \code{psHashFree} is non-NULL,
+it will be used to delete the data items that have been stored
+in the hash table; if it is NULL this is the responsibility of
+the caller.
+
+The routine \code{psHashInsert} must provide a non-NULL \code{itemFree}
+argument if it wishes to change the value previously inserted keys;
+if \code{itemFree} is NULL attempting to insert a pre-existing key
+is an error, and the routine will return NULL.  If  \code{psHashInsert}
+succeeds it returns \code{data}.
+
+\code{psHashLookup} returns the \code{data} associated with the
+key, or NULL if the key's invalid.
+
+\code{psHashRemove} removes the entry associated with the
+key from the table, and returns the \code{data}; if the key's invalid it returns NULL.
+
+\section{Miscellaneous Utilities}
+
+The API for miscellaneous \PS{} utilities is provided by \file{psMisc.h}
+which shall be included by \file{psUtils.h}.
+
+\begin{table}
+  \begin{verbatim}
+#define PS_CONCAT(A, B) A ## B          // Expands to AB
+#define PS_CONCAT2(A, B) PS_CONCAT(A, B) // Also expands to AB
+#define PS_CONCAT3(A, B, C) A ## B ## C // Expands to ABC
+
+#define PS_STRING(S) #S                 // converts argument S to string
+    
+void psAbort(const char *name, const char *fmt, ...);
+void psError(const char *name, const char *fmt, ...);
+char *psStringCopy(const char *str);
+  \end{verbatim}
+  \begin{caption}{The utilities provided by \file{psMisc.h}}
+      \hlabel{psMisc}
+  \end{caption}
+\end{table}
+
+\code{psAbort} shall call \code{psLogMsg} with a level of \code{PS_LOG_ABORT},
+and then call \code{abort}.
+\code{psError} shall call \code{psLogMsg} with a level of \code{PS_LOG_ERROR},
+and then return.
+In cases of doubt, a good choice for
+\code{name} is \code{__func__}.
+
+\code{psStringCopy} shall allocate and return a copy of the input string.
+
+
+\bibliographystyle{plain}
+\bibliography{panstarrs}
+
+
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/dvo/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/dvo/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/dvo/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.log *.dvi *.aux *.toc *.log *.out *.lof *.tbr *.tbd *.pdf
Index: /tags/sj_tags/sj_root_20080929/doc/dvo/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/dvo/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/dvo/Makefile	(revision 22322)
@@ -0,0 +1,29 @@
+
+PDFLATEX = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): pdflatex
+PSLATEX  = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): latex
+
+help:
+	@echo "USAGE: make (target)"
+	@echo "  targets: dvo all"
+
+dvo: dvo.pdf 
+all : dvo
+
+%.pdf: %.tex
+	$(PSLATEX) $*.tex 
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	thumbpdf --modes=dvips $*.pdf
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	@rm -f $*.ps $*.dvi $*.aux $*.log $*.tbr $*.tbd $*.toc $*.tpm $*.lof body.tmp head.tmp
+
+clean :
+	$(RM) *.log *.dvi *.aux *.toc *.tbd *.tbr *.tpm *.lof *.out *~ core body.tmp head.tmp
+
+dist : clean
+	$(RM) *.pdf
+
+empty: clean
Index: /tags/sj_tags/sj_root_20080929/doc/dvo/dvo.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/dvo/dvo.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/dvo/dvo.tex	(revision 22322)
@@ -0,0 +1,1611 @@
+\documentclass[panstarrs,spec]{panstarrs}
+
+\title{DVO Software Design Description}
+\subtitle{The Desktop Virtual Observatory: \\ Astronomical Object Databasing in the IPP}
+\author{Eugene Magnier}
+\audience{IPP}
+\shorttitle{DVO SDD}
+\group{Pan-STARRS IPP}
+\project{Pan-STARRS IPP}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-016}
+
+\begin{document}
+\maketitle
+
+\tableofcontents
+\pagebreak 
+\pagenumbering{arabic}
+
+\section{Overview}
+
+DVO, the Desktop Virtual Observatory, is a software system which
+stores data related to astronomical objects derived from various
+sources, and provides mechanisms to related multiple detections
+together as astronomical objects.  DVO deals with two related
+concepts: {\em objects} and {\em detections}.  The {\em objects} are
+descriptions of astronomical objects while the {\em detections} are
+the specific measurements of those objects, typically measured from
+astronomical images.  A collection of {\em detections} may be used to
+derive average quantities which describe a particular {\em object}.  A
+third class of measurement to be considered are those supplied by
+external references.  Such measurements may be treated as {\em
+detections}, with the caveat that access to the raw measurements and
+metadata are usually unavailable: the reported measurements and errors
+must be accepted as they are reported.
+
+DVO stores the collections of detections which were derived from
+specific images.  It provides a mechanism to determine the image from
+which a specific detection was derived, and in conjunction with the
+Image Server locate the corresponding data file.  DVO also makes it
+possible to extract all detections derived from a specific image and
+to determine quantities such as the pixel coordinates of the detection
+on the image.
+
+DVO also has the capability to associate multiple detections of a
+specific object.  Several major classes of objects will be present,
+each of which must be handled correctly.  DVO distinguished the
+following types of objects.
+
+{\bf Stars, compact galaxies, and QSOs} will have nearly fixed
+locations relative to other distant stars, with only small deviations
+for individual measurements.  The association between multiple
+detections of such objects is made on the basis of their coincident
+positions.  DVO determines the average position of the object and the
+deviations of the individual detections from that average on the basis
+of the ensemble of individual detection.
+
+{\bf Solar System Objects} do not have a fixed location.  Detections
+of such objects are linked by their orbits, and depend on both the
+position and the time of the image.  DVO does not attempt to make this
+link; this is the role of the MOPS system.  However, it has the
+ability to accept identifications made externally with specified
+detections and to return the identifier of the moving object
+associated with the specific detections.  These associations also
+include descriptive information such as the offset of the detection
+from the predicted location of the detection based on the orbit.  This
+functionality is required to allow DVO to ignore known moving object
+detections from other types of queries.
+
+{\bf High-proper-motion objects} in the general vicinity of the solar
+system fall in between these first two classes of objects.  Their
+proper motion and parallax response is significant enough ($>0.2$
+arcsec in 1 year) that they are not well-described by an average
+location and a collection of offsets.  These objects are better
+described by a distance and a proper motion vector.  DVO provides the
+association between the specific detections and an average object
+which includes finite parallax and proper motion.
+
+{\bf Orphaned detections} are not associated with a specific
+astronomical object of any of the above classes.  Most of these will
+be spurious (not representing real objects), some will be from solar
+system objects for which orbits are not yet determined, some will be
+from faint stars near the detection limits, and some will be from
+short-term transients which have only been detected once.  DVO
+maintains these detections until they have been associated with one of
+the objects above.  DVO provides mechanisms by which individual
+detections may be migrated back and forth between the orphan state and
+association with an astronomical object.
+
+DVO stores the information about the detection, the related objects,
+and the images which provided the measurements.  For every detection,
+DVO provides the mechanisms to link the detection back to the image
+which supplied it.  DVO also provides the capability to determine the
+images containing a specific location but for which no detection was
+made.  The minimum set of information which must be carried for these
+non-detections is the image and the associated object or orphan.
+
+DVO also stores the relationships between various
+photometric systems and the evolution of that relationship.  It
+provides mechanisms to convert between the measured instrumental
+magnitude of a detection with a specific filter, detector, and
+telescope, and at a particular time and the implied magnitude in the
+average Pan-STARRS photometry system, given a determined set of
+calibrations.  It also provides the capability to convert magnitudes
+in one system to the magnitudes in another system; an example of such
+a conversion is between the average Pan-STARRS filter systems and the
+various reference systems appropriate for those filters.
+
+\section{Photometric systems and the DVO Photcodes}
+
+One of the major roles of DVO is to relate different photometric
+measurements made with different instruments and detectors together.
+We may have observations made with the same basic filters, but using a
+number of different detectors.  We may have observations from
+different telescopes in similar filters.  We may have reference data
+related to some filter, but obtained and published by other observers.
+We would like to related these measurements together in optimal ways,
+making use of whatever information we have available.  DVO provides
+several mechanisms to enable these relationships.
+
+We identify three distinct types of photometry measurements within
+DVO:
+\begin{itemize}
+\item {\bf reference photometry}  These measurements are provided by
+  external observers.  For reference photometry, we do not have access
+  to very must information used to determine the magnitudes of the
+  objects of interest.  We have the reference magnitudes corresponding
+  to a type of filter, and presumably some information of the error on
+  the measurement.  We might possibly know the epoch of the
+  observations, but not necessarily.  
+\item {\bf detection photometry} This is our primary measurement of
+  interest: the photometry of objects measured from images which we
+  have processed.  More specifically, the detection photometry is an
+  instantaneous measurement from a specific image with well-known
+  properties, such as exposure time, airmass, instrument source, etc.  
+\item {\bf internal photometry} With the application of an appropriate
+  zero point and other calibration terms, any detection photometry can
+  be calibrated to represent a measurement in a well-known photometric
+  system.  The internal photometry measurements are calibrated to be
+  on a photometric system which represents a consistent system for a
+  particular telescope or collection of data, minimizing the
+  calibration transformations necsessary.
+\end{itemize}
+
+Defining the relationships between the different types of measurements
+is part of the process of photometric calibration.  DVO uses the
+concept of the 'photcode' to identify the source of the photometry,
+and to define the relationships between different photometry sources.
+A photcode identifies a photometric system: for the detection
+photometry measurments, each combination of telescope, camera, filter,
+and detector is associated with a unique photcode; there are also
+unique photcodes for the internal photometry systems and any distinct
+external reference source.  
+
+As a concrete example, consider the Pan-STARRS PS-1 system.  There
+will be three different cameras in use at different times: GPC-1,
+TC-3, and the SkyProbe camera.  There are at least 6 filter systems:
+{\it grizy} and {\it w}.  The SkyProbe camera has a single CCD, TC-3
+has 16 different detectors, and GPC-1 has up to 64 different devices.
+Each of these combinations is potentially a different photometric
+system, so a different photcode is defined for each combination.
+These photcodes would have names such as: GPC1.02.r (r filter with the
+GPC1 camera and OTA 02) or SP1.00.g (SkyProbe 1, g filter).  These
+($64 \times 6 + 16 \times 6 + 5 = 485$) photcodes are all identified
+as 'detection' photcodes, specifying that detection photometry is
+associated with them
+
+There are also 6 different internal photometric systems of interest,
+namely those associated with the 6 named filters, {\it grizy} and {\it
+w}. Each of these 6 systems is identified with an internal photcode.
+The internal photcodes are further distinguished as 'primary' or
+'secondary', which specifies how the DVO system stores average
+quantities related to these types of photcodes (see the discussion of
+the tables below).  
+
+Finally, there may be multiple external photometric systems of
+interest, some of which are related to the major internal photometry
+systems, some of which are not.  For example, the Pan-STARRS project
+may refer to photometry from the SDSS secondary standards, the SDSS
+data releases, Johnson photometry from Landolt (1992), observations
+from 2MASS in $JHK$, USNO-B observations, and so forth.  Each of these
+photometric systems is assoiciated with a different photcode; only
+some of these are relevant to the detection or internal photometry
+system.
+
+Within DVO, the detection and internal photcodes each define a
+relationships as well as a specific photometric system.  Associated
+with each of these photcodes are the parameters of the photometry
+transformation from the photometric system of the photcode to another
+photometric system.  For the detection photcodes, the parameters
+define the transformation to the equivalent internal photcode system.
+The currently-defined transformation parameters consist of the
+following photometry equation:
+%
+\[ M_i = M_r + C_r + K_r (\mbox{airmass} - 1) + \sum_{i = 1}^{i < N}
+A_{r,i} (\mbox{color} - \mbox{color}_r)^i 
+\] 
+%
+where $C_r$ represents the zero-point of the transformation, $K_r$
+represents the slope of the airmass trend, $\mbox{airmass}$ is the
+airmass for a given measurement, $\mbox{color}$ is the color of the
+source of interest (as identified below), $\mbox{color}_r$ is the
+reference color for sources in this photometry system, and $A_{r,i}$
+is the coefficient of the $i$ power of the color difference.  Up to
+fourth order color terms are currently allowed.  For any photcode, the
+color is defined as the difference of the measurements in two other
+photcodes, usually two 'internal' photcodes.  The photcode information
+also specified the equivalent photcode to which the transformation corresponds.
+
+For the detection photcodes, the target of the transformation must be
+an internal photcode.  For the internal photcodes, the target of the
+transformation is an external reference photcode system.  This
+restriction implies that the internal photometry may only be
+transformed (and thus compared with) a single external reference.
+This is in fact the best practice as far as photometric calibration is
+concerned: the 'standard' observations from different references
+should always be treated as different photometric systems.  To allow
+for the relationship of the internal photometry to multiple sources of
+reference photometry, an additional set of photcodes are defined which
+identify 'alternative' transformations for the internal photcodes.
+
+It is important to note that not all of the photometry transformation
+parameters identified above are relevant for each of the three major
+types of photcode.  The detection photcodes will in general make use
+of all of these elements, though the order of the color transformation
+will hopefully be limited if the different devices are sufficiently
+similar.  For the transformation from the internal photcodes, which
+are derivative in some way of the detection photcodes, the airmass
+component is invalid: for a single measurement, the
+detection-to-internal transformation has already removed the airmass
+trend; for an averaged internal photometric measurement, no single
+airmass corresponds to the observations.  Finally, no transformation
+parameters are defined for the reference photcodes at this time.
+
+DVO provides methods by which these photometry transforamtions are
+automatically applied.  The specific measurements (detection
+photometry) are stored in the database tables as instrumental
+magnitudes, and any operation which examines these measurements must
+make use of the APIs to convert to an appropriate common system.  A
+further complication to note is that the photcodes defined above are
+static; they do not include any information about changes to the
+system sensitivity.  This information is carried externally to the
+photcode calibration information; the transformations defined by the
+photcodes must be considered the {\em starting point} for any
+photometric analysis.  An additional adjusment can be applied.  
+
+The detections from a specific image may all have a 'calibration'
+offset applied which bring the measured photometry into a common
+relative system.  This calibration offset is associated with the image
+and may be a function of position on the detector.  The tables which
+carry the individual measurements also include the calibration
+magnitude appropriate for each measurement to speed up the application
+of this offset.  In a well-calibrated collection of photometry, all of
+the detection measurements will have a measured calibration magnitude,
+yielding a collection of internal photometry measurements which are
+all consistent.  An additional piece of information is the zero-point
+history, which tracks the system-wide variations in the average
+sensitivity.  The zero-point history can be used to predict the
+calibration magnitudes for any observation which is not tied directly
+via relative photometry to the rest of the photometric observations.
+
+Putting all of these pieces together, the photometry APIs in DVO can
+be used to return any of the following types of photometric
+measurements:
+\begin{itemize}
+\item raw instrumental magnitudes for any detection
+
+\item 'catalog' magnitudes, applying only the airmass and static
+  zero-point calibrations to a detection magnitude; this is useful to
+  test the detector-color transformation.
+
+\item 'system' measurements, applying the complete static
+  transformation for a detection magnitude to the internal photometry
+  system; for photometric weather and no zero-point variations, this
+  would be a measurement in the internal photometry system.
+
+\item 'relative' magnitudes, applying the measured calibration offset
+  to the calibrated detection magnitude determined above; in a
+  well-calibrated system, this represents a consistent internal
+  photometry measurement.
+
+\item 'calibrated' magnitudes, correcting the measure detection
+  photometry by applying the transformation from the internal
+  magnitude system to the external reference magntiude system.
+
+\item 'average' magntiudes, the raw internal photometry magnitudes
+  (note the distinction between the 'average' quantities, which are
+  derived from a collection of detections an the 'relative' quantities
+  which represent an instantenous measurement in the same system).
+
+\item 'reference' magnitudes, in which the 'average' internal
+  photometry values are transformed to the refernce magnitude system.  
+\end{itemize}
+The complexity of these transformations is necessary to allow the
+examination of the trends of actual measurements with external
+parameters.
+
+\section{DVO Database Tables}
+
+\begin{figure}
+\resizebox{4.5in}{!}{\includegraphics{pics/dvo.01.ps}}
+\caption{\label{fig:DVOtables} \small Data types managed by DVO}
+\end{figure}
+
+Figure~\ref{fig:DVOtables} illustrates the data managed by DVO, and
+Table~\ref{tab:DVOtables} provides a complete listing.  The contents
+of these tables are outlined in Appendix~\ref{sec:DVOTableContents}.
+Below, the use of these tables by DVO software is discussed below.
+Several of the tables are not just simple tables in the database but
+are instead table groups divided into many subtables, each of which
+represents a portion of the sky (a {\tt region}).  These subtables may
+also be distributed across different computers to distribute the
+processing load.
+
+\subsection{Sky Regions Table}
+
+The {\tt Regions} table is used to subdivide the tables of images,
+objects, and detections, etc, as discussed above.  DVO
+divides the sky into a hierarchy of regions (portions of the sky) each
+of which is in turn subdivided into smaller portions.  Since nearly
+all interactions with DVO performed by the IPP are limited
+in spatial coverage, subdividing the tables allows a specific
+interaction to search only a small subset of the data.  The table of
+images is the smallest of the three; the table of detections is likely
+to be the largest.  As a result, the {\tt Images} table group will be
+subdivided at a shallow hierarchical level, while the {\tt Objects}
+and {\tt Detections} are subdivided on deeper (more finely sampled)
+levels.  The {\tt Regions} table defines the boundaries of the sky
+regions and specifies if the region corresponds to an {\tt Images}
+table, an {\tt Objects} table, and/or a {\tt Detections} table.  It
+also specifies which regions in the next level of the hierarchy are
+contained by the region, and which parent region it belongs to.  In
+addition to improving the spatial access to the image, object, and
+detection data, the {\tt Regions} table allows for multiple computers
+to serve the database tables.  The region file specifies the machine
+which stores the specific table.  Figure~\ref{fig:APDBRegions}
+illustrates schematically the subdivision of the sky and the
+association between different levels of the hierarchy with different
+subtables.
+
+\begin{figure}
+\begin{center}
+\resizebox{6in}{!}{\includegraphics{pics/dvo.02.ps}}
+\caption{DVO Regions and Image / Object tables}
+\label{fig:DVOskyregions}
+\end{center}
+\end{figure}
+
+\subsection{Images Table Group}
+
+The {\tt Images} table group lists all of the images which provided
+the data in DVO.  These tables are subdivided by region on
+the sky.  In general, the images listed in this table correspond to
+the Chips.  This group of tables includes sufficient astrometric
+parameters to represent the coordinates of the detections to a
+sufficient accuracy.  Parallel to the Images table is the Mosaic
+table.  This table is very similar to the Images table, but defines
+the Mosaic which corresponds to a group of Images.  The parameters
+include the astrometric information needed to define the camera
+distortion.
+
+\subsection{Image Overlaps Table Group}
+
+The specific subtable of {\tt Images} which contains a given image is
+the one which contains the center pixel of that image.  An additional
+table group, {\tt Image Overlaps} (with the same subtable organization
+as the {\tt Images} subtables), lists images which overlap that
+specific subtable.  Thus, given a particular coordinate, in order to
+find that images which overlap that coordinate, it is necessary to
+search the images in the {\tt Images} subtable which includes that
+coordinate, and all images in the {\tt ImageOverlaps} subtable for
+that coordinate.
+
+\begin{table}[hb]
+\begin{center}
+\caption{DVO Database Tables\label{tab:DVOtables}}
+\begin{tabular}{ll}
+\hline
+\hline
+{\bf Table Name} & {\bf Description} \\
+\hline
+Images               & The images that have objects in the DB. \\
+Image Overlaps       & Image regions which are touched by specific images. \\
+Objects              & The objects --- average properties of multiple detections of the same object. \\
+Average Magnitudes   & Average photometry in multiple filters \\
+Solar System Objects & Identification of solar system objects \\
+Matched Detections   & Detections of sources in an image identified with an Object. \\
+Orphaned Detections  & Detections of sources in an image not identified with an Object. \\
+Non-detections       & Non-detections of objects in an image. \\
+SkyRegions           & spatial distribution of tables \\
+Filters              & Filters understood by the system. \\
+Photcodes            & Transformations between different photometric systems \\
+Zero Points          & History of Zero-point \& Airmass terms \\
+Distortion Models    & History of Optical Distortion terms \\
+Database Hosts       & computers used to store the tables \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsection{Objects Table Group}
+
+The {\tt Objects} table group (also divided by region) stores the
+average parameters for each astronomical object.  Certain details of
+this table have not yet been specified.  In particular, objects with
+significant parallax and/or proper motion may potentially be stored in
+a distinct table.  Solar system object identifications, to the extent
+average properties are maintained in DVO, will certainly
+be stored in a separate table.  
+
+\subsection{Average Magnitudes Table Group}
+
+A related table, also divided into the same regions, is the {\tt
+Average Magnitudes} table.  In this table, there are multiple rows per
+object, one for each of the primary filters of interest for which
+photometric averaging is performed.  This organization makes the
+number of primary (averaged) filters a configurable value.
+
+\subsection{Matched Detections Table Group}
+
+The {\tt Matched Detections} table stores all of the measurements of
+astronomical objects on specific images.  This table includes all
+detections associated with the average {\tt Objects}.  As discussed
+below, bright objects (above a configuration-specified signal-to-noise
+level) are defined object even if only one detection has been found at
+that position.  Faint orphaned objects are not added to this list or
+the list of objects.  The different types of detections (P2,
+P4$\Delta$, P4$\Sigma$) are distinguished by their photometry codes.
+(This is only valid if DVO does not store different
+quantities for these types of detections.)
+
+\subsection{Orphaned Detections Table Group}
+
+The {\tt Orphaned Detections} table stores the detections which have
+not been correlated with an existing object.  This table is only
+populated for objects below a configuration-specified signal-to-noise
+limit (e.g., 5$\sigma$).  Bright orphaned detections are assigned an
+object and added to the {\tt Matched Detections} table.
+
+\subsection{Non-detections Table Group}
+
+The {\tt Non-detections} table stores information about detection
+failures for each object.  If an image is added to the database which
+overlaps an object but the object is not detected, an entry is made in
+this table.  In practice, this table may store only the most recent
+non-detection and the total number, or a similar reduced set of
+non-detection statistics.
+
+\subsection{Other Reference Tables}
+
+The {\tt Filters} table identifies all of the physical filters
+(specific pieces of glass) known to the system.  A related table, {\tt
+Photcodes}, defines relationships between photometry systems.  A
+photometry system may consist of a detector, telescope, and specific
+filter, or it may be a derived photometry system.  The {\tt Database
+Machines} table identifies all of the computers available to DVO.
+
+\section{Database Table I/O}
+
+\begin{figure}
+\resizebox{4.5in}{!}{\includegraphics{pics/dvo.03.ps}}
+\caption{\label{fig:DVOformats} \small DVO Table I/O }
+\end{figure}
+
+DVO allows for a flexible representation of its data on disk.  Data
+may be written to disk in one four possible mode: RAW, FITS MEF, FITS
+SPLIT, and MYSQL.  These modes define the overall organization of the
+data on disk.  In the RAW mode, the data is written to disk in a
+pseudo-FITS table format which consists of a simple FITS header
+describing the layout followed by the binary data in a block.  This
+storage mode is maintained for historical reasons.  There are also two
+types of FITS modes in which the data tables are written as valid FITS
+Binary Tables.  In the SPLIT format, every data table is written as a
+separate file, while in the MEF format, the object and detection
+tables are bundled together into a single FITS file with multiple
+table extensions.  The MEF format has the advantage of minimize the
+proliferation of files, while the SPLIT format is required to make use
+of the fastest read/write capabilities of DVO.  DVO makes use of these
+raw data formats as a throughput risk mitigation strategy.  As
+discussed below, this strategy has proven very successful.
+
+There are also multiple formats in which the data may be stored.  The
+different formats define which specific database table columns are
+stored and with what numerical format and precision.
+Figure~\ref{fig:DVOformat} illustrates the conversion process which
+DVO performs when loading in the data.  When DVO loads data from a
+file-based table (FITS or RAW), it first loads from the disk file into
+a data structure representing the external format in use.  The
+external structure is then converted into the internal format. The
+internal structure is always specified to be the superset of all
+external data formats.  This capability allows DVO to maintain
+backwards compatibility with data tables written with early versions.
+As DVO is extended and new elements are added to the tables, it is
+only necessary to define the methods to convert the new internal table
+into the external table.  In addition, DVO makes use of autocoded
+table manipulation and I/O APIs which are generated for each data
+structure based on a descriptive table.  This makes it easy to add new
+data types and input/output methods without significant re-coding.
+
+\tbd{DVO mysql table storage is not yet implemented}
+
+\section{addstar : Insert Image \& Detection Set}
+
+\begin{figure}
+\resizebox{4.5in}{!}{\includegraphics{pics/dvo.04.ps}}
+\caption{\label{catalog} \small a figure }
+\end{figure}
+
+One of the most basic operations needed by DVO is to insert a
+collection of detections derived from a specific image, and add the
+definition of that image to the database.  This operation is critical
+in terms of the processing throughput.  After the detections have been
+assigned to the appropriate regions, they are matched against all
+objects in the {\tt Objects} table.  Matches are performed only on the
+basis of positional coincidence, using a matching radius which may
+depend on the image astrometry errors, or may be a fixed distance.
+Any matched detections are added to the {\tt Matched Detections}
+table.  Any unmatched detections brighter than the Faint Detection
+cut-off are specified as a new {\tt Object} and also added to the {\tt
+Matched Detections} table.  Any faint unmatched detections are added
+to the {\tt Orphaned Detections} table.  This division is important
+because it allows the automatic association of new detections with
+existing bright objects while limiting the I/O volume required to make
+the detections.  In general, there will be many fewer {\tt Objects}
+than {\tt Detections}, and there will be fewer bright orphans than
+faint orphans.
+
+A wide range of options are available to addstar.  These can be used
+to modify the object matching rules, to reduce the number of tables
+which are updated, to specify the output data format, and so forth.  A
+few options modify the behavoir in substantial ways, as discussed in
+the two sections below.
+
+\tbd{flesh out discussion of the options}
+
+\subsection{Insert Reference Objects} 
+
+\code{addstar -ref (filename)}
+
+This mode of addstar reads a text file and adds the listed objects to
+the database as a reference photcode type.  A collection of reference
+objects are added to the database as a collection of detections.  The
+reference photometry should in general be given its own photometry
+code.  The reference data is different from the image detection set
+because the associated image information is not included.  Thus, no
+corresponding images are added to the database.
+
+\subsection{Insert Catalog Objects} 
+
+\code{addstar -cat (name) -region ra ra dec dec}
+
+In this mode, any of several all-sky or large-scale reference catalogs
+are used for the input sources.  The catalog objects are added to the
+database as reference objects.  The valid catalogs consist of 2MASS,
+USNO, GSC.  Tycho and USNO-B will be added shortly.  Specific
+photcode names are defined for each of these catalogs, and must be
+appropriately requested and defined in the photcode table.  The
+optional region restriction limits the insert to a subset of the sky.
+The user does not always want to add 50GB of 2MASS detections to any
+DVO database... 
+
+\subsection{Addstar Client/Server Interactions}
+
+DVO currently uses stand-alone programs which are run from the command
+line (like addstar, or the programs listed below), or it works with
+the interactive DVO shell, which allows the user to query portions of
+the database.  These programs all interact with the database tables
+directly, making use of file locking to prevent conflicts.  
+
+Unlike the other DVO programs (currently), it is possible to run
+addstar as a client/server system.  In this configuration, the program
+\code{addstard} is launched to run in the background as a server.  It
+monitors a socket waiting for clients to contact it.  The client
+program, \code{addstarc} appears to the user identical to the
+stand-alone addstar.  However, rather than directly insert data into
+the database, \code{addstarc} contacts the addstar server and sends it
+the detections and associated image data (along with the information
+about the user options).  The daemons accepts the incoming data and
+then loads this data into the database, just as the stand-alone
+addstar does.
+
+The purpose of the addstar client/server design is three-fold.  First,
+the client can be used by processes to send data to the DVO database
+and then immediately exit.  The addstar loading process is one of the
+more time-critical functions within the IPP.  However, unlike the
+other portions of the IPP, the addstar processes must operate in
+serial, at least when they are updating the same portion of the sky
+(or the image table).  If the IPP analysis routines all needed to run
+the stand-alone addstar program, they would eventually block waiting
+for each addstar to complete, preventing other processing from
+continuing.  The addstar client / server model allows the processing
+node to invoke the addstar client, sending the data to the addstar
+server.  The addstar server will then be the entity that manages the
+serialization of the incoming data stream.  The addstar server has two
+threads which run in parallel.  One thread monitors the socket and
+accepts new data sets from addstar clients, adding the data to an
+internal queue.  The other thread pulls data off of the queue and
+updates the database with the data.  
+
+A second advantage of the client/server interaction is that only the
+new detections need to be sent across the network.  To update the
+database, addstar must load the average objects for the region from
+the database tables.  In the stand-alone mode, the addstar program
+loads this data via NFS across the network from whatever device stores
+the addstar tables.  In the client/server model, the addstar server
+always runs locally on the machine which holds the database tables.
+Thus, for the server, all database access is local disk access.
+
+The final advantage of the client/server model is that it enables the
+parallel database model, which is not yet implemented as of Jan
+2006. In this model, there are multiple addstar servers.  Each one has
+a fraction of the sky in the local tables.  The identification of
+which table is managed by this host/addstar server is stored in the
+SkyRegion table.  The addstar server simply accepts incoming
+detections from the addstar clients.  Any detections which it receives
+which fall within the boundaries of tables that it manages are updated
+as normal.  The server then identifies the other addstar servers which
+are responsible for the other detections.  It then sends these
+detections to those servers using the same socket communication used
+by the addstar clients.  The addstar server must also be ready accept
+detections from other addstar servers.  This relationship is
+completely parallel, and any addstar client may send its data to any
+addstar server, letting the servers hash out who owns what.  The only
+difficulty with this model is in handling sources near the boundaries
+of the tables.  Note that this issues exists whether those tables are
+distributed across multiple machines or not.
+
+Addstar uses the following strategy to handle detections on the table
+boundaries.  Detections are first added to each table completely
+ignoring the neighboring tables.  A detection which is close to the
+boundary may either be associated with an average object contained
+within the table, or not.  If it is, the detection is associated with
+that average object.  If not, a new average object is created at the
+location of the detection.  So far, this process is identical to the
+behavoir in the middle of the table.  One a longer time-scale, a
+process is run which mediates the table boundaries. In this analysis,
+the two neighboring tables are simultaneously examined.  The border
+region, in a strip wider than the correlation radius, is examined in
+detail.  If two objects within the border region fall within 2x the
+correlation radius of each other, their individual detections are
+re-examined.  These detections are re-added to a temporary table which
+encompases the overlap.  the resulting objects will in general have
+detections from either side of the boundary.  The average objects are
+kept within the table as normal, but the detections are allowed to
+migrate between the tables to stay with their object.  \tbd{this
+boundary cleanup process is not implemented to date}.
+
+\section{Relphot : Relative Photometry Analysis}
+
+This operation uses the overlaps of images and multiple observations
+of the same objects to determine the relative photometry zero-points
+for a collection of images.  This is a task that is run much more
+infrequently than the object insertion tasks.
+
+The relphot analysis is currently performed with a single Sky region
+as the starting point.  All images (or all chips from all mosaic
+iamges) which overlap the sky region are identified in the image
+table.  This set of images are considered set A.  Next, all skyregions
+which are overlapped by all of these images are selected.  Finally,
+all additional images which overlapped the new regions only are
+selected.  These are considered as image set B.  The image selections
+are also restricted to images of a single, user-selected photcode.  
+
+All of the objects and detections which are contributed by the images
+in sample A are extracted from the average and measure tables.  Only a
+subset of the detections for which the S/N is greater than a
+user-selected limit are kept.  Other restrictions, such as time range
+or instrumental magnitude ranges may also be specified.  The
+collection of average objects, their detections, and the images from
+which they were derived now define a system of photometry equations.
+In this system, every image has a calibration offset magnitude
+($M_{cal}$), every object has an average magnitude in a relative
+system ($M_{rel}$), and every detection of that object has a magnitude
+defined by the equation $M = M_{rel} + M_{cal}$.  The goal is to solve
+for the values of $M_{ref}$ and $M_{cal}$.  
+
+There are two points to note about this operation.  First, the system
+of equations is generally much too large to solve directly; we must
+use an iterative technique to converge on a solution. Second, it is
+important in the analysis to use robust averaging and identify
+detections, stars, or images which are deviant in some way.  These
+should be marked and given set weight in the solution.  These cases
+may represent poorly measured objects (perhaps detections on or near a
+bad column), variable stars, and images obtained in poor weather
+conditions.
+
+Relphot can also be used to determine the mosaic grid used to generate
+photometrically corrected flats (-grid option).
+
+\section{Uniphot : Zero Point Analysis}
+
+This operation uses the time history of relative photometry zero
+points for images and the spatial overlap information to determine a
+best set of image zero points which have a specific time scale for the
+atmospheric stability.
+
+\section{global astrometry analysis}
+
+This operation uses the reference and image detections to determine an
+optical distortion model for the camera and static astrometry model
+components.  The astrometry model includes: (1) field distortion
+introduced by the telescope optics, which is a smoothly-varying
+function of the field position relative to the center of the telescope
+boresite coordinates.  (2) focal plane geometry, which includes the
+chip positions and rotations in the focal relative to the boresite,
+along with chip-dependent plate-scale modifications needed to
+represent tilts or warps of the individual detectors relative to the
+ideal flat focal plane. .
+
+\begin{table}
+\begin{center}
+\caption{DBO Detection Classes \& Object Parameters\label{tab:APdetections}}
+\begin{tabular}{lrrrr}
+\hline
+\hline
+Object Parameter & P2 & P4S & P4D & SS \\ 
+\hline
+PSF x,y, covar, $\alpha,\delta$               & + & + & + & + \\
+PSF mag, $\sigma_{\rm mag}$                   & + & + & + & + \\
+star/gal sep                                  & + & + & + & + \\
+$\sigma_x$, $\sigma_y$, $\theta$              & + & + & + & + \\
+local sky data                                & + & + & + & + \\
+Petrosian R, M, $R_{50}$, $R_{90}$            & - & + & - & + \\
+S\'ersic R, M, AB, $\phi$, $\nu$              & - & + & - & + \\
+W.L. $\gamma_1$, $\gamma_2$, pol. terms       & - & - & - & + \\
+exp. spaced aps., Poisson noise, variance     & - & - & - & + \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\section{DVO shell}
+
+\subsection{User Commands}
+
+\begin{verbatim}
+ gcat                        -- get catalog at location
+ gimages                     -- get images at location
+ gstar                       -- get star statistics
+ extract                     -- extract average vectors from catalogs
+ mextract                    -- extract measurement vectors from catalogs
+ imstats                    -- plot image statistics
+ imextract                   -- extract image vectors from database
+ lcat                        -- list catalogs in display region
+ cmatch                      -- match two catalogs
+\end{verbatim}
+ 
+There are a variety of other commands which directly refer to the
+photometry database. Some of these functions extract data of various
+types from the database, others perform more complex plotting
+operations. The commands listed above are those which simply extract
+data from the database. The first three list information relevant to a
+specific RA, DEC location on the sky: gcat (RA) (DEC) lists the
+catalog at the specified location and places the name in the variable
+\$CATNAME, gimages (RA) (DEC) lists all images which overlap the
+specified location, gstars (RA) (DEC) (RADIUS) lists data about the
+stars within a specified radius of the specified location (all numbers
+above are given in decimal degrees). Similarly, lcat lists the
+catalogs in the region. Imstats lists statistics about each image
+
+The next three commands extract a specific piece of information from
+the photometry database and places it in a vector. First, extract will
+extract average values for each star and place it in a vector. Next,
+mextract will extract measurement values for each star and place it in
+a vector: as a result a single star may have multiple entries in the
+measurement vectors. Finally, imextract will extract image statistics
+into vectors (not yet implemented).
+
+DVO provides several ways to access the photometry information stored
+in the database. Several simple commands allow the user to extract 1
+dimensional information directly from one of the primary database
+tables. The commands are:
+
+\begin{verbatim}
+    * imextract
+    * avextract
+    * mextract
+    * imsearch
+    * detsearch 
+\end{verbatim}
+
+imextract
+
+This command allows the user to extract one of the columns from the
+image table, applying filtering as desired:
+
+\begin{verbatim}
+   dvo: imextract
+   USAGE: imextract (value) [-region] [-time start range] [-photcode photcode]
+   dvo: imextract help
+   value may be one of the following:
+    ra dec airmass Mcal dMcal Xm photcode time fwhm exptime nstar ncal sky flag
+\end{verbatim}
+
+The extracted data is saved in a vector with the same name used to
+select the column. The vector name will have the same case as the
+choice given, but the column selection is case-insensitive (since
+there are no ambiguities in the database columns names by case).
+
+avextract
+
+This command allows the user to extract data from one of the Average
+table columns:
+
+\begin{verbatim}
+   dvo: avextract
+   USAGE: avextract (from) (value) [options]
+     from: cpt name or 'all'
+     value: average.parameter or photcode
+   dvo: avextract all help
+   value may be one of the following:
+    ra dec dmag Nmeas Nmiss Xm Xp Nphot Ncode flag type
+\end{verbatim}
+
+This command takes as the first argument the name of one of the
+database regions. Alternatively, all regions currently displayed may
+be selection with the word 'all'. The second option specifies which
+column to select from the Average table. In addition to the basic data
+columns (ra, dec, etc), the magnitude-related average values (mag,
+dmag, Xm, Nphot, Ncode) are coupled to a photcode, which is thus
+required for these selections. The value 'mag' may also be subsituted
+with a primary or secondary photcode. Eg:
+
+\begin{verbatim}
+   avextract all ra : select ra for all objects in displayed region
+   avextract all g  : select g magnitudes
+   avextract all mag -photcode r : select r magnitudes 
+   avextract all Xm -photcode r  : select chisq values for r average mags
+\end{verbatim}
+
+mextract
+
+This command allows the user to extract data from one of the Measure
+table columns:
+
+\begin{verbatim}
+   dvo: mextract
+   USAGE: mextract (from) (value) [options]
+     from: cpt name or 'all'
+     value: measure.parameter or photcode
+   dvo: mextract all help
+   value may be one of the following:
+    ra dR dec dD mag dmag Mrel Mcal photcode time fwhm dophot xccd yccd xmosaic ymosaic flags
+\end{verbatim}
+
+This command takes as the first argument the name of one of the
+database regions. Alternatively, all regions currently displayed may
+be selection with the word 'all'. The second option specifies which
+column to select from the Measure table. In addition to the basic data
+columns (ra, dec, etc), the magnitude-related average values (mag,
+dmag) are coupled to a photcode, which is thus required for these
+selections. The value 'mag' may also be subsituted with a primary or
+secondary photcode. Eg:
+
+\begin{verbatim}
+   mextract all ra : select ra for all objects in displayed region
+   mextract all g  : select g magnitudes
+   mextract all mag -photcode r : select r magnitudes 
+   mextract all Xm -photcode r  : select chisq values for r average mags
+\end{verbatim}
+
+option filtering
+
+The following extraction options allow the user to restrict the
+selections:
+
+-time (start) (range)
+
+select data for images within the given time range. The start date is
+given in the format YYYY/MM/DD,hh:mm:ss (any of these element may be
+dropped, in which case they default to 00 [for hh,mm,ss] or 01 [for
+MM,DD]). The date may also be written as a number of days followed by
+j (JD) or J (MJD). The two special date names "now" and "today" are
+also valid. The range is written as a number followed by a unit, with
+valid units of s (seconds), m (minutes), h (hours), d (days). Times
+are in UT. For example:
+
+\begin{verbatim}
+  -time 2001/1/1 30d : select the range starting at midnight on 2001/1/1
+   and ending 30 days (86400*30 seconds) later.
+ 
+  -time now -3h : select the time range starting three hours ago and
+   ending now.
+\end{verbatim}
+
+-region
+
+restrict the selection to the currently display portion of the
+sky. This filter expects a portion of the sky to be plotted, and will
+only select data for images in the part of the sky. The algorithm for
+selecting the displayed region may not be perfect, so images near the
+boundaries may be unexpected included or excluded (this depends on the
+exact overlap details). Multiple queries with the same region will
+result in the same subset of images selected.
+
+-photcode (photcode)
+
+This restricts the selection to the given photcode. Images may only
+have photcodes of 'Dependent' type (eg, CFH12K.R.00). This filter
+allows the selection of images by exact match (selected photcode is
+Dependent type) or by equivalence match (selected photcode is Primary
+or Secondary type). Thus, selecting images based on CFH12K.R.00 will
+ignore images with photcode CFH12K.R.10, while selecting images based
+on photcode 'r' will return all images with CFH12K.R.*, since all are
+equivalent to 'r'. (NOTE: these details depend on the layout of the
+photcode table). fill this out with all other restrictions provided by
+photometry.c
+
+\begin{verbatim}
+ catalog                    -- plot catalog stars
+ cgrid                      -- plot sky coordinate grid
+ cplot                      -- plot vectors in sky coordinates
+ czplot                     -- plot scaled vectors in sky coordinates
+ images                     -- plot image boxes
+ imdense                    -- image density plot
+ lcurve                     -- plot lightcurve for a star
+ pcat                       -- plot catalog boundaries
+ region                     -- define sky region for plot
+ resid                      -- plot residuals
+ simage                     -- plot stars in an image
+ \end{verbatim}
+
+
+There are two types of database plotting functions: those that display
+or refer to the spatial charateristics of the data and those that
+refer to other types of charatersitics, such as the time domain. The
+graphics window 0 is reserved for all plots of objects on the sky. The
+command region defines the current sky coordinates for plots in
+graphic window 0. The command pcat plots the outline of all photometry
+database files which are within the currently defined region (and by
+default, only those with data). images plots the outline of the images
+in the image database, while imdense shows the number of images at a
+location by randomly spacing dots within the boundary of the
+images. The command cgrid draws a grid in celestial coordinates on the
+for the current region.
+
+The most complex, but also one of the most useful command is catalog,
+which plots the positions of stars in the photometry database (and
+others) on the sky. There are many options to this command. One set
+allows the user to plot stars from the photometry database (the
+default), from the HST GSC, or from an ASCII text file with RA, DEC,
+and Mag in specified columns. If the ASCII file has a fixed number of
+bytes per line, the data can be more quickly loaded. The size of the
+points may be scaled by the star magnitude, by the number of
+observations of the star, or by the number of missing datapoints for
+the star. In addition, points may be plotted only if they land in
+specified magnitude ranges, or with specified numbers of measurements,
+or missed measurements. Also, objects may be plotted only if they have
+a specified Average.code, so that only asteroids or only perfect stars
+may be plotted. The plotted vectors may be saved, if desired, and the
+source catalog epoch may be specified as different from J2000 (only
+valid for ASCII data).
+
+Several other commands relate to non-spatial charateristics of images
+and stars. lcurve will plot a light curve for all stars within some
+radius of a point. resid plots the photometry residuals for a
+particular region file.  Some Examples
+
+\begin{figure}
+%\resizebox{4.5in}{!}{\includegraphics{pics/fullsky}}
+\caption{\label{allsky} \small Map of the entire sky, and images added to database. } 
+\end{figure}
+
+Fig.~\ref{allsky} shows a map of the entire sky, and the location of
+the images currently in the database. This picture was made with the
+following commands: (output is not shown)
+
+\begin{verbatim}
+ dvo: region 0 0 90 gls
+ dvo: cgrid
+ dvo: style -lw 2 -c red 
+ dvo: images
+ dvo: ps
+\end{verbatim}
+
+In this example, on the graphics window, the image boxes are shown in
+red. The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth. 
+
+\begin{figure}
+%\resizebox{4.5in}{!}{\includegraphics{pics/polar}}
+\caption{\label{polar} \small Map of
+the sky in polar project, and images added to database. }
+\end{figure}
+
+Fig.~\ref{allsky} shows a map of the entire sky, and the location of
+the images currently in the database from a polar project. This
+picture was made with the following commands: (output is not shown)
+
+\begin{verbatim}
+ dvo: region 0 0 90 zea
+ dvo: cgrid
+ dvo: style -lw 2 -c red 
+ dvo: images
+ dvo: ps
+\end{verbatim}
+
+In this example, on the graphics window, the image boxes are shown in
+red. The user now has the possiblitiy of using the cursor command to
+narrow in on a specific region, and so forth. 
+
+\begin{figure}
+%\resizebox{4.5in}{!}{\includegraphics{pics/catalog}}
+\caption{\label{catalog} \small Comparison between HST GSC and
+photometry database astrometry. }
+\end{figure}
+
+Fig.~\ref{catalog} shows an example comparison of the photometry
+database star positions and the HST Guide Star Catalog star
+positions. The crosses are all objects in the photometry database,
+while the boxes are only the stars identified as USNO stars. The
+circles are the stars from the HST GSC. The size of both points is a
+function of brightness. This plot was made with the following commands
+(starting from the previous image):
+
+\begin{verbatim}
+ dvo: cursor  (typed 1 on region of interest)
+ 1 137.097858 22.698305
+ q 137.097858 22.698305
+ dvo: region $R1 $D1 0.2 TAN
+ dvo: cgrid
+ dvo: box
+ dvo: style -pt 0 
+ dvo: gcat $R1 $D1 
+   0 n2230/1951.cpt *
+ dvo: style -pt 2; cat -all -m 12 18
+ dvo: style -pt 1; cat -all -m 12 18 -ID $USNO
+ dvo: style -pt 7; cat -all -m 12 18 -g
+\end{verbatim}
+%$
+
+\section{other user tools}
+
+\subsection{delstar}
+
+\subsection{getstar}
+
+\subsection{imphotset}
+
+imphotset allows you to set certain phot.image table entries. here are
+the options: 
+
+imphotset [-photcode code] [-name foo] [-trange (start) (stop)] -flag
+and value 
+
+Here is a complete list of relphot configuration variable names, a
+quick description, and reasonable values to start with:
+
+\begin{verbatim}
+ --- configuration variables used by relphot ---
+ MAG_LIM : float
+   ignore measurements fainter than this absolute magnitude
+ 
+ SIGMA_LIM : float
+   ignore measurements with magnitude error larger than this value
+ 
+ STAR_SCATTER : float
+   mark stars as bad if their scatter is larger than this value
+ 
+ IMAGE_SCATTER : float
+   mark images as bad if their scatter is larger than this value
+ 
+ IMAGE_OFFSET : float
+   mark images as bad if the absolute value of thie zero point offset
+   is larger than this number
+ 
+ STAR_CHISQ : float
+   mark stars as variable if their reduced chisq are larger than this value
+ 
+ STAR_TOOFEW : int
+   mark stars as bad if the have fewer than this number of valid measurements
+ 
+ IMAGE_TOOFEW : int
+   mark images as bad if the have fewer than this number of valid measurements
+ 
+ IMAGE_GOOD_FRACTION : float
+   mark images as bad if the have fewer than this fraction of valid measurements  
+ 
+ IMAGE_CATALOG : string
+   name of the image catalog file
+ 
+ IMAGE_CATALOG_TEMPLATE : string
+   name of the template file to create the image catalog file
+ 
+ CATALOG_TEMPLATE : string
+   name of the template file to create the catalog file
+ 
+ GSCFILE : string
+   name of the GSC region table
+ 
+ CATDIR : string
+   directory where the database is stored
+ 
+ PHOTCODE_FILE : string
+   file containing photometry code information
+ 
+ ZERO_PT : float
+   default zero point for random data
+ 
+ RELPHOT_GRID_X : int
+   scale of mosaic correction grid 
+ 
+ RELPHOT_GRID_Y : int
+   scale of mosaic correction grid 
+ 
+ RELPHOT_GRID_BINNING : int
+   deprecated
+ 
+ CAMERA_CONFIG : string
+   name of the file containing descriptive information about the camera
+ 
+ --- sample ConfigFile entries with typical values ---
+ 
+ MAG_LIM                	 24.0
+ SIGMA_LIM              	  0.05
+ STAR_SCATTER           	  0.05
+ IMAGE_SCATTER          	  0.05
+ IMAGE_OFFSET           	  0.2
+ STAR_CHISQ             	 10.0
+ STAR_TOOFEW            	  3
+ IMAGE_TOOFEW           	 10
+ IMAGE_GOOD_FRACTION    	 
+ IMAGE_CATALOG          	 $CATDIR/Images.dat
+ IMAGE_CATALOG_TEMPLATE 	 $REFSDIR/elixir/template.cat
+ CATALOG_TEMPLATE       	 $REFSDIR/elixir/template.cat
+ GSCFILE                	 $REFSDIR/gsc/GSCregions.tbl
+ CATDIR                 	 $CATDIR
+ PHOTCODE_FILE          	 $CONFDIR/camera/$CAMERA.photcode
+ ZERO_PT                	 25.0
+ RELPHOT_GRID_X         	 4
+ RELPHOT_GRID_Y         	 8
+ RELPHOT_GRID_BINNING   	 512
+ CAMERA_CONFIG          	 $CONFDIR/camera/$CAMERA.config
+\end{verbatim}
+ 
+
+\section{Performance}
+
+DVO design partly driven by the need to make the
+detection-object associations quickly and to processes the incoming
+detections at a sufficiently high rate to meet the throughput
+requirements.  For each upload of the object detections from a
+complete FPA, DVO must match roughly $1.4 \times 10^{6}$
+detections from an FPA with roughly $6.4 \times 10^{6}$ objects,
+including orphaned bright detections.  This corresponds to roughly 640
+MB, if each object uses 100 bytes for its descriptive informations
+(more than is currently specified in the Object table).  With a
+throughput of 100 MB/s for reads from a RAID, DVO can
+perform the data read in a fraction of a second if the data is
+distributed across 10 computers.
+
+\appendix
+\pagebreak
+
+\section{DVO Tables}
+\label{sec:DVOTableContents}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Images\label{tab:images}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+\code{NAME} &                 \code{char[32]} &        	name of original image  \\
+\code{TZERO} &                \code{e_time} &           readout time (row 0) \\
+\code{COORDS} &               \code{Coords} &           astrometry \\
+\code{NSTAR} &                \code{unsigned int} &     number of stars on image \\
+\code{SECZ} &                 \code{float} &       	airmass,                   mag \\
+\code{NX} &                   \code{short} &       	image width \\
+\code{NY} &                   \code{short} &       	image height \\
+\code{APMIFIT} &              \code{float} &       	aperture correction \\
+\code{DAPMIFIT} &             \code{float} &       	apmifit error \\
+\code{MCAL} &                 \code{float} &       	calibration mag \\
+\code{DMCAL} &                \code{float} &       	error on Mcal \\
+\code{XM} &                   \code{short} &       	image chisq \\
+\code{SOURCE} &               \code{short} &       	photcode \\
+\code{EXPTIME} &              \code{float} &           	exposure time (seconds) \\
+\code{ST} &		      \code{float} &           	sidereal time of exposure \\
+\code{LAT} &		      \code{float} &           	observatory latitude (degrees) \\
+\code{DETECTION_LIMIT} &      \code{unsigned char} &   	detection limit   (10*mag) \\
+\code{SATURATION_LIMIT} &     \code{unsigned char} &   	saturation limit  (10*mag) \\
+\code{CERROR} &               \code{unsigned char} &   	astrometric error (50*arcsec) \\
+\code{FWHM_X} &               \code{unsigned char} &   	PSF x width,               (25*arcsec) \\
+\code{FWHM_Y} &               \code{unsigned char} &   	PSF y width,               (25*arcsec) \\
+\code{TRATE} &                \code{unsigned char} &   	scan rate,                 (100 usec/pixel) \\
+\code{CODE} &                 \code{char} &            	image quality flag \\
+\code{CCDNUM} &               \code{unsigned char} &   	CCD ID number \\
+\code{ORDER} &                \code{short} &       	Mrel polynomial order  \\
+\code{MREL_POLY} &            \code{short[14]} &   	Mrel polynomial \\
+\code{DUMMY} &                \code{char[18]} &         expansion \\
+\hline		  
+\end{tabular}	  
+\end{center}	  
+\end{table}	  
+		  
+\begin{table}[bh]
+\begin{center}
+\caption{Objects\label{tab:Objects}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline 
+\code{RA} &           \code{double} &           RA,                	     \\
+\code{DEC} &          \code{double} &           DEC,               	     \\
+\code{MAG} &          \code{float} &            primary mag,       	     \\
+\code{MAG_ERR} &      \code{float} &            error on primary mag,           \\
+{\it \code{V_RAf}} &          \code{float} &            proper-motion (arcsec/year), \\
+{\it \code{V_DEC}}           \code{float} &            proper-motion (arcsec/year) \\
+{\it \code{PAR}}     	      \code{float} &            parallax (arcseconds) \\
+{\it \code{D_V_RA}}  	      \code{float} &            proper-motion error (arcsec/year)  \\
+{\it \code{D_V_DEC}} 	      \code{float} &            proper-motion error (arcsec/year)  \\
+{\it \code{D_PAR}}   	      \code{float} &            parallax error (arcseconds) \\
+\code{SIGMA_POS} &    \code{short} &  	        position scatter,   	   \\
+\code{CHISQ_MAG} &    \code{short} &  	        chisq for primary mag,         \\
+\code{CHISQ_GAL} &    \code{short} &            chisq for galaxy mags,         \\
+\code{NMEAS} &        \code{unsigned short} &   number of measures \\
+\code{NMISS} &        \code{unsigned short} &   number of missings \\
+\code{CODE} &         \code{unsigned short} &   ID code (star, ghost, etc) \\
+\hline		  
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{SecFilt Magnitudes\label{tab:SecFilt} - NOTE: corresponding
+  photcodes defined externally for the table sequence, Average Object
+  association defined by sequence }
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+\code{MAG} &      \code{float} &                 other mags,       mags \\
+\code{MAG_ERR} &  \code{float} &                 scatter on mag    mags \\
+\code{MAG_CHI} &  \code{short} &                 chisq on mag      [100*log(value)] \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Matched Detections\label{tab:Detections}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+\code{D_RA} &       \code{float} &           RA offset,                	  arcsec \\
+\code{D_DEC} &      \code{float} &           DEC offset,               	  arcsec \\
+\code{MAG} &        \code{float} &           catalog mag,       	       	  mag \\
+\code{MCAL} &       \code{float} &           image cal mag,	          mag \\
+\code{MGAL} &       \code{float} &           galaxy mag,			  mag \\
+\code{DM} &         \code{float} &           mag error,                      mag \\
+\code{AIRMASS} &    \code{float} &           (airmass - 1),		  airmass \\
+\code{DT} &         \code{float} &           exposure time,                  2.5*log(exptime) \\
+\code{FWX} &        \code{short} &           object fwhm major axis,         1/100 of arcsec  \\
+\code{FWY} &        \code{short} &           object fwhm minor axis,         1/100 of arcsec  \\
+\code{THETA} &      \code{unsigned char} &   angle wrt ccd X dir,            (0xff/360) deg \\
+\code{DOPHOT} &     \code{char} &            dophot type \\
+\code{SOURCE} &     \code{unsigned short} &  photcode \\
+\code{FLAGS} &      \code{unsigned short} &  flags for various uses   \\
+\code{T} &          \code{unsigned int} &    time in seconds (UNIX) \\
+\code{AVEREF} &     \code{unsigned int} &    reference to average entry       \\
+\code{OBJECT ID} & & \\
+\code{SKY} & & \\
+\code{D_SKY} & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Orphaned Detections\label{tab:Orphans}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+$\alpha$          & & \\
+$\delta$	  & & \\
+$\sigma_{\alpha}$ & & \\
+$\sigma_{\delta}$ & & \\
+$M_{\rm inst}$	  & & \\
+$M_{\rm cal}$	  & & \\
+$\sigma_{\rm mag}$& & \\
+photcode	  & & \\
+type		  & & \\
+flags		  & & \\
+time/date	  & & \\
+airmass		  & & \\
+$\sigma_{x}$	  & & \\
+$\sigma_{y}$	  & & \\
+$\theta$	  & & \\
+exptime		  & & \\
+sky		  & & \\
+$\sigma_{\rm sky}$& & \\
+etc		  & & \\
+\hline		  
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Non-detections\label{tab:NonDetects}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline  
+object ID          & & \\
+$N_{\rm non-det}$  & & \\
+last time/date 	   & & \\
+last mag	   & & \\
+faintest time/date & & \\
+faintest mag	   & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Regions\label{tab:Regions}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+\code{R_MIN} &	   \code{float} &   \\
+\code{R_MAX} &	   \code{float} &   \\
+\code{D_MIN} &	   \code{float} &   \\
+\code{D_MAX} &	   \code{float} &   \\
+\code{CHILD_S} &   \code{int} &            sequence number in full table of first child \\
+\code{CHILD_E} &   \code{int} &            sequence number in full table of last child + 1 \\
+\code{PARENT} &	   \code{int} &            sequence number in full table of parent \\
+\code{INDEX} &     \code{int} &            sequence number in full table of this entry \\
+\code{DEPTH} &	   \code{char} &           depth of this entry \\
+\code{CHILD} &	   \code{char} &           does this entry have children? \\
+\code{TABLE} &	   \code{char} &           does this entry have a table? \\
+\code{NAME} &      \code{char[21]} &       name / filename \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Image Overlaps\label{tab:ImageOverlaps}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Image ID          & & \\
+Region Table	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Filters\label{tab:Filters}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Filter ID         & & \\
+Filter name	  & & \\
+Photcode	  & & \\
+$\lambda_0$	  & & \\
+$\delta_\lambda$  & & \\
+$\epsilon$	  & & \\
+transmission curve& & \\
+time/date	  & & \\
+\hline		  
+\end{tabular}	  
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Photcodes\label{tab:Photcodes}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+\code{CODE} &        \code{unsigned short} &  code number (stored in Measure.source)  \\
+\code{NAME} &        \code{char[32]} & 	 name for filter combination  \\
+\code{TYPE} &        \code{char} & 	      	 PRI/SEC/DEP/REF  \\
+\code{C_LAM} &       \code{short} & 	      	 primary phot calibration terms (millimags)  \\
+\code{C_LAM_ERR} &   \code{short} & 	      	 primary phot calibration terms (millimags)  \\
+\code{X_ERR} &       \code{short} & 	      	 primary phot calibration terms (millimags)  \\
+\code{K} &           \code{float} & 	      	 secondary phot calibration terms (millimags)  \\
+\code{C1} &          \code{int} & 	      	 color is average.M[c1] - average.M[c2]  \\
+\code{C2} &          \code{int} & 	      	 color is average.M[c1] - average.M[c2]  \\
+\code{EQUIV} &       \code{int} & 	      	 this dependent filter is equivalent to equiv PRI/SEC \\
+\code{NC} &          \code{int} & 	      	 number of color terms  \\
+\code{X} &           \code{float[4]} &     	 \code{color terms X[0]*mc + X[1]*mc^2 + X[2]*mc^3}  \\
+Telescope	  & & \\
+Camera		  & & \\
+Detector	  & & \\
+Filter		  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Zero Point History\label{tab:Zpts}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline 
+\code{ZP_OBS} &     \code{float} &        measured zero point,       mag \\
+\code{ZP_REF} &     \code{float} &  	   nominal zero point,        mag \\
+\code{ZP_ERR} &     \code{float} &  	   error on zero point,       mag \\
+\code{C_AIRMASS} &  \code{float} &  	   airmass coeff,             mag per airmass \\
+\code{C_COLOR} &    \code{float} &  	   color coeff,               mag per mag \\
+\code{START_TIME} & \code{e_time} &       start time of measurement, seconds since 1 Jan 1970 UT \\
+\code{STOP_TIME} &  \code{e_time} &       stop time of measurement,  seconds since 1 Jan 1970 UT \\
+\code{C1_CODE} &    \code{short} &  	   code 1 for color,          photcode \\
+\code{C2_CODE} &    \code{short} &  	   code 2 for color,          photcode \\
+\code{PHOTCODE} &   \code{short} &  	   photcode,                  photcode \\
+\code{LABEL} &      \code{char[64]} &     data label \\
+\code{REFCODE} &    \code{rawshort} & 	   photcode,                  photcode \\
+\code{N_TIME} &     \code{int} &  	   number of times \\
+\code{N_MEAS} &     \code{int} &  	   number of measurements \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Distortion History\label{tab:Distortions}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+Camera            & & \\
+Telescope	  & & \\
+distortion terms  & & \\
+time/date	  & & \\
+residuals / error & & \\
+N stars		  & & \\
+N images	  & & \\
+astrom ref set	  & & \\
+\hline		  
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Database Hosts\label{tab:APDBHosts}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+machine name	  & & \\
+machine ID	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Solar System Objects\label{tab:SSObjs}}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype } & {\bf Description} \\
+\hline
+SSO ID     	  & & \\
+$N_{\rm det}$	  & & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\end{document}
+
+\begin{figure}
+\begin{center}
+\resizebox{4.5in}{!}{\includegraphics{pics/APDB}}
+\caption{DVO components}
+\label{fig:Components}
+\end{center}
+\end{figure}
+
+DVO provides interfaces to extract lists of objects and
+detections based on various query parameters.  It provides the
+capability to extract all detections associated with a specific
+object, all non-detections of that object, all non-detections of an
+orphan, and summary statistics from these collections.  It will also
+return all objects or detections within specified spatial regions
+including regions bounded by great circles (RA,DEC; GLAT,GLON;
+ELAT,ELON) and regions described by a location and a search radius.
+It will also return the image parameters associated with a specific
+detection including image coordinates of the detection, exposure time,
+time and date of the detection, etc.
+
+As shown in Figure~\ref{fig:Components}, DVO consists of the following
+components:
+
+\begin{itemize}
+\item AP Database database tables
+\item AP Database database engine
+\item AP Database servers
+\item AP Database client APIs
+\end{itemize}
+
+
+
+\subsection{Relphot data exclusion}
+
+relphot uses only a subset of the photometry data to calculate Mcal
+and Mrel. In the first stage, calculation of the Mcal values, relphot
+loads the photometry data from each relevant catalog and creates an
+internal subset catalog with the function bcatalog, excluding some of
+the irrelevant data. In addition, it uses flags to mark some of the
+data as invalid for the processing. bcatalog exclusions
+
+\begin{verbatim}
+    * measure.photcode not equivalent to requested photcode
+    * measure.dophot != 1
+    * measure.Mcat > MAG_LIM
+    * measure.dM > SIGMA_LIM
+    * measure.Minst out of range (ImagMin - ImagMax) [optional]
+    * measure.t out of range (TSTART, TSTOP) 
+\end{verbatim}
+
+flagged data 
+flagged image data
+
+images can be flagged by setting bits of image.code stars can be
+flagged by setting bits of average.code measures can be flagged by
+setting bits of measure.flag
+
+\begin{verbatim}
+image.code
+ID_IMAGE_NOCAL : ignore, irrelevant 
+ID_IMAGE_POOR : image measured bad
+ID_IMAGE_SKIP : externally known bad dMcal > VALUE 
+FLAG_IMAGE_SCATTER clean_images fabs(Mcal) > VALUE 
+FLAG_IMAGE_ZEROPT clean_images dMcal > VALUE 
+FLAG_IMAGE_SCATTER clean_mosaics fabs(Mcal) > VALUE
+FLAG_IMAGE_ZEROPT clean_mosaics mark_images does not seem to do
+anything useful? 
+average.code Ngood < MEAS_TOOFEW setMrel Ngood <
+MEAS_TOOFEW clean_measures ChiSq > STAR_CHISQ clean_stars dM >
+STAR_SCATTER clean_stars average.code (STAR_BAD) is not saved by
+relphot: it is set by clean_stars, clean_measures, and setMrel, but
+not setMrelOutput. STAR_BAD should only be internal since it depends
+on the photcode, but is not associated with a specific photcode in the
+data. Just in case, it is reset to 0 in setMrelFinal. measure.flag X,Y
+out of range setExclusions 3 sigma clipping clean_measures
+\end{verbatim}
+
+setting Mrel final value
+
+setMrelFinal is used to set the final average.Mrel values. We do this
+in 4 stages. In each stage, we set the Mrel values for stars which
+have not already been set, based on the current exclusion settings. At
+successive stages, we relax the exclusions, allowing the more spurious
+objects to have a valid Mrel value to be set. In this loop, we
+actually run setMrelOutput twice: once to get the approximate Mrel
+value, then we flag the outlier measurements with
+\code{clean_measure}, then we redetermine the Mrel values on this
+basis, and mark the stars for exclusion from the next iteration.
+
+\begin{verbatim}
+ exclude on
+  photcode       0 1 2 3
+  time range     0 1 2 3
+  MEAS_POOR      0 1 2 3
+  MEAS_TOOFEW    0 1 2 3
+  dophot == 10   0 1 2 
+  inst mag       0 1 2 
+  dophot != 1,2  0 1  
+  ID_IMAGE_POOR  0 1
+  ID_IMAGE_SKIP  0 1
+  dophot != 1    0
+  measure.dM     0 
+\end{verbatim}
+ 
+for all relphot runs, Mrel is re-calculated, and measures are marked at least if they are outliers in mag or ccd area. setMrel.output needs to do a few things differently from setMrel:
+
+\begin{verbatim}
+    * set measure.Mcal (skipped in setMrel.basic)
+    * set average.Mrel if N < TOO_FEW (not STAR_BAD) (optional!)
+    * use MAX (stats.error, stats.sigma) (optionally)
+    * allow STAR_BAD? 
+\end{verbatim}
+
Index: /tags/sj_tags/sj_root_20080929/doc/install/local_cpan.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/install/local_cpan.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/install/local_cpan.txt	(revision 22322)
@@ -0,0 +1,285 @@
+### Local (non-root) install of Perl module from CPAN
+
+To install modules from CPAN with CPAN.pm interface you need to setup a CPAN
+configuration file in your home directory.  The CPAN.pm will walk you through
+setting up the most important configuration values.  Unfortunately, there is
+some variation in the behavior of the various versions of CPAN.pm that have
+shipped with Perl.  Some (most) of these variants will not correctly create a
+configuration files that allows a non-root user to install modules outside of
+"system" paths.  In order to make sure that you get a "correct" CPAN
+configuration file you need to "prime" it with a few values.
+
+First you need to create the directory in which the CPAN configuration file will live.
+--
+mkdir -p .cpan/CPAN/
+--
+
+Then we need to create a partial configuration file.  Note that this example
+assumes that you want to install your perl modules under
+$HOME/jhroot/i686-pc-linux-gnu/lib/perl5.
+--
+echo "\$CPAN::Config = {" >> .cpan/CPAN/MyConfig.pm
+echo "  makepl_arg => q[PREFIX=$HOME/jhroot/i686-pc-linux-gnu]," >> .cpan/CPAN/MyConfig.pm
+echo "  mbuildpl_arg => q[--install_base $HOME/jhroot/i686-pc-linux-gnu]," >> .cpan/CPAN/MyConfig.pm
+echo "};" >> .cpan/CPAN/MyConfig.pm
+echo "1;" >> .cpan/CPAN/MyConfig.pm
+echo "__END__" >> .cpan/CPAN/MyConfig.pm
+--
+
+
+Now you need to invoke CPAN.pm so it can walk you through configuring the rest of the required values.  This is an example of one possible configuration with CPAN.com version 1.8802.  *Your version of CPAN.pm may present you with different prompts.*
+--
+perl -MCPAN -e shell
+CPAN: File::HomeDir loaded ok
+Sorry, we have to rerun the configuration dialog for CPAN.pm due to
+the following indispensable but missing parameters:
+
+build_cache, build_dir, cache_metadata, cpan_home, ftp_proxy, http_proxy, index_expire, inhibit_startup_message, keep_source_where, make_arg, make_install_arg, mbuild_arg, mbuild_install_arg, mbuild_install_build_command, no_proxy, prerequisites_policy, scan_cache, urllist
+
+
+The following questions are intended to help you with the
+configuration. The CPAN module needs a directory of its own to cache
+important index files and maybe keep a temporary mirror of CPAN files.
+This may be a site-wide directory or a personal directory.
+
+
+
+I see you already have a  directory
+    /home/moanui/jhoblitt/.cpan
+Shall we use it as the general CPAN build and cache directory?
+
+CPAN build and cache directory? [/home/moanui/jhoblitt/.cpan]
+
+
+Unless you are accessing the CPAN via the filesystem directly CPAN.pm
+needs to keep the source files it downloads somewhere. Please supply a
+directory where the downloaded files are to be kept. [/home/moanui/jhoblitt/.cpan/sources]
+Directory where the build process takes place? [/home/moanui/jhoblitt/.cpan/build]
+
+
+How big should the disk cache be for keeping the build directories
+with all the intermediate files?
+
+Cache size for build directory (in MB)? [100]
+
+
+The CPAN indexes are usually rebuilt once or twice per hour, but the
+typical CPAN mirror mirrors only once or twice per day. Depending on
+the quality of your mirror and your desire to be on the bleeding edge,
+you may want to set the following value to more or less than one day
+(which is the default). It determines after how many days CPAN.pm
+downloads new indexes.
+
+Let the index expire after how many days? [1]
+
+
+By default, each time the CPAN module is started, cache scanning is
+performed to keep the cache size in sync. To prevent this, answer
+'never'.
+
+Perform cache scanning (atstart or never)? [atstart]
+
+
+To considerably speed up the initial CPAN shell startup, it is
+possible to use Storable to create a cache of metadata. If Storable
+is not available, the normal index mechanism will be used.
+
+Cache metadata (yes/no)? [yes]
+
+
+The CPAN module can detect when a module which you are trying to build
+depends on prerequisites. If this happens, it can build the
+prerequisites for you automatically ('follow'), ask you for
+confirmation ('ask'), or just ignore them ('ignore'). Please set your
+policy to one of the three values.
+
+Policy on building prerequisites (follow, ask or ignore)? [ask] follow
+
+
+Every Makefile.PL is run by perl in a separate process. Likewise we
+run 'make' and 'make install' in separate processes. If you have
+any parameters (e.g. PREFIX, LIB, UNINST or the like) you want to
+pass to the calls, please specify them here.
+
+If you don't understand this question, just press ENTER.
+Parameters for the 'make' command?
+Typical frequently used setting:
+
+    -j3              # dual processor system
+
+Your choice:  []
+Parameters for the 'make install' command?
+Typical frequently used setting:
+
+    UNINST=1         # to always uninstall potentially conflicting files
+
+Your choice:  [] UNINST=1
+
+
+The next questions deal with Module::Build support.
+
+A Build.PL is run by perl in a separate process. Likewise we run
+'./Build' and './Build install' in separate processes. If you have any
+parameters you want to pass to the calls, please specify them here.
+
+Parameters for the './Build' command?
+Setting might be:
+
+    --extra_linker_flags -L/usr/foo/lib  # non-standard library location
+
+Your choice:  []
+Do you want to use a different command for './Build install'?
+Sudo users will probably prefer:
+
+    su root -c ./Build
+or
+    sudo ./Build
+or
+    /path1/to/sudo -u admin_account ./Build
+
+or some such. Your choice:  [./Build]
+Parameters for the './Build install' command?
+Typical frequently used setting:
+
+    --uninst 1                           # uninstall conflicting files
+
+Your choice:  [] --uninst 1
+
+
+If you're accessing the net via proxies, you can specify them in the
+CPAN configuration or via environment variables. The variable in
+the $CPAN::Config takes precedence.
+
+Your ftp_proxy? []
+Your http_proxy? []
+Your no_proxy? []
+You have no /home/moanui/jhoblitt/.cpan/sources/MIRRORED.BY
+  I'm trying to fetch one
+CPAN: LWP::UserAgent loaded ok
+Fetching with LWP:
+  http://www.perl.org/CPAN/MIRRORED.BY
+
+
+Now we need to know where your favorite CPAN sites are located. Push
+a few sites onto the array (just in case the first on the array won't
+work). If you are mirroring CPAN to your local workstation, specify a
+file: URL.
+
+First, pick a nearby continent and country by typing in the number(s)
+in front of the item(s) you want to select. You can pick several of
+each, separated by spaces. Then, you will be presented with a list of
+URLs of CPAN mirrors in the countries you selected, along with
+previously selected URLs. Select some of those URLs, or just keep the
+old list. Finally, you will be prompted for any extra URLs -- file:,
+ftp:, or http: -- that host a CPAN mirror.
+
+(1) Africa
+(2) Asia
+(3) Central America
+(4) Europe
+(5) North America
+(6) Oceania
+(7) South America
+Select your continent (or several nearby continents) [] 5
+
+(1) Bahamas
+(2) Canada
+(3) Mexico
+(4) United States
+Select your country (or several nearby countries) [] 4
+
+(1) ftp://carroll.cac.psu.edu/pub/CPAN/
+(2) ftp://cpan-du.viaverio.com/pub/CPAN/
+(3) ftp://cpan-sj.viaverio.com/pub/CPAN/
+(4) ftp://cpan.calvin.edu/pub/CPAN
+(5) ftp://cpan.cs.utah.edu/pub/CPAN/
+(6) ftp://cpan.cse.msu.edu/
+(7) ftp://cpan.erlbaum.net/CPAN/
+(8) ftp://cpan.glines.org/pub/CPAN/
+(9) ftp://cpan.hostrack.net/pub/CPAN
+(10) ftp://cpan.llarian.net/pub/CPAN/
+(11) ftp://cpan.mirrors.redwire.net/pub/CPAN/
+(12) ftp://cpan.mirrors.tds.net/pub/CPAN
+(13) ftp://cpan.netnitco.net/pub/mirrors/CPAN/
+(14) ftp://cpan.pair.com/pub/CPAN/
+(15) ftp://cpan.teleglobe.net/pub/CPAN
+(16) ftp://cpan.uchicago.edu/pub/CPAN/
+40 more items, hit RETURN to show them
+Select as many URLs as you like (by number),
+put them on one line, separated by blanks, hyphenated ranges allowed
+ e.g. '1 4 5' or '7 1-4 8' [] 14 11 12
+
+Enter another URL or RETURN to quit: []
+New set of picks:
+  ftp://cpan.pair.com/pub/CPAN/
+  ftp://cpan.mirrors.redwire.net/pub/CPAN/
+  ftp://cpan.mirrors.tds.net/pub/CPAN
+
+
+Please remember to call 'o conf commit' to make the config permanent!
+
+
+cpan shell -- CPAN exploration and modules installation (v1.8802)
+ReadLine support enabled
+
+ cpan[1]> o conf commit
+commit: wrote '/home/moanui/jhoblitt/.cpan/CPAN/MyConfig.pm'
+--
+
+
+Now we should install the basic compliment of helper modules that CPAN.pm needs to function fully.
+--
+install Bundle::CPAN
+--
+
+You can quit out of the CPAN shell at this point with the `exit` command or do the following few steps in another shell
+
+
+We're ready to install the full set Perl module dependencies for IPP software.  In order to make this process a bit easier on the end user a "Bundle" module has been created.  In order to use it you need to create a directory (if it doesn't already exist) called Bundle under your .cpan directory.
+
+--
+mkdir -p .cpan/Bundle
+--
+
+The file PS.pm should copied into this directory.
+
+--
+cp ~foo/PS.pm .cpan/Bundle/
+--
+
+Enter back into the CPAN shell and 'force' the install of the PS Bundle.  The
+'force' keyword instructs the shell to ignore any tests failures.  This is
+necessary as some of the modules 'DBD::mysql'/etc. require a properly working
+database setup in order for the tests to pass.  You will most likely be
+prompted for input by several the modules.  It is safe to answer with a
+carriage return to all questions.
+--
+perl -MCPAN -e shell
+force install Bundle:PS
+--
+
+
+In order to use another of these modules that were just installed when need to
+setup an environment variable called PERL5LIB so that 'perl' can find them.
+To do this, we need to know where under 'perl5' our modules were actually
+installed.  This will variable with the version of Perl that you are using.
+The easiest way to do this is just just look in the root of the path where we
+did the install.
+
+--
+`ls jhroot/i686-pc-linux-gnu/lib/perl5/`
+--
+
+Which should show something like:
+
+5.8.8  Test  site_perl
+
+That means were using perl 5.8.8 and PERL5LIB needs to be setup as following:
+--
+export PERL5LIB=$HOME/jhroot/i686-pc-linux-gnu/lib/perl5/5.8.8:$HOME/jhroot/i686-pc-linux-gnu/lib/perl5/site_perl/5.8.8
+--
+
+For instructions on installing Perl modules from CPAN ''by hand', see:
+
+    http://www.cs.ucsc.edu/~you/notes/perl-module-install.html
+
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.log *.dvi *.aux *.toc *.log *.out *.lof *.tbr *.tbd *.pdf
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/Makefile	(revision 22322)
@@ -0,0 +1,29 @@
+
+PDFLATEX = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): pdflatex
+PSLATEX  = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): latex
+
+help:
+	@echo "USAGE: make (target)"
+	@echo "  targets: ipptools all"
+
+ipptools: ipptools.pdf 
+all : ipptools
+
+%.pdf: %.tex
+	$(PSLATEX) $*.tex 
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	thumbpdf --modes=dvips $*.pdf
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	@rm -f $*.ps $*.dvi $*.aux $*.log $*.tbr $*.tbd $*.toc $*.tpm $*.lof body.tmp head.tmp
+
+clean :
+	$(RM) *.log *.dvi *.aux *.toc *.tbd *.tbr *.tpm *.lof *.out *~ core body.tmp head.tmp
+
+dist : clean
+	$(RM) *.pdf
+
+empty: clean
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/detselect.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/detselect.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/detselect.txt	(revision 22322)
@@ -0,0 +1,88 @@
+
+detselect queries the detrend database to select the best currently
+available detrend image matching the supplied criteria.  Detselect has
+two major modes, search and select:
+
+** detselect -search 
+
+detselect -search will examine the detrend database and return the
+entry detID+iteration which matches the request criteria.  The criteria include:
+
+camera
+detrend type
+
+These criteria are required for all 'detselect -search' invocations.
+Detselect should exit with an error state if these search criteria are
+not included.
+
+Other criteria include:
+
+date/time
+filter
+exptime
+detector temperature
+airmass
+label
+
+detselect should use the following rules to select the best detrend
+data for each query:
+
+* camera must match exactly
+* detrend type must match exactly
+
+for the remaining, if the value is supplied:
+
+* date/time must be > date_start and < date_stop
+* filter must match exactly (string match)
+* exptime must be > exptime_min and < exptime_max
+* airmass must be > airmass_min and < airmass_max
+* detector temperature must be > det_temp_min and < det_temp_max
+* label must match exactly
+
+if the above rules result is more than a single valid detrun, the one
+with the greatest value for date_create should be chosen.
+
+if the above rules do not match any detrun, then detselect must return
+the value NONE for the det_id.
+
+the output for a successful operation (including the empty set) should
+include:
+
+det_id, iteration, class, 
+
+
+** detselect -select -detID (det_id) -iter (iter) -classID (class_id)
+
+returns the specific master detrend imfile filename which matches the
+specified det_id, iteration, and class_id.  the output from this
+operation should consist of a single filename, which should have the
+value NONE if no match is found.  
+
+** detrend DB tables
+
+The detrend DB tables may be missing some fields needed to perform the
+operations above.  these may perhaps be placed in detRun when the
+detRun is defined.  here is the list of fields:
+
+date_start  : start of the validity range for the detrend data
+date_stop   : end of the validity range for the detrend data
+
+date_create : date when the detrend data was defined (perhaps this
+should be the date that the iteration was set, and should be stored in
+detRunSummary).
+
+filter       : filter name used to create this data
+det_temp_min : minimum valid detector temperature for this data set
+det_temp_max : maximum valid detector temperature for this data set
+airmass_min  : minimum valid airmass for this data set
+airmass_max  : minimum valid airmass for this data set
+exptime_min  : minimum valid exptime for this data set
+exptime_max  : minimum valid exptime for this data set
+label        : a string label used to distinguish related detrend frames 
+
+these values should be specified when creating the detrun, and are
+optional (the dettools should not enforce the requirements for
+specific detrend types).
+
+
+
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/dettool.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/dettool.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/dettool.txt	(revision 22322)
@@ -0,0 +1,425 @@
+Pan-Tasks has three modes.  The first determines what (if anything)
+needs to be done, the second does what needs to be done, and the third
+cleans up.  Our operations fit broadly into this pattern: get a list
+of files to process, process them, and then update the database.
+However, we have the choice as to whether to group the processing and
+the database update into a single operation, or to allow Pan-Tasks to
+handle these separately.  We choose to group them together using Perl
+(or other such) scripts, which will allow the tasks to be performed
+more easily in a manual fashion.
+
+
+--------------------------------------------------------------------------------
+
+The "dettool -pending" command returns a list of unassociated
+rawDetrendExp (rawDetrendExps that don't correspond to a exp_tag in
+detInputExp).
+
+	dettool -pending -exp_type <type> -telescope <telescope> -camera <camera>
+
+*** To do: other options, e.g., search by time period?
+
+With a list of exposures in hand, a detrend run may be initiated by listing the
+exposure ids:
+
+	dettool -definebyexp -det_type <bias|dark|flat|fringe> -exp_tag 1 -exp_tag 2 ...
+
+The above two commands ("dettool -pending" and "dettool -definebyexp")
+are envisioned as being principally used manually, and not by
+Pan-Tasks.
+
+
+At certain intervals (e.g., at the start of each night), Pan-Tasks
+will run the following command to define a new detrend run:
+
+	dettool -definebyquery -det_type <bias|dark|flat|fringe> -camera <camera> -time <time>
+
+"dettool -definebyquery" will define a new detRun semi-automatically
+by selecting the component exps with the specified search criteria.
+
+*** To do: other options?
+
+
+--------------------------------------------------------------------------------
+
+Pan-Tasks regularly runs:
+
+	dettool -toprocess
+
+This command returns a list of rawImfiles that are associated with an
+detInputExp that do not appear in detProcessedImfiles.  This list
+includes the detrend id, exposure id, class id, detrend type (used to
+generate the recipe for processing) and the uri.  These rawImfiles are
+processed by:
+
+	ppImage -file <imfile_uri> <processed_uri> -recipe PPIMAGE <ppImage_recipe> -stat <temporary_statistics_file>
+
+In addition to removing the nominated instrumental signatures, this
+also generates binned images to create a JPEG in the next step.  Once
+processing has been performed, "dettool -addprocessedimfile" adds an
+imfile to detProcessedImfiles:
+
+	dettool -addprocessedimfile -det_id <det_id> -exp_tag <exp_tag> -class_id <class_id> -uri <processed_uri> -recip <ppImage_recipe> -bg <stat> -bg_stdev <stat> -bg_mean_stdev <stat> -b1_uri <binned1_uri> -b2_uri <binned2_uri>
+
+In practise, the above "ppImage" and "dettool -addprocessedimfile"
+commands will be performed by a script, "detrend_process_imfile.pl".
+
+--------------------------------------------------------------------------------
+
+Pan-Tasks regularly runs:
+
+	dettool -toprocessedexp
+
+This command returns a list of exposures that have had all imfiles
+processed.  This list includes the exposure id and detrend id.  Using
+this exposure id, a list of imfiles may be gathered using:
+
+	dettool -processedimfile -exp_tag <exp_tag>
+
+The output includes class id, b1_uri, b2_uri, bg, bg_stdev and
+bg_mean_stdev for the processed imfiles.  These are combined into a
+JPEG by:
+
+	ppImage -list <file_list> <output_uri> -recipe PPIMAGE PPIMAGE_J
+
+The resultant image, along with summary statistics (calculated from
+the imfile statistics) is added into the database for the exposure:
+
+	dettool -addprocessedexp -det_id <det_id> -iteration <iteration> -exp_tag
+<exp_tag> -bg <stat> -bg_stdev <stat> -bg_mean_stdev <stat> -b1_uri <jpeg_uri>
+-b2_uri <jpeg_uri>
+
+In practise, the above "dettool -processedimfile", "ppImage" and "dettool
+-addprocessedexp" commands will be performed by a script,
+"detrend_process_exp.pl".
+
+--------------------------------------------------------------------------------
+
+<Iteration starts here>
+
+--------------------------------------------------------------------------------
+
+Pan-Tasks regularly runs:
+
+	dettool -tostack
+
+which returns a list of stacks that are ready to be performed.  This
+list includes the detrend id, iteration, class id and detrend types
+(note that class id is unique per detrend id).  In terms of the
+database, the command returns the list where the detrend id, iteration
+and class id triplet do not have an entry in detStackedImfile; the
+results are masked by entries in detResidExpAnalysis
+
+
+*** To do: may need to check detResidExp's even on iter 0 - ???
+
+For each stack that is to be performed, we obtain the list of input
+imfiles.  "dettool -processedimfile" returns the list of all processed
+imfiles from different exposures with the same class_id:
+
+	dettool -processedimfile -det_id <det_id> -class_id <class_id>
+
+The output includes the exposure id and uri for each of the input
+imfiles.  These are used for the stacking:
+
+	ppMerge <stacked_uri> <in_uri> <in_uri> <in_uri> .... -recipe PPMERGE <ppMerge_recipe> -stat STAT_FILE
+
+ppMerge returns statistics of the stack in the STAT_FILE.
+
+Once the stack has been completed, "dettool -addstacked" adds a
+stacked class_id:
+
+	dettool -addstacked -det_id <det_id> -iter <iter> -class_id <class_id> -recip <recipe> -uri <stacked_uri> -bg <stat> -bg_stdev <stat> -bg_mean_stdev <stat>
+
+In practise, the above three commands ("dettool -processedimfile", "ppMerge"
+and "dettool -addstacked") will be performed by a Perl script,
+"detrend_stack.pl".
+
+
+--------------------------------------------------------------------------------
+
+Pan-Tasks regularly runs:
+
+	dettool -tonormalizedstat
+
+This returns a list of detrend id, iteration and detrend type for
+which the entire set of class ids have been processed.
+
+For each of these detrend ids, we get the list of processed imfiles:
+
+	dettool -processedimfile -det_id <det_id>
+
+This returns the complete list of processed imfiles for the specified
+det_id, providing exposure id, class id, statistic which we use in the
+normalisation:
+
+	ppNormCalc INPUT.mdc
+
+where INPUT.mdc is a metadata config file with the stats for each
+imfile.  ppNormCalc outputs to stdout a metadata config format
+with the normalisation for each class id.
+
+*** To do: Get rms of solution (for both gain and exposure level) out
+	   of ppNormCalc.
+
+Then for each imfile that comprises the stack, we push the normalisations
+into the database:
+
+	dettool -addnormalizedstat -det_id <det_id> -iter <iter> -class_id <class_id> -norm <normalisation> 
+
+In practise, the above three commands ("dettool -processedimfile",
+"ppNormCalc" and "dettool -addnormalizedstat") will be performed by a Perl
+script, "detrend_norm_calc.pl".
+
+If the detrend type is not suitable for normalisation, then
+normalisation values of unity will be generated without solving the
+matrix with ppNormCalc.
+
+*** To do: possible upgrade path is to have "dettool -addnormalizedstat"
+	   parse a metadata config file from ppNorm to get the
+	   multiple normalisations, instead of running it several
+	   times (one for each class id).
+
+
+--------------------------------------------------------------------------------
+
+Pan-Tasks regularly runs:
+
+	dettool -tonormalize
+
+which returns a list of imfiles (with corresponding detrend id,
+iteration, class id, uri, statistic and normalisation) from
+detNormStats, which have no corresponding class_id inserted into
+detNormalizedImfile.
+
+For each imfile, we apply the normalisation, generate binned images
+and statistics:
+
+	ppImage -file <in_uri> <out_uri> -norm <normalisation> -stat <statictics_file>
+
+The normalised imfile (along with binned versions) is then inserted
+into detNormalizedImfile:
+
+	dettool -addnormalizedimfile -det_id <det_id> -iter <iter> -class_id <class_id> -uri <out_uri> -b1_uri <b1_uri> -b2_uri <b2_uri> -bg <stat> -bg_stdev <stat> -bg_mean_stdev <stat>
+
+In practise, the above two commands ("ppImage" and "dettool
+-addnormalizedimfile") will be performed by a Perl script,
+"detrend_norm_apply.pl".
+
+
+--------------------------------------------------------------------------------
+
+Pan-Tasks regularly runs:
+
+	dettool -tonormalizedexp
+
+which returns a list of stack exposures which have had normalised
+imfile summaries added.  This includes the detrend id and iteration.
+
+For each exposure, we get the list of component imfiles:
+
+	dettool -normalizedimfile -det_id <det_id> -iteration <iter>
+
+This includes the class ids, statistics (bg, bg_stdev, bg_mean_stdev)
+and the URIs for the binned images.  From these, we generate summary
+statistics for the exposure, and produce a JPEG:
+
+	ppImage -list <input_list> <out_uri> -recipe PPIMAGE PPIMAGE_J
+
+The statistics and JPEG uri are pushed into the database:
+
+	dettool -addnormalizedexp -det_id <det_id> -iteration <iteration> -b1_uri <jpeg1_uri> -b2_uri <jpeg2_uri> -bg <stat> -bg_stdev <stat> -bg_mean_stdev <stat>
+
+In practise, the above three commands ("dettool -stackimfile",
+"ppImage" and "dettool -addnormalizedexp") will be performed by a Perl
+script, "detrend_norm_exp.pl".
+
+--------------------------------------------------------------------------------
+
+Pan-Tasks regularly runs:
+
+	dettool -toresid
+
+which returns a list of processed imfiles (with corresponding detrend
+id, iteration, class id, uri, type) for which the appropriate stacked
+imfile has been normalised, and which have not been masked out by
+detResidImfileAnalysis; it also returns stackedImfiles that have
+"-pleasenormalize" set to false.
+
+For each of these imfiles, we generate the residual image, and binned images:
+
+	ppImage -file <processed_uri> <residual_uri> -recipe PPIMAGE <ppImage_residual_recipe> -stat <temporary_statistics_file>
+
+The residual is then inserted into the database:
+
+	dettool -addresidimfile -det_id <det_id> -exp_tag <exp_tag> -class_id <class_id> -recip <recipe> -bg <stat> -bg_stdev <stat> -bg_mean_stdev <stat> -b1_uri <b1_fits> -b2_uri <b2_fits> 
+
+In practise, the above two commands ("ppImage" and "dettool
+-addresidimfile") will be performed by a Perl script,
+"detrend_resid.pl".
+
+XXX Note that this block is not currently dep. on the results of the
+normlaized exp block above so they can run async with respect to each other.
+
+--------------------------------------------------------------------------------
+
+Pan-Tasks regularly runs:
+
+	dettool -toresidexp
+
+which returns a list of exposures for which all component class ids
+have had residuals created.  This list includes the detrend id,
+iteration, exposure id, detrend type, and whether the exposure was
+included in the stack for this iteration.
+
+We then obtain the list of components (with corresponding statistic)
+for that exposure:
+
+	dettool -residimfile -det_id <det_id> -iter <iter> -exp_tag <exp_tag>
+
+For these, we create the mosaic jpeg images:
+
+	ppImage -file <b1_fits> <something> -recipe PPIMAGE PPIMAGE_J
+	ppImage -file <b2_fits> <something> -recipe PPIMAGE PPIMAGE_J
+
+We also use the statistics from each of the components to decide if
+the exposure is behaving, and we generate summary statistics for that
+exposure.  If there are any problems with any of the components, we
+reject the entire exposure (not just the component chip that's causing
+problems; these could be removed by other means):
+
+	dettool -addresidexp -det_id <det_id> -iter <iter> -exp_tag <exp_tag> -recip <recipe> -b1_uri <uri_jpeg1> -b2_uri <uri_jpeg2> -bg <stat> -bg_stdev <stat> -bg_mean_stdev <stat> [-reject]
+
+where the "-reject" flag indicates that the exposure is not to be
+included in the stack for the next iteration.  If "-reject" is not
+specified, it sets the "accept" boolean to true.
+
+In practise, the above three commands ("dettool -residimfile",
+"ppImage" and "dettool -addresidexp") and the decision about whether to
+reject exposures would be performed by a Perl script, detrend_reject_imfile.pl"
+
+--------------------------------------------------------------------------------
+
+Pan-Tasks regularly runs:
+
+	dettool -toresiddetrun
+
+which returns a list of detrend runs (with detrend id, iteration and
+detrend type) which have completed all residexps.
+
+For each of these detrend runs, we get the list of all residexps
+(specified by their exposure id, along with the summary statistics and
+whether they were included in the stack for this iteration):
+
+	dettool -residexp -det_id <det_id> -iter <iter>
+
+For this list of exposures, we inspect the statistics for the group as
+a whole, decide which exposures are to be rejected, and generate a
+summary statistic for the detrend iteration.  For each exposure, we
+run:
+
+	dettool -updateresidexp -det_id <det_id> -iter <iter> -exp_tag <exp_tag> [-reject]
+
+where the "-reject" flag indicates that the exposure is not to be
+included in the stack for the next iteration.  If "-reject" is not
+specified, it sets the "accept" boolean to true.  Then for the current
+iteration as a whole we can run:
+
+	dettool -adddetrunsummary -det_id <det_id> -iter <iter> -bg <stat> -bg_stdev <stat> -bg_mean_stdev <stat> [-accept]
+
+Which allows per detrend run iteration summary statics to be
+registered and can flag the iteration's results as being acceptable
+for use as a master (if -accept is indicated).
+
+Once this has been done, a decision is made whether to make another
+iteration:
+
+	dettool -updatedetrun -det_id <det_id> -iter <iter> [-again|-state <state>] 
+
+where "-rerun" causes a new iteration to be started and "-state" sets the
+detrun into other of these states: "stop", "run", "reg".  The "stop" states
+causes all iteration to cease (and potentially alerts the human overlords).
+
+In practise, the above three commands ("dettool -residexp", "detool
+-updateresid" and "dettool -updatedetrun"), as well as the decisions
+about what to reject, and whether to rerun, accept or stop will be
+made by a Perl script, "detrend_reject_exp.pl".
+
+--------------------------------------------------------------------------------
+
+The detrend run may also be restarted manually by specifying exposures
+to accept and reject:
+
+	dettool -rerun -det_id <det_id> -iter <iter> -accept <exp_tag> -accept <exp_tag> ... -reject <exp_tag> -reject <exp_tag> ...
+
+--------------------------------------------------------------------------------
+
+Registering pre-created detrends
+
+    dettool -register_detrend ...
+
+Which creates a new detrend run set to the state of "reg".
+
+    detool -register_detrend_imfile -det_id <det_id> ...
+
+registers a new imfile into the detRun.  Note that this operation is a
+little dangerous as the validity of the det_id run isn't checked.
+
+    detool -updatedetrun -state stop
+
+Sets the dettool run's state to "stop" so that it will become visiable
+to detselect.
+
+
+--------------------------------------------------------------------------------
+
+Detrend Correction analysis (for flat-field corrections)
+
+To build a correction, we need to initial a p2/p3 analysis on a
+collection of images, supplying information so the analysis tools know
+to use the appropriate recipe (ie, which specifies that, for
+flat-field analysis, ppImage should use the 'RAW' version of a flat).  
+
+possible p2tool lines:
+
+  p2tool -queuerawexp [constraints] -set_label DVOCORR
+
+  p2tool -queuerawexp [constraints] -recipe DVOCORR -set_label DVOCORR.megacam.0001
+
+* should we tie the output DVO database to the recipe?
+* should we require the output DVO database as a db value?
+
+We need to wait until all of the selected images have been processed
+before we trigger the DVO analysis.  this could be done by blocking
+until all exposures with the specified label have successfully (or
+perhaps unsuccessfully) finished p3.
+
+* do we need to define a table which track these dvo_correction
+  'runs'?
+
+--------------------------------------------------------------------------------
+
+Detrend Correction application (for flat-field corrections)
+
+Assuming there is already a flat-field correction in the db for a
+given camera and filter, we need to (optionally) apply the correction
+to flat-field images as they are built by the detrend creation system.
+
+The first step is to select a flat-field image which requires a
+flat-field correction:
+
+  dettool -tocorrectrun -det_type skyflat.raw
+
+returns a list of detrun ids and iterations which have valid masters
+and for which there are no children.
+
+  detselect -select -det_id det_id -iteration iter 
+
+returns a list of imfiles to be corrected
+
+  dettool -register_detrend -parent det_id
+
+  dettool -register_detrend_imfile -parent det_id etc
+
+inserts the completed imfiles
+
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/filenames.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/filenames.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/filenames.txt	(revision 22322)
@@ -0,0 +1,71 @@
+Filename conventions for pipeline:
+
+Want to achieve something like: expId.state.classId.ext
+
+where expId is the unique exposure identifier;
+      state is the state of processing for that file;
+      classId is the class identifier, specifying a particular component of the exposure; and
+      ext is an extension indicating the type of file (e.g., fits).
+
+Constraint: classId cannot split components known only by the caller
+(pmFPAfile groups all of these into the {OUTPUT} variable).
+
+This constraint does not hinder us, since having the classId as the
+last element before the extension makes it simple to get all
+components of an exposure at a particular processing state.
+
+Some realisations:
+
+Input is typically:	expId.classId.fits
+
+Detrend processed:	expId.detproc.detID.classId.fits
+Detrend stats:		expId.detproc.detID.classId.stats
+Processed list, bin 1:	expId.detproc.detID.b1.list
+Processed list, bin 2:	expId.detproc.detID.b2.list
+Processed JPEG, bin 1:	expId.detproc.detID.b1.jpg
+Processed JPEG, bin 2:	expId.detproc.detID.b2.jpg
+Stacked detrend:	camera.detType.detId.iter.classId.fits
+Stacked stats:		camera.detType.detId.iter.classId.fits
+Normalised stack:	camera.detType.norm.detId.iter.classId.fits
+Detrend residual:	expId.detresid.detId.iter.classId.fits
+Residual stats:		expId.detresid.detId.iter.classId.stats
+Residual, bin 1:	expId.detresid.detId.iter.classId.b1.fits
+Residual, bin 2:	expId.detresid.detId.iter.classId.b2.fits
+Residual list, bin 1:	expId.detresid.detId.iter.b1.list
+Residual list, bin 2:	expId.detresid.detId.iter.b2.list
+Residual JPEG, bin 1:	expId.detresid.detId.iter.b1.jpg
+Residual JPEG, bin 1:	expId.detresid.detId.iter.b2.jpg
+
+Requirements:
+
+Explain PPIMAGE.OUTPUT, PPIMAGE.BIN1, PPIMAGE.BIN2, etc.
+
+#
+#  NEW CONVENTION
+#
+
+Naming convention
+--
+Files that are the result of IPP processing steps are to be named as follows:
+
+dir:
+    $exp_id/
+
+filename:
+    $exp_id.$step$seq.$chipname.fits
+
+Where $step is the 2 letter abbreviations for the step.
+    gyd  -> guide star/guidetool
+    chp  -> chip level processing/chiptool
+    cam  -> camera level processing/camtool
+    wrp  -> warp to sky/warptool
+    dif  -> image differencing/difftool
+    stk  -> image combination/stacktool
+
+Where $seq is the unique ID number provided by the [foo]tool at each step in
+the processing.
+
+Example
+--
+
+./oi4411o0021/oi4411o0021.ch0000001.ccd1.fits
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/ipptools.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/ipptools.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/ipptools.tex	(revision 22322)
@@ -0,0 +1,1999 @@
+\documentclass[panstarrs,spec]{panstarrs}
+
+\title{IPPTools Software Design Description}
+\subtitle{The IPP Analysis Stages: \\ Job Relationships, Database Tables, and Data Flow}
+\author{Eugene Magnier}
+\audience{IPP}
+\shorttitle{IPPTools SDD}
+\group{Pan-STARRS IPP}
+\project{Pan-STARRS IPP}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-019}
+
+\begin{document}
+\maketitle
+
+\tableofcontents
+\pagebreak 
+\pagenumbering{arabic}
+
+\section{Overview}
+
+This document defines the tasks used by PanTasks to control the data
+analysis stages of the IPP.  The document examines the specific tasks,
+the related Metadata Database tables, and the pipeline management
+commands used to connect these components together.  The Metadata
+Database is used to store the current IPP analysis state, as well as
+result processing data points.  The tasks needed to define each of the
+IPP analysis stages (Phase 1-4, detrend creation, etc) are illustrated
+in this document and the relevant MDDB tables are listed.  The
+collection of diagrams shows the IPP tasks and the Metadata Database
+tables needed to manage the flow of data through the system.  This
+document does not discuss in depth the interactions of various
+analysis programs with either DVO (the IPP Astrometric and Photometric
+Object database) or Nebulous (the IPP large data file management
+tool).
+
+\section{PanTasks Summary}
+
+PanTasks is the IPP tool which manages the sequencing of data analysis
+steps and, with the related tool `PControl', distributes the data
+processing across a cluster of computers.  This document is not a
+user's guide to PanTasks.  We will briefly discuss the capabilities of
+PanTasks to give the reader a basic working knowledge; for further
+usage details, please see the PanTasks user's guide.
+
+The purpose of PanTasks is to manage the automatic construction and
+execution of inter-related (often repetative) operations.  PanTasks
+uses a set of rules to define UNIX commands, and their corresponding
+command-line arguments, to be performed on some regular, repeated
+basis.  The utility of PanTasks is that it can easily define an
+analysis system which is completely state-based, as opposed to an
+event-driven system.  
+
+The two basic units of PanTasks operation are the 'task' and the
+'job'.  A 'job' is simply a command the user would execute on a
+command line; it consists of a command along with optional command
+line arguments.  A 'task' is a generic description of a type of job in
+which the details of any specific piece of data are omitted.  The task
+defines the UNIX command which corresponds to the job, and it provides
+rules for determining the identity of data for the job.  The task also
+defines tests which are used to decide if the job may be executed.
+Finally, it defines a polling frequency in which PanTasks should
+attempt to construct a new job for the task.
+
+For example, we may want to regularly copy files from one location to
+another.  Perhaps the name of the next available file is available in
+a database table, and can be retrieved with the command 'nextFile'.
+Perhaps we wish to check for new files every 60 seconds.  Thus, the
+task is to copy files, while a specific job of this task might be of
+the form {\tt cp newfile newpath}.  With PanTasks, we would define a
+copy task somewhat like the following:
+
+\begin{verbatim}
+  task copyfile
+    periods -exec 60.0
+
+    task.exec
+      $file = `nextFile`
+      if ($file == "none")
+        break
+      end
+      command cp $file $newpath
+    end
+
+    task.exit 0
+      queuepush copied $file
+    end
+
+    task.exit 1
+      queuepush failure $file
+    end
+  end
+\end{verbatim}
+
+In this simple example, the task is attempted every 60 seconds.  If
+there is no new file (output of 'nextFile' is 'none'), then no job
+results from this task.  If there is a new file, the copy command is
+performed.  This example is deceptively simple because one could
+easily imagine writing a stand-along program which performs the same
+thing.  The important advantages of using PanTasks for this type of
+operation are: 
+\begin{itemize}
+\item the output from one job can be used to spawn a number of other tasks.
+\item the success or failure state of each job can be used to spawn
+  other tasks.
+\item the details of the job and data test are kept separated from the
+  rules which connect tasks together.  
+\item the relationships between tasks are kept together in a single
+  location.
+\end{itemize}
+
+In addition to these organizational advantages, PanTasks also provides
+a direct connection to the tool for monitoring parallel jobs,
+pcontrol.  Thus the jobs spawned by PanTasks can be defined to run in
+the background locally or on any of the computers in the parallel
+processing cluster.
+
+\section{Persistent vs Ephemeral State in PanTasks}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.01.ps}
+\caption{\label{queues} PanTasks queues and MDDB tables}
+\end{center}
+\end{figure}
+
+The IPP, a fairly complex analysis system, uses PanTasks to select
+jobs, distribute them to the cluster, and harvest the results.  It
+uses the Metadata Database to record the results of a given analysis
+step, and to determine which jobs must be performed when.
+
+There are some subtleties in the interaction between PanTasks, the
+Metadata Database tables which store the system state, and the jobs
+which are currently being performed.  There is a choice to be made
+between rigorously maintaining the system state in the Metadata DB at
+all times or keeping an intermediate set of state tables.  Keeping the
+exact system state in the Metadata DB tables would require many extra
+queries to/from the database and may introduce additional latencies
+which are undesirable.  This is because any attempt by PanTasks to
+initiate a new job would require PanTasks to mark the corresponding
+data item in the Metadata DB (the item which acts as the trigger) with
+a `pending' state, and then mark it again as `done' when the job
+actually completes.  This also has the drawback that, if the system
+crashes (eg, hardware failure), some initial process would be required
+on start up to find all Metadata DB items which are in the `pending'
+state (examining all possible items which can be in such a state) and
+reset them to the `new' state.
+
+We implement an alternative in which PanTasks maintains an internal,
+ephemeral stack of the pending jobs, and only updates the system state
+entries in the Metadata DB when jobs are actually completed.  In this
+scenario, as far as the Metadata DB tables are concerned, data items
+transition only between a `new' and a `done' state.  Any jobs which
+are pending when the system crashes or the power is lost are simply
+dropped, and will be automatically re-constructed when the system
+restarts.  In this paradigm, no intermediate operation state is saved,
+and no partially completed job can be recovered.  Since the IPP is
+defined in terms of a fine granularity, with jobs lasting no more than
+30 - 120 seconds, crashes under this model will not have a large
+impact on the data processing.
+
+Figure~\ref{queues} illustrates this ephemeral vs persistent state
+information and the interrelation between the metadata tables and
+PanTasks.  The left-hand portion of the diagram illustrates the
+recommended interaction between the metadata database tables and
+PanTasks' internal queues.  Some table in the metadata database
+defines a list of data items which are to be processed by some
+analysis job.  PanTasks uses a two-step approach to define the
+analysis jobs based on this list.  First, one task queries the MDDB
+for a list of pending items, adds the returned items to an internal
+PanTasks queue.  The process of adding the elements to the queue is
+defined so that only unique items are added: already existing items
+are skipped.  The entries in the queue consist of the data items of
+interest and an internal temporary state.  At first, this would be
+`pending'.  A second tasks pops `pending' entries one-by-one from this
+internal queue, submits a job based on the entry, and sets the
+temporary state in the internal queue to `running'.  The internal
+state is needed to prevent PanTasks from re-submitting a job for the
+same data item before the first job is done or assessed.  Since the
+job make take an arbitrary amount of time, PanTasks requires a
+mechanism to remember which data items it has already submitted.  When
+the job eventually completes, the metadata database table is updated
+noting the completion.  This may be done either by the job itself or
+by PanTasks as part of the job exit rules.  In addition, the state of
+the entry in the queue can be set to either `done' or the entry can be
+simply removed from the queue.
+
+The purpose of this interaction is to maintain the temporary state
+information within non-persistent elements of PanTasks rather than
+using the metadata database tables to store this information.  This
+concept has two advantages.  First, PanTasks internal queues are in
+memory and relatively small, thus interfacing with them is quite fast
+for PanTasks -- this should reduce the system latency.  Second, by
+keeping this information non-persistent, the system responds correctly
+to stopping and restarting PanTasks.  Any jobs which have not been
+completed will not be marked in the database, and will be restarted
+naturally by PanTasks.  The alternative, of writing a temporary state
+marker in the database would require PanTasks, on startup, to
+initially clean all database tables of these temporary state markers.
+
+The right-hand portion of the diagram illustrates this process using
+the process of copying the images from the summit as an example.  The
+metadata database table of interest in this case is the list of
+pending images, with entries supplied by a job which queries the
+summit data systems.  The job which is actually performed is a remote
+copy of the image file from the location specified by the summit data
+system to the appropriate location within the IPP Image Server
+(Nebulous).  (As an alternative to the above, the `pending images'
+table may be part of the summit database system, and the `get images'
+command may query the summit directly.  In this scenario, the `copy
+image' command reports to the summit data system that an individual
+image file has been copied.)
+
+In the rest of this document, the use of PanTasks internal queues to
+manage the temporary data states is glossed over and assumed part of
+the tasks defined in the process.
+
+\section{IPP Pipelines Overview}
+
+The IPP as a whole performs all of the image analysis functions
+required by the Pan-STARRS telescopes, including images from the full
+Gigapixel camera (or cameras), the test camera TC-3, and the SkyProbe
+camera.  The IPP is designed to be very flexible, with instrument
+specific details isolated in configuration files associated with the
+different cameras known to the system.  As a result, the organization
+of the top level analysis infrastructure must be sufficiently general
+that a wide range of cameras can be accomodated.  We have a few
+general principles regarding constraints on the data to be processed
+which are used to guide the IPP design and developement:
+
+\begin{itemize}
+\item {\bf Camera Focal Plane Hierarchy} The IPP analysis programs
+  assume that the images to be processed are obtained by a camera
+  which can be represented by our Camera Focal-Plane Hierarchy of data
+  structures.  This hierarchy is discussed in detail in the Modules
+  SDRS, and defines a top-level {\em Focal-Plane Array (FPA)}, which
+  may contain 1 or more {\em Chips}, each of which may contain one or
+  more {\em Cells}.  An {\em FPA} is identified as having a single
+  optical system feeding photons to the detectors.  A {\em Chip} is
+  identified as a unit of data all deriving from a single detector
+  (piece of silicon), while a {\em Cell} is identified as a collection
+  of pixels read out as a continuous cartesian grid.  Finally, a
+  single collection of data from an {\em FPA} may include multiple
+  {\em Readouts} from any or all of the {\em Cells}.  
+
+\item {\bf Exposures vs Groups} The processing presumes that the data
+  is organized into {\em exposures} and exposure {\em groups}.  An
+  exposure represents the data from a single FPA, with the possible
+  subdivision of the exposure into multiple readouts for some or all
+  of the cells.  Exposure {\em Groups} are any group of exposures
+  which are related together in some way; the definition of the {\em
+  Groups} may be provided by the observers, or they may be derived
+  from the characteristics of the exposures.  The use of a particular
+  {\em group} depends on the context of that group.  A few examples of
+  exposure groups:
+
+  \begin{itemize}
+  \item a dithered sequence of exposures to be stacked for cosmetics
+  and improved signal-to-noise.
+  \item a twilight flat-field sequence.
+  \item all images of the same filter within a 10 degree region to be
+  used to construct an sample astrometric reference.  
+  \end{itemize}
+
+\item {\bf Image Files (imfiles) vs Exposures}  Any single exposure
+  may consist of a number of different data files.  The number of {\em
+  imfiles} for a given exposure will depend on the camera, as will the
+  data organization within those image files.  Also, a particular
+  camera will supply files corresponding to one of the particular
+  Focal-Plane Hierarchy elements.  The IPP analysis must be able to
+  interpret the incoming data correctly.
+\end{itemize}
+
+As discussed elsewhere, there are several major types of analysis
+performed by the IPP.  For the purposes of data organization and
+parallel processing efficiencies, we have identified the following
+divisions of the analysis tasks.  These will be discuss in much more
+detail below.
+
+\begin{itemize}
+\item {\bf Science Image Analysis} : This represents the analysis
+  performed on the images obtained by the telescope, and generally
+  performed in real-time, night-by-night.  The science image analysis
+  tasks are further subdivided as follows:
+
+  \begin{itemize}
+  \item {\bf Phase 1} : The full focal-plane array is examined quickly
+  to determine an initial astrometric calibration.  In this step, the
+  OTA guide stars may be used as the astrometric reference; if none
+  are available, predicted bright star positions are examined.  This
+  step is only used for mosaic images, and may be skipped if no guide
+  stars are available {\em and} the astrometric calibration for the
+  telescope / camera is reliable (better than 10 arcseconds).
+
+  \item {\bf Phase 2} : Each image file is analysed independently: the
+  image is detrended (bias, dark, flat, fringe, etc), sources are then
+  detected to a modest level, improved astrometric calibration is
+  performed.
+
+  \item {\bf Phase 3} : The collection of sources measured from all of
+  the image files for the camera are used to determine a global
+  astrometric, and possibly photometric, solution for the exposure.
+  This step is only required for mosaic cameras.
+
+  \item {\bf Phase 4.1} : An exposure group consisting of images
+  obtained in a specific region of the sky are merged together.  In
+  this step, the images are first warped to a common pixel grid, defined by
+  the static sky images.  The collection of images are then used to
+  construct a single, cleaned image by rejecting the outliers from the
+  source images in the stack.  The corresponding static sky pixels are
+  then used to construct a difference image from the resulting stack.
+
+  \item {\bf Magic} : In this step, the difference images are examined
+  to find the trailed images introduced by artificial satelites.
+  These so-called {\em streaks} are excised from the difference
+  images, as well as all of the source images which were used to
+  generate the difference images; the public data sources are updated
+  with the precise, correct time.  Note that this step requires that
+  separate difference images be generated for each of the input
+  images, a step which would be skipped if {\em magic} were avoided.
+  Also note that, until {\em magic} is performed, the publically
+  available time has a limited precision (probably $\sim 1$ minute
+  errors).  This step is only necessary in the operational IPP system
+  given the restrictions from the Air Force.
+
+  \item {\bf Phase 4.2} : After {\em magic} the final difference and
+  the final cleaned stacked image are produced and objects in both
+  images are detected.  The difference sources are used to mask the
+  extreme outliers in the cleaned stack, which is then used to update
+  the Static Sky images. 
+  \end{itemize}
+
+ \item {\bf Static Sky Image Analysis} : While the science image
+ analysis is performed as images are availablef, the static sky image
+ analysi occurs on a very different timescale.  In steady state, the
+ full static sky analysis will take place over the course of a full
+ year.  At any given time, the portion of the sky corresponding to the
+ location of the sun will be under-going the analysis.  In practice,
+ for PS-1, the static sky is produced in a somewhat different fashion
+ than in the steady-state model.  In PS-1, the different survey
+ strategies introduce very different update rates for the static sky.
+ At one extreme, the AP Survey will not have enough data for a
+ complete static sky analysis until nearly 22 months after the survey
+ begins.  At the other extreme, the deep survey, which observes a much
+ smaller portion of the sky, may best be analysed quite frequently.
+ These details are part of the science guidelines of the PS-1 surveys,
+ and are beyond the scope of this document.  Rather, the IPP Static
+ Sky Image Analysis must provide the capability of defining the static
+ sky analysis in a flexible and dynamic fashion.
+
+\item {\bf Basic Detrend Creation Analysis} : The analysis of most of
+  the detrend data is grouped together in a common analysis stage.
+  The differences between the analysis of the bias, dark, flat, and
+  fringe images is primarily one of how the input images are
+  pre-processed, what statistic is used to characterize a given input
+  image, how the input images are scaled before being combined, and
+  what normalization is applied to the resulting image.  All of these
+  types of detrend images can thus be processed with a single analysis
+  pipeline which is made aware of these minor differences.  This stage
+  is never the less fairly complex, and as a result is subdivided into
+  several compenents, as discussed below.
+
+\item {\bf Other analyses} There are a number of other tasks which the
+  IPP must perform that are not well-defined by the different analysis
+  types discussed above.  Some analysis tasks are not automatically
+  triggered, and are thus outside the scope of this document; these
+  are the tasks which are more properly considered as research
+  projects than analysis systems.  The other important automatic tasks
+  are:
+  \begin{itemize}
+    \item {\bf Summit Copy} : In this stage, the data source or data
+    sources are queried for new exposures and image files, which are
+    then copied to the IPP data area.  This stage also includes the
+    copying of other metadata which are not included in the image
+    files.
+    
+    \item {\bf Image Classification} : new images which are introduced
+    to the IPP are examined by this analysis stage and placed in the
+    appropriate table for processing.  This step includes a small
+    amount of accumulating statistics about the images.
+
+    \item {\bf Data File management} : a few tasks are necessary to
+    monitor and maintain the clustered storage system.  These tasks
+    include the automatic duplication and deletion of different types
+    of files from Nebulous, the file storage archive.  This also
+    includes automatic redistribution of machine assignments as
+    hardware is added or removed from the system.  This collection of
+    tasks also includes monitoring of system parameters to alert
+    people in case of dangerous hardware situations.
+
+    \item {\bf Irregular Calibration Data} certain types of
+    calibration information is extracted on different intervals from
+    the more regular detrend images.  These types of calibration data
+    include improved telescope pointing models, astrometric
+    calibrations, photometric calibrations, flat-field correction
+    frames.
+  \end{itemize}
+\end{itemize}
+
+\section{Tables, Tasks and Tools}
+
+The following sections discuss the database tables, the tasks within
+PanTasks, and the collection of programs used by PanTasks to examine
+and manipulate the state tables.  These later programs do not, in
+general, perform any in depth analysis; instead they perform actions
+such as selecting from one table images ready for analysis in a
+following processing step.  This collection of tools is grouped under
+the name of the {\tt ippTools}, and consists of a separate tool for
+each of the different major analysis steps.
+
+The {\tt ippTools} make use of {\em glueforge} to simplify the
+management of the database table schema.  Glueforge provides a single
+mechanism to generate a collection of C data structures, database
+tables, database access APIs, and I/O routines from a simple table
+description configuration file.  All APIs generated by {\em glueforge}
+for the same type of interaction have common naming schemes.  This
+technique has several important advantages.  It makes the writing of C
+database interactions very quick and easy.  It also makes it easy to
+modify the database schema without disrupting the software
+development.  Finally, it provides a simple, self-documenting source
+for data structure of multiple types which can be shared between
+programs or platforms.
+
+Within the following diagrams, we illustrate the database tables used
+to track the state of the IPP.  We also show the commands provided by
+{\tt ippTools} to connect the tables.  Finally, we show the IPP tasks
+which initiate the different analysis steps.  The following set of
+diagrams uses several consistent features.  The blue-and-grey
+rectangles define the metadata database tables.  The blue section
+contains the table name, while the grey section lists a minimal subset
+of the table columns.  The ellipses represent programs (or program
+portions in some cases) executed by PanTasks.  The blue filled
+ellipses represent the {\tt ippTools} commands which are executed
+locally on the computer hosting PanTasks.  The grey-blue ellipses
+represent the commands executed on the parallel cluster, monitored by
+{\tt pcontrol}.  The green ellipses represent commands executed by
+hand for testing and manual intervention.
+
+In most of the analysis tasks, we use a two-table approach to the data
+in order to avoid excessive latencies.  One table is used to track
+quantities which are still pending for a particular stage.  When the
+analysis is completed, these items are moved from the 'pending' tables
+to corresponding 'done' tables.  Although this introduces a somewhat
+higher number of tables and complexity, it will avoid the system from
+slowing down as the number of data items grows with time.  The pending
+tables are searched repeatedly by the {\tt ippTools} programs as they
+attempt to select new data of interest.  In contrast, the done tables
+are searched much less frequently.  
+
+\section{Summit Copy Tasks}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.02.ps}
+\caption{\label{pcopy} Summit Copy Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{pcopy} illustrates the MDDB tables used to copy data
+(images and metadata tables) from the summit.  The left-hand portion
+of the diagram shows the tables involved in copying images from the
+summit system.  The table of pending image files lists the URLs of the
+individual image files available for transfer, along with their
+associated exposure ID and the camera which generated the image.  Two
+other entries assist in interpreting the file: the class and the class
+ID.  The final entry in this table is the current copy state of the
+file, can have the value of `ready' or `copied'.
+
+The class defines the data grouping represented by this image file and
+may have values of: FPA, Chip, Cell.  This value indicates that the
+provided image file represents the specified portion of the camera
+FPA.  If the value is FPA, the file represents data from a complete
+FPA, though the file may contain pixel data in multiple extensions or
+other groupings to be identified later.  If the value is chip, the
+file contains only data for a single chip, presumably of multiple
+chips available, and equivalently for Cell.  Further discussion of the
+FPA image hierarchy is given in the IPP documents (eg, Modules SDRS).
+The class ID gives the identifier used to name the class level
+corresponding to this file.  This value is necessary to make decisions
+on how to copy the data based on the chip / cell before the data is
+available to IPP components.  Table~\ref{classes} lists likely values
+for the class and class ID for some common cameras.  The system
+described is sufficiently flexible to allow us to transfer the GPC
+images by cell if we eventually decide that is more efficient.
+
+The copy process copies the file from the given URL to the appropriate
+IPP node and adds an entry to the table of new image files, consisting
+of the same information as the pending image file table, though with a
+new value for the URL.  This URL may be an explicit filename, a
+reference to an entry in the image server, or a web address, or
+located on the image server (marked with file:, neb:, and http:,
+respectively).  (TBD: other possible file storage types?  perhaps the
+path could be abstracted without going to the level of the image
+server?  eg: ref:DIR0001/file0001.fits might be in a directory which
+is defined in a table of directories.) After an image file is
+successfully copied, the corresponding state in the `pending chip'
+table is updated from `ready' to `copied'.
+
+\begin{table}
+\begin{center}
+\caption{Camera and Data Classes\label{classes}}
+\begin{tabular}{llll}
+\hline
+\hline
+camera   & class  & classID \\
+\hline
+GPC	 & chip   & chip02 \\
+skyprobe & fpa 	  & sp01 \\
+Megacam  & fpa	  & MegacamSpliced \\
+Suprime	 & chip	  & chip0 \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+The right hand portion of this diagram illustrates the process of
+copying a metadata table.  The table of pending tables lists the URLs
+for the tables which are ready, a unique table ID for each table, and
+the table type.  The copy function copies the listed table and uploads
+the data to the IPP version of the same metadata database.  Two
+examples of metadata tables needed by the IPP for the basic image
+processing system are illustrated: the table of new exposures and the
+table of pending matches.  The first lists the exposures which are
+avilable from the summit system, and all represent entries which are
+available from the Image server.  the second represents the matches
+between exposure IDs and chips
+
+\section{Phase 0}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.03.ps}
+\caption{\label{phase0} Phase 0 Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{phase0} illustrates phase 0, in which the image files are
+categorised, examined for summary information and basic statistics,
+and moved to the later phase 'pending' tables to trigger further
+analysis.  The command {\tt p0search -pending} examines the `new
+imfiles' and 'new exposure' tables.  It selects images from this table
+which have not yet been examined (state is `new').  These are returned
+to PanTasks, which sends each image file to a separate analysis node
+running the {\tt p0search -update} command.  With this command, the
+file header is examined and relevant metadata is extracted (eg, RA,
+DEC, times, and so forth to be defined later).  The process may also
+select a portion of the image pixel data to determine a rough bias and
+background level.  These statistics, whether derived from the header
+or the pixel values, are placed along with image summary information
+in the `raw image files' table, and the state field of the `new image
+files' table is set to `ready'.
+
+The {\tt p0search -update} command is also responsible for moving the
+exposures to the tables used for triggering the analysis process.  If
+the image class is FPA, the image can be advanced without waiting for
+any other image files.  If the class is Chip or Cell, the process must
+also examine the `new exposure' table for this exposure ID.  The
+number of class files available for this exposure is listed in this
+table.  The process must the select all image files matching the
+exposure ID with state of `ready' and compare the number avalable to
+the number expected.  If the two match, then a new exposure is ready.
+Based on the image type (from the most recently examined image file
+header or new exp table?), the exposure is added to the `raw exposure'
+table for images of that type.  The allowed types are `detrend', (all
+bias, dark, flat images), `object', `focus'(??), etc.  (** The
+different tables represent different analysis modes.  This process
+also adds an entry to the exp ID / image file match **).  This process
+also adds all science (OBJECT) exposures to the P1 exposure table (for
+mosaic data) or the P2 chip table (for single detector data).  These
+tables are used to trigger the Phase 1 and Phase 2 analysis stages.
+
+\section{Phase 1}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.04.ps}
+\caption{\label{phase1} Phase 1 Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{phase1} shows the tables involved in running the Phase 1
+analysis stage.  There are paths for exposures to enter the analysis
+automatically from the Phase 0 analysis (arrow on left) or to be added
+manually based on a selection from the raw exposure table.  Exposures
+to be analysed by Phase 1 are added to the P1 exposure table with the
+state `new'.  Exposures may be added multiple times for processing and
+reprocessing. The P1 done exposure table keeps a record of the old
+attempts for debugging and analysis.  Each time an exposure is added
+to the P1 exp table, it is given a new, unique version number,
+allowing the system as a whole to track different analysis attempts.
+This method is used in all of the image analysis stages (and
+extrapolated to iterations in the detrend analysis steps below).  The
+top portion of the diagram shows the use of the command {\tt p1search
+-define} to select and submit an exposure or a group of exposures,
+potentially selected on the basis of a query from the raw science
+exposure table.
+
+The P1 pending exposure table is examined by {\tt p1search -pending}
+to select the new exposures, which are sent to PanTasks.  PanTasks
+initiates a separate analysis job (p1astro) for each exposure, which
+are sent to the parallel processing nodes.  Within the analysis job,
+the chips (image files) associated with the exposure are select from
+the raw image file table.  The analysis examines the contents of these
+files, either extract the guide star information from the image files
+(GS table extension) or searches for and centroids the pixels on
+appropriate bright stars.  The analysis results in astrometric
+calibration terms which are written to the astrometric calibration
+file for this exposure.  The location of the astrometric calibration
+file and the statistics of the measurement are written back to the P1
+exposure table.  The images associated with exposures which are
+successfully processed by P1 are then added to the P2 image table,
+which is used to trigger the Phase 2 analysis.  This last step is
+performed by the command {\tt p1search -done}, which is executed
+regularly to search for completed Phase 1 jobs.
+
+\section{Phase 2}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.05.ps}
+\caption{\label{phase2} Phase 2 Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{phase2} shows the tables involved in running the P2
+analysis stage.  There are paths for images to enter the analysis
+automatically from the P1 analysis (arrow on left) or to be added
+manually based on a selection from the raw exposure and raw image file
+tables.  Image files to be analysed by Phase 2 are added to the P2
+pending imfiles table with the state `new'.  When images are added to
+this table, a single entry is also added to the P2 exposure table
+listing the P1 and P2 versions for this exposure.  These version
+numbers must be integers starting with 1.  If this image did not have
+a P1 analysis, the P1 version is set to 0.  Exposures may be added
+multiple times for processing and reprocessing. The P2 image table
+keeps a record of the old attempts for debugging and analysis.  As
+with P1, each time a collection of associated images from an exposure
+is added to the P2 image table, it is given a new, unique version
+number, allowing the system as a whole to track different analysis
+attempts.  Note that these version numbers are unique for each {\em
+exposure} processed by Phase 2, not just for any image file.  The top
+portion of the diagram illustrates the behavior of the commands {\tt
+p2search -define} and {\tt p2search -quick}.  The first may be used to
+re-submit the images for an exposure or a group of exposures,
+potentially selected on the basis of a query from the raw science
+exposure and raw image file tables.  The second version sends images
+files directly to PanTasks for processing; these entries will not be
+included in the processing tables, and is used only for testing
+purposes.
+
+The P2 pending image table is examined with the command {\tt p2search
+  -pending} to select the `new' images.  These images are used by
+PanTasks to generate P2 analysis jobs, running the analysis command
+{\tt ppImage}.  The P2 analysis uses the input url to find and load
+the image file.  The url may be a file on disk, an entry in the image
+server, Nebulous, etc.  The master detrend images matching the
+specific science image and the conditions are selected by examining
+the table of master detrend frames.  The specific detrend image files
+are selected by using the master detrend ID to select the matching the
+entries in the table of master detrend files.  After the analysis, the
+output image, mask, and FITS table of objects, including the
+astrometry calibration, are written back to the P2 image table, along
+with summary statistics from the P2 analysis.  The state is also
+updated (to `done').
+
+The completed images are examined by the command {\tt p2search -done},
+and when all image files for a single exposure are completed, this
+command migrates them to the P2 done table.  This process is also
+responsible for populating the P3 pending tables so exposures may be
+processing by Phase 3.
+
+\section{Phase 3}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.06.ps}
+\caption{\label{phase3} Phase 3 Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{phase3} illustrates the tables and commands involved in
+the Phase 3 analysis.  The P3 pending exposure table lists the
+exposure ID, the P3 analysis version, the P2 analysis version to be
+used as input to this P3 analysis, and the recipe to be used.  The
+command {\tt p3search -pending} extracts exposures from this table and
+provides them to PanTasks for processing.  PanTasks launches a Phase 3
+analysis (the command {\tt psastro}?) for each exposure.  In this
+analysis, the P2 exposure and image tables are used, in conjunction
+with the P2 version information, to select the P2 output measured
+objects and the astrometric calibrations from P2 and P1.  These
+measured objects are matched with the reference catalog objects, and
+calibrated astrometry {\em and eventually photometry} is produced for
+the full exposure.  The location of the resulting astometry
+calibration table is stored back in the P3 exposure table.  If the
+recipe file specifies, the 2-D photometric and background / fringe
+corrections may also be performed at this stage.  Since these analyses
+require reference data, the recipe may also be used to skip these
+analysis if such reference data is unavailable or unreliable.  At the
+end of Phase 3, the objects from the exposure are inserted into the
+photometry database (this is not shown).
+
+The astrometric calibration portion of Phase 3 is principally needed
+for a mosaic camera.  For single-chip cameras, Phase 3 may be used to
+perform the photometric calibration and simply pass the astrometric
+results along to the output file to be listed in the P3 exposure
+table.  In this way, later stages of the analysis (ie, Phase 4) can
+use the P3 exposure table as input for all cameras, even if all the
+funcionality of Phase 3 is not necessary for that camera.  This would
+be the case for the skyprobe camera, for example.
+
+\section{Phase 4}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.07.ps}
+\caption{\label{phase4} Phase 4 Tasks}
+\end{center}
+\end{figure}
+
+At the end of Phase 3, the images are ready for Phase 4.  The Phase 3
+diagram shows the output line adding the exposures to be processed by
+Phase 4 to a Phase 4 table.  However, this line is just for
+illustration purposes.  The rules for initiating a Phase 4 run are
+somewhat more complicated than those for running Phases 1-3.  Groups
+of exposures which have an appropriate overlap should be chosen for
+the Phase 4 analysis.  In the steady-state period of PS-4, it may be
+straightforward to choose the exposure groups: they would simply be
+the exposures obtained nearly simultaneously by the four separate
+cameras.  The circumstance for PS-1 will be much more complicated (and
+even PS-4 will probably be more complex than it seems at first
+glance).  For example, in PS-1, we will not have a static sky for most
+of the AP Survery.  In this circumstance, we cannot run P4, at least
+until after the complete AP Reference catalog is built, and
+potentially all exposures re-run through Phase 3.  It may be useful
+for the AP Survey data to split the Phase 4 analysis into two stages:
+image combination and image differencing.  It may even be the case
+that only the combination portion of Phase 4 is performed on the AP
+Survey data.
+
+More generally, the image groups selected for Phase 4 analysis may be
+chosen on the basis of a query of the AP Database (DVO) with some
+rules.  
+
+\note{Phase 4 run can be defined by selecting an observation group, a
+  set of exposures, or a set of rules related to a spatial region (eg,
+  region, time range, and filter}.
+
+\note{Phase 4 discussion (and diagram) needs more work}
+
+\section{Analysis Version and Recipes}
+
+Note that each of the stages P1-P4 refer to the processing version
+from the previous stage.  This allows the processing stage to request
+the correct version of the results from the previous stage, and makes
+it possible to run and re-run the analysis at any stage without
+deleting the earlier results.  As different analysis attempts are
+performed for a given image, the versions branch out.
+
+Also note that at every stage, the entries include a recipe
+identifier.  This is used to select the analysis recipe which should
+be used for this version.  By default, the recipe should be set to the
+current best recipe (use a default name for this?).  This feature
+allows the user to run test analyses with variations on the recipe
+without altering the analysis system.  For example, it is possible to
+use a different flat-field set by specifying alternate rules for the
+flat-field selection in a recipe file.  If it is necessary to run the
+P1-P3 analysis with the raw master flats, for example, the user simply
+defines that selection in the recipe file and submits the images of
+interest to P1 (or P2, etc), with the corresponding entry for the
+recipe.
+
+The recipe file may also be used to specify alternative analysis paths
+and desitinations.  For example, it is not necessary that all analysis
+stops with P4: the recipe file may be used to halt the analysis at P2
+or P3.  In addition, the recipe file may be used to specify an
+alternative destination for the output results.  For example, to
+generate the photometric flat-field correction frame from a collection
+of dithered images, the user may not want the photometry results in
+the main DVO database.  By using the recipe to set an alternative DVO
+database target, and by specifying the use of the raw master flat
+rather than the corrected one, the analysis of the dithered images is
+kept isolated from the other photometry database results.  The
+resulting photometry may be used to construct the new, corrected
+flat-field images, and the processing of the same images using the new
+flat-field images may be sent to the master DVO database.  
+
+\section{Basic Detrend Creation}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.08.ps}
+\caption{\label{detrend} Detrend Creation Tasks}
+\end{center}
+\end{figure}
+
+Figure~\ref{detrend} illustrates the tables needed for the generic
+detrend construction process, using the flat-field construction as an
+example.  This diagram is somewhat more complex than the preceeding
+versions.  In this diagram, both single jobs and multiple jobs are
+represented by the process elements (the blue ellipses).  In some
+cases, more that one task will be needed to perform the function
+illustrated by a single process task.  The complexity of this diagram
+is enhanced by the need for multiple iterations and both single chip
+and full mosaic processing.  At the moment, the distinction between
+mosaic and single chip cameras is not specifically discussed.
+Finally, the triggers which initiate a specific detrend analysis are
+glossed over.
+
+The detrend analysis is initiated by choosing a type of detrend image
+to be constructed and by specifying the criteria which will be used to
+select the input raw detrend frames for the construction.  For
+example, these criteria could specify that all twilight flat images
+over a certain period of days, perhaps with restrictions on the flux
+levels or the time-from-sunset of the images.  The detrend analysis
+run is given an ID (det ID) which will also be used to identify the
+resulting master detrend frame.  
+
+Given the definition of a master detrend run, the input exposures are
+selected from the raw detrend exposure table, and written to the input
+detrend exposure table.  In the next step, the corresponding image
+files are selected from the table of raw image files.  Since there
+will be a different set of input raw images for each attempt at
+creating a master detrend image, and since any given attempt may use
+some of the same input images as any other attempt, a separate table
+of input raw images is constructed.  
+
+Each of the input raw images may be pre-processed before it may be
+used to construct the detrend frame.  For example, the input
+flat-field images should (probably) be dark- and bias-corrected before
+they are stacked.  The information about these input processed images
+is written to the input images table.  If no processing is needed,
+this step simply copies the appropriate information to the table, and
+points back to the raw image, rather than a processed version.  
+
+The input processed images are combined (stacked) to create a master
+detrend image for the particular data element defined by the image
+class (chip/cell/fpa).  At this stage, not all input images should
+necessarily be included in the stack.  If residual statistics have
+been measured for the input images (say, using a prior stack), then
+some of the input image may be excluded.  The table of residual images
+is used to guide this process.  The information describing the
+resulting master image is written to the master images table.  
+
+The statistics of the master detrend images must examined so that any
+necessary renormalizations may be performed.  For example, after
+stacking the individual flat images, the resulting stacks must be
+renomalized to account for the different ranges of input image fluxes.
+This analysis is least-squares solution in which an appropriate scale
+is determined for each input exposure and a separate gain is
+determined for each of the chips or cells in the camera.  This
+analysis can only performed after all image stacks (ie, for all chips)
+have been constructed.  The resulting information is written to the
+table of master detrend frames.  
+
+Once the master detrend is constructed, the master detrend images may
+be used to construct residual images for each of the input images.
+These residual statistics, as well as the locations of the residual
+images and other related data products (jpeg thumbnails?) are written
+to the residual image table.  Note the red arrow which by-passes the
+stack construction and merge steps and skips directly to the residual
+analysis.  In some cases, it may be useful to have the input images
+confronted with an existing detrend image, and the resulting residual
+values used to guide the rest of the process.  For example, in the
+flat-field analysis, applying an earlier flat can result in a very
+good first-pass rejection of poor input images.  The logic to make
+this leap must be part of PanTasks, since each of the individual
+blocks represent complete processing jobs.
+
+Finally, the residual statistics from the complete mosaic (all input
+images, all chips) are used to assess the quality of the newly
+constructed master detrend image, and to potentially modify the
+selection of input images.  This latter process is performed by
+marking the state of the residual images from this iteration.  The
+stacking process always examines the state information for the
+residual images from the previous iteration, if it exists, when
+constructing the master stack.  Once a master detrend frame has been
+judged of high enough quality, the state of the entry for the frame in
+the master detrend frames table is set to an appropriate value to tell
+the other routines that this image should be used as a master detrend.
+The exact choice of which master detrend frame is used for a given
+science image depends on the recipe along with information such as the
+time period used or the conditions used.
+
+Note that, although this discussion focuses on the construction of
+flat-field images, the same structure should be capable of
+constructing the biases, dark, fringes, etc.  In some cases, as noted
+above, the `process' stage is a null operation.
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.09.ps}
+\caption{\label{detprocess} Detrend Creation : Process Tasks}
+\end{center}
+\end{figure}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.10.ps}
+\caption{\label{detresid} Detrend Creation : Residual Tasks}
+\end{center}
+\end{figure}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85]{pics/ipptools.11.ps}
+\caption{\label{detstack} Detrend Creation : Stack and Norm}
+\end{center}
+\end{figure}
+
+\pagebreak
+
+\appendix
+\section{IPP top-level commands}
+
+In this section, we define all the necessary top-level commands needed
+by the IPP to implement the data flow discussed in the previous
+sections.  These commands are user commands, and are visible in the
+UNIX command space.  The commands are discussed in the context of both
+the automatic processing and processing of individual entries.  It is
+an important goal that the user commands should provide a
+simple-enough interface that they can be used without needing to have
+the entire infrastructure of the IPP in place to function.  In
+practice, this means that data items which can be acquired from the
+Metadata DB tables can also be provided on the command line and/or in
+the recipe file.  In some cases, user commands are provided to allow a
+manual intervention beyond the automatic processing loops.  This is
+particularly true of the \code{submit.Px} type of commands.  
+
+\tbd{this section should be reconciled with the section in the SSDD which takes precedence}
+
+\begin{verbatim}
+
+copy.image
+  input: url, exp ID, camera, class, class ID
+
+  This program copies the image file from the given URL, updates the
+  new image file table with the descriptive metadata (exp ID, camera,
+  class, class ID), and notifies the external subsystem that the copy
+  has succeeded.  The destination host for the image file is
+  determined by selecting the class ID from the host-for-class table.
+
+copy.table
+  input: url, table ID, table type
+
+  This program copies the metadata table file from the given URL,
+  determines which IPP metadata table it corresponds to, adds the
+  table rows to that metadata table, and notifies the external
+  subsystem that the table copy has succeeded.
+
+classify.image
+  input: (url) or (exp ID + class ID)
+
+  This program examines the header of the specified image file,
+  determines the image type (detrend, science, etc), extracts a
+  specific set of information from the header, and adds a new entry to
+  the raw image files table.  It also checks the new exposure table
+  for the corresponding exp ID, counts the files with the same exp ID
+  that are in the raw image files table, and if they match, adds an
+  entry to the raw exposure table for the appropriate exposure type.
+  Depending on the camera format (mosaic / single chip), the process
+  also adds an entry to either the P1 exposure table or the P2 image table.
+  
+Phase 0 commands:
+
+p0search -pending :
+  * examine the new.imfiles,new.exposures tables and select exposures ready for analysis
+  * output is: (expID) (camera)
+
+p0search -update (expID) (camera):
+  * select a the corresponding images from the new.imfiles/new.exposures table
+  * extract the specified header information
+  * search the summit metadata db tables
+  * write an entry to the raw.imfiles and raw.exposure tables
+  * set the state on the new.imfiles,new.exposure tables
+  * based on the camera config information;
+    * add an entry to the p1.pending table (mosaic) 
+    - or
+    * add an entry to the p2.pending table (single)
+
+p0search -stats (expID) (camera):
+  * select a specified image in the new.imfiles/new.exposures table
+  * extract the specified header information
+  * search the summit metadata db tables
+  * report the image stats
+  [-update without output to MDDB]
+
+p0search -mkraw (expID) (camera):
+  * select a specified image in the new.imfiles/new.exposures table
+  * extract the specified header information
+  * search the summit metadata db tables
+  * write an entry to the raw.imfiles and raw.exposure tables
+  * set the state on the new.imfiles,new.exposure tables
+
+p0search -cleanup:
+  * remove completed entries from the new.imfiles,new.exposure tables
+
+** note : the division of -pending and -update allows separate processes
+   to be examining the image headers and measuring some stats.  these
+   jobs can be run via pcontrol to reduce the load on the PanTasks
+   machines
+
+Phase 1 pipeline tools:
+
+p1search -define [constraints]:
+  * examine the raw.exposures tables and select exposures matching the given criteria
+  * add entries which are allowed (mosaic) to the p1.pending table
+
+p1search -pending :
+  * examine the p1.pending table and select exposures waiting for p1
+  * output: lines consisting of:
+    (expID) (p1version) (camera)
+
+p1search -done :
+  * select completed entries in the p1.pending table
+  * move to the p1.done table
+  * add new entry to the p2.pending tables
+
+Phase 2 pipeline tools:
+
+p2search -quick 
+  * search for images which match in raw.exp,raw.imfiles
+  * output in format which can be used by ppImage pantasks script
+
+p2search -define [options]
+  * input: searches mddb:raw_exposures,raw_images
+  * output: updates mddb:P2_exposures_pending,P2_images_pending
+  * alternative output: identical to p2pending
+ 
+p2search -pending 
+  * input: searches mddb:P2_exposures_pending,P2_images_pending
+  * output: Nlines consisting of:
+    (URL) (expID) (class)
+  * options: ?
+
+p2search -update
+  * examine the imfiles and identify any completed exposures
+
+p2search -done
+  * add completed exposures to the p2.done tables
+  * remove corresponding entries from the p2.pending table
+  * send new entry to the pending p3 table
+
+ppImage file://path/filename file://path/outroot -recipe (recipe) 
+ppImage neb://nebname neb://outroot -recipe (recipe)
+
+restriction options:
+  -time (start) (stop)
+  -camera (camera) 
+  -region (ra,dec) (ra,dec)
+
+
+Phase 3 pipeline tools:
+
+p3search -define :
+  * examine the raw.exposures tables and select exposures matching the given criteria
+  * add entries which are allowed (mosaic) to the p3.pending table
+
+p3search -quick :
+  * examine the raw.exposures tables and select exposures matching the given criteria
+  * return list of entries for p3 processing
+  * output: lines consisting of:
+    (expID) (p3version) (camera)
+
+p3search -pending :
+  * examine the p3.pending exposures table and select exposures waiting for p3
+  * return list of entries for p3 processing
+  * output: lines consisting of:
+    (expID) (p3version) (camera)
+
+p3search -done :
+  * select completed entries in the p3.pending table
+  * move to the p3.done table
+
+
+mkdetrend tools
+
+dettools -define [options]
+ * define a new detRun, specifying the constraints, and adding it to
+ the run table.  if the detRun ID already exists, creates a new
+ version.  the initial state is set to START.  the iteration is set to
+ 0. also creates a new master detrend frame entry
+ (detID.version.iteration define this frame uniquely).
+ * select the input matching a given detRun.  selects the input
+ exposures and the input files.
+
+options : 
+  -ID ID : a free-form string; if not specified, a unique string is constructed
+  -type type : bias dark (mask?) flat fringe (other?)
+  -camera camera 
+  -filter filter
+  -time start stop
+  -exptime min max
+  -airmass min max
+  -expgroup groupID
+
+  (-type and -camera are mandatory)
+  (-filter is mandatory for 'light' types)
+  (-exptime is mandatory for dark)
+
+dettools -pending raw [-state state] [-outmode mode]
+ * select the unprocessed input infiles
+ - output of this program is used by pantasks to schedule the ppImage
+ run on each image
+
+dettools -pending resid [-state state] [-outmode mode]
+ * select the residual images to be processed
+ - output of this program is used by pantasks to schedule the ppImage
+ run on each image
+
+dettools -pending stack [-state state] [-outmode mode]
+ * select the files to be stacked for a given detRun, if exposures are ready
+ - output of this program is used by ppMerge to build a master stack
+
+dettools -pending norm
+ * select the master detrend frames & imfiles to be normalized
+
+dettools -update raw
+ * select the raw input exposures, count processed imfiles, update if done
+
+dettools -update resid
+ * select the resid exposures, count processed resid imfiles, update if done
+ - for a given resid exposure, compile the results from the stacks for
+ each chip and renormalize the output files as needed.
+
+dettools -assess
+ * for a given master detrend frame, compile the results from the
+ residual exposures and assess the validity of the input exposures and of
+ the complete stack.  if too many input images are rejected, the
+ affect the master detrend frame state.  if input images are rejected
+ and a new set of master stacks should be made, create a new master
+ detrend image, incrementing the iteration by one.
+
+** NOTE the sequence is: 
+
+   process, stack, merge, residual, assess
+              ^--------------------------<
+
+   IF we have no relevant detrend image for comparison.  otherwise,
+   the sequence should skip from process to residual on the first
+   pass, with a lower rejection threshold for the first assess pass.
+   the tools above allow either option; it is the choice of state
+   after process that determines which happens next.
+
+
+
+States:
+ input detrend exposures:
+  RAW
+  PROCESSED
+
+ input detrend imfiles:
+  RAW
+  PROCESSED
+  
+ master detrend frames
+  RAW
+  NORMALIZED
+  MKRESID
+  SUCCESS
+  FAILURE
+  RETRY
+  (also has NEW/PRIOR flag)
+
+ master detrend imfiles
+  NEW (not yet created)
+  RAW (created, but not normalized)
+  NORMALIZED
+
+ resid exposure
+  RAW
+  PROCESSED
+  ASSESSED
+
+ resid imfiles:
+  RAW
+  PROCESSED
+
+ master detrend run:
+  NEW
+  DONE
+
+
+
+older descriptions:
+
+Phase.1
+  input: exp ID
+
+  This program determines the chips which correspond the exposure,
+  loads the guide stars (if they exist) or reads the pixels around
+  bright stars (if the guide stars do not exit.  It determines the
+  centroids for these stars, reads in the astrometric reference stars
+  in the field, matches observed stars to reference stars, determines
+  an astrometric solution, using the default telescope / camera model
+  as the starting point, and writes out the result to the P1 output
+  file, along with the FITS table of observed star measurements (X, Y,
+  inst mag).  It writes the summary statistics back to the P1 exposure
+  table, an new entry in the P2 exposure table, and entries for each
+  of the image files in the P2 images table.
+
+Submit.P2
+  input: exposure selection criteria
+
+  This program uses the provided selection criteria (eg, exp ID, time
+  range + filter, etc) and selects the corresponding exposures and
+  image files.  If the output is specified to the database, it creates
+  new entries in the P2 images and P2 exposure tables, incrementing
+  the version fields if these exposure already exist.  If the output
+  is specified to stdout / file, it writes the corresponding
+  information in a human (and/or machine) readable format.  Note: it
+  is not necessary that a P2 exposure defined by this tool include all
+  possible or available image files.  Note also: this program must
+  validate that the selected exposures have completed Phase 1, or are
+  not required to complete Phase 1.
+
+Phase.2
+  input: (url) or (exp ID + class ID)
+
+  This program reads in the selected image file, determines the
+  matching detrend images, detrends the image data, performs object
+  detection and analysis, loads the corresponding
+  astrometric/photometric reference data, determines the astrometric
+  and photometric calibrations.  It writes out the detrended image,
+  the corresponding mask, and a FITS table of the measured objects,
+  with the astrometric parameters in the FITS table header.  It
+  updates the P2 images table with the statistics of the analysis, and
+  updates the P2 exposure table status.  If this analysis is the last
+  of the Nclass P2 image analyses to be done on this P2 exp ID and
+  version, then the program makes a new entry in the P3 exposure
+  table.  
+
+Submit.P3
+  input: exposure selection criteria
+
+  This program uses the provided selection criteria (eg, exp ID, time
+  range + filter, etc) and selects the corresponding exposures which
+  have succeeded in Phase 2 (a specific P2 version may be specified).
+  If the output is specified to the database, it creates new entries
+  in the P3 exposure table, incrementing the version field if this
+  exposure already exists.  If the output is specified to stdout /
+  file, it writes the corresponding information in a human (and/or
+  machine) readable format.
+
+Phase.3
+  input: (exp ID) or (list of P2 object files)
+
+  This program finds the collection of P2 object files for the
+  specified exposure, reads in the object data and astrometric
+  calibration terms, loads the corresponding astrometric/photometric
+  reference data, determines the improved astrometric and photometric
+  calibrations.  It writes out a single FITS table of the measured
+  objects, with the astrometric parameters in the FITS table header,
+  and astrometric calibration data in a new astrometry file for this
+  exposure.  It updates the P3 exposure table with the statistics of
+  the analysis
+
+Submit.P4
+  input: selection criteria
+
+  This program uses the provided selection criteria (eg, exp ID list,
+  observation group ID, sky region + time range + filter, etc) and
+  selects the corresponding exposures which have succeeded in Phase 3
+  (or Phase 2 if Phase 3 is not needed for this camera / recipe).  It
+  determines the matching sky cells which are overlapped by the
+  selected exposures.  If the output is specified to the database, it
+  creates new entries in the P4 run table and the P4 run input table.
+  If the output is specified to stdout / file, it writes the
+  corresponding information in a human (and/or machine) readable
+  format.
+
+Phase.4
+  input: (run ID) or (sky cell + list of P2 / P3 image files)
+
+  This program uses the skycell and the list of input exposures to
+  find the collection of image files which overlap the given skycell.
+  It then reads the image pixels, warps them to match the skycell
+  geometry, and stacks the image pixels.  It reads the pixels from the
+  skycell and performs the image difference analysis, it photometers
+  the difference image, cleans the input summed image on the basis of
+  the detections, and photometers the input summed image.  It writes
+  out the statistics of the analysis to the P4 run table, the P4 sum
+  table, and the P4 delta table.  If requested, it improves the
+  signal-to-noise in the skycell image, and updates the skycell table.
+
+Detrend.init
+  input: (detrend creation criteria)
+
+  This program adds a new entry to the master detrend run table based
+  on the given criteria.  The criteria may define a time range for the
+  input images, a detrend type, flux ranges, a filter, an observation
+  group ID.  The new master detrend run is created with the iteration
+  set to 00 and the state set to `new'.  
+
+Detrend.get.input.list
+  input: det ID, version
+
+  This program uses the selection criteria defined by the master
+  detrend run (eg, exp ID list, observation group ID, time range, type
+  + filter, etc) to select the corresponding detrend exposures.  It
+  creates a new entry in the master detrend frames table, with state
+  `new' and iteration of 00.  It also writes the exposures to the
+  input detrend exposure table.
+
+Detrend.get.images
+  input: exp ID
+
+  This program identifies the detrend images which correspond to the
+  selected detrend exposure ID and adds an entries for the image files
+  in the input detrend images table, with state set to `raw'.
+
+Detrend.process
+  input: url / detrend type
+
+  This program performs the pre-processing needed by the selected
+  detrend image in order to prepare it for combination with other
+  detrend images.  When completed, the entry for this file in the
+  input detrend images table is update to `proc'.  The processing may
+  be as simple as a null operation (eg, for a bias) or as complex as
+  bias, dark, flat-field, renormalize (eg, for a fringe).
+
+Detrend.stack
+  input: det ID, version, iteration, class ID?
+
+  This program performs the image stacking for a single image class
+  ID.  The stacking process may depend on the detrend type (different
+  for bias from flat from fringe).  The resulting statitics are
+  written to the master detrend image table.  If this is the last of
+  Nclass entries for the given master detrend frame, then the master
+  detrend frames state is updated.
+
+Detrend.merge
+  input: det ID, version, iteration
+
+  This program examines the results of the Nclass master detrend
+  images and performs any necessary re-normalizations.  It also
+  examines the statistics of the individual stacks and summarizes them
+  in the master detrend frame table..
+
+Detrend.residuals
+  input:  det ID, version, iteration, class ID, url
+
+  This program performs the detrending on the given processed input
+  image using the corresponding detrend frame.  The residual image is
+  saved, and an entry is written to the residual images table giving
+  the image location and the residual statistics for this image.
+
+Detrend.assess
+  input: det ID, version, iteration
+
+  This program examines the collection of residual image statistics
+  and creates ensemble statistics for each residual exposure (NOTE: do
+  we need a residual exposure table??).  It examines the ensemble of
+  exposure statistics and determines if 1) the complete stack meets
+  the success criteria and 2) which residual exposure do / do not meet
+  the success criteria.  It updates the master detrend run table, the
+  master detrend frame table, and the residual image / exposure table
+  to note images which should be included / excluded in a future
+  iteration.  
+\end{verbatim}
+
+
+\section{Metadata Database Tables used for IPP Job Flow}
+\label{sec:MetadataTableContents}
+
+The tables presented below define in greater detail the contents of
+the Metadata tables shown in the figures above.  In some cases, the
+quantities (eg, the analysis result statistics) are illustrative, not
+definitive.  In certain tables, data is provided which is redundant
+(non-normal) for ease of use.  In some cases, we may decide not to
+keep this redundant information.  In cases where the choice to
+replicate the redundant information is uncertain, the field names are
+written in {\it italics}.
+
+\begin{table}[bh]
+\begin{center}
+\caption{Pending Image Files\label{tab:PendingImageFiles}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+URL               & string          & file location	       & http://data/file001.fits  \\
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+{\it camera}	  & string	    & camera name	       & MegaPrime / GPC           \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+class ID	  & string	    & identify for class       & chip00 / cell0102	   \\
+state		  & string	    & state of transfer?       & ready / copied		   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{New Image Files\label{tab:NewImageFiles}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+URL               & string          & file location	       & neb://file001.fits        \\
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+camera		  & string	    & camera name	       & MegaPrime / GPC           \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+class ID	  & string	    & identify for class       & chip00 / cell0102	   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Host for Class\label{tab:HostForClass}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+camera		  & string	    & camera name	       & MegaPrime / GPC           \\
+class ID	  & string	    & identify for class       & chip00 / cell0102	   \\
+hostname	  & string	    & preferred host computer  & po01 / alala.ifa.hawaii.edu \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Raw Image Files\label{tab:RawImageFiles}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+URL               & string          & file location	       & neb://file001.fits        \\
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+camera		  & string	    & camera name	       & MegaPrime / GPC           \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+class ID	  & string	    & identify for class       & chip00 / cell0102	   \\
+type		  & string	    & exposure type	       & bias / flat / science	   \\
+Nreadout	  & int		    & number of readouts       & 1 / 5 / 100		   \\
+Treadout	  & float	    & readout exposure time    & 1.0 (sec)		   \\
+ccdtemp		  & float	    & detector temperature     & 90 (K)			   \\
+\hline
+background	  & float	    & data area median	       & 5.0 (sec)		   \\
+FHWM		  & float	    & average image quality    & 2.5 (arcsec)		   \\
+\hline
+\multicolumn{4}{l}{note: stats below the line are measured, perhaps they go elsewhere?}	   \\
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Pending Metadata Tables\label{tab:PendingTables}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+URL               & string          & file location	       & neb://file001.fits        \\
+table ID	  & string	    & unique table identifier  & table-654321 		   \\
+table type	  & string	    & table content type       & temps / skyprobe data     \\
+state    	  & string	    & copied?                  & ready / copied		   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Copied Metadata Tables\label{tab:CopiedTables}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+URL               & string          & file location	       & neb://file001.fits        \\
+table ID	  & string	    & unique table identifier  & table-654321 		   \\
+table type	  & string	    & table content type       & temps / skyprobe data     \\
+state    	  & string	    & copied?                  & ready / copied		   \\
+time		  & int		    & table arrival time       & 13243413 (s)		   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{New Exposures\label{tab:NewExp}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+camera		  & string	    & camera name	       & MegaPrime / GPC           \\
+telescope	  & string	    & telescope name	       & CFHT / PS-1 / SP-1        \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+Nfiles		  & int		    & number image files       & 64			   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Raw SCIENCE Exposure\label{tab:RawScienceExp}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+camera		  & string	    & camera name	       & MegaPrime / GPC           \\
+telescope	  & string	    & telescope name	       & CFHT / PS-1 / SP-1        \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+Nfiles		  & int		    & number of image files    & 1 / 64			   \\
+\hline
+exptime		  & float	    & exposure time	       & 10.0 (seconds)		   \\
+filter.ID	  & string	    & filter ID (glass)	       & g.PS1.01		   \\
+filter.Band	  & string	    & filter bandpass name     & g			   \\
+airmass		  & float	    & sec(zenith angle)	       & 1.35			   \\
+RA		  & float	    & boresite RA	       & 125.01 (degrees)	   \\
+DEC		  & float	    & boresite DEC	       & 35.01 (degrees)	   \\
+PA		  & float	    & FPA position angle       & 10.1 (degrees)		   \\
+obstime.sec	  & float	    & observation start	       & 123423422 (TAI)	   \\
+obstime.nsec	  & float	    & observation start (nsec) & 1234			   \\
+telfocus	  & float	    & focus distance	       & 1.50 (mm)		   \\
+FPAtemp		  & float	    & FPA temperature	       & 90 (K)			   \\
+dometemp	  & float	    & dome air temperature     & 290 (K)		   \\
+airtemp		  & float	    & outside air temperature  & 285 (K)		   \\
+mirrortemp	  & float	    & primary mirror temp      & 286 (K)		   \\
+teltemp		  & float	    & telescope structure temp & 283 (K)		   \\
+group ID	  & string	    & observation group name   & target-seq-01		   \\
+group seq	  & int		    & sequence number in group & 3			   \\
+\hline
+background	  & float	    & average of file skies    & 5.0			   \\
+dbackground	  & float	    & stdev of file skies      & 1.0			   \\
+FHWM		  & float	    & \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Raw Detrend Exposures\label{tab:RawDetrendExp}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+camera		  & string	    & camera name	       & MegaPrime / GPC           \\
+telescope	  & string	    & telescope name	       & CFHT / PS-1 / SP-1        \\
+type		  & string	    & exposure type	       & bias / domeflat / dark    \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+Nfiles		  & int		    & number of image files    & 1 / 64			   \\
+\hline
+exptime		  & float	    & exposure time	       & 10.0 (seconds)		   \\
+filter.ID	  & string	    & filter ID (glass)	       & g.PS1.01		   \\
+filter.Band	  & string	    & filter bandpass name     & g			   \\
+Alt		  & float	    & boresite Alt	       & 125.01 (degrees)	   \\
+Az		  & float	    & boresite Az	       & 35.01 (degrees)	   \\
+PA		  & float	    & FPA position angle       & 10.1 (degrees)		   \\
+obstime.sec	  & float	    & observation start	       & 123423422 (TAI)	   \\
+obstime.nsec	  & float	    & observation start (nsec) & 1234			   \\
+telfocus	  & float	    & focus distance	       & 1.50 (mm)		   \\
+FPAtemp		  & float	    & FPA temperature	       & 90 (K)			   \\
+dometemp	  & float	    & dome air temperature     & 290 (K)		   \\
+airtemp		  & float	    & outside air temperature  & 285 (K)		   \\
+mirrortemp	  & float	    & primary mirror temp      & 286 (K)		   \\
+teltemp		  & float	    & telescope structure temp & 283 (K)		   \\
+group ID	  & string	    & observation group name   & target-seq-01		   \\
+group seq	  & int		    & sequence number in group & 3			   \\
+callamp.state     & string          & calibration lamp state   & on / off		   \\
+callamp.mode      & string          & calibration lamp mode    & continuum / line / ??	   \\
+\hline
+background	  & float	    & average of file back    & 5.0			   \\
+dbackground	  & float	    & stdev of file back      & 1.0			   \\
+FHWM		  & float	    & \\
+hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\clearpage
+
+\begin{table}[bh]
+\begin{center}
+\caption{P1 Exposures\label{tab:P1-Exp}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+camera		  & string	    & camera name	       & MegaPrime / GPC           \\
+version		  & int		    & P1 version number	       & 01			   \\
+recipe		  & string	    & analysis recipe name     & basic / flattest	   \\
+state		  & string	    & P1 analysis state	       & new / done		   \\
+\hline
+output		  & string	    & location of output file  & rootname.P1.01.smf        \\
+P1-log		  & string	    & location of P1 logfile   & rootname.P1.01.log        \\
+Nstars		  & int		    & number of astrom stars   & 50			   \\
+sigma.X		  & float	    & astrom scatter in X      & 1.0 (arcsec)		   \\
+sigma.Y		  & float	    & astrom scatter in Y      & 1.2 (arcsec)		   \\
+Mcal		  & float	    & nominal zeropoint        & 25.2 (mag)		   \\
+Moff		  & float	    & measure ZP offset        & 0.5 (mag)		   \\
+dMoff		  & float	    & scatter in Moff	       & 0.1 (mag)		   \\
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{P2 Exposures\label{tab:P2-Exp}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+Nfiles	          & int 	    & number of P2 image files & 3			   \\
+Ndone	          & int 	    & number completed	       & 2			   \\
+P1 version        & int		    & P1 version number	       & 01			   \\
+P2 version	  & int		    & P2 version number	       & 01			   \\
+state		  & string	    & P2 analysis state	       & new / done / fail?	   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{P2 Images\label{tab:P2-Images}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+class ID	  & string	    & identify for class       & chip00 / cell0102	   \\
+recipe		  & string	    & analysis recipe name     & basic / flattest	   \\
+state		  & string	    & P2 analysis state	       & new / done / fail?	   \\
+input.url         & string          & file location	       & rootname.fits		   \\
+output.image.url  & string          & file location	       & rootname.P2.01.fits       \\
+output.obj.url    & string          & file location	       & rootname.P2.01.smf        \\
+output.log.url    & string          & file location	       & rootname.P2.01.log	   \\
+\hline
+bias		  & float	    & measured bias value      & 5.0			   \\
+dbias		  & float	    & bias residual scatter    & 5.0			   \\
+fringe            & float           & fringe amplitude         & 10.0 			   \\
+dfringe           & float           & fringe scatter           & 10.0 			   \\
+sky		  & float	    & reduced background       & 5.0			   \\
+dsky		  & float	    & background scatter       & 5.0			   \\
+Nstars		  & int		    & number of astrom stars   & 50			   \\
+sigma.X		  & float	    & astrom scatter in X      & 1.0 (arcsec)		   \\
+sigma.Y		  & float	    & astrom scatter in Y      & 1.2 (arcsec)		   \\
+Mcal		  & float	    & nominal zeropoint        & 25.2 (mag)		   \\
+Moff		  & float	    & measure ZP offset        & 0.5 (mag)		   \\
+dMoff		  & float	    & scatter in Moff	       & 0.1 (mag)		   \\
+runtime           & float           & processing time          & 20.2 (sec)                \\
+\hline
+\multicolumn{4}{l}{note: the stats below the line are examples to be extended}	   \\
+\end{tabular}
+\end{center}
+\end{table}
+
+The P2 analysis produces a reduced (P2) output image, a table of (P2)
+measured objects, and astrometric calibration terms.  The output
+objects are stored as a FITS table.  The astrometric solution for the
+image is stored in the header of the output object file.  Interesting
+statistics from the analysis are written to the MDDB table.
+Additional information describing the analysis is written to the log
+file in a machine-readable format.  Additional entries can be added to
+the MDDB table if we find they are useful for understanding the
+processing results.
+
+\begin{table}[bh]
+\begin{center}
+\caption{P3 Exposures\label{tab:P3-Exp}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+Nfiles	          & int 	    & number of P2 image files & 3			   \\
+Ndone	          & int 	    & number completed	       & 2			   \\
+P1 version        & int		    & P1 version number	       & 01			   \\
+P2 version	  & int		    & P2 version number	       & 01			   \\
+P3 version	  & int		    & P3 version number	       & 01			   \\
+state		  & string	    & P3 analysis state	       & new / done / fail?	   \\
+recipe		  & string	    & analysis recipe name     & basic / flattest	   \\
+output		  & string	    & location of output file  & rootname.P3.01.smf        \\
+P3-log		  & string	    & location of P3 logfile   & rootname.P3.01.log        \\
+\hline
+Nstars		  & int		    & number of astrom stars   & 50			   \\
+sigma.X		  & float	    & astrom scatter in X      & 1.0 (arcsec)		   \\
+sigma.Y		  & float	    & astrom scatter in Y      & 1.2 (arcsec)		   \\
+Mcal		  & float	    & nominal zeropoint        & 25.2 (mag)		   \\
+Moff		  & float	    & measure ZP offset        & 0.5 (mag)		   \\
+dMoff		  & float	    & scatter in Moff	       & 0.1 (mag)		   \\
+runtime           & float           & processing time          & 20.2 (sec)                \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\clearpage
+
+\begin{table}[bh]
+\begin{center}
+\caption{Master Dark Frames\label{tab:DarkFrames}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+det ID		  & string	    & detrend frame ID	       & 654321o		   \\
+version		  & int		    & version of det frame     & 02			   \\
+label		  & string          & descriptive name         & basic			   \\
+recipe		  & string          & creation recipe          & basic			   \\
+type              & string	    & detrend type	       & bias / dark / flat	   \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+Nfiles		  & int	            & number of files          & 2			   \\
+\hline
+exptime		  & float	    & exposure time            & 5.0			   \\
+Ninput		  & int		    & number of input frames   & 5			   \\
+sigma		  & float	    & stack residual stats     & 2.0			   \\ 
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Master Flat Frames\label{tab:FlatFrames}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+det ID		  & string	    & detrend frame ID	       & 654321o		   \\
+version		  & int		    & version of det frame     & 02			   \\
+label		  & string          & descriptive name         & basic			   \\
+type              & string	    & detrend type	       & bias / dark / flat	   \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+Nfiles		  & int	            & number of files          & 2			   \\
+\hline
+filter.ID	  & string	    & filter ID (glass)	       & g.PS1.01 / none	   \\
+filter.Band	  & string	    & filter bandpass name     & g / none		   \\
+Ninput		  & int		    & number of input frames   & 5			   \\
+sigma		  & float	    & stack residual stats     & 2.0			   \\ 
+Ntotal		  & float	    & ave.total input counts   & 1e6			   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Master Fringe Frames\label{tab:FringeFrames}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+det ID		  & string	    & detrend frame ID	       & 654321o		   \\
+version		  & int		    & version of det frame     & 02			   \\
+label		  & string          & descriptive name         & basic			   \\
+type              & string	    & detrend type	       & bias / dark / flat	   \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+Nfiles		  & int	            & number of files          & 2			   \\
+\hline
+filter.ID	  & string	    & filter ID (glass)	       & g.PS1.01 / none	   \\
+filter.Band	  & string	    & filter bandpass name     & g / none		   \\
+Ninput		  & int		    & number of input frames   & 5			   \\
+sigma		  & float	    & stack residual stats     & 2.0			   \\ 
+Ntotal		  & float	    & ave.total input counts   & 1e6			   \\
+fringe		  & float	    & fringe amplitude	       & 52.0			   \\
+dfringe		  & float	    & fringe stdev	       & 5.0			   \\
+airmass.min	  & float	    & minimum airmass	       & 1.00			   \\
+airmass.max	  & float	    & maximum airmass	       & 1.20			   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Master Detrend Image Files\label{tab:DetImageFiles}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+URL               & string          & file location	       & neb://file001.fits        \\
+det ID		  & string	    & detrend frame ID	       & 654321o		   \\
+{\it camera}	  & string	    & camera name	       & MegaPrime / GPC           \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+class ID	  & string	    & identify for class       & chip00 / cell0102	   \\
+{\it type}	  & string	    & exposure type	       & bias / flat / science	   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\clearpage
+
+\begin{table}[bh]
+\begin{center}
+\caption{Master Detrend Run\label{tab:DetRun}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+det ID		  & string	    & detrend frame ID	       & 654321o		   \\
+camera		  & string	    & camera name	       & MegaPrime / GPC           \\
+version		  & int		    & version of det run       & 02			   \\
+type              & string	    & detrend type	       & bias / dark / flat	   \\
+criteria	  & string	    & image selection criteria & filter flags		   \\
+state		  & string	    & analysis state	       & new / done		   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Input Detrend Exp\label{tab:InputDetExp}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+det ID		  & string	    & detrend frame ID	       & 654321o		   \\
+{\it camera}	  & string	    & camera name	       & MegaPrime / GPC           \\
+{\it version}	  & int		    & version of det run       & 02			   \\
+exp ID		  & string	    & exposure ID	       & 654321o		   \\
+state		  & string	    & analysis state	       & new / raw / proc	   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Input Detrend Image Files\label{tab:InputDetImageFiles}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+raw.URL           & string          & location of raw file     & neb://file001.fits        \\
+proc.URL          & string          & location of proc file    & neb://file001.fits        \\
+det ID		  & string	    & detrend frame ID	       & 654321o		   \\
+{\it version}	  & int		    & version of det run       & 02			   \\
+{\it camera}	  & string	    & camera name	       & MegaPrime / GPC           \\
+{\it type}	  & string	    & exposure type	       & bias / flat / science	   \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+class ID	  & string	    & identify for class       & chip00 / cell0102	   \\
+state		  & string	    & analysis state	       & new / raw / proc	   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[bh]
+\begin{center}
+\caption{Input Detrend Resid Image Files\label{tab:InputDetResidImageFiles}}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Field Name} & {\bf Datatype }  & {\bf Description}        & {\bf Examples} \\
+\hline
+image.URL         & string          & location of resid image  & neb://file001.fits        \\
+thumbnail.URL     & string          & location of resid jpeg   & neb://file001.fits        \\
+det ID		  & string	    & detrend frame ID	       & 654321o		   \\
+version		  & int		    & version of det run       & 02			   \\
+iteration	  & int		    & det creation iteration   & 03			   \\
+{\it camera}	  & string	    & camera name	       & MegaPrime / GPC           \\
+{\it type}	  & string	    & exposure type	       & bias / flat / science	   \\
+class		  & string	    & file data class	       & Cell / Chip / FPA	   \\ 
+class ID	  & string	    & identify for class       & chip00 / cell0102	   \\
+state		  & string	    & analysis state	       & new / raw / proc	   \\
+scatter		  & float	    & residual scatter	       & 1.0			   \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\section{Summit Copy Tasks}
+
+\begin{verbatim}
+# identify the images ready for copy 
+# new entries are added to queue newImages
+# need to compare the new list with the ones already being processed
+task	       new.images
+  command      new.images
+  host         local
+
+  periods      -poll 1
+  periods      -exec 1
+  periods      -timeout 30
+
+  # success
+  task.exit    0
+    local i j Nstdout Nimages
+    # compare output with new.image queue
+    # keep only new entries
+    queuesize stdout -var Nstdout
+    for i 0 $Nstdout
+      queuepop stdout -var line
+      queuepush newImages -uniq -key 0 "$line"
+    end
+  end
+
+  # locked list
+  task.exit    1
+    echo       "new.images: exec failure"
+    $new.image.failure ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "new.images: timeout"
+    $new.image.failure ++
+  end
+end
+
+# copy new images, sending job to desired host
+task	       copy.images
+  periods      -poll 0.5
+  periods      -exec 0.1
+  periods      -timeout 5
+
+  task.exec
+    queuesize  newImages -var N
+    if ($N == 0) break
+    if ($network == 0) break
+    
+    queuepop newImages -var line
+    list tmp -split $line
+    $filename   = $tmp:0
+    $chip       = $tmp:1
+    $state      = $tmp:2
+    if ($state == new) 
+      # copy this image
+      queuepush newImages -replace -key 0 "$filename $chip run"
+    else
+      # ignore this image
+      queuepush newImages -replace -key 0 "$filename $chip $state"
+      break
+    end
+    $host = `chip.host $chip`
+    host $host
+    command copy.image $filename $chip
+  end
+
+  # success
+  task.exit    0
+    # echo "done copy..."
+    queuepop stdout -var line
+    list tmp -split $line
+    $filename   = $tmp:0
+    $chip       = $tmp:1
+    exec mark.image $filename
+    queuepush newImages -replace -key 0 "$filename $chip copy"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    echo       "new.images: timeout"
+    $new.image.failure ++
+  end
+end
+\end{verbatim}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/outline.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/outline.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/outline.tex	(revision 22322)
@@ -0,0 +1,365 @@
+\documentclass[panstarrs,spec]{panstarrs}
+
+\begin{document}
+
+\begin{verbatim}
+** data concepts:
+
+url: unique filename resource location
+    http://machine/file
+    neb:storageID
+    file:/path/to/filename
+
+exp.ID: a unique exposure identifier
+    765432o.fits (MegaPrime)
+    20050901.001.fits (vysos?)
+     
+camera: name of camera
+    GPC-1
+    TC-3
+    MegaPrime
+
+class: highest valid data hierarchy level for the files
+    FPA
+    Chip
+    Cell
+
+class.ID: name of specific class entry (need not be unique over all cameras)
+    ccd00 (CFH12K)
+    chip03 (GPC)
+    MegaPrime (MegaPrime - class is FPA!)
+
+table.ID: unique table identifier (unique for all metadata FITS tables)
+    otis.9876234t.fits (?)
+
+table.type: name of metadata table
+    NewImages
+    PendingImages
+\end{verbatim}
+
+\newpage
+
+\begin{verbatim}
+** psTool outlines 
+
+new_images
+
+  USAGE: new_images 
+
+    - perform query on the summit web server for new images
+    - parse the returned list
+    - write data to the PendingImageFiles table in the Metadata DB
+
+    * PendingImageFiles: url, exp.ID, camera, class, class.ID, state
+    * Perl or C?
+    o this tool may either receive all needed info from the query,
+      or it may need to supply information based on the camera
+
+copy_image
+
+  USAGE: copy_image (url) (exp.ID) (camera) (class) (class.ID)
+  
+    - read image from the given url
+    - read camera recipe file (specifies output)
+    - write image to nebulous or filesystem
+    - write the data to the NewImageFiles table
+      (write the IPP URL, not the SUMMIT URL!)
+    - upon success, notify the summit web server for this file
+    - upon success, update status in PendingImageFiles table
+
+    * NewImageFiles: url, exp.ID, camera, class, class.ID
+    * Perl or C?
+    o target URL may be in Nebulous or filesystem, depending on camera recipe
+
+new_tables
+
+  USAGE: new_tables
+
+    - perform query on the summit web server for new tables
+    - parse the returned list
+    - write data to the PendingTables table in the Metadata DB
+
+    * PendingTables: url
+    * Perl or C?
+
+copy_table
+
+  USAGE: copy_table (url) (table.ID) (table.type)
+  
+    - read table from the given url
+    - determine table type (from header?)
+    - parse table data
+    - write table data to corresponding Metadata table
+    - write summary info to the MetadataTables table
+    - update status in PendingTables table
+    - upon success, notify the summit web server for this file
+
+    * MetadataTables: url, table.ID, table.type, date.copied
+    * Perl or C?
+    o tables are not camera-specific!
+
+\end{verbatim}
+
+\newpage
+
+\begin{verbatim}
+classify_image
+
+  USAGE: classify_image (url) 
+	 classify_image (exp.ID) (class.ID)
+
+    - if given (exp.ID + class.ID) find url from NewImageFiles table
+    - read image header
+    - determine image type
+    - write data to the RawImageFiles table
+    - select from NewExposures table, match on exp.ID
+    - select from RawImageFiles table, match on exp.ID
+    - if numbers of entries match
+      - write RawScienceExposure entry
+      - if (mosaic && type == OBJECT)
+	- write P1.Exposure entry
+      - else
+	- write P2.Exposure entry
+
+    o this program should have a user-only mode which
+      only writes the raw image file info to stdout
+      and does not update the DB.
+
+Phase.1
+
+  USAGE: Phase.1 (exp.ID) (version) (recipe) (out.url)
+	 Phase.1 (file) (version) (recipe) (out.url)
+	 Phase.1 (class.list) (version) (recipe) (out.url)
+
+    - if input from MDDB:
+      - select from RawImageFiles, match on exp.ID
+    - if input from FPA:
+      - read input data from header
+    - if input from class.list
+      - load list of files
+      - read input data from first header
+    - find exposure RA,DEC,Position Angle
+    - load reference stars 
+    - load rough astrometry data
+    - if guide stars on
+      - load guide star table
+      - convert tables to (X,Y),(R,D)
+    - else 
+      - select guide-star pixels
+      - read raster on guide-stars
+      - centroid on raster
+      - result is (X,Y),(R,D)
+    - imastro (solve for X,Y -> R,D)
+    - write output file (filename.P1.ver.smf)
+      * output file is header with astrometry, astrometry table, star table
+    - update P1.Exposures table
+    - write to P2.ImageFiles table
+    - write to P2.Exposures table
+
+Submit.P2
+
+  USAGE: Submit.P2 [-ID exp.ID] [-time start stop] [-camera camera] [-filter filter]  [-P1 version]
+
+    - must have at least one of -time or -ID
+    - select from RawScienceExposures, match on all retrictions
+    - select from RawImageFiles, match on exp.IDs
+    - write new entries in P2.ImageFiles, P2.Exposures
+
+    * if version is not supplied, use the highest for each?
+    * match on recipe?
+
+\end{verbatim}
+
+\newpage
+
+\begin{verbatim}
+Phase.2
+
+  USAGE: Phase.2 (url)
+	 Phase.2 (exp.ID + class.ID)
+
+    - if (exp.ID + class.ID), get url from P2.ImageFiles
+    - read file
+    - select matching detrend images
+    - bias
+    - dark
+    - non-linear
+    - convolve flat
+    - flatten
+    - mask
+    - splice
+    - measure fringe amplitude
+    - subtract fringe frame
+    - subtract smooth background
+    * note: propagate noise map for steps above!
+    - psphot
+    - load reference stars
+    - load astrometry info
+    - imastro (X,Y -> R,D)
+    - imphoto (m -> M)
+    - write output images
+    - write output object files
+    - write summary stats to P2.ImageFiles
+    - check P2.ImageFiles & P2.Exposures for P2 completion
+    - write P2.Exposures if all ImageFiles are done
+
+    * are the last two steps a race condition?
+    * should a (single) separate task check P2.ImageFiles and P2.Exposures?
+
+Submit.P3
+
+  USAGE: Submit.P3 [-ID exp.ID] [-time start stop] [-camera camera] [-filter filter] [-P2 version]
+
+    - must have at least one of -time or -ID
+    - select from RawScienceExposures, match on all retrictions
+    - select from P2.Exposures, match on P2.version, (state == done)
+    - write new entries in P3.Exposures
+
+    * if version is not supplied, use the highest for each?
+    * match on recipe?
+
+Phase.3
+
+  USAGE: Phase.3 (exp.ID)
+	 Phase.3 (FPA file)
+	 Phase.3 (list of chips)
+
+    - find input files
+    - read objects
+    - read astrometry data
+    - load reference stars
+    - mosastro
+    - mosphoto
+    - mosfringe / mossky??
+    - write output images
+    - write output files
+
+    - update P3.Exposures
+    - addstar? (write to DVO)
+\end{verbatim}
+
+\newpage
+
+\begin{verbatim}
+Detrend.init
+
+  USAGE: Detrend.init type label [-time start stop] [-filter filter] [others] [-group group.ID]
+
+    - if group.IDs
+      - select from RawDetrendExposures match on exp.ID
+    - else
+      - select from RawDetrendExposures match on criteria
+    - set the det.ID based on type,label,criteria
+    - add an entry to the MasterDetrendRun table (iteration == 0, state == new)
+
+    * some criteria are required (eg. light type -> filter)
+    * allow multiple groupIDs, if not conflicting types / filters?
+
+Detrend.get.input.exposures
+
+  USAGE: Detrend.get.input.exposures 
+
+    - select from MasterDetrendRun where (state == new)
+    - loop over selected detrend runs
+      - select from RawDetrendExposures exposures matching criteria
+      - add an entry to the MasterDetrendFrame table
+      - add entries to the InputDetrendExposures table (state == new)
+    - update MasterDetrendRun (state == init)
+
+    * this program could be defined to work on the det.ID, in which
+      case a second process is needed to select the detrend runds from
+      the table.  (probably not the better choice).
+
+Detrend.get.image.files
+
+  USAGE: Detrend.get.image.files
+
+    - select from InputDetrendExposures table where (state == new)
+    - loop over selected detrend exposures
+      - select from RawImageFiles match by exp.ID
+      - add entries to the InputDetrendImageFiles table (state == new)
+    - update InputDetrendExposures (state == init)
+
+    * this program could be defined to work on the exp.ID, in which
+      case a second process is needed to select the exposures from the
+      table.  (probably not the better choice).
+
+Detrend.process
+
+  USAGE: Detrend.process (url) (recipe) (type?)
+
+    - load the image
+    - perform the preprocessing (depends on the detrend type)
+    - write output image
+    - update the InputDetrendImageFiles table
+    - check the InputDetrendExposures table
+    - if Nfiles == Ndone
+      - update the InputDetrendExposure 
+    - check the MasterDetrendFrames table
+    - if Nframes == Ndone
+      - update the MasterDetrendFrames table (state == proc)
+\end{verbatim}
+
+\newpage
+
+\begin{verbatim}
+Detrend.stack
+
+  USAGE: Detrend.stack (det.ID) (version) (iteration) (class.ID)
+	 Detrend.stack (list of files)
+
+    - if (det.ID, etc) given
+      - select from InputDetrendExposure match det.ID
+
+    - load & scale each input image	 
+    - construct stacking
+    - write entry in MasterDetrendImages
+    - check MasterDetrendFrames
+    - if (Nclass == Ndone)
+      - update MasterDetrendFrames (state == stack)
+
+Detrend.merge
+
+  USAGE: Detrend.merge (det.ID) (version) (iteration)
+
+    - load master detrend image stats
+    - calculate appropriate normalization
+    - update MasterDetrendFrames table
+
+Detrend.residuals
+
+  USAGE: Detrend.residuals (url) (det.ID) (version) (iteration) (recipe) (type)
+
+    - load image
+    - select appropriate master 
+    - apply detrend master to image
+    - measure statistics
+    - write image
+    - update ResidImageFiles table     
+
+Detrend.assess 
+
+  USAGE: Detrend.assess (det.ID) (version) (iteration)
+
+    - select from ResidImageTable match on det.ID,version,iteration
+    - calculate summary statistics for each frame
+    - calculate summary statistics for master frame
+    - do input images pass?
+    - does master frame pass?
+    - update ResidExposures (does not exit?)
+    - update MasterDetrendFrames 
+    - update MasterDetrendRun
+
+Missing Modules
+
+  load from DVO
+  load PS astrom table
+  load guide-star table
+  imastro
+  mosastro
+  read WCS / write WCS
+  detrend-image selection
+  measure fringe pattern
+
+\end{verbatim}
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/p4tool.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/p4tool.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/p4tool.txt	(revision 22322)
@@ -0,0 +1,231 @@
+Give me a list of all pending exposures which could have phase 4 run:
+	p4tool -query -survey_mode <str> -filter <str> -ra <f64> -decl <f64> (plus other search options)
+
+The "p4tool -definerun" command creates a new p4Run for an exposure. A
+unique identifier for the run, or a p4Run ID, is returned:
+
+	p4tool -definerun -exp_tag <str> -mode <str> -workdir <str>
+	p4tool -addinputscfile ...
+	p4tool -updaterun -p4_id <s32> -mode run
+
+Give me all p4runs to have overlaps calculated:
+        p4tool -tooverlap
+
+Calculate overlaps for each exposure:
+        phase4_overlap.pl --p4_id <s32>
+{
+        Get list of component imfiles for exposure:
+                p4tool -imfile -p4_id <s32>
+
+        (Calculates, in some as-yet unknown way the overlaps between imfiles
+and skycells)
+
+        Set the relationship between imfiles and skycells:
+                p4tool -addoverlap -p4_id <s32> -info FILE
+        where FILE contains:
+
+        p4SkyCellMap MUTLI
+
+        p4SkyCellMap METDATA
+            p4_id           S32     1
+            skycell_id      STR     foo
+            tess_id         STR     bar
+            exp_tag         STR     baz
+            p3_version      S32     0
+            class_id        STR     quix
+        END
+
+        ...
+}
+
+Give me all skycells that need to have data warped to them:
+        p4tool -towarped
+
+Do the warp for each skycell:
+        phase4_warp.pl --p4_id <s32> --skycell <str>
+{
+        Get list of imfiles for skycell:
+                p4tool -scmap -p4_id <s32> -skycell <str>
+
+        Do the warping:
+                pswarp <something>
+
+        Register the skycell as done warping:
+                p4tool -addwarped -p4_id <s32> -skycell <str> -uri <str>
+}
+
+p4tool -updaterun -p4_id <s32> -mode stop
+
+================================================================================
+
+Give me a list of all skycells which could have subtractions performed
+(more than two warps):
+#	p5tool -query (some search parameters, including RA, Dec, seeing)
+
+Register an exposure for subtraction processing.  We need to specify which warp
+is the input and which is the template.
+
+	p5tool -definerun -workdir <str>
+
+    p5tool -addinputscfile -p5_id <s32> -p4_id <s32> -skycell_id <str> -tess_id <str> -kinds <str> [-template]
+
+	p5tool -updaterun -p5_id <s32> -state run
+
+Give me all skycells that need to be subtracted:
+        p5tool -todiffscfile
+
+Do the subtraction for each skycell:
+        phase5_subtract.pl --p5_id <s32>
+{
+        p5tool -inputscfile -p5_id <s32>
+
+        Do the subtraction:
+                pois in_uri.fits template_uri sub.fits <other stuff>
+
+        register the resulting difference image:
+
+        p5tool -adddiffscfile -p5_id <s32> -skycell_id <str> -tess_id <str>
+-uri <str> ...
+
+        Register the subtraction as done:
+                p5tool -updaterun -p5_id <s32> -state stop
+}
+
+================================================================================
+
+[Here I make some assumptions about how magic works]
+
+Get a list of exposures for which magic processing is possible
+	magictool -query (some search options)
+
+There may be multiple subtractions generated out of each exposure.
+Given an exposure, what subtractions do I have available?
+
+	magictool -options -exp_tag <str>
+
+Register an exposure for magic processing, specifying a subtraction set:
+	magictool -definerun -exp_tag <str> -subs FILE
+
+	where FILE contains something like:
+		skycell.123	  STR	    p5id.1
+		skycell.456	  STR	    p5id.2
+		skycell.789	  STR	    p5id.3
+
+Give me all skycells that need to have magic performed:
+        magictool -pendingskycell
+
+Do magic for each skycell:
+        magic_skycell.pl --magic_id <s32> --skycell <str> --uri <str>
+{
+        Do streak finding on skycell
+
+        Register sky cell as done:
+                magictool -doneskycell -magic_id <s32> -skycell <str> -uri <str>
+}
+
+Give me all imfiles that need to have magic performed:
+        magictool -pendingimfile
+
+Do magic for each imfile:
+        magic_imfile.pl --magic_id <s32> --class_id <str>
+{
+        Get list of all magic1 output relevant to this imfile
+                magictool -skycell -magic_id <s32> -class_id <str>
+
+        Do streak finding for imfile
+
+        Register imfile as done:
+                magictool -doneimfile -magic_id <s32> -class_id <str> -uri <str>
+}
+
+[Might be a mid-level process or processes, halfway between imfiles and
+full exposures, but for now, assume there's not, or that it can all be
+done quickly at the exposure level.]
+
+Give me all exposures that need to have magic performed:
+        magictool -pendingexp
+
+Do magic for each exposure:
+        magic_exp.pl --magic_id <s32>
+{
+        Get list of all magic2 output relevant to this exposure
+                magictool -imfile -magic_id <s32>
+
+        Do streak finding on exposure
+
+        Register exposure as done:
+                magictool -doneexp -magic_id <s32> -uri <str>
+}
+
+
+[Need to generate masks, but we need to decide at what level: imfile,
+or skycell?  If imfile, we need to do the warp over again.  If
+skycell, we can never give out the flattened CCD images unless they've
+been warped.  Let's do both --- it's not too expensive, and means we
+can release both without further work.]
+
+
+Give me all imfiles that need a magic mask:
+	magictool -pendingmaskimfile
+
+Generate mask for each imfile:
+	magic_mask_imfile.pl --magic_id <s32> --class_id <str> --uri <str>
+{
+	Make mask
+
+	Register mask:
+		magictool -donemaskimfile -magic_id <s32> -class_id <str> -uri <str>
+}
+
+Give me all skycells that need a magic mask:
+	magictool -pendingmaskskycell
+
+Generate mask for each skycell:
+	magic_mask_skycell.pl --magic_id <s32> --skycell <str> --uri <str>
+{
+	Make mask
+
+	Register mask:
+		magictool -donemaskskycell -magic_id <s32> -skycell <str> -uri <str>
+}
+
+Give me a list of all exposures which are clean (so we can release):
+	magictool -cleanexp (some search criteria)
+
+Give me a list of all imfiles which are clean (so we can release):
+	magictool -cleanimfile (some search criteria)
+
+Give me a list of all skycells which are clean (so we can release):
+	magictool -cleanskycell (some search criteria)
+
+================================================================================
+
+Give me a list of all skycells for which combination processing is possible:
+#	p6tool -query -min <s32> (some other search parameters, including ra, dec, time range)
+
+Register skycells for combination processing:
+	p6tool -definerun -skycell <stR>
+
+	p6tool -addinputscfile -p4_id <s32> -skycell_id <str> -tess_id <str> 
+
+Give me all pending operations/runs
+	p6tool -tosum
+
+Run phase 6:
+	phase6.pl -p6_id <s32>
+{
+	Give me the list of all inputs for this run:
+		p6tool -inputscfile -p6_id <s32>
+
+	Combine:
+		ppStac output.fits input1.fits input2.fits ...
+
+	Register the completion:
+		p6tool -addsumscfile -p6_id <s32> -uri <str>
+
+		p6tool -updaterun -p6_id <s32> -state stop
+}
+
+Give me a list of all stacks (for release):
+	p6tool -sumscfile <some search criteria>
+
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/phase0.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/phase0.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/phase0.txt	(revision 22322)
@@ -0,0 +1,52 @@
+Pan-Tasks regularly runs:
+
+	p0tool -pendingimfile
+
+which returns a list of imfiles which are pending for phase 0 processing.
+Imfiles are characterised by: exp_id, class_id, uri
+
+For each imfile, we run:
+
+	phase0imfile.pl <expId> <classId> <uri>
+
+"phase0imfile.pl" is a helper script that runs ppStats on the imfile,
+pulling out interesting qualities:
+
+	ra, dec, exptime, background, stdev, image type, ...
+
+It then pushes these qualities into the database using:
+
+	p0tool -updateimfile -ra $ra -dec $dec -exptime $expTime ... [etc]
+
+"p0tool -updateimfile" ticks off the imfile as having been processed
+through phase 0.
+
+
+
+
+Pan-Tasks regularly runs:
+
+	p0tool -pendingexp
+
+which returns a list of exposures which are pending for phase 0 processing,
+i.e., all imfiles within have been processed.  The exposures are characterised
+by an expid.
+
+For each exposure, we run:
+
+	phase0exp.pl <expId>
+
+"phase0exp.pl" is a helper script that pulls out the qualities for each component
+imfile of that exposure (which are stored in the database) using: 
+
+	p0tool -rawimfiles -expid $expId
+
+For those, it checks that the values are consistent across the
+components, and also decides if the exposure is a detrend or science
+exposure.  The results are uploaded to the database using:
+
+	p0tool -updateexp -expid -ra $ra -dec $dec -exptime $expTime ... [-detrend]
+
+"p0tool -updateexp" ticks off the exposure as having been processed
+through phase 0, and moves it over for phase 1 (or phase 2)
+processing.
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/phase2.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/phase2.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/phase2.txt	(revision 22322)
@@ -0,0 +1,44 @@
+Pan-Tasks regularly runs:
+
+	p2tool -pendingimfile
+
+This returns a list of pending imfiles that are ready to be processed
+at the phase 2 level, i.e., they are science exposures that have
+passed through phase 1 (or phase 0 if phase 1 doesn't exist).  Each
+imfile is characterised by: exp_id, class_id, version, uri.
+
+*** To do: function to get output file name(s) from input parameters.
+
+For each imfile, we run:
+
+	phase2run.pl -exp_id $expId -class_id $classId -version $version -uri $uri -out $out
+
+phase2run.pl is a helper script that runs ppImage:
+
+	ppImage -file $uri $out -recipe PPIMAGE PPIMAGE_PHASE2
+
+and then updates the database with the processed imfile:
+
+    XXX is -version nessicary or can it be automated?
+	p2tool -addprocessedimfile -exp_id $expId -class_id $classId -version
+$version -uri $out -recip $recipe -bg $background -bg_stdev $background_stdev
+-bg_mean_stdev $mean_background -b1_uri $bin1_uri -b2_uri $bin2_uri
+
+*** To do: do we need "-b1" and "-b2" flags to add the binned images?
+
+"p2search -update" ticks off the appropriate imfile in phase 2.
+
+
+We also desire to (manually) re-run a phase2 for a particular
+exposure, excluding certain chips (which may be bad, etc):
+
+	p2search -rerunexp -exp_id $expId -exclude ccd01 -exclude ccd13
+
+This option will (1) put new copy of that exposure into p2pending
+table (w/o the specified "-exclude" components), (2) increment the
+version number for each, (3) decrement the number of components by the
+number of "-exclude" components.
+
+Downstream, there will be no processing using mixed versions within an
+exposure; e.g., we don't allow building a phase 4 using version 1 of
+ccd01 and version 2 of ccd13.
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/pipeline.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/pipeline.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/pipeline.txt	(revision 22322)
@@ -0,0 +1,370 @@
+### Put Perl modules in ~/perl/
+### Statistics::Descriptive (CPAN)
+### PS-IPP-Metadata (CVS)
+### PS-IPP-Metadata-Config (CVS)
+
+setenv PERL5LIB $HOME/perl/
+
+
+### Set up the tables
+
+price@alala:/home/panstarrs/price/ipp/ippTools/pXtools>pxadmin -create
+price@alala:/home/panstarrs/price/ipp/ippTools/pXtools>pxadmin -recreate
+*** delete the P2 tables? ***
+*** to delete the tables, answer YES, and give password ***
+*** WARNING: this action is permanent ***
+ 
+delete the P2 tables (YES/[n]): YES
+enter dbh connection password: ipp
+
+### Inject everything into the DB
+
+pxinject -newExp -exp_id 850131b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850132b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850133b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850403b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850539b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850540b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850541b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850647b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850648b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850649b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850650b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850651b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850652b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850653b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850654b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850655b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850723b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850822b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850935b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 850936b -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+
+pxinject -newImfile -exp_id 850131b -class fpa -class_id mc -uri 850131b.fits
+pxinject -newImfile -exp_id 850132b -class fpa -class_id mc -uri 850132b.fits
+pxinject -newImfile -exp_id 850133b -class fpa -class_id mc -uri 850133b.fits
+pxinject -newImfile -exp_id 850403b -class fpa -class_id mc -uri 850403b.fits
+pxinject -newImfile -exp_id 850539b -class fpa -class_id mc -uri 850539b.fits
+pxinject -newImfile -exp_id 850540b -class fpa -class_id mc -uri 850540b.fits
+pxinject -newImfile -exp_id 850541b -class fpa -class_id mc -uri 850541b.fits
+pxinject -newImfile -exp_id 850647b -class fpa -class_id mc -uri 850647b.fits
+pxinject -newImfile -exp_id 850648b -class fpa -class_id mc -uri 850648b.fits
+pxinject -newImfile -exp_id 850649b -class fpa -class_id mc -uri 850649b.fits
+pxinject -newImfile -exp_id 850650b -class fpa -class_id mc -uri 850650b.fits
+pxinject -newImfile -exp_id 850651b -class fpa -class_id mc -uri 850651b.fits
+pxinject -newImfile -exp_id 850652b -class fpa -class_id mc -uri 850652b.fits
+pxinject -newImfile -exp_id 850653b -class fpa -class_id mc -uri 850653b.fits
+pxinject -newImfile -exp_id 850654b -class fpa -class_id mc -uri 850654b.fits
+pxinject -newImfile -exp_id 850655b -class fpa -class_id mc -uri 850655b.fits
+pxinject -newImfile -exp_id 850723b -class fpa -class_id mc -uri 850723b.fits
+pxinject -newImfile -exp_id 850822b -class fpa -class_id mc -uri 850822b.fits
+pxinject -newImfile -exp_id 850935b -class fpa -class_id mc -uri 850935b.fits
+pxinject -newImfile -exp_id 850936b -class fpa -class_id mc -uri 850936b.fits
+
+### Look for phase 0 imfiles:
+
+price@alala:/data/alala/price/megacam>p0tool -pendingimfile -simple
+850131b fpa mc 850131b.fits
+850132b fpa mc 850132b.fits
+850133b fpa mc 850133b.fits
+850403b fpa mc 850403b.fits
+850539b fpa mc 850539b.fits
+850540b fpa mc 850540b.fits
+850541b fpa mc 850541b.fits
+850647b fpa mc 850647b.fits
+850648b fpa mc 850648b.fits
+850649b fpa mc 850649b.fits
+850650b fpa mc 850650b.fits
+850651b fpa mc 850651b.fits
+850652b fpa mc 850652b.fits
+850653b fpa mc 850653b.fits
+850654b fpa mc 850654b.fits
+850655b fpa mc 850655b.fits
+850723b fpa mc 850723b.fits
+850822b fpa mc 850822b.fits
+850935b fpa mc 850935b.fits
+850936b fpa mc 850936b.fits
+
+### Run the phase 0 imfile script:
+
+~/ipp/helpers/phase0imfile.pl 850131b mc 850131b.fits
+~/ipp/helpers/phase0imfile.pl 850132b mc 850132b.fits
+~/ipp/helpers/phase0imfile.pl 850133b mc 850133b.fits
+~/ipp/helpers/phase0imfile.pl 850403b mc 850403b.fits
+~/ipp/helpers/phase0imfile.pl 850539b mc 850539b.fits
+~/ipp/helpers/phase0imfile.pl 850540b mc 850540b.fits
+~/ipp/helpers/phase0imfile.pl 850541b mc 850541b.fits
+~/ipp/helpers/phase0imfile.pl 850647b mc 850647b.fits
+~/ipp/helpers/phase0imfile.pl 850648b mc 850648b.fits
+~/ipp/helpers/phase0imfile.pl 850649b mc 850649b.fits
+~/ipp/helpers/phase0imfile.pl 850650b mc 850650b.fits
+~/ipp/helpers/phase0imfile.pl 850651b mc 850651b.fits
+~/ipp/helpers/phase0imfile.pl 850652b mc 850652b.fits
+~/ipp/helpers/phase0imfile.pl 850653b mc 850653b.fits
+~/ipp/helpers/phase0imfile.pl 850654b mc 850654b.fits
+~/ipp/helpers/phase0imfile.pl 850655b mc 850655b.fits
+~/ipp/helpers/phase0imfile.pl 850723b mc 850723b.fits
+~/ipp/helpers/phase0imfile.pl 850822b mc 850822b.fits
+~/ipp/helpers/phase0imfile.pl 850935b mc 850935b.fits
+~/ipp/helpers/phase0imfile.pl 850936b mc 850936b.fits
+
+### Look for phase 0 exposures:
+
+price@alala:/data/alala/price/megacam>p0tool -pendingexp -simple
+850131b MegaCam CFHT bias 1
+850132b MegaCam CFHT bias 1
+850133b MegaCam CFHT bias 1
+850403b MegaCam CFHT bias 1
+850539b MegaCam CFHT bias 1
+850540b MegaCam CFHT bias 1
+850541b MegaCam CFHT bias 1
+850647b MegaCam CFHT bias 1
+850648b MegaCam CFHT bias 1
+850649b MegaCam CFHT bias 1
+850650b MegaCam CFHT bias 1
+850651b MegaCam CFHT bias 1
+850652b MegaCam CFHT bias 1
+850653b MegaCam CFHT bias 1
+850654b MegaCam CFHT bias 1
+850655b MegaCam CFHT bias 1
+850723b MegaCam CFHT bias 1
+850822b MegaCam CFHT bias 1
+850935b MegaCam CFHT bias 1
+850936b MegaCam CFHT bias 1
+
+
+### Run the phase 0 exposure script:
+
+~/ipp/helpers/phase0exp.pl 850131b
+~/ipp/helpers/phase0exp.pl 850132b
+~/ipp/helpers/phase0exp.pl 850133b
+~/ipp/helpers/phase0exp.pl 850403b
+~/ipp/helpers/phase0exp.pl 850539b
+~/ipp/helpers/phase0exp.pl 850540b
+~/ipp/helpers/phase0exp.pl 850541b
+~/ipp/helpers/phase0exp.pl 850647b
+~/ipp/helpers/phase0exp.pl 850648b
+~/ipp/helpers/phase0exp.pl 850649b
+~/ipp/helpers/phase0exp.pl 850650b
+~/ipp/helpers/phase0exp.pl 850651b
+~/ipp/helpers/phase0exp.pl 850652b
+~/ipp/helpers/phase0exp.pl 850653b
+~/ipp/helpers/phase0exp.pl 850654b
+~/ipp/helpers/phase0exp.pl 850655b
+~/ipp/helpers/phase0exp.pl 850723b
+~/ipp/helpers/phase0exp.pl 850822b
+~/ipp/helpers/phase0exp.pl 850935b
+~/ipp/helpers/phase0exp.pl 850936b
+
+### Start a bias detrend run
+
+price@alala:/data/alala/price/megacam>dettool -definebyquery -det_type bias -camera mc
+
+detRun MULTI #
+ 
+detRun  METADATA
+   det_id           S32       1
+   iteration        S32       0
+   det_type         STR       bias
+END
+ 
+
+
+### Get list of pending raw images:
+
+
+price@alala:/data/alala/price/megacam>dettool -raw -simple
+1 bias 850131b fpa mc 850131b.fits BIAS r.MP9601 1.656000 5.496118 0.349071 0.000000 1155.934692 107.037283 46.214947 37.160000 105.480000 -133.067001 0.000000
+1 bias 850132b fpa mc 850132b.fits BIAS r.MP9601 1.656000 5.499327 0.349071 0.000000 1155.431957 107.464479 46.207451 37.160000 105.480000 -133.067001 0.000000
+1 bias 850133b fpa mc 850133b.fits BIAS r.MP9601 1.656000 5.502535 0.349071 0.000000 1154.871040 107.485792 46.227965 37.160000 105.480000 -133.067001 0.000000
+1 bias 850403b fpa mc 850403b.fits BIAS r.MP9601 1.000000 2.184885 0.349065 0.000000 1156.454996 105.813700 46.699662 89.590000 272.850000 -133.100006 0.000000
+1 bias 850539b fpa mc 850539b.fits BIAS HaOFF.MP7604 1.000000 2.928339 0.349071 0.000000 1155.991272 105.710902 46.830444 -9999.900000 -9999.900000 -133.100006 0.000000
+1 bias 850540b fpa mc 850540b.fits BIAS HaOFF.MP7604 1.000000 2.934318 0.349071 0.000000 1156.668537 106.228856 46.638641 -9999.900000 -9999.900000 -133.132996 0.000000
+1 bias 850541b fpa mc 850541b.fits BIAS NONE 1.035000 1.539753 0.349058 0.000000 1157.512387 105.972075 46.692062 75.000000 272.590000 -133.233002 0.000000
+1 bias 850647b fpa mc 850647b.fits BIAS i.MP9701 1.000000 5.661125 0.349065 0.000000 1159.236140 105.088919 46.581451 89.640000 273.780000 -133.132996 0.000000
+1 bias 850648b fpa mc 850648b.fits BIAS i.MP9701 1.000000 5.664260 0.349065 0.000000 1159.642160 105.172955 46.462718 89.640000 273.780000 -133.132996 0.000000
+1 bias 850649b fpa mc 850649b.fits BIAS i.MP9701 1.000000 5.667395 0.349065 0.000000 1159.877803 105.578676 46.323573 89.640000 273.780000 -133.132996 0.000000
+1 bias 850650b fpa mc 850650b.fits BIAS i.MP9701 1.000000 5.670604 0.349065 0.000000 1159.296260 105.885271 46.306679 89.640000 273.780000 -133.132996 0.000000
+1 bias 850651b fpa mc 850651b.fits BIAS i.MP9701 1.000000 5.673812 0.349065 0.000000 1159.130751 106.263144 46.198493 89.640000 273.780000 -133.132996 0.000000
+1 bias 850652b fpa mc 850652b.fits BIAS i.MP9701 1.000000 5.677020 0.349065 0.000000 1158.711761 106.354676 46.160953 89.640000 273.780000 -133.100006 0.000000
+1 bias 850653b fpa mc 850653b.fits BIAS i.MP9701 1.000000 5.680301 0.349065 0.000000 1157.834599 106.646493 46.197773 89.640000 273.780000 -133.100006 0.000000
+1 bias 850654b fpa mc 850654b.fits BIAS i.MP9701 1.000000 5.683510 0.349065 0.000000 1157.518896 106.736919 46.124295 89.640000 273.780000 -133.067001 0.000000
+1 bias 850655b fpa mc 850655b.fits BIAS i.MP9701 1.000000 5.686791 0.349065 0.000000 1156.908897 107.101147 46.126100 89.640000 273.780000 -133.167007 0.000000
+1 bias 850723b fpa mc 850723b.fits BIAS i.MP9701 1.000000 1.681629 0.349065 0.000000 1156.671385 105.943337 46.696208 89.640000 273.780000 -133.167007 0.000000
+1 bias 850822b fpa mc 850822b.fits BIAS r.MP9601 1.318000 1.317600 0.349039 0.000000 1156.904900 105.906221 46.700064 49.330000 156.180000 -133.167007 0.000000
+1 bias 850935b fpa mc 850935b.fits BIAS i.MP9701 1.222000 0.670460 0.349065 0.000000 1156.704276 105.995095 46.740883 54.930000 102.740000 -133.167007 0.000000
+1 bias 850936b fpa mc 850936b.fits BIAS r.MP9601 1.222000 2.741378 0.349065 0.000000 1156.577299 105.965926 46.631061 54.930000 102.740000 -133.132996 0.000000
+
+
+~/ipp/helpers/detrend_process.pl 1 850131b mc bias 850131b.fits 850131b
+~/ipp/helpers/detrend_process.pl 1 850132b mc bias 850132b.fits 850132b
+~/ipp/helpers/detrend_process.pl 1 850133b mc bias 850133b.fits 850133b
+~/ipp/helpers/detrend_process.pl 1 850403b mc bias 850403b.fits 850403b
+~/ipp/helpers/detrend_process.pl 1 850539b mc bias 850539b.fits 850539b
+~/ipp/helpers/detrend_process.pl 1 850540b mc bias 850540b.fits 850540b
+~/ipp/helpers/detrend_process.pl 1 850541b mc bias 850541b.fits 850541b
+
+### Get list of imfiles to stack:
+
+price@alala:/data/alala/price/megacam>dettool -tostack -simple
+1 0 bias mc
+
+### Stack the imfiles:
+
+price@alala:/data/alala/price/megacam>~/ipp/helpers/detrend_stack.pl 1 0 mc bias
+
+#### NB: dettool -addstacked wants statistics: need to add these into
+####     ppMerge, and parse with detrend_stack.pl; DONE
+
+### Create residuals from the stack
+
+price@alala:/data/alala/price/megacam>dettool -toresid -simple
+0 1 850131b mc o_850131b.mc.fit PPIMAGE_O 1.492870 2.848350 292.565582
+0 1 850132b mc o_850132b.mc.fit PPIMAGE_O 1.748621 1.891902 292.655562
+0 1 850133b mc o_850133b.mc.fit PPIMAGE_O 1.578467 2.000582 292.371550
+0 1 850403b mc o_850403b.mc.fit PPIMAGE_O 1.297346 3.104167 293.029486
+0 1 850539b mc o_850539b.mc.fit PPIMAGE_O 1.468040 2.212155 292.834501
+0 1 850540b mc o_850540b.mc.fit PPIMAGE_O 1.403641 2.998480 292.843640
+0 1 850541b mc o_850541b.mc.fit PPIMAGE_O 1.780084 1.854754 292.975948
+
+### Need det_type, stacked (actually normalised) URI  ---> JH to provide; DONE
+
+~/ipp/helpers/detrend_create_resid.pl 1 0 850131b mc bias bias_mc_1_0.fit o_850131b.mc.fit 850131b
+~/ipp/helpers/detrend_create_resid.pl 1 0 850132b mc bias bias_mc_1_0.fit o_850132b.mc.fit 850132b
+~/ipp/helpers/detrend_create_resid.pl 1 0 850133b mc bias bias_mc_1_0.fit o_850133b.mc.fit 850133b
+~/ipp/helpers/detrend_create_resid.pl 1 0 850403b mc bias bias_mc_1_0.fit o_850403b.mc.fit 850403b
+~/ipp/helpers/detrend_create_resid.pl 1 0 850539b mc bias bias_mc_1_0.fit o_850539b.mc.fit 850539b
+~/ipp/helpers/detrend_create_resid.pl 1 0 850540b mc bias bias_mc_1_0.fit o_850540b.mc.fit 850540b
+~/ipp/helpers/detrend_create_resid.pl 1 0 850541b mc bias bias_mc_1_0.fit o_850541b.mc.fit 850541b
+
+
+### List of exposures that have been residualized:
+
+price@alala:/data/alala/price/megacam>dettool -toresidexp -simple
+1 0 bias 850131b T
+1 0 bias 850132b T
+1 0 bias 850133b T
+1 0 bias 850403b T
+1 0 bias 850539b T
+1 0 bias 850540b T
+1 0 bias 850541b T
+
+### Check exposures:
+
+~/ipp/helpers/detrend_reject_imfile.pl 1 0 850131b bias
+~/ipp/helpers/detrend_reject_imfile.pl 1 0 850132b bias
+~/ipp/helpers/detrend_reject_imfile.pl 1 0 850133b bias
+~/ipp/helpers/detrend_reject_imfile.pl 1 0 850403b bias
+~/ipp/helpers/detrend_reject_imfile.pl 1 0 850539b bias
+~/ipp/helpers/detrend_reject_imfile.pl 1 0 850540b bias
+~/ipp/helpers/detrend_reject_imfile.pl 1 0 850541b bias
+
+### Need to allow ppImage to write a JPEG file from a pre-binned input.
+### Tried setting BIN1.XBIN=1,BIN2.YBIN=1 and BIN1.JPEG=TRUE, but it doesn't seem to work.
+
+
+### Get list of detrend runs to check for bad exposures:
+
+price@alala:/data/alala/price/megacam>dettool -residdetrun -simple
+1 0 bias
+
+### Do the check:
+
+~/ipp/helpers/detrend_reject_exp.pl 1 0 bias
+
+
+### And that's it.
+
+### Didn't try:
+### (1) Iterating
+### (2) Normalisation
+### (3) JPEG creation --- need to use chip-mosaicked camera
+
+
+
+###########################################################################################################
+
+### Trying some flats
+
+
+pxinject -newExp -exp_id 806237f -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 806238f -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 806239f -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+pxinject -newExp -exp_id 806240f -inst MegaCam -telescope CFHT -exp_type bias -imfiles 1
+
+pxinject -newImfile -exp_id 806237f -class fpa -class_id mc -uri 806237f.fits
+pxinject -newImfile -exp_id 806238f -class fpa -class_id mc -uri 806238f.fits
+pxinject -newImfile -exp_id 806239f -class fpa -class_id mc -uri 806239f.fits
+pxinject -newImfile -exp_id 806240f -class fpa -class_id mc -uri 806240f.fits
+
+
+price@alala:/data/alala/price/megacam/flats>p0tool -pendingimfile -simple
+806237f fpa mc 806237f.fits
+806238f fpa mc 806238f.fits
+806239f fpa mc 806239f.fits
+806240f fpa mc 806240f.fits
+
+### ===>
+
+~/ipp/helpers/phase0imfile.pl 806237f mc 806237f.fits
+~/ipp/helpers/phase0imfile.pl 806238f mc 806238f.fits
+~/ipp/helpers/phase0imfile.pl 806239f mc 806239f.fits
+~/ipp/helpers/phase0imfile.pl 806240f mc 806240f.fits
+
+
+price@alala:/data/alala/price/megacam/flats>p0tool -pendingexp -simple
+806237f MegaCam CFHT bias 1
+806238f MegaCam CFHT bias 1
+806239f MegaCam CFHT bias 1
+806240f MegaCam CFHT bias 1
+
+### ===>
+
+~/ipp/helpers/phase0exp.pl 806237f
+~/ipp/helpers/phase0exp.pl 806238f
+~/ipp/helpers/phase0exp.pl 806239f
+~/ipp/helpers/phase0exp.pl 806240f
+
+
+### Changing camera configuration file to use the bias we got earlier:
+   PPIMAGE.BIAS       INPUT bias_mc_1_0.fit               {CHIP.NAME}   {CHIP.NAME}  NONE         CHIP       IMAGE   
+
+### Changing ppImage_obd recipe to turn off dark:
+DARK		BOOL	FALSE		# Dark subtraction
+
+
+### Start the detrend run:
+
+price@alala:/data/alala/price/megacam/flats>dettool -definebyquery -det_type flat -camera mc
+detRun MULTI #
+ 
+detRun  METADATA
+   det_id           S32       2
+   iteration        S32       0
+   det_type         STR       flat
+END
+ 
+
+price@alala:/data/alala/price/megacam/flats>dettool -raw -simple
+2 flat 806237f fpa mc 806237f.fits FLAT g.MP9401 1.001000 0.341503 0.348775 99.112000 3687.433111 146.522771 74.441912 88.160000 275.250000 -133.033005 0.000000
+2 flat 806238f fpa mc 806238f.fits FLAT g.MP9401 1.000000 0.383900 0.348775 99.121002 5208.304792 212.152572 89.617438 89.760000 310.990000 -133.033005 0.000000
+2 flat 806239f fpa mc 806239f.fits FLAT g.MP9401 1.000000 0.395099 0.348775 80.097000 6311.806444 265.238880 100.970683 89.750000 310.150000 -133.033005 0.000000
+2 flat 806240f fpa mc 806240f.fits FLAT g.MP9401 1.000000 0.405135 0.348775 60.098999 6957.866556 293.657405 108.000238 89.750000 309.930000 -133.033005 0.000000
+
+### ===>
+
+
+~/ipp/helpers/detrend_process.pl 2 806237f mc flat 806237f.fits 806237f
+~/ipp/helpers/detrend_process.pl 2 806238f mc flat 806238f.fits 806238f
+~/ipp/helpers/detrend_process.pl 2 806239f mc flat 806239f.fits 806239f
+~/ipp/helpers/detrend_process.pl 2 806240f mc flat 806240f.fits 806240f
+
+### Note: couple hundred psString leaks in ppImage here.
+
+
+price@alala:/data/alala/price/megacam/flats>dettool -tostack -simple
+2 0 flat mc
+
+### ===>
+
+price@alala:/data/alala/price/megacam>~/ipp/helpers/detrend_stack.pl 2 0 mc flat
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/scripts/copy.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/scripts/copy.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/scripts/copy.pro	(revision 22322)
@@ -0,0 +1,137 @@
+
+# identify the images ready for copy 
+task	       new.images
+  command      new.images
+  host         -
+  stderr       /data/logfiles/new.images.log
+
+  periods      -poll 1
+  periods      -exec 30
+  periods      -timeout 2
+
+  # success
+  task.exit    0
+    push new.images $stdout
+    $new.image.failure = 0
+  end
+
+  # locked list
+  task.exit    1
+    echo       "new.images: exec failure"
+    $new.image.failure ++
+  end
+
+  # default exit status
+  task.exit    -
+    echo       "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "new.images: timeout"
+    $new.image.failure ++
+  end
+end
+
+# copy specific images from the summit
+task           copy.images
+
+  # these define task properties which are fixed
+
+  period       -exec 2
+  period       -poll 1
+  period       -timeout 50
+
+  # these commands are executed at the start of a new task
+  task.exec
+    local        RemoteName
+    local        FileID
+    local        Host
+
+    queuesize  NewImages -var N
+    if ($N > 0) break
+    if ($network == 0) break
+    if ($filesystem == 1) break
+
+    pop NewImages -var line
+    list tmp -split $line
+    $RemoteName = $tmp:0
+    $FileID     = $tmp:1
+    $Host       = $tmp:2
+    $Ntry       = $tmp:3
+
+    stderr       /data/logfiles/copy/$FileID.log
+    stdout       -queue $FileIDlog
+
+    echo $TaskID
+    echo $TaskName
+    spawn copy.images $RemoteName $FileID $Host -host $Host
+  end
+
+  # success
+  task.exit      0
+    $new.image.failure --
+  end
+
+  # summmit connection failed
+  task.exit      1
+    echo         "copy.images: copy failed $RemoteName $FileID"
+    push         NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+
+  # target disk full
+  task.exit      2
+    echo         "copy.images: disk full"
+    push         NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+
+  # file not found
+  task.exit      3
+    echo         "copy.images: missing file"
+    # send a message to OTIS?
+  end
+
+  # task timed out
+  task.exit      timeout
+    echo        "copy.images: timeout"
+    push        NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+end
+
+# identify the images ready for copy 
+TASK	       new.images
+  COMMAND      new.images
+  HOST         -
+  STDERR       /data/logfiles/new.images.log
+  STDOUT       $stdout
+  EXEC_PERIOD  30
+  POLL_PERIOD  1
+
+  # success
+  EXIT         0
+    STDOUT     @new.images
+    $new.image.failure = 0
+  END
+
+  # locked list
+  EXIT         1
+    MESSAGE    "new.images: exec failure"
+    $new.image.failure ++
+  END
+
+  # default exit status
+  EXIT         -
+    MESSAGE    "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  END
+
+  # operation times out?
+  TIMEOUT      2
+    MESSAGE    "new.images: timeout"
+    $new.image.failure ++
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/scripts/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/scripts/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/scripts/notes.txt	(revision 22322)
@@ -0,0 +1,181 @@
+
+data types:
+  @data : queue
+  $data : system variable
+  
+commands
+  MESSAGE : send message to system log
+  STDOUT  : push task stdout on queue or file
+  STDERR  : push task stderr on queue or file
+
+
+TASK properties:
+  EXEC_PERIOD : seconds between attempts
+  POLL_PERIOD : seconds between checks of task status
+  TIMEOUT     : seconds before task must complete
+  HOST        : machine for controller execution
+  
+  ARG.N       : command and arguments  
+
+  STDIN       : QUEUE source for stdin
+  STDOUT      : QUEUE sink for stdout
+
+  PRE_EXEC    : conditions for execution
+END  
+
+QUEUE 3 new.images
+
+TASK	       new.images
+  COMMAND      new.images
+  HOST         -
+  STDERR       /data/logfiles/new.images.log
+  EXEC_PERIOD  30
+  POLL_PERIOD  1
+
+  # success
+  EXIT         0
+    STDOUT     @new.images
+    ZERO       new.image.failure
+  END
+
+  # locked list
+  EXIT         1
+    MESSAGE    "new.images: exec failure"
+    INCR       new.image.failure
+  END
+
+  # default exit status
+  EXIT         -
+    MESSAGE    "new.images: unknown exit status: $EXIT"
+    INCR       new.image.failure
+  END
+
+  # operation times out?
+  TIMEOUT      2
+    MESSAGE    "new.images: timeout"
+    INCR       new.image.failure
+  END
+END
+
+TASK           copy.images
+  COMMAND      copy.images $RemoteName $FileID $Host
+  HOST         $Host
+  STDERR       /data/logfiles/copy/$file.ID.log
+  EXEC_PERIOD  2
+
+  # pre-exec condition
+  EXEC_TEST
+    REQUIRE @new.images > 0
+    REQUIRE $network    == 1
+    REQUIRE $filesystem == 1
+    PERFORM $RemoteName = @new.images[0]
+    PERFORM $FileID     = @new.images[1]
+    PERFORM $Host       = @new.images[2]
+  END
+
+  # success
+  EXIT         0
+    STDOUT     @new.images
+    $new.image.failure = 0
+  END
+
+  # locked list
+  EXIT         1
+    MESSAGE    "new.images: exec failure"
+    $new.image.failure ++
+  END
+
+  TIMEOUT      30
+    MESSAGE    "new.images: timeout"
+    $new.image.failure ++
+  END
+END
+
+TASK      phase1
+  INPUT   @science.mosaics  # (fileID)
+  ARG.0   phase1
+  ARG.1   INPUT[0]
+  STDIN   @NULL
+  STDOUT  @NULL
+  STDERR  $log/phase1/%s.log INPUT[0]
+  TIMEOUT 10
+  PERIOD  10
+  FAILURE @foo
+END
+
+TASK      test.phase2
+  ARG.0   test.phase2 
+  STDOUT  @science.chips # (hostname) (filename) (mosaicID)
+END
+
+TASK      run.phase2
+  INPUT   @science.chips
+  ARG.0   phase2
+  ARG.1   $INPUT[1]
+  ARG.2   $INPUT[2]
+  HOST    $INPUT[0]
+  TIMEOUT 40
+  PERIOD  20
+END
+
+TASK      test.phase3
+  ARG.0   test.phase3 
+  STDOUT  @phase3.mosaics # (mosaicID)
+END
+
+TASK      run.phase3
+  INPUT   @phase3.mosaics
+  ARG.0   phase3
+  ARG.1   $INPUT[1]
+  ARG.2   $INPUT[2]
+  HOST    $INPUT[0]
+  TIMEOUT 40
+  PERIOD  20
+END
+
+
+
+# example IPP functions
+
+# steps to find and copy a single image from the summit
+
+  - identify image to be copied
+    - local resource (table or file) contains a list of 
+      (remote.filename) (file.ID)
+    - local process generates list from OTIS sources   
+    - (remote.filename) : user@hostname:/full/path/filename.fits
+    - (file.ID) : GPC-01.34.20041112-200110o.raw
+    - (chip) : 34
+    * pop entry from list?
+    * read entry from list & flag?
+
+    - file.ID options:
+      - CAMERA.CHIP.SEQUENCE.TYPE.VERSION
+      * GPC-01.34.20041112-200110o.raw
+      * GPC-01.34.20041112-200110b.raw
+      * MC.13.670123o.raw
+      * MC.13.670123o.flt
+      * MC.MF.670123o.flt
+
+  - determine file.ID for remote file
+    - provided with input list
+
+  - determine primary host for image
+    - based on chip
+    - provided with input list
+
+  - create new storage object with instance on target host
+    - instance = new.object (file.ID) -host (host)
+  - on target host, copy remote.file to instance
+    @host: cp (remote.name) instance
+
+  - if copy fails:	    
+    - put back in queue?
+
+  - add new chip to metadata database raw chips
+  - add new mosaic to metadata database science frames
+
+
+
+exec @host copy.image (remote.name) (file.ID) (host)
+
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/scripts/science.notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/scripts/science.notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/scripts/science.notes.txt	(revision 22322)
@@ -0,0 +1,68 @@
+
+TASK           test.phase1
+  COMMAND      test.phase1
+  HOST         -
+  STDERR       log
+  EXEC_PERIOD  30
+  POLL_PERIOD  1
+
+  # success
+  EXIT         0
+    STDOUT     @science.mosaics
+  END
+
+  # locked list
+  EXIT         1
+    MESSAGE    "new.images: exec failure"
+    $new.image.failure ++
+  END
+
+  # operation times out?
+  TIMEOUT      10
+    MESSAGE    "db search failed"
+  END
+END
+
+TASK           phase1
+  REQUIRE      @science.mosaics > 0
+  PERFORM      $line       = POP (@science.mosaics)
+  PERFORM      $ExposureID = $line[0]
+
+  COMMAND      phase1 $ExposureID
+  STDERR       $log/phase1/$ExposureID.log
+  EXEC_PERIOD  10
+
+  # success
+  EXIT         0
+    STDOUT     @science.chips
+  END
+
+  TIMEOUT      10
+    MESSAGE    "phase1: timeout $ExposureID"
+    $phase1.failure ++
+  END
+
+END
+
+TASK           test.phase2
+
+TASK           phase2
+  REQUIRE      @science.chips > 0
+  PERFORM      $line       = POP (@science.mosaics)
+  PERFORM      $ = $line[0]
+
+  COMMAND      phase1 $ExposureID
+  STDERR       $log/phase1/$ExposureID.log
+  EXEC_PERIOD  10
+
+  # success
+  EXIT         0
+    STDOUT     @science.chips
+  END
+
+  TIMEOUT      10
+    MESSAGE    "phase1: timeout $ExposureID"
+    $phase1.failure ++
+  END
+
+END
Index: /tags/sj_tags/sj_root_20080929/doc/ipptools/tasklist.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/ipptools/tasklist.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/ipptools/tasklist.txt	(revision 22322)
@@ -0,0 +1,23 @@
+
+ task name		period	mode	number
+
+ get table list		10	B	1
+ get table pending	1	B	1
+ copy table		1	P	Ntable
+
+ get exp list		10	B	1
+ get exp pending	1	B	1
+ get imfile list	1	B	Nexp
+ get imfile pending	1	B	1
+ copy imfile		1	P	Nexp * Nchip
+
+ ** the tasks for copy table, get imfile list, and copy imfile could
+    be run less frequently if a single task pass could launch multiple
+    jobs...
+
+ p0search pending	10	B	1
+ p0search update	1	P	Nexp
+
+ p1search pending	1	B	1
+ ppImage (p1)		1	P	Nexp
+ 
Index: /tags/sj_tags/sj_root_20080929/doc/manual/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/manual/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/manual/.cvsignore	(revision 22322)
@@ -0,0 +1,8 @@
+*.aux
+*.dvi
+*.lof
+*.log
+*.tbd
+*.toc
+*.pdf
+manual.ps
Index: /tags/sj_tags/sj_root_20080929/doc/manual/manual.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/manual/manual.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/manual/manual.tex	(revision 22322)
@@ -0,0 +1,1916 @@
+ %%% $Id: manual.tex,v 1.12 2008-02-19 20:06:58 eugene Exp $
+\documentclass[panstarrs,psreport,spec]{panstarrs}
+
+% basic document variables
+\title{Pan-STARRS Image Processing Pipeline}
+\subtitle{User's Guide}
+\shorttitle{PS IPP UG}
+\author{Eugene A. Magnier, Paul A. Price, Joshua Hoblitt}
+\audience{Pan-STARRS IPP Users}
+\group{Pan-STARRS Image Processing Pipeline}
+\project{Pan-STARRS}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-???}
+
+% allow paragraphs to be listed in TOC for now
+\setcounter{tocdepth}{3}
+
+\begin{document}
+\maketitle
+
+% -- Revision History --
+\RevisionsStart
+% version     Date         Description
+DR      & 2007-01-31 & First draft \\ \hline
+\hline
+\end{tabular}
+\end{center}
+
+\inserttbd
+
+\inserttbr
+\pagebreak
+
+\tableofcontents
+\pagebreak
+
+\listoffigures
+\pagebreak
+\pagenumbering{arabic}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Overview}
+
+The Pan-STARRS Image Processing Pipeline (IPP) is a software suite for
+the reduction of astronomical images.  Although designed principally
+for the Pan-STARRS project, it is highly configurable and extensible,
+and hence highly applicable for other projects.
+
+The IPP consists of a number of elements: programs which perform
+specific image analysis steps; a parallel processing environment in
+which the image processes steps are automatically sequenced and
+tracked; a databasing system for associating and calibrating the
+detections of astronomical objects.  These programs use a number of
+internal and external libraries (see \S\ref{sec:dependencies}),
+simplifying the process of adding additional elements as needed.
+
+The core functionality of the IPP is implemented by NN principal
+programs: \code{psphot} is used for the detection and analysis of
+objects in astronomical images; \code{psastro} performs the
+astrometric calibration of the images; \code{ppImage} is used for a
+wide range of single image analysis step, include image detrending and
+the generation of Q/A images and plots; \code{ppMerge} combines
+collections of input detrend images into high-quality masters;
+\code{pswarp} transforms images between different pixel projections
+and coordinate systems; \code{ppStack} is used to combine multiple
+science images, including outlier rejection which is sensitive to
+varying image quality; \code{ppSub} performs image differencing
+incorporating the variations in seeing with multiple optional
+convolution kernels.
+
+In addition to the image-level analysis, the IPP provides DVO, the
+Desktop Virtual Observatory, a database system for tracking
+astronomical objects and detections.  DVO includes tools for querying
+and manipulating the contents of the object database. It also provides
+a number of tools for performing the analysis of quantities at the
+database level.  These include: \code{relphot}, which performs relative
+photometry and calculates robust ensemble photometry for objects;
+\code{relastro}, which calculates average astrometry quatities for
+objects, including proper motion and parallax, as well as iterative
+improvements to the astrometric calibrations of images injested by the
+database; \code{uniphot}, which is used to calculate consistent
+photometric calibrations and transformations.  
+
+The IPP provides a system for bulk automation of all stages of the
+image analysis process within a parallel processing environment.  The
+parallelization scheme is very light-weight, and makes use of
+distributed UNIX jobs operating on multiple machines within a cluster.
+Process scheduling and distribution of the resulting jobs to the
+parallel cluster is performed by \code{pantasks}.  A set of
+\code{pantasks} scripts, \code{ippTasks}, is used to define the
+processing stages.  
+
+Data flow within the IPP is managed via interaction with a collection
+of database tables, representing the steps of the analysis pipeline.
+The IPP examines the state of these database tables to determine which
+jobs should be performed next.  A set of simple text files
+(\code{dbconfig}) defines the database scheme and are also used to
+automatically generate C code used to query the database tables.
+Higher level command-line programs (\code{ippTools}), built on these
+APIs, are available to both the end user and to \code{pantasks)} to
+examine the state of the pipeline database.  A set of Perl scripts
+(\code{ippScripts}) provide the glue between the individual IPP
+analysis programs and the parallel processing environment.  A
+web-based tool, \code{ippMonitor} provides the user interface for
+monitoring the pipeline and the current status of the data analysis
+process.
+
+\note{this is not needed here: move elsewhere}
+
+\code{psLib} is the Pan-STARRS library, containing a range of
+low-level structures (e.g., vectors, images, etc.) and functions
+(e.g., write an image to FITS file), most of which are not specific to
+astronomy.  \code{psModules} is built on top of psLib, and provides
+higher-level capabilities that are centered around astronomical data
+manipulation.
+
+We have some Perl modules which are used to facilitate the data flow
+in processing: \code{PS::IPP::Metadata::Config} reads ``metadata
+configuration'' files; \code{PS::IPP::Metadata::Stats} interprets
+statistics from \code{ppStats}; \code{PS::IPP::Metadata::List}
+interprets output lists from the \code{ippTools}; and
+\code{PS::IPP::Config} reads the configuration files.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Using the IPP}
+
+\subsection{Conceptual Overview}
+
+The operating goals of the IPP can be broken down into several major categories:
+\begin{itemize}
+\item reduction, analysis, and calibration of individual astronomical
+  science images.
+\item combinations (additions and subtractions) of groups of science
+image, along with their analysis and calibration.
+\item analysis of the ensemble properties of astronomical objects
+detected from many images, including improved astrometric and
+photometric calibrations.
+\item construction of the necessary master detrend images and other
+calibration information needed to perform the science analysis.
+\item enabling further investigation into the data characteristics by
+tracking sufficient metadata and providing tools for this analysis.
+\end{itemize}
+Furthermore, the IPP is designed to perform these tasks with a high
+degree of automation and for large collections of data from multiple
+instruments.
+
+\begin{figure}
+\psfig{file=userview.ps,width=6in,angle=0}
+\caption{User's view of the IPP}
+\label{fig:ipp-userview}
+\end{figure}
+
+A cartoon of the user's view on the IPP is seen in
+Figure~\ref{fig:ipp-userview}.  Images are injected into the IPP,
+either automatically by software interacting with the telescope
+systems or manually by the user.  The images are processed by the IPP
+through any number of analysis steps; the specifics of the analysis
+will depend on the type of image, the overall configuration, and
+additional requests the user may make.  The user may use the program
+\code{pantasks} to control and monitor the ongoing processing, or view
+summary result information in the ippMonitor web browser.  As science
+images are processed, the information about the objects detected in
+these images is passed to the DVO database.  The user may explore the
+results or perform further science analysis by interacting with the
+DVO database via the DVO Shell.  From this tool, the user may, for
+example, create color-magnitude diagrams of regions in the sky, or
+plot light curves of specific objects.  The DVO Shell makes it
+possible to performing data extraction and data visualization on the
+DVO database.
+
+Below, we discuss in more detail how a user would use the IPP in
+several different operating scenarios: to reduce the results from an
+individual observing run; to perform automatic analysis of data from a
+telescope with a blocked-out schedule, as for example the CFHT Megacam
+Queue; to perform automatic analysis of data from a continuous survey
+instrument such as the Pan-STARRS PS1 Telescope.  These difference
+scenarios have slightly different conditions and underlying
+assumptions, and make a good set of examples for the types of
+decisions a user much make.
+
+All of these scenarios have a few common elements.  The first goal of
+the user is to explore the characteristics of the instrument in
+question and to define initial master detrend images.  The intial
+master detrend images may be used to test the validity of new detrend
+images and thereby test the evolution of the instrument response.
+Next, the user will make an initial analysis of science images.  This
+initial analysis may be used to construct further master detrend or
+calibration information (eg, flat-field correction images; reference
+astrometric parameters; deeper astrometric reference catalog).
+Finally, the user may begin large-scale automatic analysis of the
+complete data collection or the real-time data stream.
+
+\subsection{Projects and Databases}
+
+The IPP has the concept of a 'project': a related group of data that
+are analysed within the same context.  A project may consist of data
+from many different camera, or the user may find it more convenient to
+divide data even from the same camera into multiple independent
+projects.  A single installation of the IPP may operate on many
+separate projects at the same time.  
+
+Within a single project, the user may also choose to define more than
+one output DVO database.  For example, within the Pan-STARRS project,
+the detections from the different survey modes, with different depths
+and cadences, will initially be saved in separate DVO databases, and
+only later joined for improved calibration and more in-depth analysis.
+
+\subsection{User Interface Tools}
+
+In this section, we introduce the user to the IPP user interface
+tools.  These are the elements of the IPP which provide feedback to
+the user or allow the user to initiate a new type of analysis or to
+start and stop the automatic processing of the IPP.
+
+\subsubsection{ippMonitor}
+
+\begin{figure}
+\psfig{file=ippMonitor.eps,width=6in,angle=0}
+\caption{Example Screen Shot from the ippMonitor}
+\label{fig:ippMonitor}
+\end{figure}
+
+The \code{ippMonitor} is a web-based tool that allows the end-user to
+explore at a high level the results of the IPP analysis.  There is an
+initial login page, after which the user is asked to choose a project.
+Once a project is selected, the user may view pages which show summary
+information of the images that have been processed at each of the
+different stages.  There are top-level sections for the detrend
+analysis, the single science image analysis, and the different image
+combinations.  Further clicking takes the user to more detailed
+information about the individual images: jpegs of the reduced image,
+plots showing the psf modeling and the astrometric analysis, etc.
+
+\note{to be added to ippMonitor: pages showing summary plots from DVO}
+
+\subsubsection{DVO Shell}
+
+The DVO database is the primary output destination for results from
+the IPP.  The DVO shell is the basic user interface to the DVO
+database.  Within the DVO shell, the user may extract measurments from
+the database, create plots, or view the locations of objects and
+images on projections of the sky.
+
+A complete user's guide is available at DVO REF.  Here are a few
+quick-start details:
+
+Start the DVO shell by typing: \code{dvo}.  You will be given a
+prompt, at which you may type commands.  This is an interactive shell,
+with standard readline comand-line editing features.  The \code{help}
+command gives further information about the commands.  Commands will
+give additional information if they are given a '-help' option, or in
+many cases if the command is typed without arguments.
+
+\begin{figure}
+\psfig{file=skycells.ps,width=4in,angle=0}
+\psfig{file=tau-region.ps,width=2in,angle=0}
+\caption{Example Plots from DVO}
+\label{fig:dvo-sample}
+\end{figure}
+
+\begin{verbatim}
+catdir skycells
+region -n view1 0 0 80 ait
+images
+
+catdir taurus-project
+region -n view2 64.8 26.1 4.0
+images
+avextract ra,dec where (g - i > 2.0)
+cplot ra dec -c red
+avextract ra,dec where (g - i < 0.5)
+cplot ra dec -c blue
+\end{verbatim}
+
+Choose the DVO database with the command \code{catdir}.  \note{you
+must specify the path to the top of the database directories}.  Define
+a view port for plotting objects and images on a sky projection with:
+\code{region RA DEC range [projection]}.  The projection may be
+\code{ait} (aitoff projection), \code{sin} (sin projection), ....  
+
+\subsubsection{Pantasks}
+
+Pantasks is the program which schedules the analysis to be performed
+and distributes the analysis jobs across the parallel processing
+environemnt.  A more complete user's guide is available at REF. Here
+we give a basic overview of starting up the IPP.  In this example, we
+are using the stand-alone pantasks program.  It is also possible to
+run pantasks in a client / server mode.  In this mode, the server runs
+continuously in the background, and multiple users may interact with
+the same pantasks server via the pantasks client tool.  Most of the
+commands are the same in both contexts.  However, for security reasons
+in the client / server mode, the clients are not able to issue certain
+commands to the server.  Thus, there are some differences in the details.
+
+Start \code{pantasks}.  Load the collection of IPP tasks with the
+command: \code{module pantasks.pro}.  Add remote analysis hosts to the
+parallel processing controller (pcontrol): \code{controller host add
+NAME}, where NAME is the name of a machine set up to run the IPP.  Add
+the IPP project(s) of interest: \code{add.database (project)}.  Start
+the analysis: \code{run}.  Check the current status with
+\code{status}.  If something goes wrong, stop the processing with
+\code{stop} or \code{halt}.  The former will continue to accept the
+results from the jobs already launched, while the latter halts all
+processing. \note{halt does not current stop pcontrol}.  
+
+\subsubsection{ippTools \& ippScripts}
+
+The IPP uses a \code{mysql} database to keep track of the data to be
+analysed and the results of analysis steps.  The IPP user interacts
+with this database via a set of programs called \code{ippTools} and
+\code{ippScripts}.  The difference between these is that the
+\code{ippTools} are somewhat lower-level programs to interact directly
+with the database, while the ippScripts perform a more complex
+operation, sometimes wrapping multiple calls to commands within
+\code{ippTools}.   
+
+\begin{itemize}
+\item \code{ipp_serial_inject.pl} : inject data into the IPP database.
+\item \code{dettool -definebyquery} : define a new detrend run.  With
+  this command, the user defines a set of criteria which define a
+  collection of input images to be used to generate a detrend image.
+  The parameters which define the output detrend image also
+  specified.
+\item \code{dettool -updatedetrun} : manually set the state of a
+  detrend run.  
+\item \note{extend this list...}
+\end{itemize}
+
+The ippTools accept a common set of command-line arguments which
+modify their behavior:
+\begin{itemize}
+\item \code{-pretend} : show the expected result, but do not apply the
+  results to the database
+\item \code{-simple} return results with one line for each output
+  component.  Without this flag, the results are returned with the
+  versbose, but self-documenting, psMetadataConfig format.
+\item \code{-dbname} specify the database to use
+\item \note{extend this list...}
+\end{itemize}
+
+The collection of programs which are used to initiate different stages
+of the IPP analysis is called \code{ippTools}.  This suite of programs 
+
+\subsection{Operational Scenarios}
+
+\subsubsection{Reducing an Observing Run}
+
+For a single observing run, the user will typically have data from
+several nights, both science and raw detrend images.  The following
+steps illustrate the analysis with data from a sample observing run.
+We assume the user has already installed the IPP software and the
+external software (mysql, apache, php, etc), set up the configuration
+for the camera, defined a project for this analysis, started pantasks,
+and loaded the analysis tasks.  This example shows the steps to build
+the detrend data and process the science data.
+
+The first step is to inject all of the data into the IPP database with
+the command \code{ipp_serial_inject.pl}.  It is possible to define
+abstract data storage paths in the user's \code{.ipprc} file in the
+\code{DATAPATH} section. Each line gives the relationship between an
+abstract data location and the real-world UNIX path to that data.  The
+script \code{ipp_serial_inject.pl} will interpret the location of the
+data in terms of the DATAPATH hierarchy when identifying the data for
+the database. When injecting the data, it is convenient to define a
+working directory with the \code{--workdir} option to
+\code{ipp_serial_inject.pl}.  The science processing steps will use
+this location to store the output data products.
+
+% complete command here
+
+As the images are injected, they will be placed in the table of
+\code{new} exposures.  If \code{pantasks} is running with the modules
+loaded, the images will be processed for registration.  In this step,
+a minor analysis of the image statistics and an examination of the
+header information is performed, and the results used to provide
+further information about the images in the database.  As images are
+registered, they migrate from the \code{new} to the \code{raw}
+tables.  These tables can be view in the \code{ippMonitor} under the
+\code{Load & Setup} pages.
+
+Once the images have been injected, the user will want to create, in
+order, bias, dark, shutter, flat, and fringe frames.  As these are
+created, they may be used to generate the next level of image.  It is
+important not to define, eg, a flat run before the lower-precedence
+elements are defined.  At the moment, it is not possible to define a
+detrend analysis which blocks until a previous detrend analysis is
+complete.  To start, define a bias based on a query to the database:
+
+% example command hrere
+
+\code{dettool -dbname name -definebyquery -det_type bias etc}
+
+For a small observing run, it is probably possible to include all bias
+images in the initial pass.  IPP will iteratively reject complete
+input images which are outliers from the ensemble, as well as
+individual pixels.  In the \code{ippMonitor}, rejected images will be
+greyed out in the residual pages.  Examine the resulting residual
+images.  If there are patterns to the residuals, it may be necessary
+to define subgroups, perhaps by time or ccd temperature.  For example:
+
+% example command here.
+
+After a bias or set of biases are defined, follow the same process for
+a dark frame.  It may be necessary to supply a non-linear dark current
+correction (perhaps one for each amplifier or chip).  The script
+\code{ipp_darkstats.pl} can be used to examine this trend.  
+
+If a shutter correction is necessary, make sure to obtain data which
+can be used to generate the correction: a series of flat-field images
+with a range of exposure times, particularly near the short end of the
+range.  If the shutter correction will be skipped, set the SHUTTER
+entries in the camera's ppImage.config file to FALSE.
+
+A flat-field image is next.  Initially, we must define a raw
+flat-field image, from dome or twilight flat-field images.  Below, we
+will discuss the generation of a flat-field correction image from
+dithered science images.  Do not forget to specify the filter for each
+flat-field image
+
+% example command.
+
+Up to now, we have been ignoring the bad pixel mask.  At this point,
+the user may generate a bad pixel mask from the collection of
+processed flat-field images.  Use the script \code{UNKNOWN} to build a
+mask image.  Turn on the use of the mask by setting \code{MASK} to
+true in the \code{ppImage.config} file.  It is probably a good idea to
+now re-run the previous stages with the mask applied: this will result
+in better statistics for the residual images.
+
+If certain filters require a fringe image, the user may inform the IPP
+of this fact by setting the FRINGE.FILTERS entry in the
+\code{ppImage.config} file.  Use a set of night-time sky images to
+generate a fringe frame. \tbd{How to specify more than one fringe
+mode?}
+
+Activate the chip-level science analysis tasks in pantasks: chip.on.
+With this off, no data will be processed before the detrend images are
+ready.  Once this is turned on, the science images will start to be
+processed.
+
+\tbd{Discuss blocks, re-queuing science images, analysis versions}.
+
+\tbd{Illustrate how to specify the DVO output database}
+
+\subsubsection{Pipeline Reduction : PI-Oriented Observatory}
+
+An PI-oriented observatory has the opportunity to operate the analysis
+system on a somewhat larger scale than the individual user.  At a
+PI-oriented observatory, there will normally be a rotating suite of
+instruments, each of which is mounted on the telescope for a
+substantial number of nights.  Over the course of months and years,
+the observatory will collect enough data from the instruments to
+perform a much more detailed level of analysis of the instrument than
+is possible in a single observing run by a single PI.  In such a
+situation, the observatory may use the IPP to generate and track the
+high-quality master detrend images, to apply the detrend images to the
+data as it is being collected, and to monitor the instrumental zero
+points and other calibration information.  The basic concepts of
+running the IPP at a PI observatory are identical to that of a single
+user.  There are, however, a few modifications which may be
+interesting.
+
+First, it is natural to automatically inject and register the data as
+it is obtained at the telescope.  The telescope readout software could
+be modified to call \code{ipp_serial_inject.pl} after each image is
+obtained.  Alternatively, this step could take place downstream
+wherever the data are archived.  In an observatory environment, more
+so than in a single-user situation, it may be perfereable to use the
+Nebulous infrastructure to manage the location and copies of the image
+data.  \tbd{Define an inject mechanism that uses Nebulous}.
+
+Second, it is interesting to consider on what timescale to test and
+re-build the master detrend images.  In the single user case, the
+possible input detrend images are fairly limited.  As illustrated
+above, the single user may use the IPP to define master detrend images
+and test the consistency of the inputs against these masters.  In an
+observatory environment, it may be more appropriate to build the
+masters only on regular intervals when there is a suspicion the
+instrument may have changed.  The periods in which the instrument is
+mounted define an obvious possible starting point for this period.
+Alternatively, the detrend creation steps in the IPP can be defined to
+run in verify mode on a nightly basis to test the current state of the
+instrument.  For the PI-oriented observatory, the number of variables
+may make it difficult to rely on these results in an automatic
+fashion; user examination of the results with \code{ippMonitor} may be
+needed to decide if a new master should be built.
+
+\subsubsection{Pipeline Reduction : Survey Observatory}
+
+A telescope used as a survey instrument has a somewhat different, more
+limited range of circumstances, than a PI-oriented observatory.  There
+is generally a pre-defined schedule, perhaps with only a single
+instrument.  The instrument configuration is more controlled, and the
+types of observations are more consistently defined.  In this case, an
+automatic validation process can be more reliably used.  The IPP can
+be set up to run the detrend validation stage at the beginning of
+every night, to automatically generate a new master if any of the
+input detrend images are out of spec, and to start automatic
+processing of the night's data with the new master images when they
+are ready. 
+
+\section{IPP Components}
+
+\tbd{Need to document command-line arguments, perhaps even
+algorithms.}
+
+\subsection{Analysis Programs}
+
+\subsubsection{psphot}
+
+The detection and characterization of astronomical objects within an
+image is performed by the IPP component called \code{psphot}.  This
+software may be used as a stand-alone program, or it may be called by
+other IPP programs to operate on the images they generate.  A more
+detailed guide is available at REF.  Here we summarize the basic
+operation and command-line options.
+
+The basic call to the stand-alone version of \code{psphot} is:
+%
+\code{psphot -file input.fits output [options]}
+%
+where \code{[options]}
+represents options to the IPP configuration system as well as some
+specific to \code{psphot}.  In the case of a ``split'' camera format,
+where separate chips or amps are stored in separate files, the
+``input.fits'' may be a UNIX file glob which will expand to the set of
+file; note that the glob must be protected with double quotes for
+psphot. Alternatively, the input file list may be written to a text
+file and the \code{-file input.fits} entry replace with \code{-list
+  file.txt}.  The output entry is used as the root of a number of
+possible output files selected by the user.
+
+Standard IPP-wide command-line options (document elsewhere):
+\begin{itemize}
+\item \code{-site site.mdc}
+\item \code{-camera camera-name}
+\item \code{-recipe NAME VALUE}
+\item \code{-D KEY VALUE}
+\item \code{-Df KEY VALUE}
+\item \code{-Db KEY VALUE}
+\item \code{-Di KEY VALUE}
+\end{itemize}
+
+Here are other possible command-line options specific to psphot:
+\begin{itemize}
+\item -version : report version info and exit
+\item -mask mask.fits 
+\item -weight weight.fits
+\item -modeltest X Y : run the object model on the object at the given coordinates
+\begin{itemize}
+\item -model
+\item -fitmode
+\item -fitset
+\end{itemize}
+\item -photcode : manually specify the photcode for this image
+\item -region : limit the analysis to a specific image area
+\item -chip : limit the analysis to the given list of chips
+\item -psf : use the specified psf model (do not build a psf model)
+\item -src : perform photometry on the positions given in the list
+\end{itemize}
+
+There are many configuration options in the \code{psphot.config}
+recipe file.  These are defined in the \code{psphot} manual.
+
+\subsubsection{psastro}
+
+\subsubsection{ppStats}
+
+\subsubsection{ppImage}
+
+\subsubsection{ppMerge}
+
+\subsubsection{ppNorm}
+
+\subsection{Pipeline Infrastructure}
+
+\subsubsection{ippdb / dbconfig}
+
+The IPP uses a mysql database to track the data to be processed,
+the current state of the processing steps, and summary information
+from each of the analysis steps.  The database schema is defined by
+the set of MDC files in \code{dbconfig}.  These files are used to
+generate a set of C-level APIs to access the database tables.  
+
+\subsubsection{ippTools}
+
+\subsubsection{ippScripts}
+
+\subsubsection{ippTasks and panTasks}
+
+\subsubsection{ippMonitor}
+
+\subsection{Nebulous}
+
+\subsection{DVO}
+
+\subsubsection{DVO Shell}
+
+\subsubsection{Adding and Removing Data}
+
+\subsubsubsection{addstar}
+
+\subsubsubsection{delstar}
+
+\subsubsubsection{getstar}
+
+\subsubsection{Database Level Calibrations}
+
+\subsubsubsection{relphot}
+
+\subsubsubsection{uniphot}
+
+\subsubsubsection{relastro}
+
+\subsubsection{Other Tools}
+
+\subsubsubsection{sky cell tools}
+
+\subsection{Software Architecture}
+
+\subsubsection{psLib}
+
+\subsubsection{psModules}
+
+\subsubsection{Perl Modules}
+
+\section{Configuration}
+
+Correct use of the IPP depends on a collection of configuration files.
+These files define information about the processing environment, such
+as the location of reference data or the name of computers available
+for analysis or storage.  They also define the operating parameters
+for the different programs, and choices about which analysis steps to
+perform.
+
+Configuration information for the image processing is provided on four
+levels: the site-specific information, camera-specific details,
+information related to different formats of data from the same camera,
+and recipe configurations for the different analysis programs or
+steps.  The configuration information is stored in files using the
+``psMetadataConfig'' (MDC) file format, which is briefly described
+below (\S\ref{sec:MDC}); for a more detailed description, see the
+psLib SDRS (PSDC-430-007).
+
+The configuration levels for the image processing components of the
+IPP are:
+\begin{itemize}
+\item Options for the particular installation of the
+  IPP: the {\it site};
+\item Options specifying the details about the instrument: the {\it
+camera};
+\item Options specifying the format of the FITS file: the {\it
+  format}; and
+\item Options specifying the particular parameter choices that affect
+  the details of an analysis: the {\it recipe}.
+\end{itemize}
+Note that these are arranged in an hierarchical order, with the site
+configuration being the most general, and the recipe configurations
+the most specific.  For example, not all sites will have to deal with
+all cameras, and different cameras may require different recipes at
+different times according to their particular quirks, analysis
+experimentations, or their evolution.
+
+We have provided examples of each of these configurations in the
+\code{config} component of the IPP, which should be a useful guide for
+setting up your own.  The Pan-STARRS IPP Configuration Guide
+(PSDC-430-???) has all the detailed information.
+
+\subsubsection{Setting up configuration files}
+
+When the IPP software is installed, it generates a copy of the
+top-level {\it site} configuration file:
+\code{PREFIX/share/ippconfig/ipprc.config}. Programs look for this
+information in the user's home directory at \code{~/.ipprc}.  A new
+user may want to link \code{~/.ipprc} to the installed copy
+\code{PREFIX/share/ippconfig/ipprc.config}, in which case new
+installations will be immediately available.  Alternatively, the user
+may want to copy the file in order to make their own modifications, if
+for example the user needs to modify the recipes for a specific
+camera.
+
+After the {\it site} configuration file is read by IPP programs, they
+then attempt to read the {\it camera} configuration files defined in
+the {\it site} file by the list of cameras.  These entries refer to
+files for each camera.  The IPP configuration system searches for these
+files (and others referred there in) in the PATH defined at the top of
+the {\it site} file.  This value mimics the UNIX PATH variable, and
+consists of a colon-separated list of locations.  The first entry
+found from this list is used by the configuration system, allowing a
+user to override, for example, the globally installed camera
+configuration for some camera.  
+
+\subsubsection{Overview of MDC format}
+\label{sec:MDC}
+
+psLib defines a \code{psMetadata} structure which can carry labeled
+data of arbitrary types.  Originally designed for carrying the data in
+FITS headers, the \code{psMetadata} have proved so generally useful
+that we use them for our configurations (and a multitude of other
+uses!).  We have designed a human-readable text-based format --- the
+``MetaData Configuration'' (MDC) format --- which we use for this end.
+
+Each simple entry in an MDC file must contain the name, type and
+value.  Each of these is on a single line, separated by whitespace,
+and in that order.  Comments may be placed at the end of the line (or
+on a blank line), after a hash mark (\code{#}).  Whitespace at the
+beginning and end of strings (either the name, value or comment) are
+stripped.
+
+The simple types follow the psLib types.  Integers are specified by a
+letter indicating if the integer is signed (\code{S}) or unsigned
+(\code{U}) and a number indicating the dynamic range in bits (8, 16,
+32 or 64); e.g., \code{U8} is commonly used for bit mask values,
+\code{S32} is commonly used for ordinary integer values.  Floating
+point values are specified by the letter \code{F} and a number
+indicating the precision in bits (32 or 64): \code{F32} (single
+precision) or \code{F64} (double precision).  Strings are specified by
+\code{STR}.  Times may be specified with the following types:
+\code{UTC,UT1,TAI,TT}; values for the time are expected to be in
+ISO8601 format (\code{YYYY-MM-DDTHH:MM:SS.sZ}).
+
+Names are traditionally all-caps, though there is no reason why they
+must be; the names are case-sensitive.  A name may not be repeated
+unless it has previously been declared to be of type \code{MULTI} (no
+value should be provided with this declaration):
+\begin{verbatim}
+COMMENT    MULTI
+COMMENT    STR    Having more than one COMMENT like this
+COMMENT    STR    is permitted because of the MULTI.
+\end{verbatim}
+
+A hierarchy can be made using the \code{METADATA} type, which signals
+a new level:
+\begin{verbatim}
+JANITOR    METADATA
+    NAME          STR    John Doe
+    PAY           F32    1234.56
+    ECCENTRICITY  STR    9.87
+END
+\end{verbatim}
+Note that a \code{METADATA} block is closed by an \code{END}.  No
+identing need be done within a \code{METADATA} block, but it is useful
+to be able to see the levels at a glance (just like in a C program).
+\code{METADATA} blocks may be nested within \code{METADATA} blocks,
+probably down as far as you have the patience to try.  Note that
+\code{MULTI} declarations only apply to the current level --- there is
+no inheritance.
+
+The above format can be long if there are many \code{METADATA}s with
+similar contents.  For this reason, we provide the \code{TYPE}
+declaration, which generates a \code{METADATA} with the contents each
+of type \code{STR}:
+\begin{verbatim}
+TYPE          EMPLOYEE    NAME       PAY        ECCENTRICITY
+\end{verbatim}
+Now, the type \code{EMPLOYEE} may be used, with string values (NB: no
+spaces allowed!) to specify multiple entries:
+\begin{verbatim}
+JANITOR       EMPLOYEE    JohnDoe    1234.56    9.87
+PROGRAMMER    EMPLOYEE    FooBar     2345.67    1.00
+\end{verbatim}
+This is the same as the much longer block:
+\begin{verbatim}
+JANITOR       METADATA
+    NAME            STR     JohnDoe
+    PAY             STR     1234.56
+    ECCENTRICITY    STR     9.87
+END
+PROGRAMMER    METADATA
+    NAME            STR     FooBar
+    PAY             STR     2345.67
+    ECCENTRICITY    STR     1.00
+END
+\end{verbatim}
+Like the \code{MULTI}, \code{TYPE} declarations only apply to the
+current level.
+
+\subsection{Filenames : UNIX Paths, Abstract Paths, Nebulous}
+
+The IPP programs recognize three types of file names: a standard UNIX
+path, an abstract path with a top-level location defined by the IPP
+configuration system, and a Nebulous path, in which the file location
+is completely abstracted by the Nebulous file management system:
+\begin{itemize}
+\item \code{file:///path/to/file.ext} : a UNIX path, always relative
+  to the root of the file system.  It is valid to drop the
+  \code{file:/} component of the name.  The IPP accepts an arbitrary
+  number of slashes after \code{file:}.
+\item \code{path://PATH/file.ext} : An abstract path. The value of
+  PATH is iterpolated from the corresponding entry in the
+  \code{DATAPATH} table given in the site configuration file.
+\item \code{neb://full/nebulous/name} : A nebulous filename.  The IPP
+  programs query Nebulous for the true path of (one copy of) the file.
+\end{itemize}
+If you wish to expand a file name in any of these forms to a standard
+UNIX path, use the program \code{ipp_datapath.pl}.  
+
+\section{Installation}
+
+There presently exists a few different systems for automatically building the
+the majority of IPP software.  The \code{psconfig} method of building is
+targeted towards general end users.  Where as the current batch of
+\code{jhbuild} configuration files require the user to have access to the IPP's
+CVS tree, making it more of interest to active developers.
+
+\subsection{psconfig}
+
+The psconfig system allows the user to build and install the IPP
+software suite into a location which is flexibly defined by the user.
+The tools here also set up the user's environment variables
+(\code{PATH}, \code{PERL5LIB}, \code{LIBRARY_PATH}, etc) to make use
+of the installed software.  With the psconfig tools, it is easy to
+switch between different installed versions or to recompile subsets of
+the IPP tree.  It is also possible for a user to install the complete
+IPP system without access to the root password.
+
+\subsubsection{Preparation}
+
+\paragraph{Choose the build and install directories}  You need to
+decide where to install the IPP software, and also where to build the
+source code.  By default, the psconfig system will install the IPP
+software in \code{~/psconfig}.  The IPP code will be 
+
+\paragraph{Unpack the IPP tarball.}  The IPP source code is
+distributed as a single tarball for all of the IPP software
+(ipp-M.NN.tgz, where M.NN is the version number, currently 2.1).  Use
+the command \code{tar xvzf ipp-M.NN.tgz} or \code{gzcat ipp-M.NN.tgz |
+tar xvz} on older platforms.  The tarball will unpack into a directory
+named ipp-M.NN.
+
+\paragraph{Unpack the external Perl modules.} If needed, the complete
+collection of Perl modules used by the IPP is also distributed from
+the IPP web site as a tarball, extperl.tgz.  Use the command \code{tar
+xvzf extperl.tgz}, which will unpack the tarball into a directory
+called \code{extperl}.  This must be at the same location as the top
+level directory.  The program \code{pscheckperl} below will identify
+any external Perl modules which are missing from your system.
+
+\paragraph{Download the external C libraries.}  If needed, the
+external libraries needed by the IPP are available from the IPP web
+pages. Download these as needed to a directory called \code{extlibs},
+again parallel to the \code{ipp-M.NN} directory.  The program
+\code{pschecklibs} below will identify any external C libraries which
+are missing from your system.
+
+\paragraph{Set up your account to use the psconfig system.}  The
+psconfig system provides a script which sets up the necessary UNIX
+environment variables and aliases, enabling the compiler and the
+programs to find your installed libraries and Perl modules.  The
+details depend on your UNIX shell:
+
+\subparagraph{csh users}
+
+To use the psconfig system, place the following line in your ~/.cshrc
+file:
+
+\code{alias psconfig "source ../ipp-M.NN/psconfig/psconfig.csh"}
+
+where ../ipp-M.NN is the location of the extracted ipp tarball.  If
+you prefer, you may copy the file psconfig.csh to another location.
+If, for example, you would like to remove the build directories after
+building the IPP, you will need a persistent copy of psconfig.csh.
+
+By default, the psconfig system places the installed IPP programs and
+configuration files in a directory in your home directory:
+\code{~/psconfig}.  To use a different location, place the following
+line in \code{~/.psconfigrc} (otherwise not needed):
+
+\code{set PSCONFDIR = INSTALL_PATH}
+
+where \code{INSTALL_PATH} is the top-level directory for all installed files.
+
+\subparagraph{bash users}
+
+Add the following line to your \code{~/.bashrc} file:
+
+\code{alias psconfig="source PATH/psconfig.bash"}
+
+It is also necessary to edit the file psconfig.bash to set the
+\code{PSCONFIG_DIR} variable to this directory as well.
+
+\tbd{set this up for better discovery}
+
+\paragraph {Set the installation version}
+
+The IPP is a large and complex software system.  A major goal of the
+IPP build system is to be user-friendly for those end users which do
+not have root access on their machines.  Using the IPP build tools, it
+is possible to install the complete system as a non-priviledged user.
+The build system also makes it possible to maintain multiple
+simultaneous installations with different versions of the
+software. This latter feature is particularly important for developers
+who need to be able to make tests and comparisons of different
+versions.
+
+With \code{psconfig}, you may have multiple, parallel installations of
+the IPP.  These may be different versions of the software, or
+installations for different computer architectures, or installations
+with different compilation options.  For example, you may want an
+installation with tracing and debug information turned on and a second
+with all optimizations turned on.  With the psconfig build system,
+each installation of the IPP software is placed in a directory
+identified by an installation name and the computer architecture.  The
+installation name is up to the person who compiles the IPP software,
+and is an arbtrary word or string.  For example, you may choose the
+install the optimized version under ipp-2.1-opt and the debug version
+under ipp-2.1-debug.  The psconfig system will create a directory in
+\code{~/psconfig} (or where ever \code{PSCONDIR} is set) called
+ipp-2.1-opt.linux (if building on a linux 32bit system), or something
+equivalent.
+
+Before running or compiling the IPP, it is necessary to use
+\code{psconfig} to set the installation name:
+%
+\code{psconfig (name)}
+%
+This command sets aliases and environment variables for the current
+shell to point at the named IPP installation, for the current 
+hardware.  For example:
+%
+\code{psconfig default}
+%
+will set the \code{PATH} to include
+\code{~/psconfig/default.linux/bin} on a 32-bit linux system, and the
+other paths to point at the corresponding installation directories.
+
+Users who wish to automatically have access to an IPP installation
+should add the psconfig line to their \code{~/.cshrc} or
+\code{~/.bashrc} files.  
+
+\subsubsection{Check External Dependencies}
+
+The IPP build system is run from the directory
+\code{../ipp-M.NN/psconfig}.  Within this directory are the build
+scripts and scripts to prove the build environment.  Before building
+the IPP suite, you should first check for the needed C libraries and
+Perl scripts.  Start by \code{cd}-ing into \code{ipp-M.NN/psconfig}.  
+
+\subsubsubsection{External C libraries}
+
+The program \code{pschecklibs} in the \code{ipp-M.NN/psconfig}
+directory will check for required system libraries and headers:
+%
+\code{pschecklibs}
+%
+It examines the system libraries, libraries defined by
+\code{LIBRARY_PATH}, and the installation library defined by psconfig.
+Any missing dependencies will be listed.  Tarballs for these libraries
+may be found on the Pan-STARRS web site at:
+%
+\code{http://pan-starrs.ifa.hawaii.edu/project/IPP/software/ext}
+%
+These should be installed so they will be available in the user's
+path.  This can be done with the \code{psconfig} tools by using
+\code{psconfigure} to replace the standard \code{configure} command.
+The \code{psconfigure} command applies the needed \code{--prefix}
+options to place the libraries within the IPP installation tree.
+There is also an equivalent for \code{autogen.sh}, \code{psautogen},
+if needed.
+
+\subsubsubsection{External Perl Modules}
+
+The program \code{pscheckperl} in the \code{ipp-M.NN/psconfig}
+directory will check for required Perl modules, and can be used to
+install them in the appropriate user location in the psconfig system.
+The command defaults to the latest perl installation table in the
+\code{tagsets} directory.  If you have CVS access and choose to check
+out an older IPP distribution, it is necessary to supply the
+distribution name to \code{pscheckperl}.  Otherwise, run it without arguments:
+%
+\code{pscheckperl}
+%
+This will test for the perl modules specified for the latest ipp
+release.  If any modules are missing, you should install the perl
+module tarball (see above), or they can be download from the
+Pan-STARRS web site:
+%
+\code{http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extperl}
+
+The tarballs should be placed in a directory \code{extperl} parallel
+to the ipp directory (two levels up from this directory).  If the
+tarballs are in the correct location, they can be built by supplying
+the \code{-build} flag to \code{pscheckperl}:
+
+\code{pscheckperl -build}
+
+\subsubsection{Building IPP}
+
+To build the full IPP tree using the psconfig system, run
+\code{psbuild} in the \code{ipp-M.NN/psconfig} directory.  By default,
+\code{psbuild} will use the latest IPP distribution table, in the
+directory \code{tagsets}, and build the IPP components according to
+the rules in that distribution file.  The distribution files specify
+which versions (which CVS tags) of the different programs are to be
+built, and in which order, for a given distribution of the IPP.  If
+you have access to the IPP CVS tree and check out an old IPP
+distribution, it is possible to specify the file for that
+distribution, and build the software as it appeared for that older
+distribution.  It is also possible to use the tools in this directory
+to check out the older code and to build tarballs for the older
+distributions.  However, for the typical end user building the IPP
+from a distributed tarball, it is only necessary to run \code{psbuild}
+without additional arguments:
+%
+\code{psbuild ipp-1.2}
+%
+
+There are a number of command-line options to \code{psbuild} which
+control how the software is built or which components of the IPP to
+build:
+\begin{verbatim}
+ -version (version) : specify alternate psconfig installation version
+ -clean             : clean the source directories before building
+ -rebuild           : run 'autogen' (C code)
+ -optimize          : set flags for optimized code
+ -only (module)     : only build the specified module
+ -start (module)    : begin build at specified module
+ -stop (module)     : stop build after specified module
+\end{verbatim}
+
+Summary of psconfig operations:
+\begin{verbatim}
+psdist -tag        : tag CVS tree
+psdist -dist       : build tarball from tagged tree
+psdist -dist -head : build tarball from head
+psbuild            : build and install software in tree
+pschecklibs        : check for needed external software
+pscheckperl        : check for needed perl modules
+pscheckperl -build : build and install external modules
+\end{verbatim}
+
+\subsection{jhbuild}
+
+JH uses \code{jhbuild} even though the 'jh' in \code{jhbuild} doesn't
+really refer to him.
+
+\subsubsubsection{What is it?}
+
+According to the introduction on the \code{jhbuild} website:
+
+\begin{quote}
+\code{jhbuild} is a program that can be used to pull a number of
+modules from CVS and build them in the correct order. Unlike some
+build scripts, \code{jhbuild} lets you specify what modules you want
+built and it will then go and build those modules plus dependencies.
+
+Although \code{jhbuild} was originally developed to build
+\code{[WWW]Gnome}, it is now able to build a number of the modules in
+\code{freedesktop.org} CVS. Extending it to handle new modules is
+usually trivial (assuming the build infrastructure matches the other
+modules it handles).
+\end{quote}
+
+In additional to retrieving source code from various SCM's (CVS, SVN,
+arch, etc.), jhbuild has the ability to download tarballs via HTTP or
+FTP.
+
+\code{jhbuild} has been adopted as an official \code{freedesktop.org}
+project. You can find more information on the project's homepage
+(\code{http://www.freedesktop.org/Software/jhbuild}). Bugs can be
+filed in the Gnome Bugzilla (\code{http://bugzilla.gnome.org}).
+
+\subsubsubsection{Where to get it}
+
+It was necessary to slightly modify \code{jhbuild} for use with IPP
+software. Therefore, you must checkout the \code{jhbuild} module from
+the Pan-STARRS CVS tree. Please see the Pan-STARRS CVS Guide for help
+on setting up and using CVS. \code{jhbuild} will need to be able to
+find it's own source tree even after installation so you should choose
+a checkout path that can be permanent. Something along the lines of
+\code{$HOME/src} is recommended.
+
+\begin{verbatim}
+cd
+mkdir -p src
+cd src
+cvs co jhbuild
+\end{verbatim}
+
+After running CVS you should see something like this:
+
+\begin{verbatim}
+$ cvs co jhbuild
+cvs checkout: Updating jhbuild
+U jhbuild/.cvsignore
+U jhbuild/COPYING
+U jhbuild/ChangeLog
+U jhbuild/HACKING
+U jhbuild/Makefile
+U jhbuild/README
+U jhbuild/install-check.c
+.
+.
+\end{verbatim}
+
+\subsubsubsection{Installing jhbuild into your home directory}
+
+\code{jhbuild} should be installed locally under your home
+directory. This will require that you modify the \code{PATH}
+environment variable so that you can run jhbuild after it has been
+installed.
+
+\begin{verbatim}
+cd jhbuild
+make
+make install
+\end{verbatim}
+
+Which should look something like this:
+
+\begin{verbatim}
+$ make
+gcc -Wall -O2 -o install-check install-check.c
+Run "make install" to install.
+$ make install
+Creating /home/moanui/jhoblitt/bin/jhbuild
+Creating /home/moanui/jhoblitt/.gnome2/vfolders/applications/jhbuild.desktop
+install -m755 install-check /home/moanui/jhoblitt/bin/install-check
+install -m755 config.guess /home/moanui/jhoblitt/bin/config.guess
+\end{verbatim}
+
+That will install the \code{jhbuild} executable under
+\code{$HOME/bin}. You are responsible for including this path in your
+\code{PATH} environment variable. It is highly recommended that you
+add this to your \code{.bashrc} or equivalent shell login script.
+
+For the \code{bash} shell, place this line in your \code{.bashrc}:
+\begin{verbatim}
+export PATH=${HOME}/bin:${PATH}
+\end{verbatim}
+For the \code{tcsh} shell, place this line in your \code{.tschrc}:
+\begin{verbatim}
+setenv PATH ${HOME}/bin:${PATH}
+\end{verbatim}
+
+\subsubsubsection{Configuring jhbuild}
+
+\code{jhbuild} is configured via an rc file that lives at
+\code{${HOME}/.jhbuildrc}. Please note that this rc file is executed
+as Python code; be careful!
+
+Example \code{.jhbuildrc}, suitable for cut and paste:
+
+\begin{verbatim}
+# what profile to build?
+moduleset = 'http://pan-starrs.ifa.hawaii.edu/project/IPP/software/modulesets/ipp12.modules'
+
+# modules to build by default
+modules = [ 'pslib', 'psmodules' ]
+
+# where should working copies go?
+jhroot = os.environ['HOME'] + '/jhroot'
+
+# where should tarballs be kept?
+tarballdir = jhroot + '/src'
+
+# in what prefix should things be installed? (must be writable)
+target = os.popen('config.guess').read().rstrip()
+prefix = jhroot + '/' + target
+checkoutroot = prefix + '/build'
+
+# extra arguments to pass to the autogen.sh script?
+autogenargs = '--enable-maintainer-mode --disable-static'
+
+# use an alternative install program that preserves the
+# mtime on header files if they haven't changed.  Speeds
+# up rebuilds.
+os.environ['INSTALL'] = os.environ['HOME'] + '/bin/install-check'
+
+# don't try to use /usr/ucb/cc on Solaris
+import sys
+if sys.platform == 'sunos5':
+    os.environ['CC'] = 'gcc'
+\end{verbatim}
+
+\subsubsubsection{Running jhbuild}
+
+\code{Jhbuild} can be executed as \code{jhbuild build
+[modulename]}. Just \code{jhbuild} will build the packages specified
+in the \code{modules} variable from your rc file.
+\begin{verbatim}
+jhbuild
+\end{verbatim}
+or
+\begin{verbatim}
+jhbuild build pslib
+\end{verbatim}
+
+Run \code{jhbuild} list to get a list of the packages \code{jhbuild}
+knows how to build.
+
+\begin{verbatim}
+$ jhbuild list
+cfitsio
+gsl
+fftw
+libxml2
+mysql
+pslib
+psmodules
+\end{verbatim}
+
+\code{jhbuild} supports many other commands. Please see \code{jhbuild
+--help} for a complete list of options.
+
+\begin{verbatim}
+$ jhbuild --help
+usage: jhbuild [ -f config ] command [ options ... ]
+Build a set of CVS modules (such as GNOME).
+
+Global options:
+  -f, --file=CONFIG            use a non default configuration file
+  -m, --moduleset=URI          use a non default module set
+      --no-interact            do not prompt for input
+
+Commands:
+  gui                          build targets from a gui app
+  update                       update from cvs
+  updateone modules            update a fixed set of modules
+  build [ opts... ] [modules]  update and compile (the default)
+  buildone [ opts... ] modules build a single module
+  tinderbox [ opts... ]        build non-interactively with logging
+  run program [ args... ]      run a command in the build environment
+  shell                        start a shell in the build environment
+  sanitycheck                  check that required support tools exists
+  bootstrap                    build required support tools
+  list [ opts ... ] [modules]  list what modules would be built
+  dot [ modules ]              output a dot file of dependencies suitable
+                               for processing with graphviz
+  info modules...              prints information about modules
+
+Options valid for the build, buildone, tinderbox and update commands:
+  -s, --skip=MODULES           treat the given modules as up to date
+  -t, --start-at=MODULE        start building at the given module
+  -D date_spec                 set a sticky date when checking out modules
+
+Options valid for the build, buildone and tinderbox commands:
+  -a, --autogen                always run autogen.sh
+  -c, --clean                  run make clean before make
+  -n, --no-network             skip cvs update
+
+Options valid for the tinderbox command:
+  -o, --output=DIR             directory to save build logs in
+
+Options valid for the list command:
+  -r, --show-revision          show which revision will be built
+\end{verbatim}
+
+\subsubsubsection{Dependancies}
+
+\code{jhbuild} has a fairly minimal set of dependencies --- far less
+than what may be required to actually compile and install any
+packages. However, if your system can meet the base requirements,
+\code{jhbuild} should be able to bootstrap your build environment.
+\begin{itemize}
+\item A working C compiler (eg. \code{gcc})
+\item A working \code{libc} (eg. \code{glibc})
+\item Perl 5 with the \code{XML::Parser} module (needed by
+  \code{libtool})
+\item Python 2.?
+\item Either \code{wget} or \code{curl}
+\item GNU \code{M4} 1.4
+\item \code{tar}
+\item \code{gzip}
+\item \code{bzip2}
+\end{itemize}
+
+\subsubsubsection{Bootstrapping}
+
+\code{jhbuild} has a limited ability to install some of the necessary
+tools for maintaining software that configure its build environment
+with the GNU autotools.
+
+This step is probably required on OSX and Solaris. Your mileage will
+vary per Linux distribution but you can probably skip this step if
+your distribution is around RedHat 9 vintage or newer.
+
+\begin{verbatim}
+jhbuild bootstrap
+\end{verbatim}
+
+\code{jhbuild} will then will begin to build a series of packages.
+
+\subsubsubsection{Using the jhbuild enviroment}
+
+As you've already seen, \code{jhbuild} is capable of setting up an
+independent build environment under the (configurable) directory of
+your choice. In order to link non-\code{jhbuild} management software
+against this build environment a number of your shell's environment
+variable have to be modified. \code{jhbuild} is capable of doing this
+for you. The syntax for this is \code{jhbuild shell}, which as the
+syntax implies, spawns a new shell with the proper environment
+variables.
+
+This example demonstrates \code{jhbuild} setting up the dynamic
+linkers default search path for you.
+
+\begin{verbatim}
+$ echo $LD_LIBRARY_PATH
+
+$ jhbuild shell
+$ echo $LD_LIBRARY_PATH
+/home/moanui/jhoblitt/jhroot/i686-pc-linux-gnu/lib
+\end{verbatim}
+
+%$ --- Emacs needs this to balance out the dollar signs
+
+A fair number of other variables are also adjusted for you. Enough so
+that most (all?) \code{autoconf} configured software will be able to
+find it's dependencies.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Aliases}
+
+PAP puts the following in his \code{~/.tcshrc}:
+\begin{verbatim}
+setenv SWDIR $HOME/local/`$HOME/bin/config.guess`/
+if (! -d $SWDIR) mkdir --parents $SWDIR
+alias ./autogen.sh './autogen.sh --prefix=$SWDIR CFLAGS="-I$SWDIR/include/ -g" LDFLAGS=-L$SWDIR/lib/'
+alias   autogen.sh './autogen.sh --prefix=$SWDIR CFLAGS="-I$SWDIR/include/ -g" LDFLAGS=-L$SWDIR/lib/'
+alias ./configure  './configure  --prefix=$SWDIR CFLAGS="-I$SWDIR/include/ -g" LDFLAGS=-L$SWDIR/lib/'
+alias   configure  './configure  --prefix=$SWDIR CFLAGS="-I$SWDIR/include/ -g" LDFLAGS=-L$SWDIR/lib/'
+setenv PATH ${PATH}:$SWDIR/bin/
+setenv LD_LIBRARY_PATH $SWDIR/lib/:$SWDIR/lib/mysql:$LD_LIBRARY_PATH
+setenv MANPATH $SWDIR/man:$MANPATH
+setenv PKG_CONFIG_PATH $SWDIR/lib/pkgconfig/:$PKG_CONFIG_PATH
+\end{verbatim}
+
+%$ --- Emacs needs this to balance out the dollar signs
+
+Here, \code{config.guess} is the common GNU script for guessing the
+build system triplet (e.g., \code{i686-pc-linux-gnu}).
+
+There are a couple of notes:
+\begin{itemize}
+\item To compile a binary, simply do \code{./configure}, then
+  \code{make && make install}.
+\item \code{Ohana} doesn't like this setup, so you need to build it
+  with: \code{\./configure --prefix=$SWDIR}
+\item Perl modules can be installed: \code{./Build install
+  --prefix=$SWDIR}.
+\end{itemize}
+
+\subsection{Manual Perl Module Installation}
+
+Here we describe setting up the Perl dependencies followed by the
+IPP components.
+
+\subsubsection{Dependencies}
+\label{sec:installing-perl-dependencies}
+
+If you have access to the \code{root} account, installation as
+\code{root} is much easier.  If not, you will have to go through
+the more flaky installation as an unprivileged user.
+
+\subsubsubsection{Installation as root}
+
+Many of the Perl dependencies are available from the Comprehensive
+Perl Archive Network (CPAN) at www.cpan.org.  If you have root access
+on your target machines, they can be very simply retrieved, built and
+installed (replacing \code{MODULE_NAME} for each module):
+
+\begin{verbatim}
+> su -
+Password:
+> cpan
+[...]
+cpan> install MODULE_NAME
+[...]
+cpan> quit
+\end{verbatim}
+
+Follow the prompts.  It's usually safe to accept the default (simply
+hit enter) in response to most questions.
+
+If you get into trouble, try: \code{force install MODULE_NAME}.
+
+You can also try to use the \code{Bundle::PS} as described below if
+you're feeling adventurous.
+
+\subsubsubsection{Installation as unprivileged user}
+
+To install modules from CPAN with the \code{CPAN.pm} interface, you
+need to setup a CPAN configuration file in your home directory.  Then
+\code{CPAN.pm} can walk you through setting up the most important
+configuration values.  Unfortunately, there is some variation in the
+behavior of the various versions of \code{CPAN.pm} that have shipped
+with Perl.  Some (most) of these variants will not correctly create a
+configuration files that allows a non-\code{root} user to install
+modules outside of "system" paths.  In order to make sure that you get
+a "correct" CPAN configuration file you need to ``prime'' it with a
+few values.
+
+First you need to create the directory in which the CPAN configuration file will live.
+\begin{verbatim}
+> mkdir -p .cpan/CPAN/
+\end{verbatim}
+
+Then we need to create a partial configuration file.  Note that this example
+assumes that you want to install your perl modules under
+\code{$HOME/local/lib/perl5}.
+
+\begin{verbatim}
+> echo "\$CPAN::Config = {" >> .cpan/CPAN/MyConfig.pm
+> echo "  makepl_arg => q[PREFIX=$HOME/local/]," >> .cpan/CPAN/MyConfig.pm
+> echo "  mbuildpl_arg => q[--install_base $HOME/local/]," >> .cpan/CPAN/MyConfig.pm
+> echo "};" >> .cpan/CPAN/MyConfig.pm
+> echo "1;" >> .cpan/CPAN/MyConfig.pm
+> echo "__END__" >> .cpan/CPAN/MyConfig.pm
+\end{verbatim}
+
+%$ --- Emacs needs this to balance the previous dollar sign
+
+Now you need to invoke \code{CPAN.pm} so it can walk you through
+configuring the rest of the required values.  This is an example of
+one possible configuration with \code{CPAN.pm} version 1.8802.
+\textbf{Your version of CPAN.pm may present you with different
+prompts.}  Use your common sense.  If in doubt, it is generally safe
+to simply hit enter (and accept the default).
+
+\begin{verbatim}
+> perl -MCPAN -e shell
+CPAN: File::HomeDir loaded ok
+Sorry, we have to rerun the configuration dialog for CPAN.pm due to
+the following indispensable but missing parameters:
+
+build_cache, build_dir, cache_metadata, cpan_home, ftp_proxy, http_proxy,
+index_expire, inhibit_startup_message, keep_source_where, make_arg,
+make_install_arg, mbuild_arg, mbuild_install_arg, mbuild_install_build_command,
+no_proxy, prerequisites_policy, scan_cache, urllist
+
+
+The following questions are intended to help you with the
+configuration. The CPAN module needs a directory of its own to cache
+important index files and maybe keep a temporary mirror of CPAN files.
+This may be a site-wide directory or a personal directory.
+
+
+
+I see you already have a  directory
+    /home/moanui/jhoblitt/.cpan
+Shall we use it as the general CPAN build and cache directory?
+
+CPAN build and cache directory? [/home/moanui/jhoblitt/.cpan]
+
+
+Unless you are accessing the CPAN via the filesystem directly CPAN.pm
+needs to keep the source files it downloads somewhere. Please supply a
+directory where the downloaded files are to be kept. [/home/moanui/jhoblitt/.cpan/sources]
+Directory where the build process takes place? [/home/moanui/jhoblitt/.cpan/build]
+
+
+How big should the disk cache be for keeping the build directories
+with all the intermediate files?
+
+Cache size for build directory (in MB)? [100]
+
+
+The CPAN indexes are usually rebuilt once or twice per hour, but the
+typical CPAN mirror mirrors only once or twice per day. Depending on
+the quality of your mirror and your desire to be on the bleeding edge,
+you may want to set the following value to more or less than one day
+(which is the default). It determines after how many days CPAN.pm
+downloads new indexes.
+
+Let the index expire after how many days? [1]
+
+
+By default, each time the CPAN module is started, cache scanning is
+performed to keep the cache size in sync. To prevent this, answer
+'never'.
+
+Perform cache scanning (atstart or never)? [atstart]
+
+
+To considerably speed up the initial CPAN shell startup, it is
+possible to use Storable to create a cache of metadata. If Storable
+is not available, the normal index mechanism will be used.
+
+Cache metadata (yes/no)? [yes]
+
+
+The CPAN module can detect when a module which you are trying to build
+depends on prerequisites. If this happens, it can build the
+prerequisites for you automatically ('follow'), ask you for
+confirmation ('ask'), or just ignore them ('ignore'). Please set your
+policy to one of the three values.
+
+Policy on building prerequisites (follow, ask or ignore)? [ask] follow
+
+
+Every Makefile.PL is run by perl in a separate process. Likewise we
+run 'make' and 'make install' in separate processes. If you have
+any parameters (e.g. PREFIX, LIB, UNINST or the like) you want to
+pass to the calls, please specify them here.
+
+If you don't understand this question, just press ENTER.
+Parameters for the 'make' command?
+Typical frequently used setting:
+
+    -j3              # dual processor system
+
+Your choice:  []
+Parameters for the 'make install' command?
+Typical frequently used setting:
+
+    UNINST=1         # to always uninstall potentially conflicting files
+
+Your choice:  [] UNINST=1
+
+
+The next questions deal with Module::Build support.
+
+A Build.PL is run by perl in a separate process. Likewise we run
+'./Build' and './Build install' in separate processes. If you have any
+parameters you want to pass to the calls, please specify them here.
+
+Parameters for the './Build' command?
+Setting might be:
+
+    --extra_linker_flags -L/usr/foo/lib  # non-standard library location
+
+Your choice:  []
+Do you want to use a different command for './Build install'?
+Sudo users will probably prefer:
+
+    su root -c ./Build
+or
+    sudo ./Build
+or
+    /path1/to/sudo -u admin_account ./Build
+
+or some such. Your choice:  [./Build]
+Parameters for the './Build install' command?
+Typical frequently used setting:
+
+    --uninst 1                           # uninstall conflicting files
+
+Your choice:  [] --uninst 1
+
+
+If you're accessing the net via proxies, you can specify them in the
+CPAN configuration or via environment variables. The variable in
+the $CPAN::Config takes precedence.
+
+Your ftp_proxy? []
+Your http_proxy? []
+Your no_proxy? []
+You have no /home/moanui/jhoblitt/.cpan/sources/MIRRORED.BY
+  I'm trying to fetch one
+CPAN: LWP::UserAgent loaded ok
+Fetching with LWP:
+  http://www.perl.org/CPAN/MIRRORED.BY
+
+
+Now we need to know where your favorite CPAN sites are located. Push
+a few sites onto the array (just in case the first on the array won't
+work). If you are mirroring CPAN to your local workstation, specify a
+file: URL.
+
+First, pick a nearby continent and country by typing in the number(s)
+in front of the item(s) you want to select. You can pick several of
+each, separated by spaces. Then, you will be presented with a list of
+URLs of CPAN mirrors in the countries you selected, along with
+previously selected URLs. Select some of those URLs, or just keep the
+old list. Finally, you will be prompted for any extra URLs -- file:,
+ftp:, or http: -- that host a CPAN mirror.
+
+(1) Africa
+(2) Asia
+(3) Central America
+(4) Europe
+(5) North America
+(6) Oceania
+(7) South America
+Select your continent (or several nearby continents) [] 5
+
+(1) Bahamas
+(2) Canada
+(3) Mexico
+(4) United States
+Select your country (or several nearby countries) [] 4
+
+(1) ftp://carroll.cac.psu.edu/pub/CPAN/
+(2) ftp://cpan-du.viaverio.com/pub/CPAN/
+(3) ftp://cpan-sj.viaverio.com/pub/CPAN/
+(4) ftp://cpan.calvin.edu/pub/CPAN
+(5) ftp://cpan.cs.utah.edu/pub/CPAN/
+(6) ftp://cpan.cse.msu.edu/
+(7) ftp://cpan.erlbaum.net/CPAN/
+(8) ftp://cpan.glines.org/pub/CPAN/
+(9) ftp://cpan.hostrack.net/pub/CPAN
+(10) ftp://cpan.llarian.net/pub/CPAN/
+(11) ftp://cpan.mirrors.redwire.net/pub/CPAN/
+(12) ftp://cpan.mirrors.tds.net/pub/CPAN
+(13) ftp://cpan.netnitco.net/pub/mirrors/CPAN/
+(14) ftp://cpan.pair.com/pub/CPAN/
+(15) ftp://cpan.teleglobe.net/pub/CPAN
+(16) ftp://cpan.uchicago.edu/pub/CPAN/
+40 more items, hit RETURN to show them
+Select as many URLs as you like (by number),
+put them on one line, separated by blanks, hyphenated ranges allowed
+ e.g. '1 4 5' or '7 1-4 8' [] 14 11 12
+
+Enter another URL or RETURN to quit: []
+New set of picks:
+  ftp://cpan.pair.com/pub/CPAN/
+  ftp://cpan.mirrors.redwire.net/pub/CPAN/
+  ftp://cpan.mirrors.tds.net/pub/CPAN
+
+
+Please remember to call 'o conf commit' to make the config permanent!
+
+
+cpan shell -- CPAN exploration and modules installation (v1.8802)
+ReadLine support enabled
+
+ cpan[1]> o conf commit
+commit: wrote '/home/moanui/jhoblitt/.cpan/CPAN/MyConfig.pm'
+\end{verbatim}
+
+%$ --- Emacs needs this to balance the previous dollar sign
+
+Now we need to install the module that installs the other modules.
+
+\begin{verbatim}
+cpan> install Module::Build
+\end{verbatim}
+
+Exit out of cpan:
+
+\begin{verbatim}
+cpan> exit
+\end{verbatim}
+
+In order to use of the installed modules, we need to setup an
+environment variable called \code{PERL5LIB} so that 'perl' can find
+them.  To do this, we need to know where under 'perl5' our modules
+were actually installed.  This will set variable with the version of
+Perl that you are using.  The easiest way to do this is just just look
+in the root of the path where we did the install.
+
+\begin{verbatim}
+> ls local/lib/perl5/
+5.8.8  site_perl
+\end{verbatim}
+
+That means we're using perl 5.8.8 and \code{PERL5LIB} needs to be
+setup as following:
+\begin{verbatim}
+export PERL5LIB=$HOME/local/lib/perl5/5.8.8:$HOME/local/lib/perl5/site_perl/5.8.8
+\end{verbatim}
+
+Now we should install the basic compliment of helper modules that
+\code{CPAN.pm} needs to function fully.  Go back into CPAN (\code{perl
+-MCPAN -e shell}) and:
+
+\begin{verbatim}
+cpan> install Bundle::CPAN
+\end{verbatim}
+
+You can quit out of the CPAN shell at this point with the `exit`
+command or do the following few steps in another shell We're ready to
+install the full set Perl module dependencies for IPP software.  In
+order to make this process a bit easier on the end user a "Bundle"
+module has been created.  In order to use it you need to create a
+directory (if it doesn't already exist) called Bundle under your .cpan
+directory.
+
+\begin{verbatim}
+> mkdir -p .cpan/Bundle
+\end{verbatim}
+
+The file \code{PS.pm} should copied into this directory:
+
+\begin{verbatim}
+cp /path/to/PS.pm .cpan/Bundle/
+\end{verbatim}
+
+Back in the CPAN shell, 'force' the install of the PS Bundle.  The
+'force' keyword instructs the shell to ignore any tests failures.
+This is necessary as some of the modules 'DBD::mysql'/etc. require a
+properly working database setup in order for the tests to pass.  You
+will most likely be prompted for input by several the modules.  It is
+safe to answer with a carriage return to all questions.  If it insists
+on a path to \code{httpd}, hit \code{CTRL-C} and it will go on to the
+next step.
+
+\begin{verbatim}
+cpan> force install Bundle:PS
+\end{verbatim}
+
+For further instructions on installing Perl modules from CPAN ''by
+hand', see:
+\begin{verbatim}
+http://www.cs.ucsc.edu/~you/notes/perl-module-install.html
+\end{verbatim}
+
+After the dependencies (\S\ref{sec:dependencies}) have been satisfied,
+the IPP packages should be installed in the following order:
+\begin{itemize}
+\item \code{Ohana}
+\item \code{psLib}
+\item \code{psModules}
+\item \code{psphot}
+\item \code{psastro}
+\item \code{ppStats}
+\item \code{ppImage}
+\item \code{ppMerge}
+\item \code{ppNorm}
+\item \code{pois}
+\item \code{stac}
+\item \code{pswarp}
+\item \code{ppStac}
+\item \code{ippdb}
+\item \code{ippTools}
+\item \code{PS-IPP-Metadata-Config}
+\item \code{PS-IPP-Metadata}
+\item \code{ippScripts}
+\item \code{ippTasks}
+\item \code{config}
+\end{itemize}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsubsection{Modules and scripts}
+
+Pan-STARRS Perl modules and scripts are installed by executing the
+following in the directory containing the source:
+\begin{verbatim}
+> perl Build.PL
+> ./Build
+> ./Build install --prefix=/path/to/install/
+\end{verbatim}
+
+Make sure you set your \code{PERL5LIB} environment variable to the
+installation path (here, \code{/path/to/install/}).
+
+\section{System Requirements}
+
+From the start, we have endeavoured to have the IPP to be flexible so
+that it can be deployed on a range of systems.  With that in mind,
+however, we have specifically targeted Linux systems, stressing POSIX
+compliance.
+
+\subsection{Hardware}
+
+We have developed and tested on machines with different hardware
+architectures.  Reports of successes and failures in deploying the IPP
+on an untested hardware architecture are welcome.
+
+We have had thorough testing on x86 and amd64 architectures, with
+Gentoo and Red Hat Linux.
+
+We expect that the binaries will compile under MacOS (if there are
+problems please let us know).  We have had trouble compiling psLib
+under Solaris.
+
+\subsection{Dependencies}
+\label{sec:dependencies}
+
+We have dependencies on two levels: library dependencies (for the
+binaries) and Perl module dependencies.
+
+\subsubsubsection{Libraries}
+
+\begin{figure}
+\psfig{file=ipp.ps,height=8in,angle=0}
+\caption{Dependency chart for the IPP binaries}
+\label{fig:ipp-dependencies}
+\end{figure}
+
+The following external libraries are required to build \code{psLib}:
+\begin{itemize}
+\item \code{gsl} --- \code{http://www.gnu.org/software/gsl/}
+\item \code{fftw} --- \code{http://www.fftw.org}
+\item \code{mysql} --- \code{http://www.mysql.org}
+\item \code{cfitsio} ---
+  \code{http://heasarc.gsfc.nasa.gov/docs/software/fitsio/fitsio.html}
+\item \code{libjpeg} --- \code{http://www.ijg.org}
+\item \code{openssl} --- \code{http://www.openssl.org}
+\item \code{doxygen} (optional: for generating documentation) ---
+  \code{http://www.stack.nl/~dimitri/doxygen/}
+\end{itemize}
+
+In addition to the above, \code{libpng} (\code{http://www.libpng.org})
+is required to build the \code{Ohana} package.
+
+
+\subsubsubsection{Perl modules}
+
+\begin{figure}
+\psfig{file=perl.ps,height=8in,angle=0}
+\caption{Dependency chart for the IPP Perl components}
+\label{fig:perl-dependencies}
+\end{figure}
+
+The following external modules, available from CPAN (see
+\S\ref{sec:installing-perl-dependencies}), are required to build the
+Perl scripts:
+\begin{itemize}
+\item \code{Module::Build}
+\item \code{ExtUtils::MakeMaker}
+\item \code{Params::Validate}
+\item \code{DateTime::TimeZone}
+\item \code{DateTime::Locale}
+\item \code{Time::Local}
+\item \code{DateTime}
+\item \code{MIME::Base64}
+\item \code{IO::Compress::Base}
+\item \code{Compress::Raw::Zlib}
+\item \code{Class::Factory::Util}
+\item \code{DateTime::Format::Strptime}
+\item \code{Net::Domain::TLD}
+\item \code{Sub::Uplevel}
+\item \code{HTML::Tagset}
+\item \code{Digest}
+\item \code{IO::Compress::Zlib}
+\item \code{version}
+\item \code{Text::Balanced}
+\item \code{DateTime::Format::Builder}
+\item \code{ExtUtils::Manifest}
+\item \code{URI}
+\item \code{Data::Validate::Domain}
+\item \code{Test::Exception}
+\item \code{Tree::DAG_Node}
+\item \code{Array::Compare}
+\item \code{HTML::Parser}
+\item \code{Digest::MD5}
+\item \code{Net::FTP}
+\item \code{Compress::Zlib}
+\item \code{Locale::Maketext::Simple}
+\item \code{Parse::RecDescent}
+\item \code{Class::Accessor}
+\item \code{DateTime::Format::ISO8601}
+\item \code{CGI}
+\item \code{Test::Cmd}
+\item \code{Net::HTTPServer}
+\item \code{Digest::MD5::File}
+\item \code{File::Temp}
+\item \code{Data::Validate::URI}
+\item \code{Test::Warn}
+\item \code{YAML}
+\item \code{LWP}
+\item \code{Module::Load}
+\item \code{Params::Check}
+\item \code{Template}
+\item \code{Statistics::Descriptive}
+\item \code{Storable}
+\item \code{IO::String}
+\item \code{Date::Parse}
+\item \code{Digest::SHA1}
+\item \code{DB_File}
+\item \code{File::NFSLock}
+\item \code{Heap}
+\item \code{Module::Load::Conditional}
+\item \code{IPC::Run}
+\item \code{Cache}
+\item \code{IPC::Cmd}
+\end{itemize}
+
+\subsection{Binaries}
+
+Installation of the binaries is complicated by the fact that they may
+be used on multiple architectures.  The three developers based at
+Pan-STARRS HQ each use a different method for configuring the
+environment and installing the binaries to deal with this problem.  We
+describe each of these below.  Choose one that works for you!
+
+\section{Trouble Shooting Build Issues}
+
+\subsection{missing libX11.so}
+
+RedHat RHEL 3 \& 4 (likely other RedHat variants as well) for \emph{amd64} seem
+to often be installed without a \code{libX11.so} under
+\code{/usr/X11R6/lib64/}.  This will cause 64bit builds trying to link against
+libX11 (\code{-lX11}) to fail.
+
+If you have root access you can fix this by adding the missing symlink
+yourself.  Eg..
+
+\begin{verbatim}
+su -
+cd /usr/X116/lib64
+ln -s libX11.so.6.2 libX11.so
+\end{verbatim}
+
+or with \code{sudo}:
+
+\begin{verbatim}
+cd /usr/X116/lib64
+sudo ln -s libX11.so.6.2 libX11.so
+\end{verbatim}
+
+
+For users without root access to thier system ,a symlink needs to be installed
+under \code{\$prefix/lib}, where \code{\$prefix} is the path underwhich your
+installing IPP software.  Note that \code{\$prefix/lib} will also need to be manually added the enviroment variable \code{LD_LIBRARY_PATH} if your not using jhbuild. E.g for ksh/bash users:
+
+\begin{verbatim}
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/[foo]/[yourinstallprefix]/lib
+\end{verbatim}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+                                                                                
+\clearpage
+\appendix
+ 
+\bibliographystyle{plain}
+\bibliography{panstarrs}
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/misc/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.log *.dvi *.aux *.toc *.log *.out *.lof *.tbd *.tbr *.pdf
Index: /tags/sj_tags/sj_root_20080929/doc/misc/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/Makefile	(revision 22322)
@@ -0,0 +1,25 @@
+# $Id: Makefile,v 1.5 2006-01-16 18:58:45 eugene Exp $
+
+PDFLATEX = env TEXINPUTS=../../latex/inputs:$(TEXINPUTS):.: pdflatex
+PSLATEX  = env TEXINPUTS=../../latex/inputs:$(TEXINPUTS):.: latex
+
+all : codeConventions.pdf perlCodeConventions.pdf
+
+%.pdf: %.tex
+	$(PSLATEX) $*.tex 
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	thumbpdf --modes=dvips $*.pdf
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	@rm -f $*.ps $*.dvi $*.aux $*.log $*.tbr $*.tbd $*.tpm $*.lof $*.toc head.tmp body.tmp
+
+clean :
+	$(RM) *.log *.dvi *.aux *.toc *.tbd *.tbr *.tpm *.lof *.out *~ core head.tmp body.tmp
+
+dist : clean
+		$(RM) *.pdf
+
+empty : clean
Index: /tags/sj_tags/sj_root_20080929/doc/misc/Objects.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/Objects.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/Objects.tex	(revision 22322)
@@ -0,0 +1,270 @@
+\documentclass[panstarrs,spec]{panstarrs}
+
+\title{PSPhot} % put in your title
+\subtitle{A Strategy for Astronomical Object Detection, Classification, and Measurement}
+\author{Eugene Magnier}
+\audience{Pan-STARRS Science}
+\shorttitle{PSPhot}
+\group{Pan-STARRS IPP}
+\project{Pan-STARRS IPP}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-xxx-xxx}
+
+\begin{document}
+\maketitle
+
+\section{Object Detection}
+
+Object detection consists of smoothing the image with a kernel
+approximately equivalent to the PSF and detecting the significant
+peaks above a threshold.  The detection efficiency is only
+significantly affected by the accuracy of the PSF model in this stage
+at the faint end of the detection process.  For brighter sources, a
+very rough approximation will do.  I have implemented this step by
+smoothing with a 1D gaussian first in the X direction then in the Y
+direction.  With a gaussian with $\sigma = 1.0$ pixels, and extending
+only to $2 \sigma$ from the center, this process takes roughly 1.5
+seconds on {\em alala} for a Megacam CCD, equivalent to roughly 2.8
+seconds for a full Gigapixel camera 4k x 4k chip.  
+
+My peak-finding routine runs first along each row and finds all peaks
+above a requested threshold in the row data.  It then examines each
+row peak and eliminates any peaks which are not local peaks when all 8
+neighbor pixels are considered.  If a peak has any neighbors which are
+at the same flux level, it is kept.  This last point is a weakness of
+the algorithm as it leaves behind multiple entries for saturated
+regions, and it does not attempt to exclude double peaks from
+individual stars (typical for slightly defocussed images).  The peak
+finding and cleaning step takes a total of roughly 25 msec per
+resulting output peak.  This analysis should scale like the number of
+peaks for large numbers of peaks or like the number of pixels for
+sparse images with few peaks.  
+
+\section{Object Classification}
+
+After objects have been detected by finding their peaks, some basic
+classification should be performed.  This will allow us to identify a
+subset of objects which are likely to be stars, those which are likely
+to be cosmic rays and defects, those which are likely to be galaxies,
+and those which are likely to be saturated regions.  This
+classification can be acheived on the basis of simple object moments
+measured for a small aperture centered on the object.  My
+implementation of the object moment analysis uses a circular aperture
+(of 5 pixels) centered on the peak and an external annulus (of 15 - 25
+pixels) to measure the 'sky' or background level.  Alternatively, the
+sky could be measured once for the entire image in large (eg, 64x64
+pixel) boxes.  This latter choice is faster only if the image contains
+a large number of objects.  With 1256 pixels in the sky annulus, the
+break-even point between these two options is for $\sim 7300$ objects
+for a megacam image or $\sim 13000$ objects for a full GPC image,
+given the same aperture sizes.  Note also that the GPC images from
+Pan-STARRS are likely to have much smaller PSF sizes compared with
+Megacam (in terms of pixels: typical FWHM is expected to be $\sim$ 2.5
+vs 3.8 pixel for Megacam).  
+
+Objects can be well-classified in an initial pass simply on the basis
+of their moments and other simple statistics.  In this pass, the goal
+is to identify a collection of likely stars which can be used to
+measure the image PSF model.  This requirement suggests several
+natural filters: we should select objects for which the total number
+of counts and sky signal imply a signal-to-noise ratio above some
+generous threshold.  For my analysis, I have used $S/N > 50$.  We
+should also reject objects with saturated pixels: those with peak
+pixel values above the saturation limit.  I rejected all objects with
+peak flux $> 60000$.  We should also avoid objects which are too close
+to the edges.  Most significantly, we should select objects which have
+second moments which suggest they are stellar.  Since we assume that
+the stellar objects have roughly consistent shape (ie, PSF variations
+are less than some limit), then the stellar objects should all fall
+within a narrow region in the second-moment space.  In my analysis, I
+construct a grid of $\sigma_x, \sigma_y$ (second moments in the x and
+y directions) and count the number of objects in each grid box.  The
+peak box is assumed to represent the stellar objects.  I then measure
+the scatter of both $\sigma_x, \sigma_y$ for all objects within a
+certain range of the peak value (0.2, 0.2 pixels).  I then select only
+objects which like within $\pm 2\sigma$ of the median of this
+collection.  The resulting collection of objects is a very likely
+sample of stellar objects, which are good candidates from which to
+measure the image PSF model.
+
+We can also roughly classify the other objects based on this
+second-moment distribution space.  Objects with $\sigma_x <<
+\bar{\sigma_x}$ or $\sigma_y << \bar{\sigma_y}$ are likely to be
+cosmic-rays or other instrumental defects.  Objects with $\sigma_x >>
+\bar{\sigma_x}$ or $\sigma_y >> \bar{\sigma_y}$ are likely to be
+galaxies.  Other object types are difficult to distinguish from the
+simple moments, and must be tested on a case-by-case basis.
+
+\section{Object Measurements}
+
+We have several goals in measuring the objects in the image.  We would
+like to improve our determination of the object type.  Several basic
+object types may be identified: 1) objects which are well-represented
+with a stellar PSF, 2) objects which are sub-PSF in some dimension and
+thus must be a defect, 3) objects which are well-represented with one
+or more galaxy flux models, 4) objects which represent other
+recognizable artifacts such as highly saturated stars, bleeding
+pcolumns, satelite trails, etc.
+
+For stellar objects, we need to measure the object photometry and its
+astrometry.  An important trade-off to be made here is between
+aperture photometry and PSF modeling.  Aperture photometry has the
+advantage of exactly adding up the light from the star of interest. In
+principal, if the field is empty of other stars, and a large enough
+aperture is used, and the signal-to-noise in the star is very high,
+aperture photometry will be limited only by the photon statistics of
+the star in question.  In practice, several factors of varying
+importance break this ideal: There are always neighboring objects
+which contaminate the measurement.  For faint objects, the aperture
+must be small to maximize the signal-to-noise contribution relative to
+the sky, entailing aperture corrections.  PSF modeling is less
+sensitive to the presence of neighbors (they may be naturally
+excluded) or the choice of sky aperture.  It is also more robust for
+the photometry of faint objects.  It has several disadvantages as
+well: the PSF model is never a perfect representation of the PSF,
+entailing aperture corrections.  The more accurately the PSF model
+represents the actual stellar profile, in general, the more
+time-consuming will be the fitting process.  
+
+I have investigated several representations of the PSF as an
+analytical function.  A traditional technique is to fit the objects
+with a 2-D Gaussian function:
+\[
+PSF(x,y) = Z_{\rm pk} e^{-z} + Sky
+\]
+where
+\[
+z = \frac{x^2}{2\sigma_x^2} + \frac{y^2}{2\sigma_y^2} + x y \sigma_{xy}
+\]
+where $x = X - X_o$ and $y = y - Y_o$.  The old-standby program,
+DoPhot, uses, as a default representation, a polynomial approximation
+to the Gaussian by defining:
+\[
+PSF(x,y) = Z_{\rm pk} (1 + z + z^2/2 + z^3/6)^{-1} + Sky
+\]
+with all other definitions the same as above.  This function may be
+modified by adding coefficients to the second and third order $z$
+terms to produce a function which is more or less 'wingy' as needed:
+\[
+PSF(x,y) = Z_{\rm pk} (1 + z + \beta_1 z^2/2 + \beta_2 z^3/6)^{-1} + Sky
+\]
+Several points may be noted.  First, the shift from a pure Gaussian to
+the polynomial expansion is motivated by processing speed as well as
+by the added ability to modify the shape with $\beta_1$ and $\beta_2$.
+Care must be taken, however, if the full generalization is used: there
+is a strong degeneracy between $\beta_1$ and $\beta_2$ which can drive
+the fitting routine into bad regimes.  It is usually safer to re-write
+the function as:
+\[
+PSF(x,y) = Z_{\rm pk} (1 + z + \beta_1 z^2/2 (1 + \beta_2 z/3))^{-1} + Sky
+\]
+in which case the fitting process is stabilized.  
+
+In practice, these Gaussian and pseudo-Gaussian functions do a poor
+job of representing real astronomical objects.  One useful measure of
+how well the objects are fitted by the function is the difference
+between the photometry of the fitted function and the equivalent
+aperture.  Both the amplitude of the residual (expressed as a
+magnitude) and the scatter of the residual for an image are useful
+indicators of the accuracy of the fit, and therefore the intrinsic
+photometry.  For each image, it is necessary to measure the amplitude
+of the residual in order to correct the fit photometry to a consistent
+absolute photometry system which is consistent from image to image.
+The error on this aperture correction, as represented by the scatter,
+provides a limit to the photometry which may be achieved from the
+fitted function.  The pseudo-Gaussians typically have aperture
+corrections in range of 3-6\%, with internal scatter in the vicinity
+of 1-1.5\%.  Careful selection of the stars used to measure the
+aperture residuals may reduce the amplitude of the scatter.  The
+reason for the large residuals is quite clear when one examines actual
+stellar profiles.  The stellar profiles are typically a single power
+law slope modified by a central core.  The Gaussian has an exponential
+fall-off while the pseudo-Gaussians have multiple combined power
+laws.  
+
+In addition to only poorly representing the PSF, these simple
+pseudo-Gaussians fall down because real stellar profiles frequently
+have additional structure.  In particular, it is typical that the
+inner core of the light profile, which is dominated by the effects of
+seeing is elongated in a different way (different ellipticity,
+different orientation) from the outer wings, which are dominated by
+the effects of scattering from dust particles on the mirror or in the
+atmosphere.  In order to represent these two types of structures, it
+is necessary to have two independent radial profiles.  I have
+experimented with several representations, and have found the
+following functional form to represent real images well and to be
+generally stable:
+\[
+PSF(x,y) = Z_{\rm pk} (1 + z_1^N + z_2^M)^{-1} + Sky
+\]
+where
+\[
+z_1 = \frac{x^2}{2\sigma_{x1}^2} + \frac{y^2}{2\sigma_{y1}^2} + x y \sigma_{xy1}
+z_2 = \frac{x^2}{2\sigma_{x2}^2} + \frac{y^2}{2\sigma_{y2}^2} + x y \sigma_{xy2}
+\]
+and the values of $N$ and $M$ depend on the details of the images.
+These could potentially be fitted as well with the rest of the terms,
+but such a fitting process is fairly unstable.  In a collection of
+Megacam images, I have found the values of $N = 1$ and $M = 2.25$ to
+work quite well.  Perhaps the value of $M$ could be determined for
+each image by fitting the 1-D function of $\log (f) vs \log(r)$.  
+
+Experiments with 40 Megacam images shows that the value of the
+aperture residual is much smaller and more consistent with the
+2-component model above: GIVE dAp, Sigma dAp values.
+
+Problems with the above function: UNSTABLE for some images:
+
+Poor images (bad guiding, bad image quality -> unstable fit!)
+
+Aperture Photometry vs PSF Photometry
+
+PSF Models
+
+Speed vs Detail
+
+PSF Model    Typical Execution Time (msec / object)
+Pgauss       1.5
+pgauss       2.3
+Sgauss       3.2
+sgauss       5.4
+Moments      0.2
+
+These numbers are based on 900 image pixels for the models, along with
+a 5 pixel object radius and a 15-25 pixel sky annulus for moments.
+The size of the analysis box should be large enough that the stellar
+contribution falls well below the sky noise level at the outer
+regions.  For some of the brighter objects (peak $>$ 10000 counts),
+this choice may be somewhat small.  For objects with peak less than
+1000 counts, the analysis box can probably be somewhat reduced.  In
+addition, the actual number of pixels used in the analysis can be
+reasonably reduced by a significant factor (2-4) by binning together
+pixels at large radii.  The slope of the stellar flux profile at large
+radii is small, so binning together pixels can increase the
+signal-to-noise per-pixel and speed up the analysis without
+significantly diminishing the accuracy of the measurement.
+
+Stellar Densities
+
+Galaxy Models
+
+Names:
+
+Peak, Source, Object, Star, Galaxy, PSF 
+
+Peak: a single pixel higher than its 8 neighbors
+
+Source: 
+
+Models:
+ real Gaussian:   f(z) = Zo exp (-z) + So
+ pseudo Gaussian: f(z) = Zo (1 + z + (1/2) z^2 + (1/6) z^3)^(-1)
+ Waussian (?):    f(z) = Zo (1 + z + B_1 (1/2) z^2 (1 + B_2 (1/3) z))^(-1)
+ Core Powerlaw:   f(z) = Zo (1 + z_1 + z_2^n)^(-1)
+ moffat func:
+ sersic           f(z) = Zo exp (-z^n) (n integer? n/2 integer?)
+ sersic w/ core   f(z) = Zb (1 + z + (1/2) z^2 + (1/6) z^3)^(-1) + Zd exp (-A z^n)
+ (n = 1/2 : exponential disk; n = 1/8 : deVau)
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/misc/PSDC-notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/PSDC-notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/PSDC-notes.txt	(revision 22322)
@@ -0,0 +1,7 @@
+
+steps needed to add / update IPP document on web:
+
+- update version number to current version
+- compile new copy
+- copy generic name to PSDC/PSDC-NNN-MMM-VV.pdf name
+- tag cvs file with release version (ie SDRS-03)
Index: /tags/sj_tags/sj_root_20080929/doc/misc/astyle-options.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/astyle-options.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/astyle-options.txt	(revision 22322)
@@ -0,0 +1,8 @@
+
+-min-conditional-indent=0
+-max-instatement-indent=20
+-brackets=break
+-pad=oper
+-indent-labels
+-indent-switches
+
Index: /tags/sj_tags/sj_root_20080929/doc/misc/codeConventions.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/codeConventions.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/codeConventions.tex	(revision 22322)
@@ -0,0 +1,1495 @@
+%%% $Id: codeConventions.tex,v 1.18 2004-10-29 22:00:27 eugene Exp $
+\documentclass[panstarrs]{panstarrs}
+
+% basic document variables
+\title{Pan-STARRS Image Processing Pipeline}
+\subtitle{C Code Standards}
+\shorttitle{C Code Standards}
+\author{Robert Lupton}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{01}
+\docnumber{PSDC-430-004}
+
+\begin{document}
+\maketitle
+\newcommand{\matchBracket}[1]{}         % help emacs match properly
+
+% -- Revision History --
+\RevisionsStart
+% version     Date         Description
+DR.00 & 2004/03/01 & First draft \\
+00 & 2004/04/23 & First release \\
+01 & 2004/10/28 & Title Changed \\
+\RevisionsEnd
+
+\textbf{\Large Referenced Documents}
+\begin{center}
+\begin{tabular}{|p{1in}|p{2.75in}|p{2.75in}|}
+\hline
+\textbf{\PS{} ID} & \textbf{Title} & \textbf{Author} \\
+\hline
+%%% References here.
+\hline
+\end{tabular}
+\end{center}
+
+\pagebreak
+\tableofcontents
+
+\pagebreak 
+\pagenumbering{arabic}
+
+\section{Introduction}
+
+\subsection{Why Have Code Conventions}
+
+Code conventions are important to programmers for a number of reasons:
+\begin{itemize}
+
+    \item 80\% of the lifetime cost of a piece of software goes to
+    maintenance.
+
+    \item Hardly any software is maintained for its whole life by
+    the original author.
+
+    \item Code conventions improve the readability of the software,
+    allowing engineers to understand new code more quickly and
+    thoroughly.
+
+    \item If you ship your source code as a product, you need to
+    make sure it is as well packaged and clean as any other
+    product you create.
+\end{itemize}
+
+
+\subsection{Acknowledgments}
+
+This document is derived from the Sun Microsystems Java language
+coding standards presented in the Java Language Specification
+(\code{http://java.sun.com/docs/books/jls/index.html}).
+
+\begin{verbatim}
+      Adapted with permission from
+      CODE CONVENTIONS FOR THE JAVA^TM PROGRAMMING LANGUAGE.
+      Copyright 1995-1999 Sun
+      Microsysytems, Inc.  All rights reserved.
+\end{verbatim}
+
+See the Java Code Conventions Web site
+(\code{http://java.sun.com/docs/codeconv/}) for more details.
+
+Such an adaption is explicitly permitted by the Sun Microsystems
+copyright notice (Copyright.doc.html).
+
+\subsection{To whom should I Complain?}
+
+While we're working on this document complain to
+\code{rhl@astro.princeton.edu}.
+
+%------------------------------------------------------------------------------
+
+\section{File Names}
+
+This section lists commonly used file suffixes and names. 
+
+\subsection{File Suffixes}
+
+
+
+Software uses the following file suffixes:
+\begin{center}
+\begin{tabular}{ll}
+\textbf{File Type} & \textbf{Suffix}\\
+C source & \code{.c} \\
+header files & \code{.h} \\
+SWIG interface files & \code{.i} \\
+Perl files & \code{.pl} \\
+Python files & \code{.py} \\
+\end{tabular}
+\end{center}
+
+\subsection{Common File Names}
+
+Frequently used file names include: 
+\begin{center}
+\begin{tabular}{lp{3in}}
+\textbf{File Name} & \textbf{Use} \\
+\file{Makefile} &
+  The preferred name for makefiles.\\
+
+\file{README} & 
+The preferred name for the file that summarizes the contents of a
+particular directory. \\
+
+\file{TAGS} &
+The index file (built by etags) used by emacs to follow cross references. \\
+\end{tabular}
+\end{center}
+
+
+%------------------------------------------------------------------------------
+
+\section{File Organization}
+
+A file consists of sections that should be separated by blank lines
+and an optional comment identifying each section.
+
+Files longer than 2000 lines are cumbersome and should be avoided.
+
+For an example of a properly formatted program, see
+Source File Example (\S\ref{SourceExample}).
+
+\subsection{Source Files}
+
+\subsubsection{Include Files}
+
+Include files should have the following order:
+\begin{itemize}
+\item 
+A brief description of the functionality provided by this set of APIs.
+This comment should not duplicate information available
+elsewhere (e.g. the filename; log information available from cvs).
+
+\item 
+A check for the existance of a CPP symbol of the form
+\code{FILENAME_H} (e.g. \code{STDIO_H}). This is used
+to ensure that an include file may safely be included more than once.
+
+\item 
+Any \code{#define}d constants. {\bf Note:} \code{#define} should be used rather
+than e.g. \code{const int}; C and C++ const semantics are different.
+
+\item 
+Any enums. 
+
+\item 
+Any typedefs and structs.
+
+\item 
+Any function prototypes.
+
+\end{itemize}
+e.g. for file \file{psThing.h}:
+\begin{verbatim}
+/** \file psThing.h
+ * Support for psThings
+ */
+
+#if !defined(PS_THING_H)
+#define PS_THING_H
+
+#define PS_NMAX 10                        // maximum number of subthings
+
+enum {PS_FIRST, PS_SECOND, PS_THRID};     // subthing sequences
+typedef struct {
+    int nthing;                           // number of subthings
+    int sthing[PS_NMAX];                  // the subthings
+} psThing;
+
+/// make a new psThing
+psThing *psThingAlloc(int nthing);
+
+/// free an existing psThing
+void psThingFree(psThing *thing);
+
+#endif
+\end{verbatim}
+\tbd{add doxygen comments to this example}
+
+\subsubsection{C Source Files}
+
+C Source files have the following ordering:
+\begin{itemize}
+\item Beginning comments, concisely describing the functionality present
+in the file.  This comment should not duplicate information available
+elsewhere (e.g. the filename; log information available from cvs).
+
+\item \code{#include} statements
+
+\item \code{#defines} local to the file
+
+\item Typedefs local to the file
+
+\item Any needed prototypes for file-static functions. Prototypes need
+only be provided for functions used before their definition; in practice
+this means ones that the compiler complains about.
+
+\item File-static variable declarations
+
+\item The code that actually implements the desired functionality.
+\end{itemize}
+
+\subsubsection{SWIG Interface Files}
+
+\tbd{Write me}
+
+%------------------------------------------------------------------------------
+
+\section{Indentation}
+
+Four spaces should be used as the unit of indentation.  Spaces are
+recommended over tabs for indentation, but if tabs are used, the tabs
+stops must be set to 8 spaces, not 4.
+
+\subsection{Line Length}
+
+Avoid lines longer than 110 characters.
+
+When preparing documents, you should ensure that lines of this length
+are not wrapped.  If you are using the standard PSDC \LaTeX{} class
+file \file{panstarrs.cls}, \CODE|\begin{verbatim} ... \end{verbatim}|
+will do this for you.
+
+\subsection{Wrapping Lines}
+
+When an expression will not fit on a single line, break it according
+to these general principles:
+
+\begin{itemize}
+\item Break after a comma or operator.
+
+\begin{verbatim}
+    if(a < b ||
+       a > 2*b) {
+	x = (a + b + c) +
+	    sin(z);
+    }
+\end{verbatim}
+
+\item Prefer higher-level breaks to lower-level breaks.
+
+\item Align the new line with the beginning of the expression at the
+same level on the previous line\footnote{For emacs users, this means
+the indentation that \code{<tab>} produces}.
+
+\item If the above rules lead to confusing code or to code that's
+squished up against the right margin, just indent 8 spaces
+instead.\footnote{This should be \emph{very} rare! Consider whether
+you should be e.g. factoring code into a function.}
+
+\end{itemize}
+
+Here are some examples of breaking function calls:
+
+\begin{verbatim}
+someFunc(longExpression1, longExpression2, longExpression3, 
+         longExpression4, longExpression5);
+ 
+var = someFunc1(longExpression1,
+                someFunc2(longExpression2,
+			  longExpression3)); 
+\end{verbatim}
+
+Following are two examples of breaking an arithmetic expression. The
+first is preferred, since the break occurs outside the parenthesized
+expression, which is at a higher level.
+
+\begin{verbatim}
+    longName1 = longName2*(longName3 + longName4 - longName5) +
+        4*longname6;                    // PREFER
+    
+    longName1 = longName2*(longName3 + longName4 -
+                           longName5) + 4*longname6; // AVOID 
+\end{verbatim}
+
+Following are two examples of indenting function declarations. The
+first is the conventional case. The second would shift the second and
+third lines to the far right if it used conventional indentation, so
+instead it indents only 4 spaces (note that putting the \code{anArg}
+argument on a line of its own makes this the `natural' emacs indentation).
+
+\begin{verbatim}
+//CONVENTIONAL INDENTATION
+void someFunc(int anArg, Object anotherArg, String yetAnotherArg,
+              Object andStillAnother)
+{
+    ...
+}
+
+//INDENT 8 SPACES TO AVOID VERY DEEP INDENTS
+static type honkingLongFunctionName(
+        int anArg,
+        Object anotherArg, String yetAnotherArg,
+        Object andStillAnother)
+{
+    ...
+}
+\end{verbatim}
+
+Here are three acceptable ways to format ternary expressions:
+\begin{verbatim}
+alpha = (aLongBooleanExpression) ? beta : gamma;  
+
+alpha = (aLongBooleanExpression) ? beta
+                                 : gamma;  
+
+alpha = (aLongBooleanExpression)
+        ? beta 
+        : gamma;  
+\end{verbatim}
+
+%------------------------------------------------------------------------------
+
+\section{Comments}
+
+C99 supports both types of comments found
+in C++, namely those delimited by /*...*/, and //.
+
+Comments should not be used to `comment out code'; use \code{#if 0} instead:
+\begin{verbatim}
+#if 0
+    code that is not wanted just at present
+#endif
+\end{verbatim}
+This has two advantages:
+\begin{itemize}
+\item  The code can easily be reinstated.
+\item  Related blocks of code can be removed with
+\begin{verbatim}
+#define IGNORE_RHL 1
+#if !IGNORE_RHL
+    ...
+#endif
+...
+#if !IGNORE_RHL
+    ...
+#endif
+\end{verbatim}
+This type of \code{#define} may appear within the main body of the file.
+\end{itemize}
+
+Comments should be used to give overviews of code and provide
+additional information that is not readily available in the code
+itself. Comments should contain only information that is relevant to
+reading and understanding the program. For example, information about
+how the corresponding package is built or in what directory it resides
+should not be included as a comment.
+
+Discussion of nontrivial or nonobvious design decisions is
+appropriate, but avoid duplicating information that is present in (and
+clear from) the code. It is too easy for redundant comments to get out
+of date. In general, avoid any comments that are likely to get out of
+date as the code evolves.
+
+Note: The frequency of comments sometimes reflects poor quality of
+code. When you feel compelled to add a comment, consider rewriting the
+code to make it clearer.
+
+Comments should not be enclosed in large boxes drawn with asterisks or
+other characters.
+\hfil\break
+Comments should never include special characters such as form-feed and
+backspace.
+ 
+\subsection{Implementation Comment Formats}
+
+Programs can have four styles of implementation comments: block,
+single-line, trailing, and end-of-line.
+
+\subsubsection{Block Comments }
+\label{ImplBlockComments} 
+
+Block comments are used to provide descriptions of files, functions,
+data structures and algorithms. Block comments may be used at the
+beginning of each file and before each function. They can also be used
+in other places, such as within functions. Block comments inside a
+function should be indented to the same level as the code they
+describe.
+
+See also Documentation Comments (\S\ref{DocComments}).
+
+\subsubsection{Single-Line Comments}
+\label{SingleLineComment} 
+
+Short comments can appear on a single line indented to the level of
+the code that follows. If a comment can't be written in a single line,
+it should follow the block comment format (\S\ref{ImplBlockComments}).
+
+\subsubsection{Trailing Comments}
+\label{col40} 
+
+Very short comments can appear on the same line as the code they
+describe, and should be indented to column 40\footnote{In emacs, this
+may be achieved with \code{ESC;}}. If the code extends beyond this
+column, the comment should be separated from the closing semi-colon by
+a single space.
+
+Here's an example of a trailing comment in C code: 
+\begin{verbatim}
+if (a == 2) {
+    return TRUE;                          /* special case */
+} else {
+    return isPrime(a);                    /* works only for odd a */
+}
+\end{verbatim}
+
+\subsubsection{End-Of-Line Comments}
+
+The \code{//} comment delimiter can comment out a complete line
+or only a partial line; as for trailing comments, the comment should
+start in column 40.  It shouldn't be used on consecutive multiple
+lines for text comments.  Examples of both styles follow:
+
+\begin{verbatim}
+if (foo > 1) {
+
+    // Do a double-flip.
+    ...
+} else {
+    return false;          // Explain why here.
+}
+\end{verbatim}
+
+\subsection{Documentation Comments}
+\label{DocComments} 
+
+Doxygen\footnote{{\tt  www.doxygen.org}}  will   be  used  to  produce
+documentation of the types,  functions and variables without requiring
+much  extra  effort  for   the  programmer.   Comments  starting  with
+particular characters  (``tags'') are used by Doxygen  to identify the
+relevant code to be documented.
+
+Functions shall be tagged for Doxygenation by pre-pending them with a
+block comment (\S\ref{ImplBlockComments}) which starts with a
+\code{/**} instead of the usual \code{/*}.
+
+Variables shall be tagged for Doxygenation by appending their
+declaration with an end-of-line comment which starts with a
+\code{///<} instead of the usual \code{//}.
+
+An example of a function definition employing Doxygen-compatible
+comments follows:
+
+\begin{verbatim}
+/** This is a really cool function.
+ *  It does many really cool things.
+ */
+int reallyCoolFunction(int aNumber,     ///< This is a number 
+                       float aRealNumber ///< This is a real number
+                       )
+{
+    char *aString;                      ///< This is a string used to do stuff.
+
+    /* Do really cool stuff */
+    ...
+}
+\end{verbatim}
+
+If you need to give information about a type, interface, or variable
+that isn't appropriate for doxygenation, use a normal implementation
+block comment (\S\ref{ImplBlockComments}) or single-line comment
+(\S\ref{SingleLineComment}) immediately \textit{before} the code (as
+exampled above). For example, details about the implementation of a
+type should go in in such an implementation block comment
+\textit{before} the start of the implementation, not in the doxygen
+comment.
+
+%------------------------------------------------------------------------------
+
+\section{Declarations}
+
+All globally-visible symbols must be declared in a suitable header
+(.h) file.  Global private symbols should not appear in public header
+files, but rather in separate, private header files.
+
+\subsection{Number Per Line}
+
+One declaration per line is recommended since it encourages
+commenting. In other words,
+\begin{verbatim}
+int level;                              // indentation level
+int size;                               // size of table
+\end{verbatim}
+is preferred over
+\begin{verbatim}
+int level, size;
+\end{verbatim}
+Do not put different types on the same line. Example:
+\begin{verbatim}
+int foo,  fooarray[];                   // WRONG!
+\end{verbatim}
+
+Note: The examples above use one space between the type and the identifier. Another acceptable alternative is to use tabs, e.g.:
+\begin{verbatim}
+int    level;                           // indentation level
+int    size;                            // size of table
+Object    currentEntry;                 // currently selected table entry
+\end{verbatim}
+
+(Note that the comments are indented to column 40, as per section~\ref{col40}).
+
+\subsection{Initialization}
+
+Try to initialize local variables where they're declared.  The only
+reason not to initialize a variable where it's declared is if the
+initial value depends on some computation occurring first.
+
+In some cases it may be necessary to initialize a variable to suppress
+a compiler warning; in this case a comment should explain the circumstances.
+
+\subsection{Placement}
+
+Variables should ordinarily be declared at the top of the block in
+which they appear, unless there is some reason to declare them later.
+This allows them to be initialised as they are created, and naturally
+associates their declaration with their use.
+
+For example, declarations are naturally mixed with assertions:
+\begin{verbatim}
+void function(REGION *reg)                      // image of the sky
+{
+    assert (reg != NULL);
+    const int nrow = reg->nrow;
+    assert (reg->type == psType_U16);
+    const psTypeU16 *rows = reg->rows;
+    ...
+}
+\end{verbatim}
+
+Indexes of \code{for} loops should usually
+be declared in the \code{for} statement (the declarations appear
+in the scope of the braces):
+\begin{verbatim}
+for (int i = 0; i < maxLoops; i++) {
+    ...
+}
+\end{verbatim}
+
+Avoid local declarations that hide declarations at higher levels. For
+example, do not declare the same variable name in an inner block:
+\begin{verbatim}
+static int count;
+...
+void myFunction(void)
+{
+    if (condition) {
+    int count = 0;                      // AVOID!
+    ...
+    }
+    ...
+}
+\end{verbatim}
+
+\subsection{Function Declarations}
+
+When coding C functions the following formatting rules should be
+followed:
+
+\begin{itemize}
+\item 
+Function declarations should be preceeded by a short comment
+describing what the function does.  These comment blocks should
+include doxygen-style comments to provide a brief desription as well
+as other warnings, bugs, etc as needed.
+
+\item The function's type should appear on the same line as the
+function declaration.
+
+\item All arguments should be declared with their types; i.e. no
+classic-C declarations like:
+\begin{verbatim}
+int main(ac, av)
+         int ac;
+         char **av;
+\end{verbatim}
+
+\item No space between a function name and the parenthesis \code{(} starting
+its parameter list \matchBracket{)}
+
+\item The function body's open brace \CODE.{. should be in column 1 of the next line:
+\begin{verbatim}
+void function(void)
+{
+    ...
+}
+\end{verbatim}
+
+\item Closing brace \CODE.}. starts a line by itself indented to match its
+corresponding opening statement, except when it is a null statement
+the \CODE.}. should appear immediately after the \CODE.{..
+
+\begin{verbatim}
+void find_neos(const REGION *sky, const char *descrip)
+{
+    struct {
+        int x, y;
+    } work[10];
+    int ivar;
+
+    while(isspace(*descrip++)) {}         // skip white space
+    descrip--;                            // one too far. Not great code...
+
+    ...
+}
+\end{verbatim}
+\end{itemize}
+
+%------------------------------------------------------------------------------
+
+\section{Statements}
+\subsection{Simple Statements}
+
+Each line should usually contain only one statement. Example:
+\begin{verbatim}
+x = sqrt(x2);                           // Correct
+i++;                                    // Correct  
+x = sqrt(x2); i++;                      // Avoid!
+\end{verbatim}
+
+An example of a reasonably two-statement line is:
+\begin{verbatim}
+argv++; argc--;
+\end{verbatim}
+where the two actions are intimately related.
+
+\subsection{Compound Statements}
+
+Compound statements are statements that contain lists of statements
+enclosed in braces. See the
+following sections for examples.
+
+\begin{itemize}
+\item The enclosed statements should be indented one more level than the compound statement.
+
+\item The opening brace should be at the end of the line that begins
+the compound statement; the closing brace should begin a line and be
+indented to the beginning of the compound statement.
+
+\item Braces are used around all statements, even single statements,
+when they are part of a control structure, such as a \code{if-else} or
+\code{for} statement. This makes it easier to add statements without
+accidentally introducing bugs due to forgetting to add braces.
+\end{itemize}
+
+\subsection{return Statements}
+
+A \code{return} statement with a value should not use parentheses unless they make the return value more obvious in some way. Example:
+\begin{verbatim}
+return;
+
+return myDisk.size;
+
+return (size ? size : defaultSize);
+
+\end{verbatim}
+
+\subsection{if, if-else, if else-if else Statements}
+
+The \code{if-else} class of statements should have the following form:
+\begin{verbatim}
+if (condition) {
+    statements;
+}
+
+if (condition) {
+    statements;
+} else {
+    statements;
+}
+
+if (condition) {
+    statements;
+} else if (condition) {
+    statements;
+} else {
+    statements;
+}
+
+\end{verbatim}
+
+Note: \code{if} statements always use braces {}. Avoid the following
+error-prone form:
+\begin{verbatim}
+if (condition)                          // AVOID! THIS OMITS THE BRACES {}!
+    statement;
+\end{verbatim}
+
+\subsection{for Statements}
+
+A \code{for} statement should have the following form:
+\begin{verbatim}
+for (initialization; condition; update) {
+    statements;
+}
+\end{verbatim}
+
+An empty \code{for} statement (one in which all the work is done
+in the initialization, condition, and update clauses) should have one of
+the following forms:
+\begin{verbatim}
+for (initialization; condition; update);
+for (initialization; condition; update) {}
+\end{verbatim}
+
+When using the comma operator in the initialization or update clause
+of a \code{for} statement, avoid the complexity of using more
+than three variables. If needed, use separate statements before the
+\code{for} loop (for the initialization clause) or at the end of
+the loop (for the update clause).
+
+\subsection{while Statements}
+
+A \code{while} statement should have the following form:
+\begin{verbatim}
+while (condition) {
+    statements;
+}
+\end{verbatim}
+An empty \code{while} statement should have one of the following forms:
+\begin{verbatim}
+while (condition);
+while (condition) {}
+\end{verbatim}
+
+\subsection{do-while Statements}
+
+A \code{do-while} statement should have the following form:
+\begin{verbatim}
+do {
+    statements;
+} while (condition);
+\end{verbatim}
+
+\subsection{switch Statements}
+
+A \code{switch} statement should have the following form):
+\begin{verbatim}
+switch (condition) {
+    case ABC:
+        statements;
+        /* falls through */
+
+    case DEF:
+    case GHI:
+        statements;
+        break;
+
+    case XYZ:
+        statements;
+        break;
+
+    default:
+        statements;
+        break;
+}
+\end{verbatim}
+
+Every time a case falls through (doesn't include a \code{break}
+statement), add a comment where the \code{break} statement would
+normally be. This is shown in the preceding code example with the
+\code{/* falls through */} comment. A comment is not required (or
+expected) when the fall-through is between multiple case statements.
+
+Every \code{switch} statement should include a default case, which
+should come last. The \code{break} in the default case is redundant,
+but it prevents a fall-through error if later another \code{case} is
+later (and illegally) added after the default clause.
+
+When switching on an enumerated type, if all the elements of the type
+are included in the switch a default clause should still be added (not
+all compilers diagnose missing elements).  In this case, the action in
+the default clause should be to generate an error and abort.
+
+\subsection{label Statements}
+
+Code labels should be indented to align with the previous level of
+indentation.
+
+%------------------------------------------------------------------------------
+
+\section{White Space}
+\subsection{Blank Lines}
+
+Blank lines improve readability by setting off sections of code that
+are logically related.
+
+Two blank lines should always be used in the following circumstances:
+\begin{itemize}
+\item Between sections of a source file
+\end{itemize}
+
+One blank line should always be used in the following circumstances:
+
+\begin{itemize}
+
+\item Between the local variables in a function and its first statement
+
+\item Between logical sections inside a function to improve readability
+\end{itemize}
+
+\subsection{Blank Spaces}
+
+Blank spaces should be used in the following circumstances:
+\begin{itemize}
+
+\item A keyword followed by a parenthesis should be separated by a space. Example:
+\begin{verbatim}
+while (true) {
+    ...
+}
+\end{verbatim}
+
+Note that a blank space should not be used between a function name and
+its opening parenthesis. This helps to distinguish keywords from
+function calls.
+
+\item A blank space should appear after commas in argument lists.
+
+\item
+Binary operators should be separated from their operands by spaces.
+Blank spaces should never separate unary operators such as a type
+cast, unary minus, increment (\code{++}), and decrement (\code{--})
+from their operands. Examples:
+\begin{verbatim}
+    a += c + d;
+    a = (a + b) / (c * d);
+    
+    while (*d++ = *s++) {                 // Tricky way of copying until '\0'
+        n++;
+    }
+    printf ("size is %d\n", foo);
+\end{verbatim}
+
+\item The expressions in a \code{for} statement should be
+separated by blank spaces. Example:
+
+\begin{verbatim}
+    for (expr1; expr2; expr3)
+\end{verbatim}
+
+\item 
+The word \code{assert} should be treated as a keyword, and
+separated by a space from its logical expression.
+\end{itemize}
+
+%------------------------------------------------------------------------------
+
+\section{Naming Conventions}
+
+Naming conventions make programs more understandable by making them
+easier to read. They can also give information about the function of
+the identifier -- for example, whether it's a constant, a function, or
+a type -- which can be helpful in understanding the code.  Remember
+these are guidelines for improving readability; clarity should trump
+rigid adherence to the guideline.
+ 
+{ \small 
+\begin{tabular}{lp{3in}p{3in}}
+\textbf{Identifier Type} &
+\textbf{Rules for Naming} &
+\textbf{Examples} \\
+
+Typedefs &
+
+Type names should be nouns, in mixed case with the first letter of
+each internal word capitalized.  Little words, such as 'for', 'to',
+'at', etc, may be written in all lower-case or separated with
+underscores if it makes the name clearer.
+
+If and only if the type is visible at global scope, the
+type name should start with \code{ps}.
+
+Try to keep your type names simple and descriptive. Use whole
+words-avoid acronyms and abbreviations (unless the abbreviation is
+much more widely used than the long form, such as URL or HTML).
+
+ &
+typedef struct {...} psRaster;\hfil\break
+typedef struct {...} psImage;
+\\
+
+Functions &
+
+The names of all externally visible functions (i.e. all those that are
+not declared \code{static}) should be verbal phrases, in mixed case.
+
+Namespaces should be protected by using special naming prefixes to
+restrict the name space in particular libraries.  For example, the
+PSLib functions are all prefixed with \code{ps}.
+
+&
+
+\code{psRun(int ID);}\hfil\break
+\code{psRunFast(float velocity);}\hfil\break
+\code{psGetBackground(void);}\hfil\break
+\hfil\break
+\code{p_psForgeSignature(const char *name);}
+
+\\
+
+Variables &
+
+Except for variables, all instance, class, and class constants are in
+mixed case with 
+
+Variable names should not start with underscore \code{_} as these
+names are, under some circumstances, reserved by the C
+standard. Non-globally visible words should start with a lowercase
+first letter; Internal words start with capital letters.
+
+Variable names should be short yet meaningful. The choice of a
+variable name should be mnemonic- that is, designed to indicate to the
+casual observer the intent of its use. One-character variable names
+should be avoided except for temporary \textit{throwaway} variables. Common
+names for temporary variables are \code{i}, \code{j},
+\code{k}, \code{m}, and \code{n} for integers;
+\code{c}, \code{d}, and \code{e} for characters.
+
+&
+\code{int i;}\hfil\break
+\code{char c;}\hfil\break
+\code{float myWidth;}\hfil\break
+\hfil\break
+\code{int psNumOTA;}\hfil\break
+\code{int p_psMyFiddleFactor;}\hfil\break
+
+\\
+ 
+Constants
+
+&
+
+Constants used to e.g. dimension arrays should be set using the
+\code{#define}; \code{const} variables should not be used, especially
+in header files.  Symbolic values should usually be declared using
+enumerated types.
+
+&
+\code{#define PS_MAXLEN 40}\hfil\break
+\CODE|enum { PS_ONE = 1, PS_TWO = 2 };|\hfil\break
+
+\end{tabular}
+}
+
+%------------------------------------------------------------------------------
+
+\section{Programming Practices}
+
+\subsection{When to Make Symbols Global}
+
+Declare all functions and top-level variables \code{static} within a
+file if they are not needed outside of the file.  {\bf Note:} do not
+confuse this use of \code{static} with its usage to make
+auto-variables within a function persistent.
+
+A name (whether of a variable, a function, or a type) shall start
+\code{ps} (or \code{p_ps}) if and only if it is visible at global
+scope.  The distinction is that \code{p_ps} names are not part of the
+documented APIs, but need to be exposed for some reason.
+
+\subsection{Constants}
+
+Numerical constants (literals) should not be coded directly, except for
+small integers such as -1, 0, and 1 which are permitted to e.g. appear
+in a \code{for} loop as counter values.  
+
+\subsection{Type Qualifiers (const and restrict)}
+
+All interfaces and type definitions should use \code{const} and
+\code{restrict} wherever appropriate. For example,
+\begin{verbatim}
+typedef struct {
+    int n;
+    float *restrict vec;
+} psVec;
+
+psVec *psVecAdd(const restrict* psVec s1, const restrict* psVec s2); 
+\end{verbatim}
+
+\textit{{\bf Note:} compilers are free to ignore the \code{restrict}
+keyword, so all code should be written to explicitly handle aliasing}.
+
+\subsection{structs and typedefs}
+
+All structs should be defined as \code{typedef}s.
+
+A struct should not include a struct
+tag unless it's self-referential; E.g.
+\begin{verbatim}
+typedef struct myStruct { 		// Omit "myStruct"
+    int x;
+} myStruct;
+
+typedef struct yourStruct { 		// OK
+    struct yourStruct *next;
+    int x;
+} yourStruct;
+\end{verbatim}
+
+\subsection{Variable Assignments}
+
+Avoid assigning several variables to the same value in a single
+statement, unless the variables are intimately related. Acceptable examples:
+
+\begin{verbatim}
+row0 = col0 = 0;
+sum = sumx = sumy = 0;
+\end{verbatim}
+
+Do not use the assignment operator in a place where it can be easily confused with the equality operator. Example:
+\begin{verbatim}
+if (c++ = d++) {                        // AVOID!
+    ...
+}
+\end{verbatim}
+should be written as
+\begin{verbatim}
+if ((c++ = d++) != 0) {
+    ...
+}
+\end{verbatim}
+
+Do not use embedded assignments in an attempt to improve run-time
+performance. This is the job of the compiler. Example:
+\begin{verbatim}
+d = (a = b + c) + r;                    // AVOID!
+\end{verbatim}
+
+should be written as
+\begin{verbatim}
+a = b + c;
+d = a + r;
+\end{verbatim}
+
+\subsection{Miscellaneous Practices}
+\subsubsection{Parentheses}
+
+It is generally a good idea to use parentheses liberally in
+expressions involving mixed operators to avoid operator precedence
+problems. Even if the operator precedence seems clear to you, it might
+not be to others-you shouldn't assume that other programmers know
+precedence as well as you do. In cases where the precedence rules
+are clear, the parentheses may be omitted.
+
+\begin{verbatim}
+if (a & b || c & d)                     // AVOID!
+if ((a & b) || (c & d))                 // RIGHT
+\end{verbatim}
+
+There are some famous problems with precedence in C.  In particular,
+expressions involving the combinations of \code{||} and
+\code{&&} should be fully parenthesised, as should all
+expressions containing bitwise operators:
+\begin{verbatim}
+if ((a == b && c == d) || e == f)
+l << (j + k)
+(l << j) + k
+(i & b) | c
+\end{verbatim}
+
+\subsubsection{Returning Values}
+
+Try to make the structure of your program match the intent. Example:
+\begin{verbatim}
+if (booleanExpression) {
+    return true;
+} else {
+    return false;
+}
+\end{verbatim}
+should instead be written as
+\begin{verbatim}
+return booleanExpression;
+\end{verbatim}
+If you're concerned that the reader may not know that
+\code{booleanExpression} is boolean, use:
+\begin{verbatim}
+return (booleanExpression ? true : false);
+\end{verbatim}
+
+Similarly,
+\begin{verbatim}
+if (condition) {
+    return x;
+}
+return y;
+\end{verbatim}
+should be written as
+\begin{verbatim}
+return (condition ? x : y);
+\end{verbatim}
+
+\subsubsection{Expressions before `?' in the Conditional Operator }
+
+If an expression containing a binary operator appears before the \code{?} in the ternary \code{?: }operator, it should be parenthesized. Example:
+\begin{verbatim}
+(x >= 0) ? x : -x;
+\end{verbatim}
+
+\subsubsection{Functions that Take No Parameters}
+
+When a function takes no arguments, it should be explicitly declared
+as \code{void}:
+\begin{verbatim}
+static type myFunction(void)
+{
+    ...
+}
+\end{verbatim}
+
+
+\subsubsection{Special Comments}
+
+Doxygen has special comments which are used to provide specific notes
+in the code.  These are added in as special entries and sections in
+the Doxygen-generated documentation.  These special comments should be
+used in addition to the Doxygen usage to make these types of
+conditions easily searchable.
+
+\begin{itemize}
+\item 
+Use \code{\warning} in a comment to flag something that is bogus but works.
+
+\item 
+Use \code{\bug} to flag something that is bogus and broken.
+
+\item 
+Use \code{\todo} to note additional work to be done 
+
+\item 
+Use \code{\note} to make other general notes 
+
+\item 
+Use \code{\test} to list test cases 
+
+\item
+Use \code{\notreached} to indicate a line of code that cannot be reached,
+e.g.
+\begin{verbatim}
+if (sqrt(x) < 0) {
+    psAbort();                            // never returns
+    exit(1);                              // NOTREACHED
+}
+\end{verbatim}
+
+\item 
+Use \code{\notused} to indicate unused arguments to a function:
+e.g.
+\begin{verbatim}
+type psWorkHard(const Region *restrict reg, // Region to operate on
+                int myConst,            // magic value
+                int magicNumber         // NOTUSED; reserve for next version
+                )
+{
+   ...
+}
+\end{verbatim}
+\end{itemize}
+
+%------------------------------------------------------------------------------
+\appendix				%Begin Appendices
+%------------------------------------------------------------------------------
+
+\section{Code Examples}
+\subsection{Source File Example}
+\label{SourceExample} 
+
+\tbd{Need to include more of the rules, and shorten the example}
+Here's \file{psBuffer.h}:
+\begin{verbatim}
+#if !defined(PS_BUFFER_H)               /* here's the insides of psBuffer.h */
+#define PS_BUFFER_H
+#define PS_BUFSIZE 128                  // Size for I/O buffers
+
+typedef struct {
+    char buf[PS_BUFSIZE];               // buffer
+    int n;                              // number of bytes in buffer
+} psBuffer;
+
+psBuffer *psBufferAlloc(void);
+void psBufferFree(psBuffer *buf,         // buffer to delete
+                 int deep);             // NOTUSED. Do a deep delete
+
+void psBufferAppend(psBuffer *restrict buf, // psBuffer to append to
+                    const char *restrict str); // string to add
+
+#endif
+\end{verbatim}
+
+And here's the C source; \file{psUtils.h} provides \code{psAlloc/psFree}.
+
+\begin{verbatim}
+/*
+ * This file implements an example of formatting a file of C code
+ *
+ * It isn't a very good piece of code
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include "psUtils.h"
+#include "psBuffer.h"
+
+typedef enum { CRNL, NL } NLType;
+
+static char *getNewline(NLType type);   // return a suitable newline
+
+static psBuffer *buf = NULL;            // I/O buffer
+
+/*****************************************************************************/
+/*
+ * Create/destroy psBuffers
+ */
+psBuffer *psBufferAlloc(void)
+{
+    psBuffer *buf = psAlloc(sizeof(psBuffer));
+    buf->n = 0;
+
+    return buf;
+}
+
+void psBufferFree(psBuffer *buf,         // buffer to delete
+                 int deep)              // NOTUSED. Do a deep delete
+{
+    if (buf == NULL) {
+        return;
+    }
+    
+    if (buf->n > 0) {
+        (void)fputs(buf->buf, stdout);
+    }
+    
+    psFree(buf);
+}
+
+/*****************************************************************************/
+/*
+ * Append to a string to a psBuffer
+ */
+void psBufferAppend(psBuffer *restrict buf, // psBuffer to append to
+                    const char *restrict str) // string to add
+{
+    assert(str != NULL);
+    const int len = strlen(str);
+
+    if (buf->n + len >= PS_BUFSIZE) {   // XXX Handle this better
+        fprintf(stderr, "Sorry; too many bytes. Bye bye\n");
+        abort();
+        exit(1);                        // NOTREACHED
+    }
+
+    (void)strcat(&buf->buf[buf->n], str);
+    buf->n += len;
+}    
+
+/*****************************************************************************/
+/*
+ * Now do the work
+ */
+int main(void)
+{
+    buf = psBufferAlloc();
+    long t;                               // current time
+
+    while(t = (long)time(NULL), t%3 == 2) {}
+    
+    if (t%3 == 0) {
+        psBufferAppend(buf, "Hello");
+    } else if (t%3 == 1) {
+        psBufferAppend(buf, "Aloha");
+    }
+    psBufferAppend(buf, " World!");
+
+    psBufferAppend(buf, getNewline(NL));
+
+    psBufferFree(buf, 0);
+
+    return 0;
+}
+
+/*****************************************************************************/
+/*
+ * Return a desired line terminator
+ */
+static char *getNewline(NLType type)    // what sort of newline?
+{
+    char *newline;                      // newline to add
+    switch (type) {
+      case CRNL:
+        newline = "\r\n";
+        break;
+
+      case NL:
+        newline = "\n";
+        break;
+
+      default:
+        newline = "NL";                     // FIXME
+        break;
+    }
+
+    return newline;
+}
+\end{verbatim}
+
+%------------------------------------------------------------------------------
+
+\section{Departures from Sun Java Coding Standards}
+
+Apart from changes required by our use of C99 rather than Java,
+this document differs from the original Sun-Java standard in
+two sorts of ways; additions and changes.
+
+\subsection{ Additions to the Sun-Java standards}
+
+\begin{itemize}
+\item
+    Naming convention for include files
+
+\item
+  Added naming conventions for constructors, destructors, and conversion
+  functions.
+
+\item
+  Specified that the `output' argument should come \emph{first}.
+
+\item
+  Added conventional comments for unused arguments and unreachable
+  statements (conforming to Doxygen's conventions).
+
+\item
+  Specified that functions taking no arguments should be explicitly
+  specified as \code{(void)} (avoiding complaints on some compilers,
+  e.g. on SGIs).
+
+\item
+  Added rules on typedefing structs, and on struct tags.
+
+\end{itemize}
+
+\subsection{Departures from the Sun-Java standards}
+
+\begin{itemize}
+
+\item
+  We break lines \emph{after} (not before) an operator.
+
+\item
+  We don't specify any special indentation for continued logical
+  expressions within an \code{if} clause.
+
+\item
+  Specify that \code{case/break} statements should be indented
+  by half an indent (2 spaces)
+
+\item
+  Comments need not be preceeded by a blank line
+
+\item
+  The restriction on only declaring variables at the top of blocks (including
+  \code{for} loops has been somewhat relaxed.
+
+\item
+  Relaxed the `only one statement per line' rule a little.
+
+\item
+  High-precedence binary operators (\code{*}, \code{/}, \code{%} and above)
+    should \emph{not} be surrounded by whitespace.
+
+\item
+  The line-length limit has been changed from 80 to 110 characters.
+
+\item
+  Relaxed wording to allow parentheses to be omitted when the precedence
+  is well known and unambiguous.
+
+\end{itemize}
+
+%------------------------------------------------------------------------------
+
+\section{How to Achieve This Style in Emacs}
+  \label{dot_emacs}
+
+\textit{This section is provided for your convenience; it is, of course,
+not part of the coding standards.}
+
+The easiest way to use these conventions while writing code using
+emacs is to get \file{panstarrs.el} from \code{cvs} with the command
+\begin{verbatim}
+  cvs -d poiserver0.ifa.hawaii.edu:/usr/local/cvs/repositories/pan-starrs co Templates
+\end{verbatim}
+and then grab \file{Templates/panstarrs.el}.  Then add:
+
+\begin{verbatim}
+(load-file "/home/you/Templates/panstarrs.el")
+(add-to-list 'auto-mode-alist (cons "\\.[ch]$" 'panstarrs-c-mode))
+\end{verbatim}                          % $ % match that $ for emacs
+to your \file{.emacs} file; this will use \code{panstarrs-c-mode}
+for all \file{.c} and \file{.h} files.
+
+If you want to
+\begin{itemize}
+  \item use C99-style comments (i.e. \code{//} to end of line)
+  \item have your emacs window set to 110 characters wide
+  \item somewhat improve (or spoil?) the handling of re-indenting comments
+\end{itemize}
+add one or more of these \code{add-hook} commands to your \file{.emacs} file too:
+\begin{verbatim}
+(add-hook 'panstarrs-c-mode-hook 'panstarrs-c99-comments)
+(add-hook 'panstarrs-c-mode-hook 'panstarrs-set-width)
+(add-hook 'panstarrs-c-mode-hook
+	  '(lambda ()
+	     (set (make-variable-buffer-local 'comment-indent-function)
+		  'panstarrs-comment-indent)))
+\end{verbatim}
+          
+\section{How to Achieve This Style with Astyle}
+\label{astyle}
+
+The purpose of a coding standard is to improve coding efficiency, not
+to hinder it.  Since people are inherently falible, 100\% adherence to
+the standard is an impossible goal for human-generated code.
+Furthermore, laziness and slopiness are human nature.  To minimize the
+amount of effort spent in keeping software in line with a coding
+standard is to use automatic re-formatting tools to enforce the
+standard.  Various software tools exist to perform these tasks.  One
+of these is \code{astyle}, an open-source tool which takes a variety
+of options which allow the use to tailor the coding standard to suit
+their preferences.  We have determined the following collection of
+astyle options which achieve the many of the coding standard
+guidelines specified above.  Note that any of the listed options
+(always in the 'long' form) may be specified in the user's
+\code{.astylerc} file by dropping the leading dash.
+
+\begin{itemize}
+\item \code{--mode=c} - This option tells \code{astyle} to recognize
+  the source code as C code.
+
+\item \code{--indent-switches} - This option tells \code{astyle} to
+  indent the \code{case} statements in a \code{switch}.  Do not also
+  specify \code{--indent-cases}.
+
+\item \code{--indent-labels} - This option tells \code{astyle} to add
+  indentation to labels so they are indented one level less than the
+  current level.
+
+\item \code{--min-conditional-indent=0} - This option tells
+  \code{astyle} not to add indentation to successive lines of
+  multiple-line conditional statements.
+
+\item \code{--max-instatement-indent=20} - This option tells
+  \code{astyle} to limit the total amount of indentation to 20 spaces.
+
+\item \code{--pad=oper} - This option tells \code{astyle} to add
+  padding about binary operators.
+
+\item \code{--brackets=break} - This option tells \code{astyle} to
+  break brackets from their pre-block statements, dropping them to the
+  next line.
+
+\item \code{--convert-tabs} - This option tells \code{astyle} to
+  convert tabs into the equivalent number of space characters.
+
+\item \code{--indent=spaces=4} - This option tells \code{astyle} to
+  use 4 spaces per indent level.
+\end{itemize}
+
+\bibliographystyle{plain}
+\bibliography{panstarrs}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/misc/dualconv.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/dualconv.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/dualconv.tex	(revision 22322)
@@ -0,0 +1,200 @@
+%%% $Id: dualconv.tex,v 1.1 2007-12-14 02:22:06 price Exp $
+\documentclass[panstarrs]{panstarrs}
+
+% basic document variables
+\title{Dual Convolution Image Subtraction}
+\subtitle{Formulation}
+\shorttitle{Dual Convolution}
+\author{Paul Price}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-???}
+
+\setlength{\topsep}{-2pt}
+  
+\begin{document}
+\maketitle
+\sloppy
+
+% -- Revision History --
+% provide explicit values for the old versions
+% use '\theversion' for the current version (set above)
+% use \hline between each table row
+\RevisionsStart
+% version  Date            Description
+DR & 2007 Dec 13 & Draft \\ \hline
+\RevisionsEnd
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\DocumentsInternal
+\DocumentsExternal
+\DocumentsEnd
+
+\tableofcontents
+\pagebreak 
+\pagenumbering{arabic}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Introduction}
+
+The current state-of-the-art in image subtraction uses the algorithm
+of Alard (\code{2000A&AS..144..363A}), which builds on the original
+algorithm of Alard \& Lupton (\code{1998ApJ...503..325A}).  However,
+these algorithms cannot deal with the case where the PSFs are
+misaligned.  That is, if the PSFs are both elongated, and the
+direction of the elongation is not identical, then the Alard (2000)
+algorithm cannot provide a good subtraction.
+
+We might attempt to get around this case by using a dual convolution
+--- convolving both images to a common PSF.
+
+\section{Formulation}
+
+Given images $I_1(x,y)$ and $I_2(x,y)$, we wish to solve for the
+convolution kernels $k_1(u,v)$ and $k_2(u,v)$, that produce:
+\begin{equation}
+k_1(u,v) \otimes I_1(x,y) + \Delta bg = k_2(u,v) \otimes I_2(x,y) + I_2(x,y)
+\end{equation}
+
+We have explicitly included an unconvolved $I_2(x,y)$ on the RHS
+because our intention is that the convolution kernel $k_2$ has a sum
+of zero (i.e., its purpose is merely to broaden the PSF in $I_2$).
+Convolution kernel $k_1$ will have a non-zero sum, and allow the
+matching of the normalisation between the images.  
+
+We proceed by minimising:
+\begin{equation}
+\chi^2 = \sum_{x,y} \left[ \frac{k_1(u,v) \otimes I_1(x,y) + \Delta bg - \sum_{x,y} k_2(u,v) \otimes I_2(x,y) - I_2(x,y)}{\sigma(x,y)} \right]^2
+\label{eqn:chi2}
+\end{equation}
+
+If we express the kernels as a linear combination of basis functions,
+this becomes a linear problem which can be solved (relatively)
+quickly:
+\begin{eqnarray}
+k_1(u,v) & = & \sum_i a_i f_i(u,v) \\
+k_2(u,v) & = & \sum_i b_i g_i(u,v)
+\end{eqnarray}
+
+In order to get the normalisation behaviour we desire, we actually
+use:
+\begin{eqnarray}
+k_1(u,v) & = & a_1 \delta_{u0} \delta_{v0} + \sum_{i>1} a_i f_i' \\
+k_2(u,v) & = & \sum_{i>1} b_i g_i'
+\end{eqnarray}
+where $\delta_ij$ is the Kronecker delta, and:
+\begin{equation}
+f_i' = \begin{cases}
+  f_i / \sum_{u,v} f_i - \delta_{u0} \delta_{v0} & \text{if $f_i$ is an even function} \\
+  f_i & \text{if $f_i$ is an odd function}
+  \end{cases} \\
+\end{equation}
+and similarly for $g_i$.
+
+Then we take the derivative of equation \ref{eqn:chi2} w.r.t. $a_j$
+and $b_j$ to yield the following two equations:
+\begin{eqnarray}
+\sum_{x,y} [ \sum_i a_i A_i(x,y) - \sum_i b_i B_i(x,y) - I_2(x,y) ] A_j(x,y) & = & 0 \\
+\sum_{x,y} [ \sum_i a_i A_i(x,y) - \sum_i b_i B_i(x,y) - I_2(x,y) ] B_j(x,y) & = & 0
+\end{eqnarray}
+where we have written:
+\begin{equation}
+A_i(x,y) = \begin{cases}
+  1 & \text{if $i = 0$ (background matching);} \\
+  I_1(x,y) & \text{if $i = 1$ (normalisation matching);} \\
+  f_i(u,v) \otimes I_1(x,y) & \text{for all the rest.}
+\end{cases}
+\end{equation}
+and
+\begin{equation}
+B_i(x,y) = g_i(u,v) \otimes I_2(x,y)
+\end{equation}
+
+More general background matching might be accomplished by generalising
+the above expression for $A_0$ (replacing with multiple polynomial
+terms), but we will content ourselves with a constant background
+difference here.
+
+We therefore obtain the following equations:
+\begin{eqnarray}
+\sum_{x,y} \sum_i a_i A_i(x,y) A_j(x,y) & = & \sum_{x,y} \sum_i b_i B_i(x,y) A_j(x,y) + \sum_{x,y} I_2(x,y) A_j(x,y) \\
+\sum_{x,y} \sum_i a_i A_i(x,y) B_j(x,y) & = & \sum_{x,y} \sum_i b_i B_i(x,y) B_j(x,y) + \sum_{x,y} I_2(x,y) B_j(x,y)
+\end{eqnarray}
+which may be written as a pair of coupled matrix equations:
+\begin{eqnarray}
+A a & = & C^T b + d \\
+C a & = & B b + e
+\end{eqnarray}
+where $a$ and $b$ are column vectors containing the coefficients of interest, and:
+\begin{eqnarray}
+A_{ij} & = & \sum_{x,y} A_i(x,y) A_j(x,y) \\
+B_{ij} & = & \sum_{x,y} B_i(x,y) B_j(x,y) \\
+C_{ij} & = & \sum_{x,y} A_i(x,y) B_j(x,y) \\
+d_i    & = & \sum_{x,y} A_i(x,y) I_2(x,y) \\
+e_i    & = & \sum_{x,y} B_i(x,y) I_2(x,y)
+\end{eqnarray}
+
+Note that matrix $C$ is not necessarily square, even if we are using
+the same basis functions in $k_2$ as in $k_1$, because of the
+additional elements for background and normalisation matching.
+
+The solution to the matrix equations are:
+\begin{eqnarray}
+a & = & (C^T B^{-1} C - A)^{-1} (C^T B^{-1} e - d) \\
+b & = & B^{-1}(C a - e)
+\end{eqnarray}
+
+\section{Additional considerations}
+
+In practise, the above sums over $x,y$ are performed over selected
+sub-images (``stamps''), rather than the entire image.  Once a
+solution is obtained, it is applied to the stamps, which may be
+rejected and replaced in favour of another (e.g., the original stamp
+was a cosmic ray, or unmasked saturated star); the process is repeated
+until a satisfactory solution is found.
+
+Spatial variation of the kernels may be achieved by writing
+\begin{eqnarray}
+a_i(x,y) & = & \sum_j \alpha_j P_j(x,y) \\
+b_i(x,y) & = & \sum_j \beta_j P_j(x,y)
+\end{eqnarray}
+where $P_j$ is a polynomial term in $x$ and $y$.  Then we do the same
+calculations as before, except we add an additional iteration over the
+polynomial terms, and end up solving for the $\alpha_i$ and $\beta_i$.
+As pointed out in Alard (2000), this doesn't add much in terms of
+computation time, because the main operations are the generation of
+the $A_i$ and $B_j$, and the summing over the products of these to
+generate the least-squares matrices and vectors.
+
+If the basis functions for the kernels $k_1$ and $k_2$ are the same,
+then the solution might be close to degenerate because a solution can
+be found by making the $k_1$ and $k_2$ as wide as possible using the
+same elements of the basis function.  In this case, we can add an
+additional term to equation \ref{eqn:chi2} to force the coefficients
+of the same terms to be different:
+\begin{equation}
+\chi^2_{\rm force} = \chi^2 - w \sum_i (a_i^2 - b_i^2)
+\end{equation}
+where $w$ is a weighting parameter (read: fudge factor) to make the
+forcing term to be of the same magnitude as $\chi^2$ (so its effect is
+neither diluted nor emphasised).  Note that this forumulation means
+that the sign of $a_i$ and $b_i$ doesn't matter, but what matters is
+that $b_i$ is small when $a_i$ is large.  This change to $\chi^2$ has
+the effect of adding an additional term to the diagonals of the square
+matrices:
+\begin{eqnarray}
+A_{ij} & = & \sum_{x,y} A_i(x,y) A_j(x,y) - w \delta_{ij} \\
+B_{ij} & = & \sum_{x,y} B_i(x,y) B_j(x,y) + w \delta_{ij}
+\end{eqnarray}
+
+
+\bibliographystyle{plain}
+\bibliography{panstarrs}
+
+\end{document}
+
Index: /tags/sj_tags/sj_root_20080929/doc/misc/imageCombination.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/imageCombination.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/imageCombination.tex	(revision 22322)
@@ -0,0 +1,387 @@
+%%% $Id: imageCombination.tex,v 1.2 2005-04-08 19:51:57 price Exp $
+\documentclass[panstarrs]{panstarrs}
+
+% basic document variables
+\title{Image Combination Algorithm}
+\subtitle{A Recommendation}
+\shorttitle{Modules SDRS}
+\author{Paul Price}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-???}
+
+\setlength{\topsep}{-2pt}
+  
+\begin{document}
+\maketitle
+\sloppy
+
+% -- Revision History --
+% provide explicit values for the old versions
+% use '\theversion' for the current version (set above)
+% use \hline between each table row
+\RevisionsStart
+% version  Date            Description
+DR & 2004 Dec 09 & Draft \\ \hline
+\RevisionsEnd
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\DocumentsInternal
+PSDC-430-011  &   Pan-STARRS PS-1 IPP System/Subsystem Design Description \\ \hline
+\DocumentsExternal
+Posix Standard & Open Group Based Specifications Issue 6, IEEE Std 1003.1, 2003 \\
+\DocumentsEnd
+
+\tableofcontents
+\pagebreak 
+\pagenumbering{arabic}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Introduction}
+
+The goals of the \PS{} system can be (overly?) simply expressed
+as the following:
+\begin{enumerate}
+\item To make deep images of the static sky; and
+\item To identify variable/moving objects
+\end{enumerate}
+For the prototype telescope, PS1, multiple images will be taken
+consecutively, while for the full \PS{} system, PS4, multiple
+images will be taken simultaneously.  In either case, there is a need
+to combine multiple images.
+
+Cosmic-ray (CR) hits on the detectors have the potential to influence
+both of the goals of the \PS{} system through corrupting certain
+pixels on the detectors.  The addition of a pixel influenced by a CR
+hit into the static sky image can result in apparent objects not
+really present on the sky, and affect the photometry of real objects.
+Consequently, CRs will also produce false detections of apparently
+variable objects.  Hence it is necessary to flag pixels influenced by
+CRs so that they are not propagated to the combined image from the
+input images.
+
+The purpose of this document is to propose an algorithm suitable for
+combining multiple images in the \PS{} system.
+
+\section{Requirements}
+
+We assume at the outset that multiple images (motivated by the full
+\PS{} system concept, we invariably adopt four input images in this
+study) have been obtained and photometrically calibrated such that
+counts are proportional to flux from the sky, with the relative
+scalings between the images known, along with estimates of the Poisson
+error for each pixel (which may be calculated from the scaling, gain,
+readnoise and background).  In addition, we also assume that the
+images have been astrometrically calibrated, such that a
+transformation for each image to (and from) a common output coordinate
+system is known.  This is the case in the \PS{} Image Processing
+Pipeline (IPP) at the commencement of the Phase 4 processing.  It is
+worth mentioning that we expect that the input images have similar
+seeing, since no effort is budgeted in this step for PSF matching.
+
+The task before us is to take these images and combine them (which, in
+addition to simply combining, also involves transforming the images to
+the common output coordinate system), rejecting those pixels from the
+combination which are affected by CRs, while leaving untouched those
+pixels which remain unaffected by CRs.  In particular those pixels
+which comprise the cores of stars (even stars which are, to some
+degree, undersampled) should not be rejected.
+
+What are the specific requirements for this image combination?  Of
+course, there will always be a trade-off between the fraction of CRs
+on the image which will be identified and rejected, and the fraction
+of ``clean'' pixels on the image which are falsely identified as CRs
+--- such a trade-off is always unavoidable since there is noise in the
+input images, and since the input images are populated by stars which
+may be (as will be the case for \PS{} in excellent seeing)
+undersampled.  We propose the following requirements:
+\begin{enumerate}
+\item {\bf Given four input images with stars at a density of 400 per
+  512$^2$ pixels with total flux distributed as $N(m<M) \propto
+  10^{0.35M}$ down to 21.5 mag (using a magnitude zero point of
+  25~mag) with a FWHM of 2~pixels, the fraction of detector pixels
+  that are falsely rejected as CRs shall not exceed 1\%.}  This value
+  corresponds to an order of magnitude less than an estimate of the
+  fraction of the focal plane not covered by working detectors, so
+  that the rejection of pixels in the image combination will not be
+  the major determining factor in the working detector area obtained
+  in each exposure.  The seeing here is the median seeing expected for
+  PS4 (0.6'' with 0.3'' pixels), the limiting magnitude and magnitude
+  zero point are appropriate for PS1, the stellar density is that
+  expected for $b \sim 10^\circ$ down to $V=22$~mag ($2\times10^5$ per
+  square degree) which we obtain from the PS1 DRM, and the apparent
+  magnitude distribution corresponds to that expected in the AP
+  survey.
+
+\item {\bf Given four input images which overlap in the output
+  coordinate frame (having the same scale as the input coordinate
+  frames, and random, subpixel shifts), and are completely devoid of
+  stars, consisting only of a flat background with Gaussian noise of
+  standard deviation $\sigma$ and cosmic rays (or hot pixels) with
+  (single pixel) flux (added to the background) equally distributed
+  from 0 to $20\sigma$ covering a fraction of 0.7\% of each input
+  image, the fraction of pixels in the output image with a flux
+  greater than 1.5$\sigma$ above the background level shall not exceed
+  0.2\%.}  This follows a Gaussian distribution, since the standard
+  deviation expected in the combined image would be $\sigma/2$, and
+  hence we expect 99.8\% of pixels to lie within $3\sigma/2$ if the
+  CRs have been properly rejected.  The Gaussian distribution serves
+  as an approximation to Poisson noise from the sky.  The number of CR
+  pixels are chosen to match the number of CR pixels measured on an
+  OPTIC dark (0.62\%; this value should be diluted when sky noise is
+  added), while the CR flux distribution is chosen to be biased
+  towards low flux events which are more difficult to detect than
+  bright events; consequently, this is a strong test of the
+  algorithm's ability to identify and mask CRs.
+\end{enumerate}
+
+To these, we may add the requirement that the implementation of any
+proposed algorithm must satisfy the throughput requirements of the
+IPP.
+
+
+\section{Approach}
+
+CRs (along with other artifacts that we might seek to reject) have two
+important properties that we may use to identify them in images:
+\begin{enumerate}
+\item They have sharp boundaries instead of a stellar PSF.
+\item They are (generally) not present at the same location on
+multiple images of the same piece of sky.
+\end{enumerate}
+Using both of these methods together should result in clean combined
+images.  Relying solely on measuring the gradients in order to
+identify sharp boundaries would generate difficulties in combining
+critically sampled and under sampled images.
+
+One desirable feature would be to mask CR pixels on the detector
+before transforming the image.  The reason for this is that mapping
+from the detector coordinate system to the output coordinate system
+smears out cosmic rays, so that an event that affected only a single
+pixel on the detector may affect multiple pixels on the output image.
+The result of this is that CRs in the output image consist of a core
+of high flux plus wings in the surrounding pixels.  While the core may
+be easily identified through comparison with other images, the wings
+are harder to pick up.  Some combination methods (e.g., Tonry's {\tt
+autoclean}) attempt to identify the wings by using a lower threshold
+in the combination for pixels near pixels already identified as CRs,
+but a cleaner solution would be to mask the CR before mapping.
+Consequently, we propose an algorithm that transforms the input
+images, combines with rejection, identifies those pixels on the source
+images that correspond to the rejected pixels in the combination, and
+mask those before repeating the combination.  This process is used to
+reduce images from HST using Drizzle/Multidrizzle.
+
+The algorithm proposed here, therefore, maps the input images to the
+output coordinate system, and combines the transformed images with
+rejection.  Then, for each of the input images, a map is made of those
+pixels that were rejected in the combination (set to one if the pixel
+was masked, zero otherwise), and this map is transformed back to the
+coordinate system of the original input image.  Pixels in this
+transformed map that exceed a specified threshold (\code{frac}), and
+for which the local gradient is less than another specified threshold
+(\code{grad}) are masked as cosmic rays.  The impact of these maskings
+on the combined image is then propagated through the transformation
+and combination (with no rejection) stages to yield a clean, combined
+image.
+
+A short comment about the calculation of the local gradient is
+necessary.  Simply calculating the gradient from the image in which a
+pixel is suspected of being a CR can be problematic.  Firstly, CRs are
+not always single pixel events, but often consist of streaks in which
+several pixels are affected, which can bias the calculation of the
+gradient.  And secondly, in the case where the seeing yields
+critically sampled (or even under sampled) images, the gradient in the
+core of a star can be quite high, causing it to be falsely identified
+as a CR.  We avoid these problems by calculating the median flux of
+neighboring pixels on the other input images (using the known
+coordinate transformations), and using the mean difference of these
+from the flux of the suspect CR pixel.  Because we look at the other
+images, which should be devoid of CRs at the corresponding position,
+the first problem is circumvented; and because of the slight
+(sub-pixel) shifts between the input images, the gradient can be
+downplayed in the case that the pixel is due to the core of a star,
+and so the second problem is minimized.
+
+\tbd{Discussion about error images}
+
+\section{Implementation}
+
+We designate our implementation of the above algorithm ``STAC'', for
+Simultaneous Telescope Array Combination.  It is built on top of the
+\PS{} Library (currently \code{psLib-rel5alpha1}), making use of its
+vectors, arrays, images, polynomial transformations, statistics and
+tracing functionalities.
+
+The current version does not meet the speed requirements of the IPP,
+but we expect that optimising the code for speed should be fairly
+simple (currently, the forward--backward--forward methodology
+described above is implemented for every pixel of each input image,
+but it need only be implemented for pixels which trigger certain
+conditions).  We set this requirement aside for the time being,
+confident of meeting it in the future, and concern ourselves with the
+performance of the algorithm in detecting CRs.
+
+Given input images with no CRs (according to the first requirement
+above), STAC falsely identifies $22+109+161+47 = 339$ pixels as CRs in
+the four individual $512\times512$ input images, or 0.03\% of the
+total number of input pixels.  This meets the requirement by over an
+order of magnitude.
+
+Given input images with 2000~CRs (according to the second requirement
+above), STAC identifies $1692+1222+1256+1624 = 5794$ pixels as CRs in
+the four individual $512\times512$ input images, leaving 29 pixels in
+the combined image exceeding 1.5~times the estimated standard
+deviation of the input images, or 0.01\% of the pixels in the output
+image.  This meets the requirement by an order of magnitude.
+
+Since it meets both of the proposed requirements, STAC (modulo the
+speed issue, which should be easily solved) is suitable for use in the
+\PS{} IPP.
+
+\section{Alternatives}
+
+Here we examine two alternatives to STAC, as a check to our
+recommendation.  The first alternative is a completely naive
+combination of the shifted input images using IRAF's \code{imcombine}
+(with appropriate rejection).  The second is the more sophisticated
+\code{autoclean} developed by John Tonry, which also acts on shifted
+input images, but uses a number of rejection thresholds to identify
+and reject faint CRs.  We will apply the same tests to these
+alternatives as for STAC.
+
+\subsection{imcombine}
+
+We combine the shifted input images using \code{avsigclip} rejection:
+
+\begin{verbatim}
+Dec 14 15:19: IMCOMBINE
+  combine = average, scale = none, zero = none, weight = none
+  reject = avsigclip, mclip = yes, nkeep = 1
+  lsigma = 3., hsigma = 3.
+  blank = 0.
+                Images 
+    test_0.fits.shift.1
+    test_1.fits.shift.1
+    test_2.fits.shift.1
+    test_3.fits.shift.1
+\end{verbatim}
+
+Given input images with no CRs, \code{imcombine} identifies 5552
+pixels as CRs, or 0.5\% of the total number of input pixels.  This
+satisfies the requirement by a factor of two.  The combined image
+appears noisier than the output image produced by STAC (standard
+deviations of 3.6 compared to 3.2).
+
+Given input images with 2000~CRs each, \code{imcombine} leaves 495
+pixels in the combined image exceeding the threshold, or 0.19\%.  This
+barely satisfies the requirement.  The histogram of pixel values shows
+a distinct tail at the high end.
+
+We conclude that STAC performs better in both departments.
+
+\subsection{autoclean}
+
+We combine the shifted input images using the following
+\code{autoclean} command line:
+
+\begin{verbatim}
+autoclean test.000 4 manual scale=unity zero=zero float average mask trigger=0,2.7,2.0 grad=3 domino=3 eadu=1
+\end{verbatim}
+
+Using this setup, we performed the same tests as for the other
+combination methods.  In the test on a stellar field with no CRs,
+\code{autoclean} falsely identifies 81 pixels as CRs, or an amazingly
+low 0.007\%.  In the test on a blank field with CRs added,
+\code{autoclean} masks 11286 pixels as CRs, leaving 412 pixels above
+the background, or 0.16\% of the area.  As in the case of
+\code{imcombine}, this satisfies the requirement, but is still a
+factor of 4 above that of STAC; and the histogram still displays a
+tail.
+
+Note that the number of pixels masked here cannot be compared directly
+with that of STAC, since the latter masks in the source coordinate
+fram, while the former masks in the output frame where each CR pixel
+from the source occupies multiple pixels.  Dividing by 4 should yield
+a more direct comparison, since sub-pixel shifting in both $x$ and $y$
+results in (single pixel) CRs being smeared over 4 pixels.  Doing
+this, \code{autoclean} masks 2822 CRs in the source, while STAC masks
+5875.
+
+We tried other parameters in an attempt to increase the number of
+rejected CRs.  Brian Schmidt uses the following as his choice of
+default:
+\begin{verbatim}
+autoclean test.000 4 manual scale=unity zero=zero float average mask eadu=1 scour=50,4.5,2.5
+\end{verbatim}
+This results in 9862 pixels masked, and 545 pixels above the
+threshold.
+
+Combining the \code{trigger} and \code{scour} options:
+\begin{verbatim}
+autoclean test.000 4 manual scale=unity zero=zero float average mask eadu=1 trigger=0,2.7,2.0 scour=50,4.5,2.5
+\end{verbatim}
+This results in 11713 pixels masked, and 239 pixels above the
+threshold.
+
+Lowering the thresholds for masking pixels:
+\begin{verbatim}
+autoclean test.000 4 manual scale=unity zero=zero float average mask eadu=1 trigger=0,2.0,1.5 scour=0,1.5,1.0
+\end{verbatim}
+This results in 12935 pixels masked, and 293 pixels above the
+threshold.
+
+The best result we achieved was:
+\begin{verbatim}
+autoclean test.000 4 manual scale=unity zero=zero float average mask eadu=1 trigger=200,1.5,1.0 scour=200,1.0,0.5
+\end{verbatim}
+For this, 15419 pixels were masked, resulting in 123 pixels above the
+threshold.  Using the same parameters, no pixels are identified as CRs
+when using the stellar field devoid of CRs, but this is likely due to
+it having a different background level.  Raising the background to the
+same level as for the CR image, \code{autoclean} falsely identifies 95
+pixels as CRs, whereas STAC identifies 15 for the same field.
+
+We conclude that STAC has slightly better performance than
+\code{autoclean}, but \code{autoclean}'s performance is still very
+good.
+
+The difference in speed between the two codes is, of course
+impressive, with \code{autoclean} being much faster than STAC.  Of
+course, STAC's speed is yet to be optimised, and \code{autoclean}
+doesn't do the transformations, so the comparison isn't fair.
+
+\section{Conclusion}
+
+STAC and \code{autoclean} both perform well.  STAC performs slightly
+better; \code{autoclean} is much faster (by an order of magnitude ---
+0.35 seconds versus 3.5 seconds for STAC).  However, there exist
+simple optimisations that can be made within STAC which will vastly
+improve the speed.  If STAC can be optimised to meet the time budget
+for Phase 4, and close to \code{autoclean}, then it should be accepted
+on the basis of its superior performance.  If the time budget is
+tight, then \code{autoclean} could be used profitably.
+
+\appendix
+
+\section{Addendum: Timing}
+
+Optimisation of the STAC code results in 60 sec to combine a 4k square
+image consisting of stars and CRs on \code{alala}, a dual Opteron
+2.2~GHz machine.  This appears to be within the time budget for Phase
+4.  The initial transformation accounts for over half of this time
+(about 37 sec).  \code{autoclean} takes approximately 20 sec
+(including I/O) to do the combination.  We conclude that timing is no
+longer an issue.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bibliographystyle{plain} \bibliography{panstarrs}
+
+\end{document}
+
Index: /tags/sj_tags/sj_root_20080929/doc/misc/imageSubtraction.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/imageSubtraction.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/imageSubtraction.tex	(revision 22322)
@@ -0,0 +1,267 @@
+%%% $Id: imageSubtraction.tex,v 1.2 2005-03-04 02:48:52 price Exp $
+\documentclass[panstarrs]{panstarrs}
+
+% basic document variables
+\title{Image Subtraction Algorithm}
+\subtitle{Exploring an Alternative}
+\shorttitle{Modules SDRS}
+\author{Paul Price}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-???}
+
+\setlength{\topsep}{-2pt}
+  
+\begin{document}
+\maketitle
+\sloppy
+
+% -- Revision History --
+% provide explicit values for the old versions
+% use '\theversion' for the current version (set above)
+% use \hline between each table row
+\RevisionsStart
+% version  Date            Description
+DR & 2005 Feb 24 & Draft \\ \hline
+\RevisionsEnd
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\DocumentsInternal
+PSDC-430-011  &   Pan-STARRS PS-1 IPP System/Subsystem Design Description \\ \hline
+\DocumentsExternal
+Posix Standard & Open Group Based Specifications Issue 6, IEEE Std 1003.1, 2003 \\
+\DocumentsEnd
+
+\tableofcontents
+\pagebreak 
+\pagenumbering{arabic}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Introduction}
+
+The goals of the \PS{} system can be (overly?) simply expressed
+as the following:
+\begin{enumerate}
+\item To make deep images of the static sky; and
+\item To identify variable objects.
+\end{enumerate}
+
+We have explored the first of these goals in a companion document
+on image combination.  Here, we explore the second goal.
+
+By ``identification of variable objects'', we mean recognising a
+source which has been detected in an image but was not detected at the
+same position in a template image.  There are two methods which are
+used to identify variable objects.
+
+The first method simply generates a catalog of all sources on each
+image and compares the two catalogs.  This is a fast and simple method
+for identifying variable sources, but suffers from a strong drawback:
+it does not account for differences between the point spread functions
+(PSFs) of the two images.  For example, say that the template image is
+taken in poor seeing, and the new image has been taken in excellent
+seeing.  What appears to be a single star in the template image might
+be resolved as a double star in the input image, leading to the false
+identification of a variable object.  This problem has led to the
+general rejection of this method for identifying variable sources in
+major synoptic surveys, apart from under-sampled systems such as
+RAPTOR and ROTSE.
+
+The second method overcomes the differences in PSFs between the
+images, but it is more computationally expensive.  It involves
+matching the PSFs between the two images by solving for the
+appropriate convolution kernel.  This method has been in use for quite
+a while, but the most famous and generally used implementation is that
+of Alard (2000), known as ISIS
+(\href{http://www2.iap.fr/users/alard/package.html}{website}).
+
+ISIS provides a fast method of solving for a convolution kernel that
+matches the PSFs in each image.  The resultant subtracted image has
+deviations close to that expected for a Poisson distribution.  ISIS
+uses a linear combination of Gaussians modified by polynomials as a
+basis set for the convolution kernel, avoiding the tortuously slow
+approach of non-linear fitting using a more physically motivated
+kernel.  One obvious question, then, is why is ISIS so successful?  In
+some cases, especially around brighter stars, ISIS leaves systematic
+residuals visible in the subtracted image.
+
+In this document, we explore the possibility of using a more general
+image subtraction algorithm, in the expectation that adding generality
+would allow smaller systematic residuals in the subtracted images.
+One important issue, which is how to robustly determine which
+residuals in the subtracted image are real, and which are simply part
+of the afore-mentioned systematic residuals, is not dealt with here,
+but deferred in the expectation that a clean subtracted image (with a
+minimum of systematic residuals) will simplify this matter.  We are,
+for now, solely concerned with achieving the cleanest possible image
+subtraction, believing that this is the best way to robustly identify
+variable sources.
+
+\section{Convolution Kernels}
+
+PSF matching is achieved by solving for the convolution kernel:
+\begin{equation}
+I(x,y) = R(x,y) \otimes K(u,v)
+\end{equation}
+where $I(x,y)$ is the input image, $R(x,y)$ is the reference image,
+$K(u,v)$ is the convolution kernel, and $\otimes$ denotes convolution.
+
+The speed of ISIS comes from choosing to represent the convolution
+kernel as a linear combination of basis functions:
+\begin{equation}
+K(u,v) = \sum a_i B_i(u,v)
+\end{equation}
+
+In this case, the problem of minimising $\chi^2$ reduces to solving a
+matrix equation:
+\begin{equation}
+b_i = \sum_j M_{ij} a_j
+\end{equation}
+where
+\begin{equation}
+b_i = \sum_{x,y} I(x,y) [ R(x,y) \otimes B_i(u,v) ] / \sigma(x,y)^2
+\end{equation}
+and
+\begin{equation}
+M_{ij} = \sum_{x,y} \left[ R(x,y) \otimes B_i(u,v) \right] \  \left[ R(x,y) \otimes B_j(u,v) \right] / \sigma(x,y)^2
+\end{equation}
+Here the sum over $x,y$ indicates summing over select regions (known
+as ``stamps'') that are known not to be variable (or, in practise,
+assumed not to be variable, with iterative rejection of outliers).
+$\sigma(x,y)$ is the error map (which can easily be constructed from
+the input images by assuming that the noise is Poisson).
+
+In this section, we consider two choices for the basis functions,
+$B_i(u,v)$.
+
+\subsection{ISIS}
+
+ISIS chooses for its basis functions a set of Gaussians, each modified
+by a polynomial:
+\begin{equation}
+B_{ijk}(u,v) = e^{(u^2 + v^2)/2\sigma_i^2} u^j v^k
+\end{equation}
+
+Alard \& Lupton (1998) claim that, ``In practice, it seems that three
+Gaussian components with associated polynomial degrees in the range of
+2 to 6 can give subtracted images with residuals comparable to
+$2^{1/2} \times$ photon noise.''
+
+It is not immediately clear why such a solution would work well ---
+there is no physical motivation for this choice of basis functions.
+Nevertheless, it does seem to work well, and is widely used.
+
+\subsection{POIS}
+
+Pricey's Optimal Image Subtraction (POIS) tries a different choice of
+basis functions --- a set of delta functions:
+\begin{equation}
+B_{ij}(u,v) = \delta(u - i)\ \delta(v - j)
+\end{equation}
+In essence, this choice of basis functions amounts to measuring (a
+discrete version of) the kernel directly, and is therefore
+intrinsically more flexible than ISIS' choice of basis functions.
+While this could conceivably yield better subtractions, it may also
+introduce a couple of problems:
+\begin{itemize}
+\item Speed: the number parameters goes as the number of pixels in the
+  kernel.  Especially when adding spatial dependence to the kernel,
+  this can lead to a very large number of matrix elements.
+\item Noise: neighbouring pixels in the kernel are not correlated,
+  which raises the possibility of the noise being increased relative
+  to a kernel which is smooth.
+\end{itemize}
+
+\section{Test cases}
+
+We will place the two kernels in a head-to-head competition to produce
+the smallest stellar residuals.  May the best kernel win.
+
+In comparing the stellar residuals, there are two quantities of
+interest:
+\begin{enumerate}
+\item The deviation, $\sum_{x,y} s(x,y)$; and
+\item $\chi^2 = \sum_{x,y} [ s(x,y) / I(x,y) ]^2$.
+\end{enumerate}
+where $s(x,y) = I(x,y) - R(x,y) \otimes B(u,v)$ is the subtracted
+image.  The first simply looks at the absolute deviation from what is
+expected (zero), while the second is a measure of the deviation
+relative to the noise.  We will consider these quantities, or rather,
+the distribution of these quantities, for both kernel choices.
+Obviously, lower values indicate a better subtraction.
+
+\subsection{Test data}
+
+Our test data consists of images taken with MegaCam of a photometric
+standard field.  We will examine two chips --- one near the centre of
+the mosaic, and another near the corner.  The former is expected to be
+relatively free from spatial variations of the PSF, while the former
+may suffer strongly from these effects.  It is worth noting, however,
+that the pointing of the observations were not very different, and so
+spatial variation may not be an issue.
+
+\subsection{Results}
+
+We are not using any spatial variation of the kernel.  The quantities
+are being measured within a 5 pixel radius aperture, centered on stars
+detected in the image.  We use clipping (2 iterations at 3.5$\sigma$)
+to obtain statistics on the population unaffected by small issues
+(such as very bright stars which haven't been properly masked).
+
+\begin{table}[h]
+\begin{tabular}{|l|r|r|c|r|r|c|} \hline
+Dataset & \multicolumn{3}{c|}{Deviation} & \multicolumn{3}{c|}{$\chi^2$} \\
+        & Mean & Std.\ Dev.\  & Clipped & Mean & Std.\ Dev.\ & Clipped \\ \hline
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Centre, ISIS & -58 & 221 & 32/884 & 0.6 & 3.2 & 9/884 \\
+Centre, POIS & -7 & 238 & 31/884 & 0.5 & 2.3 & 9/884 \\
+Edge, ISIS & -224 & 269 & 102/2968 & 0.05 & 0.19 & 67/2968 \\
+Edge, ISIS & -281 & 278 & 107/2968 & 0.04 & 0.14 & 67/2968 \\
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\hline
+\end{tabular}
+\end{table}
+
+These test cases indicate that the POIS kernel is able to achieve
+similar deviations as ISIS, but it has a better distribution of
+$\chi^2$.  This is clearly not due to clipping more values when
+calculating the statistics, but indicates a real improvement in the
+quality of the subtraction.
+
+\begin{figure}[h]
+\psfig{file=subtraction.ps,width=15cm,angle=0}
+\caption{Input image (left), ISIS subtraction (centre) and POIS
+subtraction (right).  The stretch in the subtractions are the same.}
+\end{figure}
+
+\subsection{Timing}
+
+An optimised (\code{-O2}) version version of POIS performs the
+subtraction on the MegaCam images ($2112\times 4644$ pixels) on
+\code{alala} (dual Opteron 2.2~GHz; note that the code is not
+multithreaded) in 15 sec (including I/O), using a $9\times 9$ kernel.
+
+On \code{mithrandir} (dual Xeon 2.4~GHz), POIS runs in 14~sec,
+compared to ISIS in 41~sec.  It is worth noting that the ISIS
+implementation uses strictly integer arithmetic, which should be
+faster than the floating point arithmetic of POIS.
+
+\section{Conclusion}
+
+Our head-to-head comparison indicates that POIS performs a subtraction
+of comparable quality to ISIS, but is faster by a factor of a few.  We
+are yet to compare the quality of subtractions where there is a low
+density of stars with good signal-to-noise; in this regime, ISIS might
+provide a better subtraction than POIS, simply because the POIS kernel
+pixels are not correlated.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bibliographystyle{plain} \bibliography{panstarrs}
+
+\end{document}
+
Index: /tags/sj_tags/sj_root_20080929/doc/misc/modelSoftwareUsers.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/modelSoftwareUsers.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/modelSoftwareUsers.txt	(revision 22322)
@@ -0,0 +1,58 @@
+o Public Software Distribution
+    - package source tarballs
+    - aggregate source tarball
+    - utility to install ps sources tarballs + external dependencies
+
+o Developer Software Distribution
+    - utility to install ps sources from CVS + external dependencies
+
+o Production Environment Control
+    - operating system specific software packages
+
+--
+
+Scenario #1) typical organizational end-user
+
+The user wants to use application x.y.z. that has a dependency on pslib.  Odds
+are that the end-user has no idea the application even has a dependency pslib,
+or any other library, as all they care about is the functionality of the
+application.  A support request is filed with the in house support agency.  A
+short period of time later (1+ months), a systems administrator will download
+x.y.z and discover the dependency on pslib look for the website.  The software
+(pslib) will be downloaded in tarball form, compiled, and installed on the
+system under /usr or /usr/local.
+
+Mode of distribution: www/tarball or www/rpm
+
+Scenario #2) unsupported organizational end-user w/o root access
+
+Same as Scenario #1 except that they will have to do the install themselves
+and into their home directory.  If they have had to do this sort of install
+before they are likely to be comfortable building from tarballs.  The faint
+can do the install with jhbuild. 
+
+Mode of distribution: www/mega-tarball, jhbuild/tarball
+
+Scenario #3) Gentoo end-user
+
+User types something along the lines of "sudo emerge -va pslib".  psmodule and
+other PS specific software is not likely to live in the portage tree but
+ebuilds will be provided on the PS website.
+
+Mode of distribution: portage or www/ebuild
+
+Scenario #5) Production server environment
+
+Gentoo admins would follow Scenario #3.  RedHat admins would download RPMs
+from the PS website.  Others would likely download the tarballs and possible
+build RPMs (from the include spec file) if their distribution is RPM based.
+
+Mode of distribution: www/tarball, www/rpm, portage, or www/ebuild
+
+Scenario #6) External application developer
+
+Mode of distribution: www/tarball or www/rpm
+
+Scenario #7) Internal/pslib developer
+
+Mode of distribution: cvs or jhbuild/cvs
Index: /tags/sj_tags/sj_root_20080929/doc/misc/parseErrorCodes.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/parseErrorCodes.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/parseErrorCodes.pl	(revision 22322)
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2004  Somebody
+#
+# $Id$
+
+use strict;
+use warnings;
+
+# Provides functions for handling long command line options
+use Getopt::Long;
+
+my @errorCodes;
+my @errorDescriptions;
+
+my $data = "psErrorCodes.dat";
+my $verbose;
+my $help;
+
+# Assign variables based on the presence of command line options to the script
+GetOptions(
+    "data=s"  => \$data,
+    "verbose" => \$verbose,
+    "help"    => \$help
+);
+
+if ($help) {
+    print "Usage: parseErrorCodes ", "[--data=$data] ", "[--help] ",
+        "[--verbose] filename [filename ...]\n\n";
+    exit(0);
+}
+
+print "Using data file '$data'\n" if $verbose;
+open(my $dataFile, "<", $data) or die "Can not open data file $data";
+
+print "Datafile:\n" if $verbose;
+foreach my $record (<$dataFile>) {
+    chomp $record;
+    if ($record =~ /^\s*\#/) {
+        print "C $record\n" if $verbose;
+    } else {
+        if ($record =~ /^\s*(\w+)\s+([\%\w].*)/) {
+            my $errorCode        = $1;
+            my $errorDescription = $2;
+            print "  $errorCode: '$errorDescription'\n" if $verbose;
+            push(@errorCodes,        $errorCode);
+            push(@errorDescriptions, $errorDescription);
+        } else {
+            print "I $record\n" if $verbose;
+        }
+    }
+}
+close($dataFile) or die "Can not close file $data";
+
+my $found = $#errorCodes + 1;
+print "\nFound $found error codes.\n" if $verbose;
+
+foreach my $filename (@ARGV) {
+    my @result;
+
+    open(my $inFile, "<", $filename) or die "Failed to open input file";
+
+    print "\nOutput File:\n" if $verbose;
+    foreach my $record (<$inFile>) {
+
+        chomp $record;
+        push(@result, $record);
+        if ($record =~ /^\s*\/\/~Start(.*)$/) {
+            my $line = $1;
+            for (my $n = 0; $n < $found; $n++) {
+                $line =~ s/\$1/$errorCodes[$n]/g;
+                $line =~ s/\$2/$errorDescriptions[$n]/g;
+                $line =~ s/\$n/$n/g;
+                push(@result, $line);
+                print "$line\n" if $verbose;
+            }
+
+            my $break = 0;
+            while (($break == 0) && (my $record = <$inFile>)) {
+                if ($record =~ /^\s*\/\/~End/) {
+                    $break = 1;
+                }
+            }
+            chomp $record;
+            push(@result, $record);
+        }
+    }
+
+    close($inFile) or die "Can not close file $filename";
+
+    open(my $outFile, ">", $filename) or die "Failed to overwrite input file";
+
+    foreach my $res (@result) {
+        print $outFile, "$res\n";
+        print "$res\n" if $verbose;
+    }
+
+    close($outFile) or die "Can not close file $filename";
+
+}
Index: /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeConventions.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeConventions.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeConventions.tex	(revision 22322)
@@ -0,0 +1,1674 @@
+%%% $Id: perlCodeConventions.tex,v 1.48 2006-08-18 22:28:38 jhoblitt Exp $
+\documentclass[panstarrs]{panstarrs}
+
+\usepackage{verbatim}
+
+% basic document variables
+\title{Pan-STARRS Image Processing Pipeline}
+\subtitle{Perl Code Standards}
+\shorttitle{Perl Code Standards}
+\author{Joshua Hoblitt}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{DR02}
+\docnumber{PSDC-430-013}
+
+\begin{document}
+\maketitle
+\newcommand{\matchBracket}[1]{}         % help emacs match properly
+
+% -- Revision History --
+\RevisionsStart
+% version     Date         Description
+DR00& 2004-11-12 & First Draft\\
+\hline
+DR01& 2004-12-01 & Second Draft\\
+\hline
+DR02& 2005-01-24 & Third Draft\\
+\RevisionsEnd
+
+\textbf{\Large Referenced Documents}
+\begin{center}
+\begin{tabular}{|p{1in}|p{2.75in}|p{2.75in}|}
+\hline
+%%% References here.
+\textbf{\PS{} ID} & \textbf{Title} & \textbf{Author} \\
+\hline
+430-004 &Pan-STARRS IPP C Code Standards    &Robert Lupton\\
+\hline
+\end{tabular}
+\end{center}
+
+\pagebreak
+\tableofcontents
+
+\pagebreak 
+\pagenumbering{arabic}
+
+
+%------------------------------------------------------------------------------
+
+\section{Introduction}
+\subsection{Ancestry}
+
+This document is derived from the Pan-STARRS IPP C Code Standards document
+(PSDC 430-004), which is derived from the Sun Microsystems Java language coding
+standards presented in the \emph{Java Language Specification}\footnote{The
+Java Language Specification -
+\code{http://java.sun.com/docs/books/jls/index.html}}.
+
+\begin{verbatim}
+    Adapted with permission from
+    CODE CONVENTIONS FOR THE JAVA^TM PROGRAMMING LANGUAGE.
+    Copyright 1995-1999 Sun
+    Microsystems, Inc.  All rights reserved.
+\end{verbatim}
+
+See the \emph{Java Code Conventions} web site\footnote{The Java Code
+Conventions - \code{http://java.sun.com/docs/codeconv/}} for more details.
+
+Such an adaption is explicitly permitted by the Sun Microsystems
+copyright notice (Copyright.doc.html).
+
+\subsection{Limitations}
+
+Although Perl's syntax is similar to that of C or Java, there are many
+signification deviations.  This has lead to a coding style within the Perl
+community that is generally significantly different than that used for C or
+Java (See the \code{perlstyle} Pod in the standard Perl documentation for an
+example of this).  This document does not attempt to define the coding style
+common with Perl programmers.  Instead, it is an attempt to make the C and Perl
+coding styles as consistent as is reasonably feasible within the Pan-STARRS
+project.  Further limiting the possible applicability of this document, only
+language features that are deemed likely to be used by the Pan-STARRS project
+are discussed.  This has resulted in only a brief discussion of Perl's
+Object-Oriented features and none of it's more advanced features (e.g.
+AUTOLOAD).
+
+\subsection{Applicability}
+
+This document is intended to be relevant to Perl5, version 5.8.0 or later.
+
+\subsection{Conformance}
+
+If any of the conventions established in this document conflict with source
+code that has been formatted by \code{perltidy} (see \S\ref{perltidy}), the
+formatting as output by \code{perltidy} will be considered to be in compliance
+with the Perl Code Standards.
+
+\subsection{To whom should I Complain?}
+
+While Robert Lupton didn't actually write this document he would enjoy
+receiving any complaints or comments it may generate.
+
+\code{<rhl@astro.princeton.edu>}.
+
+
+%------------------------------------------------------------------------------
+
+\section{File Names}
+
+This section lists commonly used file suffixes and names. 
+
+\subsection{File Suffixes}
+
+Software uses the following file suffixes (in lowercase):
+\begin{center}
+\begin{tabular}{ll}
+\textbf{File Type}& \textbf{Suffix}\\
+\hline
+Perl scripts&               \code{.pl}\\
+Perl modules&               \code{.pm}\\
+Plain old documentation&    \code{.pod}\\
+\end{tabular}
+\end{center}
+
+\subsection{Common File Names}
+
+Frequently used file names include: 
+\begin{center}
+\begin{tabular}{lp{5in}}
+\textbf{File Name}& \textbf{Use}\\
+\hline
+\file{Makefile.PL}&
+The preferred name for a file that invokes a ``build'' process.  Typically this
+calls \code{ExtUtils::MakeMaker} or \code{Module::Build} setup the the
+necessary files to build and install a module or compile Swig generated C code.
+\emph{The capitalized \code{.PL} suffix is reserved for build and configuration
+scripts}\\
+\end{tabular}
+\end{center}
+
+
+%------------------------------------------------------------------------------
+
+\section{File Organization}
+
+General rules:
+
+\begin{itemize}
+\item
+A file consists of sections that should be separated by blank lines
+and an optional comment identifying each section.
+
+\item
+Files longer than 1000 lines are cumbersome and should be avoided.
+
+\item
+For an example of a properly formatted program, see
+\emph{Complete Program Example} (\S\ref{SourceExample}).
+
+\item
+``Scripts'' have user documentation included at the end of the file.
+
+\item
+``Modules'' have documentation included in a companion \code{.pod}.
+\end{itemize}
+
+\subsection{Source Files}
+\subsubsection{Perl Scripts}
+
+Script files should have, in the following order:
+
+\begin{itemize}
+\item 
+A Bourne shell ``she-bang'' for \code{perl}.  e.g.  \code{#!/usr/bin/perl}.
+
+\item 
+A Copyright notice followed by a blank commented line.  Then one commented line
+per CVS keyword to be expanded.
+
+\item
+An optional minimum \code{perl} version requirement in long form (vStrings are
+not allowed).  Where \code(requirement = revision + version / 1000 + subversion
+/ 1\_000\_000).  e.g. A minimum requirement of \code{perl} 5.8.5 would be
+expressed as \code{use 5.008005}.
+
+\item 
+A \code{use strict} and \code{use warnings} declaration.
+
+\item
+An optional \code{$VERSION} declaration.  This is useful with
+\code{Getopt::Long}'s \code{auto_version} feature.
+
+\item 
+Any modules to include. 
+
+\item 
+Any constant declarations. 
+
+\item 
+Any script level ``globals'' declared as lexical variables.  e.g. \code{my $foo}
+
+\item
+The functional ``body'' of the script.  This where command line parameter
+parsing, subroutine calls, etc. are placed.
+
+\item 
+Any subroutines.
+
+\item
+A \code{__END__} token to instruct the \code{perl} parser to stop looking for
+executable code.
+
+\item
+Any Pod documentation about the script.
+\end{itemize}
+
+For an example of a properly organized script, see \emph{Script File Example}
+(\S\ref{foo.pl}).
+
+\subsubsection{Perl Modules}
+
+Module files should have, in the following order:
+
+\begin{itemize}
+\item 
+A Copyright notice followed by a blank commented line.  Then one commented line
+per CVS keyword to be expanded.
+
+\item
+A Perl \code{package} declaration.
+
+\item
+An optional minimum \code{perl} version requirement in long form (vStrings are
+not allowed).  Where \code(requirement = revision + version / 1000 + subversion
+/ 1\_000\_000).  e.g. A minimum requirement of \code{perl} 5.8.5 would be
+expressed as \code{use 5.008005}.
+
+\item 
+A \code{use strict} and \code{use warnings} declaration.
+
+\item
+A \code{$VERSION} declaration.
+
+\item 
+Any ``base'' classes. 
+
+\item 
+Any modules to include. 
+
+\item 
+Any constant declarations. 
+
+\item 
+Any package variables.  e.g. \code{our $foo}
+
+\item 
+Any package scoped lexical variables.  e.g. \code{my $foo}
+
+\item 
+Any subroutines.
+
+\item
+A \code{1} on a line by itself so when the module is parsed it always ends with
+a true expression.
+
+\item
+A \code{__END__} token to instruct the perl parser to stop looking for
+executable code.
+
+\end{itemize}
+
+For an example of a properly organized module, see \emph{Module File Example}
+(\S\ref{Foo.pm}).
+
+\subsubsection{Plain Old Documentation}
+\label{pod} 
+
+Pod files that accompany a module should have the following order:
+
+\begin{itemize}
+\item
+A \code{pod} command.
+
+\item
+A \code{NAME} section.
+
+\item
+A \code{SYNOPSIS} section.
+
+\item
+A \code{DESCRIPTION} section.
+
+\item
+A \code{USAGE} section.
+
+\item
+A \code{DEVELOPER NOTES} section.
+
+\item
+A \code{CREDITS} section.
+
+\item
+A \code{SUPPORT} section.
+
+\item
+A \code{AUTHOR} section.
+
+\item
+A \code{COPYRIGHT} section.
+
+\item
+A \code{SEE ALSO} section.
+
+\item
+A \code{cut} command.
+\end{itemize}
+
+For an example of a properly organized Pod, see \emph{Pod File Example}
+(\S\ref{Foo.pod}).
+
+
+%------------------------------------------------------------------------------
+
+\section{Indentation}
+
+Four spaces should be used as the unit of indentation; tabs are forbidden.
+
+\subsection{Line Length}
+
+Avoid lines longer than 110 characters.
+
+When preparing documents, you should ensure that lines of this length
+are not wrapped.  If you are using the standard PSDC \LaTeX{} class
+file \file{panstarrs.cls}, \CODE|\begin{verbatim} ... \end{verbatim}|
+will do this for you.
+
+\subsection{Wrapping Lines}
+
+When an expression will not fit on a single line, break it according
+to these general principles:
+
+\begin{itemize}
+\item Break after a comma or operator.
+
+\begin{verbatim}
+    if ($a < $b ||
+        $a > 2*$b) {
+        $x = ($a + $b + $c) +
+        sin($z);
+    }
+\end{verbatim}
+
+\item Prefer higher-level breaks to lower-level breaks.
+
+\item Indent new lines 1 indentation unit deeper than the nesting depth of the
+line on which the expression began.
+
+\item If it is convenient to list one statement per line (similar to a hash
+declaration) place the closing parenthesis or brace on a new line.
+
+\end{itemize}
+
+Here are some examples of breaking subroutine calls:
+
+\begin{verbatim}
+    someSub(longExpression1, longExpression2, longExpression3, 
+        longExpression4, longExpression5);
+     
+    $var = someSub(
+        longExpression1,
+        someSub(longExpression2, longExpression3)
+    ); 
+\end{verbatim}
+
+Following are two examples of breaking an arithmetic expression. The first is
+preferred, since the break occurs outside the parenthesized expression, which
+is at a higher level.  The second is demonstrating a statement being broken at
+too low a level.
+
+\begin{verbatim}
+    $longName1 = $longName2 * ($longName3 + $longName4 - $longName5) +
+        4 * $longName6;                   # PREFER
+    
+    $longName1 = $longName2 * ($longName3 + $longName4 -
+                           $longName5) + 4 * $longName6; # AVOID 
+\end{verbatim}
+
+Below are three acceptable ways to format ternary expressions.
+\emph{Expression fragments should be indented such that \code{?} and \code{:}
+are aligned vertically (as opposed to being indented a fixed number of
+indentation units).}
+
+\begin{verbatim}
+    $alpha = (aLongBooleanExpression) ? $beta : $gamma;  
+
+    $alpha = (aLongBooleanExpression) ? $beta
+                                      : $gamma;  
+
+    $alpha = (aLongBooleanExpression)
+           ? $beta 
+           : $gamma;  
+\end{verbatim}
+
+
+%------------------------------------------------------------------------------
+
+\section{Comments}
+
+\subsection{Deactivating Code}
+
+Comments should not be used to comment out large sections of code.  When
+deactivating 3 or fewer consecutive lines of code a double hash (\code{##})
+should be used at the very beginning of the line(s).
+
+Example of deactivating a single line code.
+
+\begin{verbatim}
+        if ($foo) {
+    ##        print("foo");
+        }
+\end{verbatim}
+
+When deactivating 4 or more lines of code an embedded Pod statement should be
+used.  The opening statement should be \code{=for off} and the closing
+statement should be \code{=cut}.  The deactivated section should remain nested
+to the same depth as the surrounding code.
+
+Example of deactivating multiple lines of code.
+
+\begin{verbatim}
+    =for off
+    if ($foo) {
+        print("foo");
+    }
+    =cut
+\end{verbatim}
+
+Example of temporarily reactivating disabled code.
+
+\begin{verbatim}
+    #=for off
+    if ($foo) {
+        print("foo");
+    }
+    =cut
+\end{verbatim}
+
+\subsection{Implementation Comments}
+
+General rules:
+
+\begin{itemize}
+\item
+Comments should be used to give overviews of code and provide additional
+information that is not readily available in the code itself. Comments should
+contain only information that is relevant to reading and understanding the
+program. For example, information about how the corresponding package is built
+or in what directory it resides should not be included as a comment.
+
+\item
+Discussion of nontrivial or non-obvious design decisions is appropriate, but
+avoid duplicating information that is present in (and clear from) the code. It
+is too easy for redundant comments to get out of date. In general, avoid any
+comments that are likely to get out of date as the code evolves.
+
+\item
+Comments should not be enclosed in large boxes drawn with hashes or other
+characters.
+
+\item
+Comments should never include special characters such as form-feed and
+backspace.
+\end{itemize}
+
+\emph{The frequency of comments sometimes reflects poor quality of code. When
+you feel compelled to add a comment, consider rewriting the code to make it
+clearer.}
+
+Programs can have three styles of implementation comments: single-line,
+multi-line and trailing.
+
+\subsubsection{Single-Line Comments}
+
+Short comments can appear on a single line indented to the level of the code
+that follows.  The comment should appear above the code that it is describing
+and must have a space between the \code{#} and the first text.
+
+Example of a single-line comment.
+
+\begin{verbatim}
+    # some words
+\end{verbatim}
+
+\subsubsection{Multi-Line Comments}
+
+Merely a repetition of single-line comments.  Logically distinct but adjoining
+comments should be separated by an empty comment line.
+
+\begin{verbatim}
+    # this is the start of a multi-line comment that continues across a couple of
+    # lines, then has a logical separation before another comment
+    #
+    # this is an adjoined comment
+\end{verbatim}
+
+\subsubsection{Trailing Comments}
+\label{col41} 
+
+Very short comments can appear on the same line as the code they describe, and
+should be indented to column 41\footnote{In emacs, this may be achieved with
+\code{ESC;}}. If the code extends beyond this column, the comment should be
+separated from the closing semi-colon by a single space.
+
+Here's an example trailing comments:
+
+\begin{verbatim}
+    if ($a == 2) {
+        return TRUE;                        # special case
+    } else {
+        return isPrime($a);                 # works only for odd a
+    }
+\end{verbatim}
+
+\emph{\code{perltidy}(\S\ref{perltidy}) is incapable of formatting trailing
+comments in this style.  It will force trailing comments to be 4 spaces to the
+right of the last character in any statement on the line.  However, it will add
+additional spaces to cause trailing comments in adjoining lines to be
+vertically aligned.  Until such time as \code{perltidy} can following this
+convention or a replacement tool is located, trailing comments as formatted by
+\code{perltidy} will be considered acceptable.}
+
+\subsection{API Comments}
+
+Plain Old Documentation format or Pod will be used to produce documentation of
+the subroutines and variables in the exposed API.  See \emph{Plain Old
+Documentation} (\S\ref{pod}).
+
+If you need to give information about an interface, or variable that isn't
+appropriate for Podification, use a normal implementation single-line or
+multi-line comment immediately \emph{before} the code (as exampled above).
+
+
+%------------------------------------------------------------------------------
+
+\section{Declarations}
+
+\subsection{Lexical Variables}
+
+One declaration per line with a single place between \code{my} and the identifier:
+
+\begin{verbatim}
+    my $level;                              # indentation level
+    my $size;                               # size of table
+\end{verbatim}
+
+is preferred over:
+
+\begin{verbatim}
+    my ($level, $size);                     # AVOID!
+\end{verbatim}
+
+In almost all situations with the exception of when parameters are being passed
+into a subroutine or variables are highly related \emph{and} the intent is
+plainly obvious.  If there is any doubt as to the appropriate style to use
+place one declaration per line.
+
+\begin{verbatim}
+    my ($verbose, $help);                   # Correct when the meaning is clear
+    my ($foo, $bar) = @_;                   # Correct in a subroutine
+\end{verbatim}
+
+Do not combine different types in the same declaration.  Example:
+
+\begin{verbatim}
+    my ($foo, @fooArray);                   # AVOID!
+\end{verbatim}
+
+\subsubsection{Scalars}
+
+Do not initialize more then one scalar per declaration.
+
+\begin{verbatim}
+    my $foo = \$bar;                        # Correct
+    my ($foo, $bar) = (1, 2);               # AVOID!
+\end{verbatim}
+
+\subsubsection{Arrays}
+
+When assigning a large number of elements to an array a single column table
+format should be used.
+
+\begin{verbatim}
+    my @foo = (1, 'two', 3);                # Correct
+
+    my @foo = (                             # Correct
+        1,
+        'two',
+        3,
+        'four',
+        5,
+        'six',
+        7,
+        'eight',
+        9,
+        'ten'
+    );
+
+    my @foo = (1, 'two', 3, 'four', 5, 'six', ...   # AVOID!
+\end{verbatim}
+
+Consider using the \code{qw} operator to avoid repetitive string quoting when
+assigning to an array.
+
+\begin{verbatim}
+    my @foo = qw( 1 two 3 );
+
+    my @foo = qw(
+        1
+        two
+        3
+        four
+        5
+        six
+    );
+\end{verbatim}
+
+\subsubsection{Hashes}
+
+When assigning a large number of elements to a hash a double column table
+format should be used.
+
+\begin{verbatim}
+    my %foo = (one => 1, two => 2);     # Correct
+
+    my %foo = (                         # Correct
+        one   => 1,
+        two   => 2,
+        three => 3,
+        four  => 4,
+        five  => 5,
+        six   => 6,
+        seven => 7,
+        eight => 8,
+        nine  => 9,
+        ten   => 10
+    );
+
+    my %foo = (one => 1, two => 2, three => 3, ...  # AVOID!
+\end{verbatim}
+
+\subsection{Local Variables}
+
+Avoid \code{local} declarations that hide user declared variables from higher
+level scopes. For example, do not declare the same variable name in an inner
+block:
+
+\begin{verbatim}
+    my $count;
+    ...
+    sub mySubroutine
+    {
+        local $count;                       # AVOID!
+        ...
+    }
+\end{verbatim}
+
+\emph{This restriction does not apply to Perl's built-in variables or the
+package variable used by some modules to control global behavior.}
+
+\subsection{Package Variables}
+
+See \emph{When to Make Symbols Global} (\S\ref{globals}).
+
+\subsection{Initialization}
+
+General rules:
+
+\begin{itemize}
+\item
+Try to initialize local variables where they're declared.
+
+\item
+Do not initialize variables with an empty string or list; this is pointless.
+
+\item
+A reason not to initialize a variable where it's declared is if the initial
+value depends on some computation occurring first.
+
+\item
+In some cases it may be necessary to initialize a variable to suppress a
+compiler warning; in this case a comment should explain the circumstances.
+\end{itemize}
+
+\subsection{Placement}
+
+Variables should ordinarily be declared at the top of the block in
+which they appear, unless there is some reason to declare them later.
+This allows them to be initialized as they are created, and naturally
+associates their declaration with their use.
+
+Practical examples of when \emph{not} to place variable declarations at the top
+of a block include loop iterators, intermediate value variables, and
+variables used for returning values from nested scopes.
+
+\subsection{Subroutine Declarations}
+
+When coding subroutines the following formatting rules should be
+followed:
+
+\begin{itemize}
+\item 
+Subroutine declarations may be preceded by a short comment describing what the
+subroutine does.  These comments should include a brief description as well as
+other warnings, bugs, etc. as needed.
+
+\item
+The subroutine body's open brace \CODE.{. should be in column 1 of the next
+line.
+
+\item
+The first line after the opening brace should declare a list of lexical
+variables for input parameters and assign \code{shift} or \code{@_} to it.
+
+\item
+Closing brace \CODE.}. starts a line by itself.
+
+\item
+The last line before the closing brace must explicitly declare a return value.
+
+\item
+Do not directly use the \code{@_} array.
+
+\item
+Do not nest named subroutines.
+\end{itemize}
+
+\begin{verbatim}
+    sub subName                         # Correct
+    {
+        my $param = shift;
+        ...
+        return $val;
+    }
+
+    sub subName                         # Correct
+    {
+        my ($param1, $param2, ...) = @_;
+        ...
+        return $val;
+    }
+
+    sub subName                         # AVOID!
+    {
+        if ($_[0] eq "foo") {
+        ...
+    }
+\end{verbatim}
+
+
+%------------------------------------------------------------------------------
+
+\section{Statements}
+\subsection{Simple Statements}
+
+Each line should usually contain only one statement. Example:
+
+\begin{verbatim}
+    $x = sqrt($x2);                         # Correct
+    $i++;                                   # Correct  
+    $x = sqrt($x2); $i++;                   # AVOID!
+\end{verbatim}
+
+An example of a reasonable two-statement line is:
+
+\begin{verbatim}
+    $foo++; $bar--;
+\end{verbatim}
+
+where the two actions are intimately related.
+
+\subsubsection{return Statements}
+
+A \code{return} statement should not use parentheses unless they make the
+return value more obvious in some way.  All subroutines must have an explicit,
+non-null, return statement unless they are returning an error condition, in
+which case a bare \code{return} is acceptable.   Example:
+
+\begin{verbatim}
+    return $a;                              # Correct
+    return;                                 # Correct only if indicating an error
+    return $myDisk->size;                   # Correct
+    return ($size ? $size : $defaultSize);  # Correct
+    return($a);                             # AVOID!
+    return ($a);                            # AVOID!
+\end{verbatim}
+
+\emph{In the absence of an explicit return statement Perl will return the
+results of the last statement executed.  Relying on this behavior is likely to
+confuse maintenance programmers and therefore should be avoided.}
+
+Try to make the structure of your program match the intent. Example:
+
+\begin{verbatim}
+    if (booleanExpression) {
+        return true;
+    } else {
+        return false;
+    }
+\end{verbatim}
+
+should instead be written as
+
+\begin{verbatim}
+    return booleanExpression;
+\end{verbatim}
+
+If you're concerned that the reader may not know that \code{booleanExpression}
+is boolean, use:
+
+\begin{verbatim}
+    return (booleanExpression ? true : false);
+\end{verbatim}
+
+Similarly,
+
+\begin{verbatim}
+    if (condition) {
+        return $x;
+    }
+    return $y;
+\end{verbatim}
+
+should be written as
+
+\begin{verbatim}
+    return (condition ? x : y);
+\end{verbatim}
+
+\subsection{Compound Statements}
+
+Compound statements are statements that contain lists of statements enclosed in
+braces. See the following sections for examples.
+
+\begin{itemize}
+\item The enclosed statements should be indented one more level than the compound statement.
+
+\item The opening brace should be at the end of the line that begins
+the compound statement; the closing brace should begin a line and be
+indented to the beginning of the compound statement.
+\end{itemize}
+
+\subsubsection{if, if-else, if-elsif-else Statements}
+
+The \code{if-else} class of statements should have the following form:
+
+\begin{verbatim}
+    if (condition) {
+        statements;
+    }
+
+    if (condition) {
+        statements;
+    } else {
+        statements;
+    }
+
+    if (condition) {
+        statements;
+    } elsif (condition) {
+        statements;
+    } else {
+        statements;
+    }
+\end{verbatim}
+
+\emph{The same formatting applies to \code{unless}, \code{unless-else}, \&
+\code{unless-elsif-else} statements.}
+
+\subsubsection{C style for Statements}
+
+A \code{for} statement should have the following form:
+
+\begin{verbatim}
+    for (initialization; condition; update) {
+        statements;
+    }
+\end{verbatim}
+
+For example:
+
+\begin{verbatim}
+    for (my $i = 0; $i < 100; $i++) {
+        print "$i\n";
+    }
+\end{verbatim}
+
+An empty \code{for} statement (one in which all the work is done in the
+initialization, condition, and update clauses) should have the following form:
+
+\begin{verbatim}
+    for (initialization; condition; update) {}
+\end{verbatim}
+
+When using the comma operator in the initialization or update clause
+of a \code{for} statement, avoid the complexity of using more
+than three variables. If needed, use separate statements before the
+\code{for} loop (for the initialization clause) or at the end of
+the loop (for the update clause).
+
+\emph{Generally, ``Perlish'' style loops are preferred over the C form.}
+
+\subsubsection{Perl style for, foreach Statements}
+
+A Perlish \code{for} or \code{foreach} statement should have the following
+form:
+
+\begin{verbatim}
+    foreach my $item (@workingList) {
+        statements;
+    }
+
+    foreach my $item (@workingList) {
+        statements;
+    } continue {
+        statements;
+    }
+\end{verbatim}
+
+The current working item should be interacted with through a declared lexical
+variable and not via \code{$_}.
+
+\begin{verbatim}
+    foreach (@workingList) {                # AVOID!
+        # do stuff with $_
+    }
+\end{verbatim}
+
+\subsubsection{while Statements}
+
+A \code{while} statement should have the following form:
+
+\begin{verbatim}
+    while (condition) {
+        statements;
+    }
+
+    while (condition) {
+        statements;
+    } continue {
+        statements;
+    }
+\end{verbatim}
+
+Do \emph{not} use \code{$_} when iterating through a filehandle.  Instead, place
+each ``line'' of the filehandle into a named lexical variable.
+
+\begin{verbatim}
+    my $line;
+    while (defined($line = <FILE>)) {
+        statements;
+    }
+\end{verbatim}
+
+An empty \code{while} statement should have the following form:
+
+\begin{verbatim}
+    while (condition) {}
+\end{verbatim}
+
+\emph{Consider using the \code{map} subroutine instead of a loop for list
+generation.}
+
+\subsubsection{do Statements}
+
+In Perl \code{do} is a built-in subroutine.  Traditional \code{do-while} like
+statements may be constructed with it by using Statement Modifiers (see
+\S\ref{statement-modifiers}) in the following form:
+
+\begin{verbatim}
+    do {
+        statements;
+    } statement-modifier (condition);
+\end{verbatim}
+
+\subsubsection{Label Statements}
+
+Code labels should be indented to align with the previous level of indentation.
+Do \emph{not} label a loop unless you are going to use the label.
+
+\begin{verbatim}
+    MYLABEL: while (condition) {
+        if (condition) {
+            next MYLABEL;
+        }
+    }
+\end{verbatim}
+
+\subsubsection{Blocks}
+
+The contents of a block should be indented one level.
+
+\begin{verbatim}
+    $foo = foobar($baz);
+    {
+        my $bat = fooBaz();
+        ...
+    }
+\end{verbatim}
+
+\subsection{Statement Modifiers}
+\label{statement-modifiers}
+
+These keywords are considered acceptable for use as ``Statement Modifiers'':
+\code{if}, \code{unless} \& \code{until}.  These keywords should generally be
+avoided for that purpose: \code{while} \& \code{foreach}.
+
+Statement Modifiers should only be used \emph{only} when it makes the intent
+for the code more obvious.  e.g.
+
+\begin{verbatim}
+    die "you forgot to handle some case" unless $flag;  # Correct
+    $a = 1 if (($b == $c) && defined($d);               # AVOID!
+\end{verbatim}
+
+Parenthesis should be used with complex expressions.
+
+\begin{verbatim}
+    someSubCall($a, $b, $c) if ($foo eq 'bar' || $baz > 10);    # Correct
+    someSubCall($a, $b, $c) if $foo eq 'bar' || $baz > 10;      # AVOID!
+\end{verbatim}
+
+
+%------------------------------------------------------------------------------
+
+\section{White Space}
+
+In general, be very liberal in the use of whitespace, both horizontal and
+vertical, if it will improve the clarity and readability of your code.
+
+\subsection{Blank Lines}
+
+Blank lines improve readability by setting off sections of code that
+are logically related.
+
+Two blank lines should always be used between sections of a source file.
+
+One blank line should always be used in the following circumstances:
+
+\begin{itemize}
+\item
+Between the local variables in a subroutine and its first statement
+
+\item
+Between logical sections inside a subroutine to improve readability
+\end{itemize}
+
+\subsection{Blank Spaces}
+
+Blank spaces should be used in the following circumstances:
+\begin{itemize}
+\item
+A keyword followed by a parenthesis should be separated by a space. Example:
+
+\begin{verbatim}
+    while (true) {
+        ...
+    }
+\end{verbatim}
+
+\emph{A blank space should not be used between a subroutine name and its
+opening parenthesis. This helps to distinguish keywords from subroutine calls.}
+
+\item
+A blank space should appear after commas in parameter lists.
+
+\begin{verbatim}
+    subName(1, 2, 3);
+\end{verbatim}
+
+\item
+Binary operators should be separated from their operands by spaces.  Blank
+spaces should never separate unary operators such as a unary minus, increment
+(\code{++}), and decrement (\code{--}) from their operands. Examples:
+
+\begin{verbatim}
+    $a += $c + $d;
+    $a = ($a + $b) / ($c * $d);
+
+    while ($d++ = $s++) {
+        $n++;
+    }
+    printf("size is %d\n", $foo);
+\end{verbatim}
+
+\item
+The expressions in a \code{for} statement should be separated by blank spaces.
+Example:
+
+\begin{verbatim}
+    for (expr1; expr2; expr3) {}
+\end{verbatim}
+
+\item
+Built-in quoting functions should have spaces between their parameters and the
+opening/closing delimiters.
+
+\begin{verbatim}
+    my @things = qw( foo bar baz );
+\end{verbatim}
+\end{itemize}
+
+Blank spaces should \emph{not} be used in the following circumstances:
+
+\begin{itemize}
+\item
+A variable dereference or method call should not have any blank spaces around
+the \code{->} operator.
+
+\begin{verbatim}
+    $myHashRef->{'foo'};
+    $myObject->foo;
+\end{verbatim}
+
+\end{itemize}
+
+
+%------------------------------------------------------------------------------
+
+\section{Naming Conventions}
+
+Naming conventions make programs more understandable by making them easier to
+read. They can also give information about the subroutine of the identifier --
+for example, whether it's a constant or a subroutine -- which can be helpful in
+understanding the code.  Remember these are guidelines for improving
+readability; clarity should trump rigid adherence to the guideline.
+ 
+{ \small 
+\begin{tabular}{lp{3in}p{3in}}
+\textbf{Identifier Type} &
+\textbf{Rules for Naming} &
+\textbf{Examples} \\
+\hline\\
+
+Script Files &
+
+The filenames of all scripts should be verbal phrases, in mixed case.  The
+capitalized \code{.PL} suffix is reserved for build and configuration scripts.
+
+&
+
+\code{foo.pl}\hfil\break
+\code{fooBar.pl}\hfil\break
+\code{Makefile.PL}\hfil\break
+
+\\
+
+Module Files &
+
+The filenames of all modules should be verbal phrases, in mixed case, with the
+first letter capitalized.
+
+&
+
+\code{Foo.pm}\hfil\break
+\code{FooBar.pm}\hfil\break
+
+\\
+
+Pod Files &
+
+The filenames of all stand-alone Pod should follow the same rules as module
+files.
+
+&
+
+\code{Foo.pod}\hfil\break
+\code{FooBar.pod}\hfil\break
+
+\\
+
+Subroutines &
+
+The names of all public subroutines (and methods) should be verbal phrases, in
+mixed case.
+
+Private subroutines (and methods) should follow the same rules as public
+subroutines but be prefixed with an underscore.
+
+&
+
+\code{run();}\hfil\break
+\code{runFast();}\hfil\break
+\code{getBackground();}\hfil\break
+\code{_keepHandsOff();}\hfil\break
+
+\\
+
+Variables &
+
+Variable names should be in mixed case and be short yet meaningful. The choice
+of a variable name should be mnemonic- that is, designed to indicate to the
+casual observer the intent of its use. One-character variable names should be
+avoided except for temporary \emph{throwaway} variables. Common names for
+temporary variables are \code{$i}, \code{$j}, \code{$k}, \code{$m}, and \code{$n}
+for integers; \code{$c}, \code{$d}, and \code{$e} for characters.
+
+&
+
+\code{$i;}\hfil\break
+\code{$i;}\hfil\break
+\code{$c;}\hfil\break
+\code{$myWidth;}\hfil\break
+\code{$pseudo;}\hfil\break
+\code{$myFiddleFactor;}\hfil\break
+
+\\
+ 
+Constants &
+
+Constant names should be in all capital letters and highly descriptive.
+
+&
+\code{use constant PI => 3.14159265;}\hfil\break
+\code{use constant LENGTHS => qw( 2, 4, 8 );}\hfil\break
+\code{use constant CLASSES => {a => 1, b => 2};}\hfil\break
+
+\\
+
+Namespaces &
+
+Must start with a capitalized letter but otherwise follow the same naming
+conventions as variables.
+
+&
+
+\code{package PS::IPP::Metadata;}\hfil\break
+\code{package PS::IPP::Modules::Debias;}\hfil\break
+\code{package PS::MOPS::DB::Detections;}\hfil\break
+\code{package PS::MOPS::Util::MPC;}\hfil\break
+
+\\
+
+Labels &
+
+Label names should be in all capital letters and short.  Generally they should
+attempt to describe the `thing' being operated on by the loop they are labeling.
+
+&
+
+\code{LINE:}\hfil\break
+\code{CONNECTION:}\hfil\break
+\code{IMAGE:}\hfil\break
+
+\\
+
+\end{tabular}
+}
+
+
+%------------------------------------------------------------------------------
+
+\section{Programming Practices}
+
+\subsection{When to Make Symbols Global}
+\label{globals}
+
+Only declare variables as package variables (\code{our}) if they are intended
+to be visible outside of the current namespace.  All other variables within a
+namespace should be declared as lexical.
+
+\begin{verbatim}
+    our $VERSION = '1.00';                  # Correct
+    our $localCounter;                      # AVOID! - should be lexical
+
+    use base qw( Exporter );
+    our @EXPORT_OK = qw( $foo $bar );       # Correct
+\end{verbatim}
+
+\subsection{Constants}
+
+Numerical constants (literals) should not be coded directly, except for
+small integers such as -1, 0, and 1 which are permitted to e.g. appear
+in a \code{for} loop as counter values.  
+
+The \code{constant} pragma should be used to declare constants.
+\emph{``constants'' created this way can not be interpolated inside of double
+quoted strings.}
+
+See the \code{constant} module's Pod for further details.
+
+\subsection{Variable Assignments}
+
+Avoid assigning several variables to the same value in a single
+statement, unless the variables are intimately related. Acceptable examples:
+
+\begin{verbatim}
+    $row0 = $col0 = 0;
+    $sum = $sumx = $sumy = 0;
+\end{verbatim}
+
+Do not use the assignment operator in a place where it can be easily confused
+with the equality operator. Example:
+
+\begin{verbatim}
+    if ($c++ = $d++) {                      # AVOID!
+        ...
+    }
+\end{verbatim}
+
+should be written as:
+
+\begin{verbatim}
+    if (($c++ = $d++) != 0) {
+        ...
+    }
+\end{verbatim}
+
+Do not use embedded assignments in an attempt to improve run-time
+performance. This is the job of the compiler. Example:
+
+\begin{verbatim}
+    $d = ($a = $b + $c) + $r;               # AVOID!
+\end{verbatim}
+
+should be written as
+
+\begin{verbatim}
+    $a = $b + $c;
+    $d = $a + $r;
+\end{verbatim}
+
+\subsection{Miscellaneous Practices}
+\subsubsection{Parentheses}
+
+It is generally a good idea to use parentheses liberally in
+expressions involving mixed operators to avoid operator precedence
+problems. Even if the operator precedence seems clear to you, it might
+not be to others-you shouldn't assume that other programmers know
+precedence as well as you do. In cases where the precedence rules
+are clear, the parentheses may be omitted.
+
+\begin{verbatim}
+    if ($a & $b || $c & $d) {}              # AVOID!
+    if (($a & $b) || ($c & $d)) {}          # Correct
+\end{verbatim}
+
+In particular, expressions involving the combinations of \code{||} and
+\code{&&} should be fully parenthesized, as should all expressions containing
+bitwise operators:
+
+\begin{verbatim}
+    if (($a == $b && $c == $d) || $e == $f) {}
+    $l << ($j + $k)
+    ($l << $j) + $k
+    ($i & $b) | $c
+\end{verbatim}
+
+\subsubsection{Expressions before `?' in the Conditional Operator }
+
+If an expression containing a binary operator appears before the \code{?} in
+the ternary \code{?:} operator, it should be parenthesized. Example:
+
+\begin{verbatim}
+    ($x >= 0) ? $x : -$x;
+\end{verbatim}
+
+\subsubsection{\$\_}
+
+Use of the ``default'' variable (\code{$_}) should be avoided.
+
+\begin{verbatim}
+    my $foo =~ /(foo.*)/;                   # Correct
+    foreach my $foo (@bar) {}               # Correct
+    /(foo.*)/;                              # AVOID!
+    $_ = ~/(foo.*)/;                        # AVOID!
+\end{verbatim}
+
+\subsubsection{Filehandles}
+
+Do not use ``bare-words'' as filehandles except for \code{STDIN}, \code{STDOUT},
+and \code{STDERR}.
+
+\begin{verbatim}
+    open(STDOUT, '>', $foo) or die "can not open STDOUT: $!";       # Correct
+    open(my $fh, '>', $foo) or die "can not open file $foo: $!";    # Correct
+    open(FILE, '>', $foo) or die "can not open file $foo: $!";      # AVOID!
+\end{verbatim}
+
+\subsubsection{System Calls}
+
+Always check the return code of system calls.
+
+\begin{verbatim}
+    open(my $fh, '>', $foo) or die "can not open file $foo: $!";    # Correct
+    close($fh) or die "can not close file $foo: $!";                # Correct
+    chdir($foo);                                                    # AVOID!
+\end{verbatim}
+
+\subsubsection{Chop vs. Chomp}
+
+Do not use \code{chop} to remove newline characters as it will remove any
+character (newline or not).
+
+\begin{verbatim}
+    my $foo = "baz\n";                  # string with newline
+    chomp($foo);                        # removes "\n" - Correct
+    chomp($foo);                        # chomping the same string twice is safe
+    chop($foo);                         # removes "z" - AVOID!
+\end{verbatim}
+
+\subsubsection{Array Subscripts}
+
+Use ``cuddled'' brackets \code{[]} when accessing an array element.
+
+\begin{verbatim}
+    $foo[-1];                           # Correct
+    (someSub($foo))[1];                 # Correct
+    $foo[ 6 ];                          # AVOID!
+\end{verbatim}
+
+\subsubsection{Hash Subscripts}
+
+Use ``cuddled'' brackets \CODE.{}. when accessing a hash element.  It is
+acceptable to use ``bare-words'' as a hash key (\emph{this rule may be subject
+to change}).
+
+\begin{verbatim}
+    $foo{bar};                          # Correct
+    $foo{$baz};                         # Correct
+    $foo{'fiddle'};                     # Correct
+    $foo{ lish };                       # AVOID!
+    $foo{ 'zab' };                      # AVOID!
+\end{verbatim}
+
+\subsubsection{Loading Modules}
+
+Modules may be loaded with an optional version requirement.  This is
+particularly useful for modules that exist in the Perl core but have newer
+versions available on CPAN.
+
+\begin{verbatim}
+    use Foo;                            # Correct
+    use Foo 0.02;                       # Correct
+\end{verbatim}
+
+Do not use \code{require} to prevent a module from exporting symbols into the
+caller's namespace.  Instead, provide \code{use} with an empty parameter list.
+
+\begin{verbatim}
+    use Foo qw();                       # Correct
+    use Foo ();                         # Correct
+    require Foo;                        # AVOID!
+\end{verbatim}
+
+This also works with a minimum module version requirement.
+
+\begin{verbatim}
+    use Foo 0.02 qw();                  # Correct
+\end{verbatim}
+
+\subsubsection{Inheritance}
+
+Do not use the \code{@ISA} package variable for sub-classing.  Instead, use the
+\code{base} pragma.
+
+\begin{verbatim}
+    use base qw( Foo );                 # Correct
+    our @ISA = qw( Foo );               # AVOID!
+\end{verbatim}
+
+\subsubsection{Calling Subroutines}
+
+Do not use the \code{&} sigil to to disambiguate a subroutine from a
+``bare-word''.  Instead, use empty parentheses adjoined to the subroutine call.
+
+\emph{Only use the \code{subs} pragma as a last resort.}
+
+\begin{verbatim}
+    fooBar($foo);                       # Correct
+    fooBar();                           # Correct
+    &fooBar;                            # AVOID!
+    &fooBar();                          # AVOID!
+    use subs qw( fooBar );              # last resort - Correct
+\end{verbatim}
+
+\subsubsection{Calling Methods}
+
+Leave the parentheses off method calls that aren't being passed parameters.
+
+\begin{verbatim}
+    $myObj->fooBar;                     # Correct
+    $myObj->fooBar($foo);               # Correct
+    $myObj->fooBar();                   # AVOID!
+\end{verbatim}
+
+\subsubsection{Quoting Strings}
+
+\begin{itemize}
+\item
+Unless variable interpolation needs to occur inside of a string or is likely to
+be needed in the future, use single quotes \code{''} instead of double quotes
+\code{""}.
+
+\item
+When possible use \code{END} as the termination token for ``\code{<<HERE}
+docs''.
+
+\begin{verbatim}
+    my $string = <<END
+    foo bar
+    bat baz
+    END
+\end{verbatim}
+
+\item
+Try to use the \code{qw} operator whenever possible.
+
+\item
+ Consider using the \code{q} and \code{qq} operators instead of backslashes
+(\code{\}) when quoting strings that contain other quotes or backslashes.
+
+\item
+Consider using the \code{quotemeta} subroutine when handling strings that
+contain meta-characters.  \end{itemize}
+
+\subsubsection{String Concatenation}
+
+Avoid use of the string concatenation operator (\code{.}) where it's
+unnecessary.
+
+\begin{verbatim}
+    $foo = "foo" . "bar";               # Correct
+    $foo .= "bar";                      # Correct
+    print "foo" , "bar";                # Correct
+    print "foo" . "bar";                # AVOID!
+\end{verbatim}
+
+\subsubsection{Regular Expressions}
+
+Use the ``extended'' regular expression syntax whenever practical.
+
+\begin{verbatim}
+    qr/^                                # Correct
+       ( -? (?:Bork\s*){0,9} , (?:Bork\s*){0,9}
+          , (?:Bork\s*){0,9} , (?:Bork\s*){0,9} )
+        - ( (?:Bork\s*){0,9} , (?:Bork\s*){0,9} )
+        - ( (?:Bork\s*){0,9} , (?:Bork\s*){0,9} )
+        T ( (?:Bork\s*){0,9} , (?:Bork\s*){0,9} )
+        : ( (?:Bork\s*){0,9} , (?:Bork\s*){0,9} )
+        : ( (?:Bork\s*){0,9} , (?:Bork\s*){0,9} )
+    $/ix;
+
+    qr/^(-?(?:Bork\s*){0,9},(?:Bork\s*){0,9},(?:Bork\s*){0,9},...$/i # AVOID!
+\end{verbatim}
+
+\subsubsection{Subroutine Prototypes}
+
+Do not use subroutine prototypes.  If you need to validate subroutine
+parameters see \code{Params::Validate} (\S\ref{Params::Validate}).
+
+\begin{verbatim}
+    sub myopen (*;$);                   # AVOID!
+    sub myopen (*;$) { }                # AVOID!
+\end{verbatim}
+
+
+%------------------------------------------------------------------------------
+\appendix				%Begin Appendices
+%------------------------------------------------------------------------------
+
+\section{Code Examples}
+\subsection{Script File Example}
+\label{foo.pl} 
+
+\verbatiminput{perlCodeExamples/foo.pl}
+
+\subsection{Module File Example}
+\label{Foo.pm} 
+
+\verbatiminput{perlCodeExamples/Foo.pm}
+
+\subsection{Pod File Example}
+\label{Foo.pod} 
+
+\verbatiminput{perlCodeExamples/Foo.pod}
+
+\subsection{Complete Program Example}
+\label{SourceExample} 
+
+An example of \file{buildIndex.pl} that conforms with the Perl code
+convention.
+
+\verbatiminput{perlCodeExamples/buildIndex.pl}
+
+
+%------------------------------------------------------------------------------
+
+\section{How to Achieve This Style with Perltidy}
+\label{perltidy}
+
+\code{pertidy} is a Perl specific code ``beautifier'' with functionality
+similar to \code{indent} or \code{astyle}.  It is freely available from
+\code{http://perltidy.sourceforge.net/}.
+
+\code{pertidy} can be configured with either command line switches or a
+\file{.perltidyrc} file in your home directory.  Detailed instructions on how
+to configure \code{perltidy} can be found in it's manpage\footnote{Perltidy
+manpage - http://perltidy.sourceforge.net/perltidy.html}
+
+Example \file{.perltidyrc} file to achieve the Perl code convention:
+
+\verbatiminput{perlCodeExamples/.perltidyrc}
+
+
+%------------------------------------------------------------------------------
+
+\section{Params::Validate}
+\label{Params::Validate} 
+
+\code{Params::Validate}\footnote{Params::Validate -
+http://search.cpan.org/~drolsky/Params-Validate/} is a Perl module that allows
+you to perform rigorous subroutine parameter validation.  It is highly
+configurable and validation may be deactivated at runtime.
+
+The basic premise is that a validation specification is passed to one of
+several ``validating'' subroutines along with the parameters the subroutine was
+called with.
+
+An example \code{Params::Validate} named parameters validation spec taken from
+the \code{HTTP::Range}\footnote{HTTP::Range -
+http://search.cpan.org/~jhoblitt/HTTP-Range/} module.
+
+\begin{verbatim}
+    my %args = validate( @_,
+        {
+            request => {
+                type        => OBJECT,
+                isa         => 'HTTP::Request',
+            },
+            length => {
+                type        => SCALAR,
+                callbacks   => {
+                    'length is > 0'         => sub { $_[0] > 0 },
+                    'length is + integer'   => sub { $_[0] =~ /^\d+$/ },
+                },
+            },
+            segments => {
+                type        => SCALAR,
+                default     => 4,
+                callbacks   => {
+                    'segments is > 1'       => sub { $_[0] > 1 },
+                    'segments is + integer' => sub { $_[0] =~ /^\d+$/ },
+                    'segments is <= length' => sub { $_[0] <= $_[1]->{ 'length' } },
+                },
+            },
+        },
+    );
+
+\end{verbatim}
+
+Please see the \code{Params::Validate} Pod\footnote{Params::Validate Pod -
+http://search.cpan.org/~drolsky/Params-Validate/lib/Params/Validate.pm} for
+further details.
+
+
+%------------------------------------------------------------------------------
+
+\bibliographystyle{plain}
+\bibliography{panstarrs}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/.perltidyrc
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/.perltidyrc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/.perltidyrc	(revision 22322)
@@ -0,0 +1,52 @@
+# $Id: .perltidyrc,v 1.1 2005-01-25 21:48:54 jhoblitt Exp $
+
+# check the syntax with perl
+-syn
+-pscf=-wc
+
+# enable perltiday warnings
+-w
+
+# use only \n as a line ending
+-ole=unix
+
+# indent 4 spaces
+-i=4
+
+# continued statements get indented 4 spaces
+-ci=4
+
+# maximum line length
+-l=110
+
+# edit in place but backup the file first
+-b
+
+# cuddled elses
+-ce
+
+# Cish tight containers
+-bt=2
+-pt=2
+-sbt=2
+
+# place the brace on the right after a multi-line expression
+-bar
+
+# don't indent closing tokens
+-cti=0
+
+# no spaces before semicolons in Cish for loops
+-nsfs
+
+# no outdenting long lines
+-noll
+
+# don't outdent labels
+-nola
+
+# treat ## as commented code and not a comment
+-sbc
+
+# opening sub brace on a new line
+-sbl
Index: /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/Foo.pm
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/Foo.pm	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/Foo.pm	(revision 22322)
@@ -0,0 +1,31 @@
+# Copyright (C) 2004  Author's Name
+#
+# $Id: Foo.pm,v 1.1 2005-01-18 23:25:09 jhoblitt Exp $
+
+package Foo;
+
+use 5.008005;                           # optional
+
+use strict;                             # not optional
+use warnings;                           # not optional
+
+our $VERSION = '0.01';                  # this is version 0.01
+
+use base qw( Baz );                     # become a subclass of Baz
+
+use Foo qw( $bar );                     # import $bar into our namespace
+
+use constant MAXFOO => 3;               # maximum number of foos
+
+our $bar;                               # is a package variable
+
+my $baz;                                # is a package scoped lexical variable
+
+sub fuu
+{
+
+}
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/Foo.pod
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/Foo.pod	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/Foo.pod	(revision 22322)
@@ -0,0 +1,96 @@
+=pod
+
+=head1 NAME
+
+Foo - Does something with Baz.
+
+=head1 SYNOPSIS
+
+    use Foo;
+
+    ...
+
+=head1 DESCRIPTION
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+=head3 Constructors
+
+=over 4
+
+=item * new
+
+=back
+
+=head3 Object Methods
+
+=over 4
+
+=item * foo
+
+=back
+
+=head3 Class Methods
+
+=over 4
+
+=item * bar
+
+=back
+
+=head3 Destructors
+
+=over 4
+
+=item * DESTROY
+
+=back
+
+=head1 DEVELOPER NOTES
+
+=head2 REFERENCES
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Where to get help.
+
+=head1 AUTHOR
+
+Principle authors and contact info.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2004  Author's Name.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the LICENSE file included with
+this module, or in the L<perlgpl> Pod as supplied with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Bar::Baz>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/buildIndex.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/buildIndex.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/buildIndex.pl	(revision 22322)
@@ -0,0 +1,212 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2004  Joshua Hoblitt
+#
+# $Id: buildIndex.pl,v 1.1 2005-01-25 21:48:54 jhoblitt Exp $
+
+use 5.008;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.01';
+
+use File::Basename qw();
+use File::Find::Rule;
+##use HTML::Strip;
+use HTML::TreeBuilder;
+use Plucene::Index::Writer;
+use Plucene::Plugin::Analyzer::PorterAnalyzer;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($verbose, $help, $path, $indexDir);
+GetOptions(
+    'verbose' => \$verbose,
+    'path=s'  => \$path,
+    'index=s' => \$indexDir,
+    )
+    or pod2usage(2);
+
+pod2usage(-msg => "Unknown option: @ARGV", -exitval => 2) if @ARGV;
+pod2usage(-msg => "Required option: --path|-p", -exitval => 2) unless $path;
+
+$indexDir = $path . "/.index" unless defined $indexDir;
+
+##my $stripper = HTML::Strip->new;
+
+my $writer = Plucene::Index::Writer->new(
+    $indexDir,
+    Plucene::Plugin::Analyzer::PorterAnalyzer->new(),
+    1    # Create the index from scratch
+);
+
+my $doc = Plucene::Document->new;
+
+foreach my $filename (File::Find::Rule->file->name(qr/^msg\d+\.html/)->in($path)) {
+    print "indexing: $filename\n" if $verbose;
+    $writer->add_document(parseMhonarcHtml($filename));
+}
+
+sub parseMhonarcHtml
+{
+    my $filename = shift;
+
+    my $tree = HTML::TreeBuilder->new_from_file($filename);
+    my $doc  = Plucene::Document->new;
+
+    $doc->add(Plucene::Document::Field->Keyword(file_name => File::Basename::basename($filename),));
+
+    # get the header values for to, cc, from, date, and subject
+
+    # find the <div> tag labeled header
+    my $headerTag = $tree->look_down(_tag => "div", id => "header");
+
+    my %headers;
+
+    foreach my $rowNode ($headerTag->look_down(_tag => "tr")) {
+
+        # process each row of the header
+
+        # get name of header field
+        my $nameNode = $rowNode->look_down(_tag => "td");
+        my $name = lc($nameNode->look_down(_tag => "em")->as_text);
+        $nameNode->delete;
+
+        # get content of header field
+        my $contentNode = $rowNode->look_down(_tag => "td");
+        my $content     = $contentNode->as_text;
+
+        # index "cc" as part of "to"
+        $name =~ s/^cc/to/;
+
+        # index header fields
+        if ($name =~ /^(to|from|subject|date)/i) {
+            ##$content = $stripper->parse( $content );
+            print "\tadding to index $name: $content\n" if $verbose;
+            $doc->add(Plucene::Document::Field->Text($name => $content));
+        }
+    }
+
+    # find the <div> tag labeled content
+    my $contentTag = $tree->root->look_down(_tag => "div", id => "content");
+
+    # index message body
+    print "\tadding to index content\n" if $verbose;
+    $doc->add(Plucene::Document::Field->UnStored(content => $contentTag->as_text));
+
+    return $doc;
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+buildIndex.pl - generate Plucene/Lucene index of MHonarc archives
+
+=head1 SYNOPSIS
+
+    buildIndex.pl --path dir [--index dir] [--verbose]
+
+    or
+
+    buildIndex.pl -p dir [-i dir] [-v]
+
+    or
+
+    buildIndex.pl [--help | -h | -? | --version]
+
+=head1 DESCRIPTION
+
+Parses directories of MHonarc generated HTML files and builds a searchable
+index.  The parser requires the HTML files to be in a specifc format which is
+signifigantly modified from default MHonarc format.  The indexer creates a
+directory of index files in the Plucene/Lucene format.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --path dir | -p dir
+
+Directory of MHonarc generated HTML files to process.
+
+=item * --index dir | -i dir
+
+Directory to write the Plucene/Lucene index files to.  Defaults to C<--path> + '.index'.
+
+=item * --verbose | -v
+
+Print status information while processing.
+
+=item * --help | -h | -?
+
+=item * --version
+
+=back
+
+=head1 DEVELOPER NOTES
+
+The parser extracts the C<to>, C<cc>, C<from>, C<subject>, and C<date> headers
+along with the message body.  The HTML document is required to contain tags in
+this format.
+
+    <div id="header">
+        <table>
+            <tr>
+                <td> <em>[ header ]</em>:</td><td>[ value ]</td>
+            </tr>
+            .
+            .
+
+        </table>
+    </div>
+
+    <div id="content">
+        [ message body ]
+    </div>
+
+The parser is insenstive to the ordering of the C<header> and C<content>
+C<E<lt>divE<gt>> tags.  All other tags in the document are ignored.
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2004  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Plucene::Index::Writer>,
+L<Plucene::Plugin::Analyzer::PorterAnalyzer>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/foo.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/foo.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/misc/perlCodeExamples/foo.pl	(revision 22322)
@@ -0,0 +1,91 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2004  Author's Name
+#
+# $Id: foo.pl,v 1.2 2005-01-25 01:50:13 jhoblitt Exp $
+
+use 5.008005;                           # optional
+
+use strict;                             # not optional
+use warnings;                           # not optional
+
+our $VERSION = '0.01';                  # optional - handy with Getopt::Long
+
+use Foo qw( $bar );                     # import $bar into our namespace
+
+use constant MAXFOO => 3;               # maximum number of foos
+
+my $baz;                                # is a lexical variable in the top level scope
+
+fuu();                                  # the body of the script
+
+sub fuu                                 # subroutines
+{
+
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+foo.pl - Does something with Baz.
+
+=head1 SYNOPSIS
+
+    foo.pl --baz
+
+    ...
+
+=head1 DESCRIPTION
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --baz
+
+=back
+
+=head1 DEVELOPER NOTES
+
+=head1 REFERENCES
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Principle authors and contact info.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2004  Author's Name.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Bar::Baz>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/doc/modules/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/modules/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/modules/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.log *.dvi *.aux *.toc *.log *.out *.tpm *.pdf
Index: /tags/sj_tags/sj_root_20080929/doc/modules/CameraGeometry.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/modules/CameraGeometry.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/modules/CameraGeometry.tex	(revision 22322)
@@ -0,0 +1,109 @@
+
+\section{Camera Data Organization \& Camera Geometry}
+
+We require several utility functions to define the geometry of the
+detectors in a camera and to specify the organization of the camera
+data in real FITS images.  The camera data organization is defined in
+a set of \code{psMetadata} structures, and may be stored on disk in
+the file format parsed by \code{psMetadataParseConfig}.
+
+PSLib defines a hierarchy of data structures related to the
+organization of the pixels in a camera.  These structures, starting
+from the top level, follow the sequence \code{psFPA}, \code{psChip},
+\code{psCell}, \code{psReadout}, \code{psImage}.  Each lower level
+structure is carried as an array in the higher level.  The containers
+as defined by PSLib include functions which specify the astrometric
+relationships between these levels, and provide a single
+\code{psMetadata} container pointer for each level.  In this section,
+we define the type of metadata is stored in these metadata containers
+and how the image headers are parse to define the data hierarchy in
+memory.  
+
+\subsection{Coordinate Transforms and Header Data}
+\tbd{The algorithms for three functions in this section are not
+  well-defined.  Do not code yet.}
+
+Astrometric and geometric information about an image from a camera may
+be represented in a variety of ways.  A crude representation of the
+pixel geometry is specified in many image headers using the IRAF-style
+region keywords \code{DATASEC}, \code{DETSEC}, etc.  These keywords
+are used to define the location of a single image's pixels in the
+frame of the full mosaic of detectors in the assumption that the
+mosaic can be represented as a single uniform grid of pixels.  An
+alternative set of keywords have been used in cases where multiple
+cells are saved together in a single FITS image extension.  More
+sophisticated astrometric representations require elements to define
+projections, scaling, distortion, etc.  Several versions of header
+keywords have been used to represent these astrometric
+transformations.  In this section, we define three functions to
+interpret a collection of image headers and construct the appropriate
+offset and/or astrometry parameters.
+
+\begin{prototype}
+bool pmFPADefineOffsets(psFPA fpa);
+bool pmFPADefineWCS(psFPA fpa);
+bool pmFPADefineWCSfromOffsets(psFPA fpa);
+\end{prototype}
+
+The first function takes a \code{psFPA} structure which has been
+populated with header and camera configuration metadata in the
+appropriate locations as discussed above.  Using the information in
+the camera config metadata and the headers, the function sets the
+values for the elements \code{psChip.col0,row0},
+\code{psCell.col0,row0}, \code{psReadout.col0,row0},
+\code{psReadout.colParity,rowParity}, and
+\code{psReadout.colBinning,rowBinning}.  This information is
+determined by examining the regions defined by the following names.
+
+% how do we handle the CCDSUM keyword case?
+\begin{verbatim}
+psCell.metadata:CELL:CCDBIN1 $\rightarrow$ psReadout.colBins
+psCell.metadata:CELL:CCDBIN2 $\rightarrow$ psReadout.rowBins
+
+DETSEC from psCell.metadata:CELL:DETSEC
+CCDSEC from psCell.metadata:CELL:CCDSEC
+DATASEC from psCell.metadata:CELL:DATASEC
+
+if (DETSEC.x0 > DETSEC.x1) then psReadout.colParity = -1
+if (DETSEC.y0 > DETSEC.y1) then psReadout.rowParity = -1
+\end{verbatim}
+
+The second function examines the contents of the headers of the chips
+and cells and constructs the collection of astrometric coordinate
+transformations.. 
+
+The third function uses the offset information and the basic telescope
+pointing information to construct a approximate guess at the
+astrometric coefficients based on the detector geometry.
+
+\begin{figure}
+\begin{center}
+\psfig{file=CameraLayout,width=5.5in}
+\caption{Camera Pixel Layout\label{CameraLayout}}
+\end{center}
+\end{figure}
+
+\begin{figure}
+\begin{center}
+\psfig{file=CameraRegionKeywords.ps,width=5.5in}
+\caption{Camera Region Keyword Definitions\label{CameraRegionKeywords}}
+\end{center}
+\end{figure}
+
+\subsection{Chip \& Cell from FITS File}
+\tbd{the algorithm for the function in this section is not
+  well-defined.  do not code yet}.
+
+When loading data from disk, it is may be necessary to use the
+information in the DATASEC and DETSEC entries to determine which part
+of the image should be read.  If the data is stored in a chip-based
+format, then the data for each cell corresponds to only a fraction of
+the pixels stored in a single image extension.  We specify the
+following function to perform the correct read of data from a FITS
+file into the corresponding \code{psCell} entry respecting the
+boundaries of the cells within chip-based images.
+
+\begin{prototype}
+psReadout *pmReadoutLoad(psReadout *input, psFits *f, psCell *cell, int plane);
+\end{prototype}
+
Index: /tags/sj_tags/sj_root_20080929/doc/modules/CameraImages.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/modules/CameraImages.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/modules/CameraImages.tex	(revision 22322)
@@ -0,0 +1,1400 @@
+\section{Focal Plane}
+\label{sec:focalplane}
+
+\subsection{Overview}
+
+In PSLib, we have defined a basic container for a single 2D collection
+of pixels (\code{psImage}), along with basic operations to manipulate
+the image pixels.  For astronomical applications, this data structure
+is insufficient for two reasons.  First, it does not provide
+sufficient additional metadata to describe the data in detail.
+Second, astronomy applications frequently involve multiple, related
+images.  For Pan-STARRS, and for general astronomical applications, we
+require a richer collection of data structures which describe a very
+general image concept.  We have defined several layers in the
+hierarchy which are necessary to describe the image data which will be
+produced by the Pan-STARRS GigaPixel Cameras as well as other standard
+astronomical images.
+
+A simple 2D image is the basic data unit for much of astronomical
+imaging: if we consider various optical and IR array cameras, a single
+readout of the detector produces a collection of pixel measurements
+which is well represented as a single 2D image.  We define our
+lowest-level astronomical image structure, \code{pmReadout}, to
+contain the pixels produced by a single readout of the detector, along
+with metadata needed to define that readout: the origin and binning of
+the image relative to the original detector pixels explicitly in the
+structure, and pointers to the general metadata and derived objects,
+if any.
+
+A single detector may be read multiple times in sequence.  For
+example, infrared detectors frequently produce an image immediately
+after the detector is reset followed by an image after the basic
+exposure is complete.  Both readouts correspond to the same pixels,
+though the binning or rastering may be different between the two
+readouts.  Another example is the video sequence produced by the
+Pan-STARRS GigaPixel Camera guide cells, each of which represents a
+series of many images from a subraster of pixels in the detector
+readout portion.  The second level of our image container hierarchy,
+\code{pmCell}, consists of a collection of readouts from a single
+detector.
+
+In the Pan-STARRS GigaPixel camera, the basic readout region is a
+fraction of the full imaging area of a single CCD chip.  The chip is
+divided into 64 cells, any fraction of which may have been readout for
+a given exposure.  In other cameras, such as Megacam at CFHT, the
+individual CCDs have multiple amplifiers addressing contiguous
+portions of the detector.  In such cameras, each amplifier produces a
+separate collection of pixels.  In the third level of our image
+container hierarchy, the data structure \code{pmChip} represents a
+collection of different cells.
+
+The top level of our image container hierarchy is a complete focal
+plane array (\code{pmFPA}).  This structure represents the collection
+of chips in the camera, all of which are read out in a given exposure.
+
+For example, take a mosaic camera consisting of eight $2k\times 4k$
+CCDs, each of which is read out through two amplifiers.  Then there
+would be sixteen cells in total, each of which is presumably $1k\times
+4k$.  There would be eight chips, each consisting of two cells, and
+the focal plane consists of these eight chips.
+
+As another example, consider an observation by PS-1.  The focal plane
+would consist of 60 chips, each of which consist of 64 cells (or less;
+a few cells may be dead).  Some cells (those containing guide stars
+for the orthogonal transfer) will contain multiple readouts.
+
+These data structures represent containers with which to carry around
+the collection of related image data.  There is no requirement on the
+functions or the structures that each instance of one of these data
+structures represent the physical hardware.  For example, it is not
+necessary that an instance of \code{pmFPA} always carry the data for
+all 60 GigaPixel Camera OTAs.  The usage of these structures is such
+that all astronomical operations which apply to a CCD image should be
+performed on an instance of \code{pmFPA}.  If a particular
+circumstance only requires a single 2D image, then that is represented
+by an instance of \code{pmFPA} with one \code{pmChip}, which in turn
+has one \code{pmCell}, which in turn has one \code{pmReadout}.
+
+The data structures defined below provide two additional features
+beyond the hierarchy of relationships.  First, each level of the
+hierarchy includes hooks for carrying metadata to provide the PS
+concepts and analysis metadata that would be appropriate for that
+level.  The functions within PSLib do not specify the contents of
+those metadata containers.
+
+\subsection{Focal Plane Hierarchy}
+
+Here we specify the contents of the focal plane hierarchy:
+\code{pmReadout}, \code{pmCell}, \code{pmChip} and \code{pmFPA}.
+
+\subsubsection{A Readout}
+
+A readout corresponds to an individual read of a cell (e.g., a single
+image as part of a video sequence, or one of multiple coadds).  It
+contains the actual pixels used in analysis (along with mask and
+weight maps).  When reading from a FITS file, the images are subimages
+(from CELL.TRIMSEC) of the pixels read from the appropriate HDU (at
+the FPA, chip or cell level).  The readout also contains a list of
+bias sections (prescans or overscans, or otherwise), a summary of
+analysis tasks that have been performed, status flags, and the offsets
+used for reading a FITS file incrementally.
+
+\begin{verbatim}
+typedef struct {
+    int col0;                           ///< Column offset; non-zero if reading in columns incrementally
+    int row0;                           ///< Row offset; non-zero if reading in rows incrementally
+    psImage *image;                     ///< Imaging area of readout (corresponds to CELL.TRIMSEC region)
+    psImage *mask;                      ///< Mask of input image (corresponds to CELL.TRIMSEC region)
+    psImage *weight;                    ///< Weight of input image (corresponds to CELL.TRIMSEC region)
+    psList *bias;                       ///< List of bias (prescan/overscan) images
+    psMetadata *analysis;               ///< Readout-level analysis metadata
+    pmCell *parent;                     ///< Parent cell
+    bool process;                       ///< Do we bother about reading and working with this readout?
+    bool file_exists;                   ///< Does the file for this readout exist (read case only)?
+    bool data_exists;                   ///< Does the data for this readout exist (read case only)?
+} pmReadout;
+\end{verbatim}    
+
+The constructor for \code{pmReadout} is:
+\begin{verbatim}
+pmReadout *pmReadoutAlloc(pmCell *cell  ///< Parent cell, or NULL
+                          );
+\end{verbatim}
+The constructor shall make an empty \code{pmReadout}.  The metadata
+container and the \code{bias} list shall be allocated.  All other
+pointers in the structure shall be initialized to \code{NULL}.
+Processing is on by default; the existence flags are initially set to
+\code{false}.
+
+All data within a \code{pmReadout} may be freed:
+\begin{prototype}
+void pmReadoutFreeData(pmReadout *readout ///< Readout for which to free data
+                      );
+\end{prototype}
+
+\subsubsection{A Cell}
+
+A cell is the lowest-level camera structure, being part of a chip
+(e.g., an amplifier).  It may consist of one or more readouts, which
+are individual reads of the cell.  It also contains the concepts
+metadata appropriate to this level, the cell configuration information
+(for convenience) from the camera configuration, a summary of analysis
+tasks that have been performed, status flags, and any HDU that
+corresponds to this level for the file of interest
+
+\begin{verbatim}
+typedef struct {
+    psMetadata *concepts;               ///< Cell-level concepts
+    unsigned int conceptsRead;          ///< Which concepts have been read; see pmConceptsSource
+    psMetadata *config;                 ///< Cell configuration information (from CELLS in the camera config)
+    psMetadata *analysis;               ///< Cell-level analysis metadata
+    psArray *readouts;                  ///< The component readouts
+    pmChip *parent;                     ///< Parent chip
+    bool process;                       ///< Do we bother about reading and working with this cell?
+    bool file_exists;                   ///< Does the file for this cell exist (read case only)?
+    bool data_exists;                   ///< Does the data for this cell exist (read case only)?
+    pmHDU *hdu;                         ///< FITS header data unit of interest
+} pmCell;
+\end{verbatim}
+
+The constructor for \code{pmCell} shall be:
+\begin{verbatim}
+pmCell *pmCellAlloc(pmChip *chip,       ///< Parent chip, or NULL
+                    const char *name    ///< Name of cell, for CELL.NAME
+                   );
+\end{verbatim}
+The constructor shall make an empty \code{pmCell}.  The
+\code{readouts} array shall be allocated with a zero size, and the
+metadata containers constructed.  Processing is on by default; the
+existence flags are initially set to \code{false}.  The \code{name} is
+used to set \code{CELL.NAME} in the \code{concepts}.  All other
+pointers in the structure are initialized to \code{NULL}.
+
+All readouts, or all data (readouts and metadata) within a cell may be
+freed:
+\begin{prototype}
+void pmCellFreeReadouts(pmCell *cell    ///< Cell for which to free readouts
+                       );
+void pmCellFreeData(pmCell *cell        ///< Cell for which to free data
+                   );
+\end{prototype}
+
+\subsubsection{A Chip}
+
+The chip is the mid-level camera structure, being part of an FPA, and
+consisting of one or more cells (e.g., a CCD).  It also contains the
+concepts metadata appropriate to this level, a summary of analysis
+tasks that have been performed, status flags, any HDU that corresponds
+to this level for the file of interest, and astrometric
+transformations.  The astrometric transformations provide transforms
+between the chip and FPA coordinates and back.
+
+\tbd{The astrometric transformations will likely disappear into the
+analysis metadata in the future.}
+
+\begin{verbatim}
+typedef struct {
+    // Astrometric transformations
+    psPlaneTransform *toFPA;            ///< Transformation from chip to FPA coordinates, or NULL
+    psPlaneTransform *fromFPA;          ///< Transformation from FPA to chip coordinates, or NULL
+    // Information
+    psMetadata *concepts;               ///< Chip-level concepts
+    unsigned int conceptsRead;          ///< Which concepts have been read; see pmConceptsSource
+    psMetadata *analysis;               ///< Chip-level analysis metadata
+    psArray *cells;                     ///< The component cells
+    pmFPA *parent;                      ///< Parent FPA
+    bool process;                       ///< Do we bother about reading and working with this chip?
+    bool file_exists;                   ///< Does the file for this chip exist (read case only)?
+    bool data_exists;                   ///< Does the data for this chip exist (read case only)?
+    pmHDU *hdu;                         ///< FITS header data unit of interest,
+} pmChip;
+\end{verbatim}
+
+The constructor for \code{pmChip} shall be:
+\begin{verbatim}
+pmChip *pmChipAlloc(pmFPA *fpa,         ///< Parent FPA, or NULL
+                    const char *name    ///< Name of cell, for CELL.NAME
+                   );
+\end{verbatim}
+The constructor shall make an empty \code{pmChip}.  The \code{cells}
+array shall be allocated with a zero size and the metadata containers
+constructed.  Processing is on by default; the existence flags are
+initially set to \code{false}.  The \code{name} shall be used to set
+\code{CHIP.NAME} in the \code{concepts}.  All other pointers in the
+structure shall be initialized to \code{NULL}.
+
+All cells, or all data (cells and metadata) within a chip may be
+freed:
+\begin{prototype}
+void pmChipFreeCells(pmChip *chip       ///< Chip for which to free cells
+                    );
+void pmChipFreeData(pmChip *chip        ///< Chip for which to free data
+                   );
+\end{prototype}
+
+\subsubsection{A Focal Plane}
+
+The FPA is the top-level camera structure, and consists of one or more
+chips.  It also contains the concepts metadata appropriate to this
+level, a summary of analysis tasks that have been performed, the
+camera configuration information, any HDU that corresponds to this
+level for the file of interest, and astrometric transformations.  The
+astrometric transformations encode how to transform from the tangent
+plane to the sky, and back.
+
+It is expected that the astrometric transformation will consist of two
+4D polynomials (i.e.\ a function of two coordinates in position, the
+magnitude of the object, and the color of the object) in order to
+correct for optical distortions and the effects of the atmosphere;
+hence we think that it is prudent to include a reverse transformation
+which will be derived from numerically inverting the forward
+transformation.
+
+\tbd{The astrometric transformations will likely disappear into the
+analysis metadata in the future.}
+
+\begin{verbatim}
+typedef struct {
+    // Astrometric transformations
+    psPlaneDistort *fromTangentPlane;   ///< Transformation from tangent plane to focal plane, or NULL
+    psPlaneDistort *toTangentPlane;     ///< Transformation from focal plane to tangent plane, or NULL
+    psProjection *projection;           ///< Projection from tangent plane to sky, or NULL
+    // Information
+    psMetadata *concepts;               ///< FPA-level concepts
+    unsigned int conceptsRead;          ///< Which concepts have been read; see pmConceptsSource
+    psMetadata *analysis;               ///< FPA-level analysis metadata
+    const psMetadata *camera;           ///< Camera configuration
+    psArray *chips;                     ///< The component chips
+    pmHDU *hdu;                         ///< FITS header data unit of interest, or NULL
+} pmFPA;
+\end{verbatim}
+
+The constructor for \code{pmFPA} shall be:
+\begin{verbatim}
+pmFPA *pmFPAAlloc(const psMetadata *camera ///< Camera configuration (to store in FPA)
+                 );
+\end{verbatim}
+The constructor shall make an empty \code{pmFPA}.  The \code{chips}
+array shall be allocated with a zero size, the \code{camera} pointer
+set to the value provided, and the \code{concepts} metadata
+constructed.  All other pointers in the structure shall be initialized
+to \code{NULL}.
+
+All data (chips and metadata) within an FPA may be freed:
+\begin{prototype}
+void pmFPAFreeData(pmFPA *fpa           ///< FPA for which to free data
+                  );
+\end{prototype}
+
+\subsubsection{Links between levels}
+
+The inclusion of hierarchical links pointing both down (via the
+arrays) and up (via the \code{parent}) could make for difficulties.
+For this reason, we specify a utility function to manage the
+collection of upward-pointing links:
+\begin{prototype}
+bool pmFPACheckParents(pmFPA *fpa);
+\end{prototype}
+This function checks the validity of the \code{parent} links in the
+FPA hierarchy.  If any \code{parent} link in the (entire) hierarchy is
+not set (or not set correctly), it is corrected, and the function
+shall return \code{false}.  If all the \code{parent} pointers were
+correct, the function shall return \code{true}.
+
+\subsubsection{Levels}
+
+The level within an FPA hierarchy may be specified by an enumerated
+type:
+\begin{datatype}
+typedef enum {
+    PM_FPA_LEVEL_NONE,                  ///< No particular level specified
+    PM_FPA_LEVEL_FPA,                   ///< Level corresponds to an FPA
+    PM_FPA_LEVEL_CHIP,                  ///< Level corresponds to a Chip
+    PM_FPA_LEVEL_CELL,                  ///< Level corresponds to a Cell
+    PM_FPA_LEVEL_READOUT                ///< Level corresponds to a Readout
+} pmFPALevel;
+\end{datatype}
+
+The following functions are provided to convert between the enumerated type
+and string representation of the level:
+\begin{prototype}
+const char *pmFPALevelToName(pmFPALevel level ///< Level enum
+                            );
+pmFPALevel pmFPALevelFromName(const char *name ///< Level name
+                             );
+\end{prototype}
+
+The string representations are \code{FPA}, \code{CHIP}, \code{CELL}
+and \code{READOUT} (case insensitive).
+
+\subsubsection{Flags}
+
+The following functions are provided to set and check the \code{file_exists}
+and \code{data_exists} status flags within the FPA hierarchy:
+\begin{prototype}
+bool pmFPASetFileStatus(pmFPA *fpa,     ///< FPA for which to set status
+                        bool status     ///< Status to set
+                       );
+bool pmChipSetFileStatus(pmChip *chip,  ///< Chip for which to set status
+                         bool status    ///< Status to set
+                        );
+bool pmCellSetFileStatus(pmCell *cell,  ///< Cell for which to set status
+                         bool status    ///< Status to set
+                        );
+bool pmFPACheckFileStatus(const pmFPA *fpa ///< FPA for which to check status
+                         );
+bool pmChipCheckFileStatus(const pmChip *chip ///< Chip for which to check status
+                          );
+bool pmCellCheckFileStatus(const pmCell *cell ///< Cell for which to check status
+                          );
+bool pmFPASetDataStatus(pmFPA *fpa,     ///< FPA for which to set status
+                        bool status     ///< Status to set
+                       );
+bool pmChipSetDataStatus(pmChip *chip,  ///< Chip for which to set status
+                         bool status    ///< Status to set
+                        );
+bool pmCellSetDataStatus(pmCell *cell,  ///< Cell for which to set status
+                         bool status    ///< Status to set
+                        );
+\end{prototype}
+
+The following functions set the \code{process} flag within the FPA
+hierarchy, either selecting a component for processing, or excluding
+it from processing:
+\begin{prototype}
+bool pmFPASelectChip(pmFPA *fpa,        ///< FPA containing the chip of interest
+                     int chipNum,       ///< Chip number to select
+                     bool exclusive     ///< Process this chip exclusive of the others?
+                    );
+bool pmChipSelectCell(pmChip *chip,     ///< Chip containing the cell of interest
+                      int cellNum,      ///< Cell number to select
+                      bool exclusive    ///< Process this cell exclusive of the others?
+                     );
+int pmFPAExcludeChip(pmFPA *fpa,        ///< FPA containing the chip of interest
+                     int chipNum        ///< Chip number to exclude
+                    );
+int pmChipExcludeCell(pmChip *chip,     ///< Chip containing the chip of interest
+                      int cellNum       ///< Cell number to exclude
+                     );
+\end{prototype}
+
+The \code{Select} functions, set the specified component to be the
+only component (at that level) to be processed if \code{exclusive} is
+\code{true}.  A negative value for the component number is valid, and
+if \code{exclusive} is \code{true}, de-selects all components at the
+appropriate level.
+
+
+\subsubsection{Finding a component by name}
+
+A component may be found by its symbolic name (the concepts
+\code{CHIP.NAME} or \code{CELL.NAME}, as appropriate).  The following
+functions return the index of the component that matches the provided
+name, or -1 if none was found:
+
+\begin{prototype}
+int pmFPAFindChip(const pmFPA *fpa,     ///< FPA in which to find the chip
+                  const char *name      ///< Name of the chip
+                 );
+int pmChipFindCell(const pmChip *chip,  // Chip in which to find the cell
+                   const char *name     // Name of the cell
+                  );
+\end{prototype}
+
+
+\subsection{Header Data Units}
+
+Each of the levels in the hierarchy have a place to hold a
+\code{pmHDU}, which is the representation of a FITS ``header data
+unit'':
+\begin{datatype}
+typedef struct {
+    psString extname;                   ///< The extension name
+    bool blankPHU;                      ///< Is this a blank FITS Primary Header Unit, i.e., no data?
+    psMetadata *format;                 ///< The camera format
+    psMetadata *header;                 ///< The FITS header, or NULL if primary for FITS; or section info
+    psArray *images;                    ///< The pixel data
+    psArray *weights;                   ///< The pixel data
+    psArray *masks;                     ///< The pixel data
+} pmHDU;
+\end{datatype}
+
+The \code{pmHDU} simplifies the input/output from/to FITS files, since
+it is, for the most part, a representation of the HDU on disk.  Of
+course, it is not an exact replica of a FITS HDU --- they have no mask
+and weight data, but these are stored here for convenience --- it
+keeps all the relevant data about the image in one place.
+
+The allocator is:
+\begin{prototype}
+pmHDU *pmHDUAlloc(const char *extname   ///< Extension name, or NULL for PHU
+                 );
+\end{prototype}
+
+
+\subsubsection{HDU I/O}
+
+The following function moves to the appropriate extension and reads
+the FITS header into the \code{pmHDU.header} member:
+\begin{prototype}
+bool pmHDUReadHeader(pmHDU *hdu,        ///< HDU for which to read header
+                     psFits *fits       ///< FITS file from which to read
+                    );
+\end{prototype}
+
+The following function moves to the appropriate extension and reads
+the FITS header and pixels into the \code{pmHDU}:
+\begin{prototype}
+bool pmHDURead(pmHDU *hdu,              ///< HDU to read
+               psFits *fits             ///< FITS file to read from
+              );
+\end{prototype}
+
+The following function writes the HDU header and pixels to the end of
+the FITS file:
+\begin{prototype}
+bool pmHDUWrite(pmHDU *hdu,             ///< HDU to write
+                psFits *fits            ///< FITS file to write to
+               );
+\end{prototype}
+
+
+\subsubsection{HDU Utilities}
+
+The following functions return the appropriate HDU, given a member of
+the focal plane hierarchy.  If no HDU is present at the particular
+level, it searches the next highest level.  If no HDU can be found,
+the function returns \code{NULL}.
+
+\begin{prototype}
+pmHDU *pmHDUFromFPA(const pmFPA *fpa    ///< FPA for which to find HDU
+                   );
+pmHDU *pmHDUFromChip(const pmChip *chip ///< Chip for which to find HDU
+                    );
+pmHDU *pmHDUFromCell(const pmCell *cell ///< Cell for which to find HDU
+                    );
+pmHDU *pmHDUFromReadout(const pmReadout *readout ///< Readout for which to find HDU
+                       );
+\end{prototype}
+
+The following functions take multiple components of a focal plane
+hierarchy, and return the HDU at the lowest/highest level of the
+components passed to the function.  If components at all levels are
+supplied, the lowest HDU in the hierarchy is the one with the actual
+pixels, while the highest HDU is the PHU.
+\begin{prototype}
+pmHDU *pmHDUGetLowest(const pmFPA *fpa, ///< The FPA
+                      const pmChip *chip, ///< The chip, or NULL
+                      const pmCell *cell ///< The cell, or NULL
+                     );
+pmHDU *pmHDUGetHighest(const pmFPA *fpa, ///< The FPA
+                       const pmChip *chip, ///< The chip, or NULL
+                       const pmCell *cell ///< The cell, or NULL
+                      );
+\end{prototype}
+
+The following function prints details about an HDU: the extension
+name, headers (if requested), and image sizes.  This function is
+intended for testing or development use.
+
+\begin{prototype}
+void pmHDUPrint(FILE *fd,               ///< File descriptor to which to print
+                const pmHDU *hdu,       ///< HDU to print
+                int level,              ///< Level at which to print
+                bool header             ///< Print header?
+               );
+\end{prototype}
+
+
+\subsection{FPA View}
+
+The following structure allows the identification of a single
+component of the focal plane hierarchy (or multiple, if we consider
+selecting all those components below the selected component).
+Components are identified on the basis of their chip, cell, readout
+index.  An index of -1 means all components at that level.
+Additionally, since readouts may be read piecemeal, there are
+additional indices for these.
+\begin{datatype}
+typedef struct {
+    int chip;                           // Number of the chip, or -1 for all
+    int cell;                           // Number of the cell, or -1 for all
+    int readout;                        // Number of the readout, or -1 for all
+    int nRows;                          // Maximum number of rows per readout segment read, or 0 for all
+    int iRows;                          // Starting point for this read
+} pmFPAview;
+\end{datatype}
+
+The allocator is:
+\begin{prototype}
+pmFPAview *pmFPAviewAlloc(int nRows     ///< Maximum number of rows per readout segment read, or 0 for all
+    );
+\end{prototype}
+
+\subsubsection{Utilities}
+
+A view may have all components reset (select the entire FPA):
+\begin{prototype}
+bool pmFPAviewReset(pmFPAview *view     ///< View to reset
+    );
+\end{prototype}
+
+The view level is the highest index that isn't set to -1.
+\begin{prototype}
+pmFPALevel pmFPAviewLevel(const pmFPAview *view ///< View to examine
+    );
+\end{prototype}
+
+\subsubsection{Lookups}
+
+The following functions return the appropriate currently selected
+hierarchy component.  The functions return \code{NULL} if the selection
+is not specific or invalid for the appropriate level.
+
+\begin{prototype}
+pmChip *pmFPAviewThisChip(const pmFPAview *view, ///< Current view
+                          const pmFPA *fpa ///< FPA containing chip
+    );
+pmCell *pmFPAviewThisCell(const pmFPAview *view, ///< Current view
+                          const pmFPA *fpa ///< FPA containing cell
+    );
+pmReadout *pmFPAviewThisReadout(const pmFPAview *view, ///< Current view
+                                const pmFPA *fpa ///< FPA containing readout
+    );
+\end{prototype}
+
+\subsubsection{Incrementors}
+
+The following functions return the next hierarchy component, according
+to the current selection in the \code{view}.  The functions return
+\code{NULL} if there is no next.
+
+\begin{prototype}
+pmChip *pmFPAviewNextChip(pmFPAview *view, ///< Current view
+                          const pmFPA *fpa, ///< FPA containing chips
+                          int nStep     ///< Number of chips to increment
+    );
+pmCell *pmFPAviewNextCell(pmFPAview *view, ///< Current view
+                          const pmFPA *fpa, ///< FPA containing cells
+                          int nStep     ///< Number of cells to increment
+    );
+pmReadout *pmFPAviewNextReadout(pmFPAview *view, ///< Current view
+                                const pmFPA *fpa, ///< FPA containing readouts
+                                int nStep ///< Number of readouts to increment
+    );
+\end{prototype}
+
+\subsubsection{HDUs}
+
+The following function returns the HDU corresponding to the current view,
+using the \code{pmHDUFrom...} functions, combined with the \code{view}.
+\begin{prototype}
+pmHDU *pmFPAviewThisHDU(const pmFPAview *view, ///< Current view
+                        const pmFPA *fpa ///< FPA for view
+    );
+\end{prototype}
+
+This function returns the blank Primary HDU corresponding to the
+current view, if one exists.  The operation is similar to
+pmFPAviewThisHDU, except it returns \code{NULL} if no HDU is found, or
+the HDU is not a blank Primary HDU.
+\begin{prototype}
+pmHDU *pmFPAviewThisPHU(const pmFPAview *view, ///< Current view
+                        const pmFPA *fpa ///< FPA for view
+    );
+\end{prototype}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{FPA Construction}
+
+The following function creates the FPA hierarchy on the basis of the
+camera configuration.  The \code{FPA} entry in the camera
+configuration specifies the chips (each of type \code{STR}) with their
+component cells listed as the corresponding values (whitespace
+separated).  The FPA hierarchy is created devoid of any input/output
+sources (i.e., HDUs).
+\begin{prototype}
+pmFPA *pmFPAConstruct(const psMetadata *camera ///< The camera configuration
+                     );
+\end{prototype}
+
+Input/output sources may be specified within the hierarchy by
+providing information that allows the position of the new HDU to be
+determined (not only the level, but which component of that level);
+this may be done in two ways.  The first is through a \code{pmFPAview}
+that corresponds to the HDU position; this is appropriate when
+creating an output file of known format.  The second is by providing
+the primary header of an input file, from which the HDU position(s)
+can be inferred (using the \code{FILE} information within the camera
+format configuration); this is useful for adding input sources.  In
+both cases, the camera format configuration is required in order to
+describe how the FPA is laid out in terms of disk files.
+
+\begin{prototype}
+bool pmFPAAddSourceFromView(pmFPA *fpa,   ///< The FPA
+                            const pmFPAview *phuView, ///< The view, corresponding to the PHU
+                            const psMetadata *format ///< Format of file
+                           );
+pmFPAview *pmFPAAddSourceFromHeader(pmFPA *fpa, ///< The FPA
+                                    psMetadata *phu, ///< Primary header of file
+                                    const psMetadata *format ///< Format of file
+                                   );
+\end{prototype}
+
+As an aid to development, we have a function that prints to a file
+stream a representation of the FPA, including its headers and
+concepts, if desired.
+
+\begin{prototype}
+void pmFPAPrint(FILE *fd,               ///< File descriptor to which to print
+                const pmFPA *fpa,       ///< FPA to print
+                bool header,            ///< Print headers?
+                bool concepts           ///< Print concepts?
+               );
+\end{prototype}
+
+
+\subsection{FPA Reading and Writing}
+
+\subsubsection{Reading headers}
+
+The following functions read the headers, if the HDU is at the
+appropriate level.  The functions return \code{true} if they
+successfully read the header, or if the header had already been read.
+The functions do not iterate to lower levels.
+\begin{prototype}
+bool pmFPAReadHeader(pmFPA *fpa,        ///< FPA for which to read header
+                     psFits *fits       ///< FITS file handle
+                    );
+bool pmChipReadHeader(pmChip *chip,     ///< Chip for which to read header
+                      psFits *fits      ///< FITS file handle
+                     );
+bool pmCellReadHeader(pmCell *cell,     ///< Cell for which to read header
+                      psFits *fits      ///< FITS file handle
+                     );
+\end{prototype}
+
+\subsubsection{Reading pixels}
+
+The following functions iterate over the component cells, reading the
+pixels, converting them to \code{F32}, allocating readouts, and
+ingesting concepts:
+\begin{prototype}
+bool pmCellRead(pmCell *cell,           // Cell to read into
+                psFits *fits,           // FITS file from which to read
+                psDB *db                // Database handle, for "concepts" ingest
+               );
+bool pmChipRead(pmChip *chip,           // Chip to read into
+                psFits *fits,           // FITS file from which to read
+                psDB *db                // Database handle, for "concepts" ingest
+               );
+bool pmFPARead(pmFPA *fpa,              // FPA to read into
+               psFits *fits,            // FITS file from which to read
+               psDB *db                 // Database handle, for "concepts" ingest
+              );
+\end{prototype}
+
+The following functions perform the same as the above functions, but
+read the pixels into the \code{mask} or \code{weight} elements of the
+readout.
+\begin{prototype}
+bool pmCellReadMask(pmCell *cell,           // Cell to read into
+                    psFits *fits,           // FITS file from which to read
+                    psDB *db                // Database handle, for "concepts" ingest
+                   );
+bool pmChipReadMask(pmChip *chip,           // Chip to read into
+                    psFits *fits,           // FITS file from which to read
+                    psDB *db                // Database handle, for "concepts" ingest
+                   );
+bool pmFPAReadMask(pmFPA *fpa,              // FPA to read into
+                   psFits *fits,            // FITS file from which to read
+                   psDB *db                 // Database handle, for "concepts" ingest
+                  );
+bool pmCellReadWeight(pmCell *cell,           // Cell to read into
+                      psFits *fits,           // FITS file from which to read
+                      psDB *db                // Database handle, for "concepts" ingest
+                     );
+bool pmChipReadWeight(pmChip *chip,           // Chip to read into
+                      psFits *fits,           // FITS file from which to read
+                      psDB *db                // Database handle, for "concepts" ingest
+                     );
+bool pmFPAReadWeight(pmFPA *fpa,              // FPA to read into
+                     psFits *fits,            // FITS file from which to read
+                     psDB *db                 // Database handle, for "concepts" ingest
+                    );
+\end{prototype}
+
+
+The above functions all read the pixels, at least a cell at a time.
+The following function reads the pixels for a readout incrementally.
+Multiple calls to this function moves through a readout within a cell
+incrementally.  It is required to pass the readout previously acquired
+(or a newly-allocated one) in order to preserve state information
+(where the read is at).  Only a maximum of \code{numRows} rows are
+read at a time.  Returns \code{true} if pixels are read, and
+\code{false} otherwise.  To facilitate looping, no error is generated
+for reading a plane that doesn't exist.  Note that this function
+doesn't put pixels in the HDU.  It is therefore \textbf{not
+compatible} with \code{pmCellWrite}, \code{pmChipWrite}, and
+\code{pmFPAWrite} (or any function that uses or creates an HDU, for
+that matter).  \code{pmReadoutWriteNext} should be used to write the
+data that's read by this function.  This function is intended for
+reading in many readouts into memory at once (e.g., for stacking)
+where the input is not written out.
+\begin{prototype}
+bool pmReadoutReadNext(pmReadout *readout, // Readout into which to read
+                       psFits *fits,    // FITS file from which to read
+                       int z,           // Readout number/plane; zero-offset indexing
+                       int numRows      // The number of rows to read
+                      );
+\end{prototype}
+
+\subsubsection{Writing pixels and headers}
+
+The following functions write the appropriate hierarchy level to a
+FITS file, if one exists at that level.  The function generates
+CELL.TRIMSEC, CELL.BIASSEC and the HDU pixels if required (see
+\code{pmHDUGenerate}).  A blank (i.e., image-less header) is written
+only if specifically requested.  The function writes the concepts to
+the various locations, and then the HDU to the FITS file.  This
+function should be called at the beginning of the output cell loop
+with \code{blank=true} in order to produce the correct file structure.
+\begin{prototype}
+bool pmCellWrite(pmCell *cell,          ///<  Cell to write
+                 psFits *fits,          ///<  FITS file to which to write
+                 psDB *db,              ///<  Database handle for "concepts" update
+                 bool blank             ///<  Write a blank PHU?
+                );
+bool pmChipWrite(pmChip *chip,          ///<  Chip to write
+                 psFits *fits,          ///<  FITS file to which to write
+                 psDB *db,              ///<  Database handle for "concepts" update
+                 bool blank,            ///<  Write a blank PHU?
+                 bool recurse           ///<  Recurse to lower levels?
+                );
+bool pmFPAWrite(pmFPA *fpa,             ///<  FPA to write
+                psFits *fits,           ///<  FITS file to which to write
+                psDB *db,               ///<  Database handle for "concepts" update
+                bool blank,             ///<  Write a blank PHU?
+                bool recurse            ///<  Recurse to lower levels?
+               );
+\end{prototype}
+
+
+The following function is the companion to \code{pmReadoutReadNext},
+as it writes a readout to a FITS file, perhaps incrementally (if it
+has been read in using \code{pmReadoutReadNext}).  It relies on the
+FITS header to specify how many image planes there are, and the width
+and height.
+\begin{prototype}
+bool pmReadoutWriteNext(pmReadout *readout, ///< Readout to write
+                        psFits *fits,   ///<  FITS file to which to write
+                        int z           ///<  Image plane to write
+                       );
+\end{prototype}
+
+
+\subsubsection{HDU Generation}
+
+The write functions for the FPA hierarchy use \code{pmHDUWrite}, which
+assumes that the images in the readouts are subimages of the pixels in
+the HDU structure.  If this is not the case, the HDU pixels can be
+generated using some simple assumptions.
+
+The following function generate an HDU (with \code{CELL.TRIMSEC},
+\code{CELL.BIASSEC} and pixels) for a cell with pixels.  They splice
+the images and overscans together without regard for \code{CELL.X0}
+and \code{CELL.Y0} (for a proper mosaic, see \code{pmFPAMosaic}),
+though they should respect \code{CELL.READDIR} (so that the bias and
+trim sections match properly).  A warning may be generated (by the
+concept write functions) after running these functions if the bias and
+trim sections are specified in the camera format by default values
+rather than in the header.  Failure of these functions is often due to
+a bad camera format file.
+\begin{prototype}
+bool pmHDUGenerateForCell(pmCell *cell  ///< The cell for which to generate an HDU
+                         );
+bool pmHDUGenerateForChip(pmChip *chip  ///< The chip for which to generate an HDU
+                         );
+bool pmHDUGenerateForFPA(pmFPA *fpa     ///< The fpa for which to generate an HDU
+                        );
+\end{prototype}
+
+These functions are primarily intended for private use (by the write
+functions) within the Modules, but may be used by the user if desired.
+
+\subsection{Masks and Weights}
+
+Pixel mask values are defined with the following enumerated type:
+\begin{datatype}
+typedef enum {
+    PM_MASK_CLEAR   = 0x00,   ///< The pixel is not masked
+    PM_MASK_TRAP    = 0x01,   ///< The pixel is a charge trap.
+    PM_MASK_BADCOL  = 0x02,   ///< The pixel is a bad column.
+    PM_MASK_SAT     = 0x04,   ///< The pixel is saturated.
+    PM_MASK_BAD     = 0x08,   ///< The pixel is low
+    PM_MASK_FLAT    = 0x10,   ///< The pixel is non-positive in the flat-field.
+    PM_MASK_MARK    = 0x20,   ///< The pixel is marked as temporarily ignored
+    PM_MASK_EXT1    = 0x40,   ///< This mask value is not used
+    PM_MASK_EXT2    = 0x80,   ///< This mask value is not used
+} pmMaskValue;
+\end{datatype}
+
+\tbd{It would be nice to have a system of registering symbolic names
+for mask values at run time.}
+
+The following functions use the readout image and certain concepts to
+provide the mask and weight values.  There are two cases.  The first
+is that of ``setting'' the mask/weight, where the mask/weight that
+produced is temporary --- it is not added to the HDU.  This is
+intended for when the user is iterating using pmReadoutReadNext, in
+which case the HDU can't be generated.  The second case is that of
+``generating'' the mask/weight, where the mask/weight that is produced
+is suitable for output (complete with HDU entry); this is intended for
+most operations.
+
+In both cases, the mask is formed by identifying pixels are saturated
+(at or above \code{CELL.SATURATION}) or bad (at or below
+\code{CELL.BAD}); while the weights (actually variances) are
+calculated using photon statistics from the cell gain
+(\code{CELL.GAIN}) and read noise (\code{CELL.READNOISE})
+
+\begin{prototype}
+bool pmReadoutSetMask(pmReadout *readout ///< Readout for which to set mask
+                     );
+bool pmReadoutSetWeight(pmReadout *readout ///< Readout for which to set weight
+                       );
+bool pmReadoutGenerateMask(pmReadout *readout ///< Readout for which to generate mask
+                          );
+bool pmReadoutGenerateWeight(pmReadout *readout ///< Readout for which to generate weight
+                            );
+\end{prototype}
+
+The following functions are intended for convenience, generating a
+mask and weight map for a readout or cell:
+\begin{prototype}
+bool pmReadoutGenerateMaskWeight(pmReadout *readout ///< Readout for which to generate mask and weights
+                                );
+bool pmCellGenerateMaskWeight(pmCell *cell ///< Cell for which to generate mask and weights
+                             );
+\end{prototype}
+
+
+\subsection{Copying: Converting between formats}
+
+The following functions copy components of an FPA hierarchy to a
+different representation of the same camera These functions are useful
+for converting between different representations of the same camera.
+For example, between Megacam "RAW" (one amp per extension) and Megacam
+"SPLICED" formats (two amps = 1 chip per extension, spliced together).
+Components are spliced together as necessary.
+
+\begin{prototype}
+bool pmFPACopy(pmFPA *target,           ///< The target FPA
+               const pmFPA *source      ///< The source FPA, to be copied
+              );
+bool pmChipCopy(pmChip *target,         ///< The target chip
+                const pmChip *source    ///< The source chip, to be copied
+               );
+bool pmCellCopy(pmCell *target,         ///< The target cell
+                const pmCell *source    ///< The source cell, to be copied
+               );
+\end{prototype}
+
+The following functions are similar, except that the pixels are not
+copied (although images of sufficient size are allocated in the
+target), and the \code{CELL.XBIN} and \code{CELL.YBIN} are changed
+according to the provided binning factors.  These functions are
+intended for binning a camera, where the user will do the binning.
+
+\begin{prototype}
+bool pmFPACopyStructure(pmFPA *target,   ///< The target FPA
+                        const pmFPA *source, ///< The source FPA, to be copied
+                        int xBin, int yBin ///< Binning factors in x and y
+                       );
+bool pmChipCopyStructure(pmChip *target, ///< The target chip
+                         const pmChip *source, ///< The source chip, to be copied
+                         int xBin, int yBin ///< Binning factors in x and y
+                        );
+bool pmCellCopyStructure(pmCell *target, ///< The target cell
+                         const pmCell *source, ///< The source cell, to be copied
+                         int xBin, int yBin ///< Binning factors in x and y
+                        );
+\end{prototype}
+
+
+\subsection{Mosaicking: Converting between cameras}
+
+The following functions mosaic cells within the appropriate hierarchy
+component into a single cell within the target (which must have only a
+single cell).  These functions are therefore useful for converting to
+a different camera, which is generally a false representation of the
+physical camera; in this manner, one can obtain an image of the chip
+or FPA on the sky.  Cells are placed on the chip according to the
+\code{CELL.X0} and \code{CELL.Y0} offsets.  The mosaicking is done so
+as to avoid performing a deep copy of the pixels, if possible.
+
+\begin{prototype}
+bool pmChipMosaic(pmChip *target,       // Target chip --- may contain only a single cell
+                  const pmChip *source  // Source chip whose cells will be mosaicked
+                 );
+bool pmFPAMosaic(pmFPA *target,         // Target FPA --- may contain only a single chip with a single cell
+                 const pmFPA *source    // FPA whose chips and cells will be mosaicked
+                );
+\end{prototype}
+
+
+\subsection{FPA Files}
+
+\tbd{Below is the old text (it just stops suddenly).  EAM to provide
+from the following files: pmFPAfileDefine.h pmFPAfileFitsIO.h
+pmFPAfile.h pmFPAfileIO.h pmFPA\_MANAPLOT.h pmFPA\_JPEG.h }
+
+The FPA structure defines the layout of the data read from a camera.
+It contains elements to specify the location of the pixels within the
+focal plane structure.  It also provides the relationship between a
+specific hardware layout of the pixels (readouts, cells, chips, etc)
+and a specific organization model for the data (headers and pixel
+array sections, etc).  Depending on the needs of the users or the
+desires of the instrument builders, the data read out from a single
+camera may be stored in a number of different ways.  For example,
+multiple cells within the same chip may be stored within the same
+pixel array in different locations, within separate arrays in the same
+FITS file in different extensions, or as separate files.  Multple
+chips may be written to the same file or different files.  Multiple
+readouts may be separate files or slices of an image cube.  
+
+The pmFPA structure does not (and should not) define the relationship
+between the pixel data and the actual files files on disk.  
+
+The pmFPAfile structure defines a relationship between the data stored
+in a pmFPA structure and an external (file) representation of that
+data.  The purpose of this structure and its supporting functions is
+to allow the application designer to easily add or changes the data
+sources and the data destinations used by an application.  The
+pmFPAfile defines the input and output data formats.  A single
+pmFPAfile may represent FITS image data files, jpeg image files,
+photometric measurements, astrometric calibrations, etc.  Each
+instance of a pmFPAfile is identified with a particular data format.
+It is defined as a data source (file from which to read data) or a
+data destination (file to be written).  The pmFPAfile also defines the
+naming conventions to be used for the particular type of file or set
+of files.  A single pmFPAfile may be used to represent data coming
+from or going to a single file on disk or a collection of files on
+disk.  The pmFPAfile also defines anciliary data which is needed for
+some file formats and not for others.  For example, FITS file
+extensions need to be given particular EXTNAME values.  Or, jpeg
+images require a colormap and clipping rules. 
+
+The pmFPAfile system also lets the application designer have
+flexibility with which types of files are required within a particular
+program.  Consider an application which performs some manipulation of
+an image.  The program may call several analysis modules as it
+operates on the data.  For example, it may perform a bias subtraction
+on the image pixels data.  Or, it may detect objects within another
+module.  The pmFPAfile system allows the author of a module to refer tperform The program may have a few 
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Astrometry}
+
+\tbd{EAM needs to update this section.}
+
+\code{pmAstromWCS} is a structure which represents the FITS WCS
+keywords in a single header.  In a traditional astronomy image, this
+includes the conversion from pixel coordinates via rotations, scaling,
+and offset, to a coordinate system corresponding to linear degree
+offsets relative to the reference pixel.  This linear (2D) coordinate
+frame is then converted to a spherical (3D) coordinate frame via a
+defined projection.
+
+
+Astrometry is a basic functionality required for the IPP that will be
+used repeatedly, both for low-precision (roughly where is my favorite
+object?) and high-precision (what is the proper motion of this star?).
+As such, it must be flexible, yet robust.
+
+\subsection{Coordinate frames}
+\label{sec:coordinateFrames}
+
+There are five coordinate frames that we need to worry about for the
+purposes of astrometry:
+\begin{itemize}
+\item Cell: $(x,y)$ in pixels --- raw coordinates;
+\item Chip: $(X,Y)$ in pixels --- the location on the silicon;
+\item Focal Plane: $(p,q)$ in microns --- the location on the focal plane;
+\item Tangent Plane: $(l,m)$ in arcsec from the telescope boresight; and
+\item Sky: (RA,Dec) --- ICRS.
+\end{itemize}
+
+The following steps are required to convert from the cell coordinates to
+the sky:
+\begin{itemize}
+\item Cell $\longleftrightarrow$ Chip: two 2D polynomials, $(X,Y) = f(x,y)$;
+\item Chip $\longleftrightarrow$ FP: two 2D polynomials, $(p,q) = g(X,Y)$;
+\item FP $\longleftrightarrow$ TP: two 4D polynomials, $(l,m) =
+h(p,q,m,c)$, where $m$ and $c$ are the magnitude and color of the
+object, respectively; and
+\item TP $\longleftrightarrow$ Sky:  transformation to the sky using
+pre-computed coefficients for each pointing.
+\end{itemize}
+
+Note that the transformation between the Focal Plane and the Tangent
+Plane is a four-dimensional polynomial, in order to account for any
+possible dependencies in the astrometry on the stellar magnitude and
+color; the former serves as a check for charge transfer
+inefficiencies, while the latter will correct chromatic refraction,
+both through the atmosphere and the corrector lenses.
+
+We require structures to contain each of the above transformations as
+well as the pixel data.
+
+\subsection{FITS World Coordinate System}
+
+The FITS World Coordinate System (WCS) headers are commonly employed
+with astronomical images in order to relate pixels to celestial (or
+otherwise) coordinates.  Since it is a FITS standard, we must be able
+to read and write from WCS into our internal format.  For the time
+being, we will consider only celestial WCS (i.e., no spectral
+wavelength calibrations, etc).  Because WCS does not support the
+multiple layers that we have built for \PS{}, we will use a simple
+internal representation: a transformation, which handles any
+distortions (i.e., goes directly from the coordinate frame of the
+image to the tangent plane); and the projection.
+
+\begin{prototype}
+bool pmAstromReadWCS(psPlaneTransform **transform, // Output transformation
+                     psProjection **projection, // Output projection
+                     psMetadata *header // Input FITS header
+);
+bool pmAstromWriteWCS(psMetadata *header, // Output FITS header
+                      psPlaneTransform *transform, // Input transformation
+                      psProjection *projection // Input projection
+);
+\end{prototype}
+
+\code{pmAstromReadWCS} shall parse the specified FITS \code{header},
+returning new instances of the \code{transform} and \code{projection}
+that represent the WCS.  The function shall return \code{true} if it
+was able to successfully generate the outputs; otherwise it shall
+return \code{false}.
+
+\code{pmAstromWriteWCS} shall add WCS keywords to the supplied FITS
+\code{header} that implement the given \code{transform} and
+\code{projection}.  The function shall return \code{true} if it was
+able to successfully generate the output; otherwise it shall return
+\code{false}.
+
+\begin{verbatim}
+bool pmAstrometrySimplify(psPlaneTransform **transform, // Output transformation
+                          psProjection **projection, // Output projection
+                          pmCell *cell // Cell for which to generate transform and projection
+);
+\end{verbatim}
+
+\code{pmSimplifyAstrometry} shall take a \code{cell} and simplify the
+internal astrometric representation (\code{cell->toFPA} or equivalent,
+\code{cell->parent->parent->toTangentPlane} and
+\code{cell->parent->parent->grommit}) to a single \code{transform} and
+\code{projection}.  This allows the subsequent use of
+\code{pmWriteAstrometry} in the case that we have only the
+multi-layered \PS{} internal astrometric representation.  The function
+shall return \code{true} if it was able to successfully generate the
+output; otherwise it shall return \code{false}.
+
+\subsection{Astrometry Analysis}
+
+Astrometry is performed on an astronomical image after a collection of
+sources in the image have been detected and thier instrumental
+positions have been measured.  In this collection of tools, the
+coordinates should be measured in the frame of the \code{pmReadout}
+portion of the Image Hierarchy.  This potentially allows us to measure
+an astrometric transformation resulting from the transformation to any
+of the other coordinate system.  For example, it might be necessary to
+determine the coordinates of the \code{psReadout} pixels relative to
+the \code{psCell} (rather than accept the relationship defined by the
+metadata).
+
+For the moment, we define two layers of astrometric analysis which
+will be performed on typical mosaic images in the Pan-STARRS IPP:
+per-chip astrometry and per-mosaic astrometry.  In the first case, a
+collection of detections across a single chip are used to determine a
+basic, linear transformation to celestial coordinates.  This
+astrometric analysis can be used to determine an initial, approximate
+astrometry for each chip in a mosaic camera, with accurcy limited by
+the effects of optical distortion (a few pixels error, typically).  In
+the second case, a collection of detectors on a collection of chips
+from a mosaic camera are used to measure the position of the telescope
+boresight, the camera rotation, the impact of distortion from the
+telescope optics, and the chip-to-focal plane transformation resulting
+from chip placement errors or possible detector tilts or warps.
+
+The process of performing astrometry involves the following steps:
+\begin{itemize}
+\item Make an initial guess at the celestial coordinates of the raw
+  detections (eg, from metadata or image header information).
+\item Determine, and load, stars from an astrometric catalog which may
+  potentially correspond to the raw detections.
+\item Project both raw and reference stars to a common coordinate
+  frame (here we use the Focal Plane as an appropriate coordinate
+  system). 
+\item Identify matches between the raw and reference stars.
+\item Determine the transformations necessary to relate the
+  coordinates of the two sets of stars.
+\item Convert the measured transformations into appropriate terms in
+  the astrometric elements of the Image Hierarchy.
+\end{itemize}
+In this section, we specify several functions which together make
+possible the above analysis steps.  
+
+\subsubsection{Astrometry Objects}
+
+We define the following structure to carry the necessary information
+about each detection.  
+\begin{datatype}
+typedef struct {
+    psPlane pix;
+    psPlane FP;
+    psPlane TP;
+    psSphere sky;
+    double mag;
+} pmAstromObj;
+\end{datatype}
+This structure specifies the coordinate of the detection in each of
+the four necessary coordinate frames: \code{pix} defines the position
+in the \code{psReadout} frame, \code{FP} defines the position in the
+Focal Plane frame, \code{TP} defines the position in the Tangent Plane
+frame, \code{sky} defines the position on the Celestial Sphere.  In
+addition, a measurement of the brightness is given by the element
+\code{Mag}.  Such a data structure should be used for both the raw and
+the reference stars.  In astrometric processing, the raw detections
+will be projected using the best available information to each of
+these coordinate frames from the \code{pix} coordinates, while the
+reference detections will be projected to the other frames from the
+\code{sky} coordinates.
+
+The raw detections and the reference stars are both projected to a
+common coordinate frame for analysis.  In these modules, we use the
+Focal Plane for this reference frame.  After projection to the common
+frame, it is necessary to determine the match between corresponding
+objects in the two lists.  In order to match the raw detections to the
+reference stars, different methods are used depending on the
+circumstance.
+
+\subsubsection{Matching Stars : Close Match}
+
+If the two sets of coordinates are expected to agree very well (ie,
+the current best-guess astrometric solution is quite close to the
+`true' astrometric solution), then it is possible to use the simplest
+matching process: cross-correlation within a fixed radius.  The
+following function accepts two sets of \code{pmAstromObj} sources and
+determines the matched objects between the two lists.  The input
+sources must have been projected to the Focal Plane coordinates
+(\code{pmAstromObj.FP}), and the supplied \code{options} entry must
+contain the desired match radius (keyword:
+\code{ASTROM.MATCH.RADIUS}).  The output consists an array of
+\code{pmAstromMatch} values, defined below.
+
+\begin{prototype}
+psArray *pmAstromRadiusMatch (psArray *starlist1, psArray *starlist2, psMetadata *options);
+\end{prototype}
+
+\begin{datatype}
+typedef struct {
+    int idx1;
+    int idx2;
+} pmAstromMatch;
+\end{datatype}
+The \code{pmAstromMatch} structure defines the cross-correlation
+between two arrays.  An single such data item specifies that item
+number \code{pmAstromMatch.idx1} in the first list corresponds to
+\code{pmAstromMatch.idx2} in the second list.  
+
+\subsubsection{Matching Stars : Rough Match}
+
+If the two sets of coordinates are not known to agree well, a somewhat
+different approach is needed.  Several algorithms have been defined in
+the past to correlate two lists with unknown offsets, and potentially
+unknown relative rotations and scaling.  One well-known method is the
+triangle-match algorithm which searches for similar triangles observed
+in the two lists.  This algorithm has the advantage of not requiring
+the rotation or the scale to be well-known in advance.  The
+disadvantage of the triangle match algorithm is that it is necessarily
+an $O(N^3)$ process since it is necessary to construct a substantial
+fraction of all possible triangles for both input lists.  \tbd{we do
+  not define a triangle match algorithm at this time}.
+
+If the two sets of coordinates are not known to agree well, but the
+relative scale and approximate relative rotation is known, then a much
+faster match can be found using pair-pair displacements.  In such a
+case, the two lists can be considered as having the same coordinate
+system, with an unknown relative displacement.  In this algorithm, all
+possible pair-wise differences between the source positions in the two
+lists are constructed and accumulated in a grid of possible offset
+values.  The resulting grid is searched for a cluster representing the
+offset between the two input lists.  This algorithm can only tolerate
+a small error in the relative scale or the relative rotation of the
+two coordinate lists.  However, this process is naturally $O(N^2)$,
+and is thus advantageous over triangle matching in some circumstances.
+This process can be extended to allow a larger uncertainty in the
+relative rotation by allowing the procedure to scan over a range of
+rotations.  We define the following function to apply this matching algorithm: 
+\begin{prototype}
+pmAstromStats pmAstromGridMatch (psArray *raw, psArray *ref, psMetadata *options);
+\end{prototype}
+The input sources must have been projected to the Focal Plane
+coordinates (\code{pmAstromObj.FP}), and the supplied \code{options}
+entry must contain the following user-defined parameters:
+\begin{itemize}
+\item \code{GRID.OFFSET}    : maximum allowed displacement in search
+\item \code{GRID.SCALE}     : grid bin size in focal-plane coordinate units
+\item \code{GRID.MIN.ANGLE} : minimum tested relative rotation 
+\item \code{GRID.MAX.ANGLE} : maximum tested relative rotation 
+\item \code{GRID.DEL.ANGLE} : relative rotation step size
+\end{itemize}
+Note that the angles are defined as clockwise rotations of \code{raw}
+relative to \code{ref}.
+
+This function returns values in the \code{pmAstromStats} structure, defined as: 
+\begin{datatype}
+typedef struct {
+    psPlane center;
+    psPlane offset;
+    double  angle;
+    double  minMetric;
+    double  minVar;
+    int     nMatch;
+} pmAstromStats;
+\end{datatype}
+The elements \code{angle} and \code{offset} define the best rotation
+and offset; the element \code{nMatch} indicates the number of matched
+sources which fell within the match bin; the element \code{minVar}
+specifies the variance of the sources within the match bin; the
+element \code{minMetric} specifies the value of the selection metric
+for the matched bin.  Note that the metric of choice may not
+necessarily be either the simple number of sources or the varience.
+We find that a combination based on both which enhances the importance
+of having a well-populated bin with a minimal variance gives good
+results: $\mbox{metric} = \mbox{var} \times N^{-4}$.  The element
+\code{center} defines the center of rotation used for rotating the
+\code{raw} stars relative to the \code{ref} stars.
+
+The result of a \code{pmAstromGridMatch} may be used to modify a
+\code{psPlaneTransform} structure \code{map}.  The result of
+\code{pmAstromGridMatch} can be translated into adjustments of the
+\code{psPlaneTransform} (ie the rotation and offset).  This adjustment
+is made using the function:
+\begin{prototype}
+bool pmAstromGridApply (psPlaneTransform *map, pmAstromStats stats);
+\end{prototype}
+This function modifies the supplied \code{map} entry assuming the
+adjustments are relative to the provided transformation.
+
+We define two additional functions which are used in
+\code{pmAstromGridMatch}, but which may be useful on their own:
+
+\begin{prototype}
+pmAstromStats pmAstromGridAngle (psArray *raw, psArray *ref, psMetadata *options);
+\end{prototype}
+This function is identical to \code{pmAstromGridMatch}, but is valid
+for only a single relative rotation.  The input \code{config}
+information need not contain any of the \code{GRID.*.ANGLE} entries
+(they will be ignored).
+
+\begin{prototype}
+psArray *pmAstromRotateObj (psArray *old, psPlane center, double angle);
+\end{prototype}
+This function accepts an array of \code{pmAstromObj} objects and
+rotates them by the given \code{angle} about the given center
+coordinate \code{center} in the Focal Plane Array coordinates.
+
+\subsubsection{Astrometry Fitting Routines}
+
+The result of a \code{pmAstromRadiusMatch} operation is a list of
+matched entries between the two input lists.  This list may be used to
+determine a linear fit between the two sets of matched sources.  The
+following function performs this operation:
+\begin{prototype}
+bool pmAstromFitFPA (pmFPA *fpa, psArray *st1, psArray *st2, psArray *match, psMetadata *config);
+\end{prototype}
+This function accepts the raw and reference source lists and the list
+of matched entries.  It uses the matched list to determine a
+polynomial transformation between the two coordinate systems.  The
+fitting uses clipping to exclude outliers, likely representing poor
+matches.  The \code{config} element must contain the information
+\code{ASTROM.NSIGMA} (specifying the number of sigma used in the
+clipping) and \code{ASTROM.NCLIP} (specifying the number of clipping
+iterations must be performed).  The \code{config} element must also
+specify the order of the polynomial fit (keyword:
+\code{ASTROM.ORDER}).  The result of this fit is a set of
+modifications of the components of the \code{pmFPA.toTangentPlane}
+transformation, and the modifications of the reference coordinate of
+the projection (\code{pmFPA.projection.R,D}) and the projection scale
+(\code{pmFPA.projection.Xs,Ys}).  The modifications to
+\code{pmFPA.toTangentPlane} incorporate the rotation component of the
+linear terms and the higher-order terms of the polynomial fits.
+
+An alternative to fitting the rotation of the FPA relative to the
+Tangent Plane is to treat the fitted transformation as a measurement
+of the chip within the FPA.  The following function performs this
+operation in the same way as \code{pmAstromFitFPA}:
+\begin{prototype}
+bool pmAstromFitChip (pmFPA *fpa, psArray *st1, psArray *st2, psArray *match, psMetadata *config);
+\end{prototype}
+This function accepts the raw and reference source lists for a single
+chip and the list of matched entries.  It uses the matched list to
+determine a polynomial transformation between the two coordinate
+systems.  The fitting uses clipping to exclude outliers, likely
+representing poor matches.  The \code{config} element must contain the
+information \code{ASTROM.NSIGMA} (specifying the number of sigma used
+in the clipping) and \code{ASTROM.NCLIP} (specifying the number of
+clipping iterations must be performed).  The \code{config} element
+must also specify the order of the polynomial fit (keyword:
+\code{ASTROM.ORDER}).  The result of this fit is a set of
+modifications of the components of the \code{pmChip.toFPA}
+transformation.
+
+A mosaic represents a particular set of challenges when determining an
+astrometric solution.  There is substantial degeneracy between the
+astrometric terms which describe the transformationfrom the chip to
+the focal plane, and the tranformation from the focal plane to the
+tangent plane, in the presence of distortion.  The degeneracy can be
+broken by examining the distortion component in its effect on the
+gradient of the sources position residuals rather than in the source
+positions themselves.  The following function determines the position
+residual, in the tangent plane, as a function of position in the focal
+plane, for a collection of raw measurements and matched reference
+stars.  The configuration data must include the bin size over which
+the gradient is measured (keyword: \code{ASTROM.GRAD.BOX}).  The
+function returns an array of \code{pmAstromGradient} structures,
+defined below.
+\begin{prototype}
+psArray pmAstromMeasureGradients (psArray *starlist1, psArray *starlist2, psArray *match, psMetadata *config);
+\end{prototype}
+
+The following data structure carries the information about the
+residual gradient of source positions in the tangent plane
+(\code{pmAstromObj.TP}) as a function of position in the focal plane
+(\code{pmAstromObj.FP}).
+\begin{datatype}
+typedef struct {
+    psPlane FP;
+    psPlane dTPdL;
+    psPlane dTPdM;
+} pmAstromGradient;
+\end{datatype}
+
+The gradient set measured above can be fitted with a pair of 2D
+polynomials.  The resulting fits can then be related back to the
+implied polynomials which represent the distortion.  The following
+function performs the fit and applies the result to the distortion
+transformation of the supplied \code{pmFPA} structure.  The
+configuration variable supplies the polynomial order (keyword:
+\code{ASTROM.DISTORT.ORDER}).
+\begin{prototype}
+psArray pmAstromFitDistortion (pmFPA *fpa, psArray *gradients, psMetadata *config);
+\end{prototype}
Index: /tags/sj_tags/sj_root_20080929/doc/modules/ChangeLogSDRS.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/modules/ChangeLogSDRS.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/modules/ChangeLogSDRS.tex	(revision 22322)
@@ -0,0 +1,199 @@
+%%% $Id: ChangeLogSDRS.tex,v 1.44 2006-10-25 01:48:17 price Exp $
+
+\subsection{Changes from version 00 (16 August 2004) to version 01 (12 October 2004)}
+
+\begin{itemize}
+\item clarified the image offsets for pmFlatField ()
+\item changed return value to bool.
+\item added \code{pmCameraFromHeader}
+\item added \code{pmCameraValidateHeaders}
+\item added \code{pmFPAfromHeader}
+\item Added \code{pmReadoutCombine}
+\end{itemize}
+
+\subsection{Changes from version 01 (12 October 2004) to version 02 (30 November 2004)}
+
+\begin{itemize}
+\item \code{nBin} in \code{pmSubtractBias} is also interpreted as the
+  number of spline pieces if spline fitting is specified.
+\item Refined \code{pmReadoutCombine} specification in response to bug 227.
+\item added details to the functions \code{pmCameraFromHeader}, \code{pmCameraValidateHeaders}, and \code{pmFPAfromHeader}.  
+
+\item reorganiztion: placed configuration section up front, camera layout next, etc
+\item added details about configuration system
+\item added utility modules \code{pmConfigLoadSite}, \code{pmConfigLoadCamera}, \code{pmConfigLoadRecipe}
+\item added utility modules \code{pmConfigLookupSTR}, \code{pmConfigLookupS32}, \code{pmConfigLookupF64}, \code{pmConfigLookupRegion}, 
+\item added discussion about Coordinate transforms
+\item added discussion about \code{pmReadoutLoad}
+\item added module \code{pmSubtractSky}
+\end{itemize}
+
+\subsection{Changes from version 02 (30 November 2004) to version 03 (21 January 2005) }
+
+\begin{itemize}
+\item Fixed up specification of \code{fitSpec} for \code{pmSubtractSky}.
+\item Added a \code{mask} to \code{pmSubtractSky}, and specified that binned pixels which are clipped may be interpolated over, or simply ignored.
+\item Added further explanation for \code{pmReadoutCombine}.
+\item Added Object Detection section
+\item Added PSPhot pseudo-C example
+\end{itemize}
+
+\subsection{Changes from version 03 (21 January 2005) to version 04 (14 February 2005)}
+
+\begin{itemize}
+\item changed entries of the form psXXX to pmXXX (object section)
+\item added enum psSourceType
+\item Specified appropriate image types for the phase 2 modules (bug 258).
+\item clarified pmSourceRoughClass
+\item clarified pmSourceAddModel, pmSourceSubModel
+\end{itemize}
+
+\subsection{Changes from version 04 (14 February 2005) to version 05 (21 March 2005)}
+
+\begin{itemize}
+\item Added section on image combination
+\item Added section on image subtraction
+\end{itemize}
+
+\subsection{Changes from version 05 (21 March 2005) to version 06 (27 April 2005)}
+
+\begin{itemize}
+\item changed \code{pmFindImagePeaks} to return an array, not a list
+\item replaced \code{pmCullPeaks} with \code{pmPeaksSubset} which returns a new array
+\item changed \code{pmModel} to use vectors for params and dparms.
+\item added nDOF and nIter to pmModel
+\item changed models to return psF64, not psF32, to match psMinimizeLMChi2Func
+\end{itemize}
+
+\subsection{Changes from version 06 (27 April 2005) to version 07 (15 July 2005)}
+
+\begin{itemize}
+\item Changed \code{psRegion *region} to \code{psRegion region} in
+  prototypes (passed by value instead of by reference).
+\item \code{pmSourceMoments} does not require image parameter.
+\item Added \code{masks} to \code{pmRejectPixels}.
+\item Added \code{size} and \code{spatialOrder} to \code{pmSubtractionKernels}.
+\item added Image Hierarchy section from psLibSDRS
+\item added photometry section from psLibSDRS
+\item added object function abstractions to Objects
+\item modified pmSource to include modelPSF and modelFLT
+\item Major changes to configuration.  Modifyied \code{pmConfig}
+  functions, and \code{pmCameraFromHeader}, and added various other
+  functions.
+\item Modifying \code{psReadout, psCell, psChip, psFPA} structures.
+\item Removing \code{psObservatory, psExposure, psGrommit} which were
+  centered on slalib.
+\item Added \code{pmFPAConstruct, pmFPARead, pmFPAMorph, pmFPAWrite}.
+\end{itemize}
+
+\subsection{Changes from version 07 (15 July 2005) to version 08 (13 Sept 2005)}
+
+\begin{itemize}
+\item Added \code{bias} sections to \code{pmReadout}.
+\item Added \code{CELL.READDIR} to specify read direction; \code{pmCellGetReaddir}.
+\item \code{pmFPAMorph} specified, ready for coding.
+\item \code{pmConfigRead} shall call \code{psTimeInitialize}, \code{psLogSetLevel},
+  \code{psLogSetFormat}, \code{psTraceSetLevel}.
+\item Added \code{pmConfigDB}.
+\item Changed \code{pmNonLinearityLookup} to read a \code{psLookupTable}.
+\item Changed input types for \code{pmMaskBadPixels}.
+\item Added \code{pmFPAWriteMask}.
+\item \code{pmMaskBadPixels} shall grow the mask for saturated by 1, in addition
+  to the explicit grow.
+\item Added section on ``Paper Trail'' to Phase 2 functions.
+\item Adding log destination to \code{pmConfigRead}.
+\item Changing details of focal plane hierarchy.
+\item Concepts are evaluated at ingest by \code{pmFPARead}.
+\item Modified \code{pmSubtractBias}.
+\item Remove mask from \code{pmFlatField} (mask is contained in the readout).
+
+\item cleaned up / extended discussion of FITS and the FPA hierarchy.
+\item added \code{CONCEPT.DEFAULT} to \code{DEFAULTS} table.
+\item added \code{psArgumentVerbosity} requirement to \code{pmConfigRead}.
+
+\item substantial changes to the Objects section:
+\begin{itemize}
+  \item added \code{psphotMaskValues}?
+  \item added SN to \code{pmMoments}
+  \item added \code{pmPSFClump}
+  \item modified \code{pmSourceType} enum list
+  \item added radius to \code{pmModel}
+  \item added \code{pmModelGroup}
+  \item added several Model Group functions
+  \item added \code{pmPSF} and related functions
+  \item added \code{pmPSFtry} and related functions
+  \item added \code{pmSourceDefinePixels}
+  \item modified \code{pmSourceLocalSky} API
+  \item modified \code{pmSourceMoments} API
+  \item added \code{pmSourcePSFClump}
+  \item modified \code{pmSourceRoughClass} API
+  \item dropped \code{pmSourceSetPixelsCircle} (replaced with
+  \code{pmSourceDefinePixels} and the image mask functions.
+  \item added \code{pmModelFitStatus}
+  \item added \code{pmSourcePhotometry}
+  \item added \code{pmSourceDophotType}
+  \item added \code{pmSourceSextractType}
+  \item moved discussion of the object models to an appendix
+\end{itemize}
+\end{itemize}
+
+\subsection{Changes from version 08 (13 Sept 2005) to version 09 (18 Oct 2005)}
+
+\begin{itemize}
+  \item fix enum syntax
+  \item added Astrometry Fitting Support (matching / fitting routines)
+  \item added \code{pmAstromRadiusMatch}
+  \item added \code{pmAstromGridMatch}
+  \item added \code{pmAstromApplyGridMatch}
+  \item added \code{pmAstromMeasureGradients}
+  \item added \code{pmAstromFitFPA}
+  \item added \code{pmAstromFitChip}
+  \item added \code{pmAstromFitDistortion}
+  \item renamed section 7 to Detrend Creation, moved \code{pmReadoutCombine} to a subsection
+  \item added \code{pmFringeStats} (Fringe Amplitude subsection)
+  \item added \code{pmFlatNormalization} (Flat-field renorm section)
+  \item added \code{pmDetrendLookup}
+\end{itemize}
+
+\subsection{Changes from version 09 (18 Oct 2005) to version 10 (06 Dec 2005)}
+
+\begin{itemize}
+\item \code{pmSubtractBias}: The overscans are to be derived by the
+  function using \code{CELL.BIASSEC} (not from the \code{bias}
+  parameter passed to the function, which is supposed to hold the
+  full-frame bias image).
+\item \code{pmSubtractBias}: The full-frame (bias and dark)
+  subtractions should only be performed on the region of the image
+  specified by \code{CELL.TRIMSEC}.
+\item \code{pmNonLinearityPolynomial}, \code{pmNonLinearityLookup},
+  \code{pmFlatField}, \code{pmMaskBadPixels}, \code{pmSubtractSky} are
+  to act only on the region of the readout image specified by
+  \code{CELL.TRIMSEC}
+\item Added \code{p_pmHDU}.
+\item Changed \code{pmFPA,pmChip,pmCell->private} to \code{hdu}.
+
+\item changed \code{pmAstrometryReadWCS} to \code{pmAstromReadWCS}
+\item changed \code{pmAstrometryWriteWCS} to \code{pmAstromWriteWCS}
+\item changed \code{pmAstromGridMatch} to output \code{pmAstromStats}
+\item moved various \code{pmAstromGridMatch} output values from metadata 
+to the output \code{pmAstromStats}
+\item changed \code{pmAstromGridMatchAngle} to \code{pmAsromGridAngle}
+\item changed \code{pmAstromGridAngle} to output \code{pmAstromStats}
+\item changed \code{pmAstromRotateObj} to accept center as \code{psPlane}
+
+\end{itemize}
+
+\subsection{Changes from version 10 (06 Dec 2005) to version 11 (22 Jan 2006)}
+
+\begin{itemize}
+\item modification of bias subtraction API
+\item updates to object/psphot APIs
+\end{itemize}
+
+\subsection{Changes from version 11 (22 Jan 2006) to present}
+
+\begin{itemize}
+\item clarified pmFindImagePeaks to use parent coordinates
+\item Major changes to bring spec up to date with code.
+\end{itemize}
+
Index: /tags/sj_tags/sj_root_20080929/doc/modules/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/modules/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/modules/Makefile	(revision 22322)
@@ -0,0 +1,43 @@
+# $Id: Makefile,v 1.10 2006-01-16 19:14:29 eugene Exp $
+
+PDFLATEX = env TEXINPUTS=../../latex/inputs:$(TEXINPUTS):.: pdflatex
+PSLATEX  = env TEXINPUTS=../../latex/inputs:$(TEXINPUTS):.: latex
+
+help:
+	@echo "USAGE: make (target)"
+	@echo "  targets: sdrs all sdrs-tag"
+
+all : sdrs
+sdrs : ModulesSDRS.pdf
+
+SDRS = ModulesSDRS.tex \
+       ChangeLogSDRS.tex \
+       CameraImages.tex \
+       pics/CameraLayout.ps \
+       ../pslib/pics/coordinateFrames.ps \
+       ../pslib/pics/coordinateConv.ps
+
+ModulesSDRS.pdf: $(SDRS)
+
+sdrs-tag:
+	cvs tag `grep "version{" ModulesSDRS.tex | tr "{}" " " | awk '{printf "MSDR-%02d\n", $$2}'` $(SDRS)
+
+%.pdf: %.tex
+	$(PSLATEX) $*.tex 
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	thumbpdf --modes=dvips $*.pdf
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	@rm -f $*.ps $*.dvi $*.aux $*.log $*.tbr $*.tbd $*.tpm  $*.lof $*.toc body.tmp head.tmp
+
+clean :
+	$(RM) *.log *.dvi *.aux *.toc *.tbd *.tbr *.tpm *.lof *.out *~ core body.tmp head.tmp
+
+dist : clean
+	$(RM) *.pdf
+
+empty : clean
+
Index: /tags/sj_tags/sj_root_20080929/doc/modules/ModulesSDRS.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/modules/ModulesSDRS.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/modules/ModulesSDRS.tex	(revision 22322)
@@ -0,0 +1,3429 @@
+%%% $Id: ModulesSDRS.tex,v 1.81 2007-02-03 00:12:36 eugene Exp $
+\documentclass[panstarrs,spec]{panstarrs}
+
+% basic document variables
+\title{Pan-STARRS PS-1 Image Processing Pipeline Modules}
+\subtitle{Supplementary Design Requirements}
+\shorttitle{Modules SDRS}
+\author{Paul Price, Eugene Magnier}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{11}
+\docnumber{PSDC-430-012}
+
+\begin{document}
+\maketitle
+\sloppy
+
+% -- Revision History --
+% provide explicit values for the old versions
+% use '\theversion' for the current version (set above)
+% use \hline between each table row
+\RevisionsStart
+% version  Date            Description
+DR & 2004 Jun 7 & Draft \\ \hline
+00 & 2004 Aug 16 & final for cycle 3 \\ \hline
+01 & 2004 Oct 12 & draft for cycle 4 \\ \hline
+02 & 2004 Nov 30 & final for cycle 4 \\ \hline
+03 & 2005 Jan 21 & draft for cycle 5 \\ \hline
+04 & 2005 Feb 14 & final for cycle 5 \\ \hline
+05 & 2005 Mar 21 & draft for cycle 6 \\ \hline
+06 & 2005 Apr 27 & final for cycle 6 \\ \hline
+07 & 2005 Jul 15 & final for cycle 7 \\ \hline
+08 & 2005 Sep 13 & final for cycle 8 \\ \hline
+09 & 2005 Oct 18 & final for cycle 9 \\ \hline
+10 & 2005 Dec 06 & draft for cycle 10 \\ \hline
+11 & 2006 Jan 22 & revisions for CDR \\ \hline
+\RevisionsEnd
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\DocumentsInternal
+PSCD-230-001  &   PS-1 Design Reference Mission \\ \hline
+PSDC-430-004  &   Pan-STARRS PS-1 IPP C Code Conventions \\ \hline
+PSDC-430-005  &   Pan-STARRS PS-1 IPP Software Requirements Specification \\ \hline
+PSDC-430-006  &   Pan-STARRS PS-1 IPP Algorithm Design Document \\ \hline
+PSDC-430-011  &   Pan-STARRS PS-1 IPP System/Subsystem Design Description \\ \hline
+PSDC-430-???  &   Pan-STARRS PS-1 IPP Configurations \\ \hline
+\DocumentsExternal
+Posix Standard & Open Group Based Specifications Issue 6, IEEE Std 1003.1, 2003 \\
+\DocumentsEnd
+
+\tableofcontents
+\pagebreak 
+
+\listoffigures
+\pagebreak 
+\pagenumbering{arabic}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Introduction}
+
+This document describes the Pan-STARRS Image Processing Pipeline (IPP)
+data analysis Modules.  The Modules use the functionality of the
+Pan-STARRS Library (PSLib) to perform more complex tasks, especially
+tasks which require assumptions of astronomical analysis or the data
+organization.  Within the IPP, the Modules are tied together into
+programs which perform complete data analysis tasks (an ``analysis
+stage'').  The modules may be tied together within a C framework or
+using a high-level scripting language.
+
+In order to preserve name space, globally-visible structures and
+functions shall be prefixed with \code{pm}, for ``Pan-STARRS
+Modules''.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Configuration}
+
+Configuration information in the IPP is provided on four levels: the
+site, camera, format and recipe configurations.  The use of these, and
+their contents are described in an additional document, PSDC-430-???,
+``Pan-STARRS PS1 IPP Configurations''.  Here we define functions that
+read these configuration files.
+
+
+\subsection{Configuration information}
+
+This structure stores the configuration information: the site, camera
+and recipe configuration, the command-line arguments, the pmFPAfiles
+used, and the database handle.
+\begin{datatype}
+typedef struct {
+    psMetadata *site;                   ///< Site configuration
+    psMetadata *camera;                 ///< Camera specification
+    const char *cameraName;             ///< Camera name
+    psMetadata *recipes;                ///< Recipes for processing
+    psMetadata *arguments;              ///< Processed command-line arguments
+    psMetadata *files;                  ///< pmFPAfiles used for analysis
+    psDB *database;                     ///< Database handle
+    int *argc;                          ///< Number of command-line arguments
+    char **argv;                        ///< Command-line arguments (raw version)
+    // Private members
+    p_pmRecipeSource recipesRead;       ///< Which recipe sources have been read
+    psMetadata *recipesSource;          ///< Where each recipe came from
+} pmConfig;
+\end{datatype}
+
+The site, camera and recipe configurations are represented here.  The
+camera format configuration is stored elsewhere (within the
+\code{pmFPA}).
+
+Note that the \code{arguments} are different from the \code{argc} and
+\code{argv}, since it is the parsed version.  This metadata may also
+be used to carry program options, if desired.
+
+The allocator for this structure is:
+\begin{prototype}
+pmConfig *pmConfigAlloc(int *argc,      /// Number of command-line arguments
+                        char **argv     /// Command-line arguments
+                       );
+\end{prototype}
+
+
+\subsection{Reading configuration files}
+
+Read configuration information from the command line:
+\begin{prototype}
+pmConfig *pmConfigRead(int *argc,       ///< Number of command-line arguments
+                       char **argv      ///< Array of command-line arguments
+                      );
+\end{prototype}
+pmConfigRead loads the site configuration (the file name is specified
+by "-site SITE\_FILE" on the command-line, the PS\_SITE environment variable, or
+it is \code{$HOME/.ipprc}).  The configuration search %$ path is
+set. The camera configuration is loaded if it is specified on the
+command line ("-camera CAMERA\_FILE"). Recipes specified on the command
+line ("-recipe RECIPE\_NAME RECIPE\_SOURCE") are also loaded.  These
+command-line arguments are removed from from the command-line, to
+simplify parsing.  The psLib log, trace and time setups are also
+performed if specified in the site configuration.
+
+Read a configuration file:
+\begin{prototype}
+bool pmConfigFileRead(psMetadata **config, ///< Config to output
+                      const char *name, ///< Name of file
+                      const char *description ///< Description of file
+                     );
+\end{prototype}
+Read a metadata configuration file into the supplied metadata.
+Produce an error and return false if there's a problem.
+
+Set static configuration information:
+\begin{prototype}
+void pmConfigSet(const char *path ///< Search paths for configuration files; colon-delimited directories
+                );
+\end{prototype}
+The search path for the configuration files is a local static
+variable, set by this function.
+
+Free static memory used in the configuration system:
+\begin{prototype}
+void pmConfigDone(void);
+\end{prototype}
+
+
+\subsection{Camera configuration}
+
+Validate a header against the camera format:
+\begin{prototype}
+bool pmConfigValidateCameraFormat(const psMetadata *cameraFormat, ///< Camera format containing the RULE
+                                  const psMetadata *header // FITS header for the PHU
+                                 );
+\end{prototype}
+Given a FITS header (the PHU header), check it against the RULE
+metadata contained within the camera format; return true if it
+matches.
+
+Determine the camera format (and camera if unknown) from examining the
+header:
+\begin{prototype}
+psMetadata *pmConfigCameraFormatFromHeader(pmConfig *config, ///< The configuration
+        const psMetadata *header ///< The FITS header
+                                          );
+\end{prototype}
+Given a FITS header, check it against all known cameras (unless we
+already know which camera, from pmConfigRead) and all known formats
+for those cameras in order to identify which is appropriate.  The
+first matching format is accepted; further matches produce warnings.
+The accepted camera is saved in the configuration.  The accepted
+format is returned.
+
+Return the camera configuration specified by name:
+\begin{prototype}
+psMetadata *pmConfigCameraByName(pmConfig *config, ///< The configuration
+                                 const char *cameraName ///< The camera name header
+                                );
+\end{prototype}
+Given a camera name, returns the camera configuration metadata.
+
+Make the supplied header conform to the nominated camera format:
+\begin{prototype}
+bool pmConfigConformHeader(psMetadata *header, ///< Header to conform
+                           const psMetadata *format ///< Camera format
+                          );
+\end{prototype}
+Given a FITS header, make it conform to the RULE in the specified
+camera format.  This is useful for switching between formats, or
+generating fake data that must be recognised by
+pmConfigCameraFormatFromHeader.
+
+
+\subsection{Recipes}
+
+Recipes are read from a variety of sources: the command line, the site
+configuration, and the camera configuration.  In addition, recipes
+specified on the command line may be a symbolic link to recipes
+defined in the other locations.  Since the various sources of recipes
+may be read at different times (e.g., the camera configuration may be
+read from the command line, or it may only be read once a FITS file
+has been opened for inspection), we specify the following list of
+recipe sources:
+\begin{datatype}
+typedef enum {
+    PM_RECIPE_SOURCE_NONE        = 0x00, ///< None yet
+    PM_RECIPE_SOURCE_SITE        = 0x01, ///< Site configuration
+    PM_RECIPE_SOURCE_CAMERA      = 0x02, ///< Camera configuration
+    PM_RECIPE_SOURCE_CL          = 0x04, ///< Command-line
+    PM_RECIPE_SOURCE_SYMBOLIC    = 0x14, ///< Symbolic link, specified on command-line
+    PM_RECIPE_SOURCE_ALL         = 0xff  ///< All sources
+} pmRecipeSource;
+\end{datatype}
+
+See PSDC-430-???, ``IPP Configurations'' for information on symbolic
+links, and how these are identified and resolved, and also for the
+precedence of sources.
+
+Read recipes:
+\begin{prototype}
+bool pmConfigReadRecipes(pmConfig *config ///< Configuration
+                        );
+\end{prototype}
+Attempt to read recipes from the sources that are available but have
+not already been read.Having read a recipe, attempt to resolve
+symbolic links that were specified on the command line.
+
+
+\subsection{Database}
+
+Setup the database:
+\begin{prototype}
+psDB *pmConfigDB(pmConfig *config       ///< Configuration
+                );
+\end{prototype}
+Initialise the database connection using the DBSERVER, DBNAME, DBUSER,
+DBPASSWORD values provided in the site configuration.  Stores the
+database handle in the configuration, and also returns it.
+
+
+\subsection{File sets}
+
+Read the command-line for files (or a text file containing a list of
+files):
+\begin{prototype}
+psArray *pmConfigFileSets(pmConfig *config, ///< Configuration, containing command-line arguments
+                          const char *file, ///< CL argument specifying a filename
+                          const char *list ///< CL argument specifying a text file with a list of filenames
+                         );
+\end{prototype}
+Given the 'file' and 'list' arguments (e.g., "-file" and "-list"),
+find the arguments associated with these words and interpret them as
+lists of files.  Return an array of the resulting filenames.
+
+Stuff associated files from the command-line into a metadata:
+\begin{prototype}
+bool pmConfigFileSetsMD(psMetadata *metadata, ///< Metadata into which to stuff the array
+                        pmConfig *config, ///< Configuration (which command-line arguments)
+                        const char *name, ///< Name for array in the metadata
+                        const char *file, ///< CL argument specifying a filename
+                        const char *list ///< CL argument specifying a text file with a list of filenames
+                       );
+\end{prototype}
+Calls pmConfigFileSets to parse the command line for filenames (or a
+list which provides filenames), and stuffs the array of filenames into
+the metadata under "name".
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{``Concepts''}
+\label{sec:concepts}
+
+Each image from an astronomical instrument has associated with it what
+we will call {\it concepts} (for want of a better word; \tbd{we would
+like to call this ``metadata'', but unfortunately that name is already
+taken}).  These are values corresponding to general quantities and
+qualities necessary to understand and interpret the data, such as
+airmass, date, read noise and filter.  The values of each of the below
+concepts shall be determined when the FPA is read into memory (via
+\code{pmFPARead}), and stored at the appropriate level in the focal
+plane hierarchy.
+
+After ingest, the user may safely assume that all of the above
+concepts exist at the appropriate level (meaning the user needn't be
+hampered by excessive error checking), is of the specified type
+(meaning the user doesn't need to worry about whether the value of
+interest is stored in, e.g., floating point or double precision or
+even a colon-delimited string) and in the specified format (meaning
+the user doesn't need to know, e.g., whether the right ascension is in
+radians or degrees) --- all the conversions are handled by the
+``concepts'' functions at ingest.
+
+Most of the structures and functions in this section are intended to
+be ``private'', since there is no need envisioned for the user to call
+them directly.
+
+\subsection{Specifying a concept}
+
+Specifying a ``concept'' requires a (meaningful) name (preferably with
+the level in the name, e.g., \code{CELL.EXPOSURE}), a
+comment/description, a type, a default or blank value, functions to
+read and write, and a level that the concept applies to
+(FPA/Chip/Cell).
+
+\begin{datatype}
+typedef struct {
+    psMetadataItem *blank;              ///< Blank value of concept; also contains the name and comment
+    pmConceptParseFunc parse;           ///< Function to call to read the concept
+    pmConceptFormatFunc format;         ///< Function to call to write the concept
+} pmConceptSpec;
+\end{datatype}
+
+The allocator is:
+\begin{prototype}
+pmConceptSpec *pmConceptSpecAlloc(psMetadataItem *blank, ///< Blank value; contains the name
+                                  pmConceptParseFunc parse, ///< Function to call to parse the concept
+                                  pmConceptFormatFunc format ///< Function to call to format the concept
+                                 );
+\end{prototype}
+
+\code{blank} provides the name, type and default/blank value for the
+concept.  \code{parse} and \code{format} provide the functions to
+parse and format the concepts:
+
+\begin{datatype}
+typedef psMetadataItem* (*pmConceptParseFunc)(const psMetadataItem *concept, ///< Concept to parse
+        const psMetadataItem *pattern, ///< Pattern for parsing
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                             );
+typedef psMetadataItem* (*pmConceptFormatFunc)(const psMetadataItem *concept, ///< Concept to format
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                              );
+\end{datatype}
+
+A concept that has just been read requires parsing, so that the final
+product stored in the \code{concepts} metadata is of the correct type
+and format.  The parsing function requires a \code{pattern} (generally
+the \code{blank}), which provides the name and comment for the parsed
+concept.  The camera format (\code{cameraFormat}) allows looking up of
+the \code{FORMATS} or other information, and the \code{fpa},
+\code{chip} and \code{cell} allow lookup of other concepts.
+
+Before writing, concepts are formatted so that the source's format
+(likely differing from that of the psModules concepts) may be
+respected.  Again, providing the camera format (\code{cameraFormat}),
+\code{fpa}, \code{chip} and \code{cell} allow the formatting function
+to access information necessary to format.
+
+
+\subsection{Registering a concept}
+
+The concept specifications that have been registered are stored in
+three (static) \code{psMetadata}s, one for each level (FPA, chip,
+cell).
+
+Registering a concept is achieved by:
+\begin{prototype}
+bool pmConceptRegister(psMetadataItem *blank, ///< Blank value; contains the name and default comment
+                       pmConceptParseFunc parse, ///< Function to call to parse the concept, or NULL
+                       pmConceptFormatFunc format, ///< Function to call to format the concept, or NULL
+                       pmFPALevel level ///< Level at which to store concept in the FPA hierarchy
+                      );
+\end{prototype}
+
+\code{pmConceptRegister} generates a concept specification from the
+provided \code{blank}, and \code{parse} and \code{format} functions,
+and register it in the metadata specified by the \code{level}.
+
+A \code{parse} function of \code{NULL} indicates that there is no
+special interpretation of the concept required, and that it can be
+used as read.  A \code{write} function of \code{NULL} indicates that
+no special formatting of the concept is required, and that it can be
+written as is.
+
+
+\subsection{Default concepts}
+
+Below is a list of concepts that are defined in the psModules, with
+the expected type and a short description.
+
+\begin{itemize}
+\item \code{FPA.CAMERA} (STR): Camera used
+\item \code{FPA.FOCUS} (F32): Telescope focus
+\item \code{FPA.AIRMASS} (F32): Airmass at boresight
+\item \code{FPA.FILTER} (STR): Filter used
+\item \code{FPA.POSANGLE} (F32): Position angle of instrument
+\item \code{FPA.RADECSYS} (STR): Celestial coordinate system
+\item \code{FPA.RA} (F64): Right Ascension of boresight
+\item \code{FPA.DEC} (F64): Declination of boresight
+\item \code{FPA.OBSTYPE} (STR): Type of observation
+\item \code{FPA.OBJECT} (STR): Object of observation
+\item \code{FPA.ALT} (F64): Altitude of telescope
+\item \code{FPA.AZ} (F64): Azimuth of telescope
+\item \code{FPA.TIMESYS} (STR): Time system
+\item \code{FPA.TIME} (psTime): Time of exposure
+\item \code{CHIP.XPARITY} (S32): Orientation in x compared to the rest of the FPA, -1 or +1
+\item \code{CHIP.YPARITY} (S32): Orientation in y compared to the rest of the FPA, -1 or +1
+\item \code{CHIP.X0} (S32): Position of (0,0) on the FPA
+\item \code{CHIP.Y0} (S32): Position of (0,0) on the FPA
+\item \code{CHIP.TEMP} (F32): Temperature of chip
+\item \code{CELL.GAIN} (F32): CCD gain (e/count)
+\item \code{CELL.READNOISE} (F32): CCD read noise (e)
+\item \code{CELL.SATURATION} (F32): Saturation level (counts)
+\item \code{CELL.BAD} (F32): Bad level (counts)
+\item \code{CELL.XPARITY} (S32): Orientation in x compared to the rest of the chip, -1 or +1
+\item \code{CELL.YPARITY} (S32): Orientation in y compared to the rest of the chip, -1 or +1
+\item \code{CELL.READDIR} (S32): Read direction, rows=1, cols=2
+\item \code{CELL.EXPOSURE} (F32): Exposure time (sec)
+\item \code{CELL.DARKTIME} (F32): Time since flush (sec)
+\item \code{CELL.TRIMSEC} (psRegion): Trim section
+\item \code{CELL.BIASSEC} (psList of psRegions): Bias sections
+\item \code{CELL.XBIN} (S32): Binning in x
+\item \code{CELL.YBIN} (S32): Binning in y
+\item \code{CELL.TIMESYS} (STR): Time system
+\item \code{CELL.TIME} (psTime): Time of exposure
+\item \code{CELL.X0} (S32): Position of (0,0) on the chip
+\item \code{CELL.Y0} (S32): Position of (0,0) on the chip
+\end{itemize}
+
+\tbd{CELL.EXPOSURE, CELL.DARKTIME and CELL.TIME should actually be
+specified at the readout level.  However, at this present time, we're
+not sure how these should be specified, and so we move them up to the
+cell level and assume that all readouts are of the same exposure and
+dark time.}
+
+The concept specifications for the above may be registered by
+\code{pmConceptsInit}:
+\begin{prototype}
+bool pmConceptsInit(void);
+\end{prototype}
+
+Since defined concept specifications are required before any concept
+ingest can take place, all functions that work with the concepts must
+call \code{pmConceptsInit} first.  This is usually performed internal
+to the pmConcepts, so the user need not call this function.
+
+The concept specification metadata containers and the concept
+specifications that have been registered are freed by:
+\begin{prototype}
+void pmConceptsDone(void);
+\end{prototype}
+Calling \code{pmConceptsDone} is required in order to avoid a memory
+leak, since the metadata containers are defined \code{static}.
+
+
+\subsection{Concept sources}
+
+Concepts may be read from, and written to a variety of sources: the
+\code{CELLS} information within the camera format, the FITS header
+(either the primary header, or extension), default values from the
+camera format, or the database.
+
+Since some sources become available at different times from others, we
+need to provide some specificity to reading and writing concepts (or
+we're forced to wait until everything's available, which we don't want
+to do).  Concepts may be read from or written to multiple sources at
+once by OR-ing them.
+
+\begin{datatype}
+typedef enum {
+    PM_CONCEPT_SOURCE_NONE     = 0x00,  ///< No concepts
+    PM_CONCEPT_SOURCE_CELLS    = 0x01,  ///< Concept comes from the camera information
+    PM_CONCEPT_SOURCE_DEFAULTS = 0x02,  ///< Concept comes from defaults
+    PM_CONCEPT_SOURCE_PHU      = 0x04,  ///< Concept comes from PHU
+    PM_CONCEPT_SOURCE_HEADER   = 0x08,  ///< Concept comes from FITS header
+    PM_CONCEPT_SOURCE_DATABASE = 0x10,  ///< Concept comes from database
+    PM_CONCEPT_SOURCE_ALL      = 0xff   ///< All concepts
+} pmConceptSource;
+\end{datatype}
+
+
+\subsection{Blanking}
+
+By ``blanking'', we mean setting the concepts to a default or blank
+value (e.g., \code{NaN} for floating point); this takes place before
+reading, and can be used to set up a focal plane hierarchy without
+reading from any particular source.  This is not normally done by the
+user, but it is performed on the focal plane hierarchy at construction
+(see \code{pmFPAConstruct}).
+
+The following functions blank the concepts at the appropriate level:
+
+\begin{prototype}
+bool pmConceptsBlankFPA(pmFPA *fpa);
+bool pmConceptsBlankChip(pmChip *chip);
+bool pmConceptsBlankCell(pmCell *cell);
+\end{prototype}
+
+
+\subsection{Reading}
+
+Reading a concept is the act of looking up the value, parsing it, and
+setting the parsed concept in the \code{concepts} metadata in the
+focal plane hierarchy at the appropriate level.  It is not normally
+necessary for the user to call these functions, since they are called
+when constructing or reading the FPA (see \code{pmFPAConstruct},
+\code{pmFPARead}).
+
+The following functions read the concepts at the appropriate level:
+
+\begin{prototype}
+bool pmConceptsReadFPA(pmFPA *fpa,      ///< FPA for which to read concepts
+                       pmConceptSource source, ///< Source for concepts
+                       bool propagateDown, ///< Propagate to lower levels?
+                       psDB *db         ///< Database handle
+                      );
+bool pmConceptsReadChip(pmChip *chip,   ///< Chip for which to read concepts
+                        pmConceptSource source, ///< Source for concepts
+                        bool propagateUp, ///< Propagate to higher levels?
+                        bool propagateDown, ///< Propagate to lower levels?
+                        psDB *db        ///< Database handle
+                       );
+bool pmConceptsReadCell(pmCell *cell,   ///< Cell for which to read concepts
+                        pmConceptSource source, ///< Source for concepts
+                        bool propagateUp, ///< Propagate to higher levels?
+                        psDB *db        ///< Database handle
+                       );
+
+\end{prototype}
+In each case, we require the hierarchy component for which to read the
+concepts, an indication of the source from which to read concepts
+(which source has recently become available), the database handle (for
+reading concepts from the database), and flags indicating whether we
+should propagate the read to higher or lower levels in the hierarchy.
+
+An additional function reads concepts for as many hierarchy components
+as are supplied:
+\begin{prototype}
+bool pmConceptsRead(pmFPA *fpa,         ///< FPA for which to read concepts
+                    pmChip *chip,       ///< Chip for which to read concepts, or NULL
+                    pmCell *cell,       ///< Cell for which to read concepts, or NULL
+                    pmConceptSource source, ///< The source of the concepts to read
+                    psDB *db            ///< Database handle
+                   );
+\end{prototype}
+
+
+\subsection{Writing}
+
+Writing a concept is the act of formatting the concept for the native
+format, and writing it to the appropriate place (here, ``writing''
+often means preparing for writing, by updating the header metadata, or
+checking the concept value against the default).  The user will not
+normally use these functions directly, since they are called when
+writing the FPA (see \code{pmFPAWrite}).
+
+\begin{prototype}
+bool pmConceptsWriteFPA(const pmFPA *fpa,     ///< FPA for which to write concepts
+                        pmConceptSource source, ///< Source for concepts
+                        bool propagateDown, ///< Propagate to lower levels?
+                        psDB *db        ///< Database handle
+                       );
+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?
+                         psDB *db       ///< Database handle
+                        );
+bool pmConceptsWriteCell(const pmCell *cell,  ///< FPA for which to write concepts
+                         pmConceptSource source, ///< Source for concepts
+                         bool propagateUp, ///< Propagate to higher levels?
+                         psDB *db       ///< Database handle
+                        );
+\end{prototype}
+In each case, we require the hierarchy component for which to read the
+concepts, an indication of the source to which to write concepts
+(which source requires updating), the database handle (for writing
+concepts to the database), and flags indicating whether we should
+propagate the write to higher or lower levels in the hierarchy.
+
+
+\subsection{Copying concepts}
+
+The values of concepts may be copied from one source to another:
+\begin{prototype}
+bool pmFPACopyConcepts(pmFPA *target, const pmFPA *source);
+\end{prototype}
+
+\code{pmFPACopyConcepts} iterates through the focal plane hierarchy,
+copying the values of the concepts from the \code{source} to the
+\code{target}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\input{CameraImages.tex}
+
+%\input{CameraGeometry.tex}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Image Detrending}
+
+Image Detrending is the image analysis process wherein the
+instrumental signatures are removed from the individual images.  This
+section discusses the modules used for image detrending.  The basic
+image detrending steps are:
+\begin{itemize}
+\item Subtract overscan, bias and dark;
+\item Correct for non-linearity;
+\item Shutter correction;
+\item Flat-field;
+\item Mask bad pixels;
+\item Fringe subtraction;
+\item Subtract the background;
+\item Mask cosmic rays;
+\item Mask optical defects;
+\end{itemize}
+
+We also include here some of the (more complicated) functions for
+generating the image detrend calibrations (fringes, flat-field
+normalisation), and determining the suitable calibrations from the
+detrend database.
+
+\subsection{Bias subtraction}
+\label{sec:bias}
+
+The bias subtraction module provides a facility to correct detector
+images for the electronic pedestal introduced by the readout
+electronics.  The pedestal is measured from one or more of the
+overscan, bias frame and dark frames.
+
+The following function subtracts the overscan, bias and/or dark from
+the input readout.  The overscan is taken from the \code{bias} member
+of the input \code{pmReadout}, and can be turned off by supplying a
+\code{NULL} value for \code{overscanOpts}.  A bias image is subtracted
+if \code{bias} is non-\code{NULL}, and a dark frame is scaled (by
+\code{CELL.DARKTIME}) and subtracted if \code{dark} is
+non-\code{NULL}.
+\begin{prototype}
+bool pmSubtractBias(pmReadout *in,      ///< Input readout, to be overscan/bias/dark corrected
+                    pmOverscanOptions *overscanOpts, ///< Options for overscan subtraction, or NULL
+                    const pmReadout *bias, ///< Bias image to subtract, or NULL
+                    const pmReadout *dark ///< Dark image to scale and subtract, or NULL
+                   );
+\end{prototype}
+
+
+\subsubsection{Overscan subtraction}
+\label{sec:overscan}
+
+The options for performing the overscan subtraction are bundled in a
+\code{pmOverscanOptions}:
+
+\begin{datatype}
+typedef struct {
+    // Inputs
+    bool single;                ///< Reduce all overscan regions to a single value?
+    pmFit fitType;              ///< Type of fit to overscan
+    unsigned int order;         ///< Order of polynomial, or number of spline pieces
+    psStats *stat;              ///< Statistic to use when reducing the minor direction
+    // Outputs
+    psPolynomial1D *poly;       ///< Result of polynomial fit
+    psSpline1D *spline;         ///< Result of spline fit
+} pmOverscanOptions;
+\end{datatype}
+
+The overscan subtraction may be performed by reducing all overscan
+regions to a single value (e.g., if there is no structure); or the
+overscan may be fit perpendicular to the read direction (usually the
+columns) with a particular functional form; or a single value may be
+subtracted for each read/scan without fitting (if the structure defies
+characterisation).  In any case, statistics are required to reduce
+multiple values to a single value (either for the scan, or for the
+entire overscan regions).
+
+The \code{fitType} is an enumerated type which specifies the type of
+fit to employed on the overscan vector:
+\begin{datatype}
+typedef enum {
+    PM_FIT_NONE,                        ///< No fit
+    PM_FIT_POLY_ORD,                    ///< Fit ordinary polynomial
+    PM_FIT_POLY_CHEBY,                  ///< Fit Chebyshev polynomial
+    PM_FIT_SPLINE                       ///< Fit cubic splines
+} pmFit;
+\end{datatype}
+
+If \code{fitType} is \code{PM_FIT_NONE}, then the overscan vector is
+subtracted from the image without fitting.  Otherwise, the overscan
+vector is fit using the specified functional form, the fit is
+subtracted from the image, and the \code{poly} or \code{spline} is
+allocated and updated with the results of the fit.
+
+The allocator for a \code{pmOverscanOptions} is:
+\begin{prototype}
+pmOverscanOptions *pmOverscanOptionsAlloc(bool single, ///< Reduce all overscan regions to a single value?
+        pmFit fitType, ///< Type of fit to overscan
+        unsigned int order, ///< Order of polynomial, or number of splines
+        psStats *stat ///< Statistic to use
+                                         );
+\end{prototype}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Non-linearity}
+
+We here specify two functions to perform the non-linearity correction,
+since either (or both) might be used to specify the correction.
+
+The first, \code{pmNonLinearityPolynomial}, applies a polynomial to
+the flux of each pixel in the input image to determine the corrected
+flux.  The second function, \code{pmNonLinearityLookup} performs
+linear interpolation on the table (specified by two vectors) to
+determine the corrected flux.
+
+\begin{prototype}
+pmReadout *pmNonLinearityPolynomial(pmReadout *in, ///< Input image, to correct
+                                    const psPolynomial1D *coeff ///< Polynomial for non-linearity correction
+                                   );
+pmReadout *pmNonLinearityLookup(pmReadout *in, ///< Input image, to correct
+                                const psVector *inFlux, ///< Table column with input fluxes
+                                const psVector *outFlux ///< Table column with output fluxes
+                               );
+\end{prototype}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Shutter correction}
+
+A mechanical shutter may not yield uniform exposure times as a
+function of position on the detector.  The typical error consists of a
+constant exposure-time offset relative to the requested value,
+i.e. the exposure time is $T_o + \delta T(x,y)$.  The exposure error,
+$\delta T$, may be measured with the following scheme.  Obtain a set
+of exposures with different exposures times taken of the same
+flat-field source; the source must be spatially stable between the
+exposures, but need not have a stable amplitude.  For an illuminating
+flux of intensity $F(x,y) = F_o f(x,y)$, the signal recorded by any
+pixel in the detector is given by: $S(t,x,y) = F_o(t) f(x,y) (T_o +
+\delta T(x,y))$ where $F_o$ is the $F_o(t)$ is the (variable) overall
+intensity of the illuminating source and $f(x,y)$ is the spatial
+illumination patter times the flat-field response.  Choose a reference
+location in the image (e.g., the detector center) and divide by the
+value of that region (i.e., mean or median):
+
+\begin{eqnarray}
+s(t,x,y) & = & S(t,x,y) / S(t,0,0) \\
+s(t,x,y) & = & F_o(t) f(x,y) (T_o + \delta T(x,y)) / F_o(t) f(0,0) (T_o + \delta T(0,0)) \\
+s(t,x,y) & = & f(x,y) (T_o + \delta T(x,y)) / f(0,0) (T_o + \delta T(0,0)) \\
+\end{eqnarray}
+
+We can absorb the term $f(0,0)$ into $f(x,y)$, as we have no
+motivation for the scale of $f(x,y)$.  For any single pixel, over the
+set of exposures, we thus need to solve for $\delta T(x,y)$, $\delta
+T(0,0)$, and $f'(x,y)$ in the equation:
+
+\begin{equation}
+s(t,x,y) = f'(x,y) (T_o + dT(x,y)) / (T_o + \delta T(0,0))
+\end{equation}
+
+We avoid directly fitting these values, as the process would be a
+non-linear least-squares problem for every pixel in the image, and
+thus very time consuming.  There are linear options which may be used
+instead.  First, as $T_o$ goes to a large value, $s$ approaches the
+value of $f'(x,y)$.  Next, as $T_o$ goes to a very small value, $s$
+approaches the value of $f'(x,y) \delta T(x,y)/ \delta T(0,0)$.
+Finally, when s has the value of $f'(x,y)*(1 + \delta T(x,y)/ \delta
+T(0,0))/2$, then $T_o$ has the value of $\delta T(0,0)$.  With data
+points covering a reasonable dynamic range, we can solve for these
+three values by interpolation and/or extrapolation.
+
+To take the strategy one step further, we could use the above recipe
+to obtain a guess for the three parameters and then apply non-linear
+fitting to solve more accurately for the parameters.  If we limit this
+operation to a handful of positions in the image (user defined, but
+the obvious choice would be positions near the center, edges, and
+corners), then we may determine a good value for $\delta T(0,0)$.
+Since there is only one $\delta T(0,0)$ for the image, we can apply
+the resulting measurement to the rest of the pixels in the image.  If
+$\delta T(0,0)$ is not a free parameter, then the fitting process is
+linear in terms of $\delta T(x,y)$ and $f'(x,y)$.
+
+\subsubsection{Components to creating a shutter correction}
+
+We define a shutter correction, applicable for a single pixel (or region):
+\begin{datatype}
+typedef struct {
+    double scale;                       ///< The normalisation for an exposure, A(k)
+    double offset;                      ///< The time offset, dTk
+    double offref;                      ///< The reference time offset, dTo
+} pmShutterCorrection;
+\end{datatype}
+
+The allocator is:
+\begin{prototype}
+pmShutterCorrection *pmShutterCorrectionAlloc();
+\end{prototype}
+
+
+The following function provides a guess for the shutter correction,
+based on plot of counts vs exposure time.  It should be used before
+doing the full non-linear fit, to get parameters close to the true.
+The function assumes the \code{exptime} vector is sorted (ascending
+order; longest is last) prior to input.
+\begin{prototype}
+pmShutterCorrection *pmShutterCorrectionGuess(const psVector *exptime, ///< Exposure times for each exposure
+        const psVector *counts ///< Counts for each exposure
+                                             );
+\end{prototype}
+
+A full non-linear fit may then be performed to the counts as a
+function of exposure time.  The main purpose is to solve for the
+reference time offset, so that future fits may be performed using
+linear fitting with the reference time offset fixed.
+\begin{prototype}
+pmShutterCorrection *pmShutterCorrectionFullFit(const psVector *exptime, ///< Exposure times for each exposure
+        const psVector *counts, ///< Counts for each exposure
+        const psVector *cntError, ///< Error in the counts
+        const pmShutterCorrection *guess ///< Initial guess
+                                               );
+\end{prototype}
+
+With the reference time offset fixed to a known value, a (much faster)
+linear fit can be performed at each pixel to generate the shutter
+correction.  This function performs the fit with iterative clipping,
+if \code{nIter} $> 1$.
+\begin{prototype}
+pmShutterCorrection *pmShutterCorrectionLinFit(const psVector *exptime, ///< Exposure times for each exposure
+        const psVector *counts, ///< Counts for each exposure
+        const psVector *cntError, ///< Error in the counts
+        const psVector *mask, ///< Mask for each exposure
+        float offref, ///< Reference time offset
+        int nIter, ///< Number of iterations
+        float rej, ///< Rejection threshold (sigma)
+        psMaskType maskVal ///< Mask value
+                                              );
+\end{prototype}
+
+\subsubsection{Creating a shutter correction}
+
+The above steps are all performed using the following function.  Given
+an array of images with known exposure times, this function measures
+the shutter correction (our principal concern is for the time offset,
+rather than the normalisation) by measuring the reference time offset
+using the full non-linear fit for a small number of representative
+regions (middle and corners), and then using that to perform a linear
+fit to each pixel.
+\begin{prototype}
+psImage *pmShutterCorrectionMeasure(const psVector *exptimes, ///< Exposure times
+                                    const psArray *images, ///< Input images
+                                    const psArray *weights, ///< Weight images
+                                    const psArray *masks, ///< Mask images
+                                    unsigned int size, ///< Size of samples for statistics for non-linear fit
+                                    psStatsOptions meanStat, ///< Statistic to use for mean
+                                    psStatsOptions stdevStat, ///< Statistic to use for stdev
+                                    int nIter, ///< Number of iterations
+                                    float rej, ///< Rejection threshold (sigma)
+                                    psMaskType maskVal ///< Mask value
+                                   );
+\end{prototype}
+
+\subsubsection{Applying a shutter correction}
+
+Given a shutter correction (a measurement of $\delta T$ for each
+pixel), the following function applies this correction to an input
+image.
+\begin{prototype}
+bool pmShutterCorrectionApply(pmReadout *readout, ///< Readout to which to apply shutter correction
+                              const pmReadout *shutter ///< Shutter correction readout, with dT for each pixel
+                             );
+\end{prototype}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Flat-fielding}
+
+
+The following function applies the flat-field calibration to the input
+readout.  The relative offsets between the input and flat images is
+determined from the readout row0,col0 and the CELL.X0 and CELL.Y0
+concepts.  Normalisation of the flat is left as the responsibility of
+the caller.  Non-positive pixels in the flat are masked, if there is a
+mask present in the input readout.
+\begin{prototype}
+bool pmFlatField(pmReadout *in,         ///< Readout with input image
+                 const pmReadout *flat  ///< Readout with flat image
+                );
+\end{prototype}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Masking}
+
+The following function applies a mask readout to an input (science) readout.
+
+Pixels marked as bad (according to the supplied mask value) within the
+mask are marked as bad within the input image's mask.  If
+\code{maskVal} is non-zero, all pixels in the mask have any of the
+same bits sets as \code{maskVal} shall have the corresponding bits
+raised.  If \code{maskVal} is zero, any zero pixels in the mask are
+OR-ed with \code{PM_MASK_BAD}.  Position offsets (such as due to
+trimming) between the input and mask are applied so that the same
+pixels are referred to.  The science readout must already have a
+supplied mask element (use e.g. \code{pmReadoutSetMask}).
+\begin{prototype}
+bool pmMaskBadPixels(pmReadout *input,  ///< Input science image
+                     const pmReadout *mask, ///< Mask image to apply
+                     psMaskType maskVal ///< Mask value to apply
+                    );
+\end{prototype}
+
+\tbd{Masks for the charge traps need to be grown by the extent of the
+OT convolution kernel.  For other pixel types, orthogonal transfer of
+the flux in this pixel will not (necessarily) affect the flux in
+neighbouring pixels.}
+
+
+\subsection{Fringes}
+
+Some images contain a signal caused by thin-film interference in the
+device due to strong emission lines.  The resulting instrumental
+effect consists of a pattern (the ``fringe pattern'') of bright and
+dark bands corresponding to the constructive and destructive
+interference of the emission lines.  In the case that a single
+emission line causes the line structure, the resulting pattern can be
+described by two independent parameters: First, the amplitude of the
+emission line determines the overall amplitude of the pattern.
+Second, the three-dimensional surface structure of the device
+determines the shape of the pattern.  In a typical situation, the
+device is illuminated by multiple emission lines, as well as a
+continuum spectral source, which contributes to the overall light
+detected by the device without following the fringe pattern.  The
+relative intensities of the continuum background and the fringe
+pattern depend on the device structure (thickness) and on the ratio of
+the continuum and line emission fluxes. 
+
+A simple approach to the fringe pattern is to subtract a master fringe
+frame scaled by the amplitude of the fringe pattern.  The amplitude of
+the fringe pattern is used both in the process of constructing the
+master image and in scaling the master image when it is applied to
+science image.  This is the method currently in use at CFHT and it
+usually performs well.  However, the fringe signal can vary as the
+emission lines in the atmosphere change, and the above method breaks
+down unless different fringe images corresponding to different
+atmospheric conditions are constructed.  
+
+An alternative technique is to use multiple master fringe images at
+the same time, each representing different atmospheric conditions.
+The observed fringe frame can be considered as a linear combination of
+different fringe patterns, depending on the relative strengths of the
+lines active in creating each of the fringe masters.  It is not
+critical that the fringe master images represent completely orthogonal
+fringe patterns, they need only sample sufficiently different
+conditions to provide a handle on the underlying fringe signals.  
+
+We define a method of measuring the fringe pattern which is robust in
+the presence of stars and which is fast.  We implement a varient on
+the method used at CFHT in which the fringe pattern is mapped by a
+series of points distributed across the image.  At CFHT, manual effort
+is used to carefully define point pairs which correspond to peaks and
+valleys of the fringe pattern.  We implement a different approach in
+which the fringe points are randomly chosen across the image.  At each
+point in the image, the median flux is measured in a box of specified
+size.  A low-frequency spatial filter is then applied to these
+measurements.  The resulting array of points and fluxes then
+represents the strength of the fringe pattern on that image.  The
+comparison between any two fringe images is then just a linear fit
+between these fringe statistics vectors, as follows:
+\[
+S_i = C_0 + C_1 F_i
+\]
+where $S_i$ is the fringe statistic on the science image and $F_i$ is
+the fringe statistic on the reference fringe image.  Extending this
+logic to any number of reference fringe images results in the
+following relationship:
+\[
+S_i = C_0 + \sum_j C_j F_j
+\]
+
+In order to correct a single science image, the collection of fringe
+statistics ($S_i$) are used to measure the coefficients $C_0$, $C_j$.
+The linear combination of the reference fringe images is then used to
+build a master image which is subtracted from the science image.  The
+following structures and functions implement the above concepts.
+
+
+\subsubsection{Fringe regions}
+
+Fringes are measured within a box of size \code{dX,dY}.  A large scale
+smoothing is performed by subtracting the background within large
+divisions of the image.  The coordinates of the fringe points and the
+mask are originally set to \code{NULL}, which means that they will be
+generated when required.
+
+\begin{datatype}
+typedef struct {
+    psU32 nRequested;                   // Number of fringe points selected
+    psU32 nAccepted;                    // Number of fringe points not masked
+    psU32 dX;                           // Median box half-width
+    psU32 dY;                           // Median box half-height
+    psU32 nX;                           // Number of large-scale smoothing divisions in x (col)
+    psU32 nY;                           // Number of large-scale smoothing divisions in y (row)
+    psVector *x;                        // Fringe point coordinates (col), or NULL
+    psVector *y;                        // Fringe point coordinates (row), or NULL
+    psVector *mask;                     // Fringe point on/off mask, or NULL
+} pmFringeRegions;
+\end{datatype}
+
+The allocator is:
+\begin{prototype}
+pmFringeRegions *pmFringeRegionsAlloc (int nPts, ///< Number of fringe points to create
+                                       int dX, ///< Half-width of fringe boxes
+                                       int dY, ///< Half-height of fringe boxes
+                                       int nX, ///< Smoothing scale in x
+                                       int nY ///< Smoothing scale in y
+                                      );
+\end{prototype}
+
+The following function generates fringe points randomly over an image.
+No effort is made to avoid masked regions (indeed, the function knows
+nothing about masks).  If the random number generator is \code{NULL},
+then a new one will be used.
+\begin{prototype}
+bool pmFringeRegionsCreatePoints(pmFringeRegions *fringe, ///< Fringe regions to generate
+                                 const psImage *image, ///< Image for the regions (defines the size)
+                                 psRandom *random ///< Random number generator, or NULL
+                                );
+\end{prototype}
+
+Fringe regions may be written to and read from FITS files with the following functions.
+
+\code{pmFringeRegionsWriteFits} writes fringe regions to a FITS file,
+with the given extension name.  The header is supplemented with scalar
+values \code{dX}, \code{dY}, \code{nX} and \code{nY} (as
+\code{PSFRNGDX}, \code{PSFRNGDY}, \code{PSFRNGNX}, \code{PSFRNGNY})
+from the fringe regions, while the fringe coordinates and mask are
+written as a FITS table (as \code{x}, \code{y}, \code{mask}).
+
+\code{pmFringeRegionsReadFits} reads fringe regions from a FITS file,
+at the given extension name.  The scalars are retrieved from the
+header, while the table provides the fringe coordinates and mask.
+
+\begin{prototype}
+bool pmFringeRegionsWriteFits(psFits *fits, ///< Output FITS file
+                              psMetadata *header, ///< Additional headers to write, or NULL
+                              const pmFringeRegions *regions, ///< Regions to write
+                              const char *extname ///< Extension name, or NULL
+                             );
+pmFringeRegions *pmFringeRegionsReadFits(psMetadata *header, ///< Header to read, or NULL
+        const psFits *fits, ///< Input FITS file
+        const char *extname ///< Extension name, or NULL
+                                        );
+\end{prototype}
+
+\subsubsection{Fringe Statistics}
+
+Fringe measurements for a particular image are contained in the
+following structure, which contains the median and standard deviation
+for each of the fringe regions.
+\begin{datatype}
+typedef struct {
+    pmFringeRegions *regions;           ///< Fringe regions
+    psVector *f;                        ///< Fringe point median
+    psVector *df;                       ///< Fringe point stdev
+} pmFringeStats;
+\end{datatype}
+
+The allocator is:
+\begin{prototype}
+pmFringeStats *pmFringeStatsAlloc(pmFringeRegions *regions // The fringe regions which will be measured
+                                 );
+\end{prototype}
+
+The following function measures the fringe statistics (median and
+standard deviation) at each of the fringe regions for an image
+(supplied within a readout).  If the fringe regions are undefined,
+they are generated.
+\begin{prototype}
+pmFringeStats *pmFringeStatsMeasure(pmFringeRegions *fringe, ///< Fringe regions at which to measure
+                                    const pmReadout *readout, ///< Readout for which to measure
+                                    psMaskType maskVal ///< Mask value for image
+                                   );
+\end{prototype}
+
+Like the fringe regions, the fringe statistics may be written to and
+read from a FITS file, allowing rapid re-generation of the fringe
+measurements for the calibration files.
+
+\code{pmFringeStatsWriteFits} writes the fringe statistics for an
+image to a FITS table, with the given extension name.  The median and
+standard deviation measurements are written as a FITS table (as
+\code{f} and \code{df}).
+
+\code{pmFringeStatsReadFits} reads the fringe stats from a FITS table,
+at the given extension name.  It is assumed that the fringe
+measurements correspond to the regions provided.
+
+\begin{prototype}
+bool pmFringeStatsWriteFits(psFits *fits, ///< FITS file to which to write
+                            psMetadata *header, ///< Additional headers to write, or NULL
+                            const pmFringeStats *fringe, ///< Fringe statistics to be written
+                            const char *extname ///< Extension name for table
+                           );
+pmFringeStats *pmFringeStatsReadFits(psMetadata *header, ///< Header to read, or NULL
+                                     const psFits *fits, ///< FITS file from which to read
+                                     const char *extname, ///< Extension name to read
+                                     pmFringeRegions *regions ///< Corresponding regions
+                                    );
+\end{prototype}
+
+Since each readout of each chip must be measured separately (so as to
+avoid any gaps between the cells, as in the case for GPC), while the
+fit must be performed with all the readouts belonging to a chip (in
+order to get a secure measurement of the fringe amplitudes), we
+provide the following function, which concatenates the fringe stats
+for several readouts into a single fringe stats.  The function
+generates a new \code{pmFringeStats} from concatenating those in the
+array.  The corresponding \code{pmFringeRegions} is also generated.
+\begin{prototype}
+pmFringeStats *pmFringeStatsConcatenate(const psArray *fringes, ///< Array of pmFringeStats for the readouts
+                                        const psVector *x0, ///< Offset in x for the readout
+                                        const psVector *y0 ///< Offset in y for the readout
+                                       );
+\end{prototype}
+
+
+\subsubsection{Fringe scales}
+
+The following structure contains the fringe solution --- the
+coefficients of the linear system of library fringe images that best
+fit the science image.
+
+\begin{datatype}
+typedef struct {
+    int nFringeFrames;                  ///< Number of fringe frames
+    psVector *coeff;                    ///< Fringe coefficients; size = nFringeFrames
+    psVector *coeffErr;                 ///< Error in fringe coefficients; size = nFringeFrames
+} pmFringeScale;
+\end{datatype}
+
+The corresponding allocator is:
+\begin{prototype}
+pmFringeScale *pmFringeScaleAlloc(int nFringeFrames ///< Number of fringe frames
+                                 );
+\end{prototype}
+
+The following function measures the fringe scales for the library
+fringe images, solving the linear system that best fits the fringe
+measurements made on the science image.  Given a fringe measurement
+for a science image, and an array of template fringe measurements, the
+function measures the contribution of each of the templates to the
+input.  Rejection is performed on the fringe regions, to weed out
+stars etc.
+\begin{prototype}
+pmFringeScale *pmFringeScaleMeasure(pmFringeStats *science, ///< Fringe measurements from science image
+                                    psArray *fringes, ///< Array of fringe measurements from templates
+                                    float rej, ///< Rejection threshold (in standard deviations)
+                                    unsigned int nIter, ///< Maximum number of iterations
+                                    float keepFrac ///< Minimum fraction of regions to keep
+                                   );
+\end{prototype}
+
+
+\subsubsection{Fringe correction}
+
+The following function solves for and applies the fringe correction.
+It is a wrapper around each of the fringe correction components to
+measure the fringe points, solve for the fringe correction, and apply
+the fringe correction.  The input fringe images are modified (scaled
+by the solution coefficients in order to correct the science image).
+The function returns the summed fringe image, and corrects the input
+science image.
+
+\begin{prototype}
+psImage *pmFringeCorrect(pmReadout *in, ///< Input science image
+                         pmFringeRegions *fringes, ///< The fringe regions used
+                         psArray *fringeImages, ///< Fringe template images to use in correction
+                         psArray *fringeStats, ///< Fringe stats (for templates) to use in correction
+                         psMaskType maskVal, ///< Value to mask for science image
+                         float rej,     ///< Rejection threshold, for pmFringeScaleMeasure
+                         unsigned int nIter, ///< Maximum number of iterations, for pmFringeScaleMeasure
+                         float keepFrac ///< Minimum fraction of regions to keep, for pmFringeScaleMeasure
+                        );
+\end{prototype}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Subtract sky}
+
+\tbd{This has been deferred.}
+
+Given an input image, a polynomial or spline specifying the order of a
+desired fit, a binning factor and statistics to use for the binning,
+along with a clipping level, \code{pmSubtractSky} shall fit and
+subtract a model for the background of the image.  The API shall be
+the following:
+\begin{prototype}
+pmReadout *pmSubtractSky(pmReadout *in, psPolynomial2D *poly, psImage *mask, psU8 maskVal, 
+                         int binFactor, psStats *stats, float clipSD);
+\end{prototype}
+
+Note that the input image, \code{in}, shall be subtracted in-place.
+The function shall return the subtracted image, and also update the
+polynomial, Chebyshev or spline specified by \code{fitSpec}, to hold
+the coefficients used in the subtraction.
+
+The polynomial, \code{poly}, specifies the order of the polynomial,
+and on return shall contain the coefficients of the fit.  If
+\code{poly} is \code{NULL}, then no fit shall be performed, and the
+function shall generate a warning and return.
+
+When fitting the polynomial, the function shall first bin the input
+image by \code{binFactor} in order to reduce the required processing
+time.  In the binning, pixels in the \code{mask} (if non-\code{NULL})
+which satisfy the \code{maskVal} shall be excluded.  The statistic to
+use in this binning is specified by \code{stat}.  \code{stat} is of
+type \code{psStats} instead of simply \code{psStatsOptions} so that
+clipping levels may be specified, if desired.  In the event that
+multiple options are specified by \code{stats}, a warning shall be
+generated, and the option with the highest priority shall be used,
+according to the following priority order: \code{PS_STAT_SAMPLE_MEAN},
+\code{PS_STAT_SAMPLE_MEDIAN}, \code{PS_STAT_CLIPPED_MEAN},
+\code{PS_STAT_ROBUST_MEAN}, \code{PS_STAT_ROBUST_MEDIAN},
+\code{PS_STAT_ROBUST_MODE}.  If the \code{binFactor} is non-positive,
+or \code{stats} is \code{NULL} or fails to specify an option, a
+warning shall be generated, and the fit shall be performed on the
+entire image.
+
+Binned pixels deviating more than \code{clipSD} standard deviations
+from the mean of the binned pixels shall be clipped in a single
+clipping iteration before polynomial fitting.  These pixels may be
+interpolated over, or may be simply ignored in the fitting, according
+to the choice of algorithm.  If the \code{clipSD} is non-positive,
+then the function shall generate a warning and not perform any
+clipping.
+
+The \code{mask} shall be of type U8, and the input image,
+\code{in}, must be of type F32.
+
+This operation acts only on the region of the readout specified by
+\code{CELL.TRIMSEC}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Paper Trail}
+
+\tbd{This is not implemented yet.}
+
+The elements of the focal plane hierarchy each contain an
+\code{analysis} member, intended to log the results of the detrend
+tasks.  The detrend tasks shall add to the \code{analysis} members as
+follows:
+
+\begin{itemize}
+\item \code{pmMaskBadPixels}:
+  \begin{itemize}
+  \item \code{MASK.DONE} (STR): The time at which masking was
+    completed.
+  \item \code{MASK.SAT} (S32): The number of saturated pixels masked
+    in the image
+  \item \code{MASK.SAT.GROW} (S32): The number of additional pixels
+    masked by growing the saturated pixels.
+  \item \code{MASK.BAD} (S32): The number of pixels masked in the
+    image
+  \item \code{MASK.BAD.GROW} (S32): The number of additional pixels
+    masked by growing the specified bad pixels.
+  \end{itemize}
+\item \code{pmNonLinearityPolynomial} and \code{pmNonLinearityLookup}:
+  \begin{itemize}
+  \item \code{NONLIN.DONE} (STR): The time at which the non-linearity
+    correction was completed.
+  \item \code{NONLIN.POLY} (STR): The polynomial coefficients used (if
+    applicable).
+  \item \code{NONLIN.LOOKUP} (STR): The filename for the lookup table
+    (if applicable).
+  \end{itemize}
+\item \code{pmSubtractBias}:
+  \begin{itemize}
+  \item \code{BIAS.DONE} (STR): The time at which the bias-subtraction
+    was completed.
+  \item \code{BIAS.OVERSCAN.AXIS} (STR): Overscan axis used.
+  \item \code{BIAS.OVERSCAN.FIT.TYPE} (STR): Fit type applied to
+    overscan.
+  \item \code{BIAS.OVERSCAN.FIT.COEFF} (STR): Coefficients of overscan
+    fit.
+  \item \code{BIAS.OVERSCAN.REGION} (STR): Overscan regions (from
+    \code{x0,y0,numCols,numRows}).
+  \item \code{BIAS.OVERSCAN.BIN} (S32): Number of pixels per bin used
+    in overscan.
+  \item \code{BIAS.OVERSCAN.MEAN} (F32): The mean of the binned
+    overscan pixels after subtracting the fit.
+  \item \code{BIAS.OVERSCAN.SD} (F32): The standard deviation of the
+    binned overscan pixels after subtracting the fit.
+  \end{itemize}
+\item \code{pmFlatField}:
+  \begin{itemize}
+  \item \code{FLAT.DONE} (STR): The time at which the flat-fielding
+    was completed.
+  \item \code{FLAT.BAD} (S32): Number of non-positive flat-field
+    pixels.
+  \end{itemize}
+\end{itemize}
+
+To be added by higher-levels:
+\begin{itemize}
+\item \code{BIAS.NAME} (STR): Name of bias image
+\item \code{DARK.NAME} (STR): Name of dark image
+\item \code{FLAT.NAME} (STR): Name of flat image
+\item \code{MASK.NAME} (STR): Name of mask image
+\end{itemize}
+
+\subsection{Detrend Lookups}
+
+When it comes time to perform a detrend operation on an image, it is
+necessary to determine {\em which} detrend image should be used.  The
+Pan-STARRS Image Processing Pipeline uses the concept of a detrend
+image database table, or set of tables (part of the Metadata
+Database), to store the known master detrend images.  These tables can
+be accessed though the basic query functions specified for the master
+detrend database.  To simplify the interaction for the case of the
+detrend images, the following function allows the user to explicitly
+search the detrend database table or tables for detrend images which
+satisfy a set of characteristics.
+
+\begin{prototype}
+psArray *pmDetrendLookup (psMetadata *constraints, psMetadata *tableDefs);
+\end{prototype}
+This function accepts a metadata structure which restricts the
+selected detrend images.  This metadata structure may contain any of
+the following entries:
+\begin{verbatim}
+TYPE        type of detrend data (eg, flat, bias) 
+CAMERA      name of desired camera (eg, GPC, MEGACAM)
+CHIP        chip identifier (eg., ccd00)
+FILTERNAME  name of specific filter hardware (eg, r.GPC01)
+FILTERTYPE  conceptual name of filter (eg., r)
+TIME_MIN    lower bound on valid time range 
+TIME_MAX    upper bound on valid time range 
+LABEL       match the entry label
+RECIPE      recipe used to build detrend image
+EXPTIME     exposure time 
+AIRMASS     airmass 
+\end{verbatim}
+Any detrend images which match the provided constraints are returned
+as an array of \code{psMetadata} elements corresponding to the columns
+of the detrend database table.  The additional input parameter
+specifies additional information to define the detrend database
+tables.  This may include the access information (IP, Username,
+Password), as well as names for the table and the columns which
+correspond to the constraint names.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Detrend Creation}
+
+In the detrend creation process, a collection of raw images are
+combined to produce a clean, high-quality master image for correcting
+the effect of interest.  The input images may potentially be processed
+and scaled in some way.  The resulting output images may be to be
+re-scaled to have a consistent signal for all chips in the mosaic.
+The simplest example is the construction of a bias image, in the case
+where there is signficant 2-D bias structure.  In this case, the input
+raw bias images are probably combined without any additional
+processing.  In another example, flat-field image must be
+bias-corrected and scaled to a consistent normalization before being
+combined, and the flat-field images from the different chips must be
+normalized so that each chip will be flattened consistently across the
+mosaic.  A complex example is the fringe pattern, in which the input
+images must be bias-corrected and flattened, and the resulting images
+must be scaled by the amplitude of the fringe pattern on each image,
+rather than by the average flux level.  In this section, we define the
+tools necessary to perform the detrend creation process.
+
+\subsection{Image Stacking}
+
+A basic operation in generating the master detrend images is using a
+stack of many input images of a particular type and combining them,
+with perhaps some additional scaling, in order to build up
+signal-to-noise and to reject deviant pixel.  For this, we require a
+general purpose image combination module.  We forsee this module as
+only acting upon data from the same detector, and so each input image
+will have the same noise characteristics.
+
+The following structure specifies how the combination of a stack of
+pixels is performed.  The particular statistic specified by
+\code{combine} is used to combine each stack of pixels from the input
+images.  If the \code{combine} specifies clipped fitting, the
+\code{iter} and \code{rej} members provide the number of iterations
+and the rejection threshold for clipping.
+
+The \code{maskVal} specifies mask values for pixels to be
+masked out of the combination.
+
+After masking, but before performing the combination, the highest
+\code{fracHigh} fraction and lowest \code{fracLow} fraction of pixels
+in the stack are immediately rejected, unless this would leave less
+than \code{nKeep} pixels in the stack, in which case no immediate
+rejection is performed.
+
+\begin{datatype}
+typedef struct {
+    psStatsOptions combine;             ///< Statistic to use when performing the combination
+    psMaskType maskVal;                 ///< Mask value
+    int nKeep;                          ///< Mimimum number of pixels to keep
+    float fracHigh;                     ///< Fraction of high pixels to immediately throw
+    float fracLow;                      ///< Fraction of low pixels to immediately throw
+    int iter;                           ///< Number of iterations for clipping (for CLIPPED_MEAN only)
+    float rej;                          ///< Rejection threshould for clipping (for CLIPPED_MEAN only)
+} pmCombineParams;
+\end{datatype}
+
+The allocator is:
+\begin{prototype}
+pmCombineParams *pmCombineParamsAlloc(psStatsOptions statsOptions ///< Statistic to use for combination
+                                     );
+\end{prototype}
+
+The combination is performed by the following function:
+\begin{prototype}
+bool pmReadoutCombine(pmReadout *output,///< Output readout; altered and returned
+                      const psArray *inputs,  ///< Array of input readouts (F32 image and weight, U8 mask)
+                      const psVector *zero, ///< Zero corrections to subtract from input, or NULL
+                      const psVector *scale, ///< Scale corrections to divide into input, or NULL
+                      const pmCombineParams *params ///< Combination parameters
+                     );
+\end{prototype}
+
+\code{pmReadoutCombine} combines input images pixel by pixel --- for
+each pixel of the output image, a stack of contributing input pixels
+is formed and combined, using the mask and weights if supplied in the
+input readouts.
+
+The \code{output} readout is extended in size, if necessary, to
+contain the input pixels.
+
+The \code{inputs} array contains the \code{pmReadout}s to be combined.
+The images contained within the \code{pmReadout}s need not all be of
+the same size, but the module will take into account the offsets from
+the corner of the detector when comparing pixels, so that it is the
+same \textit{physical} pixels that are combined.
+
+If the \code{zero} vector is non-\code{NULL} then the appropriate
+values are subtracted from the \code{inputs} before rejection is
+performed.  If the \code{scale} vector is non-\code{NULL}, then the
+appropriate values multiply the \code{inputs} before rejection is
+performed.
+
+
+\subsection{Flat-field Re-Normalization}
+
+Consider a collection of $N_i$ flat-field images obtained with a
+mosaic camera consisting of $N_j$ chips.  Each image is exposed to an
+illumination source which should be a uniform surface
+brightness\footnote{This is likely a false assumption: the
+illumination source likely has spatial variations.  However, for the
+purposes of this discussion, it only matters that such spatial
+variations scale consistently as a function of illumination intensity.
+The spatial errors are corrected by the photometric flat-field
+correction technique (eg., Magnier \& Cuillandre 2004).}  Two factors
+determine the actual measured flux level (in Digital Numbers) on each
+of the chips in each image: the gain of each chip ($\mbox{gain}_j$)
+and the flux level from the illumination source ($\mbox{source}_i$).
+When the images are combined, the input images must be scaled so that
+the flux levels can be consistently compared.  After combining the
+collection of images, it is necessary to determine an appropriate
+re-normalization for the resulting flat-field images.  In effect, the
+individual chips must be adjusted so that the master flat-field image
+has a flux level which varies from chip to chip in proportion to the
+actual chip gain.  In this case, if a uniform illumination source
+illuminates the mosaic, the resulting flux levels will be corrected by
+the flat-field to a single, consistent flux level. 
+
+In order to determine the correct relative scaling between the
+devices, it is thus necessary to know the individual chip gains, or at
+least the gain ratios.  A typical technique scaled all chips relative
+to a reference chip, or by a statistic measured for the complete
+collection.  These techniques fail if the input collection of images
+does not always consist of the same set of chips; for the GPC on
+Pan-STARRS, we must expect that individual cells or even chips may be
+disabled on a frequent basis, so our algorithms must not be limited by
+the assumption that all chips are available in all images.  We
+therefore define the following algorithm to measure the relative chip
+gains for a collection of input flat-field images, each with a
+measured flux $\mbox{flux}_{i,j}$.  We want to solve for the chip
+gains and the source illumination fluxes which would make the best
+prediction of the measured input image fluxes:
+\[
+\mbox{flux}^{\rm pred}_{i,j} = \mbox{gain}_j \times \mbox{source}_i
+\]
+This relationship is easiest to determine if we take the logarithm of
+both sides of the equation:
+\[
+M^{\rm pred}_{i,j} = G_j + S_i
+\]
+where $M^{\rm pred}_{i,j} = \log (\mbox{flux}^{\rm pred}_{i,j})$, $G_j
+= \log (\mbox{gain}_j)$, and $S_i = \log (\mbox{source}_i)$.  We can
+then write the chi-square which we want to minimize as:
+\[
+\chi^2 = \sum_{i,j} (M^{\rm obs}_{i,j} - G_j - S_i)^2
+\]
+where we ignore the weights of the different measured flux levels.
+Taking the derivatives with respect to the parameters of interest
+($G_j, S_i$), and setting them to 0, we determine the following set of
+equations which must be solved:
+\[
+G_j \times N_i = \sum_i M^{\rm obs}_{i,j} - \sum_i S_i
+\]
+\[
+S_j \times N_j = \sum_j M^{\rm obs}_{i,j} - \sum_j G_j \\
+\]
+This set of equations can be solved iteratively, starting from the
+assumption that all chip gains are 1.0, ($G_j = 0$), or by supplying
+a guess for the chip gains.  The result of this analysis is the
+measured chip gains and the measured source illumination levels for
+each of the input flat-field images.  The chip gains can then be used
+to modify the flux levels on the master flat-field images.
+
+The following function solves this system.  The matrix of background
+measurements ($M_{i,j}$) contains the background for the flat fields
+used in the combination, as a function of exposure (rows) and chip
+(columns).  The exposure fluxes and chip gains are modified upon
+return with the solved values.  The function returns true if the
+solution converged.  An initial guess for the chip gains might be
+helpful, but is not necessary.
+\begin{prototype}
+bool pmFlatNormalize(psVector **expFluxesPtr, ///< Flux in each exposure, or NULL; modified
+                     psVector **chipGainsPtr, ///< Initial guess of the chip gains or NULL; modified
+                     const psImage *bgMatrix ///< Background measurements: rows are exposures, cols are chips
+                    );
+\end{prototype}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Objects on Images}
+
+\tbd{EAM to update, if necessary.}
+
+\subsection{Overview}
+
+The process of finding, measuring, and classifying astronomical
+sources on images is one of the critical tasks of the IPP or any
+astronomical software system.  In this section, we define structures
+and functions related to the task of source detection and measurement.
+The elements defined in this section are generally low-level
+components which can be connected together to construct a complete
+object measurement suite.  
+
+We first define the collection of structures needed to carry
+information about the detected sources.  A major challenge is to
+define what we mean by an astronomical object in the context of image
+source detection.  An astronomical object may be as simple as a
+stellar point source, or it may consist of a galaxy which has smooth
+extended structure; it may consist of an irregular galaxy or galaxy
+group with substantial and complex sub-structure, or it may consist of
+complex non-stellar structures such as planetary nebulae, reflection
+nebulae, outflows and jets.
+
+The simplest objects (ie, stars) can be sufficiently modeled by the
+point-source function (PSF).  More complex objects (such as simple,
+smooth galaxies), may have approximate analytical models which
+represent their morphology with more-or-less accuracy.  In the extreme
+cases, the objects are not well modeled at all and must be represented
+in other ways.  Thus, one aspect of our data structures must be
+elements to specify if an object has been represented by a model, what
+the model parameters are, and how well it is represented by the model.
+Another aspect of the data structures must be a representation of the
+pixels associated with the object so complex structures may be
+referenced without attempting to supply an analytical model.  Finally,
+it is often useful to allow a single complex model to be represented
+as a collection of simpler contained structures which may be modeled.
+Thus, the representation of an object must be capable of identifying
+children, or substructures, of that object.
+
+Two additional aspects must be considered.  First, source detection
+need not be performed on a single image in isolation: it is necessary
+for multiple realizations of the same source in multiple images to be
+measured together (whether or not through simultaneous fitting in
+multiple bands or via application of the results from one image to
+another image).  Second, it will be necessary to performed object
+measurements on pixels in which no source is actually detected.  For
+example, this is a convenient way to provide flux upper limits at the
+locations of known objects.
+
+In the discussion that follows, images are of type F32 and masks are
+of type U8.
+
+\subsection{Structures to Describe Sources}
+
+In the object analysis process, we will use specific mask values to
+mark the image pixels.  The following structure defines the relevant
+mask values.
+\begin{datatype}
+typedef enum {
+    PSPHOT_MASK_CLEAR     = 0x00,
+    PSPHOT_MASK_INVALID   = 0x01,
+    PSPHOT_MASK_SATURATED = 0x02,
+    PSPHOT_MASK_MARKED    = 0x08,
+} psphotMaskValues;
+\end{datatype}
+
+\subsubsection{pmSource and pmPeak}
+
+We define the following structure to represent a single source
+detected in a single image.  
+\begin{datatype}
+typedef struct {
+  pmPeak *peak;            // description of peak pixel
+  psImage *pixels;         // rectangular region including object pixels
+  psImage *weight;         // Image variance.
+  psImage *mask;           // Mask which marks pixels associated with objects.
+  pmMoments *moments;      // basic moments measure for the object
+  pmModel *modelPSF;       // PSF model parameters and type
+  pmModel *modelEXT;       // FLT model parameters and type
+  pmSourceType type;       // Best identification of object
+  pmSourceMode mode;       // flags describing the model quality 
+  psArray *blends;         // array of other sources blended with this source
+  float apMag;             // measured aperture magnitude for source
+  float fitMag;            // measured model magnitude for source
+  psRegion region;         // area on image covered by selected pixels
+} pmSource;
+\end{datatype}
+
+A source has the capacity for several types of measurements.  The
+simplest measurement of a source is the location and flux of the peak
+pixel associated with the source:
+\begin{datatype}
+typedef struct {
+  int x;                   // x-coordinate of peak pixel
+  int y;                   // y-coordinate of peak pixel
+  float counts;            // value of peak pixel (above sky?)
+  pmPeakType class;        // description of peak
+} pmPeak;
+\end{datatype}
+
+A peak pixel may have several features which may be determined when
+the peak is found or measured.  These are specified by the
+\code{pmPeakType} enum.  \code{PM_PEAK_LONE} represents a single pixel
+which is higher than its 8 immediate neighbors.  The
+\code{PM_PEAK_EDGE} represents a peak pixel which touching the image
+edge.  The \code{PM_PEAK_FLAT} represents a peak pixel which has more
+than a specific number of neighbors at the same value, within some
+tolerance:
+\begin{datatype}
+typedef enum {
+  PM_PEAK_LONE,             // isolated peak
+  PM_PEAK_EDGE,             // peak on edge
+  PM_PEAK_FLAT              // peak has equal-value neighbors
+  PM_PEAK_UNDEF             // Undefined.
+} pmPeakType; 
+\end{datatype}
+
+\subsubsection{pmMoments and source description}
+
+The pixels which contain the source are specified with the
+\code{psImage *pixels} element, a subimage of the image being
+analysed.  Similarly, the \code{mask} element is a subimage of the
+corresponding mask image and the \code{weight} element is a subimage
+of the corresponding weight image (image varience).  Since these are
+subimages, a collection of many objects may include overlapping
+pixels; care must be taken that pixel manipulations for one source do
+not unintentionally interfere with the other source pixels.  The
+\code{mask} may be used to exclude any pixels which are not considered
+part of the source.  Along with these pixel structures, we include the
+\code{psRegion region} element which defines the boundaries of the
+current associated subimages. 
+
+One of the simplest measurements which can be made quickly for an
+object are the object moments.  We specify a structure to carry the
+moment information for a specific source: 
+
+\begin{datatype}
+typedef struct {
+  float x;                  // x-coord of centroid
+  float y;                  // y-coord of centroid
+  float Sx;                 // x-second moment
+  float Sy;                 // y-second moment 
+  float Sxy;                // xy cross moment
+  float Sum;                // pixel sum above sky (background)
+  float Peak;               // peak counts above sky
+  float Sky;                // sky level (background)
+  float SN;                 // approx signal-to-noise
+  int   nPixels;            // number of pixels used
+} pmMoments;
+\end{datatype}
+
+A collection of object moment measurements can be used to determine
+approximate object classes.  The key to this analysis is the location
+and statistics (in the second-moment plane, $\sigma_x$ vs $\sigma_y$)
+of the group of objects which are likely PSF objects.  We define the
+following structure to identify the location and size of the psf clump
+in the second-moment plane.
+\begin{datatype}
+typedef struct {
+    float X;
+    float dX;
+    float Y;
+    float dY;
+} pmPSFClump;
+\end{datatype}
+
+A given source may be identified as most-likely to be one of several
+source types.  The \code{pmSource} entry \code{pmSourceType} defines
+the current best-guess for this source.  
+
+\begin{datatype}
+typedef enum {
+    PM_SOURCE_UNKNOWN,                  ///< no guess yet made
+    PM_SOURCE_DEFECT,                   ///< a cosmic-ray
+    PM_SOURCE_SATURATED,                ///< random saturated pixels
+    PM_SOURCE_STAR,                     ///< a good-quality star
+    PM_SOURCE_EXTENDED,                 ///< an extended object (eg, galaxy)
+} pmSourceType;
+\end{datatype}
+
+The related element, \code{pmSourceMode mode}, holds a collection of flags which
+are used to indicate the status of the analysis for a source.  These
+are defined below:
+\begin{datatype} 
+typedef enum {
+    PM_SOURCE_DEFAULT    = 0x0000, ///< no flags are set
+    PM_SOURCE_PSFMODEL   = 0x0001, ///< flags refer to the PSF model
+    PM_SOURCE_EXTMODEL   = 0x0002, ///< flags refer to the EXT model
+    PM_SOURCE_SUBTRACTED = 0x0004, ///< the model has been subtracted from the image
+    PM_SOURCE_FITTED     = 0x0008, ///< the source has been fitted with a model
+    PM_SOURCE_FAIL       = 0x0010, ///< the model fit failed
+    PM_SOURCE_POOR       = 0x0020, ///< the model fit was poor (low S/N, etc)
+    PM_SOURCE_PAIR       = 0x0040, ///< the model fit is one of a paired source
+    PM_SOURCE_PSFSTAR    = 0x0080, ///< the source was used to construct the image PSF model
+    PM_SOURCE_SATSTAR    = 0x0100, ///< the source is saturated
+    PM_SOURCE_BLEND      = 0x0200, ///< the source is a blend with another source
+    PM_SOURCE_LINEAR     = 0x0400, ///< the source was fitted with the linear PSF model
+    PM_SOURCE_TEMPSUB    = 0x0800, ///< the source has been subtracted, but should be replaced
+} pmSourceMode;
+\end{datatype}
+
+\subsubsection{pmModel Source Model and Abstraction} 
+
+An object's flux distribution may be modeled with some analytical
+function.  The description of the model includes the model parameters
+and their errors, along with the fit $\chi^2$.  The model type is
+identified by code \code{type}, dynamically assigned based on the
+available models (see below).  We discuss the details of these models
+in section~\ref{ObjectModels}.  The model parameters have 4 special
+elements.  The first four elements represent aspects of the source
+which are not specified by the image PSF, even for point sources.  
+These consist of, in order:
+\begin{itemize}
+\item the local sky
+\item the object normalization
+\item the x-coordinate
+\item the y-coordinate
+\end{itemize}
+
+\tbd{should be include utility pointers to these parameters so that
+  functions do not need to know the parameter sequence?}
+
+The structure which carries the information about a given source model
+is defined below:
+\begin{datatype}
+typedef struct {
+  pmModelType type;         // model to be used
+  psVector *params;         // parameter values
+  psVector *dparams;        // parameter errors
+  psF32 chisq;              // fit chisq
+  psS32 nDOF;               // number of degrees of freedom
+  psS32 nIter;              // number of iterations
+  pmModelStatus status;     // fit status
+  float radius;             // fit radius actually used
+} pmModel;
+\end{datatype}
+
+The \code{status} element carries the resulting success/failure status
+of an attempt to fit the model to the source:
+\begin{datatype}
+typedef enum {
+    PM_MODEL_UNTRIED,               ///< model fit not yet attempted
+    PM_MODEL_SUCCESS,               ///< model fit succeeded
+    PM_MODEL_NONCONVERGE,           ///< model fit did not converge
+    PM_MODEL_OFFIMAGE,              ///< model fit drove out of range
+    PM_MODEL_BADARGS                ///< model fit called with invalid args
+} pmModelStatus;
+\end{datatype}
+
+We distinguish several ways in which an analytical model may be
+applied to a source.  The PSF model represents the best fit of the
+image PSF to the specific object.  In this case, the PSF-dependent
+parameters are specified for the object by the PSF, not by the fit.
+The EXT model represents the best fit of the given model to the
+object, with all parameters floating in the fit.  Such a model would
+typically be used to represent and extended object, hence the
+abbreviation EXT.  In some circumstances, a source may be fitted with
+a PSF model in which the position is held fixed, and not allowed to
+vary in the model fitting process.  We identify such a model as FIX.
+Finally, we allow for the case in which two nearly-merged PSFs are
+fitted with a single 2-PSF model.  We identify such a model as DBL.
+The \code{pmSource} structure contains a pointer to both a PSF and an
+EXT model, allowing any source to carry information about both
+possible fitting modes \tbd{not clear that we actually use this
+information; we might be better off simply distinguishing with one of
+the pmSourceMode flags}.  The value of the model at a specific
+coordinate can be determined by calling the function:
+\begin{prototype}
+psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row);
+\end{prototype}
+For this function, the values of \code{col,row} are in the
+\code{image} coordinates, which may be a subimage, while the reference
+coordinate for the model is in the parent image coordinates.
+
+In the \code{pmSource} structure, the elements \code{apMag} and
+\code{fitMag} are used to carry the measured magnitude of the source
+determined either from aperture photometry or from the integral of the
+fitted model function.  The element \code{blends} is used to carry
+pointers to the collection of sources which were found to be blended
+with this source.  Only the primary source of a blend group carries
+this information.%%% (see Section~\ref{blends}).
+
+Every model instance belongs to a class of models, defined by the
+value of the \code{pmModelType type} entry.  Various functions need
+access to information about each of the models.  Some of this
+information varies from model to model, and may depend on the current
+parameter values or other data quantities.  In order to keep the code
+from requiring the information about each model to be coded into the
+low-level fitting routines, we define a collection of functions which
+allow us to abstract this type of model-dependent information.  These
+generic functions take the model type and return the corresponding
+function pointer for the specified model.  Each
+model is defined by creating this collection of specific functions,
+and placing them in a single file for each model.  We define the
+following structure to carry the collection of information about the
+models. 
+
+\begin{datatype}
+typedef struct {
+    char *name;
+    int nParams;
+    pmModelFunc          modelFunc;
+    pmModelFlux          modelFlux;
+    pmModelRadius        modelRadius;
+    pmModelLimits        modelLimits;
+    pmModelGuessFunc     modelGuessFunc;
+    pmModelFromPSFFunc   modelFromPSFFunc;
+    pmModelFitStatusFunc modelFitStatusFunc;
+} pmModelGroup;
+\end{datatype}
+
+Each entry in the \code{pmModelGroup} defines the information needed
+by the system to specify a model.  The function types define above are
+\begin{prototype}
+typedef psMinimizeLMChi2Func pmModelFunc;
+typedef psF64 (*pmModelFlux)(const psVector *params);
+typedef psF64 (*pmModelRadius)(const psVector *params, double flux);
+typedef bool (*pmModelLimits)(psVector **beta_lim, psVector **params_min, psVector **params_max);
+typedef bool (*pmModelGuessFunc)(pmModel *model, pmSource *source);
+typedef bool (*pmModelFromPSFFunc)(pmModel *modelPSF, pmModel *modelFLT, pmPSF *psf);
+typedef bool (*pmModelFitStatusFunc)(pmModel *model);
+\end{prototype}
+
+Each of these functions is found for a given model by calling the
+corresponding lookup function:
+\begin{prototype}
+pmModelFunc          pmModelFunc_GetFunction (pmModelType type);
+pmModelFlux          pmModelFlux_GetFunction (pmModelType type);
+pmModelRadius        pmModelRadius_GetFunction (pmModelType type);
+pmModelLimits        pmModelLimits_GetFunction (pmModelType type);
+pmModelGuessFunc     pmModelGuessFunc_GetFunction (pmModelType type);
+pmModelFromPSFFunc   pmModelFromPSFFunc_GetFunction (pmModelType type);
+pmModelFitStatusFunc pmModelFitStatusFunc_GetFunction (pmModelType type);
+\end{prototype}
+
+\code{pmModelFunc} is the function used to determine the value of the
+model at a specific coordinate, and is the one used by
+\code{psMinimizeLMChi2}.  
+
+\code{pmModelFlux} returns the total integrated flux for the given
+input parameters.
+
+\code{pmModelRadius} returns the scaling radius at which the flux of
+the model matches the specified flux.  This presumes that the model is
+a function of an elliptical contour.  
+
+\code{pmModelLimits} sets the parameter limit vectors for the
+function.
+
+\code{pmModelGuessFunc} generates an initial guess for the model based
+on the provided source statistics (moments and pixel values as
+needed).
+
+\code{pmModelFromPSFFunc} takes as input a representation of the psf
+and a value for the model and fills in the PSF parameters of the
+model.  The input primarily relies upon the centroid coordinates of
+the input model, thought the normalization may potentially be used.
+
+\code{pmModelFitStatusFunc} returns a true or false values based on
+the success or failure of a model fit.  the success is determined by
+quantities such as the chisq or the signal-to-noise.
+
+In addition, the following functions are useful for interacting with
+the collection of models:
+\begin{prototype}
+int                  pmModelParameterCount (pmModelType type);
+\end{prototype}
+This function returns the number of parameters used by the listed
+function.
+
+\begin{prototype}
+char                *pmModelGetType (pmModelType type);
+pmModelType          pmModelSetType (char *name);
+\end{prototype}
+These two functions provide translations between the user-space model
+names and the internal model type codes.  The model type codes are not
+necessarily maintained between compilations of the program; the name
+should be used to transfer models between programs or systems.
+
+\subsubsection{pmGrowthCurve}
+
+When the photometry of source is measured in a fixed aperture, there
+is always a fraction of the source light which falls outside of the
+aperture.  The resulting aperture magnitude is thus larger (ie,
+fainter) than the actual source.  As the aperture is increased, the
+amount of loss decreases and the measured magnitude increases.  This
+trend is the curve of growth for the source.  We use the following
+structure to carry information about the curve of growth.  We use the
+PSF model to measure the curve of growth for an image.  
+
+\begin{datatype}
+typedef struct {
+    psVector *radius;
+    psVector *apMag;
+    psF32 refRadius;
+    psF32 maxRadius;
+    psF32 fitMag;
+    psF32 apRef;   // apMag[refRadius]
+    psF32 apLoss;  // fitMag - apRef
+} pmGrowthCurve;
+\end{datatype}
+In this structure, \code{radius} is a monotonically increasing
+sequence of radius values (in pixels).  The \code{apMag} vector
+contains the measured magnitude at any of these radius: this is the
+curve-of-growth trend.  The remaining entries summaries the
+relationship: \code{refRadius} is the global reference radius used for
+this image; \code{maxRadius} is the outermost radius at which the
+curve of growth was measured; \code{fitMag} is the fitted PSF model
+magnitude integrated to infinity; \code{apRef} is the aperture
+magnitude at the reference radius; \code{apLoss} is the difference
+between the aperture magnitude at the reference radius and the fitted
+model magnitude.  A few related functions are specified to interact
+with the curve of growth:
+
+\begin{prototype}
+pmGrowthCurve *pmGrowthCurveAlloc (psF32 minRadius, psF32 maxRadius, psF32 dRadius);
+\end{prototype}
+This function allocates a \code{pmGrowthCurve} structure and fills in
+the \code{radius} vector (see psLib SDRS \code{psVectorCreate}).  It
+does {\em not} allocate the \code{apMag} vector.
+
+\begin{prototype}
+psF32 pmGrowthCurveCorrect (pmGrowthCurve *growth, psF32 radius);
+\end{prototype}
+This function accepts a \code{growth} curve structure and returns the
+correction between the specified radius and the reference radius
+($apMag(refRadius) - apMag(radius)$).
+
+The following two functions are used to search the growth curve to the
+corresponding radius entry:
+\begin{prototype}
+int psVectorBracket (psVector *index, psF32 key, bool above);
+psF32 psVectorInterpolate (psVector *index, psVector *value, psF32 key);
+\end{prototype}
+
+\subsubsection{Aperture Trends}
+
+With PSF model fitting, there is always some discrepancy between the
+model of the PSF and the actual PSF.  As a result, the measured flux
+from the model will not represent exactly the flux of the source.  It
+is necessary to measure the correction between the model and the
+actual source flux.  One way to perform this measurement is to compare
+the model flux with the flux measured for bright stars within a fixed
+aperture.  The quantity to be measured is $dA = m_{\rm aperture} -
+m_{\rm fit}$.  In practice, $dA$ exhibits variations as a function of
+the source position ($x,y$) and the source flux.  The variations as a
+function of source position can be understood as a change in the PSF
+model error as a function of position due to the changing shape of the
+PSF (despite the varying PSF model, it is possible that the fitted
+model yields positional variations in the residual flux).  The
+variations in $dA$ as a function of magnitude can be understood as the
+result of a bias in the local background measurement (for the fainter
+sources) and as a result of non-linearity in the detector setting on
+the bright end.  We use a 4D polynomial to represent these trends.
+The first two dimensions of the polynomial represent the variation of
+$dA$ as a function of $x,y$; we provide helper functions to define 1st
+and 2nd order polynomials in $x,y$.  The next two dimensions are
+fitted independently (no cross terms).  The first represents the
+variation as a function of $r^2 / flux$, where $r$ is the aperture
+radius used to measure $dA$; this is the scaling of a magnitude error
+in the presence of a constant error in the sky level.  The last
+dimension represents the variation of $dA$ as a function of the
+stellar flux.
+
+The following forms of the aperture correction model may be selected
+by the user:
+\begin{datatype}
+typedef enum {
+    PM_PSF_NONE,
+    PM_PSF_CONSTANT,
+    PM_PSF_SKYBIAS,
+    PM_PSF_SKYSAT,
+    PM_PSF_XY_LIN,
+    PM_PSF_XY_QUAD,
+    PM_PSF_SKY_XY_LIN,
+    PM_PSF_SKYSAT_XY_LIN,
+    PM_PSF_ALL
+} pmPSF_ApTrendOptions;
+\end{datatype}
+
+The following utility function sets the aperture correction model
+coefficient masks to select the specific desired coefficients:
+\begin{prototype}
+bool pmPSF_MaskApTrend (pmPSF *psf, pmPSF_ApTrendOptions option);
+\end{prototype}
+
+\subsubsection{pmPSF, pmPSFtry, and PSF model} 
+
+It is useful to generate a model to define the point-spread-function
+which describes the flux distribution for unresolved sources in an
+image.  In general, the PSF varies with position in the image.  We
+allow any of the source models defined for the \code{pmModel} to
+represent the PSF.  For a given source model, the 2D spatial variation
+of all of the source parameters, except the first four PSF-independent
+parameters, are represented as polynomial, stored in a \code{psArray}.
+The structure also contains the aperture correction model
+(\code{ApTrend}) and the curve-of-growth model (\code{growth}).  The
+additional elements are: \code{ApResid}, the constant term in the
+aperture correction model; \code{dApResid}, the residual scatter for
+bright sources ($S/N > 100$) after applying the aperture correction;
+\code{skyBias}, the measured average bias in the sky measurement;
+\code{skySat}, the scaling of the flux-dependent portion of the
+correction.
+
+The other elements of the structure define the quality of the PSF
+determination.  
+
+\begin{datatype}
+typedef struct {
+    pmModelType type;                   ///< PSF Model in use
+    psArray *params;                    ///< Model parameters (psPolynomial2D)
+    psPolynomial4D *ApTrend;            ///< ApResid vs (x,y,rflux) (rflux = ten(0.4*mInst)
+    pmGrowthCurve *growth;              ///< apMag vs Radius
+    float ApResid;                      ///< ???
+    float dApResid;                     ///< ???
+    float skyBias;                      ///< ???
+    float skySat;                       ///< ???
+    float chisq;                        ///< PSF goodness statistic
+    int nPSFstars;                      ///< number of stars used to measure PSF
+    int nApResid;                       ///< number of stars used to measure ApResid
+} pmPSF;
+\end{datatype}
+
+\begin{prototype}
+pmModel *pmModelFromPSF (pmModel *model, pmPSF *psf);
+\end{prototype}
+This function constructs a \code{pmModel} instance based on the
+\code{pmPSF} description of the PSF.  The input is a \code{pmModel}
+with at least the values of the centroid coordinates (possibly
+normalization if this is needed) defined.  The values of the
+PSF-dependent parameters are specified for the specific realization
+based on the coordinates of the object.  
+
+\begin{prototype}
+bool pmPSFFromModels (pmPSF *psf, psArray *models, psVector *mask);
+\end{prototype}
+This function takes a collection of \code{pmModel} fitted models from
+across a single image and builds a \code{pmPSF} representation of the
+PSF.  The input array of model fits may consist of entries to be
+ignored (noted by a non-zero \code{mask} entry).  The analysis of the
+models fits a 2D polynomial for each parameter to the collection of
+model parameters as a function of position (and normalization?).  In
+this process, some of the input models may be marked as outliers and
+excluded from the fit.  These elements will be marked with a specific
+mask value (1 == \code{PSFTRY_MASK_OUTLIER}).  
+
+We definet he following two functions to convert the PSF model
+parameters into a collection of elements on a metadata structure, and
+vice versa.  These can be used to read and write PSFs to a file and or
+a database.
+\begin{prototype}
+psMetadata *pmPSFtoMD (psMetadata *metadata, pmPSF *psf);
+pmPSF *pmPSFfromMD (psMetadata *metadata);
+\end{prototype}
+
+We have the capability to test several different model functions in an
+attempt to build an accurate PSF for an image.  The complete set of
+data needed to build and test as specific PSF model is carried by the
+\code{pmPSFtry} structure:
+\begin{datatype}
+typedef struct {
+    pmModelType modelType;
+    pmPSF      *psf;
+    psArray    *sources;      // pointers to the original sources
+    psArray    *modelEXT;     // model fits, floating parameters 
+    psArray    *modelPSF;     // model fits, PSF parameters
+    psVector   *mask;
+    psVector   *metric;
+    psVector   *fitMag;
+} pmPSFtry;
+\end{datatype}
+This structure contains a pointer to the collection of \code{sources}
+which will be used to test the PSF model form.  It lists the
+\code{pmModelType type} of model being tests, and contains an element
+to store the resulting \code{psf} representation.  In addition, this
+structure carries the complete collection of FLT (floating parameter)
+and PSF (fixed parameter) model fits to each of the sources
+\code{modelFLT} and \code{modelPSF}.  It also contains a mask which is
+set by the model fitting and psf fitting steps.  For each model, the
+value of the quality metric is stored in the vector \code{metric} and
+the fitted instrumental magnitude is stored in \code{fitMag}.  The
+quality metric for the PSF model is the aperture magnitude minus the
+fitted magnitude for each source.  
+
+This collection of aperture residuals is examined in the analysis
+process, and a linear trend of the residual with the inverse object
+flux (ie, $10^{0.4*mag}$) is fitted.  The result of this fit is a
+measured sky bias (systematic error in the sky measured by the fits),
+an effective infinite-magnitude aperture correction (\code{ApResid}),
+and the scatter of the aperture correction for the ensemble of PSF
+stars (\code{dApResid}).  The ultimate metric to intercompare multiple
+types of PSF models is the value of the aperture correction scatter.
+
+The following functions are used to try out a single PSF model.
+\begin{prototype}
+pmPSFtry *pmPSFtryModel (psArray *sources, char *modelName, float RADIUS);
+\end{prototype}
+This function takes the input collection of sources and performs a
+complete analysis to determine a PSF model of the given type
+(specified by model name).  The result is a \code{pmPSFtry} with the
+results of the analysis.
+
+\begin{prototype}
+bool pmPSFtryMetric (pmPSFtry *try, float RADIUS);
+\end{prototype}
+This function is used to measure the PSF model metric for the set of
+results contained in the \code{pmPSFtry} structure.
+
+The following datatype defines the masks used by the \code{pmPSFtry}
+analysis to identify sources which should or should not be included in
+the analysis.
+\begin{datatype}
+enum {
+    PSFTRY_MASK_CLEAR    = 0x00,
+    PSFTRY_MASK_OUTLIER  = 0x01, // 1: outlier in psf polynomial fit (provided by psPolynomials)
+    PSFTRY_MASK_EXT_FAIL = 0x02, // 2: ext model failed to converge 
+    PSFTRY_MASK_PSF_FAIL = 0x04, // 3: psf model failed to converge 
+    PSFTRY_MASK_BAD_PHOT = 0x08, // 4: invalid source photometry           
+    PSFTRY_MASK_ALL      = 0x0f,
+} pmPSFtryMaskValues;
+\end{datatype}
+
+
+\begin{datatype}
+typedef enum {
+  PM_CONTOUR_CRUDE
+} pmContourType; 
+\end{datatype}
+
+Allocators for the above structures are defined as follows:
+\begin{prototype}
+pmSource   *pmSourceAlloc ();
+pmPeak     *pmPeakAlloc (int x, int y, float counts, psPeakType class);
+pmMoments  *pmMomentsAlloc ();
+pmModel    *pmModelAlloc (pmModelType type);
+\end{prototype}
+
+\subsection{Basic Object Detection APIs}
+
+In this section, we specify a collection of basic functions which
+operate on images and sources.  We define them roughly in order in
+which we expect to use them in a basic object detection process.
+
+\begin{prototype}
+psVector *pmFindVectorPeaks(const psVector *vector, float threshold);
+\end{prototype}
+
+Find all local peaks in the given vector above the given threshold.  A
+peak is defined as any element with a value greater than its two
+neighbors and with a value above the threshold.  Two types of special
+cases must be addressed.  Equal value elements: If an element has the
+same value as the following element, it is not considered a peak.  If
+an element has the same value as the preceding element (but not the
+following), then it is considered a peak.  Note that this rule
+(arbitrarily) identifies flat regions by their trailing edge.  Edge
+cases: At start of the vector, the element must be higher than its
+neighbor.  At the end of the vector, the element must be higher or
+equal to its neighbor.  These two rules again places the peak
+associated with a flat region which touches the image edge at the
+image edge.  The result of this function is a vector containing the
+coordinates (element number) of the detected peaks (type
+\code{psU32}).
+
+\begin{prototype}
+psArray *pmFindImagePeaks(const psImage *image, float threshold);
+\end{prototype}
+
+Find all local peaks in the given image above the given threshold.
+This function should find all row peaks using
+\code{pmFindVectorPeaks}, then test each row peak and exclude peaks
+which are not local peaks.  A peak is a local peak if it has a higher
+value than all 8 neighbors.  If the peak has the same value as its +y
+neighbor or +x neighbor, it is NOT a local peak.  If any other
+neighbors have an equal value, the peak is considered a valid peak.
+Note two points: first, the +x neighbor condition is already enforced
+by \code{pmFindVectorPeaks}.  Second, these rules have the effect of
+making flat-topped regions have single peaks at the (+x,+y) corner.
+When selecting the peaks, their type must also be set.  The result of
+this function is an array of \code{pmPeak} entries.  The resulting set
+of peaks should be considered a starting point, not an unambiguous
+sample of the only real peaks.  If the input image is a subimage, the
+output peak coordinates should be in the {\em parent} coordinate
+frame.
+
+\begin{prototype}
+psArray *pmPeaksSubset(psArray *peaks, float maxvalue, const psRegion valid);
+\end{prototype}
+
+Create a new peaks array, removing certain types of peaks from the
+input array of peaks based on the given criteria.  Peaks should be
+eliminated if they have a peak value above the given maximum value
+limit or if the fall outside the valid region.  The result of the
+function is a new array with a reduced number of peaks.
+
+\begin{prototype}
+bool pmSourceDefinePixels(pmSource *mySource, 
+                          pmReadout *readout,
+                          psF32 x, 
+                          psF32 y,
+                          psF32 Radius)
+
+bool pmSourceRedefinePixels(pmSource *mySource, 
+                            pmReadout *readout,
+                            psF32 x, 
+                            psF32 y,
+                            psF32 Radius)
+\end{prototype}
+
+The first form defines \code{psImage} subarrays (pixel, weight, and
+mask) for the source located at coordinates \code{x,y} on the image
+set defined by \code{readout} (in parent coords).  The pixels defined
+by this operation consist of a square window (of full width $2 Radius
++ 1$) centered on the pixel which contains the given coordinate, in
+the frame of the readout.  The window is defined to have limits which
+are valid within the boundary of the \code{readout} image, thus if the
+radius would fall outside the image pixels, the subimage is truncated
+to only consist of valid pixels.  If \code{readout->mask} or
+\code{readout->weight} are not \code{NULL}, matching subimages are
+defined for those images as well.  This function fails if no valid
+pixels can be defined (x or y less than Radius, for example).  This
+function should be used to define a region of interest around a
+source, including both source and sky pixels.  The second form accepts
+an existing source and redefines the pixels if the requested radius
+encompasses more pixels than the existing images.
+
+\begin{prototype}
+pmSource *pmSourceLocalSky(pmSource *source,
+                           psStatsOptions statsOptions,
+                           float Radius)
+\end{prototype}
+
+Measure the local sky in the vicinity of the given \code{source}.  The
+\code{Radius} defines the square aperture in which the moments will be
+measured.  This function assumes the source pixels have been defined,
+and that the value of \code{Radius} here is smaller than the value of
+\code{Radius} used to define the pixels.  The annular region not
+contained within the radius defined here is used to measure the local
+background in the vicinity of the source.  The local background
+measurement uses the specified statistic passed in via the
+\code{statsOptions} entry.  This function allocates the
+\code{pmMoments} structure.  The resulting sky is used to set the
+value of the \code{pmMoments.sky} element of the provided
+\code{pmSource} structure.  
+
+\begin{prototype}
+bool pmSourceMoments(pmSource *source, float radius);
+\end{prototype}
+
+Measure source moments for the given \code{source}, using the value of
+\code{source.moments.sky} provided as the local background value and
+the peak coordinates as the initial source location.  The resulting
+moment values are applied to the \code{source.moments} entry, and the
+source is returned.  The moments are measured within the given
+circular radius of the \code{source.peak} coordinates.  The return
+value indicates the success (TRUE) of the operation.  This function
+also measures the approximate signal-to-noise ratio of the source
+(\code{source.SN}) based on the total number of source counts divided
+by the square-root of the total source variance, as determined from
+the weight image.
+
+\begin{prototype}
+pmPSFClump pmSourcePSFClump(psArray *sources, psMetadata *metadata);
+\end{prototype}
+
+We use the source moments to make an initial, approximate source
+classification, and as part of the information needed to build a PSF
+model for the image.  As long as the PSF shape does not vary
+excessively across the image, the sources which are represented by a
+PSF (the start) will have very similar second moments.  The function
+\code{pmSourcePSFClump} searches a collection of \code{sources} with
+measured moments for a group with moments which are all very similar.
+The function returns a \code{pmPSFClump} structure, representing the
+centroid and size of the clump in the $\sigma_x$, $\sigma_y$
+second-moment plane.  
+
+The goal is to identify and characterize the stellar clump within the
+$\sigma_x, \sigma_y$ plane.  To do this, an image is constructed to
+represent this plane.  The units of $\sigma_x$ and $\sigma_y$ are in
+image pixels.  A pixel in this analysis image represents 0.1 pixels in
+the input image.  The dimensions of the image need only be 10 pixels.
+The peak pixel in this image (above a threshold of half of the image
+maximum) is found.  The coordinates of this peak pixel represent the
+2D mode of the $\sigma_x, \sigma_y$ distribution.  The sources with
+$\sigma_x, \sigma_y$ within 0.2 pixels of this value are then used to
+calculate the median and standard deviation of the $\sigma_x,
+\sigma_y$ values.  These resulting values are returned via the
+\code{pmPSFClump} structure.
+
+The return value indicates the success (TRUE) of the operation.
+
+\tbd{limit the S/N of the candidate sources (part of Metadata)?} 
+
+\tbd{save the clump parameters on the Metadata} 
+
+\begin{prototype}
+bool pmSourceRoughClass(psArray *sources, psMetadata *metadata, pmPSFClump clump)
+\end{prototype}
+
+Based on the specified data values, make a guess at the source
+classification.  The sources are provides as a \code{psArray} of
+\code{pmSource} entries.  Definable parameters needed to make the
+classification are provided to the routine with the \code{psMetadata}
+structure.  The rules below refer to values which can be extracted
+from the metadata using the given keywords.  Except as noted, the data
+type for these parameters are \code{psF32}.
+
+The following rules are used to make the classification.  The number
+of saturated pixels are counted, based on the mask having the
+\code{PSPHOT_MASK_SATURATED} bit set.  Sources which are greater than
+1$\sigma$ larger than the \code{pmPSFClump} center in both dimensions
+and which have more than a single saturated pixel are identified as
+being a likely saturated star (\code{type = PM_SOURCE_STAR, mode =
+PM_SOURCE_SATSTAR}).  Sources which are not so large but which have
+multiple saturated pixels are identified as saturated regions, ie
+bleed trails or hot columns (\code{type = PM_SOURCE_SATURATED}).
+
+Sources with 
+\[ \sigma_x < 0.05 \]
+or
+\[ \sigma_y < 0.05\]
+should be identified as type \code{PM_SOURCE_DEFECT} (likely cosmic
+ray pixel).
+
+Sources with
+\[ \sigma_x > \mbox{CLUMP}_{x} + 3\mbox{CLUMP}_{dx}\]
+and 
+\[ \sigma_y > \mbox{CLUMP}_{y} + 3\mbox{CLUMP}_{dy}\]
+should be identified as type \code{PM_SOURCE_EXTENDED}.  
+
+All other sources should be identified as type \code{PM_SOURCE_STAR}.
+Of these sources, the mode should be set to \code{PM_SOURCE_PSFSTAR}
+for any sources with $SN$ greater than \code{PSF_SN_LIM} which are
+within 1.5$\sigma$ of the PSF clump center.  These sources are used to
+determine a guess at the shape of the PSF, based on the collection of
+$\sigma_x$ and $\sigma_y$ values.
+
+\subsection{Object Fitting}
+
+We need a way to fit a particular functional model to an object.
+PSLib includes the \code{psMinimizeLMChi2} and \code{psMinimizePowell}
+functions, which form the core of this processes.  However, additional
+support functions and wrapping functions are necessary for the
+specific case of source fitting.  The operations can be broken down
+into discrete steps:
+
+\begin{enumerate}
+\item Identify the pixels of interest
+
+\item Make a guess at the model parameters.  For some models, the
+parameters may be guessed based on only the moments.  For others,
+additional measurements must be made.
+
+\item Construct the input vectors from the pixels of interest.
+
+\item Apply fitting function \code{psMinimizeLMChi2()}
+
+\item Construct model image.
+
+\item Subtract model from image.
+\end{enumerate}
+
+\begin{prototype}
+bool pmSourceModelGuess(pmSource *source, const psImage *image, pmModelType model);
+\end{prototype}
+
+Convert available data to an initial guess for the given model.  This
+function allocates a \code{pmModel} entry for the \code{pmSource}
+structure based on the provided model selection.  The method of
+defining the model parameter guesses are determined by using
+\code{pmModelGuessFunc_GetFunction} to determine the guess function
+for the model of interest.  The returned function is called and the
+guess values are used to set the model parameters.  The function
+returns \code{TRUE} on success or \code{FALSE} on failure.
+
+\begin{prototype}
+psArray *pmSourceContour(const pmSource *source, const psImage *image, float level, pmContourType type);
+\end{prototype}
+
+Find points in a contour for the given source at the given level.  If
+\code{type} is \code{PM_CONTOUR_CRUDE}, the contour is found by starting at
+the source peak, running along each pixel row until the level is
+crossed, then interpolating to the level coordinate for that row.
+This is done for each row, with the starting point determined by the
+midpoint of the previous row, until the starting point has a value
+below the contour level.  The returned contour consists of two vectors
+giving the x and y coordinates of the contour levels.  This function
+may be used as part of the model guess inputs.
+
+\tbd{Other contour types may be specified in the future for more refined contours}
+
+\begin{prototype}
+bool pmSourceFitModel(pmSource *source, psImage *image);
+\end{prototype}
+
+Fit the requested model to the specified source.  The starting guess
+for the model is given by the input \code{source.model} parameter
+values.  The pixels of interest are specified by the
+\code{source.pixels} and \code{source.mask} entries.  This function
+calls \code{psMinimizeLMChi2()} on the image data.  The function
+returns \code{TRUE} on success or \code{FALSE} on failure.
+
+\begin{prototype}
+bool pmModelFitStatus (pmModel *model);
+\end{prototype}
+
+This function wraps the call to the model-specific function returned
+by \code{pmModelFitStatusFunc_GetFunction}.  The model-specific
+function examines the model parameters, parameter errors, Chisq, S/N,
+and other parameters available from \code{model} to decide if the
+particular fit was successful or not.
+
+\begin{prototype}
+bool pmSourceAddModel(psImage *image, pmSource *source, bool center, bool sky);
+bool pmSourceSubModel(psImage *image, pmSource *source, bool center, bool sky);
+\end{prototype}
+
+Add or subtract the given source model flux to/from the provided
+image.  The boolean option \code{center} selects if the source is
+re-centered to the image center or if it is placed at its centroid
+location.  The boolean option \code{sky} selects if the background sky
+is applied (\code{TRUE}) or not.  The pixel range in the target image
+is at most the pixel range specified by the \code{source.pixels}
+image.  The success status is returned.
+
+\begin{prototype}
+bool pmSourcePhotometry (float *fitMag,  // integrated fit magnitude
+                         float *obsMag,  // aperture flux magnitude
+                         pmModel *model, // model used for photometry
+                         psImage *image, // image pixels to be used
+                         psImage *mask   // mask of pixels to ignore
+);
+\end{prototype}
+
+The function returns both the magnitude of the fit, defined as $-2.5
+\log{\rm flux}$, where the flux is integrated under the model,
+theoretically from a radius of 0 to infinity.  In practice, we
+integrate the model beyond $50 \sigma$.  The aperture magnitude is
+defined as $-2.5 \log{\rm flux}$, where the flux is summed for all
+pixels which are not excluded by the aperture mask.  The model flux is
+calculated by calling the model-specific function provided by
+\code{pmModelFlux_GetFunction}.
+
+\begin{prototype}
+int pmSourceDophotType (pmSource *source);
+\end{prototype}
+This function converts the source classification into the closest
+available approximation to the Dophot classification scheme.  The
+following list gives the correspondence:
+\begin{verbatim}
+PM_SOURCE_DEFECT:       8
+PM_SOURCE_SATURATED:    8
+PM_SOURCE_SATSTAR:      10
+PM_SOURCE_PSFSTAR:      1
+PM_SOURCE_GOODSTAR:     1
+PM_SOURCE_POOR_FIT_PSF: 7
+PM_SOURCE_FAIL_FIT_PSF: 4
+PM_SOURCE_FAINTSTAR:    4
+PM_SOURCE_GALAXY:       2
+PM_SOURCE_FAINT_GALAXY: 2
+PM_SOURCE_DROP_GALAXY:  2
+PM_SOURCE_FAIL_FIT_GAL: 2
+PM_SOURCE_POOR_FIT_GAL: 2
+PM_SOURCE_OTHER:        ?
+\end{verbatim}
+
+\begin{prototype}
+int pmSourceSextractType (pmSource *source);
+\end{prototype}
+This function converts the source classification into the closest
+available approximation to the Sextractor classification scheme.
+\tbd{the correspondence is not yet defined}.
+
+\subsection{Object List Input/Output}
+
+We support several object catalog formats.  Some of these mimic the
+formats used by the Elixir system to support testing with existing
+data and software.  Some of these are for use by the Pan-STARRS
+project for testing.
+
+\subsubsection{OBJ Format}
+
+This format is produced by versions of DoPhot and is used by the
+Elixir system as an intermediate output data product.  The objects are
+written to a text file with fixed line-length and with fixed column
+positions.  The file has no header associated with it.  This is only
+an output format, and should be used just for testing and comparison
+with the Elixir tools.
+
+\subsubsection{SX Format}
+
+This format is produced by versions of Sextractor and is used by the
+Elixir system as an intermediate output data product.  The objects are
+written to a text file with fixed line-length and with fixed column
+positions.  The file has no header associated with it.  This is only
+an output format, and should be used just for testing and comparison
+with the Elixir tools.  The SX and OBJ formats are similar, but use a
+somewhat different definition of the columns.
+
+\subsubsection{CMP Format}
+
+This format is used extensively by the Elixir system, and many data
+files are available in this format.  The format is a pseudo-FITS
+format, consisting of a FITS header (with NAXIS=2) and a text data
+segment with fixed line length.  The CMP files are always in SPLIT
+format in the sense that each object table is a single file. 
+
+\subsubsection{CMF Format}
+
+This format is a true FITS table format.  The object data is stored
+for each readout in a separate extension.  In addition, the Cell
+headers are stored in their own extensions (with NAXIS=0).  In SPLIT
+format, the Cell header is the PHU header.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Image Combination}
+
+\tbd{PAP to code and update.}
+
+The image combination for \PS{} will employ an iterative approach, in
+order to identify cosmic rays.  The first pass involves transforming
+and combining the input images, and noting pixels which are apparently
+deviant.  These pixels are examined in further detail, before a subset
+of them are declared to be bad, whereupon these pixels are
+re-transformed, and the images are combined properly.  Here we
+introduce two functions which will perform the combination and
+examination steps.  Prototype code exists for each of these functions.
+\tbd{For further details, see the document about image combination for
+\PS{}.}
+
+\subsection{Combining images}
+
+\begin{prototype}
+psImage *pmCombineImages(psImage *combined, // Combined image
+                         psArray **questionablePixels, // Array of rejection masks
+                         const psArray *images, // Array of input images
+                         const psArray *errors, // Array of input error images
+                         const psArray *masks,// Array of input masks
+                         unsigned int maskVal, // Mask value
+                         const psPixels *pixels, // Pixels to combine
+                         int numIter,   // Number of rejection iterations
+                         float sigmaClip, // Number of standard deviations at which to reject
+                         const psStats *stats // Statistics to use in the combination
+                         );
+\end{prototype}
+
+\code{pmCombineImages} shall combine the input \code{images},
+returning the \code{combined} image and a list of
+\code{questionablePixels} in each input image.  The array of error
+images, \code{errors}, shall be used to calculate the value in the
+combined image and the list of questionable pixels, if
+non-\code{NULL}.  Pixels whose corresponding value in the array of
+mask images, \code{masks}, matches \code{maskVal} shall be masked from
+the combination.  The \code{images}, \code{errors} and \code{masks}
+arrays, if non-\code{NULL}, shall all carry the same number of images;
+otherwise the function shall generate an error and return \code{NULL}.
+The sizes of all images in the \code{images}, \code{errors} and
+\code{masks} arrays shall be identical; otherwise the function shall
+generate an error and return \code{NULL}.
+
+If \code{pixels} is non-\code{NULL}, only those pixels specified shall
+be combined.  The combination consists of \code{numIter} iterations in
+which a stack of pixels is combined using the specified \code{stats}.
+In each iteration, questionable pixels are identified as lying more
+than \code{sigmaClip} standard deviations from the combined value;
+these pixels are excluded from the stack for the next iteration.  The
+value for the combined image is that produced by the \textit{first}
+iteration (i.e., with no pixels excluded except those which have their
+corresponding mask match the \code{maskVal}); this allows subsequent
+calls to the function to only act on a small fraction of the pixels,
+since questionable pixels identified in the first call of the function
+will be properly rejected at a later point (see the example, below).
+
+In the event that \code{images} or \code{stats} are \code{NULL}, the
+function shall generate an error and return \code{NULL}.
+
+\subsection{Rejecting pixels}
+
+\begin{prototype}
+psArray *pmRejectPixels(const psArray *images, // Array of input images
+                        const psArray *masks, // Array of masks for input images
+                        const psArray *pixels, // These are the pixels which were rejected in the combination
+                        const psArray *inToOut, // Transformations from input to output system
+                        const psArray *outToIn, // Transformations from output to input system
+                        float rejThreshold, // Rejection threshold
+                        float gradLimit // Gradient limit
+                        );
+\end{prototype}
+
+\tbd{This algorithm will change: an addition will be made to avoid
+masking pixels in the wings of a star when combining images taken in
+different seeing, and the gradient limit criteria will be changed.}
+
+\code{pmRejectPixels} inspects those questionable \code{pixels}
+identified by \code{pmCombineImages} to determine if they are truly
+discrepant.  This inspection is performed in the coordinate frame of
+the detector, where the pixels haven't been smeared by transformation.
+Two tests are applied to each of the \code{images}:
+\begin{enumerate}
+\item The list of questionable pixels for an image is converted to an
+  image which is transformed back to the coordinate frame of the
+  detector.  Those pixels in the detector frame which have a value
+  exceeding \code{rejThreshold} are suspected cosmic rays and
+  subjected to the next test.  Depending on the value of the
+  \code{rejThreshold}, this test basically amounts to demanding that
+  questionable pixels neighbor each other in the transformed image.
+\item The cores of point sources may mimic a cosmic ray, especially in
+  under-sampled images.  To minimize flagging stars as cosmic rays, we
+  determine the gradient around the pixel of interest; if the gradient
+  is large, then the pixel is likely the core of a point source.  In
+  order to reliably measure the gradient in the presence of a
+  suspected cosmic ray, we use the companion images --- the gradient
+  is the mean gradient at the corresponding position on the other
+  images.  In order to calculate the corresponding positions, the
+  \code{inToOut} and \code{outToIn} transformations are required.  If
+  the gradient is less than \code{gradLimit}, then the pixel is
+  identified as a cosmic ray.
+\end{enumerate}
+
+The function shall return an array of \code{psPixels}, one for each of
+the input \code{images}, containing pixels that have been identified
+as cosmic rays according to the above criteria.
+
+If any of the input pointers are \code{NULL}, then the function shall
+generate an error and return \code{NULL}.
+
+\subsection{Example}
+
+Here is an example of what the image combination routine looks like,
+demonstrating how the various pieces fit together.  The inputs are:
+\begin{itemize}
+\item \code{psArray *inputs}: Input detector images, each a
+  \code{psImage} of type \code{psF32}
+\item \code{psArray *inputMask}: Input mask images, each a
+  \code{psImage} of type \code{psU8}
+\item \code{psArray *inputsErr}: Input error images, each a
+  \code{psImage} of type \code{psF32}
+\item \code{psPlaneTransform *skyToDetector}: Maps from sky
+  coordinates to detector coordinates, each a \code{psPlaneTransform}
+\item \code{psRegion *combineRegion}: Sky coordinate pixels to combine
+\item \code{int numIter}: Number of iterations in combination
+\item \code{float rejThreshold}: Threshold for rejection
+\item \code{float gradLimit}: Limit for gradient
+\end{itemize}
+
+The output is the combined image.
+
+\begin{verbatim}
+    psArray *transformed = psArrayAlloc(nImages); // Array of transformed images
+    psArray *transformedErr = psArrayAlloc(nImages); // Array of transformed error images
+    psArray *transformedMask = psArrayAlloc(nImages); // Array of masks for transformed images
+
+    for (int i = 0; i < nImages; i++) {
+        psPixels *blanks = NULL;        // List of blank pixels
+        transformed->data[i] = psImageTransform(NULL, &blanks, inputs->data[i],
+                                                inputMask->data[i], inputMaskVal, NAN, skyToDetector,
+                                                combineRegion, NULL, PS_INTERPOLATE_BILINEAR);
+        transformedErr->data[i] = psImageTransform(NULL, NULL, inputsErr->data[i], inputMask->data[i],
+                                                   inputMaskVal, NAN, skyToDetector, combineRegion, NULL,
+                                                   PS_INTERPOLATE_BILINEAR_VARIANCE);
+        psImage *skyImage = transformed->data[i]; // Dereference the transformed image
+        psRegion *blankRegion = psRegionAlloc(0, 0, skyImage->numCols, skyImage->numRows); // Size of
+                                                                                           // transformed
+                                                                                           // image
+        transformedMask->data[i] = psPixelsToMask(NULL, blanks, *blankRegion, PS_MASK_BLANK);
+        psFree(blankRegion);
+        psFree(blanks);
+    }
+
+    psArray *rejected = NULL;           // Array of rejected pixel lists
+    psStats *combineStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // Statistic to use in doing the combination
+    psImage *combined = pmCombineImages(NULL, &rejected, transformed, transformedErr, transformedMask, 0,
+                                        NULL, numIter, sigmaClip, combineStats); // Combined image
+    psArray *bad = pmRejectPixels(inputs, rejected, NULL, skyToDetector, rejThreshold, gradLimit); // Bad pix
+    psPixels *combinePixels = NULL;     // Pixels to combine
+    for (int i = 0; i < nImages; i++) {
+        psPixels *badSource = psPixelsTransform(NULL, bad->data[i], skyToDetector); // Bad pixels on the input
+        psImage *badMask = psPixelsToMask(NULL, badSource, PS_MASK_COSMICRAY); // Mask image for the input
+        (void)psBinaryOp(inputMask->data[i], inputMask->data[i], "|", badMask); // Put CRs into original mask
+        psFree(badSource);
+        psFree(badMask);
+
+        combinePixels = psPixelsConcatenate(redo, bad->data[i]);
+
+        // Update transformed image
+        psPixels *blanks = NULL;        // List of blank pixels
+        transformed->data[i] = psImageTransform(transformed->data[i], &blanks, inputs->data[i],
+                                                inputMask->data[i], inputMaskVal | PS_MASK_COSMICRAY, NAN,
+                                                skyToDetector, combineRegion, bad->data[i],
+                                                PS_INTERPOLATE_BILINEAR);
+        transformedErr->data[i] = psImageTransform(transformedErr->data[i], NULL, inputsErr->data[i],
+                                                   inputMask->data[i], inputMaskVal | PS_MASK_COSMICRAY,
+                                                   NAN, skyToDetector, combineRegion, bad->data[i],
+                                                   PS_INTERPOLATE_BILINEAR_VARIANCE);
+        psImage *skyImage = transformed->data[i]; // Dereference the transformed image
+        psRegion *blankRegion = psRegionAlloc(0, 0, skyImage->numCols, skyImage->numRows); // Size of
+                                                                                           // transformed
+                                                                                           // image
+        transformedMask->data[i] = psPixelsToMask(transformedMask->data[i], blanks, *blankRegion,
+                                                  PS_MASK_BLANK);
+        psFree(blankRegion);
+        psFree(blanks);
+    }
+    psFree(bad);
+
+    // Combine with no rejection
+    combined = pmCombineImages(combined, NULL, transformed, transformedErr, transformedMask,
+                               PS_MASK_BLANK, combinePixels, 0, 0.0, combineStats);
+    psFree(combineStats);
+    psFree(combinePixels);
+    psFree(transformed);
+    psFree(transformedErr);
+    psFree(transformedMask);
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Image Subtraction}
+
+\tbd{PAP to code and update.}
+
+Image subtraction is arguably the best method of identifying faint
+variable sources in images with different point-spread functions.  It
+relies on fitting for a convolution kernel that minimizes the
+residuals in subtracting small regions of the image.  The use of a
+convolution kernel consisting of a linear combination of basis
+functions allows the problem to be solved with only modest computing
+power.
+
+\subsection{The kernels}
+
+We will allow for the use of two convolution kernels.  The first is
+that employed by the popular image subtraction program,
+\href{http://www2.iap.fr/users/alard/package.html}{ISIS}, consisting
+of Gaussians modified by polynomials:
+\begin{equation}
+B_{ijk}(u,v) = e^{-(u^2 + v^2)/2\sigma_i^2} u^j v^k
+\end{equation}
+The second simply consists of delta functions, which we refer to as
+POIS (Pan-STARRS Optimal Image Subtraction):
+\begin{equation}
+B_{ij}(u,v) = \delta(u - i)\ \delta(v - j)
+\end{equation}
+\tbd{For further details, see the document about image subtraction for
+\PS{}.}  The former is widely used, while the second appears to be
+equally useful and faster, though not as tried and proven.
+
+\begin{datatype}
+typedef enum {
+    PM_SUBTRACTION_KERNEL_POIS,         // POIS kernel --- delta functions
+    PM_SUBTRACTION_KERNEL_ISIS          // ISIS kernel --- gaussians modified by polynomials
+} pmSubtractionKernelsType;
+\end{datatype}
+
+In order to simplify the book-keeping for the kernels, we will define
+a \code{pmSubtractionKernels}, which keeps track of the details of the
+each of the kernel basis functions:
+
+\begin{datatype}
+typedef struct {
+    pmSubtractionKernelType type;       // Type of kernels --- allowing the use of multiple kernels
+    int size;                           // Size of kernel in x and y
+    int spatialOrder;                   // Maximum order of spatial variations
+    psVector *u, *v;                    // Offset (for POIS) or polynomial order (for ISIS)
+    psVector *sigma;                    // Width of Gaussian (for ISIS)
+    psVector *xOrder, *yOrder;          // Spatial polynomial order (for all)
+    int subIndex;                       // Index of kernel to be subtracted to maintain flux conservation
+    psArray *preCalc;                   // Array of images containing pre-calculated kernel (to
+                                        // accelerate ISIS; don't use for POIS)
+} pmSubtractionKernels;
+\end{datatype}
+
+This structure caters for both choices of kernel type.  For a POIS
+kernel, the \code{u} and \code{v} vectors shall be set to the
+coordinates for the delta functions for the corresponding kernel.  For
+an ISIS kernel, the \code{sigma} vector shall be set to the Gaussian
+widths and the \code{u} and \code{v} vectors shall be set to the
+orders of the modifying polynomials for the corresponding kernel.  For
+both choices of kernel, the \code{xOrder} and \code{yOrder} vectors
+specify the order of the spatial variation.
+
+In order to maintain flux conservation when the kernel is spatially
+variable, we need to treat one kernel in the set differently.  The
+convolutions for this kernel, identified by the \code{subIndex}, are
+calculated in the usual way, while all others have the \code{subIndex}
+kernel subtracted from them.  For details, see the
+\href{http://www.edpsciences.org/journal/index.cfm?v_url=aas/full/2000/11/ds8706/ds8706.html}{paper
+by Alard (2000, A\&AS, 144, 363)}.
+
+Since the ISIS kernels are continuous functions, it is worth
+pre-calculating them instead of calculating them each time they are
+required.  The \code{preCalc} array, consisting of \code{psImage}s is
+provided for this purpose.
+
+The \code{pmSubtractionKernels} are generated by the following functions:
+
+\begin{prototype}
+pmSubtractionKernels *pmSubtractionKernelsAllocPOIS(int size, int spatialOrder);
+pmSubtractionKernels *pmSubtractionKernelsAllocISIS(const psVector *sigmas, const psVector *orders,
+                                                    int size, int spatialOrder);
+\end{prototype}
+
+\code{pmSubtractionKernelsAllocPOIS} shall generate the
+\code{pmSubtractionKernels} suitable for the POIS kernel basis set.
+This involves setting the \code{u}, \code{v}, \code{xOrder} and
+\code{yOrder} to the appropriate values.  \code{size} is the half-size
+of the kernel, and \code{spatialOrder} is the maximum spatial order
+(the spatial variation is $x^i y^j$ with $i+j <$ \code{spatialOrder}).
+The \code{subIndex} is set to the kernel which has \code{u = 0},
+\code{v = 0}, \code{xOrder = 0} and \code{yOrder = 0}.  There should
+be \code{(2 * size + 1) * (2 * size + 1) * (spatialOrder + 1) *
+(spatialOrder + 2) / 2} kernels.
+
+\code{pmSubtractionKernelsAllocISIS} shall generate the
+\code{pmSubtractionKernels} suitable for the ISIS kernel basis set.
+This involves setting the \code{sigma}, \code{u}, \code{v},
+\code{xOrder} and \code{yOrder} to the appropriate values, as well as
+generating the \code{preCalc} images.  Note that the \code{sigma}
+vector contained within the \code{pmSubtractionKernels} is not the
+same as the input \code{sigmas} vector, but contains repeated entries.
+\code{size} is the half-size of the kernel, which specifies the size
+of the \code{preCalc} images.  The \code{spatialOrder} is the maximum
+spatial order (the spatial variation is $x^i y^j$ with $i+j <$
+\code{spatialOrder}).  The \code{subIndex} is set to the kernel which
+has \code{u = 0}, \code{v = 0}, \code{xOrder = 0} and \code{yOrder =
+0}, for the first of the Gaussian widths in the \code{sigmas} vector.
+
+\subsection{Stamps}
+
+Sub-regions on an image which are used to derive the best-fit
+convolution kernel are referred to as ``stamps''.
+
+\begin{datatype}
+typedef struct {
+    int x, y;                           // Position
+    psImage *matrix;                    // Associated matrix
+    psVector *vector;                   // Associated vector
+    pmStampStatus status;               // Status of stamp
+} pmStamp;
+\end{datatype}
+
+A stamp is the region around a central pixel, \code{x,y}.  The
+\code{matrix} and \code{vector} are generated in the process of
+solving for the best-fit convolution kernel; each of these will likely
+be of type \code{psF64} in order to maintain the best possible
+precision (we will be summing squares).  In order to allow us to throw
+out stamps without having to laboriously recompute the total
+least-squares matrix and vector, we use a separate matrix and vector
+for each stamp.
+
+To allow iteration on the choice of stamps, a stamp contains a
+\code{status}, an enumerated type:
+
+\begin{datatype}
+typedef enum {
+    PM_STAMP_USED,                      // Use this stamp
+    PM_STAMP_REJECTED,                  // This stamp has been rejected
+    PM_STAMP_RECALC,                    // Having been reset, this stamp needs to be recalculated
+    PM_STAMP_NONE                       // No stamp in this region
+} pmStampStatus;
+\end{datatype}
+
+\begin{prototype}
+psArray *pmSubtractionFindStamps(psArray *stamps, // Output stamps, or NULL
+                                 const psImage *image, // Image for which to find stamps
+                                 const psImage *mask, // Mask
+                                 unsigned int maskVal, // Value for mask
+                                 float threshold, // Threshold for stamps in the image
+                                 int xNum, int yNum, // Number of stamps in x and y
+                                 int border // Border around image to ignore (should be size of kernel)
+                                 );
+\end{prototype}
+
+\code{pmSubtractionFindStamps} returns an array of stamps on the
+\code{image} suitable for use in calculating the best-fit convolution
+kernel.  Except for a \code{border} all the way around, the
+\code{image} is broken into \code{xNum} $\times$ \code{yNum}
+rectangles; there will be a stamp within each rectangle.  If
+\code{stamps} is non-\code{NULL}, then the function shall only attempt
+to identify a new stamp in a particular rectangle if the corresponding
+stamp \code{status} is \code{PM_STAMP_REJECTED}.
+
+A stamp shall be recognized as the pixel with the greatest value that
+does not have the corresponding pixel in the \code{mask} matching
+\code{maskVal}.  If the value of the this pixel does not exceed
+\code{threshold}, then the stamp \code{status} shall be marked as
+\code{PM_STAMP_NONE}, which means that the stamp will be ignored in
+future iterations.  If a legitimate stamp is found within the region,
+then its status shall be changed to \code{PM_STAMP_RECALC}.
+
+
+\subsection{Solving for the kernel}
+
+Calculating the best-fit convolution kernel requires solving a matrix
+equation, the elements of which are obtained by applying the kernel
+basis functions to the stamps.  The final matrix and vector are the
+sum of the matrices and vectors obtained for each of the individual
+stamps.
+
+\begin{prototype}
+bool pmSubtractionCalculateEquation(psArray *stamps, // The stamps for which to calculate the equation
+                                    const psImage *reference, // Reference image
+                                    const psImage *input, // Input image
+                                    const psSubtractionKernels *kernels, // The kernel basis functions
+                                    int footprint // Half-size of region over which to calculate equation
+                                    );
+\end{prototype}
+
+\code{pmSubtractionCalculateEquation} shall calculate the
+\code{matrix} and \code{vector} for each of the \code{stamps} which
+have \code{status} set to \code{PM_STAMP_RECALC}.  The calculation is
+made over a region with a half size of \code{footprint} on the
+\code{reference} and \code{input} images, using each of the
+\code{kernels}.  In the event that any of the input pointers are
+\code{NULL}, the function shall generate an error and return
+\code{false}; otherwise, the function shall return \code{true}.
+
+The vector is:
+\begin{equation}
+v_i = \sum_{x,y} I(x,y) [ R(x,y) \otimes B_i(u,v) ] / \sigma(x,y)^2
+\end{equation}
+and the matrix is:
+\begin{equation}
+M_{ij} = \sum_{x,y} \left[ R(x,y) \otimes B_i(u,v) \right] \  \left[ R(x,y) \otimes B_j(u,v) \right] / \sigma(x,y)^2
+\end{equation}
+where $I(x,y)$ is the input image, $R(x,y)$ is the reference image,
+$B_i(u,v)$ is the $i$-th kernel basis function, $\otimes$ denotes
+convolution, $\sigma(x,y) = R(x,y)^{1/2}$ is an estimate of the error,
+and the sum over $x,y$ indicates summing over the stamp regions.
+
+In addition to the each of the \code{kernels}, an additional parameter
+for which we must solve is the difference in the background level
+between the \code{reference} and \code{input} images.  The appropriate
+term shall be added to the \code{matrix} and \code{vector}.
+
+In order to maintain flux conservation when the kernel is spatially
+variable, for each of the kernel basis functions apart from the first,
+the kernel actually employed shall be the first kernel function
+subtracted from the original kernel function.
+
+Having calculated the matrix equation for a stamp, its \code{status}
+is set to \code{PM_STAMP_USED}.
+
+Since this step is one of the major rate-limiting factors in image
+subtraction, care should be taken with optimization.
+
+\begin{prototype}
+psVector *pmSubtractionSolveEquation(psVector *solution,        // Solution vector, or NULL
+                                     const psArray *stamps // Array of stamps
+                                     );
+\end{prototype}
+
+\code{pmSubtractionSolveEquation} shall solve the matrix equation
+provided by each of the \code{stamps}, returning the \code{solution}
+vector.  This involves summing the \code{matrix} and \code{vector} of
+each of the stamps which have \code{status} set to
+\code{PM_STAMP_USED}, and multiplying the inverse of the matrix by the
+\code{vector}.  If the \code{solution} is \code{NULL}, then the
+function shall allocate and return a new vector; otherwise, the
+\code{solution} vector shall be modified in-place.  If \code{stamps}
+is \code{NULL}, then the function shall generate an error and return
+\code{NULL}.  The type of the \code{solution} vector should be
+\code{psF64}, since the matrix equation involves summing squares.
+
+
+\subsection{Rejection of stamps}
+
+\begin{prototype}
+bool pmSubtractionRejectStamps(psArray *stamps, // Array of stamps to check for rejection
+                               psImage *mask, // Mask image
+                               unsigned int badStampMaskVal, // Value to use in mask for bad stamp
+                               int footprint, // Region to mask if stamp is bad
+                               float sigmaRej, // Number of RMS deviations above zero at which to reject
+                               const psImage *refImage, // Reference image
+                               const psImage *inImage, // Input image
+                               const psVector *solution, // Solution vector
+                               const pmSubtractionKernels *kernels // Array of kernel parameters
+                               );
+\end{prototype}
+
+\code{pmSubtractionRejectStamps} shall apply the \code{solution} to
+the \code{stamps}, rejecting stamps for which the mean square
+residuals exceed \code{sigmaRej} RMS deviations from zero.
+\code{stamps} which are rejected have their \code{status} set to
+\code{PM_STAMP_REJECTED}, and have pixels within \code{footprint} of
+the corresponding position in the \code{mask} set to
+\code{badStampMaskVal} so they will not be used again.
+
+The deviations are calculated through extracting the stamps from the
+\code{refImage} and \code{inImage}, convolving the reference stamp by
+the best-fit kernel (derived from the \code{solutions} vector and the
+\code{kernels}), subtracting and then dividing by the stamp from the
+input image, and then squaring to obtain the mean square residual.
+
+\subsection{Visualization of kernel}
+
+Having solved for the best-fit kernel, it is often useful to visualize
+it.
+
+\begin{prototype}
+psImage *pmSubtractionKernelImage(psImage *out, const psVector *solution,
+                                  const pmSubtractionKernels *kernels, float x, float y);
+\end{prototype}
+
+\code{pmSubtractionKernelImage} shall create an image of the kernel
+from the \code{solution} vector and the \code{kernels}.  The relative
+position (between -1 and +1) on the image at which to evaluate the
+kernel (important if the kernel is spatially variable) is specified by
+\code{x} and \code{y}.  If \code{out} is \code{NULL}, then the
+function shall allocate a new image of sufficient size (matching the
+\code{precalc} images), and return the result; otherwise, \code{out}
+shall be modified in-place.
+
+
+\subsection{Example}
+
+Here is an example of what the image subtraction routine looks like,
+demonstrating how the various pieces fit together.  The inputs are:
+\begin{itemize}
+\item \code{psImage *reference}: Reference image
+\item \code{psImage *refMask}: Mask for reference image
+\item \code{psImage *input}: Input image
+\item \code{psImage *inMask}: Mask for input image
+\item \code{unsigned int maskVal}: Value to be masked
+\item \code{pmSubtractionKernelType kernelType}: Type of kernel to use
+\item \code{int kernelHalfSize}: Half the kernel size (full size is \code{2*kernelHalfSize + 1})
+\item \code{psVector *sigmas}: Widths for the ISIS Gaussians
+\item \code{psVector *polyOrders}: Polynomial orders for ISIS Gaussians
+\item \code{int spatialOrder}: Maximum spatial order for spatially variable kernel
+\item \code{float stampThreshold}: Threshold for finding stamps
+\item \code{int nStampsX, nStampsY}: Number of stamps in x and y
+\item \code{int stampSize}: Half size of stamp footprint
+\item \code{int numIter}: Number of iterations on the stamps
+\item \code{float sigmaRej}: Rejection threshold for stamps
+\end{itemize}
+
+The output is the subtracted image and the corresponding mask.
+
+\begin{verbatim}
+    // Mask around bad pixels in the reference image.  There are two cases to worry about:
+    // 1. Bad pixels within the kernel, which will affect the subtracted image
+    // 2. Bad pixels within the stamp, which affects the calculation of the kernel
+    psImage *subMask = psImageGrowMask(NULL, refMask, maskVal, kernelHalfSize, PS_MASK_NEAR_BAD);
+    (void)psImageGrowMask(subMask, refMask, maskVal, stampSize, PS_MASK_BAD_STAMP);
+    // Add in the mask for the input image.  Don't need to grow this, since it isn't convolved.
+    (void)psBinaryOp(subMask, subMask, "|", inMask);
+
+    // Generate kernel basis functions
+    psArray *kernels = NULL;            // Array of kernel basis functions
+    switch (kernelType) {
+      case PM_SUBTRACTION_KERNEL_POIS:
+        // Create the kernel basis functions
+        kernels = pmSubtractionKernelsGeneratePOIS(kernelHalfSize, spatialOrder);
+        break;
+      case PM_SUBTRACTION_KERNEL_ISIS:
+        kernels = pmSubtractionKernelsGenerateISIS(sigmas, polyOrders, kernelHalfSize, spatialOrder);
+        break;
+      default:
+        barf();
+    }
+
+    psArray *stamps = NULL;             // Array of stamps
+    psVector *kernelCoeffs = NULL;      // Coefficients for the kernels
+    bool rejected = true;               // Did we reject a stamp in the last iteration?
+
+    // Iterate for a solution
+    for (int iter = 0; iter < numIter && rejected; iter++) {
+
+        // Find stamps
+        stamps = pmSubtractionFindStamps(stamps, reference, subMask, maskVal | PS_MASK_BAD_STAMP,
+                                         stampThreshold, nStampsX, nStampsY, stampSize, kernelHalfSize);
+
+        // Generate and solve matrix equations
+        (void)pmSubtractionCalculateEquation(stamps, reference, input, kernels, stampSize);
+        kernelCoeffs = pmSubtractionSolveEquation(kernelCoeffs, stamps);
+
+        // Reject bad stamps
+        rejected = pmSubtractionRejectStamps(stamps, subMask, PS_MASK_BAD_STAMP, stampSize, sigmaRej,
+                                             reference, input, kernelCoeffs, kernels);
+    }
+
+    // Convolve the reference image
+    psImage *referenceConvolved = pmSubtractionConvolveImage(NULL, reference, subMask, kernelCoeffs, kernels);
+    // Subtract
+    psImage *subtracted = (psImage*)psBinaryOp(NULL, input, "-", referenceConvolved);
+
+    // What does the kernel look like?
+    psImage *kernelImage = pmSubtractionKernelImage(NULL, kernelCoeffs, kernels, 0.0, 0.0);
+    // Check/save kernel image, print statistics....
+
+    psFree(referenceConvolved);
+    psFree(stamps);
+    psFree(kernels);
+    psFree(kernelCoeffs);
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\appendix
+
+\section{Basic Object Models}
+\label{ObjectModels}
+
+We specify a variety of basic object models which are required.
+Details of the model functional forms, parameters, and the derivatives
+are specified in the ADD.
+
+\subsubsection{Real 2D Gaussian}
+
+\begin{prototype}
+float pmMinLM_Gauss2D(psVector *deriv, psVector *params, psVector *x);
+\end{prototype}
+
+This function is a two-dimensional Gaussian with an elliptical
+cross-section and a constant local background.  
+
+The initial guess for the Gaussian parameters may be taken from the
+moments, peak value, and local sky.
+
+\subsubsection{Pseudo-Gaussian}
+
+\begin{prototype}
+float pmMinLM_PseudoGauss2D(psVector *deriv, psVector *params, psVector *x);
+\end{prototype}
+
+This function is a polynomial approximation of a 2D Gaussian otherwise
+very similar to the real Gaussian.  It is used in place of a real
+Gaussian for speed.
+
+The initial guess for the Gaussian parameters may be taken from the
+moments, peak value, and local sky.
+
+\subsubsection{Waussian}
+
+\begin{prototype}
+float pmMinLM_Wauss2D(psVector *deriv, psVector *params, psVector *x);
+\end{prototype}
+
+The Waussian is a modified polynomial approximation of a 2D Gaussian,
+with non-linear polynomial terms having variable coefficients, rather
+than the Taylor series values of 1/2 and 1/6.  
+
+\subsubsection{Twisted Gaussian}
+
+\begin{prototype}
+float pmMinLM_TwistGauss2D(psVector *deriv, psVector *params, psVector *x);
+\end{prototype}
+
+This function describes an object with power-law wings and a flattened
+core, where the core has a different contour from the wings.  
+
+The initial guess for the Gaussian parameters may be taken from the
+moments, peak value, and local sky.
+
+\tbd{future galaxy models to be implemented}
+
+\subsubsection{Sersic Galaxy Model}
+
+\begin{prototype}
+float pmMinLM_Sersic(psVector *deriv, psVector *params, psVector *x);
+\end{prototype}
+
+\subsubsection{Sersic with Core Galaxy Model}
+
+\begin{prototype}
+float pmMinLM_SersicCore(psVector *deriv, psVector *params, psVector *x);
+\end{prototype}
+
+\subsubsection{Pseudo Sersic Galaxy Model}
+
+\begin{prototype}
+float pmMinLM_PseudoSersic(psVector *deriv, psVector *params, psVector *x);
+\end{prototype}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Revision Change Log}
+\input{ChangeLogSDRS.tex}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bibliographystyle{plain}
+\bibliography{panstarrs}
+
+\end{document}
+
+%%% All this is here for storage only --- it's not part of the document.
+
+
+\subsection{Cosmic rays}
+
+Given an input image, a choice of a particular algorithm with
+corresponding parameters, \code{psPhase2MaskCRs} shall mask cosmic
+rays on the input image on the basis of their morphology.  The API shall be the following:
+\begin{prototype}
+/** Masks Cosmic Rays on the input image on the basis of morphology. */
+psReadout *psPhase2MaskCRs(psReadout *in, ///< Input image to be masked, and output
+                           int algorithm, ///< Algorithm number to use
+                           const void *params ///< Parameters for algorithm
+                           );
+\end{prototype}
+
+This is one case in which the best choice of algorithm is not known,
+and may change in the future.  For this reason, we specify the inputs
+as an integer to specify the choice of algorithm in addition to a
+pointer to some parameters which will be decoded on the basis of the
+choice of algorithm.
+
+Note that the input image is modified in-place.
+
+\subsection{psPhase2MaskOpticalDefects}
+
+Given an input image, a list of nearby stars, and a growing radius,
+\code{psPhase2MaskOpticalDefects} shall mask optical defects on the
+image.  The API shall be the following:
+\begin{prototype}
+/** Masks optical defects in the input image */
+psChip *psPhase2MaskOpticalDefects(psChip *in, ///< Image to be masked (with astrometry), and output
+                                   const psDlist *catalog, ///< Catalog stars nearby: a list of psObjects
+                                   int grow ///< Number of pixels to grow
+                                   );
+\end{prototype}
+
+\tbd{It's not clear to me how this is accomplished apart from an
+optical model of the camera.  Put this one on the backburner?}
+
+
+\section{Objects}
+
+To identify and measure objects, we must measure the PSF, and then
+convolve the image by this PSF and apply a threshold.
+
+\subsection{Measure the PSF}
+
+Given an input image, a choice of algorithm with corresponding
+parameters, \code{psPhase2MeasurePSF} shall return the PSF for the
+image.  The API shall be the following:
+\begin{prototype}
+/** Measures the PSF on the input image.  Returns the PSF */
+psImage *psPhase2MeasurePSF(const psReadout *in, ///< Input image for which to measure the PSF
+                            int algorithm, ///< Algorithm number to use
+                            const void *params ///< Parameters for algorithm
+                            );
+\end{prototype}
+
+This is another case where the algorithm is not currently clear, and
+may change in the future.  For this reason, we specify the inputs to
+be a choice of algorithm, and a pointer to some parameters, which are
+interpreted on the basis of the algorithm choice.
+
+\subsection{Find and measure objects}
+
+Given an input image, the PSF of that image, and a list of flux levels
+at which to threshold, \code{psPhase2GetObjects} shall return a
+readout with the \code{objects} member set to a list of objects.  The
+API shall be the following:
+\begin{prototype}
+/** Find and measure objects on the input image.  Fills in the "objects" member of the psReadout. */
+/** THIS NEEDS WORK. */
+psReadout *psPhase2FindObjects(psReadout *in, ///< Input image on which to find objects, and output
+                               const psImage *psf, ///< PSF to use to find objects
+                               const psVector *levels ///< Threshold levels (std dev.s)
+                               );
+\end{prototype}
+
+Note that the input image shall be modified in-place, only insofar as
+the \code{objects} member shall be set to the list of objects on that
+image.
+
+\tbd{This needs a lot more work.}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Astrometry}
+
+Given a chip, the elements of which have objects found and measured on
+them, a list of catalog stars which lie on (or near) the chip, and
+clipping parameters, \code{psPhase2Astrometry} shall fit an
+astrometric solution.  The API shall be the following:
+\begin{prototype}
+/** Corrects astrometry on the input chip */
+psChip *psPhase2Astrometry(psChip *in,  ///< Input chip for which to do astrometry, and output
+                           const psDlist *catalog, ///< Catalog stars on the chip: a list of psObjects
+                           int nClips,  ///< Number of clipping iterations
+                           float clipLevel ///< Level at which to clip
+                           );
+\end{prototype}
+
+Note that the input chip shall be modified in-place, only insofar as
+the appropriate astrometry members shall be updated to correspond to
+the fit solution (specifically, \code{in->cells[i]->cellToSky}, for
+each \code{i}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Postage stamps}
+
+Postage stamps consist of subimages of specific objects of interest,
+which are saved for more careful analysis of the pixels.
+
+\subsection{Specification}
+
+The postage stamps are specified by a center and size, in celestial
+coordinates.  We define a \code{psPostageStampSpec} to specify the
+parameters for a postage stamp:
+\begin{datatype}
+/** Specification of a postage stamp: location on the sky */
+typedef struct {
+    psSphereCoord *center;              ///< Centre of postage stamp
+    psSphereCoord *size;                ///< Size of postage stamp
+} psPostageStampSpec;
+\end{datatype}
+
+The \code{center} shall be specified in ICRS coordinates, which is the
+\PS{} standard system.  The \code{size} shall be specified in
+arcseconds on the sky.
+
+\subsection{Extracting postage stamps}
+
+Given an input chip, and a linked-list of regions on or near the input
+chip, \code{psPhase2PostageStamps} shall output an array of subimages,
+containing each of the regions.  The API shall be the following:
+\begin{prototype}
+/** Return postage stamps of a set of regions */
+psImageArray *psPhase2PostageStamps(const psChip *in, ///< Chip from which to form postage stamps
+                                    const psDlist *regions ///< Regions to postage-stampise: a list of
+                                                           ///< psPostageStampSpec
+                                    );
+\end{prototype}
+
+\code{regions} shall be a linked list of \code{psPostageStampSpec}s,
+not all of which may correspond to legal positions on the input chip,
+\code{in}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Fixed Pattern}
+
+The fixed pattern is a correction to the general astrometric solution
+formed by summing the residuals from many observations.  The intent is
+to correct for higher-order distortions in the camera system on a
+coarse grid (larger than individual pixels, but smaller than a single
+cell).  Hence, in addition to the offsets, we need to specify the size
+and scale of the grid in $x$ and $y$, as well as the origin of the
+grid.
+
+\begin{datatype}
+typedef struct {
+    int nX;                             ///< Number of elements in x
+    int nY;                             ///< Number of elements in y
+    double x0;                          ///< Position of 0,0 corner on focal plane 
+    double y0;                          ///< Position of 0,0 corner on focal plane
+    double xScale;                      ///< Scale of the grid
+    double yScale;                      ///< Scale of the grid
+    double **x;                         ///< The grid of offsets in x
+    double **y;                         ///< The grid of offsets in y
+} psFixedPattern;
+\end{datatype}
+
+The constructor for \code{psFixedPattern} shall be:
+\begin{prototype}
+psFixedPattern *psFixedPatternAlloc(double x0,        double y0, 
+                                    double xScale,    double yScale,
+                                    const psImage *x, const psImage *y);
+\end{prototype}
+Here, \code{x0}, \code{y0}, \code{xScale} and \code{yScale} have the
+same meaning as in the \code{psFixedPattern} structure.  Note that the
+values of the fixed pattern offsets are specified as images, the
+values from which need to be copied into the \code{double **x} and
+\code{double **y} of \code{psFixedPattern}, and that the number of
+elements may be derived from the size of the images.
+
+\tbd{Usage of this type is not clear, and awaits prototyping --- do not
+worry about coding this in detail yet.}
+
Index: /tags/sj_tags/sj_root_20080929/doc/modules/modules-delta
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/modules/modules-delta	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/modules/modules-delta	(revision 22322)
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: modules-delta,v 1.2 2005-09-20 21:43:56 jhoblitt Exp $
+
+MODULE=psmodule
+DOC=ModulesSDRS.tex
+UTIL_MODULE=utils
+ROOTDIR=$HOME/tmp/$0
+WORKDIR=$ROOTDIR/work
+UTILDIR=$ROOTDIR/$UTIL_MODULE
+TEXBLOCK=$UTILDIR/texblock.pl
+APIDELTA=$UTILDIR/apidelta.pl
+CVS=/usr/bin/cvs
+SSH=/usr/bin/ssh
+
+CVS_RSH=$SSH
+CVSROOT=cvs.pan-starrs.ifa.hawaii.edu:/cvsroot/pan-starrs
+export CVS_RSH CVSROOT
+
+PERL5LIB=$UTILDIR
+export PERL5LIB
+
+STARTDIR=`pwd`
+
+usage()
+{
+    cat <<EOF
+Usage: $0 [--color={none,auto,always}]
+EOF
+}
+
+if test $# -gt 1; then
+    usage
+    exit 1
+fi
+
+# checkout pslib sources
+
+CVS_ERROR=0
+
+if [ ! -d "$ROOTDIR/$MODULE" ] ; then
+    mkdir -p $ROOTDIR
+    cd $ROOTDIR
+    $CVS co $MODULE > /dev/null 2>&1 || CVS_ERROR=1
+else
+    cd "$ROOTDIR/$MODULE"
+    $CVS up -dP > /dev/null 2>&1 || CVS_ERROR=1
+fi
+
+if [ $CVS_ERROR -eq 1 ] ; then
+    echo "CVS error"
+    exit 1;
+fi
+
+# checkout utils
+
+CVS_ERROR=0
+
+if [ ! -d "$ROOTDIR/$UTIL_MODULE" ] ; then
+    mkdir -p $ROOTDIR
+    cd $ROOTDIR
+    $CVS co $UTIL_MODULE > /dev/null 2>&1 || CVS_ERROR=1
+else
+    cd "$ROOTDIR/$UTIL_MODULE"
+    $CVS up -dP > /dev/null 2>&1 || CVS_ERROR=1
+fi
+
+if [ $CVS_ERROR -eq 1 ] ; then
+    echo "CVS error from utils"
+    exit 1;
+fi
+
+# compare sources
+
+if [ ! -d "$WORKDIR" ] ; then
+    mkdir -p $WORKDIR
+fi
+
+cd $STARTDIR
+
+find $ROOTDIR/$MODULE/src -name "*.h" -exec cat {} \; > $WORKDIR/code.txt
+$TEXBLOCK -b prototype -i $DOC > $WORKDIR/spec.txt
+$TEXBLOCK -b datatype -i $DOC >> $WORKDIR/spec.txt
+$APIDELTA $1 --spec $WORKDIR/spec.txt --code $WORKDIR/code.txt
+
+# cleanup
+
+#if [ -d "$WORKDIR" ] ; then
+#    rm -rf $WORKDIR
+#fi
Index: /tags/sj_tags/sj_root_20080929/doc/modules/modules-delta-files.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/modules/modules-delta-files.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/modules/modules-delta-files.sh	(revision 22322)
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+DATE=`date +%F`
+NAME="psmodules-10-api_delta"
+
+./modules-delta --color=always > "$NAME-color-$DATE.txt"
+./modules-delta --color=none >   "$NAME-$DATE.txt"
Index: /tags/sj_tags/sj_root_20080929/doc/modules/pmFPAfile.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/modules/pmFPAfile.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/modules/pmFPAfile.tex	(revision 22322)
@@ -0,0 +1,120 @@
+
+\subsection{FPA-related file I/O, file naming, and supporting data}
+
+The Pan-STARRS IPP image analysis modules support a very wide range of
+image file formats.  The analysis programs perform a wide range of
+operations and have potentially many different input and output files
+which need to be defined.  The \code{pmFPAfile} system allows a
+program to define complex related I/O operations which may be
+easily controlled within the psModules configuration system.  
+
+To motivate the discussion of the \code{pmFPAfile} system
+capabilities, let us consider a few examples of the types of I/O
+operations which must be regularly performed.  One of the work-horse
+programs is \code{ppImage} which performs the basic detrending
+processing as well as optional object detection, astrometry, cell/chip
+mosaicing, and image jpeg creation.  Even for the simplest type of
+image file, a single array FITS file, this program needs to be able to
+determine filenames for up to 8 different types of output data
+products:
+\begin{itemize}
+\item detrended image
+\item background model
+\item background-subtracted
+\item object-subtracted
+\item object-table
+\item astrometrically calibrated table
+\item jpeg output image
+\item psf-model output
+\end{itemize}
+In addition, there are at least 6 different potential input files in
+addition to the image of interest which may need to be determined:
+\begin{itemize}
+\item input image
+\item mask image
+\item bias image
+\item dark image
+\item flat image
+\item fringe image
+\item shutter correction image
+\item cross-talk matrix
+\item supplied psf-model
+\end{itemize}
+The situation becomes substantially more complex when the image file
+consists of more complex data structures.  The input image files may
+represent a focal plane divided into multiple chips, each read out
+through multiple amplifiers (ie, multiple cells).  A single camera
+exposure may be saved as a single FITS file with many extensions,
+multiple basic FITS files, or multiple FITS files with multiple
+extensions.  The program must be able to determine the filename for
+each of these elements as appropriate to the camera of interest.  The
+\code{pmFPAfile} system provides a flexible and convenient mechanism
+to address this complex situation.  
+
+There are two possible approaches to the filenaming problem which
+represent opposite extremes.  In the one extreme, the program could
+require the user to define each of the input and output filenames on
+the command-line with flags to distinguish the different types of
+data.  Even for the case of a simple camera image format, the required
+command lines become rather complex.  For the case of the multiple
+files or multiple extension formats, the situation quickly becomes
+prohibitively cumbersome.  In the opposite extreme, the program could
+use rigid rules which define the name of an output file, perhaps based
+on the input file names.  For example, extensions or extra letters
+could be added to the filenames to denote the different types of
+output data products.  In such a scenario, the output object table for
+a given input image would always have the same resulting name.  This
+option makes it easy to write the commands, but is likely to be too
+rigid to support analysis of varied data in a range of environments.  
+
+The \code{pmFPAfile} system uses a compromise between the above two
+systems to allow the user to define {\it rules} which determine the
+input and output names (and in the case of input data, their sources).
+The rules may thus be different for different cameras, or even
+different for the same camera in different contexts, while at the same
+time freeing the user from the need to specify every output file for
+every run of the program.
+
+The \code{pmFPAfile} system is also designed to simplify the process
+of writing a program in which there are many possible, optional I/O
+operations, especially in the context of different library functions
+which may need to interact with the same set of data products.  
+
+One of the programming issues which the \code{pmFPAfile} system
+addresses is how to supply the many possible I/O concepts to
+individual APIs within a program.  Consider a high-level function
+which performs a rich set of operations, for example an object
+detection process.  The function may require a certain set of input
+data (eg, the input image) and may always produce an additional set of
+output data (eg, the set of detected objects).  However, there may be
+other data concepts which may be optionally supplied to or generated
+by the function depending on the context.  For example, the function
+may allow the calling function to supply an optional model of the
+image PSF.  Or, the function may generate an object-subtracted image,
+but only supply it back to the calling function if requested (eg,
+freeing it otherwise).  \note{these do not necessarily represent I/O
+  operations.  is there a more general concept which the pmFPAfile is
+  addressing, not just I/O?}  How might these be supplied to the
+function, and how are the choices defined?  
+
+One possibility is that the function simply include in its API of all
+of the inputs and outputs as separate function parameters.  If the
+parameter is NULL, the option is not chosen.  This is a perfectly
+workable solution, but makes for potentially very long function APIs.
+If most of the data types are the same (eg, some image structure),
+then there is the danger that the calling program will not get the
+elements in the correct order if the program or the compiler cannot
+distinguish between elements of the same type.  
+
+Another possibility is to define an structure for that function in
+which the required and optional input and output data elements are
+represented by separate structure elements.  This is also a very
+workable solution, with some aesthetic drawbacks.  If each API has a
+separate structure, then each function call must be proceeded by a
+cumbersome sequence of steps to set up the data elements to be used by
+the function call.  If a single structure is supplied to all APIs,
+then it will be necessary to maintain the synchronization of the
+structure elements between all libraries and programs which use it.  
+
+The \code{pmFPAfile} system uses a dynamic structure (a
+\code{psMetadata}) to register the data 
Index: /tags/sj_tags/sj_root_20080929/doc/pantasks/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pantasks/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pantasks/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.log *.dvi *.aux *.toc *.log *.out *.lof *.tbr *.tbd *.pdf
Index: /tags/sj_tags/sj_root_20080929/doc/pantasks/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pantasks/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pantasks/Makefile	(revision 22322)
@@ -0,0 +1,29 @@
+
+PDFLATEX = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): pdflatex
+PSLATEX  = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): latex
+
+help:
+	@echo "USAGE: make (target)"
+	@echo "  targets: pantasks all"
+
+pantasks: pantasks.pdf 
+all : pantasks
+
+%.pdf: %.tex
+	$(PSLATEX) $*.tex 
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	thumbpdf --modes=dvips $*.pdf
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	@rm -f $*.ps $*.dvi $*.aux $*.log $*.tbr $*.tbd $*.toc $*.tpm $*.lof body.tmp head.tmp
+
+clean :
+	$(RM) *.log *.dvi *.aux *.toc *.tbd *.tbr *.tpm *.lof *.out *~ core body.tmp head.tmp
+
+dist : clean
+	$(RM) *.pdf
+
+empty: clean
Index: /tags/sj_tags/sj_root_20080929/doc/pantasks/pantasks.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pantasks/pantasks.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pantasks/pantasks.tex	(revision 22322)
@@ -0,0 +1,884 @@
+\documentclass[panstarrs,spec]{panstarrs}
+
+\title{PanTasks Software Design Description}
+\subtitle{The IPP Scheduler and Controller system}
+\author{Eugene Magnier}
+\audience{IPP}
+\shorttitle{PanTasks SDD}
+\group{Pan-STARRS IPP}
+\project{Pan-STARRS IPP}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-017}
+
+\begin{document}
+\maketitle
+
+\tableofcontents
+\pagebreak 
+\pagenumbering{arabic}
+
+\section{Overview}
+
+This document discusses the design of PanTasks.  PanTasks is the IPP
+tool which manages the sequencing of data analysis steps and, with the
+related tool `PControl', distributes the data processing across a
+cluster of computers. \tbd{uses the 'opihi' shell-scripting system}
+
+The purpose of PanTasks is to manage the automatic construction and
+execution of inter-related (often repetative) operations.  PanTasks
+uses a set of rules to define UNIX commands, and their corresponding
+command-line arguments, to be performed on some regular, repeated
+basis.  The utility of PanTasks is that it can easily define and
+manage an analysis system which is completely state-based, as opposed
+to an event-driven system.
+
+Consider, for example, a telescope which obtains a collection of
+images over the course of a night. Every minute or two, it takes an
+image and writes the image to some disk. An event-driven analysis
+system would involve having the telescope initiate a process at the
+end of the exposure. This process would perform an analysis, write
+some output, then send trigger another process. This type of operation
+works very well for a simple set up with reliable hardware. Such a
+system becomes more difficult to maintain when hardware failures occur
+or when multiple systems need to interact with each other. When
+failures occur, the triggering information (the events) is easily
+lost, thus some mechanisms are needed to detect these failures and
+either re-send the trigger or send an alternative failure-mode
+trigger. Or, if two systems need to interact, one or the other system
+must block for results from the first. Stopping and restarting such an
+analysis system is very delicate since the appropriate triggers must
+be set up some how, eg by noticing which images have not succeeded and
+restarting them at the appropriate stage. All of these types of
+methods of handling complexity and failures are essentially
+state-based rules. PanTasks allows the easy definition of a totally
+state-based analysis system.
+
+In a state-based system, some mechanism examines the state of the
+system and decides which actions to perform based on the current
+state. In the illustration above, the mechanism could examine the
+images available (either by examining the disk or by examining the
+state of a data table) and decide to perform an operation based on
+what images are available. This makes it very easy to handle
+complexity and errors. If an analysis fails, the state either is not
+successfully updated or the error state is recorded, both situations
+being easy to detect and easy to handle. Restarting the system simply
+involves starting the state-monitoring mechanism. Combining results
+from multiple input sources simply involves watching for the multiple
+inputs to be available. PanTasks provides a mechanism to define state
+monitors, and to define the actions which are performed when those
+states occur. PanTasks action consist of initiating UNIX commands, where
+the arguments of those commands may depend on the results of the state
+tests.  
+
+\subsection{Tasks vs Jobs}
+
+The two basic units of PanTasks operation are the 'task' and the
+'job'.  A 'job' is simply a command the user would execute on a
+command line; it consists of a command along with optional command
+line arguments.  A 'task' is a generic description of a type of job in
+which the details of any specific piece of data are omitted.  The task
+defines the UNIX command which corresponds to the job, and it provides
+rules for determining the identity of data for the job.  The task also
+defines tests which are used to decide if the job may be executed.
+Finally, it defines a polling frequency in which PanTasks should
+attempt to construct a new job for the task.
+
+For example, we may want to regularly copy files from one location to
+another.  Perhaps the name of the next available file is available in
+a database table, and can be retrieved with the command 'nextFile'.
+Perhaps we wish to check for new files every 60 seconds.  Thus, the
+task is to copy files, while a specific job of this task might be of
+the form \code{cp newfile newpath}.  With PanTasks, we would define a
+copy task somewhat like the following:
+
+\begin{verbatim}
+  task copyfile
+    periods -exec 60.0
+
+    task.exec
+      $file = `nextFile`
+      if ($file == "none")
+        break
+      end
+      command cp $file $newpath
+    end
+
+    task.exit 0
+      queuepush copied $file
+    end
+
+    task.exit 1
+      queuepush failure $file
+    end
+  end
+\end{verbatim}
+
+In this simple example, the task is attempted every 60 seconds.  If
+there is no new file (output of 'nextFile' is 'none'), then no job
+results from this task.  If there is a new file, the copy command is
+performed.  This example is deceptively simple because one could
+easily imagine writing a stand-along program which performs the same
+thing.  The important advantages of using PanTasks for this type of
+operation are: 
+\begin{itemize}
+\item the output from one job can be used to spawn a number of other tasks.
+\item the success or failure state of each job can be used to spawn
+  other tasks.
+\item the details of the job and data test are kept separated from the
+  rules which connect tasks together.  
+\item the relationships between tasks are kept together in a single
+  location.
+\end{itemize}
+
+In addition to these organizational advantages, PanTasks also provides
+a direct connection to the tool for monitoring parallel jobs,
+pcontrol.  Thus the jobs spawned by PanTasks can be defined to run in
+the background locally or on any of the computers in the parallel
+processing cluster.
+
+\subsection{Parallel vs Local Job Processing}
+
+Jobs which are generated by PanTasks tasks may either be run locally
+(forked in the background on the same machine as PanTasks) or run on
+the IPP parallel process controller, pcontrol. The default is for the
+job to be run locally. If a job should be run on the parallel
+controller, this can be specified by including the command host
+(hostname) in the definition of a task. If the value of (hostname) is
+'anyhost', then pcontrol may select any of its host computers to run
+the job according to its own rules. If the value of (hostname) is one
+of the computers managed by pcontrol, then that machine will be
+selected for the job, if it is available. This amounts to a preference
+to use that machine, but pcontrol is allowed to substitute a different
+machine if it chooses. If the host command is given the option
+-required, then pcontrol is forced to use the named host, even if the
+machine is down, unknown, or otherwise unavailable. If the machine is
+not available, pcontrol will simply hold onto the job until the
+machine is available or the job is deleted. Note that PanTasks may
+delete jobs from pcontrol if they remain pending for too long.
+
+It is possible to interact directly with the parallel processor to
+examine the current status, halt the parallel processor, etc. Commands
+to the parallel processor are defined under the controller
+command. The following controller commands are available:
+
+\begin{itemize}
+
+\item controller host (command) (hostname): Manage the parallel
+  controller collection of hosts. This command can be used to add a
+  new host, the delete one of the existing hosts, to turn a host on or
+  off, and to check the status of a host
+
+  \begin{itemize}
+  \item controller host add (hostname): add a new host.
+
+  \item controller host delete (hostname): delete a host.
+
+  \item controller host on (hostname): tell pcontrol that the host is on.
+
+  \item controller host off (hostname): tell pcontrol that the host is off.
+
+  \item controller host retry (hostname): tell pcontrol to retry the host connection.
+
+  \item controller host check (hostname): check the current status of a host. 
+  \end{itemize}
+
+  \item controller exit: stop controller execution.
+
+  \item controller status: report controller current status.
+
+  \item controller check: check job or host status.
+
+  \item controller output: print accumulated messages from the controller. 
+\end{itemize}
+
+It is also possible to specify a host for a task which has not been
+identified to the controller. If such a host is required, the
+controller will simply keep the associated jobs in the pending state
+until such a machine exists. See the pcontrol section below for
+further discussion of the controller manipuation of jobs and hosts.
+
+\subsection{Task Restrictions}
+
+Tasks may have restrictions on when they create jobs and how
+frequently they create jobs. The task command trange is used to
+specify a valid or invalid time range for a task. A valid time range
+limits the task evaluation to that time period. An invalid time range
+excludes task evaluation from the time period. Any number of time
+range restrictions may be defined, and the union of all restrictions
+will define if a job may be created. By default, the time range is an
+inclusive time range: the task is evaluated only if the current time
+falls within the specified time range. Alternatively, if the -exclude
+flag is given, the time range is exclusive, in which case the task is
+not evaluated if the current time falls within this range.
+
+The time range may be given as a range of absolute dates as follows:
+
+\begin{verbatim}
+ trange YYYY/MM/DD,HH:MM:SS YYYY/MM/DD,HH:MM:SS 
+\end{verbatim}
+
+where the two dates specify the start and end of the time range. In
+either of these date representations, the least-significant elements
+of the date and time may be dropped, defaulting to 00 (in the case of
+hours, minutes, and seconds) or 01 (in the case of day and
+months). Rather than specifying an end date, it is also valid to
+specify a time interval from the starting date. The time interval is
+specified as a number followed by a unit indicated by a single letter:
+d (days), h (hours), m (minutes), s (seconds).
+
+The time range may also be specified as a repeated period of time,
+either as a time of day or a day and time of week. In the first case,
+the time range is specified as follows:
+
+ 
+\begin{verbatim}
+ trange HH:MM:SS HH:MM:SS
+ \end{verbatim}
+
+
+where again the least-significant elements may be dropped and default
+to 00. This type of restriction defines a time range which is valid
+every day. The alternative is to specify a time range within the week,
+in the following form:
+
+\begin{verbatim}
+ trange DAY@HH:MM:SS DAY@HH:MM:SS
+\end{verbatim}
+
+where the value of DAY may take on any of the three letter day-of-week
+names (Sun, Mon, Tue, etc). This restriction specifies a start and end
+time within a week which is evaluated for each week.
+
+Below are several examples of valid time range restrictions
+
+\begin{verbatim}
+ trange 2005/01/01 2005/12/31   (only run during 2005!)
+ trange 18:00 00:00             (only run from 6pm until midnight)
+ trange 00:00 06:00             (only run from midnight until 6am)
+ trange Mon@08:00 Fri@17:00     (only run between Mon morning and Fri afternoon)
+ trange -exclude 12:00 13:00    (skip 1 hour from noon)
+\end{verbatim}
+
+Note that the current definition of trange does not include time zone
+information. This means that all times are relative to UT. This should
+be addressed by adding a timezone environment variable to PanTasks and
+by allowing the trange to define a timezone offset.
+
+It is also possible to restrict the total number of jobs which are
+spawned for a given task. This is done with the nmax command, which is
+given as part of the task definition. Once a task has constructed nmax
+jobs, it stops task evaluation. It is possible to redefine the value
+of nmax at any time by redefining the task. Any time the task is
+redefined, the new values for any task concept will override the
+existing values for the task concept.
+
+\subsection{Inter-Task and Inter-Job Communications}
+
+There are several ways in which the results of jobs may be used to
+influence other jobs. These include:
+
+\begin{itemize}
+\item external communications
+\item job exit status
+\item job stdout/stderr parsing 
+\end{itemize}
+
+It is always possible for the interprocess communication to be
+performed externally: all jobs may simply write results to an external
+data source which is queried as part of the task evaluation. PanTasks
+may interact with UNIX programs using Opihi system interaction
+functions. These interaction methods include: the backticks for
+setting Opihi variables:
+
+\begin{verbatim}
+ $variable = `UNIX Command`
+\end{verbatim}
+
+The exec command (which executes a UNIX command) and the backticks
+both receive the UNIX command exit status, setting the variable
+\code{$STATUS}. It is also possible to set a variable list to the
+output of a UNIX command:
+
+\begin{verbatim}
+ list var -x "UNIX Command"
+\end{verbatim}
+
+In this last case, the values \code{$var:0 - $var:N-1} are set to the
+value of the stdout lines from the UNIX command, and the value
+\code{$var:n} is set to the number of output lines.
+% $
+
+Fine-grained control over the job exit status is available with the
+task.exit macro command. This allows a task to define an exit macro
+which is performed for different exit status conditions. The argument
+to the task.exit command is the exit status value which triggers the
+macro. This may consist of any valid numeric exit status value
+(0-255). It may also have the value crash, in which case the macro is
+executed if the program exited as a result of a signal (ie,
+segmentation fault, etc). Finally, if may have the value default, in
+which case, the macro is run if no other macro describes the exit
+status.
+
+Jobs may transmit their results back to PanTasks for further
+evaluation through the standard output and standard error
+streams. Whenever a job exits, the complete stdout and stderr streams
+from the job are pushed onto the PanTasks queues \code{stdout} and
+\code{stderr}. The job exit macros may then parse these queues, moving
+the results into other PanTasks / Opihi data containers (queues,
+variables, vectors, whatever is appropriate). Note that currently, the
+output data is simply pushed onto these output queues. It is currently
+the responsibility of the PanTasks programmer to use or dispose of the
+data in these queues. This may change in the future: the queues may be
+flushed for each job completion.
+
+\subsection{PanTasks Monitoring Loop}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.01.ps}
+\caption{\label{MonitorLoop} PanTasks Monitor Loop}
+\end{center}
+\end{figure}
+
+Figure~\ref{MonitorLoop} illustrates the operation of PanTasks.
+Currently, PanTasks is run as a stand-alone foreground process, not a
+server.  In this current operating mode, PanTasks accepts commands
+from the user (left side) via a GNU readline interface.  Readline
+provides a mechanism to schedule an background process which is
+executed on a regular basis, or in the interval after each keystroke.
+This background processing is represented as the loop on the right.
+In this loop, PanTasks checks on the status of pcontrol and on any
+locally spawned background jobs.  It also checks the list of tasks for
+tasks which are ready to be executed.  The tasks and the background
+jobs are kept in rotating queues.  Every time the loop is processed,
+PanTasks runs through a number of the background jobs and tasks,
+attempting to evaluate as many as possible, but stopping after a
+limited number of milliseconds.  This test stage cycles through the
+tasks and/or jobs in their queue, but stops if it examines all of the
+entries in the queue.  If this testing process runs out of time before
+the entire queue is searched, it leaves the sequence intact for the
+next pass.  The timeout is set to keep the keyboard small enough to
+keep the human interaction from being troublesome.
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.02.ps}
+\caption{\label{TaskLoop} PanTasks Task Check Loop}
+\end{center}
+\end{figure}
+
+Figure~\ref{TaskLoop} shows the interactions performed for each check
+of the list of tasks.  The task timers are examined to see if the
+specific task is ready to be executed.  If so, then the task exec
+macro is executed.  If this macro exit status is false, the loop goes
+to the next task.  If the task exec macro is true, the job command is
+constructed and the job is submitted to the correct location, either
+the controller (pcontrol) or to the queue of local, background jobs.
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.03.ps}
+\caption{\label{JobLoop} PanTasks Job Check Loop}
+\end{center}
+\end{figure}
+
+Figure~\ref{JobLoop} shows the interactions performed for each check
+of a single background job.  The job timer is checked to see if the
+job has timed out.  Next, the job run status is examined to see if the
+job has finished or not.  If the job has finished, the appropriate
+exit macro is executed and the job results are returned to the rest of
+the PanTasks data structures (ie, stderr and stdout queues are filled
+out).  Users should not define exit macros which perform long running
+jobs, or PanTasks will become quite sluggish to keyboard strokes.
+
+PanTasks also checks the current status of pcontrol on each loop.
+In this test, PanTasks requests from pcontrol a list of the jobs which
+have finished.  It then attempts to harvest the finished jobs one by
+one and place the results in the approrpiate locations.  This loop is
+also limited to a fixed amount of time; any pcontrol jobs which remain
+unharvested are left for the next pass by PanTasks.
+
+\subsection{Running the scheduler}
+
+Once a set of tasks has been defined, the scheduler can be
+started. The scheduler will run in the background, at regular
+intervals examining the collection of tasks and jobs. In these
+periods, the scheduler attempts to construct new jobs and checks on
+the status of jobs which may have finished, either locally or on the
+controller. To start the scheduler, give the command run. To stop the
+scheduler, given the command stop. The current status of the
+scheduler, controller, and any jobs which have been spawned are listed
+with the status command.
+
+It is also possible to kill or delete individual jobs by hand with the
+commands kill (jobID) or delete (jobID).  Other features
+
+\subsection{PanTasks Command Summary}
+
+\begin{verbatim}
+ controller                  -- controller commands
+ task                        -- define a schedulable task
+ host                        -- define host machine for a task
+ nmax                        -- define maximum number of jobs for a task
+ trange                      -- define valid/invalid time periods for a task
+ task.exit                   -- define exit macros for a task
+ task.exec                   -- define pre-exec macro for a task
+ command                     -- define executed command for a task
+ periods                     -- define time scales for a task
+ run                         -- run the scheduler
+ stop                        -- stop the scheduler
+ pulse                       -- set the scheduler update period
+ status                      -- get system status
+ kill                        -- kill job
+ delete                      -- delete job
+ verbose                     -- set/toggle verbose mode
+\end{verbatim}
+
+\newpage
+\section{pcontrol : the PanTasks parallel controller}
+
+The IPP uses a group of computers to store and process images and to
+manipulate collections of detections. These computers perform any of a
+large number of analysis stages or other processing tasks without
+significant interprocess communication. It is necessary to have a
+mechanism which initiates computing tasks on the different computers,
+which monitors the tasks as they are executed, which handles the
+output and the errors from these tasks, and which reacts to the
+failure of any of the computing nodes. The system responsible for the
+tasks in the IPP is pcontrol.
+
+\subsection{Host States}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.04.ps}
+\caption{\label{HostStates} PanTasks Host States}
+\end{center}
+\end{figure}
+
+pcontrol maintains a table of available processing computers (hosts)
+and tracks their status. Hosts managed by pcontrol are allowed to be
+in one of several states: \code{off}, \code{down}, \code{idle},
+\code{busy}, and \code{done} (see Figure~\ref{HostStates}). There are
+also two virtual states: \code{new} and \code{delete}.  These states
+have the following meanings:
+
+If the host is \code{off}, it is known to pcontrol, but pcontrol does
+not have an active connection to the machine. Hosts which are
+\code{off} are not available for jobs, and pcontrol does not attempt to
+initiate a connection to them.  A pcontrol user may force a host to
+transition to the \code{off} state with the command host
+\code{off~(hostname)}. (Note that this command will set only one of
+the connections to the named host to \code{off}. If multiple
+connections to a machine have been defined, multiple \code{off}
+commands must be sent).
+
+When pcontrol is told to consider a machine on, the machine is moved
+from the \code{off} state to the \code{down} state. Pcontrol attempts
+to initiate a connection to the host. Connections are made by running
+a remote client on the host, using the specified connection
+method. The connection method may be ssh, rsh, or an equivalent remote
+shell connection. The choice is specified by the \code{COMMAND} Opihi
+variable. The remote connection starts a dedicated remote client which
+must accept the pcontrol client commands and respond
+appropriately. The provided remote client is called {\em pclient},
+though in principal other equivalent programs could be used by setting
+the Opihi variable \code{SHELL}.  This feature more generally allows a
+user to specify a path to the remote client, if it is not in the
+user's path.
+
+If the remote connection is successful, the connected host is moved by
+pcontrol from the \code{down} state to the \code{idle} state. If the
+connection is unsuccessful, pcontrol will try again after a certain
+period of time. If the connection continues to be unsuccessful, the
+retry period is doubled for each successiver connection attempt. If
+the user wants to force pcontrol to retry the connection to a machine
+(if, for example, the timeout is now very long, but the user knows the
+machine's ethernet cable has been re-inserted...), this can be
+achieved with the command host \code{retry (hostname)}. A host which
+is \code{down} is in the limbo state between \code{off} and
+\code{idle}.
+
+Once pcontrol has made a successful connection to the host, the host
+is in the \code{idle} state. At this point, it is ready to accept jobs
+from pcontrol for execution. Pcontrol periodically queries the hosts
+to check that they are still alive. If a host is discovered to be
+unresponsive, and particularly if the remote pipe connection has
+closed, then the machine is moved back to the \code{down} state.
+
+Hosts which are \code{idle} may accept a job from pcontrol. A job
+simply consists of a bare UNIX command, without redirection of
+standard input or standard output. The host will initiate the job, and
+pcontrol will place the host into the \code{busy} state. The remote
+client, pclient, runs the job in the background and will continue to
+accept input from pcontrol. pcontrol will continue to check the status
+of the host, and now also the status of the specific job. As before,
+if the connection breaks, pcontrol will migrate the host to the
+\code{down} state. Any job already initiated on a host which goes down
+will be returned to the pool of unexecuted jobs for later processing,
+so the job will not be lost.
+
+When the job exits, pclient tells pcontrol that the job is completed,
+and specifies the exit status. At this point, pcontrol will move the
+host from \code{busy} to \code{done} state. It will stay in this state
+until pcontrol can determine the ending conditions and reset the
+remote client. pcontrol requests the standard error and standard
+output from the job from pclient. pcontrol stores this data with its
+information about the completed job, and send a reset command to the
+remote client. Once these cleanup tasks are successfully completed,
+pcontrol will move the host to the \code{idle} state, ready for
+further jobs.
+
+Each physical computer may have multiple processors. pcontrol treats
+each processor independently. It is up to the system configuration if
+each computer needs to reserve one of its CPUs to manage background
+tasks or if pcontrol should attempt to send one task per CPU and let
+the operating system handle the I/O load. some of this behavior will
+probably be eventually more intelligent. For example, the commands
+which turn a host on or off should be able to do the same operation to
+all host connections for the same machine name.
+
+A machine may be completely removed from pcontrol's host tables with
+the command host delete (hostname).
+
+\subsection{Jobs}
+
+The pcontrol accepts new jobs with the command \code{job ...}, in
+which the ellipsis represents the command and arguments of a valid
+UNIX command. The commands are run under \code{sh}, and are executed
+in the user's home directory. Users should be wary of the conditions
+under which the remote jobs are run. If the nodes in question all
+cross-mount the same home directories, multiple jobs which interact
+with the same named file may produce unexpected results. The
+controller cannot enforce good behavior on the part of the remote
+jobs; it is the responsibility of the user to ensure that conflicts do
+not arise by, eg, always using unique output file names.
+
+Other issues may arise from the fact that pcontrol may be choosing any
+of the hosts to run the job. Typical failures arise if the user does
+not realize that specific jobs do not behave the same on all machines,
+or if a necessary resource (eg, some input data file) is only
+available or accessible from some of the hosts. It is also the
+responsibility of the task to wait for network lags (ie, NFS delays).
+
+pcontrol gives each task a unique internal identifier (Job ID)
+equivalent to the process ID used in UNIX. When a job is submitted to
+pcontrol, the command echoes back the Job ID. This ID may be used by
+other pcontrol commands to obtain information about or interact with
+the job.  For example, PanTasks uses the Job ID to examine specific
+jobs.
+
+A job may specify a specific host for the task execution. The host
+specified for a job may be required, or desired. In the first case,
+pcontrol, will only run the job on the specified host, waiting until
+it is available before attempting the job. In the second case,
+pcontrol will attempt to send the job to the specified host, but if
+the host is unavailable (how long? what conditions?), pcontrol will
+allow the job to be sent to an alternative host. pcontrol attempts to
+honor the requests for required and desired hosts, giving priority
+first to required-host jobs, then to the desired-host jobs, and
+finally to all other jobs. To specify a host for a job, the following
+commands are used:
+
+\begin{verbatim}
+ job -host (command and arguments...)
+ job +host (command and arguments...)
+\end{verbatim}
+
+
+The first case specifies a desired host, while the second specifies a
+required host. It is also possible to specify the special host name
+anyhost, which is equivalent to not specifying a host at all.
+
+Job priority / urgency levels are not implemented at this time.
+
+I/O vs CPU tasks are not currently distinguished by pcontrol
+
+pcontrol stores the stdout and stderr for each completed job. To
+retrieve these data from these streams, the user issues the commands
+stdout (JobID) and stderr (JobID). The result is a single line
+specifying the number of bytes to expect, followed by a dump of the
+buffers, followed by the prompt. It is the user's responsibility to
+relieve pcontrol of this data load by deleting jobs once they are no
+longer needed. Job deletion is performed with the command delete
+(JobID).
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.05.ps}
+\caption{\label{queues} pcontrol job states.  Transitions labeled Ux
+  are issued by the pcontrol user (including PanTasks).  Transitions
+  labeled Px are initiated by pcontrol.  Transitions labeled Tx are
+  the result of the job completion}
+\end{center}
+\end{figure}
+
+Jobs are moved between the following states by pcontrol (see
+Figure~\ref{JobStates}):
+\begin{itemize}
+\item pending: the job has not yet been executed.
+\item busy: the job is currently being executed.
+\item done: the job has completed, but the stdout/stderr has not been processed by pcontrol.
+\item exit: the job has completed with a valid exit status
+\item crash: the job has completed with a crash status (exit on signal). 
+\end{itemize}
+
+\subsection{Miscellaneous Commands}
+
+It is possible to check the status of a single host or job with the
+user command \code{check}.
+
+pcontrol continuously examines the stack of jobs, adjusting their
+state as needed and extracting their output when it is ready. These
+checks are performed in the background, with pcontrol ready to accept
+further commands from the user in the foreground. These checks are
+performed after every keystroke, and also after an inactivity
+timeout. The interrupt interval defaults to 1 second, but may be
+adjusted with the pulse command, which takes as an argument, the
+number of microseconds for the timeout.
+
+The pcontrol system status may be examined with the command
+status. This provides a dump of the job stacks and the host stacks.
+
+It is possible to list the jobs currently in a specific stack,
+corresponding to the list of jobs with a given state. This is done
+with the command jobstack (stackname). The valid stack names are
+pending, busy, exit, crash, and done. The result is a list of all jobs
+on the specified stack. This is useful to determine quickly which jobs
+have exited or crashed.
+
+A specific job may be killed with the command \code{kill
+(JobID)}. This command is only valid for a job in the busy state. Any
+job in the pending, exit, or crash state may be deleted with the
+\code{delete (JobID)} command. This is necessary to free the memory
+associated with the job and its output streams.  PanTasks
+automatically performs these job harvesting functions.
+
+The command \code{verbose (mode)} turns the verbosity of the pcontrol
+operations on or off.
+
+The pcontrol and Nebulous have related needs for information from the
+combined storage-and-processing nodes regarding which nodes are
+available. Currently, the two systems independently examine the
+hardware to judge the availability.  It is not yet clear if this
+information is best stored in a single location (either pcontrol or
+Nebulous), which provides the information to other systems on demand.
+The current implementation allows the IPP system as a whole to
+distinguih nodes which are available for processing from those that
+are available to serve data as part of Nebulous.
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.06.ps}
+\caption{\label{PControlLoop} PControl Job Monitor Loop}
+\end{center}
+\end{figure}
+
+Figure~\ref{PControlLoop} illustrated the pcontrol job monitor loop.
+This is very similar to the loop in PanTasks.  A background process
+launched by readline during idle periods cycles through the list of
+hosts (children) and migrates jobs to and from them as needed.
+
+\subsection{pcontrol Command Summary}
+
+\begin{verbatim}
+ check                -- get job or host status
+ delete               -- delete job
+ host                 -- add / delete / modify host
+ job                  -- add job
+ jobstack             -- list jobs for a single stack
+ kill                 -- kill job
+ pulse                -- set system pulse
+ status               -- get system status
+ stderr               -- get stderr buffer for job
+ stdout               -- get stdout buffer for job
+ verbose              -- set the verbose mode for job
+\end{verbatim}
+
+\newpage
+\section{pclient}
+
+pclient is the remote process monitor for pcontrol, the parallel
+process controller.
+
+The program pclient is used to support the remote jobs which are run
+on the remote hosts by pcontrol. The concept of pclient is to act as a
+buffer between the job running on the remote host and pcontrol. The
+pcontrol design uses (by default) ssh connections initiated by
+pcontrol to the remote hosts. These connections execute the remote
+program of pclient. The use of a remote login process lets the UNIX
+system take care of the user authentication issues. In this case, the
+recommended practice is to set up ssh to allow the connection to the
+remote host without additional authentication using the appropriate
+authorized keys.  The security is maintained by the security of ssh
+logins to the computers used by PanTask and pcontrol.  
+
+It is convenient to keep a continuous connection to the remote
+hosts. This avoids incurring the overhead of authentication for each
+command which is executed, while keeping a high-quality user
+authentication process in place.
+
+pclient acts as a buffer between pcontrol and the remote background
+process, allowing the continuous connection to remain viable without
+samping pcontrol with output from the jobs.  
+
+\section{Command Summary}
+
+pclient has a very limited command set, as follows:
+
+\begin{verbatim}
+ job           : start the job (UNIX command) in the background.
+ check         : return the current job status
+ status        : return the current job status (?)
+ stdout        : dump the stdout stream accumulated from the job
+                 back to the calling program.
+ stderr        : dump the stderr stream accumulated from the job
+                 back to the calling program.
+ reset         : kill (if needed) the job and reset to accept
+                 another job.
+\end{verbatim}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.85,angle=-90]{pics/pantasks.07.ps}
+\caption{\label{queues} PanTasks queues and MDDB tables}
+\end{center}
+\end{figure}
+
+\appendix
+\pagebreak
+
+\section{Example : Pcopy.pro}
+
+Below is an example script for psched which demonstrates the
+scheduling system. This parallel-copying script implements the
+Pan-STARRS image copying system, which requests images from the summit
+and copies them to the appropriate computer. The first task in the
+script queries an external system for new image names with the
+function new.images. In the case of Pan-STARRS, this would be a
+request from OTIS, the observatory controlling system. The second task
+initiates the individual image copies, with separate CCDs being copied
+to separate computers. This script uses the concept of having specific
+machines assigned to specific CCDs (as Pan-STARRS intends to
+operate). The association is determined by calling the external
+function chip.host, providing the identifier of the chip in
+question. This returns an appropriate host. The copy.image function
+copies the file and also sends a message to the summit system to
+inform it that the image has been successfully copied.
+
+\begin{verbatim}
+verbose on
+ queueinit newImages
+ exec echo 0 > new.last
+ exec cp -f raw.list new.list
+ 
+ controller host add po01
+ controller host add po02
+ controller host add po03
+ controller host add po04
+ 
+ # identify the images ready for copy 
+ # new entries are added to queue newImages
+ # need to compare the new list with the ones already being processed
+ task	       new.images
+   command      new.images
+   host         local
+ 
+   periods      -poll 1
+   periods      -exec 5
+   periods      -timeout 5
+ 
+   # success
+   task.exit    0
+     local i j Nstdout Nimages
+     # compare output with new.image queue
+     # keep only new entries
+     queuesize stdout -var Nstdout
+     for i 0 $Nstdout
+       queuepop stdout -var line
+       queuepush newImages -uniq -key 0 "$line"
+     end
+   end
+ 
+   # locked list
+   task.exit    1
+     echo       "new.images: exec failure"
+     $new.image.failure ++
+   end
+ 
+   # default exit status
+   task.exit    default
+     echo       "new.images: unknown exit status: $EXIT"
+     $new.image.failure ++
+   end
+ 
+   # operation times out?
+   task.exit    timeout
+     echo       "new.images: timeout"
+     $new.image.failure ++
+   end
+ end
+ 
+ # copy new images, sending job to desired host
+ task	       copy.images
+   periods      -poll 0.2
+   periods      -exec 1
+   periods      -timeout 5
+ 
+   task.exec
+     queuesize  newImages -var N
+     if ($N == 0) break
+     # if ($network == 0) break
+     # if ($filesystem == 1) break
+     
+     queuepop newImages -var line
+     list tmp -split $line
+     $filename   = $tmp:0
+     $chip       = $tmp:1
+     $state      = $tmp:2
+     if ($state == new) 
+       # copy this image
+       queuepush newImages -replace -key 0 "$filename $chip run"
+     else
+       # ignore this image
+       queuepush newImages -replace -key 0 "$filename $chip $state"
+       break
+     end
+     # echo $chip
+     $host = `chip.host $chip`
+     # echo $host
+     host $host
+     # echo "starting copy for $filename on $host..."
+     command copy.image $filename $chip
+   end
+ 
+   # can I have access to argc,argv?
+ 
+   # success
+   task.exit    0
+     echo "done copy..."
+     queuepop stdout -var line
+     list tmp -split $line
+     $filename   = $tmp:0
+     $chip       = $tmp:1
+     exec mark.image $filename
+     queuepush newImages -replace -key 0 "$filename $chip copy"
+   end
+ 
+   # default exit status
+   task.exit    default
+     echo       "new.images: unknown exit status: $EXIT"
+     $new.image.failure ++
+   end
+ 
+   # operation times out?
+   task.exit    timeout
+     echo       "new.images: timeout"
+     $new.image.failure ++
+   end
+ end
+ 
+\end{verbatim}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/pilot/ADD/pilotADD.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pilot/ADD/pilotADD.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pilot/ADD/pilotADD.tex	(revision 22322)
@@ -0,0 +1,862 @@
+%%% $Id: pilotADD.tex,v 1.2 2004-04-08 23:49:47 eugene Exp $
+\documentclass[panstarrs]{panstarrs}
+
+\title{Pan-STARRS IPP Pilot Project}
+\subtitle{Algorithm Definition Document}
+\shorttitle{IPP Pilot ADD}
+\project{Pan-STARRS Image Processing Pipeline}
+\author{Eugene Magnier}
+\group{}
+\organization{}
+\version{00}
+\docnumber{PSDC-410-002}
+
+\begin{document}
+\maketitle
+
+%% revisions page
+\RevisionsStart
+DR-1 & 2003/12/08 & First draft \\
+\hline
+DR-2 & 2003/12/15 & Second Draft: include various comments, flesh-out algorithms \\
+\hline
+DR-3 & 2003/12/18 & First Release: generally complete, includes reference to spline\_note.pdf \\
+\RevisionsEnd
+
+\DocumentsInternal
+N/A          & Numerical Recipes Press et al \\ \hline
+N/A          & A Note on Splines U.W. AMATH 352 \\ \hline
+NOST 100-1.1 & Definition of the Flexible Image Transport System (FITS) NOST \\
+\DocumentsExternal
+\DocumentsEnd
+
+%% contents page
+\tableofcontents
+\pagebreak
+
+%% figures page
+\listoffigures
+\pagebreak 
+\pagenumbering{arabic}
+
+%%%%% start of main text
+
+\milsection{Scope}
+
+\milsubsection{Identification}
+
+This document establishes the algorithms to be used by the Pan-STARRS
+Pilot Project.  It makes extensive use of algorithms described by
+Numerical Recipes, without sufficient internal references.  
+
+\milsubsection{Document Overview}
+
+Open Issues and TBDs in this document are marked in bold with
+surrounding square brackets.
+
+\milsection{Algorithms}
+
+\milsubsection{Basic Statistics}
+
+Throughput the IPP, we require a number of basic statistical tests and
+numerical methods to be used.  This document defines these algorithims
+in terms of their mathematical operation.  We define the following
+statistical terms, assuming there is a set of data elements $x_i$.
+
+\milsubsubsection{Mean}
+
+The mean is defined as: \[ \bar{x} = \frac{1}{N} \sum_{i = 1}^{N} x_i \]
+
+\milsubsubsection{Clipped Mean}
+
+The clipped mean is used to determine the mean and $\sigma$ of a
+distribution in the presence of outliers. The algorithm for the
+clipped mean has 2 parameters: $N$, the number of iterations and $k$,
+the multiplying factor of the $\sigma$ to exclude outliers.  Typical values
+for $N$ and $k$ are 3 for both.
+
+\begin{enumerate}
+\item Compute the median of the sample. 
+\item Use this median as the first estimator of the mean of the
+  sample: $\bar{x}$
+\item Repeat the following N times:
+\begin{enumerate}
+  \item Compute the standard deviation of the sample $\sigma$.
+  \item Exclude all sample values $x_{i}$ that have $|x_{i} - \bar{x}| > k \sigma$ 
+  \item Compute the mean of the clipped sample
+\end{enumerate}
+\end{enumerate}
+
+\milsubsubsection{Median}
+
+The median is defined as the value for which 50\% of the data values
+are larger and 50\% are smaller.  This can be calculated in two ways
+depending on the size of the data sample.  For small data samples, the
+values are sorted and the median is given by the value of the middle
+element, if the number of values is odd, and by the average of the two
+middle values if the number of values is even.  For large data
+samples, a histogram of values is generated and the bin which contains
+the 50th percentile value is given as the median value.  For integer
+data, the bin size should be an integer.  For floating point data, the
+bin size should be a fraction of the standard deviation: $\sigma / 4$.
+
+\milsubsubsection{Mode}
+\label{mode}
+
+Formally, the mode is the bin in a data distribution with the maximum
+entries.  Care must be taken to define the bin size; given a
+distribution with a single principal component with a distribution
+with standard deviation $\sigma$, a bin size in the vicinity of
+$\sigma$ will properly sample this component of the distribution.  For
+better precision, a bin size a fraction of $\sigma$ may be used and
+the distribution smoothed by the value of $\sigma$.
+
+To measure the mode, determine the value of $\sigma$ for the data
+sample.  Construct a histogram with bins of size $\sigma / 4$ and
+smooth the distribution with a Gaussian of standard deviation $\sigma$
+(see section~\ref{smooth}).  Find the peak bin in the resulting
+histogram: the coordinate of this bin is the mode of the distribution.
+
+\milsubsubsection{Standard Deviation}
+\label{rms}
+
+The standard deviation of the sample is given by:
+
+\[ \sigma = \sqrt{\sum_{i = 1}^N \frac{(x_i - \bar{x})^2}{N - 1}} \]
+
+To minimize the numerical rounding error, this should be calculated
+numerically as:
+
+\[ \sigma = \sqrt{\frac{1}{N - 1} [ \sum_{i = 1}^{N} (x_i - \bar{x})^2 - \frac{1}{N} (\sum_{i = 1}^{N} (x_i - \bar{x}))^2 ]} \]
+
+\milsubsubsection{Chi-Square}
+\label{chisq}
+
+The chi-square is a test of the goodness-of-fit of a model to a set of
+data.  Given a set of $N$ data points $y_i$ with errors $\sigma_i$,
+dependent on free parameters $x_j$, and a model $y (x_j)$ describing
+the relationship between the parameters and the data, the chi-square
+($\chi^2$) is defined as:
+
+\[ \chi^2 = \sum_{i = 1}^N \frac{(y_i - y(x_j))^2}{\sigma^2_i} \]
+
+The reduced chi-square is defined as:
+
+\[ \chi^2_\nu = \frac{\chi^2}{N_{dof}} \]
+where $N_{dof}$, the number of degrees of freedom, is defined as $N -
+N_{par}$ where $N_{par}$ is the number of free parameters $x_j$.
+
+\tbd{Need to point out the purpose of the chi-square as a test of the
+model, and define the confidence limits on the resulting model
+parameters.}
+
+\milsubsection{LU Decomposition}
+\label{LUdecomp}
+
+The LU Decomposition method shall be used to solve matrix equations of
+the form:
+\[ \sum_{j=1}^{N} A_{i,j} \times x_j = B_j \]
+
+We wish to decompose the matrix $A$ with elements $a_{ij}$ into
+diagonal matrices that satisfy the relationship $A = L U$ where $L$ is
+a lower-diagonal matrix of the form:
+\[ L = \left(
+\begin{matrix}
+\alpha_{11} & 0           & 0           & 0 \\
+\alpha_{21} & \alpha_{22} & 0           & 0 \\
+\alpha_{31} & \alpha_{32} & \alpha_{33} & 0 \\
+\alpha_{41} & \alpha_{42} & \alpha_{43} & \alpha_{44} \\
+\end{matrix} \right)
+\]
+%
+and $U$ is an upper-diagonal matrix of the form:
+\[ U = \left(
+\begin{matrix}
+\beta_{11} & \beta_{12} & \beta_{13} & \beta_{14} \\
+0          & \beta_{22} & \beta_{23} & \beta_{24} \\
+0          & 0          & \beta_{33} & \beta_{34} \\
+0          & 0          & 0          & \beta_{44} \\
+\end{matrix} \right)
+\]
+%
+We can find the values of $\alpha_{ij}$ and $\beta_{ij}$ by following
+this procedure.  First, $\alpha_{ii} = 1$.  For all values of $j = 1,
+2, \dots, N$, follow the next steps: For each value of $i = 1, 2,
+\dots, j$, solve for $\beta_{ij}$ using the relationship:
+\[ \beta_{ij} = a_{ij} - \sum_{k=1}^{i-1} \alpha_{ik}\beta_{kj} \]
+For the values of $i = j+1, j+2, \dots, N$, solve for the values of
+$\alpha_{ij}$ with the following:
+%
+\[ \alpha_{ij} = \frac{1}{\beta_{jj}} \left( a_{ij} - \sum_{k=1}^{j-1} \alpha_{ik}\beta_{kj} \right) \]
+%
+At this point, use the values of $\alpha_{ij}$ and $\beta_{ij}$ to
+solve for the vector $x_i$ by first solving the equation $L y = B$:
+\[ y_1 = \frac{b_1}{\alpha_{11}}\]
+\[ y_i = \frac{1}{\alpha_{ii}}\left(b_i - \sum_{j=1}^{i-1} \alpha_{ij} y_i\right) \]
+% 
+The values of $y$ may be used to solve for $x_i$ using the
+relationship $U x = b$:
+\[ x_N = \frac{y_N}{\beta_{NN}}\]
+\[ x_i = \frac{1}{\beta_{ii}}\left(y_i - \sum_{j=i+1}^N \beta_{ij} y_ij\right) \]
+
+\milsubsection{Function Minimization}
+
+\milsubsubsection{General Polynomial Fitting}
+
+Given a set of data values $y_i$ with errors $\sigma_i$, related to
+independent data values $x_i$, we would like to fit a polynomial model
+of $N_{par}$ free parameters of the form $y = \sum_{j=0}^{N_{par}}
+\alpha_j x^j$.  The model is determined by minimizing the value of
+$\chi^2$ as defined above (\ref{chisq}), and the minimization is
+determined by solving for the set of parameters $\alpha_j$ for which
+the partial derivatives of the $\chi^2$ with respect to those
+parameters are zero.  The partial derivatives are
+
+\[ \frac{\partial \chi^2}{\partial \alpha_k} = -2 \sum_i (y_i - \sum_j x_i^j \alpha_j) \frac{x_i^k}{\sigma_i^2} \]
+
+Setting the derivatives to zero, this may be reduced to the following
+matrix equation:
+
+\[ \sum_j \alpha_j \sum_i \frac{x_i^k x_i^j}{\sigma_i^2} = \sum_i \frac{x_i^k y_i}{\sigma_i^2} \]
+
+This matrix equation may be solved with LU Decomposition
+(section~\ref{LUdecomp}).
+
+\milsubsubsection{Non-linear Fitting: Levenberg-Marquardt Method}
+
+For models in which the system of equations defined by the partial
+derivatives cannot be solved with the linear technique, other options
+are necessary.  The Levenberg-Marquardt Method (LMM; see Numerical
+Recipes) may be used for these situations.  In LMM, we make a guess at
+the input parameters, measure the $\chi^2$, vary the parameters by a
+particular choice based on the gradient, measure the $\chi^2$ again,
+and adjust the parameters and the parameter varient based on the
+results.
+
+Given a set of $N$ data values $y_i$ with errors $\sigma_i$, dependent
+on values $x_i$, we would like to find the parameters $a_k$ of the
+function $f(x_i; a_k)$ which minimize the $\chi^2$, defined in the
+usual manner (\ref{chisq}).  We start with a set of parameter guesses,
+$a_k$.  We calculate the gradient $\beta_k$ and the Hessian matrix
+$\alpha_{j,k}$ at this parameter selection as follows:
+
+\[ \beta_k = \sum_{i=1}^{N} \frac{\partial f(x_i)}{\partial a_k} \frac{(y_i - f(x_i))}{\sigma_i^2} \] 
+\[ \alpha_{j,k} = \sum_{i=1}^{N} \frac{\partial f(x_i)}{\partial a_k} \frac{\partial f(x_i)}{\partial a_j} \frac{1}{\sigma_i^2} \] 
+%
+We now define the new parameter guess for $a_k$ based on the gradient
+and Hessian by defining $A_{j,k}$ as a variant on $\alpha_{j,k}$ as
+follows:
+
+\[ A_{j,k} = \alpha_{j,k} ~ (j \ne k) \]
+\[ A_{j,k} = (1 + \lambda) \alpha_{j,k} ~ (j = k) \]
+%
+and solve the system of equations represented by:
+\[ A_{j,k} \alpha^\prime_k = \beta_j \]
+%
+where $\alpha^\prime_k$ represents our new attempt at a parameter
+guess. We use this parameter set to calculate $\chi^2$.  If the new
+value of $\chi^2$ is lower than the previous guess, we accept this new
+set of parameters and decrease $\lambda$ by a factor of 10, otherwise
+we keep the old set, and increase the value of $\lambda$ by a factor
+of 10.  We repeat this process until the value of the reduced $\chi^2$
+changes by much less than 1.0.  The resulting values of $a_k$ are the
+best-fit parameters for the system.  If the errors are normally
+distributed, the formal errors on the parameters are then calculated
+by setting $\lambda = 0$ and calculating the covarience matrix
+$C_{i,j}$, the inverse of the matrix $\alpha_{j,k}$.  The independent
+68.3\% confidence limit on parameter $a_k$ is then $\sqrt{C_{k,k}}$.
+Confidence contours for sets of parameters may be defined as well by
+the function $\Delta = \delta\bar{a} P_{j,k}^{-1} \delta\bar{a}$ where
+$P_{j,k}$ is the projected matrix of $C_{j,k}$, ie those rows and
+columns of $C_{j,k}$ associated with the parameters of interest, the
+vector $\delta\bar{a}$.  The value of $\Delta$ is given by the table
+below for specific confidence limits and numbers of parameters.  
+Note that it is necessary to be able to calculate both the function as
+well as its derivative for any combination of parameters and dependent
+variables.
+
+\begin{center}
+\begin{tabular}{|l|r|r|r|}
+\hline
+{\bf P} & \multicolumn{3}{c|}{\bf $N_{par}$} \\
+        & 1    & 2    & 3    \\
+\hline
+68.3\%  & 1.00 & 2.30 & 3.53 \\
+95.4\%  & 4.00 & 6.17 & 8.02 \\
+99.73\% & 9.00 & 11.8 & 14.2 \\
+\hline
+\end{tabular}
+\end{center}
+
+\milsubsection{Smoothing: Boxcar and Gaussian}
+\label{smooth}
+
+Smoothing may occasionally be perfomed on data.  We present the
+algorithms for two typical versions: boxcar and Gaussian smoothing.
+In both smoothing techniques, given a series of data values $f_i$, the
+smoothed values $g_i$ are determined by calculating a linear
+combination based on the input data point and its nearest $2N$
+neighbors in the form:
+
+\[ g_i = \sum_{n=-N}^N c_n f_i \]
+%
+where the values of $c_n$ determine the filter type.  For boxcar
+smoothing, the values $c_n$ are constant, and must be equal to $1/(2N
++ 1)$ to maintain the zeroth moment of the data (care must be taken at
+the ends of the data range to reduce the value of $c_n$ as fewer input
+data points may be used).  For Gaussian smoothing, the crucial
+parameter is $\sigma$, the standard deviation.  The
+value of $N$ should be chosen to be large enough, $N = 5\sigma$, and
+the values of $c_n$ are then just the Gaussian curve:
+
+\[ c_n = \frac{e^{\frac{-n^2}{2\sigma^2}}}{\sqrt{2\pi\sigma^2}} \]
+
+\milsubsection{Robust Statistics Estimations}
+\label{robust}
+
+Given a set of data values $x_i$ with a primary probability
+distribution described by a mean $\bar{x}$ and standard deviation
+$\sigma$ and a much smaller number of outliers with very different
+probability distribution, it is necessary to make estimates of the
+above statistics using a subset of the data to reject the outliers. 
+
+To make a robust measurement of $\sigma$, sort the data values, and
+determine $\sigma_r = (U_{\frac{1}{4}} - L_{\frac{1}{4}}) / 1.34$,
+where $U_{\frac{1}{4}}$ and $L_{\frac{1}{4}}$ are the upper and lower
+quartile values of the distribution (above and below which are found
+25\% of the data points).  Construct a histogram of the data with bins
+of width $\sigma_r / 4$ and smooth the distribution with a Gaussian of
+width $\sigma$.  Find the peak (mode, $\mu$) and fit a Gaussian to the
+histogram portion centered on the mode containing 25\% of the data
+values.  The resulting value $\mu$ is the robust mean and $\sigma$ is
+the robust standard deviation. 
+
+\milsubsection{Splines}
+
+Data interpolation and smoothing may be performed by fitting a spline
+to data points and using the spline function to determine intermediate
+values.  It is necessary to specify the boundary condition choice when
+performing a spline fit.  A very useful and readable formulation for
+three boundary conditions of interest (Natural, Clamped, Not-a-knot)
+is provided by a note from a U. Washington math course note sheet, ``A
+Note On Cubic Splines'' (AMATH 352), included.  \tbd{write this out in
+full later}.
+
+\milsubsection{Data Formats and Conversion}
+
+Image data values are represented in the FITS image in one of several
+possible ways described by the header keyword BITPIX (see FITS
+Standard Document NOST 100-1.1).  The value of BITPIX may be one of:
+
+\begin{center}
+\begin{tabular}{r|r}
+   8 & short integer \\
+  16 & medium integer \\
+  32 & long integer \\
+ -32 & single-precision floating point \\
+ -64 & double-precision floating point \\
+\end{tabular}
+\end{center}
+
+where the absolute value represents the number of bits in the data
+representation and the sign is set to positive for integer
+representations and negative for floating point representations.  The
+byte order is high-significance bytes first (big-endian).  In the
+integer representations, the data array value is represented as a
+signed integer with the number of bits specified by BITPIX.  The
+floating point representations use the IEEE-754 Floating Point
+representation of the array values.  
+
+In addition to the coding described above, the pixel data value may be
+scaled relative to the file data value.  The scaling is determined by
+the FITS header keywords BZERO and BSCALE which define floating point
+coeffiecients of the following equation:
+
+\[ physical = array \times BSCALE + BZERO \] 
+
+where $physical$ represents the actual data value, while $array$
+represents the data value of the bytes in the FITS file.
+
+For the purpose of computation, an internal floating-point
+representation shall be used.  However, data may be converted between
+these representations when written to disk.  Some details of this
+conversion are important.
+
+First, truncation to an integer representation introduces sharp
+transitions in the data which are visible after smoothing.  To avoid
+these artificial transitions, the conversion to an integer value shall
+randomized by 1/2 of that integer value.  Therefore, conversion of a
+physical value to an integer representation using BSCALE and BZERO
+shall use the following formula:
+
+\[ array = (physical - BZERO) / BSCALE + rand(0.5) \]
+
+where $physical$ represents the actual data value, while $array$
+represents the data value of the bytes in the FITS file.
+
+Second, the choice of BZERO and BSCALE shall be set to protect the
+special value of 0.0.  Since the $array$ data values experience
+integer truncation, arbitrary values of BZERO and BSCALE will force an
+initial physical value of 0.0 to have an actual physical value in the
+range -BSCALE / 2 to +BSCALE / 2.  Since 0.0 is a special value,
+BSCALE and BZERO values shall be chosen to force the physical value of
+0.0 to be represented by a number which is in a range of $\pm BSCALE /
+1000$.  This may be achieved by slightly adjusting BSCALE and BZERO
+relative to some initial choice.  The initial choice defines the
+minimum and maximum physical data value ($MIN_p$, $MAX_p$) corresponding
+to the minimum and maximum array data values ($MIN_a$, $MAX_a$) , as
+well as the array data value corresponding to the physical data value of
+0.0 ($ZERO_a$):
+
+\[ MIN_p = MIN_a * BSCALE_i + BZERO_i \]
+\[ MAX_p = MAX_a * BSCALE_i + BZERO_i \]
+\[ 0.0 = ZERO_a * BSCALE_i + BZERO_i \]
+
+For an arbitrary choice of $BSCALE_i$ and $BZERO_i$, the value
+$ZERO_a$ will not be an integer value.  We require the MAX physical data
+value to be maintained, therefore we choose new values of BSCALE and
+BZERO based on the initial values to maintain the value of MAX and to
+force $ZERO_a$ to be the closest integer value:
+
+\[ ZERO_{a,i} = -BZERO_i / BSCALE_i \]
+\[ ZERO_{a,o} = int (-BZERO_i / BSCALE_i) \]
+\[ BSCALE_o = MAX_p / (MAX_a - ZERO_{a,o}) \]
+\[ BZERO_o = - ZERO_{a,o} * MAX_p / (MAX_a - ZERO_{a,o}) \]
+
+With this representation, the physical value corresponding to 0.0 will
+have a value in the range $\pm P$ where $P$ is the precission of the
+keyword representation of BSCALE.  BSCALE shall have a precission of 5
+digits.
+
+\milsubsection{Bias Subtractions}
+
+The subtraction of a bias may be performed in several ways.  This
+document defines the algorithms to be used for these different bias
+subtraction options.  
+
+\milsubsubsection{Overscan Region Definition}
+
+The bias for an image may be determined based on the data values in
+the overscan region.  The overscan region may be defined by the FITS
+header keyword BIASSEC as follows:
+
+\begin{verbatim}
+BIASSEC = '[Xs:Xe,Ys:Ye]'
+\end{verbatim}
+
+where the pixel coordinates are taken with respect to an origin pixel
+at (1,1).  The values {\tt Xs, Xe, Ys, Ye} define a rectangular
+overscan region with {\tt Xs} the first pixel in the x direction, {\tt
+Xe} the last pixel in the x direction, {\tt Ys} the first in the y
+direction and {\tt Ye} the last in the y direction.  Note that the
+single quotes in the definiton of {\tt BIASSEC} are required by the
+FITS standard for string-type keyword values.  
+
+\milsubsubsection{Constant Value Option}
+
+The bias may be represented by a single value determined from the
+entire overscan region pixel values.  These must include:
+
+\begin{itemize}
+\item {\bf mean} of the overscan region
+\item {\bf clipped mean} of the overscan region
+\item {\bf median} of the overscan region
+\end{itemize}
+
+\milsubsubsection{One-Dimensional Function Option}
+
+The overscan region encompases an area which is long in one direction
+compared to the other and which overlaps all data values in that
+direction.  The bias may be represented as a one-dimensional function
+which varies as a function of the pixel coordinate in the long
+direction of the overscan region.  The options for this function
+include:
+
+\begin{itemize}
+\item spline
+\item polynomial
+\end{itemize}
+
+The data values used to determine this function are derived from the
+data in the short dimension of the overscan region, from which a
+single value is derived using one of the estimators:
+
+\begin{itemize}
+\item mean
+\item clipped mean
+\item median
+\end{itemize}
+
+The resulting vector may be further filtered to exclude points which
+had extremely high value of $\sigma$ ($> N \times READ\_NOISE$) or
+which were extreme outliers from the distribution.
+
+\milsubsection{Image Trimming}
+
+A science or calibration image may be trimmed to remove the portions
+of the image which do not contain valid imaging data.  The portion of
+the image which should be retained may be described by the DATASEC
+header keyword as follows:
+
+\begin{verbatim}
+DATASEC = '[Xs:Xe,Ys:Ye]'
+\end{verbatim}
+
+where the pixel coordinates are taken with respect to an origin pixel
+at (1,1).  The values {\tt Xs, Xe, Ys, Ye} define a rectangular
+overscan region with {\tt Xs} the first pixel in the x direction, {\tt
+Xe} the last pixel in the x direction, {\tt Ys} the first in the y
+direction and {\tt Ye} the last in the y direction.  Note that the
+single quotes in the definiton of {\tt DATASEC} are required by the
+FITS standard for string-type keyword values.  
+
+\milsubsection{Flat-field Correction}
+
+The science image should be divided by the flat-field image.  If any
+of the flat-field image pixels have a value less than or equal to zero, the output pixel
+should be set to the bad pixel value, BADPIX.  
+
+\milsubsection{Bad Pixel Masking}
+
+The science image should be left at the original value if the
+equivalent mask pixel value is 0.  Those pixels which have a non-zero
+value in the mask image should be set to the bad pixel value, BADPIX,
+in the science image.
+
+\milsubsection{Sky Subtraction}
+
+The ``sky'', {\it i.e.} the background of the images will be modeled
+for the Pilot project with a two--componenent model: a low spatial
+frequency component, modelled with a 4$^{th}$ order polynomial, and a
+high spatial frequency component corresponding to a fringe image.
+
+Let $(x,y)$ be the coordinates of a pixel in a given cell. The value of
+the background $B(x,y)$ in this pixel is modelized as:
+
+\begin{multline}
+B(x,y)  =   \alpha \times F_{i}(x,y) + C_{0} +C_{1} x + C_{2} y + C_{3} x^2 +
+C_{4} x y + C_{5} y^2 + C_{6} x^3 + C_{7} x^{2} y + C_{8} x y^{y} +
+C_{9} y^3 \\
+        + C_{10} x^4 + C_{11} x^3  y  + C_{12} x^2 y^2 + C_{13} x
+y^{3} +C_{14} y^4 \\ \label{eq:background}
+\end{multline}
+
+where $F_{i}(x,y)$ is the value of the pixel $(x,y)$ in the fringe
+image $F_{i}$. The free parameters of this model are $\alpha$ and
+$C_i$.  There will be a library of $N$ fringe images generated for
+each filter.
+
+In order to recover the background emission and subtract it, the Pilot
+IPP must first make a crude object detection in each cell, as
+described below, in order to perform the sky determination using empty
+sky pixels, then perform a $\chi\text{-squared}$ minimization to
+determine the coefficients of each function.
+
+The algorithm reads as follow:
+
+\begin{enumerate}
+\item make a working copy of the cell image and mask.
+\item measure robust mode and noise ($\sigma$) in the working copy.
+\item mask pixels that have a value greater than the mode plus 5 times
+  the $\sigma$.
+\item using the non masked pixels only, determine the coefficients of
+  the sky model. If there is not enough pixels to perform a sky
+  determination, issue an error.
+\item construct a sky image using the determined coefficients,
+including for those pixels that have been masked in step 3.
+\item subtract this sky image from the original input image.
+\end{enumerate}
+
+Each of the steps are detailed below.
+
+\milsubsubsection{Copy Cell image and Mask}
+
+This is straightforward.
+
+\milsubsubsection{Measure mode and noise.}
+
+The crucial step here is to recognize whether the image is being
+taken on an empty field.
+
+The mode is to be measured using the robust technique describe in
+section~\ref{robust}, using all the non masked pixels of the cell. The
+standard deviation ($\sigma$) is to be measured using the robust
+technique useful in the presence of outliers, described in
+section~\ref{robust}. The $\sigma$ is to be compared to the expected
+$\sigma$, given the mode, the gain of the cell, its readout noise, and
+the subtracted bias value. All these informations are present in the
+metadata. For reminder, the expected $\sigma$ is:
+\begin{equation} 
+\sigma \simeq g \times \sqrt{(O+M/F)/g + (R/g)^{2}}
+\end{equation}
+where $g$ is the detector gain in $e^{-}/DN$, $O$ is the average
+overscan value in DN, M is the mode in DN, F is the flat-field average
+value, $R$ is the readout noise in DN.
+
+Two cases may arise:
+\begin{itemize}
+
+\item if the measured $\sigma$ is more than 3 times the expected
+$\sigma$, this is the sign that the field is crowded (very few pixels
+see the actual sky).  Such an image should be skipped for the pilot
+project.
+\item if the measured $\sigma$ is not more than 3 times the expected
+value, this is the sign that the field is uncrowded and the sky may be
+fitted.
+\end{itemize}
+
+\milsubsubsection{Crude source detection}
+
+Once the mode and $\sigma$ have been determined, a crude object detection
+is performed by checking for each pixel in the cell:
+
+\begin{equation}
+P(x,y) 
+\begin{cases}
+> M + 5 \times \sigma  \longrightarrow \text{Mask pixel}\\
+\le  M + 5 \times \sigma \longrightarrow \text{Do nothing} \\
+\end{cases}
+\end{equation}
+
+where P(x,y) is the value of the image at position (x,y). Note that
+the masking is done on the woking copy of the image. 
+
+\milsubsubsection{Fit the background values}
+
+The fitting method is a standard $\chi-$squared fit. The background B
+is a function of 17 parameters: $B_(\alpha, i, C_{0}, \dots, C_{14})(x,y)$.
+
+The $\chi-$square statistic (see section~\ref{chisq}) with:
+\begin{equation}\label{eq:chisq}
+\chi^{2} 
+ = \sum_{x,y} \left(\frac{P(x,y) - B(x,y)}{\sigma}\right)^{2} 
+ = \frac{1}{\sigma^{2}} \sum_{x,y} \left(P(x,y) - B(x,y)\right)^{2} 
+\end{equation}
+%
+where $\sum_{x,y}$ is a sum over all unmasked pixels in the cell and
+where we use the fact that the $\sigma$ is roughly constant accross the
+cell.
+
+The fit is obtained by finding the  minimum of $\chi^{2}$ in the
+parameter space of $B$. At the minimum, we have:
+\begin{equation}
+\frac{\partial \chi^{2}}{\partial p} = 0
+\end{equation}
+
+where $p$ is one of the 17 parameters. Since the parameter $i$ is a
+discrete parameter (it is the $i^{th}$ fringe frame in the library),
+we cannot compute $\partial \chi^{2} / \partial i$. Instead we loop
+over each fringe image in the library, then form the system of 16
+equations with 16 unknown ($\alpha, C_{0}, \dots, C_{14}$):
+
+\begin{equation}
+\begin{cases}
+\frac{\partial \chi^{2}}{\partial \alpha} = \sum_{x,y} \left(P(x,y) - B(x,y)\right) F_{i}(x,y) = 0 \\
+\frac{\partial \chi^{2}}{\partial C_{0} } = \sum_{x,y} \left(P(x,y) - B(x,y)\right)            = 0 \\
+\frac{\partial \chi^{2}}{\partial C_{1} } = \sum_{x,y} \left(P(x,y) - B(x,y)\right) x          = 0 \\
+\dots \\
+\frac{\partial \chi^{2}}{\partial C_{14}} = \sum_{x,y} \left(P(x,y) - B(x,y)\right) y^4        = 0 \\
+\end{cases}
+\end{equation}
+Explicitly showing the dependence on the parameters, the systems reads:
+
+\begin{equation}
+\setcounter{MaxMatrixCols}{11}
+\begin{matrix}
+\alpha & \sum F_i^2 & + C_0 & \sum F_i & + C_1 & \sum x F_i & + \dots & + C_{14} & \sum y^4 F_i & = & \sum F_i P \\
+\alpha & \sum F_i   & + C_0 &          & + C_1 & \sum x     & + \dots & + C_{14} & \sum y^4     & = & 0 \\
+\alpha & \sum x F_i & + C_0 & \sum x   & + C_1 & \sum x^2   & + \dots & + C_{14} & \sum xy^4    & = & \sum x P \\
+\alpha & \sum y F_i & + C_0 & \sum y   & + C_1 & \sum x y   & + \dots & + C_{14} & \sum  y^5    & = & \sum y P \\
+\vdots &            &\vdots &          &\vdots &            &  \ddots &  \vdots  &              &   & \dots  \\
+\alpha & \sum y^4 F_i & + C_0 &\sum y^4  & + C_1& \sum y^4 x & +\dots & + C_{14} & \sum  y^7    & = & \sum y^4 P \\
+\end{matrix}
+\end{equation}
+
+This system can be solved by LU Decomposition (\ref{LUdecomp}).
+
+Once a solution has been obtained for $(\alpha, C_{0}, C_{1} , \dots ,
+C_{14})$, the $\chi^2$ value is computed, using
+equation~\ref{eq:chisq}. The value of $i$ (i.e. the library template)
+is the one that leads to the lowest $\chi^2$. The goodness of the fit
+should be saved in the metadata. Values of $\chi^2$ that are too large
+are indicative of problems during the fit and warnings must be issued.
+
+If some parameters of the fit are consistant with 0 (e.g. for example
+all degree 3 coefficients compatible with 0), the fit should be
+attempted again without these parameters (e.g.  a polynomial of order
+2 should be fitted), and the goodness of the fit ($\chi^{2}$) should
+be checked against the previous estimate.
+
+\milsubsubsection{Sky image construction}
+
+The critical point here is to reconstruct the sky also behind the
+objects that have been masked in step 3. The sky image $B(x,y)$ is
+reconstructed using equation~\ref{eq:background}, for all the pixels
+in the cell that are not masked in the original input image.
+
+\milsubsubsection{Subtract the sky image}
+
+This is straightforward. Masked pixels in the input image must remain
+masked with the {\tt BADPIX} value.
+
+\milsubsection{Object Detection}
+
+The goal here is to generate a list (catalog) of objects detected as peaks of a
+Gaussian smoothed version of the (assumed to be
+calibrated, masked, and sky-subtracted) source image $f[i_y][i_x]$.
+
+\noindent
+The result shall contain
+
+\begin{itemize}
+\item Locations of the peaks ${\bf r} = (x,y)$ (accurate to sub-pixel
+precision)
+\item The smoothed peak height $f_s$.
+\item An estimate of the `significance' of the peaks $\nu$, defined
+as the ratio of the peak height to the rms value of a smoothed Gaussian white-noise
+image in which the rms pixel value is the same as that estimated for the sky.
+\item The count of masked pixels whose centres lie within a distance $r_{\rm max}$
+of the position ${\bf r}$.
+\item An estimate of the curvature matrix (the second spatial derivatives of
+the smoothed image at the peak).
+\end{itemize}
+
+\noindent
+The input data are:
+
+\begin{itemize}
+\item The rectangular source image $f[i_y][i_x]$ and its dimensions $N_x$, $N_y$.
+\item An associated bad pixel mask $m[i_y][i_x]$.
+\item The dimensions $M_x$, $M_y$ of the zero-padded image on which the FFT operations will be applied.
+\item The Gaussian smoothing scale $r_g$ (in pixels).
+\item The estimated rms sky noise $\sigma$.
+\item The radius $r_{\rm max}$.
+\item The minimum significance threshold $\nu_{\rm min}$.
+\end{itemize}
+
+\noindent
+The main steps are:
+
+\begin{itemize}
+\item Compute the smoothed sky-noise variance $\sigma_s^2$.
+\item Set masked pixels to zero (if not already so).
+\item Pad with zeros.
+\item Compute the complex FFT of the (real) masked and padded image.
+\item Multiply by the Gaussian transfer function $\exp(- k^2 r_g^2 / 2)$.
+\item Inverse transform to obtain the smoothed image $f_s[i_y][i_x]$.
+\item Generate a list of pixels for which $f_s$ exceeds those of the 8 nearest neighbours.
+\item Compute the significance $\nu = f_s / \sigma_s$ .
+\item For each such pixel with $\nu > \nu_{\rm min}$, 
+compute a fractional pixel position correction $d{\bf r}$ using the nearby values.
+\item Compute the count of masked pixels $n_{\rm masked}$ within distance $r_{\rm max}$ of the peak location.
+\end{itemize}
+
+\milsubsubsection{Smoothed Sky Noise Variance}
+
+The smoothed sky-noise rms is to be estimated as
+\begin{equation}
+\sigma_s = \sigma / (2 \sqrt{\pi} r_g)
+\end{equation}
+
+\milsubsubsection{Masking and Padding}
+
+Straightforward.
+
+\milsubsubsection{FFT}
+
+The FFT algorithm should take advantage of the symmetry of the FT of a real
+function: $\tilde f({\bf k}) = \tilde f^\star (-{\bf k})$.
+
+\milsubsubsection{Transfer Function}
+
+The values of the FFT $\tilde f_s$ are defined on a grid with indices $i_x$, $j_x$.
+The spacing of these pixels in $k$-space is $dk_x = 2 \pi / M_x$ and
+$dk_y = 2 \pi / M_y$, so ${\bf k} = (k_x, k_y) = (i_x dk_x, i_y dk_y)$
+from which $k^2 = k_x^2 + k_y^2$.
+
+\milsubsubsection{Fractional Pixel Location}
+
+The integer position ${\bf R}$ is defined to be the location of the
+center of the peak pixel, referred to an origin which is the lower left
+corner of the lower left pixel of the source image.  Note that
+this is not the same as the coordinates in the FITS standard which is perverse,
+and places the spatial origin at (-1/2, -1/2) relative to the origin
+used here.
+
+To obtain the shift in the $x$-coordinate, for a peak pixel with 
+indices $(i, j)$ we define
+\begin{equation}
+F' = (f_s[i+1][j] - f_s[i-1][j]) / 2
+\end{equation}
+and
+\begin{equation}
+F'' = f_s[i+1][j] - 2 f_s[i][j] + f_s[i+1][j]
+\end{equation}
+which are respectively estimates of the gradient and second derivative of the
+smoothed field at the peak.  The corrected position is then
+\begin{equation}
+r_x = R_x - F' / F''
+\end{equation}
+and similarly for the $y$-coordinate.
+Care should be taken to deal with the pathological cases
+where $F''$ is extremely small (in such cases, no sub-pixel
+correction should be applied).
+
+\milsubsubsection{Curvature Matrix}
+
+For a peak at location $(i,j)$ the curvature matrix components are defined as:
+\begin{equation}
+\begin{array}{c}
+f_{sxx} = 2 f_s[i][j] - f_s[i+1][j] - f_s[i-1][j] \cr
+f_{syy} = 2 f_s[i][j] - f_s[i][j+1] - f_s[i][j-1] \cr
+f_{sxy} = f_{syx} = (f_s[i+1][j+1] - f_s[i+1][j-1] - f_s[i-1][j+1] + f_s[i-1][j-1]) / 4
+\end{array}
+\end{equation}
+Together with the peak height $f_s$ and significance these provide estimates of the
+position covariance matrix and also can be used as a descriminator for
+objects which are suitable for astrometry.
+
+% \milsection{Appendices}
+
+\bibliographystyle{plain}
+\bibliography{panstarrs}
+\end{document}
+
+%%%% Spline Description from Robert Lupton %%%%
+Fitting a cubic spline through a set of points ($x_i, y_i$) is well
+known to be a problem that reduces to a tri-diagonal matrix, and
+which can thus be solved in linear time. There are not quite enough
+constraints to make the solution unique, so two extra parameters
+must be specified.
+
+One approach is to specify $d^2y/dx^2$ at the ends of the interval.
+The special choice "$d^2y/dx^2 = 0$" at each end is called a "Natural
+Cubic Spline" and is in general not a good idea, since if $y_i$
+actually have some curvature at the boundaries, then the resulting
+spline approximation will "ring" for the first few gridpoints. E.g. if
+there are 9 points (x,y) in the interval $0,\pi$, then the natural
+spline fit to $\sin(x)$ [with zero-second derivatives at the
+boundaries] has a maximum error of about 6e-5; the fit to $\cos(x)$
+[which has non-zero second derivatives at the boundaries] is about
+8e-3.
+
+A better set of boundary conditions are the so-called "not-a-knot"
+conditions.  In this case, the second derivative is chosen so that the
+spline fitted to the points (i=0, 2, 3, ...) passes through the point
+($x_1, y_1$) [and an equivalent condition at the other boundary].
+
+Another problem with splines is their response to discontinuities in
+the function being fit; the spline function is forced to overshoot,
+and to "ring" for a few points on each side of the boundary. For
+example, a step discontinuity of height 1 at pi/2 with the previous
+9-point sampling produces a maximum overshoot of 0.11.
+
+This can be controlled by the use of `taut splines', which introduces
+extra knots as needed to control such overshooting.
+
+Another useful variation on a cubic spline is a least-squares spline,
+in which case the spline is not required to go through all of the
+points exactly.  In this case, the routine returns a set of knots
+($x_i,y_i$) consistent with the data in a least-squares way.
+
+%%%
Index: /tags/sj_tags/sj_root_20080929/doc/pilot/engine/PSDC-400-004-00.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pilot/engine/PSDC-400-004-00.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pilot/engine/PSDC-400-004-00.tex	(revision 22322)
@@ -0,0 +1,499 @@
+\documentclass[panstarrs]{panstarrs}
+\newcommand\note[1]{{\bf #1}}
+\newcommand\tbd[1]{{\bf [#1]}}
+
+\title{Proposal for the Pilot Project Engine / Architecture}
+\shorttitle{IPP.c Architecture Proposal}
+\project{Pan-STARRS Pilot Project}
+\author{Eugene Magnier}
+\group{}
+\organization{}
+\docnumber{PSDC-400-004-00}
+
+\fancyhead{}
+\fancyhead[L]{\theshorttitle}
+\fancyhead[C]{}
+\fancyhead[R]{\thedocnumber}
+\fancyfoot[L]{\theproject}
+\fancyfoot[R]{\today}
+
+\begin{document}
+
+%% title page
+\pagenumbering{roman} \thispagestyle{empty} \maketitle
+\pagebreak 
+
+%% revisions page
+\pagestyle{fancy}
+{ \Large \bf Revision History}
+\begin{center}
+\begin{tabular}{|p{1.25in}|p{1in}|p{4in}|}
+\hline
+{\bf Revision Number} & {\bf Release Date} & {\bf Description} \\
+\hline
+00 & 2004/01/08 & Proposal \\
+\hline
+\end{tabular}
+\end{center}
+\pagebreak
+
+%% contents page
+\tableofcontents
+\pagebreak
+
+%% figures page
+\listoffigures
+\pagebreak \pagenumbering{arabic}
+
+%%%%% start of main text
+
+The IPP Engine initially produced by MHPCC, implemented in IPP.c along
+with supporting code in the engine/dataAPI and the module/moduleAPI
+directories, provides a method to connect the low-level functional
+modules together on the basis of a simple scripting language.  This
+document attempts to evaluate this engine on the basis of the needs,
+expectations, and biases of the IfA team, and to propose modifications
+which will adapt the architecture in ways more in line with our
+desires.  This document first discusses the current implementation,
+second outlines our (IfA) concerns and thoughts about this
+implementations, and finally makes a proposal for future
+implementation.
+
+\milsection{Engine Description}
+
+The Pilot Project implements low-level modules and the engine, which
+connects the modules together.  A module performs a specific,
+well-defined task, such as reading in an image from disk to a data
+structure, correcting for a bias, or applying a flat-field.  The
+engine allows these modules to be connected together in nearly
+arbitrary ways.  
+
+For every module and for the engine itself, there are a set of
+functions which may be unique to that component.  These include: {\tt
+malloc, free, warning handler, addDescriptor, addDataItem,
+deleteDataItem}, and {\tt getDataItem}.  
+
+A module is invoked in the config file like this:
+\begin{verbatim}
+moduleName    par1=value1 par2=value2 par3=value3;
+\end{verbatim}
+
+There is no data typing at the script level; type assignement is
+performed in the module and in the engine, and depends on the usage of
+parameter names.
+
+Two IPP data structures initially carry the information about the
+modules: {\tt char *moduleNames} and {\tt DataItemList *params}.  The
+list 'params' contains the par=value entries in a linked list of data
+structures with name and value pairs elements.  When the parameter
+list is parsed, the types are identified (int/float/string) and cast
+to those value types in the dataItem structure.  An additional data
+type is available to the module programmer, cell.  A cell data type
+may be created in the script language by a call to a module which
+returns a cell-type, such as readFITS.
+
+There is also the {\tt global} parameter data structure which acts
+like an environment variable stack: the complete list is always passed
+to the modules.
+
+There is an IPP variable stack 'dataSpace' which contains data
+currently in use by IPP.  Modules share data through the dataSpace
+stack, either leaving output data on the stack or pulling required
+data from the stack, though in the current implementation, the engine
+layer (IPP.c) mediates the actual interaction with dataSpace.
+
+\milsubsection{Basic Steps of IPP.c}
+
+The top-level of the IPP program performs the following operations:
+
+\begin{itemize}
+
+\item {\bf Define engine functions (initEngine) } This sets the values
+    of the abstracted engine functions discussed above.
+
+\item {\bf Parse configuration file (ParseConfigurationFile) } This
+    reads the config file and sets up the moduleName/params
+    structures.
+
+\item {\bf Load the identified modules (a loop in main) } This calls
+    {\tt loadModule} for each module identified in the config file,
+    which in turn creates a Module structure for the module, loads the
+    library file associated with the named module, assigns the
+    parameter list to the module structure, and calls the module's
+    init function.  The init function makes the association between
+    module parameters and module data descriptors.  Note that not all
+    parameters are associated with a data descriptor; the module
+    programmer may choose this association on whatever basis.
+
+\item {\bf Execute each module (a loop in main)}  The execution of the
+  module includes the following steps:
+
+  \begin{itemize}
+    \item {\bf Determine the module type} This simply loops over the
+      module data descriptors and identifies the data type, but gives
+      an error if a module defines multiple data types.
+
+    \item {\bf Identify the required data in the dataSpace}  This
+      section will automatically create a MAJOR\_FRAME data item in the
+      dataSpace if required. 
+
+    \item {\bf Call the module} In this step, callModule iterates
+      through data type levels to match the given data entry with the
+      given module data level.
+
+    \item {\bf free temporary data}
+  \end{itemize}
+\end{itemize}
+
+\milsection{IfA Comments and Concerns}
+
+The existing IPP.c engine is quite complex, and performs a significant
+fraction of the data manipulation.  Much of the complexity seems to
+stem from the concerns about data I/O demands introduced by the large
+data volume and the need to control aspects of the parallelization
+scheme in great detail.  Aside from the point that parallization is
+outside the scope of the Pilot Project, we do not share these I/O
+throughput concerns, as discussed below, and are instead concerned
+that the level of complexity involved in the current design will
+result in a high level of development and debugging effort which is
+not justified.  Some of our specific concerns follow.
+
+\milsubsection{Excessive Abstraction (engineFcns)}
+
+The use of unique abstracted functions for each module for such basic
+functions as {\tt malloc} and {\tt free} and for the list management
+APIs {\tt addDescriptor, addDataItem, deleteDataItem}, and {\tt
+getDataItem} seems extreme and unneccesary.  
+
+It is very unclear why this level of flexibility and abstraction is
+helpful or useful.  The only one of the engineFcns entries which is
+obvious to associate with the module is the warning handler.  Clearly,
+it is important for the system, IPP.c, to define its unique value of
+malloc and free, but why should this be allowed to vary for each
+module?  Also, why should the methods to interact with the I/O data
+structures be allowed to vary from module to module.  This
+implementations seems to add a layer of complexity that is not
+justified.  These may have been introduced for reasons of
+parallelization, but we believe this is a red herring.
+
+\milsubsection{Obfuscated Data Handling}
+
+There is a surprisingly complex series of steps necessary for a module
+to expose data to other modules, via the IPP.c dataSpace structure.
+
+Consider, for example, the {\tt debias} module, which needs to perform
+its operation on a given image.  It is specified in the configuration
+file as:
+%
+\begin{verbatim}
+debias image="myImage" overscans=32
+\end{verbatim}
+%
+In this syntax, the term {\tt image=''myImage''} identifies that the
+debias image parameter should be associated with the data space item
+{\tt myImage}.  In order to provide debias with access to {\tt
+myImage}, the following steps take place:
+
+First, the parameters are parsed at the {\tt initModule} stage, and
+the name associated with the parameter {\tt image} is identified.
+This is then pushed on the module descriptor list with its associated
+data type (CELL) and direction (INPUT/OUTPUT).  
+
+Next, when the module is ready to be called, the engine searches the
+module descriptor list for an INPUT or INPUT/OUTPUT entry and finds
+the corresponding data item from the dataSpace, and pushes it on the
+module data space.  (Note that if an entry is output only, the engine
+assumes it to require a MAJOR\_FRAME and creates the data space entry.
+This is an odd choice: why not have the modules responsible for
+creating their own data space entries as needed?)
+
+Finally, back in debias, the module searches the parameter list again
+to identify the image parameter name, then searches the module data
+space for the matching entry.  
+
+This seems far too complex.  Why not simply have a set of APIs to
+access the data space directly?  All of these steps could be reduced
+to two steps:
+
+First, search the parameter list for the desired parameter/name
+match.  Second, request the data value from the engine data space:
+
+\begin{verbatim}
+varName = psDiGetDataItem(*parameters,IMAGE_PARAMETER_NAME);
+if (varName) {
+  imageItem = psGetDataItemfromDataSpace((char*)varName->value);
+}
+\end{verbatim} 
+
+There would be no need to expose the dataSpace variable structure to
+the modules or to the basic engine code.  This would isolate all data
+space access to the modules.  Alternatively, the dataSpace variable
+could be passed to the modules directly, rather than via the
+temporary module data space.  
+
+\milsubsection{Unnecessary Iteration in IPP.c / execute module}
+
+The function {\tt callModule} has a clever layout to make the
+appropriate association between the data type and the module operation
+level.  However, it seems to be far too complex.  We do not see the
+need for this level of complexity in the engine; we would strongly
+prefer that a module have a specified data level of operation (ie, OTA
+or CELL) and the iteration over higher levels be performed explicity
+either at the script level or at the level of the system which invokes
+the engine, while iteration at a lower level be performed within the
+module itself.  We are not convinced that the arguments about
+parallelizing justify the level of complexity in this implementation.
+In addition, the dataDescriptor construction is apparently introduced
+to allow the iteration which takes place in the IPP.c / execute module
+state.  If the engine-level iteration were dropped, then the entire
+dataDescriptor construct could be dropped from the engine.
+
+\milsection{Proposals for the Pilot Project Engine}
+
+We would like to capitalize on the effort expended so far on the
+module and engine development.  There is some concern within the IfA
+that the current design differs far too much from our expectations to
+be of any use.  However, we would like propose here that we try to
+make use of the development so far in a way that will bring the ideas
+of the two communities into closer agreement.  Therefore, we have
+several specific proposals for the IPP.c and for the IPP architectural
+developement generally.
+
+The basic architectural requirements boil down to three concepts:
+First, there must be a scripting language which will allow a
+high-level description of how to link low-level modules together in a
+flexible way.  Second, there must be a way of associating a specific
+script with a given data item (ie, an input image), or a list of data
+items.  Third, there must be a system to organize the execution of
+these operations in parallel for many data items.
+
+The existing engine, IPP.c, performs the first of these tasks.
+Although we have specific concerns with the implementation and perhaps
+the grammar of the scripting language, the general structure defines a
+language which allows the modules to be linked together.  
+
+There is a strong concern at the IfA about investing more than a very
+minor effort to create a linking language from scratch, for either the
+Pilot Project or for the IPP in general.  We feel that such an effort
+is likely to waste a substantial amount of time and to have
+significant risk of bugs and errors.  The IfA would prefer to have the
+real functionality of the modules directly accessible.  
+
+Although the existing module/engine structure makes it rather
+difficult, it is easy to envision modules which are flexible enough to
+allows us to implement test analysis runs for each of these three
+philosophical approaches.  With some minor modification, the existing
+IPP structures would make such tests easy and feasible as well.  We
+would like to make the following proposals for the Pilot Project
+engine / module architecture.
+
+\begin{enumerate}
+
+\item {\bf ignore parallel optimization} Until it is demonstrated
+  otherwise, we feel that the addition of large amounts of complexity
+  to the engine only for the goal of enabling fancy parallel
+  processing optimizations is a distraction and a waste of time.  We
+  believe that all of the parallelization can be performed on very
+  large data chunks (ie, OTAs), and that these operations are
+  essentially independent.  Therefore, no inter-module communication
+  is necessary, and each phase (in the sense defined by the complete
+  IPP design) can be executed as a single command.  Therefore, only
+  scripting sufficient to link modules together in a linear fashion
+  for a single data instance is necessary.  If it is ultimately
+  necessary to add module-level intercommunication to ease the
+  handling of the parallel processing problems, this can be done
+  within the modules or with blocking in the scripting language at
+  that time.  We believe this stance is strongly supported by the
+  switch tests and data I/O rates as examined by Josh Hobblit (see
+  section~\ref{datarate}).
+
+\item {\bf provide direct module APIs without engine wrapping}.  The
+  current module APIs are completely flat and uniform.  While this has
+  some theoretical appeal, it requires the use of the engine
+  data API to interact with the modules.  This essentially excludes
+  the use of simple C or SWIG'ed Perl code.  In our proposal, each
+  module would have an API (or multiple APIs as needed) which
+  correspond to their function.  For example, the debias module should
+  have the debias functionality accessible via a {\tt divideByFlat (OTA
+  *image, OTA *flat);} function call.  Note that this proposal does
+  not imply dropping the existing {\tt invokeModule ();} functions
+  associated with each module; rather, the invokeModule function
+  should wrap the lower-level call.
+
+\item {\bf simplify the data-space API} If the engine is only
+  responsible for scripting, there is no need for such a complex set
+  of interactions to handle data between the modules and the engine.
+  The definition of a simple set of engine data-space APIs would allow
+  the modules to gain access to the data structures known by other
+  modules without so many layers of complexity.
+
+\item {\bf simplify module API} If the modules interact via a simple
+  data space API layer, then the module API need only consist of the
+  module parameters.  The modules should make their own decisions
+  about data typing issues and should simply return errors if the
+  script programmers attempt to pass the wrong data types to a module.
+
+\item {\bf remove engine-level iteration / implement module iteration}
+  The modules should have a defined level or set of levels of
+  operation.  We would like to see the modules all operating at the
+  OTA level, since that is the proposed parallelization quantum.  For
+  example, the debias module should receive the OTA structure and
+  performe the iteration if necessary.  This is already implemented by
+  readFITS.c 
+
+\item {\bf freeze grammar} We would like to freeze the IPP.c grammar
+  for the purpose of the Pilot Project.  Although the current grammar
+  is very simple, and minor changes could make the grammar much more
+  sophisticated, what has been delivered will suffice for the Pilot
+  Project.  Since this is potentially an area where far more
+  development time may be spent than is really necessary, we would
+  like to avoid extending the range of the grammar at this time.
+
+\item {\bf implement SWIG interface files}  If the modules have been
+  defined with direct access to the low-level functionality, then it
+  becomes trivial to write SWIG interface files.  Each module should
+  have an associated header file (eg, debias.c gets a debias.h) and an
+  associated interface file (eg, debias.i) which is used to generate
+  the SWIG wrapper file (eg, debias\_wrap.c).  
+
+\item {\bf implement sample Perl script using SWIG}.  With the defined
+  SWIG interface files, it is simple to generate wrapper files for the
+  functions specific to one of the languages supported by SWIG.  The
+  modules can then be linked together in a script within that language
+  by using calls to those modules.  We propose that, in addition to
+  the basic engine script, an equivalent basic Perl script be defined
+  to perform the Pilot Project pipeline steps.
+\end{enumerate}
+
+With these suggestions, we feel the IPP.c engine becomes a simple,
+easily understood tool for linking the modules together, without
+spending vast amounts of effort on a completely general scripting
+language.  In addition, the modules are sufficiently well-defined with
+the data well-encapsulated that they can be easily used within a
+variety of environments.  The demonstration of a Perl implementation
+will show the use of at least one high-level language as a scripting
+language.
+
+\milsubsection{Rough Example Code}
+
+I have made a quick and dirty modification to the existing IPP modules
+and engine to implement the changes we propose above, as an example /
+demonstration.  The code can be found on our website at:
+
+{\tt http://panstarrs.ifa.hawaii.edu/project/people/magnier/IPPx} 
+
+(there is also a tar-gzipped copy in the {\tt magnier} directory).
+The example is based on the original release of the Pilot Project
+Phase I code.
+
+I stripped out the dataDescrition components and all of the data-level
+iteration section in the callModule function.  I also wrapped the
+module core operations in APIs which can be more conveniently swigged.
+For the readFITS, writeFITS, and debias modules, I have defined Cell
+and OTA level operations.  Rather than implement the module level
+iterations for debias, I have limited the engine calls to the
+Cell-level module APIs for debias, readFITS, and writeFITS.  By
+limiting the data to Cells (ignoring the OTA level data), I have been
+able to compile and run these three modules successfully together.
+The text configuration file is in src/engine/example.  The other two
+modules would require a bit more work to be sure the interaction is at
+the right data level.
+
+There were a few places where the header and library dependencies were
+confusing to me, so I did a small amount of reorganization just to get
+things clear for me.  This should not be taken as an organization
+suggestion, just something that helped me understand the difference
+between the module, data, and engine API pieces.  It is probably not
+ideal in terms of file organization.  I also had to alter the
+Makefiles somewhat mostly because of the header dependencies.
+
+I have done some of the work on SWIG examples.  There are *.i files
+for debias, fits, and divideByFlat.  I have successfully run SWIG for
+TCL and compiled the results for the debias module.  The
+debias/Makefile contains the relevant steps.  I have not attempted to
+write a TCL script using the resulting module, but I have successfully
+run TCL with SWIG for sample code.  There are also apparently
+configuration problems with Perl on my laptop and the IfA poi cluster,
+so I have not demonstrated the Perl implementation at this time for
+IPPx.  SWIG / Perl interactions have been successfully demonstrated
+using Perl compiled without multithread support.
+
+\milsection{Data Throughput Calculations}
+\label{datarate}
+
+The following data throughput discussion is cut from an email by Josh
+Hoblitt.
+
+We can do some quick 'back of the envelope' estimates to figure out
+the worst case bandwidth needs.  Let's assume that control messages
+and db traffic is negligible compared to the image data.  Let's also
+assume that there isn't even trivial data locality optimization {\em
+and} we have to reload all calibration data for every exposure.  That
+gives us:
+
+\begin{verbatim}
+ 4 fully populated focal planes
+
+     2bytes * ( 4096^2pixels * 1.125overclocks ) * 240otas = 72477573120b
+
+ slew/settle/integrate/read cadence of 30s
+
+     72477573120b / 30s = 2415919104b/s
+
+ exposure data stored as float
+
+     4bytes * 4096^2pixels * 240otas = 128849018880b
+
+     128849018880b / 30s = 4294967296b/s
+
+ best/working stacked stored as float
+
+     4bytes * 4096^2pixels * 60otas = 32212254720
+
+     32212254720b / 30s = 1073741824b/s
+
+ debias, dark, flat, 2x fringe, and 2x sky calibration frames
+
+     7 * 4294967296b/s = 30064771072b/s
+
+ Which gives us a grand total of:
+
+ [phase 2]
+      2415919104b/s  summit -> disk (exposure)
+ +    2415919104b/s  non-local disk -> memory (exposure)
+ +   30064771072b/s  non-local disk -> memory (calibration)
+ +    4294967296b/s  memory -> non-local disk (reduced)
+ ------------------
+  39,191,576,576b/s  phase 2 total
+
+ [phase 4]
+      4294967296b/s  non-local disk -> memory (reduced)
+ +    1073741824b/s  non-local disk -> memory (best)
+ +    1073741824b/s  non-local disk -> memory (working)
+ +    1073741824b/s  memory -> non-local disk (diff)
+ +    1073741824b/s  memory -> non-local disk (working)
+ ------------------
+   8,589,934,592b/s  phase 4 total
+
+ [total]
+     39191576576b/s  phase 2 total
+ +    8589934592b/s  phase 4 total
+ ------------------
+  47,781,511,168b/s  total is ~48Gb/s
+\end{verbatim}
+
+ 48Gb/s is 1/15th of the bandwidth available in a 65xx/SUP720.  Given
+ these numbers I don't think that engineering effort to optimize for a
+ hierarchical network topology is justifiable.
+
+ To address your concerns about the backplane numbers being accurate,
+ I can tell you that I have a significant amount of experience with
+ enterprise class switches from Cisco, Foundry, and HP.  I've always
+ found the switching fabric specs to be accurate from all three of
+ these manufactures.  In addition, as soon as I have equipment
+ available for use by Pan-Starrs I will generate my own bandwidth
+ tests.  However it should be noted that as demonstrated above that
+ even our worst case bandwidth needs are rather modest compared to
+ COTS equipment that is available today.
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/pilot/engine/engine_notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pilot/engine/engine_notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pilot/engine/engine_notes.txt	(revision 22322)
@@ -0,0 +1,77 @@
+
+Evaluation of the MHPCC Engine (IPP.c)
+
+- for every module & for the engine itself, there are a set of
+  functions which may be unique to that component.  These include:
+  malloc, free, warning handler, addDescriptor, addDataItem,
+  deleteDataItem, and getDataItem.  
+
+  It is very unclear why this level of flexibility and abstraction is
+  helpful or useful.  The only one of these which is obvious to
+  encapsulate with the module is the warning handler.  Clearly, it is
+  important to the system, IPP.c, to define its unique value of malloc
+  and free, but why should this be allowed to vary for each module?
+  Also, why should the methods to interact with the I/O data
+  structures be allowed to vary from module to module.  This
+  implementations seems to add a layer of complexity that is not
+  justified.  
+
+- a module is invoked in the config file like this:
+
+moduleName    par1=value1 par2=value2 par3=value3;
+
+  there is no data typing at the script level; typing is implicit in
+  the module's usage of a parameter name.
+
+- two IPP data structures carry the information about the modules:
+  char *moduleNames and DataItemList *params.  The list 'params'
+  contains the par=value entries is a set of data structures with name
+  and value pairs.  When the parameter list is parsed, the types are
+  identified (int/float/string) and cast to those value types in the
+  dataItem structure.  An additional data type is available to the
+  module programmer, cell.  A cell data type may be created in the
+  script language by a call to a module which returns a cell-type,
+  such as readFITS.
+
+- there is also the global parameter data structure which acts like an
+  environment variable stack: these are always passed to the modules. 
+
+- there is an IPP variable stack 'dataSpace' which contains data
+  currently in use by IPP.  Modules share data through the dataSpace
+  stack, either leaving output data on the stack or pulling required
+  data from the stack.
+
+- basic steps of IPP.c:
+
+  - define engine functions (initEngine) 
+    this sets the values of the abstracted engine functions discussed
+    above.
+  - parse config file (ParseConfigurationFile)
+    this reads the config file and sets up the moduleName/params
+    structures.
+  - load the identified modules (a loop in main)
+    this calls 'loadModule' for each module identified in the config
+    file, which in turn creates a Module structure for the module,
+    loads the library file associated with the named module, assigns
+    the parameter list to the module structure, and calls the module's
+    init function.  The init function makes the association between
+    module parameters and module data descriptors.  Note that not all
+    parameters are associated with a data descriptor; the module
+    programmer may choose this association on whatever basis.
+  - execute each module (a loop in main)
+
+    * determine the module type: this simply loops over the module
+      data descriptors and identifies the data type, but gives an
+      error if a module defines multiple data types.
+
+    * identify the required data in the dataSpace.  this section will
+      automatically create a MAJOR_FRAME data item in the dataSpace if
+      required.  ** a strange choice here **
+
+    * call the module 
+      callModule iterates through data type levels to match the given
+      data entry with the given module data level. 
+
+    * free temporary data
+
+    
Index: /tags/sj_tags/sj_root_20080929/doc/pilot/engine/pilot-engine-prop.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pilot/engine/pilot-engine-prop.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pilot/engine/pilot-engine-prop.tex	(revision 22322)
@@ -0,0 +1,408 @@
+\documentclass[panstarrs]{panstarrs}
+\newcommand\note[1]{{\bf #1}}
+\newcommand\tbd[1]{{\bf [#1]}}
+
+\title{Proposal for the Pilot Project Engine / Architecture}
+\author{Eugene Magnier}
+\group{}
+\organization{}
+\docnumber{PSDC-xxx-xx-01}
+
+\fancyhead{}
+\fancyhead[L]{IPP.c Architecture Proposal }
+\fancyhead[R]{}
+\fancyhead[C]{Revision 01 -- Draft}
+\fancyfoot[L]{}
+\fancyfoot[R]{\today}
+
+\begin{document}
+
+\pagenumbering{roman} \thispagestyle{empty} \maketitle
+
+\pagebreak \pagestyle{fancy}
+{ \Large \bf Revision History}
+\begin{center}
+\begin{tabular}{|p{1.25in}|p{1in}|p{4in}|}
+\hline
+{\bf Revision Number} & {\bf Release Date} & {\bf Description} \\
+\hline
+01 & 2003/12/29 & First draft \\
+02 & 2004/01/06 & Update based on IfA comments \\
+\hline
+\end{tabular}
+\end{center}
+
+\pagebreak
+\tableofcontents
+
+\pagebreak
+\listoffigures
+
+\pagebreak \pagenumbering{arabic}
+
+The IPP Engine initially produced by MHPCC, implemented in IPP.c along
+with supporting code in the engine/dataAPI and the module/moduleAPI
+directories, provides a method to connect the low-level functional
+modules together on the basis of a simple scripting language.  This
+document attempts to evaluate this engine on the basis of the needs,
+expectations, and biases of the IfA team, and to propose modifications
+which will adapt the architecture in ways more in line with our
+desires.  This document first discusses the current implementation,
+second outlines our (IfA) concerns and thoughts about this
+implementations, and finally makes a proposal for future
+implementation.
+
+\milsection{Engine Description}
+
+The Pilot Project implements low-level modules and the engine, which
+connects the modules together.  A module performs a specific,
+well-defined task, such as reading in an image from disk to a data
+structure, correcting for a bias, or applying a flat-field.  The
+engine allows these modules to be connected together in nearly
+arbitrary ways.  
+
+For every module and for the engine itself, there are a set of
+functions which may be unique to that component.  These include: {\tt
+malloc, free, warning handler, addDescriptor, addDataItem,
+deleteDataItem}, and {\tt getDataItem}.  
+
+A module is invoked in the config file like this:
+\begin{verbatim}
+moduleName    par1=value1 par2=value2 par3=value3;
+\end{verbatim}
+
+There is no data typing at the script level; type assignement is
+performed in the module and in the engine, and depends on the usage of
+parameter names.
+
+Two IPP data structures initially carry the information about the
+modules: {\tt char *moduleNames} and {\tt DataItemList *params}.  The
+list 'params' contains the par=value entries in a linked list of data
+structures with name and value pairs elements.  When the parameter
+list is parsed, the types are identified (int/float/string) and cast
+to those value types in the dataItem structure.  An additional data
+type is available to the module programmar, cell.  A cell data type
+may be created in the script language by a call to a module which
+returns a cell-type, such as readFITS.
+
+There is also the {\tt global} parameter data structure which acts
+like an environment variable stack: the complete list is always passed
+to the modules.
+
+There is an IPP variable stack 'dataSpace' which contains data
+currently in use by IPP.  Modules share data through the dataSpace
+stack, either leaving output data on the stack or pulling required
+data from the stack, though in the current implementation, the engine
+layer (IPP.c) mediates the actual interaction with dataSpace.
+
+\milsubsection{Basic Steps of IPP.c}
+
+The top-level of the IPP program performs the following operations:
+
+\begin{itemize}
+
+\item {\bf Define engine functions (initEngine) } This sets the values
+    of the abstracted engine functions discussed above.
+
+\item {\bf Parse configuration file (ParseConfigurationFile) } This
+    reads the config file and sets up the moduleName/params
+    structures.
+
+\item {\bf Load the identified modules (a loop in main) } This calls
+    {\tt loadModule} for each module identified in the config file,
+    which in turn creates a Module structure for the module, loads the
+    library file associated with the named module, assigns the
+    parameter list to the module structure, and calls the module's
+    init function.  The init function makes the association between
+    module parameters and module data descriptors.  Note that not all
+    parameters are associated with a data descriptor; the module
+    programmar may choose this association on whatever basis.
+
+\item {\bf Execute each module (a loop in main)}  The execution of the
+  module includes the following steps:
+
+  \begin{itemize}
+    \item {\bf Determine the module type} This simply loops over the
+      module data descriptors and identifies the data type, but gives
+      an error if a module defines multiple data types.
+
+    \item {\bf Identify the required data in the dataSpace}  This
+      section will automatically create a MAJOR\_FRAME data item in the
+      dataSpace if required. 
+
+    \item {\bf Call the module} In this step, callModule iterates
+      through data type levels to match the given data entry with the
+      given module data level.
+
+    \item {\bf free temporary data}
+  \end{itemize}
+\end{itemize}
+
+\milsection{IfA Comments and Concerns}
+
+The existing IPP.c engine is quite complex, and performs a significant
+fraction of the data manipulation.  Much of the complexity seems to
+stem from the concerns about data I/O demands introduced by the large
+data volume and the need to control aspects of the parallelization
+scheme in great detail.  We do not share these concerns, as discussed
+below, and are instead concerned that the level of complexity involved
+in the current design will result in a high level of development and
+debugging effort which is not justified.  Some of our specific
+concerns follow.
+
+\milsubsection{Excessive Abstraction (engineFcns)}
+
+The use of unique abstracted functions for each module for such basic
+functions as {\tt malloc} and {\tt free} and for the list management
+APIs {\tt addDescriptor, addDataItem, deleteDataItem}, and {\tt
+getDataItem} seems extreme and unneccesary.  
+
+It is very unclear why this level of flexibility and abstraction is
+helpful or useful.  The only one of the engineFcns entries which is
+obvious to associate with the module is the warning handler.  Clearly,
+it is important for the system, IPP.c, to define its unique value of
+malloc and free, but why should this be allowed to vary for each
+module?  Also, why should the methods to interact with the I/O data
+structures be allowed to vary from module to module.  This
+implementations seems to add a layer of complexity that is not
+justified.  These may have been introduced for reasons of
+parallelization, but we believe this is a red herring.
+
+\milsubsection{Obfuscated Data Handling}
+
+There is a surprisingly complex series of steps necessary for a module
+to expose data to other modules, via the IPP.c dataSpace structure.
+
+Consider, for example, the {\tt debias} module, which needs to perform
+its operation on a given image.  It is specified in the configuration
+file as:
+%
+\begin{verbatim}
+debias image=''image'' overscans=32
+\end{verbatim}
+%
+In this syntax, the term {\tt image=''image''} identifies that the
+debias image parameter should be associated with the data space item
+``image''.  In order to provide debias with access to ``image'', the
+following steps take place:
+
+First, the parameters are parsed at the {\tt initModule} stage, and
+the name associated with the parameter {\tt image} is identified.
+This is then pushed on the module descriptor list with its associated
+data type (CELL) and direction (INPUT/OUTPUT).  
+
+Next, when the module is ready to be called, the engine searches the
+module descriptor list for an INPUT or INPUT/OUTPUT entry and finds
+the corresponding data item from the dataSpace, and pushes it on the
+module data space.  (Note that if an entry is output only, the engine
+assumes it to require a MAJOR\_FRAME and creates the data space entry.
+This is an odd choice: why not have the modules responsible for
+creating their own data space entries as needed?)
+
+Finally, back in debias, the module searches the parameter list again
+to identify the image parameter name, then searches the module data
+space for the matching entry.  
+
+This seems far too complex.  Why not simply have a set of APIs to
+access the data space directly?  All of these steps could be reduced
+to two steps:
+
+First, search the parameter list for the desired parameter/name
+match.  Second, request the data value from the engine data space:
+
+\begin{verbatim}
+varName = psDiGetDataItem(*parameters,IMAGE_PARAMETER_NAME);
+if (varName) {
+  imageItem = psGetDataItemfromDataSpace((char*)varName->value);
+}
+\end{verbatim} 
+
+There would be no need to expose the dataSpace variable structure to
+the modules or to the basic engine code.  This would isolate all data
+space access to the modules.  Alternatively, the dataSpace variable
+could be passed to the modules directly, rather than via the
+temporary module data space.  
+
+\milsubsection{Unnecessary Iteration in IPP.c / execute module}
+
+The function {\tt callModule} has a clever layout to make the
+appropriate association between the data type and the module operation
+level.  However, it seems to be far too complex.  We do not see the
+need for this level of complexity in the engine; we would strongly
+prefer that a module have a specified data level of operation (ie, OTA
+or CELL) and the iteration over higher levels be performed explicity
+either at the script level or at the level of the system which invokes
+the engine, while iteration at a lower level be performed within the
+module itself.  We are not convinced that the arguments about
+parallelizing justify the level of complexity in this implementation.
+In addition, the dataDescriptor construction is apparently introduced
+to allow the iteration which takes place in the IPP.c / execute module
+state.  If the engine-level iteration were dropped, then the entire
+dataDescriptor construct could be dropped from the engine.
+
+\milsection{Proposals for the Pilot Project Engine}
+
+We would like to capitalize on the effort expended so far on the
+module and engine development.  There is some concern within the IfA
+that the current design differs far too much from our expectations to
+be of any use.  However, we would like propose here that we try to
+make use of the development so far in a way that will bring the ideas
+of the two communities into closer agreement.  Therefore, we have
+several specific proposals for the IPP.c and for the IPP architectural
+developement generally.
+
+The basic architectural requirements boil down to three concepts:
+First, there must be a scripting language which will allow a
+high-level description of how to link low-level modules together in a
+flexible way.  Second, there must be a way of associating a specific
+script with a given data item (ie, an input image), or a list of data
+items.  Third, there must be a system to organize the execution of
+these operations in parallel for many data items.
+
+The existing engine, IPP.c, performs the first of these tasks.
+Although we have specific concerns with the implementation and perhaps
+the grammar of the scripting language, the general structure defines a
+language which allows the modules to be linked together.  
+
+There is a strong concern at the IfA about investing more than a very
+minor effort to create a linking language from scratch, for either the
+Pilot Project or for the IPP in general.  We feel that such an effort
+is likely to waste a substantial amount of time and to have
+significant risk of bugs and errors.  The IfA would prefer to have the
+real functionality of the modules directly accessible.  
+
+Although the existing module/engine structure makes it rather
+difficult, it is easy to envision modules which are flexible enough to
+allows us to implement test analysis runs for each of these three
+philosophical approaches.  With some minor modification, the existing
+IPP structures would make such tests easy and feasible as well.  We
+would like to make the following proposals for the Pilot Project
+engine / module architecture.
+
+\begin{enumerate}
+
+\item {\bf ignore parallel optimization} Until it is demonstrated
+  otherwise, we feel that the addition of large amounts of complexity
+  to the engine only for the goal of enabling fancy parallel
+  processing optimizations is a distraction and a waste of time.  We
+  believe that all of the parallelization can be performed on very
+  large data chunks (ie, OTAs), and that these operations are
+  essentially independent.  Therefore, no inter-module communication
+  is necessary, and each phase (in the sense defined by the complete
+  IPP design) can be executed as a single command.  Therefore, only
+  scripting sufficient to link modules together in a linear fashion
+  for a single data instance is necessary.  If it is ultimately
+  necessary to add module-level intercommunication to ease the
+  handling of the parallel processing problems, this can be done
+  within the modules or with blocking in the scripting language at
+  that time.  We believe this stance is strongly supported by the
+  switch tests and data I/O rates as examined by Josh Hobblit
+  \tbd{REF}.
+
+\item {\bf provide direct module APIs without engine wrapping}.  The
+  current module APIs are completely flat and uniform.  While this has
+  some theoretical appeal, it requires the use of the engine
+  data API to interact with the modules.  This essentially excludes
+  the use of simple C or SWIG'ed Perl code.  In our proposal, each
+  module would have an API (or multiple APIs as needed) which
+  correspond to their function.  For example, the debias module should
+  have the debias functionality accessible via a {\tt divideByFlat (OTA
+  *image, OTA *flat);} function call.  Note that this proposal does
+  not imply dropping the existing {\tt invokeModule ();} functions
+  associated with each module; rather, the invokeModule function
+  should wrap the lower-level call.
+
+\item {\bf simplify the data-space API} If the engine is only
+  responsible for scripting, there is no need for such a complex set
+  of interactions to handle data between the modules and the engine.
+  The definition of a simple set of engine data-space APIs would allow
+  the modules to gain access to the data structures known by other
+  modules without so many layers of complexity.
+
+\item {\bf simplify module API} If the modules interact via a simple
+  data space API layer, then the module API need only consist of the
+  module parameters.  The modules should make their own decisions
+  about data typing issues and should simply return errors if the
+  script programmars attempt to pass the wrong data types to a module.
+
+\item {\bf remove engine-level iteration / implement module iteration}
+  The modules should have a defined level or set of levels of
+  operation.  We would like to see the modules all operating at the
+  OTA level, since that is the proposed parallelization quantum.  For
+  example, the debias module should receive the OTA structure and
+  performe the iteration if necessary.  This is already implemented by
+  readFITS.c 
+
+\item {\bf freeze grammar} We would like to freeze the IPP.c grammar
+  for the purpose of the Pilot Project.  Although the current grammar
+  is very simple, and minor changes could make the grammar much more
+  sophisticated, what has been delivered will suffice for the Pilot
+  Project.  Since this is potentially an area where far more
+  development time may be spent than is really necessary, we would
+  like to avoid extending the range of the grammar at this time.
+
+\item {\bf implement SWIG interface files}  If the modules have been
+  defined with direct access to the low-level functionality, then it
+  becomes trivial to write SWIG interface files.  Each module should
+  have an associated header file (eg, debias.c gets a debias.h) and an
+  associated interface file (eg, debias.i) which is used to generate
+  the SWIG wrapper file (eg, debias\_wrap.c).  
+
+\item {\bf implement sample Perl script using SWIG}.  With the defined
+  SWIG interface files, it is simple to generate wrapper files for the
+  functions specific to one of the languages supported by SWIG.  The
+  modules can then be linked together in a script within that language
+  by using calls to those modules.  We propose that, in addition to
+  the basic engine script, an equivalent basic Perl script be defined
+  to perform the Pilot Project pipeline steps.
+\end{enumerate}
+
+With these suggestions, we feel the IPP.c engine becomes a simple,
+easily understood tool for linking the modules together, without
+spending vast amounts of effort on a completely general scripting
+language.  In addition, the modules are sufficiently well-defined with
+the data well-encapsulated that they can be easily used within a
+variety of environments.  The demonstration of a Perl implementation
+will show the use of at least one high-level language as a scripting
+language.
+
+\misubsection{Rough Example Code}
+
+I have made a quick and dirty modification to the existing IPP modules
+and engine to implement the changes we propose above, as an example /
+demonstration.  The code can be found on our website at {\tt
+http://panstarrs.ifa.hawaii.edu/project/people/magnier/IPPx} (there is
+also a tar-gzipped copy in the magnier directory).  The example is
+based on the original release of the Pilot Project Phase I code.
+
+I stripped out the dataDescrition components and all of the data-level
+iteration section in the callModule function.  I also wrapped the
+module core operations in APIs which can be more conveniently swigged.
+For the readFITS, writeFITS, and debias modules, I have defined Cell
+and OTA level operations.  Rather than implement the module level
+iterations for debias, I have limited the engine calls to the
+Cell-level module APIs for debias, readFITS, and writeFITS.  By
+limiting the data to Cells (ignoring the OTA level data), I have been
+able to compile and run these three modules successfully together.
+The text configuration file is in src/engine/example.  The other two
+modules would require a bit more work to be sure the interaction is at
+the right data level.
+
+There were a few places were the header and library dependencies were
+confusing to me, so I did a small amount of reorganization just to get
+things clear for me.  This should not be taken as an organization
+suggestion, just something that helped me understand the difference
+between the module, data, and engine API pieces.  It is probably not
+ideal in terms of file organization.  I also had to hack the Makefiles
+somewhat mostly because of the header dependencies.
+
+I have done some of the work on SWIG examples.  There are *.i files
+for debias, fits, and divideByFlat.  I have successfully run SWIG for
+TCL and compiled the results for the debias module.  The
+debias/Makefile contains the relevant steps.  I have not attempted to
+write a TCL script using the resulting module.  There are also
+apparently configuration problems with Perl on my laptop and the IfA
+poi cluster, so I have not demonstrated the Perl implementation at
+this time.  
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/plan/ipp.status.2008.04.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/plan/ipp.status.2008.04.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/plan/ipp.status.2008.04.txt	(revision 22322)
@@ -0,0 +1,266 @@
+
+IPP Subsystems Outstanding Tasks
+ - work still to be done
+ * who / effort (* : day; ** : week)
+
+Build / Config / PSLib 
+ - minor outstanding organizational improvements
+ * JH ++
+
+Nebulous
+ - failover automation
+ * JH ++
+
+Detrend Construction
+ - visualization
+ * at large +
+ 
+ - tweak parameters
+ * WS ++ 
+
+ - color-correction grid in flat-correction
+ * EAM ++
+
+Single Science Image Analysis
+ - more output detrend application metadata needed
+ * at large +
+
+ - move psastro to camera stage only
+ * at large +
+
+ - determine photmetry zero point in psastro
+ * EAM +
+
+ - more robustness for psphot / psastro
+ * EAM ++
+
+ - psphot optimization and threading 
+ * EAM ++
+
+ - control psphot memory footprint
+ * EAM ++
+
+ - threading for ppImage
+ * PAP ++
+
+Image Combinations
+ - top-level automation and recipe tuning (extended sources ON)
+ * PAP ++
+ * JH ++
+
+ - more visualization / output metadata
+ * PAP +
+
+ - input source lists / forced photometry
+ * at large +
+
+ - ppStack optimization
+ * PAP ++
+
+ - ppSub optimization
+ * PAP ++
+
+Magic
+ - Integration
+ * WS ++ (2x)
+
+Calibration
+ - finish outlier flagging in relphot
+ * EAM +
+
+ - measure 2D color-correction model
+ * EAM ++
+
+ - finish absphot
+ * EAM ++
+
+Postage Stamp Server
+ - finish web pages / cleanups
+ * WS ++
+
+ - magic interface / release flags / re-processing
+ * WS ++
+
+DVO
+ - optimizations for Image / Object / Detection indexes
+ * EAM ++
+
+ - various minor bugs
+ * EAM ++
+
+Output Tables
+ - PSPS : extract calibration from DVO
+ * EAM +
+
+ - convert psphot output to MOPS input
+ * WS +
+
+ - set up summit copy server
+ * JH +
+
+Automation 
+ - high level automation needed
+ * at large ++
+
+Visualization
+ - more plots in ippMonitor
+ * at large ++
+
+Fake Source Pipeline
+ - ippTools & integration work needed
+ * PAP ++
+ * JH ++
+
+Regression Test Suite 
+ - set up repository and scripts
+ * JH ++
+
+OT Guiding Issues
+ - test kernel generation / application
+ * PAP ++
+
+ - measure video astrometry / photometry
+ * EAM ++
+
+Cleanup / Reprocess Tasks
+ - create full pipeline
+ * WS ++
+ * JH ++
+
+Reference Catalogs
+ - validate synth.grizy
+ * EAM ++
+
+----
+
+Summary:
+EAM : 10 x ++, 3 x +
+PAP :  6 x ++, 1 x +
+JH  :  6 x ++, 1 x +
+WS  :  6 x ++, 1 x +
+
+----
+
+EAM Tasks:
+
+ o finish psphot galaxy models
+ o finish summit copy multi-DBs / test
+ - test summit copy multi-DBs
+ - Virgo PR pics
+ - Stripe 82 CFHT / 2MASS comparisons
+
+** psphot work
+ - more robustness for psphot / psastro                     (1w)
+ - determine photometry zero point in psastro		    (1d)
+ - psphot optimization and threading			    (1w)
+ - control psphot memory footprint			    (1w)
+
+** DVO work
+ - finish outlier flagging in relphot			    (1d)
+ - DVO optimizations for Image / Object / Detection indexes (1w)
+ - various minor DVO bugs				    (1w)
+ - measure 2D color-correction model			    (1w)
+ - color-correction grid in flat-correction		    (1w)
+ - finish absphot                                           (1w)
+
+** other tasks
+ - PSPS output tables : extract calibration from DVO	    (1d)
+ - measure video astrometry / photometry		    (1w)
+ - validate synth.grizy					    (1w)
+
+PAP Tasks:
+
+ - finish ppStack / ppSub tweaking (A - B is still incomplete)
+ - stack / diff more visualization / output metadata
+ - fake source pipeline : (ppSub and ppStack versions)
+ - test kernel generation / application
+ o convert psphot output to MOPS input (to IPP DS)
+ o minor ippconfig outstanding organizational improvements
+   (early May)
+ - stack / diff top-level automation and recipe tuning (extended sources ON)
+ - threading for ppImage
+ - ppStack optimization (some done, close to goal for now)
+ - ppSub optimization (some done, close to goal for now)
+
+JH Tasks:
+
+ - set up OTIS DB copy server 
+ - monitor summit copy [EAM : finish summit copy multi-DBs]
+ - set up regression suite repository and scripts 
+ - Nebulous failover automation (< Apr 21)
+ - ippTools for stack / diff automation [needs input from PAP & EAM]
+ - ippTools for fake source pipeline [needs pipeline layout]
+ - cleanup / reprocess pipeline(s) [needs conceptual design]
+
+WS Tasks:
+
+ - test flat-field correction suite
+ - detrend analysis tweak parameters (megacam -> gpc1)
+ - Magic integration
+ - postage stamp server : finish web pages / cleanups
+ - postage stamp server : magic interface / release flags / re-processing
+ - cleanup / reprocess pipeline 
+
+----
+
+IPP Status 2008.04.01
+
+Since about mid-November, we have been putting an increasing amount of
+our time into testing portions of the IPP on large collections of data
+(GPC1, Megacam, Essence, SDSS). This has slowed progress on some areas
+of development, but had the important benefit of uncovering a number
+of issues which needed to be addressed.  Some of these were basic
+coding bugs uncovered by real data.  
+
+One important area of concern uncovered in the test processing is the
+robustness of the psphot and psastro algorithms.  While we have
+addressed some of these problems, more work is still needed here,
+especially for cases where the input data is somewhat marginal.  One
+of our recent conclusions is that we need to isolate the photometry
+analysis (psphot), which can take a long time but more consisently
+succeeds, from the astrometry analysis, which is still more
+fragile. We have decided to remove the astrometry analysis from the
+chip processing stage, and only perform this analysis at the camera
+processing stage.  This will save a lot of time in error handling for
+the science image analysis.
+
+Another area which has caused on going error-handling effort is in the
+parameter configuration system.  We have decided to clean up the
+organization somewhat so that more of the parameter files can
+be shared by all cameras, reducing the duplication of information and
+making it easier to synchronize changes to the configurations.
+
+Paul and I had a detailed discussion this morning about the current
+IPP Status and what development work is still outstanding.  I have
+listed our detailed notes below, grouped by the related IPP Subsystem.
+There are three significant components that need substantial work:
+
+* Magic integration
+
+* Fake Source Pipeline
+
+* Cleanup / Reprocess suite
+
+These are each about 2 weeks of dedicated effort.  The elements of the
+first two of these systems are in place, but we need to finish the
+integration process into the full pipeline.  The Cleanup / Reprocess
+suite is a subsystem which we have been deferring, but for which we
+will soon need to begin work.
+
+In addition, we have several areas where we need to work on
+improvements to the code, or to finish minor additional features
+needed by the pipeline.  Some of these are:
+
+* improvements to the code robustness, particularly for psphot and
+  psastro which are not deterministic.
+
+* optimization work for psphot, ppStack, and ppSub, including
+  multithreading.
+
+* various improvements to the photometry and astrometry calibration
+  analysis, from the measurement of the zero-points in real-time to
+  the application of the calibrations to the output tables to be
+  delivered to PSPS and MOPS.
+
+----
+
Index: /tags/sj_tags/sj_root_20080929/doc/plan/ipp.status.2008.06.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/plan/ipp.status.2008.06.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/plan/ipp.status.2008.06.txt	(revision 22322)
@@ -0,0 +1,294 @@
+
+IPP Subsystems Outstanding Tasks
+ - work still to be done
+ * who / effort (* : day; ** : week)
+
+Build / Config / PSLib 
+ - add static IPP, Ohana-stand-alone builds
+ * EAM +
+
+Detrend Construction
+ - tweak parameters
+ * WS ++ 
+
+Single Science Image Analysis
+
+ - determine photmetry zero point in psastro
+ * EAM +
+
+ - more robustness for psphot / psastro
+   (substantial work done; more needed)
+ * EAM ++
+
+ - psphot optimization and threading 
+   (some work done; more needed)
+ * EAM ++
+
+ - control psphot memory footprint
+ * EAM ++
+
+ - threading for ppImage
+ * PAP ++
+
+Image Combinations
+ - top-level automation and recipe tuning (extended sources ON)
+ * PAP ++
+ * JH ++
+
+ - input source lists / forced photometry
+ * at large +
+
+Magic
+ - Integration
+ * WS ++ (2x)
+
+Calibration
+ - finish outlier flagging in relphot
+ * EAM +
+
+ - measure 2D color-correction model
+ * EAM ++
+
+ - finish absphot
+ * EAM ++
+
+Postage Stamp Server
+ - finish web pages / cleanups
+ * WS ++
+
+ - magic interface / release flags / re-processing
+ * WS ++
+
+DVO
+ - optimizations for Image / Object / Detection indexes
+ * EAM ++
+
+ - various minor bugs
+ * EAM ++
+
+Output Tables
+ - PSPS : extract calibration from DVO
+ * EAM +
+
+Automation 
+ - high level automation needed
+ * at large ++
+
+OT Guiding Issues
+ - test kernel generation / application
+ * PAP ++
+
+Cleanup / Reprocess Tasks
+ oo create full pipeline
+ * WS ++
+ * JH ++
+
+Reference Catalogs
+ o validate synth.grizy
+ * EAM ++
+
+----
+
+Summary:
+EAM : 10 x ++, 3 x +
+PAP :  6 x ++, 1 x +
+JH  :  6 x ++, 1 x +
+WS  :  6 x ++, 1 x +
+
+----
+
+Completed since 2008.04:
+
+Nebulous
+ o failover automation
+ * JH ++
+
+Build / Config / PSLib 
+ o minor outstanding organizational improvements
+ * JH ++
+
+Single Science Image Analysis
+
+ o move psastro to camera stage only
+ * at large +
+
+ @ ppStack optimization (sufficient for ORR)
+ * PAP ++
+
+ @ ppSub optimization (sufficient for ORR)
+ * PAP ++
+
+Output Tables
+ o convert psphot output to MOPS input
+ * WS +
+
+ o set up summit copy server
+ * JH +
+
+Fake Source Pipeline
+ o ippTools & integration work needed
+ * PAP ++
+ * JH ++
+
+Regression Test Suite 
+ o set up repository and scripts
+ * JH ++
+
+o(almost finished!)
+Cleanup / Reprocess Tasks
+ oo create full pipeline
+ * WS ++
+ * JH ++
+
+----
+
+Not needed for ORR 
+
+Detrend Construction
+ @ visualization
+ * at large +
+ 
+ @ color-correction grid in flat-correction
+ * EAM ++
+
+Single Science Image Analysis
+ @ more output detrend application metadata needed
+ * at large +
+
+Image Combinations
+ @ more visualization / output metadata
+ * PAP +
+
+Visualization
+ @ more plots in ippMonitor
+ * at large ++
+
+OT Guiding Issues
+ @ measure video astrometry / photometry
+ * EAM ++
+
+----
+
+EAM Tasks:
+
+ o finish psphot galaxy models
+ o finish summit copy multi-DBs / test
+ - test summit copy multi-DBs
+ - Virgo PR pics
+ - Stripe 82 CFHT / 2MASS comparisons
+
+** psphot work
+ - more robustness for psphot / psastro                     (1w)
+ - determine photometry zero point in psastro		    (1d)
+ - psphot optimization and threading			    (1w)
+ - control psphot memory footprint			    (1w)
+
+** DVO work
+ - finish outlier flagging in relphot			    (1d)
+ - DVO optimizations for Image / Object / Detection indexes (1w)
+ - various minor DVO bugs				    (1w)
+ - measure 2D color-correction model			    (1w)
+ - color-correction grid in flat-correction		    (1w)
+ - finish absphot                                           (1w)
+
+** other tasks
+ - PSPS output tables : extract calibration from DVO	    (1d)
+ - measure video astrometry / photometry		    (1w)
+ - validate synth.grizy					    (1w)
+
+PAP Tasks:
+
+ - finish ppStack / ppSub tweaking (A - B is still incomplete)
+ - stack / diff more visualization / output metadata
+ - fake source pipeline : (ppSub and ppStack versions)
+ - test kernel generation / application
+ o convert psphot output to MOPS input (to IPP DS)
+ o minor ippconfig outstanding organizational improvements
+   (early May)
+ - stack / diff top-level automation and recipe tuning (extended sources ON)
+ - threading for ppImage
+ - ppStack optimization (some done, close to goal for now)
+ - ppSub optimization (some done, close to goal for now)
+
+JH Tasks:
+
+ - set up OTIS DB copy server 
+ - monitor summit copy [EAM : finish summit copy multi-DBs]
+ - set up regression suite repository and scripts 
+ - Nebulous failover automation (< Apr 21)
+ - ippTools for stack / diff automation [needs input from PAP & EAM]
+ - ippTools for fake source pipeline [needs pipeline layout]
+ - cleanup / reprocess pipeline(s) [needs conceptual design]
+
+WS Tasks:
+
+ - test flat-field correction suite
+ - detrend analysis tweak parameters (megacam -> gpc1)
+ - Magic integration
+ - postage stamp server : finish web pages / cleanups
+ - postage stamp server : magic interface / release flags / re-processing
+ - cleanup / reprocess pipeline 
+
+----
+
+IPP Status 2008.04.01
+
+Since about mid-November, we have been putting an increasing amount of
+our time into testing portions of the IPP on large collections of data
+(GPC1, Megacam, Essence, SDSS). This has slowed progress on some areas
+of development, but had the important benefit of uncovering a number
+of issues which needed to be addressed.  Some of these were basic
+coding bugs uncovered by real data.  
+
+One important area of concern uncovered in the test processing is the
+robustness of the psphot and psastro algorithms.  While we have
+addressed some of these problems, more work is still needed here,
+especially for cases where the input data is somewhat marginal.  One
+of our recent conclusions is that we need to isolate the photometry
+analysis (psphot), which can take a long time but more consisently
+succeeds, from the astrometry analysis, which is still more
+fragile. We have decided to remove the astrometry analysis from the
+chip processing stage, and only perform this analysis at the camera
+processing stage.  This will save a lot of time in error handling for
+the science image analysis.
+
+Another area which has caused on going error-handling effort is in the
+parameter configuration system.  We have decided to clean up the
+organization somewhat so that more of the parameter files can
+be shared by all cameras, reducing the duplication of information and
+making it easier to synchronize changes to the configurations.
+
+Paul and I had a detailed discussion this morning about the current
+IPP Status and what development work is still outstanding.  I have
+listed our detailed notes below, grouped by the related IPP Subsystem.
+There are three significant components that need substantial work:
+
+* Magic integration
+
+* Fake Source Pipeline
+
+* Cleanup / Reprocess suite
+
+These are each about 2 weeks of dedicated effort.  The elements of the
+first two of these systems are in place, but we need to finish the
+integration process into the full pipeline.  The Cleanup / Reprocess
+suite is a subsystem which we have been deferring, but for which we
+will soon need to begin work.
+
+In addition, we have several areas where we need to work on
+improvements to the code, or to finish minor additional features
+needed by the pipeline.  Some of these are:
+
+* improvements to the code robustness, particularly for psphot and
+  psastro which are not deterministic.
+
+* optimization work for psphot, ppStack, and ppSub, including
+  multithreading.
+
+* various improvements to the photometry and astrometry calibration
+  analysis, from the measurement of the zero-points in real-time to
+  the application of the calibrations to the output tables to be
+  delivered to PSPS and MOPS.
+
+----
+
Index: /tags/sj_tags/sj_root_20080929/doc/plan/ipp.status.2008.08.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/plan/ipp.status.2008.08.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/plan/ipp.status.2008.08.txt	(revision 22322)
@@ -0,0 +1,332 @@
+
+I have organized this evaluation into 4 sections.  The first section
+lists the work remaining to be done as a collection of tasks, with an
+assigned person and an amount of time.  In the second section, I list
+code milestones, with a summary of the related tasks.  These are
+labeled as 'ipp 2.7', etc, but we may opt not to make an official
+release of the software suite at these times: the additional support
+load is not justified.  In the third section, I give a list of
+sequential Pre-ORR data analysis milestones.  In the fourth section, I
+give a schedule, primarily detailing the coding tasks, but with the
+data analysis goals in [].
+
+----------------------------------------------------------------------------------
+
+IPP Pending Tasks
+
+Build / Config / PSLib 
+
+ * IPP 2.6 release
+ - PAP : 0.5 day
+
+Data Management
+
+ * finish 'cleanup' analysis for all stages
+ - EAM : 1 days  (ipp 2.7)
+
+ * output config files / verfy re-run
+ - PAP : 2 days  (ipp 2.7)
+
+ * 'update' analysis code for chip stage
+ - EAM : 2 days  (ipp 2.7)
+
+ * 'update' analysis for other stages
+ - EAM : 2 days  (ipp 2.7)
+
+Detrend Construction Software
+ 
+ * ppMerge threading
+ - EAM : 1 day (ipp 2.7)
+
+ * correct detrend normalization bug
+ - EAM : 0.25 day  (ipp 2.7)
+
+ * full-featured detrend construction simtest suite
+ - PAP : 1 day  (ipp 2.8)
+ 
+ * flat-field correction simtest suite
+ - JHU : 1 day  (ipp 2.8)
+ 
+ * flat-field correction demonstration with Megacam data
+ - JHU : 3 days (2 of background)
+ 
+ * flat-field correction demonstration with GPC1 data
+ - EAM / JHU : 3 days (2 of background) 
+
+  * Determine & Apply OT shifts
+  - PAP : 3 days (ipp 2.9) [requires complete OT data from GPC1]
+
+Single Science Image Analysis
+
+ * measure photmetry zero point in psastro
+ - EAM : 0.5 day (ipp 2.7)
+
+ * psphot threading
+ - EAM : 3 days (ipp 2.7)
+
+ * ongoing psphot shakeout
+ - EAM : 5 days (ipp 2.8)
+
+ * threading for ppImage (ex. psphot)
+ - EAM : 0.5 days (ipp 2.7)
+
+ * simtest demo of fake sources and forced photometry
+ - PAP : 2 days (ipp 2.8)
+
+ * generate summit Q/A feedback
+ - WS  : 3 days (ipp 2.9)
+
+Image Combinations
+
+ * ppStack threading
+ - PAP : 2 days (ipp 2.7)
+
+ * ppSub threading
+ - PAP : 2 days (ipp 2.7)
+
+ * Magic integration
+   * ippTasks (0.5 day)
+   * ippScipts (0.5 day)
+ - PAP : 1 day (ipp 2.7)
+
+ * Magic GPC1 testing 
+ - PAP : 5 days
+ 
+ * stack extended source photometry
+ - EAM : 5 days
+
+Calibration
+
+ * finish outlier flagging in relphot
+ - EAM : 2 days (ipp 2.9)
+
+ * 2D color-correction model (relphot)
+ - EAM : 3 days (ipp 2.9)
+
+ * finish absphot
+ - EAM : 3 days (ipp 2.9)
+
+Postage Stamp Server
+
+ * detectability analysis tool (image, RA, DEC -> psf Q factor)
+ - EAM : 0.5 day (ipp 2.8)
+
+ * add additional required metadata to output images
+ - WS : 2 day (ipp 2.8)
+
+ * add image lookup to detectability request 
+ - WS : 1 day (ipp 2.8)
+
+ * add weight / mask / cmf lookups to ps requests
+ - WS : 1 day (ipp 2.8)
+
+ * magic interface / release flags
+ - WS : 2 days (ipp 2.8)
+
+ * pending re-processing mode
+ - WS : 3 days (ipp 2.8)
+
+ * spec / re-design postage stamp web pages
+ - WS : 2 days (ipp 2.9)
+
+ * implement postage stamp web pages
+ - WS : 5 days (ipp 2.9)
+
+DVO
+
+ * validate Image / Object / Detection IDs
+ - EAM : 1 day (ipp 2.8)
+
+ * optimizations for Image / Object / Detection indexes
+ - EAM : 2 days (ipp 2.9)
+
+ * extend vectors to double and int types
+ - EAM : 0.5 day (ipp 2.9)
+
+ * convert time fields to unsigned int
+ - EAM : 0.5 day (ipp 2.9)
+
+ * function name cleanups
+ - EAM : 0.5 day (ipp 2.9)
+
+ * use partial loads for catalogs 
+ - EAM : 1 day (ipp 2.9)
+
+ * addstar -refcat -update mode
+ - EAM : 1 day (ipp 2.9)
+
+ * DVO replication tools
+ - EAM : 2 days (ipp 2.9)
+
+Output Tables
+
+ * generate PSPS P2 output tables
+ - WS : 2 days (ipp 2.9)
+
+ * generate PSPS Stack output tables
+ - WS : 2 days (ipp 2.9)
+
+ * generate PSPS Diff output tables
+ - WS : 2 days (ipp 2.9)
+
+ * generate PSPS output tarballs
+ - WS : 2 days (ipp 2.9)
+
+ * generate MOPS output tarballs
+ - PAP : 2 days (ipp 2.9)
+
+ * output CMF file documentation
+ - WS : 1 day
+
+IPP ORR Demonstration Analysis (post-camera refurb)
+
+ * Generate Starting Master Dark [full grid of darks vs temp & exptime]
+ - WHO : 2 days
+
+ * Generate Starting Master Flat [several nights of twilight flats]
+ - WHO : 2 days
+
+ * Manual Mask additions
+ - WHO : 3 days
+
+ * Generate Flat-field Correction [flat-field correction dithers for each filter]
+ - WHO : 3 days
+
+ * Demonstrate Photometric consistency [Stripe 82 / Stripe 10, second-pass dither]
+ - WHO : 3 days
+
+ * Demonstrate deep stack [MD example field]
+ - WHO : 3 days
+
+----------------------------------------------------------------------------------
+
+IPP Coding Milestones
+
+* ipp 2.7 -- 2008.08.15
+  * cleanup for all stages
+  * fix flat-field normalization bug
+  * update for all stages
+  * psastro measure zero point
+  * threading for pswarp, ppMerge, ppImage, ppStack, ppSub
+  * magic integration
+  *** EAM : 10.25d; PAP : 7d
+
+* ipp 2.8 -- 2008.08.29
+  * flat-field correction simtest suite
+  * psphot improvements
+  * fake source / forced photometry simtest suite
+  * detrend construction simtest suite
+  * pstamp ex. WWW
+  *** EAM : 6.5d; PAP : 4; WS 9d (no ipp 2.7 milestones)
+
+* ipp 2.9 -- 2008.
+  * pstamp WWW
+  * summit Q/A feedback
+  * PSPS output tables
+  * PSPS & MOPS output tarballs
+  * DVO updates
+  *** EAM : 15.5d; WS : 18d
+
+----------------------------------------------------------------------------------
+
+IPP GPC1 Pre-ORR Analysis Milestones
+
+* New Uncorrected Master Flat-field Images (using current best mask)
+  * ppMerge threading
+  * fix flat-field normalization bug
+  * 'validate' run with existing flats
+  *** possible concern: non-linearity of devices
+
+* Corrected Master Flat-field Images
+
+* Single-Image cell-to-cell background variation < 1%
+
+* Single-Image Photometric residuals < 2%
+
+* Single-Image Astrometric residuals < 20mas
+
+* Demo GPC1 Stacks (best-effort flats)
+
+* Clean GPC1 Stacks (minimal / no background residuals, PSF convolution applied)
+
+* Clean GPC1 Difference Image Set (< XXX false positive detections)
+
+* Demo GPC1 Magic Analysis (minimal quality inputs)
+
+* Clean GPC1 Magic Analysis (high quality inputs)
+
+----------------------------------------------------------------------------------
+
+Goals for the weeks of:
+
+07.27
+  EAM : (ppMerge threading), ppImage threading, (flat-field normalization bug), [finish demo science run]
+  PAP : (ipp 2.6 release) (07.30), (finish magic scripts & tasks), output config files, [MOPS Megacam run 2a]
+  WS  : add'l output metadata, image lookup in det., weight, mask, cmf lookups, [monitor summit copy]
+  BG  : demo automatic (background) simtest
+
+08.03 : 
+  EAM : pcontrol comms crash, finish 'cleanup' for all stages; 'update' for chip stage, psastro zp [GPC1 detrend validate run]
+  PAP : ppStack threading, ppSub threading [MOPS Megacam run 2a]
+  WS  : magic interface issues, re-processing task, [monitor summit copy]
+ JHU  : flat-field correction simtest
+  BG  : set up cronjob for background simtest
+  JH  : documentation / bugzilla cleanup / static builds
+**MH  : psphot tests of crowded fields
+
+08.10 : 
+  EAM : 'update' for all stages, psphot threading [GPC1 improved flat-fields]
+  PAP : finish ppStack/ppSub threading & tests, ipp 2.7 release tarball (08.15) [skip release?]
+  WS  : PSPS P2 output tables, PSPS Stack output tables, [monitor summit copy]
+ JHU  : flat-field correction simtest / prepare for Megacam flatcorr demonstration 
+  BG  : design system to test simtest success / failure
+  JH  : documentation / bugzilla cleanup
+**MH  : psphot tests of crowded fields
+
+08.17 : 
+  EAM : psphot shakeout, detectability query [GPC1 flat-field correction run]
+  PAP : fake & forced simtest
+  WS  : (vacation)
+ JHU  : Megacam flatcorr demonstration 
+**MH  : psphot tests of crowded fields
+
+08.24 : 
+  EAM : more psphot shakeout, validate IDs -> DVO
+  PAP : ipp 2.8 release (08.29), [GPC1 Stacks]
+  WS  : (vacation)
+
+08.31
+  EAM : 2D color-correction in relphot, GPC1 Photometry validation
+  PAP : detrend construction simtest, [GPC1 Stacks]
+  WS  : PSPS Diff output tables, PSPS output tarballs, [monitor summit copy]
+
+09.07
+  EAM : DVO minor fixes, [GPC1 Photometry & Astrometry validation]
+  PAP : [GPC1 Stacks & Difference Images]
+  WS  : spec/design pstamp www, implement pstamp www
+
+09.14
+  EAM : extended source photometry [GPC1 Photometry & Astrometry validation]
+  PAP : [GPC1 Stacks & Difference Images + Magic Analysis]
+  WS  : continue with pstamp www
+
+09.21
+  EAM : DVO ID optimizations, DVO replication
+
+09.28
+  EAM : relphot outlier flagging, absphot
+
+10.05
+10.12
+10.19
+10.26
+11.02
+11.09
+11.16
+11.23
+11.30
+12.07
+12.14
+12.21
+12.28
+
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.log *.dvi *.aux *.toc *.log *.out *.lof *.tbr *.tbd *.pdf
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/ChangeLogADD.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/ChangeLogADD.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/ChangeLogADD.tex	(revision 22322)
@@ -0,0 +1,76 @@
+
+\subsection{Changes from version 06 (7 September 2004) to version 07 (24 November 2004)}
+
+\begin{itemize}
+\item Reworked discussion about lookup tables for \code{psTime}.
+\item Moved discussion of lookup tables for $\Delta$UT, $x_p$ and
+  $y_p$ into the SDRS, along with more thorough specification.
+\item Added discussion about using errors in calculating statistics.
+\item Fixed up LMM to be specific to $\chi^2$ fitting.
+\item Generalised entry on Gaussian smoothing.
+\item modifications to the document name and PSDC number (and refs).
+\end{itemize}
+
+\subsection{Changes from version 07 (24 November 2004) to version 08 (8 January 2005)}
+
+\begin{itemize}
+\item Added short section on histograms in the presence of errors.
+\item Added short note on inverse spherical transformations.
+\item Added section on astronomical object models
+\end{itemize}
+
+\subsection{Changes from version 08 (8 January 2005) to version 09 (14 February 2005)}
+
+\begin{itemize}
+\item Added section on inverse and combined transformations.
+\item Added \code{PS_RESAMPLE_LANCZOS[234]}, dropped
+  \code{PS_RESAMPLE_LAGRANGE}.
+\item Added section on FITS WCS.
+\end{itemize}
+
+\subsection{Changes from version 09 (14 February 2005) to version 10 (19 April 2005)}
+
+\begin{itemize}
+\item Changes to the Time section:
+\begin{itemize}
+\item Renamed TDT to TT
+\item Misc. cleanups and clarifications
+\item Added references
+\item Updated definitions of UTC, UT1, JD, and MJD
+\item Verbatim Perl code for YMD$\rightarrow$sec and sec$\rightarrow$YMD conversion
+\item Verbatim Fortran code for UT1 interpolation
+\end{itemize}
+
+\item section reorganization:
+\begin{itemize}
+\item renamed Astronomical Image Manipulations to Image Manipulations
+\item moved Image Manipulations to own subsection before Astronomy Utilities 
+\item promoted all PSLib subsections to sections
+\end{itemize}
+\item defined earth orientation algorithms
+\item provided quaternions for rotations
+\item moved some sections to reflect order in SDRS (matrix, fftw)
+\end{itemize}
+
+\subsection{Changes from version 10 (19 April 2005) to version 11 (27 April 2005)}
+
+\begin{itemize}
+\item fixed some typos in the definition of the rotation from CEO to GCRS (Eqn~\ref{CEOtoGCRS}).
+\item added references to the SDRS APIs for the Earth Orientation section
+\end{itemize}
+
+\subsection{Changes from version 11 (27 April 2005) to version 12 (11 July 2005)}
+
+\begin{itemize}
+\item Removed all references to slalib.
+\item Updated section on rotations.
+\item Divided the robust statistics into robust and fitted statistics,
+modified the algorithm somewhat. 
+\item Clarifies the LM minimization coding, added gain-factor test description
+\end{itemize}
+
+\subsection{Changes from version 12 (11 July 2005) to present}
+
+\begin{itemize}
+\item Added complete derivation of rotated ellipse representations
+\end{itemize}
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/ChangeLogSDRS.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/ChangeLogSDRS.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/ChangeLogSDRS.tex	(revision 22322)
@@ -0,0 +1,933 @@
+%%% $Id: ChangeLogSDRS.tex,v 1.213 2006-12-09 00:31:16 eugene Exp $
+
+\subsection{Changes from version 00 to version 01}
+
+\begin{itemize}
+\item cosmetic re-organizations
+\item specified data types valid for psVector and psImage functions
+\item discussion of external libraries
+\item minor discussion of threads
+\item memory callback routine names changed from ...CB to ...Callback
+\item added discussion of freeing/dereferencing components of structures
+\item renamed psPrintTraceLevels -> psTracePrintLevels, added prototype
+\item added psTraceSetDestination
+\item changed psVLogMsg to psLogMsgV
+\item changed psSetLogDestination to psLogSetDestination
+\item changed psLogSetDestination destination argument to type char *
+  extended output target concept
+\item changed psSetLogFormat to psLogSetFormat
+\item extended the concept of psError to define an error stack
+\item added psErrorGet, psErrorLast, psErrorClear
+\item defined psErr structure
+\item added psErrorStackPrint \& psErrorStackPrintV
+\item added psErrorCodeString
+\item added psErrorRegister
+\item defined psErrorDescription
+\item dropped psStringCopy \& psStringNCopy
+\item changed naming scheme for psElemType to PS\_TYPE\_F32 format
+\item dropped psFLoatArray, psIntArray, psDoubleArray, psComplexArray, 
+  psVoidPtrArray, and associated functions
+\item defined psVector, psVectorAlloc, psVectorRealloc, psVectorFree
+\item changed naming scheme for psImage union (rows.rows\_f32 -> data.F32)
+\item added psElemType entry to psImageAlloc and psVectorAlloc
+\item added 'which' argument to psDlistSetIterator, psDlistGetNext, psDlistGetPrev
+\item added psDlistSort function
+\item changed 'bitmask' to 'bitset' 
+\item added psBitsetNot function
+\item changed output of psSort to psVector
+\item changed psArrayStats to psVectorStats
+\item dropped robustMeanNvalues, etc from psStats
+\item added robustN50, robustNfit, binsize to psStats
+\item reduced available options values (psStatsOptions)
+\item simplified psHistogram, psHistogramAllocGeneric
+\item changed psGetArrayHistogram to psHistogramVector
+\item moved Matrix section after Images
+\item moved FFT section after Images
+\item replaced psMatrixOp with psMatrixMultiply
+\item changed FFT function names to have the forms psVectorFFT and psImageFFT
+\item replaced the forward and reverse version with an argument to ps...FFT
+\item added ps...Real, ps...Imaginary, ps...Complex, ps...Conjugate
+\item changed psEvalPolynomial1D to psPolynomial1DEval, (and equivalents)
+\item added 'normal' argument to psGaussian
+\item added psGaussianDev
+\item modified argument lists of psMinimize and psMinimizeChi2
+\item changed psGetVectorPolynomial to psVectorFitPolynomial1D
+\item dropped psImageFreeChildren
+\item modified union naming scheme in p\_psScalar to data.S32 format
+\item added Dates and Times functions:
+\item modified union naming scheme in psMetadataType to data.S32 format
+\item modified psMetadataType to use S32, etc types
+\item changed arguments of psMetadataItemAlloc to use stdargs to get data value
+\item added psMetadataItemAllocV
+\item modified psMetadataAppend arglist to match psMetadataItemAlloc
+\item changed psImageReadHeader to psMetadataReadHeader (\& Fread)
+\item moved psMetadataReadHeader to metadata section
+\item split psCoord into psPlane and psSphere
+\item renamed psCoordXform to psPlaneTransform
+\item renamed psDistortion to psPlaneDistortion 
+\item defined psSphereTransform, ..Alloc, ..Free
+\item changed psCoordTransform to psSphereTransformApply
+\item delete psCoordinatesItoE \& equivalents
+\item added psShereTransformItoE \& equivalents
+\item defined psProjection, psProjectionType
+\item modifed args to psProject, psDeproject
+\item changed psGetOffset, psApplyOffset to psSphereGetOffset, psSphereSetOffset
+\item dropped overscan entry from psReadout
+\item dropped grommit from psExposure
+\item added psGrommitAlloc, psGrommitFree
+\item changed args to psCoordsSkyToCell \& psCoordsSkyToCellQD
+\item modified argument lists for most psCoord conversion functions
+\item renamed psGetSun, psGetMoon functions to psSunGet, psMoonGet
+\item added ps...GetRise, ps...GetSet, psNightLength
+\item added psTimeToLunation, psLunationToTime
+\item moved some naming issues out of this document, to IPP SRS
+\item cleaned metadata discussion
+\item change psLog date format to use hyphens
+\item change Configuration File Grammar to allow periods in names and strings that begin with non-word characters
+\end{itemize}
+
+\subsection{Changes from Revision 01 (19 May 2004) to 02 (22 June 2004)}
+
+Changes consisted of incorporating feedback from Bugzilla problem
+reports (up to bug number 64).
+
+\subsection{Changes from Revision 02 (22 June 2004) to 06 (19 August 2004)}
+
+\begin{itemize}
+\item \code{libTAI} no longer used, since it does not perform as desired.
+\item Addition of \code{previousBlock}, \code{nextBlock},
+  \code{freeFcn}, \code{userMemorySize}, \code{refCounterMutex} to
+  \code{psMemBlock}.
+\item Added behavior of memory management when \code{PS_MEM_DEBUG} is
+  defined at compile time.
+\item \code{psMemExhaustedSetCallback} $\rightarrow$
+  \code{psMemExhaustedCallbackSet}.
+\item Added \code{p_psSetFreeFcn} and \code{p_psGetFreeFcn}.
+\item \code{psMemFreeIDSet}, \code{psMemAllocateIDSet} $\rightarrow$
+  \code{psMemFreeCallbackIDSet}, \code{psMemAllocateCallbackIDSet}.
+\item \code{psMemCheckCorruption} now takes a \code{bool} instead of
+  \code{int}.
+\item Specification that destructors are private functions which are
+  called by \code{psFree}.
+\item \code{psErrorRegisterSet} $rightarrow$ \code{psErrorRegister}.
+\item Added a type, \code{PS_TYPE_C64}.
+\item Added section on ``Simple Scalars'' for math operations,
+  including functions \code{psScalarAlloc} and \code{psScalarCopy}.
+\item \code{psVector} no longer carries a \code{void *} type, but now
+  has a \code{psC64} type.  The \code{void *} carrier type is
+  \code{psArray}.
+\item \code{psImage} supporting functions are valid for types
+  \code{psS8, psS16, psU8, psU16, psF32, psF64, psC32, psC64}.
+\item Section on ``Simple Arrays'' added --- an ordered collection of
+  unspecified data elements.
+\item \code{psDList} renamed \code{psList}.
+\item In \code{psList}, \code{n} changed to \code{size}.
+\item \code{psListAdd} and \code{psListRemove} now return a
+  \code{bool}.
+\item Retrieving data from the list means that the reference counter
+  is incremented.
+\item \code{psListFree} frees the list and calls \code{psFree} on all
+  the data on the list.
+\item \code{psListSort} prototype updated.
+\item \code{psHashInsert} renamed \code{psHashAdd}, now returns a
+  \code{bool} and frees existing data for the given key.
+\item \code{psHashRemove} now returns a \code{bool}.
+\item \code{psSort} renamed \code{psVectorSort}, and specified for
+  type \code{psS8}, instead of \code{psU8}.
+\item \code{psSortIndex} renamed \code{psVectorSortIndex}.
+\item \code{psVectorStats} parameters reordered.
+\item \code{psStats} does not revert to robust statistics for large
+  vectors; \code{PS_STAT_ROBUST_FOR_SAMPLE} dropped.
+\item \code{psHistogram.uniform} changed to type \code{bool}.
+\item \code{psHistogramVector} renamed \code{psVectorHistogram}, and
+  takes a \code{mask} and \code{maskVal}.
+\item \code{psPolynomial} now incorporates both general polynomials
+  and Chebyshev polynomials, with the option being specified by an
+  enumerated type in the structure, which is also passed to the
+  constructor, \code{psPolynomialAlloc}.  This will necessitate
+  changes to the evaluators as well.
+\item Vector versions of polynomial evaluators, e.g.\
+  \code{psDPolynomial2DEvalVector} defined.
+\item Splines added (\code{psSpline1D}), with corresponding
+  allocators, single and vector evaluators, and fit to a vector.
+\item \code{psGaussian} takes \code{bool normal}, instead of \code{int normal}.
+\item Specified minimization routines in much more detail.  Now have
+  both LM and Powell minimizers specified, with a $\chi^2$ minimizer
+  using the Powell minimizer, and a couple of functions to use to
+  minimize $\chi^2$ with the LM minimizer.
+\item Function \code{psImageSubsection} added.
+\item Behavior of \code{psImageCopy} specified in the event \code{out == NULL}.
+\item Function \code{psImageTrim} added.
+\item Defined \code{psImageCutDirection}, and added one to
+  \code{psImageSlice}.
+\item \code{mask} and \code{maskVal} added to \code{psImageCut}, along
+  with pointer to the algorithm in the ADD.
+\item \code{mask} and \code{maskVal} added to \code{psImageRadialCut},
+  along with pointer to the algorithm in the ADD..
+\item \code{mask} and \code{maskVal} added to \code{psImageRebin}, and
+  specified in more detail.
+\item Added function \code{psImageResample}, along with
+  \code{psImageResampleMode}.
+\item \code{psImageRotate} has new \code{float exposed} parameter.
+\item \code{psImageGetStats} renamed \code{psImageStats}, and must be
+  defined for \code{psS8} (not \code{psU8}).
+\item \code{psImageHistogram} now takes a \code{mask} and
+  \code{maskVal}.
+\item \code{psImageHistogram}, \code{psImageFitPolynomial},
+  \code{psImageEvalPolynomial} must be defined for \code{psS8} (not
+  \code{psU8}).
+\item Added function \code{psImagePixelInterpolate}.
+\item Behavior of \code{psImageReadSection, psImageWriteSection}
+specified in more detail.
+\item \code{psImageClip} parameters now specified to be \code{double},
+  and behavior specified in more detail.
+\item Function \code{psImageClipComplexRegion} added.
+\item \code{psImageClipNaN} behavior specified for complex images.
+\item \code{psBinaryOp} and \code{psUnaryOp} described in more detail.
+\item \code{psVectorTranspose} removed.
+\item Added section on convolution, including defining \code{psKernel},
+  with corresponding constructor and generation function.
+\item Section on times (\code{psTime} completely reworked.  In
+  particular, \code{libTAI} is no longer used.
+\item \code{psMetadataAppend} and \code{psMetadataAppendItem} are now
+  \code{psMetadataAdd} and \code{psMetadataAddItem}, returning
+  \code{bool}s.  Each is specified in a bit more detail.
+\item \code{psMetadataRemove} takes a \code{where} parameter, and
+  is specified in a bit more detail.
+\item Added function \code{psMetadataGet}.
+\item Iterating on the metadata described in more detail.
+\item \code{psMetadataItemPrint} changed.
+\item In distortions, the third and fourth parameters consistently
+  referred to now in the order color and then magnitude.
+\item Pre-defined spherical transforms renamed to longer, but more
+  meaningful names.
+\item GLS projection dropped.
+\item Offsets specified in more detail, with different modes
+  (\code{psSphereOffsetMode}) and units (\code{psSphereOffsetUnit})
+  instead of a single \code{type}.
+\item \code{out} parameter in \code{psCellInFPA, psChipInFPA,
+  psCellInChip} removed.
+\item \code{color, mag} parameters added to functions that transform
+  between FPA and tangent plane (\code{psCoordFPAToTP, psCoordCellToSky, psCoordTPToFPA, psCoordSkyToCell}).
+\item Specified constructors for the astronomy images and astrometry
+  structures.
+\item \code{psExposure} contains a \code{psTime *time} instead of
+  \code{mjd}.
+\item \code{psFPA} contains a \code{psGrommit} composed from the
+  relevant \code{psExposure} for assistance in astrometric transforms.
+\item Added structure \code{psObservatory}, which contains observatory
+  information.  \code{psExposure} contains a \code{psObservatory}.
+\item \code{wavelength} added to \code{psExposure}.
+\item \code{psCell.cellToSky} renamed to \code{psCell.toTP} and now
+  only goes to the tangent plane, from which the \code{psGrommit} is
+  used to get the coordinates to the sky.
+\item Added description of \code{psMemory} functions
+  \code{p_psSetFreeFcn}, \code{p_psGetFreeFcn}
+\item Modified definition of \code{psImagePixelInterpolate}.
+\item Renamed \code{psImageResampleMode} mode to
+  \code{psImageInterpolateMode}.
+\item Added \code{psImageInterpolateMode} mode to
+  \code{psImageRotate}, \code{psImageShift}.
+\item Changed input type of \code{exposed} pixels to \code{psC64}.
+\item Dropped maintaince of original type for \code{psImageCopy}.
+\item Added output \code{psVector *coords} to \code{psImageSlice}.
+\item Dropped \code{psMetadataFlags} from \code{psMetadataItem}.
+\item Cleaned \code{psMetadata} discussion to reflect new handling of
+  non-unique keys.
+\item Clarified sequence of entries in \code{psMetadataItemAlloc}.
+\item Dropped metadata naming convention reference (should be part of
+  IPP docs, not PSLib)
+\item \code{Nchildren} $\rightarrow$ \code{nChildren} in
+  \code{psImage}.
+\item Changed names of \code{p_psSetFreeFcn}, \code{p_psGetFreeFcn} to
+  \code{p_psMemSetDeallocator}, \code{p_psMemGetDeallocator}.
+\item Added \code{psFreeFnc} parameter to
+  \code{p_psMemSetDeallocator}.
+\item \code{long psMemGetId(void)} to \code{psMemoryId
+  psMemGetId(void)}.
+\end{itemize}
+
+\subsection{Changes from Revision 06 (19 August 2004) to Revision 07 (7 September 2004)}
+
+\begin{itemize}
+\item Removed \code{psTimeFromLST}, changed parameters and output for
+  \code{psTimeToLST}.
+\item Added function \code{psTimeLeapSeconds} to calculate number of
+  leap seconds between two dates, required for time arithmetic.
+\item Changed \code{psImageCut} to do a slice through the data in an
+  arbitrary direction, instead of merging the data along a particular
+  dimension.
+\item added \code{psVector *coords} to the API for \code{psImageCut}.
+\item Added \code{psLibVersion} (bug 156).
+\item Added specification of iterator to \code{psMetadataSetIterator}.
+\item Replaced \code{which} and \code{where} in \code{psMetadata} and
+  \code{psList} functions with \code{iterator} and \code{location},
+  respectively, in order to reduce confusion.
+\item Changed the order of some \code{psList} function arguments to
+  match that of \code{psMetadata}.
+\item \code{psLogSetDestination} return type changed to \code{bool}.
+\item \code{psImage.nChildren} and \code{.children} changed to
+  \code{psArray *children}.
+\item Added extra input parameters to \code{psKernelGenerate}:
+  \code{psVector *tShifts} and \code{bool relative}.
+\item Synched \code{psImageSubset}, \code{psImageTrim},
+  \code{psImageReadSection} and \code{psImageSlice} to use a
+  consistent region specification: \code{x0,y0,x1,y1}, where \code{x1}
+  and \code{y1} are exclusive, and may be negative.
+\item Added \code{psTime} arguments to
+  \code{psSphereTransformICRSToEcliptic} and
+  \code{psSphereTransformEclipticToICRS}.
+\item Added \code{bool} type to \code{psMetadata}.
+\item TBD-ed the Astronomical Objects section and the photometry
+  section.
+\item Added \code{psLibInit}.
+\item Updated \code{psReadout,Cell,Chip,FPA} to synchronise with
+  MHPCC, in particular use of \code{psArray}.
+\item \code{psImageTrim} is to free children.
+\item Specified the operators for \code{psBinaryOp} and
+  \code{psUnaryOp}.
+\item Updated types for \code{psVectorStats},
+  \code{psImageReadSection} and \code{psImageWriteSection}.
+\item Added constructors for \code{psPlaneTransform} and
+  \code{psPlaneDistort}.
+\item Updated description of behavior of \code{psFree} (doesn't barf
+  if \code{refCounter} is not 1.
+\item Clarified behavior of \code{psLogSetDestination} (no need to
+  specify a protocol for \code{stderr,stdout,none}).
+\item Leading dot in facility name for \code{psTrace} is optional.
+\item Specification of configuration file format and parse function,
+  \code{psMetadataParseConfig}.
+\item Added new function \code{psSpherePrecess}.
+\end{itemize}
+
+\subsection{Changes from Revision 07 (7 September 2004) to Revision 08 (12 October 2004)}
+
+\begin{itemize}
+\item Changed format of \code{psLogMsg}, following bug 189: format now has multiple lines.
+\item Added configuration file grammar.
+\item Added explanation of \code{psArray *coords} in \code{psMinimizeLMFunc}, and changed \code{psMatrix} to
+  \code{psImage} in response to bug 191.
+\item Added \code{psTimeTable}.
+\item \code{psLibInit} altered to load \code{psTime} configuration
+  file.
+\item Reworked \code{psList} (including addition of new functions) to
+  support multiple iterators.
+\item Specified return value for \code{psImageOverlaySection} is the
+  number of pixels overlaid.
+\item Added \code{persistent} to \code{psMemBlock}, along with
+  additional specification of behaviour of \code{psMemCheckLeaks}.
+\item Removed \code{psListRemoveNext} and \code{psListRemoveAfter}.
+  Removing an item pointed to by an iterator doesn't cause an error.
+\item Added \code{psRectangle}
+\item Added \code{PS_TYPE_BOOL} to \code{psElemType}.
+\item Dropped \code{PS_TYPE_OTHER}
+\item Replaced \code{PS_META_IMG} with \code{PS_META_MATH}
+\item Replaced \code{PS_META_ITEM_SET} with \code{PS_META_LIST}
+\item Added \code{psElemType} to \code{psMetadataItem} to specify primitive data types.
+\item Cleaned up the entries in \code{psMetadataType}.
+\item Moved the \code{psList} entry (item set, now list) to the union.
+\item Added a \code{psMetadata} entry to the union.
+\item Added typing contruct and hierarchy to \code{psMetadataParseConfig}
+\item Added FITS I/O section 
+\item Moved \code{psImageReadSection} and \code{psImageWriteSection} to FITS I/O Functions section and modified names.
+\item Moved \code{psMetadataReadHeader} and \code{psMetadataFReadHeader} to FITS I/O Functions section and modified names.
+\item Fixed typo: \code{p_psScalar} to \code{psScalar}
+\item Renamed \code{psMetadata.data.void} to \code{psMetadata.data.data} to be consistent with \code{psList}, \code{psHash} 
+\item Require comment mark in 'multiple' metadata config entry for comments
+\item Changed return values for \code{psMetadataParseConfig}
+\item Added \code{psTimeAlloc}.
+\item Adding \code{errors} to \code{psVectorStats}.
+\item Adding \code{psRandom}, with three distributions (uniform,
+  Gaussian, Poisson).  This obsoletes \code{psGaussianDev}, and
+  impacts \code{psLibInit} (no longer needs to seed the RNG).
+\item Changed \code{psImageRotate} to use radians instead of degrees.
+\end{itemize}
+
+\subsection{Changes from Revision 08 (12 October 2004) to Revision 09 (15 November 2004)}
+
+\begin{itemize}
+\item Updated \code{psSphereTransform}, according to bug 116.
+\item Removed mention of \code{libTAI} in the introduction.
+\item Fixed \code{psVectorHistogram} prototype to include \code{errors}.
+\item In \code{psVectorStats} and \code{psVectorHistogram}, the \code{errors}
+vector must be of the same type as the input values vector.
+\item Added \code{psLookupTable} section, and removed
+  \code{psTimeTableInterpolate} (now redundant).
+\item \code{psTimeAdd} and \code{psTimeSubtract} merged to
+  \code{psTimeMath}.  Specified that time differences are expressed in
+  SI seconds (i.e., not including leap seconds).  Inputs to
+  \code{psTimeMath} and \code{psTimeDelta} no longer need to be TAI
+  --- the functions will convert from and to UTC as required.
+\item modified the document name and PSDC number (and refs).
+\item cleaned up the FITS I/O functions: defined Alloc and Move functions, only use psFits for I/O.
+\item dropped deprecated psMetadataReadFits
+\item replaced \code{PS_META_ITEM_SET} with \code{PS_META_LIST}
+\item clarified use of x in psVectorFitSpline
+\item unified the form of evaluation functions for polynomials and splines
+\item clarified use of errors in stats function
+\item Changed return type of \code{psTraceReset}, \code{psLogSetFormat}, \code{psMetadataSetIterator}, \code{psMetadataItemPrint} to return type \code{bool}, so that errors won't be lost (bug 161).
+\item \code{psImageRebin} input types restricted; bug 198.
+\item Fixed up the LM minimization specification; bug 203.
+\item Removed errors from \code{psVectorFitSpline1D}.
+\item \code{psTraceReset} is to free memory used by \code{psTrace}.
+\item Added requirement on use of persistent memory --- any use of
+  persistent memory shall provide a function that frees the persistent
+  memory.
+\item Added statement on use of \code{restrict}.
+\item In \code{psSpline1D}, renamed \code{domains} to \code{knots}.
+\end{itemize}
+
+\subsection{Changes from Revision 09 (15 November 2004) to Revision 10 (30 November 2004)}
+
+\begin{itemize}
+\item changed psFitsReadHeaderSet to return a psMetadata rather than just a psHash
+\item added METADATA ... END construction in psMetadataParseConfig
+\item added psArrayAdd function to Simple Arrays
+\item added utility functions \code{psMetadataLookupPtr}, \code{psMetadataLookupS32}, \code{psMetadataLookupF64}
+\item added \code{psHashToArray}
+\item re-organization of the astronomical images section: placed constructors with structures.
+\item added \code{XML functions}
+\end{itemize}
+
+\subsection{Changes from Revision 10 (30 November 2004) to Revision 11 (21 January 2005)}
+
+\begin{itemize}
+\item Changed names of \code{psSphereTransform} structure members to conform with ADD.
+\item Altered \code{psList} and \code{psListIterator} to match that in bug 249.
+\item added status element to psMetadataLookupTYPE utilities
+\item dropped psFitsCreateExt per discussion with rdd
+\item fixed error of psHash to psMetadata in psFitsReadHeaderSet
+\item added psFitsWriteImage
+\item changed psFitsWriteImageSection to psFitsUpdateImage
+\item added header entry to psFitsWriteTable
+\item added psFitsUpdateTable 
+\item Updated \code{psSpline1D} to use a vector for the \code{knots}, and specified types.
+\item \code{psHistogram.nums} changed to type F32 to accomodate errors in the values.
+\item Synchronized use of \code{mask} throughout.  A non-zero mask value means that the
+corresponding value shall not be used.  Affected: polynomials (including plane transformations),
+minimization.
+\item Added \code{psPlaneAlloc, psSphereAlloc, psProjectionAlloc}.
+\item \code{psList}:
+  \begin{itemize}
+  \item Adopted new names: \code{psListGetAndIncrement,
+    psListGetAndDecrement} instead of \code{psListGetNext,
+    psListGetPrev} --- clearer description of functionality.
+  \item Changed \code{PS_LIST_HEAD = 0} and \code{PS_LIST_TAIL = -1};
+    negative indices specify an item relative to the end of the list.
+  \item The action of retrieving data from a list (with one of the
+    three \code{psListGet} functions) is considered ``borrowing'' the
+    reference, so no action is performed on the reference counter.
+    Removed other mentions of reference counters, since these were not
+    necessary.
+  \item \code{mutable} boolean in \code{psListIterator} specifies
+    whether \code{psListAddAfter} or \code{psListAddBefore} may be
+    used to modify the list through the iterator.  This allows the use
+    of \code{const psList} in \code{psListIteratorAlloc}.
+  \end{itemize}
+\item Added \code{psPlaneTransformInvert},
+  \code{psPlaneTransformCombine} and \code{psPlaneTransformFit}.
+\item Added \code{psAstrometryReadWCS}, \code{psAstrometryWriteWCS},
+  and \code{psAstrometrySimplify}.
+\item Added additional modes to \code{psImageInterpolateMode} to deal
+  with variances: \code{PS_INTERPOLATE_BILINEAR_VARIANCE,
+  PS_INTERPOLATE_BICUBIC_VARIANCE, PS_INTERPOLATE_SINC_VARIANCE}.
+\item Added \code{psImageTransform}.
+\item Added section of Database Functions
+\end{itemize}
+
+\subsection{Changes from Revision 11 (21 January 2005) to Revision 12 (9 February 2005)}
+
+\begin{itemize}
+\item In \code{psMatrixLUD}, changed \code{psVector *perm} to
+  \code{psVector **perm} to allow the function to allocate the vector
+  (bug 269).
+\item Removed in \code{psListAlloc}, ``The number of iterators in the
+  list is initially set to zero.'' (bug 271).
+\item Added \code{region} and \code{nSamples} to
+  \code{psPlaneTransformCombine} and specified the algorithm.
+\item re-added psMetadata iterators, modified the APIs somewhat
+\item added the mode entry to psMetadataAddItem 
+\item clarified the 'format' entry in psMetadataAdd
+\item added the function psMetadataAddV
+\item added the option flags \code{psMetadataFlags}
+\item changed psDBDumpCols() to return a psMetadata (bug 285)
+\item changed psDB to hold a MYSQL *
+\item changed psDBSelectColumnNum() to accept a psElemType parameter
+\item changed psDBSelectColumn() to return an psArray of strings
+\item renamed psDBUpdateRow() to psDBUpdateRows()
+\item renamed psDBDeleteRow() to psDBDeleteRows()
+\item renamed psDBInsertRow() to psDBInsertOneRow()
+\item add psDBInsertRows()
+\item Replaced \code{PS_INTERPOLATE_SINC} with \code{PS_INTERPOLATE_LANCZOS[234]}.
+\item noted that SLAlib wrapping will be replaced with our own functions
+\item dropped the Image I/O Functions section (functions moved to psFits)
+\end{itemize}
+
+\subsection{Changes from Revision 12 (9 February 2005) to Revision 13 (30 March 2005)}
+
+\begin{itemize}
+\item Added \code{color}, \code{magnitude} to
+  \code{psAstrometryWriteWCS}, in order to convert a
+  \code{psPlaneDistort} into a straight spatial polynomial.
+\item fix typo in psVector typedef
+\item add limit param to psDBSelectRows()
+\item change psDBUpdateRows() and psDBDeleteRows() to return signed values
+\item Made \code{psMemSetDeallocator} and \code{psMemGetDeallocator}
+  public functions, and cleaned up description.
+\item Reworked \code{psLookupTable} functions so that the number of
+  columns and their types are defined by a \code{scanf}-like format
+  string.  Added \code{psVectorsReadFromFile} and
+  \code{psLookupTableImport} to generalise the reading of tabular data
+  from a file.
+\item \code{psMetadataAddV} changed to use \code{va_list} parameter (bug 312).
+  
+\item Modified \code{psImageTransform} in preparation for image combination.
+  
+\item Added \code{psPixels} structure and related functions
+\item Added \code{psPlaneTransformDeriv}.
+\item Added \code{psImageGrowMask}.
+\item Added Earth Orientation Calculations Section
+  
+\item Changes to the Time section:
+  \begin{itemize}
+  \item Add \code{psTimeBulliten} enum
+  \item Add \code{leapsecond} member to \code{psTimeType}
+  \item Change \code{psTime.usec} $\rightarrow$ \code{psTime.nsec} (nanoseconds)
+  \item Minor reorganization and additional comments
+  \item Rename \code{psTimeGetTime()} $\rightarrow$ \code{psTimeGetNow()}
+  \item New rules for time system converstion
+  \item Rename \code{psTimeToLST()} $\rightarrow$ \code{psTimeToLMST()}
+  \item Rename \code{psTimeLeapSeconds()} $\rightarrow$ \code{psTimeLeapSecondDelta()}
+  \item Add \code{psTimeIsLeapSecond()}
+  \item ISO8601 format clarifications
+  \item Rename \code{psTimeToISOTime()} $\rightarrow$ \code{psTimeToISO()}
+  \item Add \code{psTimeFromUTC()}
+  \item Add \code{psTimeFromTT()}
+  \item Change \code{psTime} math rules
+  \item Change ``Time Tables'' to have IERS Bulliten A \& B
+  \item Add Dates \& Times Test Inputs appendix
+  \end{itemize}
+  
+\item Adding logical operations (and, or) to \code{psBinaryOp}.
+  
+\item Substantial reorganization:
+  \begin{itemize}
+  \item Moved Metadata, Database, and XML sections to new section
+  \item Re-named Detector \& Sky Coordinates to Linear \& Spherical Coordinates
+  \item Moved Exposure and Observatory information out of 'Astronomical Images'
+  \item Moved Celestial Coordinate Systems out of 'Detector \& Sky Coordinates'
+  \item Added Atmospheric Effects section, incorporating psGrommit and airmass functions from other sections)
+  \item Moved Fixed Pattern out of Astronomical Images 
+  \end{itemize}
+  \end{itemize}
+
+\subsection{Changes from Revision 13 (30 March 2005) to Revision 14 (27 April 2005)}
+
+\begin{itemize}
+\item Restrictions on the use of \code{malloc}, \code{calloc}, \code{realloc}, and \code{free} should not be unintentionaly imposed on 3rd party code.
+\item Add database support for ``auto-incrementing''
+  
+\item Changes to Configuration Files:
+  \begin{itemize}
+  \item Add \code{UTC,UT1,TAI,TT} types
+  \item Change ``multiple symbol'' declaration format to \code{[keyword] MULTI}
+  \item Add Scoping Rules
+  \item Remove Configuration File Grammar appendix
+  \item Add Configuration File Test Inputs appendix
+  \item Rename \code{psMetadataParseConfig()} $\rightarrow$ \code{psMetadataConfigParse()}
+  \item Add \code{psMetadataConfigFormat()}
+  \item Add \code{psMetadataConfigWrite()}
+  \end{itemize}
+  
+\item Add \code{PS_META_TIME} to \code{psMetadataType}
+\item Changed \code{psPixels} to vector-like array of \code{psPixelCoord} (bug 371).
+\item Added \code{psPixelsAlloc}, \code{psPixelsRealloc} and \code{psPixelsCopy} (bug 371).
+\item After conversation with GG:
+  \begin{itemize}
+  \item Mention that Chebyshev domain should only be from -1 to +1, but won't restrict.
+  \item Type for \code{psSpline1D} is F32 only.
+  \item Matrix functions return \code{NULL} in the event of an error.
+  \item Changed API for \code{psVectorFitSpline1D}.
+  \item Removed pre-defined LM minimization functions; these will be defined in the Modules SDRS.
+  \end{itemize}
+
+\item defined \code{psEarthPole}, re-cast Earth Orientation
+  Calculations to use it for inputs and outputs.
+\item minor name changes in Earth Orientation to match ADD changes.
+\item dropped TBD for \code{psFitsUpdateImage}
+\item \code{psAberration} return value defined.
+\item added \code{psArrayRemove} function (already exists in psArray.c)
+\item added \code{psVectorExtend} function
+\item changed inputs to \code{psImageSlice} to use \code{psRegion}
+
+\item changes involving \code{psRegion}
+\begin{itemize}
+  \item moved Image Regions section to beginning of Image Operations
+  \item dropped psImageSubsection (redundant now)
+
+  \item changed the following functions to use psRegion:
+  psImageSubset, psImageTrim, psImageSlice, psImageCut
+
+  \item added clarification to meaning of psRegion x0,x1,y0,y1 values.
+\end{itemize}
+
+\item removed psMetadata.ptype as per bug 313, dropped reference in psDB section.
+\item clarified the discussion of duplicate keys and the option flags in \code{psMetadata}
+\item Added \code{psLibFinalize} (bug 388).
+\end{itemize}
+
+\subsection{Changes from Revision 14 (27 April 2005) to Revision 15 (15 June 2005)}
+
+\begin{itemize}
+\item Bug 393:
+  \begin{itemize}
+  \item add \code{PS_META_TIME} to \code{psMetadataType}
+  \item change \code{psTimeFromISO()} to accept a \code{psTimeType} to specifiy
+    what time system the generated psTime object should be set to
+  \end{itemize}
+\item changes to ``Database Functions''
+  \begin{itemize}
+  \item change \code{psDBCreateTable()}'s definition to be clear about how to combine indexes and auto-increment
+  \item add \code{p_psDBRunQuery()}
+  \end{itemize}
+\item \code{psLibInit} does not seed the RNG (done in \code{psRandom}).
+\item Updated metadata iteration to use \code{psMetadataIterator}.
+\item Restoring \code{nbuckets} to \code{psHashAlloc}, since it appears to be required by the code.
+\item Adding \code{psArrayGet} and \code{psArraySet} since these are in the code.
+\item \code{psMatrixEigenvectors} returns type \code{psImage} (not a single vector).
+\item Specify that \code{col0,row0} shall be preserved with \code{psImageCopy}.
+\item Added functions \code{psErrorGetStackSize},
+  \code{psVectorRecycle}, \code{psVectorCopy},
+  \code{psMetadataItemAllocStr}, \code{psMetadataItemAllocF32},
+  \code{psMetadataItemAllocF64}, \code{psMetadataItemAllocS32},
+  \code{psMetadataItemAllocBool}, \code{psMetadataAddS32},
+  \code{psMetadataAddF32}, \code{psMetadataAddF64},
+  \code{psMetadataAddList}, \code{psMetadataAddStr},
+  \code{psMetadataAddVector}, \code{psMetadataAddImage},
+  \code{psMetadataAddHash}, \code{psMetadataAddLookupTable},
+  \code{psMetadataAddUnknown}, \code{psMetadataLookupF32},
+  \code{psBitSetClear}, \code{psRegionToString} (modified),
+  \code{psImageRecycle}, \code{psImageFreeChildren}
+  \code{psFitsGetExtName}, \code{psFitsSetExtName} already
+  implemented.
+\item Renamed \code{psMaskToPixels} to \code{psPixelsFromMask} (bug 414).
+\item \code{psRegionFromString} returns \code{NaN} for any element that doesn't parse correctly (bug 416).
+
+\item unified the single and double precision polynomials
+\item re-ordered the psPolynomial*Alloc arguments
+\item changed the psPolynomials to be defined in terms of Norder not Nterms
+\item re-ordered the psVectorFitPoly* arguments
+\item added mask \& maskValue ot psVectorFitPoly*
+\item changed the requirements on the input data vectors to psPolynomialFit and Eval
+\item added the psVectorClipFitPoly* functions
+
+\item SDRS API clean up and implementation re-synchronization
+  \begin{itemize}
+  \item rename psFinalize() $\rightarrow$ psLibCleanup()
+  \item change parameters expecting \code{__LINE__} to type \code{unsigned int}
+  \item change parameters expecting \code{__LINE__} to be named \code{lineno}
+  \item change \code{int} $\rightarrow$ \code{size_t} where appropriate
+  \item change \code{psBool} $\rightarrow$ \code{bool}
+  \item change \code{psU64} $\rightarrow$ \code{unsigned long long} where appropriate
+  \item add \code{const} to parameters where appropriate
+  \item remove \code{const} from parameters passed by value
+  \item remove \code{const} from parameters that are actually being modified
+  \item change \code{void *} $\rightarrow$ \code{psPtr} where appropriate
+  \item change parameter names to be consistent with the current implementation
+  \item change parameter names to be more consistent between related functions
+  \item change parameter types to be more consistent with related data structures 
+  \item change \code{psF64} $\rightarrow$ \code{double} where appropriate
+  \item change \code{psC64} $\rightarrow$ \code{complex double}
+  \item rename functions/datatypes to abv. ``allocate'' as ``alloc''
+  \item rename functions/datatypes to abv. ``memory'' as ``mem''
+  \item rename functions/datatypes to abv. ``function'' as ``func'' (not ``fcn'')
+  \item no longer abv. ``format'' as ``fmt''
+  \item remove ``my'' from function parameters names
+  \item add \code{psComparePtrFunc} function pointer
+  \item change parameters expecting filenames to be named \code{filename}
+  \item change parameters specifying the number of something to allocated to be named \code{nalloc}
+  \item change parameters specifying the number of bytes to be allocated to be named \code{size}
+  \item change parameters specifying the type of something to be named \code{type} where appropriate
+  \item change \code{psImage} to use \code{psU32} rows \& columns
+  \item change parameter names to not use the words ``width'' or ``height''
+  \item rename \code{psHash.nbuckets} to \code{psHash.n}
+  \item rename \code{psList.size} to \code{psList.n}
+  \item rename \code{psFitsAlloc()} $\rightarrow$ \code{psFitsOpen()}
+  \item add \code{psFitsClose()}
+  \item change blurb about ``Threads'' to clarify requirements
+  \item remove \code{lock} from \code{psList}
+  \item change \code{psVector} to store it's number of elements as an \code{long}
+  \item change \code{psArray} to store it's number of elements as an \code{long}
+  \item change \code{psList} to store it's number of elements as an \code{long}
+  \item change \code{psListIterator} to store the number of elements on the list as an \code{long}
+  \item change \code{psListIterator} to store it's index as an \code{long}
+  \item change \code{psHash} to store it's number of elements as an \code{long}
+  \item change \code{psLookupTable} to store it's index as an \code{long}
+  \item change \code{psBitSet} to store it's size as an \code{long}
+  \item remove \code{psXMLDocFree()}
+  \item add \code{psXMLDocAlloc()}
+\end{itemize}
+\item add \code{PS_META_NULL}
+\item add \code{NULL} keyword to ``Configuration files''
+\item Added \code{psMemCheckTYPE} functions, for many values of \code{TYPE}.
+\item add \code{psMetadataItemAllocPtr()}
+\item add \code{psMetadataAddBool()}
+\item condense \code{psMetadataAddVector()}, \code{psMetadataAddImage()},
+\code{psMetadataAddHash()}, \code{psMetadataAddLookupTable()},
+\code{psMetadataAddUnknown()}, \& \code{psMetadataAddMetadata()} into
+\code{psMetadataAddPtr()}
+\item add \code{psMetadataLookupStr()}
+\item add \code{psMetadataLookupBool()}
+\item add \code{psFitsType}
+\item add missing \code{psImageRecycle()} prototype (was already defined)
+\item add \code{psLookupStatusType}
+
+\item re-organized sections to place all data container before the operations.  
+\item moved psRegion to subsubsection under psImage, out of image operations
+\item added psMinimizeGaussNewtonDelta
+\item defined gain ratio test in psMinimizeLMChi2 
+\item added psRegionForImage
+\item added psImageSmooth
+\item added psVectorInit
+\item added psImageInit
+\item added fmt to psTrace
+\item added psTraceV
+\item unification of psTrace and psLogMsg syntax 
+\item moved image hierarchy section to ModulesSDRS
+\item moved photometry section to ModulesSDRS
+\item Updating section on thread safety with new policy.
+\item add \code{psFitsOpenFD()}
+\item Removed example destructor function, since the current
+  implementation does not follow it, but achieves the same result.
+\item Reorganised document: collections and mathematical structures
+  are now separate sections; metadata, pixels lists and bit sets are
+  collections; type information goes into system utilities.
+\item \code{psMetadataType} changed to \code{psDataType} (and so
+  \code{PS_META_*} changed to \code{PS_DATA_*}), and expanded to
+  include most psLib structures.  \code{PS_META_MULTI} changed to
+  \code{PS_DATA_METADATA_MULTI}.
+\item Added policy on \code{psPtr, psString}, Changed some \code{void
+  *} to \code{psPtr} and some \code{char *} to \code{psString}, as
+  appropriate.  Noted that metadata functions must copy strings in to
+  a \code{psMetadataItem}.
+\item Added \code{psMask} instead of \code{unsigned int} for mask values.
+\item Added \code{psStringCopy, psStringNCopy, psStringAppend, psStringPrepend}
+  to match implementation.
+\item added psRegionForSquare
+\item added psImageMaskRegion, psImageKeepRegion
+\item added psImageMaskCircle, psImageKeepCircle
+\item Updated \code{psFits} functions following bug 412: added
+  \code{psFitsHeaderFromImage}, \code{psFitsHeaderFromTable},
+  \code{psFitsMoveEnd}, \code{psFitsDeleteExtName},
+  \code{psFitsDeleteExtNum}, \code{psFitsTruncate},
+  \code{psFitsHeaderValidate}.  Changed \code{psFitsWriteImage} and
+  \code{psFitsWriteTable} to update the \code{extname}.  Added mode
+  options for \code{psFitsOpen}.
+\item add \code{limit} param to \code{psDBDeleteRows()}
+\item change \code{p_psDBRunQuery()} to accept a \code{printf()} style format 
+\item added F (file:line) to psLogMsg and psTrace
+\item added psLogGetLevel
+\end{itemize}
+
+\subsection{Changes from Revision 15 (15 June 2005) to Revision 16 (13 Sept 2005)}
+
+\begin{itemize}
+\item Removed \code{psLookupTableStatusType} (see bugs 304, 454).
+\item Added \code{psArrayElementsFree} (already implemented).
+\item \code{psMask} changed to \code{psMaskType} (more clear).
+\item Use the \code{region} in \code{psImageTransform} to set the size
+  of the output image (bug 453).
+\item \code{list} in \code{psListIteratorAlloc} is not const (bug
+  455).
+\item Clarified policy on signed/unsigned for memory allocation;
+  \code{psAlloc} checks the allocation size against
+  \code{PS_MEM_LIMIT}.
+\item \code{psVector} and \code{psImage} functions \code{-Alloc},
+  \code{-Realloc} and \code{-Recycle} changed to signed \code{int}.
+\item \code{nFail} in \code{psMetadataConfigParse} is \code{unsigned}.
+\item \code{timeval} changed to \code{struct timeval} throughout.
+\item Making various integers \code{unsigned} in the various
+  \code{psPolynomial} types and \code{psSpline1D} (bug 460).
+\item Adjusted \code{psStats} to reflect robust / fitted statistics as defined in the ADD.
+\item Changed definition of \code{psDB} to abstract database type.
+\item clarified psTimeToLMST 
+\item added cutCols, cutRows to psImageCut
+\item added remaining integer primatives to psMetadata
+\item added C++ compatibility specification 
+\item Added S32, S64, U32, U64 to \code{psScalar} (bug 491).
+\item Added the \code{psCompare} functions.
+\item Removed F32 from polynomials --- only use double precision.
+\item \code{psPixelCoord} now contains floats.
+\item \code{psImageSlice} uses \code{psPixels} to output coordinates.
+\item Added \code{psTimer} functions (prototypes by EAM).
+\item \code{psLibInit} and \code{psLibCleanup} changed to
+  \code{psTimeInitialize} and \code{psTimeFinalize}.  Calls to
+  functions that require the time tables before calling
+  \code{psTimeInitialize} shall produce an error.
+\item Added \code{psArgument} functions to provide simple argument
+  handling.
+\item Changed return types of \code{psXMLDocToFile, psXMLDocToMem,
+  psXMLDocToFD} to bool (bug 499).
+\item Clarified behaviour of \code{psLogSetDestination} and \code{psTraceSetDestination}.
+\item Split \code{psMetadataRemove} into \code{psMetadataRemoveKey} and \code{psMetadataRemoveIndex}.
+\item Added explanatory note about \code{psRegionFromString} and the FITS standard.
+\item add \code{psFitsOpenStream()}
+\item Removed \code{psFixedPattern}.
+\item Changed \code{format} option for
+  \code{psMetadataAddS32,psMetadataAddF32,psMetadataAddF64,psMetadataAddBool,psMetadataAddStr}.
+\item \code{psVectorFitSpline1D} now takes an input \code{psSpline1D},
+  and an optional error vector.
+\item Changed \code{complex double} to \code{complex} throughout.
+\item Added \code{psImageCountPixelMask}
+\item Added \code{psVectorCountPixelMask}
+\item Added \code{psVectorCreate}
+\item Added \code{psImageRow}
+\item Added \code{psImageColumn}
+\item moved \code{paramMask} to \code{psMinimization}
+\item added \code{paramMin} to \code{psMinimization}
+\item added \code{paramMax} to \code{psMinimization}
+\item added \code{paramDelta} to \code{psMinimization}
+\item adjusted \code{psMinimizeLMChi2} to drop \code{paramMask}
+\end{itemize}
+
+\subsection{Changes from Revision 16 (13 Sept 2005) to Revision 17 (18 Oct 2005)}
+
+\begin{itemize}
+\item moved param constraints to \code{psMinConstrain}
+\item changed type requirement on psMinimize functions to just F64
+\item changed psMinimize input arguments to accept weight not sigma
+\item changed psPolynomial masks from char to psU8.
+\item changed \code{psImage.col0,row0} changed from const.
+\item Removed \code{psMath}
+\item set \code{psPolynomial} order elements (nX, nY, etc)
+\end{itemize}
+
+\subsection{Changes from Revision 17 (18 Oct 2005) to Revision 18 (06 Dec 2005)}
+
+\begin{itemize}
+\item TBD-ed \code{psEOC_ParallaxFactor} --- do not code yet.
+\item Removed \code{psFitsOpenFD} and \code{psFitsOpenStream} --- not
+  possible to implement these with \code{cfitsio}.
+\item \code{psFitsMoveEnd} changed to \code{psFitsMoveLast}, and moves
+  to the last extension.
+\item \code{psFitsWriteImage} and \code{psFitsWriteTable} write to the
+  end of the file.
+\item Added \code{psFitsInsertImage} and \code{psFitsInsertTable} to
+  insert extensions; note that these are expensive.
+\item Changed \code{psFitsUpdateImage} to use \code{x0,y0} instead of
+  a \code{psRegion}, which would make for an overconstrained system;
+  specified origins.
+\item Removed \code{psFitsHeaderFromImage, psFitsHeaderFromTable}.
+  The functionality will be handled by \code{psFitsWriteImage,
+  psFitsWriteTable}.
+\item Added \code{psMetadataCopy}.
+\item Clarified \code{psPlaneTransformAlloc} and
+  \code{psPlaneDistortAlloc}: arguments are polynomial order, not
+  number of terms.
+\item Updated \code{psImageTransform} to use \code{psPixels} for
+  \code{blankPixels} instead of a \code{psArray}.
+\item Added requirement on dynamic setting of mutex locks:
+  \code{psMemThreadSafety} (see bug 586).
+\item Added return type for \code{psImageSmooth} (bug 588).
+\item clarified psRegion for subimages
+\item clarified units for \code{psProjectionAlloc}
+\end{itemize}
+
+\subsection{Changes from Revistion 18 (06 Dec 2005) to Revision 19 (21 Feb 2006)}
+\begin{itemize}
+\item minor \code{typedef} fixes
+\item sync \code{psImage}'s definition with the implementation in psLib
+\item convert \code{complex} declarations to be explicitly \code{double complex}
+\item change \code{psFitsReadTable()}'s \code{fits} param to be \code{const} 
+\item changes to make image, subimage, and region consistent:
+\begin{itemize}
+\item all region operations refer to the parent image coordinates
+\item all functions which take a psImage and some coordinate refer to the parent coordinate frame 
+\item psRegionForImage : changed definition of region to refer to parent coords
+\item psImageCountPixelMask : changed definition of region to refer to parent coords
+\item psImageSubset : specified definition of region to refer to parent coords
+\item psImageTrim : specified definition of region to refer to parent coords
+\item psImageRow : specified definition of region to refer to parent coords
+\item psImageColumn : specified definition of region to refer to parent coords
+\item psImageSlice : specified definition of region to refer to parent coords
+\item psImageCut : specified definition of region to refer to parent coords
+\item psImageRadialCut : specified definition of region to refer to parent coords
+\end{itemize}
+\item Added S8,S16,U8,U16,U32 to \code{psDataType} (bug 579).
+\item \code{psVector.n} and \code{psArray.n} to be initially set to zero
+\item \code{psArrayAlloc} and \code{psArrayRealloc} to initialise values
+\item add \code{*out} param to \code{psFitsReadHeaderSet()}
+\item sync \code{psElemType}, \code{psDataType}, \& \code{psFitsType} with the implementation in pslib
+\item reorder \code{psPrecessMethod} to match the implementation in pslib
+\item set \code{psPolynomial?DAlloc()} functions params to have \code{type} first
+\item dropped unused 'stats' from \code{psLookupTableInterpolateAll}
+\item psImageSubset : specified that the input region and image need not overlap, but that the bound saturate to the limits of the input image
+\item allow \code{psMetadataItem} pointer types to have a value of \code{NULL}
+\end{itemize}
+
+\subsection{Changes from Revistion 19 (21 Feb 2006) to Revision 20 (11 Apr 2006)}
+
+\begin{itemize}
+\item Added \code{psFitsIsImage} and \code{psFitsIsTable}
+\item Added
+  \code{psFitsReadImageCube,psFitsInsertImageCube,psFitsWriteImageCube,psFitsInsertImageCube}
+  functions.
+\item Added \code{extname} to \code{psFitsWriteTable} and
+  \code{psFitsInsertTable} to mirror the image versions.
+\item \code{psRegionFromString} adjusted to allow a \code{NULL} string region 
+\item added \code{PS_META_NO_REPLACE}
+\item Added \code{psMetadataItemCopy}; altered \code{psMetadataCopy}
+  slightly.  Implementations exist.
+\item Added \code{psRegionAlloc} and \code{PS_DATA_REGION} for the
+  rare occasions when we want to use a \code{psRegion} on one of the containers.
+\item Updated \code{psFitsValidateHeader, psFitsWriteImage,
+  psFitsWriteTable}.  Added \code{psFitsHeaderFromImage,
+  psFitsHeaderFromTable}.  See bug 733.
+
+\item Added \code{psLine} functions
+\item Added \code{psEllipse} functions
+\item Added \code{psStringSplit}
+\item Added \code{psStringStrip}
+\item Added \code{psMetadataPrint} % added FILE *
+\item Added \code{psMetadataItemTransfer}
+\item Added \code{psFitsWriteHeaderNotImage}
+\item Added \code{psImageFlip} % x and y in same function
+\item Added \code{psImageJpeg} functions
+\item Added \code{psMetadataItemParse} functions
+\item Added \code{psImageBicube} functions
+\item Added \code{psRegionIsBad}
+\item Added \code{psRegionIsNaN}
+\item Added \code{psSparse} functions.
+
+\end{itemize}
+
+\subsection{Changes from Revision 20 (11 Apr 2006) to Revision 21 (24 July 2006)}
+
+\begin{itemize}
+\item updated description of \code{psStringCopy()} \& \code{psStringNCopy}
+\item rename \code{psMemThreadSafety()} -> \code{psMemSetThreadSafety()}
+\item add \code{psMemGetThreadSafety()}
+\item add \code{psTimeToTM()} \& \code{psTimeFromTM()}
+\item add \code{psTimeStrptime()}
+\item add \code{psTimeStrftime()}
+\item add \code{psDBLastInsertID()}
+\item add \code{psDBExplicitTrans()}
+\item add \code{psDBTransaction()}
+\item add \code{psDBCommit()}
+\item add \code{psDBRollback()}
+\item add \code{p_psDBRunQueryPrepared()}
+\item add \code{p_psDBFetchResult}
+\item add database subsubsecionts for ``Transaction Control Database Functions'' and ``Low Level Database Functions''
+\item change \code{psMetadataConfigParse()} to operate on strings instead of files
+\item add \code{psMetadataConfigRead()}
+\item add \code{psMetadataConfigPrint()}
+\end{itemize}
+
+\subsection{Changes from Revision 21 (24 July 2006) to present}
+
+\begin{itemize}
+\item dropped requirement for \code{psTimerStop} to return max elapsed time.
+\item clarified return values for \code{psTimerMark} and \code{psTimerClear} in case of errors.
+\item changed \code{psVectorStats} to return a bool
+\item added \code{psStats->result}
+\end{itemize}
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/Makefile	(revision 22322)
@@ -0,0 +1,54 @@
+# $Id: Makefile,v 1.16 2006-01-16 01:11:40 eugene Exp $
+
+PDFLATEX = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): pdflatex
+PSLATEX  = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): latex
+
+help:
+	@echo "USAGE: make (target)"
+	@echo "  targets:  sdrs add all sdrs-tag add-tag"
+
+sdrs: psLibSDRS.pdf 
+add: psLibADD.pdf
+all : sdrs add
+
+SDRS = psLibSDRS.tex \
+       ChangeLogSDRS.tex \
+       configFileTests.tex \
+       timeTests.tex \
+       pics/Metadata.ps \
+       pics/earthrot.ps
+
+ADD =  psLibADD.tex \
+       ChangeLogADD.tex \
+       raymodel.f \
+       pics/rotations.ps \
+       pics/earthrot.ps
+
+psLibSDRS.pdf: $(SDRS)
+
+psLibADD.pdf: $(ADD)
+
+sdrs-tag:
+	cvs tag `grep "version{" psLibSDRS.tex | tr "{}" " " | awk '{printf "SDRS-%02d\n", $$2}'` $(SDRS)
+
+add-tag:
+	cvs tag `grep "version{" psLibADD.tex | tr "{}" " " | awk '{printf "ADD-%02d\n", $$2}'` $(ADD)
+
+%.pdf: %.tex
+	$(PSLATEX) $*.tex 
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	thumbpdf --modes=dvips $*.pdf
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	@rm -f $*.ps $*.dvi $*.aux $*.log $*.tbr $*.tbd $*.toc $*.tpm $*.lof body.tmp head.tmp
+
+clean :
+	$(RM) *.log *.dvi *.aux *.toc *.tbd *.tbr *.tpm *.lof *.out *~ core body.tmp head.tmp
+
+dist : clean
+	$(RM) *.pdf
+
+empty: clean
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/bulletinA_09Sep2004.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/bulletinA_09Sep2004.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/bulletinA_09Sep2004.dat	(revision 22322)
@@ -0,0 +1,368 @@
+# Bulletin A, 9 September 2004.
+# ftp://maia.usno.navy.mil/ser7/ser7.dat
+# MJD       XP(")   YP(")   UT1-UTC(s)
+53258       0.1715  0.4774  -0.45092              
+53259       0.1734  0.4757  -0.45039              
+53260       0.1753  0.4741  -0.45012              
+53261       0.1770  0.4724  -0.45015              
+53262       0.1787  0.4708  -0.45045              
+53263       0.1804  0.4691  -0.45093              
+53264       0.1820  0.4674  -0.45147              
+53265       0.1835  0.4656  -0.45196              
+53266       0.1850  0.4638  -0.45234              
+53267       0.1864  0.4620  -0.45257              
+53268       0.1878  0.4602  -0.45264              
+53269       0.1891  0.4584  -0.45256              
+53270       0.1904  0.4565  -0.45241              
+53271       0.1917  0.4546  -0.45228              
+53272       0.1930  0.4527  -0.45232              
+53273       0.1942  0.4508  -0.45264              
+53274       0.1953  0.4488  -0.45326              
+53275       0.1965  0.4469  -0.45416              
+53276       0.1975  0.4449  -0.45523              
+53277       0.1986  0.4429  -0.45635              
+53278       0.1996  0.4408  -0.45740              
+53279       0.2006  0.4388  -0.45828              
+53280       0.2016  0.4367  -0.45896              
+53281       0.2025  0.4347  -0.45944              
+53282       0.2034  0.4326  -0.45974              
+53283       0.2043  0.4305  -0.45991              
+53284       0.2051  0.4283  -0.46004              
+53285       0.2059  0.4262  -0.46020              
+53286       0.2066  0.4240  -0.46046              
+53287       0.2074  0.4219  -0.46088              
+53288       0.2080  0.4197  -0.46152              
+53289       0.2087  0.4175  -0.46238              
+53290       0.2093  0.4153  -0.46347              
+53291       0.2099  0.4131  -0.46471              
+53292       0.2104  0.4108  -0.46601              
+53293       0.2109  0.4086  -0.46724              
+53294       0.2114  0.4063  -0.46828              
+53295       0.2118  0.4041  -0.46906              
+53296       0.2122  0.4018  -0.46958              
+53297       0.2126  0.3995  -0.46993              
+53298       0.2129  0.3972  -0.47022              
+53299       0.2132  0.3950  -0.47058              
+53300       0.2135  0.3927  -0.47114              
+53301       0.2137  0.3904  -0.47193              
+53302       0.2139  0.3880  -0.47296              
+53303       0.2141  0.3857  -0.47414              
+53304       0.2142  0.3834  -0.47538              
+53305       0.2142  0.3811  -0.47658              
+53306       0.2143  0.3788  -0.47763              
+53307       0.2143  0.3764  -0.47849              
+53308       0.2143  0.3741  -0.47913              
+53309       0.2143  0.3718  -0.47957              
+53310       0.2142  0.3695  -0.47986              
+53311       0.2141  0.3671  -0.48007              
+53312       0.2139  0.3648  -0.48026              
+53313       0.2137  0.3625  -0.48053              
+53314       0.2135  0.3601  -0.48092              
+53315       0.2133  0.3578  -0.48149              
+53316       0.2130  0.3555  -0.48226              
+53317       0.2127  0.3531  -0.48324              
+53318       0.2123  0.3508  -0.48440              
+53319       0.2119  0.3485  -0.48567              
+53320       0.2115  0.3462  -0.48694              
+53321       0.2111  0.3439  -0.48808              
+53322       0.2106  0.3416  -0.48899              
+53323       0.2101  0.3393  -0.48965              
+53324       0.2095  0.3370  -0.49008              
+53325       0.2090  0.3347  -0.49041              
+53326       0.2084  0.3325  -0.49078              
+53327       0.2077  0.3302  -0.49130              
+53328       0.2071  0.3279  -0.49205              
+53329       0.2064  0.3257  -0.49301              
+53330       0.2057  0.3235  -0.49411              
+53331       0.2049  0.3212  -0.49527              
+53332       0.2042  0.3190  -0.49636              
+53333       0.2034  0.3168  -0.49732              
+53334       0.2025  0.3146  -0.49807              
+53335       0.2017  0.3124  -0.49859              
+53336       0.2008  0.3103  -0.49889              
+53337       0.1999  0.3081  -0.49900              
+53338       0.1989  0.3060  -0.49900              
+53339       0.1980  0.3038  -0.49895              
+53340       0.1970  0.3017  -0.49894              
+53341       0.1959  0.2996  -0.49903              
+53342       0.1949  0.2976  -0.49927              
+53343       0.1938  0.2955  -0.49971              
+53344       0.1927  0.2934  -0.50034              
+53345       0.1916  0.2914  -0.50114              
+53346       0.1905  0.2894  -0.50207              
+53347       0.1893  0.2874  -0.50305              
+53348       0.1881  0.2854  -0.50395              
+53349       0.1869  0.2835  -0.50467              
+53350       0.1857  0.2815  -0.50515              
+53351       0.1844  0.2796  -0.50537              
+53352       0.1831  0.2777  -0.50543              
+53353       0.1818  0.2758  -0.50547              
+53354       0.1805  0.2740  -0.50563              
+53355       0.1792  0.2721  -0.50600              
+53356       0.1778  0.2703  -0.50660              
+53357       0.1764  0.2685  -0.50736              
+53358       0.1750  0.2667  -0.50818              
+53359       0.1736  0.2650  -0.50896              
+53360       0.1722  0.2632  -0.50960              
+53361       0.1707  0.2615  -0.51003              
+53362       0.1692  0.2599  -0.51025              
+53363       0.1677  0.2582  -0.51033              
+53364       0.1662  0.2566  -0.51023              
+53365       0.1647  0.2549  -0.51000              
+53366       0.1631  0.2534  -0.50972              
+53367       0.1615  0.2518  -0.50946              
+53368       0.1599  0.2503  -0.50930              
+53369       0.1583  0.2487  -0.50928              
+53370       0.1567  0.2473  -0.50945              
+53371       0.1551  0.2458  -0.50980              
+53372       0.1534  0.2444  -0.51033              
+53373       0.1518  0.2430  -0.51099              
+53374       0.1501  0.2416  -0.51170              
+53375       0.1484  0.2402  -0.51240              
+53376       0.1467  0.2389  -0.51296              
+53377       0.1449  0.2376  -0.51332              
+53378       0.1432  0.2363  -0.51346              
+53379       0.1415  0.2351  -0.51341              
+53380       0.1397  0.2338  -0.51330              
+53381       0.1379  0.2327  -0.51329              
+53382       0.1361  0.2315  -0.51350              
+53383       0.1343  0.2304  -0.51399              
+53384       0.1325  0.2293  -0.51471              
+53385       0.1307  0.2282  -0.51556              
+53386       0.1289  0.2271  -0.51640              
+53387       0.1270  0.2261  -0.51711              
+53388       0.1252  0.2251  -0.51763              
+53389       0.1233  0.2242  -0.51791              
+53390       0.1214  0.2232  -0.51798              
+53391       0.1195  0.2223  -0.51786              
+53392       0.1177  0.2214  -0.51761              
+53393       0.1158  0.2206  -0.51729              
+53394       0.1138  0.2198  -0.51699              
+53395       0.1119  0.2190  -0.51678              
+53396       0.1100  0.2182  -0.51671              
+53397       0.1081  0.2175  -0.51683              
+53398       0.1061  0.2168  -0.51715              
+53399       0.1042  0.2162  -0.51764              
+53400       0.1022  0.2155  -0.51827              
+53401       0.1003  0.2149  -0.51898              
+53402       0.0983  0.2143  -0.51967              
+53403       0.0964  0.2138  -0.52026              
+53404       0.0944  0.2133  -0.52069              
+53405       0.0924  0.2128  -0.52091              
+53406       0.0904  0.2123  -0.52095              
+53407       0.0884  0.2119  -0.52089              
+53408       0.0865  0.2115  -0.52090              
+53409       0.0845  0.2111  -0.52110              
+53410       0.0825  0.2108  -0.52159              
+53411       0.0805  0.2105  -0.52238              
+53412       0.0785  0.2102  -0.52338              
+53413       0.0765  0.2099  -0.52446              
+53414       0.0745  0.2097  -0.52547              
+53415       0.0725  0.2095  -0.52629              
+53416       0.0705  0.2094  -0.52687              
+53417       0.0685  0.2092  -0.52721              
+53418       0.0665  0.2091  -0.52734              
+53419       0.0645  0.2090  -0.52733              
+53420       0.0624  0.2090  -0.52723              
+53421       0.0604  0.2090  -0.52714              
+53422       0.0584  0.2090  -0.52711              
+53423       0.0564  0.2090  -0.52723              
+53424       0.0544  0.2091  -0.52754              
+53425       0.0524  0.2092  -0.52806              
+53426       0.0505  0.2093  -0.52877              
+53427       0.0485  0.2095  -0.52964              
+53428       0.0465  0.2096  -0.53060              
+53429       0.0445  0.2098  -0.53156              
+53430       0.0425  0.2101  -0.53243              
+53431       0.0405  0.2103  -0.53314              
+53432       0.0386  0.2106  -0.53364              
+53433       0.0366  0.2110  -0.53395              
+53434       0.0346  0.2113  -0.53415              
+53435       0.0327  0.2117  -0.53434              
+53436       0.0307  0.2121  -0.53468              
+53437       0.0288  0.2125  -0.53528              
+53438       0.0269  0.2129  -0.53620              
+53439       0.0249  0.2134  -0.53738              
+53440       0.0230  0.2139  -0.53872              
+53441       0.0211  0.2144  -0.54006              
+53442       0.0192  0.2150  -0.54125              
+53443       0.0173  0.2156  -0.54220              
+53444       0.0154  0.2162  -0.54286              
+53445       0.0135  0.2168  -0.54326              
+53446       0.0117  0.2175  -0.54344              
+53447       0.0098  0.2181  -0.54349              
+53448       0.0080  0.2188  -0.54350              
+53449       0.0062  0.2195  -0.54353              
+53450       0.0044  0.2203  -0.54367              
+53451       0.0026  0.2210  -0.54396              
+53452       0.0009  0.2218  -0.54441              
+53453      -0.0009  0.2226  -0.54503              
+53454      -0.0027  0.2234  -0.54576              
+53455      -0.0044  0.2243  -0.54655              
+53456      -0.0061  0.2252  -0.54732              
+53457      -0.0078  0.2261  -0.54799              
+53458      -0.0095  0.2270  -0.54848              
+53459      -0.0112  0.2279  -0.54878              
+53460      -0.0128  0.2289  -0.54889              
+53461      -0.0145  0.2299  -0.54890              
+53462      -0.0161  0.2309  -0.54892              
+53463      -0.0177  0.2319  -0.54908              
+53464      -0.0193  0.2329  -0.54951              
+53465      -0.0209  0.2340  -0.55026              
+53466      -0.0225  0.2351  -0.55131              
+53467      -0.0240  0.2362  -0.55256              
+53468      -0.0255  0.2373  -0.55384              
+53469      -0.0271  0.2384  -0.55500              
+53470      -0.0286  0.2396  -0.55594              
+53471      -0.0300  0.2408  -0.55660              
+53472      -0.0315  0.2420  -0.55699              
+53473      -0.0329  0.2432  -0.55718              
+53474      -0.0343  0.2444  -0.55725              
+53475      -0.0357  0.2457  -0.55725              
+53476      -0.0371  0.2469  -0.55727              
+53477      -0.0385  0.2482  -0.55736              
+53478      -0.0398  0.2495  -0.55757              
+53479      -0.0411  0.2508  -0.55790              
+53480      -0.0424  0.2521  -0.55839              
+53481      -0.0437  0.2535  -0.55905              
+53482      -0.0449  0.2549  -0.55980              
+53483      -0.0461  0.2562  -0.56058              
+53484      -0.0473  0.2576  -0.56131              
+53485      -0.0485  0.2590  -0.56189              
+53486      -0.0497  0.2604  -0.56227              
+53487      -0.0508  0.2619  -0.56244              
+53488      -0.0519  0.2633  -0.56241              
+53489      -0.0530  0.2648  -0.56230              
+53490      -0.0540  0.2663  -0.56224              
+53491      -0.0551  0.2678  -0.56240              
+53492      -0.0561  0.2693  -0.56285              
+53493      -0.0571  0.2708  -0.56359              
+53494      -0.0580  0.2723  -0.56452              
+53495      -0.0590  0.2738  -0.56551              
+53496      -0.0599  0.2754  -0.56644              
+53497      -0.0607  0.2769  -0.56722              
+53498      -0.0616  0.2785  -0.56783              
+53499      -0.0624  0.2801  -0.56821              
+53500      -0.0632  0.2817  -0.56840              
+53501      -0.0640  0.2833  -0.56844              
+53502      -0.0648  0.2849  -0.56839              
+53503      -0.0655  0.2865  -0.56833              
+53504      -0.0662  0.2881  -0.56836              
+53505      -0.0668  0.2897  -0.56852              
+53506      -0.0675  0.2914  -0.56888              
+53507      -0.0681  0.2930  -0.56947              
+53508      -0.0687  0.2947  -0.57021              
+53509      -0.0692  0.2963  -0.57105              
+53510      -0.0698  0.2980  -0.57193              
+53511      -0.0703  0.2997  -0.57276              
+53512      -0.0707  0.3014  -0.57342              
+53513      -0.0712  0.3031  -0.57382              
+53514      -0.0716  0.3048  -0.57390              
+53515      -0.0720  0.3065  -0.57368              
+53516      -0.0724  0.3082  -0.57329              
+53517      -0.0727  0.3099  -0.57289              
+53518      -0.0730  0.3116  -0.57261              
+53519      -0.0733  0.3133  -0.57251              
+53520      -0.0735  0.3150  -0.57259              
+53521      -0.0738  0.3168  -0.57282              
+53522      -0.0739  0.3185  -0.57313              
+53523      -0.0741  0.3202  -0.57341              
+53524      -0.0742  0.3220  -0.57359              
+53525      -0.0744  0.3237  -0.57358              
+53526      -0.0744  0.3254  -0.57334              
+53527      -0.0745  0.3272  -0.57286              
+53528      -0.0745  0.3289  -0.57213              
+53529      -0.0745  0.3307  -0.57128              
+53530      -0.0745  0.3324  -0.57040              
+53531      -0.0744  0.3342  -0.56951              
+53532      -0.0743  0.3359  -0.56860              
+53533      -0.0742  0.3377  -0.56773              
+53534      -0.0741  0.3394  -0.56698              
+53535      -0.0739  0.3411  -0.56635              
+53536      -0.0737  0.3429  -0.56581              
+53537      -0.0735  0.3446  -0.56535              
+53538      -0.0732  0.3464  -0.56494              
+53539      -0.0730  0.3481  -0.56450              
+53540      -0.0727  0.3498  -0.56391              
+53541      -0.0723  0.3516  -0.56312              
+53542      -0.0720  0.3533  -0.56206              
+53543      -0.0716  0.3550  -0.56086              
+53544      -0.0712  0.3567  -0.55967              
+53545      -0.0707  0.3584  -0.55857              
+53546      -0.0703  0.3602  -0.55766              
+53547      -0.0698  0.3619  -0.55698              
+53548      -0.0693  0.3636  -0.55652              
+53549      -0.0688  0.3653  -0.55618              
+53550      -0.0682  0.3670  -0.55583              
+53551      -0.0676  0.3686  -0.55535              
+53552      -0.0670  0.3703  -0.55466              
+53553      -0.0664  0.3720  -0.55372              
+53554      -0.0657  0.3737  -0.55256              
+53555      -0.0651  0.3753  -0.55120              
+53556      -0.0644  0.3770  -0.54974              
+53557      -0.0636  0.3786  -0.54825              
+53558      -0.0629  0.3802  -0.54681              
+53559      -0.0621  0.3819  -0.54546              
+53560      -0.0613  0.3835  -0.54424              
+53561      -0.0605  0.3851  -0.54312              
+53562      -0.0597  0.3867  -0.54219              
+53563      -0.0589  0.3883  -0.54148              
+53564      -0.0580  0.3899  -0.54093              
+53565      -0.0571  0.3914  -0.54042              
+53566      -0.0562  0.3930  -0.53988              
+53567      -0.0553  0.3945  -0.53925              
+53568      -0.0543  0.3961  -0.53842              
+53569      -0.0533  0.3976  -0.53730              
+53570      -0.0524  0.3991  -0.53592              
+53571      -0.0514  0.4006  -0.53444              
+53572      -0.0503  0.4021  -0.53300              
+53573      -0.0493  0.4036  -0.53172              
+53574      -0.0483  0.4050  -0.53069              
+53575      -0.0472  0.4065  -0.52994              
+53576      -0.0461  0.4079  -0.52941              
+53577      -0.0450  0.4093  -0.52894              
+53578      -0.0439  0.4108  -0.52834              
+53579      -0.0427  0.4122  -0.52750              
+53580      -0.0416  0.4135  -0.52638              
+53581      -0.0404  0.4149  -0.52504              
+53582      -0.0393  0.4163  -0.52353              
+53583      -0.0381  0.4176  -0.52192              
+53584      -0.0369  0.4189  -0.52027              
+53585      -0.0357  0.4202  -0.51871              
+53586      -0.0344  0.4215  -0.51730              
+53587      -0.0332  0.4228  -0.51611              
+53588      -0.0320  0.4240  -0.51514              
+53589      -0.0307  0.4253  -0.51439              
+53590      -0.0294  0.4265  -0.51382              
+53591      -0.0281  0.4277  -0.51339              
+53592      -0.0269  0.4289  -0.51301              
+53593      -0.0256  0.4301  -0.51258              
+53594      -0.0242  0.4312  -0.51201              
+53595      -0.0229  0.4324  -0.51127              
+53596      -0.0216  0.4335  -0.51034              
+53597      -0.0203  0.4346  -0.50926              
+53598      -0.0189  0.4357  -0.50807              
+53599      -0.0176  0.4367  -0.50690              
+53600      -0.0162  0.4378  -0.50589              
+53601      -0.0149  0.4388  -0.50517              
+53602      -0.0135  0.4398  -0.50479              
+53603      -0.0121  0.4408  -0.50472              
+53604      -0.0107  0.4418  -0.50488              
+53605      -0.0093  0.4427  -0.50507              
+53606      -0.0079  0.4437  -0.50510              
+53607      -0.0066  0.4446  -0.50484              
+53608      -0.0052  0.4455  -0.50429              
+53609      -0.0037  0.4463  -0.50351              
+53610      -0.0023  0.4472  -0.50253              
+53611      -0.0009  0.4480  -0.50145              
+53612       0.0005  0.4488  -0.50036              
+53613       0.0019  0.4496  -0.49932              
+53614       0.0033  0.4504  -0.49841              
+53615       0.0047  0.4511  -0.49768              
+53616       0.0062  0.4518  -0.49718              
+53617       0.0076  0.4525  -0.49688              
+53618       0.0090  0.4532  -0.49677              
+53619       0.0104  0.4539  -0.49677              
+53620       0.0119  0.4545  -0.49679              
+53621       0.0133  0.4551  -0.49673              
+53622       0.0147  0.4557  -0.49654              
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/configFileTests.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/configFileTests.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/configFileTests.tex	(revision 22322)
@@ -0,0 +1,565 @@
+\subsection{Complete Examples}
+
+\subsubsection*{SDRS example}
+
+Pass.  Test with and without overwrite.
+
+\begin{verbatim}
+Double     F64     1.23456789      # This is a comment
+Float    F32 0.98765#This is a comment too
+String  STR This is the string that forms the value #comment
+
+ # This is a comment line and is to be ignored
+boolean     BOOL    T # The value of `boolean' is `true'
+ 
+@primes U8  2,3 5 7,11,13 17 #   These are prime numbers
+
+comment MULTI # The rest of this line is ignored, but `comment' is set to be non-unique
+comment STR This
+comment STR     is
+comment STR       a
+comment STR        non-unique
+comment STR                  key
+Float F64 1.23456 # This generates a warning, and, if `overwrite' is `false', is ignored
+\end{verbatim}
+
+\subsubsection*{Gene's example}
+
+Pass.  Overwrite warning.
+
+\begin{verbatim}
+# these are examples of camera definition variables:
+# skyprobe
+NCELL       S32    1
+NCHIP       S32    1
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC     
+CELL.00     STR    %f00.%x     PHU      [0,0:0,0]   CHIP.00   [0,0:0,0] 
+                   
+# megacam-raw      
+NCELL       S32    72
+NCHIP       S32    36
+#                  FILENAME    EXTNAME  DATASEC     CHIP      BIASSEC     
+CELL.00     STR    %f.%x       AMP00    [0,0:0,0]   CHIP.00   BIASSEC
+CELL.01     STR    %f.%x       AMP01    [0,0:0,0]   CHIP.00   [2100,2110:0,4096]   
+CELL.02     STR    %f.%x       AMP02    [0,0:0,0]   CHIP.01   [0,0:0,0]   
+CELL.03     STR    %f.%x       AMP03    [0,0:0,0]   CHIP.01   [0,0:0,0]   
+
+# megacam-splice
+NCELL       S32    72
+NCHIP       S32    36
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC   TRIMSEC
+CELL.00     STR    %f.%x       CCD00    ASEC-00     CHIP.00   BSEC-00   DSEC-00
+CELL.01     STR    %f.%x       CCD00    ASEC-01     CHIP.00   BSEC-01   DSEC-01
+CELL.02     STR    %f.%x       CCD01    ASEC-00     CHIP.01   BSEC-00   DSEC-00
+CELL.03     STR    %f.%x       CCD01    ASEC-01     CHIP.01   BSEC-01   DSEC-01
+
+# cfh12k-split
+NCELL       S32    12
+NCHIP       S32    12
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC     
+CELL.00     STR    %f/%f00.%x  PHU      [0,0:0,0]   CHIP.00   [0,0:0,0]   
+CELL.01     STR    %f/%f01.%x  PHU      [0,0:0,0]   CHIP.01   [0,0:0,0]   
+CELL.02     STR    %f/%f02.%x  PHU      [0,0:0,0]   CHIP.02   [0,0:0,0]   
+
+# cfh12k-mef
+NCELL       S32    12
+NCHIP       S32    12
+#                  FILENAME    EXTNAME  REGION      CHIP      BIASSEC     
+CELL.00     STR    %f.%x       CHIP00   [0,0:0,0]   CHIP.00   [0,0:0,0]   
+CELL.01     STR    %f.%x       CHIP01   [0,0:0,0]   CHIP.01   [0,0:0,0]   
+CELL.02     STR    %f.%x       CHIP02   [0,0:0,0]   CHIP.02   [0,0:0,0]   
+
+#- REGION can be defined by a header keyword in IRAF format or by a explicit IRAF format 
+#- what is default for NAXIS1,2 for IRAF format?
+#- Nreadout is always NAXIS3?
+
+# recipe file:
+# this makes the assumption that, for a given camera, all chips &
+# cells have the same recipe.  this is probably a good start, but may
+# not cut it in general. eg, it is already clear that for 
+
+# recipe file must be a function of time and camera.
+# 
+
+# BIAS:
+BIAS.IMAGE                 STR    NONE
+BIAS.IMAGE  		   STR    FILE:bias.fits
+BIAS.IMAGE  		   STR    DB:BEST
+BIAS.IMAGE  		   STR    DB:CLOSE
+
+BIAS.OVERSCAN 		   STR    HEADER:BIASSEC
+BIAS.OVERSCAN 		   STR    RECIPE:[0,0:0,0]
+BIAS.OVERSCAN 		   STR    NONE
+
+BIAS.OVERSCAN.STATS 	   STR    MEDIAN
+BIAS.OVERSCAN.STATS 	   STR    MEAN
+
+BIAS.OVERSCAN.FIT          STR    SPLINE
+BIAS.OVERSCAN.FIT.NPTS     S32    5
+
+BIAS.OVERSCAN.FIT          STR    POLYNOMIAL
+BIAS.OVERSCAN.FIT.ORDER    S32    3
+BIAS.OVERSCAN.FIT.NBIN     S32    5
+\end{verbatim}
+
+\subsection{METADATA}
+
+\subsubsection*{SDRS example}
+
+Pass.
+
+\begin{verbatim}
+CELL      METADATA
+ EXTNAME   STR   CCD00
+ BIASSEC   STR   BSEC-00
+ CHIP      STR   CHIP.00
+ NCELL     S32   24
+END
+\end{verbatim}
+
+\subsubsection*{nested metadata}
+
+Pass.
+
+\begin{verbatim}
+CELL      METADATA
+    FOO METADATA
+        BAR     STR BAZ
+        PING    STR PONG
+    END
+    
+    EXTNAME   STR   CCD00
+    BIASSEC   STR   BSEC-00
+    CHIP      STR   CHIP.00
+    NCELL     S32   24
+END
+\end{verbatim}
+
+\subsubsection*{deeply nested metadata}
+
+Pass.
+
+\begin{verbatim}
+FOO1 METADATA
+    FOO2 METADATA
+        FOO3 METADATA
+            FOO4 METADATA
+                FOO5 METADATA
+                    FOO6 METADATA
+                        BAR     STR BAZ
+                        PING    STR PONG
+                    END
+                    BAR     STR BAZ
+                    PING    STR PONG
+                END
+                BAR     STR BAZ
+                PING    STR PONG
+            END
+            BAR     STR BAZ
+            PING    STR PONG
+        END
+        BAR     STR BAZ
+        PING    STR PONG
+    END
+    BAR     STR BAZ
+    PING    STR PONG
+END
+\end{verbatim}
+
+\subsubsection*{deeply nested metadata}
+
+Pass.
+
+\begin{verbatim}
+FOO1 METADATA
+    BAR     STR BAZ
+    PING    STR PONG
+    FOO2 METADATA
+        BAR     STR BAZ
+        PING    STR PONG
+        FOO3 METADATA
+            BAR     STR BAZ
+            PING    STR PONG
+            FOO4 METADATA
+                BAR     STR BAZ
+                PING    STR PONG
+                FOO5 METADATA
+                    BAR     STR BAZ
+                    PING    STR PONG
+                    FOO6 METADATA
+                        BAR     STR BAZ
+                        PING    STR PONG
+                    END
+                END
+            END
+        END
+    END
+END
+\end{verbatim}
+
+\subsubsection*{deeply nested metadata}
+
+Pass.
+
+\begin{verbatim}
+FOO1 METADATA
+    BAR     STR BAZ
+    FOO2 METADATA
+        BAR     STR BAZ
+        FOO3 METADATA
+            BAR     STR BAZ
+            FOO4 METADATA
+                BAR     STR BAZ
+                FOO5 METADATA
+                    BAR     STR BAZ
+                    FOO6 METADATA
+                        BAR     STR BAZ
+                        PING    STR PONG
+                    END
+                    PING    STR PONG
+                END
+                PING    STR PONG
+            END
+            PING    STR PONG
+        END
+        PING    STR PONG
+    END
+    PING    STR PONG
+END
+\end{verbatim}
+
+\subsubsection*{two metadata at the same depth}
+
+Pass.
+
+\begin{verbatim}
+FOO1 METADATA
+    FOO2 METADATA
+        BAR     STR BAZ
+        PING    STR PONG
+    END
+    FOO3 METADATA
+        BAR     STR BAZ
+        PING    STR PONG
+    END
+END
+\end{verbatim}
+
+\subsection{TYPE}
+
+\subsubsection*{basic TYPE}
+
+Pass.
+
+\begin{verbatim}
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+\end{verbatim}
+
+\subsubsection*{TYPE with comments}
+
+Pass.
+
+\begin{verbatim}
+TYPE      CELL   EXTNAME   BIASSEC  CHIP    # comment
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00 # foo
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00 #
+\end{verbatim}
+
+\subsubsection*{TYPE not visible in lower scopes}
+
+Pass.
+
+\begin{verbatim}
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+FOO METADATA
+    TYPE      CELL   EXTNAME   BIASSEC
+    CELL.00   CELL   CCD00     BSEC-00
+    CELL.01   CELL   CCD01     BSEC-01
+    FOO METADATA
+        TYPE      CELL   EXTNAME
+        CELL.00   CELL   CCD00
+        CELL.01   CELL   CCD01
+    END
+END
+\end{verbatim}
+
+\subsubsection*{TYPE not in scope}
+
+Fail.
+
+\begin{verbatim}
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+FOO METADATA
+    CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+END
+\end{verbatim}
+
+\subsubsection*{TYPE not in scope}
+
+Fail.
+
+\begin{verbatim}
+FOO METADATA
+    TYPE      CELL   EXTNAME   BIASSEC  CHIP
+END
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+\end{verbatim}
+
+\subsubsection*{TYPE not in scope}
+
+Fail.
+
+\begin{verbatim}
+FOO METADATA
+    TYPE      CELL   EXTNAME   BIASSEC  CHIP
+END
+BAR METADATA
+    CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+END
+\end{verbatim}
+
+\subsubsection*{TYPE with missing parameters}
+
+Fail.
+
+\begin{verbatim}
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+CELL.00   CELL   CCD00     BSEC-00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+\end{verbatim}
+
+\subsubsection*{TYPE redefinition}
+
+Fail.
+
+\begin{verbatim}
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+\end{verbatim}
+
+\subsubsection*{missing TYPE declaration}
+
+Fail.
+
+\begin{verbatim}
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+\end{verbatim}
+
+\subsection{Time}
+
+\subsubsection*{basic IS08601}
+
+Pass.
+
+\begin{verbatim}
+recently    UTC     2005-03-18T16:05:00Z
+recently    UT1     2005-03-18T16:05:00Z
+recently    TAI     2005-03-18T16:05:00Z
+recently    TT      2005-03-18T16:05:00Z
+\end{verbatim}
+
+\subsubsection*{ISO8601 with comments}
+
+Pass.
+
+\begin{verbatim}
+recently    UTC     2005-03-18T16:05:00Z    # foo
+recently    UT1     2005-03-18T16:05:00Z    # bar
+recently    TAI     2005-03-18T16:05:00Z    # baz
+recently    TT      2005-03-18T16:05:00Z    #
+\end{verbatim}
+
+
+\subsubsection*{bad format}
+
+Fail.
+
+\begin{verbatim}
+broken      UTC     2005-03-18T16:05:00
+\end{verbatim}
+
+\subsubsection*{bad format}
+
+Fail.
+
+\begin{verbatim}
+broken      UT1     2005-03-18T16:05:00
+\end{verbatim}
+
+\subsubsection*{bad format}
+
+Fail.
+
+\begin{verbatim}
+broken      TAI     2005-03-18T16:05:00
+\end{verbatim}
+
+\subsubsection*{bad format}
+
+Fail.
+
+\begin{verbatim}
+broken      TT      2005-03-18T16:05:00
+\end{verbatim}
+
+
+\subsection{MULTI}
+
+\subsubsection*{basic MULTI}
+
+\begin{verbatim}
+foo MULTI
+foo S8      -1
+foo STR     bar baz
+foo BOOL    T
+\end{verbatim}
+
+\subsubsection*{MULTI with comments}
+
+Pass.
+
+\begin{verbatim}
+foo MULTI               # foo
+foo S8      -1          # bar
+foo STR     bar baz     # baz
+foo BOOL    T           #
+\end{verbatim}
+
+\subsubsection*{MULTI not visible in lower scopes}
+
+Pass.
+
+\begin{verbatim}
+foo MULTI
+foo S8      -1
+foo STR     bar baz
+foo BOOL    T
+bar METADATA
+    foo MULTI
+    foo S8      -1
+    foo STR     bar baz
+    foo BOOL    T
+END
+\end{verbatim}
+
+\subsubsection*{MULTI METADATA}
+
+Pass.
+
+\begin{verbatim}
+foo MULTI
+foo METADATA
+    bar BOOL    T
+END
+foo METADATA
+    bar BOOL    T
+END
+\end{verbatim}
+
+\subsubsection*{MULTI METADATA structure, not declared}
+
+Pass.  Overwrite warning.
+
+\begin{verbatim}
+foo METADATA
+    bar BOOL    T
+END
+foo METADATA
+    bar BOOL    T
+END
+\end{verbatim}
+
+\subsubsection*{MULTI TYPE}
+
+Pass.
+
+\begin{verbatim}
+foo MULTI
+TYPE bar a b c
+foo  bar x y z
+foo  bar x y z
+\end{verbatim}
+
+\subsubsection*{MULTI TYPE}
+
+Pass.
+
+\begin{verbatim}
+TYPE bar a b c
+foo MULTI
+foo  bar x y z
+foo  bar x y z
+\end{verbatim}
+
+\subsubsection*{MULTI TYPE, not declared}
+
+Pass.  Overwrite warning.
+
+\begin{verbatim}
+TYPE bar a b c
+foo  bar x y z
+foo  bar x y z
+\end{verbatim}
+
+\subsubsection*{MULTI not in scope}
+
+Pass.  Overwrite warning.
+
+\begin{verbatim}
+foo MULTI
+bar METADATA
+    foo S8      -1
+    foo STR     bar baz
+END
+\end{verbatim}
+
+\subsubsection*{MULTI not in scope}
+
+Pass.  Overwrite warning.
+
+\begin{verbatim}
+bar METADATA
+    foo MULTI
+END
+foo S8      -1
+foo STR     bar baz
+\end{verbatim}
+
+\subsubsection*{MULTI not in scope}
+
+Pass.  Overwrite warning.
+
+\begin{verbatim}
+bar METADATA
+    foo MULTI
+END
+baz METADATA
+    foo S8      -1
+END
+\end{verbatim}
+
+\subsubsection*{MULTI redeclaration}
+
+Fail.
+
+\begin{verbatim}
+foo MULTI
+foo MULTI
+foo S8      -1
+\end{verbatim}
+
+\subsubsection*{missing MULTI declaration}
+
+Pass.  Overwrite warning.
+
+\begin{verbatim}
+foo S8      -1
+foo STR     bar baz
+\end{verbatim}
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/eoc_testing.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/eoc_testing.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/eoc_testing.txt	(revision 22322)
@@ -0,0 +1,212 @@
+All angular quantities in this document are in radians, following the psLib convention.
+
+
+1. Start with RA, Dec:
+
+    r = 2.14527700450406
+    d = 0.84758370322182
+
+These get stuffed into a psSphere (obj): psSphereAlloc, followed by
+explicitly setting the values in the struct.
+
+
+2. Convert obj to a psCube (psSphereToCube), and check that:
+
+    x=-0.3596195125758298
+    y=0.5555613903455866
+    z=0.7496834983724809
+
+
+3. Stuff the Sun position into a psCube (sunCube):
+
+    x=1.467797790127511E11
+    y=2.5880956908748722E10
+    z=1.1220046291457653E10
+
+Convert to a psSphere (psCubeToSphere *sun) for later use.
+
+
+4. Gravitational deflection:
+
+    psGravityDeflection(obj, obj, sun);
+
+Convert obj to a psCube, and check that:
+
+    x=-0.35961949760293604
+    y=0.5555613950298085
+    z=0.7496835020836093
+
+
+5. Earth's direction of motion is:
+
+Barycentric velocity of the Earth in m/s:
+    x=5148.713262821658
+    y=-26945.04752348012
+    z=-11682.787302030947
+Diurnal velocity of the observer in m/s:
+    x=-357.6031690489248
+    y=248.46429758174693
+    z=0.09694774143797581
+
+These two vectors need to be summed (standard vector addition).
+Converting to a psSphere gives the direction of motion (psSphere
+*direction), while the magnitude gives the speed, after dividing by
+the speed of light (double speed).
+
+
+6. Aberration:
+
+    psAberration(obj, obj, direction, speed);
+
+Convert obj to a psCube, check that:
+
+    x=-0.35963388069046304
+    y=0.5555192509816625
+    z=0.7497078321908413
+
+
+7. Generate a psTime (time) for 2003-04-01T01:30:00 UTC.  Check that:
+
+time->sec == 1049160600
+time->nsec == 0
+time->leapsecond = false
+
+Copy time and convert to UT1:
+
+     timeUT1 = psTimeAlloc(PS_TIME_UTC);
+     timeUT1->sec = time->sec;
+     timeUT1->nsec = time->nsec;
+     timeUT1->leapsecond = time->leapsecond;
+     timeUT1 = psTimeConvert(timeUT1, PS_TIME_UT1);
+
+Then check timeUT1:
+
+     timeUT1->sec == 1049160599
+     timeUT1->nsec == 657017200 (+/- hundreds of nsec at least)
+
+
+8. Precession:
+
+(a) psEarthPole *precession = psEOC_PrecessionModel(time);
+
+Check that:
+
+    X=2.857175590089105E-4
+    Y=2.3968739377734732E-5
+    S=-1.3970066457904322E-8
+
+(b) psEarthPole *precessionCorr = psEOC_PrecessionCorr(time, PS_IERS_B);
+
+Check that:
+
+    X = 3.05224300720406e-10
+    Y = -1.39441339235822e-10
+    S = 0
+
+(c) Generate the transformation:
+
+    precession->x += precessionCorr->x;
+    precession->y += precessionCorr->y;
+    precession->s += precessionCorr->s;
+    psSphereRot *precessionNutationInv = psSphereRot_CEOtoGCRS(precession); // This is CEO->GCRS
+    psSphereRot precessionNutation = psSphereRotInvert(precessionNutationInv); // This is GCRS->CEO
+
+Check that the rotation quaternion of precessionNutationInv is:
+
+    -1.1984522406756289E-5
+    1.4285893358610674E-4
+    1.2191193518914336E-10
+    -0.9999999897238481
+
+(d) Apply the rotation to the position:
+
+    obj = psSphereRotApply(obj, precessionNutation, obj);
+
+Convert obj to a psCube and check that:
+
+    x=-0.3598480726985338
+    y=0.5555012823608123
+    z=0.7496183628158023
+
+
+9. Earth rotation
+
+(a) Get the tidal correction:
+
+    psEarthPole *tidalCorr = psEOC_PolarTideCorr(time);
+
+(b) Generate the transformation:
+
+    psSphereRot *earthRot = psSphereRot_TEOtoCEO(time, tidalCorr); // This is TEO->CEO
+
+Check that the rotation quaternion of earthRot is:
+
+    0.0
+    0.0
+    0.9625401009002903
+    -0.2711393629830588
+
+Within psSphereRot_TEOtoCEO, the Earth rotation angle should be 428251.4641536639 degrees
+
+(c) Apply the rotation to the position:
+
+    psSphereRot *earthRotInv = psSphereRotInvert(earthRot); // This is CEO->TEO
+    obj = psSphereRotApply(obj, earthRotInv, obj);
+
+Convert obj to a psCube and check that:
+
+    x=0.01698625430807123
+    y=-0.6616523084626379
+    z=0.7496183628158023
+
+
+10. Polar Motion:
+
+(a) psEarthPole *earthPM = psEOC_GetPolarMotion(time, PS_IERS_B);
+(b) psEarthPole *nutationCorr = psEOC_NutationCorr(time);
+
+Add the above components:
+
+    earthPM->x += nutationCorr->x;
+    earthPM->y += nutationCorr->y;
+    earthPM->s += nutationCorr->s;
+
+Add the tidal correction:
+
+    earthPM->x += tidalCorr->x;
+    earthPM->y += tidalCorr->y;
+
+Check that:
+
+    x=-6.43607313124045e-07
+    y=2.11351436973568e-06
+    s=-7.39617581324646e-12
+
+(c) Generate the transformation:
+
+   psSphereRot *polarMotion = psSphereRot_ITRStoTEO(earthPM); // This is ITRS->TEO
+
+Check that the rotation quaternion of polarMotion is:
+
+    -1.0567571848664005E-6
+    3.218036562931509E-7
+    -3.3580195807204483E-12
+    -0.9999999999993899
+ 
+(d) Apply these to the position:
+
+   psSphereRot *polarMotionInv = psSphereRotInvert(polarMotion); // This is TEO->ITRS
+   obj = psSphereRotApply(obj, polarMotionInv, obj);
+
+Convert obj to a psCube, should get:
+   
+    x=0.01698577185310146
+    y=-0.6616538927902393
+    z=0.7496169753347885
+
+
+11. The psLib SDRS does not define functions to go beyond this point
+(specifically, atmospheric refraction correction, and conversion to
+the observer's horizon coordinates), so we stop here.  But check that
+the above sequence can be executed faithfully in the reverse order as
+well.
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/eopc01_1900_2004.40.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/eopc01_1900_2004.40.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/eopc01_1900_2004.40.dat	(revision 22322)
@@ -0,0 +1,2092 @@
+# EOP C 01 1900.00--2004.40
+# http://hpiers.obspm.fr/eoppc/eop/eopc01/eopc01.1900-2004
+# MJD       XP(")   YP(")   UT1-UTC(s)
+15020 -.212818 -.097229 .0000000
+15038.3 -.047461 -.065631 .0000000
+15056.5 -.112006 -.040467 .0000000
+15074.8 -.076710 -.009674 .0000000
+15093 -.099850 -.066660 .0000000
+15111.3 -.131173 -.072076 .0000000
+15129.6 -.087390 -.089427 .0000000
+15147.8 -.164078 -.131034 .0000000
+15166.1 -.151407 -.105838 .0000000
+15184.4 -.143764 -.179125 .0000000
+15202.6 -.124341 -.105747 .0000000
+15220.9 -.206229 -.144555 .0000000
+15239.1 -.192711 -.095563 .0000000
+15257.4 -.190299 -.067717 .0000000
+15275.7 -.135718 -.081480 .0000000
+15293.9 -.153807 .030568 .0000000
+15312.2 -.173818 .001034 .0000000
+15330.5 -.257716 -.006499 .0000000
+15348.7 -.209085 .046511 .0000000
+15367 -.133145 .004905 .0000000
+15385.2 -.138242 .014255 .0000000
+15403.5 -.162602 .006609 .0000000
+15421.8 -.121242 -.000875 .0000000
+15440 -.109773 .024691 .0000000
+15458.3 -.103805 .039564 .0000000
+15476.6 -.021306 -.011519 .0000000
+15494.8 -.001300 .021700 .0000000
+15513.1 .037156 -.049753 .0000000
+15531.3 .003993 -.095314 .0000000
+15549.6 .054079 -.081336 .0000000
+15567.9 .089761 -.084901 .0000000
+15586.1 .004864 -.187233 .0000000
+15604.4 -.023099 -.179528 .0000000
+15622.6 -.057910 -.197673 .0000000
+15640.9 -.033170 -.202981 .0000000
+15659.2 -.155604 -.187085 .0000000
+15677.4 -.169658 -.183163 .0000000
+15695.7 -.188623 -.181458 .0000000
+15714 -.223086 -.144566 .0000000
+15732.2 -.254958 -.114447 .0000000
+15750.5 -.259243 -.078157 .0000000
+15768.7 -.211792 .038633 .0000000
+15787 -.216991 .064250 .0000000
+15805.3 -.242537 .171246 .0000000
+15823.5 -.188324 .158468 .0000000
+15841.8 -.152570 .207171 .0000000
+15860.1 -.062244 .171387 .0000000
+15878.3 -.016045 .182141 .0000000
+15896.6 .036032 .156268 .0000000
+15914.8 .217293 .252814 .0000000
+15933.1 .119283 .066262 .0000000
+15951.4 .125538 .016781 .0000000
+15969.6 .082496 -.058369 .0000000
+15987.9 .051287 -.069631 .0000000
+16006.2 .051239 -.093785 .0000000
+16024.4 -.024248 -.151921 .0000000
+16042.7 -.054789 -.179957 .0000000
+16060.9 -.146320 -.146959 .0000000
+16079.2 -.193061 -.169534 .0000000
+16097.5 -.237694 -.119404 .0000000
+16115.7 -.319420 -.043030 .0000000
+16134 -.341204 .031366 .0000000
+16152.3 -.345930 -.002271 .0000000
+16170.5 -.363449 .022246 .0000000
+16188.8 -.363639 .126744 .0000000
+16207 -.275204 .088178 .0000000
+16225.3 -.217973 .142752 .0000000
+16243.6 -.133649 .193127 .0000000
+16261.8 -.073538 .187785 .0000000
+16280.1 -.033275 .242905 .0000000
+16298.3 -.025665 .185828 .0000000
+16316.6 .065994 .199396 .0000000
+16334.9 .124642 .131603 .0000000
+16353.1 .138085 .043707 .0000000
+16371.4 .131036 -.010458 .0000000
+16389.7 .129875 -.132885 .0000000
+16407.9 .020188 -.149501 .0000000
+16426.2 -.013164 -.224089 .0000000
+16444.4 .017200 -.148400 .0000000
+16462.7 -.054459 -.187411 .0000000
+16481 -.148856 -.076687 .0000000
+16499.2 -.254004 -.136031 .0000000
+16517.5 -.260513 -.134266 .0000000
+16535.8 -.250640 -.062308 .0000000
+16554 -.318450 -.022118 .0000000
+16572.3 -.293126 .024733 .0000000
+16590.5 -.249230 .052166 .0000000
+16608.8 -.263860 .072953 .0000000
+16627.1 -.210918 .172463 .0000000
+16645.3 -.152700 .188900 .0000000
+16663.6 -.109676 .182564 .0000000
+16681.9 -.082798 .158201 .0000000
+16700.1 .001500 .164000 .0000000
+16718.4 .042084 .134884 .0000000
+16736.6 .062535 .070240 .0000000
+16754.9 .083512 .063227 .0000000
+16773.2 .042326 .010531 .0000000
+16791.4 .042284 -.029751 .0000000
+16809.7 -.008473 -.063250 .0000000
+16827.9 .028258 -.057405 .0000000
+16846.2 -.040538 -.099671 .0000000
+16864.5 -.017099 -.158118 .0000000
+16882.7 -.105713 -.176117 .0000000
+16901 -.176213 -.100777 .0000000
+16919.3 -.231493 -.136695 .0000000
+16937.5 -.251440 -.151097 .0000000
+16955.8 -.248000 -.108800 .0000000
+16974 -.304428 .045206 .0000000
+16992.3 -.236545 .042822 .0000000
+17010.6 -.250592 .048859 .0000000
+17028.8 -.185960 .112145 .0000000
+17047.1 -.156532 .117102 .0000000
+17065.4 -.109352 .166247 .0000000
+17083.6 -.067846 .135598 .0000000
+17101.9 .014037 .151407 .0000000
+17120.1 -.034403 .175804 .0000000
+17138.4 -.031721 .049970 .0000000
+17156.7 .025746 .080453 .0000000
+17174.9 .024021 -.056728 .0000000
+17193.2 .040706 -.006383 .0000000
+17211.5 .003853 -.026096 .0000000
+17229.7 -.017613 -.075020 .0000000
+17248 -.157406 -.092487 .0000000
+17266.2 -.067506 -.080298 .0000000
+17284.5 -.149450 -.119103 .0000000
+17302.8 -.128247 -.061169 .0000000
+17321 -.179648 -.057693 .0000000
+17339.3 -.186449 -.044109 .0000000
+17357.6 -.208575 -.019448 .0000000
+17375.8 -.217870 .042944 .0000000
+17394.1 -.199891 .058881 .0000000
+17412.3 -.173543 .070934 .0000000
+17430.6 -.182003 .113024 .0000000
+17448.9 -.142776 .095154 .0000000
+17467.1 -.093740 .095224 .0000000
+17485.4 -.093900 .111200 .0000000
+17503.6 -.102758 .078405 .0000000
+17521.9 -.009641 .137918 .0000000
+17540.2 -.126885 .077766 .0000000
+17558.4 -.085940 .122947 .0000000
+17576.7 -.079416 .150174 .0000000
+17595 -.067350 .041498 .0000000
+17613.2 -.027042 .024883 .0000000
+17631.5 -.069913 .039794 .0000000
+17649.7 -.059194 -.025833 .0000000
+17668 -.032978 -.010025 .0000000
+17686.3 -.032298 -.079952 .0000000
+17704.5 -.012800 -.038790 .0000000
+17722.8 -.094517 -.051596 .0000000
+17741.1 -.089447 -.124297 .0000000
+17759.3 -.131771 -.098748 .0000000
+17777.6 -.109356 -.122489 .0000000
+17795.8 -.134764 -.084340 .0000000
+17814.1 -.150915 -.095204 .0000000
+17832.4 -.183652 -.070567 .0000000
+17850.6 -.247755 -.037271 .0000000
+17868.9 -.276782 .006594 .0000000
+17887.2 -.260675 .115176 .0000000
+17905.4 -.279309 .066165 .0000000
+17923.7 -.247411 .158512 .0000000
+17941.9 -.179825 .190418 .0000000
+17960.2 -.162104 .175855 .0000000
+17978.5 -.073775 .244495 .0000000
+17996.7 -.088534 .184551 .0000000
+18015 .072181 .185212 .0000000
+18033.2 .000811 .129285 .0000000
+18051.5 .094290 .125029 .0000000
+18069.8 .098031 .048028 .0000000
+18088 .085376 -.037173 .0000000
+18106.3 .110614 -.053618 .0000000
+18124.6 .110175 -.121985 .0000000
+18142.8 .062891 -.147259 .0000000
+18161.1 .016036 -.174778 .0000000
+18179.3 -.044507 -.258578 .0000000
+18197.6 -.126698 -.236805 .0000000
+18215.9 -.185227 -.279032 .0000000
+18234.1 -.218671 -.234349 .0000000
+18252.4 -.258124 -.193490 .0000000
+18270.7 -.348498 -.117029 .0000000
+18288.9 -.441656 -.087821 .0000000
+18307.2 -.418026 .032515 .0000000
+18325.4 -.464700 .053100 .0000000
+18343.7 -.381402 .081369 .0000000
+18362 -.331872 .208343 .0000000
+18380.2 -.262800 .230900 .0000000
+18398.5 -.180022 .262477 .0000000
+18416.8 -.088674 .272316 .0000000
+18435 -.017300 .301900 .0000000
+18453.3 .082311 .293915 .0000000
+18471.5 .145682 .242356 .0000000
+18489.8 .159939 .145514 .0000000
+18508.1 .219001 .050735 .0000000
+18526.3 .251107 .038325 .0000000
+18544.6 .216000 -.092100 .0000000
+18562.8 .239771 -.129218 .0000000
+18581.1 .122030 -.229024 .0000000
+18599.4 -.027219 -.280542 .0000000
+18617.6 -.097816 -.318227 .0000000
+18635.9 -.212272 -.273213 .0000000
+18654.2 -.262295 -.211841 .0000000
+18672.4 -.323074 -.200846 .0000000
+18690.7 -.442329 -.257217 .0000000
+18708.9 -.448532 -.107453 .0000000
+18727.2 -.408744 .030815 .0000000
+18745.5 -.414020 -.006268 .0000000
+18763.7 -.385676 .162890 .0000000
+18782 -.368387 .272702 .0000000
+18800.3 -.268774 .293213 .0000000
+18818.5 -.147573 .342824 .0000000
+18836.8 -.081273 .328529 .0000000
+18855 -.020932 .388520 .0000000
+18873.3 .105674 .327555 .0000000
+18891.6 .176301 .312524 .0000000
+18909.8 .191756 .171612 .0000000
+18928.1 .222257 .091742 .0000000
+18946.4 .211332 -.009012 .0000000
+18964.6 .205000 -.105700 .0000000
+18982.9 .120900 -.162494 .0000000
+19001.1 .083916 -.253801 .0000000
+19019.4 -.057477 -.280025 .0000000
+19037.7 -.089579 -.260914 .0000000
+19055.9 -.218786 -.263597 .0000000
+19074.2 -.224700 -.257286 .0000000
+19092.5 -.335860 -.182205 .0000000
+19110.7 -.267000 -.050900 .0000000
+19129 -.370633 .021540 .0000000
+19147.2 -.353109 .047330 .0000000
+19165.5 -.332727 .126340 .0000000
+19183.8 -.234107 .168826 .0000000
+19202 -.272718 .310644 .0000000
+19220.3 -.193283 .331807 .0000000
+19238.5 -.136796 .347029 .0000000
+19256.8 -.056770 .304448 .0000000
+19275.1 .060600 .363500 .0000000
+19293.3 .112694 .325844 .0000000
+19311.6 .129599 .242729 .0000000
+19329.9 .130866 .155825 .0000000
+19348.1 .158551 .088616 .0000000
+19366.4 .145314 -.044498 .0000000
+19384.6 .095603 -.041933 .0000000
+19402.9 .083131 -.041367 .0000000
+19421.2 -.038956 -.097988 .0000000
+19439.4 .078300 -.165300 .0000000
+19457.7 -.088995 -.223549 .0000000
+19476 -.127587 -.102840 .0000000
+19494.2 -.227272 -.194080 .0000000
+19512.5 -.209405 -.109730 .0000000
+19530.7 -.237021 -.041122 .0000000
+19549 -.262082 -.006966 .0000000
+19567.3 -.225320 .056285 .0000000
+19585.5 -.263907 .044713 .0000000
+19603.8 -.196951 .144625 .0000000
+19622.1 -.198267 .185500 .0000000
+19640.3 -.219124 .151146 .0000000
+19658.6 -.175982 .183356 .0000000
+19676.8 -.160280 .162811 .0000000
+19695.1 -.103200 .187700 .0000000
+19713.4 -.078852 .172599 .0000000
+19731.6 -.076719 .222193 .0000000
+19749.9 -.018200 .178000 .0000000
+19768.1 -.088918 .098053 .0000000
+19786.4 -.043449 .115101 .0000000
+19804.7 -.001323 .124168 .0000000
+19822.9 -.021744 .057252 .0000000
+19841.2 .011122 .015761 .0000000
+19859.5 -.010477 -.071666 .0000000
+19877.7 .032491 .003044 .0000000
+19896 .048101 -.050706 .0000000
+19914.2 .032669 -.039496 .0000000
+19932.5 .008311 -.068348 .0000000
+19950.8 .000027 -.047918 .0000000
+19969 -.076685 -.083122 .0000000
+19987.3 -.108057 -.085386 .0000000
+20005.6 -.100400 -.040800 .0000000
+20023.8 -.162152 -.081823 .0000000
+20042.1 -.223157 -.030051 .0000000
+20060.3 -.224000 -.002700 .0000000
+20078.6 -.226344 .078679 .0000000
+20096.9 -.200560 .102525 .0000000
+20115.1 -.313300 .074600 .0000000
+20133.4 -.203986 .155067 .0000000
+20151.7 -.260937 .252070 .0000000
+20169.9 -.152037 .221001 .0000000
+20188.2 -.093945 .191653 .0000000
+20206.4 -.063798 .184214 .0000000
+20224.7 -.013635 .132013 .0000000
+20243 .045383 .170364 .0000000
+20261.2 .034013 .110450 .0000000
+20279.5 .073793 .089719 .0000000
+20297.7 .106389 .069624 .0000000
+20316 .104207 .003118 .0000000
+20334.3 .085502 -.025467 .0000000
+20352.5 .049258 -.008534 .0000000
+20370.8 -.013923 -.060487 .0000000
+20389.1 -.046141 -.151237 .0000000
+20407.3 -.094789 -.177467 .0000000
+20425.6 -.140169 -.214589 .0000000
+20443.8 -.220007 -.164939 .0000000
+20462.1 -.271487 -.080598 .0000000
+20480.4 -.197162 -.003731 .0000000
+20498.6 -.365371 .018627 .0000000
+20516.9 -.311390 .018527 .0000000
+20535.2 -.363577 .127944 .0000000
+20553.4 -.230546 .091634 .0000000
+20571.7 -.285678 .254673 .0000000
+20589.9 -.300793 .364102 .0000000
+20608.2 -.145488 .374588 .0000000
+20626.5 -.084157 .325899 .0000000
+20644.7 -.008400 .253100 .0000000
+20663 .086674 .275352 .0000000
+20681.3 .081245 .210673 .0000000
+20699.5 .121650 .162508 .0000000
+20717.8 .169064 -.052355 .0000000
+20736 .112084 .002862 .0000000
+20754.3 .099188 -.061928 .0000000
+20772.6 .093628 -.172293 .0000000
+20790.8 -.031363 -.267297 .0000000
+20809.1 -.050020 -.262266 .0000000
+20827.4 -.047776 -.265495 .0000000
+20845.6 -.135461 -.219164 .0000000
+20863.9 -.250486 -.127502 .0000000
+20882.1 -.339030 -.192532 .0000000
+20900.4 -.414793 -.366168 .0000000
+20918.7 -.341536 -.026629 .0000000
+20936.9 -.344556 .102289 .0000000
+20955.2 -.312204 .143646 .0000000
+20973.4 -.283589 .181301 .0000000
+20991.7 -.251093 .263902 .0000000
+21010 -.158543 .396971 .0000000
+21028.2 -.236712 .246063 .0000000
+21046.5 -.049133 .295525 .0000000
+21064.8 -.019000 .215800 .0000000
+21083 .091660 .193570 .0000000
+21101.3 .171184 .142932 .0000000
+21119.5 .135500 .053900 .0000000
+21137.8 .159276 -.060418 .0000000
+21156.1 .195249 -.060384 .0000000
+21174.3 .041662 -.606821 .0000000
+21192.6 .012991 -.219566 .0000000
+21210.9 -.003679 -.318930 .0000000
+21229.1 -.095516 -.232917 .0000000
+21247.4 -.151536 -.182785 .0000000
+21265.6 -.182049 -.136279 .0000000
+21283.9 -.292198 -.114514 .0000000
+21302.2 -.293211 -.077170 .0000000
+21320.4 -.310501 -.047076 .0000000
+21338.7 -.262144 .028782 .0000000
+21357 -.195616 .145893 .0000000
+21375.2 -.216823 .172434 .0000000
+21393.5 -.195552 .154519 .0000000
+21411.7 -.114387 .252819 .0000000
+21430 -.014913 .095478 .0000000
+21448.3 -.032948 .203552 .0000000
+21466.5 -.004274 .202191 .0000000
+21484.8 -.029300 .187800 .0000000
+21503 .047363 .077854 .0000000
+21521.3 .027454 .011902 .0000000
+21539.6 .086395 .016630 .0000000
+21557.8 .011086 .026413 .0000000
+21576.1 .061347 -.033647 .0000000
+21594.4 -.045800 -.126500 .0000000
+21612.6 -.063352 -.187119 .0000000
+21630.9 -.140380 -.029766 .0000000
+21649.1 -.139590 -.046948 .0000000
+21667.4 -.186229 -.012003 .0000000
+21685.7 -.197272 -.022847 .0000000
+21703.9 -.162233 .009864 .0000000
+21722.2 -.209522 .106965 .0000000
+21740.5 -.183315 .111698 .0000000
+21758.7 -.186120 .161230 .0000000
+21777 -.206139 .170211 .0000000
+21795.2 -.140500 .134700 .0000000
+21813.5 -.075624 .126939 .0000000
+21831.8 -.035250 .149125 .0000000
+21850 -.038388 .139481 .0000000
+21868.3 .005607 .126979 .0000000
+21886.6 -.039489 .133454 .0000000
+21904.8 -.047400 .170000 .0000000
+21923.1 -.061884 .112716 .0000000
+21941.3 -.059354 .062420 .0000000
+21959.6 -.058158 .016625 .0000000
+21977.9 -.045037 .121493 .0000000
+21996.1 -.064165 .072740 .0000000
+22014.4 -.046142 .055541 .0000000
+22032.7 -.076649 .084834 .0000000
+22050.9 -.032000 .101400 .0000000
+22069.2 -.032764 .042454 .0000000
+22087.4 -.034002 .057361 .0000000
+22105.7 -.081513 .071678 .0000000
+22124 -.024396 .011394 .0000000
+22142.2 -.005435 .014449 .0000000
+22160.5 -.036700 -.072300 .0000000
+22178.7 -.054480 -.035798 .0000000
+22197 -.109833 -.018115 .0000000
+22215.3 -.180209 -.046368 .0000000
+22233.5 -.144050 .048583 .0000000
+22251.8 -.181892 -.009524 .0000000
+22270.1 -.159213 .054630 .0000000
+22288.3 -.092562 -.197751 .0000000
+22306.6 -.186227 .107207 .0000000
+22324.8 -.097388 .112723 .0000000
+22343.1 -.145513 .244405 .0000000
+22361.4 -.066010 .127964 .0000000
+22379.6 -.108000 .125400 .0000000
+22397.9 -.066646 .249459 .0000000
+22416.2 .062692 .241400 .0000000
+22434.4 .077897 .212694 .0000000
+22452.7 .112620 .177084 .0000000
+22470.9 .081000 .157700 .0000000
+22489.2 .148386 .115656 .0000000
+22507.5 .111770 .088776 .0000000
+22525.7 .063562 -.046004 .0000000
+22544 .129866 .027842 .0000000
+22562.3 .091341 -.043051 .0000000
+22580.5 .068767 -.053549 .0000000
+22598.8 -.008467 -.089633 .0000000
+22617 -.052510 -.063097 .0000000
+22635.3 -.131312 -.127174 .0000000
+22653.6 -.098074 -.069955 .0000000
+22671.8 -.202848 -.093388 .0000000
+22690.1 -.275100 .041200 .0000000
+22708.3 -.199380 .005397 .0000000
+22726.6 -.269861 .062960 .0000000
+22744.9 -.315700 .186534 .0000000
+22763.1 -.190287 .164991 .0000000
+22781.4 -.119466 .139311 .0000000
+22799.7 -.014599 .217462 .0000000
+22817.9 .031054 .201362 .0000000
+22836.2 .091820 .160909 .0000000
+22854.4 .100985 .083825 .0000000
+22872.7 .212445 .182524 .0000000
+22891 .138182 .088243 .0000000
+22909.2 .178414 .025800 .0000000
+22927.5 .167323 .004838 .0000000
+22945.8 .073545 -.078915 .0000000
+22964 .127179 -.080673 .0000000
+22982.3 -.002474 -.169272 .0000000
+23000.5 -.007518 -.143864 .0000000
+23018.8 -.098209 .106262 .0000000
+23037.1 -.133071 -.095128 .0000000
+23055.3 -.045524 -.029486 .0000000
+23073.6 -.194756 -.097510 .0000000
+23091.9 -.208934 .039567 .0000000
+23110.1 -.210300 .137300 .0000000
+23128.4 -.165744 .125734 .0000000
+23146.6 -.162390 .134218 .0000000
+23164.9 -.132697 .221570 .0000000
+23183.2 -.090259 .277564 .0000000
+23201.4 -.047480 .260568 .0000000
+23219.7 .009070 .222538 .0000000
+23237.9 .111622 .240501 .0000000
+23256.2 .088770 .114259 .0000000
+23274.5 .163154 .076991 .0000000
+23292.7 .151317 .014994 .0000000
+23311 .141800 .014900 .0000000
+23329.3 .120772 -.019145 .0000000
+23347.5 .083381 -.139651 .0000000
+23365.8 .063654 -.088851 .0000000
+23384 .041976 .015059 .0000000
+23402.3 -.163179 -.120346 .0000000
+23420.6 -.162345 -.088042 .0000000
+23438.8 -.166986 -.055441 .0000000
+23457.1 -.332673 -.032441 .0000000
+23475.4 -.262008 -.033456 .0000000
+23493.6 -.207748 .016212 .0000000
+23511.9 -.274792 .140175 .0000000
+23530.1 -.179152 .157417 .0000000
+23548.4 -.176779 .129676 .0000000
+23566.7 -.097364 .300984 .0000000
+23584.9 -.024124 .317510 .0000000
+23603.2 -.019994 .239535 .0000000
+23621.5 .060132 .240230 .0000000
+23639.7 .112367 .311274 .0000000
+23658 .142183 .249476 .0000000
+23676.2 .151780 .226170 .0000000
+23694.5 .161990 .129040 .0000000
+23712.8 .130175 .020789 .0000000
+23731 .136144 .082803 .0000000
+23749.3 .088243 .024731 .0000000
+23767.6 .035504 -.012841 .0000000
+23785.8 -.005804 .073064 .0000000
+23804.1 -.068517 .011667 .0000000
+23822.3 -.029063 .003840 .0000000
+23840.6 -.149500 -.020500 .0000000
+23858.9 -.151107 .038096 .0000000
+23877.1 -.170714 .072246 .0000000
+23895.4 -.127400 .135100 .0000000
+23913.6 -.165261 .139194 .0000000
+23931.9 -.187563 .107926 .0000000
+23950.2 -.081980 .195088 .0000000
+23968.4 -.075229 .183050 .0000000
+23986.7 -.059435 .142566 .0000000
+24005 .014627 .135909 .0000000
+24023.2 .029893 .185564 .0000000
+24041.5 -.002107 .154303 .0000000
+24059.7 .041539 .113626 .0000000
+24078 .032319 .000498 .0000000
+24096.3 -.079291 .056224 .0000000
+24114.5 -.006172 .025546 .0000000
+24132.8 .035597 .085593 .0000000
+24151.1 -.012995 .036963 .0000000
+24169.3 -.056182 -.049412 .0000000
+24187.6 -.103481 -.003581 .0000000
+24205.8 -.107312 -.041495 .0000000
+24224.1 -.141179 -.046236 .0000000
+24242.4 -.102647 .016748 .0000000
+24260.6 -.130539 .119861 .0000000
+24278.9 -.066774 .097721 .0000000
+24297.2 -.004013 .055231 .0000000
+24315.4 -.066873 .032174 .0000000
+24333.7 -.055672 .061909 .0000000
+24351.9 -.070311 .114851 .0000000
+24370.2 .017853 .118506 .0000000
+24388.5 -.015743 .056794 .0000000
+24406.7 .001485 .004726 .0000000
+24425 -.005631 .015549 .0000000
+24443.2 .001128 .013928 .0000000
+24461.5 -.243256 -.060509 .0000000
+24479.8 -.025734 .007049 .0000000
+24498 -.041648 -.013321 .0000000
+24516.3 -.199605 -.083663 .0000000
+24534.6 -.163818 -.029853 .0000000
+24552.8 -.073051 -.074540 .0000000
+24571.1 -.177265 -.060285 .0000000
+24589.3 -.230084 -.024826 .0000000
+24607.6 -.173813 .058121 .0000000
+24625.9 -.052708 .122825 .0000000
+24644.1 -.106664 .067451 .0000000
+24662.4 -.129567 .112402 .0000000
+24680.7 -.076581 .132865 .0000000
+24698.9 -.051765 .131885 .0000000
+24717.2 -.033933 .119936 .0000000
+24735.4 -.053049 .122331 .0000000
+24753.7 -.021532 .117705 .0000000
+24772 .018915 .156256 .0000000
+24790.2 -.001894 .104146 .0000000
+24808.5 -.075772 .076597 .0000000
+24826.8 -.120294 .111799 .0000000
+24845 -.050061 .130391 .0000000
+24863.3 -.098525 .073404 .0000000
+24881.5 -.058200 .056215 .0000000
+24899.8 -.142988 .109762 .0000000
+24918.1 -.032237 .116674 .0000000
+24936.3 -.095191 .115885 .0000000
+24954.6 -.059447 .084129 .0000000
+24972.8 -.115876 .071429 .0000000
+24991.1 -.052232 .049356 .0000000
+25009.4 -.054137 .027885 .0000000
+25027.6 -.013624 .120684 .0000000
+25045.9 .095331 .074304 .0000000
+25064.2 .015128 .128662 .0000000
+25082.4 -.016024 .158240 .0000000
+25100.7 -.031647 .047004 .0000000
+25118.9 -.020014 .048718 .0000000
+25137.2 -.004146 .054252 .0000000
+25155.5 -.052691 .005745 .0000000
+25173.7 -.052571 -.025327 .0000000
+25192 -.093446 -.044573 .0000000
+25210.3 -.185615 .036816 .0000000
+25228.5 -.159711 -.002380 .0000000
+25246.8 -.008822 -.065239 .0000000
+25265 -.145700 -.017500 .0000000
+25283.3 -.134670 .100011 .0000000
+25301.6 -.142914 .014838 .0000000
+25319.8 -.130658 .109191 .0000000
+25338.1 -.071799 .135022 .0000000
+25356.4 -.077913 .109713 .0000000
+25374.6 -.084562 .184119 .0000000
+25392.9 -.070452 .122462 .0000000
+25411.1 -.071262 .128166 .0000000
+25429.4 -.023166 .138252 .0000000
+25447.7 -.049392 .114652 .0000000
+25465.9 -.132084 -.134582 .0000000
+25484.2 .020427 .100107 .0000000
+25502.5 -.028438 .032299 .0000000
+25520.7 -.110100 .107500 .0000000
+25539 -.143376 -.000841 .0000000
+25557.2 -.115036 .011216 .0000000
+25575.5 -.110841 .085727 .0000000
+25593.8 -.072093 .151032 .0000000
+25612 -.160303 .044210 .0000000
+25630.3 -.288649 .003539 .0000000
+25648.5 -.100071 .109711 .0000000
+25666.8 -.169363 .171496 .0000000
+25685.1 -.126222 .140853 .0000000
+25703.3 -.057249 .149535 .0000000
+25721.6 .003629 .164681 .0000000
+25739.9 .131595 .332923 .0000000
+25758.1 .031158 .160059 .0000000
+25776.4 .069220 .115295 .0000000
+25794.6 .103978 .187112 .0000000
+25812.9 .082883 .085168 .0000000
+25831.2 .049832 -.000080 .0000000
+25849.4 .073767 .013919 .0000000
+25867.7 .084972 .064950 .0000000
+25886 -.015329 -.028934 .0000000
+25904.2 -.093999 -.073280 .0000000
+25922.5 -.161299 -.061529 .0000000
+25940.7 -.165908 -.084395 .0000000
+25959 -.205862 .028014 .0000000
+25977.3 -.214153 -.044059 .0000000
+25995.5 -.215942 .054329 .0000000
+26013.8 -.225480 .057826 .0000000
+26032.1 -.206525 .096138 .0000000
+26050.3 -.166814 .146704 .0000000
+26068.6 -.130922 .224514 .0000000
+26086.8 -.049593 .179473 .0000000
+26105.1 -.125200 .154900 .0000000
+26123.4 .006745 .237422 .0000000
+26141.6 .017674 .198183 .0000000
+26159.9 .035577 .132240 .0000000
+26178.1 .081022 .126161 .0000000
+26196.4 .098407 .114242 .0000000
+26214.7 .074379 -.037760 .0000000
+26232.9 .062912 -.022243 .0000000
+26251.2 .028598 .019082 .0000000
+26269.5 -.011225 -.081563 .0000000
+26287.7 -.084804 -.009200 .0000000
+26306 -.116025 -.003658 .0000000
+26324.2 -.158405 -.031682 .0000000
+26342.5 -.353309 .027867 .0000000
+26360.8 -.277671 .046550 .0000000
+26379 -.239000 -.015100 .0000000
+26397.3 -.355078 .116611 .0000000
+26415.6 -.226864 .167103 .0000000
+26433.8 -.248657 .182523 .0000000
+26452.1 -.150564 .238762 .0000000
+26470.3 -.151393 .231228 .0000000
+26488.6 -.039529 .281355 .0000000
+26506.9 .024987 .296176 .0000000
+26525.1 .039600 .242500 .0000000
+26543.4 .074415 .182812 .0000000
+26561.7 .098426 .094944 .0000000
+26579.9 .082462 .055652 .0000000
+26598.2 .165785 .079323 .0000000
+26616.4 .067878 .029704 .0000000
+26634.7 .052973 -.106345 .0000000
+26653 -.083590 -.105084 .0000000
+26671.2 -.060868 -.054736 .0000000
+26689.5 -.091079 -.143458 .0000000
+26707.8 -.117202 -.090917 .0000000
+26726 -.196529 -.057717 .0000000
+26744.3 -.166200 -.015500 .0000000
+26762.5 -.312357 .014781 .0000000
+26780.8 -.324819 .020690 .0000000
+26799.1 -.229236 .137146 .0000000
+26817.3 -.199450 .181143 .0000000
+26835.6 -.169007 .207207 .0000000
+26853.8 -.138350 .219864 .0000000
+26872.1 -.071670 .214482 .0000000
+26890.4 -.075142 .230743 .0000000
+26908.6 -.052070 .236227 .0000000
+26926.9 .026338 .224720 .0000000
+26945.2 .071800 .178200 .0000000
+26963.4 .070092 .209808 .0000000
+26981.7 .087369 .059518 .0000000
+26999.9 .122666 .122560 .0000000
+27018.2 .085699 .068358 .0000000
+27036.5 .094388 .033874 .0000000
+27054.7 .097006 .030486 .0000000
+27073 -.073655 -.011865 .0000000
+27091.3 -.058596 .014472 .0000000
+27109.5 -.188030 -.012586 .0000000
+27127.8 -.173476 .013624 .0000000
+27146 -.221474 .048831 .0000000
+27164.3 -.211408 .107715 .0000000
+27182.6 -.186714 .112536 .0000000
+27200.8 -.179116 .019117 .0000000
+27219.1 -.132931 .166511 .0000000
+27237.4 -.129933 .222792 .0000000
+27255.6 -.065800 .247600 .0000000
+27273.9 -.061257 .170077 .0000000
+27292.1 -.092890 .168864 .0000000
+27310.4 -.011311 .114462 .0000000
+27328.7 -.041560 .155440 .0000000
+27346.9 -.007268 .135250 .0000000
+27365.2 -.048100 .064100 .0000000
+27383.4 -.030166 .051458 .0000000
+27401.7 -.059454 .050490 .0000000
+27420 -.000334 -.055485 .0000000
+27438.2 -.047994 .160539 .0000000
+27456.5 -.128330 .132957 .0000000
+27474.8 -.104712 -.032479 .0000000
+27493 -.123185 .026628 .0000000
+27511.3 -.156884 .046650 .0000000
+27529.5 -.146878 .039678 .0000000
+27547.8 -.212952 .066280 .0000000
+27566.1 -.109680 .080647 .0000000
+27584.3 -.064907 .218438 .0000000
+27602.6 -.115818 .182058 .0000000
+27620.9 -.093600 .146400 .0000000
+27639.1 -.026049 .206821 .0000000
+27657.4 -.036375 .197949 .0000000
+27675.6 -.018200 .130900 .0000000
+27693.9 .003251 .176660 .0000000
+27712.2 -.019469 .192448 .0000000
+27730.4 .016400 .142200 .0000000
+27748.7 .017784 .028679 .0000000
+27767 -.064537 .102035 .0000000
+27785.2 -.095694 .072497 .0000000
+27803.5 -.053671 .063917 .0000000
+27821.7 -.169016 .020530 .0000000
+27840 .005900 .019800 .0000000
+27858.3 -.121309 .043629 .0000000
+27876.5 -.067132 .059209 .0000000
+27894.8 -.065954 .062433 .0000000
+27913 -.066468 .107258 .0000000
+27931.3 -.021635 .104029 .0000000
+27949.6 -.033804 .063816 .0000000
+27967.8 -.056658 .141422 .0000000
+27986.1 -.052090 .108980 .0000000
+28004.4 -.177790 -.002912 .0000000
+28022.6 -.039043 .085087 .0000000
+28040.9 -.133855 .062467 .0000000
+28059.1 -.005002 .093252 .0000000
+28077.4 -.055390 .041687 .0000000
+28095.7 -.109561 .035449 .0000000
+28113.9 -.173680 .091852 .0000000
+28132.2 -.136077 .056437 .0000000
+28150.5 -.015578 .047611 .0000000
+28168.7 .023251 .090679 .0000000
+28187 -.146877 .108152 .0000000
+28205.2 -.047500 .098500 .0000000
+28223.5 -.125964 .209295 .0000000
+28241.8 -.159573 .182595 .0000000
+28260 -.106700 .188100 .0000000
+28278.3 -.080483 .175745 .0000000
+28296.6 -.093917 .159713 .0000000
+28314.8 -.037416 .107419 .0000000
+28333.1 .060450 .167046 .0000000
+28351.3 -.013617 .143961 .0000000
+28369.6 -.000451 .115066 .0000000
+28387.9 -.004315 .072471 .0000000
+28406.1 -.043164 .033288 .0000000
+28424.4 -.018826 .032928 .0000000
+28442.7 -.074570 -.007560 .0000000
+28460.9 -.106024 -.004717 .0000000
+28479.2 -.100771 -.011306 .0000000
+28497.4 -.188334 .029368 .0000000
+28515.7 -.173300 -.047400 .0000000
+28534 -.140145 .085152 .0000000
+28552.2 -.193481 .094688 .0000000
+28570.5 -.158811 .074182 .0000000
+28588.7 -.245489 .179964 .0000000
+28607 -.176313 .131564 .0000000
+28625.3 -.121499 .208926 .0000000
+28643.5 -.116696 .187814 .0000000
+28661.8 -.040102 .222305 .0000000
+28680.1 -.088573 .195445 .0000000
+28698.3 .035320 .209660 .0000000
+28716.6 .073386 .231432 .0000000
+28734.8 .069100 .137800 .0000000
+28753.1 .090711 .121711 .0000000
+28771.4 .056677 .054539 .0000000
+28789.6 .047138 .041472 .0000000
+28807.9 -.015251 -.021479 .0000000
+28826.2 -.008680 -.029054 .0000000
+28844.4 -.107027 -.071074 .0000000
+28862.7 -.105060 -.016349 .0000000
+28880.9 -.217410 -.006067 .0000000
+28899.2 -.204894 .021733 .0000000
+28917.5 -.303156 .013057 .0000000
+28935.7 -.147755 .071361 .0000000
+28954 -.221442 .102093 .0000000
+28972.3 -.226757 .130364 .0000000
+28990.5 -.171954 .165869 .0000000
+29008.8 -.138432 .225318 .0000000
+29027 -.116911 .287363 .0000000
+29045.3 -.046436 .227821 .0000000
+29063.6 -.080914 .286589 .0000000
+29081.8 .111257 .295958 .0000000
+29100.1 .109700 .236000 .0000000
+29118.3 .174186 .248989 .0000000
+29136.6 .172035 .065415 .0000000
+29154.9 .075919 .117887 .0000000
+29173.1 .167309 .023607 .0000000
+29191.4 .026111 -.003055 .0000000
+29209.7 -.037869 -.061382 .0000000
+29227.9 -.052264 -.072478 .0000000
+29246.2 -.134539 .030821 .0000000
+29264.4 -.301769 .106206 .0000000
+29282.7 -.116858 -.001150 .0000000
+29301 -.213896 .044637 .0000000
+29319.2 -.161300 .101800 .0000000
+29337.5 -.181054 .143917 .0000000
+29355.8 -.192526 .184454 .0000000
+29374 -.119371 .187626 .0000000
+29392.3 -.079336 .224723 .0000000
+29410.5 -.087263 .216206 .0000000
+29428.8 -.044976 .211200 .0000000
+29447.1 .023086 .214921 .0000000
+29465.3 .072500 .233900 .0000000
+29483.6 .118411 .224190 .0000000
+29501.9 .146657 .195121 .0000000
+29520.1 .095496 .180767 .0000000
+29538.4 .089672 .076885 .0000000
+29556.6 .069378 .045780 .0000000
+29574.9 .042361 .018571 .0000000
+29593.2 .005289 .055015 .0000000
+29611.4 -.058344 .043141 .0000000
+29629.7 .027411 -.055656 .0000000
+29648 -.148565 -.007660 .0000000
+29666.2 -.249170 -.018138 .0000000
+29684.5 -.210741 .047170 .0000000
+29702.7 -.226589 .078515 .0000000
+29721 -.290100 .142200 .0000000
+29739.3 -.223492 .078291 .0000000
+29757.5 -.135189 .151824 .0000000
+29775.8 -.175712 .271518 .0000000
+29794 -.063144 .235042 .0000000
+29812.3 -.029661 .260179 .0000000
+29830.6 .019816 .250937 .0000000
+29848.8 .162656 .241704 .0000000
+29867.1 .098439 .147379 .0000000
+29885.4 .065800 .175200 .0000000
+29903.6 .125165 .119102 .0000000
+29921.9 .070275 .085726 .0000000
+29940.1 .053300 .043221 .0000000
+29958.4 .004300 -.026993 .0000000
+29976.7 -.049213 .034742 .0000000
+29994.9 .033843 .015616 .0000000
+30013.2 .033262 -.034927 .0000000
+30031.5 -.043717 .028212 .0000000
+30049.7 -.000032 .034220 .0000000
+30068 .008262 .025372 .0000000
+30086.2 -.118927 .084292 .0000000
+30104.5 -.183857 .100975 .0000000
+30122.8 -.159123 .204194 .0000000
+30141 -.088480 .133512 .0000000
+30159.3 -.092443 .191571 .0000000
+30177.6 -.062260 .120954 .0000000
+30195.8 -.073372 .253085 .0000000
+30214.1 .012678 .219220 .0000000
+30232.3 -.041165 .167041 .0000000
+30250.6 -.086224 .093311 .0000000
+30268.9 .001683 .150415 .0000000
+30287.1 .068909 .186888 .0000000
+30305.4 -.028851 .102187 .0000000
+30323.6 -.020115 .119301 .0000000
+30341.9 .145863 .081312 .0000000
+30360.2 -.029700 .145400 .0000000
+30378.4 -.086609 .107756 .0000000
+30396.7 .034025 .019905 .0000000
+30415 -.065983 .120029 .0000000
+30433.2 -.026845 .031907 .0000000
+30451.5 -.066569 .031627 .0000000
+30469.7 -.008392 .098245 .0000000
+30488 -.012154 .089180 .0000000
+30506.3 -.068264 .122550 .0000000
+30524.5 -.064544 .139302 .0000000
+30542.8 -.032732 .122930 .0000000
+30561.1 -.007371 .174390 .0000000
+30579.3 -.089026 .117492 .0000000
+30597.6 -.075180 .095255 .0000000
+30615.8 -.036158 .121535 .0000000
+30634.1 -.075817 .122674 .0000000
+30652.4 -.089165 .085346 .0000000
+30670.6 -.152931 .083401 .0000000
+30688.9 -.103187 .103300 .0000000
+30707.2 -.039915 .183470 .0000000
+30725.4 -.090405 .234551 .0000000
+30743.7 -.093652 .180949 .0000000
+30761.9 -.061971 .090993 .0000000
+30780.2 -.080573 .145031 .0000000
+30798.5 -.051329 .138129 .0000000
+30816.7 .001923 .184841 .0000000
+30835 -.019745 .090795 .0000000
+30853.2 .035917 .105605 .0000000
+30871.5 .078661 .117427 .0000000
+30889.8 .033384 .108682 .0000000
+30908 .105491 .061063 .0000000
+30926.3 -.047847 -.001805 .0000000
+30944.6 -.036338 .050187 .0000000
+30962.8 .022793 .034134 .0000000
+30981.1 .070899 .024180 .0000000
+30999.3 .004438 .011224 .0000000
+31017.6 -.122806 .001491 .0000000
+31035.9 -.144327 .028604 .0000000
+31054.1 -.120953 .074676 .0000000
+31072.4 -.253705 .095567 .0000000
+31090.7 -.188535 .058157 .0000000
+31108.9 -.213170 .082626 .0000000
+31127.2 -.133388 .103606 .0000000
+31145.4 -.243714 .045296 .0000000
+31163.7 -.280687 .123924 .0000000
+31182 -.099857 .132270 .0000000
+31200.2 -.102089 .098582 .0000000
+31218.5 .034173 .068935 .0000000
+31236.8 .052475 .180058 .0000000
+31255 .204300 .170300 .0000000
+31273.3 .174930 .065429 .0000000
+31291.5 .236527 .061957 .0000000
+31309.8 .208915 -.039275 .0000000
+31328.1 .171265 -.034213 .0000000
+31346.3 .158830 -.127515 .0000000
+31364.6 -.041184 -.081574 .0000000
+31382.9 -.054199 -.141045 .0000000
+31401.1 -.122864 -.138640 .0000000
+31419.4 -.173922 -.079180 .0000000
+31437.6 -.266129 -.029277 .0000000
+31455.9 -.117527 .046084 .0000000
+31474.2 -.172366 .076642 .0000000
+31492.4 -.218168 .119657 .0000000
+31510.7 -.269380 .084980 .0000000
+31528.9 -.105476 .185324 .0000000
+31547.2 -.252149 .252063 .0000000
+31565.5 -.119100 .283700 .0000000
+31583.7 -.098829 .296275 .0000000
+31602 .041205 .299528 .0000000
+31620.3 .070100 .328900 .0000000
+31638.5 .161286 .243704 .0000000
+31656.8 .111735 .157891 .0000000
+31675 .279930 .129955 .0000000
+31693.3 .210791 .086270 .0000000
+31711.6 .167958 -.034212 .0000000
+31729.8 .387534 -.060857 .0000000
+31748.1 .203824 -.175689 .0000000
+31766.4 -.009562 -.200723 .0000000
+31784.6 .082043 -.254725 .0000000
+31802.9 -.167775 .011828 .0000000
+31821.1 -.105894 -.143593 .0000000
+31839.4 -.238800 -.143600 .0000000
+31857.7 -.161621 -.086524 .0000000
+31875.9 -.292120 -.032154 .0000000
+31894.2 -.369539 .005399 .0000000
+31912.5 -.243748 .106804 .0000000
+31930.7 -.260700 .154700 .0000000
+31949 -.227834 .263600 .0000000
+31967.2 -.105890 .285597 .0000000
+31985.5 -.219288 .379684 .0000000
+32003.8 -.063112 .324947 .0000000
+32022 .056826 .295929 .0000000
+32040.3 .082673 .302031 .0000000
+32058.5 .134672 .242569 .0000000
+32076.8 .188918 .179805 .0000000
+32095.1 .230337 .153795 .0000000
+32113.3 .230653 .126700 .0000000
+32131.6 .324003 -.014854 .0000000
+32149.9 .083430 -.030662 .0000000
+32168.1 .071411 -.089697 .0000000
+32186.4 .033741 -.095143 .0000000
+32204.6 -.003804 -.062704 .0000000
+32222.9 -.105253 -.159077 .0000000
+32241.2 -.140851 -.129656 .0000000
+32259.4 -.217901 -.081483 .0000000
+32277.7 -.204585 -.117897 .0000000
+32296 -.260050 .044441 .0000000
+32314.2 -.358491 .174358 .0000000
+32332.5 -.202905 .152120 .0000000
+32350.7 -.254945 .153196 .0000000
+32369 -.137802 .288460 .0000000
+32387.3 -.094877 .287150 .0000000
+32405.5 .023089 .335301 .0000000
+32423.8 .060945 .357002 .0000000
+32442.1 .113078 .313539 .0000000
+32460.3 .120020 .273848 .0000000
+32478.6 .175924 .294753 .0000000
+32496.8 .206992 .222458 .0000000
+32515.1 .140099 .174866 .0000000
+32533.4 .157648 .156590 .0000000
+32551.6 .225955 .143369 .0000000
+32569.9 .157129 .071959 .0000000
+32588.1 .150055 .021583 .0000000
+32606.4 .171585 -.019271 .0000000
+32624.7 .145400 -.041700 .0000000
+32642.9 .147680 -.064178 .0000000
+32661.2 .053983 -.075038 .0000000
+32679.5 -.045837 -.072956 .0000000
+32697.7 -.091431 -.067958 .0000000
+32716 -.076060 .010010 .0000000
+32734.2 -.106057 .014185 .0000000
+32752.5 -.097516 .015430 .0000000
+32770.8 -.130026 .063326 .0000000
+32789 -.136613 .110269 .0000000
+32807.3 -.236212 .081015 .0000000
+32825.6 -.122022 .165015 .0000000
+32843.8 -.082996 .181422 .0000000
+32862.1 -.102718 .194496 .0000000
+32880.3 -.056364 .288203 .0000000
+32898.6 -.060517 .337067 .0000000
+32916.9 -.038708 .272547 .0000000
+32935.1 .050766 .309560 .0000000
+32953.4 -.036412 .244629 .0000000
+32971.7 .092035 .269182 .0000000
+32989.9 .117000 .151400 .0000000
+33008.2 .167243 .119693 .0000000
+33026.4 .167879 .072692 .0000000
+33044.7 .134722 .072089 .0000000
+33063 .145230 .001256 .0000000
+33081.2 .089326 -.093079 .0000000
+33099.5 .154408 -.078539 .0000000
+33117.8 .073064 -.060780 .0000000
+33136 .043289 -.033614 .0000000
+33154.3 -.013262 -.122149 .0000000
+33172.5 -.164254 -.145566 .0000000
+33190.8 -.163827 -.040926 .0000000
+33209.1 -.236686 -.074967 .0000000
+33227.3 -.237557 .040887 .0000000
+33245.6 -.224455 .063927 .0000000
+33263.8 -.290753 .157252 .0000000
+33282.1 -.220930 .287191 .0000000
+33300.4 -.222249 .248856 .0000000
+33318.6 -.144425 .335072 .0000000
+33336.9 -.061597 .365805 .0000000
+33355.2 .023935 .338761 .0000000
+33373.4 .046105 .332438 .0000000
+33391.7 .127597 .297890 .0000000
+33409.9 .258100 .317900 .0000000
+33428.2 .238376 .209297 .0000000
+33446.5 .300481 .140257 .0000000
+33464.7 .287564 .015981 .0000000
+33483 .286892 .093660 .0000000
+33501.3 .270451 -.079337 .0000000
+33519.5 .258830 -.098373 .0000000
+33537.8 .116078 -.227489 .0000000
+33556 -.010608 -.232154 .0000000
+33574.3 -.087101 -.278552 .0000000
+33592.6 -.183732 -.306109 .0000000
+33610.8 -.306855 -.243256 .0000000
+33629.1 -.321363 -.117027 .0000000
+33647.4 -.365022 .014938 .0000000
+33665.6 -.304225 .001015 .0000000
+33683.9 -.369566 .099280 .0000000
+33702.1 -.378609 .229045 .0000000
+33720.4 -.253183 .324399 .0000000
+33738.7 -.183608 .395804 .0000000
+33756.9 -.113043 .407480 .0000000
+33775.2 .025400 .357400 .0000000
+33793.4 .169217 .366203 .0000000
+33811.7 .251730 .336817 .0000000
+33830 .268771 .266485 .0000000
+33848.2 .325834 .218909 .0000000
+33866.5 .416116 .097318 .0000000
+33884.8 .369300 .007200 .0000000
+33903 .311161 -.128485 .0000000
+33921.3 .291955 -.216643 .0000000
+33939.5 .143989 -.282893 .0000000
+33957.8 .078168 -.298582 .0000000
+33976.1 -.072498 -.293768 .0000000
+33994.3 -.121266 -.283286 .0000000
+34012.6 -.228941 -.289334 .0000000
+34030.9 -.384844 -.280973 .0000000
+34049.1 -.425432 -.085033 .0000000
+34067.4 -.415187 .007147 .0000000
+34085.6 -.438050 .104950 .0000000
+34103.9 -.373937 .199595 .0000000
+34122.2 -.214791 .330880 .0000000
+34140.4 -.237011 .436678 .0000000
+34158.7 -.206498 .424479 .0000000
+34177 -.114247 .462331 .0000000
+34195.2 .053909 .452330 .0000000
+34213.5 .152162 .415113 .0000000
+34231.7 .302914 .389455 .0000000
+34250 .303583 .272217 .0000000
+34268.3 .379665 .235664 .0000000
+34286.5 .284255 .115197 .0000000
+34304.8 .290673 .004058 .0000000
+34323.1 .273678 -.093103 .0000000
+34341.3 .201204 -.160920 .0000000
+34359.6 .092473 -.181244 .0000000
+34377.8 .123811 -.231433 .0000000
+34396.1 -.059768 -.287803 .0000000
+34414.4 -.099969 -.181194 .0000000
+34432.6 -.117820 -.081862 .0000000
+34450.9 -.288724 -.112507 .0000000
+34469.1 -.256646 -.080086 .0000000
+34487.4 -.275672 .047975 .0000000
+34505.7 -.343300 .172800 .0000000
+34523.9 -.325734 .244282 .0000000
+34542.2 -.275838 .294124 .0000000
+34560.5 -.079095 .448538 .0000000
+34578.7 -.029260 .528535 .0000000
+34597 .021387 .421200 .0000000
+34615.2 .022300 .491500 .0000000
+34633.5 .219443 .448367 .0000000
+34651.8 .204160 .288329 .0000000
+34670 .197400 .214300 .0000000
+34688.3 .224082 .155717 .0000000
+34706.6 .236336 .022065 .0000000
+34724.8 .184200 .032600 .0000000
+34743.1 .136603 -.090735 .0000000
+34761.3 .015320 -.126354 .0000000
+34779.6 .036575 -.126953 .0000000
+34797.9 -.024887 -.150786 .0000000
+34816.1 -.154021 -.147087 .0000000
+34834.4 -.164556 -.108061 .0000000
+34852.7 -.195623 -.112539 .0000000
+34870.9 -.239513 -.079926 .0000000
+34889.2 -.212137 .053878 .0000000
+34907.4 -.133960 .116843 .0000000
+34925.7 -.215416 .144317 .0000000
+34944 -.127059 .218349 .0000000
+34962.2 -.161882 .209542 .0000000
+34980.5 .022753 .272072 .0000000
+34998.7 -.093222 .296413 .0000000
+35017 -.075965 .339411 .0000000
+35035.3 .010000 .310600 .0000000
+35053.5 -.019012 .279865 .0000000
+35071.8 -.029023 .206222 .0000000
+35090.1 .099499 .204533 .0000000
+35108.3 .017291 .175152 .0000000
+35126.6 .124344 .144779 .0000000
+35144.8 .029988 .049576 .0000000
+35163.1 .124568 .140522 .0000000
+35181.4 .158988 .106674 .0000000
+35199.6 .105321 .083753 .0000000
+35217.9 .046473 -.027556 .0000000
+35236.2 .065044 -.038566 .0000000
+35254.4 .041687 -.021509 .0000000
+35272.7 -.001269 -.080686 .0000000
+35290.9 -.055935 -.006263 .0000000
+35309.2 .003124 .039639 .0000000
+35327.5 -.109785 .034162 .0000000
+35345.7 -.137337 .039769 .0000000
+35364 -.226992 .043228 .0000000
+35382.3 -.229553 .066146 .0000000
+35400.5 -.219600 .137300 .0000000
+35418.8 -.189281 .159204 .0000000
+35437 -.219306 .167037 .0000000
+35455.3 -.203131 .233918 .0000000
+35473.6 -.070247 .341707 .0000000
+35491.8 -.007398 .192673 .0000000
+35510.1 .016713 .339190 .0000000
+35528.3 .038798 .279099 .0000000
+35546.6 .117409 .334384 .0000000
+35564.9 .083759 .248766 .0000000
+35583.1 .152189 .209354 .0000000
+35601.4 .160798 .161261 .0000000
+35619.7 .211912 .132122 .0000000
+35637.9 .219468 .113755 .0000000
+35656.2 .212688 .021181 .0000000
+35674.4 .169132 -.055280 .0000000
+35692.7 .146149 .000797 .0000000
+35711 .059496 -.049301 .0000000
+35729.2 .018377 -.044723 .0000000
+35747.5 -.050507 -.037579 .0000000
+35765.8 -.204692 -.059809 .0000000
+35784 -.228535 .004844 .0000000
+35802.3 -.318792 .068497 .0000000
+35820.5 -.290530 .108736 .0000000
+35838.8 -.401787 .175879 .0000000
+35857.1 -.340710 .217146 .0000000
+35875.3 -.222754 .314496 .0000000
+35893.6 -.224552 .381610 .0000000
+35911.9 -.194858 .441895 .0000000
+35930.1 -.099300 .411100 .0000000
+35948.4 .007920 .425095 .0000000
+35966.6 .076323 .453030 .0000000
+35984.9 .152477 .481943 .0000000
+36003.2 .235240 .383132 .0000000
+36021.4 .240076 .308449 .0000000
+36039.7 .292684 .272059 .0000000
+36058 .345486 .208463 .0000000
+36076.2 .271110 .018347 .0000000
+36094.5 .229000 -.058924 .0000000
+36112.7 .126351 -.051070 .0000000
+36131 .039747 -.126282 .0000000
+36149.3 -.003182 -.164541 .0000000
+36167.5 -.069900 -.130259 .0000000
+36185.8 -.209601 -.084401 .0000000
+36204 -.305232 -.027916 .0000000
+36222.3 -.343994 .036549 .0000000
+36240.6 -.333001 .173344 .0000000
+36258.8 -.335927 .215384 .0000000
+36277.1 -.305676 .288831 .0000000
+36295.4 -.300800 .338700 .0000000
+36313.6 -.179030 .409116 .0000000
+36331.9 -.101308 .407552 .0000000
+36350.1 -.041921 .434062 .0000000
+36368.4 .051783 .422110 .0000000
+36386.7 .098517 .451402 .0000000
+36404.9 .180900 .370800 .0000000
+36423.2 .203233 .334686 .0000000
+36441.5 .303286 .248765 .0000000
+36459.7 .276253 .146280 .0000000
+36478 .267226 .075078 .0000000
+36496.2 .266128 .119921 .0000000
+36514.5 .213073 -.055607 .0000000
+36532.8 .164238 -.060513 .0000000
+36551 .101815 -.044255 .0000000
+36569.3 .016105 -.129490 .0000000
+36587.6 -.095165 -.054368 .0000000
+36605.8 -.153168 -.068371 .0000000
+36624.1 -.195475 -.058794 .0000000
+36642.3 -.253927 .017351 .0000000
+36660.6 -.238076 .061599 .0000000
+36678.9 -.240657 .198641 .0000000
+36697.1 -.252739 .262649 .0000000
+36715.4 -.214144 .308577 .0000000
+36733.6 -.142987 .396049 .0000000
+36751.9 -.037927 .401546 .0000000
+36770.2 -.034800 .382000 .0000000
+36788.4 .106849 .415365 .0000000
+36806.7 .129032 .368627 .0000000
+36825 .175804 .336333 .0000000
+36843.2 .222541 .303817 .0000000
+36861.5 .203199 .215274 .0000000
+36879.7 .202366 .197922 .0000000
+36898 .175341 .085444 .0000000
+36916.3 .132030 .058528 .0000000
+36934.5 .008173 .006252 .0000000
+36952.8 -.003037 -.014016 .0000000
+36971.1 -.096783 -.064746 .0000000
+36989.3 -.049029 -.015531 .0000000
+37007.6 -.070352 .005059 .0000000
+37025.8 -.093472 -.033430 .0000000
+37044.1 -.144743 .036124 .0000000
+37062.4 -.136289 .091137 .0000000
+37080.6 -.185600 .139573 .0000000
+37098.9 -.144215 .156992 .0000000
+37117.2 -.093892 .234106 .0000000
+37135.4 -.141900 .251100 .0000000
+37153.7 -.082108 .224170 .0000000
+37171.9 -.054888 .246897 .0000000
+37190.2 .000500 .272000 .0000000
+37208.5 .027616 .261292 .0000000
+37226.7 .028359 .217569 .0000000
+37245 -.026324 .268219 .0000000
+37263.2 .045255 .178998 .0000000
+37281.5 .018041 .148022 .0000000
+37299.8 -.010002 .178091 .0000000
+37318 .014227 .129318 .0000000
+37336.3 -.034519 .135146 .0000000
+37354.6 .020541 .071804 .0000000
+37372.8 -.008860 .093978 .0000000
+37391.1 -.009493 .077921 .0000000
+37409.3 -.013247 .104033 .0000000
+37427.6 -.011871 .106863 .0000000
+37445.9 -.055595 .050930 .0000000
+37464.1 -.006478 .093211 .0000000
+37482.4 -.052932 .075634 .0000000
+37500.7 -.008206 .085606 .0000000
+37518.9 -.051487 .123137 .0000000
+37537.2 -.017757 .095707 .0000000
+37555.4 -.062010 .097662 .0000000
+37573.7 -.081090 .089862 .0000000
+37592 -.110423 .077764 .0000000
+37610.2 -.090300 .114700 .0000000
+37628.5 -.167482 .169747 .0000000
+37646.8 -.142626 .162677 .0000000
+37665 -.033900 .203700 .0376300
+37683.3 -.050170 .211840 .0338900
+37701.5 -.002320 .229940 .0290470
+37719.8 .006920 .252420 .0240100
+37738.1 .024920 .267420 .0164880
+37756.3 .036710 .249550 .0102410
+37774.6 .072420 .225380 -.0005960
+37792.9 .094690 .208290 -.0104660
+37811.1 .093250 .193210 -.0187080
+37829.4 .107960 .129190 -.0156280
+37847.6 .095310 .127660 -.0096470
+37865.9 .086920 .113560 .0013710
+37884.2 .069240 .063740 .0066930
+37902.4 .055400 .049150 .0122100
+37920.7 .008500 .041810 .0142490
+37938.9 -.014150 .060290 .0105620
+37957.2 -.066110 .050990 .0038340
+37975.5 -.113040 .105830 -.0066870
+37993.7 -.153970 .139440 -.0175860
+38012 -.160030 .176030 -.0286510
+38030.3 -.205590 .212120 -.0332190
+38048.5 -.201720 .264370 -.0339840
+38066.8 -.173000 .287920 -.0309280
+38085 -.148670 .335970 -.0308550
+38103.3 -.099870 .335960 -.0392730
+38121.6 -.044310 .360350 -.0507330
+38139.8 .021450 .354760 -.0615850
+38158.1 .093210 .324570 -.0762480
+38176.4 .152910 .306570 -.0852720
+38194.6 .175600 .263720 -.0898040
+38212.9 .230800 .226120 -.0911400
+38231.1 .218840 .193180 -.0850800
+38249.4 .207420 .115940 -.0800220
+38267.7 .148670 .085350 -.0829150
+38285.9 .096090 .028620 -.0876470
+38304.2 .053710 -.010670 -.0974610
+38322.5 -.017030 -.023980 -.1141360
+38340.7 -.106000 -.008970 -.0370110
+38359 -.162800 .001510 -.0481380
+38377.2 -.172400 .042730 -.0669830
+38395.5 -.225800 .073470 -.0808460
+38413.8 -.277190 .142370 -.0959270
+38432 -.316290 .213450 -.1133170
+38450.3 -.278600 .281690 -.1248710
+38468.5 -.246040 .323490 -.1403400
+38486.8 -.179450 .390780 -.0597570
+38505.1 -.106780 .417820 -.0730700
+38523.3 -.051130 .465850 -.0918930
+38541.6 -.000180 .454570 -.1099970
+38559.9 .070820 .457390 -.1212000
+38578.1 .122250 .410720 -.1147420
+38596.4 .188300 .371690 -.1164510
+38614.6 .233650 .317960 -.1142910
+38632.9 .243950 .251720 -.1118720
+38651.2 .228520 .159640 -.0165730
+38669.4 .202930 .093010 -.0291840
+38687.7 .163200 .037870 -.0476800
+38706 .107390 .018400 -.0671060
+38724.2 .042310 -.014260 -.0837400
+38742.5 -.022710 -.019620 -.1003350
+38760.7 -.089890 -.005080 -.0182700
+38779 -.109340 -.011770 -.0248210
+38797.3 -.187520 .079690 -.0413790
+38815.5 -.220180 .108850 -.0595530
+38833.8 -.239960 .170890 .0201840
+38852.1 -.239880 .232370 -.0057010
+38870.3 -.221390 .286720 -.0330600
+38888.6 -.177010 .329310 -.0487050
+38906.8 -.151550 .380270 -.0661190
+38925.1 -.115410 .406270 -.0832370
+38943.4 -.033960 .439390 .0111050
+38961.6 .047390 .425360 .0060030
+38979.9 .084440 .411480 -.0046230
+38998.2 .105520 .371940 -.0087120
+39016.4 .137420 .304360 .0758570
+39034.7 .168630 .248710 .0544290
+39052.9 .200590 .176560 .0353090
+39071.2 .178810 .143930 .0119610
+39089.5 .158060 .087030 -.0139660
+39107.7 .115300 .054420 -.0327310
+39126 .047900 .005530 -.0467430
+39144.2 .003210 .018400 -.0442320
+39162.5 -.020370 .017330 -.0397280
+39180.8 -.092720 .042830 -.0398940
+39199 -.126630 .060540 -.0393290
+39217.3 -.126250 .087680 -.0396900
+39235.6 -.167720 .130720 -.0463990
+39253.8 -.161730 .172470 -.0516050
+39272.1 -.151510 .196200 -.0495510
+39290.3 -.128550 .234880 -.0432800
+39308.6 -.124150 .306450 -.0321410
+39326.9 -.081600 .310620 -.0125560
+39345.1 -.055550 .328730 .0003880
+39363.4 -.017090 .338450 .0095020
+39381.7 .006590 .333040 .0161450
+39399.9 .042120 .311320 .0146070
+39418.2 .091960 .283830 .0121820
+39436.4 .094580 .252760 .0098330
+39454.7 .116210 .220110 .0051870
+39473 .135490 .172910 .0058710
+39491.2 .097100 .162300 .0124750
+39509.5 .070010 .139910 .0184380
+39527.8 .048010 .130910 .0211400
+39546 .032320 .139020 .0229620
+39564.3 .014320 .136220 .0194950
+39582.5 .005330 .130830 .0167670
+39600.8 -.008570 .144330 .0133100
+39619.1 -.013670 .164530 .0065720
+39637.3 -.008560 .148640 .0071750
+39655.6 .003850 .168250 .0176880
+39673.8 -.011850 .176050 .0301300
+39692.1 -.008350 .193960 .0477730
+39710.4 -.011740 .189260 .0663160
+39728.6 -.001430 .186370 .0768980
+39746.9 -.029530 .191370 .0890110
+39765.2 -.033830 .184480 .0963740
+39783.4 -.030620 .195580 .0971770
+39801.7 -.035120 .205980 .0953500
+39819.9 -.026910 .212290 .0952730
+39838.2 -.026200 .222600 .0931660
+39856.5 -.034000 .239700 .0951090
+39874.7 -.005100 .237510 .0987210
+39893 -.004790 .238810 -.0023360
+39911.3 -.001680 .237720 -.0036230
+39929.5 .030920 .241420 -.0012590
+39947.8 .015230 .230330 -.0024360
+39966 .046430 .242330 -.0107430
+39984.3 .060630 .223740 -.0118800
+40002.6 .057340 .203640 -.0136970
+40020.8 .059250 .198750 -.0100140
+40039.1 .068150 .173950 .0057390
+40057.4 .067460 .166360 .0170720
+40075.6 .052060 .142860 .0302750
+40093.9 .027070 .135670 .0354390
+40112.1 .005570 .135770 .0390020
+40130.4 -.013830 .118780 .0406750
+40148.7 -.042020 .106380 .0419680
+40166.9 -.084920 .124390 .0356310
+40185.2 -.139410 .147490 .0341150
+40203.4 -.126810 .185200 .0350180
+40221.7 -.139900 .214900 .0335710
+40240 -.124100 .251100 .0372640
+40258.2 -.106290 .262510 .0391280
+40276.5 -.095880 .326120 .0309210
+40294.8 -.079080 .337320 .0246540
+40313 -.036380 .347630 .0124870
+40331.3 -.000970 .351930 .0000710
+40349.5 .055030 .361340 -.0043960
+40367.8 .090640 .316240 -.0064830
+40386.1 .125150 .295450 -.0097100
+40404.3 .133450 .267750 .0040040
+40422.6 .150150 .228760 .0133570
+40440.9 .134960 .187660 .0232700
+40459.1 .117370 .136570 .0362230
+40477.4 .095770 .114170 .0343170
+40495.6 .058680 .100680 .0285300
+40513.9 .011380 .065480 .0246530
+40532.2 -.011720 .063480 .0178860
+40550.4 -.062210 .070390 .0119090
+40568.7 -.122410 .082300 .0067420
+40587 -.163200 .117800 .0047260
+40605.2 -.200400 .175210 -.0043710
+40623.5 -.217990 .213110 -.0072880
+40641.7 -.167580 .287720 -.0178350
+40660 -.155880 .325820 -.0272820
+40678.3 -.140180 .357830 -.0373190
+40696.5 -.092070 .388230 -.0515660
+40714.8 -.036570 .394540 -.0614930
+40733.1 .009940 .407740 -.0624500
+40751.3 .077750 .394750 -.0622170
+40769.6 .130150 .370950 -.0542940
+40787.8 .166660 .330060 -.0435010
+40806.1 .200960 .262860 -.0298880
+40824.4 .205070 .222970 -.0204550
+40842.6 .196270 .160070 -.0125620
+40860.9 .178680 .101880 -.0144890
+40879.1 .141580 .043980 -.0185570
+40897.4 .064680 .026980 -.0256240
+40915.7 .013790 .006190 -.0358210
+40933.9 -.045910 -.008500 -.0395080
+40952.2 -.094800 .014300 -.0386560
+40970.5 -.145000 .063100 -.0423130
+40988.7 -.193590 .108210 -.0417900
+41007 -.226190 .164220 -.0385280
+41025.2 -.248080 .221720 -.0447450
+41043.5 -.221480 .298620 -.0573030
+41061.8 -.200070 .353230 -.0652400
+41080 -.187870 .384540 -.0779880
+41098.3 -.131860 .428740 -.0875360
+41116.6 -.046460 .458050 -.0888030
+41134.8 .029850 .466050 -.0875110
+41153.1 .104850 .444960 -.0828490
+41171.3 .162960 .400160 -.0790560
+41189.6 .211970 .361270 -.0778240
+41207.9 .233970 .304470 -.0821020
+41226.1 .225880 .218780 -.0877900
+41244.4 .208880 .167780 -.1047780
+41262.7 .181790 .101490 -.1220760
+41280.9 .140590 .071290 -.1384440
+41299.2 .084400 .039100 -.1490720
+41317.4 .031820 .019710 -.0474900
+41335.7 -.029350 .019540 -.1025340
+41354 -.085000 .023390 -.1641870
+41372.2 -.113320 .038780 -.2239540
+41390.5 -.170800 .100130 -.2844970
+41408.7 -.178190 .157660 -.3489940
+41427 -.193440 .216340 -.4137270
+41445.3 -.177900 .273960 -.4751870
+41463.5 -.148990 .302730 -.5400640
+41481.8 -.101340 .350600 -.5937930
+41500.1 -.048730 .379110 .3591600
+41518.3 .000850 .403050 .3124850
+41536.6 .057740 .404590 .2662670
+41554.8 .089870 .408890 .2208620
+41573.1 .119360 .367470 .1685090
+41591.4 .125150 .330830 .1107640
+41609.6 .161790 .292310 .0507720
+41627.9 .164080 .229730 -.0089650
+41646.2 .154490 .213280 -.0670860
+41664.4 .142830 .172430 -.1269170
+41682.7 .123610 .125710 .8114390
+41700.9 .091980 .093390 .7545430
+41719.2 .055100 .086390 .6973530
+41737.5 .024830 .089030 .6317400
+41755.7 -.023270 .093100 .5731350
+41774 -.050430 .105840 .5070570
+41792.3 -.090950 .145940 .4422160
+41810.5 -.107840 .174450 .3817300
+41828.8 -.116560 .201930 .3252920
+41847 -.109350 .245230 .2701480
+41865.3 -.097490 .273170 .2259990
+41883.6 -.074020 .300780 .1814520
+41901.8 -.053250 .315260 .1350890
+41920.1 -.014030 .319880 .0897480
+41938.4 .014500 .323150 .0381470
+41956.6 .034570 .333410 -.0166510
+41974.9 .069270 .331530 -.0721010
+41993.1 .082930 .288210 -.1313010
+42011.4 .094550 .270630 -.1950090
+42029.7 .105000 .265120 -.2487100
+42047.9 .113200 .225230 .7002310
+42066.2 .091980 .203370 .6520660
+42084.4 .077870 .176520 .6063570
+42102.7 .078890 .171530 .5537260
+42121 .050750 .157750 .4983770
+42139.2 .029950 .179730 .4438800
+42157.5 .029220 .187280 .3871170
+42175.8 .015470 .182320 .3220300
+42194 .005080 .179910 .2706650
+42212.3 -.016260 .187160 .2230120
+42230.5 .019490 .195670 .1828640
+42248.8 .022340 .210720 .1474170
+42267.1 .019950 .212310 .1109470
+42285.3 .016610 .209630 .0663730
+42303.6 .010980 .202280 .0252920
+42321.9 .003730 .216240 -.0251410
+42340.1 -.012130 .232720 -.0802570
+42358.4 -.024480 .240310 -.1325930
+42376.6 -.033410 .254200 -.1905590
+42394.9 -.043730 .267890 -.2441870
+42413.2 -.050120 .270650 .7083150
+42431.4 -.053700 .285590 .6585310
+42449.7 -.030920 .306400 .6055510
+42468 -.019500 .329540 .5525990
+42486.2 .010660 .335300 .5013940
+42504.5 .043890 .337490 .4422900
+42522.7 .070800 .313520 .3887900
+42541 .089170 .296380 .3321200
+42559.3 .103620 .270190 .2824180
+42577.5 .125630 .246550 .2386220
+42595.8 .146030 .234320 .1972230
+42614 .132900 .199120 .1637560
+42632.3 .126320 .176400 .1307220
+42650.6 .119740 .152660 .0878550
+42668.8 .092300 .118640 .0444550
+42687.1 .062280 .103450 -.0020730
+42705.4 .020950 .098770 -.0586960
+42723.6 -.041700 .103980 -.1161850
+42741.9 -.072420 .130280 -.1711520
+42760.1 -.127410 .157660 -.2229470
+42778.4 -.144340 .193270 .7251490
+42796.7 -.151500 .247180 .6775030
+42814.9 -.156180 .289100 .6214200
+42833.2 -.145290 .329230 .5693700
+42851.5 -.117400 .367510 .5147140
+42869.7 -.095400 .393190 .4538780
+42888 -.041730 .425870 .3908040
+42906.2 -.008800 .433170 .3343790
+42924.5 .046660 .423870 .2739120
+42942.8 .111730 .401030 .2245910
+42961 .156480 .370970 .1856130
+42979.3 .195270 .330200 .1405230
+42997.6 .229060 .289950 .0972370
+43015.8 .235790 .241180 .0569540
+43034.1 .220780 .189190 .0056210
+43052.3 .172600 .157950 -.0542780
+43070.6 .159550 .117960 -.1129530
+43088.9 .113500 .093390 -.1756720
+43107.1 .053590 .071240 -.2292200
+43125.4 -.004070 .065610 -.2824520
+43143.6 -.068560 .069220 .6657090
+43161.9 -.139290 .118540 .6143360
+43180.2 -.188740 .156640 .5651410
+43198.4 -.226790 .231150 .5121100
+43216.7 -.228460 .290850 .4600880
+43235 -.216780 .362450 .4021850
+43253.2 -.174880 .414280 .3418380
+43271.5 -.133880 .458560 .2827490
+43289.7 -.071690 .489080 .2309290
+43308 .012030 .504510 .1838870
+43326.3 .082770 .495390 .1468180
+43344.5 .149380 .454360 .1142770
+43362.8 .210730 .410780 .0768320
+43381.1 .249250 .351780 .0408590
+43399.3 .269380 .292600 -.0085350
+43417.6 .263020 .222970 -.0636710
+43435.8 .243710 .151260 -.1196200
+43454.1 .195430 .095930 -.1771710
+43472.4 .133680 .061560 -.2363140
+43490.6 .063460 .030340 -.2912280
+43508.9 -.000350 .027050 .6495220
+43527.2 -.067600 .036990 .5903240
+43545.4 -.136660 .072860 .5357610
+43563.7 -.182100 .105900 .4701360
+43581.9 -.214500 .163140 .4061470
+43600.2 -.236230 .234600 .3442840
+43618.5 -.229900 .312160 .2835510
+43636.7 -.200940 .379730 .2243820
+43655 -.157910 .433860 .1677820
+43673.3 -.110240 .468530 .1209930
+43691.5 -.044260 .492280 .0799780
+43709.8 .033760 .496900 .0461430
+43728 .098530 .478150 .0128410
+43746.3 .150640 .444140 -.0312900
+43764.6 .194320 .408640 -.0754340
+43782.8 .221790 .353150 -.1252120
+43801.1 .234780 .290520 -.1826230
+43819.3 .228930 .229200 -.2362770
+43837.6 .196110 .176960 -.2927440
+43855.9 .169350 .117020 -.3501350
+43874.1 .140600 .085500 .5990550
+43892.4 .098400 .059560 .5436940
+43910.7 .023200 .064460 .4873690
+43928.9 -.017580 .069020 .4372970
+43947.2 -.067290 .087350 .3838980
+43965.4 -.111680 .125490 .3253590
+43983.7 -.137550 .174350 .2698590
+44002 -.152140 .218350 .2124640
+44020.2 -.158240 .263800 .1596610
+44038.5 -.145410 .301690 .1196450
+44056.8 -.120340 .348910 .0786440
+44075 -.093020 .378590 .0405690
+44093.3 -.032280 .404160 .0082610
+44111.5 .013040 .420310 -.0308800
+44129.8 .053770 .423000 -.0723390
+44148.1 .087730 .410920 -.1151000
+44166.3 .103920 .386840 -.1669670
+44184.6 .133930 .357770 -.2160390
+44202.9 .132630 .322640 -.2606420
+44221.1 .135250 .285040 -.3086520
+44239.4 .142150 .250260 .6449390
+44257.6 .121150 .224530 .5994770
+44275.9 .089390 .199240 .5548120
+44294.2 .064250 .182700 .5085250
+44312.4 .043590 .179240 .4666600
+44330.7 .012160 .187460 .4165130
+44348.9 -.020440 .201760 .3666940
+44367.2 -.043480 .221870 .3219240
+44385.5 -.052250 .240890 .2763230
+44403.7 -.054760 .259160 .2387700
+44422 -.051620 .282750 .2063630
+44440.3 -.039350 .301970 .1755970
+44458.5 -.035550 .314750 .1452760
+44476.8 -.030090 .328120 .1151990
+44495 -.030200 .337170 .0750790
+44513.3 -.023160 .345640 .0300980
+44531.6 -.012370 .358870 -.0127940
+44549.8 .000830 .366600 -.0624580
+44568.1 .022640 .373160 -.1078580
+44586.4 .049120 .373030 -.1500100
+44604.6 .070580 .360660 -.1953270
+44622.9 .077000 .348400 -.2391430
+44641.1 .089260 .329090 -.2776990
+44659.4 .084840 .315780 -.3202690
+44677.7 .090210 .304230 -.3727280
+44695.9 .101630 .289720 -.4159950
+44714.2 .096310 .268760 -.4685540
+44732.5 .093750 .257220 -.5165520
+44750.7 .090250 .241670 -.5587390
+44769 .078570 .226280 -.6009320
+44787.2 .068150 .210210 .3698510
+44805.5 .059540 .193490 .3498320
+44823.8 .033080 .184820 .3198500
+44842 .004150 .185370 .2918900
+44860.3 -.024140 .193490 .2653000
+44878.5 -.060920 .209640 .2228820
+44896.8 -.084890 .235550 .1804970
+44915.1 -.104520 .264190 .1393580
+44933.3 -.110340 .298730 .0957520
+44951.6 -.112180 .336860 .0571520
+44969.9 -.093080 .376820 .0184280
+44988.1 -.056710 .406900 -.0233770
+45006.4 -.029250 .429470 -.0576400
+45024.6 .016930 .440390 -.0968960
+45042.9 .062800 .438790 -.1429420
+45061.2 .108360 .429570 -.1860770
+45079.4 .144250 .410160 -.2324500
+45097.7 .175760 .379360 -.2826500
+45116 .202440 .335180 -.3212050
+45134.2 .227390 .284220 -.3589540
+45152.5 .225400 .235390 .6073090
+45170.7 .211690 .183440 .5824430
+45189 .188000 .139540 .5595280
+45207.3 .147580 .100920 .5266190
+45225.5 .090980 .077680 .4890550
+45243.8 .028040 .068920 .4484150
+45262.1 -.036070 .079650 .4039600
+45280.3 -.092580 .099410 .3645380
+45298.6 -.143280 .139280 .3204710
+45316.8 -.184870 .194480 .2722880
+45335.1 -.202510 .257960 .2276730
+45353.4 -.208590 .319420 .1807080
+45371.6 -.188080 .386630 .1215960
+45389.9 -.166980 .446850 .0712980
+45408.2 -.130670 .499770 .0173990
+45426.4 -.060990 .541150 -.0393690
+45444.7 .018010 .561080 -.0893990
+45462.9 .089520 .551750 -.1353850
+45481.2 .165160 .528540 -.1815520
+45499.5 .220450 .481900 -.2193950
+45517.7 .270130 .421220 .7475800
+45536 .307330 .346400 .7215090
+45554.2 .323430 .271320 .6952120
+45572.5 .311990 .193620 .6642850
+45590.8 .270820 .123360 .6298510
+45609 .212870 .065270 .5983220
+45627.3 .144260 .027540 .5583590
+45645.6 .061990 .014950 .5132280
+45663.8 -.016890 .026220 .4716860
+45682.1 -.085210 .050470 .4337780
+45700.3 -.133280 .091680 .3954610
+45718.6 -.178600 .145720 .3663980
+45736.9 -.217680 .203540 .3393920
+45755.1 -.239790 .267220 .3071050
+45773.4 -.240330 .342950 .2769000
+45791.7 -.215180 .414620 .2386810
+45809.9 -.165880 .478570 .2012160
+45828.2 -.103940 .529520 .1686240
+45846.4 -.036610 .557690 .1381370
+45864.7 .043530 .559030 .1142480
+45883 .118910 .544370 .0977110
+45901.2 .192030 .505220 .0854250
+45919.5 .256550 .452690 .0683640
+45937.8 .294920 .386890 .0497870
+45956 .308190 .317250 .0228220
+45974.3 .311850 .246370 -.0075050
+45992.5 .284570 .180700 -.0363910
+46010.8 .247250 .123390 -.0715010
+46029.1 .198900 .081750 -.1028830
+46047.3 .127310 .041390 -.1301440
+46065.6 .045080 .022560 -.1575460
+46083.8 -.021710 .036290 -.1889130
+46102.1 -.079030 .063220 -.2116370
+46120.4 -.133690 .102890 -.2407380
+46138.6 -.185320 .156740 -.2736890
+46156.9 -.201050 .230230 -.3070830
+46175.2 -.195870 .298580 -.3429910
+46193.4 -.182010 .364520 -.3771130
+46211.7 -.138460 .418650 -.4025590
+46229.9 -.095300 .459340 -.4317500
+46248.2 -.043950 .483270 .5478600
+46266.5 .017740 .494700 .5395290
+46284.7 .076740 .494160 .5251140
+46303 .135950 .477470 .5115900
+46321.3 .182620 .445550 .4948280
+46339.5 .211380 .402010 .4660470
+46357.8 .227170 .347600 .4338690
+46376 .228420 .297330 .4018540
+46394.3 .228310 .251090 .3668480
+46412.6 .209470 .210230 .3364070
+46430.8 .185940 .170580 .3132290
+46449.1 .148620 .136920 .2869110
+46467.4 .097820 .120120 .2595480
+46485.6 .050650 .114890 .2343500
+46503.9 -.004480 .120220 .2069080
+46522.1 -.045110 .137800 .1831500
+46540.4 -.074620 .174240 .1553600
+46558.7 -.091050 .210930 .1220280
+46576.9 -.103510 .248370 .0984900
+46595.2 -.097870 .286570 .0863460
+46613.5 -.071490 .325960 .0698390
+46631.7 -.044780 .356680 .0591900
+46650 -.017440 .376640 .0494220
+46668.2 .010780 .391720 .0327720
+46686.5 .039560 .398150 .0179060
+46704.8 .061300 .393670 -.0068750
+46723 .085600 .389850 -.0389540
+46741.3 .108410 .379180 -.0658570
+46759.5 .121680 .358470 -.0899120
+46777.8 .139690 .333040 -.1163700
+46796.1 .146260 .313070 -.1380760
+46814.3 .139250 .286810 -.1601160
+46832.6 .129080 .265690 -.1865420
+46850.9 .122130 .247910 -.2129500
+46869.1 .110320 .223500 -.2463650
+46887.4 .094220 .212870 -.2807030
+46905.6 .072280 .204670 -.3086560
+46923.9 .053320 .200540 -.3362570
+46942.2 .030170 .199490 -.3639160
+46960.4 .015310 .199420 -.3832670
+46978.7 -.006420 .206770 -.3985660
+46997 -.025810 .218770 -.4124200
+47015.2 -.031970 .234330 -.4193230
+47033.5 -.037620 .254830 -.4346680
+47051.7 -.043540 .272020 -.4584800
+47070 -.055190 .296620 -.4825460
+47088.3 -.063220 .318760 -.5129600
+47106.5 -.060180 .341610 -.5455880
+47124.8 -.056330 .371220 -.5748670
+47143.1 -.049860 .395210 -.6043940
+47161.3 -.023960 .412730 .3641980
+47179.6 .004500 .424730 .3384500
+47197.8 .047020 .432110 .3144130
+47216.1 .067110 .430020 .2785320
+47234.4 .105530 .427540 .2471970
+47252.6 .133210 .406300 .2166860
+47270.9 .150780 .378320 .1792750
+47289.1 .171170 .354020 .1522840
+47307.4 .176460 .321580 .1225530
+47325.7 .165470 .287990 .0988100
+47343.9 .169520 .251610 .0898640
+47362.2 .162560 .216260 .0807490
+47380.5 .140250 .179670 .0689520
+47398.7 .106640 .154640 .0603010
+47417 .066470 .136560 .0448510
+47435.2 .009150 .129100 .0228930
+47453.5 -.050020 .138100 -.0018930
+47471.8 -.100790 .165460 -.0330640
+47490 -.138410 .207940 -.0653100
+47508.3 -.164740 .261170 -.0918720
+47526.6 -.160800 .315200 -.1150310
+47544.8 -.142800 .359530 -.1376110
+47563.1 -.118060 .400480 -.1614360
+47581.3 -.076130 .436720 -.1886810
+47599.6 -.027240 .466260 -.2181740
+47617.9 .029070 .480900 -.2452640
+47636.1 .085860 .481670 -.2761340
+47654.4 .127570 .470310 -.3109290
+47672.7 .169220 .445260 -.3388540
+47690.9 .204420 .412890 -.3680090
+47709.2 .239210 .364510 -.3859960
+47727.4 .259150 .312920 -.4009490
+47745.7 .259400 .255620 -.4214040
+47764 .250970 .197720 -.4424920
+47782.2 .217420 .146080 -.4611150
+47800.5 .165120 .105110 -.4903590
+47818.7 .098020 .083150 -.5228010
+47837 .031480 .074730 -.5592470
+47855.3 -.036820 .087780 -.6014820
+47873.5 -.090410 .118340 -.6390910
+47891.8 -.132820 .163800 .3294770
+47910.1 -.165870 .215080 .2971590
+47928.3 -.202780 .276390 .2614270
+47946.6 -.201680 .344430 .2205110
+47964.8 -.185180 .413230 .1791940
+47983.1 -.153910 .471710 .1351550
+48001.4 -.101550 .524360 .0948910
+48019.6 -.049070 .556830 .0554040
+48037.9 .025280 .572830 .0201240
+48056.2 .096160 .568570 -.0096640
+48074.4 .166980 .541670 -.0403750
+48092.7 .230520 .499990 -.0614930
+48110.9 .276540 .439320 -.0830990
+48129.2 .306230 .379690 -.1154370
+48147.5 .312300 .309110 -.1458280
+48165.7 .295990 .241750 -.1816860
+48184 .258290 .178550 -.2226650
+48202.3 .214630 .130690 -.2630650
+48220.5 .158460 .095450 -.3023270
+48238.8 .092590 .076290 -.3458130
+48257 .023070 .070420 .6189660
+48275.3 -.040210 .085850 .5788470
+48293.6 -.104560 .109710 .5375110
+48311.8 -.159350 .156090 .4982140
+48330.1 -.193710 .215880 .4563180
+48348.4 -.218250 .286330 .4101900
+48366.6 -.211540 .359670 .3695240
+48384.9 -.196100 .427740 .3269160
+48403.1 -.148030 .487300 .2852580
+48421.4 -.087780 .535430 .2509990
+48439.7 -.027600 .563210 .2244370
+48457.9 .049470 .572690 .1994110
+48476.2 .118890 .560660 .1792000
+48494.4 .172940 .527310 .1523860
+48512.7 .218130 .487510 .1145380
+48531 .251390 .434860 .0781450
+48549.2 .262130 .372840 .0419620
+48567.5 .263160 .317840 .0002900
+48585.8 .247290 .263630 -.0397540
+48604 .217400 .209210 -.0833930
+48622.3 .183430 .168250 -.1253090
+48640.5 .132860 .139720 -.1664230
+48658.8 .083410 .121340 -.2133580
+48677.1 .026850 .116920 -.2592480
+48695.3 -.034650 .126400 -.3037880
+48713.6 -.082680 .163990 -.3574120
+48731.9 -.115610 .203320 -.4102900
+48750.1 -.145550 .245630 -.4539040
+48768.4 -.156500 .291880 -.4959850
+48786.6 -.153110 .334650 -.5303330
+48804.9 -.140940 .381020 .4420910
+48823.2 -.115160 .420770 .4184490
+48841.4 -.077490 .459320 .3892290
+48859.7 -.031830 .492120 .3596050
+48878 .014530 .506730 .3231410
+48896.2 .055600 .503780 .2854080
+48914.5 .103120 .494250 .2458780
+48932.7 .137780 .473110 .1994080
+48951 .170750 .444760 .1538820
+48969.3 .187750 .406300 .1096390
+48987.5 .208200 .361780 .0639800
+49005.8 .212870 .311750 .0163900
+49024 .205710 .265790 -.0283100
+49042.3 .186400 .225690 -.0785000
+49060.6 .159450 .194090 -.1275600
+49078.8 .113650 .170110 -.1750500
+49097.1 .065420 .163930 -.2286500
+49115.4 .025160 .166410 -.2813400
+49133.6 -.005520 .177900 -.3249500
+49151.9 -.036590 .190810 -.3670900
+49170.1 -.063740 .211350 .5975800
+49188.4 -.073070 .234400 .5709200
+49206.7 -.091560 .266690 .5374700
+49224.9 -.101560 .302050 .5021000
+49243.2 -.099970 .336910 .4679700
+49261.5 -.095410 .370980 .4245500
+49279.7 -.080940 .403840 .3774500
+49298 -.062500 .428470 .3322200
+49316.2 -.041250 .448950 .2851400
+49334.5 -.019600 .464960 .2441700
+49352.8 .009200 .476210 .2006800
+49371 .054050 .478850 .1576400
+49389.3 .099840 .467200 .1161800
+49407.6 .137660 .448700 .0759200
+49425.8 .158340 .421910 .0247300
+49444.1 .175010 .390000 -.0223200
+49462.3 .183970 .356950 -.0662200
+49480.6 .186340 .317810 -.1154700
+49498.9 .180670 .281910 -.1586500
+49517.1 .160850 .243850 -.1904900
+49535.4 .134830 .210780 .7815600
+49553.7 .101460 .184820 .7562100
+49571.9 .067260 .172080 .7314100
+49590.2 .026570 .168170 .7029600
+49608.4 -.016290 .179410 .6665200
+49626.7 -.066370 .200140 .6249200
+49645 -.106130 .232880 .5793400
+49663.2 -.141410 .271820 .5355300
+49681.5 -.152620 .321150 .4935800
+49699.7 -.153790 .368190 .4476700
+49718 -.153720 .418090 .3991000
+49736.3 -.127450 .461200 .3532000
+49754.5 -.111580 .494070 .3028200
+49772.8 -.075420 .524080 .2573700
+49791.1 -.022770 .546010 .2056500
+49809.3 .035210 .558400 .1532400
+49827.6 .092910 .555900 .1027500
+49845.8 .162220 .532900 .0547700
+49864.1 .209220 .497340 .0067200
+49882.4 .254650 .443560 -.0324300
+49900.6 .281610 .378980 -.0635800
+49918.9 .287570 .308430 -.0926700
+49937.2 .273820 .237780 -.1136100
+49955.4 .237560 .179860 -.1418400
+49973.7 .189520 .133990 -.1803800
+49991.9 .135580 .105900 -.2213600
+50010.2 .066650 .085830 -.2632900
+50028.5 .002730 .084730 -.3084000
+50046.7 -.067070 .101140 -.3533700
+50065 -.138200 .136100 -.3981800
+50083.3 -.176160 .192180 .5551900
+50101.5 -.203930 .246410 .5221600
+50119.8 -.223380 .312950 .4899200
+50138 -.220960 .378460 .4499500
+50156.3 -.196520 .448570 .4092600
+50174.6 -.150870 .508690 .3699700
+50192.8 -.101690 .553340 .3258300
+50211.1 -.033850 .584490 .2846000
+50229.3 .040580 .595820 .2454400
+50247.6 .115360 .581210 .2114200
+50265.9 .181570 .545250 .1862900
+50284.1 .239170 .495410 .1659300
+50302.4 .267760 .436840 .1407800
+50320.7 .297160 .371810 .1230500
+50338.9 .290780 .296700 .0985300
+50357.2 .267110 .228290 .0638000
+50375.4 .227160 .171270 .0297800
+50393.7 .175940 .122030 -.0071800
+50412 .109730 .096200 -.0441600
+50430.2 .039500 .084250 -.0749400
+50448.5 -.020320 .095060 -.1096500
+50466.8 -.073030 .119750 -.1459800
+50485 -.124420 .156650 -.1730900
+50503.3 -.161820 .203960 -.2056400
+50521.5 -.186450 .263310 -.2482000
+50539.8 -.190710 .331710 -.2929300
+50558.1 -.174960 .396620 -.3387100
+50576.3 -.145010 .452930 -.3794300
+50594.6 -.098250 .498370 -.4162300
+50612.9 -.047990 .525350 -.4516000
+50631.1 .023090 .536790 .5261800
+50649.4 .088210 .526330 .5094900
+50667.6 .141170 .505000 .4812500
+50685.9 .181140 .467130 .4540400
+50704.2 .216860 .428130 .4276000
+50722.4 .221240 .378750 .3908700
+50740.7 .226280 .329150 .3513700
+50758.9 .214670 .278540 .3151900
+50777.2 .190380 .234080 .2784700
+50795.5 .151700 .201890 .2466500
+50813.7 .103371 .174919 .2187928
+50832 .051655 .167526 .1878736
+50850.3 -.005228 .173571 .1500342
+50868.5 -.051800 .188169 .1146000
+50886.8 -.079308 .220212 .0745974
+50905 -.110294 .254246 .0360847
+50923.3 -.113475 .301652 .0024942
+50941.6 -.114391 .344204 -.0345923
+50959.8 -.107406 .383854 -.0684825
+50978.1 -.099535 .414513 -.0887736
+50996.4 -.065007 .440850 -.1011400
+51014.6 -.021674 .465308 -.1082697
+51032.9 .017316 .478858 -.1146002
+51051.1 .058055 .476585 -.1250252
+51069.4 .097135 .465706 -.1418293
+51087.7 .125857 .444588 -.1587117
+51105.9 .149956 .418823 -.1862362
+51124.2 .163218 .388340 -.2147711
+51142.5 .154524 .350655 -.2401730
+51160.7 .137225 .321181 -.2653190
+51179 .138906 .296108 .7168562
+51197.2 .117532 .271385 .7014530
+51215.5 .100290 .252373 .6791687
+51233.8 .077098 .244566 .6561574
+51252 .054921 .239895 .6399826
+51270.3 .023809 .240618 .6145151
+51288.6 .001100 .248865 .5879676
+51306.8 -.017471 .262305 .5664931
+51325.1 -.023004 .281431 .5435885
+51343.3 -.031745 .296763 .5267825
+51361.6 -.031910 .311187 .5198637
+51379.9 -.024948 .330418 .5120479
+51398.1 -.014139 .345615 .5055648
+51416.4 -.008467 .361644 .4977768
+51434.6 .002079 .370914 .4819857
+51452.9 .006586 .379679 .4688203
+51471.2 .015741 .382597 .4506114
+51489.4 .024443 .379545 .4230946
+51507.7 .032087 .377378 .3974911
+51526 .037562 .380865 .3782104
+51544.2 .043300 .377661 .3554817
+51562.5 .052375 .375702 .3413574
+51580.7 .060632 .372427 .3250578
+51599 .067994 .365396 .3071171
+51617.3 .074610 .357979 .2899885
+51635.5 .075437 .346051 .2762541
+51653.8 .082353 .343278 .2552646
+51672.1 .088049 .332016 .2358447
+51690.3 .096303 .321406 .2198842
+51708.6 .113565 .304386 .2064913
+51726.8 .110079 .279086 .2040804
+51745.1 .096137 .261702 .2004203
+51763.4 .083088 .248498 .1987985
+51781.6 .058388 .242745 .1949233
+51799.9 .025972 .239588 .1871360
+51818.2 -.005722 .246917 .1747298
+51836.4 -.036045 .262831 .1600032
+51854.7 -.059965 .292223 .1391654
+51872.9 -.076915 .325404 .1205337
+51891.2 -.082660 .357017 .1045817
+51909.5 -.074006 .396764 .0936988
+51927.7 -.060835 .427064 .0806986
+51946 -.032401 .451341 .0762852
+51964.2 .004327 .475921 .0624390
+51982.5 .044840 .486196 .0388944
+52000.8 .091974 .489418 .0251539
+52019 .139116 .477702 .0096675
+52037.3 .178791 .452008 -.0088652
+52055.6 .215170 .409242 -.0228961
+52073.8 .242795 .359595 -.0245445
+52092.1 .254005 .304725 -.0278187
+52110.3 .247620 .249184 -.0254430
+52128.6 .220968 .199893 -.0206066
+52146.9 .175053 .158811 -.0281492
+52165.1 .125932 .132198 -.0304154
+52183.4 .064697 .117778 -.0383976
+52201.7 .001422 .120744 -.0558549
+52219.9 -.061030 .140506 -.0697006
+52238.2 -.103646 .180496 -.0868556
+52256.4 -.153361 .228492 -.1008918
+52274.7 -.176817 .292216 -.1154380
+52293 -.178852 .348438 -.1269166
+52311.2 -.163183 .406360 -.1417309
+52329.5 -.135832 .461499 -.1552923
+52347.8 -.088799 .513379 -.1739301
+52366 -.027634 .540974 -.1901581
+52384.3 .029239 .554816 -.2007113
+52402.5 .092521 .550437 -.2137458
+52420.8 .142157 .534179 -.2303514
+52439.1 .192923 .504114 -.2287794
+52457.3 .228889 .458092 -.2296246
+52475.6 .244514 .404459 -.2327614
+52493.8 .257521 .348296 -.2240428
+52512.1 .254939 .295393 -.2248033
+52530.4 .235509 .242129 -.2303263
+52548.6 .197659 .199413 -.2327877
+52566.9 .151551 .165804 -.2429725
+52585.2 .084865 .146837 -.2549716
+52603.4 .022450 .142124 -.2612171
+52621.7 -.038228 .154264 -.2767537
+52639.9 -.087743 .187387 -.2892390
+52658.2 -.127578 .228891 -.2995032
+52676.5 -.147530 .278647 -.3078795
+52694.7 -.154775 .332102 -.3201905
+52713 -.157108 .383491 -.3285379
+52731.3 -.131007 .438825 -.3439523
+52749.5 -.094644 .486399 -.3588043
+52767.8 -.043183 .522695 -.3638535
+52786 .001931 .541957 -.3745652
+52804.3 .062632 .546600 -.3752839
+52822.6 .135203 .538240 -.3661265
+52840.8 .191134 .509318 -.3577046
+52859.1 .232086 .463954 -.3537978
+52877.4 .259508 .415127 -.3494880
+52895.6 .264299 .356543 -.3529986
+52913.9 .257771 .302232 -.3575570
+52932.1 .226130 .250158 -.3628298
+52950.4 .195950 .207643 -.3728456
+52968.7 .147593 .174905 -.3807556
+52986.9 .087282 .158884 -.3820300
+53005.2 .031246 .153785 -.3896036
+53023.5 -.014033 .166554 -.3994989
+53041.7 -.057805 .191331 -.4041551
+53060 -.101004 .228144 -.4153922
+53078.2 -.126021 .268583 -.4225892
+53096.5 -.140343 .321451 -.4338646
+53114.8 -.132911 .375713 -.4512439
+53133 -.112725 .423506 -.4596331
+53151.3 -.090722 .458455 -.4683249
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/interp.f
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/interp.f	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/interp.f	(revision 22322)
@@ -0,0 +1,165 @@
+      SUBROUTINE INTERP (RJD,X,Y,T,N,RJDINT,XINT,YINT,TINT)
+C
+C     THIS SUBROUTINE TAKES A SERIES OF X, Y, AND UT1-UTC VALUES
+C     AND INTERPOLATES THEM TO AN EPOCH OF CHOICE.  THIS ROUTINE
+C     ASSUMES THAT THE VALUES OF X AND Y ARE IN SECONDS OF
+C     ARC AND THAT UT1-UTC IS IN SECONDS OF TIME.  AT LEAST
+C     ONE POINT BEFORE AND ONE POINT AFTER THE EPOCH OF THE
+C     INTERPOLATION POINT ARE NECESSARY IN ORDER FOR THE
+C     INTERPOLATION SCHEME TO WORK.
+C
+C     PARAMETERS ARE :
+C     RJD   - ARRAY OF THE EPOCHS OF DATA (GIVEN IN MJD)
+C     X     - ARRAY OF X POLAR MOTION (ARCSEC)
+C     Y     - ARRAY OF Y POLAR MOTION (ARCSEC)
+C     T     - ARRAY OF UT1-UTC (SEC)
+C     N     - NUMBER OF POINTS IN ARRAYS
+C     RJDINT- EPOCH FOR THE INTERPOLATED VALUE
+C     XINT  - INTERPOLATED VALUE OF X
+C     YINT  - INTERPOLATED VALUE OF Y
+C     TINT  - INTERPOLATED VALUE OF UT1-UTC
+C
+      DOUBLE PRECISION RJD(N), X(N), Y(N), T(N),
+     . RJDINT, XINT, YINT, TINT, CORX, CORY, CORT
+      CALL LAGINT (RJD,X,N,RJDINT,XINT)
+      CALL LAGINT (RJD,Y,N,RJDINT,YINT)
+      CALL LAGINT (RJD,T,N,RJDINT,TINT)
+      CALL RAY (RJDINT,CORX,CORY,CORT)
+      XINT = XINT + CORX
+      YINT = YINT + CORY
+      TINT = TINT + CORT
+      RETURN
+      END
+C
+C----------------------------------------------------------------
+C
+      SUBROUTINE LAGINT (X,Y,N,XINT,YOUT)
+C  
+C     THIS SUBROUTINE PERFORMS LAGRANGIAN INTERPOLATION
+C     WITHIN A SET OF (X,Y) PAIRS TO GIVE THE Y
+C     VALUE CORRESPONDING TO XINT.  THIS PROGRAM USES A
+C     WINDOW OF 4 DATA POINTS TO PERFORM THE INTERPOLATION.
+C     IF THE WINDOW SIZE NEEDS TO BE CHANGED, THIS CAN BE
+C     DONE BY CHANGING THE INDICES IN THE DO LOOPS FOR 
+C     VARIABLES M AND J.
+C
+C     PARAMETERS ARE :
+C     X     - ARRAY OF VALUES OF THE INDEPENDENT VARIABLE
+C     Y     - ARRAY OF FUNCTION VALUES CORRESPONDING TO X
+C     N     - NUMBER OF POINTS
+C     XINT  - THE X-VALUE FOR WHICH ESTIMATE OF Y IS DESIRED
+C     YOUT  - THE Y VALUE RETURNED TO CALLER
+C
+      REAL*8 X(N),Y(N),XINT,YOUT,TERM
+      INTEGER N,I,J
+C
+      YOUT = 0.0D0
+      DO 5 I = 1,N-1
+        IF ( XINT .GE. X(I) .AND. XINT .LT. X(I+1) ) K = I
+    5 CONTINUE
+      IF ( K .LT. 2 ) K = 2
+      IF ( K .GT. N-2 ) K = N-2
+      DO 20 M = K-1,K+2
+        TERM = Y(M)
+        DO 10 J = K-1,K+2
+          IF ( M .NE. J ) THEN
+            TERM = TERM * (XINT - X(J))/(X(M) - X(J))
+          END IF
+   10   CONTINUE
+        YOUT = YOUT + TERM
+   20 CONTINUE
+      RETURN
+      END
+
+C
+C----------------------------------------------------------------
+C
+      SUBROUTINE RAY (RJD,CORX,CORY,CORT)
+C
+C   THIS SUBROUTINE IMPLEMENTS THE RAY MODEL FOR
+C   DIURNAL/SUBDIURNAL TIDES.  IT USES THE SIMON ET AL.
+C   FUNDAMENTAL ARGUMENTS.  THE CORRECTIONS IN X AND Y ARE IN
+C   UNITS OF SEC. OF ARC AND UT1-UTC IN SEC. OF TIME.  THESE
+C   CORRECTIONS SHOULD BE ADDED TO "AVERAGE" EOP VALUES TO GET
+C   ESTIMATES OF THE INSTANTANEOUS VALUES.
+C
+C     PARAMETERS ARE :
+C     RJD   - EPOCH OF INTEREST GIVEN IN MJD
+C     CORX  - TIDAL CORRECTION IN X (SEC. OF ARC)
+C     CORY  - TIDAL CORRECTION IN Y (SEC. OF ARC)
+C     CORT  - TIDAL CORRECTION IN UT1-UTC (SEC. OF TIME)
+C
+      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
+      DOUBLE PRECISION
+     .   L,        LPRIME
+      HALFPI = 1.5707963267948966d0
+      T = (RJD - 51544.5D0)/36525.0D0
+      L = -0.00024470d0*T**4 + 0.051635d0*T**3 + 31.8792d0*T**2
+     .  + 1717915923.2178d0*T + 485868.249036d0
+      L = DMOD(L,1296000d0)
+      LPRIME = -0.00001149d0*T**4 - 0.000136d0*T**3
+     .  -  0.5532d0*T**2
+     .  + 129596581.0481d0*T + 1287104.79305d0
+      LPRIME = DMOD(LPRIME,1296000d0)
+      CAPF = 0.00000417d0*T**4 - 0.001037d0*T**3 - 12.7512d0*T**2
+     .  + 1739527262.8478d0*T + 335779.526232d0
+      CAPF = DMOD(CAPF,1296000d0)
+      CAPD = -0.00003169d0*T**4 + 0.006593d0*T**3 - 6.3706d0*T**2
+     .  + 1602961601.2090d0*T + 1072260.70369d0
+      CAPD = DMOD(CAPD,1296000d0)
+      OMEGA = -0.00005939d0*T**4 + 0.007702d0*T**3
+     .  + 7.4722d0*T**2
+     .  - 6962890.2665d0*T + 450160.398036d0
+      OMEGA = DMOD(OMEGA,1296000d0)
+      THETA = (67310.54841d0 + 
+     .        (876600d0*3600d0 + 8640184.812866d0)*T +
+     .         0.093104d0*T**2 -
+     .         6.2d-6*T**3)*15.0d0 + 648000.0d0
+      ARG7 = DMOD((-L - 2.0D0*CAPF - 2.0D0*OMEGA + THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0) - HALFPI
+      ARG1 = DMOD((-2.0d0*CAPF - 2.0d0*OMEGA + THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0) - HALFPI
+      ARG2 = DMOD((-2.0d0*CAPF + 2.0d0*CAPD - 2.0d0*OMEGA
+     .       + THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0) - HALFPI
+      ARG3 = DMOD(THETA *
+     .        3.14159265D0/648000.0D0,6.28318530718D0)
+     .     + HALFPI
+      ARG4 = DMOD((-L - 2.0d0*CAPF - 2.0D0*OMEGA + 2.0d0*THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0)
+      ARG5 = DMOD((-2.0D0*CAPF - 2.0D0*OMEGA + 2.0d0*THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0)
+      ARG6 = DMOD((-2.0d0*CAPF + 2.0d0*CAPD - 2.0d0*OMEGA
+     .     + 2.0d0*THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0)
+      ARG8 = DMOD((2.0d0*THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0)
+      CORX = - 0.026D0*DSIN(ARG7) + 0.006D0*DCOS(ARG7)
+     .       - 0.133D0*DSIN(ARG1) + 0.049D0*DCOS(ARG1)
+     .       - 0.050D0*DSIN(ARG2) + 0.025D0*DCOS(ARG2)
+     .       - 0.152D0*DSIN(ARG3) + 0.078D0*DCOS(ARG3)
+     .       - 0.057D0*DSIN(ARG4) - 0.013D0*DCOS(ARG4)
+     .       - 0.330D0*DSIN(ARG5) - 0.028D0*DCOS(ARG5)
+     .       - 0.145D0*DSIN(ARG6) + 0.064D0*DCOS(ARG6)
+     .       - 0.036D0*DSIN(ARG8) + 0.017D0*DCOS(ARG8)
+      CORY = - 0.006D0*DSIN(ARG7) - 0.026D0*DCOS(ARG7)
+     .       - 0.049D0*DSIN(ARG1) - 0.133D0*DCOS(ARG1)
+     .       - 0.025D0*DSIN(ARG2) - 0.050D0*DCOS(ARG2)
+     .       - 0.078D0*DSIN(ARG3) - 0.152D0*DCOS(ARG3)
+     .       + 0.011D0*DSIN(ARG4) + 0.033D0*DCOS(ARG4)
+     .       + 0.037D0*DSIN(ARG5) + 0.196D0*DCOS(ARG5)
+     .       + 0.059D0*DSIN(ARG6) + 0.087D0*DCOS(ARG6)
+     .       + 0.018D0*DSIN(ARG8) + 0.022D0*DCOS(ARG8)
+      CORT = + 0.0245D0*DSIN(ARG7) + 0.0503D0*DCOS(ARG7)
+     .       + 0.1210D0*DSIN(ARG1) + 0.1605D0*DCOS(ARG1)
+     .       + 0.0286D0*DSIN(ARG2) + 0.0516D0*DCOS(ARG2)
+     .       + 0.0864D0*DSIN(ARG3) + 0.1771D0*DCOS(ARG3)
+     .       - 0.0380D0*DSIN(ARG4) - 0.0154D0*DCOS(ARG4)
+     .       - 0.1617D0*DSIN(ARG5) - 0.0720D0*DCOS(ARG5)
+     .       - 0.0759D0*DSIN(ARG6) - 0.0004D0*DCOS(ARG6)
+     .       - 0.0196D0*DSIN(ARG8) - 0.0038D0*DCOS(ARG8)
+      CORX = CORX * 1.0d-3
+      CORY = CORY * 1.0d-3
+      CORT = CORT * 0.1d-3
+      RETURN
+      END
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/memory.notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/memory.notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/memory.notes.txt	(revision 22322)
@@ -0,0 +1,17 @@
+
+*** I have moved functions and static vars around to place the close
+    together and only when needed
+
+*** I have put a lock around a larger fraction of psRealloc since it
+    was potentially hitting a race condition for hold of
+    lastMemBlockAllocated
+
+*** I removed some nonsensical code in psRealloc (eg, if (X == Y) { Y = X; })
+    and cleaned up the memBlock reference mods to they are more readable.
+
+*** WHY do we modify p_psMemAllocID after hitting memAllocCallback???
+    this makes no sense to me.
+
+*** does get psMemIncrRefCounter really need to lock?  only if
+    p_psMemAllocID is being modified, which makes no sense to me (see
+    above).
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/pics/coordinateConv.fig
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/pics/coordinateConv.fig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/pics/coordinateConv.fig	(revision 22322)
@@ -0,0 +1,68 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 5662.500 5400.000 5850 4950 6150 5400 5850 5850
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 5737.500 6600.000 5925 6150 6225 6600 5925 7050
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 5437.500 4200.000 5625 3750 5925 4200 5625 4650
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 4762.500 1800.000 4950 1350 5250 1800 4950 2250
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 4912.500 3000.000 5100 2550 5400 3000 5100 3450
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 3787.000 6600.000 3600 7050 3300 6600 3600 6150
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 3787.000 5400.000 3600 5850 3300 5400 3600 4950
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 3787.000 4200.000 3600 4650 3300 4200 3600 3750
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 3787.000 3000.000 3600 3450 3300 3000 3600 2550
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 3787.000 1800.000 3600 2250 3300 1800 3600 1350
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 1 0 1 8282.143 3675.000 7575 4875 9675 3675 7575 2475
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 1 1 1 8990.878 4800.000 8775 7350 11550 4800 8775 2250
+	2 1 1.00 60.00 120.00
+	2 1 1.00 60.00 120.00
+6 1425 1575 3150 2025
+4 0 0 50 0 0 14 0.0000 4 195 1695 1425 1725 x -= myFrame->x0;\001
+4 0 0 50 0 0 14 0.0000 4 195 1695 1425 1980 y -= myFrame->y0;\001
+-6
+6 5550 1575 7350 2025
+4 0 0 50 0 0 14 0.0000 4 195 1740 5550 1725 x += myFrame->x0;\001
+4 0 0 50 0 0 14 0.0000 4 195 1740 5550 1980 y += myFrame->y0;\001
+-6
+6 1200 3975 3075 4425
+4 0 0 50 0 0 14 0.0000 4 195 1335 1200 4125 psChipInFPA()\001
+4 0 0 50 0 0 14 0.0000 4 195 1860 1200 4380 psCoordFPAtoChip()\001
+-6
+6 1200 2700 3075 3225
+4 0 0 50 0 0 14 0.0000 4 195 1275 1200 2865 psCellInChip()\001
+4 0 0 50 0 0 14 0.0000 4 195 1875 1200 3120 psCoordChipToCell()\001
+-6
+6 8925 5475 11250 6225
+4 0 0 50 0 0 14 0.0000 4 195 1800 8925 5625 psCoordCellToSky()\001
+4 0 0 50 0 0 14 0.0000 4 195 2310 8925 5880 psCoordCellToSkyQuick()\001
+4 0 0 50 0 0 14 0.0000 4 195 1800 8925 6135 psCoordSkyToCell()\001
+-6
+4 0 0 50 0 0 14 0.0000 4 150 570 3900 1350 Frame\001
+4 0 0 50 0 0 14 0.0000 4 195 795 3900 2550 Cell (x,y)\001
+4 0 0 50 0 0 14 0.0000 4 195 930 3900 3750 Chip (X,Y)\001
+4 0 0 50 0 0 14 0.0000 4 195 1455 3900 4950 Focal Plane (p,q)\001
+4 0 0 50 0 0 14 0.0000 4 195 1695 3900 6150 Tangent Plane (l,m)\001
+4 0 0 50 0 0 14 0.0000 4 195 1230 3900 7350 Sky (RA,Dec)\001
+4 0 0 50 0 0 14 0.0000 4 195 1875 5775 3000 psCoordCellToChip()\001
+4 0 0 50 0 0 14 0.0000 4 195 1875 6150 4200 psCoordCellToFPA()\001
+4 0 0 50 0 0 14 0.0000 4 195 1710 6375 5400 psCoordFPAtoTP()\001
+4 0 0 50 0 0 14 0.0000 4 195 1635 6450 6600 psCoordTPtoSky()\001
+4 0 0 50 0 0 14 0.0000 4 195 1710 1275 5475 psCoordTPtoFPA()\001
+4 0 0 50 0 0 14 0.0000 4 195 1710 1275 6600 psCoordSkyToTP()\001
+4 0 0 50 0 0 14 0.0000 4 195 1875 7725 3675 psCoordCellToFPA()\001
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/pics/coordinateFrames.fig
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/pics/coordinateFrames.fig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/pics/coordinateFrames.fig	(revision 22322)
@@ -0,0 +1,58 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 1 5062.500 1800.000 5250 1350 5550 1800 5250 2250
+	2 1 1.00 60.00 120.00
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 1 5137.500 3000.000 5325 2550 5625 3000 5325 3450
+	2 1 1.00 60.00 120.00
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 1 5737.500 4200.000 5925 3750 6225 4200 5925 4650
+	2 1 1.00 60.00 120.00
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 1 5962.500 5400.000 6150 4950 6450 5400 6150 5850
+	2 1 1.00 60.00 120.00
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 1 6037.500 6600.000 6225 6150 6525 6600 6225 7050
+	2 1 1.00 60.00 120.00
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 1 1 0 2883.750 4800.000 3300 2250 300 4800 3300 7350
+	2 1 1.00 60.00 120.00
+5 1 0 1 0 7 50 0 -1 0.000 0 1 1 0 5587.500 3600.000 3900 2550 3600 3600 3900 4650
+	2 1 1.00 60.00 120.00
+6 1650 3075 3525 4275
+4 0 0 50 0 0 14 0.0000 4 195 1755 1650 3225 myCell->cellToFPA\001
+4 0 0 50 0 0 14 0.0000 4 195 1815 1650 3480 combines cellToChip\001
+4 0 0 50 0 0 14 0.0000 4 195 1725 1650 3735 and chipToFPA into\001
+4 0 0 50 0 0 14 0.0000 4 195 1545 1650 3990 a single low-order\001
+4 0 0 50 0 0 14 0.0000 4 150 1260 1650 4245 transformation\001
+-6
+6 6675 4950 11100 5925
+4 0 0 50 0 0 14 0.0000 4 195 4260 6675 5100 myFPA->FPtoTP and myFPA->TPtoFP specifies\001
+4 0 0 50 0 0 14 0.0000 4 195 3990 6675 5355 high-order transformation to correct for optical\001
+4 0 0 50 0 0 14 0.0000 4 180 4410 6675 5610 distortion in the camera, and chromatic effects from\001
+4 0 0 50 0 0 14 0.0000 4 195 1335 6675 5865 the atmosphere\001
+-6
+6 6750 6375 10500 6825
+4 0 0 50 0 0 14 0.0000 4 150 2790 6750 6780 SLALib to do the transformation\001
+4 0 0 50 0 0 14 0.0000 4 195 3735 6750 6525 myFPA->exp contains information allowing\001
+-6
+4 0 0 50 0 0 14 0.0000 4 150 570 4200 1350 Frame\001
+4 0 0 50 0 0 14 0.0000 4 195 795 4200 2550 Cell (x,y)\001
+4 0 0 50 0 0 14 0.0000 4 195 930 4200 3750 Chip (X,Y)\001
+4 0 0 50 0 0 14 0.0000 4 195 1455 4200 4950 Focal Plane (p,q)\001
+4 0 0 50 0 0 14 0.0000 4 195 1695 4200 6150 Tangent Plane (l,m)\001
+4 0 0 50 0 0 14 0.0000 4 195 1230 4200 7350 Sky (RA,Dec)\001
+4 0 0 50 0 0 14 0.0000 4 195 3150 5850 1875 x0, y0 specifies offset from cell origin\001
+4 0 0 50 0 0 14 0.0000 4 195 4875 6450 4275 myChip->chipToFPA specifies low-order transformation\001
+4 0 0 50 0 0 14 0.0000 4 195 4665 5775 3075 myCell->cellToChip specifies low-order tranformation\001
+4 0 0 50 0 0 14 0.0000 4 195 2190 525 4980 provides a quick and dirty\001
+4 0 0 50 0 0 14 0.0000 4 150 2160 525 5235 low-order transformation\001
+4 0 0 50 0 0 14 0.0000 4 195 1770 525 5490 from a cell to the sky\001
+4 0 0 50 0 0 14 0.0000 4 195 1680 525 4725 myCell->cellToSky\001
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/psLibADD.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/psLibADD.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/psLibADD.tex	(revision 22322)
@@ -0,0 +1,3527 @@
+%%% $Id: psLibADD.tex,v 1.95 2006-12-09 00:30:57 eugene Exp $
+\documentclass[panstarrs]{panstarrs}
+
+\usepackage{amsmath}
+\usepackage{verbatim}
+
+% basic document variables
+\title{Pan-STARRS PS-1 Image Processing Pipeline}
+\subtitle{Algorithm Design Description}
+\shorttitle{IPP ADD}
+\author{Eugene Magnier, Paul Price, Joshua Hoblitt, Robert Lupton}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{12}
+\docnumber{PSDC-430-006}
+
+% \setcounter{tocdepth}{5} % lowest level to be included in toc
+
+\newcommand\citealt{}
+
+\begin{document}
+\maketitle
+
+% -- Revision History --
+% provide explicit values for the old versions
+% use '\theversion' for the current version (set above)
+% use \hline between each table row
+\RevisionsStart
+% version     Date         Description
+00 & 2004 Mar 11 & Hacking \\ \hline
+01 & 2004 May 21 & Added section on 2D Chebyshev fitting, then removed. \\ \hline
+02 & 2004 Jun 22 & modified stats specification \\ \hline
+03 & 2004 Jul 13 & \\ \hline
+04 & 2004 Aug 16 & \\ \hline
+05 & 2004 Sep 01 & \\ \hline
+06 & 2004 Sep 07 & Frozen for PSLib-2 \\ \hline
+07 & 2004 Nov 24 & Frozen for Cycle 4 \\ \hline
+08 & 2005 Jan 21 & Draft for Cycle 5 \\ \hline
+09 & 2005 Feb 14 & Frozen for Cycle 5 \\ \hline
+10 & 2005 Apr 19 & Frozen for Cycle 6 \\ \hline
+11 & 2005 Apr 27 & Update for Cycle 6 \\ \hline
+\RevisionsEnd
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\DocumentsInternalSection
+PSDC-230-001  &   PS-1 Design Reference Mission \\ \hline
+PSDC-430-004  &   Pan-STARRS PS-1 IPP C Code Conventions \\ \hline
+PSDC-430-005  &   Pan-STARRS PS-1 IPP Software Requirements Specification \\ \hline
+PSDC-430-006  &   Pan-STARRS PS-1 IPP Algorithm Design Document \\ \hline
+PSDC-430-011  &   Pan-STARRS PS-1 IPP System/Subsystem Design Description \\ \hline
+\DocumentsExternalSection
+Posix Standard                      & Open Group Based Specifications Issue 6, IEEE Std 1003.1, 2003 \\ \hline
+Numerical Recipes (NR)              & Press, Teukolsky, Vetterline, Flannery \\ \hline
+Knuth, D.E.                         & Sorting and Searching; The Art of Computer Programming \\ \hline
+Sedgewick, R.                       & Algorithms, Ch. 8 \\ \hline
+Sorting Summary                     & \code{http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/algoen.htm } \\ \hline
+GSL                                 & \\ \hline
+FFTW                                & (Fastest Fourier Transform in the West) \\ 
+                                    & \code{http://www.fftw.org} \\ \hline
+FITS Projection Article             & {Greisen \& Calabretta (1995, ADASS, 4, 233)} \\
+                                    & \code{http://www.cv.nrao.edu/fits/documents/wcs/wcs.all.ps} \\ \hline
+Hipparcos and Tycho Catalogues      & \code{http://astro.estec.esa.nl/Hipparcos/CATALOGUE_VOL1/catalog_vol1.html} \\ \hline
+Zombeck                             & ``Handbook of Space Astronomy and Astrophysics'', second edition, \\ 
+                                    & \code{http://ads.harvard.edu/books/hsaa/toc.html} \\ \hline
+Reingold \& Dershowitz              & ``Calendrical Calculations: The Millenium Edition'', Cambridge University Press, 2002. \\
+\hline
+\DocumentsEnd
+
+\tableofcontents
+\pagebreak 
+\pagenumbering{arabic}
+
+% \section{Pan-STARRS Library PSLib}
+
+\section{PSLib Math Utilities}
+
+\subsection{Sorting}
+
+A variety of sorting algorithms exist, with a wide range in speed for
+different set sizes.  Three of the best sorting algorithms are
+``heapsort'', ``quicksort'', and ``mergesort'' (see, e.g.,
+\citealt{knuth}, \citealt{sedgewick}, \citealt{press}).  These three
+sorting algorithms all run in a time of $O(n \log n)$ on the average,
+but have different worse-case run times.  Implementations of all three
+sorting algorithms are described in references above and in various
+places online (e.g.,
+http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/algoen.htm).
+The linux \code{qsort} function uses a heapsort if it can allocate a
+sufficiently large temporary buffer, and otherwise resorts to a
+quicksort in-place.  The GSL function \code{gsl_heapsort} uses the
+heapsort algorithm.  Both of these implemenations require a pointer to
+the comparison function, which may result in a slower code than if the
+comparison were done within the sort function.
+
+For PSLib, the sorting functions shall be implemented via the system
+\code{qsort}.  The function \code{psSort} shall return the sorted
+result of the input array without over-writing the input array.  The
+function \code{psSortIndex} shall return an integer index to the
+sequence of the input array without overwriting the input array.
+Given the following line of code:
+\begin{verbatim}
+out = psSortIndex (NULL,&in);}
+\end{verbatim}
+the elements of the array \code{out} are in the sequence
+\code{in.arr[out->arr[0]]} to \code{in.arr[out->arr[in.n - 1]]}.
+
+\subsection{Smoothing: Boxcar and Gaussian}
+\label{smooth}
+
+Smoothing may occasionally be perfomed on data.  We present the
+algorithms for two typical versions: boxcar and Gaussian smoothing.
+In both smoothing techniques, given a series of data values $f_i(x_i)$
+where $x_i$ are the values of the corresponding to the center of the
+bin, the smoothed values $g_i(x_i)$ are determined by calculating a
+linear combination based on the input data point and its nearest $2N$
+neighbors in the form:
+
+\begin{equation}
+g_i = \sum_{j=i_{\rm min}}^{i_{\rm max}} c_{ij} f_j
+\end{equation}
+%
+where the values of $c_{ij}$ determine the filter type.  For boxcar
+smoothing, the values $c_{ij}$ are constant and scaled to maintain the
+zeroth moment of the data (care must be taken at the ends of the data
+range to reduce the value of $c_{ij}$ as fewer input data points may
+be used).  For Gaussian smoothing, the crucial parameter is
+$\sigma_x$, the standard deviation.  The values of $i_{\rm min}$ and
+$i_{\rm max}$ are functions of the standard deviation: $i_{\rm min}$
+corresponds to the bin in which $x_i - N\sigma_x$ is found; similarly
+$i_{\rm max}$ is the bin corresponding to $x_i + N\sigma_x$.  The
+value of $N$ should be chosen to be large enough to sample the
+Gaussian, $N = 5$.  The values of $c_{ij}$ are then just the Gaussian
+curve:
+
+\begin{equation}
+c_{ij} = \frac{e^{\frac{-(x_j - x_i)^2}{2\sigma_x^2}}}{\sqrt{2\pi\sigma_x^2}}
+\end{equation}
+
+\subsection{Statistics}
+
+The general statistics function \code{psStats} performs a variety of
+statistical calculations on a collection of floating point numbers.
+These statistics include both sample values, in which the formal
+statistic is calculated for the complete sample, and robust values, in
+which the statistic is estimated on the basis of an assumption of an
+underlying population.  While more reliable in general, the robust
+estimators may be somewhat slower than the sample statisic, so their
+use may vary depending on the context.  In addition, certain
+sigma-clipped values are defined as an intermediate choice between the
+sample and robust estimators.
+
+\subsubsection{Sample Statistics}
+
+We define the following statistical terms, assuming there is a set of
+data elements $x_i$ with (standard) errors $\sigma_i$.
+
+\paragraph{Mean}
+
+The simple mean is defined as:
+\begin{equation}
+\bar{x} = \frac {1}{N} \sum^N_i x_i
+\end{equation}
+
+\paragraph{Weighted Mean}
+
+The weighted mean is defined as:
+\begin{equation}
+\bar{x} = \sum_i \frac{x_i}{\sigma_i^2} \ / \ \sum_i \frac{1}{\sigma_i^2}
+\end{equation}
+
+In the event that all the errors are identical, this reduces to the
+standard definition of the mean.
+
+\paragraph{Median}
+
+The median is defined as the value for which 50\% of the data values
+are larger and 50\% are smaller.  For a sample, the values are sorted
+and the median is given by the value of the middle element, if the
+number of values is odd, and by the average of the two middle values
+if the number of values is even.  This median should be avoided for
+samples which are large (e.g., $N > 10^4$ elements) as the basic
+robust median is quicker and more accurate.  Errors are ignored when
+calculating the sample median.
+
+\paragraph{Upper and Lower Quartiles}
+
+The upper and lower quartiles ($U_{\frac{1}{4}}$ and
+$L_{\frac{1}{4}}$) are first and last quarter equivalents to the
+median.  The upper quartile is the value for which 25\% of the data
+are larger and 75\% are smaller.  The lower quartile is the value for
+which 75\% of the data are larger and 25\% are smaller.  For a sample,
+the values are sorted and the quartiles are given by the values of
+elements $N/4$ and $3N/4$.  For the purpose of a sample upper and
+lower quartile, it is sufficient to provide the closest integer entry
+to these values.  The sample quartiles should be avoided for samples
+which are large (e.g., $N > 10^4$ elements) as the robust quartiles
+are quicker and more accurate.  Errors are ignored when calculating
+the sample quartiles.
+
+\paragraph{Standard Deviation}
+
+The standard deviation of the sample is given by:
+
+\begin{equation}
+\sigma = \sqrt{\sum_{i = 1}^N \frac{(x_i - \bar{x})^2}{N - 1}}
+\end{equation}
+
+To minimize the numerical rounding error, this should be calculated
+numerically as:
+
+\begin{equation}
+\sigma = \sqrt{\frac{1}{N - 1} \left[ \sum_{i = 1}^{N} (x_i - \bar{x})^2 - \frac{1}{N} \left(\sum_{i = 1}^{N} (x_i - \bar{x})\right)^2 \ \right]}
+\end{equation}
+
+If the errors are known, then the sample standard deviation is:
+
+\begin{equation}
+\sigma = \left( \sum_i \frac{1}{\sigma_i^2} \right) ^{-1/2}
+\end{equation}
+
+
+\subsubsection{Clipped Statistics}
+
+The clipped statistics are used to determine the mean and standard
+deviation of a distribution in the presence of outliers. The clipped
+statistics are quicker than the complete robust statistical estimators
+and more reliable than the sample statistics.  The clipped statistics
+algorithm produces the clipped mean and clipped standard deviation.
+The algorithm has 2 parameters: $N$, the number of iterations and $k$,
+the multiplying factor of the standard deviation used to exclude
+outliers.  Typical values for both $N$ and $k$ are 3.  The algorithm
+is as follows:
+
+\begin{enumerate}
+\item Compute the sample median. The number of data points must be
+      limited to 10000; the input dataset must be randomly subsampled
+      if more data points are used.
+\item Compute the sample standard deviation.
+\item Use the sample median as the first estimator of the mean, $\bar{x}$.
+\item Use the sample standard deviation as the first estimator of the
+  true standard deviation, $\sigma$.
+\item Repeat the following N times:
+  \begin{enumerate}
+  \item Exclude all values $x_{i}$ for which $|x_{i} - \bar{x}| > k \sigma$.
+  \item Compute the mean and standard deviation of the sub-sample.
+  \item Use the new mean for $\bar{x}$.
+  \item Use the new standard deviations for $\sigma$.
+\end{enumerate}
+\item The last calculated value of $\bar{x}$ is the clipped mean.
+\item The last calculated value of $\sigma$ is the clipped standard
+  deviation.
+\end{enumerate}
+
+If the errors in the input values are known, then the clips are made
+on the basis of the errors in the input values instead of the standard
+deviation of the sample: values are excluded for which $|x_i -
+\bar{x}| > k \sigma_i$.
+
+\subsubsection{Robust Statistics}
+
+Robust statistics algorithms provide estimators of basic statistical
+concepts which are reliable even for data samples with significant
+contamination.  A typical case is the situation in which the data of
+interest represent a primary population of interest with a
+single-valued mean and standard deviation and a secondary population
+of data with a substantially different distribution.  For example, an
+image of an uncrowded night-time field may consist of a sparse
+collection of stars and an overall background level.  The majority of
+pixels have the background value, with some variance due to noise
+sources, but many are significantly higher (contributed by stars) or
+significantly lower (dead pixels).  If we want to measure the mean of
+the background, a robust mean is necessary as the outliers will bias
+the sample statistics.  We define two levels of robust statistical
+estimators: robust statistics using the cumulative histogram, and
+statistics measured by fitting the differential histogram.
+
+The robust statistics from the cumulative are calculated by
+constructing a cumulative histogram of the values and performing
+simple measurements of the histogram data distribution.  The use of
+the cumulative histogram by this algorithm reduces the sensitivity to
+the exact bin size.  The initial bin size is set to 1/1000 of the
+total data range. The data values are found using quadratic
+interpolation between the bin of interest and its two neighbors.
+
+\begin{itemize}
+\item Construct the histogram with the specified bin size.
+\item Construct the cumulative histogram from the specific histogram
+\item Find the bin which contains the 50\% data point.  
+\item Interpolate to the exact 50\% position: this is the robust
+  histogram median.
+\item Find the bins which contains the 15.8655\% and 84.1345\% data
+  points.  
+\item Interpolate to find these two positions exactly: these are the
+  $\pm 1\sigma$ positions.
+\item Determine $\sigma$ as 1/2 of the distance between these
+  positions.
+\item If the measured $\sigma$ is less than 2 times the bin size,
+  exclude points which are more than 25 bins from the median,
+  recalculate the bin size, and perform the algorithm again.
+\item Find the bins which contains the 25\% and 75\% data points.
+\item Interpolate to find these two positions exactly: these are the
+  upper and lower quartile positions.
+\end{itemize}
+
+If the errors in the input values are known, then the same approach is
+used, except that the histograms become probability density functions
+(PDFs).  In this case, the input values are spread out, so that they
+do not simply contribute a single unit to the histogram, but rather
+contribute a fraction of a value, equivalent to the weight.  In the
+interests of speed, a boxcar PDF may be used to represent each input
+value (as opposed to a Gaussian), where the boxcar width is equal to
+$2 \sqrt{2 \ln 2}$ times the error and each input value contributes
+constant area.  Then the robust median and standard deviation are
+estimated in the same manner as above.
+
+\subsubsection{Fitted Statistics}
+
+The fitted statistics algorithm starts with the histogram used for the
+robust statistics and determines the population statistics by fitting
+a Gaussian model to the histogram.  The algorithm is as follows:
+
+\begin{itemize}
+\item Perform the Robust Histogram Statistics algorithm above,
+  yielding an estimated standard deviation, $\sigma$.
+
+\item Generate a new histogram for the data sample setting a bin size,
+  $d\sigma$, based on the estimated standard deviation and the number
+  of data points in the inner 50 percentile ($N_{\rm 50}$) as follows:
+
+\begin{itemize}
+\item let $dN = (\sigma / d\sigma) = 0.017 N_{50}$
+\item limit $dN$ to the range 1 to 5.
+\item set the bin size $d\sigma = \sigma / dN$
+\end{itemize}
+
+\item Smooth the resulting histogram with a Gaussian with $\sigma_x$ =
+  1 bin in this new histogram.
+\item Find the bin with the peak value in the range $\pm 2 \sigma$ of
+  the robust histogram median.
+
+\item Fit a Gaussian to the bins in the range $\pm 20 \sigma$ of the
+  robust histogram median. Limit the fit range to the data range, if
+  the latter is less then $\pm 20 \sigma$.  If the data range is small
+  compared to the estimated $\sigma$, fit at least 4 bins of the
+  hisgram centered on the robust histogram median.
+
+\item The robust mean $\mbox{mean}_r$ is derived directly from the
+  fitted Gaussian mean.  
+\item The robust standard deviation, $\sigma_r$, is determined by
+  subtracting the smoothing scale in quadrature: $\sigma_r^2 =
+  \sigma_{\rm fit}^2 - \sigma_s^2$
+\end{itemize}
+
+To explain the choice of the histogram bin size: a histogram of a
+Gaussian distribution with bin size $d\sigma$ will have approximately
+$(2.35 \sigma/d\sigma)$ bins covering the range LQ to UQ.  Thus, the
+average number of points per bin ($N_{\rm bin}$) in that interval will
+be $N_{50} / (2.35 \sigma/d\sigma)$.  The value of $d\sigma$ should be
+no larger than $\sigma$, regardless of the number of points, to avoid
+too much undersampling.  The value of $d\sigma$ should also be no
+smaller than $5\sigma$, again regardless of the number of points, to
+avoid excessive oversample.  Intermediate to those two values, the bin
+size is choosen to keep about 25 points per bin.  Thus, the bin size
+($d\sigma$) is set to about:
+\begin{equation}
+d\sigma = 2.35 \sigma (N_{\rm bin}/N_{50}) = (25 \times 2.35) (\sigma/N_{50})
+\end{equation}
+With the limitation that $\sigma/d\sigma$ should be limited on one end
+to the value 1, and the other to the value 5.  The easiest way to set
+this limit is to define dN to be:
+\begin{equation}
+dN = (\sigma / d\sigma) = (N_{50} / N_{\rm bin}) / 2.35 = 0.017 * N_{50}
+\end{equation}
+\subsubsection{Histograms}
+
+When calculating histograms in the presence of known errors in the
+input values, the approach described above for the robust statistics
+is used (i.e., the histograms become probability density functions).
+
+An example may help here.  Say we have our histogram bounds being 0,
+1, 2, 3, 4, 5; and our value is $2.5 \pm 0.5$.  Then, the width of the
+contribution is $0.5 \times 2.35... \approx 1.175$.  Half the width is
+0.5875, so we will treat this value as a boxcar from $2.5 - 0.5875$ to
+$2.5 + 0.5875$.
+
+Consequently, the bins 0 to 1 and 4 to 5 get no value, because none of
+the boxcar overlaps.  The bin 1 to 2 gets 0.0875, because that's the
+fraction of the boxcar that overlaps with it; same thing with the bin
+3 to 4.  The bin 2 to 3 gets 0.825 because that's the fraction of the
+boxcar that overlaps with it.  So the single value $2.5 \pm 0.5$ makes
+the following histogram:
+
+\begin{tabular}{lr}
+Bin & Value \\ \hline
+0--1 & 0 \\
+1--2 & 0.0875 \\
+2--3 & 0.8250 \\
+3--4 & 0.0875 \\
+4--5 & 0 \\
+\end{tabular}
+
+Note that the total adds to one --- the number of values added.
+
+\subsection{Polynomials}
+\label{sec:polynomials}
+
+We will employ Chebyshev polynomials (NR \S 5.8) to approximate functions:
+\begin{equation}
+f(x) = \sum_{i=0}^{n} c_i T_i(x)
+\end{equation}
+These have some desirable features:
+\begin{itemize}
+\item They are bounded on $-1 < x < 1$, with the maxima and minima
+over this range being 1 and $-1$, respectively;
+\item Truncation of the higher-order terms leaves one with the most accurate
+lower-order polynomial representation of the desired function.
+\end{itemize}
+
+The first few Chebyshev polynomials are:
+\begin{eqnarray}
+T_0(x) & = & 1 \\
+T_1(x) & = & x \\
+T_2(x) & = & 2x^2 - 1 \\
+T_3(x) & = & 4x^3 - 3x \\
+T_4(x) & = & 8x^4 - 8x^2 + 1 
+\end{eqnarray}
+Chebyshev polynomials follow the recurrence relation:
+\begin{equation}
+T_{n+1} = 2xT_n - T_{n-1}
+\end{equation}
+
+Practically, Chebyshev polynomials should be evaluated using Clenshaw's recurrence
+formula (NR \S 5.5):
+\begin{eqnarray}
+d_j  & = & 2xd_{j+1} - d_{j+2} + c_j \\
+f(x) & = & x*d_1 - d_2 + 1/2 c_0
+\end{eqnarray}
+
+It shall be the responsibility of the user to convert the domain into the range
+$-1 < x < 1$.
+
+\subsubsection{Multi-dimensional polynomials}
+
+Multi-dimensional polynomials shall be composed of multiplications of
+1D Chebyshev polynomials, with the coefficients stored in tensors of
+the appropriate rank.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Fitting}
+
+\subsubsection{Chi-squared}
+\label{chisq}
+
+Given a set of N ordinates, $x_i$, measured coordinates, $y_i$, with
+errors, $\sigma_i$, and a model function, $f(x_i)$, then $\chi^2$
+(``chi-squared'') is defined:
+
+\begin{equation}
+\chi^2 = \sum_{i=0}^{N-1} \left( \frac{y_i - f(x_i)}{\sigma_i} \right)^2
+\end{equation}
+
+\subsubsection{General Polynomial Fitting}
+
+Given a set of data values $y_i$ with errors $\sigma_i$, related to
+independent data values $x_i$, we would like to fit a polynomial model
+of $N_{par}$ free parameters of the form $y = \sum_{j=0}^{N_{par}}
+\alpha_j x^j$.  The model is determined by minimizing the value of
+$\chi^2$ (\ref{chisq}), and the minimization is determined by solving
+for the set of parameters $\alpha_j$ for which the partial derivatives
+of the $\chi^2$ with respect to those parameters are zero.  The
+partial derivatives are
+
+\begin{equation}
+\frac{\partial \chi^2}{\partial \alpha_k} = -2 \sum_i (y_i - \sum_j x_i^j \alpha_j) \frac{x_i^k}{\sigma_i^2}
+\end{equation}
+
+Setting the derivatives to zero, this may be reduced to the following
+matrix equation:
+
+\begin{equation}
+\sum_j \alpha_j \sum_i \frac{x_i^k x_i^j}{\sigma_i^2} = \sum_i \frac{x_i^k y_i}{\sigma_i^2}
+\end{equation}
+
+This matrix equation may be solved with LU Decomposition
+(section~\ref{LUdecomp}).
+
+\subsection{Non-linear Minimization}
+
+Non-linear minimization techniques use an iterative approach to find a
+minimization when an analytical inversion is impractical or not
+possible.  These techniques use a starting guess for the parameters of
+interest, and make a sequence of new guess parameters based on the
+properties of the function at the previous position.  If the new
+parameters yield a reduced function value, the new position is used as
+the starting position for the next iteration.  Otherwise, the guess
+must be modified and another attempt is made.  Convergence may be
+determined based on the absolute amount of change in the function
+value, or by comparison with the expectation for a linear system.
+
+The two common techniques used to construct a guess parameter set are
+the 'steepest descent method' and the 'Gauss-Newton method'.  In the
+first case, the guess is selected some distance along the local
+gradient.  In the second case, a local Taylor expansion of the
+function is used to construct a linear model for the function, and the
+new guess is chosen to minimize that linear model.  The methods
+discussed below make use of combinations of these two methods.  Aside
+from the differences in their guess steps, the two methods differ in
+using first derivatives of the function supplied by the user, or by
+locally calculating the first derivatives.
+
+Mathematically, we would like to choose the parameter set $a_m$ to
+minimize a function of those parameters $F(a_m)$.  We iterate by
+choosing a new parameter set $a^\prime_m = a_m + \delta_m$ based on
+the behavior of the function at $a_m$.  
+
+The steepest descent method chooses a step direction of $\bar{\delta}
+= -\nabla F$, or defining $g_m$ as a component of the gradient,
+$\delta_m = -g_m$.  The Gauss-Newton method uses a Taylor expansion of
+the function to solve for the step: $(\nabla^2 F) \bar{\delta} =
+-\nabla F$.  Defining an element of the Hessian matrix $H_{m,n}$ as a
+component of the second derivatives, we can write the Gauss-Newton
+step as $\delta_m = - H_{m,n}^{-1} g_n$.
+
+\subsubsection{Levenberg-Marquardt Method}
+
+In the Levenberg-Marquardt Method (LMM; see NR \S 15.5, Madsen et al),
+we make a guess at the input parameters, evaluate the function of
+interest, vary the parameters by a particular choice based on the
+gradient, evaluate the function again, and adjust the parameters and
+the parameter varient based on the results.  The LMM requires the
+second derivative of the function to be negligible, as in the case of
+minimizing $\chi^2$.
+
+Consider the chi-square function. Given some ordinates, $x_i$, we
+would like to find the parameters, $a_m$, of the function which
+minimize $\chi^2$ for some measurements, $y_i$ and associated errors,
+$\sigma_i$:
+\begin{eqnarray}
+\chi^2 (\bar{a})  & = & \sum_i \frac{1}{\sigma_i^2} \left( y_i - p(x_i;a_m) \right)^2 
+\end{eqnarray}
+
+We simplify this as:
+\begin{eqnarray}
+p_i (a_m)         & = & p(x_i;a_m) \\
+f_i (a_m)         & = & \frac{1}{\sigma_i} (y_i - p_i) \\
+\chi^2 (\bar{a})  & = & \sum_i f_i^2 
+\end{eqnarray}
+
+We write the minimization function $F(a_m) = \frac{1}{2} \chi^2$ to
+avoid the various extra factors of 2.  We can now write out the needed
+derivatives in terms of $f_i$:
+\begin{eqnarray}
+F(a_m)            & = & \frac{1}{2} \sum_i f_i^2 \\
+\nabla F(a_m)     & = & \sum_i f_i \frac{\partial f_i}{\partial a_m} \\
+\nabla^2 F(a_m)   & = & \sum_i \frac{\partial f_i}{\partial a_m} \frac{\partial f_i}{\partial a_n}
+\end{eqnarray}
+
+where we have dropped the second-derivatives of the function in the
+representation of $\nabla^2 F(a_m)$.  Since $\frac{\partial
+f_i}{\partial a_m} = -\frac{1}{\sigma_i}\frac{\partial p_i}{\partial
+a_m}$, we can write these in terms of the derivatives of $p_i$ only:
+\begin{eqnarray}
+\nabla F(a_m)     & = & -\sum_i \frac{f_i}{\sigma_i} \frac{\partial p_i}{\partial a_m} \\
+\nabla^2 F(a_m)   & = &  \sum_i \frac{1}{\sigma_i^2} \frac{\partial p_i}{\partial a_m} \frac{\partial p_i}{\partial a_n}
+\end{eqnarray}
+
+Writing these in matrix representation, and replacing $f_i$, we have:
+\begin{eqnarray}
+-g_m              & = & \sum_i \frac{(y_i - p_i)}{\sigma_i^2} \frac{\partial p_i}{\partial a_m} \\
+H_{m,n}           & = & \sum_i \frac{1}{\sigma_i^2} \frac{\partial p_i}{\partial a_m} \frac{\partial p_i}{\partial a_n}
+\end{eqnarray}
+
+In the Levenberg-Marquart Method, we define a new guess using a
+combination of the Steepest Descent and Gauss-Newton methods discussed
+above.  We replace the Hessian matrix above with $A_{m,n}$ as a
+variant on $H_{m,n}$ as follows:
+
+\begin{eqnarray}
+A_{m,n} & = & H_{m,n} \mbox{if} (j \ne k) \\
+A_{m,n} & = & H_{m,n}(1 + \lambda) \mbox{if} (j = k)
+\end{eqnarray}
+%
+and solve the system of equations represented by:
+\begin{equation}
+A_{m,n} \delta_n = -g_m
+\end{equation}
+%
+The new parameter guess is then found from this value with
+$\alpha^\prime_n = \alpha_n + \delta_n$. We use this parameter set to
+evaluate the function.
+
+To assess the quality of the new parameter set, we compare the change
+in $\chi^2$ with the change expected from the linear model (the Taylor
+expansion).  If the linear model were correct, we would have expected
+a change (a reduction) in $\chi^2$ of $\Delta =
+\frac{\lambda}{2}\sum\delta_m^2 + \frac{1}{2}\sum\delta_m g_m$.  We
+use the 'gain ratio' $\rho = \frac{\chi^2_{\rm old} - \chi^2_{\rm
+new}}{\Delta}$ to judge the new step. If $\rho > 0$, we accept this
+new set of parameters and decrease $\lambda$ by a factor of 10,
+otherwise we keep the old set, and increase the value of $\lambda$ by
+a factor of 10.  We repeat this process until the value of the
+function changes by much less than the tolerance.  The resulting
+values of $a_m$ are the best-fit parameters for the system.
+
+The covariance matrix, $C_{i,j}$, which is the inverse of the matrix
+$H_{m,n}$ provides an estimate of the confidence limits of the
+parameters.
+
+%If the errors are normally distributed, the formal errors on the
+%parameters are then calculated by setting $\lambda = 0$ and
+%calculating the covarience matrix $C_{i,j}$, the inverse of the matrix
+%$\alpha_{j,k}$.
+%The independent 68.3\% confidence limit on parameter $a_k$ is then
+%$\sqrt{C_{k,k}}$.  Confidence contours for sets of parameters may be
+%defined as well by the function $\Delta = \delta\bar{a} P_{j,k}^{-1}
+%\delta\bar{a}$ where $P_{j,k}$ is the projected matrix of $C_{j,k}$,
+%ie those rows and columns of $C_{j,k}$ associated with the parameters
+%of interest, the vector $\delta\bar{a}$.  The value of $\Delta$ is
+%given by the table below for specific confidence limits and numbers of
+%parameters.  Note that it is necessary to be able to calculate both
+%the function as well as its derivative for any combination of
+%parameters and dependent variables.
+%
+%\begin{center}
+%\begin{tabular}{|l|r|r|r|}
+%\hline
+%{\bf P} & \multicolumn{3}{c|}{\bf $N_{par}$} \\
+%        & 1    & 2    & 3    \\
+%\hline
+%68.3\%  & 1.00 & 2.30 & 3.53 \\
+%95.4\%  & 4.00 & 6.17 & 8.02 \\
+%99.73\% & 9.00 & 11.8 & 14.2 \\
+%\hline
+%\end{tabular}
+%\end{center}
+
+\subsubsection{Powell's method}
+
+Powell's method is a type of ``Direction Set'' methods in
+multi-dimensions for finding a local minimum.  Given a starting point
+(the ``best guess'' for the minimum) and a set of direction vectors, a
+direction set method advances in the direction of the vectors,
+determines a new direction vector by some method, and proceeds in this
+manner until the advances along the vectors are smaller than some
+pre-defined tolerance.  Such direction set methods, including Powell's
+Quadratically Convergent method are discussed in NR \S 10.5.
+
+We will use for our algorithm the modified version of Powell's
+Quadratically Convergent Method, which is described below, adapted
+from NR.
+
+\begin{enumerate}
+\item Given a function in $N$ dimensions to minimize, $f$, and a best
+  guess for the minimum, point $P$ in $N$ dimensions, take an initial
+  set of $N$ vectors, $v_i$, to be the unit vectors.
+\item Set point $Q = P$.
+\item For each dimension in turn, move $Q$ \textit{only} in the
+  direction $v_i$ to minimize the function of interest.
+\item Set vector $u = Q - P$.
+\item Move $Q$ \textit{only} in the direction $u$
+\item Replace the vector along which the largest minimization was
+  made, $v_{i,\rm max}$, with $u$, except under either of the
+  following circumstances:
+  \begin{itemize}
+  \item If $f_QP \ge f_P$, then there is no point in keeping the new
+    vector, because there is no further minimization to be made in
+    that direction.
+  \item If $2 ( f_P - 2f_Q + f_{QP} ) \left[ ( f_P - f_Q ) -
+  \Delta_{\rm max} \right]^2 \ge ( f_P - f_{QP} )^2 \Delta_{\rm max}$,
+  then either the decrease in the function was not due to any single
+  direction, or we are close to the minimum.
+  \end{itemize}
+  where $f_P = f(P)$, $f_Q = f(Q)$, $f_{QP} = f(2Q - P)$, and
+  $\Delta_{\rm max} \ge 0$ is the magnitude of the minimization made
+  along $v_{i,\rm max}$.
+\item Set $P$ to $Q$.
+\item Return to step 3 until the change in this last move is less
+  than some specified tolerance, or a maximum number of iterations
+  has been reached.
+\end{enumerate}
+
+In regards to minimizing the function only in a particular direction,
+we shall adopt, as NR recommends, bracketing the minimum before
+applying Brent's method, \tbd{which will be specified in detail
+later}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Image Manipulations}
+
+\subsubsection{Interpolation}
+
+Interpolation is needed in various image manipulation operations,
+including rotation and resampling.  We have specified a function to
+perform the interpolation using one of several possible interpolation
+methods, defined below.  It is important in the discussions that
+follow to remember that a pixel with column,row if $i,j$ has
+coordinate at the center of $i+0.5,j+0.5$ and corners with coordinates
+from $i,j$ to $i+1,j+1$.  Thus, the interpolation of a coordinate
+$x,y$ = 5.0,4.0 is a value midway between the four pixels with
+column,row of (5,4), (5,5), (6,4), (6,5).  
+
+\paragraph{Nearest Pixel Interpolation ({\tt PS\_INTERPOLATE\_FLAT})}
+
+In this interpolation, the value of the closest pixel is returned.
+This is equivalent to pixel duplication or replication.
+
+\paragraph{Bilinear Interpolation ({\tt PS\_INTERPOLATE\_BILINEAR})}
+
+In this interpolation, the value at the coordinate is calculated using
+linear interpolation in two dimensions from the four nearest neighbor
+pixels.  The bilinear interpolation value at a coordinate $x,y$
+depends on the four nearest neighbor pixels and the fractional
+distance $fx,fy$ of the given coordinates from the centers of those
+four pixels.  Consider four neighboring pixels at column,row of $i,j$,
+$i+1,j$, $i,j+1$, and $i+1,j+1$ with pixel values $V_{0,0}$,
+$V_{1,0}$, $V_{0,1}$, $V_{1,1}$.  The value at $x,y$ is given by:
+\begin{equation} V = (V_{0,0}(1 - f_x) + V_{1,0}f_x)(1 - f_y) + (V_{0,1}(1-f_x) + V_{1,1}f_x)f_y \end{equation}
+This expression is more efficiently evaluated by factoring and
+calculating the expresion as:
+\begin{equation} r_x = V_{0,0} + (V_{1,0} - V_{0,0})f_x \end{equation}
+\begin{equation} V = r_x + (V_{0,1} + (V_{1,1} - V_{0,1})f_x - r_x)f_y \end{equation}
+
+Note that the values of $f_x$ and $f_y$ require some care.  Given a
+coordinate $x,y$, the value of $f_x$ is calculated as $f_x - 0.5 -
+int(f_x - 0.5)$.  For example, when interpolating the value at
+(5.8.5.2), the relevant neighbor pixels are (5,4), (6,4), (5,5), (6,5)
+and the fractional coordinate values $f_x, f_y = 0.3, 0.7$.  The
+resulting coordinate would be contained within the pixel at column,row
+(5,5).
+
+\paragraph{Sinc Interpolation ({\tt PS\_INTERPOLATE\_LANCZOS[234]})}
+
+Because it would be slow to specify the size of the kernel
+dynamically, we specify three hard-coded kernel sizes: 4, 6 and 8
+pixels in each dimension (a kernel of size 2 pixels in each dimension
+is handled by the bilinear interpolation).  These correspond to the
+options \code{PS_INTERPOLATE_LANCZOS2}, \code{PS_INTERPOLATE_LANCZOS3} and
+\code{PS_INTERPOLATE_LANCZOS4}, respectively.
+
+Given a position on the input image, $(x_0,y_0)$, a kernel is derived
+according to pixels local to the position:
+\begin{equation}
+  h(x,y) = {\rm sinc}(\pi \delta x) {\rm sinc}(\pi \delta x / N) \rm{sinc}(\pi \delta y) \rm{sinc}(\pi \delta y / N)
+\end{equation}
+where
+\begin{eqnarray}
+  \delta x & = & x - x_0 \\
+  \delta y & = & y - y_0 \\
+  {\rm sinc}(z) & = & \sin(z)/z
+\end{eqnarray}
+and $N$ corresponds to the choice of kernel size.  For $N = 2$, the
+kernel size is 4 pixels in each dimension (i.e., $-2 < \delta x \le
+2$).  For $N = 3$, the kernel size is 6 pixels in each dimension
+(i.e., $-3 < \delta x \le 3$).  For $N = 4$, the kernel size is 8
+pixels in each dimension (i.e., $-4 < \delta x \le 4$).
+
+The interpolated value at the given position, $(x_0,y_0)$, is then
+simply the dot product of the kernel and the fluxes:
+\begin{equation}
+  f(x_0,y_0) = \sum_R f(x,y) h(x,y)
+\end{equation}
+where $R$ is the region defined by the kernel size, and $f(x,y)$ is
+the flux at the pixel position.
+
+For further information, see the
+\href{http://terapix.iap.fr/IMG/pdf/swarp.pdf}{SWarp manual}.
+
+\subsubsection{Image Cuts and Slices}
+
+Several functions specify operations which manipulate a collection of
+pixels to return a statistic on the pixel collection.  In the simplest
+case, these are trivial to define: if the boundaries of the region of
+interest are specified along integral pixel coordinates, then the
+pixels used to measure the statistic are always an exact integer.
+This is the case for the function \code{psImageSlice} which requires a
+starting coordinate which is an integer and a width in both dimensions
+which is an integer.  For the case of the functions \code{psImageCut}
+and \code{psImageRadialCut}, the situation is a bit more subtle.  In
+both of these cases, the region is unlikely to contain only whole
+pixels and some choices must be made.
+
+One posibility which we reject is to identify the fractional pixels
+which are overlapped by the region of interest and add that fraction
+of the pixel's flux when calculating the statistic of interest.  This
+is computationally intensive, and not necessarily well defined for all
+statistics.  
+
+In PSLib, we instead identify the pixels overlapped by the region, use
+the complete set of pixel values, treating all pixels equally, and
+renormalize as needed.  To perform this, the region of interest is
+laid on top of the image pixels.  Any pixels which overlap the region
+are identified as part of the input sample.  The statistic (ie, sample
+mean, robust mode, etc), is then calculated on this collection of
+pixels.  If the output statistic is an average value, the measured
+value is reported.  If the output statistic is a sum value (sum of
+counts, sum of pixels), then the value is renormalized by the ratio of
+pixels used in the calculation to the pixel area of the region of
+interest.  For example, if the sum within a radial aperture is
+requested, the circle of the specified radius and center is placed on
+the pixel grid.  Any pixels which touch the circle are then placed in
+a list to be analysed.  The statistic of interest is the measured for
+this collection of pixels.  In the case of a circular aperture which
+is centered at the coordinate (2,2) and has a radius of 2, the number
+of pixels which are touched by the circle is 16, while the total pixel
+area of the circle is 12.57 square pixels.  In this case, the pixel
+sum is renormalized by the ratio (12.57/16.00).
+
+\paragraph{Radial Cuts}
+
+Consider an image with pixels $x_i,y_i$ and a reference coordinate
+$x_c, y_c$.  We want to construct a radial cut by measuring statistics
+for pixels in a sequence of radial annulii $r_s < r < r_e$.  For each
+annulus, we need to select the pixels which fall within this annulus.
+The coordinates of the center of pixel $i,j$ are $i+0.5,j+0.5$.  A
+given pixel has a distance from the reference coordinate of $dX = x_c
+- i - 0.5, dY = y_c - j - 0.5$.  The pixels to be used for a given
+radial annulus are all of those pixels for which $r_s < \sqrt{dX^2 +
+  dY^2} < r_e$.  This is more efficiently calculated by comparing the
+square of the radii and distances.  All pixels which satisfy the above
+condition are included in a specific annular radius.  All average
+quantities are calculated directly from the pixel ensemble
+statistics.  
+
+\paragraph{Arbitrary Linear Cuts}
+
+Select the pixels which lie along a line following steps of 1 pixel
+length:
+
+\begin{verbatim}
+
+  dX = xe - xs;
+  dY = ye - ys;
+  L = hypot (dX, dY);
+  dX = dX / L;
+  dY = dY / L;
+
+  REALLOCATE (xvec[0].elements, float, MAX (L, 1));
+  REALLOCATE (yvec[0].elements, float, MAX (L, 1));
+  xvec[0].Nelements = L;
+  yvec[0].Nelements = L;
+
+  V = (float *)buf[0].matrix.buffer;
+  for (i = 0; i < L; i++) {
+    xi = xs + i*dX - 0.5;
+    yi = ys + i*dY - 0.5;
+    xvec[0].elements[i] = i;
+    yvec[0].elements[i] = V[xi + Nx*yi];
+  }
+\end{verbatim}
+
+\subsubsection{Image Rotation}
+
+Image rotation can be performed in two possible ways under different
+circumstances, identified in the following discussion.
+
+In the simplest case, the rotation angle is an integer multiple of 90
+degrees ($\pi/2$ rad).  In these cases, the input and output pixels
+have a one-to-one mapping.  If the input image has dimensions of $N_x,
+N_y$, then the output image will have dimensions of either $N_x, N_y$
+(for even multiples of 90 degrees) or $N_y, N_x$ (for odd multiples).
+
+If the angle of the rotation is not a multiple of 90, then the output
+pixels necessarily result from the interpolation of several input
+pixels.  In this case, for an input image of dimensions $N_x, N_y$ and
+rotation angle $\theta$, the output image has dimensions $Lx = |N_x
+\cos \theta| + |N_y \sin \theta|$ and $Ly = |N_x \sin \theta| + |N_y
+\cos \theta|$, each dimension rounded up to the nearest integer as
+needed.  Every pixel in the output image is in general derived from an
+interpolation over 4 neighboring pixels.  The coordinate of a pixel in
+the output image ($i,j$) corresponds to a fractional pixel coordinate
+($x,y$) in the input image according to:
+\begin{equation} x = (i - i_o)*\cos\theta + (j - j_o)*\sin\theta \end{equation}
+\begin{equation} y = (i_o - i)*\sin\theta + (j - j_o)*\cos\theta \end{equation}
+where the offset coordinate ($i_o,j_o$) depends on the sign of the
+sine of the angle $\theta$.  If the sign of that sine is positive, the
+offset coordinate is ($N_y\sin\theta$,0), otherwise it is
+(0,$-N_x\sin\theta$).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagebreak 
+
+\subsection{Matrix Operations}
+
+In this section, we define the linear algebra operations performed on
+matrices.  We have defined APIs to implement the following matrix
+functions:
+
+\begin{itemize}
+\item Invert a matrix;
+\item Calculate a matrix determinant;
+\item Perform matrix addition, subtraction and multiplication;
+\item Transpose a matrix; and
+\item Convert a matrix to a vector.
+\end{itemize}
+
+Many of these operations are implemented using LU decomposition.  We
+define LU decomposition in the following paragraph.  Implementation of
+LU decomposition shall make use of the GSL function
+\code{gsl_linalg_LU_decomp}.
+
+\subsubsection{LU Decomposition}
+\label{LUdecomp}
+
+We wish to decompose the matrix $A$ with elements $a_{ij}$ into
+diagonal matrices that satisfy the relationship $A = L U$ where $L$ is
+a lower-diagonal matrix of the form:
+\begin{equation}
+L = \left(
+\begin{matrix}
+\alpha_{11} & 0           & 0           & 0 \\
+\alpha_{21} & \alpha_{22} & 0           & 0 \\
+\alpha_{31} & \alpha_{32} & \alpha_{33} & 0 \\
+\alpha_{41} & \alpha_{42} & \alpha_{43} & \alpha_{44} \\
+\end{matrix} \right)
+\end{equation}
+%
+(where all diagonal values $\alpha_{ii} = 1$) and $U$ is an upper-diagonal matrix of the form:
+\begin{equation}
+U = \left(
+\begin{matrix}
+\beta_{11} & \beta_{12} & \beta_{13} & \beta_{14} \\
+0          & \beta_{22} & \beta_{23} & \beta_{24} \\
+0          & 0          & \beta_{33} & \beta_{34} \\
+0          & 0          & 0          & \beta_{44} \\
+\end{matrix} \right)
+\end{equation}
+
+We can find the values of $\alpha_{ij}$ and $\beta_{ij}$ by following
+this procedure.  First, $\alpha_{ii} = 1$.  For all values of $j = 1,
+2, \dots, N$, follow the next steps: For each value of $i = 1, 2,
+\dots, j$, solve for $\beta_{ij}$ using the relationship:
+\begin{equation}
+\beta_{ij} = a_{ij} - \sum_{k=1}^{i-1} \alpha_{ik}\beta_{kj}
+\end{equation}
+For the values of $i = j+1, j+2, \dots, N$, solve for the values of
+$\alpha_{ij}$ with the following:
+%
+\begin{equation}
+\alpha_{ij} = \frac{1}{\beta_{jj}} \left( a_{ij} - \sum_{k=1}^{j-1} \alpha_{ik}\beta_{kj} \right)
+\end{equation}
+
+\subsubsection{Calculate a matrix determinant}
+
+The determinant $D$ of a matrix $a_{ij}$ is calculated from the
+product of the diagonal elements of the LU decomposition: 
+\begin{equation}
+D = P_{i=1}{N} U_{ii}
+\end{equation}
+
+This shall be calculated using the GSL function
+\code{gsl_linalg_LU_det}.  If the matrix is large or the magnitude of
+the elements is large, the determinant may overflow the data type.  In
+these cases, the (natural) logarithm of the determinant may be
+calculated instead (as the sum of the logarithms of the diagonal
+elements).  In this case, the GSL function \code{gsl_linalg_LU_lndet}
+shall be used.
+
+\subsubsection{Solving a Linear Equation}
+
+The LU decomposition of a matrix may be used to solve the
+matrix equation $\sum_{j=1}^{N} A_{i,j} \times x_j = B_j $
+
+Given the LU decomposition of a matrix into $\alpha_{ij}$ and
+$\beta_{ij}$, the technique of back-substitution is used to solve the
+equation above. First solve the equation $L y = B$:
+\begin{eqnarray}
+y_1 & = & \frac{b_1}{\alpha_{11}} \\
+y_i & = & \frac{1}{\alpha_{ii}}\left(b_i - \sum_{j=1}^{i-1} \alpha_{ij} y_i\right)
+\end{eqnarray}
+
+The values of $y$ may be used to solve for $x_i$ using the
+relationship $U x = b$:
+\begin{eqnarray}
+x_N & = & \frac{y_N}{\beta_{NN}} \\
+x_i & = & \frac{1}{\beta_{ii}}\left(y_i - \sum_{j=i+1}^N \beta_{ij} y_ij\right)
+\end{eqnarray}
+
+\subsubsection{Invert a matrix}
+
+Inversion of a matrix using the LU decomposition is performed by
+performing back-subsitution to solve a series of linear equations of
+the form $\sum_{j=1}^{N} A_{i,j} \times x_j = B_j $ where the values
+of $B_j$ represent the columns of the identity matrix.  The solution
+vectors $x_j$ represent the columns of the inverse matrix.  This
+operation shall be implemented using the GSL function \code{gsl_linalg_LU_invert}.
+
+\subsubsection{Perform matrix addition, subtraction and multiplication}
+
+Matrix binary arithmetic operations differ from image binary
+arithmetic operations in a fundamental way: For image operations, the
+resulting pixel value is determined by performing the operation on the
+two corresponding input operand pixels.  For matrix operations, the
+resulting element value results from the operation on the
+corresponding pair of row and colum from the input matrices.  So, for
+example, given the input matrices $\alpha_{ij}$ and $\beta_{ij}$, the
+image and matrix multiplications correspond to:
+%
+\begin{eqnarray}
+\mbox{image}_{ij} & = & \alpha_{ij} \times \beta_{ij} \\
+\mbox{matrix}_{jk} = \sum_{i=1}^N \alpha_{ij} \times \beta_{ki}
+\end{eqnarray}
+%
+The matrix and image operations for addition and subtraction are
+identical: the operation is performed on the corresponding elements in
+each matrix.  Matrix division is not defined (to divide, the user
+should first invert the matrix, and then multiply).  The matrix math
+function \code{psMatrixOp} shall implement the matrix version of the
+binary arithmetical operations for the following operators: $+, -,
+\times$.
+
+\subsubsection{Transpose a matrix}
+
+The transpose of a matrix is simply the reorganization of the matrix
+elements by 'flipping' the matrix along a diagonal.  For non-square
+matrices with dimensions $N \times M$, the resulting matrix has
+dimensions $M \times N$.  The element of the output matrix $T_{ij}$ is
+given by
+%
+\begin{equation}
+T_{ij} = M_{ji}
+\end{equation}
+where $M_{ij}$ is the matrix to be transposed.
+
+\subsubsection{Convert a matrix to a vector}
+
+Matrix-to-vector conversion is only defined for a matrix that has a
+size of one in at least one dimension.  In that case, the elements
+should be extracted, and placed in a vector, with care that the
+\code{psType} is defined correctly --- a $1\times N$ matrix is
+converted to a \code{PS_DIMEN_VECTOR}-type vector, while a $N\times 1$
+matrix is converted to a \code{PS_DIMEN_TRANV}-type vector.
+
+\subsection{(Fast) Fourier Transforms}
+
+(Fast) Fourier Transforms (FFTs) shall be implemented using the
+\href{www.fftw.org}{{\em Fastest Fourier Transform in the West} (FFTW)
+library}.
+
+\subsubsection{FFTW Plans}
+
+FFTW requires the user to create a ``plan'' for each transform size,
+the time to create which is a function of the desired speed of the
+transform --- faster transforms for a given size (and machine) may be
+performed if more time is spent testing plans.
+
+In the \PS{} IPP, we will want to perform FFTs on images of common
+sizes (e.g.\ $512 \times 512$) regularly.  This means that we would
+gain from determining an FFTW plan for each of these common sizes.
+FFTW provides a binary, \code{fftw-wisdom} which may be used to
+generate and save ``wisdom''.  The location of the \code{wisdom} file
+will be specified as a configuration variable for the IPP (defaulting
+to \code{/etc/fftw/wisdom}).  The \code{wisdom} should be read in upon
+initialisation of the PSLib FFT functions and saved at the conclusion.
+
+\subsubsection{Function mapping}
+
+The forward and reverse transforms call the corresponding
+FFTW function to plan the transform:
+
+\begin{tabular}{ll}
+  PSLib function        & Major FFTW call \\ \hline
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+  \code{psFFTForward()} & \code{fftw_plan_dft_r2c_2d()} \\
+  \code{psFFTReverse()} & \code{fftw_plan_dft_c2r_2d()} \\
+\end{tabular}
+
+These plans should be formulated using the \code{FFTW_ESTIMATE} flag,
+which will allow FFTW to default to a minimal planning time if the
+wisdom has not been loaded.  Transforms should be performed out of
+place to avoid the need to pad the input array to hold the output.
+
+\subsubsection{More Complicated Functions}
+
+\code{psFFTCrossCorrelate()} and \code{psFFTConvolve()} both involve
+multiplication of two Fourier transforms.  In the former, the first
+Fourier transform is multiplied by the complex conjugate of the second
+Fourier transform to yield the Fourier transform of the
+cross-correlation (NR \S 13.2).  In the latter, the two Fourier
+transforms are multiplied directly to yield the Fourier transform of
+the convolution (NR \S 13.1).
+
+If the elements of the discrete Fourier transform are $C_k$, then the
+the elements of the power spectrum are (NR \S 13.4):
+\begin{eqnarray}
+P_0     & = & \left| C_0 \right|^2 / N^2 \\
+P_j     & = & \left( \left| C_j \right|^2 + \left| C_{N-j} \right|^2 \right)/ N^2 \\
+P_{N/2} & = & \left| C_{N/2} \right|^2 / N^2
+\end{eqnarray}
+where $j = 1, 2, \ldots, (N/2 - 1)$.
+
+Note that we leave the issue of ``windowing'' the data up to the
+caller, and choose to normalise by $1/N^2$.
+
+\subsection{Ellipse Representations}
+
+Images of astronomical objects may often be represented using a model
+consisting of a radial profile combined with an elliptical contour.
+Two common ways to measure such a shape are to fit a model to the
+light distribution or to measure second-order moments, perhaps with
+some weighting profile.  In the special case of a 2D Gaussian with an
+elliptical contour, these representations are equivalent.  The
+following discussion shows how to relate the fitted parameters and
+second-order moments of a elliptical Gaussian of an arbitrary
+orientation with the parameters of an unrotate elliptical Gaussian.
+
+Consider a 2D Gaussian with an elliptical contour.  If the ellipse is
+oriented with the major axis along the x-axis, then the formula for
+such a Gaussian may be written $f = exp(-z)$ where
+\begin{equation}
+\label{aligned-ellipse}
+z = \frac{x^2}{2\sigma_a^2} + \frac{y^2}{2\sigma_b^2}
+\end{equation}
+with $\sigma_a$ the semi-major axis and $\sigma_b$ the semi-minor axis
+of the 1$\sigma$ contour.  Given such a Gaussian, we may measure its
+second moments by integration, and find that the second moment tensor
+is
+\begin{equation} \left| \begin{array}{cc}
+\sigma_a^2 & 0 \\
+0 & \sigma_b^2 \\
+\end{array} \right| \end{equation}
+
+Now consider the same ellipse rotated to an arbitrary angle $\theta$.
+The formula for such a Gaussian may be written $f = exp(-z)$ where
+\begin{equation}
+\label{rotated-ellipse}
+z = \frac{x^2}{2\sigma_x^2} + \frac{y^2}{2\sigma_y^2} + \sigma_{xy}xy
+\end{equation}
+Note that, in the above form of the equation, $\sigma_{xy}$ goes to 0
+as the ellipse is rotated to be aligned with the x (or y) axis.  Thus,
+in this representation, $\sigma_{xy}$ is well behaved, but does not
+have the same units of length that $\sigma_x$ or $\sigma_y$ have.  Our
+goal is to determine the relationships between the rotated and
+unrotated components of the Gaussian formula as well as the second
+moments.
+
+To determine the behavior of $\sigma_x$, etc, under rotation, we start
+with the aligned ellipse (\ref{aligned-ellipse}) and rotate the
+coordinate frame by an angle $-\theta$:
+\begin{equation} 
+\left( \begin{array}{c} x^\prime \\ y^\prime \end{array} \right) =
+\left| \begin{array}{cc} \cos \theta & -\sin \theta \\ 
+                         \sin \theta & \cos \theta 
+\end{array} \right|
+\left( \begin{array}{c} x \\ y \end{array} \right)
+\end{equation}
+where $x^\prime$ and $y^\prime$ are the coordinates for the unrotated
+(aligned) ellipse.  Applying this rotation to (\ref{aligned-ellipse}) yields:
+\begin{equation} 
+z = \frac{x^2 \cos^2 \theta + y^2 \sin^2 \theta - 2 x y \sin \theta \cos \theta}{2\sigma_a^2} +
+    \frac{x^2 \sin^2 \theta + y^2 \cos^2 \theta + 2 x y \sin \theta \cos \theta}{2\sigma_b^2} 
+\end{equation}
+Grouping these terms together, we find:
+\begin{equation} 
+z = \frac{x^2}{2}(\sigma_a^{-2} \cos^2 \theta + \sigma_b^{-2}\sin^2 \theta) + 
+    \frac{y^2}{2}(\sigma_b^{-2} \cos^2 \theta + \sigma_a^{-2}\sin^2 \theta) + 
+    \frac{xy}{2} \sin (2 \theta) (\sigma_b^{-2} - \sigma_a^{-2})
+\end{equation}
+We then associate the components of this equation with those of (\ref{rotated-ellipse}) and find:
+\begin{eqnarray}
+\sigma_x^{-2} & = & \sigma_a^{-2} \cos^2 \theta + \sigma_b^{-2}\sin^2 \theta \\
+\sigma_y^{-2} & = & \sigma_b^{-2} \cos^2 \theta + \sigma_a^{-2}\sin^2 \theta \\
+\sigma_{xy}   & = & \frac{1}{2} \sin (2 \theta) (\sigma_b^{-2} - \sigma_a^{-2})
+\end{eqnarray}
+Replacing $\cos^2$ and $\sin^2$ with the double-angle relationships, we find:
+\begin{eqnarray}
+\sigma_x^{-2} & = & \frac{1}{2}(\sigma_a^{-2} + \sigma_b^{-2}) - \frac{1}{2}(\sigma_b^{-2} - \sigma_a^{-2}) \cos (2 \theta) \\
+\sigma_y^{-2} & = & \frac{1}{2}(\sigma_a^{-2} + \sigma_b^{-2}) + \frac{1}{2}(\sigma_b^{-2} - \sigma_a^{-2}) \cos (2 \theta) \\
+\sigma_{xy}   & = & \frac{1}{2} \sin (2 \theta) (\sigma_b^{-2} - \sigma_a^{-2})
+\end{eqnarray}
+These formulae thus define the values of $\sigma_x$, $\sigma_y$, and
+$\sigma_{xy}$ given $\sigma_a$, $\sigma_b$, and $\theta$.  Note that
+in this equation and the one above, we represent the quantities in
+terms of $\sigma_b^{-2} - \sigma_a^{-2}$ which is always greater than
+0, thus attributing the sign of the equation to the $\sin$ or $\cos$
+term.  This is necessary to determine the angle in the proper quadrant
+using the arctangent below.
+
+With the above relationships, we may now form combinations that help
+us to solve for $\sigma_a$, $\sigma_b$, and $\theta$:
+\begin{eqnarray}
+f_1 = \sigma_y^{-2} + \sigma_x^{-2} & = & \sigma_b^{-2} + \sigma_a^{-2} \\
+f_2 = \sigma_y^{-2} - \sigma_x^{-2} & = & (\sigma_b^{-2} - \sigma_a^{-2}) \cos (2 \theta) \\
+f_3 = \sqrt{f_2^2 + 4\sigma_{xy}^2} & = & \sigma_b^{-2} - \sigma_a^{-2}
+\end{eqnarray}
+From these, we may derive the equations for $\sigma_a$, $\sigma_b$, and $\theta$:
+\begin{eqnarray}
+\theta & = & \frac{1}{2} \arg (2 \sigma_{xy}, f_2) \\
+\sigma_a & = & \sqrt{\frac{2}{f_1 - f_3}} \\
+\sigma_b & = & \sqrt{\frac{2}{f_1 + f_3}}
+\end{eqnarray}
+
+The relationship between the rotated ($m_{i,j}$) and unrotated
+($M_{i,j}$) second moments, the latter being equal to $\sigma_a^2$ and
+$\sigma_b^2$, is derived in a similar fashion.  We start with the
+point that the second moment is rotated as a tensor:
+\begin{equation} 
+\left| 
+\begin{array}{cc}
+m_{x,x} & m_{x,y} \\
+m_{y,x} & m_{y,y} \\
+\end{array} \right| 
+=
+\left| 
+\begin{array}{cc}
++\cos \theta & -\sin \theta \\
++\sin \theta & +\cos \theta \\
+\end{array} \right| 
+\left| 
+\begin{array}{cc}
+M_{x,x} & 0 \\
+0       & M_{y,y} \\
+\end{array} \right| 
+\left| 
+\begin{array}{cc}
++\cos \theta & +\sin \theta \\
+-\sin \theta & +\cos \theta \\
+\end{array} \right| 
+\end{equation}
+Multiplying this out and substituting $\sigma_a^2$, $\sigma_b^2$ for $M_{x,x}$, $M_{y,y}$, we find:
+\begin{eqnarray}
+m_{x,x} & = & \sigma_a^{2} \cos^2 \theta + \sigma_b^{2}\sin^2 \theta \\
+m_{y,y} & = & \sigma_b^{2} \cos^2 \theta + \sigma_a^{2}\sin^2 \theta \\
+m_{x,y} & = & \frac{1}{2} \sin (2 \theta) (\sigma_a^2 - \sigma_b^2)
+\end{eqnarray}
+Using the double-angle relationships, these become:
+\begin{eqnarray}
+m_{x,x} & = & \frac{1}{2}(\sigma_a^{2} + \sigma_b^{2}) + \frac{1}{2}(\sigma_a^{2} - \sigma_b^{2}) \cos (2 \theta) \\
+m_{y,y} & = & \frac{1}{2}(\sigma_a^{2} + \sigma_b^{2}) - \frac{1}{2}(\sigma_a^{2} - \sigma_b^{2}) \cos (2 \theta) \\
+m_{x,y} & = & \frac{1}{2} \sin (2 \theta) (\sigma_a^{2} - \sigma_b^{2})
+\end{eqnarray}
+These three formulae define the second moments in terms of $\sigma_a$, $\sigma_b$, and $\theta$. 
+
+We define equivalent intermediate products to the above:
+\begin{eqnarray}
+g_1 = m_{x,x} + m_{y,y}   	 & = & \sigma_a^{2} + \sigma_b^{2} \\
+g_2 = m_{x,x} - m_{y,y}   	 & = & (\sigma_a^{2} - \sigma_b^{2}) \cos (2 \theta) \\
+g_3 = \sqrt{g_2^2 + 4 m_{x,y}^2} & = & \sigma_a^{2} - \sigma_b^{2}
+\end{eqnarray}
+From these, we may derive the equations for $\sigma_a$, $\sigma_b$, and $\theta$:
+\begin{eqnarray}
+\theta   & = & \frac{1}{2} \arg (2 m_{x,y}, g_2) \\
+\sigma_a & = & \sqrt{\frac{g_1 + g_3}{2}} \\
+\sigma_b & = & \sqrt{\frac{g_1 - g_3}{2}}
+\end{eqnarray}
+
+\section{PSLib Astronomy Utilities}
+
+\subsection{Time}
+
+Correct time representation is \emph{critical} in astronomical software.  PSLib
+uses the \code{psTime} structure to represent time values.  This structure
+represents a time which consists of seconds and nanoseconds in a time
+system defined by the \code{psTimeType} element \code{type}.  All available
+time-systems are defined in terms of the reference epoch
+``1970-01-01T00:00:00Z'' (Gregorian\footnote{Gregorian Calendar -
+http://en.wikipedia.org/wiki/Gregorian\_calendar}), but with minor
+modifications, as needed, for features such as leap-seconds.  The first
+represenatation, TAI (International Atomic Time), has seconds of uniform length
+(SI seconds) and no leap-seconds.  The exact zero reference is
+``1970-01-01T00:00:10Z'' UTC.  The second representation is UTC, which has
+seconds of uniform length and leap-seconds as needed to adjust it to remain
+within $0.9s$ of the Earth's rotation.  It has a zero-point of exactly
+``1970-01-01T00:00:00Z'' UTC.
+
+\subsubsection{Coordinated Universal Time (UTC)}
+
+Coordinated Univeral Time (UTC) is defined by the International
+Telecommunication Union (ITU)\footnote{ITU website -
+http://www.itu.int/home/index.html}.  It is a system of time with SI length
+seconds but attempts to stay within $1s$ of UT1.  This is done by the insertion
+of a ``leap-second'' whenever $\lvert UTC-UT1 \rvert \ge 0.9s$.  By
+definition\footnote{UTC definition -
+http://www.cl.cam.ac.uk/~mgk25/volatile/ITU-R-TF.460-4.pdf}, $UTC-TAI$ is an
+integer number of seconds.  UTC went into effect on ``1972-01-01T00:00:00Z''
+and is defined as being $TAI-UTC = 10s$ on that date.  For dates prior to
+``1972-01-01'' a fixed offset of 10s relative to TAI will be assumed.
+
+\begin{equation}
+{\rm UTC} = {\rm TAI} - 10{\rm s} - {\rm leapseconds}
+\end{equation}
+
+Leap-seconds are declared by the International Earth Rotation and Reference
+Systems Service (IERS)\footnote{IERS website - http://www.iers.org/}.
+Leap-seconds only occur in the UTC time system and cannot be accurately
+predicted due to variations in the Earth's rotational period.  To determine the
+number of leap-second in a given UTC date a table of leap-seconds as annouced by
+the IERS must be consulted.  This table will have to be updated each time a new
+leap-second occurs.
+
+For ease of conversion, UTC should be represented as the number of
+seconds since the UNIX epoch of ``1970-01-01T00:00:00Z'',
+non-inclusive of leap-seconds.  \tbd{what does this statement actually
+mean? what is the source of time (gettimeofday?  let's be explicit
+here}
+
+\tbd{Times will always be expressed in the 'UTC timezone'.  Use of the
+local timezone is forbidden.  -- this makes no sense given that we
+define LST.  In any case, this statement, or somethign equivalent,
+belongs in the SDRS not the ADD}
+
+\subsubsection{International Atomic Time (TAI)}
+
+International Atomic Time or Temps Atomique International (TAI) is a system of
+time defined by the Bureau International des Poids et Mesures
+(BIPM)\footnote{BIPM website - http://www.bipm.fr/} with SI length seconds as
+measured at sea level.  To convert from UTC to TAI add the base delta of $10s$
+and all of the accumulated leap-seconds since ``1972-01-01'' up until the UTC
+date being converted.
+
+\begin{equation}
+{\rm TAI} = {\rm UTC} + 10{\rm s} + {\rm leapseconds}
+\end{equation}
+
+For ease of conversion, TAI should be represented as the number of
+seconds since the UNIX epoch of ``1970-01-01T00:00:00Z''. \tbd{what
+does this statement actually mean?}
+
+\subsubsection{Leap-seconds}
+
+Leap seconds keep UTC within 0.9s of UT1.  The offset between TAI and
+UTC must be looked up from tables.  Jumps in the offset correspond to
+leap seconds.
+
+\begin{verbatim}
+ 1972 JUL  1 =JD 2441499.5  TAI-UTC=  11.0       S + (MJD - 41317.) X 0.0 S
+ 1973 JAN  1 =JD 2441683.5  TAI-UTC=  12.0       S + (MJD - 41317.) X 0.0 S
+ 1974 JAN  1 =JD 2442048.5  TAI-UTC=  13.0       S + (MJD - 41317.) X 0.0 S
+ 1975 JAN  1 =JD 2442413.5  TAI-UTC=  14.0       S + (MJD - 41317.) X 0.0 S
+ 1976 JAN  1 =JD 2442778.5  TAI-UTC=  15.0       S + (MJD - 41317.) X 0.0 S
+ 1977 JAN  1 =JD 2443144.5  TAI-UTC=  16.0       S + (MJD - 41317.) X 0.0 S
+ 1978 JAN  1 =JD 2443509.5  TAI-UTC=  17.0       S + (MJD - 41317.) X 0.0 S
+ 1979 JAN  1 =JD 2443874.5  TAI-UTC=  18.0       S + (MJD - 41317.) X 0.0 S
+ 1980 JAN  1 =JD 2444239.5  TAI-UTC=  19.0       S + (MJD - 41317.) X 0.0 S
+ 1981 JUL  1 =JD 2444786.5  TAI-UTC=  20.0       S + (MJD - 41317.) X 0.0 S
+ 1982 JUL  1 =JD 2445151.5  TAI-UTC=  21.0       S + (MJD - 41317.) X 0.0 S
+ 1983 JUL  1 =JD 2445516.5  TAI-UTC=  22.0       S + (MJD - 41317.) X 0.0 S
+ 1985 JUL  1 =JD 2446247.5  TAI-UTC=  23.0       S + (MJD - 41317.) X 0.0 S
+ 1988 JAN  1 =JD 2447161.5  TAI-UTC=  24.0       S + (MJD - 41317.) X 0.0 S
+ 1990 JAN  1 =JD 2447892.5  TAI-UTC=  25.0       S + (MJD - 41317.) X 0.0 S
+ 1991 JAN  1 =JD 2448257.5  TAI-UTC=  26.0       S + (MJD - 41317.) X 0.0 S
+ 1992 JUL  1 =JD 2448804.5  TAI-UTC=  27.0       S + (MJD - 41317.) X 0.0 S
+ 1993 JUL  1 =JD 2449169.5  TAI-UTC=  28.0       S + (MJD - 41317.) X 0.0 S
+ 1994 JUL  1 =JD 2449534.5  TAI-UTC=  29.0       S + (MJD - 41317.) X 0.0 S
+ 1996 JAN  1 =JD 2450083.5  TAI-UTC=  30.0       S + (MJD - 41317.) X 0.0 S
+ 1997 JUL  1 =JD 2450630.5  TAI-UTC=  31.0       S + (MJD - 41317.) X 0.0 S
+ 1999 JAN  1 =JD 2451179.5  TAI-UTC=  32.0       S + (MJD - 41317.) X 0.0 S
+\end{verbatim}
+
+For the present time, it should be assumed that this table resides on
+local disk in a known location (i.e., there is no need that it is
+downloaded from the internet by PSLib).  Later, the location of this
+file will be made configurable. This data is available from
+USNO\footnote{ftp://maia.usno.navy.mil/ser7/tai-utc.dat}.
+
+\subsubsection{Universal Time (UT1)}
+
+UT1 is directly tied to the rotation of the Earth.  Historically, time
+has been measured with respect to the rising and setting of the
+Sun. However, in the modern era of atomic clocks, the rotation of the
+Earth makes for a highly unstable time standard. Tidal effects,
+changes in the angular momentum of the atmosphere, seasonal changes in
+the polar ice caps, movement within the Earth's core, and other
+effects all cause measurable changes in the Earth's rotation on a
+daily basis.  However, UT1 is still vitally important for determining
+the orientation of the Earth with respect to the sky.  UT1 is
+calculated by applying the value UT1-UTC to the value of UTC.  UT1 is
+continuously measured by the International Earth Rotation Service, and
+tabulated values of the offset of UT1 from UTC are published at
+regular intervals, along with predicted future values.  The process of
+calculating the UT1-UTC offsets are discussed in the section on Earth
+Orientation (Section~\ref{sec:ut1}).
+
+\subsubsection{Gregorian dates to seconds}
+
+The Perl code below, based on an algorithm described in the book ``Calendrical
+Calculations''\footnote{Calendrical Calculations -
+http://emr.cs.iit.edu/home/reingold/calendar-book/second-edition/} and modified
+to return seconds, converts from Gregorian-formatted dates to seconds since the
+UNIX epoch.
+
+Given year, month, day as \code{$y, $m, $d}.
+\begin{verbatim}
+    use integer;
+
+    my $adj;
+
+    # make month in range 3..14 (treat Jan & Feb as months 13..14 of
+    # prev year)
+    if ( $m <= 2 )
+    {
+        $y -= ( $adj = ( 14 - $m ) / 12 );
+        $m += 12 * $adj;
+    }
+    elsif ( $m > 14 )
+    {
+        $y += ( $adj = ( $m - 3 ) / 12 );
+        $m -= 12 * $adj;
+    }
+
+    # make year positive (oh, for a use integer 'sane_div'!)
+    if ( $y < 0 )
+    {
+        $d -= 146097 * ( $adj = ( 399 - $y ) / 400 );
+        $y += 400 * $adj;
+    }
+
+    # add: day of month, days of previous 0-11 month period that began
+    # w/March, days of previous 0-399 year period that began w/March
+    # of a 400-multiple year), days of any 400-year periods before
+    # that, and 306 days to adjust from Mar 1, year 0-relative to Jan
+    # 1, year 1-relative (whew)
+
+    $d += ( $m * 367 - 1094 ) / 12 + $y % 100 * 1461 / 4 +
+          ( $y / 100 * 36524 + $y / 400 ) - 306;
+
+    # convert from count of days to seconds since the UNIX epoch
+    $unix = ( ( $d - 1 ) * 86400 ) - 62135596800;
+    $utc = $unix - leapseconds($unix);
+\end{verbatim}
+Outputs seconds as \code{$utc}.
+
+To go the other way:
+
+Given the number of seconds since the UNIX epoch as \code{$utc}.
+\begin{verbatim}
+    use integer;
+
+    my $unix = $utc + leapseconds( $utc )
+    $d = ( unix + 62135596800 ) / 86400
+ 
+    my $rd = $d;
+
+    my $yadj = 0;
+    my ( $c, $y, $m );
+
+    # add 306 days to make relative to Mar 1, 0; also adjust $d to be
+    # within a range (1..2**28-1) where our calculations will work
+    # with 32bit ints
+    if ( $d > 2**28 - 307 )
+    {
+        # avoid overflow if $d close to maxint
+        $yadj = ( $d - 146097 + 306 ) / 146097 + 1;
+        $d -= $yadj * 146097 - 306;
+    }
+    elsif ( ( $d += 306 ) <= 0 )
+    {
+        $yadj =
+          -( -$d / 146097 + 1 );    # avoid ambiguity in C division of negatives        $d -= $yadj * 146097;
+    }
+
+    $c = ( $d * 4 - 1 ) / 146097;   # calc # of centuries $d is after 29 Feb of yr 0
+    $d -= $c * 146097 / 4;          # (4 centuries = 146097 days)
+    $y = ( $d * 4 - 1 ) / 1461;     # calc number of years into the century,
+    $d -= $y * 1461 / 4;            # again March-based (4 yrs =~ 146[01] days)
+    $m = ( $d * 12 + 1093 ) / 367;  # get the month (3..14 represent March through
+    $d -= ( $m * 367 - 1094 ) / 12; # February of following year)
+    $y += $c * 100 + $yadj * 400;   # get the real year, which is off by
+    ++$y, $m -= 12 if $m > 12;      # one if month is January or February
+
+    if ( $_[0] )
+    {
+        my $dow;
+
+        if ( $rd < -6 )
+        {
+            $dow = ( $rd + 6 ) % 7;
+            $dow += $dow ? 8 : 1;
+        }
+        else
+        {
+            $dow = ( ( $rd + 6 ) % 7 ) + 1;
+        }
+
+        my $doy =
+            $class->_end_of_last_month_day_of_year( $y, $m );
+
+        $doy += $d;
+
+        my $quarter;
+        {
+            no integer;
+            $quarter = int( ( 1 / 3.1 ) * $m ) + 1;
+        }
+
+        my $qm = ( 3 * $quarter ) - 2;
+
+        my $doq =
+            ( $doy -
+              $class->_end_of_last_month_day_of_year( $y, $qm )
+            );
+\end{verbatim}
+Outputs year, month, day as \code{$y, $m, $d}.
+%$
+
+\emph{The above code was taken [and slightly altered] from
+\code{DateTime.pm}\footnote{DateTime.pm -
+http://search.cpan.org/~drolsky/DateTime/} (C)  2003 Dave Rolsky.
+Please see the DateTime project website\footnote{DateTime project -
+http://datetime.perl.org} for further details.}
+
+
+\subsubsection{Julian Date and Modified Julian Date}
+
+The follow definitions of Julian Date (JD) and Modified Julian Date (MJD) was
+taken from, ``RESOLUTION B1: ON THE USE OF JULIAN DATES'' of ``The XXIIIrd
+International Astronomical Union General Assembly''\footnote{RESOLUTION B1: ON
+THE USE OF JULIAN DATES -
+http://www.iers.org/iers/earth/resolutions/UAI\_b1.html}.
+
+\paragraph{Julian Date}
+
+\begin{verbatim}
+1. Julian day number (JDN)
+
+The Julian day number associated with the solar day is the number assigned to a
+day in a continuous count of days beginning with the Julian day number 0
+assigned to the day starting at Greenwich mean noon on 1 January 4713 BC,
+Julian proleptic calendar -4712.
+
+2. Julian Date (JD)
+
+The Julian Date (JD) of any instant is the Julian day number for the preceding
+noon plus the fraction of the day since that instant. A Julian Date begins at
+12h 0m 0s and is composed of 86400 seconds. To determine time intervals in a
+uniform time system it is necessary to express the JD in a uniform time scale.
+For that purpose it is recommended that JD be specified as SI seconds in
+Terrestrial Time (TT) where the length of day is 86,400 SI seconds.
+
+In some cases it may be necessary to specify Julian Date using a different time
+scale. (See Seidelmann, 1992, for an explanation of the various time scales in
+use). The time scale used should be indicated when required such as JD(UT1). It
+should be noted that time intervals calculated from differences of Julian Dates
+specified in non-uniform time scales, such as UTC, may need to be corrected for
+changes in time scales (e.g. leap seconds).
+\end{verbatim}
+
+\paragraph{Modified Julian Date}
+
+\begin{verbatim}
+"that for those cases where it is convenient to employ a day beginning at
+midnight, the Modified Julian Date (equivalent to the Julian Date minus 2 400
+000.5) be used"
+\end{verbatim}
+
+\paragraph{JD and MJD conversion}
+
+Conversion between \code{psTime} values and MJD and JD are determined
+from:
+
+Where \code{psTime} is a \code{PS_TIME_TAI}.
+\begin{verbatim}
+mjd = psTime.sec/86400.0 + psTime.nsec/86400000000000.0 + 40587.0;
+ jd = psTime.sec/86400.0 + psTime.nsec/86400000000000.0 + 2440587.5;
+\end{verbatim}
+
+For reference $2451545.0$ JD $= 51544.5$ MJD is equivalent to
+``2000-01-01T00:00:00Z''.
+
+\begin{equation}
+{\rm JD} = {\rm MJD} + 2400000.5
+\end{equation}
+
+\subsubsection{Terrestrial Time (TT)}
+
+Terrestrial Time (TT) is defined as a fixed offset from TAI.
+
+\begin{equation}
+{\rm TT} = {\rm TAI} + 32.184{\rm s}
+\end{equation}
+
+\subsubsection{TT as Julian Centuries since J2000.0}
+
+The algorithm for calulating GMST requires TT formated in Julian centruies
+since the J2000.0 epoch.
+
+\begin{equation} t_u = \frac{{\rm JD}_{\rm TT} - 2451545.0}{36525}
+\end{equation}
+
+\subsubsection{UT1 as Julian Centuries since J2000.0}
+
+The algorithm for calulating GMST requires UT1 be formated in Julian centuries
+since the J2000.0 epoch.
+
+\begin{equation}
+t = \frac{{\rm JD}_{\rm UT1} - 2451545.0}{36525}
+\end{equation}
+
+\subsubsection{Local Mean Sidereal Time (LMST)}
+
+Local Mean Sidereal Time (LMST) is Greenwich Mean Sideral Time (GMST) plus the
+observer's location in East longitude. Calculating LMST requires the input of
+Universal Time (UT1), Terrestrial Dynamical Time (TT) and a longitude (measured
+East of Greenwich).
+
+\begin{equation}
+LMST = GMST00(t_u, t) + longitude
+\end{equation}
+
+Gives $LMST$ in seconds.
+
+\subsubsection{Greenwich Mean Sidereal Time (GMST)}
+
+Greenwich Mean Sidereal Time (GMST) is caclulated from UT1 and TT.  This
+algorithm requires that both time inputs are expressed as Julian centuries
+since J2000.0 \footnote{Expressions to implement the IAU 2000 definition of UT1
+- http://www.edpsciences.org/articles//aa/abs/2003/30/aa3487/aa3487.html}.
+
+Here $t_u$ is UT1 expressed in Julian centuries since J2000.0, and $t$
+is TT expressed in Julian centuries since J2000.0.
+
+\begin{eqnarray}
+{\rm GMST00}(t_u, t) & = & UT1 + 24110.5493771\\
+& & + 8639877.3173760\, t_u + 307.4771600\, t\\
+& & + 0.0931118\, t^2 - 0.0000062\, t^3\\
+& & + 0.0000013\, t^4
+\end{eqnarray}
+
+Gives $GMST00$ in seconds.
+
+\subsection{2D transformations}
+
+In PSLib, we implement 2-dimensional transformations using
+\code{psPlaneTransform}, which contains a matrix of polynomial
+coefficients for each dimension.  Since we are using these to model
+the real world, where, for example, a particular point on the detector
+maps to a particular point on the sky, we consider only
+transformations that are ``one-to-one''.  This makes it possible to
+speak of inverse transformations, and of combining multiple
+transformations.
+
+Given a transformation, $f(x,y)$, the inverse transformation,
+$g(x,y)$, is that for which $g(f(x,y)) = (x,y)$ for $(x,y)$ over the
+range of interest (not necessarily the entire set of real numbers).
+
+Given two transformations, $f(x,y)$ and $g(x,y)$, the combined
+transformation is the transformation, $h(x,y) = g(f(x,y))$ for $(x,y)$
+over the range of interest (not necessarily the entire set of real
+numbers).
+
+Both of these operations are straightforward if the transformation is
+linear.  If the function $(u,v) = f(x,y)$ is:
+\begin{eqnarray}
+u & = & a + bx + cy \\
+v & = & d + ex + fy
+\end{eqnarray}
+then the inverse transformation $(x,y) = g(u,v)$ is:
+\begin{eqnarray}
+x & = & (-fa+cd)/\Delta + fu/\Delta - cv/\Delta \\
+y & = & (ae-bd)/\Delta - eu/\Delta + bv/\Delta
+\end{eqnarray}
+where $\Delta = bf - ce$ is the matrix determinant.  Given two
+functions $f_i(x,y)$ for $i=1,2$:
+\begin{eqnarray}
+u & = & a_i + b_i x + c_i y \\
+v & = & d_i + e_i x + f_i y
+\end{eqnarray}
+then the combined transformation, $(u,v) = f_2(f_1(x,y))$ is:
+\begin{eqnarray}
+u & = & (a_2 + b_2 a_1 + c_2 d_1) + (b_2 b_1 + c_2 e_1) x + (b_2 c_1 + c_2 f_1) y \\
+v & = & (d_2 + e_2 a_1 + f_2 d_1) + (e_2 b_1 + f_2 e_1) x + (e_2 c_1 + f_2 f_1) y
+\end{eqnarray}
+
+When the transformations are not linear, the inverse and combined
+transformations can be estimated by sampling a grid over the region of
+interest, calculating the transformation (or double transformation)
+for each sample, and using this information to derive the best fit
+transformation that produces the inverse or combined transformation.
+The inverse transformation should be of the same order as that of the
+forward transformation, while the combined transformation should be of
+the higher order of the two component transformations.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Spherical Rotations}
+
+Spherical rotations may be implemented with a variety of mathematical
+methods.  A single rotation may be specified by three angles which
+describe rotations about the principal axes.  Figure~\ref{rotations}
+shows the rotation angles for an arbitrary 3-D rotation.  A single
+rotation may be represented by defining this set of Euler angles.  As
+an alternative, the quaternion is a convenient construct with which to
+represent a single rotation, and it has useful mathematical properties
+when applying those rotations.
+
+\begin{figure}
+\begin{center}
+\psfig{file=pics/rotations}
+\caption{Definition of the rotation angles\label{rotations}.  Three
+rotations are performed in series: first, a rotation of $\alpha_p$ is
+made about the $z$ axis; second, a rotation of $\delta_p$ (not $90 -
+\delta_p$ as shown) is made about the modified $y$ axis, $y'$;
+finally, a rotation of $\phi_p$ is made about the modified $z$ axis,
+$z''$.  Note that it is the coordinate system that rotates, not the
+position of interest.}
+\end{center}
+\end{figure}
+
+
+\subsubsection{Quaternions}
+
+A quaternion is an ordered set of four numbers, $q = (q_0, q_1, q_2,
+q_3)$, which is useful for specifying rotations.  A quaternion is made
+up of a three-vector which specifies an axis about which to rotate,
+and a scalar which specifies the amount of rotation.  In the
+following, we call the final value, $q_3$, the scalar value; note that
+other sources (e.g., MathWorld) may choose to call the first value the
+scalar value.
+
+The conjugate of a quaterion, $q = (q_0, q_1, q_2, q_3)$, is $\bar{q}
+= (-q_0, -q_1, -q_2, q_3)$.  Note that the vector components are
+negated, but not the scalar component.
+
+\subsubsection{Quaternion for a position}
+
+Given an angular position on the sky, $(\alpha, \delta)$, we can
+construct a quaternion by treating it as a unit vector in cartesian
+space:
+\begin{eqnarray}
+p_0 & = & \cos \delta \cos \alpha \\
+p_1 & = & \cos \delta \sin \alpha \\
+p_2 & = & \sin \delta
+\end{eqnarray}
+and we set the scalar value to zero, $p_3 = 0$.
+
+Given a quaternion, $p$, we can calculate the position using the
+inverse of the above equations:
+\begin{eqnarray}
+\phi & = & \arctan(p_1, p_0) \\
+\theta & = & \arcsin(p_2)
+\end{eqnarray}
+where $\phi$ is the longitude and $\theta$ is the latitude.  Note that
+in this case, we neglect the scalar component of the quaternion --- it
+should be zero --- and that we implicitly assume that the quaternion
+is normalised to unity.
+
+\subsubsection{Quaternion for a rotation}
+
+A rotation of angle $\theta$ about the axis defined by the unit vector
+$(v_x, v_y, v_z)$ is specified by a quaternion with components:
+\begin{eqnarray}
+r_0 & = & v_x \sin(\theta/2) \\
+r_1 & = & v_y \sin(\theta/2) \\
+r_2 & = & v_z \sin(\theta/2) \\
+r_3 & = & \cos(\theta/2)
+\end{eqnarray}
+Note that the sine and cosine are taken of the half-angle of the
+rotation.  Note also that this implies that the quaternion components
+are normalized such that $|r| \equiv r_0^2 + r_1^2 + r_2^2 + r_3^2
+= 1$.
+
+\subsubsection{Multiplication of quaternions}
+
+Given two quaternions $a$ and $b$, there is a third quaternion, $p =
+ab$.  The components of $p$ are given by:
+
+\begin{eqnarray}
+p_0 & = &  b_3 a_0 + b_2 a_1 - b_1 a_2 + b_0 a_3 \\
+p_1 & = & -b_2 a_0 + b_3 a_1 + b_0 a_2 + b_1 a_3 \\
+p_2 & = &  b_1 a_0 - b_0 a_1 + b_3 a_2 + b_2 a_3 \\
+p_3 & = & -b_0 a_0 - b_1 a_1 - b_2 a_2 + b_3 a_3
+\end{eqnarray}
+
+Note that quaternion multiplication is associative (whether you do the
+left pair or the right pair first doesn't matter):
+\begin{equation}
+(ab)c = a(bc)
+\end{equation}
+but not commutative (you can't switch the order of the operands):
+\begin{equation}
+abc \ne acb
+\end{equation}
+
+\subsubsection{Rotating a Vector}
+
+Rotation of a position is performed by constructing the quaternion for
+the position, $p$, and the rotation, $r$, according to the above
+equations, and calculating the product:
+\begin{equation}
+q = r p \bar{r}
+\end{equation}
+$q$ is the quaternion of the result.  Note the use of the conjugate of
+the rotation quaternion.
+
+A general rotation may be specified by three individual rotations
+about a predefined set of axes.  We choose to specify rotations around
+the $z$, $y$ and $z$ axes, in that order.  The amount of rotation
+around each of these axes are known as Euler angles.  Given the Euler
+angles of a rotation, the rotation may be performed by rotating in
+turn about the designated axes.  Euler angles are specified below for
+the various rotations required.  To use them, the following rotation
+quaternions are used:
+
+\begin{itemize}
+\item First, about the Z axis:
+\begin{eqnarray}
+r_0 & = & 0 \\
+r_1 & = & 0 \\
+r_2 & = & \sin(\alpha_p/2) \\
+r_3 & = & \cos(\alpha_p/2)
+\end{eqnarray}
+\item Second, about the Y axis:
+\begin{eqnarray}
+s_0 & = & 0 \\
+s_1 & = & \sin(\delta_p/2) \\
+s_2 & = & 0 \\
+s_3 & = & \cos(\delta_p/2)
+\end{eqnarray}
+\item Finally, about the Z axis again:
+\begin{eqnarray}
+t_0 & = & 0 \\
+t_1 & = & 0 \\
+t_2 & = & \sin(\phi_p/2) \\
+t_3 & = & \cos(\phi_p/2)
+\end{eqnarray}
+\end{itemize}
+
+These three quaternions may be multiplied together to yield the
+quaternion of the combined rotation: $tsr$ (note the order --- $r$ is
+done first, so it is nearest the position quaternion, etc.).
+
+\subsubsection{Rotation Matrix}
+
+The rotation matrix representation of a rotation may be derived
+directly from the quaternion representation.  The following formulae
+convert a quaternion to a rotation matrix:
+
+\begin{eqnarray}
+    rot_{x,x} & = &  q_0 q_0 - q_1 q_1 - q_2 q_2 + q_3 q_3 \\
+    rot_{y,y} & = & -q_0 q_0 + q_1 q_1 - q_2 q_2 + q_3 q_3 \\
+    rot_{z,z} & = & -q_0 q_0 - q_1 q_1 + q_2 q_2 + q_3 q_3 \\
+    rot_{x,y} & = & 2 (q_0 q_1 + q_2 q_3) \\
+    rot_{y,x} & = & 2 (q_0 q_1 - q_2 q_3) \\
+    rot_{x,z} & = & 2 (q_0 q_2 - q_1 q_3) \\
+    rot_{z,x} & = & 2 (q_0 q_2 + q_1 q_3) \\
+    rot_{y,z} & = & 2 (q_1 q_2 + q_0 q_3) \\
+    rot_{z,y} & = & 2 (q_1 q_2 - q_0 q_3)
+\end{eqnarray}
+
+\subsubsection{Conversion to Other Representations}
+
+You may convert a rotation matrix, m, to a quaternion, p, with the following
+code:
+
+\begin{verbatim}
+double diag_sum[3];
+int maxi;
+double recip;
+
+diag_sum[0]=1+m[0][0]-m[1][1]-m[2][2];
+diag_sum[1]=1-m[0][0]+m[1][1]-m[2][2];
+diag_sum[2]=1-m[0][0]-m[1][1]+m[2][2];
+diag_sum[3]=1+m[0][0]+m[1][1]+m[2][2];
+
+
+maxi=0;
+for(i=1;i<4;++i) {
+    if(diag_sum[i]>diag_sum[maxi]) maxi=i;
+}
+
+
+p[maxi]=0.5*sqrt(diag_sum[maxi]);
+recip=1./(4.*p[maxi]);
+
+if(maxi==0) {
+    p[1]=recip*(m[0][1]+m[1][0]);
+    p[2]=recip*(m[2][0]+m[0][2]);
+    p[3]=recip*(m[1][2]-m[2][1]);
+
+} else if(maxi==1) {
+    p[0]=recip*(m[0][1]+m[1][0]);
+    p[2]=recip*(m[1][2]+m[2][1]);
+    p[3]=recip*(m[2][0]-m[0][2]);
+
+} else if(maxi==2) {
+    p[0]=recip*(m[2][0]+m[0][2]);
+    p[1]=recip*(m[1][2]+m[2][1]);
+    p[3]=recip*(m[0][1]-m[1][0]);
+
+} else if(maxi==3) {
+    p[0]=recip*(m[1][2]-m[2][1]);
+    p[1]=recip*(m[2][0]-m[0][2]);
+    p[2]=recip*(m[0][1]-m[1][0]);
+}
+\end{verbatim}
+
+\subsection{Celestial Coordinate Conversions}
+
+Changes between spherical coordinate systems (ie, Ecliptic, Galactic,
+and ICRS, or Celestial, coordinates) is equivalent to a rotation in
+3D.  Given two coordinate system, $\alpha,\delta$ and $\phi,\theta$
+which differ by only a rotation, the transformation between these two
+systems are defined by the following three parameters:
+\begin{itemize}
+\item $\alpha_p$ : the longitude of the target system pole in the
+  source system
+\item $\delta_p$ : the latitutde of the target system pole in the
+  source system.  
+\item $\phi_p$ : the longitude of the ascending node in the target system
+\end{itemize}
+Note that $\theta_p$, the latitude of the source system pole in the
+target system, is equal to $\delta_p$ by symmetry.  Transformations
+between arbitrary systems are specified by determining the appropriate
+values of $\alpha_p$, $\delta_p$, and $\phi_p$, and then constructing
+the quarternion for this transformation.
+
+The relevant trigonometric relationships are:
+%
+\begin{eqnarray}
+\sin \theta                        & = & \sin \delta \cos \delta_p - \cos \delta \sin \delta_p \sin (\alpha - \alpha_p) \\
+\cos \theta \sin (\phi - \phi_p)   & = & \cos \delta \cos \delta_p \sin (\alpha - \alpha_p) + \sin \delta \sin \delta_p \\
+\cos \theta \cos (\phi - \phi_p)   & = & \cos \delta \cos (\alpha - \alpha_p)
+\end{eqnarray}
+%
+and for the inverse transformations, the equivalent relationships are:
+%
+\begin{eqnarray}
+\sin \delta                          & = & \sin \theta \cos \delta_p - \cos \theta \sin \delta_p \sin (\phi - \phi_p) \\
+\cos \delta \sin (\alpha - \alpha_p) & = & \cos \theta \cos \delta_p \sin (\phi - \phi_p) + \sin \theta \sin \delta_p \\
+\cos \delta \cos (\alpha - \alpha_p) & = & \cos \theta \cos (\phi - \phi_p)
+\end{eqnarray}
+Since $\theta$ and $\delta$ have domains of $-\pi/2, \pi/2$, the value
+of these angles are found by applying the arcsin to the sine of these
+angles ($\theta = \arcsin \sin \theta$) which is always single-valued
+and defined.  The value of $\alpha-\alpha_p$ may be found from
+\code{atan2(y,x)}, where $y = \cos \delta \sin (\alpha - \alpha_p)$
+and $x = \cos \delta \cos (\alpha - \alpha_p)$; and similarly for
+$\phi-\phi_p$.
+
+Note that the symmetry between the two sets of above equations means
+that inverse transformations can be made simply by switching
+$\alpha_p$ and $\phi_p$, changing the sign of $\delta_p$, and using
+the forward transformation.
+
+\subsubsection{Galactic to ICRS}
+
+The appropriate values, from the Hipparcos and Tycho Catalogues are:
+\begin{eqnarray}
+\alpha_p & = & 180^\circ - 192.85948^\circ \\
+\delta_p & = & 90^\circ - 62.87175^\circ \\
+\phi_p & = & 90^\circ + 32.93192^\circ
+\end{eqnarray}
+
+\subsubsection{Ecliptic to ICRS}
+
+The appropriate values, from Zombeck, are:
+\begin{eqnarray}
+\alpha_p & = & 270^\circ \\
+\delta_p & = & 23^\circ27'8''.26 - 46''.845\, T - 0''.0059\, T^2 + 0''.00181\, T^3 \\
+\phi_p & = & 90^\circ
+\end{eqnarray}
+where $T$ is the time in Julian centuries since 1900.
+
+\subsubsection{Precession}
+
+Approximate precession, good for modest sub-arcsecond precision, may
+be rapidly calculated using the following rotation angles:
+\begin{eqnarray}
+\alpha_p & = & 180^\circ + (0^\circ.6406161\, T + 0^\circ.0000839\, T^2 + 0^\circ.0000050\, T^3) \\
+\delta_p & = & 0^\circ .5567530\, T - 0^\circ.0001185\, T^2 - 0^\circ.0000116\, T^3 \\
+\phi_p & = & 180^\circ + 0^\circ.6406161\, T + 0^\circ.0003041\, T^2 + 0^\circ.0000051\, T^3
+\end{eqnarray}
+where $T$ is $($MJD$_{\rm out} -$ MJD$_{\rm in})/36525$ is the
+difference between the two epochs, in Julian centuries.  This
+precession form shall be used to implement \code{PS_PRECESS_ROUGH}.
+
+\subsubsection{Suggested test cases}
+
+$(\alpha,\delta) = (0^\circ,0^\circ)$ transforms to Galactic
+coordinates $(l,b) = (96.337272^\circ,-60.188553^\circ)$, and Ecliptic
+coordinates $(\lambda,\beta) = (0^\circ,0^\circ)$.
+
+$(\alpha,\delta) = (0^\circ,90^\circ)$ transforms to Galactic coordinates
+$(l,b) = (122.93192^\circ,27.12825^\circ)$, and Ecliptic coordinates
+at J2000.0 (i.e., $T=1$), $(\lambda,\beta) =
+(90^\circ,66.560719^\circ)$.
+
+$(\alpha,\delta) = (180^\circ,30^\circ)$ transforms to Galactic
+coordinates $(l,b) = (195.639488^\circ,78.353806^\circ)$, and Ecliptic
+coordinates at J2100.0 (i.e., $T=2$), $(\lambda,\beta) =
+(167.072470^\circ,27.308813^\circ)$.
+
+For each of the above input coordinates, precessing from J2100 to
+J1900 gives the following output coordinates:
+$(357.437^\circ,-1.113^\circ)$, $(358.719^\circ,88.886^\circ)$ and
+$(177.423^\circ,31.113^\circ)$.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Sky to Tangent Plane}
+
+This section describes the transformation between celestial coordinates
+(R.A., Dec.) and local terrestrial coordinates (Az, Alt). This transformation
+is broken down into a number of steps as described below.
+
+\subsubsection{Reference Implementations}
+
+There are two reference implementations for the code to account for the
+motion of the Earth in space. The first are the sample routines
+provided by the IERS to accompany chaper 5 of IERS Bulletin
+32.\footnote{http://maia.usno.navy.mil/conv2003.html} The second
+reference implementation is the SOFA software package managed by the
+IAU.\footnote{http://www.iau-sofa.rl.ac.uk} Only the 2003-04-29
+version of SOFA should be considered.  The IERS code requires a few of
+the rotation matrix utility routines from SOFA.
+
+Both implementations are in FORTRAN 77. The SOFA code has a more
+complex implementation of precession-nutation for backward
+compatibility with the pre 2003-01-01 conventions.  The IERS code
+includes some tricks to achieve greater precision in the fundamental
+arguments of nutation, which the SOFA code omits.  Therefore, the main
+reference for psLib should be the IERS code.  Note that the IERS code
+calculates the transform from terrestrial to celestial coordinates,
+while the SOFA code calculates its inverse.  This code may be using as
+a comparison for testing purposes.
+
+\subsubsection{Coordinate Systems}
+
+\begin{figure}
+\begin{center}
+\psfig{file=pics/earthrot.ps}
+\caption{Coordinates systems and the transformations between them\label{earthrot}}
+\end{center}
+\end{figure}
+
+Figure~\ref{earthrot} shows the transformation steps and intermediate
+coordinate systems between celestial and local terrestrial coordinate
+systems. The intermediate coordinate systems are defined below.
+
+\paragraph{ICRS}
+The official IAU-sanctioned celestial coordinate system is the
+International Celestial Reference System (ICRS). It is defined in terms of
+a number of radio sources whose positions have been measured using VLBI.
+It can be tied to the optical through the Hipparcos catalog. The ICRS has its
+origin at the solar system barycenter.
+
+\paragraph{GCRS}
+The Geocentric Celestial Reference System (GCRS) corresponds to the ICRS, but
+has its origin at the center of the Earth. The differences between the two
+systems are due to the velocity of the Earth (aberration), the position of
+the Earth (parallax), and general relativistic bending of light rays.
+There is no net rotation between the ICRS and the GCRS.
+
+\paragraph{ITRS}
+The International Terrestrial Reference System (ITRS) is a coordinate
+system which is fixed with respect to the Earth's crust.
+
+\paragraph{Intermediate Coordinate Systems - CIP, CEO, TEO}
+The transform between the GCRS and ITRS is conventionally
+decomposed into three parts in order to isolate the relatively rapid rotation
+of the Earth from the movement of the Earth's rotational axis in the GCRS
+and ITRS. All three sub-transforms are rigid rotations.
+
+This decomposition results in two intermediate coordinate systems. Both of
+these share the same pole, known as the Celestial Intermediate Pole (CIP).
+The CIP is defined by its motion in the GCRS to match the Tisserand
+mean axis of the Earth (Seidelmann 1982, Celesial Mechanics 27, 78-106),
+excluding motions with periods less than or equal
+to two days. The CIP approximates the angular momentum vector of the
+rotating Earth.
+
+The X axes of the intermediate coordinate systems are known as the
+Celestial and Terrestrial Ephemeris Origins. (CEO and TEO). Both are
+defined to be non-rotating origins. A non-rotating origin is a point
+on the equator whose instantaneous motion is always orthogonal to the
+equator (Kaplan 2003 IAU XXV Joint Discussion
+16\footnote{http://aa.usno.navy.mil/kaplan/NROs\%5BJD16proc\%5D.pdf}).
+Thus the CEO is defined by its position in the GCRS at some epoch and
+by the motion of the CIP in the GCRS since that date. Similarly the
+TEO is defined by its position in the ITRS at some epoch and the
+motion of the CIP in the ITRS since that date.
+
+\subsubsection{ICRS - GCRS}
+
+The transformation between barycentric (ICRS) and geocentric (GCRS) coordinates
+involves two components. These are
+the general relativistic deflection of light rays by the Sun's gravity, and
+aberration, due to the orbital motion
+of the Earth.
+
+\paragraph{Gravitational Deflection}
+
+The Sun's gravity bends the path of light rays which pass near it.  To
+first order, a light ray is deflected by an angle of $4GM/c^2r_0$
+radians, where $G$ is the gravitational constant, $M$ is the mass of
+the Sun, $c$ is the speed of light, and $r_0$ is the point of closest
+approach to the light ray to the Sun.  To the same order this is equal
+to the impact parameter - i.e. the point of closest approach if the
+light ray were not deflected. Note that $r_0/d = \tan(\theta)$, where
+$d$ is the distance from the Earth to the Sun, and $\theta$ is the
+angular separation of the star from the center of the Sun.
+
+There is a maximum deflection of 1.75 arc seconds if we set $r_0$ to
+the radius of the sun.  Since the Sun bends light rays toward it, a
+star appears shifted away from the sun in the sky.
+
+\paragraph{Aberration}
+
+Aberration is the apparent change in direction of a ray of light in
+the reference frame of a moving observer. Traditionally the aberration
+calculation has been done with a linear expansion of the full
+relativistic expression, often neglecting all but the linear term in
+$v/c$, since the relativistic terms are on the order of a
+miliarcsecond.  However, the full relativistic expression poses no
+challenge for modern computers, so psLib will use the following
+procedure to calculate aberration.
+
+Suppose an observer has a velocity $\beta\hat{\beta}$, with respect to
+the Solar System barycenter, where $\beta$ is in units of the speed of
+light, and $\hat{\beta}$ is a unit vector. Suppose also that the unit
+vector $\hat{r}$ points toward a star in the barycenter frame of
+reference (i.e. the ``actual'' position).  and $\hat{r}'$ gives the
+direction of the star in the observer's frame, (i.e. the apparent
+position).
+
+First, decompose $\hat{r}$ into components parallel and perpendicular
+to $\hat{\beta}$ by calculating $\mu = \hat{r}\cdot\hat{\beta}$ and
+$\vec{r}_\perp = \hat{r} - \mu \hat{\beta}$.
+
+Next, use the following expression for relativistic beaming, modified
+slightly from equation 4.8b of Rybicki and Lightman:
+\begin{equation}
+\mu' = \mu + \beta \frac{1 - \mu^2}{1 - \beta\mu}
+\end{equation}
+where $\mu' = \hat{r}' \cdot \hat{\beta}$.
+
+Now, the component of $\hat{r}'$ perpendicular to $\hat{\beta}$
+(i.e. $\vec{r}_\perp'$) must point
+in the same direction as $\vec{r}_\perp$, but will have a different magnitude
+because $\hat{r}'$ is a unit vector. In other words,
+$\vec{r}_\perp' = a\vec{r}_\perp$, for some scalar $a$. So the next step is
+to calculate $a = \sqrt{(1-\mu'^2)/\vec{r}_\perp^2}$.
+
+Finally, reassemble the components of
+$\hat{r}' = \mu'\hat{\beta} + a \vec{r_\perp}$.
+
+
+\subsubsection{GCRS - ITRS}
+The transformation between geocentric celestial coordinates and terrestrial
+coordinates is a solid body rotation due to the motion of the Earth is space.
+This is conventionally broken down into three components to isolate the
+relatively rapid rotation of the Earth from the motion of its rotational axis.
+
+This section is largely a summary of Chapter 5 of IERS Technical Note
+32\footnote{http://maia.usno.navy.mil/conv2003.html} (hereafter
+IERS32), which is a description of the implementation of the
+Resoltions of the XXIVth General Assembly of the IAU, available from
+the same URL as above.  These two documents describe a set of
+conventions which have been in effect since 2003-01-01. The
+conventions in effect before that date will not be implemented by
+psLib.
+
+\paragraph{Precession/Nutation}
+
+The transform between the GCRS and the CIP/CEO coordinate systems is
+described by the IAU 2000A precession-nutation model, which is
+accurate to the 0.2 mas level.  For higher accuracy the user must
+apply corrections to the model, which are tabulated by the IERS.
+
+\subparagraph{IAU 2000A Precession/Nutation Model : {\tt psEOC\_PrecessionModel}}
+
+The IAU 2000A precession-nutation model may be calculated in the
+following way. First calculate the time $t$ as the number of Julian
+centuries since 2000-01-01T12:00:00 TT.
+
+Next calculate the fundamental arguments of nutation using equations (40)
+and (41) of IERS32, reproduced below:
+\begin{eqnarray}
+F_1\equiv l\quad  =~&\ Mean\ Anomaly\ of\ the\ Moon \cr
+ =~& 134.96340251^\circ + 1717915923.2178'' t
+ + 31.8792'' t^2 + 0.051635'' t^3 - 0.00024470'' t^4,\cr
+F_2\equiv l'\quad =~&\ Mean\ Anomaly\ of\ the\ Sun\cr
+=~& 357.52910918^\circ + 129596581.0481'' t
+- 0.5532'' t^2 + 0.000136'' t^3 - 0.00001149'' t^4,\cr
+F_3\equiv F\quad  =~& L - \Omega\cr
+=~& 93.27209062^\circ + 1739527262.8478'' t - 12.7512'' t^2
+- 0.001037'' t^3 + 0.00000417'' t^4,\cr
+F_4\equiv D\quad  =~&\ Mean\ Elongation\ of\ the\ Moon\ from\ the\ Sun\cr
+=~& 297.85019547^\circ + 1602961601.2090'' t - 6.3706'' t^2
++ 0.006593'' t^3 - 0.00003169'' t^4,\cr
+F_5\equiv\Omega\quad  =~&\ Mean\ Longitude\ of\ the\ Ascending\ Node\ of\
+the\ Moon\cr
+=~& 125.04455501^\circ - 6962890.5431'' t + 7.4722'' t^2 + 0.007702'' t^3 - 0.00005939'' t^4 \cr
+F_6\ \equiv l_{Me}\quad    =~& 4.402 608 842 + 2608.7903 141 574\times t,\cr
+F_7\ \equiv l_{Ve}\quad    =~& 3.176 146 697 + 1021.3285 546 211 \times t,\cr
+F_8\ \equiv l_{E\ }\quad   =~& 1.753 470 314 + 628.3075 849 991 \times t,\cr
+F_9\equiv l_{Ma}\quad    =~& 6.203 480 913 + 334.0612 426 700 \times t,\cr
+F_{10}\equiv l_{Ju}\quad =~& 0.599 546 497 + 52.9690 962 641 \times t,\cr
+F_{11}\equiv l_{Sa}\quad =~& 0.874 016 757 + 21.3299 104 960 \times t,\cr
+F_{12}\equiv l_{Ur}\quad =~& 5.481 293 872 +  7.4781 598 567 \times t,\cr
+F_{13}\equiv l_{Ne}\quad =~& 5.311 886 287 +  3.8133 035 638 \times t,\cr
+F_{14}\equiv p_{a\ }\quad =~& 0.024 381 750 \times t + 0.000 005 386 91 \times t^2.
+\end{eqnarray}
+
+Next calculate the quantities $X$, $Y$, and $s$, using expressions of the form:
+
+\begin{equation}
+     \sum_{j} p_j t^j + \sum_{j} \sum_{i}[
+     (a_{{\rm s},j})_i \sin ({\rm \scriptstyle {ARG_{i,j}}}) t^j 
+   + (a_{{\rm c},j})_i \cos ({\rm \scriptstyle {ARG_{i,j}}})] t^j 
+   ,
+\end{equation}
+
+where the $\rm \scriptstyle{ARG_{i,j}} = \sum_{k} w_{i,j,k} F_k$ represent linear
+combinations of the fundamental arguments of nutation.
+
+The constants $p_j$, $w_{i,j,k}$, $(a_{{\rm s},j})_i$, and $(a_{{\rm c},j})_i$
+are given in the ASCII files:
+tab5.2a.txt\footnote{http://maia.usno.navy.mil/conv2000/chapter5/tab5.2a.txt} (for $X$),
+tab5.2b.txt\footnote{http://maia.usno.navy.mil/conv2000/chapter5/tab5.2b.txt} (for $Y$), and
+tab5.2c.txt\footnote{http://maia.usno.navy.mil/conv2000/chapter5/tab5.2c.txt} (for $s+XY/2$).
+Note that the expansion is given for $s+XY/2$, since this series converges
+more rapidly than the one for $s$ alone.
+
+Each file contains a human-readable header, which includes the polynomial
+coeficients, $p_j$ under the heading ``Polynomial part''. The data part of the
+file lists the remaining constants, with rows cycling first through $i$, and
+then through $j$. There is a separate heading each time $j$ increments.
+Each row contains the following columns:
+
+\begin{itemize}
+\item col 1 - A running index of rows in the table.
+\item col 2 - The sine coeficients, $(a_{{\rm s},j})_i$
+\item col 3 - The cosine coeficients, $(a_{{\rm c},j})_i$
+\item cols 4 - 17 The weighting factors for the fundamental arguments of
+                  nutation, $w_{i,j,k}$.
+\end{itemize}
+
+A FORTRAN reference implementation for the precession/nutation model
+is available from the
+IERS.\footnote{http://maia.usno.navy.mil/conv2000/chapter5/XYS2000A.f}
+The psLib results should agree with the reference implementation to
+within the limits of numerical precision.
+
+\subparagraph{Corrections to the Model : {\tt psEOC\_PrecessionCorr}}
+
+Corrections to $X$, and $Y$ may be obtained from the IERS as part of
+Bulletin A, or B. It is recommended to use the values published daily
+by USNO in the table
+\code{finals2000A.data}\footnote{http://maia.usno.navy.mil/ser7/finals2000A.data},
+which has the format described by
+\code{readme.finals2000A}\footnote{http://maia.usno.navy.mil/ser7/readme.finals2000A}. The
+quantities of interest are labeled dX and dY, which should be
+interpolated using Lagrangian interpolation.  Note that UT1$-$UTC and
+the polar motion values are obtained from this same table.
+
+There is no correction to apply to $S$.
+
+By convention, nutation terms with periods of less than two days are
+accounted for by the corresponding polar motion. So it is sufficient
+to interpolate the corrections tabulated daily by the IERS, and take
+the result as instantaneous values.
+
+\subparagraph{Spherical Rotation from Polar Coordinates : {\tt psSphereRot\_CEOtoGCRS}}
+
+In order to relate the values $X$, $Y$, and $s$ to the rotation
+components, the rotation matrix below must be used.  The definitions
+of $X$, $Y$, and $s$ transform from the CIP/CEO system to the GCRS
+using IERS32 equation (10), reproduced below:
+
+\begin{equation}
+\label{CEOtoGCRS}
+\begin{pmatrix}1-aX^2& -aXY& X\cr -aXY& 1-aY^2& Y\cr -X& -Y&
+1-a(X^2+Y^2)\cr
+\end{pmatrix} \cdot R_3(s),
+\end{equation}
+where $R_3$ denotes a rotation about the Z axis, $a = 1/(1+\sqrt{1 -
+(X^2 + Y^2})$, and $X$ and $Y$ are expressed in radians.  A FORTRAN
+reference implementation for this calculation is given by the
+IERS.\footnote{http://maia.usno.navy.mil/conv2000/chapter5/BPN2000.f}  
+
+Note that above we gave the expression for the transform toward
+celestial coordinates (upward in Figure~\ref{earthrot}), in order to
+match the IERS reference code.  The inverse transform may be found by
+inverting the resulting rotation.
+
+\paragraph{Earth Rotation}
+
+The transform from the CIP/CEO to CIP/TEO coordinate systems is a
+rotation about the CIP (i.e. the Z axis) by an angle known as the
+``Earth Rotation Angle''.
+By definition the Earth Rotation Angle is given by
+equation (13) of IERS32, reproduced below:
+\begin{equation}
+\theta(T_u)=2\pi(0.7790572732640 + 1.00273781191135448T_u),
+\end{equation}
+where $T_u$ is the Julian UT1 date minus 2451545.0 .
+
+\paragraph{Polar Motion}
+
+The motion of the CIP in the ITRS is known as ``polar
+motion''. Similarly to precession/nutation, the instantaneous position
+of the CIP in the ITRS is specified by the quantites $x_p$, and $y_p$,
+and a third quantity, $s'$, which give the position of the TEO with
+respect to the ITRS.  The values of $x_p$ and $y_p$ are published
+daily by the
+IERS,\footnote{http://maia.usno.navy.mil/ser7/finals2000A.daily} with
+a format described by their
+\code{readme.finals2000A}\footnote{http://maia.usno.navy.mil/ser7/readme.finals2000A}.
+The UT1$-$UTC, and the precession/nutation corrections (discussed
+elsewhere in this document) come from this same source.
+
+\subparagraph{Polar Motion from Bulletin : {\tt psEOC\_GetPolarMotion}}
+
+The polar motion coordinates should be interpolated using a third
+order polynomial, as described in IERS Gazette \#13
+\footnote{http://maia.usno.navy.mil/iers-gaz13}, which gives a FORTRAN
+reference implementation of the correct procedure.
+
+The values published by the IERS are smoothed to remove noise and
+variations on the timescale of a day or less. There are two sources of
+short timescale variations - tidal effects on the order of 0.1
+milliarcseconds, and short period nutation terms on the order of 15
+microarcseconds.  Both of these effects may be modeled and added to
+the interpolated values for higher accuracy.
+
+The tidal effects should be included by using the Ray tidal model
+given in IERS Gazette \#13. The definition of this correction is
+provided below (Section~\ref{Raymodel}).
+
+\subparagraph{Polar Motion Nutation Correction : {\tt psEOC\_NutationCorr}}
+
+By definition of the CIP, nutation terms with periods less than 2 days
+are not included in the IAU 2000A precession/nutation model.  So these
+motions must be compensated for by their equivalent polar
+motions. These may be calculated using a form similar to that of the
+precession/nutation $X$, and $Y$. The constants to use are given in
+Table 5.1 of IERS32.  Note that only the terms with periods less than
+2 days should be used.
+
+The quantity $s'$ may be approximated with microarcsecond accuracy
+over this century by $s' = -4.7 \times 10^{-5} t$ in arcseconds. There
+is no need to apply short timescale corrections to $s'$.
+
+\subparagraph{Spherical Rotation from Polar Motion : {\tt psSphereRot\_ITRStoTEO}}
+
+The transform from the ITRS to the CIP/TEO frame can be constructed by
+first rotating about the X axis by $y_p$, then rotating about the Y
+axis by $x_p$, and finally rotating about the Z axis by $-s'$.  The
+IERS reference implementation for this is given in the subroutine
+POM2000
+\footnote{http://maia.usno.navy.mil/conv2000/chapter5/POM2000.f}.
+Note that we describe the transform toward celestial coordinates
+(upward in Figure~\ref{earthrot}), in order to match the reference
+implementation.
+
+\subsubsection{Universal Time (UT1)}
+\label{sec:ut1}
+
+Since 2003-01-01, UT1 has been defined as directly proportional to the
+Earth Rotation Angle (see IERS Technical Note 32\footnote{IERS
+Technical Note 32 - http://maia.usno.navy.mil/conv2003.html}).
+Previous to that date, a different definition was in effect (see IERS
+Technical Note 21\footnote{IERS Technical Note 21 -
+http://maia.usno.navy.mil/conventions.html}).  We will always use the
+post-2003 definition.
+
+UT1 is continuously measured by the International Earth Rotation Service, and
+tabulated values of the offset of UT1 from UTC are published at regular
+intervals, along with predicted future values.  IERS Bulletin A gives ``rapid
+response'' values necessary for real-time and near real-time data analysis
+(such as Pan-STARRS Otis and IPP subsystems). Bulletin B gives the results of a
+final, definitive data reduction.  An amalgam of Bulletin A and B values is
+published daily on the IERS website\footnote{IERS Bulletin A \& B -
+http://maia.usno.navy.mil/ser7/finals2000A.daily} along with a desciption of
+the format\footnote{IERS finals2000A.daily table format -
+http://maia.usno.navy.mil/ser7/readme.finals2000A}.
+
+The UT1 offsets should be interpolated using the prescription of IERS
+Gazette \#13\footnote{IERS Gazette \#13 -
+http://maia.usno.navy.mil/iers-gaz13}.  This entails using a third
+order polynomial to interpolate the table values, and then applying a
+model for diurnal and semi-diurnal fluctuations due to tidal effects.
+An example implimentation\footnote{interp.f -
+ftp://maia.usno.navy.mil/dist/interp.f} written in Fortran is
+available.  The interpolated value of $dT$ must then have the tidal
+correction from the Ray Tidal Model applied.
+
+\subsubsection{Ray Tidal Model : {\tt psEOC\_PolarTideCorr}}
+\label{Raymodel}
+
+The Ray Model tidal corrections to X, Y, and dT are given by the the
+Fortran code listed below.  The input information is the epoch of
+interest in MJD, while the output results are the corrections $C_x$,
+$C_y$, and $C_dt$, which are in turn added to the interpolated values
+determined above.
+
+\verbatiminput{raymodel.f}
+
+The Polar motion X, and Y coordinates are also important for determining the
+orientation of the Earth with respect to the sky. This is also given in the
+IERS publications references above, and should be interpolated in the same way.
+
+\subsubsection{Longitude}
+
+Longitudes are often expressed in the form of decimal degrees while the
+algorithm for calculating GMST outputs seconds.
+
+\begin{equation}
+1\degree = 240s
+\end{equation}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsubsection{ITRS - Alt/Az}
+
+\paragraph{Orientation of the Observer}
+
+An observer's astronomical longitude and latitude give the orientation of
+the local vertical with respect to the ITRS. Note that these coordinates
+can be approximated by the geographic longitude and latitude of the observatory,
+but their exact values must be calibrated from observation of stars
+with known coordinates in the ICRS.
+
+The transform from the ITRS to Az/Alt in the absence of atmospheric refraction
+is first a rotation about the Z axis by the observer's astronomical longitude,
+and then a rotation about the Y axis of 90 degrees minus the observer's
+astronomical latitude, followed by a rotation about the Z axis of 180 degrees
+so that North is zero azimuth.
+
+\paragraph{Atmospheric Refraction}
+\newcommand\citep[1]{\textit{#1}}
+\newcommand\citet[1]{\textit{#1}}
+
+{\em The following discussion is adapted from an article by Ken Chambers}
+
+The hypsometric structure and index of refraction of the Earth's
+atmosphere produces:
+\begin{itemize}
+\item atmospheric refraction, resulting in an apparent positional
+displacement of astronomical objects towards smaller topocentric
+zenith distances,
+\item chromatic dispersion, along great circles intersecting the
+topocentric zenith with shorter wavelengths having smaller zenith
+distances; and
+\item extinction, from scattering and absorption of light by
+atmospheric gases (including water vapor) and aerosols.
+\end{itemize}
+
+Atmospheric refraction $R(\lambda) = z_{vac} - z_1$ is the difference
+between the topocentric zenith angle {\it in vacuo}, where the index
+of refraction is $n \equiv 1$, and the observed refracted zenith angle
+at the observatory $z_1$.  (All subscripts ``1" in this discussion
+indicate the local values of quantities at a given observatory site,
+and all units are SI.)  There are various ways to express the equation
+for zenith angle refraction; common ones include the ``general
+equation", e.g. \citep{s1936,s1962}, the Auer \& Standish
+transformation \citep{as1979,as2000}, and the Saastamoinen
+approximation \citep{saas73a,saas73b,saas73c}.  All are derived from
+the ``refractive invariant'', the fact that along the refracted light
+path described by the locus of points $r(z,n)$, where $r$ is the
+radial distance from the center of the Earth and $z$ and $n$ are the
+local values of the refracted zenith angle and the index of refraction
+of air respectively, the product $ n r {\rm sin} z = \rm {constant} $
+remains invariant.
+
+We have revisted the Saastamoinen approximation in search of an
+improved analytical expression accurate to $\sim 1$ mas up to zenith
+angles of 75 degrees.  Although different approximations are made at
+different stages, both the general equation and the Saastamoinen
+approximation lead to an expression for the refraction $R(\lambda)$ in
+the form of a series in odd powers of tan$z_1$:
+\begin{equation}
+ R(\lambda) = R_1 {\rm tan} z_1 + R_3 {\rm tan}^3 z_1 + 
+              R_5 {\rm tan}^5 z_1 + R_7 {\rm tan}^7 z_1 + 
+              R_9 {\rm tan}^9 z_1 + \dots                    
+\end{equation}
+where we have chosen to label the coefficients with a subscript reflecting
+the exponent of the tan$z_1$ terms.
+We find (c.f. Stone 1996)
+%
+\footnote{ The $R_1$ coefficient and the first two terms in the $R_2$
+          coefficient in equation 2 above give results similar to that
+          of Stone's equation (4), if it is modified such that the
+          $\kappa$ factor multiplies only his $\beta$, rather than the
+          whole coefficient as is done for both coefficients.  We
+          suspect this is a typographical error; his equation equation
+          as written gives much greater residuals when compared to the
+          Pulkovo Refraction Tables than he reports in his Table 2,
+          whereas the agreement is comparable, if his equation is
+          modified as described, and computed for the Pulkovo baseline
+          model of $\lambda = 590$nm, $15\deg C$, 101325 Pa, zero
+          water vapor, and mean sea level at latitude 45 degrees.  An
+          exact comparison is not possible because the residuals in
+          his Table 2 are averages computed from a wide but
+          unspecified range of meteorological conditions.  }
+%
+\begin{equation}
+ R(\lambda) = \gamma_1(1 - h_1/r_1){\rm tan} z_1 + 
+                [\gamma_1(\gamma_1 / 2  - h_1/r_1) + \delta_1]
+                {\rm tan}^3 z_1                                  
+\end{equation}
+in radians, where we define for convenience the variable 
+$$ \gamma_1 = n_1 -1 $$ where $n_1(\lambda)$ is the index of
+refraction of air at the observatory, which is dependent on the
+observatory's altitude and the meteorological conditions at the time
+of the observation (see Section 3 below).  Each of the other variables
+Eq.(2) take detailed discussions which, for the sake of clarity, are
+divided into separate subsections.
+
+\subparagraph{Observatory height}
+
+The height of the observatory from the geometric center 
+of the Earth is 
+\begin{equation}
+\begin{array}{ll}
+ r_1 = r_e + r_h, \qquad  r_h \approx r_n + r_o,                    
+\end{array}
+\end{equation}
+where 
+$r_e$ is the local radius of the reference ellipsoid,
+$r_h$ is the local height above the reference ellipsoid,
+$r_n$ is the local geoid height, normal to the reference ellipsoid,  
+$r_o$ is the orthometric height, which is the height above the geoid
+      (or Mean Sea Level) in the direction of normal gravity
+      (i.e. a local plumb line). 
+
+\subparagraph{The magnitude of normal gravity at the observatory }
+                                                                                
+The local magnitude of normal gravity
+%
+\footnote{ Stone uses a common
+  \citep[e.g.][]{allen1973,seid1992,allen2001} expression for the
+  normal gravity as a function of lattitude and altitude, which
+  apparently first appeared in \citet{lamb1949}.  However its
+  derivation is unclear -- cited as {\it ``Some notes on the
+  calculation of the geopotential", unpublished manuscript},
+  \citet{lamb1949}.  }
+%
+$g_1$
+is the acceleration due to the combination
+of the gravity of the ellipsoid, the local mass distribution,
+and the centripetal acceleration from the Earth's rotation,
+the vector sum being directed opposite to the local zenith by definition,
+being close but not identical to the normal to the ellipsoidal, but
+not intersecting the the geometric center of the Earth.
+i.e. the atmospheric topocentric zenith differs from the geocentric
+astronomical zenith, the impact of this difference on calculating
+the atmospheric refraction is minimal, see \citep{seid1992}. 
+The acceleration at the observatory is 
+\begin{equation}
+ g_1(r_h,\phi) = g(\phi)\left[ 1 - 2( 1 + f + m_r - 2 f \sin^2\phi)
+                 \left(\frac{r_h}{a} \right) +
+               3 \left(\frac{r_h}{a} \right)^2 \right]
+\end{equation}
+where
+\begin{equation}
+ g(\phi) = g_e \left( \frac{1 + k_s \sin^2 \phi}{\sqrt{1 - \epsilon^2 \sin^2 \phi}} \right)
+\end{equation}
+and $\phi$ is the latitude of the observatory and the other constants
+are given in Table 2.
+\begin{table}[!ht]
+\caption{WGS-84 World Geodetic System Reference Ellipsoid for GPS}
+\smallskip
+\begin{center}
+{\small
+\begin{tabular}{lr}
+%\tableline
+\noalign{\smallskip}
+Semi-major axis $a$                              & 6356752.3142 $m$\\
+Flattening $f =(a-b)/a$                          & 0.003352811 \\
+Eccentricity $\epsilon = \sqrt{a^2 +b^2}/a$      & 0.081819\\
+Polar gravity $g_p$                              & 9.8321849378 $m s^{-2}$ \\
+Equatorial gravity $g_e$                         & 9.7803253359 $m s^{-2}$ \\
+Somigliana's Constant $k_s = ((b/a)(g_p/g_e)-1)$ & 0.001931853 \\
+Angular velocity of the Earth $\omega$           & 0.00007292115 $rad/s$\\
+$GM_{earth}$ including atmosphere         & 3986004.418$\times 10^8 m^3
+s^{-2}$\\
+Gravity ratio $m_r = \omega^2 a^2 b /(GM)$       & 0.003449787\\
+\noalign{\smallskip}
+%\tableline
+\end{tabular}
+}
+\end{center}
+\end{table}
+
+
+\subparagraph{The scale height above the observatory} 
+
+The scale height of the atmosphere above the observatory is
+\begin{equation}
+ h_1 = \mathcal{Z}_1\mathcal{R}T_1/g_1 M_a[1 - x_w(1-M_w/M_a)],  
+\end{equation}
+where
+$T_1$ is the local air temperature at the observatory in degrees Kelvin,
+$\mathcal{R} = 8.314472 {\rm J \ mol^{-1} K^{-1}}$ 
+is the gas constant,
+\begin{equation}
+\begin{array}{ll} \mathcal{Z}_1 = 1 - (P_1/T_1)\left[a_0 + a_1t_1 + a_2t_1^2 + 
+                  (b_0 + b_1)t_1 x_w + (c_0+c_1t_1)x_w^2 \right] \\
+                  \qquad \qquad +(P_1/T_1)^2(d+ex_w^2)  
+                \end{array}
+\end{equation}
+is the compressibility of moist air at local air temperature 
+$t_1 = T_1 - 273.15$  
+in degrees Celsius. 
+The values of the other constants in $\mathcal{Z}_1$ are given in
+Appendix A. 
+The quantity  
+$M_a = 0.0289635 + 1.2001 \times 10^{-8}(x_{c1} - 400)$ 
+kg/mole
+is the molar mass of dry air with a $CO_2$ concentration $x_{c1}$ 
+in $\mu$mol/mol,
+$M_w = 0.018015 $ kg/mole is the molar mass of water vapor, 
+and
+$x_w$ is the molar fraction of water vapor in moist air, which 
+depends on the local humidity.   
+
+To calculate both the scale height $h_1$ and 
+the index of refraction of moist air at the observatory $n_1$
+we need to calculate $x_{w1}$, the molar fraction of water vapor
+from local measurements of the relative humidity or, much better, the
+dew point. Simple formulas such as \citet{davis1992}
+are only suitable above $0\deg C$, whereas at Mauna Kea observatories the
+temperature is often below $0\deg C$, and occasionally the surrounding 
+ground is covered in snow or ice, which also alters saturation vapor pressure.
+Thus, we adopt the best available equation for the 
+saturation water vapor pressure $p_{sv}$, 
+the IAPWS equations \citep{huang1998}. 
+Haung's equations are:
+
+\begin{equation} 
+\begin{array}{ll}
+   \Omega = T + K_9/(T - K_{10}) \qquad A = \Omega^2 +K_1\Omega + K_2 \\ 
+   B = K_3\Omega^2 + K_4\Omega + K_5 \qquad C = K_6\Omega^2 + K_7\Omega + K_8 \\
+   X = - B + \sqrt{B^2 - 4AC} \qquad p_{sv}(t) = 10^6(2C/X)^4  \\
+\end{array}
+\end{equation} 
+For saturation vapor pressure over ice or snow, use 
+\begin{equation} 
+\begin{array}{ll}
+   \Theta = T/273.16  \qquad Y=A_1(1-\Theta^{-1.5})+A_2(1-\Theta^{-1.25}) \\ 
+   p_{sv}(t) = 611.657 e^Y  \qquad  \\
+\end{array}
+\end{equation} 
+The constants are given in Table 1.  
+\begin{table}[!ht]
+\caption{Constants for Compressibility and Humidity Equations} 
+\smallskip
+\begin{center}
+{\small
+\begin{tabular}{lcl}
+%\tableline
+\noalign{\smallskip}
+ $a_0=1.58123\times 10^{-6}$KPa$^{-1}$     &  &  $K_1= 1.16705214528E+03$ \\  
+ $a_1=-2.9331\times 10^{-8}$Pa$^{-1}$      &  &  $K_2= -7.24213167032E+05$ \\  
+ $a_2=1.1043\times 10^{-8}$K$^-1$Pa$^{-1}$ &  &  $K_3= -1.70738469401e+01$ \\  
+ $b_0=5.707\times 10^{-6}$KPa$^{-1}$       &  &  $K_4= 1.20208247025E+04$ \\  
+ $b_1=-2.051\times 10^{-8}$Pa$^{-1}$       &  &  $K_5= -3.23255503223E+06$ \\  
+ $c_0=1.9898\times 10^{-4}$KPa$^{-1}$      &  &  $K_6= 1.49151086135E+01$ \\  
+ $c_1=-2.376\times 10^{-6}$Pa$^{-1}$       &  &  $K_7= -4.82326573616E+03$ \\  
+ $d  =1.83\times 10^{-11}$K$^2$Pa$^{-2}$   &  &  $K_8= 4.05113405421E+05 $ \\  
+ $e  =-0.765\times 10^{-8}$$^2$KPa$^{-2}$  &  &  $K_9= -2.38555575678E-01$ \\  
+                                           &  & $K_{10}=6.50175348448E+02 $ \\  
+                                           &  & $A_1=-13.928169$           \\  
+                                           &  & $A_2=34.7078238$           \\  
+\noalign{\smallskip}
+%\tableline
+\end{tabular}
+}
+\end{center}
+\end{table}
+
+\noindent Now, to calculate the mole fraction of water vapor $x_w$, we need the
+so called ``enhancement factor"
+\begin{equation}
+   f(p,t)= a' + b' p + c' t^2 
+\end{equation}
+where $a' = 1.00062$,$b'= 3.14 \times 10^{-8}$, and 
+$c' = 5.60\times 10^{-7}$, and where $p$ and $t$ are the air pressure in
+Pascals and air temperature. 
+
+If you have the more precise measurement of the dew point $t_d$ (or frost point)then
+\begin{equation} 
+x_w = f(p,t) \times p_{sv}(t_d)/p.
+\end{equation}
+On the other hand if you only have the relative humidity $RH$, a less accurate
+expression is 
+\begin{equation} 
+x_w = (RH/100) \times f(p,t) \times p_{sv}(t_d)/p.
+\end{equation}
+
+\subparagraph{The index of refraction of moist air at the observatory}
+
+The Ciddor equation for the index of refraction of moist air 
+\citep{ciddor1996} has been adopted by the International Association
+of Geodesy (IAG) as the standard as it is believed to provide the most
+accurate results under the largest
+range of wavelength, temperature, and humidity conditions (300 to 1690 nm;
+-40 to 100 C; 0-80\% RH). Note for astronomy, the air temperature at the
+tropopause in the 1976 standard atmosphere is 216.6 K, below the stated range ofvalidity. Astronomical observers often observe up to 90\% RH, where
+water droplets can form and change the effective index of refraction.
+Note that developments in the equation for the index of refraction of moist
+air have been poorly tracked in the astronomical literature and it is critical to examine in
+every application what equation is actually being used. \footnote{
+Edlen's (1953) original fit to the available data covered the wavelength
+range 2752 to 6440 /AA with reasonable residuals. His 1953 constants still
+survive in the astronomical literature in the equations of \citet{allen1973};
+%Allen (1973);
+\citet{stone1996}; \citet{allen2001}; 
+%Stone(1996); Allen(2001), 
+Roe (2002 - who extrapolates the 1953 equation to
+K band to correct for dichoric adaptive optics) and in the computer codes
+SLALIB and ZEEMAX. None discuss the range of validity. However, Elden himself
+revised them \citep{elden1966},
+%(Elden 1966), 
+and these were further discussed and updated by
+\citet{pr1972, bd1993, bd1994, ciddor1996, bp1998}. 
+\citet{rueg1998} and \citet{sz2004} make convincing arguments 
+for the Ciddor equation, and the latter's approach is followed here.}  
+
+The index of refraction of air at standard temperature and pressure
+is \citep{ciddor1996} 
+\begin{equation}
+\gamma_{as} = 10^{-8} \left( { \left[ { k_1 \over k_0 - \sigma^2} \right] 
+                    + \left[ { k_3\over k_2 - \sigma^2} \right] } \right),
+\end{equation} 
+where $\sigma = 1/\lambda$ is the wavenumber of wavelength of light in microns.
+Adjusting for the (annually varying and secularly increasing) value of 
+atmospheric $CO_2$ concentration $x_{CO2}$ in units of $\mu$mole/mole, 
+the expression for dry air becomes  
+\begin{equation}
+\gamma_{axs} = \gamma_{as} 
+               \left[ 1 + 5.34\times10^{-7} 
+               (x_{CO2} - 450\ \mu{\rm mole/mole} )\right]. 
+\end{equation} 
+For water vapor under standard conditions, Ciddor finds 
+\begin{equation}
+\gamma_{ws} = 1.022 \times 10^{-8}
+              \left[\omega_0 + \omega_1 \sigma^2 + 
+              \omega_2 \sigma^4 + \omega_3 \sigma^6 \right]. 
+\end{equation} 
+Following \citep{owens1967}, 
+the indicies can be combined in proportion to their densities,
+thus the index of refraction of moist air at the observatory 
+$n_1 = \gamma_1 +1 $ is given by     
+\begin{equation}
+\gamma_1 = (\rho_a/ \rho_{axs})\gamma_{axs} + 
+           (\rho_w/ \rho_{ws})\gamma_{ws},  
+\end{equation} 
+where
+\begin{equation} 
+\begin{array}{ll}
+   \rho_{a}   = (1 - x_w) P_1 M_a/({\cal Z}_m {\cal R} T_1) \\ 
+   \rho_{w}   = x_w P_1 M_w/( {\cal Z}_m {\cal R} T_1) \\ 
+   \rho_{axs} = P_{STP} M_a/( {\cal Z}_a {\cal R} T_{STP}) \\ 
+\end{array}
+\end{equation} 
+and $\rho_{ws}$ is given in Table 3.  
+
+\begin{table}[!ht]
+\caption{Constants in the Ciddor Eq. for index of refraction of moist air}  
+\smallskip
+\begin{center}
+{\small
+\begin{tabular}{lll}
+%\tableline
+\noalign{\smallskip}
+$k_0 = 23.0185\ \mu {\rm m}^{-2}$       &
+$\omega_0 = 295.235\  \mu {\rm m}^{-2}$ & 
+$P_{STP} = 101325$ Pa                   \\ 
+
+$k_1 = 5792105\ \mu{\rm m}^{-2} $       &
+$\omega_1 = 2.6422\   \mu {\rm m}^{-2}$ & 
+$T_{STP} = 288.15 $ K                   \\
+
+$k_2 = 57.362\ \mu{\rm m}^{-2}  $       & 
+$\omega_2 = -0.03238\ \mu {\rm m}^{-4}$ & 
+${\cal Z}_{a} = 0.9995922115$           \\
+
+$k_3 = 167917\ \mu {\rm m}^{-2} $       & 
+$\omega_3 = 0.004028\ \mu {\rm m}^{-6}$ &
+$\rho_{ws} = 0.00985938\ {\rm kg\ m}^3$ \\
+\noalign{\smallskip}
+%\tableline
+\end{tabular}
+}
+\end{center}
+\end{table}
+
+\subparagraph{The tropopause term in the equation of refraction}
+
+The final term in the Refraction Equation (2) 
+is $\delta_1$, which comes from our re-derivation of the 
+Saastamoinen approximation: 
+\begin{equation}
+ \delta_1 = 5 \left( {h_1 \over {r_1 T_1}}\right)^2
+              \left[ {{{\gamma_{ft}T_{ft}^2 - \gamma_t T_t^2} \over
+                                {1 - (h_1\beta/T_1)}}} 
+                                 + \gamma_tT_t^2 \right]   
+\end{equation}
+where $\beta$ is the lapse rate of the troposphere, 
+and the index of refraction of the free troposphere is given by 
+\begin{equation}
+ \gamma_{ft} = \gamma_t(T_{ft}/T_t) ^{-(T_1/h_1 \beta)-1}       
+\end{equation}
+and the index of refraction of the tropopause is given by  
+\begin{equation}
+ \gamma_t = \gamma_1 
+           {\rm exp} \left[ {T_1 (r_t - r_1) \over T_t h_1 }\right]    
+\end{equation}
+where $\beta$ is the lapse rate in K/m,  
+the temperature of the tropopause $T_t$, and height of the tropopause $r_t$ 
+are all determined from contemporaneous meterological data 
+(radiosonde or modern forecast models). Then the temperature of the 
+free troposphere is given by 
+\begin{equation}
+ T_{ft} = T_t - \beta(r_t - r_1).    
+\end{equation}
+
+\subparagraph{Calculating the atmospheric refraction from both 
+  the observed and true zenith angle}
+
+The monochromatic refraction can now be calculated for any given wavelength
+$\lambda_{air}$
+(formally only within the range of validity - 300 to 1670 nm) given
+the altitude of the observatory
+$h_1$; 
+contemporaneous meteorological measurements at the observatory of   
+air temperaure $T_1$ (K); 
+atmospheric pressure $P_1$ (Pa);
+percent relative humidity $RH$, or preferably dew point temperature $t_d$ ($\deg$C);
+as well as a small set of additional meteorological data:   
+the lapse rate of the troposphere $\beta$ (K/m);
+the radius of the tropopause $r_t = r_e+h_t$(m), where
+$h_t$ is the height above MSL of the tropopause; 
+the temperature of the tropopause $T_t$(K);
+and the atmospheric concentration of CO$_2$ 
+$x_{CO2}$ ($\mu$mole/mole). 
+The characterization of the troposphere and tropopause can 
+be determined either by interpolation between radiosonde measurements 
+or from hourly updated meteorological models available at many observatory 
+sites, or, worst case, simply adopting the 1976 Standard Atmosphere 
+values:\footnote{
+\citet{seid1992}
+%Seidelmann 1992 
+contains a typographical error quoting this value in units of K/km} 
+$\beta = -0.0065$ K/m, 
+$h_t = 11000$m,
+$T_t = 216.6$ K and assuming 
+$x_{C02} = 375 \mu$mole/mole.\footnote{
+Closed rooms have higher CO$_2$ concentration, thus the STP laboratory measurements 
+have concentrations near 450 $\mu$mole/mole (the prefered unit to parts per million per volume). The secular
+increase of atmospheric CO$_2$ in the industrial age is well documented, with 
+an annual cycle superimposed due to terrestrial biomass and ocean exchange. 
+}
+
+
+If the number of electrons being created from the illumination from the source in
+the interval of wavelength $d\lambda$ is $N_{\lambda}d\lambda$, 
+then the mean refraction is  
+\begin{equation}
+\bar R = {{\int R(\lambda) N_{\lambda} d \lambda} \over 
+         {\int N_{\lambda} d\lambda}}  
+\end{equation}
+
+\subsubsection{Air Mass and Extinction}    
+
+By Laplace's theorem, the monochromatic airmass (mass per unit area
+along the refracted path) is 
+\begin{equation}
+M(z_1) = (P_1/g_1) R(\lambda) / {\rm sin} z_1
+\end{equation}
+in kg/m$^2$. Thus
+\begin{equation}
+ M(z_1) = (P_1/g_1) \left( \gamma_1(1 - h_1/r_1){\rm sec} z_1 + 
+                [\gamma_1(\gamma_1 / 2  - h_1/r_1) + \delta_1]
+                {\rm sec}^3 z_1 \right).                                  
+\end{equation}
+This is generally normalized to $P_1/g_1$, i.e. in spite of the
+daily changes in barometric pressure, and thus daily changes in the 
+true mass of air over the observatory, the resulting change in extinction is
+generally treated as a drift in photometric zeropoint.  
+For a survey program like Pan-STARRS, one could instead normalize to a 
+standard barometric pressure (i.e. the altitute pressure), and thus
+during a low in atmospheric pressure, the airmass would be less than
+1 at zenith, and during periods of high pressure the airmass would
+be greater than 1 at zenith. From the variation with zeropoint and
+temperature and barometric pressure, this would remove most of the
+observed variation in zeropoint in the CFHT legacy program. 
+(Magnier, private comunication). 
+
+The mean airmass is then
+\begin{equation}
+\bar M(z_1) = \left( P_1 \over g_1 \right) \int 
+               \left( \gamma_1(1 - h_1/r_1){\rm sec} z_1 + 
+                [\gamma_1(\gamma_1 / 2  - h_1/r_1) + \delta_1]
+                {\rm sec}^3 z_1 \right) N_{\lambda} d \lambda.                                   
+\end{equation}
+and depends weakly on the filter bandpass. Use of this more
+accurate expression for airmass should lead to improved extinction
+corrections at high airmass.  
+
+\subsection{Projections}
+
+We implement three types of projections: {\em zenithal}, {\em
+cylindrical} and {\em pseudocylindrical}, each requiring slightly
+different handling.  Our representations are based on the treatment of
+projections presented by
+\href{http://www.cv.nrao.edu/fits/documents/wcs/wcs.all.ps}{Greisen \&
+Calabretta (1995, ADASS, 4, 233)}.  In all of these projections, we
+are converting from a spherical coordinate $\alpha,\delta$ to a linear
+(2-D) coordinate $x_p,y_p$.  The projection is defined by the
+projection type, the projection center ($\alpha_p, \delta_p$) and the
+the plate scales in the $x_p$ and $y_p$ directions ($\rho_x,\rho_y$).
+
+In the structure, \code{psProjection}, the projection type is defined
+by the element \code{type}, the projection center $\alpha_p,\delta_p$
+is defined by the elements \code{R,D}, and the plate scales,
+$\rho_x,\rho_y$, are defined by the elements \code{Xs,Ys}.  The plate
+scales are applied independently to the $x$ and $y$ coordinates to
+convert them to the corresponding linear units (ie, pixels):
+%
+\begin{eqnarray}
+x_p & = & \rho_x x \\
+y_p & = & \rho_y y
+\end{eqnarray}
+% 
+In the discussions below, we ignore this last step (or first step,
+depending on the direction of the conversion).
+
+\subsubsection{Zenithal Projections}
+
+The {\em zenithal} projections are defined relative to a set of
+spherical coordinates with pole at the center of the projection
+($\alpha_p, \delta_p$), and which thus represents a coordinate system
+rotated relative to the coordinate system of $\alpha, \delta$.  In
+this spherical coordinate system, the coordinate of longitude is
+labeled $\phi$, and has domain of $-\pi < \phi \le \pi$, while the
+latitude, measured from the pole, is labeled $\theta$ and has domain
+$0 \le \theta \le \pi$.  The coordinate frame of $\phi,\theta$ is
+defined so that $\phi_p$, the longitude of the target system pole, is
+0.0.
+
+For an arbitrary projection center, it is necessary to convert the
+spherical coordinates to be projected ($\alpha,\delta$) to the
+projection spherical coordinate system coordinates ($\phi, \theta$).
+In practice, we construct the following useful trigonometric
+relationships between $\phi$ and $\theta$ which may be employed in the
+equations of $x,y$ below:
+%
+\begin{eqnarray}
+\sin \theta           & = & \sin \delta \sin \delta_p + \cos \delta \cos \delta_p \cos (\alpha - \alpha_p) \\
+\cos \theta \cos \phi & = & \sin \delta \cos \delta_p - \cos \delta \sin \delta_p \cos (\alpha - \alpha_p) \\
+\cos \theta \sin \phi & = & - \cos \delta \sin (\alpha - \alpha_p)
+\end{eqnarray}
+%
+For the inverse transformations, the equivalent relationships are:
+%
+\begin{eqnarray}
+\sin \delta                          & = & \sin \theta \sin \delta_p + \cos \theta \cos \delta_p \cos \phi \\
+\cos \delta \cos (\alpha - \alpha_p) & = & \sin \theta \cos \delta_p - \cos \theta \sin \delta_p \cos \phi \\
+\cos \delta \sin (\alpha - \alpha_p) & = & - \cos \theta \sin \phi
+\end{eqnarray}
+%
+For zenithal projections, the linear coordinates are related to
+$\phi,\theta$ by:
+%
+\begin{eqnarray}
+x & = & R_\theta \sin \phi \\
+y & = & -R_\theta \cos \phi
+\end{eqnarray}
+%
+and the inverse:
+%
+\begin{eqnarray}
+R_\theta & = & \sqrt{x^2 + y^2} \\
+\phi     & = & {\rm atan} (-y,x)
+\end{eqnarray}
+%
+The coordinates $x,y$ above are defined to be in angular units (ie,
+radians).  
+
+From these relationships, we can calculate $\alpha, \delta$ as:
+%
+\begin{eqnarray}
+\alpha - \alpha_p & = & \arctan (\sin \alpha, \cos \alpha) \\
+\delta            & = & \arcsin (\sin \delta)
+\end{eqnarray}
+%
+Note that if $(x,y) = (0,0)$, then $\alpha = \alpha_p, \delta = \delta_p$.
+
+\paragraph{Gnomonic}
+
+The Gnomonic projection (``TAN'') is a zenithal projection with
+$R_\theta = \cot \theta$.  The resulting relationships for $(x,y)$ and
+for $\sin \theta, \cos \theta$ are:
+
+\begin{eqnarray}
+x           & = & \frac{\cos \theta \sin \phi}{\sin \theta} \\
+y           & = & \frac{-\cos \theta \cos \phi}{\sin \theta} \\
+\sin \theta & = & \zeta / \sqrt{1 + \zeta^2} \\
+\cos \theta & = & 1 / \sqrt{1 + \zeta^2}
+\end{eqnarray}
+
+where $\zeta = 1 / R_\theta$.
+
+\paragraph{Orthographic}
+
+The Orthographic projection (``SIN'') is a zenithal projection with
+$R_\theta = \cos \theta$.  The resulting relationships for $(x,y)$ and
+for $\sin \theta, \cos \theta$ are:
+
+\begin{eqnarray}
+x           & = & \cos \theta \sin \phi \\
+y           & = & -\cos \theta \cos \phi \\
+\sin \theta & = & \sqrt{1 - R_\theta^2} \\
+\cos \theta & = & R_\theta
+\end{eqnarray}
+
+\subsubsection{Cylindrical and Pseudocylindrical Projections}
+
+The {\em cylindrical} and {\em pseudocylindrical} projections are
+defined relative to a set of cylindrical coordinates whose pole is
+coincident with the pole of the spherical coordinates.  These
+projections are particularly used for full-sky representations, and
+are only defined for projection centers with $\delta_p = 0$.  In this
+spherical coordinate system, the coordinate of longitude is labeled
+$\phi$, and has domain of $-\pi < \phi \le \pi$, while the latitude,
+measured from the pole, is labeled $\theta$ and has domain $0 \le
+\theta \le \pi$.  The projection center longitude, $\alpha_p$
+corresponds to $\phi = 0$, thus the value of $\phi$ is determined as
+$\alpha - \alpha_p$ for all such projections.
+
+\paragraph{Cartesian}
+
+The Cartesian projection (``CAR'') is a very simple cylindrical
+projection with the following relationships between $x,y$ and
+$\phi,\theta$:
+
+\begin{eqnarray}
+x & = & \phi \\
+y & = & \theta
+\end{eqnarray}
+
+\paragraph{Mercator}
+
+The Mercator projection (``MER'') is a cylindrical projection.
+
+\begin{eqnarray}
+x & = & \phi \\
+y & = & \ln \left( \tan (\pi/4 + \theta/2) \right) \\
+{\rm and}\hspace{1cm} \theta & = & 2 \arctan \left( e^y \right) - \pi/2
+\end{eqnarray}
+
+\paragraph{Hammer-Aitoff}
+
+The Hammer-Aitoff projection(``AIT'') is a pseudocylindrical projection, and is defined:
+
+\begin{eqnarray}
+x & = & 2 \zeta \cos \theta \sin \frac{\phi}{2} \\
+y & = & \zeta \sin \theta \\
+{\rm where}\hspace{1cm} \zeta^{-1} & \equiv & \sqrt{\frac{1}{2}\left(1 + \cos \theta \cos \frac{\phi}{2} \right)}
+\end{eqnarray}
+
+And in reverse:
+
+\begin{eqnarray}
+\phi & = & 2 {\rm \arctan} (2z^2 - 1, x z) \\
+\theta & = & \arcsin (yz) \\
+{\rm where}\hspace{1cm} z & \equiv & \sqrt{1 - (x/2)^2 - y^2}
+\end{eqnarray}
+
+\paragraph{Parabolic}
+
+The Parabolic projection (``PAR'') is a pseudocylindrical projection, and is defined:
+
+\begin{eqnarray}
+x & = & \phi \left( 2 \cos \frac{2 \theta}{3} - 1 \right) \\
+y & = & \pi \sin \frac{\theta}{3}
+\end{eqnarray}
+
+And in reverse:
+
+\begin{eqnarray}
+\theta & = & 3 \sin^{-1} \rho \\
+\phi   & = & \frac{x}{1 - 4\rho^2} \\
+{\rm where}\hspace{1cm} \rho & \equiv & y/\pi
+\end{eqnarray}
+
+\subsection{Offset}
+
+Coordinate offsets can be either spherical offsets or linear offsets.
+
+A spherical offset is performed by adding the components of the
+offset, after unit conversion, to the given position.  The resulting
+coordinates must be wrapped to within the allowed range ($-\pi$ to
+$\pi$, 0 to $2\pi$).
+
+A linear offset is defined to be a linear offset in a tangent
+projection centered on the starting coordinate with $y$ axis aligned
+with the local direction or increasing Declination.  This projection
+is undefined only for the coordinates exactly at the north and south
+poles, in which case the orientation is defined to have the $y$ axis
+parallel to the line of RA = 0.0.  The scale of the projection is 1.0
+(ie, 1 'pixel' is 1 radian) and the given offsets must the scaled
+based on the given offset units.  
+
+Pseudo-code to implement the above for an offset:
+
+\begin{verbatim}
+psSphere *psSphereSetOffset (psSphere pos, psSphere offset) {
+
+  psPlane lin;
+  psSphere new;
+  psProjection proj;
+
+  proj.R = pos->r;
+  proj.D = pos->d;
+  proj.X = 0;
+  proj.Y = 0;
+  proj.type = PS_PROJ_TAN;
+
+  lin.x = offset.r;
+  lin.y = offset.d;
+
+  new = psDeproject (&lin, &proj);
+  return (new);
+}
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{The One-to-Many Problem with Mosaic Cameras}
+
+The \PS{} focal plane consists of several chips, so we will often want
+to identify which chip a source lies on, when we know the coordinates
+in the focal plane.  This is an example of the one-to-many problem
+(one coordinate, many chips that it may lie on).
+
+If this needs to be repeated for only one (or a small number of) focal
+plane coordinates, then the fastest method is to simply convert the
+focal plane coordinates to chip coordinates for each of the chips, and
+determine for which of the chips the chip coordinates are valid (i.e.\
+on the chip).
+
+On the other hand, if this needs to be repeated for many source focal
+plane coordinates, then it is most efficient to convert the centers of
+each of the chips to coordinates on the focal plane and to use the
+distance of the source to each of the centers to optimise which chips
+are tested first.  This saves testing many chips for every source.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{General Astronomy Functions}
+
+\tbd{we will provide a new airmass function, and a new mean-to-apparent conversion}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Positions of Major Solar System Objects}
+
+\tbd{ephemerides code?}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagebreak 
+
+\section{Pan-STARRS Modules}
+
+\subsection{Object Models}
+
+In the discussions below, the following relationships between the
+given C variables and the \code{pmSource} entries should be used:
+\begin{itemize}
+\item \code{Xo} is \code{pmMomemt.x}
+\item \code{Yo} is \code{pmMomemt.y}
+\item \code{SigmaX} is \code{pmMomemt.Sx}
+\item \code{SigmaY} is \code{pmMomemt.Sy}
+\item \code{Zo} is \code{pmPeak.counts - pmMoment.Sky}
+\item \code{So} is \code{pmMomemt.Sky}
+\end{itemize}
+
+\subsubsection{Real 2D Gaussian}
+
+This function is a two-dimensional Gaussian with an elliptical
+cross-section and a constant local background:
+\begin{equation}
+f(x,y) = Z_o e^{-z} + S_o
+\end{equation}
+where
+\begin{equation}
+z = \frac{(x - x_o)^2}{2\sigma_x^2} + \frac{(y-y_o)^2}{2\sigma_y^2} + (x-x_o) (y - y_o) \sigma_{xy}
+\end{equation}
+
+Below is the relationship between the \code{psModel} parameters and
+the function parameters, sample C-code implementing the function
+efficiently, and the value of the derivatives. 
+
+\begin{verbatim}
+  param[0] = So;
+  param[1] = Zo;
+  param[2] = Xo;
+  param[3] = Yo;
+  param[4] = sqrt(2) / SigmaX;
+  param[5] = sqrt(2) / SigmaY;
+  param[6] = Sxy;
+
+  X = x[0] - param[2];
+  Y = x[1] - param[3];
+  
+  px = param[4]*X;
+  py = param[5]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + param[6]*X*Y;
+  r = exp(-z);
+  f = param[1]*r + param[0];
+  /* f is the function value */
+
+  q = param[1]*r;
+  deriv[0] = +1;
+  deriv[1] = +r;
+  deriv[2] = q*(2*px*param[4] + param[6]*Y);
+  deriv[3] = q*(2*py*param[5] + param[6]*X);
+  deriv[4] = -2*q*px*X;
+  deriv[5] = -2*q*py*Y;
+  deriv[6] = -q*X*Y;
+\end{verbatim}
+
+The intial guess for the Gaussian parameters may be taken from the
+moments, peak value, and local sky.
+
+\subsubsection{Pseudo-Gaussian}
+
+This function is a polynomial approximation of a 2D Gaussian.  The
+function is very similar to the real Gaussian:
+\begin{equation}
+f(x,y) = Z_o (1 + z + z^2/2 + z^3/6)^{-1} + S_o
+\end{equation}
+where
+\begin{equation}
+z = \frac{(x - x_o)^2}{2\sigma_x^2} + \frac{(y-y_o)^2}{2\sigma_y^2} + (x-x_o) (y - y_o) \sigma_{xy}
+\end{equation}
+
+Below is the relationship between the \code{psModel} parameters and
+the function parameters, sample C-code implementing the function
+efficiently, and the value of the derivatives:
+
+\begin{verbatim}
+  param[0] = So;
+  param[1] = Zo;
+  param[2] = Xo;
+  param[3] = Yo;
+  param[4] = sqrt(2) / SigmaX;
+  param[5] = sqrt(2) / SigmaY;
+  param[6] = Sxy;
+
+  X = x[0] - param[2];
+  Y = x[1] - param[3];
+  
+  px = param[4]*X;
+  py = param[5]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + param[6]*X*Y;
+  t = 1 + z + 0.5*z*z;
+  r = 1.0 / (t*(1 + z/3)); /* ~ exp (-Z) */
+  f = param[1]*r + param[0];
+  /* f is the function value */
+
+  /* note difference from a pure gaussian: q = param[1]*r */
+  q = param[1]*r*r*t;
+  deriv[0] = +1;
+  deriv[1] = +r;
+  deriv[2] = q*(2*px*param[4] + param[6]*Y);
+  deriv[3] = q*(2*py*param[5] + param[6]*X);
+  deriv[4] = -2*q*px*X;
+  deriv[5] = -2*q*py*Y;
+  deriv[6] = -q*X*Y;
+\end{verbatim}
+
+The intial guess for the Gaussian parameters may be taken from the
+moments, peak value, and local sky.
+
+\subsubsection{Waussian}
+
+The Waussian is a modified polynomial approximation of a 2D Gaussian,
+with non-linear polynomial terms having variable coefficients, rather
+than the Taylor series values of 1/2 and 1/6.  The
+function is very similar to the pseudo-Gaussian:
+\begin{equation}
+f(x,y) = Z_o (1 + z + B_2 (z^2/2 + B_3 z^3/6))^{-1} + S_o
+\end{equation}
+where
+\begin{equation}
+z = \frac{(x - x_o)^2}{2\sigma_x^2} + \frac{(y-y_o)^2}{2\sigma_y^2} + (x-x_o) (y - y_o) \sigma_{xy}
+\end{equation}
+
+Below is the relationship between the \code{psModel} parameters and
+the function parameters, sample C-code implementing the function
+efficiently, and the value of the derivatives.  Note the fudge factors
+of 100 in the derivatives of $B_2$ and $B_3$: these are included to
+slow the variation of these parameters, which are otherwise very
+sensitive to small errors.
+
+\begin{verbatim}
+  param[0] = So;
+  param[1] = Zo;
+  param[2] = Xo;
+  param[3] = Yo;
+  param[4] = Sx;
+  param[5] = Sy;
+  param[6] = Sxy;
+  param[7] = B2;
+  param[8] = B3;
+
+  X = x - param[2];
+  Y = y - param[2];
+  
+  px = param[4]*X;
+  py = param[5]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + param[6]*X*Y;
+  t = 0.5*z*z*(1 + param[8]*z/3);
+  r = 1.0 / (1 + z + param[7]*t); /* ~ exp (-Z) */
+  f = param[1]*r + param[0];
+
+  /* note difference from gaussian: q = param[1]*r */
+  q = param[1]*r*r*(1 + param[7]*z*(1 + param[8]*z/2));
+  deriv[0] = +1;
+  deriv[1] = +r;
+  deriv[2] = q*(2*px*param[4] + param[6]*Y);
+  deriv[3] = q*(2*py*param[5] + param[6]*X);
+  deriv[4] = -2*q*px*X;
+  deriv[5] = -2*q*py*Y;
+  deriv[6] = -q*X*Y;
+  deriv[7] = -100*param[1]*r*r*t;
+  deriv[8] = -100*param[1]*r*r*param[7]*(z*z*z)/6;
+  /* the values of 100 dampen the swing of param[7,8] */
+\end{verbatim}
+
+\subsubsection{Twisted Gaussian}
+
+This function describes an object with power-law wings and a flattened
+core, where the core has a different contour from the wings.  
+
+\begin{equation}
+f(x,y) = Z_{\rm pk} (1 + z_1 + z_2^M)^{-1} + Sky
+\end{equation}
+where
+\begin{equation}
+z_1 = \frac{x^2}{2\sigma_{x,in}^2} + \frac{y^2}{2\sigma_{y,in}^2} + x y \sigma_{xy,in}
+z_2 = \frac{x^2}{2\sigma_{x,out}^2} + \frac{y^2}{2\sigma_{y,out}^2} + x y \sigma_{xy,out}
+\end{equation}
+
+\begin{verbatim}
+  param[0]  = So;
+  param[1]  = Zo;
+  param[2]  = Xo;
+  param[3]  = Yo;
+  param[4]  = SxInner;
+  param[5]  = SyInner;
+  param[6]  = SxyInner;
+  param[7]  = SxOuter;
+  param[8]  = SyOuter;
+  param[9]  = SxyOuter;
+  param[10] = N;
+
+  X = x - param[2];
+  Y = y - param[3];
+  
+  px1 = param[4]*X;
+  py1 = param[5]*Y;
+  px2 = param[7]*X;
+  py2 = param[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + param[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + param[9]*X*Y;
+
+  r  = 1.0 / (1 + z1 + pow(z2,param[10]));
+  f  = param[5]*r + param[6];
+
+  q1 = param[5]*SQ(r);
+  q2 = param[5]*SQ(r)*param[10]*pow(z2,(param[10]-1));
+
+  deriv[0] = +1;
+  deriv[1] = +r;
+  deriv[2] = q1*(2*px1*param[4] + param[6]*Y) + q2*(2*px2*param[7] + param[9]*Y);
+  deriv[3] = q1*(2*py1*param[5] + param[6]*X) + q2*(2*py2*param[8] + param[9]*X);
+
+  /* these fudge factors impede the growth of param[4] beyond param[7] */
+  f1 = fabs(param[7]) / fabs(param[4]);
+  f2 = (f1 < FSCALE) ? 1 : FFACTOR*(f1 - FSCALE) + 1;
+  deriv[4] = -2*q1*px1*X*f2;
+
+  /* these fudge factors impede the growth of param[5] beyond param[8] */
+  f1 = fabs(param[8]) / fabs(param[5]);
+  f2 = (f1 < FSCALE) ? 1 : FFACTOR*(f1 - FSCALE) + 1;
+  deriv[5] = -2*q1*py1*Y*f2;
+
+  deriv[6] = -q1*X*Y;
+
+  deriv[7] = -2*q2*px2*X;
+  deriv[8] = -2*q2*py2*Y;
+  deriv[9] = -q2*X*Y;
+  deriv[10] = -q1*ln(z2);
+\end{verbatim}
+
+The intial guess for the Gaussian parameters may be taken from the
+moments, peak value, and local sky.
+
+\tbd{future galaxy models to be implemented}
+
+\begin{verbatim}
+float Sersic()
+  param[0] = So;
+  param[1] = Zo;
+  param[2] = Xo;
+  param[3] = Yo;
+  param[4] = Sx;
+  param[5] = Sy;
+  param[6] = Sxy;
+  param[7] = Nexp;
+
+float SersicBulge()
+  param[0]  So;
+  param[1]  Zo;
+  param[2]  Xo;
+  param[3]  Yo;
+  param[4]  SxInner;
+  param[5]  SyInner;
+  param[6]  SxyInner;
+  param[7]  Zd;
+  param[8]  SxOuter;
+  param[9]  SyOuter;
+  param[10] = SxyOuter;
+  param[11] = Nexp;
+\end{verbatim}
+
+\subsection{WCS Translation}
+
+The FITS World Coordinate System (WCS) standard is specified in two
+papers: Greisen \& Calabretta, 2002, A\&A, 375, 1061 (Paper I); and
+Calabretta \& Greisen, 2002, A\&A, 375, 1077 (Paper II).  Two further
+papers (Papers III and IV) are available as drafts, and have not yet
+been accepted.  All these papers, in their most up-to-date form, are
+available from
+\href{http://www.atnf.csiro.au/people/mcalabre/WCS/index.html}{Mark
+Calabretta's page}.
+
+Papers I and II together lay out a system for converting pixel
+coordinates to celestial coordinates (RA, Dec).  However, these assume
+that linear transformations, followed by projection or deprojection
+are sufficient, whereas we do not expect that this is adequate to
+describe the \PS{} focal plane.  Paper III has to do with spectral
+coordinates, and does not concern us.  Paper IV has a proposed
+mechanism for dealing with distortions which appears to be adequate to
+our needs.  While the formalism has not been officially approved, and
+the syntax may change, the current version of the paper provides a
+means for translating the multilayered \PS{} system to FITS.
+Consequently, we will use the current version (the version we consider
+here is dated 22 April 2004) and update any syntax changes later as
+required.
+
+Paper IV proposes two separate distortion corrections --- before and
+after a linear transformation.  Given pixel coordinates, a distortion
+correction is made yielding ``corrected pixel coordinates''.  This
+first distortion allows correction of the individual detector (for
+example, if the detector is not flat, as may be the case for MegaCam).
+A linear transformation is then performed to ``intermediate pixel
+coordinates'', accounting for translation, rotation and scale.  The
+second distortion correction to ``corrected intermediate pixel
+coordinates'' is performed, which corrects for optical distortion.
+The resultant is then scaled and deprojected onto the sky, yielding
+the celestial coordinates.
+
+\subsubsection{Implementation}
+
+The first distortion will correct for tilts or bends of the detectors
+so that pixels are in the same plane as the focal plane
+(\code{psCell.toChip}).  The linear transformation will correct for
+the position on the focal plane (\code{psChip.toFPA}).  The second
+distortion will correct for distortions in the optics
+(\code{psFPA.toTangentPlane}).  The projection will be a simple
+gnomonic (``TAN'') projection.  Thus, the Paper IV formalism satisfies
+our needs.
+
+Paper IV also goes far beyond our needs, by providing several types of
+distortion functions.  We are interested (at least, for now) solely in
+simple polynomial distortion functions, as this is what is currently
+implemented for \PS{} (i.e., we are not interested in cubic spline,
+B-spline or lookup tables; nor are we interested in the use of
+auxiliary variables, though this may change in the future).  In
+particular, the proposed WCS system cannot handle the color and
+magnitude dependence currently built into psLib's \code{psDistortion},
+so we will need to specify a mean color and magnitude at which to
+evaluate the spatial polynomials.
+
+In the event that the multiple layers (\code{psCell.toChip}
+$\rightarrow$ \code{psChip.toFPA} $\rightarrow$
+\code{psFPA.toTangentPlane}) are not available, the short-cut
+transformation (\code{psCell.toFPA}) can be used as the first WCS
+distortion.  If the only available information is the ``quick and
+dirty'' transformation (\code{psCell.toSky}), this may be implemented
+using the linear transformation without any of the WCS distortions,
+followed by a linear `projection' (``LIN'').
+
+Reading the WCS into a psFPA can be done in the reverse order of
+writing the WCS.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Missing and Todo}
+
+\tbd{define sunrise, sunset, sun position}
+
+\tbd{define moonrise, moonset, moon position, moon phase}
+
+\tbd{define planet functions}
+
+\tbd{clean up FITS I/O issues}
+
+\tbd{define Brent's method \& minimization bracketing}
+
+\appendix
+\section{Change Log}
+\input{ChangeLogADD.tex}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/psLibSDRS.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/psLibSDRS.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/psLibSDRS.tex	(revision 22322)
@@ -0,0 +1,7910 @@
+%%% $Id: psLibSDRS.tex,v 1.445 2007-03-09 20:27:03 price Exp $
+\documentclass[panstarrs,spec]{panstarrs}
+
+% basic document variables
+\title{Pan-STARRS PS-1 Image Processing Pipeline Library}
+\subtitle{Supplementary Design Requirements}
+\shorttitle{PSLib SDRS}
+\author{Eugene Magnier, Paul Price, Robert Lupton, Joshua Hoblitt}
+\audience{Pan-STARRS PMO}
+\group{Pan-STARRS Algorithm Group}
+\project{Pan-STARRS Image Processing Pipeline}
+\organization{Institute for Astronomy}
+\version{21}
+\docnumber{PSDC-430-007}
+
+% \setcounter{tocdepth}{5} % lowest level to be included in toc
+% \setlength{\topsep}{-2pt}
+  
+\begin{document}
+\maketitle
+\sloppy
+
+% make a margin comment:
+% \marginpar{note!}
+
+% -- Revision History --
+% provide explicit values for the old versions
+% use '\theversion' for the current version (set above)
+% use \hline between each table row
+\RevisionsStart
+% version  Date            Description
+DR & 2004 Mar 29 & Draft \\ \hline
+00 & 2004 Apr 1  & First version, sent to MHPCC \\ \hline
+01 & 2004 May 19 & Extensive modifications, see Appendix B \\ \hline
+02 & 2004 Jun 22 & Incorporation of Bugzilla PRs (up to 69) \\ \hline
+03 & 2004 Jul 06 & \\ \hline
+04 & 2004 Jul 13 & See Appendix B for a change log. \\ \hline
+05 & 2004 Aug 16 & draft for start of cycle 3 \\ \hline
+06 & 2004 Aug 19 & revision for cycle 3 \\ \hline
+07 & 2004 Sep 07 & final for cycle 3 \\ \hline
+08 & 2004 Oct 12 & draft for start of cycle 4 \\ \hline
+09 & 2004 Nov 15 & final for cycle 4 \\ \hline
+10 & 2004 Nov 30 & update for cycle 4 \\ \hline
+11 & 2005 Jan 21 & draft for cycle 5 \\ \hline
+12 & 2005 Feb 09 & final for cycle 5 \\ \hline
+13 & 2005 Mar 30 & draft for cycle 6 \\ \hline
+14 & 2005 Apr 27 & final for cycle 6 \\ \hline
+15 & 2005 Jun 15 & draft for cycle 7 \\ \hline
+16 & 2005 Sep 13 & final for cycle 8 \\ \hline
+17 & 2005 Oct 18 & draft for cycle 9 \\ \hline
+18 & 2005 Dec 06 & draft for cycle 10 \\ \hline
+19 & 2006 Feb 21 & draft for cycle 11 \\ \hline
+20 & 2006 Apr 11 & draft for cycle 12 \\ \hline
+21 & 2006 Jul 24 & final for cycle 13 \\ \hline
+\RevisionsEnd
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\DocumentsInternal
+PSDC-230-001  &   PS-1 Design Reference Mission \\ \hline
+PSDC-430-004  &   Pan-STARRS PS-1 IPP C Code Conventions \\ \hline
+PSDC-430-005  &   Pan-STARRS PS-1 IPP Software Requirements Specification \\ \hline
+PSDC-430-006  &   Pan-STARRS PS-1 IPP Algorithm Design Document \\ \hline
+PSDC-430-011  &   Pan-STARRS PS-1 IPP System/Subsystem Design Description \\ \hline
+\DocumentsExternal
+Posix Standard & Open Group Based Specifications Issue 6, IEEE Std 1003.1, 2003 \\
+\DocumentsEnd
+
+\tableofcontents
+\pagebreak 
+\pagenumbering{arabic}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Introduction and policies}
+
+This document describes the Pan-STARRS Image Processing Pipeline (IPP)
+Toolkit Library, PSLib.  Any large software project such as the IPP
+benefits from the existence of a library of basic software functions
+which can be used throughout the project to simplify programming
+tasks.  Among the benefits are the ability to reuse code,
+simplification of the testing process, streamlining of the code, and
+the isolation and encapsulation of concepts which may be subject to
+change.  The component functions of such a library should represent
+well-defined, concise operations which can be coded with only a modest
+number of lines.  PSLib is a library of basic functions required by
+the IPP, and it includes many programming concepts which may be useful
+for other software projects, especially those which deal with
+astronomical data handling tasks.
+
+PSLib consists of a collection of library function calls --- the
+Application Programming Interfaces (APIs) --- and the associated data
+structures.  The capabilities provided by PSLib are grouped into the
+following areas:
+%
+\begin{itemize}
+\item System Utilities
+\item Basic Data Collections
+\item Data Manipulation
+\item Astronomy-Specific Functions.
+\end{itemize}
+This list is sorted in a hierarchical order: the later entries depend
+on the functions and data types of the earlier entries.  
+
+The installed code base for PSLib consists of header files, the binary
+library code, \code{libpslib.a} and the shared-library equivalent,
+\code{libpslib.so} (or \code{libpslib.dylib} in the case of OS/X).
+Assuming these components have been installed into the library and
+search path, PSLib may be used within a program by including the line
+\code{#include <pslib.h>} into the C code and linking with
+\code{-lpslib.}
+
+This document describes the data structures and details the functions
+calls. The specified data structures and functions follow the naming
+conventions detailed in the IPP Software Requirements Specification
+(PSDC-430-005).  In particular, these coding conventions restrict the
+namespace used by the library functions by requiring that all globally
+visible symbols start with the two letters \code{ps}.  Further
+namespace organization is achieved by encouraging functions to be
+named in the form psNounVerbPhrase, where Noun is the data type of
+principle relevance and VerbPhase describes the operation applied to
+that data type.  For example, the function which copies an image (of
+type \code{psImage}) is called \code{psImageCopy().}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{External Libraries}
+
+It is anticipated that many of the functions specified in this
+document will be implemented through wrapping external libraries:
+\begin{itemize}
+\item Many of the matrix functions, some of the polynomial and some of
+the minimization functions functions should wrap the GNU Scientific
+Library (GSL):
+
+\href{www.gnu.org/software/gsl/}{\tt www.gnu.org/software/gsl};
+
+\item The sort functions should wrap the system \code{qsort} call
+
+\item Some of the Fourier transform functions should wrap the Fastest Fourier
+Transform in the West library (FFTW):
+
+\href{www.fftw.org}{\tt www.fftw.org}
+
+\item The FITS functions should wrap the CFITSIO library:
+
+\href{heasarc.gsfc.nasa.gov/docs/software/fitsio}{\tt heasarc.gsfc.nasa.gov/docs/software/fitsio}
+
+\end{itemize}
+
+In addition, some of the functions were implemented in a limited
+fashion in the Pilot Project.  Also, RHL has provided functional
+prototype code for the memory management, tracing and message logging
+functions (some of which need to be revised), and some of the data
+containers (doubly-linked lists, hashes, arrays).
+
+\subsection{Threads and Re-entrancy}
+
+Due to current developments in CPU architecture, we must assume that
+PSLib will be used in a threaded environment.  However, coding the
+library to be thread-safe may have implications for the speed of the
+library and for the simplicity of use.  We therefore make the
+following policies:
+
+\begin{itemize}
+\item The memory management and error stack functions, defined below
+  (Sections~\ref{sec:memory} \&~\ref{sec:errors}), must be written to
+  be thread-safe, since we cannot risk this crucial area being
+  unstable.  However, this can have a large impact on the efficiency
+  of the code, and so we specify that this behaviour may be activated
+  and deactivated dynamically.  The default behaviour, however, will
+  be thread-safety, since it is more important to err on the side of
+  being safe rather than efficient.  
+\item Re-entrant versions of system calls and external library
+  functions should be used.  We expect that these cases are
+  sufficiently small that we are prepared to err on the side of
+  caution. 
+\item The practise of using \code{static} variable to achieve high
+  efficiency (e.g., so that subsequent calls do not have to repeat a
+  large memory allocation) should be kept to an absolute minimum.
+  Where it has been justified (i.e., through code profiling), the
+  \code{static} variable must be protected by a ``mutex''.
+\item Cross-thread synchronization for PSLib's fundamental datatypes
+  (\code{psArray}, \code{psList}, etc.) is left to the end-user
+  (although some convenience is provided --- see below).
+\end{itemize}
+
+As a convenience to the user in achieving thread-safe operation, each
+of the data structures classified as a ``collection'' (i.e.,
+\code{psList, psHash, psMetadata, psArray, psPixels, psVector,
+psBitSet}) and \code{psImage} shall contain a member, \code{void
+*lock}, which provides a place for the user to carry around a mutex or
+semaphore.  This is provided so that the user doesn't have to pass
+around both the structure and a mutex, or wrap PSLib structures in
+their own thread-safe structures that contain a mutex.  PSLib is not
+responsible for allocating, setting, checking or freeing the
+\code{lock} --- these are entirely the responsibility of the user.
+PSLib provides only a place to hang it.  Your mileage may vary.
+
+If these policies become a burden on the processing speed, we can
+investigate alternative measures, such as defining specifically
+re-entrant versions of select speed-critical functions.
+
+\subsection{Angles}
+
+To maintain consistency throughout the library, angles shall be
+specified in radians.  In a small number of cases which we expect will
+be used heavily (i.e., \code{psSphereOffset} and trigonometric
+operations on vectors), the unit may be specified, which provides
+convenience to the user.
+
+\subsection{C++ Compatibility}
+
+All PSLib ``public'' header files should comptable with \code{C++}.
+This primarily involves using \code{C} pre-processor directives to
+enable \code{C++}'s \code{extern "C"} linkage specification when a
+header file is being processed as part of a \code{C++} compilation.
+
+An example of wrapping a header file with an \code{extern "C"} block:
+
+\begin{verbatim}
+#ifndef FOO_H
+#define FOO_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+...
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // FOO_H
+\end{verbatim}
+
+Other coding cautions include avoiding trailing commas in
+\code{enum}s, protection of the poison \code{pragma}s used for the
+memory system (below), and avoiding the qualifier \code{restrict}.
+
+\subsection{Use of {\tt restrict}}
+
+The \code{restrict} type qualifier in C99 indicates to the compiler
+that the memory pointed to by a particular pointer is not also pointed
+to by some other pointer.  This allows the compiler to optimise the
+code, based on the fact that aliasing is not an issue.  However, the
+compiler does not check the accuracy of the assumption on which the
+optimisation is made, which can lead to data corruption.
+
+Due to the large number of cross-links in psLib (e.g., the metadata
+container keeps two pointers to the data), the assumption behind the
+use of \code{restrict} generally will not hold.  Correspondingly, use
+of \code{restrict} should be kept to a minimum; it should only be
+employed where necessary, and where the assumption will hold.
+Restricts should also be avoided because they are incompatible with
+\code{C++}.
+
+\subsection{Return values}
+
+In some cases, we have defined functions that return a boolean value.
+It is intended that (unless otherwise specified), the function returns
+\code{true} if there was no error, and returns \code{false} in the
+event of an error.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagebreak 
+\section{System Utilities}
+
+\subsection{Configuration}
+
+It is important to be able to access the version of the code in use.
+\code{psLibVersion} shall return the current version of PSLib in use,
+as determined from CVS tags.
+
+\begin{prototype}
+psString psLibVersion(void);
+\end{prototype}
+
+\subsection{Initialization}
+
+Initialization of the library is necessary in the general case.
+\code{psLibInit} shall initialize those elements of the library that
+require initialization.  This includes loading the time configuration
+(through \code{psTimeInitialize}).  In addition, if the environment
+variable \code{PS_ALLOC_CHECK} (analagous to \code{MALLOC_CHECK_}
+under GNU libc 2.x), then a memory check (checking for all leaks, and
+corruption) shall be performed at exit, with any problems reported to
+the file specified by \code{PS_ALLOC_CHECK}.
+
+\code{psLibFinalize} shall free all memory used by psLib.
+
+\begin{prototype}
+void psLibInit(const char *timeConfig);
+void psLibFinalize(void);
+\end{prototype}
+
+
+
+\subsection{Memory Management}
+\hlabel{psMemBlock}
+
+\subsubsection{Introduction}
+
+PSLib needs a level of memory management placed between the operating
+system (\code{malloc}/\code{free}) and the high level routines (e.g.\
+\code{psMetadataAlloc}).  This layer is in addition to the possibility
+that specific heavily used data types may need their own
+special-purpose memory managers.  However, since we require that all
+user-level objects be allocated via associated \code{Alloc/Free}
+functions, we will easily be able to implement such functionality
+without impacting the facilities described here.
+
+\subsubsection{Rationale}
+
+We wish to insert our own layer of memory management for a number of
+reasons:
+
+\begin{itemize}
+\item
+  We wish to insulate ourselves from the details of the system-provided
+  \code{malloc}.  There is no guarantee that the goals of the system
+  architect align with those of the PSLib or the IPP.
+
+\item
+  We need at least a wrapper layer which handles failed memory
+  requests without requiring the application programmer to check
+  every attempted allocation.
+
+\item
+  We need to provide a mechanism for tracking and fixing memory leaks.
+  While it is possible to do this by linking with external libraries
+  (e.g.\ \href{gnu.org}{Electric Fence}), it is convenient to do so
+  within the framework provided by PSLib.
+
+\item
+  Similarly, we wish to provide convenient hooks to detect and diagnose
+  memory corruption.
+
+\item
+  While debugging complex scientific code, it is very useful to be
+  able to trace a specific data structure as it passes through the
+  processing pipeline.
+
+\item
+  There may be other features that we wish to add in the future (e.g.
+  associating a type with every allocation).
+\end{itemize}
+
+\subsubsection{Memory Management}
+\label{sec:memory}
+
+In the following sections, we specify the API set and define the
+appropriate data structures needed by the PSLib memory management
+system in order to meet the requirements specified by the desiderata
+listed above.
+
+Within the PSLib memory management system, every allocated memory
+block which is provided to the user is bounded by two additional
+memory segments.  The segment preceeding the user-memory contains data
+describing the allocated block, using the \code{psMemBlock} structure.
+The first and last elements of this structure are \code{void} pointers
+called \code{startblock} and \code{endblock}, which are assigned a
+special value, \code{P_PS_MEMMAGIC}.  The segment following the
+user-memory block consists of a single \code{void} pointer, and is
+also assigned the special value of \code{P_PS_MEMMAGIC}.  The element
+\code{userMemorySize} specifies the number of bytes allocated in the
+user block and allows the endpost to be found.
+
+In practice, these bounding memory blocks mean that when a user
+requests $N$ bytes of memory, the memory management system in fact
+allocates \code{N + sizeof(psMemBlock) + sizeof(void)} bytes, starting
+at a particular address, \code{ADDR}.  It then fills in the first
+\code{sizeof(psMemBlock)} bytes with the data of the \code{psMemBlock}
+structure, and the last \code{sizeof(void)} bytes with
+\code{P_PS_MEMMAGIC}.  It returns to the user the pointer corresponding
+to the address \code{ADDR + sizeof(psMemBlock)}.  If the memory
+management system reallocates a block of memory, it must also allocate
+the additional space and fill in the boundary values.  If the memory
+management system is given a specific pointer for some operation, it
+is able to find the corresponding \code{psMemBlock} by simply
+subtracting \code{sizeof(psMemBlock)} from the pointer address.
+
+The purpose of the three boundary markers is to catch corruption and
+to act as an aid in low-level debugging.  In the first case, memory
+over- and under-run errors are likely to overwrite the special values
+in either the leading or trailing boundaries.  The typical situation
+is one where the coder mis-counts the range and either fills the data
+just before the start of the valid memory or just after the end of the
+valid memory.  These actions are likely to alter the boundary-post
+values, which can be detected by the memory management system.  In the
+second case, hexadecimal dumps of large blocks of memory are easier to
+examine if the value of \code{P_PS_MEMMAGIC} is chosen to catch the
+eye.  A traditional value for \code{P_PS_MEMMAGIC} is \code{0xdeadbeef}
+which is also easily recognized in a dump of the memory table.
+
+The structure \code{psMemBlock} specifies additional information
+maintained for each block of allocated memory, and is defined as
+follows:
+%
+%\filbreak
+\begin{datatype}
+typedef struct psMemBlock {
+    const void* startblock;             ///< initialised to p_psMEMMAGIC
+    struct psMemBlock* previousBlock;   ///< previous block in allocation list
+    struct psMemBlock* nextBlock;       ///< next block allocation list
+    psFreeFunc freeFunc;                ///< deallocator.  If NULL, use generic deallocation.
+    size_t  userMemorySize;             ///< the size of the user-portion of the memory block
+    const psMemId id;                   ///< a unique ID for this allocation
+    const char* file;                   ///< set from __FILE__ in e.g. p_psAlloc
+    const unsigned int lineno;          ///< set from __LINE__ in e.g. p_psAlloc
+    pthread_mutex_t refCounterMutex;    ///< mutex to ensure exclusive access to reference counter
+    psReferenceCount refCounter;        ///< how many times pointer is referenced
+    bool persistent;                    ///< marks if non-user persistent data like error stack, etc.
+    const void* endblock;               ///< initialised to p_psMEMMAGIC
+} psMemBlock;
+
+typedef void (*psFreeFunc)(void* ptr);
+typedef unsigned long psMemId;
+typedef unsigned long psReferenceCount;
+\end{datatype}
+%
+The PSLib memory management system must maintain the collection of
+allocated memory blocks.  The entries \code{previousBlock} and
+\code{nextBlock} point to the previous and next memory blocks
+allocated by the memory management system (if they exist, or else
+\code{NULL}) and are used to scan through memory blocks as a linked
+list.
+
+The element \code{freeFunc} specifies the deallocator associated with
+a specific block of memory.  If this element is \code{NULL}, the basic
+deallocator (\code{psFree}) is used and the memory block must not be a
+rich data structure which requires additional freeing functionality
+(otherwise memory leaks will occur).
+
+The element \code{id} in the structure is a sequential memory block
+ID.  The memory management system must maintain an internal memory
+block ID counter from which a new ID may be supplied to each newly
+allocated block of memory and saved in the element \code{id}.  This ID
+should also be the key to the memory block in the memory block table.
+
+The two entries \code{file} and \code{lineno} are set to the line
+number and file at which the memory was originally allocated.  This is
+most easily implemented by the use of the C preprocessor macros
+\code{__LINE__} and \code{__FILE__}.  For this reason, we specify
+below that the basic memory managment functions be implemented as
+preprocessor macros which wrap the intrinsic C level functions.
+
+The element \code{refCounterMutex} is a mutex used to limit access to
+the reference counter (below) to a single thread at a time.
+
+The element \code{persistent} indicates if the memory is to be used by
+some non-user persistent data.  This allows memory marked as
+\code{persistent} to be ignored when identifing memory leaks with
+\code{psMemCheckLeaks}.  Private functions shall be provided to test
+and set the value of \code{persistent}:
+\begin{prototype}
+void p_psMemSetPersistent(psPtr ptr, bool value);
+bool p_psMemGetPersistent(psPtr ptr);
+\end{prototype}
+
+Sometimes system code will need to allocate complex structures (such
+as a \code{psMetadata}) that must all be marked as \code{persistent}.
+To save this code the trouble of tracking down every allocated pointer
+to mark each \code{persistent}, we define an additional private
+function, \code{p_psMemAllocatePersistent}, that sets the use of
+\code{persistent} within \code{psAlloc}.  This allows us to call
+\code{p_psMemAllocatePersistent(true)}, allocate all our persistent
+memory, and then call \code{p_psMemAllocatePersistent(false)}.  The
+initial setting shall be for allocated memory not to be marked
+\code{persistent}.
+\begin{prototype}
+bool p_psMemAllocatePersistent(bool is_persistent);
+\end{prototype}
+
+The \code{psMemBlock} structure element \code{refCounter} is provided
+so APIs may cleanly manage multiple references to the same block of
+memory.  As discussed below, the basic free function, \code{psFree},
+is specified to free the memory block only if the reference counter is
+set to 1.  See the discussion in section~\ref{sec:free} for an example
+of the usage.  Usage of this feature is strongly encouraged, but not
+enforced by the memory management system.
+
+In order to trace double frees and other memory errors, the memory
+block reference is not automatically deleted when the assocated memory
+is deleted.  Rather, the \code{psMemBlock} data and the endpost data
+are left behind.  If \code{userMemorySize} is 0, then the memory block
+has been freed.  This state must be enforced by \code{psFree}.  This
+behavior, in which the associated \code{psMemBlock} is retained, is
+only provided if the code is compiled with the preprocessor variable
+\code{PS_MEM_DEBUG} defined.
+
+\paragraph{Use of Persistent Memory}
+
+The \code{persistent} member of the \code{psMemBlock} is provided as a
+convenience to the end-user of psLib --- it allows him to check for
+memory leaks in his own code, without going to the trouble of freeing
+memory allocated for psLib core functions.  It also allows the use of
+psLib core functions (e.g., \code{psLogMsg, psTrace}) within the
+function that is called by \code{psMemCheckLeaks}, without the memory
+allocated by those psLib core functions being identified as a leak.
+
+For these reasons, the \code{persistant} feature should only be used
+as necessary.  In order to allow proper testing of psLib, all
+components that employ any \code{persistent} memory shall provide a
+function that frees all of its \code{persistent} memory (and only that
+persistent memory that belongs to that component), all of which should
+be called from \code{psLibCleanup}.
+
+\subsubsection{APIs for Allocating and Freeing}
+
+PSLib must provide the following APIs to create and destroy memory
+blocks:
+%
+\begin{prototype}
+psPtr psAlloc(size_t size);
+psPtr psRealloc(psPtr ptr, size_t size);
+void psFree(psPtr ptr);
+\end{prototype}
+%
+From the user's perspective, the functions \code{psAlloc},
+\code{psRealloc}, and \code{psFree} have identical semantics to the
+standard C library functions \code{malloc}, \code{realloc}, and
+\code{free}.  In fact, these functions should be implemented as C
+preprocessor macros which call the following private functions:
+%
+\begin{prototype}
+psPtr p_psAlloc(size_t size, const char *filename, unsigned int lineno);
+psPtr p_psRealloc(psPtr ptr, size_t size, const char *filename, unsigned int lineno);
+void p_psFree(psPtr ptr, const char *filename, unsigned int lineno);
+\end{prototype}
+%
+In these function calls, \code{size} is the number of bytes required
+to be allocated, \code{ptr} is the pointer to the allocated memory
+block, \code{file} is the file containing the calling function (set by
+\code{__FILE__}), and \code{line} is the calling function line number
+in the source-code file (set by \code{__LINE__}).  Because the user
+should only see the preprocessor versions (ie, \code{psAlloc}), we do
+not distinguish between these two in the following discussion.
+
+In order to enforce the use of the PSLib versions, the header file
+must take steps to ensure that code calling the functions
+\code{malloc}, \code{calloc}, \code{realloc}, or \code{free} will not
+compile.  This may be achieved by defining preprocessor macros which
+mask these functions with invalid statements (\eg{} \code{#define
+malloc(S) for}).  In exceptional cases, such as the memory management
+system itself, programmers may choose to override this prohibition by
+defining the symbol \code{PS_ALLOW_MALLOC}.  Application code will
+call \code{p_psAlloc}, \code{p_psRealloc}, or \code{p_psFree} via the
+macros defined above.
+
+The above restrictions must apply strictly to the code within PSLib, and other
+dependent software, such as e.g., PSModules, may choose to enforce the
+restrictions as well.  Software which employs PSLib should be allowed to
+optionally employ this enforcement or not, depending on which version of the
+global include file is chosen.
+  
+The functions \code{psAlloc} and \code{psRealloc} must never return a
+\code{NULL} pointer. If they are unable to provide the requested
+memory they must attempt to obtain the desired memory by calling the
+routine registered by \code{psMemExhaustedCallbackSet} (see
+\S\ref{secMemAdvanced}), and if still unsuccessful, call
+\code{psAbort()}.  The same behavior is true for constructors of rich
+structures, with names of the form \code{psFooAlloc}.
+
+Note that we have not specified an equivalent of the \code{calloc}
+function.  The \code{calloc} function provides two aspects which
+\code{malloc} originally did not: aligned memory and inialization.
+Neither of these are required: under POSIX, \code{malloc} is required
+to be aligned.  Also, for all structures it is necessary to explicity
+define the initialization independently since a byte value of 0 is
+usually insufficient.
+
+\begin{prototype}
+void psMemSetDeallocator(psPtr ptr, psFreeFunc freeFunc);     
+psFreeFunc psMemGetDeallocator(const psPtr ptr);     
+\end{prototype}
+
+A free function handles any deallocation procedures of an object
+referred to by a pointer, excluding the freeing of the memory block of
+the pointer reference itself.  If the pointer refers to an object that
+references other memory blocks this free function would insure the
+freeing of any encapsulated memory block references. For example,
+\code{psList} references a series of node objects of a linked list, so
+its free function would free these node objects and the node object's
+free function would free the data element references they may contain.
+
+The fucntion \code{psMemSetDeallocator} is used to associated a free
+function to a memory block, while \code{psMemGetDeallocator} retrieves
+the last free function set.  To remove a free function from a memory
+block, the \code{psMemSetDeallocator} should be invoked with
+\code{NULL}.  If no free function is set, \code{psMemGetDeallocator}
+shall return \code{NULL}.
+
+\paragraph{Negative allocations}
+
+Note that we have specified that the memory size is unsigned
+(\code{size_t}), so that we can address the full range of memory that
+the architecture will allow, and to match the behaviour of the system
+\code{malloc}.  This creates the potential for problems if a negative
+value is inadvertently passed to \code{psAlloc} --- it will be
+interpreted as a very large positive value.  To guard against this, we
+specify that \code{psAlloc} must check that the allocation is less
+than \code{PS_MEM_LIMIT} (a preprocessor variable).
+
+For array-like collections (specifically, \code{psArray},
+\code{psPixels}, \code{psVector}, and \code{psImage}) we allow the
+user to refer to a negative index in the accessor (e.g.,
+\code{psArrayGet}) to mean address from the end.  Consequently, the
+number of elements in structures should be signed (in order to be able
+to access the full range of allocated values).  It is the
+responsibility of these structure allocators (e.g.,
+\code{psArrayAlloc}) to check that the requested number of elements is
+not negative (calling \code{psAbort} otherwise).  All other allocators
+shall simply use \code{size_t} where the number of elements is needed
+(saving the trouble of checking before passing to psAlloc).
+
+\subsubsection{Callback Routines}
+
+The PSLib memory management system uses callback functions to handle
+certain errors and trace conditions.  The callbacks are registered by
+the programmer and called by the basic memory management functions if
+needed.  The four callbacks currently defined are called in the
+following situations:
+%
+\begin{itemize}
+\item when insufficient memory is available (\code{psMemExhaustedCallback})
+\item when a memory block is found to be corrupted (\code{psMemProblemCallback})
+\item when a specified memory ID is allocated (\code{psMemAllocateCallback})
+\item when a specified memory ID is freed (\code{psMemFreeCallback})
+\end{itemize}
+%
+The callback functions are defined in terms of specific callback
+types, specified below.  The callbacks are set using functions with
+names of the form \code{psCallbackSet}.  In all cases, the \code{Set}
+routine takes a pointer to the desired callback function and returns a
+pointer to the one that was previously installed. The default
+functions for each of these callbacks is listed below:
+%
+\begin{itemize}
+\item \code{psMemExhaustedCallbackDefault}
+\item \code{psMemProblemCallbackDefault}
+\item \code{psMemAllocCallbackDefault}
+\item \code{psMemFreeCallbackDefault}
+\end{itemize}
+%
+and have the behavior of immediately returning \code{NULL}, as if the
+callback had been skipped.  If the function pointer passed to the
+functions above is \code{NULL}, the callback must be reset to the
+default callback function.  The named default callbacks may be used in
+within a debugger to catch these conditions by breaking when the
+functions are called.  We discuss the use of each of the four
+callbacks below.
+
+\subsubsubsection{\tt psMemExhaustedCallback}
+
+If not enough memory is available to satisfy a request by
+\code{psAlloc} or \code{psRealloc}, these functions attempt to find an
+alternative solution by calling the \code{psMemExhaustedCallback}, a
+function which may be set by the programmer in appropriate
+circumstances, rather than immediately fail.  The typical use of such
+a feature may be when a program needs a large chunk of memory to do an
+operation, but the exact size is not critical.  This feature gives the
+programmer the opportunity to make a smaller request and try again,
+limiting the size of the operating buffer.  This callback has the
+following form:
+%
+\begin{datatype}
+typedef psPtr (*psMemExhaustedCallback)(size_t size);
+\end{datatype}
+
+\begin{prototype}
+psMemExhaustedCallback psMemExhaustedCallbackSet(psMemExhaustedCallback func);
+\end{prototype}
+%
+The callback function is called with the attempted size and is
+expected to return a pointer to the allocated memory or \code{NULL}.
+Until the callback function is set with
+\code{psMemExhaustedCallbackSet}, or if this callback is set to NULL,
+\code{psAlloc} immediately calls \code{psAbort}.
+
+\subsubsubsection{\tt psMemProblemCallback}
+
+At various occasions, the memory manager can check the state of the
+memory stack.  If any of these checks discover that the memory stack
+is corrupted, the \code{psMemProblemCallback} is called.  The callback
+has the following form:
+%
+\begin{datatype}
+typedef void (*psMemProblemCallback)(psMemBlock *ptr, const char *filename, unsigned int lineno);
+\end{datatype}
+
+\begin{prototype}
+psMemProblemCallback psMemProblemCallbackSet(psMemProblemCallback func);
+\end{prototype}
+%
+This callback may be used to report the error and other status
+information.  No return value is accepted, and no specific operations
+are expected.  The callback is for informational purposes only.  Where
+practical and efficient, the memory manager must call the routine
+registered using \code{psMemProblemCallbackSet} whenever a corrupted block
+of memory is discovered.  For example, doubly-freed blocks can be
+detected by checking \code{psMemBlock.refCounter}.
+
+\subsubsubsection{\tt psMemAllocCallback \& psMemFreeCallback}
+
+Two private variables, \code{p_psMemAllocID} and
+\code{p_psMemFreeID}, can be used to trace the allocation and freeing
+of specific memory blocks.  If the first (\code{p_psMemAllocID}) is
+set and a memory block with that ID is allocated, the corresponding
+callback is called just before memory is returned to the calling
+function.  If the second (\code{p_psMemFreeID}) is set and the memory
+block with the ID is about to be freed, the corresponding callback is
+called just before the memory block is freed.  These variables are
+internal and private to the memory manager and should be set using the
+following two functions:
+%
+\begin{prototype}
+psMemId psMemAllocCallbackSetID(psMemId id);
+psMemId psMemFreeCallbackSetID(psMemId id);
+\end{prototype}
+%
+The corresponding callback functions have the following form:
+%
+\begin{datatype}
+typedef psMemId (*psMemAllocCallback)(const psMemBlock *ptr);
+typedef psMemId (*psMemFreeCallback)(const psMemBlock *ptr);
+\end{datatype}
+
+and are set with the following functions:
+
+\begin{prototype}
+psMemAllocCallback psMemAllocCallbackSet(psMemAllocCallback func);
+psMemFreeCallback psMemFreeCallbackSet(psMemFreeCallback func);
+psMemId psMemGetId(void);
+psMemId psMemGetLastId(void);
+\end{prototype}
+%
+The callback functions are called with a pointer to the corresponding
+memory block.  The routines \code{psMemFreeCallbackIDSet} and
+\code{psMemAllocCallbackIDSet} accept the desired ID value and return
+the old value to the user.  The return values of the handlers
+installed by \code{psMemAllocCallbackSet} and
+\code{psMemFreeCallbackSet} are used to increment the values of
+\code{p_psMemAllocID} and \code{p_psMemFreeID} respectively.  For
+example, a return value of \code{0} implies that the value is
+unchanged; if the value is \code{2} the callback will be called again
+when the memory ID counter has increased by two.  This functionality
+may be useful to check, for example, every 100th block allocated.  The
+function \code{psMemGetId} returns the next identification number to
+be assigned to a memory block, while \code{psMemGetLastId} returns the
+last identification number to be assigned.  These functions can be
+used to guide the choice of ID set with the functions above.
+
+\subsubsection{Memory Tracing and Corruption Checks}
+\hlabel{secMemAdvanced}
+
+The PSLib memory management system includes features to facilitate
+tracing the memory allocation and freeing process and to debug memory
+errors in the calling code.  The types and function prototypes for
+this part of the memory API are shown below.
+%
+\begin{prototype}
+int psMemCheckLeaks(psMemId id0, psMemBlock ***array, FILE *fd, bool persistence);
+int psMemCheckCorruption(bool abort_on_error);
+\end{prototype}
+%
+The routine \code{psMemCheckLeaks} may be used to check for memory
+leaks. The return value is the number of blocks that have been
+allocated but not freed.  Only blocks with \code{psMemBlock.id}
+greater than \code{id0} are checked; this allows the user to ignore
+blocks allocated by initialization routines.
+
+If the argument \code{array} is non-\code{NULL}, then \code{**array}
+is set to an array of pointers to \code{psMemBlock} when the function
+returns.  These pointers represent the blocks which have been
+allocated but not freed.  It is the caller's responsibility to free
+this array with \code{psFree}. Also note that \code{**array} should be
+\code{NULL} (or not point to allocated memory) upon entering the call
+or the corresponding memory reference will be lost.
+
+If the argument \code{fd} is non-\code{NULL}, a one-line summary
+of each block that has been allocated but not freed is written to that
+file descriptor.
+
+If \code{persistence} is \code{false}, then only those
+\code{psMemBlock}s not marked as \code{persistent} shall be considered
+as memory leaks.  If \code{persistence} is \code{true}, then all
+\code{psMemBlocks} shall be considered as memory leaks.
+
+The routine \code{psMemCheckCorruption} checks the entire heap for
+corruption, calling the routine registered with
+\code{psMemProblemCallbackSet} for each block detected as being corrupted.
+The return value is the number of corrupted blocks detected. If the
+argument \code{abort_on_error} is true, \code{psMemCheckCorruption}
+must call \code{psAbort} as soon as memory corruption is detected.
+
+\begin{prototype}
+size_t psMemStats(const bool print, size_t *allocated, size_t *persistent, size_t *freelist);
+\end{prototype}
+
+\code{psMemStats} returns the total amount of memory consumed by
+psLib.  If \code{print} is \code{true}, also prints a summary of the
+memory usage.  If \code{allocated}, \code{persistent} or
+\code{freelist} are non-\code{NULL}, the function returns in those
+variables the amount of memory that's allocated and non-persistent,
+allocated and persistent, or waiting to be recycled, respectively.
+
+
+\subsubsection{Reference Counting}
+\hlabel{secMemRefcounter}
+
+As mentioned above, the memory management system provides the
+\code{refCounter} element in \code{psMemBlock} to allow for the
+management of multiple references to the same block of memory.
+External APIs which make use of this structure must increment the
+counter for every additional reference to an allocated memory block,
+and decrement it when those references are removed.  The memory
+management routines respect the use of the \code{refCounter} field:
+\code{psFree} will not free a block for which \code{refCounter} is not
+1, but shall decrement the \code{refCounter}, and \code{psAlloc} will
+initialize the field to 1.  However, these functions do not (and
+cannot practically) enforce the use of the counters; this is a
+requirement of external APIs which intend to use the feature.
+
+Several APIs are provided to manage the reference counters.  These
+APIs are:
+%
+\begin{prototype}
+psReferenceCount psMemGetRefCounter(const psPtr ptr);
+psPtr psMemIncrRefCounter(const psPtr ptr);
+psPtr psMemDecrRefCounter(psPtr ptr);
+psPtr psMemSetRefCounter(psPtr ptr, psReferenceCount count);
+\end{prototype}
+%
+The functions all take a pointer to the start of a user block of
+memory.  The first simply returns the value of the reference counter.
+If \code{vptr} is \code{NULL}, this function must return a value of
+NULL.  The next two functions increment or decrement the reference
+counter, returning the pointer which was passed in.  The final
+function explicitly sets the value of the reference counter (and
+should therefore be used sparingly, if ever!).  These functions must
+validate the memory pointer by determining the corresponding
+\code{psMemBlock.id} and checking for consistency in the internal
+memory block table (the table pointer for \code{psMemBlock.id} must be
+in the valid range and must correspond to the address of the
+\code{psMemBlock}).
+
+\subsubsection{Thread safety}
+
+Locking a mutex is an expensive operation that can dramatically impact
+the efficiency of a program for the worse.  When the user is not
+concerned with multiple threads, this is a needless waste, and so we
+specify that the thread safety in the memory management system may
+be deactivated (and activated) dynamically.
+
+\begin{prototype}
+bool psMemSetThreadSafety(bool safe);
+\end{prototype}
+
+\code{psMemThreadSafety} shall turn on thread safety in the memory
+management functions if \code{safe} is \code{true}, and deactivate all
+mutex locking in the memory management functions if \code{safe} is
+\code{false}.  The function shall return the previous value of the
+thread safety.
+
+\begin{prototype}
+bool psMemGetThreadSafety(void);
+\end{prototype}
+
+\code{psMemGetThreadSafety} shall return the current state of thread safety in the memory management system.
+
+Note that the default behaviour of the library shall be for the
+locking to be performed.
+
+\subsubsection{Relation of Memory Management to Structures}
+\label{sec:free}
+
+Within PSLib and throughout the Pan-STARRS project, we specify a
+variety of rich data structures.  The IPP Software Requirements
+Specification states that structures should be defined with
+corresponding constructors and destructors.  The destructors are
+private functions used only by the memory management system.
+Instances of, for example, \code{psSomeType} should be constructed using
+\code{psSomeTypeAlloc()} calls, and are destroyed using the basic
+\code{psFree} function, which calls \code{p_psSomeTypeFree()} to free the
+components of the structure, but leaves the task of freeing the
+structure to \code{psFree}.  The allocator will allocate the required
+memory with \code{psAlloc} and increment the appropriate
+\code{refCounter}.
+
+The existence of complicated structures which include pointers to
+other structures require that we lay out a rule regarding destructors
+and reference counters.  Simply put, \textit{the destructor for every
+structure should only free the structure if the \code{refCounter ==
+1}; otherwise, it decrements the reference counter and returns.}
+
+\subsubsection{Conventions adopted for pointers}
+
+Only pointers to memory allocated with the PSLib memory functions are
+compatible with the various PSLib container types (e.g., \code{psList,
+psMetadata}), because the functions working with the container types
+search for the attached \code{psMemBlock}.  If a pointer to memory
+allocated with another memory system (e.g., the system \code{malloc}),
+or generated by offsetting from another pointer that was allocated
+with \code{psAlloc}, is used with PSLib, the PSLib functions would
+falsely determine that memory is corrupted, because of the missing
+\code{psMemBlock}.
+
+To pilot our way through the potential confusion, instead of calling
+all pointers (of unspecified type) a ``\code{void*}'', we adopt a
+convention, both in this document and to be used in the source, of
+referring to a pointer that has a \code{psMemBlock} attached as a
+\code{psPtr}:
+\begin{datatype}
+typedef void* psPtr;
+\end{datatype}
+
+For the same reason, we also adopt a convention of referring to a string
+that has a \code{psMemBlock} attached as a \code{psString}:
+\begin{datatype}
+typedef char* psString;
+\end{datatype}
+
+That is, \code{psPtr} is used in cases where the function requires the
+use of a pointer of unspecified type allocated with \code{psAlloc},
+and \code{psString} is used in cases where the function requires the
+use of a \code{char*} allocated with \code{psAlloc} (e.g., via
+\code{psStringCopy} or \code{psStringNCopy}).
+
+\subsubsection{Strings}
+
+The use of strings within the PSLib memory management system requires
+that they be allocated with \code{psAlloc}.  This means that
+substrings cannot be placed on containers such as \code{psArray} or
+\code{psMetadata}, since these check for the existence of the attached
+\code{psMemBlock}.  To get around this, we specify functions that copy
+a string into \code{psAlloc}-ed memory:
+\begin{prototype}
+psString psStringAlloc(long nChar);
+psString psStringCopy(const char *string);
+psString psStringNCopy(const char *string, unsigned int nChar);
+\end{prototype}
+
+\code{psStringAlloc} shall allocate a \code{psString} of length
+\code{nChar}.  \code{psStringCopy} shall perform a deep copy of the
+\code{string} into a new \code{psString}.  \code{psStringNCopy} shall
+do the same, up to a maximum of \code{nChar} characters.  Both copy
+functions shall ``pass through'' \code{NULL} values without generating
+an error.
+
+We also specify two useful functions:
+
+\begin{prototype}
+ssize_t psStringAppend(char **dest, const char *format, ...);
+ssize_t psStringPrepend(char **dest, const char *format, ...);
+ssize_t psStringAppendV(char **dest, const char *format, va_list ap);
+ssize_t psStringPrependV(char **dest, const char *format, va_list ap);
+\end{prototype}
+
+\code{psStringAppend} shall append, according to the \code{format}
+values into the \code{dest} string.  \code{dest} shall be allocated if
+\code{NULL}, and the length of the new string (excluding the
+terminator) shall be returned.  \code{psStringPrepend} shall do
+similarly, except it shall prepend to the \code{dest} string.
+\code{psStringAppendV} and \code{psStringPrependV} are the var-args
+versions of these functions.
+
+Other string manipulation functions are listed below.
+
+\begin{prototype}
+psList *psStringSplit(const char *string, const char *splitters, bool multipleAreSignificant);
+psArray *psStringSplitArray(const char *string, const char *splitters, bool multipleAreSignificant);
+\end{prototype}
+
+\code{psStringSplit} shall split the input \code{string} into a
+\code{psList} of \code{psStrings}.  The \code{string} is split at any
+one of the characters in \code{splitters}.  Split strings of zero
+length should not be included in the output list if
+\code{multipleAreSignificant} is \code{true}.
+\code{psStringSplitArray} does the same, but returns the result as a
+\code{psArray}.
+
+String whitespace from head and tail of string:
+\begin{prototype}
+size_t psStringStrip(char *string);
+\end{prototype}
+
+Substitute a \code{key} with \code{replace} in the \code{input} string:
+\begin{prototype}
+psString psStringSubstitute(psString input, const char *replace, const char *key);
+\end{prototype}
+
+
+\subsubsection{Fixed-Length Lines}
+
+We define the \code{psLine} structure to carry a pre-allocated block
+to store a character string.  The interfaces are similar to
+\code{psString}, but allow for the container to be allocated initially
+upfront with a specified length.  They are used to force a
+fixed-length line.
+
+\begin{datatype}
+// structure to carry a dynamic string
+typedef struct {
+    long NLINE;			// allocated length
+    long Nline;			// current length
+    psString line;		// character string data
+} psLine;
+\end{datatype}
+
+The following function allocates a line object of length Nline.
+\begin{prototype}
+psLine *psLineAlloc(long Nline);
+\end{prototype}
+
+The following function initializes or re-initializes the line, setting
+the current length to zero and setting the string data values to 0.
+If the function is passed \code{NULL}, the function returns \code{false}.
+\begin{prototype}
+bool psLineInit(psLine *line);
+\end{prototype}
+
+The following function appends a string to a line segment, returning
+\code{false} if the new segment would overflow the allocated line
+length.
+\begin{prototype}
+bool psLineAdd(psLine *line, const char *format, ...);
+\end{prototype}
+
+\subsubsection{File Input}
+
+To simplify the process of reading a file, we define:
+\begin{prototype}
+psString psSlurpFD(int fd);
+psString psSlurpFile(FILE *stream);
+\end{prototype}
+
+\code{psSlurpFD} shall read the nominated file descriptor, \code{fd},
+and return the contents as a \code{psString}.  \code{psSlurpFile} does
+similarly, but based on a file \code{stream}.
+
+\subsubsection{Type information}
+
+We need to be able to specify two different sorts of types.
+
+The first, for use with math structures (\S\ref{sec:mathStructures}),
+defines a numerical type; e.g., short integer, double-precision
+floating point, etc:
+
+\begin{datatype}
+typedef enum {
+    PS_TYPE_S8      = 0x0101,           ///< Character
+    PS_TYPE_S16     = 0x0102,           ///< Short integer
+    PS_TYPE_S32     = 0x0104,           ///< Integer
+    PS_TYPE_S64     = 0x0108,           ///< Long integer
+    PS_TYPE_U8      = 0x0301,           ///< Unsigned character
+    PS_TYPE_U16     = 0x0302,           ///< Unsigned short integer
+    PS_TYPE_U32     = 0x0304,           ///< Unsigned integer
+    PS_TYPE_U64     = 0x0308,           ///< Unsigned long integer
+    PS_TYPE_F32     = 0x0404,           ///< Floating point
+    PS_TYPE_F64     = 0x0408,           ///< Double-precision floating point
+    PS_TYPE_C32     = 0x0808,           ///< Float complex
+    PS_TYPE_C64     = 0x0810,           ///< Double complex
+    PS_TYPE_BOOL    = 0x1301            ///< Boolean value
+} psElemType;
+\end{datatype}
+
+
+The second, primarily for use with the metadata
+(\S\ref{sec:metadata}), defines a data structure; e.g., list, array,
+FITS file:
+
+\begin{datatype}
+typedef enum {                         ///< type of item.data is:
+    PS_DATA_S8      = PS_TYPE_S8,      ///< psS8
+    PS_DATA_S16     = PS_TYPE_S16,     ///< psS16
+    PS_DATA_S32     = PS_TYPE_S32,     ///< psS32
+    PS_DATA_S64     = PS_TYPE_S64,     ///< psS64
+    PS_DATA_U8      = PS_TYPE_U8,      ///< psU8
+    PS_DATA_U16     = PS_TYPE_U16,     ///< psU16
+    PS_DATA_U32     = PS_TYPE_U32,     ///< psU32
+    PS_DATA_U64     = PS_TYPE_U64,     ///< psU64
+    PS_DATA_F32     = PS_TYPE_F32,     ///< psF32
+    PS_DATA_F64     = PS_TYPE_F64,     ///< psF64
+    PS_DATA_BOOL    = PS_TYPE_BOOL,    ///< psBool
+    PS_DATA_STRING  = 0x10000,         ///< String (char *)
+    PS_DATA_ARRAY,                     ///< psArray
+    PS_DATA_BITSET,                    ///< psBitSet
+    PS_DATA_CUBE,                      ///< psCube
+    PS_DATA_FITS,                      ///< psFits
+    PS_DATA_HASH,                      ///< psHash
+    PS_DATA_HISTOGRAM,                 ///< psHistogram
+    PS_DATA_IMAGE,                     ///< psImage
+    PS_DATA_KERNEL,                    ///< psKernel
+    PS_DATA_LINE,                      ///< psLine
+    PS_DATA_LIST,                      ///< psList
+    PS_DATA_LOOKUPTABLE,               ///< psLookupTable
+    PS_DATA_METADATA,                  ///< psMetadata
+    PS_DATA_METADATAITEM,              ///< psMetadataItem
+    PS_DATA_MINIMIZATION,              ///< psMinimization
+    PS_DATA_PIXELS,                    ///< psPixels
+    PS_DATA_PLANE,                     ///< psPlane
+    PS_DATA_PLANEDISTORT,              ///< psPlaneDistort
+    PS_DATA_PLANETRANSFORM,            ///< psPlaneTransform
+    PS_DATA_POLYNOMIAL1D,              ///< psPolynomial1D
+    PS_DATA_POLYNOMIAL2D,              ///< psPolynomial2D
+    PS_DATA_POLYNOMIAL3D,              ///< psPolynomial3D
+    PS_DATA_POLYNOMIAL4D,              ///< psPolynomial4D
+    PS_DATA_PROJECTION,                ///< psProjection
+    PS_DATA_REGION,                    ///< psRegion
+    PS_DATA_SCALAR,                    ///< psScalar
+    PS_DATA_SPHERE,                    ///< psSphere
+    PS_DATA_SPHEREROT,                 ///< psSphereRot
+    PS_DATA_SPLINE1D,                  ///< psSpline1D
+    PS_DATA_STATS,                     ///< psStats
+    PS_DATA_TIME,                      ///< psTime
+    PS_DATA_VECTOR,                    ///< psVector
+    PS_DATA_UNKNOWN,                   ///< Other data of an unknown type
+    PS_DATA_METADATA_MULTI             ///< Used internally for metadata; not a 'real' type
+} psDataType;
+\end{datatype}
+
+Here we have included every type of structure used in PSLib that we expect will
+be frequently carried around in containers.  If applicable, the value should
+corespond to the same type as represented in \code{psElemType}.
+
+The other values are offset from these ``primitive'' types so that
+they can be identified easily.  A macro,
+\code{PS_DATA_IS_PRIMITIVE(type)}, shall return \code{true} if the
+type is one of the numerical data types (S32, F32, F64, bool).  In
+such a case, the data value is directly available from the metadata.
+Otherwise, a pointer to the data is available.
+
+\code{PS_DATA_METADATA_MULTI} is used by the metadata, so the user
+should not use this type.
+
+\subsubsection{Type checking}
+
+Several of the collections contain data using a \code{void*} pointer.
+This makes it difficult to ensure that data coming off a collection is
+of a particular, desired type.  Nevertheless, there is a way of
+identifying the type of a pointer pulled off a collection --- the
+registered deallocator function stored in the \code{psMemBlock}.  We
+specify functions below, one for each of the major types in psLib,
+that check the type on a particular pointer, returning \code{true} if
+the \code{ptr} matches the desired type, as determined from the
+registered deallocator function.  These functions may be implemented
+as macros or inline functions if that is deemed convenient.
+
+\begin{prototype}
+bool psMemCheckArray(psPtr ptr);
+bool psMemCheckBitSet(psPtr ptr);
+bool psMemCheckCube(psPtr ptr);
+bool psMemCheckFits(psPtr ptr);
+bool psMemCheckHash(psPtr ptr);
+bool psMemCheckHistogram(psPtr ptr);
+bool psMemCheckImage(psPtr ptr);
+bool psMemCheckKernel(psPtr ptr);
+bool psMemCheckLine(psPtr ptr);
+bool psMemCheckList(psPtr ptr);
+bool psMemCheckLookupTable(psPtr ptr);
+bool psMemCheckMetadata(psPtr ptr);
+bool psMemCheckMetadataItem(psPtr ptr);
+bool psMemCheckMinimization(psPtr ptr);
+bool psMemCheckPixels(psPtr ptr);
+bool psMemCheckPlane(psPtr ptr);
+bool psMemCheckPlaneDistort(psPtr ptr);
+bool psMemCheckPlaneTransform(psPtr ptr);
+bool psMemCheckPolynomial1D(psPtr ptr);
+bool psMemCheckPolynomial2D(psPtr ptr);
+bool psMemCheckPolynomial3D(psPtr ptr);
+bool psMemCheckPolynomial4D(psPtr ptr);
+bool psMemCheckProjection(psPtr ptr);
+bool psMemCheckRegion(psPtr ptr);
+bool psMemCheckScalar(psPtr ptr);
+bool psMemCheckSphere(psPtr ptr);
+bool psMemCheckSphereRot(psPtr ptr);
+bool psMemCheckSpline1D(psPtr ptr);
+bool psMemCheckStats(psPtr ptr);
+bool psMemCheckString(psPtr ptr);
+bool psMemCheckTime(psPtr ptr);
+bool psMemCheckVector(psPtr ptr);
+\end{prototype}
+
+For user convenience, we also specify a one-stop shop which simply
+executes the appropriate \code{psMemCheckWhatever} function according
+to the \code{type}:
+
+\begin{prototype}
+bool psMemCheckType(psDataType type, psPtr ptr);
+\end{prototype}
+
+The reason for having functions that check the type instead of
+returning the type is that the check is quick and the result is clean,
+while returning the type involves descending through a case statement.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%
+%%% Not sure we need these --- all we do is wrap pthread_mutex, so why bother?
+%%%
+
+%% \subsection{Locks}
+%% 
+%% We define the following conveniences:
+%% \begin{datatype}
+%% typedef pthread_mutex_t psMutex;
+%% \end{datatype}
+%% 
+%% \begin{prototype}
+%% psMutex *psMutexAlloc(void);
+%% bool psMutexLock(psMutex *mutex);
+%% bool psMutexUnlock(psMutex *mutex);
+%% \end{prototype}
+%% 
+%% \code{psMutex} simply wraps a POSIX thread mutex (this is necessary in
+%% order to use the PS memory management system).
+%% 
+%% \code{psMutexAlloc} shall allocate memory for a \code{psMutex}, and
+%% initialise the mutex.  \code{psMutexLock} shall lock the \code{mutex},
+%% and \code{psMutexUnlock} shall unlock the \code{mutex}.
+%% 
+%% These functions, in the interests of speed, should be implemented as
+%% preprocessor macros.
+%% 
+%% These functions and the \code{void *lock} in the collection structures
+%% and  \code{psImage} should  only  be defined  if \code{_REENTRANT}  is
+%% defined; otherwise the functions should be poisoned.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Tracing and Logging}
+
+This section defines the \PS{} Tracing and Logging APIs; the former
+refers to debug information that we wish to be able to turn on and off
+without recompiling (it will \emph{not} be available in production
+code); the latter means information about the processing that must be
+collected and saved, even in the production system.  We envision that
+extensive use will be made of \code{psTrace} throughout the \PS{}
+code.
+
+\subsubsection{Tracing APIs}
+\hlabel{psTrace}
+
+A code-tracing facility should allow the programmer to place messages
+in the code which, when called, will print some useful information
+about the containing block of code.  Ideally, different messages may
+be specified to have different levels (of severity or interest).  For
+a given run of the program, the level of interest should be set to
+provide more or less feedback, depending on the needs of the
+programmer.  In a typical situation, low-level messages would be
+placed generously throughout the code, indicating the flow of the
+program.  Higher-level messages would be placed in a limited number of
+special locations, such as the start of major code segments or where a
+particularly unusual condition is met.  Top-level messages would be
+placed in code triggered under serious error conditions.  A normal run
+of the program would have the trace messages printed only for the
+top-level.  If the user needs to dig deeper into the code, the trace
+level should be set lower, and the more detailed messages could be
+examined.  In a case of a serious, poorly-understood problem with the
+code, the trace threshold would be placed to the bottom and the
+lowest-level step-by-step messages would be printed.
+
+The PSLib tracing facility provides the above functionality, along
+with the ability to assign different trace levels to messages from
+different software components.  Each trace message, when placed in the
+code, is assigned to be part of a specific tracing 'facility', defined
+in more detail below.  The trace level for that specific message is
+also set when the message is placed.  Each facility may have its trace
+level set independently.  Thus, it is possible to request detailed
+trace output for one facility while minimizing the verbosity of the
+trace output from the rest of the program.
+
+The trace facilities consist of a hierarchy of names.  A trace
+facility is defined by a string consisting of words separated by dots,
+with a hierarchy equivalent to that of UNIX directory names.  The
+top-level facility is simply \code{'.'} (one dot).  The next level
+would be \code{'.A'}, followed by \code{.A.B}, and so on.  The
+relationship is seen in two ways.  First, a facility inherits the
+trace level of its parent unless explicitly specified.  Second, the
+hierarchy is used to format the listing of the trace facilities so
+that they are easy to read.  The first of these rules provides a
+mechanism to define the default trace levels for any facility even if
+it has not been registered explicitly since all named facilities are
+implicitly children of the top level facility (\code{.}).  The second
+rule is simply an organizational technique to make the listing of
+facility information clear.  In specifying a facility, the leading
+dot shall be optional, as a convenience to the user.
+
+The API to place a trace message in the code, and simultaneously set
+its trace level and facility, is:
+%
+\begin{prototype}
+void psTrace(const char *facil, int level, const char *format,...);
+void psTraceV(const char *facil, int level, const char *format, va_list ap);
+\end{prototype}
+% 
+where the \code{format} argument is a printf-style formatting code
+followed by possible arguments to that formatting statement, to be
+implemented using the \code{vprintf} functions.  This command
+specifies the name of the facility to which the message belongs
+(\code{facil}), the trace level for this message in that facility
+(\code{level}) and the message itself.  The \code{psTraceV} version of
+the command accepts a \code{va_list} argument list rather than a
+variable number of arguments.
+
+The trace level for any facility may be set at any time with the
+following function:
+%
+\begin{prototype}
+int psTraceSetLevel(const char *facil, int level);
+\end{prototype}
+% 
+where \code{level} specifies the current trace level for the facility
+named by \code{facil}.  The function returns the previous trace level
+for that facility.  The currently defined trace level for a given
+facility may be determined by the function:
+%
+\begin{prototype}
+int psTraceGetLevel(const char *facil);
+\end{prototype}
+% 
+which returns the trace level of the named facility following the
+rules specified above.  A specified trace message (identified by
+\code{psTrace}) must be printed if and only if
+\code{psTraceGetLevel(facil)} returns a value greater than or equal to
+the value of \code{level} for that message.  That is, a larger
+number for the trace level corresponds to lower-level statements, and
+hence is more verbose.
+
+PSLib includes a utility function for examining the current tracing
+levels of all facilities: 
+%
+\begin{prototype}
+void psTracePrintLevels(void);
+\end{prototype}
+%
+This function prints the hierarchy of trace facilities along with the
+current trace level for each facility.  For example, a particular
+program may have a few facilities defined, along with their trace
+levels.  A call to \code{psTracePrintLevels} may produce a listing
+which appears as:
+\begin{verbatim}
+.                        0
+ .IPP                    0
+ .IPP.debias             1
+ .IPP.flatten            1
+  .IPP.flatten.divide    2
+  .IPP.object.findpeak   1
+  .IPP.object.getsky     1
+\end{verbatim}
+
+Considering the same program, the programmer might place a variety of
+trace messages throughout the \code{flatten} portion of the code with
+different types of messages, such as:
+%
+\begin{verbatim}
+psTrace("IPP.flatten", 2, "starting flatten function\n");
+psTrace("IPP.flatten", 0, "flat-field image is \%s\n", filename);
+psTrace("IPP.flatten.divide", 2, "doing the divide\n");
+psTrace("IPP.flatten.divide", 3, "trying the loop, i = \%d\n", i);
+psTrace("IPP.flatten.divide", 1, "got an invalid pixel value (NaN) at \%d,\%d\n");
+psTrace("IPP.flatten.divide", 2, "divide is done\n");
+\end{verbatim}
+%
+Under the trace levels set above, if the code actually reached each of
+these trace messages, the following messages would be printed:
+%
+\begin{verbatim}
+flat-field image is foo.fits
+  doing the divide
+  got an invalid pixel value (NaN) at 500,20
+  divide is done
+\end{verbatim}
+%
+while these two would not be printed because their facility level was
+too low:
+%
+\begin{verbatim}
+  starting flatten function
+   trying the loop, i = 0   
+   trying the loop, i = 1   
+   trying the loop, i = 2   
+...
+\end{verbatim}
+%
+
+The availability of the tracing facility at run-time, must be decided
+at compilation: If the C pre-processor macro \code{PS_NO_TRACE} is
+defined, all trace code must be replaced by empty space so that none
+of the code is compiled.  This can be implemented via macro front-ends
+to private versions of the user APIs.  In addition, a function
+\code{psTraceReset} will free memory used by \code{psTrace}
+functions, effectively resetting all trace levels to 0:
+\begin{prototype}
+void psTraceReset(void);
+\end{prototype}
+
+The trace may optionally be written to a file or other output
+destination with \code{psTraceSetDestination}:
+\begin{prototype}
+bool psTraceSetDestination(int fd);
+\end{prototype}
+If the \code{fd} is zero, then tracing is disabled.  Otherwise, the
+trace is sent to the specified file descriptor.  As a convenience, the
+following are defined:
+\begin{datatype}
+enum {
+    PS_TRACE_TO_NONE = 0,                ///< turn off all traces
+    PS_TRACE_TO_STDOUT = 1,              ///< trace to system's stdout
+    PS_TRACE_TO_STDERR = 2,              ///< trace to system's stderr
+};
+\end{datatype}
+%
+This arrangement mirros the file descriptors for standard input,
+output and error.  A call to \code{psTraceSetDestination}
+automatically closes an open file descriptor.
+
+The corresponding function
+\begin{prototype}
+int psTraceGetDestination(void);
+\end{prototype}
+returns the current trace destination file descriptor.  If the
+destination has not been defined by the user, the descriptor for
+\code{stdout} is returned.
+
+The trace output format is controlled with the function:
+%
+\begin{prototype}
+bool psTraceSetFormat(const char *format);
+\end{prototype}
+%
+which expects a string consisting of the letters \code{H} (host),
+\code{L} (level), \code{M} (message), \code{N} (name), \code{F}
+(file:name), and \code{T} (time).  The default is \code{THLNM}, which
+produces trace messages in the form:
+\begin{verbatim}
+YYYY-MM-DD hh:mm:ssZ | hostname | L | name | file:line
+    The message goes here
+    and is indented by 4 spaces.
+\end{verbatim}
+where \code{YYYY}, \code{MM}, \code{DD}, \code{hh}, \code{mm}, and
+\code{ss} are the year, month (Jan is 01), day of the month, hours
+(0--23), minutes, and seconds when the trace message was received.
+Note that the timestamp is in ISO order, and that the timezone is GMT
+(hence the \code{Z}).  The \code{hostname} is returned by
+\code{gethostname}, \code{L} is the numerical level. The other two
+fields, \code{name} and \code{msg}, are the facility name and the
+complete message provided to \code{psTrace}.  The \code{msg} is placed
+on a new line (allowing the \code{name} to fill the rest of the
+previous line), with each line indented by 4 spaces.  An example
+message is:
+%
+\begin{verbatim}
+2004-02-24 20:14:18Z | alibaba.IfA.Hawaii.Edu | I | example.utils.helloWorld | example.c:20
+    Hello world,
+    it's me calling.
+\end{verbatim}
+%
+The possible order of the format entries is fixed and not determined
+by the order of the letters used in \code{psTraceSetFormat}.  Selecting
+an output format with fewer than the complete set of 5 entries simply
+removes those entries from the output messages.
+
+Specifying a \code{format} of \code{NULL} turns off logging (equivalent
+to calling \code{psTraceSetDestination(PS_TRACE_TO_NONE)}, whereas if the
+\code{format} is \code{""}, then the format reverts to the default.
+
+\subsubsection{Message Logging}
+\hlabel{psLogMsg}
+
+Message logging is similar in some respects to tracing.  Like trace
+messages, log messages are placed in the code at various locations to
+provide output describing the current state of the program.  Like
+the PSLib trace facility, a good log facility should have the
+capability of associating each message with an importance or severity
+level, and at any point, the level for which messages are actually
+printed should be set in a flexible manner.   Unlike trace messages,
+however, log messages are always part of the code and are available in
+the production version as well as in test versions.  
+
+The PSLib logging facility does not include the extensive facility
+levels which are provided by the trace facility.  Less control over
+the granularity is needed for the log messages than for the trace
+messages.  
+
+A log message is placed in the code with the command:
+%
+\begin{prototype}
+void psLogMsg(const char *name, int level, const char *format, ...); 
+void psLogMsgV(const char *name, int level, const char *format, va_list ap); 
+\end{prototype}
+where \code{name} is a word to describe the source of the message,
+\code{level} is the severity level of this message, and \code{format}
+is a printf-style formatting statement defining the actual message,
+and is followed by the arguments to the formatting code.  The second
+form, \code{psLogMsgV} is an equivalent command which takes a
+\code{va_list} argument.
+
+A log message may have any level specified in the range 0-9, though
+the first 4 levels are associated with symbolic names:
+%
+\begin{datatype}
+enum { PS_LOG_ABORT = 0, PS_LOG_ERROR, PS_LOG_WARN, PS_LOG_INFO };
+\end{datatype}
+%
+
+At any time, the program may set the current log level, the level
+above which log messages are ignored, using the function:
+%
+\begin{prototype}
+int psLogSetLevel(int level);           
+\end{prototype}
+%
+This function returns the previous log level.  A specific message
+invoked with \code{psLogMsg} is only printed if its value of
+\code{level} is less than the current value set by
+\code{psLogSetLevel}.  The default log level is set to
+\code{PS_LOG_INFO}.
+
+\begin{prototype}
+int psLogGetLevel();           
+\end{prototype}
+%
+This function returns the current log level.  
+
+Log messages are sent to the destination most recently set using:
+%
+\begin{prototype}
+bool psLogSetDestination(int fd);      
+\end{prototype}
+%
+If the \code{fd} is zero, then logging is disabled.  Otherwise, the
+log is sent to the specified file descriptor.  As a convenience, the
+following are defined:
+\begin{datatype}
+enum {
+    PS_LOG_TO_NONE = 0,                  ///< turn off logging
+    PS_LOG_TO_STDOUT = 1,                ///< log to system's stdout
+    PS_LOG_TO_STDERR = 2,                ///< log to system's stderr
+};
+\end{datatype}
+%
+This arrangement mirros the file descriptors for standard input,
+output and error.  A call to \code{psLogSetDestination} automatically
+closes an open file descriptor.
+
+The corresponding function
+\begin{prototype}
+int psLogGetDestination();
+\end{prototype}
+returns the current log destination file descriptor.  If the
+destination has not been defined by the user, the descriptor for
+\code{stdout} is returned.
+
+The output format is controlled with the function:
+%
+\begin{prototype}
+bool psLogSetFormat(const char *format);
+\end{prototype}
+%
+which expects a string consisting of the letters \code{H} (host),
+\code{L} (level), \code{M} (message), \code{N} (name), \code{F}
+(file:line), and \code{T} (time).  The default is \code{THLNM}, which
+produces log messages in the form:
+\begin{verbatim}
+YYYY-MM-DD hh:mm:ssZ | hostname | L | name | file:line
+    The message goes here
+    and is indented by 4 spaces.
+\end{verbatim}
+where \code{YYYY}, \code{MM}, \code{DD}, \code{hh}, \code{mm}, and
+\code{ss} are the year, month (Jan is 01), day of the month, hours
+(0--23), minutes, and seconds when the log message was received.  Note
+that the timestamp is in ISO order, and that the timezone is GMT
+(hence the \code{Z}).  The \code{hostname} is returned by
+\code{gethostname}, \code{L} is a character associated with the level
+(\code{A}, \code{E}, \code{W}, and \code{I} for \code{PS_LOG_ABORT},
+\code{PS_LOG_ERROR}, \code{PS_LOG_WARN}, and \code{PS_LOG_INFO}
+respectively. Other levels are represented numerically (\code{5}
+etc.). The other two fields, \code{name} and \code{msg}, are the
+arguments to \code{psLogMsg}.  The \code{msg} is placed on a new line
+(allowing the \code{name} to fill the rest of the previous line),
+with each line indented by 4 spaces.  An example message is:
+%
+\begin{verbatim}
+2004-02-24 20:14:18Z | alibaba.IfA.Hawaii.Edu | I | example.utils.helloWorld
+    Hello world,
+    it's me calling.
+\end{verbatim}
+%
+The possible order of the format entries is fixed and not determined
+by the order of the letters used in \code{psLogSetFormat}.  Selecting
+an output format with fewer than the complete set of 5 entries simply
+removes those entries from the output messages.
+
+Specifying a \code{format} of \code{NULL} turns off logging (equivalent
+to calling \code{psLogSetDestination(PS_LOG_TO_NONE)}, whereas if the
+\code{format} is \code{""}, then the format reverts to the default.
+
+The following utility opens an output file descriptor for use by the
+trace and log facilities. 
+\begin{prototype}
+int psMessageDestination (const char *dest);
+\end{prototype}
+%
+The destination string consists of a URL in the form
+\code{protocol:location}.  The \code{protocol} value may be
+\code{file}, to send the log to a local file named by the value of
+\code{location}.  Future expansion may allow the logger to send
+messages to an IP logger, with a protocol to be defined later.  Three
+other special values are allowed for the \code{dest} parameter
+(without specifying a protocol): \code{stderr} and \code{stdout},
+which return the file descriptors for \code{stderr} and \code{stdout}
+respectively, and \code{none} which returns the special descriptor to
+turn off logging.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Error Handling}
+\label{sec:errors}
+\hlabel{errorStack}
+
+\PS{} errors shall be raised using a function, \code{psError}, with the
+caller supplying a component name and error message.  It is desireable
+to be able to trace an error through the program so that the events
+that led to the error may be quickly and clearly identified.
+\code{psError} prints an error message and doesn't abort, but instead
+returns the error code.
+\begin{prototype}
+psErrorCode p_psError(const char *filename, 
+                      unsigned int lineno, 
+                      const char *func, 
+                      psErrorCode code,
+                      bool new, 
+                      const char *format, ...);
+\end{prototype}
+\begin{datatype}
+#define psError(code, new, format, ...) \
+    p_psError(__FILE__, __LINE__, __func__, code, new, format, __VA_ARGS__)
+\end{datatype}
+
+\code{psError} is a macro definition that allows the filename, line
+number and function name to be inputted to a private function,
+\code{p_psError}.  The \code{code} is an enumerated type which lists
+the possible \textit{classes} of errors (e.g. \code{PS_ERR_IO}) that
+\PS{} code can generate (see section~\ref{psErrorCodes}). The
+\code{new} argument takes a boolean which, if \code{true} specifies
+that the error was set initially at this location, and if \code{false}
+specifies that an error was passed to this location.  Raising new
+error should clear the error stack.  The final required argument,
+\code{format}, is a \code{printf}-style format that is passed to
+\code{psLogMsgV} with code \code{PS_LOG_ERROR}, and component equal to
+the concatenation of the file name and the line number, separated by a
+colon.  The result of a call to \code{psError} shall be to push an
+error onto a stack; this stack is cleared if \code{new} is true, or by
+a call to \code{psErrorClear}.
+
+The errors on the error stack are defined as the following:
+\begin{datatype}
+typedef struct {
+    char *name;                         ///< category of code that caused the error
+    psErrorCode code;                   ///< class of error (equivalent to errno)
+    char *msg;                          ///< the message associated with the error
+} psErr;
+\end{datatype}
+
+The last error reported is available from \code{psErrorLast}; if no
+errors are current, a non-\code{NULL} \code{psErr} shall be returned
+with code \code{PS_ERR_NONE}.  Previous errors on the stack shall be
+returned by \code{psErrorGet} (a value of \code{0} passed to
+\code{psErrorGet} is equivalent to a call to \code{psErrorLast}).
+The error stack may be completely cleared with \code{psErrorClear}.
+%
+\begin{prototype}
+unsigned int psErrorGetStackSize(void);
+const psErr *psErrorGet(int which);
+const psErr *psErrorLast(void);
+void psErrorClear(void);
+\end{prototype}
+
+\code{psErrorGetStackSize} shall return the number of errors on the
+stack.  The entire error stack may be printed to an open file
+descriptor by calling \code{psErrorStackPrint} (or
+\code{psErrorStackPrintV}); if and only if there are current errors,
+the printf-style string \code{format} is first printed to the file
+descriptor \code{fd}. In this printout, error codes shall be replaced
+by their string equivalents as defined in the next section.  Note that
+these are also available in the \code{psErr} structure. The successive
+lines of the traceback should be indented by an additional space.
+\code{psErrorStackPrintV} must not invoke \code{va_end}.
+%
+\begin{prototype}
+void psErrorStackPrint(FILE *fd, const char *format, ...);
+void psErrorStackPrintV(FILE *fd, const char *format, va_list va);
+\end{prototype}
+
+Any error \code{code}s less then or equal to \code{PS_ERR_BASE} (see
+next section) must be taken to be valid values of \code{errno}, and
+\code{psErrorStackPrint} must print the value returned by
+\code{strerror} if such error codes are encountered.
+
+\code{psErrorCodeLast} returns the last error code:
+\begin{prototype}
+psErrorCode psErrorCodeLast(void);
+\end{prototype}
+
+The routine \code{psErrorCodeString} returns the string associated
+with an error code:
+\begin{prototype}
+const char *psErrorCodeString(psErrorCode code);
+\end{prototype}
+
+\subsubsection{Error Codes}
+\hlabel{psErrorCodes}
+
+Both error codes for PSLib and error codes for projects that use PSLib
+may be registered.  In the former case, the error codes must be
+registered on initialisation (see \code{psLibInit}), whereas in the
+latter case, they must be explicitly registered by the programmer.
+
+\paragraph{Registering error codes}
+
+PSLib and any project needed to use PSLib must define the necessary
+error codes and associated message strings.  An array of error codes
+may be registered with the PSLib error handler using the function:
+\begin{prototype}
+void psErrorRegister(const psErrorDescription *errors, int errorCode);
+\end{prototype}
+where the errors are represented internally as follows:
+\begin{datatype}
+typedef struct {
+    psErrorCode code;                  ///< An error code
+    const char *description;           ///< the associated description
+} psErrorDescription;
+\end{datatype}
+PSLib internal errors must be registered with the function
+\code{psErrorRegister}, which should be called as part of the
+program initialization to set up the error codes.  It is left to the
+external project to produce their own error registration functions
+which must also be called during initialization. There is a clear need
+to coordinate the choice of error numbers.  It is expected that error
+code ranges for different projects must be managed by the Project
+Office within Pan-STARRS.
+
+\tbd{psErrorRegister is not yet coded.}
+
+\paragraph{Error Codes for PSLib}
+
+For ease of maintenance, error codes for PSLib must be defined by an
+auxiliary file, conventionally named \file{psErrorCodes.dat}.  The
+format of this file must consist of a number of lines, each of the
+form:
+\begin{verbatim}
+NAME [ = value ][,] STRING
+\end{verbatim}
+where \code{[ = value]} and the comma are optional, and no spaces are
+significant except in the STRING.  Comments extend from \code{#} to
+the end of the line (except that a \code{\#} must be replaced by
+\code{#} and not taken to start a comment). For example,
+\begin{verbatim}
+#
+# This file is used to generate psErrorClasses.h
+#
+NONE = 0,               not an error; must be 0
+BASE = 256,             first value we use; should avoid errno conflicts
+UNKNOWN,                unknown error
+# This is a comment, and is ignored.
+IO,                     I/O error
+BADFREE,                bad argument to psFree()
+MEMORY_CORRUPTION,      memory corruption detected
+\end{verbatim}
+The values \code{NONE = 0} and {UNKNOWN} must be present.
+
+A script, called from the Makefiles, must generate two files,
+\file{psErrorCodes.h} and \file{psErrorCodes.c} from the input file
+\file{psErrorCodes.dat}.  \file{psErrorCodes.h} must define an
+enumerated type \code{psErrorCode} with elements \code{PS_ERR_NAME}
+and values as specified in \file{psErrorCodes.dat}, e.g.
+\begin{datatype}
+#if !defined(PS_ERROR_CODES_H)
+#define PS_ERROR_CODES_H
+
+typedef enum {
+    PS_ERR_NONE = 0,
+    PS_ERR_BASE = 256,
+    PS_ERR_UNKNOWN,
+    PS_ERR_IO,
+    PS_ERR_BADFREE,
+    PS_ERR_MEMORY_CORRUPTION,
+    PS_ERR_N_ERR_CLASSES,
+} psErrorCode;
+#endif
+\end{datatype}
+
+\file{psErrorCodes.c} must define the necessary functions to register
+the error codes.
+
+This script will likely be of use to the user, and so it shall be
+installed as part of PSLib.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Abort}
+
+\code{psAbort}, must call \code{psLogMsgV} with a level of
+\code{PS_LOG_ABORT}, and then call \code{abort}.
+
+\begin{prototype}
+void psAbort(const char *name, const char *format, ...);
+\end{prototype}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Command-line arguments}
+
+The following functions are provided to aid parsing of command-line
+arguments:
+
+\begin{prototype}
+int psArgumentGet(int argc, char **argv, const char *arg);
+bool psArgumentRemove(int argnum, int *argc, char **argv);
+\end{prototype}
+
+\code{psArgumentGet} shall return the index of the element in the
+argument list, \code{argv} with number of entries \code{argc}, that
+matches the specified argument, \code{arg}, or zero if there is no
+match.
+
+\code{psArgumentRemove} shall remove from the argument list
+(\code{argv} with number of entries \code{argc}) the argument whose
+index is \code{argnum}.  The number of entries in the argument list
+shall be decremented.  The function shall return \code{true} if the
+\code{argnum} is in the argument list, and \code{false} otherwise.
+
+\begin{prototype}
+int psArgumentVerbosity(int *argc, char **argv);
+\end{prototype}
+
+\code{psArgumentVebosity} shall implement the various verbosity
+controls under the following guidelines:
+\begin{itemize}
+\item \code{-v} switch shall set the log level to 3.
+\item \code{-vv} switch shall set the log level to 4.
+\item \code{-vvv} switch shall set the log level to 5.
+\item \code{-logfmt someFormat} switch shall set the log format to
+  \code{someFormat}.
+\item \code{-trace facility level} switch shall set the trace level
+  for the specified \code{facility} to the specified \code{level}.
+\item \code{-trace-levels} switch shall print the trace levels as
+  currently set.
+\end{itemize}
+
+The above arguments shall be removed from the argument list as they
+are processed.  The function shall return the resultant logging level.
+
+\begin{prototype}
+bool psArgumentParse(psMetadata *arguments, int *argc, char **argv);
+\end{prototype}
+
+\code{psArgumentParse} shall parse the command line arguments
+(supplied via \code{argc, argv}) into a metadata container of
+\code{arguments}.  The input \code{arguments} shall contain the list
+of possible arguments as the keywords providing the default values (of
+the appropriate type).  As matching arguments are found on the command
+line, the values shall be read into the \code{arguments} metadata,
+with the appropriate type.
+
+An argument may be specified multiple times on the command line (e.g.,
+\code{-arg 1 -arg 2}) if the argument is specified using a
+\code{PS_DATA_METADATA_MULTI} list (i.e., by specifying a type with
+the \code{PS_META_DUPLICATE_OK} flag on creation of the
+\code{arguments}).  An argument may take more than one parameter
+(e.g., \code{-arg 1 2 3}) by adding a \code{psMetadata} to the
+\code{arguments}, containing an entry for each of the parameters.  A
+boolean argument shall be set to \code{true} by the presence of the
+argument itself (no value is specified).  The arguments and their
+values shall be removed from the list of command line arguments as
+they are processed.  The function shall return \code{false} without
+modifying the \code{arguments} if any argument (i.e., a string
+beginning with a dash; but not a string that does not start with a
+dash, which is likely a mandatory argument for the program; the
+purpose of this requirement is so that the default values are
+preserved) was encountered that is not present in the
+\code{arguments}.
+
+\begin{prototype}
+void psArgumentHelp(psMetadata *arguments);
+\end{prototype}
+
+\code{psArgumentHelp} shall print to \code{stdout} a guide to the
+command-line \code{arguments} (containing the same information as for
+\code{psArgumentParse}) of the form:
+\begin{verbatim}
+Optional arguments, with default values:
+    -names     (FRED)     This is the comment from the first value
+               (JIM)      This is the comment from the second value
+               (BOB)      This is the comment from the third value
+    -answer    (42)       This is a comment
+    -truth     (FALSE)    This is another comment
+\end{verbatim}
+Here the \code{arguments} contains three keywords, \code{names, magic,
+truth}.  \code{names} has three values, \code{FRED, JIM, BOB}, stored
+as a \code{PS_DATA_METADATA_MULTI}, each with the comment as shown.
+
+It will be the responsibility of the user to supply information for
+the mandatory arguments.  An example follows:
+\begin{verbatim}
+int main(int argc, char *argv[])
+{
+    // Parse optional command-line arguments
+    psMetadata *arguments = psMetadataAlloc(); // The arguments, with default values
+    psMetadataAdd(arguments, PS_LIST_TAIL, "-string", PS_DATA_STR | PS_META_DUPLICATE_OK,
+                  "Test strings", NULL); // Multiple strings are permitted
+    psMetadataAdd(arguments, PS_LIST_TAIL, "-bool", PS_TYPE_BOOL, "Test bool", false);
+    psMetadata *intParams = psMetadataAlloc(); // Parameters for -int
+    psMetadataAdd(intParams, PS_LIST_TAIL, "int1", PS_TYPE_S32, "Test integer 1", 1);
+    psMetadataAdd(intParams, PS_LIST_TAIL, "int2", PS_TYPE_S32, "Test integer 2", 2);
+    psMetadataAdd(intParams, PS_LIST_TAIL, "int3", PS_TYPE_S32, "Test integer 3", 3);
+    psMetadataAdd(arguments, PS_LIST_TAIL, "-int", PS_DATA_METADATA, "Test integers", intParams);
+    psFree(intParams); // Drop reference
+    psMetadataAdd(arguments, PS_LIST_TAIL, "-float", PS_TYPE_F32, "Test float", 1.234567);
+    if (! psArgumentParse(arguments, &argc, argv) || argc != 3) {
+	printf("\nName of the program here\n\n");
+	printf("Usage: %s INPUT OUTPUT\n\n", argv[0]);
+	psArgumentHelp(arguments);
+	psFree(arguments);
+	exit(EXIT_FAILURE);
+    }
+    const char *inputName = argv[1];	// Name of input
+    const char *outputName = argv[2];	// Name of output
+    printf("Success: %s %s\n", inputName, outputName);
+
+    psMetadataItem *strings = psMetadataLookup(arguments, "-string"); // This is a MULTI
+    psListIterator *stringsIter = psListIteratorAlloc(strings->data.V, PS_LIST_HEAD, false);
+    psString string;
+    while ((string = psListGetAndIncrement(stringsIter))) {
+        printf("String: %s\n", string);
+    }
+    psFree(stringsIter);
+    float floating = psMetadataLookupF32(NULL, arguments, "-float");
+    bool boolean = psMetadataLookupBool(NULL, arguments, "-bool");
+    printf("Float: %f\n", floating);
+    printf("Boolean: %d\n", boolean);
+
+    psMetadata *ints = psMetadataLookupMD(NULL, arguments, "-int");
+    int int1 = psMetadataLookupS32(NULL, ints, "int1");
+    int int2 = psMetadataLookupS32(NULL, ints, "int2");
+    int int3 = psMetadataLookupS32(NULL, ints, "int3");
+    printf("Integer 1: %d\n", int1);
+    printf("Integer 2: %d\n", int2);
+    printf("Integer 3: %d\n", int3);
+}
+\end{verbatim}
+
+The above program should produce, on being given no arguments:
+\begin{verbatim}
+
+Name of the program here
+
+Usage: ./program INPUT OUTPUT
+
+Optional arguments, with default values:
+    -string    ()                Test strings
+    -bool      (FALSE)           Test bool
+    -int       (1)               Test integer 1
+               (2)               Test integer 2
+               (3)               Test integer 3
+    -float     (1.234567e+00)    Test float
+\end{verbatim}
+
+and called with: \code{./program -string foo -string bar -float 9.876543
+-int 4 5 6 -bool input output}, then it should produce:
+
+\begin{verbatim}
+Success: input output
+String: foo
+String: bar
+Float: 9.876543
+Boolean: 1
+Integer1: 4
+Integer2: 5
+Integer3: 6
+\end{verbatim}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagebreak 
+\section{Containers}
+
+We require general data containers, so that associated values (e.g.\
+the elements of an array) may be connected as a whole.  We require the
+following types of containers:
+\begin{itemize}
+\item Arrays;
+\item Doubly-linked lists;
+\item Hashes;
+\item Pixel lists;
+\item Bit sets;
+\item Metadata; and
+\item Lookup tables.
+\end{itemize}
+
+The reference counter for a pointer shall be incremented when it is
+placed in a container, and decremented when the pointer is it is
+removed.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Arrays}
+
+We require an order collection of unspecified data elements.  We
+define \code{psArray} to carry such a collection:
+%
+\begin{datatype}
+typedef struct {
+    long n;                             ///< size of array 
+    const long nalloc;                  ///< allocated data block
+    psPtr *data;                        ///< pointer to data block
+    void *lock;                         ///< Optional lock for thread safety
+} psArray;
+\end{datatype}
+%
+In this structure, the argument \code{n} is the length of the array
+(the number of elements); \code{nalloc} is the number of elements
+allocated ($nalloc \ge n$).  The allocated memory is pointed to by
+\code{data}.  The structure is associated with a constructor and a
+destructor:
+%
+\begin{prototype}
+psArray *psArrayAlloc(long nalloc);
+psArray *psArrayAllocEmpty(long nalloc);
+psArray *psArrayRealloc(psArray *array, long nalloc);
+\end{prototype}
+%
+In these functions, \code{nalloc} is the number of elements to
+allocate.  In \code{psArrayAlloc}, the value of \code{psArray.n} is
+initially set to the number of allocated elements; in
+\code{psArrayAllocEmpty}, the value of \code{psArray.n} is initially
+set to zero.  Users may choose to restrict the data range after the
+\code{psArrayAlloc} function is called.  For \code{psArrayRealloc}, if
+the value of \code{nalloc} is smaller than the current value of
+\code{psArray.n}, then \code{psArray.n} is set to \code{nalloc}, the
+array is adjusted down to match \code{nalloc}, and the extra elements
+are dropped and freed if necesitated by the reference counter.  If
+\code{nalloc} is larger than the current value of \code{psArray.n},
+\code{psArray.n} is left intact.  If the value of \code{array} is
+\code{NULL}, then \code{psArrayRealloc} must return an error.
+
+Since \code{psArray} stores pointers, values on the array shall always
+be initialised to \code{NULL} on \code{psArrayAlloc}.
+\code{psArrayRealloc} shall initialise values to \code{NULL} where the
+array has been grown.
+
+\begin{prototype}
+void psArrayElementsFree(psArray* array);
+\end{prototype}
+%
+\code{psArrayElementsFree} shall free all elements on the
+\code{array}.
+
+\begin{prototype}
+psArray *psArrayAdd(psArray *array, long delta, psPtr data);
+\end{prototype}
+
+This function adds a value to the end of an array.  If the current
+length of the array (\code{psArray.n}) is at the limit of the
+allocated space, additional space is allocated.  The value of
+\code{delta} defines how many elements to add on each pass (if this
+value is less than 1, 10 shall be used).
+
+\begin{prototype}
+bool psArrayRemoveData(psArray *array, const psPtr data);
+\end{prototype}
+
+This function removes all entries of \code{data} in the \code{array},.
+Returns \code{TRUE} if any elements were removed, otherwise
+\code{FALSE}.
+
+\begin{prototype}
+bool psArrayRemoveIndex(psArray *array, long index);
+\end{prototype}
+
+This function removes the \code{array} element at the specified
+\code{index}, returning \code{true} upon success.
+
+\begin{prototype}
+long psArrayLength(const psArray *array);
+\end{prototype}
+
+This function returns the length of the array (\code{psArray.n}).
+
+\begin{prototype}
+bool psArraySet(psArray *array, long position, psPtr data);
+psPtr psArrayGet(const psArray *array, long position);
+\end{prototype}
+
+These accessor functions are provided as a convenience to the user.
+\code{psArraySet} sets the value of the \code{in} array at the
+specified \code{position} to \code{value}, returning \code{true} if
+successful.  \code{psArrayGet} returns the value of the \code{in}
+array at the specified \code{position}.  A negative \code{position}
+means index from the end.
+
+\begin{datatype}
+typedef int (*psComparePtrFunc) (
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+\end{datatype}
+
+\begin{prototype}
+psArray *psArraySort(psArray *array, psComparePtrFunc func);
+\end{prototype}
+An array may be sorted using \code{psArraySort}, which requires the
+specification of a comparison function to specify how the objects on
+the list should be sorted.  The motivation is primarily to be able to
+iterate on a sorted list of keys from a hash.  The \code{array} is
+sorted in-place.
+
+\subsubsection{Comparison functions}
+
+We specify several comparison functions as a convenience to the user.
+The \code{psCompareTypePtr} functions are intended for sorting where
+the container has a pointer to the value (e.g., \code{psList}).  The
+functions return an integer less than, equal to, or greater than zero
+if \code{a} is is less than, equal to, or greater than \code{b},
+respectively (\code{qsort} man page).
+
+\begin{prototype}
+int psCompareS8Ptr(const void **a, const void **b);
+int psCompareS16Ptr(const void **a, const void **b);
+int psCompareS32Ptr(const void **a, const void **b);
+int psCompareS64Ptr(const void **a, const void **b);
+int psCompareU8Ptr(const void **a, const void **b);
+int psCompareU16Ptr(const void **a, const void **b);
+int psCompareU32Ptr(const void **a, const void **b);
+int psCompareU64Ptr(const void **a, const void **b);
+int psCompareF32Ptr(const void **a, const void **b);
+int psCompareF64Ptr(const void **a, const void **b);
+int psCompareDescendingS8Ptr(const void **a, const void **b);
+int psCompareDescendingS16Ptr(const void **a, const void **b);
+int psCompareDescendingS32Ptr(const void **a, const void **b);
+int psCompareDescendingS64Ptr(const void **a, const void **b);
+int psCompareDescendingU8Ptr(const void **a, const void **b);
+int psCompareDescendingU16Ptr(const void **a, const void **b);
+int psCompareDescendingU32Ptr(const void **a, const void **b);
+int psCompareDescendingU64Ptr(const void **a, const void **b);
+int psCompareDescendingF32Ptr(const void **a, const void **b);
+int psCompareDescendingF64Ptr(const void **a, const void **b);
+int psCompareS8(const void *a, const void *b);
+int psCompareS16(const void *a, const void *b);
+int psCompareS32(const void *a, const void *b);
+int psCompareS64(const void *a, const void *b);
+int psCompareU8(const void *a, const void *b);
+int psCompareU16(const void *a, const void *b);
+int psCompareU32(const void *a, const void *b);
+int psCompareU64(const void *a, const void *b);
+int psCompareF32(const void *a, const void *b);
+int psCompareF64(const void *a, const void *b);
+int psCompareDescendingS8(const void *a, const void *b);
+int psCompareDescendingS16(const void *a, const void *b);
+int psCompareDescendingS32(const void *a, const void *b);
+int psCompareDescendingS64(const void *a, const void *b);
+int psCompareDescendingU8(const void *a, const void *b);
+int psCompareDescendingU16(const void *a, const void *b);
+int psCompareDescendingU32(const void *a, const void *b);
+int psCompareDescendingU64(const void *a, const void *b);
+int psCompareDescendingF32(const void *a, const void *b);
+int psCompareDescendingF64(const void *a, const void *b);
+\end{prototype}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Doubly-linked lists}
+\label{sec:psList}
+
+\PS{} shall support doubly linked lists through a type \code{psList}:
+%
+\begin{datatype}
+typedef struct {
+   long n;                              ///< number of elements on list
+   psListElem *head;                    ///< first element on list (may be NULL)
+   psListElem *tail;                    ///< last element on list (may be NULL)
+   psArray *iterators;                  ///< array of psListIterator: iteration cursors
+   void *lock;                          ///< Optional lock for thread safety
+} psList;
+\end{datatype}
+%
+The type \code{psList} represents the container of the list.  It has a
+pointer to the first element in the linked list (\code{head}), a
+pointer to the last element in the list (\code{tail}), an array of
+iteration cursors, (\code{iterators}), and an entry to define the
+number of elements in the list (\code{n}).
+
+The elements of the list are defined by the type \code{psListElem}:
+%
+\begin{datatype}
+typedef struct psListElem {
+   struct psListElem *prev;            ///< previous link in list
+   struct psListElem *next;            ///< next link in list
+   psPtr data;                         ///< real data item
+} psListElem;
+\end{datatype}
+%
+which includes a pointer to the next element in the list
+(\code{next}), the previous element in the list (\code{prev}), and a
+\code{void} pointer to whatever data is represented by this list
+element.    The following supporting functions are required:
+
+\begin{prototype}
+psList *psListAlloc(psPtr data);
+\end{prototype}
+Create a list.  This function may take a pointer to a data item, or it
+may take \code{NULL}.  The allocator creates both the \code{psList}
+and the first element in the list, pointed to by both
+\code{psList.head} and \code{psList.tail}.  If the data entry is
+\code{NULL}, then an empty list, with both pointers set to \code{NULL}
+should be created.
+
+The destructor function for \code{psList} must call \code{psFree} for
+all the the data associated with the list.
+
+\begin{prototype}
+long psListLength(const psList *list);
+\end{prototype}
+Return the length of the list (\code{psList.n}).
+
+All data items placed onto lists must have their reference counters
+(section~\ref{secMemRefcounter}) incremented.  When elements are
+removed from a list, they must have their reference counters
+decremented.  The action of retrieving data from a list (with one of
+the three \code{psListGet} functions) is considered ``borrowing'' the
+reference, so no action is performed on the reference counter.
+
+Iteration on the list shall be achieved by means of a list iterator
+type:
+\begin{datatype}
+typedef struct {
+    psList *list;                       ///< List iterator works on
+    psListElem *cursor;                 ///< The current iterator cursor
+    bool offEnd;                        ///< Is the iterator off the end?
+    long index;                         ///< Index of iterator, to assist performance
+    bool mutable;                       ///< Is it permissible to modify the list?
+} psListIterator;
+\end{datatype}
+The \code{psListIterator} keeps track of which list element the
+iterator \code{cursor} is currently pointing at.  \code{index} is the
+index of the list iterator, which is used to assist performance when
+using numerical locations.  The boolean member, \code{offEnd},
+indicates whether the iterator has progressed off the end of the list
+(i.e., beyond the last item).  The boolean \code{mutable} specifies
+whether it is permissible to modify the list pointed to by the
+iterator.  \code{psListAddBefore} and \code{psListAddAfter} are not
+permitted to modify a list that is not \code{mutable} (i.e., only the
+\code{psListGetAndIncrement} and \code{psListGetAndDecrement}
+operations are permissible for a non-\code{mutable} list).
+
+The corresponding constructor shall be:
+\begin{prototype}
+psListIterator *psListIteratorAlloc(psList *list, long location, bool mutable);
+\end{prototype}
+Here, \code{list} is the \code{psList} on which the iterator will
+iterate, and \code{location} is the initial starting point, and may be
+a numerical index or it may be one of the special values:
+\code{PS_LIST_HEAD} or \code{PS_LIST_TAIL}, which are defined as 0 and
+-1, respectively; a negative index is interpreted as relative to the
+end of the list.  The boolean \code{mutable} specifies whether it is
+permissible to modify the list pointed to by the iterator.
+
+The destructor for \code{psListIterator} shall, after freeing the
+\code{psListIterator}, also reorganise the \code{iter} array
+(replacing the element being removed with the last element) and
+resizing the array appropriately.
+
+A list \code{iterator} shall be set to a specific \code{location} on
+the list upon calling \code{psListIteratorSet}:
+\begin{prototype}
+bool psListIteratorSet(psListIterator *iterator, long location);
+\end{prototype}
+Again, the \code{location} may be a numerical index or it may be one
+of the special values: \code{PS_LIST_HEAD} or \code{PS_LIST_TAIL},
+which are defined as 0 and -1, respectively; a negative index is
+interpreted as relative to the end of the list.  The function shall
+return \code{true} if the reset was successful, or \code{false}
+otherwise.
+
+\begin{prototype}
+bool psListAdd(psList *list, long location, psPtr data);
+bool psListAddAfter(psListIterator *iterator, psPtr data);
+bool psListAddBefore(psListIterator *iterator, psPtr data);
+\end{prototype}
+the first function, \code{psListAdd}, adds an entry to the \code{list}
+and returns a boolean giving the success or failure of the
+operation. The value of \code{location} may be a numerical index the
+\code{data} is to inhabit (if \code{location} is greater than the
+number of items on the list, then the function shall generate a
+warning and add the \code{data} to the tail) or it may be one of the
+special values: \code{PS_LIST_HEAD} or \code{PS_LIST_TAIL}, which are
+defined as 0 and -1, respectively; a negative index is interpreted as
+relative to the end of the list.  The other two functions,
+\code{psListAddAfter} and \code{psListAddBefore} specify that the
+\code{data} shall be added after or before (respectively) the current
+cursor position of the \code{iterator}.
+
+\begin{prototype}
+psPtr psListGet(psList *list, long location);
+psPtr psListGetAndIncrement(psListIterator *iterator);
+psPtr psListGetAndDecrement(psListIterator *iterator);
+\end{prototype}
+A data item may be retrieved from the list with these functions.  The
+first function, \code{psListGet} simply returns the value specified by
+its \code{location}, which may be a numerical index or it may be one
+of the special values: \code{PS_LIST_HEAD = 0} or \code{PS_LIST_TAIL =
+-1}; negative indices are interpreted as relative to the end of the
+list.  The other two functions, \code{psListGetAndIncrement} and
+\code{psListGetAndDecrement} return the item under the iteration
+cursor before advancing to the next or previous item, respectively.
+
+In the event that the iteration cursor is at the tail of the list,
+\code{psListGetAndIncrement} shall return the tail item and then set
+the \code{cursor} to \code{NULL} and \code{offEnd} to \code{true}.  In
+the event that the iteration cursor is at the head of the list,
+\code{psListGetAndDecrement} shall return the head item and then set
+the \code{cursor} to \code{NULL} (and \code{offEnd} should already be
+\code{false}).  In the event that the iteration \code{cursor} is
+\code{NULL}, \code{psListGetAndIncrement} and
+\code{psListGetAndDecrement} shall return \code{NULL}, and advance the
+iteration \code{cursor} only if the intended direction places the
+cursor back on the list; otherwise a warning shall be generated, and
+no change shall be made.  If \code{psListGetAndDecrement} was called
+with \code{offEnd} as \code{true}, then \code{offEnd} shall also be
+toggled back to \code{false} to indicate that the \code{cursor} is no
+longer off the end of the list.
+
+\begin{prototype}
+bool psListRemove(psList *list, long location);
+bool psListRemoveData(psList *list, psPtr data);
+\end{prototype}
+A data item may be removed from the list with these functions.  For
+\code{psListRemove}, the value of \code{location} may be the numerical
+index or it may be one of the special values: \code{PS_LIST_HEAD} or
+\code{PS_LIST_TAIL}, which are defined as 0 and -1, respectively; a
+negative index is interpreted as relative to the end of the list.  For
+\code{psListRemoveData}, the data item to be removed is identified by
+matching the pointer value with \code{psPtr data}.  The functions
+return a value of \code{true} if the operation was successful, and
+\code{false} otherwise.  In both cases, if any iterators are currently
+pointing at the item to be removed, the item shall be removed and
+those iterators pointing at it shall be moved to the next, and the
+function shall return \code{true}.  If the item to be removed is not
+on the list, an error shall be generated and the function shall return
+\code{false}.
+
+\begin{prototype}
+psArray *psListToArray(const psList *list);
+psList  *psArrayToList(const psArray *array);
+\end{prototype}
+These two functions are available to convert between the
+\code{psList} and \code{psArray} containers.  These functions do not
+free the elements or destroy the input collection.  Rather, they
+increment the reference counter for each of the elements.
+
+\begin{prototype}
+psList *psListSort(psList *list, psComparePtrFunc func);
+\end{prototype}
+A list may be sorted using \code{psListSort}, which requires the
+specification of a comparison function to specify how the objects on
+the list should be sorted.  The motivation is primarily to be able to
+iterate on a sorted list of keys from a hash.  The \code{list} is
+sorted in-place.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Hash Tables}
+\hlabel{psHash}
+
+Hash tables are critical for quick retrieval of text-based data.  The
+concept is as follows: Given a large collection of text strings, it is
+inefficient to search for a particular entry by performing a basic
+string comparison on all entries until a match is found.  Even if a
+single list is sorted, we will still spend a substantial amount of
+time iterating across the entries in the list.  In a hash table, we
+define an operation, the hash function, which uses the bytes of the
+string to construct a numerical value, the hash value.  The hash value
+is defined to have a limited range of $N$ values.  The hash table
+consists of $N$ buckets, each of which contains a list of the strings
+whose hash value corresponds to the bucket number.  Searching for a
+specific string involves calculating the hash value for the string,
+going to the appropriate bucket, and searching through the
+corresponding list until the string is matched.  
+
+For PSLib, we define a hash table and hash buckets as follows:
+\footnote{ We choose not to use the POSIX function \code{hcreate},
+\code{hdestroy}, and \code{hsearch} as they only support a single hash
+table at any one time.}
+%
+\begin{datatype}
+typedef struct {
+    long n;                             ///< number of buckets
+    psHashBucket **buckets;             ///< the buckets themselves
+    void *lock;                         ///< Optional lock for thread safety
+} psHash;
+\end{datatype}
+%
+where \code{n} is the number of buckets defined for the hash functions, and
+\code{buckets} is an array of pointers to the individual buckets, each of which
+is defined by:
+%
+\begin{datatype}
+typedef struct psHashBucket {
+    char *key;                          ///< key for this item of data
+    psPtr data;                         ///< the data itself
+    struct psHashBucket *next;          ///< list of other possible keys
+} psHashBucket;
+\end{datatype}
+where each bucket contains the value of the \code{key}, a pointer to
+the \code{data}, and a pointer to the \code{next} list entry in the
+bucket (in the event that two or more keys have the same hash value).
+
+A hash table is created with the following function:
+\begin{prototype}
+psHash *psHashAlloc(long nalloc);
+\end{prototype}
+which allocates the space for the hash table, creating and
+initializing \code{n} hash buckets.
+
+The destructor for \code{psHash} must free all data associated with a complete hash table.
+
+A data item may be added to the hash table with the function:
+\begin{prototype}
+bool psHashAdd(psHash *hash, const char *key, psPtr data);
+\end{prototype}
+In this function, the value of the string \code{key} is used to
+construct the hash value, find the appropriate bucket set, and add the
+new element \code{data} to the list.  An existing element with the same
+value of \code{key} is destroyed using its registered destructor
+(\code{psMemBlock}). The return value of the function is a boolean
+defining the success or failure of the operation.
+
+The data associated with a given key may be found with the function:
+\begin{prototype}
+psPtr psHashLookup(const psHash *hash, const char *key);
+\end{prototype}
+which returns the data value pointed to by the element associated with
+\code{key}, or the value \code{NULL} if no match is found.  Similarly,
+a specific key may be removed (deleted) with the function:
+\begin{prototype}
+bool psHashRemove(psHash *hash, const char *key);
+\end{prototype}
+The function returns a value of \code{true} if the operation was
+successfull, and \code{false} otherwise.
+
+The function
+\begin{prototype}
+psList *psHashKeyList(const psHash *hash);
+\end{prototype}
+returns the complete list of defined keys associated with the
+\code{psHash} table as a linked list.
+
+\begin{prototype}
+psArray *psHashToArray(const psHash *hash);
+\end{prototype}
+This function places the data in a \code{psHash} into a \code{psArray}
+container.  This function does not free the elements or destroy the
+input collection.  Rather, it increments the reference counter for
+each of the elements.  The resulting array does not have any
+information about the has key values, and the order is not
+significant.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Pixel Lists}
+
+Usually an image mask is the best way to carry information about what
+pixels mean what.  However, in the case where the number of pixels in
+which we are interested is limited, it is more efficient to simply
+carry a list of pixels.  An example of this is in the image
+combination code, where we want to perform an operation on a
+relatively small fraction of pixels, and it is inefficient to go
+through an entire mask image checking each pixel.
+
+\begin{datatype}
+typedef struct {
+    float x;                    // x coordinate
+    float y;                    // y coordinate
+} psPixelCoord;
+
+typedef struct {
+    long n;                     // Number in use
+    const long nalloc;          // Number allocated
+    psPixelCoord *data;         // The pixel coordinates
+    void *lock;                 // Lock for thread safety
+} psPixels;
+\end{datatype}
+
+\begin{prototype}
+psPixels *psPixelsAlloc(long nalloc);
+psPixels *psPixelsRealloc(psPixels *pixels, long nalloc);
+\end{prototype}
+
+\code{psPixelsAlloc} and \code{psPixelsRealloc} provide dynamic
+allocation and reallocation in a manner analogous to those provided
+by \code{psVectorAlloc} and \code{psVectorRealloc}.
+
+\begin{prototype}
+long psPixelsLength(const psPixels *pixels);
+\end{prototype}
+
+\code{psPixelsLength} shall return the length of the pixel array (\code{psPixels.n}).
+
+\begin{prototype}
+psPixels *psPixelsCopy(psPixels *out, const psPixels *pixels);
+psPixels *psPixelsConcatenate(psPixels *out, const psPixels *pixels);
+\end{prototype}
+
+\code{psPixelsCopy} shall copy the contents of \code{pixels} to the
+\code{out}.  In the event that \code{out} is \code{NULL}, a new
+\code{psPixels} shall be allocated, and the contents of \code{pixels}
+simply copied in.  If \code{pixels} is \code{NULL}, the function shall
+generate an error and return \code{NULL}.
+
+\code{psPixelsConcatenate} shall concatenate the \code{pixels} onto
+\code{out}.  In the event that \code{out} is \code{NULL}, the function
+performs a \code{psPixelsCopy}, returning the copy.  If \code{pixels}
+is \code{NULL}, the function shall generate an error and return
+\code{NULL}.  The function shall take care to ensure that there are no
+duplicate pixels in \code{out} (since the order in which the pixels
+are stored is not important, the values may be sorted, allowing the
+use of a faster algorithm than a linear scan).
+
+\begin{prototype}
+bool psPixelsSet(psPixels *pixels, long position, psPixelCoord value);
+psPixelCoord psPixelsGet(const psPixels *pixels, long position);
+\end{prototype}
+
+These accessor functions are provided as a convenience to the user.
+\code{psPixelsSet} sets the value of the \code{pixels} array at the
+specified \code{position} to \code{value} (a \code{psPixelCoord}
+passed by value), returning \code{true} if successful.
+\code{psPixelsGet} returns the value of the \code{pixels} array at the
+specified \code{position}.  A negative \code{position} means index
+from the end.  In the event of an error, \code{psPixelsGet} shall
+return a \code{psPixelCoord} with components set to \code{NaN}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{BitSets}
+
+BitSets are required in order to turn options on and off.  We require
+the capability to have a bitset of arbitrary length (i.e., not limited
+by the length of a \code{long}, say).  The \code{psBitSet} structure
+is defined below.  Note that the entry \code{bits} is an array of type
+\code{char} storing the bits as bits of each byte in the array, with 8
+bits available for each byte in the array.  Also note that the
+constructor is passed the number of required bits, which implies that
+\code{ceil(n/8)} bytes must be allocated.  The bitset structure is
+define by:
+\begin{datatype}
+typedef struct {
+    long n;                             ///< Number of chars that form the bitset
+    psU8 *bits;                         ///< The bits
+    void *lock;                         ///< Optional lock for thread safety
+} psBitSet;
+\end{datatype}
+
+We also require the corresponding constructor and destructor:
+\begin{prototype}
+psBitSet *psBitSetAlloc(long nalloc);
+\end{prototype}
+where \code{n} is the requested number of bits.
+
+Several basic operations on bitsets are required:
+\begin{itemize}
+\item Set a bit;
+\item Check if a bit is set; and
+\item \code{OR}, \code{AND} and \code{XOR} two bitsets.
+\item \code{NOT} a bitset.
+\end{itemize}
+The corresponding APIs are defined below:
+
+\begin{prototype}
+bool psBitSetSet(psBitSet *bitSet, long bit);
+bool psBitSetClear(psBitSet *bitSet, long bit);
+psBitSet *psBitSetOp(psBitSet *outBitSet, const psBitSet *inBitSet1,
+                     const char *operator, const psBitSet *inBitSet2);
+psBitSet *psBitSetNot(psBitSet *outBitSet, const psBitSet *inBitSet);
+bool psBitSetTest(const psBitSet *bitSet, long bit);
+psString psBitSetToString(const psBitSet* bitSet);
+\end{prototype}
+
+\code{psBitSetSet} sets the specified \code{bit} in the
+\code{psBitSet}.  The input bitset
+will be modified.
+
+\code{psBitSetClear} clears the specified \code{bit} in the \code{bitSet}
+and returns the updated bitset.  The input bitset will be modified.
+
+\code{psBitSetOp} returns the \code{psBitSet} that is the result of
+performing the specified \code{operator} (one of \code{"AND"},
+\code{"OR"}, or \code{"XOR"}) on \code{inBitSet1} and \code{inBitSet2}.
+If the output bitset \code{outBitSet} is \code{NULL}, it is created by
+the function.
+
+\code{psBitSetNot} applies a unary \code{NOT} to a bitset, placing the
+answer in the bitset \code{out}, or creating a new bitset if
+\code{out} is \code{NULL}.
+
+\code{psBitSetTest} returns a true value if the specified \code{bit}
+is set; otherwise, it returns a false value.
+
+Finally, \code{psBitSetToString} returns a string representation of
+the specified \code{bits}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Metadata}
+\label{sec:metadata}
+
+\subsubsection{Conceptual Overview}
+
+Within PSLib, we provide a data structure to carry metadata and
+mechanisms to manipulate the metadata.  Metadata is a general concept
+that requires some discussion.  In any data analysis task, the
+ensemble of all possible data may be divided into two or three
+classes: there is the specific data of interest, there is data which
+is related or critical but not the primary data of interest, and there
+is all of the other data which may or may not be interesting.  For
+example, consider a simple 2D image obtained of a galaxy from a CCD
+camera on a telescope.  If you want to study the galaxy, the specific
+data of interest is the collection of pixels.  There are a variety of
+other pieces of data which are closely related and crucial to
+understanding the data in those pixels, such as the dimensions of the
+image, the coordinate system, the time of the image, the exposure
+time, and so forth.  Other data may be known which may be less
+critical to understanding the image, but which may be interesting or
+desired at a later date.  For example, the observer who took the
+image, the filter manufacturer, the humidity at the telescope, etc.
+
+Formally, all of the related data which describe the principal data of
+interest are metadata.  Note that which piece is the metadata and
+which is the data may depend on the context.  If you are examining the
+pixels in an image, the coordinate and flux of an object may be part
+of the metadata.  However, if you are analyzing a collection of
+objects extracted from an image, you may consider then pixel data
+simply part of the metadata associated with the list of objects.  
+
+There are various ways to handle metadata vs data within a programming
+environment.  In C, it is convenient to use structures to group
+associated data together.  One possibility is to define the metadata
+as part of the associated data structure.  For example, the image data
+structure would have elements for all possible associated measurement.
+This approach is both cumbersome (because of the large number metadata
+types), impractical (because the full range of necessary metadata is
+difficult to know in advance) and inflexible (because any change in
+the collection of metadata requires addition of new structure elements
+and recompilation).  
+
+An alternative is to place the metadata in a generic container and use
+lookup mechanisms to extract the appropriate metadata when needed.  An
+example of this is would be a text-based FITS header for an image read
+into a flat text buffer.  In this implementation, metadata lookup
+functions could return the current value of, for example, NAXIS1 (the
+number of columns of the image) by scanning through the header buffer.
+This method has the benefits of flexibility and simplicity of
+programming interface, but it has the disadvantage that all metadata
+is accessed though this lookup mechanism.  This may make the code less
+readable and it may slow down the access.  
+
+PSLib implements an intermediate solution to this problem.  We specify
+a flexible, generic metadata container and access methods.  Data types
+which require association with a general collection of metadata should
+include an entry of this metadata type.  However, a subset of metadata
+concepts which are basic and frequently required may be placed in the
+coded structure elements.  This approach allows the code to refer to
+the basic metadata concepts as part of the data structure (ie,
+\code{image.nx}), but also allows us to provide access to any
+arbitrary metadata which may be generated.  As a practical matter, the
+choice of which entries are only in the metadata and which are part of
+the explicit structure elements is rather subjective.  Any data
+elements which are frequently used should be put in the structure;
+those which are only infrequently needed should be left in the generic
+metadata.
+
+There are some points of caution which must be noted.  Any
+manipulation of the data should be reflected in the metadata where
+appropriate.  This is always an issue of concern.  For example,
+consider an image of dimensions \code{nx, ny}.  If a function extracts
+a subraster, it must change the values of \code{nx, ny} to match the
+new dimensions.  What should it do to the corresponding metadata?
+Clearly, it should change the corresponding value which defines
+\code{nX, nY}.  However, it is not quite so simple: there may be other
+metadata values which depend on those values.  These must also be
+changed appropriately.  What if the metadata element points to a
+copy of the metadata which may be shared by other representations of
+the image?  These must be treated differently because the change would
+invalidate those other references.  Care must be taken, therefore,
+when writing functions which operate on the data to consider all of
+the relevant metadata entries which must also be updated. 
+
+A related issue is the definition of metadata names.  Entries in a
+structure have the advantage of being hardwired: every instance of
+that structure will have the same name for the same entry.  This is
+not necessarily the case with a more flexible metadata container.  The
+image exposure time is a notorious example in astronomy.  Different
+observatories use different header keywords (ie, metadata names) for
+the same concept of the exposure time (\code{EXPTIME},
+\code{EXPOSURE}, \code{OPENTIME}, \code{INTTIME}, etc).  Any system
+which operates on these metadata needs to address the issue of
+identifying these names.  This issue seems like an argument for
+hardwiring metadata in the structure, but in fact it does not present
+such a strong case.  If the metadata are hardwired, some function will
+still have to know how to interpret the various names to populate the
+structure.  The concept can still be localized with generic metadata
+containers by including abstract metadata names within the code which
+are tied to the various implementations-specific metadata names.
+
+\subsubsection{Metadata Representation}
+
+\begin{figure}
+\psfig{file=pics/Metadata,width=6.5in}
+\caption{Metadata Structures\label{fig:metadata}}
+\end{figure}
+
+This section addresses the question of how \PS{} metadata should be
+represented in memory, not how it should be represented on disk.
+
+We define an item of metadata with the following structure:
+\filbreak
+\begin{datatype}
+typedef struct {
+    const psS32 id;                     ///< unique ID for this item
+    psString name;                      ///< Name of item
+    psDataType type;                    ///< type of this item
+    union {
+        psBool B;                       ///< boolean value
+        psS8  S8;                       ///< integer data
+        psS16 S16;                      ///< integer data
+        psS32 S32;                      ///< integer data
+        psS64 S64;                      ///< integer data
+        psU8  U8;                       ///< integer data
+        psU16 U16;                      ///< integer data
+        psU32 U32;                      ///< integer data
+        psU64 U64;                      ///< integer data
+        psF32 F32;                      ///< floating-point data
+        psF64 F64;                      ///< double-precision data
+        psList *list;                   ///< psList entry
+        psMetadata *md;                 ///< psMetadata entry
+	psString str;                   ///< string data
+        psPtr V;                        ///< other type
+    } data;                             ///< value of metadata
+    psString comment;                   ///< optional comment ("", not NULL)
+} psMetadataItem;
+\end{datatype}
+
+The \code{id} is a unique identifier for this item of metadata;
+experience shows that such tags are useful.  The entry \code{name}
+specifies the name of the metadata item.  The value of the metadata is
+given by the union \code{data}, and may be of type \code{psU8},
+\code{psU16}, \code{psU32}, \code{psS8}, \code{psS16}, \code{psS32},
+\code{psF32}, \code{psF64}, or an arbitrary rich structure pointed at
+by the \code{void} pointer \code{V}.  A character string comment
+associated with this metadata item may be stored in the element
+\code{comment}. The \code{type} entry specifies how to interpret the
+type of the data being represented, given by the enumerated type
+\code{psDataType}.
+
+Note that the \code{name} and \code{comment} must be allocated by the
+constructor using, e.g., \code{psStringCopy}.
+
+A collection of metadata is represented by the \code{psMetadata} structure:
+\begin{datatype}
+typedef struct {
+    psList *list;                       ///< list of psMetadataItem
+    psHash *hash;                       ///< hash table of the same metadata
+    void *lock;                         ///< Optional lock for thread safety
+} psMetadata;
+\end{datatype}
+The type \code{psMetadata} is a container class for metadata. Note
+that there are in fact \emph{two} representations of the metadata
+(each \code{psMetadataItem} appears on both).  The first
+representation employs a doubly-linked list that allows the order of
+the metadata to be preserved (e.g., if FITS headers are read in a
+particular order, they should be written in the same order).  The
+second representation employs a hash table which allows fast look-up
+given a specific metadata keyword.
+
+Certain metadata names (such as the FITS keywords \code{COMMENT} and
+\code{HISTORY} in a FITS header) may be repeated with different
+values.  In such a case, the \code{psMetadata.list} structure contains
+the entries in their original sequence with duplicate keys.  The
+\code{psMetadata.hash} entries, which are required to have unique
+keys, would have a single entry with the keyword of the repeated key,
+with the value of \code{psDataType} set to \code{PS_DATA_METADATA_MULTI},
+and the \code{psMetadataItem.data} element pointing to a \code{psList}
+containing the actual entries.  If \code{psMetadataItemAlloc} is
+called with the type set to \code{PS_DATA_METADATA_MULTI}, such a repeated key
+is created.  In this case, the data value passed to
+\code{psMetadataItemAlloc} (the quantity in ellipsis) must be
+\code{NULL}.  An empty \code{psMetadataItem} with the given keyword is
+created to hold future entries of that keyword.
+
+As a convenience to the user, the following type-specific functions are
+also defined:
+\begin{prototype}
+psMetadataItem* psMetadataItemAllocStr(const char* name, const char* comment, const char* value);
+psMetadataItem* psMetadataItemAllocF32(const char* name, const char* comment, psF32 value);
+psMetadataItem* psMetadataItemAllocF64(const char* name, const char* comment, psF64 value);
+psMetadataItem* psMetadataItemAllocS8(const char* name, const char* comment, psS8 value);
+psMetadataItem* psMetadataItemAllocS16(const char* name, const char* comment, psS16 value);
+psMetadataItem* psMetadataItemAllocS32(const char* name, const char* comment, psS32 value);
+psMetadataItem* psMetadataItemAllocS64(const char* name, const char* comment, psS64 value);
+psMetadataItem* psMetadataItemAllocU8(const char* name, const char* comment, psU8 value);
+psMetadataItem* psMetadataItemAllocU16(const char* name, const char* comment, psU16 value);
+psMetadataItem* psMetadataItemAllocU32(const char* name, const char* comment, psU32 value);
+psMetadataItem* psMetadataItemAllocU64(const char* name, const char* comment, psU64 value);
+psMetadataItem* psMetadataItemAllocBool(const char* name, const char* comment, bool value);
+psMetadataItem* psMetadataItemAllocPtr(const char* name, psDataType type, const char* comment, psPtr value);
+\end{prototype}
+
+\subsubsection{Metadata APIs}
+
+\begin{prototype}
+psMetadata *psMetadataAlloc(void);
+\end{prototype}
+
+The constructor for the collection of metadata, \code{psMetadata},
+simply returns an empty metadata container (employing the constructors
+for the doubly-linked list and hash table).  The destructor needs to
+free each of the \code{psMetadataItem}s.
+
+\begin{prototype}
+psMetadataItem *psMetadataItemAlloc(const char *name, psDataType type, const char *comment, ...);
+psMetadataItem *psMetadataItemAllocV(const char *name, psDataType type, const char *comment, va_list list);
+\end{prototype}
+
+The allocator for \code{psMetadataItem} returns a full \code{psMetadataItem}
+ready for insertion into the \code{psMetadata}.  The \code{name} entry
+specifies the name to use for this metadata item, and may include
+\code{sprintf}-type formating codes.  The \code{comment} entry is a fixed
+string which is used for the comment associated with this metadata item.  The
+metadata data and the arguments to the \code{name} formatting codes are passed,
+in that order (metadata pointer first), to \code{psMetadataItemAlloc} as
+arguments following the comment string.  The data must be a pointer for any
+data types which are stored in the element \code{data.void}, while other data
+types are passed as numeric values.  All \code{data.void} types may be set to
+have a value of \code{NULL}.  The argument list must be interpreted
+appropriately by the \code{va_list} operators in the function.
+
+\begin{prototype}
+bool psMetadataAddItem(psMetadata *md, const psMetadataItem *item, int location, psS32 flags);
+bool psMetadataAdd(psMetadata *md, long location, const char *name, int format, const char *comment, ...);
+bool psMetadataAddV(psMetadata *md, long location, const char *name, int format, const char *comment,
+                    va_list list);
+\end{prototype}
+
+Items may be added to the metadata in one of two ways --- firstly, an
+item may be added by appending a \code{psMetadataItem} which has
+already been created; and secondly by directly providing the data to
+be appended.  In both cases, the return value defines the success
+(\code{true}) or failure of the operation.  The second function,
+\code{psMetadataAdd} takes a pointer or value which is interpreted by
+the function using variadic argument interpretation.  The third
+version is the \code{va_list} version of the second function.  All
+three functions take a parameter, \code{location}, which specifies
+where in the list to place the element, following the conventions for
+the \code{psList}.  The entry \code{mode} for \code{psMetadataAddItem}
+is a bit mask constructed by OR-ing the allowed option flags (eg,
+\code{PS_DATA_REPLACE}) which specify minor variations on the
+behavior.  The \code{format} entry, which specifies both the metadata
+type and the optional flags, is constructed by bit-wise OR-ing the
+appropriate \code{psDataType} and allowed option flags.  Care
+should be taken not to leak memory when appending an item for which
+the key already exists in the metadata (and is not
+\code{PS_DATA_METADATA_MULTI}).
+%
+
+\begin{datatype}
+typedef enum {                           ///< option flags for psMetadata functions
+    PS_META_DEFAULT         = 0,         ///< default behavior (0x0000) for use in mode above
+    PS_META_REPLACE         = 0x1000000, ///< allow entry to be replaced
+    PS_META_NO_REPLACE      = 0x2000000, ///< duplicate entry is silently skipped
+    PS_META_DUPLICATE_OK    = 0x4000000, ///< allow duplicate entries
+    PS_META_NULL            = 0x8000000  ///< psMetadataItem.data is a NULL value
+} psMetadataFlags;
+\end{datatype}
+
+The functions above take option flags which modify the behavior when
+metadata items are added to the metadata list.  These flags must be
+bit-exclusive of those used above for the \code{psDataTypes}.  The
+flags have the following meanings: 
+
+\code{PS_META_DEFAULT}: This is the zero bit mask, to allow the
+default behavior for \code{psMetadataAddItem} above.  If this is OR-ed
+with a \code{psDataType}, the result is as if no OR-ing took
+place.
+
+\code{PS_META_REPLACE}: Replace an existing, unique entry. If the
+given metadata item exists in the metadata collection, and is not of
+type \code{PS_META_MULTI}, then the item replaces the existing entry.
+
+\code{PS_META_DUPLICATE_OK}: Allow the new metadata item key to be a
+duplicate (ie, \code{PS_DATA_METADATA_MULTI}).  If an existing item
+with the same key is already \code{PS_DATA_METADATA_MULTI}, the new
+item is added to the \code{PS_DATA_METADATA_MULTI} list.  If the
+existing item is not \code{PS_DATA_METADATA_MULTI}, a
+\code{PS_DATA_METADATA_MULTI} list is created to contain both the
+existing item and the new item.  The original entry's location on the
+psMetadata.list must be maintained.
+
+\code{PS_META_NULL}: Indicates that \code{psMetadataItem.data} should
+be ignored and that the the current value is ``NULL'' or undefined.
+The \code{psMetadataItem} must have a proper \code{type} set and it's
+\code{data} field shall have a valid value.  e.g. A \code{type} of
+\code{PS_DATA_STR} would require that its \code{data} is set to
+\code{NULL}.
+
+There are several of cases to handle for duplication of an existing
+key by a new key, some identified above.  The following situations
+must also be handled:
+
+If the new key already exists, but is not
+\code{PS_DATA_METADATA_MULTI}, and the new item is not flagged as
+either \code{PS_META_DUPLICATE_OK} or \code{PS_META_REPLACE}, an error
+is raised.
+
+If the new key already exists, and the existing item is
+\code{PS_DATA_METADATA_MULTI}, the new item is added to the MULTI
+list.  Note that if the new item is also of type
+\code{PS_DATA_METADATA_MULTI}, no action is taken, but a successful
+exit status is returned (the action of adding a
+\code{PS_DATA_METADATA_MULTI} item to the metadata is equivalent to
+setting that key to be tagged as \code{PS_DATA_METADATA_MULTI}.  If it
+is {\em already} \code{PS_DATA_METADATA_MULTI}, this effect has
+already been achieved).
+
+An example of code to use these metadata APIs to generate the
+structure seen in Figure~\ref{fig:metadata} is given below.
+
+\begin{verbatim}
+md = psMetadataAlloc();
+
+psMetadataAdd(md, PS_LIST_TAIL, "SIMPLE",   PS_DATA_BOOL, "basic fits",            TRUE);
+psMetadataAdd(md, PS_LIST_TAIL, "BLANK",    PS_DATA_S32,  "invalid pixel data",    -32768);
+psMetadataAdd(md, PS_LIST_TAIL, "DATE-OBS", PS_DATA_STR,  "observing date UT", "   2004-6-16");
+psMetadataAdd(md, PS_LIST_TAIL, "COMMENT",  PS_DATA_LIST, "head of comment block", NULL);
+psMetadataAdd(md, PS_LIST_TAIL, "COMMENT",  PS_DATA_STR,  "",                      "DATA");
+psMetadataAdd(md, PS_LIST_TAIL, "COMMENT",  PS_DATA_STR,  "",                      "PARAMS"); 
+psMetadataAdd(md, PS_LIST_TAIL, "EXPTIME",  PS_DATA_F32,  "exposure time (sec)",   1.05);
+psMetadataAdd(md, PS_LIST_TAIL, "COMMENT",  PS_DATA_STR,  "",                      "FOO");
+
+cell = psMetadataAlloc();
+psMetadataAdd(cell, PS_LIST_TAIL, "EXTNAME",  PS_DATA_STR,  "",                    "CCD00");
+psMetadataAdd(cell, PS_LIST_TAIL, "BIASNAME", PS_DATA_STR,  "",                    "BSEC-00");
+psMetadataAdd(cell, PS_LIST_TAIL, "CHIP",     PS_DATA_STR,  "",                    "CHIP.00");
+psMetadataAdd(md,   PS_LIST_TAIL, "CELL.00",  PS_DATA_META, "",                    cell);
+
+cell = psMetadataAlloc();
+psMetadataAdd(cell, PS_LIST_TAIL, "EXTNAME",  PS_DATA_STR,  "",                    "CCD01");
+psMetadataAdd(cell, PS_LIST_TAIL, "BIASNAME", PS_DATA_STR,  "",                    "BSEC-01");
+psMetadataAdd(cell, PS_LIST_TAIL, "CHIP",     PS_DATA_STR,  "",                    "CHIP.01");
+psMetadataAdd(md,   PS_LIST_TAIL, "CELL.01",  PS_DATA_META, "",                    cell);
+\end{verbatim}
+
+The following code shows how to use the APIs to replace one of these values:
+\begin{verbatim}
+psMetadataAdd(md, PS_LIST_TAIL, "EXPTIME",  PS_DATA_F32 | PS_REPLACE,  "new exposure time (sec)",   2.05);
+\end{verbatim}
+
+As a convenience to the user, the following type-specific functions
+are specified:
+\begin{prototype}
+bool psMetadataAddPtr(psMetadata* md, long location, const char* name, psDataType type,
+                      const char* comment, psPtr value);
+bool psMetadataAddStr(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, const char* value);
+bool psMetadataAddS8(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, psS8 value);
+bool psMetadataAddS16(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, psS16 value);
+bool psMetadataAddS32(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, psS32 value);
+bool psMetadataAddS64(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, psS64 value);
+bool psMetadataAddU8(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, psU8 value);
+bool psMetadataAddU16(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, psU16 value);
+bool psMetadataAddU32(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, psU32 value);
+bool psMetadataAddU64(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, psU64 value);
+bool psMetadataAddF32(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, psF32 value);
+bool psMetadataAddF64(psMetadata* md, long location, const char* name, int format,
+                      const char* comment, psF64 value);
+bool psMetadataAddBool(psMetadata* md, long location, const char* name, int format,
+                       const char* comment, bool value);
+bool psMetadataAddList(psMetadata* md, long location, const char* name, int format,
+                       const char* comment, psList *value);
+bool psMetadataAddVector(psMetadata* md, long location, const char* name, int format,
+                       const char* comment, psVector *value);
+bool psMetadataAddArray(psMetadata* md, long location, const char* name, int format,
+                       const char* comment, psArray *value);
+bool psMetadataAddImage(psMetadata* md, long location, const char* name, int format,
+                       const char* comment, psImage *value);
+bool psMetadataAddTime(psMetadata* md, long location, const char* name, int format,
+                       const char* comment, psTime *value);
+bool psMetadataAddHash(psMetadata* md, long location, const char* name, int format,
+                       const char* comment, psHash *value);
+bool psMetadataAddLookupTable(psMetadata* md, long location, const char* name, int format,
+                       const char* comment, psLookupTable *value);
+bool psMetadataAddMetadata(psMetadata* md, long location, const char* name, int format,
+                       const char* comment, psMetadata *value);
+bool psMetadataAddUnknown(psMetadata* md, long location, const char* name, int format,
+                       const char* comment, psPtr value);
+\end{prototype}
+
+
+Items may be removed from the metadata by specifying a key or a
+location in the list.  For \code{psMetadataRemoveKey}, if the key
+matches a metadata item, the item is removed from the metadata and
+\code{true} is returned; otherwise, \code{false} is returned.  If the
+key is not unique, then \emph{all} items corresponding to the key are
+removed, and \code{true} is returned.  For
+\code{psMetadataRemoveIndex}, the metadata item at the specified
+\code{location} is removed, if valid, and \code{true} is returned;
+otherwise the function returns \code{false}.
+%
+\begin{prototype}
+bool psMetadataRemoveKey(psMetadata *md, const char *key);
+bool psMetadataRemoveIndex(psMetadata *md, long location);
+\end{prototype}
+
+Items may be found within the metadata by providing a key.  In the
+event that the key is non-unique, the first item is returned.
+\begin{prototype}
+psMetadataItem *psMetadataLookup(const psMetadata *md, const char *key);
+\end{prototype}
+
+Several utility functions are provided for simple cases.  These
+functions perform the effort of casting the data to the appropriate
+type.  The numerical functions shall return 0.0 if their key is not
+found.  If the pointer value of \code{status} is not \code{NULL}, it
+is set to reflect the success or failure of the lookup.
+\begin{prototype}
+bool psMetadataLookupBool(bool *status, const psMetadata *md, const char *key);
+psS8  psMetadataLookupS8(bool *status, const psMetadata *md, const char *key);
+psS16 psMetadataLookupS16(bool *status, const psMetadata *md, const char *key);
+psS32 psMetadataLookupS32(bool *status, const psMetadata *md, const char *key);
+psS64 psMetadataLookupS64(bool *status, const psMetadata *md, const char *key);
+psU8  psMetadataLookupU8(bool *status, const psMetadata *md, const char *key);
+psU16 psMetadataLookupU16(bool *status, const psMetadata *md, const char *key);
+psU32 psMetadataLookupU32(bool *status, const psMetadata *md, const char *key);
+psU64 psMetadataLookupU64(bool *status, const psMetadata *md, const char *key);
+psF32 psMetadataLookupF32(bool *status, const psMetadata *md, const char *key);
+psF64 psMetadataLookupF64(bool *status, const psMetadata *md, const char *key);
+psPtr psMetadataLookupPtr(bool *status, const psMetadata *md, const char *key);
+psString psMetadataLookupStr(bool *status, const psMetadata *md, const char *key);
+psTime *psMetadataLookupTime(bool *status, const psMetadata *md, const char *key);
+psMetadata *psMetadataLookupMetadata(bool *status, const psMetadata *md, const char *key);
+\end{prototype}
+
+The following utility functions simplify further the selection of
+elements from the metadata.  These functions convert the data
+associated with the supplied metadata item to the desired type if
+possible. Conversions between types are performed as needed, and
+default values are returned for the integer and floating point types
+if the data is not available (0 for int, \code{NaN} for float).
+
+\begin{prototype}
+psBool psMetadataItemParseBool(const psMetadataItem *item);
+psF32 psMetadataItemParseF32(const psMetadataItem *item);
+psF64 psMetadataItemParseF64(const psMetadataItem *item);
+psS8  psMetadataItemParseS8(const psMetadataItem *item);
+psS16 psMetadataItemParseS16(const psMetadataItem *item);
+psS32 psMetadataItemParseS32(const psMetadataItem *item);
+psU8  psMetadataItemParseU8(const psMetadataItem *item);
+psU16 psMetadataItemParseU16(const psMetadataItem *item);
+psU32 psMetadataItemParseU32(const psMetadataItem *item);
+psString psMetadataItemParseString(const psMetadataItem *item);
+\end{prototype}
+
+Items may be retrieved from the metadata by their entry position.  The
+value of which specifies the desired entry in the fashion of
+\code{psList}.
+\begin{prototype}
+psMetadataItem *psMetadataGet(const psMetadata *md, int location);
+\end{prototype}
+
+The metadata list component may be iterated over by using a
+\code{psMetadataIterator} in a fashion equivalent to the
+\code{psListIterator}:
+\begin{datatype}
+typedef struct {
+    psListIterator* iter;              ///< iterator for the psMetadata's psList
+    regex_t* regex;                     ///< the subsetting regular expression
+} psMetadataIterator;
+\end{datatype}
+
+The iterator may be set to a location in the \code{psMetadata} list,
+and the user may get the previous or next item in the list relative to
+that location.  The iterators may be used to return the next key
+matching a POSIX \code{regex}, e.g., if the user only wants to iterate
+through \code{IPP.machines.sky} and doesn't want to bother with
+\code{IPP.machines.detector}.  The iterator should iterate over every
+item in the metadata list, even those that are contained in a
+\code{PS_DATA_LIST}.  The value \code{iterator} specifies the iterator
+to be used.  In setting the iterator, the position of the iterator is
+defined by \code{location}, which follows the conventions of the
+\code{psList} iterators.
+\begin{prototype}
+psMetadataIterator *psMetadataIteratorAlloc(const psMetadata *md, long location,
+                                            const char *regex);
+bool psMetadataIteratorSet(psMetadataIterator *iterator, long location);
+psMetadataItem *psMetadataGetAndIncrement(psMetadataIterator *iterator);
+psMetadataItem *psMetadataGetAndDecrement(psMetadataIterator *iterator);
+\end{prototype}
+
+Metadata items may be printed to an open file descriptor based on a
+provided format.  The format string is an sprintf format statement
+with exactly one \% formatting command.  If the metadata item type is
+a numeric type, this formatting command must also be numeric, and type
+conversion performed to the value to match the format type.  If the
+metadata item type is a string, the formatting command must also be
+for a string (\%s type of command).  If the metadata type is any other
+data type, printing is not allowed.
+\begin{prototype}
+bool psMetadataItemPrint(FILE *fd, const char *format, const psMetadataItem *item);
+\end{prototype}
+
+A complete metadata structure may be printed to the given file
+descriptor with the following command, where the level represents the
+desired indentation
+\begin{prototype}
+bool psMetadataPrint(FILE *fd, const psMetadata *md, int level);
+\end{prototype}
+
+There will be occasions when we want to perform a deep copy of a
+\code{psMetadata}, for example, to generate a new and independent FITS
+header.
+\begin{prototype}
+psMetadataItem *psMetadataItemCopy(const psMetadataItem *in);
+psMetadata *psMetadataCopy(psMetadata *out, const psMetadata *in);
+\end{prototype}
+\code{psMetadataItemCopy} shall create a new copy of the input
+\code{psMetadataItem}.  Since it is not feasible (at this time) to be
+able to copy every type, data of the standard numeric types plus
+\code{PS_DATA_VECTOR}, \code{PS_DATA_TIME}, \code{PS_DATA_METADATA}
+and \code{PS_DATA_REGION} shall be copied; other pointer types may
+simply be copied with a warning \tbd{for now}.
+
+\code{psMetadataCopy} shall create a new copy of all
+\code{psMetadataItem}s in the \code{in} metadata, and place them in
+the \code{out} metadata, or a new \code{psMetadata} if \code{out} is
+\code{NULL}.  If the \code{in} is NULL, an error shall be returned.
+
+The following function copies a single metadata item, specified by the
+\code{key} from the \code{in} metadata to the \code{out} metadata:
+\begin{prototype}
+bool psMetadataItemTransfer(psMetadata *out, const psMetadata *in, const char *key);
+\end{prototype}
+
+\code{psMetadataItemCompare} shall compare two metadata items, returning
+\code{true} if the names, and values are the same.
+\begin{prototype}
+bool psMetadataItemCompare(const psMetadataItem *compare,
+                           const psMetadataItem *template);
+\end{prototype}
+
+\subsubsection{Configuration files}
+\label{sec:configspec}
+
+It will be necessary for the \PS{} system, in order to load
+pre-defined settings, to parse a configuration file into a
+\code{psMetadata} structure.  This shall be performed by the
+function \code{psMetadataConfigParse}, as described below.
+
+\begin{prototype}
+psMetadata *psMetadataConfigParse(psMetadata *md, unsigned int *nFail,
+                                  const char *str, bool overwrite);
+psMetadata *psMetadataConfigRead(psMetadata *md, unsigned int *nFail,
+                                 const char *filename, bool overwrite);
+\end{prototype}
+
+Given a metadata container, \code{md}, and the name of a configuration
+file, \code{str}, \code{psMetadataConfigParse} shall parse the
+configuration fstring, placing the contained key/type/value/comment quads
+into the metadata, and returning a pointer to the metadata structure.
+The number of lines that failed to parse is returned in \code{nFail}.
+Multiple specifications of a key that haven't been declared (see
+below) are overwritten if and only if \code{overwrite} is \code{true}.
+If the metadata container is \code{NULL}, it shall be allocated.  
+
+\code{psMetadataConfigRead} is identical in behavor to
+\code{psMetadataConfigParse} except that it parses from a file
+(\code{filename}) instead of from a string.
+
+On error, these functions shall return \code{NULL}.
+
+It is also useful to be able to convert a \code{psMetadata} structure into the
+Configuration File format for debugging purposes and to enable persistent
+configuration.
+
+\begin{prototype}
+psString psMetadataConfigFormat(psMetadata *md);
+bool psMetadataConfigWrite(psMetadata *md, const char *filename);
+\end{prototype}
+
+The \code{psMetadataConfigFormat} function converts a \code{psMetadata}
+structure (including any nested \code{psMetadata}) into a Configuration File
+formatted string.  A \code{NULL} shall be returned on error.  The
+\code{psMetadataConfigWrite} behaves the same as \code{psMetadataConfigFormat}
+except that the string is written out to \code{filename}.  \code{false} is
+returned on failure.
+
+% XXX this is pretty much the same thing as psMetadataPrint() but in a
+% different format.  Can we dump psMetadataPrint()? -JH
+%%% Would prefer not to --- it's a simpler format, useful for debugging --- PAP.
+\begin{prototype}
+bool psMetadataConfigPrint(FILE *stream, psMetadata *md);
+\end{prototype}
+
+The metadata \code{md} is processed in same manner as it would be with
+\code{psMetadataConfigFormat()} and the resulting string is printed to
+\code{stream}.  \code{false} is returned on failure.
+
+\paragraph{Comments}
+
+The configuration file shall consist of plain text with
+key/type/value/comment quads on separate lines.  Blank lines,
+including those consisting solely of whitespace (both spaces and
+tabs), shall be ignored, as shall lines that commence with the comment
+character (a hash mark, \code{#}), either immediately at the start of
+the line, or preceded by whitespace.  The key/type/value/comment quads
+shall all lie on a single line, separated by whitespace.
+
+The key shall be first, possibly preceded on the line by whitespace
+which should not form part of the key.
+
+\paragraph{NULL values}
+
+The ``value'' of a quad may be declare to be undefined with the \code{NULL}
+keyword.  \code{NULL} is allowed to co-exist with a ``comment'' and may be
+surrounded by whitespace.  Any non-whitespace character will cause of the
+``value'' to be interpreted as a string.
+
+\begin{verbatim}
+foo     STR     NULL    # string with a NULL value
+bar     STR     NULL a  # string with a value of "NULL a"
+\end{verbatim}
+
+\paragraph{Types}
+\subparagraph{Scalar \& Vector}
+
+Next, to assist the casting of the value, shall be a string identifying the
+type of the value, which shall correspond to one of the simple types supported
+in \code{psMetadata}: \code{STRING,BOOL,S32,F32,F64}; \code{STR} may be used to
+abbreviate \code{STRING}; valid time types are \code{UTC,UT1,TAI,TT}.
+
+\tbd{May, in the future, require more types, including U8,S16,C64.}
+
+The value shall follow the type: strings may consist of multiple words, and
+shall have all leading and trailing whitespace removed; booleans shall simply
+be either \code{T} or \code{F}.  Time type values will be in the ISO8601
+compatible format of "YYYY-MM-DDTHH:MM:SS.sZ".  When parsed, time types shall
+be represented as a \code{psTime} object.
+
+Following the value may be an optional comment, preceded by a comment
+character (a hash mark, \code{#}), which in the case of a string
+value, serves to mark the end of the value, and for other types serves
+to identify the comment to the reader.  Only one comment character may
+be present on any single line (i.e., neither strings nor comments are
+permitted to contain the comment character).  The comment may consist
+of multiple words, and shall have leading and trailing whitespace
+removed.
+
+One wrinkle is the specification of vectors.  Keys for which the value
+is to be parsed as a vector shall be preceded immediately by a
+``vector symbol'', which we choose to be the ``at'' sign, \code{@}.
+In this case, the type shall be interpreted as the type for the
+vector, which may be any of the signed or unsigned integer or floating
+point types (\code{U8,U16,U32,U64,S8,S16,S32,S32,S64,F32,F64}) but not
+the complex floating point types; and the value shall consist of
+multiple numbers, separated either by a comma or whitespace.  These
+values shall populate a \code{psVector} of the appropriate type in the
+order in which they appear in the configuration file.
+
+\tbd{May add complex types, likely to be specified with values such as
+  1.23+4.56i in the future.}
+
+A value specified as a floating point type (\code{F32} or \code{F64})
+may be Not-a-Number (\code{nan}) or positive infinity (\code{inf}) or
+negative infinity (\code{-inf}).
+
+\tbd{May add null, de-normalized, underflow, and/or overflow values
+for selected types.}
+
+\subparagraph{MULTI}
+
+An additional hurdle is the specification of keys that may be non-unique (such
+as the \code{COMMENT} keyword in a FITS header).  These keys shall be specified
+in the configuration file as non-unique with a \code{MULTI} declaration.  In
+the form \code{[keyword] MULTI}.  No other data may be provided on this line,
+though a comment, preceded by the comment marker, is valid.  A warning shall
+be produced when a key which has not been specified to be non-unique is
+repeated; in this case, the former value shall be overwritten if
+\code{overwrite} is \code{true}, otherwise the line shall be ignored and
+counted as one that could not be parsed.  It should be noted that non-unique
+keys may be of mixed type (even the \code{TYPE} and \code{METADATA} complex
+types). For example:
+\begin{verbatim}
+comment     MULTI   # a comment
+comment     STR     some string
+comment     F32     1.23456
+comment     BOOL    T
+\end{verbatim}
+
+If a line does not conform to the rules laid out here, a warning shall
+be generated, it shall be ignored and counted as a line that could not
+be parsed.  The total number of lines that were not able to be parsed
+(including those that were ignored because \code{overwrite} is
+\code{false}, and any other parsing problems, but not including blank
+lines and comment lines) shall be returned by the function in the
+argument \code{nFail}.
+
+Here are some examples of lines of a valid configuration file:
+\filbreak
+\begin{verbatim}
+Double     F64     1.23456789      # This is a comment
+Float    F32 0.98765 # This is a comment too
+String  STR This is the string that forms the value #comment
+
+ # This is a comment line and is to be ignored
+boolean     BOOL    T # The value of `boolean' is `true'
+
+@primes U8  2,3 5 7,11,13 17 #   These are prime numbers
+
+comment MULTI # The rest of this line is ignored, but `comment' is set to be non-unique
+comment STR This
+comment STR     is
+comment STR       a
+comment STR        non-unique
+comment STR                  key
+Float F64 1.23456 # This generates a warning, and, if `overwrite' is `false', is ignored
+\end{verbatim}
+
+Of course, a real configuration file should look much nicer to humans
+than the above example, but PSLib must be able to parse such ugly
+files.
+
+\paragraph{Complex Types}
+\subparagraph{TYPE}
+
+We support a modest tree structure by defining a reserved keyword \code{TYPE}.
+Any line in the config file which starts with the word \code{TYPE} shall be
+interpreted as defining a new valid type.  The defined type name follows the
+word \code{TYPE}, and is in turn followed by an arbitrary number of words.
+These words are to be interpreted as the names of an embedded \code{psMetadata}
+entry, where the values are given on any line which (following the \code{TYPE}
+definition) employs the new type name.  For example, a new type may be defined
+as:
+\begin{verbatim}
+TYPE      CELL   EXTNAME   BIASSEC  CHIP
+CELL.00   CELL   CCD00     BSEC-00  CHIP.00
+CELL.01   CELL   CCD01     BSEC-01  CHIP.00
+\end{verbatim}
+
+When \code{psMetadataConfigParse} encounters the \code{TYPE} line, it
+should construct a \code{psMetadata} container and fill it with
+\code{psMetadataItems} having the names \code{EXTNAME, BIASSEC, CHIP},
+with type \code{PS_DATA_STR}, but data allocated.  When it next
+encounters an entry of type \code{CELL}, it should then use the given
+name (e.g., \code{CELL.00}) for the \code{psMetadataItem}, and copy
+the \code{psMetadata} data onto the \code{psMetadataItem.data.md}
+entry, filling in the values from the rest of the line (\code{CCD00,
+BSEC-00, CHIP.00}).  This hierarchical structure is illustrated in
+Figure~\ref{fig:metadata}.
+
+\subparagraph{METADATA}
+
+Another way to form a tree-like structure is to directly define a
+\code{psMetadata} entry using a sequence of successive lines to define the
+values of the \code{psMetadataItem} entries.  The initial line defines the new
+\code{psMetadata} entry and its name.  The following lines have the same format
+as the other metadata config file entries.  The sequence is terminated with a
+line with a single word \code{END}.  For example, a metadata entry may be
+defined as:
+\begin{verbatim}
+CELL      METADATA
+ EXTNAME   STR   CCD00
+ BIASSEC   STR   BSEC-00
+ CHIP      STR   CHIP.00
+ NCELL     S32   24
+END
+\end{verbatim}
+
+\paragraph{Scoping Rules}
+
+A simple set of ``Scoping Rules'' are required to properly parse a
+configuration file.  ``Scope'' refers to the current ``level'' of
+\code{METADATA} that a statement appears in.  Statements that are not contained
+in a nested \code{METADATA} are said to be in the ``Top level scope''.  Each
+level of nested \code{METADATA} statements create a new ``lower level scope''.
+
+\begin{itemize}
+\item 
+Variable names are unique only to the current level of scope.
+
+\item
+non-unique keywords (\code{MULTI}) apply only to the current scope.  i.e. They
+are invalid in ``higher'' or ``lower'' level scopes.
+
+\item
+\code{TYPE} declarations apply only to the current scope.
+
+\item
+\code{METADATA} declarations must begin and end in the same scope.  i.e.  They
+may not be declared and end in two different nested METADATA and the same
+depth.
+\end{itemize}
+
+A series of test inputs is contained in
+\S\ref{sec:configtest}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Lookup Tables}
+
+Lookup tables store a variety of values indexed on a certain column.
+An example is for storing the difference between UT1 and UTC, and the
+polar motion vector as a function of date.
+
+One of the key functionalities of a lookup table is to read data from
+an ordinary text file into an array of vectors.  This functionality is
+generally useful, and so we specify a separate function that may be
+called independently:
+\begin{prototype}
+psArray *psVectorsReadFromFile(const char *filename, const char *format);
+\end{prototype}
+\code{psVectorsReadFromFile} shall return an array of
+\code{psVector}s, read from the specified \code{filename}.  The file
+shall be plain text, consisting of an identical number of columns on
+each line, with the values separated by whitespace.  Lines commencing
+with a comment character (the pound sign, \code{#}) and blank lines
+shall be ignored.  The \code{format} is a \code{scanf}-like format
+which specifies the number of columns in the file, as well as their
+types.  The following formats shall be defined: \code{\%d} for psS32,
+\code{\%ld} for psS64, \code{\%f} for psF32, and \code{\%lf} for
+psF64.  A star (\code{*}) in the format shall indicate that the column
+is to be skipped.  If the input file does not conform to the expected
+format (e.g., not enough columns in a row) the function shall generate
+an error and return \code{NULL}.
+
+\begin{datatype}
+typedef struct {
+    const char *filename;               ///< File from which data is to be read
+    const char *format;                 ///< scanf-like format string for file
+    long indexCol;                      ///< Column of the index vector (starting at zero)
+    psVector *index;                    ///< Index values
+    psArray *values;                    ///< Corresponding values: an array of vectors
+    const double validFrom;             ///< Minimum index value for validity
+    const double validTo;               ///< Maximum index value for validity
+} psLookupTable;
+\end{datatype}
+
+\code{filename} shall specify the file from which the lookup table
+data is to be read.  \code{format} shall contain a \code{scanf}-like
+format string specifying how the columns are to be interpreted (see
+\code{psVectorsReadFromFile}).  \code{indexCol} shall specify the
+index of the column (with the first column having an index of zero)
+that will form the index values.  \code{index} shall contain the index
+values, which shall be sorted in increasing order.  The \code{values}
+shall consist of an array of vectors, each of the same length as the
+\code{index} vector.  The vectors (including the \code{index} and all
+vectors in the \code{values} array) may be any numerical type except
+complex types.  The \code{validFrom} and \code{validTo} shall specify
+the range of valid values for the index; in most cases, these will
+simply be the first and last indices.
+
+The constructor shall be:
+\begin{prototype}
+psLookupTable *psLookupTableAlloc(const char *filename, const char *format,
+                                  long indexCol);
+\end{prototype}
+This function shall allocate a \code{psLookupTable}, and set the
+appropriate values, but it shall not read the lookup table.  This is
+so that the lookup table can be specified at the initialisation of a
+program, but not read unless required.
+
+The destructor shall free all the components.
+
+\begin{prototype}
+psLookupTable *psLookupTableImport(psLookupTable *table, const psArray *vectors,
+                                   long indexCol);
+\end{prototype}
+\code{psLookupTableImport} shall import an array of vectors into a
+\code{table}.  If \code{table} is \code{NULL}, a new
+\code{psLookupTable} shall be allocated and returned.  The array of
+\code{vectors}, which was likely generated by
+\code{psVectorsReadFromFile}, are imported by setting the
+\code{table->index} to the vector specified by the \code{indexCol},
+and pointing the \code{table->values} array data to the remaining
+vectors in \code{vectors}.  Reference counters for the vectors shall
+be incremented as appropriate.  The \code{validFrom} and
+\code{validTo} members of the \code{table} shall be set to the first
+and last values in the index vector.  If the \code{index} vector is
+not sorted in the file, the lookup table shall be sorted prior to the
+function returning.
+
+\begin{prototype}
+long psLookupTableRead(psLookupTable *table);
+\end{prototype}
+\code{psLookupTableRead} combines \code{psVectorsReadFromFile} and
+\code{psLookupTableImport} to read the appropriate file and import the
+data into the extant \code{table}.  If the input \code{table} has
+already been read from a file, the file shall be re-read, and the
+contents replaced.  The function shall return the number of lines read
+(not including ignored lines).
+
+Interpolation on a lookup table is performed by the following
+functions:
+
+\begin{prototype}
+double psLookupTableInterpolate(const psLookupTable *table, double index,
+                                long column);
+psVector *psLookupTableInterpolateAll(const psLookupTable *table, double index);
+\end{prototype}
+Both functions shall interpolate the \code{table} at the provided
+\code{index}.  For \code{psLookupTableInterpolate}, only the value in
+the specified \code{column} shall be calculated and returned.  For
+\code{psLookupTableInterpolateAll}, all the values shall be calculated
+and returned as a \code{psVector}, the type of which shall be
+\code{PS_TYPE_F64}.
+
+If the \code{index} is beyond the range of the \code{table},
+\code{psLookupTableInterpolate} shall return \code{NaN}, and
+\code{psLookupTableInterpolateAll} shall return \code{NULL} --- that
+is, no attempt is made at extrapolation.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Mathematical Structures}
+\label{sec:mathStructures}
+
+Throughout PSLib, we require a variety of structures which correspond
+to different mathematical data concepts.  For example, we have a data
+structure which corresponds to one-dimensional arrays (vectors) of
+different data types (\code{int}, \code{float}, etc).  We also have a
+data structure which corresponds to two-dimensional arrays (images or
+matrices), again with different data types for the individual
+elements.
+
+A variety of functions perform operations which are equivalent for
+different data types of the same dimension, or may even be defined for
+different data types of different dimensions.  For example, if we
+write the operation $x + y$, the operation is clearly defined
+regardless of whether the operands $x$ and $y$ are both zero
+dimensional (single numbers), one dimensional (vectors), two
+dimensional (images), etc. It is even reasonable to define the meaning
+of such an operation if the data dimensions do not match: if $x$ is a
+scalar and $y$ is an image, the natural operation is to add the value
+of $x$ to every element of $y$; we can also define the meaning of the
+operation if $x$ is a vector and $y$ is a matrix.  Nor does it matter
+mathematically that the element data types match; the sum of a float
+and an integer is a well-defined quantity.  One constraint should be
+noted: the size of the elements in each dimension must match.  For
+example, if $x$ were a vector of 100 elements, but $y$ were a vector
+of 1000 elements, the meaning of the operation $x + y$ is unclear.
+This type of operation should be invalid and should generate an error.
+
+Given that some functions should be able to operate equivalently (or
+identically) on a wide range of data types, we define a mechanism
+which allows the C functions to accept a generic data type, and
+determine the type of the data on the basis of the data.  
+Supported data types must be defined by a structure in which
+the first element is always of type \code{psMathType}:
+\begin{datatype}
+typedef struct {
+    psElemType type;                    ///< The type
+    psDimen dimen;                      ///< The dimensionality
+} psMathType;
+\end{datatype}
+where \code{psDimen dimen} defines the dimensionality of the data:
+\begin{datatype}
+typedef enum {
+    PS_DIMEN_SCALAR,                    ///< Scalar
+    PS_DIMEN_VECTOR,                    ///< A vector
+    PS_DIMEN_TRANSV,                    ///< A transposed vector
+    PS_DIMEN_IMAGE,                     ///< An image (matrix)
+    PS_DIMEN_OTHER                      ///< Not supported for arithmetic
+} psDimen;
+\end{datatype}
+\code{psElemType type}, which defines the data type of each element, has
+already been defined
+
+We discuss the application of \code{psMathType} in more detail in
+section~\ref{sec:arithmetic}.  
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Scalars}
+
+We define a basic scalar data type which includes the type
+information.  This structure allows scalars to be used in functions
+which interpret the data type from the structure when deciding how to
+perform an operation.  The basic scalar structure is:
+\begin{datatype}
+typedef struct {
+    psMathType type;                    ///< data type information
+    union {                            
+        psS8   S8;                      ///< bye value entry
+        psS16 S16;                      ///< short int value entry
+        psS32 S32;                      ///< int value entry
+        psS64 S64;                      ///< long int value entry
+        psU8   U8;                      ///< unsigned byte value entry
+        psU16 U16;                      ///< unsigned short int value entry
+        psU32 U32;                      ///< unsigned int value entry
+        psU64 U64;                      ///< unsigned long int value entry
+        psF32 F32;                      ///< float value entry
+        psF64 F64;                      ///< double value entry
+        psC32 C32;                      ///< float complex value entry
+        psC64 C64;                      ///< double complex value entry
+    } data;
+} psScalar;
+\end{datatype}
+
+In addition, we specify two functions for working with \code{psScalar} data:
+\begin{prototype}
+psScalar *psScalarAlloc(double complex value, psElemType type);
+psScalar *psScalarCopy(const psScalar *value);
+\end{prototype}
+The first creates a \code{psMathType}-ed structure from a constant
+value, casting it as appropriate based on the \code{type}.  The second
+copies the provided \code{psScalar} value.  This latter function is
+necessary to keep a copy of an existing \code{psScalar} value, since
+\code{psBinaryOp} and \code{psUnaryOp} are required to free incoming
+\code{psScalar} data (see \S\ref{sec:arithmetic}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Vectors}
+
+We require several related types of basic one-dimensional arrays:
+arrays of values of type \code{int}, \code{float}, \code{double},
+\code{float complex}, and \code{double complex}.  We have defined a
+single structure, \code{psVector} to represent these concepts:
+%
+\begin{datatype}
+typedef struct {
+    psMathType type;                    ///< vector data type and dimension
+    long n;                             ///< size of vector
+    const long nalloc;                  ///< allocated data block
+    union {
+        psS8  *S8;                      ///< Pointers to byte data
+        psS16 *S16;                     ///< Pointers to short-integer data
+        psS32 *S32;                     ///< Pointers to integer data
+        psS64 *S64;                     ///< Pointers to long-integer data
+        psU8  *U8;                      ///< Pointers to unsigned-byte data
+        psU16 *U16;                     ///< Pointers to unsigned-short-integer data
+        psU32 *U32;                     ///< Pointers to unsigned-integer data
+        psU64 *U64;                     ///< Pointers to unsigned-long-integer data
+        psF32 *F32;                     ///< Pointers to floating-point data
+        psF64 *F64;                     ///< Pointers to double-precision data
+        psC32 *C32;                     ///< Pointers to complex floating-point data
+        psC64 *C64;                     ///< Pointers to complex double-precision data
+    } data;
+    void *lock;                         ///< Lock for thread safety
+} psVector;
+\end{datatype}
+%
+In this structure, the argument \code{n} is the length of the array
+(the number of elements); \code{nalloc} is the number of elements
+allocated (\code{nalloc} $\ge$ \code{n}).  The allocated memory is
+available in the union \code{data} which consists of pointers to each
+of the defined primitive data types.  Note the parallelism in the
+names of the types, union elements, and the \code{psElemType} names.
+This parallelism allows us to use automatic construction mechanisms
+effectively.  The data type is defined by the first element,
+\code{psMathType}.  The structure is associated with a constructor and
+reallocator:
+%
+\begin{prototype}
+psVector *psVectorAlloc(long nalloc, psElemType type);
+psVector *psVectorAllocEmpty(long nalloc, psElemType type);
+psVector *psVectorRealloc(psVector *vector, long nalloc);
+psVector *psVectorRecycle(psVector *vector, long nalloc, psElemType type);
+\end{prototype}
+%
+In these functions, \code{nalloc} is the number of elements to
+allocate.  For \code{psVectorAlloc}, the value of \code{psVector.n} is
+initially set to the number of allocated values; for
+\code{psVectorAllocEmpty}, the value of \code{psVector.n} is initially
+set to zero.  For \code{psVectorRealloc}, if the value of
+\code{nalloc} is smaller than the current value of \code{psVector.n},
+then \code{psVector.n} is set to \code{nalloc}, the array is adjusted
+down to match \code{nalloc}, and the extra elements are lost.  If
+\code{nalloc} is larger than the current value of \code{psVector.n},
+\code{psVector.n} is left intact.  If the value of \code{vector} is
+\code{NULL}, then \code{psVectorRealloc} must generate an error.
+
+\code{psVectorRecycle} shall recycle the input \code{vector}, such
+that the output \code{psVector} matches the length required for
+\code{nalloc} elements of the specified \code{type}.  In the event
+that the input \code{vector} is \code{NULL}, a new \code{psVector}
+shall be allocated and returned.
+
+\begin{prototype}
+long psVectorLength(const psVector *vector);
+\end{prototype}
+
+This function returns the length of the vector (\code{psVector.n}).
+
+\begin{prototype}
+psVector *psVectorExtend(psVector *vector, long delta, long nExtend);
+\end{prototype}
+
+This function increments \code{psVector.n} (the number of elements in
+the vector) by \code{nExtend}.  If the current length of the vector
+plus {\em twice} the number of new elements (\code{nExtend}) is
+greater than the allocated space, an additional \code{delta} elements
+are allocated.  The function shall generate an error if \code{nExtend}
+is negative.  If the value of \code{delta} is less than 1, 10 shall be
+used.
+
+Here is an example of how \code{psVectorExtend} is used to
+automatically increment the vector length.
+\begin{verbatim}
+  // create data vector
+  psVector *y = psVectorAlloc(100, PS_TYPE_F32);
+  y->n = 0;
+  for (int i = 0; i < 1000; i++) {
+    y->data.F32[y->n + 0] = 2*i;
+    y->data.F32[y->n + 1] = 2*i;
+    y->data.F32[y->n + 2] = 2*i;
+    y = psVectorExtend (y, 100, 3);
+    // increments n by 3, extends length by 100 if needed
+  }
+\end{verbatim}
+Note that the specification that the allocation always be greater than
+the number of elements by twice the number of new elements implies
+that there will be room on the next loop for \code{nExtend} new
+elements, as in this example.
+
+\begin{prototype}
+psVector *psVectorCopy(psVector *output, const psVector *input, psElemType type);
+\end{prototype}
+
+\code{psVectorCopy} shall copy the elements in the \code{input} vector
+to the \code{output} vector, casting to the specified \code{type}.  In
+the event that the \code{output} is \code{NULL}, a new \code{psVector}
+shall be allocated.  The returned \code{psVector} shall be of the
+given \code{type}.
+
+\begin{prototype}
+bool psVectorInit(psVector *vector, double value);
+\end{prototype}
+
+\code{psVectorInit} shall initialize the vector with the given value.
+The input data is cast to match the vector datatype, allowing for
+integers to be preserved.
+
+\begin{prototype}
+bool psVectorSet(psVector *input, long position, double complex value);
+double complex psVectorGet(const psVector *input, long position);
+\end{prototype}
+
+These accessor functions are provided as a convenience to the user.
+\code{psVectorSet} sets the value of the \code{input} vector at the
+specified \code{position} to \code{value} (appropriately cast),
+returning \code{true} if successful.  \code{psVectorGet} returns the
+value of the \code{input} vector at the specified \code{position}.  A
+negative \code{position} means index from the end.
+
+\begin{prototype}
+psVector *psVectorCreate(psVector *input, double lower, double upper, double delta, psElemType type);
+\end{prototype}
+
+This function creates a new vector, or reallocates the provided vector
+if \code{input} is not \code{NULL}.  The created vector consists of
+the data range starting at \code{lower}, running to \code{upper}, in
+steps of \code{delta}.  The upper-end value is {\em exclusive}; the
+sequence is equivalent to \code{for (x = lower; x < upper; x += delta)}.
+
+\begin{prototype}
+psString psVectorToString(const psVector *vector, int maxLength);
+\end{prototype}
+
+This function returns a string (of maximum length \code{maxLength})
+containing the values in the \code{vector}, separated by whitespace.
+This function is useful for testing and output.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Images}
+
+The most important data product produced by the telescope is an image.
+The simplest image is a 2-D collection of pixels, each with some
+value.  We require a basic image data type:
+
+\begin{datatype}
+typedef struct psImage {
+    const psMathType type;              ///< image data type and dimension
+    const int numCols;                  ///< Number of columns in image
+    const int numRows;                  ///< Number of rows in image.
+    int col0;                           ///< Column position relative to parent.
+    int row0;                           ///< Row position relative to parent.
+    union {
+        psS8  **S8;                     ///< Pointers to char data
+        psS16 **S16;                    ///< Pointers to short-integer data
+        psS32 **S32;                    ///< Pointers to integer data
+        psS64 **S64;                    ///< Pointers to long-integer data
+        psU8  **U8;                     ///< Pointers to unsigned-char data
+        psU16 **U16;                    ///< Pointers to unsigned-short-integer data
+        psU32 **U32;                    ///< Pointers to unsigned-integer data
+        psU64 **U64;                    ///< Pointers to unsigned-long-integer data
+        psF32 **F32;                    ///< Pointers to floating-point data
+        psF64 **F64;                    ///< Pointers to double-precision data
+        psC32 **C32;                    ///< Pointers to complex floating-point data
+        psC64 **C64;                    ///< Pointers to complex double-precision data
+        psPtr *V;                       ///< Pointers to untyped data
+    } data;
+    const struct psImage *parent;       ///< parent, if a subimage 
+    psPtr p_rawDataBuffer;              ///< Raw data; private
+    psArray *children;                  ///< children of this region
+    void *lock;                         ///< Lock for thread safety
+} psImage;
+\end{datatype}
+
+This structure represents an image consisting of a 2-D array of
+pixels.  The size of this array is given by the elements
+\code{(numRows, numCols)}.  The data type of the pixel is defined in
+the \code{psMathType type} entry (specifically, the \code{psElemType}
+member, \code{type}; see \S\ref{sec:arithmetic}).  (N.B.\ that for
+FITS images, these values are restricted to the datatypes equivalent
+to the valid BITPIX values 8, 16, 32, -32, -64).  The image
+represented in the data structure may represent a subset of the pixels
+in a complete array, in which case the image is considered to be the
+child of that parent array.  The offset of the \code{(0,0)} pixel in
+this array relative to the parent array is given by the elements
+\code{(col0,row0)}: \code{col0} is the starting column number in the
+parent image while \code{row0} is the starting row number.  The
+structure may include references to subrasters (\code{children})
+and/or to a containing array (\code{parent}).  Unless this image is a
+child of another image (represents a subset of the pixels of another
+image), the image data is allocated in a contiguous block
+(\code{data.V}).
+
+\subsubsection{Support Functions}
+
+We define the following supporting functions for images, which are
+valid for data types \code{psS8, psS16, psU8, psU16, psF32, psF64,
+psC32, psC64}.
+
+\begin{prototype}
+psImage *psImageAlloc(int numCols, int numRows, psElemType type);
+\end{prototype}
+Create an image of a specified \code{numCols}, \code{numRows}, and data
+\code{type}.  This function must allow any of the valid image data
+types and not restrict to the valid FITS BITPIX types.  The image
+dimensionality must be 2.  
+
+\begin{prototype}
+psImage* psImageRecycle(psImage* old, int numCols, int numRows, const psElemType type);
+\end{prototype}
+\code{psImageRecycle} shall recycle the input \code{old} image, such
+that the output \code{psImage} matches the specified size
+(\code{numCols}$\times$\code{numRows}) and \code{type}.  In the event
+that the input \code{old} image is \code{NULL}, a new \code{psImage}
+shall be allocated and returned.
+
+\begin{prototype}
+int psImageFreeChildren(psImage *image);
+\end{prototype}
+
+\code{psImageFreeChildren} shall free all child images of the given
+\code{image}.
+
+\begin{prototype}
+bool psImageInit(psImage *image, double value);
+\end{prototype}
+
+\code{psImageInit} shall initialize the image with the given value.
+The input data is cast to match the image datatype, allowing for
+integers to be preserved.
+
+\begin{prototype}
+bool psImageSet(psImage *image, int x, int y, double complex value);
+double complex psImageGet(const psImage *image, int x, int y);
+\end{prototype}
+
+These accessor functions are provided as a convenience to the user.
+\code{psImageSet} sets the value of the \code{image} at the specified
+\code{x,y} position to \code{value} (appropriately cast), returning
+\code{true} if successful.  \code{psImageGet} returns the value of the
+\code{image} at the specified \code{x,y} position.  A negative value
+for the \code{x,y} position means index from the end.
+
+\subsubsection{Image Regions}
+
+In many places, we need to refer to a rectangular area.  We define a
+structure to represent a rectangle:
+\begin{datatype}
+typedef struct {
+    float x0;
+    float x1;
+    float y0;
+    float y1;
+} psRegion;
+\end{datatype}
+This structure is used in psLib as an abbreviation for the four
+floats, and is defined statically.  Functions which accept or return a
+\code{psRegion} shall do so by value, not by pointer.
+
+In the limited cases where we wish to store a \code{psRegion} on one
+of the containers, we need an attached \code{psMemBlock}, which can
+be obtained by calling \code{psRegionAlloc}:
+\begin{prototype}
+psRegion *psRegionAlloc(float x0, float x1, float y0, float y1);
+\end{prototype}
+
+All functions which use a \code{psRegion} must interpret the
+definition of $(x0,y0)$ and $(x1,y1)$ in the same way.  The coordinate
+$(x0,y0)$ defines the starting pixel in the region.  The coordinate
+$(x1,y1)$ defines the outer bound of the region, and are NOT included
+in the region.  The size of the region is $(x1-x0,y1-y1)$.  If either
+$x1$ or $y1$ is less than or equal to 0, the value is added to the
+image dimensions (e.g., $Nx + x1$).  Thus a region \code{[0:0,0:0]}
+refers to the full image array, while \code{[0:-10,0:-20]} refers to
+the entire image, minus the last 10 columns and the last 20 rows.
+
+We define two functions to set and return the value of a
+\code{psRegion}.  The first defines the region by the corner
+coordinates.  The second function converts the IRAF description region
+in the form \code{[x0:x1,y0:y1]}, used for header entries such as
+\code{BIASSEC}, into the corresponding \code{psRegion} structure (any
+values that do not parse correctly shall be returned as \code{NaN}).
+We also define a function that converts a \code{psRegion} to the
+corresponding IRAF description.
+
+\begin{prototype}
+psRegion psRegionSet(float x0, float x1, float y0, float y1);
+psRegion psRegionFromString(const char *region);
+psString psRegionToString(const psRegion region);
+\end{prototype}
+
+{\bf Note that regions specified by strings are in the FITS standard.}
+It is the responsibility of \code{psRegionFromString} and
+\code{psRegionToString} to convert between the PS standard (0 means
+first pixel; upper value is not included, but lower is) and the FITS
+standard (1 means first pixel; lower and upper values are included),
+which simply involves subtracting one from \code{x0} and \code{y0}
+when going from a string representation to a \code{psRegion}.  A
+\code{NULL} string is allowed and is equivalent to the default region
+(\code{0:0,0:0}).
+
+\begin{prototype}
+psRegion psRegionForImage(psImage *image, psRegion in);
+\end{prototype}
+
+An image region defined with negative upper limits may be rationalized
+for the bounds of a specific image with \code{psRegionForImage}.  The
+output of this function is a region with negative upper limits
+replaced by their corrected value appropriate to the given image.  In
+addition, the lower and upper limits are forced to lie within the
+bounds of the image.  If the lower-limit coordinates are less than the
+lower bound of the image, they are limited to the lower bound of the
+image.  Conversely, if the upper-limit coordinates are greater than
+the upper bound of the image, they are truncated to define only valid
+pixels.  If the lower-limit coordinates are greater than the upper
+bounds of the image, or the upper-limit coorindates are less than the
+lower bounds of the image, the coordinates should saturate on those
+limits.  The output of this function is always a valid region, though
+it may define an area of 0 pixels.  If \code{image} is a subimage, the
+input and output region coordinates refer to the parent pixel
+coordinates.  The only exception to this statement is that the
+negative limits should be applied to the upper limits of the subimage,
+not the parent.  Thus, if we have an input subimage with
+\code{col0,row0} of (10,20), and \code{numCols,numRows} of 1000,1000
+(implying parent image dimensions of at least 1010,1020), we would
+have the following conversions:
+\begin{verbatim}
+(50:100,50:100) -> (50:100,50:100)   : no change (region within image)
+(0:0,0:0)       -> (10:1010,20:1020) : upper and lower limits constrained 
+(5:-5,5:-5)     -> (10:1005,20:1015)
+(5:1020,5:1020) -> (10:1010,20:1020)
+\end{verbatim}
+
+\begin{prototype}
+psRegion psRegionForSquare(double x, double y, double radius);
+\end{prototype}
+This utility function defines a \code{psRegion} corresponding to the
+square with center at coordinate \code{x,y} and with code{radius}.
+The width of the square is thus \code{2radius + 1}.
+
+The following fucntions provide tests of the validity of
+\code{regions}
+\begin{prototype}
+bool psRegionIsNaN(psRegion region);
+\end{prototype}
+
+\pagebreak 
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Input/Output}
+
+\subsection{XML Functions}
+
+\tbd{The XML code is currently disabled in psLib.  Instead, we use the
+metadata config files as a transport mechanism.}
+
+Within Pan-STARRS, we will use XML documents as a transport mechanism
+to carry data between programs and between IPP and other subsystems.
+Configuration information may be stored as well as XML documents, in
+addition to the text format discussed in the discussion on Metadata.
+XML is an extremely variable document format, and it is not currently
+the intention of PSLib to provide a complete PSLib version of XML
+operations.  Rather, a limited number of operations are defined to
+convert specific data structures to an appropriate XML document.  To
+maximize the simplicity of the XML APIs, we will use the convention
+that a single XML document to be parsed by PSLib shall contain only a
+single data structure.  Each of the XML APIs therefore take as input a
+reference to a complete XML document and return a PSLib data
+structure, or take a PSLib data structure and return a complete XML
+document.
+
+We start by defining a PSLib wrapper type which is a pointer to an XML
+document in memory.  We wrap the \code{libxml2} version of an XML
+document pointer for now:
+\begin{datatype}
+typedef xmlDocPtr psXMLDoc;
+\end{datatype}
+
+\begin{prototype}
+psXMLDoc *psXMLDocAlloc(void);
+\end{prototype}
+
+The next pair of functions convert a \code{psMetadata} data structure
+to a complete \code{psXMLDoc} (in memory) and vice versa:
+\begin{prototype}
+psXMLDoc *psMetadataToXMLDoc(const psMetadata *md);
+psMetadata *psXMLDocToMetadata(const psXMLDoc *doc);
+\end{prototype}
+
+The next pair of functions loads the data in a named file into a
+complete \code{psXMLDoc} (in memory) and write out the \code{psXMLDoc}
+to a named file:
+\begin{prototype}
+psXMLDoc *psXMLParseFile(const char *filename);
+bool psXMLDocToFile(const psXMLDoc *doc, const char *filename);
+\end{prototype}
+
+The next pair of functions accepts a block of memory and parses it
+into a complete \code{psXMLDoc} (also in memory), and vice versa:
+\begin{prototype}
+psXMLDoc *psXMLParseMem(const char *buffer, int size);
+bool psXMLDocToMem(const psXMLDoc *doc, char *buffer);
+\end{prototype}
+
+The next pair of functions read from and write to a file descriptor.
+The first converts the incoming data to a complete \code{psXMLDoc}
+(also in memory), the second writes the \code{psXMLDoc} to the file
+descriptor:
+\begin{prototype}
+psXMLDoc *psXMLParseFD(int fd);
+bool psXMLDocToFD(const psXMLDoc *doc, int fd);
+\end{prototype}
+
+\subsection{Database Functions}
+
+Many of the applications that PSLib will be used for will require
+access to a simple relational database.  PSLib includes generic
+database-independent interface mechanisms as part of its API set.  The
+most important aspect of PSLib's database support is to abstract as
+much database specific complexity as is feasible.  As almost all RDBMS
+provide at least a simple transactional model, commit and rollback
+support should be provided.
+
+Currently, only support for MySQL 4.1.x is required but other backends
+may be added as options in the future.  As a particular example which
+has implications for the database interaction model, support for
+SQLite may be required in the future.  Currently, the choice of
+backend database interface may be made as a compile option.  Details
+of the specified APIs in the discussion below refer to the relevant
+MySQL functions.
+
+Database errors must be trapped and placed onto the psError stack.
+The complete error message should be retrieved with the database's
+error function.
+
+\subsubsection{Managing the Database Connection}
+
+We specify a database handle which carries the information about the
+database connection:
+
+\begin{datatype}
+typedef struct {
+    void *mysql;
+} psDB;
+\end{datatype}
+
+The following collection of functions provides basic database functionality:
+
+\begin{prototype}
+psDB *psDBInit(const char *host, const char *user, const char *passwd,
+               const char *dbname, unsigned int port);
+void psDBCleanup(psDB *dbh);
+bool psDBCreate(psDB *dbh, const char *dbname);
+bool psDBChange(psDB *dbh, const char *dbname);
+bool psDBDrop(psDB *dbh, const char *dbname);
+\end{prototype}
+
+For MySQL support, \code{psDBInit} wraps \code{mysql_init} and
+\code{mysql_real_connect} in order to initialize a psDB structure and
+establish a database connection.  A null pointer should be returned on
+failure.
+
+When implementing support for SQLite, or other DB which is purely
+file-based, the \code{host}, \code{user}, and \code{passwd} arguments
+would be ignored while \code{dbname} would specify the path to the
+SQLite db file.
+
+\code{psDBCleanup} shall wrap \code{mysql_close}.  \code{psDBCreate}
+shall wrap \code{mysql_create_db}.  \code{psDBChange} shall wrap
+\code{mysql_select_db}.  \code{psDBDrop} shall wrap
+\code{mysql_drop_db}.
+
+\subsubsection{Interacting with Database Tables}
+
+The functions in this section perform high level interactions with the
+database tables.  All of them should behave ``atomically'' with
+respect to the state of the database.  Specifically, all interactions
+with the database should be done as a part of a transaction that is
+rolled-back on failure and committed only after all queries used by
+the API have been run.  In general, this API set attempts to treat a
+database table as a 2D matrix where columns can be represented by a
+\code{psVector} and rows as a \code{psMetadata} type.  A
+\code{psMetadata} collection is also used to define the columns of a
+table and as part of the query restrictions.
+
+\begin{prototype}
+bool psDBCreateTable(psDB *dbh, const char *tableName, const psMetadata *md);
+\end{prototype}
+
+This function generates and executes the SQL needed to create a table
+named \code{tableName}, with the column names and datatypes as
+described in \code{md}.  Each data item in the \code{psMetadata}
+collection represents a single table field.  The name of the field is
+given by the name of the \code{psMetadataItem} and the data type is
+given by the \code{psMetadataItem.type} entry.  A lookup table should
+be used to convert from PSLib types into MySQL compatible SQL data
+types.  For example, a \code{PS_DATA_STR} would map to an SQL99
+varchar.  If the value of \code{type} is \code{PS_DATA_STR} then the
+\code{psMetadataItem.data} element is set to a string with the length
+for the field written as a text string.  The value of the
+\code{psMetadataItem.data} element is unused for the
+\code{PS_DATA_PRIMITIVE} types.  Other metadata types beyond
+\code{PS_DATA_STR} and \code{PS_DATA_PRIMITIVE} are not allowed in a
+table definition metadata collection.
+
+Database indexes can be specified by setting the \code{comment} field to
+``\code{Primary Key}'' or ``\code{Key}''.  ``Auto-incrementing'' columns may be
+specified by setting the \code{comment} field to ``\code{AUTO_INCREMENT}''.
+Indexes and auto-increments maybe be combined in the same \code{comment}.  They
+must be separated by optional whitespace, a comma, and then more optional
+whitespace.  The \code{comment} field is otherwise ignored.
+
+\begin{prototype}
+bool psDBDropTable(psDB *dbh, const char *tableName);
+\end{prototype}
+
+This function deletes the specified table.
+
+\begin{prototype}
+bool p_psDBRunQuery(psDB *dbh, const char *format, ...);
+\end{prototype}
+
+This function will execute a string as a raw SQL query.  \code{format} is a
+\code{printf} style formatting code to be implimented with \code{vsprintf()}.
+No additional processing of the string or abstraction of the underlying
+database's SQL dialect is provided.
+
+\begin{prototype}
+psArray *psDBSelectColumn(psDB *dbh, const char *tableName, const char *col, unsigned long long limit);
+psVector *psDBSelectColumnNum(psDB *dbh, const char *tableName, const char *col,
+                              psElemType type, unsigned long long limit);
+\end{prototype}
+
+These functions generates and executes the SQL needed to select an entire
+column from a table or up to \code{limit} rows from it.  If \code{limit} is 0,
+the entire range is returned.  The database response is processed and a
+\code{psArray} of strings is returned.  The Num version of the function returns
+the data in a \code{psVector}, data cast to \code{type}.  It returns an error
+(NULL) if the requested field is not a numerical type.
+
+\begin{prototype}
+psArray *psDBSelectRows(psDB *dbh, const char *tableName, const psMetadata *where,
+                        unsigned long long limit);
+\end{prototype}
+
+This function returns rows from the specified table which match
+the restrictions given by \code{where}.  The restrictions are
+specified as field / value pairs.  The \code{psMetadata} collection
+where must consist of valid database fields, though the database query
+checking functions may be used to validate the fields as part of the
+query.  If \code{where} is \code{NULL}, then there are no restrictions
+on the rows selected.  The selected rows are returned as a
+\code{psArray} of \code{psMetadata} values, one per row. 
+
+\begin{prototype}
+bool psDBInsertOneRow(psDB *dbh, const char *tableName, const psMetadata *row);
+\end{prototype}
+
+Insert the data from \code{row} into \code{tableName}.  It should be noted in
+the API reference that if fields are specified in \code{row} that do not exist
+in \code{tablename}, the insert will fail.
+
+\begin{prototype}
+bool psDBInsertRows(psDB *dbh, const char *tableName, const psArray *rowSet);
+\end{prototype}
+
+Similar to \code{psDBInsertOneRow()}, this function inserts many rows at once
+and is atomic for the complete set of rows.
+
+\begin{prototype}
+psArray *psDBDumpRows(psDB *dbh, const char *tableName);
+\end{prototype}
+
+Fetch all rows as an psArray of psMetadata.
+
+\begin{prototype}
+psMetadata *psDBDumpCols(psDB *dbh, const char *tableName);
+\end{prototype}
+
+Fetch all columns, as either a psVector or a psArray depending on whether or not
+the column is numeric, and return them in a psMetadata structure where
+psMetadataItem.name contains the column's name.
+
+\begin{prototype}
+long psDBUpdateRows(psDB *dbh, const char *tableName, const psMetadata *where,
+                    psMetadata *values);
+\end{prototype}
+
+Update the columns contained in \code{values} in the row(s) that have a field
+with the value indicated by \code{where} (note that this is only allows very
+limited use of SQL99's ``where'' semantics).  The number of rows modified is
+returned.  A negative value is return to indicate an error. If there are
+multiple psMetadataItems in \code{where} then each item should be considered as
+an additional constraint.  e.g.  ``where foo = x and where bar = y''
+
+\begin{prototype}
+long psDBDeleteRows(psDB *dbh, const char *tableName, const psMetadata *where,
+                    unsigned long long limit);
+\end{prototype}
+
+Delete the rows that are matched by \code{where} using the same semantics for
+\code{where} as in psDBUpdateRow().  \code{limit} specifies the maximum number
+of rows that may be deleted per invocation.  A negative value is returned to
+indicate an error.
+
+\begin{prototype}
+long psDBLastInsertID(psDB *dbh);
+\end{prototype}
+
+Returns the last value created in an ``auto-increment'' field.
+
+
+\begin{prototype}
+psString psDBGenerateWhereConditionSQL(const psMetadata *where, const char *tableName);
+psString psDBGenerateWhereSQL(const psMetadata *where, const char *tableName);
+\end{prototype}
+
+\code{psDBGenerateWhereConditionSQL} shall generate a SQL fragment
+consisting of the \code{WHERE} condition, based on the \code{where}
+metadata format.
+
+\code{psDBGenerateWhereSQL} shall append ``where'' to the front of
+the SQL fragment returned by \code{psDBGenerateWhereConditionSQL}.
+
+
+\subsubsection{Transaction Control Database Functions}
+
+By default the database functions shall behave as if each operation effects a
+permanent change to the database.  An API is provided to change this behavior
+such that operations may be grouped together into a ``transaction'' that is
+atomic.
+
+\begin{prototype}
+bool psDBExplicitTrans(psDB *dbh, bool mode);
+\end{prototype}
+
+Enable/Disable explicit transactions (accordinte to \code{mode}).
+When enabled \code{psDBCommit()} must be called to make changes to the
+database's state persistent.
+
+\begin{prototype}
+bool psDBTransaction(psDB *dbh);
+\end{prototype}
+
+Start a new transaction.
+
+\begin{prototype}
+bool psDBCommit(psDB *dbh);
+\end{prototype}
+
+Commit the current transaction.
+
+\begin{prototype}
+bool psDBRollback(psDB *dbh);
+\end{prototype}
+
+Undo the current transaction.
+
+
+\subsubsection{Low Level Database Functions}
+
+This collection of functions is primarily intended for us internally by the
+other database functions.  However, in certain cases their direct use may be
+needed to express semantics that are not supported by the public database
+interface.
+
+\begin{prototype}
+long p_psDBRunQueryPrepared(psDB *dbh, const psArray *rowSet,
+                            const char *format, ...);
+\end{prototype}
+
+\code{p_psDBRunQueryPrepared()} is similar to \code{p_psDBRunQuery()} except
+that \code{format} is expected to be a query string with value ``place
+holders'' in it.  This allows the data in \code{rowSet} is inserted into the
+database via the use of a more efficent ``prepared query''.
+
+\begin{prototype}
+psArray *p_psDBFetchResult(psDB *dbh);
+\end{prototype}
+
+\code{p_psDBFetchResult()} is the inverse operation of \code{p_psDBRunQuery}.
+It shall load the latest result set from the database into an \code{psArray}
+and return it.
+
+\subsubsection{Utility functions}
+
+These functions are utility functions, that assist in constructing database
+commands, and interpreting the results.
+
+\begin{prototype}
+psString psDBIntToString(psU64 value);
+\end{prototype}
+This function prints the \code{value} to a string of suitable size.
+
+
+\subsection{FITS I/O Functions}
+
+We need a variety of I/O functions between the disk and certain of our
+PSLib data structures.  We need the ability to access FITS headers,
+images and tables (both ASCII and Binary).  We define here the FITS
+I/O functions, all of which are currently specified as wrappers to
+functions within CFITSIO.  CFITSIO provides a wide range of utilities
+which we do not feel are particularly appropriate as part of a generic
+I/O library, such as assumptions about names which change the data
+interpretation, etc.  We are defining our calls to avoid the hidden
+'features'.  The CFITSIO functions which are wrapped should in general
+be the most basic versions.
+
+\begin{datatype}
+typedef struct {
+    fitsfile *fd;                  // cfitsio structure
+    bool writable;                 // Is the file writable?
+} psFits;
+\end{datatype}
+We begin by defining a datatype to wrap the CFITSIO \code{fitsfile}
+structure.  This is necessary to allow repeated access to the data in
+a file without multiple open commands (which are expensive).  Write
+operations are only permitted if \code{writable} is \code{true}.
+
+\subsubsection{FITS File Manipulations}
+
+\begin{prototype}
+psFits *psFitsOpen(const char *filename, const char *mode);
+\end{prototype}
+
+Opens a FITS file and positions the pointer to the PHU.  The file is
+opened in the requested \code{mode}, which may be one of \code{r}
+(read only) \code{r+} (read and write), \code{rw} (alias for
+\code{r+}) or \code{w} (create new file for writing).
+
+%%%%
+%%%% psFitsOpenFD and psFitsOpenStream probably can't be implemented
+%%%% using cfitsio without creating temporary files.
+%%%%
+%% \begin{prototype}
+%% psFits *psFitsOpenFD(int fd);
+%% \end{prototype}
+%% Opens a file descriptor and positions the pointer to the PHU.
+%% \begin{prototype}
+%% psFits *psFitsOpenStream(FILE *stream);
+%% \end{prototype}
+%% Opens a stream and positions the pointer to the PHU.
+
+\begin{prototype}
+bool psFitsClose(psFits *fits);
+\end{prototype}
+
+Closes a FITS file.
+
+\begin{prototype}
+bool psFitsMoveExtName(const psFits *fits, const char *extname);
+\end{prototype}
+
+Positions the pointer to the beginning of the specified
+\code{extname}.  If the \code{extname} does not exist, the function
+shall fail.  
+
+\begin{prototype}
+bool psFitsMoveExtNum(const psFits* fits, int extnum, bool relative);
+\end{prototype}
+
+Moves the pointer to the beginning of the specified HDU number.  If
+\code{relative} is TRUE, \code{extnum} represents the number of HDUs
+relative to the current HDU.  The PHU is entry number 0, while the
+extended data segments start at number 1.
+
+\begin{prototype}
+bool psFitsMoveLast(psFits *fits);
+\end{prototype}
+
+Moves the pointer to the last extension in the file.
+
+\begin{prototype}
+int psFitsGetExtNum(const psFits* fits);
+\end{prototype}
+
+Returns the current HDU number (i.e., file position).  
+
+\begin{prototype}
+int psFitsGetSize(const psFits* fits);
+\end{prototype}
+
+Returns the number of HDUs in the file.
+
+\begin{prototype}
+bool psFitsDeleteExtNum(psFits *fits, int extnum, bool relative);
+bool psFitsDeleteExtName(psFits *fits, const char *extname);
+\end{prototype}
+
+These functions are similar to \code{psFitsMoveExtNum} and
+\code{psFitsMoveExtName} except that they delete the specified
+extension.  The file pointer is left pointing to where the extension
+was before deletion.
+
+\begin{prototype}
+bool psFitsTruncate(psFits *fits);
+\end{prototype}
+
+Deletes all extensions after the position of the file pointer.
+
+\begin{datatype}
+typedef enum {
+    PS_FITS_TYPE_NONE           = -1,
+    PS_FITS_TYPE_IMAGE          = IMAGE_HDU,
+    PS_FITS_TYPE_BINARY_TABLE   = BINARY_TBL,
+    PS_FITS_TYPE_ASCII_TABLE    = ASCII_TBL,
+    PS_FITS_TYPE_ANY            = ANY_HDU
+} psFitsType;
+\end{datatype}
+
+\begin{prototype}
+psFitsType psFitsGetExtType(const psFits* fits);
+\end{prototype}
+
+Gets the current HDU's type (table or image).
+
+\begin{prototype}
+psString psFitsGetExtName(const psFits* fits);
+bool psFitsSetExtName(psFits* fits, const char* name);
+\end{prototype}
+
+\code{psFitsGetExtName} shall return the name of the current extension
+for the given \code{fits} file (as specified by the \code{EXTNAME}
+header).  \code{psFitsSetExtName} shall change the name of the current
+extension for the given \code{fits} file to \code{name}, returning
+\code{true} upon success and \code{false} otherwise.
+
+\subsubsection{FITS Header I/O Functions}
+
+\begin{prototype}
+psMetadata *psFitsReadHeader(psMetadata *out, const psFits *fits);
+\end{prototype}
+Read header data into a \code{psMetadata} structure.  The data is read
+from the current HDU pointed at by the \code{psFits *fits} entry.  If
+\code{out} is \code{NULL}, a new psMetadata is created.
+
+\begin{prototype}
+psMetadata *psFitsReadHeaderSet(psMetadata *out, const psFits *fits);
+\end{prototype}
+Load a complete set of headers from the \code{psFits} file pointer.
+This function loads the headers from all extensions into a
+\code{psMetadata} collection, each entry of which is a pointer to a
+\code{psMetadata} structure containing the header data.  The metadata
+entry names are the \code{EXTNAME} values for each header (with the
+value of \code{PHU} for the primary header unit).  At the start of the
+operation, the file pointer is rewound to the beginning of the file.
+At the end, it is positioned where it started when the function was
+called. If \code{out} is \code{NULL}, a new psMetadata is created.
+
+\begin{prototype}
+bool psFitsWriteHeader(psFits *fits, const psMetadata *output);
+\end{prototype}
+Write metadata into the header of a FITS image file.  The header is
+written at the current HDU.
+
+\begin{prototype}
+bool psFitsWriteBlank(psFits *fits, const psMetadata *output, const char *extname);
+\end{prototype}
+This function creates a header in the \code{fits} file for a 0 length
+image, setting the extension name to \code{extname}.  The resulting
+header shall have \code{NAXIS = 0}.  Any \code{NAXISi} elements
+present in the header shall be maintained as reference data.
+
+\begin{prototype}
+bool psFitsHeaderValidate(psMetadata *header);
+\end{prototype}
+
+\code{psFitsHeaderValidate} shall validate the supplied \code{header}
+so that it is in compliance to the FITS standard for header keyword
+names and types.  This involves scanning for types that cannot be
+represented in FITS, and changing the keywords to conform to the FITS
+standard where possible by using the \code{HIERARCH} keyword.  If the
+resulting \code{header} conforms to the FITS standard, the function
+shall return \code{true}; otherwise the function shall return
+\code{false}.
+
+\begin{prototype}
+bool psFitsIsImage(psMetadata *header);
+bool psFitsIsTable(psMetadata *header);
+\end{prototype}
+
+\code{psFitsIsImage} shall return \code{true} if the FITS
+\code{header} corresponds to that of a FITS image.
+\code{psFitsIsTable} shall return \code{true} if the FITS
+\code{header} corresponds to that of a FITS table.
+
+\tbd{psFitsIsImage and psFitsIsTable are not yet coded.}
+
+\subsubsection{FITS Image I/O Functions}
+
+\begin{prototype}
+psImage *psFitsReadImage(const psFits *fits, psRegion region, int z);
+\end{prototype}
+Read an image or subimage from the \code{psFits} file pointer.  This
+function is a wrapper to the CFITSIO library function.  The input
+parameters allow a full image or a subimage to be read.  The region to
+be read is specified by \code{region}.  A negative value for either of
+\code{region.x1} or \code{region.y1} specifies the size of the region
+to be read counting down from the end of the array.  
+
+If the native image is a cube, the value of z specifies the requested
+slice of the image.  This function must call \code{psError} and return
+\code{NULL} if any of the specified parameters are out of range for
+the data in the image file, or if the image on disk is zero- or
+one-dimensional.  This function need only read images of the native
+FITS image types (\code{psU8}, \code{psS16}, \code{psS32},
+\code{psF32}, \code{psF64}).  The user is expected to convert the data
+type as needed with \code{psImageCopy}.
+
+\begin{prototype}
+psImage *psFitsReadImageBuffer(psImage *output, const psFits *fits,
+                               psRegion region, int z);
+\end{prototype}
+As for \code{psFitsReadImage}, but read into the extant
+\code{output} image.                              
+ 
+\begin{prototype}
+bool psFitsUpdateImage(psFits *fits, const psImage *input, int x0, int y0, int z);
+\end{prototype}
+Write an image section to the open \code{psFits} file pointer.  This
+operation may write a portion of an image over the existing bytes of
+an existing image, starting at \code{x0,y0} in the \code{fits} image.
+Note that the origin of the \code{input} must be (0,0), not that of
+any parent (i.e., \code{input->col0,input->row0)}).  Care must be
+taken to interpret region which specifies the output pixels to be
+written / over-written.  If the combination of \code{x0,y0} and the
+size of \code{psImage *input} implies writing pixels outside the
+existing data area of the image, the function shall return an error
+(ie, if \code{x0 + image.nx >= NAXIS1}, \code{y0 + image.ny >=
+NAXIS2}, or \code{z >= NAXIS3}).  This function will only write images
+of the native FITS image types (\code{psU8}, \code{psS16},
+\code{psS32}, \code{psF32}, \code{psF64}).  The user is expected to
+convert the data type as needed with \code{psImageCopy}.  The return
+value must be 0 for a successful operation and 1 for an error.
+
+\begin{prototype}
+bool psFitsWriteImage(psFits *fits, const psMetadata *header, const psImage *input, int depth,
+                      const char *extname);
+\end{prototype}
+Create a new image based on the dimensions specified for the image and
+the requested depth.  The header and image data segments are written
+at the end of the file.  This function will only write images of the
+native FITS image types (\code{psU8}, \code{psS16}, \code{psS32},
+\code{psF32}, \code{psF64}).  The user is expected to convert the data
+type as needed with \code{psImageCopy}.  The return value must be 0
+for a successful operation and 1 for an error.
+
+\begin{prototype}
+bool psFitsInsertImage(psFits *fits, const psMetadata *header, const psImage *input, int depth,
+                       const char *extname, bool after);
+\end{prototype}
+
+\code{psFitsInsertImage} behaves in the same manner as
+\code{psFitsWriteImage}, except that the extension is inserted
+according to the value of the boolean \code{after}.  If \code{after}
+is \code{true}, then the extension is inserted after the current
+\code{psFits} pointer; otherwise the extension is inserted before the
+current \code{psFits} pointer.  \textbf{The user should beware that
+this is potentially a very expensive operation in terms of time, since
+the entire file following the inserted extension must be rewritten.}
+
+We also provide the following functions that use a \code{psArray} of
+\code{psImages}, so that the user need not worry about updating
+\code{NAXIS} headers:
+
+\begin{prototype}
+psArray *psFitsReadImageCube(const psFits *fits, psRegion region);
+bool psFitsUpdateImageCube(psFits *fits, const psArray *input, int x0, int y0);
+bool psFitsWriteImageCube(psFits *fits, psMetadata *header, const psArray *input,
+                          const char *extname);
+bool psFitsInsertImageCube(psFits *fits, psMetadata *header, const psArray *input,
+                           const char *extname, bool after);
+\end{prototype}
+
+\tbd{psFitsInsertImageCube is not yet coded.}
+
+
+\subsubsection{FITS Table I/O Functions}
+
+\begin{prototype}
+psMetadata *psFitsReadTableRow(const psFits *fits, int row);
+\end{prototype}
+This function reads a single row of the table in the extension pointed
+at by the \code{psFits} file pointer.  The row number to be read is
+given by \code{row}.  The result is returned as a \code{psMetadata}
+collection with elements of the appropriate types and keys
+corresponding to the table column names.  The function must apply the
+needed byte-swapping on the data in the row based on the description
+of the table data in the table header.  \tbr{we may need to be more
+flexible here: if we call this function repeatedly, it would be more
+efficient to pass the corresponding header or keep it somewhere (and
+the file pointer location, for that matter).}
+
+\begin{prototype}
+psPtr psFitsReadTableRowRaw(size_t *size, const psFits *fits, int row);
+\end{prototype}
+This function reads a single row of the table in the extension pointed
+at by the \code{psFits} file pointer.  The row number to be read is
+given by \code{row}.  The result is returned as collection of
+\code{size} bytes allocated by the function.  The function must
+apply the needed byte-swapping on the data in the row based on the
+description of the table data in the table header.  \tbr{we may need
+to be more flexible here: if we call this function repeatedly, it
+would be more efficient to pass the corresponding header or keep it
+somewhere (and the file pointer location, for that matter).}
+
+\tbd{psFitsReadTableRowRaw is not yet coded.}
+
+\begin{prototype}
+psArray *psFitsReadTableColumn(const psFits *fits, const char *colname);
+\end{prototype}
+This function reads a single column of the table in the extension
+pointed at by the \code{psFits} file pointer.  The column is specified
+by the FITS table column key given by \code{row}.  The result is
+returned as a \code{psArray}, with the data from one row of the table
+column per array element.
+
+\begin{prototype}
+psVector *psFitsReadTableColumnNum(const psFits *fits, const char *colname);
+\end{prototype}
+This function reads a single column of the table in the extension
+pointed at by the \code{psFits} file pointer.  The column is specified
+by the FITS table column key given by \code{row} and must be of a
+numeric data type.  The result is returned as a \code{psVector} of the
+appropriate data type, with the data from one row of the table column
+per array element.
+
+\begin{prototype}
+psArray *psFitsReadTableRaw(size_t *size, const psFits *fits);
+\end{prototype}
+This function reads the entire data block from a table into the a
+\code{psArray}, with one element of the array per row.  The number of
+bytes per row is returned in \code{size}.  The function must apply
+the needed byte-swapping on the data in each row based on the
+description of the table data in the table header.
+
+\tbd{psFitsReadTableRaw is not yet coded.}
+
+\begin{prototype}
+psArray *psFitsReadTable(const psFits *fits);
+\end{prototype}
+This function reads the entire data block from a table into the a
+\code{psArray}, with one element of the array per row.  Each row is
+stored as a \code{psMetadata} collection as described above for
+\code{psFitsReadTableRow}. 
+
+\begin{prototype}
+bool psFitsWriteTable(psFits* fits, const psMetadata *header, const psArray* table,
+                      const char *extname); 
+\end{prototype}
+Accepts a \code{psArray} of \code{psMetadata} and writes it to the end
+of the file with the given \code{extname}.
+
+\begin{prototype}
+bool psFitsUpdateTable(psFits* fits, const psMetadata* data, int row); 
+\end{prototype}
+Writes the \code{psMetadata} data to a FITS table at the specified row
+in the current HDU.  If the current HDU is not a table type, this will
+fail and return FALSE.  
+
+\begin{prototype}
+bool psFitsInsertTable(psFits *fits, const psMetadata *header, const psArray* table,
+                       const char *extname, bool after);
+\end{prototype}
+
+\code{psFitsInsertTable} behaves in the same manner as
+\code{psFitsWriteTable}, except that the extension is inserted
+according to the value of the boolean \code{after}.  If \code{after}
+is \code{true}, then the extension is inserted after the current
+\code{psFits} pointer; otherwise the extension is inserted before the
+current \code{psFits} pointer.  \textbf{The user should beware that
+this is potentially a very expensive operation in terms of time, since
+the entire file following the inserted extension must be rewritten.}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagebreak
+\section{Data manipulation}
+
+We require a variety of basic data manipulation functions which will
+act upon data (in particular, arrays/vectors).  We require the
+following capabilities:
+\begin{itemize}
+\item Vector and image arithmetic;
+\item Sorting;
+\item Statistics;
+\item Matrix operations and linear algebra;
+\item (Fast) Fourier Transforms;
+\item General mathematical functions; and
+\item Minimization and fitting routines.
+\end{itemize}
+
+\subsection{Sorting}
+
+We require the ability to sort a vector.  The following function
+returns the vector, sorted from the smallest (i.e.\ most negative)
+value in the first element, and the largest (i.e.\ most positive)
+value in the last element.  The input vector, \code{in}, may be sorted
+in-place if it is also specified as the \code{out} vector. This
+function is specified for input types \code{psS8, psU16, psF32,
+psF64}.  The input and output vectors must have the same type.
+
+\begin{prototype}
+psVector *psVectorSort(psVector *outVector, const psVector *inVector);
+\end{prototype}
+
+We also require the ability to sort one vector based on another.  For
+example, we may want to sort both \code{x} and \code{y} by the value
+in \code{x}.  In order to facilitate this, we will have a sort
+function return a vector containing the indices for the unsorted list
+in the order appropriate for the sorted vector, sorted from the
+smallest (i.e.\ most negative) value in the first element, and the
+largest (i.e.\ most positive) value in the last element.  The output
+vector must be of type \code{psU32}.  This function is specified for
+input types \code{psS8, psU16, psF32, psF64}.
+
+\begin{prototype}
+psVector *psVectorSortIndex(psVector *outVector, const psVector *inVector);
+\end{prototype}
+
+The sorted vectors may be accessed in the following manner:
+\begin{verbatim}
+indexVector = psVectorSortIndex(NULL, x);
+for (int i = 0; i < indexVector.n; i++) {
+    doMyFunc(x[indexVector.arr.arr_U32[i]], y[indexVector[i].arr.arr_U32]);
+}
+\end{verbatim}
+
+\subsection{Vector Operations}
+
+\begin{prototype}
+psVector *psVectorSmooth(psVector *output, const psVector *input,
+                         double sigma, double Nsigma);
+\end{prototype}
+
+\code{psVectorSmooth} shall apply Gaussian smoothing to the
+\code{input} vector, with the result in the \code{output} vector
+(which shall be allocated and returned if \code{NULL}).  The Gaussian
+shall be specified by a standard deviation, \code{sigma}, and extend
+\code{Nsigma} standard deviations.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Masks}
+
+Several functions --- especially the statistical and image functions
+--- use a mask vector and a mask value to specify values in an input
+list that should be excluded from the calculations.  We define a mask
+type, which we will initially set to U8.  In the event that our masks
+exceed eight bits, we can then change the mask definition without
+major changes throughout the code.
+
+\begin{datatype}
+typedef psU8 psMaskType;
+\end{datatype}
+
+\subsection{Statistical Functions}
+
+\subsubsection{Statistical measures}
+
+We require a very general statistics function, which, given a vector
+of floating-point values, will be able to calculate the following
+population statistics:
+\begin{itemize}
+\item Minimum value;
+\item Maximum value;
+\item Sample mean;
+\item Sample median;
+\item Sample standard deviation;
+\item Sample upper and lower quartiles;
+\item Clipped mean and number of values used to calculate;
+\item Clipped standard deviation; 
+\item Robust median and number of values used to calculate;
+\item Robust standard deviation;
+\item Robust upper and lower quartiles;
+\item Fitted mean and number of values used to calculate;
+\item Fitted standard deviation;
+\end{itemize}
+
+For definitions of each of these, see the accompanying Algorithms
+Definition Document (ADD), but in general, ``sample'' refers to the
+entire vector, ``clipped'' refers to clipping the distribution,
+``robust'' refers to determining the quantities from the cumulative
+histogram, and ``fitted'' refers to fitting the data histogram with a
+Gaussian model.  Each of these must be available from a single
+function:
+
+\begin{prototype}
+bool psVectorStats(psStats *stats, const psVector *in, const psVector *errors,
+                   const psVector *mask, psMaskType maskVal);
+\end{prototype}
+%
+This function takes the input data in \code{in} (with optional
+\code{errors} in these values; and with optional masking in
+\code{mask}, so that the user may explicitly reject specific entries)
+and a \code{psStats} structure, which will be altered and returned.
+The \code{psStats} structure includes several fields which are used
+for both input and output: \code{nSubsample} (default value of
+100,000) specifies the maximum number of data points to be used for
+the statistics calculation.  If more points than this are available,
+then the input vector must be randomly sampled to provide
+\code{nSubsample} measurements.  \code{min} and \code{max} may be used
+to specify a value range for which the statistics are calculated.
+\code{binsize} specifies a choice for the robust statistics histogram
+bin size.  If these are to be used, the user must set the
+corresponding \code{options} bits \code{PS_STAT_USE_RANGE} or
+\code{PS_STAT_USE_BINSIZE}.  \code{clipSigma} specifies the number of
+standard deviations for which data should be clipped.  \code{clipIter}
+specifies the number of iterations which should be used for clipping.
+The defaults for these two numbers is both 3.  Since the sample
+statistics scale like $N\log N$, for large numbers of input data
+points, it is faster to use the robust statistics.  Default input
+field values must be set by the \code{psStats} constructor.  The input
+vector may be of type \code{psS8}, \code{psU16}, \code{psF32},
+\code{psF64}.  The \code{errors} must be of the same type as the
+\code{in} vector.  If \code{errors} is not \code{NULL}, the
+calculation of certain statistics are modified: The sample mean is
+calculated using the formula for the weighted mean; the standard
+deviation is modified as specified in the ADD; the clipping used for
+clipped statistics are modified according to the ADD; the robust
+median and quartiles are modified as specified in the ADD.  The mask
+must be of type \code{psMaskType}.
+
+The \code{psStats} structure is defined with entries for each of the
+desired statistical quantities:
+
+\begin{datatype}
+typedef struct {
+    double sampleMean;                  ///< formal mean of sample
+    double sampleMedian;                ///< formal median of sample
+    double sampleStdev;                 ///< standard deviation of sample
+    double sampleUQ;                    ///< upper quartile of sample
+    double sampleLQ;                    ///< lower quartile of sample
+    double robustMedian;                ///< robust median of data
+    double robustStdev;                 ///< robust standard deviation of data
+    double robustUQ;                    ///< robust upper quartile
+    double robustLQ;                    ///< robust lower quartile
+    long   robustN50;                   ///< Number of points UQ-LQ
+    double fittedMean;                  ///< robust mean of data
+    double fittedStdev;                 ///< robust standard deviation of data
+    long   fittedNfit;                  ///< Number of points in Gauss. fit
+    double clippedMean;                 ///< Nsigma clipped mean
+    double clippedStdev;                ///< standard deviation after clipping
+    long   clippedNvalues;              ///< number of data points used for clipped mean
+    double clipSigma;                   ///< Nsigma used for clipping; user input
+    int    clipIter;                    ///< Number of clipping iterations; user input
+    double min;                         ///< minimum data value in data; input range
+    double max;                         ///< maximum data value in data; input range
+    double binsize;                     ///< binsize for robust fit (input/output)
+    long   nSubsample;                  ///< maximum number of measuremenst (input)
+    psStatsOptions options;             ///< bitmask of desired values
+    psStatsOptions results;             ///< bitmask of calculated values
+} psStats;
+\end{datatype}
+where \code{psStatsOptions} is defined with entries to turn on the
+calculation of each of the statistics:
+
+\begin{datatype}
+typedef enum {
+    PS_STAT_SAMPLE_MEAN           = 0x000001,
+    PS_STAT_SAMPLE_MEDIAN         = 0x000002,
+    PS_STAT_SAMPLE_STDEV          = 0x000004,
+    PS_STAT_SAMPLE_QUARTILE       = 0x000008, 
+    PS_STAT_ROBUST_MEDIAN         = 0x000010,
+    PS_STAT_ROBUST_STDEV          = 0x000020,
+    PS_STAT_ROBUST_QUARTILE       = 0x000040, 
+    PS_STAT_FITTED_MEAN           = 0x000080,
+    PS_STAT_FITTED_STDEV          = 0x000100,
+    PS_STAT_CLIPPED_MEAN          = 0x000200,
+    PS_STAT_CLIPPED_STDEV         = 0x000400,
+    PS_STAT_MAX                   = 0x000800,     
+    PS_STAT_MIN                   = 0x001000,
+    PS_STAT_USE_RANGE             = 0x002000,
+    PS_STAT_USE_BINSIZE           = 0x004000
+} psStatsOptions;                         
+\end{datatype}
+
+A constructor is also required:
+%
+\begin{prototype}
+psStats *psStatsAlloc(psStatsOptions options);
+\end{prototype}
+
+It is convenient for the (end-)user to refer to the statistics by strings,
+so we specify functions to convert between strings and \code{psStats}:
+\begin{prototype}
+psStatsOptions psStatsOptionFromString(const char *string);
+psString psStatsOptionToString(psStatsOptions option);
+psStats *psStatsFromString(const char *string);
+psString psStatsToString(const psStats *stats);
+\end{prototype}
+\code{psStatsOptionFromString} shall parse the \code{string} for a
+single statistics option.  The options shall be specified by their
+\code{psStatsOptions} enum name, without the leading \code{PS_STAT_}.
+In addition, \code{SAMPLE_} may be excluded for the user's
+convenience, and \code{ROBUST} shall refer to a \code{ROBUST_MEDIAN},
+\code{FITTED} to a \code{FITTED_MEAN}, and \code{CLIPPED} to a
+\code{CLIPPED_MEAN}.  \code{psStatsOptionToString} shall translate in
+the reverse direction, returning the appropriate string given the
+\code{option} (which may be a blend of options).
+\code{psStatsFromString} shall parse the input \code{string} for
+statistics options, where multiple options may be included in the same
+string, separated by spaces, commas or semi-colons.
+\code{psStatsToString} shall translate in the reverse direction.
+
+Since the statistics functions are often used with only a single
+statistic desired (e.g., in image combination, where the output pixel
+is a statistic value), we provide functions to assist with this:
+\begin{prototype}
+psStatsOptions psStatsSingleOption(psStatsOptions option);
+double psStatsGetValue(const psStats *stats, psStatsOptions option);
+\end{prototype}
+\code{psStatsSingleOption} shall return the single statistics option
+that is set in the input \code{option}, or zero in the event that
+multiple or no options are set.  \code{psStatsGetValue} shall return
+the value of the particular statistic in the \code{stats} structure
+that corresponds to the supplied single statistics \code{option}; the
+function shall return \code{NAN} in the event of an error.
+
+\begin{prototype}
+long psVectorCountPixelMask (psVector *mask, psMaskType value);
+\end{prototype}
+This function returns the number of pixels in the \code{vector} which
+satisfy any of the mask bits.  An error (eg, invalid vector) results
+in a return value of -1.  The vector must be of the same type as
+psMaskValue.
+
+\subsubsection{Histograms}
+\label{sec:histograms}
+
+We also require to be able to generate histograms, given a list of
+upper and lower bounds for each of the bins.  We define the following
+data structure to represent a histogram:
+\begin{datatype}
+typedef struct {
+    const psVector *bounds;             ///< Bounds for the bins
+    psVector *nums;                     ///< Number in each of the bins
+    int minNum;                         ///< Number below minimum
+    int maxNum;                         ///< Number above maximum
+    bool uniform;                       ///< Is it a uniform distribution?
+} psHistogram;
+\end{datatype}
+In this structure, the vector \code{bounds} specifies the boundaries
+of the histogram bins, and must of type \code{psF32}, while
+\code{nums} specifies the number of entries in the bin, and must be of
+type \code{psF32} in order to accomodate errors.  The value of
+\code{bounds.n} must therefore be 1 greater than than \code{nums.n}.
+The two values \code{minNum} and \code{maxNum} are the number of data
+values which fell below the lower limit bound or above the upper limit
+bound, respectively.
+
+The constructors and destructor follow.  We specify two constructors,
+so that the bounds of the bins may either be specified explicitly, or
+implicitly through simply specifying an upper and lower limit along
+with the size of the bins.
+
+\begin{prototype}
+psHistogram *psHistogramAlloc(float lower, float upper, int n);
+\end{prototype}
+where \code{lower} specifies the lower bound of the histogram range,
+\code{upper} specified the upper bound of the histogram range, and
+\code{n} is the number of bins desired across the range.  This
+constructor sets the value of \code{uniform} to be true.
+
+A histogram with a more flexible bin set may be constructed with the
+following constructor:
+\begin{prototype}
+psHistogram *psHistogramAllocGeneric(const psVector *bounds);
+\end{prototype}
+where the \code{psVector *bounds} (of type \code{psF32}) is defined by
+the user to specify the boundaries of the histogram bins. This
+constructor sets the value of \code{uniform} to be false.
+
+The following function populates the histogram bins from the specified
+vector (\code{values}), and optionally the \code{errors} in the input
+values.  It alters the histogram \code{out} structure.
+\begin{prototype}
+bool psVectorHistogram(psHistogram *out, const psVector *values,
+                               const psVector *errors, const psVector *mask,
+                               psMaskType maskVal);
+\end{prototype}
+The \code{values} vector may be of types \code{psU8, psU16, psF32,
+psF64}, with the \code{errors} vector of the corresponding type.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Analytical functions}
+
+\subsubsection{Polynomials}
+
+PSLib provides APIs to represent and interact with polynomials in up
+to four dimensions, with both floating-point and double-precision
+numbers.  In Pan-STARRS processing, the astrometry requirements push
+the need for at least four dimensions ($x$,$y$, color and magnitude)
+and double-precision (for milli-arcsec precision) versions.  We must
+also be able to calculate the errors in the fit coefficients, as well
+as be able to turn on and off each coefficient.
+
+In addition to general polynomials ($\sum_{i=0}^n a_i x^i$), we also
+use Chebyshev polynomials ($\sum_{i=0}^n a_i T_i(x)$), which have
+properties which are useful in the modeling of data over a defined
+domain.  Note that Chebyshev polynomials should only have inputs in
+the range $-1 \le x \le +1$ (because they are bounded over this
+range), but we will not enforce this.
+
+This leads us to define the following polynomial types:
+
+\begin{datatype}
+/** One-dimensional polynomial */
+typedef struct {
+    psPolynomialType type;              ///< Polynomial type
+    unsigned int nX;                    ///< Order Number
+    psF64 *coeff;                       ///< Coefficients
+    psF64 *coeffErr;                    ///< Error in coefficients
+    psMaskType *mask;                   ///< Coefficient mask
+} psPolynomial1D;
+\end{datatype}
+
+\begin{datatype}
+/** Two-dimensional polynomial */
+typedef struct {
+    psPolynomialType type;              ///< Polynomial type
+    unsigned int nX;                    ///< Order Number in X dimension
+    unsigned int nY;                    ///< Order Number in Y dimension
+    psF64 **coeff;                      ///< Coefficients
+    psF64 **coeffErr;                   ///< Error in coefficients
+    psMaskType **mask;                  ///< Coefficients mask
+} psPolynomial2D;
+\end{datatype}
+
+etc., up to four dimensions.  \code{psPolynomialType} is an
+enumerated type specifying the type of the polynomial: ordinary
+or Chebyshev:
+
+\begin{datatype}
+typedef enum {
+    PS_POLYNOMIAL_ORD,                  ///< Ordinary polynomial
+    PS_POLYNOMIAL_CHEB                  ///< Chebyshev polynomial
+} psPolynomialType;
+\end{datatype}
+
+The constructors are:
+\begin{prototype}
+psPolynomial1D *psPolynomial1DAlloc(psPolynomialType type, unsigned int nX);
+psPolynomial2D *psPolynomial2DAlloc psPolynomialType type, unsigned int nX,
+                                    unsigned int nY);
+psPolynomial3D *psPolynomial3DAlloc(psPolynomialType type, unsigned int nX,
+                                    unsigned int nY, unsigned int nZ);
+psPolynomial4D *psPolynomial4DAlloc(psPolynomialType type, unsigned int nX,
+                                    unsigned int nY, unsigned int nZ,
+                                    unsigned int nT);
+\end{prototype}
+where \code{nX}, \code{nY}, etc specify the polynomial order in the
+given dimension.  The coefficients, errors and masks are set initially
+to zero.
+
+To evaluate the polynomials at specific coordinates, we define:
+\begin{prototype}
+psF64 psPolynomial1DEval(const psPolynomial1D *poly, psF64 x);
+psF64 psPolynomial2DEval(const psPolynomial2D *poly, psF64 x, psF64 y);                
+psF64 psPolynomial3DEval(const psPolynomial3D *poly, psF64 x, psF64 y, psF64 z);                 
+psF64 psPolynomial4DEval(const psPolynomial4D *poly, psF64 x, psF64 y, psF64 z, psF64 t);
+\end{prototype}
+
+In the event that several evaluations are required, we also define:
+\begin{prototype}
+psVector *psPolynomial1DEvalVector(const psPolynomial1D *poly, const psVector *x);
+psVector *psPolynomial2DEvalVector(const psPolynomial2D *poly, const psVector *x, 
+                                   const psVector *y);
+psVector *psPolynomial3DEvalVector(const psPolynomial3D *poly, const psVector *x, 
+                                   const psVector *y, const psVector *z);
+psVector *psPolynomial4DEvalVector(const psPolynomial4D *poly, const psVector *x, 
+                                   const psVector *y, const psVector *z, 
+                                   const psVector *t);
+\end{prototype}
+The function shall accept input vectors of any type, converting to
+\code{psF64} as needed.  In the event that the \code{x} and \code{y}
+vectors are of differing sizes, the function shall generate a warning,
+truncate the longer vector to the size of the shorter, and continuing.
+The precision of the output \code{psVector} shall be \code{psF64}.  In
+evaluation, those coefficients that have the corresponding \code{mask}
+element non-zero shall not be evaluated.
+
+\subsubsubsection{Conversion to Metadata}
+
+In order to facilitate storing polynomials in metadata configuration
+files, we supply functions to convert between polynomials and
+metadata containers.
+
+\begin{prototype}
+psPolynomial1D *psPolynomial1DfromMetadata(const psMetadata *folder);
+psPolynomial2D *psPolynomial2DfromMetadata(const psMetadata *folder);
+psPolynomial3D *psPolynomial3DfromMetadata(const psMetadata *folder);
+psPolynomial4D *psPolynomial4DfromMetadata(const psMetadata *folder);
+bool psPolynomial1DtoMetadata(psMetadata *md, const psPolynomial1D *poly,
+                              const char *format, ...);
+bool psPolynomial2DtoMetadata(psMetadata *md, const psPolynomial2D *poly,
+                              const char *format, ...);
+bool psPolynomial3DtoMetadata(psMetadata *md, const psPolynomial3D *poly,
+                              const char *format, ...);
+bool psPolynomial4DtoMetadata(psMetadata *md, const psPolynomial4D *poly,
+                              const char *format, ...);
+\end{prototype}
+
+The \code{psPolynomial?DfromMetadata} functions shall create a
+\code{psMetadata} folder with \code{psPolynomial?D} information.  The
+first two elements of the metadata folder shall specify the order of
+the x and y variables, \code{NORDER_X}, \code{NORDER_Y}, etc.  The
+following elements shall be the values of the coefficients and the
+coefficient errors (i.e., \code{VAL_X00_Y00}, \code{ERR_X00_Y00},
+etc.).  The input polynomial must be of ordinary type and have a valid
+name format.  \code{false} is returned if any inputs are \code{NULL}.
+If a particular mask element is non-zero, that polynomial coefficient
+(and error) are skipped.
+
+The \code{psPolynomial?DtoMetadata} functions shall parse a psMetadata
+container with \code{psPolynomial?D} information.  The first three
+elements of the metadata folder specify the order of the x, y, \& z
+variables.  (i.e., \code{NORDER_X}, \code{NORDER_Y}, etc.).  The
+following elements are the values of the coefficients and the
+coefficient errors.  (i.e., \code{VAL_X00_Y00}, \code{ERR_X00_Y00},
+etc.).  If the orders or any coefficients are missing or have
+incorrect syntax, \code{NULL} is returned.
+
+\subsubsection{Splines}
+
+A spline is a popular choice for fitting 1D data, such as overscans,
+but we neglected to define them for PSLib.  We now define
+one-dimensional cubic splines, \code{psSpline1D}, which shall be
+incorporated into PSLib:
+
+\begin{datatype}
+typedef struct {
+    unsigned int n;                     ///< Number of spline pieces
+    psPolynomial1D **spline;            ///< Array of n pointers to splines
+    psVector *knots;                    ///< The boundaries between each spline piece.  Size is n+1.
+    psF32 *p_psDeriv2;                  ///< Private 
+} psSpline1D;
+\end{datatype}
+
+The \code{psSpline1D} structure consists of an array of \code{n}
+polynomials, which are the spline pieces.  Note that this means that
+the spline pieces may, in general, be of any order.  \textbf{For the
+present, we shall restrict the order of the polynomials to either 1
+(linear) or 3 (cubic).}  All the spline pieces shall have the same
+order polynomial (the type of polynomial is left to the
+implementation).  The \code{knots} member specifies the boundaries
+between each spline piece (including the two ends).  The \code{knots}
+vector may be of type U32 or F32.
+
+Of course, we require the appropriate constructors and destructor:
+\begin{prototype}
+psSpline1D *psSpline1DAlloc();
+\end{prototype}
+
+\code{psSpline1DAlloc} shall allocate and return a \code{psSpline1D}.
+Since the number of spline pieces and locations of the knots depends
+on the input data, we do not set those here, but leave them to be set
+by the fitting function.
+
+\code{psSpline1DAllocGeneric} shall allocate and return a
+\code{psSpline1D}, using the \code{bounds} to define the number of
+spline pieces and the \code{knots}.  The spline pieces shall be of
+the specified \code{order}.
+
+Also, as for the polynomials, we require evaluators.  Given a
+\code{spline} and ordinate at which to evaluate, \code{x},
+\code{psSpline1DEval} shall evaluate and return the value of the
+spline at the ordinate.  If the ordinate is outside the bounds, then
+the function shall generate a warning, and extrapolate the spline to
+the ordinate and return the value.  Similarly,
+\code{psSpline1DEvalVector} shall return a vector of evaluated values
+for an input vector of ordinates.
+
+\begin{prototype}
+float psSpline1DEval(const psSpline1D *spline, float x);
+psVector *psSpline1DEvalVector(const psSpline1D *spline, const psVector *x);
+\end{prototype}
+
+\subsubsection{Gaussians}
+
+Gaussians are used extensively in any data-analysis system, in
+particular to represent a real population distribution.  We require a
+function to evaluate a Gaussian for a given coordinate.
+
+The Gaussian evaluation is provide by:
+\begin{prototype}
+float psGaussian(float x, float mean, float sigma, bool normal);
+\end{prototype}
+which evaluates a Gaussian with the given \code{mean} and \code{sigma}
+at the given coordinate \code{x}.  If \code{normal} is true, the
+Gaussian is normalized (total integral = 1), otherwise, the Gaussian
+is non-normalized (central peak value = 1).  The evaluated Gaussian
+is:
+
+\[ \frac{1}{\sqrt{2\pi\sigma^2}} exp^{-\frac{(x-mean)^2}{2\sigma^2}} \]
+
+In the case of the non-normalized Gaussian, the leading coefficient is
+dropped.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Minimization and fitting routines}
+
+We require a general minimization routine, a routine that will
+specifically minimize $\chi^2$ given a list of data with associated
+errors, and functions that will analytically determine the best
+polynomial and spline fits by minimizing $\chi^2$.
+
+We specify two minimization engines, Levenberg-Marquardt and Powell,
+since the former is relatively robust, but requires derivatives to be
+known, while the latter does not require derivatives to be known, but
+since it needs to calculate the derivatives on the fly, is more
+computationally intensive.  
+
+We define the structure \code{psMinimization} to carry in user-defined
+limits on the minimization process, and to carry out information about
+the minimization analysis.  The maximum number of iterations is
+specified by \code{maxIter}, while the maximum tolerance for
+convergence is \code{tol}.  The output information carried by the
+structure consists of the value of the function at the minimum
+(\code{value}), the number of iterations performed (\code{iter}) and
+last change in tolerance before returning (\code{lastDelta}).
+
+\begin{datatype}
+typedef struct {
+    const int maxIter;                  ///< Maximum number of iterations
+    const float tol;                    ///< Tolerance to reach
+    float value;                        ///< Value after minimization
+    int iter;                           ///< Actual number of iterations performed
+    float lastDelta;                    ///< Last change before quitting
+} psMinimization;
+\end{datatype}
+
+We define the \code{psMinConstrain} structure to define values which
+constrain the allowed parameter values.  The \code{paramMask} vector
+defines the free (0) or frozen (not 0) parameters.  The
+\code{paramMin} defines the minimum allowed value for each parameter,
+while \code{paramMax} defines the maximum allowed value for each
+parameter.  During the analysis, parameters which would trend beyond
+these limits saturate to the limit.  The \code{paramDelta} specifies
+the maximum allowed absolute value of the swing of a given parameter.
+If the delta for a specific parameter would be larger than this, the
+swing is saturated to the limit (with the correct sign).  Any of these
+parameter vectors may be set to \code{NULL}, in which case the concept
+is ignored in the analysis.
+
+\begin{datatype}
+typedef struct {
+    psVector *paramMask;                ///< valid / invalid parameters
+    psVector *paramMax;                 ///< max allowed parameters
+    psVector *paramMin;                 ///< min allowed parameters
+    psVector *paramDelta;               ///< max allowed param swing
+} psMinConstrain;
+\end{datatype}
+
+The corresponding allocators are:
+\begin{prototype}
+psMinimization *psMinimizationAlloc(int maxIter, float tol);
+psMinConstrain *psMinConstrainAlloc();
+\end{prototype}
+and the parameter vectors are initially set to \code{NULL}.  
+
+\subsubsection{Levenberg-Marquardt}
+
+Consider a function of a collection of parameters, \code{params},
+which is evaluated at a position, \code{x}, which returns a single
+floating point value which is the value of the function given the
+parameters and coordinate vectors, along with the derivatives of the
+function with respect to each of the parameters, \code{deriv}:
+\begin{datatype}
+typedef float (*psMinimizeLMChi2Func)(psVector *deriv, const psVector *params, const psVector *x);
+\end{datatype}
+
+Then \code{psMinimizeLMChi2} shall fit the specified function,
+\code{func}, to a set of measurements, \code{x,y,yWt}, using the
+Levenberg-Marquardt method:
+
+\begin{prototype}
+bool psMinimizeLMChi2(psMinimization *min, psImage *covar, psVector *params,
+		      psMinConstrain *constrain, const psArray  *x, 
+                      const psVector *y, const psVector *yWt, 
+                      psMinimizeLMChi2Func func);
+\end{prototype}
+
+The function shall return \code{false} in the event that there was an
+error (bad input, too many iterations), and also call \code{psError}.
+It is not an error for the minimization to reach one of the parameter
+limits.  
+
+The minimization specification, \code{min}, shall be modified with the
+\code{value}, \code{iter} and \code{lastDelta} members updated with
+the values appropriate from the minimization.
+
+On calling the minimizer, the \code{params} vector shall consist of a
+``best guess'' for the parameters that minimize the model function,
+\code{func}.  On successful completion, the input parameters,
+\code{params}, shall be updated with the values that minimize the
+input function.  The function shall also update the covariance matrix,
+\code{covar}, in the event that it is non-\code{NULL}.  The function
+shall generate in error in the event that \code{covar} is
+non-\code{NULL} and is not a square matrix with size matching that of
+\code{params}.
+
+Parameters that have a corresponding \code{paramMask} entry that is
+non-zero are to be held fixed by the minimizer.  It shall be an error
+for \code{paramMask} not to be of the same dimension as \code{params}.
+
+The measurement ordinates, \code{x}, shall consist of multiple
+vectors, each of which may be passed to the model \code{func}.  If the
+measurement coordinates, \code{y}, and weights, \code{yWt}, are not of
+the same length as the ordinates array, \code{x}, then the function
+shall generate a warning, and truncate the longest of the
+array/vectors to match the length of the shortest.  The vectors
+contained within the \code{x} array, and the \code{y} and \code{yWt}
+vectors must be of type \code{psF64}.  The \code{yWt} vector may be
+\code{NULL}, in which case the errors shall be assumed to be
+identical.
+
+\code{paramMask} must be of type \code{psMaskType}, while
+\code{params} must be of type \code{psF64}.  The \code{func} function
+must be valid only for type \code{psF64}.
+
+\begin{prototype}
+bool psMinimizeGaussNewtonDelta(psVector *delta, const psVector *params,
+                                const psVector *paramMask, const psArray  *x, 
+                                const psVector *y, const psVector *yErr, 
+                                psMinimizeLMChi2Func func);
+\end{prototype}
+
+The function \code{psMinimizeGaussNewtonDelta} can be used to evaluate
+the goodness of a fit.  This function returns the distances of the
+current parameter set from the minimization parameters expected from
+Gauss-Newton minimization.  The arguments have the same meaning as for
+\code{psMinimizeLMChi2}.  The returned vector, \code{delta}, consists
+of the distances.  This vector must be pre-allocated to the
+dimenstions of \code{params}.
+
+\subsubsection{Powell}
+
+As for the LM minimizer, consider a function of a collection of
+parameters, \code{params}, and (possibly several) coordinate vectors
+(which we represent as an array of vectors), \code{coord}, and returns
+a single floating point value which is the value of the function given
+the parameters and coordinate vectors, but now the derivatives are not
+known:
+\begin{datatype}
+typedef float (*psMinimizePowellFunc)(const psVector *params, const psArray *coords);
+\end{datatype}
+
+Then \code{psMinimizePowell} shall minimize the specified function,
+\code{func}, using the Powell method:
+
+\begin{prototype}
+bool psMinimizePowell(psMinimization *min, psVector *params, const psVector *paramMask,
+                      const psArray *coords, psMinimizePowellFunc func);
+\end{prototype}
+
+The inputs and general behavior of this function is the same as for
+\code{psMinimizeLM}, except for the absence of the covariance matrix,
+\code{covar} \note{why does this not have a covar matrix?}
+
+\subsubsubsection{Minimizing $\chi^2$ with Powell}
+
+We require a front-end to the Powell minimizer to allow fitting
+general functions to data.
+
+\begin{datatype}
+typedef psVector* (*psMinimizeChi2PowellFunc)(const psVector *params, const psArray *coords);
+\end{datatype}
+
+\begin{prototype}
+bool psMinimizeChi2Powell(psMinimization *min, psVector *params,
+                          psMinConstrain *constrain, const psArray *coords,
+                          const psVector *value, const psVector *error,
+                          psMinimizeChi2PowellFunc model);
+\end{prototype}
+
+The inputs and general behavior of \code{psMinimizeChi2Powell} is
+similar as for \code{psMinimizePowell}, with the exception that
+instead of being provided the function to be minimized, it is provided
+a model to fit and must calculate the $\chi^2$ to be minimized itself.
+
+\note{why does this not have a covar matrix?}
+
+\note{unify the param names with psMinimizeLMChi2Func?}
+
+For this purpose, \code{value} shall contain measured values at the
+coordinates, and \code{error} may either be non-\code{NULL}, in which
+case it contains the errors in the measured values; otherwise the
+errors shall assumed to be unity for the purpose of fitting where the
+errors are all identical (and possibly unknown).
+
+Furthermore, the \code{model} function provided by the user shall
+return a vector of values (instead of a single value, as was the case
+for \code{psMinimizePowell}), corresponding to the model predictions
+to be compared against the measured values.  If the lengths of the
+\code{value}, \code{error} (if provided) vectors and the vector
+returned by the \code{model} function differ, then
+\code{psMinimizeChi2Powell} shall generate a warning, truncate the
+vectors to the length of the shortest and proceed (only one error
+should be generated per call).
+
+\subsubsection{Analytical fits}
+
+\begin{prototype}
+bool psVectorFitPolynomial1D(psPolynomial1D *poly, const psVector *mask, 
+                             psMaskType maskValue, const psVector *f,
+                             const psVector *fErr, const psVector *x);
+bool psVectorFitPolynomial2D(psPolynomial2D *poly, const psVector *mask, 
+                             psMaskType maskValue, const psVector *f,
+                             const psVector *fErr, const psVector *x, 
+                             const psVector *y);
+bool psVectorFitPolynomial3D(psPolynomial3D *poly, const psVector *mask, 
+                             psMaskType maskValue, const psVector *f,
+                             const psVector *fErr, const psVector *x, 
+                             const psVector *y, const psVector *z);
+bool psVectorFitPolynomial4D(psPolynomial4D *poly, const psVector *mask, 
+                             psMaskType maskValue, const psVector *f,
+                             const psVector *fErr, const psVector *x, 
+                             const psVector *y, const psVector *z, 
+                             const psVector *t);
+\end{prototype}
+These functions return the polynomial that best fits the input data.
+The provided polynomial, \code{poly}, specifies the fit order, and
+will be returnd with the best-fit coefficients.  The dependent
+variable and its error (\code{f, fErr}) are provided along with the
+appropriate number of independent variables (\code{x}, \code{y}, etc).
+In the special case of 1D fitting, the independent variable list,
+\code{x} may be \code{NULL}, in which case the vector index is used.
+The dependent variable error, \code{yErr} may be null, in which case
+the solution is determined in the assumption that all data errors are
+equal.  This function must be valid only for input data types of
+\code{psF32}, \code{psF64}. The \code{mask} and \code{maskValue}
+entries may be used to specify an input data set to ignore.  All of
+the input vectors must be the same length.  Coefficients in the
+\code{poly} that are masked shall not be fit.
+
+\begin{prototype}
+bool psVectorClipFitPolynomial1D(psPolynomial1D *poly, psStats *stats,
+                                 const psVector *mask, psMaskType maskValue,
+                                 const psVector *f, const psVector *fErr,
+                                 const psVector *x);
+bool psVectorClipFitPolynomial2D(psPolynomial2D *poly, psStats *stats,
+                                 const psVector *mask, psMaskType maskValue,
+                                 const psVector *f, const psVector *fErr,
+                                 const psVector *x, const psVector *y);
+bool psVectorClipFitPolynomial3D(psPolynomial3D *poly, 
+                                 psStats *stats, const psVector *mask, 
+                                 psMaskType maskValue, const psVector *f,
+                                 const psVector *fErr, const psVector *x, 
+                                 const psVector *y, const psVector *z);
+bool psVectorClipFitPolynomial4D(psPolynomial4D *poly, psStats *stats,
+                                 const psVector *mask, psMaskType maskValue,
+                                 const psVector *f, const psVector *fErr,
+                                 const psVector *x, const psVector *y, 
+                                 const psVector *z, const psVector *t);
+\end{prototype}
+These functions replicate the capability of the vector fitting
+functions, but also include an iterative clipping stage.  The clipping
+parameters are defined by the \code{stats} entry, to which are also
+returned the clipped statistics for the final residuals.  Thus, if N
+clipping iterations are requested, the function performs the fit,
+constructs the residuals, measures the residual scatter, and masks the
+outlier vector elements.  This cycle is repeated N times, though on
+the last iteration, no additional masking is performed.  The provided
+mask must be respected, and any additionally masked elements are also
+masked with the same mask vector, which must be provided.  The
+elements masked by this routine are given the mask value of 1 and may
+thus be distinguished from input masked elements if they use a
+different mask value.
+
+\begin{prototype}
+psSpline1D *psVectorFitSpline1D(const psVector *x, const psVector *y);
+\end{prototype}
+\code{psVectorFitSpline1D} shall return the spline that best fits the
+given combination of ordinates (\code{x}) and coordinates (\code{y}).
+As is the case for \code{psVectorFitPolynomial1D}, if \code{x} is
+\code{NULL}, then the index of \code{y} shall be used as the ordinate.
+This function must be valid only for types \code{psF32}, \code{psF64}.
+
+\subsubsection{Additional polynomial functions}
+
+\tbd{EAM to explain in more detail}
+
+\begin{prototype}
+bool psVectorChiClipFitPolynomial4D(psPolynomial4D *poly, psStats *stats,
+                                    const psVector *mask, psMaskType maskValue,
+                                    const psVector *f, const psVector *fErr,
+                                    const psVector *x, const psVector *y,
+                                    const psVector *z, const psVector *t);
+\end{prototype}
+
+\code{psVectorChiClipFitPolynomial4D} shall perform a vector clip-fit
+of a \code{poly}nomial to the input data (\code{f,fErr,x,y,z,t}),
+based on significance of deviations
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Image Operations}
+
+We require a variety of functions to manipulate these image
+structures, including creation, destruction, input, output, and
+various manipulations of the pixels.  The required functions are
+listed below, and fall into several categories.
+
+\subsubsection{Image Structure Manipulation}
+
+\begin{prototype}
+psImage *psImageSubset(psImage *image, psRegion region);
+\end{prototype}
+Define a subimage of the specified area of the given image.  This
+function must raise an error if the requested subset area lies outside
+of the parent image and return \code{NULL}.  The argument \code{image}
+is the parent image, \code{region.x0, region.y0} specify the starting
+pixel of the subraster, and \code{region.x1,region.y1} specify the
+extent of the desired subraster.  Note that the row and column of this
+``upper right-hand corner'' are \textit{NOT} included in the region.
+Note that, if the \code{image} is itself a subimage, then the
+\code{region} coordinates correspond to coordinates of the parent
+image of \code{image}.  The only exception to this is in the event
+that \code{x1} or \code{y1} are non-positive, in which case they shall
+be interpreted as being relative to the upper-bounds of \code{image}
+in that dimension.  The entire resulting subraster will be contained
+within the raster of the input image; if the requested bounds of
+region fall beyond the bounds of the input image, they should saturate
+on the input image region (in analogy with the behavior of
+\code{psRegionForImage}).  Note that the \code{refCounter} for the
+parent should be incremented.  This function must be defined for the
+following types: \code{psU8}, \code{psU16}, \code{psS8}, \code{psS16},
+\code{psF32}, \code{psF64}, \code{psC32}, \code{psC64}.
+
+\begin{prototype}
+psImage *psImageCopy(psImage *output, const psImage *input, psElemType type);
+\end{prototype}
+Create a copy of the specified image, converting the type in the
+process.  If the output target pointer is not \code{NULL}, place the
+result in the specified structure.  The output image data must be
+allocated as a single, contiguous block of memory.  The output image
+may not be the input image.  The \code{col0,row0} of the \code{input}
+image shall be preserved.  This function must be defined for the
+following types: \code{psU8}, \code{psU16}, \code{psS8}, \code{psS16},
+\code{psF32}, \code{psF64}, \code{psC32}, \code{psC64}.
+
+\begin{prototype}
+psImage *psImageTrim(psImage *image, psRegion region);
+\end{prototype}
+Trim the specified \code{image} in-place, which involves shuffling the
+pixels around in memory.  The region to be kept is defined by the
+lower-left corner, \code{region.x0,region.y0}, and the upper-right
+corner, \code{region.x1,region.y1}.  Note that the row and column of
+the ``upper right-hand corner'' are \textit{NOT} included in the
+region.  Note that, if the \code{image} is itself a subimage, then the
+\code{region} coordinates correspond to coordinates of the parent
+image of \code{image}.  However, in the event that \code{region.x1} or
+\code{region.y1} are negative, they shall be interpreted as being
+relative to the upper bounds of the input image in that dimension.
+
+The function shall generate an error if the specified region is
+outside the bounds of the input image.  Any children of the input
+image shall be freed by \code{psImageTrim} before the trim takes
+place.
+
+The following function flips the given image in the x and/or y
+direction.  Note that a request for both an x and a y flip is
+identical to a 180\degree\ rotation.
+\begin{prototype}
+psImage *psImageFlip(psImage *output, const psImage *input,
+                     bool xFlip, bool yFlip);
+\end{prototype}
+
+\subsubsection{Image Pixel Extractions}
+
+\begin{datatype}
+typedef enum {
+    PS_CUT_X_POS,                     ///< Cut in positive x direction
+    PS_CUT_X_NEG,                     ///< Cut in negative x direction
+    PS_CUT_Y_POS,                     ///< Cut in positive y direction
+    PS_CUT_Y_NEG                      ///< Cut in negative y direction
+} psImageCutDirection;
+\end{datatype}
+
+\begin{prototype}
+psVector *psImageRow(psVector *out, const psImage *input, int row);
+psVector *psImageCol(psVector *out, const psImage *input, int column);
+\end{prototype}
+
+These two functions extract a single complete \code{row} or
+\code{column} from the \code{image} and return it to the provided
+\code{vector}, allocating it if it is \code{NULL}.  If the input image
+is a subimage, the \code{row} and \code{column} arguments refer to
+parent coordinates.  These are provided as fast, simple
+implementations of data extraction.  For more sophisticated
+operations, the following two functions should be used.
+
+\begin{prototype}
+psVector *psImageSlice(psVector *out, psPixels *coords, const psImage *input,
+                       const psImage *mask, psMaskType maskVal, psRegion region,
+                       psImageCutDirection direction, const psStats *stats);
+\end{prototype}
+Extract pixels from a rectilinear region to a vector (array of
+floats).  The output vector contains either \code{region.x1-region.x0}
+or \code{region.y1-region.y0} elements, based on the value of the
+direction: e.g., if \code{direction} is \code{PS_CUT_X_POS}, there are
+\code{region.x1-region.x0} elements.  The region to be ``sliced'' is
+defined by the lower-left corner, \code{region.x0,region.y0}, and the
+upper-right corner, \code{region.x1,region.y1}.  Note that the row and
+column of the ``upper right-hand corner'' are \textit{NOT} included in
+the region.  In the event that \code{region.x1} or \code{region.y1}
+are negative, they shall be interpreted as being relative to the size
+of the input image in that dimension. If the input image is a
+subimage, the \code{region} argument refers to parent coordinates.
+
+The pixel region defined by the coordinate pair \code{start} \&
+\code{stop} is collapsed in the direction perpendicular to that
+specified by \code{direction}, and each element of the output vectors
+is derived from the statistics of the pixels at that direction
+coordinate.  The statistic used to derive the output vector value is
+specified by \code{stats}.  If \code{mask} is non-\code{NULL}, pixels
+for which the corresponding \code{mask} pixel matches \code{maskVal}
+are excluded from operations.  If \code{coords} is not \code{NULL},
+the calculated coordinates along the slice are returned in this array
+of pixels.  Only one of the statistics choices may be specified,
+otherwise the function must return an error.  This function must be
+defined for the following types: \code{psS8}, \code{psU16},
+\code{psF32}, \code{psF64}.
+
+\begin{prototype}
+psVector *psImageCut(psVector *out, psVector *cutCols, psVector *cutRows, 
+                     const psImage *input, const psImage *mask, psMaskType maskVal,
+		     psPlane *start, psPlane *stop, unsigned int nSamples, 
+                     psImageInterpolateMode mode);
+\end{prototype}
+Extract pixels along a line segment, \code{(region.x0,region.y0)} to
+\code{(region.x1,region.y1)}, on the image to a vector of the same
+data type.  The line segment is sampled \code{nSamples} times, hence
+the output vector contains \code{nSamples} elements.  If the input image is a
+subimage, the \code{region} argument refers to parent coordinates.  The
+interpolation method used to derive the output vector value at each
+sample along the line segment is specified by \code{mode}.  If the
+\code{mask} is non-\code{NULL}, then pixels for which the
+corresponding mask value is \code{maskVal} are not included in the
+interpolation.  This function must be defined for the following types:
+\code{psS8}, \code{psU16}, \code{psF32}, \code{psF64}. 
+
+\tbd{The state of psImageCut is currently uncertain; the API in the
+code does not match the above.}
+
+\begin{prototype}
+psVector *psImageRadialCut(psVector *out, const psImage *input, const psImage *mask, 
+                           psMaskType maskVal, float x, float y, const psVector *radii, 
+                           const psStats *stats);
+\end{prototype}
+Extract radial region data to a vector.  A vector is constructed where
+each vector elements is derived from the statistics of the pixels
+which land within one of a sequence of radii.  The radii are centered
+on the image pixel coordinate \code{x,y}, and are defined by the
+sequence of values in the vector \code{radii}.  If the input image is
+a subimage, the \code{x,y} arguments refer to parent coordinates.  The
+specific algorithm which must be used is described in the PSLib ADD
+(PSDC-430-006).  The statistic used to derive the output vector value
+is specified by \code{stats}.  Only one of the statistics choices may
+be specified, otherwise the function must return an error.  If
+\code{mask} is non-\code{NULL}, pixels for which the corresponding
+\code{mask} pixel matches \code{maskVal} are excluded from operations.
+This function must be defined for the following types: \code{psS8},
+\code{psU16}, \code{psF32}, \code{psF64}.
+
+\subsubsection{Image Geometry Manipulation}
+
+Several functions which manipulate the image geometry require the
+specification of the interpolation scheme to be used.  This
+information is carried by the following enum:
+\begin{datatype}
+typedef enum {
+    PS_INTERPOLATE_FLAT, 
+    PS_INTERPOLATE_BILINEAR, 
+    PS_INTERPOLATE_LANCZOS2,
+    PS_INTERPOLATE_LANCZOS3,
+    PS_INTERPOLATE_LANCZOS4,
+    PS_INTERPOLATE_BILINEAR_VARIANCE,
+    PS_INTERPOLATE_LANCZOS2_VARIANCE,
+    PS_INTERPOLATE_LANCZOS3_VARIANCE,
+    PS_INTERPOLATE_LANCZOS4_VARIANCE
+} psImageInterpolateMode;
+\end{datatype}
+
+The first five are fairly straightforward.  The last four, however,
+require some explanation.  When attempting to account for noise in a
+CCD image, it is customary to carry an additional image containing the
+variance for each pixel.  When operating on the CCD image (performing
+some transformation), we want to perform the same operation on the
+variance image, but the weights of the different input pixels
+contributing to the output pixel must be squared in order to propagate
+the noise (adding in quadrature).
+
+\begin{prototype}
+psImage *psImageRebin(psImage *out, const psImage *in, const psImage *mask, 
+                      psMaskType maskVal, int scale, const psStats *stats);
+\end{prototype}
+Rebin image to new scale.  A new image is constructed in which the
+dimensions are reduced by a factor of \code{1 / scale}.  The
+\code{scale}, always a positive number, is equal in each dimension and
+specified the number of pixels used to define a new pixel in the
+output image.  The output image is generated from all input image
+pixels.  Care must be taken on the image boundary if the image
+dimensions are not divisible by the scaling factor.  In those regions,
+the output pixel must be constructed from the available input pixels.
+Each pixel in the output image is derived from the statistics of the
+corresponding set of input image pixels based on the statistics
+specified by \code{stats}.  Only one of the statistics choices may be
+specified, otherwise the function must return an error.  If
+\code{mask} is non-\code{NULL}, pixels for which the corresponding
+\code{mask} pixel matches \code{maskVal} are excluded from operations.
+This function must be defined for the following types: \code{psS8},
+\code{psU16}, \code{psF32} and \code{psF64}.
+
+\begin{prototype}
+psImage *psImageResample(psImage *out, const psImage *in, 
+                         int scale, psImageInterpolateMode mode);
+\end{prototype}
+Resample image to new scale.  A new image is constructed in which the
+dimensions are increased by a factor of \code{scale}.  The
+\code{scale}, always a positive number, is equal in each dimension.
+The output image is generated from all input image pixels.  Each pixel
+in the output image is derived by interpolating between neighboring
+pixels using the specified interpolation method (\code{mode}).
+
+\begin{prototype}
+psImage *psImageRotate(psImage *out, const psImage *input, float angle,
+                       double complex exposed, psImageInterpolateMode mode);
+\end{prototype}
+Rotate the input image by given angle, specified in radians.  The
+output image must contain all of the pixels from the input image in
+their new frame.  Pixels in the output image which do not map to input
+pixels should be set to \code{exposed}, cast to the same type as the
+image.  The center of rotation is always the center pixel of the
+image.  The rotation is specified in the sense that a positive angle
+is an anti-clockwise rotation.  This function must be defined for the
+following types: \code{psU8}, \code{psU16}, \code{psS8}, \code{psS16},
+\code{psF32}, \code{psF64}, \code{psC32}, \code{psC64}.
+
+\begin{prototype}
+psImage *psImageShift(psImage *out, const psImage *input, float dx, float dy,
+                      double complex exposed, psImageInterpolateMode mode);
+\end{prototype}
+Shift image by an arbitrary number of pixels (\code{dx,dy}) in either
+direction.  If the shift values are fractional, the output pixel
+values should interpolate between the input pixel values.  The output
+image has the same dimensions as the input image.  Pixels which fall
+off the edge of the output image are lost.  Newly exposed pixels are
+set to the value given by \code{exposed}, cast to the same type as the
+image.  This function must be defined for the following types:
+\code{psU8}, \code{psU16}, \code{psS8}, \code{psS16}, \code{psF32},
+\code{psF64}, \code{psC32}, \code{psC64}.
+
+\begin{prototype}
+psImage *psImageRoll(psImage *out, const psImage *input, int dx, int dy);
+\end{prototype}
+Roll image by an integer number of pixels (\code{dx,dy}) in either
+direction.  The output image is the same dimensions as the input
+image.  Edge pixels wrap to the other side (no values are lost).  This
+function must be defined for the following types: \code{psU8},
+\code{psU16}, \code{psS8}, \code{psS16}, \code{psF32}, \code{psF64},
+\code{psC32}, \code{psC64}.
+
+\begin{prototype}
+psImage *psImageTransform(psImage *output, psPixels **blankPixels, const psImage *input,
+                          const psImage *inputMask, psMaskType inputMaskVal,
+                          const psPlaneTransform *outToIn, psRegion region, 
+                          const psPixels *pixels, psImageInterpolateMode mode,
+                          double exposedValue);
+\end{prototype}
+Transform the \code{input} image according the supplied
+transformation.  The size of the transformed image is defined by the
+\code{region} (size \code{region.x1 - region.x0} by \code{region.y1 -
+region.y0}, with \code{out->x0 = region.x0} and \code{out->y0 =
+region.y0}).  If the input image is itself a subimage, then the region
+refers to the parent image coordinates.
+
+If the \code{inputMask} is non-\code{NULL}, those pixels in the
+\code{inputMask} matching \code{inputMaskVal} are to be ignored in the
+transformation.  The \code{inputMask} must be of type
+\code{psMaskType}, and of the same size as the \code{input}, otherwise
+the function shall generate an error and return \code{NULL}.  The
+transformation \code{outToIn} specifies the coordinates in the input
+image of a pixel in the output image --- note that this is the reverse
+of what might be naively expected, but it is what is required in order
+to use \code{psImagePixelInterpolate}.  If the \code{pixels} array is
+non-\code{NULL}, it shall consist of \code{psPixelCoord}s, and only
+those pixels in the output image shall be transformed; otherwise, the
+entire image is generated.  The interpolation is performed using the
+specified interpolation \code{mode}.  Where a pixel in the output
+image does not correspond to a pixel in the input image (or all
+appropriate pixels in the input image are masked), the value shall be
+set to \code{exposed}, and the pixel added to the appropriate list of
+pixels, \code{blankPixels} (which shall be allocated if
+\code{blankPixels} is \code{non-NULL} and \code{*blankPixels} is
+\code{NULL}), for return to the user.  This function must be capable
+of handling the following types for the \code{input} (with
+corresponding types for the \code{output}): \code{psF32},
+\code{psF64}.
+
+\tbd{Description required for psImageUnbin}
+\begin{prototype}
+psImage *psImageUnbin(psImage *out, const psImage *in, int DX, int DY, int dx, int dy);
+\end{prototype}
+\tbd{Can ``out'' be NULL?  I'm not sure this is the best way to specify this function for a library.}
+
+
+\subsubsection{Image Statistical Functions}
+
+\begin{prototype}
+bool psImageStats(psStats *stats, const psImage *in,
+                  const psImage *mask, psMaskType maskVal);
+\end{prototype}
+Determine statistics for image (or subimage).  The statistics to be
+determined are specified by \code{stats}.  The \code{mask} allows
+pixels to be excluded if their corresponding mask pixel value matches
+the value of \code{maskVal}.  This function must be defined for the
+following types: \code{psS8}, \code{psU16}, \code{psF32},
+\code{psF64}.
+
+\begin{prototype}
+bool psImageHistogram(psHistogram *out, const psImage *in,
+                      const psImage *mask, psMaskType maskVal);
+\end{prototype}
+Construct a histogram from an image (or subimage).  The histogram to
+generate is specified by \code{psHistogram out} (see
+section~\ref{sec:histograms}).  The \code{mask} and \code{maskVal}
+entries are passed to the psLib statistics function used to calculate
+the ensemble statistics.  This function must be defined for the
+following types: \code{psS8}, \code{psU16}, \code{psF32},
+\code{psF64}.
+
+\begin{prototype}
+long psImageCountPixelMask(psImage *mask, psRegion region, psMaskType value);
+\end{prototype}
+This function returns the number of pixels in the image region which
+satisfy any of the mask bits.  An error (eg, invalid image, invalid
+region) results in a return value of -1.  The \code{region} refers to
+the pixels of the \code{mask}; if \code{mask} is a subimage, the
+region must be defined relative to the parent pixel coordinate.
+
+\begin{prototype}
+bool psImageFitPolynomial(psPolynomial2D *coeffs, const psImage *input);
+\end{prototype}
+Fit a 2-D Chebychev polynomial surface to an image.  The input
+structure \code{coeffs} contains the desired order and terms of
+interest.  This function must be defined for the
+following types: \code{psS8}, \code{psU16}, \code{psF32}, \code{psF64}.
+
+\begin{prototype}
+psImage *psImageEvalPolynomial(psImage *input, const psPolynomial2D *coeffs);
+\end{prototype}
+Evaluate a 2-D polynomial surface for the image pixels.  Given the
+input polynomial coefficients, set the image pixel values on the basis
+of the polynomial function.  This function must be defined for the
+following types: \code{psS8}, \code{psU16}, \code{psF32}, \code{psF64}.
+
+\begin{prototype}
+double complex psImagePixelInterpolate(const psImage *input, float x, float y,
+                              const psImage *mask, psMaskType maskVal,
+                              double complex unexposedValue, psImageInterpolateMode mode);
+\end{prototype}
+Perform interpolation of image pixel values to the given fractional
+coordinate \code{x,y}.  The function returns the interpolated value of
+the image at the given fractional pixel coordinates, based on the
+specified interpolation \code{mode}.  The \code{mask} allows pixels to
+be excluded if their corresponding mask pixel value matches the value
+of \code{maskVal}.  This function will likely be implemented as a
+macro for processing speed.  It may also be necessary to define a
+setup macro which pre-calculates certain values which would be reused
+in a loop.
+
+\begin{prototype}
+psStats *psImageBackground(const psImage *image, const psImage *mask,
+                           psMaskType maskValue, double fmin, double fmax,
+                           long nMax, psRandom *rng);
+\end{prototype}
+\code{psImageBackground} shall measure the median background level for
+the input \code{image} from a random subsample of pixels, with the
+median in the \code{robustMedian} of the returned \code{psStats}.  If
+\code{mask} is non-\code{NULL}, then pixels with a corresponding mask
+value with the \code{maskValue} set shall be excluded from the
+calculation.  A maximum of \code{nMax} pixels shall be selected
+randomly from the image, using the provided random number generator,
+\code{rng}.  The function shall also set the values for which the
+cumulative fractions are \code{fmin} and \code{fmax} in the
+\code{robustLQ} and \code{robustUQ} fields of the returned
+\code{psStats}, respectively.
+
+
+\subsubsection{Image Pixel Manipulations}
+
+\begin{prototype}
+int psImageClip(psImage *input, double min, double vmin, double max, double vmax);
+\end{prototype}
+Clip image values outside of range to given values.  All pixels with
+values \code{< min} are set to the value \code{vmin}. All pixels with
+values \code{> max} are set to the value \code{vmax}. Returns the
+number of clipped pixels.  This function must be defined for the
+following types: \code{psU8}, \code{psU16}, \code{psS8}, \code{psS16},
+\code{psF32}, \code{psF64}, \code{psC32}, \code{psC64}.  The arguments
+(\code{min}, \code{max}, etc) must be cast to the appropriate types to
+match the image data.  If the input parameters \code{vmin} or
+\code{vmax} are out of bounds for the image pixel type, the function
+must raise an error.  It is not an error for \code{min} or \code{max}
+to be out of range.  In the case of complex numbers, the input
+parameters \code{min} and \code{max} must be compared against the
+absolute value of the pixel values.  The values to which complex image
+data are clipped employ the provided value for the real component and
+0.0 for the imaginary component.
+
+\begin{prototype}
+int psImageClipComplexRegion(psImage *input, double complex min,
+                             double complex vmin, double complex max,
+                             double complex vmax);
+\end{prototype}
+Clip image values outside of range to given values.  All pixels with
+values \code{< min} are set to the value \code{vmin}. All pixels with
+values \code{> max} are set to the value \code{vmax}. Returns the
+number of clipped pixels.  This function must be defined for the
+following types: \code{psC32}, \code{psC64}.  The arguments
+(\code{min}, \code{max}, etc) define a rectangular region in complex
+space; data values inside this regions are unchanged while those
+outside are set to either \code{vmax} (if either their real or
+imaginary portions are greater than the corresponding values of
+\code{max}) or \code{vmin} (in all other cases).  If the input
+parameters \code{vmin} or \code{vmax} are out of bounds for the image
+pixel type, the function must raise an error.  It is not an error for
+\code{min} or \code{max} to be out of range.
+
+\begin{prototype}
+int psImageClipNaN(psImage *input, float value);
+\end{prototype}
+Clip \code{NaN} image pixels to given value.  Pixels with \code{NaN},
+\code{+Inf} or \code{-Inf} values are set to the specified value.
+Returns the number of clipped pixels.  This function must be defined
+for the following types: \code{psF32}, \code{psF64}, \code{psC32},
+\code{psC64}.  In the case of complex values, if either the real or
+imaginary part have the value \code{NaN}, then that component will be
+set to the specified value.
+
+\begin{prototype}
+int psImageOverlaySection(psImage *image, const psImage *overlay, 
+                          int x0, int y0, const char *op);
+\end{prototype}
+Overlay subregion of image with another image.  Replace the pixels in
+the \code{image} which correspond to the pixels in \code{overlay} with
+values derived from the values in \code{image} and \code{overlay}
+based on the given operator \code{op}.  Valid operators are \code{=}
+(set image value to overlay value), \code{+} (add overlay value to
+image value), \code{-} (subtract overlay from image), \code{*}
+(multiply overlay times image), \code{/} (divide image by overlay).
+This function must be defined for the following types: \code{psU8},
+\code{psU16}, \code{psS8}, \code{psS16}, \code{psF32}, \code{psF64},
+\code{psC32}, \code{psC64}.  The two input images must have the same
+datatype and the output image must be constructed with that same
+datatype.  The return value shall be the number of pixels overlaid.
+
+\subsubsection{Image Local Bicube}
+
+The following functions provide interpolations of image data values
+based on bicubic interpolation.
+
+This function fits a 2D 2nd order polynomial to the 9 pixels centered
+on the coordinate x,y.
+\begin{prototype}
+psPolynomial2D *psImageBicubeFit(const psImage *image, int x, int y);
+\end{prototype}
+
+This function detemines the min (or max) of the special 2D 2nd order
+polynomial representing the fit to 9 pixels of an image.
+\begin{prototype}
+psPlane psImageBicubeMin(const psPolynomial2D *poly);
+\end{prototype}
+
+\subsubsection{JPEG operations}
+
+The following functions and structures are used to convert an image in
+memory to a output JPEG file.  These functions allow the user to
+define a color map (from a list of predefined color maps) and to
+define the dynamic range of the translation from data values to JPEG
+color values.  
+
+The representation of a JPEG colormap is given by the following structure:
+\begin{datatype}
+typedef struct {
+    psVector *red;
+    psVector *green;
+    psVector *blue;
+} psImageJpegColormap;
+\end{datatype}
+The colormap is just a translation from a \code{psImage} data value to
+a JPEG image data value.  The colormap may be used to construct either
+single channel or multichannel images.
+
+The following function allocates, but does not specify, a JPEG colormap:
+\begin{prototype}
+psImageJpegColormap *psImageJpegColormapAlloc();
+\end{prototype}
+
+The following function sets the colormap values based on the named
+colormap.  Currently defined colormaps have the names \code{greyscale}
+(or \code{grayscale}), \code{-grayscale} (or \code{-greyscale}),
+\code{rainbow}, \code{heat}.
+\begin{prototype}
+psImageJpegColormap *psImageJpegColormapSet(psImageJpegColormap *map, const char *name);
+\end{prototype}
+
+The following function writes out the specified image as a JPEG file
+using the supplied colormap.  The output goes to the specified
+filename.  This function performs a single-channel JPEG conversion
+(the values of the single image determine the colors).
+\begin{prototype}
+bool psImageJpeg(const psImageJpegColormap *map, const psImage *image,
+                 const char *filename, float min, float max);
+\end{prototype}
+
+\subsubsection{Mask operations}
+
+\begin{prototype}
+psImage *psImageGrowMask(psImage *out, const psImage *in, psMaskType maskVal,
+                         unsigned int growSize, psMaskType growVal);
+\end{prototype}
+
+\code{psImageGrowMask} grows specified values on the input mask image,
+\code{in}, returning the result.  If \code{out} is \code{NULL}, then a
+new image of the same type and dimension as \code{in} shall be
+allocated and returned; otherwise \code{out} shall be modified.  If
+\code{out} is non-\code{NULL} and does not have the same size and type
+as \code{in}, the function shall generate an error and return
+\code{NULL}.  Pixels in the \code{in} image within \code{growSize}
+pixels (either horizontal or vertical) of a pixel which matches the
+\code{maskVal} shall have the corresponding pixel in the \code{out}
+image set to the \code{growValue}.
+
+\begin{prototype}
+psImage *psPixelsToMask(psImage *out, const psPixels *pixels, psRegion region,
+                        psMaskType maskVal);
+psPixels *psPixelsFromMask(psPixels *out, const psImage *mask, psMaskType maskVal);
+\end{prototype}
+
+\code{psPixelsToMask} shall return an image of type \code{psMaskType}
+with the \code{pixels} lying within the specified \code{region} set to
+the \code{maskVal}.  The \code{out} image shall be modified if
+supplied, or allocated and returned if \code{NULL}.  The size of the
+output image shall be \code{region.x1 - region.x0} by \code{region.y1
+- region.y0}, with \code{out->x0 = region.x0} and \code{out->y0 =
+region.y0}.  In the event that either of \code{pixels} or
+\code{region} are \code{NULL}, the function shall generate an error
+and return \code{NULL}.  If \code{out} is not supplied, then the
+constructed image is not a subimage.  If \code{out} is supplied and is
+a subimage, the pixels refer to the parent image coordinates.
+
+\code{psMaskToPixels} shall return a \code{psPixels} containing the
+coordinates in the \code{mask} that match the \code{maskVal}.  The
+\code{out} pixel list shall be modified if supplied, or allocated and
+returned if \code{NULL}.  In the event that \code{mask} is
+\code{NULL}, the function shall generate an error and return
+\code{NULL}.  If \code{mask} is a subimage, the pixels refer to the
+parent image coordinates.
+
+\begin{prototype}
+void psImageMaskRegion(psImage *image, psRegion region, const char *op, psMaskType maskValue);
+void psImageKeepRegion(psImage *image, psRegion region, const char *op, psMaskType maskValue);
+\end{prototype}
+
+These two complementary functions set bit specified bits
+(\code{maskValue}) in the mask \code{image} interior to or exterior to
+the specified \code{region}.  The first function,
+\code{psImageMaskRegion}, sets the bits inside the region, ignoring
+pixels outside.  The second, \code{psImageKeepRegion}, sets the bits
+outside the region, ignoring pixels inside.  The pixel values are set
+by combining the existing pixel value and the given \code{maskValue}
+with a logical operation.  The allowed operations are \code{=},
+\code{AND}, \code{OR} and \code{XOR}. If \code{image} is a subimage,
+the region refers to the parent image coordinates.
+
+\begin{prototype}
+void psImageMaskCircle(psImage *image, double x, double y, double radius, const char *op,
+                       psMaskType maskValue);
+void psImageKeepCircle(psImage *image, double x, double y, double radius, const char *op,
+                       psMaskType maskValue);
+\end{prototype}
+
+These two complementary functions set bit specified bits
+(\code{maskValue}) in the mask \code{image} interior to or exterior to
+the specified circle, defined by the center coordinates \code{x,y} and
+a \code{radius}.  The first function, \code{psImageMaskCircle}, sets
+the bits inside the circle, ignoring pixels outside.  The second,
+\code{psImageKeepCircle}, sets the bits outside the circle, ignoring
+pixels inside.  The pixel values are set by combining the existing
+pixel value and the given \code{maskValue} with a logical operation.
+The allowed operations are \code{=}, \code{AND}, \code{OR} and
+\code{XOR}.  If \code{image} is a subimage, the coordinates refers to
+the parent image coordinates.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Vector and Image Arithmetic}
+\label{sec:arithmetic}
+
+We will need to be able to perform various operations on vectors and
+images, e.g.\ dividing one image by another, subtracting a vector from
+an image, etc.  Both binary operations and unary operations are
+required.  To avoid the burden of memorizing a plethora of APIs, we
+specify two generic APIs for the binary and unary operations.
+
+\begin{prototype}
+psMathType *psBinaryOp(psPtr out, const psPtr in1, const char *op, const psPtr in2);
+psMathType *psUnaryOp(psPtr out, const psPtr in, const char *op);
+\end{prototype}
+These functions determine the type of the operands on the basis of
+their \code{psMathType} elements, which always are the first elements.
+Note that these functions return a pointer to the appropriate type for
+the operation.  Since the result may be cast to \code{psMathType}, the
+resulting type may be determined by examining the return value.  It is
+expected that the implementation of these functions will employ
+pre-processor macros to perform the onerous task of creating the
+loops.  An attempt to perform an arithmetic operation on an object of
+dimension \code{PS_DIMEN_OTHER} should produce an error.  Operations
+between data structures with different types (e.g., \code{psS32} and
+\code{psF32}) are not allowed and must raise an error (it is the
+responsibility of calling functions to perform type conversions).
+Operations between data structures with incompatible sizes are not
+allowed.  However, operations between data elements of different rank
+(scalar, vector, image) are allowed, and defined below.  These
+functions are valid for all data types \code{psU8}, \code{psU16},
+\code{psS8}, \code{psS16}, \code{psF32}, \code{psF64}, \code{psC32},
+\code{psC64}.
+
+Binary operations between an image and a vector have a potential
+ambiguity --- do the vector elements correspond to the rows or the
+columns?  For this reason, we define two vector types: a ``vector''
+(\code{PS_DIMEN_VECTOR}), and a ``transposed vector''
+(\code{PS_DIMEN_TRANSV}).  We specify that a ``vector'', when involved
+in binary operations on an image, acts on all rows of the image, while
+a ``transposed vector'' in the same context acts on all columns.
+Vectors, when created, will be created as ``vectors'', but may be
+converted to ``transposed vectors'' by setting
+\code{vector->type.dimen = PS_DIMEN_TRANSV}.
+
+It is further desirable to allow scalar values to be used within these
+functions.  These functions may take a pointer to type
+\code{psScalar}, which is freed by the function. This allows one to
+write the following lines to take the sine of the square of all pixels
+in an image:
+\begin{verbatim}
+psImage *A, *B;
+A = someCoolImage(); // Initialise A
+
+B = (psImage*)psBinaryOp (NULL, A, "^", psScalarAlloc(2, PS_TYPE_S16));
+// Note, have to cast the output to a psImage for proper compilation
+psUnaryOp(B, B, "sin");
+\end{verbatim}
+
+Note that the \code{psUnaryOp} is performed on \code{B} in-place.
+
+The list of required operators for \code{psBinaryOp} are:
+\begin{itemize}
+\item Addition ($+$)
+\item Subtraction ($-$)
+\item Multiplication ($*$)
+\item Division ($/$)
+\item Power (\^)
+\item Minimum (min)
+\item Maximum (max)
+\item Logical or ($|$; integer type only)
+\item Logical and (\&; integer type only)
+\end{itemize}
+
+The list of required operators for \code{psUnaryOp} are:
+\begin{itemize}
+\item Absolute value (abs)
+\item Exponent (exp)
+\item Natural Log (ln)
+\item Power of 10 (ten)
+\item Log (log)
+\item Sine (sin and dsin)
+\item Cosine (cos and dcos)
+\item Tangent (tan and dtan)
+\item Arcsine (asin and dasin)
+\item Arccosine (acos and dacos)
+\item Arctan (atan and datan)
+\end{itemize}
+The trigonometric operators prefixed with ``d'' refer to the standard
+trigonometric operators acting on input that is in decimal degrees.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+\subsection{Matrix operations and linear algebra}
+
+In addition to the ability to perform image arithmetic
+(\S\ref{sec:arithmetic}), we also require the ability to perform basic
+linear algebra on matrices, in order to solve equations.  We use
+\code{psImage} as a matrix, since it has the correct form.  We define
+the following basic matrix operations:
+\begin{itemize}
+\item $LU$ Decompose a matrix, and solve for $x$ in $Ax = b$;
+\item Invert a matrix;
+\item Calculate a matrix determinant;
+\item Perform matrix addition, subtraction and multiplication;
+\item Transpose a matrix;
+\item Get eigenvectors for a matrix; and
+\item Convert a matrix to a vector.
+\end{itemize}
+The corresponding APIs follow.
+
+In the event of an error, the matrix functions shall return \code{NULL}.
+
+\begin{prototype}
+psImage *psMatrixLUD(psImage *out, psVector **perm, const psImage *in);
+psVector *psMatrixLUSolve(psVector *out, const psImage *LU, const psVector *RHS,
+                          const psVector *perm);
+\end{prototype}
+The above functions decompose a matrix, \code{in}, into its $LU$
+representation (\code{psMatrixLUD}, which returns the decomposed
+matrix), and uses the decomposed matrix to solve for $x$ in the
+equation $Ax = b$.  In this case, the $LU$ decomposed matrix $A$ is
+specified as \code{LU}, and $b$ is \code{RHS}; the solution vector for
+$x$ is returned.  These functions are specified for data types
+\code{psF32, psF64}.  The output and input vectors and images must all
+have the same data type.
+
+The GSL routines require the use of a permutation vector, \code{perm}.
+This vector shall be created by \code{psMatrixLUD}, and the user need
+only pass this to \code{psMatrixLUSolve} before destroying it in the
+standard manner.  In order to avoid memory leaks, \code{perm} must be
+\code{NULL} on calling \code{psMatrixLUD}.
+
+\begin{prototype}
+bool psMatrixGJSolve(psImage *A, psVector *b);
+\end{prototype}
+This function solves the matrix equation $Ax = b$ for $x$ using the
+Gauss-Jordan method.  On completion, the input matrix has been
+inverted, and the input vector contains the solution, $x$.  The
+function is specified for data type \code{F64} only.
+
+\begin{prototype}
+psImage *psMatrixInvert(psImage *out, const psImage *in, float *determinant);
+\end{prototype}
+\code{psMatrixInvert} returns the inverse of the specified matrix
+(\code{in}), along with the \code{determinant}, if the \code{float}
+pointer is non-NULL.  The input and output images must have the same
+type.  This function is specified for data types \code{psF32, psF64}.
+
+The matrix determinant may be calculated using
+\code{psMatrixDeterminant}, which simply returns the determinant for
+the specified matrix, \code{in}.  This function is specified for data
+types \code{psF32, psF64}.
+\begin{prototype}
+float psMatrixDeterminant(const psImage *in);
+\end{prototype}
+
+Matrix multiplication is supported through \code{psMatrixMultiply}.  This function is
+specified for data types \code{psF32, psF64}.
+\begin{prototype}
+psImage *psMatrixMultiply(psImage *out, const psImage *in1, const psImage *in2);
+\end{prototype}
+
+The transpose of an input matrix, \code{in}, is returned by
+\code{psMatrixTranspose}, optionally into the provided matrix
+\code{out}.  This function is specified for data types \code{psF32,psF64}.
+\begin{prototype}
+psImage *psMatrixTranspose(psImage *out, const psImage *in);
+\end{prototype}
+
+Eigenvectors of a matrix are calculated by
+\code{psMatrixEigenvectors}.  This function is specified for data
+types \code{psF32, psF64}.  Input and output data types should match.
+\begin{prototype}
+psImage *psMatrixEigenvectors(psImage *out, const psImage *in);
+\end{prototype}
+\tbd{Should this return an array of vectors?  Specified here as
+currently implemented by MHPCC.}
+
+Finally, we specify two functions to convert between matrices and
+vectors.  This allows the use of vectors in matrix arithmetic, since a
+vector can be converted to a matrix, utilized, and the result can be
+converted back to a vector if required.  This function is specified
+for data types \code{psF32, psF64}.  Input and output data types
+should match.
+\begin{prototype}
+psVector *psMatrixToVector(psVector *outVector, const psImage *inImage);
+psImage *psVectorToMatrix(psImage *outImage, const psVector *inVector);
+\end{prototype}
+
+\subsubsection{Sparse Matrices}
+
+Very large matrices (N elements $> 1000$) can be very time consuming
+to manipulate.  A certain class of matrices, sparse matrices, are
+amenable to iterative solutions even when extremely large (100,000s of
+elements).  The following functions define structures to efficiently
+represent sparse matricies, to add elements to those matrices, and to
+perform linear algebra with them. 
+
+\begin{datatype}
+typedef struct {
+    psVector *Aij;
+    psVector *Bfj;
+    psVector *Qii;
+    psVector *Si;
+    psVector *Sj;
+    int Nelem;
+    int Nrows;
+} psSparse;
+\end{datatype}
+
+The above structure contains the following elements, describing a
+sparse matrix equation of the form $A \bar{x} = \bar{Bf}$:
+\begin{itemize}
+\item the vector \code{Aij} contains the populated elements of the matrix
+\item the vector \code{Bfj} contains the elements of the vector Bf
+\item the vector \code{Qii} contains the diagonal elements of Aij
+\item the vector \code{Si} contains the i-index values of Aij
+\item the vector \code{Sj} contains the j-index values of Aij
+\item the element \code{Nelem} defines the total number of elements in
+matrix \code{Aij}
+\item the element \code{Nrows} defines the size of the matrix \code{Aij}
+\end{itemize}
+
+The following structure defines constraints to limit the range of the
+value matrix equation solution:
+\begin{datatype}
+typedef struct {
+    double paramDelta;
+    double paramMin;
+    double paramMax;
+} psSparseConstraint;
+\end{datatype}
+
+The following function allocates a sparse matrix structure:
+\begin{prototype}
+psSparse *psSparseAlloc (int Nrows, int Nelem);
+\end{prototype}
+
+The following function adds a new matrix element.  The user should
+only add elements above the diagonal.
+\begin{prototype}
+bool psSparseMatrixElement(psSparse *sparse, int i, int j, float value);
+\end{prototype}
+
+The following function define a new sparse matrix equation vector element:
+\begin{prototype}
+void psSparseVectorElement(psSparse *sparse, int i, float value);
+\end{prototype}
+
+The following function performs the operation ``matrix times vector''
+on a sparse matrix and a vector:
+\begin{prototype}
+psVector *psSparseMatrixTimesVector(psVector *output, const psSparse *matrix,
+                                    const psVector *vector);
+\end{prototype}
+
+The following function re-sorts a sparse matrix to have all elements
+in index order rather than insertion order.  The user must call this
+function before attempting to solve, but after populating, the matrix and vector:
+\begin{prototype}
+bool psSparseResort(psSparse *sparse);
+\end{prototype}
+
+The following function iteratively solves the equation $A \bar{x} =
+\bar{Bf}$.  For the value of $\bar{x}$, a good starting guess is the vector $\bar{Bf}$
+\begin{prototype}
+psVector *psSparseSolve(psVector *output, psSparseConstraint constraint,
+                        const psSparse *sparse, int Niter);
+\end{prototype}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{(Fast) Fourier Transforms}
+
+We require the ability to calculate the (fast) Fourier transforms of
+floating-point one-dimensional vectors and two-dimensional images.
+These will be implemented through wrapping an external library.  We
+expect that we shall only want to deal with purely real vectors and
+images, rather than complex; this can save operations when performing
+the FFT (roughly a factor of 2).  We define the following APIs to
+support FFT operations on vectors:
+
+\begin{prototype}
+bool psVectorForwardFFT(psVector **real psVector **imag, const psVector *in);
+bool psVectorBackwardFFT(psVector **out, const psVector *real, const psVector *imag, int origNum);
+psVector *psVectorPowerSpectrum(psVector *out, const psVector *in);
+bool psVectorComplexMultiply(psVector **outReal, psVector **outImag,
+                             const psVector *in1Real, const psVector *in1Imag,
+			     const psVector *in2Real, const psVector *in2Imag);
+\end{prototype}
+
+We might have chosen to use the \code{complex} types that C99
+provides, but unfortunately this isn't implemented on all the systems
+that we would have desired.  Instead, we use separate vectors
+to carry the real and imaginary parts.
+
+The forward FFT (exponent $-1$) is calculated using
+\code{psVectorForwardFFT}, which takes as input the vector of interest
+(\code{in}) of type F32, returning the \code{real} and
+\code{imaginary} parts in separate vectors, which are allocated if
+required.
+
+The backward FFT (exponent $+1$) is calculated using
+\code{psVectorBackwardFFT}, which takes as input the \code{real} and
+\code{imaginary} parts, each of type F32, and returns the purely real
+\code{out} vector, which is allocated if required.  Due to the
+ambiguity in the size of the original vector when the purely real
+optimisation is made, this function also requires the number of
+elements, \code{origNum} in the original vector before FFT-ing with
+\code{psVectorForwardFFT}.
+
+Note that the FFTs are not normalised, and so it falls to the
+responsibility of the user to multiply a vector that has been forward-
+and reverse-transformed by $1/N$.
+
+\code{psVectorPowerSpectrum} calculates the power spectrum (magnitude
+of the fourier transform) from the \code{in} vector (of type F32.
+
+Finally, for convenience, the function \code{psVectorComplexMultiply}
+is provided to multiply two vectors of complex numbers which are
+specified by their real and imaginary parts, and returning the real
+and imaginary parts separately.
+
+In analogy with the vector FFT operations, we also define matching
+image operations as follows:
+\begin{prototype}
+bool psImageForwardFFT(psImage **real psImage **imag, const psImage *in);
+bool psImageBackwardFFT(psImage **out, const psImage *real, const psImage *imag, int origCols);
+psImage *psImagePowerSpectrum(psImage *out, const psImage *in);
+bool psImageComplexMultiply(psImage **outReal, psImage **outImag,
+                             const psImage *in1Real, const psImage *in1Imag,
+			     const psImage *in2Real, const psImage *in2Imag);
+\end{prototype}
+
+The only subtle difference is in \code{psImageBackwardFFT}, where the
+number of columns in the original image (before applying
+\code{psImageForwardFFT}) is required due to the ambiguity arising
+from the optimisation made for purely real data.  Note that the number
+of rows need not be provided, because the FFT-ed image is shrunk in
+the columns but not the rows.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Convolution}
+
+Convolution will be an essential operation for the IPP.  For example,
+if images on the sky are obtained in Orthogonal Transfer (OT) mode,
+then the calibration frames used to correct them must be convolved by
+a kernel derived from the list of OT shifts made during the exposure.
+Also, convolution is also required for object detection and
+PSF-matching.
+
+\subsubsection{Basic Image Smoothing}
+
+\begin{prototype}
+bool psImageSmooth(psImage *image, double sigma, double Nsigma);
+\end{prototype}
+
+The quickest way to smooth an image is to smooth by parts, using 1D
+gaussian smoothing in X and Y independently.  \code{psImageSmooth}
+applies a single parameter Gaussian (circularly symmetric) by
+smoothing first in the X and then in the Y directions with just a
+vector.  In general, for a Gaussian of dimension N, this process is 2N
+faster than for the 2D convolution defined below.  The arguments to
+this function include the image to be smoothed, the width of the
+smoothing kernel in pixels (\code{sigma}), and the size of the
+smoothing box in sigmas.  
+
+\subsubsection{Kernel definition}
+
+In order to perform a convolution, we need to define the convolution
+kernel.  We need a more general object than a \code{psImage} so that
+we can incorporate the offset from the $(0,0)$ pixel to the $(0,0)$
+value of the kernel.  It might be convenient to allow both positive
+and negative indices to convey the positive and negative shifts.  One
+might consider setting the \code{x0} and \code{y0} members of a
+\code{psImage} to the appropriate offsets, but this is not the purpose
+of these members, and doing so may affect the behavior of other
+\code{psImage} operations.  We define a \code{psKernel}:
+
+\begin{datatype}
+/** A convolution kernel */
+typedef struct {
+    psImage *image;                     ///< Kernel data, in the form of an image
+    int xMin;                           ///< Most negative indices
+    int yMin;                           ///< Most negative indices
+    int xMax;                           ///< Most positive indices
+    int yMax;                           ///< Most positive indices
+    float **kernel;                     ///< Pointer to the kernel data
+    float **p_kernelRows;               ///< Pointer to the rows of the kernel data
+} psKernel;
+\end{datatype}
+
+The kernel data is carried primarily by the \code{data} member which
+is a normal \code{psImage}.  In order to allow negative indices, we
+add two additional members.  \code{kernelRows} is an array of pointers
+to \code{float}; these pointers point into the \code{psImage} data,
+offset by the desired column offset.  \code{kernel} point into the
+\code{kernelRows}, offset by the desired row offset.  This
+construction allows the \code{kernel} member to use negative indices,
+while preserving the location of \code{psMemBlock}s relative to
+allocated memory.
+
+The maximum extent of the kernel shifts shall be defined by the
+\code{xMin}, \code{xMax}, \code{yMin} and \code{yMax} members.  Note
+that \code{xMin} and \code{yMin}, under normal circumstances, should
+be negative numbers.  That is, \code{myKernel->kernel[-3][-2]} may be
+defined if \code{yMin} and \code{xMin} are equal to or more negative
+than -3 and -2, respectively.
+
+Of course, we require the appropriate constructor:
+\begin{prototype}
+psKernel *psKernelAlloc(int xMin, int xMax, int yMin, int yMax);
+\end{prototype}
+
+\code{psKernelAlloc} shall allocate a kernel.  In the event that one
+of the minimum values is greater than the corresponding maximum value,
+the function shall generate a warning, and the offending values shall
+be exchanged.
+
+\subsubsection{Generation of a convolution kernel}
+
+Given a list of values (e.g., shifts made in the course of OT
+guiding), \code{psKernelGenerate} shall return the appropriate kernel.
+The API shall be the following:
+\begin{prototype}
+psKernel *psKernelGenerate(const psVector *tShifts, const psVector *xShifts,
+                           const psVector *yShifts, bool relative);
+\end{prototype}
+
+The vectors \code{xShifts} and \code{yShifts}, which are a list of
+shifts made at the times \code{tShifts}, are used to construct the
+appropriate kernel.  If \code{relative} is \code{true}, then each
+shift is to be interpreted relative the shift made before; if
+\code{relative} is \code{false}, then the shifts are to be interpreted
+relative to some starting point.  The elements of the vectors should
+be of an integer type; otherwise the values shall be truncated to
+integers.  The output kernel shall be normalized such that the sum
+over the kernel is unity.
+
+If the vectors are not all of the same number of elements, then the
+function shall generate an error and return \code{NULL}.
+
+\subsubsection{Convolve an image with the kernel}
+
+Two methods are available for the convolution: direct and FFT.  Given
+an input image and the convolution kernel,
+\code{psImageConvolveDirect} and \code{psImageConvolveFFT} shall
+convolve the input image, \code{in}, with the kernel, \code{kernel}
+and return the convolved image, \code{out}.  The APIs shall be the
+following:
+\begin{prototype}
+psImage *psImageConvolveDirect(const psImage *in, const psKernel *kernel);
+psImage *psImageConvolveFFT(const psImage *in, const psKernel *kernel);
+\end{prototype}
+
+\code{psImageConvolveDirect} shall perform the convolution in real
+space (appropriate for small kernels); \code{psImageConvolveFFT} shall
+perform the convolution using Fast Fourier Transforms (FFTs;
+appropriate for larger kernels).  The latter option involves padding
+the input image, copying the kernel into an image of the same size as
+the padded input image, performing an FFT on each, multiplying the
+FFTs, and performing an inverse FFT before trimming the image back to
+the original size.
+
+In the event that \code{out} is \code{NULL}, a new \code{psImage}
+shall be allocated and returned.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Random Numbers}
+
+Many applications involve random numbers, with the particular
+distribution depending upon the application.  We will wrap the GSL
+implementation.
+
+We define a \code{psRandom} type, which will allow us to carry around
+pre-computed random numbers, if required.  For the time being, we only
+specify a single random number generator (RNG) in \code{psRandomType}
+(that provided by \code{gsl_rng_taus}), but we leave this open to
+expansion in the future, depending upon user requirements.
+
+\begin{datatype}
+typedef enum {
+    PS_RANDOM_TAUS                      ///< A maximally equidistributed combined Tausworthe generator
+} psRandomType;
+
+typedef struct {
+    psRandomType type;                  ///< The type of RNG
+    gsl_rng *gsl;                       ///< The RNG itself
+} psRandom;
+\end{datatype}
+
+We require the ability to seed the random number generator (RNG) with
+a known value, so that bugs in modules which rely upon random numbers
+may be reproduced.
+
+\begin{prototype}
+psRandom *psRandomAlloc(psRandomType type, unsigned long seed);
+void psRandomReset(psRandom *rand, unsigned long seed);
+\end{prototype}
+
+\code{psRandomAlloc} shall construct a new instance of \code{psRandom}
+of the given \code{type}, and seed it with the given \code{seed}.  The
+\code{seed} is specified as an \code{unsigned long} so that the system
+clock may be used to set it.  If the \code{seed} is zero, then the RNG
+shall be seeded from \code{/dev/urandom} if it exists, or otherwise
+from the system clock, and the particular seed shall be printed using
+\code{psLogMsg} with a level of \code{PS_LOG_INFO}.
+
+\begin{prototype}
+double psRandomUniform(const psRandom *r);
+double psRandomGaussian(const psRandom *r);
+double psRandomPoisson(const psRandom *r, double mean);
+\end{prototype}
+
+\code{psRandomUniform} shall return random numbers uniformly
+distributed on $[0,1)$, using \code{gsl_rng_uniform}.
+\code{psRandomGaussian} shall return random numbers distributed on a
+Gaussian deviate, $N(0,1)$, using \code{gsl_ran_gaussian}.
+\code{psRandomPoisson} shall return random numbers distributed on a
+Poisson distribution with the given \code{mean} using
+\code{gsl_ran_poisson}.
+
+\subsection{Ellipse Shape Functions}
+
+Astronomical objects are frequently decomposed into components
+represented by a radial function modified by an elliptical contour.
+There are a few ways in which the elliptical shape information can be
+represented depending on the circumstance in which it is used.  The
+structures and functions in the section provide tools for converting
+between the elliptical representations.  We provide three structures
+representing three ways in which the elliptical shape is represented.
+Like the \code{psRegion}, these datatypes and their supporting
+functions do not use allocators.  See the ADD Section 2.10 for the
+relationships between these representations.
+
+This structure represents an ellipse by its major and minor axis
+lengths ($\sigma_a$, $\sigma_b$) and the orientation angle ($\theta$).
+\begin{datatype}
+typedef struct {
+    double major;
+    double minor;
+    double theta;
+} psEllipseAxes;
+\end{datatype}
+
+This structure represents an elliptical Gaussian by the second moments
+($M_{x,x}$, $M_{y,y}$, $M_{x,y}$) measured for that Gaussian.
+\begin{datatype}
+typedef struct {
+    double x2;
+    double y2;
+    double xy;
+} psEllipseMoments;
+\end{datatype}
+
+This structure represents a rotated ellipse by the components of the
+cartesian coordiante equation: $z = \frac{x^2}{2\sigma_x^2} +
+\frac{y^2}{2\sigma_y^2} + \sigma_{xy}xy$
+\begin{datatype}
+typedef struct {
+    double sx;
+    double sy;
+    double sxy;
+} psEllipseShape;
+\end{datatype}
+
+The following functions provide conversions between the elliptical
+shape representations:
+
+\begin{prototype}
+psEllipseAxes psEllipseMomentsToAxes(psEllipseMoments moments);
+psEllipseShape psEllipseAxesToShape(psEllipseAxes axes);
+psEllipseAxes psEllipseShapeToAxes(psEllipseShape shape);
+\end{prototype}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagebreak 
+\section{Astronomy-Related Functions}
+
+The previous sections of this document defined basic functionality
+needed by a wide range of scientific programming.  The rest of this
+document concentrates on aspects of the library which are specific to
+astronomy.  Some basic, relatively simple astronomy-specific functions
+are required which will serve as the foundation for building the
+higher level modules.  These functions are not expected to cover every
+foreseeable function, but will serve as the building blocks of more
+complicated processing functions.  
+
+We require functions covering each of the following areas:
+\begin{itemize}
+\item Dates and times
+\item Detector and sky positions
+\item Astrometry
+\item Photometry
+\item Astronomical objects
+\end{itemize}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Dates and times}
+\label{sec:timespec}
+
+\subsubsection{Overview}
+
+We require a collection of functions to manipulate time data.  These
+operations primarily consist of conversions between specific time
+formats.  Internally, PSLib handles times as a structure similar to
+the POSIX \code{struct timeval} struct which has been extended to
+track the time system being represented.
+
+\subsubsection{Initialization and Finalization}
+
+The conversions between various time standards, and the polar motion
+requires importing external data (``time tables''), the locations of
+which are specified in a configuration file.
+
+\begin{prototype}
+void psTimeInitialize(const char *timeConfig);
+\end{prototype}
+
+\code{psTimeInitialize} shall read the \code{psTime} configuration
+file (\code{timeConfig}) and set up the appropriate
+\code{psTimeTable}s and predictions.
+
+{\bf Calls to \code{psTime} functions that require the time tables
+before calling \code{psTimeInitialize} shall result in an error.}
+
+\begin{prototype}
+void psTimeFinalize(void);
+\end{prototype}
+
+\code{psTimeFinalize} shall free memory that was allocated by
+\code{psTime} functions, allowing a subsequent search for leaked
+memory (even memory marked as \code{persistent}).
+
+\subsubsection{Data Types}
+
+\begin{datatype}
+typedef enum {
+    PS_TIME_TAI,      ///< seconds since 1970-01-01T00:00:00Z (Gregorian), SI seconds
+    PS_TIME_UTC,      ///< seconds since 1970-01-01T00:00:00Z (Gregorian), SI seconds + leapseconds
+    PS_TIME_UT1,      ///< seconds since 1970-01-01T00:00:00Z (Gregorian), variable length seconds
+    PS_TIME_TT,       ///< seconds since 1970-01-01T00:00:00Z (Gregorian), SI seconds
+} psTimeType;
+
+typedef enum {
+    PS_IERS_A,                      ///< IERS Bulletin A
+    PS_IERS_B,                      ///< IERS Bulletin B
+} psTimeBulletin;
+
+typedef struct {
+    psS64            sec;           ///< seconds, negative values represent dates before 1970
+    psU32            nsec;          ///< nanoseconds
+    bool             leapsecond;    ///< if time falls on a UTC leapsecond
+    psTimeType       type;          ///< type of time
+} psTime;
+\end{datatype}
+
+\subsubsection{Constructors}
+
+To allocate a new, initialized to zero, \code{psTime}:
+
+\begin{prototype}
+psTime *psTimeAlloc(psTimeType type);
+\end{prototype}
+
+To allocate a new \code{psTime} set to the current time (in the given system):
+
+\begin{prototype}
+psTime *psTimeGetNow(psTimeType type);
+\end{prototype}
+
+\subsubsection{Time Conversion}
+
+Converting between the \code{psTime} time systems is done with:
+
+\begin{prototype}
+psTime *psTimeConvert(psTime *time, psTimeType type);
+\end{prototype}
+
+This function may be used to convert between the various \code{psTimeType} time
+representations.  The \code{time} is modified and returned.  Conversion between
+all of the \emph{SI} length second systems should be implemented by first
+converting to TAI and then to the destination system.  UT1 is a special case
+for conversion as it uses variable length seconds.  Conversation to UT1, via
+TAI, is allowed but conversion \emph{from} UT1 is currently forbidden.
+
+The following function converts to Local Mean Sidereal Time.  It is
+necessary to provide the local longitude (specified in radians,
+positive East of Greenwich) as well:
+
+\begin{prototype}
+double psTimeToLMST(psTime *time, double longitude);
+\end{prototype}
+
+The function may accept any of the \code{psTimeType} types with
+\emph{SI} length seconds.  The \code{time} is modified (rationlized to
+TAI units) and returned.  Note that this function requires the value
+$UT1-UTC$ (see \code{psTimeGetUT1Delta()}) and should use the UT1
+values interpolated from IERS bulletin B.
+
+The following utility function encapsulates the PSLib mechanism to
+extract the value of $UT1-UTC$ from the IERS Time Tables:
+
+\begin{prototype}
+double psTimeGetUT1Delta(const psTime *time, psTimeBulletin bulletin);
+\end{prototype}
+
+The following function provides tidal corrections to UT1-UTC, using
+the Ray model of Simon et al (REF).
+\begin{prototype}
+psTime *psTime_TideUT1Corr(const psTime *time);
+\end{prototype}
+
+Leap seconds are added to UTC in order to keep it within $0.9s$ of UT1
+(which is defined relative to the Earth's rotation, and hence is
+useful for astronomical purposes).  The following function calculates
+the absolute number of leap seconds different between two times.
+
+\begin{prototype}
+long psTimeLeapSecondDelta(const psTime *time1, const psTime *time2);
+\end{prototype}
+
+The following function accepts \code{PS_TIME_UTC} objects and
+determines if the time is potentially a leapsecond, returning
+\code{TRUE} if so.
+
+\begin{prototype}
+bool psTimeIsLeapSecond(const psTime *utc);
+\end{prototype}
+
+\subsubsection{External Date and Time Formats}
+
+A collection of functions convert from the \code{psTime} types to various
+external formats.  The ISO8601 format\footnote{ISO8601:2000(E) -
+http://www.pvv.ntnu.no/~nsaa/8601v2000.pdf} we will be using is
+"YYYY-MM-DDThh:mm:ss.sZ".  \emph{Note that the ISO8601 format is only valid for
+the years $0000 .. 9999$.  Values out side that range should cause a
+\code{NULL} pointer to be returned.}
+
+\begin{prototype}
+double psTimeToJD(const psTime *time);
+double psTimeToMJD(const psTime *time);
+psString psTimeToISO(const psTime *time);
+struct timeval *psTimeToTimeval(const psTime *time);
+struct tm *psTimeToTM(const psTime *time);
+psString psTimeStrftime(const psTime *time, const char *format);
+\end{prototype}
+
+The \code{psTimeToISO()} function converts \code{PS_TIME_UTC} objects
+with the \code{leapsecond} flag set to represent the number of seconds
+as ``:60''.
+
+The \code{psTimeStrftime()} function converts psTime objects into a string
+who's formatted is defined by \code{format}.  Where \code{format} is a
+\code{strftime(3)} compatable formating string.
+
+\subsubsection{Date and Time Parsing}
+
+A related collection of functions convert from external formats to a
+\code{psTime} type.
+
+\begin{prototype}
+psTime *psTimeFromJD(double jd);
+psTime *psTimeFromMJD(double mjd);
+psTime *psTimeFromISO(const char *input, psTimeType type);
+psTime *psTimeFromTimeval(const struct timeval *input);
+psTime *psTimeFromTM(const struct tm* time);
+psTime *psTimeFromUTC(psS64 sec, psU32 nsec, bool leapsecond);
+psTime *psTimeFromTT(psS64 sec, psU32 nsec);
+psTime *psTimeStrptime(const char *s, const char *format);
+\end{prototype}
+
+Where \code{psTimeFromUTC()} will validate that it is possible for the input
+time to land on a UTC leapsecond (using \code{psTimeIsLeapSecond()}) if
+\code{leapsecond} is set to \code{true}.
+
+The \code{psTimeStrptime()} function parses the string \code{s} into a \code{psTime} using a \code{strptime(3)} compatible format specified as \code{format}.
+
+\subsubsection{Date and Time Math}
+
+\begin{prototype}
+psTime *psTimeMath(const psTime *time, double delta);
+double psTimeDelta(const psTime *time1, const psTime *time2);
+\end{prototype}
+
+\code{psTimeMath} adds the \code{delta} (in seconds; may be negative) to a
+\code{psTime}.  \code{PS_TIME_UTC} objects will be converted to
+\code{PS_TIME_TAI}, before applying the delta, and then back to
+\code{PS_TIME_UTC}.  \code{psTimeDelta} gives the difference between times
+\code{time1} and \code{time2} (which may be negative).
+
+The API documentation should note that when handling UT1, date math is allowed
+on the UT1 system but that the seconds are not of \emph{SI} length (this is why
+conversion from UT1 is correctly forbidden).  It should also be noted that with
+\code{psTimeDelta()} it is possible to overflow the dynamic range of a
+\code{psS64} (the type of \code{psTime.sec}).
+
+Note that in both these functions the difference between two times is
+the elapsed number of seconds, including leap seconds.  For example,
+if we add 30 seconds to 1998-12-31T23:59:45Z, the result is
+1999-01-01T00:00:14Z, since a leap second was introduced at
+1999-01-01T00:00:00Z.
+
+Time math may only be done on \code{psTime} objects of the same type,
+and except in the case of UT1, the functions shall internally convert
+the \code{psTime} inputs to TAI before performing the math; this
+ensures that leap seconds are correctly counted.
+
+The type of the time returned by \code{psTimeMath} shall be the same
+as that of the input \code{time}.
+
+\subsubsection{Time Tables}
+
+The offset of UTC from UT1, $\Delta UT = UT1-UTC$, as well as the polar motion,
+$x_p$ and $y_p$, may be determined from table lookups.  Tables are available
+covering different time periods and with different time resolution, and so it
+is important to be able to utilize multiple tables.  Some tables may be found
+at:
+
+\begin{itemize}
+\item IERS Bulletin A \& B (1 year ago $\rightarrow$ now + $\sim$3 months)
+\begin{itemize}
+\item \code{ftp://maia.usno.navy.mil/ser7/finals.daily}
+\end{itemize}
+
+\item IERS Bulletin A \& B (1973-1-2 $\rightarrow$ now + $\sim$1 year)
+\begin{itemize}
+\item \code{ftp://maia.usno.navy.mil/ser7/finals.all}
+\end{itemize}
+
+\item $TAI - UTC$
+\begin{itemize}
+\item \code{ftp://maia.usno.navy.mil/ser7/tai-utc.dat}
+\end{itemize}
+
+\item $TAI -UTC$ (contains estimates prior to 1972)
+\begin{itemize}
+\item \code{http://hpiers.obspm.fr/eoppc/eop/eopc01/eopc01.1900-2004}
+\end{itemize}
+\end{itemize}
+
+The format of \code{finals.daily} and \code{finals.all} is documented at
+\code{ftp://maia.usno.navy.mil/ser7/readme.finals}.
+
+The tables shall reside on local disk in known locations (i.e., there is no
+need that they are downloaded from the internet and parsed by PSLib).  The
+format of these files shall be simple, for speed in reading.  Each line shall
+contain the date in MJD and the following values from both the A \& B IERS
+bulletins: $x_p$ (in arcseconds), $y_p$ (in arcseconds) and $\Delta$UT (in
+seconds).  This format must be readable by \code{psLookupTableRead}.  For
+example:
+
+\begin{verbatim}
+# finals.daily, 2005-03-17
+# ftp://maia.usno.navy.mil/ser7/finals.daily
+# MJD    PM-IP  PM-X"   PM-Y"   UT1-YP  UT1-UTCs    PM-X"   PM-Y"   UT1-UTCs
+#        A      A       A       A       A           B       B       B
+53403.00 I      .089198 .207785 I       -.5218847   .089220 .207360 -.5219040
+53404.00 I      .086549 .206873 I       -.5224164   .086670 .206850 -.5224260
+53405.00 I      .083589 .206143 I       -.5227681
+53406.00 I      .081541 .205865 I       -.5229582
+\end{verbatim}
+
+The location of these files, their priority order, and the ``from''
+and ``to'' dates of applicability will be specified through metadata
+(\S\ref{sec:timeMetadata}).
+
+{\bf Calls to \code{psTime} functions that require the time tables
+before calling \code{psTimeInitialize} shall result in an error.}
+
+When a value is required, the tables shall shall be checked in
+priority order to see if the date is within the range of applicability
+for the table.  If a table is found that is applicable, then the
+appropriate value shall be derived from linear interpolation between
+the nearest entries in the table.  If no table is found that is
+applicable, and the required date is later than those covered in the
+tables, a warning shall be generated, and a pre-determined formula
+shall be applied (\S\ref{sec:timeMetadata}).  For dates prior to those
+covered in the tables, the function shall generate a warning and use
+pre-determined values (\S\ref{sec:timeMetadata}).
+
+\paragraph{Time Metadata}
+\label{sec:timeMetadata}
+
+The following metadata keys will be used in time calculations:
+
+\begin{tabular}{l|l} \hline
+Metadata key & Purpose \\ \hline
+psLib.time.tables.dir   & Time table directory                                             \\
+psLib.time.tables.files & Time table file names (space-delimited)                          \\
+psLib.time.tables.from  & Time tables are valid from these MJDs (vector)                   \\
+psLib.time.tables.to    & Time tables are valid to these MJDs (vector)                     \\
+psLib.time.before.xp    & Value of XP for before the earliest MJD                          \\
+psLib.time.before.yp    & Value of YP for before the earliest MJD                          \\
+psLib.time.before.dut   & Value of UT1-UTC for before the earliest MJD                     \\
+pslib.time.predict.xp   & A vector containing the $x_p$ prediction formula coefficients    \\
+pslib.time.predict.yp   & A vector containing the $y_p$ prediction formula coefficients    \\
+pslib.time.predict.mjd  & A value containing the MJD offset for temporary variables        \\
+pslib.time.predict.dut  & A vector containing the UT1-UTC prediction formula coefficients  \\
+\hline
+\end{tabular}
+
+These metadata keys shall reside in a configuration file
+(\S\ref{sec:configspec}), \code{psTime.config}, which shall be loaded
+into a \code{psMetadata} structure private to \code{psTime}, as part
+of \code{psLibInit}.  An example of \code{psTime.config} follows:
+
+\begin{verbatim}
+# This configuration file specifies values required for time calculations by psLib.
+psLib.time.tables.dir   STR     /home/panstarrs/psLib/config/                   # Directory for time tables
+psLib.time.tables.n     U8      2                                               # Number of time tables
+psLib.time.tables.files STR     bulletinA_09Sep2004.dat eopc01_1900_2004.40.dat # These are the file names
+@psLib.time.tables.from F64     53258.0, 15020.0                                # Valid from these MJDs
+@psLib.time.tables.to   F64     53622.0, 53258.0                                # Valid to these MJDs
+psLib.time.before.xp    F64     0.0                     # Value of XP for before the earliest MJD
+psLib.time.before.yp    F64     0.0                     # Value of YP for before the earliest MJD
+psLib.time.before.dut   F64     0.0                     # Value of UT1-UTC for before the earliest MJD
+                                                        
+# Now follows formulae for predicting ahead of the most recent available table entry.
+# xp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# yp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# A = 2*pi*(MJD - pslib.time.predict.mjd)/365.25
+# C = 2*pi*(MJD - pslib.time.predict.mjd)/435.0
+# dut = ut1-utc = [0] + [1]*(MJD - [2]) - (UT2-UT1)
+# ut2-ut1 = 0.022 sin(2*pi*T) - 0.012 cos(2*pi*T) - 0.006 sin(4*pi*T) + 0.007 cos(4*pi*T)
+# T = 2000.0 + (MJD - 51544.03)/365.2422
+@psLib.time.predict.xp  F64     0.0569, 0.0555, -0.0200, 0.0513, 0.1483
+@psLib.time.predict.yp  F64     0.3498, -0.0176, -0.0498, 0.1483, -0.0513
+psLib.time.predict.mjd  F64     53257.0
+@psLib.time.predict.dut F64     -0.4944, -0.00023, 53262.0
+\end{verbatim}
+
+A series of test inputs is contained in \S\ref{sec:timetest}.
+
+\subsection{Timers}
+
+It is useful to be able to time an operation.  For this purpose, we specify
+the following:
+
+\begin{prototype}
+bool psTimerStart(char *name);
+psF64 psTimerMark(char *name);
+psF64 psTimerClear(char *name);
+bool psTimerStop(void);
+\end{prototype}
+
+\code{psTimerStart} shall store the current time in a \code{psHash} of
+timers, under the supplied \code{name}.  \code{psTimerMark} shall
+return the elapsed time, in seconds, for the timer specified by
+\code{name}, or NAN if the timer does not exist.  \code{psTimerClear}
+resets the named timer, return the elapsed time on that timer so far,
+or NAN if not currently defined.  \code{psTimerStop} shall free all
+memory associated with all timers, so that no memory is leaked.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Linear and Spherical Coordinates}
+
+Both detector and sky positions will be used extensively in the IPP.
+The first are linear coordinates which conform to Euclidean geometry
+while the second are angular coordinates which define a position on
+the sphere of the sky.  We put these into two structures,
+\code{psPlane} and \code{psSphere}, respectively.  Partitioning these
+two will enable error-checking.  An alternative representation for
+angular positions is the 3-D unit vector.  These are used in
+particular as part of spherical rotation calculations.  We define
+\code{psCube} to represent such an element.
+%
+\begin{datatype}
+typedef struct {
+    double x;                           ///< x position
+    double y;                           ///< y position
+    double xErr;                        ///< Error in x position
+    double yErr;                        ///< Error in y position
+} psPlane;
+
+typedef struct {
+    double r;                           ///< RA
+    double d;                           ///< Dec
+    double rErr;                        ///< Error in RA
+    double dErr;                        ///< Error in Dec
+} psSphere;
+
+typedef struct {
+    double x;                           ///< cos (DEC) cos (RA) 
+    double y;                           ///< cos (DEC) sin (RA) 
+    double z;                           ///< sin (DEC)
+    double xErr;                        ///< Error in x
+    double yErr;                        ///< Error in y
+    double zErr;                        ///< Error in z
+} psCube;
+\end{datatype}
+
+Three major classes of coordinate transformations are necessary.
+First, linear coordinates from one frame must be converted to linear
+coordinates in a different frame of references.  Simple
+transformations of this type are independent of other quantities of
+the positions -- they are simply mapping between two linear spaces.
+In practice, these transformations may often be a function of the
+color or even magnitude of the imaged object.  The second type of
+conversion is the transformation of linear coordinates to angular
+coordinates and vice-versa.  This conversion depends on the desired
+projection, and may represent the real mapping performed by the
+telescope or may simply represent a convenient mechanism to display 3D
+coordinates in useful forms.  The third conversion of interest is the
+transformation of one set of spherical coordinates to another set.
+Frequently in astronomy, these conversions consist only of rotations
+between the two spherical coordinates systems, where the coordinates
+of the pole and equatorial rotation between the two systems define the
+transformation.  Conversions between standard coordinate systems such
+as Galactic, Ecliptic, and various epochs of the Celestial coordinates
+are represented by these spherical transformations.
+
+Constructors for these are straight-forward:
+\begin{prototype}
+psPlane  *psPlaneAlloc(void);
+psSphere *psSphereAlloc(void);
+psCube   *psCubeAlloc(void);
+\end{prototype}
+Initialization of the structures is not necessary.
+
+\subsubsection{Linear Coordinate Transformations}
+
+We specify two types of transforms between coordinate systems.  The
+first consists simply of two 2D polynomials to transform both
+components -- the output coordinates depend only on the input
+coordinates and no other quantities of objects at those coordinates.
+The second consists of two 4D polynomials in which the output
+coordinates are also specified to be a function of the color and
+magnitude of the object with the given coordinates.  This type of
+coordinate transformation is necessary to represent the
+(color-dependent) optical distortions caused by the atmosphere and
+camera optics, and the possibly effects of charge transfer
+inefficiency.  We specify two structures to represent the coefficients
+of these transformations:
+
+\begin{datatype}
+typedef struct {
+    psPolynomial2D *x;
+    psPolynomial2D *y;
+} psPlaneTransform;
+\end{datatype}
+
+The \code{psPolynomial2D} structures represent polynomials of
+arbitrary order as a function of two dimensions.  There is one of
+these structures for each of the two output dimensions.  As an
+example, consider the simple transformation from one linear coordinate
+frame ($x,y$), e.g., on a CCD, to a second frame ($p,q$), e.g., on a
+chip. If we have only first order terms in the transformation
+\code{psPlaneTransform T}, the new coordinates would be represented by
+the terms:
+%
+\begin{verbatim}
+p = T.x->coeff[0][0] + x*T.x->coeff[1][0] + y*T.x->coeff[0][1];
+q = T.y->coeff[0][0] + x*T.y->coeff[1][0] + y*T.y->coeff[0][1];
+\end{verbatim}
+%
+where we have excluded the basic cross-term ($x \times y$) by using
+the mask: \code{T.x->mask[1][1] = 1; T.y->mask[1][1] = 1;}
+
+The \code{psPlaneDistort} represents an optical distortion.  The
+lowest two terms are the $x$ and $y$ axis of the target system.  The
+higher two terms may represent color and magnitude terms.
+\begin{datatype}
+typedef struct {
+    psPolynomial4D *x;
+    psPolynomial4D *y;
+} psPlaneDistort;
+\end{datatype}
+
+Like \code{psPlaneTransform}, \code{psPlaneDistort} contains two
+\code{psPolynomial4D} structures representing polynomials of
+arbitrary order as a function of four, rather than two dimensions.
+There is one of these structures for each of the two output
+dimensions.  In this structure, the highest two dimensions could
+represent a color and magnitude.  As an example, consider the simple
+transformation from one linear coordinate frame ($x,y$), e.g., on a
+CCD, of an object with color and magnitude ($c,m$) to a second frame
+($p,q$), e.g., the focal plane. If we have only first order terms in
+the transformation \code{psPlaneDistort T}, the new coordinates
+would be represented by the terms:
+%
+\begin{verbatim}
+p = T.x->coeff[0][0][0][0] + x*T.x->coeff[1][0][0][0] + y*T.x->coeff[0][1][0][0] 
+  + c*T.x->coeff[0][0][1][0] + m*T.x->coeff[0][0][0][1]
+q = T.y->coeff[0][0][0][0] + x*T.y->coeff[1][0][0][0] + y*T.y->coeff[0][1][0][0] 
+  + c*T.y->coeff[0][0][1][0] + m*T.y->coeff[0][0][0][1]
+\end{verbatim}
+%
+where we have again excluded the cross-term ($x \times y$) by using the
+mask.
+
+Each of \code{psPlaneTransform} and \code{psPlaneDistort} has an
+appropriate allocator that takes the polynomial order in each
+dimension.  Both the \code{x} and \code{y} polynomials shall be have
+the same dimensions.
+\begin{prototype}
+psPlaneTransform *psPlaneTransformAlloc(int order1, int order2);
+psPlaneDistort *psPlaneDistortAlloc(int order1, int order2, int order3, int order4);
+\end{prototype}
+
+We require corresponding functions to apply the transformations to a
+specified coordinate \code{coords}:
+%
+\begin{prototype}
+psPlane *psPlaneTransformApply(psPlane *out, const psPlaneTransform *transform, 
+                               const psPlane *coords);
+psPlane *psPlaneDistortApply(psPlane *out, const psPlaneDistort *distort, 
+                             const psPlane *coords, float mag, float color);
+\end{prototype}
+
+
+The following functions perform operations on transformations:
+
+\begin{prototype}
+psPlaneTransform *psPlaneTransformInvert(psPlaneTransform *out, const psPlaneTransform *in,
+                                         psRegion region, int nSamples);
+psPlaneTransform *psPlaneTransformCombine(psPlaneTransform *out, const psPlaneTransform *trans1,
+                                          const psPlaneTransform *trans2, psRegion region,
+                                          int nSamples);
+bool psPlaneTransformFit(psPlaneTransform *trans, const psArray *source, const psArray *dest,
+                         int nRejIter, float sigmaClip);
+\end{prototype}
+
+\code{psPlaneTransformInvert} shall return the transformation that
+inverts the given transformation.  It may assume that the input
+transformation is one-to-one, and that the inverse transformation may
+be specified through using polynomials of the same type and order as
+the forward transformation.  In the event that the input
+transformation is linear, an exact solution may be calculated;
+otherwise \code{nSamples} samples in each axis, covering the region
+specified by \code{region} shall be used as a grid to fit the best
+inverse transformation.  The function shall return \code{NULL} if it
+was unable to generate the inverse transformation; otherwise it shall
+return the inverse transformation.  In the event that \code{out} is
+\code{NULL}, a new \code{psPlaneTransform} shall be allocated and
+returned. \tbd{is this subimage-safe?}
+
+\code{psPlaneTransformCombine} takes two transformations
+(\code{trans1} and \code{trans2}) and returns a single transformation
+that has the effect of performing \code{trans1} followed by
+\code{trans2}.  In the event that the input transformation is linear,
+an exact solution may be calculated; otherwise \code{nSamples} samples
+in each axis, covering the region specified by \code{region} shall be
+used as a grid to fit the best inverse transformation.  The function
+shall return \code{NULL} if it was unable to generate the
+transformation; otherwise it shall return the transformation.
+
+\code{psPlaneTransformFit} takes two arrays containing matched
+coordinates (i.e., coordinates in the \code{source} array correspond
+to the coordinates in the \code{dest} array) and returns the
+best-fitting transformation.  The \code{source} and \code{dest} will
+contain \code{psCoord}s.  In the event that the number of coordinates
+in each is not identical, the function shall generate a warning, and
+extra coordinates in the longer of the two shall be ignored.  The
+\code{trans} transform may not be \code{NULL}, since it specifies the
+desired order, polynomial type and any polynomial terms to mask.
+\code{nRejIter} rejection iterations shall be performed, wherein
+coordinates lying more than \code{sigmaClip} standard deviations from
+the fit shall be rejected.
+
+\paragraph{Derivatives}
+
+In order to simply calculate which pixels in one coordinate frame
+overlap those in another coordinate frame tied by a
+\code{psPlaneTransform}, we need to calculate the derivative of a
+transformation with respect to each coordinate.
+
+\begin{prototype}
+psPlane *psPlaneTransformDeriv(psPlane *out, const psPlaneTransform *transformation,
+                               const psPlane *coord);
+\end{prototype}
+
+\code{psPlaneTransformDeriv} shall return the derivatives of the
+\code{transformation} at the specified coordinates, \code{coord}.
+Both the derivatives with respect to $x$ and $y$ shall be returned.
+If \code{out} is non-\code{NULL}, it shall be modified and returned;
+otherwise a new \code{psPlane} shall be allocated and returned.  In
+the event that either \code{transformation} or \code{coord} are
+\code{NULL}, the function shall generate an error and return
+\code{NULL}.
+
+\begin{prototype}
+psPixels *psPixelsTransform(psPixels *out, const psPixels *input,
+                            const psPlaneTransform *inToOut);
+\end{prototype}
+
+\code{psPixelsTransform} shall generate a list of pixels in the output
+coordinate frame that overlap the \code{input} pixels in the input
+coordinate frame through the specified transformation, \code{inToOut}.
+Note that this is more complicated than simply transforming the
+\code{input} pixels, but requires the evaluation of the derivatives of
+the transformation in order to obtain the list of all pixels in the
+output coordinate frame that possibly overlap the pixel in the input
+coordinate frame (assuming that $x' + \Delta x' = f(x) + \Delta x
+\times \partial f(x) / \partial x$, where $x'$ is the coordinate in
+the output coordinate frame, $\Delta x'$ is the size of the region in
+the output coordinate frame in the positive direction that overlaps
+the input pixel, $x$ is the coordinate in the input coordinate frame,
+$f(x)$ is the transformation from the input to the output coordinate
+frames, and $\Delta x$ is the size of a pixel; care should be taken
+with half-pixel problems).  In the event that \code{input} or
+\code{inToOut} are \code{NULL}, the function shall generate an error
+and return \code{NULL}.
+
+\subsubsection{Spherical Rotations}
+
+Spherical rotations represent coordinate transformation in 3-D, as
+well as the effects of precession and nutation.  We need spherical
+rotations to convert between ICRS, Galactic and Ecliptic
+coordinates, and to determine Alt-Az coordinates for sources.  All of
+these basic spherical transformations represent rotations of the
+spherical coordinate reference.  We specify a general transformation
+function which takes a structure, \code{psSphereRot}, defining the
+transformation between two spherical coordinate systems.  The
+structure contains the elements of a quaternion to represent the
+spherical rotational.  We define two allocators for
+\code{psSphereRot}, one which defines the rotation in terms of the
+coordinate of the pole and the rotation about that pole.  The other
+defines the rotation from the elements of the quaternion.  We also
+specify functions to manipulate \code{psSphereRot} in several useful
+way.
+
+\begin{datatype}
+typedef struct {
+    double q0;
+    double q1;
+    double q2;
+    double q3;
+} psSphereRot;
+\end{datatype}
+
+The constructor is defined as follows:
+\begin{prototype}
+psSphereRot *psSphereRotAlloc(double alphaP, double deltaP, double phiP);
+\end{prototype}
+where \code{alphaP} and \code{deltaP} define the coordinates in the
+input system of the axis of rotation (the north pole of the output
+system), while \code{phiP} defines the rotation about that pole.  This
+last angle is also equal to 270\degree - $\phi_a$, where $\phi_a$ is
+the longitude in the output system of the ascending node (equatorial
+intersection between the two systems, e.g, the first point of Ares).
+
+The \code{psSphereRot} may also be constructed by supplying the
+elements of the quaternion to the following function:
+\begin{prototype}
+psSphereRot *psSphereRotQuat(double q0, double q1, double q2, double q3);
+\end{prototype}
+This function normalizes the quaternion, so the input elements need
+not be normalized.
+
+\begin{prototype}
+psSphereRot *psSphereRotConjugate(psSphereRot *out, const psSphereRot *in);
+\end{prototype}
+This function shall return the conjugate of the \code{in} rotation,
+and store it in the \code{out} rotation, if non-\code{NULL}.
+
+Spherical coordinates may be transformed by providing the
+transformation and the coordinate in the input system to
+\code{psSphereRot}.  The output pointer may be optionally supplied, or
+if \code{NULL}, is allocated by the function.
+
+\begin{prototype}
+psSphere *psSphereRotApply(psSphere *out, const psSphereRot *transform,
+                           const psSphere *coord);
+\end{prototype}
+
+The following function combines two rotations, to produce a single
+rotation which is the equivalent of applying the first rotation and
+then the second.  The output rotation may be supplied, or will be
+allocated if \code{NULL}.
+
+\begin{prototype}
+psSphereRot *psSphereRotCombine(psSphereRot *out, const psSphereRot *rot1,
+                                const psSphereRot *rot2);
+\end{prototype}
+
+The following function changes the given rotation to its inverse:
+
+\begin{prototype}
+psSphereRot *psSphereRotInvert(psSphereRot *rot);
+\end{prototype}
+
+The 3-vector representation of the angles (\code{psCube}) is needed to
+implement these functions, and is useful in other circumstances as
+well.  Two utility functions are provided to convert between the
+angular and 3-vector representations:
+\begin{prototype}
+psSphere *psCubeToSphere(const psCube *cube);
+psCube *psSphereToCube(const psSphere *sphere);
+\end{prototype}
+
+\subsubsection{Offsets}
+We require a function to calculate the offset between two positions on
+the sky, as well as a function to apply an offset to a position.  The
+first determines the offset (RA,Dec) on the sky between two positions.
+The second applies the given offset to the coordinate.  Both an offset
+mode and an offset unit may be defined.  The mode may be either
+\code{PS_SPHERICAL}, in which case the specified offset corresponds to
+an offset in angles, or it may be \code{PS_LINEAR}, in which case the
+offset corresponds to a linear offset in a local projection.  The
+offset unit may be in one of \code{PS_ARCSEC}, \code{PS_ARCMIN},
+\code{PS_DEGREE}, and \code{PS_RADIAN}, which specifies the units of
+the offset only.
+
+\begin{prototype}
+psSphere *psSphereGetOffset(const psSphere *position1, const psSphere *position2, 
+                            psSphereOffsetMode mode, psSphereOffsetUnit unit);
+psSphere *psSphereSetOffset(const psSphere *position, const psSphere *offset,
+                            psSphereOffsetMode mode, psSphereOffsetUnit unit);
+\end{prototype}
+
+\begin{datatype}
+typedef enum {
+    PS_SPHERICAL,                       ///< Offset on a sphere
+    PS_LINEAR                           ///< Linear offset
+} psSphereOffsetMode;
+
+typedef enum {
+    PS_ARCSEC,                          ///< Arcseconds
+    PS_ARCMIN,                          ///< Arcminutes
+    PS_DEGREE,                          ///< Degrees
+    PS_RADIAN                           ///< Radians
+} psSphereOffsetUnit;
+\end{datatype}
+Note that these should propagate the errors appropriately.
+
+\subsection{Celestial Coordinate Systems}
+
+The following functions simply return the appropriate
+\code{psSphereRot} to convert between predefined spherical
+coordinate systems (i.e., ICRS, Ecliptic and Galactic).  These are
+constructors as well as the above \code{psSphereRotAlloc}.
+%
+\begin{prototype}
+psSphereRot *psSphereRotICRSToEcliptic(const psTime *time);
+psSphereRot *psSphereRotEclipticToICRS(const psTime *time);
+psSphereRot *psSphereRotICRSToGalactic(void);
+psSphereRot *psSphereRotGalacticToICRS(void);
+\end{prototype}
+
+\subsection{Earth Orientation Calculations}
+
+One of the critical sets of calculations in astronomy is the sequence
+of steps needed to convert between the celestial coordinates of an
+object and the observed coordinates of the object.  This problem is
+best divided into two major components: transformation between the
+celestial sphere and coordinates relative to the surface of the solid
+earth, excluding the effects of the atmosphere, and compensation for
+the effects of the atmosphere.  In this section, we address the first
+of these two transformations: the Earth Orientation Calculations.
+
+The Earth Orientation Calculations are further subdivided into several
+steps, illustrated in Figure~\ref{CoordinateSystems} .  Celestial
+coordinates are defined in the International Celestial Reference
+System (ICRS), which has the solar barycenter as its reference
+position and velocity.  The next coordinate system is the Geocentric
+Celestial Reference System (GCRS), which uses the earth barycenter as
+a reference.  The transformation between these two includes the
+aberration due to the Earth's velocity, the parallax of the object,
+which depends on both the Earth's position and the distance to the
+object of interest, and the general relativistic correction for the
+bending of light as it approaches the Earth.
+
+The next set of transformations compensate for the 3-D rotation of
+the Earth on various timescales, including the effects of precession,
+nutation, and simple solid-body rotation.  These calculations can be
+performed using different amounts of information for higher levels of
+precision.  Since the Earth's rotation is constantly affected by
+stochastic processes (weather, earthquakes, etc), these conversions
+are constantly modified by observations reported by authoritative
+sources such as the US Naval Observatory.  The target of this
+transformation is the International Terrestrial Reference System
+(ITRS), which is fixed with respect to the Earth's crust.  This
+transformation is subdivided into slow precession and nutation
+(yielding the coordinate system CIP/CEO), followed by the Earth's
+rotation (yielding the coordinate system CIP/TEO), and finally
+corrections for the short-period motion of the Earth's pole.  
+
+\begin{figure}
+\begin{center}
+\psfig{file=pics/earthrot,height=4in}
+\caption{Earth Orientation Coordinate Frames\label{CoordinateSystems}}
+\end{center}
+\end{figure}
+
+\subsubsection{Transformation from ICRS to GCRS}
+
+\tbd{we need a function to construct the direction and speed elements
+  given the time}.
+
+\tbd{supply the velocity as an un-normalized 3 vector?}
+
+\tbd{MHPCC: please code this section as currently specified.  We will
+  define a function, and algorithm, to return the current velocity
+  vector given a time and position, which can be fed to this
+  function}.
+
+\paragraph{Aberration}
+The following function calculates the \code{apparent} position of a
+star, given its \code{actual} position and the velocity vector of the
+observer, represented as a speed and a direction:
+\begin{prototype}
+psSphere *psAberration(psSphere *apparent, const psSphere *actual,
+                       const psSphere *direction, double speed);
+\end{prototype}
+The \code{actual} and \code{apparent} positions are represented as
+\code{psSphere} entries, as is the \code{direction} of motion.  The
+speed in that direction is given in units of the speed of light.  If
+the value of \code{apparent} is NULL, a new \code{psSphere} is
+allocated, otherwise the point to \code{apparent} is used for the
+result.
+
+\paragraph{Gravitational Deflection}
+
+The following function calculates the \code{apparent} position of a
+star, given its \code{actual} position and the position of the sun:
+\begin{prototype}
+psSphere *psGravityDeflection(psSphere *apparent, psSphere *actual, psSphere *sun);
+\end{prototype}
+The \code{actual} and \code{apparent} positions are represented as
+\code{psSphere} entries, as is position of the sun.  If the value of
+\code{apparent} is NULL, a new \code{psSphere} is allocated, otherwise
+the point to \code{apparent} is used for the result.
+
+\paragraph{Parallax}
+
+\tbd{The parallax factor is not critical to the EOC calculations, and
+we don't have a formula handy for it at the moment, so do not code.}
+
+\begin{verbatim}
+double psEOC_ParallaxFactor(const psSphere *coords, const psTime *time);
+\end{verbatim}
+Calculate the parallax factor for the given position and time.
+
+\subsubsection{Transformation from GCRS to ITRS}
+
+The following functions calculate the components, $X$, $Y$, and $s$,
+representing the location of the earth's pole at any moment, or they
+determine the velocity of the pole $X'$, $Y'$, $s'$.  We use the
+following structure to carry the polar coordinate information.  This
+representation may be converted to a rotation between the frames.
+
+\begin{datatype}
+typedef struct {
+  double x;
+  double y;
+  double s;
+} psEarthPole;
+\end{datatype}
+
+The constructor is:
+\begin{prototype}
+psEarthPole *psEarthPoleAlloc(void);
+\end{prototype}
+
+\paragraph{Precession/Nutation}
+
+The following routine calculates the components of the rotation
+between the CEO and GCRS frames, $X$, $Y$, and $s$, using to the
+IAU2000A precession \& nutation model:
+%
+\begin{prototype}
+psEarthPole *psEOC_PrecessionModel(const psTime *time);
+\end{prototype}
+%
+The input to this function is the desired \code{time}, which may be
+represented in any format other than UT1.  This routine must give
+results identical to the IERS XYS2000A subroutine within the limits of
+machine accuracy.
+
+The following function provides interpolated corrections to the $X$
+and $Y$ components of the polar coordinates from the tables provided
+by the IERS, just as it does for UT1 and polar motion.
+
+\begin{prototype}
+psEarthPole *psEOC_PrecessionCorr(const psTime *time, psTimeBulletin bulletin);
+\end{prototype}
+
+The polar correction is applied to the $X$ and $Y$ elements of the
+rotation to provide higher accuracy.  The spherical rotation term is
+generated by providing the polar coordinate to the following function:
+\begin{prototype}
+psSphereRot *psSphereRot_CEOtoGCRS(const psEarthPole *pole);
+\end{prototype}
+This function constructs the rotation element as described in the ADD (
+The resulting \code{psSphereRot} may be used to determine the rotation
+from CIP/CEO to GCRS.  This function must give results identical to
+the IERS BPN2000, within the limits of machine accuracy.
+
+\paragraph{Earth Rotation}
+
+The following routine calculates the rotation of the Earth about the CIP:
+\begin{prototype}
+psSphereRot *psSphereRot_TEOtoCEO(const psTime *time, psEarthPole *tidalCorr);
+\end{prototype}
+The IERS code to create the comparable rotation is embedded in
+T2C2000, with the Earth Rotation Angle calculated by ERA2000.  The
+tidal correction, \code{tidalCorr} from \code{psEOC_PolarTideCorr} is
+used to correct UT1.
+
+\paragraph{Polar Motion}
+
+The following function provides interpolated values of the polar
+motion components, $x_p$ and $y_p$, extracted from the IERS tables.  
+\begin{prototype}
+psEarthPole *psEOC_GetPolarMotion(const psTime *time, psTimeBulletin bulletin);
+\end{prototype}
+
+The following function provides tidal corrections to the polar motion
+components, $x_p$ and $y_p$, using the Ray model of Simon et al (see
+ADD).  It also provides a time correction to UT1 for
+\code{psSphereRot_TEOtoCEO} that we will, for convenience, place in
+the \code{s} component of the output \code{psEarthPole}.
+\begin{prototype}
+psEarthPole *psEOC_PolarTideCorr(const psTime *time);
+\end{prototype}
+
+The following function provides the additional corrections due to nutation
+terms with periods less than or equal to two days, as well as the
+correction to the $s'$ component of the polar motion:
+\begin{prototype}
+psEarthPole *psEOC_NutationCorr(psTime *time);
+\end{prototype}
+
+The following function converts the polar motion corrections to a
+spherical rotation using the prescription in the ADD:
+\begin{prototype}
+psSphereRot *psSphereRot_ITRStoTEO(const psEarthPole *motion);
+\end{prototype}
+This function should give identical results to the IERS POM2000
+subroutine.
+
+\subsubsection{Earth Orientation Wrappers}
+
+The following function generates the complete spherical rotation to
+account for precession between two times.  If \code{NULL} is provided
+for either time, it is assumed to have the reference equinox value of
+J2000.
+\begin{prototype}
+psSphereRot *psSpherePrecess(const psTime *fromTime, const psTime *toTime, psPrecessMethod mode);
+\end{prototype}
+The mode argument is used to specify the level of detail used in the
+calculation.
+
+\begin{datatype}
+typedef enum {
+  PS_PRECESS_ROUGH,
+  PS_PRECESS_COMPLETE_A,
+  PS_PRECESS_COMPLETE_B,
+  PS_PRECESS_IAU2000A
+} psPrecessMethod;
+\end{datatype}
+
+\code{PS_PRECESS_ROUGH} indicates that an approximate precession
+rotation is determined from a cubic polynomial in the time difference
+(ADD 3.4.3).  \code{PS_PRECESS_IAU2000A} indicates that the precession
+rotation is determined from differencing the two rotations obtained
+from applying the IAU 2000A model (\code{psEOC_PrecessionModel},
+followed by \code{psSphereRot_CEOtoGCRS}) at each epoch.
+\code{PS_PRECESS_COMPLETE_A} and \code{PS_PRECESS_COMPLETE_B}
+indicates that the precession rotation is the same as for
+\code{PS_PRECESS_IAU2000A}, except that the earth pole correction
+published by the IERS is included (using the appropriate bulletin in
+\code{psEOC_PrecessionCorr} --- \code{PS_IERS_A} for
+\code{PS_PRECESS_COMPLETE_A}, and \code{PS_IERS_B} for
+\code{PS_PRECESS_COMPLETE_B}).
+
+\subsection{Atmospheric Effects}
+
+\tbd{The ATM effects components should be deferred until we clean up
+  the refraction definitions}
+
+A-priori astrometric transformations between the telescope orientation
+(Alt/Az) and the predicted stellar coordinates above the atmosphere
+(DEC/HA) requires several pieces of information describing the current
+environmental conditions.  These quantities are consistent across an
+image, and may vary only slowly with time.  Pre-computing these
+quantities for exposures means that subsequent transformations are
+faster.  The structure below carries the environmental data of interest.
+For historical reasons, this structure is known colloquially as ``the
+Grommit''.
+
+\tbd{this structure needs to be modified to correspond to what we
+  actually need to carry around for the atmosphere functions}
+
+\tbd{provide a single Grommit to carry around all EOC + ATM
+  pre-calculated entries and a separate structure for ATM effect?}
+
+\begin{verbatim} %%% Changed to verbatim to remove from api-delta
+typedef struct {
+    const double latitude;              ///< geodetic latitude (radians)
+    const double longitude;             ///< longitude + ... (radians)
+    const double height;                ///< height (HM)
+    const double abberationMag;         ///< magnitude of diurnal aberration vector
+    const double temperature;           ///< ambient temperature (TDK)
+    const double pressure;              ///< pressure (PMB)
+    const double humidity;              ///< relative humidity (RH)
+    const double wavelength;            ///< wavelength (WL)
+    const double lapseRate;             ///< lapse rate (TLR)
+    const double refractA, refractB;    ///< refraction constants A and B (radians)
+    const double siderealTime;          ///< local apparent sidereal time (radians)
+} psGrommit;
+\end{verbatim}
+
+The \code{psGrommit} is calculated from telescope information for the
+particular exposure, \code{exp}:
+\begin{verbatim}
+psGrommit *psGrommitAlloc(const psExposure *exp);
+\end{verbatim}
+
+\tbd{these functions probably need to take the ATM structure}
+
+We require additional functions to perform general functions which
+will be useful for astrometry.  Given coordinates on the sky, we
+need to get the airmass, the parallactic angle, and an estimate of
+the atmospheric refraction.
+
+\begin{verbatim}
+float psGetAirmass(const psSphere *coord, psTime *lst, float altitude);
+\end{verbatim}
+which returns the airmass for a given position and local sidereal time
+(\code{lst}).
+
+\begin{verbatim}
+float psGetParallactic(const psSphere *coord, double siderealTime);
+\end{verbatim}
+which returns the parallactic angle for a given position and sidereal time.
+
+\begin{verbatim}
+float psGetRefraction(float colour,            ///< Colour of object
+                      psPhotSystem colorPlus,  ///< Colour reference
+                      psPhotSystem colorMinus, ///< Colour reference
+                      const psExposure *exp);  ///< Telescope pointing information
+\end{verbatim}
+which provides an estimate of the atmospheric refraction, along the parallactic angle.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+
+\subsection{Projections}
+
+We require functions to convert between spherical and linear
+coordinate systems based on a variety of projections.  The required
+projections include:
+\begin{itemize}
+\item TAN
+\item SIN
+\item AIT
+\item PAR
+\end{itemize}
+
+We specify the following structure \code{psProjection} to define the
+parameters of the projection:
+\begin{datatype}
+typedef struct {
+    double R;                           ///< coordinates of projection center
+    double D;                           ///< coordinates of projection center
+    double Xs;                          ///< plate-scale in X and Y directions
+    double Ys;                          ///< plate-scale in X and Y directions
+    psProjectionType type;              ///< projection type
+} psProjection;
+\end{datatype}
+
+The projection type is defined by the following enumerated type \code{psProjectionType}:
+\begin{datatype}
+typedef enum {                          ///< type of val is:
+    PS_PROJ_TAN,                        ///< Tangent projection
+    PS_PROJ_SIN,                        ///< Sine projection
+    PS_PROJ_AIT,                        ///< Aitoff projection
+    PS_PROJ_PAR,                        ///< Par projection
+    PS_PROJ_NTYPE                       ///< Number of types; must be last
+} psProjectionType;
+\end{datatype}
+
+The constructor is straight-forward:
+\begin{prototype}
+psProjection *psProjectionAlloc(double R, double D, double Xs, double Ys,
+                                psProjectionType type);
+\end{prototype}
+where the units of \code{R} and \code{D} are radians, while \code{Xs}
+and \code{Ys} are in radians per pixel.
+
+The following functions will project and deproject (respectively)
+spherical coordinates:
+
+\begin{prototype}
+psPlane  *psProject(const psSphere *coord, const psProjection *projection);
+psSphere *psDeproject(const psPlane *coord, const psProjection *projection);
+\end{prototype}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Astronomical objects}
+
+\tbd{These functions are all of low priority, have not yet been
+defined in detail, and hence are to be deferred.}
+
+\subsubsection{Positions of Major SS Objects}
+
+We may require the ability to calculate the position of major Solar System
+objects, as well as Lunar phase.
+
+\begin{verbatim} %%% XXX: This is set to 'verbatim' instead of 'prototype'
+psSphere *psSunGetPos(psTime *time);
+psTime *psSunGetRise (psTime *twi15, psTime *twi18, const psTime *time);
+psTime *psSunGetSet (psTime *twi15, psTime *twi18, const psTime *time);
+
+psSphere *psMoonGetPos(psTime time, psSphere location);
+psTime *psMoonGetRise (psTime *twi15, psTime *twi18, psTime *time);
+psTime *psMoonGetSet (psTime *twi15, psTime *twi18, psTime *time);
+float psGetMoonPhase(psTime time);
+
+psSphere *psPlanetGetPos(psTime time, psSphere location);
+psTime *psPlanetGetRise (psTime *twi15, psTime *twi18, psTime *time);
+psTime *psPlanetGetSet (psTime *twi15, psTime *twi18, psTime *time);
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\appendix
+
+\pagebreak
+\section{Configuration File Test Inputs}
+\label{sec:configtest}
+
+Here are a series of test inputs for the Configuration File syntax defined in
+\S\ref{sec:configspec}.
+
+\input{configFileTests.tex}
+
+\section{Dates \& Times Test Inputs}
+\label{sec:timetest}
+
+Here are a series of test inputs for the \code{psTime}(\S\ref{sec:timespec})
+ISO8601 parsing and formatting functions.  These tests will also validate the
+behavior of \code{psTime}'s conversion algorithms.
+
+\input{timeTests.tex}
+
+\section{Revision Change Log}
+\input{ChangeLogSDRS.tex}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/psTime.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/psTime.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/psTime.config	(revision 22322)
@@ -0,0 +1,21 @@
+# This configuration file specifies values required for time calculations by psLib.
+psLib.time.tables.n     U8      2                                               # Number of time tables
+psLib.time.tables.files STR     bulletinA_09Sep2004.dat eopc01_1900_2004.40.dat # These are the file names
+@psLib.time.tables.from F64     53258.0, 15020.0                                # Valid from these MJDs
+@psLib.time.tables.to   F64     53622.0, 53258.0                                # Valid to these MJDs
+psLib.time.before.xp    F64     0.0                     # Value of XP for before the earliest MJD
+psLib.time.before.yp    F64     0.0                     # Value of YP for before the earliest MJD
+psLib.time.before.dut   F64     0.0                     # Value of UT1-UTC for before the earliest MJD
+							
+# Now follows formulae for predicting ahead of the most recent available table entry.
+# xp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# yp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# A = 2*pi*(MJD - pslib.time.predict.mjd)/365.25
+# C = 2*pi*(MJD - pslib.time.predict.mjd)/435.0
+# dut = ut1-utc = [0] + [1]*(MJD - [2]) - (UT2-UT1)
+# ut2-ut1 = 0.022 sin(2*pi*T) - 0.012 cos(2*pi*T) - 0.006 sin(4*pi*T) + 0.007 cos(4*pi*T)
+# T = 2000.0 + (MJD - 51544.03)/365.2422
+@pslib.time.predict.xp  F64     0.0569, 0.0555, -0.0200, 0.0513, 0.1483
+@pslib.time.predict.yp  F64     0.3498, -0.0176, -0.0498, 0.1483, -0.0513
+pslib.time.predict.mjd  F64     53257.0
+@pslib.time.predict.dut F64     -0.4944, -0.00023, 53262.0
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/pslib-delta
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/pslib-delta	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/pslib-delta	(revision 22322)
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: pslib-delta,v 1.13 2006-07-25 00:46:20 jhoblitt Exp $
+
+MODULE=pslib
+DOC=psLibSDRS.tex
+UTIL_MODULE=utils
+ROOTDIR=$HOME/tmp/$0
+WORKDIR=$ROOTDIR/work
+UTILDIR=$ROOTDIR/$UTIL_MODULE
+TEXBLOCK=$UTILDIR/texblock.pl
+APIDELTA=$UTILDIR/apidelta.pl
+CVS=/usr/bin/cvs
+SSH=/usr/bin/ssh
+
+CVS_RSH=$SSH
+CVSROOT=cvs.pan-starrs.ifa.hawaii.edu:/cvsroot/pan-starrs
+export CVS_RSH CVSROOT
+
+PERL5LIB=$UTILDIR
+export PERL5LIB
+
+STARTDIR=`pwd`
+
+usage()
+{
+    cat <<EOF
+Usage: $0 [--color={none,auto,always}]
+EOF
+}
+
+if test $# -gt 1; then
+    usage
+    exit 1
+fi
+
+# checkout pslib sources
+
+CVS_ERROR=0
+
+if [ ! -d "$ROOTDIR/$MODULE" ] ; then
+    mkdir -p $ROOTDIR
+    cd $ROOTDIR
+    $CVS co $MODULE > /dev/null 2>&1 || CVS_ERROR=1
+else
+    cd "$ROOTDIR/$MODULE"
+    $CVS up -dP > /dev/null 2>&1 || CVS_ERROR=1
+fi
+
+if [ $CVS_ERROR -eq 1 ] ; then
+    echo "CVS error"
+    exit 1;
+fi
+
+# checkout utils
+
+CVS_ERROR=0
+
+if [ ! -d "$ROOTDIR/$UTIL_MODULE" ] ; then
+    mkdir -p $ROOTDIR
+    cd $ROOTDIR
+    $CVS co $UTIL_MODULE > /dev/null 2>&1 || CVS_ERROR=1
+else
+    cd "$ROOTDIR/$UTIL_MODULE"
+    $CVS up -dP > /dev/null 2>&1 || CVS_ERROR=1
+fi
+
+if [ $CVS_ERROR -eq 1 ] ; then
+    echo "CVS error from utils"
+    exit 1;
+fi
+
+# compare sources
+
+if [ ! -d "$WORKDIR" ] ; then
+    mkdir -p $WORKDIR
+fi
+
+cd $STARTDIR
+
+cd $ROOTDIR/$MODULE
+sh ./autogen.sh > /dev/null 2>&1
+
+cd $STARTDIR
+find $ROOTDIR/$MODULE/src -name "*.h" -exec cat {} \; > $WORKDIR/code.txt
+#find $ROOTDIR/$MODULE/src -name "*.h.in" -exec cat {} \; >> $WORKDIR/code.txt
+$TEXBLOCK -b prototype -i $DOC > $WORKDIR/spec.txt
+$TEXBLOCK -b datatype -i $DOC >> $WORKDIR/spec.txt
+$APIDELTA $1 --spec $WORKDIR/spec.txt --code $WORKDIR/code.txt
+#$APIDELTA $1 --private --code $WORKDIR/spec.txt --spec $WORKDIR/code.txt
+
+# cleanup
+
+#if [ -d "$WORKDIR" ] ; then
+#    rm -rf $WORKDIR
+#fi
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/pslib-delta-files.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/pslib-delta-files.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/pslib-delta-files.sh	(revision 22322)
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+DATE=`date +%F`
+NAME="pslib-18-fixes-api_delta"
+
+./pslib-delta --color=always > "$NAME-color-$DATE.txt"
+./pslib-delta --color=none >   "$NAME-$DATE.txt"
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/raymodel.f
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/raymodel.f	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/raymodel.f	(revision 22322)
@@ -0,0 +1,90 @@
+C
+      SUBROUTINE RAY (RJD,CORX,CORY,CORT)
+C
+C   THIS SUBROUTINE IMPLEMENTS THE RAY MODEL FOR
+C   DIURNAL/SUBDIURNAL TIDES.  IT USES THE SIMON ET AL.
+C   FUNDAMENTAL ARGUMENTS.  THE CORRECTIONS IN X AND Y ARE IN
+C   UNITS OF SEC. OF ARC AND UT1-UTC IN SEC. OF TIME.  THESE
+C   CORRECTIONS SHOULD BE ADDED TO "AVERAGE" EOP VALUES TO GET
+C   ESTIMATES OF THE INSTANTANEOUS VALUES.
+C
+C     PARAMETERS ARE :
+C     RJD   - EPOCH OF INTEREST GIVEN IN MJD
+C     CORX  - TIDAL CORRECTION IN X (SEC. OF ARC)
+C     CORY  - TIDAL CORRECTION IN Y (SEC. OF ARC)
+C     CORT  - TIDAL CORRECTION IN UT1-UTC (SEC. OF TIME)
+C
+      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
+      DOUBLE PRECISION
+     .   L,        LPRIME
+      HALFPI = 1.5707963267948966d0
+      T = (RJD - 51544.5D0)/36525.0D0
+      L = -0.00024470d0*T**4 + 0.051635d0*T**3 + 31.8792d0*T**2
+     .  + 1717915923.2178d0*T + 485868.249036d0
+      L = DMOD(L,1296000d0)
+      LPRIME = -0.00001149d0*T**4 - 0.000136d0*T**3
+     .  -  0.5532d0*T**2
+     .  + 129596581.0481d0*T + 1287104.79305d0
+      LPRIME = DMOD(LPRIME,1296000d0)
+      CAPF = 0.00000417d0*T**4 - 0.001037d0*T**3 - 12.7512d0*T**2
+     .  + 1739527262.8478d0*T + 335779.526232d0
+      CAPF = DMOD(CAPF,1296000d0)
+      CAPD = -0.00003169d0*T**4 + 0.006593d0*T**3 - 6.3706d0*T**2
+     .  + 1602961601.2090d0*T + 1072260.70369d0
+      CAPD = DMOD(CAPD,1296000d0)
+      OMEGA = -0.00005939d0*T**4 + 0.007702d0*T**3
+     .  + 7.4722d0*T**2
+     .  - 6962890.2665d0*T + 450160.398036d0
+      OMEGA = DMOD(OMEGA,1296000d0)
+      THETA = (67310.54841d0 + 
+     .        (876600d0*3600d0 + 8640184.812866d0)*T +
+     .         0.093104d0*T**2 -
+     .         6.2d-6*T**3)*15.0d0 + 648000.0d0
+      ARG7 = DMOD((-L - 2.0D0*CAPF - 2.0D0*OMEGA + THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0) - HALFPI
+      ARG1 = DMOD((-2.0d0*CAPF - 2.0d0*OMEGA + THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0) - HALFPI
+      ARG2 = DMOD((-2.0d0*CAPF + 2.0d0*CAPD - 2.0d0*OMEGA
+     .       + THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0) - HALFPI
+      ARG3 = DMOD(THETA *
+     .        3.14159265D0/648000.0D0,6.28318530718D0)
+     .     + HALFPI
+      ARG4 = DMOD((-L - 2.0d0*CAPF - 2.0D0*OMEGA + 2.0d0*THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0)
+      ARG5 = DMOD((-2.0D0*CAPF - 2.0D0*OMEGA + 2.0d0*THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0)
+      ARG6 = DMOD((-2.0d0*CAPF + 2.0d0*CAPD - 2.0d0*OMEGA
+     .     + 2.0d0*THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0)
+      ARG8 = DMOD((2.0d0*THETA)
+     .     * 3.14159265D0/648000.0D0,6.28318530718D0)
+      CORX = - 0.026D0*DSIN(ARG7) + 0.006D0*DCOS(ARG7)
+     .       - 0.133D0*DSIN(ARG1) + 0.049D0*DCOS(ARG1)
+     .       - 0.050D0*DSIN(ARG2) + 0.025D0*DCOS(ARG2)
+     .       - 0.152D0*DSIN(ARG3) + 0.078D0*DCOS(ARG3)
+     .       - 0.057D0*DSIN(ARG4) - 0.013D0*DCOS(ARG4)
+     .       - 0.330D0*DSIN(ARG5) - 0.028D0*DCOS(ARG5)
+     .       - 0.145D0*DSIN(ARG6) + 0.064D0*DCOS(ARG6)
+     .       - 0.036D0*DSIN(ARG8) + 0.017D0*DCOS(ARG8)
+      CORY = - 0.006D0*DSIN(ARG7) - 0.026D0*DCOS(ARG7)
+     .       - 0.049D0*DSIN(ARG1) - 0.133D0*DCOS(ARG1)
+     .       - 0.025D0*DSIN(ARG2) - 0.050D0*DCOS(ARG2)
+     .       - 0.078D0*DSIN(ARG3) - 0.152D0*DCOS(ARG3)
+     .       + 0.011D0*DSIN(ARG4) + 0.033D0*DCOS(ARG4)
+     .       + 0.037D0*DSIN(ARG5) + 0.196D0*DCOS(ARG5)
+     .       + 0.059D0*DSIN(ARG6) + 0.087D0*DCOS(ARG6)
+     .       + 0.018D0*DSIN(ARG8) + 0.022D0*DCOS(ARG8)
+      CORT = + 0.0245D0*DSIN(ARG7) + 0.0503D0*DCOS(ARG7)
+     .       + 0.1210D0*DSIN(ARG1) + 0.1605D0*DCOS(ARG1)
+     .       + 0.0286D0*DSIN(ARG2) + 0.0516D0*DCOS(ARG2)
+     .       + 0.0864D0*DSIN(ARG3) + 0.1771D0*DCOS(ARG3)
+     .       - 0.0380D0*DSIN(ARG4) - 0.0154D0*DCOS(ARG4)
+     .       - 0.1617D0*DSIN(ARG5) - 0.0720D0*DCOS(ARG5)
+     .       - 0.0759D0*DSIN(ARG6) - 0.0004D0*DCOS(ARG6)
+     .       - 0.0196D0*DSIN(ARG8) - 0.0038D0*DCOS(ARG8)
+      CORX = CORX * 1.0d-3
+      CORY = CORY * 1.0d-3
+      CORT = CORT * 0.1d-3
+      RETURN
+      END
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/testing.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/testing.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/testing.txt	(revision 22322)
@@ -0,0 +1,152 @@
+Function		Tests to perform
+========		================
+p_psAlloc		Careful inspection of code.  Tested through testing other modules.
+p_psFree		Careful inspection of code.  Tested through testing other modules.
+p_psRealloc		Careful inspection of code.  Tested through testing other modules.
+psAlloc			Careful inspection of code.  Tested through testing other modules.
+psFree			Careful inspection of code.  Tested through testing other modules.
+psMemAllocateCB		This is a typedef.
+psMemAllocateCBSet	Set up a memory callback, allocate memory and make sure it calls back.
+psMemAllocateCBSetID	Set up a memory callback, allocate memory and make sure it calls back.
+psMemCheckCorruption	Allocate memory, and check corruption.  Deliberately overwrite a memBlock.
+psMemCheckLeaks		Allocate and free memory, check for leaks.
+psMemDecrRefCounter	Decrement reference counter to 0 and call psFree.
+psMemExhaustedCBSet	Set CB and check explicitly.
+psMemFreeCBSet		Set CB to use with psMemFreeCBSetID
+psMemFreeCBSetID	Allocate memory, set CB and free memory.
+psMemGetID		Careful inspection of code.  Tested through testing other modules (e.g., psMemFreeCBSetID).
+psMemGetRefCounter	Allocate memory, increment rc, get rc.
+psMemIncrRefCounter	See psMemGetRefCounter.  Attempt to free with rc > 1.
+psMemProblemCBSet	Set CB and check explicitly.  Should be called by psMemCheckCorruption.
+psRealloc		Careful inspection of code.  Tested through testing other modules.
+psAbort			Do an abort.
+psError			Generate multiple levels of errors.
+psGetTraceLevel		Set trace level, get trace level, print trace level.  Check inheritance.
+psLogMsg		Set log level, print log message.  Check inheritance?  Try log level outside 0--9.
+psPrintTraceLevels	Set trace level, get trace level, print trace level.  Check inheritance.
+psSetLogDestination	Set log destination, write log.
+psSetLogFormat		Set log format, write log.
+psSetLogLevel		Set log level, print log message.  Set log level outside 0--9 range.
+psSetTraceLevel		Set trace level, get trace level.  Check inheritance.
+psTrace			Check inheritance.  Try negative trace level.
+psTraceReset		Set trace level, reset trace, print a trace message.
+psDlistAlloc		Constructor/Destructor will be tested through testing other functions.
+psDlistFree		Constructor/Destructor will be tested through testing other functions.
+psDlistAdd		Add to middle, beginning and end of a list.  Add to an empty list.
+psDlistAppend		Append to a list.  Append to empty list.
+psDlistRemove		Remove from middle, beginning and end of list.  Remove from single-element list.
+psDlistGet		Iterate through list to print stuff.
+psDlistSetIterator	Iterate through list to print stuff.
+psDlistGetNext		Iterate through list to print stuff.
+psDlistGetPrev		Iterate backwards through list to print stuff.
+psDlistToArray		Convert list to an array and then iterate through array.
+psArrayToDlist		Convert array back to Dlist and compare.
+psHashAlloc		Constructor/Destructor will be tested through testing other functions.
+psHashFree		Constructor/Destructor will be tested through testing other functions.
+psHashInsert		Put stuff into a hash, look for it.
+psHashLookup		Put stuff into a hash, look for it.
+psHashRemove		Look for something in a hash, remove it.
+psVectorAlloc		Constructor/Destructor will be tested through testing other functions.
+psVectorRealloc		Constructor/Destructor will be tested through testing other functions.
+psVectorFree		Constructor/Destructor will be tested through testing other functions.
+psVectorElementFree	Inspection of code?
+psVoidPtrArrayAlloc	Constructor/Destructor will be tested through testing other functions.
+psVoidPtrArrayRealloc	Constructor/Destructor will be tested through testing other functions.
+psVoidPtrArrayFree	Constructor/Destructor will be tested through testing other functions.
+psEvalDPolynomial2D	Create simple polynomial and check expected result.
+psGaussian		Input random values, compare output to self-calculated values.
+psPolynomial2DAlloc	Constructor/Destructor will be tested through testing other functions.
+psPolynomial2DFree	Constructor/Destructor will be tested through testing other functions.
+psBitMaskAlloc		Constructor/Destructor will be tested through testing other functions.
+psBitMaskFree		Constructor/Destructor will be tested through testing other functions.
+psBitMaskOp		Set various bitmasks, and test AND, OR, XOR.  Test different sized bitmasks.
+psBitMaskNot		Set bitmask, test NOT: a AND NOT a --> everything 0.
+psBitMaskSet		Tested in the course of psBitMaskOp.
+psBitMaskTest		Tested in the course of psBitMaskOp.
+psBinaryOp		+ - * /, divide by 0, ^, min, max: by a scalar and by a vector and by a matrix
+psMatrixDeterminant	Test singular matrix
+psMatrixInvert		Test singular matrix
+psMatrixLUD		Test singular matrix
+psMatrixLUSolve		Test singular matrix
+psMatrixOp		Test matrix multiplication
+psMatrixToVector	Convert matrix to vector and back to matrix.
+psMatrixTranspose	Transpose matrix and check
+psScalarType		Tested in the course of psBinaryOp
+psUnaryOp		abs, exp, ln, ten = 10^x, log = log_10, sin, cos, tan, asin, acos, atan: scalar, vector, matrix
+psVectorToMatrix	Convert matrix to vector and back to matrix
+psMinimize		Use a quadratic function and check that numerical minimum is analytic minimum.
+psMinimizeChi2		Similar to psMinimize, except using data points generated from a quadratic.
+psImageAlloc		Constructor/Destructor will be tested through testing other functions.
+psImageClip		Create random image, clip and check that everything is clipped.
+psImageCopy		Copy an image and compare with original.  Check type conversion!
+psImageCut		Create image with known pattern, cut across pattern and compare with pattern.
+psImageEvalPolynomial	Create image with polynomial, and fit polynomial to that image.
+psImageFitPolynomial	Create image with polynomial, and fit polynomial to that image.
+psImageFree		Constructor/Destructor will be tested through testing other functions.
+psImageFreePixels	Constructor/Destructor will be tested through testing other functions.
+psImageGetStats		Create image with known statistics, check stats against parent.
+psImageHistogram	Create image with 5 different values, get histogram with 3 bins and check.
+psImageOverlaySection	Create two random images, overlay one on the other and check pixel values.  Overlay over the edges.
+psImageRadialCut	Create image with known pattern, cut through pattern.
+psImageReadSection	Create an image, write image, read section and compare with original.
+psImageRebin		Create image with simple pattern (0,1,0,1), rebin and compare with expectation.
+psImageRoll		Roll an image and check with original.
+psImageRotate		Rotate an image 90 deg and 180 deg and compare both.
+psImageShift		Create image with simple pattern (0,1,0,1), shift by 0.5 pix and compare with expectation.
+psImageSlice		Create image with known pattern, slice through pattern.
+psImageSubset		Create random image, make subimage, compare.  Try to make subimage over edges.
+psImageWriteSection	Create image, write section, read image and compare.  Try to write over edges.
+psSort			Create random vector, sort, check.  Make sure there are conflicts, x[5] == x[6].
+psSortIndex		Create random vector, sort, check.  Make sure there are conflicts, x[5] == x[6].
+psArrayStats		Create vector with known statistics, get stats and compare with parent.
+psGetArrayHistogram	Create vector with 5 values, get histogram with 3 bins, compare.
+psHistogramAlloc	Constructor/Destructor will be tested through testing other functions.
+psHistogramAllocGeneric	Constructor/Destructor will be tested through testing other functions.
+psHistogramFree		Constructor/Destructor will be tested through testing other functions.
+psStatsAlloc		Constructor/Destructor will be tested through testing other functions.
+psStatsFree		Constructor/Destructor will be tested through testing other functions.
+
+
+
+[[[These functions are no longer relevant]]]
+
+psStringCopy		Not required, but used by RHL.
+psStringNCopy		Not required, but used by RHL.
+psFloatArrayAlloc	Replaced by psVector.
+psFloatArrayRealloc	Replaced by psVector.
+psFloatArrayFree	Replaced by psVector.
+psComplexArrayAlloc	Replaced by psVector.
+psComplexArrayRealloc	Replaced by psVector.
+psComplexArrayFree	Replaced by psVector.
+psIntArrayAlloc		Replaced by psVector.
+psIntArrayRealloc	Replaced by psVector.
+psIntArrayFree		Replaced by psVector.
+psDoubleArrayAlloc	Replaced by psVector.
+psDoubleArrayRealloc	Replaced by psVector.
+psDoubleArrayFree	Replaced by psVector.
+psVectorArrayAlloc	Did we specify this?
+psVectorArrayRealloc	Did we specify this?	
+psVectorArrayFree	Did we specify this?
+psFFTAlloc		FFT API changed.
+psFFTAlloc1D		FFT API changed.
+psFFTConvolve		FFT API changed.
+psFFTCrossCorrelate	FFT API changed.
+psFFTFilter		FFT API changed.
+psFFTFilterComplex	FFT API changed.
+psFFTForward		FFT API changed.
+psFFTFree		FFT API changed.
+psFFTGetFT		FFT API changed.
+psFFTGetImage		FFT API changed.
+psFFTPowerSpec		FFT API changed.
+psFFTReverse		FFT API changed.
+psGetArrayPolynomial	Dropped?
+psImageFReadHeader	Is this being implemented?
+psImageFReadSection	Is this being implemented?
+psImageFWriteSection	Is this being implemented?
+psImageFreeChildren	Dropped.
+psImageReadHeader	Dropped?
+psMemExhaustedCB	This is a typedef.
+psMemFreeCB		This is a typedef
+psMemProblemCB		This is a typedef.
+p_psTrace		This is a macro replacement for psTrace.
+psVLogMsg		This is a macro replacement for psLogMsg?
Index: /tags/sj_tags/sj_root_20080929/doc/pslib/timeTests.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/pslib/timeTests.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/pslib/timeTests.tex	(revision 22322)
@@ -0,0 +1,43 @@
+\subsection{Equivalent Dates/Times}
+
+\begin{verbatim}
+1999-01-01T00:00:29.000Z TAI
+1998-12-31T23:59:58.000Z UTC
+1998-12-31T23:59:57.716Z UT1
+1999-01-01T00:01:01.184Z TT
+
+1999-01-01T00:00:29.500Z TAI
+1998-12-31T23:59:58.500Z UTC
+1998-12-31T23:59:58.216Z UT1
+1999-01-01T00:01:01.684Z TT
+
+1999-01-01T00:00:30.000Z TAI
+1998-12-31T23:59:59.000Z UTC
+1998-12-31T23:59:58.716Z UT1
+1999-01-01T00:01:02.184Z TT
+
+1999-01-01T00:00:30.500Z TAI
+1998-12-31T23:59:59.500Z UTC
+1998-12-31T23:59:59.216Z UT1
+1999-01-01T00:01:02.684Z TT
+
+1999-01-01T00:00:31.000Z TAI
+1998-12-31T23:59:60.000Z UTC
+1998-12-31T23:59:59.716Z UT1
+1999-01-01T00:01:03.184Z TT
+
+1999-01-01T00:00:31.500Z TAI
+1998-12-31T23:59:60.500Z UTC
+1999-01-01T00:00:00.216Z UT1
+1999-01-01T00:01:03.684Z TT
+
+1999-01-01T00:00:32.000Z TAI
+1999-01-01T00:00:00.000Z UTC
+1999-01-01T00:00:00.716Z UT1
+1999-01-01T00:01:04.184Z TT
+
+1999-01-01T00:00:32.500Z TAI
+1999-01-01T00:00:00.500Z UTC
+1999-01-01T00:00:01.216Z UT1
+1999-01-01T00:01:04.684Z TT
+\end{verbatim}
Index: /tags/sj_tags/sj_root_20080929/doc/psphot/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/psphot/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/psphot/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.log *.dvi *.aux *.toc *.log *.out *.lof *.tbr *.tbd *.pdf
Index: /tags/sj_tags/sj_root_20080929/doc/psphot/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/psphot/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/psphot/Makefile	(revision 22322)
@@ -0,0 +1,29 @@
+
+PDFLATEX = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): pdflatex
+PSLATEX  = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): latex
+
+help:
+	@echo "USAGE: make (target)"
+	@echo "  targets: psphot all"
+
+psphot: psphot.pdf 
+all : psphot
+
+%.pdf: %.tex
+	$(PSLATEX) $*.tex 
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	thumbpdf --modes=dvips $*.pdf
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	@rm -f $*.ps $*.dvi $*.aux $*.log $*.tbr $*.tbd $*.toc $*.tpm $*.lof body.tmp head.tmp
+
+clean :
+	$(RM) *.log *.dvi *.aux *.toc *.tbd *.tbr *.tpm *.lof *.out *~ core body.tmp head.tmp
+
+dist : clean
+	$(RM) *.pdf
+
+empty: clean
Index: /tags/sj_tags/sj_root_20080929/doc/psphot/psphot.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/psphot/psphot.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/psphot/psphot.tex	(revision 22322)
@@ -0,0 +1,1271 @@
+\documentclass[panstarrs,spec]{panstarrs}
+
+\title{PSPhot Sofware Design Description} % put in your title
+\subtitle{The Pan-STARRS IPP Object Photometry Tool}
+\author{Eugene Magnier}
+\audience{IPP}
+\shorttitle{PSPhot SDD}
+\group{Pan-STARRS IPP}
+\project{Pan-STARRS IPP}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-021}
+
+\newcommand\ugriz{$u^\prime g^\prime r^\prime i^\prime z^\prime$}
+\newcommand\grizy{$g r i z y$}
+
+\begin{document}
+\maketitle
+
+\section{Introduction}
+
+\subsection{Overview} 
+
+The Institute for Astronomy at the University of Hawaii is developing
+a large optical synoptic survey telescope system, the Panoramic Survey
+Telescope and Rapid Response System (Pan-STARRS). The science goals,
+priorities, top-level concept of operations with associated
+operational requirements, and system performance drivers with
+associated system performance requirements are described in the
+Pan-STARRS Science Goals Statement (SGS).  As described in this
+document, The system conceptual design for Pan-STARRS utilizes an
+array of four 1.8m telescopes each with a 7 degree$^2$ field of view,
+giving the system an \'etendue larger than all existing survey
+instruments combined (defined as the product of the collecting area
+$A$ multiplied by the field-of-view solid angle $\Omega$).  Each
+telescope will be equipped with a 1.4 billion pixel CCD camera with
+low noise and rapid read-out, and the data will be reduced in near
+real time to produce both cumulative static sky and difference images
+from which transient, moving, and variable objects can be
+detected. Pan-STARRS will be able to survey up to $\approx 6,000$
+degree$^{2}$ per night to a detection limit of approximately 24$^{th}$
+magnitude.  This unique combination of sensitivity and sky coverage
+will open up many new possibilities in time domain astronomy including
+a major goal of surveying the Potentially Hazardous Object (PHO)
+population down to a diameter of $\approx 300$ meters.  In addition,
+the Pan-STARRS data will be used to investigate a broad range of
+astronomical problems of extreme current interest concerning the Solar
+System, the Galaxy, and the Cosmos at large.  A prototype single
+telescope system, PS-1, is being developed as a preliminary step
+before construction of the complete four telescope system.
+
+\begin{tabular}{ll}
+Project sponsor:&	AFRL, United States Air Force \\
+Acquirer:       &	University of Hawaii Institute for Astronomy \\
+User: 		&	Astronomical community \\
+Developer:      &	University of Hawaii Institute for Astronomy, participating \\
+                &       institutions, and associated subcontractors	
+\end{tabular}
+
+The Pan-STARRS Image Processing Pipeline is responsible for the basic
+analysis of images from the Pan-STARRS telescopes Gigapixel Camera.
+The overall goals and requirements of the Image Processing Pipeline
+are described in the IPP System/Subsystem Design Description (SSDD;
+PSDC-430-XXX) and the IPP System Requirements Specification (SRS;
+PSDC-430-XXX).  Among the Pan-STARRS project survey goals is a
+repeated all-sky survey in 5 filters, {\it grizy}, beginning with a
+pre-survey with the prototype telescope PS-1.  The photometric and
+astrometric precision goals for the all-sky surveys, as well as the
+other survey components, are quite stringent:
+
+\begin{itemize}
+\item relative photometry: 10 millimagnitudes scatter for bright stars
+across the sky in the internal photometric system; 
+
+\item relative astrometry; 10 milliarcseconds scatter for individual
+stars between repeated images.
+
+\item absolute astrometry: 100 milliarcseconds scatter for all ICRS
+  reference stars (Tycho).
+\end{itemize}
+
+An additional constraint on the Pan-STARRS system comes from the high
+data rate.  The prototype telescope alone is expected to produce
+typically $\sim 700$ GB per night of imaging data.  These images will
+not be limited to high galactic latitudes, so large numbers of
+measurable stars can be expected in much of the data.  The combination
+of the high precision goals of the astrometric and photometric
+measurements and the high data rate (and a finite computing budget)
+mean that the process of detecting, classifying, and measuring the
+astronomical objects in the image data stream will be a significant
+challenge.  
+
+In order to achieve these ambitious goals, the object detection,
+classification, and measurement process must be both precise and
+efficient.  Not only is it necessary to make a careful measurement of
+the flux of individual objects, it is also critical to characterize
+the image point-spread-function, and its variations across the field
+and from image to image.  Since comparisons between images must be
+reliable, the measurements must be stable for both photometry and
+astrometry.
+
+\subsection{Comparable Programs}
+
+A variety of astronomical software packages perform the basic object
+detection, measurement, and classification tasks needed by the
+Pan-STARRS IPP.  Each of these programs have their own advantages and
+disadvantages.  Below we discuss some of the most widely used of these
+other packages, highlighting the features of the programs which are
+particularly desirable, and noting aspects of the programs which are
+problematic for the IPP.
+
+\begin{itemize}
+
+\item DoPhot : analytical fitted model with aperture corrections.
+  pro: well-tested, stable code.  con: limited range of models,
+  algorithm converges slowly to a PSF model, limited tests of PSF
+  validity, inflexible code base, fortran (P. Schechter)
+
+\item DAOPhot : Pixel-map PSF model with analytical component.  pro:
+  well-tested, high-quality photometry.  con: Difficult to use in an
+  automated fashion, does it handle 2D variations well? (P. Stetson)
+
+\item Sextractor : pure aperture measurement with rudimentary
+  object subtraction.  pro: fast, widely used, easy to automate.  con:
+  poor object separation in crowded regions, PSF-modeling is only
+  beta (psfex), what models are available? (E. Bertin)
+
+\item apphot : IRAF-based aperture photometry.  pro: widely used.
+  con: IRAF-based, aperture photometry. (???)
+
+\item galfit : detailed galaxy modeling.  not a multi-object PSF
+  analysis tool.  con: does not provide a PSF model, not easily
+  automated.  very detailed results in very slow processing.  only a
+  galaxy analysis program. (C. Impey)
+
+\item SDSS phot : con: tightly integrated into the SDSS software
+  environment.  (R. Lupton)
+
+\end{itemize}
+
+\note{discussion of these packages is insufficient: flesh out
+  discussion and add in the references.}
+
+\note{Add discussion of the lessons learned from experience with previous
+  analysis programs}
+
+The Pan-STARRS IPP team decided that none of the existing packages met
+all of their needs, particularly given the very challenging goals of
+the project.  We decided to redesign the photometry analysis from
+scratch, using the lessons learned from the existing photometry
+systems.  In the process, the object analysis software would be
+written using the data analysis C-code library written for the IPP,
+\code{psLib}, and the components of the photometry code would be
+integrated into the IPP's mid-level astronomy data analysis toolkit
+called \code{psModules}.  The result is 'PSPhot', which can be used
+either as a stand-alone C program, or as one of the high-level IPP
+components of \code{psModules}, available to programmers either via a
+C interface or through a SWIG interface in Perl (or potentially
+Python).
+
+\section{PSPhot Design Goals}
+
+PSPhot has a number of important requirements that it must meet, and a
+number of design goals which we believe will help to make usable in a
+wide range of circumstances.  The critical requirements of the
+Pan-STARRS IPP which drive the requirements for PSPhot:
+
+\begin{itemize}
+\item {\bf 10 millimagnitude photometric accuracy}.  For PSPhot, this
+  implies that the measured photometry of stellar objects must be
+  substantially better than this 10 mmag since the photometry error
+  per image is combined with an error in the flat-field calibration
+  and an error in measuring the atmospheric effects.  We have set a
+  goal for PSPhot of 3mmag photometric consistency for bright stars
+  between pairs of images obtained in photometric conditions at the
+  same pointing, ie to remove sensitivity to flat-field errors.  This
+  goal splits the difference between the three main contributors and
+  still allows some leeway.  This requirement must be met for
+  well-sampled images and images with only modest undersampling.
+
+\item {\bf 10 milliarcsecond astrometric accuracy}. Relative
+  astrometric calibration depends on the consistency of the individual
+  measurements.  The measurements from PSPhot must be sufficiently
+  representative of the true object position to enable astrometric
+  calibration at the 10mas level.  The error in the individual
+  measurements will be folded together with the errors introduced by
+  the optical system, the effects of seeing, and by the available
+  reference catalogs.  We have set a goal for PSPhot of 5mas
+  consistency between the true source postion and the measured
+  position given reasonable PSF variations under simulations.  This
+  level must be reached for images with 250 mas pixels, implying
+  PSPhot must introduce measurement errors less than 1/50th of a
+  pixel. \note{the choice of F32 parameters places a numerical limit
+  of 1e-7 on the accuracy of a pixel relative to the size of a chip
+  (since a single data value is used for X or Y).  For the $4800^2$
+  GPC chips, this yields a limit of about 0.25 milliarcsecond.}
+
+\item {\bf processing time of 45 seconds} This requirement depends
+  strongly on the hardware organization, the amount of time spent on
+  other analysis steps, the density of stars per image, and the depth
+  for a given type of image.  For the sources at the faint limit (eg,
+  $5\sigma$), the average density of sources is expected to be roughly
+  $3\times10^5$ per square degree, while sources at the 20 $\sigma$
+  level may have densities of $\sim 5\times10^4$ per square degree.
+  Allowing 30 seconds for the PSPhot portion of the analysis, of which
+  15 is used for careful analysis of the brighter sources, 10 seconds
+  is used for PSF modeling and other overheads, and the remaining 5
+  seconds is used for the PSF fitting of the faintest source implies
+  that the detailed modelling may take roughly 3msec per source, and
+  the basic PSF fitting may be allowed 150 usec per source.
+\end{itemize}
+
+The design goals for PSPhot are chosen to make the program flexible,
+general, and able to meet the unknown usages cases future projects may
+require:
+
+\begin{itemize}
+\item {\bf Flexible PSF model} Different image sources require
+  different ways of representing the PSF.  Ideally, both analytical
+  and pixel-based versions should be possible.
+
+\item {\bf PSF spatial variation} Most images result in some spatial
+  PSF variations at a certain level.  The PSF representation should
+  naturally incorporate 2-D variations.
+
+\item {\bf Flexible non-PSF models} PSPhot must be able to represent
+  PSF-like objects as well as non-PSF sources.  It must be easy to add
+  new object models as interesting representations of sources are
+  invented.
+
+\item {\bf Clean code base} PSPhot should incorporate a high-degree of
+  abstraction and encapsulation so that changes to the code structure
+  can be performed without pulling the code apart and starting from scratch.
+
+\item {\bf PSF validity tests} PSPhot should include the ability to
+  choose different types of PSF models for diffent situations, or to
+  provide the user with methods for assessing the different PSF models.
+
+\item {\bf Careful aperture corrections} PSPhot must carefully measure
+  and correct for the photometric and astrometric trends introduced by
+  using analytical PSF models.
+
+\item {\bf User Configurable} PSPhot should allow users to change the
+  options easily and to allow different approaches to the analysis.
+
+\end{itemize}
+
+\section{PSPhot Analysis Process}
+
+\subsection{Overview}
+
+The PSPhot analysis is divided into several major stages:
+
+\begin{itemize}
+\item {\bf Image preparation} Load data, characterize the image
+  background, load or construct noise and mask images.
+
+\item {\bf Initial object detection} Smooth, find peaks, measure basic
+  properties
+
+\item {\bf PSF determination} Select PSF candidates, perform model
+  fits, build PSF model from fits, select best PSF model class.
+
+\item {\bf Bright object analysis} Fit objects with PSFs, determine
+  PSF validity, subtract PSF-like objects, fit non-PSF model(s),
+  select best model class, subtract model.
+
+\item {\bf Low S/N sources} Detect low-level sources, measure
+  properties (aperture or PSF)
+
+\item {\bf Aperture corrections} Measure the curve-of-growth, spatial
+  aperture variations, and background-error corrections.  
+
+\item {\bf Output} Write out objects in selected format, write out
+  difference image, noise image, etc, as selected.
+\end{itemize}
+
+Note that a given run of PSPhot \note{should} allow the user to
+perform any of these stages as an option.  For example, the PSF model
+may already be available from external information, in which case the
+PSF modeling stage can be skipped.  Or, when used as a library
+function, the image may have already been loaded and the mask and
+weight images constructed.  In some implementations, it may be
+possible to skip the initial object detection stage because only
+supplied sources are measured.  These are only some of the possible
+configurations.  The use of these different configurations depends on
+the source of the image, the desired detail and speed of the
+processing, and the level of accuracy desired from the analysis.
+
+\subsection{Image Preparation}
+
+The first step is to prepare the image for detection of the
+astronomical objects.  We need three separate images: the measured
+flux, the corresponding noise level, and a mask defining which pixels
+are valid and which should be ignored.  For the stand-alone program,
+the input flux image is a required program argument.  When it is
+loaded, it is converted by default to 32-bit floating point
+representation.  In the function-call form of PSPhot, the image must
+be supplied by the user in 32-bit floating point format.  The noise
+and mask images may either be provided by the user, or they may be
+automatically generated from the input image, based on
+configuration-defined values for the image gain, read-noise,
+saturation, and so forth.  For the function-call form of the program,
+the flux image is provided in the API, and references to the mask and
+noise are provided in the configuration information.  As in the
+stand-alone C-program, the noise and mask may be constructed
+automatically by PSPhot.
+
+For the mask, we use an 8-bit image in which a value of 0 represents a
+valid pixel.  We use each of the 8 bits to define different reasons a
+pixel should be ignored.  This allows use to optionally respect or
+ignore the mask depending on the circumstance.  For example, in some
+cases, we ignore saturated pixels completely while in other
+circumstances, it may be useful to know the flux value of the
+saturated pixel.  In addition, the mask pixels are used to define the
+pixels available during a model fit, and which should be ignored for
+that specific fit.  The initial mask, if not supplied by the user, is
+constructed by default from the image by applying three rules: 1)
+Pixels which are above a specified saturation level are marked as
+saturated (configuration keyword: \code{SATURATE}).  2) Pixels which
+are below a user-defined value are considered unresponsive and masked
+as dead.  3) Pixels which lie outside of a user-defined window are
+considered non-data pixels (eg, overscan) and are marked as invalid.
+The valid window is defined by the configuration variables
+\code{XMIN}, \code{XMAX}, \code{YMIN}, \code{YMAX}.
+
+\note{Mask values are currently hard-wired numbers.  We need a method
+  for user-defined mask values to be supplied.  PSLib needs to have a
+  mask registration system.}
+
+The noise image, if not supplied is constructed by default from the
+flux image using the configuration supplied values of \code{GAIN} and
+\code{READ_NOISE} to calculate the appropriate Poisson statistics for
+each pixel.  In this case, the image is assumed to represent the
+readout from a single detector, with well-defined gain and read noise
+characteristics.  In some obvious cases, this assumption will not be
+valid.  For example, if the input flux image is the result of an image
+stack with significantly variable number of input measurements per
+pixel, it will necessary to supply a noise image which accurately
+represents the noise as a function of position in the image.
+
+\subsection{Initial Object Detection}
+
+The objects are initially detected by finding the location of local
+peaks in the image.  The flux image is smoothed with a very small
+circularly symmetric kernel using a two-pass 1D Gaussian.  At this
+stage, the goal is only to detect the brighter sources, above a user
+defined S/N limit (configuration keyword: \code{PEAK_NSIGMA}).  The
+detection efficiency for the brighter sources is not strongly
+dependent on the form of this smoothing function.
+
+\note{Is this smoothing needed?  we could save time here by skipping
+it.}
+
+The local peaks in the smoothed image are found by first detecting
+local peaks in each row.  For each peak, the neighboring pixels are
+then examined and the peak is accepted or rejected depending on a set
+of simple rules.  First, any peak which is greater than all 8
+neighboring pixels is kept.  Any peak which is lower than any of the 8
+neighboring pixels is rejected.  Any peak which has the same value as
+any of the other 8 pixels is kept if the pixel $X$ and $Y$ coordinates
+are greater than or equal to the other equal value pixels.  This
+simple rule set means that a flat-topped region will maintain peaks at
+the maximum $X$ and $Y$ corners of the region.
+
+\note{The current implementation ignores the S/N map in making the
+peak detection.  This code must be modified (a la Kaiser) to be used
+for a peak-detection pass in a difference image or to re-find peaks in
+the image after the modeled objects have been subtracted}.
+
+Once a collection of peaks have been identified, basic properties of
+the objects are measured.  First, the local sky flux is measured
+within a square annulus with user-defined dimensions
+(\code{INNER_RADIUS} and \code{OUTER_RADIUS}), using the sample
+median.  This local background value is then used to calculate the
+object first and second moments within a small user-defined aperture
+(\code{MOMENT_RADIUS}).  The first-order moments are a good
+representation of the object position, while the second-order moments
+are a measure of the object shape.  The second-order moments are
+somewhat sensitive to the size of the aperture and the accuracy of the
+background measurement.  The moment calculation is only performed
+using pixels which exceed a S/N of 1.  If, in the process of
+calculating the source moments, the S/N limits reject all but \note{3}
+or fewer of the source pixels, the peak is identified as being
+suspect, and is not used for further analysis.  If the measured
+centroid coordinates differ from the peak coordinates be a large
+amount (\code{MOMENT_RADIUS}), then the peak is again identified as
+being of poor quality and is rejected.  In both of these cases, it is
+likely that the `peak' was identified in a region of flat flux
+distribution or many saturated or edge pixels.
+
+\subsection{PSF Determination}
+
+\subsubsection{PSF Model vs Object Model}
+
+PSPhot uses an analytical model to represent the shape and flux of an
+object.  An important concept within the PSPhot code is the
+distinction between a model which describes an object on an image and
+a model with describes the point-spread-function (PSF) across an
+image.
+
+Any object in an image may be represented by some analytical model,
+for example, a 2-D elliptical Gaussian:
+\begin{eqnarray}
+f(x,y) & = & I_o exp (-z) + S  \\
+    R  & = & \frac{(x - x_o)^2}{2\sigma_x^2} + \frac{(y -
+    y_o)^2}{2\sigma_y^2} + \sigma_{\rm xy}(x - x_o)(y - y_o)
+\end{eqnarray}
+The object model will have a variety of model parameters, in this case
+the centroid coordinates ($x_o, y_o$), the elliptical shape parameters
+($\sigma_x, \sigma_y, \sigma_{\rm xy}$), the model normalization
+($I_o$) and the local value of the background ($S$).  A specific
+object will have a particular set of values for these different
+parameters.
+
+The point-spread-function (PSF) of an image describes the shape of all
+unresolved objects in the image.  In a typical image, the shape of
+point sources is not well described by a single functional form;
+rather, the shape will vary as a function of position in the image.
+The PSF model therefore must describe the parameter variation as a
+function of the position of the object on the image.  Note that the
+object model consists of a certain number of parameters which are
+defined by the PSF model, and another set of parameters which are
+independent from object to object.  For the case of the elliptical
+Gaussian model, the PSF parameters would be the shape terms
+($\sigma_x, \sigma_y, \sigma_{\rm xy}$) while the independent
+parameters would be the centroid, normalization and local sky values
+($x_o, y_o, I_o, S$).  PSPhot uses a 2-D polynomial to specify the
+variation in the PSF parameters as a function of position in the
+image.  In the case of the elliptical Gaussian, this implies that the
+parameters are each a function of the object centroid coordinates:
+\begin{eqnarray}
+\sigma_x    & = & f_1(x,y) \\
+\sigma_y    & = & f_2(x,y) \\
+\sigma_{xy} & = & f_3(x,y) \\
+\end{eqnarray}
+
+PSPhot uses a single structure to represent the object model and
+another structure to represent the PSF model.  The object model
+structure consists of the collection of measured object model
+parameters, carried as a \code{psLib} vector (\code{psVector}) along
+with an equal-length vector with the parameter errors.  The structure
+also includes an integer giving the identifier of the model used in
+the particular case, as well as model fit statistics such as the
+Chi-Square of the fit and the magnitude representation of the ratio
+between the model flux and an aperture flux (see below for more
+details on this value).
+
+The PSPhot representation of the PSF consists of an array of
+polynomials, each representing the variation in the object model PSF
+parameters (\code{psArray} of \code{psPolynomial2D}).  The PSF model
+structure also includes the same integer used to identify which model
+corresponds to particular instance of the PSF.  At the moment, the
+number of PSF parameters is a fixed number (4) fewer than the number
+of parameters of the corresponding object model.  For example, the
+elliptical Gaussian model uses 7 parameters to represent the object and
+3 for the PSF model.  
+
+PSPhot is written so that the object detection, measurement, and
+classification code does not depend on the specific form of the
+available object model functions.  Access to the characteristics of
+the models is provided through a simple function abstraction method.
+Throughout PSPhot, there are many places where it is necessary for the
+code to refer to an aspect of the object or PSF model.  Often, these
+quantities are needed deep within other parts of the code.  For
+example, when attempting to fit the pixel flux values for an object,
+it is necessary to generate a guess for the model parameters.  Or, in
+order to limit the domain of the fit, it is necessary to determine an
+isophotal radius for a model.  
+
+In order to avoid having the code depend on the specific form of a
+model, the function calls needed in these types of circumstances are
+abstracted, and a method is provided to return the necessary function
+to the higher-level software.  For example, each model type has its
+own function to define an initial guess for the model, or a function
+to determine the radius for a given flux level.  These are then
+registered as part of the model function code.  Another function is
+then used to return the appropriate function for a specific model
+type.  For example, the \code{psModelLookup_GetFunction} will return
+the \code{psModelLookup} function for a given model type.  This
+mechanism makes it very easy to add new model functions into the
+PSPhot code base.  To add a new model function, the programmer simply
+defines a new model name (a string), the set of all necessary model
+lookup functions, and places the reference to the model code at the
+appropriate location in the psModelInit.c routine.
+
+When a new model is provided to PSPhot, it is not necessary to specify
+the intended use of the object model function (ie, PSF-like object,
+galaxy, comet, etc).  Any model can be used for the PSF model, or to
+describe the flux distributions of the non-PSF objects.  The code
+currently uses a fixed translation between the object model parameters
+and the PSF model parameters.  It also defines a specific order for
+the 4 independent parameters.  
+
+\note{the code may also require that two of the PSF-like parameters
+represent the shape in some way}.
+
+\subsubsection{PSF Candidate Object Selection}
+
+The first stage of determining the PSF model for an image is to
+identify a collection of objects in the image which are {\em likely}
+to be PSF-like.  PSPhot uses the object moments to make the initial
+guess at a collection of PSF-like objects.  At this point, the program
+has measured the second order moments for all objects identified by
+their peaks, as well as an approximate signal-to-noise ratio.  All
+objects with a S/N ratio greater than a user-defined parameter
+(\code{PSF_SHAPE_NSIGMA} ???) are selected by PSPhot, though objects
+which have more than a certain number of saturated pixels are excluded
+at this stage.  PSPhot then examines the 2-D plane of $\sigma_x,
+\sigma_y$ in search of a concentrated clump of objects.  To do this,
+it constructs an artificial image with pixels representing the value
+of $\sigma_x, \sigma_y$, using a user-defined scale for the size of a
+pixel in this artificial image (note that the units of the $\sigma_x,
+\sigma_y$ plane are the size of the second-moment in pixels in the
+original image).  A typical value for the bin size is approximately
+0.1 image pixels.  The binned $\sigma_x, \sigma_y$ plane is then
+examined to find a peak which has a significance greater than XXX.
+Unless the image is extremely sparse, such a peak will be well-defined
+and should represent the objects which are all very similar in shape.
+Other objects in the image will tend to land in very different
+locations, failing to produce a single peak.  To avoid detecting a
+peak from the unresolved cosmic rays, objects which have
+second-moments very close to 0 are ignored.  The only danger is if the
+PSF is very small and too many of these objects are rejected as cosmic
+rays.
+
+Once a peak has been detected in this plane, the centroid and second
+moments of this peak are measured.  All objects which land within XXX
+$\sigma$ of this centroid are selected as likely PSF-like objects in
+the image.  
+
+\subsubsection{PSF Candidate Object Model Fits}
+
+All candidate PSF objects are then fitted with the selected object
+model, allowing all of the parameters (PSF and independent) to vary in
+the fit.  PSPhot uses the Levenberg-Marqardt process for the
+non-linear fitting.  Non-linear fitting can be very computationally
+intensive, particularly for if the starting parameters are far from
+the minimization values.  PSPhot uses the first and second moments to
+make a good guess for the centroid and shape parameters for the PSF
+models.  In order to minimize the impact of close neighbors, the noise
+values used in the fit are enhanced by a fraction of the deviation of
+the particular pixel value from the model guess.  Any objects which
+fail to converge in the fit are flagged as invalid.
+
+\note{does the noise enhancement introduce too much bias?}
+
+\note{discuss the convergence criteria, model parameter guesses}
+
+For the resulting collection of object model parameters, the
+PSF-dependent parameters of the models are all fitted as a function of
+position to a 2-D polynomial.  The order of this polynomial is (should
+be?) a user-defined parameter.  The fitting process for these
+polynomials is iterative, and rejects the $3-\sigma$ outliers in each
+of three passes.  This fitting technique results in a robust
+measurement of the variation of the PSF model parameters as a function
+of position without being excessively biased by individual objects
+which fail drastically.  Objects whose model parameters are rejected
+by this iterative fitting technique are also marked as invalid and
+ignored in the later PSF model fitting stages.
+
+All of the PSF-candidate objects are then re-fitted using the PSF
+model to specify the dependent model parameter values for each object.
+For example, in the case of the elliptical Gaussian model, the shape
+parameters ($\sigma_x, \sigma_y, \sigma_{xy}$) for each object are
+set by the coordinates of the object centroid and fixed (not allowed
+to vary) in the fitting procedure.  The resulting fitted models are
+then used to determine a metric which tests the quality of the PSF
+model for this particular image.  
+
+The metric used by PSPhot to assess the PSF model is the scatter in
+the differences between the aperture and fit magnitudes for the PSF
+objects.  The difference between the aperture and fit magnitudes ({\em
+ApResid}) is a critical parameter for any PSF modeling software which
+uses an analytical model to represent the flux distribution of the
+objects in an image.  An approximate correction is measured here, with
+a more detailed correction measured after all object analysis is
+performed.  The PSF model with the best consistency of the aperture
+correction is judged to be the best model.
+
+\subsubsection{Basic Deblending}
+
+The collection of identified peaks is examined to find peaks which are
+'blended', that is, they are close enough together to make the
+analysis of one of the sources difficult if performed in isolation.
+Saturated stars also result in additional peaks which are likely to be
+invalid; it is useful to restrict a saturated star to a single primary
+position with associated neighboring peaks.
+
+The deblending process first searches for any peaks which are within
+the image cell of another peak.  All such groups are examined,
+starting with the brightest source.  An isophot is found about the
+primary peak which is at least \code{DEBLEND_SKY_NSIGMA} times the sky
+sigma above the local background and which is otherwise
+\code{DEBLEND_PEAK_FRACTION} of the primary peak central pixel flux.
+Any secondary sources which are contained within this isophot are
+considered to be blended peaks associated with the primary peak.  
+
+\subsection{Bright Source Analysis}
+
+After a PSF model has been determined, PSPhot performs the analysis of
+the bright objects in the image.  In this stage, all of the objects
+with an estimated signal to noise (based on the moments analysis)
+greater than a user-set threshold are analysed and subtracted from the
+image.  An optional successive stage then finds fainter sources and
+measures them as well (see Faint Source Analysis,
+Section~\ref{faintsources}).  In the bright source analysis stage, two
+major varients are available.  In the primary version, all objects are
+examined (in decending order of brightness) and an appropriate models
+is determined for each object which is then subtracted; in the
+alternate version, the objects are examined (in decending order of
+brightness) and the PSF-like objects subtracted first, then the
+extended objects are analysed on a second pass.
+
+\subsubsection{Fast Ensemble PSF Fitting}
+
+Before the detailed analysis of the objects is performed, it is
+convenient to subtract off all of the sources, at least as well as
+possible at this stage.  We make the assumption that all sources are
+PSF-like.  We also assume their position can be taken as the peak of a
+2D quadratic function fitted to the peak pixel and its surrounding 8
+pixels.  A single linear fit is used to simultaneously measure all
+source fluxes.  Since the local sky has been subtracted, this
+measurement assumes the local sky is zero.  
+
+\[
+\chi^2 = \sum_{\rm pixels} (F_{x,y} - \sum_{\rm sources} A_i PSF[x,y])^2
+\]
+
+Minimizing this equation with respect to each of the $A_i$ values
+results in a matrix equation:
+\[ M_{i,j} \bar{A_i} = \bar{F_j}\]
+where $\bar{A_i}$ is the vector of $A_i$ values, the elements of
+$M_{i,j}$ consist of the dot product of the unit-flux PSF for source
+$i$ and source $i$, and $\bar{F_j}$ is the dot product of the
+unit-flux PSF for source $i$ with the pixels corresponding to source
+$i$.  The dot products are calculated only using pixels within the
+source apertures.  Since most sources have no overlap with most other
+sources, this matrix equation results in a very sparse, mostly
+diagonal square matrix.  The dimension is the number of sources,
+likely to be 1000s or 10,000s.  Such a matrix does not lend itself to
+direct inversion.  However, an interative solution quickly yields a
+result with sufficient accuracy.  In the iterative solution, a guess
+at the solution is made; the guess is multiplied by the matrix, and
+the result compared with the observed vector $\bar{F_j}$.  The
+difference is used to modify the initial guess. This proces is
+repeated several times to achieve a good convergence.  
+
+Once a solution set for $A_i$ is found, all of the objects are
+subtracted from the by applying these values to the unit-flux PSF.
+
+\subsubsection{PSF Model applied to detected objects}
+
+Once a PSF model has been selected for an image, PSPhot attempts to
+fit all of the detected objects, above a user-defined signal-to-noise
+ratio (\note{KEYWORD}) with the PSF model.  For these fits, the
+dependent parameters are fixed by the PSF model and only the 4
+independent object model parameters are allowed to vary in the fit.
+PSPhot again uses the Levenberg-Marqardt process for the non-linear
+fitting.  The objects are fitted in their S/N order, starting with the
+brightest and working down to the user-specified limit.
+
+Once a solution has been achieved, PSPhot attempts to judge the
+quality of the PSF model as a representation of the object shape.  To
+do this, it calculates the next step of the minimization {\em allowing
+the shape parameters to vary}.  This step, essentially the
+Gauss-Newton minimization distance from the current local minimum,
+should be very small if the object is well represented by the PSF, but
+large if the PSF is not a good representation of the object flux.  The
+model quality is judged by the change in the two shape parameters
+which represent the 2D size of the object.  For the case of the
+elliptical Gaussian, these two parameters are $\sigma_x$ and
+$\sigma_y$.  For a generic model, the shape parameters may be defined
+differently, but the should always be two parameters which scale the
+object size in two dimensions (what about a polar-coordinate form?)
+Currently, PSPhot requires the two relevant shape parameters to be the
+first two dependent parameters in the list of model parameters (ie,
+parameters 4 \& 5).
+
+The expected distribution of the variation of the two shape parameters
+will be a function of the signal-to-noise of the object in question
+and the value of the shape parameter itself.  The expected standard
+deviation on the shape parameter is, eg, $\sigma_x / {\rm SN}$.  If
+the object is well-represented by the PSF, then the shape parameter
+values should be close to their minimization value.  We can thus ask,
+for each object, given the measured amplitude of the Gauss-Newton
+step, how many standard deviations from the expected value (of 0.0) is
+this particular value?  Objects for which the variation in the shape
+parameters is a large positive number of standard deviations are
+likely to be better represented by a larger flux distribution than the
+PSF (eg, a Galaxy or Comet, etc).  Objects for which the variation in
+the shape parameters is a large negative number of standard deviations
+are likely to be better represented by a smaller flux distribution
+than the PSF (ie, a cosmic ray or other defect).  A user-defined
+number of standard deviations is used to select these two cases, and
+to flag the object as a likely galaxy (really meaning 'extended') or
+as a likely defect.  
+
+At this stage of the analysis, PSPhot uses two additional indicators
+to identify good and poor PSF fits.  The first of these is the
+signal-to-noise ratio.  It is possible for the peak finding algorithm
+to identify peaks in locations which are not actually a normal peak.
+Some of these cases are in the edges of saturated, bleeding columns
+from bright stars, in the nearly flat halos of very bright stars, and
+so on.  In these cases, a local peak exists, with a lower nearby sky
+region.  However, the fitted PSF model cannot converge on the peak
+because it is very poorly defined (perhaps only existing in the
+smoothed image).  The fit can either fail to converge or it can
+converge on a fit with very low or negative peak flux / flux
+normalization.  PSPhot will flag any non-convergent PSF fit and any
+object with PSF S/N ratio lower than a user-defined cutoff.  It is
+also useful to identify very poor fits by setting a maximum Chi-Square
+cutoff for objects.  
+
+As the objects are fitted to the PSF model, those which survive the
+exclusion stage are subtracted from the image.  The subtraction
+process modifies the image pixels (removing the fitted flux, though
+not the locally fitted background) but does not modify the mask or the
+noise images.  The signal-to-noise ratio in the image after
+subtraction represents the significance of the remaining flux.  If the
+subtractions are sufficiently accurate models of the PSF flux
+distribution, the remaining flux should be below 1 $\sigma$
+significance.  In practice the cores of bright stars are poorly
+represented and may have larger residual significance. \note{in future
+work, we may choose to enhance the noise to minimize detection of
+objects in the residuals of brighter objects}.
+
+\subsubsection{Blended Sources}
+
+Sources which are blended with other sources are fitted together as a set of
+PSFs.  A single multi-object fit is performed on all blended peaks.
+The resulting fits are evaluated independently and any which are
+determined to be PSFs are subtracted from the image.
+
+\subsubsection{Double Sources}
+
+Sources which are judged to be non-PSF-like are confronted with two
+possible alternative choices.  First, the object is fitted with a
+double-source model.  In this pass, the assumption is made that there
+are two neighboring sources, but the peaks are blended together, or
+otherwise not distinguished.  The initial guess for the two peaks is
+made by splitting the flux of the single source in half and locating
+the two starting peaks at +/- 2 pixels from the original peak along
+the direction of the semi-major axis of the sources, as measured from
+the second moments.  In order for the two-source model to be accepted,
+both sources must be judged as a valid PSF source.  Otherwise, the
+double-PSF model is rejected and the source is fitted with the
+available non-PSF model or models.
+
+\note{better description of the acceptance criteria; the FLT model is
+  tried before the DBL is accepted or rejected}. 
+
+\subsubsection{Non-PSF Objects}
+
+Once every object (above the S/N cutoff) has been confronted with the
+PSF model, the objects which are thought to be galaxies (extended) can
+now be fit with appropriate models for the galaxies (or other likely
+extended shapes).  Again, the fitting stage starts with the brightest
+sources (as judged by the rough S/N measured from the moments
+aperture) and working to a user defined S/N limit.  
+
+PSPhot will use the user-selected galaxy model to attempt the galaxy
+model fits.  In the configuration system, the keyword \code{GAL_MODEL}
+is set to the model of interest.  All suspected extended objects are
+fitted with the model, allowing all of the parameters to float.  The
+initial parameter guesses are critical here to achieving convergence
+on the model fits in a reasonable time.  The moments and the pixel
+flux distribution are used to make the initial parameter guess.  Many
+of the object parameters can be accurately guessed from the first and
+second moments.  The power-law slope can be guessed by measuring the
+isophotal level at two elliptical radii and comparing the ratio to
+that expected.
+
+For each of the galaxy models (in fact for all object models), a
+function is defined which examines the fit results and determines if
+the fit can be consider as a success or a failure.  The exact criteria
+for this decision will depend on the details of the model, and so this
+level of abstraction is needed.  For example, in some case, the range
+of valid values for each of the parameters must be considered in the
+fit assessment.  In other cases, we may choose to use only the
+parameter errors and the fit Chi-Square value.
+
+All galaxy model fits which are successful are then subtracted from
+the image as is done for the successful PSF model fits.  Of course,
+the background flux is retained, with the result that only the object
+is subtracted from the image.  Again, the noise image is (currently)
+not modified.  
+
+\note{we have no code yet to select the best of several models for a
+  given objects.  The relative value of the Chi-Square is the obvious
+  test in this case}.
+
+\subsection{Faint Sources}
+
+\note{this is not done : should use the ensemble PSF fitting to fit
+  just the new significant peaks}
+
+After a first pass through the image, in which the brighter sources
+above a high threshold level have been detected, measured, and
+subtracted, PSPhot optionally begins a second pass at the image.  In
+this stage, the new peaks are detected on the image with the bright
+objects subtracted.  In this pass, the peak detection process uses the
+noise image to test the validity of the individual peaks.  All peaks
+with a significance greater than a user-defined minimum threshold are
+accepted as objects of potential interest.  
+
+The objects which are measured in this faint-object stage are clearly
+low significance detections.  A typical threshold for the bright
+object analysis would S/N of 5 - 10.  The lower limit cutoff for the
+faint object analysis would typically be S/N of 2 - 4.  In this stage,
+PSPhot can perform one of three types of analysis.  The difference
+between these options is one of speed vs detail.
+
+In the first option, PSPhot can repeat the analysis described above in
+sections XXX and XXX, performing a PSF fit followed by a non-PSF fit
+to the objects which are not PSF-like, and subtracting them.  The
+advantage of this option is that the faint objects are treated
+identically to the bright objects, and all potential parameters are
+measured, even for marginally extended sources.  The disadvantage of
+this option is that the low signal-to-noise of the objects in this
+stage limits the amount of information which can be extracted from
+them.  The marginal gain may not be worth the large expense of
+processing time.
+
+In the second option, PSPhot can perform only the PSF model fit to the
+remaining peaks, but ignore any further questions of the shape of the
+objects.  The advantage of this option is that it is substantially
+faster than performing the more complex (and less stable)
+multi-parameter non-linear fits on all faint objects.  On the
+downside, less information is learned about the objects.
+
+Finally, PSPhot can simply ignore the fitting process and instead
+glean information about the fainter sources on the basis of the peak
+characteristics.  In this option, the image is smoothed with the PSF
+model, and the peak for each object is measured.  The peak flux and
+the local peak curvature theoretically give sufficient information to
+recover the object flux, the centroid coordinates, and the centroid
+errors.  The advantage of the stage is speed, especially for the very
+faintest levels: if the lower limit is not sufficiently faint, the
+time spent in performing the smoothing (3 FFTs) cannot make up for the
+time which would have been spent applying the PSF model to the peaks.
+The downside of this method is an increased sensitivity to the local
+sky model (the local sky must be correctly subtracted) and fewer
+constraints on the quality of the detection (no Chi-Square is
+measured, for example).
+
+\note{In the ideal case, if we were only interested in detecting PSFs,
+and we had a good model for the PSF, we could optimally find the
+sources by smoothing the image and the noise image with the PSF model.
+\em write out the description of Nick's optimal PSF finding}.
+
+PSPhot allows the user to select between these three options for the
+analysis of the faint sources.  Three separate user-controlled
+signal-to-noise ratio limits are defined.  One specifies the depth to
+which the PSF / non-PSF analysis is performed.  A second (which must
+be smaller) specifies the depth to which only the PSF is fitted.  A
+third specifies the depth to which the analysis is performed using on
+the peak statistics.  If two of these are identical, then certain of
+these options are simply skipped.  For example, if the peak analysis
+threshold is set to the same value as the PSF-only threshold, no peak
+analysis is performed.
+
+\subsection{Aperture Correction Measurement}
+
+The important concept here is that an analytical model will always
+fail to describe the flux of the objects at some level.  In the end,
+all astronomical photometry is in some sense a relative measurement
+between two images.  Whether the goal is calibration of a science
+image taken at one location to a standard star image at another
+location, or the goal is simply the repetitive photometry of the same
+star at the same location in the image, it is always necessary to
+compare the photometry between two images.  If this measurement is to
+be consistent, then the measurement must represent the flux of the
+stars in the same way regardless of the conditions under which the
+images were taken, at least within some range of normal image
+conditions.  So, for example, two images with different image quality,
+or with different tracking and focus errors, will have different PSF
+models.  Since an analytical model will always fail to represent the
+flux of the star at some level, the measured flux of the same object
+in the two images will be different (even assuming all other
+atmospheric and instrumental effects have been corrected).  The
+amplitude of the error will by determined by how inconsistently the
+models represent the actual object flux.  For example, if the first
+image PSF model flux is consistently 10\% too low and the second is 5\%
+too high, then the comparison between the two images will be in error
+by 15\%.  
+
+Aperture photometry avoids these problems, by trading for other
+difficulties.  In aperture photometry, if a large enough aperture is
+chosen, the amount of flux which is lost will be a small fraction of
+the total object flux.  Even more importantly, as the image conditions
+change, the amount lost will change by an even smaller fraction, at
+least for a large aperture.  This can be seen by the fact that the
+dominant variations in the image quality are in the focus, tracking
+and seeing.  All of these errors initially affect the cores of the
+stellar images, rather than the wide wings.  The wide wings are
+largely dominated by scattering in the optics and scattering in the
+atmosphere.  The amplitude and distribution of these two scattering
+functions do not change significantly or quickly for a single
+telescope and site.  
+
+The difficulty for aperture photometry is the need to make an accurate
+measurement of the local background for each object.  As the aperture
+grows, errors in the measurement of the sky flux start to become
+dominant.  If the aperture is too small, then variation in the image
+quality are dominant.  The brighter is the object, the smaller is the
+error introduced by the large size of the aperture.  However, the
+number of very bright stars is limited in any image, and of course the
+brighter stars are more likely to suffer from non-linearity or
+saturation.  
+
+\note{this discussion sucks: put in some more details of my point:
+  amplitude of systematic vs random sky errors}
+
+How important is this effect?  Consider a typical bright object with a
+flux of (say) 40,000 counts in an image of background 1000 counts per
+pixel, with FWHM of 4 pixels.  In principle, the flux of this object
+should be measurable with an accuracy of roughly 0.57\%
+($\frac{\sqrt{40000 + 1000 \times 12}}{40000}$).  However, the
+measurement of the sky is limited at some finite level by Poisson
+statistics.  If we are required to use an aperture of (say) 25 pixels
+in radius (eg, 5 arcseconds for an 0.2 arcsec / pixel detector), and
+we have an annulus of twice this radius to measure the local sky, then
+we will have an error of XXX.
+
+\note{outline the variation of {\em ApResid} as a function of
+magnitude}.
+
+PSPhot measures the aperture correction ({\em ApResid}) for every PSF
+candidate object, then calculates the trend of this correction as a
+function of the magnitude.  This trend is fitted with a line.  The
+resulting function can be used to determine the effective aperture
+correction for an infinite flux object and the average bias inherent
+in the sky measurement for the image.  The scatter of the
+PSF-candidate object measurements about this trend is a measure of how
+well we can measure photometry from the image by applying the specific
+PSF model.  The slope of this trend is a measure of the bias in the
+local sky measurment for each object.  In principal, the measured sky
+levels could be modified by this bias.  More generally, the measured
+bias in a collection of images could be used to improve the model
+fitting or sky fitting portion of the software the remove the bias
+term.
+
+PSPhot allows a collection of PSF model functions to be tried on all
+PSF candidate objects.  For each model test, the above corrected
+ApResid scatter is measured.  The PSF model function with the smallest
+value for the ApResid scatter is then used by PSPhot as the best PSF
+model for this image.  The number of models to be tested is specified
+by the configuration keyword \code{PSF_MODEL_N}.  The configuration
+variables \code{PSF_MODEL_0}, \code{PSF_MODEL_1}, through
+\code{PSF_MODEL_N - 1} specify the names of the models which should be
+tested.
+
+\subsubsection{Types of Object / PSF models currently available}
+
+\note{the discussion of the model types needs to be extended}
+
+\begin{itemize}
+\item GAUSS  : Pure elliptical Gaussian
+\item PGAUSS : polynomial approximation to a Gaussian (PGAUSS)
+\item QGAUSS : power law with variable exponential term
+\item SGAUSS : 
+\end{itemize}
+
+\note{discuss the stability issues with the galaxy model(s)}
+
+\subsection{Output Options}
+
+\note{need to discuss tests}
+
+\note{need to discuss failings and holes}
+
+\section{Alternative Scenarios}
+
+\subsection{Trailed Sources}
+
+\subsection{Fixed / Known-position Sources}
+
+\subsection{Difference Images}
+
+\note{much of this discussion is theoretical: PSPhot can incorporate
+  these modifications, but it currently does not.}
+
+The noise map for a difference image must be generated from the two
+images use to construct the difference.  Otherwise, the low sky level
+will automatically result in inconsistent interpretation of the noise.
+
+For a difference image, both positive and negative objects will be
+present.  The basic peak detection algorithm will only trigger for the
+positive sources.  One solution is to simply apply PSPhot to both the
+difference image and its negative value.  \note{do we want to code in
+an automatic switch to get both positive and negative excursions in
+the single pass?}.
+
+In the case of a difference image, the PSF model construction stage
+will probably fail for lack of valid sources.  It is better in these
+cases to provide PSF model from some other source.  For example, the
+two images which are combined to generate the difference image
+represent the PSF.  Presumably, one or both have been convolved with a
+PSF-matching kernel.  The images which result from the convolution
+should be used to measure the PSF model.  
+
+The object classification scheme defaults to the galaxy models for
+objects which are not well represented by the PSF model.  In a
+properly-constructed difference image, galaxies are unlikely to remain
+behind as significant sources.  Most real objects in the difference
+image will be PSF-like and will consist of photometrically variable
+objects (flare stars, supernovae, etc) or astrometrically variable
+objects (high-proper motion stars or solar-system objects).  There are
+three likely classes of objects which will not be well represented by
+the PSF model.  1) Fast-moving solar-system objects will appear as
+short streaks.  For example, a fast solar system object would have an
+apparent rate of 0.5 degrees per hour, translating to 15 arcseconds in
+a 30 second exposure.  Even a main belt asteroid at roughly 1 AU would
+have reflect motion of approximately 1 degree per day, equivalent to
+1.25 arcsec in a 30 second exposure, and could be noticeably smeared
+and non-PSF-like.  A trailed-star model can be used to characterize
+these types of objects.  2) Small offset stars, either due to
+atmospheric / color effects or modest proper motion will appear as PSF
+dipoles in the difference images.  The positive and the negative
+images will have stellar profiles, but they will be significantly
+offset and will not subtract well.  The two components may not have
+the same amplitude.  A PSF-dipole model can be used to fit these types
+of objects, with free parameters of the two centroids and the two
+fluxes.  3) Comets will appear in the difference images as a non-PSF
+objects.  Their 2-D structure includes both the flux from the coma
+(with a typical power-law profile) and flux from the tail (with a more
+complex flux distribution).  A comet flux model can be used to
+characterize these objects in difference images.  A major difficulty
+in applying these three types of models is in making a robust test of
+which model should be used.  This problem is akin to the issue of
+selecting and distinguishing between multiple galaxy models, as
+discussed in the section on Galaxy models.
+
+\section{PSPhot Structures and Data Elements}
+
+The following structures are described in detail in the document
+`Pan-STARRS PS-1 Image Processing Pipeline Modules Supplementary
+Design Requirements' (psModules SDRS; PSDC-430-012).
+
+\begin{datatype}
+ pmModel
+ pmModelGroup
+ pmGrowthCurve
+ pmPSF
+ pmPSFTry
+ pmSource
+ pmPeak
+ pmMoments
+\end{datatype}
+
+\note{psphot is supposed to operate on individual readouts, and use
+  the techniques used by ppImage to extract header-related metadata.
+  currently, psphot uses an alternative to the psReadout until the
+  ppImage code can be folded together with psphot}. 
+
+\subsection{Top-Level APIs}
+
+\begin{prototype}
+psMetadata     *psphotArguments (int *argc, char **argv);
+\end{prototype}
+Load the command-line arguments, parse the configuration file, and
+place the configuration information on a single metadata structure.
+This function searches for the following command line option flags,
+and places their corresponding values on the output metadata with the
+given name.  These options override any such values in the
+configuration file.
+\begin{verbatim}
+-mask (filename)      : MASK_IMAGE
+-weight (filename)    : WEIGHT_IMAGE
+-resid (filename)     : RESID_IMAGE
+-region [x0:x1,y0:y1] : ANALYSIS_REGIONP
+-photcode (code)      : PHOTCODE
+-psf (filename)       : PSF_INPUT_FILE
+-modeltest x y        : TEST_FIT_X, TEST_FIT_Y
+-model (name)         : TEST_FIT_MODEL
+-fitmode (name)       : TEST_FIT_MODE
+-fitset (name)        : TEST_FIT_SET
+\end{verbatim}
+
+The following option flags can be used to set any option:
+\begin{verbatim}
+-D  (key) (value)      : any string value
+-Df (key) (value)      : any F32 value
+-Di (key) (value)      : any S32 value
+\end{verbatim}
+
+The function next examines the remaining command-line arguments and
+complains if there are not exactly 3 arguments, reporting the program
+usage.  It sets default configuration variables, and then loads the
+configuration file specified as the third command-line option.
+Finally, it sets the \code{IMAGE} and \code{OUTPUT_FILE} config
+options to arguments 1 and 2, respecitively.
+
+\begin{prototype}
+eamReadout     *psphotSetup (psMetadata *config);
+\end{prototype}
+This function examines the configuration data in \code{config} and
+loads the image into memory.  It constructs the weight and mask images
+if they have not been specified, or loads the specified images.  The
+weight image is built based on the read noise and gain of the image,
+as extracted from the header or from the configuration options
+directly.  It defines the mask based on the selection image region,
+the values for saturation and the \code{min_VALID_PIXEL}.  
+
+\begin{prototype}
+bool            psphotModelTest (eamReadout *imdata, psMetadata *config);
+\end{prototype}
+This function is an optional test mode for psphot.  If the test mode
+has been selected, this function will attempt to fit a single object
+with the requested model.  It writes out subimage containing the
+source, the difference, the mask, and the weight.  This function may
+load a PSF model or fit a floating model.
+
+\begin{prototype}
+psStats        *psphotImageStats (eamReadout *imdata, psMetadata *config);
+\end{prototype}
+Measure the basic image properties: median sky, expected sky sigma
+
+\begin{prototype}
+psPolynomial2D *psphotImageBackground (eamReadout *imdata, psMetadata *config, psStats *sky);
+\end{prototype}
+Model the image background as a 2D polynomial and subtract from the
+image.   The should use a more sophisticated model and return the
+subtracted image.
+
+\begin{prototype}
+psArray        *psphotFindPeaks (eamReadout *imdata, psMetadata *config, psStats *sky);
+\end{prototype}
+Create a smoothed image and find all local peaks above the threshold
+level (uses: \code{PEAKS_SMOOTH_SIGMA, PEAKS_SMOOTH_NSIGMA,
+PEAKS_NSIGMA_LIMIT, PEAKS_OUTPUT_FILE})
+
+\begin{prototype}
+psArray        *psphotSourceStats (eamReadout *imdata, psMetadata *config, psArray *allpeaks);
+\end{prototype}
+Create the basic source structures for all peaks, define the initial
+pixels, measure the local sky (sky offset) and the source moments.
+
+\begin{prototype}
+bool            psphotRoughClass (psArray *sources, psMetadata *config);
+\end{prototype}
+Find the PSF clump and make the first cut source identifications
+
+\begin{prototype}
+bool            psphotBasicDeblend (psArray *sources, psMetadata *config, psStats *sky);
+\end{prototype}
+Find all blended peaks and tag, group with single primary source.
+
+\begin{prototype}
+pmPSF          *psphotChoosePSF (psMetadata *config, psArray *sources, psStats *sky);
+\end{prototype}
+Try each of the selected PSF models on a subset of likely PSF stars.
+Measure the metric (aperture residual scatter) for each PSF model and
+choose the best model.  
+
+\begin{prototype}
+bool 	        psphotEnsemblePSF (eamReadout *imdata, psMetadata *config, psArray *sources, pmPSF *psf, psStats *sky);
+\end{prototype}
+Perform simultaneous fitting to all sources in the array using a
+linear fitting process which assumes all sources are PSFs and their
+positions are fixed.  Set the positions based on the bilinear
+interpolation of the peak implied by the 3x3 square of pixels
+containing the peak.  Local sky is also assumed to be correctly subtracted.
+
+\begin{prototype}
+bool            psphotFullFit (eamReadout *imdata, psMetadata *config, psArray *sources, pmPSF *psf, psStats *sky);
+\end{prototype}
+Fit all sources in sequence starting from the brightest, and
+subtracting the sources as they are fitted.  This function only
+attempts single PSF and single EXT models and chooses between them.
+The sources are assumed to have been subtracted in advance (ie, using
+psphotEnsembleFit).  The models which do not succeed are re-subtracted
+using the prior model.
+
+\begin{prototype}
+bool            psphotBlendFit (eamReadout *imdata, psMetadata *config, psArray *sources, pmPSF *psf, psStats *sky);
+\end{prototype}
+Fit all sources in sequence starting from the brightest, and
+subtracting the sources as they are fitted.  This function attempts a
+multi-source fit for blended sources, or a single PSF if it is not a
+blend, followed by both EXT and DBL models and chooses between them.
+The sources are assumed to have been subtracted in advance (ie, using
+psphotEnsembleFit).  The models which do not succeed are re-subtracted
+using the prior model.
+
+\begin{prototype}
+bool            psphotReplaceUnfit (psArray *sources);
+\end{prototype}
+After models have been attempted for all sources, this function
+replaces the sources which were temporarily subtracted, but which did
+not succeed or converge on a good solution.
+
+\begin{prototype}
+bool            psphotApplyPSF (eamReadout *imdata, psMetadata *config, psArray *sources, pmPSF *psf, psStats *sky);
+\end{prototype}
+Attempt to fit the PSF model to all sources in brightness order,
+subtracting the resulting model if successful.  Only attempts single
+PSF models.   
+
+\begin{prototype}
+bool            psphotFitExtended (eamReadout *imdata, psMetadata *config, psArray *sources, psStats *skyStats);
+\end{prototype}
+Attempt to fit the PSF model to all sources in brightness order,
+subtracting the resulting model if successful.  Only attempts single
+EXT models.
+
+\begin{prototype}
+bool            psphotApResid (eamReadout *imdata, psArray *sources, psMetadata *config, pmPSF *psf);
+ \end{prototype}
+Measure the curve-of-growth and the aperture correction trend.
+
+\begin{prototype}
+void            psphotOutput (eamReadout *imdata, psMetadata *config, psArray *sources, pmPSF *psf, psStats *sky);
+\end{prototype}
+Write out data in various formats as selected.
+
+\section{User's Guide}
+
+\subsection{Configuration Parameters}
+
+\begin{verbatim}
+FAINT_SN_LIM
+FIT_MAX_CHI
+FIT_MIN_SN
+FIT_NSIGMA
+FIT_PADDING
+FIT_RADIUS
+GAIN
+GAL_MODEL
+GAL_MOMENTS_RADIUS
+INNER_RADIUS
+INPUT
+MASK
+NOISE
+NSUBSET
+OUTER_RADIUS
+OUTPUT
+OUTPUT_MODE
+PEAK_NSIGMA
+PSF_MODEL_N
+PSF_MOMENTS_RADIUS
+PSF_SHAPE_NSIGMA
+RDNOISE
+SATURATE
+SMOOTH_NSIGMA
+SMOOTH_SIGMA
+XMAX
+XMIN
+YMAX
+YMIN
+\end{verbatim}
+
+\subsection{Command-Line Arguments and Options}
+
+\subsection{Input \& Output Data Formats} 
+
+\section{Sample Tests}
+
+\section{Further Work to be Completed}
+
+\begin{itemize}
+\item convert to pmCell as input data
+\item loop over all readouts in a pmCell
+\item write out multiple files?
+\item better method for defining the recipe?
+\item additional options for image background
+\item image background should return a background image
+\end{itemize}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/psphot/psphot.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/psphot/psphot.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/psphot/psphot.txt	(revision 22322)
@@ -0,0 +1,70 @@
+
+psphot description article (outline)
+
+- background
+
+  - discuss dophot, sextractor, daophot
+  - other photometry solutions?
+
+- process
+
+  - image preparation
+
+    - load image
+    - measure sky level & noise
+    - construct noise image
+    - construct mask image
+
+  - PSF determination 
+
+    - initial object detection (find peaks)
+    - measure object moments & basic properties
+    - classify on basis of moments
+    - select likely PSF stars
+    - test PSF model functions
+    - select best PSF model function
+    - aperture correction  
+    
+    - aperture photometry vs psf photometry
+
+      - aperture is always the reference
+      - how do we choose an appropriate aperture
+      - how do we measure the aperture correction?
+      - predicting / correcting for aperture loss?
+
+  - apply PSF to sources
+
+    - use dsigma to select PSF-like objects
+    - fit and subtract PSF-like objects
+
+  - fit non-stellar objects
+
+    - galaxy models
+    - asteroid models
+    - low S/N limits
+
+- user's guide
+
+  - configuration parameters
+  - input images / output files
+  - command-line arguments
+
+- demonstration tests
+
+  - simulated data
+    - astrometric accuracy 
+      - as a function of S/N
+      - as a function of crowding
+      - as a function of fringe residual amplitude & scale
+      - as a function of PSF model quality
+      - as a function of seeing
+    - photometric accuracy 
+      - as a function of S/N
+      - as a function of crowding
+      - as a function of fringe residual amplitude & scale
+      - as a function of seeing
+      - as a function of PSF model quality
+  - real data
+    - quality of galaxy models
+    - accuracy of PSF models (megacam, cfh12k, suprime, skyprobe?)
+
Index: /tags/sj_tags/sj_root_20080929/doc/simtest/effects.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/simtest/effects.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/simtest/effects.config	(revision 22322)
@@ -0,0 +1,66 @@
+
+# this entry defines the parameters of the bias images
+BIAS METADATA
+  # LEVEL : the bias value as a function of position
+  LEVEL METADATA  
+     NORDER_X                         S32   0               # number of x orders
+     NORDER_Y                         S32   2               # number of y orders
+     VAL_X00_Y00                      F64   100.0           # polynomial coefficient
+     VAL_X00_Y01                      F64   1.0             # polynomial coefficient
+     VAL_X00_Y02                      F64   0.1             # polynomial coefficient
+     NELEMENTS                        S32   3               # number of unmasked components
+  END  # folder for 2D polynomial
+
+  # the bias level will use the above 2D variation, plus the Gaussian offset 
+  LEVEL.SIGMA                         F32   5.0
+
+  # per-pixel noise
+  READNOISE                           F32   5.0
+
+  # number
+  N
+END
+
+# this entry defines the parameters of the dark images
+DARK METADATA
+  # LEVEL : the bias value as a function of position
+  LEVEL METADATA  
+     NORDER_X                         S32   0               # number of x orders
+     NORDER_Y                         S32   2               # number of y orders
+     VAL_X00_Y00                      F64   100.0           # polynomial coefficient
+     VAL_X00_Y01                      F64   1.0             # polynomial coefficient
+     VAL_X00_Y02                      F64   0.1             # polynomial coefficient
+     NELEMENTS                        S32   3               # number of unmasked components
+  END  # folder for 2D polynomial
+
+  EXPTIME.TREND METADATA  
+     NORDER_X                         S32   1               # number of x orders
+     VAL_X00                          F64   0.0             # polynomial coefficient
+     VAL_X01                          F64   1.0             # polynomial coefficient
+     NELEMENTS                        S32   2               # number of unmasked components
+  END  # folder for 2D polynomial
+
+  TEMPERATURE.TREND METADATA  
+     NORDER_X                         S32   1               # number of x orders
+     VAL_X00                          F64   0.0             # polynomial coefficient
+     VAL_X01                          F64   1.0             # polynomial coefficient
+     NELEMENTS                        S32   2               # number of unmasked components
+  END  # folder for 2D polynomial
+
+  # the bias level will use the above 2D variation, plus the Gaussian offset 
+  LEVEL.SIGMA                         F32   5.0
+
+  # use any of these values for the exposure time
+  EXPTIME.VALUE                       F32  (vector)
+
+  # choose a temperature anywhere in this range
+  TEMPERATURE.MIN                     F32   -90
+  TEMPERATURE.MAX                     F32   -70
+END
+
+FLAT METADATA
+END
+
+FRINGE METADATA
+END
+
Index: /tags/sj_tags/sj_root_20080929/doc/simtest/sequence.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/simtest/sequence.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/simtest/sequence.config	(revision 22322)
@@ -0,0 +1,39 @@
+
+# define the simulated data to be generated
+# SEQUENCE MULTI
+
+SEQUENCE METADATA
+  TYPE       STR OBJECT
+  CENTER.RA  F32 10.0 
+  CENTER.DEC F32 60.0
+
+  OFFSET.RA  F32 3600.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.DEC F32 3600.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.NR  S32 5
+  OFFSET.ND  S32 5
+
+  DITHER.RA  F32 30.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.DEC F32 30.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.NR  S32 2
+  DITHER.ND  S32 2
+
+  DVODB      STR /data/alala.0/ipp/ippRefs/catdir.synth.grizy 
+  FILTERS    STR g,r,i
+  @EXPTIMES  F32 60.0,30.0,30.0
+  @SKYMAGS   F32 21.0,20.5,20.0
+
+  CAMERA     STR megacam  ## force case to match?
+
+  # zero point & plate scale describe the camera -> add to ppSim.config
+
+  # XXX use a more realistic IQ distribution...
+  IQ_MIN     F32 0.5
+  IQ_MAX     F32 0.8
+  
+  # rotation sequence
+  POS_MIN    F32 0.0
+  POS_MAX    F32 0.0
+  POS_DELTA  F32 1.0
+
+END
+
Index: /tags/sj_tags/sj_root_20080929/doc/simtest/simtest.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/simtest/simtest.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/simtest/simtest.config	(revision 22322)
@@ -0,0 +1,34 @@
+
+# define the simulated data to be generated
+
+IMAGE METADATA
+
+
+END
+
+SCIENCE METADATA
+  EXPTIME F32 vector
+
+  # we may have any number of image sequences
+  # each sequence is a set of images in a regular grid, with 
+  # a number of small dithers for each position
+  SEQUENCE MULTI
+  SEQUENCE METADATA
+    CENTER.RA  F32 10.0 
+    CENTER.DEC F32 60.0
+    OFFSET.RA  F32 3600.0 # linear offset in arcsec (do not include cos(DEC) correction)
+    OFFSET.DEC F32 3600.0 # linear offset in arcsec (do not include cos(DEC) correction)
+    OFFSET.NR  S32 5
+    OFFSET.ND  S32 5
+
+    DITHER.RA  F32 30.0   # linear offset in arcsec (do not include cos(DEC) correction)
+    DITHER.DEC F32 30.0   # linear offset in arcsec (do not include cos(DEC) correction)
+    DITHER.NR  S32 2
+    DITHER.ND  S32 2
+
+    FILTER     STR g,r,i
+    EXPTIME    F32 60.0,30.0,30.0
+  END
+
+  DVODB        STR /data/alala.0/ipp/ippRefs/catdir.synth.grizy 
+END
Index: /tags/sj_tags/sj_root_20080929/doc/simtest/simtest.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/simtest/simtest.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/simtest/simtest.txt	(revision 22322)
@@ -0,0 +1,28 @@
+
+The full IPP simulation suite needs to perform the following tasks:
+
+* define the parameters of a simulation run:
+  * IPP stages to be triggered
+    * detrend creation
+    * chip/camera/warp
+    * stack/diff
+    * calibrations
+    * etc
+
+  * types of data to be generate
+    * camera / telescope
+    * raw detrend images
+    * science images (filters, exposure times, position parameters)
+    * transient data to include
+    * photometry inputs
+
+* generate a fake data set
+
+* save the input truth valuesa
+
+* inject into IPP & initiate processing
+
+* test for successful completion
+
+* compare the output measured values to the input truth values
+
Index: /tags/sj_tags/sj_root_20080929/doc/stamps/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/stamps/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/stamps/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.log *.dvi *.aux *.toc *.log *.out *.lof *.tbr *.tbd *.pdf *.ps *.tmp
Index: /tags/sj_tags/sj_root_20080929/doc/stamps/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/stamps/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/stamps/Makefile	(revision 22322)
@@ -0,0 +1,47 @@
+# $Id: Makefile,v 1.3 2008-07-26 02:09:09 bills Exp $
+
+PDFLATEX = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): pdflatex
+PSLATEX  = env TEXINPUTS=.:LaTeX:$(TEXINPUTS): latex
+
+help:
+	@echo "USAGE: make (target)"
+	@echo "  targets: all cd design icd"
+
+cd: stampsCD.pdf
+
+design: stampsDesign.pdf
+
+icd:    stampsICD.pdf
+
+all : cd design icd
+
+CD = stampsCD.tex
+DESIGN = stampsDesign.tex
+
+stampsCD.pdf: $(SDRS)
+
+stampsDesign.pdf: $(DESIGN)
+
+stampsICD.pdf:  stampsICD.tex
+
+add-tag:
+	cvs tag `grep "version{" psLibADD.tex | tr "{}" " " | awk '{printf "ADD-%02d\n", $$2}'` $(ADD)
+
+%.pdf: %.tex
+	$(PSLATEX) $*.tex 
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	thumbpdf --modes=dvips $*.pdf
+	$(PSLATEX) $*.tex 
+	dvips -z -t letter -o $*.ps $*.dvi
+	ps2pdf $*.ps $*.pdf
+	@rm -f $*.ps $*.dvi $*.aux $*.log $*.tbr $*.tbd $*.toc $*.tpm $*.lof body.tmp head.tmp
+
+clean :
+	$(RM) *.log *.dvi *.aux *.toc *.tbd *.tbr *.tpm *.lof *.out *~ core body.tmp head.tmp
+
+dist : clean
+	$(RM) *.pdf
+
+empty: clean
Index: /tags/sj_tags/sj_root_20080929/doc/stamps/stampsCD.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/stamps/stampsCD.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/stamps/stampsCD.tex	(revision 22322)
@@ -0,0 +1,156 @@
+\documentclass[panstarrs,spec]{panstarrs}
+
+\title{Postage Stamp Server} % put in your title
+\subtitle{Conceptual Design}
+\author{Eugene Magnier}
+\audience{IPP}
+\shorttitle{Postage Stamp CD}
+\group{Pan-STARRS IPP}
+\project{Pan-STARRS IPP}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-xxx}
+
+\newcommand\ugriz{$u^\prime g^\prime r^\prime i^\prime z^\prime$}
+\newcommand\grizy{$g r i z y$}
+
+\begin{document}
+\maketitle
+
+\section{Overview}
+
+The basic functionality of the postage stamp server is to accept lists
+of postage stamp requests, determine the corresponding images, extract
+the needed pixels from the images, and return the postage stamps to
+the user.  The nominal mode of interation as an interface to MOPS and
+the other Science Clients is for the postage lists to be FITS tables
+which are placed as incoming data on the IPP Data Store (or the data
+store of the requesting system) and for the results to be placed back
+on the IPP Data Store, along with a FITS table description of the
+resulting output image files.  For the generic user, the requests and
+the result may be sent directly via a web server.  
+
+\section{Inputs}
+
+The basic information needed to by the postage stamp server to honor a
+request consists of: 1) the image of interest and 2) the region on the
+image desired.  This information may potentially be represented in a
+wide number of ways.
+
+\subsection{Image of Interest}
+
+Within the IPP, there are a number of different derived image products
+which may be produced for a given exposure, and other which are not
+specifically coupled to a given exposures (eg, static sky image
+stacks).  In addition, within a given IPP installation, there may be
+multiple IPP projects with different representations of the same image
+data.  There are a number of ways which the end users may want to
+specify the desired images.  These are listed as follows, in order of
+simplicity for the program, but in reverse order of simplicity for the
+user.  
+
+\begin{itemize}
+
+\item {\bf Project \& Image ID set}.  The easiest way to define the
+  input image, in terms of programming, is to explicitly specify the
+  database ID which uniquely identifies the output image, and the IPP
+  Project in which that ID is found.  Within the IPP pipeline
+  database, there is not a single value which is the image ID for all
+  images.  Instead, it is necessary to supply some additional
+  information about which type of image is desired:
+  
+  \begin{tabular}{ll}
+    DB table                   & keys \\
+    \code{rawImfile}           & \code{exp_id} + \code{class_id} \\
+    \code{chipProcessedImfile} & \code{exp_id} + \code{chip_id} \\
+    \code{warpSkyfile} 	       & \code{warp_id} \\
+    \code{diffSkyfile} 	       & \code{diff_id} \\
+    \code{stackSumSkyfile}     & \code{stack_id} \\
+  \end{tabular}
+
+  The URI of the necessary input file can be determined from the
+  specified data by simply doing a query on the specific table of
+  interest.  In the event that the image is no longer on disk, the
+  postage stamp server would need to have the ability of re-creating
+  the image by re-running the needed IPP analysis steps.
+
+ \item {\bf Project \& Exposure \& Type}.  A somewhat more abstract
+ way to define the desired image would be to specify the exposure ID
+ and the type of exposure (raw, chipProcessed, warped, etc), and make
+ some additional assumptions to find the desired data.  For example,
+ the coordinates specified may define which chip within an exposure or
+ which output skyfile from a warp is desired.  The assumption could be
+ made that, for a given type, the last of all versions available that
+ match should be used.  The stack is ambiguous, but some choice could
+ be made, such as the deepest or latest stack which includes the
+ specified exposure.
+
+ \item {\bf Project \& RA \& DEC \& Type}.  One step beyond the previous
+ example would be to specify the coordinates to define the image(s) of
+ interest.  To determine the images which overlap the given
+ coordinates, a query can be made to the appropriate DVO instance.
+ The will in general be more than one overlapping image.  The postage
+ stamp server should be able to either return a collection of multiple
+ images which match, or additional information, such as the time of
+ the exposure or a version number, should be used to limit the result
+ set.
+
+\end{itemize}
+
+\subsection{Region of Interest}
+
+The postage stamp server needs to know which pixels to return from the
+specified image(s).  The output images will be rectangular, so we need
+only specify the center coordinate and the width \& height.  There
+main options are:
+
+\begin{tabular}{ll}
+  Center Coordinate       & Size \\
+  X,Y pixels              & dX,dY pixels \\
+  $\alpha,\delta$ degrees & dX,dY pixels \\
+  X,Y pixels              & d$\alpha$,d$\delta$ arcmin \\
+  $\alpha,\delta$ degrees & d$\alpha$,d$\delta$ arcmin \\
+\end{tabular}
+
+If the measurements are given in celestial coordinates, the image
+astrometry (the best available from the camera analysis table) should
+be used to convert to pixel coordinates.  The postage stamp server
+should given an error if the requested region does not itersect with
+the requested image pixels.
+
+
+\section{Components}
+
+\begin{itemize}
+
+\item {\bf stamp extraction tool} : this program accepts the image URI
+  (or image set?), the region information (center, width, height), and
+  and output URI.  It selects the image and extracts the pixels into a
+  new image which it writes to the specified URI.
+
+\item {\bf request parser} : this program accepts a FITS table with
+  the requests in all possible formats listed above, examine the IPP
+  database(s), and constructs the list of requests in the basic format
+  of the image URI + region description.  This program need not
+  determine if the desired image exists.
+
+\item {\bf image regeneration tool} : this program takes the
+  definition of the desired image (in the form of the table and the
+  database IDs) and determines the steps needed to re-create that
+  output image.  This tool should use the information in the database
+  + the output result files as needed to recreate the original
+  analysis, using, eg, the same detrend images, and the same
+  astrometric calibration, etc.
+
+\item {\bf pantasks postage stamp tasks} : the postage stamp server
+  should be automated by defining a set of pantasks tasks for each of
+  the steps.  One task would examine the data store for new requests,
+  A second tasks would take the requests and send them to the request
+  parse.  A third task would perform the extractions and either return
+  the result to the data store or send the result to the next task.
+  The last task would invoke the image regeneration tool for images
+  which no longer exist on the spinning disks.
+
+\end{itemize}
+
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/stamps/stampsDesign.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/stamps/stampsDesign.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/stamps/stampsDesign.tex	(revision 22322)
@@ -0,0 +1,422 @@
+\documentclass[panstarrs,spec]{panstarrs}
+
+\title{IPP Postage Stamp Server} % put in your title
+\subtitle{Design}
+\author{Bill Sweeney}
+\audience{IPP}
+\shorttitle{Postage Stamp Design}
+\group{Pan-STARRS IPP}
+\project{Pan-STARRS IPP}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-430-xxx}
+
+\newcommand\ugriz{$u^\prime g^\prime r^\prime i^\prime z^\prime$}
+\newcommand\grizy{$g r i z y$}
+
+
+\begin{document}
+
+\maketitle
+\tableofcontents
+
+\pagebreak
+\pagenumbering{arabic}
+
+\section{Overview}
+
+The basic functionality of the postage stamp server is to accept lists
+of postage stamp requests, determine the corresponding images, extract
+the needed pixels from the images, and return the postage stamps to
+the user.  The nominal mode of interaction as an interface to MOPS and
+the other Science Clients is for the postage lists to be FITS tables
+which are placed as incoming data on the IPP Data Store (or the data
+store of the requesting system) and for the results to be placed back
+on the IPP Data Store, along with a FITS table description of the
+resulting output image files.  For the generic user, the requests and
+the result may be sent directly via a web server.  
+
+The basic information needed to by the postage stamp server (PSS) to honor a
+request consists of: 1) the image of interest and 2) the region on the
+image desired (ROI).  This information may be represented in a number of ways
+which are discussed in section 2.1. % How do I include a reference by tag?
+
+The implementation of the PSS is a set of \code{pantasks} tasks, standalone programs,
+database tables, web server content, and associated programs.
+
+\section{Postage Stamp Requests}
+
+
+\subsection{Request Table Contents}
+
+Postage stamp requests are packaged for submission to the PSS as a
+FITS table. The contents of the table specify the ROI and parameters that 
+enable the PSS to choose the images of interest. The columns contained in table are
+shown in Table~\ref{RTableDef}. The \code{EXTNAME} for the FITS table is \code{PS1_PS_REQUEST}.
+
+\begin{table}[h]
+\begin{center}
+\caption{Postage Stamp Request Table Contents}\label{RTableDef}
+\begin{tabular}{lllc}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Values} &             {\bf Required } \\
+\hline
+\code{VERSION}  & string     & N.N               &                      yes \\
+\code{PROJECT}  & string     & any               &                      yes \\
+\code{USER_TAG} & string     & must be unique    &                      yes \\
+\code{REQ_TYPE} & string     & bycoord byexp byid &                     yes \\
+\code{IMG_TYPE} & string     & raw chip warp diff stack &               yes \\
+\code{EXPOSURE} &    string  & any               &                      byexp \\
+\code{ID}       &    string  & any               &                      byid \\
+\code{CLASS_ID} &    string  & any               &                      byid raw \& byexp raw \\
+\code{CHIP_ID}  &    string  & any               &                      byid chip \& byexp chip \\
+\\
+\code{FILTER}   &    string  & any               &                      no \\
+\code{DATE_MIN} &    string  & YYYYMMDD-HH:MM:SS &                      no \\
+\code{DATE_MAX} &    string  & YYYYMMDD-HH:MM:SS &                      no \\
+\\
+\code{RA}       &    string  & sexagesimal or decimal deg &             bycoord, null if pixcenter \\
+\code{DEC}      &    string  & segagesimal or decimal deg &             bycoord, null if pixcenter \\
+\code{CENTER_X} &    S32     & any               &                      null if not pixcenter \\
+\code{CENTER_Y} &    S32     & any               &                      null if not pixcenter \\
+\code{D_RA}     &    F32     & \code{arcsec > 0} &                      0 if pixrange \\
+\code{D_DEC}    &    F32     & \code{arcsec > 0} &                      0 if pixrange \\
+\code{WIDTH}    &    U32     & \code{> 0}        &                      0 if not pixrange \\
+\code{HEIGHT}   &    U32     & \code{> 0}        &                      0 if not pixrange \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsubsection{Image Types}
+All requests need IMG\_TYPE. This parameter selects the stage in the
+image processing pipeline from which the the postage stamp will be extracted.
+The allowed values for IMG\_TYPE are raw, chip, warp, diff, and stack.
+
+\subsubsection{Request Types}
+The REQ\_TYPE determines how the images of interest are selected. There are three allowed values. 
+
+\begin{itemize}
+\item{\textbf{bycoord}} Images are selected by querying the IPP DVO database for images that
+contain the celestial coordinates of the ROI center. 
+The set of images of interest may be limited by specifying certain criteria as described
+in Section \ref{ImageFilters}.
+
+\item{\textbf{byexp}}  The image of interest is the image from a particular exposure
+with type IMG\_TYPE.
+If IMG\_TYPE is raw the CLASS\_ID must be provided. If IMG\_TYPE is chip 
+and the ROI center is specified in pixel coordinates then CHIP\_ID must be specified.
+
+\item{\textbf{byid}}  The image with given IMG\_TYPE and ID is the image of interest.
+The ID parameter refers to IPP image database key that corresponds to the IMG\_TYPE. 
+e.g: either exp\_id, warp\_id, diff\_id, or stack\_id.
+
+\end{itemize}
+
+
+
+\subsubsection{Image Selection Filters}\label{ImageFilters}
+
+A request for given celestial coordinates may refer to many images. The scope of the search can be
+limited to certain dates with the parameters DATE\_MIN and DATE\_MAX.
+
+The FILTER parameter may be used to select only images with a given filter name. XXX: What is the
+namespace for filter names.  For example Megacam has filter names like U.9301 which IPP maps to u.
+Which name do we allow? Either?
+
+
+
+
+\subsection{Creating Postage Stamp Requests}
+A postage stamp request table may be created one of three ways.
+\begin{itemize}
+\item \code{pstamprequest} is a simple command line program provided by the IPP
+which builds a request file from user supplied command line arguments.
+
+\item The PSS will include a web server providing a user interface for creating
+and submitting postage stamp requests.
+This sub-project has not been defined yet.
+It is expected that there will be web pages 
+containing forms with fields similar to \code{pstamprequest}'s command line arguments.
+The web server will be submit the request directly to the PSS and optimize
+a step or two in the processing.
+
+\item A client may build the request tables inside it's own software following the table format
+described above.
+
+\end{itemize}
+
+
+
+\subsubsection{pstamprequest}
+
+\code{pstamprequest} is a simple program takes its command line arguments and
+creates a postage stamp request table.  Here are a couple of examples showing how it is used.
+
+\begin{verbatim}
+    pstamprequest -project gpc1 -byexp o4435g0537o chip xy33 -pixcenter 2700 2700 -pixrange 100 100  \
+                  -utag stampreq1 stampreq1.fits 
+\end{verbatim}
+
+\begin{verbatim}
+    pstamprequest -project gpc1 -bycoord warp 0:41:49.2 41.2 -pixrange 100 100 -utag stampreq2 stampreq2.fits
+\end{verbatim}
+
+
+The following is a brief description of the command line arguments used in these examples.
+The complete set of command line arguments is given in Appendix~\ref{pstamprequestArgs}.
+
+
+\begin{itemize}
+
+\item{\code{-project gpc1 }} tells the PSS to use the gpc1 project's database. 
+
+\item{\code{-byexp  o4435g0537o chip xy33 }} requests a stamp from the chip processed image 
+from chip xy33 from the given exposure. XXX: perhaps we should extend this to allow chip+cell to be
+specfied and interpret pixel coordinates as cell coordinates?
+
+\item{\code{-pixcenter 2700 2700 -pixrange 100 100 }} 100 pixel x 100 pixel region of interest 
+centered at x = 2700, y = 2700 in chip coordinates.
+
+\item{\code{-bycoord warp}} request stamps from warp images containing the given coordinates.
+
+\item{\code{-skycenter }} specifies the Right Ascension and Declination for the center of the
+postage stamp ROI.
+Note that in this example postage stamps will be extracted from \emph{all} warp images that contain the ROI.
+
+\item stampreq1.fits and stampreq2.fits are the names of the resulting request table files. 
+
+
+\item{code{-utag }} The user tag for the request. This tag may be used to refer to the postage stamp
+request during processing and is used to generate the names for the resulting postage stamp files.
+It is the user's responsibility to assign a unique tag.
+The web server interface will automatically select a unique user tag.
+
+\end{itemize}
+
+XXX can project be defaulted using the site file?
+
+\subsubsection{Creating Postage Stamp Requests through the Web Interface}
+TBD
+
+\section{Queuing Postage Stamp Requests for processing}
+
+A postage stamp request is submitted to the PSS for processing by placing the request file
+on a Data Store.  XXX insert Ref to DataStore spec.
+The \code{pantasks} task \code{pstampmonitor} monitors a collection of Data Stores for
+new postage stamp request files.
+For example the PSS is required to handle postage stamp requests from the MOPS science client,
+so \code{pstampmonitor} will be configured to periodically check for the presence of new request
+files in the MOPS data store.
+
+The following psuedo-code outlines the operation of pstampmonitor.
+
+\begin{verbatim}
+    task pstampmonitor {
+        for each Data Store being monitored {
+            lookup lastFileDownloaded               // TODO: lookup where? DB, a file?
+
+            newFileLlist = results of query to Data Store: list of files newer than lastFileDownloaded
+
+            for each file in newFileList {
+                download request file to PSS local storage
+
+                // add entry to pstampRequests table
+                INSERT INTO pstampRequests (status, uri) VALUES ('new', file);
+
+                save lastFileDownloaded = file      // in DB?
+            }
+        }
+    }
+\end{verbatim}
+
+When \code{pstampmonitor} finds a new request file it queues the request for processing by adding
+a row to the database table \code{pstampRequests}. 
+The columns in this table are described in Table~\ref{pstampRequestsTab}
+
+
+\begin{table}[h]
+\begin{center}
+\caption{pstampRequests Table Contents}\label{pstampRequestsTab}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Description} \\
+\hline
+\code{ps_id}      & integer        &  request id key assigned by database \\
+\code{status}     & string         &  status of the request new, run, stop \\
+\code{uri}        & string         &  uri for the request table's file    \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+As an optimization, the PSS web interface for creating postage stamp requests will bypass the
+request monitor and add the request entry to the database directly.
+
+
+
+\section{Launching Postage Stamp Extractions}
+
+The \code{pstamprequestparser} is a pantask tasks which watches for new entries in the DB table
+\code{pstampRequests}.
+When a new entry is found, it runs the postage stamp request parser \code{pstampparse}
+which is a C program that reads the request file and turns it into one or more postage
+stamp extraction operations.
+
+\begin{verbatim}
+    task pstampRequestParser {
+        newRequests = SELECT ps_id, uri FROM pstampRequests WHERE status = 'new';
+        foreach req in newRequests {
+            execute pstampparse req->ps_id req->uri
+        }
+    }
+
+    program pstampparse(id, uri) {
+        request = metadata read by psFitsReadTable(uri);
+
+        if (request->REQ_TYPE == bycoord) {
+            imageList = results of query DVO database for images containing the ROI center
+            // XXX: can DVO select by filter or do we have to do it here?
+
+            for each image in imageList {
+                if (request->FILTER && (request->FILTER != requestimage->filter))
+                    continue;
+                if (date range specified) {
+                    if (image->DATE is not in range)
+                        continue;
+                }
+                if (image needs to be regenerated)
+                    and entry to pstampJob with state regen
+                else 
+                    add entry to pstampJob with state run
+            }
+        } else if (request->REQ_TYPE == byexp) {
+            image = lookup image of IMG_TYPE for the given exposure in IPP database
+            if (image needs to be regenerated)
+                and entry to pstampJob with state regen
+            else 
+                add entry to pstampJob with state run
+        } else if (request->REQ_TYPE = byid) {
+            image is determined by parameters in request
+            if (image needs to be regenerated)
+                and entry to pstampJob with state regen
+            else 
+                add entry to pstampJob with state run
+        }
+    }
+\end{verbatim}
+
+If the request specifies the image directly the request parser's job is quite easy. 
+The request parser simply insures that the input images exists and
+queues an extraction operation for execution by adding one or more rows describing the commands
+to run to the database table \code{pstampJob}.
+
+If the input image does not exist, but the system knows how to generated it the status is
+set to regen.
+
+
+
+\begin{table}[h]
+\begin{center}
+\caption{pstampJob Table Contents}\label{pstampJobTab}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Description} \\
+\hline
+\code{ps_jobid}   & integer        &  job id key assigned by database \\
+\code{ps_id}      & integer        &  request which this job is from             \\
+\code{state}      & string         &  status of the job regen, run, stop \\
+\code{status}     & integer        &  result code 0 for success, other for error \\
+\code{command}    & string         &  command string to execute    \\
+\code{output uri} & string         &  uri for the output postage stamp request file\\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+XXX: is putting the command strings in the database just adding clutter? Is there a better way to do this?
+How about if we create a script the appropriate commands and have pstampregen and pstampextract
+exectute that.
+
+XXX: maybe fault should be an error code instead of a boolean so we can tell the user what happened.
+
+\section{Image Regeneration}
+\code{pstampregen} is a pantasks task that selects entries in psJob with the state \code{regen}.
+Using magic to be specified later this task regenerates the image from the raw data. For example,
+regenerating a chip processed image by running the detrend operation. 
+
+When the regeneration process is complete, the state of the jobs entry in pstampJob is set to run.
+
+\section{Extracting the Postage Stamps}
+
+\code{pstampextract} is a pantasks task which looks for entries in pstampJob with state run
+and executes the extraction tool program \code{ppstamp}. When ppstamp completes the state
+of the job is set to stop and a result code is stored in status.
+
+The postage stamp files are placed within the PSS Data Store.
+
+\code{ppstamp} is the program which takes input fits images and creates the actual postage stamps.
+Appendix \ref{ppstampArgs} shows the arguments accepted by ppstamp.
+
+The WCS in the stamp is calculated from the WCS in the input image.
+The resulting postage stamp image is a SKYCELL file that corresponds to the image's camera. 
+XXX: Using a skycell isn't the right thing to do. I'd like to have a simple image file, but the
+existing code that writes WCS to the header requires a pmFPA. I need to write a function that
+takes the calculated transforms and gets them to the header without an pmFPA.
+
+\section{Creating the request status file}
+
+Another pantasks task is needed to handle request that generate multiple postage stamp
+extractions. This task will figure out when all of jobs are done and build a fits table that
+gives the results of the postage stamp request.
+
+From the user's perspective the request is done when the results file with the supplied
+user tag becomes available on the PSS Data Store.
+
+\section{Issues}
+
+\begin{itemize}
+
+\item What about Magic? Do we need to do magic processing on un-magic'd images before generating
+stamps?
+
+\item Do we want a dry-run mode which will show the user what will be done? It will be easy to generate
+requests that result in very many postage stamps being created.
+
+\end{itemize}
+
+\pagebreak
+
+\appendix
+
+\section{pstamprequest Command Line Arguments}\label{pstamprequestArgs}
+\begin{verbatim}
+    The description of pstamprequestArgs will go here.
+
+    It's pretty much what you'd expect. There are arguments that correspond to each of
+    the parameters in the request FITS table.
+
+    ROI specification is just like ppstamp below.
+\end{verbatim}
+
+\section{ppstamp Command Line Arguments}\label{ppstampArgs}
+\begin{verbatim}
+
+  ppstamp -skycenter RA DEC -pixrange dx dy    [-file INPUT.fits] [-list INPUT.txt] OUTPUT
+  ppstamp -skycenter RA DEC -arcrange dRA dDEC [-file INPUT.fits] [-list INPUT.txt] OUTPUT
+  ppstamp -pixcenter x y    -pixrange dx dy    [-chip chipname] [-file INPUT.fits] [-list INPUT.txt] OUTPUT
+  ppstamp -pixcenter x y    -arcrange dRA dDEC [-chip chipname] [-file INPUT.fits] [-list INPUT.txt] OUTPUT
+
+-skycenter is specified as HH:MM:SS DD:MM:SS or decimal degrees
+-arcrange values are seconds of arc
+
+Optional arguments:
+         -chip chipName    selects chip (only used with -pixcenter)
+
+
+
+\end{verbatim}
+\end{document}
Index: /tags/sj_tags/sj_root_20080929/doc/stamps/stampsICD.tex
===================================================================
--- /tags/sj_tags/sj_root_20080929/doc/stamps/stampsICD.tex	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/doc/stamps/stampsICD.tex	(revision 22322)
@@ -0,0 +1,368 @@
+\documentclass[panstarrs,spec]{panstarrs}
+
+\title{IPP Postage Stamp Server} % put in your title
+\subtitle{ICD}
+\author{Bill Sweeney}
+\audience{IPP}
+\shorttitle{Postage Stamp ICD}
+\group{Pan-STARRS IPP}
+\project{Pan-STARRS IPP}
+\organization{Institute for Astronomy}
+\version{DR}
+\docnumber{PSDC-940-xxx}
+
+\newcommand\ugriz{$u^\prime g^\prime r^\prime i^\prime z^\prime$}
+\newcommand\grizy{$g r i z y$}
+
+
+\begin{document}
+
+\maketitle
+\tableofcontents
+
+\pagebreak
+\pagenumbering{arabic}
+
+\section{Overview}
+
+The basic functionality of the postage stamp server is to accept lists
+of postage stamp requests, determine the corresponding images, extract
+the desired pixels from the images, and return the postage stamps to
+the user.  The nominal mode of interaction as an interface to MOPS and
+the other Science Clients is for the postage stamp requests to be FITS tables
+which are placed as incoming data on the IPP Data Store (or the data
+store of the requesting system) and for the results to be placed back
+on the IPP Data Store, along with a FITS table description of the
+resulting output image files.  For the generic user, the requests and
+the result may be accessed directly via a web server.  
+
+The basic information needed to by the postage stamp server (PSS) to honor a
+request consists of: 1) the image of interest and 2) the region on the
+image desired (ROI).  This information may be represented in a number of ways
+which are discussed in section 2.1. % How do I include a reference by tag?
+
+\section{Postage Stamp Requests}
+
+
+Postage stamp requests are packaged for submission to the PSS in a file set containing
+one or more FITS binary tables called \emph{request tables}.
+Each row in a request table contains a postage stamp request specification.
+Each request specification causes zero or more postage stamp jobs to be processed.
+An image is added to the output file set for each successful job.
+
+The request specification give the parameters that the PSS uses to determine the images of interest
+and the Region of Interest. 
+
+\subsection{Request Table Contents}
+
+The required header keywords for a request table
+are given in Table~\ref{RHeaderDef}.  
+The definition for the table's columns is in Table~\ref{RTableDef}.
+
+\begin{table}[h]
+\begin{center}
+\caption{Postage Stamp Request File Header Keywords}\label{RHeaderDef}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf FITS Keyword} & {\bf Datatype} &  {\bf Description } \\
+\hline
+\code{EXTNAME}  & string     & \code{PS1_PS_REQUEST} \\
+\code{EXTVER}   & string     & 1  \\
+\code{REQ_NAME} & string     & unique name of the request (see text) \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+The IPP will be configured to periodicly query a set of Data Stores for new request file sets.
+We will refer to each Data Store as a \emph{requestor}.
+Each request table submitted by a given requstor must have a unique value for 
+the keyword \code{REQ_NAME}. This value is used for the name of the request's output file set.
+
+The postage stamp server supports three types of "postage stamp" job specified by the value
+the column \code{JOB_TYPE}. If the value is \emph{stamp} postage stamp images will be extracted
+from the images of interest.  If the value of \code{JOB_TYPE} is \emph{get\_image} a copy
+of the selected image(s) with (satelite streaks removed) will be placed on the output file set.
+Finally if the \code{JOB_TYPE} is \emph{list} the results file will list the input selected images
+but no postage stamps will be made.
+
+\begin{table}[h]
+\begin{center}
+\caption{Postage Stamp Request Table Columns}\label{RTableDef}
+\begin{tabular}{llll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Values}       &        {\bf Notes } \\
+\hline
+\code{ROWNUM}   & U32       &                           &        must be unique in file\\
+\code{PROJECT}  & string    & TBD                       &        \\
+\code{JOB_TYPE} & string    & stamp or get\_image       &        \\
+\\
+\code{STAMP_NAME}  & string & letters, numbers, '.',  '\-' and '\_'  &        \\
+\code{OPTION_MASK} & U32    & \code{[0-5]} see text     &      \\ 
+\\
+\code{REQ_TYPE} & string    & bycoord, byexp, or byid   &        \\
+\code{IMG_TYPE} & string    & raw, chip, warp, diff, or stack  & \\
+\code{ID} & string          & depends on project and REQ\_TYPE& \\
+\code{CLASS_ID} & string    & see text                  &        not null for byid raw \& byexp raw \\
+\\
+\code{COORD_MASK} &  U32    & \code{[0-3]} see text     &        not null if \code{JOB_TYPE} = stamp \\
+\code{CENTER_X} &  F64      & see text                  &        not null if \code{JOB_TYPE} = stamp \\
+\code{CENTER_Y} &  F64      & see text                  &        not null if \code{JOB_TYPE} = stamp \\
+\code{WIDTH}    &  F64      & see text                  &        not null if \code{JOB_TYPE} = stamp \\
+\code{HEIGHT}   &  F64      & see text                  &        not null if \code{JOB_TYPE} = stamp \\
+\\
+\code{REQFILT}  &   string  & any                       &        used only for \code{REQ_TYPE} = bycoord \\
+\code{MJD_MIN}  &   F64     & Modified Julian Day       &        used only for \code{REQ_TYPE} = bycoord \\
+\code{MJD_MAX}  &   F64     &                           &        used only for \code{REQ_TYPE} = bycoord \\
+\\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\subsubsection{Image Types}
+All requests need IMG\_TYPE. This parameter selects the stage in the processing pipeline
+for the images of interest.
+The allowed values for IMG\_TYPE are raw, chip, warp, diff, and stack.
+
+\subsubsection{Request Types}
+The method used to select the images of interest is determined by the value of the REQ\_TYPE column.
+There are three alllowed values. 
+
+\begin{itemize}
+
+\item{\textbf{bycoord}} The center of the region of interest is specifed in celestial coordinates.
+Images are selected by querying the project's DVO database for images with the provided
+IMG\_TYPE that contain the center.
+The set of images of interest may be limited by specifying certain criteria as described
+in Section \ref{ImageFilters}.
+
+\item{\textbf{byexp}}  The image of interest is the image resulting from a particular exposure 
+name given by the value of the ID column.
+
+\item{\textbf{byid}}  The image with given IMG\_TYPE and ID is the image  of interest.
+The ID parameter refers to IPP image database key that corresponds to the IMG\_TYPE. 
+e.g: either exp\_id, chip\_id, warp\_id, diff\_id, or stack\_id.
+\end{itemize}
+
+CLASS\_ID must also be supplied for request specifications where all of the following are true
+
+\begin{itemize}
+
+\item the ROI center is specified in pixel coordinates
+
+\item JOB\_TYPE=stamp
+
+\item REQ\_TYPE=byexp or REQ\_TYPE=byid 
+
+\item IMG\_TYPE=chip or IMG\_TYPE=raw
+
+\end{itemize}
+
+
+\subsubsection{Image Selection Filters}\label{ImageFilters}
+
+A request for given celestial coordinates may refer to many images. The scope of the search can be
+limited to certain dates with the parameters MJD\_MIN and MJD\_MAX. The specified range is inclusive.
+
+The REQFILT parameter may be used to restrict the search for images taken with a given filter.
+XXX: What is the namespace for filter names.  For example Megacam has filter names like U.9301 which
+IPP maps to u.  Which name do we allow? Probably should used the mapped names g r i etc.
+
+\subsection{The Region of Interest}
+
+Postage stamps are extracted from a rectangular region on the input image. The region is defined by
+a center,  width, and height.
+
+The center is defined by the columns CENTER\_X and CENTER\_Y and the dimenions of the rectangle are
+the values of WIDTH and HEIGHT. The two least signficant bits in value for the column COORD\_MASK
+determine the type for the coordinates
+
+\begin{table}[h]
+\begin{center}
+\caption{}\label{Rcoordmask}
+\begin{tabular}{ll}
+\hline
+\hline
+{\bf COORD\_MASK value} &  {\bf Description } \\
+\hline
+0                       &   center in RA/DEC; width \& height in arcseconds\\
+1                       &   center in pixel coordinates; width and height in arcseconds\\
+2                       &   center in RA/DEC; width and height in pixels\\
+3                       &   center x/y, width, and height in pixel coordinates\\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+Another way to describe this is that COORD\_MASK is two bit mask that bitmask that
+determines the format of the ROI values. In the C language:
+
+\begin{itemize}
+\item\code{#define PSTAMP_CENTER_IN_PIXELS 1}
+\item\code{#define PSTAMP_RANGE_IN_PIXELS  2}
+\end{itemize}
+
+If \code{COORD_MASK & PSTAMP_CENTER_IN_PIXELS} is zero, CENTER\_X is the right ascension of
+the ROI center in degrees and CENTER\_Y  is its declination in degrees.
+
+If \code{COORD_MASK & PSTAMP_RANGE_IN_PIXELS} is zero, WIDTH and HEIGHT are measured in seconds of arc.
+
+When pixel coordinates are used the coordinate system is that of the image. (For chip level images, before
+the stamps are created the cells are mosaiced into a single image.) 
+
+Any fractional parts in pixel coordinate values are ignored.
+
+The value of \code{OPTION_MASK} is used to select various other options. It is a bitwise or of the following
+values.
+
+\begin{table}[h]
+\begin{center}
+\caption{}\label{optionmask}
+\begin{tabular}{ll}
+\hline
+\hline
+{\bf OPTION\_MASK value} &  {\bf Description } \\
+\hline
+1                       &   create postage stamp of the image pixels \\
+2                       &   create postage stamp of the mask pixels \\
+4                       &   create postage stamp of the weight pixels \\
+8                       &   convert WCS to approximate linear transformation in the stamp images\\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+
+\section{Results File}
+
+Each request specification (each row in a request file) results in zero or more postage stamp jobs
+to be queued and executed. Each job results in a row in the Postage stamp server results table.
+
+
+\subsection{Results Table Contents}
+
+The required header keywords for a results table
+are given in Table~\ref{ResHeaderDef}.  
+The definition for the table's columns is in Table~\ref{ResTableDef}.
+
+\begin{table}[h]
+\begin{center}
+\caption{Postage Stamp Results File Header Keywords}\label{ResHeaderDef}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf FITS Keyword} & {\bf Datatype} &  {\bf Description } \\
+\hline
+\code{EXTNAME}  & string     & \code{PS1_PS_RESULTS} \\
+\code{EXTVER}   & string     & 1  \\
+\code{REQ_NAME} & string     & name of the request the results correspond to ) \\
+\code{REQ_ID} & S64     & internal postage stamp id for the request \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+\begin{table}[h]
+\begin{center}
+\caption{Postage Stamp Results Table Columns}\label{ResTableDef}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Column Name} & {\bf Datatype} & {\bf Values}       \\
+\code{ROWNUM}       & U32       & identifier for this row in table\\
+\code{ERROR_CODE}   & U32       & Error code (see text) \\
+\code{IMG_NAME}     & string    & Name of image in Data Store file set \\
+\code{JOB_ID}       & S64       & Internal postage stamp server job id that produced this image \\
+\\
+\code{RA_DEG}       & F64       &  RA of stamp center in degrees \\
+\code{DEC_DEG}      & F64       &  Declination of stamp center in degrees \\
+\\
+\code{MJD_OBS}      & F64       &  actual start time of exposure \\
+\code{RA_OBS}       & F64       &  J2000 field center RA at start of exposure MJD \\
+\code{DEC_OBS}      & F64       &  J2000 field center Dec at start of exposure MJD \\
+\code{FILTER}       & string    &  Actual filter retrieved (may differ from REQFILT\\
+\code{EXPTIME}      & F64       &  exposure time of parent image \\
+\code{FPA_ID}       & string    &  orginal FPA\_ID used at injection \\
+\\
+\code{PROJECT}  & string    & Values from initial request specification\\
+\code{JOB_TYPE} & string    & \\
+\\
+\code{REQ_TYPE} & string    & \\
+\code{IMG_TYPE} & string    & \\
+\code{CLASS_ID} & string    & \\
+\\
+\code{COORD_MASK} &  U32    & \\
+\code{CENTER_X} &  F64      & \\
+\code{CENTER_Y} &  F64      & \\
+\code{WIDTH}    &  F64      & \\
+\code{HEIGHT}   &  F64      & \\
+\\
+\code{REQFILT}  &   string  & \\
+\code{MJD_MIN}  &   F64     & \\
+\code{MJD_MAX}  &   F64     & \\
+\\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+Each line in the results file corresponds to a job to create a postage stamp image. ROWNUM gives
+the corresponding row in the request file.
+Note that if more than one bit is set in the OPTION\_MASK, a single job may generate multiple images
+and so there may be multiple rows in the results file with the same value of ROWNUM.
+
+IMG\_NAME give the name of the image in the Data Store file set.
+
+If no STAMP\_NAME is provided in the request specifiction the image file names will be in the form
+\code{ROWNUM_I.fits}  where I is an integer that ranges from 1 to the number of jobs that the request
+specification generated. Mask images will be named \code{ROWNUM_I.mk.fits} and weight images will
+have the name \code{ROWNUM_I.mk.fits}
+
+If STAMP\_NAME is provided in the resquest specification, the name of the image, mask, and weight files
+will be 
+\begin{itemize}
+\item\code{ROWNUM_I_STAMP_NAME.fits}
+\item\code{ROWNUM_I_STAMP_NAME.mk.fits},
+\item\code{ROWNUM_I_STAMP_NAME.wt.fits}
+\end{itemize}
+
+JOB\_ID is the value of the postage stamp server's internal job id. This is provided primarily as a debugging
+aid.
+
+The column named PROJECT and following columns are copies from the input request specification.
+
+If ERROR\_CODE is non-zero one or more errors occured processing the request specification.
+The value of ERROR\_CODE is a bitwise or of the values given in the table\ref{Rerrorcodes}.
+
+
+
+\begin{table}[h]
+\begin{center}
+\caption{Postage Stamp Error Codes}\label{Rerrorcodes}
+\begin{tabular}{lll}
+\hline
+\hline
+{\bf Name} & {\bf ERROR\_CODE value} &  {\bf Description } \\
+\hline
+NO\_ERROR            &   0   &   \\
+NO\_MATCHING\_IMAGE   &   1   & No image matches the request specification \\
+RA\_DEC\_NOT\_ON\_PIXEL &   2   & The central RA, DEC are on a blind area (for example a inter-chip gap) \\
+RA\_DEC\_NOT\_IN\_FOV   &   4   & Wrong position on sky for given MJD \\
+NO\_IMAGE\_FOR\_FILTER &   8   & Image is not in the filter requested \\
+NOT\_AVAILABLE\_PERM  &   16  & Image matched but is permanantly unavailable\\
+NOT\_AVAILABLE\_TEMP  &   32  & Image matched but is currently unaccessible\\
+INVALID\_REQUEST     &   64  & request specification was invalid \\
+SYSTEM\_ERROR        &   128 & system error \\
+\hline
+\end{tabular}
+\end{center}
+\end{table}
+
+In the case of an error parsing the request table a file containing a textual description of the errror
+may be added to the resulting file set.
+
+\end{document}
+
Index: /tags/sj_tags/sj_root_20080929/dvoTools/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/.cvsignore	(revision 22322)
@@ -0,0 +1,16 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.guess
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+ltmain.sh
+missing
+
+libtool
Index: /tags/sj_tags/sj_root_20080929/dvoTools/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/dvoTools/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppImage
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/dvoTools/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/configure.ac	(revision 22322)
@@ -0,0 +1,29 @@
+AC_PREREQ(2.61)
+
+AC_INIT([dvoTools], [1.0.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+.deps
+Makefile
+Makefile.in
+config.h
+config.h.in
+dvoApplyCorr
+dvoMakeCorr
+stamp-h1
+.libs
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/Makefile.am	(revision 22322)
@@ -0,0 +1,38 @@
+bin_PROGRAMS = dvoMakeCorr dvoApplyCorr
+
+noinst_HEADERS = \
+	dvoMakeCorr.h \
+	dvoApplyCorr.h
+
+dvoMakeCorr_CFLAGS = $(DVOCORR_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+dvoMakeCorr_LDFLAGS = $(DVOCORR_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+dvoMakeCorr_SOURCES = \
+	dvoMakeCorr.c \
+	dvoMakeCorrArguments.c \
+	dvoMakeCorrParseCamera.c \
+	dvoMakeCorrLoop.c \
+	dvoMakeCorrUnbin.c \
+	dvoMakeCorrCleanup.c \
+	dvoMakeCorrOptions.c \
+	dvoMakeCorrVersion.c
+
+dvoApplyCorr_CFLAGS = $(DVOCORR_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+dvoApplyCorr_LDFLAGS = $(DVOCORR_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+dvoApplyCorr_SOURCES = \
+	dvoApplyCorr.c \
+	dvoApplyCorrArguments.c \
+	dvoApplyCorrParseCamera.c \
+	dvoApplyCorrLoop.c \
+	dvoApplyCorrReadout.c \
+	dvoApplyCorrCleanup.c \
+	dvoApplyCorrOptions.c \
+	dvoApplyCorrVersion.c
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorr.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorr.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorr.c	(revision 22322)
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+int main(int argc, char **argv) {
+
+    psLibInit(NULL);
+
+    psTimerStart(TIMERNAME);
+
+    // USAGE: dvoApplyCorr -file INPUT OUTPUT
+
+    // Parse the configuration and arguments
+    pmConfig *config = dvoApplyCorrArguments (argc, argv);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse command-line arguments.");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // define recipe options
+    // define the active I/O files
+    dvoApplyCorrOptions *options = dvoApplyCorrParseCamera(config);
+    if (options == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse camera.");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // Image Loop
+    if (!dvoApplyCorrLoop(config, options)) {
+        psErrorStackPrint(stderr, "Unable to loop over input");
+        exit(PS_EXIT_SYS_ERROR);
+    }
+
+    psLogMsg ("ppImage", 3, "Complete ppImage run: %f sec\n", psTimerMark(TIMERNAME));
+
+    // Cleaning up
+    dvoApplyCorrCleanup(config, options);
+
+    return PS_EXIT_SUCCESS;
+}
+
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorr.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorr.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorr.h	(revision 22322)
@@ -0,0 +1,49 @@
+#ifndef DVO_MAKE_CORR_H
+#define DVO_MAKE_CORR_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "psmodules.h"
+
+#define RECIPE_NAME "DVOCORR"		// Name of the recipe to use
+#define TIMERNAME "dvoApplyCorr"		// Name of timer
+
+// Options for dvoApplyCorr
+typedef struct {
+    int test;
+} dvoApplyCorrOptions;
+
+dvoApplyCorrOptions *dvoApplyCorrOptionsAlloc(void);
+
+dvoApplyCorrOptions *dvoApplyCorrOptionsParse(pmConfig *config);
+
+// Get the configuration
+pmConfig *dvoApplyCorrArguments(int argc, char **argv);
+
+// Determine what type of camera, and initialise
+dvoApplyCorrOptions *dvoApplyCorrParseCamera(pmConfig *config);
+
+// Loop over the input
+bool dvoApplyCorrLoop(pmConfig *config, dvoApplyCorrOptions *options);
+
+// apply the correction factor pixel-by-pixel
+bool dvoApplyCorrReadout (pmConfig *config, pmFPAview *view, char *inName, char *corrName);
+
+// free memory, check for leaks
+void dvoApplyCorrCleanup (pmConfig *config, dvoApplyCorrOptions *options);
+
+/// Return short version information
+psString dvoApplyCorrVersion(void);
+
+/// Return long version information
+psString dvoApplyCorrVersionLong(void);
+
+/// Update the metadata with version information for all dependencies
+void dvoApplyCorrVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrArguments.c	(revision 22322)
@@ -0,0 +1,66 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+static void usage (void) {
+    fprintf(stderr, "USAGE: dvoApplyCorr -file INPUT.fits OUTPUT [-corr CORR.fits]\n\n");
+    exit (2);
+}
+
+pmConfig *dvoApplyCorrArguments(int argc, char **argv)
+{
+    int argnum;
+
+    if (argc == 1) {
+        usage();
+    }
+
+    if (psArgumentGet (argc, argv, "-version")) {
+	psString version;
+	version = dvoApplyCorrVersionLong(); fprintf (stdout, "%s\n", version); psFree (version);
+	version = psModulesVersionLong();   fprintf (stdout, "%s\n", version); psFree (version);
+	version = psLibVersionLong();       fprintf (stdout, "%s\n", version); psFree (version);
+	exit (0);
+    }
+
+    // load the site-wide configuration information
+    pmConfig *config = pmConfigRead(&argc, argv, RECIPE_NAME);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // save the following additional recipe values based on command-line options
+    // these options override the DVOCORR recipe values loaded from recipe files
+    psMetadata *options = pmConfigRecipeOptions (config, RECIPE_NAME);
+
+    // XXX add options from command-line here
+
+    // drop the local view on the options (saved on config->arguments)
+    psFree (options);
+
+    // chip selection is used to limit chips to be processed
+    if ((argnum = psArgumentGet (argc, argv, "-chip"))) {
+        psArgumentRemove (argnum, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_DATA_STRING, "",
+                          argv[argnum]);
+        psArgumentRemove (argnum, &argc, argv);
+    }
+
+    // the input file is a required argument; if not found, we will exit
+    bool status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
+    if (!status) {
+        usage ();
+    }
+
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "CORR", "-corr", "-corrlist");
+
+    if (argc != 2) usage ();
+
+    // Add the output image (which remain on the command-line) to the arguments list
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrCleanup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrCleanup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrCleanup.c	(revision 22322)
@@ -0,0 +1,22 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "dvoApplyCorr.h"
+
+void dvoApplyCorrCleanup (pmConfig *config, dvoApplyCorrOptions *options)
+{
+    // Free memory used by dvoApplyCorrCleanup
+    psFree(options);
+    psFree(config);
+
+    // Free memory used by psModules
+    pmConceptsDone();
+    pmConfigDone();
+
+    // Free memory used by psLib
+    psLibFinalize();
+    fprintf (stderr, "Found %d leaks at %s\n", psMemCheckLeaks (0, NULL, stderr, false), "dvoApplyCorrCleanup");
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrLoop.c	(revision 22322)
@@ -0,0 +1,94 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+bool dvoApplyCorrLoop (pmConfig *config, dvoApplyCorrOptions *options) {
+
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    // psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, RECIPE_NAME);
+
+    // select the input image
+    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "DVOFLAT.INPUT");
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find input data!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
+
+    // load data at FPA level
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+	psError(PS_ERR_UNKNOWN, false, "failed IO for fpa in dvoApplyCorr\n");
+	psFree(view);
+        return false;
+    }
+
+    // process each chip in the FPA
+    while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
+        psLogMsg ("dvoApplyCorrLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) continue;
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) {
+            psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d in dvoApplyCorr\n", view->chip);
+	    psFree (view);
+	    return false;
+	}
+
+	// process each cell in the Chip
+        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
+            psLogMsg ("dvoApplyCorrLoop", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) continue;
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+		psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d in dvoApplyCorr\n", view->chip, view->cell);
+		psFree (view);
+		return false;
+	    }
+
+	    // process each of the readouts
+	    while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
+		if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+		    psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d, readout %d in dvoApplyCorr\n", view->chip, view->cell, view->readout);
+		    psFree (view);
+		    return false;
+		}
+		if (!readout->data_exists) continue;
+
+		// XXX put the function here which multiplies the input image and the correction image
+		dvoApplyCorrReadout (config, view, "DVOFLAT.INPUT", "DVOFLAT.CORR");
+
+		if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+		    psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d, readout %d in dvoApplyCorr\n", view->chip, view->cell, view->readout);
+		    psFree (view);
+		    return false;
+		}
+	    }
+	    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+		psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d in dvoApplyCorr\n", view->chip, view->cell);
+		psFree (view);
+		return false;
+	    }
+	}
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+	    psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d in dvoApplyCorr\n", view->chip);
+	    psFree (view);
+	    return false;
+	}
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+	psError(PS_ERR_UNKNOWN, false, "failed IO for fpa in dvoApplyCorr\n");
+	psFree (view);
+	return false;
+    }
+
+    psFree (view);
+
+    // fail if we failed to handle an error
+    if (psErrorCodeLast() != PS_ERR_NONE) return false;
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrOptions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrOptions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrOptions.c	(revision 22322)
@@ -0,0 +1,28 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+static void dvoApplyCorrOptionsFree(dvoApplyCorrOptions *options)
+{
+    if (options == NULL) return;
+
+    return;
+}
+
+dvoApplyCorrOptions *dvoApplyCorrOptionsAlloc(void)
+{
+    dvoApplyCorrOptions *options = psAlloc(sizeof(dvoApplyCorrOptions));
+    psMemSetDeallocator(options, (psFreeFunc)dvoApplyCorrOptionsFree);
+
+    // Initialise options
+    options->test = 0;
+    return options;
+}
+
+dvoApplyCorrOptions *dvoApplyCorrOptionsParse(pmConfig *config)
+{
+    dvoApplyCorrOptions *options = dvoApplyCorrOptionsAlloc ();
+    return options;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrParseCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrParseCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrParseCamera.c	(revision 22322)
@@ -0,0 +1,60 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "dvoApplyCorr.h"
+
+dvoApplyCorrOptions *dvoApplyCorrParseCamera (pmConfig *config) {
+
+    bool status = false;
+
+    // the input image defines the camera, and all recipes and options the follow
+    pmFPAfile *input = pmFPAfileDefineFromArgs (NULL, config, "DVOFLAT.INPUT", "INPUT");
+    if (!input) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from DVOFLAT.INPUT");
+        return NULL;
+    }
+
+    // add recipe options supplied on command line
+    // psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, RECIPE_NAME);
+
+    // parse the options from the metadata format to the dvoApplyCorrOptions structure
+    dvoApplyCorrOptions *options = dvoApplyCorrOptionsParse (config);
+
+    // the following files are output targets
+    pmFPAfile *output = pmFPAfileDefineOutput(config, input->fpa, "DVOFLAT.OUTPUT");
+    if (!output) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from DVOFLAT.OUTPUT"));
+        psFree(options);
+        return NULL;
+    }
+
+    // find the flat-field correction image (from command-line, config file, or detrend db)
+    status = false;
+    pmFPAfileDefineFromArgs  (&status, config, "DVOFLAT.CORR", "CORR");
+    psErrorClear();
+    pmFPAfileDefineFromConf  (&status, config, "DVOFLAT.CORR");
+    psErrorClear();
+    pmFPAfileDefineFromDetDB (&status, config, "DVOFLAT.CORR", input->fpa, PM_DETREND_TYPE_FLAT_CORRECTION);
+    if (!status) {
+	psError (PS_ERR_IO, false, "can't find a flat-field correction image source");
+	return NULL;
+    }
+    
+    // Chip selection: turn on only the chips specified (pass status to suppress missing-key log msg)
+    status = false;
+    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS");
+    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    if (chips->n > 0) {
+        pmFPASelectChip (output->fpa, -1, true); // deselect all chips
+        for (int i = 0; i < chips->n; i++) {
+            int chipNum = atoi(chips->data[i]);
+            if (! pmFPASelectChip(output->fpa, chipNum, false)) {
+                psError(PS_ERR_IO, false, "Chip number %d doesn't exist in camera.\n", chipNum);
+                return false;
+            }
+        }
+    }
+    psFree (chips);
+    return (options);
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrReadout.c	(revision 22322)
@@ -0,0 +1,44 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+// this function is operating on each readout, assuming there is a correspondence between
+// readouts in the input (raw) flat-field image and the correction frame.  this is not really
+// true
+
+bool dvoApplyCorrReadout (pmConfig *config, pmFPAview *view, char *inName, char *corrName) {
+    
+    bool status;
+
+    pmFPAfile *inFile = psMetadataLookupPtr (&status, config->files, inName);
+    pmFPAfile *corrFile = psMetadataLookupPtr (&status, config->files, corrName);
+    assert (inFile);
+    assert (corrFile);
+
+    pmReadout *inData = pmFPAviewThisReadout (view, inFile->fpa);
+    pmReadout *corrData = pmFPAviewThisReadout (view, corrFile->fpa);
+    assert (inData);
+    assert (corrData);
+
+    // dimensions of input image:
+    assert (inData->image->numCols == corrData->image->numCols);
+    assert (inData->image->numRows == corrData->image->numRows);
+
+    // NOTE : the output pmFPA points at the same pixels as the input pmFPA
+
+    // the corr image is a multiplicative factor
+    for (int j = 0; j < inData->image->numRows; j++) {
+	for (int i = 0; i < inData->image->numCols; i++) {
+	    inData->image->data.F32[j][i] *= corrData->image->data.F32[j][i]; 
+	}
+    }
+
+    // psFits *fits = psFitsOpen ("tmp.fits", "w");
+    // psFitsWriteImage (fits, NULL, inData->image, 0, NULL);
+    // psFitsClose (fits);
+    // exit (0);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoApplyCorrVersion.c	(revision 22322)
@@ -0,0 +1,52 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString dvoApplyCorrVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString dvoApplyCorrVersionLong(void)
+{
+    psString version = dvoApplyCorrVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
+
+void dvoApplyCorrVersionMetadata(psMetadata *metadata)
+{
+    PS_ASSERT_METADATA_NON_NULL(metadata,);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString dvoApplyCorr = dvoApplyCorrVersionLong(); // dvoApplyCorr version
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString head = NULL;               // Head string
+    psStringAppend(&head, "dvoApplyCorr processing at %s. Component information:", timeString);
+    psFree(timeString);
+
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, dvoApplyCorr, "");
+
+    psFree(head);
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(dvoApplyCorr);
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorr.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorr.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorr.c	(revision 22322)
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+int main(int argc, char **argv) {
+
+    psLibInit(NULL);
+
+    psTimerStart(TIMERNAME);
+
+    // USAGE: dvoMakeCorr (mosaic.fits) (output)
+
+    // Parse the configuration and arguments
+    pmConfig *config = dvoMakeCorrArguments (argc, argv);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse command-line arguments.\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // define recipe options
+    // define the active I/O files
+    dvoMakeCorrOptions *options = dvoMakeCorrParseCamera(config);
+    if (options == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse camera.\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // Image Loop
+    if (!dvoMakeCorrLoop(config, options)) {
+        psErrorStackPrint(stderr, "Unable to loop over input\n");
+        exit(PS_EXIT_SYS_ERROR);
+    }
+
+    psLogMsg ("ppImage", 3, "Complete dvoMakeCorr run: %f sec\n", psTimerMark(TIMERNAME));
+
+    // Cleaning up
+    dvoMakeCorrCleanup(config, options);
+
+    return PS_EXIT_SUCCESS;
+}
+
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorr.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorr.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorr.h	(revision 22322)
@@ -0,0 +1,49 @@
+#ifndef DVO_MAKE_CORR_H
+#define DVO_MAKE_CORR_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "psmodules.h"
+
+#define RECIPE_NAME "DVOCORR"		// Name of the recipe to use
+#define TIMERNAME "dvoMakeCorr"		// Name of timer
+
+// Options for dvoMakeCorr
+typedef struct {
+    int test;
+} dvoMakeCorrOptions;
+
+dvoMakeCorrOptions *dvoMakeCorrOptionsAlloc(void);
+
+dvoMakeCorrOptions *dvoMakeCorrOptionsParse(pmConfig *config);
+
+// Get the configuration
+pmConfig *dvoMakeCorrArguments(int argc, char **argv);
+
+// Determine what type of camera, and initialise
+dvoMakeCorrOptions *dvoMakeCorrParseCamera(pmConfig *config);
+
+// Loop over the input
+bool dvoMakeCorrLoop(pmConfig *config, dvoMakeCorrOptions *options);
+
+// convert low-res image to hi-res image
+bool dvoMakeCorrUnbin (pmConfig *config, pmFPAview *view, char *outName, char *inName, char *refName);
+
+// free memory, check for leaks
+void dvoMakeCorrCleanup (pmConfig *config, dvoMakeCorrOptions *options);
+
+/// Return short version information
+psString dvoMakeCorrVersion(void);
+
+/// Return long version information
+psString dvoMakeCorrVersionLong(void);
+
+/// Update the metadata with version information for all dependencies
+void dvoMakeCorrVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrArguments.c	(revision 22322)
@@ -0,0 +1,68 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+static void usage (void) {
+    fprintf(stderr, "USAGE: dvoMakeCorr -file MOSAIC.fits -ref REF.fits OUTPUT\n\n");
+    exit (2);
+}
+
+pmConfig *dvoMakeCorrArguments(int argc, char **argv)
+{
+    int argnum;
+
+    if (argc == 1) {
+        usage();
+    }
+
+    if (psArgumentGet (argc, argv, "-version")) {
+	psString version;
+	version = dvoMakeCorrVersionLong(); fprintf (stdout, "%s\n", version); psFree (version);
+	version = psModulesVersionLong();   fprintf (stdout, "%s\n", version); psFree (version);
+	version = psLibVersionLong();       fprintf (stdout, "%s\n", version); psFree (version);
+	exit (0);
+    }
+
+    // load the site-wide configuration information
+    pmConfig *config = pmConfigRead(&argc, argv, RECIPE_NAME);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // save the following additional recipe values based on command-line options
+    // these options override the DVOCORR recipe values loaded from recipe files
+    psMetadata *options = pmConfigRecipeOptions (config, RECIPE_NAME);
+
+    // XXX add options from command-line here
+
+    // drop the local view on the options (saved on config->arguments)
+    psFree (options);
+
+    // the input file is a required argument; if not found, we will exit
+    if (!pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list")) {
+        usage ();
+    }
+
+    // the input file is a required argument; if not found, we will exit
+    if (!pmConfigFileSetsMD (config->arguments, &argc, argv, "REFHEAD", "-ref", "-reflist")) {
+        usage ();
+    }
+
+    // chip selection is used to limit chips to be processed
+    if ((argnum = psArgumentGet (argc, argv, "-chip"))) {
+        psArgumentRemove (argnum, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_DATA_STRING, "",
+                          argv[argnum]);
+        psArgumentRemove (argnum, &argc, argv);
+    }
+
+    if (argc != 2) usage ();
+
+    // Add the output image (which remain on the command-line) to the arguments list
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrCleanup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrCleanup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrCleanup.c	(revision 22322)
@@ -0,0 +1,22 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "dvoMakeCorr.h"
+
+void dvoMakeCorrCleanup (pmConfig *config, dvoMakeCorrOptions *options)
+{
+    // Free memory used by dvoMakeCorrCleanup
+    psFree(options);
+    psFree(config);
+
+    // Free memory used by psModules
+    pmConceptsDone();
+    pmConfigDone();
+
+    // Free memory used by psLib
+    psLibFinalize();
+    fprintf (stderr, "Found %d leaks at %s\n", psMemCheckLeaks (0, NULL, stderr, false), "dvoMakeCorrCleanup");
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrLoop.c	(revision 22322)
@@ -0,0 +1,93 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+bool dvoMakeCorrLoop (pmConfig *config, dvoMakeCorrOptions *options) {
+
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    // psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, RECIPE_NAME);
+
+    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "DVOCORR.INPUT");
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find input data!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
+
+    // load data at FPA leve
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+	psError(PS_ERR_UNKNOWN, false, "failed IO for fpa in psphot\n");
+	psFree(view);
+        return false;
+    }
+
+    // process each chip in the FPA
+    while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
+        psLogMsg ("dvoMakeCorrLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) continue;
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) {
+            psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d in dvoMakeCorr\n", view->chip);
+	    psFree (view);
+	    return false;
+	}
+
+	// process each cell in the Chip
+        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
+            psLogMsg ("dvoMakeCorrLoop", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) continue;
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+		psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d in dvoMakeCorr\n", view->chip, view->cell);
+		psFree (view);
+		return false;
+	    }
+
+	    // process each of the readouts
+	    while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
+		if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+		    psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d, readout %d in dvoMakeCorr\n", view->chip, view->cell, view->readout);
+		    psFree (view);
+		    return false;
+		}
+		if (!readout->data_exists) continue;
+
+		// XXX put the function here which unbins the input image
+		dvoMakeCorrUnbin (config, view, "DVOCORR.OUTPUT", "DVOCORR.INPUT", "DVOCORR.REFHEAD");
+
+		if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+		    psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d, readout %d in dvoMakeCorr\n", view->chip, view->cell, view->readout);
+		    psFree (view);
+		    return false;
+		}
+	    }
+	    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+		psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d in dvoMakeCorr\n", view->chip, view->cell);
+		psFree (view);
+		return false;
+	    }
+	}
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+	    psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d in dvoMakeCorr\n", view->chip);
+	    psFree (view);
+	    return false;
+	}
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+	psError(PS_ERR_UNKNOWN, false, "failed IO for fpa in dvoMakeCorr\n");
+	psFree (view);
+	return false;
+    }
+
+    psFree (view);
+
+    // fail if we failed to handle an error
+    if (psErrorCodeLast() != PS_ERR_NONE) return false;
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrOptions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrOptions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrOptions.c	(revision 22322)
@@ -0,0 +1,28 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+static void dvoMakeCorrOptionsFree(dvoMakeCorrOptions *options)
+{
+    if (options == NULL) return;
+
+    return;
+}
+
+dvoMakeCorrOptions *dvoMakeCorrOptionsAlloc(void)
+{
+    dvoMakeCorrOptions *options = psAlloc(sizeof(dvoMakeCorrOptions));
+    psMemSetDeallocator(options, (psFreeFunc)dvoMakeCorrOptionsFree);
+
+    // Initialise options
+    options->test = 0;
+    return options;
+}
+
+dvoMakeCorrOptions *dvoMakeCorrOptionsParse(pmConfig *config)
+{
+    dvoMakeCorrOptions *options = dvoMakeCorrOptionsAlloc ();
+    return options;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrParseCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrParseCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrParseCamera.c	(revision 22322)
@@ -0,0 +1,54 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "dvoMakeCorr.h"
+
+dvoMakeCorrOptions *dvoMakeCorrParseCamera (pmConfig *config) {
+
+    // the input image defines the camera, and all recipes and options the follow
+    pmFPAfile *input = pmFPAfileDefineFromArgs (NULL, config, "DVOCORR.INPUT", "INPUT");
+    if (!input) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from DVOCORR.INPUT");
+        return NULL;
+    }
+
+    // the input image defines the camera, and all recipes and options the follow
+    pmFPAfile *refhead = pmFPAfileDefineFromArgs (NULL, config, "DVOCORR.REFHEAD", "REFHEAD");
+    if (!refhead) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from DVOCORR.REFHEAD");
+        return NULL;
+    }
+
+    // add recipe options supplied on command line
+    // psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, RECIPE_NAME);
+
+    // parse the options from the metadata format to the dvoMakeCorrOptions structure
+    dvoMakeCorrOptions *options = dvoMakeCorrOptionsParse (config);
+
+    // the following files are output targets
+    // XXX get the binning from where?
+    pmFPAfile *output = pmFPAfileDefineFromFPA(config, input->fpa, 1, 1, "DVOCORR.OUTPUT");
+    if (!output) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from DVOCORR.OUTPUT"));
+        psFree(options);
+        return NULL;
+    }
+
+    // Chip selection: turn on only the chips specified (pass status to suppress missing-key log msg)
+    bool status = false;
+    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS");
+    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    if (chips->n > 0) {
+        pmFPASelectChip (input->fpa, -1, true); // deselect all chips
+        for (int i = 0; i < chips->n; i++) {
+            int chipNum = atoi(chips->data[i]);
+            if (! pmFPASelectChip(input->fpa, chipNum, false)) {
+                psError(PS_ERR_IO, false, "Chip number %d doesn't exist in camera.\n", chipNum);
+                return false;
+            }
+        }
+    }
+    psFree (chips);
+    return (options);
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrUnbin.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrUnbin.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrUnbin.c	(revision 22322)
@@ -0,0 +1,73 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+bool dvoMakeCorrUnbin (pmConfig *config, pmFPAview *view, char *outName, char *inName, char *refName) {
+    
+    bool status;
+
+    pmFPAfile *inFile = psMetadataLookupPtr (&status, config->files, inName);
+    pmFPAfile *refFile = psMetadataLookupPtr (&status, config->files, refName);
+    pmFPAfile *outFile = psMetadataLookupPtr (&status, config->files, outName);
+    assert (inFile);
+    assert (refFile);
+    assert (outFile);
+
+    pmCell *refCell = pmFPAviewThisCell (view, refFile->fpa);
+    assert (refCell);
+
+    pmReadout *inData = pmFPAviewThisReadout (view, inFile->fpa);
+    assert (inData);
+
+    // determine the output array size based on the reference TRIMSEC values
+    psRegion *trimsec = psMetadataLookupPtr(NULL, refCell->concepts, "CELL.TRIMSEC");
+    assert (trimsec);
+
+    // I have the fine and ruff image sizes, determine the binning factor
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXruff = inData->image->numCols;
+    binning->nYruff = inData->image->numRows;
+    binning->nXfine = trimsec->x1 - trimsec->x0;
+    binning->nYfine = trimsec->y1 - trimsec->y0;
+    psImageBinningSetScale (binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkip(binning, inData->image);
+
+    // construct the supporing pmFPA/pmChip/pmCell structures
+    pmReadout *outData = pmFPAviewThisReadout (view, outFile->fpa);
+    if (outData == NULL) {
+	pmChip *inChip = pmFPAviewThisChip (view, inFile->fpa);
+	pmChip *outChip = pmFPAviewThisChip (view, outFile->fpa);
+
+	pmChipCopyStructure (outChip, inChip, binning->nXbin, binning->nYbin);
+	outData = pmFPAviewThisReadout (view, outFile->fpa);
+	assert (outData != NULL);
+    }
+
+    // generate the output (fine-scale) image array
+    psImageRecycle (outData->image, binning->nXfine, binning->nYfine, PS_TYPE_F32);
+
+    // linear interpolation to full fine scale
+    if (!psImageUnbin (outData->image, inData->image, binning)) {
+	psError (PS_ERR_UNKNOWN, true, "failed to unbin image");
+	psFree (binning);
+	return false;
+    }
+
+    // the input image is in magnitudes.  convert here to a multiplicative factor...
+    for (int j = 0; j < outData->image->numRows; j++) {
+	for (int i = 0; i < outData->image->numCols; i++) {
+	    float value = outData->image->data.F32[j][i]; 
+	    outData->image->data.F32[j][i] = pow(10.0, -0.4*value);
+	}
+    }
+
+    // psFits *fits = psFitsOpen ("tmp.fits", "w");
+    // psFitsWriteImage (fits, NULL, outData->image, 0, NULL);
+    // psFitsClose (fits);
+    // exit (0);
+
+    psFree (binning);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/dvoTools/src/dvoMakeCorrVersion.c	(revision 22322)
@@ -0,0 +1,52 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString dvoMakeCorrVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString dvoMakeCorrVersionLong(void)
+{
+    psString version = dvoMakeCorrVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
+
+void dvoMakeCorrVersionMetadata(psMetadata *metadata)
+{
+    PS_ASSERT_METADATA_NON_NULL(metadata,);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString dvoMakeCorr = dvoMakeCorrVersionLong(); // dvoMakeCorr version
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString head = NULL;               // Head string
+    psStringAppend(&head, "dvoMakeCorr processing at %s. Component information:", timeString);
+    psFree(timeString);
+
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, dvoMakeCorr, "");
+
+    psFree(head);
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(dvoMakeCorr);
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+Makefile
+Makefile.in
+aclocal.m4
+configure
+autom4te.cache
+config.log
+config.status
+install-sh
+missing
+glueforge
+glueforge.1
Index: /tags/sj_tags/sj_root_20080929/glueforge/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/INSTALL	(revision 22322)
@@ -0,0 +1,95 @@
+# $Id: INSTALL,v 1.6 2006-01-11 02:12:34 jhoblitt Exp $
+
+Using the glueforge meta-database API generator
+--
+
+- Step 0
+
+Setup your shell's environment for Pan-STARRS CVS.  If you need help setting up
+CVS please see this article:
+
+    http://pan-starrs.ifa.hawaii.edu/project/resources/ps-cvs.html
+
+- Step 1
+
+Install the PS:IPP::Metadata::Config and Template modules.
+
+# PS:IPP::Metadata::Config
+$ cvs co PS-IPP-Metadata-Config
+$ cd PS-IPP-Metadata-Config
+$ perl Build.PL
+$ ./Build
+$ ./Build test
+# if you have sudo
+$ sudo ./Build install
+# if not
+$ su
+$ ./Build install
+
+# Template
+# if you have sudo
+$ sudo perl -MCPAN -e 'install Template'
+# if not
+$ su -
+$ perl -MCPAN -e 'install Template'
+
+If you have never used CPAN before you will be given a series of prompts to
+configure the module.
+
+- Step 2
+
+Checkout the glueforge module from Pan-STARRS CVS and build it.  If you need
+help setting up CVS please see this article:
+
+    http://pan-starrs.ifa.hawaii.edu/project/resources/ps-cvs.html
+
+$ cvs co glueforge
+$ cd glueforge
+$ ./autogen.sh
+$ make
+
+- Step 3
+
+Create a MetadataConfig file that describes the table you want to access.  The
+MetadataConfig file syntax is described in the pslib SDRS.  glueforge specific
+details are included in the "glueforge" man page. For reference, both a simple
+and complex config example is provided in the examples directory.
+
+$ perldoc glueforge
+
+or
+
+$ man glueforge
+
+$ cat examples/simple.md
+
+or
+
+$ cat examples/complex.md
+
+- Step 4
+
+Generate the database bindings with the "glueforge" executable.  The example
+simple.md file will generate code under the directory "foodb".
+
+$./glueforge -t templates/psdb -i examples/simple.md
+$ cd foodb
+
+- Step 5
+
+Package the software for distribution.  This is the same as for any 'standard'
+autotools package.  Please note that you must have pslib installed for the step
+to complete.  If you need help installing pslib please see this article:
+
+    http://pan-starrs.ifa.hawaii.edu/project/IPP/software/jhbuild/
+
+$ jhbuild shell
+$ ./autogen.sh && make dist
+
+This should generate two packages suitable for distribution.
+
+$ ls -la foodb-*
+-rw-r--r--  1 jhoblitt users 216367 Jul 28 17:54 foodb-0.0.1.tar.bz2
+-rw-r--r--  1 jhoblitt users 324926 Jul 28 17:54 foodb-0.0.1.tar.gz
+
+The End.
Index: /tags/sj_tags/sj_root_20080929/glueforge/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/Makefile.am	(revision 22322)
@@ -0,0 +1,37 @@
+SUBDIRS = templates/psdb templates/latex
+
+bin_SCRIPTS = glueforge
+CLEANFILES = $(bin_SCRIPTS)
+
+do_subst = sed \
+	-e 's,[@]datadir[@],$(datadir),g' \
+	-e 's,[@]PACKAGE_NAME[@],$(PACKAGE_NAME),g' \
+	-e 's,[@]PACKAGE_VERSION[@],$(PACKAGE_VERSION),g'
+
+glueforge: glueforge.in
+	$(do_subst) < $(srcdir)/glueforge.in > glueforge
+	chmod +x glueforge
+
+example_files = \
+   examples/simple.md \
+   examples/complex.md
+
+glueforgedatadir = $(datadir)/@PACKAGE_NAME@
+exampledir = $(glueforgedatadir)/examples
+example_DATA = $(example_files)
+
+install-data-local:
+	-chmod 0755 $(glueforgedatadir) $(exampledir)
+
+man_MANS = \
+    $(top_builddir)/@PACKAGE_NAME@.1
+
+@PACKAGE@.1: glueforge
+	$(POD2MAN) glueforge > @PACKAGE@.1
+
+clean-local:
+	-rm -rf docs @PACKAGE@.1
+
+EXTRA_DIST = autogen.sh glueforge.in $(example_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/glueforge/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=glueforge
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+#($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+#        DIE=1
+#}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+#($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+#        DIE=1
+#}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+#$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL -I m4 || echo "$ACLOCAL failed"
+#$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/glueforge/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/configure.ac	(revision 22322)
@@ -0,0 +1,31 @@
+AC_PREREQ(2.59)
+
+AC_INIT([glueforge], [1.04], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([glueforge.in])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_MAINTAINER_MODE
+
+AC_PROG_INSTALL
+
+AC_PROG_PERL_MODULES(
+  [PS::IPP::Metadata::Config], ,
+  [AC_MSG_ERROR(perl module PS::IPP::Metadata::Config is required)]
+)
+
+AC_PROG_PERL_MODULES(
+  [Template], ,
+  [AC_MSG_ERROR(perl module Template is required)]
+)
+
+AC_PATH_PROG([POD2MAN], [pod2man], [missing])
+if test "$POD2MAN" = "missing" ; then
+  AC_MSG_ERROR([pod2man is required])
+fi
+
+AC_CONFIG_FILES([
+  Makefile
+  templates/latex/Makefile
+  templates/psdb/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/glueforge/examples/complex.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/examples/complex.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/examples/complex.md	(revision 22322)
@@ -0,0 +1,20 @@
+glueforge METADATA
+    pkg_name        STR  foodb
+    pkg_namespace   STR  foo
+END
+
+foo METADATA
+    foo     STR     60      # Primary Key # the name of foo thing
+    bar     S32     0       ## count of bar
+    baz     F32     0.0
+    boing   F64     0.0
+    zot     BOOL    t       # Key
+END
+
+bar METADATA
+    zot     BOOL    t       # Key
+    boing   F64     0.0
+    baz     F32     0.0
+    bar     S32     0       ## count of bar
+    foo     STR     60      # Primary Key # the name of foo thing
+END
Index: /tags/sj_tags/sj_root_20080929/glueforge/examples/simple.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/examples/simple.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/examples/simple.md	(revision 22322)
@@ -0,0 +1,6 @@
+table   STR     foo
+foo     STR     60      # Primary Key # the name of foo thing
+bar     S32     0       ## count of bar
+baz     F32     0.0
+boing   F64     0.0
+zot     BOOL    t       # Key
Index: /tags/sj_tags/sj_root_20080929/glueforge/glueforge.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/glueforge.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/glueforge.in	(revision 22322)
@@ -0,0 +1,513 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005-2006  Joshua Hoblitt
+#
+# $Id: glueforge.in,v 1.51 2006-10-20 00:55:43 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '@PACKAGE_VERSION@';
+
+use PS::IPP::Metadata::Config;
+use Template;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ( $input, $output, $template );
+GetOptions(
+    'input=s'       => \$input,
+    'output=s'      => \$output,
+    'template=s'    => \$template,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required option: --input|-i", -exitval => 2 ) unless $input;
+
+$template = "@datadir@/@PACKAGE_NAME@/templates/psdb" unless $template;
+
+die "file doesn't exist: $input" unless -e $input;
+die "file isn't readable: $input" unless -r $input;
+
+my $config = parse_config( $input );
+
+die "file doesn't not contain a valid config" unless defined $config->[0];
+
+# if the first hash has a class of "metadata" then this is a complex config.
+# Otherwise it is a simple config.
+my $complex = ($config->[0]->{class} eq "metadata") ? 1 : 0;
+
+my $data; # hashref
+if ($complex) {
+    $data = complex_config($config);
+} else {
+    $data = simple_config($config);
+}
+
+# determine output path
+$output = $data->{pkg_name} unless defined $output;
+
+my $ttconfig;
+{
+    # slurp config.pl
+    open(my $fh, "$template/config.pl")
+        or die "can't open $template/config.pl: $!";
+    $ttconfig = do {local $/; <$fh>};
+    close($fh) or die "can't close $template/config.pl: $!";
+}
+
+# configure template file paths
+my %tt;
+eval $ttconfig;
+die "can't eval contents of $template/config.pl: $@" if $@;
+
+my $mangler = Template->new({ INCLUDE_PATH => $template, EVAL_PERL => 1 });
+
+foreach my $t ( keys %tt ) {
+    $mangler->process( $t, $data, $tt{$t})
+        or die $mangler->error;
+}
+
+# make autogen.sh executable
+# permissions are in octal
+#chmod 0744, $tt{'autogen_sh.tt'}; 
+
+sub parse_config {
+    my $file = shift;
+
+    my $mdparser = PS::IPP::Metadata::Config->new;
+    $mdparser->overwrite( 1 );
+
+    open( my $data, $file ) or die "can't open file: $!";
+    my $example = do { local $/; <$data> };
+    close( $data ) or die "can't close file: $!";
+
+    my $config = $mdparser->parse( $example )
+        or die "error parsing file: $input";
+
+    return $config;
+}
+
+sub lookup_type {
+    my $type = shift;
+
+    my %primitives = map { $_ => 1 }
+            qw( S8 S16 S32 S64 U8 U16 U32 U64 F32 F64 );
+    my %times = map { $_ => 1 } qw( UTC UT1 TAI TT );
+    my %mtypes = (map({ $_ => "PS_DATA_$_" } keys %primitives),
+                  map({ $_ => "PS_DATA_TIME" } keys %times));
+    my %ctypes = (map({ $_ => "ps$_" } keys %primitives),
+                  map({ $_ => "psTime*"} keys %times));
+
+    my %ttypes = (
+        S8      => -8,
+        S16     => -16,
+        S32     => -32,
+        S64     => -64,
+        U8      => 8,
+        U16     => 16,
+        U32     => 32,
+        U64     => 64,
+        F32     => 32.32,
+        F64     => 64.64,
+        UTC     => '"0001-01-01T00:00:00Z"',
+        UT1     => '"0001-01-01T00:00:00Z"',
+        TAI     => '"0001-01-01T00:00:00Z"',
+        TT      => '"0001-01-01T00:00:00Z"',
+    );
+
+    if ($primitives{$type}) {
+        return {
+            ctype   => $ctypes{$type},
+            mtype   => $mtypes{$type},
+            test    => $ttypes{$type},
+        }; 
+    }
+    elsif ($times{$type}) {
+        return {
+            ctype   => $ctypes{$type},
+            mtype   => $mtypes{$type},
+            test    => $ttypes{$type},
+            timetype => "PS_TIME_$type",
+        }; 
+    } elsif ( $type =~ /STR|STRING/ ) {
+        return {
+            ctype   => "char*",
+            mtype   => 'PS_DATA_STRING',
+            test    => '"a string"',
+        };
+    } elsif ( $type =~ /BOOL/ ) {
+        return {
+            ctype   => "bool",
+            mtype   => 'PS_DATA_BOOL',
+            test    => 'true',
+        };
+    } else {
+        return undef;
+    }
+}
+
+sub simple_config {
+    my $config = shift;
+
+    # find table name
+    my $table_name;
+    for (my $i = 0; $i < @{$config}; $i++) {
+        my $elem = $config->[$i];
+
+        if ( $elem->{name} eq "table" ) {
+            $table_name = $elem->{value};
+
+            # delete element from array
+            splice @$config, $i, 1;
+
+            last;
+        }
+    }
+
+    my %data;
+
+    # global data
+    $data{glueforge_version}= $VERSION;
+
+    $data{pkg_name}         = $table_name . "db";
+    $data{pkg_namespace}    = $table_name;
+
+    # per table data
+    my %table;
+    $table{name}        = $table_name;
+    $table{namespace}   = $table_name;
+    $table{object_name} = $table{namespace} . "Row";
+
+    my @items;
+
+    # setup one table per nested metadata
+    my @tables;
+    foreach my $meta (@{$config}) {
+        push @tables, tabledata_from_meta($meta);
+    }
+
+=for off
+    # setup items
+    for (my $i = 0; $i < @{$config}; $i++) {
+        my $elem = $config->[$i];
+
+        my $mdtypes = lookup_type( $elem->{type} );
+        next if ! defined $mdtypes;
+
+        push @items, {
+            %{$mdtypes},
+            name    => $elem->{name},
+            type    => $elem->{type},
+#            ctype   => $mdtypes->{ctype},
+#            mtype   => $mdtypes->{mtype},
+#            test    => $mdtypes->{test},
+            comment => $elem->{comment},
+            value   => $elem->{value},
+        };
+    }
+=cut
+
+    $table{columns} = \@items;
+    $data{tables} = [ \%table ];
+
+    return \%data;
+}
+
+sub complex_config {
+    my $config = shift;
+
+    # find the glueforge metadata
+    my $glueforge_meta;
+    for (my $i = 0; $i < @{$config}; $i++) {
+        my $elem = $config->[$i];
+
+        if ($elem->{name} eq "glueforge") {
+            $glueforge_meta = $elem;
+
+            # delete element from array
+            splice @$config, $i, 1;
+
+            last;
+        }
+    }
+
+    # global data
+
+    my ($pkg_name, $pkg_namespace, $pkg_version);
+    unless (defined $glueforge_meta) {
+        # set default values
+        warn "missing glueforge METADATA, using default values";
+        $pkg_name = "foodb";
+        $pkg_namespace = "foo";
+    } else {
+        # process glueforge metadata for global data
+
+        foreach my $item (@{$glueforge_meta->{value}}) {
+            if ($item->{name} eq 'pkg_name') {
+                $pkg_name = $item->{value};
+            } elsif ($item->{name} eq 'pkg_namespace') {
+                $pkg_namespace = $item->{value};
+            } elsif ($item->{name} eq 'pkg_version') {
+                $pkg_version = $item->{value};
+            } else {
+                die "invalid glueforge METADATA key: $item->{name}";
+            }
+        }
+    }
+
+    die "pkg_name is required in complex config" unless defined $pkg_name;
+    die "pkg_namespace is required in complex config" unless defined
+        $pkg_namespace;
+    die "pkg_version is required in complex config" unless defined
+        $pkg_version;
+
+    my %data;
+    $data{glueforge_version}= $VERSION;
+    $data{pkg_name}         = $pkg_name;
+    $data{pkg_namespace}    = $pkg_namespace;
+    $data{pkg_version}      = $pkg_version;
+
+    # setup one table per nested metadata
+    my @tables;
+    foreach my $meta (@{$config}) {
+        push @tables, tabledata_from_meta($meta);
+    }
+
+    $data{tables} = \@tables;
+
+    return \%data;
+}
+
+sub tabledata_from_meta {
+    my $meta = shift;   # hashref
+
+    die "not a valid metadata" unless $meta->{class} eq 'metadata';
+
+    my $table_name = $meta->{name};
+
+    my @fields;
+    foreach my $column (@{$meta->{value}}) {
+        my $mdtypes = lookup_type( $column->{type} );
+        next if not defined $mdtypes;
+
+        push @fields, {
+            name    => $column->{name},
+            type    => $column->{type},
+#            ctype   => $mdtypes->{ctype},
+#            mtype   => $mdtypes->{mtype},
+#            test    => $mdtypes->{test},
+            comment => $column->{comment},
+            value   => $column->{value},
+            %{$mdtypes},
+        };
+    }
+
+    my %table = (
+        name        => $table_name,
+        namespace   => $table_name,
+        object_name => $table_name . "Row",
+        columns     => \@fields,
+    );
+
+    return \%table;
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+@PACKAGE_NAME@ - auto-generate Pan-STARRS IPP 'meta' database bindings
+
+=head1 SYNOPSIS
+
+    @PACKAGE_NAME@ --input <filename> [--output <dirname>] [--template <dirname>]
+
+=head1 DESCRIPTION
+
+This program generates a set of database table format specific bindings in the
+ANSI C language.  The code generated is largely just wrapper functions around
+the C<pslib> C<psDB> API.  Table information is read in from a configuration
+file in the pslib C<MetadataConfig> format.  A complete autotools configured
+package that builds both static & shared libraries is generated.
+
+=head2 Features
+
+=over 4
+
+=item * Generates a complete autoconf/automake/libtool package.
+
+=item * Generated packages include an C<autogen.sh> script.
+
+=item * Installs a pkgconfig C<.pc> data file.
+
+=item * Includes a table format specific autotest test suite.
+
+=item * Builds both shared and static libraries by default.
+
+=item * Generates man pages and HTML documentation with Doxygen.
+
+=item * Header files should be C++ safe.
+
+=item * Functions to open and close database connections.
+
+=item * Functions to create and destroy a table of the appropriate format.
+
+=item * Functions to insert and pop a row.
+
+=item * Functions to insert and pop a simple object representing a row.
+
+=item * Functions to insert, select, and pop to/from FITS files.
+
+=item * Functions to convert between row structs and pslib C<psMetadata>s.
+
+=back
+
+=head1 TABLE DESCRIPTION FORMAT
+
+There are two slightly different configuration file forms.  A C<simple> format
+for describing a single table and a C<complex> format for describing more then
+one table and/or configuring specific details as to how the package is
+generated.  The syntax for both of these forms follows the C<Configuration
+files> format as described in C<PSDC-430-007>.
+
+=head2 Simple configuration
+
+A simple config file specifies the name of the table, the name and types of all
+columns, and can optionally specify which column(s) are to be used as indexes.
+
+=head2 Example simple configuration
+
+    table   STR     foo
+    foo     STR     60      # Primary Key # the name of foo thing
+    bar     S32     0       ## count of bar
+    baz     F32     0.0
+    boing   F64     0.0
+    zot     BOOL    t       # Key
+
+The key name C<table> is reserved and specifies the name of the database table.
+It may appear any where in the file but it is most I<convenient> to have it as
+the first line of the table description.
+
+The key name C<type> is reserved and I<may not> be used.
+
+The value of a C<STR> column specifies the maximum number of characters that
+the database is required to store for that field.  The values for C<S32>,
+C<F32>, C<F64>, & C<BOOL> columns are ignored.
+
+Indexes maybe specified as comment value of either C<Primary Key> or C<Key>.
+Only one C<Primary Key> may be specified per table.  
+
+Description comments may specified as either a C<# ...> after an index
+specifier or as stand alone comment value of C<## ...>.
+
+The key name C<position> is also reserved as it is used internally by the
+database bindings.
+
+=head2 Complex configuration
+
+A complex config file consists of a series of C<METADATA> blocks.  One (and
+only one) of these blocks I<must> be named C<glueforge>. This block contains
+the global package configuration data.  Only the keys C<pkg_name>,
+C<pkg_namespace>, and C<pkg_version> are allowed in this block and they are
+both required.
+
+All other C<METADATA> blocks describe a single table with the name of the
+C<METATA> block being the tables name.  Otherwise, the format of items
+contained in a C<METADATA> block is the same as for the simple configuration.
+
+Note that nested C<METADATA> blocks are not allowed.
+
+=head2 Example complex configuration
+
+glueforge METADATA
+    pkg_name        STR  foodb 
+    pkg_namespace   STR  foo
+    pkg_version     STR  0.0.1
+END
+
+foo METADATA
+    foo     STR     60      # Primary Key # the name of foo thing
+    bar     S32     0       ## count of bar
+    baz     F32     0.0
+    boing   F64     0.0
+    zot     BOOL    t       # Key
+END
+
+bar METADATA
+    zot     BOOL    t       # Key
+    boing   F64     0.0
+    baz     F32     0.0
+    bar     S32     0       ## count of bar
+    foo     STR     60      # Primary Key # the name of foo thing
+END
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --input|-i <filename>
+
+File to read table description from.
+
+=item * --output|-o <dirname>
+
+Send the generate files to this directory name.
+
+Defaults to the name of the database table.
+
+=item * --template|-t <dirname>
+
+Directory to load template files from.
+
+Defaults to C<@datadir@/@PACKAGE_NAME@/templates/psdb>.
+
+=back
+
+=head1 BUGS
+
+The format of configuration files is not rigorously checked.  Improperly
+formatted files may not be caught and cause random (possibly silent) errors
+and/or improperly code generation.
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005-2006  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+C<pslib>, C<PSDC-430-007>. L<PS:IPP::Metadata::Config>
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/glueforge/m4/ac_prog_perl_modules.m4
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/m4/ac_prog_perl_modules.m4	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/m4/ac_prog_perl_modules.m4	(revision 22322)
@@ -0,0 +1,53 @@
+dnl @synopsis AC_PROG_PERL_MODULES([MODULES], [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+dnl
+dnl Checks to see if the the given perl modules are available. If true
+dnl the shell commands in ACTION-IF-TRUE are executed. If not the shell
+dnl commands in ACTION-IF-FALSE are run. Note if $PERL is not set (for
+dnl example by calling AC_CHECK_PROG, or AC_PATH_PROG),
+dnl AC_CHECK_PROG(PERL, perl, perl) will be run.
+dnl
+dnl Example:
+dnl
+dnl   AC_CHECK_PERL_MODULES(Text::Wrap Net::LDAP, ,
+dnl                         AC_MSG_WARN(Need some Perl modules)
+dnl
+dnl @category InstalledPackages
+dnl @author Dean Povey <povey@wedgetail.com>
+dnl @version 2002-09-25
+dnl @license AllPermissive
+
+AC_DEFUN([AC_PROG_PERL_MODULES],[
+    ac_perl_modules="$1"
+    # Make sure we have perl
+    if test -z "$PERL"; then
+        AC_CHECK_PROG(PERL,perl,perl)
+    fi
+
+    if test "x$PERL" != x; then
+        ac_perl_modules_failed=0
+        for ac_perl_module in $ac_perl_modules; do
+            AC_MSG_CHECKING(for perl module $ac_perl_module)
+
+            # Would be nice to log result here, but can't rely on autoconf
+            # internals
+            $PERL "-M$ac_perl_module" -e exit > /dev/null 2>&1
+            if test $? -ne 0; then
+                AC_MSG_RESULT(no);
+                ac_perl_modules_failed=1
+            else
+                AC_MSG_RESULT(ok);
+            fi
+        done
+
+        # Run optional shell commands
+        if test "$ac_perl_modules_failed" = 0; then
+            :
+            $2
+        else
+            :
+            $3
+        fi
+    else
+        AC_MSG_WARN(could not find perl)
+    fi
+])
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/Makefile.am	(revision 22322)
@@ -0,0 +1,12 @@
+template_files = \
+    config.pl \
+    tables.tt 
+
+templatedir = $(datadir)/@PACKAGE_NAME@/templates
+latextemplatedir = $(templatedir)/latex
+latextemplate_DATA = $(template_files)
+
+install-data-hook:
+	chmod 0755 $(templatedir) $(latextemplatedir)
+
+EXTRA_DIST = $(template_files)
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/config.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/config.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/config.pl	(revision 22322)
@@ -0,0 +1,3 @@
+%tt = (
+    'tables.tt'         => "$output/tables.tex",
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/tables.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/tables.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/latex/tables.tt	(revision 22322)
@@ -0,0 +1,21 @@
+[% FOREACH table = tables -%]
+[% PERL %] print "\\clearpage\n\n" if not [% loop.count %] % 10 [% END -%]
+\begin{table}[ht]
+\begin{center}
+\caption{\label{[% table.namespace %]}[% table.namespace.replace('_', '\\_') %]}
+\begin{tabular}{|l|l|l|}
+\hline
+\textbf{name}   & \textbf{type} & \textbf{description} \\
+\hline
+[% FOREACH item = table.columns -%]
+[%- IF item.type == 'STR' -%]
+[% item.name.replace('_', '\\_') %] & [% item.type %] (up to [% item.value %] chars) & \parbox[t]{3.0in}{[% item.comment.replace('\#', '') %]} \\ \hline
+[% ELSE -%] 
+[% item.name.replace('_', '\\_') %] & [% item.type %] & \parbox[t]{3.0in}{[% item.comment.replace('\#', '') %]} \\ \hline
+[% END -%]
+[% END -%]
+\end{tabular}
+\end{center}
+\end{table}
+
+[% END %]
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/COPYING
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/COPYING	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/COPYING	(revision 22322)
@@ -0,0 +1,345 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/Makefile.am	(revision 22322)
@@ -0,0 +1,97 @@
+template_files = \
+	COPYING \
+	metadata \
+	metadata_from_params \
+	metadata_from_struct \
+	lookup_from_metadata \
+    alloc.tt \
+    alloc_at.tt \
+    alloc_c.tt \
+    alloc_h.tt \
+    autogen_sh.tt \
+    bootstrap_sh.tt \
+    cleanup.tt \
+    cleanup_at.tt \
+    cleanup_c.tt \
+    cleanup_h.tt \
+    code.tt \
+    config.pl \
+    configure_ac.tt \
+    createtable.tt \
+    createtable_at.tt \
+    createtable_c.tt \
+    createtable_h.tt \
+    dbcleanup_c.tt \
+    dbsetup_c.tt \
+    delete.tt \
+    delete_h.tt \
+    deleteobject.tt \
+    deleteobject_h.tt \
+    deleterowobjects.tt \
+    deleterowobjects_h.tt \
+    doxyfile_in.tt \
+    droptable.tt \
+    droptable_at.tt \
+    droptable_c.tt \
+    droptable_h.tt \
+    header.tt \
+    init.tt \
+    init_at.tt \
+    init_c.tt \
+    init_h.tt \
+    insert.tt \
+    insert_at.tt \
+    insert_c.tt \
+    insert_h.tt \
+    insertfits.tt \
+    insertfits_at.tt \
+    insertfits_c.tt \
+    insertfits_h.tt \
+    insertobject.tt \
+    insertobject_at.tt \
+    insertobject_c.tt \
+    insertobject_h.tt \
+    insertobjects.tt \
+    insertobjects_h.tt \
+    makefile.tt \
+    metadatafromobject.tt \
+    metadatafromobject_at.tt \
+    metadatafromobject_c.tt \
+    metadatafromobject_h.tt \
+    object_h.tt \
+    objectfrommetadata.tt \
+    objectfrommetadata_at.tt \
+    objectfrommetadata_c.tt \
+    objectfrommetadata_h.tt \
+    pkgconfig_pc_in.tt \
+    printmetadata.tt \
+    printmetadata_h.tt \
+    printmetadataraw.tt \
+    printmetadataraw_h.tt \
+    printmetadatas.tt \
+    printmetadatas_h.tt \
+    printmetadatasraw.tt \
+    printmetadatasraw_h.tt \
+    printobject.tt \
+    printobject_h.tt \
+    printobjects.tt \
+    printobjects_h.tt \
+    selectrowobjects.tt \
+    selectrowobjects_h.tt \
+    selectrowsfits.tt \
+    selectrowsfits_at.tt \
+    selectrowsfits_c.tt \
+    selectrowsfits_h.tt \
+    src_makefile_am.tt \
+    tests_makefile_am.tt \
+    testsuite_at.tt \
+    top_makefile_am.tt
+
+templatedir = $(datadir)/@PACKAGE_NAME@/templates
+psdbtemplatedir = $(templatedir)/psdb
+psdbtemplate_DATA = $(template_files)
+
+install-data-hook:
+	chmod 0755 $(templatedir) $(psdbtemplatedir)
+
+EXTRA_DIST = $(template_files)
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc.tt	(revision 22322)
@@ -0,0 +1,44 @@
+[% USE format -%]
+[% indented = format('%-15s %s') -%]
+static void [% table.object_name %]Free([% table.object_name %] *object);
+
+[% table.object_name %] *[% table.object_name %]Alloc(
+[%- SET i = 0 -%]
+[% FOREACH item = table.columns -%]
+[% IF i == 0 %][% i = 1 %][% ELSE %], [% END -%]
+[% IF item.type == "STR" -%]
+const char *[% item.name %]
+[%- ELSE -%]
+[% item.ctype %] [% item.name %]
+[%- END -%]
+[% END -%]
+)
+{
+    [% indented(table.object_name, "*_object") %];
+
+    _object = psAlloc(sizeof([% table.object_name %]));
+    psMemSetDeallocator(_object, (psFreeFunc)[% table.object_name %]Free);
+
+[% FOREACH item = table.columns -%]
+[% IF item.type == "STR" -%]
+    _object->[% item.name %] = psStringCopy([% item.name %]);
+[% ELSIF item.mtype == "PS_DATA_TIME" -%]
+    _object->[% item.name %] = psTimeCopy([% item.name %]);
+[% ELSE -%]
+    _object->[% item.name %] = [% item.name %];
+[% END -%]
+[% END -%]
+
+    return _object;
+}
+
+static void [% table.object_name %]Free([% table.object_name %] *object)
+{
+[% FOREACH item = table.columns -%]
+[% IF item.type == "STR" -%]
+    psFree(object->[% item.name %]);
+[% ELSIF item.mtype == "PS_DATA_TIME" -%]
+    psFree(object->[% item.name %]);
+[% END -%]
+[% END -%]
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc_at.tt	(revision 22322)
@@ -0,0 +1,6 @@
+AT_SETUP([[% table.object_name %]Alloc()])
+AT_KEYWORDS([[% table.object_name %]Alloc])
+
+AT_CHECK([alloc])
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc_c.tt	(revision 22322)
@@ -0,0 +1,54 @@
+[% USE format -%]
+[% indented = format('%-15s %s') -%]
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_STRING_LENGTH 1024
+
+int main ()
+{
+[% FOREACH table = tables -%]
+    {
+        [% indented(table.object_name, "*object") %];
+
+        object = [% table.object_name %]Alloc(
+[%- SET i = 0 -%]
+[%- FOREACH item = table.columns -%]
+        [%- IF i == 0 -%][%- i = 1 -%][%- ELSE -%], [% END -%] [%- item.test -%]
+[%- END -%]
+    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+[% FOREACH item = table.columns -%]
+[% IF item.type == "U8"
+   or item.type == "U16"
+   or item.type == "U32"
+   or item.type == "U64"
+   or item.type == "S8"
+   or item.type == "S16"
+   or item.type == "S32"
+   or item.type == "S64"
+   or item.type == "F32"
+   or item.type == "F64"
+   or item.type == "BOOL" 
+-%]
+        if (!object->[% item.name %] == [% item.test %]) {
+[% ELSIF item.type == "STR" -%]
+        if (strncmp(object->[% item.name %], [% item.test %], MAX_STRING_LENGTH)) {
+[% END -%]
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+[% END -%]
+
+        psFree(object);
+    }
+
+[% END -%]
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/alloc_h.tt	(revision 22322)
@@ -0,0 +1,18 @@
+[% USE format -%]
+[% indented = format('%-15s %s') -%]
+/** Creates a new [% table.object_name %] object
+ *
+ *  @return A new [% table.object_name %] object or NULL on failure.
+ */
+
+[% table.object_name %] *[% table.object_name %]Alloc(
+[%- SET i = 0 -%]
+[% FOREACH item = table.columns -%]
+[% IF i == 0 %][% i = 1 %][% ELSE %],[% END %]
+[% IF item.type == "STR" -%]
+    [% indented("const char", "*$item.name") %]
+[%- ELSE -%]
+    [% indented("$item.ctype", "$item.name") %]
+[%- END -%]
+[% END %]
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/autogen_sh.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/autogen_sh.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/autogen_sh.tt	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=[% pkg_name %]
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOlIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOlIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/bootstrap_sh.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/bootstrap_sh.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/bootstrap_sh.tt	(revision 22322)
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+libtoolize --automake --copy \
+&& aclocal \
+&& autoheader \
+&& automake --foreign --add-missing --copy \
+&& autoconf
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup.tt	(revision 22322)
@@ -0,0 +1,4 @@
+void [% pkg_namespace %]Cleanup(psDB *dbh)
+{
+    psDBCleanup(dbh);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup_at.tt	(revision 22322)
@@ -0,0 +1,6 @@
+AT_SETUP([[% table.namespace %]Cleanup()])
+AT_KEYWORDS([[% table.namespace %]Cleanup])
+
+AT_CHECK([cleanup])
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup_c.tt	(revision 22322)
@@ -0,0 +1,17 @@
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+
+int main ()
+{
+    psDB            *dbh;
+
+    dbh = psDBInit("localhost", "test", NULL, "test");
+    if (!dbh) {
+        exit(EXIT_FAILURE);
+    }
+
+    [% pkg_namespace %]Cleanup(dbh);
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/cleanup_h.tt	(revision 22322)
@@ -0,0 +1,6 @@
+/** Closes a database connection
+ */
+
+void [% pkg_namespace %]Cleanup(
+    psDB            *dbh                ///< Database handle
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/code.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/code.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/code.tt	(revision 22322)
@@ -0,0 +1,61 @@
+/*
+ * code.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ *
+ * This file was generated by glueforge [% glueforge_version %]
+ *
+ * Do NOT directly edit this file.
+ *
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "[% pkg_name %].h"
+
+[% FOREACH table = tables -%]
+#define [% table.namespace FILTER upper %]_TABLE_NAME "[% table.name %]"
+[% END -%]
+#define MAX_STRING_LENGTH 1024
+
+[% INCLUDE init.tt %]
+[% INCLUDE cleanup.tt %]
+[% INCLUDE printmetadata.tt %]
+[% INCLUDE printmetadataraw.tt %]
+[% INCLUDE printmetadatas.tt %]
+[% INCLUDE printmetadatasraw.tt %]
+[% FOREACH table = tables -%]
+[% INCLUDE alloc.tt %]
+[% INCLUDE createtable.tt %]
+[% INCLUDE droptable.tt %]
+[% INCLUDE insert.tt %]
+[% INCLUDE delete.tt -%]
+[% INCLUDE insertobject.tt %]
+[% INCLUDE insertobjects.tt %]
+[% INCLUDE insertfits.tt %]
+[% INCLUDE selectrowsfits.tt %]
+[% INCLUDE metadatafromobject.tt %]
+[% INCLUDE objectfrommetadata.tt -%]
+[% INCLUDE selectrowobjects.tt -%]
+[% INCLUDE deleteobject.tt -%]
+[% INCLUDE deleterowobjects.tt -%]
+[% INCLUDE printobjects.tt -%]
+[% INCLUDE printobject.tt -%]
+[% END -%]
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/config.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/config.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/config.pl	(revision 22322)
@@ -0,0 +1,26 @@
+%tt = (
+    'COPYING'               => "$output/COPYING",
+    'autogen_sh.tt'         => "$output/autogen.sh",
+    'configure_ac.tt'       => "$output/configure.ac",
+    'pkgconfig_pc_in.tt'    => "$output/$data->{pkg_name}.pc.in",
+    'doxyfile_in.tt'        => "$output/Doxyfile.in",
+    'top_makefile_am.tt'    => "$output/Makefile.am",
+    'src_makefile_am.tt'    => "$output/src/Makefile.am",
+    'header.tt'             => "$output/src/$data->{pkg_name}.h",
+    'code.tt'               => "$output/src/$data->{pkg_name}.c",
+    'tests_makefile_am.tt'  => "$output/tests/Makefile.am",
+    'testsuite_at.tt'       => "$output/tests/testsuite.at",
+    'dbsetup_c.tt'          => "$output/tests/dbsetup.c",
+    'dbcleanup_c.tt'        => "$output/tests/dbcleanup.c",
+    'alloc_c.tt'            => "$output/tests/alloc.c",
+    'init_c.tt'             => "$output/tests/init.c",
+    'cleanup_c.tt'          => "$output/tests/cleanup.c",
+    'createtable_c.tt'      => "$output/tests/createtable.c",
+    'droptable_c.tt'        => "$output/tests/droptable.c",
+    'insert_c.tt'           => "$output/tests/insert.c",
+    'insertobject_c.tt'     => "$output/tests/insertobject.c",
+    'insertfits_c.tt'       => "$output/tests/insertfits.c",
+    'selectrowsfits_c.tt'   => "$output/tests/selectrowsfits.c",
+    'metadatafromobject_c.tt' => "$output/tests/metadatafromobject.c",
+    'objectfrommetadata_c.tt' => "$output/tests/objectfrommetadata.c",
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/configure_ac.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/configure_ac.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/configure_ac.tt	(revision 22322)
@@ -0,0 +1,63 @@
+dnl
+dnl This file was generated by glueforge [% glueforge_version %]
+dnl
+dnl Do NOT directly edit this file.
+dnl
+
+AC_PREREQ(2.61)
+
+AC_INIT([[% pkg_name %]], [[% pkg_version %]], [pan-starrs.ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([[% pkg_name %].pc.in])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([config.h])
+AM_MAINTAINER_MODE
+
+AC_CONFIG_TESTDIR([tests])
+AC_CONFIG_FILES([tests/Makefile])
+AM_MISSING_PROG([AUTOM4TE], [autom4te])
+
+AC_LANG(C)
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 0.9.0]) 
+ 
+dnl check to make sure that pslib declares HAVE_PSDB
+TMP_CFLAGS=${CFLAGS}
+CFLAGS="${CFLAGS=} ${PSLIB_CFLAGS}"
+
+AC_MSG_CHECKING([pslib's psDB support])
+AC_RUN_IFELSE(
+  [AC_LANG_PROGRAM([], [dnl
+#ifndef HAVE_PSDB
+    return 1;
+#endif])],
+  [AC_MSG_RESULT([yes])],
+  [AC_MSG_FAILURE([pslib was built without psDB support (HAVE_PSDB)])])
+
+CFLAGS=${TMP_CFLAGS}
+
+
+AC_PATH_PROG([PERL], [perl], [missing])
+if test "$PERL" = "missing" ; then
+  AC_MSG_ERROR([perl is required])
+fi
+
+AC_PATH_PROG([DOXYGEN], [doxygen], [])
+AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
+
+dnl is this the best was to setup recursive CFLAGS?
+IPP_STDOPTS
+dnl -Werror causes problems on OSX as inttypes.h uses some non standard C types
+[% pkg_name %]_CFLAGS="-Wall -pedantic -fno-strict-aliasing"
+AC_SUBST([[% pkg_name %]_CFLAGS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  [% pkg_name %].pc
+  Doxyfile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable.tt	(revision 22322)
@@ -0,0 +1,10 @@
+bool [% table.namespace %]CreateTable(psDB *dbh)
+{
+[% INCLUDE metadata -%]
+
+    bool status = psDBCreateTable(dbh, [% table.namespace FILTER upper %]_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable_at.tt	(revision 22322)
@@ -0,0 +1,9 @@
+AT_SETUP([[% table.namespace %]CreateTable()])
+AT_KEYWORDS([[% table.namespace %]CreateTable])
+
+# make sure the table is not there.
+AT_CHECK([dbcleanup])
+AT_CHECK([createtable])
+AT_CHECK([dbcleanup])
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable_c.tt	(revision 22322)
@@ -0,0 +1,25 @@
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+
+int main ()
+{
+[% FOREACH table = tables -%]
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(![% table.namespace %]CreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+[% END -%]
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/createtable_h.tt	(revision 22322)
@@ -0,0 +1,8 @@
+/** Creates a new [% table.name %] table
+ *
+ * @return true on success
+ */
+
+bool [% table.namespace %]CreateTable(
+    psDB            *dbh                ///< Database handle
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/dbcleanup_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/dbcleanup_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/dbcleanup_c.tt	(revision 22322)
@@ -0,0 +1,20 @@
+#include <pslib.h>
+#include <stdlib.h>
+
+int main ()
+{
+    psDB            *dbh;
+
+    dbh = psDBInit("localhost", "test", NULL, "test", 0);
+    if (!dbh) {
+        exit(EXIT_FAILURE);
+    }
+
+[% FOREACH table = tables -%]
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS [% table.name %]");
+[% END -%]
+
+    psDBCleanup(dbh);
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/dbsetup_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/dbsetup_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/dbsetup_c.tt	(revision 22322)
@@ -0,0 +1,23 @@
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+
+int main ()
+{
+    psDB            *dbh;
+
+    dbh = psDBInit("localhost", "test", NULL, "test", 0);
+    if (!dbh) {
+        exit(EXIT_FAILURE);
+    }
+
+    // remove the table if it already exists
+[% FOREACH table = tables -%]
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS [% table.name %]");
+    [% table.namespace %]CreateTable(dbh);
+
+[% END -%]
+    psDBCleanup(dbh);
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/delete.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/delete.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/delete.tt	(revision 22322)
@@ -0,0 +1,14 @@
+long long [% table.namespace %]Delete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, [% table.namespace FILTER upper %]_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from [% table.namespace %]");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/delete_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/delete_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/delete_h.tt	(revision 22322)
@@ -0,0 +1,10 @@
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long [% table.namespace %]Delete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleteobject.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleteobject.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleteobject.tt	(revision 22322)
@@ -0,0 +1,18 @@
+bool [% table.namespace %]DeleteObject(psDB *dbh, const [% table.object_name %] *object)
+{
+    psMetadata *where = [% table.namespace %]MetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, [% table.namespace FILTER upper %]_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from [% table.namespace %]");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "[% table.object_name%] object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleteobject_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleteobject_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleteobject_h.tt	(revision 22322)
@@ -0,0 +1,12 @@
+/** Deletes a row from the database coresponding to an [% table.namespace %]
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool [% table.namespace %]DeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const [% table.object_name %] *object    ///< Object to delete
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleterowobjects.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleterowobjects.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleterowobjects.tt	(revision 22322)
@@ -0,0 +1,19 @@
+long long [% table.namespace %]DeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        [% table.object_name %] *object = objects->data[i];
+        psMetadata *where = [% table.namespace %]MetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, [% table.namespace FILTER upper %]_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from [% table.namespace %]");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleterowobjects_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleterowobjects_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/deleterowobjects_h.tt	(revision 22322)
@@ -0,0 +1,13 @@
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long [% table.namespace %]DeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/doxyfile_in.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/doxyfile_in.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/doxyfile_in.tt	(revision 22322)
@@ -0,0 +1,1219 @@
+# Doxyfile 1.4.2
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = @PACKAGE_NAME@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = @PACKAGE_VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+# @top_builddir@ doesn't work for some reason
+OUTPUT_DIRECTORY       = @builddir@/docs
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like the Qt-style comments (thus requiring an 
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation.
+
+SHOW_DIRECTORIES       = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from the 
+# version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the progam writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = @top_srcdir@/src
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+
+FILE_PATTERNS          = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = @PERL@
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that a graph may be further truncated if the graph's 
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH 
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), 
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, which results in a white background. 
+# Warning: Depending on the platform used, enabling this option may lead to 
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to 
+# read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable.tt	(revision 22322)
@@ -0,0 +1,4 @@
+bool [% table.namespace %]DropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, [% table.namespace FILTER upper %]_TABLE_NAME);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable_at.tt	(revision 22322)
@@ -0,0 +1,9 @@
+AT_SETUP([[% table.namespace %]DropTable()])
+AT_KEYWORDS([[% table.namespace %]DropTable])
+
+# make sure the table is not there.
+AT_CHECK([dbsetup])
+AT_CHECK([droptable])
+AT_CHECK([dbcleanup])
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable_c.tt	(revision 22322)
@@ -0,0 +1,25 @@
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+
+int main ()
+{
+[% FOREACH table = tables -%]
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (![% table.namespace %]DropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+[% END -%]
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/droptable_h.tt	(revision 22322)
@@ -0,0 +1,8 @@
+/** Deletes a [% table.name %] table
+ *
+ * @return true on success
+ */
+
+bool [% table.namespace %]DropTable(
+    psDB            *dbh                ///< Database handle
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/header.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/header.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/header.tt	(revision 22322)
@@ -0,0 +1,72 @@
+/*
+ * header.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ *
+ * This file was generated by glueforge [% glueforge_version %]
+ *
+ * Do NOT directly edit this file.
+ *
+ */
+
+#ifndef [% pkg_name FILTER upper %]_H
+#define [% pkg_name FILTER upper %]_H 1
+
+#include <pslib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// @addtogroup [% pkg_name %]
+/// @{
+
+[% INCLUDE init_h.tt %]
+[% INCLUDE cleanup_h.tt %]
+[% INCLUDE printmetadata_h.tt %]
+[% INCLUDE printmetadataraw_h.tt %]
+[% INCLUDE printmetadatas_h.tt %]
+[% INCLUDE printmetadatasraw_h.tt %]
+[% FOREACH table = tables -%]
+[% INCLUDE object_h.tt %]
+[% INCLUDE alloc_h.tt %]
+[% INCLUDE createtable_h.tt %]
+[% INCLUDE droptable_h.tt %]
+[% INCLUDE insert_h.tt %]
+[% INCLUDE delete_h.tt %]
+[% INCLUDE insertobject_h.tt %]
+[% INCLUDE insertobjects_h.tt %]
+[% INCLUDE insertfits_h.tt %]
+[% INCLUDE selectrowsfits_h.tt %]
+[% INCLUDE metadatafromobject_h.tt %]
+[% INCLUDE objectfrommetadata_h.tt -%]
+[% INCLUDE selectrowobjects_h.tt -%]
+[% INCLUDE deleteobject_h.tt -%]
+[% INCLUDE deleterowobjects_h.tt -%]
+[% INCLUDE printobjects_h.tt -%]
+[% INCLUDE printobject_h.tt -%]
+[% END -%]
+
+/// @}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // [% table.namespace FILTER upper %]_DB_H
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init.tt	(revision 22322)
@@ -0,0 +1,4 @@
+psDB *[% pkg_namespace %]Init(const char *host, const char *user, const char *passwd, const char *dbname, unsigned int port)
+{
+    return psDBInit(host, user, passwd, dbname, port);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init_at.tt	(revision 22322)
@@ -0,0 +1,6 @@
+AT_SETUP([[% table.namespace %]Init()])
+AT_KEYWORDS([[% table.namespace %]Init])
+
+AT_CHECK([init])
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init_c.tt	(revision 22322)
@@ -0,0 +1,17 @@
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+
+int main ()
+{
+    psDB            *dbh;
+
+    dbh = [% pkg_namespace %]Init("localhost", "test", NULL, "test");
+    if (!dbh) {
+        exit(EXIT_FAILURE);
+    }
+
+    psDBCleanup(dbh);
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/init_h.tt	(revision 22322)
@@ -0,0 +1,13 @@
+/** Opens a new database connection
+ *
+ *  @return A new psDB object if the database connection is successful or NULL
+ *  on failure.
+ */
+
+psDB *[% pkg_namespace %]Init(
+    const char      *host,              ///< Database server hostname
+    const char      *user,              ///< Database username
+    const char      *passwd,            ///< Database password
+    const char      *dbname,            ///< Database table.namespace
+    unsigned int    port                ///< Database port
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert.tt	(revision 22322)
@@ -0,0 +1,19 @@
+bool [% table.namespace %]Insert(psDB * dbh,
+[%- SET i = 0 -%]
+[% FOREACH item = table.columns -%]
+[% IF i == 0 %][% i = 1 %] [% ELSE %], [% END -%]
+[% IF item.type == "STR" -%]
+const char *[% item.name %]
+[%- ELSE -%]
+[% item.ctype %] [% item.name %]
+[%- END -%]
+[% END -%]
+)
+{
+[% INCLUDE metadata_from_params -%]
+
+    bool status = psDBInsertOneRow(dbh, [% table.namespace FILTER upper %]_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert_at.tt	(revision 22322)
@@ -0,0 +1,8 @@
+AT_SETUP([[% table.namespace %]Insert()])
+AT_KEYWORDS([[% table.namespace %]Insert])
+
+AT_CHECK([dbsetup])
+AT_CHECK([insert])
+AT_CHECK([dbcleanup])
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert_c.tt	(revision 22322)
@@ -0,0 +1,32 @@
+[% USE format -%]
+[% indented = format('%-15s %s') -%]
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+
+int main ()
+{
+[% FOREACH table = tables -%]
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (![% table.namespace %]Insert(dbh, 
+[%- SET i = 0 -%]
+[%- FOREACH item = table.columns -%]
+[%- IF i == 0 -%][%- i = 1 -%][%- ELSE -%], [% END -%] [%- item.test -%]
+[%- END -%]
+)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+[% END -%]
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insert_h.tt	(revision 22322)
@@ -0,0 +1,21 @@
+[% USE format -%]
+[% indented = format('%-15s %s') -%]
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool [% table.namespace %]Insert(
+    psDB            *dbh,               ///< Database handle
+[%- SET i = 0 -%]
+[% FOREACH item = table.columns -%]
+[% IF i == 0 %][% i = 1 %][% ELSE %],[% END %]
+[% IF item.type == "STR" -%]
+    [% indented("const char", "*$item.name") -%]
+[% ELSE -%]
+    [% indented("$item.ctype", "$item.name") -%]
+[% END -%]
+[% END %]
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits.tt	(revision 22322)
@@ -0,0 +1,34 @@
+bool [% table.namespace %]InsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  [% table.namespace FILTER upper %]_TABLE_NAME
+    if (!psFitsMoveExtName(fits, [% table.namespace FILTER upper %]_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", [% table.namespace FILTER upper %]_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, [% table.namespace FILTER upper %]_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits_at.tt	(revision 22322)
@@ -0,0 +1,15 @@
+AT_SETUP([[% table.namespace %]InsertFits()])
+AT_KEYWORDS([[% table.namespace %]InsertFits])
+
+AT_CHECK([dbsetup])
+# run insert so there is a row in the table
+AT_CHECK([insert])
+# run popfis so there is afits file to read
+AT_CHECK([selectrowsfits])
+AT_CHECK([insertfits])
+AT_CHECK([dbcleanup])
+if [ test -f "blargh" ] ; then
+    rm -f "blargh"
+fi
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits_c.tt	(revision 22322)
@@ -0,0 +1,40 @@
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define TMP_FILENAME "./blargh"
+
+int main ()
+{
+[% FOREACH table = tables -%]
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (![% table.namespace %]InsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+[% END -%]
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertfits_h.tt	(revision 22322)
@@ -0,0 +1,14 @@
+/** Insert data from a binary FITS table [% table.object_name %] into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool [% table.namespace %]InsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject.tt	(revision 22322)
@@ -0,0 +1,10 @@
+bool [% table.namespace %]InsertObject(psDB *dbh, [% table.object_name %] *object)
+{
+    return [% table.namespace %]Insert(dbh,
+[%- SET i = 0 -%]
+[%- FOREACH item = table.columns -%]
+[%- IF i == 0 -%][%- i = 1 -%] [% ELSE %], [% END -%]
+object->[% item.name -%]
+[%- END -%]
+);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject_at.tt	(revision 22322)
@@ -0,0 +1,8 @@
+AT_SETUP([[% table.namespace %]InsertObject()])
+AT_KEYWORDS([[% table.namespace %]InsertObject])
+
+AT_CHECK([dbsetup])
+AT_CHECK([insertobject])
+AT_CHECK([dbcleanup])
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject_c.tt	(revision 22322)
@@ -0,0 +1,39 @@
+[% USE format -%]
+[% indented = format('%-15s %s') -%]
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+
+int main ()
+{
+[% FOREACH table = tables -%]
+    {
+        psDB            *dbh;
+        [% indented(table.object_name, "*object") %];
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = [% table.object_name %]Alloc(
+[%- SET i = 0 -%]
+[%- FOREACH item = table.columns -%]
+    [%- IF i == 0 -%][%- i = 1 -%][%- ELSE -%], [% END -%] [%- item.test -%]
+[%- END -%]
+);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (![% table.namespace %]InsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+[% END -%]
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobject_h.tt	(revision 22322)
@@ -0,0 +1,13 @@
+[% USE format -%]
+[% indented = format('%-15s %-20s') -%]
+/** Insert a single [% table.object_name %] object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool [% table.namespace %]InsertObject(
+    psDB            *dbh,               ///< Database handle
+    [% indented(table.object_name, "*object") %]///< [% table.object_name %] object
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobjects.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobjects.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobjects.tt	(revision 22322)
@@ -0,0 +1,10 @@
+bool [% table.namespace %]InsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (![% table.namespace %]InsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobjects_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobjects_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/insertobjects_h.tt	(revision 22322)
@@ -0,0 +1,11 @@
+/** Insert an array of [% table.object_name %] object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool [% table.namespace %]InsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of [% table.object_name %] objects
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/lookup_from_metadata
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/lookup_from_metadata	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/lookup_from_metadata	(revision 22322)
@@ -0,0 +1,14 @@
+bool status = false;
+[% FOREACH item = table.columns -%]
+[% IF item.type == "STR" OR item.mtype == "PS_DATA_TIME" -%]
+    [% item.ctype %] [% item.name %] = psMetadataLookupPtr(&status, md, "[% item.name %]");
+[% ELSIF item.type == "BOOL" -%]
+    [% item.ctype %] [% item.name %] = psMetadataLookupBool(&status, md, "[% item.name %]");
+[% ELSE -%]
+    [% item.ctype %] [% item.name %] = psMetadataLookup[% item.type %](&status, md, "[% item.name %]");
+[% END -%]
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item [% item.name %]");
+        return false;
+    }
+[% END -%]
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/makefile.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/makefile.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/makefile.tt	(revision 22322)
@@ -0,0 +1,19 @@
+SHELL   = /bin/sh
+CFLAGS  = -g -O0 -pipe -fpic -Wall -pedantic -std=c99
+LIBS    = -lpslib
+CFLAGS += `mysql_config --cflags`
+LIBS   += `mysql_config --libs`
+
+
+objects=[% table.namespace %]db.o
+
+all: lib[% table.namespace %]db.so
+
+lib[% table.namespace %]db.so: $(objects)
+	$(CC) $(CFLAGS) -shared -o lib[% table.namespace %]db.so $(objects) $(LIBS)
+
+$(objects): %.o : %.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+clean:
+	$(RM) *.so *.o core test
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadata
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadata	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadata	(revision 22322)
@@ -0,0 +1,29 @@
+    psMetadata *md = psMetadataAlloc();
+[% FOREACH item = table.columns -%]
+[% IF item.comment;
+    SET item.comment = "\"$item.comment\"";
+ELSE;
+    SET item.comment = 'NULL';
+END;
+-%]
+[% IF item.type == "STR" -%]
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "[% item.name %]", [% item.mtype %], [% item.comment %], "[% item.value %]")) {
+[% ELSIF item.mtype == "PS_DATA_TIME" -%]
+[% IF DEFINED(item.value) -%]
+    [% item.ctype %] [% item.name %] = psTimeFromISO("[% item.value %]", [% item.timetype %]);
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "[% item.name %]", [% item.mtype %], [% item.comment %], [% item.name %])) {
+        psFree([% item.name %]);
+[% ELSE -%]
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "[% item.name %]", [% item.mtype %], [% item.comment %], NULL)) {
+[% END -%]
+[% ELSE -%]
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "[% item.name %]", [% item.mtype %], [% item.comment %], [% item.value %])) {
+[% END -%]
+        psError(PS_ERR_UNKNOWN, false, "failed to add item [% item.name %]");
+        psFree(md);
+        return false;
+    }
+[% IF item.mtype == "PS_DATA_TIME" AND DEFINED(item.value) -%]
+    psFree([% item.name %]);
+[% END -%]
+[% END -%]
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadata_from_params
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadata_from_params	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadata_from_params	(revision 22322)
@@ -0,0 +1,8 @@
+    psMetadata *md = psMetadataAlloc();
+[% FOREACH item = table.columns -%]
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "[% item.name %]", [% item.mtype %], NULL, [% item.name %])) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item [% item.name %]");
+        psFree(md);
+        return false;
+    }
+[% END -%]
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadata_from_struct
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadata_from_struct	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadata_from_struct	(revision 22322)
@@ -0,0 +1,8 @@
+    psMetadata *md = psMetadataAlloc();
+[% FOREACH item = table.columns -%]
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "[% item.name %]", [% item.mtype %], NULL, object->[% item.name %])) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item [% item.name %]");
+        psFree(md);
+        return false;
+    }
+[% END -%]
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject.tt	(revision 22322)
@@ -0,0 +1,6 @@
+psMetadata *[% table.namespace %]MetadataFromObject(const [% table.object_name %] *object)
+{
+[% INCLUDE metadata_from_struct %]
+
+    return md;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject_at.tt	(revision 22322)
@@ -0,0 +1,6 @@
+AT_SETUP([[% table.namespace %]MetadataFromObject()])
+AT_KEYWORDS([[% table.namespace %]MetadataFromObject])
+
+AT_CHECK([metadatafromobject])
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject_c.tt	(revision 22322)
@@ -0,0 +1,53 @@
+[% USE format -%]
+[% indented = format('%-15s %s') -%]
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_STRING_LENGTH 1024
+
+int main ()
+{
+[% FOREACH table = tables -%]
+    {
+        psMetadata      *md;
+        [% indented(table.object_name, "*object") %];
+        bool            status;
+
+        object = [% table.object_name %]Alloc(
+[%- SET i = 0 -%]
+[%- FOREACH item = table.columns -%]
+    [%- IF i == 0 -%][%- i = 1 -%][%- ELSE -%], [% END -%] [%- item.test -%]
+[%- END -%]
+);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = [% table.namespace %]MetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+[% FOREACH item = table.columns -%]
+[% IF item.type == "S32" or item.type == "F32" or item.type == "F64" -%]
+        if (!psMetadataLookup[% item.type %](&status, md, "[% item.name %]") == [% item.test %]) {
+[% ELSIF item.type == "STR" -%]
+        if (strncmp(psMetadataLookupPtr(&status, md, "[% item.name %]"), [% item.test %], MAX_STRING_LENGTH)) {
+[% ELSIF item.type == "BOOL" -%]
+        if (!psMetadataLookupBool(&status, md, "[% item.name %]") == [% item.test %]) {
+[% END -%]
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+[% END -%]
+
+        psFree(md);
+    }
+
+[% END -%]
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/metadatafromobject_h.tt	(revision 22322)
@@ -0,0 +1,10 @@
+[% USE format -%]
+[% indented = format('%-15s %-20s') -%]
+/** Convert a [% table.object_name %] into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *[% table.namespace %]MetadataFromObject(
+    [% indented("const $table.object_name", "*object") %]///< fooRow to convert into a psMetadata
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/object_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/object_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/object_h.tt	(revision 22322)
@@ -0,0 +1,17 @@
+[% USE format -%]
+[% indented = format('%-15s %s') -%]
+/** [% table.object_name %] data structure
+ *
+ * Structure for representing a single row of [% table.name %] table data.
+ */
+
+typedef struct {
+[% SET i = 0 -%]
+[% FOREACH item = table.columns -%]
+[% IF item.type == "STR" -%]
+    [% indented("char", "*$item.name") %];
+[% ELSE -%]
+    [% indented("$item.ctype", "$item.name") %];
+[% END -%]
+[% END -%]
+} [% table.object_name %];
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata.tt	(revision 22322)
@@ -0,0 +1,14 @@
+[% USE format -%]
+[% indented = format('%-15s %s') -%]
+[% table.object_name %] *[% table.namespace %]ObjectFromMetadata(psMetadata *md)
+{
+
+[% INCLUDE lookup_from_metadata -%]
+
+    return [% table.object_name %]Alloc(
+[%- SET i = 0 -%]
+[% FOREACH item = table.columns -%]
+[% IF i == 0 -%][%- i = 1 -%][% ELSE %], [% END -%]
+[% item.name %]
+[%- END -%]);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata_at.tt	(revision 22322)
@@ -0,0 +1,6 @@
+AT_SETUP([[% table.namespace %]ObjectFromMetadata()])
+AT_KEYWORDS([[% table.namespace %]ObjectFromMetadata])
+
+AT_CHECK([objectfrommetadata])
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata_c.tt	(revision 22322)
@@ -0,0 +1,55 @@
+[% USE format -%]
+[% indented = format('%-15s %s') -%]
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_STRING_LENGTH 1024
+
+int main ()
+{
+[% FOREACH table = tables -%]
+    {
+        psMetadata      *md;
+        [% indented(table.object_name, "*object") %];
+
+        md = psMetadataAlloc();
+[% FOREACH item = table.columns -%]
+[% IF item.type == "S32" or item.type == "F32" or item.type == "F64" -%]
+        if (!psMetadataAdd[% item.type %](md, PS_LIST_TAIL, "[% item.name %]", 0, NULL, [% item.test %])) {
+[% ELSIF item.type == "STR" -%]
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "[% item.name %]", 0, NULL, [% item.test %])) {
+[% ELSIF item.type == "BOOL" -%]
+        if (!psMetadataAdd(md, PS_LIST_TAIL, "[% item.name %]", PS_DATA_BOOL, NULL, [% item.test %])) {
+[% END -%]
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+[% END -%]
+
+        object = [% table.namespace %]ObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+[% FOREACH item = table.columns -%]
+[% IF item.type == "S32" or item.type == "F32" or item.type == "F64" or item.type == "BOOL" -%]
+        if (!object->[% item.name %] == [% item.test %]) {
+[% ELSIF item.type == "STR" -%]
+        if (strncmp(object->[% item.name %], [% item.test %], MAX_STRING_LENGTH)) {
+[% END -%]
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+[% END -%]
+
+        psFree(object);
+    }
+
+[% END -%]
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/objectfrommetadata_h.tt	(revision 22322)
@@ -0,0 +1,8 @@
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A [% table.object_name %] pointer or NULL on error
+ */
+
+[% table.object_name %] *[% table.namespace %]ObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/pkgconfig_pc_in.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/pkgconfig_pc_in.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/pkgconfig_pc_in.tt	(revision 22322)
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: @PACKAGE@
+Description: some library
+Version: @VERSION@
+Requires: pslib
+Libs: -L${libdir} -l@PACKAGE@
+Cflags: -I${includedir}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadata.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadata.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadata.tt	(revision 22322)
@@ -0,0 +1,15 @@
+bool [% pkg_namespace %]PrintMetadata(FILE *stream, psMetadata *md, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(md, false);
+
+    psMetadata *clean = psMetadataCopy(NULL, md);
+
+    if (![% pkg_namespace %]PrintMetadataRaw(stream, clean, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(clean);
+        return false;
+    }
+    psFree(clean);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadata_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadata_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadata_h.tt	(revision 22322)
@@ -0,0 +1,15 @@
+/** Formats and prints a metadata
+ *
+ * Any internal use fields are stripped.
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool [% pkg_namespace %]PrintMetadata(
+    FILE            *stream,            ///< a stream
+    psMetadata      *md,                ///< An array of metadata
+    bool            mdcf                ///< format as mdconfig or simple
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadataraw.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadataraw.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadataraw.tt	(revision 22322)
@@ -0,0 +1,92 @@
+bool [% pkg_namespace %]PrintMetadataRaw(FILE *stream, psMetadata *md, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(md, false);
+
+    if (mdcf) {
+        psString str = psMetadataConfigFormat(md);
+        if (!str) {
+            psError(PS_ERR_UNKNOWN, false, "failed to format data into a string");
+            psFree(str);
+            return false;
+        }
+        fprintf(stream, "%s\n", str);
+        psFree(str);
+
+        return true;
+    }
+
+#define METADATAITEM_STRIFY_CASE(ptype, format, type) \
+case ptype: \
+    psStringAppend(&str, format, item->data.type); \
+    break;
+
+    // else
+    // flatten the metadata into | separated values
+    psString str = NULL;
+    psMetadataItem *item = NULL;
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, 0, NULL);
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        switch (item->type) {
+            METADATAITEM_STRIFY_CASE(PS_DATA_S8, "%hhd", S8);
+            METADATAITEM_STRIFY_CASE(PS_DATA_S16, "%hd", S16);
+            METADATAITEM_STRIFY_CASE(PS_DATA_S32, "%d", S32);
+            METADATAITEM_STRIFY_CASE(PS_DATA_S64, "%" PRId64, S64);
+            METADATAITEM_STRIFY_CASE(PS_DATA_U8, "%hhu", U8);
+            METADATAITEM_STRIFY_CASE(PS_DATA_U16, "%hu", U16);
+            METADATAITEM_STRIFY_CASE(PS_DATA_U32, "%u", U32);
+            METADATAITEM_STRIFY_CASE(PS_DATA_U64, "%" PRIu64, U64);
+            METADATAITEM_STRIFY_CASE(PS_DATA_F32, "%f", F32);
+            METADATAITEM_STRIFY_CASE(PS_DATA_F64, "%f", F64);
+            case PS_DATA_STRING:
+                if (item->data.str) {
+                    psString tmpStr = psStringCopy(item->data.str);
+                    psStringSubstitute(&tmpStr, "_", " ");
+                    psStringAppend(&str, "%s", tmpStr);
+                    psFree(tmpStr);
+                }
+                break;
+            case PS_DATA_BOOL:
+                if (item->data.B) {
+                    psStringAppend(&str, "T");
+                } else {
+                    psStringAppend(&str, "F");
+                }
+                break;
+            case PS_DATA_METADATA:
+                if (![% pkg_namespace %]PrintMetadataRaw(stream, item->data.md, mdcf)) {
+                    psError(PS_ERR_UNKNOWN, false ,"failed to print nested metadata");
+                }
+                // a metadata is a special case. We don't want a | seperating
+                // the metadata from any other output so we need to skip the
+                // !->offEnd test
+                continue;
+            case PS_DATA_TIME:
+               // pass through NULLs as "NULL"
+                if (item->data.V) {
+                    psString time = psTimeToISO(item->data.V);
+                    psStringAppend(&str, "%s", time);
+                psFree(time);
+                } else {
+                    psStringAppend(&str, "NULL");
+                }
+                break;
+            default:
+                psError(PS_ERR_UNKNOWN, true,"unsupported psMetadataItem type");
+                psFree(iter);
+                psFree(str);
+                return false;
+        }
+        if (!iter->iter->offEnd) {
+            psStringAppend(&str, " ");
+        }
+    }
+    psFree(iter);
+
+    // if we did nothing but handle recursive metadatas str will be NULL
+    if (str) {
+        fprintf(stream, "%s\n", str);
+        psFree(str);
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadataraw_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadataraw_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadataraw_h.tt	(revision 22322)
@@ -0,0 +1,15 @@
+/** Formats and prints a metadata
+ *
+ * The metadata is printed verbatium without removing any fields.
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool [% pkg_namespace %]PrintMetadataRaw(
+    FILE            *stream,            ///< a stream
+    psMetadata      *md,                ///< An array of metadata
+    bool            mdcf                ///< format as mdconfig or simple
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatas.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatas.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatas.tt	(revision 22322)
@@ -0,0 +1,28 @@
+bool [% pkg_namespace %]PrintMetadatas(FILE *stream, psArray *mds, const char *mdname, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(mds, false);
+    PS_ASSERT_PTR_NON_NULL(mdname, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(mds); i++) {
+        psMetadata *md = psMetadataCopy(NULL, mds->data[i]);
+        if (!psMetadataAddMetadata(output, PS_LIST_TAIL, mdname, PS_META_DUPLICATE_OK, NULL, md)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add a metadata item");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+
+        psFree(md);
+    }
+
+    if (![% pkg_namespace %]PrintMetadata(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatas_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatas_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatas_h.tt	(revision 22322)
@@ -0,0 +1,16 @@
+/** Formats and prints an array of metadata
+ *
+ * Any internal use fields are stripped.
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool [% pkg_namespace %]PrintMetadatas(
+    FILE            *stream,            ///< a stream
+    psArray         *mds,               ///< An array of metadata
+    const char      *mdname,            ///< name of the metadata(s)
+    bool            mdcf                ///< format as mdconfig or simple
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatasraw.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatasraw.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatasraw.tt	(revision 22322)
@@ -0,0 +1,25 @@
+bool [% pkg_namespace %]PrintMetadatasRaw(FILE *stream, psArray *mds, const char *mdname, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(mds, false);
+    PS_ASSERT_PTR_NON_NULL(mdname, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(mds); i++) {
+        psMetadata *md = mds->data[i];
+        if (!psMetadataAddMetadata(output, PS_LIST_TAIL, mdname, PS_META_DUPLICATE_OK, NULL, md)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add a metadata item");
+            psFree(output);
+            return false;
+        }
+    }
+
+    if (![% pkg_namespace %]PrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatasraw_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatasraw_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printmetadatasraw_h.tt	(revision 22322)
@@ -0,0 +1,16 @@
+/** Formats and prints an array of metadata
+ *
+ * The metadatas are printed verbatium without removing any fields.
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool [% pkg_namespace %]PrintMetadatasRaw(
+    FILE            *stream,            ///< a stream
+    psArray         *mds,               ///< An array of metadata
+    const char      *mdname,            ///< name of the metadata(s)
+    bool            mdcf                ///< format as mdconfig or simple
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobject.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobject.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobject.tt	(revision 22322)
@@ -0,0 +1,15 @@
+bool [% table.namespace %]PrintObject(FILE *stream, [% table.object_name %] *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = [% table.namespace %]MetadataFromObject(object);
+
+    if (![% pkg_namespace %]PrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobject_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobject_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobject_h.tt	(revision 22322)
@@ -0,0 +1,13 @@
+/** Formats and prints an [% table.object_name %] object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool [% table.namespace %]PrintObject(
+    FILE            *stream,            ///< a stream
+    [% table.object_name %] *object,    ///< an [% table.object_name %] object
+    bool            mdcf                ///< format as mdconfig or simple
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobjects.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobjects.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobjects.tt	(revision 22322)
@@ -0,0 +1,31 @@
+bool [% table.namespace %]PrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = [% table.namespace %]MetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            [% table.namespace FILTER upper %]_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (![% pkg_namespace %]PrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobjects_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobjects_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/printobjects_h.tt	(revision 22322)
@@ -0,0 +1,13 @@
+/** Formats and prints an array of [% table.object_name %] objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool [% table.namespace %]PrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of [% table.object_name %] objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowobjects.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowobjects.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowobjects.tt	(revision 22322)
@@ -0,0 +1,31 @@
+psArray *[% table.namespace %]SelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, [% table.namespace FILTER upper %]_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        [% table.object_name %] *object = [% table.namespace %]ObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowobjects_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowobjects_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowobjects_h.tt	(revision 22322)
@@ -0,0 +1,12 @@
+/** Selects up to limit rows from the database and returns as [% table.object_name %] objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *[% table.namespace %]SelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits.tt	(revision 22322)
@@ -0,0 +1,20 @@
+bool [% table.namespace %]SelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, [% table.namespace FILTER upper %]_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, [% table.namespace FILTER upper %]_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits_at.tt	(revision 22322)
@@ -0,0 +1,13 @@
+AT_SETUP([[% table.namespace %]SelectRowsFits()])
+AT_KEYWORDS([[% table.namespace %]SelectRowsFits])
+
+AT_CHECK([dbsetup])
+# run insert so there is a row in the table
+AT_CHECK([insert])
+AT_CHECK([selectrowsfits])
+AT_CHECK([dbcleanup])
+if [ test -f "blargh" ] ; then
+    rm -f "blargh"
+fi
+
+AT_CLEANUP
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits_c.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits_c.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits_c.tt	(revision 22322)
@@ -0,0 +1,34 @@
+#include <pslib.h>
+#include <[% pkg_name %].h>
+#include <stdlib.h>
+
+#define TMP_FILENAME "./blargh"
+
+int main ()
+{
+[% FOREACH table = tables -%]
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (![% table.namespace %]SelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+[% END -%]
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits_h.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits_h.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/selectrowsfits_h.tt	(revision 22322)
@@ -0,0 +1,16 @@
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool [% table.namespace %]SelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/src_makefile_am.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/src_makefile_am.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/src_makefile_am.tt	(revision 22322)
@@ -0,0 +1,10 @@
+AM_CFLAGS = @[% pkg_name %]_CFLAGS@
+
+include_HEADERS = [% pkg_name %].h
+lib_LTLIBRARIES = lib[% pkg_name %].la
+lib[% pkg_name %]_la_CFLAGS  = $(PSLIB_CFLAGS) $(AM_CFLAGS)
+lib[% pkg_name %]_la_LIBS    = $(PSLIB_LIBS) $(AM_LIBS)
+lib[% pkg_name %]_la_LDFLAGS = -release $(PACKAGE_VERSION)
+lib[% pkg_name %]_la_SOURCES = \
+    [% pkg_name %].h \
+    [% pkg_name %].c
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/tests_makefile_am.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/tests_makefile_am.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/tests_makefile_am.tt	(revision 22322)
@@ -0,0 +1,64 @@
+# copied from bison-1.875d
+
+EXTRA_DIST = $(TESTSUITE_AT) testsuite package.m4
+
+DISTCLEANFILES       = atconfig $(check_SCRIPTS)
+MAINTAINERCLEANFILES = Makefile.in $(TESTSUITE)
+
+## ------------ ##
+## package.m4.  ##
+## ------------ ##
+
+$(srcdir)/package.m4: $(top_srcdir)/configure.ac
+	{					\
+	  echo '# Signature of the current package.'; \
+	  echo 'm4_define([AT_PACKAGE_NAME],      [@PACKAGE_NAME@])'; \
+	  echo 'm4_define([AT_PACKAGE_TARNAME],   [@PACKAGE_TARNAME@])'; \
+	  echo 'm4_define([AT_PACKAGE_VERSION],   [@PACKAGE_VERSION@])'; \
+	  echo 'm4_define([AT_PACKAGE_STRING],    [@PACKAGE_STRING@])'; \
+	  echo 'm4_define([AT_PACKAGE_BUGREPORT], [@PACKAGE_BUGREPORT@])'; \
+	} >$(srcdir)/package.m4
+
+## ------------ ##
+## Test suite.  ##
+## ------------ ##
+
+TESTSUITE = $(srcdir)/testsuite
+
+AUTOTEST = $(AUTOM4TE) --language=autotest
+$(TESTSUITE): package.m4 $(TESTSUITE_AT)
+	$(AUTOTEST) -I $(srcdir) testsuite.at -o $@.tmp
+	mv $@.tmp $@
+
+atconfig: $(top_builddir)/config.status
+	cd $(top_builddir) && ./config.status tests/$@
+
+clean-local:
+	test ! -f $(TESTSUITE) || $(SHELL) $(TESTSUITE) --clean
+
+check-local: atconfig $(TESTSUITE)
+	$(SHELL) $(TESTSUITE)
+
+#
+# END BISON
+#
+
+UNITS = \
+	alloc \
+	init \
+	cleanup \
+	createtable \
+    droptable \
+    insert \
+    insertobject \
+    insertfits \
+    selectrowsfits \
+    metadatafromobject \
+    objectfrommetadata
+
+AM_CPPFLAGS = -I$(top_srcdir)/src$
+AM_CFLAGS   = @[% pkg_name %]_CFLAGS@ $(PSLIB_CFLAGS)$
+AM_LDFLAGS  = $(PSLIB_LIBS)$
+LDADD       = $(top_builddir)/src/lib[% pkg_name %].la$
+
+check_PROGRAMS = dbsetup dbcleanup $(UNITS)
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/testsuite_at.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/testsuite_at.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/testsuite_at.tt	(revision 22322)
@@ -0,0 +1,27 @@
+m4_version_prereq([2.59])
+
+AT_INIT
+
+###
+[% INCLUDE alloc_at.tt %]
+###
+[% INCLUDE init_at.tt %]
+###
+[% INCLUDE cleanup_at.tt %]
+###
+[% INCLUDE createtable_at.tt %]
+###
+[% INCLUDE droptable_at.tt %]
+###
+[% INCLUDE insert_at.tt %]
+###
+[% INCLUDE insertobject_at.tt %]
+###
+[% INCLUDE insertfits_at.tt %]
+###
+[% INCLUDE selectrowsfits_at.tt %]
+###
+[% INCLUDE metadatafromobject_at.tt %]
+###
+[% INCLUDE objectfrommetadata_at.tt %]
+###
Index: /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/top_makefile_am.tt
===================================================================
--- /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/top_makefile_am.tt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/glueforge/templates/psdb/top_makefile_am.tt	(revision 22322)
@@ -0,0 +1,30 @@
+SUBDIRS = src tests
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= [% pkg_name %].pc
+
+EXTRA_DIST = [% pkg_name %].pc.in Doxyfile.in
+
+if HAVE_DOXYGEN
+
+man_MANS = \
+    $(top_builddir)/docs/man/man3/[% pkg_name %].3 \
+[%- FOREACH table = tables %]
+[% IF NOT loop.last -%]
+    $(top_builddir)/docs/man/man3/[% table.object_name %].3 \
+[%- ELSE -%]
+    $(top_builddir)/docs/man/man3/[% table.object_name %].3 
+[% END -%]
+[% END %]
+
+docs/man/man3/[% pkg_name %].3
+[%- FOREACH table = tables -%]
+ docs/man/man3/[% table.object_name %].3
+[%- IF loop.last %]:[% END -%]
+[%- END %]
+	$(DOXYGEN)
+
+endif
+
+clean-local:
+	-rm -rf docs
Index: /tags/sj_tags/sj_root_20080929/icd-demo/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+bin fits
Index: /tags/sj_tags/sj_root_20080929/icd-demo/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/Makefile	(revision 22322)
@@ -0,0 +1,70 @@
+default: icd-demo sample
+help:
+	@echo "USAGE: make pstest"
+
+CC      =       gcc
+SRC     =       src
+BIN     =       bin
+
+DESTBIN =       $(HOME)/src/bin/$(ARCH)
+
+LPSLIB  :=      $(shell pslib-config --libs)
+IPSLIB  :=      $(shell pslib-config --cflags)
+
+INCS	= 	$(IPSLIB)
+LIBS	= 	$(LPSLIB)
+CFLAGS	=	$(INCS) -std=c99 -Wall -Werror -g
+LFLAGS	=	$(LIBS) 
+
+ICDDEMO = $(BIN)/icd-demo.$(ARCH)
+
+icd-demo: $(ICDDEMO)
+
+ICDTEST = $(BIN)/icd-test.$(ARCH)
+
+icd-test: $(ICDTEST)
+
+sample:
+	@if [ ! -d fits ]; then mkdir -p fits; fi
+	cd def; ../bin/icd-demo.$(ARCH) P2.file.txt ../fits/P2.file.fits
+	cd def; ../bin/icd-demo.$(ARCH) P4S.file.txt ../fits/P4S.file.fits
+	cd def; ../bin/icd-demo.$(ARCH) P4D.file.txt ../fits/P4D.file.fits
+	cd def; ../bin/icd-demo.$(ARCH) P4Dlo.file.txt ../fits/P4Dlo.file.fits
+	cd def; ../bin/icd-demo.$(ARCH) CS.file.txt ../fits/CS.file.fits
+
+# dependancy rules for binary code #########################
+.PRECIOUS: %.$(ARCH).o
+.PRECIOUS: $(BIN)/%.$(ARCH)
+
+%.$(ARCH).o : %.c
+	$(CC) $(CFLAGS) -c $< -o $@
+
+$(BIN)/%.$(ARCH) : $(SRC)/%.$(ARCH).o
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	$(CC) $^ -o $@ $(LFLAGS)
+	@echo "done with $@"
+
+$(DESTBIN)/%: $(BIN)/%.$(ARCH)
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	rm -f $(DESTBIN)/$*
+	cp $(BIN)/$*.$(ARCH) $(DESTBIN)/$*
+
+$(INSTALL): % : $(BIN)/%.$(ARCH)
+
+%.clean :
+	rm -f $(BIN)/$*.$(ARCH)
+	rm -f $(SRC)/*.$(ARCH).o
+
+%.install:
+	make $(DESTBIN)/$*
+
+# utilities #################################################
+
+install:
+	for i in $(INSTALL); do make $$i.install; done
+
+clean:	
+	rm -f $(BIN)/*.$(ARCH)
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell0.ext.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell0.ext.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell0.ext.txt	(revision 22322)
@@ -0,0 +1,132 @@
+
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    COMMENT  STR  MULTI
+    COMMENT  STR   
+    COMMENT  STR   This HDU applies to all detections that have been analyzed
+    COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+    COMMENT  STR   must be unique within a file so as to allow matching up
+    COMMENT  STR   sources between the PSF matching and either the alternative
+    COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+    COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+    COMMENT  STR
+    EXTNAME  STR EXTENDED_SRC_PARAMS
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE              NONE   NONE  "IPP detection identifer index"
+    PET_R             TTYPE  E     ARCSECONDS           0      1  "Petrosian radius"
+    PET_MAG           TTYPE  E     MAG                -10    +30  "Petrosian magnitude"
+    PET_R50           TTYPE  E     ARCSECONDS           0      1  "Petrosian radius for 50% light"
+    PET_R90           TTYPE  E     ARCSECONDS           0      1  "Petrosian radius for 90% light"
+    RA_PET            TTYPE  D    "DECIMAL DEGREES"     0    360  "RA from Petrosian fit"
+    DEC_PET           TTYPE  D    "DECIMAL DEGREES"   -90    +90  "DEC from Petrosian fit"
+    RA_PET_SIG        TTYPE  E     ARCSECONDS           0      1  "Sigma of Petrosian fit RA"
+    DEC_PET_SIG       TTYPE  E     ARCSECONDS           0      1  "Sigma of Petrosian fit DEC"
+    PET_QF            TTYPE  E     NONE              NONE   NONE  "Petrosian coverage/quality factor"
+    DEV_R             TTYPE  E     ARCSECONDS           0      1  "deVaucouleurs radius"
+    MAG_DEV           TTYPE  E     MAG                -10    +30  "deVaucouleurs magnitude"
+    MAG_DEV_SIG       TTYPE  E     MAG                -10    +30  "Sigma of deVaucouleurs magnitude"
+    DEV_AB            TTYPE  E     NONE              NONE   NONE  "deVaucouleurs axis ratio"
+    DEV_PHI           TTYPE  E     DEGREES              0    180  "deVaucouleurs orientation angle"
+    RA_DEV            TTYPE  D    "DECIMAL DEGREES"     0    360  "RA from deVaucouleurs fit"
+    DEC_DEV           TTYPE  D    "DECIMAL DEGREES"   -90    +90  "DEC from deVaucouleurs fit"
+    RA_DEV_SIG        TTYPE  E     ARCSECONDS           0      1  "Sigma of deVaucouleurs fit RA"
+    DEC_DEV_SIG       TTYPE  E     ARCSECONDS           0      1  "Sigma of deVaucouleurs fit DEC"
+    DEV_QF            TTYPE  E     NONE              NONE   NONE  "deVaucouleurs coverage/quality factor"
+    EXP_R             TTYPE  E     ARCSECONDS           0      1  "Exponential fit model radius"
+    MAG_EXP           TTYPE  E     MAG                -10    +30  "Exponential fit model magnitude"
+    MAG__EXP_SIG      TTYPE  E     MAG                -10    +30  "Sigma of Exponential fit model magnitude"
+    EXP_AB            TTYPE  E     NONE              NONE   NONE  "Exponential fit model axis ratio"
+    EXP_PHI           TTYPE  E     DEGREES              0    180  "Exponetial fit orientation angle"
+    RA_EXP            TTYPE  D    "DECIMAL DEGREES"     0    360  "RA from Expondential model fit"
+    DEC_EXP           TTYPE  D    "DECIMAL DEGREES"   -90    +90  "DEC from Expondential model fit"
+    RA_EXP_SIG        TTYPE  E     ARCSECONDS           0      1  "Sigma of Expondential model fit RA"
+    DEC_EXP_SIG       TTYPE  E     ARCSECONDS           0      1  "Sigma of Expondential model fit DEC"
+    EXP_QF            TTYPE  E     NONE              NONE   NONE  "Exponential fit coverage/quality factor"
+    SER_R             TTYPE  E     ARCSECONDS           0      1  "Sersic fit model radius"
+    MAG_SER           TTYPE  E     MAG                -10    +30  "Sersic fit model magnitude"
+    MAG_SER_SIG       TTYPE  E     MAG                -10    +30  "Sigma of Sersic fit model magnitude"
+    SER_AB            TTYPE  E     NONE              NONE   NONE  "Sersic fit model axis ratio"
+    SER_PHI           TTYPE  E     DEGREES              0    180  "Exponetial fit orientation angle"
+    SER_NU            TTYPE  E     NONE                 0     10  "Sersic fit index nu"
+    RA_SER            TTYPE  D    "DECIMAL DEGREES"     0    360  "RA from Sersic fit"
+    DEC_SER           TTYPE  D    "DECIMAL DEGREES"   -90    +90  "DEC from Sersic fit"
+    RA_SER_SIG        TTYPE  E     ARCSECONDS           0      1  "Sigma of Sersic fit RA"
+    DEC_SER_SIG       TTYPE  E     ARCSECONDS           0      1  "Sigma of Sersic fit DEC"
+    SER_QF            TTYPE  E     NONE              NONE   NONE  "Sersic fit coverage/quality factor"
+    PCS_R             TTYPE  E     ARCSECONDS           0      1  "PSF Convolved Sersic fit model radius"
+    MAG_PSC           TTYPE  E     MAG                -10    +30  "PSF Convolved Sersic fit model magnitude"
+    MAG_PSC_SIG       TTYPE  E     MAG                -10    +30  "Sigma of PSF Convolved Sersic fit model magnitude"
+    PCS_AB            TTYPE  E     NONE              NONE   NONE  "PSF Convolved Sersic fit model axis ratio"
+    PCS_PHI           TTYPE  E     DEGREES              0    180  "Exponetial fit orientation angle"
+    PCS_NU            TTYPE  E     NONE                 0     10  "PSF Convolved Sersic fit index nu"
+    RA_PSC            TTYPE  D    "DECIMAL DEGREES"     0    360  "RA from PSF Convolved Sersic fit"
+    DEC_PSC           TTYPE  D    "DECIMAL DEGREES"   -90    +90  "DEC from PSF Convolved Sersic fit"
+    RA_PSC_SIG        TTYPE  E     ARCSECONDS           0      1  "Sigma of PSF Convolved Sersic fit RA"
+    DEC_PSC_SIG       TTYPE  E     ARCSECONDS           0      1  "Sigma of PSF Convolved Sersic fit DEC"
+    PCS_QF            TTYPE  E     NONE              NONE   NONE  "PSF Convolved Sersic coverage/quality factor"
+    WL_SIGMA          TTYPE  E     NONE              NONE   NONE  "Weak lensing sigma"
+    EPS_1             TTYPE  E     NONE              NONE   NONE  "Weak lensing eps(1)"
+    EPS_2             TTYPE  E     NONE              NONE   NONE  "Weak lensing eps(2)"
+    P_SM_1_1          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SM(1,1)"
+    P_SM_1_2          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SM(1,2)"
+    P_SM_2_1          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SM(2,1)"
+    P_SM_2_2          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SM(2,2)"
+    P_SH_1_1          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SH(1,1)"
+    P_SH_1_2          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SH(1,2)"
+    P_SH_2_1          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SH(2,1)"
+    P_SH_2_2          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SH(2,2)"
+    SB_R_1            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #1"
+    SB_R_1_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #1"
+    SB_R_1_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #1"
+    SB_R_2            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #2"
+    SB_R_2_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #2"
+    SB_R_2_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #2"
+    SB_R_3            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #3"
+    SB_R_3_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #3"
+    SB_R_3_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #3"
+    SB_R_4            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #4"
+    SB_R_4_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #4"
+    SB_R_4_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #4"
+    SB_R_5            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #5"
+    SB_R_5_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #5"
+    SB_R_5_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #5"
+    SB_R_6            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #6"
+    SB_R_6_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #6"
+    SB_R_5_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #6"
+    SB_R_7            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #7"
+    SB_R_7_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #7"
+    SB_R_7_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #7"
+    SB_R_8            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #8"
+    SB_R_8_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #8"
+    SB_R_8_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #8"
+    SB_R_9            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #9"
+    SB_R_9_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #9"
+    SB_R_9_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #9"
+    SB_R_10           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #10"
+    SB_R_10_ERR       TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #10"
+    SB_R_10_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #10"
+    SB_R_11           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #11"
+    SB_R_11_ERR       TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #11"
+    SB_R_11_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #11"
+    SB_R_12           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #12"
+    SB_R_12_RR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #12"
+    SB_R_12_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #12"
+    SB_R_13           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #13"
+    SB_R_13_ERR       TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #13"
+    SB_R_13_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #13"
+    SB_R_14           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #14"
+    SB_R_14_ERR       TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #14"
+    SB_R_14_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #14"
+    SB_R_15           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #15"
+    SB_R_15_ERR       TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #15"
+    SB_R_15_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #15"
+    LOG_A             TTYPE  E     NONE              NONE   NONE  "Log of asymmetry parameter"
+    LOG_C             TTYPE  E     NONE              NONE   NONE  "Log of concentration parameter"
+    INCL              TTYPE  E     NONE                 0    180  "Inclination"
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell0.hdu.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell0.hdu.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell0.hdu.txt	(revision 22322)
@@ -0,0 +1,93 @@
+
+# description of the chip metadata
+BLANK METADATA
+  COMMENT  STR  MULTI
+  COMMENT  STR
+  COMMENT  STR  Basic Info about this frame
+  COMMENT  STR
+  SKYCELL  S32                       # Sky Cell ID number
+  REFID    S32                       # Reference Sky Cell ID number
+  SUBTRID  S32                       # ID of Sky Cell Image subtracted 
+  FILTER   S32                       # Filter 
+  STACKID  S32                       # ID for this image stack
+  NP2IMGS  S32                       # Number of P2 images contributing
+  ANALVER  S32                       # Software Analysis Version
+  COMMENT  STR
+  COMMENT  STR  Astrometric Solution (up to linear terms)
+  COMMENT  STR
+  NASTRO   S32                  100 # Number of astrometry stars
+  CERROR   F32               10.000 # Scatter in astrometry solution (MAS)
+  CTYPE1   STR  RA---TAN            # WCS Coordinate type
+  CTYPE2   STR  DEC--TAN            # WCS Coordinate type
+  CRVAL1   F32      321.30941666667 # WCS Ref value (RA in decimal degrees)
+  CRVAL2   F32     -5.1685277777778 # WCS Ref value (DEC in decimal degrees)
+  CRPIX1   F32    -1051.17692913657 # WCS Coordinate reference pixel
+  CRPIX2   F32     2033.40939404705 # WCS Coordinate reference pixel
+  CD1_1    F32  1.13378419402790E-4 # WCS Coordinate scale matrix
+  CD1_2    F32  8.93919113249280E-8 # WCS Coordinate scale matrix
+  CD2_1    F32  -5.4105516940550E-7 # WCS Coordinate scale matrix
+  CD2_2    F32  -1.1365323690945E-4 # WCS Coordinate scale matrix
+  COMMENT  STR
+  COMMENT  STR  Higher order terms in astrometric solution
+  COMMENT  STR
+  POLYORD  S32                    3 # Polynomial Order
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 1, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 1, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 1, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 1, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 1, coefficient of Y^2
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 2, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 2, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 2, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 2, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 2, coefficient of Y^2
+  COMMENT  STR
+  COMMENT  STR  PSF Description Parameters
+  COMMENT  STR
+  PSFMOD   S32                    1 # PSF Model number
+  PSFFWHM  F32                  1.0 # PSF Full width at Half Maximum (pixels)
+  PSFWIDX  F32                  1.0 # PSF Width in X coordinate (pixels)
+  PSFWIDY  F32                  1.0 # PSF Width in Y coordinate (pixels)
+  PSFTHETA F32                  0.0 # PSF Orientation angle (degrees)
+  PSF_EX1  F32                  0.0 # PSF Extra parameter #1
+  PSF_EX2  F32                  0.0 # PSF Extra parameter #2
+  COMMENT  STR
+  COMMENT  STR  Photometry Calibration information
+  COMMENT  STR
+  COMMENT  STR   Formula for Photometry, based on keywords given in this header:
+  COMMENT  STR   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+  COMMENT  STR   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+  COMMENT  STR
+  PHOTCODE STR  GPC.00.r            # Photometry reduction code identifier
+  PHOTOCID S32                      # Numerical code for reduction code
+  PHOT_C   F32              25.7150 # Elixir zero point - measured for camera run
+  PHOT_CS  F32               0.0048 # Elixir zero point - scatter
+  PHOT_NS  S32                   19 # Elixir zero point - N stars
+  PHOT_NM  S32                    5 # Elixir zero point - N images
+  PHOT_C0  F32              25.7430 # Elixir zero point - nominal
+  PHOT_X   F32               0.0830 # Elixir zero point - color term
+  PHOT_K   F32              -0.0400 # Elixir zero point - airmass term
+  PHOT_C1  STR  i_SDSS              # Elixir zero point - color 1
+  PHOT_C2  STR  r_SDSS              # Elixir zero point - color 2
+  COMMENT  STR
+  COMMENT  STR  Some statistics on this image
+  COMMENT  STR
+  NDETECT  S32                 1000 # Number of detections
+  MAGSAT   F32                15.0  # Magnitude at which sources saturate
+  MAGCOMP  F32                22.0  # Magnitude for which frame is 95% complete
+  SKYVAL   F32              1000.0  # Median sky value on detector
+  SKYVAR   F32                 1.0  # Variance of sky values
+  BIASVAL  F32               200.0  # Mean bias level of frame
+  BIASVAR  F32                 1.0  # Variance of bias values
+  COMMENT  STR
+  COMMENT  STR  P2 images used to generate this image stack
+  COMMENT  STR 
+  COMMENT  STR
+  COMMENT  STR  Quality Assurance Flags from IPP
+  COMMENT  STR
+  QAFLAGS  STR  UnknownValue        # IP QA flags
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell0.psf.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell0.psf.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell0.psf.txt	(revision 22322)
@@ -0,0 +1,43 @@
+
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    COMMENT  STR  MULTI
+    COMMENT  STR   
+    COMMENT  STR   This HDU applies to all detections that have been analyzed
+    COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+    COMMENT  STR   must be unique within a file so as to allow matching up
+    COMMENT  STR   sources between the PSF matching and either the alternative
+    COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+    COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+    COMMENT  STR
+    EXTNAME  STR PSF_FIT_PARAMS
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    X_PSF             TTYPE  E     PIXELS              0   5000   "PSF x coordinate"                              
+    Y_PSF             TTYPE  E     PIXELS              0   5000   "PSF y coordinate"                              
+    X_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF x coordinate"                     
+    Y_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF y coordinate"                     
+    RA_PSF            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"
+    DEC_PSF           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"
+    RA_PSF_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"
+    DEC_PSF_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"
+    PSF_INST_MAG      TTYPE  E     MAG               -20      0   "PSF fit instrumental magnitude"
+    PSF_INST_MAG_SIG  TTYPE  E     MAG                 0    0.5   "Sigma of PSF instrumental magnitude"
+    PEAK_FLUX_AS_MAG  TTYPE  E     MAG               -20      0   "Peak flux expressed as magnitude"
+    SKY               TTYPE  E     ADU                 0  60000   "Sky level"
+    SKY_SIGMA         TTYPE  E     ADU                 0  60000   "Sigma of sky level"
+    STAR_GALAXY_SEP   TTYPE  E     NONE                0      1   "Star-Galaxy separator"
+    PSF_WIDTH_X       TTYPE  E     ARCSECONDS          0     20   "PSF width in x coordinate"
+    PSF_WIDTH_Y       TTYPE  E     ARCSECONDS          0     20   "PSF width in y coordinate"
+    PSF_THETA         TTYPE  E     DEGREES          -180   +180   "PSF orientation angle"
+    PSF_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"
+    CAL_PSF_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"
+    CAL_PSF_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"
+    N_FRAMES          TTYPE  I     NONE                0  10000   "Number of frames overlapping source center"
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell1.ext.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell1.ext.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell1.ext.txt	(revision 22322)
@@ -0,0 +1,132 @@
+
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    COMMENT  STR  MULTI
+    COMMENT  STR   
+    COMMENT  STR   This HDU applies to all detections that have been analyzed
+    COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+    COMMENT  STR   must be unique within a file so as to allow matching up
+    COMMENT  STR   sources between the PSF matching and either the alternative
+    COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+    COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+    COMMENT  STR
+    EXTNAME  STR EXTENDED_SRC_PARAMS
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE              NONE   NONE  "IPP detection identifer index"
+    PET_R             TTYPE  E     ARCSECONDS           0      1  "Petrosian radius"
+    PET_MAG           TTYPE  E     MAG                -10    +30  "Petrosian magnitude"
+    PET_R50           TTYPE  E     ARCSECONDS           0      1  "Petrosian radius for 50% light"
+    PET_R90           TTYPE  E     ARCSECONDS           0      1  "Petrosian radius for 90% light"
+    RA_PET            TTYPE  D    "DECIMAL DEGREES"     0    360  "RA from Petrosian fit"
+    DEC_PET           TTYPE  D    "DECIMAL DEGREES"   -90    +90  "DEC from Petrosian fit"
+    RA_PET_SIG        TTYPE  E     ARCSECONDS           0      1  "Sigma of Petrosian fit RA"
+    DEC_PET_SIG       TTYPE  E     ARCSECONDS           0      1  "Sigma of Petrosian fit DEC"
+    PET_QF            TTYPE  E     NONE              NONE   NONE  "Petrosian coverage/quality factor"
+    DEV_R             TTYPE  E     ARCSECONDS           0      1  "deVaucouleurs radius"
+    MAG_DEV           TTYPE  E     MAG                -10    +30  "deVaucouleurs magnitude"
+    MAG_DEV_SIG       TTYPE  E     MAG                -10    +30  "Sigma of deVaucouleurs magnitude"
+    DEV_AB            TTYPE  E     NONE              NONE   NONE  "deVaucouleurs axis ratio"
+    DEV_PHI           TTYPE  E     DEGREES              0    180  "deVaucouleurs orientation angle"
+    RA_DEV            TTYPE  D    "DECIMAL DEGREES"     0    360  "RA from deVaucouleurs fit"
+    DEC_DEV           TTYPE  D    "DECIMAL DEGREES"   -90    +90  "DEC from deVaucouleurs fit"
+    RA_DEV_SIG        TTYPE  E     ARCSECONDS           0      1  "Sigma of deVaucouleurs fit RA"
+    DEC_DEV_SIG       TTYPE  E     ARCSECONDS           0      1  "Sigma of deVaucouleurs fit DEC"
+    DEV_QF            TTYPE  E     NONE              NONE   NONE  "deVaucouleurs coverage/quality factor"
+    EXP_R             TTYPE  E     ARCSECONDS           0      1  "Exponential fit model radius"
+    MAG_EXP           TTYPE  E     MAG                -10    +30  "Exponential fit model magnitude"
+    MAG__EXP_SIG      TTYPE  E     MAG                -10    +30  "Sigma of Exponential fit model magnitude"
+    EXP_AB            TTYPE  E     NONE              NONE   NONE  "Exponential fit model axis ratio"
+    EXP_PHI           TTYPE  E     DEGREES              0    180  "Exponetial fit orientation angle"
+    RA_EXP            TTYPE  D    "DECIMAL DEGREES"     0    360  "RA from Expondential model fit"
+    DEC_EXP           TTYPE  D    "DECIMAL DEGREES"   -90    +90  "DEC from Expondential model fit"
+    RA_EXP_SIG        TTYPE  E     ARCSECONDS           0      1  "Sigma of Expondential model fit RA"
+    DEC_EXP_SIG       TTYPE  E     ARCSECONDS           0      1  "Sigma of Expondential model fit DEC"
+    EXP_QF            TTYPE  E     NONE              NONE   NONE  "Exponential fit coverage/quality factor"
+    SER_R             TTYPE  E     ARCSECONDS           0      1  "Sersic fit model radius"
+    MAG_SER           TTYPE  E     MAG                -10    +30  "Sersic fit model magnitude"
+    MAG_SER_SIG       TTYPE  E     MAG                -10    +30  "Sigma of Sersic fit model magnitude"
+    SER_AB            TTYPE  E     NONE              NONE   NONE  "Sersic fit model axis ratio"
+    SER_PHI           TTYPE  E     DEGREES              0    180  "Exponetial fit orientation angle"
+    SER_NU            TTYPE  E     NONE                 0     10  "Sersic fit index nu"
+    RA_SER            TTYPE  D    "DECIMAL DEGREES"     0    360  "RA from Sersic fit"
+    DEC_SER           TTYPE  D    "DECIMAL DEGREES"   -90    +90  "DEC from Sersic fit"
+    RA_SER_SIG        TTYPE  E     ARCSECONDS           0      1  "Sigma of Sersic fit RA"
+    DEC_SER_SIG       TTYPE  E     ARCSECONDS           0      1  "Sigma of Sersic fit DEC"
+    SER_QF            TTYPE  E     NONE              NONE   NONE  "Sersic fit coverage/quality factor"
+    PCS_R             TTYPE  E     ARCSECONDS           0      1  "PSF Convolved Sersic fit model radius"
+    MAG_PSC           TTYPE  E     MAG                -10    +30  "PSF Convolved Sersic fit model magnitude"
+    MAG_PSC_SIG       TTYPE  E     MAG                -10    +30  "Sigma of PSF Convolved Sersic fit model magnitude"
+    PCS_AB            TTYPE  E     NONE              NONE   NONE  "PSF Convolved Sersic fit model axis ratio"
+    PCS_PHI           TTYPE  E     DEGREES              0    180  "Exponetial fit orientation angle"
+    PCS_NU            TTYPE  E     NONE                 0     10  "PSF Convolved Sersic fit index nu"
+    RA_PSC            TTYPE  D    "DECIMAL DEGREES"     0    360  "RA from PSF Convolved Sersic fit"
+    DEC_PSC           TTYPE  D    "DECIMAL DEGREES"   -90    +90  "DEC from PSF Convolved Sersic fit"
+    RA_PSC_SIG        TTYPE  E     ARCSECONDS           0      1  "Sigma of PSF Convolved Sersic fit RA"
+    DEC_PSC_SIG       TTYPE  E     ARCSECONDS           0      1  "Sigma of PSF Convolved Sersic fit DEC"
+    PCS_QF            TTYPE  E     NONE              NONE   NONE  "PSF Convolved Sersic coverage/quality factor"
+    WL_SIGMA          TTYPE  E     NONE              NONE   NONE  "Weak lensing sigma"
+    EPS_1             TTYPE  E     NONE              NONE   NONE  "Weak lensing eps(1)"
+    EPS_2             TTYPE  E     NONE              NONE   NONE  "Weak lensing eps(2)"
+    P_SM_1_1          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SM(1,1)"
+    P_SM_1_2          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SM(1,2)"
+    P_SM_2_1          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SM(2,1)"
+    P_SM_2_2          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SM(2,2)"
+    P_SH_1_1          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SH(1,1)"
+    P_SH_1_2          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SH(1,2)"
+    P_SH_2_1          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SH(2,1)"
+    P_SH_2_2          TTYPE  E     NONE              NONE   NONE  "Weak lensing P_SH(2,2)"
+    SB_R_1            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #1"
+    SB_R_1_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #1"
+    SB_R_1_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #1"
+    SB_R_2            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #2"
+    SB_R_2_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #2"
+    SB_R_2_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #2"
+    SB_R_3            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #3"
+    SB_R_3_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #3"
+    SB_R_3_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #3"
+    SB_R_4            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #4"
+    SB_R_4_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #4"
+    SB_R_4_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #4"
+    SB_R_5            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #5"
+    SB_R_5_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #5"
+    SB_R_5_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #5"
+    SB_R_6            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #6"
+    SB_R_6_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #6"
+    SB_R_5_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #6"
+    SB_R_7            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #7"
+    SB_R_7_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #7"
+    SB_R_7_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #7"
+    SB_R_8            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #8"
+    SB_R_8_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #8"
+    SB_R_8_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #8"
+    SB_R_9            TTYPE  E     MAG                -10    +30  "Surface brightness inside R #9"
+    SB_R_9_ERR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #9"
+    SB_R_9_VAR        TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #9"
+    SB_R_10           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #10"
+    SB_R_10_ERR       TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #10"
+    SB_R_10_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #10"
+    SB_R_11           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #11"
+    SB_R_11_ERR       TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #11"
+    SB_R_11_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #11"
+    SB_R_12           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #12"
+    SB_R_12_RR        TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #12"
+    SB_R_12_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #12"
+    SB_R_13           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #13"
+    SB_R_13_ERR       TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #13"
+    SB_R_13_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #13"
+    SB_R_14           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #14"
+    SB_R_14_ERR       TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #14"
+    SB_R_14_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #14"
+    SB_R_15           TTYPE  E     MAG                -10    +30  "Surface brightness inside R #15"
+    SB_R_15_ERR       TTYPE  E     MAG                -10    +30  "Estimated error of SB inside R #15"
+    SB_R_15_VAR       TTYPE  E     MAG                -10    +30  "Estimated variance of SB inside R #15"
+    LOG_A             TTYPE  E     NONE              NONE   NONE  "Log of asymmetry parameter"
+    LOG_C             TTYPE  E     NONE              NONE   NONE  "Log of concentration parameter"
+    INCL              TTYPE  E     NONE                 0    180  "Inclination"
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell1.hdu.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell1.hdu.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell1.hdu.txt	(revision 22322)
@@ -0,0 +1,93 @@
+
+# description of the chip metadata
+BLANK METADATA
+  COMMENT  STR  MULTI
+  COMMENT  STR
+  COMMENT  STR  Basic Info about this frame
+  COMMENT  STR
+  SKYCELL  S32                       # Sky Cell ID number
+  REFID    S32                       # Reference Sky Cell ID number
+  SUBTRID  S32                       # ID of Sky Cell Image subtracted 
+  FILTER   S32                       # Filter 
+  STACKID  S32                       # ID for this image stack
+  NP2IMGS  S32                       # Number of P2 images contributing
+  ANALVER  S32                       # Software Analysis Version
+  COMMENT  STR
+  COMMENT  STR  Astrometric Solution (up to linear terms)
+  COMMENT  STR
+  NASTRO   S32                  100 # Number of astrometry stars
+  CERROR   F32               10.000 # Scatter in astrometry solution (MAS)
+  CTYPE1   STR  RA---TAN            # WCS Coordinate type
+  CTYPE2   STR  DEC--TAN            # WCS Coordinate type
+  CRVAL1   F32      321.30941666667 # WCS Ref value (RA in decimal degrees)
+  CRVAL2   F32     -5.1685277777778 # WCS Ref value (DEC in decimal degrees)
+  CRPIX1   F32    -1051.17692913657 # WCS Coordinate reference pixel
+  CRPIX2   F32     2033.40939404705 # WCS Coordinate reference pixel
+  CD1_1    F32  1.13378419402790E-4 # WCS Coordinate scale matrix
+  CD1_2    F32  8.93919113249280E-8 # WCS Coordinate scale matrix
+  CD2_1    F32  -5.4105516940550E-7 # WCS Coordinate scale matrix
+  CD2_2    F32  -1.1365323690945E-4 # WCS Coordinate scale matrix
+  COMMENT  STR
+  COMMENT  STR  Higher order terms in astrometric solution
+  COMMENT  STR
+  POLYORD  S32                    3 # Polynomial Order
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 1, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 1, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 1, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 1, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 1, coefficient of Y^2
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 2, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 2, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 2, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 2, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 2, coefficient of Y^2
+  COMMENT  STR
+  COMMENT  STR  PSF Description Parameters
+  COMMENT  STR
+  PSFMOD   S32                    1 # PSF Model number
+  PSFFWHM  F32                  1.0 # PSF Full width at Half Maximum (pixels)
+  PSFWIDX  F32                  1.0 # PSF Width in X coordinate (pixels)
+  PSFWIDY  F32                  1.0 # PSF Width in Y coordinate (pixels)
+  PSFTHETA F32                  0.0 # PSF Orientation angle (degrees)
+  PSF_EX1  F32                  0.0 # PSF Extra parameter #1
+  PSF_EX2  F32                  0.0 # PSF Extra parameter #2
+  COMMENT  STR
+  COMMENT  STR  Photometry Calibration information
+  COMMENT  STR
+  COMMENT  STR   Formula for Photometry, based on keywords given in this header:
+  COMMENT  STR   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+  COMMENT  STR   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+  COMMENT  STR
+  PHOTCODE STR  GPC.00.r            # Photometry reduction code identifier
+  PHOTOCID S32                      # Numerical code for reduction code
+  PHOT_C   F32              25.7150 # Elixir zero point - measured for camera run
+  PHOT_CS  F32               0.0048 # Elixir zero point - scatter
+  PHOT_NS  S32                   19 # Elixir zero point - N stars
+  PHOT_NM  S32                    5 # Elixir zero point - N images
+  PHOT_C0  F32              25.7430 # Elixir zero point - nominal
+  PHOT_X   F32               0.0830 # Elixir zero point - color term
+  PHOT_K   F32              -0.0400 # Elixir zero point - airmass term
+  PHOT_C1  STR  i_SDSS              # Elixir zero point - color 1
+  PHOT_C2  STR  r_SDSS              # Elixir zero point - color 2
+  COMMENT  STR
+  COMMENT  STR  Some statistics on this image
+  COMMENT  STR
+  NDETECT  S32                 1000 # Number of detections
+  MAGSAT   F32                15.0  # Magnitude at which sources saturate
+  MAGCOMP  F32                22.0  # Magnitude for which frame is 95% complete
+  SKYVAL   F32              1000.0  # Median sky value on detector
+  SKYVAR   F32                 1.0  # Variance of sky values
+  BIASVAL  F32               200.0  # Mean bias level of frame
+  BIASVAR  F32                 1.0  # Variance of bias values
+  COMMENT  STR
+  COMMENT  STR  P2 images used to generate this image stack
+  COMMENT  STR 
+  COMMENT  STR
+  COMMENT  STR  Quality Assurance Flags from IPP
+  COMMENT  STR
+  QAFLAGS  STR  UnknownValue        # IP QA flags
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell1.psf.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell1.psf.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.cell1.psf.txt	(revision 22322)
@@ -0,0 +1,43 @@
+
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    COMMENT  STR  MULTI
+    COMMENT  STR   
+    COMMENT  STR   This HDU applies to all detections that have been analyzed
+    COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+    COMMENT  STR   must be unique within a file so as to allow matching up
+    COMMENT  STR   sources between the PSF matching and either the alternative
+    COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+    COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+    COMMENT  STR
+    EXTNAME  STR PSF_FIT_PARAMS
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    X_PSF             TTYPE  E     PIXELS              0   5000   "PSF x coordinate"                              
+    Y_PSF             TTYPE  E     PIXELS              0   5000   "PSF y coordinate"                              
+    X_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF x coordinate"                     
+    Y_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF y coordinate"                     
+    RA_PSF            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"
+    DEC_PSF           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"
+    RA_PSF_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"
+    DEC_PSF_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"
+    PSF_INST_MAG      TTYPE  E     MAG               -20      0   "PSF fit instrumental magnitude"
+    PSF_INST_MAG_SIG  TTYPE  E     MAG                 0    0.5   "Sigma of PSF instrumental magnitude"
+    PEAK_FLUX_AS_MAG  TTYPE  E     MAG               -20      0   "Peak flux expressed as magnitude"
+    SKY               TTYPE  E     ADU                 0  60000   "Sky level"
+    SKY_SIGMA         TTYPE  E     ADU                 0  60000   "Sigma of sky level"
+    STAR_GALAXY_SEP   TTYPE  E     NONE                0      1   "Star-Galaxy separator"
+    PSF_WIDTH_X       TTYPE  E     ARCSECONDS          0     20   "PSF width in x coordinate"
+    PSF_WIDTH_Y       TTYPE  E     ARCSECONDS          0     20   "PSF width in y coordinate"
+    PSF_THETA         TTYPE  E     DEGREES          -180   +180   "PSF orientation angle"
+    PSF_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"
+    CAL_PSF_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"
+    CAL_PSF_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"
+    N_FRAMES          TTYPE  I     NONE                0  10000   "Number of frames overlapping source center"
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.file.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.file.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/CS.file.txt	(revision 22322)
@@ -0,0 +1,14 @@
+
+# Keywords for CSS Frame Descriptor HDU
+HEADER METADATA
+  NCELLS  S32                2 # Number of sky cell extensions
+END
+
+# the file contains table data for each chip:
+TABLE MULTI
+TABLE STR CS.cell0.hdu.txt
+TABLE STR CS.cell0.psf.txt
+TABLE STR CS.cell0.ext.txt
+TABLE STR CS.cell1.hdu.txt
+TABLE STR CS.cell1.psf.txt
+TABLE STR CS.cell1.ext.txt
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip0.alt.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip0.alt.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip0.alt.txt	(revision 22322)
@@ -0,0 +1,34 @@
+ 
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    EXTNAME STR ALT_FIT_PARAMS
+    COMMENT STR
+    COMMENT STR  This HDU applies to all detections that have been analyzed
+    COMMENT STR  with the IPP alternative PSF fitting procedure. 
+    COMMENT STR  Note that the IPP_IDET must be unique within a file so as
+    COMMENT STR  to allow matching up sources between the PSF matching and
+    COMMENT STR  either the alternative PSF fits for P2, P4S, and P4D
+    COMMENT STR  detections or the extended source fits to the
+    COMMENT STR  Image Stack/Cumulative Sky detections.
+    COMMENT STR   
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    RA_ALT            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"                                
+    DEC_ALT           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"                               
+    RA_ALT_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"                            
+    DEC_ALT_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"                           
+    ALT_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"                    
+    CAL_ALT_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"                           
+    CAL_ALT_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"                  
+    ALT_PARM_1        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 1"                  
+    ALT_PARM_2        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 2"                  
+    ALT_PARM_3        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 3"                  
+    ALT_PARM_4        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 4"                  
+    ALT_PARM_5        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 5"                  
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip0.hdu.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip0.hdu.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip0.hdu.txt	(revision 22322)
@@ -0,0 +1,100 @@
+ 
+# description of the chip metadata
+BLANK METADATA
+  COMMENT  STR  MULTI
+  COMMENT  STR
+  COMMENT  STR  Combination (P4S, P4D, Image Stack/ Cumulative Sky) Image
+  COMMENT  STR  Metadata HDU
+  COMMENT  STR  
+  OBS_ID   STR  UnknownFormat       # Observation Identifier
+  CLASSID  S32                    0 # Class ID = focal plane location
+  DETECTID S32                    0 # Numerical ID of detector
+  NX       S32                 4800 # Number of columns on the detector
+  NY       S32                 4800 # Number of rows on the detector   DATE    STR 2006/02/05
+  COMMENT  STR
+  COMMENT  STR  Astrometric Solution (up to linear terms)
+  COMMENT  STR
+  NASTRO   S32                  100 # Number of astrometry stars
+  CERROR   F32               10.000 # Scatter in astrometry solution (MAS)
+  CTYPE1   STR  RA---TAN            # WCS Coordinate type
+  CTYPE2   STR  DEC--TAN            # WCS Coordinate type
+  CRVAL1   F32      321.30941666667 # WCS Ref value (RA in decimal degrees)
+  CRVAL2   F32     -5.1685277777778 # WCS Ref value (DEC in decimal degrees)
+  CRPIX1   F32    -1051.17692913657 # WCS Coordinate reference pixel
+  CRPIX2   F32     2033.40939404705 # WCS Coordinate reference pixel
+  CD1_1    F32  1.13378419402790E-4 # WCS Coordinate scale matrix
+  CD1_2    F32  8.93919113249280E-8 # WCS Coordinate scale matrix
+  CD2_1    F32  -5.4105516940550E-7 # WCS Coordinate scale matrix
+  CD2_2    F32  -1.1365323690945E-4 # WCS Coordinate scale matrix
+  COMMENT  STR 
+  COMMENT  STR  Higher order terms in astrometric solution
+  COMMENT  STR 
+  POLYORD  S32                    3 # Polynomial Order
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 1, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 1, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 1, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 1, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 1, coefficient of Y^2
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 2, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 2, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 2, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 2, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 2, coefficient of Y^2
+  COMMENT  STR
+  COMMENT  STR  PSF Description Parameters
+  COMMENT  STR
+  PSFMOD   S32                    1 # PSF Model number
+  PSFFWHM  F32                  1.0 # PSF Full width at Half Maximum (pixels)
+  PSFWIDX  F32                  1.0 # PSF Width in X coordinate (pixels)
+  PSFWIDY  F32                  1.0 # PSF Width in Y coordinate (pixels)
+  PSFTHETA F32                  0.0 # PSF Orientation angle (degrees)
+  PSF_EX1  F32                  0.0 # PSF Extra parameter #1
+  PSF_EX2  F32                  0.0 # PSF Extra parameter #2
+  COMMENT  STR
+  COMMENT  STR  Photometry Calibration information
+  COMMENT  STR
+  COMMENT  STR   Formula for Photometry, based on keywords given in this header:
+  COMMENT  STR   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+  COMMENT  STR   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+  COMMENT  STR
+  PHOTCODE STR  GPC.00.r            # Photometry reduction code identifier
+  PHOTOCID S32                      # Numerical code for reduction code
+  PHOT_C   F32              25.7150 # Elixir zero point - measured for camera run
+  PHOT_CS  F32               0.0048 # Elixir zero point - scatter
+  PHOT_NS  S32                   19 # Elixir zero point - N stars
+  PHOT_NM  S32                    5 # Elixir zero point - N images
+  PHOT_C0  F32              25.7430 # Elixir zero point - nominal
+  PHOT_X   F32               0.0830 # Elixir zero point - color term
+  PHOT_K   F32              -0.0400 # Elixir zero point - airmass term
+  PHOT_C1  STR  i_SDSS              # Elixir zero point - color 1
+  PHOT_C2  STR  r_SDSS              # Elixir zero point - color 2
+  COMMENT  STR
+  COMMENT  STR  Some statistics on this image
+  COMMENT  STR
+  NDETECT  S32                 1000 # Number of detections
+  MAGSAT   F32                15.0  # Magnitude at which sources saturate
+  MAGCOMP  F32                22.0  # Magnitude for which frame is 95% complete
+  SKYVAL   F32              1000.0  # Median sky value on detector
+  SKYVAR   F32                 1.0  # Variance of sky values
+  BIASVAL  F32               200.0  # Mean bias level of frame
+  BIASVAR  F32                 1.0  # Variance of bias values
+  COMMENT  STR
+  COMMENT  STR  Images used to process this exposure
+  COMMENT  STR 
+  DETREND1 STR  bias.00.fits        # 
+  DETREND2 STR  dark.00.fits        # 
+  DETREND3 STR  flat.00.fits        # 
+  DETREND4 STR  fringe.01.fits      # 
+  DETREND5 STR  none                # 
+  DETREND6 STR  none                # 
+  DETREND7 STR  none                # 
+  DETREND8 STR  none                # 
+  COMMENT  STR
+  COMMENT  STR  Quality Assurance Flags from IPP
+  COMMENT  STR
+  QAFLAGS  STR  UnknownValue        # IP QA flags
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip0.psf.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip0.psf.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip0.psf.txt	(revision 22322)
@@ -0,0 +1,43 @@
+
+# extra data which belongs to table header
+FITSTABLE METADATA 
+ HEADER METADATA
+   COMMENT  STR  MULTI
+   COMMENT  STR   
+   COMMENT  STR   This HDU applies to all detections that have been analyzed
+   COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+   COMMENT  STR   must be unique within a file so as to allow matching up
+   COMMENT  STR   sources between the PSF matching and either the alternative
+   COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+   COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+   COMMENT  STR
+   EXTNAME  STR PSF_FIT_PARAMS
+ END
+
+ # description of the FITS table columns
+ COLUMNS METADATA
+   TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+   IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+   X_PSF             TTYPE  E     PIXELS              0   5000   "PSF x coordinate"                              
+   Y_PSF             TTYPE  E     PIXELS              0   5000   "PSF y coordinate"                              
+   X_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF x coordinate"                     
+   Y_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF y coordinate"                     
+   RA_PSF            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"
+   DEC_PSF           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"
+   RA_PSF_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"
+   DEC_PSF_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"
+   PSF_INST_MAG      TTYPE  E     MAG               -20      0   "PSF fit instrumental magnitude"
+   PSF_INST_MAG_SIG  TTYPE  E     MAG                 0    0.5   "Sigma of PSF instrumental magnitude"
+   PEAK_FLUX_AS_MAG  TTYPE  E     MAG               -20      0   "Peak flux expressed as magnitude"
+   SKY               TTYPE  E     ADU                 0  60000   "Sky level"
+   SKY_SIGMA         TTYPE  E     ADU                 0  60000   "Sigma of sky level"
+   STAR_GALAXY_SEP   TTYPE  E     NONE                0      1   "Star-Galaxy separator"
+   PSF_WIDTH_X       TTYPE  E     ARCSECONDS          0     20   "PSF width in x coordinate"
+   PSF_WIDTH_Y       TTYPE  E     ARCSECONDS          0     20   "PSF width in y coordinate"
+   PSF_THETA         TTYPE  E     DEGREES          -180   +180   "PSF orientation angle"
+   PSF_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"
+   CAL_PSF_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"
+   CAL_PSF_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"
+   N_FRAMES          TTYPE  I     NONE                0  10000   "Number of frames overlapping source center"
+ END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip1.alt.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip1.alt.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip1.alt.txt	(revision 22322)
@@ -0,0 +1,34 @@
+ 
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    EXTNAME STR ALT_FIT_PARAMS
+    COMMENT STR
+    COMMENT STR  This HDU applies to all detections that have been analyzed
+    COMMENT STR  with the IPP alternative PSF fitting procedure. 
+    COMMENT STR  Note that the IPP_IDET must be unique within a file so as
+    COMMENT STR  to allow matching up sources between the PSF matching and
+    COMMENT STR  either the alternative PSF fits for P2, P4S, and P4D
+    COMMENT STR  detections or the extended source fits to the
+    COMMENT STR  Image Stack/Cumulative Sky detections.
+    COMMENT STR   
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    RA_ALT            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"                                
+    DEC_ALT           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"                               
+    RA_ALT_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"                            
+    DEC_ALT_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"                           
+    ALT_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"                    
+    CAL_ALT_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"                           
+    CAL_ALT_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"                  
+    ALT_PARM_1        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 1"                  
+    ALT_PARM_2        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 2"                  
+    ALT_PARM_3        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 3"                  
+    ALT_PARM_4        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 4"                  
+    ALT_PARM_5        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 5"                  
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip1.hdu.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip1.hdu.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip1.hdu.txt	(revision 22322)
@@ -0,0 +1,99 @@
+ 
+# description of the chip metadata
+BLANK METADATA
+  COMMENT  STR
+  COMMENT  STR  Combination (P4S, P4D, Image Stack/ Cumulative Sky) Image
+  COMMENT  STR  Metadata HDU
+  COMMENT  STR  
+  OBS_ID   STR  UnknownFormat       # Observation Identifier
+  CLASSID  S32                    0 # Class ID = focal plane location
+  DETECTID S32                    0 # Numerical ID of detector
+  NX       S32                 4800 # Number of columns on the detector
+  NY       S32                 4800 # Number of rows on the detector   DATE    STR 2006/02/05
+  COMMENT  STR
+  COMMENT  STR  Astrometric Solution (up to linear terms)
+  COMMENT  STR
+  NASTRO   S32                  100 # Number of astrometry stars
+  CERROR   F32               10.000 # Scatter in astrometry solution (MAS)
+  CTYPE1   STR  RA---TAN            # WCS Coordinate type
+  CTYPE2   STR  DEC--TAN            # WCS Coordinate type
+  CRVAL1   F32      321.30941666667 # WCS Ref value (RA in decimal degrees)
+  CRVAL2   F32     -5.1685277777778 # WCS Ref value (DEC in decimal degrees)
+  CRPIX1   F32    -1051.17692913657 # WCS Coordinate reference pixel
+  CRPIX2   F32     2033.40939404705 # WCS Coordinate reference pixel
+  CD1_1    F32  1.13378419402790E-4 # WCS Coordinate scale matrix
+  CD1_2    F32  8.93919113249280E-8 # WCS Coordinate scale matrix
+  CD2_1    F32  -5.4105516940550E-7 # WCS Coordinate scale matrix
+  CD2_2    F32  -1.1365323690945E-4 # WCS Coordinate scale matrix
+  COMMENT  STR
+  COMMENT  STR  Higher order terms in astrometric solution
+  COMMENT  STR
+  POLYORD  S32                    3 # Polynomial Order
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 1, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 1, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 1, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 1, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 1, coefficient of Y^2
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 2, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 2, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 2, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 2, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 2, coefficient of Y^2
+  COMMENT  STR
+  COMMENT  STR  PSF Description Parameters
+  COMMENT  STR
+  PSFMOD   S32                    1 # PSF Model number
+  PSFFWHM  F32                  1.0 # PSF Full width at Half Maximum (pixels)
+  PSFWIDX  F32                  1.0 # PSF Width in X coordinate (pixels)
+  PSFWIDY  F32                  1.0 # PSF Width in Y coordinate (pixels)
+  PSFTHETA F32                  0.0 # PSF Orientation angle (degrees)
+  PSF_EX1  F32                  0.0 # PSF Extra parameter #1
+  PSF_EX2  F32                  0.0 # PSF Extra parameter #2
+  COMMENT  STR
+  COMMENT  STR  Photometry Calibration information
+  COMMENT  STR
+  COMMENT  STR   Formula for Photometry, based on keywords given in this header:
+  COMMENT  STR   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+  COMMENT  STR   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+  COMMENT  STR
+  PHOTCODE STR  GPC.00.r            # Photometry reduction code identifier
+  PHOTOCID S32                      # Numerical code for reduction code
+  PHOT_C   F32              25.7150 # Elixir zero point - measured for camera run
+  PHOT_CS  F32               0.0048 # Elixir zero point - scatter
+  PHOT_NS  S32                   19 # Elixir zero point - N stars
+  PHOT_NM  S32                    5 # Elixir zero point - N images
+  PHOT_C0  F32              25.7430 # Elixir zero point - nominal
+  PHOT_X   F32               0.0830 # Elixir zero point - color term
+  PHOT_K   F32              -0.0400 # Elixir zero point - airmass term
+  PHOT_C1  STR  i_SDSS              # Elixir zero point - color 1
+  PHOT_C2  STR  r_SDSS              # Elixir zero point - color 2
+  COMMENT  STR
+  COMMENT  STR  Some statistics on this image
+  COMMENT  STR
+  NDETECT  S32                 1000 # Number of detections
+  MAGSAT   F32                15.0  # Magnitude at which sources saturate
+  MAGCOMP  F32                22.0  # Magnitude for which frame is 95% complete
+  SKYVAL   F32              1000.0  # Median sky value on detector
+  SKYVAR   F32                 1.0  # Variance of sky values
+  BIASVAL  F32               200.0  # Mean bias level of frame
+  BIASVAR  F32                 1.0  # Variance of bias values
+  COMMENT  STR
+  COMMENT  STR  Images used to process this exposure
+  COMMENT  STR 
+  DETREND1 STR  bias.00.fits        # 
+  DETREND2 STR  dark.00.fits        # 
+  DETREND3 STR  flat.00.fits        # 
+  DETREND4 STR  fringe.01.fits      # 
+  DETREND5 STR  none                # 
+  DETREND6 STR  none                # 
+  DETREND7 STR  none                # 
+  DETREND8 STR  none                # 
+  COMMENT  STR
+  COMMENT  STR  Quality Assurance Flags from IPP
+  COMMENT  STR
+  QAFLAGS  STR  UnknownValue        # IP QA flags
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip1.psf.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip1.psf.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.chip1.psf.txt	(revision 22322)
@@ -0,0 +1,42 @@
+
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    COMMENT  STR   
+    COMMENT  STR   This HDU applies to all detections that have been analyzed
+    COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+    COMMENT  STR   must be unique within a file so as to allow matching up
+    COMMENT  STR   sources between the PSF matching and either the alternative
+    COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+    COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+    COMMENT  STR
+    EXTNAME  STR PSF_FIT_PARAMS
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    X_PSF             TTYPE  E     PIXELS              0   5000   "PSF x coordinate"                              
+    Y_PSF             TTYPE  E     PIXELS              0   5000   "PSF y coordinate"                              
+    X_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF x coordinate"                     
+    Y_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF y coordinate"                     
+    RA_PSF            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"
+    DEC_PSF           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"
+    RA_PSF_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"
+    DEC_PSF_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"
+    PSF_INST_MAG      TTYPE  E     MAG               -20      0   "PSF fit instrumental magnitude"
+    PSF_INST_MAG_SIG  TTYPE  E     MAG                 0    0.5   "Sigma of PSF instrumental magnitude"
+    PEAK_FLUX_AS_MAG  TTYPE  E     MAG               -20      0   "Peak flux expressed as magnitude"
+    SKY               TTYPE  E     ADU                 0  60000   "Sky level"
+    SKY_SIGMA         TTYPE  E     ADU                 0  60000   "Sigma of sky level"
+    STAR_GALAXY_SEP   TTYPE  E     NONE                0      1   "Star-Galaxy separator"
+    PSF_WIDTH_X       TTYPE  E     ARCSECONDS          0     20   "PSF width in x coordinate"
+    PSF_WIDTH_Y       TTYPE  E     ARCSECONDS          0     20   "PSF width in y coordinate"
+    PSF_THETA         TTYPE  E     DEGREES          -180   +180   "PSF orientation angle"
+    PSF_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"
+    CAL_PSF_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"
+    CAL_PSF_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"
+    N_FRAMES          TTYPE  I     NONE                0  10000   "Number of frames overlapping source center"
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.file.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.file.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P2.file.txt	(revision 22322)
@@ -0,0 +1,32 @@
+
+# Keywords for P2 Frame Descriptor HDU
+HEADER METADATA
+  TELESCOP STR  PS1                 # Telescope name
+  TELNUM   S32                    1 # Telescope number
+  CAMERA   STR  GPC1                # Camera name
+  CAMERAID S32                    1 # Camera number
+  OBS_ID   STR  UnknownFormat       # Observation identifier
+  ANALVER  S32                    0 # Analysis version number
+  P1RECIP  STR              Basic.0 # Phase 1 recipe
+  P2RECIP  STR               GPC.01 # Phase 2 recipe
+  P3RECIP  STR               GPC.00 # Phase 3 recipe
+  NFILES   F32                    1 # Number of associated files ???
+  CERROR   F32               10.000 # Scatter in astrometry solution (MAS)
+  PHOT_CS  F32                0.007 # Scater in photometric solution (mag)
+  MJDATE   F64        10000.0011223 # MJD at start of exposure
+  EXPTIME  F32                30.01 # Exposure time (seconds)
+  FILTER   STR  r.12345             # Filter identification string
+  FILTERID F32                    2 # Filter holder position
+  AIRMASS  F32                1.012 # Airmass at start of exposure
+  RA       F32            10.000000 # Telescope boresight in RA (degrees)
+  DEC      F32            -10.00000 # Telescope boresight in DEC (degrees)
+END
+
+# the file contains table data for each chip:
+TABLE MULTI
+TABLE STR P2.chip0.hdu.txt
+TABLE STR P2.chip0.psf.txt
+TABLE STR P2.chip0.alt.txt
+TABLE STR P2.chip1.hdu.txt
+TABLE STR P2.chip1.psf.txt
+TABLE STR P2.chip1.alt.txt
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.alt.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.alt.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.alt.txt	(revision 22322)
@@ -0,0 +1,34 @@
+ 
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    EXTNAME STR ALT_FIT_PARAMS
+    COMMENT STR
+    COMMENT STR  This HDU applies to all detections that have been analyzed
+    COMMENT STR  with the IPP alternative PSF fitting procedure. 
+    COMMENT STR  Note that the IPP_IDET must be unique within a file so as
+    COMMENT STR  to allow matching up sources between the PSF matching and
+    COMMENT STR  either the alternative PSF fits for P2, P4S, and P4D
+    COMMENT STR  detections or the extended source fits to the
+    COMMENT STR  Image Stack/Cumulative Sky detections.
+    COMMENT STR   
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    RA_ALT            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"                                
+    DEC_ALT           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"                               
+    RA_ALT_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"                            
+    DEC_ALT_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"                           
+    ALT_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"                    
+    CAL_ALT_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"                           
+    CAL_ALT_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"                  
+    ALT_PARM_1        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 1"                  
+    ALT_PARM_2        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 2"                  
+    ALT_PARM_3        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 3"                  
+    ALT_PARM_4        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 4"                  
+    ALT_PARM_5        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 5"                  
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.hdu.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.hdu.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.hdu.txt	(revision 22322)
@@ -0,0 +1,93 @@
+
+# description of the chip metadata
+BLANK METADATA
+  COMMENT  STR  MULTI
+  COMMENT  STR
+  COMMENT  STR  Basic Info about this frame
+  COMMENT  STR
+  SKYCELL  S32                       # Sky Cell ID number
+  REFID    S32                       # Reference Sky Cell ID number
+  SUBTRID  S32                       # ID of Sky Cell Image subtracted 
+  FILTER   S32                       # Filter 
+  STACKID  S32                       # ID for this image stack
+  NP2IMGS  S32                       # Number of P2 images contributing
+  ANALVER  S32                       # Software Analysis Version
+  COMMENT  STR
+  COMMENT  STR  Astrometric Solution (up to linear terms)
+  COMMENT  STR
+  NASTRO   S32                  100 # Number of astrometry stars
+  CERROR   F32               10.000 # Scatter in astrometry solution (MAS)
+  CTYPE1   STR  RA---TAN            # WCS Coordinate type
+  CTYPE2   STR  DEC--TAN            # WCS Coordinate type
+  CRVAL1   F32      321.30941666667 # WCS Ref value (RA in decimal degrees)
+  CRVAL2   F32     -5.1685277777778 # WCS Ref value (DEC in decimal degrees)
+  CRPIX1   F32    -1051.17692913657 # WCS Coordinate reference pixel
+  CRPIX2   F32     2033.40939404705 # WCS Coordinate reference pixel
+  CD1_1    F32  1.13378419402790E-4 # WCS Coordinate scale matrix
+  CD1_2    F32  8.93919113249280E-8 # WCS Coordinate scale matrix
+  CD2_1    F32  -5.4105516940550E-7 # WCS Coordinate scale matrix
+  CD2_2    F32  -1.1365323690945E-4 # WCS Coordinate scale matrix
+  COMMENT  STR
+  COMMENT  STR  Higher order terms in astrometric solution
+  COMMENT  STR
+  POLYORD  S32                    3 # Polynomial Order
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 1, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 1, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 1, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 1, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 1, coefficient of Y^2
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 2, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 2, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 2, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 2, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 2, coefficient of Y^2
+  COMMENT  STR
+  COMMENT  STR  PSF Description Parameters
+  COMMENT  STR
+  PSFMOD   S32                    1 # PSF Model number
+  PSFFWHM  F32                  1.0 # PSF Full width at Half Maximum (pixels)
+  PSFWIDX  F32                  1.0 # PSF Width in X coordinate (pixels)
+  PSFWIDY  F32                  1.0 # PSF Width in Y coordinate (pixels)
+  PSFTHETA F32                  0.0 # PSF Orientation angle (degrees)
+  PSF_EX1  F32                  0.0 # PSF Extra parameter #1
+  PSF_EX2  F32                  0.0 # PSF Extra parameter #2
+  COMMENT  STR
+  COMMENT  STR  Photometry Calibration information
+  COMMENT  STR
+  COMMENT  STR   Formula for Photometry, based on keywords given in this header:
+  COMMENT  STR   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+  COMMENT  STR   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+  COMMENT  STR
+  PHOTCODE STR  GPC.00.r            # Photometry reduction code identifier
+  PHOTOCID S32                      # Numerical code for reduction code
+  PHOT_C   F32              25.7150 # Elixir zero point - measured for camera run
+  PHOT_CS  F32               0.0048 # Elixir zero point - scatter
+  PHOT_NS  S32                   19 # Elixir zero point - N stars
+  PHOT_NM  S32                    5 # Elixir zero point - N images
+  PHOT_C0  F32              25.7430 # Elixir zero point - nominal
+  PHOT_X   F32               0.0830 # Elixir zero point - color term
+  PHOT_K   F32              -0.0400 # Elixir zero point - airmass term
+  PHOT_C1  STR  i_SDSS              # Elixir zero point - color 1
+  PHOT_C2  STR  r_SDSS              # Elixir zero point - color 2
+  COMMENT  STR
+  COMMENT  STR  Some statistics on this image
+  COMMENT  STR
+  NDETECT  S32                 1000 # Number of detections
+  MAGSAT   F32                15.0  # Magnitude at which sources saturate
+  MAGCOMP  F32                22.0  # Magnitude for which frame is 95% complete
+  SKYVAL   F32              1000.0  # Median sky value on detector
+  SKYVAR   F32                 1.0  # Variance of sky values
+  BIASVAL  F32               200.0  # Mean bias level of frame
+  BIASVAR  F32                 1.0  # Variance of bias values
+  COMMENT  STR
+  COMMENT  STR  P2 images used to generate this image stack
+  COMMENT  STR 
+  COMMENT  STR
+  COMMENT  STR  Quality Assurance Flags from IPP
+  COMMENT  STR
+  QAFLAGS  STR  UnknownValue        # IP QA flags
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.low.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.low.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.low.txt	(revision 22322)
@@ -0,0 +1,41 @@
+
+# extra data which belongs to table header
+HEADER METADATA
+  COMMENT  STR  MULTI
+  COMMENT  STR   
+  COMMENT  STR   This HDU applies to all detections that have been analyzed
+  COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+  COMMENT  STR   must be unique within a file so as to allow matching up
+  COMMENT  STR   sources between the PSF matching and either the alternative
+  COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+  COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+  COMMENT  STR
+  EXTNAME  STR PSF_FIT_PARAMS
+END
+
+# description of the FITS table columns
+COLUMNS METADATA
+  TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+  IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+  X_PSF             TTYPE  E     PIXELS              0   5000   "PSF x coordinate"                              
+  Y_PSF             TTYPE  E     PIXELS              0   5000   "PSF y coordinate"                              
+  X_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF x coordinate"                     
+  Y_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF y coordinate"                     
+  RA_PSF            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"
+  DEC_PSF           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"
+  RA_PSF_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"
+  DEC_PSF_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"
+  PSF_INST_MAG      TTYPE  E     MAG               -20      0   "PSF fit instrumental magnitude"
+  PSF_INST_MAG_SIG  TTYPE  E     MAG                 0    0.5   "Sigma of PSF instrumental magnitude"
+  PEAK_FLUX_AS_MAG  TTYPE  E     MAG               -20      0   "Peak flux expressed as magnitude"
+  SKY               TTYPE  E     ADU                 0  60000   "Sky level"
+  SKY_SIGMA         TTYPE  E     ADU                 0  60000   "Sigma of sky level"
+  STAR_GALAXY_SEP   TTYPE  E     NONE                0      1   "Star-Galaxy separator"
+  PSF_WIDTH_X       TTYPE  E     ARCSECONDS          0     20   "PSF width in x coordinate"
+  PSF_WIDTH_Y       TTYPE  E     ARCSECONDS          0     20   "PSF width in y coordinate"
+  PSF_THETA         TTYPE  E     DEGREES          -180   +180   "PSF orientation angle"
+  PSF_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"
+  CAL_PSF_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"
+  CAL_PSF_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"
+  N_FRAMES          TTYPE  I     NONE                0  10000   "Number of frames overlapping source center"
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.psf.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.psf.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell0.psf.txt	(revision 22322)
@@ -0,0 +1,43 @@
+
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    COMMENT  STR  MULTI
+    COMMENT  STR   
+    COMMENT  STR   This HDU applies to all detections that have been analyzed
+    COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+    COMMENT  STR   must be unique within a file so as to allow matching up
+    COMMENT  STR   sources between the PSF matching and either the alternative
+    COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+    COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+    COMMENT  STR
+    EXTNAME  STR PSF_FIT_PARAMS
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    X_PSF             TTYPE  E     PIXELS              0   5000   "PSF x coordinate"                              
+    Y_PSF             TTYPE  E     PIXELS              0   5000   "PSF y coordinate"                              
+    X_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF x coordinate"                     
+    Y_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF y coordinate"                     
+    RA_PSF            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"
+    DEC_PSF           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"
+    RA_PSF_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"
+    DEC_PSF_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"
+    PSF_INST_MAG      TTYPE  E     MAG               -20      0   "PSF fit instrumental magnitude"
+    PSF_INST_MAG_SIG  TTYPE  E     MAG                 0    0.5   "Sigma of PSF instrumental magnitude"
+    PEAK_FLUX_AS_MAG  TTYPE  E     MAG               -20      0   "Peak flux expressed as magnitude"
+    SKY               TTYPE  E     ADU                 0  60000   "Sky level"
+    SKY_SIGMA         TTYPE  E     ADU                 0  60000   "Sigma of sky level"
+    STAR_GALAXY_SEP   TTYPE  E     NONE                0      1   "Star-Galaxy separator"
+    PSF_WIDTH_X       TTYPE  E     ARCSECONDS          0     20   "PSF width in x coordinate"
+    PSF_WIDTH_Y       TTYPE  E     ARCSECONDS          0     20   "PSF width in y coordinate"
+    PSF_THETA         TTYPE  E     DEGREES          -180   +180   "PSF orientation angle"
+    PSF_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"
+    CAL_PSF_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"
+    CAL_PSF_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"
+    N_FRAMES          TTYPE  I     NONE                0  10000   "Number of frames overlapping source center"
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.alt.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.alt.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.alt.txt	(revision 22322)
@@ -0,0 +1,34 @@
+ 
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    EXTNAME STR ALT_FIT_PARAMS
+    COMMENT STR
+    COMMENT STR  This HDU applies to all detections that have been analyzed
+    COMMENT STR  with the IPP alternative PSF fitting procedure. 
+    COMMENT STR  Note that the IPP_IDET must be unique within a file so as
+    COMMENT STR  to allow matching up sources between the PSF matching and
+    COMMENT STR  either the alternative PSF fits for P2, P4S, and P4D
+    COMMENT STR  detections or the extended source fits to the
+    COMMENT STR  Image Stack/Cumulative Sky detections.
+    COMMENT STR   
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    RA_ALT            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"                                
+    DEC_ALT           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"                               
+    RA_ALT_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"                            
+    DEC_ALT_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"                           
+    ALT_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"                    
+    CAL_ALT_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"                           
+    CAL_ALT_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"                  
+    ALT_PARM_1        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 1"                  
+    ALT_PARM_2        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 2"                  
+    ALT_PARM_3        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 3"                  
+    ALT_PARM_4        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 4"                  
+    ALT_PARM_5        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 5"                  
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.hdu.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.hdu.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.hdu.txt	(revision 22322)
@@ -0,0 +1,93 @@
+
+# description of the chip metadata
+BLANK METADATA
+  COMMENT  STR  MULTI
+  COMMENT  STR
+  COMMENT  STR  Basic Info about this frame
+  COMMENT  STR
+  SKYCELL  S32                       # Sky Cell ID number
+  REFID    S32                       # Reference Sky Cell ID number
+  SUBTRID  S32                       # ID of Sky Cell Image subtracted 
+  FILTER   S32                       # Filter 
+  STACKID  S32                       # ID for this image stack
+  NP2IMGS  S32                       # Number of P2 images contributing
+  ANALVER  S32                       # Software Analysis Version
+  COMMENT  STR
+  COMMENT  STR  Astrometric Solution (up to linear terms)
+  COMMENT  STR
+  NASTRO   S32                  100 # Number of astrometry stars
+  CERROR   F32               10.000 # Scatter in astrometry solution (MAS)
+  CTYPE1   STR  RA---TAN            # WCS Coordinate type
+  CTYPE2   STR  DEC--TAN            # WCS Coordinate type
+  CRVAL1   F32      321.30941666667 # WCS Ref value (RA in decimal degrees)
+  CRVAL2   F32     -5.1685277777778 # WCS Ref value (DEC in decimal degrees)
+  CRPIX1   F32    -1051.17692913657 # WCS Coordinate reference pixel
+  CRPIX2   F32     2033.40939404705 # WCS Coordinate reference pixel
+  CD1_1    F32  1.13378419402790E-4 # WCS Coordinate scale matrix
+  CD1_2    F32  8.93919113249280E-8 # WCS Coordinate scale matrix
+  CD2_1    F32  -5.4105516940550E-7 # WCS Coordinate scale matrix
+  CD2_2    F32  -1.1365323690945E-4 # WCS Coordinate scale matrix
+  COMMENT  STR
+  COMMENT  STR  Higher order terms in astrometric solution
+  COMMENT  STR
+  POLYORD  S32                    3 # Polynomial Order
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 1, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 1, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 1, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 1, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 1, coefficient of Y^2
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 2, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 2, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 2, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 2, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 2, coefficient of Y^2
+  COMMENT  STR
+  COMMENT  STR  PSF Description Parameters
+  COMMENT  STR
+  PSFMOD   S32                    1 # PSF Model number
+  PSFFWHM  F32                  1.0 # PSF Full width at Half Maximum (pixels)
+  PSFWIDX  F32                  1.0 # PSF Width in X coordinate (pixels)
+  PSFWIDY  F32                  1.0 # PSF Width in Y coordinate (pixels)
+  PSFTHETA F32                  0.0 # PSF Orientation angle (degrees)
+  PSF_EX1  F32                  0.0 # PSF Extra parameter #1
+  PSF_EX2  F32                  0.0 # PSF Extra parameter #2
+  COMMENT  STR
+  COMMENT  STR  Photometry Calibration information
+  COMMENT  STR
+  COMMENT  STR   Formula for Photometry, based on keywords given in this header:
+  COMMENT  STR   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+  COMMENT  STR   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+  COMMENT  STR
+  PHOTCODE STR  GPC.00.r            # Photometry reduction code identifier
+  PHOTOCID S32                      # Numerical code for reduction code
+  PHOT_C   F32              25.7150 # Elixir zero point - measured for camera run
+  PHOT_CS  F32               0.0048 # Elixir zero point - scatter
+  PHOT_NS  S32                   19 # Elixir zero point - N stars
+  PHOT_NM  S32                    5 # Elixir zero point - N images
+  PHOT_C0  F32              25.7430 # Elixir zero point - nominal
+  PHOT_X   F32               0.0830 # Elixir zero point - color term
+  PHOT_K   F32              -0.0400 # Elixir zero point - airmass term
+  PHOT_C1  STR  i_SDSS              # Elixir zero point - color 1
+  PHOT_C2  STR  r_SDSS              # Elixir zero point - color 2
+  COMMENT  STR
+  COMMENT  STR  Some statistics on this image
+  COMMENT  STR
+  NDETECT  S32                 1000 # Number of detections
+  MAGSAT   F32                15.0  # Magnitude at which sources saturate
+  MAGCOMP  F32                22.0  # Magnitude for which frame is 95% complete
+  SKYVAL   F32              1000.0  # Median sky value on detector
+  SKYVAR   F32                 1.0  # Variance of sky values
+  BIASVAL  F32               200.0  # Mean bias level of frame
+  BIASVAR  F32                 1.0  # Variance of bias values
+  COMMENT  STR
+  COMMENT  STR  P2 images used to generate this image stack
+  COMMENT  STR 
+  COMMENT  STR
+  COMMENT  STR  Quality Assurance Flags from IPP
+  COMMENT  STR
+  QAFLAGS  STR  UnknownValue        # IP QA flags
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.low.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.low.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.low.txt	(revision 22322)
@@ -0,0 +1,41 @@
+
+# extra data which belongs to table header
+HEADER METADATA
+  COMMENT  STR  MULTI
+  COMMENT  STR   
+  COMMENT  STR   This HDU applies to all detections that have been analyzed
+  COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+  COMMENT  STR   must be unique within a file so as to allow matching up
+  COMMENT  STR   sources between the PSF matching and either the alternative
+  COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+  COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+  COMMENT  STR
+  EXTNAME  STR PSF_FIT_PARAMS
+END
+
+# description of the FITS table columns
+COLUMNS METADATA
+  TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+  IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+  X_PSF             TTYPE  E     PIXELS              0   5000   "PSF x coordinate"                              
+  Y_PSF             TTYPE  E     PIXELS              0   5000   "PSF y coordinate"                              
+  X_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF x coordinate"                     
+  Y_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF y coordinate"                     
+  RA_PSF            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"
+  DEC_PSF           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"
+  RA_PSF_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"
+  DEC_PSF_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"
+  PSF_INST_MAG      TTYPE  E     MAG               -20      0   "PSF fit instrumental magnitude"
+  PSF_INST_MAG_SIG  TTYPE  E     MAG                 0    0.5   "Sigma of PSF instrumental magnitude"
+  PEAK_FLUX_AS_MAG  TTYPE  E     MAG               -20      0   "Peak flux expressed as magnitude"
+  SKY               TTYPE  E     ADU                 0  60000   "Sky level"
+  SKY_SIGMA         TTYPE  E     ADU                 0  60000   "Sigma of sky level"
+  STAR_GALAXY_SEP   TTYPE  E     NONE                0      1   "Star-Galaxy separator"
+  PSF_WIDTH_X       TTYPE  E     ARCSECONDS          0     20   "PSF width in x coordinate"
+  PSF_WIDTH_Y       TTYPE  E     ARCSECONDS          0     20   "PSF width in y coordinate"
+  PSF_THETA         TTYPE  E     DEGREES          -180   +180   "PSF orientation angle"
+  PSF_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"
+  CAL_PSF_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"
+  CAL_PSF_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"
+  N_FRAMES          TTYPE  I     NONE                0  10000   "Number of frames overlapping source center"
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.psf.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.psf.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.cell1.psf.txt	(revision 22322)
@@ -0,0 +1,43 @@
+
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    COMMENT  STR  MULTI
+    COMMENT  STR   
+    COMMENT  STR   This HDU applies to all detections that have been analyzed
+    COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+    COMMENT  STR   must be unique within a file so as to allow matching up
+    COMMENT  STR   sources between the PSF matching and either the alternative
+    COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+    COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+    COMMENT  STR
+    EXTNAME  STR PSF_FIT_PARAMS
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    X_PSF             TTYPE  E     PIXELS              0   5000   "PSF x coordinate"                              
+    Y_PSF             TTYPE  E     PIXELS              0   5000   "PSF y coordinate"                              
+    X_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF x coordinate"                     
+    Y_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF y coordinate"                     
+    RA_PSF            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"
+    DEC_PSF           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"
+    RA_PSF_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"
+    DEC_PSF_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"
+    PSF_INST_MAG      TTYPE  E     MAG               -20      0   "PSF fit instrumental magnitude"
+    PSF_INST_MAG_SIG  TTYPE  E     MAG                 0    0.5   "Sigma of PSF instrumental magnitude"
+    PEAK_FLUX_AS_MAG  TTYPE  E     MAG               -20      0   "Peak flux expressed as magnitude"
+    SKY               TTYPE  E     ADU                 0  60000   "Sky level"
+    SKY_SIGMA         TTYPE  E     ADU                 0  60000   "Sigma of sky level"
+    STAR_GALAXY_SEP   TTYPE  E     NONE                0      1   "Star-Galaxy separator"
+    PSF_WIDTH_X       TTYPE  E     ARCSECONDS          0     20   "PSF width in x coordinate"
+    PSF_WIDTH_Y       TTYPE  E     ARCSECONDS          0     20   "PSF width in y coordinate"
+    PSF_THETA         TTYPE  E     DEGREES          -180   +180   "PSF orientation angle"
+    PSF_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"
+    CAL_PSF_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"
+    CAL_PSF_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"
+    N_FRAMES          TTYPE  I     NONE                0  10000   "Number of frames overlapping source center"
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.file.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.file.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4D.file.txt	(revision 22322)
@@ -0,0 +1,14 @@
+
+# Keywords for P4D Frame Descriptor HDU
+HEADER METADATA
+  NCELLS  S32                2 # Number of sky cell extensions
+END
+
+# the file contains table data for each cell:
+TABLE MULTI
+TABLE STR P4D.cell0.hdu.txt
+TABLE STR P4D.cell0.psf.txt
+TABLE STR P4D.cell0.alt.txt
+TABLE STR P4D.cell1.hdu.txt
+TABLE STR P4D.cell1.psf.txt
+TABLE STR P4D.cell1.alt.txt
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4Dlo.file.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4Dlo.file.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4Dlo.file.txt	(revision 22322)
@@ -0,0 +1,12 @@
+
+# Keywords for P4D Frame Descriptor HDU
+HEADER METADATA
+  NCELLS  S32                2 # Number of sky cell extensions
+END
+
+# the file contains table data for each cell:
+TABLE MULTI
+TABLE STR P4D.cell0.hdu.txt
+TABLE STR P4D.cell0.low.txt
+TABLE STR P4D.cell1.hdu.txt
+TABLE STR P4D.cell1.low.txt
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell0.alt.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell0.alt.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell0.alt.txt	(revision 22322)
@@ -0,0 +1,34 @@
+ 
+# extra data which belongs to table header
+FITSTABLE METADATA
+HEADER METADATA
+    EXTNAME STR ALT_FIT_PARAMS
+    COMMENT STR
+    COMMENT STR  This HDU applies to all detections that have been analyzed
+    COMMENT STR  with the IPP alternative PSF fitting procedure. 
+    COMMENT STR  Note that the IPP_IDET must be unique within a file so as
+    COMMENT STR  to allow matching up sources between the PSF matching and
+    COMMENT STR  either the alternative PSF fits for P2, P4S, and P4D
+    COMMENT STR  detections or the extended source fits to the
+    COMMENT STR  Image Stack/Cumulative Sky detections.
+    COMMENT STR   
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    RA_ALT            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"                                
+    DEC_ALT           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"                               
+    RA_ALT_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"                            
+    DEC_ALT_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"                           
+    ALT_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"                    
+    CAL_ALT_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"                           
+    CAL_ALT_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"                  
+    ALT_PARM_1        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 1"                  
+    ALT_PARM_2        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 2"                  
+    ALT_PARM_3        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 3"                  
+    ALT_PARM_4        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 4"                  
+    ALT_PARM_5        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 5"                  
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell0.hdu.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell0.hdu.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell0.hdu.txt	(revision 22322)
@@ -0,0 +1,93 @@
+
+# description of the chip metadata
+BLANK METADATA
+  COMMENT  STR  MULTI
+  COMMENT  STR
+  COMMENT  STR  Basic Info about this frame
+  COMMENT  STR
+  SKYCELL  S32                       # Sky Cell ID number
+  REFID    S32                       # Reference Sky Cell ID number
+  SUBTRID  S32                       # ID of Sky Cell Image subtracted 
+  FILTER   S32                       # Filter 
+  STACKID  S32                       # ID for this image stack
+  NP2IMGS  S32                       # Number of P2 images contributing
+  ANALVER  S32                       # Software Analysis Version
+  COMMENT  STR
+  COMMENT  STR  Astrometric Solution (up to linear terms)
+  COMMENT  STR
+  NASTRO   S32                  100 # Number of astrometry stars
+  CERROR   F32               10.000 # Scatter in astrometry solution (MAS)
+  CTYPE1   STR  RA---TAN            # WCS Coordinate type
+  CTYPE2   STR  DEC--TAN            # WCS Coordinate type
+  CRVAL1   F32      321.30941666667 # WCS Ref value (RA in decimal degrees)
+  CRVAL2   F32     -5.1685277777778 # WCS Ref value (DEC in decimal degrees)
+  CRPIX1   F32    -1051.17692913657 # WCS Coordinate reference pixel
+  CRPIX2   F32     2033.40939404705 # WCS Coordinate reference pixel
+  CD1_1    F32  1.13378419402790E-4 # WCS Coordinate scale matrix
+  CD1_2    F32  8.93919113249280E-8 # WCS Coordinate scale matrix
+  CD2_1    F32  -5.4105516940550E-7 # WCS Coordinate scale matrix
+  CD2_2    F32  -1.1365323690945E-4 # WCS Coordinate scale matrix
+  COMMENT  STR
+  COMMENT  STR  Higher order terms in astrometric solution
+  COMMENT  STR
+  POLYORD  S32                    3 # Polynomial Order
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 1, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 1, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 1, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 1, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 1, coefficient of Y^2
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 2, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 2, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 2, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 2, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 2, coefficient of Y^2
+  COMMENT  STR
+  COMMENT  STR  PSF Description Parameters
+  COMMENT  STR
+  PSFMOD   S32                    1 # PSF Model number
+  PSFFWHM  F32                  1.0 # PSF Full width at Half Maximum (pixels)
+  PSFWIDX  F32                  1.0 # PSF Width in X coordinate (pixels)
+  PSFWIDY  F32                  1.0 # PSF Width in Y coordinate (pixels)
+  PSFTHETA F32                  0.0 # PSF Orientation angle (degrees)
+  PSF_EX1  F32                  0.0 # PSF Extra parameter #1
+  PSF_EX2  F32                  0.0 # PSF Extra parameter #2
+  COMMENT  STR
+  COMMENT  STR  Photometry Calibration information
+  COMMENT  STR
+  COMMENT  STR   Formula for Photometry, based on keywords given in this header:
+  COMMENT  STR   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+  COMMENT  STR   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+  COMMENT  STR
+  PHOTCODE STR  GPC.00.r            # Photometry reduction code identifier
+  PHOTOCID S32                      # Numerical code for reduction code
+  PHOT_C   F32              25.7150 # Elixir zero point - measured for camera run
+  PHOT_CS  F32               0.0048 # Elixir zero point - scatter
+  PHOT_NS  S32                   19 # Elixir zero point - N stars
+  PHOT_NM  S32                    5 # Elixir zero point - N images
+  PHOT_C0  F32              25.7430 # Elixir zero point - nominal
+  PHOT_X   F32               0.0830 # Elixir zero point - color term
+  PHOT_K   F32              -0.0400 # Elixir zero point - airmass term
+  PHOT_C1  STR  i_SDSS              # Elixir zero point - color 1
+  PHOT_C2  STR  r_SDSS              # Elixir zero point - color 2
+  COMMENT  STR
+  COMMENT  STR  Some statistics on this image
+  COMMENT  STR
+  NDETECT  S32                 1000 # Number of detections
+  MAGSAT   F32                15.0  # Magnitude at which sources saturate
+  MAGCOMP  F32                22.0  # Magnitude for which frame is 95% complete
+  SKYVAL   F32              1000.0  # Median sky value on detector
+  SKYVAR   F32                 1.0  # Variance of sky values
+  BIASVAL  F32               200.0  # Mean bias level of frame
+  BIASVAR  F32                 1.0  # Variance of bias values
+  COMMENT  STR
+  COMMENT  STR  P2 images used to generate this image stack
+  COMMENT  STR 
+  COMMENT  STR
+  COMMENT  STR  Quality Assurance Flags from IPP
+  COMMENT  STR
+  QAFLAGS  STR  UnknownValue        # IP QA flags
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell0.psf.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell0.psf.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell0.psf.txt	(revision 22322)
@@ -0,0 +1,43 @@
+
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    COMMENT  STR  MULTI
+    COMMENT  STR   
+    COMMENT  STR   This HDU applies to all detections that have been analyzed
+    COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+    COMMENT  STR   must be unique within a file so as to allow matching up
+    COMMENT  STR   sources between the PSF matching and either the alternative
+    COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+    COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+    COMMENT  STR
+    EXTNAME  STR PSF_FIT_PARAMS
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    X_PSF             TTYPE  E     PIXELS              0   5000   "PSF x coordinate"                              
+    Y_PSF             TTYPE  E     PIXELS              0   5000   "PSF y coordinate"                              
+    X_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF x coordinate"                     
+    Y_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF y coordinate"                     
+    RA_PSF            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"
+    DEC_PSF           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"
+    RA_PSF_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"
+    DEC_PSF_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"
+    PSF_INST_MAG      TTYPE  E     MAG               -20      0   "PSF fit instrumental magnitude"
+    PSF_INST_MAG_SIG  TTYPE  E     MAG                 0    0.5   "Sigma of PSF instrumental magnitude"
+    PEAK_FLUX_AS_MAG  TTYPE  E     MAG               -20      0   "Peak flux expressed as magnitude"
+    SKY               TTYPE  E     ADU                 0  60000   "Sky level"
+    SKY_SIGMA         TTYPE  E     ADU                 0  60000   "Sigma of sky level"
+    STAR_GALAXY_SEP   TTYPE  E     NONE                0      1   "Star-Galaxy separator"
+    PSF_WIDTH_X       TTYPE  E     ARCSECONDS          0     20   "PSF width in x coordinate"
+    PSF_WIDTH_Y       TTYPE  E     ARCSECONDS          0     20   "PSF width in y coordinate"
+    PSF_THETA         TTYPE  E     DEGREES          -180   +180   "PSF orientation angle"
+    PSF_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"
+    CAL_PSF_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"
+    CAL_PSF_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"
+    N_FRAMES          TTYPE  I     NONE                0  10000   "Number of frames overlapping source center"
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell1.alt.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell1.alt.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell1.alt.txt	(revision 22322)
@@ -0,0 +1,34 @@
+ 
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    EXTNAME STR ALT_FIT_PARAMS
+    COMMENT STR
+    COMMENT STR  This HDU applies to all detections that have been analyzed
+    COMMENT STR  with the IPP alternative PSF fitting procedure. 
+    COMMENT STR  Note that the IPP_IDET must be unique within a file so as
+    COMMENT STR  to allow matching up sources between the PSF matching and
+    COMMENT STR  either the alternative PSF fits for P2, P4S, and P4D
+    COMMENT STR  detections or the extended source fits to the
+    COMMENT STR  Image Stack/Cumulative Sky detections.
+    COMMENT STR   
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    RA_ALT            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"                                
+    DEC_ALT           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"                               
+    RA_ALT_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"                            
+    DEC_ALT_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"                           
+    ALT_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"                    
+    CAL_ALT_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"                           
+    CAL_ALT_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"                  
+    ALT_PARM_1        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 1"                  
+    ALT_PARM_2        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 2"                  
+    ALT_PARM_3        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 3"                  
+    ALT_PARM_4        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 4"                  
+    ALT_PARM_5        TTYPE  E     NONE             NONE   NONE   "ALT model parameter 5"                  
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell1.hdu.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell1.hdu.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell1.hdu.txt	(revision 22322)
@@ -0,0 +1,93 @@
+
+# description of the chip metadata
+BLANK METADATA
+  COMMENT  STR  MULTI
+  COMMENT  STR
+  COMMENT  STR  Basic Info about this frame
+  COMMENT  STR
+  SKYCELL  S32                       # Sky Cell ID number
+  REFID    S32                       # Reference Sky Cell ID number
+  SUBTRID  S32                       # ID of Sky Cell Image subtracted 
+  FILTER   S32                       # Filter 
+  STACKID  S32                       # ID for this image stack
+  NP2IMGS  S32                       # Number of P2 images contributing
+  ANALVER  S32                       # Software Analysis Version
+  COMMENT  STR
+  COMMENT  STR  Astrometric Solution (up to linear terms)
+  COMMENT  STR
+  NASTRO   S32                  100 # Number of astrometry stars
+  CERROR   F32               10.000 # Scatter in astrometry solution (MAS)
+  CTYPE1   STR  RA---TAN            # WCS Coordinate type
+  CTYPE2   STR  DEC--TAN            # WCS Coordinate type
+  CRVAL1   F32      321.30941666667 # WCS Ref value (RA in decimal degrees)
+  CRVAL2   F32     -5.1685277777778 # WCS Ref value (DEC in decimal degrees)
+  CRPIX1   F32    -1051.17692913657 # WCS Coordinate reference pixel
+  CRPIX2   F32     2033.40939404705 # WCS Coordinate reference pixel
+  CD1_1    F32  1.13378419402790E-4 # WCS Coordinate scale matrix
+  CD1_2    F32  8.93919113249280E-8 # WCS Coordinate scale matrix
+  CD2_1    F32  -5.4105516940550E-7 # WCS Coordinate scale matrix
+  CD2_2    F32  -1.1365323690945E-4 # WCS Coordinate scale matrix
+  COMMENT  STR
+  COMMENT  STR  Higher order terms in astrometric solution
+  COMMENT  STR
+  POLYORD  S32                    3 # Polynomial Order
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 1, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 1, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 1, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 1, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 1, coefficient of Y^2
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 2, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 2, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 2, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 2, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 2, coefficient of Y^2
+  COMMENT  STR
+  COMMENT  STR  PSF Description Parameters
+  COMMENT  STR
+  PSFMOD   S32                    1 # PSF Model number
+  PSFFWHM  F32                  1.0 # PSF Full width at Half Maximum (pixels)
+  PSFWIDX  F32                  1.0 # PSF Width in X coordinate (pixels)
+  PSFWIDY  F32                  1.0 # PSF Width in Y coordinate (pixels)
+  PSFTHETA F32                  0.0 # PSF Orientation angle (degrees)
+  PSF_EX1  F32                  0.0 # PSF Extra parameter #1
+  PSF_EX2  F32                  0.0 # PSF Extra parameter #2
+  COMMENT  STR
+  COMMENT  STR  Photometry Calibration information
+  COMMENT  STR
+  COMMENT  STR   Formula for Photometry, based on keywords given in this header:
+  COMMENT  STR   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+  COMMENT  STR   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+  COMMENT  STR
+  PHOTCODE STR  GPC.00.r            # Photometry reduction code identifier
+  PHOTOCID S32                      # Numerical code for reduction code
+  PHOT_C   F32              25.7150 # Elixir zero point - measured for camera run
+  PHOT_CS  F32               0.0048 # Elixir zero point - scatter
+  PHOT_NS  S32                   19 # Elixir zero point - N stars
+  PHOT_NM  S32                    5 # Elixir zero point - N images
+  PHOT_C0  F32              25.7430 # Elixir zero point - nominal
+  PHOT_X   F32               0.0830 # Elixir zero point - color term
+  PHOT_K   F32              -0.0400 # Elixir zero point - airmass term
+  PHOT_C1  STR  i_SDSS              # Elixir zero point - color 1
+  PHOT_C2  STR  r_SDSS              # Elixir zero point - color 2
+  COMMENT  STR
+  COMMENT  STR  Some statistics on this image
+  COMMENT  STR
+  NDETECT  S32                 1000 # Number of detections
+  MAGSAT   F32                15.0  # Magnitude at which sources saturate
+  MAGCOMP  F32                22.0  # Magnitude for which frame is 95% complete
+  SKYVAL   F32              1000.0  # Median sky value on detector
+  SKYVAR   F32                 1.0  # Variance of sky values
+  BIASVAL  F32               200.0  # Mean bias level of frame
+  BIASVAR  F32                 1.0  # Variance of bias values
+  COMMENT  STR
+  COMMENT  STR  P2 images used to generate this image stack
+  COMMENT  STR 
+  COMMENT  STR
+  COMMENT  STR  Quality Assurance Flags from IPP
+  COMMENT  STR
+  QAFLAGS  STR  UnknownValue        # IP QA flags
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell1.psf.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell1.psf.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.cell1.psf.txt	(revision 22322)
@@ -0,0 +1,43 @@
+
+# extra data which belongs to table header
+FITSTABLE METADATA
+  HEADER METADATA
+    COMMENT  STR  MULTI
+    COMMENT  STR   
+    COMMENT  STR   This HDU applies to all detections that have been analyzed
+    COMMENT  STR   with the IPP PSF fitting procedure. Note that the IPP_IDET
+    COMMENT  STR   must be unique within a file so as to allow matching up
+    COMMENT  STR   sources between the PSF matching and either the alternative
+    COMMENT  STR   PSF fits for P2, P4S, and P4D detections or the extended
+    COMMENT  STR   source fits to the Image Stack/Cumulative Sky detections.
+    COMMENT  STR
+    EXTNAME  STR PSF_FIT_PARAMS
+  END
+
+  # description of the FITS table columns
+  COLUMNS METADATA
+    TYPE              TTYPE  TFORM TUNIT            TMIN   TMAX   COMMENT
+    IPP_IDET          TTYPE  J     NONE             NONE   NONE   "IPP detection identifer index"                 
+    X_PSF             TTYPE  E     PIXELS              0   5000   "PSF x coordinate"                              
+    Y_PSF             TTYPE  E     PIXELS              0   5000   "PSF y coordinate"                              
+    X_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF x coordinate"                     
+    Y_PSF_SIG         TTYPE  E     PIXELS              0     10   "Sigma in PSF y coordinate"                     
+    RA_PSF            TTYPE  D    "DECIMAL DEGREES"    0    360   "RA from PSF fit"
+    DEC_PSF           TTYPE  D    "DECIMAL DEGREES"  -90    +90   "DEC from PSF fit"
+    RA_PSF_SIG        TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit RA"
+    DEC_PSF_SIG       TTYPE  E     ARCSECONDS          0      1   "Sigma of PSF fit DEC"
+    PSF_INST_MAG      TTYPE  E     MAG               -20      0   "PSF fit instrumental magnitude"
+    PSF_INST_MAG_SIG  TTYPE  E     MAG                 0    0.5   "Sigma of PSF instrumental magnitude"
+    PEAK_FLUX_AS_MAG  TTYPE  E     MAG               -20      0   "Peak flux expressed as magnitude"
+    SKY               TTYPE  E     ADU                 0  60000   "Sky level"
+    SKY_SIGMA         TTYPE  E     ADU                 0  60000   "Sigma of sky level"
+    STAR_GALAXY_SEP   TTYPE  E     NONE                0      1   "Star-Galaxy separator"
+    PSF_WIDTH_X       TTYPE  E     ARCSECONDS          0     20   "PSF width in x coordinate"
+    PSF_WIDTH_Y       TTYPE  E     ARCSECONDS          0     20   "PSF width in y coordinate"
+    PSF_THETA         TTYPE  E     DEGREES          -180   +180   "PSF orientation angle"
+    PSF_QF            TTYPE  E     NONE                0      1   "PSF coverage/quality factor"
+    CAL_PSF_MAG       TTYPE  E     MAG               -10    +30   "Calibrated magnitude"
+    CAL_PSF_MAG_SIG   TTYPE  E     MAG                 0    0.5   "Sigma of calibrated magnitude"
+    N_FRAMES          TTYPE  I     NONE                0  10000   "Number of frames overlapping source center"
+  END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.file.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.file.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/P4S.file.txt	(revision 22322)
@@ -0,0 +1,14 @@
+
+# Keywords for P4S Frame Descriptor HDU
+HEADER METADATA
+  NCELLS  S32                2 # Number of sky cell extensions
+END
+
+# the file contains table data for each chip:
+TABLE MULTI
+TABLE STR P4S.cell0.hdu.txt
+TABLE STR P4S.cell0.psf.txt
+TABLE STR P4S.cell0.alt.txt
+TABLE STR P4S.cell1.hdu.txt
+TABLE STR P4S.cell1.psf.txt
+TABLE STR P4S.cell1.alt.txt
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/filedef.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/filedef.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/filedef.txt	(revision 22322)
@@ -0,0 +1,12 @@
+
+# description of PHU keywords
+HEADER METADATA
+  NAME    STR PHU
+  NLINE   S32 100
+  WIDTH   F32 2.54
+  HEIGHT  F32 25.4
+END
+
+TABLE MULTI
+TABLE STR tabledef.txt
+TABLE STR tabledef.txt
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/init.CS.cell.hdu.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/init.CS.cell.hdu.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/init.CS.cell.hdu.mdc	(revision 22322)
@@ -0,0 +1,29 @@
+# 3.6.6.8 Sky Cell Definition Metadata HDU
+#
+FITSTABLE METADATA
+    HEADER METADATA
+        EXTNAME     STR     SKY_CELL_TABLE
+        COMMENT     MULTI
+        COMMENT     STR     NULL
+        COMMENT     STR     This file defines the regions on the sky covered by
+        COMMENT     STR     each sky cell used by the IPP. For this sample, it
+        COMMENT     STR     is assumed each cell is a quadralateral defined by
+        COMMENT     STR     4 vertices on the sky.
+        COMMENT     STR     NULL
+    END
+    #    CHECKSUM=                                      #   Checksum for this header
+    #    DATASUM =                                      #   Checksum for table data
+    #
+    COLUMNS METADATA
+        TYPE    TTYPE   TUNIT   TFORM
+        ID      TTYPE   NONE    J           # Sky cell identifier
+        RA1     TTYPE   DEGREES D           # RA of 1st cell vertex.
+        DEC1    TTYPE   DEGREES D           # DEC of 1st cell vertex.
+        RA2     TTYPE   DEGREES D           # RA of 2nd cell vertex.
+        DEC2    TTYPE   DEGREES D           # DEC of 2nd cell vertex.
+        RA3     TTYPE   DEGREES D           # RA of 34d cell vertex.
+        DEC3    TTYPE   DEGREES D           # DEC of 3rd cell vertex.
+        RA4     TTYPE   DEGREES D           # RA of 4th cell vertex.
+        DEC4    TTYPE   DEGREES D           # DEC of 4th cell vertex.
+    END
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/init.CS.file.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/init.CS.file.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/init.CS.file.mdc	(revision 22322)
@@ -0,0 +1,20 @@
+# from 3.6.2 Initialization Phase Products
+#
+# A Sky Cell definition consisting of a FITS file with
+#
+#   A primary HDU
+#
+#   A FITS extension with an HDU describing the format of the FITS table
+#   containing the cell number and a description of its spatial extent
+#   (IPP-PSPS Table 3.2).
+#
+#   A set of binary data records defining the sky cells and their boundaries.
+#
+
+# PDU
+HEADER METADATA
+    DATE    UTC     NULL
+END
+
+TABLE MULTI
+TABLE STR init.CS.cell.hdu.mdc
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/init.camera.file.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/init.camera.file.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/init.camera.file.mdc	(revision 22322)
@@ -0,0 +1,21 @@
+# from 3.6.2 Initialization Phase Products
+#
+# A Sky Cell definition consisting of a FITS file with
+#
+#   A primary HDU
+#
+#   A FITS extension with an HDU describing the format of the FITS table
+#   containing the cell number and a description of its spatial extent
+#   (IPP-PSPS Table 3.2).
+#
+#   A set of binary data records defining the sky cells and their boundaries.
+#
+
+# PDU
+HEADER METADATA
+    DATE        UTC     NULL
+    CAMERAID    STR     uCam
+END
+
+TABLE MULTI
+TABLE STR init.camera.hdu.mdc
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/init.camera.hdu.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/init.camera.hdu.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/init.camera.hdu.mdc	(revision 22322)
@@ -0,0 +1,22 @@
+# 3.6.6.8 Sky Cell Definition Metadata HDU
+#
+FITSTABLE METADATA
+    HEADER METADATA
+        EXTNAME     STR     CAMERA_CONFIG
+        COMMENT     MULTI
+        COMMENT     STR     NULL
+        COMMENT     STR     This table identifies the various camera
+        COMMENT     STR     configurations of the Pan-STARRS system
+        COMMENT     STR     NULL
+    #    CHECKSUM=                                      #   Checksum for this header
+    #    DATASUM =                                      #   Checksum for table data
+    END
+
+    COLUMNS METADATA
+        TYPE        TTYPE   TUNIT   TFORM
+        CLASSID     TTYPE   NONE    J   # Focal Plane Location ID
+        DETID       TTYPE   NONE    J   # Numerical ID of CCD
+        ELECID      TTYPE   NONE    J   # Numerical ID of Electronics
+    END
+END
+
Index: /tags/sj_tags/sj_root_20080929/icd-demo/def/tabledef.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/def/tabledef.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/def/tabledef.txt	(revision 22322)
@@ -0,0 +1,29 @@
+ 
+# this file describes a FITS table to be simulated
+BLANK METADATA
+  EXTNAME STR FOOFOO
+  NAME    STR CHIP0
+  NLINE   S32 100
+  WIDTH   F32 2.54
+  HEIGHT  F32 25.4
+END
+
+HEADER METADATA
+  EXTNAME STR FOOBAR
+  DATE    STR 2006/02/05
+  NLINE   S32 100
+  WIDTH   F32 2.54
+  HEIGHT  F32 25.4
+END
+
+# description of the FITS table columns
+COLUMNS METADATA
+  TYPE    TTYPE TFORM TUNIT       TMIN   TMAX       COMMENT
+  IPP_ID  TTYPE J     NONE           0   100000     "this is a test"
+  RA      TTYPE D     DEGREES      0.0    360.0     "this is a test"
+  DEC     TTYPE D     DEGREES    -90.0    +90.0     "this is a test"
+  RA_SIG  TTYPE E     ARCSECONDS   0.0      15.0    "this is a test"
+  DEC_SIG TTYPE E     ARCSECONDS   0.0      15.0    "this is a comment"
+  NFRAMES TTYPE I     NONE           0     100      "this is a test"
+  NAME    TTYPE A     NONE        NONE     NONE
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/raw/alt-fits-detections.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/raw/alt-fits-detections.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/raw/alt-fits-detections.txt	(revision 22322)
@@ -0,0 +1,60 @@
+XTENSION= 'BINTABLE          ' /
+BITPIX  =                    8 /
+NAXIS   =                    2 /
+NAXIS1  =           NNNNNNNNNN / Number of objects in the table
+NAXIS2  =                  118 / Number of bytes per row 
+PCOUNT  =                    0 /
+GCOUNT  =                    1 /
+TFIELDS =                   27 /
+EXTNAME = 'ALT_FIT_PARAMS    ' /
+COMMENT
+COMMENT   This HDU applies to all detections that have been analyzed
+COMMENT   with the IPP alternative PSF fitting procedure. 
+COMMENT   Note that the IPP_IDET must be unique within a file so as
+COMMENT   to allow matching up sources between the PSF matching and
+COMMENT   either the alternative PSF fits for P2, P4S, and P4D
+COMMENT   detections or the extended source fits to the
+COMMENT   Image Stack/Cumulative Sky detections.
+COMMENT
+TTYPE1  = 'IPP_IDET          ' / IPP detection identifer index
+TUNIT1  = '                  ' /
+TFORM1  = 'J                 ' /
+TTYPE2  = 'ALT_MODEL_ID      ' / Alternate ALT fit model index
+TUNIT2  = '                  ' /
+TFORM2  = 'I                 ' /
+TTYPE6  = 'RA_ALT            ' / RA from ALT fit
+TUNIT6  = 'DECIMAL DEGREES   ' /
+TFORM6  = 'D                 ' /
+TTYPE7  = 'DEC_ALT           ' / DEC from ALT fit
+TUNIT7  = 'DECIMAL DEGREES   ' /
+TFORM7  = 'D                 ' /
+TTYPE8  = 'RA_ALT_SIG        ' / Sigma of ALT fit RA
+TUNIT8  = 'ARCSECONDS        ' /
+TFORM8  = 'E                 ' /
+TTYPE9  = 'DEC_ALT_SIG       ' / Sigma of ALT fit DEC
+TUNIT9  = 'ARCSECONDS        ' /
+TFORM9  = 'E                 ' /
+TTYPE20 = 'ALT_QF            ' / ALT coverage/quality factor
+TUNIT20 = '                  ' /
+TFORM20 = 'E                 ' /
+TTYPE21 = 'CAL_ALT_MAG       ' / Calibrated ALT magnitude
+TUNIT21 = 'MAG               ' /
+TFORM21 = 'E                 ' /
+TTYPE23 = 'ALT_PARM_1        ' / ALT model parameter 1
+TUNIT23 = '                  ' /
+TFORM23 = 'E                 ' /
+TTYPE24 = 'ALT_PARM_2        ' / ALT model parameter 2
+TUNIT24 = '                  ' /
+TFORM24 = 'E                 ' /
+TTYPE25 = 'ALT_PARM_3        ' / ALT model parameter 3
+TUNIT25 = '                  ' /
+TFORM25 = 'E                 ' /
+TTYPE26 = 'ALT_PARM_4        ' / ALT model parameter 4
+TUNIT26 = '                  ' /
+TFORM26 = 'E                 ' /
+TTYPE27 = 'ALT_PARM_5        ' / ALT model parameter 5
+TUNIT27 = '                  ' /
+TFORM27 = 'E                 ' /
+CHECKSUM=                      / Header checksum
+DATASUM =                      / Data section checksum
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/raw/combination.image.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/raw/combination.image.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/raw/combination.image.txt	(revision 22322)
@@ -0,0 +1,102 @@
+
+# description of the chip metadata
+BLANK METADATA
+  COMMENT  STR  MULTI
+  COMMENT  STR
+  COMMENT  STR  Basic Info about this frame
+  COMMENT  STR
+  SKYCELL  S32                       # Sky Cell ID number
+  CELLVER  S32                       # Sky Cell version number
+  FILTER   S32                       # Filter 
+  STACKID  S32                       # ID for this image stack
+  NP2IMGS  S32                       # Number of P2 images contributing
+  ANALVER  S32                       # Software Analysis Version
+  COMMENT  STR
+  COMMENT  STR  Combination (P4S, P4D, Image Stack/ Cumulative Sky) Image
+  COMMENT  STR  Metadata HDU
+  COMMENT  STR  
+  OBS_ID   STR  UnknownFormat       # Observation Identifier
+  CLASSID  S32                    0 # Class ID = focal plane location
+  DETECTID S32                    0 # Numerical ID of detector
+  NX       S32                 4800 # Number of columns on the detector
+  NY       S32                 4800 # Number of rows on the detector   DATE    STR 2006/02/05
+  COMMENT  STR
+  COMMENT  STR  Astrometric Solution (up to linear terms)
+  COMMENT  STR
+  NASTRO   S32                  100 # Number of astrometry stars
+  CERROR   F32               10.000 # Scatter in astrometry solution (MAS)
+  CTYPE1   STR  RA---TAN            # WCS Coordinate type
+  CTYPE2   STR  DEC--TAN            # WCS Coordinate type
+  CRVAL1   F32      321.30941666667 # WCS Ref value (RA in decimal degrees)
+  CRVAL2   F32     -5.1685277777778 # WCS Ref value (DEC in decimal degrees)
+  CRPIX1   F32    -1051.17692913657 # WCS Coordinate reference pixel
+  CRPIX2   F32     2033.40939404705 # WCS Coordinate reference pixel
+  CD1_1    F32  1.13378419402790E-4 # WCS Coordinate scale matrix
+  CD1_2    F32  8.93919113249280E-8 # WCS Coordinate scale matrix
+  CD2_1    F32  -5.4105516940550E-7 # WCS Coordinate scale matrix
+  CD2_2    F32  -1.1365323690945E-4 # WCS Coordinate scale matrix
+  COMMENT  STR
+  COMMENT  STR  Higher order terms in astrometric solution
+  COMMENT  STR
+  POLYORD  S32                    3 # Polynomial Order
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 1, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 1, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 1, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 1, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 1, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 1, coefficient of Y^2
+  PCA1X3Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^3
+  PCA1X2Y1 F32                  0.0 # WCS, Axis 2, coefficient of X^2 * Y
+  PCA1X1Y2 F32                  0.0 # WCS, Axis 2, coefficient of X * Y^2
+  PCA1X0Y3 F32                  0.0 # WCS, Axis 2, coefficient of Y^3
+  PCA1X2Y0 F32                  0.0 # WCS, Axis 2, coefficient of X^2
+  PCA1X1Y1 F32                  0.0 # WCS, Axis 2, coefficient of X * Y
+  PCA1X0Y2 F32                  0.0 # WCS, Axis 2, coefficient of Y^2
+  COMMENT  STR
+  COMMENT  STR  PSF Description Parameters
+  COMMENT  STR
+  PSFMOD   S32                    1 # PSF Model number
+  PSFFWHM  F32                  1.0 # PSF Full width at Half Maximum (pixels)
+  PSFWIDX  F32                  1.0 # PSF Width in X coordinate (pixels)
+  PSFWIDY  F32                  1.0 # PSF Width in Y coordinate (pixels)
+  PSFTHETA F32                  0.0 # PSF Orientation angle (degrees)
+  PSF_EX1  F32                  0.0 # PSF Extra parameter #1
+  PSF_EX2  F32                  0.0 # PSF Extra parameter #2
+  COMMENT  STR
+  COMMENT  STR  Photometry Calibration information
+  COMMENT  STR
+  COMMENT  STR   Formula for Photometry, based on keywords given in this header:
+  COMMENT  STR   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+  COMMENT  STR   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+  COMMENT  STR
+  PHOTCODE STR  GPC.00.r            # Photometry reduction code identifier
+  PHOTOCID S32                      # Numerical code for reduction code
+  PHOT_C   F32              25.7150 # Elixir zero point - measured for camera run
+  PHOT_CS  F32               0.0048 # Elixir zero point - scatter
+  PHOT_NS  S32                   19 # Elixir zero point - N stars
+  PHOT_NM  S32                    5 # Elixir zero point - N images
+  PHOT_C0  F32              25.7430 # Elixir zero point - nominal
+  PHOT_X   F32               0.0830 # Elixir zero point - color term
+  PHOT_K   F32              -0.0400 # Elixir zero point - airmass term
+  PHOT_C1  STR  i_SDSS              # Elixir zero point - color 1
+  PHOT_C2  STR  r_SDSS              # Elixir zero point - color 2
+  COMMENT  STR
+  COMMENT  STR  Some statistics on this image
+  COMMENT  STR
+  NDETECT  S32                 1000 # Number of detections
+  MAGSAT   F32                15.0  # Magnitude at which sources saturate
+  MAGCOMP  F32                22.0  # Magnitude for which frame is 95% complete
+  SKYVAL   F32              1000.0  # Median sky value on detector
+  SKYVAR   F32                 1.0  # Variance of sky values
+  BIASVAL  F32               200.0  # Mean bias level of frame
+  BIASVAR  F32                 1.0  # Variance of bias values
+  COMMENT  STR
+  COMMENT  STR  P2 images used to generate this image stack
+  COMMENT  STR 
+  COMMENT  STR
+  COMMENT  STR  Quality Assurance Flags from IPP
+  COMMENT  STR
+  QAFLAGS  STR  UnknownValue        # IP QA flags
+END
+
Index: /tags/sj_tags/sj_root_20080929/icd-demo/raw/ext-fits-detections.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/raw/ext-fits-detections.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/raw/ext-fits-detections.txt	(revision 22322)
@@ -0,0 +1,353 @@
+XTENSION= 'BINTABLE          ' /
+BITPIX  =                    8 /
+NAXIS   =                    2 /
+NAXIS1  =           NNNNNNNNNN / Number of objects in the table
+NAXIS2  =                  448 / Number of bytes per row 
+PCOUNT  =                    0 /
+GCOUNT  =                    1 /
+TFIELDS =                  112 /
+EXTNAME = 'EXTENDED_SRC_PARMS' /
+COMMENT
+COMMENT   This HDU applies to all detections that have been analyzed
+COMMENT   with the IPP extended source fitting procedure. 
+COMMENT   Note that the IPP_IDET must be unique within a file so as 
+COMMENT   to allow matching up sources between the PSF matching and 
+COMMENT   either the alternative PSF fits for P2, P4S, and P4D 
+COMMENT   detections or the extended source fits to the 
+COMMENT   Image Stack/Cumulative Sky detections.
+COMMENT
+TTYPE1  = 'IPP_IDET          ' / IPP detection identifer index
+TUNIT1  = '                  ' /
+TFORM1  = 'J                 ' /
+TTYPE2  = 'PET_R             ' / Petrosian radius
+TUNIT2  = 'ARCSECONDS        ' /
+TFORM2  = 'E                 ' /
+TTYPE3  = 'PET_MAG           ' / Petrosian magnitude
+TUNIT3  = 'MAG               ' /
+TFORM3  = 'E                 ' /
+TTYPE4  = 'PET_R50           ' / Petrosian radius for 50% light
+TUNIT4  = 'ARCSECONDS        ' /
+TFORM4  = 'E                 ' /
+TTYPE4  = 'PET_R90           ' / Petrosian radius for 90% light
+TUNIT4  = 'ARCSECONDS        ' /
+TFORM4  = 'E                 ' /
+TTYPE6  = 'RA_PET            ' / RA from Petrosian fit
+TUNIT6  = 'DECIMAL DEGREES   ' /
+TFORM6  = 'D                 ' /
+TTYPE7  = 'DEC_PET           ' / DEC from Petrosian fit
+TUNIT7  = 'DECIMAL DEGREES   ' /
+TFORM7  = 'D                 ' /
+TTYPE8  = 'RA_PET_SIG        ' / Sigma of Petrosian fit RA
+TUNIT8  = 'ARCSECONDS        ' /
+TFORM8  = 'E                 ' /
+TTYPE9  = 'DEC_PET_SIG       ' / Sigma of Petrosian fit DEC
+TUNIT9  = 'ARCSECONDS        ' /
+TFORM9  = 'E                 ' /
+TTYPE10 = 'PET_QF            ' / Petrosian coverage/quality factor
+TUNIT10 = '                  ' /
+TFORM10 = 'E                 ' /
+TTYPE11 = 'DEV_R             ' / deVaucouleurs radius
+TUNIT11 = 'ARCSECONDS        ' /
+TFORM11 = 'E                 ' /
+TTYPE12 = 'MAG_DEV           ' / deVaucouleurs magnitude
+TUNIT12 = 'MAG               ' /
+TFORM12 = 'E                 ' /
+TTYPE13 = 'MAG_DEV_SIG       ' / Sigma of deVaucouleurs magnitude
+TUNIT13 = 'MAG               ' /
+TFORM13 = 'E                 ' /
+TTYPE14 = 'DEV_AB            ' / deVaucouleurs axis ratio
+TUNIT14 = '                  ' /
+TFORM14 = 'E                 ' /
+TTYPE15 = 'DEV_PHI           ' / deVaucouleurs orientation angle
+TUNIT15 = 'DEGREES           ' /
+TFORM15 = 'E                 ' /
+TTYPE16 = 'RA_DEV            ' / RA from deVaucouleurs fit
+TUNIT16 = 'DECIMAL DEGREES   ' /
+TFORM16 = 'D                 ' /
+TTYPE17 = 'DEC_DEV           ' / DEC from deVaucouleurs fit
+TUNIT17 = 'DECIMAL DEGREES   ' /
+TFORM17 = 'D                 ' /
+TTYPE18 = 'RA_DEV_SIG        ' / Sigma of deVaucouleurs fit RA
+TUNIT18 = 'ARCSECONDS        ' /
+TFORM18 = 'E                 ' /
+TTYPE19 = 'DEC_DEV_SIG       ' / Sigma of deVaucouleurs fit DEC
+TUNIT19 = 'ARCSECONDS        ' /
+TFORM19 = 'E                 ' /
+TTYPE20 = 'DEV_QF            ' / deVaucouleurs coverage/quality factor
+TUNIT20 = '                  ' /
+TFORM20 = 'E                 ' /
+TTYPE21 = 'EXP_R             ' / Exponential fit model radius
+TUNIT21 = 'ARCSECONDS        ' /
+TFORM21 = 'E                 ' /
+TTYPE22 = 'MAG_EXP           ' / Exponential fit model magnitude
+TUNIT22 = 'MAG               ' /
+TFORM22 = 'E                 ' /
+TTYPE23 = 'MAG__EXP_SIG      ' / Sigma of Exponential fit model magnitude
+TUNIT23 = 'MAG               ' /
+TFORM23 = 'E                 ' /
+TTYPE24 = 'EXP_AB            ' / Exponential fit model axis ratio
+TUNIT24 = '                  ' /
+TFORM24 = 'E                 ' /
+TTYPE25 = 'EXP_PHI           ' / Exponetial fit orientation angle
+TUNIT25 = 'DEGREES           ' /
+TFORM25 = 'E                 ' /
+TTYPE26 = 'RA_EXP            ' / RA from Expondential model fit
+TUNIT26 = 'DECIMAL DEGREES   ' /
+TFORM26 = 'D                 ' /
+TTYPE27 = 'DEC_EXP           ' / DEC from Expondential model fit
+TUNIT27 = 'DECIMAL DEGREES   ' /
+TFORM27 = 'D                 ' /
+TTYPE28 = 'RA_EXP_SIG        ' / Sigma of Expondential model fit RA
+TUNIT28 = 'ARCSECONDS        ' /
+TFORM28 = 'E                 ' /
+TTYPE29 = 'DEC_EXP_SIG       ' / Sigma of Expondential model fit DEC
+TUNIT29 = 'ARCSECONDS        ' /
+TFORM29 = 'E                 ' /
+TTYPE30 = 'EXP_QF            ' / Exponential fit coverage/quality factor
+TUNIT30 = '                  ' /
+TFORM30 = 'E                 ' /
+TTYPE31 = 'SER_R             ' / Sersic fit model radius
+TUNIT31 = 'ARCSECONDS        ' /
+TFORM31 = 'E                 ' /
+TTYPE32 = 'MAG_SER           ' / Sersic fit model magnitude
+TUNIT32 = 'MAG               ' /
+TFORM32 = 'E                 ' /
+TTYPE33 = 'MAG_SER_SIG       ' / Sigma of Sersic fit model magnitude
+TUNIT33 = 'MAG               ' /
+TFORM33 = 'E                 ' /
+TTYPE34 = 'SER_AB            ' / Sersic fit model axis ratio
+TUNIT34 = '                  ' /
+TFORM34 = 'E                 ' /
+TTYPE35 = 'SER_PHI           ' / Exponetial fit orientation angle
+TUNIT35 = 'DEGREES           ' /
+TFORM35 = 'E                 ' /
+TTYPE36 = 'SER_NU            ' / Sersic fit index nu
+TUNIT36 = '                  ' /
+TFORM36 = 'E                 ' /
+TTYPE16 = 'RA_SER            ' / RA from Sersic fit
+TUNIT16 = 'DECIMAL DEGREES   ' /
+TFORM16 = 'D                 ' /
+TTYPE37 = 'DEC_SER           ' / DEC from Sersic fit
+TUNIT37 = 'DECIMAL DEGREES   ' /
+TFORM37 = 'D                 ' /
+TTYPE38 = 'RA_SER_SIG        ' / Sigma of Sersic fit RA
+TUNIT38 = 'ARCSECONDS        ' /
+TFORM38 = 'E                 ' /
+TTYPE39 = 'DEC_SER_SIG       ' / Sigma of Sersic fit DEC
+TUNIT39 = 'ARCSECONDS        ' /
+TFORM39 = 'E                 ' /
+TTYPE40 = 'SER_QF            ' / Sersic fit coverage/quality factor
+TUNIT40 = '                  ' /
+TFORM40 = 'E                 ' /
+TTYPE41 = 'PCS_R             ' / PSF Convolved Sersic fit model radius
+TUNIT41 = 'ARCSECONDS        ' /
+TFORM41 = 'E                 ' /
+TTYPE42 = 'MAG_PSC           ' / PSF Convolved Sersic fit model magnitude
+TUNIT42 = 'MAG               ' /
+TFORM42 = 'E                 ' /
+TTYPE43 = 'MAG_PSC_SIG       ' / Sigma of PSF Convolved Sersic fit model magnitude
+TUNIT43 = 'MAG               ' /
+TFORM43 = 'E                 ' /
+TTYPE44 = 'PCS_AB            ' / PSF Convolved Sersic fit model axis ratio
+TUNIT44 = '                  ' /
+TFORM45 = 'E                 ' /
+TTYPE46 = 'PCS_PHI           ' / Exponetial fit orientation angle
+TUNIT46 = 'DEGREES           ' /
+TFORM46 = 'E                 ' /
+TTYPE47 = 'PCS_NU            ' / PSF Convolved Sersic fit index nu
+TUNIT47 = '                  ' /
+TFORM47 = 'E                 ' /
+TTYPE48 = 'RA_PSC            ' / RA from PSF Convolved Sersic fit
+TUNIT48 = 'DECIMAL DEGREES   ' /
+TFORM48 = 'D                 ' /
+TTYPE49 = 'DEC_PSC           ' / DEC from PSF Convolved Sersic fit
+TUNIT49 = 'DECIMAL DEGREES   ' /
+TFORM49 = 'D                 ' /
+TTYPE50 = 'RA_PSC_SIG        ' / Sigma of PSF Convolved Sersic fit RA
+TUNIT50 = 'ARCSECONDS        ' /
+TFORM50 = 'E                 ' /
+TTYPE51 = 'DEC_PSC_SIG       ' / Sigma of PSF Convolved Sersic fit DEC
+TUNIT51 = 'ARCSECONDS        ' /
+TFORM51 = 'E                 ' /
+TTYPE52 = 'PCS_QF            ' / PSF Convolved Sersic coverage/quality factor
+TUNIT52 = '                  ' /
+TFORM52 = 'E                 ' /
+TTYPE53 = 'WL_SIGMA          ' / Weak lensing sigma
+TUNIT53 = '                  ' /
+TFORM53 = 'E                 ' /
+TTYPE54 = 'EPS_1             ' / Weak lensing eps(1)
+TUNIT54 = '                  ' /
+TFORM54 = 'E                 ' /
+TTYPE55 = 'EPS_2             ' / Weak lensing eps(2)
+TUNIT55 = '                  ' /
+TFORM55 = 'E                 ' /
+TTYPE56 = 'P_SM_1_1          ' / Weak lensing P_SM(1,1)
+TUNIT56 = '                  ' /
+TFORM56 = 'E                 ' /
+TTYPE57 = 'P_SM_1_2          ' / Weak lensing P_SM(1,2)
+TUNIT57 = '                  ' /
+TFORM57 = 'E                 ' /
+TTYPE58 = 'P_SM_2_1          ' / Weak lensing P_SM(2,1)
+TUNIT58 = '                  ' /
+TFORM58 = 'E                 ' /
+TTYPE59 = 'P_SM_2_2          ' / Weak lensing P_SM(2,2)
+TUNIT59 = '                  ' /
+TFORM59 = 'E                 ' /
+TTYPE60 = 'P_SH_1_1          ' / Weak lensing P_SH(1,1)
+TUNIT60 = '                  ' /
+TFORM60 = 'E                 ' /
+TTYPE61 = 'P_SH_1_2          ' / Weak lensing P_SH(1,2)
+TUNIT61 = '                  ' /
+TFORM61 = 'E                 ' /
+TTYPE62 = 'P_SH_2_1          ' / Weak lensing P_SH(2,1)
+TUNIT62 = '                  ' /
+TFORM62 = 'E                 ' /
+TTYPE63 = 'P_SH_2_2          ' / Weak lensing P_SH(2,2)
+TUNIT63 = '                  ' /
+TFORM63 = 'E                 ' /
+TTYPE64 = 'SB_R_1            ' / Surface brightness inside R #1
+TUNIT64 = 'MAG               ' /
+TFORM64 = 'E                 ' /
+TTYPE65 = 'SB_R_1_ERR        ' / Estimated error of SB inside R #1
+TUNIT65 = 'MAG               ' /
+TFORM65 = 'E                 ' /
+TTYPE66 = 'SB_R_1_VAR        ' / Estimated variance of SB inside R #1
+TUNIT66 = 'MAG               ' /
+TFORM66 = 'E                 ' /
+TTYPE67 = 'SB_R_2            ' / Surface brightness inside R #2
+TUNIT67 = 'MAG               ' /
+TFORM67 = 'E                 ' /
+TTYPE68 = 'SB_R_2_ERR        ' / Estimated error of SB inside R #2
+TUNIT68 = 'MAG               ' /
+TFORM68 = 'E                 ' /
+TTYPE69 = 'SB_R_2_VAR        ' / Estimated variance of SB inside R #2
+TUNIT69 = 'MAG               ' /
+TFORM69 = 'E                 ' /
+TTYPE70 = 'SB_R_3            ' / Surface brightness inside R #3
+TUNIT70 = 'MAG               ' /
+TFORM70 = 'E                 ' /
+TTYPE71 = 'SB_R_3_ERR        ' / Estimated error of SB inside R #3
+TUNIT71 = 'MAG               ' /
+TFORM71 = 'E                 ' /
+TTYPE72 = 'SB_R_3_VAR        ' / Estimated variance of SB inside R #3
+TUNIT72 = 'MAG               ' /
+TFORM72 = 'E                 ' /
+TTYPE73 = 'SB_R_4            ' / Surface brightness inside R #4
+TUNIT73 = 'MAG               ' /
+TFORM73 = 'E                 ' /
+TTYPE74 = 'SB_R_4_ERR        ' / Estimated error of SB inside R #4
+TUNIT74 = 'MAG               ' /
+TFORM74 = 'E                 ' /
+TTYPE75 = 'SB_R_4_VAR        ' / Estimated variance of SB inside R #4
+TUNIT75 = 'MAG               ' /
+TFORM75 = 'E                 ' /
+TTYPE76 = 'SB_R_5            ' / Surface brightness inside R #5
+TUNIT76 = 'MAG               ' /
+TFORM76 = 'E                 ' /
+TTYPE77 = 'SB_R_5_ERR        ' / Estimated error of SB inside R #5
+TUNIT77 = 'MAG               ' /
+TFORM77 = 'E                 ' /
+TTYPE78 = 'SB_R_5_VAR        ' / Estimated variance of SB inside R #5
+TUNIT78 = 'MAG               ' /
+TFORM78 = 'E                 ' /
+TTYPE79 = 'SB_R_6            ' / Surface brightness inside R #6
+TUNIT79 = 'MAG               ' /
+TFORM79 = 'E                 ' /
+TTYPE80 = 'SB_R_6_ERR        ' / Estimated error of SB inside R #6
+TUNIT80 = 'MAG               ' /
+TFORM80 = 'E                 ' /
+TTYPE81 = 'SB_R_5_VAR        ' / Estimated variance of SB inside R #6
+TUNIT81 = 'MAG               ' /
+TFORM81 = 'E                 ' /
+TTYPE82 = 'SB_R_7            ' / Surface brightness inside R #7
+TUNIT82 = 'MAG               ' /
+TFORM82 = 'E                 ' /
+TTYPE83 = 'SB_R_7_ERR        ' / Estimated error of SB inside R #7
+TUNIT83 = 'MAG               ' /
+TFORM83 = 'E                 ' /
+TTYPE84 = 'SB_R_7_VAR        ' / Estimated variance of SB inside R #7
+TUNIT84 = 'MAG               ' /
+TFORM84 = 'E                 ' /
+TTYPE85 = 'SB_R_8            ' / Surface brightness inside R #8
+TUNIT85 = 'MAG               ' /
+TFORM85 = 'E                 ' /
+TTYPE86 = 'SB_R_8_ERR        ' / Estimated error of SB inside R #8
+TUNIT86 = 'MAG               ' /
+TFORM86 = 'E                 ' /
+TTYPE87 = 'SB_R_8_VAR        ' / Estimated variance of SB inside R #8
+TUNIT87 = 'MAG               ' /
+TFORM87 = 'E                 ' /
+TTYPE88 = 'SB_R_9            ' / Surface brightness inside R #9
+TUNIT88 = 'MAG               ' /
+TFORM88 = 'E                 ' /
+TTYPE89 = 'SB_R_9_ERR        ' / Estimated error of SB inside R #9
+TUNIT89 = 'MAG               ' /
+TFORM89 = 'E                 ' /
+TTYPE90 = 'SB_R_9_VAR        ' / Estimated variance of SB inside R #9
+TUNIT90 = 'MAG               ' /
+TFORM90 = 'E                 ' /
+TTYPE91 = 'SB_R_10           ' / Surface brightness inside R #10
+TUNIT91 = 'MAG               ' /
+TFORM91 = 'E                 ' /
+TTYPE92 = 'SB_R_10_ERR       ' / Estimated error of SB inside R #10
+TUNIT92 = 'MAG               ' /
+TFORM92 = 'E                 ' /
+TTYPE93 = 'SB_R_10_VAR       ' / Estimated variance of SB inside R #10
+TUNIT93 = 'MAG               ' /
+TFORM93 = 'E                 ' /
+TTYPE94 = 'SB_R_11           ' / Surface brightness inside R #11
+TUNIT94 = 'MAG               ' /
+TFORM94 = 'E                 ' /
+TTYPE95 = 'SB_R_11_ERR       ' / Estimated error of SB inside R #11
+TUNIT95 = 'MAG               ' /
+TFORM95 = 'E                 ' /
+TTYPE96 = 'SB_R_11_VAR       ' / Estimated variance of SB inside R #11
+TUNIT96 = 'MAG               ' /
+TFORM96 = 'E                 ' /
+TTYPE97 = 'SB_R_12           ' / Surface brightness inside R #12
+TUNIT97 = 'MAG               ' /
+TFORM97 = 'E                 ' /
+TTYPE98 = 'SB_R_12_RR        ' / Estimated error of SB inside R #12
+TUNIT98 = 'MAG               ' /
+TFORM98 = 'E                 ' /
+TTYPE99 = 'SB_R_12_VAR       ' / Estimated variance of SB inside R #12
+TUNIT99 = 'MAG               ' /
+TFORM99 = 'E                 ' /
+TTYPE100= 'SB_R_13           ' / Surface brightness inside R #13
+TUNIT100= 'MAG               ' /
+TFORM100= 'E                 ' /
+TTYPE101= 'SB_R_13_ERR       ' / Estimated error of SB inside R #13
+TUNIT101= 'MAG               ' /
+TFORM101= 'E                 ' /
+TTYPE102= 'SB_R_13_VAR       ' / Estimated variance of SB inside R #13
+TUNIT102= 'MAG               ' /
+TFORM102= 'E                 ' /
+TTYPE103= 'SB_R_14           ' / Surface brightness inside R #14
+TUNIT103= 'MAG               ' /
+TFORM103= 'E                 ' /
+TTYPE104= 'SB_R_14_ERR       ' / Estimated error of SB inside R #14
+TUNIT104= 'MAG               ' /
+TFORM104= 'E                 ' /
+TTYPE105= 'SB_R_14_VAR       ' / Estimated variance of SB inside R #14
+TUNIT106= 'MAG               ' /
+TFORM106= 'E                 ' /
+TTYPE107= 'SB_R_15           ' / Surface brightness inside R #15
+TUNIT107= 'MAG               ' /
+TFORM107= 'E                 ' /
+TTYPE108= 'SB_R_15_ERR       ' / Estimated error of SB inside R #15
+TUNIT108= 'MAG               ' /
+TFORM108= 'E                 ' /
+TTYPE109= 'SB_R_15_VAR       ' / Estimated variance of SB inside R #15
+TUNIT109= 'MAG               ' /
+TFORM109= 'E                 ' /
+TTYPE110= 'LOG_A             ' / Log of asymmetry parameter
+TUNIT110= '                  ' /
+TFORM110= 'E                 ' /
+TTYPE111= 'LOG_C             ' / Log of concentration parameter
+TUNIT111= '                  ' /
+TFORM111= 'E                 ' /
+TTYPE112= 'INCL              ' / Inclination
+TUNIT112= '                  ' /
+TFORM112= 'E                 ' /
+CHECKSUM=                      / Header checksum
+DATASUM =                      / Data checksum
Index: /tags/sj_tags/sj_root_20080929/icd-demo/raw/p2frame.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/raw/p2frame.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/raw/p2frame.txt	(revision 22322)
@@ -0,0 +1,29 @@
+SIMPLE  =                    T / FITS STANDARD
+BITPIX  =                    0 / FITS BITS/PIXEL
+NAXIS   =                    0 / NUMBER OF AXES
+EXTEND  =                    T /
+DATE    = 'DD/MM/YY'           / Date file created
+CHECKSUM=                      / Checksum for this header
+COMMENT
+COMMENT   P2 Frame Metadata HDU
+COMMENT
+TELESCOP= 'PS1               ' / Telescope name
+TELNUM  =                    1 / Telescope number
+CAMERA  = 'GPC1              ' / Camera name
+CAMERAID=                    1 / Camera number
+OBS_ID  = '                  ' / Observation identifier
+ANALVER =                  XXX / Analysis version number
+P1RECIP =                  YYY / Phase 1 recipe
+P2RECIP =                  ZZZ / Phase 2 recipe
+P3RECIP =                  AAA / Phase 3 recipe
+NFILES  =                    1 / Number of associated files
+CERROR  =              xxx.yyy / Scatter in astrometry solution (MAS)
+PHOT_CS =              aa.bbbb / Scater in photometric solution (mag)
+MJDATE  =        XXXXX.YYYYYYY / MJD at start of exposure
+EXPTIME =               TTT.SS / Exposure time (seconds)
+FILTER  = 'r.12345           ' / Filter identification string
+FILTERID=                    2 / Filter holder position
+AIRMASS =               xx.yyy / Airmass at start of exposure
+RA      =            aaa.bbbbb / Telescope boresight in RA (degrees)
+DEC     =            ccc.ddddd / Telescope boresight in DEC (degrees)
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/raw/p2image.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/raw/p2image.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/raw/p2image.txt	(revision 22322)
@@ -0,0 +1,101 @@
+BITPIX  =                    8 / FITS BITS/PIXEL
+NAXIS   =                    0 / NUMBER OF AXES
+EXTEND  =                    T /
+DATE    = 'DD/MM/YY'           / Date file created
+CHECKSUM=                      / Checksum for this header
+COMMENT
+COMMENT  Combination (P4S, P4D, Image Stack/ Cumulative Sky) Image
+COMMENT  Metadata HDU
+COMMENT  
+OBS_ID  = '                  ' / Observation Identifier
+CLASSID =                      / Class ID = focal plane location
+DETECTID=                      / Numerical ID of detector
+NX      =                 CCCC / Number of columns on the detector
+NY      =                 RRRR / Number of rows on the detector 
+COMMENT
+COMMENT  Astrometric Solution (up to linear terms)
+COMMENT
+NASTRO  =                 NNNN / Number of astrometry stars
+CERROR  =              XXX.YYY / Scatter in astrometry solution (MAS)
+CTYPE1  = 'RA---TAN'           / WCS Coordinate type
+CTYPE2  = 'DEC--TAN'           / WCS Coordinate type
+CRVAL1  =      321.30941666667 / WCS Ref value (RA in decimal degrees)
+CRVAL2  =     -5.1685277777778 / WCS Ref value (DEC in decimal degrees)
+CRPIX1  =    -1051.17692913657 / WCS Coordinate reference pixel
+CRPIX2  =     2033.40939404705 / WCS Coordinate reference pixel
+CD1_1   =  1.13378419402790E-4 / WCS Coordinate scale matrix
+CD1_2   =  8.93919113249280E-8 / WCS Coordinate scale matrix
+CD2_1   =  -5.4105516940550E-7 / WCS Coordinate scale matrix
+CD2_2   =  -1.1365323690945E-4 / WCS Coordinate scale matrix
+COMMENT
+COMMENT  Higher order terms in astrometric solution
+COMMENT
+POLYORD =                    3 / Polynomial Order
+PCA1X3Y0=                      / WCS, Axis 1, coefficient of X^3
+PCA1X2Y1=                      / WCS, Axis 1, coefficient of X^2 * Y
+PCA1X1Y2=                      / WCS, Axis 1, coefficient of X * Y^2
+PCA1X0Y3=                      / WCS, Axis 1, coefficient of Y^3
+PCA1X2Y0=                      / WCS, Axis 1, coefficient of X^2
+PCA1X1Y1=                      / WCS, Axis 1, coefficient of X * Y
+PCA1X0Y2=                      / WCS, Axis 1, coefficient of Y^2
+PCA1X3Y0=                      / WCS, Axis 2, coefficient of X^3
+PCA1X2Y1=                      / WCS, Axis 2, coefficient of X^2 * Y
+PCA1X1Y2=                      / WCS, Axis 2, coefficient of X * Y^2
+PCA1X0Y3=                      / WCS, Axis 2, coefficient of Y^3
+PCA1X2Y0=                      / WCS, Axis 2, coefficient of X^2
+PCA1X1Y1=                      / WCS, Axis 2, coefficient of X * Y
+PCA1X0Y2=                      / WCS, Axis 2, coefficient of Y^2
+COMMENT
+COMMENT  PSF Description Parameters
+COMMENT
+PSFMOD  =                      / PSF Model number
+PSFFWHM =                      / PSF Full width at Half Maximum (pixels)
+PSFWIDX =                      / PSF Width in X coordinate (pixels)
+PSFWIDY =                      / PSF Width in Y coordinate (pixels)
+PSFTHETA=                      / PSF Orientation angle (degrees)
+PSF_EX1 =                      / PSF Extra parameter #1
+PSF_EX2 =                      / PSF Extra parameter #2
+COMMENT
+COMMENT  Photometry Calibration information
+COMMENT
+COMMENT   Formula for Photometry, based on keywords given in this header:
+COMMENT   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+COMMENT   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+COMMENT
+PHOTCODE=                      / Photometry reduction code identifier
+PHOTOCID=                      / Numerical code for reduction code
+PHOT_C  =              25.7150 / Elixir zero point - measured for camera run
+PHOT_CS =               0.0048 / Elixir zero point - scatter
+PHOT_NS =                   19 / Elixir zero point - N stars
+PHOT_NM =                    5 / Elixir zero point - N images
+PHOT_C0 =              25.7430 / Elixir zero point - nominal
+PHOT_X  =               0.0830 / Elixir zero point - color term
+PHOT_K  =              -0.0400 / Elixir zero point - airmass term
+PHOT_C1 = 'i_SDSS            ' / Elixir zero point - color 1
+PHOT_C2 = 'r_SDSS            ' / Elixir zero point - color 2
+COMMENT
+COMMENT  Some statistics on this image
+COMMENT
+NDETECT =                      / Number of detections
+MAGSAT  =                      / Magnitude at which sources saturate
+MAGCOMP =                      / Magnitude for which frame is 95% complete
+SKYVAL  =                      / Median sky value on detector
+SKYVAR  =                      / Variance of sky values
+BIASVAL =                      / Mean bias level of frame
+BIASVAR =                      / Variance of bias values
+COMMENT
+COMMENT  Images used to process this exposure
+COMMENT 
+DETREND1= '                  ' / 
+DETREND2= '                  ' / 
+DETREND3= '                  ' / 
+DETREND4= '                  ' / 
+DETREND5= '                  ' / 
+DETREND6= '                  ' / 
+DETREND7= '                  ' / 
+DETREND8= '                  ' / 
+COMMENT
+COMMENT  Quality Assurance Flags from IPP
+COMMENT
+QAFLAGS = '                  ' / IP QA flags
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/raw/p4image.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/raw/p4image.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/raw/p4image.txt	(revision 22322)
@@ -0,0 +1,93 @@
+
+XTENSION= 'IMAGE'               /
+BITPIX  =                    8  /
+NAXIS   =                    0  /
+COMMENT
+COMMENT  Calibration information for this sky cell
+COMMENT
+SKYCELL =                       / Sky Cell ID number
+REFID   =                       / Reference Sky Cell ID number
+SUBTRID =                       / ID of Sky Cell Image subtracted 
+FILTER  =                       / Filter 
+STACKID =                       / ID for this image stack
+NP2IMGS =                       / Number of P2 images contributing
+ANALVER =                       / Software Analysis Version
+COMMENT
+COMMENT  Astrometric Solution (up to linear terms)
+COMMENT
+NASTRO  =                 NNNN  / Number of astrometry stars
+CERROR  =              XXX.YYY  / Scatter in astrometry solution (MAS)
+CTYPE1  = 'RA---TAN'            / WCS Coordinate type
+CTYPE2  = 'DEC--TAN'            / WCS Coordinate type
+CRVAL1  =      321.30941666667  / WCS Ref value (RA in decimal degrees)
+CRVAL2  =     -5.1685277777778  / WCS Ref value (DEC in decimal degrees)
+CRPIX1  =    -1051.17692913657  / WCS Coordinate reference pixel
+CRPIX2  =     2033.40939404705  / WCS Coordinate reference pixel
+CD1_1   =  1.13378419402790E-4  / WCS Coordinate scale matrix
+CD1_2   =  8.93919113249280E-8  / WCS Coordinate scale matrix
+CD2_1   =  -5.4105516940550E-7  / WCS Coordinate scale matrix
+CD2_2   =  -1.1365323690945E-4  / WCS Coordinate scale matrix
+COMMENT
+COMMENT  Higher order terms in astrometric solution
+COMMENT
+POLYORD =                    3  / Polynomial Order
+PCA1X3Y0=                       / WCS, Axis 1, coefficient of X^3
+PCA1X2Y1=                       / WCS, Axis 1, coefficient of X^2 * Y
+PCA1X1Y2=                       / WCS, Axis 1, coefficient of X * Y^2
+PCA1X0Y3=                       / WCS, Axis 1, coefficient of Y^3
+PCA1X2Y0=                       / WCS, Axis 1, coefficient of X^2
+PCA1X1Y1=                       / WCS, Axis 1, coefficient of X * Y
+PCA1X0Y2=                       / WCS, Axis 1, coefficient of Y^2
+PCA1X3Y0=                       / WCS, Axis 2, coefficient of X^3
+PCA1X2Y1=                       / WCS, Axis 2, coefficient of X^2 * Y
+PCA1X1Y2=                       / WCS, Axis 2, coefficient of X * Y^2
+PCA1X0Y3=                       / WCS, Axis 2, coefficient of Y^3
+PCA1X2Y0=                       / WCS, Axis 2, coefficient of X^2
+PCA1X1Y1=                       / WCS, Axis 2, coefficient of X * Y
+PCA1X0Y2=                       / WCS, Axis 2, coefficient of Y^2
+COMMENT
+COMMENT  PSF Description Parameters
+COMMENT
+PSFMOD  =                       / PSF Model number
+PSFFWHM =                       / PSF Full width at Half Maximum (pixels)
+PSFWIDX =                       / PSF Width in X coordinate (pixels)
+PSFWIDY =                       / PSF Width in Y coordinate (pixels)
+PSFTHETA=                       / PSF Orientation angle (degrees)
+PSF_EX1 =                       / PSF Extra parameter #1
+PSF_EX2 =                       / PSF Extra parameter #2
+COMMENT
+COMMENT  Photometry Calibration information
+COMMENT
+COMMENT   Formula for Photometry, based on keywords given in this header:
+COMMENT   m = -2.5*log(DN) + 2.5*log(EXPTIME)
+COMMENT   M = m + PHOT_C + PHOT_K*(AIRMASS - 1) + PHOT_X*(PHOT_C1 - PHOT_C2)
+COMMENT
+PHOTCODE=                      / Photometry reduction code identifier
+PHOTOCID=                      / Numerical code for reduction code
+PHOT_C  =              25.7150 / Elixir zero point - measured for camera run
+PHOT_CS =               0.0048 / Elixir zero point - scatter
+PHOT_NS =                   19 / Elixir zero point - N stars
+PHOT_NM =                    5 / Elixir zero point - N images
+PHOT_C0 =              25.7430 / Elixir zero point - nominal
+PHOT_X  =               0.0830 / Elixir zero point - color term
+PHOT_K  =              -0.0400 / Elixir zero point - airmass term
+PHOT_C1 = 'i_SDSS            ' / Elixir zero point - color 1
+PHOT_C2 = 'r_SDSS            ' / Elixir zero point - color 2
+COMMENT
+COMMENT  Some statistics on this image
+COMMENT
+NDETECT =                      / Number of detections
+MAGSAT  =                      / Magnitude at which sources saturate
+MAGCOMP =                      / Magnitude for which frame is 95% complete
+SKYVAL  =                      / Median sky value on detector
+SKYVAR  =                      / Variance of sky values
+BIASVAL =                      / Mean bias level of frame
+BIASVAR =                      / Variance of bias values
+COMMENT
+COMMENT  P2 images used to generate this image stack
+COMMENT 
+COMMENT
+COMMENT  Quality Assurance Flags from IPP
+COMMENT
+QAFLAGS = '                  ' / IP QA flags
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/raw/p4set.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/raw/p4set.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/raw/p4set.txt	(revision 22322)
@@ -0,0 +1,8 @@
+SIMPLE  =                    T  / 
+BITPIX  =                    8  / FITS BITS/PIXEL
+NAXIS   =                    0  / NUMBER OF AXES
+EXTEND  =                    T  /
+DATE    = 'DD/MM/YY'            / Date file created
+NCELLS  =                  NNN  / Number of sky cell extensions
+CHECKSUM=                       / Checksum for this header
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/raw/predefined-objects.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/raw/predefined-objects.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/raw/predefined-objects.txt	(revision 22322)
@@ -0,0 +1,34 @@
+ 
+# this file describes a FITS table to be simulated
+EXTNAME STR PREDEFINED_OBJECTS
+
+HEADER METADATA
+END
+
+# description of the FITS table columns
+COLUMNS METADATA
+  TYPE              TTYPE TFORM   TUNIT        TMIN TMAX  COMMENT
+  IPP_ID            TTYPE     J   NONE         NONE NONE  "IPP object identification index"   
+  RA                TTYPE     D   DEGREES      NONE NONE  "RA "                              
+  DEC               TTYPE     D   DEGREES      NONE NONE  "DEC "                             
+  RA_SIG            TTYPE     E   ARCSECONDS   NONE NONE  "Sigma of RA"                      
+  DEC_SIG           TTYPE     D   ARCSECONDS   NONE NONE  "Sigma of DEC "                    
+  MU_RA_COS_DEC     TTYPE     E   MAS/YR       NONE NONE  "Mu in RA * Cos (Dec)"             
+  MU_DEC            TTYPE     E   MAS/YR       NONE NONE  "Mu in DEC"                        
+  MU_RA_COS_DEC_SIG TTYPE     E   MAS/YR       NONE NONE  "sigma of Mu in RA * Cos(Dec)"     
+  MU_DEC_SIG        TTYPE     E   MAS/YR       NONE NONE  "sigma of mu in DEC"               
+  PARALLAX          TTYPE     E   MAS          NONE NONE  "Parallax"                         
+  PARALLAX_SIG      TTYPE     E   MAS          NONE NONE  "Sigma of Parallax"                
+  G_MAG             TTYPE     E   MAG          NONE NONE  "g magnitude"                      
+  G_MAG_SIG         TTYPE     E   MAG          NONE NONE  "Sigma of g magnitude"             
+  R_MAG             TTYPE     E   MAG          NONE NONE  "r magnitude"                      
+  R_MAG_SIG         TTYPE     E   MAG          NONE NONE  "Sigma of r magnitude"             
+  I_MAG             TTYPE     E   MAG          NONE NONE  "i magnitude"                      
+  I_MAI_SIG         TTYPE     E   MAG          NONE NONE  "Sigma of i magnitude"             
+  Y_MAG             TTYPE     E   MAG          NONE NONE  "y magnitude"                      
+  Y_MAY_SIG         TTYPE     E   MAG          NONE NONE  "Sigma of y magnitude"             
+  Z_MAG             TTYPE     E   MAG          NONE NONE  "z magnitude"                      
+  Z_MAZ_SIG         TTYPE     E   MAG          NONE NONE  "Sigma of z magnitude"             
+  W_MAG             TTYPE     E   MAG          NONE NONE  "w magnitude"                      
+  W_MAG_SIG         TTYPE     E   MAG          NONE NONE  "Sigma of w magnitude"             
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/raw/psf-fits-detections.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/raw/psf-fits-detections.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/raw/psf-fits-detections.txt	(revision 22322)
@@ -0,0 +1,86 @@
+
+XTENSION= 'BINTABLE          ' /
+BITPIX  =                    8 /
+NAXIS   =                    2 /
+NAXIS1  =           NNNNNNNNNN / Number of objects in the table
+NAXIS2  =                   96 / Number of bytes per row 
+PCOUNT  =                    0 /
+GCOUNT  =                    1 /
+TFIELDS =                   23 /
+EXTNAME = 'PSF_FIT_PARAMS    ' /
+COMMENT   
+COMMENT   This HDU applies to all detections that have been analyzed
+COMMENT   with the IPP PSF fitting procedure. Note that the IPP_IDET
+COMMENT   must be unique within a file so as to allow matching up
+COMMENT   sources between the PSF matching and either the alternative
+COMMENT   PSF fits for P2, P4S, and P4D detections or the extended
+COMMENT   source fits to the Image Stack/Cumulative Sky detections.
+COMMENT
+TTYPE1  = 'IPP_IDET          ' / IPP detection identifer index
+TUNIT1  = '                  ' /
+TFORM1  = 'J                 ' /
+TTYPE2  = 'X_PSF             ' / PSF x coordinate
+TUNIT2  = 'PIXELS            ' /
+TFORM2  = 'E                 ' /
+TTYPE3  = 'Y_PSF             ' / PSF y coordinate
+TUNIT3  = 'PIXELS            ' /
+TFORM3  = 'E                 ' /
+TTYPE4  = 'X_PSF_SIG         ' / Sigma in PSF x coordinate
+TUNIT4  = 'PIXELS            ' /
+TFORM4  = 'E                 ' /
+TTYPE5  = 'Y_PSF_SIG         ' / Sigma in PSF y coordinate
+TUNIT5  = 'PIXELS            ' /
+TFORM5  = 'E                 ' /
+TTYPE6  = 'RA_PSF            ' / RA from PSF fit
+TUNIT6  = 'DECIMAL DEGREES   ' /
+TFORM6  = 'D                 ' /
+TTYPE7  = 'DEC_PSF               ' / DEC from PSF fit
+TUNIT7  = 'DECIMAL DEGREES   ' /
+TFORM7  = 'D                 ' /
+TTYPE8  = 'RA_PSF_SIG        ' / Sigma of PSF fit RA
+TUNIT8  = 'ARCSECONDS        ' /
+TFORM8  = 'E                 ' /
+TTYPE9  = 'DEC_PSF_SIG           ' / Sigma of PSF fit DEC
+TUNIT9  = 'ARCSECONDS        ' /
+TFORM9  = 'E                 ' /
+TTYPE10 = 'PSF_INST_MAG      ' / PSF fit instrumental magnitude
+TUNIT10 = 'MAG               ' /
+TFORM10 = 'E                 ' /
+TTYPE11 = 'PSF_INST_MAG_SIG  ' / Sigma of PSF instrumental magnitude
+TUNIT11 = 'MAG               ' /
+TFORM11 = 'E                 ' /
+TTYPE12 = 'PEAK_FLUX_AS_MAG  ' / Peak flux expressed as magnitude
+TUNIT12 = 'MAG               ' /
+TFORM12 = 'E                 ' /
+TTYPE13 = 'SKY               ' / Sky level
+TUNIT13 = 'ADU               ' /
+TFORM13 = 'E                 ' /
+TTYPE14 = 'SKY_SIGMA         ' / Sigma of sky level
+TUNIT14 = 'ADU               ' /
+TFORM14 = 'E                 ' /
+TTYPE15 = 'STAR_GALAXY_SEP   ' / Star-Galaxy separator
+TUNIT15 = '                  ' /
+TFORM15 = 'E                 ' /
+TTYPE16 = 'PSF_WIDTH_X       ' / PSF width in x coordinate
+TUNIT16 = 'ARCSECONDS        ' /
+TFORM16 = 'E                 ' /
+TTYPE17 = 'PSF_WIDTH_Y       ' / PSF width in y coordinate
+TUNIT17 = 'ARCSECONDS        ' /
+TFORM17 = 'E                 ' /
+TTYPE19 = 'PSF_THETA         ' / PSF orientation angle
+TUNIT18 = 'DEGREES           ' /
+TFORM18 = 'E                 ' /
+TTYPE19 = 'PSF_QF            ' / PSF coverage/quality factor
+TUNIT19 = '                  ' /
+TFORM19 = 'E                 ' /
+TTYPE20 = 'CAL_PSF_MAG           ' / Calibrated magnitude
+TUNIT20 = 'MAG               ' /
+TFORM20 = 'E                 ' /
+TTYPE21 = 'CAL_PSF_MAG_SIG   ' / Sigma of calibrated magnitude
+TUNIT21 = 'MAG               ' /
+TFORM21 = 'E                 ' /
+TTYPE22 = 'N_FRAMES          ' / Number of frames overlapping source center
+TUNIT22 = '                  ' /
+TFORM22 = 'I                 ' /
+CHECKSUM=                      / Header checksum
+DATASUM =                      / Data section checksum
Index: /tags/sj_tags/sj_root_20080929/icd-demo/raw/skycell.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/raw/skycell.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/raw/skycell.txt	(revision 22322)
@@ -0,0 +1,56 @@
+SIMPLE  =                    T / FITS STANDARD
+BITPIX  =                    8 / FITS BITS/PIXEL
+NAXIS   =                    0 / NUMBER OF AXES
+EXTEND  =                    T /
+DATE    = 'DD/MM/YY'           / Date file created
+CHECKSUM=                      / Checksum for this header
+COMMENT
+COMMENT   Sky Cell Definition Metadata
+COMMENT
+END
+
+XTENSION= 'BINTABLE          ' / 
+BITPIX  =                    8 /
+NAXIS   =                    2 /
+NAXIS1  =                   68 /
+NAXIS2  =             NNNNNNNN /
+PCOUNT  =                    0 /
+GCOUNT  =                    1 /
+TFIELDS =                    9 /
+EXTNAME = 'SKY_CELL_TABLE    ' /
+COMMENT   
+COMMENT   This file defines the regions on the sky covered by
+COMMENT   each sky cell used by the IPP. For this sample, it
+COMMENT   is assumed each cell is a quadralateral defined by
+COMMENT   4 vertices on the sky.
+COMMENT
+CHECKSUM=                      / Checksum for this header
+DATASUM =                      / Checksum for table data
+TTYPE1  = 'ID                ' / Sky cell identifier
+TUNIT1  = '                  ' /
+TFORM1  = 'J                 ' /
+TTYPE2  = 'RA1               ' / RA of 1st cell vertex.
+TUNIT2  = 'DEGREES           ' /
+TFORM2  = 'D                 ' /
+TTYPE3  = 'DEC1              ' / DEC of 1st cell vertex.
+TUNIT3  = 'DEGREES           ' /
+TFORM3  = 'D                 ' /
+TTYPE4  = 'RA2               ' / RA of 2nd cell vertex.
+TUNIT4  = 'DEGREES           ' /
+TFORM4  = 'D                 ' /
+TTYPE5  = 'DEC2              ' / DEC of 2nd cell vertex.
+TUNIT5  = 'DEGREES           ' /
+TFORM5  = 'D                 ' /
+TTYPE6  = 'RA3               ' / RA of 34d cell vertex.
+TUNIT6  = 'DEGREES           ' /
+TFORM6  = 'D                 ' /
+TTYPE7  = 'DEC3              ' / DEC of 3rd cell vertex.
+TUNIT7  = 'DEGREES           ' /
+TFORM7  = 'D                 ' /
+TTYPE8  = 'RA4               ' / RA of 4th cell vertex.
+TUNIT8  = 'DEGREES           ' /
+TFORM8  = 'D                 ' /
+TTYPE9  = 'DEC4              ' / DEC of 4th cell vertex.
+TUNIT9  = 'DEGREES           ' /
+TFORM9  = 'D                 ' /
+END
Index: /tags/sj_tags/sj_root_20080929/icd-demo/src/icd-demo.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/src/icd-demo.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/src/icd-demo.c	(revision 22322)
@@ -0,0 +1,209 @@
+# include <stdio.h>
+# include <strings.h>
+# include <math.h>
+# include "pslib.h"
+
+int parseFile (psFits *fits, char *file);
+
+int main (int argc, char **argv) {
+
+    unsigned int nFail;
+
+    if (argc != 3) {
+        fprintf (stderr, "USAGE: icd-demo (input) (output)\n");
+        exit (2);
+    }
+
+    // construct a sample FITS table based on the given descriptive metadata file
+    psMetadata *fd      = psMetadataConfigRead(NULL, &nFail, argv[1], false);
+
+    psFits *fits = psFitsOpen (argv[2], "w");
+
+    psMetadata *header  = psMetadataLookupPtr (NULL, fd, "HEADER");
+    psFitsWriteBlank (fits, header, "");
+
+    // get the list pointers for the PSF_MODEL entries
+    psList *list = NULL;
+    psMetadataItem *mdi = psMetadataLookup (fd, "TABLE");
+    if (mdi->type == PS_DATA_STRING) {
+        list = psListAlloc(NULL);
+        psListAdd (list, PS_LIST_HEAD, mdi);
+    } else {
+        list = psMemIncrRefCounter(mdi->data.list);
+    }
+
+    // try each model option listed in config
+    psMetadataItem *item = NULL;
+    psListIterator *iter = psListIteratorAlloc (list, PS_LIST_HEAD, FALSE);
+    while ((item = psListGetAndIncrement (iter)) != NULL) {
+        char *file = item->data.V;
+        parseFile (fits, file);
+    }
+    psFitsClose (fits);
+}
+
+int parseFile (psFits *fits, char *file) {
+
+    unsigned int nFail;
+    char unitWord[64];
+    double min, max, rvalue, range;
+
+    fprintf (stderr, "reading %s\n", file);
+    psMetadataItem *col;
+    psRandom *rnd = psRandomAlloc (PS_RANDOM_TAUS, 0);
+
+    // construct a sample FITS table based on the given descriptive metadata file
+    psMetadata *fd      = psMetadataConfigRead(NULL, &nFail, file, false);
+
+    psMetadata *blank  = psMetadataLookupPtr (NULL, fd, "BLANK");
+    if (blank) {
+        fprintf (stderr, "adding BLANK\n");
+        psFitsWriteBlank (fits, blank, "");
+    }
+
+    psMetadata *fitstable  = psMetadataLookupPtr (NULL, fd, "FITSTABLE");
+    if (fitstable) {
+        fprintf (stderr, "adding TABLE\n");
+        psMetadata *header  = psMetadataLookupPtr (NULL, fitstable, "HEADER");
+        psMetadata *columns = psMetadataLookupPtr (NULL, fitstable, "COLUMNS");
+        psMetadataIterator *iter = psMetadataIteratorAlloc (columns, PS_LIST_HEAD, NULL);
+        char *extname       = psMetadataLookupStr (NULL, header, "EXTNAME");
+
+        psArray *table = psArrayAllocEmpty (10);
+
+        // XXX insert unit values: we need a method to do this in psFitsWriteTable
+        int nCol = 1;
+        while ((col = psMetadataGetAndIncrement (iter)) != NULL) {
+            psMetadata *md = col->data.V;
+            char *unit = psMetadataLookupStr (NULL, md, "TUNIT");
+            if (strcasecmp (unit, "NONE")) {
+                sprintf (unitWord, "TUNIT%d", nCol);
+                psMetadataAddStr (header, PS_LIST_TAIL, unitWord, 0, "", unit);
+            }
+            nCol ++;
+        }
+        psMetadataIteratorSet (iter, PS_LIST_HEAD);
+
+        // make a total of NN fake data columns
+        for (int i = 0; i < 10; i++) {
+
+            int nCol = 1;
+
+            psMetadata *row = psMetadataAlloc ();
+
+            while ((col = psMetadataGetAndIncrement (iter)) != NULL) {
+
+                psMetadata *md = col->data.V;
+
+                char *format = psMetadataLookupStr (NULL, md, "TFORM");
+                char *comment = psMetadataLookupStr (NULL, md, "COMMENT");
+                char *unit = psMetadataLookupStr (NULL, md, "TUNIT");
+                if (strcasecmp (unit, "NONE")) {
+                    sprintf (unitWord, "TUNIT%d", nCol);
+                    psMetadataAddStr (header, PS_LIST_TAIL, unitWord, 0, "", unit);
+                }
+
+                if (!strcasecmp (format, "I")) {
+                    int value;
+
+                    char *minWord = psMetadataLookupStr (NULL, md, "TMIN");
+                    char *maxWord = psMetadataLookupStr (NULL, md, "TMAX");
+
+                    min = atoi (minWord);
+                    if (!strcasecmp (minWord, "NONE")) min = 0;
+                    max = atoi (maxWord);
+                    if (!strcasecmp (maxWord, "NONE")) max = 100;
+                    range = max - min;
+
+                    rvalue = psRandomUniform (rnd);
+                    value = range * rvalue + min;
+
+                    psMetadataAdd (row, PS_LIST_TAIL, col->name, PS_DATA_S16, comment, value);
+                }
+
+                if (!strcasecmp (format, "J")) {
+                    int value;
+
+                    char *minWord = psMetadataLookupStr (NULL, md, "TMIN");
+                    char *maxWord = psMetadataLookupStr (NULL, md, "TMAX");
+
+                    min = atoi (minWord);
+                    if (!strcasecmp (minWord, "NONE")) min = 0;
+                    max = atoi (maxWord);
+                    if (!strcasecmp (maxWord, "NONE")) max = 100;
+                    range = max - min;
+
+                    rvalue = psRandomUniform (rnd);
+                    value = range * rvalue + min;
+
+                    psMetadataAdd (row, PS_LIST_TAIL, col->name, PS_DATA_S32, comment, value);
+                }
+
+                if (!strcasecmp (format, "K")) {
+                    long int value;
+
+                    char *minWord = psMetadataLookupStr (NULL, md, "TMIN");
+                    char *maxWord = psMetadataLookupStr (NULL, md, "TMAX");
+
+                    min = atoi (minWord);
+                    if (!strcasecmp (minWord, "NONE")) min = 0;
+                    max = atoi (maxWord);
+                    if (!strcasecmp (maxWord, "NONE")) max = 100;
+                    range = max - min;
+
+                    rvalue = psRandomUniform (rnd);
+                    value = range * rvalue + min;
+
+                    psMetadataAdd (row, PS_LIST_TAIL, col->name, PS_DATA_S64, comment, value);
+                }
+
+                if (!strcasecmp (format, "E")) {
+                    float value;
+
+                    char *minWord = psMetadataLookupStr (NULL, md, "TMIN");
+                    char *maxWord = psMetadataLookupStr (NULL, md, "TMAX");
+
+                    min = atoi (minWord);
+                    if (!strcasecmp (minWord, "NONE")) min = 0;
+                    max = atoi (maxWord);
+                    if (!strcasecmp (maxWord, "NONE")) max = 100;
+                    range = max - min;
+
+                    rvalue = psRandomUniform (rnd);
+                    value = range * rvalue + min;
+
+                    psMetadataAdd (row, PS_LIST_TAIL, col->name, PS_DATA_F32, comment, value);
+                }
+
+                if (!strcasecmp (format, "D")) {
+                    double value;
+
+                    char *minWord = psMetadataLookupStr (NULL, md, "TMIN");
+                    char *maxWord = psMetadataLookupStr (NULL, md, "TMAX");
+
+                    min = atoi (minWord);
+                    if (!strcasecmp (minWord, "NONE")) min = 0;
+                    max = atoi (maxWord);
+                    if (!strcasecmp (maxWord, "NONE")) max = 100;
+                    range = max - min;
+
+                    rvalue = psRandomUniform (rnd);
+                    value = range * rvalue + min;
+
+                    psMetadataAdd (row, PS_LIST_TAIL, col->name, PS_DATA_F64, comment, value);
+                }
+
+                if (!strcasecmp (format, "A")) {
+                    char *value = NULL;
+                    psStringAppend (&value, "word %03d", i);
+                    psMetadataAdd (row, PS_LIST_TAIL, col->name, PS_DATA_STRING, "", value);
+                }
+            }
+            psArrayAdd (table, 100, row);
+            psFree (row);
+            psMetadataIteratorSet (iter, PS_LIST_HEAD);
+        }
+        psFitsInsertTable (fits, header, table, extname, true);
+    }
+    return 1;
+}
Index: /tags/sj_tags/sj_root_20080929/icd-demo/src/icd-test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/icd-demo/src/icd-test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/icd-demo/src/icd-test.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include <stdio.h>
+# include <strings.h>
+# include <math.h>
+# include "pslib.h"
+
+int main (int argc, char **argv) {
+
+    if (argc != 3) {
+        fprintf (stderr, "USAGE: icd-demo (input) (output)\n");
+        exit (2);
+    }
+
+    // construct a sample FITS table based on the given descriptive metadata file
+    psFits *fits = psFitsOpen (argv[2], "w");
+
+    {
+        psMetadata *header  = psMetadataAlloc ();
+        psMetadataAddStr (header, PS_LIST_HEAD, "EXTID", 0, "", "blank");
+        psFitsWriteBlank (fits, header, "blank");
+    }
+
+    {
+        psMetadata *header  = psMetadataAlloc ();
+        psMetadataAddStr (header, PS_LIST_HEAD, "EXTID", 0, "", "blank1");
+        psFitsWriteBlank (fits, header, "blank1");
+    }
+
+    {
+        psMetadata *header  = psMetadataAlloc ();
+        psMetadataAddStr (header, PS_LIST_HEAD, "EXTID", 0, "", "table1");
+
+        psArray *table = psArrayAllocEmpty (100);
+        for (int i = 0; i < 10; i++) {
+
+            psMetadata *row = psMetadataAlloc ();
+            psMetadataAdd (row, PS_LIST_TAIL, "N", PS_DATA_S16, "", i);
+            psMetadataAdd (row, PS_LIST_TAIL, "F", PS_DATA_F32, "", i*0.2);
+            psMetadataAdd (row, PS_LIST_TAIL, "G", PS_DATA_F32, "", i*0.5);
+            psArrayAdd (table, 100, row);
+            psFree (row);
+        }
+
+        psFitsInsertTable (fits, header, table, "table1", true);
+    }
+
+    {
+        psMetadata *header  = psMetadataAlloc ();
+        psMetadataAddStr (header, PS_LIST_HEAD, "EXTID", 0, "", "blank2");
+        psFitsWriteBlank (fits, header, "blank2");
+    }
+
+    {
+        psMetadata *header  = psMetadataAlloc ();
+        psMetadataAddStr (header, PS_LIST_HEAD, "EXTID", 0, "", "table2");
+
+        psArray *table = psArrayAllocEmpty (100);
+        for (int i = 0; i < 10; i++) {
+
+            psMetadata *row = psMetadataAlloc ();
+            psMetadataAdd (row, PS_LIST_TAIL, "N", PS_DATA_S16, "", i);
+            psMetadataAdd (row, PS_LIST_TAIL, "F", PS_DATA_F32, "", i*0.2);
+            psMetadataAdd (row, PS_LIST_TAIL, "G", PS_DATA_F32, "", i*0.5);
+            psArrayAdd (table, 100, row);
+            psFree (row);
+        }
+
+        psFitsInsertTable (fits, header, table, "table2", true);
+    }
+
+    psFitsClose (fits);
+}
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+src
+Makefile
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/INSTALL	(revision 22322)
@@ -0,0 +1,15 @@
+
+after setting up psconfig:
+
+alala example:
+ psconfigure --dbhost alala --dbuser ipp --dbpass ipp --htdocs /var/www/localhost/htdocs
+
+kiawe / alala example:
+ psconfigure --dbhost alala --dbuser ipp --dbpass ipp --htdocs /var/www/kiawe
+
+ipp004 / ipp017 example:
+ psconfigure --dbhost ipp017 --dbuser ipp --dbpass ipp --htdocs ~ipp/htdocs
+
+ipp004 / ipp004 example:
+ psconfigure --dbhost ipp004 --dbuser ipp --dbpass ipp --htdocs ~ipp/htdocs
+
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/Makefile.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/Makefile.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/Makefile.in	(revision 22322)
@@ -0,0 +1,214 @@
+default: php
+help:
+	@echo "USAGE: make php"
+
+SRC     =       src
+DEF	=	def
+RAW	=	raw
+SCRIPTS =       scripts
+DESTBIN	=       @BINDIR@
+DESTWWW =       @HTDOCS@/ippMonitor
+
+GENERATE = $(SCRIPTS)/generate
+
+PROGRAMS = \
+$(DESTBIN)/dbadmin
+
+RAWSRC = \
+$(DESTWWW)/Login.php \
+$(DESTWWW)/SelectProject.php \
+$(DESTWWW)/ipp.css \
+$(DESTWWW)/ipp.cal.dat \
+$(DESTWWW)/ipp.cal.php \
+$(DESTWWW)/ipp.detrend.dat \
+$(DESTWWW)/ipp.detrend.php \
+$(DESTWWW)/ipp.diff.dat \
+$(DESTWWW)/ipp.diff.php \
+$(DESTWWW)/ipp.imfiles.dat \
+$(DESTWWW)/ipp.imfiles.php \
+$(DESTWWW)/ipp.load.dat \
+$(DESTWWW)/ipp.load.php \
+$(DESTWWW)/ipp.menu.dat \
+$(DESTWWW)/ipp.php \
+$(DESTWWW)/ipp.science.dat \
+$(DESTWWW)/ipp.science.php \
+$(DESTWWW)/ipp.stack.dat \
+$(DESTWWW)/ipp.stack.php \
+$(DESTWWW)/ipp.warp.dat \
+$(DESTWWW)/ipp.warp.php \
+$(DESTWWW)/phptest.php \
+$(DESTWWW)/site.php \
+$(DESTWWW)/broken.png \
+$(DESTWWW)/down.png \
+$(DESTWWW)/down_sel.png \
+$(DESTWWW)/left.png \
+$(DESTWWW)/left_sel.png \
+$(DESTWWW)/missing.png \
+$(DESTWWW)/right.png \
+$(DESTWWW)/right_sel.png \
+$(DESTWWW)/up.png \
+$(DESTWWW)/up_sel.png \
+$(DESTWWW)/getimage.php
+
+DEFSRC = \
+$(DESTWWW)/keptDetrendFrames.php \
+$(DESTWWW)/masterDetrendFrames.php \
+$(DESTWWW)/masterDetrendImfiles.php \
+$(DESTWWW)/detInputExp.php \
+$(DESTWWW)/detMasterFrame.php \
+$(DESTWWW)/detMasterImfile.php \
+$(DESTWWW)/detNormalizedExp.php \
+$(DESTWWW)/detNormalizedImfile.php \
+$(DESTWWW)/detNormalizedStatImfile.php \
+$(DESTWWW)/detProcessedImfile.php \
+$(DESTWWW)/detProcessedExp.php \
+$(DESTWWW)/detProcessedExp_noimage.php \
+$(DESTWWW)/detResidExp.php \
+$(DESTWWW)/detResidExp_noimage.php \
+$(DESTWWW)/detResidImfile.php \
+$(DESTWWW)/detStackedImfile.php \
+$(DESTWWW)/detNormalizedExp_failure.php \
+$(DESTWWW)/detNormalizedImfile_failure.php \
+$(DESTWWW)/detNormalizedStatImfile_failure.php \
+$(DESTWWW)/detProcessedImfile_failure.php \
+$(DESTWWW)/detProcessedExp_failure.php \
+$(DESTWWW)/detResidExp_failure.php \
+$(DESTWWW)/detResidImfile_failure.php \
+$(DESTWWW)/detStackedImfile_failure.php \
+$(DESTWWW)/detRun.php \
+$(DESTWWW)/detRunSummary.php \
+$(DESTWWW)/newExp.php \
+$(DESTWWW)/newImfile.php \
+$(DESTWWW)/guidePendingExp.php \
+$(DESTWWW)/chipSummary.php \
+$(DESTWWW)/chipStageExp.php \
+$(DESTWWW)/chipPendingExp.php \
+$(DESTWWW)/chipPendingImfile.php \
+$(DESTWWW)/chipProcessedExp.php \
+$(DESTWWW)/chipProcessedImfile.php \
+$(DESTWWW)/chipProcessedImfile_failure.php \
+$(DESTWWW)/camSummary.php \
+$(DESTWWW)/camStageExp.php \
+$(DESTWWW)/camPendingExp.php \
+$(DESTWWW)/camProcessedExp.php \
+$(DESTWWW)/camProcessedExp_NoImages.php \
+$(DESTWWW)/camProcessedExp_failure.php \
+$(DESTWWW)/camProcessedImfile.php \
+$(DESTWWW)/fakeSummary.php \
+$(DESTWWW)/fakeStageExp.php \
+$(DESTWWW)/fakePendingExp.php \
+$(DESTWWW)/fakeProcessedExp.php \
+$(DESTWWW)/fakePendingImfile.php \
+$(DESTWWW)/fakeProcessedImfile.php \
+$(DESTWWW)/fakeProcessedImfile_failure.php \
+$(DESTWWW)/pzPendingExp.php \
+$(DESTWWW)/pzPendingImfile.php \
+$(DESTWWW)/pzDoneExp.php \
+$(DESTWWW)/pzDoneImfile.php \
+$(DESTWWW)/pzDoneExp_failed.php \
+$(DESTWWW)/pzDoneImfile_failed.php \
+$(DESTWWW)/rawUnknownExp.php \
+$(DESTWWW)/rawDetrendExp.php \
+$(DESTWWW)/rawDetrendExp_detrend.php \
+$(DESTWWW)/rawImfile.php \
+$(DESTWWW)/rawImfile_failed.php \
+$(DESTWWW)/rawExp_failed.php \
+$(DESTWWW)/rawScienceExp.php \
+$(DESTWWW)/rawExp.php \
+$(DESTWWW)/summitExp.php \
+$(DESTWWW)/summitImfile.php \
+$(DESTWWW)/warpSummary.php \
+$(DESTWWW)/warpStageExp.php \
+$(DESTWWW)/warpStageSkyfiles.php \
+$(DESTWWW)/warpStageSkyfileInputs.php \
+$(DESTWWW)/warpProcessedSkyfiles.php \
+$(DESTWWW)/warpFailedSkyfiles.php \
+$(DESTWWW)/diffRun.php \
+$(DESTWWW)/diffInputSkyfile.php \
+$(DESTWWW)/diffSkyfile.php \
+$(DESTWWW)/stackRun.php \
+$(DESTWWW)/stackInputSkyfile.php \
+$(DESTWWW)/stackProcessedSkyfile.php \
+$(DESTWWW)/stackFailedSkyfile.php \
+$(DESTWWW)/calDB.php \
+$(DESTWWW)/calRun.php \
+$(DESTWWW)/flatcorrRun.php \
+$(DESTWWW)/flatcorrExp.php
+
+
+PICTURES = \
+$(DESTWWW)/PScolorlogo2.jpg \
+$(DESTWWW)/missing.png
+
+DESTLINK = $(DESTWWW)/index.php
+
+php: $(RAWSRC) $(DEFSRC) $(PICTURES) $(DESTLINK) $(PROGRAMS)
+
+# dependancy rules for binary code #########################
+# .PRECIOUS: %.$(ARCH).o
+# .PRECIOUS: $(BIN)/%.$(ARCH)
+
+$(DESTLINK): $(DESTWWW)/Login.php
+	rm -f $@
+	ln -s $(DESTWWW)/Login.php $@
+
+$(SRC)/%.php: $(DEF)/%.d $(DEF)/autocode.php $(GENERATE)
+	@if [ ! -d $(SRC) ]; then mkdir -p $(SRC); fi
+	$(GENERATE) $< $(DEF)/autocode.php $@
+
+# generated php code is built into SRC first
+$(DESTWWW)/%.php: $(SRC)/%.php
+	@if [ ! -d $(DESTWWW) ]; then mkdir -p $(DESTWWW) || exit; fi
+	rm -f $(DESTWWW)/$*.php || exit
+	cp $(SRC)/$*.php $(DESTWWW)/$*.php || exit
+
+# non-generated php code is copied directly
+$(DESTWWW)/%.php: $(RAW)/%.php
+	@if [ ! -d $(DESTWWW) ]; then mkdir -p $(DESTWWW) || exit; fi
+	rm -f $(DESTWWW)/$*.php || exit
+	cp $(RAW)/$*.php $(DESTWWW)/$*.php || exit
+
+# all other files are copied directly
+$(DESTWWW)/%.css: $(RAW)/%.css
+	@if [ ! -d $(DESTWWW) ]; then mkdir -p $(DESTWWW) || exit; fi
+	rm -f $(DESTWWW)/$*.css || exit
+	cp $(RAW)/$*.css $(DESTWWW)/$*.css || exit
+
+$(DESTWWW)/%.dat: $(RAW)/%.dat
+	@if [ ! -d $(DESTWWW) ]; then mkdir -p $(DESTWWW) || exit; fi
+	rm -f $(DESTWWW)/$*.dat || exit
+	cp $(RAW)/$*.dat $(DESTWWW)/$*.dat || exit
+
+$(DESTWWW)/%.jpg: $(RAW)/%.jpg
+	@if [ ! -d $(DESTWWW) ]; then mkdir -p $(DESTWWW) || exit; fi
+	rm -f $(DESTWWW)/$*.jpg || exit
+	cp $(RAW)/$*.jpg $(DESTWWW)/$*.jpg || exit
+
+$(DESTWWW)/%.jpeg: $(RAW)/%.jpeg
+	@if [ ! -d $(DESTWWW) ]; then mkdir -p $(DESTWWW) || exit; fi
+	rm -f $(DESTWWW)/$*.jpeg || exit
+	cp $(RAW)/$*.jpeg $(DESTWWW)/$*.jpeg || exit
+
+$(DESTWWW)/%.png: $(RAW)/%.png
+	@if [ ! -d $(DESTWWW) ]; then mkdir -p $(DESTWWW) || exit; fi
+	rm -f $(DESTWWW)/$*.png || exit
+	cp $(RAW)/$*.png $(DESTWWW)/$*.png || exit
+
+$(DESTBIN)/%: $(SCRIPTS)/%
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN) || exit; fi
+	cp $(SCRIPTS)/$* $(DESTBIN)/$* || exit
+	chmod +x $(DESTBIN)/$* || exit
+
+# utilities #################################################
+
+clean:	
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
+
+.PRECIOUS: $(SRC)/%.php
+.PRECIOUS: $(SRC)/%.css
+.PRECIOUS: $(SRC)/%.dat
+.PRECIOUS: $(SRC)/%.png
+.PRECIOUS: $(SRC)/%.jpg
+.PRECIOUS: $(SRC)/%.jpeg
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/configure
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/configure	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/configure	(revision 22322)
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+./configure.tcsh $*
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/configure.tcsh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/configure.tcsh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/configure.tcsh	(revision 22322)
@@ -0,0 +1,203 @@
+#!/bin/csh -f
+
+# this is a very low-tech version of configure, not built by autoconf.
+
+# user needs to supply:
+# --htdocs : top-level of www files
+# --dbhost : machine running database
+# --dbuser : username for database access
+# --dbpass : password for database access
+
+set htdocs  = ""
+set dbhost  = ""
+set dbuser  = ""
+set dbpass  = ""
+
+set prefix  = ""
+set bindir  = ""
+set libdir  = ""
+set datadir = ""
+
+set root    = ""
+set args    = ""
+
+while ("$1" != "") 
+ switch ("$1")
+  # options passed by jhbuild or others which we ignore
+  case --enable-maintainer-mode
+  case --no-create
+  case --no-recursion
+  case --sbindir*
+  case --libexecdir*
+  case --sharedstatedir*
+  case --localstatedir*
+  case --oldincludedir*
+  case --infodir*
+  case --mandir*
+  case --includedir*
+  case --sysconfdir*
+   # we need to strip the --opt word and --opt=word versions
+   set word = `echo $1 | tr = ' '`
+   if ($#word == 1) shift
+   breaksw;
+  case --htdocs*
+   if ("$1" == "--htdocs") then
+     shift
+     set htdocs = $1
+   else
+     set htdocs = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --dbhost*
+   if ("$1" == "--dbhost") then
+     shift
+     set dbhost = $1
+   else
+     set dbhost = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --dbuser*
+   if ("$1" == "--dbuser") then
+     shift
+     set dbuser = $1
+   else
+     set dbuser = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --dbpass*
+   if ("$1" == "--dbpass") then
+     shift
+     set dbpass = $1
+   else
+     set dbpass = `echo $1 | tr = ' ' | awk '{print $2}'`
+   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 --libdir*
+   if ("$1" == "--libdir") then
+     shift
+     set libdir = $1
+   else
+     set libdir = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --datadir*
+   if ("$1" == "--datadir") then
+     shift
+     set datadir = $1
+   else
+     set datadir = `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
+
+echo
+echo "prefix: $prefix"
+echo "bindir: $bindir"
+echo "libdir: $libdir"
+echo "datadir:$datadir"
+echo "htdocs: $htdocs"
+echo "dbhost: $dbhost"
+echo "dbuser: $dbuser"
+echo "dbpass: $dbpass"
+echo
+
+if ("$htdocs" == "") goto usage
+if ("$dbhost" == "") goto usage
+if ("$dbuser" == "") goto usage
+if ("$dbpass" == "") goto usage
+
+# BINDIR holds the output binary files
+if ("$bindir" == "") then
+  if ("$prefix" == "") goto usage
+  set bindir = $prefix/bin
+endif
+
+# LIBDIR set the install lib directory
+if ("$libdir" == "") then
+  if ("$prefix" == "") goto usage
+  set libdir = $prefix/lib
+endif
+
+# DATADIR holds the general non-binary files
+if ("$datadir" == "") then
+  if ("$prefix" == "") goto usage
+  set datadir = $prefix/share
+endif
+
+rm -f tmp.1 tmp.2
+
+# convert Makefile.in to Makefile:
+if (-e Makefile) mv -f Makefile Makefile~
+cat Makefile.in | sed "s|@HTDOCS@|$htdocs|" > tmp.1
+cat tmp.1       | sed "s|@BINDIR@|$bindir|" > Makefile
+rm -f tmp.1
+
+# convert raw/site.php.in to raw/site.php
+if (-e raw/site.php) mv -f raw/site.php raw/site.php~
+cat raw/site.php.in | sed "s|@DBHOST@|$dbhost|"  > tmp.1
+cat tmp.1           | sed "s|@DBUSER@|$dbuser|"  > tmp.2
+cat tmp.2           | sed "s|@DBPASS@|$dbpass|"  > tmp.1
+cat tmp.1           | sed "s|@BINDIR@|$bindir|"  > tmp.2
+cat tmp.2           | sed "s|@PERLLIB@|$libdir|" > tmp.1
+cat tmp.1           | sed "s|@SITE@|$datadir|"   > raw/site.php
+rm -f tmp.1 tmp.2
+
+# need to set the PERLLIB and BINDIR variables (use prefix, bindir, libdir)
+
+exit 0
+
+usage:
+cat <<EOF
+USAGE: configure [OPTION]
+
+echo $#args
+echo remaining args: $args
+
+set the WWW installation directory root with --htdocs
+set the IPP installation directory root with --prefix
+
+set the database host, user, password with: --dbhost, --dbuser, -dbpass
+ 
+either --prefix or all of --bindir, --libdir, --datadir are required
+
+Configuration:
+  -h, --help              display this help and exit
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [PREFIX/bin/$ARCH] 
+  --libdir=DIR           object code libraries [PREFIX/lib/$ARCH]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+
+EOF
+ exit 2;
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/README	(revision 22322)
@@ -0,0 +1,99 @@
+
+** the current definition files correspond to tasks.md 1.86
+** update this file if you need to update the tables
+
+Each of the *.d files in this directory (except example.d) are used to
+define a page in the ipp web tools which show the contents of a
+database table (or tables).  There are also pages which do not refer
+to a specific database table: these are defined in ../raw and are not
+pre-processed with the 'generate' function.  The contents of the
+description file are confronted with a template file and used to
+generate PHP code.  
+
+The layout of each page is essentially the same: the page is divided
+into a main data area and a left-hand side navigation table.  The
+contents of the navigation table are defined by the menu definition
+file specified for the page.  The data area includes the following
+elements:
+
+- TITLE
+- HEAD (html or php code)
+- TABLE
+- TAIL (html or php code)
+
+The *.d file defines each of these regions with a set of rules.  Each
+rule is specified on a single line, and begins with one of the defined
+keywords.  Several keywords are required to be unique (eg, TABLE and
+TITLE) while others may be repeated (eg, WHERE, FIELD).  For most of
+the keywords, there is no fixed format for the value, but in the case
+of the FIELD keyword, care must be taken to follow the mandatory
+syntax. 
+
+The TITLE is define by a single, fixed keyword (TITLE).
+
+The HEAD and TAIL regions are defined by the HEAD and TAIL lines, and
+are optional.  Each HEAD or TAIL line is followed by a word defining
+the code type (may be HTML or PHP).  The rest of the line defines code
+which is interpreted as either HTML or PHP.  These replace the lines
+"// ** HEAD CODE **" and "// ** TAIL CODE **" in the template file.
+
+By default, the menu definition file for the page is defined by the
+specified MENU key.  If, however, the page is passed a "menu=XXXX"
+directive (either via GET or POST), then the specified file is used
+instead as a menu definition file.  This mechanism is used to allow
+the same table definition page to be linked from different locations
+in the pages and have the same appearence as the referencing page.
+
+The rest of the file defines the displayed contents of the table.  The
+page generates an HTML table based on a query of one (or possibly more
+than one, see below) database tables.  The table used for the query is
+specified by the TABLE key.  An SQL command is generated based on the
+requested fields, defined by the FIELD keys.  If desired, WHERE
+statements may be added to the SQL command with the WHERE key.  Each
+line marked by WHERE is appended (with AND) to the SQL WHERE clause.  
+
+The FIELDS lines define both the queried fields and the rules for
+presenting the fields in the HTML table.  Each FIELD row results in a
+column in the output HTML table, unless otherwise noted (see below).
+Each FIELD row also corresponds to a queried field from the database
+table, again unless otherwise noted.  Each FIELD command is followed
+by a set of comma-separated values.  The first entry defines the name
+of the database table field.  The second entry defines the minimum
+width of the column.  The third entry defines the column header.  The
+rest of the entries are optional.  The fourth entry specifies what is
+placed in the cell, and may consist of:
+
+- the string "value", in which case the corresponding data entry from
+the database for the column and row are used to fill the cell.  This
+is the default behavior.
+
+- the string "value=XXX", in which case the cell is filled with the
+value of XXX instead of the corresponding database cell value.  If XXX
+consists of one of the specified database fields, that column's data
+for this row is substituted instead.  
+
+- the string "image=XXX", in which case the cell is filled with the
+image represented by the URI XXX.  
+
+- the string "none", in which case the cell is column in the output
+HTML table.  Such elements are needed to lookup a value from the
+database (ie, to be passed to links, see below) but not to display the
+value in the table.
+
+The fifth entry defines an optional link for the cell.  If this field
+is set, a link is created for the cell using the given word as the
+target of the link.  The link will also have the mandatory GET data
+attached (including the menu and the pass data needed to validate the
+user).  
+
+The sixth entry is used to specify additional information passed to
+the linked page via GET data elements.  These are typically of the
+form "word=value", and may include multiple comma separated
+entries. If the value elements consist of $field where field is one of
+the valid database field names, this value for the current row is
+passed with the link.
+
+If the first entry of the FIELD row has the value "*", then the row
+is NOT used to define a database field; it is only used to define an
+output column in the HTML table.
+
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/autocode.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/autocode.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/autocode.php	(revision 22322)
@@ -0,0 +1,129 @@
+<?php 
+
+include 'ipp.php';
+
+$ID = checkID ();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+$db = dbconnect($ID['proj']);
+
+if ($ID['menu']) {
+  $myMenu = $ID['menu'];
+} else {
+  $myMenu = "$MENU";
+}
+
+menu($myMenu, '$TITLE', '$STYLE', $ID['link'], $ID['proj']);
+
+echo "<p> $TITLE </p>";
+
+// set up the form
+echo "<form action=\"$FILE\" method=\"POST\">\n";
+
+// define restrictiosn to the queries
+// ** TABLE RESTRICTIONS **
+
+// get the result table count
+if ($MODE == "basic") {
+  $sql = "SELECT count(*) FROM $TABLE $WHERE";
+}
+if ($MODE == "summary") {
+  $sql = "SELECT count(*) FROM (SELECT $FIELDS FROM $TABLE $WHERE) as TEMP";
+}
+
+$qry = $db->query($sql);
+if (DB::isError($qry)) {
+  echo "<b>error reading $TABLE table count</b><br>\n";
+  echo "<br><small><b> count query : $sql </b></small><br>\n";
+  menu_end();
+}
+if (!$qry->fetchInto($row)) {
+  echo "<b>error reading $TABLE table count</b><br>\n";
+  echo "<br><small><b> count query : $sql </b></small><br>\n";
+  menu_end();
+}
+// set up the row counter variables
+if ($ID['from']) {
+  $rowStart = $ID['from'];
+} else {
+  $rowStart = 0;
+}
+$rowLast = $rowStart + $dTABLE;
+$rowTotal = $row[0];
+if ($rowLast > $rowTotal) { $rowLast = $rowTotal; }
+echo "<b> $rowStart to $rowLast of $rowTotal items</b><br>\n";
+
+// query the database
+if ($MODE == "basic") {
+  $sql = "SELECT $FIELDS FROM $TABLE $WHERE LIMIT $dTABLE OFFSET $rowStart";
+}
+if ($MODE == "summary") {
+  $sql = "SELECT $FIELDS FROM $TABLE $WHERE LIMIT $dTABLE OFFSET $rowStart";
+}
+
+$qry = $db->query($sql);
+if (DB::isError($qry)) {
+  echo "<b>error reading $TABLE table</b><br>\n";
+  echo "<br><small><b> table query : $sql </b></small><br>\n";
+  menu_end();
+}
+
+// ** HEAD CODE **
+
+// ** BUTTON RESTRICTIONS **
+navigate_buttons ($rowStart, $rowLast, $dTABLE, $rowTotal, $buttonLink, $ID, '$FILE');
+
+echo "&nbsp; : &nbsp; or enter start row: <input type=\"text\" name=\"from\" size=\"5\" value=\"$rowStart\">\n";
+
+// set up the table
+echo "<table class=list>\n";
+
+// echo "<tr><td></td>\n"; // first field is a label set below for the query rows only
+// ** TABLE HEADER **
+// echo "</tr>\n";
+
+// query restriction form
+// echo "<tr>\n";
+// echo "<td class=list> <input type=\"text\" name=\"expID\"></td>\n";
+// ** TABLE QUERY **
+// echo "</tr>\n";
+
+// list the results
+while ($qry->fetchInto($row)) {
+  // $link = "$FILE" . "?expID=" . $row[0] . "&" . $ID['link'];
+
+  $class = "list";
+  // ** TD CLASS **
+
+  echo "<tr><td></td>\n";
+  // ** TABLE DATA **
+  echo "</tr>\n";
+}
+
+// query restriction form
+// echo "<tr>\n";
+// echo "<td class=list> <input type=\"text\" name=\"expID\"></td>\n";
+// TABLE QUERY 
+// echo "</tr>\n";
+
+// close the table and form
+echo "</table>\n";
+echo "<input type=\"submit\" name=\"constraints\" value=\"refine search\">\n";
+$pass = $ID['pass'];
+$proj = $ID['proj'];
+$menu = $ID['menu'];
+echo "<input type=\"hidden\" name=\"pass\" value=\"$pass\">\n";
+echo "<input type=\"hidden\" name=\"proj\" value=\"$proj\">\n";
+echo "<input type=\"hidden\" name=\"menu\" value=\"$menu\">\n";
+echo "</form>\n";
+
+// ** TAIL CODE **
+
+echo "<small> WHERE: $WHERE<br><br></small>\n";
+echo "<small> SQL: $sql<br><br></small>\n";
+
+menu_end();
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/calDB.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/calDB.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/calDB.d	(revision 22322)
@@ -0,0 +1,9 @@
+TABLE calDB
+TITLE calDB
+FILE  calDB.php
+MENU  ipp.cal.dat
+
+#        field        size  format  name           show     link to         extras
+FIELD    cal_id,	20, %s,     calibration ID
+FIELD    dvodb,		20, %s,     DVO database
+FIELD    state,	        20, %s,     state
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/calRun.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/calRun.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/calRun.d	(revision 22322)
@@ -0,0 +1,10 @@
+TABLE calRun
+TITLE calRun
+FILE  calRun.php
+MENU  ipp.cal.dat
+
+#        field        size  format  name           show     link to         extras
+FIELD    cal_id,	20, %s,     calibration ID
+FIELD    region,	20, %s,     region
+FIELD    last_step,	20, %s,     late calibration
+FIELD    state,	        20, %s,     state
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/camPendingExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/camPendingExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/camPendingExp.d	(revision 22322)
@@ -0,0 +1,37 @@
+TABLE camRun, chipRun, rawExp
+TITLE Camera Pending
+FILE  camPendingExp.php
+MENU  ipp.science.dat
+
+WHERE camRun.state = 'new'
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                     size  format  name          show   link to                  extras
+FIELD rawExp.exp_name,     	10,   %s,     Exp Name
+FIELD rawExp.exp_id,	         5,   %s,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,           5,   %s,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,             5,   %s,     Cam ID
+FIELD camRun.state,              5,   %s,     State
+FIELD camRun.label,              5,   %s,     Label
+FIELD rawExp.telescope,      	10,   %s,     Telescope
+FIELD rawExp.camera,         	10,   %s,     Camera
+FIELD rawExp.dateobs,        	19,   %T,     Date/Time
+FIELD rawExp.ra,                 8,   %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,               8,   %10.6f, DEC,          op=OP2
+FIELD rawExp.object,         	10,   %s,     Object
+FIELD rawExp.filter,         	10,   %s,     FILTER
+FIELD rawExp.exp_time,       	 5,   %.2f,   exp_time    
+FIELD rawExp.airmass,        	 5,   %.4f,   airmass     
+FIELD rawExp.bg,             	 5,   %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	 5,   %.2f,   stdev    
+FIELD rawExp.comment,  	        65,   %s,     Comment
+
+# FIELD rawExp.bg_mean_stdev,  	 5,   %.2f,   &lt;backgnd&gt;
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedExp.d	(revision 22322)
@@ -0,0 +1,58 @@
+TABLE camRun, camProcessedExp, chipRun, rawExp
+TITLE Camera Processed
+FILE  camProcessedExp.php
+MENU  ipp.science.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE camRun.state = 'full'
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE camProcessedExp.cam_id  = camRun.cam_id
+WHERE camProcessedExp.fault = 0
+
+# define image names to be used below
+# IMAGE VAR basename filerule camera class_id
+IMAGE JPEG2 $camProcessedExp.path_base PPIMAGE.JPEG2 $rawExp.camera NONE
+
+# define the arguments supplied to the links below (if any)
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+
+ARGS  ARG4 camRun.cam_id=$camRun.cam_id
+ARGS  ARG4 camera=$rawExp.camera
+ARGS  ARG4 basename=$camProcessedExp.path_base
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                     	size  format  name          show   	 link to                  extras
+FIELD rawExp.exp_name,     	 	10,   %s,     Exp Name
+FIELD rawExp.exp_id,	         	 5,   %s,     Exp ID,       value,  	 rawImfile.php,           ARG1
+FIELD chipRun.chip_id,           	 5,   %s,     Chip ID,      value,  	 chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,             	 5,   %s,     Cam ID,       value,  	 camProcessedExp.php,     ARG3
+FIELD camRun.label,             	 5,   %s,     Label
+FIELD *,    	                	 8,   %s,     image,        image=JPEG2, camProcessedImfile.php,  ARG4
+FIELD rawExp.telescope,      		10,   %s,     Telescope
+FIELD rawExp.camera,         		10,   %s,     Camera
+FIELD rawExp.dateobs,        		19,   %T,     Date/Time
+FIELD rawExp.ra,           		 8,   %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,         		 8,   %10.6f, DEC,          op=OP2
+FIELD rawExp.object,         		10,   %s,     Object
+FIELD rawExp.filter,         		10,   %s,     FILTER
+FIELD rawExp.exp_time,       		 5,   %.2f,     exp_time    
+FIELD rawExp.airmass,        		 5,   %.4f,     airmass     
+FIELD camProcessedExp.bg,             	 5,   %.2f,     backgnd
+FIELD camProcessedExp.bg_stdev,       	 5,   %.2f,     stdev    
+FIELD camProcessedExp.n_stars,  	 5,   %d,     Nstars
+FIELD camProcessedExp.n_astrom, 	 5,   %d,     Nastrom
+FIELD camProcessedExp.sigma_ra, 	 5,   %f,     sigma ra
+FIELD camProcessedExp.fwhm_major,     	 5,   %f,     FHWM (major)
+FIELD rawExp.comment,  	                65, %s,     Comment
+
+FIELD camProcessedExp.path_base,     	 5,   %s,     path_base,    none
+FIELD rawExp.exp_id,     		 5,   %s,     Exp ID,       none
+
+# the last two are used as arguments elsewhere, thus needed in the list, even if not displayed
+# FIELD camRun.reduction,       	         5,   %s,     reduction
+# FIELD camProcessedExp.bg_mean_stdev,  	 5,   %.2f,     &lt;backgnd&gt;
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedExp_NoImages.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedExp_NoImages.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedExp_NoImages.d	(revision 22322)
@@ -0,0 +1,52 @@
+TABLE camRun, camProcessedExp, chipRun, rawExp
+TITLE Camera Processed (No Images)
+FILE  camProcessedExp_NoImages.php
+MENU  ipp.science.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE camRun.state = 'full'
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE camProcessedExp.cam_id  = camRun.cam_id
+WHERE camProcessedExp.fault = 0
+
+# define the arguments supplied to the links below (if any)
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+
+ARGS  ARG4 camRun.cam_id=$camRun.cam_id
+ARGS  ARG4 camera=$rawExp.camera
+ARGS  ARG4 basename=$camProcessedExp.path_base
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                     	size  format  name          show   	 link to                  extras
+FIELD rawExp.exp_name,     	 	10,   %s,     Exp Name
+FIELD rawExp.exp_id,	         	 5,   %s,     Exp ID,       value,  	 rawImfile.php,           ARG1
+FIELD chipRun.chip_id,           	 5,   %s,     Chip ID,      value,  	 chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,             	 5,   %s,     Cam ID,       value,  	 camProcessedExp.php,     ARG3
+FIELD camRun.state,                      5,   %s,     state,        value,  	 camProcessedImfile.php,  ARG4
+FIELD camRun.label,                      5,   %s,     label
+FIELD rawExp.telescope,      		10,   %s,     Telescope
+FIELD rawExp.camera,         		10,   %s,     Camera
+FIELD rawExp.dateobs,        		19,   %T,     Date/Time
+FIELD rawExp.ra,           		 8,   %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,         		 8,   %10.6f, DEC,          op=OP2
+FIELD rawExp.object,         		10,   %s,     Object
+FIELD rawExp.filter,         		10,   %s,     FILTER
+FIELD rawExp.exp_time,       		 5,   %.2f,     exp_time    
+FIELD rawExp.airmass,        		 5,   %.4f,     airmass     
+FIELD camProcessedExp.bg,             	 5,   %.2f,     backgnd
+FIELD camProcessedExp.bg_stdev,       	 5,   %.2f,     stdev    
+FIELD camProcessedExp.n_stars,  	 5,   %d,     Nstars
+FIELD camProcessedExp.n_astrom, 	 5,   %d,     Nastrom
+FIELD camProcessedExp.sigma_ra, 	 5,   %f,     sigma ra
+FIELD camProcessedExp.fwhm_major,     	 5,   %f,     FHWM (major)
+FIELD rawExp.comment,  	                65, %s,     Comment
+
+FIELD camProcessedExp.path_base,     	 5,   %s,     path_base,    none
+FIELD rawExp.exp_id,     		 5,   %s,     Exp ID,       none
+
+# FIELD camProcessedExp.bg_mean_stdev,  	 5,   %.2f,     &lt;backgnd&gt;
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedExp_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedExp_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedExp_failure.d	(revision 22322)
@@ -0,0 +1,55 @@
+TABLE camRun, camProcessedExp, chipRun, rawExp
+TITLE Camera Failed Exposures
+FILE  camProcessedExp_failure.php
+MENU  ipp.science.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE camRun.state = 'full'
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE camProcessedExp.cam_id  = camRun.cam_id
+WHERE camProcessedExp.fault != 0
+
+# define the arguments supplied to the links below (if any)
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+
+ARGS  ARG4 camRun.cam_id=$camRun.cam_id
+ARGS  ARG4 camera=$rawExp.camera
+ARGS  ARG4 basename=$camProcessedExp.path_base
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                     	size  format  name          show   	 link to                      extras
+FIELD rawExp.exp_name,     	 	 5,   %s,     Exp Name
+FIELD rawExp.exp_id,	         	 5,   %s,     Exp ID,       value,  	 rawImfile.php,               ARG1
+FIELD chipRun.chip_id,           	 5,   %s,     Chip ID,      value,  	 chipProcessedImfile.php,     ARG2
+FIELD camRun.cam_id,            	 5,   %d,     Cam ID,       value,       camProcessedExp_failure.php, ARG3
+FIELD camRun.state,                      5,   %s,     state,        value,       camProcessedImfile.php,      ARG4
+FIELD camRun.label,                      5,   %s,     label
+FIELD rawExp.telescope,      		10,   %s,     Telescope
+FIELD rawExp.camera,         		10,   %s,     Camera
+FIELD rawExp.dateobs,        		19,   %T,     Date/Time
+FIELD rawExp.ra,           		 8,   %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,         		 8,   %10.6f, DEC,          op=OP2
+FIELD rawExp.object,         		10,   %s,     Object
+FIELD rawExp.filter,         		10,   %s,     FILTER
+FIELD rawExp.exp_time,       		 5,   %.2f,     exp_time    
+FIELD rawExp.airmass,        		 5,   %.4f,     airmass     
+FIELD camProcessedExp.bg,             	 5,   %.2f,     backgnd
+FIELD camProcessedExp.bg_stdev,       	 5,   %.2f,     stdev    
+FIELD camProcessedExp.n_stars,  	 5,   %d,     Nstars
+FIELD camProcessedExp.n_astrom, 	 5,   %d,     Nastrom
+FIELD camProcessedExp.sigma_ra, 	 5,   %f,     sigma ra
+FIELD camProcessedExp.fwhm_major,     	 5,   %f,     FHWM (major)
+FIELD rawExp.comment,  	                65, %s,     Comment
+
+FIELD camProcessedExp.path_base,     	 5,   %s,     path_base,    none
+FIELD rawExp.exp_id,     		 5,   %s,     Exp ID,       none
+
+# FIELD camRun.reduction,       	         5,   %s,     reduction
+# FIELD camProcessedExp.bg_mean_stdev,  	 5,   %.2f,     &lt;backgnd&gt;
+
+TAIL PHP insert_log ('LOG.IMFILE');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/camProcessedImfile.d	(revision 22322)
@@ -0,0 +1,46 @@
+TABLE camRun, camProcessedExp, chipRun, rawExp, chipProcessedImfile
+TITLE Camera Processed Imfiles
+FILE  camProcessedImfile.php
+MENU  ipp.science.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE camRun.state = 'full'
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE camProcessedExp.cam_id = camRun.cam_id
+WHERE chipProcessedImfile.chip_id = camRun.chip_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                     	size  format  name          show   	 link to                  extras
+FIELD rawExp.exp_name,     		10,   %s,     Exp Name,     value
+FIELD chipProcessedImfile.class_id,  	 8,   %s,     Class ID
+FIELD rawExp.exp_id,	        	 5,   %s,     Exp ID,       value,       rawImfile.php,           ARG1
+FIELD chipRun.chip_id,          	 5,   %s,     Chip ID,      value,       chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,            	 5,   %s,     Cam ID,       value,       camProcessedExp.php,     ARG3
+FIELD camRun.label,             	 5,   %s,     Label
+FIELD rawExp.telescope,      		10,   %s,     Telescope
+FIELD rawExp.camera,         		10,   %s,     Camera
+FIELD rawExp.dateobs,        		19,   %T,     Date/Time
+FIELD rawExp.ra,           		 8,   %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,         		 8,   %10.6f, DEC,          op=OP2
+FIELD rawExp.object,         		10,   %s,     Object
+FIELD rawExp.filter,         		10,   %s,     FILTER
+FIELD rawExp.exp_time,       		 5,   %.2f,     exp_time    
+FIELD rawExp.airmass,        		 5,   %.4f,     airmass     
+FIELD chipProcessedImfile.bg,            5,   %.2f,   backgnd
+FIELD chipProcessedImfile.bg_stdev,      5,   %.2f,   stdev    
+FIELD chipProcessedImfile.bg_mean_stdev, 5,   %.2f,   &lt;backgnd&gt;
+FIELD chipProcessedImfile.n_stars,  	 5,   %d,     Nstars
+FIELD camProcessedExp.n_astrom, 	 5,   %d,     Nastrom
+FIELD camProcessedExp.sigma_ra, 	 5,   %.2f,   sigma ra
+FIELD chipProcessedImfile.fwhm_major,  	 5,   %.2f,   FHWM (major)
+FIELD rawExp.comment,  	                65,   %s,     Comment
+
+TAIL PHP insert_image ('PPIMAGE.JPEG1');
+TAIL PHP insert_log ('LOG.EXP');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/camStageExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/camStageExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/camStageExp.d	(revision 22322)
@@ -0,0 +1,36 @@
+TABLE camRun, chipRun, rawExp
+TITLE Camera Stage Exposures
+FILE  camStageExp.php
+MENU  ipp.science.dat
+
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                     size  format  name          show   link to                  extras
+FIELD rawExp.exp_name,     	10,   %s,     Exp Name
+FIELD rawExp.exp_id,	         5,   %s,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,           5,   %s,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,             5,   %s,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD camRun.state,              5,   %s,     state
+FIELD camRun.label,              5,   %s,     label
+FIELD rawExp.telescope,      	10,   %s,     Telescope
+FIELD rawExp.camera,         	10,   %s,     Camera
+FIELD rawExp.dateobs,        	19,   %T,     Date/Time
+FIELD rawExp.ra,                 8,   %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,               8,   %10.6f, DEC,          op=OP2
+FIELD rawExp.object,         	10,   %s,     Object
+FIELD rawExp.filter,         	10,   %s,     FILTER
+FIELD rawExp.exp_time,       	 5,   %.2f,   exp_time    
+FIELD rawExp.airmass,        	 5,   %.4f,   airmass     
+FIELD rawExp.bg,             	 5,   %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	 5,   %.2f,   stdev    
+FIELD rawExp.comment,  	        65,   %s,     Comment
+
+# FIELD rawExp.bg_mean_stdev,  	 5,   %.2f,   &lt;backgnd&gt;
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/camSummary.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/camSummary.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/camSummary.d	(revision 22322)
@@ -0,0 +1,16 @@
+TABLE camRun
+TITLE Cam Summary
+FILE  camSummary.php
+MENU  ipp.science.dat
+
+#     field           size  format  name         show    link to         extras
+FIELD label,    	 7, %s,     label
+FIELD workdir,    	 7, %s,     workdir
+FIELD tess_id,    	 7, %s,     tess_id
+FIELD state,    	 7, %s,     state
+FIELD count(state) as n, 7, %d,     count
+
+MODE  summary
+GROUP state
+GROUP label
+GROUP workdir
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipPendingExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipPendingExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipPendingExp.d	(revision 22322)
@@ -0,0 +1,37 @@
+TABLE chipRun, rawExp
+TITLE chip Pending Exposures
+FILE  chipPendingExp.php
+MENU  ipp.science.dat
+
+# limit this table to non-OBJECT types of images
+WHERE chipRun.state = 'new'
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                   size  format  name         show    link to         extras
+FIELD rawExp.exp_name,     	10, %s,     Exp Name
+FIELD rawExp.exp_id,     	 5, %d,     Exp ID,      value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	 7, %d,     Chip ID,     value,  chipProcessedImfile.php, ARG2
+FIELD chipRun.state,    	 7, %s,     State
+FIELD chipRun.label,    	 7, %s,     Label
+FIELD rawExp.telescope,      	10, %s,     Telescope
+FIELD rawExp.camera,         	10, %s,     Camera
+FIELD rawExp.dateobs,        	19, %T,     Date/Time
+FIELD rawExp.ra,       	         8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	 8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	 8, %s,     Object
+FIELD rawExp.filter,         	10, %s,     FILTER
+FIELD rawExp.exp_time,       	 5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	 5, %.4f,   airmass     
+FIELD rawExp.bg,             	 5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	 5, %.2f,   stdev    
+FIELD rawExp.comment,  	        65, %s,     Comment
+
+# FIELD rawExp.bg_mean_stdev,  	 5, %7.2f,  &lt;backgnd&gt;
+# FIELD chipPendingExp.reduction,    20,   reduction
+# FIELD chipPendingExp.guide_id,     20, guide ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipPendingImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipPendingImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipPendingImfile.d	(revision 22322)
@@ -0,0 +1,40 @@
+TABLE chipRun, rawExp, rawImfile
+TITLE Chip Pending Imfiles
+FILE  chipPendingImfile.php
+MENU  ipp.science.dat
+
+# XXX this query does not exclude imfiles already processed (unless the exp is done)
+# I need to join against chipProcessedImfile and exclude ones where chipProcessedImfile.class_id is not NULL
+
+# limit this table to OBJECT types of images
+WHERE chipRun.state = 'new'
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE rawImfile.exp_id = rawExp.exp_id
+
+ARGS  ARG1 rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2 chipRun.chip_id=$chipRun.chip_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                   size  format  name         show    link to                  extras
+FIELD rawExp.exp_name,     	10, %s,     Exp Name
+FIELD rawImfile.class_id,     	 8, %s,     Class ID
+FIELD rawExp.exp_id,     	 5, %d,     Exp ID,      value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	 7, %d,     Chip ID,     value,  chipProcessedImfile.php, ARG2
+FIELD chipRun.state,    	 7, %s,     State
+FIELD chipRun.label,    	 7, %s,     Label
+FIELD rawExp.telescope,      	10, %s,     Telescope
+FIELD rawExp.camera,         	10, %s,     Camera
+FIELD rawExp.dateobs,        	19, %T,     Date/Time
+FIELD rawExp.ra,       	         8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	 8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	 8, %s,     Object
+FIELD rawExp.filter,         	10, %s,     FILTER
+FIELD rawExp.exp_time,       	 5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	 5, %.4f,   airmass     
+FIELD rawExp.bg,             	 5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	 5, %.2f,   stdev    
+FIELD *,                         5, %d,     Nstars,      value=0
+FIELD *, 	                 5, %.2f,   FHWM,        value=0.0
+FIELD rawExp.comment,  	        65, %s,     Comment
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipProcessedExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipProcessedExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipProcessedExp.d	(revision 22322)
@@ -0,0 +1,38 @@
+TABLE chipRun, rawExp
+TITLE chip Processed Exposures
+FILE  chipProcessedExp.php
+MENU  ipp.science.dat
+
+# limit this table to non-OBJECT types of images
+WHERE chipRun.state != 'new'
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                   size  format  name         show    link to                  extras
+FIELD rawExp.exp_name,     	 5, %s,     Exp Name
+FIELD rawExp.exp_id,     	 5, %d,     Exp ID,      value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	 7, %d,     Chip ID,     value,  chipProcessedImfile.php, ARG2
+FIELD chipRun.state,    	 7, %s,     State
+FIELD chipRun.label,    	 7, %s,     Label
+FIELD rawExp.telescope,      	10, %s,     Telescope
+FIELD rawExp.camera,         	10, %s,     Camera
+FIELD rawExp.dateobs,        	19, %T,     Date/Time
+FIELD rawExp.ra,           	 8, %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,         	 8, %10.6f, DEC,          op=OP2
+FIELD rawExp.object,         	 8, %s,     Object
+FIELD rawExp.filter,         	10, %s,     FILTER
+FIELD rawExp.exp_time,       	 5, %.2f,     exp_time    
+FIELD rawExp.airmass,        	 5, %.4f,     airmass     
+FIELD rawExp.bg,             	 5, %.2f,     backgnd
+FIELD rawExp.bg_stdev,       	 5, %.2f,     stdev    
+FIELD rawExp.comment,  	        65, %s,     Comment
+
+# FIELD rawExp.bg_mean_stdev,  	 5, %s,     &lt;backgnd&gt;
+# FIELD rawExp.sat_pixel_frac, 	 5, %s,     f(sat pixels)
+# FIELD chipPendingExp.reduction,    20,   reduction
+# FIELD chipPendingExp.guide_id,     20, guide ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipProcessedImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipProcessedImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipProcessedImfile.d	(revision 22322)
@@ -0,0 +1,48 @@
+TABLE chipRun, rawExp, chipProcessedImfile
+TITLE chip Processed Imfile
+FILE  chipProcessedImfile.php
+MENU  ipp.science.dat
+
+# limit this table to non-OBJECT types of images
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE chipProcessedImfile.exp_id = chipRun.exp_id
+WHERE chipProcessedImfile.chip_id = chipRun.chip_id
+WHERE chipProcessedImfile.fault = 0
+
+ARGS  ARG1 rawImfile.exp_id=$rawExp.exp_id
+
+ARGS  ARG2 chipRun.chip_id=$chipRun.chip_id
+
+ARGS  ARG3 chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3 chipProcessedImfile.class_id=$chipProcessedImfile.class_id
+ARGS  ARG3 camera=$rawExp.camera
+ARGS  ARG3 basename=$chipProcessedImfile.path_base
+ARGS  ARG3 class=$chipProcessedImfile.class_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                            size  format  name         show    link to                  extras
+FIELD rawExp.exp_name,     	   	 10, %s,     Exp Name
+FIELD chipProcessedImfile.class_id,  	  8, %s,     Class ID
+FIELD rawExp.exp_id,         	   	  5, %d,     Exp ID,      value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,        	   	  7, %d,     Chip ID,     value,  chipProcessedImfile.php, ARG2
+FIELD chipRun.state,        	   	  7, %s,     State,       value,  chipProcessedImfile.php, ARG3
+FIELD chipRun.label,        	   	  7, %s,     Label
+FIELD rawExp.telescope,      	   	 10, %s,     Telescope
+FIELD rawExp.camera,         	   	 10, %s,     Camera
+FIELD rawExp.dateobs,        	   	 19, %T,     Date/Time
+FIELD rawExp.ra,           	 	  8, %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,         	 	  8, %10.6f, DEC,          op=OP2
+FIELD rawExp.object,       	   	  8, %s,     Object
+FIELD rawExp.filter,         	   	 10, %s,     FILTER
+FIELD rawExp.exp_time,       	   	  5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	   	  5, %.4f,   airmass     
+FIELD chipProcessedImfile.bg,             5, %.2f,   backgnd
+FIELD chipProcessedImfile.bg_stdev,       5, %.2f,   stdev    
+FIELD chipProcessedImfile.n_stars,  	  5, %d,     Nstars
+FIELD chipProcessedImfile.fwhm_major, 	  5, %.2f,   FHWM
+FIELD chipProcessedImfile.path_base,      5, %s,     path_base, none
+FIELD rawExp.comment,  	                 65, %s,     Comment
+
+TAIL PHP insert_log ('LOG.IMFILE');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipProcessedImfile_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipProcessedImfile_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipProcessedImfile_failure.d	(revision 22322)
@@ -0,0 +1,48 @@
+TABLE chipRun, rawExp, chipProcessedImfile
+TITLE Chip Failed Imfiles
+FILE  chipProcessedImfile_failure.php
+MENU  ipp.science.dat
+
+# limit this table to non-OBJECT types of images
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE chipProcessedImfile.exp_id = chipRun.exp_id
+WHERE chipProcessedImfile.chip_id = chipRun.chip_id
+WHERE chipProcessedImfile.fault != 0
+
+ARGS  ARG1 rawImfile.exp_id=$chipRun.exp_id
+
+ARGS  ARG2 chipRun.chip_id=$chipRun.chip_id
+
+ARGS  ARG3 chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3 chipProcessedImfile.class_id=$chipProcessedImfile.class_id
+ARGS  ARG3 camera=$rawExp.camera
+ARGS  ARG3 basename=$chipProcessedImfile.path_base
+ARGS  ARG3 class=$chipProcessedImfile.class_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                            size  format  name         show    link to                          extras
+FIELD rawExp.exp_name,     	   	  5, %s,     Exp Name
+FIELD chipProcessedImfile.class_id,	  8, %s,     Class ID
+FIELD rawExp.exp_id,         	   	  5, %d,     Exp ID,      value,  rawImfile.php,                   ARG1
+FIELD chipRun.chip_id,        	   	  7, %d,     Chip ID,     value,  chipProcessedImfile_failure.php, ARG2
+FIELD chipRun.state,        	   	  7, %s,     State,       value,  chipProcessedImfile_failure.php, ARG3
+FIELD chipRun.label,        	   	  7, %s,     Label
+FIELD rawExp.telescope,      	   	 10, %s,     Telescope
+FIELD rawExp.camera,         	   	 10, %s,     Camera
+FIELD rawExp.dateobs,        	   	 19, %T,     Date/Time
+FIELD rawExp.ra,           	 	  8, %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,         	 	  8, %10.6f, DEC,          op=OP2
+FIELD rawExp.object,       	   	  8, %s,     Object
+FIELD rawExp.filter,         	   	 10, %s,     FILTER
+FIELD rawExp.exp_time,       	   	  5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	   	  5, %.4f,   airmass     
+FIELD chipProcessedImfile.bg,             5, %.2f,   backgnd
+FIELD chipProcessedImfile.bg_stdev,       5, %.2f,   stdev    
+FIELD chipProcessedImfile.n_stars,  	  5, %d,     Nstars
+FIELD chipProcessedImfile.fwhm_major, 	  5, %.2f,   FHWM
+FIELD chipProcessedImfile.path_base,      5, %s,     path_base, none
+FIELD rawExp.comment,  	                 65, %s,     Comment
+
+TAIL PHP insert_log ('LOG.IMFILE');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipStageExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipStageExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipStageExp.d	(revision 22322)
@@ -0,0 +1,35 @@
+TABLE chipRun, rawExp
+TITLE Chip Stage Exposures
+FILE  chipStageExp.php
+MENU  ipp.science.dat
+
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                   size  format  name         show    link to                  extras
+FIELD rawExp.exp_name,     	 5, %s,     Exp Name
+FIELD rawExp.exp_id,     	 5, %d,     Exp ID,      value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	 7, %d,     Chip ID,     value,  chipProcessedImfile.php, ARG2
+FIELD chipRun.state,    	 7, %s,     State
+FIELD chipRun.label,    	 7, %s,     Label
+FIELD rawExp.telescope,      	10, %s,     Telescope
+FIELD rawExp.camera,         	10, %s,     Camera
+FIELD rawExp.dateobs,        	19, %T,     Date/Time
+FIELD rawExp.ra,       	         8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	 8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	 8, %s,     Object
+FIELD rawExp.filter,         	10, %s,     FILTER
+FIELD rawExp.exp_time,       	 5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	 5, %.4f,   airmass     
+FIELD rawExp.bg,             	 5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	 5, %.2f,   stdev    
+FIELD rawExp.comment,  	        65, %s,     Comment
+
+# FIELD rawExp.bg_mean_stdev,  	 5, %7.2f,  &lt;backgnd&gt;
+# FIELD chipPendingExp.reduction,    20,   reduction
+# FIELD chipPendingExp.guide_id,     20, guide ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipSummary.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipSummary.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/chipSummary.d	(revision 22322)
@@ -0,0 +1,16 @@
+TABLE chipRun
+TITLE Chip Summary
+FILE  chipSummary.php
+MENU  ipp.science.dat
+
+#     field           size  format  name         show    link to         extras
+FIELD label,    	 7, %s,     label
+FIELD workdir,    	 7, %s,     workdir
+FIELD tess_id,    	 7, %s,     tess_id
+FIELD state,    	 7, %s,     state
+FIELD count(state) as n, 7, %d,     count
+
+MODE  summary
+GROUP state
+GROUP label
+GROUP workdir
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detInputExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detInputExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detInputExp.d	(revision 22322)
@@ -0,0 +1,43 @@
+TABLE detInputExp, rawExp
+TITLE detInputExp
+FILE  detInputExp.php
+MENU  ipp.detrend.dat
+
+# limit this table to non-OBJECT types of images
+WHERE detInputExp.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$detInputExp.exp_id
+
+ARGS  ARG2  detResidImfile.exp_id=$detInputExp.exp_id
+ARGS  ARG2  detResidImfile.det_id=$detInputExp.det_id
+ARGS  ARG2  detResidImfile.iteration=$detInputExp.iteration
+
+ARGS  ARG2  detProcessedImfile.exp_id=$detInputExp.exp_id
+ARGS  ARG2  detProcessedImfile.det_id=$detInputExp.det_id
+ARGS  ARG2  detProcessedImfile.iteration=$detInputExp.iteration
+
+HEAD PHP if ($_GET['detInputExp_det_id']) { 
+HEAD PHP   echo "<a href=detResidExp.php?" . $ID['link'] . "> resid </a>";
+HEAD PHP   echo "<a href=detProcessedExp.php?" . $ID['link'] . "> proc </a>"; }
+
+#     field                size  format  name        show         link to                 extras
+FIELD detInputExp.det_id,     7, %s,     Det ID
+FIELD detInputExp.iteration,  5, %s,     iteration
+FIELD detInputExp.exp_id,     5, %s,     Exp ID,     value,       rawImfile.php,          ARG1
+FIELD rawExp.exp_name,        5, %s,     Exp Name,   value,       rawImfile.php,          ARG1
+FIELD detInputExp.include,    3, %t,     include
+FIELD rawExp.telescope,      10, %s,     Telescope
+FIELD rawExp.camera,         10, %s,     Camera
+FIELD rawExp.exp_type,       10, %s,     Type    
+FIELD rawExp.filter,         10, %s,     FILTER
+FIELD rawExp.dateobs,        19, %T,     Date/Time
+FIELD rawExp.exp_time,        5, %.2f,   exp_time    
+FIELD rawExp.sat_pixel_frac,  5, %.2f,   f(sat pixels)
+FIELD rawExp.airmass,         5, %.4f,   airmass     
+FIELD rawExp.bg,              5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,        5, %.2f,   stdev    
+FIELD rawExp.bg_mean_stdev,   5, %.2f,   &lt;backgnd&gt;
+FIELD *,                      5, %s,     choose,     value=resid, detResidImfile.php,     ARG2
+FIELD *,                      5, %s,     choose,     value=proc,  detProcessedImfile.php, ARG3
+
+TD_CLASS det_off  $detInputExp.include == 0
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detMasterFrame.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detMasterFrame.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detMasterFrame.d	(revision 22322)
@@ -0,0 +1,9 @@
+TABLE detMasterFrame
+TITLE detMasterFrame
+FILE  detMasterFrame.php
+MENU  ipp.detrend.dat
+
+#     field    size  format  name     show  link to     extras
+FIELD det_id,    20, %s,     det_id
+FIELD iteration, 20, %s,     iteration
+FIELD comment,   20, %s,     comment
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detMasterImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detMasterImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detMasterImfile.d	(revision 22322)
@@ -0,0 +1,10 @@
+TABLE detMasterImfile
+TITLE detMasterImfile
+FILE  detMasterImfile.php
+MENU  ipp.imfiles.dat
+
+#     field    size  format  name     show  link to     extras
+FIELD det_id,    20, %s,     det_id
+FIELD class_id,  20, %s,     class_id
+FIELD uri,       20, %s,     uri
+FIELD recipe,    20, %s,     recipe
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedExp.d	(revision 22322)
@@ -0,0 +1,27 @@
+TABLE detNormalizedExp, detRun
+TITLE detNormalizedExp
+FILE  detNormalizedExp.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detNormalizedExp.det_id    = detRun.det_id
+# WHERE detNormalizedExp.iteration = detRun.iteration
+
+# define image names to be used below
+# IMAGE VAR basename filerule camera class_id
+IMAGE JPEG2 $detNormalizedExp.path_base PPIMAGE.JPEG2 $detRun.camera NONE
+
+ARGS  ARG2 det_id=$detNormalizedExp.det_id
+ARGS  ARG2 iteration=$detNormalizedExp.iteration
+ARGS  ARG2 basename=$detNormalizedExp.path_base
+ARGS  ARG2 camera=$detRun.camera
+
+#     field        		      size   format  name     show          link to                  extras
+FIELD detNormalizedExp.det_id,        7,     %s,     det_id
+FIELD detNormalizedExp.bg,            8,     %s,     backgnd
+FIELD detNormalizedExp.bg_mean_stdev, 8,     %s,     [stdev]
+FIELD detNormalizedExp.bg_stdev,      8,     %s,     stdev
+FIELD detNormalizedExp.iteration,     5,     %s,     iteration
+FIELD *,             		      8,     %s,     image,     image=JPEG2,  detNormalizedImfile.php, ARG2
+FIELD detNormalizedExp.path_base,     20,    %s,     path_base, none
+FIELD detRun.camera,                  20,    %s,     camera,    none
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedExp_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedExp_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedExp_failure.d	(revision 22322)
@@ -0,0 +1,22 @@
+TABLE detNormalizedExp, detRun
+TITLE detNormalizedExp Failures
+FILE  detNormalizedExp_failure.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detNormalizedExp.det_id = detRun.det_id
+WHERE detNormalizedExp.fault  > 0
+
+ARGS  ARG2 det_id=$detNormalizedExp.det_id
+ARGS  ARG2 iteration=$detNormalizedExp.iteration
+ARGS  ARG2 basename=$detNormalizedExp.path_base
+ARGS  ARG2 camera=$detRun.camera
+
+#     field        		      size   format  name     show          link to                  extras
+FIELD detNormalizedExp.det_id,        7,     %s,     det_id
+FIELD detNormalizedExp.bg,            8,     %s,     backgnd
+FIELD detNormalizedExp.bg_mean_stdev, 8,     %s,     [stdev]
+FIELD detNormalizedExp.bg_stdev,      8,     %s,     stdev
+FIELD detNormalizedExp.iteration,     5,     %s,     iteration
+FIELD detNormalizedExp.path_base,     20,    %s,     path_base, none
+FIELD detRun.camera,                  20,    %s,     camera,    none
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedImfile.d	(revision 22322)
@@ -0,0 +1,17 @@
+TABLE detNormalizedImfile
+TITLE detNormalizedImfile
+FILE  detNormalizedImfile.php
+MENU  ipp.imfiles.dat
+
+#     field         size  format  name     show   link to     extras
+FIELD det_id,          7, %s,     det_id
+FIELD class_id,        8, %s,     class_id
+FIELD bg,              8, %s,     backgnd
+FIELD bg_stdev,        8, %s,     stdev
+FIELD bg_mean_stdev,   8, %s,     [stdev]
+FIELD iteration,       5, %s,     iteration
+#FIELD uri,           20, %s,     uri
+#FIELD path_base,     20, %s,     path_base
+
+TAIL PHP insert_image ('PPIMAGE.JPEG1');
+TAIL PHP insert_log ('LOG.EXP');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedImfile_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedImfile_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedImfile_failure.d	(revision 22322)
@@ -0,0 +1,19 @@
+TABLE detNormalizedImfile
+TITLE detNormalizedImfile Failures
+FILE  detNormalizedImfile_failure.php
+MENU  ipp.detrend.dat
+
+WHERE fault > 0
+
+#     field         size  format  name     show   link to     extras
+FIELD det_id,          7, %s,     det_id
+FIELD class_id,        8, %s,     class_id
+FIELD bg,              8, %s,     backgnd
+FIELD bg_stdev,        8, %s,     stdev
+FIELD bg_mean_stdev,   8, %s,     [stdev]
+FIELD iteration,       5, %s,     iteration
+#FIELD uri,           20, %s,     uri
+#FIELD path_base,     20, %s,     path_base
+
+TAIL PHP insert_image ('PPIMAGE.JPEG1');
+TAIL PHP insert_log ('LOG.EXP');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedStatImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedStatImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedStatImfile.d	(revision 22322)
@@ -0,0 +1,10 @@
+TABLE detNormalizedStatImfile
+TITLE detNormalizedStatImfile
+FILE  detNormalizedStatImfile.php
+MENU  ipp.imfiles.dat
+
+#     field    size  format  name   show   link to     extras
+FIELD det_id,     7, %s,     det_id
+FIELD class_id,   8, %s,     class_id
+FIELD iteration,  5, %s,     iteration
+FIELD norm,       5, %s,     norm
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedStatImfile_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedStatImfile_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detNormalizedStatImfile_failure.d	(revision 22322)
@@ -0,0 +1,12 @@
+TABLE detNormalizedStatImfile
+TITLE detNormalizedStatImfile Failures
+FILE  detNormalizedStatImfile_failure.php
+MENU  ipp.detrend.dat
+
+where fault > 0
+
+#     field    size  format  name   show   link to     extras
+FIELD det_id,     7, %s,     det_id
+FIELD class_id,   8, %s,     class_id
+FIELD iteration,  5, %s,     iteration
+FIELD norm,       5, %s,     norm
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedExp.d	(revision 22322)
@@ -0,0 +1,42 @@
+TABLE detProcessedExp, rawExp
+TITLE detProcessedExp
+FILE  detProcessedExp.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detProcessedExp.exp_id = rawExp.exp_id
+
+# define image names to be used below
+# IMAGE VAR basename filerule camera class_id
+IMAGE JPEG2 $detProcessedExp.path_base PPIMAGE.JPEG2 $rawExp.camera NONE
+
+ARGS  ARG1 detProcessedImfile.det_id=$detProcessedExp.det_id
+ARGS  ARG1 detProcessedImfile.exp_id=$detProcessedExp.exp_id
+ARGS  ARG1 camera=$rawExp.camera
+ARGS  ARG1 basename=$detProcessedExp.path_base
+
+# OP    OP1  $detProcessedExp.bg_stdev > 0.0 ? $detProcessedExp.bg / $detProcessedExp.bg_stdev : NAN
+
+#     field                    size    	format  name       show          link to                 extras
+FIELD detProcessedExp.det_id,     7,   	%d,     Det ID,    value
+FIELD rawExp.camera,             10,    %s,     camera,    value
+FIELD detProcessedExp.exp_id, 	  5,   	%d,     Exp ID,    value
+FIELD rawExp.exp_name, 	          5,   	%s,     Exp Name,  value
+FIELD detProcessedExp.fault,      5,   	%d,     Fault,     value
+FIELD *, 	  		  8,   	%s,     image,     image=JPEG2,  detProcessedImfile.php, ARG1
+FIELD detProcessedExp.bg, 	  8,   	%.3f,   backgnd,   value
+FIELD detProcessedExp.bg_mean_stdev, 8,	%.3f,   [stdev],   value
+FIELD detProcessedExp.bg_stdev,   8,   	%.3f,   stdev,     value
+FIELD (detProcessedExp.bg/detProcessedExp.bg_stdev), 8, %.3f,   S/N,     value
+# FIELD *,                          8,    %.3f,   S/N,       op=OP1
+FIELD rawExp.dateobs,            19,   	%T,     obs date,  value
+FIELD rawExp.filter,             10,   	%s,     filter,    value
+FIELD rawExp.exp_time,            8,   	%.2f,    exptime,   value
+FIELD rawExp.ccd_temp,            8,   	%.2f,    ccd temp,  value
+FIELD rawExp.airmass,             8,   	%.4f,    airmass,   value
+FIELD detProcessedExp.path_base, 20,   	%s,     path_base, none
+
+# TD_CLASS det_off $detProcessedExp.fault > 0
+
+# note: you must include all desired fields, where ever they are used,
+# in FIELD
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedExp_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedExp_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedExp_failure.d	(revision 22322)
@@ -0,0 +1,37 @@
+TABLE detProcessedExp, rawExp
+TITLE detProcessedExp Failures
+FILE  detProcessedExp_failure.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detProcessedExp.exp_id = rawExp.exp_id
+WHERE detProcessedExp.fault > 0
+
+ARGS  ARG1 detProcessedImfile.det_id=$detProcessedExp.det_id
+ARGS  ARG1 detProcessedImfile.exp_id=$detProcessedExp.exp_id
+ARGS  ARG1 camera=$rawExp.camera
+ARGS  ARG1 basename=$detProcessedExp.path_base
+
+OP    OP1  $detProcessedExp.bg / $detProcessedExp.bg_stdev
+
+#     field                    size    	format  name       show          link to                 extras
+FIELD detProcessedExp.det_id,     7,   	%d,     Det ID,    value
+FIELD rawExp.camera,             10,    %s,     camera,    value
+FIELD detProcessedExp.exp_id, 	  5,   	%d,     Exp ID,    value
+FIELD rawExp.exp_name, 	          5,   	%s,     Exp Name,  value
+FIELD detProcessedExp.fault,      5,   	%d,     Fault,     value
+FIELD detProcessedExp.bg, 	  8,   	%.3f,   backgnd,   value
+FIELD detProcessedExp.bg_mean_stdev, 8,	%.3f,   [stdev],   value
+FIELD detProcessedExp.bg_stdev,   8,   	%.3f,   stdev,     value
+FIELD *,                          8,    %.3f,   S/N,       op=OP1
+FIELD rawExp.dateobs,            19,   	%T,     obs date,  value
+FIELD rawExp.filter,             10,   	%s,     filter,    value
+FIELD rawExp.exp_time,            8,   	%.2f,    exptime,   value
+FIELD rawExp.ccd_temp,            8,   	%.2f,    ccd temp,  value
+FIELD rawExp.airmass,             8,   	%.4f,    airmass,   value
+FIELD detProcessedExp.path_base, 20,   	%s,     path_base, none
+
+# TD_CLASS det_off $detProcessedExp.fault > 0
+
+# note: you must include all desired fields, where ever they are used,
+# in FIELD
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedExp_noimage.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedExp_noimage.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedExp_noimage.d	(revision 22322)
@@ -0,0 +1,42 @@
+TABLE detProcessedExp, rawExp
+TITLE detProcessedExp
+FILE  detProcessedExp_noimage.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detProcessedExp.exp_id = rawExp.exp_id
+
+# define image names to be used below
+# IMAGE VAR basename filerule camera class_id
+# IMAGE JPEG2 $detProcessedExp.path_base PPIMAGE.JPEG2 $rawExp.camera NONE
+
+ARGS  ARG1 detProcessedImfile.det_id=$detProcessedExp.det_id
+ARGS  ARG1 detProcessedImfile.exp_id=$detProcessedExp.exp_id
+ARGS  ARG1 camera=$rawExp.camera
+ARGS  ARG1 basename=$detProcessedExp.path_base
+
+# OP    OP1  $detProcessedExp.bg_stdev > 0.0 ? $detProcessedExp.bg / $detProcessedExp.bg_stdev : NAN
+
+#     field                    size    	format  name       show          link to                 extras
+FIELD detProcessedExp.det_id,     7,   	%d,     Det ID,    value
+FIELD rawExp.camera,             10,    %s,     camera,    value
+FIELD detProcessedExp.exp_id, 	  5,   	%d,     Exp ID,    value
+FIELD rawExp.exp_name, 	          5,   	%s,     Exp Name,  value
+FIELD detProcessedExp.fault,      5,   	%d,     Fault,     value
+# FIELD *, 	  		  8,   	%s,     image,     image=JPEG2,  detProcessedImfile.php, ARG1
+FIELD detProcessedExp.bg, 	  8,   	%.3f,   backgnd,   value
+FIELD detProcessedExp.bg_mean_stdev, 8,	%.3f,   [stdev],   value
+FIELD detProcessedExp.bg_stdev,   8,   	%.3f,   stdev,     value
+FIELD (detProcessedExp.bg/detProcessedExp.bg_stdev), 8, %.3f,   S/N,     value
+# FIELD *,                          8,    %.3f,   S/N,       op=OP1
+FIELD rawExp.dateobs,            19,   	%T,     obs date,  value
+FIELD rawExp.filter,             10,   	%s,     filter,    value
+FIELD rawExp.exp_time,            8,   	%.2f,    exptime,   value
+FIELD rawExp.ccd_temp,            8,   	%.2f,    ccd temp,  value
+FIELD rawExp.airmass,             8,   	%.4f,    airmass,   value
+FIELD detProcessedExp.path_base, 20,   	%s,     path_base, none
+
+# TD_CLASS det_off $detProcessedExp.fault > 0
+
+# note: you must include all desired fields, where ever they are used,
+# in FIELD
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedImfile.d	(revision 22322)
@@ -0,0 +1,24 @@
+TABLE detProcessedImfile, rawExp
+TITLE detProcessedImfile
+FILE  detProcessedImfile.php
+MENU  ipp.imfiles.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detProcessedImfile.exp_id = rawExp.exp_id
+
+#     field                          size  format  name      show   link to     extras
+FIELD detProcessedImfile.det_id,        7, %s,     Det ID
+FIELD detProcessedImfile.exp_id,        5, %s,     Exp ID
+FIELD rawExp.exp_name,                  5, %s,     Exp Name
+FIELD detProcessedImfile.class_id,      8, %s,     Chip ID
+FIELD detProcessedImfile.fault,         5, %d,     Chip ID
+FIELD detProcessedImfile.bg, 	        8, %f,     backgnd
+FIELD detProcessedImfile.bg_stdev,      8, %s,     stdev
+FIELD detProcessedImfile.bg_mean_stdev, 8, %s,     [stdev]
+FIELD detProcessedImfile.path_base,     10, %s,     path_base
+
+TD_CLASS det_off $detProcessedImfile.fault > 0
+
+TAIL PHP insert_image ('PPIMAGE.JPEG1');
+TAIL PHP insert_log ('LOG.EXP');
+
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedImfile_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedImfile_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detProcessedImfile_failure.d	(revision 22322)
@@ -0,0 +1,35 @@
+TABLE detProcessedImfile, rawExp, rawImfile
+TITLE detProcessedImfile Failures
+FILE  detProcessedImfile_failure.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detProcessedImfile.exp_id = rawExp.exp_id
+WHERE detProcessedImfile.exp_id = rawImfile.exp_id
+WHERE detProcessedImfile.class_id = rawImfile.class_id
+WHERE detProcessedImfile.fault > 0
+
+ARGS  ARG1 detProcessedImfile.det_id=$detProcessedImfile.det_id
+ARGS  ARG1 detProcessedImfile.exp_id=$detProcessedImfile.exp_id
+ARGS  ARG1 detProcessedImfile.class_id=$detProcessedImfile.class_id
+ARGS  ARG1 camera=$rawExp.camera
+ARGS  ARG1 basename=$detProcessedImfile.path_base
+ARGS  ARG1 class=$detProcessedImfile.class_id
+
+#     field                          size  format  name      show   link to     extras
+FIELD detProcessedImfile.det_id,       12, %d,     Det ID
+FIELD detProcessedImfile.exp_id,        5, %d,     Exp ID
+FIELD rawExp.exp_name,                  5, %s,     Exp Name
+FIELD detProcessedImfile.class_id,      8, %s,     Chip ID,  value,  detProcessedImfile_failure.php, ARG1
+FIELD detProcessedImfile.fault,         5, %d,     fault
+FIELD detProcessedImfile.bg, 	        8, %.2f,   backgnd
+FIELD detProcessedImfile.bg_stdev,      8, %.2f,   stdev
+FIELD detProcessedImfile.bg_mean_stdev, 8, %.2f,   [stdev]
+FIELD rawImfile.ccd_temp,               5, %.2f,   CCD Temp
+FIELD detProcessedImfile.path_base,    20, %s,     path_base
+
+FIELD rawExp.camera,                    5, %s,     Exp Name, none
+
+# TD_CLASS det_off $detProcessedImfile.fault > 0
+
+TAIL PHP insert_log ('LOG.IMFILE');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidExp.d	(revision 22322)
@@ -0,0 +1,57 @@
+TABLE detResidExp, rawExp, detInputExp
+TITLE detResidExp
+FILE  detResidExp.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detResidExp.exp_id = rawExp.exp_id
+WHERE detResidExp.exp_id = detInputExp.exp_id
+WHERE detResidExp.iteration = detInputExp.iteration
+
+# define image names to be used below
+# IMAGE VAR basename filerule camera class_id
+IMAGE JPEG2 $detResidExp.path_base PPIMAGE.JPEG2 $rawExp.camera NONE
+
+# define the arguments supplied to the links below (if any)
+ARGS  ARG1 exp_id=$detResidExp.exp_id
+
+ARGS  ARG2 detResidImfile.det_id=$detResidExp.det_id
+ARGS  ARG2 detResidImfile.iteration=$detResidExp.iteration
+ARGS  ARG2 detResidImfile.exp_id=$detResidExp.exp_id
+ARGS  ARG2 camera=$rawExp.camera
+ARGS  ARG2 basename=$detResidExp.path_base
+
+# OP    OP1  $detResidExp.bin_stdev > 0.0 ? $detResidExp.bg / $detResidExp.bin_stdev : NAN
+
+#     field                   size  format  name       show         link to              linkargs
+FIELD detResidExp.det_id,        7, %s,     Det ID,    value
+FIELD rawExp.camera,            10, %s,     camera,    value
+FIELD detResidExp.exp_id, 	 5, %s,     Exp ID,    value,       rawImfile.php,       ARG1
+FIELD rawExp.exp_name, 	         5, %s,     Exp Name,  value,       rawImfile.php,       ARG1
+FIELD detResidExp.iteration,     5, %s,     iter,      value
+FIELD detInputExp.include,       3, %t,     used,      value
+FIELD detResidExp.accept,        3, %t,     keep,      value
+FIELD detResidExp.fault,         4, %d,     fault,     value
+FIELD *,    	                 8, %s,     image,     image=JPEG2, detResidImfile.php,  ARG2
+FIELD detResidExp.bg,            8, %.3f,   backgnd,   value
+FIELD detResidExp.bg_mean_stdev, 8, %.3f,   [stdev],   value
+FIELD detResidExp.bg_stdev,      8, %.3f,   stdev,     value
+FIELD detResidExp.bin_stdev,     8, %.3f,   bin stdev, value
+FIELD (detResidExp.bg/detResidExp.bin_stdev), 8, %.3f, S/N, value
+# FIELD *,                         8, %s,     S/N,       op=OP1
+FIELD detResidExp.fringe_0,      8, %.3f,   fringe,    value
+FIELD detResidExp.fringe_1,      8, %.3f,   fringe err, value
+FIELD detResidExp.fringe_2,      8, %.3f,   fringe stdev, value
+FIELD detResidExp.user_1,        8, %.3f,   dfringe,    value
+FIELD detResidExp.user_2,        8, %.3f,   dfringe err, value
+FIELD detResidExp.user_3,        8, %.3f,   dfringe stdev, value
+FIELD rawExp.dateobs,           19, %T,     obs date,  value
+FIELD rawExp.filter,            10, %s,     filter,    value
+FIELD rawExp.exp_time,           8, %.2f,   exptime,   value
+FIELD rawExp.ccd_temp,           8, %.2f,   ccd temp,  value
+FIELD rawExp.airmass,            8, %.4f,   airmass,   value
+FIELD detResidExp.path_base,    20, %s,     path_base, none
+
+TD_CLASS det_drop $detInputExp.include == 1 && $detResidExp.accept == 0
+TD_CLASS det_off  $detInputExp.include == 0 && $detResidExp.accept == 0
+TD_CLASS det_add  $detInputExp.include == 0 && $detResidExp.accept == 1
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidExp_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidExp_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidExp_failure.d	(revision 22322)
@@ -0,0 +1,52 @@
+TABLE detResidExp, rawExp, detInputExp
+TITLE detResidExp Failures
+FILE  detResidExp_failure.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detResidExp.exp_id = rawExp.exp_id
+WHERE detResidExp.exp_id = detInputExp.exp_id
+WHERE detResidExp.iteration = detInputExp.iteration
+WHERE detResidExp.fault > 0
+
+# define the arguments supplied to the links below (if any)
+ARGS  ARG1 exp_id=$detResidExp.exp_id
+
+ARGS  ARG2 detResidImfile.det_id=$detResidExp.det_id
+ARGS  ARG2 detResidImfile.iteration=$detResidExp.iteration
+ARGS  ARG2 detResidImfile.exp_id=$detResidExp.exp_id
+ARGS  ARG2 camera=$rawExp.camera
+ARGS  ARG2 basename=$detResidExp.path_base
+
+OP    OP1  $detResidExp.bin_stdev > 0.0 ? $detResidExp.bg / $detResidExp.bin_stdev : NAN
+
+#     field                   size  format  name       show         link to              linkargs
+FIELD detResidExp.det_id,        7, %s,     Det ID,    value
+FIELD rawExp.camera,            10, %s,     camera,    value
+FIELD detResidExp.exp_id, 	 5, %s,     Exp ID,    value,       rawImfile.php,       ARG1
+FIELD rawExp.exp_name, 	         5, %s,     Exp Name,  value,       rawImfile.php,       ARG1
+FIELD detResidExp.iteration,     5, %s,     iter,      value
+FIELD detInputExp.include,       3, %t,     used,      value
+FIELD detResidExp.accept,        3, %t,     keep,      value
+FIELD detResidExp.fault,         4, %d,     fault,     value
+FIELD detResidExp.bg,            8, %.3f,   backgnd,   value
+FIELD detResidExp.bg_mean_stdev, 8, %.3f,   [stdev],   value
+FIELD detResidExp.bg_stdev,      8, %.3f,   stdev,     value
+FIELD detResidExp.bin_stdev,     8, %.3f,   bin stdev, value
+FIELD *,                         8, %s,     S/N,       op=OP1
+FIELD detResidExp.fringe_0,      8, %.3f,   fringe,    value
+FIELD detResidExp.fringe_1,      8, %.3f,   fringe err, value
+FIELD detResidExp.fringe_2,      8, %.3f,   fringe stdev, value
+FIELD detResidExp.user_1,        8, %.3f,   dfringe,    value
+FIELD detResidExp.user_2,        8, %.3f,   dfringe err, value
+FIELD detResidExp.user_3,        8, %.3f,   dfringe stdev, value
+FIELD rawExp.dateobs,           19, %T,     obs date,  value
+FIELD rawExp.filter,            10, %s,     filter,    value
+FIELD rawExp.exp_time,           8, %.2f,   exptime,   value
+FIELD rawExp.ccd_temp,           8, %.2f,   ccd temp,  value
+FIELD rawExp.airmass,            8, %.4f,   airmass,   value
+FIELD detResidExp.path_base,    20, %s,     path_base, none
+
+TD_CLASS det_drop $detInputExp.include == 1 && $detResidExp.accept == 0
+TD_CLASS det_off  $detInputExp.include == 0 && $detResidExp.accept == 0
+TD_CLASS det_add  $detInputExp.include == 0 && $detResidExp.accept == 1
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidExp_noimage.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidExp_noimage.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidExp_noimage.d	(revision 22322)
@@ -0,0 +1,57 @@
+TABLE detResidExp, rawExp, detInputExp
+TITLE detResidExp
+FILE  detResidExp_noimage.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detResidExp.exp_id = rawExp.exp_id
+WHERE detResidExp.exp_id = detInputExp.exp_id
+WHERE detResidExp.iteration = detInputExp.iteration
+
+# define image names to be used below
+# IMAGE VAR basename filerule camera class_id
+# IMAGE JPEG2 $detResidExp.path_base PPIMAGE.JPEG2 $rawExp.camera NONE
+
+# define the arguments supplied to the links below (if any)
+ARGS  ARG1 exp_id=$detResidExp.exp_id
+
+ARGS  ARG2 detResidImfile.det_id=$detResidExp.det_id
+ARGS  ARG2 detResidImfile.iteration=$detResidExp.iteration
+ARGS  ARG2 detResidImfile.exp_id=$detResidExp.exp_id
+ARGS  ARG2 camera=$rawExp.camera
+ARGS  ARG2 basename=$detResidExp.path_base
+
+# OP    OP1  $detResidExp.bin_stdev > 0.0 ? $detResidExp.bg / $detResidExp.bin_stdev : NAN
+
+#     field                   size  format  name       show         link to              linkargs
+FIELD detResidExp.det_id,        7, %s,     Det ID,    value
+FIELD rawExp.camera,            10, %s,     camera,    value
+FIELD detResidExp.exp_id, 	 5, %s,     Exp ID,    value,       rawImfile.php,       ARG1
+FIELD rawExp.exp_name, 	         5, %s,     Exp Name,  value,       rawImfile.php,       ARG1
+FIELD detResidExp.iteration,     5, %s,     iter,      value
+FIELD detInputExp.include,       3, %t,     used,      value
+FIELD detResidExp.accept,        3, %t,     keep,      value
+FIELD detResidExp.fault,         4, %d,     fault,     value
+# FIELD *,    	                 8, %s,     image,     image=JPEG2, detResidImfile.php,  ARG2
+FIELD detResidExp.bg,            8, %.3f,   backgnd,   value
+FIELD detResidExp.bg_mean_stdev, 8, %.3f,   [stdev],   value
+FIELD detResidExp.bg_stdev,      8, %.3f,   stdev,     value
+FIELD detResidExp.bin_stdev,     8, %.3f,   bin stdev, value
+FIELD (detResidExp.bg/detResidExp.bin_stdev), 8, %.3f, S/N, value
+# FIELD *,                         8, %s,     S/N,       op=OP1
+FIELD detResidExp.fringe_0,      8, %.3f,   fringe,    value
+FIELD detResidExp.fringe_1,      8, %.3f,   fringe err, value
+FIELD detResidExp.fringe_2,      8, %.3f,   fringe stdev, value
+FIELD detResidExp.user_1,        8, %.3f,   dfringe,    value
+FIELD detResidExp.user_2,        8, %.3f,   dfringe err, value
+FIELD detResidExp.user_3,        8, %.3f,   dfringe stdev, value
+FIELD rawExp.dateobs,           19, %T,     obs date,  value
+FIELD rawExp.filter,            10, %s,     filter,    value
+FIELD rawExp.exp_time,           8, %.2f,   exptime,   value
+FIELD rawExp.ccd_temp,           8, %.2f,   ccd temp,  value
+FIELD rawExp.airmass,            8, %.4f,   airmass,   value
+FIELD detResidExp.path_base,    20, %s,     path_base, none
+
+TD_CLASS det_drop $detInputExp.include == 1 && $detResidExp.accept == 0
+TD_CLASS det_off  $detInputExp.include == 0 && $detResidExp.accept == 0
+TD_CLASS det_add  $detInputExp.include == 0 && $detResidExp.accept == 1
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidImfile.d	(revision 22322)
@@ -0,0 +1,25 @@
+TABLE detResidImfile, rawImfile
+TITLE detResidImfile
+FILE  detResidImfile.php
+MENU  ipp.imfiles.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detResidImfile.exp_id = rawImfile.exp_id
+WHERE detResidImfile.class_id = rawImfile.class_id
+
+#     field                      size  format  name        show  link to     extras
+FIELD detResidImfile.det_id,        7, %s,      Det ID
+FIELD detResidImfile.iteration,     5, %s,      iter
+FIELD detResidImfile.exp_id,        5, %s,      Exp ID
+FIELD rawImfile.exp_name,           5, %s,      Exp Name
+FIELD detResidImfile.class_id,      8, %s,      Chip ID
+FIELD detResidImfile.bg,            8, %s,      backgnd
+FIELD detResidImfile.bg_stdev,      8, %s,      bg stdev
+FIELD detResidImfile.bg_mean_stdev, 8, %s,      [stdev]
+FIELD rawImfile.ccd_temp,           5, %s,      CCD Temp
+FIELD detResidImfile.fault,         5, %s,      fault
+FIELD detResidImfile.path_base,     5, %s,      path_base
+FIELD detResidImfile.uri,           5, %s,      uri
+
+TAIL PHP insert_image ('PPIMAGE.JPEG1');
+TAIL PHP insert_log ('LOG.EXP');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidImfile_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidImfile_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detResidImfile_failure.d	(revision 22322)
@@ -0,0 +1,35 @@
+TABLE detResidImfile, rawExp, rawImfile
+TITLE detResidImfile Failures
+FILE  detResidImfile_failure.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detResidImfile.exp_id = rawExp.exp_id
+WHERE detResidImfile.exp_id = rawImfile.exp_id
+WHERE detResidImfile.class_id = rawImfile.class_id
+WHERE detResidImfile.fault > 0
+
+ARGS  ARG1 detResidImfile.det_id=$detResidImfile.det_id
+ARGS  ARG1 detResidImfile.iteration=$detResidImfile.iteration
+ARGS  ARG1 detResidImfile.exp_id=$detResidImfile.exp_id
+ARGS  ARG1 detResidImfile.class_id=$detResidImfile.class_id
+ARGS  ARG1 camera=$rawExp.camera
+ARGS  ARG1 basename=$detResidImfile.path_base
+ARGS  ARG1 class=$detResidImfile.class_id
+
+#     field                      size  format  name        show  link to     extras
+FIELD detResidImfile.det_id,        5, %d,      Det ID
+FIELD detResidImfile.iteration,     3, %d,      iter
+FIELD detResidImfile.exp_id,        5, %d,      Exp ID
+FIELD rawImfile.exp_name,           5, %s,      Exp Name
+FIELD detResidImfile.class_id,      8, %s,      Chip ID,  value,  detResidImfile_failure.php, ARG1
+FIELD detResidImfile.fault,         5, %d,      fault
+FIELD detResidImfile.bg,            8, %.2f,    backgnd
+FIELD detResidImfile.bg_stdev,      8, %.2f,    bg stdev
+FIELD detResidImfile.bg_mean_stdev, 8, %.2f,    [stdev]
+FIELD rawImfile.ccd_temp,           5, %.2f,    CCD Temp
+FIELD detResidImfile.path_base,    20, %s,      path_base
+
+FIELD rawExp.camera,                    5, %s,     Exp Name, none
+
+TAIL PHP insert_log ('LOG.IMFILE');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detRun.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detRun.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detRun.d	(revision 22322)
@@ -0,0 +1,42 @@
+TABLE detRun
+TITLE detRun
+FILE  detRun.php
+MENU  ipp.detrend.dat
+
+ARGS  ARG1 detRunSummary.det_id=$det_id
+
+ARGS  ARG2 detInputExp.det_id=$det_id
+ARGS  ARG2 detInputExp.iteration=$iteration
+
+ARGS  ARG3 detProcessedExp.det_id=$det_id
+
+ARGS  ARG4 detResidExp.det_id=$det_id
+ARGS  ARG4 detResidExp.iteration=$iteration
+
+ARGS  ARG5 det_id=$det_id
+
+#     field       size  format  name            show         link to                extras
+FIELD det_id,        5, %s,     det ID,       value,       detRunSummary.php,     ARG1
+FIELD iteration,     3, %s,     iter
+FIELD det_type,     12, %s,     type
+FIELD state,         5, %s,     state
+FIELD *,             5, %s,     choose,       value=input, detInputExp.php,       ARG2
+FIELD *,             5, %s,     choose,       value=proc,  detProcessedExp.php,   ARG3
+FIELD *,             5, %s,     choose,       value=stack, detStackedImfile.php,  ARG5
+FIELD *,             5, %s,     choose,       value=resid, detResidExp.php,       ARG4
+FIELD mode,          5, %s,     mode
+FIELD filter,       10, %s,     filter     
+FIELD registered,   19, %s,     registered 
+FIELD use_begin,    19, %s,     use_begin  
+FIELD use_end,      19, %s,     use_end    
+FIELD airmass_min,   5, %.2f,   min_airmass    
+FIELD airmass_max,   5, %.2f,   max     
+FIELD exp_time_min,  5, %.2f,   min_exp_time   
+FIELD exp_time_max,  5, %.2f,   max 
+FIELD ccd_temp_min,  5, %.2f,   min_ccd_temp   
+FIELD ccd_temp_max,  5, %.2f,   max    
+FIELD posang_min,    5, %.2f,   min_posang     
+FIELD posang_max,    5, %s,     max      
+
+TD_CLASS list_off $state == 'drop'
+TD_CLASS list_run $state == 'run'
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detRunSummary.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detRunSummary.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detRunSummary.d	(revision 22322)
@@ -0,0 +1,26 @@
+TABLE detRunSummary, detRun
+TITLE Detrend Run Iterations (does not list registered frames)
+FILE  detRunSummary.php
+MENU  ipp.detrend.dat
+
+WHERE detRunSummary.det_id = detRun.det_id
+
+ARGS  ARG1  detInputExp.det_id=$detRunSummary.det_id
+ARGS  ARG1  detInputExp.iteration=$detRunSummary.iteration
+
+ARGS  ARG2  detResidExp.det_id=$detRunSummary.det_id
+ARGS  ARG2  detResidExp.iteration=$detRunSummary.iteration
+
+#     field                   size  format  name  show      link to           extras
+FIELD detRunSummary.det_id,     7,  %s,     det_id    
+FIELD detRunSummary.iteration,  5,  %s,     iter
+FIELD detRunSummary.accept,     4,  %s,     keep
+FIELD *,                       20,  %s,     choose,    value=input, detInputExp.php,  ARG1
+FIELD *,                       20,  %s,     choose,    value=proc,  detProcessedExp.php,      ARG3
+FIELD *,                       20,  %s,     choose,    value=resid, detResidExp.php,  ARG2
+FIELD detRunSummary.bg,        20,  %s,     bg
+FIELD detRunSummary.bg_stdev,  20,  %s,     bg_stdev
+FIELD detRun.state,            20,  %s,     state,     none
+
+TD_CLASS list_off $detRun.state == 'drop'
+TD_CLASS list_run $detRun.state == 'run'
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detStackedImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detStackedImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detStackedImfile.d	(revision 22322)
@@ -0,0 +1,17 @@
+TABLE detStackedImfile
+TITLE detStackedImfile
+FILE  detStackedImfile.php
+MENU  ipp.imfiles.dat
+
+#     field         size  format  name                   show  link to     extras
+FIELD det_id,         7,  %s,    det_id
+FIELD iteration,      5,  %s,    iteration
+FIELD class_id,       8,  %s,    class_id
+FIELD fault,          5,  %d,    fault
+FIELD bg,             8,  %f,    backgnd
+FIELD bg_stdev,       8,  %f,    stdev
+FIELD bg_mean_stdev,  8,  %f,    [stdev]
+FIELD uri,           20,  %s,    uri
+# FIELD recipe, 20,   recipe
+
+TD_CLASS list_off $fault > 0
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/detStackedImfile_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/detStackedImfile_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/detStackedImfile_failure.d	(revision 22322)
@@ -0,0 +1,19 @@
+TABLE detStackedImfile Failures
+TITLE detStackedImfile
+FILE  detStackedImfile_failure.php
+MENU  ipp.detrend.dat
+
+WHERE fault > 0
+
+#     field         size  format  name                   show  link to     extras
+FIELD det_id,         7,  %s,    det_id
+FIELD iteration,      5,  %s,    iteration
+FIELD class_id,       8,  %s,    class_id
+FIELD fault,          5,  %d,    fault
+FIELD bg,             8,  %f,    backgnd
+FIELD bg_stdev,       8,  %f,    stdev
+FIELD bg_mean_stdev,  8,  %f,    [stdev]
+FIELD uri,           20,  %s,    uri
+# FIELD recipe, 20,   recipe
+
+# TD_CLASS list_off $fault > 0
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/diffInputSkyfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/diffInputSkyfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/diffInputSkyfile.d	(revision 22322)
@@ -0,0 +1,12 @@
+TABLE diffInputSkyfile
+TITLE diffInputSkyfile
+FILE  diffInputSkyfile.php
+MENU  ipp.diff.dat
+
+#        field        size  format  name           show     link to         extras
+FIELD    diff_id,	20, %s,     diff ID
+FIELD    warp_id,	20, %s,     warp ID
+FIELD    skycell_id,	20, %s,     skycell ID
+FIELD    tess_id,	20, %s,     tessellation ID
+FIELD    kind,          20, %s,     Kind
+FIELD    template,	20, %s,     Template?
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/diffRun.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/diffRun.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/diffRun.d	(revision 22322)
@@ -0,0 +1,13 @@
+TABLE diffRun
+TITLE diffRun
+FILE  diffRun.php
+MENU  ipp.diff.dat
+
+#        field        size  format  name           show     link to         extras
+FIELD    diff_id,	20, %s,     diff ID
+FIELD    state,		20, %s,     state
+FIELD    workdir,	20, %s,     workdir
+FIELD    dvodb,		20, %s,     DVO database
+FIELD    registered,	20, %s,     Time registered
+FIELD    skycell_id,	20, %s,     skycell ID
+FIELD    tess_id,	20, %s,     tessellation ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/diffSkyfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/diffSkyfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/diffSkyfile.d	(revision 22322)
@@ -0,0 +1,10 @@
+TABLE diffSkyfile
+TITLE diffSkyfile
+FILE  diffSkyfile.php
+MENU  ipp.diff.dat
+
+#        field        size  format  name           show     link to         extras
+FIELD    diff_id,	20, %s,     diff ID
+FIELD    uri,		20, %s,     URI
+FIELD    bg,		20, %s,     background
+FIELD    bg_stdev,	20, %s,     standard deviation
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/example.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/example.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/example.d	(revision 22322)
@@ -0,0 +1,32 @@
+# this file il
+# master detrend runs which have been accepted
+
+TABLE detRunSummary, detRun
+TITLE Master Detrend Images
+FILE  masterDetrendImages.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detRunSummary.accept = 1
+WHERE detRun.det_it = detRunSummary.det_id
+WHERE detRun.iteration = detRunSummary.iteration
+
+ARGS  ARG1 det_id=$detRunSummary.det_id
+ARGS  ARG1 iteration=$detRunSummary.iteration
+
+#     field                    width format  name           show         link to           extras
+FIELD detRunSummary.det_id,    7,    %s,     det_id
+FIELD detRun.det_type,         5,    %s,     type
+FIELD detRunSummary.iteration, 5,    %s,     iter
+FIELD *,                       20,   %s,     choose,       value=input, detInputExp.php,  ARG1
+FIELD *,                       20,   %s,     choose,       value=resid, detResidExp.php,  ARG1
+FIELD detRunSummary.bg,        20,   %s,     bg
+FIELD detRunSummary.bg_stdev,  20,   %s,     bg_stdev
+# FIELD bg_mean_stdev, 20,   bg_mean_stdev
+
+# this file illustrates a simple inner join to generate the output table
+# assuming no other dynamic restrictions are added, this generates the following query:
+# SELECT detRunSummary.det_id,detRunSummary.iteration,detRunSummary.iteration,detRun.det_type,detRunSummary.bg,detRunSummary.bg_stdev \
+# FROM detRunSummary, detRun \
+# WHERE detRunSummary.accept = 1 AND detRun.det_id = detRunSummary.det_id AND detRun.iteration = detRunSummary.iteration \
+# LIMIT 20
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakePendingExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakePendingExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakePendingExp.d	(revision 22322)
@@ -0,0 +1,43 @@
+TABLE fakeRun, camRun, chipRun, rawExp
+TITLE fake Pending Exposures
+FILE  fakePendingExp.php
+MENU  ipp.science.dat
+
+# limit this table to non-OBJECT types of images
+WHERE fakeRun.state = 'new'
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                   size  format  name         show    link to         extras
+FIELD rawExp.exp_name,     	10, %s,     Exp Name
+FIELD rawExp.exp_id,     	 5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	 7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,    	 7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,    	 7, %d,     Fake ID,      value,  fakeProcessedImfile.php, ARG4
+FIELD fakeRun.state,    	 7, %s,     state
+FIELD fakeRun.label,    	 7, %s,     label
+FIELD rawExp.telescope,      	10, %s,     Telescope
+FIELD rawExp.camera,         	10, %s,     Camera
+FIELD rawExp.dateobs,        	19, %T,     Date/Time
+FIELD rawExp.ra,       	         8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	 8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	 8, %s,     Object
+FIELD rawExp.filter,         	10, %s,     FILTER
+FIELD rawExp.exp_time,       	 5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	 5, %.4f,   airmass     
+FIELD rawExp.bg,             	 5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	 5, %.2f,   stdev    
+FIELD rawExp.comment,  	        65, %s,     Comment
+
+# FIELD rawExp.bg_mean_stdev,  	 5, %7.2f,  &lt;backgnd&gt;
+# FIELD chipPendingExp.reduction,    20,   reduction
+# FIELD chipPendingExp.guide_id,     20, guide ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakePendingImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakePendingImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakePendingImfile.d	(revision 22322)
@@ -0,0 +1,42 @@
+TABLE fakeRun, camRun, chipRun, rawExp, rawImfile
+TITLE Fake Pending Imfiles
+FILE  fakePendingImfile.php
+MENU  ipp.science.dat
+
+WHERE fakeRun.state = 'new'
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE rawImfile.exp_id = rawExp.exp_id
+
+ARGS  ARG1 rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2 chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3 camRun.cam_id=$camRun.cam_id
+ARGS  ARG3 fakeRun.fake_id=$fakeRun.fake_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                   size  format  name         show    link to         extras
+FIELD rawExp.exp_name,     	10, %s,     Exp Name
+FIELD rawImfile.class_id,     	 8, %s,     Class ID
+FIELD rawExp.exp_id,     	 5, %d,     Exp ID,      value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	 7, %d,     Chip ID,     value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,          	 5, %s,     Cam ID,      alue,   camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,        	 5, %s,     Fake ID,     value,  fakeProcessedImfile.php, ARG4
+FIELD fakeRun.state,    	 7, %s,     State
+FIELD fakeRun.label,    	 7, %s,     label
+FIELD rawExp.telescope,      	10, %s,     Telescope
+FIELD rawExp.camera,         	10, %s,     Camera
+FIELD rawExp.dateobs,        	19, %T,     Date/Time
+FIELD rawExp.ra,       	         8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	 8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	 8, %s,     Object
+FIELD rawExp.filter,         	10, %s,     FILTER
+FIELD rawExp.exp_time,       	 5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	 5, %.4f,   airmass     
+FIELD rawExp.bg,             	 5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	 5, %.2f,   stdev    
+FIELD *,                         5, %d,     Nstars,      value=0
+FIELD *, 	                 5, %.2f,   FHWM,        value=0.0
+FIELD rawExp.comment,  	        65, %s,     Comment
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeProcessedExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeProcessedExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeProcessedExp.d	(revision 22322)
@@ -0,0 +1,44 @@
+TABLE fakeRun, camRun, chipRun, rawExp
+TITLE fake Processed Exposures
+FILE  fakeProcessedExp.php
+MENU  ipp.science.dat
+
+# limit this table to non-OBJECT types of images
+WHERE fakeRun.state != 'new'
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                   size  format  name         show    link to                  extras
+FIELD rawExp.exp_name,     	10, %s,     Exp Name
+FIELD rawExp.exp_id,     	 5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	 7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,    	 7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,    	 7, %d,     Fake ID,      value,  fakeProcessedImfile.php, ARG4
+FIELD fakeRun.state,    	 7, %s,     state
+FIELD fakeRun.label,    	 7, %s,     label
+FIELD rawExp.telescope,      	10, %s,     Telescope
+FIELD rawExp.camera,         	10, %s,     Camera
+FIELD rawExp.dateobs,        	19, %T,     Date/Time
+FIELD rawExp.ra,           	 8, %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,         	 8, %10.6f, DEC,          op=OP2
+FIELD rawExp.object,         	 8, %s,     Object
+FIELD rawExp.filter,         	10, %s,     FILTER
+FIELD rawExp.exp_time,       	 5, %.2f,     exp_time    
+FIELD rawExp.airmass,        	 5, %.4f,     airmass     
+FIELD rawExp.bg,             	 5, %.2f,     backgnd
+FIELD rawExp.bg_stdev,       	 5, %.2f,     stdev    
+FIELD rawExp.comment,  	        65, %s,     Comment
+
+# FIELD rawExp.bg_mean_stdev,  	 5, %s,     &lt;backgnd&gt;
+# FIELD rawExp.sat_pixel_frac, 	 5, %s,     f(sat pixels)
+# FIELD chipPendingExp.reduction,    20,   reduction
+# FIELD chipPendingExp.guide_id,     20, guide ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeProcessedImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeProcessedImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeProcessedImfile.d	(revision 22322)
@@ -0,0 +1,54 @@
+TABLE fakeRun, fakeProcessedImfile, camRun, chipRun, rawExp
+TITLE fake Processed Imfile
+FILE  fakeProcessedImfile.php
+MENU  ipp.science.dat
+
+# limit this table to non-OBJECT types of images
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE fakeProcessedImfile.fake_id = fakeRun.fake_id
+WHERE fakeProcessedImfile.fault = 0
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+
+ARGS  ARG5 fakeRun.fake_id=fakeRun.fake_id
+ARGS  ARG5 fakeProcessedImfile.class_id=$fakeProcessedImfile.class_id
+ARGS  ARG5 camera=$rawExp.camera
+ARGS  ARG5 basename=$fakeProcessedImfile.path_base
+ARGS  ARG5 class=$fakeProcessedImfile.class_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                         size  format  name         show    link to                  extras
+FIELD rawExp.exp_name,                10, %s,     Exp Name
+FIELD fakeProcessedImfile.class_id,    8, %s,     Class ID
+FIELD rawExp.exp_id,                   5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,                 7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,                   7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,                 7, %d,     Fake ID,      value,  fakeProcessedImfile.php, ARG4
+FIELD fakeRun.state,                   7, %s,     State,       value,  fakeProcessedImfile.php, ARG5
+FIELD fakeRun.label,                   7, %s,     Label
+FIELD rawExp.telescope,               10, %s,     Telescope
+FIELD rawExp.camera,                  10, %s,     Camera
+FIELD rawExp.dateobs,                 19, %T,     Date/Time
+FIELD rawExp.ra,                       8, %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,                     8, %10.6f, DEC,          op=OP2
+FIELD rawExp.object,                   8, %s,     Object
+FIELD rawExp.filter,                  10, %s,     FILTER
+FIELD rawExp.exp_time,                 5, %.2f,   exp_time    
+FIELD rawExp.airmass,                  5, %.4f,   airmass     
+FIELD rawExp.comment,                 65, %s,     Comment
+
+FIELD fakeProcessedImfile.path_base,      5, %s,     path_base, none
+
+TAIL PHP insert_log ('LOG.IMFILE');
+
+# FIELD chipProcessedImfile.bg,          5, %.2f,   backgnd
+# FIELD chipProcessedImfile.bg_stdev,    5, %.2f,   stdev    
+# FIELD chipProcessedImfile.n_stars,     5, %d,     Nstars
+# FIELD chipProcessedImfile.fwhm_major,  5, %.2f,   FHWM
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeProcessedImfile_failure.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeProcessedImfile_failure.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeProcessedImfile_failure.d	(revision 22322)
@@ -0,0 +1,54 @@
+TABLE fakeRun, fakeProcessedImfile, camRun, chipRun, rawExp
+TITLE fake Processed Imfile
+FILE  fakeProcessedImfile_failure.php
+MENU  ipp.science.dat
+
+# limit this table to non-OBJECT types of images
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE fakeProcessedImfile.fake_id = fakeRun.fake_id
+WHERE fakeProcessedImfile.fault != 0
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+
+ARGS  ARG5 fakeRun.fake_id=fakeRun.fake_id
+ARGS  ARG5 fakeProcessedImfile.class_id=$fakeProcessedImfile.class_id
+ARGS  ARG5 camera=$rawExp.camera
+ARGS  ARG5 basename=$fakeProcessedImfile.path_base
+ARGS  ARG5 class=$fakeProcessedImfile.class_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                         size  format  name         show    link to                  extras
+FIELD rawExp.exp_name,                10, %s,     Exp Name
+FIELD fakeProcessedImfile.class_id,    8, %s,     Class ID
+FIELD rawExp.exp_id,                   5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,                 7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,                   7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,                 7, %d,     Fake ID,      value,  fakeProcessedImfile_failure.php, ARG4
+FIELD fakeRun.state,                   7, %s,     State,       value,  fakeProcessedImfile_failure.php, ARG5
+FIELD fakeRun.label,                   7, %s,     Label
+FIELD rawExp.telescope,               10, %s,     Telescope
+FIELD rawExp.camera,                  10, %s,     Camera
+FIELD rawExp.dateobs,                 19, %T,     Date/Time
+FIELD rawExp.ra,                       8, %10.6f, RA,           op=OP1      
+FIELD rawExp.decl,                     8, %10.6f, DEC,          op=OP2
+FIELD rawExp.object,                   8, %s,     Object
+FIELD rawExp.filter,                  10, %s,     FILTER
+FIELD rawExp.exp_time,                 5, %.2f,   exp_time    
+FIELD rawExp.airmass,                  5, %.4f,   airmass     
+FIELD rawExp.comment,                 65, %s,     Comment
+
+FIELD fakeProcessedImfile.path_base,      5, %s,     path_base, none
+
+TAIL PHP insert_log ('LOG.IMFILE');
+
+# FIELD chipProcessedImfile.bg,          5, %.2f,   backgnd
+# FIELD chipProcessedImfile.bg_stdev,    5, %.2f,   stdev    
+# FIELD chipProcessedImfile.n_stars,     5, %d,     Nstars
+# FIELD chipProcessedImfile.fwhm_major,  5, %.2f,   FHWM
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeStageExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeStageExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeStageExp.d	(revision 22322)
@@ -0,0 +1,41 @@
+TABLE fakeRun, camRun, chipRun, rawExp
+TITLE Fake Stage Exposures
+FILE  fakeStageExp.php
+MENU  ipp.science.dat
+
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                   size  format  name         show    link to         extras
+FIELD rawExp.exp_name,     	10, %s,     Exp Name
+FIELD rawExp.exp_id,     	 5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	 7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,    	 7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,    	 7, %d,     Fake ID,      value,  fakeProcessedImfile.php, ARG4
+FIELD fakeRun.state,    	 7, %s,     state
+FIELD fakeRun.label,    	 7, %s,     label
+FIELD rawExp.telescope,      	10, %s,     Telescope
+FIELD rawExp.camera,         	10, %s,     Camera
+FIELD rawExp.dateobs,        	19, %T,     Date/Time
+FIELD rawExp.ra,       	         8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	 8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	 8, %s,     Object
+FIELD rawExp.filter,         	10, %s,     FILTER
+FIELD rawExp.exp_time,       	 5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	 5, %.4f,   airmass     
+FIELD rawExp.bg,             	 5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	 5, %.2f,   stdev    
+FIELD rawExp.comment,  	        65, %s,     Comment
+
+# FIELD rawExp.bg_mean_stdev,  	 5, %7.2f,  &lt;backgnd&gt;
+# FIELD chipPendingExp.reduction,    20,   reduction
+# FIELD chipPendingExp.guide_id,     20, guide ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeSummary.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeSummary.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/fakeSummary.d	(revision 22322)
@@ -0,0 +1,16 @@
+TABLE fakeRun
+TITLE Fake Summary
+FILE  fakeSummary.php
+MENU  ipp.science.dat
+
+#     field           size  format  name         show    link to         extras
+FIELD label,    	 7, %s,     label
+FIELD workdir,    	 7, %s,     workdir
+FIELD tess_id,    	 7, %s,     tess_id
+FIELD state,    	 7, %s,     state
+FIELD count(state) as n, 7, %d,     count
+
+MODE  summary
+GROUP state
+GROUP label
+GROUP workdir
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/flatcorrExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/flatcorrExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/flatcorrExp.d	(revision 22322)
@@ -0,0 +1,8 @@
+TABLE flatcorrExp
+TITLE flatcorrExp
+FILE  flatcorrExp.php
+MENU  ipp.detrend.dat
+
+#        field        size  format  name           show     link to         extras
+FIELD    corr_id,	20, %s,     correction ID
+FIELD    chip_id,	20, %s,     chip ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/flatcorrRun.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/flatcorrRun.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/flatcorrRun.d	(revision 22322)
@@ -0,0 +1,13 @@
+TABLE flatcorrRun
+TITLE flatcorrRun
+FILE  flatcorrRun.php
+MENU  ipp.detrend.dat
+
+#        field        size  format  name           show     link to         extras
+FIELD    corr_id,	20, %s,     correction ID
+FIELD    dvodb,		20, %s,     dvodb
+FIELD    filter,	20, %s,     filter
+FIELD    state,	        20, %s,     state
+FIELD    workdir,       20, %s,     workdir
+FIELD    label,	        20, %s,     label
+FIELD    stats,	        20, %s,     stats
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/guidePendingExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/guidePendingExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/guidePendingExp.d	(revision 22322)
@@ -0,0 +1,12 @@
+TABLE guidePendingExp
+TITLE guidePendingExp
+FILE  guidePendingExp.php
+MENU  ipp.science.dat
+
+ARGS  ARG1  exp_id=$exp_id
+
+#     field          size format  name       show     link to         extras
+FIELD exp_id,        5,   %s,     Exp ID,    value,   rawImfile.php,  ARG1
+FIELD exp_name,      5,   %s,     Exp Name,  value,   rawImfile.php,  ARG1
+FIELD recipe,	     5,   %s,     recipe
+FIELD guide_version, 5,   %s,     guide version
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/keptDetrendFrames.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/keptDetrendFrames.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/keptDetrendFrames.d	(revision 22322)
@@ -0,0 +1,42 @@
+# this file is based on the detRunSummary.d file: it uses the same tables restricted to the 
+# master detrend runs which have been accepted
+
+TABLE detRunSummary, detRun
+TITLE Kept Detrend Frames
+FILE  masterDetrendFrames.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE detRunSummary.accept = 1
+WHERE detRun.state = 'stop'
+WHERE detRun.det_id = detRunSummary.det_id
+WHERE detRun.iteration = detRunSummary.iteration
+
+ARGS  ARG1 detRunSummary.det_id=$detRunSummary.det_id
+
+ARGS  ARG2 detInputExp.det_id=$detRunSummary.det_id
+ARGS  ARG2 detInputExp.iteration=$detRunSummary.iteration
+
+ARGS  ARG3 detProcessedExp.det_id=$detRunSummary.det_id
+
+ARGS  ARG4 detResidExp.det_id=$detRunSummary.det_id
+ARGS  ARG4 detResidExp.iteration=$detRunSummary.iteration
+
+#     field                    width format  name          show         link to                   extras
+FIELD detRunSummary.det_id,    5,    %s,     det ID,       value,       detRunSummary.php,        ARG1
+FIELD detRunSummary.iteration, 3,    %s,     iter
+FIELD detRun.det_type,         5,    %s,     type
+FIELD detRun.state,            5,    %s,     state
+FIELD *,                       20,   %s,     choose,       value=input, detInputExp.php,          ARG2
+FIELD *,                       20,   %s,     choose,       value=proc,  detProcessedExp.php,      ARG3
+FIELD *,                       20,   %s,     choose,       value=resid, detResidExp.php,          ARG4
+FIELD detRunSummary.bg,        20,   %s,     bg
+FIELD detRunSummary.bg_stdev,  20,   %s,     bg_stdev
+# FIELD bg_mean_stdev, 20,   bg_mean_stdev
+
+# this file illustrates a simple inner join to generate the output table
+# assuming no other dynamic restrictions are added, this generates the following query:
+# SELECT detRunSummary.det_id,detRunSummary.iteration,detRunSummary.iteration,detRun.det_type,detRunSummary.bg,detRunSummary.bg_stdev \
+# FROM detRunSummary, detRun \
+# WHERE detRunSummary.accept = 1 AND detRun.det_id = detRunSummary.det_id AND detRun.iteration = detRunSummary.iteration \
+# LIMIT 20
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/masterDetrendFrames.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/masterDetrendFrames.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/masterDetrendFrames.d	(revision 22322)
@@ -0,0 +1,58 @@
+# this file is based on the detRunSummary.d file: it uses the same tables restricted to the 
+# master detrend runs which have been accepted
+
+TABLE detRun LEFT JOIN detRunSummary ON detRun.det_id = detRunSummary.det_id
+TITLE Master Detrend Frames
+FILE  masterDetrendFrames.php
+MENU  ipp.detrend.dat
+
+# the following WHERE clauses are added to all queries joined by AND
+WHERE (detRunSummary.accept = 1 or detRun.mode = 'register')
+WHERE detRun.state = 'stop'
+
+ARGS  ARG1 detRunSummary.det_id=$detRunSummary.det_id
+
+ARGS  ARG2 detInputExp.det_id=$detRunSummary.det_id
+ARGS  ARG2 detInputExp.iteration=$detRunSummary.iteration
+
+ARGS  ARG3 detProcessedExp.det_id=$detRunSummary.det_id
+
+ARGS  ARG4 detResidExp.det_id=$detRunSummary.det_id
+ARGS  ARG4 detResidExp.iteration=$detRunSummary.iteration
+
+ARGS  ARG5 det_id=$det_id
+
+#     field                    width format  name          show         link to                   extras
+FIELD detRun.det_id,           5,    %s,     det ID,       value,       detRunSummary.php,        ARG1
+FIELD detRunSummary.iteration, 3,    %s,     iter
+FIELD detRun.det_type,        12,    %s,     type
+FIELD detRun.state,            5,    %s,     state
+# FIELD detRun.mode,             5,    %s,     mode
+# FIELD detRunSummary.accept,    5,    %s,     accept
+FIELD *,                       5,    %s,     choose,       value=input, detInputExp.php,          ARG2
+FIELD *,                       5,    %s,     choose,       value=proc,  detProcessedExp.php,      ARG3
+FIELD *,                       5,    %s,     choose,       value=stack, detStackedImfile.php,  ARG5
+FIELD *,                       5,    %s,     choose,       value=resid, detResidExp.php,          ARG4
+FIELD mode,         	       5,    %s,     mode
+FIELD filter,       	      10,    %s,     filter     
+FIELD registered,             19,   %s,      registered 
+FIELD use_begin,              19,   %s,      use_begin  
+FIELD use_end,                19,   %s,      use_end    
+FIELD detRunSummary.bg,        8,   %.2f,    bg
+FIELD detRunSummary.bg_stdev,  8,   %.2f,    bg_stdev
+# FIELD airmass_min,             5,   %.2f,    min_airmass    
+# FIELD airmass_max,             5,   %.2f,    max     
+# FIELD exp_time_min,            5,   %.2f,    min_exp_time   
+# FIELD exp_time_max,            5,   %.2f,    max 
+# FIELD ccd_temp_min,            5,   %.2f,    min_ccd_temp   
+# FIELD ccd_temp_max,            5,   %.2f,    max    
+# FIELD posang_min,              5,   %.2f,    min_posang     
+# FIELD posang_max,              5,   %.2f,    max      
+# FIELD bg_mean_stdev, 20,   bg_mean_stdev
+
+# this file illustrates a simple inner join to generate the output table
+# assuming no other dynamic restrictions are added, this generates the following query:
+# SELECT detRunSummary.det_id,detRunSummary.iteration,detRunSummary.iteration,detRun.det_type,detRunSummary.bg,detRunSummary.bg_stdev \
+# FROM detRunSummary, detRun \
+# WHERE detRunSummary.accept = 1 AND detRun.det_id = detRunSummary.det_id AND detRun.iteration = detRunSummary.iteration \
+# LIMIT 20
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/masterDetrendImfiles.d.src
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/masterDetrendImfiles.d.src	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/masterDetrendImfiles.d.src	(revision 22322)
@@ -0,0 +1,19 @@
+# this file is based on the detRunSummary.d file: it uses the same tables restricted to the 
+# master detrend runs which have been accepted
+
+TABLE detStackedImfile
+TITLE Master Detrend Imfiles
+FILE  masterDetrendImfiles.php
+MENU  ipp.detrend.dat
+
+#     field      width name          show         link to            extras
+FIELD det_id,    7,    det_id
+FIELD iteration, 5,    iter
+FIELD uri,       5,    uri
+
+# this file illustrates a simple inner join to generate the output table
+# assuming no other dynamic restrictions are added, this generates the following query:
+# SELECT detRunSummary.det_id,detRunSummary.iteration,detRunSummary.iteration,detRun.det_type,detRunSummary.bg,detRunSummary.bg_stdev \
+# FROM detRunSummary, detRun \
+# WHERE detRunSummary.accept = 1 AND detRun.position = detRunSummary.det_id AND detRun.iteration = detRunSummary.iteration \
+# LIMIT 20
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/newExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/newExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/newExp.d	(revision 22322)
@@ -0,0 +1,13 @@
+TABLE newExp
+TITLE New Exposures
+FILE  newExp.php
+MENU  ipp.load.dat
+
+WHERE state != 'stop'
+
+#        field        size   format  name           show     link to         extras
+FIELD    exp_id,	5,   %d,     exposure ID
+FIELD    tmp_exp_name,	10,  %s,     exposure Name
+FIELD    tmp_camera,	5,   %s,     camera
+FIELD    tmp_telescope,	5,   %s,     telescope
+FIELD    state,	        5,   %s,     state
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/newImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/newImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/newImfile.d	(revision 22322)
@@ -0,0 +1,9 @@
+TABLE newImfile
+TITLE Raw Science Exposures
+FILE  newImfile.php
+MENU  ipp.load.dat
+
+#        field       size  format  name           show   link to         extras
+FIELD    exp_id,	5, %d,     exposure ID
+FIELD    tmp_class_id,	5, %s,     class ID
+FIELD    uri,		5, %s,     URI
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneExp.d	(revision 22322)
@@ -0,0 +1,17 @@
+TABLE pzDownloadExp, summitExp
+TITLE Downloaded Exposures
+FILE  pzDoneExp.php
+MENU  ipp.load.dat
+
+WHERE pzDownloadExp.exp_name  = summitExp.exp_name
+WHERE pzDownloadExp.camera    = summitExp.camera
+WHERE pzDownloadExp.telescope = summitExp.telescope
+WHERE pzDownloadExp.state     = 'stop'
+
+#        field                  size format  name           show     link to         extras
+FIELD    summitExp.exp_name,	5,   %s,     exposures ID
+FIELD    summitExp.camera, 	5,   %s,     camera
+FIELD    summitExp.telescope,	5,   %s,     telescope
+FIELD    summitExp.dateobs,    19,   %T,     date/time
+FIELD    summitExp.exp_type,    5,   %s,     type
+FIELD    summitExp.uri,         5,   %s,     URI
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneExp_failed.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneExp_failed.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneExp_failed.d	(revision 22322)
@@ -0,0 +1,20 @@
+TABLE pzDoneExp, summitExp, pzDoneImfile
+TITLE done exposures
+FILE  pzDoneExp.php
+MENU  ipp.load.dat
+
+WHERE pzDoneExp.exp_name  = summitExp.exp_name
+WHERE pzDoneExp.camera    = summitExp.camera
+WHERE pzDoneExp.telescope = summitExp.telescope
+WHERE pzDoneExp.exp_name  = pzDoneImfile.exp_name
+WHERE pzDoneExp.camera    = pzDoneImfile.camera
+WHERE pzDoneExp.telescope = pzDoneImfile.telescope
+WHERE pzDoneImfile.fault > 0
+
+#        field                  size format  name           show     link to         extras
+FIELD    summitExp.exp_name,	5,   %s,     exposures ID
+FIELD    summitExp.camera, 	5,   %s,     camera
+FIELD    summitExp.telescope,	5,   %s,     telescope
+FIELD    summitExp.dateobs,     5,   %T,     date/time
+FIELD    summitExp.exp_type,    5,   %s,     type
+FIELD    summitExp.uri,         5,   %s,     URI
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneImfile.d	(revision 22322)
@@ -0,0 +1,14 @@
+TABLE pzDownloadImfile
+TITLE done exposures
+FILE  pzDoneImfile.php
+MENU  ipp.load.dat
+
+WHERE pzDownloadImfile.fault     = 0
+
+#        field                       size 	format  name           show     link to         extras
+FIELD    pzDownloadImfile.exp_name,  5,   	%s,     exposures ID
+FIELD    pzDownloadImfile.camera,    5,   	%s,     camera
+FIELD    pzDownloadImfile.telescope, 5,   	%s,     telescope
+FIELD    pzDownloadImfile.class,     5,        	%s,     class
+FIELD    pzDownloadImfile.class_id,  5,        	%s,     class ID
+FIELD    pzDownloadImfile.uri,       5,        	%s,     filename
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneImfile_failed.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneImfile_failed.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzDoneImfile_failed.d	(revision 22322)
@@ -0,0 +1,14 @@
+TABLE pzDownloadImfile
+TITLE done exposures
+FILE  pzDoneImfile_failed.php
+MENU  ipp.load.dat
+
+WHERE pzDownloadImfile.fault     != 0
+
+#        field                       size 	format  name           show     link to         extras
+FIELD    pzDownloadImfile.exp_name,  5,   	%s,     exposures ID
+FIELD    pzDownloadImfile.camera,    5,   	%s,     camera
+FIELD    pzDownloadImfile.telescope, 5,   	%s,     telescope
+FIELD    pzDownloadImfile.class,     5,        	%s,     class
+FIELD    pzDownloadImfile.class_id,  5,        	%s,     class ID
+FIELD    pzDownloadImfile.uri,       5,        	%s,     filename
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzPendingExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzPendingExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzPendingExp.d	(revision 22322)
@@ -0,0 +1,17 @@
+TABLE pzDownloadExp, summitExp
+TITLE pending exposures
+FILE  pzPendingExp.php
+MENU  ipp.load.dat
+
+WHERE pzDownloadExp.exp_name  = summitExp.exp_name
+WHERE pzDownloadExp.camera    = summitExp.camera
+WHERE pzDownloadExp.telescope = summitExp.telescope
+WHERE pzDownloadExp.state     = 'run'
+
+#        field                  size format  name           show     link to         extras
+FIELD    summitExp.exp_name,	5,   %s,     exposures ID
+FIELD    summitExp.camera, 	5,   %s,     camera
+FIELD    summitExp.telescope,	5,   %s,     telescope
+FIELD    summitExp.dateobs,     5,   %T,     date/time
+FIELD    summitExp.exp_type,    5,   %s,     type
+FIELD    summitExp.uri,         5,   %s,     URI
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzPendingImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzPendingImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/pzPendingImfile.d	(revision 22322)
@@ -0,0 +1,16 @@
+TABLE summitImfile, pzDownloadExp
+TITLE pending imfiles
+FILE  pzPendingImfile.php
+MENU  ipp.load.dat
+
+WHERE summitImfile.exp_name  = pzDownloadExp.exp_name 
+WHERE summitImfile.camera    = pzDownloadExp.camera   
+WHERE summitImfile.telescope = pzDownloadExp.telescope
+WHERE pzDownloadExp.state     = 'run'
+
+#        field                    size  format  name             show   link to         extras
+FIELD    summitImfile.exp_name,    5, %s,      Exp Name
+FIELD    summitImfile.camera,      5, %s,      camera
+FIELD    summitImfile.telescope,   5, %s,      telescope
+FIELD    summitImfile.class,	   5, %s,      class
+FIELD    summitImfile.class_id,    5, %s,      class ID 
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawDetrendExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawDetrendExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawDetrendExp.d	(revision 22322)
@@ -0,0 +1,25 @@
+TABLE rawExp
+TITLE Raw Detrend Exposures
+FILE  rawDetrendExp.php
+MENU  ipp.load.dat
+
+# limit this table to non-OBJECT types of images
+WHERE rawExp.exp_type != 'OBJECT'
+
+ARGS ARG1 exp_id=$exp_id
+
+#        field        	size  format  name       show     link to         extras
+FIELD    exp_id,      	 5,   %d,     Exp ID
+FIELD    exp_name,    	 8,   %s,     Exp Name,  value,   rawImfile.php,  ARG1
+FIELD    telescope,   	 8,   %s,     Telescope
+FIELD    camera,      	 8,   %s,     Camera
+FIELD    exp_type,    	 8,   %s,     Type    
+FIELD    dateobs,     	19,   %T,     Date/Time
+FIELD    filter,      	10,   %s,     FILTER
+FIELD    airmass,      	 5,   %.2f,   airmass     
+FIELD    exp_time,    	 5,   %.2f,    exp_time    
+FIELD    ccd_temp,       5,   %.2f,   ccd_temp
+FIELD    sat_pixel_frac, 5,   %.4f,   f(sat pixels)
+FIELD    bg,           	 5,   %.2f,   backgnd
+FIELD    bg_stdev,     	 5,   %.2f,   stdev    
+FIELD    bg_mean_stdev,  5,   %.2f,   &lt;backgnd&gt;
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawDetrendExp_detrend.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawDetrendExp_detrend.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawDetrendExp_detrend.d	(revision 22322)
@@ -0,0 +1,24 @@
+TABLE rawExp
+TITLE Raw Detrend Exposures
+FILE  rawDetrendExp_detrend.php
+MENU  ipp.detrend.dat
+
+# limit this table to non-OBJECT types of images
+WHERE rawExp.exp_type != 'OBJECT'
+
+ARGS ARG1 exp_id=$exp_id
+
+#        field        	size  format  name           show     link to         extras
+FIELD    exp_id,      	 5,   %d,     Exp ID
+FIELD    exp_name,    	10,   %s,     Exp Name,     value,   rawImfile.php,  ARG1
+FIELD    telescope,   	10,   %s,     Telescope
+FIELD    camera,      	10,   %s,     Camera
+FIELD    exp_type,    	10,   %s,     Type    
+FIELD    filter,      	10,   %s,     FILTER
+FIELD    dateobs,     	19,   %T,     Date/Time
+FIELD    exp_time,    	 5,   %.2f,   exp_time    
+FIELD    sat_pixel_frac, 5,   %.2f,   f(sat pixels)
+FIELD    airmass,      	 5,   %.4f,   airmass     
+FIELD    bg,           	 5,   %.2f,   backgnd
+FIELD    bg_stdev,     	 5,   %.2f,   stdev    
+FIELD    bg_mean_stdev,  5,   %.2f,   &lt;backgnd&gt;
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawExp.d	(revision 22322)
@@ -0,0 +1,27 @@
+TABLE rawExp
+TITLE Raw Exposures
+FILE  rawExp.php
+MENU  ipp.load.dat
+
+ARGS ARG1 exp_id=$exp_id
+
+OP   OP1  $ra * 57.295783
+OP   OP2  $decl * 57.295783
+
+#        field        size format  name           show          link to   extras
+FIELD    exp_name,    10,  %s,     Exp Name
+FIELD    exp_id,       5,  %d,     Exp ID,        value,   rawImfile.php,  ARG1
+FIELD    telescope,   10,  %s,     Telescope
+FIELD    camera,      10,  %s,     Camera
+FIELD    exp_type,     8,  %s,     Type    
+FIELD    dateobs,     19,  %T,     Date/Time
+FIELD    ra,           8,  %10.6f, RA,           op=OP1      
+FIELD    decl,         8,  %10.6f, DEC,          op=OP2
+FIELD    object,      10,  %s,     object
+FIELD    filter,      10,  %s,     FILTER
+FIELD    airmass,      5,  %.4f,   airmass     
+FIELD    exp_time,     5,  %.2f,   exp_time    
+FIELD    ccd_temp,     5,  %.2f,   ccd_temp
+FIELD    bg,	       8,  %.2f,   backgnd
+FIELD    bg_stdev,     8,  %.2f,   stdev
+FIELD    bg_mean_stdev, 8, %.2f,   [stdev]
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawExp_failed.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawExp_failed.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawExp_failed.d	(revision 22322)
@@ -0,0 +1,38 @@
+TABLE rawExp
+TITLE Failed Raw Exposures
+FILE  rawExp_failed.php
+MENU  ipp.load.dat
+
+# limit this table to non-OBJECT types of images
+WHERE fault > 0
+WHERE rawExp.exp_type = 'OBJECT'
+
+ARGS ARG1 exp_id=$exp_id
+
+OP   OP1  $ra * 57.295783
+OP   OP2  $decl * 57.295783
+
+#        field        size  format  name       show     link to         extras
+FIELD    exp_id,      5,    %s,     Exp ID
+FIELD    exp_name,    10,   %s,     Exp Name,  value,   rawImfile.php,  ARG1
+FIELD    telescope,   10,   %s,     Telescope
+FIELD    camera,      10,   %s,     Camera
+FIELD    ra,           8,  %10.6f, RA,           op=OP1      
+FIELD    decl,         8,  %10.6f, DEC,          op=OP2
+FIELD    filter,      10,   %s,     FILTER
+FIELD    dateobs,     19,   %T,     Date/Time
+FIELD    exp_time,     5,   %s,     exp_time    
+FIELD    airmass,      5,   %s,     airmass     
+FIELD    bg,           5,   %s,     backgnd
+FIELD    bg_stdev,     5,   %s,     stdev    
+FIELD    bg_mean_stdev, 5,  %s,     &lt;backgnd&gt;
+# FIELD  ra,           8,   %s,     ra          
+# FIELD  decl,         8,   %s,     decl        
+# FIELD  object,      10,   %s,     object
+# FIELD  imfiles,     20,   %s,     imfiles     
+# FIELD  airmass,     20,   %s,     airmass     
+# FIELD  bg_mean_stdev, 20, %s,     bg_mean_stdev   
+# FIELD  alt,         20,   %s,     alt         
+# FIELD  az,	      20,   %s,     az          
+# FIELD  ccd_temp,    20,   %s,     ccd_temp    
+# FIELD  posang,      20,   %s,     posang      
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawImfile.d	(revision 22322)
@@ -0,0 +1,34 @@
+TABLE rawImfile
+TITLE Raw Image File
+FILE  rawImfile.php
+MENU  ipp.imfiles.dat
+
+# HEAD PHP insert_backref ("rawExp.php", "exp_id", $ID['link']);
+
+OP   OP1  $ra * 57.295783
+OP   OP2  $decl * 57.295783
+
+#        field                 size  format   name          show         link to         extras
+FIELD    rawImfile.exp_name,      5, %s,      Exposure
+FIELD    rawImfile.class_id,      8, %s,      Chip ID
+FIELD    rawImfile.exp_id,        5, %d,      Exp ID
+FIELD    rawImfile.telescope,    10, %s,      Telescope
+FIELD    rawImfile.camera,       10, %s,      Camera
+FIELD    rawImfile.exp_type,      8, %s,      exp_type
+FIELD    rawImfile.dateobs,      19, %T,      Date/Time
+FIELD    rawImfile.ra,            8, %10.6f,  RA,           op=OP1      
+FIELD    rawImfile.decl,          8, %10.6f,  DEC,          op=OP2
+FIELD    rawImfile.object,        8, %s,      object
+FIELD    rawImfile.filter,       10, %s,      FILTER
+FIELD    rawImfile.airmass,       5, %.4f,    airmass     
+FIELD    rawImfile.exp_time,      5, %.2f,    exp_time    
+FIELD    rawImfile.ccd_temp,      5, %.2f,    ccd_temp
+FIELD    rawImfile.bg,            8, %.2f,    backgnd
+FIELD    rawImfile.bg_stdev,      8, %.2f,    stdev
+FIELD    rawImfile.bg_mean_stdev, 8, %.2f,    [stdev]
+FIELD    rawImfile.uri,          10, %s,      uri
+
+# FIELD    alt,       20,   alt
+# FIELD    az,        20,   az
+# FIELD    posang,    20,   posang
+# FIELD    object,    20,   object
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawImfile_failed.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawImfile_failed.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawImfile_failed.d	(revision 22322)
@@ -0,0 +1,34 @@
+TABLE rawImfile
+TITLE Failed Raw Image Files
+FILE  rawImfile.php
+MENU  ipp.load.dat
+
+HEAD PHP insert_backref ("rawExp.php", "exp_id", $ID['link']);
+WHERE fault > 0
+
+OP   OP1  $ra * 57.295783
+OP   OP2  $decl * 57.295783
+
+#        field       size  format  name    show         link to         extras
+FIELD    exp_id,        5, %s,        Exp ID
+FIELD    exp_name,      5, %s,        Exposure
+FIELD    class_id,      8, %s,        Chip ID
+FIELD    ra,           8,  %10.6f, RA,           op=OP1      
+FIELD    decl,         8,  %10.6f, DEC,          op=OP2
+FIELD    bg,	        8, %s,        backgnd
+FIELD    bg_stdev,      8, %s,        stdev
+FIELD    bg_mean_stdev, 8, %s,        [stdev]
+FIELD    uri,	       10, %s,        uri
+# FIELD    class,      20, %s,        Class
+# FIELD    uri,	      20,   uri
+# FIELD    exp_type,  20,   exp_type
+# FIELD    filter,    20,   filter
+# FIELD    airmass,   20,   airmass
+# FIELD    ra,	      20,   ra
+# FIELD    decl,      20,   decl
+# FIELD    exp_time,  20,   exp_time
+# FIELD    alt,       20,   alt
+# FIELD    az,	      20,   az
+# FIELD    ccd_temp,  20,   ccd_temp
+# FIELD    posang,    20,   posang
+# FIELD    object,    20,   object
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawScienceExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawScienceExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawScienceExp.d	(revision 22322)
@@ -0,0 +1,31 @@
+TABLE rawExp
+TITLE Raw Science Exposures
+FILE  rawScienceExp.php
+MENU  ipp.load.dat
+
+# limit this table to OBJECT types of images
+WHERE rawExp.exp_type = 'OBJECT'
+
+ARGS ARG1 exp_id=$exp_id
+
+OP   OP1  $ra * 57.295783
+OP   OP2  $decl * 57.295783
+
+#        field        size format  name           show          link to   extras
+FIELD    exp_id,       5,  %d,     Exp ID
+FIELD    exp_name,     8,  %s,     Exp Name,     value,   rawImfile.php,  ARG1
+FIELD    telescope,    8,  %s,     Telescope
+FIELD    camera,       8,  %s,     Camera
+FIELD    dateobs,     19,  %T,     Date/Time
+FIELD    ra,           8,  %10.6f, RA,           op=OP1      
+FIELD    decl,         8,  %10.6f, DEC,          op=OP2
+FIELD    object,       8,  %s,     object
+FIELD    filter,      10,  %s,     FILTER
+FIELD    exp_time,     5,  %s,     exp_time    
+FIELD    airmass,      5,  %.4f,   airmass     
+FIELD    exp_time,     5,  %.2f,   exp_time    
+FIELD    ccd_temp,     5,  %.2f,   ccd_temp
+FIELD    bg,	       8,  %.2f,   backgnd
+FIELD    bg_stdev,     8,  %.2f,   stdev
+FIELD    bg_mean_stdev, 8, %.2f,   [stdev]
+FIELD    comment,      65,  %s,     Comment
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawUnknownExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawUnknownExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/rawUnknownExp.d	(revision 22322)
@@ -0,0 +1,38 @@
+TABLE rawExp
+TITLE Raw Unknown Exposures
+FILE  rawUnknownExp.php
+MENU  ipp.load.dat
+
+# limit this table to non-OBJECT types of images
+WHERE rawExp.exp_type is NULL
+
+ARGS ARG1 exp_id=$exp_id
+
+OP   OP1  $ra * 57.295783
+OP   OP2  $decl * 57.295783
+
+#        field        size  format  name           show     link to        extras
+FIELD    exp_id,       5,   %s,     Exp ID
+FIELD    exp_name,    10,   %s,     Exp Name,     value,   rawImfile.php,  ARG1
+FIELD    exp_type,     8,   %s,     Type    
+FIELD    telescope,   10,   %s,     Telescope
+FIELD    camera,      10,   %s,     Camera
+FIELD    dateobs,     19,   %T,     Date/Time
+FIELD    ra,           8,  %10.6f, RA,           op=OP1      
+FIELD    decl,         8,  %10.6f, DEC,          op=OP2
+FIELD    object,       8,   %s,     object
+FIELD    filter,      10,   %s,     FILTER
+FIELD    exp_time,     5,   %s,     exp_time    
+FIELD    airmass,      5,   %s,     airmass     
+FIELD    bg,           5,   %s,     backgnd
+FIELD    bg_stdev,     5,   %s,     stdev    
+# FIELD  bg_mean_stdev, 5,  %s,     &lt;backgnd&gt;
+# FIELD  ra,           8,   %s,     ra          
+# FIELD  decl,         8,   %s,     decl        
+# FIELD  imfiles,     20,   %s,     imfiles     
+# FIELD  airmass,     20,   %s,     airmass     
+# FIELD  bg_mean_stdev, 20, %s,     bg_mean_stdev   
+# FIELD  alt,         20,   %s,     alt         
+# FIELD  az,	      20,   %s,     az          
+# FIELD  ccd_temp,    20,   %s,     ccd_temp    
+# FIELD  posang,      20,   %s,     posang      
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackFailedSkyfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackFailedSkyfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackFailedSkyfile.d	(revision 22322)
@@ -0,0 +1,25 @@
+TABLE stackRun, stackSumSkyfile
+TITLE Stack Failed Skyfiles
+FILE  stackFailedSkyfile.php
+MENU  ipp.stack.dat
+
+WHERE stackRun.stack_id = stackSumSkyfile.stack_id
+WHERE stackSumSkyfile.fault != 0
+
+ARGS  ARG7 stackRun.stack_id=$stackRun.stack_id
+ARGS  ARG7 stackRun.skycell_id=$stackRun.skycell_id
+# XXX need to fix this: there may not be a single camera per stack...
+ARGS  ARG7 camera=GPC1
+ARGS  ARG7 basename=$stackSumSkyfile.path_base
+
+#        field                          size  format  name    show     link to         extras
+FIELD stackRun.stack_id,            5, %s,     Stack ID
+FIELD stackRun.skycell_id,          5, %s,     Skycell ID
+FIELD stackRun.state,    	    7, %s,     State,         value,   stackFailedSkyfile.php, ARG7
+FIELD stackSumSkyfile.bg,           5, %.2f,   backgnd
+FIELD stackSumSkyfile.bg_stdev,     5, %.2f,   stdev    
+FIELD stackSumSkyfile.dtime_stack,  5, %.2f,   dtime
+
+FIELD stackSumSkyfile.path_base,    5, %s,     path_base,     none
+
+TAIL PHP insert_log ('LOG.EXP');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackInputSkyfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackInputSkyfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackInputSkyfile.d	(revision 22322)
@@ -0,0 +1,49 @@
+TABLE stackInputSkyfile, stackRun, warpRun, warpSkyfile, fakeRun, camRun, camProcessedExp, chipRun, rawExp
+TITLE Stack Input Exposures
+FILE  stackInputSkyfile.php
+MENU  ipp.stack.dat
+
+WHERE stackRun.stack_id = stackInputSkyfile.stack_id
+WHERE stackInputSkyfile.warp_id = warpRun.warp_id
+WHERE warpRun.fake_id = fakeRun.fake_id
+WHERE warpSkyfile.warp_id = warpRun.warp_id
+WHERE warpSkyfile.skycell_id = stackRun.skycell_id
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camProcessedExp.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+ARGS  ARG5  warpRun.warp_id=$warpRun.warp_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#        field                          size  format  name           show     link to         extras
+FIELD rawExp.exp_name,     	    5, %s,     Exp Name
+FIELD rawExp.exp_id,     	    5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	    7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,    	    7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,    	    7, %d,     Fake ID,      value,  fakeProcessedImfile.php, ARG4
+FIELD warpRun.warp_id,    	    7, %d,     Warp ID,      value,  warpStageExp.php,        ARG5
+FIELD stackInputSkyfile.stack_id,   5, %s,     Stack ID
+FIELD stackRun.skycell_id,          5, %s,     Skycell ID
+FIELD stackRun.state,    	    7, %s,     State
+FIELD rawExp.telescope,      	   10, %s,     Telescope
+FIELD rawExp.camera,         	   10, %s,     Camera
+FIELD rawExp.dateobs,        	   19, %T,     Date/Time
+FIELD rawExp.ra,       	            8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	    8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	    8, %s,     Object
+FIELD rawExp.filter,         	   10, %s,     Filter
+FIELD rawExp.exp_time,       	    5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	    5, %.4f,   airmass     
+FIELD camProcessedExp.fwhm_major,   5, %.4f,   FWHM     
+FIELD warpSkyfile.good_frac,   	    7, %.4f,   warp good f
+FIELD rawExp.bg,             	    5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	    5, %.2f,   stdev    
+FIELD rawExp.comment,  	           65, %s,     Comment
+
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackProcessedSkyfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackProcessedSkyfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackProcessedSkyfile.d	(revision 22322)
@@ -0,0 +1,26 @@
+TABLE stackRun, stackSumSkyfile
+TITLE Stack Processed Skyfiles
+FILE  stackProcessedSkyfile.php
+MENU  ipp.stack.dat
+
+WHERE stackRun.stack_id = stackSumSkyfile.stack_id
+WHERE stackRun.state = 'full'
+WHERE stackSumSkyfile.fault = 0
+
+ARGS  ARG7 stackRun.stack_id=$stackRun.stack_id
+ARGS  ARG7 stackRun.skycell_id=$stackRun.skycell_id
+# XXX need to fix this: there may not be a single camera per stack...
+ARGS  ARG7 camera=GPC1
+ARGS  ARG7 basename=$stackSumSkyfile.path_base
+
+#        field                   size  format  name           show     link to         extras
+FIELD stackRun.stack_id,            5, %s,     Stack ID
+FIELD stackRun.skycell_id,          5, %s,     Skycell ID
+FIELD stackRun.state,    	    7, %s,     State,         value,   stackProcessedSkyfile.php, ARG7
+FIELD stackSumSkyfile.bg,           5, %.2f,   backgnd
+FIELD stackSumSkyfile.bg_stdev,     5, %.2f,   stdev    
+FIELD stackSumSkyfile.dtime_stack,  5, %.2f,   dtime
+
+FIELD stackSumSkyfile.path_base,    5, %s,     path_base,     none
+
+TAIL PHP insert_log ('LOG.EXP');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackRun.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackRun.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackRun.d	(revision 22322)
@@ -0,0 +1,16 @@
+TABLE stackRun
+TITLE stackRun
+FILE  stackRun.php
+MENU  ipp.stack.dat
+
+#        field          size  format  name           show     link to         extras
+FIELD    stack_id,	 5,   %s,     Stack ID
+FIELD    skycell_id,	 5,   %s,     Skycell ID
+FIELD    filter,	 5,   %s,     Filter
+FIELD    label,	 	 5,   %s,     Label
+FIELD    tess_id,	 5,   %s,     Tessellation ID
+FIELD    state,		 5,   %s,     State
+
+#FIELD    registered,	 5,   %s,     time registered
+#FIELD    workdir,	10,   %s,     workdir
+#FIELD    dvodb,		10,   %s,     DVO database
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackSumSkyfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackSumSkyfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/stackSumSkyfile.d	(revision 22322)
@@ -0,0 +1,10 @@
+TABLE stackSumSkyfile
+TITLE stackSumSkyfile
+FILE  stackSumSkyfile.php
+MENU  ipp.stack.dat
+
+#        field           size format  name           show     link to         extras
+FIELD    stack_id,	 5,   %s,     stack ID
+FIELD    URI,		 5,   %s,     URI
+FIELD    bg,		 5,   %s,     background
+FIELD    bg_stdev,	 5,   %s,     standard deviation
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/summitExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/summitExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/summitExp.d	(revision 22322)
@@ -0,0 +1,12 @@
+TABLE summitExp
+TITLE Pending Summit Exposures
+FILE  summitExp.php
+MENU  ipp.load.dat
+
+#        field        size, format  name           show    link to         extras
+FIELD    exp_name,    5,    %s,     exposure ID
+FIELD    camera,      5,    %s,     camera
+FIELD    telescope,   5,    %s,     telescope
+FIELD    dateobs,    19,    %T,     date/time
+FIELD    exp_type,    5,    %s,     type
+FIELD    uri,         5,    %s,     URI
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/summitImfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/summitImfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/summitImfile.d	(revision 22322)
@@ -0,0 +1,13 @@
+TABLE summitImfile
+TITLE Summit Image Files
+FILE  summitImfile.php
+MENU  ipp.load.dat
+
+#        field     size  format  name             show   link to         extras
+FIELD    exp_name,    5, %s,      Exp Name
+FIELD    camera,      5, %s,      camera
+FIELD    telescope,   5, %s,      telescope
+FIELD    file_id,     5, %s,      file_id
+FIELD    class,	      5, %s,      class
+FIELD    class_id,    5, %s,      class ID 
+FIELD    uri,         5, %s,      URI 
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpFailedSkyfiles.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpFailedSkyfiles.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpFailedSkyfiles.d	(revision 22322)
@@ -0,0 +1,57 @@
+TABLE warpSkyfile, warpRun, fakeRun, camRun, chipRun, rawExp
+TITLE Warp Failed Skyfiles
+FILE  warpFailedSkyfiles.php
+MENU  ipp.science.dat
+
+WHERE warpSkyfile.warp_id = warpRun.warp_id
+WHERE warpRun.fake_id = fakeRun.fake_id
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE warpSkyfile.fault != 0
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+ARGS  ARG5  warpRun.warp_id=$warpRun.warp_id
+ARGS  ARG6  warpSkyfile.skycell_id=$warpSkyfile.skycell_id
+
+ARGS  ARG7 warpRun.warp_id=$warpRun.warp_id
+ARGS  ARG7 warpSkyfile.skycell_id=$warpSkyfile.skycell_id
+ARGS  ARG7 camera=$rawExp.camera
+ARGS  ARG7 basename=$warpSkyfile.path_base
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                    size  format  name         show    link to         extras
+FIELD rawExp.exp_name,     	  5, %s,     Exp Name
+FIELD rawExp.exp_id,     	  5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	  7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,    	  7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,    	  7, %d,     Fake ID,      value,  fakeProcessedImfile.php, ARG4
+FIELD warpRun.warp_id,    	  7, %d,     Warp ID,      value,  warpStageExp.php,        ARG5
+FIELD warpSkyfile.skycell_id, 	 10, %s,     skycell,      value,  warpFailedSkyfiles.php,  ARG6
+FIELD warpRun.state,    	  7, %s,     state,        value,  warpFailedSkyfiles.php,  ARG7
+FIELD warpRun.label,    	  7, %s,     label
+FIELD warpSkyfile.tess_id,    	 10, %s,     tess
+FIELD warpSkyfile.good_frac,  	  5, %s,     good_frac
+FIELD warpSkyfile.ignored,     	  5, %s,     ignored
+FIELD warpSkyfile.fault,      	  5, %s,     fault
+FIELD rawExp.telescope,      	 10, %s,     Telescope
+FIELD rawExp.camera,         	 10, %s,     Camera
+FIELD rawExp.dateobs,        	 19, %T,     Date/Time
+FIELD rawExp.ra,       	          8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	  8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	  8, %s,     Object
+FIELD rawExp.filter,         	 10, %s,     FILTER
+FIELD rawExp.exp_time,       	  5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	  5, %.4f,   airmass     
+FIELD rawExp.bg,             	  5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	  5, %.2f,   stdev    
+FIELD rawExp.comment,  	         65, %s,     Comment
+
+FIELD warpSkyfile.path_base,      5, %s,     path_base,   none
+
+TAIL PHP insert_log ('LOG.EXP');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpProcessedSkyfiles.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpProcessedSkyfiles.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpProcessedSkyfiles.d	(revision 22322)
@@ -0,0 +1,60 @@
+TABLE warpSkyfile, warpRun, fakeRun, camRun, chipRun, rawExp
+TITLE Warp Processed Skyfiles
+FILE  warpProcessedSkyfiles.php
+MENU  ipp.science.dat
+
+WHERE warpSkyfile.warp_id = warpRun.warp_id
+WHERE warpRun.fake_id = fakeRun.fake_id
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE warpSkyfile.fault = 0
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+ARGS  ARG5  warpRun.warp_id=$warpRun.warp_id
+ARGS  ARG6  warpSkyfile.skycell_id=$warpSkyfile.skycell_id
+
+ARGS  ARG8  warpRun.warp_id=$warpRun.warp_id
+ARGS  ARG8  warpSkyCellMap.skycell_id=$warpSkyfile.skycell_id
+
+ARGS  ARG7 warpRun.warp_id=$warpRun.warp_id
+ARGS  ARG7 warpSkyfile.skycell_id=$warpSkyfile.skycell_id
+ARGS  ARG7 camera=$rawExp.camera
+ARGS  ARG7 basename=$warpSkyfile.path_base
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                    size  format  name         show    link to         extras
+FIELD rawExp.exp_name,     	  5, %s,     Exp Name,     value,  warpStageSkyfileInputs.php, ARG8
+FIELD rawExp.exp_id,     	  5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	  7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,    	  7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,    	  7, %d,     Fake ID,      value,  fakeProcessedImfile.php, ARG4
+FIELD warpRun.warp_id,    	  7, %d,     Warp ID,      value,  warpStageExp.php,        ARG5
+FIELD warpSkyfile.skycell_id, 	 10, %s,     skycell,      value,  warpProcessedSkyfiles.php, ARG6
+FIELD warpRun.state,    	  7, %s,     state,        value,  warpProcessedSkyfiles.php, ARG7
+FIELD warpRun.label,    	  7, %s,     label,        value
+FIELD warpSkyfile.tess_id,    	 10, %s,     tess
+FIELD warpSkyfile.good_frac,  	  5, %s,     good_frac
+FIELD warpSkyfile.ignored,     	  5, %s,     ignored
+FIELD warpSkyfile.fault,      	  5, %s,     fault
+FIELD rawExp.telescope,      	 10, %s,     Telescope
+FIELD rawExp.camera,         	 10, %s,     Camera
+FIELD rawExp.dateobs,        	 19, %T,     Date/Time
+FIELD rawExp.ra,       	          8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	  8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	  8, %s,     Object
+FIELD rawExp.filter,         	 10, %s,     FILTER
+FIELD rawExp.exp_time,       	  5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	  5, %.4f,   airmass     
+FIELD rawExp.bg,             	  5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	  5, %.2f,   stdev    
+FIELD rawExp.comment,  	         65, %s,     Comment
+
+FIELD warpSkyfile.path_base,      5, %s,     path_base,   none
+
+TAIL PHP insert_log ('LOG.EXP');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpSkycellMap.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpSkycellMap.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpSkycellMap.d	(revision 22322)
@@ -0,0 +1,35 @@
+TABLE warpSkyCellMap, warpRun, camProcessedExp, chipRun, rawExp
+TITLE warpSkyCellMap
+FILE  warpSkycellMap.php
+MENU  ipp.science.dat
+
+WHERE warpSkyCellMap.warp_id     = warpRun.warp_id
+WHERE warpSkyCellMap.cam_id      = camProcessedExp.cam_id
+WHERE camProcessedExp.chip_id    = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#        field                      size  format  name           show     link to         extras
+FIELD    chipRun.exp_id,  	      5,    %s,     Exp ID
+FIELD    rawExp.exp_name,             5,    %s,     Exp Name
+FIELD    chipRun.chip_id, 	      5,    %s,     Chip ID
+FIELD    camProcessedExp.cam_id,      5,    %s,     Cam ID
+FIELD    warpSkyCellMap.warp_id,      5,    %s,     Warp ID
+FIELD    warpRun.state,               5,    %s,     State
+FIELD    warpRun.label,               5,    %s,     Label
+FIELD    warpSkyCellMap.skycell_id,   5,    %s,     Skycell ID
+FIELD    warpSkyCellMap.tess_id,      5,    %s,     Tessellation ID
+FIELD    warpSkyCellMap.class_id,     5,    %s,     Class ID
+FIELD    rawExp.telescope,            10,   %s,     Telescope
+FIELD    rawExp.camera,               10,   %s,     Camera
+FIELD    rawExp.dateobs,              19,   %T,     Date/Time
+FIELD    rawExp.ra,                    8,   %10.6f, RA,           op=OP1      
+FIELD    rawExp.decl,                  8,   %10.6f, DEC,          op=OP2
+FIELD    rawExp.object,                8,   %s,     Object
+FIELD    rawExp.filter,               10,   %s,     Filter
+FIELD    rawExp.exp_time,              5,   %.2f,   exptime    
+FIELD    rawExp.airmass,               5,   %.4f,   airmass
+FIELD    rawExp.bg,                    5,   %.2f,   bg
+FIELD    rawExp.bg_stdev,              5,   %.2f,   bg_stdev
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpSkyfile.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpSkyfile.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpSkyfile.d	(revision 22322)
@@ -0,0 +1,12 @@
+TABLE warpSkyfile
+TITLE warpSkyfile
+FILE  warpSkyfile.php
+MENU  ipp.science.dat
+
+#        field          size  format  name       show     link to         extras
+FIELD    warp_id,	 5,   %s,     warp ID
+FIELD    skycell_id,	 5,   %s,     skycell ID
+FIELD    tess_id,	 5,   %s,     tessellation ID
+FIELD    uri,		10,   %s,     URI
+FIELD    bg,		 8,   %s,     background
+FIELD    bg_stdev,       8,   %s,     standard deviation
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpStageExp.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpStageExp.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpStageExp.d	(revision 22322)
@@ -0,0 +1,44 @@
+TABLE warpRun, fakeRun, camRun, chipRun, rawExp
+TITLE Warp Stage Exposures
+FILE  warpStageExp.php
+MENU  ipp.science.dat
+
+WHERE warpRun.fake_id = fakeRun.fake_id
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+ARGS  ARG5  warpRun.warp_id=$warpRun.warp_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                   size  format  name         show    link to         extras
+FIELD rawExp.exp_name,     	 5, %s,     Exp Name
+FIELD rawExp.exp_id,     	 5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	 7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,    	 7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,    	 7, %d,     Fake ID,      value,  fakeProcessedImfile.php, ARG4
+FIELD warpRun.warp_id,    	 7, %d,     Warp ID,      value,  warpStageExp.php,        ARG5
+FIELD warpRun.state,    	 7, %s,     state
+FIELD warpRun.label,    	 7, %s,     label
+FIELD rawExp.telescope,      	10, %s,     Telescope
+FIELD rawExp.camera,         	10, %s,     Camera
+FIELD rawExp.dateobs,        	19, %T,     Date/Time
+FIELD rawExp.ra,       	         8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	 8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	 8, %s,     Object
+FIELD rawExp.filter,         	10, %s,     FILTER
+FIELD rawExp.exp_time,       	 5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	 5, %.4f,   airmass     
+FIELD rawExp.bg,             	 5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	 5, %.2f,   stdev    
+FIELD rawExp.comment,  	        65, %s,     Comment
+
+# FIELD rawExp.bg_mean_stdev,  	 5, %7.2f,  &lt;backgnd&gt;
+# FIELD chipPendingExp.reduction,    20,   reduction
+# FIELD chipPendingExp.guide_id,     20, guide ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpStageSkyfileInputs.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpStageSkyfileInputs.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpStageSkyfileInputs.d	(revision 22322)
@@ -0,0 +1,59 @@
+TABLE warpSkyCellMap, warpRun, fakeRun, camRun, chipRun, rawExp, chipProcessedImfile
+TITLE Warp Stage Skyfile Inputs
+FILE  warpStageSkyfileInputs.php
+MENU  ipp.science.dat
+
+WHERE warpSkyCellMap.warp_id = warpRun.warp_id
+WHERE warpRun.fake_id = fakeRun.fake_id
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+WHERE chipProcessedImfile.chip_id = chipRun.chip_id
+WHERE chipProcessedImfile.class_id = warpSkyCellMap.class_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+ARGS  ARG5  warpRun.warp_id=$warpRun.warp_id
+
+ARGS  ARG6  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG6  chipProcessedImfile.class_id=$warpSkyCellMap.class_id
+ARGS  ARG6  camera=$rawExp.camera
+ARGS  ARG6  basename=$chipProcessedImfile.path_base
+ARGS  ARG6  class=$warpSkyCellMap.class_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                    size  format  name         show    link to         extras
+FIELD rawExp.exp_name,     	 10, %s,     Exp Name
+FIELD warpSkyCellMap.class_id,    8, %s,     Class ID,     value,  chipProcessedImfile.php, ARG6
+FIELD rawExp.exp_id,     	  5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	  7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,    	  7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,    	  7, %d,     Fake ID,      value,  fakeProcessedImfile.php, ARG4
+FIELD warpRun.warp_id,    	  7, %d,     Warp ID,      value,  warpStageExp.php, ARG5
+FIELD warpRun.state,    	  7, %s,     state
+FIELD warpRun.label,    	  7, %s,     label
+FIELD warpSkyCellMap.skycell_id, 10, %s,     skycell
+FIELD warpSkyCellMap.tess_id,    10, %s,     tess
+FIELD warpSkyCellMap.fault,      10, %s,     fault
+FIELD rawExp.telescope,      	 10, %s,     Telescope
+FIELD rawExp.camera,         	 10, %s,     Camera
+FIELD rawExp.dateobs,        	 19, %T,     Date/Time
+FIELD rawExp.ra,       	          8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	  8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	  8, %s,     Object
+FIELD rawExp.filter,         	 10, %s,     FILTER
+FIELD rawExp.exp_time,       	  5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	  5, %.4f,   airmass     
+FIELD rawExp.bg,             	  5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	  5, %.2f,   stdev    
+FIELD rawExp.comment,  	         65, %s,     Comment
+
+FIELD chipProcessedImfile.path_base, 65, %s, path_base, none
+
+# FIELD rawExp.bg_mean_stdev,  	 5, %7.2f,  &lt;backgnd&gt;
+# FIELD chipPendingExp.reduction,    20,   reduction
+# FIELD chipPendingExp.guide_id,     20, guide ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpStageSkyfiles.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpStageSkyfiles.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpStageSkyfiles.d	(revision 22322)
@@ -0,0 +1,49 @@
+TABLE warpSkyCellMap, warpRun, fakeRun, camRun, chipRun, rawExp
+TITLE Warp Stage Skyfiles
+FILE  warpStageSkyfiles.php
+MENU  ipp.science.dat
+
+WHERE warpSkyCellMap.warp_id = warpRun.warp_id
+WHERE warpRun.fake_id = fakeRun.fake_id
+WHERE fakeRun.cam_id = camRun.cam_id
+WHERE camRun.chip_id = chipRun.chip_id
+WHERE chipRun.exp_id = rawExp.exp_id
+
+ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
+ARGS  ARG2  chipRun.chip_id=$chipRun.chip_id
+ARGS  ARG3  camRun.cam_id=$camRun.cam_id
+ARGS  ARG4  fakeRun.fake_id=$fakeRun.fake_id
+ARGS  ARG5  warpRun.warp_id=$warpRun.warp_id
+ARGS  ARG6  warpSkyfile.skycell_id=$warpSkyCellMap.skycell_id
+
+OP   OP1  $rawExp.ra * 57.295783
+OP   OP2  $rawExp.decl * 57.295783
+
+#     field                    size  format  name         show    link to         extras
+FIELD rawExp.exp_name,     	  5, %s,     Exp Name
+FIELD rawExp.exp_id,     	  5, %d,     Exp ID,       value,  rawImfile.php,           ARG1
+FIELD chipRun.chip_id,    	  7, %d,     Chip ID,      value,  chipProcessedImfile.php, ARG2
+FIELD camRun.cam_id,    	  7, %d,     Cam ID,       value,  camProcessedExp.php,     ARG3
+FIELD fakeRun.fake_id,    	  7, %d,     Fake ID,      value,  fakeProcessedImfile.php, ARG4
+FIELD warpRun.warp_id,    	  7, %d,     Warp ID,      value,  warpStageExp.php, ARG5
+FIELD warpSkyCellMap.skycell_id, 10, %s,     skycell,      value,  warpProcessedSkyfiles.php, ARG6
+FIELD warpRun.state,    	  7, %s,     state
+FIELD warpRun.label,    	  7, %s,     label
+FIELD warpSkyCellMap.tess_id,    10, %s,     tess
+FIELD warpSkyCellMap.fault,      10, %s,     fault
+FIELD rawExp.telescope,      	 10, %s,     Telescope
+FIELD rawExp.camera,         	 10, %s,     Camera
+FIELD rawExp.dateobs,        	 19, %T,     Date/Time
+FIELD rawExp.ra,       	          8, %10.6f, RA,          op=OP1    
+FIELD rawExp.decl,       	  8, %10.6f, DEC,         op=OP2
+FIELD rawExp.object,         	  8, %s,     Object
+FIELD rawExp.filter,         	 10, %s,     FILTER
+FIELD rawExp.exp_time,       	  5, %.2f,   exp_time    
+FIELD rawExp.airmass,        	  5, %.4f,   airmass     
+FIELD rawExp.bg,             	  5, %.2f,   backgnd
+FIELD rawExp.bg_stdev,       	  5, %.2f,   stdev    
+FIELD rawExp.comment,  	         65, %s,     Comment
+
+# FIELD rawExp.bg_mean_stdev,  	 5, %7.2f,  &lt;backgnd&gt;
+# FIELD chipPendingExp.reduction,    20,   reduction
+# FIELD chipPendingExp.guide_id,     20, guide ID
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpSummary.d
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpSummary.d	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/def/warpSummary.d	(revision 22322)
@@ -0,0 +1,18 @@
+TABLE warpRun
+TITLE Warp Summary
+FILE  warpSummary.php
+MENU  ipp.science.dat
+
+# WHERE warp_id > 200 
+
+#     field           size  format  name         show    link to         extras
+FIELD label,    	 7, %s,     label
+FIELD workdir,    	 7, %s,     workdir
+FIELD tess_id,    	 7, %s,     tess_id
+FIELD state,    	 7, %s,     state
+FIELD count(state) as n, 7, %d,     count
+
+MODE  summary
+GROUP state
+GROUP label
+GROUP workdir
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/doc/notes.txt	(revision 22322)
@@ -0,0 +1,35 @@
+
+Installation:
+
+the code currently requires three (3) hardwired paths to be set before
+installation:
+
+- the DESTBIN directory in the Makefile must be set to the directory
+  in your apache installation where the package will reside
+
+- the database hostname, user, and password must be set in ipp.php
+
+- the location of the ipprc.config file must be specific in
+  getimage.php
+
+** TODO: set these with a configure script.
+
+- to update the mysql database from a dump:
+  * mysql -u root -p
+    create database NAME;
+    grant all on NAME.* to ipp@localhost;
+  * mysql -u ipp NAME -p < DUMPFILE
+
+- norm exp need to use dettool (-toresid ?) to get the list.
+
+- select * from detNormalizedImfile where detid,iteration
+  union select from detStackedImfile where detid,iteration,normalize=0
+  
+- create users table:
+  create database ippadmin;
+  use ippadmin;
+  create table users (username char(20) not null, password varchar(255));
+  insert into users (username, password) values ('eugene', 'test1');
+
+  create table cookies (username char(20) not null, cookie varchar(255));
+  insert into cookies (username, cookie) values ('eugene', 'foobar');
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+site.php
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/Login.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/Login.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/Login.php	(revision 22322)
@@ -0,0 +1,61 @@
+<?php 
+
+include 'ipp.php';
+
+if (($_SERVER[REQUEST_METHOD] != 'POST') && ($_SERVER[REQUEST_METHOD] != 'GET')) {
+  menu ('ipp.menu.dat', 'Login', 'ipp.css', '', '');
+  echo "Invalid Client Request<br>\n";
+  menu_end ();
+  exit ();
+}
+
+if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+
+  $ID = checkID ();
+
+  menu ('ipp.menu.dat', 'Login', 'ipp.css', $ID['link'], $ID['proj']);
+
+  $pass = $ID['pass'];
+  $name = $ID['name'];
+  echo "<p>You are currently logged in as $name.<br>\n";
+  echo "Click here to log out.\n";
+  echo "<form action=\"Login.php\" method=\"POST\">\n";
+  echo "<input type=\"hidden\" name=\"pass\" value=\"$pass\">\n";
+  echo "<input type=\"submit\" name=\"logout\" value=\"logout\">\n";
+  echo "</form></p>\n\n";
+  
+  menu_end ();
+  exit ();
+} 
+
+if ($_SERVER[REQUEST_METHOD] == 'POST') { 
+  if (key_exists (login, $_POST)) {
+
+    $ID = checkLogin ();
+
+    menu ('ipp.menu.dat', 'Login', 'ipp.css', $ID['link'], $ID['proj']);
+    echo "Login Accepted\n";
+    menu_end();
+    exit ();
+  }
+
+  if (key_exists (logout, $_POST)) {
+    menu ('ipp.menu.dat', 'Login', 'ipp.css', '', '');
+    echo "You are now logged out<br>\n";
+    logintext ();
+    loginform ();
+    menu_end ();
+    exit ();
+  }
+
+  // missing a valid POST state (login or logout)
+  menu ('ipp.menu.dat', 'Login', 'ipp.css', '', '');
+  echo "Invalid Client Post Request<br>\n";
+  foreach ($_POST as $key => $value) {
+    echo "$key : $value<br>\n";
+  }
+  menu_end ();
+  exit ();
+}
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/SelectProject.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/SelectProject.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/SelectProject.php	(revision 22322)
@@ -0,0 +1,37 @@
+<?php 
+
+include 'ipp.php';
+
+if (($_SERVER[REQUEST_METHOD] != 'POST') && ($_SERVER[REQUEST_METHOD] != 'GET')) {
+  menu ('ipp.menu.dat', 'Select Project', 'ipp.css', '', '');
+  echo "Invalid Client Request<br>\n";
+  menu_end ();
+  exit ();
+}
+
+$ID = checkID ();
+
+if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+  projectform ($ID);
+}
+
+if ($_SERVER[REQUEST_METHOD] == 'POST') { 
+
+  if (key_exists (project, $_POST)) {
+    $myProj = $_POST[proj];
+    $ID['proj'] = $myProj;
+    // validate the existence of the project
+
+    // update ID['link']
+    // $link is attached to every nav link
+    $ID['link'] = "pass=" . $ID['pass'];
+    if ($ID['proj']) {
+      $ID['link'] = $ID['link'] . "&proj=" . $ID['proj'];
+    }
+
+    menu ('ipp.menu.dat', 'Select Project', 'ipp.css', $ID['link'], $ID['proj']);
+    echo "New project is : $myProj<br>\n";
+    menu_end();
+  }
+}
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/getimage.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/getimage.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/getimage.php	(revision 22322)
@@ -0,0 +1,81 @@
+<?php
+
+$debug = 0;
+include 'site.php';
+
+// these are defined by site.php
+// global $PERLLIB;
+// global $BINDIR;
+// global $SITE;
+
+### these need to be set to the correct locations!!
+$MISSING = "missing.png";
+
+// load an image file from the image directory
+// validate request
+if ($_SERVER[REQUEST_METHOD] != 'GET') { 
+  exit ();
+}
+
+putenv("PERL5LIB=$PERLLIB:");
+$PATH = getenv("PATH");
+putenv("PATH=$BINDIR:$PATH");
+
+# echo "args: $args<br>";
+
+# $basename may contain filename@filerule
+$basename = $_GET[name];
+$filerule = $_GET[rule];
+$camera   = $_GET[camera];
+$class_id = $_GET[class_id];
+
+# $filerule = strtok("@");
+# echo "basename: $basename<br>";
+# echo "filerule: $filerule<br>";
+
+# need to supply the camera as well...
+
+# if ($filerule) {
+#   if ($filerule == "PPIMAGE.JPEG1") {
+#     $basename = "$basename.b1.jpg";
+#   }
+#   if ($filerule == "PPIMAGE.JPEG2") {
+#     $basename = "$basename.b2.jpg";
+#   }
+# }
+# exec ("ipp_datapath.pl --site=$SITE $basename", $output, $status);
+
+# do I need to do this to the output result as well?
+$basename = escapeshellarg($basename);
+$basename = str_replace ('..','',$basename);
+
+exec ("ipp_filename.pl --site=$SITE --basename $basename --filerule $filerule --camera $camera --class_id $class_id", $output, $status);
+
+# use these to check the environment
+# passthru ("env");
+# passthru ("perl --version");
+
+$filename = $output[0];
+if ($status) {
+  $filename = $MISSING;
+}
+
+if ($debug) {
+  echo "basename: $basename<br>";
+  for ($i = 0; $i < count($output); $i++) {
+    echo "output $i: $output[$i]<br>";
+  }
+  echo "output:   $output[0]<br>";
+  echo "status:   $status<br>";
+  echo "filename: $filename<br>";
+  exit ();
+}
+
+$file = fopen ($filename, "r");
+if ($file && !$debug) {
+  # do I need to use image/jpg? can I modify this to image/png, etc, based on the type?
+  header ('Content-Type: image/jpg');
+  fpassthru ($file);
+}
+exit ();
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/getlog.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/getlog.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/getlog.php	(revision 22322)
@@ -0,0 +1,66 @@
+<?php
+
+### these need to be set to the correct locations!!
+$PERLLIB = "/home/kiawe/eugene/psconfig/dev.linux/lib";
+$BINDIR = "/home/kiawe/eugene/psconfig/dev.linux/bin";
+$SITE = "/home/kiawe/eugene/.ipprc";
+$MISSING = "missing.png";
+
+// load an image file from the image directory
+// validate request
+if ($_SERVER[REQUEST_METHOD] != 'GET') { 
+  exit ();
+}
+
+putenv("PERL5LIB=$PERLLIB:");
+$PATH = getenv("PATH");
+putenv("PATH=$BINDIR:$PATH");
+
+# echo "args: $args<br>";
+
+# $basename may contain filename@filerule
+$basename = strtok($_GET[name],"@");
+$filerule = strtok("@");
+
+# echo "basename: $basename<br>";
+# echo "filerule: $filerule<br>";
+
+# need to supply the camera as well...
+
+# if ($filerule) {
+#   if ($filerule == "PPIMAGE.JPEG1") {
+#     $basename = "$basename.b1.jpg";
+#   }
+#   if ($filerule == "PPIMAGE.JPEG2") {
+#     $basename = "$basename.b2.jpg";
+#   }
+# }
+# exec ("ipp_datapath.pl --site=$SITE $basename", $output, $status);
+
+# do I need to do this to the output result as well?
+$basename = escapeshellarg($basename);
+$basename = str_replace ('..','',$basename);
+
+exec ("ipp_filename.pl --site=$SITE --basename $basename --camera $camera --class_id $class_id", $output, $status);
+
+$filename = $output[0];
+if ($status) {
+  $filename = $MISSING;
+}
+
+if (0) {
+  echo "basename: $basename<br>";
+  echo "output:   $output[0]<br>";
+  echo "status:   $status<br>";
+  echo "filename: $filename<br>";
+  exit ();
+}
+
+$file = fopen ($filename, "r");
+if ($file) {
+  # do I need to use image/jpg? can I modify this to image/png, etc, based on the type?
+  header ('Content-Type: image/jpg');
+  fpassthru ($file);
+}
+exit ();
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.cal.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.cal.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.cal.dat	(revision 22322)
@@ -0,0 +1,29 @@
+# style   | select-style | type    | menu line                    | link page       
+
+# we have four valid menu types: picture, piclink, link, plain
+# picture   | picture    | picture | PScolorlogo2.jpg             | none
+# piclink   | piclink    | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+# menulink  | menuselect | link    | other page                   | notest.php     
+# menutop   | menutop    | plain   | foo bar                      | none            
+
+piclink   | piclink      | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS public            | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS project           | http://panstarrs.ifa.hawaii.edu/project
+menuext   | menuext      | link    | Pan-STARRS IPP               | http://panstarrs.ifa.hawaii.edu/project/IPP
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Login                        | Login.php     
+menulink  | menuselect   | link    | Select Project               | SelectProject.php     
+menulink  | menuselect   | link    | [$PROJECT]                   | SelectProject.php     
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutop   | menutop      | link    | Load and Setup               | ipp.load.php
+menutop   | menutop      | link    | Detrend Steps                | ipp.detrend.php
+menutop   | menutop      | link    | Science Steps                | ipp.science.php
+menutop   | menutop      | link    | Stack Steps                  | ipp.stack.php
+menutops  | menutops     | link    | Cal Steps                    | ipp.cal.php
+menutop   | menutop      | link    | Imfile Tables                | ipp.imfiles.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Calibration DBs              | calDB.php
+menulink  | menuselect   | link    | Calibration Runs             | calRun.php
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.cal.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.cal.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.cal.php	(revision 22322)
@@ -0,0 +1,17 @@
+<?php 
+
+include 'ipp.php';
+
+$ID = checkID ();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+menu('ipp.cal.dat', 'Calibration Steps', 'ipp.css', $ID['link'], $ID['proj']);
+
+// document body
+echo "Calibration Steps<br>\n";
+
+menu_end();
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.css
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.css	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.css	(revision 22322)
@@ -0,0 +1,179 @@
+
+/* internal link line: c0d0d0 : d8d0f0
+   external link line: 303070 
+   border:             0080c0
+ */
+
+body  { bgcolor: #a0d0e0; background-color: #a0d0e0; padding: 5px; margin: 5px }
+
+a:link, a:visited, a:active { text-decoration: underline; font-weight: bold; color: #ff0000 }
+
+a.button      { text-decoration: none; font-weight: bold;   color: #000000; background: #0080c0}
+a.menutop     { text-decoration: none; font-weight: bold;   color: #ffffff}
+a.menutops    { text-decoration: none; font-weight: bold;   color: #80f0ff}
+a.menuext     { text-decoration: none; font-weight: bold;   color: #ffffff}
+a.menulink    { text-decoration: none; font-weight: normal; color: #000000}
+a.menuselect  { text-decoration: none; font-weight: normal; color: #0000ff}
+a.piclink     { text-decoration: none; font-weight: normal; color: #0080c0}
+
+td.menutop    { text-align: left; background: #0080c0; font-size: small; color: #ffffff; padding: 2px; }
+td.menutops   { text-align: left; background: #0080c0; font-size: small; color: #000000; padding: 2px; }
+td.menuext    { text-align: left; background: #303070; font-size: small; color: #ffffff; padding: 2px; }
+td.menulink   { text-align: left; background: #d0d0ff; font-size: small; color: #000000; padding: 2px; font-weight: normal; text-indent: 0px; }
+td.menuselect { text-align: left; background: #d0d0ff; font-size: small; color: #ff0000; padding: 2px; font-weight: normal; text-indent: 0px; }
+td.menuline   { text-align: left; background: #d0d0ff; font-size: small; color: #000000; padding: 2px; font-weight: normal; text-indent: 0px; }
+
+td.picture    { text-align: left; background: #00ffff; font-size: small; color: #ff0000; padding: 0px; text-indent: 2px; font-weight: normal; }
+td.piclink    { text-align: left; background: #0080c0; font-size: small; color: #ff0000; padding: 0px; text-indent: 0px; font-weight: normal; }
+
+table.page { border: 0px solid #0080c0; background: #a0d0e0; padding: 0px}
+
+td.title { background-color: #ff0000; padding: 5px; font-size: x-large; text-align: left; vertical-align: top}
+
+td.body  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: normal;  
+           vertical-align: top;
+	   background: #d0d0ff; 
+	   background-color: #d0d0ff; 
+	   border: 3px solid #0080c0; 
+	   padding: 5px; 
+}
+
+table.menu { text-align: left; 
+             font-size: small; 
+	     font-weight: normal; 
+	     background: #ff0000; 
+	     border: 3px solid #0080c0; 
+	     padding: 0px; 
+}
+
+table.select { text-align: left; 
+             font-size: small; 
+	     font-weight: normal; 
+	     background: #0080c0; 
+	     border: 1px solid #0080c0; 
+	     padding: 0px; 
+}
+
+td.select  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: bold;  
+           vertical-align: top;
+	   color: #ffffff; 
+	   background: #0080c0; 
+	   background-color: #0080c0; 
+	   border: 2px solid #0080c0; 
+	   padding: 2px; 
+}
+
+th.select  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: bold;  
+           vertical-align: top;
+	   color: #000000; 
+	   background: #d0d0ff; 
+	   background-color: #d0d0ff; 
+	   border: 0px solid #0080c0; 
+	   padding: 2px; 
+}
+
+table.list { text-align: left; 
+             font-size: small; 
+	     font-weight: normal; 
+	     background: #d0d0ff; 
+	     border: 2px solid #ffffff; 
+	     padding: 0px; 
+}
+
+td.list  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: normal;  
+           vertical-align: top;
+	   background: #d0d0ff; 
+	   background-color: #d0d0ff; 
+	   border: 2px solid #000000; 
+	   padding: 2px; 
+}
+
+td.list_off  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: normal;  
+           vertical-align: top;
+	   background: #a0a0a0; 
+	   background-color: #a0a0a0;
+	   border: 2px solid #000000; 
+	   padding: 2px; 
+}
+
+td.list_drop  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: normal;  
+           vertical-align: top;
+	   background: #a0a0a0; 
+	   background-color: #a0a0a0;
+	   border: 2px solid #000000; 
+	   padding: 2px; 
+}
+
+td.list_run  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: normal;  
+           vertical-align: top;
+	   background: #ffa0a0; 
+	   background-color: #ffa0a0;
+	   border: 2px solid #000000; 
+	   padding: 2px; 
+}
+
+td.det_off  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: normal;  
+           vertical-align: top;
+	   background: #a0a0a0; 
+	   background-color: #a0a0a0;
+	   border: 2px solid #000000; 
+	   padding: 2px; 
+}
+
+td.det_drop  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: normal;  
+           vertical-align: top;
+	   background: #a0a0a0; 
+	   background-color: #ffa0a0;
+	   border: 2px solid #000000; 
+	   padding: 2px; 
+}
+
+td.det_add  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: normal;  
+           vertical-align: top;
+	   background: #ffa0a0; 
+	   background-color: #a0a0ff;
+	   border: 2px solid #000000; 
+	   padding: 2px; 
+}
+
+th.list  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: bold;  
+           vertical-align: top;
+	   background: #d0d0ff; 
+	   background-color: #d0d0ff; 
+	   border: 2px solid #000000; 
+	   padding: 2px; 
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.detrend.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.detrend.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.detrend.dat	(revision 22322)
@@ -0,0 +1,54 @@
+# style   | select-style | type    | menu line                    | link page       
+
+# we have four valid menu types: picture, piclink, link, plain
+# picture   | picture    | picture | PScolorlogo2.jpg             | none
+# piclink   | piclink    | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+# menulink  | menuselect | link    | other page                   | notest.php     
+# menutop   | menutop    | plain   | foo bar                      | none            
+
+piclink   | piclink      | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS public            | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS project           | http://panstarrs.ifa.hawaii.edu/project
+menuext   | menuext      | link    | Pan-STARRS IPP               | http://panstarrs.ifa.hawaii.edu/project/IPP
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Login                        | Login.php     
+menulink  | menuselect   | link    | Select Project               | SelectProject.php     
+menulink  | menuselect   | link    | [$PROJECT]                   | SelectProject.php     
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutop   | menutop      | link    | Load and Setup               | ipp.load.php
+menutops  | menutops     | link    | Detrend Steps                | ipp.detrend.php
+menutop   | menutop      | link    | Science Steps                | ipp.science.php
+menutop   | menutop      | link    | Stack Steps                  | ipp.stack.php
+menutop   | menutop      | link    | Cal Steps                    | ipp.cal.php
+menutop   | menutop      | link    | Imfile Tables                | ipp.imfiles.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect 	 | link    | Detrend Frames               | masterDetrendFrames.php
+menulink  | menuselect 	 | link    | Detrend Runs                 | detRun.php             
+menulink  | menuselect 	 | link    | Detrend Iterations           | detRunSummary.php             
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect 	 | link    | Raw Detrend Exp              | rawDetrendExp_detrend.php
+menulink  | menuselect 	 | link    | Input Detrend Exp            | detInputExp.php
+menulink  | menuselect 	 | link    | Proc Detrend Exp             | detProcessedExp.php
+menulink  | menuselect 	 | link    | Proc Detrend Exp<br>(no image)  | detProcessedExp_noimage.php
+menulink  | menuselect 	 | link    | Norm Detrend Exp             | detNormalizedExp.php
+menulink  | menuselect 	 | link    | Resid Detrend Exp            | detResidExp.php
+menulink  | menuselect 	 | link    | Resid Detrend Exp<br>(no images)| detResidExp_noimage.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutop   | menutop      | plain   | Failures                     | 
+menulink  | menuselect 	 | link    | DetProc Fail Imfiles         | detProcessedImfile_failure.php
+menulink  | menuselect 	 | link    | DetProc Fail Exp             | detProcessedExp_failure.php
+menulink  | menuselect 	 | link    | DetStack Fail Imfile         | detStackedImfile_failure.php
+menulink  | menuselect 	 | link    | DetNormStat Fail Imfile      | detNormalizedStatImfile_failure.php
+menulink  | menuselect 	 | link    | DetNorm Fail Imfile          | detNormalizedImfile_failure.php
+menulink  | menuselect 	 | link    | DetNorm Fail Exp             | detNormalizedExp_failure.php
+menulink  | menuselect 	 | link    | DetResid Fail Imfile         | detResidImfile_failure.php
+menulink  | menuselect 	 | link    | DetResid Fail Exp            | detResidExp_failure.php
+
+menutop   | menutop      | plain   | Flat Correction              | 
+menulink  | menuselect 	 | link    | Flat Corr Runs               | flatcorrRun.php
+menulink  | menuselect 	 | link    | Flat Corr Exp                | flatcorrExp.php
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.detrend.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.detrend.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.detrend.php	(revision 22322)
@@ -0,0 +1,24 @@
+<?php 
+
+include 'ipp.php';
+
+$ID = checkID ();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+menu('ipp.detrend.dat', 'Detrend Steps', 'ipp.css', $ID['link'], $ID['proj']);
+
+// document body
+echo "Detrend Image Creation<br>\n
+
+The pages in this section allow you to examine the master detrend
+images and the processing steps involved in their creation.  You may
+search through the detrend images which have been accepted by
+the detrend analysis system by selecting the Master Detrend Images tab.
+
+";
+
+menu_end();
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.diff.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.diff.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.diff.dat	(revision 22322)
@@ -0,0 +1,32 @@
+# style   | select-style | type    | menu line                    | link page       
+
+# we have four valid menu types: picture, piclink, link, plain
+# picture   | picture    | picture | PScolorlogo2.jpg             | none
+# piclink   | piclink    | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+# menulink  | menuselect | link    | other page                   | notest.php     
+# menutop   | menutop    | plain   | foo bar                      | none            
+
+piclink   | piclink      | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS public            | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS project           | http://panstarrs.ifa.hawaii.edu/project
+menuext   | menuext      | link    | Pan-STARRS IPP               | http://panstarrs.ifa.hawaii.edu/project/IPP
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Login                        | Login.php     
+menulink  | menuselect   | link    | Select Project               | SelectProject.php     
+menulink  | menuselect   | link    | [$PROJECT]                   | SelectProject.php     
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutop   | menutop      | link    | Load and Setup               | ipp.load.php
+menutop   | menutop      | link    | Detrend Steps                | ipp.detrend.php
+menutop   | menutop      | link    | Science Steps                | ipp.science.php
+menutop   | menutop      | link    | Warp Steps                   | ipp.warp.php
+menutop   | menutop      | link    | Stack Steps                  | ipp.stack.php
+menutops  | menutops     | link    | Diff Steps                   | ipp.diff.php
+menutop   | menutop      | link    | Cal Steps                    | ipp.cal.php
+menutop   | menutop      | link    | Imfile Tables                | ipp.imfiles.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Diff Run                     | diffRun.php
+menulink  | menuselect   | link    | Diff Input Skyfile           | diffInputSkyfile.php
+menulink  | menuselect   | link    | Diff Skyfile                 | diffSkyfile.php
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.diff.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.diff.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.diff.php	(revision 22322)
@@ -0,0 +1,17 @@
+<?php 
+
+include 'ipp.php';
+
+$ID = checkID ();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+menu('ipp.diff.dat', 'Diff Steps', 'ipp.css', $ID['link'], $ID['proj']);
+
+// document body
+echo "diff steps<br>\n";
+
+menu_end();
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.imfiles.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.imfiles.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.imfiles.dat	(revision 22322)
@@ -0,0 +1,42 @@
+# style   | select-style | type    | menu line                    | link page       
+
+# we have four valid menu types: picture, piclink, link, plain
+# picture   | picture    | picture | PScolorlogo2.jpg             | none
+# piclink   | piclink    | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+# menulink  | menuselect | link    | other page                   | notest.php     
+# menutop   | menutop    | plain   | foo bar                      | none            
+
+piclink   | piclink      | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS public            | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS project           | http://panstarrs.ifa.hawaii.edu/project
+menuext   | menuext      | link    | Pan-STARRS IPP               | http://panstarrs.ifa.hawaii.edu/project/IPP
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Login                        | Login.php     
+menulink  | menuselect   | link    | Select Project               | SelectProject.php     
+menulink  | menuselect   | link    | [$PROJECT]                   | SelectProject.php     
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutop   | menutop      | link    | Load and Setup               | ipp.load.php
+menutop   | menutop      | link    | Detrend Steps                | ipp.detrend.php
+menutop   | menutop      | link    | Science Steps                | ipp.science.php
+menutop   | menutop      | link    | Stack Steps                  | ipp.stack.php
+menutop   | menutop      | link    | Cal Steps                    | ipp.cal.php
+menutops  | menutops     | link    | Imfile Tables                | ipp.imfiles.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect 	 | link    | newImfile                	  | newImfile.php                         
+menulink  | menuselect 	 | link    | rawImfile                	  | rawImfile.php                         
+menulink  | menuselect 	 | link    | detProcessedImfile       	  | detProcessedImfile.php                
+menulink  | menuselect 	 | link    | detStackedImfile         	  | detStackedImfile.php                  
+menulink  | menuselect 	 | link    | detNormalizedImfile      	  | detNormalizedImfile.php               
+menulink  | menuselect 	 | link    | detNormalizedStatImfile  	  | detNormalizedStatImfile.php           
+menulink  | menuselect 	 | link    | detResidImfile           	  | detResidImfile.php            
+menulink  | menuselect 	 | link    | masterDetrendImfiles      	  | masterDetrendImfiles.php
+menulink  | menuselect 	 | link    | pzPendingImfile          	  | pzPendingImfile.php                   
+menulink  | menuselect 	 | link    | chipPendingImfile            | chipPendingImfile.php                   
+menulink  | menuselect 	 | link    | chipProcessedImfile          | chipProcessedImfile.php                 
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutop   | menutop      | plain   | External Links               | none            
+menutop   | menutop      | link    | test page                    | phptest.php     
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.imfiles.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.imfiles.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.imfiles.php	(revision 22322)
@@ -0,0 +1,17 @@
+<?php 
+
+include 'ipp.php';
+
+$ID = checkID ();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+menu('ipp.imfiles.dat', 'Imfile Tables', 'ipp.css', $ID['link'], $ID['proj']);
+
+// document body
+echo "browse the imfile tables<br>\n";
+
+menu_end();
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.load.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.load.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.load.dat	(revision 22322)
@@ -0,0 +1,45 @@
+# style   | select-style | type    | menu line                    | link page       
+
+# we have four valid menu types: picture, piclink, link, plain
+# picture   | picture    | picture | PScolorlogo2.jpg             | none
+# piclink   | piclink    | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+# menulink  | menuselect | link    | other page                   | notest.php     
+# menutop   | menutop    | plain   | foo bar                      | none            
+
+piclink   | piclink      | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS public            | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS project           | http://panstarrs.ifa.hawaii.edu/project
+menuext   | menuext      | link    | Pan-STARRS IPP               | http://panstarrs.ifa.hawaii.edu/project/IPP
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Login                        | Login.php     
+menulink  | menuselect   | link    | Select Project               | SelectProject.php     
+menulink  | menuselect   | link    | [$PROJECT]                   | SelectProject.php     
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutops  | menutops     | link    | Load and Setup               | ipp.load.php
+menutop   | menutop      | link    | Detrend Steps                | ipp.detrend.php
+menutop   | menutop      | link    | Science Steps                | ipp.science.php
+menutop   | menutop      | link    | Stack Steps                  | ipp.stack.php
+menutop   | menutop      | link    | Cal Steps                    | ipp.cal.php
+menutop   | menutop      | link    | Imfile Tables                | ipp.imfiles.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect 	 | link    | Raw Detrend Exp              | rawDetrendExp.php             
+menulink  | menuselect 	 | link    | Raw Science Exp              | rawScienceExp.php             
+menulink  | menuselect 	 | link    | Raw Unknown Exp              | rawUnknownExp.php             
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect 	 | link    | Summit Exp                   | summitExp.php                         
+menulink  | menuselect 	 | link    | pz Pending Exp               | pzPendingExp.php              
+menulink  | menuselect 	 | link    | pz Copied Exp                | pzDoneExp.php              
+menulink  | menuselect 	 | link    | New Exp                      | newExp.php                    
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect 	 | link    | Failed Raw Imfiles           | rawImfile_failed.php
+menulink  | menuselect 	 | link    | Failed Raw Exp               | rawExp_failed.php                    
+menulink  | menuselect 	 | link    | Failed Copy Imfiles          | pzDoneImfile_failed.php
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect 	 | link    | Summit Imfile                | summitImfile.php                         
+menulink  | menuselect 	 | link    | pz Pending Imfile            | pzPendingImfile.php              
+menulink  | menuselect 	 | link    | pz Copied Imfile             | pzDoneImfile.php              
+menulink  | menuselect 	 | link    | New Imfile                   | newImfile.php                    
+menulink  | menuselect 	 | link    | Raw Imfile                   | rawImfile.php                    
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.load.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.load.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.load.php	(revision 22322)
@@ -0,0 +1,17 @@
+<?php 
+
+include 'ipp.php';
+
+$ID = checkID ();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+menu('ipp.load.dat', 'Load and Setup', 'ipp.css', $ID['link'], $ID['proj']);
+
+// document body
+echo "load and setup page<br>\n";
+
+menu_end();
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.menu.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.menu.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.menu.dat	(revision 22322)
@@ -0,0 +1,26 @@
+# style   | select-style | type    | menu line                    | link page       
+
+# we have four valid menu types: picture, piclink, link, plain
+# picture   | picture    | picture | PScolorlogo2.jpg             | none
+# piclink   | piclink    | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+# menulink  | menuselect | link    | other page                   | notest.php     
+# menutop   | menutop    | plain   | foo bar                      | none            
+
+piclink   | piclink      | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS public            | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS project           | http://panstarrs.ifa.hawaii.edu/project
+menuext   | menuext      | link    | Pan-STARRS IPP               | http://panstarrs.ifa.hawaii.edu/project/IPP
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Login                        | Login.php     
+menulink  | menuselect   | link    | Select Project               | SelectProject.php     
+menulink  | menuselect   | link    | [$PROJECT]                   | SelectProject.php     
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutop   | menutop      | link    | Load and Setup               | ipp.load.php
+menutop   | menutop      | link    | Detrend Steps                | ipp.detrend.php
+menutop   | menutop      | link    | Science Steps                | ipp.science.php
+menutop   | menutop      | link    | Stack Steps                  | ipp.stack.php
+menutop   | menutop      | link    | Cal Steps                    | ipp.cal.php
+menutop   | menutop      | link    | Imfile Tables                | ipp.imfiles.php
+
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.php	(revision 22322)
@@ -0,0 +1,706 @@
+<?php
+
+$dTABLE = 80;
+include 'site.php';
+
+function logintext () {
+  echo '<p>Welcome to the IPP status pages.<br><br>', "\n";
+  echo 'From these pages, you may examine summary information about the IPP processing results.<br>', "\n";
+  echo 'In order to continue, it is necessary to login as an authorized IPP user.</p>', "\n\n";
+}
+
+function loginform ($username) { 
+  echo '<form action="Login.php" method="POST">', "\n";
+  if ($username) {
+    echo "Username: <input type=\"text\" name=\"username\" value=\"$username\"><br>\n";
+  } else {
+    echo "Username: <input type=\"text\" name=\"username\"><br>\n";
+  }
+  echo 'Password: <input type="text" name="password"><br>', "\n";
+  echo '<input type="submit" name="login" value="login">', "\n";
+  echo '</form>', "\n\n";
+}
+
+function projectform ($ID) {
+    menu ('ipp.menu.dat', 'Select Project', 'ipp.css', $ID['link'], $ID['proj']);
+    if ($_GET['new'] && $_GET['proj']) {
+      echo "<p> Project is now <b>" . $_GET['proj'] . "</b></p>\n";
+    }
+    echo "<p> Please select the project of interest </p>\n";
+    // echo "<form action=\"SelectProject.php\" method=\"POST\">\n";
+    // echo "Project: <input type=\"text\" name=\"proj\"><br>\n";
+    // echo "<input type=\"submit\" name=\"project\">\n";
+
+    $pass = $ID['pass'];
+    // echo "<input type=\"hidden\" name=\"pass\" value=\"$pass\">\n";
+    // echo "</form>\n";
+
+    $db = dbconnect('ippadmin');
+
+    // make this a DB lookup
+    $sql = "SELECT projname FROM projects";
+
+    $qry = $db->query($sql);
+    if (DB::iserror($db)) {
+      echo "<b>error reading project list</b><br>\n";
+      menu_end();
+    }
+  
+    echo "<table class=select>\n";
+    echo "<tr class=select><th class=select> projects </th></tr>\n";
+    while ($qry->fetchInto($row)) {
+      // add a link here to set the database
+      $tmp_proj = $row[0];
+      $tmp_link = "pass=$pass";
+      echo "<tr class=select><td class=select><a class=\"menutop\" href=\"SelectProject.php?$tmp_link&proj=$tmp_proj&new=1\"> $row[0] </a></td></tr>\n";
+      // echo "<tr class=select><td class=select> $row[0] </td></tr>\n";
+    }
+    echo "</table>\n";
+
+    menu_end ();
+}
+
+function dbconnect ($database) {
+
+  require_once('DB.php');
+
+  // these are set in 'site.php'
+  global $DBHOST;
+  global $DBUSER;
+  global $DBPASS;
+
+  // connect to the database
+  $db = DB::connect("mysql://$DBUSER:$DBPASS@$DBHOST/$database");
+  if (DB::iserror($db)) {
+    echo "<b>error accessing database</b><br>\n";
+    echo "<b>tried mysql://$DBUSER:XXX@$DBHOST/$database</b><br>\n";
+    $result = $db->getMessage();
+    echo "$result<br>";
+    menu_end();
+  }
+  return $db;
+}
+
+// there are three pieces of data that are used to define a user:
+// - name (the username: available somewhere: db table?)
+// - proj (defines which database is being used)
+// - pass (NOT the same as the user password: randomly generated for the session and given a timeout) 
+
+// have this function take the current menu and style?
+function checkLogin () {
+
+  $username = $_POST[username];
+  $password = $_POST[password];
+
+  // if password is not specified, keep username and ask again
+  if ($password == "") {
+    menu ('ipp.menu.dat', 'Login', 'ipp.css', '', '');
+    loginform ($username);
+    menu_end ();
+  }
+
+  $db = dbconnect('ippadmin');
+
+  // get the users from the DB
+  $sql = "SELECT username FROM users WHERE username = '$username' AND password = '$password'";
+
+  $qry = $db->query($sql);
+  if (DB::iserror($db)) {
+    menu ('ipp.menu.dat', 'Login', 'ipp.css', '', '');
+    echo "<b>error reading users table</b><br>\n";
+    echo "Login Failed, please try again<br>\n";
+    loginform ("");
+    menu_end();
+  }
+  
+  $success = $qry->fetchInto($row);
+  if (!$success) {  
+    menu ('ipp.menu.dat', 'Login', 'ipp.css', '', '');
+    echo "Login Failed, please try again<br>\n";
+    loginform ("");
+    menu_end ();
+    exit ();
+  }
+  
+  // generate an random ephemeral ID
+  $ID['name'] = $username;
+  $ID['proj'] = "";
+
+  // generate a cookie for the user
+  $sql = "SELECT username,cookie FROM cookies WHERE username = '$username'";
+  $qry = $db->query($sql);
+  if (DB::iserror($db)) {
+    echo "Cookie Failed, please try again<br>\n";
+    menu_end();
+  }
+
+  if ($qry->fetchInto($row)) {
+    $ID['pass'] = $row[1];
+  } else {
+    $cookie = rand(0,100000);
+    $ID['pass'] = $cookie;
+    $sql = "INSERT into cookies (username, cookie) values ('$username', '$cookie')";
+    $qry = $db->query($sql);
+    if (DB::iserror($db)) {
+      echo "Cookie Failed, please try again<br>\n";
+      menu_end();
+    }
+  }
+
+  // the link is attached to every nav link
+  $ID['link'] = "pass=" . $ID['pass'];
+  if ($ID['proj']) {
+    $ID['link'] = $link . "&proj=" . $ID['proj'];
+  }
+
+  return $ID;
+}
+
+function checkID () {
+
+  $VERBOSE = 0;
+
+  // check for valid server method
+  if (($_SERVER[REQUEST_METHOD] != 'POST') && ($_SERVER[REQUEST_METHOD] != 'GET')) {
+    menu ('ipp.menu.dat', 'Login', 'ipp.css', '', '');
+    echo "Invalid Client Request<br>\n";
+    menu_end ();
+    exit ();
+  }
+
+  // look for pass & proj in appropriate location
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $ID['pass'] = $_GET[pass];
+    $ID['proj'] = $_GET[proj];
+    $ID['menu'] = $_GET[menu];
+    $ID['from'] = $_GET[from];
+
+    if ($VERBOSE) {
+      echo "using GET: ", $ID['pass'], $ID['proj'], "<br>\n";
+      echo "GET list<br>\n";
+      foreach ($_GET as $key => $value) {
+	echo "$key : $value<br>\n";
+      }
+    }
+  }
+  if ($_SERVER[REQUEST_METHOD] == 'POST') { 
+    $ID['pass'] = $_POST[pass];
+    $ID['proj'] = $_POST[proj];
+    $ID['menu'] = $_POST[menu];
+    $ID['from'] = $_POST[from];
+
+    if ($VERBOSE) {
+      echo "using POST: ", $ID['pass'], $ID['proj'], "<br>\n";
+      echo "POST list<br>\n";
+      foreach ($_POST as $key => $value) {
+	echo "$key : $value<br>\n";
+      }
+    }
+  }
+
+  // user is not logged in at all
+  if ($ID['pass'] == "") {
+    menu('ipp.menu.dat', 'Login', 'ipp.css', '', '');
+    logintext ();    
+    loginform ("");
+    menu_end ();
+  }
+
+  $db = dbconnect ('ippadmin');
+
+  $pass = $ID['pass'];
+
+  // search for password
+  $sql = "SELECT username FROM cookies WHERE cookie = '$pass'";
+  $qry = $db->query($sql);
+  if (DB::iserror($db)) {
+    echo "<b>error reading users table</b><br>\n";
+    echo "Login Failed, please try again<br>\n";
+    loginform ("");
+    menu_end();
+  }
+  
+  $success = $qry->fetchInto($row);
+  if (! $success) {
+    menu('ipp.menu.dat', 'Login', 'ipp.css', '', '');
+    echo "unknown user, please login again<br>\n";
+    loginform ("");
+    menu_end ();
+  }
+  // use the pass value to find the corresponding user
+  $ID['name'] = $row[0];
+
+  // $link is attached to every nav link
+  $ID['link'] = "pass=" . $ID['pass'];
+  if ($ID['proj']) {
+    $ID['link'] = $ID['link'] . "&proj=" . $ID['proj'];
+  }
+  
+  return $ID;
+}
+
+function menu ($source, $title, $sheet, $append, $project) {
+
+  echo "<html><head><title> $title </title></head>\n\n";
+  echo "<link rel=\"STYLESHEET\" HREF=\"$sheet\">\n";
+  echo "<body>\n";
+
+  $root = "/ippMonitor";
+
+  if (! $project) { $project = "none"; }
+
+  $file = fopen ($source, "r");
+
+  echo "<table class=page cellspacing=10px><tr><td valign=top>\n";
+  echo "<table class=menu cellspacing=0px>\n";
+
+  while ($line = fgetcsv ($file, 1024, "|")) {
+    if (count($line) != 5) continue;
+    if (ereg ('^[:blank:]*#', $line[0])) continue;
+
+    $type = trim($line[2]);
+    $name = trim($line[3]);
+    $base = trim($line[4]);
+
+    if (preg_match ('|\$PROJECT|', $name)) {
+      $name = preg_replace ('|\$PROJECT|', $project, $name);
+    }
+
+    if ($append) {
+      $link = $base . "?" . $append;
+    } else {
+      $link = $base;
+    }
+	
+    $thisname = $_SERVER[SCRIPT_NAME]; 
+    $page = "$root/" . $base;
+    if ($page == $thisname) {
+	$style = trim($line[1]);
+    } else { 
+	$style = trim($line[0]);
+    }       
+
+    switch ($type) { 
+      case 'plain':
+        echo "<tr><td class=\"$style\"> $name </td></tr>\n";  
+        break;
+      case 'picture':
+        echo "<tr><td class=\"$style\"> <img src=\"$name\"> </td></tr>\n";     
+        break;
+      case 'piclink':
+        echo "<tr><td class=\"$style\"> <a class=\"$style\" href=\"$link\"> <img class=\"$style\" src=\"$name\"> </a></td></tr>\n";     
+        break;
+      default:
+        echo "<tr><td class=\"$style\"> <a class=\"$style\" href=\"$link\"> $name </a></td></tr>\n";     
+        break;
+    } 
+  }
+  fclose ($file);
+  echo '</table></td><td class="body" valign=top>', "\n\n";
+}
+
+function menu_end () { 
+  echo "</td></tr></table>\n";
+  echo "</body></html>\n\n";
+  exit ();
+}
+
+function navigate_buttons ($rowStart, $rowLast, $dTABLE, $rowTotal, $buttonLink, $ID, $file) {
+
+  // add a sort key?
+  $sortKey = "";
+  // try sort forward
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $sortVal = $_GET['sort']; 
+  } else {
+    $sortVal = $_POST['sort'];
+  }
+  if ($sortVal != "") {
+    $sortKey = "&sort=" . $sortVal;
+  }
+  // try sort reverse
+  if ($sortKey == "") {
+    if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+      $sortKey = $_GET['rsort']; 
+    } else {
+      $sortKey = $_POST['rsort'];
+    }
+    if ($sortVal != "") {
+      $sortKey = "&rsort=" . $sortVal;
+    }
+  }
+
+  if ($rowStart > 0) {
+    $value = 0;
+    $link = "$file" . "?" . $ID['link'] . "&from=$value";
+    if ($buttonLink != "") {
+      $link = $link . "&$buttonLink";
+    }
+    $link = $link . $sortKey;
+    echo "<a width=10 class=\"button\" href=\"$link\"><img src=\"left.png\"><img src=\"left.png\"></a>\n";
+  } else {
+    echo "<img src=\"left_sel.png\"><img src=\"left_sel.png\">\n";
+  }
+  if ($rowStart >= $dTABLE) {
+    $value = $rowStart - $dTABLE;
+    $link = "$file" . "?" . $ID['link'] . "&from=$value";
+    if ($buttonLink != "") {
+      $link = $link . "&$buttonLink";
+    }
+    $link = $link . $sortKey;
+    echo "<a class=\"button\" href=\"$link\"><img src=\"left.png\"></a>\n";
+  } else {
+    echo "<img src=\"left_sel.png\">\n";
+  }
+
+  if ($rowLast < $rowTotal) {
+    $value = $rowStart + $dTABLE;
+    $link = "$file" . "?" . $ID['link'] . "&from=$value";
+    if ($buttonLink != "") {
+      $link = $link . "&$buttonLink";
+    }
+    $link = $link . $sortKey;
+    echo "<a class=\"button\" href=\"$link\"><img src=\"right.png\"></a>\n";
+  } else {
+    echo "<img src=\"right_sel.png\">\n";
+  }
+
+  if ($rowLast < $rowTotal) {
+    $value = $rowTotal - $dTABLE;
+    $link = "$file" . "?" . $ID['link'] . "&from=$value";
+    if ($buttonLink != "") {
+      $link = $link . "&$buttonLink";
+    }
+    $link = $link . $sortKey;
+    echo "<a class=\"button\" href=\"$link\"><img src=\"right.png\"><img src=\"right.png\"></a>\n";
+  } else {
+    echo "<img src=\"right_sel.png\"><img src=\"right_sel.png\">\n";
+  }
+}
+
+function check_restrict ($key, $where, $mode) {
+  $htmlkey = preg_replace ('|\.|', '_', $key);
+  if ($mode == 'min') {
+    $htmlkey = $htmlkey . "_min";
+  }
+  if ($mode == 'max') {
+    $htmlkey = $htmlkey . "_max";
+  }
+
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET[$htmlkey]; 
+  } else {
+    $value = $_POST[$htmlkey];
+  }
+  if ($value == "") { return $where; }
+  if ($where) { 
+    $where = $where . " AND"; 
+  } else {
+    $where = "WHERE";
+  }
+
+  if ($mode == 'string') {
+    // can we pass the '%' through the html?
+    $where = $where . " $key like '$value'";
+  } 
+  if ($mode == 'min') {
+    // can we pass the '%' through the html?
+    $where = $where . " $key >= '$value'";
+  } 
+  if ($mode == 'max') {
+    // can we pass the '%' through the html?
+    $where = $where . " $key <= '$value'";
+  } 
+
+  return $where;
+}
+
+function check_ordering ($groups, $where) {
+
+  if ($groups != "") {
+    $where = $where . " GROUP BY $groups";
+  }
+
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET['sort']; 
+  } else {
+    $value = $_POST['sort'];
+  }
+  if ($value != "") {
+    $where = $where . " ORDER BY $value";
+    return $where;
+  }
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET['rsort']; 
+  } else {
+    $value = $_POST['rsort'];
+  }
+  if ($value != "") {
+    $where = $where . " ORDER BY $value DESC";
+    return $where;
+  }
+  return $where;
+}
+
+// if we have a restriction for a string-valued field, supply it to outgoing links
+function button_restrict_string ($key, $line) {
+  $htmlkey = preg_replace ('|\.|', '_', $key);
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET[$htmlkey]; 
+  } else {
+    $value = $_POST[$htmlkey];
+  }
+  if ($value != "") {
+    if ($line) {
+      $line = $line . "&$key=$value";
+    } else {
+      $line = "$key=$value";
+    }
+  }
+  return $line;
+}
+
+// for numerical fields, test for both 'min' and plain values
+function button_restrict_min ($key, $line) {
+  $htmlkey = preg_replace ('|\.|', '_', $key);
+  $htmlkey = $htmlkey . '_min';
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET[$htmlkey]; 
+  } else {
+    $value = $_POST[$htmlkey];
+  }
+  if ($value != "") {
+    if ($line) {
+      $line = $line . "&$htmlkey=$value";
+    } else {
+      $line = "$htmlkey=$value";
+    }
+  }
+  return $line;
+}
+
+// for numerical fields, test for both 'min' and 'max' values
+function button_restrict_max ($key, $line) {
+  $htmlkey = preg_replace ('|\.|', '_', $key);
+  $htmlkey = $htmlkey . '_max';
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET[$htmlkey]; 
+  } else {
+    $value = $_POST[$htmlkey];
+  }
+  if ($value != "") {
+    if ($line) {
+      $line = $line . "&$htmlkey=$value";
+    } else {
+      $line = "$htmlkey=$value";
+    }
+  }
+  return $line;
+}
+
+function write_header_cell ($class, $name) {
+
+  echo "<th class=\"$class\">$name</th>\n";
+}
+
+// write a table cell with the up and down sort arrows
+function write_sort_cell ($class, $value, $buttonLink, $ID, $file) {
+  
+  echo "<td>";
+
+  // base link
+  $baselink = "$file" . "?" . $ID['link'];
+  if ($buttonLink != "") {
+    $baselink = $baselink . "&$buttonLink";
+  }
+
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $up   = $_GET['rsort']; 
+    $down = $_GET['sort']; 
+    $from = $_GET['from']; 
+  } else {
+    $up   = $_POST['rsort']; 
+    $down = $_POST['sort']; 
+    $from = $_POST['from']; 
+  }
+
+  // down button link
+  $link = "$baselink" . "&sort=$value";
+  if ($from != "") {
+    $link = $link . "&from=$from";
+  }
+  if ($down == $value) {
+    echo "<a href=\"$link\"><img src=\"down_sel.png\"></a>";
+  } else {
+    echo "<a href=\"$link\"><img src=\"down.png\"></a>";
+  }
+
+  // up button link
+  $link = "$baselink" . "&rsort=$value";
+  if ($from != "") {
+    $link = $link . "&from=$from";
+  }
+  if ($up == $value) {
+    echo "<a href=\"$link\"><img src=\"up_sel.png\"></a>";
+  } else {
+    echo "<a href=\"$link\"><img src=\"up.png\"></a>";
+  }
+
+  echo "</td>\n";
+}
+
+// there are two query rows: for strings, only the first is set; for numbers, the first = min, the second = max
+// mode == string, min, max
+function write_query_row ($key, $width, $mode) {
+  $htmlkey = preg_replace ('|\.|', '_', $key);
+
+  // optional bits by mode
+  if ($mode == 'min') {
+    $htmlkey = $htmlkey . "_min";
+  }
+  if ($mode == 'max') {
+    $htmlkey = $htmlkey . "_max";
+  }
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET[$htmlkey]; 
+  } else {
+    $value = $_POST[$htmlkey];
+  }
+
+  // full table element includes optionally the <= or >= words
+  echo "<td class=\"list\"> <input type=\"text\" name=\"$htmlkey\" size=\"$width\"";
+  if ($value != "") { 
+    echo "value=\"$value\">";
+  } 
+  echo "</td>\n";
+}
+
+function write_table_cell ($class, $format, $link, $value) {
+
+  if ($format == '%T') {
+    $format = '%s';
+  }
+  if ($format == '%t') {
+    $format = '%s';
+    if ($value == 0) { 
+      $value = 'N'; 
+    } else {
+      $value = 'Y';
+    }
+  }
+
+  $myValue = sprintf ($format, $value);
+  if ($myValue == "") { $myValue = "&nbsp;"; }
+
+  if ($link) {
+    echo "<td class=\"$class\"><a href=\"$link\"> $myValue </a></td>\n";
+  } else {
+    echo "<td class=\"$class\"> $myValue </td>\n";
+  }
+}
+
+// expect image supplied with key=name
+// this function should define matching hidden inputs in the form
+function insert_image ($rule) {
+
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $basename = $_GET['basename']; 
+    $class_id = $_GET['class']; 
+    $camera   = $_GET['camera']; 
+  } else {
+    $basename = $_POST['basename']; 
+    $class_id = $_POST['class']; 
+    $camera   = $_POST['camera']; 
+  }
+
+  if (! $class_id) { $class_id = "NONE"; }
+
+  if ($basename && $camera && $rule) {
+    echo "<img src=\"getimage.php?name=$basename&rule=$rule&camera=$camera&class_id=$class_id\">\n";
+  }
+}
+
+// expect image supplied with key=name
+// this function should define matching hidden inputs in the form
+function insert_log ($filerule) {
+
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $basename = $_GET['basename']; 
+    $class_id = $_GET['class']; 
+    $camera   = $_GET['camera']; 
+  } else {
+    $basename = $_POST['basename']; 
+    $class_id = $_POST['class']; 
+    $camera   = $_POST['camera']; 
+  }
+
+  // echo "<b>insert log</b><br>\n";
+  // echo "<b>basename: $basename</b><br>\n";
+  // echo "<b>class_id: $class_id</b><br>\n";
+  // echo "<b>camera:   $camera</b><br>\n";
+
+  if (! $class_id) { $class_id = "NONE"; }
+
+  if ($basename && $camera && $filerule) {
+
+    // do I need to do this to the output result as well?
+    $basename = escapeshellarg($basename);
+    $basename = str_replace ('..','',$basename);
+
+    // these are defined by site.php
+    global $PERLLIB;
+    global $BINDIR;
+    global $SITE;
+
+    putenv("PERL5LIB=$PERLLIB:");
+    $PATH = getenv("PATH");
+    putenv("PATH=$BINDIR:$PATH");
+
+    // be careful of the definition of PATH in SITE: cannot contain HOME
+    echo "<br>ipp_filename.pl --site=$SITE --basename $basename --filerule $filerule --camera $camera --class_id $class_id<br>\n";
+    exec ("ipp_filename.pl --site=$SITE --basename $basename --filerule $filerule --camera $camera --class_id $class_id", $output, $status);
+
+    if (0) {
+      echo "basename: $basename<br>";
+      for ($i = 0; $i < count($output); $i++) {
+	echo "output $i: $output[$i]<br>";
+      }
+      echo "output:   $output[0]<br>";
+      echo "status:   $status<br>";
+      echo "filename: $filename<br>";
+      exit ();
+    }
+
+    $filename = $output[0];
+    if ($status) {
+      echo "log file ($filerule, $basename) not found<br>\n";
+      return;
+    }
+
+    echo "<pre>\n";
+    $file = fopen ($filename, "r");
+    if ($file && !$debug) {
+      fpassthru ($file);
+    }
+    echo "</pre>\n";
+  }
+}
+
+// expect image supplied with key=name
+// this function should define matching hidden inputs in the form
+function insert_backref ($page, $key, $link) {
+
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $name = $_GET[$key]; 
+  } else {
+    $name = $_POST[$key]; 
+  }
+
+  if ($name) {
+    echo "back to <a href=\"$page?$link&$key=$name\"> $name </a><br>\n";
+  }
+}
+
+// $myPage = $_SERVER[SCRIPT_NAME] . "?pass=$pass";
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.science.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.science.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.science.dat	(revision 22322)
@@ -0,0 +1,60 @@
+# style   | select-style | type    | menu line                    | link page       
+
+# we have four valid menu types: picture, piclink, link, plain
+# picture   | picture    | picture | PScolorlogo2.jpg             | none
+# piclink   | piclink    | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+# menulink  | menuselect | link    | other page                   | notest.php     
+# menutop   | menutop    | plain   | foo bar                      | none            
+
+piclink   | piclink      | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS public            | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS project           | http://panstarrs.ifa.hawaii.edu/project
+menuext   | menuext      | link    | Pan-STARRS IPP               | http://panstarrs.ifa.hawaii.edu/project/IPP
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Login                        | Login.php     
+menulink  | menuselect   | link    | Select Project               | SelectProject.php     
+menulink  | menuselect   | link    | [$PROJECT]                   | SelectProject.php     
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutop   | menutop      | link    | Load and Setup               | ipp.load.php
+menutop   | menutop      | link    | Detrend Steps                | ipp.detrend.php
+menutops  | menutops     | link    | Science Steps                | ipp.science.php
+menutop   | menutop      | link    | Stack Steps                  | ipp.stack.php
+menutop   | menutop      | link    | Cal Steps                    | ipp.cal.php
+menutop   | menutop      | link    | Imfile Tables                | ipp.imfiles.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Chip Summary                 | chipSummary.php
+menulink  | menuselect 	 | link    | Chip-Stage Exp               | chipStageExp.php            
+menulink  | menuselect 	 | link    | Chip Pending Exp             | chipPendingExp.php              
+menulink  | menuselect 	 | link    | Chip Processed Exp           | chipProcessedExp.php            
+menulink  | menuselect 	 | link    | Chip Pending Imfiles         | chipPendingImfile.php            
+menulink  | menuselect 	 | link    | Chip Processed Imfiles       | chipProcessedImfile.php            
+menulink  | menuselect 	 | link    | Chip Failed Imfiles          | chipProcessedImfile_failure.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Cam Summary                 | camSummary.php
+menulink  | menuselect 	 | link    | Cam-Stage Exp                | camStageExp.php            
+menulink  | menuselect 	 | link    | Cam Pending Exp              | camPendingExp.php              
+menulink  | menuselect 	 | link    | Cam Processed Exp            | camProcessedExp_NoImages.php            
+menulink  | menuselect 	 | link    | Cam Proc. Exp w/ Images      | camProcessedExp.php            
+menulink  | menuselect 	 | link    | Cam Failed Exp               | camProcessedExp_failure.php
+menulink  | menuselect 	 | link    | Cam Processed Imfiles        | camProcessedImfile.php            
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Fake Summary                 | fakeSummary.php
+menulink  | menuselect 	 | link    | Fake-Stage Exp               | fakeStageExp.php            
+menulink  | menuselect 	 | link    | Fake Pending Exp             | fakePendingExp.php              
+menulink  | menuselect 	 | link    | Fake Processed Exp           | fakeProcessedExp.php            
+menulink  | menuselect 	 | link    | Fake Pending Imfiles         | fakePendingImfile.php            
+menulink  | menuselect 	 | link    | Fake Processed Imfiles       | fakeProcessedImfile.php            
+menulink  | menuselect 	 | link    | Fake Failed Imfiles          | fakeProcessedImfile_failure.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Warp Summary                 | warpSummary.php
+menulink  | menuselect   | link    | Warp Stage Exp               | warpStageExp.php
+menulink  | menuselect   | link    | Warp Stage Skyfiles          | warpStageSkyfiles.php
+menulink  | menuselect   | link    | Warp Skyfile Inputs          | warpStageSkyfileInputs.php
+menulink  | menuselect   | link    | Warp Processed Skyfiles      | warpProcessedSkyfiles.php
+menulink  | menuselect   | link    | Warp Failed Skyfiles         | warpFailedSkyfiles.php
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.science.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.science.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.science.php	(revision 22322)
@@ -0,0 +1,17 @@
+<?php 
+
+include 'ipp.php';
+
+$ID = checkID ();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+menu('ipp.science.dat', 'Science Steps', 'ipp.css', $ID['link'], $ID['proj']);
+
+// document body
+echo "science image steps<br>\n";
+
+menu_end();
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.stack.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.stack.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.stack.dat	(revision 22322)
@@ -0,0 +1,36 @@
+# style   | select-style | type    | menu line                    | link page       
+
+# we have four valid menu types: picture, piclink, link, plain
+# picture   | picture    | picture | PScolorlogo2.jpg             | none
+# piclink   | piclink    | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+# menulink  | menuselect | link    | other page                   | notest.php     
+# menutop   | menutop    | plain   | foo bar                      | none            
+
+piclink   | piclink      | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS public            | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS project           | http://panstarrs.ifa.hawaii.edu/project
+menuext   | menuext      | link    | Pan-STARRS IPP               | http://panstarrs.ifa.hawaii.edu/project/IPP
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Login                        | Login.php     
+menulink  | menuselect   | link    | Select Project               | SelectProject.php     
+menulink  | menuselect   | link    | [$PROJECT]                   | SelectProject.php     
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutop   | menutop      | link    | Load and Setup               | ipp.load.php
+menutop   | menutop      | link    | Detrend Steps                | ipp.detrend.php
+menutop   | menutop      | link    | Science Steps                | ipp.science.php
+menutops  | menutops     | link    | Stack Steps                  | ipp.stack.php
+menutop   | menutop      | link    | Cal Steps                    | ipp.cal.php
+menutop   | menutop      | link    | Imfile Tables                | ipp.imfiles.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Stack Run                    | stackRun.php
+menulink  | menuselect   | link    | Stack Input Skyfile          | stackInputSkyfile.php
+menulink  | menuselect   | link    | Stack Processed Skyfile      | stackProcessedSkyfile.php
+menulink  | menuselect   | link    | Stack Failed Skyfile         | stackFailedSkyfile.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Diff Run                     | diffRun.php
+menulink  | menuselect   | link    | Diff Input Skyfile           | diffInputSkyfile.php
+menulink  | menuselect   | link    | Diff Skyfile                 | diffSkyfile.php
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.stack.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.stack.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.stack.php	(revision 22322)
@@ -0,0 +1,17 @@
+<?php 
+
+include 'ipp.php';
+
+$ID = checkID ();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+menu('ipp.stack.dat', 'Stack Steps', 'ipp.css', $ID['link'], $ID['proj']);
+
+// document body
+echo "stack steps<br>\n";
+
+menu_end();
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.warp.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.warp.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.warp.dat	(revision 22322)
@@ -0,0 +1,33 @@
+# style   | select-style | type    | menu line                    | link page       
+
+# we have four valid menu types: picture, piclink, link, plain
+# picture   | picture    | picture | PScolorlogo2.jpg             | none
+# piclink   | piclink    | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+# menulink  | menuselect | link    | other page                   | notest.php     
+# menutop   | menutop    | plain   | foo bar                      | none            
+
+piclink   | piclink      | piclink | PScolorlogo2.jpg             | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS public            | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext      | link    | Pan-STARRS project           | http://panstarrs.ifa.hawaii.edu/project
+menuext   | menuext      | link    | Pan-STARRS IPP               | http://panstarrs.ifa.hawaii.edu/project/IPP
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Login                        | Login.php     
+menulink  | menuselect   | link    | Select Project               | SelectProject.php     
+menulink  | menuselect   | link    | [$PROJECT]                   | SelectProject.php     
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menutop   | menutop      | link    | Load and Setup               | ipp.load.php
+menutop   | menutop      | link    | Detrend Steps                | ipp.detrend.php
+menutop   | menutop      | link    | Science Steps                | ipp.science.php
+menutops  | menutops     | link    | Warp Steps                   | ipp.warp.php
+menutop   | menutop      | link    | Stack Steps                  | ipp.stack.php
+menutop   | menutop      | link    | Diff Steps                   | ipp.diff.php
+menutop   | menutop      | link    | Cal Steps                    | ipp.cal.php
+menutop   | menutop      | link    | Imfile Tables                | ipp.imfiles.php
+
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect   | link    | Warp Run                     | warpRun.php
+menulink  | menuselect   | link    | Warp Input Exp               | warpInputExp.php
+menulink  | menuselect   | link    | Warp Skycell Map             | warpSkycellMap.php
+menulink  | menuselect   | link    | Warp Skyfile                 | warpSkyfile.php
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.warp.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.warp.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/ipp.warp.php	(revision 22322)
@@ -0,0 +1,17 @@
+<?php 
+
+include 'ipp.php';
+
+$ID = checkID ();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+menu('ipp.warp.dat', 'Warp Steps', 'ipp.css', $ID['link'], $ID['proj']);
+
+// document body
+echo "warp steps<br>\n";
+
+menu_end();
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/masterDetrendImfiles.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/masterDetrendImfiles.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/masterDetrendImfiles.php	(revision 22322)
@@ -0,0 +1,95 @@
+<?php 
+
+include 'ipp.php';
+
+$ID = checkID ();
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+$db = dbconnect($ID['proj']);
+
+if ($ID['menu']) {
+  $myMenu = $ID['menu'];
+} else {
+  $myMenu = "ipp.detrend.dat";
+}
+
+menu($myMenu, 'Master Detrend Imfiles', 'ipp.css', $ID['link'], $ID['proj']);
+
+echo "<p> Master Detrend Imfiles </p>";
+
+// define restrictiosn to the queries
+// ** TABLE RESTRICTIONS **
+$WHERE = check_restrict ('det_id', $WHERE);
+$WHERE = check_restrict ('iteration', $WHERE);
+$WHERE = check_restrict ('uri', $WHERE);
+
+// query the database
+if ($WHERE) {
+  $sql = "SELECT det_id,iteration,uri FROM detNormalizedImfile WHERE $WHERE UNION SELECT det_id,iteration,uri FROM detStackedImfile WHERE normalize = 0 AND $WHERE LIMIT 20";
+} else {
+  $sql = "SELECT det_id,iteration,uri FROM detNormalizedImfile UNION SELECT det_id,iteration,uri FROM detStackedImfile WHERE normalize = 0 LIMIT 20";
+}
+
+$qry = $db->query($sql);
+if (DB::iserror($db)) {
+  echo "<b>error reading detStackedImfile table</b><br>\n";
+  menu_end();
+}
+
+// ** HEAD CODE **
+
+// set up the table and form
+echo "<form action=\"masterDetrendImfiles.php\" method=\"POST\">\n";
+
+echo "<table class=list>\n";
+echo "<tr>\n";
+// ** TABLE HEADER **
+echo "<th class=\"list\"> det_id </th>\n";
+echo "<th class=\"list\"> iter </th>\n";
+echo "<th class=\"list\"> uri </th>\n";
+// echo "<th class=list> FIELD </th>\n";
+echo "</tr>\n";
+
+// list the results
+while ($qry->fetchInto($row)) {
+  // $link = "masterDetrendImfiles.php" . "?expID=" . $row[0] . "&" . $ID['link'];
+
+  echo "<tr>\n";
+  // ** TABLE DATA **
+  echo "<td class=\"list\"> $row[0] </td>\n";
+  echo "<td class=\"list\"> $row[1] </td>\n";
+  echo "<td class=\"list\"> $row[2] </td>\n";
+  // echo "<td class=list><a href=\"$link\"> $row[0] </a></td>\n";
+  // echo "<td class=list> $row[1] </td>\n";
+  echo "</tr>\n";
+}
+
+// query restriction form
+echo "<tr>\n";
+// echo "<td class=list> <input type=\"text\" name=\"expID\"></td>\n";
+// ** TABLE QUERY **
+write_query_row ('det_id', 7);
+write_query_row ('iteration', 5);
+write_query_row ('uri', 5);
+echo "</tr>\n";
+
+// close the table and form
+echo "</table>\n";
+echo "<input type=\"submit\" name=\"constraints\" value=\"refine search\">\n";
+$pass = $ID['pass'];
+$proj = $ID['proj'];
+$menu = $ID['menu'];
+echo "<input type=\"hidden\" name=\"pass\" value=\"$pass\">\n";
+echo "<input type=\"hidden\" name=\"proj\" value=\"$proj\">\n";
+echo "<input type=\"hidden\" name=\"menu\" value=\"$menu\">\n";
+echo "</form>\n";
+
+// ** TAIL CODE **
+
+echo "<small> WHERE: $WHERE<br><br></small>\n";
+echo "<small> SQL: $sql<br><br></small>\n";
+
+menu_end();
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/phptest.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/phptest.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/phptest.php	(revision 22322)
@@ -0,0 +1,25 @@
+<?php 
+
+include 'ipp.php';
+
+menu('ipp.menu.dat', 'test.page', 'ipp.css', '', '');
+
+$varlist = array ('SERVER_NAME', 'GATEWAY_INTERFACE', 'SERVER_PROTOCOL',
+'SERVER_PORT', 'REQUEST_METHOD', 'PATH_INFO', 'PATH_TRANSLATED', 'SCRIPT_NAME',
+'QUERY_STRING', 'REMOTE_HOST', 'REMOTE_ADDR', 'AUTH_TYPE', 'REMOTE_USER',
+'REMOTE_IDENT', 'CONTENT_TYPE', 'CONTENT_LENGTH');
+
+foreach ($varlist as $name) {
+  echo "$name : $_SERVER[$name]<br>\n";
+}
+
+echo "get list<br>\n";
+foreach ($_GET as $key => $value) {
+  echo "$key : $value<br>\n";
+}
+
+$value = system ("ls /etc", $status);
+echo "status: $status\n";
+
+menu_end();
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/raw/site.php.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/raw/site.php.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/raw/site.php.in	(revision 22322)
@@ -0,0 +1,11 @@
+<?php
+
+$DBHOST = "@DBHOST@";
+$DBUSER = "@DBUSER@";
+$DBPASS = "@DBPASS@";
+
+$PERLLIB = "@PERLLIB@";
+$BINDIR  = "@BINDIR@";
+$SITE    = "@SITE@/ippconfig/ipprc.config";
+
+?>
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/scripts/dbadmin
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/scripts/dbadmin	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/scripts/dbadmin	(revision 22322)
@@ -0,0 +1,77 @@
+#!/bin/csh -f
+## this script creates the tables needed by ippMonitor for user administration
+
+if ($#argv < 4) goto usage;
+
+if ("$1" == "init") then
+  if ($#argv != 4) goto usage;
+  set dbserver = $2
+  set dbuser = $3
+  set dbpass = $4
+
+  mysql -h $dbserver -u root -p <<EOF
+  create database ippadmin;
+  use ippadmin;
+  create table users (username char(20) not null, password varchar(255));
+  create table cookies (username char(20) not null, cookie varchar(255));
+  create table projects (projname char(20) not null);
+  grant all on *.* to $dbuser@localhost identified by '$dbpass';
+EOF
+    exit 0;
+endif
+
+if ("$1" == "client") then
+  if ($#argv != 5) goto usage;
+  set dbserver = $2
+  set dbuser = $3
+  set dbpass = $4
+  set client = $5
+
+  mysql -h $dbserver -u root -p <<EOF
+   grant all on *.* to $dbuser@"$client" identified by '$dbpass';
+EOF
+    exit 0;
+endif
+
+if ("$1" == "user") then
+  if ($#argv != 5) goto usage;
+  set dbserver = $2
+  set dbuser = $3
+  set user = $4
+  set pass = $5
+
+  mysql -h $dbserver -u $dbuser -p ippadmin <<EOF
+  insert into users (username, password) values ('$user', '$pass');
+EOF
+    exit 0;
+endif
+
+if ("$1" == "project") then
+  if ($#argv != 4) goto usage;
+  set dbserver = $2
+  set dbuser = $3
+  set dbname = $4
+
+  mysql -h $dbserver -u $dbuser -p ippadmin <<EOF
+   insert into projects (projname) values ('$dbname');
+   create database $dbname;
+EOF
+    exit 0;
+endif
+
+usage:
+  echo "USAGE: dbadmin (options)"
+  echo ""
+  echo "  dbadmin init (dbserver) (dbuser) (dbpassword)"
+  echo "      creates admin tables, basic db user -- requires root password"
+  echo ""
+  echo "  dbadmin client (dbserver) (dbuser) (dbpassword) (client)"
+  echo "      add client name or regex"
+  echo ""
+  echo "  dbadmin user (dbserver) (dbuser) (username) (password)"
+  echo "      create a new www user and password"
+  echo ""
+  echo "  dbadmin project (dbserver) (dbuser) (dbname)"
+  echo "      create a new ipp project (database)"
+  echo ""
+  exit 2
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/scripts/generate
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/scripts/generate	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/scripts/generate	(revision 22322)
@@ -0,0 +1,694 @@
+#!/usr/bin/env perl
+
+$VERBOSE = 0;
+
+if ($ARGV[0] eq "-v") { $VERBOSE = 1; shift @ARGV; }
+if (@ARGV != 3) { die "generate (schema) (template) (output)\n"; }
+
+$schema   = $ARGV[0];
+$template = $ARGV[1];
+$output   = $ARGV[2];
+
+# global variables:
+# @key, @value                         : paired list of keys and values
+# @headcode, @tailcode                 : additional HTML or PHP code before or after table
+# @field, @width, @name, @show, @link, @extras : properties of each table column
+# @imagedefs, %image: array of image defines
+
+&parse_schema;
+&parse_template;
+exit 0;
+
+sub parse_schema {
+    open (FILE, "$schema");
+    @list = <FILE>;
+    close (FILE);
+
+    &init_key ("MENU");
+    &init_key ("STYLE");
+    &init_key ("MODE");
+    &init_key ("TABLE");
+    &init_key ("TITLE");
+    &init_key ("FILE");
+
+    &init_key ("FIELDS");
+    &init_key ("GROUPS");
+
+    foreach $line (@list) {
+	chop $line;
+	($key, $value) = split (" ", $line, 2);
+	
+	# strip leading and trailing white space from the following
+	if ($key eq "TABLE") { ($value) = $value =~ m|^\s*(.+)\s*$|; }
+	if ($key eq "TYPE")  { ($value) = $value =~ m|^\s*(.+)\s*$|; }
+
+	&set_keypair ($key, $value);
+
+	# list of the table fields
+ 	if ($key eq "FIELD") {
+	    ($field, $width, $format, $name, $show, $link, $extras) = split (/,\s+/, $value, 7);
+	    if (! $name) { die "table $ARGV[0] missing required elements\n"; }
+	    if ($field =~ m|\S+\s+as\s+\S+|) {
+		($fieldreal) = $field =~ m|\S+\s+as\s+(\S+)|;
+	    } else {
+		$fieldreal = $field;
+	    }
+	    
+	    if ($VERBOSE) { printf "%-20s %-20s %-20s %-20s %-20s %-20s\n", $field, $name, $format, $show, $link, $extras; }
+
+	    if ($show eq "") {$show = "value";}
+	    push @field,     $field;
+	    push @fieldreal, $fieldreal;
+	    push @width,     $width;
+	    push @format,    $format;
+	    push @name,      $name;
+	    push @show,      $show;
+	    push @link,      $link;
+	    push @extras,    $extras;
+	}
+
+	if ($key eq "FILE") {
+	    $myFile = $value;
+	}
+
+	# list of the table fields
+ 	if ($key eq "HEAD") {
+	    push @headcode, $value;
+	}
+
+	# the IMAGE commands sets up a reference name
+	if ($key eq "IMAGE") {
+	    push @imagedefs, $value;
+	}
+
+	# the GROUP entries are used in summary tables as arguments to the GROUP BY sql statement
+	if ($key eq "GROUP") {
+	    push @group, $value;
+	}
+
+	# the ARGS commands define arguments to FIELDS commands
+	if ($key eq "ARGS") {
+	    push @linkargs, $value;
+	}
+
+	# the OP commands define field operations
+	if ($key eq "OP") {
+	    push @opwords, $value;
+	}
+
+	# the TD_CLASS command sets up an alternate TD class
+	if ($key eq "TD_CLASS") {
+	    push @tdClasses, $value;
+	    # print STDERR "found TD_CLASS\n";
+	}
+
+	# list of the table fields
+ 	if ($key eq "TAIL") {
+	    push @tailcode, $value;
+	}
+
+	# list of the table restrictions
+	# replace this with a flexible where statement?
+ 	if ($key eq "WHERE") {
+	    ($field) = split (/,\s+/, $value, 1);
+	    if ($VERBOSE) { printf "%-20s\n", $field; }
+	    push @where,   $field;
+	}
+    }
+
+    # check on keys
+    &check_key ("MENU", "ipp.menu.dat");
+    &check_key ("STYLE", "ipp.css");
+    &check_key ("MODE", "basic");
+    &check_key ("TABLE", "");
+    &check_key ("TITLE", "");
+    &check_key ("FILE", "");
+    
+    # these are used internally (not just a replacement)
+    for ($i = 0; $i < @key; $i++) {
+	if ($key[$i] eq "MENU") { $MENU = $value[$i]; }
+	if ($key[$i] eq "MODE") { 
+	    if ($value[$i] eq "summary") {
+		if (@group == 0) {
+		    print STDERR "a summary table must have at least one group\n";
+		    exit 1;
+		}
+		&define_groups_string (@group);
+	    }
+	}
+    }
+
+    &check_key ("GROUPS", "none");
+
+    # define FIELDS and WHERE strings, add to keypairs
+    &define_fields_count;
+    &define_fields_string (@field);
+    &define_where_string (@where);
+
+    foreach $linkarg (@linkargs) {
+	&parse_linkarg ($linkarg);
+    }
+
+    foreach $imagedef (@imagedefs) {
+	&parse_imagedef ($imagedef);
+    }
+
+    foreach $opword (@opwords) {
+	&parse_opwords ($opword);
+    }
+
+    if (0) {
+	print STDERR "show: $show[0]\n";
+	($a, $b, $c) = &parse_label ($show[0]);
+	print STDERR "a: $a, b: $b, c: $c\n";
+
+	print STDERR "extras: $extras[0]\n";
+	($a, $b, $c) = &parse_label ($extras[0]);
+	print STDERR "a: $a, b: $b, c: $c\n";
+
+	print STDERR "show: $show[1]\n";
+	($a, $b, $c) = &parse_label ($show[1]);
+	print STDERR "a: $a, b: $b, c: $c\n";
+
+	print STDERR "extras: $extras[1]\n";
+	($a, $b, $c) = &parse_label ($extras[1]);
+	print STDERR "a: $a, b: $b, c: $c\n";
+	exit 0;
+    }
+}
+
+sub parse_template {
+    open (FILE, $template);
+    @list = <FILE>;
+    close (FILE);
+
+    open (FILE, ">$output");
+
+    foreach $line (@list) {
+	
+	&check_keypairs;
+
+	print FILE $line;
+
+	# fill in table header
+	if ($line =~ m|// \*\* TABLE HEADER \*\*|) {
+	    &write_table_header;
+	}
+
+	# fill in TD_CLASS test
+	if ($line =~ m|// \*\* TD CLASS \*\*|) {
+	    &write_td_class;
+	}
+
+	# fill in table data
+	if ($line =~ m|// \*\* TABLE DATA \*\*|) {
+	    &write_table_data;
+	}
+
+	# fill in table query
+	if ($line =~ m|// \*\* TABLE QUERY \*\*|) {
+	    &write_table_query;
+	}
+
+	# fill in table restricts
+	if ($line =~ m|// \*\* TABLE RESTRICTIONS \*\*|) {
+	    &write_table_restrict;
+	}
+
+	# fill in table restricts
+	if ($line =~ m|// \*\* BUTTON RESTRICTIONS \*\*|) {
+	    &write_button_restrict;
+	}
+
+	# fill in head HTML or PHP code
+	if ($line =~ m|// \*\* HEAD CODE \*\*|) {
+	    &write_inline_code (@headcode);
+	}
+
+	# fill in tail HTML or PHP code
+	if ($line =~ m|// \*\* TAIL CODE \*\*|) {
+	    &write_inline_code (@tailcode);
+	}
+    }
+    close (FILE);
+}
+
+sub write_inline_code {
+    my (@code) = @_;
+    my ($i, $code, $type);
+    
+    for ($i = 0; $i < @code; $i++) {
+	($type, $line) = split (" ", $code[$i], 2);
+	if ($type eq "PHP") {
+	    print FILE "$line\n";
+	}
+	if ($type eq "HTML") {
+	    print FILE "?> $line <?\n";
+	}
+    }
+}
+
+# generate a table header cell (<th> </th>) for each field (show != none)
+sub write_table_header {
+
+    # print the table header (field labels)
+    print FILE "echo \"<tr><td></td>\\n\";\n"; 
+    for ($i = 0; $i < @field; $i++) {
+	($label, $value, $string) = &parse_label ($show[$i]);
+	if ($show[$i] eq "none")  { next; }
+	print FILE "write_header_cell (\"list\", \"$name[$i]\");\n";
+    }
+    print FILE "echo \"</tr>\\n\";\n";
+
+    # print the column sort cells
+    print FILE "echo \"<tr><td></td>\\n\";\n"; 
+    for ($i = 0; $i < @field; $i++) {
+	($label, $value, $string) = &parse_label ($show[$i]);
+	if ($show[$i] eq "none")  { next; }
+	if ($field[$i] eq "*") { print FILE "echo \"<td></td>\\n\";\n"; next; } # empty cell (no sort for such fields)
+	if ($label eq "op") {
+	    print FILE "write_sort_cell (\"list\", \"$opf{$value}\", \$buttonLink, \$ID, '$myFile');\n";
+	} else {
+	    print FILE "write_sort_cell (\"list\", \"$fieldreal[$i]\", \$buttonLink, \$ID, '$myFile');\n";
+	}
+    }
+    print FILE "echo \"</tr>\\n\";\n";
+}
+
+sub write_td_class {
+    my ($field, $testline);
+
+    if (@tdClasses == 0) { return; }
+    foreach $tdClass (@tdClasses) {
+
+	# TD_CLASS class field expression
+	($class, $testline) = split (" ", $tdClass, 2);
+
+	$testlineFixed = "";
+	@testlineBits = split (" ", $testline);
+	foreach my $bit (@testlineBits) {
+	    $newbit = &parse_fieldname ($bit);
+	    $testlineFixed = "$testlineFixed $newbit";
+	}
+
+	print FILE "  if ($testlineFixed) {\n";
+	print FILE "    \$class = \"$class\";\n";
+	print FILE "  }\n";
+    }
+}
+
+# generate a WHERE test for each field (field != *)
+sub write_table_restrict {
+
+    if ($WHERE) {
+	print FILE "\$WHERE = \"WHERE $WHERE\";\n";
+    }	
+    for ($i = 0; $i < @field; $i++) {
+	$value = $fieldreal[$i];
+	if ($value eq "*") { next; }
+	if ($format[$i] eq "%s") {
+	    print FILE "\$WHERE = check_restrict ('$value', \$WHERE, 'string');\n";
+	} else {
+	    print FILE "\$WHERE = check_restrict ('$value', \$WHERE, 'string');\n";
+	    print FILE "\$WHERE = check_restrict ('$value', \$WHERE, 'min');\n";
+	    print FILE "\$WHERE = check_restrict ('$value', \$WHERE, 'max');\n";
+	}
+    }
+    print FILE "\$WHERE = check_ordering ('$GROUPS', \$WHERE);\n";
+}
+
+# generate a button link entry for each field (field != *)
+sub write_button_restrict {
+
+    for ($i = 0; $i < @field; $i++) {
+	$value = $fieldreal[$i];
+	if ($value eq "*") { next; }
+	if ($format[$i] eq "%s") {
+	    print FILE "\$buttonLink = button_restrict_string ('$value', \$buttonLink);\n";
+	} else {
+	    print FILE "\$buttonLink = button_restrict_string ('$value', \$buttonLink);\n";
+	    print FILE "\$buttonLink = button_restrict_min ('$value', \$buttonLink);\n";
+	    print FILE "\$buttonLink = button_restrict_max ('$value', \$buttonLink);\n";
+	}
+    }
+}
+
+# there are two query rows: the first is populated for both strings
+# and numbers, the second only for numbers
+sub write_table_query {
+    my ($i);
+
+    print FILE "echo \"<tr><td>&ge</td>\\n\";\n";
+    for ($i = 0; $i < @field; $i++) {
+	if ($show[$i] eq "none")  { next; }
+	if ($field[$i] eq "*")  { 
+	    # * fields create an empty cell 
+	    print FILE "echo \"<td> &nbsp; </td>\\n\";\n";
+	} else {
+	    if ($format[$i] eq "%s") {
+		print FILE "write_query_row ('$fieldreal[$i]', $width[$i], 'string');\n";
+	    } else {
+		print FILE "write_query_row ('$fieldreal[$i]', $width[$i], 'min');\n";
+	    }
+	}
+    }
+    print FILE "echo \"</tr><tr><td>&le</td>\\n\";\n";
+    for ($i = 0; $i < @field; $i++) {
+	if ($show[$i] eq "none")  { next; }
+	if (($field[$i] eq "*") || ($format[$i] eq "%s")) { 
+	    # * fields create an empty cell 
+	    print FILE "echo \"<td> &nbsp; </td>\\n\";\n";
+	} else {
+	    print FILE "write_query_row ('$fieldreal[$i]', $width[$i], 'max');\n";
+	}
+    }
+    print FILE "echo \"</tr>\\n\";\n";
+}
+
+sub write_table_data {
+    my ($i, $Nrow);
+    my ($label, $value, $string);
+
+    for ($i = 0; $i < @field; $i++) {
+	($label, $value, $string) = &parse_label ($show[$i]);
+	$Nrow = $count[$i];
+
+	# create the link variable if this entry should be linked
+	if ($label eq "none")  { next; }
+
+	if ($label eq "value") {
+	    # create the basic link variable
+	    if ($link[$i]) {
+		print FILE "  \$link = \"$link[$i]\" . \"?menu=$MENU&\" . \$ID['link'];\n";
+		# add extra GET data to target
+		if ($extras[$i]) { &parse_extras ($linkarg{$extras[$i]}); } 
+		$myLink = "\$link";
+	    } else {
+		$myLink = "\"\"";
+	    }
+	    # print the actual table cell line with the link...
+	    if (! $value && ($field[$i] ne "*")) { $value = "\$row[$Nrow]"; }
+
+	    # add special format elements
+	    # $realFormat = $format[$i];
+	    # if ($format[$i] eq '%T') {
+	    # 	$realFormat = '%s';
+	    # }
+	    print FILE "  write_table_cell (\$class, '$format[$i]', $myLink, $value);\n";
+	    next;
+	}
+
+	if ($label eq "op") {
+	    # create the basic link variable
+	    if ($link[$i]) {
+		print FILE "  \$link = \"$link[$i]\" . \"?menu=$MENU&\" . \$ID['link'];\n";
+		# add extra GET data to target
+		if ($extras[$i]) { &parse_extras ($linkarg{$extras[$i]}); } 
+		$myLink = "\$link";
+	    } else {
+		$myLink = "\"\"";
+	    }
+	    print FILE "  write_table_cell (\$class, '$format[$i]', $myLink, $ops{$value});\n";
+	    next;
+	}
+
+	if (($label eq "image") && !$link[$i]) {
+	    # print the actual table cell line with the image...
+	    print FILE "  echo \"<td class=\\\"\$class\\\">\n";
+	    print FILE "  echo \"              <img src=\\\"$image{$value}\\\"></td>\\n\";\n";
+	    next;
+	}
+
+	if (($label eq "image") && $link[$i]) {
+	    # create the basic link 
+	    print FILE "  \$link = \"$link[$i]\" . \"?menu=$MENU&\" . \$ID['link'];\n";
+	    # add extra php data to target
+	    if ($extras[$i]) { &parse_extras ($linkarg{$extras[$i]}); } 
+	    # print the actual table cell line with the link...
+	    print FILE "  echo \"<td class=\\\"\$class\\\"><a href=\\\"\$link\\\"> <img src=\\\"$image{$value}\\\"> </a></td>\\n\";\n";
+	    next;
+	}
+    }
+}
+
+sub define_fields_string {
+    my (@array) = @_;
+    my ($i, $FIELDS);
+
+    $FIELDS = "$array[0]";
+    for ($i = 1; $i < @array; $i++) {
+	# skip * fields (not a valid query element
+	if ($array[$i] eq "*") { next; }
+	$FIELDS = "$FIELDS,$array[$i]";
+    }
+    set_keypair ("FIELDS", $FIELDS);
+}
+
+sub define_groups_string {
+    my (@array) = @_;
+    my ($i);
+
+    $GROUPS = "$array[0]";
+    for ($i = 1; $i < @array; $i++) {
+	# skip * fields (not a valid query element
+	if ($array[$i] eq "*") { next; }
+	$GROUPS = "$GROUPS,$array[$i]";
+    }
+    set_keypair ("GROUPS", $GROUPS);
+}
+
+sub define_fields_count {
+    my ($i, $Nrow);
+
+    @count = ();
+
+    $Nrow = 0;
+    for ($i = 0; $i < @field; $i++) {
+	# skip * fields (not a valid query element
+	if ($field[$i] eq "*") { 
+	    $count[$i] = -1;
+	} else {
+	    $count[$i] = $Nrow;
+	    $Nrow ++;
+	}
+    }
+}
+
+sub define_where_string {
+    my (@array) = @_;
+    my ($i, $FIELDS);
+
+    if (@array == 0) { return; }
+
+    $WHERE = "$array[0]";
+    for ($i = 1; $i < @array; $i++) {
+	$WHERE = "$WHERE AND $array[$i]";
+    }
+    
+    set_keypair ("WHERE", $WHERE);
+}
+
+sub parse_extras {
+    my ($extras) = $_[0];
+    my (@extfields);
+    my ($label, $value, $outline);
+    my ($i);
+
+    # examine the extras and parse the embedded fields (comma-separated)
+    @extfields = split (/,/, $extras);
+    for ($i = 0; $i < @extfields; $i++) {
+	($label, $value, $outline) = &parse_label ($extfields[$i]);
+	print FILE "  \$link = \$link . \"&$outline\";\n";
+    }
+}
+
+# each linkarg is an entry of the form "word name=value"
+# the entries for matching words are concatenated together
+# separated by commas to create the value $linkarg{word}
+sub parse_linkarg {
+    my ($word, $value);
+
+    ($word, $value) = split (" ", $_[0]);
+
+    if ($linkarg{$word}) {
+	$linkarg{$word} = "$linkarg{$word},$value";
+    } else {
+	$linkarg{$word} = $value;
+    }	
+}
+
+# parse strings of the form label=value
+# if 'value' is of the form $field, try to match with 
+# a supplied DB field, and replace with $row[$n] if found
+sub parse_label {
+    my ($string) = $_[0];
+    my ($label, $value, $outline);
+    my ($Nrow, $i);
+
+    # unless we replace the value with the db row element, return the original string
+    $outline = $string;
+    $label = $string;
+    $value = "";
+
+    # search for embedded table fields and replace with row[N]
+    if ($VERBOSE) { print STDERR "string: $string\n"; }
+    if ($string =~ m|\S+=\S+|) {
+	($label, $value) = $string =~ m|(\S+)=(\S+)|;
+	if ($value =~ m|^\$|) {
+	  MATCH_LABEL:
+	    for ($i = 0; $i < @field; $i++) {
+		if ($field[$i] eq "*") { next; }
+		if ($value =~ m|^\$$fieldreal[$i]|) {
+		    # print "field: $fieldreal[$i]\n";
+		    ($name, $extra) = $value =~ m|^\$($fieldreal[$i])\@(\S+)|;
+		    # print "name: $name\n";
+		    # print "extra: $extra\n";
+		    # print "value: $value\n";
+
+		    $Nrow = $count[$i];
+		    if ($extra) {
+			$value = "\$row[$Nrow]\@$extra";
+		    } else {
+			$value = "\$row[$Nrow]";
+		    }
+		    # print "outval: $value\n";
+		    last MATCH_LABEL;
+		}
+	    }
+	    $outline = "$label=$value";
+	} 
+    }
+    return ($label, $value, $outline);
+}
+
+# given a variable of the form $fieldname, return the corresponding
+# row entry in the form $row[N], otherwise return $fieldname
+# globals: field (list of field names), count (list of sequence)
+sub parse_fieldname {
+    my ($variable) = $_[0];
+    my ($i);
+    
+    # print STDERR "variable: $variable\n";
+
+    unless ($variable =~ m|^\$|) {
+	return $variable;
+    }
+
+    $fieldname = substr ($variable, 1);
+
+    # print STDERR "fieldname: $fieldname\n";
+
+    for ($i = 0; $i < @field; $i++) {
+	# print STDERR "$i: $fieldreal[$i] : $fieldname\n";
+
+	if ($field[$i] eq "*") { next; }
+	if ($fieldname ne $fieldreal[$i]) { next; }
+
+
+	$Nrow = $count[$i];
+
+	$value = "\$row[$Nrow]";
+
+	# print STDERR "found : $i : $count[$i] : $Nrow : $value\n";
+	return $value;
+    }
+    return $variable;
+}
+
+sub parse_imagedef {
+    my ($value) = $_[0];
+
+    ($var, $name, $rule, $camera, $class) = split (" ", $value);
+    $name   = &parse_fieldname ($name);
+    $camera = &parse_fieldname ($camera);
+    $class  = &parse_fieldname ($class);
+    $image{$var} = "getimage.php?name=$name&rule=$rule&camera=$camera&class_id=$class";
+    return 1;
+}
+
+sub parse_opwords {
+    my ($value) = $_[0];
+
+    @words = split (" ", $value);
+    $name = shift (@words);
+
+    $opline = "";
+    @outwords = ();
+    @outfields = ();
+    foreach $word (@words) {
+	$fword = &parse_fieldname ($word);
+	# print "field: $word -> $fword\n";
+	push @outwords, $fword;
+	if ($word =~ m|^\$|) {
+	    $word = substr ($word, 1);
+	}
+	push @outfields, $word;
+    }
+    $outline = join (" ", @outwords);
+    $ops{$name} = $outline;
+    $outline = join (" ", @outfields);
+    $opf{$name} = $outline;
+
+    # print "ops: $name : $ops{$name}\n";
+    # print "opf: $name : $opf{$name}\n";
+
+    return 1;
+}
+
+sub init_key {
+    my ($key)   = $_[0];
+
+    push @key, $key;
+    push @value, "";
+}
+
+sub set_keypair {
+    my ($i);
+    my ($key)   = $_[0];
+    my ($value) = $_[1];
+
+    for ($i = 0; $i < @key; $i++) {
+	if ($key eq $key[$i]) {
+	    if ($value[$i] ne "") { die "key is multiply defined\n"; }
+	    $value[$i] = $value;
+	    if ($VERBOSE) { print STDERR "setting $key = $value\n"; }
+	    return;
+	}
+    }
+}
+
+sub check_key {
+    my ($i);
+    my ($key)     = $_[0];
+    my ($default) = $_[1];
+
+    for ($i = 0; $i < @key; $i++) {
+	if ($key eq $key[$i]) {
+	    if ($VERBOSE) { print "found $key: $key[$i]  -- $value[$i] (def: $default)\n"; }
+	    if (($default eq "") && ($value[$i] eq "")) { die "missing value for required key $key[$i]\n"; }
+	    if ($value[$i] eq "") { $value[$i] = $default; }
+	    return;
+	}
+    }
+    die "unknown key $key\n";
+}
+
+sub check_keypairs {
+    my ($i);
+    for ($i = 0; $i < @key; $i++) {
+	if ($VERBOSE) { print "$key[$i]  -- $value[$i]\n"; }
+	if ($line =~ m|\$$key[$i]|) {
+	    if ($value[$i] eq "") { die "missing value for required key $key[$i]\n"; }
+	    $line =~ s|\$$key[$i]|$value[$i]|g;
+	}
+    }
+}
+
+# we need to find the structure size, including padding 
+# i'm not sure I know the answer to this: it is probably 
+# the total number of bytes rounded up to the largest 
+# data item in the structure (ie, 8 for a double, etc)
+# if we have the size, then we can double check the structure
+# against the expectation at runtime.  for the moment,
+# calculate by hand and add to def.d file 
+
Index: /tags/sj_tags/sj_root_20080929/ippMonitor/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippMonitor/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippMonitor/src/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*.php
Index: /tags/sj_tags/sj_root_20080929/ippScripts/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Build
+META.yml
+Makefile
+Makefile.PL
+_build
+blib
Index: /tags/sj_tags/sj_root_20080929/ippScripts/Build.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/Build.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/Build.PL	(revision 22322)
@@ -0,0 +1,81 @@
+use Module::Build;
+# See perldoc Module::Build for details of how this works
+
+Module::Build->new(
+    module_name         => 'ippscripts',
+    dist_version        => '0.01',
+    author              => 'Joshua Hoblitt <jhoblitt@cpan.org>',
+    license             => 'gpl',
+    create_makefile_pl  => 'passthrough',
+    requires            => {
+        'Astro::FITS::CFITSIO'      => 0,
+        'Cache::File'               => 0,
+        'Carp'                      => 0,
+        'Data::Dumper'              => 0,
+        'File::Basename'            => 0,
+        'File::Spec'                => 0,
+        'File::Temp'                => 0,
+        'Getopt::Long'              => 0,
+        'IPC::Cmd'                  => 0,
+        'IPC::Run'                  => 0,
+        'Math::Trig'                => 0,
+        'Pod::Usage'                => 0,
+        'PS::IPP::Config'           => '1.01',
+        'PS::IPP::Metadata::Config' => 0,
+        'Statistics::Descriptive'   => 0,
+        'Storable'                  => 0,
+        'Sys::Hostname'             => 0,
+    },
+    recommends          => {
+        'Test::Distribution'    => '1.22',
+    },
+    script_files        => [qw(
+        scripts/ipp_datapath.pl
+        scripts/ipp_filename.pl
+        scripts/mdc2list.pl
+        scripts/register_imfile.pl
+        scripts/register_exp.pl
+        scripts/detrend_process_imfile.pl
+        scripts/detrend_process_exp.pl
+        scripts/detrend_stack.pl
+        scripts/detrend_norm_calc.pl
+        scripts/detrend_norm_apply.pl
+        scripts/detrend_norm_exp.pl
+        scripts/detrend_resid_imfile.pl
+        scripts/detrend_resid_exp.pl
+        scripts/detrend_reject_exp.pl
+        scripts/chip_imfile.pl
+        scripts/camera_exp.pl
+        scripts/fake_imfile.pl
+        scripts/warp_overlap.pl
+        scripts/warp_skycell.pl
+        scripts/diff_skycell.pl
+        scripts/stack_skycell.pl
+        scripts/magic_tree.pl
+        scripts/magic_process.pl
+        scripts/magic_mask.pl
+        scripts/ipp_cleanup.pl
+        scripts/ipp_inject_fileset.pl
+        scripts/ipp_inject_expname.pl
+        scripts/ipp_serial_inject.pl
+        scripts/ipp_serial_inject_split.pl
+        scripts/ipp_serial_inject_mosaic.pl
+        scripts/ipp_serial_register.pl
+        scripts/ipp_serial_detrend.pl
+        scripts/ipp_serial_chip.pl
+        scripts/ipp_serial_camera.pl
+        scripts/ipp_serial_warp.pl
+        scripts/ipp_serial_diff.pl
+        scripts/ipp_serial_stack.pl
+        scripts/ipp_serial_mops.pl
+        scripts/ipp_mops_translate.pl
+        scripts/ipp_simulation_data.pl
+        scripts/isp_trans.pl
+        scripts/ds9_cmf_regions.pl
+        scripts/ipp_detrend_combine.pl
+        scripts/summit_copy.pl
+        scripts/ipp_image_path.pl
+    )],
+    dist_abstract => 'Scripts for running the Pan-STARRS IPP',
+    dist_author => 'Paul Price <price@ifa.hawaii.edu>',
+)->create_build_script;
Index: /tags/sj_tags/sj_root_20080929/ippScripts/Changes
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/Changes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/Changes	(revision 22322)
@@ -0,0 +1,6 @@
+Revision history for Perl module ippScripts
+
+0.01 Thu Feb 23 13:55:57 2006
+    - help scripts split out of the ippTools package
+
+
Index: /tags/sj_tags/sj_root_20080929/ippScripts/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/LICENSE	(revision 22322)
@@ -0,0 +1,260 @@
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
Index: /tags/sj_tags/sj_root_20080929/ippScripts/MANIFEST
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/MANIFEST	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/MANIFEST	(revision 22322)
@@ -0,0 +1,38 @@
+Build.PL
+Changes
+LICENSE
+MANIFEST
+README
+scripts/ipp_datapath.pl
+scripts/ipp_filename.pl
+scripts/mdc2list.pl
+scripts/register_imfile.pl
+scripts/register_exp.pl
+scripts/detrend_process_imfile.pl
+scripts/detrend_process_exp.pl
+scripts/detrend_stack.pl
+scripts/detrend_norm_calc.pl
+scripts/detrend_norm_apply.pl
+scripts/detrend_norm_exp.pl
+scripts/detrend_resid_exp.pl
+scripts/detrend_resid_imfile.pl
+scripts/detrend_reject_exp.pl
+scripts/chip_imfile.pl
+scripts/camera_exp.pl
+scripts/warp_overlap.pl
+scripts/warp_skycell.pl
+scripts/diff_skycell.pl
+scripts/stack_skycell.pl
+scripts/ipp_serial_inject.pl
+scripts/ipp_serial_inject_mosaic.pl
+scripts/ipp_serial_register.pl
+scripts/ipp_serial_detrend.pl
+scripts/ipp_serial_chip.pl
+scripts/ipp_serial_camera.pl
+scripts/ipp_serial_warp.pl
+scripts/ipp_serial_diff.pl
+scripts/ipp_serial_stack.pl
+scripts/ipp_simulation_data.pl
+scripts/isp_trans.pl
+scripts/ds9_cmf_regions.pl
+t/00_distribution.t
Index: /tags/sj_tags/sj_root_20080929/ippScripts/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/README	(revision 22322)
@@ -0,0 +1,15 @@
+pod2text foo.pm > README
+
+If this is still here it means the programmer was too lazy to create the readme file.
+
+You can create it now by using the command shown above from this directory.
+
+At the very least you should be able to use this set of instructions
+to install the module...
+
+perl Build.PL
+./Build
+./Build test
+./Build install
+
+If you are on a windows box you should use 'nmake' rather than 'make'.
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/calibrate_dvo.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/calibrate_dvo.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/calibrate_dvo.pl	(revision 22322)
@@ -0,0 +1,196 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Storable qw(freeze thaw);
+use File::Basename qw( basename);
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+use File::Spec;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($cal_id, $dvodb, $region, $dbname, $workdir, $no_update, $no_op);
+GetOptions(
+    'cal_id|i=s'       => \$cal_id,
+    'dvodb|c=s'        => \$dvodb,
+    'region|r=s'       => \$region,
+    'dbname|d=s'       => \$dbname,# Database name
+    'workdir|w=s'      => \$workdir, # Working directory for output files
+    'no-update'        => \$no_update,
+    'no-op'            => \$no_op,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --cal_id --dvodb --region",
+           -exitval => 3) unless
+    defined $cal_id and
+    defined $dvodb and
+    defined $region;
+
+# Look for programs we need
+my $missing_tools;
+my $addstar  = can_run('addstar')  or (warn "Can't find addstar"  and $missing_tools = 1);
+my $relphot  = can_run('relphot')  or (warn "Can't find relphot"  and $missing_tools = 1);
+my $uniphot  = can_run('uniphot')  or (warn "Can't find uniphot"  and $missing_tools = 1);
+my $relastro = can_run('relastro') or (warn "Can't find relastro" and $missing_tools = 1);
+my $caltool  = can_run('caltool')  or (warn "Can't find caltool"  and $missing_tools = 1);
+
+if ($missing_tools) { 
+    warn ("Can't find required tools");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+
+# select the primary filters from DVO query?
+my (@filters) = `photcodeList -average`;
+
+# parse the region (RAs,RAe:DECs,DECe) : item = +/-NNN.NNNN
+my @coords = split (":", $region);
+my ($RAs, $RAe) = split (",", $coords[0]);
+my ($DECs, $DECe) = split (",", $coords[1]);
+
+# Run addstar -resort
+{
+    my $command = "$addstar -resort";
+    $command .= "-D CATDIR $dvodb";
+    $command .= "-region $RAs $RAe $DECs $DECe";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        cache_run(command => $command, verbose => 1);
+
+    unless ($success) { 
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RESORT", $status, $dbname);
+    }
+}
+
+# Run relphot (filter) for each filter
+{
+    foreach my $filter (@filters) {
+	my $command = "$relphot $filter";
+	$command .= "-D CATDIR $dvodb";
+	$command .= "-region $RAs $RAe $DECs $DECe";
+
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    cache_run(command => $command, verbose => 1);
+
+	unless ($success) { 
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    &my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RELPHOT", $status, $dbname);
+	}
+    }
+}
+
+# Run uniphot (filter) for each filter
+# XXX skip this one?  run less frequently?
+if (0) {
+    foreach my $filter (@filters) {
+	my $command = "$uniphot $filter";
+	$command .= "-D CATDIR $dvodb";
+	$command .= "-region $RAs $RAe $DECs $DECe";
+
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    cache_run(command => $command, verbose => 1);
+
+	unless ($success) { 
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    &my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "UNIPHOT", $status, $dbname);
+	}
+    }
+}
+
+{
+    my $command = "$relastro -objects";
+    $command .= "-D CATDIR $dvodb";
+    $command .= "-region $RAs $RAe $DECs $DECe";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	cache_run(command => $command, verbose => 1);
+
+    unless ($success) { 
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RELASTRO.OBJECTS", $status, $dbname);
+    }
+}
+
+{
+    my $command = "$relastro -images";
+    $command .= "-D CATDIR $dvodb";
+    $command .= "-region $RAs $RAe $DECs $DECe";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	cache_run(command => $command, verbose => 1);
+
+    unless ($success) { 
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RELASTRO.IMAGES", $status, $dbname);
+    }
+}
+
+my $command = "$caltool -addrun";
+$command .= " -cal_id $cal_id";
+$command .= " -region $region";
+$command .= " -last_step RELASTRO.IMAGES";
+$command .= " -state 0";
+$command .= " -dbname $dbname" if defined $dbname;
+
+# Push the results into the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => 1);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn ("Unable to perform regtool -addprocessedimfile: $error_code");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $cal_id    = shift;
+    my $region    = shift;
+    my $last_step = shift;
+    my $status 	  = shift;
+    my $dbname 	  = shift;
+
+    carp($msg);
+    if (defined $cal_id && defined $region && defined $last_step && defined $status and not $no_update) {
+        my $command = "$caltool -addcalrun";
+	$command .= " -cal_id $cal_id";
+        $command .= " -region $region";
+	$command .= " -last_step $last_step";
+	$command .= " -state $status";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+# Pau.
+
+END {
+    my $exit = $?;
+    system("sync") == 0 or die "failed to execute sync: $!";
+    $? = $exit;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/camera_exp.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/camera_exp.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/camera_exp.pl	(revision 22322)
@@ -0,0 +1,408 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+use Carp;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Temp qw( tempfile );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ( $exp_tag, $cam_id, $camera, $outroot, $recipe, $dbname, $reduction, $dvodb, $verbose, $no_update,
+     $no_op, $redirect, $save_temps, $run_state);
+GetOptions(
+    'exp_tag=s'          => \$exp_tag, # Exposure identifier
+    'cam_id=s'          => \$cam_id, # Camtool identifier
+    'recipe=s'          => \$recipe, # Recipe to use
+    'camera|c=s'        => \$camera, # Camera
+    'dbname|d=s'        => \$dbname, # Database name
+    'outroot|w=s'       => \$outroot, # output file base name
+    'reduction=s'       => \$reduction, # Reduction class
+    'dvodb|w=s'         => \$dvodb,  # output DVO database
+    'run-state=s'       => \$run_state, # 'new' or 'update'
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update, # Update the database?
+    'no-op'             => \$no_op, # Don't do any operations?
+    'redirect-output'   => \$redirect,
+    'save-temps'        => \$save_temps, # Save temporary files?
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+          -msg => "Required options: --exp_tag --cam_id --camera --outroot",
+          -exitval => 3,
+          ) unless
+    defined $exp_tag and
+    defined $cam_id and
+    defined $outroot and
+    defined $camera;
+
+$ipprc->define_camera($camera);
+
+my $logDest = $ipprc->filename("LOG.EXP", $outroot) or &my_die("Missing entry from camera config", $cam_id, $PS_EXIT_CONFIG_ERROR);
+if ($run_state eq 'update') {
+    $logDest .= '.update';
+}
+
+$ipprc->redirect_output($logDest) if $redirect;
+
+# Recipes to use based on reduction class
+$reduction = 'DEFAULT' unless defined $reduction;
+
+my $recipe1 = $ipprc->reduction($reduction, 'JPEG_BIN1'); # Recipe to use
+&my_die("Unrecognised JPEG recipe", $cam_id, $PS_EXIT_CONFIG_ERROR) unless defined $recipe1;
+
+my $recipe2 = $ipprc->reduction($reduction, 'JPEG_BIN2'); # Recipe to use
+&my_die("Unrecognised JPEG recipe", $cam_id, $PS_EXIT_CONFIG_ERROR) unless defined $recipe2;
+
+my $recipe_addstar = $ipprc->reduction($reduction, 'ADDSTAR'); # Recipe to use
+&my_die("Unrecognised ADDSTAR recipe", $cam_id, $PS_EXIT_CONFIG_ERROR) unless defined $recipe1;
+
+# values to extract from output metadata and the stats to calculate
+# these should be coming from the psastro results
+my $CHIPSTATS =
+    [
+        #          PPSTATS KEYWORD         STATISTIC          CHIPTOOL FLAG
+        { name => "bg",             type => "mean",  flag => "-bg",             dtype => "float" },
+        { name => "bg_stdev",       type => "rms",   flag => "-bg_stdev",       dtype => "float" },
+        { name => "bg_mean_stdev",  type => "stdev", flag => "-bg_mean_stdev",  dtype => "float" },
+        { name => "bias",           type => "mean",  flag => "-bias",           dtype => "float" },
+        { name => "bias_stdev",     type => "rms",   flag => "-bias_stdev",     dtype => "float" },
+        { name => "fringe_0",       type => "mean",  flag => "-fringe_0",       dtype => "float" },
+        { name => "fringe_1",       type => "rms",   flag => "-fringe_1",       dtype => "float" },
+        { name => "fringe_2",       type => "stdev", flag => "-fringe_2",       dtype => "float" },
+#        { name => "sigma_ra",       type => "rms",   flag => "-sigma_ra",       dtype => "float" },
+#        { name => "sigma_dec",      type => "rms",   flag => "-sigma_dec",      dtype => "float" },
+        { name => "ap_resid",       type => "mean",  flag => "-ap_resid",       dtype => "float" },
+        { name => "ap_resid_stdev", type => "rms",   flag => "-ap_resid_stdev", dtype => "float" },
+        { name => "zp_mean",        type => "mean",  flag => "-zp_mean",        dtype => "float" },
+        { name => "zp_stdev",       type => "rms",   flag => "-zp_stdev",       dtype => "float" },
+        { name => "fwhm_major",     type => "mean",  flag => "-fwhm_major",     dtype => "float" },
+        { name => "fwhm_minor",     type => "mean",  flag => "-fwhm_minor",     dtype => "float" },
+        { name => "dtime_detrend",  type => "sum",   flag => "-dtime_detrend",  dtype => "float" },
+        { name => "dtime_photom",   type => "sum",   flag => "-dtime_photom",   dtype => "float" },
+        { name => "dtime_astrom",   type => "sum",   flag => "-dtime_astrom",   dtype => "float" },
+        { name => "n_stars",        type => "sum",   flag => "-n_stars",        dtype => "int"   },
+        { name => "n_extended",     type => "sum",   flag => "-n_extended",     dtype => "int"   },
+        { name => "n_cr",           type => "sum",   flag => "-n_cr",           dtype => "int"   },
+        ];
+my $chipStats = PS::IPP::Metadata::Stats->new($CHIPSTATS); # Stats parser
+
+# values to extract from camera-level output metadata and the stats to calculate
+my $CAMSTATS =
+   [
+       #          PPSTATS KEYWORD         STATISTIC          CHIPTOOL FLAG
+      { name => "CERROR",         type => "rms",   flag => "-sigma_ra",       dtype => "float" },
+      { name => "CERROR",         type => "rms",   flag => "-sigma_dec",      dtype => "float" },
+      { name => "DT_ASTR",        type => "sum",   flag => "-dtime_astrom",   dtype => "float" },
+      { name => "NASTRO",         type => "sum",   flag => "-n_astrom",       dtype => "int"   },
+#     { name => "ZP??",           type => "mean",  flag => "-zp_mean",        dtype => "float" },
+#     { name => "ZP??",           type => "rms",   flag => "-zp_stdev",       dtype => "float" },
+   ];
+my $camStats = PS::IPP::Metadata::Stats->new($CAMSTATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $camtool = can_run('camtool') or (warn "Can't find camtool" and $missing_tools = 1);
+my $ppImage = can_run('ppImage') or (warn "Can't find ppImage" and $missing_tools = 1);
+my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
+
+# test for addstar and psastro:
+my $psastro = can_run('psastro') or (warn "Can't find psastro" and $missing_tools = 1);
+my $addstar = can_run('addstar') or (warn "Can't find addstar" 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
+
+# Get list of component files
+my $files;                      # Array of component files
+{
+    my $command = "$camtool -pendingimfile -cam_id $cam_id"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform camtool: $error_code", $cam_id, $error_code);
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $cam_id, $PS_EXIT_PROG_ERROR);
+
+    # extract the metadata for the files into a hash list
+    $files = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $cam_id, $PS_EXIT_PROG_ERROR);
+
+    # extract the stats from the metadata
+    unless ($chipStats->parse($metadata)) {
+        &my_die("Unable to find all values in statistics output.\n", $cam_id, $PS_EXIT_PROG_ERROR);
+    }
+}
+
+### not needed to have such an extensive temp file name.
+my ($list1File, $list1Name) = tempfile( "/tmp/$exp_tag.cm.$cam_id.b1.list.XXXX", UNLINK => !$save_temps ); # For binning 1
+my ($list2File, $list2Name) = tempfile( "/tmp/$exp_tag.cm.$cam_id.b2.list.XXXX", UNLINK => !$save_temps ); # For binning 2
+my ($list3File, $list3Name) = tempfile( "/tmp/$exp_tag.cm.$cam_id.b3.list.XXXX", UNLINK => !$save_temps ); # For astrometry
+
+# XXX we perform astrometry iff photometry output exists
+my $chipObjectsExist = 0;
+foreach my $file (@$files) {
+    # use the path_base as OUTPUT root and convert the filenames with ipprc->filename:
+    my $class_id = $file->{class_id};
+
+    # If there is only one chip, we use this name for the input to addstar
+    # we expect the chip analysis stage to produce psphot output (cmf file) and two binned images
+    my $chipObjects = $ipprc->filename("PSPHOT.OUTPUT", $file->{path_base}, $class_id);
+
+    # if any of the output chip photometry files exist, we can run psastro / addstar below
+    if ($ipprc->file_exists($chipObjects)) {
+        $chipObjectsExist = 1;
+    } else {
+        if ($verbose) { print "skipping $chipObjects (not found)\n"; }
+        next;
+    }
+
+    print $list1File ($ipprc->filename("PPIMAGE.BIN1", $file->{path_base}, $class_id) . "\n");
+    print $list2File ($ipprc->filename("PPIMAGE.BIN2", $file->{path_base}, $class_id) . "\n");
+    print $list3File ($ipprc->file_resolve($chipObjects, 0) . "\n");
+}
+close $list1File;
+close $list2File;
+close $list3File;
+
+# Output products
+$ipprc->outroot_prepare($outroot);
+
+# the camera configurations should define the psastro output to be a single file (MEF), regardless of the inputs
+my $jpeg1      = $ipprc->filename("PPIMAGE.JPEG1",      $outroot) or &my_die("Missing entry from camera config", $cam_id, $PS_EXIT_CONFIG_ERROR);
+my $jpeg2      = $ipprc->filename("PPIMAGE.JPEG2",      $outroot) or &my_die("Missing entry from camera config", $cam_id, $PS_EXIT_CONFIG_ERROR);
+my $fpaObjects = $ipprc->filename("PSASTRO.OUTPUT",     $outroot) or &my_die("Missing entry from camera config", $cam_id, $PS_EXIT_CONFIG_ERROR);
+my $fpaStats   = $ipprc->filename("PSASTRO.STATS",      $outroot) or &my_die("Missing entry from camera config", $cam_id, $PS_EXIT_CONFIG_ERROR);
+my $traceDest  = $ipprc->filename("TRACE.EXP",          $outroot) or &my_die("Missing entry from camera config", $cam_id, $PS_EXIT_CONFIG_ERROR);
+my $configuration = $ipprc->filename("PSASTRO.CONFIG",  $outroot) or &my_die("Missing entry from camera config", $cam_id, $PS_EXIT_CONFIG_ERROR);
+
+if ($run_state eq 'update') {
+    $traceDest .= '.update';
+    $fpaStats .= '.update';
+}
+
+# convert supplied DVO database name to UNIX filename
+my $dvodbReal;
+if (defined $dvodb) {
+    $dvodbReal = $ipprc->dvo_catdir( $dvodb ); # catdir for DVO
+    $dvodbReal = $ipprc->convert_filename_absolute( $dvodbReal );
+}
+
+
+unless ($no_op) {
+
+    ## build the output JPEG images first so we get them even if the astrometry fails
+
+    # Make the jpeg for binning 1
+    if ($run_state eq 'new') {
+        my $command = "$ppImage -list $list1Name $outroot"; # Command to run
+        $command .= " -recipe PPIMAGE $recipe1";
+        $command .= " -dbname $dbname" if defined $dbname;
+
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform ppImage: $error_code", $cam_id, $error_code);
+        }
+        &my_die("Unable to find expected output file: $jpeg1", $cam_id, $PS_EXIT_PROG_ERROR) unless -f $ipprc->file_resolve($jpeg1);
+    }
+
+    # Make the jpeg for binning 2
+    if ($run_state eq 'new') {
+        my $command = "$ppImage -list $list2Name $outroot"; # Command to run
+        $command .= " -recipe PPIMAGE $recipe2";
+        $command .= " -dbname $dbname" if defined $dbname;
+
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform ppImage: $error_code", $cam_id, $error_code);
+        }
+        &my_die("Unable to find expected output file: $jpeg2", $cam_id, $PS_EXIT_PROG_ERROR) unless -f $ipprc->file_resolve($jpeg2);
+    }
+
+    # only run psastro / addstar if any of the output chip astrometry files exist (should we test for successful astrometry?)
+    if ($chipObjectsExist) {
+        # run psastro on the chipObjects, producing fpaObjects
+        # XXX add a ppStats call which will collect the astrometry stats
+        my $command;
+        $command  = "$psastro -list $list3Name $outroot";
+        $command .= " -tracedest $traceDest -log $logDest";
+        $command .= " -dbname $dbname" if defined $dbname;
+
+        my $do_stats;
+        if ($run_state eq 'new') {
+            $command .= " -stats $fpaStats -recipe PPSTATS CHIPSTATS";
+            $command .= " -dumpconfig $configuration";
+            $do_stats = 1;
+        } elsif ($run_state eq 'update') {
+            $command .= " -ipprc $configuration";
+        } else {
+            &my_die("invalid value for run-state: $run_state", $cam_id, $PS_EXIT_CONFIG_ERROR);
+        }
+
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform psastro: $error_code", $cam_id, $error_code);
+        }
+        # XXX do we want to give an error if astrometry fails here?
+        &my_die("Unable to find expected output file: $fpaObjects", $cam_id, $PS_EXIT_PROG_ERROR) unless -f $ipprc->file_resolve($fpaObjects);
+
+        if ($do_stats) {
+            &my_die("Unable to find expected output file: $fpaStats",   $cam_id, $PS_EXIT_PROG_ERROR) unless -f $ipprc->file_resolve($fpaObjects);
+
+            # Get the statistics on the processed image
+            my $statsFile;              # File handle
+            open $statsFile, $ipprc->file_resolve($fpaStats) or &my_die("Can't open statistics file $fpaStats: $!", $cam_id, $PS_EXIT_SYS_ERROR);
+            my @contents = <$statsFile>; # Contents of file
+            close $statsFile;
+
+            # parse the statistics MDC file
+            my $mdcParser = PS::IPP::Metadata::Config->new();   # Parser for metadata config files
+            my $metadata = $mdcParser->parse(join "", @contents);
+            unless ($metadata) {
+                &my_die("Unable to parse metadata config doc", $cam_id, $PS_EXIT_PROG_ERROR);
+            }
+
+            # extract the stats from the metadata
+            unless ($camStats->parse($metadata)) {
+                &my_die("Failure extracting metadata from the statistics output file.\n", $cam_id, $PS_EXIT_PROG_ERROR);
+            }
+        }
+
+        # run addstar on the output fpaObjects (if a DVO database is defined)
+        if (defined $dvodbReal and ($run_state eq 'new')) {
+
+            ## XXX the camera analysis can either save the full set of
+            ## detections, or just the image metadata, in the dvodb
+
+            ## get the addstar recipe for this camera and CAMERA reduction
+            $command = "$ppConfigDump -camera $camera -recipe ADDSTAR $recipe_addstar -dump-recipe ADDSTAR -";
+            ( $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", $cam_id, $PS_EXIT_SYS_ERROR);
+            }
+            my $recipeData = $mdcParser->parse(join "", @$stdout_buf) or
+                &my_die("Unable to parse metadata config doc", $cam_id, $PS_EXIT_SYS_ERROR);
+
+            ## allow the dvodb to save only images, or the full detection set
+            my $imagesOnly = metadataLookupBool($recipeData, 'IMAGES.ONLY');
+
+            # XXX this construct requires the user to have a valid .ptolemyrc
+            # XXX which in turn points at ippconfig/dvo.site
+            # require a defined output dvo database to run addstar (ie, refuse to use the .ptolemyrc default)
+            # XXX this needs to be converted to addstar_client...
+
+            my $camdir = $ipprc->dvo_cameradir(); # Camera directory for addstar
+            my $command;
+            $command  = "$addstar -D CAMERA $camdir -update";
+            $command .= " -image" if $imagesOnly;
+            $command .= " -D CATDIR $dvodbReal";
+
+            my $realFile = $ipprc->file_resolve($fpaObjects);
+            $command .= " $realFile";
+
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+            unless ($success) {
+                $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+                &my_die("Unable to perform addstar: $error_code", $cam_id, $error_code);
+            }
+        }
+    }
+}
+
+
+my $fpaCommand = "$camtool -cam_id $cam_id";
+if ($run_state eq 'new') {
+    $fpaCommand .= " -addprocessedexp";
+    $fpaCommand .= " -uri UNKNOWN";
+    $fpaCommand .= " -path_base $outroot";
+    $fpaCommand .= $chipStats->cmdflags();
+    $fpaCommand .= $camStats->cmdflags();
+    $fpaCommand .= " -hostname $host" if defined $host;
+} else {
+    $fpaCommand .= " -updaterun -state full";
+}
+$fpaCommand .= " -dbname $dbname" if defined $dbname;
+
+# Add the result into the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $fpaCommand, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to add result to database: $error_code\n");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $fpaCommand\n";
+}
+
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $cam_id = shift; # Camtool identifier
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    if (defined $cam_id and not $no_update) {
+        my $command = "$camtool -cam_id $cam_id";
+        if ($run_state eq 'new') {
+            $command .= " -addprocessedexp";
+            $command .= " -uri UNKNOWN";
+            $command .= " -code $exit_code";
+            $command .= " -path_base $outroot";
+            $command .= " -path_base $outroot" if defined $outroot;
+        } else {
+            $command .= " -updateprocessedexp";
+            $command .= " -code $exit_code";
+        }
+        $command .= " -hostname $host" if defined $host;
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/chip_imfile.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/chip_imfile.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/chip_imfile.pl	(revision 22322)
@@ -0,0 +1,300 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line arguments
+my ( $exp_id, $chip_id, $class_id, $uri, $camera, $outroot, $dbname, $run_state, $reduction, $threads, $verbose,
+     $no_update, $no_op, $redirect );
+GetOptions(
+    'exp_id=s'      	=> \$exp_id,    # Exposure identifier
+    'chip_id=s'     	=> \$chip_id,   # Chiptool identifier
+    'class_id=s'    	=> \$class_id,  # Class identifier
+    'uri|u=s'       	=> \$uri,       # Input FITS file
+    'camera|c=s'    	=> \$camera,    # Camera
+    'outroot|w=s'   	=> \$outroot,   # output file base name
+    'dbname|d=s'    	=> \$dbname,    # Database name
+    'reduction=s'   	=> \$reduction, # Reduction class
+    'run-state=s'       => \$run_state, # current state of the run (new, update)
+    'threads=s'       	=> \$threads,   # Number of threads to use for ppImage
+    'verbose'       	=> \$verbose,   # Print to stdout
+    'no-update'     	=> \$no_update, # Don't update the database?
+    'no-op'         	=> \$no_op,     # Don't do any operations?
+    'redirect-output'   => \$redirect,
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --exp_id --chip_id --class_id --uri --camera --outroot --run-state",
+           -exitval => 3) unless
+    defined $exp_id and
+    defined $chip_id and
+    defined $class_id and
+    defined $uri and
+    defined $camera and
+    defined $outroot and
+    defined $run_state;
+
+$ipprc->define_camera($camera);
+
+my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+$logDest .= ".update" if $run_state eq "update";
+
+$ipprc->redirect_output($logDest) if $redirect;
+
+# Recipes to use based on reduction class
+# the CHIP recipe should not perform astrometry anymore (2008.04.08)
+
+$reduction = 'DEFAULT' unless defined $reduction;
+my $recipe = $ipprc->reduction($reduction, 'CHIP'); # Recipe to use
+unless ($recipe) {
+    &my_die("Couldn't find selected reduction for CHIP: $reduction\n", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+}
+
+# values to extract from output metadata and the stats to calculate
+# XXX commented-out entries are not yet defined in the output files
+# XXX EAM : we are removing the astrometry analysis from chip, so we
+# do not bother to calculate those stats.
+my $STATS =
+   [
+       #          PPSTATS KEYWORD         STATISTIC          CHIPTOOL FLAG
+       { name => "ROBUST_MEDIAN",  type => "mean",  flag => "-bg",             dtype => "float" },
+       { name => "ROBUST_MEDIAN",  type => "stdev", flag => "-bg_mean_stdev",  dtype => "float" },
+       { name => "ROBUST_STDEV",   type => "rms",   flag => "-bg_stdev",       dtype => "float" },
+       { name => "OVER_VAL",       type => "mean",  flag => "-bias",           dtype => "float" },
+       { name => "OVER_VAL",       type => "stdev", flag => "-bias_stdev",     dtype => "float" },
+       { name => "FRINGE_0",       type => "rms",   flag => "-fringe_0",       dtype => "float" },
+       { name => "FRINGE_RESID_0", type => "rms",   flag => "-fringe_1",       dtype => "float" },
+       { name => "FRINGE_ERR_0",   type => "rms",   flag => "-fringe_2",       dtype => "float" },
+#      { name => "CERROR",         type => "rms",   flag => "-sigma_ra",       dtype => "float" },
+#      { name => "CERROR",         type => "rms",   flag => "-sigma_dec",      dtype => "float" },
+       { name => "APMIFIT",        type => "mean",  flag => "-ap_resid",       dtype => "float" },
+       { name => "DAPMIFIT",       type => "rms",   flag => "-ap_resid_stdev", dtype => "float" },
+#      { name => "ZP??",           type => "mean",  flag => "-zp_mean",        dtype => "float" },
+#      { name => "ZP??",           type => "rms",   flag => "-zp_stdev",       dtype => "float" },
+       { name => "FWHM_X",         type => "mean",  flag => "-fwhm_major",     dtype => "float" },
+       { name => "FWHM_Y",         type => "mean",  flag => "-fwhm_minor",     dtype => "float" },
+       { name => "DT_DET",         type => "sum",   flag => "-dtime_detrend",  dtype => "float" },
+       { name => "DT_PHOT",        type => "sum",   flag => "-dtime_photom",   dtype => "float" },
+#       { name => "DT_ASTR",        type => "sum",   flag => "-dtime_astrom",   dtype => "float" },
+       { name => "NSTARS",         type => "sum",   flag => "-n_stars",        dtype => "int"   },
+#      { name => "?",              type => "sum",   flag => "-n_extended",     dtype => "int"   },
+#      { name => "?",              type => "sum",   flag => "-n_cr",           dtype => "int"   },
+#      { name => "NASTRO",         type => "sum",   flag => "-n_astrom",       dtype => "int"   },
+   ];
+my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $chiptool = can_run('chiptool') or (warn "Can't find chiptool" and $missing_tools = 1);
+my $ppImage = can_run('ppImage') or (warn "Can't find ppImage" and $missing_tools = 1);
+my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+&my_die("Couldn't find input file: $uri\n", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($uri);
+
+# outroot examples (HOST components must be set)
+# file://data/ipp004.0/gpc1/20080130
+# neb:///ipp004-v1/gpc1/20080130
+# neb:///*/gpc1/20080130 (volume not specified)
+
+# check for existing directory, generate if needed
+$ipprc->outroot_prepare($outroot);
+
+## these names are used in ppImage, and thus may be URIs
+my $outputImage   = $ipprc->filename("PPIMAGE.CHIP",        $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $outputMask    = $ipprc->filename("PPIMAGE.CHIP.MASK",   $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $outputWeight  = $ipprc->filename("PPIMAGE.CHIP.WEIGHT", $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $outputBin1    = $ipprc->filename("PPIMAGE.BIN1",        $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $outputBin2    = $ipprc->filename("PPIMAGE.BIN2",        $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $outputStats   = $ipprc->filename("PPIMAGE.STATS",       $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $traceDest     = $ipprc->filename("TRACE.IMFILE",        $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $configuration = $ipprc->filename("PPIMAGE.CONFIG",      $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+
+if ($run_state eq 'update') {
+    $outputStats .= '.update';
+    $traceDest .= '.update';
+}
+
+# Run ppImage
+unless ($no_op) {
+    ## XXX can we convert ppImage log and trace to use the filerules to generate consistent names
+    ## XXX also stats: output should be implied by $outroot
+    my $command;
+    my $do_stats;
+
+    if ($run_state eq "new") {
+	$command  = "$ppImage -file $uri $outroot";
+	$command .= " -recipe PPIMAGE $recipe";
+	$command .= " -threads $threads" if defined $threads;
+	$command .= " -dbname $dbname" if defined $dbname;
+        $command .= " -dumpconfig $configuration";
+	$command .= " -tracedest $traceDest -log $logDest";
+        $do_stats = 1;
+    } else {
+	$command  = "$ppImage -file $uri $outroot";
+	$command .= " -ipprc $configuration";
+	$command .= " -threads $threads" if defined $threads;
+	$command .= " -dbname $dbname" if defined $dbname;
+	$command .= " -tracedest $traceDest -log $logDest";
+	$command .= " -Db PPIMAGE:PHOTOM FALSE";
+    }
+    if ($do_stats) {
+	$command .= " -recipe PPSTATS CHIPSTATS";
+	$command .= " -stats $outputStats";
+    }
+
+    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 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 -";
+    ( $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
+    my $outputImageExpect = metadataLookupBool($recipeData, 'CHIP.FITS');
+    if ($outputImageExpect) {
+        &my_die("Couldn't find expected output file: $outputImage\n",  $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputImage);
+    }
+
+    my $outputMaskExpect = metadataLookupBool($recipeData, 'CHIP.MASK.FITS');
+    if ($outputMaskExpect) {
+        &my_die("Couldn't find expected output file: $outputMask\n",   $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputMask);
+    }
+
+    my $outputWeightExpect = metadataLookupBool($recipeData, 'CHIP.WEIGHT.FITS');
+    if ($outputWeightExpect) {
+        &my_die("Couldn't find expected output file: $outputWeight\n", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputWeight);
+    }
+
+    &my_die("Couldn't find expected output file: $outputBin1\n",   $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputBin1);
+    &my_die("Couldn't find expected output file: $outputBin2\n",   $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputBin2);
+
+    if ($do_stats) {
+        &my_die("Couldn't find expected output file: $outputStats\n",  $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputStats);
+
+        # Get the statistics on the processed image
+        my $statsFile;              # File handle
+        open $statsFile, $ipprc->file_resolve($outputStats) or &my_die("Can't open statistics file $outputStats: $!", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+        my @contents = <$statsFile>; # Contents of file
+        close $statsFile;
+
+        # parse the statistics MDC file
+        my $mdcParser = PS::IPP::Metadata::Config->new();   # Parser for metadata config files
+        my $metadata = $mdcParser->parse(join "", @contents);
+        unless ($metadata) {
+            &my_die("Unable to parse metadata config doc", $exp_id, $chip_id, $class_id, $PS_EXIT_PROG_ERROR);
+        }
+
+        # extract the stats from the metadata
+        unless ($stats->parse($metadata)) {
+            &my_die("Failure extracting metadata from the statistics output file.\n", $exp_id, $chip_id, $class_id, $PS_EXIT_PROG_ERROR);
+        }
+    }
+}
+
+my $command;
+if ($run_state eq 'new') {
+    # command to update database
+    $command = "$chiptool -addprocessedimfile";
+    $command .= " -exp_id $exp_id";
+    $command .= " -chip_id $chip_id";
+    $command .= " -class_id $class_id";
+    $command .= " -uri $outputImage";
+    $command .= " -path_base $outroot";
+    $command .= " -hostname $host" if defined $host;
+    $command .= " -dbname $dbname" if defined $dbname;
+    $command .= $stats->cmdflags();
+} else {
+    $command = "$chiptool -tofullimfile";
+    $command .= " -chip_id $chip_id";
+    $command .= " -class_id $class_id";
+    $command .= " -dbname $dbname" if defined $dbname;
+}
+
+# Add the processed file to the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform chiptool -addprocessedimfile: $error_code\n");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $exp_id = shift; # Chiptool identifier
+    my $chip_id = shift; # Chiptool identifier
+    my $class_id = shift; # Class identifier
+    my $exit_code = shift; # Exit code to add
+    # run_state, outputImage, and outroot are globals
+
+    carp($msg);
+    if (defined $chip_id and defined $class_id and not $no_update) {
+        my $command;
+        if ($run_state eq 'new') {
+            $command = "$chiptool -addprocessedimfile";
+            $command .= " -exp_id $exp_id";
+            $command .= " -uri $outputImage";
+            $command .= " -path_base $outroot";
+            $command .= " -hostname $host" if defined $host;
+        } else {
+            $command .= "$chiptool -updateprocessedimfile";
+        }
+        $command .= " -chip_id $chip_id";
+        $command .= " -class_id $class_id";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_correct_imfile.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_correct_imfile.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_correct_imfile.pl	(revision 22322)
@@ -0,0 +1,156 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ( $det_id, $exp_id, $class_id, $det_type, $exp_tag, $input_uri, $camera, $dbname, $workdir, $reduction,
+     $verbose, $no_update, $no_op, $outroot, $redirect, $corr_id, $corr_type, $corr_uri );
+GetOptions(
+    'det_id|d=s'        => \$det_id,
+    'exp_id|e=s'        => \$exp_id,
+    'class_id|i=s'      => \$class_id,
+    'det_type|t=s'      => \$det_type,
+    'exp_tag|=s'        => \$exp_tag,
+    'input_uri|u=s'     => \$input_uri,
+    'corr_id|u=s'       => \$corr_id,
+    'corr_type|u=s'     => \$corr_type,
+    'camera|c=s'        => \$camera,
+    'dbname|d=s'        => \$dbname, # Database name
+    'workdir|w=s'       => \$workdir, # Working directory, for output files
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update,
+    'no-op'             => \$no_op,
+    'outroot'           => \$outroot,
+    'redirect-output'   => \$redirect,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --det_id --exp_id --class_id --det_type --exp_tag --input_uri --camera",
+	   -exitval => 3)
+    unless defined $det_id
+    and defined $exp_id
+    and defined $class_id
+    and defined $det_type
+    and defined $exp_tag
+    and defined $input_uri
+    and defined $camera;
+
+# XXX this exits with status = 0 on failure
+$ipprc->define_camera($camera);
+
+if ($redirect) {
+    die "must suplly --outroot with --redirect-output" if !defined ($outroot);
+    my $logDest     = $ipprc->filename("LOG.IMFILE", $outroot, $class_id)
+       or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+    $ipprc->redirect_output($logDest);
+}
+
+# Recipes to use as a function of detrend type
+$reduction = "DETREND" unless defined $reduction;
+my $recipe = $ipprc->reduction($reduction, uc($det_type) . '_PROCESS'); # Recipe name to use
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $dvocorr = can_run('dvoApplyCorr') or (warn "Can't find dvoApplyCorr" and $missing_tools = 1);
+if ($missing_tools) { 
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+
+&my_die("Couldn't find input file: $input_uri\n", $det_id, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($input_uri);
+
+$workdir = caturi( $workdir, "$camera.$det_type.$det_id" ) if defined $workdir;
+
+# detselect -select -det_id $corr_id -class_id $class_id;
+
+my $outputRoot  = $ipprc->file_prepare( "$exp_tag/$exp_tag.detproc.$det_id", $workdir, $input_uri );
+my $outputImage = $ipprc->filename("DVOCORR.OUTPUT", $outputRoot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
+
+# Run dvoApplyCorr
+unless ($no_op) {
+    my $command = "$dvocorr -file $input_uri $outputRoot";
+    $command .= " -corr $corr_uri";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => $verbose);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform ppImage: $error_code", $det_id, $exp_id, $class_id, $error_code);
+    }
+
+    &my_die("Couldn't find expected output file: $outputImage", $det_id, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($outputImage);
+}
+
+# command to update database
+my $command = "$dettool -addcorrectedimfile";
+$command .= " -det_id $det_id";
+$command .= " -exp_id $exp_id";
+$command .= " -class_id $class_id";
+$command .= " -uri $outputImage";
+$command .= " -path_base $outputRoot";
+$command .= " -dbname $dbname" if defined $dbname;
+
+# Add the processed file to the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => $verbose);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	warn("Unable to perform dettool -addcorrectedimfile: $error_code\n");
+	exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $det_id = shift;		# Detrend identifier
+    my $exp_id = shift; # Exposure tag
+    my $class_id = shift; # Class identifier
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    if (defined $det_id and defined $exp_id and defined $class_id and not $no_update) {
+	my $command = "$dettool -addcorrectedimfile";
+	$command .= " -det_id $det_id";
+	$command .= " -exp_id $exp_id"; 
+	$command .= " -class_id $class_id";
+	$command .= " -path_base $outputRoot";
+	$command .= " -code $exit_code";
+	$command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_norm_apply.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_norm_apply.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_norm_apply.pl	(revision 22322)
@@ -0,0 +1,222 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line
+my ( $det_id, $iter, $class_id, $value, $input_uri, $camera, $det_type, $outroot, $dbname, $verbose,
+     $no_update, $no_op, $redirect );
+GetOptions(
+    'det_id|d=s'        => \$det_id,     # Detrend ID
+    'iteration|n=s'     => \$iter,       # Iteration
+    'class_id|i=s'      => \$class_id,   # Class ID
+    'value|v=s'         => \$value,      # Value to multiple (for normalisation)
+    'input_uri|u=s'     => \$input_uri,  # Input file
+    'camera|c=s'        => \$camera,     # Camera
+    'det_type|t=s'      => \$det_type,   # Detrend type
+    'outroot|w=s'       => \$outroot,    # output file base name
+    'dbname|d=s'        => \$dbname,     # Database name
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update,  # Don't update the database
+    'no-op'             => \$no_op,      # Don't do any operations
+    'redirect-output'   => \$redirect,   # send output from script to LOG.IMFILE
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --det_id --iteration --class_id --value --input_uri --camera --det_type --outroot",
+           -exitval => 3) unless
+    defined $det_id    and
+    defined $iter      and
+    defined $class_id  and
+    defined $value     and
+    defined $input_uri and
+    defined $camera    and
+    defined $det_type  and
+    defined $outroot;
+
+$ipprc->define_camera($camera);
+
+my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id) 
+        or &my_die("Missing entry from camera config", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR);
+$ipprc->redirect_output($logDest) if $redirect;
+
+my $RECIPE_PPIMAGE = 'PPIMAGE_N'; # Recipe to use with ppImage
+
+# Define which detrend types we normalise
+use constant DETTYPE => {
+    'bias'     	       => 'bias',
+    'dark'     	       => 'dark',   
+    'dark_premask'     => 'dark',
+    'shutter'  	       => 'shutter',
+    'flat_premask'     => 'flat',
+    'domeflat_premask' => 'flat',
+    'skyflat_premask'  => 'flat',
+    'flat_raw'         => 'flat',
+    'domeflat_raw'     => 'flat',
+    'skyflat_raw'      => 'flat',   
+    'flat'     	       => 'flat',
+    'domeflat' 	       => 'flat',
+    'skyflat'  	       => 'flat',   
+    'fringe'   	       => 'fringe',   
+    'mask'     	       => 'mask',
+    'darkmask' 	       => 'mask',
+    'flatmask' 	       => 'mask',
+    }; 
+
+# convert supplied detrend type to a controlled namespace
+&my_die("Unrecognised detrend type: $det_type", $det_id, $iter, $class_id, $PS_EXIT_PROG_ERROR) unless exists DETTYPE()->{lc($det_type)};
+my $det_type_real = DETTYPE()->{lc($det_type)};
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppImage = can_run('ppImage') or (warn "Can't find ppImage" and $missing_tools = 1);
+my $ppStatsFromMetadata = can_run('ppStatsFromMetadata') or (warn "Can't find ppStatsFromMetadata" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+&my_die("Couldn't find input file: $input_uri\n", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($input_uri);
+
+# outroot examples (HOST components must be set)
+# file://data/ipp004.0/gpc1/20080130
+# neb:///ipp004-v1/gpc1/20080130
+# neb:///*/gpc1/20080130 (volume not specified)
+
+# check for existing directory, generate if needed
+$ipprc->outroot_prepare($outroot);
+
+my $outFile = ($det_type_real eq "mask") ? 'PPIMAGE.OUTPUT.DETMASK' : 'PPIMAGE.OUTPUT';; # XXXX something of a hack (too many places to control things...)
+
+my $RECIPE_PPSTATS = ($det_type_real eq "dark") ? 'DARKSTATS' : 'CHIPSTATS';; # XXXX something of a hack (too many places to control things...)
+
+my $output    = $ipprc->filename($outFile,        $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $b1name    = $ipprc->filename("PPIMAGE.BIN1",  $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $b2name    = $ipprc->filename("PPIMAGE.BIN2",  $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $statsName = $ipprc->filename("PPIMAGE.STATS", $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $traceDest = $ipprc->filename("TRACE.IMFILE",  $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR);
+
+my $cmdflags;
+
+# Run normalisation
+unless ($no_op) {
+
+    # we cannot use ppImage to load a normalized mask : just copy it and build the jpeg images
+    if ($det_type_real eq 'mask') {
+	$RECIPE_PPIMAGE = 'PPIMAGE_BIN';
+	my $input_real = $ipprc->file_resolve($input_uri, 0);
+	my $output_real = $ipprc->file_resolve($output, 1);
+	system ("cp $input_real $output_real");
+    }
+
+    my $command = "$ppImage -file $input_uri $outroot";
+    $command .= " -norm $value -stats $statsName";
+    $command .= " -recipe PPIMAGE $RECIPE_PPIMAGE";
+    $command .= " -recipe PPSTATS $RECIPE_PPSTATS";
+    $command .= " -F PPIMAGE.OUTPUT $outFile" unless $outFile eq 'PPIMAGE.OUTPUT';
+    $command .= ' -isfringe' if $det_type_real eq 'fringe';
+    $command .= ' -isdark' if $det_type_real eq 'dark';
+    $command .= " -tracedest $traceDest -log $logDest";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform ppImage: $error_code", $det_id, $iter, $class_id, $error_code);
+    }
+    &my_die("Can't find expected output file: $output",    $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($output);
+    &my_die("Can't find expected output file: $b1name",    $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($b1name);
+    &my_die("Can't find expected output file: $b2name",    $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($b2name);
+
+    my $statsNameReal = $ipprc->file_resolve($statsName);
+    &my_die("Can't find expected output file: $statsName", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless -f $statsNameReal;
+
+    # ppStatsFromMetadata $outputStats - DETREND_RESID_IMFILE
+    $command = "$ppStatsFromMetadata $statsNameReal - DETREND_RESID_IMFILE";
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform ppStatsFromMetadata: $error_code", $det_id, $iter, $exp_id, $class_id, $error_code);
+    }
+    foreach my $line (@$stdout_buf) {
+	$cmdflags .= " $line";
+    }
+    chomp $cmdflags;
+}
+
+# Command to update the database
+my $command = "$dettool -addnormalizedimfile";
+$command .= " -det_id $det_id";
+$command .= " -iteration $iter";
+$command .= " -class_id $class_id";
+$command .= " -uri $output";
+$command .= " -path_base $outroot";
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= " $cmdflags";
+
+# Add the processed file to the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform dettool -addnormalizedimfile: $error_code\n");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $det_id = shift;         # Detrend identifier
+    my $iter = shift;           # Iteration
+    my $class_id = shift;       # Class identifier
+    my $exit_code = shift;      # Exit code to add
+
+    carp($msg);
+    if (defined $det_id and defined $iter and defined $class_id and not $no_update) {
+        my $command = "$dettool -addnormalizedimfile";
+        $command .= " -det_id $det_id";
+        $command .= " -iteration $iter";
+        $command .= " -class_id $class_id";
+	$command .= " -path_base $outroot";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_norm_calc.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_norm_calc.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_norm_calc.pl	(revision 22322)
@@ -0,0 +1,253 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run );
+use IPC::Run 0.36 qw( run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse command-line arguments
+my ($det_id, $iter, $detType, $outroot, $dbname, $verbose, $no_update, $no_op, $redirect );
+GetOptions(
+    'det_id|d=s'	=> \$det_id,    # Detrend id			     
+    'iteration|i=s'	=> \$iter,	# Iteration			     
+    'det_type|t=s'  	=> \$detType,	# Detrend type			     
+    'outroot|w=s'   	=> \$outroot,   # output file base name
+    'dbname|d=s'    	=> \$dbname,	# Database name			     
+    'verbose'       	=> \$verbose,   # Print to stdout
+    'no-update'     	=> \$no_update,	# Don't update the database?	     
+    'no-op'         	=> \$no_op,	# Don't do operations                
+    'redirect-output'   => \$redirect,
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options --det_id --iteration --det_type --outroot",
+	   -exitval => 3,
+	   ) unless 
+    defined $det_id  and 
+    defined $iter    and 
+    defined $detType and
+    defined $outroot;
+
+my $logfile = $outroot . ".log";
+
+$ipprc->redirect_output($logfile) if $redirect;
+
+use constant STATISTIC => 'bg'; # Background statistic to use from the database
+
+# Define which detrend types we normalise
+use constant NORMALIZE => {
+    'bias'     	       => 0,
+    'dark'     	       => 0,   
+    'dark_premask'     => 0,
+    'shutter'  	       => 0,
+    'flat_premask'     => 1,
+    'domeflat_premask' => 1,
+    'skyflat_premask'  => 1,
+    'flat_raw'         => 1,
+    'domeflat_raw'     => 1,
+    'skyflat_raw'      => 1,
+    'flat'     	       => 1,
+    'domeflat' 	       => 1,
+    'skyflat'  	       => 1,   
+    'fringe'   	       => 0,   
+    'mask'     	       => 0,
+    'darkmask' 	       => 0,
+    'flatmask' 	       => 0,
+    }; 
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppNormCalc = can_run('ppNormCalc') or (warn "Can't find ppNormCalc" and $missing_tools = 1);
+if ($missing_tools) { 
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+
+&my_die("Unrecognised detrend type: $detType", $det_id, $iter, $PS_EXIT_PROG_ERROR) unless exists NORMALIZE()->{lc($detType)};
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+# Get the list of inputs
+my @files;			# The input files
+{
+    my $command = "$dettool -processedimfile";
+    $command .= " -det_id $det_id"; # Command to run
+    $command .= " -included"; # only use the inputs for this detrend run to calculate the norm
+    $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 dettool -processedimfile on detrend $det_id/$iter: $?",
+		$det_id, $iter, $PS_EXIT_SYS_ERROR);
+    }
+    print $stdout . "\n" if $verbose;
+    
+    # Because of the length, need to split into individual metadatas --- it parses SO much quicker!
+    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 dettool", $det_id, $iter, $PS_EXIT_PROG_ERROR) unless
+		defined $list;
+	    push @files, @$list;
+	    @single = ();
+	}
+    }
+}
+
+my $norms;			# MDC with normalisations
+if (NORMALIZE()->{lc($detType)} and not $no_op) {
+
+    my %matrix; # Matrix of statistics as a function of exposures and classes
+    foreach my $file (@files) {
+	my $exp_id = $file->{'exp_id'}; # Exposure ID
+	my $class_id = $file->{'class_id'}; # Class ID
+	my $stat = $file->{STATISTIC()}; # Statistic of interest
+	
+	# Create matrix elements
+	$matrix{$exp_id} = {} if not defined $matrix{$exp_id};
+	$matrix{$exp_id}->{$class_id} = $stat;
+    }
+    
+    # Generate the input for ppNormCalc
+    my $normData;			# Normalisation data
+    foreach my $exp_id (keys %matrix) {
+	$normData .= "$exp_id\tMETADATA\n";
+	foreach my $class_id (keys %{$matrix{$exp_id}}) {
+	    $normData .= "\t" . $class_id . "\tF32\t" . $matrix{$exp_id}->{$class_id} . "\n";
+	}
+	$normData .= "END\n\n";
+    }
+
+    # Run ppNormCalc
+    {
+	my ( $stdout, $stderr ); # Buffers for running program
+	my @command = split /\s+/, $ppNormCalc;
+	print "Running [$ppNormCalc]...\n" if $verbose;
+	if (not run(\@command, \$normData, \$stdout, \$stderr)) {
+	    &my_die("Unable to perform ppNormCalc: $?", $det_id, $iter, $PS_EXIT_SYS_ERROR);
+	}
+	print $stdout . "\n" if $verbose;
+	
+	# Parse the output
+	$norms = $mdcParser->parse($stdout);
+	&my_die("Unable to parse metadata config doc", $det_id, $iter, $PS_EXIT_PROG_ERROR) unless $norms;
+    }
+
+} else {
+    # It's something that doesn't need normalisation --- just push in a normalisation of 1
+    my %classes;		# List of unique classes
+    foreach my $file (@files) {
+	my $class_id = $file->{'class_id'}; # Class Id
+	$classes{$class_id} = 1;
+    }
+    
+    foreach my $class_id (keys %classes) {
+	my %mdValue;	# Metadata value for this class id
+	$mdValue{name} = $class_id;
+	$mdValue{value} = 1.0;
+	push @$norms, \%mdValue;
+    }
+}
+
+my $commandBase = "$dettool -addnormalizedstat";
+$commandBase .= " -det_id $det_id";
+$commandBase .= " -iteration $iter";
+$commandBase .= " -dbname $dbname" if defined $dbname;
+
+# Process output normalisations
+unless ($no_update) {
+    foreach my $normItem (@$norms) {
+
+	my $className = $normItem->{name}; # Name of component
+	my $normalisation = $normItem->{value}; # Normalisation for component
+
+	if ($normalisation == 0.0 or lc($normalisation) eq 'nan') {
+	    warn("Class $className has bad normalisation: $normalisation");
+	    exit($PS_EXIT_SYS_ERROR);
+	}
+
+	my $command = $commandBase;
+	$command .= " -class_id $className";
+	$command .= " -norm $normalisation";
+
+	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) {
+	    warn("Unable to perform dettool -addnormstat for $className: $?");
+	    exit($PS_EXIT_SYS_ERROR);
+	}
+	print $stdout . "\n" if $verbose;
+    }
+} else {
+    print "skipping command: $commandBase\n";
+    foreach my $normItem (@$norms) {
+
+	my $className = $normItem->{name}; # Name of component
+	my $normalisation = $normItem->{value}; # Normalisation for component
+
+	if ($normalisation == 0.0 or lc($normalisation) eq 'nan') {
+	    warn("Class $className has bad normalisation: $normalisation");
+	    exit($PS_EXIT_SYS_ERROR);
+	}
+	print "$className : $normalisation\n";
+    }
+}
+
+sub my_die
+{
+    my $msg = shift;		# Warning message on die
+    my $det_id = shift;		# Detrend identifier
+    my $iter = shift;		# Iteration
+    my $exit_code = shift;	# Exit code to add
+
+    carp($msg);
+    if (defined $det_id and defined $iter and not $no_update) {
+	my $command = "$dettool -addnormalizedstat";
+	$command .= " -det_id $det_id";
+	$command .= " -iteration $iter";
+	# XXX EAM : we should add this to the db : $command .= " -path_base $outroot";
+	$command .= " -code $exit_code";
+	$command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_norm_exp.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_norm_exp.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_norm_exp.pl	(revision 22322)
@@ -0,0 +1,209 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Config 1.01 qw( :standard );
+use PS::IPP::Metadata::List qw( parse_md_list );
+use File::Temp qw( tempfile );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($det_id, $iter, $det_type, $camera, $outroot, $dbname, $reduction, $verbose, $no_update, $no_op, $redirect, $save_temps);
+GetOptions(
+    'det_id|d=s'        => \$det_id,
+    'iteration|i=s'     => \$iter,
+    'camera|c=s'        => \$camera,
+    'det_type|t=s'      => \$det_type,
+    'outroot|w=s'       => \$outroot,   # output file base name
+    'dbname|d=s'        => \$dbname, # Database name
+    'reduction|=s'      => \$reduction,
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update,
+    'no-op'             => \$no_op,
+    'redirect-output'   => \$redirect,
+    'save-temps'        => \$save_temps, # Save temporary files?
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --det_id --iteration --camera --det_type --outroot",
+           -exitval => 3) unless
+    defined $det_id   and
+    defined $iter     and
+    defined $camera   and
+    defined $det_type and
+    defined $outroot;
+
+my $logfile = $outroot . ".log";
+
+$ipprc->redirect_output($logfile) if $redirect;
+
+$ipprc->define_camera($camera);
+
+# Recipes to use based on reduction class
+$reduction = 'DETREND' unless defined $reduction;
+my $recipe = $ipprc->reduction($reduction, uc($det_type) . '_JPEG_IMAGE');
+&my_die("Unrecognised detrend type: $det_type", $det_id, $iter, $PS_EXIT_PROG_ERROR) unless defined $recipe;
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppImage = can_run('ppImage') or (warn "Can't find ppImage" and $missing_tools = 1);
+my $ppStatsFromMetadata = can_run('ppStatsFromMetadata') or (warn "Can't find ppStatsFromMetadata" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Get list of component files
+my $cmdflags;
+my ($files, $command, $success, $error_code, $full_buf, $stdout_buf, $stderr_buf);
+{
+    $command  = "$dettool -normalizedimfile"; # Command to run
+    $command .= " -det_id $det_id";
+    $command .= " -iteration $iter";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to get list of normalized imfiles from dettool: $error_code", $det_id, $iter, $error_code);
+    }
+    # XXX report an error message if stdout_buf is empty
+
+    # convert stdout to a metadata
+    my $mdcParser = PS::IPP::Metadata::Config->new;     # Parser for metadata config files
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $det_id, $iter, $PS_EXIT_PROG_ERROR);
+
+    # parse the file info in the metadata
+    $files = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $det_id, $iter, $PS_EXIT_PROG_ERROR);
+
+    # since I can't figure out how to do input and output within PERL, I'm writing to a temp file
+    my ($statFile, $statName) = tempfile( "/tmp/$camera.$det_type.norm.$det_id.$iter.stats.XXXX", UNLINK => !$save_temps );
+    foreach my $line (@$stdout_buf) {
+        print $statFile $line;
+    }
+    close $statFile;
+
+    $command = "$ppStatsFromMetadata $statName - DETREND_NORM_EXP";
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform ppStatsFromMetadata: $error_code\n");
+        exit($error_code);
+    }
+
+    foreach my $line (@$stdout_buf) {
+        $cmdflags .= " $line";
+    }
+    print "cmdflags: $cmdflags\n";
+}
+
+my ($list1File, $list1Name) = tempfile( "/tmp/$camera.$det_type.norm.$det_id.$iter.b1.list.XXXX", UNLINK => !$save_temps );
+my ($list2File, $list2Name) = tempfile( "/tmp/$camera.$det_type.norm.$det_id.$iter.b2.list.XXXX", UNLINK => !$save_temps );
+foreach my $file (@$files) {
+    print $list1File ( $ipprc->filename( "PPIMAGE.BIN1", $file->{path_base}, $file->{class_id} ) . "\n");
+    print $list2File ( $ipprc->filename( "PPIMAGE.BIN2", $file->{path_base}, $file->{class_id} ) . "\n");
+}
+close $list1File;
+close $list2File;
+
+# outroot examples (HOST components must be set)
+# file://data/ipp004.0/gpc1/20080130
+# neb:///ipp004-v1/gpc1/20080130
+# neb:///*/gpc1/20080130 (volume not specified)
+
+# check for existing directory, generate if needed
+$ipprc->outroot_prepare($outroot);
+
+my $jpeg1Name = $ipprc->filename("PPIMAGE.JPEG1", $outroot); # Binned JPEG #1
+my $jpeg2Name = $ipprc->filename("PPIMAGE.JPEG2", $outroot); # Binned JPEG #2
+
+unless ($no_op) {
+    # Make the jpeg for binning 1
+    $command = "$ppImage -list $list1Name $outroot"; # Command to run
+    $command .= " -recipe PPIMAGE PPIMAGE_J1";
+    $command .= " -recipe JPEG $recipe";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    &my_die("Unable to find expected output file: $jpeg1Name", $det_id, $iter, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($jpeg1Name);
+
+    # Make the jpeg for binning 2
+    $command = "$ppImage -list $list2Name $outroot"; # Command to run
+    $command .= " -recipe PPIMAGE PPIMAGE_J2";
+    $command .= " -recipe JPEG $recipe";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    &my_die("Unable to find expected output file: $jpeg2Name", $det_id, $iter, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($jpeg2Name);
+
+}
+
+# command to update the database
+$command  = "$dettool -addnormalizedexp";
+$command .= " -det_id $det_id";
+$command .= " -iteration $iter";
+$command .= " -recip $recipe";
+$command .= " -path_base $outroot ";
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= " $cmdflags";
+
+# Add the processed file to the database
+unless ($no_update) {
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform dettool -addnormalizedexp: $error_code", $det_id, $iter, $error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $det_id = shift;         # Detrend identifier
+    my $iter = shift;           # Iteration
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    if (defined $det_id and defined $iter and not $no_update) {
+        my $command = "$dettool -addnormalizedexp";
+        $command .= " -det_id $det_id";
+        $command .= " -iteration $iter";
+        $command .= " -path_base $outroot ";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_process_exp.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_process_exp.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_process_exp.pl	(revision 22322)
@@ -0,0 +1,217 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Config 1.01 qw( :standard );
+use PS::IPP::Metadata::List qw( parse_md_list );
+use File::Temp qw( tempfile );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ( $det_id, $exp_id, $det_type, $exp_tag, $camera, $outroot, $dbname, $reduction, $verbose, $no_update,
+     $no_op, $save_temps, $redirect );
+GetOptions(
+    'det_id|d=s'        => \$det_id,
+    'det_type|t=s'      => \$det_type,
+    'exp_id|d=s'        => \$exp_id,
+    'exp_tag|=s'        => \$exp_tag,
+    'camera|c=s'        => \$camera,
+    'outroot|w=s'       => \$outroot,   # output file base name
+    'dbname|d=s'        => \$dbname, # Database name
+    'reduction|=s'      => \$reduction,
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update,
+    'no-op'             => \$no_op,
+    'save-temps'        => \$save_temps, # Save temporary files?
+    'redirect-output'   => \$redirect,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --det_id --det_type --exp_id --exp_tag --camera --outroot",
+           -exitval => 3) unless
+    defined $det_id   and
+    defined $det_type and
+    defined $exp_id   and
+    defined $exp_tag  and
+    defined $camera   and
+    defined $outroot;
+
+$ipprc->define_camera($camera);
+
+if ($redirect) {
+    my $logDest = $ipprc->filename("LOG.EXP", $outroot, "NONE")
+        or &my_die("Missing entry in file rules", $det_id, $exp_id, $PS_EXIT_CONFIG_ERROR);
+    $ipprc->redirect_output($logDest);
+}
+
+# Recipes to use based on reduction class
+$reduction = 'DETREND' unless defined $reduction;
+
+my $recipe = $ipprc->reduction($reduction, uc($det_type) . '_JPEG_IMAGE'); # Recipe to use
+&my_die("Unrecognised detrend type: $det_type", $det_id, $exp_id, $PS_EXIT_PROG_ERROR) unless defined $recipe;
+
+# values to extract from output metadata and the stats to calculate
+# XXX -bg_mean_stdev should take rms of bg_mean_stdev if bg_mean_stdev != 0 (A)
+# XXX -bg_mean_stdev should take stdev of bg_mean if bg_mean_stdev == 0     (B)
+# XXX  (A) if imfile.Ncomp > 1, (B) if imfile.Ncomp == 1
+my $STATS =
+   [
+       #          KEYWORD                 STATISTIC          CHIPTOOL FLAG
+       { name => "bg",             type => "mean",  flag => "-bg",            dtype => "float" },
+       { name => "bg",             type => "stdev", flag => "-bg_mean_stdev", dtype => "float" },
+       { name => "bg_stdev",       type => "rms",   flag => "-bg_stdev",      dtype => "float" },
+       # { name => "bg_mean_stdev",  type => "rms",   flag => "-bg_mean_stdev" },
+   ];
+my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppImage = can_run('ppImage') or (warn "Can't find ppImage" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Get list of component files
+my ($files, $command, $success, $error_code, $full_buf, $stdout_buf, $stderr_buf);
+{
+    $command = "$dettool -processedimfile -det_id $det_id -exp_id $exp_id"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform dettool -processedimfile: $error_code", $det_id, $exp_id, $error_code);
+    }
+
+    # convert stdout to a metadata
+    my $mdcParser = PS::IPP::Metadata::Config->new;     # Parser for metadata config files
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $det_id, $exp_id, $PS_EXIT_PROG_ERROR);
+
+    # parse the file info in the metadata
+    $files = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $det_id, $exp_id, $PS_EXIT_PROG_ERROR);
+
+    # parse the stats in the metadata
+    unless ($stats->parse($metadata)) {
+        &my_die("Unable to find all values in statistics output.\n", $det_id, $exp_id, $PS_EXIT_PROG_ERROR);
+    }
+}
+
+# File lists for the two binnings
+my ($list1File, $list1Name) = tempfile( "/tmp/$exp_tag.detproc.$det_id.b1.list.XXXX", UNLINK => !$save_temps );
+my ($list2File, $list2Name) = tempfile( "/tmp/$exp_tag.detproc.$det_id.b2.list.XXXX", UNLINK => !$save_temps );
+foreach my $file (@$files) {
+    print $list1File ($ipprc->filename( "PPIMAGE.BIN1", $file->{path_base}, $file->{class_id} ) . "\n");
+    print $list2File ($ipprc->filename( "PPIMAGE.BIN2", $file->{path_base}, $file->{class_id} ) . "\n");
+}
+close $list1File;
+close $list2File;
+
+# outroot examples (HOST components must be set)
+# file://data/ipp004.0/gpc1/20080130
+# neb:///ipp004-v1/gpc1/20080130
+# neb:///*/gpc1/20080130 (volume not specified)
+
+# check for existing directory, generate if needed
+$ipprc->outroot_prepare($outroot);
+
+my $jpeg1 = $ipprc->filename("PPIMAGE.JPEG1", $outroot); # Binned JPEG #1
+my $jpeg2 = $ipprc->filename("PPIMAGE.JPEG2", $outroot); # Binned JPEG #2
+
+unless ($no_op) {
+    # Make the jpeg for binning 1
+    $command = "$ppImage -list $list1Name $outroot"; # Command to run
+    $command .= " -recipe PPIMAGE PPIMAGE_J1";
+    $command .= " -recipe JPEG $recipe";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform ppImage: $error_code", $det_id, $exp_id, $error_code);
+    }
+    &my_die("Unable to find expected output file: $jpeg1", $det_id, $exp_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($jpeg1);
+
+    # Make the jpeg for binning 2
+    $command = "$ppImage -list $list2Name $outroot"; # Command to run
+    $command .= " -recipe PPIMAGE PPIMAGE_J2";
+    $command .= " -recipe JPEG $recipe";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform ppImage: $error_code", $det_id, $exp_id, $error_code);
+    }
+    &my_die("Unable to find expected output file: $jpeg2", $det_id, $exp_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($jpeg2);
+}
+
+# Command to update the database
+$command  = "$dettool -addprocessedexp";
+$command .= " -det_id $det_id";
+$command .= " -exp_id $exp_id";
+$command .= " -recip $recipe -path_base $outroot";
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= $stats->cmdflags();
+
+# Add the processed file to the database
+unless ($no_update) {
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform dettool -addprocessedexp: $error_code");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $det_id = shift;         # Detrend identifier
+    my $exp_id = shift; # Exposure tag
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    if (defined $det_id and defined $exp_id and not $no_update) {
+        my $command = "$dettool -addprocessedexp";
+        $command .= " -det_id $det_id";
+        $command .= " -exp_id $exp_id";
+        $command .= " -code $exit_code";
+        $command .= " -path_base $outroot";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_process_imfile.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_process_imfile.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_process_imfile.pl	(revision 22322)
@@ -0,0 +1,194 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ( $det_id, $exp_id, $class_id, $det_type, $exp_tag, $input_uri, $camera, $outroot, $dbname, $reduction,
+     $threads, $verbose, $no_update, $no_op, $redirect );
+GetOptions(
+    'det_id|d=s'        => \$det_id,
+    'exp_id|e=s'        => \$exp_id,
+    'class_id|i=s'      => \$class_id,
+    'det_type|t=s'      => \$det_type,
+    'exp_tag|=s'        => \$exp_tag,
+    'input_uri|u=s'     => \$input_uri,
+    'camera|c=s'        => \$camera,
+    'outroot|w=s'       => \$outroot, # output file base name
+    'dbname|d=s'        => \$dbname, # Database name
+    'reduction=s'       => \$reduction, # Reduction class
+    'threads=s'         => \$threads,   # Number of threads to use for ppImage
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update,
+    'no-op'             => \$no_op,
+    'redirect-output'   => \$redirect,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --det_id --exp_id --class_id --det_type --exp_tag --input_uri --camera --outroot",
+           -exitval => 3) unless
+    defined $det_id    and
+    defined $exp_id    and
+    defined $class_id  and
+    defined $det_type  and
+    defined $exp_tag   and
+    defined $input_uri and
+    defined $camera    and
+    defined $outroot;
+
+# XXX this exits with status = 0 on failure
+$ipprc->define_camera($camera);
+
+my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+
+$ipprc->redirect_output($logDest) if $redirect;
+
+# Recipes to use as a function of detrend type
+$reduction = "DETREND" unless defined $reduction;
+my $ppimage_recipe = $ipprc->reduction($reduction, uc($det_type) . '_PROCESS'); # Recipe name for ppImage
+my $jpeg_recipe = $ipprc->reduction($reduction, uc($det_type) . '_JPEG_IMAGE'); # Recipe name for JPEG
+
+# values to extract from output metadata and the stats to calculate
+my $STATS =
+   [
+       #          PPSTATS KEYWORD         STATISTIC          CHIPTOOL FLAG
+       { name => "ROBUST_MEDIAN",  type => "mean",  flag => "-bg",            dtype => "float" },
+       { name => "ROBUST_MEDIAN",  type => "stdev", flag => "-bg_mean_stdev", dtype => "float" },
+       { name => "ROBUST_STDEV",   type => "rms",   flag => "-bg_stdev",      dtype => "float" },
+   ];
+my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppImage = can_run('ppImage') or (warn "Can't find ppImage" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+&my_die("Couldn't find input file: $input_uri\n", $det_id, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($input_uri);
+
+# outroot examples (HOST components must be set)
+# file://data/ipp004.0/gpc1/20080130
+# neb:///ipp004-v1/gpc1/20080130
+# neb:///*/gpc1/20080130 (volume not specified)
+
+# check for existing directory, generate if needed
+$ipprc->outroot_prepare($outroot);
+
+my $outputImage = $ipprc->filename("PPIMAGE.OUTPUT", $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
+my $outputBin1  = $ipprc->filename("PPIMAGE.BIN1",   $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
+my $outputBin2  = $ipprc->filename("PPIMAGE.BIN2",   $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
+my $outputStats = $ipprc->filename("PPIMAGE.STATS",  $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
+my $traceDest   = $ipprc->filename("TRACE.IMFILE",   $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $exp_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+# Run ppImage
+unless ($no_op) {
+    my $command = "$ppImage -file $input_uri $outroot";
+    $command .= " -recipe PPIMAGE $ppimage_recipe";
+    $command .= " -recipe JPEG $jpeg_recipe";
+    $command .= " -recipe PPSTATS CHIPSTATS";
+    $command .= " -stats $outputStats";
+    $command .= " -tracedest $traceDest -log $logDest";
+    $command .= " -threads $threads" if defined $threads;
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform ppImage: $error_code", $det_id, $exp_id, $class_id, $error_code);
+    }
+
+    &my_die("Couldn't find expected output file: $outputImage", $det_id, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($outputImage);
+    &my_die("Couldn't find expected output file: $outputStats", $det_id, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($outputStats);
+    &my_die("Couldn't find expected output file: $outputBin1",  $det_id, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($outputBin1);
+    &my_die("Couldn't find expected output file: $outputBin2",  $det_id, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($outputBin2);
+
+    # Get the statistics on the processed image
+    my $statsFile;              # File handle
+    open $statsFile, $ipprc->file_resolve("$outputStats") or die "Can't open statistics file $outputStats: $!\n";
+    my @contents = <$statsFile>; # Contents of file
+    close $statsFile;
+
+    # parse the statistics MDC file
+    my $mdcParser = PS::IPP::Metadata::Config->new;     # Parser for metadata config files
+    my $metadata = $mdcParser->parse(join "", @contents)
+        or &my_die("Unable to parse metadata config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
+
+    # extract the stats from the metadata
+    $stats->parse($metadata) or &my_die("Unable to find all values in statistics output.", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
+}
+
+# command to update database
+my $command = "$dettool -addprocessedimfile";
+$command .= " -det_id $det_id";
+$command .= " -exp_id $exp_id";
+$command .= " -class_id $class_id";
+$command .= " -recip $reduction";
+$command .= " -uri $outputImage -path_base $outroot";
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= $stats->cmdflags();
+
+# Add the processed file to the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform dettool -addprocessedimfile: $error_code\n");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $det_id = shift;         # Detrend identifier
+    my $exp_id = shift; # Exposure tag
+    my $class_id = shift; # Class identifier
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    if (defined $det_id and defined $exp_id and defined $class_id and not $no_update) {
+        my $command = "$dettool -addprocessedimfile";
+        $command .= " -det_id $det_id";
+        $command .= " -exp_id $exp_id";
+        $command .= " -class_id $class_id";
+        $command .= " -path_base $outroot";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_reject_exp.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_reject_exp.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_reject_exp.pl	(revision 22322)
@@ -0,0 +1,371 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Config 1.01 qw( :standard );
+use PS::IPP::Metadata::List qw( parse_md_list );
+use Statistics::Descriptive;
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+my $ITER_LIMIT = 20;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($det_id, $iter, $det_type, $camera, $outroot, $filter, $dbname, $verbose, $no_update, $no_op, $redirect);
+GetOptions(
+    'det_id|d=s'        => \$det_id,
+    'iteration=s'       => \$iter,
+    'det_type|t=s'      => \$det_type,
+    'camera=s'          => \$camera,
+    'outroot|w=s'       => \$outroot,   # output file base name
+    'filter=s'          => \$filter,
+    'dbname|d=s'        => \$dbname, # Database name
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update,
+    'no-op'             => \$no_op,
+    'redirect-output'   => \$redirect,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --det_id --iteration --det_type --camera --outroot",
+           -exitval => 3) unless
+    defined $det_id   and
+    defined $iter     and
+    defined $det_type and
+    defined $camera   and
+    defined $outroot;
+
+# check for existing directory, generate if needed
+$ipprc->outroot_prepare($outroot);
+
+my $logName = "$outroot.log"; # Name for log
+
+$ipprc->redirect_output($logName) if $redirect;
+
+# values to extract from output metadata and the stats to calculate
+# XXX -bg_mean_stdev should take rms of bg_mean_stdev if bg_mean_stdev != 0 (A)
+# XXX -bg_mean_stdev should take stdev of bg_mean if bg_mean_stdev == 0     (B)
+# XXX  (A) if imfile.Ncomp > 1, (B) if imfile.Ncomp == 1
+my $STATS =
+   [
+       #          KEYWORD                 STATISTIC          CHIPTOOL FLAG
+       { name => "bg",             type => "mean",  flag => "-bg",            dtype => "float" },
+       { name => "bg_mean_stdev",  type => "stdev", flag => "-bg_mean_stdev", dtype => "float" },
+       { name => "bg_stdev",       type => "rms",   flag => "-bg_stdev",      dtype => "float" },
+   ];
+my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# these stats are used it the rejections but not passed to the database
+# there is some duplication with the above, but the calculation time is minimal
+my $REJSTATS =
+   [
+       #          KEYWORD                 STATISTIC          CHIPTOOL FLAG
+       { name => "bg",             type => "clipmean",  flag => "ensMeanMean",       dtype => "float" },
+       { name => "bg",             type => "clipstdev", flag => "ensMeanStdev",      dtype => "float" },
+       { name => "bg_mean_stdev",  type => "clipmean",  flag => "ensMeanStdevMean",  dtype => "float" },
+       { name => "bg_mean_stdev",  type => "clipstdev", flag => "ensMeanStdevStdev", dtype => "float" },
+       { name => "bg_stdev",       type => "clipmean",  flag => "ensStdevMean",      dtype => "float" },
+       { name => "bg_stdev",       type => "clipstdev", flag => "ensStdevStdev",     dtype => "float" },
+       { name => "bg_skewness",    type => "clipmean",  flag => "ensSkewness",       dtype => "float" },
+       { name => "bg_kurtosis",    type => "clipmean",  flag => "ensKurtosis",       dtype => "float" },
+
+   ];
+my $rejstats = PS::IPP::Metadata::Stats->new($REJSTATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Get list of component files
+my ($exposures, $command, $success, $error_code, $full_buf, $stdout_buf, $stderr_buf);
+{
+    # dettool command to select exp data for this det_run
+    $command = "$dettool -residexp";
+    $command .= " -det_id $det_id";
+    $command .= " -iteration $iter";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform dettool -residexp: $error_code", $det_id, $iter, $error_code);
+    }
+
+    # Parse the stdout buffer into a metadata
+    my $mdcParser = PS::IPP::Metadata::Config->new;     # Parser for metadata config files
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $det_id, $iter, $PS_EXIT_PROG_ERROR);
+
+    # parse the file info in the metadata
+    $exposures = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $det_id, $iter, $PS_EXIT_PROG_ERROR);
+
+    # Parse the statistics on the residual image
+    $stats->parse($metadata) or &my_die("Unable to find all values in statistics output.", $det_id, $iter, $PS_EXIT_PROG_ERROR);
+
+    # Parse the statistics for rejections
+    $rejstats->parse($metadata) or &my_die("Unable to find all values in statistics output.", $det_id, $iter, $PS_EXIT_PROG_ERROR);
+}
+
+# each image has a mean mean (average of means over all chips)
+# a standard deviation mean (average of standard deviations over all chips)
+# and a
+
+
+
+# we use the statistics of the ensemble to accept/reject exposurs
+my $ensMeanMean       = $rejstats->value_for_flag ("ensMeanMean");       # average of all exposure means (in turn averaged over all chips)
+my $ensMeanStdev      = $rejstats->value_for_flag ("ensMeanStdev");      # standard deviation of all exposure means
+my $ensMeanStdevMean  = $rejstats->value_for_flag ("ensMeanStdevMean");  # average over all exposures of the stdev of the means for each chip
+my $ensMeanStdevStdev = $rejstats->value_for_flag ("ensMeanStdevStdev"); # standard deviation over all exposures of the stdev of the means for each chip
+my $ensStdevMean      = $rejstats->value_for_flag ("ensStdevMean");      # average over all exposures of the sum of the squares of the stdevs for each chip
+my $ensStdevStdev     = $rejstats->value_for_flag ("ensStdevStdev");     # standard deviation over all exposures of the sum of the squares of the stdevs for each chip
+
+$ipprc->define_camera($camera);
+# Rejection thresholds
+my $reject_mean      = rejection_limit( 'ENSEMBLE.MEAN',      $det_type, $filter );
+my $reject_stdev     = rejection_limit( 'ENSEMBLE.STDEV',     $det_type, $filter );
+my $reject_meanstdev = rejection_limit( 'ENSEMBLE.MEANSTDEV', $det_type, $filter );
+
+# outroot examples (HOST components must be set)
+# file://data/ipp004.0/gpc1/20080130
+# neb:///ipp004-v1/gpc1/20080130
+# neb:///*/gpc1/20080130 (volume not specified)
+
+unless ($no_op) {
+    print "Ensemble mean $ensMeanMean +/- $ensMeanStdev\n";
+    print "Ensemble stdev $ensStdevMean +/- $ensStdevStdev\n";
+    print "Ensemble mean rms (over imfiles) $ensMeanStdevMean +/- $ensMeanStdevStdev\n\n";
+}
+
+# Go through again to do rejection, and update the database for each exposure
+my $numChanges = 0;             # Number of exposures with changed status
+my $numReject = 0;              # Number of exposures rejected
+my $firstElement = 1;
+
+foreach my $exposure (@$exposures) {
+    my $mean      = $exposure->{bg};    # Mean for this exposure
+    my $stdev     = $exposure->{bg_stdev}; # Stdev for this exposure
+    my $meanStdev = $exposure->{bg_mean_stdev}; # Stdev of Means for this exposure
+    my $expID     = $exposure->{exp_id};
+    my $accept    = $exposure->{accept};
+    my $include   = $exposure->{include};
+
+    &my_die("Unable to find exposure id.\n", $det_id, $iter, $PS_EXIT_SYS_ERROR) unless defined $expID;
+    &my_die("Unable to find accept.\n",      $det_id, $iter, $PS_EXIT_SYS_ERROR) unless defined $accept;
+    &my_die("Unable to find include.\n",     $det_id, $iter, $PS_EXIT_SYS_ERROR) unless defined $include;
+
+    my $reject = 0;             # Reject this exposure?
+
+    $command  = "$dettool -updateresidexp";
+    $command .= " -det_id $det_id";
+    $command .= " -iteration $iter";
+    $command .= " -exp_id $expID";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    if (not $accept) {
+        # Rejected this at an earlier stage
+        unless ($no_op) {
+            print "Rejecting $expID based on earlier determination.\n";
+        }
+        $reject = 1;
+        goto UPDATE;
+    }
+
+    # Cop-out if we're not doing any operations
+    if ($no_op) {
+        # Make sure something gets rejected (just once!), just so that
+        # we can trace the full range of the workflow
+        if ($firstElement and $iter == 0) {
+            $reject = 1;
+        }
+        goto UPDATE;
+    }
+
+    if ($reject_mean > 0 and $ensMeanStdev > 0) {
+        my $delta = abs($mean - $ensMeanMean);
+        if ($delta > ($reject_mean * $ensMeanStdev)) {
+            print "Rejecting $expID based on ensemble mean value: ";
+            $reject = 1;
+            #goto UPDATE;
+        } else {
+            print "$expID OK against ensemble mean: ";
+        }
+        print "$mean --> $delta vs " . $reject_mean * $ensMeanStdev . "\n";
+    } else {
+        print "No rejection of $expID for ensemble mean\n";
+    }
+
+    if ($reject_stdev > 0 and $ensStdevStdev > 0) {
+        my $delta = abs($stdev - $ensStdevMean);
+        if ($delta > ($reject_stdev * $ensStdevStdev)) {
+            print "Rejecting $expID based on ensemble stdev: ";
+            $reject = 1;
+            #goto UPDATE;
+        } else {
+            print "$expID OK against ensemble stdev: ";
+        }
+        print "$stdev --> $delta sigma vs " . $reject_stdev * $ensStdevStdev . "\n";
+    } else {
+        print "No rejection of $expID for ensemble stdev\n";
+    }
+
+    if ($reject_meanstdev > 0 and $ensMeanStdevStdev > 0) {
+        my $delta = abs($meanStdev - $ensMeanStdevMean);
+        if ($delta > ($reject_meanstdev * $ensMeanStdevStdev)) {
+            print "Rejecting $expID based on ensemble mean stdev: ";
+            $reject = 1;
+            #goto UPDATE;
+        } else {
+            print "$expID OK against ensemble mean stdev: ";
+        }
+        print "$meanStdev --> $delta sigma vs " . $reject_meanstdev * $ensMeanStdevStdev. "\n";
+    } else {
+        print "No rejection of $expID for ensemble mean stdev\n";
+    }
+
+  UPDATE:
+    if ($reject) {
+        $command .= ' -reject';
+        $numReject++;
+    }
+
+    # Check for status changes
+    if ((not $include and not $reject) or ($include and $reject)) {
+        unless ($no_op) {
+            print "Status of $expID has changed.\n";
+        }
+        $numChanges++;
+    }
+
+    unless ($no_update) {
+        # Update
+        ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform dettool -updateresidexp: $error_code", $det_id, $iter, $error_code);
+        }
+    }
+}
+
+# Decide if the current is sufficient to use as a master, and if we can stop iterating
+my $master = 1;                 # This is good enough for a master
+my $stop = 1;                   # Stop iterating
+
+if ($numChanges > 0) {
+    $master = 0;
+    $stop = 0;
+}
+
+# Rejecting everything --- stop before something bad happens!
+if ($numReject == scalar @$exposures) {
+    $master = 0;
+    $stop = 1;
+    carp "All inputs rejected!\n";
+}
+
+unless ($no_op) {
+    print "Master: $master\n";
+    print "Stop: $stop\n";
+}
+
+# Allow iteration to be turned off
+my $allow_iter = metadataLookupBool($ipprc->{rejection}, "ITERATION"); # Allow iteration?
+my $force_master = metadataLookupBool($ipprc->{rejection}, "MASTER"); # Force the stack to be accepted
+$stop = 1 unless $allow_iter;
+$master = 1 if $force_master and $stop;
+
+# attempt to prevent endless, pathological iterations
+if ($iter >= $ITER_LIMIT) {
+    warn("iteration limit reached -- bailing out");
+    exit($PS_EXIT_PROG_ERROR);
+}
+
+## add the summary statistics, and request a new iteration if needed
+$command = "$dettool -adddetrunsummary";
+$command .= " -det_id $det_id";
+$command .= " -iteration $iter";
+$command .= " -accept" if $master;
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= " -again" unless $stop;
+$command .= $stats->cmdflags();
+
+# Put results into the database
+unless ($no_update) {
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform dettool -adddetrunsummary: $error_code");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $det_id = shift;         # Detrend identifier
+    my $iter = shift;           # Iteration
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    if (defined $det_id and defined $iter and not $no_update) {
+        my $command = "$dettool -adddetrunsummary";
+        $command .= " -det_id $det_id";
+        $command .= " -iteration $iter";
+	# XXX EAM : we should add this to the db : $command .= " -path_base $outroot";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+# Retrieve the requested rejection limit, dying unless extant
+sub rejection_limit
+{
+    my $name = shift;           # Rejection limit to
+    my $type = shift;           # Type of exposure
+    my $filter = shift;         # Filter
+
+    my $value = $ipprc->rejection( $name, $det_type, $filter );
+    if (not defined $value) {
+        $filter = "(no filter)" unless defined $filter;
+        die "Unable to determine $name rejection limit for $det_type with $filter.\n";
+    }
+
+    return $value;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_resid_exp.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_resid_exp.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_resid_exp.pl	(revision 22322)
@@ -0,0 +1,613 @@
+#!/usr/bin/env perl
+
+# this program has two jobs:
+# 1: for the given exp_tag, generate the binned & mosaiced JPEG images
+# 2: examine the collection of per-imfile statistics and reject.  At the moment,
+#    this program (and the database) only allows rejection at the exposure level,
+#    not at the imfile level.
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );             # tools to run UNIX programs with control over I/O
+# use IPC::Run qw ( start finish timeout );
+use IPC::Run;
+
+use PS::IPP::Metadata::Config;                   # tools to parse the psMetadataConfig files
+
+use PS::IPP::Metadata::List qw( parse_md_list ); # tools to parse a metadata into a hash list
+use Statistics::Descriptive;                     # tools for calculating basic statistical quantities
+use File::Temp qw( tempfile );                   # tools to construct temp files
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt ); # option parsing
+use Pod::Usage qw( pod2usage );
+
+# parse the command-line options
+my ( $det_id, $iter, $exp_id, $exp_tag, $det_type, $camera, $filter, $reject, $outroot, $dbname, $reduction,
+     $verbose, $no_update, $no_op, $save_temps, $redirect );
+GetOptions(
+           'det_id|d=s'        => \$det_id,
+           'iteration=s'       => \$iter,
+           'exp_id|e=s'        => \$exp_id,
+           'exp_tag|=s'        => \$exp_tag,
+           'det_type|t=s'      => \$det_type,
+           'camera=s'          => \$camera,
+           'outroot|w=s'       => \$outroot,   # output file base name
+           'filter=s'          => \$filter,
+           'reject'            => \$reject,
+           'dbname|d=s'        => \$dbname, # Database name
+           'reduction|=s'      => \$reduction,
+           'verbose'           => \$verbose,   # Print to stdout
+           'no-update'         => \$no_update,
+           'no-op'             => \$no_op,
+           'save-temps'        => \$save_temps, # Save temporary files?
+           'redirect-output'   => \$redirect,   # redirect output to LOG.EXP
+           ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --det_id --iteration --exp_id --exp_tag --det_type --camera --outroot",
+           -exitval => 3) unless
+    defined $det_id   and
+    defined $iter     and
+    defined $exp_id   and
+    defined $exp_tag  and
+    defined $det_type and
+    defined $camera   and
+    defined $outroot;
+
+# load IPP config information for the specified camera
+$ipprc->define_camera($camera);
+
+my $logDest = $ipprc->filename("LOG.EXP", $outroot) or &my_die("Missing entry from camera config", $det_id, $iter, $exp_id, $PS_EXIT_CONFIG_ERROR);
+
+$ipprc->redirect_output($logDest) if $redirect;
+
+# Recipes to use based on reduction class
+$reduction = 'DETREND' unless defined $reduction;
+
+my $recipe = $ipprc->reduction($reduction, uc($det_type) . '_JPEG_RESID'); # Recipe to use
+&my_die("Unrecognised detrend type: $det_type", $det_id, $iter, $PS_EXIT_PROG_ERROR) unless defined $recipe;
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppImage = can_run('ppImage') or (warn "Can't find ppImage" and $missing_tools = 1);
+my $ppStatsFromMetadata = can_run('ppStatsFromMetadata') or (warn "Can't find ppStatsFromMetadata" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Get list of imfile files
+my $cmdflags;
+my ($files, $command, $success, $error_code, $full_buf, $stdout_buf, $stderr_buf);
+{
+    # dettool command to select imfile data for this exp_id
+    $command  = "$dettool -residimfile";
+    $command .= " -det_id $det_id";
+    $command .= " -iteration $iter";
+    $command .= " -exp_id $exp_id";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform dettool -residimfile: $error_code\n");
+        exit($error_code);
+    }
+    # XXX report an error message if stdout_buf is empty
+
+    # Parse the stdout buffer into a metadata
+    my $mdcParser = PS::IPP::Metadata::Config->new;
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $det_id, $iter, $exp_id, $PS_EXIT_PROG_ERROR);
+
+    # parse the file info in the metadata
+    $files = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $det_id, $iter, $exp_id, $PS_EXIT_PROG_ERROR);
+
+    # since I can't figure out how to do input and output within PERL, I'm writing to a temp file
+    my ($statFile, $statName) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.stats.XXXX", UNLINK => !$save_temps );
+    foreach my $line (@$stdout_buf) {
+        print $statFile $line;
+    }
+    close $statFile;
+
+    $command = "$ppStatsFromMetadata $statName - DETREND_RESID_EXP";
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform ppStatsFromMetadata: $error_code\n");
+        exit($error_code);
+    }
+
+    foreach my $line (@$stdout_buf) {
+        $cmdflags .= " $line";
+    }
+    print "cmdflags: $cmdflags\n";
+}
+
+# outroot examples (HOST components must be set)
+# file://data/ipp004.0/gpc1/20080130
+# neb:///ipp004-v1/gpc1/20080130
+# neb:///*/gpc1/20080130 (volume not specified)
+
+# check for existing directory, generate if needed
+$ipprc->outroot_prepare($outroot);
+
+my $jpeg1Name  = $ipprc->filename("PPIMAGE.JPEG1", $outroot); # Binned JPEG #1
+my $jpeg2Name  = $ipprc->filename("PPIMAGE.JPEG2", $outroot); # Binned JPEG #2
+
+# XXX in debug mode, unlink = 0
+my ($list1File, $list1Name) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.b1.list.XXXX", UNLINK => !$save_temps );
+my ($list2File, $list2Name) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.b2.list.XXXX", UNLINK => !$save_temps );
+foreach my $file (@$files) {
+    print $list1File ($ipprc->filename( "PPIMAGE.BIN1", $file->{path_base}, $file->{class_id} ) . "\n");
+    print $list2File ($ipprc->filename( "PPIMAGE.BIN2", $file->{path_base}, $file->{class_id} ) . "\n");
+}
+close $list1File;
+close $list2File;
+
+
+# build the JPEG images
+unless ($no_op) {
+    # Make the jpeg for binning 1
+    $command = "$ppImage -list $list1Name $outroot"; # Command to run
+    $command .= " -recipe PPIMAGE PPIMAGE_J1";
+    $command .= " -recipe JPEG $recipe";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to run ppImage: $error_code", $det_id, $iter, $exp_id, $error_code);
+    }
+    &my_die("Unable to find expected output file: $jpeg1Name", $det_id, $iter, $exp_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($jpeg1Name);
+
+    # Make the jpeg for binning 2
+    $command = "$ppImage -list $list2Name $outroot"; # Command to run
+    $command .= " -recipe PPIMAGE PPIMAGE_J2";
+    $command .= " -recipe JPEG $recipe";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to run ppImage: $error_code", $det_id, $iter, $exp_id, $error_code);
+    }
+    &my_die("Unable to find expected output file: $jpeg2Name", $det_id, $iter, $exp_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($jpeg2Name);
+}
+
+#### measure stats and reject if needed ####
+
+# Rejection thresholds & related data
+my $expected                = rejection_limit( 'EXPECTED',         $det_type, $filter );
+my $reject_imfile_mean      = rejection_limit( 'IMFILE.MEAN',      $det_type, $filter );
+my $reject_imfile_flux      = rejection_limit( 'IMFILE.FLUX',      $det_type, $filter );
+my $reject_imfile_stdev     = rejection_limit( 'IMFILE.STDEV',     $det_type, $filter );
+my $reject_imfile_skewness  = rejection_limit( 'IMFILE.SKEWNESS',  $det_type, $filter );
+my $reject_imfile_kurtosis  = rejection_limit( 'IMFILE.KURTOSIS',  $det_type, $filter );
+my $reject_imfile_meanstdev = rejection_limit( 'IMFILE.MEANSTDEV', $det_type, $filter );
+my $reject_imfile_snr       = rejection_limit( 'IMFILE.SNR',       $det_type, $filter );
+my $reject_imfile_bin_stdev = rejection_limit( 'IMFILE.BIN.STDEV', $det_type, $filter );
+my $reject_imfile_bin_snr   = rejection_limit( 'IMFILE.BIN.SNR',   $det_type, $filter );
+my $reject_exp_mean         = rejection_limit( 'EXP.MEAN',         $det_type, $filter );
+my $reject_exp_flux         = rejection_limit( 'EXP.FLUX',         $det_type, $filter );
+my $reject_exp_stdev        = rejection_limit( 'EXP.STDEV',        $det_type, $filter );
+my $reject_exp_meanstdev    = rejection_limit( 'EXP.MEANSTDEV',    $det_type, $filter );
+my $reject_exp_snr          = rejection_limit( 'EXP.SNR',          $det_type, $filter );
+my $reject_exp_bin_stdev    = rejection_limit( 'EXP.BIN.STDEV',    $det_type, $filter );
+my $reject_exp_bin_snr      = rejection_limit( 'EXP.BIN.SNR',      $det_type, $filter );
+
+# storage array
+my @fluxes;
+
+foreach my $file (@$files) {
+    my $name      = $file->{class_id};
+    my $mean      = $file->{bg};        # Mean for this imfile
+    my $stdev     = $file->{bg_stdev}; # Stdev for this imfile
+    my $skewness  = $file->{bg_skewness}; # Skewness for this imfile
+    my $kurtosis  = $file->{bg_kurtosis}; # Kurtosis for this imfile
+    my $meanStdev = $file->{bg_mean_stdev}; # Stdev of Means for this imfile
+    my $binStdev  = $file->{bin_stdev}; # Binned Stdev for this imfile
+
+    # calculate and save the fluxes
+    my $flux;
+    if ($file->{exp_time} == 0.0) {
+        $flux = $mean;
+    } else {
+        $flux = $mean / $file->{exp_time};
+    }
+    push @fluxes, $flux;
+
+    $mean -= $expected;
+
+    last if $no_op;
+
+    # reject exposure if, for any imfiles, the mean residual counts
+    # deviate from the expected value by more than N sigma, (sigma =
+    # total pixel variance).  this test is sensible for images which
+    # should have a predictable nominal residual mean count value (eg,
+    # 0.0 for a bias).   in general, use with ADDITIVE components
+    if ($reject_imfile_mean > 0) {
+        if (abs($mean) > $reject_imfile_mean * $stdev) {
+            print "Rejecting exposure based on bad imfile mean for $name: ";
+            $reject = 1;
+        } else {
+            print "Imfile mean for $name meets requirements: ";
+        }
+        print "$mean vs $reject_imfile_mean * $stdev\n";
+    }  else {
+        print "No rejection on imfile mean for $name\n";
+    }
+
+    # reject exposure if, for any imfiles, the mean residual flux
+    # deviates from the expected value by more than N sigma, (sigma =
+    # total pixel variance).  this test is sensible for images which
+    # should have a predictable nominal residual flux value (eg, 0.0
+    # for a bias).  in general, use with ADDITIVE components
+    if ($reject_imfile_flux > 0) {
+        if (abs($flux) > $reject_imfile_flux) {
+            print "Rejecting exposure based on bad imfile flux for $name: ";
+            $reject = 1;
+        } else {
+            print "Imfile flux for $name meets requirements: ";
+        }
+        print "$flux vs $reject_imfile_flux\n";
+    }  else {
+        print "No rejection on imfile flux for $name\n";
+    }
+
+    # reject exposure if, for any imfiles, the total pixel variance is
+    # larger than the limit
+    if ($reject_imfile_stdev > 0) {
+        if ($stdev > $reject_imfile_stdev) {
+            print "Rejecting exposure based on bad imfile stdev for $name: ";
+            $reject = 1;
+        } else {
+            print "Imfile stdev for $name meets requirements: ";
+        }
+        print "$stdev vs $reject_imfile_stdev\n";
+
+    } else {
+        print "No rejection on imfile stdev for $name\n";
+    }
+
+    # reject exposure if, for any imfiles, the skewness is
+    # larger than the limit
+    if ($reject_imfile_skewness > 0) {
+        if ($stdev > $reject_imfile_skewness) {
+            print "Rejecting exposure based on bad imfile skewness for $name: ";
+            $reject = 1;
+        } else {
+            print "Imfile skewness for $name meets requirements: ";
+        }
+        print "$skewness vs $reject_imfile_skewness\n";
+
+    } else {
+        print "No rejection on imfile skewness for $name\n";
+    }
+
+    # reject exposure if, for any imfiles, the kurtosis is
+    # larger than the limit
+    if ($reject_imfile_kurtosis > 0) {
+        if ($stdev > $reject_imfile_kurtosis) {
+            print "Rejecting exposure based on bad imfile kurtosis for $name: ";
+            $reject = 1;
+        } else {
+            print "Imfile kurtosis for $name meets requirements: ";
+        }
+        print "$kurtosis vs $reject_imfile_kurtosis\n";
+
+    } else {
+        print "No rejection on imfile kurtosis for $name\n";
+    }
+
+    # reject exposure if, for any imfiles, the variance of the imfile
+    # component means is larger than the limit
+    if ($reject_imfile_meanstdev > 0) {
+        if ($meanStdev > $reject_imfile_meanstdev) {
+            print "Rejecting exposure based on bad imfile mean stdev for $name: ";
+            $reject = 1;
+        } else {
+            print "Imfile mean stdev for $name meets requirements: ";
+        }
+        print "$meanStdev vs $reject_imfile_meanstdev\n";
+    } else {
+        print "No rejection on imfile mean stdev for $name\n";
+    }
+
+    # reject exposure if, for any imfiles, the signal-to-noise (ie,
+    # the mean counts / total pixel variance) of the imfile component
+    # means are less than the limit.  this test is sensible for images
+    # which have finite residual flux such as a flat-field image.
+    if ($reject_imfile_snr > 0) {
+        if ($mean < $stdev * $reject_imfile_snr) {
+            print "Rejecting exposure based on bad imfile S/N for $name: ";
+            $reject = 1;
+        } else {
+            print "Imfile S/N for $name meets requirements: ";
+        }
+        print "mean: $mean vs stdev*SNlimit: " . $stdev * $reject_imfile_snr . "\n";
+    } else {
+        print "No rejection on imfile S/N for $name\n";
+    }
+
+    # reject exposure if, for any imfiles, the signal-to-noise of the
+    # imfile component means, based on the stdev of the binned image
+    # is less than the limit.  this test is sensible for images which
+    # have finite residual flux such as a flat-field image.
+    if ($reject_imfile_bin_stdev > 0) {
+        if ($binStdev > $reject_imfile_bin_stdev) {
+            print "Rejecting exposure based on bad imfile binned stdev for $name: ";
+            $reject = 1;
+        } else {
+            print "Imfile binned stdev for $name meets requirements: ";
+        }
+        print "$binStdev vs $reject_imfile_bin_stdev\n";
+    } else {
+        print "No rejection on imfile binned stdev for $name\n";
+    }
+    if ($reject_imfile_bin_snr > 0) {
+        if ($mean < $binStdev * $reject_imfile_bin_snr) {
+            print "Rejecting exposure based on bad imfile binned S/N for $name: ";
+            $reject = 1;
+        } else {
+            print "Imfile binned S/N for $name meets requirements: ";
+        }
+        print "mean: $mean vs binStdev*SNlimit: " . $binStdev * $reject_imfile_bin_snr . "\n";
+    } else {
+        print "No rejection on imfile binned S/N for $name\n";
+    }
+}
+
+# basic ensemble stats
+my $mean               = &value_for_flag ($cmdflags, "-bg");
+my $meanStdev          = &value_for_flag ($cmdflags, "-bg_mean_stdev");
+my $stdev              = &value_for_flag ($cmdflags, "-bg_stdev");
+my $binStdev           = &value_for_flag ($cmdflags, "-bin_stdev");
+my $fringe_mean        = &value_for_flag ($cmdflags, "-fringe_0");
+my $fringe_err         = &value_for_flag ($cmdflags, "-fringe_1");
+my $fringe_mean_stdev  = &value_for_flag ($cmdflags, "-fringe_2");
+my $dfringe_mean       = &value_for_flag ($cmdflags, "-fringe_resid_0");
+my $dfringe_err        = &value_for_flag ($cmdflags, "-fringe_resid_1");
+my $dfringe_mean_stdev = &value_for_flag ($cmdflags, "-fringe_resid_2");
+
+# other stats (flux depends on bg and exp_time)
+my $fluxStats = Statistics::Descriptive::Sparse->new(); # Statistics calculator for means
+$fluxStats->add_data(@fluxes);
+my $flux = $fluxStats->mean();  # Mean of the imfile means
+
+# other stats
+my $exp_snr = 0.0;
+if ($stdev > 0) { $exp_snr = $mean / $stdev; }
+
+## Reject based on the exposure ensemble stats
+# reject if the exposure ensemble mean is deviant
+unless ($no_op) {
+    print "Exposure mean $mean, stdev $stdev, mean stdev $meanStdev, exp s/n: $exp_snr\n";
+
+    # reject exposure if the ensemble mean residual counts deviate
+    # from the expected value by more than N sigma, (sigma = total
+    # pixel variance).  this test is sensible for images which should
+    # have a predictable nominal residual mean count value (eg, 0.0
+    # for a bias).  in general, use with ADDITIVE components
+    if ($reject_exp_mean > 0) {
+        if (abs($mean) > $reject_exp_mean * $stdev) {
+            print "Rejecting exposure based on bad mean counts: ";
+            $reject = 1;
+        } else {
+            print "Exposure mean counts meets requirements: ";
+        }
+        print "mean: $mean, stdev: $stdev (limit is: " . $reject_exp_mean * $stdev . ") \n";
+    } else {
+        print "No rejection for exp mean\n";
+    }
+
+    # reject exposure if, for any imfiles, the mean residual flux
+    # deviates from the expected value by more than N sigma, (sigma =
+    # total pixel variance).  this test is sensible for images which
+    # should have a predictable nominal residual flux value (eg, 0.0
+    # for a bias).  in general, use with ADDITIVE components
+    if ($reject_exp_flux > 0) {
+        if (abs($flux) > $reject_exp_flux * $stdev) {
+            print "Rejecting exposure based on bad mean flux: ";
+            $reject = 1;
+        } else {
+            print "Exposure mean flux meets requirements: ";
+        }
+        print "flux: $flux, stdev: $stdev (limit is: " . $reject_exp_flux * $stdev . ")\n";
+    } else {
+        print "No rejection for exp mean\n";
+    }
+
+    # reject exposure if the total pixel variance is larger than the
+    # limit
+    if ($reject_exp_stdev > 0) {
+        if ($stdev > $reject_exp_stdev) {
+            print "Rejecting exposure based on bad stdev: ";
+            $reject = 1;
+        } else {
+            print "Exposure stdev meets requirements: ";
+        }
+        print "$stdev vs $reject_exp_stdev\n";
+    } else {
+        print "No rejection for exp stdev\n";
+    }
+
+    # reject exposure if the variance of the imfile means is larger
+    # than the limit
+    if ($reject_exp_meanstdev > 0) {
+        if ($meanStdev > $reject_exp_meanstdev) {
+            print "Rejecting exposure based on bad mean stdev: ";
+            $reject = 1;
+        } else {
+            print "Exposure mean stdev meets requirements: ";
+        }
+        print "$meanStdev vs $reject_exp_meanstdev\n";
+    } else {
+        print "No rejection for exp mean stdev\n";
+    }
+
+    # reject if the signal-to-noise is insufficient
+    if ($reject_exp_snr > 0) {
+        if (abs($mean) < abs($stdev * $reject_exp_snr)) {
+            print "Rejecting exposure based on poor S/N: \n";
+            $reject = 1;
+        } else {
+            print "Exposure S/N meets requirements: \n";
+        }
+        print "signal: $mean vs noise: $stdev (s/n limit is: $reject_exp_snr)\n";
+    } else {
+        print "No rejection for exp S/N\n";
+    }
+    # reject if the exposure ensemble stdev is deviant
+    if ($reject_exp_bin_stdev > 0) {
+        if ($binStdev > $reject_exp_bin_stdev) {
+            print "Rejecting exposure based on bad binned stdev: ";
+            $reject = 1;
+        } else {
+            print "Exposure binned stdev meets requirements: ";
+        }
+        print "$stdev vs $reject_exp_bin_stdev\n";
+    } else {
+        print "No rejection for exp binned stdev\n";
+    }
+    # reject if the signal-to-noise is insufficient
+    if ($reject_exp_bin_snr > 0) {
+        if (abs($mean) < abs($binStdev * $reject_exp_bin_snr)) {
+            print "Rejecting exposure based on poor binned S/N: \n";
+            $reject = 1;
+        } else {
+            print "Exposure binned S/N meets requirements: \n";
+        }
+        print "signal: $mean vs noise: $binStdev (s/n limit is: $reject_exp_bin_snr)\n";
+    } else {
+        print "No rejection for exp binned S/N\n";
+    }
+}
+
+$command  = "$dettool -addresidexp -det_id $det_id -iteration $iter -exp_id $exp_id";
+$command .= " -recip $recipe -path_base $outroot ";
+$command .= ' -reject' if $reject;
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= " $cmdflags";
+
+unless ($no_update) {
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform dettool -addresidexp: $error_code\n");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+
+sub value_for_flag
+{
+    my $cmdflags = shift;
+    my $flag = shift;
+
+    my $value = 0.0;
+    if ($cmdflags =~ m|$flag|) {
+        ($value) = $cmdflags =~ m|$flag\s+(\S+)|;
+    }
+    $value;
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $det_id = shift;         # Detrend identifier
+    my $iter = shift;           # Iteration
+    my $exp_id = shift; # Exposure tag
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    if (defined $det_id and defined $iter and defined $exp_id and not $no_update) {
+        my $command = "$dettool -addresidexp";
+        $command .= " -det_id $det_id";
+        $command .= " -iteration $iter";
+        $command .= " -exp_id $exp_id";
+        $command .= " -path_base $outroot";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+# Retrieve the requested rejection limit, dying if not extant
+sub rejection_limit
+{
+    my $name = shift;           # Rejection limit to
+    my $type = shift;           # Type of exposure
+    my $filter = shift;         # Filter
+
+    my $value = $ipprc->rejection( $name, $det_type, $filter );
+    if (not defined $value) {
+        $filter = "(no filter)" if not defined $filter;
+        die "Unable to determine $name rejection limit for $det_type with $filter.\n";
+    }
+
+    return $value;
+}
+
+__END__
+
+####
+
+## this function is not well though out.  it loads a collection of
+## values (background mean and stdev) for a collection of imfile
+## subcomponents.  it then measures the mean background, the
+## background stdev (rms of ensemble stdevs), and the background mean
+## stdev (rms of the means).  it then tries to reject based on the
+## following criteria:
+## (background / stdev > limit) for a single component
+##    ---> this will ACCEPT for a poor images (large stdev)
+## (stdev > limit)
+## (mean background / stdev > limit) same problem
+## (mean stdev > limit)
+
+### I would suggest the following:
+## 1) calculate the ensemble mean and stdev
+## 2) for each component, is the (back - mean) / mean stdev > limit?
+##    (ie, is it an outlier?)
+## 3) is the component stdev > limit? (absolute test)
+## 4) calculate the background stdev for the ensemble (excluding rejects)
+## 5) is the mean stdev > limit (poor images)
+## 6) calculate the ensemble stdev (rms) of remaining (excluding rejects)
+## 7) is the ensemble stdev > limit
+
+## for flats, the value used for stdev should be the fractional stdev
+## (stdev/mean) and no rejection should be performed on the basis of
+## the mean value, but could still be done on the mean stdev
+
+## the same logical cuts above can be applied to components in an
+## imfile, imfiles in an exposure, and exposures in a stack
+
+### this function needs to avoid div by zero: compare Signal vs Noise*limit
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_resid_imfile.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_resid_imfile.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_resid_imfile.pl	(revision 22322)
@@ -0,0 +1,258 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ( $det_id, $iter, $ref_det_id, $ref_iter, $exp_id, $exp_tag, $class_id, $det_type, $detrend, $input_uri, $camera, $mode, $outroot,
+     $dbname, $reduction, $threads, $verbose, $no_update, $no_op, $redirect );
+GetOptions(
+    'det_id|d=s'        => \$det_id,
+    'iteration=s'       => \$iter,
+    'ref_det_id=s'      => \$ref_det_id,
+    'ref_iter=s'        => \$ref_iter,
+    'exp_id|e=s'        => \$exp_id,
+    'exp_tag|=s'        => \$exp_tag,
+    'class_id|i=s'      => \$class_id,
+    'det_type|t=s'      => \$det_type,
+    'detrend=s'         => \$detrend,
+    'input_uri|u=s'     => \$input_uri,
+    'camera|c=s'        => \$camera,
+    'mode|m=s'          => \$mode,
+    'outroot|w=s'       => \$outroot,   # output file base name
+    'dbname|d=s'        => \$dbname, # Database name
+    'reduction=s'       => \$reduction, # Reduction class
+    'threads=s'         => \$threads,   # Number of threads to use for ppImage
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update,
+    'no-op'             => \$no_op,
+    'redirect-output'   => \$redirect,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --det_id --iteration --ref_det_id --ref_iter --exp_id --exp_tag --class_id --det_type --camera --input_uri --mode --detrend --outroot (not for 'verify' mode)",
+           -exitval => 3) unless
+    defined $det_id     and
+    defined $iter       and
+    defined $ref_det_id and
+    defined $ref_iter   and
+    defined $exp_id     and
+    defined $exp_tag    and
+    defined $class_id   and
+    defined $det_type   and
+    defined $input_uri  and
+    defined $camera     and
+    defined $mode       and
+    defined $outroot    and
+    defined $detrend;
+
+$ipprc->define_camera($camera);
+my $logDest     = $ipprc->filename("LOG.IMFILE", $outroot, $class_id);
+if ($redirect) {
+    $ipprc->redirect_output($logDest);
+}
+
+# Recipes to use as a function of detrend type and mode
+# XXX probably can drop the distinct 'verify' recipes
+$reduction = 'DETREND' unless defined $reduction;
+my $recipe;                     # Name of recipe to use
+if ($mode eq 'master') {
+    $recipe = uc($det_type) . '_RESID';
+} elsif ($mode eq 'verify') {
+    $recipe = uc($det_type) . '_VERIFY';
+} else {
+    &my_die("Unrecognised mode: $mode", $det_id, $iter, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
+}
+
+print "raw recipe: $recipe\n";
+my $ppimage_recipe = $ipprc->reduction($reduction, $recipe);
+my $jpeg_recipe = $ipprc->reduction($reduction, uc($det_type) . '_JPEG_RESID');
+print "real recipe: $recipe\n";
+
+# Flags to specify the particular detrend to use
+use constant DETRENDS => {
+    'bias'             => '-bias',      # Specify the bias frame
+    'dark'             => '-dark',      # Specify the dark frame
+    'dark_premask'     => '-dark',      # Specify the dark frame
+    'shutter'          => '-shutter',   # Specify the shutter frame
+    'flat_premask'     => '-flat',      # Specify the flat frame
+    'domeflat_premask' => '-flat',      # Specify the flat frame
+    'skyflat_premask'  => '-flat',      # Specify the flat frame
+    'flat_raw'         => '-flat',      # Specify the flat frame
+    'domeflat_raw'     => '-flat',      # Specify the flat frame
+    'skyflat_raw'      => '-flat',      # Specify the flat frame
+    'flat'             => '-flat',      # Specify the flat frame
+    'domeflat'         => '-flat',      # Specify the flat frame
+    'skyflat'          => '-flat',      # Specify the flat frame
+    'fringe'           => '-fringe',    # Specify the fringe frame
+    'mask'             => '-mask',      # Specify the mask frame
+    'darkmask'         => '-mask',      # Specify the mask frame
+    'flatmask'         => '-mask',      # Specify the mask frame
+};
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppImage = can_run('ppImage') or (warn "Can't find ppImage" and $missing_tools = 1);
+my $ppStats = can_run('ppStats') or (warn "Can't find ppStats" and $missing_tools = 1);
+my $ppStatsFromMetadata = can_run('ppStatsFromMetadata') or (warn "Can't find ppStatsFromMetadata" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# outroot examples (HOST components must be set)
+# file://data/ipp004.0/gpc1/20080130
+# neb:///ipp004-v1/gpc1/20080130
+# neb:///*/gpc1/20080130 (volume not specified)
+
+# check for existing directory, generate if needed
+$ipprc->outroot_prepare($outroot);
+
+# XXX use PPIMAGE.OUTPUT.RESID for output file (compressed, with pos & neg range allowed)
+# my $outputName  = $ipprc->filename("PPIMAGE.OUTPUT", $outroot, $class_id);
+
+my $outputName  = $ipprc->filename("PPIMAGE.OUTPUT.RESID", $outroot, $class_id);
+my $bin1Name    = $ipprc->filename("PPIMAGE.BIN1",   $outroot, $class_id);
+my $bin2Name    = $ipprc->filename("PPIMAGE.BIN2",   $outroot, $class_id);
+my $outputStats = $ipprc->filename("PPIMAGE.STATS",  $outroot, $class_id);
+my $traceDest   = $ipprc->filename("TRACE.IMFILE",   $outroot, $class_id);
+
+my $cmdflags;
+
+# Run ppImage & ppStats
+unless ($no_op) {
+    my $command = "$ppImage -file $input_uri $outroot";
+    $command .= " -recipe PPIMAGE $ppimage_recipe";
+    $command .= " -recipe JPEG $jpeg_recipe";
+    $command .= " -recipe PPSTATS RESIDUAL";
+    $command .= " -F PPIMAGE.OUTPUT PPIMAGE.OUTPUT.RESID";
+    $command .= " -stats $outputStats";
+    $command .= " -tracedest $traceDest -log $logDest";
+    $command .= " -threads $threads" if defined $threads;
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    # Detrend to use in processing
+    my $detFlag = DETRENDS->{lc($det_type)};
+    &my_die("Unrecognised detrend type: $det_type", $det_id, $iter, $exp_id, $class_id, $PS_EXIT_PROG_ERROR) unless defined $detFlag;
+    $command .= " $detFlag $detrend";
+
+    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 ppImage: $error_code", $det_id, $iter, $exp_id, $class_id, $error_code);
+    }
+
+    &my_die("Couldn't find expected output file: $outputName", $det_id, $iter, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($outputName);
+    &my_die("Couldn't find expected output file: $bin1Name", $det_id, $iter, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($bin1Name);
+    &my_die("Couldn't find expected output file: $bin2Name", $det_id, $iter, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($bin2Name);
+
+    my $outputStatsReal = $ipprc->file_resolve($outputStats);
+    &my_die("Couldn't find expected output file: $outputStats", $det_id, $iter, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless -f $outputStatsReal;
+
+    # ppStatsFromMetadata $outputStats - DETREND_RESID_IMFILE
+    $command = "$ppStatsFromMetadata $outputStatsReal - DETREND_RESID_IMFILE";
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform ppStatsFromMetadata: $error_code", $det_id, $iter, $exp_id, $class_id, $error_code);
+    }
+    foreach my $line (@$stdout_buf) {
+	$cmdflags .= " $line";
+    }
+    chomp $cmdflags;
+
+    # run ppStats on the binned image
+    $command = "$ppStats -recipe PPSTATS RESIDUAL $bin2Name | $ppStatsFromMetadata - - DETREND_RESID_IMFILE_BINNED";
+    ( $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 ppStats: $error_code", $det_id, $iter, $exp_id, $class_id, $error_code);
+    }
+    foreach my $line (@$stdout_buf) {
+	$cmdflags .= " $line";
+    }
+    chomp $cmdflags;
+}
+
+# Command to update the database
+my $command = "$dettool -addresidimfile";
+$command .= " -det_id $det_id";
+$command .= " -iteration $iter";
+$command .= " -ref_det_id $ref_det_id";
+$command .= " -ref_iter $ref_iter";
+$command .= " -exp_id $exp_id";
+$command .= " -class_id $class_id";
+$command .= " -recip $recipe";
+$command .= " -uri $outputName";
+$command .= " -path_base $outroot";
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= " $cmdflags";
+
+# Add the processed file to the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform dettool -addresidimfile: $error_code\n");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $det_id = shift;         # Detrend identifier
+    my $iter = shift;           # Iteration
+    my $exp_id = shift; # Exposure tag
+    my $class_id = shift; # Class identifier
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    if (defined $det_id and defined $iter and defined $exp_id and not $no_update) {
+        my $command = "$dettool -addresidimfile";
+        $command .= " -det_id $det_id";
+        $command .= " -iteration $iter";
+        $command .= " -ref_det_id $ref_det_id";
+        $command .= " -ref_iter $ref_iter";
+        $command .= " -exp_id $exp_id";
+        $command .= " -class_id $class_id";
+        $command .= " -path_base $outroot";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $exit = $?;
+    system("sync") == 0 or die "failed to execute sync: $!";
+    $? = $exit;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_stack.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_stack.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/detrend_stack.pl	(revision 22322)
@@ -0,0 +1,287 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ( $det_id, $iter, $class_id, $det_type, $camera, $outroot, $dbname, $reduction, $threads, $verbose, $save_temps,
+     $no_update, $no_op, $redirect );
+GetOptions(
+    'det_id|d=s'        => \$det_id,
+    'iteration=s'       => \$iter,
+    'class_id|i=s'      => \$class_id,
+    'det_type|t=s'      => \$det_type,
+    'camera|c=s'        => \$camera,
+    'outroot|w=s'       => \$outroot,   # output file base name
+    'dbname|d=s'        => \$dbname,    # Database name
+    'reduction=s'       => \$reduction, # Reduction class for processing
+    'threads=s'         => \$threads,
+    'verbose'           => \$verbose,   # Print to stdout
+    'save-temps'        => \$save_temps, # Save temporary files?
+    'no-update'         => \$no_update,
+    'no-op'             => \$no_op,
+    'redirect-output'   => \$redirect,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --det_id --iteration --class_id --det_type --camera --outroot",
+           -exitval => 3) unless
+    defined $det_id   and
+    defined $iter     and
+    defined $class_id and
+    defined $det_type and
+    defined $camera   and
+    defined $outroot;
+
+$ipprc->define_camera($camera);
+$det_type = uc($det_type);
+
+my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id)
+    or &my_die("Missing entry in file rules", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR);
+
+# optionally redirect the outputs from this script to LOG.IMFILE
+$ipprc->redirect_output($logDest) if $redirect;
+
+# Recipes to use as a function of detrend type
+$reduction = "DETREND" unless defined $reduction;
+my $recipe = $ipprc->reduction($reduction, $det_type . '_STACK'); # Recipe name to use
+
+# values to extract from output metadata and the stats to calculate
+# XXX -bg_mean_stdev should take rms of bg_mean_stdev if bg_mean_stdev != 0 (A)
+# XXX -bg_mean_stdev should take stdev of bg_mean if bg_mean_stdev == 0     (B)
+# XXX  (A) if imfile.Ncomp > 1, (B) if imfile.Ncomp == 1
+my $STATS =
+   [
+       #          KEYWORD          STATISTIC            DETTOOL FLAG
+       { name => "ROBUST_MEDIAN",  type => "clipmean",  flag => "-bg",             dtype => "float" },
+       { name => "ROBUST_MEDIAN",  type => "clipstdev", flag => "-bg_mean_stdev",  dtype => "float" },
+       { name => "ROBUST_STDEV",   type => "rms",       flag => "-bg_stdev",       dtype => "float" },
+   ];
+my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppMerge = can_run('ppMerge') or (warn "Can't find ppMerge" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# The output file rule name depends on the detrend type
+my $FILERULES = { 'FLATMASK'         => 'PPMERGE.OUTPUT.MASK',
+                  'DARKMASK'         => 'PPMERGE.OUTPUT.MASK',
+                  'MASK'             => 'PPMERGE.OUTPUT.MASK',
+                  'BIAS'             => 'PPMERGE.OUTPUT.BIAS',
+                  'DARK'             => 'PPMERGE.OUTPUT.DARK',
+                  'DARK_PREMASK'     => 'PPMERGE.OUTPUT.DARK',
+                  'SHUTTER'          => 'PPMERGE.OUTPUT.SHUTTER',
+                  'FLAT_PREMASK'     => 'PPMERGE.OUTPUT.FLAT',
+                  'DOMEFLAT_PREMASK' => 'PPMERGE.OUTPUT.FLAT',
+                  'SKYFLAT_PREMASK'  => 'PPMERGE.OUTPUT.FLAT',
+                  'FLAT_RAW'         => 'PPMERGE.OUTPUT.FLAT',
+                  'DOMEFLAT_RAW'     => 'PPMERGE.OUTPUT.FLAT',
+                  'SKYFLAT_RAW'      => 'PPMERGE.OUTPUT.FLAT',
+                  'FLAT'             => 'PPMERGE.OUTPUT.FLAT',
+                  'DOMEFLAT'         => 'PPMERGE.OUTPUT.FLAT',
+                  'SKYFLAT'          => 'PPMERGE.OUTPUT.FLAT',
+                  'FRINGE'           => 'PPMERGE.OUTPUT.FRINGE',
+              };
+my $output_filerule = $FILERULES->{$det_type}; # File rule for output
+&my_die("Unrecognised detrend type: $det_type", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless defined $output_filerule;
+
+# The stats recipe depends on the detrend type
+my $STATRECIPES = {'FLATMASK'         => 'CHIPSTATS',
+                   'DARKMASK'         => 'CHIPSTATS',
+                   'MASK'             => 'CHIPSTATS',
+                   'BIAS'             => 'CHIPSTATS',
+                   'DARK'             => 'DARKSTATS',
+                   'DARK_PREMASK'     => 'DARKSTATS',
+                   'SHUTTER'          => 'DARKSTATS',
+                   'FLAT_PREMASK'     => 'CHIPSTATS',
+                   'DOMEFLAT_PREMASK' => 'CHIPSTATS',
+                   'SKYFLAT_PREMASK'  => 'CHIPSTATS',
+                   'FLAT_RAW'         => 'CHIPSTATS',
+                   'DOMEFLAT_RAW'     => 'CHIPSTATS',
+                   'SKYFLAT_RAW'      => 'CHIPSTATS',
+                   'FLAT'             => 'CHIPSTATS',
+                   'DOMEFLAT'         => 'CHIPSTATS',
+                   'SKYFLAT'          => 'CHIPSTATS',
+                   'FRINGE'           => 'CHIPSTATS',
+              };
+my $statrecipe = $STATRECIPES->{$det_type}; # File rule for output
+
+# Get list of files to stack
+my ($files, $command, $success, $error_code, $full_buf, $stdout_buf, $stderr_buf);
+{
+    $command = "$dettool -processedimfile -included";
+    $command .= " -det_id $det_id";
+    $command .= " -class_id $class_id";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform dettool -processedimfile: $error_code", $det_id, $iter, $class_id, $error_code);
+    }
+
+    my $mdcParser = PS::IPP::Metadata::Config->new;     # Parser for metadata config files
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $det_id, $iter, $class_id, $PS_EXIT_PROG_ERROR);
+    $files = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $det_id, $iter, $class_id, $PS_EXIT_PROG_ERROR);
+}
+
+# Generate MDC file with the inputs
+my ($listFile, $listName) = $ipprc->create_temp_file("$outroot.$class_id.list", $save_temps);
+
+my $num = 0;
+foreach my $file (@$files) {
+    if ($file->{ignored}) { next; }
+
+    print $listFile "INPUT$num\tMETADATA\n";
+    $num++;
+
+    my $image = $file->{uri};   # Image name
+    my $mask = $ipprc->filename( "PPIMAGE.OUTPUT.MASK", $file->{path_base}, $class_id ); # Mask name
+    my $weight = $ipprc->filename( "PPIMAGE.OUTPUT.WEIGHT", $file->{path_base}, $class_id ); # Weight name
+
+    &my_die("Image $image does not exist", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists( $image );
+    print $listFile "\tIMAGE\tSTR\t" . $image . "\n";
+
+    if ($ipprc->file_exists( $mask )) {
+        print $listFile "\tMASK\tSTR\t" . $mask . "\n";
+    }
+    if ($ipprc->file_exists( $weight )) {
+        print $listFile "\tWEIGHT\tSTR\t" . $weight . "\n";
+    }
+
+    print $listFile "END\n\n";
+}
+close $listFile;
+
+
+# outroot examples (HOST components must be set)
+# file://data/ipp004.0/gpc1/20080130
+# neb:///ipp004-v1/gpc1/20080130
+# neb:///*/gpc1/20080130 (volume not specified)
+
+# check for existing directory, generate if needed
+$ipprc->outroot_prepare($outroot);
+
+my $outputStack = $ipprc->filename($output_filerule, $outroot, $class_id) or &my_die("Missing entry in file rules", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR); # Output name
+my $outputCount = $ipprc->filename("PPMERGE.OUTPUT.COUNT", $outroot, $class_id) or &my_die("Missing entry in file rules", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR); # Count image
+my $outputSigma = $ipprc->filename("PPMERGE.OUTPUT.SIGMA", $outroot, $class_id) or &my_die("Missing entry in file rules", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR); # Stdev image
+my $outputStats = $ipprc->filename("PPIMAGE.STATS",  $outroot, $class_id) or &my_die("Missing entry in file rules", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR); # Statistics name
+my $traceDest   = $ipprc->filename("TRACE.IMFILE",   $outroot, $class_id) or &my_die("Missing entry in file rules", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR); # Trace messages
+
+$command = "$ppMerge $listName $outroot"; # Command to run
+$command .= " -recipe PPMERGE $recipe";
+$command .= ' -type ' . uc($det_type); # Type of stacking to perform
+$command .= " -stats $outputStats";     # Statistics output filename
+$command .= " -recipe PPSTATS $statrecipe";
+$command .= " -tracedest $traceDest -log $logDest";
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= " -threads $threads" if defined $threads;
+
+# Stack the files
+unless ($no_op) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform ppMerge: $error_code", $det_id, $iter, $class_id, $error_code);
+    }
+    &my_die("Unable to find expected output file: $outputStack\n", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($outputStack);
+    &my_die("Unable to find expected output file: $outputCount\n", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($outputCount);
+    &my_die("Unable to find expected output file: $outputSigma\n", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($outputSigma);
+    &my_die("Unable to find expected output file: $outputStats\n", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless -f $ipprc->file_resolve($outputStats);
+
+    # Get the statistics on the stacked image
+    open(my $statsFile, $ipprc->file_resolve("$outputStats")) or
+        &my_die("Can't open statistics file $outputStats: $!", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR);
+    my $contents = do { local $/; <$statsFile> }; # Contents of file
+    close($statsFile);
+
+    my $mdcParser = PS::IPP::Metadata::Config->new;     # Parser for metadata config files
+    my $metadata = $mdcParser->parse($contents) or
+        &my_die("Unable to parse metadata config doc", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR);
+
+    $stats->parse($metadata)  or
+        &my_die("Unable to find all values in statistics output.", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR);
+}
+
+# Command to update the database
+$command  = "$dettool -addstacked";
+$command .= " -det_id $det_id";
+$command .= " -iteration $iter";
+$command .= " -class_id $class_id";
+$command .= " -uri $outputStack";
+$command .= " -recip $recipe";
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= $stats->cmdflags();
+
+# Add the resultant into the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform dettool -addstacked: $error_code\n");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $det_id = shift;         # Detrend identifier
+    my $iter = shift;           # Iteration
+    my $class_id = shift; # Class identifier
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    if (defined $det_id and defined $iter and defined $class_id and not $no_update) {
+        my $command = "$dettool -addstacked";
+        $command .= " -det_id $det_id";
+        $command .= " -iteration $iter";
+        $command .= " -class_id $class_id";
+	# XXX EAM : we should add this to the db : $command .= " -path_base $outroot";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/diff_skycell.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/diff_skycell.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/diff_skycell.pl	(revision 22322)
@@ -0,0 +1,278 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use Data::Dumper;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($diff_id, $dbname, $threads, $outroot, $verbose, $no_update, $no_op, $redirect);
+GetOptions(
+    'diff_id|d=s'       => \$diff_id, # Diff identifier
+    'dbname|d=s'        => \$dbname, # Database name
+    'threads=s'         => \$threads,   # Number of threads to use
+    'outroot=s'         => \$outroot, # Output root name
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update, # Don't update the database?
+    'no-op'             => \$no_op, # Don't do any operations?
+    'redirect-output'   => \$redirect,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --diff_id --outroot",
+    -exitval => 3,
+          ) unless defined $diff_id
+    and defined $outroot;
+
+# XXX camera is not known here; cannot use filerules...
+# my $logDest = $ipprc->filename("LOG.EXP", $outroot);
+
+my $logDest = "$outroot.log";
+$ipprc->redirect_output($logDest) if $redirect;
+
+my $STATS =
+   [
+       #          PPSTATS KEYWORD         STATISTIC          DIFFTOOL FLAG
+       { name => "ROBUST_MEDIAN",    type => "mean", flag => "-bg",          dtype => "float" },
+       { name => "ROBUST_STDEV",     type => "rms",  flag => "-bg_stdev",    dtype => "float" },
+       { name => "TIME_SUB",         type => "sum",  flag => "-dtime_diff",  dtype => "float" },
+       { name => "TIME_MATCH",       type => "sum",  flag => "-dtime_match", dtype => "float" },
+       { name => "TIME_PHOT",        type => "sum",  flag => "-dtime_phot",  dtype => "float" },
+       { name => "SUBTRACTION.NUM",  type => "mean", flag => "-stamps_num",  dtype => "int" },
+       { name => "SUBTRACTION.MEAN", type => "mean", flag => "-stamps_mean", dtype => "float" },
+       { name => "SUBTRACTION.RMS",  type => "mean", flag => "-stamps_rms",  dtype => "float" },
+       { name => "NUM_SOURCES",      type => "sum",  flag => "-sources",     dtype => "int" },
+       { name => "GOOD_PIXEL_FRAC",  type => "mean", flag => "-good_frac",   dtype => "float" },
+   ];
+my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $difftool = can_run('difftool') or (warn "Can't find difftool" and $missing_tools = 1);
+my $ppSub = can_run('ppSub') or (warn "Can't find ppSub" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Get list of components for subtraction
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+my $files;
+{
+    my $command = "$difftool -inputskyfile -diff_id $diff_id";
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform difftool -inputskyfile: $error_code", $diff_id, $error_code);
+    }
+
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $diff_id, $PS_EXIT_PROG_ERROR);
+    $files = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $diff_id, $PS_EXIT_PROG_ERROR);
+}
+
+&my_die("Subtraction list does not contain exactly two elements", $diff_id, $PS_EXIT_SYS_ERROR) unless
+    scalar @$files == 2;
+
+# Identify the input and the template
+my ($input, $inputMask, $inputWeight, $inputPath); # Input files and path
+my ($template, $templateMask, $templateWeight, $templatePath, $templateSources); # Template files and path
+my $tess_id;                    # Tesselation identifier
+my $skycell_id;                 # Skycell identifier
+my $camera;                     # Camera
+foreach my $file (@$files) {
+    if (defined $file->{template} and $file->{template}) {
+        $template = $file->{uri};
+        $templatePath = $file->{path_base};
+        if ($file->{warp_id} == 0) {
+            $templateMask = "PPSTACK.OUTPUT.MASK";
+            $templateWeight = "PPSTACK.OUTPUT.WEIGHT";
+            $templateSources = "PSPHOT.OUT.CMF.MEF";  ## this must be consistent with the value in stack_skycell.pl:161
+            ## use an explicit stack name for psphot output objects
+        } else {
+            $templateMask = "PSWARP.OUTPUT.MASK";
+            $templateWeight = "PSWARP.OUTPUT.WEIGHT";
+            $templateSources = "PSWARP.OUTPUT.SOURCES";
+        }
+    } else {
+        $input = $file->{uri};
+        $inputPath = $file->{path_base};
+        if ($file->{warp_id} == 0) {
+            $inputMask = "PPSTACK.OUTPUT.MASK";
+            $inputWeight = "PPSTACK.OUTPUT.WEIGHT";
+        } else {
+            $inputMask = "PSWARP.OUTPUT.MASK";
+            $inputWeight = "PSWARP.OUTPUT.WEIGHT";
+        }
+    }
+    if (defined $tess_id) {
+        &my_die("Tesselation identifiers don't match", $diff_id, $PS_EXIT_SYS_ERROR) unless
+            $file->{tess_id} eq $tess_id;
+    } else {
+        $tess_id = $file->{tess_id};
+    }
+    if (defined $skycell_id) {
+        &my_die("Skycell identifiers don't match", $diff_id, $PS_EXIT_SYS_ERROR) unless
+            $file->{skycell_id} eq $skycell_id;
+    } else {
+        $skycell_id = $file->{skycell_id};
+    }
+    if (defined $camera) {
+        &my_die("Cameras don't match", $diff_id, $PS_EXIT_SYS_ERROR) unless $file->{camera} eq $camera;
+    } else {
+        $camera = $file->{camera};
+    }
+
+}
+
+&my_die("Unable to identify template", $diff_id, $PS_EXIT_SYS_ERROR) unless defined $template;
+&my_die("Unable to identify input", $diff_id, $PS_EXIT_SYS_ERROR) unless defined $input;
+&my_die("Unable to identify camera", $diff_id, $PS_EXIT_SYS_ERROR) unless defined $camera;
+$ipprc->define_camera($camera);
+
+# print "templateMask: $templateMask\n";
+# print "templatePath: $templatePath\n";
+# print "inputMask: $inputMask\n";
+# print "inputPath: $inputPath\n";
+# print "templateWeight: $templateWeight\n";
+# print "inputWeight: $inputWeight\n";
+# print "templateSources: $templateSources\n";
+
+$templateMask = $ipprc->filename($templateMask, $templatePath);
+$inputMask = $ipprc->filename($inputMask, $inputPath);
+$templateWeight = $ipprc->filename($templateWeight, $templatePath);
+$inputWeight = $ipprc->filename($inputWeight, $inputPath);
+$templateSources = $ipprc->filename($templateSources, $templatePath);
+
+print "templateMask: $templateMask\n";
+print "templatePath: $templatePath\n";
+print "inputMask: $inputMask\n";
+print "inputPath: $inputPath\n";
+print "templateWeight: $templateWeight\n";
+print "inputWeight: $inputWeight\n";
+print "templateSources: $templateSources\n";
+
+&my_die("Couldn't find input: $template", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($template);
+&my_die("Couldn't find input: $templateMask", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($templateMask);
+&my_die("Couldn't find input: $templateWeight", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($templateWeight);
+&my_die("Couldn't find input: $input", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($input);
+&my_die("Couldn't find input: $inputMask", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($inputMask);
+&my_die("Couldn't find input: $inputWeight", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($inputWeight);
+&my_die("Couldn't find input: $templateSources", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($templateSources);
+
+# Get the output filenames
+my $outputName = $ipprc->filename("PPSUB.OUTPUT", $outroot);
+my $outputMask = $ipprc->filename("PPSUB.OUTPUT.MASK", $outroot);
+my $outputWeight = $ipprc->filename("PPSUB.OUTPUT.WEIGHT", $outroot);
+my $configuration = $ipprc->filename("PPSUB.CONFIG", $outroot);
+my $outputSources = $ipprc->filename("PSPHOT.OUT.CMF.MEF", $outroot);
+#my $bin1Name =  $ipprc->filename("PPSUB.BIN1", $outroot);
+#my $bin2Name =  $ipprc->filename("PPSUB.BIN2", $outroot);
+my $outputStats = $ipprc->filename("SKYCELL.STATS", $outroot);
+my $traceDest = $ipprc->filename("TRACE.EXP", $outroot);
+
+# Perform subtraction
+unless ($no_op) {
+    my $command = "$ppSub $input $template $outroot";
+    $command .= " -inmask $inputMask";
+    $command .= " -refmask $templateMask";
+    $command .= " -inweight $inputWeight";
+    $command .= " -refweight $templateWeight";
+    $command .= " -stats $outputStats";
+    $command .= " -threads $threads" if defined $threads;
+    $command .= " -recipe PPSTATS WARPSTATS";
+    $command .= " -F PSPHOT.OUTPUT PSPHOT.OUT.CMF.MEF";
+    $command .= " -sources $templateSources";
+    $command .= " -photometry";
+    $command .= " -tracedest $traceDest -log $logDest";
+    $command .= " -dumpconfig $configuration";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform ppSub: $error_code", $diff_id, $error_code);
+    }
+    &my_die("Couldn't find expected output file: $outputName", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputName);
+    &my_die("Couldn't find expected output file: $outputMask", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputMask);
+    &my_die("Couldn't find expected output file: $outputWeight", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputWeight);
+    &my_die("Couldn't find expected output file: $outputSources", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputSources);
+#    &my_die("Couldn't find expected output file: $bin1Name",    $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($bin1Name);
+#    &my_die("Couldn't find expected output file: $bin2Name",    $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($bin2Name);
+    &my_die("Couldn't find expected output file: $outputStats", $diff_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputStats);
+
+    # Get the statistics on the residual image
+    my $statsFile;              # File handle
+    open $statsFile, $ipprc->file_resolve($outputStats) or &my_die("Can't open statistics file $outputStats: $!", $diff_id, $PS_EXIT_SYS_ERROR);
+    my @contents = <$statsFile>; # Contents of file
+    close $statsFile;
+    my $metadata = $mdcParser->parse(join "", @contents) or
+        &my_die("Unable to parse metadata config doc", $diff_id, $PS_EXIT_PROG_ERROR);
+    $stats->parse($metadata) or &my_die("Unable to find all values in statistics output.", $diff_id, $PS_EXIT_PROG_ERROR);
+}
+
+unless ($no_update) {
+
+    # Add the subtraction result
+    {
+        my $command = "$difftool -adddiffskyfile -diff_id $diff_id -uri $outputName -path_base $outroot";
+        $command .= $stats->cmdflags();
+        $command .= " -hostname $host" if defined $host;
+        $command .= " -dbname $dbname" if defined $dbname;
+
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform difftool -adddiffskyfile: $error_code", $diff_id, $error_code);
+        }
+    }
+}
+
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $diff_id = shift;        # Diff identifier
+    my $exit_code = shift;      # Exit code to add
+
+    warn($msg);
+    if (defined $diff_id and not $no_update) {
+        my $command = "$difftool -adddiffskyfile -diff_id $diff_id -code $exit_code";
+        $command .= " -hostname $host" if defined $host;
+        $command .= " -dbname $dbname" if defined $dbname;
+        run(command => $command, verbose => $verbose);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $exit = $?;
+    system("sync") == 0 or die "failed to execute sync: $!";
+    $? = $exit;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ds9_cmf_regions.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ds9_cmf_regions.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ds9_cmf_regions.pl	(revision 22322)
@@ -0,0 +1,134 @@
+#!/usr/bin/env perl
+
+use strict;
+use Astro::FITS::CFITSIO qw( :constants );
+use File::Temp qw( tempfile );
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use Data::Dumper;
+use Carp;
+
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+
+my $xpaset = `which xpaset`;
+my $xpaget = `which xpaget`;
+
+die "Unable to find xpaget and xpaset.\n" unless ($xpaset =~ /\S+/ and $xpaget =~ /\S+/);
+
+
+my ( $filename,                 # Filename containing photometry
+     $extname,                  # Extension name containing photometry
+     $frame,                    # Frame number in ds9
+     $colour,                   # Region colour
+     $flag_colour,              # Flagged source region colour
+     $flag,                     # Flags
+     $radius,                   # Radius for circle
+     $save_temps
+     );
+
+# Defaults
+$colour = "blue";
+$flag_colour = "red";
+$radius = 5;
+$flag = 0x3888;
+
+GetOptions(
+           'file=s' => \$filename,
+           'ext=s' => \$extname,
+           'frame=s' => \$frame,
+           'colour=s' => \$colour,
+           'flag-colour=s' => \$flag_colour,
+           'flag=o' => \$flag,
+           'radius=f' => \$radius,
+           'save-temps'        => \$save_temps, # Save temporary files?
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --file --ext",
+           -exitval => 3)
+    unless defined $filename
+    and defined $extname;
+
+my $status;                     # Status of FITSIO calls
+my $fits = Astro::FITS::CFITSIO::open_file( $filename, READONLY, $status ); # FITS file handle
+check_fitsio($status);
+$fits->movnam_hdu(BINARY_TBL, $extname, 0, $status) and check_fitsio($status);
+my $numRows;                    # Number of rows in table
+$fits->get_num_rows($numRows, $status) and check_fitsio($status);
+
+my ($xCol, $yCol, $flagCol);     # Column numbers for x,y, flag
+$fits->get_colnum(0, 'X_PSF', $xCol, $status) and check_fitsio($status);
+$fits->get_colnum(0, 'Y_PSF', $yCol, $status) and check_fitsio($status);
+$fits->get_colnum(0, 'FLAGS', $flagCol, $status) and check_fitsio($status);
+
+my ($x, $y, $flags);            # Coordinates and flags read from table
+$fits->read_col(TFLOAT, $xCol, 1, 1, $numRows, 0, $x, undef, $status) and check_fitsio($status);
+$fits->read_col(TFLOAT, $yCol, 1, 1, $numRows, 0, $y, undef, $status) and check_fitsio($status);
+$fits->read_col(TINT, $flagCol, 1, 1, $numRows, 0, $flags, undef, $status) and check_fitsio($status);
+$fits->close_file($status);
+
+my ($coordFile, $coordName) = tempfile( "/tmp/ds9_cmf_regions.XXXX", UNLINK => !$save_temps );
+for (my $i = 0; $i < $numRows; $i++) {
+    print $coordFile "image; circle(" . ($$x[$i] + 1) . ',' . ($$y[$i] + 1) . ",$radius) \# color = " .
+        (($$flags[$i] & $flag) ? $flag_colour : $colour) . "\n";
+}
+close $coordFile;
+
+my @settings = settings_save("regions format",
+                             "regions system"); # Settings to save
+
+xpaset("frame $frame") if defined $frame;
+xpaset("regions format ds9");
+xpaset("regions system image");
+xpaset("regions load $coordName");
+
+xpaset(@settings);
+
+print "Plotted $numRows sources.\n";
+
+### Pau.
+
+
+
+
+# From Astro::FITS::CFITSIO demo
+sub check_fitsio
+{
+    my $status = shift;         # Status of FITSIO calls
+
+    if ($status != 0) {
+        my $msg;                # Message to output
+        Astro::FITS::CFITSIO::fits_get_errstatus( $status , $msg );
+        croak "CFITSIO error: $msg\n";
+    }
+}
+
+# Save specified settings
+sub settings_save
+{
+    my @settings;               # Values of settings
+    foreach my $setting (@_) {
+        my @values = xpaget($setting);
+        push @settings, $setting . ' ' . shift @values;
+    }
+    return @settings;
+}
+
+
+# XPA subroutines courtesy Derek Fox
+sub xpaset {
+    foreach my $cmd (@_) {
+        system("xpaset -p ds9 $cmd");
+    }
+}
+sub xpaget {
+    my @out;
+    foreach my $cmd (@_) {
+        my $output = `xpaget ds9 $cmd`;
+        push @out, $output;
+    }
+    return @out;
+}
+
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/fake_imfile.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/fake_imfile.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/fake_imfile.pl	(revision 22322)
@@ -0,0 +1,241 @@
+#!/usr/bin/env perl
+
+## this script is run on every imfile to perform the fake source
+## analysis and the forced photometry analysis.  
+
+## For the fake source analysis, we load the image, inject a number of
+## fake source (with a flat mag distribution), save the input source,
+## then perform photometry on the image to recover the input sources.
+## we should save a file with the matched input and recovered
+## sources.  the output metadata should include an analysis of the
+## recovery rate and measurement error as a function of magnitude
+
+## For the forced photometry, we need to use the measured astrometry
+## to select the desired locations in pixel coord.  
+
+# XXX : seeing needs to be determined from the input PSF (not currently done in ppSim)
+# ppSim output -input input.fits -cmf input.cmf -psf input.psf -seeing 0.563760 -recipe PPSIM FAKEPHOT
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line arguments
+my ( $exp_id, $fake_id, $class_id, $chiproot, $camroot, $camera, $outroot, 
+     $dbname, $reduction, $verbose, $no_update, $no_op, $redirect  );
+GetOptions(
+    'exp_id=s'      	=> \$exp_id,    # Exposure identifier
+    'fake_id=s'     	=> \$fake_id,   # Chiptool identifier
+    'class_id=s'    	=> \$class_id,  # Class identifier
+    'chiproot=s'    	=> \$chiproot,  # Input Chip files (root)
+    'camroot=s'     	=> \$camroot,   # Input Camera files (root)
+    'camera|c=s'    	=> \$camera,	   # Camera
+    'outroot|w=s'   	=> \$outroot,   # output file base name
+    'dbname|d=s'    	=> \$dbname,    # Database name
+    'reduction=s'   	=> \$reduction, # Reduction class
+    'verbose'       	=> \$verbose,   # Print to stdout
+    'no-update'     	=> \$no_update, # Don't update the database?
+    'no-op'         	=> \$no_op,	   # Don't do any operations?
+    'redirect-output'   => \$redirect,
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --exp_id --fake_id --class_id --chiproot --camroot --camera --outroot",
+	   -exitval => 3) unless
+    defined $exp_id and
+    defined $fake_id and
+    defined $class_id and
+    defined $chiproot and
+    defined $camroot and
+    defined $camera and
+    defined $outroot;
+
+$ipprc->define_camera($camera);
+
+my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id)  or &my_die("Missing entry from camera config", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+
+$ipprc->redirect_output($logDest) if $redirect;
+
+# Recipes to use based on reduction class
+$reduction = 'DEFAULT' unless defined $reduction;
+my $recipe = $ipprc->reduction($reduction, 'FAKEPHOT'); # Recipe to use
+unless ($recipe) {
+    &my_die("Couldn't find selected reduction for FAKE: $reduction\n", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+}
+
+# values to extract from output metadata and the stats to calculate
+# XXX not sure if this is needed by fake_imfile.pl
+# my $STATS =
+#    [
+#        #          PPSTATS KEYWORD         STATISTIC          CHIPTOOL FLAG
+#        { name => "ROBUST_MEDIAN",  type => "mean",  flag => "-bg",             dtype => "float" },
+#        { name => "ROBUST_MEDIAN",  type => "stdev", flag => "-bg_mean_stdev",  dtype => "float" },
+#        { name => "ROBUST_STDEV",   type => "rms",   flag => "-bg_stdev",       dtype => "float" },
+#    ];
+# my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $faketool = can_run('faketool') or (warn "Can't find faketool" and $missing_tools = 1);
+my $ppSim = can_run('ppSim') or (warn "Can't find ppSim" and $missing_tools = 1);
+my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my $mdcParser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+
+# outroot examples (HOST components must be set)
+# file://data/ipp004.0/gpc1/20080130
+# neb:///ipp004-v1/gpc1/20080130
+# neb:///*/gpc1/20080130 (volume not specified)
+
+# check for existing directory, generate if needed
+$ipprc->outroot_prepare($outroot);
+
+## these names are used in ppImage, and thus may be URIs
+my $chipImage     = $ipprc->filename("PPIMAGE.CHIP",        $chiproot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $chipMask      = $ipprc->filename("PPIMAGE.CHIP.MASK",   $chiproot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $chipWeight    = $ipprc->filename("PPIMAGE.CHIP.WEIGHT", $chiproot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $chipPSF       = $ipprc->filename("PSPHOT.PSF.SAVE",     $chiproot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $cameraObjects = $ipprc->filename("PSASTRO.OUTPUT",      $camroot)             or &my_die("Missing entry from camera config", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+my $traceDest     = $ipprc->filename("TRACE.IMFILE", 	    $outroot, $class_id)  or &my_die("Missing entry from camera config", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+
+# XXX check for existence of input data
+# &my_die("Couldn't find input file: $uri\n", $exp_id, $fake_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($uri);
+
+## get the PPSIM recipe for this camera and FAKEPHOT reduction
+my $command = "$ppConfigDump -camera $camera -dump-recipe PPSIM -recipe PPSIM $recipe -";
+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, $fake_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, $fake_id, $class_id, $PS_EXIT_SYS_ERROR);
+
+## allow the output images to be optional, depending on the recipe / reduction class
+my $skipFake = metadataLookupBool($recipeData, 'SKIP.FAKE');
+
+# Run ppSim
+unless ($no_op || $skipFake) {
+    # examine the PPSIM recipe to decide if we need to run this or opt out
+
+    my $command = "$ppSim $outroot";
+    $command .= " -input $chipImage";
+    # XXX add input mask and weight to ppSim
+    # $command .= " -mask $chipMask";
+    # $command .= " -weight $chipWeight";
+    $command .= " -cmf $cameraObjects";
+    $command .= " -psf $chipPSF";
+    $command .= " -recipe PPSIM $recipe";
+    $command .= " -dbname $dbname" if defined $dbname;
+    $command .= " -tracedest $traceDest";
+    $command .= " -log $logDest";
+
+    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 ppSim: $error_code", $exp_id, $fake_id, $class_id, $error_code);
+    }
+
+    # XXX check for output files?
+    # &my_die("Couldn't find expected output file: $outputBin1\n",   $exp_id, $fake_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputBin1);
+
+    # XXX use this to parse the output metadata : eg, detection limits, Nfakes, Nforced
+    # Get the statistics on the processed image
+    # my $statsFile;		# File handle
+    # open $statsFile, $ipprc->file_resolve($outputStats) or &my_die("Can't open statistics file $outputStats: $!", $exp_id, $fake_id, $class_id, $PS_EXIT_SYS_ERROR);
+    # my @contents = <$statsFile>; # Contents of file
+    # close $statsFile;
+    # 
+    # # parse the statistics MDC file
+    # my $mdcParser = PS::IPP::Metadata::Config->new();	# Parser for metadata config files
+    # my $metadata = $mdcParser->parse(join "", @contents);
+    # unless ($metadata) {
+    # 	&my_die("Unable to parse metadata config doc", $exp_id, $fake_id, $class_id, $PS_EXIT_PROG_ERROR);
+    # }
+    # 
+    # # extract the stats from the metadata
+    # unless ($stats->parse($metadata)) {
+    # 	&my_die("Failure extracting metadata from the statistics output file.\n", $exp_id, $fake_id, $class_id, $PS_EXIT_PROG_ERROR);
+    # }
+} else {
+    print "skipping ppSim processing\n";
+}
+
+# command to update database
+$command = "$faketool -addprocessedimfile";
+$command .= " -fake_id $fake_id";
+$command .= " -exp_id $exp_id";
+$command .= " -class_id $class_id";
+$command .= " -path_base $outroot";
+$command .= " -dbname $dbname" if defined $dbname;
+# XXX add this after defined
+# $command .= $stats->cmdflags();
+
+# Add the processed file to the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => $verbose);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	warn("Unable to perform faketool -addprocessedimfile: $error_code\n");
+	exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $exp_id = shift; # rawExp identifier
+    my $fake_id = shift; # fakeRun identifier
+    my $class_id = shift; # Class identifier
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    if (defined $exp_id and defined $fake_id and defined $class_id and not $no_update) {
+	my $command = "$faketool -addprocessedimfile";
+	$command .= " -fake_id $fake_id";
+	$command .= " -exp_id $exp_id";
+	$command .= " -class_id $class_id";
+	$command .= " -path_base $outroot";
+	$command .= " -code $exit_code";
+	$command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/flatcorr_init.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/flatcorr_init.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/flatcorr_init.pl	(revision 22322)
@@ -0,0 +1,212 @@
+#!/usr/bin/env perl
+
+## USAGE:flatcorr_init.pl 
+## given dbname, dvodb, filter, time range, camera, telescope, etc?
+## select matching images and register as a new flatcorr run
+
+## should this be implemented as a ippTool program?
+## this is a lot like dettool -definebyquery
+## flatcorr -definebyquery
+## this should queue the selected images with a mode that stops at camera
+## ?? how do we block this batch from running through warp ??
+
+## we also need the test / query tool:
+## flatcorr -pendingcorr
+## examine the chip & camera stages and check for all images to have completed (successfully or not)
+
+## allow a certain fraction of failures...
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Storable qw(freeze thaw);
+use File::Basename qw( basename);
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+use File::Spec;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($dvo_id, $catdir, $region, $dbname, $workdir, $no_update, $no_op);
+GetOptions(
+    'dvo_id|i=s'       => \$dvo_id,
+    'catdir|c=s'       => \$catdir,
+    'region|r=s'       => \$region,
+    'dbname|d=s'       => \$dbname,# Database name
+    'workdir|w=s'      => \$workdir, # Working directory for output files
+    'no-update'        => \$no_update,
+    'no-op'            => \$no_op,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --dvo_id --catdir --region",
+           -exitval => 3) unless
+    defined $dvo_id and
+    defined $catdir and
+    defined $region;
+
+# Look for programs we need
+my $missing_tools;
+my $addstar  = can_run('addstar')  or (warn "Can't find addstar"  and $missing_tools = 1);
+my $relphot  = can_run('relphot')  or (warn "Can't find relphot"  and $missing_tools = 1);
+my $uniphot  = can_run('uniphot')  or (warn "Can't find uniphot"  and $missing_tools = 1);
+my $relastro = can_run('relastro') or (warn "Can't find relastro" and $missing_tools = 1);
+my $caltool  = can_run('caltool')  or (warn "Can't find caltool"  and $missing_tools = 1);
+
+if ($missing_tools) { 
+    warn ("Can't find required tools");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+
+# select the primary filters from DVO query?
+my (@filters) = `photcodeList -average`;
+
+# parse the region (RAs,RAe:DECs,DECe) : item = +/-NNN.NNNN
+my @coords = split (":", $region);
+my ($RAs, $RAe) = split (",", $coords[0]);
+my ($DECs, $DECe) = split (",", $coords[1]);
+
+# Run addstar -resort
+{
+    my $command = "$addstar -resort";
+    $command .= "-D CATDIR $catdir";
+    $command .= "-region $RAs $RAe $DECs $DECe";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        cache_run(command => $command, verbose => 1);
+
+    unless ($success) { 
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die ("Unable to perform addstar -resort on region $region: $error_code", $dvo_id, $region, "RESORT", $status, $dbname);
+    }
+}
+
+# Run relphot (filter) for each filter
+{
+    foreach my $filter (@filters) {
+	my $command = "$relphot $filter";
+	$command .= "-D CATDIR $catdir";
+	$command .= "-region $RAs $RAe $DECs $DECe";
+
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    cache_run(command => $command, verbose => 1);
+
+	unless ($success) { 
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    &my_die ("Unable to perform addstar -resort on region $region: $error_code", $dvo_id, $region, "RELPHOT", $status, $dbname);
+	}
+    }
+}
+
+# Run uniphot (filter) for each filter
+# XXX skip this one?  run less frequently?
+if (0) {
+    foreach my $filter (@filters) {
+	my $command = "$uniphot $filter";
+	$command .= "-D CATDIR $catdir";
+	$command .= "-region $RAs $RAe $DECs $DECe";
+
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    cache_run(command => $command, verbose => 1);
+
+	unless ($success) { 
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    &my_die ("Unable to perform addstar -resort on region $region: $error_code", $dvo_id, $region, "UNIPHOT", $status, $dbname);
+	}
+    }
+}
+
+{
+    my $command = "$relastro -objects";
+    $command .= "-D CATDIR $catdir";
+    $command .= "-region $RAs $RAe $DECs $DECe";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	cache_run(command => $command, verbose => 1);
+
+    unless ($success) { 
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die ("Unable to perform addstar -resort on region $region: $error_code", $dvo_id, $region, "RELASTRO.OBJECTS", $status, $dbname);
+    }
+}
+
+{
+    my $command = "$relastro -images";
+    $command .= "-D CATDIR $catdir";
+    $command .= "-region $RAs $RAe $DECs $DECe";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	cache_run(command => $command, verbose => 1);
+
+    unless ($success) { 
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die ("Unable to perform addstar -resort on region $region: $error_code", $dvo_id, $region, "RELASTRO.IMAGES", $status, $dbname);
+    }
+}
+
+my $command = "$caltool -addcalrun";
+$command .= " -dvo_id $dvo_id";
+$command .= " -region $region";
+$command .= " -last_step RELASTRO.IMAGES";
+$command .= " -status SUCCESS";
+$command .= " -dbname $dbname" if defined $dbname;
+
+# Push the results into the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => 1);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn ("Unable to perform regtool -addprocessedimfile: $error_code");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $dvo_id    = shift;
+    my $region    = shift;
+    my $last_step = shift;
+    my $status 	  = shift;
+    my $dbname 	  = shift;
+
+    carp($msg);
+    if (defined $dvo_id && defined $region && defined $last_step && defined $status and not $no_update) {
+        my $command = "$caltool -addcalrun";
+	$command .= " -dvo_id $dvo_id";
+        $command .= " -region $region";
+	$command .= " -last_step $last_step";
+	$command .= " -status $status";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+# Pau.
+
+END {
+    my $exit = $?;
+    system("sync") == 0 or die "failed to execute sync: $!";
+    $? = $exit;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/flatcorr_proc.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/flatcorr_proc.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/flatcorr_proc.pl	(revision 22322)
@@ -0,0 +1,324 @@
+#!/usr/bin/env perl
+
+## USAGE:flatcorr_proc.pl --dbname --corr_id
+
+## this script does the following steps:
+
+# extract the details of the flatcorr run: dvodb, filter, camera, etc?
+
+# relphot -D CATDIR $dvodb -grid (outgrid.fits) (filter) -region 0 360 -90 90 (other parameters?)
+
+# dvoMakeCorr -file outgrid.fits -ref ref.fits outcorr
+
+# dettool -register -det_type FLATCORR -filelevel (level) -workdir -inst, etc 
+
+# foreach $imfile () 
+#   dettool -register_imfile -uri, etc, etc
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Storable qw(freeze thaw);
+use File::Basename qw( basename);
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+use File::Spec;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($corr_id, $dvodb, $region, $filter, $dbname, $workdir, $verbose, $no_update, $no_op);
+GetOptions(
+    'corr_id|i=s'      => \$corr_id,
+    'dvodb|c=s'        => \$dvodb,
+    'region|r=s'       => \$region,
+    'filter|f=s'       => \$filter,
+    'dbname|d=s'       => \$dbname,# Database name
+    'workdir|w=s'      => \$workdir, # Working directory for output files
+    'verbose'       	=> \$verbose,   # Print to stdout
+    'no-update'        => \$no_update,
+    'no-op'            => \$no_op,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --corr_id --dvodb --region --filter --workdir",
+           -exitval => 3) unless
+    defined $corr_id and
+    defined $dvodb and
+    defined $region and
+    defined $workdir and
+    defined $filter;
+
+# Look for programs we need
+my $missing_tools;
+my $relphot     = can_run('relphot')      or (warn "Can't find relphot"      and $missing_tools = 1);
+my $addstar     = can_run('addstar')      or (warn "Can't find addstar"      and $missing_tools = 1);
+my $dvoMakeCorr = can_run('dvoMakeCorr')  or (warn "Can't find dvoMakeCorr"  and $missing_tools = 1);
+my $detselect   = can_run('detselect') 	  or (warn "Can't find detselect"    and $missing_tools = 1);
+my $dettool     = can_run('dettool')   	  or (warn "Can't find dettool"      and $missing_tools = 1);
+my $flatcorr    = can_run('flatcorr')  	  or (warn "Can't find flatcorr"     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 $outgrid = "$workdir/grid.$corr_id.fits";
+my $outcorr = "$workdir/corr.$corr_id";
+
+# parse the region (RAs,RAe:DECs,DECe) : item = +/-NNN.NNNN
+my @coords = split (":", $region);
+my ($RAs, $RAe) = split (",", $coords[0]);
+my ($DECs, $DECe) = split (",", $coords[1]);
+
+# Run addstar -resort to ensure the db is indexed
+{
+    my $command = "echo $addstar -resort";
+    $command .= " -D CATDIR $dvodb";
+    $command .= " -region $RAs $RAe $DECs $DECe";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => $verbose);
+
+    unless ($success) { 
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die ("Unable to perform addstar -resort for dvodb $dvodb on region $region: $error_code", $corr_id, $error_code);
+    }
+}
+
+# Run relphot (filter) for the specified region (need to clarify the options like imfreeze)
+{
+    my $command = "echo $relphot $filter";
+    $command .= " -D CATDIR $dvodb";
+    $command .= " -region $RAs $RAe $DECs $DECe";
+    $command .= " -grid $outgrid";
+    $command .= " -imfreeze";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => $verbose);
+
+    unless ($success) { 
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die ("Unable to perform relphot -grid for dvodb $dvodb on region $region: $error_code", $corr_id, $error_code);
+    }
+}
+
+# get a single input exposure
+# flatcorr -inputexp -corr_id $corr_id -limit 1
+my $chip_id;
+{
+    my $command = "$flatcorr -inputexp";
+    $command .= " -corr_id $corr_id";
+    $command .= " -limit 1";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform camtool: $error_code", $corr_id, $error_code);
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $corr_id, $PS_EXIT_PROG_ERROR);
+
+    # extract the metadata for the files into a hash list
+    my $files = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $corr_id, $PS_EXIT_PROG_ERROR);
+
+    # check for existence 
+    my $file = $$files[0];
+    $chip_id = $file->{chip_id};
+}
+
+# get the list of imfiles for the single input exposure
+# flatcorr -inputimfile -chip_id $chip_id
+my $files;
+{
+    my $command = "$flatcorr -inputimfile";
+    $command .= " -chip_id $chip_id";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform camtool: $error_code", $corr_id, $error_code);
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $corr_id, $PS_EXIT_PROG_ERROR);
+
+    # extract the metadata for the files into a hash list
+    $files = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $corr_id, $PS_EXIT_PROG_ERROR);
+}
+
+# set up the detrend run to store the corrected imfiles
+my $det_id;
+{
+    # get the filelevel from one of these chips
+    my $file = $$files[0];
+
+    my $filelevel = $file->{filelevel};
+    my $camera    = $file->{camera};
+    my $telescope = $file->{telescope};
+    my $filter    = $file->{filter};
+
+    ## XXX the flat-field correction is only defined for a single
+    ## camera.  we are grabbing the camera here, but we are assuming
+    ## the flatcorrRun was defined for only a single camera.
+    $ipprc->define_camera($camera);
+
+    my $command = "$dettool -register_detrend";
+    $command .= " -det_type FLATCORR";
+    $command .= " -filelevel $filelevel";
+    $command .= " -workdir $workdir";
+    $command .= " -inst $camera";
+    $command .= " -telescope $telescope";
+    $command .= " -filter $filter";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    ## the flat-field correction is valid for any airmass, exptime, solangle
+    ## for now, we assume the posangle and ccd_temp are not important
+    ## XXX someone needs to set the use_begin, use_end values??
+    ## XXX inherit a label from the flatcorrRun?
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+
+    unless ($success) { 
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die ("Unable to register new detrend: $error_code", $corr_id, $PS_EXIT_PROG_ERROR);
+    }
+
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $corr_id, $PS_EXIT_PROG_ERROR);
+
+    # extract the metadata for the files into a hash list
+    my $output = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $corr_id, $PS_EXIT_PROG_ERROR);
+
+    # $file = $$output[0];
+    $det_id = $$output[0]->{det_id};
+}
+
+# use input chip image as a reference image
+foreach my $file (@$files) {
+    # create the detrend correction for the imfiles based on the input imfiles
+    my $reffile = $file->{uri};
+    my $class_id = $file->{class_id};
+
+    my $uri = $ipprc->filename("DVOCORR.OUTPUT", $outcorr, $class_id);
+    unless ($uri) {
+	&my_die ("Unable to find DVOCORR.OUTPUT in filerules", $corr_id, $PS_EXIT_PROG_ERROR);
+    }
+
+    my $command = "echo $dvoMakeCorr $outcorr";
+    $command .= " -file $outgrid";
+    $command .= " -ref $reffile";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+
+    unless ($success) { 
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die ("Unable to perform dvoMakeCorr: $error_code", $corr_id, $PS_EXIT_PROG_ERROR);
+    }
+
+    # register the detrend correction imfile
+    $command = "$dettool -register_detrend_imfile";
+    $command .= " -det_id $det_id";
+    $command .= " -class_id $class_id";
+    $command .= " -uri $uri";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+
+    unless ($success) { 
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die ("Unable to register new detrend: $error_code", $corr_id, $PS_EXIT_PROG_ERROR);
+    }
+}
+
+# set the detrun state to 'stop'
+{
+    my $command = "$dettool -updatedetrun";
+    $command .= " -det_id $det_id";
+    $command .= " -state stop";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => 1);
+
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn ("Unable to perform dettool -updatedetrun: $error_code");
+        exit($error_code);
+    }
+}
+
+# Push the results into the database
+{ 
+    my $command = "$flatcorr -addprocess";
+    $command .= " -corr_id $corr_id";
+    $command .= " -hostname $host" if defined $host;
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    unless ($no_update) {
+
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $command, verbose => 1);
+	unless ($success) {
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    warn ("Unable to perform regtool -addprocessedimfile: $error_code");
+	    exit($error_code);
+	}
+    } else {
+	print "skipping command: $command\n";
+    }
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $corr_id    = shift;
+    my $exit_code  = shift;
+
+    carp($msg);
+    if (not $no_update) {
+        my $command = "$flatcorr -addprocess";
+	$command .= " -corr_id $corr_id";
+        $command .= " -code $exit_code";
+        $command .= " -hostname $host" if defined $host;
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+# Pau.
+
+END {
+    my $exit = $?;
+    system("sync") == 0 or die "failed to execute sync: $!";
+    $? = $exit;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/gpc_seeing.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/gpc_seeing.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/gpc_seeing.pl	(revision 22322)
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+# basic ISP transmission analysis:
+
+if (@ARGV != 1) { die "USAGE: gpc_seeing.pl (input.fits)\n"; }
+$input = $ARGV[0];
+
+# for input file /path/foo.fits, use /path/foo for output
+
+@words = split ('\.', $input);
+if (@words > 1) { pop @words; }
+$output = join (".", @words);
+
+# use constant RECIPE => 'PPIMAGE_OBDSFRA'; # Recipe to use
+$RECIPE_PPIMAGE  = 'PPIMAGE_OP';
+$RECIPE_PSPHOT   = 'PSPHOT.SEEING';
+
+# recommend only processing to PSFMODEL
+vsystem ("ppImage -file $input $output -recipe PPIMAGE $RECIPE_PPIMAGE -recipe PSPHOT $RECIPE_PSPHOT");
+if ($status) { die "failure running ppImage\n"; }
+
+# XXX otis can read the output psf model, or we can supply a program to interpret the model
+
+sub vsystem {
+    print STDERR "@_\n";
+    my $status = system ("@_");
+    $status;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_cleanup.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_cleanup.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_cleanup.pl	(revision 22322)
@@ -0,0 +1,374 @@
+#!/usr/bin/env perl
+
+# this script is used to cleanup the files from the different ipp
+# stages.  It can be called with one of two cleanup modes: clean and
+# purge.  the former removes temporary data files, leaving behind
+# enough information for the results to be rebuilt.  The latter
+# removes all but basic logging data.
+
+use warnings;
+use strict;
+use Carp;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Spec;
+use PS::IPP::Config 1.01 qw( :standard );
+use PS::IPP::Metadata::List qw( parse_md_list );
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my $ipprc = PS::IPP::Config->new(); # this is used for PATH, NEB filename conversions
+
+# Parse the command-line arguments
+my ($stage, $camera, $stage_id, $mode, $path_base, $dbname, $verbose, $no_op, $helplist);
+GetOptions('stage=s'        => \$stage,     # which analysis stage to clean?
+	   'camera|i=s'     => \$camera,    # user-supplied camera name
+	   'stage_id=s'     => \$stage_id,  # id for this stage (only needed for certain stages)
+	   'mode|m=s'       => \$mode,      # cleanup mode (clean / purge)
+	   'path_base=s'    => \$path_base, # basename for files
+	   'dbname|d=s'     => \$dbname,    # Database name
+           'verbose'        => \$verbose,   # Print to stdout
+	   'no-op'          => \$no_op,     # pretend but don't actually inject
+	   'helplist'       => \$helplist   # give help listing
+	   ) or pod2usage( 2 );
+
+pod2usage( -msg => "remove temporary / all data files for an IPP analysis stage", 
+	   -exitval => 2) if defined $helplist;
+
+pod2usage( -msg => "Usage: $0 --camera (name) --stage (stage) --stage_id (stage_id) --mode (mode) [--path_base (path)] [--dbname dbname] [--no-op] [--help]", 
+	   -exitval => 2 ) if scalar @ARGV;
+
+pod2usage( -msg => "Required options:--camera (name) --stage (stage) --mode (mode)",
+	   -exitval => 3) unless 
+    defined $camera and
+    defined $stage and
+    defined $mode;
+
+# $mode must be one of "goto_cleaned" or "goto_purged"
+unless (($mode eq "goto_cleaned") || ($mode eq "goto_purged")) {
+    die "invalid cleanup mode $mode\n";    
+}
+
+my %stages = ( chip => 1, camera => 1, fake => 1, warp => 1, stack => 1, diff  => 1);
+unless ($stages{$stage}) {
+    die "unknown stage $stage for ipp_cleanup.pl\n";
+}
+
+$ipprc->define_camera($camera);
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+# choice of files to delete depends on the stage
+if ($stage eq "chip") {
+    
+    die "--stage_id required for stage chip\n" if !$stage_id;
+    ### select the imfiles for this entry
+
+    # this stage uses 'chiptool'
+    my $chiptool = can_run('chiptool') or die "Can't find chiptool";
+
+    # Get list of component imfiles
+    # XXX may need a different my_die for each stage
+    my $imfiles;                      # Array of component files
+    my $command = "$chiptool -pendingcleanupimfile -chip_id $stage_id"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform chiptool: $error_code", "chip", $stage_id, $error_code);
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", "chip", $stage_id, $PS_EXIT_PROG_ERROR);
+
+    # extract the metadata for the files into a hash list
+    $imfiles = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", "chip", $stage_id, $PS_EXIT_PROG_ERROR);
+
+    # loop over all of the imfiles, determine the path_base and class_id for each
+    foreach my $imfile (@$imfiles) {
+	my $class_id = $imfile->{class_id};
+	my $path_base = $imfile->{path_base};
+        my $status = 1;
+
+        # don't clean up unless the data needed to update is available
+        if ($mode eq "goto_cleaned") {
+            my $config_file = $ipprc->filename("PPIMAGE.CONFIG", $path_base, $class_id);
+
+            if (!$config_file or ! -e $config_file) {
+                print STDERR "skipping cleanup for chipRun $stage_id $class_id "
+                    . " because config file is missing\n";
+                $status = 0;
+            }
+        }
+
+        if ($status) {
+            # array of actual filenames to delete
+            my @files = ();
+
+            # delete the temporary image datafiles
+            addFilename (\@files, "PPIMAGE.OUTPUT", $path_base, $class_id);
+            addFilename (\@files, "PPIMAGE.OUTPUT.MASK", $path_base, $class_id);
+            addFilename (\@files, "PPIMAGE.OUTPUT.WEIGHT", $path_base, $class_id);
+            addFilename (\@files, "PPIMAGE.CHIP", $path_base, $class_id);
+            addFilename (\@files, "PPIMAGE.CHIP.MASK", $path_base, $class_id);
+            addFilename (\@files, "PPIMAGE.CHIP.WEIGHT", $path_base, $class_id);
+            if ($mode eq "goto_purged") {
+                # additional files to remove for 'purge' mode
+                addFilename (\@files, "PPIMAGE.OUTPUT.FPA1", $path_base, $class_id);
+                addFilename (\@files, "PPIMAGE.OUTPUT.FPA2", $path_base, $class_id);
+                addFilename (\@files, "PPIMAGE.BIN1", $path_base, $class_id);
+                addFilename (\@files, "PPIMAGE.BIN2", $path_base, $class_id);
+                addFilename (\@files, "PPIMAGE.JPEG1", $path_base, $class_id);
+                addFilename (\@files, "PPIMAGE.JPEG", $path_base, $class_id);
+                addFilename (\@files, "PPIMAGE.STATS", $path_base, $class_id);
+                addFilename (\@files, "PPIMAGE.CONFIG", $path_base, $class_id);
+            }
+	
+            # actual command to delete the files
+            $status = &delete_files (\@files);
+        }
+
+	if ($status)  {
+	    my $command = "$chiptool -chip_id $stage_id -class_id $class_id";
+            if ($mode eq "goto_purged") {
+                $command .= " -topurgedimfile";
+            } else {
+                $command .= " -tocleanedimfile";
+            }
+	    $command .= " -dbname $dbname" if defined $dbname;
+
+	    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run(command => $command, verbose => $verbose);
+	    unless ($success) {
+		$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+		&my_die("Unable to perform chiptool: $error_code", "chip", $stage_id, $error_code);
+	    }
+        } else {
+	    my $command = "$chiptool -updateprocessedimfile -chip_id $stage_id -class_id $class_id -code 1";
+	    $command .= " -dbname $dbname" if defined $dbname;
+
+	    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run(command => $command, verbose => $verbose);
+	    unless ($success) {
+		$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+		&my_die("Unable to perform chiptool: $error_code", "chip", $stage_id, $error_code);
+	    }
+	}
+    }
+
+} elsif ($stage eq "camera") {
+    die "--stage_id required for stage camera\n" if !$stage_id;
+    # this stage uses 'camtool'
+    my $camtool = can_run('camtool') or die "Can't find camtool";
+
+    # Get list of component imfiles
+    # XXX may need a different my_die for each stage
+    my $exps;                      # Array of component files
+    my $command = "$camtool -pendingcleanupexp -cam_id $stage_id"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform camtool: $error_code", "camera", $stage_id, $error_code);
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", "camera", $stage_id, $PS_EXIT_PROG_ERROR);
+
+    $exps = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", "camera", $stage_id, $PS_EXIT_PROG_ERROR);
+
+    my $n_exps = @$exps;
+    &my_die("unexpected number of exposures $n_exps", "camera", $stage_id, $PS_EXIT_PROG_ERROR)
+        if $n_exps != 1;
+
+    my $exp = $exps->[0];
+    my $path_base = $exp->{path_base};
+
+    my $status = 1;
+    # don't clean up unless the data needed to update is available
+    if ($mode eq "goto_cleaned") {
+        my $config_file = $ipprc->filename("PSASTRO.CONFIG", $path_base);
+
+        if (!$config_file or ! -e $config_file) {
+            print STDERR "skipping cleanup for camRun $stage_id because config file is missing\n";
+            $status = 0;
+        }
+    }
+    if ($status) {
+        my @files = ();
+        # delete the temporary image datafiles
+        addFilename (\@files, "PSASTRO.OUTPUT", $path_base);
+        if ($mode eq "goto_purged") {
+            # additional files to remove for 'purge' mode
+            addFilename (\@files, "PPIMAGE.JPEG1", $path_base);
+            addFilename (\@files, "PPIMAGE.JPEG2", $path_base);
+            addFilename (\@files, "PSASTRO.STATS", $path_base);
+        }
+        # actual command to delete the files
+        $status = &delete_files (\@files);
+    }
+
+    if ($status)  {
+        my $command = "$camtool -cam_id $stage_id -updaterun";
+        if ($mode eq "goto_cleaned") {
+            $command .= " -state cleaned";
+        } else {
+            $command .= " -state purged";
+        }
+        $command .= " -dbname $dbname" if defined $dbname;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform camtool: $error_code", "camera", $stage_id, $error_code);
+        }
+    } else {
+        my $command = "$camtool -updateprocessedexp -cam_id $stage_id -code 1";
+        $command .= " -dbname $dbname" if defined $dbname;
+
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform camtool: $error_code", "camera", $stage_id, $error_code);
+        }
+        exit $PS_EXIT_UNKNOWN_ERROR;
+    }
+    exit 0;
+} elsif ($stage eq "warp") {
+    die "--stage_id required for stage warp\n" if !$stage_id;
+    # this stage uses 'warptool'
+    my $warptool = can_run('warptool') or die "Can't find warptool";
+
+    # Get list of component imfiles
+    # XXX may need a different my_die for each stage
+    my $skyfiles;                      # Array of component files
+    my $command = "$warptool -pendingcleanupskyfile -warp_id $stage_id"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform warptool: $error_code", "warp", $stage_id, $error_code);
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", "warp", $stage_id, $PS_EXIT_PROG_ERROR);
+
+    $skyfiles = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", "warp", $stage_id, $PS_EXIT_PROG_ERROR);
+
+    my @files = ();
+    foreach my $skyfile (@$skyfiles) {
+        my $path_base = $skyfile->{path_base};
+        my $skycell_id = $skyfile->{skycell_id};
+
+        my $status = 1;
+        if ($mode eq "goto_cleaned") {
+            my $config_file = $ipprc->filename("PSWARP.CONFIG", $path_base, $skycell_id);
+
+            if (!$config_file or ! -e $config_file) {
+                print STDERR "skipping cleanup for warpRun $stage_id $skycell_id" .
+                    " because config file is missing\n";
+                $status = 0;
+            }
+        }
+        if ($status) {
+            # delete the temporary image datafiles
+            addFilename(\@files, "PSWARP.OUTPUT", $path_base, $skycell_id );
+            addFilename(\@files, "PSWARP.OUTPUT.MASK", $path_base, $skycell_id);
+            addFilename(\@files, "PSWARP.OUTPUT.WEIGHT", $path_base, $skycell_id);
+            addFilename(\@files, "PSWARP.OUTPUT.SOURCES", $path_base, $skycell_id);
+
+            if ($mode eq "goto_purged") {
+                # additional files to remove for 'purge' mode
+                addFilename(\@files, "PSWARP.BIN1", $path_base, $skycell_id );
+                addFilename(\@files, "PSWARP.BIN2", $path_base, $skycell_id );
+                addFilename(\@files, "SKYCELL.STATS", $path_base, $skycell_id );
+                # addFilename(\@files, "PSPHOT.PSF.SKY.SAVE", $path_base);
+
+                # XXX: do we want to delete these?
+                # addFilename(\@files, "TRACE.EXP", $path_base, $skycell_id);
+                # addFilename(\@files, "PSWARP.CONFIG", $path_base, $skycell_id);
+            }
+            # actual command to delete the files
+            $status = &delete_files (\@files);
+        }
+
+	if ($status)  {
+	    my $command = "$warptool -warp_id $stage_id -skycell_id $skycell_id";
+            if ($mode eq "goto_purged") {
+                $command .= " -topurgedskyfile";
+            } else {
+                $command .= " -tocleanedskyfile";
+            }
+	    $command .= " -dbname $dbname" if defined $dbname;
+
+	    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run(command => $command, verbose => $verbose);
+	    unless ($success) {
+		$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+		&my_die("Unable to perform warptool: $error_code", "warp", $stage_id, $error_code);
+	    }
+         } else {
+            # XXX: -updateskyfile mode does not exist, need to add it
+	    my $command = "$warptool -updateskyfile -warp_id $stage_id -skycell_id $skycell_id -code 1";
+	    $command .= " -dbname $dbname" if defined $dbname;
+
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+	    unless ($success) {
+		$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+		&my_die("Unable to perform warptool: $error_code", "warp", $stage_id, $error_code);
+	    }
+            exit $PS_EXIT_UNKNOWN_ERROR;
+	}
+    }
+    exit 0;
+}
+
+# left TODO
+# fake : faketool : -pendingcleanupimfile (loop over imfiles)
+# stack: stacktool : -pendingcleanupskyfile (loop over skyfiles)
+# diff:  difftool : -pendingcleanupskyfile
+
+die "ipp_cleanup.pl -stage $stage not yet implemented\n";
+
+sub delete_files 
+{
+    my $files = shift; # reference to a list of files to unlink
+    
+    # this script is, of course, very dangerous.  
+    foreach my $file (@$files) {
+	print STDERR "unlinking $file\n";
+        # XXX: need to handle files in nebulous
+	unlink $file;
+    }
+    return 1;
+}
+
+sub addFilename 
+{
+    my $files      = shift; # reference to a list of files to unlink
+    my $filerule   = shift; # filerule to add
+    my $path_base  = shift; # base filename
+    my $class_id   = shift; # class_id, if needed
+
+    my $file = $ipprc->filename($filerule, $path_base, $class_id);
+
+    push @$files, $file;
+    return 1;
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $stage = shift; # stage name
+    my $stage_id = shift; #  identifier
+    my $exit_code = shift; # Exit code
+    # outputImage and path_base are globals
+
+    carp($msg);
+    exit $exit_code;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_darkstats.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_darkstats.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_darkstats.pl	(revision 22322)
@@ -0,0 +1,188 @@
+#!/usr/bin/env perl
+
+# use warnings;
+# use strict;
+use Carp;
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use IPC::Cmd 0.36 qw( can_run run );
+use IO::Handle;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+my ($dbname, $det_id, $camera);
+
+GetOptions('dbname=s'    => \$dbname,
+	   'det_id=s'    => \$det_id,
+	   'camera|c=s'  => \$camera,
+	   ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+pod2usage(
+	  -msg => "USAGE: ipp_darkstats.pl --dbname (name) --det_id (id) --camera (name)",
+	  -exitval => 3,
+	  ) unless defined $dbname and defined $det_id and defined $camera;
+
+$ipprc->define_camera($camera);
+
+###  Get list of dark imfile results
+
+# define the dettool command
+my $command = "dettool -processedimfile -select_state stop"; # Command to run
+$command .= " -det_id $det_id";
+$command .= " -dbname $dbname" if defined $dbname;
+
+# run the dettool command and catch the output
+my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+    run(command => $command, verbose => 0);
+unless ($success) {
+    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+    &my_die("Unable to perform dettool: $error_code", $error_code);
+}
+
+# parse the output into a list
+my $mdcParser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+    &my_die("Unable to parse metadata config doc", $PS_EXIT_PROG_ERROR);
+my $list = parse_md_list($metadata) or
+    &my_die("Unable to parse metadata list", $PS_EXIT_PROG_ERROR);
+
+my @bg_data;
+my @bg_stdev_data;
+my @bg_name;
+my @bg_exptime;
+my %components;
+
+print STDERR "extracted the data from the database\n";
+
+# we now have a list of imfiles; we need to extract the background for each cell
+# from the stats files for each imfile
+foreach my $item (@$list) {
+    my $path_base = $item->{path_base};
+    my $class_id = $item->{class_id};
+    my $exp_time = $item->{exp_time};
+
+    my $rootName  = $ipprc->file_resolve ($path_base);
+    my $statsName = "$rootName.$class_id.stats";
+
+    # print STDERR "rootName: $rootName : $exp_time\n";
+    print STDERR "statsName: $statsName : $exp_time\n";
+
+    my $statsFile;
+    open $statsFile, $statsName;
+    my @contents = <$statsFile>;
+    close ($statsFile);
+
+    # print STDERR "contents: @contents\n";
+
+    my $parser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+    my $statsList = $parser->parse(join "", @contents) or &my_die("Unable to parse metadata for imfile stats", $PS_EXIT_SYS_ERROR);
+
+    &parse_stats_table ($exp_time, $class_id, $statsList);
+}
+
+print STDERR "parsed the stats from the data files\n";
+
+for (my $i = 0; $i < @bg_data; $i++) {
+    $nameX = "$bg_name[$i].exp";
+    $nameY = "$bg_name[$i].bg";
+    push @{$nameX}, $bg_exptime[$i];
+    push @{$nameY}, $bg_data[$i];
+}
+
+if (-e "output.dat") { unlink "output.dat"; }
+
+print STDERR "dumping stats\n";
+open (MANA, "|mana --norc");
+MANA->autoflush;
+
+foreach my $component (@components) {
+    $nameX = "$component.exp";
+    $nameY = "$component.bg";
+
+    print MANA "delete X Y\n";
+
+    open (DATA, ">$component.dat");
+    for (my $i = 0; $i < @{$nameX}; $i++) {
+	print DATA "${$nameX}[$i] ${$nameY}[$i]\n";
+    }
+    close (DATA);
+
+    print MANA "data $component.dat\n";
+    print MANA "read X 1 Y 2\n";
+    print MANA "fit X Y 2 -clip 3 3\n";
+    print MANA "output output.dat\n";
+    print MANA "echo $component METADATA\n";
+    print MANA "echo \"   NORDER_X  S32 2   \"\n";
+    print MANA "echo \"   VAL_X00   F64 \$C0\"\n";
+    print MANA "echo \"   VAL_X01   F64 \$C1\"\n";
+    print MANA "echo \"   VAL_X02   F64 \$C2\"\n";
+    print MANA "echo \"   NELEMENTS S32 3    \"\n";
+    print MANA "echo END\n";
+    print MANA "echo\n";
+    print MANA "output stdout\n";
+
+    print MANA "applyfit X Yf\n";
+    print MANA "lim X Y\n";
+    print MANA "clear\n";
+    print MANA "box\n";
+    print MANA "plot -x 2 -pt 2 -sz 1.0 -c black X Y\n";
+    print MANA "plot -x 2 -pt 7 -sz 1.0 -c red X Yf\n";
+
+    print STDERR "hit return to continue\n";
+    $answer = <STDIN>;
+}
+
+close (MANA);
+
+exit 0;
+
+sub parse_stats_table
+{
+    my ($exp_time, $tag, $md) = @_;
+
+    # descend through the fpa        
+    foreach my $entry (@$md) {
+	# print STDERR "name: $entry->{name}, class: $entry->{class}\n";
+        # recurse on nested metadata
+        if ($entry->{class} eq 'metadata') {
+	    my $newtag = $tag . "_" . $entry->{name};
+            &parse_stats_table ($exp_time, $newtag, $entry->{value});
+        }
+
+        if ($entry->{name} =~ /^(SAMPLE|ROBUST|FITTED|CLIPPED)/) {
+            # It's a statistic of some sort
+            if ($entry->{name} =~ /_STDEV$/) {
+                push @bg_stdev_data, $entry->{value};
+            } else {
+		push @bg_name,    $tag;
+                push @bg_data,    $entry->{value};
+		push @bg_exptime, $exp_time;
+		# print STDERR "$tag $exp_time $entry->{value}\n";
+            }
+	    if (!$componentsHash{$tag}) {
+		push @components, $tag;
+		$componentsHash{$tag} = 1;
+	    }
+	    next;
+	} 
+    }
+    return 1;
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    exit $exit_code;
+}
+
+# - get the exp_time as well from dettool
+# - build an array of bg & exptime for each cell
+# - fit the trend (in mana? pslib functions?)
+# - write the polynomial for each cell
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_datapath.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_datapath.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_datapath.pl	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+
+use PS::IPP::Config;
+my $ipprc = PS::IPP::Config->new();
+
+my $touch;
+
+GetOptions(
+    'touch'     => \$touch,
+);
+
+die "No filename specified.\n" if scalar @ARGV != 1;
+
+my $filename = shift @ARGV;
+
+my $resolved = $ipprc->file_resolve($filename, $touch);
+print "$resolved\n" if ($resolved);
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_detrend_combine.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_detrend_combine.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_detrend_combine.pl	(revision 22322)
@@ -0,0 +1,220 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Data::Dumper;
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use PS::IPP::Metadata::Stats;
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my $RECIPE_PPSTATS = 'CHIPSTATS'; # Recipe to use with ppStats
+
+# Parse command-line arguments
+my ($det_type, $filelevel, $inst, $telescope, $filter,
+    $det_id1, $iter1, $det_id2, $iter2, $operation, $mask,
+    $workdir, $dbname, $no_update);
+GetOptions(
+	   'det_type=s'    => \$det_type, # Detrend type for new detrend
+	   'filelevel=s'   => \$filelevel, # File level for new detrend
+	   'inst=s'        => \$inst, # Instrument for new detrend
+	   'telescope=s'   => \$telescope, # Telescope for new detrend
+	   'filter=s'      => \$filter,	# Filter name for new detrend
+	   'det_id1=s'	   => \$det_id1, # Detrend id for detrend 1
+	   'iteration1=s'  => \$iter1, # Iteration for detrend 1
+	   'det_id2=s'	   => \$det_id2, # Detrend id for detrend 2
+	   'iteration2=s'  => \$iter2, # Iteration for detrend 2
+	   'operation=s'   => \$operation, # Operation to perform on files
+	   'mask'          => \$mask, # Operation is on a mask
+	   'workdir=s'     => \$workdir, # Working directory for output files
+	   'dbname=s'      => \$dbname,	# Database name
+	   'no-update'     => \$no_update, # Don't update the database
+	   ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options --det_type --filelevel --inst --telescope --det_id1 --iteration1 --det_id2 --iteration2 --workdir",
+	   -exitval => 3,
+	   )
+    unless defined $det_type
+    and defined $filelevel
+    and defined $inst
+    and defined $telescope
+    and defined $det_id1
+    and defined $iter1
+    and defined $det_id2
+    and defined $iter2
+    and defined $operation
+    and defined $workdir;
+
+$ipprc->define_camera($inst);
+
+my $STATS = 
+   [   
+       #          PPSTATS KEYWORD         STATISTIC          CHIPTOOL FLAG
+       { name => "ROBUST_MEDIAN",  type => "mean",  flag => "-bg",             dtype => "float" },
+       { name => "ROBUST_MEDIAN",  type => "stdev", flag => "-bg_mean_stdev",  dtype => "float" },
+       { name => "ROBUST_STDEV",   type => "rms",   flag => "-bg_stdev",       dtype => "float" },
+   ];
+
+# Look for programs we need
+my $missing_tools;
+my $detselect = can_run('detselect') or (warn "Can't find detselect" and $missing_tools = 1);
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppArith = can_run('ppArith') or (warn "Can't find ppArith" 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
+
+# Get the list of inputs
+my $files1 = filelist($det_id1, $iter1); # Hash of input files for detrend 1
+my $files2 = filelist($det_id2, $iter2); # Hash of input files for detrend 2
+die("File lists for detrends have differing lengths") unless scalar keys %$files1 == scalar keys %$files2;
+
+my ($det_id, $iter);	      # Detrend identifier for the new detrend
+unless ($no_update) {
+    my $command = "$dettool -register_detrend -det_type $det_type -filelevel $filelevel -workdir $workdir " .
+	"-inst $inst -telescope $telescope"; # Command to run
+    $command .= " -filter $filter" if defined $filter;
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	die("Unable to run dettool -register_detrend: $error_code");
+    }
+
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or die("Unable to parse metadata config doc\n");
+    my $md = parse_md_list($metadata) or die("Unable to parse metadata list\n");
+
+    $det_id = $$md[0]->{det_id};
+    $iter = $$md[0]->{iteration};
+
+    die("Unable to get det_id and iteration for new detrend.\n") unless defined $det_id and defined $iter;
+} else {
+    $det_id = 'DUMMY_DET_ID';
+    $iter = 'DUMMY_ITER';
+}
+
+my $outRoot = caturi($workdir, "$inst.$det_id.$iter"); # Output root name
+my $filerule = (defined $mask ? "PPARITH.OUTPUT.MASK" : "PPARITH.OUTPUT.IMAGE"); # File rule for ppArith
+
+foreach my $class_id ( keys %$files1 ) {
+    my $md1 = $$files1{$class_id};
+    my $md2 = $$files2{$class_id};
+    die("Class_id=$class_id not defined for det_id=$det_id2") unless defined $md2;
+
+    my $uri1 = $$md1[0]->{uri};
+    my $uri2 = $$md2[0]->{uri};
+
+    die("Unable to find input file $uri1\n") unless $ipprc->file_exists($uri1);
+    die("Unable to find input file $uri2\n") unless $ipprc->file_exists($uri2);
+
+    my $outName = $ipprc->filename($filerule, $outRoot, $class_id);
+    my $outStats = $outRoot . '.stats';
+
+    my $command = "$ppArith -file1 $uri1 -op \'$operation\' -file2 $uri2 $outRoot"; # Command to run
+    $command .= " -stats $outStats -recipe PPSTATS $RECIPE_PPSTATS";
+    $command .= ' -mask' if defined $mask;
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	die("Unable to run ppArith: $error_code");
+    }
+
+    die("Unable to find ppArith product: $outName\n") unless $ipprc->file_exists($outName);
+    die("Unable to find ppArith product: $outStats\n") unless $ipprc->file_exists($outStats);
+
+    # Get the statistics on the processed image
+    my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+    {
+	my $statsFile;		# File handle
+	open $statsFile, $ipprc->file_resolve($outStats) or die("Can't open stats file $outStats: $!");
+	my @contents = <$statsFile>; # Contents of file
+	close $statsFile;
+	
+	my $metadata = $mdcParser->parse(join "", @contents) or die("Unable to parse metadata config doc");
+
+	unless ($stats->parse($metadata)) {
+	    &my_die("Failure extracting metadata from the statistics output file.\n");
+	}
+    }
+
+    # Register the imfile
+    unless ($no_update) {
+	my $command = "$dettool -register_detrend_imfile -det_id $det_id "; # Command to run
+	$command .= " -class_id $class_id -uri $outName -path_base $outRoot";
+	$command .= $stats->cmdflags();
+	$command .= " -dbname $dbname" if defined $dbname;
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $command, verbose => 1);
+	unless ($success) {
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    die("Unable to run dettool -register_detrend_imfile: $error_code");
+	}
+    }
+}
+
+
+### Pau.
+
+
+# Get a list of files for the given detrend
+sub filelist
+{
+    my $det_id = shift;		# Detrend identifier
+    my $iter = shift;		# Iteration
+
+    my $command = "$detselect -select -det_id $det_id -iteration $iter"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	die("Unable to run detselect: $error_code");
+    }
+
+    # Because of the length, need to split into individual metadatas --- it parses SO much quicker!
+    my %files;
+
+    my $md = $mdcParser->parse( join( "", @$stdout_buf ) ); # Parsed metadata
+    my $list = parse_md_list( $md );
+
+    foreach my $item ( @$list ) {
+	my $class_id = $item->{class_id};
+	die("Multiple definitions of class_id=$class_id found for det_id=$det_id, iteration=$iter\n") if
+	    defined $files{$class_id};
+	$files{$class_id} = parse_md_list($md);
+    }
+
+    return \%files;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_filename.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_filename.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_filename.pl	(revision 22322)
@@ -0,0 +1,52 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+ 
+# use these to check the apache environment
+#print "$ENV{'PATH'}\n";
+#print "$ENV{'PERL5LIB'}\n";
+
+use PS::IPP::Config;
+my $ipprc = PS::IPP::Config->new();
+  
+my ($filerule, $class_id, $basename, $camera, $touch);
+
+GetOptions('filerule=s'    => \$filerule,
+	   'class_id=s'    => \$class_id,
+	   'basename=s'    => \$basename,
+	   'camera|c=s'    => \$camera,
+           'touch'         => \$touch,
+	   ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --filerule --class_id --basename --camera",
+    -exitval => 3,
+) unless defined $basename
+    and defined $filerule 
+    and defined $class_id 
+    and defined $camera;
+
+$touch = 0 unless (defined $touch);
+
+$ipprc->define_camera($camera);
+
+# print "$filerule\n";
+# print "$basename\n";
+# print "$camera\n";
+# print "$class_id\n";
+
+my $filename = $ipprc->filename($filerule, $basename, $class_id);
+# print "$filename\n";
+# print "touch: $touch\n";
+
+my $realname = $ipprc->file_resolve( $filename, $touch );
+print "$realname\n";
+
+1;
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_image_path.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_image_path.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_image_path.pl	(revision 22322)
@@ -0,0 +1,170 @@
+#!/usr/bin/env perl
+
+# ipp_image_path.pl print out the unix path name(s) for rawImage files for an exposure
+# Note this program should really be called gpc1_image_path since the camera is
+# hardcoded to gpc1 and the telescope is gpc1
+#
+
+use warnings;
+use strict;
+
+# get images that are on ipp008 from alternative location until the
+# dataase is updated
+my $use_008_workaround  = 1;
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use DBI;
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Config 1.01 qw( :standard );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($exp_name, $class_id, $from_registered, $dbname, $verbose);
+
+GetOptions(
+    'exp_name|e=s'  => \$exp_name,
+    'class_id|c=s'  => \$class_id,
+    'registered|r'  => \$from_registered,
+    'dbname|d=s'    => \$dbname, # Database name    
+    'verbose'       => \$verbose,   # Print to stdout
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --exp_name",
+	   -exitval => 3) unless
+    defined $exp_name;
+
+my $ota_num;
+if ($class_id) {
+    # we accept class_id in 3 formats (all we care about is the number)
+    # xy23
+    # ota23
+    # 23
+    $class_id = lc($class_id);
+    my $extra;
+    (undef, $ota_num, $extra) = $class_id =~ m/(\D*)(\d\d)(.*)/;
+    if ($extra || !$ota_num) {
+        print STDERR "$class_id is not a valid class_id\n";
+        exit 1;
+    }
+}
+
+# Look for commands we need
+my $missing_tools;
+my $neb_locate = can_run('neb-locate')
+    or (warn "can't find neb-locate" and $missing_tools = 1);
+
+if ($missing_tools) { 
+    warn ("Can't find required tools");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+
+my $ipprc = PS::IPP::Config->new();
+my $dbh = getDBHandle();
+
+# Get the list of imfiles
+{
+    # XXX: If there are multiple exposures with the same exposure name
+    # this query will return them all
+    my $query;
+    if ($from_registered) {
+        $query = "SELECT exp_id, class_id, uri FROM rawImfile"
+                . " WHERE exp_name = \'$exp_name\'"
+                . " AND camera = \'gpc1\' AND telescope = \'ps1\'";
+        $query .= " AND class_id = \'xy$ota_num\'" if ($ota_num);
+    } else {
+        $query = "SELECT exp_id FROM newExp WHERE tmp_exp_name = ?"
+                . " AND tmp_camera = \'gpc1\' AND tmp_telescope = \'ps1\'";
+        my $stmt = $dbh->prepare($query);
+        $stmt->execute($exp_name);
+        my $exp_ref = $stmt->fetchrow_hashref();
+        if (!$exp_ref) {
+            print STDERR "exposure $exp_name not found\n";
+            exit 1;
+        }
+        $stmt->finish();
+        $query = "SELECT exp_id, tmp_class_id, uri FROM newImfile WHERE exp_id = $exp_ref->{exp_id}";
+        $query .= " AND tmp_class_id = \'ota$ota_num\'" if ($ota_num);
+    }
+
+    my $stmt = $dbh->prepare($query);
+    $stmt->execute();
+    my $nfiles = 0;
+    while (my $ref = $stmt->fetchrow_hashref()) {
+        my $uri = $ref->{uri};
+        my $path;
+        my ($scheme) = $uri =~/^(path|neb|file):/;
+
+        if (!$scheme) {
+            $path =  $uri;
+        } else {
+            if ($use_008_workaround && ($uri =~ /ipp008/)) {
+                $path = resolve_ipp008_file($uri);
+            } else {
+                $path = $ipprc->file_resolve($uri);
+            }
+        }
+        if ($path) {
+            # remove the leading "file://" if present
+            $path =~ s/^file\:\/\///;
+        }
+        $nfiles++;
+        print "$path\n";
+    }
+    if (!$nfiles) {
+        my $cstr = defined($class_id) ? $class_id . " " : "";
+        print STDERR "exposure $exp_name " . $cstr . "not found\n";
+        exit 2;
+    }
+}
+
+sub getDBHandle {
+    my $dbserver = metadataLookupStr($ipprc->{_siteConfig}, "DBSERVER");
+    my $dbuser = metadataLookupStr($ipprc->{_siteConfig}, "DBUSER");
+    my $dbpassword = metadataLookupStr($ipprc->{_siteConfig}, "DBPASSWORD");
+    if (!$dbname) {
+        $dbname = metadataLookupStr($ipprc->{_siteConfig}, "DBNAME");
+    }
+
+    die "database configuration set up" unless defined($dbserver) and defined($dbuser)
+        and defined($dbpassword) and defined($dbname);
+
+    my $dsn = "DBI:mysql:host=$dbserver;database=$dbname";
+
+    my $dbh = DBI->connect($dsn, $dbuser, $dbpassword) 
+        or die "Cannot connect to database.\n";
+
+    return $dbh;
+}
+
+sub resolve_ipp008_file {
+    my %locations = ( "ota24" => "ipp006",
+                      "ota25" => "ipp009",
+                      "ota26" => "ipp011",
+                      "ota27" => "ipp019",
+                      "ota30" => "ipp020",
+                      "ota31" => "ipp021"
+                    );
+
+    my $uri = shift;
+
+    # uri's look like neb://ipp008.0/gpc1/20080625/o4642g0400o/o4642g0400o.ota24.fits
+    
+    my ($left, $middle, $ota, $fits) = split '\.', $uri;
+    
+    my $node = $locations{$ota};
+
+    die "can't find node for $ota: $uri" if (!$node);
+
+    my $path =  $uri;
+    
+    $path =~  s%neb://ipp008.0%/data/${node}.0/recover08%;
+
+    die "sorry backup image for $uri not found\n" if (!-e $path);
+
+    return $path;
+}
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_inject_expname.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_inject_expname.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_inject_expname.pl	(revision 22322)
@@ -0,0 +1,190 @@
+#!/usr/bin/env perl
+
+# this program injects a set of files the db, assuming the list of
+# files all belong to a single exposure the user supplies a temporary
+# telescope and camera name.  these are used for informational
+# purposes only until the registration step can determine the true
+# telescope and camera name from the image headers.
+
+# this program should not fail because of the data format or the
+# configuration, except for the very basic database setup.
+
+use warnings;
+use strict;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Spec;
+use PS::IPP::Config 1.01 qw( :standard );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+my $ipprc = PS::IPP::Config->new(); # this is used for PATH, NEB filename conversions
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line arguments
+my ($camera, $telescope, $workdir, $reduction, $dvo_db, $tess_id, $end_stage, $label, $from_db, $dbname, $no_op, $verbose, $help);
+GetOptions('camera|i=s'     => \$camera,    # user-supplied camera name
+	   'telescope|t=s'  => \$telescope, # user-supplied telescope name
+	   'workdir|w=s'    => \$workdir,   # working directory for output files
+	   'reduction=s'    => \$reduction, # user-supplied camera name
+	   'dvodb=s'        => \$dvo_db,    # target dvo database 
+	   'tess_id=s'      => \$tess_id,   # tessalation for warping
+	   'end_stage=s'    => \$end_stage, # stop processing at this step
+	   'label=s'        => \$label,     # set chip label
+	   'from=s'         => \$from_db,   # Source Database name
+	   'dbname|d=s'     => \$dbname,    # Database name
+           'verbose'        => \$verbose,   # Print to stdout
+	   'no-op'          => \$no_op,     # pretend but don't actually inject
+	   'help'           => \$help       # give help listing
+	   ) or pod2usage( 2 );
+
+pod2usage( -msg => "inject files based on query to another IPP db", 
+	   -exitval => 2) if 
+    defined $help;
+
+pod2usage( -msg => "Usage: $0 [--from dbname] [--workdir path] [--reduction class] [--dvodb db] [--tess_id tess] [--end_stage stage] [--label label] [--dbname dbname] (exp_name)", 
+	   -exitval => 2 ) if 
+    scalar @ARGV == 0;
+
+pod2usage(
+          -msg => "Required options: --exp_tag --cam_id --camera --outroot",
+          -exitval => 3,
+          ) unless
+    defined $from_db;
+
+my $exp_name = $ARGV[0];
+
+my $pxinject = can_run('pxinject') or die "Can't find pxinject\n";
+my $regtool = can_run('regtool') or die "Can't find regtool\n";
+
+# tess_id needs to be an absolute path, or it must start with file://, path://, or neb://
+if ($tess_id) {
+    $tess_id = &fixpath ($tess_id);
+}
+
+# dvodb needs to be an absolute path, or it must start with file://, path://, or neb://
+if ($dvo_db) {
+    $dvo_db = &fixpath ($dvo_db);
+}
+
+# workdir needs to be an absolute path, or it must start with file://, path://, or neb://
+if ($workdir) {
+    $workdir = &fixpath ($workdir);
+}
+
+# if workdir is not defined, assign the current path
+if (! $workdir) {
+    $workdir = File::Spec->rel2abs( "." );
+}
+
+if (! $telescope) {
+    $telescope = "UNKNOWN";
+}
+
+if (! $camera) {
+    $camera = "UNKNOWN";
+}
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+# query the from_db for the desired imfiles:
+# regtool -dbname $from_db -processedimfile -exp_name $exp_name
+
+# Get list of component files
+my $files;                      # Array of component files
+{
+    my $command = "$regtool -dbname $from_db -processedimfile -exp_name $exp_name"; # Command to run
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        die "Unable to perform regtool for $exp_name: $error_code";
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        die "Unable to parse metadata config doc $exp_name: $error_code";
+
+    # extract the metadata for the files into a hash list
+    $files = parse_md_list($metadata) or
+        die "Unable to parse metadata list: $exp_name: $error_code";
+}
+
+## inject the exposure
+
+# the telescope, instrument, and exp_name used here are temporary : register replaces them with the true values
+my $command_exp = "$pxinject -newExp";
+$command_exp .= " -tmp_exp_name $exp_name";
+$command_exp .= " -tmp_inst $camera";
+$command_exp .= " -tmp_telescope $telescope";
+$command_exp .= " -workdir $workdir";
+$command_exp .= " -reduction $reduction" if defined $reduction;
+$command_exp .= " -dvodb $dvo_db"        if defined $dvo_db;
+$command_exp .= " -tess_id $tess_id"     if defined $tess_id;
+$command_exp .= " -end_stage $end_stage" if defined $end_stage;
+$command_exp .= " -label $label"         if defined $label;
+$command_exp .= " -dbname $dbname"       if defined $dbname;
+
+my $exp_id = 0;
+unless ($no_op) {
+    my ( $success_exp, $error_code_exp, $full_buf_exp, $stdout_buf_exp, $stderr_buf_exp ) =
+	run( command => $command_exp, verbose => 1 );
+    die "Unable to inject $exp_name: $error_code_exp\n" if not $success_exp;
+
+    my @line = split(/\s+/, $$stdout_buf_exp[0]); # The output line, containing the exposure tag
+    $exp_id = $line[2];	# The exposure tag
+} else {
+    print "skipping command: $command_exp\n";
+}
+
+# now inject the imfiles one at a time
+foreach my $file (@$files) {
+
+    my $uri = $file->{uri};
+    my $class_id = $file->{class_id};
+
+    # the class_id used here is temporary : register replaces it with the true class_id
+    my $command_imfile = "$pxinject -newImfile";
+    $command_imfile .= " -exp_id $exp_id";
+    $command_imfile .= " -tmp_class_id $class_id";
+    $command_imfile .= " -uri $uri";
+    $command_imfile .= " -dbname $dbname" if defined ($dbname);
+    
+    unless ($no_op) {
+	my ( $success_imfile, $error_code_imfile, $full_buf_imfile, $stdout_buf_imfile, $stderr_buf_imfile ) = run( command => $command_imfile, verbose => 1 );
+	die "Unable to inject $exp_name imfile: $error_code_imfile\n" if not $success_imfile;
+    } else {
+	print "skipping command: $command_imfile\n";
+    }
+}
+
+# the class_id used here is temporary : register replaces it with the true class_id
+my $command_update = "$pxinject -updatenewExp";
+$command_update .= " -exp_id $exp_id";
+$command_update .= " -state run";
+$command_update .= " -dbname $dbname" if defined ($dbname);
+
+unless ($no_op) {
+    my ( $success_update, $error_code_update, $full_buf_update, $stdout_buf_update, $stderr_buf_update ) = run( command => $command_update, verbose => 1 );
+    die "Unable to update $exp_name: $error_code_update\n" if not $success_update;
+} else {
+    print "skipping command: $command_update\n";
+}
+
+exit 0;
+
+sub fixpath {
+    my $path = shift;
+    my $valid = 0;
+    $valid |= ($path =~ m|^file://|);
+    $valid |= ($path =~ m|^path://|);
+    $valid |= ($path =~ m|^neb://|);
+    $valid |= ($path =~ m|^/|);
+
+    if (!$valid) {
+	$path = File::Spec->rel2abs( $path );
+    }
+    return $path;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_inject_fileset.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_inject_fileset.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_inject_fileset.pl	(revision 22322)
@@ -0,0 +1,177 @@
+#!/usr/bin/env perl
+
+# this program injects a set of files the db, assuming the list of
+# files all belong to a single exposure the user supplies a temporary
+# telescope and camera name.  these are used for informational
+# purposes only until the registration step can determine the true
+# telescope and camera name from the image headers.
+
+# this program should not fail because of the data format or the
+# configuration, except for the very basic database setup.
+
+use warnings;
+use strict;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Spec;
+use PS::IPP::Config;
+
+my $ipprc = PS::IPP::Config->new(); # this is used for PATH, NEB filename conversions
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line arguments
+my ($camera, $telescope, $workdir, $reduction, $dvo_db, $tess_id, $end_stage, $label, $dbname, $no_op, $help);
+GetOptions('camera|i=s'     => \$camera,    # user-supplied camera name
+	   'telescope|t=s'  => \$telescope, # user-supplied telescope name
+	   'workdir|w=s'    => \$workdir,   # working directory for output files
+	   'reduction=s'    => \$reduction, # user-supplied camera name
+	   'dvodb=s'        => \$dvo_db,    # target dvo database 
+	   'tess_id=s'      => \$tess_id,   # tessalation for warping
+	   'end_stage=s'    => \$end_stage, # stop processing at this step
+	   'label=s'        => \$label,     # set chip label
+	   'dbname|d=s'     => \$dbname,    # Database name
+	   'no-op'          => \$no_op,     # pretend but don't actually inject
+	   'help'           => \$help       # give help listing
+	   ) or pod2usage( 2 );
+
+pod2usage( -msg => "inject one or many files into the IPP pipeline database", 
+	   -exitval => 2) if 
+    defined $help;
+
+pod2usage( -msg => "Usage: $0 --telescope (name) --camera (name) [--workdir path] [--reduction class] [--dvodb db] [--tess_id tess] [--end_stage stage] [--label label] [--dbname dbname] (files)", 
+	   -exitval => 2 ) if 
+    scalar @ARGV == 0;
+
+# XXX why are these required?
+pod2usage( -msg => "Required options: --telescope (name) --camera (name)",
+	   -exitval => 3) unless @ARGV > 0;
+
+my $pxinject = can_run('pxinject') or die "Can't find pxinject\n";
+
+# tess_id needs to be an absolute path, or it must start with file://, path://, or neb://
+if ($tess_id) {
+    $tess_id = &fixpath ($tess_id);
+}
+
+# dvodb needs to be an absolute path, or it must start with file://, path://, or neb://
+if ($dvo_db) {
+    $dvo_db = &fixpath ($dvo_db);
+}
+
+# workdir needs to be an absolute path, or it must start with file://, path://, or neb://
+if ($workdir) {
+    $workdir = &fixpath ($workdir);
+}
+
+# if workdir is not defined, assign the current path
+if (! $workdir) {
+    $workdir = File::Spec->rel2abs( "." );
+}
+
+if (! $telescope) {
+    $telescope = "UNKNOWN";
+}
+
+if (! $camera) {
+    $camera = "UNKNOWN";
+}
+
+# use the first file name as the exp_name (strip off .fits)
+my $num = 0;
+my $exp_name;
+foreach my $file ( @ARGV ) {
+    # check for file existence
+    if (! -e $file) { die "file $file not found\n"; }
+    if (! $exp_name) { 
+	# strip off the extension
+	my ( $vol, $path, $name ) = File::Spec->splitpath( $file );
+	( $exp_name ) = $name =~ /(.*)\.(fits|fit|fts)(|.gz)/;
+	print "exp_name : $exp_name.\n";
+    }
+    $num ++;
+}
+
+print "$num files in fileset.\n";
+
+## inject the exposure
+
+# the telescope, instrument, and exp_name used here are temporary : register replaces them with the true values
+my $command_exp = "$pxinject -newExp";
+$command_exp .= " -tmp_exp_name $exp_name";
+$command_exp .= " -tmp_inst $camera";
+$command_exp .= " -tmp_telescope $telescope";
+$command_exp .= " -workdir $workdir";
+$command_exp .= " -reduction $reduction" if defined $reduction;
+$command_exp .= " -dvodb $dvo_db"        if defined $dvo_db;
+$command_exp .= " -tess_id $tess_id"     if defined $tess_id;
+$command_exp .= " -end_stage $end_stage" if defined $end_stage;
+$command_exp .= " -label $label"         if defined $label;
+$command_exp .= " -dbname $dbname"       if defined $dbname;
+
+my $exp_id = 0;
+unless ($no_op) {
+    my ( $success_exp, $error_code_exp, $full_buf_exp, $stdout_buf_exp, $stderr_buf_exp ) =
+	run( command => $command_exp, verbose => 1 );
+    die "Unable to inject $exp_name: $error_code_exp\n" if not $success_exp;
+
+    my @line = split(/\s+/, $$stdout_buf_exp[0]); # The output line, containing the exposure tag
+    $exp_id = $line[2];	# The exposure tag
+} else {
+    print "skipping command: $command_exp\n";
+}
+
+# now inject the imfiles one at a time
+for (my $i = 0; $i < @ARGV; $i++) {
+
+    my $file = $ARGV[$i];
+
+    my $absfile = File::Spec->rel2abs( $file );
+    my $relfile = $ipprc->convert_filename_relative( $absfile );
+
+    # the class_id used here is temporary : register replaces it with the true class_id
+    my $command_imfile = "$pxinject -newImfile";
+    $command_imfile .= " -exp_id $exp_id";
+    $command_imfile .= " -tmp_class_id file.$i";
+    $command_imfile .= " -uri $relfile";
+    $command_imfile .= " -dbname $dbname" if defined ($dbname);
+    
+    unless ($no_op) {
+	my ( $success_imfile, $error_code_imfile, $full_buf_imfile, $stdout_buf_imfile, $stderr_buf_imfile ) = run( command => $command_imfile, verbose => 1 );
+	die "Unable to inject $exp_name imfile: $error_code_imfile\n" if not $success_imfile;
+    } else {
+	print "skipping command: $command_imfile\n";
+    }
+}
+
+# the class_id used here is temporary : register replaces it with the true class_id
+my $command_update = "$pxinject -updatenewExp";
+$command_update .= " -exp_id $exp_id";
+$command_update .= " -state run";
+$command_update .= " -dbname $dbname" if defined ($dbname);
+
+unless ($no_op) {
+    my ( $success_update, $error_code_update, $full_buf_update, $stdout_buf_update, $stderr_buf_update ) = run( command => $command_update, verbose => 1 );
+    die "Unable to update $exp_name: $error_code_update\n" if not $success_update;
+} else {
+    print "skipping command: $command_update\n";
+}
+
+exit 0;
+
+sub fixpath {
+    my $path = shift;
+    my $valid = 0;
+    $valid |= ($path =~ m|^file://|);
+    $valid |= ($path =~ m|^path://|);
+    $valid |= ($path =~ m|^neb://|);
+    $valid |= ($path =~ m|^/|);
+
+    if (!$valid) {
+	$path = File::Spec->rel2abs( $path );
+    }
+    return $path;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_maskscript.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_maskscript.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_maskscript.pl	(revision 22322)
@@ -0,0 +1,190 @@
+#!/usr/bin/env perl
+
+# use warnings;
+# use strict;
+use Carp;
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use IPC::Cmd 0.36 qw( can_run run );
+use IO::Handle;
+
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+my ($dbname, $det_id, $camera);
+
+GetOptions('dbname=s'    => \$dbname,
+	   'det_id=s'    => \$det_id,
+	   'camera|c=s'  => \$camera,
+	   ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+pod2usage(
+	  -msg => "USAGE: ipp_maskscript.pl --dbname (name) --det_id (id) --iter (iteration) --camera (name)",
+	  -exitval => 3,
+	  ) unless defined $dbname and defined $det_id and defined $camera;
+
+# I could determine the camera from a query for the detrun
+$ipprc->define_camera($camera);
+
+###  Get list of dark imfile results
+
+# define the dettool command
+my $command = "dettool -processedimfile -select_state stop"; # Command to run
+$command .= " -det_id $det_id";
+$command .= " -dbname $dbname" if defined $dbname;
+
+# run the dettool command and catch the output
+my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+    run(command => $command, verbose => 0);
+unless ($success) {
+    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+    &my_die("Unable to perform dettool: $error_code", $error_code);
+}
+
+# parse the output into a list
+my $mdcParser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+    &my_die("Unable to parse metadata config doc", $PS_EXIT_PROG_ERROR);
+my $list = parse_md_list($metadata) or
+    &my_die("Unable to parse metadata list", $PS_EXIT_PROG_ERROR);
+
+my @bg_data;
+my @bg_stdev_data;
+my @bg_name;
+my @bg_exptime;
+my %components;
+
+print STDERR "extracted the data from the database\n";
+
+# we now have a list of imfiles; we need to extract the background for each cell
+# from the stats files for each imfile
+foreach my $item (@$list) {
+    my $path_base = $item->{path_base};
+    my $class_id = $item->{class_id};
+    my $exp_time = $item->{exp_time};
+
+    my $rootName  = $ipprc->file_resolve ($path_base);
+    my $statsName = "$rootName.$class_id.stats";
+
+    # print STDERR "rootName: $rootName : $exp_time\n";
+    print STDERR "statsName: $statsName : $exp_time\n";
+
+    my $statsFile;
+    open $statsFile, $statsName;
+    my @contents = <$statsFile>;
+    close ($statsFile);
+
+    # print STDERR "contents: @contents\n";
+
+    my $parser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+    my $statsList = $parser->parse(join "", @contents) or &my_die("Unable to parse metadata for imfile stats", $PS_EXIT_SYS_ERROR);
+
+    &parse_stats_table ($exp_time, $class_id, $statsList);
+}
+
+print STDERR "parsed the stats from the data files\n";
+
+for (my $i = 0; $i < @bg_data; $i++) {
+    $nameX = "$bg_name[$i].exp";
+    $nameY = "$bg_name[$i].bg";
+    push @{$nameX}, $bg_exptime[$i];
+    push @{$nameY}, $bg_data[$i];
+}
+
+if (-e "output.dat") { unlink "output.dat"; }
+
+print STDERR "dumping stats\n";
+open (MANA, "|mana --norc");
+MANA->autoflush;
+
+foreach my $component (@components) {
+    $nameX = "$component.exp";
+    $nameY = "$component.bg";
+
+    print MANA "delete X Y\n";
+
+    open (DATA, ">$component.dat");
+    for (my $i = 0; $i < @{$nameX}; $i++) {
+	print DATA "${$nameX}[$i] ${$nameY}[$i]\n";
+    }
+    close (DATA);
+
+    print MANA "data $component.dat\n";
+    print MANA "read X 1 Y 2\n";
+    print MANA "fit X Y 2 -clip 3 3\n";
+    print MANA "output output.dat\n";
+    print MANA "echo $component METADATA\n";
+    print MANA "echo \"   NORDER_X  S32 2   \"\n";
+    print MANA "echo \"   VAL_X00   F64 \$C0\"\n";
+    print MANA "echo \"   VAL_X01   F64 \$C1\"\n";
+    print MANA "echo \"   VAL_X02   F64 \$C2\"\n";
+    print MANA "echo \"   NELEMENTS S32 3    \"\n";
+    print MANA "echo END\n";
+    print MANA "echo\n";
+    print MANA "output stdout\n";
+
+    print MANA "applyfit X Yf\n";
+    print MANA "lim X Y\n";
+    print MANA "clear\n";
+    print MANA "box\n";
+    print MANA "plot -x 2 -pt 2 -sz 1.0 -c black X Y\n";
+    print MANA "plot -x 2 -pt 7 -sz 1.0 -c red X Yf\n";
+
+    print STDERR "hit return to continue\n";
+    $answer = <STDIN>;
+}
+
+close (MANA);
+
+exit 0;
+
+sub parse_stats_table
+{
+    my ($exp_time, $tag, $md) = @_;
+
+    # descend through the fpa        
+    foreach my $entry (@$md) {
+	# print STDERR "name: $entry->{name}, class: $entry->{class}\n";
+        # recurse on nested metadata
+        if ($entry->{class} eq 'metadata') {
+	    my $newtag = $tag . "_" . $entry->{name};
+            &parse_stats_table ($exp_time, $newtag, $entry->{value});
+        }
+
+        if ($entry->{name} =~ /^(SAMPLE|ROBUST|FITTED|CLIPPED)/) {
+            # It's a statistic of some sort
+            if ($entry->{name} =~ /_STDEV$/) {
+                push @bg_stdev_data, $entry->{value};
+            } else {
+		push @bg_name,    $tag;
+                push @bg_data,    $entry->{value};
+		push @bg_exptime, $exp_time;
+		# print STDERR "$tag $exp_time $entry->{value}\n";
+            }
+	    if (!$componentsHash{$tag}) {
+		push @components, $tag;
+		$componentsHash{$tag} = 1;
+	    }
+	    next;
+	} 
+    }
+    return 1;
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $exit_code = shift; # Exit code to add
+
+    carp($msg);
+    exit $exit_code;
+}
+
+# - get the exp_time as well from dettool
+# - build an array of bg & exptime for each cell
+# - fit the trend (in mana? pslib functions?)
+# - write the polynomial for each cell
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_mops_translate.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_mops_translate.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_mops_translate.pl	(revision 22322)
@@ -0,0 +1,271 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use Math::Trig;
+use Data::Dumper;
+
+use constant EXTNAME => 'MOPS_TRANSIENT_DETECTIONS'; # Extension name for output table
+use constant POSITION_ERROR => 0.2 / 3600; # Error in positions, degrees
+use constant ZERO_POINT => 25;  # Magnitude zero point
+use constant MAG_ERROR => 0.1;  # Error in magnitudes
+use constant OBSERVATORY_CODE => 566; # IAU Observatory Code
+use constant FAKE_LIMITING_MAG => 23.0; # Fake limiting magnitude to report
+use constant FAKE_DETECTION_EFFICIENCY => 0.0; # Fake detection efficiency to report
+
+my ( $input,                    # Name of input file with IPP photometry
+     $extname,                  # Name of extension containing photometry
+     $skycell,                  # Skycell file with WCS
+     $output,                   # Name of output file
+     $save_temps,               # Save temporary files?
+     );
+
+GetOptions(
+           'input=s'    => \$input,
+           'extname=s'  => \$extname,
+           'skycell=s'  => \$skycell,
+           'output=s'   => \$output,
+           'save-temps' => \$save_temps,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --input --extname --skycell --output",
+           -exitval => 3)
+    unless defined $input
+    and defined $extname
+    and defined $skycell
+    and defined $output;
+
+# Specification of columns to write
+my $columns = [ { name => 'RA_DEG',   type => 'D' }, # Right ascension
+                { name => 'DEC_DEG',  type => 'D' }, # Declination
+                { name => 'RA_SIG',   type => 'D' }, # Error in right ascension
+                { name => 'DEC_SIG',  type => 'D' }, # Error in declination
+                { name => 'FLUX',     type => 'D' }, # Flux
+                { name => 'FLUX_SIG', type => 'D' }, # Error in flux
+                { name => 'ANG',      type => 'D' }, # Angle
+                { name => 'ANG_SIG',  type => 'D' }, # Error in angle
+                { name => 'LEN',      type => 'D' }, # Length
+                { name => 'LEN_SIG',  type => 'D' }, # Error in length
+                ];
+
+# Header translation table
+my $headers = {
+    'FPA.RA'       => { name => 'RA',       type => TSTRING, comment => 'Right ascension of boresight'},
+    'FPA.DEC'      => { name => 'DEC',      type => TSTRING, comment => 'Declination of boresight' },
+    'FPA.OBS'      => { name => 'FPA_ID',   type => TSTRING, comment => 'Exposure identifier' },
+    'FPA.FILTER'   => { name => 'FILTER',   type => TSTRING, comment => 'Filter name' },
+    'EXPTIME'      => { name => 'EXPTIME',  type => TDOUBLE, comment => 'Exposure time' },
+    'FPA.POSANGLE' => { name => 'ROTANGLE', type => TDOUBLE, comment => 'Position angle' },
+    'FPA.ALT'      => { name => 'TEL_ALT',  type => TDOUBLE, comment => 'Telescope altitude' },
+    'FPA.AZ'       => { name => 'TEL_AZ',   type => TDOUBLE, comment => 'Telescope azimuth' },
+            };
+
+
+# Read the skycell header for the WCS
+my ($wcsheader, $status) = Astro::FITS::CFITSIO::fits_read_header( $skycell );
+
+# Get the useful header keywords
+my $naxis1 = $$wcsheader{'NAXIS1'} or die("Can't find NAXIS1");
+my $naxis2 = $$wcsheader{'NAXIS2'} or die("Can't find NAXIS2");
+my $ctype1 = $$wcsheader{'CTYPE1'} or die("Can't find CTYPE1");
+my $ctype2 = $$wcsheader{'CTYPE2'} or die("Can't find CTYPE2");
+my $cdelt1 = $$wcsheader{'CDELT1'} or die("Can't find CDELT1");
+my $cdelt2 = $$wcsheader{'CDELT2'} or die("Can't find CDELT2");
+my $crval1 = deg2rad($$wcsheader{'CRVAL1'}) or die("Can't find CRVAL1");
+my $crval2 = deg2rad($$wcsheader{'CRVAL2'}) or die("Can't find CRVAL2");
+my $crpix1 = $$wcsheader{'CRPIX1'} or die("Can't find CRPIX1");
+my $crpix2 = $$wcsheader{'CRPIX2'} or die("Can't find CRPIX2");
+my $pc11 = $$wcsheader{'PC001001'} or die("Can't find PC001001");
+my $pc12 = $$wcsheader{'PC001002'} or die("Can't find PC001002");
+my $pc21 = $$wcsheader{'PC002001'} or die("Can't find PC002001");
+my $pc22 = $$wcsheader{'PC002002'} or die("Can't find PC002002");
+my $crota1 = $$wcsheader{'CROTA1'};
+my $crota2 = $$wcsheader{'CROTA2'};
+
+die("Unexpected projection: $ctype1 and $ctype2.") unless $ctype1 =~ /^\'RA---TAN\s*\'$/ and
+    $ctype2 =~ /^\'DEC--TAN\s*\'$/;
+die("Can't determine size of skycell ($naxis1,$naxis2)") unless $naxis1 > 0 and $naxis2 > 0;
+die("Can't determine scale of skycell ($cdelt1,$cdelt2)") if not defined $cdelt1 or $cdelt1 == 0 or
+    not defined $cdelt2 or $cdelt2 == 0;
+die("We don't know how to handle rotations ($crota1,$crota2)") if defined $crota1 or defined $crota2;
+
+
+# Read the input table
+my $inFits = Astro::FITS::CFITSIO::open_file( $input, READONLY, $status ); # FITS file handle
+my $inHeader = $inFits->read_header(); # Header for input
+check_fitsio($status);
+$inFits->movnam_hdu(BINARY_TBL, $extname, 0, $status) and check_fitsio($status);
+my $numRows;                    # Number of rows in table
+$inFits->get_num_rows($numRows, $status) and check_fitsio($status);
+
+my ($xCol, $yCol, $magCol);             # Column numbers for x and y
+$inFits->get_colnum(0, 'X_PSF', $xCol, $status) and check_fitsio($status);
+$inFits->get_colnum(0, 'Y_PSF', $yCol, $status) and check_fitsio($status);
+$inFits->get_colnum(0, 'PSF_INST_MAG', $magCol, $status) and check_fitsio($status);
+
+my ($xType, $yType, $magType);  # Column types
+$inFits->get_coltype($xCol, $xType, undef, undef, $status) and check_fitsio($status);
+$inFits->get_coltype($yCol, $yType, undef, undef, $status) and check_fitsio($status);
+$inFits->get_coltype($magCol, $magType, undef, undef, $status) and check_fitsio($status);
+
+my ($x, $y, $mag);              # Sources arrays
+$inFits->read_col($xType, $xCol, 1, 1, $numRows, 0, $x, undef, $status) and check_fitsio($status);
+$inFits->read_col($yType, $yCol, 1, 1, $numRows, 0, $y, undef, $status) and check_fitsio($status);
+$inFits->read_col($magType, $magCol, 1, 1, $numRows, 0, $mag, undef, $status) and check_fitsio($status);
+
+$inFits->close_file( $status );
+
+# Parse the list of columns
+my @colNames;                   # Names of columns
+my @colTypes;                   # Types of columns
+my %colData;                    # Data for each column
+foreach my $colSpec ( @$columns) {
+    push @colNames, $colSpec->{name};
+    push @colTypes, $colSpec->{type};
+    $colData{$colSpec->{name}} = [];
+}
+
+
+# Convert the input data into the output formats
+for (my $i = 0; $i < $numRows; $i++) {
+    my ($ra, $dec) = pixels_to_sky($$x[$i], $$y[$i]); # Sky coordinates
+
+    push @{$colData{'RA_DEG'}}, $ra;
+    push @{$colData{'DEC_DEG'}}, $dec;
+    push @{$colData{'RA_SIG'}}, POSITION_ERROR;
+    push @{$colData{'DEC_SIG'}}, POSITION_ERROR;
+    push @{$colData{'FLUX'}}, $$mag[$i] + ZERO_POINT;
+    push @{$colData{'FLUX_SIG'}}, MAG_ERROR;
+    push @{$colData{'ANG'}}, 0.0;
+    push @{$colData{'ANG_SIG'}}, 0.0;
+    push @{$colData{'LEN'}}, 0.0;
+    push @{$colData{'LEN_SIG'}}, 0.0;
+}
+
+
+# Write the output
+unlink "$output" if -e "$output";
+my $outFits = Astro::FITS::CFITSIO::create_file( $output, $status ); # Output file handle
+check_fitsio( $status );
+
+# Write the table
+$outFits->create_tbl( BINARY_TBL(), $numRows, scalar @colNames, \@colNames, \@colTypes, undef, EXTNAME,
+                      $status );
+check_fitsio( $status );
+
+# Write the header keywords
+foreach my $keyword ( keys %$headers ) {
+    my $value = $inHeader->{$keyword}; # Header keyword value
+    unless (defined $value) {
+        print "Can't find header keyword $keyword\n";
+        next;
+    }
+    $value =~ s/\'//g;
+    my $name = $headers->{$keyword}->{name}; # New name
+    my $type = $headers->{$keyword}->{type}; # Type
+    my $comment = $headers->{$keyword}->{comment}; # Comment
+    $outFits->write_key( $type, $name, $value, $comment, $status );
+    check_fitsio( $status );
+}
+
+# Adjust the time from start to mid-point
+{
+    my $mjd = $inHeader->{'MJD-OBS'} or die "Can't find MJD.\n"; # Modified Julian Date
+    my $exptime = $inHeader->{'EXPTIME'} or die "Can't find EXPTIME.\n"; # Exposure time, seconds
+    $mjd += $exptime / 2.0 / 3600 / 24;     # start --> mid-point
+    $outFits->write_key( TDOUBLE, 'MJD-OBS', $mjd, 'Time of exposure mid-point', $status);
+    check_fitsio( $status );
+}
+
+# Stellar PSF
+my $fwhm = 0.5 * 3600 * ($inHeader->{'FWHM_X'} * $cdelt1 + $inHeader->{'FWHM_Y'} * $cdelt2); # FWHM for star
+$outFits->write_key( TSTRING, 'STARPSF', $fwhm, 'Stellar PSF (arcsec)', $status );
+check_fitsio( $status );
+
+# Observatory code
+$outFits->write_key( TINT, 'OBSCODE', OBSERVATORY_CODE, 'IAU observatory code', $status );
+
+# Limiting magnitude
+$outFits->write_key( TINT, 'LIMITMAG', FAKE_LIMITING_MAG, 'Limiting magnitude (FAKE)', $status );
+
+# Detection efficiency
+$outFits->write_key( TINT, 'DE1', FAKE_DETECTION_EFFICIENCY, 'Detection efficiency (FAKE)', $status );
+$outFits->write_key( TINT, 'DE2', FAKE_DETECTION_EFFICIENCY, 'Detection efficiency (FAKE)', $status );
+$outFits->write_key( TINT, 'DE3', FAKE_DETECTION_EFFICIENCY, 'Detection efficiency (FAKE)', $status );
+$outFits->write_key( TINT, 'DE4', FAKE_DETECTION_EFFICIENCY, 'Detection efficiency (FAKE)', $status );
+$outFits->write_key( TINT, 'DE5', FAKE_DETECTION_EFFICIENCY, 'Detection efficiency (FAKE)', $status );
+$outFits->write_key( TINT, 'DE6', FAKE_DETECTION_EFFICIENCY, 'Detection efficiency (FAKE)', $status );
+$outFits->write_key( TINT, 'DE7', FAKE_DETECTION_EFFICIENCY, 'Detection efficiency (FAKE)', $status );
+$outFits->write_key( TINT, 'DE8', FAKE_DETECTION_EFFICIENCY, 'Detection efficiency (FAKE)', $status );
+$outFits->write_key( TINT, 'DE9', FAKE_DETECTION_EFFICIENCY, 'Detection efficiency (FAKE)', $status );
+$outFits->write_key( TINT, 'DE10', FAKE_DETECTION_EFFICIENCY, 'Detection efficiency (FAKE)', $status );
+
+# Write the data
+for (my $i = 0; $i < scalar @colNames; $i++) {
+    my $colName = $colNames[$i];# Column name
+    $outFits->write_col( TDOUBLE, $i + 1, 1, 1, $numRows, $colData{$colName}, $status );
+    check_fitsio( $status );
+}
+
+$outFits->close_file( $status );
+
+### Pau.
+
+
+
+# From Astro::FITS::CFITSIO demo
+sub check_fitsio
+{
+    my $status = shift;         # Status of FITSIO calls
+
+    if ($status != 0) {
+        my $msg;                # Message to output
+        Astro::FITS::CFITSIO::fits_get_errstatus( $status , $msg );
+        die "CFITSIO error: $msg\n";
+    }
+}
+
+# Convert pixel coordinates to sky coordinates
+sub pixels_to_sky
+{
+    my ($x, $y) = @_;           # Coordinates
+
+    # Pixel coordinate relative to reference
+    $x -= $crpix1;
+    $y -= $crpix2;
+
+    # Coordinates on tangent plane
+    my $xi = $pc11 * ($x) + $pc12 * ($y);
+    my $eta = $pc21 * ($x) + $pc22 * ($y);
+    $xi *= $cdelt1;
+    $eta *= $cdelt2;
+
+    # Coordinates on rotated celestial sphere
+    my $phi = atan2($eta,$xi) + pi/2;
+    my $theta = atan(180 / pi / sqrt($xi**2 + $eta**2));
+
+    # Coordinates on celestial sphere
+    my $ra = $crval1 + atan2(cos($theta) * sin($phi),
+                             sin($theta) * cos($crval2) + cos($theta) * sin($crval2) * cos($phi));
+    my $dec = asin(sin($theta) * sin($crval2) - cos($theta) * cos($crval2) * cos($phi));
+
+    $ra = rad2deg($ra);
+    $dec = rad2deg($dec);
+
+    $ra += 360 if $ra < 0;
+    $dec += 360 if $dec < 0;
+    $ra -= 360 if $ra >= 360;
+    $dec -= 360 if $dec >= 360;
+
+    return ($ra, $dec);
+}
+
+
+__END__
+
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_camera.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_camera.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_camera.pl	(revision 22322)
@@ -0,0 +1,85 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config qw( caturi );
+use Data::Dumper;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($dbname,			# Database name to use
+    $workdir_default,		# Default working directory
+    $verbose,			# Verbose operations?
+    $no_op,			# No operations?
+    $no_update,			# No updating?
+    );
+GetOptions(
+	   'dbname=s' => \$dbname,
+	   'workdir=s' => \$workdir_default,
+	   'verbose' => \$verbose,
+	   'no-op' => \$no_op,
+	   'no-update' => \$no_update,
+) or pod2usage( 2 );
+
+pod2usage(
+	  -msg => "Required options: --dbname",
+	  -exitval => 3,
+	  ) unless defined $dbname;
+
+$workdir_default = `pwd` unless defined $workdir_default;
+
+my $mdcParser = PS::IPP::Metadata::Config->new;	# Metadata config parser
+my $ipprc = PS::IPP::Config->new; # IPP Configuration
+
+# Look for programs we need
+my $missing_tools;
+my $camtool = can_run('camtool') or (warn "Can't find camtool" and $missing_tools = 1);
+my $camera_exp = can_run('camera_exp.pl') or (warn "Can't find camera_exp.pl" and $missing_tools = 1);
+die "Can't find required tools.\n" if $missing_tools;
+
+# Camera exposure processing
+my $list;
+{
+    my $command = "$camtool -pendingexp -dbname $dbname"; # Command to run
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run( command => $command, verbose => 1 );
+    die "Unable to get camera exposure list: $error_code\n" if not $success;
+    $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+	die "Unable to parse output from camtool.\n";
+}
+
+foreach my $item (@$list) {
+    my $cam_id = $item->{cam_id};
+    my $exp_tag = $item->{exp_tag};
+    my $camera = $item->{camera};
+    my $workdir = $item->{workdir};
+    
+    $workdir = $workdir_default unless (defined $workdir or $workdir ne "NULL");
+    my $outroot = caturi( $workdir, $exp_tag, "$exp_tag.cm.$cam_id" );
+    $ipprc->outroot_prepare( $outroot );
+
+    my $command = "$camera_exp --cam_id $cam_id --exp_tag $exp_tag --camera $camera --dbname $dbname --outroot $outroot";
+    $command .= " --verbose" if defined $verbose;
+    $command .= " --no-op" if defined $no_op;
+    $command .= " --no-update" if defined $no_update;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run( command => $command, verbose => 1 );
+    die "Unable to do camera processing on $cam_id: $error_code\n" if not $success;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+
+__END__
+
+
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_chip.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_chip.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_chip.pl	(revision 22322)
@@ -0,0 +1,105 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config qw( caturi );
+use Data::Dumper;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($dbname,			# Database name to use
+    $workdir_default,		# Default working directory
+    $verbose,			# Verbose operations?
+    $no_op,			# No operations?
+    $no_update,			# No updating?
+    );
+GetOptions(
+	   'dbname=s' => \$dbname,
+	   'workdir=s' => \$workdir_default,
+	   'verbose' => \$verbose,
+	   'no-op' => \$no_op,
+	   'no-update' => \$no_update,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Required options: --dbname --workdir",
+	   -exitval => 3,
+	   ) unless
+    defined $dbname;
+
+$workdir_default = `pwd` unless defined $workdir_default;
+
+my $mdcParser = PS::IPP::Metadata::Config->new;	# Metadata config parser
+my $ipprc = PS::IPP::Config->new; # IPP Configuration
+
+# Look for programs we need
+my $missing_tools;
+my $chiptool = can_run('chiptool') or (warn "Can't find chiptool" and $missing_tools = 1);
+my $chip = can_run('chip_imfile.pl') or (warn "Can't find chip_imfile.pl" and $missing_tools = 1);
+die "Can't find required tools.\n" if $missing_tools;
+
+# Imfile processing
+my @whole;			# The whole list for processing
+{
+    my $command = "$chiptool -pendingimfile -dbname $dbname"; # Command to run
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run( command => $command, verbose => 1 );
+    die "Unable to get phase 2 imfile list: $error_code\n" if not $success;
+    @whole = split /\n/, join( '', @$stdout_buf );
+}
+
+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 ) ) ) or
+	    die "Unable to parse output from chiptool.\n";
+	    
+	foreach my $item (@$list) {
+	    my $chip_id = $item->{chip_id};
+	    my $exp_id = $item->{exp_id};
+	    my $exp_tag = $item->{exp_tag};
+	    my $camera = $item->{camera};
+	    my $class_id = $item->{class_id};
+	    my $uri = $item->{uri};
+	    my $reduction = $item->{reduction};
+	    my $workdir = $item->{workdir};
+	    $workdir = $workdir_default unless (defined $workdir or $workdir ne "NULL");
+
+	    my $outroot = caturi( $workdir, $exp_tag, "$exp_tag.ch.$chip_id" );
+	    $ipprc->outroot_prepare( $outroot );
+
+	    my $command = "$chip --chip_id $chip_id --exp_id $exp_id --class_id $class_id --uri $uri --dbname $dbname --camera $camera --outroot $outroot";
+	    $command .= " --reduction $reduction" if defined $reduction;
+	    $command .= " --verbose" if defined $verbose;
+	    $command .= " --no-op" if defined $no_op;
+	    $command .= " --no-update" if defined $no_update;
+	    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+		run( command => $command, verbose => 1 );
+	    die "Unable to do phase 2 processing on $chip_id $class_id: $error_code\n" if not $success;
+	}
+
+	@single = ();
+
+    }
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+
+__END__
+
+
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_detrend.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_detrend.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_detrend.pl	(revision 22322)
@@ -0,0 +1,377 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use Getopt::Long;
+use Pod::Usage qw( pod2usage );
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config qw( caturi );
+use Data::Dumper;
+
+my ($dbname,                    # Database name to use
+    $workdir_global,            # Global working directory
+    $verbose,                   # Produce verbose output?
+    );
+GetOptions(
+           'dbname=s'  => \$dbname,
+           'workdir=s' => \$workdir_global,
+           'verbose'   => \$verbose,
+) or pod2usage( 2 );
+
+pod2usage(
+          -msg => "Required options: --dbname",
+          -exitval => 3,
+          ) unless defined $dbname;
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Metadata config parser
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $detrend_process_imfile = can_run('detrend_process_imfile.pl')  or (warn "Can't find detrend_process_imfile.pl" and $missing_tools = 1);
+my $detrend_process_exp = can_run('detrend_process_exp.pl') or (warn "Can't find detrend_process_exp.pl" and $missing_tools = 1);
+my $detrend_stack = can_run('detrend_stack.pl') or (warn "Can't find detrend_stack.pl" and $missing_tools = 1);
+my $detrend_norm_calc = can_run('detrend_norm_calc.pl') or (warn "Can't find detrend_norm_calc.pl" and $missing_tools = 1);
+my $detrend_norm_apply = can_run('detrend_norm_apply.pl') or (warn "Can't find detrend_norm_apply.pl" and $missing_tools = 1);
+my $detrend_norm_exp = can_run('detrend_norm_exp.pl') or (warn "Can't find detrend_norm_exp.pl" and $missing_tools = 1);
+my $detrend_resid_imfile = can_run('detrend_resid_imfile.pl') or (warn "Can't find detrend_resid_imfile.pl" and $missing_tools = 1);
+my $detrend_resid_exp = can_run('detrend_resid_exp.pl') or (warn "Can't find detrend_resid_exp.pl" and $missing_tools = 1);
+my $detrend_reject_exp = can_run('detrend_reject_exp.pl') or (warn "Can't find detrend_reject_exp.pl" and $missing_tools = 1);
+die "Can't find required tools.\n" if $missing_tools;
+
+# Process raw imfiles
+{
+    my $command = "$dettool -toprocessedimfile -dbname $dbname";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run( command => $command, verbose => 1 );
+    die "Unable to get detrend raw list: $error_code\n" if not $success;
+
+    my @whole = split /\n/, join( '', @$stdout_buf );
+    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 ) ) ) or
+                die "Unable to parse output from dettool.\n";
+
+            foreach my $item (@$list) {
+                my $det_id = $item->{det_id};
+                my $det_type = $item->{det_type};
+                my $exp_tag = $item->{exp_tag};
+                my $exp_id = $item->{exp_id};
+                my $class_id = $item->{class_id};
+                my $uri = $item->{uri};
+                my $camera = $item->{camera};
+                my $workdir = $item->{workdir};
+                my $reduction = $item->{reduction};
+
+                $workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+                die "No working directory specified.\n" unless defined $workdir;
+
+                my $outroot = caturi( $workdir, "$camera.$det_type.$det_id", $exp_tag, "$exp_tag.detproc.$det_id" );
+                $ipprc->outroot_prepare( $outroot );
+
+                my $command = "$detrend_process_imfile --det_id $det_id --exp_tag $exp_tag --exp_id $exp_id --class_id $class_id --det_type $det_type --input_uri $uri --camera $camera --dbname $dbname --outroot $outroot";
+                $command .= " --reduction $reduction" if defined $reduction;
+                $command .= " --verbose" if defined $verbose;
+                my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run( command => $command, verbose => 1 );
+                die "Unable to do raw imfile processing on $exp_tag $class_id: $error_code\n" if not $success;
+            }
+
+            @single = ();
+
+        }
+    }
+}
+
+# Process raw exposures
+{
+    my $command = "$dettool -toprocessedexp -dbname $dbname";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run( command => $command, verbose => 1 );
+    die "Unable to get detrend raw list: $error_code\n" if not $success;
+    my $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+        die "Unable to parse output from dettool.\n";
+
+    foreach my $item (@$list) {
+        my $exp_tag = $item->{exp_tag};
+        my $exp_id = $item->{exp_id};
+        my $camera = $item->{camera};
+        my $det_id = $item->{det_id};
+        my $det_type = $item->{det_type};
+        my $workdir = $item->{workdir};
+
+        $workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+        die "No working directory specified.\n" unless defined $workdir;
+
+        my $outroot = caturi( $workdir, "$camera.$det_type.$det_id", $exp_tag, "$exp_tag.detproc.$det_id" );
+        $ipprc->outroot_prepare( $outroot );
+
+        my $command = "$detrend_process_exp --det_id $det_id --det_type $det_type --exp_tag $exp_tag --exp_id $exp_id --camera $camera --dbname $dbname --outroot $outroot";
+        $command .= " --verbose" if defined $verbose;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run( command => $command, verbose => 1 );
+        die "Unable to do raw exposure processing on $det_id $exp_tag: $error_code\n" if not $success;
+    }
+}
+
+# Stack
+{
+    my $command = "$dettool -tostacked -dbname $dbname";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run( command => $command, verbose => 1 );
+    die "Unable to get stack list: $error_code\n" if not $success;
+    my $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+        die "Unable to parse output from dettool.\n";
+
+    foreach my $item (@$list) {
+        my $det_id = $item->{det_id};
+        my $iteration = $item->{iteration};
+        my $class_id = $item->{class_id};
+        my $det_type = $item->{det_type};
+        my $camera = $item->{camera};
+        my $workdir = $item->{workdir};
+        my $reduction = $item->{reduction};
+
+        $workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+        die "No working directory specified.\n" unless defined $workdir;
+
+        my $outroot = caturi( $workdir, "$camera.$det_type.$det_id", "$camera.$det_type.$det_id.$iteration" );
+        $ipprc->outroot_prepare( $outroot );
+
+
+        my $command = "$detrend_stack --det_id $det_id --iteration $iteration --class_id $class_id --det_type $det_type --camera $camera --dbname $dbname --outroot $outroot";
+        $command .= " --reduction $reduction" if defined $reduction;
+        $command .= " --verbose" if defined $verbose;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run( command => $command, verbose => 1 );
+        die "Unable to stack detrend $det_id $iteration $class_id: $error_code\n" if not $success;
+    }
+}
+
+# Calculate normalisation
+{
+    my $command = "$dettool -tonormalizedstat -dbname $dbname";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run( command => $command, verbose => 1 );
+    die "Unable to get normalise calculation list: $error_code\n" if not $success;
+    my $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+        die "Unable to parse output from dettool.\n";
+
+    foreach my $item (@$list) {
+        my $det_id = $item->{det_id};
+        my $iteration = $item->{iteration};
+        my $det_type = $item->{det_type};
+        my $camera = $item->{camera};
+        my $workdir = $item->{workdir};
+
+        $workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+        die "No working directory specified.\n" unless defined $workdir;
+
+        my $outroot = caturi( $workdir, "$camera.$det_type.$det_id", "$camera.$det_type.normstat.$det_id.$iteration" );
+        $ipprc->outroot_prepare( $outroot );
+
+        my $command = "$detrend_norm_calc --det_id $det_id --iteration $iteration --det_type $det_type --dbname $dbname --outroot $outroot";
+        $command .= " --verbose" if defined $verbose;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run( command => $command, verbose => 1 );
+        die "Unable to calculate normalisation for $det_id $iteration: $error_code\n" if not $success;
+    }
+}
+
+# Apply normalisation
+{
+    my $command = "$dettool -tonormalize -dbname $dbname";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run( command => $command, verbose => 1 );
+    die "Unable to get normalisation list: $error_code\n" if not $success;
+    my $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+        die "Unable to parse output from dettool.\n";
+
+    foreach my $item (@$list) {
+        my $det_id = $item->{det_id};
+        my $iteration = $item->{iteration};
+        my $det_type = $item->{det_type};
+        my $class_id = $item->{class_id};
+        my $value = $item->{norm};
+        my $uri = $item->{uri};
+        my $camera = $item->{camera};
+        my $workdir = $item->{workdir};
+
+        $workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+        die "No working directory specified.\n" unless defined $workdir;
+
+        my $outroot = caturi( $workdir, "$camera.$det_type.$det_id", "$camera.$det_type.norm.$det_id.$iteration" );
+        $ipprc->outroot_prepare( $outroot );
+
+        my $command = "$detrend_norm_apply --det_id $det_id --iteration $iteration --class_id $class_id --value $value --input_uri $uri --camera $camera --det_type $det_type --dbname $dbname --outroot $outroot";
+        $command .= " --verbose" if defined $verbose;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run( command => $command, verbose => 1 );
+        die "Unable to apply normalisation for $det_id $iteration $class_id: $error_code\n" if not $success;
+    }
+}
+
+# Examine normalised exposure
+{
+    my $command = "$dettool -tonormalizedexp -dbname $dbname";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run( command => $command, verbose => 1 );
+    die "Unable to get normalised exposures list: $error_code\n" if not $success;
+    my $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+        die "Unable to parse output from dettool.\n";
+
+    foreach my $item (@$list) {
+        my $det_id = $item->{det_id};
+        my $iteration = $item->{iteration};
+        my $det_type = $item->{det_type};
+        my $class_id = $item->{class_id};
+        my $value = $item->{norm};
+        my $uri = $item->{uri};
+        my $camera = $item->{camera};
+        my $workdir = $item->{workdir};
+
+        $workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+        die "No working directory specified.\n" unless defined $workdir;
+
+        my $outroot = caturi( $workdir, "$camera.$det_type.$det_id", "$camera.$det_type.normexp.$det_id.$iteration" );
+        $ipprc->outroot_prepare( $outroot );
+
+        my $command = "$detrend_norm_exp --det_id $det_id --iteration $iteration --camera $camera --det_type $det_type --dbname $dbname --outroot $outroot";
+        $command .= " --verbose" if defined $verbose;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run( command => $command, verbose => 1 );
+        die "Unable to examine normalised exposure for $det_id $iteration: $error_code\n" if not $success;
+    }
+}
+
+# Get residuals
+{
+    my $command = "$dettool -toresidimfile -dbname $dbname";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run( command => $command, verbose => 1 );
+    die "Unable to get residual processing list: $error_code\n" if not $success;
+
+    my @whole = split /\n/, join( '', @$stdout_buf );
+    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 ) ) ) or
+                die "Unable to parse output from dettool.\n";
+
+            foreach my $item (@$list) {
+                my $exp_tag = $item->{exp_tag};
+                my $exp_id = $item->{exp_id};
+                my $camera = $item->{camera};
+                my $det_id = $item->{det_id};
+                my $iteration = $item->{iteration};
+                my $class_id = $item->{class_id};
+                my $det_type = $item->{det_type};
+                my $detrend = $item->{det_uri};
+                my $uri = $item->{uri};
+                my $mode = $item->{mode};
+                my $workdir = $item->{workdir};
+                my $reduction = $item->{reduction};
+
+                $workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+                die "No working directory specified.\n" unless defined $workdir;
+
+                my $outroot = caturi( $workdir, "$camera.$det_type.$det_id", $exp_tag, "$exp_tag.detresid.$det_id.$iteration" );
+                $ipprc->outroot_prepare( $outroot );
+
+                my $command = "$detrend_resid_imfile --det_id $det_id --iteration $iteration --exp_tag $exp_tag --exp_id $exp_id --class_id $class_id --det_type $det_type --camera $camera --input_uri $uri --mode $mode --dbname $dbname --outroot $outroot";
+                $command .= " --reduction $reduction" if defined $reduction;
+                $command .= " --detrend $detrend" if defined $detrend;
+                $command .= " --verbose" if defined $verbose;
+
+                my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run( command => $command, verbose => 1 );
+                die "Unable to do residual processing on $exp_tag $class_id: $error_code\n" if not $success;
+            }
+
+            @single = ();
+
+        }
+    }
+}
+
+# Reject based on imfiles
+{
+    my $command = "$dettool -toresidexp -dbname $dbname";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run( command => $command, verbose => 1 );
+    die "Unable to get residual imfile list: $error_code\n" if not $success;
+    my $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+        die "Unable to parse output from dettool.\n";
+
+    foreach my $item (@$list) {
+        my $exp_tag = $item->{exp_tag};
+        my $exp_id = $item->{exp_id};
+        my $camera = $item->{camera};
+        my $det_id = $item->{det_id};
+        my $iteration = $item->{iteration};
+        my $det_type = $item->{det_type};
+        my $workdir = $item->{workdir};
+
+        $workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+        die "No working directory specified.\n" unless defined $workdir;
+
+        my $outroot = caturi( $workdir, "$camera.$det_type.$det_id", $exp_tag, "$exp_tag.detresid.$det_id.$iteration" );
+        $ipprc->outroot_prepare( $outroot );
+
+        my $command = "$detrend_resid_exp --det_id $det_id --iteration $iteration --exp_tag $exp_tag --exp_id $exp_id --det_type $det_type --camera $camera --dbname $dbname --outroot $outroot";
+        $command .= " --verbose" if defined $verbose;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run( command => $command, verbose => 1 );
+        die "Unable to do imfile rejection on $exp_tag $det_id $iteration: $error_code\n" if not $success;
+    }
+}
+
+# Reject based on exposures
+{
+    my $command = "$dettool -todetrunsummary -dbname $dbname";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run( command => $command, verbose => 1 );
+    die "Unable to get residual exposure list: $error_code\n" if not $success;
+    my $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+        die "Unable to parse output from dettool.\n";
+
+    foreach my $item (@$list) {
+        my $camera = $item->{camera};
+        my $det_id = $item->{det_id};
+        my $iteration = $item->{iteration};
+        my $det_type = $item->{det_type};
+        my $workdir = $item->{workdir};
+
+        $workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+        die "No working directory specified.\n" unless defined $workdir;
+
+        my $outroot = caturi( $workdir, "$camera.$det_type.$det_id", "$camera.$det_type.$det_id.$iteration.detreject" );
+        $ipprc->outroot_prepare( $outroot );
+
+        my $command = "$detrend_reject_exp --det_id $det_id --iteration $iteration --det_type $det_type --camera $camera --dbname $dbname --outroot $outroot";
+        $command .= " --verbose" if defined $verbose;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run( command => $command, verbose => 1 );
+        die "Unable to do exposure rejection on $det_id $iteration: $error_code\n" if not $success;
+    }
+}
+
+
+__END__
+
+
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_diff.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_diff.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_diff.pl	(revision 22322)
@@ -0,0 +1,96 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Basename;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+my ($dbname,			# Database name to use
+    $verbose,			# Verbose operations?
+    $workdir_global,		# Global working directory
+    $no_op,			# No operations?
+    $no_update,			# No updating?
+    );
+GetOptions(
+	   'dbname=s' => \$dbname,
+	   'verbose' => \$verbose,
+	   'workdir' => \$workdir_global,
+	   'no-op' => \$no_op,
+	   'no-update' => \$no_update,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+pod2usage(
+	  -msg => "Required options: --dbname",
+	  -exitval => 3,
+	  ) unless defined $dbname;
+
+my $mdcParser = PS::IPP::Metadata::Config->new;	# Metadata config parser
+
+# Look for programs we need
+my $missing_tools;
+my $difftool = can_run('difftool') or
+    (warn "Can't find difftool" and $missing_tools = 1);
+my $diff_skycell = can_run('diff_skycell.pl') or
+    (warn "Can't find diff_skycell.pl" and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn ("Can't find required tools");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+
+
+# Image differencing
+{
+    my $list;
+    my $command = "$difftool -todiffskyfile -dbname $dbname"; # Command to run
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run( command => $command, verbose => 1 );
+    die "Unable to get list of diffs: $error_code\n" if not $success;
+    $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+	die "Unable to parse output from difftool.\n";
+
+    foreach my $item (@$list) {
+	my $diff_id = $item->{diff_id};
+	my $skycell_id = $item->{skycell_id};
+	my $tess_id = basename($item->{tess_id});
+	my $workdir = $item->{workdir};
+	
+	$workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+	die "No working directory specified.\n" unless defined $workdir;
+	
+	my $outroot = caturi( $workdir, $tess_id, $skycell_id, "$tess_id.$skycell_id.dif.$diff_id" );
+	$ipprc->outroot_prepare( $outroot );
+
+	my $command = "$diff_skycell --diff_id $diff_id --dbname $dbname --outroot $outroot";
+	$command .= " --verbose" if defined $verbose;
+	$command .= " --no-op" if defined $no_op;
+	$command .= " --no-update" if defined $no_update;
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run( command => $command, verbose => 1 );
+	die "Unable to do difference for $diff_id: $error_code\n" if not $success;
+    }
+}
+
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_inject.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_inject.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_inject.pl	(revision 22322)
@@ -0,0 +1,143 @@
+#!/usr/bin/env perl
+
+# this program injects a list of single-file exposures into the db,
+# taking the filename (without .fits) as the exp_tag.  the user
+# supplies a temporary telescope and camera name.  these are used
+# for informational purposes only until the registration step can
+# determine the true telescope and camera name from the image
+# headers.
+
+# this program should not fail because of the data format or the
+# configuration, except for the very basic database setup.
+
+use warnings;
+use strict;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Spec;
+use PS::IPP::Config;
+
+my $ipprc = PS::IPP::Config->new(); # this is used for PATH, NEB filename conversions
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line arguments
+my ($camera, $telescope, $workdir, $reduction, $dvo_db, $tess_id, $end_stage, $label, $dbname, $no_op, $help);
+GetOptions('camera|i=s'     => \$camera,    # user-supplied camera name
+	   'telescope|t=s'  => \$telescope, # user-supplied telescope name
+	   'workdir|w=s'    => \$workdir,   # working directory for output files
+	   'reduction=s'    => \$reduction, # user-supplied camera name
+	   'dvodb=s'        => \$dvo_db,    # target dvo database 
+	   'tess_id=s'      => \$tess_id,   # tessalation for warping
+	   'end_stage=s'    => \$end_stage, # stop processing at this step
+	   'label=s'        => \$label,     # set chip label
+	   'dbname|d=s'     => \$dbname,    # Database name
+	   'no-op'          => \$no_op,     # pretend but don't actually inject
+	   'help'           => \$help       # give help listing
+) or pod2usage( 2 );
+
+pod2usage( -msg => "inject one or many files into the IPP pipeline database", 
+	   -exitval => 2) if 
+    defined $help;
+
+pod2usage( -msg => "Usage: $0 --telescope (name) --camera (name) [--workdir path] [--reduction class] [--dvodb db] [--tess_id tess] [--end_stage stage] [--label label] [--dbname dbname] (files)", 
+	   -exitval => 2 ) if 
+    scalar @ARGV == 0;
+
+pod2usage( -msg => "Required options: --telescope (name) --camera (name)",
+	   -exitval => 3) unless
+    defined $telescope and
+    defined $camera;
+
+my $pxinject = can_run('pxinject') or die "Can't find pxinject\n";
+
+# if workdir is not defined, assign the current path
+# XXX we need to handle relative paths for workdir (not allowed)
+if (! $workdir) {
+    $workdir = File::Spec->rel2abs( "." );
+}
+
+my $num = 0;
+foreach my $file ( @ARGV ) {
+    # check for file existence
+    if (! -e $file) { die "file $file not found\n"; }
+    my $absfile = File::Spec->rel2abs( $file );
+    inject($absfile, $workdir, $dbname, $telescope, $camera);
+    $num ++;
+}
+
+print "$num files injected.\n";
+
+sub inject
+{
+    my $absfile = shift;	# absolute path for this file
+    my $workdir  = shift;	# absolute path for output directory
+    my $dbname = shift;		# IPP database to use
+    my $telescope = shift;	# user-specified telescope
+    my $camera = shift;	# user-specified camera
+
+    # XXX provide an option for an alternative extension
+    my ( $vol, $path, $name ) = File::Spec->splitpath( $absfile );
+    my ( $exp_name ) = $name =~ /(.*)\.(fits|fit|fts)(|.gz)/;
+    $exp_name =~ s|\s|_|g;
+
+    my $relfile = $ipprc->convert_filename_relative( $absfile );
+
+    # the telescope, instrument, and exp_name used here are temporary : register replaces them with the true values
+    my $command_exp = "$pxinject -newExp";
+    $command_exp .= " -tmp_exp_name $exp_name";
+    $command_exp .= " -tmp_inst $camera";
+    $command_exp .= " -tmp_telescope $telescope";
+    $command_exp .= " -workdir $workdir";
+    $command_exp .= " -reduction $reduction" if defined $reduction;
+    $command_exp .= " -dvodb $dvo_db"       if defined $dvo_db;
+    $command_exp .= " -tess_id $tess_id"     if defined $tess_id;
+    $command_exp .= " -end_stage $end_stage" if defined $end_stage;
+    $command_exp .= " -label $label"         if defined $label;
+    $command_exp .= " -dbname $dbname"       if defined $dbname;
+
+    my $exp_id = 0;
+    unless ($no_op) {
+	my ( $success_exp, $error_code_exp, $full_buf_exp, $stdout_buf_exp, $stderr_buf_exp ) =
+	    run( command => $command_exp, verbose => 1 );
+	die "Unable to inject $exp_name: $error_code_exp\n" if not $success_exp;
+	
+	my @line = split(/\s+/, $$stdout_buf_exp[0]); # The output line, containing the exposure tag
+	$exp_id = $line[2];	# The exposure tag
+    } else {
+	print "skipping command: $command_exp\n";
+    }
+    
+    # the class_id used here is temporary : register replaces it with the true class_id
+    my $command_imfile = "$pxinject -newImfile";
+    $command_imfile .= " -exp_id $exp_id";
+    $command_imfile .= " -tmp_class_id fpa";
+    $command_imfile .= " -uri '$relfile'";
+    $command_imfile .= " -dbname $dbname" if defined ($dbname);
+    
+    unless ($no_op) {
+	my ( $success_imfile, $error_code_imfile, $full_buf_imfile, $stdout_buf_imfile, $stderr_buf_imfile ) = run( command => $command_imfile, verbose => 1 );
+	die "Unable to inject $exp_name imfile: $error_code_imfile\n" if not $success_imfile;
+    } else {
+	print "skipping command: $command_imfile\n";
+    }
+
+    # the class_id used here is temporary : register replaces it with the true class_id
+    my $command_update = "$pxinject -updatenewExp";
+    $command_update .= " -exp_id $exp_id";
+    $command_update .= " -state run";
+    $command_update .= " -dbname $dbname" if defined ($dbname);
+    
+    unless ($no_op) {
+	my ( $success_update, $error_code_update, $full_buf_update, $stdout_buf_update, $stderr_buf_update ) = run( command => $command_update, verbose => 1 );
+	die "Unable to update $exp_name: $error_code_update\n" if not $success_update;
+    } else {
+	print "skipping command: $command_update\n";
+    }
+
+    return 1;
+}
+
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_inject_mosaic.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_inject_mosaic.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_inject_mosaic.pl	(revision 22322)
@@ -0,0 +1,168 @@
+#!/usr/bin/env perl
+
+# this program injects a set of multi-file exposures into the db.  the
+# program takes a list of base exposure names and injects all files
+# associated with the exposure.  It constructs the expected filenames
+# from the exposure tag and rules for the camera.  This program is for
+# the test only since it requires too much information at the inject
+# stage.  use 'ipp_serial_inject.pl' for single-file images and
+# 'ipp_serial_inject_split.pl' for multiple file images in split
+# format
+
+# this program should not fail because of the data format or the
+# configuration, except for the very basic database setup.
+
+use warnings;
+use strict;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Config qw( caturi );
+use Data::Dumper;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($camera,                    # Camera used
+    $telescope,                 # Telescope used
+    $tess_id,                   # Tessellation identifier
+    $dbname,                    # Database name
+    $workdir,                   # Working directory
+    $path,                      # Path to data
+    );
+GetOptions(
+           'camera|c=s'    => \$camera,
+           'telescope|t=s' => \$telescope,
+           'workdir=s'     => \$workdir,
+           'tess_id=s'     => \$tess_id,
+           'path=s'        => \$path,
+           'dbname=s'      => \$dbname,
+) or pod2usage( 2 );
+
+pod2usage(
+          -msg => "Required options: --camera --telescope --workdir --path --dbname",
+          -exitval => 3,
+          ) unless defined $camera
+    and defined $telescope
+    and defined $workdir
+    and defined $path
+    and defined $dbname;
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+# Look for programs we need
+my $missing_tools;
+my $pxinject = can_run('pxinject')  or (warn "Can't find pxinject" and $missing_tools = 1);
+
+if (scalar @ARGV == 0) {
+    die "No exposures provided.\n";
+}
+
+# Inject new data into the database
+my @classes;                    # Names of the classes
+my @files;                      # What to add to the filename for each class
+my $imfiles;
+my $add_dir;                    # Add directory name to get file name?
+if ($camera eq "MEGACAM") {
+    for (my $i = 0; $i < 36; $i++) {
+        push @classes, sprintf("ccd%02d", $i);
+        push @files, sprintf(".ccd%02d", $i);
+    }
+} elsif ($camera eq "MCSHORT") {
+    @classes = ( 'ccd12', 'ccd13', 'ccd14', 'ccd21', 'ccd22', 'ccd23' );
+    @files   = ( '.ccd12', '.ccd13', '.ccd14', '.ccd21', '.ccd22', '.ccd23' );
+} elsif ($camera eq "CTIO_MOSAIC2") {
+    @classes = ();
+    @files = ();
+} elsif ($camera eq "TC3") {
+    @classes = ( 'CCID58-1-06b2', 'CCID45-1-14A', 'CCID45-1-11A', 'CCID45-1-22A',
+                 'CCID45-1-04C', 'CCID45-1-13A', 'CCID45-1-05A', 'CCID45-1-19A' );
+    @files = ( '00', '01', '10', '11', '20', '21', '30', '31' );
+    $add_dir = 1;
+} elsif ($camera eq "SIMMOSAIC") {
+    @classes = ( 'Chip00', 'Chip01', 'Chip10', 'Chip11' );
+    @files   = ( '.Chip00', '.Chip01', '.Chip10', '.Chip11' );
+} elsif ($camera eq "SIMTEST") {
+    @classes = ();
+    @files = ();
+} elsif ($camera eq "GPC1") {
+    for (my $i = 0; $i < 8; $i++) {
+        for (my $j = 0; $j < 8; $j++) {
+            if (($i == 0 or $i == 7) and ($j == 0 or $j == 7)) {
+                # Excluding corner chips
+                next;
+            }
+            push @classes, "XY$i$j";
+            push @files, "$i$j";
+        }
+    }
+    $add_dir = 1;
+} else {
+    die "Unrecognised camera name: $camera.\nDid you mean to use ipp_serial_inject.pl?\n";
+}
+
+foreach my $exp_name ( @ARGV ) {
+    my $command = "$pxinject -newExp -tmp_exp_name $exp_name -tmp_inst $camera -tmp_telescope $telescope -workdir $workdir -tess_id $tess_id"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run( command => $command, verbose => 1 );
+    die "Unable to inject $exp_name: $error_code\n" if not $success;
+
+    my @line = split(/\s+/, $$stdout_buf[0]); # The output line, containing the exposure tag
+    my $exp_id = $line[2];      # The exposure tag
+    for (my $i = 0; $i < scalar @classes; $i++) {
+        my $class_id = $classes[$i];
+        my $file_id = $files[$i];
+        my $filename = $exp_name . $file_id . '.fits';
+        $filename = caturi( $exp_name, $filename ) if defined $add_dir;
+        $filename = caturi( $path, $filename );
+
+        die "Unable to find file $filename" unless -f $ipprc->file_resolve( $filename );
+
+        $filename = $ipprc->convert_filename_relative( $filename );
+        my $command = "$pxinject -newImfile -exp_id $exp_id -tmp_class_id $class_id -uri $filename"; # Command to run
+        $command .= " -dbname $dbname" if defined ($dbname);
+
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run( command => $command, verbose => 1 );
+        die "Unable to inject $exp_name $class_id: $error_code\n" if not $success;
+    }
+
+    if (scalar @classes == 0) {
+        my $filename = $exp_name . '.fits';
+        $filename = caturi( $exp_name, $filename ) if defined $add_dir;
+        $filename = caturi( $path, $filename );
+
+        die "Unable to find file $filename" unless -f $ipprc->file_resolve( $filename );
+
+        $filename = $ipprc->convert_filename_relative( $filename );
+        my $command = "$pxinject -newImfile -exp_id $exp_id -tmp_class_id fpa -uri $filename"; # Command to run
+        $command .= " -dbname $dbname" if defined ($dbname);
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run( command => $command, verbose => 1 );
+        die "Unable to inject $exp_name imfile: $error_code\n" if not $success;
+    }
+
+    # Update the exposure to run
+    {
+        my $command = "$pxinject -updatenewExp -exp_id $exp_id -state run"; # Command to run
+        $command .= " -dbname $dbname" if defined ($dbname);
+
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run( command => $command, verbose => 1 );
+        die "Unable to activate $exp_name: $error_code\n" if not $success;
+    }
+
+}
+
+END {
+    my $status = $?;
+system("sync") == 0
+    or die "failed to execute sync: $!" ;
+$? = $status;
+}
+
+
+__END__
+
+
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_inject_split.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_inject_split.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_inject_split.pl	(revision 22322)
@@ -0,0 +1,138 @@
+#!/usr/bin/env perl
+
+# this program injects a list of multi-file exposures into the db.
+# the program takes a list of directory names (dir), and injects all
+# dir/*.fits.  It takes the directory name as the exp_tag.  the user
+# supplies a temporary telescope and instrument name.  these are used
+# for informational purposes only until the registration step can
+# determine the true telescope and instrument name from the image
+# headers.
+
+# this program should not fail because of the data format or the
+# configuration, except for the very basic database setup.
+
+use warnings;
+use strict;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Spec;
+use PS::IPP::Config;
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line arguments
+my ($camera, $telescope, $workdir, $reduction, $dvo_db, $tess_id, $end_stage, $dbname, $help);
+GetOptions('camera|c=s'    => \$camera,    # Camera used	      
+           'telescope|t=s' => \$telescope, # Telescope used      
+           'workdir|w=s'   => \$workdir,   # working directory for output files
+	   'reduction=s'   => \$reduction, # user-supplied camera name
+	   'dvodb=s'       => \$dvo_db,    # target dvo database 
+	   'tess_id=s'     => \$tess_id,   # tessalation for warping
+	   'end_stage=s'   => \$end_stage, # stop processing at this step
+    	   'dbname|d=s'    => \$dbname, # Database name
+	   'help'          => \$help # give help listing
+) or pod2usage( 2 );
+
+pod2usage( -msg => "inject one or many image exposures (directories) into the IPP pipeline database", 
+	   -exitval => 2) if 
+    defined $help;
+
+pod2usage( -msg => "Usage: $0 --telescope (name) --camera (name) [--workdir path] [--reduction class] [--dvodb db] [--tess_id tess] [--end_stage stage] [--dbname name] (files)", 
+	   -exitval => 2 ) if 
+    scalar @ARGV == 0;
+
+pod2usage(
+	  -msg => "Required options: --camera --telescope",
+	  -exitval => 3) unless 
+    defined $telescope and
+    defined $camera;
+
+# Look for programs we need
+my $pxinject = can_run('pxinject') or die "Can't find pxinject\n";
+
+# if workdir is not defined, assign the current path
+# we need to handle relative paths for workdir (not allowed)
+if (! $workdir) {
+    $workdir = File::Spec->rel2abs( "." );
+}
+
+my $num = 0;
+foreach my $filedir ( @ARGV ) {
+    # check for filedir existence
+    if (! -e $filedir) { die "file dir $filedir not found\n"; }
+    my $absfiledir = File::Spec->rel2abs( $filedir );
+    inject($absfiledir, $workdir, $dbname, $telescope, $camera);
+    $num ++;
+}
+
+sub inject
+{
+    my $absfiledir = shift;	# absolute path for this file
+    my $workdir  = shift;	# absolute path for output directory
+    my $dbname = shift;		# IPP database to use
+    my $telescope = shift;	# user-specified telescope
+    my $camera = shift;	# user-specified camera
+
+    # XXX provide an option for an alternative extension
+    my @files = <$absfiledir/*.fits>;
+    my $Nfiles = scalar @files;
+
+    print "absfiledir: $absfiledir\n";
+    print "Nfiles: $Nfiles\n";
+
+    # the absfiledir is of the form path://PATH/data/foo/expname
+    my ( $vol, $path, $exp_name ) = File::Spec->splitpath( $absfiledir );
+
+    # the telescope, instrument, and exp_name used here are temporary : register replaces them with the true values
+    my $command_exp = "$pxinject -newExp";
+    $command_exp .= " -tmp_exp_name $exp_name";
+    $command_exp .= " -tmp_inst $camera";
+    $command_exp .= " -tmp_telescope $telescope";
+    $command_exp .= " -workdir $workdir";
+    $command_exp .= " -reduction $reduction" if defined $reduction;
+    $command_exp .= " -dvodb $dvo_db"       if defined $dvo_db;
+    $command_exp .= " -tess_id $tess_id"     if defined $tess_id;
+    $command_exp .= " -end_stage $end_stage" if defined $end_stage;
+    $command_exp .= " -dbname $dbname"       if defined $dbname;
+
+    my ( $success_exp, $error_code_exp, $full_buf_exp, $stdout_buf_exp, $stderr_buf_exp ) =
+	run( command => $command_exp, verbose => 1 );
+    die "Unable to inject $exp_name: $error_code_exp\n" if not $success_exp;
+    
+    my @line = split(/\s+/, $$stdout_buf_exp[0]); # The output line, containing the exposure tag
+    my $exp_id = $line[2];	# The exposure tag
+
+    foreach my $absfile (@files) {
+
+	my $relfile = $ipprc->convert_filename_relative( $absfile );
+
+	my ( $tmpvol, $tmppath, $filename ) = File::Spec->splitpath( $absfile );
+	my ( $class_name ) = $filename =~ /(.*)\.fits/;
+
+	# the class_id used here is temporary : register replaces it with the true class_id
+	my $command_imfile = "$pxinject -newImfile";
+	$command_imfile .= " -exp_id $exp_id";
+	$command_imfile .= " -tmp_class_id $class_name";
+	$command_imfile .= " -uri $relfile";
+	$command_imfile .= " -dbname $dbname" if defined ($dbname);
+	
+	my ( $success_imfile, $error_code_imfile, $full_buf_imfile, $stdout_buf_imfile, $stderr_buf_imfile ) = run( command => $command_imfile, verbose => 1 );
+	die "Unable to inject $exp_name imfile: $error_code_imfile\n" if not $success_imfile;
+    }
+
+    # the class_id used here is temporary : register replaces it with the true class_id
+    my $command_update = "$pxinject -updatenewExp";
+    $command_update .= " -exp_id $exp_id";
+    $command_update .= " -state run";
+    $command_update .= " -dbname $dbname" if defined ($dbname);
+    
+    my ( $success_update, $error_code_update, $full_buf_update, $stdout_buf_update, $stderr_buf_update ) = run( command => $command_update, verbose => 1 );
+    die "Unable to update $exp_name: $error_code_update\n" if not $success_update;
+
+    return 1;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_mops.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_mops.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_mops.pl	(revision 22322)
@@ -0,0 +1,127 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use DBI;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use Data::Dumper;
+use File::Temp qw( tempfile );
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config qw( caturi );
+
+# Look for programs we need
+my $missing_tools;
+my $mops = can_run('ipp_mops_translate.pl') or (warn "Can't find ipp_mops_translate.pl" and $missing_tools = 1);
+my $dsreg = can_run('dsreg') or (warn "Can't find dsreg" and $missing_tools = 1);
+die "Can't find required tools.\n" if $missing_tools;
+
+my $ipprc = PS::IPP::Config->new; # IPP Configuration
+
+my ( $dbhost,                   # Database host
+     $dbname,                   # Database name
+     $dbuser,                   # Database user
+     $dbpass,                   # Database p/w
+     $camera,                   # Camera used
+     $outroot,                  # Output directory
+     $fileset,                  # File set
+     $label,                    # Data label to search for
+     $verbose,                  # Verbose output?
+     $no_update,                # Don't update state?
+     $no_op,                    # Don't do any operations?
+     $save_temps                # Save temporary files?
+     );
+
+GetOptions(
+           'dbhost=s'   => \$dbhost,
+           'dbname=s'   => \$dbname,
+           'dbuser=s'   => \$dbuser,
+           'dbpass=s'   => \$dbpass,
+           'camera=s'   => \$camera,
+           'outroot=s'  => \$outroot,
+           'fileset=s'  => \$fileset,
+           'label=s'    => \$label,
+           'verbose'    => \$verbose,
+           'no-update'  => \$no_update, # Don't update the database?
+           'no-op'      => \$no_op, # Don't do any operations?
+           'save-temps' => \$save_temps, # Save temporary files?
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --dbhost --dbname --dbuser --dbpass --camera --outroot --fileset",
+           -exitval => 3)
+    unless defined $dbhost
+    and defined $dbname
+    and defined $dbuser
+    and defined $dbpass
+    and defined $camera
+    and defined $outroot
+    and defined $fileset;
+
+$ipprc->define_camera($camera);
+
+my $dbsrc = 'DBI:mysql:database=' . $dbname . ';host=' . $dbhost .
+    ';mysql_socket=/var/run/mysqld/mysqld.sock';
+my $db = DBI->connect($dbsrc, $dbuser, $dbpass, { RaiseError => 1, AutoCommit => 1 } ) or
+    die "Unable to connect to database: $DBI::errstr";
+
+my $sql = "SELECT exp_id, diff_id, skycell_id, warp_id, diffSkyfile.path_base AS diff_path_base, warpSkyfile.path_base AS warp_path_base FROM diffSkyfile JOIN diffRun using(diff_id) JOIN diffInputSkyfile USING(diff_id,tess_id,skycell_id) JOIN warpRun USING(warp_id,tess_id) JOIN warpSkyfile USING(warp_id,skycell_id,tess_id) JOIN fakeRun using(fake_id) JOIN camRun USING(cam_id) JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id) WHERE diffSkyfile.fault = 0 AND rawExp.camera = \'$camera\'";
+$sql .= " AND diffRun.label = \'$label\'" if defined $label;
+$sql .= ';';
+
+my $rows = $db->selectall_arrayref( $sql, { Slice => {} } ) or die "Unable to execute SQL: $DBI::errstr";
+$db->disconnect;
+
+print "Selected " . scalar @$rows . " rows.\n";
+
+$ipprc->outroot_prepare( $outroot );
+my $outrootResolved = $ipprc->file_resolve( $outroot );
+my ($dsFile, $dsName) = tempfile( "$outrootResolved.dslist.XXXX", UNLINK => !$save_temps);
+
+foreach my $row ( @$rows ) {
+    my $exp_id = $row->{exp_id};
+    my $diff_id = $row->{diff_id};
+    my $skycell_id = $row->{skycell_id};
+    my $warp_id = $row->{warp_id};
+    my $diff_base = $row->{diff_path_base};
+    my $warp_base = $row->{warp_path_base};
+
+    my $input = $ipprc->filename("PSPHOT.OUT.CMF.MEF", $diff_base);
+    die "Can't find $input\n" unless $ipprc->file_exists($input);
+    $input = $ipprc->file_resolve($input);
+
+    my $skycell = $ipprc->filename("SKYCELL.TEMPLATE", $warp_base);
+    die "Can't find $skycell\n" unless $ipprc->file_exists($skycell);
+    $skycell = $ipprc->file_resolve($skycell);
+
+    my $output = caturi( $outroot, "mops.$exp_id.$skycell_id.$diff_id.fits" );
+    $output = $ipprc->file_resolve($output);
+
+    unless ($no_op) {
+        $ipprc->file_prepare($output);
+        my $command = "$mops --input $input --extname SkyChip.psf --skycell $skycell --output $output";
+        my $success = run( command => $command, verbose => $verbose );
+        die "Couldn't translate $input\n" unless $success;
+    }
+
+    # format: filename|filesize|md5sum|filetype|
+    # note: since we omit filesize and md5sum, dsreg will calculate them
+    print $dsFile "${output}|||ipp-mops|\n";
+}
+close $dsFile;
+
+# Register new files with the data store
+unless ($no_update) {
+    my $command = "$dsreg --add $fileset --product mops_transient_detections --type MOPS_TRANSIENT_DETECTIONS --list $dsName --copy --abspath --dbname DataStore";
+    my $success = run( command => $command, verbose => $verbose );
+    die "Couldn't register files with data store.\n" unless $success;
+}
+
+
+__END__
+
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_register.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_register.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_register.pl	(revision 22322)
@@ -0,0 +1,118 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use Data::Dumper;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($dbname,			# Database name to use
+    );
+GetOptions(
+	   'dbname|d=s'  => \$dbname,
+) or pod2usage( 2 );
+
+pod2usage(
+	  -msg => "Required options: --dbname",
+	  -exitval => 3,
+	  ) unless defined $dbname;
+
+my $mdcParser = PS::IPP::Metadata::Config->new;	# Metadata config parser
+
+# Look for programs we need
+my $missing_tools;
+my $regtool = can_run('regtool') or (warn "Can't find regtool" and $missing_tools = 1);
+my $register_imfile = can_run('register_imfile.pl') or (warn "Can't find register_imfile.pl" and $missing_tools = 1);
+my $register_exp = can_run('register_exp.pl') or (warn "Can't find register_exp.pl" and $missing_tools = 1);
+die "Can't find required tools.\n" if $missing_tools;
+
+# Phase 0 imfile processing
+{
+    my $command = "$regtool -pendingimfile -dbname $dbname"; # Command to run
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run( command => $command, verbose => 1 );
+    die "Unable to get phase 0 imfile list: $error_code\n" if not $success;
+
+    my @whole = split /\n/, join( '', @$stdout_buf );
+    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 ) ) ) or
+		die "Unable to parse output from regtool.\n";
+
+	    foreach my $item (@$list) {
+		my $exp_id = $item->{exp_id};
+		my $exp_name = $item->{tmp_exp_name};
+		my $class_id = $item->{tmp_class_id};
+		my $uri = $item->{uri};
+		
+		my $command = "$register_imfile --exp_id $exp_id --tmp_class_id $class_id --tmp_exp_name $exp_name --uri $uri --dbname $dbname";
+		my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+		    run( command => $command, verbose => 1 );
+		die "Unable to do phase 0 imfile processing on $exp_name $class_id: $error_code\n" if not $success;
+	    }
+
+
+	    @single = ();
+
+	}	
+    }
+}
+
+# Phase 0 exposure processing
+{
+    my $command = "$regtool -pendingexp -dbname $dbname"; # Command to run
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run( command => $command, verbose => 1 );
+    die "Unable to get phase 0 exposure list: $error_code\n" if not $success;
+
+    my @whole = split /\n/, join( '', @$stdout_buf );
+    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 ) ) ) or
+		die "Unable to parse output from regtool.\n";
+
+	    foreach my $item (@$list) {
+		my $exp_tag = $item->{tmp_exp_name} . '.' . $item->{exp_id};
+		my $exp_id = $item->{exp_id};
+		
+		my $command = "$register_exp --exp_tag $exp_tag --exp_id $exp_id --dbname $dbname";
+		my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+		    run( command => $command, verbose => 1 );
+		die "Unable to do phase 0 exposure processing on $exp_tag: $error_code\n" if not $success;
+	    }
+
+	    @single = ();
+
+	}	
+    }
+}
+
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+
+__END__
+
+
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_stack.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_stack.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_stack.pl	(revision 22322)
@@ -0,0 +1,99 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use File::Basename;
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+my ($dbname,			# Database name to use
+    $verbose,			# Verbose operations?
+    $workdir_global,		# Global working directory
+    $no_op,			# No operations?
+    $no_update,			# No updating?
+    $save_temps,		# Save temporary files?
+    );
+GetOptions(
+	   'dbname=s' => \$dbname,
+	   'verbose' => \$verbose,
+	   'workdir' => \$workdir_global,
+	   'no-op' => \$no_op,
+	   'no-update' => \$no_update,
+	   'save-temps' => \$save_temps,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+pod2usage(
+	  -msg => "Required options: --dbname",
+	  -exitval => 3,
+	  ) unless defined $dbname;
+
+my $mdcParser = PS::IPP::Metadata::Config->new;	# Metadata config parser
+
+# Look for programs we need
+my $missing_tools;
+my $stacktool = can_run('stacktool') or
+    (warn "Can't find difftool" and $missing_tools = 1);
+my $stack_skycell = can_run('stack_skycell.pl') or
+    (warn "Can't find stack_skycell.pl" and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn ("Can't find required tools");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+
+
+# Image stacking
+{
+    my $list;
+    my $command = "$stacktool -tosum -dbname $dbname"; # Command to run
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run( command => $command, verbose => 1 );
+    die "Unable to get list of stacks: $error_code\n" if not $success;
+    $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+	die "Unable to parse output from stacktool.\n";
+
+    foreach my $item (@$list) {
+	my $stack_id = $item->{stack_id};
+	my $workdir = $item->{workdir};
+	my $tess_id = basename($item->{tess_id});
+	my $skycell_id = $item->{skycell_id};
+	
+	$workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+	die "No working directory specified.\n" unless defined $workdir;
+	
+	my $outroot = caturi( $workdir, $tess_id, $skycell_id, "$tess_id.$skycell_id.stk.$stack_id" );
+	$ipprc->outroot_prepare( $outroot );
+
+	my $command = "$stack_skycell --stack_id $stack_id --dbname $dbname --outroot $outroot";
+	$command .= " --verbose" if defined $verbose;
+	$command .= " --no-op" if defined $no_op;
+	$command .= " --no-update" if defined $no_update;
+	$command .= " --save-temps" if defined $save_temps;
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run( command => $command, verbose => 1 );
+###	die "Unable to do stack for $stack_id: $error_code\n" if not $success;
+    }
+}
+
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_warp.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_warp.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_serial_warp.pl	(revision 22322)
@@ -0,0 +1,129 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use File::Basename;
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+my ($dbname,			# Database name to use
+    $verbose,			# Verbose operations?
+    $workdir_global,		# Global working directory
+    $no_op,			# No operations?
+    $no_update,			# No updating?
+    );
+GetOptions(
+	   'dbname=s' => \$dbname,
+	   'verbose' => \$verbose,
+	   'workdir' => \$workdir_global,
+	   'no-op' => \$no_op,
+	   'no-update' => \$no_update,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+pod2usage(
+	  -msg => "Required options: --dbname",
+	  -exitval => 3,
+	  ) unless defined $dbname;
+
+my $mdcParser = PS::IPP::Metadata::Config->new;	# Metadata config parser
+
+# Look for programs we need
+my $missing_tools;
+my $warptool = can_run('warptool') or
+    (warn "Can't find warptool" and $missing_tools = 1);
+my $warp_skycell = can_run('warp_skycell.pl') or
+    (warn "Can't find warp_skycell.pl" and $missing_tools = 1);
+my $warp_overlap = can_run('warp_overlap.pl') or
+    (warn "Can't find warp_overlap.pl" and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn ("Can't find required tools");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+
+
+# Calculate overlaps
+{
+    my $list;
+    my $command = "$warptool -tooverlap -dbname $dbname"; # Command to run
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run( command => $command, verbose => 1 );
+    die "Unable to get warps for which to calculate overlaps: $error_code\n" if not $success;
+    $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+	die "Unable to parse output from warptool.\n";
+
+    foreach my $item (@$list) {
+	my $warp_id = $item->{warp_id};
+	my $cam_id = $item->{cam_id};
+	my $workdir = $item->{workdir};
+	my $camera = $item->{camera};
+	my $tess_id = $item->{tess_id};
+	
+	my $command = "$warp_overlap --warp_id $warp_id --camera $camera --tess_id $tess_id --dbname $dbname";
+	$command .= " --verbose" if defined $verbose;
+	$command .= " --no-op" if defined $no_op;
+	$command .= " --no-update" if defined $no_update;
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run( command => $command, verbose => 1 );
+	die "Unable to get warp overlaps on $warp_id: $error_code\n" if not $success;
+    }
+}
+
+
+# Warping proper
+{
+    my $list;
+    my $command = "$warptool -towarped -dbname $dbname"; # Command to run
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run( command => $command, verbose => 1 );
+    die "Unable to get warps for warping: $error_code\n" if not $success;
+    $list = parse_md_list( $mdcParser->parse( join( '', @$stdout_buf ) ) ) or
+	die "Unable to parse output from warptool.\n";
+
+    foreach my $item (@$list) {
+	my $warp_id = $item->{warp_id};
+	my $skycell_id = $item->{skycell_id};
+	my $tess_id = basename($item->{tess_id});
+	my $cam_id = $item->{cam_id};
+	my $workdir = $item->{workdir};
+	my $camera = $item->{camera};
+	
+	$workdir = $workdir_global unless defined $workdir and $workdir ne "NULL";
+	die "No working directory specified.\n" unless defined $workdir;
+	
+	my $outroot = caturi( $workdir, $tess_id, $skycell_id, "$tess_id.$skycell_id.wrp.$warp_id" );
+	$ipprc->outroot_prepare( $outroot );
+
+	my $command = "$warp_skycell --warp_id $warp_id --skycell_id $skycell_id --tess_id $tess_id --camera $camera --dbname $dbname --outroot $outroot";
+	$command .= " --verbose" if defined $verbose;
+	$command .= " --no-op" if defined $no_op;
+	$command .= " --no-update" if defined $no_update;
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run( command => $command, verbose => 1 );
+	die "Unable to do warp processing on $warp_id,$skycell_id: $error_code\n" if not $success;
+    }
+}
+
+
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_simulation_data.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_simulation_data.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipp_simulation_data.pl	(revision 22322)
@@ -0,0 +1,215 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use Math::Trig;
+use File::Spec;
+use PS::IPP::Config 1.01 qw( :standard );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+my ($name,                      # Base name for output images
+    $camera,                    # Name of camera to use
+    $telescope,                 # Telescope name
+    $tess_id,                   # Tessellation identifier
+    $dbname,                    # Database name
+    $path,                      # Path to data
+    $workdir,                   # Working directory for data
+    $no_cal,                    # Don't produce calibration files
+    $no_update                  # Don't update the database
+    );
+
+GetOptions(
+           'name=s'        => \$name,
+           'camera=s'      => \$camera,
+           'telescope=s'   => \$telescope,
+           'tess_id=s'     => \$tess_id,
+           'dbname=s'      => \$dbname,
+           'path=s'        => \$path,
+           'workdir=s'     => \$workdir,
+           'no-cal'        => \$no_cal,
+           'no-update'     => \$no_update,
+           ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+pod2usage(
+          -msg => "Required options: --name --path --camera --telescope --dbname",
+            -exitval => 3,
+          ) unless
+    defined $name and
+    defined $path and
+    defined $camera and
+    defined $telescope and
+    defined $dbname;
+
+$workdir = $path if not defined $workdir;
+
+# Look for programs we need
+my $missing_tools;
+my $ppSim = can_run('ppSim')
+    or (warn "Can't find ppSim" and $missing_tools = 1);
+my $inject = can_run('ipp_serial_inject_mosaic.pl')
+    or (warn "Can't find ipp_serial_inject_mosaic.pl" and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn ("Can't find required tools");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Number of bias images
+use constant BIAS => 20;
+# Dark exposure times
+use constant DARK => [ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+                       300, 300, 300, 300, 300, 300, 300, 300, 300, 300 ];
+# Flat-field image characteristics
+use constant FLAT => [
+                      {
+                          filter => 'r',
+                          exptime => [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.5, 1, 2, 5, 10, 20, 20, 20, 20, 20 ],
+                      },
+                      {
+                          filter => 'i',
+                          exptime => [ 20, 20, 20, 20, 20 ],
+                      }
+                      ];
+# Object image characteristics
+use constant OBJECT => [
+                        {
+                            filter => 'r',
+                            exptime => [ 5, 10, 10, 10, 10, 10, 240 ],
+                            seeing => [ 0.8, 0.4, 0.6, 0.8, 1.2, 1.5, 0.7 ],
+                            ra => 150.119167,
+                            dec => 2.205833,
+                            pa => 0,
+                            zp => 25.15,
+                            sky => 20.86,
+                            dither => 40,
+                        },
+                        {
+                            filter => 'i',
+                            exptime => [ 5, 30, 30, 30, 30, 30, 240 ],
+                            seeing => [ 0.8, 0.4, 0.6, 0.8, 1.2, 1.5, 0.7 ],
+                            ra => 150.119167,
+                            dec => 2.205833,
+                            pa => 0,
+                            zp => 25.00,
+                            sky => 20.15,
+                            dither => 40,
+                        },
+                        ];
+use constant SCALE => 0.2;      # Plate scale
+
+#############################################################################################################
+### Now do the work
+#############################################################################################################
+
+my $counter = 0;
+my $tessellation = (defined $tess_id) ? "--tess_id $tess_id" : ""; # Additional switch for tessellation
+
+# Generate bias images
+for (my $i = 0; $i < BIAS; $i++) {
+    my $basename;               # Output base filename
+    ( $basename, $counter ) = filename( $name, $counter );
+    my $filename = caturi( $path, $basename );
+    unless ($no_cal) {
+        run( command => "$ppSim -camera $camera -type BIAS $filename",
+             verbose => 1 ) or die "Unable to run ppSim";
+        unless ($no_update) {
+            run( command => "$inject --camera $camera --telescope $telescope --path $path " .
+                 "$tessellation --workdir $workdir --dbname $dbname $basename",
+                 verbose => 1 ) or die "Unable to inject file.";
+        }
+    }
+}
+
+# Generate dark images
+foreach my $exptime ( @{DARK()} ) {
+    my $basename;               # Output base filename
+    ( $basename, $counter ) = filename( $name, $counter );
+    my $filename = caturi( $path, $basename );
+    unless ($no_cal) {
+        run ( command => "$ppSim -camera $camera -type DARK -exptime $exptime $filename",
+              verbose => 1 ) or die "Unable to run ppSim";
+        unless ($no_update) {
+            run( command => "$inject --camera $camera --telescope $telescope --path $path " .
+                 "$tessellation --workdir $workdir --dbname $dbname $basename",
+                 verbose => 1 ) or die "Unable to inject file.";
+        }
+    }
+}
+
+# Generate flat images
+foreach my $set ( @{FLAT()} ) {
+    my $filter = $set->{filter}; # Name of filter
+    foreach my $exptime ( @{$set->{exptime}} ) {
+        my $basename;           # Output base filename
+        ( $basename, $counter ) = filename( $name, $counter );
+        my $filename = caturi( $path, $basename );
+        unless ($no_cal) {
+            run( command => "$ppSim -camera $camera -type FLAT -filter $filter -exptime $exptime $filename",
+                 verbose => 1 ) or die "Unable to run ppSim";
+            unless ($no_update) {
+                run( command => "$inject --camera $camera --telescope $telescope --path $path " .
+                     "$tessellation --workdir $workdir --dbname $dbname $basename",
+                     verbose => 1 ) or die "Unable to inject file.";
+            }
+        }
+    }
+}
+
+# Generate object images
+foreach my $set ( @{OBJECT()} ) {
+    my $filter = $set->{filter}; # Name of filter
+    my $ra0 = $set->{ra};       # Base Right Ascension (deg)
+    my $dec0 = $set->{dec};     # Base Declination (deg)
+    my $pa = $set->{pa};        # Position angle (deg)
+    my $zp = $set->{zp};        # Zero point
+    my $scale = SCALE();        # Plate scale (arcsec/pix)
+    my $sky = 10**( -0.4 * ( $set->{sky} - $zp ) ) * $scale**2; # Sky background (counts/s)
+    my $dither = $set->{dither} / 3600; # Dither size (deg)
+
+    for (my $i = 0; $i < scalar @{$set->{exptime}}; $i++) {
+        my $exptime = ${$set->{exptime}}[$i]; # Exposure time
+        my $seeing = ${$set->{seeing}}[$i]; # Seeing (pix)
+        my $ra = $ra0 + (2*rand() - 1) * $dither * cos(deg2rad($dec0)); # RA with dither
+        my $dec = $dec0 + (2*rand() - 1) * $dither; # Dec with dither
+
+        my $basename;           # Output base filename
+        ( $basename, $counter ) = filename( $name, $counter );
+        my $filename = caturi( $path, $basename );
+        run( command => "$ppSim -camera $camera -type OBJECT -filter $filter -exptime $exptime " .
+             "-skyrate $sky -ra $ra -dec $dec -pa $pa -scale $scale -zp $zp -seeing $seeing $filename",
+             verbose => 1 ) or die "Unable to run ppSim";
+        unless ($no_update) {
+            run( command => "$inject --camera $camera --telescope $telescope --path $path " .
+                 "$tessellation --workdir $workdir --dbname $dbname $basename",
+                 verbose => 1 ) or die "Unable to inject file.";
+        }
+    }
+}
+
+
+### Pau.
+
+
+# Generate a filename from the base name and counter
+sub filename
+{
+    my $base = shift;           # Base name
+    my $num = shift;            # Number
+    my $workdir = shift;        # Working directory
+    my $name = sprintf("$base%04d", $num);
+    $num++;
+    return ( $name, $num );
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipprc.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipprc.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/ipprc.txt	(revision 22322)
@@ -0,0 +1,16 @@
+
+these scripts use the following library functions to work with the IPP Config system:
+
+$ipprc->convert_filename_absolute( $tess_dir );
+$ipprc->convert_filename_relative( $absfile );
+$ipprc->define_camera($camera);
+$ipprc->extname_rule("CMF.HEAD", $class_id); # MEF psastro output
+$ipprc->file_create_append( $logName );
+$ipprc->file_exists( $skyfile );
+$ipprc->file_prepare( "$exp_tag/$exp_tag.detproc.$det_id", $workdir, $input_uri );
+$ipprc->file_resolve ($path_base);
+$ipprc->filename( "PPIMAGE.BIN1", $file->{path_base}, $file->{class_id} ) . "\n");
+$ipprc->outroot_prepare($outroot);
+$ipprc->reduction($reduction, 'JPEG_BIN1_IMAGE_' . uc($det_type)); # Recipe to use
+$ipprc->rejection( $name, $det_type, $filter );
+$ipprc->tessellation_catdir( $tess_id ); # Tessellation catdir for DVO
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/isp_trans.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/isp_trans.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/isp_trans.pl	(revision 22322)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+# basic ISP transmission analysis:
+
+if (@ARGV != 1) { die "USAGE: isp_trans.pl (input.fits)\n"; }
+$input = $ARGV[0];
+
+# for input file /path/foo.fits, use /path/foo for output
+
+@words = split ('\.', $input);
+if (@words > 1) { pop @words; }
+$output = join (".", @words);
+
+# use constant RECIPE => 'PPIMAGE_OBDSFRA'; # Recipe to use
+$RECIPE_PPIMAGE  = 'PPIMAGE_OA'; # Recipe to use (switch to OBDSFRA when detrend images are ready)
+$RECIPE_PSPHOT   = 'PSPHOT.SUMMIT'; 
+$CALDIR  = '/data/alala.0/ipp/ippRefs/catdir.synth.bright'; # source of photometric calibration data
+$IMTABLE = 'images.dat'; # source of photometric calibration data
+
+vsystem ("ppImage -file $input $output -recipe PPIMAGE $RECIPE_PPIMAGE -recipe PSPHOT $RECIPE_PSPHOT");
+if ($status) { die "failure running ppImage\n"; }
+
+vsystem ("addstar -incal -image -D CAMERA isp -D IMAGE_TABLE $IMTABLE -D CATDIR $CALDIR $output.smf");
+if ($status) { die "failure getting calibration from addstar\n"; }
+
+sub vsystem {
+    print STDERR "@_\n";
+    my $status = system ("@_");
+    $status;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/magic_mask.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/magic_mask.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/magic_mask.pl	(revision 22322)
@@ -0,0 +1,171 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line arguments
+my ($magic_id, $camera, $dbname, $outroot, $save_temps, $verbose, $no_update, $no_op, $redirect);
+GetOptions(
+           'magic_id=s'      => \$magic_id,   # Magic identifier
+           'camera=s'        => \$camera,     # Camera name
+           'dbname=s'        => \$dbname,     # Database name
+           'outroot=s'       => \$outroot,    # Output root name
+           'save-temps'      => \$save_temps, # Save temporary files?
+           'verbose'         => \$verbose,    # Print stuff?
+           'no-update'       => \$no_update,  # Don't update the database?
+           'no-op'           => \$no_op,      # Don't do any operations?
+           'redirect-output' => \$redirect,   # Redirect output?
+           ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --magic_id --camera --outroot",
+           -exitval => 3) unless
+    defined $magic_id and
+    defined $camera and
+    defined $outroot;
+
+$ipprc->define_camera($camera);
+
+my $logDest = $ipprc->filename("LOG.EXP", $outroot, $magic_id) or
+    &my_die("Missing entry from camera config", $magic_id, $PS_EXIT_CONFIG_ERROR);
+$ipprc->redirect_output($logDest) if $redirect;
+
+# Look for programs we need
+my $missing_tools;
+my $magictool = can_run('magictool') or (warn "Can't find magictool" and $missing_tools = 1);
+my $streaks = can_run('RemoveStreaks') or (warn "Can't find RemoveStreaks" 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
+
+### Get a list of inputs
+my $inputs;                     # List of inputs
+{
+    my $command = "$magictool -inputs -magic_id $magic_id -node root"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform magictool -inputs: $error_code", $magic_id, $error_code);
+    }
+
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $magic_id, $PS_EXIT_PROG_ERROR);
+
+    $inputs = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $magic_id, $PS_EXIT_PROG_ERROR);
+}
+
+### Do the heavy lifting
+{
+    my $command = "$streaks --streaks"; # Command to execute
+    my @basenames;              # List of base names
+
+    # Concatenate the names
+    foreach my $node (@$inputs) {
+        push @basenames, $ipprc->file_resolve( $node->{path_base} );
+    }
+    $command .= ' ' . join(' ', @basenames);
+
+    unless ($no_op) {
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform RemoveStreaks: $error_code", $magic_id, $error_code);
+        }
+
+        foreach my $basename (@basenames) {
+            file_check( $basename . '.mask' );
+        }
+    } else {
+        print "Skipping command: $command\n";
+    }
+}
+
+
+
+### Input mask into database
+{
+    my $command = "$magictool -addmask";
+    $command   .= " -magic_id $magic_id";
+    $command   .= " -uri $outroot";
+    $command   .= " -dbname $dbname" if defined $dbname;
+
+    # Add the processed file to the database
+    unless ($no_update) {
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform magictool -addmask: $error_code", $magic_id, $error_code);
+        }
+    } else {
+        print "Skipping command: $command\n";
+    }
+}
+
+
+
+### Pau.
+
+sub file_check
+{
+    my $file = shift;           # Name of file
+    &my_die("Unable to find output file: $file", $magic_id, $PS_EXIT_SYS_ERROR) unless
+        $ipprc->file_exists($file);
+}
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $magic_id = shift;       # Magic identifier
+    my $exit_code = shift;      # Exit code to add
+
+    carp($msg);
+    if (defined $magic_id and not $no_update) {
+        my $command = "$magictool -addmask";
+        $command .= " -magic_id $magic_id";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/magic_process.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/magic_process.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/magic_process.pl	(revision 22322)
@@ -0,0 +1,190 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line arguments
+my ($magic_id, $node, $camera, $dbname, $outroot, $save_temps, $verbose, $no_update, $no_op, $redirect);
+GetOptions(
+           'magic_id=s'      => \$magic_id,   # Magic identifier
+           'node=s'          => \$node,       # Node name
+           'camera=s'        => \$camera,     # Camera name
+           'dbname=s'        => \$dbname,     # Database name
+           'outroot=s'       => \$outroot,    # Output root name
+           'save-temps'      => \$save_temps, # Save temporary files?
+           'verbose'         => \$verbose,    # Print stuff?
+           'no-update'       => \$no_update,  # Don't update the database?
+           'no-op'           => \$no_op,      # Don't do any operations?
+           'redirect-output' => \$redirect,   # Redirect output?
+           ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --magic_id --camera --node --outroot",
+           -exitval => 3) unless
+    defined $magic_id and
+    defined $node and
+    defined $camera and
+    defined $outroot;
+
+$ipprc->define_camera($camera);
+
+my $logDest = $ipprc->filename("LOG.EXP", $outroot, $magic_id) or
+    &my_die("Missing entry from camera config", $magic_id, $PS_EXIT_CONFIG_ERROR);
+$ipprc->redirect_output($logDest) if $redirect;
+
+# Look for programs we need
+my $missing_tools;
+my $magictool = can_run('magictool') or (warn "Can't find magictool" and $missing_tools = 1);
+my $streaks = can_run('RemoveStreaks') or (warn "Can't find RemoveStreaks" 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
+
+### Get a list of inputs
+my $inputs;                     # List of inputs
+{
+    my $command = "$magictool -inputs -magic_id $magic_id -node $node"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform magictool -inputs: $error_code", $magic_id, $node, $error_code);
+    }
+
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $magic_id, $node, $PS_EXIT_PROG_ERROR);
+
+    $inputs = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $magic_id, $node, $PS_EXIT_PROG_ERROR);
+}
+
+
+### Do the heavy lifting
+my @basenames;                  # Base names of inputs
+{
+    my $command;                # Command to execute
+    if (scalar @$inputs == 1 and $node ne "root") {
+        # Leaf node: 'detect' stage
+        $command = "$streaks --detect";
+
+        my $node = $$inputs[0];     # Input node
+        push @basenames, $ipprc->file_resolve( $node->{path_base} );
+    } else {
+        # Branch node: 'merge' stage
+        $command = "$streaks --merge";
+
+        # Concatenate the names
+        foreach my $node (@$inputs) {
+            push @basenames, $ipprc->file_resolve( $node->{path_base} );
+        }
+    }
+
+    $command .= ' ' . join(' ', @basenames);
+
+    unless ($no_op) {
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform RemoveStreaks: $error_code", $magic_id, $node, $error_code);
+        }
+
+        foreach my $basename (@basenames) {
+            file_check( $basename . '.clusters' );
+            file_check( $basename . '.streaks' );
+            file_check( $basename . '_hough.fits' );
+        }
+    } else {
+        print "Skipping command: $command\n";
+    }
+}
+
+
+
+### Input result into database
+{
+    my $command = "$magictool -addresult";
+    $command   .= " -magic_id $magic_id";
+    $command   .= " -node $node";
+    $command   .= " -uri $outroot";
+    $command   .= " -dbname $dbname" if defined $dbname;
+
+    # Add the processed file to the database
+    unless ($no_update) {
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform magictool -addresult: $error_code", $magic_id, $node, $error_code);
+        }
+    } else {
+        print "Skipping command: $command\n";
+    }
+}
+
+
+
+### Pau.
+
+sub file_check
+{
+    my $file = shift;           # Name of file
+    &my_die("Unable to find output file: $file", $magic_id, $node, $PS_EXIT_SYS_ERROR) unless
+        $ipprc->file_exists($file);
+}
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $magic_id = shift;       # Magic identifier
+    my $node = shift;           # Node name
+    my $exit_code = shift;      # Exit code to add
+
+    carp($msg);
+    if (defined $magic_id and defined $node and not $no_update) {
+        my $command = "$magictool -addresult";
+        $command .= " -magic_id $magic_id";
+        $command .= " -node $node";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/magic_tree.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/magic_tree.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/magic_tree.pl	(revision 22322)
@@ -0,0 +1,332 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+
+use Math::Trig;
+use File::Temp qw( tempfile );
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+use constant MAX_FIELDS => 4;   # Maximum number of fields to be in a node
+
+# Parse the command-line arguments
+my ($magic_id, $tess_id, $camera, $ra0, $dec0, $dbname, $outroot,
+    $save_temps, $verbose, $no_update, $no_op, $redirect);
+GetOptions(
+           'magic_id=s'      => \$magic_id,   # Magic identifier
+           'tess_id=s'       => \$tess_id,    # Tessellation identifier
+           'camera=s'        => \$camera,     # Camera name
+           'ra=f'            => \$ra0,        # Boresight right ascension, radians
+           'dec=f'           => \$dec0,       # Boresight declination, radians
+           'dbname=s'        => \$dbname,     # Database name
+           'outroot=s'       => \$outroot,    # Output root name
+           'save-temps'      => \$save_temps, # Save temporary files?
+           'verbose'         => \$verbose,    # Print stuff?
+           'no-update'       => \$no_update,  # Don't update the database?
+           'no-op'           => \$no_op,      # Don't do any operations?
+           'redirect-output' => \$redirect,   # Redirect output?
+           ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --magic_id --camera --tess_id --ra --dec --outroot",
+           -exitval => 3) unless
+    defined $magic_id and
+    defined $tess_id and
+    defined $ra0 and
+    defined $dec0 and
+    defined $camera and
+    defined $outroot;
+
+$ipprc->define_camera($camera);
+
+my $logDest = $ipprc->filename("LOG.EXP", $outroot, $magic_id) or
+    &my_die("Missing entry from camera config", $magic_id, $PS_EXIT_CONFIG_ERROR);
+$ipprc->redirect_output($logDest) if $redirect;
+
+# Look for programs we need
+my $missing_tools;
+my $magictool = can_run('magictool') or (warn "Can't find magictool" 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
+
+### Get a list of skycells
+my @skycells;                   # List of skycells
+{
+    my $command = "$magictool -inputskyfile -magic_id $magic_id"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform magictool -inputfile: $error_code", $magic_id, $error_code);
+    }
+
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $magic_id, $PS_EXIT_PROG_ERROR);
+
+    my $inputs = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $magic_id, $PS_EXIT_PROG_ERROR);
+
+    foreach my $input ( @$inputs ) {
+        push @skycells, $input->{node}; # NB: Storing the skycell_id in magicInputSkyfile.node
+    }
+}
+
+### For each skycell, project centre of skycell onto tangent plane of boresight
+my @fields;
+foreach my $skycell_id ( @skycells ) {
+    my $skyfile = $ipprc->filename("SKYCELL.TEMPLATE", $outroot, $skycell_id );
+    $ipprc->skycell_file($tess_id, $skycell_id, $skyfile, $verbose) or &my_die("Unable to generate skycells $skycell_id", $magic_id, $PS_EXIT_PROG_ERROR);
+    my $skyfileResolved = $ipprc->file_resolve( $skyfile );
+    my ($header, $status) = Astro::FITS::CFITSIO::fits_read_header( $skyfileResolved );
+    &my_die("Unable to read skycell header: $status", $magic_id, $PS_EXIT_SYS_ERROR) if $status;
+
+    # Get the useful header keywords
+    my $naxis1 = $$header{'NAXIS1'} or &my_die("Can't find NAXIS1", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $naxis2 = $$header{'NAXIS2'} or &my_die("Can't find NAXIS2", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $ctype1 = $$header{'CTYPE1'} or &my_die("Can't find CTYPE1", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $ctype2 = $$header{'CTYPE2'} or &my_die("Can't find CTYPE2", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $cdelt1 = $$header{'CDELT1'} or &my_die("Can't find CDELT1", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $cdelt2 = $$header{'CDELT2'} or &my_die("Can't find CDELT2", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $crval1 = $$header{'CRVAL1'} or &my_die("Can't find CRVAL1", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $crval2 = $$header{'CRVAL2'} or &my_die("Can't find CRVAL2", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $crpix1 = $$header{'CRPIX1'} or &my_die("Can't find CRPIX1", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $crpix2 = $$header{'CRPIX2'} or &my_die("Can't find CRPIX2", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $pc11 = $$header{'PC001001'} or &my_die("Can't find PC001001", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $pc12 = $$header{'PC001002'} or &my_die("Can't find PC001002", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $pc21 = $$header{'PC002001'} or &my_die("Can't find PC002001", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $pc22 = $$header{'PC002002'} or &my_die("Can't find PC002002", $magic_id, $PS_EXIT_SYS_ERROR);
+    my $crota1 = $$header{'CROTA1'};
+    my $crota2 = $$header{'CROTA2'};
+
+    &my_die("Unexpected projection: $ctype1 and $ctype2.", $magic_id, $PS_EXIT_SYS_ERROR) unless
+        $ctype1 =~ /^\'RA---TAN\s*\'$/ and $ctype2 =~ /^\'DEC--TAN\s*\'$/;
+    &my_die("Can't determine size of skycell ($naxis1,$naxis2)", $magic_id, $PS_EXIT_SYS_ERROR) unless
+        $naxis1 > 0 and $naxis2 > 0;
+    &my_die("Can't determine scale of skycell ($cdelt1,$cdelt2)", $magic_id, $PS_EXIT_SYS_ERROR) if
+        not defined $cdelt1 or $cdelt1 == 0 or not defined $cdelt2 or $cdelt2 == 0;
+    &my_die("We don't know how to handle rotations ($crota1,$crota2)", $magic_id, $PS_EXIT_SYS_ERROR)
+        if defined $crota1 or defined $crota2;
+
+    # Relative coordinates of centre of the field
+    my $x = $naxis1 - $crpix1;
+    my $y = $naxis2 - $crpix2;
+
+    # Coordinates on tangent plane
+    my $xi = $pc11 * ($x) + $pc12 * ($y);
+    my $eta = $pc21 * ($x) + $pc22 * ($y);
+    $xi *= $cdelt1;
+    $eta *= $cdelt2;
+
+    # Coordinates on rotated celestial sphere
+    my $phi = atan2($eta,$xi) + pi/2;
+    my $theta = atan(180 / pi / sqrt($xi**2 + $eta**2));
+
+    # Coordinates on celestial sphere
+    $crval1 = deg2rad($crval1);
+    $crval2 = deg2rad($crval2);
+    my $ra = $crval1 + atan2(cos($theta) * sin($phi),
+                             sin($theta) * cos($crval2) + cos($theta) * sin($crval2) * cos($phi));
+    my $dec = asin(sin($theta) * sin($crval2) - cos($theta) * cos($crval2) * cos($phi));
+
+    # Rotate to boresight
+    my $phi_new = atan2(cos($dec) * sin($ra - $ra0),
+                        sin($dec) * cos($dec0) + cos($dec) * sin($dec0) * cos($ra - $ra0));
+    my $theta_new = asin(sin($dec) * sin($dec0) - cos($dec) * cos($dec0) * cos($ra - $ra0));
+
+    # Project
+    my $rad = 180 / pi * cot($theta_new);
+    my $xi_new = $rad * sin($phi);
+    my $eta_new = - $rad * cos($phi);
+
+    my $field = { id => $skycell_id,
+                  xi => $xi_new,
+                  eta => $eta_new,
+              };
+
+    push @fields, $field;
+}
+
+### Subdivide list of positions into kd-tree
+my $root = {                    # Root node of tree
+    contents => \@fields,       # Contents of node
+    position => 'root',         # Position in tree
+    children => {},             # Children of node
+};
+my @tasks = ( $root );
+while (scalar @tasks > 1) {
+    my $node = shift @tasks;
+    divide_node($node, \@tasks);
+}
+
+### Format tree for magictool
+my $mdcTree = print_node($root); # The tree in MDC format
+my ($treeFile, $treeName) = tempfile( "magictree.${magic_id}.XXXX", UNLINK => !$save_temps );
+print $treeFile $mdcTree;
+close $treeFile;
+
+### Input tree into database
+{
+    my $command = "$magictool -inputtree";
+    $command   .= " -magic_id $magic_id";
+    $command   .= " -dep_file $treeName";
+    $command   .= " -dbname $dbname" if defined $dbname;
+
+    # Add the processed file to the database
+    unless ($no_update) {
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform magictool -inputtree: $error_code", $magic_id, $error_code);
+        }
+    } else {
+        print "Skipping command: $command\n";
+    }
+}
+
+### Pau.
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $magic_id = shift;       # Magic identifier
+    my $exit_code = shift;      # Exit code to add
+
+    carp($msg);
+    if (defined $magic_id and not $no_update) {
+        my $command = "$magictool -inputtree";
+        $command .= " -magic_id $magic_id";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system($command);
+    }
+    exit $exit_code;
+}
+
+# Divide a list into two, returning the lower and upper parts
+sub divide_list
+{
+    my $list = shift;           # List to divide
+    my $index = shift;          # Name of index for sorting
+
+    my @sorted = sort { $$a{$index} <=> $$b{$index} } @$list; # Sorted list
+    my $median = int(scalar @sorted / 2); # Median point of list
+    my @upper = splice(@sorted, $median); # Upper part of the sorted list
+
+    return (\@sorted, \@upper);
+}
+
+# Create a new node, add it to the parent, and add it to the task list if required
+sub new_node
+{
+    my $parent = shift;         # The parent node
+    my $contents = shift;       # Contents of the new node
+    my $position = shift;       # Position description
+    my $tasks = shift;          # Tasks to do
+
+    my $node = {
+        contents => $contents,
+        position => $parent->{position} . '_' . $position,
+        children => {},
+    };
+
+    $parent->{children}->{$position} = $node;
+
+    push @$tasks, $node if scalar @$contents > 4;
+
+    return $node;
+}
+
+# Divide a node
+sub divide_node
+{
+    my $node = shift;           # Node to divide
+    my $tasks = shift;          # Tasks to do
+
+    my $position = $node->{position};
+
+    my $contents = $node->{contents} or die "Can't find contents of node."; # Contents of node
+
+    my ($lower, $upper) = divide_list($contents, 'xi');
+
+    if (scalar @$lower > 4) {
+        my ($ll, $lr) = divide_list($lower, 'eta');
+        new_node($node, $ll, 'll', $tasks);
+        new_node($node, $lr, 'lr', $tasks);
+    } else {
+        new_node($node, $lower, 'L', $tasks);
+    }
+
+    if (scalar @$upper > 4) {
+        my ($ul, $ur) = divide_list($upper, 'eta');
+        new_node($node, $ul, 'ul', $tasks);
+        new_node($node, $ur, 'ur', $tasks);
+    } else {
+        new_node($node, $upper, 'U', $tasks);
+    }
+
+    $node->{contents} = undef;
+
+    return $node;
+}
+
+# Print the contents of a node
+sub print_node
+{
+    my $node = shift;           # Node to print
+
+    my $position = $node->{position}; # Position of node
+
+    my $output = "$position\t\tMULTI\n"; # Output text
+
+    if (defined $node->{contents}) {
+        foreach my $field ( @{$node->{contents}} ) {
+            my $skycell_id = $field->{id};      # Skycell name
+            $output .= "$position\t\tSTR\t$skycell_id\n";
+            $output .= "$skycell_id\t\tSTR\tNULL\t\# $field->{xi},$field->{eta}\n";
+        }
+    } else {
+        foreach my $div ( keys %{$node->{children}} ) {
+            $output .= "$position\t\tSTR\t${position}_$div\n";
+            $output .= print_node($node->{children}->{$div});
+        }
+    }
+
+    return $output;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/mdc2list.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/mdc2list.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/mdc2list.pl	(revision 22322)
@@ -0,0 +1,77 @@
+#!/usr/bin/env perl
+
+# Program to convert a PS metadata config syntax (usually from one of
+# the IPP tools) into a flat tab-delimited list suitable for parsing
+# using "split".
+
+# Copyright (C) 2006  Joshua Hoblitt, Paul A. Price.
+
+use strict;
+use warnings;
+
+use PS::IPP::Metadata::Config;	# Supplies the metadata config parser.
+
+die "Program to convert a PS metadata config file (usually from one of the IPP\n" .
+    "tools) into a flat tab-delimited list suitable for parsing using \'split\'.\n\n" .
+    "Usage: $0 [INFILE [OUTFILE]]\n" if ((join "", @ARGV) =~ /--help/ or scalar @ARGV > 2);
+
+my $inFile;			# Input file
+my $outFile;			# Output file
+if (scalar @ARGV >= 1) {
+    my $inName = shift @ARGV;
+    open $inFile, $inName or die "Can't open $inName: $!\n";
+    if (scalar @ARGV == 1) {
+	my $outName = shift @ARGV;
+	open $outFile, ">", $outName or die "Can't open $outName: $!\n";
+    } else {
+	$outFile = *STDOUT;
+    }
+} else {
+    $inFile = *STDIN;
+    $outFile = *STDOUT;
+}
+
+my @input = <$inFile>;		# Contents of the metadata config file
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+my $md = $mdcParser->parse(join "", @input)
+        or die "unable to parse metadata config doc";
+my $hashes = mds2hashes($md);	# An array of hashes
+foreach my $pending (@$hashes) {
+    foreach my $key (keys %$pending) {
+	print $outFile ( $pending->{$key} . "\t");
+    }
+    print $outFile "\n";
+}
+
+### Pau.
+
+
+# Given an array of MDs, return an array of hashes
+sub mds2hashes
+{
+    my $mds = shift;		# Reference to the metadatas
+    my @array;			# The array of hashes, to be returned
+    foreach my $md (@$mds) {
+        my $values = md2hash($md->{value});
+        push @array, $values;
+    }
+    return \@array;
+}
+
+# Convert the metadata to a hash; in effect, strips out the comment, type and class fields.
+sub md2hash
+{
+    my $values = shift;		# Reference to the metadata
+    my %hash;			# Hash, to be returned
+    foreach my $data (@$values) {
+        $hash{$data->{name}} = $data->{value};
+    }
+    return \%hash;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/register_exp.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/register_exp.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/register_exp.pl	(revision 22322)
@@ -0,0 +1,228 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Cache::File;
+use Storable qw( freeze thaw );
+use File::Basename qw( basename );
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Config 1.01 qw( :standard );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my $ipprc = PS::IPP::Config->new();
+
+my ($cache, $exp_id, $exp_tag, $dbname, $verbose, $no_update, $no_op, $logfile);
+GetOptions(
+    'caches'        => \$cache,
+    'exp_id|e=s'    => \$exp_id,
+    'exp_tag|t=s'   => \$exp_tag,
+    'dbname|d=s'    => \$dbname, # Database name
+    'verbose'       => \$verbose,   # Print to stdout
+    'no-update'     => \$no_update,
+    'no-op'         => \$no_op,
+    'logfile=s'     => \$logfile,
+) or pod2usage( 2 );
+
+$ipprc->redirect_output($logfile) if $logfile;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --exp_id --exp_tag",
+           -exitval => 3) unless
+    defined $exp_id and
+    defined $exp_tag;
+
+# add -detrend UNLESS type is one of SCIENCE listed below (eg, OBJECT)
+my @SCIENCE = ( "object", "science", "light" ); # Observation types to NOT mark as detrend
+my $DETREND_FLAG = "-end_stage reg"; # Flag to use to mark detrend exposure
+
+# values to extract from output metadata and the stats to calculate
+my $STATS =
+   [   #          register imfile
+       #          label             STATISTIC          CHIPTOOL FLAG
+       { name => "exp_name",        type => "constant",   flag => "-exp_name",        dtype => "string" }, # File level
+       { name => "telescope",       type => "constant",   flag => "-telescope",       dtype => "string" }, # File level
+       { name => "camera",          type => "constant",   flag => "-inst",            dtype => "string" }, # File level
+       { name => "filelevel",       type => "constant",   flag => "-filelevel",       dtype => "string" }, # File level
+       { name => "object",          type => "constant",   flag => "-object",          dtype => "string" },
+       { name => "exp_type",        type => "constant",   flag => "-exp_type",        dtype => "string" }, # File level
+       { name => "filter",          type => "constant",   flag => "-filter",          dtype => "string" }, # File level
+       { name => "comment",         type => "constant",   flag => "-comment",         dtype => "string" }, # ObsComment
+       { name => "dateobs",         type => "constant",   flag => "-dateobs",         dtype => "string" }, # File level
+       { name => "ccd_temp",        type => "mean",       flag => "-ccd_temp",        dtype => "float"  }, # CCD temperature
+       { name => "exp_time",        type => "mean",       flag => "-exp_time",        dtype => "float"  }, # Exposure time
+       { name => "sat_pixel_frac",  type => "mean",       flag => "-sat_pixel_frac",  dtype => "float"  }, # Fraction of saturated pixels
+       { name => "airmass",         type => "mean",       flag => "-airmass",         dtype => "float"  }, # Airmass
+       { name => "ra",              type => "mean",       flag => "-ra",              dtype => "float"  }, # Right ascension
+       { name => "decl",            type => "mean",       flag => "-decl",            dtype => "float"  }, # Declination
+       { name => "posang",          type => "mean",       flag => "-posang",          dtype => "float"  }, # Position angle
+       { name => "alt",             type => "mean",       flag => "-alt",             dtype => "float"  }, # Altitude
+       { name => "az",              type => "mean",       flag => "-az",              dtype => "float"  }, # Azimuth
+       { name => "m1_x",            type => "constant",   flag => "-m1_x",            dtype => "float"  }, # M1X
+       { name => "m1_y",            type => "constant",   flag => "-m1_y",            dtype => "float"  }, # M1Y
+       { name => "m1_z",            type => "constant",   flag => "-m1_z",            dtype => "float"  }, # M1Z
+       { name => "m1_tip",          type => "constant",   flag => "-m1_tip",          dtype => "float"  }, # M1TIP
+       { name => "m1_tilt",         type => "constant",   flag => "-m1_tilt",         dtype => "float"  }, # M1TILT
+       { name => "m2_x",            type => "constant",   flag => "-m2_x",            dtype => "float"  }, # M2X
+       { name => "m2_y",            type => "constant",   flag => "-m2_y",            dtype => "float"  }, # M2Y
+       { name => "m2_z",            type => "constant",   flag => "-m2_z",            dtype => "float"  }, # M2Z
+       { name => "m2_tip",          type => "constant",   flag => "-m2_tip",          dtype => "float"  }, # M2TIP
+       { name => "m2_tilt",         type => "constant",   flag => "-m2_tilt",         dtype => "float"  }, # M2TILT
+       { name => "env_temperature", type => "constant",   flag => "-env_temperature", dtype => "float"  }, # external temp
+       { name => "env_humidity",    type => "constant",   flag => "-env_humidity",    dtype => "float"  }, # external humidity
+       { name => "env_wind_speed",  type => "constant",   flag => "-env_wind_speed",  dtype => "float"  }, # external wind speed
+       { name => "env_wind_dir",    type => "constant",   flag => "-env_wind_dir",    dtype => "float"  }, # external wind direction
+
+       { name => "teltemp_m1",      type => "constant",   flag => "-teltemp_m1",      dtype => "float"  }, # Primary mirror temps (C)
+       { name => "teltemp_m1cell",  type => "constant",   flag => "-teltemp_m1cell",  dtype => "float"  }, # Primary mirror support temps (C)
+       { name => "teltemp_m2",      type => "constant",   flag => "-teltemp_m2",      dtype => "float"  }, # Secondary mirror temps (C
+       { name => "teltemp_spider",  type => "constant",   flag => "-teltemp_spider",  dtype => "float"  }, # Spider temperatures (C)
+       { name => "teltemp_truss",   type => "constant",   flag => "-teltemp_truss",   dtype => "float"  }, # Mid truss temperatures (C
+       { name => "teltemp_extra",   type => "constant",   flag => "-teltemp_extra",   dtype => "float"  }, # Miscellaneous temperatures (C)
+
+       { name => "sun_angle",       type => "constant",   flag => "-sun_angle",       dtype => "float"  }, # Angle to sun
+       { name => "sun_alt",         type => "constant",   flag => "-sun_alt",         dtype => "float"  }, # Altitude of sun
+       { name => "moon_angle",      type => "constant",   flag => "-moon_angle",      dtype => "float"  }, # Angle to moon
+       { name => "moon_alt",        type => "constant",   flag => "-moon_alt",        dtype => "float"  }, # Altitude of moon
+       { name => "moon_phase",      type => "constant",   flag => "-moon_phase",      dtype => "float"  }, # Phase of moon
+
+       { name => "pon_time",        type => "mean",       flag => "-pon_time",        dtype => "float"  }, # time since last power on
+       { name => "bg",              type => "mean",       flag => "-bg",              dtype => "float"  }, # background
+       { name => "bg",              type => "stdev",      flag => "-bg_mean_stdev",   dtype => "float"  }, # Azimuth
+       { name => "bg_stdev",        type => "rms",        flag => "-bg_stdev",        dtype => "float"  }, # Azimuth
+       ];
+my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# Look for commands we need
+my $missing_tools;
+my $regtool = can_run('regtool')
+    or (warn "can't find regtool" and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn ("Can't find required tools");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# setup cache interface
+my $c = Cache::File->new(
+    cache_root => File::Spec->catdir($ENV{'HOME'}, '.pxtools', basename($0)),
+    default_expires => '7200 sec',
+);
+
+# Get the list of imfiles & their stats
+{
+    my $command = "$regtool -processedimfile -exp_id $exp_id";
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        cache_run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn ("Unable to perform regtool -processedimfile on exposure id $exp_id: $error_code");
+        exit ($error_code);
+    }
+
+    # Parse the output
+    my $mdcParser = PS::IPP::Metadata::Config->new;        # Parser for metadata config files
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf);
+    unless ($metadata) {
+        &my_die ("Unable to parse metadata config doc", $exp_id, $PS_EXIT_PROG_ERROR);
+    }
+
+    # extract the stats from the metadata
+    unless ($stats->parse($metadata)) {
+        &my_die ("Unable to find all values", $exp_id, $PS_EXIT_CONFIG_ERROR);
+    }
+}
+
+# we require at a minimum: -telescope, -inst, -filelevel, -class_id, -exp_type
+if (uc($stats->value_for_flag ("-telescope")) eq "NULL") { &my_die ("telescope not found", $exp_id, $PS_EXIT_CONFIG_ERROR); }
+if (uc($stats->value_for_flag ("-inst"))      eq "NULL") { &my_die ("inst      not found", $exp_id, $PS_EXIT_CONFIG_ERROR); }
+if (uc($stats->value_for_flag ("-filelevel")) eq "NULL") { &my_die ("filelevel not found", $exp_id, $PS_EXIT_CONFIG_ERROR); }
+if (uc($stats->value_for_flag ("-exp_type"))  eq "NULL") { &my_die ("exp_type  not found", $exp_id, $PS_EXIT_CONFIG_ERROR); }
+if (uc($stats->value_for_flag ("-exp_name"))  eq "NULL") { &my_die ("exp_name  not found", $exp_id, $PS_EXIT_CONFIG_ERROR); }
+
+my $command = "$regtool -addprocessedexp";
+$command .= " -exp_id $exp_id";
+$command .= " -exp_tag $exp_tag";
+$command .= " -hostname $host" if defined $host;
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= $stats->cmdflags();
+
+my $exp_type = $stats->value_for_flag ("-exp_type");
+
+# Add the detrend flag, if needed
+{
+    my $object = 0;             # Is it an object exposure?
+    foreach my $scienceType (@SCIENCE) {
+        if (lc($exp_type) =~ /$scienceType/) {
+            $object = 1;
+            last;
+        }
+    }
+    $command .= " $DETREND_FLAG" unless $object;
+}
+
+# Output results to the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        cache_run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn ("Unable to run regtool -addprocessedexp for $exp_id: $error_code");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+### Pau.
+
+sub cache_run
+{
+    my %p = @_;
+
+    my $cmd_output = $c->get($p{command}) if $cache;
+    if (defined $cmd_output) {
+        return @{thaw $cmd_output};
+    } else {
+        my @output = run(%p);
+        $c->set($p{command}, freeze \@output) if $cache;
+        return @output;
+    }
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $exp_id = shift;
+    my $exit_code = shift;
+
+    carp($msg);
+    if (defined $exp_id and not $no_update) {
+        my $command = "$regtool -addprocessedexp -exp_id $exp_id -code $exit_code";
+        $command .= " -hostname $host" if defined $host;
+        $command .= " -dbname $dbname" if defined $dbname;
+        system($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $exit = $?;
+    system("sync") == 0 or die "failed to execute sync: $!";
+    $? = $exit;
+}
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/register_imfile.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/register_imfile.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/register_imfile.pl	(revision 22322)
@@ -0,0 +1,279 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Cache::File;
+use Storable qw(freeze thaw);
+use File::Basename qw( basename);
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Config 1.01 qw( :standard );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+
+my $PI = 3.141592653589793238462643383279502;
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+use File::Spec;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($cache, $exp_id, $tmp_class_id, $tmp_exp_name, $uri, $dbname, $verbose, $no_update, $no_op, $logfile);
+GetOptions(
+    'caches'           => \$cache,
+    'exp_id|e=s'       => \$exp_id,
+    'tmp_class_id|i=s' => \$tmp_class_id,
+    'tmp_exp_name|n=s' => \$tmp_exp_name,
+    'uri|u=s'          => \$uri,
+    'dbname|d=s'       => \$dbname,    # Database name
+    'verbose'          => \$verbose,   # Print to stdout
+    'no-update'        => \$no_update,
+    'no-op'            => \$no_op,
+    'logfile=s'        => \$logfile,
+) or pod2usage( 2 );
+
+$ipprc->redirect_output($logfile) if $logfile;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --exp_id --tmp_class_id --tmp_exp_name --uri",
+           -exitval => 3) unless
+    defined $exp_id and
+    defined $tmp_class_id and
+    defined $tmp_exp_name and
+    defined $uri;
+
+
+my $RECIPE = "REGISTER"; # Recipe to use for ppStats
+
+# values to extract from output metadata and the stats to calculate
+my $STATS =
+   [
+       #          PPSTATS KEYWORD         STATISTIC          CHIPTOOL FLAG
+       { name => "FILE.LEVEL",     type => "constant", flag => "-filelevel",       dtype => "string" }, # File level
+       { name => "CLASS.ID",       type => "constant", flag => "-class_id",        dtype => "string" }, # Real Class ID
+       { name => "FPA.OBJECT",     type => "constant", flag => "-object",          dtype => "string" }, # Object
+       { name => "FPA.OBSTYPE",    type => "constant", flag => "-exp_type",        dtype => "string" }, # Exposure type
+       { name => "FPA.FILTER",     type => "constant", flag => "-filter",          dtype => "string" }, # Filter used
+       { name => "FPA.COMMENT",    type => "constant", flag => "-comment",         dtype => "string" }, # Obs Comment
+       { name => "FPA.AIRMASS",    type => "constant", flag => "-airmass",         dtype => "float"  }, # Airmass
+       { name => "FPA.RA",         type => "constant", flag => "-ra",              dtype => "float"  }, # Right ascension
+       { name => "FPA.DEC",        type => "constant", flag => "-decl",            dtype => "float"  }, # Declination
+       { name => "FPA.ALT",        type => "constant", flag => "-alt",             dtype => "float"  }, # Altitude
+       { name => "FPA.AZ",         type => "constant", flag => "-az",              dtype => "float"  }, # Azimuth
+       { name => "FPA.POSANGLE",   type => "constant", flag => "-posang",          dtype => "float"  }, # Position angle
+       { name => "FPA.TIME",       type => "constant", flag => "-dateobs",         dtype => "string" }, # Date of observation (UTC)
+       { name => "FPA.TELESCOPE",  type => "constant", flag => "-telescope",       dtype => "string" }, # Telescope
+       { name => "FPA.CAMERA",     type => "constant", flag => "-inst",            dtype => "string" }, # Instrument
+       { name => "FPA.LONGITUDE",  type => "constant", flag => "-longitude",       dtype => "float"  }, # Site longitude
+       { name => "FPA.LATITUDE",   type => "constant", flag => "-latitude",        dtype => "float"  }, # Site latitude
+       { name => "FPA.ELEVATION",  type => "constant", flag => "-elevation",       dtype => "float"  }, # Site elevation
+       { name => "FPA.M1X",        type => "constant", flag => "-m1_x",            dtype => "float"  }, # M1X
+       { name => "FPA.M1Y",        type => "constant", flag => "-m1_y",            dtype => "float"  }, # M1Y
+       { name => "FPA.M1Z",        type => "constant", flag => "-m1_z",            dtype => "float"  }, # M1Z
+       { name => "FPA.M1TIP",      type => "constant", flag => "-m1_tip",          dtype => "float"  }, # M1TIP
+       { name => "FPA.M1TILT",     type => "constant", flag => "-m1_tilt",         dtype => "float"  }, # M1TILT
+       { name => "FPA.M2X",        type => "constant", flag => "-m2_x",            dtype => "float"  }, # M2X
+       { name => "FPA.M2Y",        type => "constant", flag => "-m2_y",            dtype => "float"  }, # M2Y
+       { name => "FPA.M2Z",        type => "constant", flag => "-m2_z",            dtype => "float"  }, # M2Z
+       { name => "FPA.M2TIP",      type => "constant", flag => "-m2_tip",          dtype => "float"  }, # M2TIP
+       { name => "FPA.M2TILT",     type => "constant", flag => "-m2_tilt",         dtype => "float"  }, # M2TILT
+       { name => "FPA.ENV.TEMP",   type => "constant", flag => "-env_temperature", dtype => "float"  }, # external temp
+       { name => "FPA.ENV.HUMID",  type => "constant", flag => "-env_humidity",    dtype => "float"  }, # external humidity
+       { name => "FPA.ENV.WIND",   type => "constant", flag => "-env_wind_speed",  dtype => "float"  }, # external wind speed
+       { name => "FPA.ENV.DIR",    type => "constant", flag => "-env_wind_dir",    dtype => "float"  }, # external wind direction
+
+       { name => "FPA.TELTEMP.M1",     type => "constant", flag => "-teltemp_m1",     dtype => "float"  }, # Primary mirror temps (C)
+       { name => "FPA.TELTEMP.M1CELL", type => "constant", flag => "-teltemp_m1cell", dtype => "float"  }, # Primary mirror support temps (C)
+       { name => "FPA.TELTEMP.M2",     type => "constant", flag => "-teltemp_m2",     dtype => "float"  }, # Secondary mirror temps (C
+       { name => "FPA.TELTEMP.SPIDER", type => "constant", flag => "-teltemp_spider", dtype => "float"  }, # Spider temperatures (C)
+       { name => "FPA.TELTEMP.TRUSS",  type => "constant", flag => "-teltemp_truss",  dtype => "float"  }, # Mid truss temperatures (C
+       { name => "FPA.TELTEMP.EXTRA",  type => "constant", flag => "-teltemp_extra",  dtype => "float"  }, # Miscellaneous temperatures (C)
+
+       { name => "FPA.PON.TIME",   type => "constant", flag => "-pon_time",        dtype => "float"  }, # time since last power on
+       { name => "CHIP.TEMP",      type => "mean",     flag => "-ccd_temp",        dtype => "float"  }, # CCD temperature
+       { name => "CELL.EXPOSURE",  type => "mean",     flag => "-exp_time",        dtype => "float"  }, # Exposure time
+       { name => "SAT_PIXEL_FRAC", type => "mean",     flag => "-sat_pixel_frac",  dtype => "float"  }, # fraction of saturated pixels
+       { name => "ROBUST_MEDIAN",  type => "mean",     flag => "-bg",              dtype => "float"  },
+       { name => "ROBUST_MEDIAN",  type => "stdev",    flag => "-bg_mean_stdev",   dtype => "float"  },
+       { name => "ROBUST_STDEV",   type => "rms",      flag => "-bg_stdev",        dtype => "float"  },
+   ];
+my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $regtool = can_run('regtool')
+    or (warn "Can't find regtool" and $missing_tools = 1);
+my $ppStats = can_run('ppStats')
+    or (warn "Can't find ppStats" and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn ("Can't find required tools");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# setup cache interface
+# XXX why is this being cached?
+my $c = Cache::File->new(
+    cache_root => File::Spec->catdir($ENV{'HOME'}, '.pxtools', basename($0)),
+    default_expires => '7200 sec',
+);
+
+my $now_time = localtime();
+printf STDERR "\nstarting ppStats: %s\n", $now_time if $verbose;
+
+# Run ppStats on the input file
+{
+    # extract the data from the image header; -level is used to get FILE.LEVEL and CLASS.ID
+    my $command = "$ppStats $uri -recipe PPSTATS $RECIPE -level"; # Command to run ppStats
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        cache_run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die ("Unable to perform ppStats on exposure id $exp_id: $error_code", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $error_code);
+    }
+
+    # Parse the output
+    my $mdcParser = PS::IPP::Metadata::Config->new;        # Parser for metadata config files
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf); # XXX is this join necessary?
+    unless ($metadata) {
+        &my_die ("Unable to parse metadata config doc", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_PROG_ERROR);
+    }
+
+    # extract the stats from the metadata
+    unless ($stats->parse($metadata)) {
+        &my_die ("Unable to find all values", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR);
+    }
+}
+
+$now_time = localtime();
+printf STDERR "\ndone with ppStats: %s\n", $now_time if $verbose;
+
+# we require at a minimum: -telescope, -inst, -filelevel, -class_id, -exp_type
+if (uc($stats->value_for_flag ("-telescope")) eq "NULL") { &my_die ("telescope not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
+if (uc($stats->value_for_flag ("-inst"))      eq "NULL") { &my_die ("inst      not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
+if (uc($stats->value_for_flag ("-filelevel")) eq "NULL") { &my_die ("filelevel not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
+if (uc($stats->value_for_flag ("-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($stats->value_for_flag ("-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";
+$command .= " -exp_id $exp_id";
+$command .= " -exp_name $tmp_exp_name"; # keep the supplied exp_name (could be derived from the file)
+$command .= " -tmp_class_id $tmp_class_id"; # the original class_id supplied by the user, replace by ppStats CLASS.ID
+$command .= " -uri $uri ";
+$command .= " -hostname $host" if defined $host;
+$command .= " -dbname $dbname" if defined $dbname;
+$command .= $stats->cmdflags();
+
+# determine solar-system parameters
+my $longitude = $stats->value_for_flag ("-longitude");
+my $latitude  = $stats->value_for_flag ("-latitude");
+my $elevation = $stats->value_for_flag ("-elevation");
+my $ra        = $stats->value_for_flag ("-ra");
+my $dec       = $stats->value_for_flag ("-decl");
+my $dateobs   = $stats->value_for_flag ("-dateobs");
+
+# if the needed data is available, pass it to sunmoon:
+if ($longitude && $latitude && $ra && $dec && $dateobs) {
+    
+    $longitude *= 12.0 / $PI; # longitude is reported in West radians; sunmoon wants it in West Hours
+    $latitude *= 180.0 / $PI; # latitude is reported in North radians; sunmoon wants it in North Degrees
+    $ra *= 180.0 / $PI; # ra is reported in radians; sunmoon wants it in degrees
+    $dec *= 180.0 / $PI; # dec is reported in radians; sunmoon wants it in degrees
+
+    my $sunmoon_cmd = "sunmoon -latitude $latitude -longitude $longitude -elevation $elevation $dateobs $ra $dec";
+    my $sunmoon_data = `$sunmoon_cmd`;
+    chomp $sunmoon_data;
+
+    # print STDERR "run: $sunmoon_cmd\n";
+    # print STDERR "got: $sunmoon_data\n";
+
+    if ($?) {
+	warn ("failure running $sunmoon_cmd, not supplying\n");
+    } else {
+	$command .= " $sunmoon_data";
+    }
+}
+
+$now_time = localtime();
+printf STDERR "\nrunning regtool update: %s\n", $now_time if $verbose;
+
+# Push the results into the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn ("Unable to perform regtool -addprocessedimfile: $error_code");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+$now_time = localtime();
+printf STDERR "\ndone with regtool update: %s\n", $now_time if $verbose;
+
+sub cache_run
+{
+    my %p = @_;
+
+    my $cmd_output = $c->get($p{command}) if $cache;
+    if (defined $cmd_output) {
+        return @{thaw $cmd_output};
+    } else {
+        my @output = run(%p);
+        $c->set($p{command}, freeze \@output) if $cache;
+        return @output;
+    }
+}
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $exp_id = shift;
+    my $exp_name = shift;
+    my $tmp_class_id = shift;
+    my $uri = shift;
+    my $exit_code = shift;
+
+    # for failed imfiles, we insert UNKNOWN for inst, telescope, class_id
+
+    carp($msg);
+    if (defined $exp_id && defined $tmp_class_id and not $no_update) {
+        my $command = "$regtool -addprocessedimfile";
+        $command .= " -exp_id $exp_id";
+        $command .= " -exp_name $exp_name";
+        $command .= " -tmp_class_id $tmp_class_id";
+        $command .= " -uri $uri ";
+        $command .= " -telescope UNKNOWN";
+        $command .= " -inst UNKNOWN";
+        $command .= " -class_id $tmp_class_id";
+        $command .= " -code $exit_code";
+        $command .= " -hostname $host" if defined $host;
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+# Pau.
+
+END {
+    my $exit = $?;
+    system("sync") == 0 or die "failed to execute sync: $!";
+    $? = $exit;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/stack_skycell.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/stack_skycell.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/stack_skycell.pl	(revision 22322)
@@ -0,0 +1,311 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+use Carp;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use Data::Dumper;
+use File::Temp qw( tempfile );
+use File::Basename;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($stack_id, $dbname, $outroot, $debug, $run_state, $threads, $verbose, $no_update, $no_op, $redirect, $save_temps);
+GetOptions(
+    'stack_id|d=s'      => \$stack_id, # Stack identifier
+    'dbname|d=s'        => \$dbname, # Database name
+    'outroot=s'         => \$outroot, # Output root name
+    'run-state=s'       => \$run_state,
+    'debug'             => \$debug,   # Print to stdout
+    'threads=s'         => \$threads,   # Number of threads to use for ppStack
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update, # Don't update the database?
+    'no-op'             => \$no_op, # Don't do any operations?
+    'redirect-output'   => \$redirect,
+    'save-temps'        => \$save_temps, # Save temporary files?
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --stack_id --outroot --run-state",
+    -exitval => 3,
+          ) unless defined $stack_id
+    and defined $outroot
+    and defined $run_state;
+
+# XXX camera is not known here; cannot use filerules...
+# my $logDest = $ipprc->filename("LOG.EXP", $outroot);
+
+my $logDest = "$outroot.log";
+
+my $do_stats;
+if ($run_state eq 'new') {
+    $do_stats = 1;
+} else {
+    $logDest .= ".update";
+}
+$ipprc->redirect_output($logDest) if $redirect;
+
+my $STATS =
+   [
+       #          PPSTATS KEYWORD         STATISTIC          STACKTOOL FLAG
+       { name => "ROBUST_MEDIAN",   type => "mean",  flag => "-bg",                dtype => "float" },
+       { name => "ROBUST_STDEV",    type => "rms",   flag => "-bg_stdev",          dtype => "float" },
+       { name => "TIME_STACK",      type => "sum",   flag => "-dtime_stack",       dtype => "float" },
+       { name => "TIME_MATCH",      type => "mean",  flag => "-dtime_match_mean",  dtype => "float" },
+       { name => "TIME_MATCH",      type => "stdev", flag => "-dtime_match_stdev", dtype => "float" },
+       { name => "TIME_INITIAL",    type => "sum",   flag => "-dtime_initial",     dtype => "float" },
+       { name => "TIME_REJECT",     type => "sum",   flag => "-dtime_reject",      dtype => "float" },
+       { name => "TIME_FINAL",      type => "sum",   flag => "-dtime_final",       dtype => "float" },
+       { name => "TIME_PHOT",       type => "sum",   flag => "-dtime_phot",        dtype => "float" },
+       { name => "STAMP.MEAN",      type => "mean",  flag => "-match_mean",        dtype => "float" },
+       { name => "STAMP.MEAN",      type => "stdev", flag => "-match_stdev",       dtype => "float" },
+       { name => "STAMP.RMS",       type => "rms",   flag => "-match_rms",         dtype => "float" },
+       { name => "STAMP.NUM",       type => "mean",  flag => "-stamps_mean",       dtype => "float" },
+       { name => "STAMP.NUM",       type => "stdev", flag => "-stamps_stdev",      dtype => "float" },
+       { name => "STAMP.NUM",       type => "min",   flag => "-stamps_min",        dtype => "int" },
+       { name => "TIME_PHOT",       type => "sum",   flag => "-dtime_phot",        dtype => "float" },
+       { name => "REJECT_IMAGES",   type => "sum",   flag => "-reject_images",     dtype => "int" },
+       { name => "REJECT_PIXELS",   type => "mean",  flag => "-reject_pix_mean",   dtype => "float" },
+       { name => "REJECT_PIXELS",   type => "stdev", flag => "-reject_pix_stdev",  dtype => "float" },
+       { name => "NUM_SOURCES",     type => "sum",   flag => "-sources",           dtype => "int" },
+       { name => "GOOD_PIXEL_FRAC", type => "mean",  flag => "-good_frac",         dtype => "float" },
+   ];
+my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $stacktool = can_run('stacktool') or (warn "Can't find stacktool" and $missing_tools = 1);
+my $ppStack = can_run('ppStack') or (warn "Can't find ppStack" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Get list of components for stacking
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+my $files;
+{
+    my $command = "$stacktool -inputskyfile -stack_id $stack_id";
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform stacktool -inputskyfile: $error_code", $stack_id, $error_code);
+    }
+
+    if (@$stdout_buf == 0) {
+        &my_die("No input skyfiles selected", $stack_id, $PS_EXIT_PROG_ERROR);
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $stack_id, $PS_EXIT_PROG_ERROR);
+    $files = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $stack_id, $PS_EXIT_PROG_ERROR);
+}
+
+&my_die("Stack list contains less than two elements", $stack_id, $PS_EXIT_SYS_ERROR) unless
+    scalar @$files >= 2;
+
+# Parse the list of input files to get the tesselation, skycell identifiers and camera
+my $skycell_id;                 # Skycell identifier
+my $tess_id;                    # Tesselation identifier
+my $camera;                     # Camera
+foreach my $file (@$files) {
+    # skip warps which are specified as 'ignored'
+    if ($file->{ignored}) { next; }
+    if (defined $tess_id) {
+        &my_die("Tesselation identifiers don't match", $stack_id, $PS_EXIT_SYS_ERROR) unless
+            $file->{tess_id} eq $tess_id;
+    } else {
+        $tess_id = $file->{tess_id};
+    }
+    if (defined $skycell_id) {
+        &my_die("Skycell identifiers don't match", $stack_id, $PS_EXIT_SYS_ERROR) unless
+            $file->{skycell_id} eq $skycell_id;
+    } else {
+        $skycell_id = $file->{skycell_id};
+    }
+    if (defined $camera) {
+        &my_die("Cameras don't match", $stack_id, $PS_EXIT_SYS_ERROR) unless $file->{camera} eq $camera;
+    } else {
+        $camera = $file->{camera};
+    }
+}
+
+&my_die("Can't find camera", $stack_id, $PS_EXIT_SYS_ERROR) unless defined $camera;
+$ipprc->define_camera($camera);
+
+# Generate MDC file with the inputs
+my $tess_base = basename($tess_id);
+my ($listFile, $listName) = tempfile("/tmp/$tess_base.$skycell_id.stk.$stack_id.list.XXXX", UNLINK => !$save_temps );
+my $num = 0;
+my $inputSources;               # Sources to use as stamps
+foreach my $file (@$files) {
+    if ($file->{ignored}) { next; }
+
+    print $listFile "INPUT$num\tMETADATA\n";
+    $num++;
+
+    my $image = $file->{uri};   # Image name
+    my $mask = $ipprc->filename( "PSWARP.OUTPUT.MASK", $file->{path_base} ); # Mask name
+    my $weight = $ipprc->filename( "PSWARP.OUTPUT.WEIGHT", $file->{path_base} ); # Weight name
+    my $psf = $ipprc->filename( "PSPHOT.PSF.SKY.SAVE", $file->{path_base} ); # PSF name
+    my $sources = $ipprc->filename("PSWARP.OUTPUT.SOURCES", $file->{path_base}); # Sources name
+
+    &my_die("Image $image does not exist", $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists( $image );
+    &my_die("Mask $mask does not exist", $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists( $mask );
+    &my_die("Weight $weight does not exist", $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists( $weight );
+    &my_die("PSF $psf does not exist", $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists( $psf );
+    &my_die("Sources $sources does not exist", $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists( $sources );
+
+    print $listFile "\tIMAGE\tSTR\t" . $image . "\n";
+    print $listFile "\tMASK\tSTR\t" . $mask . "\n";
+    print $listFile "\tWEIGHT\tSTR\t" . $weight . "\n";
+    print $listFile "\tPSF\tSTR\t" . $psf . "\n";
+    print $listFile "\tSOURCES\tSTR\t" . $sources . "\n";
+
+    ### XXX NEED TO UPDATE THESE appropriately
+    print $listFile "\tWEIGHTING\tF32\t" . 1.0 . "\n";
+
+    print $listFile "END\n\n";
+}
+
+# Get the output filenames
+my $outputName = $ipprc->filename("PPSTACK.OUTPUT", $outroot);
+my $outputMask = $ipprc->filename("PPSTACK.OUTPUT.MASK", $outroot);
+my $outputWeight = $ipprc->filename("PPSTACK.OUTPUT.WEIGHT", $outroot);
+my $outputSources = $ipprc->filename("PSPHOT.OUT.CMF.MEF", $outroot); ## this must be consistent with the value in diff_skycell.pl:101
+## use an explicit stack name for psphot output objects
+#my $bin1Name =  $ipprc->filename("PPSTACK.BIN1", $outroot);
+#my $bin2Name =  $ipprc->filename("PPSTACK.BIN2", $outroot);
+my $outputStats = $ipprc->filename("SKYCELL.STATS", $outroot);
+my $traceDest = $ipprc->filename("TRACE.EXP", $outroot);
+$traceDest .= ".update" if $run_state eq 'update';
+my $configuration = $ipprc->filename("PPSTACK.CONFIG", $outroot);
+
+# for update we need to resolve the config filename here because the code that reads it
+# doesn't know how to resolve paths. (The information is stored in the configuration file)
+$configuration = $ipprc->file_resolve($configuration) if ($run_state eq 'update');
+
+# Perform stacking
+unless ($no_op) {
+    my $command = "$ppStack $listName $outroot";
+    $command .= " -stats $outputStats" if $do_stats;;
+    $command .= " -recipe PPSUB STACK";
+    $command .= " -recipe PPSTATS WARPSTATS" if $do_stats;;
+    $command .= " -F PSPHOT.PSF.SAVE PSPHOT.PSF.SKY.SAVE";
+    $command .= " -F PSPHOT.OUTPUT PSPHOT.OUT.CMF.MEF";
+    $command .= " -F PSPHOT.BACKMDL PSPHOT.BACKMDL.MEF";
+    $command .= " -photometry";
+    $command .= " -threads $threads" if defined $threads;
+    $command .= " -debug-stack" if defined $debug;
+    $command .= " -tracedest $traceDest -log $logDest";
+    $command .= " -dbname $dbname" if defined $dbname;
+    if ($run_state eq 'new') {
+        $command .= " -dumpconfig $configuration";
+    } else {
+        $command .= " -ipprc $configuration";
+    }
+
+
+    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 ppStack: $error_code", $stack_id, $error_code);
+    }
+    &my_die("Couldn't find expected output file: $outputName", $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputName);
+    &my_die("Couldn't find expected output file: $outputMask", $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputMask);
+    &my_die("Couldn't find expected output file: $outputWeight", $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputWeight);
+    &my_die("Couldn't find expected output file: $outputSources", $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputSources);
+#   &my_die("Couldn't find expected output file: $bin1Name",    $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($bin1Name);
+#   &my_die("Couldn't find expected output file: $bin2Name",    $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($bin2Name);
+
+    if ($do_stats) {
+        &my_die("Couldn't find expected output file: $outputStats", $stack_id, $PS_EXIT_SYS_ERROR)
+            unless $ipprc->file_exists($outputStats);
+        # Get the statistics on the stacked image
+        my $statsFile;              # File handle
+        open $statsFile, $ipprc->file_resolve($outputStats) or &my_die("Can't open statistics file $outputStats: $!", $stack_id, $PS_EXIT_SYS_ERROR);
+        my @contents = <$statsFile>; # Contents of file
+        close $statsFile;
+        my $metadata = $mdcParser->parse(join "", @contents) or
+            &my_die("Unable to parse metadata config doc", $stack_id, $PS_EXIT_PROG_ERROR);
+        $stats->parse($metadata) or &my_die("Unable to find all values in statistics output.", $stack_id, $PS_EXIT_PROG_ERROR);
+    }
+}
+
+unless ($no_update) {
+
+    # Add the stack result
+    {
+        my $command = "$stacktool";
+        my $mode;
+        if ($run_state eq 'new') {
+            $mode = "-addsumskyfile";
+            $command .= " -uri $outputName -path_base $outroot";
+            $command .= " -hostname $host" if defined $host;
+        } else {
+            $mode = "-updaterun -state full";
+        }
+        $command .= " $mode -stack_id $stack_id";
+        $command .= $stats->cmdflags() if $do_stats;
+        $command .= " -dbname $dbname" if defined $dbname;
+
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform stacktool $mode $error_code", $stack_id, $error_code);
+        }
+    }
+
+}
+
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $stack_id = shift;       # Stack identifier
+    my $exit_code = shift;      # Exit code to add
+
+    carp($msg);
+    if (defined $stack_id and not $no_update) {
+        my $command = "$stacktool -stack_id $stack_id -code $exit_code";
+        if ($run_state eq 'new') {
+            $command .= " -addsumskyfile";
+            $command .= " -hostname $host" if defined $host;
+        } else {
+            $command .= " -updatesumskyfile";
+        }
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $exit = $?;
+    system("sync") == 0 or die "failed to execute sync: $!";
+    $? = $exit;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/summit_copy.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/summit_copy.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/summit_copy.pl	(revision 22322)
@@ -0,0 +1,163 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Config 1.01 qw( :standard );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use Sys::Hostname;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+## report the program and machine
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+# Parse the command-line arguments
+my ( $uri, $filename, $compress, $bytes, $md5, $nebulous, $exp_name, $inst, $telescope, $class, $class_id, $end_stage, $workdir,
+     $dbname, $verbose, $no_update, $no_op, $timeout );
+GetOptions(
+       'uri=s'          => \$uri,       # source location of file on data store
+       'filename=s'     => \$filename, # target location of file on local system
+       'compress'       => \$compress,  # request file in compressed format
+       'bytes=s'        => \$bytes,     # reported file size in bytes
+       'md5=s'          => \$md5,       # reported md5 checksum
+       'nebulous'       => \$nebulous,  # use nebulous for the target file
+       'exp_name=s'     => \$exp_name,  # Exposure name
+       'inst=s'         => \$inst,      # Instrument
+       'telescope=s'    => \$telescope, # Telescope
+       'class=s'        => \$class,     # Class level
+       'class_id=s'     => \$class_id,  # Class identifier
+       'end_stage=s'    => \$end_stage, # end of processing
+       'workdir=s'      => \$workdir,   # workdir
+       'dbname=s'       => \$dbname,    # Database name
+       'verbose'        => \$verbose,   # Print to stdout
+       'no-update'      => \$no_update, # Don't update the database?
+       'no-op'          => \$no_op,     # Don't do any operations?
+       'timeout=s'      => \$timeout,   # passed through to dsget
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --uri --filename --exp_name --inst --telescope --class --class_id --workdir",
+       -exitval => 3)
+    unless defined $uri
+    and defined $filename
+    and defined $exp_name
+    and defined $inst
+    and defined $telescope
+    and defined $class
+    and defined $class_id
+    and defined $workdir;
+
+# Look for programs we need
+my $missing_tools;
+my $dsget = can_run('dsget')
+    or (warn "Can't find dsget" and $missing_tools = 1);
+my $pztool = can_run('pztool')
+    or (warn "Can't find pztool" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# dsget command
+my $command;
+$command  = "$dsget --uri $uri --filename $filename";
+$command .= " --compress"           if defined $compress;
+$command .= " --bytes $bytes"       if defined $bytes;
+$command .= " --nebulous"           if defined $nebulous;
+$command .= " --md5 $md5"           if defined $md5;
+$command .= " --timeout $timeout"   if defined $timeout;
+
+# run command
+unless ($no_op) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf )
+        = run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        my_die("Unable to perform dsget: $error_code",
+            $exp_name,
+            $inst,
+            $telescope,
+            $class,
+            $class_id,
+            $uri,
+            $error_code
+        );
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+# command to update database
+$command  = "$pztool -copydone";
+$command .= " -exp_name $exp_name";
+$command .= " -inst $inst";
+$command .= " -telescope $telescope";
+$command .= " -class $class";
+$command .= " -class_id $class_id";
+$command .= " -uri $filename";
+$command .= " -workdir $workdir";
+$command .= " -end_stage $end_stage" if defined $end_stage;
+$command .= " -dbname $dbname" if defined $dbname;
+
+# update the database
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf )
+        = run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform $command: $error_code\n");
+        exit($error_code);
+    }
+} else {
+    print "skipping command: $command\n";
+}
+
+sub my_die
+{
+    my $msg       = shift; # Warning message on die
+    my $exp_name  = shift; # Chiptool identifier
+    my $inst      = shift; # Chiptool identifier
+    my $telescope = shift; # Class identifier
+    my $class     = shift; # Class identifier
+    my $class_id  = shift; # Class identifier
+    my $uri       = shift; # Class identifier
+    my $exit_code = shift; # Exit code to add
+
+    warn $msg;
+    unless ($no_update) {
+        # command to update database
+        my $command;
+        $command  = "$pztool -copydone";
+        $command .= " -exp_name $exp_name";
+        $command .= " -inst $inst";
+        $command .= " -telescope $telescope";
+        $command .= " -class $class";
+        $command .= " -class_id $class_id";
+        $command .= " -uri $uri";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+
+        system ($command);
+    }
+
+    exit $exit_code;
+}
+
+# XXX: I don't think that we need this - JH
+#END {
+#    my $status = $?;
+#    system("sync") == 0
+#        or die "failed to execute sync: $!" ;
+#    $? = $status;
+#}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/warp_overlap.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/warp_overlap.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/warp_overlap.pl	(revision 22322)
@@ -0,0 +1,314 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use File::Temp qw( tempfile );
+use File::Spec;
+use PS::IPP::Config 1.01 qw( :standard );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+my ($warp_id, $camera, $tess_dir, $dbname, $verbose, $no_update, $no_op, $logfile, $save_temps);
+GetOptions(
+    'warp_id|i=s'       => \$warp_id, # Warp identifier
+    'camera|c=s'        => \$camera, # Camera name
+    'tess_dir=s'        => \$tess_dir, # Tessellation directory
+    'dbname|d=s'        => \$dbname, # Database name
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update, # Don't update the database?
+    'no-op'             => \$no_op, # Don't do any operations
+    'logfile=s'         => \$logfile,
+    'save-temps'        => \$save_temps, # Save temporary files?
+) or pod2usage( 2 );
+
+$ipprc->redirect_output($logfile) if $logfile;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --warp_id --camera --tess_dir",
+    -exitval => 3,
+) unless defined $warp_id
+    and defined $camera
+    and defined $tess_dir;
+
+$ipprc->define_camera($camera);
+
+# Look for programs we need
+my $missing_tools;
+my $warptool = can_run('warptool') or (warn "Can't find warptool" and $missing_tools = 1);
+my $dvoImageOverlaps = can_run('dvoImageOverlaps') or (warn "Can't find dvoImageOverlaps" and $missing_tools = 1);
+my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+# Get list of component imfiles for exposure
+my $imfiles;
+{
+    my $command = "$warptool -imfile -warp_id $warp_id";
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform warptool -imfile: $error_code", $warp_id, $error_code);
+    }
+
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $warp_id, $PS_EXIT_PROG_ERROR);
+    $imfiles = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $warp_id, $PS_EXIT_PROG_ERROR);
+}
+
+# Where do we get the astrometry source from? 
+my $astromSource;               # The astrometry source filerule (eg, PSASTRO.OUTPUT, PSASTRO.OUTPUT.MEF)
+my $astromAccept;               # Accept the astrometry unconditionally?
+{
+    my $command = "$ppConfigDump -camera $camera -dump-recipe PSWARP -";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform ppConfigDump: $error_code", $warp_id, $error_code);
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $warp_id, $PS_EXIT_PROG_ERROR);
+    $astromSource = metadataLookupStr($metadata, 'ASTROM.SOURCE');
+    $astromAccept = metadataLookupBool($metadata, 'ASTROM.ACCEPT');
+}
+
+# Determine the imfile/skycell overlaps
+my @overlaps = ();
+unless ($no_op) {
+    # Calculate the overlaps between imfiles and skycells
+
+    # tess_dir is the DVO db holding the tessalations
+    # this may be an abstract name in site.config:TESSELLATIONS, a URI, or an absolute path.
+    # convert this to an absolute path
+    my $tess_dir_abs = $ipprc->tessellation_catdir( $tess_dir ); # Tessellation catdir for DVO
+    $tess_dir_abs = $ipprc->convert_filename_absolute( $tess_dir_abs );
+
+    my %unique_skycells = (); # Identified skycells (all unique by virtue of hash property)
+
+    # astrometry is always determined at the camera stage; we have a MEF astrometry file from psastro
+    my $imfile = $imfiles->[0];
+    my $camRoot = $imfile->{cam_path_base};
+    my $astromFile = $ipprc->filename($astromSource, $camRoot); # Astrometry file
+    if (!$astromFile) {
+	&my_die("Unable to determine the astrometry source", $warp_id, $PS_EXIT_DATA_ERROR);
+    }
+    $astromFile = $ipprc->file_resolve($astromFile);
+    if (!$astromFile) {
+	&my_die("Unable to resolve real astrometry source filename", $warp_id, $PS_EXIT_DATA_ERROR);
+    }
+
+    my @matchlist = get_overlaps($astromFile, $tess_dir_abs, $astromAccept); # List of overlaps
+    if (! @matchlist) {
+	&my_die("Unable to perform dvoImageOverlaps: missing astrometry", $warp_id, $PS_EXIT_DATA_ERROR);
+    }
+    # Match each of the imfiles to this list (the input images may be split, but the astrometry is not)
+    # tess_dir (not tess_dir_abs) is supplied here so it may be written to the db
+    foreach my $imfile (@$imfiles) {
+	extract_overlaps(\@matchlist, $imfile, $astromFile, $tess_dir, \@overlaps, \%unique_skycells);
+    }
+} else {
+    # create an overlap with an entry for each skycell:imfile match
+    foreach my $imfile (@$imfiles) {
+        my %overlap = ();
+        $overlap{skycell_id} = 'default';
+        $overlap{tess_dir}   = 'default';
+        $overlap{cam_id}     = $imfile->{cam_id};
+        $overlap{class_id}   = $imfile->{class_id};
+        $overlap{fault}      = $imfile->{fault};
+        push @overlaps, \%overlap;
+    }
+}
+
+# If no overlaps are found, we
+# keep running this step over and over (a successful warptool -addoverlap prevents
+# successive warptime -imfile from running.
+&my_die("Unable to find any overlaps", $warp_id, $PS_EXIT_PROG_ERROR) if scalar @overlaps == 0;
+
+# Generate a MDC file with the overlaps
+my ($overlapFile, $overlapName) = tempfile( "/tmp/overlaps.wrp.$warp_id.mdc.XXXX", UNLINK => !$save_temps );
+print $overlapFile "warpSkyCellMap MULTI\n\n";
+foreach my $overlap (@overlaps) {
+    print $overlapFile "warpSkyCellMap   METADATA\n";
+    print $overlapFile "  warp_id        S32    $warp_id\n";
+    print $overlapFile "  skycell_id     STR    $overlap->{skycell_id}\n";
+    # XXX convert tess_id here to tess_dir when db scheme is updated
+    print $overlapFile "  tess_id        STR    $overlap->{tess_dir}\n"; 
+    print $overlapFile "  cam_id         S32    $overlap->{cam_id}\n";
+    print $overlapFile "  class_id       STR    $overlap->{class_id}\n";
+    print $overlapFile "  fault          S16    $overlap->{fault}\n";
+    print $overlapFile "END\n\n";
+}
+close $overlapFile;
+
+system "cat $overlapName" if $verbose;
+
+# Add the processed file to the database
+unless ($no_update) {
+    my $command = "$warptool -addoverlap -mapfile $overlapName"; # Command to run warptool
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform warptool -addoverlap: $error_code\n");
+        exit($error_code);
+    }
+}
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $warp_id = shift;        # Warp identifier
+    my $exit_code = shift;      # Exit code to add
+
+    carp($msg);
+    if (defined $warp_id and not $no_update) {
+        my $command = "$warptool -addoverlap";
+        $command .= " -warp_id $warp_id";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+# Run dvoImageOverlaps to get the overlaps; return the output
+sub get_overlaps
+{
+    my $filename = shift;       # Filename on which to run dvoImageOverlaps
+    my $tess_dir_abs = shift;       # Tessellation directory
+    my $accept = shift;         # Do we use the -accept-astrom flag?
+
+    my $command = "$dvoImageOverlaps -D CATDIR $tess_dir_abs " . $filename;
+    $command .= ' -accept-astrom' if $accept;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    if (!$success) {
+        print "Unable to calculate overlaps from $filename\n";
+        print "STDOUT:\n";
+        foreach my $line (@$stdout_buf) {
+            print ">>>$line";
+        }
+        print "STDERR:\n";
+        foreach my $line (@$stderr_buf) {
+            print ">>>$line";
+        }
+        return 0;
+    }
+    return split ('\n', (join "", @$stdout_buf));
+}
+
+# Extract a list of overlaps for an imfile
+#
+# The command "dvoImageOverlaps FILENAME" returns:
+# FILENAME[HDU] : SKYCELL.ID
+# eg: 729534pa.cmf[ccd00.hdr]  :  skycell.051.fits
+# for the case of split data, we need to identify the input imfiles based on this information
+sub extract_overlaps
+{
+    my $matches = shift;         # Reference to list of skycells from dvoImageOverlaps
+    my $imfile = shift;          # Imfile information
+    my $filename = shift;        # Filename used with dvoImageOverlaps
+    my $tess_dir = shift;        # Tessellation identifier
+    my $overlaps = shift;        # Reference to list of overlaps
+    my $unique_skycells = shift; # Reference to hash of found skycells
+
+    # Get rid of the path
+    my @dirlist = File::Spec->splitdir( $filename ); # The elements of the full path
+    $filename = pop @dirlist;
+
+    # Work out how to identify this imfile in the output
+    my $fileLevel = $imfile->{filelevel};
+    my $entry;  # How to identify this imfile in the dvoImageOverlaps output
+
+    if (lc($fileLevel) eq "chip") {
+	# in the case of SPLIT images, all CLASSes are included in the output list
+	# we need to pull out the single CLASS_ID for this imfile from the full list
+        my $class_id = $imfile->{class_id};
+        my $chipRoot = $imfile->{chip_path_base};
+        my $extname = $ipprc->extname_rule("CMF.HEAD", $class_id); # MEF psastro output
+
+        $entry = $filename . '\[' . $extname . '\]';
+        print STDERR "entry: $entry, class: $class_id, extname: $extname, chiproot: $chipRoot\n" if $verbose;
+    } else {
+	# in the case of MEF or SINGLE images, there is only a single CLASS in the output list
+        $entry = $filename;
+        print STDERR "entry: $entry\n" if $verbose;
+    }
+
+    my @skycells = &select_skycells($entry, @$matches); # Matching skycells
+    my $Nskycells = @skycells;
+    printf STDERR "Nskycells: $Nskycells\n" if $verbose;
+    foreach my $skycell (@skycells) {
+        my %overlap = ();       # Overlap information for warptool
+        $overlap{skycell_id} = $skycell;
+        $overlap{tess_dir}   = $tess_dir;
+        $overlap{cam_id}     = $imfile->{cam_id};
+        $overlap{class_id}   = $imfile->{class_id};
+        $overlap{fault}      = $imfile->{fault};
+        push @$overlaps, \%overlap;
+
+        printf STDERR "overlap: %s : %s , %s\n", $skycell, $imfile->{cam_id}, $imfile->{class_id} if $verbose;
+
+        $unique_skycells->{$skycell} = 1;
+    }
+
+    return;
+}
+
+# Find skycells in the list that come from a particular entry
+sub select_skycells
+{
+    my $entry = shift;          # File+Ext to search for
+    my @list = @_;              # List of "File+Ext : skycell"
+
+    my @skycells = ();
+    my %unique = ();            # Ensure we only return unique skycells for this entry
+
+    foreach my $line (@list) {
+        if ($line =~ m|$entry|) {
+            my ($skycell) = $line =~ m|$entry\S*\s+:\s+(\S+)|;
+            if (not defined $unique{$skycell}) {
+                push @skycells, $skycell;
+                $unique{$skycell} = 1;
+            }
+        }
+    }
+    return @skycells;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/scripts/warp_skycell.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/scripts/warp_skycell.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/scripts/warp_skycell.pl	(revision 22322)
@@ -0,0 +1,321 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Spec;
+use File::Temp qw( tempfile );
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+my ($warp_id, $skycell_id, $tess_dir, $camera, $dbname, $outroot, $threads, $run_state, $verbose, $no_update, $no_op, $redirect, $save_temps);
+GetOptions(
+    'warp_id|i=s'       => \$warp_id, # Warp identifier
+    'skycell_id|s=s'    => \$skycell_id, # Skycell identifier
+    'tess_dir|s=s'       => \$tess_dir, # Tesselation identifier
+    'camera|c=s'        => \$camera, # Camera name
+    'dbname|d=s'        => \$dbname, # Database name
+    'outroot=s'         => \$outroot, # Output root name
+    'threads=s'         => \$threads,   # Number of threads to use for pswarp
+    'run-state=s'       => \$run_state,  # 'new' or 'update'
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update, # Don't update the database?
+    'no-op'             => \$no_op, # Don't do any operations?
+    'redirect-output'   => \$redirect,
+    'save-temps'        => \$save_temps, # Save temporary files?
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --warp_id --skycell_id --tess_dir --camera --outroot --run-state",
+    -exitval => 3,
+) unless defined $warp_id
+    and defined $skycell_id
+    and defined $tess_dir
+    and defined $camera
+    and defined $outroot
+    and defined $run_state;
+
+$ipprc->define_camera($camera);
+
+my $logDest = $ipprc->filename("LOG.EXP", $outroot, $skycell_id);
+$logDest .= ".update" if ($run_state eq 'update');
+
+$ipprc->redirect_output($logDest) if $redirect;
+
+my $STATS =
+   [
+       #          PPSTATS KEYWORD         STATISTIC          WARPTOOL FLAG
+       { name => "ROBUST_MEDIAN",   type => "mean", flag => "-bg",         dtype => "float" },
+       { name => "ROBUST_STDEV",    type => "rms",  flag => "-bg_stdev",   dtype => "float" },
+       { name => "DT_WARP",         type => "sum",  flag => "-dtime_warp", dtype => "float" },
+       { name => "GOOD_PIXEL_FRAC", type => "mean", flag => "-good_frac",  dtype => "float" },
+       { name => "RANGE.XMIN",      type => "mean", flag => "-xmin",       dtype => "int"   },
+       { name => "RANGE.XMAX",      type => "mean", flag => "-xmax",       dtype => "int"   },
+       { name => "RANGE.YMIN",      type => "mean", flag => "-ymin",       dtype => "int"   },
+       { name => "RANGE.YMAX",      type => "mean", flag => "-ymax",       dtype => "int"   },
+   ];
+my $stats = PS::IPP::Metadata::Stats->new($STATS); # Stats parser
+
+# Look for programs we need
+my $missing_tools;
+my $warptool = can_run('warptool') or (warn "Can't find warptool" and $missing_tools = 1);
+my $pswarp = can_run('pswarp') or (warn "Can't find pswarp" and $missing_tools = 1);
+my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Get list of component imfiles for exposure
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+my $imfiles;
+{
+    # XXX change -tess_id to -tess_dir when db schema is updated
+    my $command = "$warptool -scmap -warp_id $warp_id -skycell_id $skycell_id -tess_id $tess_dir";
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform warptool -scmap: $error_code", $warp_id, $skycell_id, $tess_dir, $error_code);
+    }
+
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_PROG_ERROR);
+    $imfiles = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_PROG_ERROR);
+}
+
+# Where do we get the astrometry source from?
+my $astromSource;               # The astrometry source
+{
+    my $command = "$ppConfigDump -camera $camera -dump-recipe PSWARP -";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die("Unable to perform ppConfigDump: $error_code", $warp_id, $error_code);
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $warp_id, $PS_EXIT_PROG_ERROR);
+    $astromSource = metadataLookupStr($metadata, 'ASTROM.SOURCE');
+}
+
+my $outputImage = $ipprc->filename("PSWARP.OUTPUT", $outroot, $skycell_id );
+my $outputMask = $ipprc->filename("PSWARP.OUTPUT.MASK", $outroot, $skycell_id);
+my $outputWeight = $ipprc->filename("PSWARP.OUTPUT.WEIGHT", $outroot, $skycell_id);
+my $outputSources = $ipprc->filename("PSWARP.OUTPUT.SOURCES", $outroot, $skycell_id);
+my $outputPSF = $ipprc->filename("PSPHOT.PSF.SKY.SAVE", $outroot);
+my $outputBin1 = $ipprc->filename("PSWARP.BIN1", $outroot, $skycell_id );
+my $outputBin2 = $ipprc->filename("PSWARP.BIN2", $outroot, $skycell_id );
+my $outputStats = $ipprc->filename("SKYCELL.STATS", $outroot, $skycell_id );
+my $traceDest = $ipprc->filename("TRACE.EXP", $outroot, $skycell_id);
+my $configuration =  $ipprc->filename("PSWARP.CONFIG", $outroot, $skycell_id);
+
+if ($run_state eq 'update') {
+    $traceDest .= ".update";
+    $outputStats .= ".update";
+}
+
+my $skyFile = $ipprc->filename("SKYCELL.TEMPLATE", $outroot, $skycell_id );
+$ipprc->skycell_file( $tess_dir, $skycell_id, $skyFile, $verbose ) or &my_die("Unable to generate template skycell", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR);
+## XXX this seems to have insufficient error checking: dvoImageExtract can fail to write and still return a valid exit status
+
+# Get list of filenames
+my $tempOutRoot = "/tmp/warp.$camera.$warp_id.$skycell_id";
+my ($imageFile,  $imageName)  = tempfile( "$tempOutRoot.image.list.XXXX",  UNLINK => !$save_temps);
+my ($maskFile,   $maskName)   = tempfile( "$tempOutRoot.mask.list.XXXX",   UNLINK => !$save_temps);
+my ($weightFile, $weightName) = tempfile( "$tempOutRoot.weight.list.XXXX", UNLINK => !$save_temps);
+my ($astromFile, $astromName) = tempfile( "$tempOutRoot.astrom.list.XXXX", UNLINK => !$save_temps);
+
+my $wrote_astrom = 0;
+foreach my $imfile (@$imfiles) {
+    my $image = $imfile->{uri}; # Image name
+    my $mask = $ipprc->filename("PPIMAGE.CHIP.MASK", $imfile->{chip_path_base}, $imfile->{class_id}); # Mask name
+    my $weight = $ipprc->filename("PPIMAGE.CHIP.WEIGHT", $imfile->{chip_path_base}, $imfile->{class_id}); # Mask name
+
+    &my_die("Couldn't find input file: $image", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($image);
+    &my_die("Couldn't find input file: $mask", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($mask);
+
+    # Astrometry file: astrometry is done at the camera stage, and always results in a MEF file
+    # XXX allow an option to use the image header astrometry?
+    my $astrom = $ipprc->filename($astromSource, $imfile->{cam_path_base});
+
+    &my_die("Couldn't find input file: $astrom", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless defined $astrom and $ipprc->file_exists($astrom);
+
+    print $imageFile  "$image\n";
+    print $maskFile   "$mask\n";
+    print $weightFile "$weight\n";
+
+    if (!$wrote_astrom) {
+        print $astromFile "$astrom\n";
+        $wrote_astrom = 1;
+    }
+}
+close $imageFile;
+close $maskFile;
+close $weightFile;
+close $astromFile;
+
+
+# Run pswarp
+my $accept = 1;                 # Accept the skycell?
+my $do_stats;
+unless ($no_op) {
+    my $command = "$pswarp";
+    $command .= " -list $imageName";
+    $command .= " -masklist $maskName";
+    $command .= " -weightlist $weightName";
+    $command .= " -astromlist $astromName";
+    $command .= " $outroot $skyFile";
+    $command .= " -F PSPHOT.PSF.SAVE PSPHOT.PSF.SKY.SAVE";
+    $command .= " -F PSPHOT.OUTPUT PSPHOT.OUT.CMF.MEF";
+    $command .= " -F PSPHOT.BACKMDL PSPHOT.BACKMDL.MEF";
+    $command .= " -psf";        # Turn on PSF determination
+    $command .= " -tracedest $traceDest -log $logDest";
+    $command .= " -threads $threads" if defined $threads;
+    $command .= " -dbname $dbname" if defined $dbname;
+    if ($run_state eq 'new') {
+        $command .= " -dumpconfig $configuration";
+        $do_stats = 1;
+    } else {
+        $command .= " -ipprc $configuration";
+    }
+    if ($do_stats) {
+        $command .= " -recipe PPSTATS WARPSTATS";
+        $command .= " -stats $outputStats";
+    }
+
+    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 pswarp: $error_code", $warp_id, $skycell_id, $tess_dir, $error_code);
+    }
+
+    if ($do_stats) {
+        # Check first for the stats file, and if the ACCEPT flag is set.
+        &my_die("Couldn't find expected output file: $outputStats", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputStats);
+        # Get the statistics on the warped image
+        my $statsFile;              # File handle
+        open $statsFile, $ipprc->file_resolve($outputStats) or die "Can't open statistics file $outputStats: $!\n";
+        my @contents = <$statsFile>; # Contents of file
+        close $statsFile;
+        my $contents = join "", @contents;
+
+        my $metadata = $mdcParser->parse($contents)
+            or &my_die("Unable to parse metadata config", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_PROG_ERROR);
+        $accept = metadataLookupBool($metadata, "ACCEPT");
+
+        # $accept is set above based on the fraction of lit pixels
+        # XXX for some files, there may not be enough stars to find a good psf.  these should be dropped as well
+        if ($accept && !$ipprc->file_exists($outputPSF)) {
+            $accept = 0;
+        }
+
+        if ($accept) {
+            $stats->parse($metadata) or &my_die("Unable to find all values in statistics output.", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_PROG_ERROR);
+
+            &my_die("Couldn't find expected output file: $outputImage", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputImage);
+            &my_die("Couldn't find expected output file: $outputMask", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputMask);
+            &my_die("Couldn't find expected output file: $outputWeight", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputWeight);
+            &my_die("Couldn't find expected output file: $outputSources", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputSources);
+    #    &my_die("Couldn't find expected output file: $outputPSF", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputPSF);
+    #    &my_die("Couldn't find expected output file: $outputBin1", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputBin1);
+    #    &my_die("Couldn't find expected output file: $outputBin2", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputBin2);
+        }
+
+        unless ($no_update) {
+            # XXX change -tess_id to -tess_dir when db is updated
+            my $command = "$warptool -addwarped";
+            $command .= " -warp_id $warp_id";
+            $command .= " -skycell_id $skycell_id";
+            $command .= " -tess_id $tess_dir";
+            $command .= " -path_base $outroot"; # needed for logfile lookups
+            $command .= " -ignore"           if not $accept; # Completed succesfully, but can't produce product
+            $command .= " -uri $outputImage" if $accept;
+            $command .= $stats->cmdflags()   if $accept;
+            $command .= " -hostname $host"   if defined $host;
+            $command .= " -dbname $dbname"   if defined $dbname;
+
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+            unless ($success) {
+                $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+                warn("Unable to perform warptool -addwarped: $error_code\n");
+                exit($error_code);
+            }
+        }
+    } else {
+        # $run_state eq 'update'
+        unless ($no_update) {
+            my $command = "$warptool -tofullskyfile";
+            $command .= " -warp_id $warp_id";
+            $command .= " -skycell_id $skycell_id";
+            $command .= " -dbname $dbname"   if defined $dbname;
+
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+            unless ($success) {
+                $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+                warn("Unable to perform warptool -addwarped: $error_code\n");
+                exit($error_code);
+            }
+        }
+    }
+}
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $warp_id = shift;        # Warp identifier
+    my $skycell_id = shift;     # Skycell identifier
+    my $tess_dir = shift;        # Tesselation identifier
+    my $exit_code = shift;      # Exit code to add
+
+    warn($msg);
+    if (defined $warp_id and defined $skycell_id and defined $tess_dir and not $no_update) {
+        # XXX change -tess_id to -tess_dir when db is updated
+        my $command = "$warptool";
+        if ($run_state eq 'new') {
+            $command .= " -addwarped";
+            $command .= " -tess_id $tess_dir";
+            $command .= " -path_base $outroot";
+            $command .= " -hostname $host" if defined $host;
+        } else {
+            $command .= " -updateskyfile";
+        }
+        $command .= " -warp_id $warp_id";
+        $command .= " -skycell_id $skycell_id";
+        $command .= " -code $exit_code";
+        $command .= " -dbname $dbname" if defined $dbname;
+        run(command => $command, verbose => $verbose);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/ippScripts/t/00_distribution.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippScripts/t/00_distribution.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippScripts/t/00_distribution.t	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005  Joshua Hoblitt
+#
+# $Id: 00_distribution.t,v 1.1 2007-01-30 01:52:16 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use lib qw( ./lib ./t );
+
+use Test::More;
+
+# example taken from Test::Distribution Pod
+
+BEGIN {
+    eval {
+        require Test::Distribution;
+    };
+    if($@) {
+        plan skip_all => 'Test::Distribution not installed';
+    } else {
+        import Test::Distribution; # not => qw( podcover );
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/ippTasks/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.log
+config.status
+configure
+install-sh
+missing
Index: /tags/sj_tags/sj_root_20080929/ippTasks/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/Makefile.am	(revision 22322)
@@ -0,0 +1,44 @@
+task_files = \
+	detrend.mkruns.pro \
+	detrend.norm.pro \
+	detrend.process.pro \
+	detrend.reject.pro \
+	detrend.resid.pro \
+	detrend.stack.pro \
+	automate.pro \
+	pantasks.pro \
+	register.pro \
+	chip.pro \
+	camera.pro \
+	fake.pro \
+	warp.pro \
+	magic.pro \
+	diff.pro \
+	stack.pro \
+	summit.copy.pro \
+	replicate.pro \
+	pstamp.pro
+
+other_files = \
+	ipphosts.manoa.config \
+	ipphosts.mhpcc.config \
+	site.manoa.pro \
+	site.mhpcc.pro \
+	simtest.pro \
+	simtest.basic.config \
+	simtest.basic.auto \
+	simtest.detverify.config \
+	simtest.detverify.auto \
+	simtest.flatcorr.config \
+	simtest.flatcorr.auto
+
+pantasksdir = $(datadir)/pantasks
+ipptasksdir = $(pantasksdir)/modules
+ipptasks_DATA = $(task_files) $(other_files)
+
+install-data-hook:
+	chmod 0755 $(panstasks) $(ipptasksdir)
+
+EXTRA_DIST = $(task_files) $(other_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippTasks/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ippTasks
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+#($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+#        DIE=1
+#}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+#($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+#        DIE=1
+#}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+#$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+#$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ippTasks/automate.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/automate.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/automate.pro	(revision 22322)
@@ -0,0 +1,456 @@
+## automate.pro : autorun the detrend analysis : -*- sh -*-
+
+# example automation stage
+# automate METADATA
+#  name       STR DARK
+#  check      STR "detselect -search -inst SIMTEST -det_type BIAS -dbname eamtest"
+#  ncheck     S32 1
+#  launch     STR "dettool -definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type DARK -select_exp_type DARK -dbname eamtest"
+#  block      STR "dettool -runs -active -det_type DARK -dbname eamtest"
+# END
+
+# an entry in the 'automate' book goes through the following states:
+# INIT (book loaded from input file)
+# CHECK (stage prereqs being checked)
+# READY (prereqs satisfield, ready to be launched)
+# LAUNCH (action being performed)
+# DONE (action finished)
+
+macro automate.reset
+  ## probably should not always init
+  book init automate
+end
+
+macro automate.load
+  if ($0 != 4)
+    echo "USAGE: automate.load (filename) (camera) (dbname)"
+    break
+  end
+  queueload tmp -x "cat $MODULES:0/$1"
+
+  pwd -var cwd
+
+  ## interpolate standard values
+  queuesubstr tmp @CAMERA@ $2
+  queuesubstr tmp @DBNAME@ $3
+  queuesubstr tmp @CWD@ $cwd
+
+  ipptool2book tmp automate -key name -uniq -setword pantaskState INIT.BLOCK
+  queuedelete tmp
+
+  ## Pull out the ones that are to be run regularly
+  local npages
+  book npages automate -var npages
+  for i 0 $npages
+    book getpage automate $i -var pageName
+    if ("$pageName" != "NULL") 
+      book getword automate $pageName regular -var regularCommand
+      if ("$regularCommand" != "NULL")
+         book setword automate $pageName pantaskState INIT.REGULAR
+      end
+    end
+  end
+
+end
+
+macro automate.on
+  task automate.block
+    active true
+  end
+  task automate.check
+    active true
+  end
+  task automate.launch
+    active true
+  end
+  task automate.regular
+    active true
+  end
+end
+
+macro automate.off
+  task automate.block
+    active false
+  end
+  task automate.check
+    active false
+  end
+  task automate.launch
+    active false
+  end
+  task automate.regular
+    active false
+  end
+end
+
+$automate_Nblock = -1
+task automate.block
+  host         local
+
+  periods      -poll 1
+  periods      -exec 5
+  periods      -timeout 30
+  active       true
+  npending     1
+
+  task.exec
+    local Npage pageName
+
+    # how many pages are waiting to be started?
+    book npages automate -var Npage -key pantaskState INIT.BLOCK
+    if ($Npage == 0) 
+      if ($VERBOSE >= 2) 
+        echo "no entries in INIT.BLOCK state"
+      end
+      break
+    end
+
+    # cycle over the number of INIT.BLOCK pages: no point to loop over the others
+    $automate_Nblock ++
+    if ($automate_Nblock >= $Npage) set automate_Nblock = 0
+
+    # search the automate book for an entry which is unstarted (state INIT.BLOCK) 
+    book getpage automate $automate_Nblock -var pageName -key pantaskState INIT.BLOCK
+    if ("$pageName" == "NULL") 
+      if ($VERBOSE >= 2) 
+        echo "entry $automate_Nblock not in INIT.BLOCK state"
+      end
+      break 
+    end
+ 
+    book getword automate $pageName block -var blockCommand
+    if (("$blockCommand" == "NULL") || ("$blockCommand" == "NONE"))
+      # if there is no block needed, we can immediate progress to the next stage (INIT.CHECK)
+      book setword automate $pageName pantaskState INIT.CHECK
+      if ($VERBOSE >= 2) 
+        echo "$pageName is ready : INIT.CHECK"
+      end
+      break
+    end
+
+    book setword automate $pageName pantaskState RUN.BLOCK
+
+    if ($VERBOSE >= 2)
+      echo "starting automate block for $pageName"
+      echo "command $blockCommand"
+    end
+
+    options $pageName
+    command $blockCommand
+  end
+
+  task.exit $EXIT_SUCCESS
+    local pageName Npage
+
+    $pageName = $options:0
+
+    # convert 'stdout' to book format
+    # XXX to use other tests, we'll need to modify this 
+    ipptool2book stdout tmpBlock
+    if ($VERBOSE > 2)
+      book listbook tmpBlock
+    end
+
+    # if the block test returns any valid pages (valid results), the block is set, don't move to check
+    book npages tmpBlock -var Npage 
+    if ($Npage == 0) 
+      if ($VERBOSE >= 2)
+        echo "$pageName is not blocked, ready for CHECK"
+      end
+      book setword automate $pageName pantaskState INIT.CHECK
+    else
+      if ($VERBOSE >= 2)
+        echo "$pageName is blocked, not ready for CHECK"
+      end
+      book setword automate $pageName pantaskState INIT.BLOCK
+    end
+
+    # drop the detExt book after we've grabbed the state
+    book delete tmpBlock
+  end
+
+  # all failures here (what state?)
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+$automate_Ncheck = -1
+task automate.check
+  host         local
+
+  periods      -poll 1
+  periods      -exec 5
+  periods      -timeout 30
+  active       true
+  npending     1
+
+  task.exec
+    local Npage pageName
+
+    # how many pages are waiting to be started?
+    book npages automate -var Npage -key pantaskState INIT.CHECK
+    if ($Npage == 0) 
+      if ($VERBOSE >= 2) 
+        echo "no entries in INIT.CHECK state"
+      end
+      break
+    end
+
+    # cycle over the number of INIT.CHECK pages: no point to loop over the others
+    $automate_Ncheck ++
+    if ($automate_Ncheck >= $Npage) set automate_Ncheck = 0
+
+    # search the automate book for an entry which is unstarted (state INIT.CHECK) 
+    book getpage automate $automate_Ncheck -var pageName -key pantaskState INIT.CHECK
+    if ("$pageName" == "NULL") 
+      if ($VERBOSE >= 2) 
+        echo "entry $automate_Ncheck not in INIT.CHECK state"
+      end
+      break 
+    end
+ 
+    book getword automate $pageName check -var checkCommand
+    if (("$checkCommand" == "NULL") || ("$checkCommand" == "NONE"))
+      # if there is no check needed, we can immediate progress to the next stage (INIT.LAUNCH)
+      book setword automate $pageName pantaskState INIT.LAUNCH
+      if ($VERBOSE >= 2) 
+        echo "$pageName is ready : INIT.LAUNCH"
+      end
+      break
+    end
+
+    book setword automate $pageName pantaskState RUN.CHECK
+
+    if ($VERBOSE >= 2)
+      echo "starting automate check for $pageName"
+      echo "command $checkCommand"
+    end
+
+    options $pageName
+    command $checkCommand
+  end
+
+  task.exit $EXIT_SUCCESS
+    local pageName Npage
+
+    $pageName = $options:0
+
+    # convert 'stdout' to book format
+    # XXX to use other tests, we'll need to modify this 
+    ipptool2book stdout tmpCheck
+    if ($VERBOSE > 2)
+      book listbook tmpCheck
+    end
+
+    book getword automate $pageName ncheck -var Ncheck
+    if ("$Ncheck" == "NULL")
+      $Ncheck = 1
+    end 
+    book npages tmpCheck -var Npage 
+    if ($Npage < $Ncheck) 
+      if ($VERBOSE >= 2) 
+        echo "$pageName not ready for LAUNCH"
+      end
+      book setword automate $pageName pantaskState INIT.CHECK
+    else
+      if ($VERBOSE >= 2) 
+        echo "$pageName is ready for LAUNCH"
+      end
+      book setword automate $pageName pantaskState INIT.LAUNCH
+
+      if ($Ncheck == 1) 
+        # XXX this is a somewhat hackish way to carry information from the 'check' to the 'launch'
+        book getword tmpCheck page.000 det_id -var DET_ID
+        book getword tmpCheck page.000 iteration -var ITERATION
+        book setword automate $pageName det_id $DET_ID
+        book setword automate $pageName iteration $ITERATION
+      end
+    end
+
+    # drop the detExt book after we've grabbed the state
+    book delete tmpCheck
+  end
+
+  # all failures here (what state?)
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+$automate_Nlaunch = 0
+task automate.launch
+  host         local
+
+  periods      -poll 1
+  periods      -exec 5
+  periods      -timeout 30
+  active       true
+  npending     1
+
+  task.exec
+    local Npage pageName
+
+    # how many pages are waiting to be started?
+    book npages automate -var Npage -key pantaskState INIT.LAUNCH
+    if ($Npage == 0) 
+      if ($VERBOSE >= 2)
+        echo "no entries in INIT.LAUNCH state"
+      end
+      break
+    end
+
+    # cycle over the number of INIT.LAUNCH pages: no point to loop over the others
+    $automate_Nlaunch ++
+    if ($automate_Nlaunch >= $Npage) set automate_Nlaunch = 0
+
+    # search the automate book for an entry which is unstarted (state INIT.LAUNCH) 
+    book getpage automate $automate_Nlaunch -var pageName -key pantaskState INIT.LAUNCH
+    if ("$pageName" == "NULL") 
+      if ($VERBOSE >= 2)
+        echo "entry $automate_Nlaunch not in INIT.LAUNCH state"
+      end
+      break 
+    end
+ 
+    book getword automate $pageName launch -var launchCommand
+    if (("$launchCommand" == "NULL") || ("$launchCommand" == "NONE"))
+      # if there is no launch needed, we can immediate progress to the next stage (INIT.LAUNCH)
+      book setword automate $pageName pantaskState INIT.LAUNCH
+      if ($VERBOSE >= 2)
+        echo "$pageName is ready : INIT.LAUNCH"
+      end
+      break
+    end
+
+    # modify the launch command to replace certain elements from the page
+    book getword automate $pageName det_id -var DET_ID
+    if ("$DET_ID" != "NULL") 
+      echo '$launchCommand'
+      strsub "$launchCommand" @det_id@ $DET_ID -var launchCommand
+    end
+
+    book getword automate $pageName iteration -var ITERATION
+    if ("$ITERATION" != "NULL") 
+      echo '$launchCommand'
+      strsub "$launchCommand" @iteration@ $ITERATION -var launchCommand
+    end
+
+    echo '$launchCommand'
+
+    book setword automate $pageName pantaskState RUN.LAUNCH
+
+    if ($VERBOSE >= 2)
+      echo "starting automate launch for $pageName"
+      echo "command $launchCommand"
+    end
+
+    options $pageName
+    command $launchCommand
+  end
+
+  task.exit $EXIT_SUCCESS
+    local pageName Npage
+
+    $pageName = $options:0
+
+    book setword automate $pageName pantaskState DONE.LAUNCH
+  end
+
+  # all failures here (what state?)
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+
+$automate_Nregular = 0
+task automate.regular
+  host         local
+
+  periods      -poll 1
+  periods      -exec 5
+  periods      -timeout 30
+  active       true
+  npending     1
+
+  task.exec
+    local Npage pageName
+
+    # how many pages are waiting to be started?
+    book npages automate -var Npage -key pantaskState INIT.REGULAR
+    if ($Npage == 0) 
+      if ($VERBOSE >= 2)
+        echo "no entries in INIT.REGULAR state"
+      end
+      break
+    end
+
+    # cycle over the number of INIT.REGULAR pages: no point to loop over the others
+    $automate_Nregular ++
+    if ($automate_Nregular >= $Npage) set automate_Nregular = 0
+
+    # search the automate book for an entry which is unstarted (state INIT.REGULAR) 
+    book getpage automate $automate_Nregular -var pageName -key pantaskState INIT.REGULAR
+    if ("$pageName" == "NULL") 
+      if ($VERBOSE >= 2)
+        echo "entry $automate_Nregular not in INIT.REGULAR state"
+      end
+      break 
+    end
+ 
+    book getword automate $pageName regular -var regularCommand
+    if (("$regularCommand" == "NULL") || ("$regularCommand" == "NONE"))
+      if ($VERBOSE >= 2)
+        echo "Warning: $pageName has no regular command"
+      end
+      break
+    end
+
+    book setword automate $pageName pantaskState RUN.REGULAR
+
+    if ($VERBOSE >= 2)
+      echo "starting automate regular for $pageName"
+      echo "command $regularCommand"
+    end
+
+    options $pageName
+    command $regularCommand
+  end
+
+  task.exit $EXIT_SUCCESS
+    local pageName Npage
+
+    $pageName = $options:0
+
+    book setword automate $pageName pantaskState INIT.REGULAR
+  end
+
+  # all failures here (what state?)
+  task.exit    default
+    book setword automate $pageName pantaskState FAIL.REGULAR
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    book setword automate $pageName pantaskState INIT.REGULAR
+    showcommand timeout
+  end
+end
+
Index: /tags/sj_tags/sj_root_20080929/ippTasks/calibration.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/calibration.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/calibration.pro	(revision 22322)
@@ -0,0 +1,173 @@
+## calibration.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the calibration stage
+## these tasks use the book 'calBook'
+
+# test for required global variables
+check.globals
+
+$LOGSUBDIR = $LOGDIR/calibration
+exec mkdir -p $LOGSUBDIR
+
+book init calBook
+
+macro calibration.reset
+  book init calBook
+end
+
+macro calibration.status
+  book listbook calBook
+end
+
+macro calibration.on
+  task calibration.init
+    active true
+  end
+end
+
+macro calibration.off
+  task calibration.init
+    active false
+  end
+end
+
+# these variables will cycle through the known ippdb database names
+$calInit_DB = 0
+
+# we have three steps here:
+# 1) get the list of dvo databases:  caltool -dbs
+# 2) define a new calibration run for each of the dvo dbs
+#    caltool 
+
+# create new calibration entries for the currently known DVO databases
+# run this multiple times once an hour - on pass for each db
+task	       calibration.load
+  host         local
+
+  # check the list of available dvo dbs regularly
+  periods      -poll 10
+  periods      -exec 900
+  periods      -timeout 60
+
+  # this is a strange construction; what is the purpose?
+  if ($DB:n == 0) 
+    npending $DB:n
+  else
+    npending 1
+  end
+
+  # define the command (does not depend on previous queries)
+  if ($DB:n != 0)
+    command caltool -dbs -active true
+  else
+    # save the DB name for the exit tasks
+    # note that this DB name refers to the ippdb, not the dvodb
+    option $DB:$calInit_DB
+    command caltool -dbs -dbname $DB:$calInit_DB
+    $calInit_DB ++
+    if ($calInit_DB >= $DB:n) set calInit_DB = 0
+  end
+
+  # silently drop stdout
+  stdout NULL
+  stderr $LOGSUBDIR/calibration.log
+
+  # success
+  task.exit $EXIT_SUCCESS
+    # convert 'stdout' to book format
+    # XXX have ippTools report the dbname?
+    ipptool2book stdout calBook -key dbname:cal_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook calBook
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup calBook
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# create new calibration entries for the currently known DVO databases
+# run this multiple times once an hour - one pass for each db
+# can we include information that the db has been updated without a recent cal analysis?
+task	       calibration.resort
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 60
+  # trange     Hourly@00:00 Hourly@00:10
+  # every hour on the hour
+  npending     1
+
+  # silently drop stdout
+  stdout NULL
+  stderr $LOGSUBDIR/calibration.log
+
+  task.exec
+    book npages calBook -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in calBook
+    # the sequencing in this task set is by the pantasksState (see notes.txt)
+    book getpage calBook 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    # get the current ST: 
+    $RAs = 15*($ST - 1)
+    $RAe = 15*($ST - 2)
+    $DECs = -90.0
+    $DECe = +90.0
+
+    $REGION = "$RAs,$RAe:$DECs,$DECe"
+
+    book setword calBook $pageName pantaskState     RUN
+    book setword calBook $pageName region           $REGION
+
+    # XXX probably need to set the output / log based on WORKDIR...
+    book getword calBook $pageName cal_id      -var ID
+    book getword calBook $pageName dvodb       -var DVODB
+    book getword calBook $pageName dbname      -var DBNAME
+
+    # specify choice of remote host
+    # set a specific DVO host here 
+    if ($PARALLEL)
+      host anyhost
+    else
+      host local
+    end
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # XXX do we modify relphot to loop over the filters?
+    $run = calibrate_dvo.pl --cal_id $ID --dvodb $DVODB --region $REGION
+    add_standard_args run
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit default
+    process_exit calBook $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword calBook $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/camera.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/camera.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/camera.pro	(revision 22322)
@@ -0,0 +1,296 @@
+## camera.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the camera analysis stage
+## these tasks use the book camPendingExp
+
+# test for required global variables
+check.globals
+
+book init camPendingExp
+book init camPendingCleanup
+
+macro camera.status
+  book listbook camPendingExp
+  book listbook camPendingCleanup
+end
+
+macro camera.reset
+  book init camPendingExp
+  book init camPendingCleanup
+end
+
+macro camera.on
+  task camera.exp.load
+    active true
+  end
+  task camera.exp.run
+    active true
+  end
+end
+
+macro camera.off
+  task camera.exp.load
+    active false
+  end
+  task camera.exp.run
+    active false
+  end
+end
+
+macro camera.cleanup.on
+  task camera.cleanup.load
+    active true
+  end
+  task camera.cleanup.run
+    active true
+  end
+end
+
+macro camera.cleanup.off
+  task camera.cleanup.load
+    active false
+  end
+  task camera.cleanup.run
+    active false
+  end
+end
+
+# this variable will cycle through the known database names
+$camera_DB = 0
+
+# select images ready for camera analysis
+# new entries are added to camPendingExp
+# skip already-present entries
+task	       camera.exp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/camera.exp.log
+
+  task.exec
+    $run = camtool -pendingexp
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$camera_DB
+      $run = $run -dbname $DB:$camera_DB
+      $camera_DB ++
+      if ($camera_DB >= $DB:n) set camera_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout camPendingExp -key cam_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook camPendingExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup camPendingExp
+  end
+
+  # default exit status
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the cameraexposure script on pending images
+task	       camera.exp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 10
+
+  task.exec
+    book npages camPendingExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in camPendingExp (pantaskState == INIT)
+    book getpage camPendingExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword camPendingExp $pageName pantaskState RUN
+    book getword camPendingExp $pageName camera -var CAMERA
+    book getword camPendingExp $pageName exp_tag -var EXP_TAG
+    book getword camPendingExp $pageName cam_id -var CAM_ID
+    book getword camPendingExp $pageName workdir -var WORKDIR_TEMPLATE
+    book getword camPendingExp $pageName dvodb  -var DVODB
+    book getword camPendingExp $pageName dbname -var DBNAME
+    book getword camPendingExp $pageName reduction -var REDUCTION
+    book getword camPendingExp $pageName state -var RUN_STATE
+
+    # specify choice of remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    # set the WORKDIR variable
+    set.workdir.by.camera $CAMERA FPA $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    # notes on how this works:
+    # -- raw workdir examples:
+    # file://data/@HOST@.0/gpc1/20080130
+    # neb:///@HOST@-vol0/gpc1/20080130 (need to supply volname?, or are we re-defining this each time?)
+    # -- out workdir examples:
+    # file://data/ipp004.0/gpc1/20080130
+    # neb:///ipp004-vol0/gpc1/20080130
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s/%s.cm.%s" $WORKDIR $EXP_TAG $EXP_TAG $CAM_ID
+
+    stdout $LOGDIR/camera.exp.log
+    stderr $LOGDIR/camera.exp.log
+
+    $run = camera_exp.pl --exp_tag $EXP_TAG --cam_id $CAM_ID --camera $CAMERA --outroot $outroot --redirect-output --run-state $RUN_STATE
+    if ("$REDUCTION" != "NULL")
+      $run = $run --reduction $REDUCTION
+    end
+    if ("$DVODB" != "NULL")
+      $run = $run --dvodb $DVODB
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # success
+  task.exit default
+    process_exit camPendingExp $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword camPendingExp $options:0 pantaskState TIMEOUT
+  end
+end
+
+# this variable will cycle through the known database names
+$camera_cleanup_DB = 0
+
+# select images ready for cam analysis
+# new entries are added to camPendingImfile
+# skip already-present entries
+task	       camera.cleanup.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       false
+
+  stdout NULL
+  stderr $LOGDIR/camera.cleanup.log
+
+  task.exec
+    $run = camtool -pendingcleanuprun
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$camera_cleanup_DB
+      $run = $run -dbname $DB:$camera_cleanup_DB
+      $camera_cleanup_DB ++
+      if ($camera_cleanup_DB >= $DB:n) set camera_cleanup_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout camPendingCleanup -key cam_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook camPendingCleanup
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup camPendingCleanup
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       camera.cleanup.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       false
+
+  task.exec
+    book npages camPendingCleanup -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in camPendingCleanup (pantaskState == INIT)
+    book getpage camPendingCleanup 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword camPendingCleanup $pageName pantaskState RUN
+    book getword camPendingCleanup $pageName camera -var CAMERA
+    book getword camPendingCleanup $pageName state  -var CLEANUP_MODE
+    book getword camPendingCleanup $pageName cam_id -var CAM_ID
+    book getword camPendingCleanup $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and cam (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/camera.cleanup.log
+    stderr $LOGDIR/camera.cleanup.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage camera --stage_id $CAM_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 camPendingCleanup $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword camPendingCleanup $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/chip.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/chip.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/chip.pro	(revision 22322)
@@ -0,0 +1,298 @@
+## chip.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the phase 0 stage
+## these tasks use the book chipPendingImfile
+
+## XXX the cleanup stage probably needs to mimic the chip processing stages, with an exposure and imfile layer
+## can we define the cleanup stages in a separate file derived from this file? 
+
+# test for required global variables
+check.globals
+
+book init chipPendingImfile
+book init chipPendingCleanup
+
+macro chip.status
+  book listbook chipPendingImfile
+  book listbook chipPendingCleanup
+end
+
+macro chip.reset
+  book init chipPendingImfile
+  book init chipPendingCleanup
+end
+
+macro chip.on
+  task chip.imfile.load
+    active true
+  end
+  task chip.imfile.run
+    active true
+  end
+end
+
+macro chip.off
+  task chip.imfile.load
+    active false
+  end
+  task chip.imfile.run
+    active false
+  end
+end
+
+macro chip.cleanup.on
+  task chip.cleanup.load
+    active true
+  end
+  task chip.cleanup.run
+    active true
+  end
+end
+
+macro chip.cleanup.off
+  task chip.cleanup.load
+    active false
+  end
+  task chip.cleanup.run
+    active false
+  end
+end
+
+# this variable will cycle through the known database names
+$chip_DB = 0
+
+# select images ready for chip analysis
+# new entries are added to chipPendingImfile
+# skip already-present entries
+task	       chip.imfile.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/chip.imfile.log
+
+  task.exec
+    $run = chiptool -pendingimfile
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$chip_DB
+      $run = $run -dbname $DB:$chip_DB
+      $chip_DB ++
+      if ($chip_DB >= $DB:n) set chip_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout chipPendingImfile -key chip_id:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook chipPendingImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup chipPendingImfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the chip_imfile.pl script on pending images
+task	       chip.imfile.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    book npages chipPendingImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in chipPendingImfile (pantaskState == INIT)
+    book getpage chipPendingImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword chipPendingImfile $pageName pantaskState RUN
+    book getword chipPendingImfile $pageName camera -var CAMERA
+    book getword chipPendingImfile $pageName exp_id -var EXP_ID
+    book getword chipPendingImfile $pageName exp_tag -var EXP_TAG
+    book getword chipPendingImfile $pageName chip_id -var CHIP_ID
+    book getword chipPendingImfile $pageName workdir -var WORKDIR_TEMPLATE
+    book getword chipPendingImfile $pageName class_id -var CLASS_ID
+    book getword chipPendingImfile $pageName uri -var URI
+    book getword chipPendingImfile $pageName dbname -var DBNAME
+    book getword chipPendingImfile $pageName reduction -var REDUCTION
+    book getword chipPendingImfile $pageName state -var RUN_STATE
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA $CLASS_ID
+
+    # set the WORKDIR variable
+    set.workdir.by.camera $CAMERA $CLASS_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    # notes on how this works:
+    # -- raw workdir examples:
+    # file://data/@HOST@.0/gpc1/20080130
+    # neb:///@HOST@-vol0/gpc1/20080130 (need to supply volname?, or are we re-defining this each time?)
+    # -- out workdir examples:
+    # file://data/ipp004.0/gpc1/20080130
+    # neb:///ipp004-vol0/gpc1/20080130
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s/%s.ch.%s" $WORKDIR $EXP_TAG $EXP_TAG $CHIP_ID
+
+    stdout $LOGDIR/chip.imfile.log
+    stderr $LOGDIR/chip.imfile.log
+
+    $run = chip_imfile.pl --threads @MAX_THREADS@ --exp_id $EXP_ID --chip_id $CHIP_ID --class_id $CLASS_ID --uri $URI --camera $CAMERA --run-state $RUN_STATE --outroot $outroot --redirect-output 
+    if ("$REDUCTION" != "NULL")
+      $run = $run --reduction $REDUCTION
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit chipPendingImfile $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword chipPendingImfile $options:0 pantaskState TIMEOUT
+  end
+end
+
+# this variable will cycle through the known database names
+$chip_cleanup_DB = 0
+
+# select images ready for chip analysis
+# new entries are added to chipPendingImfile
+# skip already-present entries
+task	       chip.cleanup.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       false
+
+  stdout NULL
+  stderr $LOGDIR/chip.cleanup.log
+
+  task.exec
+    $run = chiptool -pendingcleanuprun
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$chip_cleanup_DB
+      $run = $run -dbname $DB:$chip_cleanup_DB
+      $chip_cleanup_DB ++
+      if ($chip_cleanup_DB >= $DB:n) set chip_cleanup_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout chipPendingCleanup -key chip_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook chipPendingCleanup
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup chipPendingCleanup
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       chip.cleanup.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       false
+
+  task.exec
+    book npages chipPendingCleanup -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in chipPendingCleanup (pantaskState == INIT)
+    book getpage chipPendingCleanup 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword chipPendingCleanup $pageName pantaskState RUN
+    book getword chipPendingCleanup $pageName camera -var CAMERA
+    book getword chipPendingCleanup $pageName state -var CLEANUP_MODE
+    book getword chipPendingCleanup $pageName chip_id -var CHIP_ID
+    book getword chipPendingCleanup $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/chip.cleanup.log
+    stderr $LOGDIR/chip.cleanup.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage chip --stage_id $CHIP_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 chipPendingCleanup $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword chipPendingCleanup $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/configure.ac	(revision 22322)
@@ -0,0 +1,14 @@
+AC_PREREQ(2.59)
+
+AC_INIT([ippTasks], [1.1.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([pantasks.pro])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_MAINTAINER_MODE
+
+AC_PROG_INSTALL
+
+AC_CONFIG_FILES([
+  Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ippTasks/detrend.correct.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/detrend.correct.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/detrend.correct.pro	(revision 22322)
@@ -0,0 +1,298 @@
+## detrend.correct.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the detrend correction stage
+## these tasks use the books detPendingCorrectedImfile and detPendingCorrectedExp
+
+# test for required global variables
+check.globals
+
+book init detPendingCorrectedImfile
+book init detPendingCorrectedExp
+
+macro detcorr.reset
+  book init detPendingCorrectedImfile
+  book init detPendingCorrectedExp
+end
+
+macro detcorr.status
+  echo detPendingCorrectedImfile
+  book listbook detPendingCorrectedImfile
+  echo detPendingCorrectedExp
+  book listbook detPendingCorrectedExp
+end
+
+macro detcorr.on
+  task detrend.correct.load
+    active true
+  end
+  task detrend.correct.run
+    active true
+  end
+  task detrend.correctexp.load
+    active true
+  end
+  task detrend.correctexp.run
+    active true
+  end
+end
+
+macro detcorr.off
+  task detrend.correct.load
+    active false
+  end
+  task detrend.correct.run
+    active false
+  end
+  task detrend.correctexp.load
+    active false
+  end
+  task detrend.correctexp.run
+    active false
+  end
+end
+
+
+# these variables will cycle through the known database names
+$detPendingCorrectedImfile_DB = 0
+$detPendingCorrectedExp_DB = 0
+
+# select images ready for copy 
+# new entries are added to detPendingCorrectedImfile
+# compare the new list with the ones already selected
+task	       detrend.correct.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detcorr.load.log
+
+  task.exec
+    $run = dettool -tocorrectedimfile
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detPendingCorrectedImfile_DB
+      $run = $run -dbname $DB:$detPendingCorrectedImfile_DB
+      $detPendingCorrectedImfile_DB ++
+      if ($detPendingCorrectedImfile_DB >= $DB:n) set detPendingCorrectedImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detPendingCorrectedImfile -key det_id:exp_id:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detPendingCorrectedImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detPendingCorrectedImfile
+  end
+
+  # error
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand
+  end
+end
+
+# run detrend_correct_imfile.pl on pending images
+task	       detrend.correct.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detPendingCorrectedImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detPendingCorrectedImfile
+    book getpage detPendingCorrectedImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detPendingCorrectedImfile $pageName pantaskState RUN
+    book getword detPendingCorrectedImfile $pageName det_id    -var DET_ID   
+    book getword detPendingCorrectedImfile $pageName exp_id    -var EXP_ID  
+    book getword detPendingCorrectedImfile $pageName class_id  -var CLASS_ID 
+    book getword detPendingCorrectedImfile $pageName exp_tag   -var EXP_TAG  XXX where does this come from
+    book getword detPendingCorrectedImfile $pageName det_type  -var DET_TYPE  XXX where does this come from
+    book getword detPendingCorrectedImfile $pageName camera    -var CAMERA  XXX where does this come from
+    book getword detPendingCorrectedImfile $pageName uri       -var URI      
+    book getword detPendingCorrectedImfile $pageName corr_id   -var CORR_ID
+    book getword detPendingCorrectedImfile $pageName corr_type -var CORR_TYPE
+    book getword detPendingCorrectedImfile $pageName workdir   -var WORKDIR_TEMPLATE
+    book getword detPendingCorrectedImfile $pageName dbname    -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA $CLASS_ID
+
+    # set the WORKDIR variable
+    set.workdir.by.camera $CAMERA $CLASS_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s.%s.%s/%s/%s.detcorr.%s" $WORKDIR $CAMERA $DET_TYPE $DET_ID $EXP_TAG $EXP_TAG $DET_ID
+
+    stdout $LOGDIR/detcorr.run.log
+    stderr $LOGDIR/detcorr.run.log
+
+    $run = detrend_correct_imfile.pl --det_id $DET_ID --exp_id $EXP_ID --class_id $CLASS_ID --exp_tag $EXP_TAG --input_uri $URI --corr_id $CORR_ID --corr_type $CORR_TYPE --camera $CAMERA --outroot $outroot --redirect-output --verbose
+    if ("$WORKDIR" != "NULL")
+      $run = $run --workdir $WORKDIR
+    end
+    if ("$REDUCTION" != "NULL")
+      $run = $run --reduction $REDUCTION
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create example job options as a demonstration
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit detPendingCorrectedImfile $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detPendingCorrectedImfile $options:0 pantaskState TIMEOUT
+ end
+end
+
+# select images ready for copy 
+# new entries are added to detPendingCorrectedExp
+# compare the new list with the ones already selected
+task	       detrend.correctexp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detcorr.exp.load.log
+
+  task.exec
+    $run = dettool -tocorrectedexp
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detPendingCorrectedExp_DB
+      $run = $run -dbname $DB:$detPendingCorrectedExp_DB
+      $detPendingCorrectedExp_DB ++
+      if ($detPendingCorrectedExp_DB >= $DB:n) set detPendingCorrectedExp_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detPendingCorrectedExp -key det_id:exp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detPendingCorrectedExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detPendingCorrectedExp
+  end
+
+  # error
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# copy new images, sending job to desired host
+task	       detrend.correctexp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detPendingCorrectedExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new exposures in detPendingCorrectedExp
+    book getpage detPendingCorrectedExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detPendingCorrectedExp $pageName pantaskState RUN
+    book getword detPendingCorrectedExp $pageName det_id    -var DET_ID
+    book getword detPendingCorrectedExp $pageName exp_id    -var EXP_ID
+    book getword detPendingCorrectedExp $pageName det_type  -var DET_TYPE
+    book getword detPendingCorrectedExp $pageName camera    -var CAMERA  
+    book getword detPendingCorrectedExp $pageName exp_tag   -var EXP_TAG
+    book getword detPendingCorrectedExp $pageName corr_id   -var CORR_ID
+    book getword detPendingCorrectedExp $pageName corr_type -var CORR_TYPE
+    book getword detPendingCorrectedExp $pageName workdir   -var WORKDIR_TEMPLATE
+    book getword detPendingCorrectedExp $pageName dbname    -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $camera FPA
+
+    # set workdir (interpolate host; see camera.pro for examples)
+    set.workdir.by.camera $CAMERA FPA $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s.%s.%s/%s/%s.detcorr.%s" $WORKDIR $CAMERA $DET_TYPE $DET_ID $EXP_TAG $EXP_TAG $DET_ID
+
+    stdout $LOGDIR/detcorr.exp.run.log
+    stderr $LOGDIR/detcorr.exp.run.log
+
+    $run = detrend_correct_exp.pl --det_id $DET_ID --exp_id $EXP_ID --exp_tag $EXP_TAG --det_type $DET_TYPE --camera $CAMERA --corr_id $CORR_ID --corr_type $CORR_TYPE --camera $CAMERA --redirect-output --verbose
+
+    if ("$WORKDIR" != "NULL")
+      $run = $run --workdir $WORKDIR
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create example job options as a demonstration
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # success
+  task.exit    default
+    process_exit detPendingCorrectedExp $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detPendingCorrectedExp $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/detrend.mkruns.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/detrend.mkruns.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/detrend.mkruns.pro	(revision 22322)
@@ -0,0 +1,99 @@
+## 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
+
+  # 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
+
+  # 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
+
+  # operation timed out?  is the database down?
+  task.exit timeout
+    # send someone email?
+    echo "unable to define a new detrend run"
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/detrend.norm.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/detrend.norm.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/detrend.norm.pro	(revision 22322)
@@ -0,0 +1,781 @@
+## detrend.norm.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the detrend normalization stages
+## these tasks use the books detPendingNormStatImfile detPendingNormImfile detPendingNormExp
+
+# test for required global variables
+check.globals
+
+book init detPendingNormStatImfile
+book init detPendingNormImfile
+book init detPendingNormExp
+
+book init detCleanupNormStatImfile
+book init detCleanupNormImfile
+book init detCleanupNormExp
+
+macro detnorm.reset
+  book init detPendingNormStatImfile
+  book init detPendingNormImfile
+  book init detPendingNormExp
+
+  book init detCleanupNormStatImfile
+  book init detCleanupNormImfile
+  book init detCleanupNormExp
+end
+
+macro detnorm.status
+  book listbook detPendingNormStatImfile
+  book listbook detPendingNormImfile
+  book listbook detPendingNormExp
+
+  book listbook detCleanupNormStatImfile
+  book listbook detCleanupNormImfile
+  book listbook detCleanupNormExp
+end
+
+macro detnorm.on
+  task detrend.norm.load
+    active true
+  end
+  task detrend.norm.run
+    active true
+  end
+  task detrend.normexp.load
+    active true
+  end
+  task detrend.normexp.run
+    active true
+  end
+  task detrend.normstat.load
+    active true
+  end
+  task detrend.normstat.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
+end
+
+macro detnorm.off
+  task detrend.norm.load
+    active false
+  end
+  task detrend.norm.run
+    active false
+  end
+  task detrend.normexp.load
+    active false
+  end
+  task detrend.normexp.run
+    active false
+  end
+  task detrend.normstat.load
+    active false
+  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
+end
+
+# these variables will cycle through the known database names
+$detPendingNormStatImfile_DB = 0
+$detPendingNormImfile_DB = 0
+$detPendingNormExp_DB = 0
+
+$detCleanupNormStatImfile_DB = 0
+$detCleanupNormImfile_DB = 0
+$detCleanupNormExp_DB = 0
+
+# select images ready for copy 
+# new entries are added to detPendingNormStatImfile
+# compare the new list with the ones already selected
+task	       detrend.normstat.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detrend.normstat.log
+
+  task.exec
+    $run = dettool -tonormalizedstat
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detPendingNormStatImfile_DB
+      $run = $run -dbname $DB:$detPendingNormStatImfile_DB
+      $detPendingNormStatImfile_DB ++
+      if ($detPendingNormStatImfile_DB >= $DB:n) set detPendingNormStatImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detPendingNormStatImfile -key det_id:iteration -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detPendingNormStatImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detPendingNormStatImfile
+  end
+
+  # default exit status
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# copy new images, sending job to desired host
+task	       detrend.normstat.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detPendingNormStatImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detPendingNormStatImfile
+    book getpage detPendingNormStatImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detPendingNormStatImfile $pageName pantaskState RUN
+    book getword detPendingNormStatImfile $pageName det_id    -var DET_ID
+    book getword detPendingNormStatImfile $pageName det_type  -var DET_TYPE
+    book getword detPendingNormStatImfile $pageName iteration -var ITERATION
+    book getword detPendingNormStatImfile $pageName camera    -var CAMERA  
+    book getword detPendingNormStatImfile $pageName workdir   -var WORKDIR_TEMPLATE
+    book getword detPendingNormStatImfile $pageName dbname    -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    # set workdir (interpolate host; see camera.pro for examples)
+    set.workdir.by.camera $CAMERA FPA $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s.%s.%s/%s.%s.normstat.%s.%s" $WORKDIR $CAMERA $DET_TYPE $DET_ID $CAMERA $DET_TYPE $DET_ID $ITERATION
+
+    stdout $LOGDIR/detrend.normstat.log
+    stderr $LOGDIR/detrend.normstat.log
+
+    $run = detrend_norm_calc.pl --det_id $DET_ID --iteration $ITERATION --det_type $DET_TYPE --outroot $outroot --redirect-output
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create example job options as a demonstration
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit detPendingNormStatImfile $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detPendingNormStatImfile $options:0 pantaskState TIMEOUT
+  end
+end
+
+# select images ready for copy 
+# new entries are added to detPendingNormImfile
+# compare the new list with the ones already selected
+task	       detrend.norm.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detrend.norm.log
+
+  task.exec
+    $run = dettool -tonormalize
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detPendingNormImfile_DB
+      $run = $run -dbname $DB:$detPendingNormImfile_DB
+      $detPendingNormImfile_DB ++
+      if ($detPendingNormImfile_DB >= $DB:n) set detPendingNormImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detPendingNormImfile -key det_id:iteration:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detPendingNormImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detPendingNormImfile
+  end
+
+  # default exit status
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# copy new images, sending job to desired host
+task	       detrend.norm.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detPendingNormImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detPendingNormImfile
+    book getpage detPendingNormImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detPendingNormImfile $pageName pantaskState RUN
+    book getword detPendingNormImfile $pageName det_type  -var DET_TYPE 
+    book getword detPendingNormImfile $pageName camera    -var CAMERA   
+    book getword detPendingNormImfile $pageName uri       -var URI      
+    book getword detPendingNormImfile $pageName det_id    -var DET_ID   
+    book getword detPendingNormImfile $pageName iteration -var ITERATION     
+    book getword detPendingNormImfile $pageName class_id  -var CLASS_ID 
+    book getword detPendingNormImfile $pageName norm      -var NORM     
+    book getword detPendingNormImfile $pageName workdir   -var WORKDIR_TEMPLATE
+    book getword detPendingNormImfile $pageName dbname    -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA $CLASS_ID
+
+    # set workdir (interpolate host; see camera.pro for examples)
+    set.workdir.by.camera $CAMERA $CLASS_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this stack
+    sprintf outroot "%s/%s.%s.%s/%s.%s.norm.%s.%s" $WORKDIR $CAMERA $DET_TYPE $DET_ID $CAMERA $DET_TYPE $DET_ID $ITERATION
+
+    stdout $LOGDIR/detrend.norm.log
+    stderr $LOGDIR/detrend.norm.log
+
+    $run = detrend_norm_apply.pl --det_id $DET_ID --iteration $ITERATION --class_id $CLASS_ID --value $NORM --input_uri $URI --camera $CAMERA --det_type $DET_TYPE --outroot $outroot --redirect-output --verbose
+    add_standard_args run
+    
+    # save the pageName for future reference below
+    options $pageName
+
+    # create example job options as a demonstration
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit detPendingNormImfile $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detPendingNormImfile $options:0 pantaskState TIMEOUT
+  end
+end
+
+# select images ready for copy 
+# new entries are added to detPendingNormExp
+# compare the new list with the ones already selected
+task	       detrend.normexp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detrend.normexp.log
+
+  task.exec
+    $run = dettool -tonormalizedexp
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detPendingNormExp_DB
+      $run = $run -dbname $DB:$detPendingNormExp_DB
+      $detPendingNormExp_DB ++
+      if ($detPendingNormExp_DB >= $DB:n) set detPendingNormExp_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detPendingNormExp -key det_id:iteration -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detPendingNormExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detPendingNormExp
+  end
+
+  # default exit status
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# copy new images, sending job to desired host
+task	       detrend.normexp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detPendingNormExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detPendingNormExp
+    book getpage detPendingNormExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detPendingNormExp $pageName pantaskState RUN
+    book getword detPendingNormExp $pageName det_id    -var DET_ID  
+    book getword detPendingNormExp $pageName iteration -var ITERATION    
+    book getword detPendingNormExp $pageName det_type  -var DET_TYPE
+    book getword detPendingNormExp $pageName camera    -var CAMERA  
+    book getword detPendingNormExp $pageName workdir   -var WORKDIR_TEMPLATE
+    book getword detPendingNormExp $pageName dbname    -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    # set workdir (interpolate host; see camera.pro for examples)
+    set.workdir.by.camera $CAMERA FPA $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s.%s.%s/%s.%s.normexp.%s.%s" $WORKDIR $CAMERA $DET_TYPE $DET_ID $CAMERA $DET_TYPE $DET_ID $ITERATION
+
+    stdout $LOGDIR/detrend.normexp.log
+    stderr $LOGDIR/detrend.normexp.log
+
+    $run = detrend_norm_exp.pl --det_id $DET_ID --iteration $ITERATION --camera $CAMERA --det_type $DET_TYPE  --outroot $outroot
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create example job options as a demonstration
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit detPendingNormExp $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detPendingNormExp $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
+
+  # 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
+
+  # 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
+
+  # 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
+
+  # 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
+
+  # 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
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detCleanupNormExp $options:0 pantaskState TIMEOUT
+  end
+end
+ 
Index: /tags/sj_tags/sj_root_20080929/ippTasks/detrend.process.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/detrend.process.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/detrend.process.pro	(revision 22322)
@@ -0,0 +1,540 @@
+## 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 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
+
+macro detproc.status
+  echo detPendingProcessedImfile
+  book listbook detPendingProcessedImfile
+  echo detPendingProcessedExp
+  book listbook detPendingProcessedExp
+  echo detCleanupProcessedImfile
+  book listbook detCleanupProcessedImfile
+  echo detCleanupProcessedExp
+  book listbook detCleanupProcessedExp
+end
+
+macro detproc.on
+  task detrend.process.load
+    active true
+  end
+  task detrend.process.run
+    active true
+  end
+  task detrend.processexp.load
+    active true
+  end
+  task detrend.processexp.run
+    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
+
+macro detproc.off
+  task detrend.process.load
+    active false
+  end
+  task detrend.process.run
+    active false
+  end
+  task detrend.processexp.load
+    active false
+  end
+  task detrend.processexp.run
+    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
+
+
+# these variables will cycle through the known database names
+$detPendingProcessedImfile_DB = 0
+$detPendingProcessedExp_DB = 0
+$detCleanupProcessedImfile_DB = 0
+$detCleanupProcessedExp_DB = 0
+
+# select images ready for copy 
+# new entries are added to detPendingProcessedImfile
+# compare the new list with the ones already selected
+task	       detrend.process.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detrend.process.imfile.log
+
+  task.exec
+    $run = dettool -toprocessedimfile
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detPendingProcessedImfile_DB
+      $run = $run -dbname $DB:$detPendingProcessedImfile_DB
+      $detPendingProcessedImfile_DB ++
+      if ($detPendingProcessedImfile_DB >= $DB:n) set detPendingProcessedImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detPendingProcessedImfile -key det_id:exp_id:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detPendingProcessedImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detPendingProcessedImfile
+  end
+
+  # error
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand
+  end
+end
+
+# run detrend_process_imfile.ps on pending images
+task	       detrend.process.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detPendingProcessedImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detPendingProcessedImfile
+    book getpage detPendingProcessedImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detPendingProcessedImfile $pageName pantaskState RUN
+    book getword detPendingProcessedImfile $pageName det_id   -var DET_ID   
+    book getword detPendingProcessedImfile $pageName det_type -var DET_TYPE 
+    book getword detPendingProcessedImfile $pageName exp_id   -var EXP_ID  
+    book getword detPendingProcessedImfile $pageName class_id -var CLASS_ID 
+    book getword detPendingProcessedImfile $pageName exp_tag  -var EXP_TAG  
+    book getword detPendingProcessedImfile $pageName uri      -var URI      
+    book getword detPendingProcessedImfile $pageName camera   -var CAMERA   
+    book getword detPendingProcessedImfile $pageName workdir  -var WORKDIR_TEMPLATE
+    book getword detPendingProcessedImfile $pageName dbname   -var DBNAME
+    book getword detPendingProcessedImfile $pageName reduction -var REDUCTION
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA $CLASS_ID
+
+    # see chip.pro for examples
+    set.workdir.by.camera $CAMERA $CLASS_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s.%s.%s/%s/%s.detproc.%s" $WORKDIR $CAMERA $DET_TYPE $DET_ID $EXP_TAG $EXP_TAG $DET_ID
+
+    stdout $LOGDIR/detrend.process.imfile.log
+    stderr $LOGDIR/detrend.process.imfile.log
+
+    $run = detrend_process_imfile.pl --threads @MAX_THREADS@ --det_id $DET_ID --exp_id $EXP_ID --det_type $DET_TYPE --class_id $CLASS_ID --exp_tag $EXP_TAG --input_uri $URI --camera $CAMERA --outroot $outroot --redirect-output
+    if ("$REDUCTION" != "NULL")
+      $run = $run --reduction $REDUCTION
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create example job options as a demonstration
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit detPendingProcessedImfile $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detPendingProcessedImfile $options:0 pantaskState TIMEOUT
+ end
+end
+
+# select images ready for copy 
+# new entries are added to detPendingProcessedExp
+# compare the new list with the ones already selected
+task	       detrend.processexp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detrend.process.exp.log
+
+  task.exec
+    $run = dettool -toprocessedexp
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detPendingProcessedExp_DB
+      $run = $run -dbname $DB:$detPendingProcessedExp_DB
+      $detPendingProcessedExp_DB ++
+      if ($detPendingProcessedExp_DB >= $DB:n) set detPendingProcessedExp_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detPendingProcessedExp -key det_id:exp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detPendingProcessedExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detPendingProcessedExp
+  end
+
+  # error
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# copy new images, sending job to desired host
+task	       detrend.processexp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detPendingProcessedExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new exposures in detPendingProcessedExp
+    book getpage detPendingProcessedExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detPendingProcessedExp $pageName pantaskState RUN
+    book getword detPendingProcessedExp $pageName det_id    -var DET_ID
+    book getword detPendingProcessedExp $pageName exp_id    -var EXP_ID
+    book getword detPendingProcessedExp $pageName det_type  -var DET_TYPE
+    book getword detPendingProcessedExp $pageName camera    -var CAMERA  
+    book getword detPendingProcessedExp $pageName exp_tag   -var EXP_TAG
+    book getword detPendingProcessedExp $pageName workdir   -var WORKDIR_TEMPLATE
+    book getword detPendingProcessedExp $pageName dbname    -var DBNAME
+    book getword detPendingProcessedExp $pageName reduction -var REDUCTION
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    # set workdir (interpolate host; see camera.pro for examples)
+    set.workdir.by.camera $CAMERA FPA $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s.%s.%s/%s/%s.detproc.%s" $WORKDIR $CAMERA $DET_TYPE $DET_ID $EXP_TAG $EXP_TAG $DET_ID
+
+    stdout $LOGDIR/detrend.process.exp.log
+    stderr $LOGDIR/detrend.process.exp.log
+
+    $run = detrend_process_exp.pl --det_id $DET_ID --exp_id $EXP_ID --exp_tag $EXP_TAG --det_type $DET_TYPE --camera $CAMERA --outroot $outroot --redirect-output --verbose
+    if ("$REDUCTION" != "NULL")
+      $run = $run --reduction $REDUCTION
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create example job options as a demonstration
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # success
+  task.exit    default
+    process_exit detPendingProcessedExp $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detPendingProcessedExp $options:0 pantaskState TIMEOUT
+  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
+
+  # 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
+
+  # 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
+
+  # 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
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detCleanupProcessedExp $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/detrend.reject.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/detrend.reject.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/detrend.reject.pro	(revision 22322)
@@ -0,0 +1,150 @@
+## detrend.reject.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the detrend processing stage
+## these tasks use the book detRejectExp
+
+# test for required global variables
+check.globals
+
+book init detRejectExp
+
+macro detreject.reset
+  book init detRejectExp
+end
+
+macro detreject.status
+  book listbook detRejectExp
+end
+
+macro detreject.on
+  task detrend.reject.load
+    active true
+  end
+  task detrend.reject.run
+    active true
+  end
+end
+
+macro detreject.off
+  task detrend.reject.load
+    active false
+  end
+  task detrend.reject.run
+    active false
+  end
+end
+
+# this variable will cycle through the known database names
+$detRejectExp_DB = 0
+
+# select images ready for copy 
+# new entries are added to detRejectExp
+# compare the new list with the ones already selected
+task	       detrend.reject.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detrend.reject.log
+
+  task.exec
+    $run = dettool -todetrunsummary
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detRejectExp_DB
+      $run = $run -dbname $DB:$detRejectExp_DB
+      $detRejectExp_DB ++
+      if ($detRejectExp_DB >= $DB:n) set detRejectExp_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detRejectExp -key det_id:iteration -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detRejectExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detRejectExp
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand failure
+  end
+end
+
+# copy new images, sending job to desired host
+task	       detrend.reject.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detRejectExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detRejectExp
+    book getpage detRejectExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detRejectExp $pageName pantaskState RUN
+    book getword detRejectExp $pageName det_id    -var DET_ID   
+    book getword detRejectExp $pageName iteration -var ITERATION     
+    book getword detRejectExp $pageName det_type  -var DET_TYPE 
+    book getword detRejectExp $pageName mode      -var MODE     
+    book getword detRejectExp $pageName camera    -var CAMERA   
+    book getword detRejectExp $pageName workdir   -var WORKDIR_TEMPLATE
+    book getword detRejectExp $pageName dbname    -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    # set workdir (interpolate host; see camera.pro for examples)
+    set.workdir.by.camera $CAMERA FPA $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s.%s.%s/%s.%s.%s.%s.detreject" $WORKDIR $CAMERA $DET_TYPE $DET_ID $CAMERA $DET_TYPE $DET_ID $ITERATION
+
+    stdout $LOGDIR/detrend.reject.log
+    stderr $LOGDIR/detrend.reject.log
+
+    $run = detrend_reject_exp.pl --det_id $DET_ID --iteration $ITERATION --det_type $DET_TYPE --camera $CAMERA --outroot $outroot --redirect-output
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create example job options as a demonstration
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+ end
+
+  # default exit status
+  task.exit    default
+    process_exit detRejectExp $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand failure
+    book setword detRejectExp $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/detrend.resid.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/detrend.resid.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/detrend.resid.pro	(revision 22322)
@@ -0,0 +1,543 @@
+## detrend.resid.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the detrend processing stage
+## these tasks use the books detPendingResidImfile and detPendingResidExp
+
+# test for required global variables
+check.globals
+
+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
+
+macro detresid.status
+  book listbook detPendingResidImfile
+  book listbook detPendingResidExp
+  book listbook detCleanupResidImfile
+  book listbook detCleanupResidExp
+end
+
+macro detresid.on
+  task detrend.resid.load
+    active true
+  end
+  task detrend.resid.run
+    active true
+  end
+  task detrend.residexp.load
+    active true
+  end
+  task detrend.residexp.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 detresid.off
+  task detrend.resid.load
+    active false
+  end
+  task detrend.resid.run
+    active false
+  end
+  task detrend.residexp.load
+    active false
+  end
+  task detrend.residexp.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
+$detPendingResidImfile_DB = 0
+$detPendingResidExp_DB = 0
+$detCleanupResidImfile_DB = 0
+$detCleanupResidExp_DB = 0
+
+# select images ready for copy 
+# new entries are added to detPendingResidImfile
+# compare the new list with the ones already selected
+task	       detrend.resid.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detrend.resid.imfile.log
+
+  task.exec
+    $run = dettool -toresidimfile
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detPendingResidImfile_DB
+      $run = $run -dbname $DB:$detPendingResidImfile_DB
+      $detPendingResidImfile_DB ++
+      if ($detPendingResidImfile_DB >= $DB:n) set detPendingResidImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detPendingResidImfile -key det_id:iteration:exp_id:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detPendingResidImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detPendingResidImfile
+  end
+
+  # default exit status
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# copy new images, sending job to desired host
+task	       detrend.resid.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detPendingResidImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detPendingResidImfile
+    book getpage detPendingResidImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detPendingResidImfile $pageName pantaskState RUN
+    book getword detPendingResidImfile $pageName det_id     -var DET_ID   
+    book getword detPendingResidImfile $pageName exp_id     -var EXP_ID  
+    book getword detPendingResidImfile $pageName iteration  -var ITERATION     
+    book getword detPendingResidImfile $pageName det_type   -var DET_TYPE 
+    book getword detPendingResidImfile $pageName mode       -var MODE     
+    book getword detPendingResidImfile $pageName exp_tag    -var EXP_TAG  
+    book getword detPendingResidImfile $pageName class_id   -var CLASS_ID 
+    book getword detPendingResidImfile $pageName uri        -var URI      
+    book getword detPendingResidImfile $pageName det_uri    -var DET_URI  
+    book getword detPendingResidImfile $pageName ref_det_id -var REF_DET_ID   
+    book getword detPendingResidImfile $pageName ref_iter   -var REF_ITER
+    book getword detPendingResidImfile $pageName camera     -var CAMERA   
+    book getword detPendingResidImfile $pageName workdir    -var WORKDIR_TEMPLATE
+    book getword detPendingResidImfile $pageName dbname     -var DBNAME
+    book getword detPendingResidImfile $pageName reduction  -var REDUCTION
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA $CLASS_ID
+
+    # set workdir (interpolate host; see camera.pro for examples)
+    set.workdir.by.camera $CAMERA $CLASS_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s.%s.%s/%s/%s.detresid.%s.%s" $WORKDIR $CAMERA $DET_TYPE $DET_ID $EXP_TAG $EXP_TAG $DET_ID $ITERATION
+
+    stdout $LOGDIR/detrend.resid.imfile.log
+    stderr $LOGDIR/detrend.resid.imfile.log
+
+    $run = detrend_resid_imfile.pl --threads @MAX_THREADS@ --det_id $DET_ID --iteration $ITERATION --ref_det_id $REF_DET_ID --ref_iter $REF_ITER --exp_id $EXP_ID --exp_tag $EXP_TAG --class_id $CLASS_ID --det_type $DET_TYPE --detrend $DET_URI --input_uri $URI --camera $CAMERA --mode $MODE --outroot $outroot --redirect-output --verbose
+
+    if ("$REDUCTION" != "NULL")
+      $run = $run --reduction $REDUCTION
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create command
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit detPendingResidImfile $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detPendingResidImfile $options:0 pantaskState TIMEOUT
+  end
+end
+
+# select images ready for copy 
+# new entries are added to detPendingResidExp
+# compare the new list with the ones already selected
+task	       detrend.residexp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detrend.resid.exp.log
+
+  task.exec
+    $run = dettool -toresidexp
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detPendingResidExp_DB
+      $run = $run -dbname $DB:$detPendingResidExp_DB
+      $detPendingResidExp_DB ++
+      if ($detPendingResidExp_DB >= $DB:n) set detPendingResidExp_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detPendingResidExp -key det_id:iteration:exp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detPendingResidExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detPendingResidExp
+  end
+
+  # default exit status
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# copy new images, sending job to desired host
+task	       detrend.residexp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detPendingResidExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detPendingResidExp
+    book getpage detPendingResidExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detPendingResidExp $pageName pantaskState RUN
+    book getword detPendingResidExp $pageName det_id    -var DET_ID  
+    book getword detPendingResidExp $pageName exp_id    -var EXP_ID 
+    book getword detPendingResidExp $pageName iteration -var ITERATION    
+    book getword detPendingResidExp $pageName det_type  -var DET_TYPE
+    book getword detPendingResidExp $pageName mode      -var MODE    
+    book getword detPendingResidExp $pageName exp_tag   -var EXP_TAG 
+    book getword detPendingResidExp $pageName include   -var INCLUDE 
+    book getword detPendingResidExp $pageName camera    -var CAMERA  
+    book getword detPendingResidExp $pageName workdir   -var WORKDIR_TEMPLATE
+    book getword detPendingResidExp $pageName dbname    -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    # set workdir (interpolate host; see camera.pro for examples)
+    set.workdir.by.camera $CAMERA FPA $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s.%s.%s/%s/%s.detresid.%s.%s" $WORKDIR $CAMERA $DET_TYPE $DET_ID $EXP_TAG $EXP_TAG $DET_ID $ITERATION
+
+    stdout $LOGDIR/detrend.resid.exp.log
+    stderr $LOGDIR/detrend.resid.exp.log
+
+    $run = detrend_resid_exp.pl --det_id $DET_ID --iteration $ITERATION --exp_id $EXP_ID --exp_tag $EXP_TAG --det_type $DET_TYPE --camera $CAMERA --outroot $outroot --redirect-output --verbose
+
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create example job options as a demonstration
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit detPendingResidExp $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detPendingResidExp $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
+
+  # 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
+
+  # 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
+
+  # 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
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detCleanupResidExp $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/detrend.stack.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/detrend.stack.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/detrend.stack.pro	(revision 22322)
@@ -0,0 +1,280 @@
+## detrend.stack.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the detrend stacking stage
+## these tasks use the book detPendingStackedImfile
+
+# test for required global variables
+check.globals
+
+book init detPendingStackedImfile
+book init detCleanupStackedImfile
+
+macro detstack.reset
+  book init detPendingStackedImfile
+  book init detCleanupStackedImfile
+end
+
+macro detstack.status
+  echo detPendingStackedImfile
+  book listbook detPendingStackedImfile
+  echo detCleanupStackedImfile
+  book listbook detCleanupStackedImfile
+end
+
+macro detstack.on
+  task detrend.stack.load
+    active true
+  end
+  task detrend.stack.run
+    active true
+  end
+  task detrend.cleanup.stack.load
+    active true
+  end
+  task detrend.cleanup.stack.run
+    active true
+  end
+end
+
+macro detstack.off
+  task detrend.stack.load
+    active false
+  end
+  task detrend.stack.run
+    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
+# new entries are added to detPendingStackedImfile
+# compare the new list with the ones already selected
+task	       detrend.stack.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/detrend.stack.log
+
+  task.exec
+    $run = dettool -tostacked
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detPendingStackedImfile_DB
+      $run = $run -dbname $DB:$detPendingStackedImfile_DB
+      $detPendingStackedImfile_DB ++
+      if ($detPendingStackedImfile_DB >= $DB:n) set detPendingStackedImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detPendingStackedImfile -key det_id:iteration:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detPendingStackedImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detPendingStackedImfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# copy new images, sending job to desired host
+task	       detrend.stack.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages detPendingStackedImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detPendingStackedImfile
+    book getpage detPendingStackedImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detPendingStackedImfile $pageName pantaskState RUN
+    book getword detPendingStackedImfile $pageName det_id    -var DET_ID
+    book getword detPendingStackedImfile $pageName iteration -var ITERATION
+    book getword detPendingStackedImfile $pageName det_type  -var DET_TYPE
+    book getword detPendingStackedImfile $pageName class_id  -var CLASS_ID
+    book getword detPendingStackedImfile $pageName camera    -var CAMERA  
+    book getword detPendingStackedImfile $pageName workdir   -var WORKDIR_TEMPLATE
+    book getword detPendingStackedImfile $pageName dbname    -var DBNAME
+    book getword detPendingStackedImfile $pageName reduction -var REDUCTION
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA $CLASS_ID
+
+    # set workdir (interpolate host; see camera.pro for examples)
+    set.workdir.by.camera $CAMERA FPA $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure
+    sprintf outroot "%s/%s.%s.%s/%s.%s.%s.%s" $WORKDIR $CAMERA $DET_TYPE $DET_ID $CAMERA $DET_TYPE $DET_ID $ITERATION
+
+    stdout $LOGDIR/detrend.stack.log
+    stderr $LOGDIR/detrend.stack.log
+
+    $run = detrend_stack.pl --threads @MAX_THREADS@ --det_id $DET_ID --iteration $ITERATION --class_id $CLASS_ID --det_type $DET_TYPE --camera $CAMERA --outroot $outroot --redirect-output
+    if ("$REDUCTION" != "NULL")
+      $run = $run --reduction $REDUCTION
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit detPendingStackedImfile $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detPendingStackedImfile $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
+
+  # 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
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detCleanupStackedImfile $options:0 pantaskState TIMEOUT
+  end
+end
+ 
Index: /tags/sj_tags/sj_root_20080929/ippTasks/diff.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/diff.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/diff.pro	(revision 22322)
@@ -0,0 +1,288 @@
+## diff.pro : image difference analysis : -*- sh -*-
+
+## This file contains panTasks definitions for performing the image differencing.
+## After a difference (with associated diff_id) is defined, the difference is performed
+## (tasks in diffSkyfile).
+
+# test for required global variables
+check.globals
+
+### Initialise the books containing the tasks to do
+book init diffSkyfile
+book init diffCleanup
+
+### Database lists
+$diffSkycell_DB = 0
+$diffCleanup_DB = 0
+
+### Check status of diffing tasks
+macro diff.status
+  book listbook diffSkyfile
+  book listbook diffCleanup
+end
+
+### Reset diffing tasks
+macro diff.reset
+  book init diffSkyfile
+  book init diffCleanup
+end
+
+### Turn diffing tasks on
+macro diff.on
+  task diff.skycell.load
+    active true
+  end
+  task diff.skycell.run
+    active true
+  end
+end
+
+### Turn diffing tasks off
+macro diff.off
+  task diff.skycell.load
+    active false
+  end
+  task diff.skycell.run
+    active false
+  end
+end
+
+macro diff.cleanup.on
+  task diff.cleanup.load
+    active true
+  end
+  task diff.cleanup.run
+    active true
+  end
+end
+
+macro diff.cleanup.off
+  task diff.cleanup.load
+    active false
+  end
+  task diff.cleanup.run
+    active false
+  end
+end
+
+
+### Load tasks for doing the differences
+### Tasks are loaded into diffSkyfile.
+task	       diff.skycell.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/diff.skycell.log
+
+  task.exec
+    $run = difftool -todiffskyfile
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$diffSkycell_DB
+      $run = $run -dbname $DB:$diffSkycell_DB
+      $diffSkycell_DB ++
+      if ($diffSkycell_DB >= $DB:n) set diffSkycell_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout diffSkyfile -key diff_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook diffSkyfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup diffSkyfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+### Run tasks for calculating the diff overlaps
+### Tasks are taken from diffSkyfile.
+task	       diff.skycell.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    book npages diffSkyfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in diffSkyfile (pantaskState == INIT)
+    book getpage diffSkyfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword diffSkyfile $pageName pantaskState RUN
+    book getword diffSkyfile $pageName diff_id -var DIFF_ID
+    book getword diffSkyfile $pageName tess_id -var TESS_DIR
+    book getword diffSkyfile $pageName skycell_id -var SKYCELL_ID
+    book getword diffSkyfile $pageName camera -var CAMERA
+    book getword diffSkyfile $pageName workdir -var WORKDIR_TEMPLATE
+    book getword diffSkyfile $pageName dbname -var DBNAME
+
+    # set the host and workdir based on the skycell hash
+    set.host.for.skycell $SKYCELL_ID
+    set.workdir.by.skycell $SKYCELL_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    # XXX old code:
+    # host anyhost
+    # $WORKDIR = $WORKDIR_TEMPLATE
+
+    basename $TESS_DIR -var TESS_ID
+    sprintf outroot "%s/%s/%s/%s.%s.dif.%s" $WORKDIR $TESS_ID $SKYCELL_ID $TESS_ID $SKYCELL_ID $DIFF_ID
+
+    stdout $LOGDIR/diff.skycell.log
+    stderr $LOGDIR/diff.skycell.log
+
+    $run = diff_skycell.pl --threads @MAX_THREADS@ --diff_id $DIFF_ID --outroot $outroot --redirect-output
+    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 diffSkyfile $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword diffSkyfile $options:0 pantaskState TIMEOUT
+  end
+end
+
+# select images ready for diff analysis
+# new entries are added to diffPendingImfile
+# skip already-present entries
+task	       diff.cleanup.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       false
+
+  stdout NULL
+  stderr $LOGDIR/diff.cleanup.log
+
+  task.exec
+    $run = difftool -pendingcleanuprun
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$diffCleanup_DB
+      $run = $run -dbname $DB:$diffCleanup_DB
+      $diffCleanup_DB ++
+      if ($diffCleanup_DB >= $DB:n) set diffCleanup_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout diffCleanup -key diff_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook diffCleanup
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup diffCleanup
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       diff.cleanup.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       false
+
+  task.exec
+    book npages diffCleanup -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in diffCleanup (pantaskState == INIT)
+    book getpage diffCleanup 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword diffCleanup $pageName pantaskState RUN
+    book getword diffCleanup $pageName camera -var CAMERA
+    book getword diffCleanup $pageName state -var CLEANUP_MODE
+    book getword diffCleanup $pageName diff_id -var DIFF_ID
+    book getword diffCleanup $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and diff (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/diff.cleanup.log
+    stderr $LOGDIR/diff.cleanup.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage diff --stage_id $DIFF_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 diffCleanup $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword diffCleanup $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/fake.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/fake.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/fake.pro	(revision 22322)
@@ -0,0 +1,288 @@
+## fake.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the phase 0 stage
+## these tasks use the book fakePendingImfile
+
+# test for required global variables
+check.globals
+
+book init fakePendingImfile
+book init fakePendingCleanup
+
+macro fake.status
+  book listbook fakePendingImfile
+  book listbook fakePendingCleanup
+end
+
+macro fake.reset
+  book init fakePendingImfile
+  book init fakePendingCleanup
+end
+
+macro fake.on
+  task fake.imfile.load
+    active true
+  end
+  task fake.imfile.run
+    active true
+  end
+end
+
+macro fake.off
+  task fake.imfile.load
+    active false
+  end
+  task fake.imfile.run
+    active false
+  end
+end
+
+macro fake.cleanup.on
+  task fake.cleanup.load
+    active true
+  end
+  task fake.cleanup.run
+    active true
+  end
+end
+
+macro fake.cleanup.off
+  task fake.cleanup.load
+    active false
+  end
+  task fake.cleanup.run
+    active false
+  end
+end
+
+# this variable will cycle through the known database names
+$fakeImfile_DB = 0
+
+# select images ready for fake analysis
+# new entries are added to fakePendingImfile
+# skip already-present entries
+task	       fake.imfile.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/fake.imfile.log
+
+  task.exec
+    $run = faketool -pendingimfile
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$fakeImfile_DB
+      $run = $run -dbname $DB:$fakeImfile_DB
+      $fakeImfile_DB ++
+      if ($fakeImfile_DB >= $DB:n) set fakeImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout fakePendingImfile -key fake_id:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook fakePendingImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup fakePendingImfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the fake_imfile.pl script on pending images
+task	       fake.imfile.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    book npages fakePendingImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in fakePendingImfile (pantaskState == INIT)
+    book getpage fakePendingImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword fakePendingImfile $pageName pantaskState RUN
+    book getword fakePendingImfile $pageName camera -var CAMERA
+    book getword fakePendingImfile $pageName exp_id -var EXP_ID
+    book getword fakePendingImfile $pageName exp_tag -var EXP_TAG
+    book getword fakePendingImfile $pageName fake_id -var FAKE_ID
+    book getword fakePendingImfile $pageName workdir -var WORKDIR_TEMPLATE
+    book getword fakePendingImfile $pageName class_id -var CLASS_ID
+    book getword fakePendingImfile $pageName chip_path_base -var CHIPROOT
+    book getword fakePendingImfile $pageName cam_path_base -var CAMROOT
+    book getword fakePendingImfile $pageName dbname -var DBNAME
+    book getword fakePendingImfile $pageName reduction -var REDUCTION
+
+    # specify choice of local or remote host based on camera and fake (class_id)
+    set.host.for.camera $CAMERA $CLASS_ID
+
+    # set the WORKDIR variable
+    set.workdir.by.camera $CAMERA $CLASS_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf outroot "%s/%s/%s.fk.%s" $WORKDIR $EXP_TAG $EXP_TAG $FAKE_ID
+
+    stderr $LOGDIR/fake.imfile.log
+    stderr $LOGDIR/fake.imfile.log
+
+    $run = fake_imfile.pl --exp_id $EXP_ID --fake_id $FAKE_ID --class_id $CLASS_ID --chiproot=$CHIPROOT --camroot=$CAMROOT --camera $CAMERA --outroot $outroot
+    if ("$REDUCTION" != "NULL")
+      $run = $run --reduction $REDUCTION
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit fakePendingImfile $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword fakePendingImfile $options:0 pantaskState TIMEOUT
+  end
+end
+
+# this variable will cycle through the known database names
+$fake_cleanup_DB = 0
+
+# select images ready for fake analysis
+# new entries are added to fakePendingImfile
+# skip already-present entries
+task	       fake.cleanup.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       false
+
+  stdout NULL
+  stderr $LOGDIR/fake.cleanup.log
+
+  task.exec
+    $run = faketool -pendingcleanuprun
+    if ($DB:n == 0)
+      option DEFAULT
+      command 
+    else
+      # save the DB name for the exit tasks
+      option $DB:$fake_cleanup_DB
+      $run = $run -dbname $DB:$fake_cleanup_DB
+      $fake_cleanup_DB ++
+      if ($fake_cleanup_DB >= $DB:n) set fake_cleanup_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout fakePendingCleanup -key fake_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook fakePendingCleanup
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup fakePendingCleanup
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       fake.cleanup.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       false
+
+  task.exec
+    book npages fakePendingCleanup -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in fakePendingCleanup (pantaskState == INIT)
+    book getpage fakePendingCleanup 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword fakePendingCleanup $pageName pantaskState RUN
+    book getword fakePendingCleanup $pageName camera -var CAMERA
+    book getword fakePendingCleanup $pageName state -var CLEANUP_MODE
+    book getword fakePendingCleanup $pageName fake_id -var FAKE_ID
+    book getword fakePendingCleanup $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and fake (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/fake.cleanup.log
+    stderr $LOGDIR/fake.cleanup.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage fake --stage_id $FAKE_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 fakePendingCleanup $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword fakePendingCleanup $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/flatcorr.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/flatcorr.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/flatcorr.pro	(revision 22322)
@@ -0,0 +1,216 @@
+## flatcorr.pro : tasks to run flat-field correction : -*- sh -*-
+## this file contains the tasks for running the flatcorr stage
+## these tasks use the book 'flatcorrBook'
+
+# test for required global variables
+check.globals
+
+book init flatcorrBook
+
+macro flatcorr.reset
+  book init flatcorrBook
+end
+
+macro flatcorr.status
+  book listbook flatcorrBook
+end
+
+macro flatcorr.on
+  task flatcorr.load
+    active true
+  end
+  task flatcorr.run
+    active true
+  end
+end
+
+macro flatcorr.off
+  task flatcorr.load
+    active false
+  end
+  task flatcorr.run
+    active false
+  end
+end
+
+# these variables will cycle through the known ippdb database names
+$flatcorr_addcamera_DB = 0
+$flatcorr_pendingprocess_DB = 0
+
+# a flat-field correction run is initiated (manually) with a command like:
+# flatcorr -definebyquery -filter r ...
+# the result is a flatcorrRun entry, a set of chipRun entries, a linking list in flatcorrChipList.  
+# the chipRun entries are all set to stop at the "chip" stage.
+
+# we also can define a run with:
+# flatcorr -definerun 
+# flatcorr -addchip ...
+
+# as the chip analysis progresses, we need to occasionally migrate the
+# completed chips to the camera stage by calling:
+# flatcorr -addcamera 
+
+# we wait for the completed chip and camera analysis and search for
+# completed processing analysis with:
+# flatcorr -pendingprocess
+
+# these define arguments to an analysis run.  successful completion of
+# the analysis is marked with:
+# flatcorr -addprocess
+# and failures with:
+# flatcorr -addprocess -fault N
+
+# migrate complete flatcorr chips to the camera stage analysis
+task	       flatcorr.addcamera
+  host         local
+
+  # check the list of pending flatcorr runs
+  periods      -poll 10
+  periods      -exec 120
+  periods      -timeout 60
+  npending 1
+
+  # define the command (does not depend on previous queries)
+  if ($DB:n == 0)
+    command flatcorr -addcamera
+  else
+    # save the DB name for the exit tasks
+    # note that this DB name refers to the ippdb, not the dvodb
+    option $DB:$flatcorr_addcamera_DB
+    command flatcorr -addcamera -dbname $DB:$flatcorr_addcamera_DB
+    $flatcorr_addcamera_DB ++
+    if ($flatcorr_addcamera_DB >= $DB:n) set flatcorr_addcamera_DB = 0
+  end
+
+  # silently drop stdout
+  stdout NULL
+  stderr $LOGDIR/flatcorr.log
+
+  # success (no action required)
+  task.exit $EXIT_SUCCESS
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# create new flatcorr entries for the currently known DVO databases
+# run this multiple times once an hour - on pass for each db
+task	       flatcorr.load
+  host         local
+
+  # check the list of pending flatcorr runs
+  periods      -poll 10
+  periods      -exec 900
+  periods      -timeout 60
+  npending 1
+
+  # define the command (does not depend on previous queries)
+  $run = flatcorr -pendingprocess
+  if ($DB:n != 0)
+    # save the DB name for the exit tasks
+    option $DB:$flatcorr_pendingprocess_DB
+    $run = $run -dbname $DB:$flatcorr_pendingprocess_DB
+    $flatcorr_pendingprocess_DB ++
+    if ($flatcorr_pendingprocess_DB >= $DB:n) set flatcorr_pendingprocess_DB = 0
+  end
+  add_poll_args run
+  command $run
+
+  # silently drop stdout
+  stdout NULL
+  stderr $LOGDIR/flatcorr.log
+
+  # success
+  task.exit $EXIT_SUCCESS
+    ipptool2book stdout flatcorrBook -key corr_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook flatcorrBook
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup flatcorrBook
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# check for flatcorr runs which are ready to go
+task	       flatcorr.run
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 60
+
+  # silently drop stdout
+  stdout $LOGDIR/flatcorr.log
+  stderr $LOGDIR/flatcorr.log
+
+  task.exec
+    book npages flatcorrBook -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new entries in flatcorrBook
+    book getpage flatcorrBook 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword flatcorrBook $pageName pantaskState     RUN
+
+    # XXX probably need to set the output / log based on WORKDIR...
+    book getword flatcorrBook $pageName corr_id     -var ID
+    book getword flatcorrBook $pageName dvodb       -var DVODB
+    book getword flatcorrBook $pageName region      -var REGION
+    book getword flatcorrBook $pageName filter      -var FILTER
+    book getword flatcorrBook $pageName dbname      -var DBNAME
+    book getword flatcorrBook $pageName workdir     -var WORKDIR
+
+    # specify choice of remote host
+    # set a specific DVO host here 
+    if ($PARALLEL)
+      host anyhost
+    else
+      host local
+    end
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # XXX get these arguments right
+    $run = flatcorr_proc.pl --corr_id $ID --dvodb $DVODB --region $REGION --filter $FILTER
+    add_standard_args run
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit default
+    process_exit flatcorrBook $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword flatcorrBook $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/ipphosts.manoa.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/ipphosts.manoa.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/ipphosts.manoa.config	(revision 22322)
@@ -0,0 +1,19 @@
+## basic chiphost table: use the defaults for everything
+
+chiphosts MULTI
+
+chiphosts METADATA
+  camera STR MEGACAM
+END
+
+chiphosts METADATA
+  camera STR CFH12K
+END
+
+chiphosts METADATA
+  camera STR GPC1
+END
+
+chiphosts METADATA
+  camera STR gpc1
+END
Index: /tags/sj_tags/sj_root_20080929/ippTasks/ipphosts.mhpcc.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/ipphosts.mhpcc.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/ipphosts.mhpcc.config	(revision 22322)
@@ -0,0 +1,193 @@
+ipphosts MULTI
+
+ipphosts METADATA
+  camera STR skycell
+  count S32  8
+  sky00 STR  ipp006
+  sky01 STR  ipp007
+  sky02 STR  ipp008 
+  sky03 STR  ipp009
+  sky04 STR  ipp010
+  sky05 STR  ipp011
+  sky06 STR  ipp020
+  sky07 STR  ipp021
+END
+
+ipphosts METADATA
+  camera STR MEGACAM
+  ccd00 STR po00
+  ccd01 STR po01
+  ccd02 STR po02
+  ccd03 STR po03
+  ccd04 STR po04
+  ccd05 STR po05
+  ccd06 STR po06
+  ccd07 STR po07
+  ccd08 STR po08
+  ccd09 STR po09
+END
+
+ipphosts METADATA
+  camera STR CFH12K
+  ccd00 STR po10
+  ccd01 STR po11
+  ccd02 STR po12
+END
+
+ipphosts METADATA
+  camera STR GPC1
+
+  XY01  STR  ipp014
+  XY02  STR  ipp014
+  XY03  STR  ipp014
+  XY04  STR  ipp014
+
+  XY05  STR  ipp023
+  XY06  STR  ipp023
+  XY10  STR  ipp023
+  XY11  STR  ipp023
+
+  XY12  STR  ipp024
+  XY13  STR  ipp024
+  XY14  STR  ipp024
+  XY15  STR  ipp024
+
+  XY16  STR  ipp026
+  XY17  STR  ipp026
+  XY20  STR  ipp026
+  XY21  STR  ipp026
+
+  XY22  STR  ipp027
+  XY23  STR  ipp027
+  XY24  STR  ipp027 
+  XY25  STR  ipp027
+
+  XY26  STR  ipp028
+  XY27  STR  ipp028
+  XY30  STR  ipp028
+  XY31  STR  ipp028
+
+  XY32  STR  ipp029
+  XY33  STR  ipp029
+  XY34  STR  ipp029
+  XY35  STR  ipp029
+
+  XY36  STR  ipp030
+  XY37  STR  ipp030
+  XY40  STR  ipp030
+  XY41  STR  ipp030
+
+  XY42  STR  ipp031
+  XY43  STR  ipp031
+  XY44  STR  ipp031
+  XY45  STR  ipp031
+
+  XY46  STR  ipp032
+  XY47  STR  ipp032
+  XY50  STR  ipp032
+  XY51  STR  ipp032
+
+  XY52  STR  ipp033
+  XY53  STR  ipp033
+  XY54  STR  ipp033
+  XY55  S0R  ipp033
+
+  XY56  STR  ipp034
+  XY57  STR  ipp034
+  XY60  STR  ipp034
+  XY61  STR  ipp034
+
+  XY62  STR  ipp035
+  XY63  STR  ipp035
+  XY64  STR  ipp035
+  XY65  STR  ipp035
+
+  XY66  STR  ipp036
+  XY67  STR  ipp036
+  XY71  STR  ipp036
+  XY72  STR  ipp036
+
+  XY73  STR  ipp015
+  XY74  STR  ipp015
+  XY75  STR  ipp015
+  XY76  STR  ipp015
+END
+
+ipphosts METADATA
+  camera STR gpc1
+
+  ota01  STR  ipp014
+  ota02  STR  ipp014
+  ota03  STR  ipp014
+  ota04  STR  ipp014
+
+  ota05  STR  ipp023
+  ota06  STR  ipp023
+  ota10  STR  ipp023
+  ota11  STR  ipp023
+
+  ota12  STR  ipp024
+  ota13  STR  ipp024
+  ota14  STR  ipp024
+  ota15  STR  ipp024
+
+  ota16  STR  ipp026
+  ota17  STR  ipp026
+  ota20  STR  ipp026
+  ota21  STR  ipp026
+
+  ota22  STR  ipp027
+  ota23  STR  ipp027
+  ota24  STR  ipp027 
+  ota25  STR  ipp027
+
+  ota26  STR  ipp028
+  ota27  STR  ipp028
+  ota30  STR  ipp028
+  ota31  STR  ipp028
+
+  ota32  STR  ipp029
+  ota33  STR  ipp029
+  ota34  STR  ipp029
+  ota35  STR  ipp029
+
+  ota36  STR  ipp030
+  ota37  STR  ipp030
+  ota40  STR  ipp030
+  ota41  STR  ipp030
+
+  ota42  STR  ipp031
+  ota43  STR  ipp031
+  ota44  STR  ipp031
+  ota45  STR  ipp031
+
+  ota46  STR  ipp032
+  ota47  STR  ipp032
+  ota50  STR  ipp032
+  ota51  STR  ipp032
+
+  ota52  STR  ipp033
+  ota53  STR  ipp033
+  ota54  STR  ipp033
+  ota55  S0R  ipp033
+
+  ota56  STR  ipp034
+  ota57  STR  ipp034
+  ota60  STR  ipp034
+  ota61  STR  ipp034
+
+  ota62  STR  ipp035
+  ota63  STR  ipp035
+  ota64  STR  ipp035
+  ota65  STR  ipp035
+
+  ota66  STR  ipp036
+  ota67  STR  ipp036
+  ota71  STR  ipp036
+  ota72  STR  ipp036
+
+  ota73  STR  ipp015
+  ota74  STR  ipp015
+  ota75  STR  ipp015
+  ota76  STR  ipp015
+END
Index: /tags/sj_tags/sj_root_20080929/ippTasks/isp.det
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/isp.det	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/isp.det	(revision 22322)
@@ -0,0 +1,91 @@
+
+detRunDef MULTI
+
+detRunDef METADATA
+  detRunName   STR BIAS
+  prereq       STR NONE
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/bias -det_type bias -select_exp_type bias -select_dateobs_begin 2007-01-13T00:00:00"
+  dbname       STR isp
+  pantaskState STR DONE
+  detRunID     STR 1
+END
+
+detRunDef METADATA
+  detRunName   STR DARK-warm
+  prereq       STR BIAS
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/dark -det_type dark -select_exp_type dark -select_exp_time_min 0.5 -select_ccd_temp_min -27.5 -select_ccd_temp_max -24.0 -ccd_temp_min -27.5 -ccd_temp_max -24.0"
+  dbname       STR isp
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName   STR DARK-cool
+  prereq       STR BIAS
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/dark -det_type dark -select_exp_type dark -select_exp_time_min 0.5 -select_ccd_temp_max -27.5 -ccd_temp_max -27.5"
+  dbname       STR isp
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName   STR SHUTTER
+  prereq       STR "DARK-warm DARK-cool"
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/shutter -det_type shutter -select_exp_type skyflat -select_exp_time_min 0.05 -select_filter g -select_dateobs_begin 2007-01-14T04:00:00 -select_dateobs_end 2007-01-14T05:00:00 -select_sat_pixel_frac_max 0.1"
+  dbname       STR isp
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName   STR FLAT-g
+  prereq       STR SHUTTER
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/flat/g -det_type flat -select_exp_type skyflat -select_exp_time_min 0.05 -select_sat_pixel_frac_max 0.1 -select_filter g -filter g -select_dateobs_begin 2007-01-14T00:00:00 -select_dateobs_end 2007-01-15T00:00:00"
+  dbname       STR isp
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName   STR FLAT-r
+  prereq       STR SHUTTER
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/flat/r -det_type flat -select_exp_type skyflat -select_exp_time_min 0.05 -select_sat_pixel_frac_max 0.1 -select_filter r -filter r -select_dateobs_begin 2007-01-14T00:00:00 -select_dateobs_end 2007-01-15T00:00:00"
+  dbname       STR isp
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName   STR FLAT-i
+  prereq       STR SHUTTER
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/flat/i -det_type flat -select_exp_type skyflat -select_exp_time_min 0.05 -select_sat_pixel_frac_max 0.1 -select_filter i -filter i -select_dateobs_begin 2007-01-14T00:00:00 -select_dateobs_end 2007-01-15T00:00:00"
+  dbname       STR isp
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName   STR FLAT-z
+  prereq       STR SHUTTER
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/flat/z -det_type flat -select_exp_type skyflat -select_exp_time_min 0.05 -select_sat_pixel_frac_max 0.1 -select_filter z -filter z -select_dateobs_begin 2007-01-14T00:00:00 -select_dateobs_end 2007-01-15T00:00:00"
+  dbname       STR isp
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName   STR FLAT-y
+  prereq       STR SHUTTER
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/flat/y -det_type flat -select_exp_type skyflat -select_exp_time_min 0.05 -select_sat_pixel_frac_max 0.1 -select_filter y -filter y -select_dateobs_begin 2007-01-14T00:00:00 -select_dateobs_end 2007-01-15T00:00:00"
+  dbname       STR isp
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName   STR FRINGE-z
+  prereq       STR FLAT-z
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/fringe/z -det_type fringe -select_exp_type object -select_exp_time_min 0.05 -select_filter z -filter z -select_dateobs_begin 2007-01-14T09:19:00 -select_dateobs_end 2007-01-18"
+  dbname       STR isp
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName   STR FRINGE-y
+  prereq       STR FLAT-y
+  options      STR "-definebyquery -inst ISP-Apogee -workdir path://ISP/detrend/fringe/y -det_type fringe -select_exp_type object -select_exp_time_min 0.05 -select_filter y -filter y -select_dateobs_begin 2007-01-13 -select_dateobs_end 2007-01-18"
+  dbname       STR isp
+  pantaskState STR NULL
+END
Index: /tags/sj_tags/sj_root_20080929/ippTasks/magic.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/magic.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/magic.pro	(revision 22322)
@@ -0,0 +1,419 @@
+## magic.pro : support for the streak removal : -*- sh -*-
+
+# test for required global variables
+check.globals
+
+$LOGSUBDIR = $LOGDIR/magic
+exec mkdir -p $LOGSUBDIR
+
+### Initialise the books containing the tasks to do
+book init magicToTree
+book init magicToProcess
+book init magicToMask
+
+### Database lists
+$magicTree_DB = 0
+$magicProcess_DB = 0
+$magicMask_DB = 0
+
+### Check status of tasks
+macro magic.status
+  book listbook magicToTree
+  book listbook magicToProcess
+  book listbook magicToMask
+end
+
+### Reset tasks
+macro magic.reset
+  book init magicTree
+  book init magicProcess
+  book init magicMask
+end
+
+### Turn tasks on
+macro magic.on
+  task magic.tree.load
+    active true
+  end
+  task magic.tree.run
+    active true
+  end
+  task magic.process.load
+    active true
+  end
+  task magic.process.run
+    active true
+  end
+  task magic.mask.load
+    active true
+  end
+  task magic.mask.run
+    active true
+  end
+end
+
+### Turn tasks off
+macro magic.on
+  task magic.tree.load
+    active false
+  end
+  task magic.tree.run
+    active false
+  end
+  task magic.process.load
+    active false
+  end
+  task magic.process.run
+    active false
+  end
+  task magic.mask.load
+    active false
+  end
+  task magic.mask.run
+    active false
+  end
+end
+
+task	       magic.tree.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/magic.tree.log
+
+  task.exec
+    if ($DB:n == 0)
+      option DEFAULT
+      command magictool -totree -limit 20
+    else
+      # save the DB name for the exit tasks
+      option $DB:$magicTree_DB
+      command magictool -totree -limit 20 -dbname $DB:$magicTree_DB
+      $magicTree_DB ++
+      if ($magicTree_DB >= $DB:n) set magicTree_DB = 0
+    end
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout magicTree -key magic_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook magicTree
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup magicTree
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+task	       magic.tree.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    book npages magicTree -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images (pantaskState == INIT)
+    book getpage magicTree 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword magicTree $pageName pantaskState RUN
+    book getword magicTree $pageName magic_id -var MAGIC_ID
+    book getword magicTree $pageName exp_id -var EXP_ID
+    book getword magicTree $pageName camera -var CAMERA
+    book getword magicTree $pageName workdir -var WORKDIR_TEMPLATE
+    book getword magicTree $pageName dbname -var DBNAME
+    book getword magicTree $pageName tess_id -var TESS_DIR
+    book getword magicTree $pageName ra -var RA
+    book getword magicTree $pageName decl -var DEC
+
+#    set.host.for.camera $CAMERA $MAGIC_ID
+#    set.workdir.by.camera $CAMERA $MAGIC_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+    host anyhost
+    $WORKDIR = $WORKDIR_TEMPLATE
+
+    basename $TESS_DIR -var TESS_ID
+    sprintf outroot "%s/%s/%s.mgc.%s" $WORKDIR $EXP_ID $EXP_ID $MAGIC_ID
+
+    ## generate output log based on filerule (convert the URI to a PATH)
+    $logfile = `ipp_filename.pl --filerule LOG.EXP --camera $CAMERA --class_id $MAGIC_ID --basename $outroot`
+    if ("$logfile" == "") 
+      echo "WARNING: logfile not defined in magic.tree.run"
+      break
+    end
+
+    stdout $logfile
+    stderr $logfile
+    dirname $logfile -var outpath
+    mkdir $outpath
+
+    $run = magic_tree.pl --magic_id $MAGIC_ID --camera $CAMERA --tess_id $TESS_DIR --ra $RA --dec $DEC --outroot $outroot --logfile $logfile
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit magicTree $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword magicTree $options:0 pantaskState TIMEOUT
+  end
+end
+
+task	       magic.process.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/magic.process.log
+
+  task.exec
+    if ($DB:n == 0)
+      option DEFAULT
+      command magictool -toprocess -limit 20
+    else
+      # save the DB name for the exit tasks
+      option $DB:$magicTree_DB
+      command magictool -toprocess -limit 20 -dbname $DB:$magicProcess_DB
+      $magicProcess_DB ++
+      if ($magicProcess_DB >= $DB:n) set magicProcess_DB = 0
+    end
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout magicProcess -key magic_id:node -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook magicProcess
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup magicProcess
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+task	       magic.process.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    book npages magicProcess -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images (pantaskState == INIT)
+    book getpage magicProcess 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword magicTree $pageName pantaskState RUN
+    book getword magicTree $pageName magic_id -var MAGIC_ID
+    book getword magicTree $pageName exp_id -var EXP_ID
+    book getword magicTree $pageName node -var NODE
+    book getword magicTree $pageName camera -var CAMERA
+    book getword magicTree $pageName workdir -var WORKDIR_TEMPLATE
+    book getword magicTree $pageName dbname -var DBNAME
+
+#    set.host.for.camera $CAMERA $MAGIC_ID
+#    set.workdir.by.camera $CAMERA $MAGIC_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+    host anyhost
+    $WORKDIR = $WORKDIR_TEMPLATE
+
+    basename $TESS_DIR -var TESS_ID
+    sprintf outroot "%s/%s/%s.mgc.%s.%s" $WORKDIR $EXP_ID $EXP_ID $MAGIC_ID $NODE
+
+    ## generate output log based on filerule (convert the URI to a PATH)
+    $logfile = `ipp_filename.pl --filerule LOG.EXP --camera $CAMERA --class_id $MAGIC_ID --basename $outroot`
+    if ("$logfile" == "") 
+      echo "WARNING: logfile not defined in magic.process.run"
+      break
+    end
+
+    stdout $logfile
+    stderr $logfile
+    dirname $logfile -var outpath
+    mkdir $outpath
+
+    $run = magic_process.pl --magic_id $MAGIC_ID --camera $CAMERA --node $NODE --outroot $outroot --logfile $logfile
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit magicProcess $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword magicProcess $options:0 pantaskState TIMEOUT
+  end
+end
+
+task	       magic.mask.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/magic.mask.log
+
+  task.exec
+    if ($DB:n == 0)
+      option DEFAULT
+      command magictool -tomask -limit 20
+    else
+      # save the DB name for the exit tasks
+      option $DB:$magicMask_DB
+      command magictool -tomask -limit 20 -dbname $DB:$magicMask_DB
+      $magicMask_DB ++
+      if ($magicMask_DB >= $DB:n) set magicMask_DB = 0
+    end
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout magicMask -key magic_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook magicMask
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup magicMask
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+task	       magic.mask.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    book npages magicMask -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images (pantaskState == INIT)
+    book getpage magicMask 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword magicMask $pageName pantaskState RUN
+    book getword magicMask $pageName magic_id -var MAGIC_ID
+    book getword magicMask $pageName exp_id -var EXP_ID
+    book getword magicMask $pageName camera -var CAMERA
+    book getword magicMask $pageName workdir -var WORKDIR_TEMPLATE
+    book getword magicMask $pageName dbname -var DBNAME
+
+#    set.host.for.camera $CAMERA $MAGIC_ID
+#    set.workdir.by.camera $CAMERA $MAGIC_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+    host anyhost
+    $WORKDIR = $WORKDIR_TEMPLATE
+
+    basename $TESS_DIR -var TESS_ID
+    sprintf outroot "%s/%s/%s.mgc.%s.mask" $WORKDIR $EXP_ID $EXP_ID $MAGIC_ID
+
+    ## generate output log based on filerule (convert the URI to a PATH)
+    $logfile = `ipp_filename.pl --filerule LOG.EXP --camera $CAMERA --class_id $MAGIC_ID --basename $outroot`
+    if ("$logfile" == "") 
+      echo "WARNING: logfile not defined in magic.tree.run"
+      break
+    end
+
+    stdout $logfile
+    stderr $logfile
+    dirname $logfile -var outpath
+    mkdir $outpath
+
+    $run = magic_mask.pl --magic_id $MAGIC_ID --camera $CAMERA --outroot $outroot --logfile $logfile
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit magicMask $options:0 $JOB_STATUS
+   end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword magicMask $options:0 pantaskState TIMEOUT
+  end
+end
+
Index: /tags/sj_tags/sj_root_20080929/ippTasks/mkdetrend.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/mkdetrend.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/mkdetrend.config	(revision 22322)
@@ -0,0 +1,43 @@
+
+detRunDef MULTI
+
+# add in the min number of input images and a time range
+detRunDef METADATA
+  detRunName STR BIAS
+  prereq     STR NONE
+  options    STR "-definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type BIAS -select_exp_type BIAS"
+  dbname     STR eamtest
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName STR DARK
+  prereq     STR BIAS
+  options    STR "-definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type DARK -select_exp_type DARK"
+  dbname     STR eamtest
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName STR SHUTTER
+  prereq     STR DARK
+  options    STR "-definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type SHUTTER -filter r -select_exp_type FLAT -select_filter r"
+  dbname     STR eamtest
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName STR FLAT-r
+  prereq     STR SHUTTER
+  options    STR "-definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type FLAT -filter r -select_exp_type FLAT -select_filter r"
+  dbname     STR eamtest
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName STR FLAT-i
+  prereq     STR SHUTTER
+  options    STR "-definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type FLAT -filter i -select_exp_type FLAT -select_filter i"
+  dbname     STR eamtest
+  pantaskState STR NULL
+END
Index: /tags/sj_tags/sj_root_20080929/ippTasks/mkdetrend.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/mkdetrend.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/mkdetrend.pro	(revision 22322)
@@ -0,0 +1,217 @@
+## mkdetrend.pro : autorun the detrend analysis : -*- sh -*-
+
+# detRun  METADATA  
+#    det_id           STR       3               
+#    iteration        S32       0              
+#    det_type         STR       SHUTTER         
+#    mode             STR       master          
+#    state            STR       run             
+#    filelevel        STR       FPA             
+# END
+
+macro load.detRunDefs
+  if ($0 != 2)
+    echo "USAGE: load.detRunDefs (filename)"
+    break
+  end
+  queueload detruns -x "cat $MODULES:0/$1"
+  ipptool2book detruns detRunDef -key detRunName -uniq -setword pantaskState INIT
+  queuedelete detruns
+end
+
+task detrun.launch
+  host         local
+
+  periods      -poll 1
+  periods      -exec 5
+  periods      -timeout 30
+  active       true
+  npending     1
+
+  # each outstanding detrend run blocks the next one.  only launch the next 
+  # if the current one is done
+  task.exec
+    local i j N found missed detRunName prereq prereqList prereqState cmdOptions dbname
+
+    # how many detRunDefs are waiting to be started?
+    book npages detRunDef -var N -key pantaskState INIT
+    # echo "detrun.launch N: $N"
+    if ($N == 0) break
+
+    $found = 0
+
+    # search the detRunDef book for an entry which is unstarted (state NULL) 
+    # and for which the dependencies are done 
+    for i 0 $N
+      book getpage detRunDef $i -var detRunName -key pantaskState INIT
+      # echo "detrun.launch detRunName: $detRunName"
+      if ("$detRunName" == "NULL") break 
+ 
+      book getword detRunDef $detRunName prereq -var prereqList
+      if (("$prereqList" == "NULL") || ("$prereqList" == "NONE"))
+        $found = 1
+        last
+      end
+
+      list prereq -split $prereqList
+      $missed = 0
+      for j 0 $prereq:n
+        # prereq must exist and be in state of DONE
+        book getword detRunDef $prereq:$j pantaskState -var prereqState
+
+        # echo "detrun.launch prereqState: $prereqState"
+        # echo "detrun.launch detRunName: $detRunName"
+        if ("$prereqState" != "DONE") 
+          $missed = 1
+          last
+        end
+      end
+      if ($missed == 0)
+        $found = 1
+        last
+      end
+
+      if ($found == 0) 
+        if ($VERBOSE >= 2)
+          echo "$detRunName not ready"
+        end
+      else
+        if ($VERBOSE >= 2)
+          echo "$detRunName ready"
+        end
+      end
+    end
+
+    if ($found == 0) break
+
+    # echo "detrun.launch detRunName: $detRunName"
+
+    book getword detRunDef $detRunName options -var cmdOptions
+    book getword detRunDef $detRunName dbname -var dbname
+    book setword detRunDef $detRunName pantaskState INIT
+
+    if ($VERBOSE >= 1)
+      echo "starting detrend $detRunName :"
+      echo "command dettool $cmdOptions -dbname $dbname"
+    end
+
+    options $dbname $detRunName
+    command dettool $cmdOptions -dbname $dbname
+  end
+
+  task.exit $EXIT_SUCCESS
+    local detRunID
+
+    $dbname = $options:0
+    $detRunName = $options:1
+
+    # convert 'stdout' to book format
+    ipptool2book stdout detRun -key det_id -uniq
+    if ($VERBOSE > 2)
+      book listbook detRun
+    end
+
+    # we should have launched only a single detRun
+    book getpage detRun 0 -var detRunID
+    if ("$detRunID" == "NULL") 
+      echo "ERROR:detRun not defined"
+      break
+    end
+
+    # we need to track the detID generated
+    book setword detRunDef $detRunName detRunID $detRunID
+    book setword detRunDef $detRunName pantaskState RUN
+
+    # drop the detRun book after we've grabbed the state
+    book delete detRun
+  end
+end
+
+$detRunNcheck = 0
+
+# loop over the outstanding detRuns and update state
+task detrun.check
+  host         local
+
+  periods      -poll 1
+  periods      -exec 5
+  periods      -timeout 30
+  active       true
+  npending     1
+
+  # each outstanding detrend run blocks the next one.  only launch the next 
+  # if the current one is done
+  task.exec
+    local i N found detRunName detRunID dbname
+
+    book npages detRunDef -var N -key pantaskState RUN
+    if ($N == 0) break
+    if ($detRunNcheck >= $N) 
+      $detRunNcheck = 0
+    end
+
+    # try the next detRun entry which is in state RUN
+    book getpage detRunDef $detRunNcheck -var detRunName -key pantaskState RUN
+    $detRunNcheck ++
+
+    if ("$detRunName" == "NULL")
+      if ($VERBOSE >= 3)
+        echo "no more active detruns (Ncheck = $detRunNcheck)"
+      end
+      break
+    end 
+
+    # info needed to check on current state
+    book getword detRunDef $detRunName detRunID -var detRunID
+    book getword detRunDef $detRunName dbname -var dbname
+
+    if ($VERBOSE >= 2) 
+      echo " checking on $detRunName"
+      echo "command dettool -runs -det_id $detRunID -dbname $dbname"
+    end
+
+    # we need a dettool command which takes a specific det_id and returns
+    # the current state
+    options $dbname $detRunName
+    command dettool -runs -det_id $detRunID -dbname $dbname
+  end
+
+  task.exit $EXIT_SUCCESS
+    local detRunID state
+
+    $dbname = $options:0
+    $detRunName = $options:1
+
+    # convert 'stdout' to book format
+    ipptool2book stdout detRun -key det_id -uniq
+    if ($VERBOSE > 2)
+      book listbook detRun
+    end
+
+    # we should have launched only a single detRun
+    book getpage detRun 0 -var detRunID
+    if ("$detRunID" == "NULL") 
+      echo "ERROR:detRun not defined"
+      break
+    end
+
+    # what is the current state of the detRun?
+    book getword detRun $detRunID state -var state
+
+    # use any other state?
+    if ("$state" == "stop")
+      # update the state for this detRunName, drop detRun page
+      book setword detRunDef $detRunName pantaskState DONE
+      if ($VERBOSE > 1)
+        echo "$detRunName is done"
+      end
+    else
+      if ($VERBOSE > 1)
+        echo "$detRunName is still running"
+      end
+    end
+
+    # drop this book when completed
+    book delete detRun
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/nebulous.site.pro.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/nebulous.site.pro.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/nebulous.site.pro.in	(revision 22322)
@@ -0,0 +1,6 @@
+# customize these variables for your site
+
+$NEB_DB    = nebulous
+$NEB_HOST  = alala
+$NEB_USER  = XXX
+$NEB_PASS  = XXX
Index: /tags/sj_tags/sj_root_20080929/ippTasks/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/notes.txt	(revision 22322)
@@ -0,0 +1,373 @@
+
+2008.08.31
+
+  * flatcorr modes:
+    * definebyquery : select the matching rawExp entries and add them to chip table.  this is functionally identical to chiptool -definebyquery,
+      except that it also adds entries in the flatcorr tables to track the processing
+
+    
+    
+
+2008.08.22
+
+  issues related to creating and applying the flat-field correction in a simtest context
+
+  * generating the fake data:
+    * ppSim must be called with a recipe in which the SCATTER.FRACTION is non-zero	
+    * simtest thus needs to accept a ppsim_recipe and supply it to ppSimSequence
+    * ppSimSequence needs to accept the ppsim_recipe and supply it to the generated ppSim calls
+
+  * first stage processing:
+    * we need to build flats which are not corrected (use the standard 'flat' type?)
+    * we need to apply flats which are not corrected
+    ** these can both be accomplished with the current DEFAULT reduction class
+
+  * second stage processing:
+    * we need to apply flats which are corrected
+    ** define a reduction class of type FLATCORR which:
+       * modifies ppImage.config:DETREND.CONTRAINTS:FLAT:DETTYPE = FLAT_CORRECT
+
+  ** adding a new detrend type:
+
+   * add an entry to detrend_stack.pl FILERULES
+   * add an entry to detrend_stack.pl STATRECIPES
+   * add an entry to ppMerge/src/ppMergeArguments.c
+   * add an entry to ippconfig/recipes/ppMerge.config
+
+   * add an entry to ippconfig/recipes/rejections.config
+   * add a section to ippconfig/recipes/reductionClasses.mdc
+
+   * ippScripts/scripts/detrend_norm_apply.pl : entry in DETTYPE table
+   * ippScripts/scripts/detrend_norm_calc.pl : entry in NORMALIZE table
+   * ippScripts/scripts/detrend_resid_imfile.pl : entry in DETRENDS table
+   * ippScripts/scripts/detrend_stack.pl : entry in FILERULES and STATRECIPES tables
+   * ippScripts/scripts/detrend_resid_imfile.pl : entry in DETRENDS table
+
+  * need a block in simtest.auto (or equiv) to check for when the chip processing / cam processing is complete
+
+  * need to modify the detselect -inst arguments in simtest.auto (use @INST@ or equiv)
+
+  * reductions currently being used:
+    ** CHIP -- chip-level analysis
+    ** JPEG.BIN1, JPEG.BIN2 -- camera jpeg terms
+    ** JPEG_BIN1_IMAGE_(dettype), JPEG_BIN2_IMAGE_(dettype) 
+    ** JPEG_BIN1_RESID_(dettype), JPEG_BIN2_RESID_(dettype) 
+    ** (dettype)_STACK, 
+    ** (dettype)_PROCESS, 
+    ** FAKEPHOT
+
+2008.01.22
+
+  proposal to clean scripts/tasks up somewhat wrt names:
+
+  * scripts do NOT use workdir to change the workdir in the db
+  * thus, what is supplied is actually the outputRoot
+  * require the output root for the scripts
+  * have the calling tasks construct the outputRoot
+  ** replace -workdir with -outroot 
+
+  some filename rules :
+
+  * the db entry WORKDIR is the most abstract version:
+    path://GPC-@HOST@/20080130
+    file://data/@HOST@.0/gpc1/20080130
+    neb:///@HOST@/gpc1/20080130 
+    neb://gpc1/20080130 
+    XXX have nebulous distinguish between neb:///host/path and
+    neb://path?
+
+  * the db WORKDIR entry is carried from step to step unless
+    over-ridden with a -queue command
+
+  * psLib-based binaries (ppImage, etc) may be passed URIs, but not
+    files with the @HOST@ abstraction
+
+  * IPP perl scripts may be passed URIs, but not files with the @HOST@
+    abstraction
+
+  * nebulous : convert neb:///@HOST@/path/name
+
+  *** examples:
+
+  * this tells ppImage to use the ipp004 version for both input and output:
+  ppImage -file neb:///ipp004-v0/gpc1/20080130/o4414g0001o/o4414g0001o.XY01.fits neb:///ipp004-v0/gpc1/20080130/o4414g0001o/o4414g0001o.XY01
+  
+  * we already know the hostname at this point; include it in the call
+  chip_imfile.pl --uri neb:///ipp004-v0/gpc1/20080130/o4414g0001o/o4414g0001o.XY01.fits --workdir neb:///ipp004-v0/gpc1/20080130/
+
+
+
+
+
+2008.01.21
+
+  I'm working on the summit.copy.pro interaction with nebulous (and
+  those of the other ippTasks) and the assignment of jobs and data
+  locations to hosts on the basis of the chip/host relationship.  I
+  have defined a table which relates the chip (class_id) to the
+  desired host name.  There are a few places, and a few slightly
+  different ways, in which this gets used:
+
+  * choice of host for command execution:
+
+    every job which operates on a specific chip must be told the chip
+    host so it can run the job on that machine.  the information is
+    supplied in pantasks to the job with the 'host' command.  I've
+    made a pantasks macro 'set.host.by.camera' which takes the camera
+    and class_id, looks up the match in the chip/host table, and calls
+    the host command with the desired host.  If there is no match
+    (camera not listed in chip/host table or chip not listed in
+    table), the host is set to 'anyhost', which randomly chooses one.
+    I have used this command for all tasks which generate a pcontrol
+    command.  For the tasks which are performed at the camera level,
+    these are set to anyhost.  For the tasks which operate on stacks,
+    we can use the same structure to associate the skycell with a
+    host.  
+
+  * specific output filenames
+
+    In both ippTasks and ippScripts, there are references to files
+    which must correctly located on the host.
+
+  * workdir choice of output directory.   
+
+    the 
+
+  * dsget output target:
+
+    dsget is called by pantasks in the task 'dsget'.  currently, dsget
+    accepts a --filename argument which specifies the output filename
+    to use.  in the nebulous context, this is of the form
+    neb://top/next/stuff, without any host-specific information.  To
+    target the file instance to a specific host, we pass dsget the
+    --volume (name) argument.  The volumes refer to the specific
+    devices (potentially more than one per host), and the names can
+    then be chosen to include the host.  
+  
+2007.10.11
+
+  The calibration suite uses a database table of known/registered DVO
+  databases to be calibrated.  The suite operates on a segment of the
+  sky in each run, nominally a section ~1h west of the current ST.
+
+  caltool -pendingdbs returns a list of entries (mdc format), one for
+  each DVO database in the table.  
+
+  caltool -databases [-active] : returns the list of dvo databases
+  caltool -addcalrun : inserts the stats for a single calibration run
+  caltool -setstate (active/inactive) : modify the state of a dvo
+    database
+  caltool -adddatabase : insert a new database
+
+  XXX a potential source of collisions.  the books are marked by
+  unique keys in the db tables, but a single pantasks run may be
+  addressing multiple databases.  IDs are not unique across
+  databases.  we can solve this by including in the key the dbname
+
+  the calibration suite runs the following tasks, in sequences on each
+  of the databases:
+
+  * resort, relphot, uniphot, relastro.objects, relastro.images
+
+  ** detrend correction: any detrend images which need to be corrected
+     get inserted in the detCorrectedExp table.  
+
+  dettool commands needed:
+  * add a detrend exposure (det_id, exp_id) to the detCorrectedExp table
+  * add a detrend imfile (det_id, exp_id, class_id) to the
+  detCorrectedImfile table
+  * select pending detCorrectedExp
+  * select pending detCorrectedImfiles
+
+
+2007.01.08
+
+  I have updated pantasks to used named tags in named groups to make
+  life much easier when changing the schema of the db tables.  The
+  function ipptool2book convernts the output from an ippTool query in
+  the form of a set of metadata structures (saved in stdout) into a
+  book format.
+
+  dettool -tonormalizedstat     DetrendNormStat
+  dettool -tonormalize          DetrendNorm
+  dettool -tonormalizedexp      DetrendNormExp
+  dettool -toprocessedimfile    DetrendProcessImfiles
+  dettool -toprocessedexp       DetrendProcessExposures
+  dettool -residdetrun          DetrendRejectExp
+  dettool -toresidimfile        DetrendResidImfiles
+  dettool -toresidexp           DetrendResidExposures
+  dettool -tostacked            DetrendStackClass
+  p0tool -pendingimfile         Phase0Imfiles
+  p0tool -pendingexp            Phase0Exposures
+  p2tool -pendingimfile         Phase2Imfiles
+  p3tool -pendingexp            Phase3Exposures
+
+2006.08.28
+
+- ipptools need to use expID:camera:telescope as unique key (ie, not expID alone)
+- p0tool -pendingimfile -simple needs to report the expKey (ie, not expID)
+- 
+
+
+This directory contains the PanTasks scripts which define the steps
+performed by the IPP.  These scripts use the ippTools to decide what
+images need to be processed, and to update the Metadata Database with
+the current state of the analysis.  The analysis is performed using
+IPP programs which are distributed to the cluster computers as needed.
+
+1. The Detrend Creation Scripts
+
+The detrend creation process is among the more complex elements of the
+IPP.  There are N major steps which must be performed:
+
+1.1. Define New Detrend Runs
+
+The script 'detrend.mkruns.pro' defines tasks which initiate new
+Detrend Runs.  There is a separate task for each camera automatically
+processed by the system (mkdetruns.CAMERA).  These tasks are executed
+on a regular, but infrequent basis.  The tasks call external helper
+programs which actually run the command dettools -define.  The helper
+programs are thus responsible for identifying all of the types of
+detrend images to be run for a given camera, as well as the correct
+set of filters to apply.  These tasks are run locally and only affect
+the database.  PanTasks does not track the detrend runs which are
+created.  
+
+- we may need to define these within the PanTasks scripts
+- how frequently are these tasks run (1/day?)
+- if no raw images are selected by the query, no new detrun should be
+  created.
+- we may need different schedules for the dark (bias/dark) and the
+  light (flat/fringe) data.  And yet another schedule for sky data
+  (skyflat/skyfringe).
+
+1.2. Process new raw imfiles
+
+The script 'detrend.process.pro' defines tasks which processes the new
+raw imfiles for any detrend run.  This script defines two tasks,
+'dettool.raw.load' and 'dettool.raw.process'.  The first script
+occasionally polls the Metadata Database to select raw imfiles which
+have been identified as part of a detrend run.  The identified raw
+imfiles are added to an internal queue, DetrendImfilesToProcess.  Any
+raw imfile already in the list is ignored.  The imfiles are added to
+the queue with a state of NEW.  The second task examines the queue for
+imfiles with state NEW and defines a ppImage analysis job for that
+imfile.  The job is sent to pcontrol for processing on the distributed
+cluster.  At this point, the corresponding queue entry is set to the
+state RUN. Upon success, the queue entry is set to DONE and the
+database is notified of the successful operation.  The DONE entries
+are occasionally pruned from the queue; this must be done in the same
+task as the initial query and the update of the list based on the
+database results.  
+
+- we need to define the columns in the output of dettool -raw
+- we need a method to associate an imfile with a node in the cluster
+- we need to perform some appropriate action when the queries fail.
+- we need a method to generate the output URIs
+- we need a method to identify the correct recipe (function of type?)
+
+1.3. Stack new processed imfiles
+
+The script 'detrend.stack.pro' defines tasks which stack the newly
+processed imfiles for any detrend run iteration.  This script defines
+two tasks, 'dettool.stack.load and 'dettool.stack.process.  The first
+script occasionally polls the Metadata Database to select class IDs
+for detrend runs for which the input imfiles have all been processed.
+The identified detRun/classID combination is added to an internal
+queue, DetrendClassIDtoStack.  The entries are added to the queue with
+a state of NEW.  The second task examines the queue for entries with a
+state of NEW.  It defines a ppMerge analysis job for that
+detID/classID.  The helper script selects the corresponding input
+imfiles and passes them to ppMerge.  The job is sent to pcontrol for
+processing on the distributed cluster.  At this point, the
+corresponding queue entry is set to the state RUN. Upon success, the
+queue entry is set to DONE and the database is notified of the
+successful operation.  The DONE entries are occasionally pruned from
+the queue; this must be done in the same task as the initial query and
+the update of the list based on the database results.
+
+- we need to define the columns in the output of dettool -chip -processed
+- we need a method to associate a classID with a node in the cluster
+- we need to perform some appropriate action when the queries fail.
+- we need a method to generate the output URIs (not a function of the imfiles...)
+- we need a method to identify the correct recipe (function of type?)
+
+
+1.4. Normalize stacked masters
+
+The script 'detrend.norm.pro' defines tasks which normalize the newly
+processed stacks for any detrend run iteration.  This script defines
+two tasks, 'dettool.norm.load and 'dettool.norm.process.  The first
+script occasionally polls the Metadata Database to select detrun IDs
+for which the classes have all been stacked.  The identified
+detRunID/iteration combination is added to an internal queue,
+DetrendRunToNormalize.  The entries are added to the queue with a
+state of NEW.  The second task examines the queue for entries with a
+state of NEW.  It defines a ppNorm analysis job for that
+detID/iteration.  The job is sent to pcontrol for processing on the
+distributed cluster.  At this point, the corresponding queue entry is
+set to the state RUN. Upon success, the queue entry is set to DONE and
+the database is notified of the successful operation.  The DONE
+entries are occasionally pruned from the queue; this must be done in
+the same task as the initial query and the update of the list based on
+the database results.
+
+- we need to define the columns in the output of dettool
+- we need to perform some appropriate action when the queries fail.
+- we need a method to generate the output URIs (not a function of the imfiles...)
+- we need a method to identify the correct recipe (function of type?)
+- we need to define the ppNorm operation inputs and outputs better
+
+
+1.5. Generate Resid Imfiles
+
+The script 'detrend.resid.pro' defines tasks which apply the newly
+generated detrend image to the corresponding input images to generate
+the residual images.  This script defines two tasks,
+'dettool.resid.load and 'dettool.resid.process.  The first script
+occasionally polls the Metadata Database to select imfiles for which
+the master detrend images has been normalized.  The identified imfiles
+are added to an internal queue, DetrendResidImfiles.  The entries are
+added to the queue with a state of NEW.  The second task examines the
+queue for entries with a state of NEW.  It defines a ppImage analysis
+job for that imfile.  The job is sent to pcontrol for processing on
+the distributed cluster.  At this point, the corresponding queue entry
+is set to the state RUN. Upon success, the queue entry is set to DONE
+and the database is notified of the successful operation.  The DONE
+entries are occasionally pruned from the queue; this must be done in
+the same task as the initial query and the update of the list based on
+the database results.
+
+- we need to define the columns in the output of dettool
+- can we select the imfiles here without apriori knowledge that the
+  master has been normalized?
+- we need to perform some appropriate action when the queries fail.
+- we need a method to generate the output URIs (not a function of the imfiles...)
+- we need a method to identify the correct recipe (function of type?)
+- we need a method to pass the matching detrend image to ppImage in
+  the correct context (ie, a bias needs to be supplied to the -bias element...)
+
+1.6. Assess Detrend Results
+
+The script 'detrend.assess.pro' defines tasks which assess the
+residual statistics for completed detrend runs.  This script defines
+two tasks, 'dettool.assess.load and 'dettool.assess.process.  The
+first script occasionally polls the Metadata Database to select
+detrunIDs/iterations for which the resid imfiles have all been
+generated.  The identified detruns are added to an internal queue,
+DetrendRunToAssess.  The entries are added to the queue with a state
+of NEW.  The second task examines the queue for entries with a state
+of NEW.  It defines an analysis job for that collection.  The job is
+sent to pcontrol for processing on the distributed cluster.  At this
+point, the corresponding queue entry is set to the state RUN. Upon
+success, the queue entry is set to DONE and the database is notified
+of the successful operation.  The DONE entries are occasionally pruned
+from the queue; this must be done in the same task as the initial
+query and the update of the list based on the database results.
+
+- the assess tool needs to trigger a new run, if needed.
+- what is the command to run on the resid statistics?
+- we need to define the columns in the output of dettool
+- can we select the detrend runs here?
+- we need to perform some appropriate action when the queries fail.
+- we need a method to generate the output URIs (not a function of the imfiles...)
Index: /tags/sj_tags/sj_root_20080929/ippTasks/pantasks.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/pantasks.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/pantasks.pro	(revision 22322)
@@ -0,0 +1,494 @@
+## pantasks.pro : globals and support macros : -*- sh -*-
+
+# globals used to control system behavior : these should be in uppercase
+$NEBULOUS = 0
+$NETWORK = 1
+$PARALLEL = 1
+$VERBOSE = 1
+$LABEL = "NONE"
+$POLLLIMIT = 32
+$LOGDIR = `pwd`
+$LOGDIR = $LOGDIR/pantasks_logs
+mkdir $LOGDIR
+$KEEP_FAILURES = 0
+
+$LOADPOLL = 0.25
+$LOADEXEC = 5
+$RUNPOLL = 0.5
+$RUNEXEC = 1
+
+$EXIT_SUCCESS     = 0
+$EXIT_UNKNOWN_ERR = 1
+$EXIT_SYS_ERR     = 2
+$EXIT_CONFIG_ERR  = 3
+$EXIT_PROG_ERR    = 4
+$EXIT_DATA_ERR    = 5
+
+# DB lists the database names in use; the default ipprc database
+# is only used if DB:n is 0
+$DB:n = 0
+
+# very basic values: set these with init.copy.mhpcc
+$default_host     = anyhost
+$workdir_template = `pwd`
+
+# user function to add databases by name (avoids duplicates)
+macro add.database
+  if ($0 != 2)
+    echo "USAGE: add.database (db)"
+    break
+  end
+  if ($?DB:n == 0)
+    list DB -add $1
+    return
+  end
+
+  $found = 0
+  for i 0 $DB:n
+    if ($DB:$i == $1) 
+      $found = 1
+      echo "$DB:$i set"
+      last
+    end
+  end
+  
+  if ($found == 0)
+    list DB -add $1
+  end
+end
+
+macro del.database
+  if ($0 != 2)
+    echo "USAGE: del.database (db)"
+    break
+  end
+  if ($?DB:n == 0)
+    return
+  end
+
+  list DB -del $1
+end
+
+macro init.isp
+  list DB -add isp
+end
+
+macro init.essence
+  list DB -add essence_v2
+end
+
+macro init.simtest.local
+  list DB -add simtest
+  $PARALLEL = 0
+  controller exit true
+end
+
+macro init.simtest.default.db
+  $host = `hostname`
+  if ($PARALLEL) 
+    controller exit true
+    controller host add $host
+  end
+end
+
+macro init.simtest
+  list DB -add simtest
+
+  $host = `hostname`
+  if ($PARALLEL) 
+    controller exit true
+    controller host add $host
+  end
+end
+
+# need the ability to activate or deactivate specific tasks...
+
+macro detrend.on
+  detproc.on
+  detstack.on
+  detnorm.on
+  detresid.on
+  detreject.on
+end
+
+macro detrend.off
+  detproc.off
+  detstack.off
+  detnorm.off
+  detresid.off
+  detreject.off
+end
+
+macro detrend.reset
+  detproc.reset
+  detstack.reset
+  detnorm.reset
+  detresid.reset
+  detreject.reset
+end
+
+macro all.on
+  register.on
+  detrend.on
+  chip.on
+  camera.on
+  fake.on
+  warp.on
+  diff.on
+  stack.on
+end
+
+macro all.off
+  register.off
+  detrend.off
+  chip.off
+  camera.off
+  fake.off
+  warp.off
+  diff.off
+  stack.off
+end
+
+macro module.tasks
+  module register.pro
+  module detrend.process.pro
+  module detrend.stack.pro
+  module detrend.norm.pro
+  module detrend.resid.pro
+  module detrend.reject.pro
+  module chip.pro
+  module camera.pro
+  module fake.pro
+  module warp.pro
+  module diff.pro
+  module stack.pro
+end
+
+macro showcommand
+  local n 
+
+  if ($VERBOSE)
+    $command = $taskarg:0
+    for n 1 $taskarg:n
+      $command = $command $taskarg:$n
+    end
+    if ($0 == 2)
+      echo ""
+      echo "$1 for: $command"
+      echo "job exit status: $JOB_STATUS"
+      echo "job host: $JOB_HOSTNAME"
+      echo "job dtime: $JOB_DTIME"
+    else
+      echo "command: $command"
+    end    
+  end
+end
+
+# Add standard arguments to the poll functions
+macro add_poll_args
+    if ($0 != 2)
+	echo "Must pass in the command of interest, and this function will supplement"
+	stop
+    end
+
+    local command
+    $command = $$1 -limit $POLLLIMIT
+
+    # Only process the data with the specified label.
+    if ($?LABEL && $LABEL != "NONE")
+	$command = $command -label $LABEL
+    end
+
+    $$1 = $command
+end
+
+macro add_standard_args
+  if ($0 != 2)
+    echo "Must pass in the command of interest, and this function will supplement"
+    stop
+  end
+
+  local command
+  $command = $$1
+
+  if ($?DBNAME && "$DBNAME" != "DEFAULT")
+    $command = $command --dbname $DBNAME
+  end
+  if ($?NOOP != 0) 
+    if ($NOOP != 0)
+      $command = $command --no-op
+    end
+  end
+  if ($?NOUPDATE != 0) 
+    if ($NOUPDATE != 0)
+      $command = $command --no-update
+    end
+  end
+  $$1 = $command --verbose
+end
+
+
+macro process_exit
+  if ($0 != 4)
+    echo "USAGE: process_exit (bookName) (pageName) (exitCode)"
+    break
+  end
+
+  $bookName = $1
+  $pageName = $2
+  $exitCode = $3
+
+  if ($VERBOSE > 4)
+    echo "*** exit status ***"
+    echo "JOB_STATUS: $JOB_STATUS"
+    echo "JOB_HOSTNAME: $JOB_HOSTNAME"
+    echo "JOB_DTIME: $JOB_DTIME"
+    echo "*** stdout ***"
+    queueprint stdout
+    echo "*** stderr ***"
+    queueprint stderr
+  end
+
+  # success
+  if ($exitCode == $EXIT_SUCCESS) 
+    # the handler scripts update DB the tables; here we just update the page
+    book setword $bookName $pageName pantaskState DONE
+    return
+  end
+
+  # failure related to the data files
+  # jobs which result in DATAERR must have db pantaskState updated 
+  if ($exitCode == $EXIT_DATA_ERR)
+    showcommand failure
+    book setword $bookName $pageName pantaskState DATA_ERR
+    return
+  end
+
+  if ($VERBOSE)
+    echo "*** stdout ***"
+    queueprint stdout
+    echo "*** stderr ***"
+    queueprint stderr
+  end
+
+  # failure related to the data files
+  if ($exitCode == $EXIT_SYS_ERR)
+    # stop
+    showcommand "system failure"
+    book setword $bookName $pageName pantaskState SYS_ERR
+    return
+  end
+
+  # failure related to the data files
+  if ($exitCode == $EXIT_CONFIG_ERR)
+    # stop
+    showcommand "config error"
+    book setword $bookName $pageName pantaskState CONFIG_ERR
+    return
+  end
+
+  # failure related to the data files
+  if ($exitCode == $EXIT_PROG_ERR)
+    # stop
+    showcommand "programming error"
+    book setword $bookName $pageName pantaskState PROG_ERR
+    return
+  end
+
+  # any other exit status
+  showcommand failure
+  book setword $bookName $pageName pantaskState UNKNOWN_ERR
+end
+
+## XXX for the moment, remove all errors
+macro process_cleanup
+  if ($0 != 2)
+    echo "USAGE: process_cleanup (bookname)"
+    break
+  end
+
+  book delpage $1 -key pantaskState DONE
+
+  if (not($KEEP_FAILURES))
+    book delpage $1 -key pantaskState SYS_ERR
+    book delpage $1 -key pantaskState DATA_ERR
+    book delpage $1 -key pantaskState PROG_ERR
+    book delpage $1 -key pantaskState CONFIG_ERR
+    book delpage $1 -key pantaskState UNKNOWN_ERR
+  end
+end
+
+macro set.verbosity
+  if ($0 != 2)
+    echo "USAGE: set.verbosity (level)"
+    break
+  end
+  $VERBOSE = $1
+end
+
+macro show.variables
+  ??
+end
+
+macro set.workdir.by.camera
+  if ($0 != 6)
+    echo "USAGE: set.workdir.by.camera (camera) (class_id) (template) (default) (varname)"
+    break
+  end
+
+  local host default template camera classID varname
+  $camera = $1
+  $classID = $2
+  $template = $3
+  $default = $4
+  $varname = $5
+
+  if ("$template" == "NULL")
+    $varname = $workdir_template
+    echo "WARNING: WORKDIR template not set.  Defaulting to $workdir_template"
+    break
+  end
+
+  # missing camera and/or ipphosts table results in host = NULL
+  book getword ipphosts $camera $classID -var host
+
+  if ("$host" == "NULL")
+    strsub $template @HOST@ $default -var $varname
+  else
+    strsub $template @HOST@ $host -var $varname
+  end
+end
+
+macro set.workdir.by.skycell
+  if ($0 != 5)
+    echo "USAGE: set.workdir.by.skycell (skycellID) (template) (default) (varname)"
+    break
+  end
+
+  local host default template skycellID varname count
+  $skycellID = $1
+  $template = $2
+  $default = $3
+  $varname = $4
+
+  if ("$template" == "NULL")
+    $varname = $workdir_template
+    echo "WARNING: WORKDIR template not set.  Defaulting to $workdir_template"
+    break
+  end
+
+  # get the folding count for this camera  
+  book getword ipphosts skycell count -var count
+  if ("$count" == "NULL")
+    strsub $template @HOST@ $default -var $varname
+    return
+  end    
+
+  strhash $skycellID $count -var skyhash
+  sprintf skyname "sky%02d" $skyhash
+
+  # missing ipphosts table results in host = NULL
+  book getword ipphosts skycell $skyname -var host
+
+  if ("$host" == "NULL")
+    strsub $template @HOST@ $default -var $varname
+  else
+    strsub $template @HOST@ $host -var $varname
+  end
+end
+
+macro set.host.for.camera
+  if ($0 != 3)
+    echo "USAGE: set.host.for.camera (camera) (class_id)"
+    break
+  end
+
+  local host
+
+  if (not($PARALLEL))
+    host local
+    return
+  end
+
+  # missing camera and/or ipphosts table results in host = NULL
+  book getword ipphosts $1 $2 -var host
+
+  if ("$host" == "NULL")
+    host anyhost
+  else
+    host $host
+  end
+end
+
+macro set.host.for.skycell
+  if ($0 != 2)
+    echo "USAGE: set.host.for.skycell (skycellID)"
+    break
+  end
+
+  local skycellID varname count host skyname skyhash
+  $skycellID = $1
+
+  # get the folding count for this camera  
+  book getword ipphosts skycell count -var count
+  if ("$count" == "NULL")
+    host anyhost
+    return
+  end    
+
+  strhash $skycellID $count -var skyhash
+  sprintf skyname "sky%02d" $skyhash
+
+  # missing ipphosts table results in host = NULL
+  book getword ipphosts skycell $skyname -var host
+
+  if ("$host" == "NULL")
+    host anyhost
+  else
+    host $host
+  end
+end
+
+macro get.host.for.camera
+  if ($0 != 3)
+    echo "USAGE: get.host.for.camera (camera) (class_id)"
+    break
+  end
+
+  if (not($PARALLEL))
+    echo local
+    return
+  end
+
+  # missing camera and/or ipphosts table results in host = NULL
+  book getword ipphosts $1 $2 -var host
+
+  if ("$host" == "NULL")
+    echo anyhost
+  else
+    echo $host
+  end
+end
+
+macro check.globals
+  if ($?NETWORK == 0)
+    echo "NETWORK not defined: load pantasks.pro first"
+    break
+  end
+  if ($?PARALLEL == 0)
+    echo "PARALLEL not defined: load pantasks.pro first"
+    break
+  end
+  if ($?VERBOSE == 0)
+    echo "VERBOSE not defined: load pantasks.pro first"
+    break
+  end
+  if ($?LOGDIR == 0)
+    echo "LOGDIR not defined: load pantasks.pro first"
+    break
+  end
+  if ($?KEEP_FAILURES == 0)
+    echo "KEEP_FAILURES not defined: load pantasks.pro first"
+    break
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/pstamp.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/pstamp.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/pstamp.pro	(revision 22322)
@@ -0,0 +1,355 @@
+
+#$VERBOSE = 3
+
+# these variables wil cycle through the known database names
+
+$pstampDS_DB = 0
+$pstampReq_DB = 0
+$pstampJob_DB = 0
+$pstampFin_DB = 0
+
+macro pstamp.reset
+    book init pstampRequest
+    book init pstampJob
+    book init pstampFinish
+end
+
+pstamp.reset
+
+
+macro pstamp.on
+    task pstamp.request.find
+        active true
+    end
+    task pstamp.request.load
+        active true
+    end
+    task pstamp.request.run
+        active true
+    end
+
+    task request.finish.load
+        active true
+    end
+    task request.finish.run
+        active true
+    end
+
+    task pstamp.job.load
+        active true
+    end
+    task pstamp.job.run
+        active true
+    end
+end
+
+macro pstamp.off
+    task pstamp.request.find
+        active false
+    end
+    task pstamp.request.load
+        active false
+    end
+    task pstamp.request.run
+        active false
+    end
+    task request.finish.load
+        active false
+    end
+    task request.finish.run
+        active false
+    end
+
+    task pstamp.job.load
+        active false
+    end
+    task pstamp.job.run
+        active false
+    end
+end
+
+task pstamp.request.find
+    host        local
+
+    periods     -poll $LOADPOLL
+    periods     -exec 10
+    periods     -timeout 30
+    npending    1
+
+    task.exec
+        if ($DB:n == 0)
+            option DEFAULT
+            command pstamp_queue_requests.pl --limit 5
+        else 
+            option $DB:$pstampDS_DB
+            command pstamp_queue_requests.pl --limit 5 --dbname $DB:$pstampDS_DB --verbose
+            $pstampDS_DB ++
+            if ($pstampDS_DB >= $DB:n) set pstampDS_DB = 0
+        end
+    end
+
+    task.exit $EXIT_SUCCESS
+        # echo nothing to do
+    end
+
+    task.exit   default
+        showcommand failure
+    end
+
+    task.exit   timeout
+        showcommand timeout
+    end
+end
+
+task pstamp.request.load
+    host        local
+
+    periods     -poll $LOADPOLL
+    periods     -exec $LOADEXEC
+    periods     -timeout 300
+    npending    1
+
+    task.exec
+        if ($DB:n == 0)
+            option DEFAULT
+            command pstamptool  -pendingreq
+        else 
+            option $DB:$pstampReq_DB
+            command pstamptool  -pendingreq -dbname $DB:$pstampReq_DB
+            $pstampReq_DB ++
+            if ($pstampReq_DB >= $DB:n) set pstampReq_DB = 0
+        end
+    end
+
+    task.exit $EXIT_SUCCESS
+        ipptool2book stdout pstampRequest -key req_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+        if ($VERBOSE > 2)
+            echo starting request
+            book listbook pstampRequest
+        end
+
+        process_cleanup pstampRequest
+    end
+
+    task.exit   default
+        showcommand failure
+    end
+
+    task.exit   timeout
+        showcommand timeout
+    end
+end
+
+task pstamp.request.run
+    periods     -poll $RUNPOLL
+    periods     -exec $RUNEXEC
+    periods     -timeout 30
+    npending    1
+
+    task.exec
+        book npages pstampRequest -var N
+        if ($N == 0) break
+        
+        book getpage pstampRequest 0 -var pageName -key pantaskState INIT
+        if ("$pageName" == "NULL") break
+
+        book setword pstampRequest $pageName pantaskState RUN
+        book getword pstampRequest $pageName req_id -var REQ_ID
+        book getword pstampRequest $pageName dbname -var DBNAME
+
+        $run = pstamp_parser_run.pl --req_id $REQ_ID
+
+        add_standard_args run
+        options $pageName
+
+        if ($VERBOSE > 1) 
+            echo command $run
+        end
+        command $run
+    end
+
+
+    task.exit default
+#        echo pstamp.request.run task.exit status: $JOB_STATUS
+        process_exit pstampRequest $options:0 $JOB_STATUS
+    end
+
+    task.exit timeout
+        showcommand timeout
+        book setword pstampRequest $options:0 pantaskState TIMEOUT
+    end
+end
+
+task request.finish.load
+    host        local
+
+    periods     -poll $LOADPOLL
+    periods     -exec $LOADEXEC
+    periods     -timeout 300
+    npending    1
+
+    task.exec
+        if ($DB:n == 0)
+            option DEFAULT
+            command pstamptool  -completedreq
+        else 
+            option $DB:$pstampFin_DB
+            command pstamptool  -completedreq -dbname $DB:$pstampFin_DB
+            $pstampFin_DB ++
+            if ($pstampFin_DB >= $DB:n) set pstampFin_DB = 0
+        end
+    end
+
+    task.exit $EXIT_SUCCESS
+        ipptool2book stdout pstampFinish -key req_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+        if ($VERBOSE > 2)
+            book listbook pstampFinish
+        end
+
+        process_cleanup pstampFinish
+    end
+
+    task.exit   default
+        showcommand failure
+    end
+
+    task.exit   timeout
+        showcommand timeout
+    end
+end
+
+task request.finish.run
+    periods     -poll $RUNPOLL
+    periods     -exec $RUNEXEC
+    periods     -timeout 30
+    npending    1
+
+    task.exec
+        book npages pstampFinish -var N
+        if ($N == 0) break
+        
+        book getpage pstampFinish 0 -var pageName -key pantaskState INIT
+        if ("$pageName" == "NULL") break
+
+        book setword pstampFinish $pageName pantaskState RUN
+        book getword pstampFinish $pageName req_id -var REQ_ID
+        book getword pstampFinish $pageName uri -var URI
+        book getword pstampFinish $pageName dbname -var DBNAME
+        book getword pstampFinish $pageName reqType -var REQ_TYPE
+        book getword pstampFinish $pageName name -var REQ_NAME
+        book getword pstampFinish $pageName outProduct -var PRODUCT
+
+        $run = request_finish.pl --req_id $REQ_ID --req_type $REQ_TYPE --req_file $URI --req_name $REQ_NAME --product $PRODUCT
+
+        add_standard_args run
+        options $pageName
+
+        if ($VERBOSE > 1) 
+            echo command $run
+        end
+        command $run
+    end
+
+    task.exit default
+        # echo request.finish.run task.exit status: $JOB_STATUS
+        process_exit pstampFinish $options:0 $JOB_STATUS
+    end
+
+    task.exit timeout
+        showcommand timeout
+        book setword pstampFinish $options:0 pantaskState TIMEOUT.run
+    end
+end
+
+task pstamp.job.load
+    host        local
+
+    periods     -poll $LOADPOLL
+    periods     -exec $LOADEXEC
+    periods     -timeout 30
+    npending    1
+
+    task.exec
+        if ($DB:n == 0)
+            option DEFAULT
+            command pstamptool -pendingjob -limit 10
+        else
+            option $DB:$pstampJob_DB
+            command pstamptool -pendingjob -limit 10 -dbname $DB:$pstampJob_DB
+            $pstampJob_DB ++
+            if ($pstampJob_DB >= $DB:n) set pstampJob_DB = 0
+        end
+    end
+
+    task.exit $EXIT_SUCCESS
+        # echo ipptool2book stdout pstampJob -key job_id -setword dbname $options:0 -setword pantaskState INIT
+        ipptool2book stdout pstampJob -key job_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+
+        book npages pstampJob -var N
+        if ($VERBOSE > 2)
+            book listbook pstampJob
+        end
+
+        # delete existing entries in the appropriate pantaskStates
+        process_cleanup pstampJob
+    end
+
+    task.exit   default
+        showcommand failure
+    end
+
+    task.exit   timeout
+        showcommand timeout
+    end
+end
+
+task pstamp.job.run
+    periods     -poll $RUNPOLL
+    periods     -exec $RUNEXEC
+    periods     -timeout 1200
+    npending    1
+
+    task.exec
+        book npages pstampJob -var N
+        if ($N == 0) break
+        
+        book getpage pstampJob 0 -var pageName -key pantaskState INIT
+        if ("$pageName" == "NULL") break
+
+        #echo pageName: $pageName
+
+        book setword pstampJob $pageName pantaskState RUN
+        book getword pstampJob $pageName job_id -var JOB_ID
+        book getword pstampJob $pageName dbname -var DBNAME
+
+        if ($VERBOSE > 1) 
+            book listpage pstampJob $pageName
+        end
+
+
+        $run = pstamp_job_run.pl --job_id $JOB_ID
+        add_standard_args run
+
+        options $pageName
+
+        if ($VERBOSE > 1) 
+            echo command $run
+        end
+        command $run
+    end
+
+
+    task.exit default
+        if ($VERBOSE > 1)
+            echo pstamp.job.run task.exit $JOB_ID status: $JOB_STATUS
+        end
+        process_exit pstampJob $options:0 $JOB_STATUS
+    end
+
+    task.exit timeout
+        echo pstamp.job.run task.timeout $JOB_ID status: $JOB_STATUS
+        process_exit pstampJob $options:0 $JOB_STATUS
+        showcommand timeout
+        book setword pstampJob $options:0 pantaskState TIMEOUT
+    end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/register.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/register.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/register.pro	(revision 22322)
@@ -0,0 +1,308 @@
+## register.pro : tasks for image registration (insert into database) : -*- sh -*-
+## this file contains the tasks for running the registration stage
+## these tasks use the books regPendingImfile and regPendingExp
+
+# test for required global variables
+check.globals
+
+book init regPendingImfile
+book init regPendingExp
+
+macro register.reset
+  book init regPendingImfile
+  book init regPendingExp
+end
+
+macro register.status
+  book listbook regPendingImfile
+  book listbook regPendingExp
+end
+
+macro register.on
+  task register.imfile.load
+    active true
+  end
+  task register.imfile.run
+    active true
+  end
+  task register.exp.load
+    active true
+  end
+  task register.exp.run
+    active true
+  end
+end
+
+macro register.off
+  task register.imfile.load
+    active false
+  end
+  task register.imfile.run
+    active false
+  end
+  task register.exp.load
+    active false
+  end
+  task register.exp.run
+    active false
+  end
+end
+
+# these variables will cycle through the known database names
+$regPendingImfile_DB = 0
+$regPendingExp_DB = 0
+
+# select images ready for register analysis
+# new entries are added to regPendingImfile
+# compare the new list with the ones already selected
+task	       register.imfile.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  # silently drop stdout
+  stdout NULL
+  stderr $LOGDIR/register.imfile.load.log
+
+  # 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
+    $run = regtool -pendingimfile
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$regPendingImfile_DB
+      $run = $run -dbname $DB:$regPendingImfile_DB
+      $regPendingImfile_DB ++
+      if ($regPendingImfile_DB >= $DB:n) set regPendingImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit $EXIT_SUCCESS
+    # convert 'stdout' to book format
+    ipptool2book stdout regPendingImfile -key exp_id:tmp_class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    book shuffle regPendingImfile
+    if ($VERBOSE > 2)
+      book listbook regPendingImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup regPendingImfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the register_imfile.pl script on pending images
+task	       register.imfile.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    if ($NETWORK == 0) break
+    book npages regPendingImfile -var N
+    if ($N == 0)
+        periods -exec $RUNEXEC
+        break
+    end
+    periods -exec 0.05 
+    
+    # look for new images in regPendingImfile
+    book getpage regPendingImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword regPendingImfile $pageName pantaskState RUN
+
+    book getword regPendingImfile $pageName exp_id       -var EXP_ID
+    book getword regPendingImfile $pageName tmp_exp_name -var TMP_EXP_NAME
+    book getword regPendingImfile $pageName tmp_camera   -var TMP_CAMERA
+    book getword regPendingImfile $pageName tmp_class_id -var TMP_CLASS_ID
+    book getword regPendingImfile $pageName uri          -var URI
+    book getword regPendingImfile $pageName workdir      -var WORKDIR_TEMPLATE
+    book getword regPendingImfile $pageName dbname       -var DBNAME
+
+    # EXP_TAG is used to generate the unique, but human-readable, filenames
+    sprintf EXP_TAG "%s.%s" $TMP_EXP_NAME $EXP_ID
+
+    # specify choice of remote host
+    set.host.for.camera $TMP_CAMERA $TMP_CLASS_ID 
+
+    # set the WORKDIR variable
+    set.workdir.by.camera $TMP_CAMERA $TMP_CLASS_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    # notes on how this works:
+    # -- raw workdir examples:
+    # file://data/@HOST@.0/gpc1/20080130
+    # neb:///@HOST@-vol0/gpc1/20080130 (need to supply volname?, or are we re-defining this each time?)
+    # -- out workdir examples:
+    # file://data/ipp004.0/gpc1/20080130
+    # neb:///ipp004-vol0/gpc1/20080130
+
+    ## generate outroot specific to this exposure (& chip)
+    sprintf logfile "%s/%s/%s.reg.%s.log" $WORKDIR $EXP_TAG $EXP_TAG $TMP_CLASS_ID
+
+    stdout $LOGDIR/register.imfile.run.log
+    stderr $LOGDIR/register.imfile.run.log
+
+    # XXX register_imfile.pl differs from the standard script : it does not have an 'outroot' argument, and it does not take '--redirect'
+    $run = register_imfile.pl --exp_id $EXP_ID --tmp_class_id $TMP_CLASS_ID --tmp_exp_name $TMP_EXP_NAME --uri $URI --logfile $logfile
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit default
+    process_exit regPendingImfile $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword regPendingImfile $options:0 pantaskState TIMEOUT
+  end
+end
+
+# select exposures ready for register_exp.pl
+task	       register.exp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/register.exp.load.log
+
+  task.exec
+    $run = regtool -pendingexp
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$regPendingExp_DB
+      $run = $run -dbname $DB:$regPendingExp_DB
+      $regPendingExp_DB ++
+      if ($regPendingExp_DB >= $DB:n) set regPendingExp_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit $EXIT_SUCCESS
+    # convert 'stdout' to book format
+    ipptool2book stdout regPendingExp -key exp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook regPendingExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup regPendingExp
+  end
+
+  # default exit status
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the register_exp.pl script on pending exposures
+task	       register.exp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 30
+
+  task.exec
+    book npages regPendingExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in regPendingExp (pantaskState == INIT)
+    book getpage regPendingExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword regPendingExp $pageName pantaskState RUN
+    book getword regPendingExp $pageName exp_id       -var EXP_ID
+    book getword regPendingExp $pageName tmp_exp_name -var TMP_EXP_NAME
+    book getword regPendingExp $pageName tmp_camera   -var TMP_CAMERA
+    book getword regPendingExp $pageName workdir      -var WORKDIR_TEMPLATE
+    book getword regPendingExp $pageName dbname       -var DBNAME
+
+    # EXP_TAG is used to generate the unique, but human-readable, filenames
+    sprintf EXP_TAG "%s.%s" $TMP_EXP_NAME $EXP_ID
+
+    # specify choice of remote host
+    set.host.for.camera $TMP_CAMERA $TMP_CLASS_ID 
+
+    # set the WORKDIR variable
+    set.workdir.by.camera $TMP_CAMERA $TMP_CLASS_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    # notes on how this works:
+    # -- raw workdir examples:
+    # file://data/@HOST@.0/gpc1/20080130
+    # neb:///@HOST@-vol0/gpc1/20080130 (need to supply volname?, or are we re-defining this each time?)
+    # -- out workdir examples:
+    # file://data/ipp004.0/gpc1/20080130
+    # neb:///ipp004-vol0/gpc1/20080130
+    ## generate output log based on filerule
+    sprintf logfile "%s/%s/%s.reg.log" $WORKDIR $EXP_TAG $EXP_TAG
+
+    stdout $LOGDIR/register.exp.run.log
+    stderr $LOGDIR/register.exp.run.log
+
+    $run = register_exp.pl --exp_id $EXP_ID --exp_tag $EXP_TAG --logfile $logfile
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # success
+  task.exit default
+    process_exit regPendingExp $options:0 $JOB_STATUS
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword regPendingExp $options:0 pantaskState TIMEOUT
+  end
+end
+
+## XXX add a global path to output files  
Index: /tags/sj_tags/sj_root_20080929/ippTasks/replicate.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/replicate.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/replicate.pro	(revision 22322)
@@ -0,0 +1,166 @@
+## replicate.pro : tasks for data replication : -*- sh -*-
+## this file contains the tasks for maintaining duplicates as needed in Nebulous
+## these tasks use the books replicatePending
+
+# test for required global variables
+check.globals
+
+# load in the local configuration (NEB_USER, etc.)
+module nebulous.site.pro
+
+$LOGSUBDIR = $LOGDIR/replicate
+exec mkdir -p $LOGSUBDIR
+
+book init replicatePending
+
+macro replicate.reset
+  book init replicatePending
+end
+
+macro replicate.status
+  book listbook replicatePending
+end
+
+macro replicate.on
+  task replicate.load
+    active true
+  end
+  task replicate.run
+    active true
+  end
+end
+
+macro replicate.off
+  task replicate.load
+    active false
+  end
+  task replicate.run
+    active false
+  end
+end
+
+macro set.host.for.replicate
+  if ($0 != 2)
+    echo "USAGE: set.host.for.replicate (hostname)"
+    break
+  end
+
+  if (not($PARALLEL))
+    host local
+    return
+  end
+
+# parse volume name
+
+  if ("$1" == "NULL")
+    host anyhost
+  else
+    host $1
+  end
+end
+
+# the replicate process interacts with only the single Nebulous server
+
+# select Nebulous objects which desire additional copies
+task	       replicate.load
+  host         local
+
+  # modify these after the tasks are tested
+  periods      -poll 10
+  periods      -exec 1
+  periods      -timeout 1500
+  npending     1
+
+  # silently drop stdout
+  stdout NULL
+  stderr $LOGSUBDIR/replicate.log
+
+  task.exec
+      # command does not need to be dynamic, but having it so allows us to adjust the periods
+      # so that we dont have to wait 10 minutes for things to start up
+      command neb-admin --host $NEB_HOST --db $NEB_DB --user $NEB_USER --pass $NEB_PASS --pendingreplicate --limit 7500
+      periods      -exec 1800
+
+  end
+
+  # success
+  task.exit $EXIT_SUCCESS
+    # 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
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# create the desired replicas
+task	       replicate.run
+  periods      -poll $RUNPOLL
+  periods      -exec 30
+  periods      -timeout 30
+
+  task.exec
+    book npages replicatePending -var N
+    if ($NETWORK == 0) break
+    if ($N == 0)
+        periods -exec 30
+        break
+    end
+    periods -exec 0.05
+    
+    # look for new objects in replicatePending
+    book getpage replicatePending 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword replicatePending $pageName pantaskState RUN
+
+    # XXX what values do I need to get back?
+    book getword replicatePending $pageName key         -var KEY
+    book getword replicatePending $pageName need_copies -var NEED_COPIES
+    book getword replicatePending $pageName volume_name -var VOLUME_NAME
+    book getword replicatePending $pageName volume_host -var VOLUME_HOST
+
+    set.host.for.replicate $VOLUME_HOST
+
+    stdout NULL
+    stderr $LOGSUBDIR/replicate.log
+
+    # these operations do not require a database to be specified
+    $run = neb-replicate --copies $NEED_COPIES $KEY
+
+    # 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 replicatePending $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword replicatePending $options:0 pantaskState TIMEOUT
+  end
+end
+
Index: /tags/sj_tags/sj_root_20080929/ippTasks/simtest.auto
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/simtest.auto	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/simtest.auto	(revision 22322)
@@ -0,0 +1,119 @@
+
+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 SIMTEST -dbname @DBNAME@"
+  ncheck     S32 20
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst SIMTEST -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 SIMTEST -det_type BIAS -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst SIMTEST -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 SIMTEST -det_type DARK -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst SIMTEST -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 SIMTEST -det_type SHUTTER -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst SIMTEST -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
+
+automate METADATA
+  name       STR FLAT-i
+  check      STR "detselect -search -inst SIMTEST -det_type SHUTTER -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst SIMTEST -det_type FLAT -filter i -select_exp_type FLAT -select_filter i -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type FLAT -filter i -dbname @DBNAME@"
+END
+
+automate METADATA
+  name       STR OBJECT-i
+  check      STR "detselect -dbname @DBNAME@ -search    -inst SIMTEST -filter i -det_type FLAT"
+  launch     STR "chiptool  -dbname @DBNAME@ -updaterun -inst SIMTEST -filter i -label wait -set_label proc"
+  block      STR NONE
+END
+
+automate METADATA
+  name       STR OBJECT-r
+  check      STR "detselect -dbname @DBNAME@ -search    -inst SIMTEST -filter r -det_type FLAT"
+  launch     STR "chiptool  -dbname @DBNAME@ -updaterun -inst SIMTEST -filter r -label wait -set_label proc"
+  block      STR NONE
+END
+
+### Stack automation???
+automate METADATA
+  name       STR STACK
+  regular    STR "stacktool -definebyquery -all -workdir file://@CWD@/stack -min_new 4 -min_frac 2 -select_good_frac_min 0.2 -dbname @DBNAME@"
+END
+
+### Diff automation???
+automate METADATA
+  name       STR DIFF
+  regular    STR "difftool -definebyquery -workdir file://@CWD@/diff -good_frac 0.2 -dbname @DBNAME@"
+END
+
+### Magic automation???
+automate METADATA
+  name       STR MAGIC
+  regular    STR "magictool -definebyquery -workdir file://@CWD@/magic -good_frac 0.2 -dbname @DBNAME@"
+END
+
+
+
+
+### automate METADATA
+###   name       STR OBJECT-r
+###   check      STR "detselect -search -inst SIMTEST -det_type FLAT -dbname @DBNAME@ -filter r"
+###   launch     STR "chiptool -unblock -label simtest-r -dbname @DBNAME@"
+###   block      STR "chiptool -unmasked -label simtest-r -dbname @DBNAME@"
+### END
+
+### automate METADATA
+###   name       STR OBJECT-i
+###   check      STR "detselect -search -inst SIMTEST -det_type FLAT -dbname @DBNAME@ -filter i"
+###   launch     STR "chiptool -unblock -label simtest-i -dbname @DBNAME@"
+###   block      STR "chiptool -unmasked -label simtest-i -dbname @DBNAME@"
+### END
+
+### there is a weakness in the label / block business: the labels are
+### generic, and the blocks are against those fairly generic words.
+### that makes it difficult to block and unblock science exposures
+### based on different detrend types that are available.  For example,
+### to block the r-band against the absence of the desired r-band
+### flat, we would need to specify a label specific to the r-band
+### images and remove that block when ready.
+
+### a better approach might be to modify the labels rather than the
+### blocks.  we can define, eg, chiptool -set_label -definebyquery to
+### turn assign the label names based on various properties of the
+### interesting images.
+
+### here is what the automate element might look like for such a circumstance:
+
+### automate METADATA
+###   name       STR OBJECT-i
+###   block      STR "chiptool -dbname @DBNAME@ -unmasked -label wait -filter i -time_begin 2008/1/1 -time_end 2008/1/2"
+###   check      STR "detselect -dbname @DBNAME@ -search -inst SIMTEST -det_type FLAT -dbname @DBNAME@ -filter i -time_begin 2008/1/1 -time_end 2008/1/2" 
+###   launch     STR "chiptool -dbname @DBNAME@ -set_label proc -label wait -dbname @DBNAME@ -time_begin 2008/1/1 -time_end 2008/1/2"
+### END
+
+### XXX still not quite there....
+### the 'checks' or maybe the 'launch' needs to include a '-n_exp_min' ?
+### 
Index: /tags/sj_tags/sj_root_20080929/ippTasks/simtest.basic.auto
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/simtest.basic.auto	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/simtest.basic.auto	(revision 22322)
@@ -0,0 +1,119 @@
+
+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 20
+  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
+
+automate METADATA
+  name       STR FLAT-i
+  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 i -select_exp_type FLAT -select_filter i -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type FLAT -filter i -dbname @DBNAME@"
+END
+
+automate METADATA
+  name       STR OBJECT-i
+  check      STR "detselect -dbname @DBNAME@ -search    -inst @CAMERA@ -filter i -det_type FLAT"
+  launch     STR "chiptool  -dbname @DBNAME@ -updaterun -inst @CAMERA@ -filter i -label wait -set_label proc"
+  block      STR NONE
+END
+
+automate METADATA
+  name       STR OBJECT-r
+  check      STR "detselect -dbname @DBNAME@ -search    -inst @CAMERA@ -filter r -det_type FLAT"
+  launch     STR "chiptool  -dbname @DBNAME@ -updaterun -inst @CAMERA@ -filter r -label wait -set_label proc"
+  block      STR NONE
+END
+
+### Stack automation???
+automate METADATA
+  name       STR STACK
+  regular    STR "stacktool -definebyquery -all -workdir file://@CWD@/stack -min_new 4 -min_frac 2 -select_good_frac_min 0.2 -dbname @DBNAME@"
+END
+
+### Diff automation???
+automate METADATA
+  name       STR DIFF
+  regular    STR "difftool -definebyquery -workdir file://@CWD@/diff -good_frac 0.2 -dbname @DBNAME@"
+END
+
+### Magic automation???
+automate METADATA
+  name       STR MAGIC
+  regular    STR "magictool -definebyquery -workdir file://@CWD@/magic -good_frac 0.2 -dbname @DBNAME@"
+END
+
+
+
+
+### automate METADATA
+###   name       STR OBJECT-r
+###   check      STR "detselect -search -inst @CAMERA@ -det_type FLAT -dbname @DBNAME@ -filter r"
+###   launch     STR "chiptool -unblock -label simtest-r -dbname @DBNAME@"
+###   block      STR "chiptool -unmasked -label simtest-r -dbname @DBNAME@"
+### END
+
+### automate METADATA
+###   name       STR OBJECT-i
+###   check      STR "detselect -search -inst @CAMERA@ -det_type FLAT -dbname @DBNAME@ -filter i"
+###   launch     STR "chiptool -unblock -label simtest-i -dbname @DBNAME@"
+###   block      STR "chiptool -unmasked -label simtest-i -dbname @DBNAME@"
+### END
+
+### there is a weakness in the label / block business: the labels are
+### generic, and the blocks are against those fairly generic words.
+### that makes it difficult to block and unblock science exposures
+### based on different detrend types that are available.  For example,
+### to block the r-band against the absence of the desired r-band
+### flat, we would need to specify a label specific to the r-band
+### images and remove that block when ready.
+
+### a better approach might be to modify the labels rather than the
+### blocks.  we can define, eg, chiptool -set_label -definebyquery to
+### turn assign the label names based on various properties of the
+### interesting images.
+
+### here is what the automate element might look like for such a circumstance:
+
+### automate METADATA
+###   name       STR OBJECT-i
+###   block      STR "chiptool -dbname @DBNAME@ -unmasked -label wait -filter i -time_begin 2008/1/1 -time_end 2008/1/2"
+###   check      STR "detselect -dbname @DBNAME@ -search -inst @CAMERA@ -det_type FLAT -dbname @DBNAME@ -filter i -time_begin 2008/1/1 -time_end 2008/1/2" 
+###   launch     STR "chiptool -dbname @DBNAME@ -set_label proc -label wait -dbname @DBNAME@ -time_begin 2008/1/1 -time_end 2008/1/2"
+### END
+
+### XXX still not quite there....
+### the 'checks' or maybe the 'launch' needs to include a '-n_exp_min' ?
+### 
Index: /tags/sj_tags/sj_root_20080929/ippTasks/simtest.basic.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/simtest.basic.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/simtest.basic.config	(revision 22322)
@@ -0,0 +1,112 @@
+
+# define the simulated data to be generated
+SEQUENCE MULTI
+
+SEQUENCE METADATA
+  OBSTYPE    STR BIAS
+  CAMERA     STR SIMTEST
+  NIMAGES    S32  20
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR DARK
+  CAMERA     STR SIMTEST
+  @EXPTIMES  F32  30.0, 300.0
+  @NIMAGES   S32  10,   10
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR FLAT
+  CAMERA     STR SIMTEST
+
+  FILTERS    STR r,r,i
+  @EXPTIMES  F32 0.1,20.0,20.0
+
+  NSETUP     S32 5
+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 OBJECT
+  CAMERA     STR SIMTEST
+
+### Carina
+  CENTER.RA  F32 270.75
+  CENTER.DEC F32 -23.7
+### COSMOS field
+#  CENTER.RA  F32 150.119167 
+#  CENTER.DEC F32   2.205833
+
+  OFFSET.RA  F32 3600.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.DEC F32 3600.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.NR  S32 1
+  OFFSET.ND  S32 1
+
+  DITHER.RA  F32 60.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.DEC F32 60.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.NR  S32 3
+  DITHER.ND  S32 2
+
+  DVODB      STR /data/alala.0/ipp/ippRefs/catdir.synth.simtest 
+  FILTERS    STR r,i
+  @EXPTIMES  F32 10.00,10.00
+  @SKYMAGS   F32 20.86,20.15
+
+  # XXX use a more realistic IQ distribution...
+  IQ_MIN     F32 0.55
+  IQ_MAX     F32 1.25
+  
+  # rotation sequence
+  POS_MIN    F32 0.0
+  POS_MAX    F32 0.0
+  POS_DELTA  F32 1.0
+
+END
+
+
+SEQUENCE METADATA
+  OBSTYPE    STR OBJECT
+  CAMERA     STR SIMTEST
+
+### Carina
+  CENTER.RA  F32 270.75
+  CENTER.DEC F32 -23.7
+### COSMOS field
+#  CENTER.RA  F32 150.119167 
+#  CENTER.DEC F32   2.205833
+
+  OFFSET.RA  F32 3600.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.DEC F32 3600.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.NR  S32 1
+  OFFSET.ND  S32 1
+
+  DITHER.RA  F32 30.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.DEC F32 30.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.NR  S32 1
+  DITHER.ND  S32 1
+
+  DVODB      STR /data/alala.0/ipp/ippRefs/catdir.synth.simtest 
+  FILTERS    STR r,r,i,i
+  @EXPTIMES  F32 5.0, 240.0, 5.0, 240.0
+  @SKYMAGS   F32 20.86,20.86,20.15,20.15
+
+  # XXX use a more realistic IQ distribution...
+  IQ_MIN     F32 0.55
+  IQ_MAX     F32 1.25
+  
+  # rotation sequence
+  POS_MIN    F32 0.0
+  POS_MAX    F32 0.0
+  POS_DELTA  F32 1.0
+
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippTasks/simtest.det
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/simtest.det	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/simtest.det	(revision 22322)
@@ -0,0 +1,42 @@
+
+detRunDef MULTI
+
+detRunDef METADATA
+  detRunName STR BIAS
+  prereq     STR NONE
+  options    STR "-definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type BIAS -select_exp_type BIAS"
+  dbname     STR eamtest
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName STR DARK
+  prereq     STR BIAS
+  options    STR "-definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type DARK -select_exp_type DARK"
+  dbname     STR eamtest
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName STR SHUTTER
+  prereq     STR DARK
+  options    STR "-definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type SHUTTER -filter r -select_exp_type FLAT -select_filter r"
+  dbname     STR eamtest
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName STR FLAT-r
+  prereq     STR SHUTTER
+  options    STR "-definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type FLAT -filter r -select_exp_type FLAT -select_filter r"
+  dbname     STR eamtest
+  pantaskState STR NULL
+END
+
+detRunDef METADATA
+  detRunName STR FLAT-i
+  prereq     STR SHUTTER
+  options    STR "-definebyquery -workdir path://EAMWORK -inst SIMTEST -det_type FLAT -filter i -select_exp_type FLAT -select_filter i"
+  dbname     STR eamtest
+  pantaskState STR NULL
+END
Index: /tags/sj_tags/sj_root_20080929/ippTasks/simtest.detverify.auto
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/simtest.detverify.auto	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/simtest.detverify.auto	(revision 22322)
@@ -0,0 +1,78 @@
+
+automate MULTI
+
+# we run in the sequence BLOCK -> CHECK -> LAUNCH -> BLOCK -> CHECK
+# success on CHECK -> LAUNCH
+# failure on BLOCK -> CHECK
+
+automate METADATA
+  name       STR BIAS
+  check      STR "regtool -processedexp -exp_type BIAS -inst @CAMERA@ -dbname @DBNAME@"
+  ncheck     S32 20
+  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 -mode master -dbname @DBNAME@"
+END
+
+automate METADATA
+  name       STR BIAS_VERIFY
+  check      STR "detselect -search -inst @CAMERA@ -det_type BIAS -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst @CAMERA@ -det_type BIAS -select_exp_type BIAS -mode verify -ref_det_id @det_id@ -ref_iter @iteration@ -dbname @DBNAME@" 
+  block      STR "dettool -runs -active -det_type BIAS -mode verify -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 -mode master -dbname @DBNAME@"
+END
+ 
+automate METADATA
+  name       STR DARK_VERIFY
+  check      STR "detselect -search -inst @CAMERA@ -det_type DARK -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst @CAMERA@ -det_type DARK -select_exp_type DARK -mode verify -ref_det_id @det_id@ -ref_iter @iteration@ -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type DARK -mode verify -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 -mode master -dbname @DBNAME@"
+END
+
+automate METADATA
+  name       STR SHUTTER_VERIFY
+  check      STR "detselect -search -inst @CAMERA@ -det_type SHUTTER -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst @CAMERA@ -det_type SHUTTER -filter r -select_exp_type FLAT -select_filter r -mode verify -ref_det_id @det_id@ -ref_iter @iteration@ -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type FLAT -filter r -mode verify -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 -mode master -dbname @DBNAME@"
+END
+
+automate METADATA
+  name       STR FLAT-i
+  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 i -select_exp_type FLAT -select_filter i -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type FLAT -filter i -mode master -dbname @DBNAME@"
+END
+
+automate METADATA
+  name       STR FLAT_VERIFY-r
+  check      STR "detselect -search -inst @CAMERA@ -det_type FLAT -filter r -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst @CAMERA@ -det_type FLAT -filter r -select_exp_type FLAT -select_filter r -mode verify -ref_det_id @det_id@ -ref_iter @iteration@ -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type FLAT -filter r -mode verify -dbname @DBNAME@"
+END
+
+automate METADATA
+  name       STR FLAT_VERIFY-i
+  check      STR "detselect -search -inst @CAMERA@ -det_type FLAT -filter i -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst @CAMERA@ -det_type FLAT -filter i -select_exp_type FLAT -select_filter i -mode verify -ref_det_id @det_id@ -ref_iter @iteration@ -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type FLAT -filter i -mode verify -dbname @DBNAME@"
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippTasks/simtest.detverify.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/simtest.detverify.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/simtest.detverify.config	(revision 22322)
@@ -0,0 +1,37 @@
+
+# define the simulated data to be generated
+SEQUENCE MULTI
+
+SEQUENCE METADATA
+  OBSTYPE    STR BIAS
+  CAMERA     STR SIMTEST
+  NIMAGES    S32  20
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR DARK
+  CAMERA     STR SIMTEST
+  @EXPTIMES  F32  30.0, 300.0
+  @NIMAGES   S32  10,   10
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR FLAT
+  CAMERA     STR SIMTEST
+
+  FILTERS    STR r,r,i
+  @EXPTIMES  F32 0.1,20.0,20.0
+
+  NSETUP     S32 5
+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
+
Index: /tags/sj_tags/sj_root_20080929/ippTasks/simtest.flatcorr.auto
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/simtest.flatcorr.auto	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/simtest.flatcorr.auto	(revision 22322)
@@ -0,0 +1,77 @@
+
+automate MULTI
+
+# we run in the sequence BLOCK -> CHECK -> LAUNCH -> BLOCK -> CHECK
+# success on CHECK -> LAUNCH
+# failure on BLOCK -> CHECK
+
+# this is a simplified automate script which only models BIAS and FLAT.  
+# generate appropriate simulated data with the FLATCORR recipe
+
+# wait until 20 bias frames are registered, then define a new bias run
+automate METADATA
+  name       STR BIAS
+  check      STR "regtool -processedexp -exp_type BIAS -inst @CAMERA@ -dbname @DBNAME@"
+  ncheck     S32 20
+  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
+
+# wait until the master bias frame is registered, then define a new dark run
+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
+
+# wait until the master dark frame is registered, then define a new shutter run
+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
+
+# wait until the master bias frame is registered, then define a new flat-r run
+automate METADATA
+  name       STR RAWFLAT-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_RAW -filter r -select_exp_type FLAT -select_filter r -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type FLAT_RAW -filter r -dbname @DBNAME@"
+END
+
+# wait until the master bias frame is registered, then define a new flat-i run
+automate METADATA
+  name       STR RAWFLAT-i
+  check      STR "detselect -search -inst @CAMERA@ -det_type SHUTTER -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst @CAMERA@ -det_type FLAT_RAW -filter i -select_exp_type FLAT -select_filter i -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type FLAT_RAW -filter i -dbname @DBNAME@"
+END
+
+## # wait until the master flat-r frame is registered, then unblock the r-band data for chip-level processing
+## automate METADATA
+##   name       STR OBJECT-i
+##   check      STR "detselect -dbname @DBNAME@ -search    -inst @CAMERA@ -filter i -det_type FLAT"
+##   launch     STR "chiptool  -dbname @DBNAME@ -updaterun -inst @CAMERA@ -filter i -label wait -set_label proc"
+##   block      STR NONE
+## END
+## 
+## # wait until the master flat-i frame is registered, then unblock the i-band data for chip-level processing
+## automate METADATA
+##   name       STR OBJECT-r
+##   check      STR "detselect -dbname @DBNAME@ -search    -inst @CAMERA@ -filter r -det_type FLAT"
+##   launch     STR "chiptool  -dbname @DBNAME@ -updaterun -inst @CAMERA@ -filter r -label wait -set_label proc"
+##   block      STR NONE
+## END
+## 
+## # wait until the r-band is done with camera, then define a new flat-correction run
+## automate METADATA
+##   name       STR FLATCORR-i
+##   check      STR "camtool -dbname @DBNAME@ -search -inst @CAMERA@ -filter r"
+##   ncheck     S32 8
+##   launch     STR "chiptool  -dbname @DBNAME@ -updaterun -inst @CAMERA@ -filter i -label wait -set_label proc"
+##   block      STR NONE
+## END
+
+## flatcorr -definebyquery -dbname eamtest -filter r -set_workdir /data/alala.0/eugene/swtests/simtest/test.f1/work -set_reduction FLATCORR -set_dvodb /data/alala.0/eugene/swtests/simtest/test.f1/catdir.flatcorr -set_region 270.6,270.9:-23.8,-23.5 -set_filter r
Index: /tags/sj_tags/sj_root_20080929/ippTasks/simtest.flatcorr.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/simtest.flatcorr.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/simtest.flatcorr.config	(revision 22322)
@@ -0,0 +1,189 @@
+
+# generate a simple fake data set with 2x2 dithers at offset of
+# 50, 100, 200 arcsec (camera is 530 arcsec on a side)
+
+# define the simulated data to be generated
+SEQUENCE MULTI
+
+SEQUENCE METADATA
+  OBSTYPE    STR BIAS
+  CAMERA     STR SIMTEST
+  NIMAGES    S32  20
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR DARK
+  CAMERA     STR SIMTEST
+  @EXPTIMES  F32  30.0, 300.0
+  @NIMAGES   S32  10,   10
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR FLAT
+  CAMERA     STR SIMTEST
+
+  FILTERS    STR r,r,i
+  @EXPTIMES  F32 0.1,20.0,20.0
+
+  NSETUP     S32 5
+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
+
+# a single image at the field center
+SEQUENCE METADATA
+  OBSTYPE    STR OBJECT
+  CAMERA     STR SIMTEST
+
+  ### Carina (modestly high density)
+  CENTER.RA  F32 270.75
+  CENTER.DEC F32 -23.7
+
+  # the simtest camera is 2k x 2k, 530 arcsec on a side
+  # XXX note that we cannot define the offsets in terms of the camera dimensions...
+
+  OFFSET.RA  F32 0.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.DEC F32 0.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.NR  S32 1
+  OFFSET.ND  S32 1
+
+  DITHER.RA  F32 0.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.DEC F32 0.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.NR  S32 1
+  DITHER.ND  S32 1
+
+  # XXX is DVODB used?
+  DVODB      STR /data/alala.0/ipp/ippRefs/catdir.synth.simtest 
+  FILTERS    STR r,i
+  @EXPTIMES  F32 10.00,10.00
+  @SKYMAGS   F32 20.86,20.15
+
+  # modest IQ range here
+  IQ_MIN     F32 0.70
+  IQ_MAX     F32 0.90
+  
+  # rotation sequence
+  POS_MIN    F32 0.0
+  POS_MAX    F32 0.0
+  POS_DELTA  F32 1.0
+
+END
+
+# dither set @ scale 1 = 60 arcsec
+SEQUENCE METADATA
+  OBSTYPE    STR OBJECT
+  CAMERA     STR SIMTEST
+
+  ### Carina (modestly high density)
+  CENTER.RA  F32 270.75
+  CENTER.DEC F32 -23.7
+
+  # XXX note that we cannot define the offsets in terms of the camera dimensions...
+  OFFSET.RA  F32 0.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.DEC F32 0.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.NR  S32 1
+  OFFSET.ND  S32 1
+
+  DITHER.RA  F32 60.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.DEC F32 60.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.NR  S32 2
+  DITHER.ND  S32 2
+
+  # XXX is DVODB used?
+  DVODB      STR /data/alala.0/ipp/ippRefs/catdir.synth.simtest 
+  FILTERS    STR r,i
+  @EXPTIMES  F32 10.00,10.00
+  @SKYMAGS   F32 20.86,20.15
+
+  # modest IQ range here
+  IQ_MIN     F32 0.70
+  IQ_MAX     F32 0.90
+  
+  # rotation sequence
+  POS_MIN    F32 0.0
+  POS_MAX    F32 0.0
+  POS_DELTA  F32 1.0
+
+END
+
+# dither set @ scale 2 = 120 arcsec
+SEQUENCE METADATA
+  OBSTYPE    STR OBJECT
+  CAMERA     STR SIMTEST
+
+  ### Carina (modestly high density)
+  CENTER.RA  F32 270.75
+  CENTER.DEC F32 -23.7
+
+  # XXX note that we cannot define the offsets in terms of the camera dimensions...
+  OFFSET.RA  F32 0.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.DEC F32 0.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.NR  S32 1
+  OFFSET.ND  S32 1
+
+  DITHER.RA  F32 120.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.DEC F32 120.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.NR  S32 2
+  DITHER.ND  S32 2
+
+  # XXX is DVODB used?
+  DVODB      STR /data/alala.0/ipp/ippRefs/catdir.synth.simtest 
+  FILTERS    STR r,i
+  @EXPTIMES  F32 10.00,10.00
+  @SKYMAGS   F32 20.86,20.15
+
+  # modest IQ range here
+  IQ_MIN     F32 0.70
+  IQ_MAX     F32 0.90
+  
+  # rotation sequence
+  POS_MIN    F32 0.0
+  POS_MAX    F32 0.0
+  POS_DELTA  F32 1.0
+
+END
+
+# dither set @ scale 3 = 240 arcsec
+SEQUENCE METADATA
+  OBSTYPE    STR OBJECT
+  CAMERA     STR SIMTEST
+
+  ### Carina (modestly high density)
+  CENTER.RA  F32 270.75
+  CENTER.DEC F32 -23.7
+
+  # XXX note that we cannot define the offsets in terms of the camera dimensions...
+  OFFSET.RA  F32 0.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.DEC F32 0.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.NR  S32 1
+  OFFSET.ND  S32 1
+
+  DITHER.RA  F32 240.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.DEC F32 240.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.NR  S32 2
+  DITHER.ND  S32 2
+
+  # XXX is DVODB used?
+  DVODB      STR /data/alala.0/ipp/ippRefs/catdir.synth.simtest 
+  FILTERS    STR r,i
+  @EXPTIMES  F32 10.00,10.00
+  @SKYMAGS   F32 20.86,20.15
+
+  # modest IQ range here
+  IQ_MIN     F32 0.70
+  IQ_MAX     F32 0.90
+  
+  # rotation sequence
+  POS_MIN    F32 0.0
+  POS_MAX    F32 0.0
+  POS_DELTA  F32 1.0
+
+END
Index: /tags/sj_tags/sj_root_20080929/ippTasks/simtest.megacam.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/simtest.megacam.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/simtest.megacam.config	(revision 22322)
@@ -0,0 +1,67 @@
+
+# define the simulated data to be generated
+SEQUENCE MULTI
+
+SEQUENCE METADATA
+  OBSTYPE    STR BIAS
+  CAMERA     STR MEGACAM
+
+  BIAS.LEVEL F32 100.0
+  BIAS.RANGE F32  10.0
+
+  NIMAGE     S32 15
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR DARK
+  CAMERA     STR MEGACAM
+
+  DARK.MIN   F32 100.0
+  DARK.MAX   F32 200.0
+
+  NIMAGE     S32 15
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR FLAT
+  CAMERA     STR MEGACAM
+
+  FILTERS    STR g,r,i
+  @EXPTIMES  F32 60.0,30.0,30.0
+
+  NSETUP     S32 15
+END
+
+SEQUENCE_SKIP METADATA
+  OBSTYPE    STR OBJECT
+  CAMERA     STR MEGACAM
+
+  CENTER.RA  F32 10.0 
+  CENTER.DEC F32 60.0
+
+  OFFSET.RA  F32 3600.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.DEC F32 3600.0 # linear offset in arcsec (do not include cos(DEC) correction)
+  OFFSET.NR  S32 5
+  OFFSET.ND  S32 5
+
+  DITHER.RA  F32 30.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.DEC F32 30.0   # linear offset in arcsec (do not include cos(DEC) correction)
+  DITHER.NR  S32 2
+  DITHER.ND  S32 2
+
+  DVODB      STR /data/alala.0/ipp/ippRefs/catdir.synth.grizy 
+  FILTERS    STR g,r,i
+  @EXPTIMES  F32 60.0,30.0,30.0
+  @SKYMAGS   F32 21.0,20.5,20.0
+
+  # XXX use a more realistic IQ distribution...
+  IQ_MIN     F32 0.5
+  IQ_MAX     F32 0.8
+  
+  # rotation sequence
+  POS_MIN    F32 0.0
+  POS_MAX    F32 0.0
+  POS_DELTA  F32 1.0
+
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippTasks/simtest.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/simtest.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/simtest.pro	(revision 22322)
@@ -0,0 +1,133 @@
+## simtest.pro : automatic full-suite simulated IPP test : -*- sh -*-
+
+## there are four features which define a simtest run:
+## the camera : any camera can be used, the sim... cameras are defined to be quick tests
+## the sequence : this file defines the set of observations generated
+## the auto : the automation file defines the sequence of analysis performed
+## the recipe : the ppsim recipe defines the features which are included in the generate data
+
+## these are not completely independent: a certain sequence may be
+## required to yield a sensible result for a specific auto, and a
+## specific recipe may also be needed
+
+if ($?PPSIM_RECIPE == 0) 
+  $PPSIM_RECIPE = default
+end
+if ($?SIMTEST_CAMERA == 0)
+  $SIMTEST_CAMERA = SIMTEST
+end
+if ($?SIMTEST_SEQUENCE == 0)
+  $SIMTEST_SEQUENCE = simtest.basic.config
+end
+if ($?SIMTEST_AUTO == 0)
+  $SIMTEST_AUTO = simtest.basic.auto
+end
+if ($?SIMTEST_THREADS == 0) 
+  $SIMTEST_THREADS = 0
+end
+
+macro simtest
+  if ($0 != 4)
+    echo "USAGE: simtest (dbname) (hostname) (init)"
+    echo " if (init) == run    : just run the analysis tasks"
+    echo " if (init) == inject : (re)create the database and (re)inject the images"
+    echo " if (init) == new    : restart completely from scratch"
+    echo ""
+    echo "there are additional options which may be specified by setting the following variable names:" 
+    echo "  PPSIM_RECIPE     : define the recipe to use when generating the fake data"
+    echo "  SIMTEST_CAMERA   : define an alternate camera (otherwise uses entry in sequence file)"
+    echo "  SIMTEST_SEQUENCE : define the set of observations generated (simtest.basic.config)"
+    echo "  SIMTEST_AUTO     : define the analysis steps to perform (simtest.auto)"
+    echo "  SIMTEST_THREADS  : set the number of threads for the processing node (0)"
+    echo ""
+    echo "the following macros can be used to set up specific simtest suites:"
+    echo "  simtest.setup.basic : run standard simtest suite"
+    echo "  simtest.setup.detverify : run detrend creation and detrend verification"
+    echo "  simtest.setup.flatcorr : run a flat-field correction demonstration"
+    break
+  end
+
+  $dbname = $1
+  $hostname = $2
+  $init = $3
+  if (("$init" != "run") && ("$init" != "inject") && ("$init" != "new"))
+    echo "USAGE: simtest (dbname) (hostname) (init)"
+    echo " if (init) == run    : just run the analysis tasks"
+    echo " if (init) == inject : (re)create the database and (re)inject the images"
+    echo " if (init) == new    : restart completely from scratch"
+    break
+  end    
+ 
+  if (("$init" == "new") || ("$init" == "inject"))  
+    # XXX this will fail and exit the script if the db does not exist or is old...
+    exec pxadmin -delete -dbname $dbname
+
+    # XXX this gives warnings if the db exists...
+    exec pxadmin -create -dbname $dbname
+
+    if ("$init" == "new")
+      # the labels "wait" and "proc" are special names used in automate.pro
+    
+      $ppsim = "ppSimSequence $MODULES:0/$SIMTEST_SEQUENCE simtest.mkimages simtest.inject -path raw -workdir work -dbname $dbname -label wait -dvodb DVO -tess_id TESS"
+      if ("$PPSIM_RECIPE" != "default") 
+         $ppsim = $ppsim -ppsim_recipe $PPSIM_RECIPE
+      end
+      $ppsim = $ppsim -camera $SIMTEST_CAMERA
+
+      exec $ppsim
+
+      exec source simtest.mkimages
+
+      ## XXX this will need to be optional as well 
+      file TESS/Images.dat found
+      if ($found == 0)
+        exec skycells 8 -scale 0.2 -D CATDIR TESS
+      end
+    end
+
+    exec chiptool -dbname $dbname -block -label wait
+    exec source simtest.inject
+  end
+
+  module pantasks.pro
+  module automate.pro
+
+  module.tasks
+
+  if ($SIMTEST_THREADS == 0) 
+    controller host add $hostname
+  else
+    controller host add $hostname -threads $SIMTEST_THREADS
+  end
+
+  add.database $dbname
+
+  automate.load $SIMTEST_AUTO $SIMTEST_CAMERA $dbname
+  run
+end
+
+# XXX add a test function to pxadmin to check for db existence
+
+## currently defined options for sequence, etc:
+
+# sequence: simtest.basic.config, simtest.flatcorr.config
+# auto: simtest.basic.auto, simtest.flatcorr.auto
+
+macro simtest.setup.basic
+  $PPSIM_RECIPE = default
+  $SIMTEST_SEQUENCE = simtest.basic.config
+  $SIMTEST_AUTO = simtest.basic.auto
+end
+
+macro simtest.setup.detverify
+  $PPSIM_RECIPE = default
+  $SIMTEST_SEQUENCE = simtest.detverify.config
+  $SIMTEST_AUTO = simtest.detverify.auto
+end
+
+macro simtest.setup.flatcorr
+  $PPSIM_RECIPE = FLATCORR
+  $SIMTEST_SEQUENCE = simtest.flatcorr.config
+  $SIMTEST_AUTO = simtest.flatcorr.auto
+end
+
Index: /tags/sj_tags/sj_root_20080929/ippTasks/site.manoa.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/site.manoa.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/site.manoa.pro	(revision 22322)
@@ -0,0 +1,52 @@
+## site.mhpcc.pro : example configuration script for the MHPCC IPP cluster : -*- sh -*-
+
+## a site configuration needs to define the following pieces of information:
+## 1) the machines used for processing
+## 2) the default_host and workdir_template variables
+## 3) the ipphost table
+
+macro init.cluster.po
+  $PARALLEL = 1
+  controller exit true
+
+  # po05 -- using for pantasks 
+  # po23 -- broken perl
+
+  controller host add po02
+  controller host add po03
+  controller host add po04
+# controller host add po05
+  controller host add po06
+  controller host add po07
+# controller host add po08
+# controller host add po09
+# controller host add po10
+# controller host add po11
+# controller host add po12
+# controller host add po13
+# controller host add po14
+# controller host add po15
+# controller host add po16
+# controller host add po17
+# controller host add po18
+# controller host add po19
+# controller host add po20
+# controller host add po21
+# controller host add po22
+# controller host add po23
+# controller host add po24
+end
+
+macro init.cluster.sn
+  $PARALLEL = 1
+  controller exit true
+  controller host add sn2
+  controller host add sn3
+  controller host add sn4
+  controller host add sn5
+end
+
+macro init.site
+  queueload tmp -x "cat $MODULES:0/ipphosts.manoa.config"
+  ipptool2book tmp ipphosts -key camera
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/site.mhpcc.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/site.mhpcc.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/site.mhpcc.pro	(revision 22322)
@@ -0,0 +1,75 @@
+## site.mhpcc.pro : example configuration script for the MHPCC IPP cluster : -*- sh -*-
+
+## a site configuration needs to define the following pieces of information:
+## 1) the machines used for processing
+## 2) the default_host and workdir_template variables
+## 3) the ipphost table
+
+macro init.cluster.mhpcc
+  $PARALLEL = 1
+  controller exit true
+
+  controller host add ipp014
+  controller host add ipp015
+  controller host add ipp023
+  controller host add ipp024
+  controller host add ipp026
+  controller host add ipp027
+  controller host add ipp028
+  controller host add ipp029
+  controller host add ipp030
+  controller host add ipp031
+  controller host add ipp032
+  controller host add ipp033
+  controller host add ipp034
+  controller host add ipp035
+  controller host add ipp036
+
+end
+
+## override the basic inits set in pantasks.pro
+macro init.copy.mhpcc
+ if ($0 != 2)
+   echo "USAGE: init.copy.mhpcc (nebulous)"
+   echo "nebulous may be 'on' or 'off'"
+   break
+ end
+
+ if (("$1" != "on") && ("$1" != "off")) 
+   echo "USAGE: init.copy.mhpcc (nebulous)"
+   echo "nebulous may be 'on' or 'off'"
+   break
+ end
+ 
+ # XXX this is only used by summit.copy.pro.  move this as a check into summit.copy.pro?
+ $COMPRESS = 1
+
+ # the templates are used if we have a class_id/host relationship; 
+ # if none is found, the default values are used
+ # XXX not sure how to handle the .N value if we need to use more than one
+
+ if ("$1" == "on")
+  $NEBULOUS = 1
+  $default_host     = ipp023
+  $workdir_template = neb://@HOST@.0
+ else
+  $NEBULOUS = 0
+  $default_host     = ipp023
+  $workdir_template = /data/@HOST@.0
+ end
+end
+
+macro init.site
+  init.cluster.mhpcc
+  init.copy.mhpcc on
+
+  queueload tmp -x "cat $MODULES:0/ipphosts.mhpcc.config"
+  ipptool2book tmp ipphosts -key camera
+end
+
+macro init.site.nohosts
+  init.copy.mhpcc on
+
+  queueload tmp -x "cat $MODULES:0/ipphosts.mhpcc.config"
+  ipptool2book tmp ipphosts -key camera
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/stack.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/stack.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/stack.pro	(revision 22322)
@@ -0,0 +1,289 @@
+## stack.pro : tasks for image stacking : -*- sh -*-
+
+## This file contains panTasks definitions for performing the image stacking.
+## After a stack (with associated stack_id) is defined, the stack is performed
+## (tasks in stackSumSkyfile).
+
+# test for required global variables
+check.globals
+
+### Initialise the books containing the tasks to do
+book init stackSumSkyfile
+book init stackCleanup
+
+### Database lists
+$stackSkycell_DB = 0
+$stackCleanup_DB = 0
+
+### Check status of stacking tasks
+macro stack.status
+  book listbook stackSumSkyfile
+  book listbook stackCleanup
+end
+
+### Reset stacking tasks
+macro stack.reset
+  book init stackSumSkyfile
+  book init stackCleanup
+end
+
+### Turn stacking tasks on
+macro stack.on
+  task stack.skycell.load
+    active true
+  end
+  task stack.skycell.run
+    active true
+  end
+end
+
+### Turn stacking tasks off
+macro stack.off
+  task stack.skycell.load
+    active false
+  end
+  task stack.skycell.run
+    active false
+  end
+end
+
+macro stack.cleanup.on
+  task stack.cleanup.load
+    active true
+  end
+  task stack.cleanup.run
+    active true
+  end
+end
+
+macro stack.cleanup.off
+  task stack.cleanup.load
+    active false
+  end
+  task stack.cleanup.run
+    active false
+  end
+end
+
+
+### Load tasks for doing the stack
+### Tasks are loaded into stackSumSkyfile.
+task	       stack.skycell.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/stack.skycell.log
+
+  task.exec
+    $run = stacktool -tosum
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$stackSkycell_DB
+      $run = $run -dbname $DB:$stackSkycell_DB
+      $stackSkycell_DB ++
+      if ($stackSkycell_DB >= $DB:n) set stackSkycell_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout stackSumSkyfile -key stack_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook stackSumSkyfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup stackSumSkyfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+### Run tasks for calculating the stack overlaps
+### Tasks are taken from stackSumSkyfile.
+task	       stack.skycell.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 10800
+
+  task.exec
+    book npages stackSumSkyfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in stackSumSkyfile (pantaskState == INIT)
+    book getpage stackSumSkyfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword stackSumSkyfile $pageName pantaskState RUN
+    book getword stackSumSkyfile $pageName stack_id -var STACK_ID
+    book getword stackSumSkyfile $pageName tess_id -var TESS_DIR
+    book getword stackSumSkyfile $pageName skycell_id -var SKYCELL_ID
+    book getword stackSumSkyfile $pageName workdir -var WORKDIR_TEMPLATE
+    book getword stackSumSkyfile $pageName dbname -var DBNAME
+    book getword stackSumSkyfile $pageName state -var RUN_STATE
+
+    # set the host and workdir based on the skycell hash
+    set.host.for.skycell $SKYCELL_ID
+    set.workdir.by.skycell $SKYCELL_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    # XXX old code:
+    # host anyhost
+    # $WORKDIR = $WORKDIR_TEMPLATE
+
+    basename $TESS_DIR -var TESS_ID
+    sprintf outroot "%s/%s/%s/%s.%s.stk.%s" $WORKDIR $TESS_ID $SKYCELL_ID $TESS_ID $SKYCELL_ID $STACK_ID
+
+    stdout $LOGDIR/stack.skycell.log
+    stderr $LOGDIR/stack.skycell.log
+
+    $run = stack_skycell.pl --threads @MAX_THREADS@ --stack_id $STACK_ID --outroot $outroot --redirect-output --run-state $RUN_STATE
+    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 stackSumSkyfile $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword stackSumSkyfile $options:0 pantaskState TIMEOUT
+  end
+end
+
+
+# select images ready for stack analysis
+# new entries are added to stackCleanup
+# skip already-present entries
+task	       stack.cleanup.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       false
+
+  stdout NULL
+  stderr $LOGDIR/stack.cleanup.log
+
+  task.exec
+    $run = stacktool -pendingcleanuprun
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$stackCleanup_DB
+      $run = $run -dbname $DB:$stackCleanup_DB
+      $stackCleanup_DB ++
+      if ($stackCleanup_DB >= $DB:n) set stackCleanup_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout stackCleanup -key stack_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook stackCleanup
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup stackCleanup
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       stack.cleanup.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       false
+
+  task.exec
+    book npages stackCleanup -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in stackCleanup (pantaskState == INIT)
+    book getpage stackCleanup 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword stackCleanup $pageName pantaskState RUN
+    book getword stackCleanup $pageName camera -var CAMERA
+    book getword stackCleanup $pageName state -var CLEANUP_MODE
+    book getword stackCleanup $pageName stack_id -var STACK_ID
+    book getword stackCleanup $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and stack (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/stack.cleanup.log
+    stderr $LOGDIR/stack.cleanup.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage stack --stage_id $STACK_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 stackCleanup $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword stackCleanup $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/summit.copy.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/summit.copy.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/summit.copy.pro	(revision 22322)
@@ -0,0 +1,474 @@
+## summit.copy.pro : tasks for the summit to IPP download : -*- sh -*-
+## PanTasks scripts for Summit Copy
+
+## XXX note that this currently works with a single database as defined in .ipprc
+## XXX tie the database for output to the database from which the datastore was determined
+
+# pztool -adddatastore -inst isp -telescope ps1 -uri http://otis1.ifa.hawaii.edu/ds/skyprobe/index.txt
+# pztool -adddatastore -inst gpc1 -telescope ps1 -uri http://conductor/ds/gpc1/index.txt
+# pztool -adddatastore -inst allskycam -telescope ps1 -uri  http://otis1.ifa.hawaii.edu/ds/allskycam/index.txt
+
+# NOTE: workdir / volume mangling and nebulous.  these tasks copy the
+# imfiles from the summit, placing the output files in a directory
+# which is based on the chip/host relationship.  If nebulous is being
+# used, the volume name is set based on the host; if nebulous is not
+# being used, the workdir is set to include the host name.  The copy
+# operation is targetted to the same host by pcontrol.  The value of
+# workdir is set to be a template into which the appropriate value of
+# @HOST@ may be substituted in later scripts.
+
+# test for required global variables
+check.globals
+
+# list of DataStores to pull data from
+book init pzDataStore
+# list of summit exps that need to be queried
+book init pzPendingExp
+# list of summit imfiles that need to be downloaded
+book init pzPendingImfile
+
+macro copy.on
+  task pztool.datastore
+    active true
+  end
+  task pzgetexp
+    active true
+  end
+  task pztool.pendingexp
+    active true
+  end
+  task pzgetimfile 
+    active true
+  end
+  task pztool.pendingimfile
+    active true
+  end
+  task summit_copy
+    active true
+  end
+  task pztool.clearfault
+      active true
+  end
+end
+
+macro copy.off
+  task pztool.datastore
+    active false
+  end
+  task pzgetexp
+    active false
+  end
+  task pztool.pendingexp
+    active false
+  end
+  task pzgetimfile 
+    active false
+  end
+  task pztool.pendingimfile
+    active false
+  end
+  task summit_copy
+    active false
+  end
+  task pztool.clearfault
+      active false
+  end
+end
+
+# these variables will cycle through the known database names
+$pztoolDatastore_DB = 0
+$pztoolPendingExp_DB = 0
+$pztoolPendingImfile_DB = 0
+$pztoolClearFault_DB = 0
+
+# build a book of datastores to poll for data
+task pztool.datastore
+    host         local
+
+    # timeout shorter than exec so jobs do not build up
+    periods      -exec      10
+    periods      -poll       1
+    periods      -timeout   20
+    npending     1
+    # trange       16:00 23:59
+    # trange       00:00 04:00
+
+    task.exec
+      if ($DB:n == 0)
+        option DEFAULT
+        command pztool -datastore
+      else
+        # save the DB name for the exit tasks
+        option $DB:$pztoolDatastore_DB
+        command pztool -datastore -dbname $DB:$pztoolDatastore_DB
+        $pztoolDatastore_DB ++
+        if ($pztoolDatastore_DB >= $DB:n) set pztoolDatastore_DB = 0
+      end
+    end
+
+    # success
+    task.exit 0
+        # flush pzDataStore book
+        book init pzDataStore
+        # convert 'stdout' to book format
+        ipptool2book stdout pzDataStore -key camera:telescope -uniq -setword dbname $options:0
+    end
+
+    task.exit default
+        showcommand failure
+    end
+    task.exit timeout
+        showcommand timeout
+    end
+end
+
+$datastore_index = 0
+
+# run pzgetexp periodically to populate pzPendingExp in the database (no I/O)
+# this task is querying the data store for a list of exposures ("filesets")
+# and inserting these into a db table on the local cluster (pzPendingExp)
+task pzgetexp
+  periods      -exec     60
+  periods      -poll     1
+  periods      -timeout  700
+  # trage       16:00 23:59
+  # trage       00:00 04:00
+  npending      1
+  host         local
+
+  task.exec
+        # find an exp that needs imfiles fetched
+        book getpage pzDataStore $datastore_index -var pageName
+        if ("$pageName" == "NULL") break
+
+        # increment our pzDataStore index and loop back to 0 and the end of the
+        # book
+        $datastore_index ++
+        book npages pzDataStore -var npages
+        if ($datastore_index == $npages )
+            $datastore_index = 0
+        end
+
+        book getword pzDataStore $pageName camera    -var CAMERA
+        book getword pzDataStore $pageName telescope -var TELESCOPE
+        book getword pzDataStore $pageName uri       -var URI
+        book getword pzDataStore $pageName dbname    -var DBNAME
+
+        # store the current page
+        options $pageName
+
+        $run = pzgetexp -uri $URI -inst $CAMERA -telescope $TELESCOPE -dbname $DBNAME -timeout 650
+
+        # create the command line
+        if ($VERBOSE > 1)
+          echo command $run
+        end
+
+        command $run
+  end
+
+  task.exit     0
+  end
+
+  task.exit     default
+    showcommand failure
+  end
+  task.exit     timeout
+    showcommand timeout
+  end
+end
+
+# build a book of exps/filesetids that need to be queried
+task pztool.pendingexp
+    host         local
+
+    periods      -exec     10
+    periods      -poll     1
+    periods      -timeout  20
+    # trange       16:00 23:59
+    # trange       00:00 04:00
+    npending     1
+
+    task.exec
+      if ($DB:n == 0)
+        option DEFAULT
+        command pztool -pendingexp -limit 5
+      else
+        # save the DB name for the exit tasks
+        option $DB:$pztoolPendingExp_DB
+        command pztool -pendingexp -limit 5 -dbname $DB:$pztoolPendingExp_DB
+        $pztoolPendingExp_DB ++
+        if ($pztoolPendingExp_DB >= $DB:n) set pztoolPendingExp_DB = 0
+      end
+    end
+
+    # success
+    task.exit 0
+        # convert 'stdout' to book format
+        ipptool2book stdout pzPendingExp -key exp_name:camera:telescope -uniq -setword dbname $options:0 -setword pantaskState INIT
+
+        # delete existing entries in the appropriate pantaskStates
+        process_cleanup pzPendingExp
+    end
+
+    task.exit default
+        showcommand failure
+        if ($VERBOSE)
+          echo "*** stdout ***"
+          queueprint stdout
+          echo "*** stderr ***"
+          queueprint stderr
+        end
+    end
+    task.exit timeout
+        showcommand timeout
+    end
+end
+
+# run pzgetimfiles on pending exps.  analogous to pzgetexp, this task
+# is downloading a list of imfiles reported by the datastore for a
+# given exposure ("fileset"), and placing the result in the local
+# database table of imfiles
+task pzgetimfile 
+    periods      -exec     0.05
+    periods      -poll     0.025
+    periods      -timeout  700
+    # trage       16:00 23:59
+    # trage       00:00 04:00
+    host 	local
+    npending 	10
+
+    task.exec
+        if ($NETWORK == 0) break
+
+        # if we are waiting on data, make the interval long
+        book npages pzPendingExp -var N
+        if ($N == 0)
+            periods -exec 20
+            break
+        end
+        periods -exec 0.05
+
+        # find an exp that needs imfiles fetched
+        book getpage pzPendingExp 0 -var pageName -key pantaskState INIT
+        if ("$pageName" == "NULL") break
+
+        # set that exp to run
+        book setword pzPendingExp $pageName pantaskState RUN
+
+        book getword pzPendingExp $pageName exp_name  -var EXP_NAME
+        book getword pzPendingExp $pageName camera    -var CAMERA
+        book getword pzPendingExp $pageName telescope -var TELESCOPE
+        book getword pzPendingExp $pageName dateobs   -var DATEOBS
+        book getword pzPendingExp $pageName exp_type  -var EXP_TYPE
+        book getword pzPendingExp $pageName uri       -var URI
+        book getword pzPendingExp $pageName imfiles   -var IMFILES
+        book getword pzPendingExp $pageName dbname    -var DBNAME
+
+        # store the current page
+        options $pageName
+
+        $batman = $EXP_NAME
+        # Sidik says we should use a longer timeout
+        $run = pzgetimfiles -uri $URI -filesetid $batman -inst $CAMERA -telescope $TELESCOPE -dbname $DBNAME -timeout 650
+
+        # create the command line
+        if ($VERBOSE > 1)
+          echo command $run
+        end
+        command $run
+    end
+
+    # success
+    task.exit 0
+        process_exit pzPendingExp $options:0 $JOB_STATUS
+    end
+
+    task.exit default
+        showcommand failure
+        process_exit pzPendingExp $options:0 $JOB_STATUS
+    end
+
+    task.exit timeout
+        showcommand timeout
+        book setword pzPendingExp $options:0 pantaskState TIMEOUT
+    end
+end
+
+
+# build a book of imfiles/files that need to be downloaded
+task pztool.pendingimfile
+    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 -pendingimfile -limit 40
+      else
+        # save the DB name for the exit tasks
+        option $DB:$pztoolPendingImfile_DB
+        command pztool -pendingimfile -limit 60 -dbname $DB:$pztoolPendingImfile_DB
+        $pztoolPendingImfile_DB ++
+        if ($pztoolPendingImfile_DB >= $DB:n) set pztoolPendingImfile_DB = 0
+      end
+    end
+  
+    # success
+    task.exit    0
+        # convert 'stdout' to book format
+        ipptool2book stdout pzPendingImfile -key exp_name:camera:telescope:class:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+	book shuffle pzPendingImfile 
+
+        # delete existing entries in the appropriate pantaskStates
+        process_cleanup pzPendingImfile
+    end
+
+    task.exit     default
+        showcommand failure
+    end
+    task.exit     timeout
+        showcommand timeout
+    end
+end
+
+# retreive an imfile with dsget and then call pztool -copydone
+task summit_copy
+    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 pzPendingImfile -var N
+        if ($N == 0)
+            periods -exec 20
+            break
+        end
+        periods -exec 0.05
+
+        # find an exp that needs imfiles fetched
+        book getpage pzPendingImfile 0 -var pageName -key pantaskState INIT
+        if ("$pageName" == "NULL") break
+
+        # set that exp to run
+        book setword pzPendingImfile $pageName pantaskState RUN
+
+        book getword pzPendingImfile $pageName uri     	 -var URI
+        book getword pzPendingImfile $pageName bytes   	 -var BYTES
+        book getword pzPendingImfile $pageName md5sum  	 -var MD5SUM
+        book getword pzPendingImfile $pageName dateobs 	 -var DATEOBS
+        book getword pzPendingImfile $pageName exp_name  -var EXP_NAME
+        book getword pzPendingImfile $pageName camera    -var CAMERA
+        book getword pzPendingImfile $pageName telescope -var TELESCOPE
+        book getword pzPendingImfile $pageName class     -var CLASS
+        book getword pzPendingImfile $pageName class_id  -var CLASS_ID
+        book getword pzPendingImfile $pageName dbname    -var DBNAME
+
+        set.host.for.camera $CAMERA $CLASS_ID
+
+        # 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
+
+        # figure out filename
+	# XXX may need to use sprintf here
+        $FILENAME = $workdir_base/$CAMERA/$YEAR\$MONTH\$DAY/$EXP_NAME/$EXP_NAME.$CLASS_ID.fits
+        $workdir = $workdir_template/$CAMERA/$YEAR\$MONTH\$DAY
+
+	# workdir examples:
+	# file://data/@HOST@.0/gpc1/20080130
+	# neb://@HOST@.0/gpc1/20080130
+
+	# filename examples:
+	# file://data/ipp005.0/gpc1/20080130/o4437g0025d/o4437g0025d.XY05.fits
+	# neb://@HOST@.0/gpc1/20080130/o4437g0025d/o4437g0025d.XY05.fits
+
+        book setword pzPendingImfile $pageName filename $FILENAME
+
+        $run = summit_copy.pl --uri $URI --filename $FILENAME --exp_name $EXP_NAME --inst $CAMERA --telescope $TELESCOPE --class $CLASS --class_id $CLASS_ID --bytes $BYTES --md5 $MD5SUM --end_stage reg --workdir $workdir --dbname $DBNAME --timeout 120 --verbose --copies 2
+	if ($COMPRESS) 
+            $run = $run --compress
+        else
+            $run = $run --bytes $BYTES 
+        end
+        if (("$MD5SUM" != "NULL") && ("$MD5SUM" != "0") && (not($COMPRESS)))
+            $run = $run --md5 $MD5SUM
+        end
+	if ($NEBULOUS) 
+            $run = $run --nebulous
+        end
+        # add_standard_args run
+
+        # 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 pzPendingImfile $options:0 $JOB_STATUS
+    end
+
+    # operation timed out?
+    task.exit timeout
+        showcommand timeout
+        book setword pzPendingImfile $options:0 pantaskState TIMEOUT
+    end 
+end
+
+task pztool.clearfault
+    host         local
+
+    # -exec is set much longer the first time this task runs
+    periods      -exec       7  
+    periods      -poll       1
+    periods      -timeout   30
+    npending     1
+
+    task.exec
+      if ($DB:n == 0)
+        command pztool -clearcommonfaults
+      else
+        command pztool -clearcommonfaults -dbname $DB:$pztoolClearFault_DB
+
+        $pztoolClearFault_DB ++
+        if ($pztoolClearFault_DB >= $DB:n) set pztoolClearFault_DB = 0
+      end
+      periods -exec 1800
+    end
+
+    # success
+    task.exit 0
+    end
+
+    task.exit default
+        showcommand failure
+    end
+    task.exit timeout
+        showcommand timeout
+    end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTasks/warp.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTasks/warp.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTasks/warp.pro	(revision 22322)
@@ -0,0 +1,416 @@
+## warp.pro : image warping tasks : -*- sh -*-
+## This file contains panTasks definitions for performing the image warping.
+
+### This is done in two (main) steps.  After a warp (with associated warp_id) is defined,
+### overlaps between the exposure being warped and skycells are calculated (tasks in warpInputExp).
+### Then for each skycell, the warp is made (tasks in warpPendingSkycell).
+
+### Setups
+check.globals
+
+### Initialise the books containing the tasks to do
+book init warpInputExp
+book init warpPendingSkyCell
+book init warpPendingCleanup
+
+### Database lists
+$warpExp_DB = 0
+$warpSkycell_DB = 0
+$warpCleanup_DB = 0
+
+### Check status of warping tasks
+macro warp.status
+  book listbook warpInputExp
+  book listbook warpPendingSkyCell
+  book listbook warpPendingCleanup
+end
+
+### Reset warping tasks
+macro warp.reset
+  book init warpInputExp
+  book init warpPendingSkyCell
+  book init warpPendingCleanup
+end
+
+### Turn warping tasks on
+macro warp.on
+  task warp.exp.load
+    active true
+  end
+  task warp.exp.run
+    active true
+  end
+  task warp.skycell.load
+    active true
+  end
+  task warp.skycell.run
+    active true
+  end
+end
+
+### Turn warping tasks off
+macro warp.off
+  task warp.exp.load
+    active false
+  end
+  task warp.exp.run
+    active false
+  end
+  task warp.skycell.load
+    active false
+  end
+  task warp.skycell.run
+    active false
+  end
+end
+
+macro warp.cleanup.on
+  task warp.cleanup.load
+    active true
+  end
+  task warp.cleanup.run
+    active true
+  end
+end
+macro warp.cleanup.off
+  task warp.cleanup.load
+    active false
+  end
+  task warp.cleanup.run
+    active false
+  end
+end
+
+### Load tasks for calculating the warp overlaps
+### Tasks are loaded into warpInputExp.
+task	       warp.exp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/warp.exp.log
+
+  task.exec
+    $run = warptool -tooverlap
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$warpExp_DB
+      $run = $run -dbname $DB:$warpExp_DB
+      $warpExp_DB ++
+      if ($warpExp_DB >= $DB:n) set warpExp_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout warpInputExp -key warp_id:fake_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook warpInputExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup warpInputExp
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+### Run tasks for calculating the warp overlaps
+### Tasks are taken from warpInputExp.
+task	       warp.exp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    book npages warpInputExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in warpInputExp (pantaskState == INIT)
+    book getpage warpInputExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword warpInputExp $pageName pantaskState RUN
+    book getword warpInputExp $pageName warp_id -var WARP_ID
+    book getword warpInputExp $pageName camera -var CAMERA
+    book getword warpInputExp $pageName workdir -var WORKDIR_TEMPLATE
+    book getword warpInputExp $pageName dbname -var DBNAME
+    # XXX change tess_id to tess_dir when schema is changed
+    book getword warpInputExp $pageName tess_id -var TESS_DIR
+    book getword warpInputExp $pageName exp_tag -var EXP_TAG
+
+    # set the host and workdir (default)
+    set.host.for.camera $CAMERA $WARP_ID
+    set.workdir.by.camera $CAMERA $WARP_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure
+    sprintf logfile "%s/%s/%s.wrp.%s.log" $WORKDIR $EXP_TAG $EXP_TAG $WARP_ID
+
+    stdout $LOGDIR/warp.exp.log
+    stderr $LOGDIR/warp.exp.log
+
+    # XXX warp_overlap.pl differs from the standard script : it does not have an 'outroot' argument, and it does not take '--redirect'
+    $run = warp_overlap.pl --warp_id $WARP_ID --camera $CAMERA --tess_dir $TESS_DIR --logfile $logfile
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit warpInputExp $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword warpInputExp $options:0 pantaskState TIMEOUT
+  end
+end
+
+
+### Load tasks for doing the warps
+### Tasks are loaded into warpPendingSkyCell.
+task	       warp.skycell.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/warp.skycell.log
+
+  task.exec
+    $run = warptool -towarped
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$warpSkycell_DB
+      $run = $run -dbname $DB:$warpSkycell_DB
+      $warpSkycell_DB ++
+      if ($warpSkycell_DB >= $DB:n) set warpSkycell_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    # XXX change tess_id to tess_dir when db is updated
+    ipptool2book stdout warpPendingSkyCell -key warp_id:skycell_id:tess_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook warpPendingSkyCell
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup warpPendingSkyCell
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+### Run tasks for calculating the warp overlaps
+### Tasks are taken from warpPendingSkyCell.
+task	       warp.skycell.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    book npages warpPendingSkyCell -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in warpPendingSkyCell (pantaskState == INIT)
+    book getpage warpPendingSkyCell 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword warpPendingSkyCell $pageName pantaskState RUN
+    book getword warpPendingSkyCell $pageName warp_id -var WARP_ID
+    book getword warpPendingSkyCell $pageName skycell_id -var SKYCELL_ID
+    book getword warpPendingSkyCell $pageName camera -var CAMERA
+    book getword warpPendingSkyCell $pageName workdir -var WORKDIR_TEMPLATE
+    book getword warpPendingSkyCell $pageName dbname -var DBNAME
+    # XXX change tess_id to tess_dir when schema is changed
+    book getword warpPendingSkyCell $pageName tess_id -var TESS_DIR
+    book getword warpPendingSkyCell $pageName exp_tag -var EXP_TAG
+    book getword warpPendingSkyCell $pageName state -var RUN_STATE
+
+    # set the host and workdir based on the skycell hash
+    set.host.for.skycell $SKYCELL_ID
+    set.workdir.by.skycell $SKYCELL_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    ## generate outroot specific to this exposure
+    sprintf outroot "%s/%s/%s.wrp.%s.%s" $WORKDIR $EXP_TAG $EXP_TAG $WARP_ID $SKYCELL_ID
+
+    stdout $LOGDIR/warp.skycell.log
+    stderr $LOGDIR/warp.skycell.log
+
+    $run = warp_skycell.pl --threads @MAX_THREADS@ --warp_id $WARP_ID --skycell_id $SKYCELL_ID --tess_dir $TESS_DIR --camera $CAMERA --outroot $outroot --redirect-output --run-state $RUN_STATE
+    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 warpPendingSkyCell $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword warpPendingSkyCell $options:0 pantaskState TIMEOUT
+  end
+end
+
+# select images ready for warp analysis
+# new entries are added to warpPendingImfile
+# skip already-present entries
+task	       warp.cleanup.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       false
+
+  stdout NULL
+  stderr $LOGDIR/warp.cleanup.log
+
+  task.exec
+    $run = warptool -pendingcleanuprun
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$warpCleanup_DB
+      $run = $run -dbname $DB:$warpCleanup_DB
+      $warpCleanup_DB ++
+      if ($warpCleanup_DB >= $DB:n) set warpCleanup_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout warpPendingCleanup -key warp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook warpPendingCleanup
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup warpPendingCleanup
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       warp.cleanup.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       false
+
+  task.exec
+    book npages warpPendingCleanup -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in warpPendingCleanup (pantaskState == INIT)
+    book getpage warpPendingCleanup 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword warpPendingCleanup $pageName pantaskState RUN
+    book getword warpPendingCleanup $pageName camera -var CAMERA
+    book getword warpPendingCleanup $pageName state -var CLEANUP_MODE
+    book getword warpPendingCleanup $pageName warp_id -var WARP_ID
+    book getword warpPendingCleanup $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and warp (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/warp.cleanup.log
+    stderr $LOGDIR/warp.cleanup.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage warp --stage_id $WARP_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 warpPendingCleanup $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword warpPendingCleanup $options:0 pantaskState TIMEOUT
+  end
+end
Index: /tags/sj_tags/sj_root_20080929/ippTests/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTests/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTests/README	(revision 22322)
@@ -0,0 +1,17 @@
+
+This directory contains the scripts for testing the results of an ipp analysis
+
+note from sebastian 2008-09-25:
+
+Mark and I talked; there's a new wiki page
+http://kiawe.ifa.hawaii.edu/IPPwiki/index.php/SimtestVerification
+which we'll use to collect information and plans.
+
+We'll let Eric figure out ppSim (or you with Eric's help,
+rather). Mark is looking at the script I wrote and will be thinking
+about how to adapt it to Essence data (and any type of data in
+general). He'll also look at reducing Essence data again. The SDSS
+pipeline isn't really set up for time-domain stuff, and I'm not too
+familiar with time domain anyway, so I thought letting the JHU people
+do it will be more productive.
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/.cvsignore	(revision 22322)
@@ -0,0 +1,16 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.guess
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+pxtools.pc
Index: /tags/sj_tags/sj_root_20080929/ippTools/COPYING
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/COPYING	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/COPYING	(revision 22322)
@@ -0,0 +1,345 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /tags/sj_tags/sj_root_20080929/ippTools/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/Makefile.am	(revision 22322)
@@ -0,0 +1,12 @@
+SUBDIRS = src share scripts
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= pxtools.pc
+
+EXTRA_DIST = \
+	pxtools.pc.in \
+	autogen.sh
+
+CLEANFILES = *~ core core.*
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippTools/TODO
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/TODO	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/TODO	(revision 22322)
@@ -0,0 +1,14 @@
+- change dettol -pretend -define... to return a metadata structure that shows what the created detRun would look like
+- rename dettool -residdetrun -> -todetrunsummary
+- add foreign key constraints to the database
+- add the ability to remove dettool detResidExps
+- change p4tool to accept input from/with magic masks
+- change p5tool to accept input from warp * stack
+- difftool/stacktool error handling
+- move all large SQL statements out into separate files
+- add the option of not automatically flowing through from reg -> chip -> cam
+  tools
+- add workdir inheritance rooted in rawExp, eg.
+    select chipProcessedExp.workdir, rawExp.workdir, IFNULL(chipProcessedExp.workdir, rawExp.workdir) from chipProcessedExp join rawExp using(exp_tag);
+- dettool/detRun support for filelevel
+- combind dettool -definebytag & -definebyquery
Index: /tags/sj_tags/sj_root_20080929/ippTools/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=pxtools
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+#($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+#        DIE=1
+#}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+#($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+#        DIE=1
+#}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL -I m4 || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ippTools/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/configure.ac	(revision 22322)
@@ -0,0 +1,65 @@
+AC_PREREQ(2.61)
+
+AC_INIT([ipptools], [1.1.36], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([autogen.sh])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.1.0])
+PKG_CHECK_MODULES([PSMODULES], [psmodules >= 1.1.0])
+PKG_CHECK_MODULES([IPPDB], [ippdb >= 1.1.39]) 
+
+PXTOOLS_CFLAGS="${PSLIB_CFLAGS=} ${PSMODULES_CFLAGS=} ${IPPDB_CFLAGS=}"
+PXTOOLS_LIBS="${PSLIB_LIBS=} ${PSMODULES_LIBS=} ${IPPDB_LIBS=}"
+AC_SUBST(PXTOOLS_CFLAGS,[$PXTOOLS_CFLAGS])
+AC_SUBST(PXTOOLS_LIBS,[$PXTOOLS_LIBS])
+
+dnl check for PSDB support
+TMP_CFLAGS=${CFLAGS}
+CFLAGS="${CFLAGS=} ${PSLIB_CFLAGS}"
+
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+    [[#if !defined HAVE_PSDB
+      error: PSDB support is not avaiable
+      #endif
+    ]])],
+  [],
+  [AC_MSG_ERROR([psLib was built without PSDB support, bailing out.])]
+)
+
+CFLAGS=${TMP_CFLAGS}
+
+dnl --  check for psParseErrorCodes --------------------------------------
+
+AC_PATH_PROG([ERRORCODES], [psParseErrorCodes], [missing])
+if test "$ERRORCODES" = "missing" ; then
+  AC_MSG_ERROR([psParseErrorCodes is required])
+fi
+
+dnl check for perl
+AC_PATH_PROG([PERL], [perl], [missing])
+if test "$PERL" = "missing" ; then
+  AC_MSG_ERROR([perl is required])
+fi
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  share/Makefile
+  scripts/Makefile
+  pxtools.pc
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/dettool.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/dettool.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/dettool.txt	(revision 22322)
@@ -0,0 +1,181 @@
+# returns a list of unassociated rawDetrendExp (rawDetrendExps that don't
+# correspond to a exp_id in detInputExp)
+dettool -pending -exp_type -inst ..
+
+# define a new detRun semi-automatically by selecting the component exps with
+# the specified search criteria
+dettool -definebyquery -det_type ...
+
+# create a detRun with the specified input exp_ids
+dettool -definebyexp -det_type -exp_id 1 -exp_id 2 ...
+
+--
+# returns a list of rawImfiles that are associated with an detInputExp that do
+# not appear in detProcessedImfiles
+dettool -raw
+
+# add an imfile to detProcessedImfiles
+dettool -addprocessed
+
+--
+# the list of class_ids that are ready to be stacked
+# returns a list of det_id/iter/class_id/det_typs  where class_id is unique per
+# det_id (not a list of all exp_ids/class_ids) that do not have an entry in
+# detStackedImfile
+# results are masked by entries in detResidExpAnalysis
+# may need to check detResidExp's even on iter 0 - ???
+dettool -tostack
+
+# list all of imfiles from different exps with the same class_id that have
+# been processed
+dettool -processed -wholestack -det_id ...
+
+# adds a stacked class_id and and optionally marks it for normalization
+detool -addstacked [-pleasenormalize] -det_id -iter -class_id ...
+
+--
+# returns a list of det_ids/iters where the entire set of class_ids has been
+# processed
+dettool -tonormalize
+
+# returns the complete list of processed imfiles for the specified det_id
+dettool -processed -unmask -det_id -iter
+
+# add per stacked imfile normalization statistics
+# O: this could accept a MDC doc as input from stdin
+dettool -addnormstat -det_id -iter -class_idi -norm F32
+
+--
+# returns a list of det_ids/iter/class_id/uri from detNormStats.  Stops
+# returning entries after the class_is is inserted into detNormalizedImfile
+dettool -normstat -det_id 
+
+# inserts an entry into detNormalizedImfile
+dettool -addnormalized -det_id -ter -class_id -uri
+
+--
+# returns a list of processed imfiles that have also been normalized with
+# processed imfiles being masked out per detResidImfileAnalysis
+# also returns stackedimfiles that have -pleasenormalize set to false
+dettool -toresid -det_id -iter
+
+# inserts a per det_id/iter/exp_id/class_id residual
+dettool -addresidimfile -det_id -exp_id -class_id -iter -stat F32 -stat_stdev F32 -uri -b1 -b2
+
+# returns a list of det_id/iter/exp_ids but only for exp_ids that have resids
+# for all of their imfiles
+dettool -toresidexp
+
+# returns a list of residual imfile data
+dettool -residimfile -det_id -iter -exp_id
+
+# inserts into detResidExpAnalysis 
+# sets the accept accept bool unless -reject is specified
+dettool -addresidexp -det_id -iter -exp_id -jpeg1 -jpeg2 -stat F32 -stat_stdev
+F32 [-reject]
+
+--
+# lists det_id/iter for detruns that have completed all residexps for their
+# current iteration 
+dettool -residdetrun
+
+# lists all residexps for the given det_id/iter
+dettool -residexp -det_id -iter
+
+# updates the given residexp to be accept/reject
+dettool -updateresid -det_id -iter [-reject]
+
+# changes the state of the detrun.  -rerun cause a new iteration to be started
+# with the accepted with just the exp_ids
+dettool -updatedetrun -det_id -iter -stat F32 -stat_stdev [-rerun|-ok|-stop]
+
+# manual starts a new detrun iteration starting from the specified -iter
+dettool -rerun -det_id -iter -accept expid -accept expid -reject expid -reject expid
+dettool -rerun -det_id -iter
+
+# detInputExp needs to track...
+det_id iter exp_id use bool accept bool
+
+
+
+dettool
+
+    -define | creates a a new detrend Run
+        input:
+            exp IDs?
+            type of det run?
+        creates a new detRun ID automatically
+    
+        output: the new det_id
+
+    -raw    | lists raw imfiles needing to be processed
+        input: 
+            det ID
+            exp ID
+            class ID
+            iteration number?
+        output: list of unprocessed imfiles
+    
+    -addprocessed? | marks a raw imfile as processed
+        input:
+            det ID
+            exp ID
+            class ID
+            iteration number?
+            uri
+            stat
+            recipe
+        output: outout on error only
+    -processed | lists processed imfiles
+        input:
+            det ID
+            exp ID
+            class ID
+            iteration number?
+        output: list of processed imfiles
+    -addstacked | adds a stacked imfiles
+        input:
+            det ID
+            class ID
+            iteration number?
+            URI
+            recipe
+            stats
+        output: output on error only
+    -stacked     | lists stacked imfiles
+        input:
+            det ID
+            class ID
+            iteration number?
+        output: list of stacked imfiles
+    -stacked     | lists stacked imfiles
+        input:
+            det ID
+            class ID
+            iteration number?
+        output: list of stacked imfiles 
+    -stackedframe     | lists stacked imfiles for COMPLETE frames
+        input:
+            det ID  // required for simplicity
+            iteration number?
+        output: list of stacked imfiles
+    -addmaster | adds a master imfiles
+        input:
+            det ID
+            class ID
+            iteration number?
+            URI
+            recipe
+            stats
+        output: output on error only
+    -master     | lists master imfiles
+        input:
+            det ID  // required for simplicity
+            class ID
+            iteration number?
+        output: a list of master imfiles
+    -masterframe     | lists master imfiles for COMPLETE frames
+        input:
+            det ID  // required for simplicity
+            iteration number?
+        output: a list of master imfiles
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/dsfilesetls
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/dsfilesetls	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/dsfilesetls	(revision 22322)
@@ -0,0 +1,6 @@
+#!/usr/bin/env perl
+
+print "# uri fileid bytes md5sum type \n";
+print "http://example.org/1 foo1.0 123 asadfasdfasdfasfdasdf CELL\n";
+print "http://example.org/2 foo1.1 123 asadfasdfasdfasdfasdf CELL\n";
+print "http://example.org/3 foo1.2 123 asadfasdfasdfasdfasdf CELL\n";
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/dsproductls
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/dsproductls	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/dsproductls	(revision 22322)
@@ -0,0 +1,8 @@
+#!/usr/bin/env perl
+
+print "# uri fileset datetime type\n";
+print "http://example.org/1 foo1 2006-04-17T00:00:12 OBJECT\n";
+print "http://example.org/2 foo2 2006-04-17T00:00:12 OBJECT\n";
+print "http://example.org/3 foo3 2006-04-17T00:00:12 OBJECT\n";
+
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/install.pod
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/install.pod	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/install.pod	(revision 22322)
@@ -0,0 +1,251 @@
+=pod
+
+=head1 SYNOPSIS
+
+C<pXtools> is a collection of utilties for tracking image processing
+tasks.  Very little decision making logic is built into these programs.  The
+intent is for higher level programs and scripts to make the real I<decisions>
+and then use C<pXtools> to encode those choices in a persistent manner.
+
+=head1 INSTALL
+
+=head2 Prerequisites
+
+C<pXtools> depends on C<psLib> (built with C<psDB> support), C<psModules>, and
+C<ippdb> and requires a C<C99> capable compiler.
+
+XXX document required library versions
+
+=head2 Build Procedure
+
+C<pXtools> is built with the so-called suite of autotools.   The build
+procedure is pretty typical for software built with these tools.  E.g.
+    
+    ./configure
+    make
+    make install
+
+If you checked out the sources directly from CVS you need to run the
+C<autogen.sh> script.  C<autogen.sh> will run C<configure> for you so that
+changes the build procedure to just:
+
+    sh autogen.sh
+    make
+    make install
+
+=head2 jhbuild Build Procedure
+
+If you are using one of the C<ippdev> modulesets jhbuild can build and install C<pXtools> for you.  E.g.
+
+    jhbuild build pxtools
+    
+=head2 Database Setup
+
+All of the C<pXtools>' persistent storage is ultimately done through C<psLib>'s
+C<psDB> facilties.  In turn, C<psDB> uses C<MySQL> to impliment storage (and
+relational querying).  C<MySQL> requires that each namespace (aka database) is
+configured with basic access controls.  These include usernames, passwords,
+access filtering, and privilege restrictions.  Below is a simple (but somewhat
+dangerous) example of configuring a database named C<ipp>.
+
+    mysql -u root mysql -p
+    (enter your MySQL root password)
+
+    DROP DATABASE IF EXISTS ipp;
+    CREATE DATABASE ipp;
+    GRANT ALL PRIVILEGES ON *.* TO 'ippuser'@'localhost' IDENTIFIED BY 'ipppass';
+    FLUSH PRIVILEGES;
+    quit
+
+After setting up the database you should verify that you can connect to with the username and password you configured
+
+    mysql -u ipp ipp -pipp
+
+Next, C<pXtools> needs to be told where to to find MySQL, the name of the
+database you created, the username, and the password you created.  C<pXtools>
+uses the typical C<psModules> configuration system.  The simplest method is to
+add the follow lines to your $HOME/.ipprc file.
+
+    ### Database configuration
+    DBSERVER        STR     localhost   # Database host name (for psDBInit)
+    DBNAME          STR     ipp
+    DBUSER          STR     ipp         # Database user name (for psDBInit)
+    DBPASSWORD      STR     ipp         # Database password (for psDBInit)
+
+The C<pxTools> utilities require the a schema is pre-loaded into the database.  The C<pxadmin> tool can do this for you.  E.g.
+
+    pxadmin -create
+
+You should always verify that the scheme was actually loaded.  E.g.
+
+    mysql -u ipp ipp -pipp
+    Welcome to the MySQL monitor.  Commands end with ; or \g.
+    Your MySQL connection id is 6 to server version: 4.1.20-log
+
+    Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
+
+    mysql> show tables;
+    +------------------------+
+    | Tables_in_ipp          |
+    +------------------------+
+    | detInputExp            |
+    | detMasterFrame         |
+    | detMasterImfile        |
+    | detNormalizedImfile    |
+    | detProcessedImfile     |
+    | detResidExpAnalysis    |
+    | detResidImfileAnalysis |
+    | detRun                 |
+    | detStackedImfile       |
+    | newExp                 |
+    | newImfile              |
+    | p1PendingExp           |
+    | p2DoneExp              |
+    | p2DoneImfile           |
+    | p2PendingExp           |
+    | p2PendingImfile        |
+    | p3PendingExp           |
+    | pzPendingExp           |
+    | pzPendingImfile        |
+    | rawDetrendExp          |
+    | rawImfile              |
+    | rawScienceExp          |
+    | summitExp              |
+    +------------------------+
+    23 rows in set (0.00 sec)
+
+=head1 USE
+
+The workflow is as follows:
+
+XXX
+
+=head2 Phase 0 tools
+
+The primary C<pXtools> utility for handling Phase 0 is C<p0search>.  This tool
+has two modes C<-pending> and C<-update>. The pending mode lists all
+C<newImfile>s that are part of an exposure that I<has not> yet been classified
+as either science or detrend data.
+
+For example:
+
+    p0search -pending
+
+Would output something like:
+
+    newImfile MULTI #
+
+    newImfile  METADATA
+       exp_id STR  10
+       class STR  OTA
+       class_id STR  0
+       uri STR  file://0
+    END
+
+    newImfile  METADATA
+       exp_id STR  10
+       class STR  OTA
+       class_id STR  1
+       uri STR  file://1
+    END
+
+    newImfile  METADATA
+       exp_id STR  10
+       class STR  OTA
+       class_id STR  2
+       uri STR  file://2
+    END
+
+    newImfile  METADATA
+       exp_id STR  10
+       class STR  OTA
+       class_id STR  3
+       uri STR  file://3
+    END
+
+To declare C<exp_id 10> as science data, you would invoke C<p0search> like this:
+    
+    p0search -update -exp_id 10
+
+Note that by default C<-update> flags the exposure as science data.  To declare it to be detrend data the command would be:
+
+    p0search -update -exp_id 10 -detrend
+
+Also keep in mind that this is a one time declaration.  Once it has been
+declared as either science or detrend data this decision can not be undone
+(without manually editing the database)
+
+=head2 Phase 1 tools
+
+These tools are not yet in use.
+
+=head2 Phase 2 tools
+
+C<p2search> handles all of state for Phase 2.  It has four basic modes:
+
+=over 4
+
+=item * C<-quick>
+
+=item * C<-define>
+
+=item * C<-pending>
+
+    p0search -pending
+
+=item * C<-done>
+
+=head2 Administrative tools
+
+=over 4
+
+=item * C<pxadmin>
+
+This tool can be used to load/unload the database schema.  It has three modes of operation:
+
+=over 4
+
+=item * C<-create>
+
+Loads the database schema.
+
+=item * C<-delete>
+
+Deletes the database schema.
+
+=item * C<-recreate>
+
+Deletes the database schema then reloads it.
+
+=back
+
+=item * C<pxinject>
+
+I<Injects> data directly into the database.  It has one mode parameter for each database table.  Note: support for only a few tables has been impliemented.
+
+=back
+
+=head2 Bypassing 'Summit Copy'
+
+The C<pXtools> suite assumes that exposures and image data are initially
+registered with the C<pz*> set of I<Summit Copy step> tools.  Sometimes this is
+inconvenient as you want to work with local data and bypass this set.  This is
+what the C<pxinject> utility is for.  C<pxinject> directly inserts data into
+the database, side stepping any ordering rules or sanity checking.
+
+The end result of the C<pz*> utilities is that new exposures are registered in the C<newExp> table and raw image data is in the C<newImfile> table.  You can insert this data yourself, preparing the system to run I<phase 0>.
+
+Here is an example of inserting an exposure that has 4 component image files.
+
+    #!//bin/sh
+
+    inject="./pxinject"
+
+    $inject -newExp -exp_id 10 -inst gpc -telescope ps1 -exp_type object -imfiles 4
+
+    `$inject -newImfile -exp_id 10 -class OTA -class_id 0 -uri file://0`
+    `$inject -newImfile -exp_id 10 -class OTA -class_id 1 -uri file://1`
+    `$inject -newImfile -exp_id 10 -class OTA -class_id 2 -uri file://2`
+    `$inject -newImfile -exp_id 10 -class OTA -class_id 3 -uri file://3`
+
+=cut
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/magic_flow.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/magic_flow.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/magic_flow.txt	(revision 22322)
@@ -0,0 +1,65 @@
+### Set up a run:
+magictool -definerun -workdir /path/to/workdir
+magictool -addinputskyfile -magic_id 6 -diff_id 1 -node 5.1
+magictool -addinputskyfile -magic_id 6 -diff_id 2 -node 5.2
+magictool -addinputskyfile -magic_id 6 -diff_id 3 -node 5.3
+magictool -addinputskyfile -magic_id 6 -diff_id 4 -node 5.4
+magictool -updaterun -magic_id 6 -state run
+
+### Set up a run in a single call:
+magictool -queue -select_XXX
+
+### Generate a processing tree
+magictool -totree
+#magic_tree.pl --magic_id X [camera, tess_id, ra0, dec0]
+* magictool -inputskyfile -magic_id 6
+* magictool -inputtree -magic_id 6 -dep_file magic_dep.mdc
+
+### Process the tree
+magictool -toprocess
+#magicMe  METADATA
+#   magic_id         S64       5
+#   node             STR       5.1
+#   dep              STR       NULL
+#   done             S64       0
+#   uri              STR       NULL
+#END
+#magic_process.pl --magic_id 6 --node 5.1
+magictool -inputs -magic_id 6 -node 5.1
+
+magictool -addresult -magic_id 6 -node 5.1 -uri foo
+magictool -addresult -magic_id 6 -node 5.2 -uri bar
+magictool -addresult -magic_id 6 -node 5.3 -uri baz
+magictool -addresult -magic_id 6 -node 5.4 -uri qix
+
+### Merging the tree
+magictool -toprocess
+#magicMe  METADATA
+#   magic_id         S64       5
+#   node             STR       5A
+#   uri MULTI
+#   uri              STR       foo
+#   uri              STR       bar
+#END
+#magic_process.pl --magic_id 6 --node 5A
+magictool -inputs -magic_id 6 -node 5A
+
+magictool -addresult -magic_id 6 -node 5A -uri foobar
+magictool -addresult -magic_id 6 -node 5B -uri bazqix
+
+### Final merge
+magictool -toprocess
+#magicMe  METADATA
+#   magic_id         S64       5
+#   node             STR       root
+#END
+#magic_process.pl --magic_id 6 --node root
+magictool -inputs -magic_id 6 -node root
+
+magictool -addresult -magic_id 6 -node root -uri foobarbazqix
+
+### Generate the mask description
+magictool -tomask
+magic_mask.pl --magic_id X
+magictool -inputs -magic_id 6 -node root
+magictool -addmask -magic_id 6 -uri foobarbazqixqit
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/p0tools.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/p0tools.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/p0tools.txt	(revision 22322)
@@ -0,0 +1,40 @@
+
+Phase 0 pipeline tools:
+
+p0search -pending :
+  * examine the new.imfiles,new.exposures tables and select exposures ready for analysis
+  * output is: (expID) (camera)
+
+p0search -update (expID) (camera):
+  * select a the corresponding images from the new.imfiles/new.exposures table
+  * extract the specified header information
+  * search the summit metadata db tables
+  * write an entry to the raw.imfiles and raw.exposure tables
+  * set the state on the new.imfiles,new.exposure tables
+  * based on the camera config information;
+    * add an entry to the p1.pending table (mosaic) 
+    - or
+    * add an entry to the p2.pending table (single)
+
+p0search -stats (expID) (camera):
+  * select a specified image in the new.imfiles/new.exposures table
+  * extract the specified header information
+  * search the summit metadata db tables
+  * report the image stats
+  [-update without output to MDDB]
+
+p0search -mkraw (expID) (camera):
+  * select a specified image in the new.imfiles/new.exposures table
+  * extract the specified header information
+  * search the summit metadata db tables
+  * write an entry to the raw.imfiles and raw.exposure tables
+  * set the state on the new.imfiles,new.exposure tables
+
+p0search -cleanup:
+  * remove completed entries from the new.imfiles,new.exposure tables
+
+** note : the division of -pending and -update allows separate processes
+   to be examining the image headers and measuring some stats.  these
+   jobs can be run via pcontrol to reduce the load on the PanTasks
+   machines
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/p1tools.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/p1tools.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/p1tools.txt	(revision 22322)
@@ -0,0 +1,16 @@
+
+Phase 1 pipeline tools:
+
+p1search -define [constraints]:
+  * examine the raw.exposures tables and select exposures matching the given criteria
+  * add entries which are allowed (mosaic) to the p1.pending table
+
+p1search -pending :
+  * examine the p1.pending table and select exposures waiting for p1
+  * output: lines consisting of:
+    (expID) (p1version) (camera)
+
+p1search -done :
+  * select completed entries in the p1.pending table
+  * move to the p1.done table
+  * add new entry to the p2.pending tables
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/p2search.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/p2search.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/p2search.sh	(revision 22322)
@@ -0,0 +1,8 @@
+#!/bin/csh -f
+
+echo "# expID class classID url"
+echo "654320o fpa megacam 654320o.fits"
+echo "654321o fpa megacam 654321o.fits"
+echo "654322o fpa megacam 654322o.fits"
+echo "654323o fpa megacam 654323o.fits"
+echo "654324o fpa megacam 654324o.fits"
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/p2tools.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/p2tools.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/p2tools.txt	(revision 22322)
@@ -0,0 +1,41 @@
+
+Phase 2 pipeline tools:
+
+p2search -quick 
+  * search for images which match in raw.exp,raw.imfiles
+  * output in format which can be used by ppImage pantasks script
+
+p2search -define [options]
+  * input: searches mddb:raw_exposures,raw_images
+        - compares against p2Pending[Exp|Imfile]
+        - compares against p2Done[Exp|Imfile]
+        - XXX should be implemented as a special wrapper function that
+          implements this comparison as a SQL query
+  * output: updates mddb:P2_exposures_pending,P2_images_pending
+  * alternative output: identical to p2pending
+ 
+p2search -pending 
+  * input: searches mddb:P2_exposures_pending,P2_images_pending
+  * output: Nlines consisting of:
+    (URL) (expID) (class)
+  * options: ?
+
+p2search -update
+  * examine the imfiles and identify any completed exposures
+
+p2search -done
+  * get exp_id/class/class_id from the CLI
+  * add the completed imfile to the p2DoneImfile tables
+  * remove corresponding entries from the p2PendingImfile table
+  * check to see if any p2PendingExps have no associated p2PendingImfiles
+        if so move the p2PendingExp(s) to p2DoneExp
+//  * send new entry to the pending p3 table
+
+ppImage file://path/filename file://path/outroot -recipe (recipe) 
+ppImage neb://nebname neb://outroot -recipe (recipe)
+
+restriction options:
+  -time (start) (stop)
+  -camera (camera) 
+  -region (ra,dec) (ra,dec)
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/p3tools.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/p3tools.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/p3tools.txt	(revision 22322)
@@ -0,0 +1,22 @@
+
+Phase 3 pipeline tools:
+
+p3search -define :
+  * examine the raw.exposures tables and select exposures matching the given criteria
+  * add entries which are allowed (mosaic) to the p3.pending table
+
+p3search -quick :
+  * examine the raw.exposures tables and select exposures matching the given criteria
+  * return list of entries for p3 processing
+  * output: lines consisting of:
+    (expID) (p3version) (camera)
+
+p3search -pending :
+  * examine the p3.pending exposures table and select exposures waiting for p3
+  * return list of entries for p3 processing
+  * output: lines consisting of:
+    (expID) (p3version) (camera)
+
+p3search -done :
+  * select completed entries in the p3.pending table
+  * move to the p3.done table
Index: /tags/sj_tags/sj_root_20080929/ippTools/doc/summit_copy.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/doc/summit_copy.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/doc/summit_copy.txt	(revision 22322)
@@ -0,0 +1,49 @@
+The summit copy phase (phase Z or pz) consists of three basic steps.
+Discovering what exposures and available, discovering what image files are
+associated with those exposures, and the downloading of image files.  All
+"external" communication with the DataStore is done with three utilities;
+pzgetexp, pzgetimfiles & dsget.  While local state information is accessed and
+updated via the 'pztool' utility.
+
+The basic work follow is:
+
+pzgetexp retrieves a list of all of the exposures (filesets) available from a
+DataStore "product".  If any previously unknown file exposures are found they
+are added to the pzPendingExp tables and the summitExp table.  New entries to
+the summitExp table have a imfile value of NULL as this information can't yet
+be known.  If any exposures are already in the summitExp table the most recent
+exp_id is used to constrain the result set requested from the DataStore.
+
+    ./pzgetexp -uri http://otis/ds/skyprobe/ -inst skyprobe -telescope ps1
+
+The results of pzgetexp and accessible with pztool -pendingexp`.  Which will
+return a list of new exposures from the pzPendingExp table.
+
+    ./pztool -pendingexp
+
+The output of which is used to invoke pzgetimfiles.
+
+pzgetimfiles retrieves a list of all of the imfiles (files) in an exposure
+(fileset) from the DataStore.  It then updates the imfiles count in the
+summitExp table and adds each imfile to the pzPendingImfile table.
+
+./pzgetimfiles -uri http://otis/ds/skyprobe/sep8_twi_05/ -filesetid sep8_twi_05 -inst skyprobe -telescope ps1
+
+pztool -pendingimfile returns the list of imfiles that need to be downloaded.
+
+    ./pztool -pendingimfile
+
+The results of which are used to invoke dsget and pztool -copydone after dsget
+completes it's download.
+
+dsget from the 'DataStore' package takes the follow parameters.
+
+    dsget --uri <uri> --filename <filename> [--bytes <nbytes>] [--md5 <hex>]
+
+Where "filename" may be either just a filename relative to the CWD or a fully
+qualified path.  After dsget has copied the file pztool -copydone needs to be
+invoked
+
+    ./pztool -copydone -exp_id sep8_twi_03 -inst skyprobe -telescope ps1 -class chip -class_id sep8_twi_03.fits -uri file///data/alala/sep8_twi_03.fits
+
+And then we're Pau.
Index: /tags/sj_tags/sj_root_20080929/ippTools/m4/ac_prog_perl_modules.m4
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/m4/ac_prog_perl_modules.m4	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/m4/ac_prog_perl_modules.m4	(revision 22322)
@@ -0,0 +1,53 @@
+dnl @synopsis AC_PROG_PERL_MODULES([MODULES], [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+dnl
+dnl Checks to see if the the given perl modules are available. If true
+dnl the shell commands in ACTION-IF-TRUE are executed. If not the shell
+dnl commands in ACTION-IF-FALSE are run. Note if $PERL is not set (for
+dnl example by calling AC_CHECK_PROG, or AC_PATH_PROG),
+dnl AC_CHECK_PROG(PERL, perl, perl) will be run.
+dnl
+dnl Example:
+dnl
+dnl   AC_CHECK_PERL_MODULES(Text::Wrap Net::LDAP, ,
+dnl                         AC_MSG_WARN(Need some Perl modules)
+dnl
+dnl @category InstalledPackages
+dnl @author Dean Povey <povey@wedgetail.com>
+dnl @version 2002-09-25
+dnl @license AllPermissive
+
+AC_DEFUN([AC_PROG_PERL_MODULES],[
+    ac_perl_modules="$1"
+    # Make sure we have perl
+    if test -z "$PERL"; then
+        AC_CHECK_PROG(PERL,perl,perl)
+    fi
+
+    if test "x$PERL" != x; then
+        ac_perl_modules_failed=0
+        for ac_perl_module in $ac_perl_modules; do
+            AC_MSG_CHECKING(for perl module $ac_perl_module)
+
+            # Would be nice to log result here, but can't rely on autoconf
+            # internals
+            $PERL "-M$ac_perl_module" -e exit > /dev/null 2>&1
+            if test $? -ne 0; then
+                AC_MSG_RESULT(no);
+                ac_perl_modules_failed=1
+            else
+                AC_MSG_RESULT(ok);
+            fi
+        done
+
+        # Run optional shell commands
+        if test "$ac_perl_modules_failed" = 0; then
+            :
+            $2
+        else
+            :
+            $3
+        fi
+    else
+        AC_MSG_WARN(could not find perl)
+    fi
+])
Index: /tags/sj_tags/sj_root_20080929/ippTools/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/notes.txt	(revision 22322)
@@ -0,0 +1,63 @@
+
+2008.07.19 EAM
+
+  * need to add cleanup modes to all stages.  here is the minimum list
+    of stages that need the cleanups:
+
+    * chiptool - DONE
+    * camtool 
+    * faketool
+    * warptool
+    * difftool
+    * stacktool
+    * dettool processedimfile
+    * dettool processedexp
+    * dettool stackedimfile
+    * dettool normalizedstat
+    * dettool normalizedimfile
+    * dettool normalizedexp
+    * dettool residimfile
+    * dettool residexp
+
+    * I am modifiying the tools to use the new states: new, full, goto_cleaned, etc.
+      I am NOT changing: regtool, pztool, magictool, flatcorr, pxregister, pstamptool
+
+    * for dettool, I have added a new field, 'data_state' to the
+      detRunSummary.  My plan here is that a complete detRunIteration
+      is cleaned at once.  I've added functions to all of the dettool
+      major modes to list pending entries to be cleaned and a function
+      to update the data_state when cleaning is done.
+
+2008.07.11 EAM 
+
+  * break dettool.c into files for each detrend stage
+  * require -iteration in more places? (esp -add...)
+  * allow %s, %d arguments in the *.sql files or not?
+  * adddetrunsummary has the ability to set values for multiple detRunSummary entries.  why?
+
+2008.07.09 EAM
+
+  In order to implement the 'cleanup' and 'update' strategies, we have
+  changed the state names and defined addtional states as follows:
+
+  * new          : entry is unprocessed (was 'run')
+  * full         : entry is processed and has all output data products (was 'done')
+  * goto_cleaned : entry should be processed by the 'cleanup' system
+  * cleaned   	 : entry has been processed by the 'cleanup' system --
+    		   some output data products are now missing.
+		   
+  * goto_full    : entry should be re-processed by the 'update' system
+  * full         : entry has been re-processed by the 'update' system
+  
+  * goto_purged  : entry should be processed by the 'cleanup' ssytem
+    		   to purge all output data
+  * purged       : entry has been purged
+
+
+
+2008.05.16 EAM
+
+caltool
+  * add the active field, -active flag to -dbs
+  * add the -region 
+  * change 'catdir' to 'dvo_id'
Index: /tags/sj_tags/sj_root_20080929/ippTools/pxtools.pc.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/pxtools.pc.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/pxtools.pc.in	(revision 22322)
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/@PACKAGE_NAME@
+
+Name: @PACKAGE_NAME@
+Description: Pan-STARRS IPP Tools Library
+Version: @VERSION@
+Requires: pslib psmodules ippdb
+Libs: -L${libdir} -lpxtools
+Cflags: -I${includedir} @PXTOOLS_CFLAGS@
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+
+bin_SCRIPTS = \
+	ippadmin
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/camtest.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/camtest.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/camtest.sh	(revision 22322)
@@ -0,0 +1,11 @@
+#!//bin/sh
+
+set -o verbose
+
+chiptest.sh || exit 1
+
+camtool -pendingexp || exit 1
+camtool -pendingimfile || exit 1
+
+camtool -addprocessedexp -cam_id 1 -uri file:///cam -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -sigma_ra 1 -sigma_dec 2 -zp_mean 10 -zp_stdev 2 -n_stars 2 -n_extended 0 -n_astrom 42 -n_cr 10000000 -path_base file:///foo || exit 1
+camtool -addprocessedexp -cam_id 2 -uri file:///cam -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -sigma_ra 1 -sigma_dec 2 -zp_mean 10 -zp_stdev 2 -n_stars 2 -n_extended 0 -n_astrom 42 -n_cr 10000000 -path_base file:///foo || exit 1
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/chiptest.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/chiptest.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/chiptest.sh	(revision 22322)
@@ -0,0 +1,17 @@
+#!//bin/sh
+
+set -o verbose
+
+regtest.sh || exit 1
+
+chiptool -pendingimfile || exit 1
+
+for ID in `seq 0 3`; do
+    chiptool -addprocessedimfile -chip_id 1 -exp_id 1 -class_id $ID -uri file://chipp-t10.$ID -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -path_base file:///foo || exit 1
+done;
+
+for ID in `seq 0 3`; do
+    chiptool -addprocessedimfile -chip_id 2 -exp_id 2 -class_id $ID -uri file://chipp-t11.$ID -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -path_base file:///foo || exit 1
+done;
+
+chiptool -pendingimfile || exit 1
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/dettest.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/dettest.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/dettest.sh	(revision 22322)
@@ -0,0 +1,88 @@
+#!//bin/sh
+
+set -o verbose
+
+det_id=1
+
+./regtest.sh -end_stage reg || exit 1
+
+det_id=`dettool -definebyquery -det_type bias -inst gpc -filelevel fpa -select_exp_type bias -airmass_min 1 -airmass_max 10 -exp_time_min 10 -exp_time_max 30.0 -workdir file::///some/path -simple | cut -f1 -d" "` || exit 1
+
+dettool -raw || exit 1
+
+dettool -toprocessedimfile || exit 1
+
+for ID in `seq 0 3` ; do
+    dettool -addprocessedimfile -det_id 1 -exp_id 1 -class_id $ID -uri file://proc-$ID -recip myrecip -bg 2 -bg_stdev 3 -bg_mean_stdev 4 || exit 1
+done;
+
+for ID in `seq 0 3` ; do
+    dettool -addprocessedimfile -det_id 1 -exp_id 2 -class_id $ID -uri file://proc-$ID -recip myrecip -bg 2 -bg_stdev 3 -bg_mean_stdev 4 || exit 1
+done;
+
+
+dettool -tostacked || exit 1
+
+for ID in `seq 0 3` ; do
+    dettool -addstacked -det_id $det_id -uri file://stacked-$ID -class_id $ID -recip myrecipe -bg 1 -bg_stdev 2 -bg_mean_stdev 3 || exit 1
+done;
+
+dettool -tonormalizedstat || exit 1
+
+for ID in `seq 0 3` ; do
+    dettool -addnormalizedstat -det_id $det_id -class_id $ID -norm 0.12345 || exit 1
+done;
+
+dettool -tonormalize || exit 1
+for ID in `seq 0 3` ; do
+    dettool -addnormalizedimfile -det_id $det_id -class_id $ID -uri file://normalized-$ID -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -path_base banana1 || exit 1
+done;
+
+dettool -tonormalizedexp || exit 1
+dettool -addnormalizedexp -det_id $det_id -recip myrecipe -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -path_base file://normalizedexp || exit 1
+
+dettool -toresidimfile || exit 1
+
+for ID in `seq 0 3` ; do
+    dettool -addresidimfile -det_id $det_id -exp_id 1 -class_id $ID -recip myrecip -bg 1 -bg_stdev 1 -bg_mean_stdev 1 -uri file://resid-$ID || exit 1
+done;
+
+for ID in `seq 0 3` ; do
+    dettool -addresidimfile -det_id $det_id -exp_id 2 -class_id $ID -recip myrecip -bg 1 -bg_stdev 1 -bg_mean_stdev 1 -uri file://resid-$ID || exit 1
+done;
+
+dettool -toresidexp || exit 1
+dettool -addresidexp -det_id $det_id -exp_id 1 -recip myrecipe -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -path_base jpeg1 || exit 1
+dettool -addresidexp -det_id $det_id -exp_id 2 -recip myrecipe -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -path_base jpeg1 -reject || exit 1
+
+dettool -todetrunsummary || exit 1
+dettool -residexp || exit 1
+dettool -updateresidexp -det_id $det_id -iteration 0 -recip yourrecipe || exit 1
+dettool -updateresidexp -det_id $det_id -iteration 0 -exp_id 2 -reject || exit 1
+dettool -updateresidexp -det_id $det_id -iteration 0 -exp_id 2 || exit 1
+
+dettool -adddetrunsummary -det_id $det_id -iteration 0 -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -accept || exit 1
+
+dettool -todetrunsummary || exit 1
+
+dettool -residexp || exit 1
+
+dettool -updatedetrun -det_id $det_id -state stop || exit 1
+
+dettool -register_detrend -det_type bias -mode master -filelevel fpa -workdir file:/// -label foo || exit 1
+dettool -register_detrend_imfile -det_id 2 -class_id 1 -uri file:///foo || exit 1
+dettool -register_detrend_imfile -det_id 2 -class_id 2 -uri file:///foo || exit 1
+dettool -register_detrend_imfile -det_id 2 -class_id 3 -uri file:///foo || exit 1
+dettool -register_detrend_imfile -det_id 2 -class_id 4 -uri file:///foo || exit 1
+dettool -updatedetrun -det_id 2 -state stop || exit 1
+
+# correct test
+dettool -makecorrection -det_id 1 || exit 1
+dettool -tocorrectexp || exit 1
+dettool -tocorrectimfile -det_id 3 || exit 1
+dettool -addcorrectimfile -det_id 3 -class_id 0 -uri file:///correct/0 || exit 1
+dettool -addcorrectimfile -det_id 3 -class_id 1 -uri file:///correct/1 || exit 1
+dettool -addcorrectimfile -det_id 3 -class_id 2 -uri file:///correct/2 || exit 1
+dettool -addcorrectimfile -det_id 3 -class_id 3 -uri file:///correct/3 || exit 1
+# detRun 3 should be automatically set to stop by this point 
+dettool -tocorrectexp || exit 1
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/difftest.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/difftest.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/difftest.sh	(revision 22322)
@@ -0,0 +1,29 @@
+#!//bin/sh
+
+set -o verbose
+
+warptest.sh || exit 1
+
+
+difftool -definerun -workdir file:///tmp/diff -skycell_id foo1 -tess_id bar || exit 1
+difftool -addinputskyfile -diff_id 1 -warp_id 1 -kind warped -template || exit 1
+difftool -addinputskyfile -diff_id 1 -warp_id 1 -kind warped || exit 1
+#difftool -updaterun -state run -diff_id 1 || exit 1
+difftool -todiffskyfile || exit 1
+difftool -inputskyfile || exit 1
+difftool -adddiffskyfile -diff_id 1 -uri file:///tmp/diff/skyfile -path_base file://lalaland -bg 1 -bg_stdev 2 || exit 1
+difftool -diffskyfile -diff_id 1 || exit 1
+difftool -updaterun -state stop -diff_id 1 || exit 1
+
+#
+# diff_id 2
+#
+difftool -definerun -workdir file:///tmp/diff -skycell_id foo1 -tess_id bar || exit 1
+difftool -addinputskyfile -diff_id 2 -warp_id 1 -kind warped -template || exit 1
+difftool -addinputskyfile -diff_id 2 -warp_id 1 -kind warped || exit 1
+#difftool -updaterun -state run -diff_id 1 || exit 1
+difftool -todiffskyfile || exit 1
+difftool -inputskyfile || exit 1
+difftool -adddiffskyfile -diff_id 2 -uri file:///tmp/diff/skyfile -path_base file://lalaland -bg 1 -bg_stdev 2 || exit 1
+difftool -diffskyfile -diff_id 2 || exit 1
+difftool -updaterun -state stop -diff_id 2 || exit 1
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/faketest.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/faketest.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/faketest.sh	(revision 22322)
@@ -0,0 +1,19 @@
+#!//bin/sh
+
+set -o verbose
+
+camtest.sh || exit 1
+
+#faketool -pendingexp || exit 1
+faketool -pendingimfile || exit 1
+
+for ID in `seq 0 3`; do
+    faketool -addprocessedimfile -fake_id 1 -exp_id 1 -class_id $ID -uri file://chipp-t10.$ID -path_base file:///foo || exit 1
+done;
+
+for ID in `seq 0 3`; do
+    faketool -addprocessedimfile -fake_id 2 -exp_id 2 -class_id $ID -uri file://chipp-t11.$ID -path_base file:///foo || exit 1
+done;
+
+faketool -pendingimfile || exit 1
+faketool -processedimfile || exit 1
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/ippadmin
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/ippadmin	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/ippadmin	(revision 22322)
@@ -0,0 +1,77 @@
+#!/bin/csh -f
+## this script creates the tables needed by ippMonitor for user administration
+
+if ($#argv < 4) goto usage;
+
+if ("$1" == "init") then
+  if ($#argv != 4) goto usage;
+  set dbserver = $2
+  set dbuser = $3
+  set dbpass = $4
+
+  mysql -h $dbserver -u root -p <<EOF
+  create database ippadmin;
+  use ippadmin;
+  create table users (username char(20) not null, password varchar(255));
+  create table cookies (username char(20) not null, cookie varchar(255));
+  create table projects (projname char(20) not null);
+  grant all on *.* to $dbuser@localhost identified by '$dbpass';
+EOF
+    exit 0;
+endif
+
+if ("$1" == "client") then
+  if ($#argv != 5) goto usage;
+  set dbserver = $2
+  set dbuser = $3
+  set dbpass = $4
+  set client = $5
+
+  mysql -h $dbserver -u root -p <<EOF
+   grant all on *.* to $dbuser@"$client" identified by '$dbpass';
+EOF
+    exit 0;
+endif
+
+if ("$1" == "user") then
+  if ($#argv != 5) goto usage;
+  set dbserver = $2
+  set dbuser = $3
+  set user = $4
+  set pass = $5
+
+  mysql -h $dbserver -u $dbuser -p ippadmin <<EOF
+  insert into users (username, password) values ('$user', '$pass');
+EOF
+    exit 0;
+endif
+
+if ("$1" == "project") then
+  if ($#argv != 4) goto usage;
+  set dbserver = $2
+  set dbuser = $3
+  set dbname = $4
+
+  mysql -h $dbserver -u $dbuser -p ippadmin <<EOF
+   insert into projects (projname) values ('$dbname');
+   create database $dbname;
+EOF
+    exit 0;
+endif
+
+usage:
+  echo "USAGE: ippadmin (options)"
+  echo ""
+  echo "  ippadmin init (dbserver) (dbuser) (dbpassword)"
+  echo "      creates admin tables, basic db user -- requires root password"
+  echo ""
+  echo "  ippadmin client (dbserver) (dbuser) (dbpassword) (client)"
+  echo "      add client name or regex"
+  echo ""
+  echo "  ippadmin user (dbserver) (dbuser) (username) (password)"
+  echo "      create a new www user and password"
+  echo ""
+  echo "  ippadmin project (dbserver) (dbuser) (dbname)"
+  echo "      create a new ipp project (database)"
+  echo ""
+  exit 2
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/magic_dep.md
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/magic_dep.md	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/magic_dep.md	(revision 22322)
@@ -0,0 +1,6 @@
+# node      dep
+a   STR     NULL
+b   STR     NULL
+root   MULTI
+root   STR     a
+root   STR     b
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/magictest.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/magictest.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/magictest.sh	(revision 22322)
@@ -0,0 +1,28 @@
+#!//bin/sh
+
+set -o verbose
+
+difftest.sh || exit 1
+
+
+magictool -definerun -workdir file:///foo/bar/ || exit 1
+magictool -addinputskyfile -magic_id 1 -diff_id 1 -node a || exit 1
+magictool -addinputskyfile -magic_id 1 -diff_id 2 -node b || exit 1
+magictool -inputtree -magic_id 1 -dep_file magic_dep.md 
+magictool -updaterun -state run -magic_id 1 || exit 1
+
+magictool -addresult -magic_id 1 -node a -uri file:///foo/a
+magictool -addresult -magic_id 1 -node b -uri file:///foo/b
+magictool -addresult -magic_id 1 -node root -uri file:///foo/root
+magictool -tomask
+magictool -addmask -magic_id 1 -uri file:///foo/mask
+magictool -toskyfilemask
+magictool -addskyfilemask -magic_id 1 -diff_id 1 -uri file:///foo/mask_1
+magictool -addskyfilemask -magic_id 1 -diff_id 2 -uri file:///foo/mask_2
+magictool -updaterun -state stop -magic_id 1 || exit 1
+
+#difftool -todiffskyfile || exit 1
+#difftool -inputskyfile || exit 1
+#difftool -adddiffskyfile -diff_id 1 -uri file:///tmp/diff/skyfile -path_base file://lalaland -bg 1 -bg_stdev 2 || exit 1
+#difftool -diffskyfile -diff_id 1 || exit 1
+#difftool -updaterun -state stop -diff_id 1 || exit 1
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/mapfile.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/mapfile.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/mapfile.txt	(revision 22322)
@@ -0,0 +1,19 @@
+warpSkyCellMap MULTI
+
+warpSkyCellMap METADATA
+    warp_id         S64     1
+    cam_id          S64     1
+    skycell_id      STR     foo1
+    tess_id         STR     bar
+    class_id        STR     quix1
+    fault           S16     0
+END
+
+warpSkyCellMap METADATA
+    warp_id         S64     1
+    cam_id          S64     1
+    skycell_id      STR     foo2
+    tess_id         STR     bar
+    class_id        STR     quix2
+    fault           S16     0
+END
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/regtest.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/regtest.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/regtest.sh	(revision 22322)
@@ -0,0 +1,49 @@
+#!//bin/sh
+
+set -o verbose
+
+inject="pxinject"
+regtool="regtool"
+
+echo -e "YES\njhipp\n\n" | pxadmin -recreate || exit 1
+
+exp_id1=`$inject -newExp -tmp_exp_name t10 -tmp_inst gpc -tmp_telescope ps1 -workdir file::///some/path -reduction batman -simple` || exit 1
+
+echo $exp_id1
+
+for ID in `seq 0 3`; do
+    $inject -newImfile -exp_id $exp_id1 -tmp_class_id tmp.$ID -uri file://$ID || exit 1
+done;
+
+$inject -updatenewExp -exp_id $exp_id1 -state run
+
+exp_id2=`$inject -newExp -tmp_exp_name t11 -tmp_inst gpc -tmp_telescope ps1 -workdir file::///some/path -reduction robin -simple` || exit 1
+
+for ID in `seq 0 3`; do
+    $inject -newImfile -exp_id $exp_id2 -tmp_class_id tmp.$ID -uri file://$ID || exit 1
+done;
+
+$inject -updatenewExp -exp_id $exp_id2 -state run
+
+
+$regtool -pendingimfile || exit 1
+
+for ID in `seq 0 3`; do
+    $regtool -addprocessedimfile -exp_id $exp_id1 -exp_name t10 -inst gpc -telescope ps1 -tmp_class_id tmp.$ID -class_id $ID -filter r -airmass 10 -ra 1 -decl 2 -exp_time 0 -bg 1 -bg_stdev 1 -bg_mean_stdev 10 -alt 10 -az 10 -ccd_temp 10 -posang 10 -object dog -dateobs 2006-10-20T10:10:10Z -uri file://$ID || exit 1
+done;
+
+$regtool -pendingexp|| exit 1
+
+$regtool -addprocessedexp -exp_id $exp_id1 -exp_name t10 -inst gpc -telescope ps1 -exp_tag batman.t10 -filelevel OTA -filter r -airmass 10 -ra 1 -decl 2 -exp_type object -exp_time 0 -bg 10 -bg_stdev 1 -bg_mean_stdev 10 -alt 10 -az 10 -ccd_temp 45 -posang 10 -object dog -dateobs "2006-10-20T10:10:10Z" -label foobar -tess_id moldyshoe $* || exit 1
+# use BIAS or OBJECT? $regtool -addprocessedexp -exp_id $exp_id1 -exp_name t10 -inst gpc -telescope ps1 -exp_tag batman.t10 -filelevel OTA -filter r -airmass 10 -ra 1 -decl 2 -exp_type bias -exp_time 0 -bg 10 -bg_stdev 1 -bg_mean_stdev 10 -alt 10 -az 10 -ccd_temp 45 -posang 10 -object dog -dateobs "2006-10-20T10:10:10Z" -label foobar -tess_id moldyshoe $* || exit 1
+
+$regtool -pendingimfile || exit 1
+
+for ID in `seq 0 3`; do
+    $regtool -addprocessedimfile -exp_id $exp_id2 -exp_name t11 -inst gpc -telescope ps1  -tmp_class_id tmp.$ID -class_id $ID -filter r -airmass 10 -ra 1 -decl 2 -exp_time 0 -bg 1 -bg_stdev 1 -bg_mean_stdev 10 -alt 10 -az 10 -ccd_temp 10 -posang 10 -object dog -dateobs 2006-10-20T10:10:10Z -uri file://$ID  || exit 1
+done;
+
+$regtool -pendingexp|| exit 1
+
+$regtool -addprocessedexp -exp_id $exp_id2 -exp_name t11 -inst gpc -telescope ps1 -exp_tag batman.t11 -filelevel OTA -filter r -airmass 11 -ra 1 -decl 2 -exp_type object -exp_time 0 -bg 11 -bg_stdev 1 -bg_mean_stdev 11 -alt 11 -az 11 -ccd_temp 45 -posang 11 -object dog -dateobs 2006-10-20T10:10:10Z -label foobar -tess_id moldyshoe $* || exit 1
+# BIAS or OBJECT? $regtool -addprocessedexp -exp_id $exp_id2 -exp_name t11 -inst gpc -telescope ps1 -exp_tag batman.t11 -filelevel OTA -filter r -airmass 11 -ra 1 -decl 2 -exp_type bias -exp_time 0 -bg 11 -bg_stdev 1 -bg_mean_stdev 11 -alt 11 -az 11 -ccd_temp 45 -posang 11 -object dog -dateobs 2006-10-20T10:10:10Z -label foobar -tess_id moldyshoe $* || exit 1
Index: /tags/sj_tags/sj_root_20080929/ippTools/scripts/warptest.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/scripts/warptest.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/scripts/warptest.sh	(revision 22322)
@@ -0,0 +1,30 @@
+#!//bin/sh
+
+set -o verbose
+
+faketest.sh || exit 1
+
+#warp tool -definerun -fake_id 1 -mode warp -workdir file:///foo 
+
+#warptool -addinputexp -warp_id 1 -cam_id 1 || exit 1
+#warptool -addinputexp -warp_id 1 -cam_id 2 || exit 1
+#warptool -updaterun -warp_id 1 -state run || exit 1
+
+warptool -exp -warp_id 1 || exit 1
+
+warptool -imfile -warp_id 1 || exit 1
+
+warptool -tooverlap -warp_id 1 || exit 1
+
+warptool -addoverlap -mapfile mapfile.txt || exit 1
+
+warptool -scmap || exit 1
+
+warptool -towarped || exit 1
+
+warptool -addwarped -warp_id 1 -skycell_id foo1 -tess_id bar -uri file:///tmp/foo -path_base file://wonderland -bg 1 -bg_stdev 2 || exit 1
+warptool -addwarped -warp_id 1 -skycell_id foo2 -tess_id bar -uri file:///tmp/foo -path_base file://wonderland -bg 1 -bg_stdev 2 || exit 1
+
+warptool -warped -warp_id 1 || exit 1
+
+#warptool -updaterun -warp_id 1 -state stop || exit 1
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile.in
+Makefile
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/Makefile.am	(revision 22322)
@@ -0,0 +1,164 @@
+dist_pkgdata_DATA = \
+     camtool_donecleanup.sql \
+     camtool_find_chip_id.sql \
+     camtool_find_pendingexp.sql \
+     camtool_find_pendingimfile.sql \
+     camtool_find_processedexp.sql \
+     camtool_pendingcleanupexp.sql \
+     camtool_pendingcleanuprun.sql \
+     camtool_queue_chip_id.sql \
+     camtool_reset_faulted_runs.sql \
+     camtool_revertprocessedexp.sql \
+     chiptool_change_exp_state.sql \
+     chiptool_change_imfile_data_state.sql \
+     chiptool_completely_processed_exp.sql \
+     chiptool_donecleanup.sql \
+     chiptool_find_rawexp.sql \
+     chiptool_pendingcleanupimfile.sql \
+     chiptool_pendingcleanuprun.sql \
+     chiptool_pendingimfile.sql \
+     chiptool_processedimfile.sql \
+     chiptool_revertprocessedimfile.sql \
+     chiptool_run.sql \
+     chiptool_unmasked.sql \
+     detselect_search.sql \
+     detselect_select.sql \
+     dettool_addprocessedexp.sql \
+     dettool_childlessrun.sql \
+     dettool_definebydetrun.sql \
+     dettool_detrunsummary.sql \
+     dettool_find_completed_runs.sql \
+     dettool_input.sql \
+     dettool_normalizedexp.sql \
+     dettool_normalizedimfile.sql \
+     dettool_normalizedstat.sql \
+     dettool_pending.sql \
+     dettool_pendingcleanup_normalizedexp.sql \
+     dettool_pendingcleanup_normalizedimfile.sql \
+     dettool_pendingcleanup_normalizedstat.sql \
+     dettool_pendingcleanup_processedexp.sql \
+     dettool_pendingcleanup_processedimfile.sql \
+     dettool_pendingcleanup_residexp.sql \
+     dettool_pendingcleanup_residimfile.sql \
+     dettool_pendingcleanup_stacked.sql \
+     dettool_processedimfile.sql \
+     dettool_raw.sql \
+     dettool_residexp.sql \
+     dettool_residimfile.sql \
+     dettool_revertdetrunsummary.sql \
+     dettool_revertnormalizedexp.sql \
+     dettool_revertnormalizedimfile.sql \
+     dettool_revertnormalizedstat.sql \
+     dettool_revertprocessedexp.sql \
+     dettool_revertprocessedimfile.sql \
+     dettool_revertresidexp.sql \
+     dettool_revertresidimfile.sql \
+     dettool_revertstacked.sql \
+     dettool_runs.sql \
+     dettool_stacked.sql \
+     dettool_start_new_iteration.sql \
+     dettool_stop_completed_correct_runs.sql \
+     dettool_tocorrectexp.sql \
+     dettool_tocorrectimfile.sql \
+     dettool_todetrunsummary.sql \
+     dettool_tonormalize.sql \
+     dettool_tonormalizedexp.sql \
+     dettool_tonormalizedstat.sql \
+     dettool_toprocessedexp.sql \
+     dettool_toprocessedimfile.sql \
+     dettool_toresidexp.sql \
+     dettool_toresidimfile.sql \
+     dettool_tostacked.sql \
+     difftool_definebyquery.sql \
+     difftool_donecleanup.sql \
+     difftool_inputskyfile.sql \
+     difftool_pendingcleanuprun.sql \
+     difftool_pendingcleanupskyfile.sql \
+     difftool_revertdiffskyfile_delete.sql \
+     difftool_revertdiffskyfile_update.sql \
+     difftool_skyfile.sql \
+     difftool_todiffskyfile.sql \
+     faketool_change_exp_state.sql \
+     faketool_change_imfile_data_state.sql \
+     faketool_completely_processed_exp.sql \
+     faketool_donecleanup.sql \
+     faketool_find_camrun.sql \
+     faketool_find_pendingexp.sql \
+     faketool_pendingcleanupimfile.sql \
+     faketool_pendingcleanuprun.sql \
+     faketool_pendingimfile.sql \
+     faketool_processedimfile.sql \
+     faketool_queue_cam_id.sql \
+     faketool_revertprocessedimfile.sql \
+     faketool_unmasked.sql \
+     flatcorr_chiprundone.sql \
+     flatcorr_pendingprocess.sql \
+     flatcorr_inputimfile.sql \
+     magictool_addmask.sql \
+     magictool_create_tmp_warpcomplete.sql \
+     magictool_definebyquery.sql \
+     magictool_definebyquery_insert.sql \
+     magictool_definebyquery_select_part1.sql \
+     magictool_definebyquery_select_part2.sql \
+     magictool_definebyquery_select_test.sql \
+     magictool_definebyquery_temp_create.sql \
+     magictool_definebyquery_temp_insert.sql \
+     magictool_definebyquery_temp_insert_groupby.sql \
+     magictool_inputs.sql \
+     magictool_inputskyfile.sql \
+     magictool_mask.sql \
+     magictool_tomask.sql \
+     magictool_toprocess_inputs.sql \
+     magictool_toprocess_tree.sql \
+     magictool_toskyfilemask.sql \
+     magictool_totree.sql \
+     pstamptool_addjob_otherjob.sql \
+     pstamptool_addjob_stampjob.sql \
+     pstamptool_datastore.sql \
+     pstamptool_pendingjob.sql \
+     pstamptool_pendingreq.sql \
+     pstamptool_project.sql \
+     pxadmin_create_tables.sql \
+     pxadmin_drop_tables.sql \
+     pztool_find_completed_exp.sql \
+     pztool_pendingimfile.sql \
+     pztool_revert_downloadimfile_faults.sql \
+     pztool_revert_fileset_faults.sql \
+     pztool_revertcopied.sql \
+     regtool_create_dup_table.sql \
+     regtool_pendingexp.sql \
+     regtool_pendingimfile.sql \
+     regtool_populate_dup_table.sql \
+     regtool_processedexp.sql \
+     regtool_processedimfile.sql \
+     regtool_revertprocessedexp.sql \
+     regtool_revertprocessedimfile.sql \
+     stacktool_definebyquery_insert.sql \
+     stacktool_definebyquery_insert_random_part1.sql \
+     stacktool_definebyquery_insert_random_part2.sql \
+     stacktool_definebyquery_part1.sql \
+     stacktool_definebyquery_part2.sql \
+     stacktool_definebyquery_test.sql \
+     stacktool_donecleanup.sql \
+     stacktool_find_complete_warps.sql \
+     stacktool_inputskyfile.sql \
+     stacktool_pendingcleanuprun.sql \
+     stacktool_pendingcleanupskyfile.sql \
+     stacktool_revertsumskyfile_delete.sql \
+     stacktool_revertsumskyfile_update.sql \
+     stacktool_sumskyfile.sql \
+     stacktool_tosum.sql \
+     warptool_change_skyfile_data_state.sql \
+     warptool_change_run_state.sql \
+     warptool_definebyquery.sql \
+     warptool_donecleanup.sql \
+     warptool_exp.sql \
+     warptool_imfile.sql \
+     warptool_pendingcleanuprun.sql \
+     warptool_pendingcleanupskyfile.sql \
+     warptool_revertwarped.sql \
+     warptool_scmap.sql \
+     warptool_tooverlap.sql \
+     warptool_towarped.sql \
+     warptool_updateskyfile.sql \
+     warptool_warped.sql
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_donecleanup.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_donecleanup.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_donecleanup.sql	(revision 22322)
@@ -0,0 +1,5 @@
+SELECT
+    camRun.*
+FROM camRun
+WHERE
+    camRun.state = 'cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_chip_id.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_chip_id.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_chip_id.sql	(revision 22322)
@@ -0,0 +1,31 @@
+SELECT
+    *
+FROM
+    (SELECT DISTINCT
+        chipRun.*,
+        rawExp.camera,
+        rawExp.telescope,
+        rawExp.dateobs,
+        rawExp.exp_tag,
+        rawExp.exp_type,
+        rawExp.filelevel,
+        rawExp.filter,
+        rawExp.airmass,
+        rawExp.ra,
+        rawExp.decl,
+        rawExp.exp_time,
+        rawExp.sat_pixel_frac,
+        rawExp.bg,
+        rawExp.bg_stdev,
+        rawExp.bg_mean_stdev,
+        rawExp.alt,
+        rawExp.az,
+        rawExp.ccd_temp,
+        rawExp.posang,
+        rawExp.object,
+        rawExp.solang
+    FROM chipRun
+    JOIN rawExp
+        using(exp_id)
+    WHERE
+        chipRun.state = 'full') as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_pendingexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_pendingexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_pendingexp.sql	(revision 22322)
@@ -0,0 +1,30 @@
+-- this query is used by both camtool -pendingexp & camtool -addprocessedexp it
+-- does a little more work then is necessary for -addprocessed but it seems
+-- "cleaner" to use the same query for both cases 
+SELECT * FROM
+    (SELECT
+        camRun.*,
+        rawExp.exp_tag,
+        rawExp.exp_id,
+        rawExp.exp_name,
+        rawExp.camera,
+        rawExp.telescope,
+        rawExp.filelevel
+    FROM camRun
+    JOIN chipRun
+        USING(chip_id)
+--  JOIN chipProcessedImfile
+--      USING(chip_id)
+    JOIN rawExp
+        USING(exp_id)
+--      ON chipProcessedImfile.exp_id = rawExp.exp_id
+    LEFT JOIN camProcessedExp
+        USING(cam_id)
+    LEFT JOIN camMask
+        ON camRun.label = camMask.label
+    WHERE
+        chipRun.state = 'full'
+        AND ((camRun.state = 'new' AND camProcessedExp.cam_id IS NULL)
+            OR camRun.state = 'update')
+        AND camMask.label IS NULL
+    ) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_pendingimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_pendingimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_pendingimfile.sql	(revision 22322)
@@ -0,0 +1,40 @@
+SELECT DISTINCT * FROM 
+     -- the subselect is so where criteria can be specified without knowing
+     -- which table the field came from
+    (SELECT
+        camRun.*,
+        chipProcessedImfile.exp_id,
+        chipProcessedImfile.class_id,
+        chipProcessedImfile.uri,
+        chipProcessedImfile.bg,
+        chipProcessedImfile.bg_stdev,
+        chipProcessedImfile.bg_mean_stdev,
+        chipProcessedImfile.fringe_0,
+        chipProcessedImfile.fringe_1,
+        chipProcessedImfile.fringe_2,
+        chipProcessedImfile.ap_resid,
+        chipProcessedImfile.ap_resid_stdev,
+        chipProcessedImfile.sigma_ra,
+        chipProcessedImfile.sigma_dec,
+        chipProcessedImfile.n_stars,
+        chipProcessedImfile.n_extended,
+        chipProcessedImfile.n_cr,
+        chipProcessedImfile.n_astrom,
+        chipProcessedImfile.path_base,
+        rawExp.exp_name,
+        rawExp.camera,
+        rawExp.telescope,
+        rawExp.filelevel
+    FROM camRun
+    JOIN chipRun
+        USING(chip_id)
+    JOIN chipProcessedImfile
+        USING(chip_id)
+    JOIN rawExp
+        ON chipProcessedImfile.exp_id = rawExp.exp_id
+    LEFT JOIN camProcessedExp
+        USING(cam_id)
+    LEFT JOIN camMask
+        ON camRun.label = camMask.label
+    WHERE
+        camMask.label IS NULL) as foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_processedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_processedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_find_processedexp.sql	(revision 22322)
@@ -0,0 +1,18 @@
+SELECT DISTINCT
+    camProcessedExp.*,
+    rawExp.exp_tag,
+    rawExp.exp_name,
+    rawExp.camera,
+    rawExp.telescope,
+    rawExp.filelevel
+FROM camRun
+JOIN camProcessedExp
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN chipProcessedImfile
+    USING(chip_id)
+JOIN rawExp
+    ON chipProcessedImfile.exp_id = rawExp.exp_id
+WHERE -- bogus condition so there is a pre-existing where to append to
+    camProcessedExp.cam_id IS NOT NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_pendingcleanupexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_pendingcleanupexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_pendingcleanupexp.sql	(revision 22322)
@@ -0,0 +1,16 @@
+SELECT
+    camProcessedExp.*,
+    camRun.state,
+    camRun.workdir,
+    camRun.label,
+    camRun.reduction,
+    camRun.expgroup,
+    camRun.dvodb,
+    camRun.tess_id,
+    camRun.end_stage
+FROM camRun
+JOIN camProcessedExp
+    USING(cam_id)
+WHERE
+    (camRun.state = 'goto_cleaned' OR camRun.state = 'goto_purged')
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_pendingcleanuprun.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_pendingcleanuprun.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_pendingcleanuprun.sql	(revision 22322)
@@ -0,0 +1,11 @@
+SELECT
+    camRun.cam_id,
+    rawExp.camera,
+    camRun.state
+FROM camRun
+JOIN chipRun
+USING (chip_id)
+JOIN rawExp 
+USING (exp_id)
+WHERE
+    (camRun.state = 'goto_cleaned' OR camRun.state = 'goto_purged')
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_queue_chip_id.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_queue_chip_id.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_queue_chip_id.sql	(revision 22322)
@@ -0,0 +1,19 @@
+-- camtool only operates on exposures so we can safely queue more then one at a
+-- time without worrying about losing the track of the generated cam_id
+INSERT INTO camRun
+    SElECT
+        0,              -- cam_id
+        chip_id,        -- chip_id
+        '%s',           -- state
+        '%s',           -- workdir
+        '%s',           -- workdir_state
+        '%s',           -- label
+        '%s',           -- reduction
+        '%s',           -- expgroup
+        '%s',           -- dvodb 
+        '%s',           -- tess_id
+        '%s'            -- end_stage
+    FROM chipRun
+    WHERE
+        chipRun.state = 'full'
+        AND chipRun.chip_id = %lld
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_reset_faulted_runs.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_reset_faulted_runs.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_reset_faulted_runs.sql	(revision 22322)
@@ -0,0 +1,7 @@
+UPDATE camRun, camProcessedExp, chipRun, rawExp
+SET camRun.state = 'new'
+WHERE
+    camRun.cam_id = camProcessedExp.cam_id
+    AND camRun.chip_id = chipRun.chip_id
+    AND chipRun.exp_id = rawExp.exp_id
+    AND camProcessedExp.fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_revertprocessedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_revertprocessedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/camtool_revertprocessedexp.sql	(revision 22322)
@@ -0,0 +1,7 @@
+DELETE FROM camProcessedExp
+USING camProcessedExp, camRun, chipRun, rawExp
+WHERE
+    camRun.cam_id = camProcessedExp.cam_id
+    AND camRun.chip_id = chipRun.chip_id
+    AND chipRun.exp_id = rawExp.exp_id
+    AND camProcessedExp.fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_change_exp_state.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_change_exp_state.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_change_exp_state.sql	(revision 22322)
@@ -0,0 +1,15 @@
+-- change state of chipRun from goto_cleaned to cleaned or goto_purged to purged
+-- when all of the constituant imfiles are in the right state
+-- arguments are new state (cleaned or purged) chip_id and new state again for 
+-- the chipProcessedImfile sub query
+UPDATE chipRun
+    SET state = '%s'
+    WHERE
+    chipRun.chip_id = %lld
+    AND (SELECT
+        COUNT(chip_id)
+        FROM chipProcessedImfile
+        WHERE
+            chipProcessedImfile.chip_id = chipRun.chip_id
+            AND data_state != '%s'
+        ) = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_change_imfile_data_state.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_change_imfile_data_state.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_change_imfile_data_state.sql	(revision 22322)
@@ -0,0 +1,14 @@
+-- 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
+UPDATE chipProcessedImfile
+    SET 
+    data_state = '%s'
+WHERE
+    chip_id = %lld
+    AND class_id = '%s'
+    -- only update if chipRun.state has the expected value
+    AND (
+        SELECT state from chipRun where chipRun.chip_id = chipProcessedImfile.chip_id
+    ) = '%s'
+    
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_completely_processed_exp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_completely_processed_exp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_completely_processed_exp.sql	(revision 22322)
@@ -0,0 +1,34 @@
+-- the output of this query must match the format of chipRun row
+SELECT DISTINCT
+    chip_id,
+    exp_id,
+    state,
+    workdir,
+    workdir_state,
+    label,
+    reduction,
+    expgroup,
+    dvodb,
+    tess_id,
+    end_stage
+FROM
+    (SELECT 
+        chipRun.*,
+        rawImfile.class_id as rawimfile_class_id,
+        chipProcessedImfile.class_id
+    FROM chipRun
+    JOIN rawImfile
+        USING(exp_id)
+    LEFT JOIN chipProcessedImfile
+        ON chipRun.chip_id = chipProcessedImfile.chip_id
+        AND rawImfile.exp_id = chipProcessedImfile.exp_id
+        AND rawImfile.class_id = chipProcessedImfile.class_id
+    WHERE
+        chipRun.state = 'new'
+    GROUP BY
+        chipRun.chip_id,
+        chipRun.exp_id
+    HAVING
+        COUNT(rawImfile.class_id) = COUNT(chipProcessedImfile.class_id)
+        AND SUM(chipProcessedImfile.fault) = 0
+    ) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_donecleanup.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_donecleanup.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_donecleanup.sql	(revision 22322)
@@ -0,0 +1,5 @@
+SELECT
+    chipRun.*
+FROM chipRun
+WHERE
+    chipRun.state = 'cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_find_rawexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_find_rawexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_find_rawexp.sql	(revision 22322)
@@ -0,0 +1,7 @@
+-- this query is used to find potental rawExps to be queued for chiptool
+-- processeing
+SELECT
+    *
+FROM rawExp
+WHERE
+    rawExp.fault = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_find_unprocessed_imfile.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_find_unprocessed_imfile.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_find_unprocessed_imfile.pl	(revision 22322)
@@ -0,0 +1,8 @@
+SELECT DISTINCT
+   chipInputImfile.*
+FROM chipInputImfile
+LEFT JOIN chipProcessedImfile
+    USING(chip_id, class_id)
+WHERE
+    chipProcessedImfile.chip_id IS NULL
+    AND chipProcessedImfile.class_id IS NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_pendingcleanupimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_pendingcleanupimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_pendingcleanupimfile.sql	(revision 22322)
@@ -0,0 +1,17 @@
+SELECT
+    chipProcessedImfile.*,
+    chipRun.state,
+    chipRun.workdir,
+    chipRun.label,
+    chipRun.reduction,
+    chipRun.expgroup,
+    chipRun.dvodb,
+    chipRun.tess_id,
+    chipRun.end_stage
+FROM chipRun
+JOIN chipProcessedImfile
+    USING(chip_id)
+WHERE
+    ((chipRun.state = 'goto_cleaned' AND chipProcessedImfile.data_state = 'full')
+OR 
+    (chipRun.state = 'goto_purged' AND chipProcessedImfile.data_state != 'purged'))
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_pendingcleanuprun.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_pendingcleanuprun.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_pendingcleanuprun.sql	(revision 22322)
@@ -0,0 +1,9 @@
+SELECT
+    chipRun.chip_id,
+    rawExp.camera,
+    chipRun.state
+FROM chipRun
+JOIN rawExp 
+USING (exp_id)
+WHERE
+    (chipRun.state = 'goto_cleaned' OR chipRun.state = 'goto_purged')
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_pendingimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_pendingimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_pendingimfile.sql	(revision 22322)
@@ -0,0 +1,29 @@
+SELECT
+    chipRun.*,
+    rawImfile.class_id,
+    rawImfile.uri,
+    rawExp.exp_tag,
+    rawExp.exp_name,
+    rawExp.camera,
+    rawExp.telescope,
+    rawExp.filelevel
+FROM chipRun
+JOIN rawExp
+    USING(exp_id)
+JOIN rawImfile
+    USING(exp_id)
+LEFT JOIN chipProcessedImfile
+    ON chipRun.chip_id = chipProcessedImfile.chip_id
+    AND rawImfile.exp_id = chipProcessedImfile.exp_id
+    AND rawImfile.class_id = chipProcessedImfile.class_id
+LEFT JOIN chipMask
+    ON chipRun.label = chipMask.label
+WHERE
+    ((chipRun.state = 'new'
+    AND chipProcessedImfile.chip_id IS NULL
+    AND chipProcessedImfile.exp_id IS NULL
+    AND chipProcessedImfile.class_id IS NULL
+    AND chipMask.label IS NULL)
+    OR
+    (chipRun.state = 'update'
+    AND chipProcessedImfile.data_state = 'cleaned'))
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_processedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_processedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_processedimfile.sql	(revision 22322)
@@ -0,0 +1,23 @@
+SELECT DISTINCT
+    chipProcessedImfile.class_id,
+    chipProcessedImfile.uri,
+    chipProcessedImfile.bg,
+    chipProcessedImfile.bg_stdev,
+    chipProcessedImfile.bg_mean_stdev,
+    chipProcessedImfile.path_base,
+    chipRun.state,
+    rawExp.exp_id,
+    rawExp.exp_tag,
+    rawExp.exp_name,
+    rawExp.camera,
+    rawExp.telescope,
+    rawExp.filelevel
+FROM chipRun
+JOIN chipProcessedImfile
+    USING(chip_id)
+JOIN rawExp
+    ON chipProcessedImfile.exp_id = rawExp.exp_id
+WHERE
+-- bogus test; just here so there there is a 'WHERE' stmt to append conditionals too
+    chipProcessedImfile.exp_id is NOT NULL
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_revertprocessedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_revertprocessedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_revertprocessedimfile.sql	(revision 22322)
@@ -0,0 +1,5 @@
+DELETE FROM chipProcessedImfile
+USING chipProcessedImfile, chipRun, rawExp
+WHERE
+    rawExp.exp_id = chipProcessedImfile.exp_id
+    AND chipProcessedImfile.fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_run.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_run.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_run.sql	(revision 22322)
@@ -0,0 +1,3 @@
+SELECT
+    chipRun.*
+FROM chipRun
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_unmasked.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_unmasked.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/chiptool_unmasked.sql	(revision 22322)
@@ -0,0 +1,43 @@
+-- SELECT
+--  *
+-- FROM
+--  (SELECT 
+--      chipMask.label as label, 
+--      count(chipRun.chip_id) as n_chipruns
+--   FROM 
+--      chipMask 
+--   LEFT JOIN 
+--      chipRun 
+--   USING 
+--      (label) 
+--   WHERE chipRun.chip_id IS NOT NULL 
+--   GROUP BY label) as chipMask
+
+-- SELECT
+--  *
+-- FROM
+--  SELECT 
+--      chipRun.*, 
+--      rawExp.*,
+--      chipRun.chip_id as n_chipruns
+--   FROM 
+--      chipRun 
+--   JOIN rawExp ON chipRun.exp_id = rawExp.exp_id
+--   LEFT JOIN chipMask 
+--   USING (label) 
+--   WHERE chipMask.label IS NULL 
+--   AND filter = 'r'
+--   AND chipRun.exp_id = 61
+--  GROUP BY chipRun.label) as chipUnmask
+
+SELECT
+ *
+FROM
+ (SELECT 
+     chipRun.label, 
+     count(chipRun.chip_id) as n_chipruns
+  FROM chipRun 
+  LEFT JOIN chipMask 
+  USING (label) 
+  WHERE chipMask.label IS NULL 
+  GROUP BY chipRun.label) as chipUnmask
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/detselect_search.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/detselect_search.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/detselect_search.sql	(revision 22322)
@@ -0,0 +1,29 @@
+-- this query needs to use the same fields in both of the tables in
+-- the union statement, but we need to report the
+-- detRunSummary.iteration in the first case, and this is missing in
+-- the second case.
+
+SELECT DISTINCT
+    det_id,
+    good_iteration as iteration,
+    filelevel
+FROM
+    (SELECT DISTINCT
+	detRun.*,
+	detRunSummary.iteration as good_iteration
+    FROM detRun
+    JOIN detRunSummary
+        USING(det_id)
+    WHERE
+       detRun.mode  = 'master'
+       AND detRunSummary.accept = 1
+    UNION
+    SELECT DISTINCT
+	detRun.*,
+	detRun.iteration as good_iteration
+    FROM detRun
+    WHERE
+       (detRun.mode  = 'register' OR detRun.mode = 'correction')
+    ) as Foo
+WHERE
+    (state = 'stop' OR state = 'register')
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/detselect_select.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/detselect_select.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/detselect_select.sql	(revision 22322)
@@ -0,0 +1,27 @@
+SELECT DISTINCT
+    *
+FROM
+    (SELECT DISTINCT
+	detRun.state,
+        detNormalizedImfile.*
+    FROM detNormalizedImfile
+    JOIN detRun
+        USING(det_id)
+    JOIN detRunSummary
+        ON detNormalizedImfile.det_id = detRunSummary.det_id
+        AND detNormalizedImfile.iteration = detRunSummary.iteration
+    WHERE
+        detRun.mode  = 'master'
+        AND detRunSummary.accept = 1
+    UNION
+    SELECT DISTINCT
+	detRun.state,
+        detRegisteredImfile.*
+    FROM detRegisteredImfile
+    JOIN detRun
+        USING(det_id)
+    WHERE
+        (detRun.mode  = 'register' OR detRun.mode = 'correction')
+    ) as Foo
+WHERE
+    (state = 'stop' OR state = 'register')
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_addprocessedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_addprocessedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_addprocessedexp.sql	(revision 22322)
@@ -0,0 +1,34 @@
+SELECT DISTINCT
+   detProcessedImfile.det_id,
+   detRun.iteration,
+   detRun.det_type,
+   detProcessedImfile.exp_id
+FROM detRun
+JOIN detInputExp
+   ON detRun.det_id = detInputExp.det_id
+   AND detRun.iteration = detInputExp.iteration
+JOIN rawExp
+   ON detInputExp.exp_id = rawExp.exp_id
+JOIN detProcessedImfile
+   ON detInputExp.det_id = detProcessedImfile.det_id
+   AND detInputExp.exp_id = detProcessedImfile.exp_id
+LEFT JOIN detProcessedExp
+   ON detInputExp.det_id = detProcessedExp.det_id
+   AND detProcessedImfile.exp_id= detProcessedExp.exp_id
+LEFT JOIN rawImfile
+   ON detInputExp.exp_id = rawImfile.exp_id
+   AND detProcessedImfile.class_id = rawImfile.class_id
+WHERE
+  detRun.state = 'run'
+  AND (detRun.mode = 'master' or detRun.mode = 'verify')
+  AND detProcessedExp.det_id IS NULL
+  AND detProcessedExp.exp_id IS NULL
+  AND detInputExp.include = 1
+  AND detRun.det_id = %lld
+  AND detProcessedImfile.exp_id = %lld
+GROUP BY
+   detProcessedImfile.class_id,
+   rawImfile.class_id,
+   detRun.det_id
+HAVING
+   COUNT(detProcessedImfile.class_id) = COUNT(rawImfile.class_id)
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_childlessrun.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_childlessrun.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_childlessrun.sql	(revision 22322)
@@ -0,0 +1,12 @@
+-- dettool -childlessrun
+
+SELECT DISTINCT
+   detRun.*
+ FROM detRun
+ LEFT JOIN detRun as foo
+   ON foo.ref_det_id = detRun.det_id
+-- XXX do we need to restrict to foo.ref_iter = detRun.iteration ?   
+ WHERE
+   detRun.state = 'stop'
+   AND detRun.mode = 'master'
+   AND foo.det_id IS NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_definebydetrun.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_definebydetrun.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_definebydetrun.sql	(revision 22322)
@@ -0,0 +1,13 @@
+-- dettool -definebydetrun
+
+INSERT INTO detInputExp
+   SELECT
+       %lld,
+       0,
+       detResidExp.exp_id,
+       detResidExp.accept
+   FROM detResidExp
+   JOIN rawExp
+       USING(exp_id)
+   WHERE det_id = %lld
+   AND iteration = %d
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_detrunsummary.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_detrunsummary.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_detrunsummary.sql	(revision 22322)
@@ -0,0 +1,9 @@
+SELECT DISTINCT
+   detRunSummary.*,
+   detRun.det_type,
+   detRun.mode
+ FROM detRun
+ JOIN detRunSummary
+   USING(det_id, iteration)
+ WHERE
+   detRun.state = 'run'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_find_completed_runs.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_find_completed_runs.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_find_completed_runs.sql	(revision 22322)
@@ -0,0 +1,26 @@
+SELECT DISTINCT
+   det_id,
+   iteration
+FROM
+    (SELECT DISTINCT
+        detRun.det_id,
+        detRun.iteration,
+        detInputExp.exp_id
+    FROM detRun
+        LEFT JOIN detInputExp
+        ON detRun.det_id = detInputExp.det_id
+        AND detRun.iteration = detInputExp.iteration
+    LEFT JOIN rawExp
+        ON detInputExp.exp_id = rawExp.exp_id
+    LEFT JOIN detResidExp
+        ON detRun.det_id = detResidExp.det_id
+        AND detRun.iteration = detResidExp.iteration
+        AND detInputExp.exp_id = detResidExp.exp_id
+   WHERE
+        detRun.state = 'run'
+   GROUP BY
+        detRun.det_id,
+        detRun.iteration
+    HAVING
+        COUNT(detResidExp.exp_id) = COUNT(detInputExp.exp_id)
+    ) AS residdetrun
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_input.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_input.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_input.sql	(revision 22322)
@@ -0,0 +1,4 @@
+SELECT DISTINCT *
+ FROM detInputExp
+ JOIN rawExp
+ USING(exp_id)
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_normalizedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_normalizedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_normalizedexp.sql	(revision 22322)
@@ -0,0 +1,8 @@
+SELECT
+   detNormalizedExp.*
+ FROM detNormalizedExp
+ JOIN detRun
+   USING(det_id, iteration)
+ WHERE
+   detRun.state = 'run'
+   AND detRun.mode = 'master'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_normalizedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_normalizedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_normalizedimfile.sql	(revision 22322)
@@ -0,0 +1,8 @@
+SELECT
+ detNormalizedImfile.*
+ FROM detNormalizedImfile
+ JOIN detRun
+   USING(det_id, iteration)
+ WHERE
+   detRun.state = 'run'
+   AND detRun.mode = 'master'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_normalizedstat.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_normalizedstat.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_normalizedstat.sql	(revision 22322)
@@ -0,0 +1,12 @@
+SELECT
+    *
+FROM
+    (SELECT
+        detNormalizedStatImfile.*
+    FROM detNormalizedStatImfile
+    JOIN detRun
+    USING(det_id, iteration)
+    WHERE
+        detRun.state = 'run'
+        AND detRun.mode = 'master'
+    ) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pending.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pending.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pending.sql	(revision 22322)
@@ -0,0 +1,10 @@
+-- dettool -pending : not sure this is still used (EAM : 2008.07.10)
+
+SELECT
+   rawExp.*
+ FROM rawExp
+ LEFT JOIN detInputExp
+   ON rawExp.exp_id = detInputExp.exp_id
+ WHERE
+    detInputExp.exp_id IS NULL
+    AND rawExp.object != 'object'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_normalizedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_normalizedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_normalizedexp.sql	(revision 22322)
@@ -0,0 +1,9 @@
+SELECT
+    detNormalizedExp.*,
+    detRunSummary.data_state
+FROM detRunSummary
+JOIN detNormalizedExp
+    USING(det_id,iteration)
+WHERE
+    detRunSummary.data_state = 'goto_cleaned'
+AND detNormalizedExp.data_state = 'full'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_normalizedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_normalizedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_normalizedimfile.sql	(revision 22322)
@@ -0,0 +1,9 @@
+SELECT
+    detNormalizedImfile.*,
+    detRunSummary.data_state
+FROM detRunSummary
+JOIN detNormalizedImfile
+    USING(det_id,iteration)
+WHERE
+    detRunSummary.data_state = 'goto_cleaned'
+AND detNormalizedImfile.data_state = 'full'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_normalizedstat.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_normalizedstat.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_normalizedstat.sql	(revision 22322)
@@ -0,0 +1,9 @@
+SELECT
+    detNormalizedStatImfile.*,
+    detRunSummary.data_state
+FROM detRunSummary
+JOIN detNormalizedStatImfile
+    USING(det_id,iteration)
+WHERE
+    detRunSummary.data_state = 'goto_cleaned'
+AND detNormalizedStatImfile.data_state = 'full'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_processedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_processedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_processedexp.sql	(revision 22322)
@@ -0,0 +1,10 @@
+-- need to restrict to a single detRunSummary (require all to say 'cleaned'?)
+SELECT
+    detProcessedExp.*,
+    detRunSummary.data_state
+FROM detRunSummary
+JOIN detProcessedExp
+    USING(det_id)
+WHERE
+    detRunSummary.data_state = 'goto_cleaned'
+AND detProcessedExp.data_state = 'full'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_processedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_processedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_processedimfile.sql	(revision 22322)
@@ -0,0 +1,10 @@
+-- need to restrict to a single detRunSummary (require all to say 'cleaned'?)
+SELECT
+    detProcessedImfile.*,
+    detRunSummary.data_state
+FROM detRunSummary
+JOIN detProcessedImfile
+    USING(det_id)
+WHERE
+    detRunSummary.data_state = 'goto_cleaned'
+AND detProcessedImfile.data_state = 'full'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_residexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_residexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_residexp.sql	(revision 22322)
@@ -0,0 +1,9 @@
+SELECT
+    detResidExp.*,
+    detRunSummary.data_state
+FROM detRunSummary
+JOIN detResidExp
+    USING(det_id,iteration)
+WHERE
+    detRunSummary.data_state = 'goto_cleaned'
+AND detResidExp.data_state = 'full'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_residimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_residimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_residimfile.sql	(revision 22322)
@@ -0,0 +1,9 @@
+SELECT
+    detResidImfile.*,
+    detRunSummary.data_state
+FROM detRunSummary
+JOIN detResidImfile
+    USING(det_id,iteration)
+WHERE
+    detRunSummary.data_state = 'goto_cleaned'
+AND detResidImfile.data_state = 'full'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_stacked.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_stacked.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_pendingcleanup_stacked.sql	(revision 22322)
@@ -0,0 +1,9 @@
+SELECT
+    detStackedImfile.*,
+    detRunSummary.data_state
+FROM detRunSummary
+JOIN detStackedImfile
+    USING(det_id,iteration)
+WHERE
+    detRunSummary.data_state = 'goto_cleaned'
+AND detStackedImfile.data_state = 'full'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_processedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_processedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_processedimfile.sql	(revision 22322)
@@ -0,0 +1,13 @@
+SELECT DISTINCT
+  detRun.det_type,
+  rawExp.exp_time,
+  detProcessedImfile.*
+FROM detProcessedImfile
+JOIN detRun
+  USING(det_id)
+JOIN detInputExp
+  ON detRun.det_id = detInputExp.det_id
+  AND detRun.iteration = detInputExp.iteration
+  AND detProcessedImfile.exp_id = detInputExp.exp_id
+JOIN rawExp
+  ON rawExp.exp_id = detProcessedImfile.exp_id
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_raw.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_raw.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_raw.sql	(revision 22322)
@@ -0,0 +1,12 @@
+SELECT
+    detInputExp.det_id,
+    detRun.det_type,
+    rawImfile.*
+FROM detRun
+JOIN detInputExp
+    USING(det_id) 
+JOIN rawImfile
+    ON detInputExp.exp_id = rawImfile.exp_id
+WHERE
+    detRun.state = 'run'
+    AND detRun.mode = 'master'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_residexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_residexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_residexp.sql	(revision 22322)
@@ -0,0 +1,11 @@
+SELECT
+   detRun.mode,
+   detResidExp.*,
+   detInputExp.include
+ FROM detRun
+ JOIN detInputExp
+   USING(det_id, iteration)
+ JOIN detResidExp
+   USING(det_id, iteration, exp_id)
+ WHERE
+   detRun.state = 'run'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_residimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_residimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_residimfile.sql	(revision 22322)
@@ -0,0 +1,10 @@
+SELECT
+   detRun.det_type,
+   detRun.mode,
+   detResidImfile.*,
+   rawExp.exp_time
+ FROM detResidImfile
+ JOIN detRun
+   USING(det_id, iteration)
+ JOIN rawExp
+   USING(exp_id)
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertdetrunsummary.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertdetrunsummary.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertdetrunsummary.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM detRunSummary
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertnormalizedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertnormalizedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertnormalizedexp.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM detNormalizedExp
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertnormalizedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertnormalizedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertnormalizedimfile.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM detNormalizedImfile
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertnormalizedstat.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertnormalizedstat.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertnormalizedstat.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM detNormalizedStatImfile
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertprocessedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertprocessedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertprocessedexp.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM detProcessedExp
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertprocessedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertprocessedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertprocessedimfile.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM detProcessedImfile
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertresidexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertresidexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertresidexp.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM detResidExp
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertresidimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertresidimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertresidimfile.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM detResidImfile
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertstacked.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertstacked.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_revertstacked.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM detStackedImfile
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_runs.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_runs.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_runs.sql	(revision 22322)
@@ -0,0 +1,2 @@
+
+SELECT * from detRun
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_stacked.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_stacked.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_stacked.sql	(revision 22322)
@@ -0,0 +1,12 @@
+-- select detStackedImfile.*
+-- by:
+-- where det_id, iteration, class_id is not in detNormalizedImfile
+
+SELECT
+   detStackedImfile.*
+ FROM detStackedImfile
+ JOIN detRun
+   USING(det_id, iteration)
+ WHERE
+   detRun.state = 'run'
+   AND detRun.mode = 'master'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_start_new_iteration.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_start_new_iteration.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_start_new_iteration.sql	(revision 22322)
@@ -0,0 +1,33 @@
+-- select detRun.iteration
+-- select detInputExp.exp_id
+-- select detResidExp.accept
+-- by:
+-- find the current iteration bassed on det_id
+-- find all exp_ids in the current det_id/iteration from detInputExp
+-- find all exp_ids in the current det_id/iteration from detResidExp
+-- compare the counts of exp_ids
+
+SELECT * FROM
+    (SELECT DISTINCT
+        detRun.det_id AS det_id,
+        detRun.iteration,
+        detInputExp.exp_id,
+        detResidExp.accept
+    FROM detRun
+    JOIN detInputExp
+        ON detRun.det_id = detInputExp.det_id
+        AND detRun.iteration = detInputExp.iteration
+    JOIN detResidExp
+        ON detRun.det_id = detResidExp.det_id
+        AND detRun.iteration = detResidExp.iteration
+        AND detInputExp.exp_id = detResidExp.exp_id
+    WHERE
+        detRun.state = 'run'
+        AND detRun.mode = 'master'
+    GROUP BY
+        detRun.det_id,
+        detRun.iteration,
+        detInputExp.exp_id
+    HAVING
+        COUNT(detResidExp.exp_id) = COUNT(detInputExp.exp_id)
+    ) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_stop_completed_correct_runs.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_stop_completed_correct_runs.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_stop_completed_correct_runs.sql	(revision 22322)
@@ -0,0 +1,29 @@
+UPDATE detRun
+SET state = 'stop'
+WHERE det_id = (SELECT det_id FROM
+    (SELECT
+        det1.det_id,
+        imfile.class_id,
+        detRegisteredImfile.class_id as reg_class_id
+    FROM detRun AS det1
+    INNER JOIN detRun as det2
+        ON det1.ref_det_run = det2.det_id 
+	-- do we need to restrict by iteration?  not clear this logic below is right or tested
+    JOIN detNormalizedImfile as imfile
+        ON det2.det_id = imfile.det_id
+        AND det2.iteration = imfile.iteration
+    LEFT JOIN detRegisteredImfile
+        ON det1.det_id = detRegisteredImfile.det_id
+        AND det1.iteration = detRegisteredImfile.iteration
+        AND imfile.class_id = detRegisteredImfile.class_id
+    WHERE 
+        det1.state = 'run'
+        AND det1.mode = 'correction'
+        AND det2.state = 'stop'
+    GROUP BY
+        det1.det_id
+    HAVING
+        COUNT(imfile.class_id) = COUNT(reg_class_id)
+        AND SUM(imfile.fault) = 0
+    ) as Foo
+)
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tocorrectexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tocorrectexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tocorrectexp.sql	(revision 22322)
@@ -0,0 +1,4 @@
+SELECT * FROM detRun
+WHERE
+    detRun.mode = 'correction'
+    AND detRun.state = 'run'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tocorrectimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tocorrectimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tocorrectimfile.sql	(revision 22322)
@@ -0,0 +1,32 @@
+-- select imfiles from detRun 1 (the reference detRun) that match the desired correction in detRun 2
+SELECT
+    det1.*,
+    imfile.class_id,
+    imfile.uri,
+    imfile.bg,
+    imfile.bg_stdev,
+    imfile.bg_mean_stdev,
+    imfile.user_1,
+    imfile.user_2,
+    imfile.user_3,
+    imfile.user_4,
+    imfile.user_5,
+    imfile.path_base,
+    imfile.fault
+FROM detRun AS det1
+JOIN detRun as det2
+    ON det1.ref_det_id = det2.det_id 
+JOIN detNormalizedImfile as imfile
+    ON det1.ref_det_id = imfile.det_id
+    AND det1.ref_iter = imfile.iteration
+LEFT JOIN detRegisteredImfile
+    ON det1.det_id = detRegisteredImfile.det_id
+    AND det1.iteration = detRegisteredImfile.iteration
+    AND imfile.class_id = detRegisteredImfile.class_id
+WHERE 
+    det1.state = 'run'
+    AND det1.mode = 'correction'
+    AND det2.state = 'stop'
+    AND detRegisteredImfile.det_id IS NULL
+    AND detRegisteredImfile.iteration IS NULL
+    AND detRegisteredImfile.class_id IS NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_todetrunsummary.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_todetrunsummary.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_todetrunsummary.sql	(revision 22322)
@@ -0,0 +1,47 @@
+-- select detRun.det_id
+-- select detRun.iteration
+-- select detRun.det_type
+-- by:
+-- find the current iteration bassed on det_id
+-- find all exp_ids in the current det_id/iteration from detInputExp
+-- find all exp_ids in the current det_id/iteration from detResidExp
+-- compare the counts of exp_ids
+
+SELECT DISTINCT
+    det_id,
+    iteration,
+    det_type,
+    mode,
+    workdir,
+    camera
+FROM
+    (SELECT DISTINCT
+        detRun.det_id,
+        detRun.iteration,
+        detRun.det_type,
+        detRun.mode,
+        detRun.workdir,
+        detInputExp.exp_id,
+        rawExp.camera
+    FROM detRun
+    JOIN detInputExp
+        USING(det_id, iteration)
+    JOIN rawExp
+        ON detInputExp.exp_id = rawExp.exp_id
+    LEFT JOIN detResidExp
+        ON detRun.det_id = detResidExp.det_id
+        AND detRun.iteration = detResidExp.iteration
+        AND detInputExp.exp_id = detResidExp.exp_id
+    LEFT JOIN detRunSummary
+        ON detRun.det_id = detRunSummary.det_id
+        AND detRun.iteration = detRunSummary.iteration
+    WHERE
+        detRun.state = 'run'
+        AND detRunSummary.det_id IS NULL
+        AND detRunSummary.iteration IS NULL
+    GROUP BY
+        detRun.det_id,
+        detRun.iteration
+    HAVING
+        COUNT(detResidExp.exp_id) = COUNT(detInputExp.exp_id)
+    ) AS Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tonormalize.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tonormalize.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tonormalize.sql	(revision 22322)
@@ -0,0 +1,27 @@
+SELECT DISTINCT
+   detRun.det_type,
+   detRun.workdir,
+   rawExp.camera,
+   detStackedImfile.uri,
+   detNormalizedStatImfile.*
+ FROM detRun
+ JOIN detStackedImfile
+   USING(det_id, iteration)
+ JOIN detInputExp
+   USING(det_id, iteration)
+ JOIN rawExp
+   ON detInputExp.exp_id = rawExp.exp_id
+ JOIN detNormalizedStatImfile
+   ON detStackedImfile.det_id = detNormalizedStatImfile.det_id
+   AND detStackedImfile.iteration = detNormalizedStatImfile.iteration
+   AND detStackedImfile.class_id = detNormalizedStatImfile.class_id
+ LEFT JOIN detNormalizedImfile
+   ON detNormalizedStatImfile.det_id = detNormalizedImfile.det_id
+   AND detNormalizedStatImfile.iteration = detNormalizedImfile.iteration
+   AND detNormalizedStatImfile.class_id = detNormalizedImfile.class_id
+ WHERE
+   detNormalizedImfile.det_id IS NULL
+   AND detNormalizedImfile.iteration IS NULL
+   AND detNormalizedImfile.class_id IS NULL
+   AND detNormalizedStatImfile.fault = 0
+   AND detRun.state = 'run'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tonormalizedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tonormalizedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tonormalizedexp.sql	(revision 22322)
@@ -0,0 +1,48 @@
+-- this is used by both 'tonormalizedexp' and 'addnormalizedexp'
+
+SELECT
+    *
+FROM (SELECT 
+      *
+    FROM (
+        SELECT DISTINCT
+            detRun.det_id,
+            detRun.iteration,
+            detRun.det_type,
+            detRun.workdir,
+            rawExp.camera,
+            rawExp.telescope,
+            rawExp.exp_type,
+            detNormalizedImfile.class_id as norm_class_id,
+            detNormalizedImfile.fault,
+            detStackedImfile.class_id as stack_class_id
+        FROM detRun
+        JOIN detInputExp
+            ON detRun.det_id = detInputExp.det_id
+            AND detRun.iteration = detInputExp.iteration
+        JOIN rawExp
+            ON detInputExp.exp_id = rawExp.exp_id
+        JOIN detStackedImfile
+            ON detStackedImfile.det_id = detRun.det_id
+            AND detStackedImfile.iteration = detRun.iteration
+        LEFT JOIN detNormalizedImfile
+            ON detStackedImfile.det_id = detNormalizedImfile.det_id
+            AND detStackedImfile.iteration = detNormalizedImfile.iteration
+            AND detStackedImfile.class_id = detNormalizedImfile.class_id
+        LEFT JOIN detNormalizedExp
+            ON detInputExp.det_id = detNormalizedExp.det_id
+            AND detInputExp.iteration = detNormalizedExp.iteration
+        WHERE
+            detRun.state = 'run' AND
+            detRun.mode = 'master' AND
+            detNormalizedExp.det_id IS NULL AND
+            detNormalizedExp.iteration IS NULL AND
+            detInputExp.include = 1
+    ) as Foo
+    GROUP BY
+      det_id,
+      iteration
+    HAVING
+      COUNT(norm_class_id) = COUNT(stack_class_id)
+      AND SUM(fault) = 0
+) as Bar
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tonormalizedstat.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tonormalizedstat.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tonormalizedstat.sql	(revision 22322)
@@ -0,0 +1,58 @@
+-- select detRun.det_id (det_id)
+-- select detRun.iteration
+-- by:
+-- find the current iteration bassed on det_id
+-- find all exp_ids in the current det_id/iteration from detInputExp
+-- sort to detInputExp.imfiles to find the largest value per det_id/iter
+-- compare imfiles to the number of detStackedImfiles by class_id
+-- and:
+-- ???
+-- det_id is not in detStackedImfile;
+-- iteration is not in detStackedImfile;
+
+SELECT
+    det_id,
+    det_type,
+    iteration,
+    camera,
+    workdir,
+    class_id
+FROM
+    (SELECT DISTINCT
+        detRun.det_id,
+        detRun.det_type,
+        detRun.iteration,
+        detRun.workdir,
+        rawExp.camera,
+        detStackedImfile.class_id,
+        rawImfile.class_id as rawimfile_class_id
+    FROM detRun
+    JOIN detInputExp
+        ON detRun.det_id = detInputExp.det_id
+        AND detRun.iteration = detInputExp.iteration
+    JOIN rawExp
+        ON detInputExp.exp_id = rawExp.exp_id
+    JOIN rawImfile
+        ON rawExp.exp_id = rawImfile.exp_id
+    LEFT JOIN detStackedImfile
+        ON detInputExp.det_id = detStackedImfile.det_id
+        AND detInputExp.iteration = detStackedImfile.iteration
+        AND rawImfile.class_id = detStackedImfile.class_id
+    LEFT JOIN detNormalizedStatImfile
+        ON detStackedImfile.det_id = detNormalizedStatImfile.det_id
+        AND detStackedImfile.iteration = detNormalizedStatImfile.iteration
+        AND detStackedImfile.class_id = detNormalizedStatImfile.class_id
+    WHERE
+        detRun.state = 'run'
+        AND detRun.mode = 'master'
+        AND detNormalizedStatImfile.det_id IS NULL
+        AND detNormalizedStatImfile.iteration IS NULL
+        AND detNormalizedStatImfile.class_id IS NULL
+    GROUP BY
+--        rawExp.exp_id,
+        detRun.iteration,
+        detRun.det_id
+    HAVING
+        COUNT(rawImfile.class_id) = COUNT(detStackedImfile.class_id)
+        AND SUM(detStackedImfile.fault) = 0
+    ) as tonormalizedstat
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toprocessedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toprocessedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toprocessedexp.sql	(revision 22322)
@@ -0,0 +1,61 @@
+-- select detProcessedImfile.det_id
+-- select detRun.iteration
+-- select detRun.det_type
+-- select detProcessedImfile.exp_id
+-- by:
+-- find the current iteration bassed on det_id
+-- find all exp_ids in the current det_id/iteration from detInputExp
+-- find all rawImfiles in the current exp_ids
+-- compare to detProcessedImfiles by det_id/exp_id
+-- found how many imfile there are in each class_id
+-- and:
+-- det_id is not in detProcessedExp;
+-- iteration is not in detProcessedExp;
+
+SELECT DISTINCT
+    det_id,
+    iteration,
+    det_type,
+    exp_id,
+    camera,
+    workdir,
+    exp_tag
+FROM
+    (SELECT DISTINCT
+        detRun.det_id,
+        detRun.iteration,
+        detRun.det_type,
+        detRun.workdir,
+        detProcessedImfile.exp_id,
+        detProcessedImfile.class_id,
+        rawImfile.class_id as rawimfile_class_id,
+        rawExp.camera,
+        rawExp.exp_tag
+    FROM detRun
+    JOIN detInputExp
+        USING(det_id, iteration)
+    JOIN rawExp
+        ON detInputExp.exp_id = rawExp.exp_id
+    JOIN rawImfile
+        ON rawExp.exp_id = rawImfile.exp_id
+    LEFT JOIN detProcessedImfile
+        ON detRun.det_id = detProcessedImfile.det_id
+        AND detInputExp.exp_id = detProcessedImfile.exp_id
+        AND rawImfile.class_id = detProcessedImfile.class_id
+    LEFT JOIN detProcessedExp
+        ON detProcessedImfile.det_id = detProcessedExp.det_id
+        AND detProcessedImfile.exp_id = detProcessedExp.exp_id
+    WHERE
+        detRun.state = 'run'
+        AND (detRun.mode = 'master' or detRun.mode = 'verify')
+        AND detProcessedExp.det_id IS NULL
+        AND detProcessedExp.exp_id IS NULL
+        AND detInputExp.include = 1
+    GROUP BY
+        rawExp.exp_id,
+        detRun.det_id
+    HAVING
+        COUNT(detProcessedImfile.class_id) = COUNT(rawImfile.class_id)
+        AND SUM(detProcessedImfile.fault) = 0
+    ) AS detProcessedExp
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toprocessedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toprocessedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toprocessedimfile.sql	(revision 22322)
@@ -0,0 +1,24 @@
+SELECT
+    detRun.det_id,
+    detRun.det_type,
+    detRun.workdir,
+    detRun.reduction,
+    rawImfile.*,
+    rawExp.exp_tag
+FROM detRun
+JOIN detInputExp
+    USING(det_id, iteration)
+JOIN rawExp
+    ON detInputExp.exp_id = rawExp.exp_id
+JOIN rawImfile
+    ON detInputExp.exp_id = rawImfile.exp_id
+LEFT JOIN detProcessedImfile
+    ON detInputExp.det_id = detProcessedImfile.det_id
+    AND rawImfile.exp_id = detProcessedImfile.exp_id
+    AND rawImfile.class_id = detProcessedImfile.class_id
+WHERE
+    detRun.state = 'run'
+    AND (detRun.mode = 'master' or detRun.mode = 'verify')
+    AND detProcessedImfile.det_id IS NULL
+    AND detProcessedImfile.exp_id IS NULL
+    AND detProcessedImfile.class_id IS NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toresidexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toresidexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toresidexp.sql	(revision 22322)
@@ -0,0 +1,70 @@
+-- this is used by both 'toresidexp' and 'addresidexp'
+
+-- which returns a list of exposures for which all component class ids have had
+-- residuals created.  This list includes the detrend id, iteration, exposure
+-- id, detrend type, and whether the exposure was included in the stack for
+-- this iteration.
+
+-- select detRun.det_id
+-- select detRun.iteration
+-- select detRun.det_type
+-- select detInputExp.exp_id
+-- select detInputExp.include
+-- by:
+-- find the current iteration bassed on det_id
+-- find all exp_ids in the current det_id/iteration from detInputExp
+-- compare to detInputExp.imfiles to derResidImfile by class_id
+-- and:
+-- detResidImfile.{det_id, iteration, exp_id} is not in detResidExp
+
+SELECT DISTINCT
+    det_id,
+    iteration,
+    det_type,
+    mode,
+    exp_id,
+    include,
+    camera,
+    workdir,
+    exp_tag
+FROM
+    (SELECT DISTINCT
+        detRun.det_id AS det_id,
+        detRun.iteration,
+        detRun.det_type,
+        detRun.mode,
+        detRun.workdir,
+        detInputExp.exp_id,
+        detInputExp.include,
+        rawExp.camera,
+        rawExp.exp_tag,
+        detResidImfile.class_id
+    FROM detRun
+    JOIN detInputExp
+        USING(det_id, iteration)
+    JOIN rawExp
+        ON detInputExp.exp_id = rawExp.exp_id
+    JOIN rawImfile
+        on rawExp.exp_id = rawImfile.exp_id
+    LEFT JOIN detResidImfile
+        ON detRun.det_id = detResidImfile.det_id
+        AND detRun.iteration = detResidImfile.iteration
+        AND detInputExp.exp_id = detResidImfile.exp_id
+        AND rawImfile.class_id = detResidImfile.class_id
+    LEFT JOIN detResidExp
+        ON detResidImfile.det_id = detResidExp.det_id
+        AND detResidImfile.iteration = detResidExp.iteration
+        AND detResidImfile.exp_id = detResidExp.exp_id
+    WHERE
+        detRun.state = 'run'
+        AND detResidExp.det_id IS NULL
+        AND detResidExp.iteration IS NULL
+        AND detResidExp.exp_id IS NULL
+    GROUP BY
+        detInputExp.exp_id,
+        detRun.iteration,
+        detRun.det_id
+    HAVING
+        COUNT(rawImfile.class_id) = COUNT(detResidImfile.class_id)
+        AND SUM(detResidImfile.fault = 0)
+    ) AS toresidexp
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toresidimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toresidimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_toresidimfile.sql	(revision 22322)
@@ -0,0 +1,94 @@
+-- which returns a list of processed imfiles (with corresponding detrend id,
+-- iteration, class id, uri, type) for which the appropriate stacked imfile has
+-- been normalised, and which have not been masked out by detResidImfileAnalysis
+
+SELECT DISTINCT
+    detRun.det_id,
+    detRun.iteration,
+    detRun.det_type,
+    detRun.mode,
+    detRun.workdir,
+    detRun.reduction,
+    detProcessedImfile.exp_id,
+    detProcessedImfile.class_id,
+    detProcessedImfile.uri,
+    detNormalizedImfile.uri AS det_uri,
+    detRun.det_id AS ref_det_id,
+    detRun.iteration AS ref_iter,
+    rawExp.camera,
+    rawExp.exp_tag
+FROM detRun
+JOIN detInputExp
+    USING(det_id, iteration)
+JOIN rawExp
+    ON detInputExp.exp_id = rawExp.exp_id
+JOIN detProcessedImfile
+    ON detRun.det_id = detProcessedImfile.det_id
+    AND detInputExp.exp_id = detProcessedImfile.exp_id
+JOIN detNormalizedImfile
+    ON detRun.det_id = detNormalizedImfile.det_id
+    AND detRun.iteration = detNormalizedImfile.iteration
+    AND detProcessedImfile.class_id = detNormalizedImfile.class_id
+JOIN detNormalizedExp
+    ON detRun.det_id = detNormalizedExp.det_id
+    AND detRun.iteration = detNormalizedExp.iteration
+LEFT JOIN detResidImfile
+    ON detRun.det_id = detResidImfile.det_id
+    AND detRun.iteration = detResidImfile.iteration
+    AND detProcessedImfile.exp_id = detResidImfile.exp_id
+    AND detProcessedImfile.class_id = detResidImfile.class_id
+WHERE
+    detRun.state = 'run'
+    AND detRun.mode = 'master'
+    AND detNormalizedImfile.fault = 0
+    AND detNormalizedExp.fault = 0
+    AND detResidImfile.det_id IS NULL
+    AND detResidImfile.iteration IS NULL
+    AND detResidImfile.exp_id IS NULL
+    AND detResidImfile.class_id IS NULL
+UNION
+SELECT
+    detRun.det_id,
+    detRun.iteration,
+    detRun.det_type,
+    detRun.mode,
+    detRun.workdir,
+    detRun.reduction,
+    detProcessedImfile.exp_id,
+    detProcessedImfile.class_id,
+    detProcessedImfile.uri,
+    detNormalizedImfile.uri AS det_uri,
+    detRun.ref_det_id,
+    detRun.ref_iter,
+    rawExp.camera,
+    rawExp.exp_tag
+FROM detRun
+JOIN detRun AS detRunRef
+  ON detRunRef.det_id = detRun.ref_det_id
+JOIN detInputExp
+  ON detInputExp.det_id    = detRun.det_id
+ AND detInputExp.iteration = detRun.iteration
+JOIN rawExp
+  ON rawExp.exp_id = detInputExp.exp_id
+JOIN detProcessedImfile
+  ON detProcessedImfile.det_id    = detRun.det_id
+ AND detProcessedImfile.exp_id    = detInputExp.exp_id
+JOIN detNormalizedImfile
+  ON detNormalizedImfile.det_id    = detRun.ref_det_id
+ AND detNormalizedImfile.iteration = detRun.ref_iter
+ AND detNormalizedImfile.class_id  = detProcessedImfile.class_id
+LEFT JOIN detResidImfile
+  ON detResidImfile.det_id    = detRun.det_id      
+ AND detResidImfile.iteration = detRun.iteration   
+ AND detResidImfile.exp_id    = detProcessedImfile.exp_id   
+ AND detResidImfile.class_id  = detProcessedImfile.class_id 
+WHERE
+ detRun.state = 'run'
+ AND detRun.mode = 'verify'
+ AND detRunRef.state = 'stop'
+ AND detNormalizedImfile.fault = 0
+ AND detResidImfile.det_id IS NULL
+ AND detResidImfile.iteration IS NULL
+ AND detResidImfile.exp_id IS NULL
+ AND detResidImfile.class_id IS NULL
+ORDER BY exp_id
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tostacked.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tostacked.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/dettool_tostacked.sql	(revision 22322)
@@ -0,0 +1,52 @@
+-- select detRun.iteration
+-- select detProcessedImfile.det_id
+-- select detProcessedImfile.class_id
+-- by:
+-- find the current iteration bassed on det_id
+-- find all exp_ids in the current det_id/iteration from detInputExp
+-- find all rawImfiles in the current exp_ids
+-- compare to detProcessedImfiles by det_id/exp_id
+-- found how many imfile there are in each class_id
+-- and:
+-- det_id is not in detStackedImfile;
+-- iteration is not in detStackedImfile;
+-- class_id is not in detStackedImfile;
+
+SELECT
+    detProcessedImfile.det_id,
+    detRun.iteration,
+    detRun.det_type,
+    detRun.workdir,
+    detRun.reduction,
+    detProcessedImfile.class_id,
+    rawExp.camera,
+    rawExp.exp_tag
+FROM detRun
+JOIN detInputExp
+    ON detRun.det_id = detInputExp.det_id
+    AND detRun.iteration = detInputExp.iteration
+JOIN rawExp
+    ON detInputExp.exp_id = rawExp.exp_id
+JOIN rawImfile
+    ON detInputExp.exp_id = rawImfile.exp_id
+LEFT JOIN detProcessedImfile
+    ON detInputExp.det_id = detProcessedImfile.det_id
+    AND detInputExp.exp_id = detProcessedImfile.exp_id
+    AND rawImfile.class_id = detProcessedImfile.class_id
+LEFT JOIN detStackedImfile
+    ON detInputExp.det_id = detStackedImfile.det_id
+    AND detInputExp.iteration = detStackedImfile.iteration
+    AND rawImfile.class_id = detStackedImfile.class_id
+WHERE
+    detRun.state = 'run'
+    AND detRun.mode = 'master'
+    AND detStackedImfile.det_id IS NULL
+    AND detStackedImfile.iteration IS NULL
+    AND detStackedImfile.class_id IS NULL
+    AND detInputExp.include = 1
+GROUP BY
+    rawImfile.class_id,
+    detRun.det_id
+HAVING
+    COUNT(detProcessedImfile.class_id) = COUNT(rawImfile.class_id)
+    AND SUM(detProcessedImfile.fault) = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_definebyquery.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_definebyquery.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_definebyquery.sql	(revision 22322)
@@ -0,0 +1,64 @@
+-- Get list of warps that can be diffed, with any associated diff,
+-- and the best stack to use as a template
+-- Warps without an existing diff can be identified by a NULL diff_id
+SELECT
+    warpsToDiff.warp_id,
+    warpsToDiff.skycell_id,
+    warpsToDiff.tess_id,
+    warpsToDiff.filter,
+    stacksForDiff.stack_label,
+    warpsToDiff.warp_label,
+    warpsToDiff.good_frac,
+    warpsToDiff.diff_id,
+    warpsToDiff.kind,
+    warpsToDiff.label,
+    current_stack_id,
+    best_stack_id
+FROM (
+    -- Get list of warps that can be diffed, with any associated diff
+    SELECT
+        warpSkyfile.warp_id,
+        warpSkyfile.skycell_id,
+        warpSkyfile.tess_id,
+        warpSkyfile.good_frac,
+        warpRun.label,
+        filter,
+        warpRun.label as warp_label,
+        diffInputs.diff_id,
+        diffInputs.kind,
+        diffTemplates.stack_id AS current_stack_id
+    FROM warpSkyfile
+    JOIN warpRun USING(warp_id)
+    JOIN fakeRun USING(fake_id)
+    JOIN camRun USING(cam_id)
+    JOIN chipRun USING(chip_id)
+    JOIN rawExp USING(exp_id)
+    -- Check if it has an associated diff
+    LEFT JOIN diffInputSkyfile AS diffInputs
+        ON diffInputs.warp_id = warpSkyfile.warp_id
+        AND diffInputs.skycell_id = warpSkyfile.skycell_id
+        AND diffInputs.template = 0 -- only join input files
+    -- Get the stack_id currently used as a template, if any
+    LEFT JOIN diffInputSkyfile AS diffTemplates
+        ON diffTemplates.diff_id = diffInputs.diff_id
+        AND diffTemplates.template != 0 -- only join template files
+    WHERE
+        warpSkyfile.fault = 0
+        AND warpSkyfile.ignored = 0
+    ) AS warpsToDiff
+-- Get best stack as a function of skycell_id, filter
+JOIN (
+    SELECT
+        MAX(stack_id) AS best_stack_id, -- most recent stack, by virtue of auto-increment
+        skycell_id,
+        tess_id,
+        filter,
+	label as stack_label
+    FROM stackRun
+    JOIN stackSumSkyfile USING(stack_id)
+    WHERE stackRun.state = 'full'
+        AND stackSumSkyfile.fault = 0
+    GROUP BY
+        skycell_id,
+        filter
+    ) AS stacksForDiff USING(skycell_id, filter)
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_donecleanup.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_donecleanup.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_donecleanup.sql	(revision 22322)
@@ -0,0 +1,5 @@
+SELECT
+    diffRun.*
+FROM diffRun
+WHERE
+    diffRun.state = 'cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_inputskyfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_inputskyfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_inputskyfile.sql	(revision 22322)
@@ -0,0 +1,75 @@
+SELECT * FROM
+    (SELECT 
+        diffRun.diff_id,
+        diffRun.skycell_id,
+        diffRun.tess_id,
+        0 as stack_id,
+        warpSkyfile.warp_id,
+        warpSkyfile.uri,
+        warpSkyfile.path_base,
+        diffInputSkyfile.template,
+        rawExp.camera
+    FROM diffRun
+    JOIN diffInputSkyfile
+        USING(diff_id)
+    JOIN warpSkyfile
+        ON  diffInputSkyfile.warp_id    = warpSkyfile.warp_id
+        AND diffInputSkyfile.skycell_id = warpSkyfile.skycell_id
+        AND diffInputSkyfile.tess_id    = warpSkyfile.tess_id
+    JOIN warpRun
+        ON diffInputSkyfile.warp_id = warpRun.warp_id
+    JOIN fakeRun
+        USING(fake_id)
+    JOIN camRun
+        USING(cam_id)
+    JOIN chipRun
+        USING(chip_id)
+    JOIN chipProcessedImfile
+        USING(chip_id)
+    JOIN rawExp
+        ON chipRun.exp_id = rawExp.exp_id
+    WHERE
+        diffRun.state = 'new'
+        AND warpRun.state = 'full'
+        AND fakeRun.state = 'full'
+        AND camRun.state = 'full'
+        AND chipRun.state = 'full'
+        -- where hook %s
+    UNION
+    SELECT 
+        diffRun.diff_id,
+        diffRun.skycell_id,
+        diffRun.tess_id,
+        stackSumSkyfile.stack_id,
+        0 as warp_id,
+        stackSumSkyfile.uri,
+        stackSumSkyfile.path_base,
+        diffInputSkyfile.template,
+        rawExp.camera
+    FROM diffRun
+    JOIN diffInputSkyfile
+        USING(diff_id)
+    JOIN stackSumSkyfile
+        ON  diffInputSkyfile.stack_id = stackSumSkyfile.stack_id
+    JOIN stackInputSkyfile
+        ON diffInputSkyfile.stack_id = stackInputSkyfile.stack_id
+    JOIN warpRun
+        ON stackInputSkyfile.warp_id = warpRun.warp_id
+    JOIN fakeRun
+        USING(fake_id)
+    JOIN camRun
+        USING(cam_id)
+    JOIN chipRun
+        USING(chip_id)
+    JOIN chipProcessedImfile
+        USING(chip_id)
+    JOIN rawExp
+        ON chipRun.exp_id = rawExp.exp_id
+    WHERE
+        diffRun.state = 'new'
+        AND warpRun.state = 'full'
+        AND fakeRun.state = 'full'
+        AND camRun.state = 'full'
+        AND chipRun.state = 'full'
+        -- where hook %s
+    ) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_pendingcleanuprun.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_pendingcleanuprun.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_pendingcleanuprun.sql	(revision 22322)
@@ -0,0 +1,25 @@
+-- does this result in too many entries (one for each diffInputSkyfile?)
+-- all of this is just to get the camera used for the diff run
+SELECT
+    diffRun.diff_id,
+    rawExp.camera,
+    diffRun.state
+FROM diffRun
+JOIN diffInputSkyfile
+    USING(diff_id)
+JOIN warpSkyfile
+    ON  diffInputSkyfile.warp_id    = warpSkyfile.warp_id
+    AND diffInputSkyfile.skycell_id = warpSkyfile.skycell_id
+    AND diffInputSkyfile.tess_id    = warpSkyfile.tess_id
+JOIN warpRun
+    ON warpRun.warp_id = warpSkyfile.warp_id
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN rawExp
+    USING(exp_id)
+WHERE
+    diffRun.state = 'goto_cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_pendingcleanupskyfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_pendingcleanupskyfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_pendingcleanupskyfile.sql	(revision 22322)
@@ -0,0 +1,11 @@
+SELECT
+    diffSkyfile.*,
+    diffRun.state,
+    diffRun.workdir,
+    diffRun.dvodb,
+    diffRun.tess_id
+FROM diffRun
+JOIN diffSkyfile
+    USING(diff_id)
+WHERE
+    diffRun.state = 'goto_cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_revertdiffskyfile_delete.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_revertdiffskyfile_delete.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_revertdiffskyfile_delete.sql	(revision 22322)
@@ -0,0 +1,5 @@
+DELETE FROM diffSkyfile
+USING diffSkyfile, diffRun
+WHERE
+    diffRun.diff_id = diffSkyfile.diff_id
+    AND fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_revertdiffskyfile_update.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_revertdiffskyfile_update.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_revertdiffskyfile_update.sql	(revision 22322)
@@ -0,0 +1,5 @@
+UPDATE diffRun
+JOIN diffSkyfile USING(diff_id)
+SET diffRun.state = 'new'
+WHERE
+    diffSkyfile.fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_skyfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_skyfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_skyfile.sql	(revision 22322)
@@ -0,0 +1,24 @@
+SELECT
+    diffRun.skycell_id,
+    diffRun.tess_id,
+    diffRun.state,
+    diffSkyfile.*,
+    rawExp.exp_id,
+    rawExp.camera
+FROM diffRun
+JOIN diffSkyfile
+    USING(diff_id)
+-- WS: my new stuff begins here
+JOIN diffInputSkyfile
+    ON diffInputSkyfile.diff_id = diffRun.diff_id
+    AND diffInputSkyfile.template = 0
+JOIN warpRun
+    ON diffInputSkyfile.warp_id = warpRun.warp_id
+JOIN fakeRun
+    ON warpRun.fake_id = fakeRun.fake_id
+JOIN camRun
+    ON camRun.cam_id = fakeRun.cam_id
+JOIN chipRun
+    ON camRun.chip_id = chipRun.chip_id
+JOIN rawExp
+    ON chipRun.exp_id = rawExp.exp_id
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_todiffskyfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_todiffskyfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/difftool_todiffskyfile.sql	(revision 22322)
@@ -0,0 +1,78 @@
+-- This query is a little complicated, since it contains multiple self-joins.
+-- This is because of the options involved: both templates and inputs can be either warps or stacks.
+-- All of the templates and inputs need to exist, so we query against all of them.
+SELECT DISTINCT
+    diffRun.diff_id,
+    diffRun.workdir,
+    diffRun.skycell_id,
+    diffRun.tess_id,
+    diffRun.label,
+    diffRun.state
+FROM diffRun
+-- Get list of templates for each diffRun
+JOIN diffInputSkyfile AS diffTemplateSkyfile
+    ON diffRun.diff_id = diffTemplateSkyfile.diff_id
+    AND diffRun.skycell_id = diffTemplateSkyfile.skycell_id
+    AND diffTemplateSkyfile.template = 1
+-- Get list of inputs for each diffRun
+JOIN diffInputSkyfile
+    ON diffRun.diff_id = diffInputSkyfile.diff_id
+    AND diffRun.skycell_id = diffInputSkyfile.skycell_id
+    AND diffInputSkyfile.template = 0
+-- Get warp templates
+LEFT JOIN warpRun AS warpTemplateRun
+    ON warpTemplateRun.warp_id = diffTemplateSkyfile.warp_id
+    AND diffTemplateSkyfile.warp_id IS NOT NULL
+LEFT JOIN warpSkyfile AS warpTemplateSkyfile
+    ON warpTemplateSkyfile.warp_id = warpTemplateRun.warp_id
+    AND warpTemplateSkyfile.skycell_id = diffTemplateSkyfile.skycell_id
+-- Get warp inputs
+LEFT JOIN warpRun
+    ON warpRun.warp_id = diffInputSkyfile.warp_id
+    AND diffInputSkyfile.warp_id IS NOT NULL
+LEFT JOIN warpSkyfile
+    ON warpSkyfile.warp_id = warpRun.warp_id
+    AND warpSkyfile.skycell_id = diffInputSkyfile.skycell_id
+-- Get stack templates
+LEFT JOIN stackRun AS stackTemplateRun
+    ON stackTemplateRun.stack_id = diffTemplateSkyfile.stack_id
+LEFT JOIN stackSumSkyfile AS stackTemplateSkyfile
+    ON stackTemplateSkyfile.stack_id = stackTemplateRun.stack_id
+-- Get stack inputs
+LEFT JOIN stackRun
+    ON stackRun.stack_id = diffInputSkyfile.stack_id
+    AND diffInputSkyfile.warp_id IS NULL
+LEFT JOIN stackSumSkyfile
+    ON stackSumSkyfile.stack_id = stackRun.stack_id
+    AND diffInputSkyfile.warp_id IS NULL
+-- Get what's already been processed
+LEFT JOIN diffSkyfile
+    ON diffInputSkyfile.diff_id = diffSkyfile.diff_id
+WHERE
+-- Ready to be processed
+    ((diffRun.state = 'new'
+    AND diffSkyfile.diff_id IS NULL)
+    OR (diffRun.state = 'update'
+    AND diffSkyfile.fault = 0)
+    )
+-- Ensure input warps are available
+    AND (diffInputSkyfile.warp_id IS NULL
+    OR (warpRun.state = 'full'
+    AND warpSkyfile.fault = 0
+    AND warpSkyfile.ignored = 0))
+-- Ensure input stacks are available
+    AND (diffInputSkyfile.stack_id IS NULL
+    OR (stackRun.state = 'full'
+    AND stackSumSkyfile.fault = 0))
+-- Ensure template warps are available
+    AND (diffTemplateSkyfile.warp_id IS NULL
+    OR (warpTemplateRun.state = 'full'
+    AND warpTemplateSkyfile.fault = 0
+    AND warpTemplateSkyfile.ignored = 0))
+-- Ensure template stacks are available
+    AND (diffTemplateSkyfile.stack_id IS NULL
+    OR (stackTemplateRun.state = 'full'
+    AND stackTemplateSkyfile.fault = 0))
+
+
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_change_exp_state.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_change_exp_state.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_change_exp_state.sql	(revision 22322)
@@ -0,0 +1,15 @@
+-- change state of fakeRun from goto_cleaned to cleaned or goto_purged to purged
+-- when all of the constituant imfiles are in the right state
+-- arguments are new state (cleaned or purged) fake_id and new state again for 
+-- the fakeProcessedImfile sub query
+UPDATE fakeRun
+    SET state = '%s'
+    WHERE
+    fakeRun.fake_id = %lld
+    AND (SELECT
+        COUNT(fake_id)
+        FROM fakeProcessedImfile
+        WHERE
+            fakeProcessedImfile.fake_id = fakeRun.fake_id
+            AND data_state != '%s'
+        ) = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_change_imfile_data_state.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_change_imfile_data_state.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_change_imfile_data_state.sql	(revision 22322)
@@ -0,0 +1,14 @@
+-- handle changes in data_state. Used for the modes tocleanedimfile and topurgedimfile
+-- args are new data_state, fake_id, class_id, and current expected state for fakeRun
+UPDATE fakeProcessedImfile
+    SET 
+    data_state = '%s'
+WHERE
+    fake_id = %lld
+    AND class_id = '%s'
+    -- only update if fakeRun.state has the expected value
+    AND (
+        SELECT state from fakeRun where fakeRun.fake_id = fakeProcessedImfile.fake_id
+    ) = '%s'
+    
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_completely_processed_exp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_completely_processed_exp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_completely_processed_exp.sql	(revision 22322)
@@ -0,0 +1,40 @@
+-- the output of this query must match the format of fakeRun row
+SELECT DISTINCT
+    fake_id,
+    cam_id,
+    state,
+    workdir,
+    label,
+    reduction,
+    expgroup,
+    dvodb,
+    tess_id,
+    end_stage,
+    epoch
+FROM
+    (SELECT 
+        fakeRun.*,
+        rawImfile.class_id as rawimfile_class_id,
+        fakeProcessedImfile.class_id
+    FROM fakeRun
+    JOIN camRun
+        USING(cam_id)
+    JOIN chipRun
+        USING(chip_id)
+    JOIN rawExp
+        USING(exp_id)
+    JOIN rawImfile
+        USING(exp_id)
+    LEFT JOIN fakeProcessedImfile
+        ON fakeRun.fake_id = fakeProcessedImfile.fake_id
+        AND rawImfile.exp_id = fakeProcessedImfile.exp_id
+        AND rawImfile.class_id = fakeProcessedImfile.class_id
+    WHERE
+        fakeRun.state = 'new'
+    GROUP BY
+        fakeRun.fake_id,
+        rawExp.exp_id
+    HAVING
+        COUNT(rawImfile.class_id) = COUNT(fakeProcessedImfile.class_id)
+        AND SUM(fakeProcessedImfile.fault) = 0
+    ) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_donecleanup.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_donecleanup.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_donecleanup.sql	(revision 22322)
@@ -0,0 +1,5 @@
+SELECT
+    fakeRun.*
+FROM fakeRun
+WHERE
+    fakeRun.state = 'cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_find_camrun.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_find_camrun.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_find_camrun.sql	(revision 22322)
@@ -0,0 +1,37 @@
+SELECT
+    *
+FROM
+    (SELECT DISTINCT
+        camRun.cam_id,
+        chipRun.*,
+        rawExp.camera,
+        rawExp.telescope,
+        rawExp.dateobs,
+        rawExp.exp_tag,
+        rawExp.exp_type,
+        rawExp.filelevel,
+        rawExp.filter,
+        rawExp.airmass,
+        rawExp.ra,
+        rawExp.decl,
+        rawExp.exp_time,
+        rawExp.sat_pixel_frac,
+        rawExp.bg,
+        rawExp.bg_stdev,
+        rawExp.bg_mean_stdev,
+        rawExp.alt,
+        rawExp.az,
+        rawExp.ccd_temp,
+        rawExp.posang,
+        rawExp.object,
+        rawExp.solang,
+        rawExp.comment
+    FROM camRun
+    JOIN chipRun
+        using(chip_id)
+    JOIN rawExp
+        using(exp_id)
+    WHERE
+        camRun.state = 'full'
+        AND chipRun.state = 'full'
+) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_find_pendingexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_find_pendingexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_find_pendingexp.sql	(revision 22322)
@@ -0,0 +1,34 @@
+-- this query is used by both camtool -pendingexp & camtool -addprocessedexp it
+-- does a little more work then is necessary for -addprocessed but it seems
+-- "cleaner" to use the same query for both cases
+SELECT * FROM
+    (SELECT
+        fakeRun.*,
+        rawExp.exp_tag,
+        rawExp.exp_id,
+        rawExp.exp_name,
+        rawExp.camera,
+        rawExp.telescope,
+        rawExp.filelevel
+    FROM fakeRun
+    JOIN camRun
+        USING(cam_id)
+    JOIN chipRun
+        USING(chip_id)
+    JOIN chipProcessedImfile
+        USING(chip_id)
+    JOIN rawExp
+        ON chipRun.exp_id = rawExp.exp_id
+    LEFT JOIN fakeProcessedImfile
+        USING(fake_id)
+    LEFT JOIN fakeMask
+        ON fakeRun.label = fakeMask.label
+    WHERE
+        fakeRun.state = 'new'
+        AND camRun.state = 'full'
+        AND chipRun.state = 'full'
+        AND fakeMask.label IS NULL
+        AND fakeProcessedImfile.fake_id IS NOT NULL
+    GROUP BY
+        fakeRun.fake_id
+    ) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_pendingcleanupimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_pendingcleanupimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_pendingcleanupimfile.sql	(revision 22322)
@@ -0,0 +1,15 @@
+SELECT
+    fakeProcessedImfile.*,
+    fakeRun.state,
+    fakeRun.workdir,
+    fakeRun.label,
+    fakeRun.reduction,
+    fakeRun.expgroup,
+    fakeRun.dvodb,
+    fakeRun.tess_id,
+    fakeRun.end_stage
+FROM fakeRun
+JOIN fakeProcessedImfile
+    USING(fake_id)
+WHERE
+    fakeRun.state = 'goto_cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_pendingcleanuprun.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_pendingcleanuprun.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_pendingcleanuprun.sql	(revision 22322)
@@ -0,0 +1,13 @@
+SELECT
+    fakeRun.fake_id,
+    rawExp.camera,
+    fakeRun.state
+FROM fakeRun
+JOIN camRun
+USING (cam_id)
+JOIN chipRun
+USING (chip_id)
+JOIN rawExp 
+USING (exp_id)
+WHERE
+    fakeRun.state = 'goto_cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_pendingimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_pendingimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_pendingimfile.sql	(revision 22322)
@@ -0,0 +1,51 @@
+SELECT DISTINCT * FROM (
+    -- the subselect is so where criteria can be specified without knowing
+    -- which table the field came from
+    SELECT
+        fakeRun.*,
+        chipProcessedImfile.exp_id,
+        chipProcessedImfile.class_id,
+        chipProcessedImfile.uri,
+        chipProcessedImfile.bg,
+        chipProcessedImfile.bg_stdev,
+        chipProcessedImfile.bg_mean_stdev,
+        chipProcessedImfile.fringe_0,
+        chipProcessedImfile.fringe_1,
+        chipProcessedImfile.fringe_2,
+        chipProcessedImfile.ap_resid,
+        chipProcessedImfile.ap_resid_stdev,
+        chipProcessedImfile.sigma_ra,
+        chipProcessedImfile.sigma_dec,
+        chipProcessedImfile.n_stars,
+        chipProcessedImfile.n_extended,
+        chipProcessedImfile.n_cr,
+        chipProcessedImfile.n_astrom,
+        chipProcessedImfile.path_base as chip_path_base,
+        camProcessedExp.path_base as cam_path_base,
+        rawExp.exp_name,
+        rawExp.exp_tag,
+        rawExp.camera,
+        rawExp.telescope,
+        rawExp.filelevel
+    FROM fakeRun
+    JOIN camRun USING(cam_id)
+    JOIN camProcessedExp USING(cam_id)
+    JOIN chipRun USING(chip_id)
+    JOIN chipProcessedImfile USING(chip_id)
+    JOIN rawExp
+        ON chipProcessedImfile.exp_id = rawExp.exp_id
+    LEFT JOIN fakeProcessedImfile
+        ON fakeRun.fake_id = fakeProcessedImfile.fake_id
+        AND chipProcessedImfile.class_id = fakeProcessedImfile.class_id
+    LEFT JOIN fakeMask
+        ON fakeRun.label = fakeMask.label
+    WHERE
+        ((fakeRun.state = 'new'
+            AND fakeProcessedImfile.fake_id IS NULL
+            AND fakeProcessedImfile.class_id IS NULL
+        ) 
+        OR (fakeRun.state = 'update'
+            AND fakeProcessedImfile.data_state = 'cleaned')
+        )
+        AND fakeMask.label IS NULL
+    ) as fakePendingImfile
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_processedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_processedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_processedimfile.sql	(revision 22322)
@@ -0,0 +1,17 @@
+SELECT DISTINCT
+    fakeProcessedImfile.*,
+    rawExp.exp_tag,
+    rawExp.exp_name,
+    rawExp.camera,
+    rawExp.telescope,
+    rawExp.filelevel
+FROM fakeRun
+JOIN fakeProcessedImfile
+    USING(fake_id)
+JOIN rawExp
+    ON fakeProcessedImfile.exp_id = rawExp.exp_id
+WHERE
+-- bogus test; just here so there there is a 'WHERE' stmt to append
+-- conditionals too
+    fakeProcessedImfile.exp_id is NOT NULL
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_queue_cam_id.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_queue_cam_id.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_queue_cam_id.sql	(revision 22322)
@@ -0,0 +1,17 @@
+INSERT INTO fakeRun
+    SElECT
+        0,              -- fake_id
+        cam_id,         -- cam_id
+        '%s',           -- state
+        '%s',           -- workdir
+        '%s',           -- label
+        '%s',           -- reduction
+        '%s',           -- expgroup
+        '%s',           -- dvodb 
+        '%s',           -- tess_id
+        '%s',           -- end_stage
+        NULL            -- epoch
+    FROM camRun
+    WHERE
+        camRun.state = 'full'
+        AND camRun.cam_id = %lld
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_revertprocessedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_revertprocessedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_revertprocessedimfile.sql	(revision 22322)
@@ -0,0 +1,8 @@
+DELETE FROM fakeProcessedImfile
+USING fakeProcessedImfile, fakeRun, camRun, chipRun, rawExp
+WHERE
+    fakeProcessedImfile.fake_id = fakeRun.fake_id
+    AND fakeRun.cam_id = camRun.cam_id
+    AND camRun.chip_id = chipRun.chip_id
+    AND chipRun.exp_id = rawExp.exp_id
+    AND fakeProcessedImfile.fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_unmasked.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_unmasked.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/faketool_unmasked.sql	(revision 22322)
@@ -0,0 +1,11 @@
+SELECT
+ *
+FROM
+ (SELECT 
+     fakeRun.label, 
+     count(fakeRun.fake_id) as n_fakeruns
+  FROM fakeRun 
+  LEFT JOIN fakeMask 
+  USING (label) 
+  WHERE fakeMask.label IS NULL 
+  GROUP BY fakeRun.label) as fakeUnmask
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_chiprundone.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_chiprundone.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_chiprundone.sql	(revision 22322)
@@ -0,0 +1,15 @@
+SELECT
+    corr_id,
+    cam_id,
+    chipRun.*
+FROM flatcorrRun
+JOIN flatcorrChipLink
+    USING(corr_id)
+JOIN chipRun
+    USING(chip_id)
+LEFT JOIN camRun
+    ON chipRun.chip_id = camRun.chip_id
+WHERE
+    flatcorrRun.state = 'new'
+    AND chipRun.state = 'full'
+    AND camRun.cam_id is NULL;
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_completely_processed_chiprun.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_completely_processed_chiprun.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_completely_processed_chiprun.sql	(revision 22322)
@@ -0,0 +1,36 @@
+INSERT INTO chipRunDone
+SELECT
+    chip_id,
+    exp_id
+FROM (SELECT DISTINCT
+        chip_id,
+        exp_id,
+        state,
+        workdir,
+        workdir_state,
+        label,
+        reduction,
+        expgroup,
+        dvodb
+    FROM
+        (SELECT 
+            chipRun.*,
+            rawImfile.class_id as rawimfile_class_id,
+            chipProcessedImfile.class_id
+        FROM chipRun
+        JOIN rawImfile
+            USING(exp_id)
+        LEFT JOIN chipProcessedImfile
+            ON chipRun.chip_id = chipProcessedImfile.chip_id
+            AND rawImfile.exp_id = chipProcessedImfile.exp_id
+            AND rawImfile.class_id = chipProcessedImfile.class_id
+        WHERE
+            chipRun.state = 'full'
+        GROUP BY
+            chipRun.chip_id,
+            chipRun.exp_id
+        HAVING
+            COUNT(rawImfile.class_id) = COUNT(chipProcessedImfile.class_id)
+            AND SUM(chipProcessedImfile.fault) = 0
+        ) as Foo
+    ) as Bar
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_create_tmp_chiprundone.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_create_tmp_chiprundone.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_create_tmp_chiprundone.sql	(revision 22322)
@@ -0,0 +1,3 @@
+CREATE TEMPORARY TABLE chipRunDone
+(chip_id BIGINT, exp_id VARCHAR(64), PRIMARY KEY(chip_id)) ENGINE=MEMORY
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_find_completed_floatcorruns.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_find_completed_floatcorruns.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_find_completed_floatcorruns.sql	(revision 22322)
@@ -0,0 +1,13 @@
+SELECT DISTINCT
+    flatcorrRun.*
+FROM flatcorrRun
+JOIN flatcorrExp
+    USING(corr_id)
+JOIN chipRunDone
+    USING(chip_id)
+WHERE
+    flatcorrRun.state != "stop"
+GROUP BY
+    corr_id, chip_id
+HAVING
+    COUNT(flatcorrExp.chip_id) = COUNT(chipRunDone.chip_id)
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_find_processedimfiles.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_find_processedimfiles.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_find_processedimfiles.sql	(revision 22322)
@@ -0,0 +1,14 @@
+SELECT
+    corr_id,
+    chipProcessedImfile.*
+FROM flatcorrRun
+JOIN flatcorrExp
+    USING(corr_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN chipProcessedImfile
+    USING(chip_id)
+WHERE
+    flatcorrRun.state = 'run'
+    AND chipRun.state = 'new'
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_inputimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_inputimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_inputimfile.sql	(revision 22322)
@@ -0,0 +1,9 @@
+SELECT 
+       chipProcessedImfile.*, 
+       rawExp.filelevel ,
+       rawExp.camera,
+       rawExp.telescope,
+       rawExp.filter
+FROM chipProcessedImfile
+JOIN rawExp
+USING (exp_id)
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_pending.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_pending.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_pending.sql	(revision 22322)
@@ -0,0 +1,24 @@
+SELECT 
+    *
+FROM
+flatcorrRun
+JOIN flatcorrExp
+    USING(corr_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN rawImfile
+    USING(exp_id)
+LEFT JOIN chipProcessedImfile
+    ON chipRun.chip_id = chipProcessedImfile.chip_id
+    AND rawImfile.exp_id = chipProcessedImfile.exp_id
+    AND rawImfile.class_id = chipProcessedImfile.class_id
+WHERE
+    flatcorrRun.state = 'run'
+    AND chipRun.state = 'full'
+GROUP BY
+    chipRun.chip_id,
+    chipRun.exp_id
+HAVING
+    COUNT(rawImfile.class_id) = COUNT(chipProcessedImfile.class_id)
+    AND SUM(chipProcessedImfile.fault) = 0
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_pendingprocess.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_pendingprocess.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/flatcorr_pendingprocess.sql	(revision 22322)
@@ -0,0 +1,34 @@
+SELECT 
+  corr_id,
+  dvodb,
+  filter,
+  state,
+  workdir,
+  label,
+  reduction,
+  region,
+  chip_count,
+  cam_count
+FROM 
+  (SELECT 
+     flatcorrRun.*,
+     flatcorrChipLink.corr_id as chip_corr_id,
+     count(flatcorrChipLink.chip_id) as chip_count
+   FROM flatcorrRun
+   JOIN flatcorrChipLink
+     ON flatcorrChipLink.corr_id = flatcorrRun.corr_id
+   GROUP BY
+     flatcorrChipLink.corr_id) AS t1
+LEFT JOIN
+  (SELECT 
+     flatcorrCamLink.corr_id as cam_corr_id,
+     count(flatcorrCamLink.cam_id) as cam_count
+   FROM flatcorrCamLink
+   JOIN camRun
+       ON flatcorrCamLink.cam_id = camRun.cam_id
+   WHERE camRun.state = 'full'
+   GROUP BY
+       flatcorrCamLink.corr_id) AS t2
+ON t1.chip_corr_id = t2.cam_corr_id
+WHERE chip_count = cam_count
+AND state = 'new'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_addmask.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_addmask.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_addmask.sql	(revision 22322)
@@ -0,0 +1,6 @@
+UPDATE
+    magicRun
+SET
+    state = 'stop'
+WHERE
+    state != 'stop'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_create_tmp_warpcomplete.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_create_tmp_warpcomplete.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_create_tmp_warpcomplete.sql	(revision 22322)
@@ -0,0 +1,3 @@
+CREATE TEMPORARY TABLE warpComplete
+ (warp_id BIGINT, skycell_id VARCHAR(64), tess_id VARCHAR(64),
+ PRIMARY KEY(warp_id, skycell_id, tess_id)) ENGINE=MEMORY;
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery.sql	(revision 22322)
@@ -0,0 +1,110 @@
+-- This is a sequence of SQL for searching for exposures on which
+-- magic may be run, and populating the run.  This file is provided as
+-- a convenience, and is intended solely for playing around and
+-- testing.  Updates here will not affect the operation of magictool.
+-- Conversely, it may not be up to date with the actual queries used
+-- by magictool.
+
+--------------------------------------------------------------------
+-- NOTE: THIS FILE IS NOT USED BY magictool
+-- NOTE : this file contains examples of tess_id as a join qualifier which are wrong
+--------------------------------------------------------------------
+
+-- magictool_definebyquery_temp_create.sql
+CREATE TEMPORARY TABLE magicBestDiffs (
+exp_id BIGINT,
+skycell_id VARCHAR(64),
+tess_id VARCHAR(64),
+diff_id BIGINT,
+PRIMARY KEY(exp_id, skycell_id, tess_id)
+) ENGINE=MEMORY;
+
+
+-- magictool_definebyquery_temp_insert.sql
+-- List of best differences for each exposure
+INSERT INTO magicBestDiffs
+SELECT
+    exp_id,
+    warpSkyfile.skycell_id,
+    warpSkyfile.tess_id,
+    MAX(diffSkyfile.diff_id) AS diff_id
+FROM rawExp
+JOIN chipRun USING(exp_id, tess_id)
+JOIN camRun USING(chip_id, tess_id)
+JOIN fakeRun USING(cam_id, tess_id)
+JOIN warpRun USING(fake_id, tess_id)
+JOIN warpSkyCellMap USING(warp_id, tess_id)
+JOIN warpSkyfile USING(warp_id, skycell_id, tess_id)
+JOIN diffInputSkyfile
+    ON diffInputSkyfile.warp_id = warpSkyfile.warp_id
+    AND diffInputSkyfile.skycell_id = warpSkyfile.skycell_id
+    AND diffInputSkyfile.tess_id = warpSkyfile.tess_id
+    AND diffInputSkyfile.template = 0 -- selecting inputs only
+JOIN diffSkyfile USING(diff_id)
+WHERE
+    diffSkyfile.fault = 0
+-----    AND warpSkyfile.good_frac >= 0.5
+-- magictool_definebyquery_temp_insert_groupby.sql
+GROUP BY
+    exp_id,
+    warpSkyfile.skycell_id
+;
+
+
+-- magictool_definebyquery_select_part1.sql
+-- This is part 1 of 2 of a query to get a list of exposures on which magic may be performed
+-- After this follows magictool_definebyquery_select_part2.sql
+SELECT
+    exp_id,
+    filter,
+    num_todo,
+    num_done
+FROM (
+    -- Number of skycells as a function of exposure
+    SELECT
+        exp_id,
+        filter,
+        COUNT(skycell_id) AS num_todo
+    FROM rawExp
+    JOIN chipRun USING(exp_id, tess_id)
+    JOIN camRun USING(chip_id, tess_id)
+    JOIN fakeRun USING(cam_id, tess_id)
+    JOIN warpRun USING(fake_id, tess_id)
+    JOIN warpSkyCellMap USING(warp_id, tess_id)
+    JOIN warpSkyfile USING(warp_id, skycell_id, tess_id)
+    WHERE
+        warpSkyfile.ignored = 0
+        AND warpRun.state = 'stop'
+    -- INSERT HERE any additional restrictions (e.g., exp_id, warpSkyfile.good_frac)
+-----        AND warpSkyfile.good_frac > 0.5
+-- INSERT HERE magictool_definebyquery_select_part2.sql
+-- magictool_definebyquery_select_part2.sql
+-- This is part 2 of 2 of a query to get a list of exposures on which magic may be performed
+-- This follows magictool_definebyquery_select_part1.sql
+    GROUP BY
+        exp_id
+    ) AS magicSkycellNums
+JOIN (
+    -- Number of completed diffs for an exposure
+    SELECT
+        exp_id,
+        COUNT(diff_id) AS num_done
+    FROM magicBestDiffs
+    GROUP BY
+        exp_id
+    ) AS magicDiffNums USING(exp_id)
+LEFT JOIN magicRun USING(exp_id)
+WHERE
+    num_done = num_todo
+    AND magicRun.magic_id IS NULL
+;
+
+-- magictool_definebyquery_insert.sql
+-- Insert the best list of diffs as magic inputs
+INSERT INTO magicInputSkyfile
+SELECT
+    @MAGIC_ID@, -- Update this with the appropriate magic_id
+    diff_id,
+    CONCAT(tess_id, '.', skycell_id) AS node
+FROM magicBestDiffs
+;
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_insert.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_insert.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_insert.sql	(revision 22322)
@@ -0,0 +1,9 @@
+-- Insert the best list of diffs as magic inputs
+INSERT INTO magicInputSkyfile
+SELECT
+    @MAGIC_ID@, -- Update this with the appropriate magic_id
+    diff_id,
+    skycell_id
+FROM magicBestDiffs
+WHERE
+    exp_id = @EXP_ID@ -- Update this with the appropriate exp_id
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_select_part1.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_select_part1.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_select_part1.sql	(revision 22322)
@@ -0,0 +1,25 @@
+-- This is part 1 of 2 of a query to get a list of exposures on which magic may be performed
+-- After this follows magictool_definebyquery_select_part2.sql
+SELECT
+    exp_id,
+    filter,
+    num_todo,
+    num_done
+FROM (
+    -- Number of skycells as a function of exposure
+    SELECT
+        exp_id,
+        filter,
+        COUNT(skycell_id) AS num_todo
+    FROM rawExp
+    JOIN chipRun USING(exp_id)
+    JOIN camRun USING(chip_id)
+    JOIN fakeRun USING(cam_id)
+    JOIN warpRun USING(fake_id)
+    JOIN warpSkyCellMap USING(warp_id)
+    JOIN warpSkyfile USING(warp_id, skycell_id)
+    WHERE
+        warpSkyfile.ignored = 0
+        AND warpRun.state = 'stop'
+    -- INSERT HERE any additional restrictions (e.g., exp_id, warpSkyfile.good_frac)
+-- INSERT HERE magictool_definebyquery_select_part2.sql
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_select_part2.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_select_part2.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_select_part2.sql	(revision 22322)
@@ -0,0 +1,18 @@
+-- This is part 2 of 2 of a query to get a list of exposures on which magic may be performed
+-- This follows magictool_definebyquery_select_part1.sql
+    GROUP BY
+        exp_id
+    ) AS magicSkycellNums
+JOIN (
+    -- Number of completed diffs for an exposure
+    SELECT
+        exp_id,
+        COUNT(diff_id) AS num_done
+    FROM magicBestDiffs
+    GROUP BY
+        exp_id
+    ) AS magicDiffNums USING(exp_id)
+LEFT JOIN magicRun USING(exp_id)
+WHERE
+    num_done = num_todo
+    AND magicRun.magic_id IS NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_select_test.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_select_test.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_select_test.sql	(revision 22322)
@@ -0,0 +1,46 @@
+-- This is the combination of the two parts of the query to get a list
+-- of exposures to be magic-ed
+
+--------------------------------------------------------------------------------
+-- THIS FILE IS INTENDED FOR TESTING ONLY!  IT IS NOT USED BY stacktool.
+-- CHANGES SHOULD BE MADE TO magictool_definebyquery_select_part1.sql
+-- AND magictool_definebyquery_select_part2.sql
+--------------------------------------------------------------------------------
+
+-- magictool_definebyquery_select_part1.sql
+SELECT
+    *
+FROM (
+    -- Number of skycells as a function of exposure
+    SELECT
+        exp_id,
+        filter,
+        COUNT(skycell_id) AS num_todo
+    FROM rawExp
+    JOIN chipRun USING(exp_id)
+    JOIN camRun USING(chip_id)
+    JOIN fakeRun USING(cam_id)
+    JOIN warpRun USING(fake_id)
+    JOIN warpSkyCellMap USING(warp_id)
+    JOIN warpSkyfile USING(warp_id, skycell_id)
+    WHERE
+        warpSkyfile.ignored = 0
+        AND warpRun.state = 'stop'
+    -- INSERT HERE any additional restrictions (e.g., exp_id, warpSkyfile.good_frac)
+-- magictool_definebyquery_select_part2.sql
+    GROUP BY
+        exp_id
+    ) AS magicSkycellNums
+JOIN (
+    -- Number of completed diffs for an exposure
+    SELECT
+        exp_id,
+        COUNT(diff_id) AS num_done
+    FROM magicBestDiffs
+    GROUP BY
+        exp_id
+    ) AS magicDiffNums USING(exp_id)
+LEFT JOIN magicRun USING(exp_id)
+WHERE
+    num_done = num_todo
+    AND magicRun.magic_id IS NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_temp_create.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_temp_create.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_temp_create.sql	(revision 22322)
@@ -0,0 +1,7 @@
+CREATE TEMPORARY TABLE magicBestDiffs (
+exp_id BIGINT,
+skycell_id VARCHAR(64),
+tess_id VARCHAR(64),
+diff_id BIGINT,
+PRIMARY KEY(exp_id, skycell_id, tess_id)
+) ENGINE=MEMORY;
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_temp_insert.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_temp_insert.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_temp_insert.sql	(revision 22322)
@@ -0,0 +1,21 @@
+-- List of best differences for each exposure
+INSERT INTO magicBestDiffs
+SELECT
+    rawExp.exp_id,
+    warpSkyfile.skycell_id,
+    warpSkyfile.tess_id,
+    MAX(diffSkyfile.diff_id) AS diff_id
+FROM rawExp
+JOIN chipRun USING(exp_id)
+JOIN camRun USING(chip_id)
+JOIN fakeRun USING(cam_id)
+JOIN warpRun USING(fake_id)
+JOIN warpSkyCellMap USING(warp_id)
+JOIN warpSkyfile USING(warp_id, skycell_id)
+JOIN diffInputSkyfile
+    ON diffInputSkyfile.warp_id = warpSkyfile.warp_id
+    AND diffInputSkyfile.skycell_id = warpSkyfile.skycell_id
+    AND diffInputSkyfile.template = 0 -- selecting inputs only
+JOIN diffSkyfile USING(diff_id)
+WHERE
+    diffSkyfile.fault = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_temp_insert_groupby.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_temp_insert_groupby.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_definebyquery_temp_insert_groupby.sql	(revision 22322)
@@ -0,0 +1,3 @@
+GROUP BY
+    exp_id,
+    warpSkyfile.skycell_id
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_inputs.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_inputs.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_inputs.sql	(revision 22322)
@@ -0,0 +1,33 @@
+SELECT *
+FROM (
+-- Single skycells
+SELECT
+    magicRun.magic_id,
+    magicRun.state,
+    magicInputSkyfile.node,
+    diffSkyfile.uri,
+    diffSkyfile.path_base,
+    diffSkyfile.fault
+FROM magicInputSkyfile
+JOIN diffSkyfile
+    USING(diff_id)
+JOIN magicRun
+    USING(magic_id)
+UNION
+-- Merged skycells
+SELECT
+    magicRun.magic_id,
+    magicRun.state,
+    magicTree.node,
+    magicNodeResult.uri,
+    NULL, -- magicNodeResult doesn't have a path_base
+    magicNodeResult.fault
+FROM magicTree
+JOIN magicRun
+    USING(magic_id)
+JOIN magicNodeResult
+    ON magicTree.magic_id = magicNodeResult.magic_id
+    AND magicTree.dep = magicNodeResult.node
+) as Foo
+WHERE
+    fault = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_inputskyfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_inputskyfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_inputskyfile.sql	(revision 22322)
@@ -0,0 +1,7 @@
+SELECT
+    magicInputSkyfile.*
+FROM magicInputSkyfile
+JOIN magicRun
+    USING(magic_id)
+WHERE
+    magicRun.state = 'run'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_mask.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_mask.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_mask.sql	(revision 22322)
@@ -0,0 +1,8 @@
+SELECT
+    magicMask.*
+FROM magicMask
+JOIN magicRun
+    USING(magic_id)
+WHERE
+    magicRun.state = 'stop'
+    AND magicMask.fault = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_tomask.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_tomask.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_tomask.sql	(revision 22322)
@@ -0,0 +1,18 @@
+SELECT
+    magic_id,
+    exp_id,
+    camera,
+    magicRun.workdir,
+    uri
+FROM magicRun
+JOIN rawExp USING(exp_id)
+JOIN magicTree USING(magic_id)
+LEFT JOIN magicNodeResult USING(magic_id, node)
+WHERE
+    magicRun.state = 'run'
+    AND magicNodeResult.node = 'root'
+    AND magicNodeResult.fault = 0
+GROUP BY
+    magic_id
+HAVING
+    COUNT(magicTree.node) = COUNT(magicNodeResult.node)
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_toprocess_inputs.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_toprocess_inputs.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_toprocess_inputs.sql	(revision 22322)
@@ -0,0 +1,19 @@
+SELECT
+    magicTree.*,
+    rawExp.camera,
+    diffSkyfile.path_base,
+    -- convert magic_id into a boolean value (1 or 0)
+    -- note that the type stays a 64 bit int
+    magicNodeResult.magic_id IS TRUE as done
+FROM magicTree
+JOIN magicRun USING(magic_id)
+JOIN magicInputSkyfile USING(magic_id, node)
+JOIN diffSkyfile USING(diff_id)
+JOIN rawExp USING(exp_id)
+LEFT JOIN magicNodeResult
+    ON magicTree.magic_id = magicNodeResult.magic_id
+    AND magicTree.node = magicNodeResult.node
+WHERE
+    magicRun.state = 'run'
+    AND magicNodeResult.magic_id IS NULL
+    AND magicNodeResult.node IS NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_toprocess_tree.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_toprocess_tree.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_toprocess_tree.sql	(revision 22322)
@@ -0,0 +1,17 @@
+SELECT
+    magicTree.*,
+    exp_id,
+    camera,
+    magicRun.workdir,
+    -- convert magic_id into a boolean value (1 or 0)
+    -- note that the type stays a 64 bit int
+    magicNodeResult.magic_id IS TRUE as done,
+    magicNodeResult.fault IS TRUE as bad
+FROM magicTree
+JOIN magicRun USING(magic_id)
+JOIN rawExp USING(exp_id)
+LEFT JOIN magicNodeResult
+    ON magicTree.magic_id = magicNodeResult.magic_id
+    AND magicTree.node = magicNodeResult.node
+WHERE
+    magicRun.state = 'run'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_toskyfilemask.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_toskyfilemask.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_toskyfilemask.sql	(revision 22322)
@@ -0,0 +1,10 @@
+SELECT
+    magicMask.*
+FROM magicRun
+JOIN magicMask
+    USING(magic_id)
+LEFT JOIN magicSkyfileMask
+    USING(magic_id)
+WHERE
+    magicRun.state = 'run'
+    AND magicSkyfileMask.magic_id IS NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_totree.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_totree.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/magictool_totree.sql	(revision 22322)
@@ -0,0 +1,16 @@
+SELECT
+    magic_id,
+    exp_id,
+    camera,
+    workdir,
+    ra,
+    decl,
+    tess_id
+FROM magicRun
+JOIN rawExp USING(exp_id)
+LEFT JOIN magicTree
+    USING(magic_id)
+WHERE
+    magicRun.state = 'run'
+    AND magicTree.node IS NULL
+    AND magicRun.fault = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_addjob_otherjob.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_addjob_otherjob.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_addjob_otherjob.sql	(revision 22322)
@@ -0,0 +1,3 @@
+INSERT INTO pstampJob
+ (req_id, rownum, state, jobType, uri, exp_id, outputBase, fault)
+ VALUES( %lld, '%s', '%s', '%s', '%s', %lld, '%s', %s)
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_addjob_stampjob.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_addjob_stampjob.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_addjob_stampjob.sql	(revision 22322)
@@ -0,0 +1,3 @@
+INSERT INTO pstampJob
+ (req_id, rownum, state, jobType, uri, exp_id, outputBase, fault, args)
+ VALUES( %lld, '%s', '%s', '%s', '%s', %lld, '%s', %s, '%s')
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_datastore.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_datastore.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_datastore.sql	(revision 22322)
@@ -0,0 +1,1 @@
+SELECT * FROM pstampDataStore
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_pendingjob.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_pendingjob.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_pendingjob.sql	(revision 22322)
@@ -0,0 +1,4 @@
+SELECT
+ *
+ FROM pstampJob
+ WHERE state = 'run'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_pendingreq.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_pendingreq.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_pendingreq.sql	(revision 22322)
@@ -0,0 +1,4 @@
+SELECT
+ *
+ FROM pstampRequest
+ WHERE state = 'new'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_project.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_project.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pstamptool_project.sql	(revision 22322)
@@ -0,0 +1,1 @@
+SELECT * FROM pstampProject
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pxadmin_create_tables.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pxadmin_create_tables.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pxadmin_create_tables.sql	(revision 22322)
@@ -0,0 +1,1089 @@
+CREATE TABLE pzDataStore (
+    camera VARCHAR(64),
+    telescope VARCHAR(64),
+    uri VARCHAR(255),
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(camera, telescope)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE summitExp (
+    exp_name VARCHAR(64),
+    camera VARCHAR(64),
+    telescope VARCHAR(64),
+    dateobs DATETIME,
+    exp_type VARCHAR(64),
+    uri VARCHAR(255),
+    imfiles INT,
+    fault SMALLINT NOT NULL,
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(exp_name, camera, telescope),
+    KEY(fault))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE summitImfile (
+    exp_name VARCHAR(64),
+    camera VARCHAR(64),
+    telescope VARCHAR(64),
+    file_id VARCHAR(64),
+    bytes INT,
+    md5sum VARCHAR(32),
+    class VARCHAR(64),
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(exp_name, camera, telescope, class, class_id),
+    KEY(file_id),
+    FOREIGN KEY (exp_name, camera, telescope)
+        REFERENCES  summitExp(exp_name, camera, telescope))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE pzDownloadExp (
+    exp_name VARCHAR(64),
+    camera VARCHAR(64),
+    telescope VARCHAR(64),
+    state VARCHAR(64),
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(exp_name, camera, telescope),
+    KEY(state),
+    FOREIGN KEY (exp_name, camera, telescope)
+        REFERENCES  summitExp(exp_name, camera, telescope))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE pzDownloadImfile (
+    exp_name VARCHAR(64),
+    camera VARCHAR(64),
+    telescope VARCHAR(64),
+    class VARCHAR(64),
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    fault SMALLINT NOT NULL,
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(exp_name, camera, telescope, class, class_id),
+    KEY(fault),
+    FOREIGN KEY (exp_name, camera, telescope)
+        REFERENCES  pzDownloadExp(exp_name, camera, telescope),
+    FOREIGN KEY (exp_name, camera, telescope, class, class_id)
+        REFERENCES  summitImfile(exp_name, camera, telescope, class, class_id)) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE newExp (
+    exp_id BIGINT AUTO_INCREMENT,
+    tmp_exp_name VARCHAR(64),
+    tmp_camera VARCHAR(64),
+    tmp_telescope VARCHAR(64),
+    state VARCHAR(64),
+    workdir VARCHAR(255),
+    workdir_state VARCHAR(64),
+    reduction VARCHAR(64),
+    dvodb VARCHAR(255),
+    tess_id VARCHAR(64),
+    end_stage VARCHAR(64),
+    label VARCHAR(64),
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(exp_id),
+    KEY(exp_id),
+    KEY(tmp_exp_name),
+    KEY(tmp_camera),
+    KEY(tmp_telescope),
+    KEY(state),
+    KEY(workdir_state),
+    KEY(end_stage),
+    KEY(label)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE newImfile (
+    exp_id BIGINT,
+    tmp_class_id VARCHAR(64),
+    uri VARCHAR(255),
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(exp_id, tmp_class_id),
+    FOREIGN KEY (exp_id)
+        REFERENCES  newExp(exp_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE rawExp (
+    exp_id BIGINT,
+    exp_name VARCHAR(64),
+    camera VARCHAR(64),
+    telescope VARCHAR(64),
+    dateobs DATETIME,
+    exp_tag VARCHAR(255),
+    exp_type VARCHAR(64),
+    filelevel VARCHAR(64),
+    workdir VARCHAR(255),
+    reduction VARCHAR(64),
+    dvodb VARCHAR(255),
+    tess_id VARCHAR(64),
+    end_stage VARCHAR(64),
+    filter VARCHAR(64),
+    comment VARCHAR(80),
+    airmass FLOAT,
+    ra DOUBLE,
+    decl DOUBLE,
+    exp_time FLOAT,
+    sat_pixel_frac FLOAT,
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    alt DOUBLE,
+    az DOUBLE,
+    ccd_temp FLOAT,
+    posang DOUBLE,
+    m1_x FLOAT,
+    m1_y FLOAT,
+    m1_z FLOAT,
+    m1_tip FLOAT,
+    m1_tilt FLOAT,
+    m2_x FLOAT,
+    m2_y FLOAT,
+    m2_z FLOAT,
+    m2_tip FLOAT,
+    m2_tilt FLOAT,
+    env_temperature FLOAT,
+    env_humidity FLOAT,
+    env_wind_speed FLOAT,
+    env_wind_dir FLOAT,
+    teltemp_m1 FLOAT,
+    teltemp_m1cell FLOAT,
+    teltemp_m2 FLOAT,
+    teltemp_spider FLOAT,
+    teltemp_truss FLOAT,
+    teltemp_extra FLOAT,
+    pon_time FLOAT,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    object VARCHAR(64),
+    sun_angle FLOAT,
+    sun_alt FLOAT,
+    moon_angle FLOAT,
+    moon_alt FLOAT,
+    moon_phase FLOAT,
+    hostname VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(exp_id),
+    KEY(exp_name),
+    KEY(end_stage),
+    KEY(fault),
+    FOREIGN KEY (exp_id)
+        REFERENCES  newExp(exp_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE rawImfile (
+    exp_id BIGINT,
+    exp_name VARCHAR(64),
+    camera VARCHAR(64),
+    telescope VARCHAR(64),
+    dateobs DATETIME,
+    tmp_class_id VARCHAR(64),
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    exp_type VARCHAR(64),
+    filelevel VARCHAR(64),
+    filter VARCHAR(64),
+    comment VARCHAR(80),
+    airmass FLOAT,
+    ra DOUBLE,
+    decl DOUBLE,
+    exp_time FLOAT,
+    sat_pixel_frac FLOAT,
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    alt DOUBLE,
+    az DOUBLE,
+    ccd_temp FLOAT,
+    posang DOUBLE,
+    m1_x FLOAT,
+    m1_y FLOAT,
+    m1_z FLOAT,
+    m1_tip FLOAT,
+    m1_tilt FLOAT,
+    m2_x FLOAT,
+    m2_y FLOAT,
+    m2_z FLOAT,
+    m2_tip FLOAT,
+    m2_tilt FLOAT,
+    env_temperature FLOAT,
+    env_humidity FLOAT,
+    env_wind_speed FLOAT,
+    env_wind_dir FLOAT,
+    teltemp_m1 FLOAT,
+    teltemp_m1cell FLOAT,
+    teltemp_m2 FLOAT,
+    teltemp_spider FLOAT,
+    teltemp_truss FLOAT,
+    teltemp_extra FLOAT,
+    pon_time FLOAT,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    object VARCHAR(64),
+    sun_angle FLOAT,
+    sun_alt FLOAT,
+    moon_angle FLOAT,
+    moon_alt FLOAT,
+    moon_phase FLOAT,
+    hostname VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(exp_id, class_id),
+    KEY(tmp_class_id),
+    KEY(fault),
+    UNIQUE KEY(exp_id, tmp_class_id),
+    FOREIGN KEY (exp_id, tmp_class_id)
+        REFERENCES newImfile(exp_id, tmp_class_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE guidePendingExp (guide_id BIGINT AUTO_INCREMENT, exp_id BIGINT, recipe VARCHAR(64), PRIMARY KEY(guide_id), KEY(guide_id), KEY(exp_id)) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE chipRun (
+    chip_id BIGINT AUTO_INCREMENT,
+    exp_id BIGINT,
+    state VARCHAR(64),
+    workdir VARCHAR(255),
+    workdir_state VARCHAR(64),
+    label VARCHAR(64),
+    reduction VARCHAR(64),
+     expgroup VARCHAR(64),
+    dvodb VARCHAR(255),
+    tess_id VARCHAR(64),
+    end_stage VARCHAR(64),
+    PRIMARY KEY(chip_id),
+    KEY(chip_id), KEY(exp_id),
+    KEY(state),
+    KEY(workdir_state),
+    KEY(label),
+    KEY(expgroup),
+    KEY(end_stage),
+    INDEX(chip_id, exp_id),
+    FOREIGN KEY (exp_id)
+        REFERENCES rawExp(exp_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE chipProcessedImfile (
+    chip_id BIGINT,
+    exp_id BIGINT,
+    class_id VARCHAR(64),
+    data_state VARCHAR(64),
+    uri VARCHAR(255),
+    bg FLOAT,
+    bg_stdev FLOAT,
+    bg_mean_stdev FLOAT,
+    bias FLOAT,
+    bias_stdev FLOAT,
+    fringe_0 FLOAT,
+    fringe_1 FLOAT,
+    fringe_2 FLOAT,
+    sigma_ra FLOAT,
+    sigma_dec FLOAT,
+    ap_resid FLOAT,
+    ap_resid_stdev FLOAT,
+    zp_mean FLOAT,
+    zp_stdev FLOAT,
+    fwhm_major FLOAT,
+    fwhm_minor FLOAT,
+    dtime_detrend FLOAT,
+    dtime_photom FLOAT,
+    dtime_astrom FLOAT,
+    hostname VARCHAR(64),
+    n_stars INT,
+    n_extended INT,
+    n_cr INT,
+    n_astrom INT,
+    path_base VARCHAR(255),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(chip_id, exp_id, class_id),
+    KEY(data_state),
+    KEY(fault),
+    FOREIGN KEY (chip_id, exp_id)
+        REFERENCES  chipRun(chip_id, exp_id),
+    FOREIGN KEY (exp_id, class_id)
+        REFERENCES  rawImfile(exp_id, class_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE chipMask (
+    label VARCHAR(64),
+    PRIMARY KEY(label))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE camRun (
+    cam_id BIGINT AUTO_INCREMENT,
+    chip_id BIGINT,
+    state VARCHAR(64),
+    workdir VARCHAR(255),
+    workdir_state VARCHAR(64),
+    label VARCHAR(64),
+    reduction VARCHAR(64),
+    expgroup VARCHAR(64),
+    dvodb VARCHAR(255),
+    tess_id VARCHAR(64),
+    end_stage VARCHAR(64),
+    PRIMARY KEY(cam_id),
+    KEY(cam_id),
+    KEY(chip_id),
+    KEY(state),
+    KEY(workdir_state),
+    KEY(label),
+    KEY(expgroup),
+    KEY(end_stage),
+    INDEX(cam_id, chip_id),
+    FOREIGN KEY (chip_id)
+        REFERENCES chipRun(chip_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE camProcessedExp (
+    cam_id BIGINT,
+    uri VARCHAR(255),
+    bg FLOAT,
+    bg_stdev FLOAT,
+    bg_mean_stdev FLOAT,
+    bias FLOAT,
+    bias_stdev FLOAT,
+    fringe_0 FLOAT,
+    fringe_1 FLOAT,
+    fringe_2 FLOAT,
+    sigma_ra FLOAT,
+    sigma_dec FLOAT,
+    ap_resid FLOAT,
+    ap_resid_stdev FLOAT,
+    zp_mean FLOAT,
+    zp_stdev FLOAT,
+    fwhm_major FLOAT,
+    fwhm_minor FLOAT,
+    dtime_detrend FLOAT,
+    dtime_photom FLOAT,
+    dtime_astrom FLOAT,
+    hostname VARCHAR(64),
+    n_stars INT,
+    n_extended INT,
+    n_cr INT,
+    n_astrom INT,
+    path_base VARCHAR(255),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(cam_id),
+    KEY(fault),
+    FOREIGN KEY (cam_id)
+        REFERENCES camRun(cam_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE camMask (
+    label VARCHAR(64),
+    PRIMARY KEY(label))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE fakeRun (
+    fake_id BIGINT AUTO_INCREMENT,
+    cam_id BIGINT,
+    state VARCHAR(64),
+    workdir VARCHAR(255),
+    label VARCHAR(64),
+    reduction VARCHAR(64),
+    expgroup VARCHAR(64),
+    dvodb VARCHAR(255),
+    tess_id VARCHAR(64),
+    end_stage VARCHAR(64),
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(fake_id),
+    KEY(cam_id),
+    KEY(state),
+    KEY(label),
+    KEY(expgroup),
+    KEY(end_stage),
+    INDEX(fake_id, cam_id),
+    FOREIGN KEY (cam_id)
+        REFERENCES camRun(cam_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE fakeProcessedImfile (
+    fake_id BIGINT AUTO_INCREMENT,
+    exp_id BIGINT,
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    dtime_fake FLOAT,
+    hostname VARCHAR(64),
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(fake_id, exp_id, class_id),
+    KEY(fault),
+    FOREIGN KEY (fake_id)
+        REFERENCES fakeRun(fake_id),
+    FOREIGN KEY (exp_id, class_id)
+        REFERENCES rawImfile(exp_id, class_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE fakeMask (
+    label VARCHAR(64),
+    PRIMARY KEY(label))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detRun (
+    det_id BIGINT AUTO_INCREMENT,
+    iteration INT,
+    det_type VARCHAR(64),
+    mode VARCHAR(64),
+    state VARCHAR(64),
+    filelevel VARCHAR(64),
+    workdir VARCHAR(255),
+    camera VARCHAR(64),
+    telescope VARCHAR(64),
+    exp_type VARCHAR(64),
+    reduction VARCHAR(64),
+    filter VARCHAR(64),
+    airmass_min FLOAT,
+    airmass_max FLOAT,
+    exp_time_min FLOAT,
+    exp_time_max FLOAT,
+    ccd_temp_min FLOAT,
+    ccd_temp_max FLOAT,
+    posang_min DOUBLE,
+    posang_max DOUBLE,
+    registered DATETIME,
+    time_begin DATETIME,
+    time_end DATETIME,
+    use_begin DATETIME,
+    use_end DATETIME,
+    solang_min FLOAT,
+    solang_max FLOAT,
+    label VARCHAR(64),
+    ref_det_id BIGINT,
+    ref_iter INT,
+    -- parent INT, :: dropping this
+    PRIMARY KEY(det_id),
+    KEY(det_id),
+    KEY(iteration),
+    KEY(det_type),
+    KEY(mode),
+    KEY(state),
+    KEY(label),
+    -- KEY(parent), :: dropping this
+    INDEX(det_id, iteration))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detInputExp (
+det_id BIGINT,
+iteration INT,
+exp_id BIGINT,
+include TINYINT,
+PRIMARY KEY(det_id, iteration, exp_id),
+INDEX(det_id, exp_id),
+INDEX(det_id, iteration),
+FOREIGN KEY (det_id)
+REFERENCES  detRun(det_id),
+FOREIGN KEY (exp_id)
+REFERENCES  rawExp(exp_id))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detProcessedImfile (
+    det_id BIGINT,
+    exp_id BIGINT,
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    recipe VARCHAR(64),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    fringe_0 DOUBLE,
+    fringe_1 DOUBLE,
+    fringe_2 DOUBLE,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, exp_id, class_id),
+    KEY(fault),
+    INDEX(det_id, class_id),
+    INDEX(det_id, exp_id),
+    FOREIGN KEY (det_id, exp_id)
+        REFERENCES  detInputExp(det_id, exp_id),
+    FOREIGN KEY (exp_id, class_id)
+        REFERENCES  rawImfile(exp_id, class_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detProcessedExp (
+    det_id BIGINT,
+    exp_id BIGINT,
+    recipe VARCHAR(64),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    fringe_0 DOUBLE,
+    fringe_1 DOUBLE,
+    fringe_2 DOUBLE,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, exp_id),
+    KEY(fault),
+    FOREIGN KEY (det_id, exp_id)
+        REFERENCES  detInputExp(det_id, exp_id),
+    FOREIGN KEY (det_id, exp_id)
+        REFERENCES  detProcessedImfile(det_id, exp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detStackedImfile (
+    det_id BIGINT,
+    iteration INT,
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    recipe VARCHAR(64),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, iteration, class_id),
+    KEY(fault),
+    FOREIGN KEY (det_id, iteration)
+        REFERENCES  detInputExp(det_id, iteration),
+    FOREIGN KEY (det_id, class_id)
+        REFERENCES  detProcessedImfile(det_id, class_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detNormalizedStatImfile (
+    det_id BIGINT,
+    iteration INT,
+    class_id VARCHAR(64),
+    norm FLOAT,
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, iteration, class_id),
+    KEY(fault),
+    FOREIGN KEY (det_id, iteration)
+    REFERENCES  detInputExp(det_id, iteration),
+    FOREIGN KEY (det_id, iteration, class_id)
+    REFERENCES  detStackedImfile(det_id, iteration, class_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detNormalizedImfile (
+    det_id BIGINT,
+    iteration INT,
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, iteration, class_id),
+    KEY(fault),
+    INDEX(det_id, iteration),
+    FOREIGN KEY (det_id)
+    REFERENCES  detInputExp(det_id),
+    FOREIGN KEY (det_id, iteration, class_id)
+    REFERENCES  detNormalizedStatImfile(det_id, iteration, class_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detNormalizedExp (
+    det_id BIGINT,
+    iteration INT,
+    recipe VARCHAR(64),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, iteration),
+    KEY(fault),
+    FOREIGN KEY (det_id, iteration)
+    REFERENCES  detInputExp(det_id, iteration),
+    FOREIGN KEY (det_id, iteration)
+    REFERENCES  detNormalizedImfile(det_id, iteration)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detResidImfile (
+    det_id BIGINT,
+    iteration INT,
+    ref_det_id BIGINT,
+    ref_iter INT,
+    exp_id BIGINT,
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    recipe VARCHAR(64),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    bg_skewness DOUBLE,
+    bg_kurtosis DOUBLE,
+    bin_stdev DOUBLE,
+    fringe_0 DOUBLE,
+    fringe_1 DOUBLE,
+    fringe_2 DOUBLE,
+    fringe_resid_0 DOUBLE,
+    fringe_resid_1 DOUBLE,
+    fringe_resid_2 DOUBLE,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, iteration, exp_id, class_id),
+    KEY(fault),
+    INDEX(det_id, iteration, exp_id),
+    FOREIGN KEY (det_id, iteration, exp_id)
+    REFERENCES  detInputExp(det_id, iteration, exp_id),
+    FOREIGN KEY (det_id, exp_id, class_id)
+    REFERENCES  detProcessedImfile(det_id, exp_id, class_id),
+    FOREIGN KEY (ref_det_id, ref_iter)
+    REFERENCES  detNormalizedExp(det_id, iteration)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detResidExp (
+       det_id BIGINT,
+       iteration INT,
+       exp_id BIGINT,
+       recipe VARCHAR(64),
+       bg DOUBLE,
+       bg_stdev DOUBLE,
+       bg_mean_stdev DOUBLE,
+       bg_skewness DOUBLE,
+       bg_kurtosis DOUBLE,
+       bin_stdev DOUBLE,
+       fringe_0 DOUBLE,
+       fringe_1 DOUBLE,
+       fringe_2 DOUBLE,
+       fringe_resid_0 DOUBLE,
+       fringe_resid_1 DOUBLE,
+       fringe_resid_2 DOUBLE,
+       user_1 DOUBLE,
+       user_2 DOUBLE,
+       user_3 DOUBLE,
+       user_4 DOUBLE,
+       user_5 DOUBLE,
+       path_base VARCHAR(255),
+       data_state VARCHAR(64),
+       accept TINYINT,
+       fault SMALLINT NOT NULL,
+       PRIMARY KEY(det_id, iteration, exp_id),
+       KEY(fault),
+       INDEX(det_id, iteration),
+       FOREIGN KEY (det_id, iteration, exp_id)
+       REFERENCES  detInputExp(det_id, iteration, exp_id),
+       FOREIGN KEY (det_id, iteration, exp_id)
+       REFERENCES  detResidImfile(det_id, iteration, exp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detRunSummary (
+       det_id BIGINT,
+       iteration INT,
+       data_state VARCHAR(64),
+       bg DOUBLE,
+       bg_stdev DOUBLE,
+       bg_mean_stdev DOUBLE,
+       accept TINYINT,
+       fault SMALLINT NOT NULL,
+       PRIMARY KEY(det_id, iteration),
+       KEY(fault),
+       FOREIGN KEY (det_id, iteration)
+         REFERENCES  detInputExp(det_id, iteration),
+       FOREIGN KEY (det_id, iteration)
+         REFERENCES  detResidExp(det_id, iteration)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detRegisteredImfile (
+       det_id BIGINT,
+       iteration INT,
+       class_id VARCHAR(64),
+       uri VARCHAR(255),
+       bg DOUBLE,
+       bg_stdev DOUBLE,
+       bg_mean_stdev DOUBLE,
+       user_1 DOUBLE,
+       user_2 DOUBLE,
+       user_3 DOUBLE,
+       user_4 DOUBLE,
+       user_5 DOUBLE,
+       path_base VARCHAR(255),
+       data_state VARCHAR(64),
+       fault SMALLINT NOT NULL,
+       PRIMARY KEY(det_id, iteration, class_id),
+       KEY(fault),
+       FOREIGN KEY (det_id, iteration)
+         REFERENCES  detRun(det_id, iteration)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE warpRun (
+    warp_id BIGINT AUTO_INCREMENT,
+    fake_id BIGINT,
+    mode VARCHAR(64),
+    state VARCHAR(64),
+    workdir VARCHAR(255),
+    workdir_state VARCHAR(64),
+    label VARCHAR(64),
+    dvodb VARCHAR(255),
+    tess_id VARCHAR(64),
+    end_stage VARCHAR(64),
+    registered DATETIME,
+    magiced TINYINT,
+    PRIMARY KEY(warp_id),
+    KEY(warp_id),
+    KEY(fake_id),
+    KEY(mode),
+    KEY(state),
+    KEY(workdir_state),
+    KEY(label),
+    KEY(end_stage),
+    KEY(magiced),
+    INDEX(warp_id, fake_id),
+    FOREIGN KEY (fake_id)
+        REFERENCES fakeRun(fake_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE warpSkyCellMap (
+    warp_id BIGINT,
+    skycell_id VARCHAR(64),
+    tess_id VARCHAR(64),
+    class_id VARCHAR(64),
+    fault SMALLINT,
+    PRIMARY KEY(warp_id, skycell_id, tess_id, class_id),
+    KEY(fault),
+    FOREIGN KEY (warp_id)
+        REFERENCES warpRun(warp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE warpSkyfile (
+    warp_id BIGINT,
+    skycell_id VARCHAR(64),
+    tess_id VARCHAR(64),
+    uri VARCHAR(255),
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    dtime_warp FLOAT,
+    hostname VARCHAR(64),
+    good_frac FLOAT,
+    xmin INT,
+    xmax INT,
+    ymin INT,
+    ymax INT,
+    ignored TINYINT,
+    fault SMALLINT,
+    PRIMARY KEY(warp_id, skycell_id, tess_id),
+    KEY(good_frac),
+    KEY(ignored), KEY(fault),
+    FOREIGN KEY (warp_id, skycell_id, tess_id)
+        REFERENCES warpSkyCellMap(warp_id, skycell_id, tess_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE warpMask (
+    label VARCHAR(64),
+    PRIMARY KEY(label)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE stackRun (
+        stack_id BIGINT AUTO_INCREMENT,
+        state VARCHAR(64),
+        workdir VARCHAR(255),
+        label VARCHAR(64),
+        reduction VARCHAR(64),
+        dvodb VARCHAR(255),
+        registered DATETIME,
+        skycell_id VARCHAR(64),
+        tess_id VARCHAR(64),
+        filter VARCHAR(64),
+        PRIMARY KEY(stack_id),
+        KEY(stack_id),
+        KEY(state),
+        KEY(skycell_id),
+        KEY(tess_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE stackInputSkyfile (
+        stack_id BIGINT,
+        warp_id BIGINT,
+        PRIMARY KEY(stack_id, warp_id),
+        FOREIGN KEY (stack_id)  REFERENCES  stackRun(stack_id),
+        FOREIGN KEY (warp_id)  REFERENCES  warpSkyfile(warp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE stackSumSkyfile (
+        stack_id BIGINT,
+        uri VARCHAR(255),
+        path_base VARCHAR(255),
+        bg DOUBLE,
+        bg_stdev DOUBLE,
+        dtime_stack FLOAT,
+        dtime_match_mean FLOAT,
+        dtime_match_stdev FLOAT,
+        dtime_initial FLOAT,
+        dtime_reject FLOAT,
+        dtime_final FLOAT,
+        dtime_phot FLOAT,
+        match_mean FLOAT,
+        match_stdev FLOAT,
+        match_rms FLOAT,
+        stamps_mean FLOAT,
+        stamps_stdev FLOAT,
+        stamps_min INT,
+        reject_images INT,
+        reject_pix_mean FLOAT,
+        reject_pix_stdev FLOAT,
+        sources INT,
+        hostname VARCHAR(64),
+        good_frac FLOAT,
+        fault SMALLINT,
+        PRIMARY KEY(stack_id),
+        KEY(dtime_stack),
+        KEY(good_frac),
+        KEY(fault),
+        FOREIGN KEY (stack_id)  REFERENCES  stackRun(stack_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE diffRun (
+        diff_id BIGINT AUTO_INCREMENT,
+        state VARCHAR(64),
+        workdir VARCHAR(255),
+        label VARCHAR(64),
+        reduction VARCHAR(64),
+        dvodb VARCHAR(255),
+        registered DATETIME,
+        skycell_id VARCHAR(64),
+        tess_id VARCHAR(64),
+        PRIMARY KEY(diff_id),
+        KEY(diff_id),
+        KEY(state),
+        KEY(skycell_id),
+        KEY(tess_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE diffInputSkyfile (
+        diff_id BIGINT,
+        template TINYINT,
+        stack_id BIGINT,
+        warp_id BIGINT,
+        skycell_id VARCHAR(64),
+        tess_id VARCHAR(64),
+        kind VARCHAR(64),
+        PRIMARY KEY(diff_id, template),
+        KEY(stack_id),
+        KEY(warp_id),
+        KEY(skycell_id),
+        KEY(tess_id),
+        KEY(kind),
+        FOREIGN KEY (diff_id)  REFERENCES  diffRun(diff_id),
+        FOREIGN KEY (stack_id)  REFERENCES  stackSumSkyfile(stack_id),
+        FOREIGN KEY (warp_id, skycell_id, tess_id)  REFERENCES  warpSkyfile(warp_id, skycell_id, tess_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE diffSkyfile (
+        diff_id BIGINT,
+        uri VARCHAR(255),
+        path_base VARCHAR(255),
+        bg DOUBLE,
+        bg_stdev DOUBLE,
+        stamps_num INT,
+        stamps_mean FLOAT,
+        stamps_rms FLOAT,
+        sources INT,
+        dtime_diff FLOAT,
+        dtime_match FLOAT,
+        dtime_phot FLOAT,
+        hostname VARCHAR(64),
+        good_frac FLOAT,
+        fault SMALLINT,
+        PRIMARY KEY(diff_id),
+        KEY(good_frac),
+        KEY(fault),
+        FOREIGN KEY (diff_id)  REFERENCES  diffRun(diff_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE magicRun (
+        magic_id BIGINT AUTO_INCREMENT,
+        exp_id BIGINT,
+        state VARCHAR(64),
+        workdir VARCHAR(255),
+        workdir_state VARCHAR(255),
+        label VARCHAR(64),
+        dvodb VARCHAR(255),
+        registered DATETIME,
+        fault SMALLINT,
+        PRIMARY KEY(magic_id),
+        KEY(magic_id),
+        KEY(state),
+        KEY(workdir_state),
+        KEY(label),
+        KEY(fault),
+        FOREIGN KEY (exp_id)  REFERENCES  rawExp(exp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE magicInputSkyfile (
+        magic_id BIGINT,
+        diff_id BIGINT,
+        node VARCHAR(64),
+        PRIMARY KEY(magic_id, diff_id),
+        FOREIGN KEY (magic_id)  REFERENCES  magicRun(magic_id),
+        FOREIGN KEY (diff_id)  REFERENCES  diffRun(diff_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE magicTree (
+        magic_id BIGINT,
+        node VARCHAR(64),
+        dep VARCHAR(64),
+        KEY(magic_id),
+        KEY(node),
+        KEY(dep),
+        INDEX(magic_id, node),
+        FOREIGN KEY (magic_id)  REFERENCES  magicRun(magic_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE magicNodeResult (
+        magic_id BIGINT,
+        node VARCHAR(64),
+        uri VARCHAR(255),
+        fault SMALLINT,
+        PRIMARY KEY(magic_id, node),
+        FOREIGN KEY (magic_id)  REFERENCES  magicRun(magic_id),
+        FOREIGN KEY (magic_id, node)  REFERENCES  magicTree(magic_id, node),
+        KEY(fault)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE magicMask (
+        magic_id BIGINT,
+        uri VARCHAR(255),
+        streaks INT,
+        fault SMALLINT,
+        PRIMARY KEY(magic_id),
+        FOREIGN KEY (magic_id)  REFERENCES  magicRun(magic_id),
+        KEY(fault)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE calDB (
+        cal_id BIGINT AUTO_INCREMENT,
+        dvodb VARCHAR(64),
+        state VARCHAR(64),
+        PRIMARY KEY(cal_id),
+        KEY(cal_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE calRun (
+        cal_id BIGINT AUTO_INCREMENT,
+        region VARCHAR(64),
+        last_step VARCHAR(64),
+        state VARCHAR(64),
+        PRIMARY KEY(cal_id),
+        KEY(cal_id),
+        KEY(last_step),
+        FOREIGN KEY (cal_id)  REFERENCES  calDB(cal_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE flatcorrRun (
+        corr_id BIGINT AUTO_INCREMENT,
+        dvodb VARCHAR(64),
+        filter VARCHAR(64),
+        state VARCHAR(64),
+        workdir VARCHAR(255),
+        label VARCHAR(64),
+        reduction VARCHAR(64),
+        region VARCHAR(64),
+        hostname VARCHAR(64),
+        fault SMALLINT NOT NULL,
+        PRIMARY KEY(corr_id),
+        KEY(corr_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+-- these two tables link the flatcorrRun to the associated chip and camera runs
+CREATE TABLE flatcorrChipLink (
+        corr_id BIGINT,
+        chip_id BIGINT,
+        PRIMARY KEY(corr_id, chip_id),
+        FOREIGN KEY (corr_id)  REFERENCES  flatcorrRun(corr_id),
+        FOREIGN KEY (chip_id)  REFERENCES  chipRun(chip_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE flatcorrCamLink (
+        corr_id BIGINT,
+        chip_id BIGINT,
+        cam_id BIGINT,
+        PRIMARY KEY(corr_id, cam_id),
+        FOREIGN KEY (corr_id)  REFERENCES  flatcorrRun(corr_id),
+        FOREIGN KEY (chip_id)  REFERENCES  chipRun(chip_id),
+        FOREIGN KEY (cam_id)  REFERENCES  camRun(cam_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE pstampDataStore (
+        ds_id BIGINT AUTO_INCREMENT,
+        state VARCHAR(64),
+        lastFileset VARCHAR(64),
+        outProduct VARCHAR(64) UNIQUE,
+        uri VARCHAR(255),
+        PRIMARY KEY(ds_id),
+        KEY(ds_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE pstampProject (
+        proj_id BIGINT AUTO_INCREMENT,
+        name VARCHAR(64) UNIQUE,
+        state VARCHAR(64),
+        dbname VARCHAR(64),
+        dvodb VARCHAR(64),
+        camera VARCHAR(64),
+        telescope VARCHAR(64),
+        need_magic TINYINT,
+        PRIMARY KEY(proj_id),
+        KEY(proj_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE pstampRequest (
+        req_id BIGINT AUTO_INCREMENT,
+        ds_id BIGINT,
+        state VARCHAR(64),
+        name VARCHAR(64) UNIQUE,
+        reqType VARCHAR(16),
+        outProduct VARCHAR(64),
+        uri VARCHAR(255),
+        fault SMALLINT,
+        PRIMARY KEY(req_id),
+        KEY(req_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE pstampJob (
+        job_id BIGINT AUTO_INCREMENT,
+        req_id BIGINT,
+        rownum VARCHAR(64),
+        state VARCHAR(64),
+        jobType VARCHAR(16),
+        fault SMALLINT,
+        uri VARCHAR(255),
+        exp_id BIGINT,
+        outputBase VARCHAR(255),
+        args VARCHAR(511),
+        PRIMARY KEY(job_id, req_id),
+        KEY(job_id),
+        FOREIGN KEY (req_id)  REFERENCES  pstampRequest(req_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pxadmin_drop_tables.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pxadmin_drop_tables.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pxadmin_drop_tables.sql	(revision 22322)
@@ -0,0 +1,58 @@
+SET FOREIGN_KEY_CHECKS=0;
+DROP TABLE IF EXISTS pzDataStore;
+DROP TABLE IF EXISTS summitExp;
+DROP TABLE IF EXISTS summitImfile;
+DROP TABLE IF EXISTS pzDownloadExp;
+DROP TABLE IF EXISTS pzDownloadImfile;
+DROP TABLE IF EXISTS newExp;
+DROP TABLE IF EXISTS newImfile;
+DROP TABLE IF EXISTS rawExp;
+DROP TABLE IF EXISTS rawImfile;
+DROP TABLE IF EXISTS guidePendingExp;
+DROP TABLE IF EXISTS chipRun;
+DROP TABLE IF EXISTS chipProcessedImfile;
+DROP TABLE IF EXISTS chipMask;
+DROP TABLE IF EXISTS camRun;
+DROP TABLE IF EXISTS camProcessedExp;
+DROP TABLE IF EXISTS camMask;
+DROP TABLE IF EXISTS detRun;
+DROP TABLE IF EXISTS detInputExp;
+DROP TABLE IF EXISTS fakeRun;
+DROP TABLE IF EXISTS fakeProcessedImfile;
+DROP TABLE IF EXISTS fakeMask;
+DROP TABLE IF EXISTS detProcessedImfile;
+DROP TABLE IF EXISTS detProcessedExp;
+DROP TABLE IF EXISTS detStackedImfile;
+DROP TABLE IF EXISTS detNormalizedStatImfile;
+DROP TABLE IF EXISTS detNormalizedImfile;
+DROP TABLE IF EXISTS detNormalizedExp;
+DROP TABLE IF EXISTS detResidImfile;
+DROP TABLE IF EXISTS detResidExp;
+DROP TABLE IF EXISTS detRunSummary;
+DROP TABLE IF EXISTS detRegisteredImfile;
+DROP TABLE IF EXISTS warpRun;
+DROP TABLE IF EXISTS warpSkyCellMap;
+DROP TABLE IF EXISTS warpSkyfile;
+DROP TABLE IF EXISTS warpMask;
+DROP TABLE IF EXISTS diffRun;
+DROP TABLE IF EXISTS diffInputSkyfile;
+DROP TABLE IF EXISTS diffSkyfile;
+DROP TABLE IF EXISTS stackRun;
+DROP TABLE IF EXISTS stackInputSkyfile;
+DROP TABLE IF EXISTS stackSumSkyfile;
+DROP TABLE IF EXISTS magicRun;
+DROP TABLE IF EXISTS magicInputSkyfile;
+DROP TABLE IF EXISTS magicTree;
+DROP TABLE IF EXISTS magicNodeResult;
+DROP TABLE IF EXISTS magicMask;
+DROP TABLE IF EXISTS magicSkyfileMask;
+DROP TABLE IF EXISTS calDB;
+DROP TABLE IF EXISTS calRun;
+DROP TABLE IF EXISTS flatcorrRun;
+DROP TABLE IF EXISTS flatcorrChipLink;
+DROP TABLE IF EXISTS flatcorrCamLink;
+DROP TABLE IF EXISTS pstampDataStore;
+DROP TABLE IF EXISTS pstampProject;
+DROP TABLE IF EXISTS pstampRequest;
+DROP TABLE IF EXISTS pstampJob;
+SET FOREIGN_KEY_CHECKS=1
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_find_completed_exp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_find_completed_exp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_find_completed_exp.sql	(revision 22322)
@@ -0,0 +1,35 @@
+SELECT DISTINCT
+    exp_name, -- return should match pzDownloadExp
+    camera,
+    telescope,
+    state,
+    NULL as epoch    -- epoch
+FROM (
+    SELECT
+        pzDownloadImfile.*,
+        pzDownloadExp.state,
+        summitExp.imfiles
+    FROM pzDownloadExp
+    JOIN summitExp
+        USING(exp_name, camera, telescope)
+    LEFT JOIN pzDownloadImfile
+        USING(exp_name, camera, telescope)
+    LEFT JOIN newExp
+        ON pzDownloadExp.exp_name = newExp.tmp_exp_name
+        AND pzDownloadExp.camera = newExp.tmp_camera
+        AND pzDownloadExp.telescope = newExp.tmp_telescope
+    WHERE
+        pzDownloadExp.state = 'run'
+        AND newExp.tmp_exp_name IS NULL
+        AND newExp.tmp_camera IS NULL
+        AND newExp.tmp_telescope IS NULL
+    GROUP BY
+        pzDownloadExp.exp_name,
+        pzDownloadExp.camera,
+        pzDownloadExp.telescope
+    -- it doesn't matter which field in pzDownloadImfile we count as we've
+    -- already Download a group by
+    HAVING
+        COUNT(pzDownloadImfile.exp_name) = summitExp.imfiles
+        AND SUM(pzDownloadImfile.fault) = 0
+    ) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_pendingimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_pendingimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_pendingimfile.sql	(revision 22322)
@@ -0,0 +1,20 @@
+SELECT *
+FROM (
+    SELECT
+        summitImfile.*,
+        summitExp.dateobs
+    FROM summitImfile
+    JOIN pzDownloadExp
+        USING(exp_name, camera, telescope)
+    LEFT JOIN pzDownloadImfile
+        USING(exp_name, camera, telescope, class, class_id)
+    JOIN summitExp
+        USING(exp_name, camera, telescope)
+    WHERE 
+        pzDownloadExp.state = 'run'
+        AND pzDownloadImfile.exp_name IS NULL
+        AND pzDownloadImfile.camera IS NULL
+        AND pzDownloadImfile.telescope IS NULL
+        AND pzDownloadImfile.class IS NULL
+        AND pzDownloadImfile.class_id IS NULL
+) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_revert_downloadimfile_faults.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_revert_downloadimfile_faults.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_revert_downloadimfile_faults.sql	(revision 22322)
@@ -0,0 +1,17 @@
+DELETE FROM pzDownloadImfile
+WHERE
+    fault != 0
+    -- fault was logged in the last 3 days
+    AND ABS(TIMESTAMPDIFF(SECOND, epoch, NOW())) < 86400 * 3
+    AND (
+        -- HTTP 503: try again
+        fault = 203
+        -- HTTP 500: timeout
+        OR fault = 200
+        -- HTTP 404: unknown datastore internal problem
+        OR fault = 104
+        -- ipptool errors
+        OR fault < 100
+        -- perl untrapped die()
+        OR fault = 255
+    )
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_revert_fileset_faults.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_revert_fileset_faults.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_revert_fileset_faults.sql	(revision 22322)
@@ -0,0 +1,17 @@
+UPDATE summitExp SET fault = 0
+WHERE
+    fault != 0
+    -- fault is older than 10 minutes
+    AND ABS(TIMESTAMPDIFF(SECOND, epoch, NOW())) > 60 * 10 
+    -- fault was logged in the last 3 days
+    AND ABS(TIMESTAMPDIFF(SECOND, epoch, NOW())) < 86400 * 3
+    AND (
+        -- HTTP 500: timeout
+        fault = 200
+        -- HTTP 404: unknown datastore internal problem
+        OR fault = 104
+        -- ipptool errors
+        OR fault < 100
+        -- perl untrapped die()
+        OR fault = 255
+    )
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_revertcopied.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_revertcopied.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/pztool_revertcopied.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM pzDownloadImfile
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_create_dup_table.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_create_dup_table.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_create_dup_table.sql	(revision 22322)
@@ -0,0 +1,2 @@
+CREATE TEMPORARY TABLE duplicate (exp_id BIGINT, PRIMARY KEY(exp_id))
+ENGINE=MEMORY
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_pendingexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_pendingexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_pendingexp.sql	(revision 22322)
@@ -0,0 +1,39 @@
+-- output should match the format of newExp
+SELECT DISTINCT
+    exp_id,
+    tmp_exp_name,
+    tmp_camera,
+    tmp_telescope,
+    state,
+    workdir,
+    workdir_state,
+    reduction,
+    dvodb,
+    tess_id,
+    end_stage,
+    label,
+    epoch
+FROM
+    (SELECT
+       newExp.*,
+       newImfile.tmp_class_id,
+       rawImfile.tmp_class_id as raw_tmp_class_id
+    FROM newExp
+    JOIN newImfile
+       USING(exp_id)
+    LEFT JOIN rawExp
+       USING(exp_id)
+    LEFT JOIN rawImfile
+        ON newImfile.exp_id = rawImfile.exp_id
+        AND newImfile.tmp_class_id = rawImfile.tmp_class_id
+    WHERE
+        newExp.state = 'run'
+        AND rawExp.exp_id IS NULL
+-- where hook %s
+    GROUP BY
+        newExp.exp_id
+    HAVING
+        COUNT(newImfile.tmp_class_id) = COUNT(rawImfile.tmp_class_id)
+        AND SUM(rawImfile.fault) = 0
+-- limit hook %s
+    ) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_pendingimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_pendingimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_pendingimfile.sql	(revision 22322)
@@ -0,0 +1,17 @@
+SELECT DISTINCT
+    newExp.exp_id,
+    newExp.tmp_exp_name,
+    newExp.tmp_camera,
+    newExp.tmp_telescope,
+    newExp.workdir,
+    newImfile.tmp_class_id,
+    newImfile.uri
+FROM newImfile
+JOIN newExp
+    USING(exp_id)
+LEFT JOIN rawImfile
+    ON newExp.exp_id = rawImfile.exp_id
+    AND newImfile.tmp_class_id = rawImfile.tmp_class_id
+WHERE
+    newExp.state = 'run'
+    AND rawImfile.tmp_class_id IS NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_populate_dup_table.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_populate_dup_table.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_populate_dup_table.sql	(revision 22322)
@@ -0,0 +1,13 @@
+INSERT INTO duplicate
+SELECT exp_id FROM
+    (
+        SELECT
+        MAX(exp_id) AS exp_id,
+--            tmp_exp_name,
+--            tmp_camera,
+--            tmp_telescope,
+        count(exp_id) AS count
+        FROM newExp
+        GROUP BY tmp_exp_name, tmp_camera, tmp_telescope
+        HAVING count > 1
+    ) as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_processedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_processedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_processedimfile.sql	(revision 22322)
@@ -0,0 +1,10 @@
+SELECT
+    rawImfile.*,
+    newExp.tmp_exp_name,
+    newExp.tmp_camera,
+    newExp.tmp_telescope
+FROM rawImfile
+JOIN newExp
+    USING(exp_id)
+-- bogus conditional so there is a where clause to append to
+WHERE rawImfile.exp_id is NOT NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_revertprocessedexp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_revertprocessedexp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_revertprocessedexp.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM rawExp
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_revertprocessedimfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_revertprocessedimfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/regtool_revertprocessedimfile.sql	(revision 22322)
@@ -0,0 +1,3 @@
+DELETE FROM rawImfile
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_insert.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_insert.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_insert.sql	(revision 22322)
@@ -0,0 +1,23 @@
+-- Define the inputs to a defined stack
+INSERT INTO
+        stackInputSkyfile(stack_id, warp_id)
+SELECT
+        @STACK_ID@, -- This should be replaced with the stack_id
+        warpRun.warp_id
+FROM warpSkyfile
+JOIN warpRun
+    USING(warp_id)
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN rawExp
+    USING(exp_id)
+WHERE
+    warpSkyfile.skycell_id = '%s'
+    AND warpRun.state = 'full'
+    AND rawExp.filter = '%s'
+    AND warpSkyfile.fault = 0
+    AND warpSkyfile.ignored = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_insert_random_part1.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_insert_random_part1.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_insert_random_part1.sql	(revision 22322)
@@ -0,0 +1,32 @@
+-- This is the part 1 of 2 of a query to use a random set of warps for
+-- the inputs to a defined stack.
+-- stacktool_definebyquery_insert_random_part2.sql should be appended.
+INSERT INTO
+        stackInputSkyfile(stack_id, warp_id)
+SELECT
+        @STACK_ID@, -- This should be replaced with the stack_id
+        warp_id
+FROM (
+    -- Sub-select to get the random set of warps
+    SELECT
+            warpSkyfile.*,
+            rand() AS rnd_num
+    FROM warpSkyfile
+    JOIN warpRun
+        USING(warp_id)
+    JOIN fakeRun
+        USING(fake_id)
+    JOIN camRun
+        USING(cam_id)
+    JOIN chipRun
+        USING(chip_id)
+    JOIN rawExp
+        USING(exp_id)
+    WHERE
+        skycell_id = '%s'
+        AND warpRun.state = 'full'
+        AND rawExp.filter = '%s' -- the result of the query is grouped by filter and inserted for one at a time
+        AND warpSkyfile.fault = 0
+        AND warpSkyfile.ignored = 0
+-- Put additional constraints here
+-- stacktool_definebyquery_insert_random_part2.sql goes here
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_insert_random_part2.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_insert_random_part2.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_insert_random_part2.sql	(revision 22322)
@@ -0,0 +1,8 @@
+-- This is the part 2 of 2 of a query to use a random set of warps for
+-- the inputs to a defined stack.
+-- stacktool_definebyquery_insert_random_part1.sql should be prepended.
+    -- Sort by the random number, and take the first N
+    -- to get a random set of N.
+    ORDER BY rnd_num
+    LIMIT %d
+) AS randomWarps
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_part1.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_part1.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_part1.sql	(revision 22322)
@@ -0,0 +1,30 @@
+-- This is the first part of the query to get a list of skycells with
+-- warps to be stacked, along with the number of warps already in a
+-- stack.  It needs to be completed by part 2 (see below).
+
+SELECT
+    skycell_id,
+    filter,
+    tess_id,
+    num_warp,
+    MAX(num_stack) AS num_stack
+FROM ((
+    -- Number of stack-ready warps as a function of skycell and filter
+    SELECT
+        skycell_id,
+        warpSkyfile.tess_id as tess_id,
+        rawExp.filter,
+        COUNT(warpSkyfile.skycell_id) AS num_warp -- number of warps that can be stacked
+    FROM warpRun
+        JOIN warpSkyfile USING(warp_id)
+        JOIN fakeRun USING(fake_id)
+        JOIN camRun USING(cam_id)
+        JOIN chipRun USING(chip_id)
+        JOIN rawExp USING(exp_id)
+    WHERE
+        warpRun.state = 'full'
+    AND warpSkyfile.ignored = 0
+    AND warpSkyfile.fault = 0
+    -- Here should follow the SQL in stacktool_definebyquery_part2.sql,
+    -- after any additional WHERE conditions have been added
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_part2.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_part2.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_part2.sql	(revision 22322)
@@ -0,0 +1,28 @@
+
+-- This is the second part of the query to get a list of skycells with
+-- warps to be stacked, along with the number of warps already in a
+-- stack.  It should follow part 1, which is in
+-- stacktool_definebyquery_part1.sql
+
+    GROUP BY
+        skycell_id,
+        filter
+        ) AS warpsToStack
+        LEFT JOIN (
+    -- Number of stack inputs as a function of skycell and filter
+    SELECT
+        skycell_id,
+	stackRun.tess_id as stack_tess_id,
+        filter,
+        COUNT(stackInputSkyfile.warp_id) as num_stack -- number of warps in a stack
+    FROM stackRun
+        JOIN stackInputSkyfile USING(stack_id)
+    GROUP BY
+        stack_id
+        ) AS stackSizes
+    -- JOINing the warpsToStack and stackSizes tables
+        USING(skycell_id, filter)
+        )
+    GROUP BY
+        skycell_id,
+        filter
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_test.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_test.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_definebyquery_test.sql	(revision 22322)
@@ -0,0 +1,59 @@
+-- This is the combination of the two parts of the query to get a list
+-- of skycells with warps to be stacked, along with the number of
+-- warps already in a stack.
+
+--------------------------------------------------------------------------------
+-- THIS FILE IS INTENDED FOR TESTING ONLY!  IT IS NOT USED BY stacktool.
+-- CHANGES SHOULD BE MADE TO stacktool_definebyquery_part1.sql
+-- AND stacktool_definebyquery_part2.sql
+--------------------------------------------------------------------------------
+
+-- stacktool_definebyquery_part1.sql
+SELECT
+    skycell_id,
+    tess_id,
+    filter,
+    num_warp,
+    MAX(num_stack) AS num_stack
+FROM ((
+    -- Number of stack-ready warps as a function of skycell and filter
+    SELECT
+        skycell_id,
+        tess_id,
+        filter,
+        COUNT(warpSkyfile.skycell_id) AS num_warp -- number of warps that can be stacked
+    FROM warpRun
+        JOIN warpSkyfile USING(warp_id)
+        JOIN fakeRun USING(fake_id)
+        JOIN camRun USING(cam_id)
+        JOIN chipRun USING(chip_id)
+        JOIN rawExp USING(exp_id)
+    WHERE
+        warpRun.state = 'full'
+    AND warpSkyfile.ignored = 0
+    AND warpSkyfile.fault = 0
+    -- Any additional selection on warps/exposures goes here
+-- stacktool_definebyquery_part2.sql
+    GROUP BY
+        skycell_id,
+        filter
+        ) AS warpsToStack
+        LEFT JOIN (
+    -- Number of stack inputs as a function of skycell and filter
+    SELECT
+        skycell_id,
+        tess_id,
+        filter,
+        COUNT(stackInputSkyfile.warp_id) as num_stack -- number of warps in a stack
+    FROM stackRun
+        JOIN stackInputSkyfile USING(stack_id)
+    GROUP BY
+        stack_id
+        ) AS stackSizes
+    -- JOINing the warpsToStack and stackSizes tables
+        USING(skycell_id, filter)
+        )
+    GROUP BY
+        skycell_id,
+        filter
+;
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_donecleanup.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_donecleanup.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_donecleanup.sql	(revision 22322)
@@ -0,0 +1,5 @@
+SELECT
+    stackRun.*
+FROM stackRun
+WHERE
+    stackRun.state = 'cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_find_complete_warps.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_find_complete_warps.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_find_complete_warps.sql	(revision 22322)
@@ -0,0 +1,45 @@
+SELECT
+    warpSkyfile.skycell_id,
+    warpSkyfile.tess_id,
+    stackRun.stack_id,
+    COUNT(warpSkyfile.skycell_id) AS num_avail,
+    COUNT(stackRun.stack_id) AS num_extant
+FROM warpRun
+JOIN warpSkyfile 
+    USING(warp_id)
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN rawExp
+    USING(exp_id)
+LEFT JOIN stackInputSkyfile
+    ON warpSkyfile.warp_id = stackInputSkyfile.warp_id
+LEFT JOIN stackRun
+    ON stackRun.skycell_id = warpSkyfile.skycell_id
+    AND stackRun.stack_id = stackInputSkyfile.stack_id
+WHERE
+    warpRun.state = 'full'
+    AND warpSkyfile.ignored = 0
+GROUP BY
+    warpSkyfile.skycell_id, stackRun.stack_id
+HAVING
+    num_avail > num_extant
+
+-- It seems like we should be grouping the results here but in fact we don't
+-- want to do that as each warp_id may contain multiple skycells.  So a warp_id
+-- may end up in more than one stackRun (for different skycells)
+--    GROUP BY
+--        warp_id
+
+
+INSERT INTO
+        stackInputSkyfile(stack_id, warp_id)
+SELECT
+        13,
+        warp_id
+FROM warpSkyfile
+WHERE
+    skycell_id = 'new';
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_inputskyfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_inputskyfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_inputskyfile.sql	(revision 22322)
@@ -0,0 +1,22 @@
+SELECT DISTINCT
+    warpSkyfile.*,
+    rawExp.camera
+FROM stackRun
+JOIN stackInputSkyfile
+    USING(stack_id)
+JOIN warpSkyfile
+    ON  stackInputSkyfile.warp_id = warpSkyfile.warp_id
+    AND stackRun.skycell_id       = warpSkyfile.skycell_id
+    AND stackRun.tess_id          = warpSkyfile.tess_id
+JOIN warpRun
+    ON stackInputSkyfile.warp_id = warpRun.warp_id
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN chipProcessedImfile
+    USING(chip_id)
+JOIN rawExp
+    ON chipProcessedImfile.exp_id = rawExp.exp_id
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_pendingcleanuprun.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_pendingcleanuprun.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_pendingcleanuprun.sql	(revision 22322)
@@ -0,0 +1,29 @@
+-- results in too many runs                 adding DISTINCT fixes this
+-- is the camera needed for this?           YES I think so that update can define the camera
+-- is the camera unique for this?           YES. Well, sort of.
+--                                          stack_skycell.pl insures that the files
+--                                          we're here to clean come from the same camera so
+--                                          if the camera is not unique we have nothing to do
+SELECT DISTINCT
+    stackRun.stack_id,
+    rawExp.camera,
+    stackRun.state
+FROM stackRun
+JOIN stackInputSkyfile
+    USING(stack_id)
+JOIN warpSkyfile
+    ON  stackInputSkyfile.warp_id = warpSkyfile.warp_id
+    AND stackRun.skycell_id       = warpSkyfile.skycell_id
+    AND stackRun.tess_id          = warpSkyfile.tess_id
+JOIN warpRun
+    ON warpRun.warp_id = warpSkyfile.warp_id
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN rawExp 
+     USING (exp_id)
+WHERE
+    stackRun.state = 'goto_cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_pendingcleanupskyfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_pendingcleanupskyfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_pendingcleanupskyfile.sql	(revision 22322)
@@ -0,0 +1,11 @@
+SELECT
+    stackSumSkyfile.*,
+    stackRun.state,
+    stackRun.workdir,
+    stackRun.dvodb,
+    stackRun.tess_id
+FROM stackRun
+JOIN stackSumSkyfile
+    USING(stack_id)
+WHERE
+    stackRun.state = 'goto_cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_revertsumskyfile_delete.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_revertsumskyfile_delete.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_revertsumskyfile_delete.sql	(revision 22322)
@@ -0,0 +1,4 @@
+DELETE FROM stackSumSkyfile
+USING stackSumSkyfile, stackRun
+WHERE stackSumSkyfile.stack_id = stackRun.stack_id
+    AND fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_revertsumskyfile_update.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_revertsumskyfile_update.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_revertsumskyfile_update.sql	(revision 22322)
@@ -0,0 +1,5 @@
+UPDATE stackRun
+JOIN stackSumSkyfile USING(stack_id)
+SET stackRun.state = 'new'
+WHERE
+    fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_sumskyfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_sumskyfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_sumskyfile.sql	(revision 22322)
@@ -0,0 +1,19 @@
+SELECT
+    stackSumSkyfile.*,
+    stackRun.state,
+    rawExp.camera
+FROM stackRun
+JOIN stackSumSkyfile
+    USING(stack_id)
+JOIN stackInputSkyfile
+    USING(stack_id)
+JOIN warpRun
+    USING(warp_id)
+JOIN fakeRun
+    ON warpRun.fake_id = fakeRun.fake_id
+JOIN camRun
+    ON camRun.cam_id   = fakeRun.cam_id
+JOIN chipRun
+    ON camRun.chip_id  = chipRun.chip_id
+JOIN rawExp
+    ON chipRun.exp_id  = rawExp.exp_id
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_tosum.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_tosum.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/stacktool_tosum.sql	(revision 22322)
@@ -0,0 +1,18 @@
+SELECT DISTINCT
+    stackRun.stack_id,
+    stackRun.tess_id,
+    stackRun.skycell_id,
+    stackRun.workdir,
+    stackRun.label,
+    stackRun.state
+FROM stackRun
+JOIN stackInputSkyfile
+    USING(stack_id)
+LEFT JOIN stackSumSkyfile
+    USING(stack_id)
+WHERE
+    ((stackRun.state = 'new'
+        AND stackSumSkyfile.stack_id IS NULL)
+    OR
+    (stackRun.state = 'update'
+        AND stackSumSkyfile.fault = 0))
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_change_run_state.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_change_run_state.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_change_run_state.sql	(revision 22322)
@@ -0,0 +1,15 @@
+-- change state of warpRun from goto_cleaned to cleaned or goto_purged to purged
+-- when all of the consituant skyfiles are in the end state
+-- arguments are new state (cleaned or purged) warp_id and new state again for 
+-- the chipProcessedImfile sub query
+UPDATE warpRun
+    SET state = '%s'
+    WHERE
+    warpRun.warp_id = %lld
+    AND (SELECT
+        COUNT(warp_id)
+        FROM warpSkyfile
+        WHERE
+            warpSkyfile.warp_id = warpRun.warp_id
+            AND data_state != '%s'
+        ) = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_change_skyfile_data_state.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_change_skyfile_data_state.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_change_skyfile_data_state.sql	(revision 22322)
@@ -0,0 +1,15 @@
+-- handle changes in warpSkyfile.data_state.
+-- Used for the modes tocleanedskyfile and topurgedskyfile
+-- args are new data_state, warp_id, skycell_id and current expected state for warpRun
+UPDATE warpSkyfile
+    SET 
+    data_state = '%s'
+WHERE
+    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: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_definebyquery.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_definebyquery.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_definebyquery.sql	(revision 22322)
@@ -0,0 +1,36 @@
+SELECT
+    *
+FROM
+    (SELECT DISTINCT
+        fakeRun.*,
+        chipRun.chip_id,
+        rawExp.camera,
+        rawExp.telescope,
+        rawExp.dateobs,
+        rawExp.exp_tag,
+        rawExp.exp_type,
+        rawExp.filelevel,
+        rawExp.filter,
+        rawExp.airmass,
+        rawExp.ra,
+        rawExp.decl,
+        rawExp.exp_time,
+        rawExp.sat_pixel_frac,
+        rawExp.bg,
+        rawExp.bg_stdev,
+        rawExp.bg_mean_stdev,
+        rawExp.alt,
+        rawExp.az,
+        rawExp.ccd_temp,
+        rawExp.posang,
+        rawExp.object,
+        rawExp.solang
+    FROM fakeRun
+    JOIN camRun
+        using(cam_id)
+    JOIN chipRun
+        using(chip_id)
+    JOIN rawExp
+        using(exp_id)
+    WHERE
+	fakeRun.state = 'full') as Foo
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_donecleanup.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_donecleanup.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_donecleanup.sql	(revision 22322)
@@ -0,0 +1,5 @@
+SELECT
+    warpRun.*
+FROM warpRun
+WHERE
+    warpRun.state = 'cleaned'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_exp.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_exp.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_exp.sql	(revision 22322)
@@ -0,0 +1,8 @@
+SELECT
+    fakeRun.*
+FROM warpRun
+JOIN fakeRun
+    USING(fake_id)
+WHERE
+    warpRun.state = 'new'
+    AND fakeRun.state = 'full'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_imfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_imfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_imfile.sql	(revision 22322)
@@ -0,0 +1,27 @@
+SELECT DISTINCT
+    rawImfile.*,
+    warpRun.fake_id,
+    camRun.cam_id as cam_id,
+    chipProcessedImfile.uri as chip_uri,
+    chipProcessedImfile.path_base as chip_path_base,
+    camProcessedExp.path_base as cam_path_base
+FROM warpRun
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN camProcessedExp
+    USING(cam_id)
+JOIN chipRun
+    ON camRun.chip_id = chipRun.chip_id
+JOIN chipProcessedImfile
+    ON chipRun.chip_id = chipProcessedImfile.chip_id
+JOIN rawImfile -- is there any reason not to refer back to rawimfiles?
+    ON chipProcessedImfile.exp_id = rawImfile.exp_id
+    AND chipProcessedImfile.class_id = rawImfile.class_id 
+WHERE
+    warpRun.state = 'new'
+    AND fakeRun.state = 'full'
+    AND camRun.state = 'full'
+    AND chipRun.state = 'full'
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_pendingcleanuprun.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_pendingcleanuprun.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_pendingcleanuprun.sql	(revision 22322)
@@ -0,0 +1,15 @@
+SELECT
+    warpRun.warp_id,
+    rawExp.camera,
+    warpRun.state
+FROM warpRun
+JOIN fakeRun
+USING (fake_id)
+JOIN camRun
+USING (cam_id)
+JOIN chipRun
+USING (chip_id)
+JOIN rawExp 
+USING (exp_id)
+WHERE
+    (warpRun.state = 'goto_cleaned' OR warpRun.state = 'goto_purged')
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_pendingcleanupskyfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_pendingcleanupskyfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_pendingcleanupskyfile.sql	(revision 22322)
@@ -0,0 +1,14 @@
+SELECT
+    warpSkyfile.*,
+    warpRun.state,
+    warpRun.workdir,
+    warpRun.label,
+    warpRun.dvodb,
+    warpRun.end_stage
+FROM warpRun
+JOIN warpSkyfile
+    USING(warp_id)
+WHERE
+    (warpRun.state = 'goto_cleaned' AND warpSkyfile.data_state = 'full')
+    OR
+    (warpRun.state = 'goto_purged' AND warpSkyfile.data_state != 'purged')
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_revertwarped.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_revertwarped.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_revertwarped.sql	(revision 22322)
@@ -0,0 +1,9 @@
+DELETE FROM warpSkyfile
+USING warpSkyfile, warpRun, fakeRun, camRun, chipRun, rawExp
+WHERE
+    warpSkyfile.warp_id = warpRun.warp_id
+    AND warpRun.fake_id = fakeRun.fake_id
+    AND fakeRun.cam_id = camRun.cam_id
+    AND camRun.chip_id = chipRun.chip_id
+    AND chipRun.exp_id = rawExp.exp_id
+    AND warpSkyfile.fault != 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_scmap.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_scmap.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_scmap.sql	(revision 22322)
@@ -0,0 +1,24 @@
+SELECT
+    warpSkyCellMap.*,
+    chipProcessedImfile.uri,
+    chipProcessedImfile.path_base as chip_path_base,
+    camProcessedExp.path_base as cam_path_base
+FROM warpRun
+JOIN warpSkyCellMap
+    USING(warp_id)
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN camProcessedExp
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN chipProcessedImfile
+    ON chipRun.chip_id = chipProcessedImfile.chip_id
+    AND warpSkyCellMap.class_id = chipProcessedImfile.class_id
+WHERE
+--    warpRun.state = 'new'
+    fakeRun.state = 'full'
+    AND camRun.state = 'full'
+    AND chipRun.state = 'full'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_tooverlap.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_tooverlap.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_tooverlap.sql	(revision 22322)
@@ -0,0 +1,30 @@
+SELECT
+    warpRun.warp_id,
+    warpRun.fake_id,
+    warpRun.workdir,
+    warpRun.tess_id,
+    warpRun.label,
+    rawExp.camera,
+    exp_id,
+    exp_tag,
+    warpRun.magiced
+FROM warpRun
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN rawExp
+    USING(exp_id)
+LEFT JOIN warpSkyCellMap
+    USING(warp_id)
+LEFT JOIN warpMask
+    ON warpRun.label = warpMask.label
+WHERE
+    warpRun.state = 'new'
+    AND fakeRun.state = 'full'
+    AND camRun.state = 'full'
+    AND chipRun.state = 'full'
+    AND warpSkyCellMap.warp_id IS NULL
+    AND warpMask.label IS NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_towarped.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_towarped.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_towarped.sql	(revision 22322)
@@ -0,0 +1,42 @@
+SELECT DISTINCT
+    warpSkyCellMap.warp_id,
+    warpSkyCellMap.skycell_id,
+    warpSkyCellMap.tess_id,
+    warpRun.fake_id,
+    warpRun.state,
+    camRun.cam_id,
+    rawExp.camera,
+    rawExp.exp_tag,
+    warpRun.workdir
+FROM warpRun
+JOIN warpSkyCellMap
+    USING(warp_id)
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN chipProcessedImfile
+    USING(chip_id)
+JOIN rawExp
+    ON chipRun.exp_id = rawExp.exp_id
+LEFT JOIN warpSkyfile
+    ON warpRun.warp_id = warpSkyfile.warp_id
+    AND warpSkyCellMap.skycell_id = warpSkyfile.skycell_id
+    AND warpSkyCellMap.tess_id = warpSkyfile.tess_id
+LEFT JOIN warpMask
+    ON warpRun.label = warpMask.label
+WHERE
+    ((warpRun.state = 'new'
+        AND warpSkyfile.warp_id IS NULL
+        AND warpSkyfile.skycell_id IS NULL
+        AND warpSkyfile.tess_id IS NULL)
+    OR (warpRun.state = 'update'
+        AND warpSkyfile.data_state = 'cleaned')
+    )
+    AND fakeRun.state = 'full'
+    AND camRun.state = 'full'
+    AND chipRun.state = 'full'
+    AND warpMask.label IS NULL
+    AND warpSkyCellMap.fault = 0
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_updateskyfile.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_updateskyfile.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_updateskyfile.sql	(revision 22322)
@@ -0,0 +1,1 @@
+UPDATE warpSkyfile SET fault = %d WHERE warp_id = %lld AND skycell_id = '%s'
Index: /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_warped.sql
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_warped.sql	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/share/warptool_warped.sql	(revision 22322)
@@ -0,0 +1,20 @@
+SELECT
+    warpSkyfile.*,
+    warpRun.state,
+    rawExp.exp_id,
+    rawExp.camera
+FROM warpRun
+JOIN warpSkyfile
+    USING(warp_id)
+JOIN fakeRun
+    ON warpRun.fake_id = fakeRun.fake_id
+JOIN camRun
+    ON camRun.cam_id   = fakeRun.cam_id
+JOIN chipRun
+    ON camRun.chip_id  = chipRun.chip_id
+JOIN rawExp
+    ON chipRun.exp_id  = rawExp.exp_id
+WHERE
+-- bogus test; just here so there there is a 'WHERE' stmt to append conditionals too
+-- XXX EAM : not needed: fix warptool.c
+    warpRun.warp_id is NOT NULL
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/.cvsignore	(revision 22322)
@@ -0,0 +1,32 @@
+.deps
+.gdb_history
+.libs
+Makefile
+Makefile.in
+config.h
+config.h.in
+stamp-h1
+*.la
+*.lo
+pxtoolsErrorCodes.c
+pxtoolsErrorCodes.h
+pxadmin
+pxinject
+pztool
+pzgetexp
+pzgetimfiles
+regtool
+guidetool
+chiptool
+camtool
+warptool
+difftool
+stacktool
+faketool
+dettool
+detselect
+pxdata.c
+magictool
+caltool
+flatcorr
+pstamptool
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/Makefile.am	(revision 22322)
@@ -0,0 +1,220 @@
+bin_PROGRAMS = \
+	caltool \
+	camtool \
+	chiptool \
+	detselect \
+	dettool \
+	difftool \
+	flatcorr \
+	magictool \
+	pstamptool \
+	pxadmin \
+	pxinject \
+	faketool \
+	pzgetexp \
+	pzgetimfiles \
+	pztool \
+	regtool \
+	stacktool \
+	warptool
+
+
+bin_SCRIPTS = \
+	fakemagic
+
+pkginclude_HEADERS = \
+	pxadmin.h \
+	pxcam.h \
+	pxchip.h \
+	pxconfig.h \
+	pxdata.h \
+	pxfake.h \
+	pxregister.h \
+	pxtag.h \
+	pxtools.h \
+	pxtoolsErrorCodes.h \
+	pxtree.h \
+	pxwarp.h
+
+noinst_HEADERS = \
+	caltool.h \
+	camtool.h \
+	chiptool.h \
+	detselect.h \
+	dettool.h \
+	difftool.h \
+	flatcorr.h \
+	faketool.h \
+	magictool.h \
+	pstamptool.h \
+	pxinject.h \
+	pzgetexp.h \
+	pzgetimfiles.h \
+	pztool.h \
+	regtool.h \
+	stacktool.h \
+	warptool.h
+
+lib_LTLIBRARIES = libpxtools.la
+libpxtools_la_CFLAGS	= $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+libpxtools_la_LIBS      = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS)
+libpxtools_la_LDFLAGS   = -release $(PACKAGE_VERSION)
+libpxtools_la_SOURCES   = \
+	pxcam.c \
+	pxchip.c \
+	pxconfig.c \
+	pxdata.c \
+	pxerrors.c \
+	pxfake.c \
+	pxfault.c \
+	pxregister.c \
+	pxtag.c \
+	pxtools.c \
+	pxtoolsErrorCodes.c \
+	pxtree.c \
+	pxwarp.c
+
+# for pxtools.h
+AM_CPPFLAGS = -I$(top_srcdir)/src$
+
+pztool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS) 
+pztool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+pztool_SOURCES = \
+    pztool.c \
+    pztoolConfig.c
+
+pstamptool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS) 
+pstamptool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+pstamptool_SOURCES = \
+    pstamptool.c \
+    pstamptoolConfig.c
+
+caltool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+caltool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+caltool_SOURCES = \
+    caltool.c \
+    caltoolConfig.c
+
+flatcorr_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+flatcorr_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+flatcorr_SOURCES = \
+    flatcorr.c \
+    flatcorrConfig.c
+
+regtool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+regtool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+regtool_SOURCES = \
+    regtool.c \
+    regtoolConfig.c
+
+chiptool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+chiptool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+chiptool_SOURCES = \
+    chiptool.c \
+    chiptoolConfig.c
+
+camtool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+camtool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+camtool_SOURCES = \
+    camtool.c \
+    camtoolConfig.c
+
+faketool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+faketool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+faketool_SOURCES = \
+    faketool.c \
+    faketoolConfig.c
+
+magictool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+magictool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+magictool_SOURCES = \
+    magictool.c \
+    magictoolConfig.c
+
+warptool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+warptool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+warptool_SOURCES = \
+    warptool.c \
+    warptoolConfig.c
+
+difftool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+difftool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+difftool_SOURCES = \
+    difftool.c \
+    difftoolConfig.c
+
+stacktool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+stacktool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+stacktool_SOURCES = \
+    stacktool.c \
+    stacktoolConfig.c
+
+pxadmin_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+pxadmin_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+pxadmin_SOURCES = \
+    pxadmin.c \
+    pxadminConfig.c
+
+pzgetexp_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+pzgetexp_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+pzgetexp_SOURCES = \
+    pzgetexp.c \
+    pzgetexpConfig.c
+
+pzgetimfiles_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+pzgetimfiles_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+pzgetimfiles_SOURCES = \
+    pzgetimfiles.c \
+    pzgetimfilesConfig.c
+
+dettool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+dettool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+dettool_SOURCES = \
+    dettool.c \
+    dettoolConfig.c \
+    dettool_correction.c \
+    dettool_detrunsummary.c \
+    dettool_normalizedexp.c \
+    dettool_normalizedimfile.c \
+    dettool_normalizedstat.c \
+    dettool_processedexp.c \
+    dettool_processedimfile.c \
+    dettool_residexp.c \
+    dettool_residimfile.c \
+    dettool_stack.c
+
+detselect_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+detselect_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+detselect_SOURCES = \
+    detselect.c \
+    detselectConfig.c
+
+pxinject_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+pxinject_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+pxinject_SOURCES = \
+    pxinject.c \
+    pxinjectConfig.c
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
+# Error codes.
+BUILT_SOURCES = pxtoolsErrorCodes.h pxtoolsErrorCodes.c
+CLEANFILES = pxtoolsErrorCodes.h pxtoolsErrorCodes.c
+EXTRA_DIST = pxtoolsErrorCodes.dat pxtoolsErrorCodes.c.in pxtoolsErrorCodes.h.in fakemagic
+
+pxtoolsErrorCodes.h : pxtoolsErrorCodes.dat pxtoolsErrorCodes.h.in
+	$(ERRORCODES) --data=pxtoolsErrorCodes.dat --outdir=. pxtoolsErrorCodes.h
+
+pxtoolsErrorCodes.c : pxtoolsErrorCodes.dat pxtoolsErrorCodes.c.in pxtoolsErrorCodes.h
+	$(ERRORCODES) --data=pxtoolsErrorCodes.dat --outdir=. pxtoolsErrorCodes.c
+
+BUILT_SOURCES += pxdata.c
+EXTRA_DIST += pxdata.c.template
+
+pxdata.c: $(srcdir)/pxdata.c.template $(srcdir)/Makefile
+	$(PERL) -pe 's|\@PKGDATADIR\@|$(pkgdatadir)|' $< > $@
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/caltool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/caltool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/caltool.c	(revision 22322)
@@ -0,0 +1,261 @@
+/*
+ * caltool.c
+ *
+ * Copyright (C) 2006-2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <ippdb.h>
+
+#include "pxtools.h"
+#include "caltool.h"
+
+static bool adddbMode(pxConfig *config);
+static bool dbsMode(pxConfig *config);
+static bool addrunMode(pxConfig *config);
+static bool runsMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = caltoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(CALTOOL_MODE_ADDDB,        adddbMode);
+        MODECASE(CALTOOL_MODE_DBS,          dbsMode);
+        MODECASE(CALTOOL_MODE_ADDRUN,       addrunMode);
+        MODECASE(CALTOOL_MODE_RUNS,         runsMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+static bool adddbMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required 
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb", true, false);
+
+    if (!calDBInsert(config->dbh,
+            0,       // cal_id
+            dvodb,
+            "active"    // state
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+
+    }
+
+    return false;
+}
+
+
+static bool dbsMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM calDB");
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("caltool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "calDB", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_STR(cal_id, config->args, "-cal_id", true, false);
+    PXOPT_LOOKUP_STR(region, config->args, "-region", true, false);
+    PXOPT_LOOKUP_STR(last_step, config->args, "-last_step", true, false);
+    PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
+
+    if (!calRunInsert(config->dbh,
+            (psS64)atoll(cal_id),
+            region,
+            last_step,
+            state
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+
+    }
+
+    return false;
+}
+
+
+static bool runsMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where,  "-cal_id",     "cal_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM calRun");
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "WHERE fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "WHERE fault = 0");
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " AND %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("caltool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "calRun", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/caltool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/caltool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/caltool.h	(revision 22322)
@@ -0,0 +1,35 @@
+/*
+ * caltool.h
+ *
+ * Copyright (C) 2006-2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef CALTOOL_H
+#define CALTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    CALTOOL_MODE_NONE           = PXTOOL_MODE_NONE,
+    CALTOOL_MODE_DBS,
+    CALTOOL_MODE_ADDDB,
+    CALTOOL_MODE_ADDRUN,
+    CALTOOL_MODE_RUNS
+} caltoolMode;
+
+pxConfig *caltoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // CALTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/caltoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/caltoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/caltoolConfig.c	(revision 22322)
@@ -0,0 +1,104 @@
+/*
+ * caltoolConfig.c
+ *
+ * Copyright (C) 2006-2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "caltool.h"
+
+pxConfig *caltoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI);
+    psString now = psTimeToISO(time);
+    psFree(time);
+
+    // -adddb
+    psMetadata *adddbArgs = psMetadataAlloc();
+    psMetadataAddStr(adddbArgs, PS_LIST_TAIL, "-dvodb",  0,            "define DVO db (required)", NULL);
+
+    // -dbs
+    psMetadata *dbsArgs = psMetadataAlloc();
+    psMetadataAddU64(dbsArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(dbsArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -addrun
+    psMetadata *addrunArgs = psMetadataAlloc();
+    psMetadataAddStr(addrunArgs, PS_LIST_TAIL, "-cal_id",  0,            "define calibration DB ID (required)", NULL);
+    psMetadataAddStr(addrunArgs, PS_LIST_TAIL, "-region",  0,            "define region (required)", NULL);
+    psMetadataAddStr(addrunArgs, PS_LIST_TAIL, "-last_step",  0,            "define last step (required)", NULL);
+    psMetadataAddStr(addrunArgs, PS_LIST_TAIL, "-state",  0,            "define state (required)", NULL);
+
+    // -runs
+    psMetadata *runsArgs = psMetadataAlloc();
+    psMetadataAddStr(runsArgs, PS_LIST_TAIL, "-cal_id",  0,            "search for calibration ID", NULL);
+    psMetadataAddU64(runsArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(runsArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(runsArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    psFree(now);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-adddb",   "add a DVO calibration DB",        CALTOOL_MODE_ADDDB,     adddbArgs);
+    PXOPT_ADD_MODE("-dbs",     "list DVO calibration DBs",        CALTOOL_MODE_DBS,       dbsArgs);
+    PXOPT_ADD_MODE("-addrun",  "add the results of a calibration run",        CALTOOL_MODE_ADDRUN,    addrunArgs);
+    PXOPT_ADD_MODE("-runs",    "list the results of calibration runs",        CALTOOL_MODE_RUNS,      runsArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/camtool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/camtool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/camtool.c	(revision 22322)
@@ -0,0 +1,1001 @@
+/*
+ * camtool.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "pxtools.h"
+#include "pxcam.h"
+#include "camtool.h"
+
+static bool definebyqueryMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool pendingexpMode(pxConfig *config);
+static bool pendingimfileMode(pxConfig *config);
+static bool addprocessedexpMode(pxConfig *config);
+static bool processedexpMode(pxConfig *config);
+static bool revertprocessedexpMode(pxConfig *config);
+static bool updateprocessedexpMode(pxConfig *config);
+static bool blockMode(pxConfig *config);
+static bool maskedMode(pxConfig *config);
+static bool unblockMode(pxConfig *config);
+static bool pendingcleanuprunMode(pxConfig *config);
+static bool pendingcleanupexpMode(pxConfig *config);
+static bool donecleanupMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = camtoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(CAMTOOL_MODE_DEFINEBYQUERY,        definebyqueryMode);
+        MODECASE(CAMTOOL_MODE_UPDATERUN,            updaterunMode);
+        MODECASE(CAMTOOL_MODE_PENDINGEXP,           pendingexpMode);
+        MODECASE(CAMTOOL_MODE_PENDINGIMFILE,        pendingimfileMode);
+        MODECASE(CAMTOOL_MODE_ADDPROCESSEDEXP,      addprocessedexpMode);
+        MODECASE(CAMTOOL_MODE_PROCESSEDEXP,         processedexpMode);
+        MODECASE(CAMTOOL_MODE_REVERTPROCESSEDEXP,   revertprocessedexpMode);
+        MODECASE(CAMTOOL_MODE_UPDATEPROCESSEDEXP,   updateprocessedexpMode);
+        MODECASE(CAMTOOL_MODE_BLOCK,                blockMode);
+        MODECASE(CAMTOOL_MODE_MASKED,               maskedMode);
+        MODECASE(CAMTOOL_MODE_UNBLOCK,              unblockMode);
+        MODECASE(CAMTOOL_MODE_PENDINGCLEANUPRUN,    pendingcleanuprunMode);
+        MODECASE(CAMTOOL_MODE_PENDINGCLEANUPEXP,    pendingcleanupexpMode);
+        MODECASE(CAMTOOL_MODE_DONECLEANUP,          donecleanupMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *where = psMetadataAlloc();
+    pxcamGetSearchArgs (config, where);
+    PXOPT_COPY_STR(config->args, where, "-label", "chipRun.label", "==");
+
+    if (!psListLength(where->list)
+        && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(expgroup, config->args, "-set_expgroup", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb", false, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-set_tess_id", false, false);
+    PXOPT_LOOKUP_STR(end_stage, config->args, "-set_end_stage", false, false);
+
+    // find the exp_id of all the exposures that we want to queue up.
+    psString query = pxDataGet("camtool_find_chip_id.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, "%s", whereClause);
+        psFree(whereClause);
+    }
+
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // start a transaction so we don't end up with an exp without any associted
+    // imfiles
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+
+    // would could do this "all in the database" if we didn't want the option
+    // of changing the label/reduction/expgroup/dvodb/etc.  So we're pulling the
+    // data out so we have the option of changing these values or leaving the
+    // old values in place (i.e., passing the values through).
+
+    // loop over our list of chipRun rows
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+
+        chipRunRow *row = chipRunObjectFromMetadata(md);
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into chipRun");
+            psFree(output);
+            return false;
+        }
+
+        // queue the exp
+        if (!pxcamQueueByChipID(config,
+                    row->chip_id,
+                    workdir     ? workdir   : row->workdir,
+                    label       ? label     : row->label,
+                    reduction   ? reduction : row->reduction,
+                    expgroup    ? expgroup  : row->expgroup,
+                    dvodb       ? dvodb     : row->dvodb,
+                    tess_id     ? tess_id   : row->tess_id,
+                    end_stage   ? end_stage : row->end_stage
+        )) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to trying to queue chip_id: %" PRId64, row->chip_id);
+            psFree(row);
+            psFree(output);
+            return false;
+        }
+        psFree(row);
+    }
+    psFree(output);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-cam_id", "cam_id", "==");
+    pxcamGetSearchArgs (config, where);
+    PXOPT_COPY_STR(config->args, where, "-label", "camRun.label", "==");
+
+    if (!psListLength(where->list)
+        && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+
+    if ((!state) && (!label)) {
+        psError(PXTOOLS_ERR_DATA, false, "parameters are required");
+        psFree(where);
+        return false;
+    }
+
+    if (state) {
+        // set chipRun.state to state
+        if (!pxcamRunSetStateByQuery(config, where, state)) {
+            psFree(where);
+            return false;
+        }
+    }
+
+    if (label) {
+        // set chipRun.label to label
+        if (!pxcamRunSetLabelByQuery(config, where, label)) {
+            psFree(where);
+            return false;
+        }
+    }
+
+    psFree(where);
+
+    return true;
+}
+
+
+static bool pendingexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    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_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("camtool_find_pendingexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, "%s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "camPendingExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool pendingimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    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_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("camtool_find_pendingimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, "%s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipProcessedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool addprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(cam_id, config->args, "-cam_id", true, false);
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", true, false);
+
+    // optional
+    PXOPT_LOOKUP_F32(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F32(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F32(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F32(bias, config->args,           "-bias", false, false);
+    PXOPT_LOOKUP_F32(bias_stdev, config->args,     "-bias_stdev", false, false);
+    PXOPT_LOOKUP_F32(fringe_0, config->args,       "-fringe_0", false, false);
+    PXOPT_LOOKUP_F32(fringe_1, config->args,       "-fringe_1", false, false);
+    PXOPT_LOOKUP_F32(fringe_2, config->args,       "-fringe_2", false, false);
+    PXOPT_LOOKUP_F32(sigma_ra, config->args, "-sigma_ra", false, false);
+    PXOPT_LOOKUP_F32(sigma_dec, config->args, "-sigma_dec", false, false);
+    PXOPT_LOOKUP_F32(ap_resid, config->args,       "-ap_resid", false, false);
+    PXOPT_LOOKUP_F32(ap_resid_stdev, config->args, "-ap_resid_stdev", false, false);
+    PXOPT_LOOKUP_F32(zp_mean, config->args, "-zp_mean", false, false);
+    PXOPT_LOOKUP_F32(zp_stdev, config->args, "-zp_stdev", false, false);
+    PXOPT_LOOKUP_F32(fwhm_major, config->args, "-fwhm_major", false, false);
+    PXOPT_LOOKUP_F32(fwhm_minor, config->args, "-fwhm_minor", false, false);
+    PXOPT_LOOKUP_F32(dtime_detrend, config->args, "-dtime_detrend", false, false);
+    PXOPT_LOOKUP_F32(dtime_photom, config->args, "-dtime_photom", false, false);
+    PXOPT_LOOKUP_F32(dtime_astrom, config->args, "-dtime_astrom", false, false);
+    PXOPT_LOOKUP_STR(hostname, config->args,       "-hostname", false, false);
+    PXOPT_LOOKUP_S32(n_stars, config->args,        "-n_stars", false, false);
+    PXOPT_LOOKUP_S32(n_extended, config->args,     "-n_extended", false, false);
+    PXOPT_LOOKUP_S32(n_cr, config->args,           "-n_cr", false, false);
+    PXOPT_LOOKUP_S32(n_astrom, config->args,       "-n_astrom", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // generate restrictions
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-cam_id",   "cam_id",   "==");
+
+    psString query = pxDataGet("camtool_find_pendingexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+    if (psListLength(where->list)) {
+        psString whereClaus = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, "%s", whereClaus);
+        psFree(whereClaus);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+
+    camRunRow *pendingRow = camRunObjectFromMetadata(output->data[0]);
+    psFree(output);
+    camProcessedExpRow *row = camProcessedExpRowAlloc(
+            pendingRow->cam_id,
+            uri,
+            bg,
+            bg_stdev,
+            bg_mean_stdev,
+            bias,
+            bias_stdev,
+            fringe_0,
+            fringe_1,
+            fringe_2,
+            sigma_ra,
+            sigma_dec,
+            ap_resid,
+            ap_resid_stdev,
+            zp_mean,
+            zp_stdev,
+            fwhm_major,
+            fwhm_minor,
+            dtime_detrend,
+            dtime_photom,
+            dtime_astrom,
+            hostname,
+            n_stars,
+            n_extended,
+            n_cr,
+            n_astrom,
+            path_base,
+            code
+        );
+
+    if (!camProcessedExpInsertObject(config->dbh, row)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(row);
+        psFree(pendingRow);
+        return false;
+    }
+
+    // since there is only one exp per 'new' set camRun.state = 'full'
+    if (!pxcamRunSetState(config, row->cam_id, "full")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to change camRun.state for cam_id: %" PRId64, row->cam_id);
+        psFree(row);
+        psFree(pendingRow);
+        return false;
+    }
+
+    // NULL for end_stage means go as far as possible
+    // EAM : skip here if code != 0
+    // Also, we can run fake even if tess_id is not defined
+    if (code || (pendingRow->end_stage && psStrcasestr(pendingRow->end_stage, "cam"))) {
+        psFree(row);
+        psFree(pendingRow);
+        if (!psDBCommit(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+        return true;
+    }
+    psFree(row);
+    // else continue on...
+
+    if (!pxfakeQueueByCamID(config,
+            pendingRow->cam_id,
+            pendingRow->workdir,
+            pendingRow->label,
+            pendingRow->reduction,
+            pendingRow->expgroup,
+            pendingRow->dvodb,
+            pendingRow->tess_id,
+            pendingRow->end_stage
+    )) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "failed to queue new fakeRun");
+        psFree(pendingRow);
+        return false;
+    }
+    psFree(pendingRow);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool processedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    // 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", "==");
+
+    psString query = pxDataGet("camtool_find_processedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // use psDBGenerateWhereConditionalSQL with AND ... because the SQL ends in a WHERE
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND camProcessedExp.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND camProcessedExp.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "camProcessedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    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_S16(config->args, where, "-code", "camProcessedExp.fault", "==");
+
+    if (!psListLength(where->list) && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(where);
+        return false;
+    }
+
+    {
+        psString query = pxDataGet("camtool_reset_faulted_runs.sql");
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            psFree(where);
+            return false;
+        }
+
+        // use psDBGenerateWhereConditionalSQL with AND ... because the SQL ends in a WHERE
+        if (where && psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            psFree(where);
+            return false;
+        }
+        psFree(query);
+    }
+
+    {
+        psString query = pxDataGet("camtool_revertprocessedexp.sql");
+        if (!query) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            psFree(where);
+            return false;
+        }
+
+        // use psDBGenerateWhereConditionalSQL with AND ... because the SQL ends in a WHERE
+        if (where && psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            psFree(where);
+            return false;
+        }
+        psFree(query);
+    }
+    psFree(where);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool updateprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", true, false);
+
+    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", "==");
+
+    if (!pxSetFaultCode(config->dbh, "camProcessedExp", where, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        psFree (where);
+        return false;
+    }
+    psFree (where);
+
+    return true;
+}
+
+
+static bool blockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    if (!camMaskInsert(config->dbh, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool maskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM camMask");
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "camMask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool unblockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    char *query = "DELETE FROM camMask WHERE label = '%s'";
+
+    if (!p_psDBRunQuery(config->dbh, query, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool pendingcleanuprunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("camtool_pendingcleanuprun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "camPendingCleanupRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool pendingcleanupexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(cam_id, config->args, "-cam_id", false, false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    if (cam_id) {
+        PXOPT_COPY_S64(config->args, where, "-cam_id", "cam_id", "==");
+    }
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("camtool_pendingcleanupexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "camPendingCleanupExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool donecleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("camtool_donecleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "camDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/camtool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/camtool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/camtool.h	(revision 22322)
@@ -0,0 +1,45 @@
+/*
+ * camtool.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef CAMTOOL_H
+#define CAMTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    CAMTOOL_MODE_NONE      = 0x0,
+    CAMTOOL_MODE_DEFINEBYQUERY,
+    CAMTOOL_MODE_UPDATERUN,
+    CAMTOOL_MODE_PENDINGEXP,
+    CAMTOOL_MODE_PENDINGIMFILE,
+    CAMTOOL_MODE_ADDPROCESSEDEXP,
+    CAMTOOL_MODE_PROCESSEDEXP,
+    CAMTOOL_MODE_REVERTPROCESSEDEXP,
+    CAMTOOL_MODE_UPDATEPROCESSEDEXP,
+    CAMTOOL_MODE_BLOCK,
+    CAMTOOL_MODE_MASKED,
+    CAMTOOL_MODE_UNBLOCK,
+    CAMTOOL_MODE_PENDINGCLEANUPRUN,
+    CAMTOOL_MODE_PENDINGCLEANUPEXP,
+    CAMTOOL_MODE_DONECLEANUP,
+} camtoolMode;
+
+pxConfig *camtoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // CAMTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/camtoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/camtoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/camtoolConfig.c	(revision 22322)
@@ -0,0 +1,231 @@
+/*
+ * camtoolConfig.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <stdint.h>
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "pxcam.h"
+#include "camtool.h"
+
+pxConfig *camtoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    // -definebyquery
+    // XXX need to allow multiple chip_ids
+    // XXX need to allow multiple exp_ids
+    psMetadata *definebyqueryArgs = psMetadataAlloc();
+    pxcamSetSearchArgs(definebyqueryArgs);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label", 0, "search for label", NULL);
+
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_workdir",        0, "define workdir", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_label",          0, "define label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_reduction",      0, "define reduction class", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_expgroup",       0, "define exposure group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_dvodb",          0, "define DVO db", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_tess_id",        0, "define tess ID", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_end_stage",      0, "define end stage", NULL);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-all",               0, "allow everything to be queued without search terms", false);
+
+    // -updaterun
+    // XXX need to allow multiple cam_ids
+    // XXX need to allow multiple chip_ids
+    // XXX need to allow multiple exp_ids
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    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);
+
+    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);
+
+    // -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);
+    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);
+
+    // -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);
+    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);
+
+    // -addprocessedexp
+    psMetadata *addprocessedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(addprocessedexpArgs, PS_LIST_TAIL, "-cam_id", 0,            "define camtool ID (required)", 0);
+    psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-uri", 0,            "define URI (required)", NULL);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-bg", 0,            "define exposure background", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-bg_stdev", 0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-bg_mean_stdev", 0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-bias",  0,            "define bias", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-bias_stdev",  0,            "define bias stdev", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-fringe_0",  0,            "define fringe term 0", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-fringe_1",  0,            "define fringe term 1", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-fringe_2",  0,            "define fringe term 2", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-sigma_ra", 0,            "define exposure E ra", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-sigma_dec", 0,            "define exposure E dec", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-ap_resid",  0,            "define aperture residual", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-ap_resid_stdev",  0,            "define aperture residual stdev", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-zp_mean", 0,            "define zero point mean", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-zp_stdev", 0,            "define zero point stdev", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-fwhm_major", 0,            "define FWHM (major axis; arcsec)", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-fwhm_minor", 0,            "define FWHM (minor axis; arcsec)", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-dtime_detrend", 0,            "define elapsed detrend processing time (seconds)", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-dtime_photom", 0,            "define elapsed photometry processing time (seconds)", NAN);
+    psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-dtime_astrom", 0,            "define elapsed astrometry processing time (seconds)", NAN);
+    psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-hostname", 0,            "define hostname", NULL);
+    psMetadataAddS32(addprocessedexpArgs, PS_LIST_TAIL, "-n_stars", 0,            "define number of stars", 0);
+    psMetadataAddS32(addprocessedexpArgs, PS_LIST_TAIL, "-n_extended", 0,            "define number of extended objects", 0);
+    psMetadataAddS32(addprocessedexpArgs, PS_LIST_TAIL, "-n_cr", 0,            "define number of cosmic rays", 0);
+    psMetadataAddS32(addprocessedexpArgs, PS_LIST_TAIL, "-n_astrom", 0,            "define number of astrometry reference objects", 0);
+    psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-path_base", 0,            "define base output location", NULL);
+    psMetadataAddS16(addprocessedexpArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+    psMetadataAddBool(addprocessedexpArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+
+    // -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);
+
+    psMetadataAddU64(processedexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(processedexpArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+    psMetadataAddBool(processedexpArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+
+    // -revertprocessedexp
+    // XXX need to allow multiple cam_ids
+    // XXX need to allow multiple chip_ids
+    // 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);
+
+    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
+    // XXX allow full search options?
+    psMetadata *updateprocessedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(updateprocessedexpArgs, PS_LIST_TAIL, "-cam_id", 0,            "search by camtool ID", 0);
+    psMetadataAddS64(updateprocessedexpArgs, PS_LIST_TAIL, "-chip_id",  0,            "search by chiptool ID", 0);
+    psMetadataAddStr(updateprocessedexpArgs, PS_LIST_TAIL, "-class",  0,            "search by class", NULL);
+    psMetadataAddStr(updateprocessedexpArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+    psMetadataAddS16(updateprocessedexpArgs, PS_LIST_TAIL, "-code",  0,            "set fault code (required)", INT16_MAX);
+
+    // -block
+    psMetadata *blockArgs = psMetadataAlloc();
+    psMetadataAddStr(blockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to mask out (required)", NULL);
+
+    // -masked
+    psMetadata *maskedArgs = psMetadataAlloc();
+    psMetadataAddBool(maskedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -unblock
+    psMetadata *unblockArgs = psMetadataAlloc();
+    psMetadataAddStr(unblockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to unmask (required)", NULL);
+
+    // -pendingcleanuprun
+    // XXX allow full search options?
+    psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanuprunArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(pendingcleanuprunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanuprunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -pendingcleanupexp
+    // XXX allow full search options?
+    psMetadata *pendingcleanupexpArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanupexpArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddS64(pendingcleanupexpArgs, PS_LIST_TAIL, "-cam_id", 0,            "search by camera ID", 0);
+    psMetadataAddStr(pendingcleanupexpArgs, PS_LIST_TAIL, "-exp_id",                 0,            "search by exp_id", NULL);
+    psMetadataAddBool(pendingcleanupexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanupexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -donecleanup
+    psMetadata *donecleanupArgs = psMetadataAlloc();
+    psMetadataAddStr(donecleanupArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(donecleanupArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanupArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definebyquery",        "create runs from chip stage",          CAMTOOL_MODE_DEFINEBYQUERY, definebyqueryArgs);
+    PXOPT_ADD_MODE("-updaterun",            "change cam run properties",            CAMTOOL_MODE_UPDATERUN,      updaterunArgs);
+    PXOPT_ADD_MODE("-pendingexp",           "show pending exposures",               CAMTOOL_MODE_PENDINGEXP,    pendingexpArgs);
+    PXOPT_ADD_MODE("-pendingimfile",        "show pending imfiles",                 CAMTOOL_MODE_PENDINGIMFILE, pendingimfileArgs);
+    PXOPT_ADD_MODE("-addprocessedexp",      "add a processed exposure",             CAMTOOL_MODE_ADDPROCESSEDEXP, addprocessedexpArgs);
+    PXOPT_ADD_MODE("-processedexp",         "show processed exposures",             CAMTOOL_MODE_PROCESSEDEXP,  processedexpArgs);
+    PXOPT_ADD_MODE("-revertprocessedexp",   "change procesed exp properties",       CAMTOOL_MODE_REVERTPROCESSEDEXP,  revertprocessedexpArgs);
+    PXOPT_ADD_MODE("-updateprocessedexp",   "undo a processed exposure",            CAMTOOL_MODE_UPDATEPROCESSEDEXP,updateprocessedexpArgs);
+    PXOPT_ADD_MODE("-block",                "set a label block",                    CAMTOOL_MODE_BLOCK,         blockArgs);
+    PXOPT_ADD_MODE("-masked",               "show blocked labels",                  CAMTOOL_MODE_MASKED,        maskedArgs);
+    PXOPT_ADD_MODE("-unblock",              "remove a label block",                 CAMTOOL_MODE_UNBLOCK,       unblockArgs);
+    PXOPT_ADD_MODE("-pendingcleanuprun",    "show runs that need to be cleaned up", CAMTOOL_MODE_PENDINGCLEANUPRUN, pendingcleanuprunArgs);
+    PXOPT_ADD_MODE("-pendingcleanupexp",    "show exposures for cleanup runs",      CAMTOOL_MODE_PENDINGCLEANUPEXP, pendingcleanupexpArgs);
+    PXOPT_ADD_MODE("-donecleanup",          "show runs that have been cleaned",     CAMTOOL_MODE_DONECLEANUP,       donecleanupArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, false, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/chiptool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/chiptool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/chiptool.c	(revision 22322)
@@ -0,0 +1,1147 @@
+/*
+ * chiptool.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "pxtools.h"
+#include "pxdata.h"
+#include "pxchip.h"
+
+#include "chiptool.h"
+#include "camtool.h"
+
+static bool definebyqueryMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool pendingimfileMode(pxConfig *config);
+static bool addprocessedimfileMode(pxConfig *config);
+static bool processedimfileMode(pxConfig *config);
+static bool revertprocessedimfileMode(pxConfig *config);
+static bool updateprocessedimfileMode(pxConfig *config);
+static bool blockMode(pxConfig *config);
+static bool maskedMode(pxConfig *config);
+static bool unmaskedMode(pxConfig *config);
+static bool unblockMode(pxConfig *config);
+static bool pendingcleanuprunMode(pxConfig *config);
+static bool pendingcleanupimfileMode(pxConfig *config);
+static bool donecleanupMode(pxConfig *config);
+static bool runMode(pxConfig *config);
+static bool tocleanedimfileMode(pxConfig *config);
+static bool tofullimfileMode(pxConfig *config);
+static bool topurgedimfileMode(pxConfig *config);
+
+static bool chipProcessedCompleteExp(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv) {
+    psLibInit(NULL);
+
+    pxConfig *config = chiptoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(CHIPTOOL_MODE_DEFINEBYQUERY,           definebyqueryMode);
+        MODECASE(CHIPTOOL_MODE_UPDATERUN,               updaterunMode);
+        MODECASE(CHIPTOOL_MODE_PENDINGIMFILE,           pendingimfileMode);
+        MODECASE(CHIPTOOL_MODE_ADDPROCESSEDIMFILE,      addprocessedimfileMode);
+        MODECASE(CHIPTOOL_MODE_PROCESSEDIMFILE,         processedimfileMode);
+        MODECASE(CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,   revertprocessedimfileMode);
+        MODECASE(CHIPTOOL_MODE_UPDATEPROCESSEDIMFILE,   updateprocessedimfileMode);
+        MODECASE(CHIPTOOL_MODE_BLOCK,                   blockMode);
+        MODECASE(CHIPTOOL_MODE_MASKED,                  maskedMode);
+        MODECASE(CHIPTOOL_MODE_UNMASKED,                unmaskedMode);
+        MODECASE(CHIPTOOL_MODE_UNBLOCK,                 unblockMode);
+        MODECASE(CHIPTOOL_MODE_PENDINGCLEANUPRUN,       pendingcleanuprunMode);
+        MODECASE(CHIPTOOL_MODE_PENDINGCLEANUPIMFILE,    pendingcleanupimfileMode);
+        MODECASE(CHIPTOOL_MODE_DONECLEANUP,             donecleanupMode);
+        MODECASE(CHIPTOOL_MODE_RUN,                     runMode);
+        MODECASE(CHIPTOOL_MODE_TOCLEANEDIMFILE,         tocleanedimfileMode);
+        MODECASE(CHIPTOOL_MODE_TOFULLIMFILE,            tofullimfileMode);
+        MODECASE(CHIPTOOL_MODE_TOPURGEDIMFILE,          topurgedimfileMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); // rawExp only
+    PXOPT_COPY_STR(config->args, where, "-label", "rawExp.label", "LIKE");
+
+    // psListLength(where->list) is at least 1 because exp_type defaults to "object"
+    // so we require a list longer than 1 entry
+    if ((psListLength(where->list) <= 1) && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", true, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(expgroup, config->args, "-set_expgroup", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb", false, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-set_tess_id", false, false);
+    PXOPT_LOOKUP_STR(end_stage, config->args, "-set_end_stage", false, false);
+
+    // default
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find the exp_id of all the exposures that we want to queue up.
+    psString query = pxDataGet("chiptool_find_rawexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+
+    psFree(whereClause);
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "rawExp", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+        psFree(output);
+        return true;
+    }
+
+    // start a transaction so we don't end up with an exp without any associted
+    // imfiles
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+
+    // loop over our list of exp_ids
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+
+        bool status;
+        psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for exp_id");
+            psFree(output);
+            return false;
+        }
+
+        // queue the exp
+        if (!pxchipQueueByExpTag(config, exp_id, workdir, label, reduction, expgroup, dvodb, tess_id, end_stage)) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to trying to queue exp_id: %" PRId64, exp_id);
+            psFree(output);
+            return false;
+        }
+    }
+    psFree(output);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); // rawExp, chipRun
+    PXOPT_COPY_S64(config->args,  where, "-chip_id", "chipRun.chip_id", "==");
+    PXOPT_COPY_STR(config->args,  where, "-label", "chipRun.label", "==");
+
+    if (!psListLength(where->list)
+        && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        where = NULL;
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+
+    if ((!state) && (!label)) {
+        psError(PXTOOLS_ERR_DATA, false, "parameters are required");
+        psFree(where);
+        return false;
+    }
+
+    if (state) {
+        // set chipRun.state to state
+        if (!pxchipRunSetStateByQuery(config, where, state)) {
+            psFree(where);
+            return false;
+        }
+    }
+
+    if (label) {
+        // set chipRun.label to label
+        if (!pxchipRunSetLabelByQuery(config, where, label)) {
+            psFree(where);
+            return false;
+        }
+    }
+
+    psFree(where);
+
+    return true;
+}
+
+
+static bool pendingimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); //chipRun, rawExp
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "chipRun.label", "==");
+
+    psString query = pxDataGet("chiptool_pendingimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipPendingImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // chip_id, exp_tag, class_id are required
+    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id", true, false);
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(uri, config->args,            "-uri", false, false);
+    PXOPT_LOOKUP_F32(bg, config->args,             "-bg", false, false);
+    PXOPT_LOOKUP_F32(bg_stdev, config->args,       "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F32(bg_mean_stdev, config->args,  "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F32(bias, config->args,           "-bias", false, false);
+    PXOPT_LOOKUP_F32(bias_stdev, config->args,     "-bias_stdev", false, false);
+    PXOPT_LOOKUP_F32(fringe_0, config->args,       "-fringe_0", false, false);
+    PXOPT_LOOKUP_F32(fringe_1, config->args,       "-fringe_1", false, false);
+    PXOPT_LOOKUP_F32(fringe_2, config->args,       "-fringe_2", false, false);
+    PXOPT_LOOKUP_F32(sigma_ra, config->args,       "-sigma_ra", false, false);
+    PXOPT_LOOKUP_F32(sigma_dec, config->args,      "-sigma_dec", false, false);
+    PXOPT_LOOKUP_F32(ap_resid, config->args,       "-ap_resid", false, false);
+    PXOPT_LOOKUP_F32(ap_resid_stdev, config->args, "-ap_resid_stdev", false, false);
+    PXOPT_LOOKUP_F32(zp_mean, config->args,        "-zp_mean", false, false);
+    PXOPT_LOOKUP_F32(zp_stdev, config->args,       "-zp_stdev", false, false);
+    PXOPT_LOOKUP_F32(fwhm_major, config->args,     "-fwhm_major", false, false);
+    PXOPT_LOOKUP_F32(fwhm_minor, config->args,     "-fwhm_minor", false, false);
+    PXOPT_LOOKUP_F32(dtime_detrend, config->args,  "-dtime_detrend", false, false);
+    PXOPT_LOOKUP_F32(dtime_photom,  config->args,  "-dtime_photom",  false, false);
+    PXOPT_LOOKUP_F32(dtime_astrom,  config->args,  "-dtime_astrom",  false, false);
+    PXOPT_LOOKUP_STR(hostname, config->args,       "-hostname", false, false);
+    PXOPT_LOOKUP_F32(n_stars, config->args, "-n_stars", false, false);
+    PXOPT_LOOKUP_F32(n_extended, config->args, "-n_extended", false, false);
+    PXOPT_LOOKUP_F32(n_cr, config->args, "-n_cr", false, false);
+    PXOPT_LOOKUP_F32(n_astrom, config->args, "-n_astrom", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!chipProcessedImfileInsert(config->dbh,
+                                   chip_id,
+                                   exp_id,
+                                   class_id,
+                                   "full",
+                                   uri,
+                                   bg,
+                                   bg_stdev,
+                                   bg_mean_stdev,
+                                   bias,
+                                   bias_stdev,
+                                   fringe_0,
+                                   fringe_1,
+                                   fringe_2,
+                                   sigma_ra,
+                                   sigma_dec,
+                                   ap_resid,
+                                   ap_resid_stdev,
+                                   zp_mean,
+                                   zp_stdev,
+                                   fwhm_major,
+                                   fwhm_minor,
+                                   dtime_detrend,
+                                   dtime_photom,
+                                   dtime_astrom,
+                                   hostname,
+                                   n_stars,
+                                   n_extended,
+                                   n_cr,
+                                   n_astrom,
+                                   path_base,
+                                   code
+            )) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // XXX I've decided to make the transaction cover the Exp migration as
+    // well.  Otherwise, if the last imfile in an exp is moved and the exp
+    // migration fails then the data base is left in a situation where the exp
+    // migration can't happen.
+
+    if (!chipProcessedCompleteExp(config)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool processedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); // chipRun, chipProcessedImfile, rawExp
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "chipProcessedImfile.class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "chipRun.reduction", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "chipRun.label", "LIKE");
+
+    psString query = pxDataGet("chiptool_processedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND chipProcessedImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND chipProcessedImfile.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipProcessedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool revertprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); // chipProcessedImfile, rawExp
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "chipProcessedImfile.class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "chipRun.label", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "chipRun.reduction", "==");
+    PXOPT_COPY_S16(config->args, where, "-code", "chipProcessedImfile.fault", "==");
+
+    if (!psListLength(where->list)
+        && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    psString query = pxDataGet("chiptool_revertprocessedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+static bool updateprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+    PXOPT_LOOKUP_S16(code, config->args, "-code", true, false);
+
+    if (!pxSetFaultCode(config->dbh, "chipProcessedImfile", where, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        return false;
+    }
+    psFree(where);
+
+    return true;
+}
+
+
+static bool blockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    if (!chipMaskInsert(config->dbh, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool maskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    if (where->list->n < 1) {
+        psFree(where);
+        where = NULL;
+    }
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM chipMask");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psFree(where);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipMask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// return the list of labels which are NOT blocked
+static bool unmaskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    if (where->list->n < 1) {
+        psFree(where);
+        where = NULL;
+    }
+
+    psString query = pxDataGet("chiptool_unmasked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, "chipUnmask");
+        psFree(where);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipUnmask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool unblockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    char *query = "DELETE FROM chipMask WHERE label = '%s'";
+
+    if (!p_psDBRunQuery(config->dbh, query, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool pendingcleanuprunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("chiptool_pendingcleanuprun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipPendingCleanupRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool pendingcleanupimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id", false, false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    if (chip_id) {
+        PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
+    }
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("chiptool_pendingcleanupimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipPendingCleanupImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool donecleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("chiptool_donecleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool runMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // make sure that the state string is valid
+    if (!pxIsValidState(state)) {
+        psError(PXTOOLS_ERR_DATA, false, "%s is not a valid state", state);
+        return false;
+    }
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
+
+    psString query = pxDataGet("chiptool_run.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool chipProcessedCompleteExp(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // look for completed chipPendingExp
+    // migrate them to chipProccessedExp & camPendingExp
+    psString query = pxDataGet("chiptool_completely_processed_exp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i];
+
+        chipRunRow *chipRun = chipRunObjectFromMetadata(row);
+        // set chipRun.state to 'stop'
+        if (!pxchipRunSetState(config, chipRun->chip_id, "full")) {
+            psError(PS_ERR_UNKNOWN, false, "failed to change chipRun.state for chip_id: %" PRId64, chipRun->chip_id);
+            psFree(chipRun);
+            psFree(output);
+            return false;
+        }
+
+        // should we stop here or proceed on to the cam stage?
+        // NULL for end_stage means go as far as possible
+        if (chipRun->end_stage && psStrcasestr(chipRun->end_stage, "chip")) {
+            psFree(chipRun);
+            continue;
+        }
+        // else continue on...
+
+        // camQueueChipID() can only be run after chipRun.state has been set to
+        // stop
+        if (!pxcamQueueByChipID(config,
+                    chipRun->chip_id,
+                    chipRun->workdir,
+                    chipRun->label,
+                    chipRun->reduction,
+                    chipRun->expgroup,
+                    chipRun->dvodb,
+                    chipRun->tess_id,
+                    chipRun->end_stage
+        )) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to queue camPendingExp");
+            psFree(chipRun);
+            psFree(output);
+            return false;
+        }
+        psFree(chipRun);
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// update chipProcessedImfile.data_state to given value.
+// afterwards, if all imfiles in the exposure have the new state, update the state for the exposure as well
+// shared code for the modes -tocleanedimfile -tofullimfile -topurgedimfile
+
+static bool change_imfile_data_state(pxConfig *config, psString data_state, psString run_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // chip_id, class_id are required
+    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    psString query = pxDataGet("chiptool_change_imfile_data_state.sql");
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // note only updates if chipRun.state = run_state
+    if (!p_psDBRunQuery(config->dbh, query, data_state, chip_id, class_id, run_state)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    query = pxDataGet("chiptool_change_exp_state.sql");
+    if (!p_psDBRunQuery(config->dbh, query, data_state, chip_id, data_state)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+static bool tocleanedimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "cleaned", "goto_cleaned");
+}
+static bool tofullimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "full", "update");
+}
+static bool topurgedimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "purged", "goto_purged");
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/chiptool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/chiptool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/chiptool.h	(revision 22322)
@@ -0,0 +1,50 @@
+/*
+ * chiptool.h
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef CHIPTOOL_H
+#define CHIPTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    CHIPTOOL_MODE_NONE      = 0x0,
+    CHIPTOOL_MODE_DEFINEBYQUERY,
+    CHIPTOOL_MODE_UPDATERUN,
+    CHIPTOOL_MODE_PENDINGIMFILE,
+    CHIPTOOL_MODE_ADDPROCESSEDIMFILE,
+    CHIPTOOL_MODE_PROCESSEDIMFILE,
+    CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,
+    CHIPTOOL_MODE_UPDATEPROCESSEDIMFILE,
+    CHIPTOOL_MODE_BLOCK,
+    CHIPTOOL_MODE_MASKED,
+    CHIPTOOL_MODE_UNMASKED,
+    CHIPTOOL_MODE_UNBLOCK,
+    CHIPTOOL_MODE_RETRYPROCESSEDIMFILE,
+    CHIPTOOL_MODE_PENDINGCLEANUPRUN,
+    CHIPTOOL_MODE_PENDINGCLEANUPIMFILE,
+    CHIPTOOL_MODE_DONECLEANUP,
+    CHIPTOOL_MODE_RUN,
+    CHIPTOOL_MODE_TOCLEANEDIMFILE,
+    CHIPTOOL_MODE_TOFULLIMFILE,
+    CHIPTOOL_MODE_TOPURGEDIMFILE
+} chiptoolMode;
+
+pxConfig *chiptoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // CHIPTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/chiptoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/chiptoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/chiptoolConfig.c	(revision 22322)
@@ -0,0 +1,247 @@
+/*
+ * chiptoolConfig.c
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "chiptool.h"
+
+pxConfig *chiptoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (! config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    // -definebyquery
+    psMetadata *definebyqueryArgs = psMetadataAlloc();
+    pxchipSetSearchArgs (definebyqueryArgs);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label",              0, "search by rawExp label (LIKE comparison)", NULL);
+
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-exp_type",          PS_META_REPLACE, "search by exp_type", "object");
+
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_workdir",  0,            "define workdir (required)", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_label",  0,            "define label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_reduction",  0,            "define reduction class", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_expgroup",  0,            "define exposure group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_dvodb",  0,            "define DVO db", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_tess_id",  0,            "define tessellation identifier", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_end_stage",  0,            "define end stage", NULL);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-pretend",  0,            "do not actually modify the database", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
+
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    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, "-set_state", 0,        "set state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label", 0,        "set label", NULL);
+    psMetadataAddBool(updaterunArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label",  0,          "search by label", NULL);
+
+    // -pendingimfile
+    psMetadata *pendingimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingimfileArgs, PS_LIST_TAIL, "-chip_id",  0,            "search by chip ID", 0);
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-label",  0, "search by label", 0);
+    pxchipSetSearchArgs(pendingimfileArgs);
+    psMetadataAddU64(pendingimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(pendingimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addprocessedimfile
+    psMetadata *addprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addprocessedimfileArgs, PS_LIST_TAIL, "-chip_id",  0,            "define chip ID (required)", 0);
+    psMetadataAddS64(addprocessedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "define exposure ID (required)", 0);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "define class ID (required)", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-uri",  0,            "define URL", NULL);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-bias",  0,            "define bias", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-bias_stdev",  0,            "define bias stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fringe_0",  0,            "define fringe term 0", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fringe_1",  0,            "define fringe term 1", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fringe_2",  0,            "define fringe term 2", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-sigma_ra",  0,            "define error in RA", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-sigma_dec",  0,            "define error in DEC", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-ap_resid",  0,            "define aperture residual", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-ap_resid_stdev",  0,            "define aperture residual stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-zp_mean",  0,            "define zero point", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-zp_stdev",  0,            "define zero point stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fwhm_major",  0,            "define fwhm (major axis; arcsec)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fwhm_minor",  0,            "define fwhm (minor axis; arcsec)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-dtime_detrend",  0,            "define elapsed time for detrend (seconds)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-dtime_photom",  0,            "define elapsed time for photometry (seconds)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-dtime_astrom",  0,            "define elapsed time for astrometry (seconds)", NAN);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-hostname",  0,            "define hostname", NULL);
+    psMetadataAddS32(addprocessedimfileArgs, PS_LIST_TAIL, "-n_stars",  0,            "define number of stars in image", 0);
+    psMetadataAddS32(addprocessedimfileArgs, PS_LIST_TAIL, "-n_extended",  0,            "define number of extended objects in image", 0);
+    psMetadataAddS32(addprocessedimfileArgs, PS_LIST_TAIL, "-n_cr",  0,            "define number of cosmic rays in image", 0);
+    psMetadataAddS32(addprocessedimfileArgs, PS_LIST_TAIL, "-n_astrom",  0,            "define number of astrometry stars used", 0);
+
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+    psMetadataAddS16(addprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+
+    // -processedimfile
+    psMetadata *processedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(processedimfileArgs, PS_LIST_TAIL, "-chip_id",  0,         "define chip ID", 0);
+    psMetadataAddStr(processedimfileArgs,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
+    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);
+    pxchipSetSearchArgs(processedimfileArgs);
+    psMetadataAddU64(processedimfileArgs, PS_LIST_TAIL, "-limit",  0,           "limit result set to N items", 0);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-faulted",  0,        "only return imfiles with a fault status set", false);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-simple",  0,         "use the simple output format", false);
+
+    // -revertprocessedimfile
+    psMetadata *revertprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(revertprocessedimfileArgs, PS_LIST_TAIL, "-chip_id", 0,            "search by chip ID", 0);
+    psMetadataAddStr(revertprocessedimfileArgs,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
+    psMetadataAddStr(revertprocessedimfileArgs,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
+    psMetadataAddStr(revertprocessedimfileArgs,  PS_LIST_TAIL, "-label",              0, "search by chipRun label (LIKE comparison)", NULL);
+    pxchipSetSearchArgs(revertprocessedimfileArgs);
+    psMetadataAddBool(revertprocessedimfileArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
+    psMetadataAddS16(revertprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -updateprocessedimfile
+    psMetadata *updateprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(updateprocessedimfileArgs, PS_LIST_TAIL, "-chip_id",  0,            "search by chip ID", 0);
+    psMetadataAddStr(updateprocessedimfileArgs,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
+    psMetadataAddS16(updateprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code (required)", 0);
+
+    // -block
+    psMetadata *blockArgs = psMetadataAlloc();
+    psMetadataAddStr(blockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to mask out (required)", NULL);
+
+    // -masked
+    psMetadata *maskedArgs = psMetadataAlloc();
+    psMetadataAddStr(maskedArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(maskedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(maskedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -unmasked
+    psMetadata *unmaskedArgs = psMetadataAlloc();
+    psMetadataAddStr(unmaskedArgs, PS_LIST_TAIL, "-label",  0,            "restrict to specified label", NULL);
+    psMetadataAddBool(unmaskedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(unmaskedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -unblock
+    psMetadata *unblockArgs = psMetadataAlloc();
+    psMetadataAddStr(unblockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to unmask (required)", NULL);
+
+    // -pendingcleanuprun
+    psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanuprunArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(pendingcleanuprunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanuprunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -pendingcleanupimfile
+    psMetadata *pendingcleanupimfileArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanupimfileArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddS64(pendingcleanupimfileArgs, PS_LIST_TAIL, "-chip_id", 0,          "search by chip ID", 0);
+    psMetadataAddStr(pendingcleanupimfileArgs, PS_LIST_TAIL, "-exp_id",                 0,            "search by exp_id", NULL);
+    psMetadataAddBool(pendingcleanupimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanupimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -donecleanup
+    psMetadata *donecleanupArgs = psMetadataAlloc();
+    psMetadataAddStr(donecleanupArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(donecleanupArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanupArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -run
+    psMetadata *runArgs = psMetadataAlloc();
+    psMetadataAddStr(runArgs, PS_LIST_TAIL, "-label",  0,       "search by label", NULL);
+    psMetadataAddBool(runArgs, PS_LIST_TAIL, "-simple",  0,     "use the simple output format", false);
+    psMetadataAddU64(runArgs, PS_LIST_TAIL, "-limit",  0,       "limit result set to N items", 0);
+    psMetadataAddStr(runArgs, PS_LIST_TAIL, "-state", 0,        "search by state (required)", NULL);
+
+    // -tocleanedimfile
+    psMetadata *tocleanedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tocleanedimfileArgs, PS_LIST_TAIL, "-chip_id", 0,          "chip ID to update", 0);
+    psMetadataAddStr(tocleanedimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    // -tofullimfile
+    psMetadata *tofullimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tofullimfileArgs, PS_LIST_TAIL, "-chip_id", 0,          "chip ID to update", 0);
+    psMetadataAddStr(tofullimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    // -topurgedimfile
+    psMetadata *topurgedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(topurgedimfileArgs, PS_LIST_TAIL, "-chip_id", 0,          "chip ID to update", 0);
+    psMetadataAddStr(topurgedimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definebyquery",        "create runs from raw exposures",       CHIPTOOL_MODE_DEFINEBYQUERY,        definebyqueryArgs);
+    PXOPT_ADD_MODE("-updaterun",            "change chip run properties",           CHIPTOOL_MODE_UPDATERUN,            updaterunArgs);
+    PXOPT_ADD_MODE("-pendingimfile",        "show pending imfiles",                 CHIPTOOL_MODE_PENDINGIMFILE,        pendingimfileArgs);
+    PXOPT_ADD_MODE("-addprocessedimfile",   "add a processed imfile",               CHIPTOOL_MODE_ADDPROCESSEDIMFILE,   addprocessedimfileArgs);
+    PXOPT_ADD_MODE("-processedimfile",      "show processed imfiles",               CHIPTOOL_MODE_PROCESSEDIMFILE,      processedimfileArgs);
+    PXOPT_ADD_MODE("-updateprocessedimfile","change procesed imfile properties",    CHIPTOOL_MODE_UPDATEPROCESSEDIMFILE,updateprocessedimfileArgs);
+    PXOPT_ADD_MODE("-revertprocessedimfile","undo a processed imfile",              CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,revertprocessedimfileArgs);
+    PXOPT_ADD_MODE("-block",                "set a label block",                    CHIPTOOL_MODE_BLOCK,                blockArgs);
+    PXOPT_ADD_MODE("-masked",               "show blocked labels",                  CHIPTOOL_MODE_MASKED,               maskedArgs);
+    PXOPT_ADD_MODE("-unmasked",             "",                                     CHIPTOOL_MODE_UNMASKED,             unmaskedArgs);
+    PXOPT_ADD_MODE("-unblock",              "remove a label block",                 CHIPTOOL_MODE_UNBLOCK,              unblockArgs);
+    PXOPT_ADD_MODE("-pendingcleanuprun",    "show runs that need to be cleaned up", CHIPTOOL_MODE_PENDINGCLEANUPRUN,    pendingcleanuprunArgs);
+    PXOPT_ADD_MODE("-pendingcleanupimfile", "show runs that need to be cleaned up", CHIPTOOL_MODE_PENDINGCLEANUPIMFILE, pendingcleanupimfileArgs);
+    PXOPT_ADD_MODE("-donecleanup",          "show runs that have been cleaned",     CHIPTOOL_MODE_DONECLEANUP,          donecleanupArgs);
+    PXOPT_ADD_MODE("-run",                  "show runs",                            CHIPTOOL_MODE_RUN,                  runArgs);
+    PXOPT_ADD_MODE("-tocleanedimfile",      "set imfile state to cleaned",          CHIPTOOL_MODE_TOCLEANEDIMFILE,      tocleanedimfileArgs);
+    PXOPT_ADD_MODE("-tofullimfile",        "set imfile state to full",              CHIPTOOL_MODE_TOFULLIMFILE,         tofullimfileArgs);
+    PXOPT_ADD_MODE("-topurgedimfile",      "set imfile state to purged",            CHIPTOOL_MODE_TOPURGEDIMFILE,       topurgedimfileArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/detselect.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/detselect.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/detselect.c	(revision 22322)
@@ -0,0 +1,220 @@
+/*
+ * detselect.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "pxtools.h"
+#include "detselect.h"
+
+static bool searchMode(pxConfig *config);
+static bool selectMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = detselectConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(DETSELECT_MODE_SEARCH,        searchMode);
+        MODECASE(DETSELECT_MODE_SELECT,        selectMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+static bool searchMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(unlimit, config->args, "-unlimit", false);
+
+    PXOPT_LOOKUP_TIME(time, config->args, "-time", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-inst",      "camera", "==");
+    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
+    PXOPT_COPY_STR(config->args, where, "-det_type",  "det_type", "==");
+    PXOPT_COPY_STR(config->args, where, "-type",      "det_type", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter",    "filter", "==");
+
+    // airmass_min  < airmass  < airmass_max
+    PXOPT_COPY_F32(config->args, where, "-airmass", "airmass_min", "<=");
+    PXOPT_COPY_F32(config->args, where, "-airmass", "airmass_max", ">=");
+
+    // exp_time_min < exp_time < exp_time_max
+    PXOPT_COPY_F32(config->args, where, "-exp_time", "exp_time_min", "<=");
+    PXOPT_COPY_F32(config->args, where, "-exp_time", "exp_time_max", ">=");
+
+    // ccd_temp_min < ccd_temp < ccd_temp_max
+    PXOPT_COPY_F32(config->args, where, "-ccd_temp", "ccd_temp_min", "<=");
+    PXOPT_COPY_F32(config->args, where, "-ccd_temp", "ccd_temp_max", ">=");
+
+    PXOPT_COPY_F64(config->args, where, "-posang", "posang_min", "<=");
+    PXOPT_COPY_F64(config->args, where, "-posang", "posang_max", ">=");
+
+    // time_begin    < time     < time_end
+    // the == NULL tests invokes some psDB magic to make an OR
+    // conditional query
+    psMetadataAddTime(where, PS_LIST_TAIL, "time_begin", 0, "<=", time);
+    psMetadataAddTime(where, PS_LIST_TAIL, "time_begin", PS_META_DUPLICATE_OK, "==", NULL);
+    psMetadataAddTime(where, PS_LIST_TAIL, "time_end", 0, ">=", time);
+    psMetadataAddTime(where, PS_LIST_TAIL, "time_end", PS_META_DUPLICATE_OK, "==", NULL);
+
+    psString query = pxDataGet("detselect_search.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // use psDBGenerateWhereConditionalSQL with AND ... because the SQL ends in a WHERE
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+	psStringAppend(&query, " AND %s", whereClause);
+	psFree(whereClause);
+    }
+    psFree(where);
+
+    // we choose the single detrend image which matches all criteria and has
+    // the latest insertion date
+
+    // unless explicitly specified by the user, list all possible matches
+    if (!unlimit) {
+        psStringAppend(&query, " ORDER BY registered DESC LIMIT 1");
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        // XXX check psError here
+        psError(PS_ERR_UNKNOWN, false, "no detrend exposures found");
+        psFree(output);
+        return true;
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool selectMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+
+    psString query = pxDataGet("detselect_select.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // use psDBGenerateWhereConditionalSQL with AND ... because the SQL ends in a WHERE
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        // XXX check psError here
+        psError(PS_ERR_UNKNOWN, false, "no detNormalizedImfile rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detNormalizedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/detselect.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/detselect.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/detselect.h	(revision 22322)
@@ -0,0 +1,33 @@
+/*
+ * detselect.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef DETSELECT_H
+#define DETSELECT_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    DETSELECT_MODE_NONE      = 0x0,
+    DETSELECT_MODE_SEARCH,
+    DETSELECT_MODE_SELECT,
+} detselectMode;
+
+pxConfig *detselectConfig(pxConfig *config, int argc, char **argv);
+
+#endif // DETSELECT_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/detselectConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/detselectConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/detselectConfig.c	(revision 22322)
@@ -0,0 +1,94 @@
+/*
+ * detselectConfig.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <psmodules.h>
+#include <math.h>
+
+#include "pxtools.h"
+#include "detselect.h"
+
+pxConfig *detselectConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    // -search
+    psMetadata *searchArgs = psMetadataAlloc();
+    psMetadataAddStr(searchArgs, PS_LIST_TAIL, "-inst", 0,                     "search by camera", NULL);
+    psMetadataAddStr(searchArgs, PS_LIST_TAIL, "-telescope", 0,                     "search by telescope", NULL);
+    psMetadataAddStr(searchArgs, PS_LIST_TAIL, "-det_type", 0,                     "search by detrend type", NULL);
+    psMetadataAddStr(searchArgs, PS_LIST_TAIL, "-type", 0,                     "search by detrend type (alias for -det_type)", NULL);
+    psMetadataAddStr(searchArgs, PS_LIST_TAIL, "-filter", 0,                     "search by filter", NULL);
+    psMetadataAddF32(searchArgs, PS_LIST_TAIL, "-airmass", 0,                     "define airmass", NAN);
+    psMetadataAddF32(searchArgs, PS_LIST_TAIL, "-exp_time", 0,                     "search by exposure time", NAN);
+    psMetadataAddF32(searchArgs, PS_LIST_TAIL, "-ccd_temp", 0,                     "search by ccd tempature", NAN);
+    psMetadataAddF64(searchArgs, PS_LIST_TAIL, "-posang", 0,                     "search by rotator position angle", NAN);
+    psMetadataAddTime(searchArgs, PS_LIST_TAIL, "-time", 0,                      "define time for desired detrend data", NULL);
+    psMetadataAddBool(searchArgs, PS_LIST_TAIL, "-simple",  0,                      "use the simple output format", false);
+    psMetadataAddBool(searchArgs, PS_LIST_TAIL, "-unlimit",  0,                      "list all possible detruns, not just the best match", false);
+ 
+    // -select
+    psMetadata *selectArgs = psMetadataAlloc();
+    psMetadataAddStr(selectArgs, PS_LIST_TAIL, "-det_id", 0,                     "search by detrend ID", NULL);
+    psMetadataAddS32(selectArgs, PS_LIST_TAIL, "-iteration", 0,                     "search by iteration number", 0);
+    psMetadataAddStr(selectArgs, PS_LIST_TAIL, "-class_id", 0,                     "search by class ID", NULL);
+    psMetadataAddBool(selectArgs, PS_LIST_TAIL, "-simple",  0,                      "use the simple output format", false);
+    
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-search",  "search for an appropriate detrend", DETSELECT_MODE_SEARCH, searchArgs);
+    PXOPT_ADD_MODE("-select",  "retreive detrend information", DETSELECT_MODE_SELECT, selectArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool.c	(revision 22322)
@@ -0,0 +1,2084 @@
+/*
+ * dettool.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+static bool pendingMode(pxConfig *config);
+static bool definebytagMode(pxConfig *config);
+static bool definebyqueryMode(pxConfig *config);
+static bool definebydetrunMode(pxConfig *config);
+static bool runsMode(pxConfig *config);
+static bool childlessrunMode(pxConfig *config);
+static bool inputMode(pxConfig *config);
+static bool rawMode(pxConfig *config);
+
+// run
+static bool updatedetrunMode(pxConfig *config);
+static bool rerunMode(pxConfig *config);
+// register
+static bool register_detrendMode(pxConfig *config);
+
+//static psArray *validDetInputClassIds(pxConfig *config, const char *det_id);
+//static psArray *searchInputImfiles(pxConfig *config, const char *det_id);
+static detInputExpRow *rawDetrenTodetInputExpRow(rawExpRow *rawExp, psS64 det_id, psS32 iteration);
+static psS32 incrementIteration(pxConfig *config, psS64 det_id);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = dettoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(DETTOOL_MODE_PENDING,          pendingMode);
+        MODECASE(DETTOOL_MODE_DEFINEBYTAG,      definebytagMode);
+        MODECASE(DETTOOL_MODE_DEFINEBYQUERY,    definebyqueryMode);
+        MODECASE(DETTOOL_MODE_DEFINEBYDETRUN,   definebydetrunMode);
+        MODECASE(DETTOOL_MODE_RUNS,             runsMode);
+        MODECASE(DETTOOL_MODE_CHILDLESSRUN,     childlessrunMode);
+        MODECASE(DETTOOL_MODE_INPUT,            inputMode);
+        MODECASE(DETTOOL_MODE_RAW,              rawMode);
+        // correction
+        MODECASE(DETTOOL_MODE_MAKECORRECTION,    makecorrectionMode);
+        MODECASE(DETTOOL_MODE_TOCORRECTEXP,      tocorrectexpMode);
+        MODECASE(DETTOOL_MODE_TOCORRECTIMFILE,   tocorrectimfileMode);
+        MODECASE(DETTOOL_MODE_ADDCORRECTIMFILE,  addcorrectimfileMode);
+        // imfile
+        MODECASE(DETTOOL_MODE_TOPROCESSEDIMFILE,toprocessedimfileMode);
+        MODECASE(DETTOOL_MODE_ADDPROCESSEDIMFILE,addprocessedimfileMode);
+        MODECASE(DETTOOL_MODE_PROCESSEDIMFILE,  processedimfileMode);
+        MODECASE(DETTOOL_MODE_REVERTPROCESSEDIMFILE, revertprocessedimfileMode);
+        MODECASE(DETTOOL_MODE_UPDATEPROCESSEDIMFILE, updateprocessedimfileMode);
+        MODECASE(DETTOOL_MODE_PENDINGCLEANUP_PROCESSEDIMFILE, pendingcleanup_processedimfileMode);
+        MODECASE(DETTOOL_MODE_DONECLEANUP_PROCESSEDIMFILE, donecleanup_processedimfileMode);
+        // exp
+        MODECASE(DETTOOL_MODE_TOPROCESSEDEXP,   toprocessedexpMode);
+        MODECASE(DETTOOL_MODE_ADDPROCESSEDEXP,  addprocessedexpMode);
+        MODECASE(DETTOOL_MODE_PROCESSEDEXP,     processedexpMode);
+        MODECASE(DETTOOL_MODE_REVERTPROCESSEDEXP, revertprocessedexpMode);
+        MODECASE(DETTOOL_MODE_UPDATEPROCESSEDEXP, updateprocessedexpMode);
+        MODECASE(DETTOOL_MODE_PENDINGCLEANUP_PROCESSEDEXP, pendingcleanup_processedexpMode);
+        MODECASE(DETTOOL_MODE_DONECLEANUP_PROCESSEDEXP, donecleanup_processedexpMode);
+        // stacked
+        MODECASE(DETTOOL_MODE_TOSTACKED,        tostackedMode);
+        MODECASE(DETTOOL_MODE_ADDSTACKED,       addstackedMode);
+        MODECASE(DETTOOL_MODE_STACKED,          stackedMode);
+        MODECASE(DETTOOL_MODE_REVERTSTACKED,    revertstackedMode);
+        MODECASE(DETTOOL_MODE_UPDATESTACKED,    updatestackedMode);
+        MODECASE(DETTOOL_MODE_PENDINGCLEANUP_STACKED, pendingcleanup_stackedMode);
+        MODECASE(DETTOOL_MODE_DONECLEANUP_STACKED, donecleanup_stackedMode);
+        // normalizedstat
+        MODECASE(DETTOOL_MODE_TONORMALIZEDSTAT, tonormalizedstatMode);
+        MODECASE(DETTOOL_MODE_ADDNORMALIZEDSTAT,addnormalizedstatMode);
+        MODECASE(DETTOOL_MODE_NORMALIZEDSTAT,   normalizedstatMode);
+        MODECASE(DETTOOL_MODE_REVERTNORMALIZEDSTAT, revertnormalizedstatMode);
+        MODECASE(DETTOOL_MODE_UPDATENORMALIZEDSTAT, updatenormalizedstatMode);
+        MODECASE(DETTOOL_MODE_PENDINGCLEANUP_NORMALIZEDSTAT, pendingcleanup_normalizedstatMode);
+        MODECASE(DETTOOL_MODE_DONECLEANUP_NORMALIZEDSTAT, donecleanup_normalizedstatMode);
+        // normalizedimfile
+        MODECASE(DETTOOL_MODE_TONORMALIZE,      tonormalizeMode);
+        MODECASE(DETTOOL_MODE_ADDNORMALIZEDIMFILE,addnormalizedimfileMode);
+        MODECASE(DETTOOL_MODE_NORMALIZEDIMFILE, normalizedimfileMode);
+        MODECASE(DETTOOL_MODE_REVERTNORMALIZEDIMFILE, revertnormalizedimfileMode);
+        MODECASE(DETTOOL_MODE_UPDATENORMALIZEDIMFILE, updatenormalizedimfileMode);
+        MODECASE(DETTOOL_MODE_PENDINGCLEANUP_NORMALIZEDIMFILE, pendingcleanup_normalizedimfileMode);
+        MODECASE(DETTOOL_MODE_DONECLEANUP_NORMALIZEDIMFILE, donecleanup_normalizedimfileMode);
+        // normalizedexp
+        MODECASE(DETTOOL_MODE_TONORMALIZEDEXP,  tonormalizedexpMode);
+        MODECASE(DETTOOL_MODE_ADDNORMALIZEDEXP, addnormalizedexpMode);
+        MODECASE(DETTOOL_MODE_NORMALIZEDEXP,    normalizedexpMode);
+        MODECASE(DETTOOL_MODE_REVERTNORMALIZEDEXP, revertnormalizedexpMode);
+        MODECASE(DETTOOL_MODE_UPDATENORMALIZEDEXP, updatenormalizedexpMode);
+        MODECASE(DETTOOL_MODE_PENDINGCLEANUP_NORMALIZEDEXP, pendingcleanup_normalizedexpMode);
+        MODECASE(DETTOOL_MODE_DONECLEANUP_NORMALIZEDEXP, donecleanup_normalizedexpMode);
+        // residimfile
+        MODECASE(DETTOOL_MODE_TORESIDIMFILE,    toresidimfileMode);
+        MODECASE(DETTOOL_MODE_ADDRESIDIMFILE,   addresidimfileMode);
+        MODECASE(DETTOOL_MODE_RESIDIMFILE,      residimfileMode);
+        MODECASE(DETTOOL_MODE_REVERTRESIDIMFILE,revertresidimfileMode);
+        MODECASE(DETTOOL_MODE_UPDATERESIDIMFILE, updateresidimfileMode);
+        MODECASE(DETTOOL_MODE_PENDINGCLEANUP_RESIDIMFILE, pendingcleanup_residimfileMode);
+        MODECASE(DETTOOL_MODE_DONECLEANUP_RESIDIMFILE, donecleanup_residimfileMode);
+        // residexp
+        MODECASE(DETTOOL_MODE_TORESIDEXP,       toresidexpMode);
+        MODECASE(DETTOOL_MODE_ADDRESIDEXP,      addresidexpMode);
+        MODECASE(DETTOOL_MODE_RESIDEXP,         residexpMode);
+        MODECASE(DETTOOL_MODE_REVERTRESIDEXP,   revertresidexpMode);
+        MODECASE(DETTOOL_MODE_UPDATERESIDEXP,   updateresidexpMode);
+        MODECASE(DETTOOL_MODE_PENDINGCLEANUP_RESIDEXP, pendingcleanup_residexpMode);
+        MODECASE(DETTOOL_MODE_DONECLEANUP_RESIDEXP, donecleanup_residexpMode);
+        // detrunsummary
+        MODECASE(DETTOOL_MODE_TODETRUNSUMMARY,  todetrunsummaryMode);
+        MODECASE(DETTOOL_MODE_ADDDETRUNSUMMARY, adddetrunsummaryMode);
+        MODECASE(DETTOOL_MODE_DETRUNSUMMARY,    detrunsummaryMode);
+        MODECASE(DETTOOL_MODE_REVERTDETRUNSUMMARY, revertdetrunsummaryMode);
+        MODECASE(DETTOOL_MODE_UPDATEDETRUNSUMMARY, updatedetrunsummaryMode);
+        MODECASE(DETTOOL_MODE_UPDATEDETRUN,     updatedetrunMode);
+        MODECASE(DETTOOL_MODE_RERUN,            rerunMode);
+        // register
+        MODECASE(DETTOOL_MODE_REGISTER_DETREND, register_detrendMode);
+        MODECASE(DETTOOL_MODE_REGISTER_DETREND_IMFILE, register_detrend_imfileMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+static bool pendingMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_pending.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+    
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_type", "exp_type", "==");
+    PXOPT_COPY_STR(config->args, where, "-inst", "camera", "==");
+    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter", "filter", "==");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "rawExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "rawExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool definebytagMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // what type of detRun is this?
+    // required
+    PXOPT_LOOKUP_STR(det_type, config->args, "-det_type", true, false);
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(filelevel, config->args, "-filelevel", false, false);
+
+    // check mode
+    PXOPT_LOOKUP_STR(mode, config->args, "-mode", true, false); // default ('master') is supplied
+    if (!isValidMode(config, mode)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid mode");
+        return false;
+    }
+
+    // get -ref_det_id and -ref_iter : required for 'verify' mode / disallowed otherwise
+    PXOPT_LOOKUP_S64(ref_det_id, config->args, "-ref_det_id", true, false);
+    PXOPT_LOOKUP_S32(ref_iter, config->args, "-ref_iter", true, false);
+    if (!strncmp(mode, "verify", 7) && ((ref_det_id == 0) || (ref_iter == -1))) {
+        psError(PS_ERR_UNKNOWN, false, "verify mode requires both -ref_det_id and -ref_iter");
+        return false;
+    } 
+    if (strncmp(mode, "verify", 7) && ((ref_det_id != 0) || (ref_iter != -1))) {
+        psError(PS_ERR_UNKNOWN, false, "master mode cannot have -ref_det_id or -ref_iter set");
+        return false;
+    } 
+	
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", false, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", false, false);
+    PXOPT_LOOKUP_STR(exp_type, config->args, "-exp_type", false, false);
+    PXOPT_LOOKUP_STR(filter, config->args, "-filter", false, false);
+
+    PXOPT_LOOKUP_F32(airmass_min, config->args, "-airmass_min", false, false);
+    PXOPT_LOOKUP_F32(airmass_max, config->args, "-airmass_max", false, false);
+    PXOPT_LOOKUP_F32(exp_time_min, config->args, "-exp_time_min", false, false);
+    PXOPT_LOOKUP_F32(exp_time_max, config->args, "-exp_time_max", false, false);
+    PXOPT_LOOKUP_F32(ccd_temp_min, config->args, "-ccd_temp_min", false, false);
+    PXOPT_LOOKUP_F32(ccd_temp_max, config->args, "-ccd_temp_max", false, false);
+    PXOPT_LOOKUP_F64(posang_min, config->args, "-posang_min", false, false);
+    PXOPT_LOOKUP_F64(posang_max, config->args, "-posang_max", false, false);
+    PXOPT_LOOKUP_F64(solang_min, config->args, "-solang_min", false, false);
+    PXOPT_LOOKUP_F64(solang_max, config->args, "-solang_max", false, false);
+    PXOPT_LOOKUP_TIME(time_begin, config->args, "-time_begin", false, false);
+    PXOPT_LOOKUP_TIME(time_end, config->args, "-time_end", false, false);
+    PXOPT_LOOKUP_TIME(use_begin, config->args, "-use_begin", false, false);
+    PXOPT_LOOKUP_TIME(use_end, config->args, "-use_end", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-reduction", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+
+    // default
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+
+    // we have to support multipe exp_ids
+    psMetadataItem *item = psMetadataLookup(config->args, "-exp_id");
+    if (!item) {
+        // this shouldn't actually happen when using psArgs
+        psError(PS_ERR_UNKNOWN, true, "-exp_id is required");
+        return false;
+    }
+    psMetadata *where = psMetadataAlloc();
+
+    // make sure that -exp_id was parsed correctly
+    // XXX this can be removed someday
+    // XXX Special case for a multi : move into a macro?
+    if (item->type == PS_DATA_METADATA_MULTI) {
+        psListIterator *iter = psListIteratorAlloc(item->data.list, 0, false);
+        psMetadataItem *mItem = NULL;
+        while ((mItem = psListGetAndIncrement(iter))) {
+            psString exp_id = mItem->data.V;
+            // if exp_id is NULL then it means that -exp_id has not been
+            // specified
+            if (!exp_id) {
+                psError(PS_ERR_UNKNOWN, true,
+                        "at least one -exp_id is required");
+                psFree(where);
+                return false;
+            }
+
+            if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_id",
+                 PS_META_DUPLICATE_OK, "==", exp_id)) {
+                psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+                psFree(iter);
+                psFree(where);
+                return false;
+            }
+        }
+        psFree(iter);
+    } else {
+        psAbort(                "-exp_id was not parsed correctly (this should not happen");
+    }
+
+    if (psListLength(where->list) < 1) {
+        psFree(where);
+        where = NULL;
+    }
+
+    // check that the specified exp_ids actually exist
+    psArray *detrendExps = rawExpSelectRowObjects(config->dbh, where, 0);
+    psFree(where);
+    if (!detrendExps) {
+        psError(PS_ERR_UNKNOWN, false, "no rawExp rows found");
+        return false;
+    }
+
+    // we should have one rawExp row per exp_id specified
+    if (psListLength(item->data.list) != psArrayLength(detrendExps)) {
+        psAbort(    "an -exp_id matched more then one rawExp (this should not happen");
+
+    }
+
+    // check to see if -filelevel was set on the command line
+    if (!filelevel) {
+        filelevel = psStringCopy(((rawExpRow *)detrendExps->data[0])->filelevel);
+    }
+
+    // start a transaction so we don't end up with childlessed det_ids
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(detrendExps);
+        return false;
+    }
+
+    // the first iteration is always 0
+    // XXX the camera name is set from the first inputExp
+    // XXX det_id
+    detRunInsert(config->dbh,
+                 0,
+                 0,
+                 det_type,
+                 mode,
+                 "run",
+                 filelevel,
+                 workdir,
+                 camera,
+                 telescope,
+                 exp_type,
+                 reduction,
+                 filter,
+                 airmass_min,
+                 airmass_max,
+                 exp_time_min,
+                 exp_time_max,
+                 ccd_temp_min,
+                 ccd_temp_max,
+                 posang_min,
+                 posang_max,
+                 registered,
+                 time_begin,
+                 time_end,
+                 use_begin,
+                 use_end,
+                 solang_min,
+                 solang_max,
+                 label,
+                 ref_det_id,
+                 ref_iter
+        );
+    psS64 det_id = psDBLastInsertID(config->dbh);
+
+    // create new detInputExp row(s) from the rawExp row(s)
+    psArray *inputExps = psArrayAllocEmpty(psArrayLength(detrendExps));
+    for (long i = 0; i < psArrayLength(detrendExps); i++) {
+        detInputExpRow *inputExp = rawDetrenTodetInputExpRow(
+            detrendExps->data[i],
+            det_id,
+            0 // the first iteration is explicitly 0
+        );
+        psArrayAdd(inputExps, 0, inputExp);
+        psFree(inputExp);
+    }
+
+    psFree(detrendExps);
+
+    // insert detInputExp objects into the database
+    if (!detInputExpInsertObjects(config->dbh, inputExps)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psFree(inputExps);
+        return false;
+    }
+    psFree(inputExps);
+
+    // point of no return for det_id creation
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // print the new det_id
+    psArray *detRuns = NULL;
+    {
+        psMetadata *where = psMetadataAlloc();
+        psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", det_id);
+        detRuns = psDBSelectRows(config->dbh, "detRun", where, 0);
+        psFree(where);
+    }
+    if (!detRuns) {
+        psError(PS_ERR_UNKNOWN, false, "can't find the detRun we just created");
+        return false;
+    }
+    // sanity check results
+    if (psArrayLength(detRuns) != 1) {
+        psAbort("found more then one detRun matching det_id %" PRId64 " (this should not happen)", det_id);
+        return false;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, detRuns, "detRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(detRuns);
+        return false;
+    }
+    psFree(detRuns);
+
+    return true;
+}
+
+#if 0
+// This function is used to convert the det_id from an int, as it is used
+// internally, to be a string for external use.  The rationale being that we may
+// want to change how det_id is generated in the future and don't want to
+// external programs to become depending on this value being an int.
+static bool convertDetIdToStr(psArray *mds)
+{
+    PS_ASSERT_PTR_NON_NULL(mds, false);
+
+    for (long i = 0; i < psArrayLength(mds); i++) {
+        psMetadata *md = mds->data[i];
+        bool status = false;
+        psS32 det_id = psMetadataLookupS32(&status, md, "det_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for det_id");
+            return false;
+        }
+        psMetadataRemoveKey(md, "det_id");
+        psString det_idStr = psDBIntToString((psU64)det_id);
+        psMetadataAddStr(mds->data[i], PS_LIST_HEAD, "det_id", 0, NULL, det_idStr);
+        psFree(det_idStr);
+    }
+
+    return true;
+}
+#endif
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_STR(det_type, config->args, "-det_type", true, false);
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", true, false);
+
+    // check mode
+    PXOPT_LOOKUP_STR(mode, config->args, "-mode", true, false); // default ('master') is supplied
+    if (!isValidMode(config, mode)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid mode");
+        return false;
+    }
+
+    // get -ref_det_id and -ref_iter : required for 'verify' mode / disallowed otherwise
+    PXOPT_LOOKUP_S64(ref_det_id, config->args, "-ref_det_id", false, false);
+    PXOPT_LOOKUP_S32(ref_iter, config->args, "-ref_iter", false, false);
+    if (!strncmp(mode, "verify", 7) && ((ref_det_id == 0) || (ref_iter == -1))) {
+        psError(PS_ERR_UNKNOWN, false, "verify mode requires both -ref_det_id and -ref_iter");
+        return false;
+    } 
+    if (strncmp(mode, "verify", 7) && ((ref_det_id != 0) || (ref_iter != -1))) {
+        psError(PS_ERR_UNKNOWN, false, "master mode cannot have -ref_det_id or -ref_iter set");
+        return false;
+    } 
+
+    PXOPT_LOOKUP_STR(filelevel, config->args, "-filelevel", false, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", false, false);
+    PXOPT_LOOKUP_STR(filter, config->args, "-filter", false, false);
+    PXOPT_LOOKUP_F32(airmass_min, config->args, "-airmass_min", false, false);
+    PXOPT_LOOKUP_F32(airmass_max, config->args, "-airmass_max", false, false);
+    PXOPT_LOOKUP_F32(exp_time_min, config->args, "-exp_time_min", false, false);
+    PXOPT_LOOKUP_F32(exp_time_max, config->args, "-exp_time_max", false, false);
+    PXOPT_LOOKUP_F32(ccd_temp_min, config->args, "-ccd_temp_min", false, false);
+    PXOPT_LOOKUP_F32(ccd_temp_max, config->args, "-ccd_temp_max", false, false);
+    PXOPT_LOOKUP_F64(posang_min, config->args, "-posang_min", false, false);
+    PXOPT_LOOKUP_F64(posang_max, config->args, "-posang_max", false, false);
+    PXOPT_LOOKUP_F64(solang_min, config->args, "-solang_min", false, false);
+    PXOPT_LOOKUP_F64(solang_max, config->args, "-solang_max", false, false);
+
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-reduction", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    PXOPT_LOOKUP_BOOL(randomSubset, config->args, "-random_subset", false);
+    PXOPT_LOOKUP_S32(randomLimit, config->args, "-random_limit", false, false);
+
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+    PXOPT_LOOKUP_TIME(time_begin, config->args, "-time_begin", false, false);
+    PXOPT_LOOKUP_TIME(time_end, config->args, "-time_end", false, false);
+    PXOPT_LOOKUP_TIME(use_begin, config->args, "-use_begin", false, false);
+    PXOPT_LOOKUP_TIME(use_end, config->args, "-use_end", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-select_exp_type", "exp_type", "==");
+    PXOPT_COPY_STR(config->args, where, "-select_inst", "camera", "==");
+    PXOPT_COPY_STR(config->args, where, "-select_telescope", "telescope", "==");
+    PXOPT_COPY_STR(config->args, where, "-select_filter", "filter", "==");
+    PXOPT_COPY_STR(config->args, where, "-select_uri", "uri", "==");
+    PXOPT_COPY_TIME(config->args, where, "-select_dateobs_begin", "dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, where, "-select_dateobs_end", "dateobs", "<=");
+    PXOPT_COPY_F32(config->args, where, "-select_airmass_min", "airmass", ">=");
+    PXOPT_COPY_F32(config->args, where, "-select_airmass_max", "airmass", "<=");
+    PXOPT_COPY_F32(config->args, where, "-select_sat_pixel_frac_max", "sat_pixel_frac", "<=");
+    PXOPT_COPY_F32(config->args, where, "-select_exp_time_min", "exp_time", ">=");
+    PXOPT_COPY_F32(config->args, where, "-select_exp_time_max", "exp_time", "<=");
+    PXOPT_COPY_F64(config->args, where, "-select_ccd_temp_min", "ccd_temp", ">=");
+    PXOPT_COPY_F64(config->args, where, "-select_ccd_temp_max", "ccd_temp", "<=");
+    PXOPT_COPY_F64(config->args, where, "-select_pon_time_min", "pon_time", ">=");
+    PXOPT_COPY_F64(config->args, where, "-select_pon_time_max", "pon_time", "<=");
+    PXOPT_COPY_F64(config->args, where, "-select_posang_min", "posang", ">=");
+    PXOPT_COPY_F64(config->args, where, "-select_posang_max", "posang", "<=");
+    PXOPT_COPY_F64(config->args, where, "-select_solang_min", "solang", ">=");
+    PXOPT_COPY_F64(config->args, where, "-select_solang_max", "solang", "<=");
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        where = NULL;
+    }
+
+    // there is some namespace overlap between the names of the fields we'd
+    // like to search by to setup a detrun and the names of the fields we'd
+    // like to assign values to so I've separated them but prepending set- to
+    // the assigned values
+
+    // search for rawExps with the specified options
+    psArray *detrendExps = rawExpSelectRowObjects(config->dbh, where, 0);
+    psFree(where);
+    // make sure that we found at least one rawExp
+    if (!detrendExps) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(detrendExps)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(detrendExps);
+        return true;
+    }
+
+    if (randomSubset && (randomLimit < detrendExps->n)) {
+      // generate a random-valued vector, return an index sorted by the random values
+      psVector *randomVector = psVectorAlloc(detrendExps->n, PS_TYPE_F32); // random values
+      psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+      for (int i = 0; i < randomVector->n; i++) {
+        randomVector->data.F32[i] = psRandomUniform(rng);
+      }
+      psVector *indexVector = psVectorSortIndex(NULL, randomVector);
+
+      // accept for first n of the sequence
+      psArray *subset = psArrayAlloc (randomLimit);
+      for (int i = 0; i < randomLimit; i++ ){
+        int j = indexVector->data.S32[i];
+        subset->data[i] = psMemIncrRefCounter (detrendExps->data[j]);
+      }
+      psFree (detrendExps);
+      detrendExps = subset;
+    }
+
+    // check to see if -filelevel was set on the command line
+    if (!filelevel) {
+        filelevel = psStringCopy(((rawExpRow *)detrendExps->data[0])->filelevel);
+    }
+
+    if (pretend) {
+        // negative simple so the default is true
+        if (!rawExpPrintObjects(stdout, detrendExps, !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(detrendExps);
+            return false;
+        }
+        psFree(detrendExps);
+        return true;
+    }
+
+    // start a transaction so we don't end up with childlessed det_ids
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(detrendExps);
+        return false;
+    }
+
+    // the first iteration is always 0
+    // XXX det_id
+    detRunInsert(config->dbh,
+                 0,      // det_id
+                 0,      // iteration
+                 det_type,
+                 mode,
+                 "run",  // state
+                 filelevel,
+                 workdir,
+                 camera,
+                 telescope,
+                 "NA",
+                 reduction,
+                 filter,
+                 airmass_min,
+                 airmass_max,
+                 exp_time_min,
+                 exp_time_max,
+                 ccd_temp_min,
+                 ccd_temp_max,
+                 posang_min,
+                 posang_max,
+                 registered,
+                 time_begin,
+                 time_end,
+                 use_begin,
+                 use_end,
+                 solang_min,
+                 solang_max,
+                 label,
+                 ref_det_id,
+		 ref_iter
+        );
+    psS64 det_id = psDBLastInsertID(config->dbh);
+
+    // create new detInputExp row(s) from the rawExp row(s)
+    psArray *inputExps = psArrayAllocEmpty(psArrayLength(detrendExps));
+    for (long i = 0; i < psArrayLength(detrendExps); i++) {
+        detInputExpRow *inputExp = rawDetrenTodetInputExpRow(
+            detrendExps->data[i],
+            det_id,
+            0 // the first iteration is explicitly 0
+        );
+        psArrayAdd(inputExps, 0, inputExp);
+        psFree(inputExp);
+    }
+
+    psFree(detrendExps);
+
+    // insert detInputExp objects into the database
+    if (!detInputExpInsertObjects(config->dbh, inputExps)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psFree(inputExps);
+        return false;
+    }
+    psFree(inputExps);
+
+    // point of no return for det_id creation
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+
+    // print the new det_id
+    psArray *detRuns = NULL;
+    {
+        psMetadata *where = psMetadataAlloc();
+        psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", det_id);
+        detRuns = psDBSelectRows(config->dbh, "detRun", where, 0);
+        psFree(where);
+    }
+    if (!detRuns) {
+        psError(PS_ERR_UNKNOWN, false, "can't find the detRun we just created");
+        return false;
+    }
+    // sanity check results
+    if (psArrayLength(detRuns) != 1) {
+        psAbort("found more then one detRun matching det_id %" PRId64 " (this should not happen)", det_id);
+        return false;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, detRuns, "detRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(detRuns);
+        return false;
+    }
+    psFree(detRuns);
+
+    return true;
+}
+
+static bool definebydetrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    
+    // XXX this mode is not well-tested: probably need to specify iteration here
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_STR(det_type, config->args, "-set_det_type", false, false); // optional
+
+    PXOPT_LOOKUP_STR(mode, config->args, "-set_mode", false, false); // optional
+    if (!isValidMode(config, mode)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid mode");
+        return false;
+    }
+
+    // get -ref_det_id and -ref_iter : required for 'verify' mode / disallowed otherwise
+    PXOPT_LOOKUP_S64(ref_det_id, config->args, "-ref_det_id", false, false);
+    PXOPT_LOOKUP_S32(ref_iter, config->args, "-ref_iter", false, false);
+    if (!strncmp(mode, "verify", 7) && ((ref_det_id == 0) || (ref_iter == -1))) {
+        psError(PS_ERR_UNKNOWN, false, "verify mode requires both -ref_det_id and -ref_iter");
+        return false;
+    } 
+    if (strncmp(mode, "verify", 7) && ((ref_det_id != 0) || (ref_iter != -1))) {
+        psError(PS_ERR_UNKNOWN, false, "master mode cannot have -ref_det_id or -ref_iter set");
+        return false;
+    } 
+
+    // the new detRun may have different values for these limits:
+    PXOPT_LOOKUP_STR(exp_type, config->args, "-set_exp_type", false, false);
+    PXOPT_LOOKUP_STR(filelevel, config->args, "-set_filelevel", false, false);
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", false, false);
+    PXOPT_LOOKUP_STR(filter, config->args, "-set_filter", false, false);
+    PXOPT_LOOKUP_F32(airmass_min, config->args, "-set_airmass_min", false, false);
+    PXOPT_LOOKUP_F32(airmass_max, config->args, "-set_airmass_max", false, false);
+    PXOPT_LOOKUP_F32(exp_time_min, config->args, "-set_exp_time_min", false, false);
+    PXOPT_LOOKUP_F32(exp_time_max, config->args, "-set_exp_time_max", false, false);
+    PXOPT_LOOKUP_F64(ccd_temp_min, config->args, "-set_ccd_temp_min", false, false);
+    PXOPT_LOOKUP_F64(ccd_temp_max, config->args, "-set_ccd_temp_max", false, false);
+    PXOPT_LOOKUP_F64(posang_min, config->args, "-set_posang_min", false, false);
+    PXOPT_LOOKUP_F64(posang_max, config->args, "-set_posang_max", false, false);
+    PXOPT_LOOKUP_F64(solang_min, config->args, "-set_solang_min", false, false);
+    PXOPT_LOOKUP_F64(solang_max, config->args, "-set_solang_max", false, false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-set_registered", false, false);
+    PXOPT_LOOKUP_TIME(time_begin, config->args, "-set_time_begin", false, false);
+    PXOPT_LOOKUP_TIME(time_end, config->args, "-set_time_end", false, false);
+    PXOPT_LOOKUP_TIME(use_begin, config->args, "-set_use_begin", false, false);
+    PXOPT_LOOKUP_TIME(use_end, config->args, "-set_use_end", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false); // optional
+
+    // reference iteration to use (otherwise last iteration)
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false); // for specifying input exp restrictions
+
+    // lookup the detRun that we will be basing this one on
+    psArray *detRuns = NULL;
+    {
+        psMetadata *where = psMetadataAlloc();
+        psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", det_id);
+        detRuns = detRunSelectRowObjects(config->dbh, where, 0);
+        psFree(where);
+    }
+    if (!detRuns) {
+        psError(PS_ERR_UNKNOWN, false, "database error: no matching det_id found");
+        return false;
+    }
+
+    // sanity check the result... we should have only found one det_id
+    if (psArrayLength(detRuns) != 1) {
+        psAbort("found more then one detRun matching det_id %" PRId64 " (this should not happen)", det_id);
+    }
+
+    // pull the detRun object out the result array
+    detRunRow *detRun = psMemIncrRefCounter(detRuns->data[0]);
+
+    // discard the resultarray
+    psFree(detRuns);
+
+    if (iteration < 0) {
+	iteration = detRun->iteration;
+    }
+
+    detRun->det_id = 0; // set the det_id to 0/NULL so the database can assign it
+    detRun->iteration = 0; // reset the iteration to 0
+
+    detRun->ref_det_id = ref_det_id; // not inherited: may only be set for 'verify' run
+    detRun->ref_iter = ref_iter;  // not inherited: may only be set for 'verify' run
+
+    // reset the state to "run"
+    psFree(detRun->state);
+    detRun->state = psStringCopy("run");
+
+    // walk through the optional values and update the detRun as required.  Otherwise, these
+    // value are inherited from the specified detRun
+    if (det_type) {
+        psFree(detRun->det_type);
+        detRun->det_type = psStringCopy(det_type);
+    }
+
+    if (mode) {
+        psFree(detRun->mode);
+        detRun->mode = psStringCopy(mode);
+    }
+
+    if (exp_type) {
+        psFree(detRun->exp_type);
+        detRun->exp_type = psStringCopy(exp_type);
+    }
+
+    if (filelevel) {
+        psFree(detRun->filelevel);
+        detRun->filelevel = psStringCopy(filelevel);
+    }
+
+    if (workdir) {
+        psFree(detRun->workdir);
+        detRun->workdir = psStringCopy(workdir);
+    }
+
+    if (filter) {
+        psFree(detRun->filter);
+        detRun->filter = psStringCopy(filter);
+    }
+
+    if (!isnan(airmass_min)) {
+        detRun->airmass_min = airmass_min;
+    }
+
+    if (!isnan(airmass_max)) {
+        detRun->airmass_max = airmass_max;
+    }
+
+    if (!isnan(exp_time_min)) {
+        detRun->exp_time_min = exp_time_min;
+    }
+
+    if (!isnan(exp_time_max)) {
+        detRun->exp_time_max = exp_time_max;
+    }
+
+    if (!isnan(ccd_temp_min)) {
+        detRun->ccd_temp_min = ccd_temp_min;
+    }
+
+    if (!isnan(ccd_temp_max)) {
+        detRun->ccd_temp_max = ccd_temp_max;
+    }
+
+    if (!isnan(posang_min)) {
+        detRun->posang_min = posang_min;
+    }
+
+    if (!isnan(posang_max)) {
+        detRun->posang_max = posang_max;
+    }
+
+    if (!isnan(solang_min)) {
+        detRun->solang_min = solang_min;
+    }
+
+    if (!isnan(solang_max)) {
+        detRun->solang_max = solang_max;
+    }
+
+    if (label) {
+        psFree(detRun->label);
+        detRun->label = label;
+    }
+
+    if (registered) {
+        psFree(detRun->registered);
+        detRun->registered = psMemIncrRefCounter(registered);
+    }
+
+    if (time_begin) {
+        psFree(detRun->time_begin);
+        detRun->time_begin = psMemIncrRefCounter(time_begin);
+    }
+
+    if (time_end) {
+        psFree(detRun->time_end);
+        detRun->time_end = psMemIncrRefCounter(time_end);
+    }
+
+    if (use_begin) {
+        psFree(detRun->use_begin);
+        detRun->use_begin = psMemIncrRefCounter(use_begin);
+    }
+
+    if (use_end) {
+        psFree(detRun->use_end);
+        detRun->use_end = psMemIncrRefCounter(use_end);
+    }
+
+    if (reduction) {
+        psFree(detRun->reduction);
+        detRun->reduction = psStringCopy(reduction);
+    }
+
+    // create a metadata to restrict detInputExp's be in in the specified range
+
+    psMetadata *input_filter = psMetadataAlloc();
+
+    // additional restriction on the detInputExp's to be selected
+    PXOPT_COPY_TIME(config->args, input_filter, "-set_input_begin", "dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, input_filter, "-set_input_end", "dateobs", "<");
+
+    PXOPT_LOOKUP_BOOL(only_accepted, config->args, "-only_accepted", false); // optional
+
+    // start a transaction so we don't end up with childlessed det_ids
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(input_filter);
+        psFree(detRun);
+        return false;
+    }
+
+    if (!detRunInsertObject(config->dbh, detRun)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psFree(input_filter);
+        psFree(detRun);
+        return false;
+    }
+    psFree(detRun);
+
+    // get the det_id
+    psS64 newDet_id = psDBLastInsertID(config->dbh);
+
+    psString query = pxDataGet("dettool_definebydetrun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (input_filter->list->n) {
+        psString whereClause = psDBGenerateWhereConditionSQL(input_filter, "rawExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(input_filter);
+
+    if (only_accepted) {
+        psString whereClause = NULL;
+	psStringAppend(&whereClause, " AND accept = 1");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, (long long) newDet_id, (long long) det_id, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    // point of no return for det_id creation
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // print the new det_id
+    psArray *newDetRuns = NULL;
+    {
+        psMetadata *where = psMetadataAlloc();
+        psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", newDet_id);
+        newDetRuns = psDBSelectRows(config->dbh, "detRun", where, 0);
+        psFree(where);
+    }
+    if (!newDetRuns) {
+        psError(PS_ERR_UNKNOWN, false, "can't find the detRun we just created");
+        return false;
+    }
+    // sanity check the result... we should have only found one det_id
+    if (psArrayLength(newDetRuns) != 1) {
+        psAbort("found more then one detRun matching det_id %" PRId64 " (this should not happen)", newDet_id);
+        return false;                   // unreachable
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, newDetRuns, "detRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(newDetRuns);
+        return false;
+    }
+    psFree(newDetRuns);
+
+    return true;
+}
+
+static bool runsMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_type", "det_type", "==");
+    PXOPT_COPY_S64(config->args, where, "-det_id",   "det_id", "==");
+
+    PXOPT_COPY_STR(config->args, where, "-inst", "camera", "==");
+    PXOPT_COPY_STR(config->args, where, "-mode", "mode", "==");
+    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter", "filter", "==");
+
+    // airmass_min  < airmass  < airmass_max
+    PXOPT_COPY_F32(config->args, where, "-airmass", "airmass_min", "<=");
+    PXOPT_COPY_F32(config->args, where, "-airmass", "airmass_max", ">=");
+
+    // exp_time_min < exp_time < exp_time_max
+    PXOPT_COPY_F32(config->args, where, "-exp_time", "exp_time_min", "<=");
+    PXOPT_COPY_F32(config->args, where, "-exp_time", "exp_time_max", ">=");
+
+    // ccd_temp_min < ccd_temp < ccd_temp_max
+    PXOPT_COPY_F32(config->args, where, "-ccd_temp", "ccd_temp_min", "<=");
+    PXOPT_COPY_F32(config->args, where, "-ccd_temp", "ccd_temp_max", ">=");
+
+    PXOPT_COPY_F64(config->args, where, "-posang", "posang_min", "<=");
+    PXOPT_COPY_F64(config->args, where, "-posang", "posang_max", ">=");
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(active, config->args, "-active", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psString query = pxDataGet("dettool_runs.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list) && active) {
+        psString whereClause = psDBGenerateWhereSQL(where, "detRun");
+        psStringAppend(&query, " %s AND (detRun.state = 'run' OR detRun.state = 'stop' OR detRun.state = 'register')", whereClause);
+        psFree(whereClause);
+    }
+    if (psListLength(where->list) && !active) {
+        psString whereClause = psDBGenerateWhereSQL(where, "detRun");
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    if (!psListLength(where->list) && active) {
+        psStringAppend(&query, " WHERE (detRun.state = 'run' OR detRun.state = 'stop' OR detRun.state = 'register')");
+    }
+    psFree (where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *runs = p_psDBFetchResult(config->dbh);
+    if (!runs) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!psArrayLength(runs)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(runs);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, runs, "detRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(runs);
+        return false;
+    }
+
+    psFree(runs);
+
+    return true;
+}
+
+static bool childlessrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_type", "det_type", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find the exp_id of all the exposures that we want to queue up.
+    psString query = pxDataGet("dettool_childlessrun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detRun");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree (where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static detInputExpRow *rawDetrenTodetInputExpRow(rawExpRow *rawExp, psS64 det_id, psS32 iteration)
+{
+    PS_ASSERT_PTR_NON_NULL(rawExp, NULL);
+
+    return detInputExpRowAlloc(
+        det_id,
+        iteration,
+        rawExp->exp_id,
+        true            // use
+    );
+}
+
+static bool inputMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",    "exp_id", "==");
+
+    psString query = pxDataGet("dettool_input.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, "detInputExp");
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree (where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detInputExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool rawMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    psString query = pxDataGet("dettool_raw.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "rawImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree (where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "rawImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool updatedetrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_BOOL(again, config->args, "-again", false);
+    PXOPT_LOOKUP_STR(state, config->args, "-state", false, false);
+
+    // either -rerun or -state must be specified
+    if (!(again || state)) {
+        psError(PS_ERR_UNKNOWN, true, "either -again or -state must be specified");
+        return false;
+    }
+    if (again && state) {
+        psError(PS_ERR_UNKNOWN, true, "-accept and -reject are exclusive");
+        return false;
+    }
+
+    if (state) {
+        // set detRun.state to state
+        return setDetRunState(config, det_id, state);
+    }
+
+    // else
+    // -again
+    if (!startNewIteration(config, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to start new iteration");
+        return false;
+    }
+
+    return true;
+}
+
+// used by updatedetrunMode 
+bool startNewIteration(pxConfig *config, psS64 det_id)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psString query = pxDataGet("dettool_start_new_iteration.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // restrict the update only to the specified det_id
+    psStringAppend(&query, " WHERE det_id = %" PRId64 , det_id);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psError(PS_ERR_UNKNOWN, false, "det_id %" PRId64 " not found", det_id);
+        psFree(output);
+        return false;
+    }
+
+    // start a transaction so we don't end up with an incremented iteration
+    // count but no detInputExps
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+
+    // up the detRuns iteration count for for the single det_id we are
+    // operating on
+    // XXX this will have to changed in order to support multiple det_ids with
+    // a single invocation of this functions
+    psS32 newIteration = incrementIteration(config, det_id);
+    if (!newIteration) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psFree(output);
+        return false;
+    }
+
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i];
+        bool status = false;
+        psS64 exp_id = psMetadataLookupS64(&status, row, "exp_id");
+        if (!status) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for exp_id");
+            psFree(output);
+            return false;
+        }
+        bool accept = psMetadataLookupBool(&status, row, "accept");
+        if (!status) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for accept");
+            psFree(output);
+            return false;
+        }
+
+        // detResidExp.include is used to set detInputExp.include
+        if (!detInputExpInsert(
+                    config->dbh,
+                    det_id,
+                    newIteration,
+                    exp_id,
+                    accept
+                )
+        ) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    // point of no return for det_id creation
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool rerunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // det_id is required
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+
+    // we have to support multipe exp_ids
+    psMetadataItem *item = psMetadataLookup(config->args, "-exp_id");
+    if (!item) {
+        // this shouldn't actually happen when using psArgs
+        psError(PS_ERR_UNKNOWN, true, "-exp_id is required");
+        return false;
+    }
+
+    psList *exp_id_list = item->data.list;
+    psMetadata *where = psMetadataAlloc();
+    // make sure that -exp_id was parsed correctly
+    // XXX this can be removed someday
+    if (item->type == PS_DATA_METADATA_MULTI) {
+        psListIterator *iter = psListIteratorAlloc(item->data.list, 0, false);
+        psMetadataItem *mItem = NULL;
+        while ((mItem = psListGetAndIncrement(iter))) {
+            psString exp_id = mItem->data.V;
+            // if exp_id is NULL then it means that -exp_id has not been
+            // specified
+            if (!exp_id) {
+                psError(PS_ERR_UNKNOWN, true,
+                        "at least one -exp_id is required");
+                psFree(where);
+                return false;
+            }
+
+            if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_id",
+                        PS_META_DUPLICATE_OK, "==", exp_id)) {
+                psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+                psFree(iter);
+                psFree(where);
+                return false;
+            }
+        }
+        psFree(iter);
+    } else {
+        psAbort("-exp_id was not parsed correctly (this should not happen");
+    }
+
+    // check that the specified exp_ids actually exist in the iteration zero
+    // detInputExp set
+
+    // add the det_id & iteration == 0 to the where clause
+    if (!psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(where);
+        return false;
+    }
+    if (!psMetadataAddS32(where, PS_LIST_TAIL, "iteration", 0, "==", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(where);
+        return false;
+    }
+
+    psArray *detrendExps = detInputExpSelectRowObjects(config->dbh, where, 0);
+    psFree(where);
+    if (!detrendExps) {
+        psError(PS_ERR_UNKNOWN, false, "no rawExp rows found");
+        psFree(where);
+        return false;
+    }
+
+    // build a hash for the valid exp_ids
+    psHash *valid_exp_ids = psHashAlloc(psArrayLength(detrendExps));
+    for (long i = 0; i < psArrayLength(detrendExps); i++) {
+        psString exp_idStr = psDBIntToString(((detInputExpRow *)detrendExps->data[i])->exp_id);
+        psHashAdd(valid_exp_ids, exp_idStr, detrendExps->data[i]);
+        psFree(exp_idStr);
+    }
+    psFree(detrendExps);
+
+    // start a transaction so we don't end up with an incremented iteration
+    // count but no detInputExps
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(valid_exp_ids);
+        return false;
+    }
+
+    // up the detRuns iteration count
+    psS32 newIteration = incrementIteration(config, det_id);
+    if (!newIteration) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psFree(valid_exp_ids);
+        return false;
+    }
+
+    // check exp_ids and build up an array of new detInputExp rows at the same
+    // time
+    psListIterator *iter = psListIteratorAlloc(exp_id_list, 0, false);
+    psMetadataItem *mItem = NULL;
+    psArray *newInputExps = psArrayAllocEmpty(psListLength(exp_id_list));
+    while ((mItem = psListGetAndIncrement(iter))) {
+        detInputExpRow *inputExp = psHashLookup(valid_exp_ids,
+                (char *)mItem->data.V);
+        if (!inputExp) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            // invalid exp_id
+            psError(PS_ERR_UNKNOWN, false, "exp_id %s is invalid for det_id %" PRId64,
+                    (char *)mItem->data.V, det_id);
+            psFree(iter);
+            psFree(valid_exp_ids);
+            return false;
+        }
+        detInputExpRow *newInputExp = detInputExpRowAlloc(
+	    det_id,
+            newIteration,
+            inputExp->exp_id,
+            true   // use
+        );
+        psArrayAdd(newInputExps, 0, newInputExp);
+        psFree(newInputExp);
+    }
+    psFree(iter);
+    psFree(valid_exp_ids);
+
+    for (long i = 0; i < psArrayLength(newInputExps); i++) {
+        if (!detInputExpInsertObject(config->dbh, newInputExps->data[i])) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psFree(newInputExps);
+            return false;
+        }
+    }
+    psFree(newInputExps);
+
+    // point of no return for det_id creation
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool register_detrendMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(det_type, config->args, "-det_type", true, false); // required
+    PXOPT_LOOKUP_STR(filelevel, config->args, "-filelevel", true, false); // required
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", false, false);
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", false, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", false, false);
+    PXOPT_LOOKUP_STR(exp_type, config->args, "-exp_type", false, false);
+    PXOPT_LOOKUP_STR(filter, config->args, "-filter", false, false);
+    PXOPT_LOOKUP_F32(airmass_min, config->args, "-airmass_min", false, false);
+    PXOPT_LOOKUP_F32(airmass_max, config->args, "-airmass_max", false, false);
+    PXOPT_LOOKUP_F32(exp_time_min, config->args, "-exp_time_min", false, false);
+    PXOPT_LOOKUP_F32(exp_time_max, config->args, "-exp_time_max", false, false);
+    PXOPT_LOOKUP_F32(ccd_temp_min, config->args, "-ccd_temp_min", false, false);
+    PXOPT_LOOKUP_F32(ccd_temp_max, config->args, "-ccd_temp_max", false, false);
+    PXOPT_LOOKUP_F64(posang_min, config->args, "-posang_min", false, false);
+    PXOPT_LOOKUP_F64(posang_max, config->args, "-posang_max", false, false);
+    PXOPT_LOOKUP_F64(solang_min, config->args, "-solang_min", false, false);
+    PXOPT_LOOKUP_F64(solang_max, config->args, "-solang_max", false, false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+    PXOPT_LOOKUP_TIME(time_begin, config->args, "-time_begin", false, false);
+    PXOPT_LOOKUP_TIME(time_end, config->args, "-time_end", false, false);
+    PXOPT_LOOKUP_TIME(use_begin, config->args, "-use_begin", false, false);
+    PXOPT_LOOKUP_TIME(use_end, config->args, "-use_end", false, false);
+    PXOPT_LOOKUP_S64(ref_det_id, config->args, "-ref_det_id", false, false);
+    PXOPT_LOOKUP_S32(ref_iter, config->args, "-ref_iter", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!detRunInsert(config->dbh,
+                      0,            // det_id
+                      0,            // the iteration is fixed at 0
+                      det_type,
+                      "register",   // mode is required to be "register"
+                      "register",   // state
+                      filelevel,
+                      workdir,
+                      camera,
+                      telescope,
+                      exp_type,
+                      NULL,
+                      filter,
+                      airmass_min,
+                      airmass_max,
+                      exp_time_min,
+                      exp_time_max,
+                      ccd_temp_min,
+                      ccd_temp_max,
+                      posang_min,
+                      posang_max,
+                      registered,
+                      time_begin,
+                      time_end,
+                      use_begin,
+                      use_end,
+                      solang_min,
+                      solang_max,
+                      label,
+		      ref_det_id,
+		      ref_iter
+            )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        return false;
+    }
+
+    // print the new detRun
+    psS64 det_id = psDBLastInsertID(config->dbh);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psArray *detRuns = NULL;
+    {
+        psMetadata *where = psMetadataAlloc();
+        psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", det_id);
+        detRuns = psDBSelectRows(config->dbh, "detRun", where, 0);
+        psFree(where);
+    }
+    if (!detRuns) {
+        psError(PS_ERR_UNKNOWN, false, "can't find the detRun we just created");
+        return false;
+    }
+    // sanity check results
+    if (psArrayLength(detRuns) != 1) {
+        psAbort("found more then one detRun matching det_id %" PRId64 "(this should not happen)", det_id);
+        return false;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, detRuns, "detRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(detRuns);
+        return false;
+    }
+    psFree(detRuns);
+
+    return true;
+}
+
+// NOTE : this function is also used by addcorrectimfileMode
+bool register_detrend_imfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false); // required
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", true, false); // required
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+
+    if (!detRegisteredImfileInsert(config->dbh,
+				   det_id,
+				   0,  // the iteration is fixed at 0
+				   class_id,
+				   uri,
+				   bg,
+				   bg_stdev,
+				   bg_mean_stdev,
+				   user_1,
+				   user_2,
+				   user_3,
+				   user_4,
+				   user_5,
+				   path_base,
+				   "full",				   
+				   0       // fault code
+	    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static psS32 incrementIteration(pxConfig *config, psS64 det_id)
+{
+    // this function returns zero on error
+    PS_ASSERT_PTR_NON_NULL(config, 0);
+
+    char *query = "UPDATE detRun SET iteration = iteration + 1 WHERE det_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, det_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to increment iteration for det_id %" PRId64, det_id);
+        return 0;
+    }
+
+    psMetadata *where = psMetadataAlloc();
+    if (!psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(where);
+        return 0;
+    }
+
+    psArray *detRuns = detRunSelectRowObjects(config->dbh, where, 0);
+    psFree(where);
+    if (!detRuns) {
+        psError(PS_ERR_UNKNOWN, false, "no detRun rows found");
+        return 0;
+    }
+
+    // sanity check the database
+    if (psArrayLength(detRuns) != 1) {
+        // this should no happen
+        psAbort(                "database query return too many rows (this should not happen");
+    }
+
+    psS32 newIteration = ((detRunRow *)detRuns->data[0])->iteration;
+    psFree(detRuns);
+
+    return newIteration;
+}
+
+bool setDetRunState(pxConfig *config, psS64 det_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!(
+            (strncmp(state, "run", 4) == 0)
+            || (strncmp(state, "stop", 5) == 0)
+            || (strncmp(state, "drop", 5) == 0)
+            || (strncmp(state, "register", 4) == 0)
+        )
+    ) {
+        psError(PS_ERR_UNKNOWN, false,
+                "invalid detRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE detRun SET state = '%s' WHERE det_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, state, det_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for det_id %" PRId64, det_id);
+        return false;
+    }
+
+    return true;
+}
+
+bool isValidDataState (const char *data_state) {
+
+    // check that state is a valid string value
+    if (!strncmp(data_state, "run", 4)) return true;
+    if (!strncmp(data_state, "stop", 5)) return true;
+    if (!strncmp(data_state, "drop", 5)) return true;
+    if (!strncmp(data_state, "register", 4)) return true;
+
+    psError(PS_ERR_UNKNOWN, true, "invalid data state: %s", data_state);
+    return false;
+}
+
+bool isValidMode(pxConfig *config, const char *mode)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(mode, false);
+
+    // check that state is a valid string value
+    if (!strncmp(mode, "master", 7)) return true;
+    if (!strncmp(mode, "verify", 7)) return true;
+
+    psError(PS_ERR_UNKNOWN, false, "invalid detRun mode: %s", mode);
+    return false;
+}
+
+
+bool setProcessedImfileDataState(pxConfig *config, psS64 det_id, psS64 exp_id, const char *class_id, const char *data_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(data_state, false);
+    PS_ASSERT_PTR_NON_NULL(class_id, false);
+
+    if (!isValidDataState (data_state)) return false;
+
+    char *query = "UPDATE detProcessedImfile SET data_state = '%s'"
+	" WHERE det_id = %" PRId64
+	" AND exp_id = %" PRId64 
+	" AND class_id = '%s'";
+    if (!p_psDBRunQuery(config->dbh, query, data_state, det_id, exp_id, class_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for det_id %" PRId64 ", exp_id %" PRId64 ", class_id %s", 
+		det_id, exp_id, class_id);
+        return false;
+    }
+
+    return true;
+}
+
+bool setProcessedExpDataState(pxConfig *config, psS64 det_id, psS64 exp_id, const char *data_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(data_state, false);
+
+    if (!isValidDataState (data_state)) return false;
+
+    char *query = "UPDATE detProcessedExp SET data_state = '%s'"
+	" WHERE det_id = %" PRId64
+	" AND exp_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, data_state, det_id, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for det_id %" PRId64 ", exp_id %" PRId64, 
+		det_id, exp_id);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool setStackedImfileDataState(pxConfig *config, psS64 det_id, psS32 iteration, const char *class_id, const char *data_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(class_id, false);
+    PS_ASSERT_PTR_NON_NULL(data_state, false);
+
+    if (!isValidDataState (data_state)) return false;
+
+    char *query = "UPDATE detStackedImfile SET data_state = '%s'"
+	" WHERE det_id = %" PRId64
+	" AND iteration = %" PRId32
+	" AND class_id = %s";
+    if (!p_psDBRunQuery(config->dbh, query, data_state, det_id, iteration, class_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for det_id %" PRId64 ", iteration %" PRId32 "class_id %s", 
+		det_id, iteration, class_id);
+        return false;
+    }
+
+    return true;
+}
+
+bool setNormStatImfileDataState(pxConfig *config, psS64 det_id, psS32 iteration, const char *class_id, const char *data_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(class_id, false);
+    PS_ASSERT_PTR_NON_NULL(data_state, false);
+
+    if (!isValidDataState (data_state)) return false;
+
+    char *query = "UPDATE detNormalizedStatImfile SET data_state = '%s'"
+	" WHERE det_id = %" PRId64
+	" AND iteration = %" PRId32
+	" AND class_id = %s";
+    if (!p_psDBRunQuery(config->dbh, query, data_state, det_id, iteration)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for det_id %" PRId64 ", iteration %" PRId32, 
+		det_id, iteration);
+        return false;
+    }
+
+    return true;
+}
+
+bool setNormImfileDataState(pxConfig *config, psS64 det_id, psS32 iteration, const char *class_id, const char *data_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(class_id, false);
+    PS_ASSERT_PTR_NON_NULL(data_state, false);
+
+    if (!isValidDataState (data_state)) return false;
+
+    char *query = "UPDATE detNormalizedImfile SET data_state = '%s'"
+	" WHERE det_id = %" PRId64
+	" AND iteration = %" PRId32
+	" AND class_id = %s";
+    if (!p_psDBRunQuery(config->dbh, query, data_state, det_id, iteration, class_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for det_id %" PRId64 ", iteration %" PRId32 " class %s", 
+		det_id, iteration, class_id);
+        return false;
+    }
+
+    return true;
+}
+
+bool setNormExpDataState(pxConfig *config, psS64 det_id, psS32 iteration, const char *data_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(data_state, false);
+
+    if (!isValidDataState (data_state)) return false;
+
+    char *query = "UPDATE detNormalizedExp SET data_state = '%s'"
+	" WHERE det_id = %" PRId64
+	" AND iteration = %" PRId32;
+    if (!p_psDBRunQuery(config->dbh, query, data_state, det_id, iteration)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for det_id %" PRId64 ", iteration %" PRId32, 
+		det_id, iteration);
+        return false;
+    }
+
+    return true;
+}
+
+bool setResidImfileDataState(pxConfig *config, psS64 det_id, psS32 iteration, psS64 exp_id, const char *class_id, const char *data_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(data_state, false);
+    PS_ASSERT_PTR_NON_NULL(class_id, false);
+
+    if (!isValidDataState (data_state)) return false;
+
+    char *query = "UPDATE detResidImfile SET data_state = '%s'"
+	" WHERE det_id = %" PRId64
+	" AND iteration = %" PRId32 
+	" AND exp_id = %" PRId64 
+	" AND class_id = '%s'";
+    if (!p_psDBRunQuery(config->dbh, query, data_state, det_id, iteration, exp_id, class_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for det_id %" PRId64 ", iteration %" PRId32 ", exp_id %" PRId64 ", class_id %s", 
+		det_id, iteration, exp_id, class_id);
+        return false;
+    }
+
+    return true;
+}
+
+bool setResidExpDataState(pxConfig *config, psS64 det_id, psS32 iteration, psS64 exp_id, const char *data_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(data_state, false);
+
+    if (!isValidDataState (data_state)) return false;
+
+    char *query = "UPDATE detResidExp SET data_state = '%s'"
+	" WHERE det_id = %" PRId64
+	" AND iteration = %" PRId32 
+	" AND exp_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, data_state, det_id, iteration, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for det_id %" PRId64 ", iteration %" PRId32 ", exp_id %" PRId64, 
+		det_id, iteration, exp_id);
+        return false;
+    }
+
+    return true;
+}
+
+#if 0
+// XXX this function was left in commented as this method may be useful in the
+// future
+static psArray *validDetInputClassIds(pxConfig *config, const char *det_id)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    // det_id is input as a string because the fact that it is an integer
+    // is just a database impliementation detail.
+    PS_ASSERT_PTR_NON_NULL(det_id, NULL);
+
+    psArray *rawImfiles = searchInputImfiles(config, det_id);
+    if (!rawImfiles) {
+        return NULL;
+    }
+
+    psArray *valid_class_ids = NULL;
+    {
+        // All this jumping through hoops is so that we end up with unique
+        // values. PP thinks that making multiple passes through this array and
+        // deleting matched elements would end up being more expensive then a
+        // double sort and stagger scheme. JH thinks it would be cheaper to
+        // just do a unqiue sort and delete.  So this is really just a cheap
+        // hack to avoid implimenting a unique sort function but at least it's
+        // stable.
+        psHash *hash = psHashAlloc(psArrayLength(rawImfiles));
+        for (long i = 0; i < psArrayLength(rawImfiles); i++) {
+            psHashAdd(hash,
+                ((rawImfileRow *)rawImfiles->data[i])->class_id,
+                ((rawImfileRow *)rawImfiles->data[i])->class_id
+            );
+        }
+        valid_class_ids = psHashToArray(hash);
+        psFree(hash);
+    }
+    psFree(rawImfiles);
+
+    return valid_class_ids;
+}
+
+static psArray *searchInputImfiles(pxConfig *config, const char *det_id)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    // det_id is input as a string because the fact that it is an integer
+    // is just a database impliementation detail.
+    PS_ASSERT_PTR_NON_NULL(det_id, NULL);
+
+    psArray *inputExps = NULL;
+    {
+        psMetadata *where = psMetadataAlloc();
+        if (!psMetadataAddS32(where, PS_LIST_TAIL, "det_id", 0, "==",
+                (psS32)atoi(det_id))) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+            psFree(where);
+            return NULL;
+        }
+        inputExps = detInputExpSelectRowObjects(config->dbh, where, 0);
+        psFree(where);
+    }
+    if (!inputExps) {
+        psError(PS_ERR_UNKNOWN, false, "no detInputExp rows found");
+        return NULL;
+    }
+
+    // find rawImfiles associated with detInputExps
+    psArray *rawImfiles = NULL;
+    {
+        psMetadata *where = psMetadataAlloc();
+        for (long i = 0; i < psArrayLength(inputExps); i++) {
+            if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_id",
+                    PS_META_DUPLICATE_OK, "==",
+                    ((detInputExpRow *)inputExps->data[i])->exp_id)) {
+                psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+                psFree(inputExps);
+                psFree(where);
+                return NULL;
+            }
+        }
+        psFree(inputExps);
+        rawImfiles = rawImfileSelectRowObjects(config->dbh, where, 0);
+        // XXX this really should be sorted for uniqueness
+        psFree(where);
+    }
+    if (!rawImfiles) {
+        psError(PS_ERR_UNKNOWN, false, "no rawImfile rows found");
+        return NULL;
+    }
+
+    return rawImfiles;
+}
+#endif
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool.h	(revision 22322)
@@ -0,0 +1,225 @@
+/*
+ * dettool.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef DETTOOL_H
+#define DETTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    DETTOOL_MODE_NONE           = PXTOOL_MODE_NONE,
+    DETTOOL_MODE_PENDING,
+    DETTOOL_MODE_DEFINEBYTAG,
+    DETTOOL_MODE_DEFINEBYQUERY,
+    DETTOOL_MODE_DEFINEBYDETRUN,
+    DETTOOL_MODE_MAKECORRECTION,
+    DETTOOL_MODE_TOCORRECTEXP,
+    DETTOOL_MODE_TOCORRECTIMFILE,
+    DETTOOL_MODE_ADDCORRECTIMFILE,
+    DETTOOL_MODE_RUNS,
+    DETTOOL_MODE_CHILDLESSRUN,
+    DETTOOL_MODE_INPUT,
+    DETTOOL_MODE_RAW,
+
+    DETTOOL_MODE_TOPROCESSEDIMFILE,
+    DETTOOL_MODE_ADDPROCESSEDIMFILE,
+    DETTOOL_MODE_PROCESSEDIMFILE,
+    DETTOOL_MODE_REVERTPROCESSEDIMFILE,
+    DETTOOL_MODE_UPDATEPROCESSEDIMFILE,
+    DETTOOL_MODE_PENDINGCLEANUP_PROCESSEDIMFILE,
+    DETTOOL_MODE_DONECLEANUP_PROCESSEDIMFILE,
+
+    DETTOOL_MODE_TOPROCESSEDEXP,
+    DETTOOL_MODE_ADDPROCESSEDEXP,
+    DETTOOL_MODE_PROCESSEDEXP,
+    DETTOOL_MODE_REVERTPROCESSEDEXP,
+    DETTOOL_MODE_UPDATEPROCESSEDEXP,
+    DETTOOL_MODE_PENDINGCLEANUP_PROCESSEDEXP,
+    DETTOOL_MODE_DONECLEANUP_PROCESSEDEXP,
+
+    DETTOOL_MODE_TOSTACKED,
+    DETTOOL_MODE_ADDSTACKED,
+    DETTOOL_MODE_STACKED,
+    DETTOOL_MODE_REVERTSTACKED,
+    DETTOOL_MODE_UPDATESTACKED,
+    DETTOOL_MODE_PENDINGCLEANUP_STACKED,
+    DETTOOL_MODE_DONECLEANUP_STACKED,
+
+    DETTOOL_MODE_TONORMALIZEDSTAT,
+    DETTOOL_MODE_ADDNORMALIZEDSTAT,
+    DETTOOL_MODE_NORMALIZEDSTAT,
+    DETTOOL_MODE_REVERTNORMALIZEDSTAT,
+    DETTOOL_MODE_UPDATENORMALIZEDSTAT,
+    DETTOOL_MODE_PENDINGCLEANUP_NORMALIZEDSTAT,
+    DETTOOL_MODE_DONECLEANUP_NORMALIZEDSTAT,
+
+    DETTOOL_MODE_TONORMALIZE,
+    DETTOOL_MODE_ADDNORMALIZEDIMFILE,
+    DETTOOL_MODE_NORMALIZEDIMFILE,
+    DETTOOL_MODE_REVERTNORMALIZEDIMFILE,
+    DETTOOL_MODE_UPDATENORMALIZEDIMFILE,
+    DETTOOL_MODE_PENDINGCLEANUP_NORMALIZEDIMFILE,
+    DETTOOL_MODE_DONECLEANUP_NORMALIZEDIMFILE,
+
+    DETTOOL_MODE_TONORMALIZEDEXP,
+    DETTOOL_MODE_ADDNORMALIZEDEXP,
+    DETTOOL_MODE_NORMALIZEDEXP,
+    DETTOOL_MODE_REVERTNORMALIZEDEXP,
+    DETTOOL_MODE_UPDATENORMALIZEDEXP,
+    DETTOOL_MODE_PENDINGCLEANUP_NORMALIZEDEXP,
+    DETTOOL_MODE_DONECLEANUP_NORMALIZEDEXP,
+
+    DETTOOL_MODE_TORESIDIMFILE,
+    DETTOOL_MODE_ADDRESIDIMFILE,
+    DETTOOL_MODE_RESIDIMFILE,
+    DETTOOL_MODE_REVERTRESIDIMFILE,
+    DETTOOL_MODE_UPDATERESIDIMFILE,
+    DETTOOL_MODE_PENDINGCLEANUP_RESIDIMFILE,
+    DETTOOL_MODE_DONECLEANUP_RESIDIMFILE,
+
+    DETTOOL_MODE_TORESIDEXP,
+    DETTOOL_MODE_ADDRESIDEXP,
+    DETTOOL_MODE_RESIDEXP,
+    DETTOOL_MODE_REVERTRESIDEXP,
+    DETTOOL_MODE_UPDATERESIDEXP,
+    DETTOOL_MODE_PENDINGCLEANUP_RESIDEXP,
+    DETTOOL_MODE_DONECLEANUP_RESIDEXP,
+
+    DETTOOL_MODE_TODETRUNSUMMARY,
+    DETTOOL_MODE_ADDDETRUNSUMMARY,
+    DETTOOL_MODE_DETRUNSUMMARY,
+    DETTOOL_MODE_REVERTDETRUNSUMMARY,
+    DETTOOL_MODE_UPDATEDETRUNSUMMARY,
+    DETTOOL_MODE_UPDATEDETRUN,
+    DETTOOL_MODE_RERUN,
+    DETTOOL_MODE_REGISTER_DETREND,
+    DETTOOL_MODE_REGISTER_DETREND_IMFILE
+} dettoolMode;
+
+pxConfig *dettoolConfig(pxConfig *config, int argc, char **argv);
+
+// correct
+bool makecorrectionMode(pxConfig *config);
+bool tocorrectexpMode(pxConfig *config);
+bool tocorrectimfileMode(pxConfig *config);
+bool addcorrectimfileMode(pxConfig *config);
+
+// register
+bool register_detrend_imfileMode(pxConfig *config);
+
+// processedimfile
+bool toprocessedimfileMode(pxConfig *config);
+bool addprocessedimfileMode(pxConfig *config);
+bool processedimfileMode(pxConfig *config);
+bool revertprocessedimfileMode(pxConfig *config);
+bool updateprocessedimfileMode(pxConfig *config);
+bool pendingcleanup_processedimfileMode(pxConfig *config);
+bool donecleanup_processedimfileMode(pxConfig *config);
+
+// processedexp
+bool toprocessedexpMode(pxConfig *config);
+bool addprocessedexpMode(pxConfig *config);
+bool processedexpMode(pxConfig *config);
+bool revertprocessedexpMode(pxConfig *config);
+bool updateprocessedexpMode(pxConfig *config);
+bool pendingcleanup_processedexpMode(pxConfig *config);
+bool donecleanup_processedexpMode(pxConfig *config);
+
+// stackedimfile
+bool tostackedMode(pxConfig *config);
+bool addstackedMode(pxConfig *config);
+bool stackedMode(pxConfig *config);
+bool revertstackedMode(pxConfig *config);
+bool updatestackedMode(pxConfig *config);
+bool pendingcleanup_stackedMode(pxConfig *config);
+bool donecleanup_stackedMode(pxConfig *config);
+
+// normalizedstat
+bool tonormalizedstatMode(pxConfig *config);
+bool addnormalizedstatMode(pxConfig *config);
+bool normalizedstatMode(pxConfig *config);
+bool revertnormalizedstatMode(pxConfig *config);
+bool updatenormalizedstatMode(pxConfig *config);
+bool pendingcleanup_normalizedstatMode(pxConfig *config);
+bool donecleanup_normalizedstatMode(pxConfig *config);
+
+// normalizedimfile
+bool tonormalizeMode(pxConfig *config);
+bool addnormalizedimfileMode(pxConfig *config);
+bool normalizedimfileMode(pxConfig *config);
+bool revertnormalizedimfileMode(pxConfig *config);
+bool updatenormalizedimfileMode(pxConfig *config);
+bool pendingcleanup_normalizedimfileMode(pxConfig *config);
+bool donecleanup_normalizedimfileMode(pxConfig *config);
+
+// normalizedexp
+bool tonormalizedexpMode(pxConfig *config);
+bool addnormalizedexpMode(pxConfig *config);
+bool normalizedexpMode(pxConfig *config);
+bool revertnormalizedexpMode(pxConfig *config);
+bool updatenormalizedexpMode(pxConfig *config);
+bool pendingcleanup_normalizedexpMode(pxConfig *config);
+bool donecleanup_normalizedexpMode(pxConfig *config);
+
+// residimfile
+bool toresidimfileMode(pxConfig *config);
+bool addresidimfileMode(pxConfig *config);
+bool residimfileMode(pxConfig *config);
+bool revertresidimfileMode(pxConfig *config);
+bool updateresidimfileMode(pxConfig *config);
+bool pendingcleanup_residimfileMode(pxConfig *config);
+bool donecleanup_residimfileMode(pxConfig *config);
+
+// residexp
+bool toresidexpMode(pxConfig *config);
+bool addresidexpMode(pxConfig *config);
+bool residexpMode(pxConfig *config);
+bool revertresidexpMode(pxConfig *config);
+bool updateresidexpMode(pxConfig *config);
+bool pendingcleanup_residexpMode(pxConfig *config);
+bool donecleanup_residexpMode(pxConfig *config);
+
+// detrunsummary
+bool todetrunsummaryMode(pxConfig *config);
+bool adddetrunsummaryMode(pxConfig *config);
+bool detrunsummaryMode(pxConfig *config);
+bool revertdetrunsummaryMode(pxConfig *config);
+bool updatedetrunsummaryMode(pxConfig *config);
+
+// other utilities
+bool startNewIteration(pxConfig *config, psS64 det_id);
+bool setDetRunState(pxConfig *config, psS64 det_id, const char *state);
+bool isValidDataState (const char *data_state);
+bool isValidMode(pxConfig *config, const char *mode);
+
+// functions to set the 'data_state' for stages
+bool setProcessedImfileDataState(pxConfig *config, psS64 det_id, psS64 exp_id, const char *class_id, const char *data_state);
+bool setProcessedExpDataState(pxConfig *config, psS64 det_id, psS64 exp_id, const char *data_state);
+bool setStackedImfileDataState(pxConfig *config, psS64 det_id, psS32 iteration, const char *class_id, const char *data_state);
+bool setNormStatImfileDataState(pxConfig *config, psS64 det_id, psS32 iteration, const char *class_id, const char *data_state);
+bool setNormImfileDataState(pxConfig *config, psS64 det_id, psS32 iteration, const char *class_id, const char *data_state);
+bool setNormExpDataState(pxConfig *config, psS64 det_id, psS32 iteration, const char *data_state);
+bool setResidImfileDataState(pxConfig *config, psS64 det_id, psS32 iteration, psS64 exp_id, const char *class_id, const char *data_state);
+bool setResidExpDataState(pxConfig *config, psS64 det_id, psS32 iteration, psS64 exp_id, const char *data_state);
+
+#endif // DETTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettoolConfig.c	(revision 22322)
@@ -0,0 +1,966 @@
+/*
+ * dettoolConfig.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+pxConfig *dettoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    psTime *now = psTimeGetNow(PS_TIME_TAI);
+
+    // -pending
+    // XXX EAM : is this used?  does it make sense?
+    psMetadata *pendingArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exposure ID", 0);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-exp_type",  0,            "search by exposure type", NULL);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-inst",  0,            "search by camera", NULL);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-telescope",  0,            "search by telescope", NULL);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-filter",  0,            "search by filter", NULL);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-uri",  0,            "search by URL", NULL);
+    psMetadataAddBool(pendingArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -definebytag
+    psMetadata *definebytagArgs = psMetadataAlloc();
+    psMetadataAddS64(definebytagArgs, PS_LIST_TAIL, "-exp_id",            PS_META_DUPLICATE_OK,           "include this exposure (multiple OK, required)", 0);
+    psMetadataAddStr(definebytagArgs, PS_LIST_TAIL, "-det_type",  0,            "define the type of detrend run (required)", NULL);
+    psMetadataAddStr(definebytagArgs, PS_LIST_TAIL, "-mode",  0,            "define the mode of this detrend run (master, verify)", "master");
+    psMetadataAddStr(definebytagArgs, PS_LIST_TAIL, "-filelevel",  0,            "define filelevel", NULL);
+    psMetadataAddStr(definebytagArgs, PS_LIST_TAIL, "-workdir",  0,            "define workdir (required)", NULL);
+    psMetadataAddStr(definebytagArgs, PS_LIST_TAIL, "-inst",  0,            "define camera", NULL);
+    psMetadataAddStr(definebytagArgs, PS_LIST_TAIL, "-telescope",  0,            "define telescope", NULL);
+    psMetadataAddStr(definebytagArgs, PS_LIST_TAIL, "-exp_type",  0,            "define exposure type", NULL);
+    psMetadataAddStr(definebytagArgs, PS_LIST_TAIL, "-filter",  0,            "define filter ", NULL);
+    psMetadataAddF32(definebytagArgs, PS_LIST_TAIL, "-airmass_min",  0,            "define min airmass", NAN);
+    psMetadataAddF32(definebytagArgs, PS_LIST_TAIL, "-airmass_max",  0,            "define max airmass", NAN);
+    psMetadataAddF32(definebytagArgs, PS_LIST_TAIL, "-exp_time_min",  0,            "define min exposure time", NAN);
+    psMetadataAddF32(definebytagArgs, PS_LIST_TAIL, "-exp_time_max",  0,            "define max exposure time", NAN);
+    psMetadataAddF64(definebytagArgs, PS_LIST_TAIL, "-ccd_temp_min",  0,            "define min ccd tempature", NAN);
+    psMetadataAddF64(definebytagArgs, PS_LIST_TAIL, "-ccd_temp_max",  0,            "define max ccd tempature", NAN);
+    psMetadataAddF64(definebytagArgs, PS_LIST_TAIL, "-posang_min",  0,            "define min rotator position angle", NAN);
+    psMetadataAddF64(definebytagArgs, PS_LIST_TAIL, "-posang_max",  0,            "define max rotator position angle", NAN);
+    psMetadataAddF64(definebytagArgs, PS_LIST_TAIL, "-solang_min",  0,            "define min solar angle", NAN);
+    psMetadataAddF64(definebytagArgs, PS_LIST_TAIL, "-solang_max",  0,            "define max solar angle", NAN);
+    psMetadataAddTime(definebytagArgs, PS_LIST_TAIL, "-registered",  0,            "time detrend run was registered", now);
+    psMetadataAddTime(definebytagArgs, PS_LIST_TAIL, "-time_begin",  0,            "detrend applies to exposures taken during this period", NULL);
+    psMetadataAddTime(definebytagArgs, PS_LIST_TAIL, "-time_end",  0,            "detrend applies to exposures taken during this period", NULL);
+    psMetadataAddTime(definebytagArgs, PS_LIST_TAIL, "-use_begin",  0,            "start of detrend run applicable period", NULL);
+    psMetadataAddTime(definebytagArgs, PS_LIST_TAIL, "-use_end",  0,            "end of detrend run applicable period", NULL);
+    psMetadataAddStr(definebytagArgs, PS_LIST_TAIL, "-reduction",  0,            "define reduction class for processing", NULL);
+    psMetadataAddStr(definebytagArgs, PS_LIST_TAIL, "-label",  0,            "define detrun label", NULL);
+    psMetadataAddBool(definebytagArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    psMetadataAddS64(definebytagArgs, PS_LIST_TAIL, "-ref_det_id", 0,           "define reference detrend (verify mode only)", 0);
+    psMetadataAddS32(definebytagArgs, PS_LIST_TAIL, "-ref_iter", 0,           "define reference detrend (verify mode only)", -1);
+
+    // -definebyquery
+    psMetadata *definebyqueryArgs = psMetadataAlloc();
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-det_type",  0,            "define the type of detrend run (required)", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-mode",  0,            "define the mode of this detrend run (master, verify)", "master");
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-filelevel",  0,            "define filelevel", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-workdir",  0,            "define workdir (required)", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-inst",  0,            "define camera (required)", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-telescope",  0,            "define telescope", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-filter",  0,            "define filter ", NULL);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-airmass_min",  0,            "define min airmass", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-airmass_max",  0,            "define max airmass", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-exp_time_min",  0,            "define min exposure time", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-exp_time_max",  0,            "define max exposure time", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-ccd_temp_min",  0,            "define min ccd tempature", NAN);
+    psMetadataAddF64(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);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-solang_min",  0,            "define min solar angle", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-solang_max",  0,            "define max solar angle", NAN);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-registered",  0,            "time detrend run was registered", now);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-time_begin",  0,            "detrend applies to exposures taken during this period", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-time_end",  0,            "detrend applies to exposures taken during this period", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-use_begin",  0,            "start of detrend run applicable period", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-use_end",  0,            "end of detrend run applicable period", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_exp_type",  0,            "search for exp_type", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_inst",  0,            "search for camera", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_telescope",  0,            "search for telescope", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_filter",  0,            "search for filter", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_uri",  0,            "search for uri", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-select_dateobs_begin", 0,            "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-select_dateobs_end", 0,            "search for exposures by time (<)", NULL);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_airmass_min",  0,            "define min airmass", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_airmass_max",  0,            "define max airmass", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_sat_pixel_frac_max",  0,            "define max fraction of saturated pixels", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_exp_time_min",  0,            "define min exposure time", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_exp_time_max",  0,            "define max exposure time", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_ccd_temp_min",  0,            "define min ccd tempature", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_ccd_temp_max",  0,            "define max ccd tempature", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_pon_time_min",  0,            "define min power-on time", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_pon_time_max",  0,            "define max power-on time", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_posang_min",  0,            "define min rotator position angle", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_posang_max",  0,            "define max rotator position angle", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_solang_min",  0,            "define min solar angle", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_solang_max",  0,            "define max solar angle", NAN);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-pretend",  0,            "print the exposures that would be included in the detrend run and exit", false);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-reduction",  0,            "define reduction class for processing", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label",  0,            "define detrun label", NULL);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-random_subset",  0,            "use a random subset of elements", false);
+    psMetadataAddS32(definebyqueryArgs, PS_LIST_TAIL, "-random_limit",  0,            "use this number of random elements", 20);
+    psMetadataAddS64(definebyqueryArgs, PS_LIST_TAIL, "-ref_det_id", 0,           "define reference detrend (verify mode only)", 0);
+    psMetadataAddS32(definebyqueryArgs, PS_LIST_TAIL, "-ref_iter", 0,           "define reference detrend (verify mode only)", -1);
+
+    // -definebydetrun
+    psMetadata *definebydetrunArgs = psMetadataAlloc();
+    psMetadataAddS64(definebydetrunArgs, PS_LIST_TAIL, "-det_id",  0,            "det ID to base a new detRun on (required)", 0);
+    psMetadataAddStr(definebydetrunArgs, PS_LIST_TAIL, "-set_det_type",  0,            "define the type of detrend run", NULL);
+    psMetadataAddStr(definebydetrunArgs, PS_LIST_TAIL, "-set_mode",  0,            "define the mode of this detrend run", "master");
+    psMetadataAddS64(definebydetrunArgs, PS_LIST_TAIL, "-ref_det_id",  0,            "reference det ID ('verify' run only)", 0);
+    psMetadataAddS32(definebydetrunArgs, PS_LIST_TAIL, "-ref_iter",  0,            "reference iteration ('verify' run only)", -1);
+    psMetadataAddStr(definebydetrunArgs, PS_LIST_TAIL, "-set_exp_type",  0,            "define exposure type", NULL);
+    psMetadataAddStr(definebydetrunArgs, PS_LIST_TAIL, "-set_filelevel",  0,            "define filelevel", NULL);
+    psMetadataAddStr(definebydetrunArgs, PS_LIST_TAIL, "-set_workdir",  0,            "define workdir", NULL);
+    psMetadataAddStr(definebydetrunArgs, PS_LIST_TAIL, "-set_filter",  0,            "define filter ", NULL);
+    psMetadataAddF32(definebydetrunArgs, PS_LIST_TAIL, "-set_airmass_min",  0,            "define airmass", NAN);
+    psMetadataAddF32(definebydetrunArgs, PS_LIST_TAIL, "-set_airmass_max",  0,            "define airmass", NAN);
+    psMetadataAddF32(definebydetrunArgs, PS_LIST_TAIL, "-set_exp_time_min",  0,            "define exposure time", NAN);
+    psMetadataAddF32(definebydetrunArgs, PS_LIST_TAIL, "-set_exp_time_max",  0,            "define exposure time", NAN);
+    psMetadataAddF64(definebydetrunArgs, PS_LIST_TAIL, "-set_ccd_temp_min",  0,            "define ccd tempature", NAN);
+    psMetadataAddF64(definebydetrunArgs, PS_LIST_TAIL, "-set_ccd_temp_max",  0,            "define ccd tempature", NAN);
+    psMetadataAddF64(definebydetrunArgs, PS_LIST_TAIL, "-set_posang_min",  0,            "define rotator position angle", NAN);
+    psMetadataAddF64(definebydetrunArgs, PS_LIST_TAIL, "-set_posang_max",  0,            "define rotator position angle", NAN);
+    psMetadataAddF64(definebydetrunArgs, PS_LIST_TAIL, "-set_solang_min",  0,            "define solar angle", NAN);
+    psMetadataAddF64(definebydetrunArgs, PS_LIST_TAIL, "-set_solang_max",  0,            "define solar angle", NAN);
+    psMetadataAddTime(definebydetrunArgs, PS_LIST_TAIL, "-set_registered",  0,            "time detrend run was registered", now);
+    psMetadataAddTime(definebydetrunArgs, PS_LIST_TAIL, "-set_time_begin",  0,            "start of period to apply detrend too", NULL);
+    psMetadataAddTime(definebydetrunArgs, PS_LIST_TAIL, "-set_time_end",  0,            "end of period to apply detrend too", NULL);
+    psMetadataAddTime(definebydetrunArgs, PS_LIST_TAIL, "-set_use_begin",  0,            "start of detrend run applicable period", NULL);
+    psMetadataAddTime(definebydetrunArgs, PS_LIST_TAIL, "-set_use_end",  0,            "end of detrend run applicable period", NULL);
+    psMetadataAddStr(definebydetrunArgs, PS_LIST_TAIL, "-set_reduction",  0,            "define reduction class for processing", NULL);
+    psMetadataAddStr(definebydetrunArgs, PS_LIST_TAIL, "-set_label",  0,            "define detrun label", NULL);
+    psMetadataAddBool(definebydetrunArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+    psMetadataAddStr(definebydetrunArgs, PS_LIST_TAIL, "-set_input_begin", 0,     "require input exp to be in this period", NULL);
+    psMetadataAddStr(definebydetrunArgs, PS_LIST_TAIL, "-set_input_end", 0,       "require input exp to be in this period", NULL);
+    psMetadataAddBool(definebydetrunArgs, PS_LIST_TAIL, "-only_accepted", 0,       "limit input exp to those accepted in the source detRun", false);
+    psMetadataAddS32(definebydetrunArgs, PS_LIST_TAIL, "-iteration",  0,            "det ID to base a new detRun on (required)", -1);
+
+    // *** these are in dettool_correction.c ***
+    // -makecorrection
+    psMetadata *makecorrectionArgs = psMetadataAlloc();
+    psMetadataAddS64(makecorrectionArgs, PS_LIST_TAIL, "-det_id",  0,     "detRun det_id to be corrected (required)", 0);
+    psMetadataAddS32(makecorrectionArgs, PS_LIST_TAIL, "-iteration",  0,  "detRun iteration to be corrected (required)", -1);;
+    psMetadataAddBool(makecorrectionArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -tocorrectexp
+    psMetadata *tocorrectexpArgs = psMetadataAlloc();
+    psMetadataAddU64(tocorrectexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tocorrectexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -tocorrectimfile
+    psMetadata *tocorrectimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tocorrectimfileArgs, PS_LIST_TAIL, "-det_id", 0,            "search for detrend ID", 0);
+    psMetadataAddU64(tocorrectimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tocorrectimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addcorrectimfile
+    psMetadata *addcorrectimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addcorrectimfileArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddStr(addcorrectimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search for class ID (required)", NULL);
+    psMetadataAddStr(addcorrectimfileArgs, PS_LIST_TAIL, "-uri",  0,            "define resid file URI", NULL);
+    psMetadataAddF64(addcorrectimfileArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(addcorrectimfileArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(addcorrectimfileArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF64(addcorrectimfileArgs, PS_LIST_TAIL, "-user_1",  0,            "define user statistic (1)", NAN);
+    psMetadataAddF64(addcorrectimfileArgs, PS_LIST_TAIL, "-user_2",  0,            "define user statistic (2)", NAN);
+    psMetadataAddF64(addcorrectimfileArgs, PS_LIST_TAIL, "-user_3",  0,            "define user statistic (3)", NAN);
+    psMetadataAddF64(addcorrectimfileArgs, PS_LIST_TAIL, "-user_4",  0,            "define user statistic (4)", NAN);
+    psMetadataAddF64(addcorrectimfileArgs, PS_LIST_TAIL, "-user_5",  0,            "define user statistic (5)", NAN);
+    psMetadataAddStr(addcorrectimfileArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+
+    // -runs
+    psMetadata *runsArgs = psMetadataAlloc();
+    psMetadataAddStr(runsArgs, PS_LIST_TAIL, "-det_type",  0,            "search for type of detrend run", NULL);
+    psMetadataAddS64(runsArgs, PS_LIST_TAIL, "-det_id", 0,            "search for detrend ID", 0);
+    psMetadataAddStr(runsArgs, PS_LIST_TAIL, "-inst", 0,            "search for camera (instrument)", NULL);
+    psMetadataAddStr(runsArgs, PS_LIST_TAIL, "-telescope", 0,            "search for telescope", NULL);
+    psMetadataAddStr(runsArgs, PS_LIST_TAIL, "-filter", 0,            "search for filter", NULL);
+    psMetadataAddF32(runsArgs, PS_LIST_TAIL, "-airmass", 0,            "match airmass", NAN);
+    psMetadataAddF32(runsArgs, PS_LIST_TAIL, "-exp_time", 0,            "match exp time", NAN);
+    psMetadataAddF32(runsArgs, PS_LIST_TAIL, "-ccd_temp", 0,            "match ccd temp", NAN);
+    psMetadataAddF64(runsArgs, PS_LIST_TAIL, "-posang", 0,            "match posang", NAN);
+    psMetadataAddStr(runsArgs, PS_LIST_TAIL, "-mode", 0,            "search for det run by mode", NULL);
+    psMetadataAddBool(runsArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddBool(runsArgs, PS_LIST_TAIL, "-active",  0,            "only return active detRuns", false);
+    psMetadataAddU64(runsArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -childlessrun
+    psMetadata *childlessrunArgs = psMetadataAlloc();
+    psMetadataAddStr(childlessrunArgs, PS_LIST_TAIL, "-det_type",  0,            "search for type of detrend run", NULL);
+    psMetadataAddU64(childlessrunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(childlessrunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -input
+    psMetadata *inputArgs = psMetadataAlloc();
+    psMetadataAddS64(inputArgs, PS_LIST_TAIL, "-det_id", 0,            "search for detrend ID", 0);
+    psMetadataAddS32(inputArgs, PS_LIST_TAIL, "-iteration",  0,            "search for iteration number (default 0)", 0);
+    psMetadataAddS64(inputArgs, PS_LIST_TAIL, "-exp_id",  0,            "search for exp ID", 0);
+    psMetadataAddBool(inputArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -raw
+    psMetadata *rawArgs = psMetadataAlloc();
+    psMetadataAddBool(rawArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -toprocessedimfile
+    psMetadata *toprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(toprocessedimfileArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID", 0);
+    psMetadataAddS64(toprocessedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "search for exp ID", 0);
+    psMetadataAddStr(toprocessedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search for class ID", NULL);
+    psMetadataAddU64(toprocessedimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(toprocessedimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addprocessedimfile
+    psMetadata *addprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addprocessedimfileArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddS64(addprocessedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "define exp ID (required)", 0);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "define class ID (required)", NULL);
+    psMetadataAddS16(addprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-uri",  0,            "define URI", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-recip",  0,            "define recipe", NULL);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-fringe_0",  0,            "define fringe slope (0th term)", NAN);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-fringe_1",  0,            "define fringe slope (1st term)", NAN);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-fringe_2",  0,            "define fringe slope (2nd term)", NAN);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-user_1",  0,            "define user statistic (1)", NAN);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-user_2",  0,            "define user statistic (2)", NAN);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-user_3",  0,            "define user statistic (3)", NAN);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-user_4",  0,            "define user statistic (4)", NAN);
+    psMetadataAddF64(addprocessedimfileArgs, PS_LIST_TAIL, "-user_5",  0,            "define user statistic (5)", NAN);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+
+    // -processedimfile
+    psMetadata *processedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(processedimfileArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID", 0);
+    psMetadataAddS64(processedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "search for exp ID", 0);
+    psMetadataAddStr(processedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search for class ID", NULL);
+    psMetadataAddStr(processedimfileArgs, PS_LIST_TAIL, "-select_state",  0,            "search for state", NULL);
+    psMetadataAddStr(processedimfileArgs, PS_LIST_TAIL, "-select_mode",  0,            "search for mode", NULL);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-included",  0,            "restrict results to exposures 'includeded' in the current iteration", false);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddU64(processedimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -revertprocessedimfile
+    psMetadata *revertprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(revertprocessedimfileArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID (required)", 0);
+    psMetadataAddS64(revertprocessedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exposure ID", 0);
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+    psMetadataAddS16(revertprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -updateprocessedimfile
+    psMetadata *updateprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(updateprocessedimfileArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS64(updateprocessedimfileArgs, PS_LIST_TAIL, "-exp_id",               0,            "search by exp_id", 0);
+    psMetadataAddStr(updateprocessedimfileArgs, PS_LIST_TAIL, "-class_id",             0,            "search by exp_name", NULL);
+    psMetadataAddStr(updateprocessedimfileArgs, PS_LIST_TAIL, "-data_state",           0,            "search for telescope", NULL);
+
+    // -pendingcleanup_processedimfile
+    psMetadata *pendingcleanup_processedimfileArgs = psMetadataAlloc();
+    psMetadataAddBool(pendingcleanup_processedimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanup_processedimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(pendingcleanup_processedimfileArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS64(pendingcleanup_processedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exposure ID", 0);
+    psMetadataAddStr(pendingcleanup_processedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+
+    // -donecleanup_processedimfile
+    psMetadata *donecleanup_processedimfileArgs = psMetadataAlloc();
+    psMetadataAddBool(donecleanup_processedimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanup_processedimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(donecleanup_processedimfileArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS64(donecleanup_processedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exposure ID", 0);
+    psMetadataAddStr(donecleanup_processedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+
+    // -toprocessedexp
+    psMetadata *toprocessedexpArgs = psMetadataAlloc();
+    psMetadataAddU64(toprocessedexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(toprocessedexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addproccessedexp
+    psMetadata *addprocessedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(addprocessedexpArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddS64(addprocessedexpArgs, PS_LIST_TAIL, "-exp_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-recip",  0,            "define recipe", NULL);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-fringe_0",  0,            "define fringe slope (0th term)", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-fringe_1",  0,            "define fringe slope (1st term)", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-fringe_2",  0,            "define fringe slope (2nd term)", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-user_1",  0,            "define user statistic (1)", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-user_2",  0,            "define user statistic (2)", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-user_3",  0,            "define user statistic (3)", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-user_4",  0,            "define user statistic (4)", NAN);
+    psMetadataAddF64(addprocessedexpArgs, PS_LIST_TAIL, "-user_5",  0,            "define user statistic (5)", NAN);
+    psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+    psMetadataAddS16(addprocessedexpArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+
+    // -proccessedexp
+    psMetadata *processedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(processedexpArgs, PS_LIST_TAIL, "-det_id",  0,            "search by detrend ID", 0);
+    psMetadataAddS64(processedexpArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exp ID", 0);
+    psMetadataAddU64(processedexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(processedexpArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(processedexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -revertprocessedexp
+    psMetadata *revertprocessedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(revertprocessedexpArgs, PS_LIST_TAIL, "-det_id",  0,            "search by detrend 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);
+
+    // -updateprocessedexp
+    psMetadata *updateprocessedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(updateprocessedexpArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS64(updateprocessedexpArgs, PS_LIST_TAIL, "-exp_id",               0,            "search by exp_id", 0);
+    psMetadataAddStr(updateprocessedexpArgs, PS_LIST_TAIL, "-data_state",           0,            "search for telescope", NULL);
+
+    // -pendingcleanup_processedexp
+    psMetadata *pendingcleanup_processedexpArgs = psMetadataAlloc();
+    psMetadataAddBool(pendingcleanup_processedexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanup_processedexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(pendingcleanup_processedexpArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS64(pendingcleanup_processedexpArgs, PS_LIST_TAIL, "-exp_id",               0,            "search by exp_id", 0);
+
+    // -donecleanup_processedexp
+    psMetadata *donecleanup_processedexpArgs = psMetadataAlloc();
+    psMetadataAddBool(donecleanup_processedexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanup_processedexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(donecleanup_processedexpArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS64(donecleanup_processedexpArgs, PS_LIST_TAIL, "-exp_id",               0,            "search by exp_id", 0);
+
+    // -tostacked
+    psMetadata *tostackedArgs = psMetadataAlloc();
+    psMetadataAddU64(tostackedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tostackedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addstacked
+    psMetadata *addstackedArgs = psMetadataAlloc();
+    psMetadataAddS64(addstackedArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddS32(addstackedArgs, PS_LIST_TAIL, "-iteration",  0,            "define iteration number (required)", 0);
+    psMetadataAddStr(addstackedArgs, PS_LIST_TAIL, "-class_id",  0,            "define class ID (required)", NULL);
+    psMetadataAddS16(addstackedArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+    psMetadataAddStr(addstackedArgs, PS_LIST_TAIL, "-uri",  0,            "define URI", NULL);
+    psMetadataAddStr(addstackedArgs, PS_LIST_TAIL, "-recip",  0,            "define recipe", NULL);
+    psMetadataAddF64(addstackedArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(addstackedArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(addstackedArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF64(addstackedArgs, PS_LIST_TAIL, "-user_1",  0,            "define user statistic (1)", NAN);
+    psMetadataAddF64(addstackedArgs, PS_LIST_TAIL, "-user_2",  0,            "define user statistic (2)", NAN);
+    psMetadataAddF64(addstackedArgs, PS_LIST_TAIL, "-user_3",  0,            "define user statistic (3)", NAN);
+    psMetadataAddF64(addstackedArgs, PS_LIST_TAIL, "-user_4",  0,            "define user statistic (4)", NAN);
+    psMetadataAddF64(addstackedArgs, PS_LIST_TAIL, "-user_5",  0,            "define user statistic (5)", NAN);
+
+    // -stacked
+    psMetadata *stackedArgs = psMetadataAlloc();
+    psMetadataAddS64(stackedArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID", 0);
+    psMetadataAddS32(stackedArgs, PS_LIST_TAIL, "-iteration",  0,            "search for iteration number", 0);
+    psMetadataAddStr(stackedArgs, PS_LIST_TAIL, "-class_id",  0,            "search for class ID", NULL);
+    psMetadataAddStr(stackedArgs, PS_LIST_TAIL, "-recip",  0,            "search for recipe", NULL);
+    psMetadataAddU64(stackedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(stackedArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(stackedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -revertstacked
+    psMetadata *revertstackedArgs= psMetadataAlloc();
+    psMetadataAddS64(revertstackedArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID (required)", 0);
+    psMetadataAddS32(revertstackedArgs, PS_LIST_TAIL, "-iteration",  0,            "search by iteration number", 0);
+    psMetadataAddStr(revertstackedArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+    psMetadataAddS16(revertstackedArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -updatestacked
+    psMetadata *updatestackedArgs = psMetadataAlloc();
+    psMetadataAddS64(updatestackedArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(updatestackedArgs, PS_LIST_TAIL, "-iteration",  0,            "search by iteration number", 0);
+    psMetadataAddStr(updatestackedArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+    psMetadataAddStr(updatestackedArgs, PS_LIST_TAIL, "-data_state",           0,            "search for telescope", NULL);
+
+    // -pendingcleanup_stacked
+    psMetadata *pendingcleanup_stackedArgs = psMetadataAlloc();
+    psMetadataAddBool(pendingcleanup_stackedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanup_stackedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(pendingcleanup_stackedArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(pendingcleanup_stackedArgs, PS_LIST_TAIL, "-iteration",  0,            "search by iteration number", 0);
+    psMetadataAddStr(pendingcleanup_stackedArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+
+    // -donecleanup_stacked
+    psMetadata *donecleanup_stackedArgs = psMetadataAlloc();
+    psMetadataAddBool(donecleanup_stackedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanup_stackedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(donecleanup_stackedArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(donecleanup_stackedArgs, PS_LIST_TAIL, "-iteration",  0,            "search by iteration number", 0);
+    psMetadataAddStr(donecleanup_stackedArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+
+    // -tonormalizedstat
+    psMetadata *tonormalizedstatArgs = psMetadataAlloc();
+    psMetadataAddU64(tonormalizedstatArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tonormalizedstatArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addnormalizedstat
+    psMetadata *addnormstatArgs = psMetadataAlloc();
+    psMetadataAddS64(addnormstatArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddS32(addnormstatArgs, PS_LIST_TAIL, "-iteration",  0,            "define iteration number", 0);
+    psMetadataAddStr(addnormstatArgs, PS_LIST_TAIL, "-class_id",  0,            "define class ID (required)", NULL);
+    psMetadataAddF32(addnormstatArgs, PS_LIST_TAIL, "-norm",  0,            "define normal value (required)", NAN);
+    psMetadataAddS16(addnormstatArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+
+    // -normalizedstat
+    psMetadata *normalizedstatArgs = psMetadataAlloc();
+    psMetadataAddS64(normalizedstatArgs, PS_LIST_TAIL, "-det_id",  0,            "search by detrend ID", 0);
+    psMetadataAddS32(normalizedstatArgs, PS_LIST_TAIL, "-iteration",  0,            "search by iteration number", 0);
+    psMetadataAddStr(normalizedstatArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+    psMetadataAddU64(normalizedstatArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(normalizedstatArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(normalizedstatArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -revertnormalizedstat
+    psMetadata *revertnormalizedstatArgs= psMetadataAlloc();
+    psMetadataAddS64(revertnormalizedstatArgs, PS_LIST_TAIL, "-det_id",  0,            "search by detrend ID (required)", 0);
+    psMetadataAddS32(revertnormalizedstatArgs, PS_LIST_TAIL, "-iteration",  0,            "search by iteration number", 0);
+    psMetadataAddStr(revertnormalizedstatArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+    psMetadataAddS16(revertnormalizedstatArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -updatenormalizedstat
+    psMetadata *updatenormalizedstatArgs = psMetadataAlloc();
+    psMetadataAddS64(updatenormalizedstatArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(updatenormalizedstatArgs, PS_LIST_TAIL, "-iteration",  0,            "search by iteration number", 0);
+    psMetadataAddStr(updatenormalizedstatArgs, PS_LIST_TAIL, "-class_id",             0,            "search by exp_name", NULL);
+    psMetadataAddStr(updatenormalizedstatArgs, PS_LIST_TAIL, "-data_state",           0,            "search for telescope", NULL);
+
+    // -pendingcleanup_normalizedstat
+    psMetadata *pendingcleanup_normalizedstatArgs = psMetadataAlloc();
+    psMetadataAddBool(pendingcleanup_normalizedstatArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanup_normalizedstatArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(pendingcleanup_normalizedstatArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(pendingcleanup_normalizedstatArgs, PS_LIST_TAIL, "-iteration",  0,            "search by iteration number", 0);
+    psMetadataAddStr(pendingcleanup_normalizedstatArgs, PS_LIST_TAIL, "-class_id",             0,            "search by exp_name", NULL);
+
+    // -donecleanup_normalizedstat
+    psMetadata *donecleanup_normalizedstatArgs = psMetadataAlloc();
+    psMetadataAddBool(donecleanup_normalizedstatArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanup_normalizedstatArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(donecleanup_normalizedstatArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(donecleanup_normalizedstatArgs, PS_LIST_TAIL, "-iteration",  0,            "search by iteration number", 0);
+    psMetadataAddStr(donecleanup_normalizedstatArgs, PS_LIST_TAIL, "-class_id",             0,            "search by exp_name", NULL);
+
+    // -tonormalize
+    psMetadata *tonormalizeArgs = psMetadataAlloc();
+    psMetadataAddU64(tonormalizeArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tonormalizeArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addnormalizedimfile
+    psMetadata *addnormalizedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addnormalizedimfileArgs, PS_LIST_TAIL, "-det_id", 0,            "define detrend ID (required)", 0);
+    psMetadataAddS32(addnormalizedimfileArgs, PS_LIST_TAIL, "-iteration", 0,            "define iteration number", 0);
+    psMetadataAddStr(addnormalizedimfileArgs, PS_LIST_TAIL, "-class_id", 0,            "define class ID (required)", NULL);
+    psMetadataAddS16(addnormalizedimfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+    psMetadataAddStr(addnormalizedimfileArgs, PS_LIST_TAIL, "-uri", 0,            "define URI", NULL);
+    psMetadataAddF64(addnormalizedimfileArgs, PS_LIST_TAIL, "-bg", 0,            "define exposure background", NAN);
+    psMetadataAddF64(addnormalizedimfileArgs, PS_LIST_TAIL, "-bg_stdev", 0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(addnormalizedimfileArgs, PS_LIST_TAIL, "-bg_mean_stdev", 0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF64(addnormalizedimfileArgs, PS_LIST_TAIL, "-user_1",  0,            "define user statistic (1)", NAN);
+    psMetadataAddF64(addnormalizedimfileArgs, PS_LIST_TAIL, "-user_2",  0,            "define user statistic (2)", NAN);
+    psMetadataAddF64(addnormalizedimfileArgs, PS_LIST_TAIL, "-user_3",  0,            "define user statistic (3)", NAN);
+    psMetadataAddF64(addnormalizedimfileArgs, PS_LIST_TAIL, "-user_4",  0,            "define user statistic (4)", NAN);
+    psMetadataAddF64(addnormalizedimfileArgs, PS_LIST_TAIL, "-user_5",  0,            "define user statistic (5)", NAN);
+    psMetadataAddStr(addnormalizedimfileArgs, PS_LIST_TAIL, "-path_base", 0,            "define base output location", NULL);
+
+    // -normalizedimfile
+    psMetadata *normalizedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(normalizedimfileArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID", 0);
+    psMetadataAddS32(normalizedimfileArgs, PS_LIST_TAIL, "-iteration",  0,            "search for iteration number", 0);
+    psMetadataAddStr(normalizedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search for class ID", NULL);
+    psMetadataAddStr(normalizedimfileArgs, PS_LIST_TAIL, "-recip",  0,            "search for recipe", NULL);
+    psMetadataAddU64(normalizedimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(normalizedimfileArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(normalizedimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -revertnormalizedimfile
+    psMetadata *revertnormalizedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(revertnormalizedimfileArgs, PS_LIST_TAIL, "-det_id", 0,            "search by detrend ID (required)", 0);
+    psMetadataAddS32(revertnormalizedimfileArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+    psMetadataAddStr(revertnormalizedimfileArgs, PS_LIST_TAIL, "-class_id", 0,            "search by class ID", NULL);
+    psMetadataAddS16(revertnormalizedimfileArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -updatenormalizedimfile
+    psMetadata *updatenormalizedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(updatenormalizedimfileArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(updatenormalizedimfileArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+    psMetadataAddStr(updatenormalizedimfileArgs, PS_LIST_TAIL, "-class_id",             0,            "search by exp_name", NULL);
+    psMetadataAddStr(updatenormalizedimfileArgs, PS_LIST_TAIL, "-data_state",           0,            "search for telescope", NULL);
+
+    // -pendingcleanup_normalizedimfile
+    psMetadata *pendingcleanup_normalizedimfileArgs = psMetadataAlloc();
+    psMetadataAddBool(pendingcleanup_normalizedimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanup_normalizedimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(pendingcleanup_normalizedimfileArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(pendingcleanup_normalizedimfileArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+    psMetadataAddStr(pendingcleanup_normalizedimfileArgs, PS_LIST_TAIL, "-class_id",             0,            "search by exp_name", NULL);
+
+    // -donecleanup_normalizedimfile
+    psMetadata *donecleanup_normalizedimfileArgs = psMetadataAlloc();
+    psMetadataAddBool(donecleanup_normalizedimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanup_normalizedimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(donecleanup_normalizedimfileArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(donecleanup_normalizedimfileArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+    psMetadataAddStr(donecleanup_normalizedimfileArgs, PS_LIST_TAIL, "-class_id",             0,            "search by exp_name", NULL);
+
+    // -tonormalizedexp
+    psMetadata *tonormalizedexpArgs = psMetadataAlloc();
+    psMetadataAddU64(tonormalizedexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tonormalizedexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addnormalizedexp
+    psMetadata *addnormalizedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(addnormalizedexpArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddS32(addnormalizedexpArgs, PS_LIST_TAIL, "-iteration",  0,            "define iteration number", 0);
+    psMetadataAddS16(addnormalizedexpArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+    psMetadataAddStr(addnormalizedexpArgs, PS_LIST_TAIL, "-recip",  0,            "search for recipe", NULL);
+    psMetadataAddF64(addnormalizedexpArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(addnormalizedexpArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(addnormalizedexpArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF64(addnormalizedexpArgs, PS_LIST_TAIL, "-user_1",  0,            "define user statistic (1)", NAN);
+    psMetadataAddF64(addnormalizedexpArgs, PS_LIST_TAIL, "-user_2",  0,            "define user statistic (2)", NAN);
+    psMetadataAddF64(addnormalizedexpArgs, PS_LIST_TAIL, "-user_3",  0,            "define user statistic (3)", NAN);
+    psMetadataAddF64(addnormalizedexpArgs, PS_LIST_TAIL, "-user_4",  0,            "define user statistic (4)", NAN);
+    psMetadataAddF64(addnormalizedexpArgs, PS_LIST_TAIL, "-user_5",  0,            "define user statistic (5)", NAN);
+    psMetadataAddStr(addnormalizedexpArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+
+    // -normalizedexp
+    psMetadata *normalizedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(normalizedexpArgs, PS_LIST_TAIL, "-det_id",  0,            "search by detrend ID", 0);
+    psMetadataAddS32(normalizedexpArgs, PS_LIST_TAIL, "-iteration",  0,            "search by iteration number", 0);
+    psMetadataAddStr(normalizedexpArgs, PS_LIST_TAIL, "-recip",  0,            "search for recipe", NULL);
+    psMetadataAddU64(normalizedexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(normalizedexpArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(normalizedexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -revertnormalizedexp
+    psMetadata *revertnormalizedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(revertnormalizedexpArgs, PS_LIST_TAIL, "-det_id", 0,            "search by detrend ID (required)", 0);
+    psMetadataAddS32(revertnormalizedexpArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+    psMetadataAddS16(revertnormalizedexpArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -updatenormalizedexp
+    psMetadata *updatenormalizedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(updatenormalizedexpArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(updatenormalizedexpArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+    psMetadataAddStr(updatenormalizedexpArgs, PS_LIST_TAIL, "-data_state",           0,            "search for telescope", NULL);
+
+    // -pendingcleanup_normalizedexp
+    psMetadata *pendingcleanup_normalizedexpArgs = psMetadataAlloc();
+    psMetadataAddBool(pendingcleanup_normalizedexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanup_normalizedexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(pendingcleanup_normalizedexpArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(pendingcleanup_normalizedexpArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+
+    // -donecleanup_normalizedexp
+    psMetadata *donecleanup_normalizedexpArgs = psMetadataAlloc();
+    psMetadataAddBool(donecleanup_normalizedexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanup_normalizedexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(donecleanup_normalizedexpArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(donecleanup_normalizedexpArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+
+    // -toresidimfile
+    psMetadata *toresidimfileArgs = psMetadataAlloc();
+    psMetadataAddU64(toresidimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(toresidimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addresidimfile
+    // XXX does a default value of 0 mean the entry is not instatiated? what about a supplied value of 0?
+    psMetadata *addresidimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addresidimfileArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddS32(addresidimfileArgs, PS_LIST_TAIL, "-iteration",  0,         "define iteration number", 0);
+    psMetadataAddS64(addresidimfileArgs, PS_LIST_TAIL, "-ref_det_id",  0,        "define reference detrend ID (required)", 0);
+    psMetadataAddS32(addresidimfileArgs, PS_LIST_TAIL, "-ref_iter",  0,          "define reference iteration number (required)", -1);
+    psMetadataAddS64(addresidimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddStr(addresidimfileArgs, PS_LIST_TAIL, "-class_id",  0,          "define class ID (required)", NULL);
+    psMetadataAddS16(addresidimfileArgs, PS_LIST_TAIL, "-code",  0,              "set fault code", 0);
+    psMetadataAddStr(addresidimfileArgs, PS_LIST_TAIL, "-uri",  0,               "define resid file URI", NULL);
+    psMetadataAddStr(addresidimfileArgs, PS_LIST_TAIL, "-recip",  0,             "define recipe", NULL);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-bg",  0,                "define exposure background", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-bg_skewness",  0,            "define exposure background skewness", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-bg_kurtosis",  0,            "define exposure background kurtosis", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-bin_stdev",  0,            "define exposure background binned stdev", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-fringe_0",  0,            "define fringe slope (0th term)", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-fringe_1",  0,            "define fringe slope (1st term)", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-fringe_2",  0,            "define fringe slope (2nd term)", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-fringe_resid_0",  0,            "define fringe residual (0th term)", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-fringe_resid_1",  0,            "define fringe residual (1st term)", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-fringe_resid_2",  0,            "define fringe residual (2nd term)", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-user_1",  0,            "define user statistic (1)", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-user_2",  0,            "define user statistic (2)", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-user_3",  0,            "define user statistic (3)", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-user_4",  0,            "define user statistic (4)", NAN);
+    psMetadataAddF64(addresidimfileArgs, PS_LIST_TAIL, "-user_5",  0,            "define user statistic (5)", NAN);
+    psMetadataAddStr(addresidimfileArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+
+    // -residimfile
+    psMetadata *residimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(residimfileArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID", 0);
+    psMetadataAddS32(residimfileArgs, PS_LIST_TAIL, "-iteration",  0,            "search for iteration number", 0);
+    psMetadataAddS64(residimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by detrend ID", 0);
+    psMetadataAddStr(residimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search for class ID", NULL);
+    psMetadataAddStr(residimfileArgs, PS_LIST_TAIL, "-recip",  0,            "search for recipe", NULL);
+    psMetadataAddU64(residimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(residimfileArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(residimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddStr(residimfileArgs, PS_LIST_TAIL, "-select_state",  0,            "search for state", NULL);
+
+    // -revertresidimfile
+    psMetadata *revertresidimfileArgs =  psMetadataAlloc();
+    psMetadataAddS64(revertresidimfileArgs, PS_LIST_TAIL, "-det_id", 0,          "search by detrend ID (required)", 0);
+    psMetadataAddS32(revertresidimfileArgs, PS_LIST_TAIL, "-iteration", 0,       "search by iteration number", 0);
+    psMetadataAddS64(revertresidimfileArgs, PS_LIST_TAIL, "-exp_id",  0,         "search by detrend ID", 0);
+    psMetadataAddStr(revertresidimfileArgs, PS_LIST_TAIL, "-class_id",  0,       "search for class ID", NULL);
+    psMetadataAddS16(revertresidimfileArgs, PS_LIST_TAIL, "-code",  0,           "search by fault code", 0);
+
+    // -updateresidimfile
+    psMetadata *updateresidimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(updateresidimfileArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(updateresidimfileArgs, PS_LIST_TAIL, "-iteration", 0,       "search by iteration number", 0);
+    psMetadataAddS64(updateresidimfileArgs, PS_LIST_TAIL, "-exp_id",               0,            "search by exp_id", 0);
+    psMetadataAddStr(updateresidimfileArgs, PS_LIST_TAIL, "-class_id",             0,            "search by exp_name", NULL);
+    psMetadataAddStr(updateresidimfileArgs, PS_LIST_TAIL, "-data_state",           0,            "search for telescope", NULL);
+
+    // -pendingcleanup_residimfile
+    psMetadata *pendingcleanup_residimfileArgs = psMetadataAlloc();
+    psMetadataAddBool(pendingcleanup_residimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanup_residimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(pendingcleanup_residimfileArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(pendingcleanup_residimfileArgs, PS_LIST_TAIL, "-iteration", 0,       "search by iteration number", 0);
+    psMetadataAddS64(pendingcleanup_residimfileArgs, PS_LIST_TAIL, "-exp_id",               0,            "search by exp_id", 0);
+    psMetadataAddStr(pendingcleanup_residimfileArgs, PS_LIST_TAIL, "-class_id",             0,            "search by exp_name", NULL);
+
+    // -donecleanup_residimfile
+    psMetadata *donecleanup_residimfileArgs = psMetadataAlloc();
+    psMetadataAddBool(donecleanup_residimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanup_residimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(donecleanup_residimfileArgs, PS_LIST_TAIL, "-det_id",               0,            "search by chip ID", 0);
+    psMetadataAddS32(donecleanup_residimfileArgs, PS_LIST_TAIL, "-iteration", 0,       "search by iteration number", 0);
+    psMetadataAddS64(donecleanup_residimfileArgs, PS_LIST_TAIL, "-exp_id",               0,            "search by exp_id", 0);
+    psMetadataAddStr(donecleanup_residimfileArgs, PS_LIST_TAIL, "-class_id",             0,            "search by exp_name", NULL);
+
+    // -toresidexp
+    psMetadata *toresidexpArgs = psMetadataAlloc();
+    psMetadataAddU64(toresidexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(toresidexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addresidexp
+    psMetadata *addresidexpArgs = psMetadataAlloc();
+    psMetadataAddS64(addresidexpArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddS32(addresidexpArgs, PS_LIST_TAIL, "-iteration",  0,            "define iteration number", 0);
+    psMetadataAddS64(addresidexpArgs, PS_LIST_TAIL, "-exp_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddS16(addresidexpArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+    psMetadataAddStr(addresidexpArgs, PS_LIST_TAIL, "-recip",  0,            "define recipe", NULL);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-bg_skewness",  0,            "define exposure background skewness", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-bg_kurtosis",  0,            "define exposure background kurtosis", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-bin_stdev",  0,            "define exposure background binned stdev", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-fringe_0",  0,            "define fringe slope (0th term)", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-fringe_1",  0,            "define fringe slope (1st term)", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-fringe_2",  0,            "define fringe slope (2nd term)", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-fringe_resid_0",  0,            "define fringe residual (0th term)", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-fringe_resid_1",  0,            "define fringe residual (1st term)", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-fringe_resid_2",  0,            "define fringe residual (2nd term)", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-user_1",  0,            "define user statistic (1)", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-user_2",  0,            "define user statistic (2)", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-user_3",  0,            "define user statistic (3)", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-user_4",  0,            "define user statistic (4)", NAN);
+    psMetadataAddF64(addresidexpArgs, PS_LIST_TAIL, "-user_5",  0,            "define user statistic (5)", NAN);
+    psMetadataAddStr(addresidexpArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+    psMetadataAddBool(addresidexpArgs, PS_LIST_TAIL, "-reject",  0,            "exposure is not to be stacked in the next iteration", false);
+
+    // -residexp
+    psMetadata *residexpArgs = psMetadataAlloc();
+    psMetadataAddS64(residexpArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID", 0);
+    psMetadataAddS32(residexpArgs, PS_LIST_TAIL, "-iteration",  0,            "search for iteration number", 0);
+    psMetadataAddS64(residexpArgs, PS_LIST_TAIL, "-exp_id",  0,            "search for exp ID", 0);
+    psMetadataAddStr(residexpArgs, PS_LIST_TAIL, "-recip",  0,            "search for recipe", NULL);
+    psMetadataAddBool(residexpArgs, PS_LIST_TAIL, "-reject",  0,            "search for acceptable residuals", false);
+    psMetadataAddU64(residexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(residexpArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(residexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -revertresidexp
+    psMetadata *revertresidexpArgs = psMetadataAlloc();
+    psMetadataAddS64(revertresidexpArgs, PS_LIST_TAIL, "-det_id", 0,            "search by detrend ID (required)", 0);
+    psMetadataAddS32(revertresidexpArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+    psMetadataAddS64(revertresidexpArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by detrend ID", 0);
+    psMetadataAddS16(revertresidexpArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -updateresidexp
+    psMetadata *updateresidexpArgs = psMetadataAlloc();
+    psMetadataAddS64(updateresidexpArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID", 0);
+    psMetadataAddS32(updateresidexpArgs, PS_LIST_TAIL, "-iteration",  0,            "define iteration number", 0);
+    psMetadataAddS64(updateresidexpArgs, PS_LIST_TAIL, "-exp_id",  0,            "define exp ID", 0);
+    psMetadataAddStr(updateresidexpArgs, PS_LIST_TAIL, "-recip",  0,            "define recipe", NULL);
+    psMetadataAddF64(updateresidexpArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(updateresidexpArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(updateresidexpArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddStr(updateresidexpArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+    psMetadataAddBool(updateresidexpArgs, PS_LIST_TAIL, "-reject",  0,            "exposure is not to be stacked in the next iteration", false);
+    psMetadataAddStr(updateresidexpArgs, PS_LIST_TAIL, "-data_state",           0,            "search for telescope", NULL);
+
+    // -pendingcleanup_residexp
+    psMetadata *pendingcleanup_residexpArgs = psMetadataAlloc();
+    psMetadataAddBool(pendingcleanup_residexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanup_residexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(pendingcleanup_residexpArgs, PS_LIST_TAIL, "-det_id", 0,            "search by detrend ID (required)", 0);
+    psMetadataAddS32(pendingcleanup_residexpArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+    psMetadataAddS64(pendingcleanup_residexpArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by detrend ID", 0);
+
+    // -donecleanup_residexp
+    psMetadata *donecleanup_residexpArgs = psMetadataAlloc();
+    psMetadataAddBool(donecleanup_residexpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanup_residexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(donecleanup_residexpArgs, PS_LIST_TAIL, "-det_id", 0,            "search by detrend ID (required)", 0);
+    psMetadataAddS32(donecleanup_residexpArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+    psMetadataAddS64(donecleanup_residexpArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by detrend ID", 0);
+
+    // -todetrunsummary
+    psMetadata *todetrunsummaryArgs = psMetadataAlloc();
+    psMetadataAddBool(todetrunsummaryArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(todetrunsummaryArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -adddetrunsummary
+    psMetadata *adddetrunsummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(adddetrunsummaryArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddS32(adddetrunsummaryArgs, PS_LIST_TAIL, "-iteration",  0,            "define iteration number", 0);
+    psMetadataAddF64(adddetrunsummaryArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(adddetrunsummaryArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(adddetrunsummaryArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddS16(adddetrunsummaryArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+    psMetadataAddBool(adddetrunsummaryArgs, PS_LIST_TAIL, "-accept",  0,            "declare that this detrun iteration is accepted as a master", false);
+    psMetadataAddBool(adddetrunsummaryArgs, PS_LIST_TAIL, "-again",  0,            "start a new iteration of this detrend run", false);
+
+    // -detrunsummary
+    psMetadata *detrunsummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(detrunsummaryArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID", 0);
+    psMetadataAddS32(detrunsummaryArgs, PS_LIST_TAIL, "-iteration",  0,            "search for iteration number", 0);
+    psMetadataAddU64(detrunsummaryArgs, PS_LIST_TAIL, "-limit",  0,                     "limit result set to N items", 0);
+    psMetadataAddBool(detrunsummaryArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(detrunsummaryArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddBool(detrunsummaryArgs, PS_LIST_TAIL, "-reject",  0,            "search for acceptable residuals", false);
+
+    // -revertdetrunsummary
+    psMetadata *revertdetrunsummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(revertdetrunsummaryArgs, PS_LIST_TAIL, "-det_id", 0,            "search by detrend ID (required)", 0);
+    psMetadataAddS32(revertdetrunsummaryArgs, PS_LIST_TAIL, "-iteration", 0,            "search by iteration number", 0);
+    psMetadataAddS16(revertdetrunsummaryArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -updatedetrunsummary
+    psMetadata *updatedetrunsummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(updatedetrunsummaryArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend master for detrend ID (required)", 0);
+    psMetadataAddBool(updatedetrunsummaryArgs, PS_LIST_TAIL, "-accept",  0,            "declare that this detrun iteration is accepted as a master", false);
+    psMetadataAddBool(updatedetrunsummaryArgs, PS_LIST_TAIL, "-reject",  0,            "reject this detrun iteration as a master", false);
+
+    // -updatedetrun
+    psMetadata *updatedetrunArgs = psMetadataAlloc();
+    psMetadataAddS64(updatedetrunArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend master for detrend ID (required)", 0);
+    psMetadataAddBool(updatedetrunArgs, PS_LIST_TAIL, "-again",  0,            "start a new iteration of this detrend run", false);
+    psMetadataAddStr(updatedetrunArgs, PS_LIST_TAIL, "-state",  0,            "set the state of this detrend run", false);
+
+    // -rerun
+    psMetadata *rerunArgs = psMetadataAlloc();
+    psMetadataAddS64(rerunArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend master for detrend ID (required)", 0);
+    psMetadataAddS64(rerunArgs, PS_LIST_TAIL, "-exp_id",  PS_META_DUPLICATE_OK,            "include this exposure (multiple OK, required)", 0);
+
+    // -register_detrend
+    psMetadata *register_detrendArgs = psMetadataAlloc();
+    psMetadataAddStr(register_detrendArgs, PS_LIST_TAIL, "-det_type",  0,            "define the type of detrend run (required)", NULL);
+    psMetadataAddStr(register_detrendArgs, PS_LIST_TAIL, "-filelevel",  0,            "define filelevel (required)", NULL);
+    psMetadataAddStr(register_detrendArgs, PS_LIST_TAIL, "-workdir",  0,            "define workdir", NULL);
+    psMetadataAddStr(register_detrendArgs, PS_LIST_TAIL, "-inst",  0,            "define camera", NULL);
+    psMetadataAddStr(register_detrendArgs, PS_LIST_TAIL, "-telescope",  0,            "define telescope", NULL);
+    psMetadataAddStr(register_detrendArgs, PS_LIST_TAIL, "-exp_type",  0,            "define exposure type", NULL);
+    psMetadataAddStr(register_detrendArgs, PS_LIST_TAIL, "-filter",  0,            "define filter ", NULL);
+    psMetadataAddF32(register_detrendArgs, PS_LIST_TAIL, "-airmass_min",  0,            "define min airmass", NAN);
+    psMetadataAddF32(register_detrendArgs, PS_LIST_TAIL, "-airmass_max",  0,            "define max airmass", NAN);
+    psMetadataAddF32(register_detrendArgs, PS_LIST_TAIL, "-exp_time_min",  0,            "define min exposure time", NAN);
+    psMetadataAddF32(register_detrendArgs, PS_LIST_TAIL, "-exp_time_max",  0,            "define max exposure time", NAN);
+    psMetadataAddF32(register_detrendArgs, PS_LIST_TAIL, "-ccd_temp_min",  0,            "define min ccd tempature", NAN);
+    psMetadataAddF32(register_detrendArgs, PS_LIST_TAIL, "-ccd_temp_max",  0,            "define max ccd tempature", NAN);
+    psMetadataAddF64(register_detrendArgs, PS_LIST_TAIL, "-posang_min",  0,            "define min rotator position angle", NAN);
+    psMetadataAddF64(register_detrendArgs, PS_LIST_TAIL, "-posang_max",  0,            "define max rotator position angle", NAN);
+    psMetadataAddF64(register_detrendArgs, PS_LIST_TAIL, "-solang_min",  0,            "define min solar angle", NAN);
+    psMetadataAddF64(register_detrendArgs, PS_LIST_TAIL, "-solang_max",  0,            "define max solar angle", NAN);
+    psMetadataAddTime(register_detrendArgs, PS_LIST_TAIL, "-registered",  0,            "time detrend run was registered", now);
+    psMetadataAddTime(register_detrendArgs, PS_LIST_TAIL, "-time_begin",  0,            "detrend applies to exposures taken during this period", NULL);
+    psMetadataAddTime(register_detrendArgs, PS_LIST_TAIL, "-time_end",  0,            "detrend applies to exposures taken during this period", NULL);
+    psMetadataAddTime(register_detrendArgs, PS_LIST_TAIL, "-use_begin",  0,            "start of detrend run applicable period", NULL);
+    psMetadataAddTime(register_detrendArgs, PS_LIST_TAIL, "-use_end",  0,            "end of detrend run applicable period", NULL);
+    psMetadataAddS64(register_detrendArgs, PS_LIST_TAIL, "-ref_det_id",  0,            "define reference det_id", 0);
+    psMetadataAddS32(register_detrendArgs, PS_LIST_TAIL, "-ref_iter",  0,            "define reference iteration", -1);
+    psMetadataAddStr(register_detrendArgs, PS_LIST_TAIL, "-label",  0,            "define detrun label", NULL);
+    psMetadataAddBool(register_detrendArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -register_detrend_imfile
+    psMetadata *register_detrend_imfileArgs = psMetadataAlloc();
+    psMetadataAddS64(register_detrend_imfileArgs, PS_LIST_TAIL, "-det_id",  0,            "define detrend ID (required)", 0);
+    psMetadataAddStr(register_detrend_imfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search for class ID (required)", NULL);
+    psMetadataAddStr(register_detrend_imfileArgs, PS_LIST_TAIL, "-uri",  0,            "define resid file URI (required)", NULL);
+    psMetadataAddF64(register_detrend_imfileArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(register_detrend_imfileArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF64(register_detrend_imfileArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF64(register_detrend_imfileArgs, PS_LIST_TAIL, "-user_1",  0,            "define user statistic (1)", NAN);
+    psMetadataAddF64(register_detrend_imfileArgs, PS_LIST_TAIL, "-user_2",  0,            "define user statistic (2)", NAN);
+    psMetadataAddF64(register_detrend_imfileArgs, PS_LIST_TAIL, "-user_3",  0,            "define user statistic (3)", NAN);
+    psMetadataAddF64(register_detrend_imfileArgs, PS_LIST_TAIL, "-user_4",  0,            "define user statistic (4)", NAN);
+    psMetadataAddF64(register_detrend_imfileArgs, PS_LIST_TAIL, "-user_5",  0,            "define user statistic (5)", NAN);
+    psMetadataAddStr(register_detrend_imfileArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+
+    psFree(now);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-pending",         "list active detruns", DETTOOL_MODE_PENDING,       pendingArgs);
+    PXOPT_ADD_MODE("-definebytag",     "", DETTOOL_MODE_DEFINEBYTAG,   definebytagArgs);
+    PXOPT_ADD_MODE("-definebyquery",   "", DETTOOL_MODE_DEFINEBYQUERY, definebyqueryArgs);
+    PXOPT_ADD_MODE("-definebydetrun",  "", DETTOOL_MODE_DEFINEBYDETRUN, definebydetrunArgs);
+    PXOPT_ADD_MODE("-makecorrection",  "", DETTOOL_MODE_MAKECORRECTION, makecorrectionArgs);
+    PXOPT_ADD_MODE("-tocorrectexp",    "", DETTOOL_MODE_TOCORRECTEXP, tocorrectexpArgs);
+    PXOPT_ADD_MODE("-tocorrectimfile", "", DETTOOL_MODE_TOCORRECTIMFILE, tocorrectimfileArgs);
+    PXOPT_ADD_MODE("-addcorrectimfile", "", DETTOOL_MODE_ADDCORRECTIMFILE, addcorrectimfileArgs);
+    PXOPT_ADD_MODE("-raw",             "", DETTOOL_MODE_RAW,           rawArgs);
+    PXOPT_ADD_MODE("-runs",            "", DETTOOL_MODE_RUNS,          runsArgs);
+    PXOPT_ADD_MODE("-childlessrun",    "", DETTOOL_MODE_CHILDLESSRUN,  childlessrunArgs);
+    PXOPT_ADD_MODE("-input",           "", DETTOOL_MODE_INPUT,         inputArgs);
+
+    PXOPT_ADD_MODE("-toprocessedimfile", "",  		  DETTOOL_MODE_TOPROCESSEDIMFILE, toprocessedimfileArgs);
+    PXOPT_ADD_MODE("-addprocessedimfile", "", 		  DETTOOL_MODE_ADDPROCESSEDIMFILE,  addprocessedimfileArgs);
+    PXOPT_ADD_MODE("-processedimfile", "",    		  DETTOOL_MODE_PROCESSEDIMFILE, processedimfileArgs);
+    PXOPT_ADD_MODE("-revertprocessedimfile", "", 	  DETTOOL_MODE_REVERTPROCESSEDIMFILE, revertprocessedimfileArgs);
+    PXOPT_ADD_MODE("-updateprocessedimfile", "", 	  DETTOOL_MODE_UPDATEPROCESSEDIMFILE, updateprocessedimfileArgs);
+    PXOPT_ADD_MODE("-pendingcleanup_processedimfile", "", DETTOOL_MODE_PENDINGCLEANUP_PROCESSEDIMFILE, pendingcleanup_processedimfileArgs);
+    PXOPT_ADD_MODE("-donecleanup_processedimfile", "",    DETTOOL_MODE_DONECLEANUP_PROCESSEDIMFILE, donecleanup_processedimfileArgs);
+
+    PXOPT_ADD_MODE("-toprocessedexp",  "", DETTOOL_MODE_TOPROCESSEDEXP,  toprocessedexpArgs);
+    PXOPT_ADD_MODE("-addprocessedexp", "", DETTOOL_MODE_ADDPROCESSEDEXP, addprocessedexpArgs);
+    PXOPT_ADD_MODE("-processedexp",    "", DETTOOL_MODE_PROCESSEDEXP, processedexpArgs);
+    PXOPT_ADD_MODE("-revertprocessedexp", "", DETTOOL_MODE_REVERTPROCESSEDEXP, revertprocessedexpArgs);
+    PXOPT_ADD_MODE("-updateprocessedexp", "", DETTOOL_MODE_UPDATEPROCESSEDEXP, updateprocessedexpArgs);
+    PXOPT_ADD_MODE("-pendingcleanup_processedexp", "", DETTOOL_MODE_PENDINGCLEANUP_PROCESSEDEXP, pendingcleanup_processedexpArgs);
+    PXOPT_ADD_MODE("-donecleanup_processedexp", "",    DETTOOL_MODE_DONECLEANUP_PROCESSEDEXP, donecleanup_processedexpArgs);
+
+    PXOPT_ADD_MODE("-tostacked",       "", DETTOOL_MODE_TOSTACKED,     tostackedArgs);
+    PXOPT_ADD_MODE("-addstacked",      "", DETTOOL_MODE_ADDSTACKED,    addstackedArgs);
+    PXOPT_ADD_MODE("-stacked",         "", DETTOOL_MODE_STACKED,       stackedArgs);
+    PXOPT_ADD_MODE("-revertstacked",   "", DETTOOL_MODE_REVERTSTACKED,  revertstackedArgs);
+    PXOPT_ADD_MODE("-updatestacked",   "", DETTOOL_MODE_UPDATESTACKED,  updatestackedArgs);
+    PXOPT_ADD_MODE("-pendingcleanup_stacked", "", DETTOOL_MODE_PENDINGCLEANUP_STACKED, pendingcleanup_stackedArgs);
+    PXOPT_ADD_MODE("-donecleanup_stacked", "",    DETTOOL_MODE_DONECLEANUP_STACKED, donecleanup_stackedArgs);
+
+    PXOPT_ADD_MODE("-tonormalizedstat",      "", DETTOOL_MODE_TONORMALIZEDSTAT,    tonormalizedstatArgs);
+    PXOPT_ADD_MODE("-addnormalizedstat",     "", DETTOOL_MODE_ADDNORMALIZEDSTAT,   addnormstatArgs);
+    PXOPT_ADD_MODE("-normalizedstat",        "", DETTOOL_MODE_NORMALIZEDSTAT,   normalizedstatArgs);
+    PXOPT_ADD_MODE("-revertnormalizedstat",  "", DETTOOL_MODE_REVERTNORMALIZEDSTAT,   revertnormalizedstatArgs);
+    PXOPT_ADD_MODE("-updatenormalizedstat",  "", DETTOOL_MODE_UPDATENORMALIZEDSTAT,   updatenormalizedstatArgs);
+    PXOPT_ADD_MODE("-pendingcleanup_normalizedstat", "", DETTOOL_MODE_PENDINGCLEANUP_NORMALIZEDSTAT, pendingcleanup_normalizedstatArgs);
+    PXOPT_ADD_MODE("-donecleanup_normalizedstat", "",    DETTOOL_MODE_DONECLEANUP_NORMALIZEDSTAT, donecleanup_normalizedstatArgs);
+
+    PXOPT_ADD_MODE("-tonormalize",     "", DETTOOL_MODE_TONORMALIZE,   tonormalizeArgs);
+    PXOPT_ADD_MODE("-addnormalizedimfile", "", DETTOOL_MODE_ADDNORMALIZEDIMFILE,addnormalizedimfileArgs);
+    PXOPT_ADD_MODE("-normalizedimfile","", DETTOOL_MODE_NORMALIZEDIMFILE, normalizedimfileArgs);
+    PXOPT_ADD_MODE("-revertnormalizedimfile","", DETTOOL_MODE_REVERTNORMALIZEDIMFILE, revertnormalizedimfileArgs);
+    PXOPT_ADD_MODE("-updatenormalizedimfile","", DETTOOL_MODE_UPDATENORMALIZEDIMFILE, updatenormalizedimfileArgs);
+    PXOPT_ADD_MODE("-pendingcleanup_normalizedimfile", "", DETTOOL_MODE_PENDINGCLEANUP_NORMALIZEDIMFILE, pendingcleanup_normalizedimfileArgs);
+    PXOPT_ADD_MODE("-donecleanup_normalizedimfile", "",    DETTOOL_MODE_DONECLEANUP_NORMALIZEDIMFILE, donecleanup_normalizedimfileArgs);
+
+    PXOPT_ADD_MODE("-tonormalizedexp", "", DETTOOL_MODE_TONORMALIZEDEXP, tonormalizedexpArgs);
+    PXOPT_ADD_MODE("-addnormalizedexp", "", DETTOOL_MODE_ADDNORMALIZEDEXP, addnormalizedexpArgs);
+    PXOPT_ADD_MODE("-normalizedexp",   "", DETTOOL_MODE_NORMALIZEDEXP, normalizedexpArgs);
+    PXOPT_ADD_MODE("-revertnormalizedexp","", DETTOOL_MODE_REVERTNORMALIZEDEXP, revertnormalizedexpArgs);
+    PXOPT_ADD_MODE("-updatenormalizedexp","", DETTOOL_MODE_UPDATENORMALIZEDEXP, updatenormalizedexpArgs);
+    PXOPT_ADD_MODE("-pendingcleanup_normalizedexp", "", DETTOOL_MODE_PENDINGCLEANUP_NORMALIZEDEXP, pendingcleanup_normalizedexpArgs);
+    PXOPT_ADD_MODE("-donecleanup_normalizedexp", "",    DETTOOL_MODE_DONECLEANUP_NORMALIZEDEXP, donecleanup_normalizedexpArgs);
+
+    PXOPT_ADD_MODE("-toresidimfile",   "", DETTOOL_MODE_TORESIDIMFILE, toresidimfileArgs);
+    PXOPT_ADD_MODE("-addresidimfile",  "", DETTOOL_MODE_ADDRESIDIMFILE, addresidimfileArgs);
+    PXOPT_ADD_MODE("-residimfile",     "", DETTOOL_MODE_RESIDIMFILE, residimfileArgs);
+    PXOPT_ADD_MODE("-revertresidimfile", "", DETTOOL_MODE_REVERTRESIDIMFILE, revertresidimfileArgs);
+    PXOPT_ADD_MODE("-updateresidimfile", "", DETTOOL_MODE_UPDATERESIDIMFILE, updateresidimfileArgs);
+    PXOPT_ADD_MODE("-pendingcleanup_residimfile", "", DETTOOL_MODE_PENDINGCLEANUP_RESIDIMFILE, pendingcleanup_residimfileArgs);
+    PXOPT_ADD_MODE("-donecleanup_residimfile", "",    DETTOOL_MODE_DONECLEANUP_RESIDIMFILE, donecleanup_residimfileArgs);
+
+    PXOPT_ADD_MODE("-toresidexp",      "", DETTOOL_MODE_TORESIDEXP,    toresidexpArgs);
+    PXOPT_ADD_MODE("-addresidexp",     "", DETTOOL_MODE_ADDRESIDEXP,  addresidexpArgs);
+    PXOPT_ADD_MODE("-residexp",        "", DETTOOL_MODE_RESIDEXP,     residexpArgs);
+    PXOPT_ADD_MODE("-revertresidexp",  "", DETTOOL_MODE_REVERTRESIDEXP, revertresidexpArgs);
+    PXOPT_ADD_MODE("-updateresidexp",  "", DETTOOL_MODE_UPDATERESIDEXP, updateresidexpArgs);
+    PXOPT_ADD_MODE("-pendingcleanup_residexp", "", DETTOOL_MODE_PENDINGCLEANUP_RESIDEXP, pendingcleanup_residexpArgs);
+    PXOPT_ADD_MODE("-donecleanup_residexp", "",    DETTOOL_MODE_DONECLEANUP_RESIDEXP, donecleanup_residexpArgs);
+
+    PXOPT_ADD_MODE("-todetrunsummary",  "", DETTOOL_MODE_TODETRUNSUMMARY,  todetrunsummaryArgs);
+    PXOPT_ADD_MODE("-adddetrunsummary", "", DETTOOL_MODE_ADDDETRUNSUMMARY,adddetrunsummaryArgs);
+    PXOPT_ADD_MODE("-detrunsummary", "", DETTOOL_MODE_DETRUNSUMMARY,detrunsummaryArgs);
+    PXOPT_ADD_MODE("-revertdetrunsummary", "", DETTOOL_MODE_REVERTDETRUNSUMMARY, revertdetrunsummaryArgs);
+    PXOPT_ADD_MODE("-updatedetrunsummary", "", DETTOOL_MODE_UPDATEDETRUNSUMMARY, updatedetrunsummaryArgs);
+
+    PXOPT_ADD_MODE("-updatedetrun", "", DETTOOL_MODE_UPDATEDETRUN, updatedetrunArgs);
+    PXOPT_ADD_MODE("-rerun",           "", DETTOOL_MODE_RERUN,         rerunArgs);
+    PXOPT_ADD_MODE("-register_detrend", "", DETTOOL_MODE_REGISTER_DETREND, register_detrendArgs);
+    PXOPT_ADD_MODE("-register_detrend_imfile", "", DETTOOL_MODE_REGISTER_DETREND_IMFILE, register_detrend_imfileArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, false, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_correction.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_correction.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_correction.c	(revision 22322)
@@ -0,0 +1,254 @@
+/*
+ * dettool_correction.c
+ *
+ * Copyright (C) 2006-2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+bool makecorrectionMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(ref_det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_S32(ref_iter, config->args, "-iteration", true, false); // required
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false); // optional
+
+    // build the needed where 
+    psMetadata *where = psMetadataAlloc();
+    psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", ref_det_id);
+    psMetadataAddS32(where, PS_LIST_TAIL, "iteration", 0, "==", ref_iter);
+    psArray *runs = detRunSelectRowObjects(config->dbh, where, 1);
+    psFree (where);
+
+    if (!psArrayLength(runs)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    detRunRow *detRun = runs->data[0];
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    detRunInsert(config->dbh,
+         0,             // det_id
+         0,             // iteration
+         detRun->det_type,
+         "correction",  // mode
+         "run",         // state
+         detRun->filelevel,
+         detRun->workdir,
+         detRun->camera,
+         detRun->telescope,
+         detRun->exp_type,
+         detRun->reduction,
+         detRun->filter,
+         detRun->airmass_min,
+         detRun->airmass_max,
+         detRun->exp_time_min,
+         detRun->exp_time_max,
+         detRun->ccd_temp_min,
+         detRun->ccd_temp_max,
+         detRun->posang_min,
+         detRun->posang_max,
+         detRun->registered,
+         detRun->time_begin,
+         detRun->time_end,
+         detRun->use_begin,
+         detRun->use_end,
+         detRun->solang_min,
+         detRun->solang_max,
+         detRun->label,
+	 detRun->det_id, // ref_det_id
+	 detRun->iteration // ref_iter
+    );
+
+    psFree(runs);
+
+    // print the new detRun
+    psS64 new_det_id = psDBLastInsertID(config->dbh);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psMetadata *where_new = psMetadataAlloc();
+    psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", new_det_id);
+    psArray *detRuns = psDBSelectRows(config->dbh, "detRun", where_new, 0);
+    psFree(where_new);
+
+    if (!detRuns) {
+        psError(PS_ERR_UNKNOWN, false, "can't find the detRun we just created");
+        return false;
+    }
+    // sanity check results
+    if (psArrayLength(detRuns) != 1) {
+        psAbort("found more then one detRun matching det_id %" PRId64 "(this should not happen)", new_det_id);
+        return false;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, detRuns, "detRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(detRuns);
+        return false;
+    }
+    psFree(detRuns);
+
+    return true;
+}
+
+bool tocorrectexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_tocorrectexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+bool tocorrectimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_type", "det_type", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_tocorrectimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+	// NOTE the SQL uses an intermediate table 'det1' for this query
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "det1"); 
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree (where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingCorrectImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// NOTE : the command-line args are parsed by register_detrend_imfileMode
+bool addcorrectimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // insert the row into detRegisterImfile
+    if (!register_detrend_imfileMode(config)) {
+        return false;
+    }
+    
+    // automatically stop completed 'correct' detRuns
+    psString query = pxDataGet("dettool_stop_completed_correct_runs.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_detrunsummary.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_detrunsummary.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_detrunsummary.c	(revision 22322)
@@ -0,0 +1,366 @@
+/*
+ * dettool_detrunsummary.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt & EAM; IfA
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+static detRunSummaryRow *mdToDetRunSummary(pxConfig *config, psMetadata *row);
+
+/* The SQL returns a list of detrend runs (with detrend id, iteration and detrend type) which
+ * have completed all residexps.
+ */
+
+bool todetrunsummaryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_todetrunsummary.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detRejectExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// this function is used to generate the detrunSummary entries used below
+// XXX why is this a separate function?  roll it back into adddetrunsummary?
+static detRunSummaryRow *mdToDetRunSummary(pxConfig *config, psMetadata *row)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool status = false;
+    // from row
+    psS64 det_id = psMetadataLookupS64(&status, row, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, row, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for iteration");
+        return false;
+    }
+
+    // optional
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+    PXOPT_LOOKUP_BOOL(accept, config->args, "-accept", false);
+
+    return detRunSummaryRowAlloc(
+            det_id,
+            iteration,
+	    "full",
+            bg,
+            bg_stdev,
+            bg_mean_stdev,
+            accept,
+            code
+        );
+}
+
+// XXX data_state should be set to 'full' here
+bool adddetrunsummaryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    
+    // build a query to search by det_id, iteration, exp_id
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_BOOL(again, config->args, "-again", false);
+
+    // The values supplied as arguments on the command (eg, -bg) are parsed 
+    // by mdToDetRunSummary below. 
+    // XXX why is there ever more than one?
+
+    psString query = pxDataGet("dettool_find_completed_runs.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+	psStringAppend(&query, " WHERE %s", whereClause);
+	psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // start a transaction so it's all rows or nothing
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+
+    // XXX why is there ever more than one result here?
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i];
+        // convert metadata into a detResidExp object
+        detRunSummaryRow *runSummary = mdToDetRunSummary(config, row);
+        if (!runSummary) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata to detResidExp");
+            psFree(output);
+            return false;
+        }
+        // insert detResidExp object into the database
+        if (!detRunSummaryInsertObject(config->dbh, runSummary)) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(runSummary);
+            psFree(output);
+            return false;
+        }
+        psFree(runSummary);
+    }
+
+    psFree(output);
+
+    // XXX this logic does not deal with the case of -code being set
+    // XXX it should be an error for -again and -code to both be set
+    if (again) {
+        if (!startNewIteration(config, det_id)) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to start new iteration");
+            return false;
+        }
+    } else {
+        // set detRun.state to stop
+        if (!setDetRunState(config, det_id, "stop")) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to set detRun.state");
+            return false;
+        }
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    return true;
+}
+
+bool detrunsummaryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    psString query = pxDataGet("dettool_detrunsummary.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "NULL");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detRunSummary.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detRunSummary.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "rawDetrendImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool revertdetrunsummaryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertdetrunsummary.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detRunSummary");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+// XXX need to add -data_state here
+bool updatedetrunsummaryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_BOOL(accept, config->args, "-accept", false);
+    PXOPT_LOOKUP_BOOL(reject, config->args, "-reject", false);
+
+    if (accept && reject) {
+        psError(PS_ERR_UNKNOWN, true, "-accept and -reject are exclusive");
+        return false;
+    }
+
+    if (!(accept || reject)) {
+        psError(PS_ERR_UNKNOWN, true, "either -accept or -reject is required");
+        return false;
+    }
+
+    char *query = "UPDATE detRunSummary SET accept = %d WHERE det_id = %"PRId64; 
+    if (!p_psDBRunQuery(config->dbh, query, accept, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_normalizedexp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_normalizedexp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_normalizedexp.c	(revision 22322)
@@ -0,0 +1,389 @@
+/*
+ * dettool_normalizedexp.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt & EAM; IfA
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+bool tonormalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_tonormalizedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingNormExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+bool addnormalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false); // Required if code == 0
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+
+    psString query = pxDataGet("dettool_tonormalizedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // XXX in some other places, we put the arguments in the .sql and supply them to RunQuery
+    psStringAppend(&query, " WHERE det_id = %" PRId64 " AND iteration = %d", det_id, iteration);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+    psFree(output);
+
+    // insert the new row into the detProcessedImfile table
+    if (!detNormalizedExpInsert(
+	    config->dbh,
+	    det_id,
+	    iteration,
+	    recipe,
+	    bg,
+	    bg_stdev,
+	    bg_mean_stdev,
+	    user_1,
+	    user_2,
+	    user_3,
+	    user_4,
+	    user_5,
+	    path_base,
+	    "full",
+	    code
+	    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+bool normalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-recip",  "recipe", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    psString query = pxDataGet("dettool_normalizedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detNormalizedExp.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detNormalizedExp.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detNormalizedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+
+bool revertnormalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertnormalizedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detNormalizedExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+bool updatenormalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", true, false);
+    PXOPT_LOOKUP_STR(data_state, config->args, "-data_state", true, false);
+
+    if (!setNormExpDataState(config, det_id, iteration, data_state)) {
+	return false;
+    }
+    return true;
+}
+
+bool pendingcleanup_normalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "detNormalizedExp.det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "detNormalizedExp.iteration", "==");
+
+    psString query = pxDataGet("dettool_pendingcleanup_normalizedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detCleanupNormExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool donecleanup_normalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "detNormalizedExp.det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "detNormalizedExp.iteration", "==");
+
+    psString query = pxDataGet("dettool_donecleanup_normalizedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detDoneCleanup_normalizedexp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_normalizedimfile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_normalizedimfile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_normalizedimfile.c	(revision 22322)
@@ -0,0 +1,372 @@
+/*
+ * dettool_normalizedimfile.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt & EAM; IfA
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+// XXX this one is inconsistent - rename 'tonormalizedimfile' and change ipp_serial_detrend.pl and detrend.norm.pro
+bool tonormalizeMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_tonormalize.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingNormImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+bool addnormalizedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // Required if code == 0
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", (code == 0), false);
+
+    // optional
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+
+    if (!detNormalizedImfileInsert(
+	    config->dbh,
+	    det_id,
+	    iteration,
+	    class_id,
+	    uri,
+	    bg,
+	    bg_stdev,
+	    bg_mean_stdev,
+	    user_1,
+	    user_2,
+	    user_3,
+	    user_4,
+	    user_5,
+	    path_base,
+	    "full",
+	    code
+	    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+bool normalizedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-recip",     "recipe", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    psString query = pxDataGet("dettool_normalizedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detNormalizedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detNormalizedImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detNormalizedImfile.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detNormalizedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+bool revertnormalizedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertnormalizedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detNormalizedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+bool updatenormalizedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id",  true, false);
+    PXOPT_LOOKUP_STR(data_state, config->args, "-data_state", true, false);
+
+    if (!setNormImfileDataState(config, det_id, iteration, class_id, data_state)) {
+	return false;
+    }
+    return true;
+}
+
+bool pendingcleanup_normalizedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "detNormalizedImfile.det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "detNormalizedImfile.iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "detNormalizedImfile.det_id", "==");
+
+    psString query = pxDataGet("dettool_pendingcleanup_normalizedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detCleanupNormImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool donecleanup_normalizedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "detNormalizedImfile.det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "detNormalizedImfile.iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "detNormalizedImfile.det_id", "==");
+
+    psString query = pxDataGet("dettool_donecleanup_normalizedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detDoneCleanup_normalizedimfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_normalizedstat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_normalizedstat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_normalizedstat.c	(revision 22322)
@@ -0,0 +1,352 @@
+/*
+ * dettool_normalizedstat.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt & EAM; IfA
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+bool tonormalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_tonormalizedstat.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingNormStatImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+bool addnormalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    // required
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_F32(norm, config->args, "-norm", false, false);
+
+    // default
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    detNormalizedStatImfileRow *row = detNormalizedStatImfileRowAlloc
+	(det_id,
+	 iteration,
+	 class_id,
+	 norm,
+	 "full",
+	 code);
+									 
+    if (!detNormalizedStatImfileInsertObject(config->dbh, row)) {
+	psError(PS_ERR_UNKNOWN, false, "database error");
+	return false;
+    }
+
+    return true;
+}
+
+
+bool normalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_normalizedstat.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "WHERE fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "WHERE fault = 0");
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detNormalizedStatImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+bool revertnormalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertnormalizedstat.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detNormalizedStatImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+bool updatenormalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id",  true, false);
+    PXOPT_LOOKUP_STR(data_state, config->args, "-data_state", true, false);
+
+    if (!setNormStatImfileDataState(config, det_id, iteration, class_id, data_state)) {
+	return false;
+    }
+    return true;
+}
+
+bool pendingcleanup_normalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "detNormalizedStatImfile.det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "detNormalizedStatImfile.iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "detNormalizedStatImfile.det_id", "==");
+
+    psString query = pxDataGet("dettool_pendingcleanup_normalizedstat.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detCleanupNormStatImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool donecleanup_normalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "detNormalizedStatImfile.det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "detNormalizedStatImfile.iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "detNormalizedStatImfile.det_id", "==");
+
+    psString query = pxDataGet("dettool_donecleanup_normalizedstat.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detDoneCleanup_normalizedstat", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_processedexp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_processedexp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_processedexp.c	(revision 22322)
@@ -0,0 +1,398 @@
+/*
+ * dettool_processedexp.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt & EAM; IfA
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+bool toprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_toprocessedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingProcessedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+bool addprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // det_id, exp_id, recip, -bg, -bg_stdev
+    // are required
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // Required if code == 0
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false);
+
+    // optional
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(fringe_0, config->args, "-fringe_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_1, config->args, "-fringe_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_2, config->args, "-fringe_2", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", true, false);
+
+    psString query = pxDataGet("dettool_addprocessedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, (long long) det_id, (long long) exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+    psFree(output);
+
+    // create a new detProcessedImfile object
+    detProcessedExpRow *detRow = detProcessedExpRowAlloc(
+        det_id,
+        exp_id,
+        recipe,
+        bg,
+        bg_stdev,
+        bg_mean_stdev,
+        fringe_0,
+        fringe_1,
+        fringe_2,
+        user_1,
+        user_2,
+        user_3,
+        user_4,
+        user_5,
+        path_base,
+	"full",
+        code
+    );
+
+    // insert the new row into the detProcessedImfile table
+    if (!detProcessedExpInsertObject(config->dbh, detRow)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(detRow);
+        return false;
+    }
+
+    psFree(detRow);
+
+    return true;
+}
+
+bool processedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM detProcessedExp");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detProcessedExp.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detProcessedExp.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detProcessedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool revertprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",   "fault", "==");
+
+    psString query = pxDataGet("dettool_revertprocessedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detProcessedExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+bool updateprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(data_state, config->args, "-data_state", true, false);
+    if (!setProcessedExpDataState(config, det_id, exp_id, data_state)) {
+	return false;
+    }
+    return true;
+}
+
+bool pendingcleanup_processedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+
+    psString query = pxDataGet("dettool_pendingcleanup_processedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detCleanupProcessedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// XXX SQL missing
+bool donecleanup_processedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+
+    psString query = pxDataGet("dettool_donecleanup_processedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detDoneCleanupProcessedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_processedimfile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_processedimfile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_processedimfile.c	(revision 22322)
@@ -0,0 +1,433 @@
+/*
+ * dettool_processedimfile.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt & EAM; IfA
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+bool toprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",   "det_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",   "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_toprocessedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "rawImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree (where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingProcessedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+bool addprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // det_id, exp_id, class_id, uri, recipe, -bg, -bg_stdev are required
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // Required if code == 0
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", (code == 0), false);
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false);
+
+    // optional
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(fringe_0, config->args, "-fringe_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_1, config->args, "-fringe_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_2, config->args, "-fringe_2", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+
+    // find the matching rawImfile by exp_id/class_id
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-exp_id",   "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+
+    psArray *rawImfiles = rawImfileSelectRowObjects(config->dbh, where, 0);
+    psFree(where);
+
+    if (!rawImfiles) {
+        psError(PS_ERR_UNKNOWN, false, "no rawImfile rows found ");
+        return false;
+    }
+
+    // create a new detProcessedImfile object
+    detProcessedImfileRow *detRow = detProcessedImfileRowAlloc(
+        det_id,
+        exp_id,
+        class_id,
+        uri,
+        recipe,
+        bg,
+        bg_stdev,
+        bg_mean_stdev,
+        fringe_0,
+        fringe_1,
+        fringe_2,
+        user_1,
+        user_2,
+        user_3,
+        user_4,
+        user_5,
+        path_base,
+	"full",
+        code
+    );
+    psFree(rawImfiles);
+
+    // insert the new row into the detProcessedImfile table
+    if (!detProcessedImfileInsertObject(config->dbh, detRow)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(detRow);
+        return false;
+    }
+
+    psFree(detRow);
+
+    return true;
+}
+
+bool processedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool hasWhere = false;
+
+    PXOPT_LOOKUP_BOOL(included, config->args, "-included", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "detProcessedImfile.det_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "detProcessedImfile.exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "detProcessedImfile.class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-select_state", "detRun.state", "==");
+    PXOPT_COPY_STR(config->args, where, "-select_mode", "detRun.mode", "==");
+
+    psString query = pxDataGet("dettool_processedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+	hasWhere = true;
+    }
+    psFree (where);
+
+    // restrict search to included imfiles
+    if (included) {
+	if (hasWhere) {
+	    psStringAppend(&query, " AND detInputExp.include = 1");
+	} else {
+	    psStringAppend(&query, " WHERE detInputExp.include = 1");
+	}
+	hasWhere = true;
+    }
+
+    if (hasWhere) {
+	psStringAppend(&query, " AND");
+    } else {
+	psStringAppend(&query, " WHERE");
+    }
+
+    if (faulted) {
+	psStringAppend(&query, " %s", " detProcessedImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " detProcessedImfile.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "rawImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+bool revertprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code", "fault", "==");
+
+    psString query = pxDataGet("dettool_revertprocessedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detProcessedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+bool updateprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, 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_STR(data_state, config->args, "-data_state", true, false);
+
+    if (!setProcessedImfileDataState(config, det_id, exp_id, class_id, data_state)) {
+	return false;
+    }
+    return true;
+}
+
+bool pendingcleanup_processedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+
+    psString query = pxDataGet("dettool_pendingcleanup_processedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detCleanupProcessedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// XXX SQL missing
+bool donecleanup_processedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+
+    psString query = pxDataGet("dettool_donecleanup_processedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detDoneCleanup_processedimfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_residexp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_residexp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_residexp.c	(revision 22322)
@@ -0,0 +1,479 @@
+/*
+ * dettool_residexp.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt & EAM; IfA
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+/*
+  The SQL returns a list of exposures for which all component class ids have had residuals
+  created.  This list includes the detrend id, iteration, exposure id, detrend type, and
+  whether the exposure was included in the stack for this iteration.
+*/
+
+bool toresidexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_toresidexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingResidExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+/*
+  The SQL returns a list of exposures for which all component class ids have had residuals
+  created.  This list includes the detrend id, iteration, exposure id, detrend type, and
+  whether the exposure was included in the stack for this iteration.
+*/
+
+bool addresidexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // limit search by det_id, iteration, exp_id
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false);
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false); // required
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false); // Required if code == 0
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_skewness, config->args, "-bg_skewness", false, false);
+    PXOPT_LOOKUP_F64(bg_kurtosis, config->args, "-bg_kurtosis", false, false);
+    PXOPT_LOOKUP_F64(bin_stdev, config->args, "-bin_stdev", false, false);
+    PXOPT_LOOKUP_F64(fringe_0, config->args, "-fringe_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_1, config->args, "-fringe_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_2, config->args, "-fringe_2", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_0, config->args, "-fringe_resid_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_1, config->args, "-fringe_resid_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_2, config->args, "-fringe_resid_2", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+    PXOPT_LOOKUP_BOOL(reject, config->args, "-reject", false);
+
+    psString query = pxDataGet("dettool_toresidexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+	psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+	psStringAppend(&query, " WHERE %s", whereClause);
+	psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        // XXX check psError here
+        psError(PS_ERR_UNKNOWN, false, "no detResidImfile rows found");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    if (!detResidExpInsert(
+	    config->dbh,
+            det_id,
+            iteration,
+            exp_id,
+            recipe,
+            bg,
+            bg_stdev,
+            bg_mean_stdev,
+            bg_skewness,
+            bg_kurtosis,
+            bin_stdev,
+            fringe_0,
+            fringe_1,
+            fringe_2,
+            fringe_resid_0,
+            fringe_resid_1,
+            fringe_resid_2,
+            user_1,
+            user_2,
+            user_3,
+            user_4,
+            user_5,
+            path_base,
+	    "full",
+            !reject,
+            code
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+bool residexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",    "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-recip",     "recipe", "==");
+
+    PXOPT_LOOKUP_U64(limit,    config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple,  config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(reject,  config->args, "-reject", false);
+
+    psString query = pxDataGet("dettool_residexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detResidExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detResidExp.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detResidExp.fault = 0");
+    }
+
+    // list only accepted rows
+    // XXX the usage of this flag is unclear : -reject means "accepted"?
+    if (reject) {
+        psStringAppend(&query, " %s", "AND detResidExp.accept != 0");
+    } 
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detResidExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool revertresidexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",    "exp_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertresidexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detResidExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+
+bool updateresidexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(reject, config->args, "-reject", false);
+
+    // build a query to search by det_id, iteration, exp_id
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+
+    // find the values we're going to set
+    // copy everything but det_id, iteration, & exp_id from the args and
+    // remove the '-' prefix
+    psMetadata *set = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, set, "-recip", "recipe", "==");
+    PXOPT_COPY_F64(config->args, set, "-bg", "bg", "==");
+    PXOPT_COPY_F64(config->args, set, "-bg_stdev", "bg_stdev", "==");
+    PXOPT_COPY_F64(config->args, set, "-bg_mean_stdev", "bg_mean_stdev", "==");
+    PXOPT_COPY_STR(config->args, set, "-path_base", "path_base", "==");
+    PXOPT_COPY_STR(config->args, set, "-data_state", "data_state", "==");
+
+    // double-check the allowed data_state values
+    PXOPT_LOOKUP_STR(data_state, config->args, "-data_state", false, false);
+    if (data_state) {
+	if (!isValidDataState (data_state)) return false;
+    }
+
+    // this can't be PXOPT_ macro-ized as reject is !'d
+    if (!psMetadataAddBool(set, PS_LIST_TAIL, "accept", 0, "==", !reject)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item accept");
+        psFree(set);
+        psFree(where);
+        return false;
+    }
+
+    long changed = psDBUpdateRows(config->dbh, "detResidExp", where, set);
+    psFree(set);
+    psFree(where);
+
+    if (changed < 0) {
+        psError(PS_ERR_UNKNOWN, false, "no rows were updated");
+        return false;
+    }
+
+    return true;
+}
+
+bool pendingcleanup_residexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",    "exp_id", "==");
+
+    psString query = pxDataGet("dettool_pendingcleanup_residexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detCleanupResidExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool donecleanup_residexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",    "exp_id", "==");
+
+    psString query = pxDataGet("dettool_donecleanup_residexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detDoneCleanup_residexp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_residimfile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_residimfile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_residimfile.c	(revision 22322)
@@ -0,0 +1,410 @@
+/*
+ * dettool_residimfile.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt & EAM; IfA
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+bool toresidimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_toresidimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingResidImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool addresidimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false);
+
+    PXOPT_LOOKUP_S64(ref_det_id, config->args, "-ref_det_id", true, false); // required
+    PXOPT_LOOKUP_S32(ref_iter, config->args, "-ref_iter", true, false);
+
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false); // required
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false); // required
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", (code == 0), false); // Required if code == 0
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false); // Required if code == 0
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_skewness, config->args, "-bg_skewness", false, false);
+    PXOPT_LOOKUP_F64(bg_kurtosis, config->args, "-bg_kurtosis", false, false);
+    PXOPT_LOOKUP_F64(bin_stdev, config->args, "-bin_stdev", false, false);
+    PXOPT_LOOKUP_F64(fringe_0, config->args, "-fringe_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_1, config->args, "-fringe_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_2, config->args, "-fringe_2", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_0, config->args, "-fringe_resid_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_1, config->args, "-fringe_resid_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_2, config->args, "-fringe_resid_2", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+
+    if (!detResidImfileInsert(
+	    config->dbh,
+            det_id,
+            iteration,
+            ref_det_id,
+            ref_iter,
+            exp_id,
+            class_id,
+            uri,
+            recipe,
+            bg,
+            bg_stdev,
+            bg_mean_stdev,
+            bg_skewness,
+            bg_kurtosis,
+            bin_stdev,
+            fringe_0,
+            fringe_1,
+            fringe_2,
+            fringe_resid_0,
+            fringe_resid_1,
+            fringe_resid_2,
+            user_1,
+            user_2,
+            user_3,
+            user_4,
+            user_5,
+            path_base,
+	    "full",
+            code
+    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+bool residimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",       "detResidImfile.det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration",    "detResidImfile.iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",       "detResidImfile.exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",     "detResidImfile.class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-recip",        "detResidImfile.recipe", "==");
+    PXOPT_COPY_STR(config->args, where, "-select_state", "detRun.state", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    psString query = pxDataGet("dettool_residimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    bool hasWhere = false;
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+	hasWhere = true;
+    }
+    psFree(where);
+
+    if (hasWhere) {
+	psStringAppend(&query, " AND");
+    } else {
+	psStringAppend(&query, " WHERE");
+    }
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", " detResidImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " detResidImfile.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "rawResidImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool revertresidimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",  "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertresidimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detResidImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+bool updateresidimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", true, 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_STR(data_state, config->args, "-data_state", true, false);
+    if (!setResidImfileDataState(config, det_id, iteration, exp_id, class_id, data_state)) {
+	return false;
+    }
+    return true;
+}
+
+bool pendingcleanup_residimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",  "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+
+    psString query = pxDataGet("dettool_pendingcleanup_residimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detCleanupResidImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool donecleanup_residimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",  "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+
+    psString query = pxDataGet("dettool_donecleanup_residimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detDoneCleanup_residimfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_stack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_stack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/dettool_stack.c	(revision 22322)
@@ -0,0 +1,457 @@
+/*
+ * dettool_stack.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt & EAM; IfA
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dettool.h"
+
+static psArray *searchRawImfiles(pxConfig *config);
+
+bool tostackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_tostacked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingStackedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// this is NOT a command-line mode : it is used by 'addstackedMode'
+static psArray *searchRawImfiles(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+
+    // select exp_ids from detInputExp matching det_id & iteration
+    // where query should be pre-generated
+    psArray *detInputExp = detInputExpSelectRowObjects(config->dbh, where, 0);
+    psFree (where);
+
+    if (!detInputExp) {
+        psError(PS_ERR_UNKNOWN, false, "no rawExp rows found");
+        return NULL;
+    }
+
+    // generate where query with just the exp_ids
+    psMetadata *where_exp_ids = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(detInputExp); i++) {
+        detInputExpRow *row = detInputExp->data[i];
+        if (!psMetadataAddS64(where_exp_ids, PS_LIST_TAIL, "exp_id",
+                PS_META_DUPLICATE_OK, "==", row->exp_id)
+        ) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+            psFree(detInputExp);
+            psFree(where_exp_ids);
+            return NULL;
+        }
+    }
+    psFree(detInputExp);
+
+    // select rawImfiles with matching exp_ids
+    psArray *rawImfiles =
+        rawImfileSelectRowObjects(config->dbh, where_exp_ids, 0);
+    psFree(where_exp_ids);
+    if (!rawImfiles) {
+        psError(PS_ERR_UNKNOWN, false, "no rawImfile rows found");
+        return NULL;
+    }
+
+    return rawImfiles;
+}
+
+bool addstackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // det_id, iteration, class_id, uri, & recipe are required
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // Required if code == 0
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", (code == 0), false);
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false);
+
+    // optional
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+
+    // correlate the class_id against the input exposure(s)
+    // searchRawImfiles defines a where clause to search by det_id and iteration
+    psArray *rawImfiles = searchRawImfiles(config);
+
+    if (!rawImfiles) {
+        psError(PS_ERR_UNKNOWN, false, "failed to get rawImfiles from db");
+        return false;
+    }
+
+    bool valid_class_id = false;
+    if (rawImfiles) {
+        for (long i = 0; i < psArrayLength(rawImfiles); i++) {
+            if (!rawImfiles->data[i]) {
+                fprintf (stderr, "*");
+                continue;
+            }
+            if (strcmp(class_id, ((rawImfileRow *)rawImfiles->data[i])->class_id) == 0) {
+                valid_class_id = true;
+                break;
+            }
+        }
+        psFree(rawImfiles);
+    }
+
+    if (!valid_class_id) {
+        psError(PS_ERR_UNKNOWN, true,
+            "class_id can not be correlated with the input exposures");
+        return false;
+    }
+
+    // create a new detStackedImfile object
+    detStackedImfileRow *stackedImfile = detStackedImfileRowAlloc(
+            det_id,
+            iteration,
+            class_id,
+            uri,
+            recipe,
+            bg,
+            bg_stdev,
+            bg_mean_stdev,
+            user_1,
+            user_2,
+            user_3,
+            user_4,
+            user_5,
+	    "full",
+            code
+        );
+
+    // insert the new row into the detProcessedImfile table
+    if (!detStackedImfileInsertObject(config->dbh, stackedImfile)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(stackedImfile);
+        return false;
+    }
+
+    psFree(stackedImfile);
+
+    return true;
+}
+
+bool stackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-recip",     "recipe", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_stacked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detStackedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detStackedImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detStackedImfile.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "rawImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+bool revertstackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertstacked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detStackedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+bool updatestackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+    PXOPT_LOOKUP_STR(data_state, config->args, "-data_state", true, false);
+
+    if (!setStackedImfileDataState(config, det_id, iteration, class_id, data_state)) {
+	return false;
+    }
+    return true;
+}
+
+bool pendingcleanup_stackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "detStackedImfile.det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "detStackedImfile.iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "detStackedImfile.class_id", "==");
+
+    psString query = pxDataGet("dettool_pendingcleanup_stacked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detCleanupStackedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// XXX SQL missing
+bool donecleanup_stackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-det_id", "detStackedImfile.det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "detStackedImfile.iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "detStackedImfile.class_id", "==");
+
+    psString query = pxDataGet("dettool_donecleanup_stacked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detDoneCleanup_stacked", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/difftool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/difftool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/difftool.c	(revision 22322)
@@ -0,0 +1,1241 @@
+/*
+ * difftool.c
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ippdb.h>
+
+#include "pxtools.h"
+#include "difftool.h"
+
+static bool definerunMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool addinputskyfileMode(pxConfig *config);
+static bool inputskyfileMode(pxConfig *config);
+static bool todiffskyfileMode(pxConfig *config);
+static bool adddiffskyfileMode(pxConfig *config);
+static bool diffskyfileMode(pxConfig *config);
+static bool revertdiffskyfileMode(pxConfig *config);
+static bool definepoprunMode(pxConfig *config);
+static bool definebyqueryMode(pxConfig *config);
+static bool pendingcleanuprunMode(pxConfig *config);
+static bool pendingcleanupskyfileMode(pxConfig *config);
+static bool donecleanupMode(pxConfig *config);
+static bool updatediffskyfileMode(pxConfig *config);
+
+static bool setdiffRunState(pxConfig *config, psS64 diff_id, const char *state);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = difftoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(DIFFTOOL_MODE_DEFINERUN,             definerunMode);
+        MODECASE(DIFFTOOL_MODE_UPDATERUN,             updaterunMode);
+        MODECASE(DIFFTOOL_MODE_ADDINPUTSKYFILE,       addinputskyfileMode);
+        MODECASE(DIFFTOOL_MODE_INPUTSKYFILE,          inputskyfileMode);
+        MODECASE(DIFFTOOL_MODE_TODIFFSKYFILE,         todiffskyfileMode);
+        MODECASE(DIFFTOOL_MODE_ADDDIFFSKYFILE,        adddiffskyfileMode);
+        MODECASE(DIFFTOOL_MODE_DIFFSKYFILE,           diffskyfileMode);
+        MODECASE(DIFFTOOL_MODE_REVERTDIFFSKYFILE,     revertdiffskyfileMode);
+        MODECASE(DIFFTOOL_MODE_DEFINEPOPRUN,          definepoprunMode);
+        MODECASE(DIFFTOOL_MODE_DEFINEBYQUERY,         definebyqueryMode);
+        MODECASE(DIFFTOOL_MODE_PENDINGCLEANUPRUN,     pendingcleanuprunMode);
+        MODECASE(DIFFTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupskyfileMode);
+        MODECASE(DIFFTOOL_MODE_DONECLEANUP,           donecleanupMode);
+        MODECASE(DIFFTOOL_MODE_UPDATEDIFFSKYFILE,     updatediffskyfileMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+static bool definerunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required options
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+    PXOPT_LOOKUP_STR(skycell_id, config->args, "-skycell_id", true, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-tess_id", true, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-reduction", false, false);
+
+    // default
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    diffRunRow *run = diffRunRowAlloc(
+            0,          // ID
+            "reg",      // state
+            label,
+            reduction,
+            workdir,
+            NULL,       // dvodb
+            registered,
+            skycell_id,
+            tess_id
+    );
+    if (!run) {
+        psError(PS_ERR_UNKNOWN, false, "failed to alloc diffRun object");
+        return true;
+    }
+    if (!diffRunInsertObject(config->dbh, run)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(run);
+        return true;
+    }
+
+    // get the assigned diff_id
+    run->diff_id = psDBLastInsertID(config->dbh);
+
+    if (!diffRunPrintObject(stdout, run, !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print object");
+            psFree(run);
+            return false;
+    }
+
+    psFree(run);
+
+    return true;
+}
+
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required options
+    PXOPT_LOOKUP_S64(diff_id, config->args, "-diff_id", true, false);
+    PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
+
+    if (state) {
+        // set detRun.state to state
+        return setdiffRunState(config, diff_id, state);
+    }
+
+    return true;
+}
+
+
+static bool addinputskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required options
+    PXOPT_LOOKUP_S64(diff_id, config->args, "-diff_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_S64(stack_id, config->args, "-stack_id", false, false);
+    PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", false, false);
+    PXOPT_LOOKUP_STR(kind, config->args, "-kind", false, false);
+
+    // defaults to false
+    PXOPT_LOOKUP_BOOL(template, config->args, "-template", false);
+
+    // must provide either stack_id or warp_id but not BOTH
+    if (!(stack_id || warp_id)) {
+        psError(PS_ERR_UNKNOWN, true, "either -stack_id or -warp_id must be specified");
+        return false;
+    }
+    if (stack_id && warp_id) {
+        psError(PS_ERR_UNKNOWN, true, "either -stack_id or -warp_id must be specified");
+        return false;
+    }
+
+    // if a warp_id was provided we need to lookup the skycell_id and tess_id
+    // from the warpRun
+    psString skycell_id = NULL;
+    psString tess_id = NULL;
+    if (warp_id) {
+        if (!p_psDBRunQuery(config->dbh, "SELECT * from diffRun WHERE diff_id = %" PRId64, diff_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, false, "diff_id %" PRId64 " not found", diff_id);
+            psFree(output);
+            return false;
+        }
+
+        diffRunRow *run = diffRunObjectFromMetadata(output->data[0]);
+        skycell_id = run->skycell_id;
+        tess_id = run->tess_id;
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!diffInputSkyfileInsert(config->dbh,
+            diff_id,
+            template,
+            stack_id ? stack_id : PS_MAX_S64, // defined or NULL
+            warp_id ? warp_id : PS_MAX_S64, // defined or NULL
+            skycell_id,
+            tess_id,
+            kind
+        )) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, "SELECT count(diff_id) FROM diffRun JOIN diffInputSkyfile USING(diff_id) WHERE diff_id = %" PRId64, diff_id)) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "diff_id %" PRId64 " not found", diff_id);
+        psFree(output);
+        return false;
+    }
+
+    bool status;
+    psS32 count = psMetadataLookupS32(&status, output->data[0], "count(diff_id)");
+
+    if (count == 2) {
+        if (!setdiffRunState(config, diff_id, "new")) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+    }
+
+    // point of no return
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool inputskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where,  "-diff_id", "diff_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "skycell_id", "==");
+    PXOPT_COPY_STR(config->args, where,  "-tess_id", "tess_id", "==");
+    PXOPT_COPY_STR(config->args, where,  "-kind", "kind", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(template, config->args, "-template", false);
+    PXOPT_LOOKUP_BOOL(input, config->args, "-input", false);
+
+    if (template && input) {
+        // User apparently wants both, which is the default behaviour
+        template = false;
+        input = false;
+    }
+
+    // find all rawImfiles matching the default query
+    psString query = pxDataGet("difftool_inputskyfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    psString whereClause = NULL;
+    if (psListLength(where->list)) {
+        whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringPrepend(&whereClause, "\n AND ");
+    }
+    psFree(where);
+
+    // Add condition to get only templates or only inputs
+    {
+        psString templateClause = NULL;
+        if (template) {
+            psStringAppend(&templateClause, " %s", " template != 0");
+        } else if (input) {
+            psStringAppend(&templateClause, " %s", " template = 0");
+        }
+        if (templateClause) {
+            psStringAppend(&whereClause, "\n AND %s", templateClause);
+        }
+        psFree(templateClause);
+    }
+
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, whereClause, whereClause)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(whereClause);
+        psFree(query);
+        return false;
+    }
+    psFree(whereClause);
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("difftool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "diffInputSkyfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool todiffskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-diff_id", "diffRun.diff_id", "==");
+    PXOPT_COPY_STR(config->args, where,  "-label", "diffRun.label", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("difftool_todiffskyfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("difftool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "diffSkyfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool adddiffskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(diff_id, config->args, "-diff_id", true, false); // required
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", (code == 0), false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", (code == 0), false);
+
+    // optional
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F32(dtime_diff, config->args, "-dtime_diff", false, false);
+    PXOPT_LOOKUP_F32(dtime_match, config->args, "-dtime_match", false, false);
+    PXOPT_LOOKUP_F32(dtime_phot, config->args, "-dtime_phot", false, false);
+    PXOPT_LOOKUP_S32(stamps_num, config->args, "-stamps_num", false, false);
+    PXOPT_LOOKUP_F32(stamps_mean, config->args, "-stamps_mean", false, false);
+    PXOPT_LOOKUP_F32(stamps_rms, config->args, "-stamps_rms", false, false);
+    PXOPT_LOOKUP_S32(sources, config->args, "-sources", false, false);
+    PXOPT_LOOKUP_STR(hostname, config->args, "-hostname", false, false);
+    PXOPT_LOOKUP_F32(good_frac, config->args, "-good_frac", false, false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!diffSkyfileInsert(config->dbh,
+                           diff_id,
+                           uri,
+                           path_base,
+                           bg,
+                           bg_stdev,
+                           stamps_num,
+                           stamps_mean,
+                           stamps_rms,
+                           sources,
+                           dtime_diff,
+                           dtime_match,
+                           dtime_phot,
+                           hostname,
+                           good_frac,
+                           code
+          )) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!setdiffRunState(config, diff_id, "full")) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // point of no return
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool diffskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-diff_id", "diffSkyfile.diff_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "diffSkyfile.skycell_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-tess_id", "diffSkyfile.tess_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code", "diffSkyfile.fault", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "rawExp.exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_name", "rawExp.exp_name", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("difftool_skyfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("difftool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "diffSkyfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertdiffskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-diff_id", "diffSkyfile.diff_id", "==");
+    PXOPT_COPY_STR(config->args, where,  "-label", "diffRun.label", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",     "fault", "==");
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(where);
+        return false;
+    }
+
+    int num;                            // Number affected
+
+    // Update state to 'new'
+    {
+        psString query = pxDataGet("difftool_revertdiffskyfile_update.sql");
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+        psFree(query);
+
+        num = psDBAffectedRows(config->dbh);
+
+        if (num < 1) {
+            psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+    }
+
+    // Delete product
+    {
+        psString query = pxDataGet("difftool_revertdiffskyfile_delete.sql");
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+        psFree(query);
+
+        if (psDBAffectedRows(config->dbh) != num) {
+            psError(PS_ERR_UNKNOWN, false, "Updated and deleted different number of entries!");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+    }
+
+    psFree(where);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool setdiffRunState(pxConfig *config, psS64 diff_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid diffRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE diffRun SET state = '%s' WHERE diff_id = %"PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, state, diff_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for diff_id %"PRId64, diff_id);
+        return false;
+    }
+
+    return true;
+}
+
+// Generate a single populated run
+static bool populatedrun(psArray *list, // List of runs, to print
+                         const char *workdir, // Working directory
+                         const char *skycell_id, // Skycell identifier
+                         const char *tess_id, // Tessellation identifier
+                         const char *label, // label
+                         const char *reduction, // reduction
+                         psS64 input_warp_id, // Warp identifier for input image, PS_MAX_S64 for none
+                         psS64 input_stack_id, // Stack identifier for input image, PS_MAX_S64 for none
+                         psS64 template_warp_id, // Warp identifier for template image, PS_MAX_S64 for none
+                         psS64 template_stack_id, // Stack identifier for template image, PS_MAX_S64 for none
+                         pxConfig *config // Configuration
+                         )
+{
+    PS_ASSERT_STRING_NON_EMPTY(workdir, false);
+    PS_ASSERT_STRING_NON_EMPTY(skycell_id, false);
+    PS_ASSERT_STRING_NON_EMPTY(tess_id, false);
+    if ((input_warp_id == PS_MAX_S64 && input_stack_id == PS_MAX_S64) ||
+        (input_warp_id != PS_MAX_S64 && input_stack_id != PS_MAX_S64)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "One, and only one, input must be defined.");
+        return false;
+    }
+    if ((template_warp_id == PS_MAX_S64 && template_stack_id == PS_MAX_S64) ||
+        (template_warp_id != PS_MAX_S64 && template_stack_id != PS_MAX_S64)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "One, and only one, template must be defined.");
+        return false;
+    }
+
+    // default
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+
+    diffRunRow *run = diffRunRowAlloc(
+            0,          // ID
+            "reg",      // state
+            label,
+            reduction,
+            workdir,
+            NULL,       // dvodb
+            registered,
+            skycell_id,
+            tess_id
+    );
+
+    if (!run) {
+        psError(PS_ERR_UNKNOWN, false, "failed to alloc diffRun object");
+        return true;
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!diffRunInsertObject(config->dbh, run)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(run);
+        return true;
+    }
+
+    // get the assigned diff_id
+    run->diff_id = psDBLastInsertID(config->dbh);
+
+    // Template
+    if (!diffInputSkyfileInsert(config->dbh,
+            run->diff_id,
+            true,
+            template_stack_id,
+            template_warp_id,
+            skycell_id,
+            tess_id,
+            NULL    // kind
+        )) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // Input
+    if (!diffInputSkyfileInsert(config->dbh,
+            run->diff_id,
+            false,
+            input_stack_id,
+            input_warp_id,
+            skycell_id,
+            tess_id,
+            NULL    // kind
+        )) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    char *query = "UPDATE diffRun SET state = 'new' WHERE diff_id = '%" PRId64 "'";
+    if (!p_psDBRunQuery(config->dbh, query, run->diff_id)) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to set state to run for diff_id %" PRId64, run->diff_id);
+        return false;
+    }
+
+    // point of no return
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (list) {
+        psArrayAdd(list, list->n, run);
+    }
+
+    psFree(run);
+
+    return true;
+}
+
+
+static bool definepoprunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false); // required options
+    PXOPT_LOOKUP_STR(skycell_id, config->args, "-skycell_id", true, false); // required options
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-tess_id", true, false); // required options
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-reduction", false, false);
+    PXOPT_LOOKUP_S64(template_warp_id, config->args, "-template_warp_id", false, false);
+    PXOPT_LOOKUP_S64(template_stack_id, config->args, "-template_stack_id", false, false);
+    PXOPT_LOOKUP_S64(input_warp_id, config->args, "-input_warp_id", false, false);
+    PXOPT_LOOKUP_S64(input_stack_id, config->args, "-input_stack_id", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    if (template_stack_id && template_warp_id) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Only one template can be defined.");
+        return false;
+    }
+    if (!template_stack_id && !template_warp_id) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "No template has been defined (-template_stack_id or -template_warp_id)");
+        return false;
+    }
+
+    if (input_stack_id && input_warp_id) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Only one input can be defined.");
+        return false;
+    }
+    if (!input_stack_id && !input_warp_id) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "No input has been defined (-input_stack_id or -input_warp_id)");
+        return false;
+    }
+
+    psArray *list = psArrayAllocEmpty(16); // List of runs, to print
+
+    if (!populatedrun(list, workdir, skycell_id, tess_id, label, reduction,
+                      input_warp_id ? input_warp_id : PS_MAX_S64,
+                      input_stack_id ? input_stack_id : PS_MAX_S64,
+                      template_warp_id ? template_warp_id : PS_MAX_S64,
+                      template_stack_id ? template_stack_id : PS_MAX_S64,
+                      config)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to create populated diffRun");
+        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 definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warpsToDiff.warp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "warpsToDiff.skycell_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-tess_id", "warpsToDiff.tess_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter", "warpsToDiff.filter", "==");
+    PXOPT_COPY_STR(config->args, where, "-stack_label", "stacksForDiff.stack_label", "==");
+    PXOPT_COPY_STR(config->args, where, "-warp_label", "warpsToDiff.warp_label", "==");
+    PXOPT_COPY_STR(config->args, where,  "-kind", "warpsToDiff.kind", "==");
+    PXOPT_COPY_F32(config->args, where,  "-good_frac", "warpsToDiff.good_frac", ">=");
+
+    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);
+    PXOPT_LOOKUP_BOOL(newTemplates, config->args, "-new-templates", false);
+
+    // find all things to queue
+    psString query = pxDataGet("difftool_definebyquery.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (newTemplates) {
+        // Warps that haven't been diffed OR warps that can take a newer template in the diff
+        psStringAppend(&query, " WHERE (diff_id IS NULL OR best_stack_id > current_stack_id)");
+    } else {
+        // Only warps that haven't been diffed
+        psStringAppend(&query, " WHERE diff_id IS NULL");
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("difftool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    psArray *list = psArrayAllocEmpty(16); // List of runs, to print
+    long numGood = 0;                   // Number of good rows added
+    for (long i = 0; i < output->n; i++) {
+        psMetadata *row = output->data[i]; // Output row from query
+
+        // Selected parameters
+        bool mdok;                      // Status of MD lookup
+        const char *skycell_id = psMetadataLookupStr(&mdok, row, "skycell_id");
+        if (!mdok || !skycell_id) {
+            psWarning("skycell_id not found --- ignoring row %ld", i);
+            continue;
+        }
+        const char *tess_id = psMetadataLookupStr(&mdok, row, "tess_id");
+        if (!mdok || !tess_id) {
+            psWarning("tess_id not found --- ignoring row %ld", i);
+            continue;
+        }
+        psS64 stack_id = psMetadataLookupS64(&mdok, row, "best_stack_id");
+        if (!mdok) {
+            psWarning("stack_id not found --- ignoring row %ld", i);
+            continue;
+        }
+        psS64 warp_id = psMetadataLookupS64(&mdok, row, "warp_id");
+        if (!mdok) {
+            psWarning("warp_id not found --- ignoring row %ld", i);
+            continue;
+        }
+
+        if (!populatedrun(list, workdir, skycell_id, tess_id, label, reduction, warp_id, PS_MAX_S64, PS_MAX_S64,
+                          stack_id, config)) {
+            psWarning("Unable to add run for %s,%s,%" PRId64 ",%" PRId64, skycell_id, tess_id,
+                      warp_id, stack_id);
+            psErrorClear();
+            continue;
+        }
+
+        numGood++;
+    }
+    psFree(output);
+
+    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)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("difftool_pendingcleanuprun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("difftool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "diffPendingCleanupRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool pendingcleanupskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(diff_id, config->args, "-diff_id", false, false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    if (diff_id) {
+        PXOPT_COPY_S64(config->args, where, "-diff_id", "diff_id", "==");
+    }
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("difftool_pendingcleanupskyfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("difftool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "diffPendingCleanupSkyfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+    return true;
+}
+
+
+static bool donecleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("difftool_donecleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("difftool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "diffDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+    return true;
+}
+
+static bool updatediffskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", true, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-diff_id",   "diff_id",   "==");
+
+    if (!pxSetFaultCode(config->dbh, "diffSkyfile", where, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        psFree (where);
+        return false;
+    }
+    psFree (where);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/difftool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/difftool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/difftool.h	(revision 22322)
@@ -0,0 +1,45 @@
+/*
+ * difftool.h
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef DIFFTOOL_H
+#define DIFFTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    DIFFTOOL_MODE_NONE           = 0x0,
+    DIFFTOOL_MODE_DEFINERUN,
+    DIFFTOOL_MODE_UPDATERUN,
+    DIFFTOOL_MODE_ADDINPUTSKYFILE,
+    DIFFTOOL_MODE_INPUTSKYFILE,
+    DIFFTOOL_MODE_TODIFFSKYFILE,
+    DIFFTOOL_MODE_ADDDIFFSKYFILE,
+    DIFFTOOL_MODE_DIFFSKYFILE,
+    DIFFTOOL_MODE_REVERTDIFFSKYFILE,
+    DIFFTOOL_MODE_DEFINEPOPRUN,
+    DIFFTOOL_MODE_DEFINEBYQUERY,
+    DIFFTOOL_MODE_PENDINGCLEANUPRUN,
+    DIFFTOOL_MODE_PENDINGCLEANUPSKYFILE,
+    DIFFTOOL_MODE_DONECLEANUP,
+    DIFFTOOL_MODE_UPDATEDIFFSKYFILE,
+} difftoolMode;
+
+pxConfig *difftoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // DIFFTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/difftoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/difftoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/difftoolConfig.c	(revision 22322)
@@ -0,0 +1,223 @@
+/*
+ * difftoolConfig.c
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "difftool.h"
+
+pxConfig *difftoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    psTime *now = psTimeGetNow(PS_TIME_TAI);
+
+    // -definerun
+    psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-workdir", 0,            "define workdir (required)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-skycell_id",  0,            "define skycell ID (required)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-tess_id",  0,            "define tessellation ID (required)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-label",  0,            "define label", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-reduction",  0,            "define reduction class", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-registered",  0,            "time detrend run was registered", now);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-diff_id", 0,            "define diff ID (required)", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0,            "set state (required)", NULL);
+
+    // -addinputskyfile
+    psMetadata *addinputskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addinputskyfileArgs, PS_LIST_TAIL, "-diff_id", 0,            "define diff ID (required)", 0);
+    psMetadataAddS64(addinputskyfileArgs, PS_LIST_TAIL, "-stack_id", 0,            "define stack ID", 0);
+    psMetadataAddS64(addinputskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,            "define warp ID", 0);
+    psMetadataAddStr(addinputskyfileArgs, PS_LIST_TAIL, "-kind", 0,            "define kind", NULL);
+    psMetadataAddBool(addinputskyfileArgs, PS_LIST_TAIL, "-template",  0,            "this sky cell file is the subtrahend", false);
+
+    // -inputskyfile
+    psMetadata *inputskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(inputskyfileArgs, PS_LIST_TAIL, "-diff_id", 0,            "search by diff ID", 0);
+    psMetadataAddS64(inputskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warp ID", 0);
+    psMetadataAddStr(inputskyfileArgs, PS_LIST_TAIL, "-skycell_id", 0,            "search by skycell ID", NULL);
+    psMetadataAddStr(inputskyfileArgs, PS_LIST_TAIL, "-tess_id", 0,            "search by tess ID", NULL);
+    psMetadataAddStr(inputskyfileArgs, PS_LIST_TAIL, "-kind", 0,            "search by kind", NULL);
+    psMetadataAddBool(inputskyfileArgs, PS_LIST_TAIL, "-template",  0,            "find only subtrahend", false);
+    psMetadataAddBool(inputskyfileArgs, PS_LIST_TAIL, "-input", 0, "find only minuend", false);
+    psMetadataAddU64(inputskyfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(inputskyfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -todiffskyfile
+    psMetadata *todiffskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(todiffskyfileArgs, PS_LIST_TAIL, "-diff_id", 0,            "search by diff ID", 0);
+    psMetadataAddStr(todiffskyfileArgs, PS_LIST_TAIL, "-label", 0, "search by label", 0);
+    psMetadataAddU64(todiffskyfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(todiffskyfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -adddiffskyfile
+    psMetadata *adddiffskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(adddiffskyfileArgs, PS_LIST_TAIL, "-diff_id", 0,            "define warp ID (required)", 0);
+    psMetadataAddS16(adddiffskyfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+    psMetadataAddStr(adddiffskyfileArgs, PS_LIST_TAIL, "-uri", 0,            "define URI of file", 0);
+    psMetadataAddStr(adddiffskyfileArgs, PS_LIST_TAIL, "-path_base", 0,            "define base output location", 0);
+    psMetadataAddF64(adddiffskyfileArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(adddiffskyfileArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF32(adddiffskyfileArgs, PS_LIST_TAIL, "-dtime_diff",  0,            "define elapsed processing time", NAN);
+    psMetadataAddF32(adddiffskyfileArgs, PS_LIST_TAIL, "-dtime_match", 0, "define match processing time", NAN);
+    psMetadataAddF32(adddiffskyfileArgs, PS_LIST_TAIL, "-dtime_phot", 0, "define photometry processing time", NAN);
+    psMetadataAddS32(adddiffskyfileArgs, PS_LIST_TAIL, "-stamps_num",  0,            "define subtraction stamp number", 0);
+    psMetadataAddF32(adddiffskyfileArgs, PS_LIST_TAIL, "-stamps_mean", 0, "define subtraction stamp mean", NAN);
+    psMetadataAddF32(adddiffskyfileArgs, PS_LIST_TAIL, "-stamps_rms",  0,            "define subtraction stamp rms", 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);
+
+    // -diffskyfile
+    psMetadata *diffskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(diffskyfileArgs, PS_LIST_TAIL, "-diff_id", 0,            "search by warp ID", 0);
+    psMetadataAddStr(diffskyfileArgs , PS_LIST_TAIL, "-skycell_id",  0,            "define skycell ID", NULL);
+    psMetadataAddStr(diffskyfileArgs, PS_LIST_TAIL, "-tess_id",  0,            "define tessellation ID", NULL);
+    psMetadataAddS64(diffskyfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "define exposure ID", 0);
+    psMetadataAddStr(diffskyfileArgs , PS_LIST_TAIL, "-exp_name",  0,         "define exposure name", NULL);
+    psMetadataAddU64(diffskyfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(diffskyfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddS16(diffskyfileArgs, PS_LIST_TAIL, "-code",  0,            "deifine fault code", 0);
+
+    // -revertdiffskyfile
+    psMetadata *revertdiffskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(revertdiffskyfileArgs, PS_LIST_TAIL, "-diff_id", 0,            "search by diff ID", 0);
+    psMetadataAddStr(revertdiffskyfileArgs, PS_LIST_TAIL, "-label", 0, "search by label", NULL);
+    psMetadataAddS16(revertdiffskyfileArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -definepoprun
+    psMetadata *definepoprunArgs = psMetadataAlloc();
+    psMetadataAddStr(definepoprunArgs, PS_LIST_TAIL, "-workdir", 0,            "define workdir (required)", NULL);
+    psMetadataAddTime(definepoprunArgs, PS_LIST_TAIL, "-registered",  0,            "time detrend run was registered", now);
+    psMetadataAddStr(definepoprunArgs, PS_LIST_TAIL, "-skycell_id",  0,            "define skycell ID (required)", NULL);
+    psMetadataAddStr(definepoprunArgs, PS_LIST_TAIL, "-tess_id",  0,            "define tessellation ID (required)", NULL);
+    psMetadataAddStr(definepoprunArgs, PS_LIST_TAIL, "-label",  0,            "define label", NULL);
+    psMetadataAddStr(definepoprunArgs, PS_LIST_TAIL, "-reduction",  0,            "define reduction class", NULL);
+    psMetadataAddBool(definepoprunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddS64(definepoprunArgs, PS_LIST_TAIL, "-template_warp_id", 0,            "define warp ID for template", 0);
+    psMetadataAddS64(definepoprunArgs, PS_LIST_TAIL, "-template_stack_id", 0,            "define stack ID for template", 0);
+    psMetadataAddS64(definepoprunArgs, PS_LIST_TAIL, "-input_warp_id", 0,            "define warp ID for input", 0);
+    psMetadataAddS64(definepoprunArgs, PS_LIST_TAIL, "-input_stack_id", 0,            "define stack ID for input", 0);
+
+    // -definebyquery
+    psMetadata *definebyqueryArgs = psMetadataAlloc();
+    psMetadataAddS64(definebyqueryArgs, PS_LIST_TAIL, "-diff_id", 0, "search by diff ID", 0);
+    psMetadataAddS64(definebyqueryArgs, PS_LIST_TAIL, "-warp_id", 0, "search by warp ID", 0);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-skycell_id", 0, "search by skycell ID", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-tess_id", 0, "search by tess ID", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-filter", 0, "search by filter", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-stack_label", 0, "search by stack label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-warp_label", 0, "search by warp label", NULL);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-good_frac", 0, "minimum good fraction of skycell", NAN);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-kind", 0, "search by kind", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-workdir", 0, "define workdir (required)", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_label", 0, "search by label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label",  0, "define label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-reduction",  0, "define reduction class", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-registered", 0, "time detrend run was registered", now);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-new-templates", 0, "also search for diffs with new template", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -pendingcleanuprun
+    psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanuprunArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(pendingcleanuprunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanuprunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -pendingcleanupskyfile
+    psMetadata *pendingcleanupskyfileArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanupskyfileArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddS64(pendingcleanupskyfileArgs, PS_LIST_TAIL, "-diff_id", 0,          "search by diff ID", 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);
+
+    // -donecleanup
+    psMetadata *donecleanupArgs = psMetadataAlloc();
+    psMetadataAddStr(donecleanupArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(donecleanupArgs, PS_LIST_TAIL, "-simple",  0,          "use the simple output format", false);
+    psMetadataAddU64(donecleanupArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    // -updatediffskyfile
+    psMetadata *updatediffskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(updatediffskyfileArgs, PS_LIST_TAIL, "-diff_id", 0,      "define diff ID (required)", 0);
+    psMetadataAddS16(updatediffskyfileArgs, PS_LIST_TAIL, "-code", 0,         "set fault code (required)", 0);
+
+
+    psFree(now);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definerun",        "", DIFFTOOL_MODE_DEFINERUN,         definerunArgs);
+    PXOPT_ADD_MODE("-updaterun",        "", DIFFTOOL_MODE_UPDATERUN,         updaterunArgs);
+    PXOPT_ADD_MODE("-addinputskyfile",  "", DIFFTOOL_MODE_ADDINPUTSKYFILE,   addinputskyfileArgs);
+    PXOPT_ADD_MODE("-inputskyfile",     "", DIFFTOOL_MODE_INPUTSKYFILE,      inputskyfileArgs);
+    PXOPT_ADD_MODE("-todiffskyfile",    "", DIFFTOOL_MODE_TODIFFSKYFILE,     todiffskyfileArgs);
+    PXOPT_ADD_MODE("-adddiffskyfile",   "", DIFFTOOL_MODE_ADDDIFFSKYFILE,    adddiffskyfileArgs);
+    PXOPT_ADD_MODE("-diffskyfile",      "", DIFFTOOL_MODE_DIFFSKYFILE,       diffskyfileArgs);
+    PXOPT_ADD_MODE("-revertdiffskyfile","", DIFFTOOL_MODE_REVERTDIFFSKYFILE, revertdiffskyfileArgs);
+    PXOPT_ADD_MODE("-definepoprun",     "", DIFFTOOL_MODE_DEFINEPOPRUN,      definepoprunArgs);
+    PXOPT_ADD_MODE("-definebyquery",    "", DIFFTOOL_MODE_DEFINEBYQUERY,     definebyqueryArgs);
+    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);
+    PXOPT_ADD_MODE("-donecleanup",           "show runs that have been cleaned",     DIFFTOOL_MODE_DONECLEANUP,          donecleanupArgs);
+    PXOPT_ADD_MODE("-updatediffskyfile",     "update fault code for a diffskyfile",  DIFFTOOL_MODE_UPDATEDIFFSKYFILE,          updatediffskyfileArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/fakemagic
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/fakemagic	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/fakemagic	(revision 22322)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007  Joshua Hoblitt
+#
+# $Id: fakemagic,v 1.1 2007-11-05 23:29:54 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version pass_through );
+use Pod::Usage qw( pod2usage );
+
+my ($output);
+GetOptions(
+    'output|o=s'    => \$output,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Required options: --output <filepath>", -exitval => 2 )
+        unless defined $output;
+
+open(my $fh, ">$output") or die "can't open file $output: $!";
+close($fh) or die "can't close file $output: $!";
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/faketool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/faketool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/faketool.c	(revision 22322)
@@ -0,0 +1,1203 @@
+/*
+ * faketool.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "pxtools.h"
+#include "pxdata.h"
+
+#include "faketool.h"
+
+static bool definebyqueryMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool pendingexpMode(pxConfig *config);
+static bool pendingimfileMode(pxConfig *config);
+static bool addprocessedimfileMode(pxConfig *config);
+static bool processedimfileMode(pxConfig *config);
+static bool revertprocessedimfileMode(pxConfig *config);
+static bool updateprocessedimfileMode(pxConfig *config);
+static bool blockMode(pxConfig *config);
+static bool maskedMode(pxConfig *config);
+static bool unmaskedMode(pxConfig *config);
+static bool unblockMode(pxConfig *config);
+static bool pendingcleanuprunMode(pxConfig *config);
+static bool pendingcleanupimfileMode(pxConfig *config);
+static bool donecleanupMode(pxConfig *config);
+static bool tocleanedimfileMode(pxConfig *config);
+static bool tofullimfileMode(pxConfig *config);
+static bool topurgedimfileMode(pxConfig *config);
+
+static bool fakeProcessedCompleteExp(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv) {
+    psLibInit(NULL);
+
+    pxConfig *config = faketoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(FAKETOOL_MODE_DEFINEBYQUERY,           definebyqueryMode);
+        MODECASE(FAKETOOL_MODE_UPDATERUN,               updaterunMode);
+        MODECASE(FAKETOOL_MODE_PENDINGEXP,              pendingexpMode);
+        MODECASE(FAKETOOL_MODE_PENDINGIMFILE,           pendingimfileMode);
+        MODECASE(FAKETOOL_MODE_ADDPROCESSEDIMFILE,      addprocessedimfileMode);
+        MODECASE(FAKETOOL_MODE_PROCESSEDIMFILE,         processedimfileMode);
+        MODECASE(FAKETOOL_MODE_REVERTPROCESSEDIMFILE,   revertprocessedimfileMode);
+        MODECASE(FAKETOOL_MODE_UPDATEPROCESSEDIMFILE,   updateprocessedimfileMode);
+        MODECASE(FAKETOOL_MODE_BLOCK,                   blockMode);
+        MODECASE(FAKETOOL_MODE_MASKED,                  maskedMode);
+        MODECASE(FAKETOOL_MODE_UNMASKED,                unmaskedMode);
+        MODECASE(FAKETOOL_MODE_UNBLOCK,                 unblockMode);
+        MODECASE(FAKETOOL_MODE_PENDINGCLEANUPRUN,       pendingcleanuprunMode);
+        MODECASE(FAKETOOL_MODE_PENDINGCLEANUPIMFILE,    pendingcleanupimfileMode);
+        MODECASE(FAKETOOL_MODE_DONECLEANUP,             donecleanupMode);
+        MODECASE(FAKETOOL_MODE_TOCLEANEDIMFILE,         tocleanedimfileMode);
+        MODECASE(FAKETOOL_MODE_TOFULLIMFILE,            tofullimfileMode);
+        MODECASE(FAKETOOL_MODE_TOPURGEDIMFILE,          topurgedimfileMode);
+ 
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-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, "-label", "label", "==");
+    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, "-comment", "comment", "LIKE");
+
+    if (!psListLength(where->list)
+        && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(expgroup, config->args, "-set_expgroup", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb", false, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-set_tess_id", false, false);
+    PXOPT_LOOKUP_STR(end_stage, config->args, "-set_end_stage", false, false);
+
+    // default
+    PXOPT_COPY_STR(config->args, where, "-exp_type", "exp_type", "==");
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+
+    // find the exp_id of all the exposures that we want to queue up.
+    psString query = pxDataGet("faketool_find_camrun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    psFree(where);
+
+    if (pretend) {
+        // then stop before running the query
+        fprintf(stderr, "%s\n", query);
+        psFree(query);
+        return true;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("faketool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // start a transaction so we don't end up with an exp without any associted
+    // imfiles
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+
+    // loop over our list of cam_ids
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+
+        bool status;
+        psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for cam_id");
+            psFree(output);
+            return false;
+        }
+
+        // queue the exp
+        if (!pxfakeQueueByCamID(config, cam_id, workdir, label, reduction, expgroup, dvodb, tess_id, end_stage)) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to trying to queue cam_id: %" PRId64, cam_id);
+            psFree(output);
+            return false;
+        }
+    }
+    psFree(output);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_name", "exp_name", "==");
+    PXOPT_COPY_STR(config->args, where, "-inst", "camera", "==");
+    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
+    PXOPT_COPY_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, "-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", "fakeRun.label", "==");
+
+    if (!psListLength(where->list)
+        && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        where = NULL;
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+
+    if ((!state) && (!label)) {
+        psError(PXTOOLS_ERR_DATA, false, "parameters are required");
+        psFree(where);
+        return false;
+    }
+
+    if (state) {
+        // set fakeRun.state to state
+        if (!pxfakeRunSetStateByQuery(config, where, state)) {
+            psFree(where);
+            return false;
+        }
+    }
+
+    if (label) {
+        // set fakeRun.label to label
+        if (!pxfakeRunSetLabelByQuery(config, where, label)) {
+            psFree(where);
+            return false;
+        }
+    }
+
+    psFree(where);
+
+    return true;
+}
+
+
+static bool pendingexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    PXOPT_COPY_S64(config->args, where, "-cam_id", "cam_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
+
+    psString query = pxDataGet("faketool_find_pendingexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "fakePendingExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool pendingimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "rawImfile.class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-inst", "rawExp.telescope", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter", "rawExp.filter", "==");
+
+    psString query = pxDataGet("faketool_pendingimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("faketool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "fakePendingImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // fake_id, ext_tag, class_id are required
+    PXOPT_LOOKUP_S64(fake_id, config->args,     "-fake_id", true, false);
+    PXOPT_LOOKUP_S64(exp_id, config->args,      "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args,    "-class_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(uri, config->args,         "-uri", false, false);
+
+    PXOPT_LOOKUP_F32(dtime_fake, config->args,  "-dtime_fake", false, false);
+    PXOPT_LOOKUP_STR(hostname, config->args,    "-hostname", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args,   "-path_base", false, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args,        "-code", false, false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!fakeProcessedImfileInsert(config->dbh,
+                                   fake_id,
+                                   exp_id,
+                                   class_id,
+                                   uri,
+                                   dtime_fake,
+                                   hostname,
+                                   path_base,
+                                   "full",
+                                   code,
+                                   NULL         // epoch
+            )) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // XXX I've decided to make the transaction cover the Exp migration as
+    // well.  Otherwise, if the last imfile in an exp is moved and the exp
+    // migration fails then the data base is left in a satuation where the exp
+    // migration can't happen.
+
+    if (!fakeProcessedCompleteExp(config)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool processedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-fake_id", "fakeRun.fake_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "rawExp.exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "rawImfile.class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-inst", "rawExp.telescope", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter", "rawExp.filter", "==");
+
+    psString query = pxDataGet("faketool_processedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "fakeProcessedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND fakeProcessedImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND fakeProcessedImfile.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("faketool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "fakeProcessedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool revertprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "fakeRun.label", "==");
+    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, "-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", "<");
+
+    if (!psListLength(where->list)
+        && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    psString query = pxDataGet("faketool_revertprocessedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+static bool updateprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", true, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+
+    if (!pxSetFaultCode(config->dbh, "fakeProcessedImfile", where, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        psFree(where);
+        return false;
+    }
+    psFree(where);
+
+    return true;
+}
+
+
+static bool blockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    if (!fakeMaskInsert(config->dbh, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool maskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    if (where->list->n < 1) {
+        psFree(where);
+        where = NULL;
+    }
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM fakeMask");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psFree(where);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("faketool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "fakeMask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// return the list of labels which are NOT blocked
+static bool unmaskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    if (where->list->n < 1) {
+        psFree(where);
+        where = NULL;
+    }
+
+    psString query = pxDataGet("faketool_unmasked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, "fakeUnmask");
+        psFree(where);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("faketool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "fakeUnmask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool unblockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    char *query = "DELETE FROM fakeMask WHERE label = '%s'";
+
+    if (!p_psDBRunQuery(config->dbh, query, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool pendingcleanuprunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("faketool_pendingcleanuprun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("faketool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "fakePendingCleanupRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool pendingcleanupimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(fake_id, config->args, "-fake_id", false, false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    if (fake_id) {
+        PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
+    }
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("faketool_pendingcleanupimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("faketool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "fakePendingCleanupImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool donecleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("faketool_donecleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("faketool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "fakeDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool fakeProcessedCompleteExp(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // look for completed fakePendingExp
+    // migrate them to fakeProccessedExp & camPendingExp
+    psString query = pxDataGet("faketool_completely_processed_exp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("faketool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i];
+
+        fakeRunRow *fakeRun = fakeRunObjectFromMetadata(row);
+        // set fakeRun.state to 'full'
+        if (!pxfakeRunSetState(config, fakeRun->fake_id, "full")) {
+            psError(PS_ERR_UNKNOWN, false, "failed to change fakeRun.state for fake_id: %" PRId64, fakeRun->fake_id);
+            psFree(fakeRun);
+            psFree(output);
+            return false;
+        }
+
+        // should we stop here or proceed on to the cam stage?
+        // NULL for end_stage means go as far as possible
+        if ((fakeRun->end_stage && psStrcasestr(fakeRun->end_stage, "fake")) || !fakeRun->tess_id) {
+            psFree(fakeRun);
+            continue;
+        }
+        // else continue on...
+
+        // camQueueFakeID() can only be run after fakeRun.state has been set to
+        // stop
+        if (!pxwarpQueueByFakeID(config,
+                    fakeRun->fake_id,
+                    fakeRun->workdir,
+                    fakeRun->label,
+                    fakeRun->dvodb,
+                    fakeRun->tess_id,
+                    fakeRun->end_stage
+        )) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to queue camPendingExp");
+            psFree(fakeRun);
+            psFree(output);
+            return false;
+        }
+        psFree(fakeRun);
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// update fakeProcessedImfile.data_state to given value.
+// afterwards, if all imfiles in the exposure have the new state, update the state for the exposure as well
+// shared code for the modes -tocleanedimfile -tofullimfile -topurgedimfile
+
+static bool change_imfile_data_state(pxConfig *config, psString data_state, psString run_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // fake_id, class_id are required
+    PXOPT_LOOKUP_S64(fake_id, config->args, "-fake_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    psString query = pxDataGet("faketool_change_imfile_data_state.sql");
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // note only updates if fakeRun.state = run_state
+    if (!p_psDBRunQuery(config->dbh, query, data_state, fake_id, class_id, run_state)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+    
+    query = pxDataGet("faketool_change_exp_state.sql");
+    if (!p_psDBRunQuery(config->dbh, query, data_state, fake_id, data_state)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+static bool tocleanedimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "cleaned", "goto_cleaned");
+}
+static bool tofullimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "full", "update");
+}
+static bool topurgedimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "purged", "goto_purged");
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/faketool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/faketool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/faketool.h	(revision 22322)
@@ -0,0 +1,50 @@
+/*
+ * faketool.h
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef FAKETOOL_H
+#define FAKETOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    FAKETOOL_MODE_NONE      = 0x0,
+    FAKETOOL_MODE_DEFINEBYQUERY,
+    FAKETOOL_MODE_UPDATERUN,
+    FAKETOOL_MODE_PENDINGIMFILE,
+    FAKETOOL_MODE_PENDINGEXP,
+    FAKETOOL_MODE_ADDPROCESSEDIMFILE,
+    FAKETOOL_MODE_PROCESSEDIMFILE,
+    FAKETOOL_MODE_REVERTPROCESSEDIMFILE,
+    FAKETOOL_MODE_UPDATEPROCESSEDIMFILE,
+    FAKETOOL_MODE_BLOCK,
+    FAKETOOL_MODE_MASKED,
+    FAKETOOL_MODE_UNMASKED,
+    FAKETOOL_MODE_UNBLOCK,
+    FAKETOOL_MODE_RETRYPROCESSEDIMFILE,
+    FAKETOOL_MODE_PENDINGCLEANUPRUN,
+    FAKETOOL_MODE_PENDINGCLEANUPIMFILE,
+    FAKETOOL_MODE_DONECLEANUP,
+    FAKETOOL_MODE_TOCLEANEDIMFILE,
+    FAKETOOL_MODE_TOFULLIMFILE,
+    FAKETOOL_MODE_TOPURGEDIMFILE,
+} FAKETOOLMode;
+
+pxConfig *faketoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // FAKETOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/faketoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/faketoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/faketoolConfig.c	(revision 22322)
@@ -0,0 +1,340 @@
+/*
+ * faketoolConfig.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "faketool.h"
+
+pxConfig *faketoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (! config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    // -definebyquery
+    psMetadata *queueArgs = psMetadataAlloc();
+    // XXX need to allow multiple exp_ids
+    psMetadataAddS64(queueArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exp_id", 0);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-exp_name",  0,            "search by exp_name", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-inst",  0,            "search for camera", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-telescope",  0,            "search for telescope", NULL);
+    psMetadataAddTime(queueArgs, PS_LIST_TAIL, "-dateobs_begin", 0,            "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(queueArgs, PS_LIST_TAIL, "-dateobs_end", 0,            "search for exposures by time (<)", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-exp_tag",  0,            "search by exp_tag", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-label",  0,            "search by label", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-exp_type",  0,            "search by exp_type", "object");
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-filelevel",  0,            "search by filelevel", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-reduction",  0,            "search by reduction class", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-filter",  0,            "search for filter", NULL);
+    psMetadataAddF32(queueArgs, PS_LIST_TAIL, "-airmass_min",  0,            "define min airmass", NAN);
+    psMetadataAddF32(queueArgs, PS_LIST_TAIL, "-airmass_max",  0,            "define max airmass", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-ra_min",  0,            "define min", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-ra_max",  0,            "define max", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-decl_min",  0,            "define min", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-decl_max",  0,            "define max", NAN);
+    psMetadataAddF32(queueArgs, PS_LIST_TAIL, "-exp_time_min",  0,            "define min", NAN);
+    psMetadataAddF32(queueArgs, PS_LIST_TAIL, "-exp_time_max",  0,            "define max", NAN);
+    psMetadataAddF32(queueArgs, PS_LIST_TAIL, "-sat_pixel_frac_min",  0,            "define max fraction of saturated pixels", NAN);
+    psMetadataAddF32(queueArgs, PS_LIST_TAIL, "-sat_pixel_frac_max",  0,            "define min fraction of saturated pixels", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-bg_min",  0,            "define max", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-bg_max",  0,            "define max", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-bg_stdev_min",  0,            "define max", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-bg_stdev_max",  0,            "define max", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-bg_mean_stdev_min",  0,            "define max", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-bg_mean_stdev_max",  0,            "define max", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-alt_min",  0,            "define min", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-alt_max",  0,            "define max", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-az_min",  0,            "define min", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-az_max",  0,            "define max", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-ccd_temp_min",  0,            "define min ccd tempature", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-ccd_temp_max",  0,            "define max ccd tempature", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-posang_min",  0,            "define min rotator position angle", NAN);
+    psMetadataAddF64(queueArgs, PS_LIST_TAIL, "-posang_max",  0,            "define max rotator position angle", NAN);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-object",  0,            "search by exposure object", NULL);
+    psMetadataAddF32(queueArgs, PS_LIST_TAIL, "-solang_min",  0,            "define min solar angle", NAN);
+    psMetadataAddF32(queueArgs, PS_LIST_TAIL, "-solang_max",  0,            "define max solar angle", NAN);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-set_workdir",  0,            "define workdir", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-set_label",  0,            "define label", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-set_reduction",  0,            "define reduction class", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-set_expgroup",  0,            "define exposure group", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-set_dvodb",  0,            "define DVO db", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-set_tess_id",  0,            "define tessellation identifier", NULL);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-set_end_stage",  0,            "define end stage", NULL);
+    psMetadataAddBool(queueArgs, PS_LIST_TAIL, "-pretend",  0,            "do not actually modify the database", false);
+    psMetadataAddBool(queueArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
+    psMetadataAddStr(queueArgs, PS_LIST_TAIL, "-comment",     0,        "search by comment field (LIKE comparison)", NULL);
+
+
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-fake_id", 0,            "search by fake 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", "object");
+    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);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label", 0, "search by label", NULL);
+
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state", 0,            "set state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label", 0,            "set label", NULL);
+    psMetadataAddBool(updaterunArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
+
+    // -pendingexp
+    psMetadata *pendingexpArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingexpArgs, PS_LIST_TAIL, "-fake_id", 0,            "search by fake ID", 0);
+    psMetadataAddStr(pendingexpArgs, PS_LIST_TAIL, "-label", 0, "search by label", NULL);
+    psMetadataAddS64(pendingexpArgs, PS_LIST_TAIL, "-cam_id", 0,            "search by camtool ID", 0);
+    psMetadataAddS64(pendingexpArgs, PS_LIST_TAIL, "-chip_id", 0,            "search by chiptool ID", 0);
+    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);
+
+    // -pendingimfile
+    psMetadata *pendingimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingimfileArgs, PS_LIST_TAIL, "-fake_id",  0,            "search by fake ID", 0);
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-label", 0, "search by label", NULL);
+    psMetadataAddS64(pendingimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exposure ID", 0);
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-inst",  0,            "search by camera of interest", NULL);
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-filter",  0,            "search by filter of interest", NULL);
+    psMetadataAddU64(pendingimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(pendingimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addprocessedimfile
+    psMetadata *addprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addprocessedimfileArgs, PS_LIST_TAIL, "-fake_id",  0,            "define fake ID (required)", 0);
+    psMetadataAddS64(addprocessedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "define exposure ID (required)", 0);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "define class ID (required)", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-uri",  0,            "define URL", NULL);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-dtime_fake",  0,            "define elapsed time for detrend (seconds)", NAN);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-hostname",  0,            "define hostname", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+    psMetadataAddS16(addprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+
+    // -processedimfile
+    psMetadata *processedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(processedimfileArgs, PS_LIST_TAIL, "-fake_id",  0,            "define fake ID", 0);
+    psMetadataAddS64(processedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "define exposure ID", 0);
+    psMetadataAddStr(processedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "define class ID", NULL);
+    psMetadataAddStr(processedimfileArgs, PS_LIST_TAIL, "-inst",  0,            "define camera of interest", NULL);
+    psMetadataAddStr(processedimfileArgs, PS_LIST_TAIL, "-filter",  0,            "define filter of interest", NULL);
+    psMetadataAddU64(processedimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -updateprocessedimfile
+    psMetadata *updateprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(updateprocessedimfileArgs, PS_LIST_TAIL, "-fake_id",  0,            "search by fake ID", 0);
+    psMetadataAddS64(updateprocessedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exposure ID", 0);
+    psMetadataAddStr(updateprocessedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search by class ID", NULL);
+    psMetadataAddS16(updateprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code (required)", 0);
+
+    // -revertprocessedimfile
+    psMetadata *revertprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(revertprocessedimfileArgs, PS_LIST_TAIL, "-fake_id", 0,            "search by fake ID", 0);
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-label", 0, "search by label", NULL);
+    psMetadataAddS64(revertprocessedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exp_id", 0);
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-exp_name",  0,            "search by exp_name", NULL);
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-inst",  0,            "search for camera", NULL);
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-telescope",  0,            "search for telescope", NULL);
+    psMetadataAddTime(revertprocessedimfileArgs, PS_LIST_TAIL, "-dateobs_begin", 0,            "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(revertprocessedimfileArgs, PS_LIST_TAIL, "-dateobs_end", 0,            "search for exposures by time (<)", NULL);
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-exp_tag",  0,            "search by exp_tag", NULL);
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-exp_type",  0,            "search by exp_type", "object");
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-filelevel",  0,            "search by filelevel", NULL);
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-reduction",  0,            "search by reduction class", NULL);
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-filter",  0,            "search for filter", NULL);
+    psMetadataAddF32(revertprocessedimfileArgs, PS_LIST_TAIL, "-airmass_min",  0,            "define min airmass", NAN);
+    psMetadataAddF32(revertprocessedimfileArgs, PS_LIST_TAIL, "-airmass_max",  0,            "define max airmass", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-ra_min",  0,            "define min", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-ra_max",  0,            "define max", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-decl_min",  0,            "define min", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-decl_max",  0,            "define max", NAN);
+    psMetadataAddF32(revertprocessedimfileArgs, PS_LIST_TAIL, "-exp_time_min",  0,            "define min", NAN);
+    psMetadataAddF32(revertprocessedimfileArgs, PS_LIST_TAIL, "-exp_time_max",  0,            "define max", NAN);
+    psMetadataAddF32(revertprocessedimfileArgs, PS_LIST_TAIL, "-sat_pixel_frac_min",  0,            "define max fraction of saturated pixels", NAN);
+    psMetadataAddF32(revertprocessedimfileArgs, PS_LIST_TAIL, "-sat_pixel_frac_max",  0,            "define min fraction of saturated pixels", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-bg_min",  0,            "define max", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-bg_max",  0,            "define max", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-bg_stdev_min",  0,            "define max", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-bg_stdev_max",  0,            "define max", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-bg_mean_stdev_min",  0,            "define max", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-bg_mean_stdev_max",  0,            "define max", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-alt_min",  0,            "define min", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-alt_max",  0,            "define max", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-az_min",  0,            "define min", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-az_max",  0,            "define max", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-ccd_temp_min",0,            "define min ccd tempature", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-ccd_temp_max",0,            "define max ccd tempature", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-posang_min",  0,            "define min rotator position angle", NAN);
+    psMetadataAddF64(revertprocessedimfileArgs, PS_LIST_TAIL, "-posang_max",  0,            "define max rotator position angle", NAN);
+    psMetadataAddStr(revertprocessedimfileArgs, PS_LIST_TAIL, "-object",  0,            "search by exposure object", NULL);
+    psMetadataAddF32(revertprocessedimfileArgs, PS_LIST_TAIL, "-solang_min",  0,            "define min solar angle", NAN);
+    psMetadataAddF32(revertprocessedimfileArgs, PS_LIST_TAIL, "-solang_max",  0,            "define max solar angle", NAN);
+
+    psMetadataAddBool(revertprocessedimfileArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
+    psMetadataAddS16(revertprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -block
+    psMetadata *blockArgs = psMetadataAlloc();
+    psMetadataAddStr(blockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to mask out (required)", NULL);
+
+    // -masked
+    psMetadata *maskedArgs = psMetadataAlloc();
+    psMetadataAddStr(maskedArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(maskedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(maskedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -unmasked
+    psMetadata *unmaskedArgs = psMetadataAlloc();
+    psMetadataAddStr(unmaskedArgs, PS_LIST_TAIL, "-label",  0,            "restrict to specified label", NULL);
+    psMetadataAddBool(unmaskedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(unmaskedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -unblock
+    psMetadata *unblockArgs = psMetadataAlloc();
+    psMetadataAddStr(unblockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to unmask (required)", NULL);
+
+    // -pendingcleanuprun
+    psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanuprunArgs,  PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(pendingcleanuprunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanuprunArgs,  PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -pendingcleanupimfile
+    psMetadata *pendingcleanupimfileArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanupimfileArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddS64(pendingcleanupimfileArgs, PS_LIST_TAIL, "-fake_id", 0,          "search by chip ID", 0);
+    psMetadataAddBool(pendingcleanupimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanupimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -donecleanup
+    psMetadata *donecleanupArgs = psMetadataAlloc();
+    psMetadataAddStr(donecleanupArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(donecleanupArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanupArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    // -tocleanedimfile
+    psMetadata *tocleanedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tocleanedimfileArgs, PS_LIST_TAIL, "-fake_id", 0,          "fake ID to update", 0);
+    psMetadataAddStr(tocleanedimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    // -tofullimfile
+    psMetadata *tofullimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tofullimfileArgs, PS_LIST_TAIL, "-fake_id", 0,          "fake ID to update", 0);
+    psMetadataAddStr(tofullimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    // -topurgedimfile
+    psMetadata *topurgedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(topurgedimfileArgs, PS_LIST_TAIL, "-fake_id", 0,          "fake ID to update", 0);
+    psMetadataAddStr(topurgedimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definebyquery",         "create runs from raw exposures",       FAKETOOL_MODE_DEFINEBYQUERY,                    queueArgs);
+    PXOPT_ADD_MODE("-updaterun",             "change fake run properties",           FAKETOOL_MODE_UPDATERUN,                updaterunArgs);
+    PXOPT_ADD_MODE("-pendingexp",            "show pending exposures",               FAKETOOL_MODE_PENDINGEXP,               pendingexpArgs);
+    PXOPT_ADD_MODE("-pendingimfile",         "show pending imfiles",                 FAKETOOL_MODE_PENDINGIMFILE,            pendingimfileArgs);
+    PXOPT_ADD_MODE("-addprocessedimfile",    "add a processed imfile",               FAKETOOL_MODE_ADDPROCESSEDIMFILE,       addprocessedimfileArgs);
+    PXOPT_ADD_MODE("-processedimfile",       "show processed imfiles",               FAKETOOL_MODE_PROCESSEDIMFILE,          processedimfileArgs);
+    PXOPT_ADD_MODE("-updateprocessedimfile","change procesed imfile properties",     FAKETOOL_MODE_UPDATEPROCESSEDIMFILE,    updateprocessedimfileArgs);
+    PXOPT_ADD_MODE("-revertprocessedimfile", "undo a processed imfile",              FAKETOOL_MODE_REVERTPROCESSEDIMFILE,    revertprocessedimfileArgs);
+    PXOPT_ADD_MODE("-block",                 "set a label block",                    FAKETOOL_MODE_BLOCK,          blockArgs);
+    PXOPT_ADD_MODE("-masked",                "show blocked labels",                  FAKETOOL_MODE_MASKED,         maskedArgs);
+    PXOPT_ADD_MODE("-unmasked",              "",                                     FAKETOOL_MODE_UNMASKED,       unmaskedArgs);
+    PXOPT_ADD_MODE("-unblock",               "remove a label block",                 FAKETOOL_MODE_UNBLOCK,        unblockArgs);
+    PXOPT_ADD_MODE("-pendingcleanuprun",     "show runs that need to be cleaned up", FAKETOOL_MODE_PENDINGCLEANUPRUN,    pendingcleanuprunArgs);
+    PXOPT_ADD_MODE("-pendingcleanupimfile",  "show runs that need to be cleaned up", FAKETOOL_MODE_PENDINGCLEANUPIMFILE, pendingcleanupimfileArgs);
+    PXOPT_ADD_MODE("-donecleanup",           "show runs that have been cleaned",     FAKETOOL_MODE_DONECLEANUP,          donecleanupArgs);
+    PXOPT_ADD_MODE("-tocleanedimfile",      "set imfile state to cleaned",           FAKETOOL_MODE_TOCLEANEDIMFILE,      tocleanedimfileArgs);
+    PXOPT_ADD_MODE("-tofullimfile",        "set imfile state to full",               FAKETOOL_MODE_TOFULLIMFILE,         tofullimfileArgs);
+    PXOPT_ADD_MODE("-topurgedimfile",      "set imfile state to purged",             FAKETOOL_MODE_TOPURGEDIMFILE,       topurgedimfileArgs);
+
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/flatcorr.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/flatcorr.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/flatcorr.c	(revision 22322)
@@ -0,0 +1,663 @@
+/*
+ * flatcorr.c
+ *
+ * Copyright (C) 2006-2007  Joshua Hoblitt
+ * Copyright (C) 2008  Eugene Magnier
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <ippdb.h>
+
+#include "pxtools.h"
+#include "pxchip.h"
+#include "flatcorr.h"
+
+static bool definebyqueryMode(pxConfig *config);
+static bool definerunMode(pxConfig *config);
+static bool addchipMode(pxConfig *config);
+static bool addcameraMode(pxConfig *config);
+static bool pendingprocessMode(pxConfig *config);
+static bool addprocessMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool inputexpMode(pxConfig *config);
+static bool inputimfileMode(pxConfig *config);
+
+static bool setflatcorrRunState(pxConfig *config, psS64 corr_id, const char *state);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = flatcorrConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(FLATCORR_MODE_DEFINEBYQUERY,  definebyqueryMode);
+        MODECASE(FLATCORR_MODE_DEFINERUN,      definerunMode);
+        MODECASE(FLATCORR_MODE_ADDCHIP,        addchipMode);
+        MODECASE(FLATCORR_MODE_ADDCAMERA,      addcameraMode);
+        MODECASE(FLATCORR_MODE_PENDINGPROCESS, pendingprocessMode);
+        MODECASE(FLATCORR_MODE_ADDPROCESS,     addprocessMode);
+        MODECASE(FLATCORR_MODE_UPDATERUN,      updaterunMode);
+        MODECASE(FLATCORR_MODE_INPUTEXP,       inputexpMode);
+        MODECASE(FLATCORR_MODE_INPUTIMFILE,    inputimfileMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where);
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(expgroup, config->args, "-set_expgroup", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb", false, false);
+    PXOPT_LOOKUP_STR(filter, config->args, "-set_filter", false, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-set_tess_id", false, false);
+    PXOPT_LOOKUP_STR(region, config->args, "-set_region", false, false);
+    // XXX probably should make the region in -set_region match ra_min, ra_max, etc
+
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find the exp_id of all the exposures that we want to queue up.
+    psString query = pxDataGet("chiptool_find_rawexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("flatcorr", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+	for (long i = 0; i < psArrayLength(output); i++) {
+	    // negative simple so the default is true
+	    if (!ippdbPrintMetadataRaw(stdout, output->data[i], !simple)) {
+		psError(PS_ERR_UNKNOWN, false, "failed to print array");
+		psFree(output);
+		return false;
+	    }
+	}
+        psFree(output);
+        return true;
+    }
+
+    // start a transaction so we don't end up with an exp without any associted
+    // imfiles
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+
+    // create a new flatcorrRun
+    if (!flatcorrRunInsert(
+	    config->dbh,
+            0,      // corr_id
+            dvodb,
+            filter,
+            "reg",  // state
+            workdir,
+            label,
+            reduction,
+	    region,
+	    NULL,
+	    0
+        )) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // figure out the ID of the flatcorrRun we just created
+    psS64 corr_id = psDBLastInsertID(config->dbh);
+
+    // loop over our list of exp_ids
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+
+        bool status;
+        psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+        if (!status) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for exp_id");
+            psFree(output);
+            return false;
+        }
+
+        // queue the exp : force this to stop at the chip stage
+        psS64 chip_id = pxchipQueueByExpTag(config, exp_id, workdir, label, reduction, expgroup, dvodb, tess_id, "chip");
+        if (!chip_id) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to trying to queue exp_id: %" PRId64, exp_id);
+            psFree(output);
+            return false;
+        }
+
+        // add a flatcorrChipLink to the flatcorr Run we just created
+        if (!flatcorrChipLinkInsert(config->dbh, corr_id, chip_id)) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+
+    }
+    psFree(output);
+
+    // set the flatcorrRun to a state of 'new'
+    if (!setflatcorrRunState(config, corr_id, "new")) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "failed to set run state");
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool definerunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(expgroup, config->args, "-set_expgroup", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb", false, false);
+    PXOPT_LOOKUP_STR(filter, config->args, "-set_filter", false, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-set_tess_id", false, false);
+    PXOPT_LOOKUP_STR(region, config->args, "-set_region", false, false);
+    // XXX probably should make the region in -set_region match ra_min, ra_max, etc
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // start a transaction so we don't end up with an exp without any associted
+    // imfiles
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // create a new flatcorrRun
+    flatcorrRunRow *row = flatcorrRunRowAlloc(
+	0,      // corr_id
+	dvodb,
+	filter,
+	"reg",  // state
+	workdir,
+	label,
+	reduction,
+	region,
+	NULL, // hostname
+	0 // fault
+        );
+
+    // create a new flatcorrRun
+    if (!flatcorrRunInsertObject(config->dbh,row)) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    
+    // figure out the ID of the flatcorrRun we just created
+    psS64 corr_id = psDBLastInsertID(config->dbh);
+    row->corr_id = corr_id;
+
+    flatcorrRunPrintObject (stdout, row, !simple);
+    return true;
+}
+
+static bool addchipMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(corr_id, config->args, "-corr_id", true, false);
+    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id", true, false);
+
+    if (!flatcorrChipLinkInsert(config->dbh, corr_id, chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+// select the flatcorr chip runs that have completed and for which there is no camera entry
+// queue a new camera run for them
+static bool addcameraMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple,  config->args, "-simple",  false);
+    PXOPT_LOOKUP_BOOL(limit,   config->args, "-limit",   false);
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+
+    psString query = pxDataGet("flatcorr_chiprundone.sql");
+    if (!query) {
+	psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+	return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+	psString limitString = psDBGenerateLimitSQL(limit);
+	psStringAppend(&query, " %s", limitString);
+	psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("flatcorr", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "flatcorr_addcamera", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    // start a transaction so we don't end up with an exp without any associted
+    // imfiles
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+
+    // loop over our list of chipRun rows
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+
+        bool status;
+        psS64 corr_id = psMetadataLookupS64(&status, md, "corr_id");
+        if (!status) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for corr_id");
+            psFree(output);
+            return false;
+        }
+
+        chipRunRow *row = chipRunObjectFromMetadata(md);
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into chipRun");
+            psFree(output);
+            return false;
+        }
+
+        // queue the exp
+        if (!pxcamQueueByChipID(
+		config,
+		row->chip_id,
+		row->workdir,
+		row->label,
+		row->reduction,
+		row->expgroup,
+		row->dvodb,
+		row->tess_id,
+		"camera")) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to trying to queue chip_id: %" PRId64, row->chip_id);
+            psFree(row);
+            psFree(output);
+            return false;
+        }
+
+	// figure out the ID of the flatcorrRun we just created
+	psS64 cam_id = psDBLastInsertID(config->dbh);
+
+	// add the camRun entry to the flatcorrCamLink table:
+        if (!flatcorrCamLinkInsert(config->dbh, corr_id, row->chip_id, cam_id)) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+	
+        psFree(row);
+    }
+    psFree(output);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return false;
+}
+
+// select the flatcorr chip runs that have completed and for which there is no camera entry
+// queue a new camera run for them
+static bool pendingprocessMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple,  config->args, "-simple",  false);
+    PXOPT_LOOKUP_BOOL(limit,   config->args, "-limit",   false);
+
+    psString query = pxDataGet("flatcorr_pendingprocess.sql");
+    if (!query) {
+	psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+	return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+	psString limitString = psDBGenerateLimitSQL(limit);
+	psStringAppend(&query, " %s", limitString);
+	psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("flatcorr", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "flatcorrPending", !simple)) {
+	psError(PS_ERR_UNKNOWN, false, "failed to print array");
+	psFree(output);
+	return false;
+    }
+
+    return true;
+}
+
+// select the flatcorr chip runs that have completed and for which there is no camera entry
+// queue a new camera run for them
+static bool inputexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(corr_id, config->args, "-corr_id", true, false);
+    PXOPT_LOOKUP_BOOL(simple,  config->args, "-simple",  false);
+    PXOPT_LOOKUP_BOOL(limit,   config->args, "-limit",   false);
+
+    char *query = psStringCopy ("SELECT * FROM flatcorrChipLink WHERE corr_id = %" PRId64);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+	psString limitString = psDBGenerateLimitSQL(limit);
+	psStringAppend(&query, " %s", limitString);
+	psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, corr_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("flatcorr", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "flatcorrPending", !simple)) {
+	psError(PS_ERR_UNKNOWN, false, "failed to print array");
+	psFree(output);
+	return false;
+    }
+
+    return true;
+}
+
+// select the flatcorr chip runs that have completed and for which there is no camera entry
+// queue a new camera run for them
+static bool inputimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id", true, false);
+    PXOPT_LOOKUP_BOOL(simple,  config->args, "-simple",  false);
+    PXOPT_LOOKUP_BOOL(limit,   config->args, "-limit",   false);
+    
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipProcessedImfile.chip_id", "==");
+
+    psString query = pxDataGet("flatcorr_inputimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+	psString limitString = psDBGenerateLimitSQL(limit);
+	psStringAppend(&query, " %s", limitString);
+	psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("flatcorr", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "flatcorrPending", !simple)) {
+	psError(PS_ERR_UNKNOWN, false, "failed to print array");
+	psFree(output);
+	return false;
+    }
+
+    return true;
+}
+
+// XXX need a fault state
+static bool addprocessMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(corr_id, config->args, "-corr_id", true, false);
+    PXOPT_LOOKUP_STR(hostname, config->args, "-hostname", false, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    char *query = "UPDATE flatcorrRun SET state = 'full', hostname = '%s', fault = '%hd' WHERE corr_id = %" PRId64;
+
+    if (!p_psDBRunQuery(config->dbh, query, hostname, code, corr_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to change state for corr_id %" PRId64, corr_id);
+        return false;
+    }
+
+    return true;
+}
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(corr_id, config->args, "-corr_id", true, false);
+    PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
+
+    if (!setflatcorrRunState(config, corr_id, state)) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "failed to set run state");
+        return false;
+    }
+
+    return true;
+}
+
+static bool setflatcorrRunState(pxConfig *config, psS64 corr_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!strcmp(state, "reg") && 
+	!strcmp(state, "new") && 
+	!strcmp(state, "full"))
+    {
+        psError(PS_ERR_UNKNOWN, false, "invalid state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE flatcorrRun SET state = '%s' WHERE corr_id = %" PRId64;
+
+    if (!p_psDBRunQuery(config->dbh, query, state, corr_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to change state for corr_id %" PRId64, corr_id);
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/flatcorr.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/flatcorr.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/flatcorr.h	(revision 22322)
@@ -0,0 +1,40 @@
+/*
+ * flatcorr.h
+ *
+ * Copyright (C) 2006-2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef FLATCORR_H
+#define FLATCORR_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    FLATCORR_MODE_NONE           = PXTOOL_MODE_NONE,
+    FLATCORR_MODE_DEFINEBYQUERY,
+    FLATCORR_MODE_DEFINERUN,
+    FLATCORR_MODE_ADDCHIP,
+    FLATCORR_MODE_ADDCAMERA,
+    FLATCORR_MODE_PENDINGPROCESS,
+    FLATCORR_MODE_ADDPROCESS,
+    FLATCORR_MODE_UPDATERUN,
+    FLATCORR_MODE_INPUTEXP,
+    FLATCORR_MODE_INPUTIMFILE
+} flatcorrMode;
+
+pxConfig *flatcorrConfig(pxConfig *config, int argc, char **argv);
+
+#endif // FLATCORR_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/flatcorrConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/flatcorrConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/flatcorrConfig.c	(revision 22322)
@@ -0,0 +1,152 @@
+/*
+ * flatcorrConfig.c
+ *
+ * Copyright (C) 2006-2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "flatcorr.h"
+
+pxConfig *flatcorrConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    psTime *now = psTimeGetNow(PS_TIME_TAI);
+
+    // -definebyquery
+    psMetadata *definebyqueryArgs = psMetadataAlloc();
+    // XXX need to allow multiple exp_ids
+    pxchipSetSearchArgs (definebyqueryArgs);
+
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_workdir",  0,            "define workdir", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_label",    0,            "define label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_reduction",0,            "define reduction class", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_expgroup", 0,            "define exposure group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_dvodb",    0,            "define DVO db", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_filter",   0,            "define filter", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_tess_id",  0,            "define tessalation", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_region",   0,            "define region", NULL);
+
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-pretend",  0,            "print the exposures that would be included in the detrend run and exit", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -definerun
+    psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_workdir",  0,            "define workdir", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_label",  0,            "define label", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_reduction",  0,            "define reduction class", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_expgroup",  0,            "define exposure group", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_dvodb",  0,            "define DVO db", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_filter",  0,            "define filter", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_tess_id",  0,            "define tessalation", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_region",  0,            "define region", NULL);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -addchip
+    psMetadata *addchipArgs = psMetadataAlloc();
+    psMetadataAddS64(addchipArgs, PS_LIST_TAIL, "-corr_id", 0,            "define Flat Correction ID (required)", 0);
+    psMetadataAddS64(addchipArgs, PS_LIST_TAIL, "-chip_id", 0,            "define Chip ID (required)", 0);
+
+    // -addcamera
+    psMetadata *addcameraArgs = psMetadataAlloc();
+    psMetadataAddU64 (addcameraArgs, PS_LIST_TAIL, "-limit",   0, "limit result set to N items", 0);
+    psMetadataAddBool(addcameraArgs, PS_LIST_TAIL, "-simple",  0, "use the simple output format", false);
+    psMetadataAddBool(addcameraArgs, PS_LIST_TAIL, "-pretend", 0, "use the simple output format", false);
+
+    // -pendingprocess
+    psMetadata *pendingprocessArgs = psMetadataAlloc();
+    psMetadataAddU64 (pendingprocessArgs, PS_LIST_TAIL, "-limit",  0, "limit result set to N items", 0);
+    psMetadataAddBool(pendingprocessArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -addprocess (XXX need to add fault and stats)
+    psMetadata *addprocessArgs = psMetadataAlloc();
+    psMetadataAddS64 (addprocessArgs, PS_LIST_TAIL, "-corr_id",  0, "add complete run for specified corr_id (required)", 0);
+    psMetadataAddStr (addprocessArgs, PS_LIST_TAIL, "-hostname", 0, "set hostname", NULL);
+    psMetadataAddS16 (addprocessArgs, PS_LIST_TAIL, "-code",     0, "set fault code", 0);
+
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-corr_id", 0, "define correction id (required)", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state",   0, "set state (required)", NULL);
+
+    // -inputexp
+    psMetadata *inputexpArgs = psMetadataAlloc();
+    psMetadataAddS64(inputexpArgs, PS_LIST_TAIL, "-corr_id", 0, "define correction id (required)", 0);
+    psMetadataAddBool(inputexpArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+    psMetadataAddU64(inputexpArgs, PS_LIST_TAIL, "-limit",   0, "limit result set to N items", 0);
+
+    // -inputimfile
+    psMetadata *inputimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(inputimfileArgs, PS_LIST_TAIL, "-chip_id", 0, "define chip id (required)", 0);
+    psMetadataAddBool(inputimfileArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+    psMetadataAddU64(inputimfileArgs, PS_LIST_TAIL, "-limit",   0, "limit result set to N items", 0);
+
+    psFree(now);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definebyquery",  "create a new, populated flat correction run",       FLATCORR_MODE_DEFINEBYQUERY,  definebyqueryArgs);
+    PXOPT_ADD_MODE("-definerun",      "create a new, empty flat correction run",           FLATCORR_MODE_DEFINERUN,      definerunArgs);
+    PXOPT_ADD_MODE("-addchip",        "add a chip to a flat correction run",               FLATCORR_MODE_ADDCHIP,        addchipArgs);
+    PXOPT_ADD_MODE("-addcamera",      "migrate completed chips to camera stage analysis",  FLATCORR_MODE_ADDCAMERA,      addcameraArgs);
+    PXOPT_ADD_MODE("-pendingprocess", "show flat correction runs needing to be processed", FLATCORR_MODE_PENDINGPROCESS, pendingprocessArgs);
+    PXOPT_ADD_MODE("-addprocess",     "report completed flat correction analysis",         FLATCORR_MODE_ADDPROCESS,     addprocessArgs);
+    PXOPT_ADD_MODE("-updaterun",      "change a flat calibration run's state",             FLATCORR_MODE_UPDATERUN,      updaterunArgs);
+    PXOPT_ADD_MODE("-inputexp",       "list exposures for a correction run",               FLATCORR_MODE_INPUTEXP,       inputexpArgs);
+    PXOPT_ADD_MODE("-inputimfile",    "list imfiles for a chip run",                       FLATCORR_MODE_INPUTIMFILE,    inputimfileArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/magictool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/magictool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/magictool.c	(revision 22322)
@@ -0,0 +1,1278 @@
+/*
+ * magictool.c
+ *
+ * Copyright (C) 2006-2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ippdb.h>
+
+#include "pxtools.h"
+#include "magictool.h"
+
+static bool definebyqueryMode(pxConfig *config);
+static psS64 definerunMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool addinputskyfileMode(pxConfig *config);
+static bool inputskyfileMode(pxConfig *config);
+static bool totreeMode(pxConfig *config);
+static bool inputtreeMode(pxConfig *config);
+static bool reverttreeMode(pxConfig *config);
+static bool toprocessMode(pxConfig *config);
+static bool addresultMode(pxConfig *config);
+static bool revertnodeMode(pxConfig *config);
+static bool inputsMode(pxConfig *config);
+static bool tomaskMode(pxConfig *config);
+static bool addmaskMode(pxConfig *config);
+static bool revertmaskMode(pxConfig *config);
+static bool maskMode(pxConfig *config);
+
+static bool setmagicRunState(pxConfig *config, psS64 magic_id, const char *state);
+static bool parseAndInsertNodeDeps(pxConfig *config, psS64 magic_id, const char *filename);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = magictoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(MAGICTOOL_MODE_DEFINEBYQUERY,  definebyqueryMode);
+        MODECASE(MAGICTOOL_MODE_DEFINERUN,      definerunMode);
+        MODECASE(MAGICTOOL_MODE_UPDATERUN,      updaterunMode);
+        MODECASE(MAGICTOOL_MODE_ADDINPUTSKYFILE,addinputskyfileMode);
+        MODECASE(MAGICTOOL_MODE_INPUTSKYFILE,   inputskyfileMode);
+        MODECASE(MAGICTOOL_MODE_TOTREE,         totreeMode);
+        MODECASE(MAGICTOOL_MODE_INPUTTREE,      inputtreeMode);
+        MODECASE(MAGICTOOL_MODE_REVERTTREE,     reverttreeMode);
+        MODECASE(MAGICTOOL_MODE_TOPROCESS,      toprocessMode);
+        MODECASE(MAGICTOOL_MODE_ADDRESULT,      addresultMode);
+        MODECASE(MAGICTOOL_MODE_REVERTNODE,     revertnodeMode);
+        MODECASE(MAGICTOOL_MODE_INPUTS,         inputsMode);
+        MODECASE(MAGICTOOL_MODE_TOMASK,         tomaskMode);
+        MODECASE(MAGICTOOL_MODE_ADDMASK,        addmaskMode);
+        MODECASE(MAGICTOOL_MODE_REVERTMASK,     revertmaskMode);
+        MODECASE(MAGICTOOL_MODE_MASK,           maskMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // Required
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", false, false);
+
+    // Optional
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-dvodb", false, false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // Create temporary table of the best diffs
+    {
+        psString query = pxDataGet("magictool_definebyquery_temp_create.sql");
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            return false;
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+        psFree(query);
+    }
+
+    // Insert list of best diffs into temporary table
+    {
+        psString query = pxDataGet("magictool_definebyquery_temp_insert.sql");
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            return false;
+        }
+
+        psMetadata *where = psMetadataAlloc();
+        PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+        PXOPT_COPY_F32(config->args, where, "-good_frac", "warpSkyfile.good_frac", ">=");
+
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+        psFree(where);
+
+        psString groupby = pxDataGet("magictool_definebyquery_temp_insert_groupby.sql");
+        if (!groupby) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            psFree(query);
+            return false;
+        }
+
+        psStringAppend(&query, " %s", groupby);
+        psFree(groupby);
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            return false;
+        }
+        psFree(query);
+    }
+
+    // Get list of exposures ready to magic
+    {
+        psString query = pxDataGet("magictool_definebyquery_select_part1.sql");
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            return false;
+        }
+
+        {
+            psMetadata *where = psMetadataAlloc();
+            PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+            PXOPT_COPY_F32(config->args, where, "-good_frac", "warpSkyfile.good_frac", ">=");
+
+            if (psListLength(where->list)) {
+                psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+                psStringAppend(&query, " AND %s", whereClause);
+                psFree(whereClause);
+            }
+            psFree(where);
+        }
+
+        psString part2 = pxDataGet("magictool_definebyquery_select_part2.sql");
+        if (!part2) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            return false;
+        }
+
+        psStringAppend(&query, " %s", part2);
+        psFree(part2);
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            return false;
+        }
+        psFree(query);
+    }
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+          case PS_ERR_DB_CLIENT:
+            psError(PXTOOLS_ERR_SYS, false, "database error");
+          case PS_ERR_DB_SERVER:
+            psError(PXTOOLS_ERR_PROG, false, "database error");
+          default:
+            psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("magictool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psString insert = pxDataGet("magictool_definebyquery_insert.sql"); // Insert query
+
+    psArray *list = psArrayAllocEmpty(16); // List of runs, to print
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i]; // Row of interest
+        psS64 exp_id = psMetadataLookupS64(NULL, row, "exp_id"); // Exposure identifier
+
+        // create a new magicRun for this group
+        magicRunRow *run = magicRunRowAlloc(0, exp_id, "run", workdir, "dirty", label, dvodb, registered, 0);
+        if (!run) {
+            psAbort("failed to alloc magicRun object");
+        }
+
+        if (!magicRunInsertObject(config->dbh, run)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(run);
+            psFree(insert);
+            psFree(output);
+            psFree(list);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        psS64 magic_id = psDBLastInsertID(config->dbh); // Assigned identifier
+        run->magic_id = magic_id;
+
+        psArrayAdd(list, list->n, run);
+        psFree(run);
+
+        // Create a suitable insertion query for this run
+        psString thisInsert = psStringCopy(insert);
+        {
+            psString idString = NULL;
+            psStringAppend(&idString, "%" PRId64, magic_id);
+            psStringSubstitute(&thisInsert, idString, "@MAGIC_ID@");
+            psFree(idString);
+        }
+        {
+            psString idString = NULL;
+            psStringAppend(&idString, "%" PRId64, exp_id);
+            psStringSubstitute(&thisInsert, idString, "@EXP_ID@");
+            psFree(idString);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, thisInsert, magic_id, exp_id)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(thisInsert);
+            psFree(insert);
+            psFree(output);
+            psFree(list);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+        psFree(thisInsert);
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(output);
+
+    if (!magicRunPrintObjects(stdout, list, !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print object");
+        psFree(list);
+        return false;
+    }
+
+    psFree(list);
+
+    return true;
+}
+
+static psS64 definerunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-dvodb", false, false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    magicRunRow *run = magicRunRowAlloc(
+            0,          // ID
+            exp_id,
+            "reg",      // state
+            workdir,
+            "dirty",    // workdir_state
+            label,
+            dvodb,
+            registered,
+            0
+    );
+
+    if (!run) {
+        psError(PS_ERR_UNKNOWN, false, "failed to alloc magicRun object");
+        return false;
+    }
+    if (!magicRunInsertObject(config->dbh, run)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(run);
+        return false;
+    }
+
+    // get the assigned warp_id
+    psS64 magic_id = psDBLastInsertID(config->dbh);
+    run->magic_id = magic_id;
+
+    if (!magicRunPrintObject(stdout, run, !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print object");
+            psFree(run);
+            return false;
+    }
+
+    psFree(run);
+
+    return magic_id;
+}
+
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(magic_id, config->args, "-magic_id", true, false);
+    PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
+
+    if (state) {
+        // set detRun.state to state
+        return setmagicRunState(config, magic_id, state);
+    }
+
+    return true;
+}
+
+
+static bool addinputskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(magic_id, config->args, "-magic_id", true, false);
+    PXOPT_LOOKUP_S64(diff_id, config->args, "-diff_id", true, false);
+    PXOPT_LOOKUP_STR(node, config->args, "-node", true, false);
+
+    magicInputSkyfileInsert(
+            config->dbh,
+            magic_id,
+            diff_id,
+            node
+    );
+
+    return true;
+}
+
+
+static bool inputskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-magic_id", "magic_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-diff_id", "diff_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-node", "node", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find all rawImfiles matching the default query
+    psString query = pxDataGet("magictool_inputskyfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "magicInputSkyfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("magictool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "magicInputSkyfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool totreeMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-magic_id", "magicRun.magic_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // look for "inputs" that need to processed
+    psString query = pxDataGet("magictool_totree.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("magictool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "totree", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool inputtreeMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(magic_id, config->args, "-magic_id", true, false);
+
+    // Optional values
+    PXOPT_LOOKUP_STR(dep_file, config->args, "-dep_file", false, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    if (code > 0) {
+        char *query = "UPDATE magicRun SET fault = %d, state = 'stop' WHERE magic_id = %" PRId64;
+        if (!p_psDBRunQuery(config->dbh, query, code, magic_id)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to set fault for magic_id %" PRId64, magic_id);
+            return false;
+        }
+        return true;
+    }
+
+    if (!parseAndInsertNodeDeps(config, magic_id, dep_file)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to parse file");
+        return false;
+    }
+
+    return true;
+}
+
+static bool reverttreeMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-magic_id", "magic_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code", "fault", "==");
+
+    psString query = psStringCopy("UPDATE magicRun SET fault = 0, state = 'run' WHERE fault != 0");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to revert");
+        return false;
+    }
+    return true;
+}
+
+
+static bool inputsMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-magic_id", "magic_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-node", "node", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("magictool_inputs.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("magictool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "magicNode", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+bool findBaseNodes(void *arg, pxNode *node)
+{
+    bool status = false;
+    psS64 done = psMetadataLookupS64(&status, node->data, "done");
+    if (!status) {
+        psAbort("failed to lookup value for done column");
+    }
+
+    if ((!pxNodeHasChildren(node)) && (!done)) {
+        // if this node has no child and it's not 'done', then push it's data
+        // onto the void *array
+        psArrayAdd((psArray *)arg, 0, node->data);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool findReadyNodes(void *arg, pxNode *node)
+{
+    if (!node) {
+        // It's not there --- must have failed
+        return false;
+    }
+
+    if (!node->data) {
+        // It's a leaf node, not done
+        return false;
+    }
+
+    if (psMetadataLookupBool(NULL, node->data, "done")) {
+        // It's already done
+        return true;
+    }
+
+    if (pxNodeHasChildren(node)) {
+        psListIterator *iter = psListIteratorAlloc(node->children, 0, false);
+        psMetadata *work = psMetadataCopy(NULL, node->data);
+        psMetadataRemoveKey(work, "dep");
+        psMetadataRemoveKey(work, "done");
+        pxNode *child = NULL;
+        while ((child = psListGetAndIncrement(iter))) {
+            psMetadata *data = child->data;
+            if (!data) {
+                // Child is a leaf node, not done
+                psFree(iter);
+                psFree(work);
+                return false;
+            }
+
+            bool status = false;
+            psS32 done = psMetadataLookupS32(&status, data, "done");
+            if (!status) {
+                psAbort("failed to lookup value for done column");
+            }
+            psS16 bad = psMetadataLookupS16(&status, data, "bad");
+            if (!status) {
+                psAbort("failed to lookup value for bad column");
+            }
+
+            if (!done || bad) {
+                // if a child isn't "done", give up on this node and continue
+                // to crawl the tree
+                psFree(iter);
+                psFree(work);
+                return true;
+            }
+        }
+        psFree(iter);
+        // if all this nodes children are done, then push it's data onto the
+        // void *array
+        psArrayAdd((psArray *)arg, 0, work);
+        psFree(work);
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool toprocessMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-magic_id", "magic_id", "==");
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // look for "inputs" that need to processed
+    psString query = pxDataGet("magictool_toprocess_inputs.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    psString whereClause = NULL;
+    if (psListLength(where->list)) {
+        whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("magictool", PS_LOG_INFO, "no rows found");
+        // psFree(output);
+        // return true;
+    }
+
+    // look for tree nodes that need to be processed
+    query = pxDataGet("magictool_toprocess_tree.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (whereClause) {
+        psStringAppend(&query, " %s", whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *magicTree = p_psDBFetchResult(config->dbh);
+    if (!magicTree) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(magicTree)) {
+        psTrace("magictool", PS_LOG_INFO, "no rows found");
+        psFree(magicTree);
+        return true;
+    }
+
+    psHash *forest = psHashAlloc(psArrayLength(magicTree));
+
+    // convert the array of metadata into a pxTree structure
+    for (long i = 0; i < psArrayLength(magicTree); i++) {
+        bool status;
+        psString node = psMetadataLookupStr(&status, magicTree->data[i], "node");
+        if (!status) {
+            psAbort("failed to lookup value for node column");
+        }
+
+        psString dep = psMetadataLookupStr(&status, magicTree->data[i], "dep");
+        if (!status) {
+            psAbort("failed to lookup value for dep column");
+        }
+
+        pxTreeBuilder(forest, node, dep, magicTree->data[i]);
+
+    }
+    psFree(magicTree);
+
+    // find the root of the tree
+    pxNode *root = psMemIncrRefCounter(psHashLookup(forest, "root"));
+    psFree(forest);
+    //    pxTreePrint(stdout, root);
+
+    // crawl through the tree and looking for nodes with children that are all
+    // "done"
+    pxTreeCrawl(root, findReadyNodes, output);
+    psFree(root);
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "magicMe", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+    psFree(whereClause);
+
+    return true;
+}
+
+
+static bool addresultMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(magic_id, config->args, "-magic_id", true, false);
+    PXOPT_LOOKUP_STR(node, config->args, "-node", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", false, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    if (!magicNodeResultInsert(config->dbh,
+                               magic_id,
+                               node,
+                               uri,
+                               code
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool revertnodeMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-magic_id", "magic_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-node", "node", "==");
+    PXOPT_COPY_S16(config->args, where, "-code", "fault", "==");
+
+    psString query = psStringCopy("DELETE FROM magicNodeResult WHERE fault != 0");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to revert");
+        return false;
+    }
+    return true;
+}
+
+
+static bool tomaskMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // look for "inputs" that need to processed
+    psString query = pxDataGet("magictool_tomask.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("magictool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "tomask", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addmaskMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(magic_id, config->args, "-magic_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", false, false);
+    PXOPT_LOOKUP_S32(streaks, config->args, "-streaks", false, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!magicMaskInsert(config->dbh,
+                         magic_id,
+                         uri,
+                         streaks,
+                         code
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        return false;
+    }
+
+    // Set the magicRun state
+    psString query = pxDataGet("magictool_addmask.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        return false;
+    }
+
+    // manually add constraint
+    psStringAppend(&query, " AND magic_id = %" PRId64, magic_id);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        return false;
+    }
+    psFree(query);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool revertmaskMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-magic_id", "magic_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code", "fault", "==");
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // Set to "run"
+    {
+        psString query = psStringCopy("UPDATE magicRun JOIN magicMask USING(magic_id) "
+                                      "SET magicRun.state = 'run' WHERE magicMask.fault != 0");
+
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, "magicMask");
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to revert");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        psFree(query);
+    }
+
+    // Delete failed attempt at mask
+    {
+        psString query = psStringCopy("DELETE FROM magicMask WHERE fault != 0");
+
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, "magicMask");
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to revert");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        psFree(query);
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(where);
+
+    return true;
+}
+
+
+static bool maskMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-magic_id", "magicRun.magic_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("magictool_mask.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("magictool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "magicMask", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool setmagicRunState(pxConfig *config, psS64 magic_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!(
+            (strncmp(state, "run", 4) == 0)
+            || (strncmp(state, "stop", 5) == 0)
+            || (strncmp(state, "reg", 4) == 0)
+        )
+    ) {
+        psError(PS_ERR_UNKNOWN, false,
+                "invalid magicRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE magicRun SET state = '%s' WHERE magic_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, state, magic_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for magic_id %" PRId64, magic_id);
+        return false;
+    }
+
+    return true;
+}
+
+static bool parseAndInsertNodeDeps(pxConfig *config, psS64 magic_id, const char *filename)
+{
+    unsigned int nFail = 0;
+    psMetadata *deps = psMetadataConfigRead(NULL, &nFail, filename, false);
+    if (!deps) {
+        psError(PS_ERR_UNKNOWN, false, "failed to parse file: %s", filename);
+        return false;
+    }
+    if (nFail) {
+        psError(PS_ERR_UNKNOWN, false, "there were %d errors parsing file: %s", nFail, filename);
+        psFree(deps);
+        return false;
+    }
+
+    psMetadataItem *item = NULL;
+    psMetadataIterator *iter = psMetadataIteratorAlloc(deps, 0, NULL);
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (item->type != PS_DATA_STRING) {
+            psError(PS_ERR_UNKNOWN, false, "file: %s is in the wrong format", filename);
+            psFree(iter);
+            psFree(deps);
+            return false;
+        }
+
+        char *name = item->name;
+        char *dependsOn = item->data.str;
+
+        if (!magicTreeInsert(
+                config->dbh,
+                magic_id,
+                name,
+                dependsOn
+            )) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(iter);
+            psFree(deps);
+            return false;
+        }
+    }
+    psFree(iter);
+    psFree(deps);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/magictool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/magictool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/magictool.h	(revision 22322)
@@ -0,0 +1,47 @@
+/*
+ * magictool.h
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MAGICTOOL_H
+#define MAGICTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    MAGICTOOL_MODE_NONE           = 0x0,
+    MAGICTOOL_MODE_DEFINEBYQUERY,
+    MAGICTOOL_MODE_DEFINERUN,
+    MAGICTOOL_MODE_UPDATERUN,
+    MAGICTOOL_MODE_ADDINPUTSKYFILE,
+    MAGICTOOL_MODE_INPUTSKYFILE,
+    MAGICTOOL_MODE_TOTREE,
+    MAGICTOOL_MODE_INPUTTREE,
+    MAGICTOOL_MODE_REVERTTREE,
+    MAGICTOOL_MODE_TOPROCESS,
+    MAGICTOOL_MODE_INPUTS,
+    MAGICTOOL_MODE_ADDRESULT,
+    MAGICTOOL_MODE_REVERTNODE,
+    MAGICTOOL_MODE_TOMASK,
+    MAGICTOOL_MODE_ADDMASK,
+    MAGICTOOL_MODE_REVERTMASK,
+    MAGICTOOL_MODE_MASK,
+} MAGICtoolMode;
+
+pxConfig *magictoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // MAGICTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/magictoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/magictoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/magictoolConfig.c	(revision 22322)
@@ -0,0 +1,197 @@
+/*
+ * magictoolConfig.c
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "magictool.h"
+
+pxConfig *magictoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    psTime *now = psTimeGetNow(PS_TIME_TAI);
+
+    // -definebyquery
+    psMetadata *definebyqueryArgs = psMetadataAlloc();
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-workdir",     0, "define workdir (required)", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label",       0, "define label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-dvodb",       0, "define dvodb", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-registered", 0, "time detrend run was registered", now);
+    psMetadataAddS64(definebyqueryArgs, PS_LIST_TAIL, "-exp_id", 0, "search exp_id", 0);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-good_frac", 0, "limit good_frac", NAN);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -definerun
+    psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-workdir", 0, "define workdir (required)", NULL);
+    psMetadataAddS64(definerunArgs, PS_LIST_TAIL, "-exp_id", 0, "define exp_id (required)", 0);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-label", 0, "define label", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-dvodb", 0, "define dvodb", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-registered", 0, "time detrend run was registered", now);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-magic_id", 0, "define magictool ID (required)", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0, "set state (required)", NULL);
+
+    // -addinputskyfile
+    psMetadata *addinputskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addinputskyfileArgs, PS_LIST_TAIL, "-magic_id", 0, "define magictool ID (required)", 0);
+    psMetadataAddS64(addinputskyfileArgs, PS_LIST_TAIL, "-diff_id", 0, "define difftool ID (required)", 0);
+    psMetadataAddStr(addinputskyfileArgs, PS_LIST_TAIL, "-node", 0, "define symbolic node name (required)", NULL);
+
+    // -inputskyfile
+    psMetadata *inputskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(inputskyfileArgs, PS_LIST_TAIL, "-magic_id", 0, "search by magictool ID", 0);
+    psMetadataAddS64(inputskyfileArgs, PS_LIST_TAIL, "-diff_id", 0, "search by difftool ID", 0);
+    psMetadataAddStr(inputskyfileArgs, PS_LIST_TAIL, "-node", 0, "search by symbolic node name", NULL);
+    psMetadataAddU64(inputskyfileArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(inputskyfileArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -totree
+    psMetadata *totreeArgs = psMetadataAlloc();
+    psMetadataAddS64(totreeArgs, PS_LIST_TAIL, "-magic_id", 0, "search by magic ID", 0);
+    psMetadataAddU64(totreeArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(totreeArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -inputtree
+    psMetadata *inputtreeArgs = psMetadataAlloc();
+    psMetadataAddS64(inputtreeArgs, PS_LIST_TAIL, "-magic_id", 0, "define magictool ID (required)", 0);
+    psMetadataAddStr(inputtreeArgs, PS_LIST_TAIL, "-dep_file", 0, "order of operations dep. file", NULL);
+    psMetadataAddS16(inputtreeArgs, PS_LIST_TAIL, "-code", 0, "set fault code", 0);
+
+    // -reverttree
+    psMetadata *reverttreeArgs = psMetadataAlloc();
+    psMetadataAddS64(reverttreeArgs, PS_LIST_TAIL, "-magic_id", 0, "search by magictool ID", 0);
+    psMetadataAddS16(reverttreeArgs, PS_LIST_TAIL, "-code", 0, "search by fault code", 0);
+
+    // -inputs
+    psMetadata *inputsArgs = psMetadataAlloc();
+    psMetadataAddS64(inputsArgs, PS_LIST_TAIL, "-magic_id", 0, "search by magictool ID", 0);
+    psMetadataAddStr(inputsArgs, PS_LIST_TAIL, "-node", 0, "search by symbolic node name", NULL);
+    psMetadataAddU64(inputsArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(inputsArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -tooprocess
+    psMetadata *toprocessArgs = psMetadataAlloc();
+    psMetadataAddS64(toprocessArgs, PS_LIST_TAIL, "-magic_id", 0, "search by magic ID", 0);
+    psMetadataAddU64(toprocessArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(toprocessArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -addresult
+    psMetadata *addresultArgs = psMetadataAlloc();
+    psMetadataAddS64(addresultArgs, PS_LIST_TAIL, "-magic_id", 0, "define magictool ID (required)", 0);
+    psMetadataAddStr(addresultArgs, PS_LIST_TAIL, "-node", 0, "define symbolic node name (required)", NULL);
+    psMetadataAddStr(addresultArgs, PS_LIST_TAIL, "-uri", 0, "define URI", NULL);
+    psMetadataAddS16(addresultArgs, PS_LIST_TAIL, "-code", 0, "set fault code", 0);
+
+    // -revertnode
+    psMetadata *revertnodeArgs = psMetadataAlloc();
+    psMetadataAddS64(revertnodeArgs, PS_LIST_TAIL, "-magic_id", 0, "search by magictool ID", 0);
+    psMetadataAddStr(revertnodeArgs, PS_LIST_TAIL, "-node", 0, "search by node name", NULL);
+    psMetadataAddS16(revertnodeArgs, PS_LIST_TAIL, "-code", 0, "search by fault code", 0);
+
+    // -tomask
+    psMetadata *tomaskArgs = psMetadataAlloc();
+    psMetadataAddU64(tomaskArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(tomaskArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -addmask
+    psMetadata *addmaskArgs = psMetadataAlloc();
+    psMetadataAddS64(addmaskArgs, PS_LIST_TAIL, "-magic_id", 0, "define magictool ID (required)", 0);
+    psMetadataAddStr(addmaskArgs, PS_LIST_TAIL, "-uri", 0, "define URI", NULL);
+    psMetadataAddS32(addmaskArgs, PS_LIST_TAIL, "-streaks", 0, "define number of streaks", 0);
+    psMetadataAddS16(addmaskArgs, PS_LIST_TAIL, "-code", 0, "set fault code", 0);
+
+    // -revertmask
+    psMetadata *revertmaskArgs = psMetadataAlloc();
+    psMetadataAddS64(revertmaskArgs, PS_LIST_TAIL, "-magic_id", 0, "search by magictool ID", 0);
+    psMetadataAddS16(revertmaskArgs, PS_LIST_TAIL, "-code", 0, "search by fault code", 0);
+
+    // -mask
+    psMetadata *maskArgs = psMetadataAlloc();
+    psMetadataAddS64(maskArgs, PS_LIST_TAIL, "-magic_id", 0, "define magictool ID", 0);
+    psMetadataAddU64(maskArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(maskArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    psFree(now);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes   = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definebyquery",   "", MAGICTOOL_MODE_DEFINEBYQUERY,   definebyqueryArgs);
+    PXOPT_ADD_MODE("-definerun",       "", MAGICTOOL_MODE_DEFINERUN,       definerunArgs);
+    PXOPT_ADD_MODE("-updaterun",       "", MAGICTOOL_MODE_UPDATERUN,       updaterunArgs);
+    PXOPT_ADD_MODE("-addinputskyfile", "", MAGICTOOL_MODE_ADDINPUTSKYFILE, addinputskyfileArgs);
+    PXOPT_ADD_MODE("-inputskyfile",    "", MAGICTOOL_MODE_INPUTSKYFILE,    inputskyfileArgs);
+    PXOPT_ADD_MODE("-totree",          "", MAGICTOOL_MODE_TOTREE,          totreeArgs);
+    PXOPT_ADD_MODE("-inputtree",       "", MAGICTOOL_MODE_INPUTTREE,       inputtreeArgs);
+    PXOPT_ADD_MODE("-reverttree",      "", MAGICTOOL_MODE_REVERTTREE,      reverttreeArgs);
+    PXOPT_ADD_MODE("-toprocess",       "", MAGICTOOL_MODE_TOPROCESS,       toprocessArgs);
+    PXOPT_ADD_MODE("-inputs",          "", MAGICTOOL_MODE_INPUTS,          inputsArgs);
+    PXOPT_ADD_MODE("-addresult",       "", MAGICTOOL_MODE_ADDRESULT,       addresultArgs);
+    PXOPT_ADD_MODE("-revertnode",      "", MAGICTOOL_MODE_REVERTNODE,      revertnodeArgs);
+    PXOPT_ADD_MODE("-tomask",          "", MAGICTOOL_MODE_TOMASK,          tomaskArgs);
+    PXOPT_ADD_MODE("-addmask",         "", MAGICTOOL_MODE_ADDMASK,         addmaskArgs);
+    PXOPT_ADD_MODE("-revertmask",      "", MAGICTOOL_MODE_REVERTMASK,      revertmaskArgs);
+    PXOPT_ADD_MODE("-mask",            "", MAGICTOOL_MODE_MASK,            maskArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pstamptool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pstamptool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pstamptool.c	(revision 22322)
@@ -0,0 +1,832 @@
+/*
+ * pstamptool.c
+ *
+ * Copyright (C) 2008
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "pxtools.h"
+#include "pxdata.h"
+#include "pstamptool.h"
+
+static bool adddatastoreMode(pxConfig *config);
+static bool datastoreMode(pxConfig *config);
+static bool moddatastoreMode(pxConfig *config);
+static bool addReqMode(pxConfig *config);
+static bool completedReqMode(pxConfig *config);
+static bool listReqMode(pxConfig *config);
+static bool pendingReqMode(pxConfig *config);
+static bool updateReqMode(pxConfig *config);
+static bool revertReqMode(pxConfig *config);
+static bool addJobMode(pxConfig *config);
+static bool listJobMode(pxConfig *config);
+static bool pendingJobMode(pxConfig *config);
+static bool updateJobMode(pxConfig *config);
+static bool addProjectMode(pxConfig *config);
+static bool projectMode(pxConfig *config);
+static bool modProjectMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+                goto FAIL; \
+            } \
+    break;
+
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = pstamptoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(PSTAMPTOOL_MODE_ADDDATASTORE, adddatastoreMode);
+        MODECASE(PSTAMPTOOL_MODE_DATASTORE, datastoreMode);
+        MODECASE(PSTAMPTOOL_MODE_MODDATASTORE, moddatastoreMode);
+        MODECASE(PSTAMPTOOL_MODE_ADDREQ, addReqMode);
+        MODECASE(PSTAMPTOOL_MODE_COMPLETEDREQ, completedReqMode);
+        MODECASE(PSTAMPTOOL_MODE_LISTREQ, listReqMode);
+        MODECASE(PSTAMPTOOL_MODE_PENDINGREQ, pendingReqMode);
+        MODECASE(PSTAMPTOOL_MODE_UPDATEREQ, updateReqMode);
+        MODECASE(PSTAMPTOOL_MODE_REVERTREQ, revertReqMode);
+        MODECASE(PSTAMPTOOL_MODE_ADDJOB, addJobMode);
+        MODECASE(PSTAMPTOOL_MODE_LISTJOB, listJobMode);
+        MODECASE(PSTAMPTOOL_MODE_PENDINGJOB, pendingJobMode);
+        MODECASE(PSTAMPTOOL_MODE_UPDATEJOB, updateJobMode);
+        MODECASE(PSTAMPTOOL_MODE_ADDPROJECT, addProjectMode);
+        MODECASE(PSTAMPTOOL_MODE_MODPROJECT, modProjectMode);
+        MODECASE(PSTAMPTOOL_MODE_PROJECT, projectMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+static bool adddatastoreMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(uri,         config->args, "-uri",           true, false);
+    PXOPT_LOOKUP_STR(outProduct,  config->args, "-out_product",   true, false);
+    PXOPT_LOOKUP_STR(lastFileset, config->args, "-last_fileset", false, false);
+    PXOPT_LOOKUP_STR(state,       config->args, "-state",         false, false);
+
+    if (!pstampDataStoreInsert(config->dbh,
+            0,
+            state,
+            lastFileset,
+            outProduct,
+            uri
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool datastoreMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-ds_id", "ds_id", "==");
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("pstamptool_datastore.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        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)) {
+        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampDataStore", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+static bool moddatastoreMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(ds_id,       config->args, "-ds_id",         true, false);
+    PXOPT_LOOKUP_STR(lastFileset, config->args, "-last_fileset",  false, false);
+    PXOPT_LOOKUP_STR(state,       config->args, "-state",         false, false);
+
+    if (!state && !lastFileset) {
+        psError(PS_ERR_UNKNOWN, true, "at least one of -last_fileset or -state is required");
+        return false;
+    }
+
+    char *query = psStringCopy ("UPDATE pstampDataStore SET");
+    bool needComma = false;
+    
+    if (lastFileset) {
+        psStringAppend(&query, " lastFileset = '%s'", lastFileset);
+        needComma = true;
+    }
+
+    if (state) {
+        psStringAppend(&query, "%s state = '%s'", needComma ? "," : " ", state);
+        needComma = true; // be ready in case we add another field
+    }
+                
+    psStringAppend(&query, " WHERE ds_id = %" PRId64, ds_id);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    if (affected != 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %" 
+                                        PRIu64 " rows were modified", affected);
+        return false;
+    }
+
+    return true;
+}
+
+static bool addReqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(uri,         config->args, "-uri",           true, false);
+    // PXOPT_LOOKUP_STR(outFileset,  config->args, "-out_fileset",   true, false);
+    PXOPT_LOOKUP_S64(ds_id,       config->args, "-ds_id",         false, false);
+
+    char *query ="INSERT INTO pstampRequest"
+                     " (state, uri, ds_id, fault)"
+                     " VALUES( 'new', '%s', %" PRId64 ", 0 )";
+    if (!p_psDBRunQuery(config->dbh, query, uri, ds_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    if (affected != 1) {
+        psError(PS_ERR_UNKNOWN, false, 
+            "should have affected one row but %" PRIu64 " rows were modified",
+            affected);
+        return false;
+    }
+
+    psS64 req_id = psDBLastInsertID(config->dbh);
+
+    printf("%" PRId64 "\n", req_id);
+
+    return true;
+}
+
+static bool pendingReqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("pstamptool_pendingreq.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "pstampRequest");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampRequest", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool listReqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(req_id,  config->args, "-req_id", true, false);
+    PXOPT_LOOKUP_U64(limit,   config->args, "-limit",  false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = NULL;
+    psStringAppend(&query, 
+		   "SELECT * FROM pstampRequest WHERE state = 'run' AND "
+		   "(SELECT count(*) FROM pstampJob WHERE pstampJob.req_id = pstampRequest.req_id "
+		   " AND pstampJob.state != 'stop') = 0" );
+    
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampRequest", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool completedReqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = 
+        "SELECT * FROM pstampRequest WHERE state = 'run' AND "
+            "(SELECT count(*) FROM pstampJob WHERE pstampJob.req_id = pstampRequest.req_id "
+            " AND pstampJob.state != 'stop') = 0" ;
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!psArrayLength(output)) {
+        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampRequest", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool updateReqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(req_id,     config->args, "-req_id",     true, false);
+    PXOPT_LOOKUP_STR(state,      config->args, "-state",      false, false);
+    PXOPT_LOOKUP_STR(outProduct, config->args, "-outProduct", false, false);
+    PXOPT_LOOKUP_STR(fault, 	 config->args, "-fault",      false, false);
+    PXOPT_LOOKUP_STR(uri,   	 config->args, "-uri",        false, false);
+    PXOPT_LOOKUP_STR(name,  	 config->args, "-name",       false, false);
+    PXOPT_LOOKUP_STR(reqType,    config->args, "-reqType",    false, false);
+
+    psString query = NULL;
+    psStringAppend(&query, "UPDATE pstampRequest SET");
+
+    char c = ' ';
+    if (state) {
+        psStringAppend(&query, "%c state = '%s'", c, state);
+        c = ',';
+    }
+    if (outProduct) {
+        psStringAppend(&query, "%c outProduct = '%s'", c, outProduct);
+        c = ',';
+    }
+    if (fault) {
+        psStringAppend(&query, "%c fault = %s", c, fault);
+        c = ',';
+    }
+    if (uri) {
+        psStringAppend(&query, "%c uri = '%s'", c, uri);
+        c = ',';
+    }
+    if (name) {
+        psStringAppend(&query, "%c name = '%s'", c, name);
+        c = ',';
+    }
+    if (reqType) {
+        psStringAppend(&query, "%c reqType = '%s'", c, reqType);
+        c = ',';
+    }
+    if (!state && !outProduct && !fault && !uri && !name && !reqType) {
+        psError(PS_ERR_UNKNOWN, true, "at least one of state, outProduct, fault, uri, name, and reqType must be specified");
+        return false;
+    }
+
+    psStringAppend(&query, " WHERE req_id = %" PRId64, req_id);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    // note zero is not an error
+    if (affected > 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %" 
+                                        PRIu64 " rows were modified", affected);
+        return false;
+    }
+
+    return true;
+}
+
+static bool revertReqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(req_id,     config->args, "-req_id",     true, false);
+
+    // printf("Revert request %" PRId64 "\n", req_id);
+    
+    if (!p_psDBRunQuery(config->dbh, "DELETE FROM pstampJob where req_id = %" PRId64, req_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!p_psDBRunQuery(config->dbh, 
+        "UPDATE pstampRequest set state ='new', name=NULL, reqType=NULL, fault=0 where req_id = %" PRId64, req_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool addJobMode(pxConfig *config)
+{
+    bool    stampJob = false;
+    char   *query = NULL;
+
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(uri,         config->args, "-uri",        true, false);
+    PXOPT_LOOKUP_S64(req_id,      config->args, "-req_id",     true, false);
+    PXOPT_LOOKUP_STR(rownum,      config->args, "-rownum",     true, false);
+    PXOPT_LOOKUP_STR(job_type,    config->args, "-job_type",   false, false);
+    PXOPT_LOOKUP_STR(outputBase,  config->args, "-outputBase", true,  false);
+    PXOPT_LOOKUP_STR(argString,   config->args, "-args",       false, false);
+    PXOPT_LOOKUP_STR(stateString, config->args, "-state",      false, false);
+    PXOPT_LOOKUP_STR(fault,       config->args, "-fault",      false, false);
+    PXOPT_LOOKUP_S64(exp_id,      config->args, "-exp_id",     false, false);
+
+    // default value for job_type is defined in pstamptoolConfig.c
+    if (!strcmp(job_type, "get_image") || !strcmp(job_type, "detect_query")) {
+	stampJob = false;
+    } else if (!strcmp(job_type, "stamp")) {
+	stampJob = true;
+    } else {
+	psError(PS_ERR_UNKNOWN, false, "unknown value for -job_type: %s", job_type);
+	return false;
+    }
+
+    if (stampJob && !argString) {
+	psError(PS_ERR_UNKNOWN, true, "-args is required for stamp job");
+	return false;
+    }
+
+    if (stampJob) {
+	query = pxDataGet("pstamptool_addjob_stampjob.sql");
+    } else {
+	query = pxDataGet("pstamptool_addjob_otherjob.sql");
+    }
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, req_id, rownum, stateString, job_type, uri, exp_id, outputBase, fault, argString)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    if (affected != 1) {
+        psError(PS_ERR_UNKNOWN, false, 
+            "should have affected one row but %" PRIu64 " rows were modified",
+            affected);
+        return false;
+    }
+
+    psS64 job_id = psDBLastInsertID(config->dbh);
+    printf("%" PRId64 "\n", job_id);
+
+    return true;
+}
+
+static bool listJobMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(req_id, config->args, "-req_id", false, false);
+    PXOPT_LOOKUP_S64(job_id, config->args, "-job_id", false, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = NULL;
+
+    if (!req_id && !job_id) {
+        fprintf(stderr, "either req_id or job_id must be specified\n");
+        exit (1);
+    }
+
+    if (req_id) {
+        psStringAppend(&query,
+                "SELECT"
+                " *"
+                " FROM pstampJob"
+                " WHERE req_id = %" PRId64, req_id
+            );
+    } else {
+        psStringAppend(&query,
+                "SELECT"
+                " *"
+                " FROM pstampJob"
+                " WHERE job_id = %" PRId64, job_id
+            );
+    } 
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampJob", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool pendingJobMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where, "-job_id", "job_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("pstamptool_pendingjob.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "pstampJob");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampJob", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool updateJobMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(job_id,    config->args, "-job_id", true, false);
+    PXOPT_LOOKUP_STR(state,     config->args, "-state",  true, false);
+    PXOPT_LOOKUP_STR(fault,     config->args, "-fault",  false, false);
+
+    psString faultStr = NULL;
+    if (!fault) {
+        faultStr = psStringCopy("");
+    } else {
+        psStringAppend(&faultStr, ", fault = '%s'", fault);
+    }
+
+    char *query ="UPDATE pstampJob"
+	" SET state = '%s' %s"
+	" WHERE job_id = %" PRId64;
+    
+    if (!p_psDBRunQuery(config->dbh, query, state, faultStr, job_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(faultStr);
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    if (affected != 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %" 
+                                        PRIu64 " rows were modified", affected);
+        return false;
+    }
+
+    return true;
+}
+static bool addProjectMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(name,  config->args, "-name", true, false);
+    PXOPT_LOOKUP_STR(state,  config->args, "-state", false, false);
+    PXOPT_LOOKUP_STR(imagedb, config->args, "-imagedb", true, false);
+    PXOPT_LOOKUP_STR(dvodb,  config->args, "-dvodb", false, false);
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", true, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", true, false);
+    PXOPT_LOOKUP_BOOL(need_magic, config->args, "-need_magic", false);
+
+    if (!pstampProjectInsert(config->dbh,
+            0,
+            name,
+            state,
+            imagedb,
+            dvodb,
+            camera,
+            telescope,
+            need_magic
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool projectMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-name", "name", "==");
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("pstamptool_project.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        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)) {
+        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampDataStore", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+static bool modProjectMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(proj_id,    config->args, "-proj_id", true, false);
+    PXOPT_LOOKUP_STR(state,  config->args, "-state", true, false);
+
+    char *query = psStringCopy ("UPDATE pstampProject SET");
+    
+    psStringAppend(&query, " state = '%s'", state);
+                
+    psStringAppend(&query, " WHERE proj_id = %" PRId64, proj_id);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    if (affected != 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %" 
+                                        PRIu64 " rows were modified", affected);
+        return false;
+    }
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pstamptool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pstamptool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pstamptool.h	(revision 22322)
@@ -0,0 +1,48 @@
+/*
+ * pstamptool.h
+ *
+ * Copyright (C) 2008
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PSTAMPTOOL_H
+#define PSTAMPTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    PSTAMPTOOL_MODE_NONE      = 0x0,
+    PSTAMPTOOL_MODE_ADDDATASTORE,
+    PSTAMPTOOL_MODE_DATASTORE,
+    PSTAMPTOOL_MODE_MODDATASTORE,
+    PSTAMPTOOL_MODE_ADDREQ,
+    PSTAMPTOOL_MODE_LISTREQ,
+    PSTAMPTOOL_MODE_COMPLETEDREQ,
+    PSTAMPTOOL_MODE_PENDINGREQ,
+    PSTAMPTOOL_MODE_UPDATEREQ,
+    PSTAMPTOOL_MODE_REVERTREQ,
+    PSTAMPTOOL_MODE_ADDJOB,
+    PSTAMPTOOL_MODE_LISTJOB,
+    PSTAMPTOOL_MODE_PENDINGJOB,
+    PSTAMPTOOL_MODE_JOBRESULT,
+    PSTAMPTOOL_MODE_UPDATEJOB,
+    PSTAMPTOOL_MODE_ADDPROJECT,
+    PSTAMPTOOL_MODE_MODPROJECT,
+    PSTAMPTOOL_MODE_PROJECT,
+} pstamptoolMode;
+
+pxConfig *pstamptoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // PSTAMPTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pstamptoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pstamptoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pstamptoolConfig.c	(revision 22322)
@@ -0,0 +1,203 @@
+/*
+ * pstamptoolConfig.c
+ *
+ * Copyright (C) 2008
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "pstamptool.h"
+
+pxConfig *pstamptoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (! config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration!\n");
+        psFree(config);
+        return NULL;
+    }
+
+    // -adddatastore
+    psMetadata *adddatastoreArgs = psMetadataAlloc();
+    psMetadataAddStr(adddatastoreArgs, PS_LIST_TAIL, "-uri",          0, "define storage uri (required)", NULL);
+    psMetadataAddStr(adddatastoreArgs, PS_LIST_TAIL, "-out_product",  0, "define output product name (required)", NULL);
+    psMetadataAddStr(adddatastoreArgs, PS_LIST_TAIL, "-last_fileset", 0, "define last fileset seen", NULL);
+    psMetadataAddStr(adddatastoreArgs, PS_LIST_TAIL, "-state",        0, "define datastore state", "enabled");
+    
+    // -datastore
+    psMetadata *datastoreArgs = psMetadataAlloc();
+    psMetadataAddS64(datastoreArgs, PS_LIST_TAIL, "-ds_id", 0,            "define ds_id", 0); 
+    psMetadataAddBool(datastoreArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -moddatastore
+    psMetadata *moddatastoreArgs = psMetadataAlloc();
+    psMetadataAddS64(moddatastoreArgs, PS_LIST_TAIL, "-ds_id", 0,            "define ds_id", 0); 
+    psMetadataAddStr(moddatastoreArgs, PS_LIST_TAIL, "-last_fileset", 0,     "set last_fileset seen", NULL);
+    psMetadataAddStr(moddatastoreArgs, PS_LIST_TAIL, "-state", 0,            "set state", NULL);
+
+    // -addreq
+    psMetadata *addreqArgs = psMetadataAlloc();
+    psMetadataAddStr(addreqArgs, PS_LIST_TAIL, "-uri", 0,            "define request file uri (required)", NULL); 
+    psMetadataAddS64(addreqArgs, PS_LIST_TAIL, "-ds_id", 0,            "define request ds_id", 0); 
+    // psMetadataAddStr(addreqArgs, PS_LIST_TAIL, "-out_fileset", 0,            "define request output_fileset", NULL); 
+
+    // -pendingreq
+    psMetadata *pendingreqArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingreqArgs, PS_LIST_TAIL, "-req_id", 0,            "define req_id", 0); 
+    psMetadataAddU64(pendingreqArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(pendingreqArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -listreq
+    psMetadata *listreqArgs = psMetadataAlloc();
+    psMetadataAddS64(listreqArgs, PS_LIST_TAIL, "-req_id", 0,            "define req_id", 0); 
+    psMetadataAddU64(listreqArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(listreqArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -completedreq
+    psMetadata *completedreqArgs = psMetadataAlloc();
+    psMetadataAddU64(completedreqArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(completedreqArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -updatereq
+    psMetadata *updatereqArgs = psMetadataAlloc();
+    psMetadataAddS64(updatereqArgs, PS_LIST_TAIL, "-req_id", 0,            "req_id for which to change state", 0); 
+    psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-state", 0,            "new state", NULL); 
+    psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-outProduct", 0,            "define request outProduct", NULL); 
+    psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-fault", 0,            "define request fault code", NULL); 
+    psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-uri", 0,            "define the uri", NULL); 
+    psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-name", 0,            "define the name", NULL); 
+    psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-reqType", 0,            "define the reqType", NULL); 
+
+    // -revertreq
+    psMetadata *revertreqArgs = psMetadataAlloc();
+    psMetadataAddS64(revertreqArgs, PS_LIST_TAIL, "-req_id", 0,            "req_id for which to revert", 0); 
+
+    // -addjob
+    psMetadata *addjobArgs = psMetadataAlloc();
+    psMetadataAddStr(addjobArgs, PS_LIST_TAIL, "-uri", 0,            "define job file uri", NULL); 
+    psMetadataAddS64(addjobArgs, PS_LIST_TAIL, "-req_id", 0,            "define job req_id", 0); 
+    psMetadataAddStr(addjobArgs, PS_LIST_TAIL, "-rownum", 0,            "define job rownum", NULL); 
+    psMetadataAddStr(addjobArgs, PS_LIST_TAIL, "-job_type", 0,            "define job job_type", "stamp"); 
+    psMetadataAddStr(addjobArgs, PS_LIST_TAIL, "-outputBase", 0,            "define job outputBase", NULL); 
+    psMetadataAddStr(addjobArgs, PS_LIST_TAIL, "-args", 0,            "define job args", NULL); 
+    psMetadataAddStr(addjobArgs, PS_LIST_TAIL, "-state", 0,            "new state", "run"); 
+    psMetadataAddS64(addjobArgs, PS_LIST_TAIL, "-exp_id", 0,           "exposure id", 0); 
+    psMetadataAddStr(addjobArgs, PS_LIST_TAIL, "-fault", 0,            "new result", NULL); 
+
+    // -listjob
+    psMetadata *listjobArgs = psMetadataAlloc();
+    psMetadataAddS64(listjobArgs, PS_LIST_TAIL, "-req_id", 0,            "define request", 0); 
+    psMetadataAddS64(listjobArgs, PS_LIST_TAIL, "-job_id", 0,            "define job", 0); 
+    psMetadataAddU64(listjobArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(listjobArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -pendingjob
+    psMetadata *pendingjobArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingjobArgs, PS_LIST_TAIL, "-job_id", 0,            "define job", 0); 
+    psMetadataAddS64(pendingjobArgs, PS_LIST_TAIL, "-req_id", 0,            "define request", 0); 
+    psMetadataAddU64(pendingjobArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(pendingjobArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -updatejob
+    psMetadata *updatejobArgs = psMetadataAlloc();
+    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-job_id", 0,            "req_id for which to change state", 0); 
+    psMetadataAddStr(updatejobArgs, PS_LIST_TAIL, "-state", 0,            "new state", NULL); 
+    // psMetadataAddStr(updatejobArgs, PS_LIST_TAIL, "-result", 0,            "new result", NULL); 
+    psMetadataAddStr(updatejobArgs, PS_LIST_TAIL, "-fault", 0,            "new result", NULL); 
+
+    // -addproject
+    psMetadata *addprojectArgs = psMetadataAlloc();
+    psMetadataAddStr(addprojectArgs, PS_LIST_TAIL, "-name",        0, "define project name (required)", NULL);
+    psMetadataAddStr(addprojectArgs, PS_LIST_TAIL, "-state",        0, "define state for project (enabled, disabled)", "enabled");
+    psMetadataAddStr(addprojectArgs, PS_LIST_TAIL, "-imagedb",      0, "define name of database for project (required)", NULL);
+    psMetadataAddStr(addprojectArgs, PS_LIST_TAIL, "-dvodb",        0, "define name of dvo database for project", NULL);
+    psMetadataAddStr(addprojectArgs, PS_LIST_TAIL, "-inst",        0, "define name of camera for project (required)", NULL);
+    psMetadataAddStr(addprojectArgs, PS_LIST_TAIL, "-telescope",     0, "define name of telescope for project (required)", NULL);
+    psMetadataAddBool(addprojectArgs, PS_LIST_TAIL, "-need_magic",   0, "define need_magic for project", false);
+
+    // -modproject
+    psMetadata *modprojectArgs = psMetadataAlloc();
+    psMetadataAddS64(modprojectArgs, PS_LIST_TAIL, "-proj_id",      0, "define project ID to modify (required)", 0); 
+    psMetadataAddStr(modprojectArgs, PS_LIST_TAIL, "-imagedb",      0, "define name of database for project", NULL);
+    psMetadataAddStr(modprojectArgs, PS_LIST_TAIL, "-state",        0, "define state for project (enabled, disabled)", NULL);
+    psMetadataAddStr(modprojectArgs, PS_LIST_TAIL, "-dvodb",        0, "define name of dvo database for project", NULL);
+    psMetadataAddStr(modprojectArgs, PS_LIST_TAIL, "-camera",        0, "define name of camera for project", NULL);
+    psMetadataAddStr(modprojectArgs, PS_LIST_TAIL, "-telescope",     0, "define name of telescope for project", NULL);
+    psMetadataAddBool(modprojectArgs, PS_LIST_TAIL, "-need_magic",   0, "define need_magic for project", false);
+
+    // -project
+    psMetadata *projectArgs = psMetadataAlloc();
+    psMetadataAddStr(projectArgs, PS_LIST_TAIL, "-name", 0, "define project name to list (required)", NULL); 
+    psMetadataAddBool(projectArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-addreq",          "", PSTAMPTOOL_MODE_ADDREQ,       addreqArgs);
+    PXOPT_ADD_MODE("-pendingreq",      "", PSTAMPTOOL_MODE_PENDINGREQ,   pendingreqArgs);
+    PXOPT_ADD_MODE("-updatereq",    "", PSTAMPTOOL_MODE_UPDATEREQ, updatereqArgs);
+    PXOPT_ADD_MODE("-listreq",         "", PSTAMPTOOL_MODE_LISTREQ,      listreqArgs);
+    PXOPT_ADD_MODE("-completedreq",    "", PSTAMPTOOL_MODE_COMPLETEDREQ, completedreqArgs);
+    PXOPT_ADD_MODE("-revertreq",       "", PSTAMPTOOL_MODE_REVERTREQ,    revertreqArgs);
+
+    PXOPT_ADD_MODE("-addjob",          "", PSTAMPTOOL_MODE_ADDJOB,       addjobArgs);
+    PXOPT_ADD_MODE("-listjob",         "", PSTAMPTOOL_MODE_LISTJOB,      listjobArgs);
+    PXOPT_ADD_MODE("-pendingjob",      "", PSTAMPTOOL_MODE_PENDINGJOB,   pendingjobArgs);
+    PXOPT_ADD_MODE("-updatejob",    "", PSTAMPTOOL_MODE_UPDATEJOB, updatejobArgs);
+
+    PXOPT_ADD_MODE("-adddatastore",    "", PSTAMPTOOL_MODE_ADDDATASTORE, adddatastoreArgs);
+    PXOPT_ADD_MODE("-datastore",       "", PSTAMPTOOL_MODE_DATASTORE,    datastoreArgs);
+    PXOPT_ADD_MODE("-moddatastore",    "", PSTAMPTOOL_MODE_MODDATASTORE, moddatastoreArgs);
+
+    PXOPT_ADD_MODE("-addproject",      "", PSTAMPTOOL_MODE_ADDPROJECT, addprojectArgs);
+    PXOPT_ADD_MODE("-modproject",      "", PSTAMPTOOL_MODE_MODPROJECT, modprojectArgs);
+    PXOPT_ADD_MODE("-project",         "", PSTAMPTOOL_MODE_PROJECT,    projectArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxadmin.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxadmin.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxadmin.c	(revision 22322)
@@ -0,0 +1,221 @@
+/*
+ * pxadmin.c
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pxtools.h"
+#include "pxadmin.h"
+
+bool createMode(pxConfig *config);
+bool deleteMode(pxConfig *config);
+static bool runMultipleStatments(pxConfig *config, const char *query);
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = pxAdminConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        case PXADMIN_MODE_RECREATE:
+            if (!deleteMode(config)) {
+                goto FAIL;
+            }
+            // fall through
+        case PXADMIN_MODE_CREATE:
+            if (!createMode(config)) {
+                goto FAIL;
+            }
+            break;
+        case PXADMIN_MODE_DELETE:
+            if (!deleteMode(config)) {
+                goto FAIL;
+            }
+            break;
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+bool createMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psString query = pxDataGet("pxadmin_create_tables.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // BEGIN
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!runMultipleStatments(config, query)) {
+        if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    // COMMIT
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+bool deleteMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    {
+        char line[128], answer[128];
+
+        psMetadataItem *name = pmConfigUserSite(config->modules, "DBNAME", PS_DATA_STRING);
+        if (!name) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine database name.");
+            return false;
+        }
+        psString dbName = name->data.str;
+
+        fprintf(stdout, "*** delete the tables from database %s? ***\n", dbName);
+        fprintf(stdout, "*** to delete the tables, answer YES, and give password ***\n");
+        fprintf(stdout, "*** WARNING: this action is permanent ***\n\n");
+
+        fprintf(stdout, "delete the tables (YES/[n]): ");
+        if (!fgets(line, 128, stdin)) {
+            psError(PS_ERR_IO, true, "Unable to read response.");
+            return false;
+        }
+        sscanf(line, "%s", answer);
+        if (strcmp (answer, "YES"))  {
+            psError(PS_ERR_UNKNOWN, true, "tables NOT deleleted");
+            return false;
+        }
+
+        fprintf(stdout, "enter dbh connection password: ");
+        if (!fgets(line, 128, stdin)) {
+            psError(PS_ERR_IO, true, "Unable to read response.");
+            return false;
+        }
+        sscanf(line, "%s", answer);
+
+        psMetadataItem *pass = pmConfigUserSite(config->modules, "DBPASSWORD", PS_DATA_STRING);
+        if (!pass) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine database password.");
+            return false;
+        }
+        psString dbPassword = pass->data.str;
+        if (strcmp (answer, dbPassword)) {
+            psError(PS_ERR_UNKNOWN, true, "invalid passwd - tables NOT deleleted");
+            return false;
+        }
+    }
+
+    psString query = pxDataGet("pxadmin_drop_tables.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // BEGIN
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!runMultipleStatments(config, query)) {
+        if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    // COMMIT
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool runMultipleStatments(pxConfig *config, const char *query)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(query, false);
+
+    // loop over all statements in string
+    psList *statements = psStringSplit(query, ";", false);
+
+    psString q = NULL;
+    psListIterator *iter = psListIteratorAlloc(statements, PS_LIST_HEAD, false);
+    while ((q = psListGetAndIncrement(iter))) {
+        if (!p_psDBRunQuery(config->dbh, q)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(iter);
+            psFree(statements);
+            return false;
+        }
+    }
+    psFree(iter);
+    psFree(statements);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxadmin.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxadmin.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxadmin.h	(revision 22322)
@@ -0,0 +1,34 @@
+/*
+ * pxadmin.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXADMIN_H
+#define PXADMIN_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    PXADMIN_MODE_NONE      = 0x0,
+    PXADMIN_MODE_CREATE,
+    PXADMIN_MODE_DELETE,
+    PXADMIN_MODE_RECREATE
+} pxAdminMode;
+
+pxConfig *pxAdminConfig (pxConfig *config, int argc, char **argv);
+
+#endif // PXADMIN_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxadminConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxadminConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxadminConfig.c	(revision 22322)
@@ -0,0 +1,153 @@
+/*
+ * pxadminConfig.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "pxadmin.h"
+
+static void pxAdminShowDB (const pxConfig *config, const char *program)
+{
+    fprintf (stderr, "\nPan-STARRS DataBase Admin Tool\n\n");
+    fprintf (stderr, "Usage: %s [mode]\n", program);
+    fprintf (stderr, " [mode] : -create | -delete\n\n");
+
+    psMetadataItem *server = pmConfigUserSite(config->modules, "DBSERVER",   PS_DATA_STRING);
+    psMetadataItem *user   = pmConfigUserSite(config->modules, "DBUSER",     PS_DATA_STRING);
+    psMetadataItem *name   = pmConfigUserSite(config->modules, "DBNAME",     PS_DATA_STRING);
+    psMetadataItem *port   = pmConfigUserSite(config->modules, "DBPORT",     PS_TYPE_S32);
+
+    if (!server || !user || !name) {
+        psErrorClear();
+        psWarning("Unable to determine database connection details.");
+        return;
+    }
+    if (!port) {
+        psErrorClear();
+    }
+
+    fprintf (stderr, "connecting to %s as %s (port %d)\n", server->data.str, user->data.str,
+             port ? port->data.S32 : 0);
+    fprintf (stderr, "using database %s\n\n", name->data.str);
+
+    return;
+}
+
+pxConfig *pxAdminConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!psArgumentGet(argc, argv, "-dbname")) {
+        psError(PS_ERR_UNEXPECTED_NULL, true,
+                "Command-line argument '-dbname XXX' is required, for database safety");
+        return NULL;
+    }
+
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    // Parse other command-line arguments
+    psMetadata *arguments = psMetadataAlloc(); // The arguments, with default values
+
+    int N;
+    config->mode = PXADMIN_MODE_NONE;
+    if ((N = psArgumentGet(argc, argv, "-create"))) {
+        psArgumentRemove(N, &argc, argv);
+        if (config->mode) {
+            psAbort("only one mode selection is allowed");
+        }
+        config->mode = PXADMIN_MODE_CREATE;
+    }
+    if ((N = psArgumentGet(argc, argv, "-delete"))) {
+        psArgumentRemove(N, &argc, argv);
+        if (config->mode) {
+            psAbort("only one mode selection is allowed");
+        }
+        config->mode = PXADMIN_MODE_DELETE;
+    }
+    if ((N = psArgumentGet(argc, argv, "-recreate"))) {
+        psArgumentRemove(N, &argc, argv);
+        if (config->mode) {
+            psAbort("only one mode selection is allowed");
+        }
+        config->mode = PXADMIN_MODE_RECREATE;
+    }
+
+    // paul's argument parsing convention requires: -key value
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-create", 0,
+            "create all IPP tables", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-delete", 0,
+            "delete all IPP tables", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-recreate", 0,
+            "delete and recreate all tables", "");
+
+    if (config->mode == PXADMIN_MODE_NONE) {
+    pxAdminShowDB (config, argv[0]);
+        fprintf (stderr, "admin mode not specified\n");
+
+        psArgumentHelp(arguments);
+        psFree(arguments);
+        psFree(config);
+        return NULL;
+    }
+
+    if ((N = psArgumentGet (argc, argv, "-help"))) {
+    pxAdminShowDB (config, argv[0]);
+        psArgumentHelp(arguments);
+        psFree(arguments);
+        psFree(config);
+        return NULL;
+    }
+
+    if (! psArgumentParse(arguments, &argc, argv) || argc != 1) {
+    pxAdminShowDB (config, argv[0]);
+        psArgumentHelp(arguments);
+        psFree(arguments);
+        psFree(config);
+        return NULL;
+    }
+
+   psFree(arguments);
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    // save argv/argc
+    config->argv = argv;
+    config->argc = argc;
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxcam.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxcam.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxcam.c	(revision 22322)
@@ -0,0 +1,268 @@
+/*
+ * pxcam.c
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ippdb.h>
+#include <string.h>
+
+#include "pxtools.h"
+#include "pxcam.h"
+
+bool pxcamSetSearchArgs (psMetadata *md) {
+
+    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, "-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);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-airmass_max",        0, "define max airmass", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-ra_min",             0, "define min", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-ra_max",             0, "define max", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-decl_min",           0, "define min", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-decl_max",           0, "define max", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-exp_time_min",       0, "define min", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-exp_time_max",       0, "define max", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-sat_pixel_frac_min", 0, "define max fraction of saturated pixels", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-sat_pixel_frac_max", 0, "define min fraction of saturated pixels", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_min",             0, "define max", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_max",             0, "define max", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_stdev_min",       0, "define max", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_stdev_max",       0, "define max", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_mean_stdev_min",  0, "define max", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_mean_stdev_max",  0, "define max", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-alt_min",            0, "define min", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-alt_max",            0, "define max", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-az_min",             0, "define min", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-az_max",             0, "define max", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-ccd_temp_min",       0, "define min ccd tempature", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-ccd_temp_max",       0, "define max ccd tempature", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-posang_min",         0, "define min rotator position angle", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-posang_max",         0, "define 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, "define min solar angle", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-solang_max",         0, "define max solar angle", NAN);
+
+    return true;
+}
+
+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", "<");
+
+    return true;
+}
+
+bool pxcamRunSetState(pxConfig *config, psS64 cam_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "invalid camRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE camRun SET state = '%s' WHERE cam_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, state, cam_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for cam_id %" PRId64, cam_id);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool pxcamRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "invalid chipRun state: %s", state);
+        return false;
+    }
+
+    psString query = psStringCopy("UPDATE camRun JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id) SET state = '%s'");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, state)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+
+bool pxcamRunSetLabel(pxConfig *config, psS64 cam_id, const char *label)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    // note label == NULL should be explicitly allowed
+
+    char *query = "UPDATE camRun SET camRun.label = '%s' WHERE cam_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, label, cam_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for cam_id %" PRId64, cam_id);
+        return false;
+    }
+
+    return true;
+}
+
+bool pxcamRunSetLabelByQuery(pxConfig *config, psMetadata *where, const char *label)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    // note label == NULL should be explicitly allowed
+
+    psString query = psStringCopy("UPDATE camRun JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id) SET camRun.label = '%s'");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, label)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+
+bool pxcamQueueByChipID(pxConfig *config,
+                    psS64 chip_id,
+                    char *workdir,
+                    char *label,
+                    char *recipe,
+                    char *expgroup,
+                    char *dvodb,
+                    char *tess_id,
+                    char *end_stage)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // load the SQL to enqueue our exp_ids from disk once
+    static psString query = NULL;
+    if (!query) {
+        query = pxDataGet("camtool_queue_chip_id.sql");
+        psMemSetPersistent(query, true);
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            return false;
+        }
+    }
+
+    // queue the exp
+    // XXX chip_id is being cast here work around psS64 have a different type
+    // different on 32/64
+    if (!p_psDBRunQuery(config->dbh, query,
+                "new", // state
+                workdir  ? workdir  : "NULL",
+                "dirty", //workdir_state
+                label    ? label    : "NULL",
+                recipe   ? recipe   : "NULL",
+                expgroup ? expgroup : "NULL",
+                dvodb    ? dvodb    : "NULL",
+                tess_id  ? tess_id  : "NULL",
+                end_stage ? end_stage : "NULL",
+                (long long)chip_id
+    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // just to be safe, we should have changed at least one row
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "no rows affected - should have changed at least one row");
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxcam.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxcam.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxcam.h	(revision 22322)
@@ -0,0 +1,45 @@
+/*
+ * pxcam.h
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXCAM_H
+#define PXCAM_H 1
+
+#include <pslib.h>
+
+#include "pxtools.h"
+
+bool pxcamRunSetState(pxConfig *config, psS64 cam_id, const char *state);
+bool pxcamRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state);
+bool pxcamRunSetLabel(pxConfig *config, psS64 cam_id, const char *label);
+bool pxcamRunSetLabelByQuery(pxConfig *config, psMetadata *where, const char *label);
+
+bool pxcamSetSearchArgs (psMetadata *md);
+bool pxcamGetSearchArgs (pxConfig *config, psMetadata *where);
+
+bool pxcamQueueByChipID(pxConfig *config,
+                        psS64 chip_id,
+                        char *workdir,
+                        char *label,
+                        char *recipe,
+                        char *expgroup,
+                        char *dvodb,
+                        char *tess_id,
+                        char *end_stage);
+
+#endif // PXCAM_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxchip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxchip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxchip.c	(revision 22322)
@@ -0,0 +1,255 @@
+/*
+ * pxchip.c
+ *
+ * Copyright (C) 2007-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ippdb.h>
+#include <string.h>
+
+#include "pxtools.h"
+#include "pxchip.h"
+
+bool pxchipSetSearchArgs (psMetadata *md) {
+
+    // 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);
+    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, "-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);
+    psMetadataAddF32(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);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-ccd_temp_min",       0, "search by min ccd tempature", NAN);
+    psMetadataAddF64(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);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-comment",            0, "search by comment field (LIKE comparison)", NULL);
+    return true;
+}
+
+// XXX explicit refs to rawExp and chipRun?
+// XXX careful with entries that could match rawExp rawImfile, chipRun, or chipProcessedImfile (eg, bg)
+bool pxchipGetSearchArgs (pxConfig *config, psMetadata *where) {
+
+    // definebyquery : rawExp only
+    // updaterun : rawExp, chipRun
+    // pendingimfile : rawExp, chipRun
+    // processedimfile : rawExp, chipRun, chipProcessedImfile
+    // revertprocessedimfile : rawExp, chipProcessedImfile
+    // updateprocessedimfile : chipProcessedImfile
+    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, "-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", "<");
+    PXOPT_COPY_STR(config->args, where, "-comment", "rawExp.comment", "LIKE");
+    return true;
+}
+
+bool pxchipRunSetState(pxConfig *config, psS64 chip_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid chipRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE chipRun SET state = '%s' WHERE chip_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, state, chip_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for chip_id %" PRId64, chip_id);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool pxchipRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid chipRun state: %s", state);
+        return false;
+    }
+
+
+    psString query = psStringCopy("UPDATE chipRun JOIN rawExp USING(exp_id) SET state = '%s'");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, state)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+
+bool pxchipRunSetLabel(pxConfig *config, psS64 chip_id, const char *label)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    // note label == NULL should be explicitly allowed
+
+    char *query = "UPDATE chipRun SET label = '%s' WHERE chip_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, label, chip_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for chip_id %" PRId64, chip_id);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool pxchipRunSetLabelByQuery(pxConfig *config, psMetadata *where, const char *label)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    // note label == NULL should be explicitly allowed
+
+    psString query = psStringCopy("UPDATE chipRun JOIN rawExp USING(exp_id) SET label = '%s'");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, label)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+
+psS64 pxchipQueueByExpTag(pxConfig *config,
+                         psS64 exp_id,
+                         const char *workdir,
+                         const char *label,
+                         const char *reduction,
+                         const char *expgroup,
+                         const char *dvodb,
+                         const char *tess_id,
+                         const char *end_stage)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // create a chipRun
+    if (!chipRunInsert(config->dbh,
+            0x0, // chip_id
+            exp_id,
+            "new",      // state
+            workdir,
+            "dirty",    // workdir_state
+            label,
+            reduction,
+            expgroup,
+            dvodb,
+            tess_id,
+            end_stage)
+    ) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return 0;
+    }
+
+    return psDBLastInsertID(config->dbh);
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxchip.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxchip.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxchip.h	(revision 22322)
@@ -0,0 +1,46 @@
+/*
+ * pxchip.h
+ *
+ * Copyright (C) 2007-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXCHIP_H
+#define PXCHIP_H 1
+
+#include <pslib.h>
+
+#include "pxtools.h"
+
+bool pxchipRunSetState(pxConfig *config, psS64 chip_id, const char *state);
+bool pxchipRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state);
+bool pxchipRunSetLabel(pxConfig *config, psS64 chip_id, const char *label);
+bool pxchipRunSetLabelByQuery(pxConfig *config, psMetadata *where, const char *label);
+
+psS64 pxchipQueueByExpTag(pxConfig *config,
+                         psS64 exp_id,
+                         const char *workdir,
+                         const char *label,
+                         const char *reduction,
+                         const char *expgroup,
+                         const char *dvodb,
+                         const char *tess_id,
+                         const char *end_stage);
+
+
+bool pxchipSetSearchArgs (psMetadata *md);
+bool pxchipGetSearchArgs (pxConfig *config, psMetadata *where);
+
+#endif // PXCHIP_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxconfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxconfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxconfig.c	(revision 22322)
@@ -0,0 +1,227 @@
+/*
+ * pxconfig.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "pxtools.h"
+
+static void pxConfigFree(pxConfig *ptr);
+
+pxConfig *pxConfigAlloc(void)
+{
+    pxConfig *config;
+
+    config = psAlloc(sizeof(pxConfig));
+    psMemSetDeallocator(config, (psFreeFunc)pxConfigFree);
+
+    config->modeName        = NULL;
+    config->mode            = 0;
+    config->dbh             = NULL;
+    config->modules         = NULL;
+    // XXX config->where           = NULL;
+    config->args            = NULL;
+
+    return config;
+}
+
+static void pxConfigFree(pxConfig *config)
+{
+    psFree(config->modeName);
+    psFree(config->dbh);
+    psFree(config->modules);
+    // XXX psFree(config->where);
+    psFree(config->args);
+}
+
+void pxUsage(FILE *stream, int argc, char **argv, const char *modeName, psMetadata *argSet) 
+{
+    fprintf(stream, "Usage: %s %s [<options>]\n\n", argv[0], modeName);
+    fprintf(stream, "%s:\n", modeName);
+
+    psArgumentHelpSimple(stream, argSet);
+}
+
+bool pxGetOptions(FILE *stream, int argc, char **argv, pxConfig *config, psMetadata *modes, psMetadata *argSets)
+{
+    // figure out what mode we're running in
+    psMetadataIterator *iter = psMetadataIteratorAlloc(modes, PS_LIST_HEAD, NULL);
+    psMetadataItem *item = NULL;
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        int N = 0;
+        if ((N = psArgumentGet(argc, argv, item->name))) {
+            psArgumentRemove(N, &argc, argv);
+            // check for duplicate mode specification
+            if (config->mode) {
+                psError(PS_ERR_UNKNOWN, true, "only one mode selection is allowed");
+                fprintf(stream, "only one mode selection is allowed\n");
+                pxUsage(stream, argc, argv, "<mode>", modes);
+                psFree(iter);
+                return NULL;
+            }
+
+            config->mode = item->data.U32;
+            config->modeName = psStringCopy(item->name);
+
+            bool status = false;
+            psMetadata *argset = psMetadataLookupMetadata(&status, argSets, item->name);
+            // make sure we can find the argSet for the sepcified mode
+            if (!status) {
+                psError(PS_ERR_UNKNOWN, true, "can not find arguments for mode");
+                fprintf(stderr, "can not find arugments for mode");
+                psFree(iter);
+                return NULL;
+            }
+
+            config->args = psMemIncrRefCounter(argset);
+        }
+
+    }
+
+    psFree(iter);
+
+    // make sure we found a mode
+    if (config->mode == PXTOOL_MODE_NONE) {
+        psError(PS_ERR_UNKNOWN, true, "mode argument is required");
+        fprintf(stderr, "mode argument is required\n");
+        pxUsage(stream, argc, argv, "<mode>", modes);
+        return NULL;
+    }
+
+    // actually parse the command line
+    if (!psArgumentParse(config->args, &argc, argv)) {
+        psError(PS_ERR_UNKNOWN, false, "error parsing arguments");
+        fprintf(stderr, "error parsing arguments\n");
+        pxUsage(stream, argc, argv, config->modeName, config->args);
+        return NULL;
+    }
+
+    // look for left overs on the command line
+    if (argc != 1) {
+        psError(PS_ERR_UNKNOWN, false, "extra arguments: ");
+        fprintf(stderr, "extra arguments: ");
+        for (int i = 1; i < argc; i++) {
+            fprintf (stderr, "%s ", argv[i]);
+        }
+        fprintf(stderr, "\n");
+        pxUsage(stream, argc, argv, config->modeName, config->args);
+        return NULL;
+    }
+
+    // make sure that all required parameters have been specified
+    iter = psMetadataIteratorAlloc(config->args, PS_LIST_HEAD, NULL);
+    item = NULL; // Item from iterator
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (strstr(item->comment, "require") == NULL) {
+            continue;
+        }
+        if (strstr(item->comment, "(found)") != NULL) {
+            continue;
+        }
+
+        switch (item->type) {
+            case PS_DATA_BOOL:
+                psError(PS_ERR_UNKNOWN, false, "boolean type can not be required");
+                fprintf(stderr, "boolean type can not be required\n");
+                psFree(iter);
+                return NULL;
+                break;
+            case PS_DATA_S8:
+                if (item->data.S8 != INT8_MAX) {
+                    break;
+                }
+                goto ARG_REQUIRED;
+            case PS_DATA_S16:
+                if (item->data.S16 != INT16_MAX) {
+                    break;
+                }
+                goto ARG_REQUIRED;
+            case PS_DATA_S32:
+                if (item->data.S32 != INT32_MAX) {
+                    break;
+                }
+                goto ARG_REQUIRED;
+            case PS_DATA_S64:
+                if (item->data.S64 != INT64_MAX) {
+                    break;
+                }
+                goto ARG_REQUIRED;
+            case PS_DATA_U8:
+                if (item->data.U8 != UINT8_MAX) {
+                    break;
+                }
+                goto ARG_REQUIRED;
+            case PS_DATA_U16:
+                if (item->data.U16 != UINT16_MAX) {
+                    break;
+                }
+                goto ARG_REQUIRED;
+            case PS_DATA_U32:
+                if (item->data.U32 != UINT32_MAX) {
+                    break;
+                }
+                goto ARG_REQUIRED;
+            case PS_DATA_U64:
+                if (item->data.U64 != UINT64_MAX) {
+                    break;
+                }
+                goto ARG_REQUIRED;
+            case PS_DATA_F32:
+            case PS_DATA_F64:
+                if (!isnan(item->data.F64)) {
+                    break;
+                }
+                goto ARG_REQUIRED;
+            case PS_DATA_STRING:
+            case PS_DATA_TIME:
+            case PS_DATA_METADATA:
+                if (item->data.V) {
+                    break;
+                }
+                goto ARG_REQUIRED;
+            default:
+                psError(PS_ERR_UNKNOWN, false, "unknown argument type");
+                fprintf(stream, "unknown argument type\n");
+                psFree(iter);
+                return NULL;
+            ARG_REQUIRED:                
+                psError(PS_ERR_UNKNOWN, false, "argument %s is required", item->name);
+                fprintf(stream, "argument %s is required\n", item->name);
+                pxUsage(stream, argc, argv, config->modeName, config->args);
+                psFree(iter);
+                return NULL;
+        }
+
+    }
+
+    psFree(iter);
+
+    // save argv/argc 
+    config->argv = argv;
+    config->argc = argc;
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxconfig.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxconfig.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxconfig.h	(revision 22322)
@@ -0,0 +1,39 @@
+/*
+ * pxconfig.h
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXCONFIG_H
+#define PXCONFIG_H 1
+
+#include "pslib.h"
+#include "psmodules.h"
+
+typedef struct {
+    char *modeName;
+    int mode;
+    pmConfig *modules;
+    psDB *dbh;
+    psMetadata *args;
+    // XXX deprecate psMetadata *where;
+    int argc;
+    char **argv;
+} pxConfig;
+
+pxConfig *pxConfigAlloc(void);
+
+#endif // PXCONFIG_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxdata.c.template
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxdata.c.template	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxdata.c.template	(revision 22322)
@@ -0,0 +1,145 @@
+/*
+ * pxdata.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h> // getenv
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <pslib.h>
+
+#include "pxtools.h"
+#include "pxdata.h"
+
+static psHash *cachedData = NULL;
+
+static int test_f(const char *path);
+
+psString pxDataGet(const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(filename, NULL);
+
+    {
+        psString filepath = NULL;
+
+        // look to see if PXDATA is set
+        char *PXDATA = getenv("PXDATA");
+        if (PXDATA) {
+            // if it is, then prepend filename to PXDATA
+            psStringAppend(&filepath, "%s/%s", PXDATA, filename);
+        }
+
+        // see if we have a valid filename
+        if (test_f(filepath)) {
+            // if we do, slurp the file
+            psString data = psSlurpFilename(filepath);
+            if (!data) {
+                psError(PS_ERR_IO, false, "failed to slurp %s", filepath);
+                psFree(filepath);
+                return NULL;
+            }
+            psTrace("pxdata", PS_LOG_INFO, "slupred %s", filepath);
+            psFree(filepath);
+
+            return data;
+        }
+
+        psFree(filepath);
+    }
+
+    {
+        psString filepath = NULL;
+
+        // look under our share path
+        psStringAppend(&filepath, "%s/%s", "@PKGDATADIR@", filename);
+
+        // see if we have a valid filename
+        if (test_f(filepath)) {
+            // if we do, slurp the file
+            psString data = psSlurpFilename(filepath);
+            if (!data) {
+                psError(PS_ERR_IO, false, "failed to slurp %s", filepath);
+                psFree(filepath);
+                return NULL;
+            }
+            psTrace("pxdata", PS_LOG_INFO, "slupred %s", filepath);
+            psFree(filepath);
+
+            return data;
+        }
+
+        psFree(filepath);
+    }
+
+    // couldn't find a matching filename
+    psError(PS_ERR_IO, true, "can't find a file in search path(s) to match : %s", filename);
+
+    return NULL;
+}
+
+static int test_f(const char *path)
+{
+        // copied from coreutils 6.4 test.c licenced under GPL v2 
+        struct stat stat_buf;
+        return (stat (path, &stat_buf) == 0
+                   && S_ISREG (stat_buf.st_mode));
+}
+
+psString pxDataGetCached(const char *filename)
+{
+    bool persistence = p_psMemAllocatePersistent(true);
+
+    // make sure the cache is initalized
+    if (!cachedData) {
+        cachedData = psHashAlloc(10);
+    }
+
+    // look to see if we've cached this file
+    {
+        psString data = psHashLookup(cachedData, filename);
+        if (data) {
+            p_psMemAllocatePersistent(persistence);
+            return psStringCopy(data);
+        } 
+    }
+
+    // we didn't have a cached copy so we need to try to read the file
+    psString data = pxDataGet(filename);
+    if (!data) {
+        p_psMemAllocatePersistent(persistence);
+        psError(PS_ERR_IO, false, "pxDataGet(%s) failed", filename);
+        return NULL;
+    }
+
+    // store a copy of the file
+    if (!psHashAdd(cachedData, filename, data)) {
+        psFree(data);
+        p_psMemAllocatePersistent(persistence);
+        psError(PS_ERR_UNKNOWN, false, "psHashAdd() failed");
+        return NULL;
+    }
+
+    p_psMemAllocatePersistent(persistence);
+
+    return psStringCopy(data);
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxdata.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxdata.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxdata.h	(revision 22322)
@@ -0,0 +1,28 @@
+/*
+ * pxdata.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXDATA_H
+#define PXDATA_H 1
+
+#include <pslib.h>
+
+psString pxDataGet(const char *filename);
+psString pxDataGetCached(const char *filename);
+
+#endif // PXDATA_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxerrors.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxerrors.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxerrors.c	(revision 22322)
@@ -0,0 +1,42 @@
+/*
+ * pxerrors.c
+ *
+ * Copyright (C) 2006  Eugene Magnier
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "pxtools.h"
+
+psExit pxerrorGetExitStatus () {
+
+    psErrorCode err = psErrorCodeLast ();
+    switch (err) {
+      case PXTOOLS_ERR_SYS:
+    return PS_EXIT_SYS_ERROR;
+      case PXTOOLS_ERR_CONFIG:
+    return PS_EXIT_CONFIG_ERROR;
+      case PXTOOLS_ERR_PROG:
+    return PS_EXIT_PROG_ERROR;
+      case PXTOOLS_ERR_DATA:
+    return PS_EXIT_DATA_ERROR;
+      default:
+    return PS_EXIT_UNKNOWN_ERROR;
+    }    
+    return PS_EXIT_UNKNOWN_ERROR;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxfake.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxfake.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxfake.c	(revision 22322)
@@ -0,0 +1,178 @@
+/*
+ * pxfake.c
+ *
+ * Copyright (C) 2007-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ippdb.h>
+#include <string.h>
+
+#include "pxtools.h"
+#include "pxcam.h"
+
+bool pxfakeRunSetState(pxConfig *config, psS64 fake_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "invalid fakeRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE fakeRun SET state = '%s' WHERE fake_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, state, fake_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for fake_id %" PRId64, fake_id);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool pxfakeRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "invalid fakeRun state: %s", state);
+        return false;
+    }
+
+    psString query = psStringCopy("UPDATE fakeRun JOIN camRun USING(cam_id) JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id) SET state = '%s'");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, state)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+
+bool pxfakeRunSetLabel(pxConfig *config, psS64 fake_id, const char *label)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    // note label == NULL should be explicitly allowed
+
+    char *query = "UPDATE fakeRun SET fakeRun.label = '%s' WHERE fake_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, label, fake_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for fake_id %" PRId64, fake_id);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool pxfakeRunSetLabelByQuery(pxConfig *config, psMetadata *where, const char *label)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    // note label == NULL should be explicitly allowed
+
+    psString query = psStringCopy("UPDATE fakeRun JOIN camRun USING(cam_id) JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id) SET fakeRun.label = '%s'");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, label)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+
+psS64 pxfakeQueueByCamID(pxConfig *config,
+                    psS64 cam_id,
+                    char *workdir,
+                    char *label,
+                    char *reduction,
+                    char *expgroup,
+                    char *dvodb,
+                    char *tess_id,
+                    char *end_stage)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psTrace("pxtool", PS_LOG_INFO, "attemping to queue cam_id: %"PRId64, cam_id);
+
+    // load the SQL to enqueue our exp_ids from disk once
+    static psString query = NULL;
+    if (!query) {
+        query = pxDataGet("faketool_queue_cam_id.sql");
+        psMemSetPersistent(query, true);
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            return false;
+        }
+    }
+
+    // queue the exp
+    // XXX chip_id is being cast here work around psS64 have a different type
+    // different on 32/64
+    if (!p_psDBRunQuery(config->dbh, query,
+                "new", // state
+                workdir  ? workdir  : "NULL",
+                label    ? label    : "NULL",
+                reduction? reduction: "NULL",
+                expgroup ? expgroup : "NULL",
+                dvodb    ? dvodb    : "NULL",
+                tess_id  ? tess_id  : "NULL",
+                end_stage ? end_stage : "NULL",
+                (long long)cam_id
+    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // just to be safe, we should have changed at least one row
+    if (psDBAffectedRows(config->dbh) != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "should have changed just one row");
+        return false;
+    }
+
+    return psDBLastInsertID(config->dbh);
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxfake.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxfake.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxfake.h	(revision 22322)
@@ -0,0 +1,44 @@
+/*
+ * pxfake.h
+ *
+ * Copyright (C) 2007-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXFAKE_H
+#define PXFAKE_H 1
+
+#include <pslib.h>
+
+#include "pxtools.h"
+
+bool pxfakeRunSetState(pxConfig *config, psS64 fake_id, const char *state);
+bool pxfakeRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state);
+bool pxfakeRunSetLabel(pxConfig *config, psS64 fake_id, const char *label);
+bool pxfakeRunSetLabelByQuery(pxConfig *config, psMetadata *where, const char *label);
+
+
+psS64 pxfakeQueueByCamID(pxConfig *config,
+                    psS64 cam_id,
+                    char *workdir,
+                    char *label,
+                    char *reduction,
+                    char *expgroup,
+                    char *dvodb,
+                    char *tess_id,
+                    char *end_stage);
+
+
+#endif // PXFAKE_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxfault.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxfault.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxfault.c	(revision 22322)
@@ -0,0 +1,65 @@
+/*
+ * pxfault.c
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+
+#include "pxtools.h"
+
+bool pxSetFaultCode(psDB *dbh, const char *tableName, psMetadata *where, psS16 code)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(tableName, false);
+    PS_ASSERT_PTR_NON_NULL(where, false);
+
+#if 0
+    // map code string to numeric fault code
+    psU32 code = mapCodeStrToInt(codeStr);
+    if (code == (psU32)-1) {
+        psError(PS_ERR_UNKNOWN, false, "error resolving error code");
+        return false;
+    }
+#endif
+
+    // update the database
+    psMetadata *values = psMetadataAlloc();
+    if (!psMetadataAddS16(values, PS_LIST_HEAD, "fault", 0, NULL, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add metadata item fault");
+        psFree(values);
+        return false;
+    }
+
+    long rowsAffected = psDBUpdateRows(dbh, tableName, where, values); 
+    psFree(values);
+    if (rowsAffected < 0) {
+        // database error
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (rowsAffected < 1) {
+        // we didn't do anything
+        psError(PS_ERR_UNKNOWN, false, "zero rows were affected - either the search criteria didn't match any rows or the field already had the value being set.");
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxinject.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxinject.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxinject.c	(revision 22322)
@@ -0,0 +1,163 @@
+/*
+ * pxinject.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ippdb.h>
+#include <string.h>
+
+#include "pxtools.h"
+#include "pxinject.h"
+
+static bool newExpMode(pxConfig *config);
+static bool newImfileMode(pxConfig *config);
+static bool updatenewExpMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = pxinjectConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(PXINJECT_MODE_NEWEXP, newExpMode);
+        MODECASE(PXINJECT_MODE_NEWIMFILE, newImfileMode);
+        MODECASE(PXINJECT_MODE_UPDATENEWEXP, updatenewExpMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint (stderr, "failure\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+static bool newExpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(tmp_exp_name, config->args, "-tmp_exp_name", true, false);
+    PXOPT_LOOKUP_STR(tmp_camera, config->args, "-tmp_inst", true, false);
+    PXOPT_LOOKUP_STR(tmp_telescope, config->args, "-tmp_telescope", true, false);
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-reduction", 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);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    if (!newExpInsert(config->dbh,
+                0,    // exp_id
+                tmp_exp_name,
+                tmp_camera,
+                tmp_telescope,
+                "reg",  // state
+                workdir,
+                "dirty",
+                reduction,
+                dvodb,
+                tess_id,
+                end_stage,
+                label,
+                NULL    // epoch
+            )
+        ) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psS64 exp_id = psDBLastInsertID(config->dbh);
+
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAddS64(md, PS_LIST_TAIL, "exp_id", 0, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadata(stdout, md, !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(md);
+        return false;
+    }
+
+    psFree(md);
+
+    return true;
+}
+
+static bool newImfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(tmp_class_id, config->args, "-tmp_class_id", true, false);
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", true, false);
+
+    // insert with error flag state set to 0 (no errors)
+    if (!newImfileInsert(config->dbh, exp_id, tmp_class_id, uri, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool updatenewExpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
+
+    if (state) {
+        // set detRun.state to state
+        return pxnewExpSetState(config, exp_id, state);
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxinject.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxinject.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxinject.h	(revision 22322)
@@ -0,0 +1,34 @@
+/*
+ * pxinject.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXINJECT_H
+#define PXINJECT_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    PXINJECT_MODE_NONE           = 0x0,
+    PXINJECT_MODE_NEWEXP,
+    PXINJECT_MODE_NEWIMFILE,
+    PXINJECT_MODE_UPDATENEWEXP
+} pxinjectMode;
+
+pxConfig *pxinjectConfig(pxConfig *config, int argc, char **argv);
+
+#endif // PXINJECT_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxinjectConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxinjectConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxinjectConfig.c	(revision 22322)
@@ -0,0 +1,98 @@
+/*
+ * pxinjectConfig.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "pxinject.h"
+
+pxConfig *pxinjectConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    // -newExp
+    psMetadata *newExpArgs = psMetadataAlloc();
+    psMetadataAddStr(newExpArgs, PS_LIST_TAIL, "-tmp_exp_name",  0,            "define the exp_name (required)", NULL);
+    psMetadataAddStr(newExpArgs, PS_LIST_TAIL, "-tmp_inst",  0,            "define the camera name (required)", NULL);
+    psMetadataAddStr(newExpArgs, PS_LIST_TAIL, "-tmp_telescope",  0,            "define the telescope name (required)", NULL);
+    psMetadataAddStr(newExpArgs, PS_LIST_TAIL, "-workdir",  0,            "define workdir (required)", 0);
+    psMetadataAddStr(newExpArgs, PS_LIST_TAIL, "-reduction",  0,            "define reduction class", NULL);
+    psMetadataAddStr(newExpArgs, PS_LIST_TAIL, "-dvodb",  0,            "define the dvodb for the next processing step", NULL);
+    psMetadataAddStr(newExpArgs, PS_LIST_TAIL, "-tess_id",  0,            "define the tess_id for the next processing step", NULL);
+    psMetadataAddStr(newExpArgs, PS_LIST_TAIL, "-end_stage",  0,            "define the end goal processing step", NULL);
+    psMetadataAddStr(newExpArgs, PS_LIST_TAIL, "-label",  0,            "define a label (carried to chip stage)", NULL);
+    psMetadataAddBool(newExpArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -newImfile
+    psMetadata *newImfileArgs = psMetadataAlloc();
+    psMetadataAddS64(newImfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "define the exp_id (required)", 0);
+    psMetadataAddStr(newImfileArgs, PS_LIST_TAIL, "-tmp_class_id",  0,            "define the class ID (required)", NULL);
+    psMetadataAddStr(newImfileArgs, PS_LIST_TAIL, "-uri",  0,            "define the URI (required)", NULL);
+
+    // -updatenewExp
+    psMetadata *updatenewExpArgs = psMetadataAlloc();
+    psMetadataAddS64(updatenewExpArgs, PS_LIST_TAIL, "-exp_id",  0,            "define the exp_id (required)", 0);
+    psMetadataAddStr(updatenewExpArgs, PS_LIST_TAIL, "-state", 0,            "set state (required)", NULL);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-newExp",      "", PXINJECT_MODE_NEWEXP,       newExpArgs);
+    PXOPT_ADD_MODE("-newImfile",   "", PXINJECT_MODE_NEWIMFILE,    newImfileArgs);
+    PXOPT_ADD_MODE("-updatenewExp",   "", PXINJECT_MODE_UPDATENEWEXP,    updatenewExpArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxregister.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxregister.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxregister.c	(revision 22322)
@@ -0,0 +1,59 @@
+/*
+ * register.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ippdb.h>
+#include <string.h>
+
+#include "pxtools.h"
+#include "pxregister.h"
+
+bool pxnewExpSetState(pxConfig *config, psS64 exp_id, const char *state)
+{
+    if (!exp_id) {
+        psError(PS_ERR_UNKNOWN, true, "0 is not a valid exp_id");
+        return false;
+    }
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!(
+            (strncmp(state, "run", 4) == 0)
+            || (strncmp(state, "stop", 5) == 0)
+            || (strncmp(state, "reg", 4) == 0)
+        )
+    ) {
+        psError(PS_ERR_UNKNOWN, false,
+                "invalid newExp state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE newExp SET state = '%s' WHERE exp_id = %"PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, state, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for exp_id %"PRId64, exp_id);
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxregister.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxregister.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxregister.h	(revision 22322)
@@ -0,0 +1,29 @@
+/*
+ * pxregister.h
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXREGISTER_H
+#define PXREGISTER_H 1
+
+#include <pslib.h>
+
+#include "pxtools.h"
+
+bool pxnewExpSetState(pxConfig *config, psS64 exp_id, const char *state);
+
+#endif // PXREGISTER_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxtag.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxtag.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxtag.c	(revision 22322)
@@ -0,0 +1,102 @@
+/*
+ * pxtag.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <inttypes.h>
+
+#include "pxtools.h"
+#include "pxtag.h"
+
+psString pxGenExpTag(pxConfig *config, const char *exp_name)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    // start a transaction so we don't increment the expTag counter unless we
+    // can successfully retreive it's value
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return NULL;
+    }
+
+    if (!p_psDBRunQuery(config->dbh,
+        "UPDATE expTagCounter SET counter = LAST_INSERT_ID(counter + 1)")) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return NULL;
+    }
+
+    // psDBLastInsertID() can't be used here as it called mysql_insert_id()
+    // which doesn't work with this trick.  See:
+    // http://dev.mysql.com/doc/refman/4.1/en/information-functions.html
+    if (!p_psDBRunQuery(config->dbh, "SELECT LAST_INSERT_ID() as counter")) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return NULL;
+    }
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return NULL;
+    }
+    // sanity check that we only got one row
+    if (psArrayLength(output) != 1) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "should have gotten 1 row but %lu rows were returned", psArrayLength(output));
+        psFree(output);
+        return NULL;
+    }
+
+    // point of no return 
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return NULL;
+    }
+
+    psMetadata *row = output->data[0];
+    bool status = false;
+    psU64 counter = psMetadataLookupU64(&status, row, "counter");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for counter");
+        psFree(output);
+        return NULL;
+    }
+    psString exp_id = NULL;
+    psStringAppend(&exp_id, "%s.%" PRIu64, exp_name, counter);
+    psFree(output);
+
+    return exp_id;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxtag.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxtag.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxtag.h	(revision 22322)
@@ -0,0 +1,29 @@
+/*
+ * pxtag.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXTAG_H
+#define PXTAG_H 1
+
+#include <pslib.h>
+
+#include "pxconfig.h"
+
+psString pxGenExpTag(pxConfig *config, const char *exp_name);
+
+#endif // PXTAG_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxtools.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxtools.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxtools.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*
+ * pxtool.c
+ *
+ * Copyright (C) 2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "pxtools.h"
+
+bool pxIsValidState(const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(state, false);
+    
+    // XXX replace strncmp with strcmp
+
+    if (!((strncmp(state, "new", 4) == 0)
+    || (strncmp(state, "reg", 3) == 0)
+    || (strncmp(state, "full", 5) == 0)
+    || (strncmp(state, "goto_cleaned", 8) == 0)
+    || (strncmp(state, "cleaned", 8) == 0)
+    || (strncmp(state, "update", 7) == 0)
+    || (strncmp(state, "purged", 7) == 0)
+    || (strncmp(state, "goto_purged", 12) == 0))) {
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxtools.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxtools.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxtools.h	(revision 22322)
@@ -0,0 +1,480 @@
+/*
+ * pxtools.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXTOOLS_H
+#define PXTOOLS_H 1
+
+#include <stdio.h>
+#include <string.h>   // for strcmp and strncmp
+#include <strings.h>  // for strcasecmp
+#include <unistd.h>   // for unlink
+
+// #include <stdlib.h>
+// #include <stdint.h>
+// #include <inttypes.h>
+
+#include <pslib.h>
+#include <psmodules.h>
+#include <ippdb.h>
+
+#include "pxconfig.h"
+#include "pxtoolsErrorCodes.h"
+
+#include "pxcam.h"
+#include "pxchip.h"
+#include "pxdata.h"
+#include "pxfake.h"
+#include "pxwarp.h"
+#include "pxregister.h"
+#include "pxtag.h"
+#include "pxtree.h"
+
+# define MAX_ROWS 10e9
+# define PXTOOL_MODE_NONE 0x0
+
+bool pxIsValidState(const char *state);
+
+bool pxSetFaultCode(psDB *dbh, const char *tableName, psMetadata *where, psS16 code);
+
+psExit pxerrorGetExitStatus ();
+
+void pxUsage(FILE *stream, int argc, char **argv, const char *modeName, psMetadata *argSet);
+bool pxGetOptions(FILE *stream, int argc, char **argv, pxConfig *config, psMetadata *modes, psMetadata *argSets);
+
+#define PXOPT_ADD_MODE(option, comment, modeval, argset) \
+{ \
+    if (!psMetadataAddMetadata(argSets, PS_LIST_TAIL, option, 0, comment, argset)) {;\
+        psError(PS_ERR_UNKNOWN, false, "failed to add argset for %s", option); \
+    } \
+    psFree(argset); \
+\
+    if (!psMetadataAddU32(modes, PS_LIST_TAIL, option, 0, comment, modeval)) {;\
+        psError(PS_ERR_UNKNOWN, false, "failed to add argset for %s", option); \
+    } \
+}
+
+#define PXOPT_LOOKUP_STR(var, md, key, required, ret) \
+psString var; \
+{ \
+    bool status; \
+ \
+    var = psMetadataLookupStr(&status, md, key); \
+    if (!status) { \
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", key); \
+        return ret; \
+    } \
+ \
+    if (required && (!var)) { \
+        psError(PS_ERR_UNKNOWN, true, "%s is required", key); \
+        return ret; \
+    } \
+}
+
+#define PXOPT_LOOKUP_F(var, md, key, type, required, ret) \
+ps##type var; \
+{ \
+    bool status; \
+ \
+    var = psMetadataLookup##type(&status, md, key); \
+    if (!status) { \
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", key); \
+        return ret; \
+    } \
+ \
+    if (required && isnan(var)) { \
+        psError(PS_ERR_UNKNOWN, true, "%s is required", key); \
+        return ret; \
+    } \
+}
+
+#define PXOPT_LOOKUP_F32(var, md, key, required, ret) \
+    PXOPT_LOOKUP_F(var, md, key, F64, required, ret)
+
+#define PXOPT_LOOKUP_F64(var, md, key, required, ret) \
+    PXOPT_LOOKUP_F(var, md, key, F64, required, ret)
+
+#define PXOPT_LOOKUP_PRIMITIVE(var, md, key, type, suffix, max, required, ret) \
+type var; \
+{ \
+    psMetadataItem *item = psMetadataLookup(md, key); \
+    if (!item) { \
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", key); \
+        return ret; \
+    } \
+    psAssert(item->comment, "metadata item comment should be defined"); \
+    if (required && (!psStrcasestr(item->comment, "(found)"))) { \
+        psError(PS_ERR_UNKNOWN, true, "%s is required", key); \
+        return ret; \
+    } \
+ \
+    var = item->data.suffix; \
+ \
+}
+
+#define PXOPT_LOOKUP_S16(var, md, key, required, ret) \
+    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psS16, S16, INT16_MAX, required, ret)
+
+#define PXOPT_LOOKUP_S32(var, md, key, required, ret) \
+    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psS32, S32, INT32_MAX, required, ret)
+
+#define PXOPT_LOOKUP_S64(var, md, key, required, ret) \
+    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psS64, S64, INT64_MAX, required, ret)
+
+#define PXOPT_LOOKUP_U16(var, md, key, required, ret) \
+    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psU16, U16, UINT16_MAX, required, ret)
+
+#define PXOPT_LOOKUP_U32(var, md, key, required, ret) \
+    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psU32, U32, UINT32_MAX, required, ret)
+
+#define PXOPT_LOOKUP_U64(var, md, key, required, ret) \
+    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psU64, U64, UINT64_MAX, required, ret)
+
+#define PXOPT_LOOKUP_TIME(var, md, key, required, ret) \
+psTime *var; \
+{ \
+    bool status; \
+ \
+    var = psMetadataLookupTime(&status, md, key); \
+    if (!status) { \
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", key); \
+        return ret; \
+    } \
+ \
+    if (required && (!var)) { \
+        psError(PS_ERR_UNKNOWN, true, "%s is required", key); \
+        return ret; \
+    } \
+}
+
+#define PXOPT_LOOKUP_BOOL(var, md, key, ret) \
+bool var; \
+{ \
+    bool status; \
+ \
+    var = psMetadataLookupBool(&status, md, key); \
+    if (!status) { \
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", key); \
+        return ret; \
+    } \
+}
+
+// XXX the PXOPT_COPY_* macros free 'to' on error
+
+#define PXOPT_COPY_PRIMITIVE(from, to, type, suffix, oldname, newname, newcomment) \
+{ \
+    psMetadataItem *item = psMetadataLookup(from, oldname); \
+    if (!item) { \
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", oldname); \
+        return false; \
+    } \
+    psAssert(item->comment, "metadata item comment should be defined"); \
+\
+    if (psStrcasestr(item->comment, "(found)")) { \
+        if (!psMetadataAdd##suffix(to, PS_LIST_TAIL, newname, PS_META_DUPLICATE_OK, newcomment, item->data.suffix)) { \
+            psError(PS_ERR_UNKNOWN, false, "failed to add item " newname); \
+            psFree(to); \
+            return false; \
+        } \
+    } \
+}
+
+#define PXOPT_COPY_V(from, to, type, suffix, oldname, newname, comment) \
+{ \
+    bool status = false; \
+    type var = psMetadataLookup##suffix(&status, from, oldname); \
+    if (!status) { \
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for " oldname); \
+        return false; \
+    } \
+    if (var) { \
+        if (!psMetadataAdd##suffix(to, PS_LIST_TAIL, newname, PS_META_DUPLICATE_OK, comment, var)) { \
+            psError(PS_ERR_UNKNOWN, false, "failed to add item " newname); \
+            psFree(to); \
+            return false; \
+        } \
+    }\
+}
+
+#define PXOPT_COPY_F(from, to, type, oldname, newname, comment) \
+{ \
+    bool status = false; \
+    ps##type var = psMetadataLookup##type(&status, from, oldname); \
+    if (!status) { \
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for " oldname); \
+        return false; \
+    } \
+    if (!isnan(var)) { \
+        if (!psMetadataAdd##type(to, PS_LIST_TAIL, newname, PS_META_DUPLICATE_OK, comment, var)) { \
+            psError(PS_ERR_UNKNOWN, false, "failed to add item " newname); \
+            psFree(to); \
+            return false; \
+        } \
+    } \
+}
+
+#define PXOPT_COPY_F32(from, to, oldname, newname, comment) \
+  PXOPT_COPY_F(from, to, F32, oldname, newname, comment)
+
+#define PXOPT_COPY_F64(from, to, oldname, newname, comment) \
+  PXOPT_COPY_F(from, to, F64, oldname, newname, comment)
+
+#define PXOPT_COPY_TIME(from, to, oldname, newname, comment) \
+  PXOPT_COPY_V(from, to, psTime *, Time, oldname, newname, comment)
+
+#define PXOPT_COPY_STR(from, to, oldname, newname, comment) \
+  PXOPT_COPY_V(from, to, psString, Str, oldname, newname, comment)
+
+#define PXOPT_COPY_S16(from, to, oldname, newname, comment) \
+  PXOPT_COPY_PRIMITIVE(from, to, psS16, S16, oldname, newname, comment)
+
+#define PXOPT_COPY_S32(from, to, oldname, newname, comment) \
+  PXOPT_COPY_PRIMITIVE(from, to, psS32, S32, oldname, newname, comment)
+
+#define PXOPT_COPY_S64(from, to, oldname, newname, comment) \
+  PXOPT_COPY_PRIMITIVE(from, to, psS64, S64, oldname, newname, comment)
+
+#define PXOPT_COPY_U16(from, to, oldname, newname, comment) \
+  PXOPT_COPY_PRIMITIVE(from, to, psU16, U16, oldname, newname, comment)
+
+#define PXOPT_COPY_U32(from, to, oldname, newname, comment) \
+  PXOPT_COPY_PRIMITIVE(from, to, psU32, U32, oldname, newname, comment)
+
+#define PXOPT_COPY_U64(from, to, oldname, newname, comment) \
+  PXOPT_COPY_PRIMITIVE(from, to, psU64, U64, oldname, newname, comment)
+
+
+/*** these PXOPT_ADD_WHERE macros are used to construct the default sql elements ***/
+
+#define PXOPT_ADD_WHERE_STR(name) \
+{ \
+    psString str = NULL; \
+    bool status = false; \
+    if ((str = psMetadataLookupStr(&status, config->args, "-" #name))) { \
+        if (!psMetadataAddStr(config->where, PS_LIST_TAIL, #name, 0, "==", str)) {\
+            psError(PS_ERR_UNKNOWN, false, "failed to add item " #name); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_STR_ALIAS(name,realname) \
+{ \
+    psString str = NULL; \
+    bool status = false; \
+    if ((str = psMetadataLookupStr(&status, config->args, name))) { \
+        if (!psMetadataAddStr(config->where, PS_LIST_TAIL, realname, 0, "==", str)) {\
+            psError(PS_ERR_UNKNOWN, false, "failed to add item %s", name); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_S16(name) \
+{ \
+    psS16 s16 = 0; \
+    bool status = false; \
+    if ((s16= psMetadataLookupS16(&status, config->args, "-" #name))) { \
+        if (!psMetadataAddS16(config->where, PS_LIST_TAIL, #name, 0, "==", s16)) { \
+            psError(PS_ERR_UNKNOWN, false, "failed to add item " #name); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_BOOL(name) \
+{ \
+    bool value = false;	 \
+    bool status = false; \
+    if ((value = psMetadataLookupBool(&status, config->args, "-" #name))) { \
+        if (!psMetadataAddBool(config->where, PS_LIST_TAIL, #name, 0, "==", value)) { \
+            psError(PS_ERR_UNKNOWN, false, "failed to add item " #name); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_S32(name) \
+{ \
+    psS32 s32 = 0; \
+    bool status = false; \
+    if ((s32= psMetadataLookupS32(&status, config->args, "-" #name))) { \
+        if (!psMetadataAddS32(config->where, PS_LIST_TAIL, #name, 0, "==", s32)) { \
+            psError(PS_ERR_UNKNOWN, false, "failed to add item " #name); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_S64(name) \
+{ \
+    psS64 s64 = 0; \
+    bool status = false; \
+    if ((s64= psMetadataLookupS64(&status, config->args, "-" #name))) { \
+        if (!psMetadataAddS64(config->where, PS_LIST_TAIL, #name, 0, "==", s64)) { \
+            psError(PS_ERR_UNKNOWN, false, "failed to add item " #name); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_F32(name) \
+{ \
+    psF32 var = 0; \
+    bool status = false; \
+    if ((var = psMetadataLookupF32(&status, config->args, "-" #name))) { \
+        if (!isnan(var)) { \
+            if (!psMetadataAddF32(config->where, PS_LIST_TAIL, #name, 0, "==", var)) { \
+                psError(PS_ERR_UNKNOWN, false, "failed to add item " #name); \
+                psFree(config); \
+                return NULL; \
+            } \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_F64(name) \
+{ \
+    psF64 var = 0; \
+    bool status = false; \
+    if ((var = psMetadataLookupF64(&status, config->args, "-" #name))) { \
+        if (!isnan(var))  { \
+            if (!psMetadataAddF64(config->where, PS_LIST_TAIL, #name, 0, "==", var)) { \
+                psError(PS_ERR_UNKNOWN, false, "failed to add item " #name); \
+                psFree(config); \
+                return NULL; \
+            } \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_BOOL_ALIAS(flag,name)	\
+{ \
+    bool value = 0; \
+    bool status = false; \
+    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); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_S16_ALIAS(flag,name)	\
+{ \
+    psS16 s16 = 0; \
+    bool status = false; \
+    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); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_S32_ALIAS(flag,name)	\
+{ \
+    psS32 s32 = 0; \
+    bool status = false; \
+    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); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_S64_ALIAS(flag,name)	\
+{ \
+    psS64 s64 = 0; \
+    bool status = false; \
+    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); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_F32_ALIAS(flag,name)	\
+{ \
+    psF32 var = 0; \
+    bool status = false; \
+    if ((var = psMetadataLookupF32(&status, config->args, flag))) {	\
+        if (!isnan(var))  { \
+            if (!psMetadataAddF32(config->where, PS_LIST_TAIL, name, 0, "==", var)) { \
+                psError(PS_ERR_UNKNOWN, false, "failed to add item %s", flag); \
+                psFree(config); \
+                return NULL; \
+            } \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_F64_ALIAS(flag,name)	\
+{ \
+    psF64 var = 0; \
+    bool status = false; \
+    if ((var = psMetadataLookupF64(&status, config->args, flag))) {	\
+        if (!isnan(var))  { \
+            if (!psMetadataAddF64(config->where, PS_LIST_TAIL, name, 0, "==", var)) { \
+                psError(PS_ERR_UNKNOWN, false, "failed to add item %s", flag); \
+                psFree(config); \
+                return NULL; \
+            } \
+        } \
+    } \
+}
+
+#define PXOPT_ADD_WHERE_TIME_STR(name) \
+{ \
+    psString str = NULL; \
+    bool status = false; \
+    if ((str = psMetadataLookupStr(&status, config->args, "-" #name))) { \
+        psTime *time = psTimeFromISO(str, PS_TIME_UTC); \
+        if (!time) { \
+            psError(PS_ERR_UNKNOWN, false, "failed to convert " #name " into a psTime object"); \
+            psFree(config); \
+            return NULL; \
+        } \
+        psMetadataItem *item = psMetadataLookup(config->args, "-" #name); \
+        if (item) { \
+            str = item->comment; \
+        } else { \
+            str = NULL; \
+        } \
+        if (!psMetadataAddTime(config->where, PS_LIST_TAIL, #name, 0, str, time)) {\
+            psError(PS_ERR_UNKNOWN, false, "failed to add item " #name); \
+            psFree(config); \
+            return NULL; \
+        } \
+        psFree(time); \
+    } \
+}
+
+#endif // PXTOOLS_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxtoolsErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxtoolsErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxtoolsErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,25 @@
+/*
+ * The line
+    { PXTOOLS_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+#include "pslib.h"
+#include "pxtoolsErrorCodes.h"
+
+void pxtoolsErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { PXTOOLS_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PXTOOLS_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = PXTOOLS_ERR_NERROR - PXTOOLS_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxtoolsErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxtoolsErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxtoolsErrorCodes.dat	(revision 22322)
@@ -0,0 +1,10 @@
+#
+# This file is used to generate pxtoolsErrorCodes.h
+#
+BASE = 1100		First value we use; lower values belong to psLib
+UNKNOWN			Unknown PM error code
+ARGUMENTS		Incorrect arguments
+SYS			System error
+CONFIG			Problem in configure files
+PROG			Programming error
+DATA   			invalid data
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxtoolsErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxtoolsErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxtoolsErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,18 @@
+#if !defined(PXTOOLS_ERROR_CODES_H)
+#define PXTOOLS_ERROR_CODES_H
+/*
+ * The line
+ *  PXTOOLS_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    PXTOOLS_ERR_BASE = 512,
+    PXTOOLS_ERR_${ErrorCode},
+    PXTOOLS_ERR_NERROR
+} pxtoolsErrorCode;
+
+void pxtoolsErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxtree.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxtree.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxtree.c	(revision 22322)
@@ -0,0 +1,239 @@
+/*
+ * pxio.c
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+
+#include "pxtools.h"
+#include "pxtree.h"
+
+static void pxNodeFree(pxNode *node)
+{
+    psFree(node->name);
+    psFree(node->parent);
+    psFree(node->children);
+    psFree(node->data);
+}
+
+pxNode *pxNodeAlloc(const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(name, NULL);
+
+    pxNode *node = psAlloc(sizeof(pxNode));
+
+    node->name = psStringCopy(name);
+    node->parent = NULL;
+    node->children = psListAlloc(NULL);
+    node->data = NULL;
+
+    psMemSetDeallocator(node, (psFreeFunc) pxNodeFree);
+
+    return node;
+}
+
+pxNode *pxNodeAddParent(pxNode *node, pxNode *parent)
+{
+    // add this node to it's parent's children list
+    psListAdd(parent->children, PS_LIST_TAIL, node);
+    // and set the parent pointer
+    node->parent = psMemIncrRefCounter(parent);
+
+    return node;
+}
+
+pxNode *pxNodeAddChild(pxNode *node, pxNode *child)
+{
+    // add the child node to this nodes list of children
+    psListAdd(node->children, PS_LIST_TAIL, child);
+    // if the child already has a parent, release the ref count
+    if (child->parent) {
+        psFree(child->parent);
+    }
+    // [re]parent the child
+    child->parent = psMemIncrRefCounter(node);
+
+    return node;
+}
+
+pxNode *pxNodeAddData(pxNode *node, psPtr data)
+{
+    node->data = psMemIncrRefCounter(data);
+
+    return node;
+}
+
+void pxNodePrint(FILE *stream, pxNode *node)
+{
+    fprintf(stream, "node name: %s\n", node->name);
+    fprintf(stream, "node parent's name: %s\n", node->parent
+                                              ?  node->parent->name
+                                              : NULL);
+    psListIterator *iter = psListIteratorAlloc(node->children, 0, false);
+    pxNode *child = NULL;
+    while ((child = psListGetAndIncrement(iter))) {
+        fprintf(stream, "node child's name: %s\n", child->name);
+    }
+    psFree(iter);
+}
+
+
+static bool printNodes(void *arg, pxNode *node)
+{
+    pxNodePrint((FILE *)arg, node);
+    fprintf((FILE *)arg, "\n\n");
+
+    return true;
+}
+
+
+void pxTreePrint(FILE *stream, pxNode *root)
+{
+    pxTreeCrawl(root, printNodes, (void *)stream);
+}
+
+// func() returning false means decend no futher along this branch
+
+bool pxTreeCrawl(pxNode *node, pxNodeFunc func, void *arg)
+{
+    // if func() returns false, stop
+    if (!func(arg, node)) {
+        return false;
+    }
+
+    psListIterator *iter = psListIteratorAlloc(node->children, 0, false);
+    pxNode *child = NULL;
+    while ((child = psListGetAndIncrement(iter))) {
+        pxTreeCrawl(child, func, arg);
+    }
+    psFree(iter);
+
+    return true;
+}
+
+bool pxNodeFuncCountDependants(void *arg, pxNode *node)
+{
+    (*(int *)arg)++;
+    return true;
+}
+
+bool pxNodeHasChildren(pxNode *node)
+{
+    int children = psListLength(node->children);
+
+    return children ? true : false;
+}
+
+bool pxNodeHasGrandChildren(pxNode *node)
+{
+    // find out how many nodes there are lower in the tree
+    // subtract the parent node from the count so we have just a tally of
+    // decendants
+    int nodes = -1;
+    pxTreeCrawl(node, pxNodeFuncCountDependants, &nodes);
+    psTrace("pxtree", PS_LOG_INFO, "node %s has %d dependants", node->name, nodes);
+
+    // find out how many children this node has
+    int children = psListLength(node->children);
+    psTrace("pxtree", PS_LOG_INFO, "node %s has %d children", node->name, children);
+
+    if (!children) {
+        // no children
+        return false;
+    }
+
+    if (nodes < children) {
+        psAbort("you can't have fewer children than decendants!");
+    }
+
+    if (nodes == children) {
+        // no grandchildren
+        return false;
+    }
+
+    return true;
+}
+
+pxNode *pxTreeFromMetadata(psMetadata *md)
+{
+    psHash *forest = psHashAlloc(10);
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, 0, NULL);
+    psMetadataItem *item = NULL;
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (!(item->type == PS_DATA_STRING)) {
+            continue;
+        }
+
+        // add this node to the forest and hopefully it'll get attached to the
+        // root tree
+        pxTreeBuilder(forest, item->name, item->data.str, NULL);
+    }
+    psFree(iter);
+
+    pxNode *root = psHashLookup(forest, "root");
+    psFree(forest);
+
+    return psMemIncrRefCounter(root);
+}
+
+psHash *pxTreeBuilder(psHash *forest,
+                      const char *nodeName,
+                      const char *childName,
+                      psPtr data)
+{
+    // try to find a node with this name
+    pxNode *node = psHashLookup(forest, nodeName);
+    if (!node) {
+        // create a new node with this name
+        node = pxNodeAlloc(nodeName);
+        if (data) {
+            pxNodeAddData(node, data);
+        }
+        // add it to the hash of nodes
+        psHashAdd(forest, nodeName, node);
+        // node may be used below because it will still have a ref count of
+        // 1 from being on the hash, this is equivalent to the "view" we
+        // get from a hashlookup
+        psFree(node);
+    } else if (!node->data) {
+        pxNodeAddData(node, data);
+    }
+
+    // does this node declare a child?
+    if (childName) {
+        // try to find a node with this name
+        pxNode *child = psHashLookup(forest, childName);
+        if (!child ) {
+            // create a new node with this name
+            child = pxNodeAlloc(childName);
+            // add it to the hash of nodes
+            psHashAdd(forest, childName, child);
+            // child may be used below because it will still have a ref
+            // count of 1 from being on the hash, this is equivalent to the
+            // "view" we get from a hashlookup
+            psFree(child);
+        }
+        pxNodeAddChild(node, child);
+    }
+
+    return forest;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxtree.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxtree.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxtree.h	(revision 22322)
@@ -0,0 +1,72 @@
+/*
+ * pxtree.h
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXTREE_H
+#define PXTREE_H 1
+
+#include <stdio.h>
+#include <stdbool.h>
+
+#include <pslib.h>
+
+
+typedef struct pxNode {
+  char *name;
+  struct pxNode *parent;
+  psList  *children;
+  void *data;
+} pxNode;
+
+typedef bool (*pxNodeFunc)(void *arg, pxNode *node);
+
+pxNode *pxNodeAlloc(
+    const char *name
+) PS_ATTR_MALLOC;
+
+pxNode *pxNodeAddParent(pxNode *node, pxNode *parent);
+pxNode *pxNodeAddChild(pxNode *node, pxNode *child);
+pxNode *pxNodeAddData(pxNode *node, psPtr data);
+
+void pxTreePrint(FILE *stream, pxNode *root);
+
+void pxNodePrint(
+    FILE *stream,
+    pxNode  *node
+);
+
+bool pxTreeCrawl(
+    pxNode  *node,
+    pxNodeFunc func,
+    void    *arg
+);
+
+bool pxNodeFuncCountDependants(void *arg, pxNode *node);
+
+bool pxNodeHasChildren(pxNode *node);
+
+bool pxNodeHasGrandChildren(pxNode *node);
+
+pxNode *pxTreeFromMetadata(psMetadata *md);
+
+psHash *pxTreeBuilder(psHash *forest,
+                      const char *nodeName,
+                      const char *childName,
+                      psPtr data);
+
+#endif // PXTREE_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxwarp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxwarp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxwarp.c	(revision 22322)
@@ -0,0 +1,157 @@
+/*
+ * pxwarp.c
+ *
+ * Copyright (C) 2007-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ippdb.h>
+#include <string.h>
+
+#include "pxtools.h"
+#include "pxwarp.h"
+
+bool pxwarpRunSetState(pxConfig *config, psS64 warp_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid warpRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE warpRun SET state = '%s' WHERE warp_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, state, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for warp_id %" PRId64, warp_id);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool pxwarpRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid chipRun state: %s", state);
+        return false;
+    }
+
+    psString query = psStringCopy("UPDATE warpRun JOIN fakeRun USING(fake_id) JOIN camRun USING(cam_id) JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id) SET warpRun.state = '%s'");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        if (whereClause && strlen(whereClause) > 0) {
+            psStringAppend(&query, " %s", whereClause);
+        }
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, state)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+
+bool pxwarpRunSetLabel(pxConfig *config, psS64 warp_id, const char *label)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    // note label == NULL should be explicitly allowed
+
+    char *query = "UPDATE warpRun SET warpRun.label = '%s' WHERE warp_id = %" PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, label, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change label for warp_id %" PRId64, warp_id);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool pxwarpRunSetLabelByQuery(pxConfig *config, psMetadata *where, const char *label)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    // note label == NULL should be explicitly allowed
+
+    psString query = psStringCopy("UPDATE warpRun JOIN fakeRun USING(fake_id) JOIN camRun USING(cam_id) JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id) SET warpRun.label = '%s'");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, label)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+
+bool pxwarpQueueByFakeID(pxConfig *config,
+                    psS64 fake_id,
+                    char *workdir,
+                    char *label,
+                    char *dvodb,
+                    char *tess_id,
+                    char *end_stage)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // depend on the f-keys to make sure we have a valid cam_id
+    if (!warpRunInsert(config->dbh,
+        0,          // ID
+        fake_id,
+        "warp",     // mode
+        "new",      // state
+        workdir,
+        "dirty",    // workdir_state
+        label,
+        dvodb,
+        tess_id,
+        end_stage,
+        NULL,       // registered
+        false       // magiced
+    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return true;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pxwarp.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pxwarp.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pxwarp.h	(revision 22322)
@@ -0,0 +1,40 @@
+/*
+ * pxwarp.h
+ *
+ * Copyright (C) 2007-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXWARP_H
+#define PXWARP_H 1
+
+#include <pslib.h>
+
+#include "pxtools.h"
+
+bool pxwarpRunSetState(pxConfig *config, psS64 warp_id, const char *state);
+bool pxwarpRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state);
+bool pxwarpRunSetLabel(pxConfig *config, psS64 warp_id, const char *label);
+bool pxwarpRunSetLabelByQuery(pxConfig *config, psMetadata *where, const char *label);
+
+bool pxwarpQueueByFakeID(pxConfig *config,
+                    psS64 fake_id,
+                    char *workdir,
+                    char *label,
+                    char *dvodb,
+                    char *tess_id,
+                    char *end_stage);
+
+#endif // PXWARP_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetexp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetexp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetexp.c	(revision 22322)
@@ -0,0 +1,368 @@
+/*
+ * pzgetexp.c
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "pxtools.h"
+#include "pzgetexp.h"
+
+#define PRODUCT_LS_CMD "dsproductls"
+
+static bool go (pxConfig *config);
+static psArray *parseFileSets(pxConfig *config, const char *str);
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = pzgetexpConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    if (!go(config)) {
+        goto FAIL;
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+    
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+static bool go(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    // required
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", true, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", true, false);
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", true, false);
+
+    //optional
+    PXOPT_LOOKUP_S32(timeout, config->args, "-timeout", false, false);
+    PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+
+    // find last fileset/exp_name (if we have one)
+    bool haveLastFileSet = false;
+    psString lastFileSet = NULL;
+
+    // -all means "request all known filesets"
+    if (!all) {
+        char *query = "SELECT * from summitExp WHERE camera = \"%s\" and TELESCOPE = \"%s\" ORDER BY dateobs DESC LIMIT 1";
+        if (!p_psDBRunQuery(config->dbh, query, camera, telescope)) {
+            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, false, "no summitExp rows found");
+            haveLastFileSet = false;
+            psFree(output);
+        } else {
+            haveLastFileSet = true;
+            bool status = false;
+            lastFileSet = psStringCopy(psMetadataLookupStr(&status, output->data[0], "exp_name"));
+            psFree(output);
+        }
+    }
+
+    // invoke dsproductls
+    // dsproductls --uri <> --last_fileset <>
+    psString cmd = NULL;
+    if (haveLastFileSet) {
+        psStringAppend(&cmd, "%s --uri %s --last_fileset %s",
+            PRODUCT_LS_CMD, uri, lastFileSet);
+        psFree(lastFileSet);
+    } else {
+        psStringAppend(&cmd, "%s --uri %s", PRODUCT_LS_CMD, uri);
+    }
+    if (timeout) {
+        psStringAppend(&cmd, " --timeout %d", timeout);
+    }
+
+    psTrace("pzgetexp", PS_LOG_INFO, "cmd is: %s\n", cmd);
+
+    FILE *output = popen(cmd, "r");
+    psFree(cmd);
+
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, true, "popen() failed");
+        return false;
+    }
+    psString cmdOutput = psSlurpFile(output);
+    pclose(output);
+
+    psArray *newSummitExps = parseFileSets(config, cmdOutput);
+    psFree(cmdOutput);
+    if (!newSummitExps) {
+        // XXX not nessicarily an error
+        psError(PS_ERR_UNKNOWN, true, "no new fileSet/exp IDs");
+        return false;
+    }
+    if (!psArrayLength(newSummitExps)) {
+        psTrace("pzgetexp", PS_LOG_INFO, "no new fileSet/exp IDs");
+        psFree(newSummitExps);
+        return true;
+    }
+
+    // start a transaction so it's all rows or nothing
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(newSummitExps);
+        return false;
+    }
+
+    // increase memory table size limits; deafult is 16MB
+    {
+        // 512MB
+        char *query = "SET max_heap_table_size = 1024*1024*512";
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(newSummitExps);
+            return false;
+        }
+    }
+
+    // create a temporry table
+    {
+        char *query = "CREATE TEMPORARY TABLE incoming" 
+            " (exp_name VARCHAR(64), camera VARCHAR(64), telescope VARCHAR(64), dateobs DATETIME, exp_type VARCHAR(64), uri VARCHAR(255), PRIMARY KEY(exp_name, camera, telescope))"
+           " ENGINE=MEMORY";
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(newSummitExps);
+            return false;
+        }
+    }
+
+    {
+        char *query = "INSERT INTO incoming (exp_name, camera, telescope, dateobs, exp_type, uri) VALUES (?, ?, ?, ?, ?, ?)";
+
+        long inserted = p_psDBRunQueryPrepared(config->dbh, newSummitExps, query);
+        if (inserted < 0) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(newSummitExps);
+            return false;
+        }
+        // sanity check that we actually inserted something
+        if (inserted == 0) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error -- we should have inserted at least one row");
+            psFree(newSummitExps);
+            return false;
+        }
+    }
+
+    psFree(newSummitExps);
+
+    // add new exps to summitExp
+    {
+        char *query = 
+            "INSERT INTO summitExp" 
+            "   SElECT"
+            "       incoming.*,"
+            "       NULL,"  // imfiles
+            "       0,"     // fault
+            "       NULL"   // epoch
+            "   FROM incoming"
+            "   LEFT JOIN summitExp"
+            "       USING(exp_name, camera, telescope)"
+            "   WHERE"
+            "       summitExp.exp_name is NULL"
+            "       AND summitExp.camera is NULL"
+            "       AND summitExp.telescope is NULL";
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            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 psArray *parseFileSets(pxConfig *config, const char *str)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(str, NULL);
+    
+    // these are constants for all records parsed -- look them up before we do
+    // any work
+    // required
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", true, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", true, false);
+
+    // split the string into lines
+    psList *doc = psStringSplit(str, "\n", false);
+
+    psListIterator *lineCursor = psListIteratorAlloc(doc, 0, false);
+
+    psArray *summitExps = psArrayAllocEmpty(psListLength(doc));
+    psString line;
+    while ((line = psListGetAndIncrement(lineCursor))) {
+        psTrace("pzgetimfile", PS_LOG_INFO, "parsing line: %s\n", line);
+
+        // split line into tokens
+        psList *tokens = psStringSplit(line, " ", false);
+
+        // check to see if this line is a comment (or if the first token is
+        // NULL)
+        if (!psListGet(tokens, 0) || *((char *)psListGet(tokens, 0)) == '#') {
+            psFree(tokens);
+            continue;
+        }
+
+        // check that we have the right number of tokens
+        // print "# uri fileset datetime type\n";
+        if (psListLength(tokens) != 4) {
+            // error
+            return false;
+        }
+
+        // find the values of interest
+        psListIterator *tokenCursor = psListIteratorAlloc(tokens, 0, false);
+        char *uri       = psListGetAndIncrement(tokenCursor);
+        char *exp_name    = psListGetAndIncrement(tokenCursor); // fileset
+        char *dateobsStr= psListGetAndIncrement(tokenCursor); // datetime
+        char *exp_type  = psListGetAndIncrement(tokenCursor); // type
+
+        // create a new metadata to represent this line and it's values
+        psMetadata *md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_name", 0, NULL, exp_name)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "camera", 0, NULL, camera)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "telescope", 0, NULL, telescope)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        psTime *dateobs = psTimeFromISO(dateobsStr, PS_TIME_UTC);
+        if (!psMetadataAddTime(md, PS_LIST_TAIL, "dateobs", 0, NULL, dateobs)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+            psFree(dateobs);
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        psFree(dateobs);
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_type", 0, NULL, exp_type)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, uri)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+
+        // must be freed after the new metadata is built -- holds the strings
+        psFree(tokenCursor);
+        psFree(tokens);
+
+        // add the new metadata to the result set
+        psArrayAdd(summitExps, 0, md);
+
+        // debugging
+        if (psTraceGetLevel("pzgetexp") == PS_LOG_INFO) {
+            psString doc = psMetadataConfigFormat(md);
+            psTrace("pzgetexp", PS_LOG_INFO, "parsed line as:\n %s\n", doc);
+            psFree(doc);
+        }
+
+        psFree(md);
+
+    }
+
+    psFree(lineCursor);
+    psFree(doc);
+
+    return summitExps;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetexp.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetexp.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetexp.h	(revision 22322)
@@ -0,0 +1,27 @@
+/*
+ * pzgetexp.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PZGETEXP_H
+#define PZGETEXP_H 1
+
+#include "pxtools.h"
+
+pxConfig *pzgetexpConfig(pxConfig *config, int argc, char **argv);
+
+#endif // PZGETEXP_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetexpConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetexpConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetexpConfig.c	(revision 22322)
@@ -0,0 +1,88 @@
+/*
+ * pzgetexpConfig.c
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "pxtools.h"
+
+pxConfig *pzgetexpConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (! config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration!\n");
+        psFree(config);
+        return NULL;
+    }
+
+    psMetadata *args = psMetadataAlloc();
+    psMetadataAddStr(args , PS_LIST_TAIL, "-uri", 0,
+        "DataStore product URI (required)", "");
+    psMetadataAddStr(args , PS_LIST_TAIL, "-inst", 0,
+        "camera name (required)", "");
+    psMetadataAddStr(args , PS_LIST_TAIL, "-telescope",  0,
+        "telescope name (required)", "");
+    psMetadataAddS32(args, PS_LIST_TAIL, "-timeout",  0,
+        "HTTP timeout", 0);
+    psMetadataAddBool(args, PS_LIST_TAIL, "-all",  0,
+        "download ALL filesets", 0);
+
+
+    bool status = false;
+    if (!psArgumentParse(args, &argc, argv)
+        || argc != 1
+        || strcmp(psMetadataLookupStr(&status, args, "-uri"), "") == 0
+        || strcmp(psMetadataLookupStr(&status, args, "-inst"), "") == 0
+        || strcmp(psMetadataLookupStr(&status, args, "-telescope"), "") == 0
+    ) {
+        fprintf(stderr, "error parsing arguments\n");
+        printf("\nPan-STARRS Phase Z Get Exposures Tool\n");
+        printf("Usage: %s -uri <uri> -inst <camera> -telescope <telescope> [-timeout <n>]\n\n",
+            argv[0]);
+        psArgumentHelp(args);
+        psFree(config);
+        return NULL;
+    }
+
+    config->args = args;
+    // don't free args here as it's silly to increment the ref count then
+    // "free" it
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if(!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't connect to db\n");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetimfiles.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetimfiles.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetimfiles.c	(revision 22322)
@@ -0,0 +1,482 @@
+/*
+ * pzgetimfiles.c
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "pxtag.h"
+#include "pxtools.h"
+#include "pzgetimfiles.h"
+
+#define FILESET_LS_CMD "dsfilesetls"
+
+static bool go (pxConfig *config);
+static psArray *parseFiles(pxConfig *config, const char *str);
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = pzgetimfilesConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    if (!go(config)) {
+        goto FAIL;
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+static bool go(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    // required
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", true, false);
+    PXOPT_LOOKUP_STR(filesetid, config->args, "-filesetid", true, false);
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", true, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", true, false);
+
+    // optional
+    PXOPT_LOOKUP_S32(timeout, config->args, "-timeout", false, false);
+
+    // invoke dsfilesetls
+    psString cmd = NULL;
+    psStringAppend(&cmd, "%s --uri %s", FILESET_LS_CMD, uri);
+    if (timeout) {
+        psStringAppend(&cmd, " --timeout %d", timeout);
+    }
+
+    psTrace("pzgetimfiles", PS_LOG_INFO, "cmd is: %s\n", cmd);
+
+    FILE *output = popen(cmd, "r");
+    psFree(cmd);
+
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, true, "popen() failed");
+        return false;
+    }
+
+    psString cmdOutput = psSlurpFile(output);
+    int status = pclose(output);
+
+    if (status != 0) {
+        // mark the summitExp row as faulted
+        if (!p_psDBRunQuery(config->dbh, "UPDATE summitExp SET fault = %d WHERE exp_name = '%s' AND camera = '%s' AND telescope = '%s'", WEXITSTATUS(status), filesetid, camera, telescope)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+
+        psError(PS_ERR_UNKNOWN, true, "%s failed with exit status %d",
+            FILESET_LS_CMD, WEXITSTATUS(status));
+        psFree(cmdOutput);
+        return false;
+    }
+
+    // prase output of dsfilesetls
+    psArray *newImfiles = parseFiles(config, cmdOutput);
+    if (!newImfiles) {
+        // XXX not nessicarily an error
+        psError(PS_ERR_UNKNOWN, true, "no new files/imfiles");
+        psFree(cmdOutput);
+        return false;
+    }
+    psFree(cmdOutput);
+    
+    // save the number of new Imfiles;
+    long imfiles = psArrayLength(newImfiles);
+
+    // start a transaction so it's all rows or nothing
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // if the fileset was empty (no files) then we can bail out early
+    if (imfiles == 0) {
+        psFree(newImfiles);
+
+        char *query = 
+            "UPDATE summitExp"
+            " SET imfiles = %d"
+            " WHERE exp_name = '%s'"
+            " AND camera = '%s'"
+            " AND telescope = '%s'";
+        if (!p_psDBRunQuery(config->dbh, query, imfiles, filesetid, camera, telescope)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+
+        // remove the pzDownloadExp entry for this exp (fileset)
+        {
+            char *query = 
+                "DELETE FROM pzDownloadExp"
+                " WHERE"
+                "   exp_name = '%s'"
+                "   AND camera = '%s'"
+                "   AND telescope = '%s'";
+            if (!p_psDBRunQuery(config->dbh, query, filesetid, camera, telescope)) {
+                // rollback
+                if (!psDBRollback(config->dbh)) {
+                    psError(PS_ERR_UNKNOWN, false, "database error");
+                }
+                psError(PS_ERR_UNKNOWN, false, "database error");
+                return false;
+            }
+
+            // sanity check: we should have removed only one row
+            psU64 affected = psDBAffectedRows(config->dbh);
+            if (psDBAffectedRows(config->dbh) != 1) {
+                // rollback
+                if (!psDBRollback(config->dbh)) {
+                    psError(PS_ERR_UNKNOWN, false, "database error");
+                }
+                psError(PS_ERR_UNKNOWN, false, "should have affected 1 row but %" PRIu64 " rows were modified", affected);
+                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;
+    } 
+
+    // create a temp table
+    {
+        char *query = 
+            "CREATE TEMPORARY TABLE incoming (exp_name VARCHAR(64), camera VARCHAR(64), telescope VARCHAR(64), file_id VARCHAR(64), bytes INT, md5sum VARCHAR(32), class VARCHAR(64), class_id VARCHAR(64), uri VARCHAR(255), PRIMARY KEY(exp_name, camera, telescope, class, class_id)) ENGINE=MEMORY";
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(newImfiles);
+            return false;
+        }
+    }
+
+    // load the imfiles (files) into the temp table
+    {
+        char *query = "INSERT INTO incoming (exp_name, camera, telescope, file_id, bytes, md5sum, class, class_id, uri) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+        long inserted = p_psDBRunQueryPrepared(config->dbh, newImfiles, query);
+        if (inserted < 0) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(newImfiles);
+            return false;
+        }
+        // sanity check that we actually inserted something
+        if (inserted == 0) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error -- we should have inserted at least one row");
+            psFree(newImfiles);
+            return false;
+        }
+    }
+
+    psFree(newImfiles);
+
+    // copy imfiles (files) from the temp table into summitImfiles
+    {
+        char *query = 
+            "INSERT IGNORE INTO summitImfile" 
+            "   SELECT"
+            "       incoming.exp_name,"
+            "       incoming.camera,"
+            "       incoming.telescope,"
+            "       incoming.file_id,"
+            "       incoming.bytes,"
+            "       incoming.md5sum,"
+            "       incoming.class,"
+            "       incoming.class_id,"
+            "       incoming.uri,"
+            "       NULL"       // epoch
+            "   FROM incoming";
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+    }
+
+    // update summitExp.imfiles (should have been NULL)
+    {
+        char *query = 
+            "UPDATE summitExp"
+            " SET imfiles = (SELECT COUNT(*) FROM summitImfile"
+            "   WHERE"
+            "       exp_name = '%s'"
+            "       AND camera = '%s'"
+            "       AND telescope = '%s'"
+            ")" 
+            " WHERE"
+            "   exp_name = '%s'"
+            "   AND camera = '%s'"
+            "   AND telescope = '%s'"
+            "   AND imfiles IS NULL";
+        if (!p_psDBRunQuery(config->dbh, query, filesetid, camera, telescope, filesetid, camera, telescope)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+
+        // sanity check: we should have updated only one row
+        psU64 affected = psDBAffectedRows(config->dbh);
+        if (psDBAffectedRows(config->dbh) != 1) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "should have affected 1 row but %" PRIu64 " rows were modified", affected);
+            return false;
+        }
+    }
+
+    // add new exps to pzDownloadExp -- must be done before the new exps are
+    // added to summitExp because of the SQL logic
+    {
+        char *query = 
+            "INSERT IGNORE INTO pzDownloadExp" 
+            "   SELECT"
+            "       incoming.exp_name,"
+            "       incoming.camera,"
+            "       incoming.telescope,"
+            "       \"run\","    // state
+            "       NULL"
+            "   FROM incoming"
+            "   GROUP BY"
+            "       incoming.exp_name,"
+            "       incoming.camera,"
+            "       incoming.telescope";
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            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 psArray *parseFiles(pxConfig *config, const char *str)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(str, NULL);
+
+    // these are constants for all records parsed -- look them up before we do
+    // any work
+    PXOPT_LOOKUP_STR(exp_name, config->args, "-filesetid", true, false);
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", true, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", true, false);
+
+    // split the string into lines
+    psList *doc = psStringSplit(str, "\n", false);
+
+    psListIterator *lineCursor = psListIteratorAlloc(doc, 0, false);
+
+    psArray *pzPendingImfiles = psArrayAllocEmpty(psListLength(doc));
+    psString line;
+    while ((line = psListGetAndIncrement(lineCursor))) {
+        psTrace("pzgetimfiles", PS_LOG_INFO, "parsing line: %s\n", line);
+
+        // split line into tokens
+        psList *tokens = psStringSplit(line, " ", false);
+
+        // check to see if this line is a comment (or if the first token is
+        // NULL)
+        if (!psListGet(tokens, 0) || *((char *)psListGet(tokens, 0)) == '#') {
+            psFree(tokens);
+            continue;
+        }
+
+        // check that we have the right number of tokens
+        // print "# uri fileid bytes md5sum type \n";
+        if (!psListLength(tokens) > 5) {
+            psError(PS_ERR_UNKNOWN, true, "invalid line format: %s", line);
+            psFree(tokens);
+            psFree(pzPendingImfiles);                
+            psFree(lineCursor);
+            psFree(doc);
+            return false;
+        }
+
+        // find the values of interest
+        psListIterator *tokenCursor = psListIteratorAlloc(tokens, 0, false);
+        char *uri       = psListGetAndIncrement(tokenCursor);
+        char *file_id   = psListGetAndIncrement(tokenCursor); // fileid
+        char *bytes     = psListGetAndIncrement(tokenCursor); // bytes
+        char *md5sum    = psListGetAndIncrement(tokenCursor); // md5sum
+        char *class     = psListGetAndIncrement(tokenCursor); // type
+        char *class_id  = psListGetAndIncrement(tokenCursor); // chipname
+
+        // create a new metadata to represent this line and it's values
+        psMetadata *md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_name", 0, NULL, exp_name)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "camera", 0, NULL, camera)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "telescope", 0, NULL, telescope)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "file_id", 0, NULL, file_id)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddS64(md, PS_LIST_TAIL, "bytes", 0, NULL, (psS64)atoll(bytes))) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "md5sum", 0, NULL, md5sum)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item md5sum");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class", 0, NULL, class)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, class_id)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, uri)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+            psFree(md);
+            psFree(tokenCursor);
+            psFree(tokens);
+            return NULL;
+        }
+
+        // must be freed after the new metadata is built -- holds the strings
+        psFree(tokenCursor);
+        psFree(tokens);
+
+        // debugging
+        if (psTraceGetLevel("pzgetimfiles") >= PS_LOG_INFO) {
+            psString doc = psMetadataConfigFormat(md);
+            psTrace("pzgetimfiles", PS_LOG_INFO, "parsed line as:\n %s\n", doc);
+            psFree(doc);
+        }
+
+        psArrayAdd(pzPendingImfiles, 0, md);
+
+        psFree(md);
+    }
+
+    psFree(lineCursor);
+    psFree(doc);
+
+    if (!psArrayLength(pzPendingImfiles)) {
+        psError(PS_ERR_UNKNOWN, false, "string contained no valid rows");
+        psFree(pzPendingImfiles);
+        return false;
+    }
+
+    return pzPendingImfiles;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetimfiles.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetimfiles.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetimfiles.h	(revision 22322)
@@ -0,0 +1,27 @@
+/*
+ * pzgetimfiles.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PZGETIMFILES_H
+#define PZGETIMFILES 1
+
+#include "pxtools.h"
+
+pxConfig *pzgetimfilesConfig(pxConfig *config, int argc, char **argv);
+
+#endif // PZGETIMFILES_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetimfilesConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetimfilesConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pzgetimfilesConfig.c	(revision 22322)
@@ -0,0 +1,89 @@
+/*
+ * pzgetimfilesConfig.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "pxtools.h"
+
+pxConfig *pzgetimfilesConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (! config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration!\n");
+        psFree(config);
+        return NULL;
+    }
+
+    psMetadata *args = psMetadataAlloc();
+    psMetadataAddStr(args , PS_LIST_TAIL, "-uri", 0,
+            "DataStore FileSet URI (required)", NULL);
+    psMetadataAddStr(args , PS_LIST_TAIL, "-filesetid", 0,
+            "FileSet ID (required)", NULL);
+    psMetadataAddStr(args, PS_LIST_TAIL, "-inst",  0,
+            "Camera ID (required)", NULL);
+    psMetadataAddStr(args, PS_LIST_TAIL, "-telescope",  0,
+            "Telescope ID (required)", NULL);
+    psMetadataAddS32(args, PS_LIST_TAIL, "-timeout",  0,
+            "HTTP timeout", 0);
+
+
+    bool status = false;
+    if (!psArgumentParse(args, &argc, argv)
+        || argc != 1
+        || (psMetadataLookupStr(&status, args, "-uri") == NULL)
+        || (psMetadataLookupStr(&status, args, "-filesetid") == NULL)
+        || (psMetadataLookupStr(&status, args, "-inst") == NULL)
+        || (psMetadataLookupStr(&status, args, "-telescope") == NULL)
+    ) {
+        fprintf(stderr, "error parsing arguments\n");
+        printf("\nPan-STARRS Phase Z Get Imfiles Tool\n");
+        printf("Usage: %s -uri <uri> -filesetid <id> -inst <camera> -telescope <telescope [-timeout <n>]\n\n", argv[0]);
+        psArgumentHelp(args);
+        psFree(args);
+        psFree(config);
+        return NULL;
+    }
+
+    config->args = args;
+    // don't free args here as it's silly to increment the ref count then
+    // "free" it
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if(!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't connect to db\n");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pztool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pztool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pztool.c	(revision 22322)
@@ -0,0 +1,916 @@
+/*
+ * pztool.c
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "pslib.h"
+#include "pxtools.h"
+#include "pxdata.h"
+#include "pztool.h"
+
+static bool adddatastoreMode(pxConfig *config);
+static bool datastoreMode(pxConfig *config);
+static bool seenMode(pxConfig *config);
+static bool pendingExpMode(pxConfig *config);
+static bool pendingImfileMode(pxConfig *config);
+
+static bool copydoneMode(pxConfig *config);
+static bool copiedMode(pxConfig *config);
+static bool updatecopiedMode(pxConfig *config);
+static bool revertcopiedMode(pxConfig *config);
+
+static bool clearcommonfaultsMode(pxConfig *config);
+static bool advanceMode(pxConfig *config);
+
+static bool copydoneCompleteExp(pxConfig *config, const char *exp_name, const char *camera, const char *telescope);
+static psArray *pzGetPendingCameras(pxConfig *config);
+static psArray *pzArrayZip(psArray *arraySet, psS64 limit);
+static bool pzDownloadExpSetState(pxConfig *config, const char *exp_name, const char *camera, const char *telescope, const char *state);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+                goto FAIL; \
+            } \
+    break;
+
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = pztoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(PZTOOL_MODE_ADDDATASTORE, adddatastoreMode);
+        MODECASE(PZTOOL_MODE_DATASTORE, datastoreMode);
+        MODECASE(PZTOOL_MODE_SEEN, seenMode);
+        MODECASE(PZTOOL_MODE_PENDINGEXP, pendingExpMode);
+        MODECASE(PZTOOL_MODE_PENDINGIMFILE, pendingImfileMode);
+        MODECASE(PZTOOL_MODE_COPYDONE, copydoneMode);
+        MODECASE(PZTOOL_MODE_COPIED, copiedMode);
+        MODECASE(PZTOOL_MODE_UPDATECOPIED, updatecopiedMode);
+        MODECASE(PZTOOL_MODE_REVERTCOPIED, revertcopiedMode);
+        MODECASE(PZTOOL_MODE_CLEARCOMMONFAULTS, clearcommonfaultsMode);
+        MODECASE(PZTOOL_MODE_ADVANCE, advanceMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+static bool adddatastoreMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", true, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", true, false);
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", true, false);
+
+    if (!pzDataStoreInsert(config->dbh,
+            camera,
+            telescope,
+            uri,
+            NULL  // epoch
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool datastoreMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    if (!p_psDBRunQuery(config->dbh, "SELECT * FROM pzDataStore")) {
+        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)) {
+        psTrace("pztool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pzDataStore", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool seenMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    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,  "-exp_type",     "exp_type", "==");
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM summitExp");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "summitExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("pztool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "summitExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool pendingExpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    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,  "-exp_type",     "exp_type", "==");
+
+    PXOPT_LOOKUP_BOOL(desc, config->args, "-desc", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // XXX leave this query here ?
+    psString query = psStringCopy(
+            "SELECT"
+            "   summitExp.*"
+            " FROM summitExp"
+            " LEFT JOIN pzDownloadExp"
+            "   USING(exp_name, camera, telescope)"
+            " WHERE"
+            "   pzDownloadExp.exp_name IS NULL"
+            "   AND pzDownloadExp.camera IS NULL"
+            "   AND pzDownloadExp.telescope IS NULL"
+            "   AND summitExp.fault = 0"
+        );
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "pzDownloadExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    psStringAppend(&query, " ORDER BY summitExp.dateobs");
+    if (desc) {
+        psStringAppend(&query, " DESC");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("pztool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pzDownloadExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool pendingImfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    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,  "-exp_type",     "exp_type", "==");
+
+    PXOPT_LOOKUP_BOOL(desc, config->args, "-desc", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psArray *cameras = pzGetPendingCameras(config);
+    if (!cameras) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to find any cameras");
+        return false;
+    }
+
+    // array to hold the aggregate results
+    psArray *cameraImfiles = psArrayAlloc(0);
+
+    for (long i = 0; i < psArrayLength(cameras); i++) {
+        psString query = pxDataGet("pztool_pendingimfile.sql");
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            psFree(cameraImfiles);
+            return false;
+        }
+
+        bool status;
+        psString camera = psMetadataLookupStr(&status, cameras->data[i], "camera");
+        psStringAppend(&query, " WHERE camera = \"%s\"", camera);
+
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, "pzDownloadImfile");
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        psStringAppend(&query, " ORDER BY dateobs");
+        if (desc) {
+            psStringAppend(&query, " DESC");
+        }
+
+        // request the full "limit" from each known camera and throw away any
+        // "extra" rows that we may have after merging the results.  This is
+        // a lot simplier than a complicated scheme (tried that) to attempt to
+        // request on the right number of rows for each camera
+        
+        // treat limit == 0 as "no limit"
+        if (limit) {
+            psString limitString = psDBGenerateLimitSQL(limit);
+            psStringAppend(&query, " %s", limitString);
+            psFree(limitString);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            psFree(cameraImfiles);
+            return false;
+        }
+        psFree(query);
+
+        psArray *result = p_psDBFetchResult(config->dbh);
+        if (!result) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(cameraImfiles);
+            return false;
+        }
+        if (!psArrayLength(result)) {
+            psTrace("pztool", PS_LOG_INFO, "no rows found");
+            psFree(result);
+            continue;
+        }
+
+        // add this query into the array of result set
+        psArrayAdd(cameraImfiles, 0, result);
+        psFree(result);
+    }
+    psFree(where);
+
+    // stitch the arrays of imfiles together
+    psArray *output = pzArrayZip(cameraImfiles, limit);
+    psFree(cameraImfiles);
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pzDownloadImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool copydoneMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_STR(exp_name, config->args, "-exp_name", true, false);
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", true, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", true, false);
+    PXOPT_LOOKUP_STR(class, config->args, "-class", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", true, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // NOTE : the rest of the command-line args are parsed in copydoneCompleteExp
+
+    if (!pzDownloadImfileInsert(config->dbh,
+            exp_name,
+            camera,
+            telescope,
+            class,
+            class_id,
+            uri,
+            code,
+            NULL    // epoch
+    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return 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 (!copydoneCompleteExp(config, exp_name, camera, telescope)) {
+        // 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 copydoneCompleteExp(pxConfig *config, const char *exp_name, const char *camera, const char *telescope)
+{
+    // 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);
+
+    // find all exposures that have had all of their imfiles downloaded but do
+    // not appear in newExp
+    psString query = pxDataGet("pztool_find_completed_exp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // XXX this is a bit ugly : could just use the PXOPT_COPY_* methods 
+    // and not pass exp_name, camera, or telescope as additional args
+    psMetadata *where = psMetadataAlloc();
+    if (exp_name) {
+        if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_name", 0, "==", exp_name)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+            psFree(where);
+            return false;
+        }
+    }
+
+    if (camera) {
+        if (!psMetadataAddStr(where, PS_LIST_TAIL, "camera", 0, "==", camera)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+            psFree(where);
+            return false;
+        }
+    }
+
+    if (telescope) {
+        if (!psMetadataAddStr(where, PS_LIST_TAIL, "telescope", 0, "==", telescope)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+            psFree(where);
+            return false;
+        }
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("pztool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+   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
+                )
+        ) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(doneExp);
+            psFree(output);
+            return false;
+        }
+
+        psS64 exp_id = psDBLastInsertID(config->dbh);
+
+        // insert newImfiles
+        {
+            char *query =
+                "INSERT INTO newImfile"
+                "   SElECT"
+                "       %" PRId64 "," // exp_id
+                "       pzDownloadImfile.class_id," // tmp_class_id
+                "       pzDownloadImfile.uri," // uri
+                "       NULL" // epoch
+                "   FROM pzDownloadImfile"
+                "   WHERE"
+                "       pzDownloadImfile.exp_name = '%s'"
+                "       AND pzDownloadImfile.camera = '%s'"
+                "       AND pzDownloadImfile.telescope = '%s'";
+
+            if (!p_psDBRunQuery(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);
+            return false;
+        }
+
+        psFree(doneExp);
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static psArray *pzGetPendingCameras(pxConfig *config)
+{
+    // get a list of cameras we've seen exps for
+    if (!p_psDBRunQuery(config->dbh, "SELECT DISTINCT camera FROM pzDownloadExp")) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psArray *cameras = p_psDBFetchResult(config->dbh);
+    if (!cameras) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return NULL;
+    }
+    if (!psArrayLength(cameras)) {
+        psTrace("pztool", PS_LOG_INFO, "no rows found");
+        psFree(cameras);
+        return psArrayAlloc(0);
+    }
+
+    return cameras;
+}
+
+static psArray *pzArrayZip(psArray *arraySet, psS64 limit)
+{
+    // figure out the combined size of all arrays in the set
+    long setSize = 0;
+    for (long i = 0; i < psArrayLength(arraySet); i++) {
+        setSize += psArrayLength(arraySet->data[i]);
+    }
+
+    // treat 0 as "no limit"
+    if (limit == 0) {
+        limit = setSize;
+    }
+
+    psArray *output = psArrayAllocEmpty(limit);
+    // loop over each array in the set forever
+    for (
+            // init
+            long counter = 0,   // the total number of elements zipped so far
+            i = 0,              // which array in the set 
+            index = 0;          // the depth into each array
+            // test
+            (counter < setSize)
+            && (counter < limit)
+            && (i < psArrayLength(arraySet));
+            // incr
+            counter++, ++i,
+            i = i % psArrayLength(arraySet),
+            i % psArrayLength(arraySet) ? : ++index
+        ) {
+
+        psArray *array = arraySet->data[i];
+        // make sure that this array has not run out of elements
+        if (!(index < psArrayLength(array))) {
+            continue;
+        }
+
+        psArrayAdd(output, 0, array->data[index]);
+    }
+
+    return output;
+}
+
+
+static bool copiedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where,  "-exp_name", "exp_name", "==");
+    PXOPT_COPY_STR(config->args, where, "-inst", "inst", "==");
+    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
+    PXOPT_COPY_STR(config->args, where,  "-class", "class", "==");
+    PXOPT_COPY_STR(config->args, where,  "-class_id", "class_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    psString query = psStringCopy("SELECT * FROM pzDownloadImfile");
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "WHERE pzDownloadImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "WHERE pzDownloadImfile.fault = 0");
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "pzDownloadImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("pztool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipPendingImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool updatecopiedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where,  "-exp_name", "exp_name", "==");
+    PXOPT_COPY_STR(config->args, where, "-inst", "inst", "==");
+    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
+    PXOPT_COPY_STR(config->args, where,  "-class", "class", "==");
+    PXOPT_COPY_STR(config->args, where,  "-class_id", "class_id", "==");
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", true, false);
+
+    if (!pxSetFaultCode(config->dbh, "pzDownloadImfile", where, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+	psFree (where);
+	return false;
+    }
+    psFree(where);
+
+    return true;
+}
+
+
+static bool revertcopiedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where,  "-exp_name", "exp_name", "==");
+    PXOPT_COPY_STR(config->args, where, "-inst", "inst", "==");
+    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
+    PXOPT_COPY_STR(config->args, where,  "-class", "class", "==");
+    PXOPT_COPY_STR(config->args, where,  "-class_id", "class_id", "==");
+
+    psString query = pxDataGet("pztool_revertcopied.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "pzDownloadImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool clearcommonfaultsMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+{
+    psString query = pxDataGet("pztool_revert_downloadimfile_faults.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+}
+
+{
+    psString query = pxDataGet("pztool_revert_fileset_faults.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+}
+
+    return true;
+}
+
+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, NULL, NULL, NULL)) {
+        // 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)
+{
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!(
+            (strncmp(state, "run", 4) == 0)
+            || (strncmp(state, "stop", 5) == 0)
+            || (strncmp(state, "reg", 4) == 0)
+        )
+    ) {
+        psError(PS_ERR_UNKNOWN, false,
+                "invalid pzDownloadExp state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE pzDownloadExp SET state = '%s' WHERE exp_name = '%s' and camera = '%s' and telescope = '%s'";
+    if (!p_psDBRunQuery(config->dbh, query, state, exp_name, camera, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to change state for %s:%s:%s", exp_name, camera, telescope);
+        return false;
+    }
+
+    return true;
+}
+
+
+#if 0
+static psArray *pzArrayAddArray(psArray *array, psArray *input)
+{
+    for (long i = 0; i < psArrayLength(input); i++) {
+        psArrayAdd(array, psArrayLength(input), input->data[i]);
+    }
+
+    return array;
+}
+#endif
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pztool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pztool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pztool.h	(revision 22322)
@@ -0,0 +1,42 @@
+/*
+ * pztool.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PZTOOL_H
+#define PZTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    PZTOOL_MODE_NONE      = 0x0,
+    PZTOOL_MODE_ADDDATASTORE,
+    PZTOOL_MODE_DATASTORE,
+    PZTOOL_MODE_SEEN,
+    PZTOOL_MODE_PENDINGEXP,
+    PZTOOL_MODE_PENDINGIMFILE,
+    PZTOOL_MODE_COPYDONE,
+    PZTOOL_MODE_COPIED,
+    PZTOOL_MODE_UPDATECOPIED,
+    PZTOOL_MODE_REVERTCOPIED,
+    PZTOOL_MODE_CLEARCOMMONFAULTS,
+    PZTOOL_MODE_ADVANCE
+} pztoolMode;
+
+pxConfig *pztoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // PZTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/pztoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/pztoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/pztoolConfig.c	(revision 22322)
@@ -0,0 +1,175 @@
+/*
+ * pztoolConfig.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "pztool.h"
+
+pxConfig *pztoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (! config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration!\n");
+        psFree(config);
+        return NULL;
+    }
+
+    // -adddatastore
+    psMetadata *adddatastoreArgs = psMetadataAlloc();
+    psMetadataAddStr(adddatastoreArgs, PS_LIST_TAIL, "-inst", 0,            "define camera ID", NULL); 
+    psMetadataAddStr(adddatastoreArgs, PS_LIST_TAIL, "-telescope", 0,            "define telescope ID", NULL); 
+    psMetadataAddStr(adddatastoreArgs, PS_LIST_TAIL, "-uri", 0,            "define storage uri", NULL);
+    
+    // -datastore
+    psMetadata *datastoreArgs = psMetadataAlloc();
+    psMetadataAddBool(datastoreArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -seen
+    psMetadata *seenArgs = psMetadataAlloc();
+    psMetadataAddStr(seenArgs, PS_LIST_TAIL, "-exp_name", 0,            "define exposure ID", NULL); 
+    psMetadataAddStr(seenArgs, PS_LIST_TAIL, "-inst", 0,            "define camera ID", NULL); 
+    psMetadataAddStr(seenArgs, PS_LIST_TAIL, "-telescope", 0,            "define telescope ID", NULL); 
+    psMetadataAddStr(seenArgs, PS_LIST_TAIL, "-exp_type", 0,            "define exposure type", NULL); 
+    psMetadataAddBool(seenArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+    
+    // -pendingexp
+    psMetadata *pendingexpArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingexpArgs, PS_LIST_TAIL, "-exp_name", 0,            "define exposure ID", NULL); 
+    psMetadataAddStr(pendingexpArgs, PS_LIST_TAIL, "-inst", 0,            "define camera ID", NULL); 
+    psMetadataAddStr(pendingexpArgs, PS_LIST_TAIL, "-telescope", 0,            "define telescope ID", NULL); 
+    psMetadataAddStr(pendingexpArgs, PS_LIST_TAIL, "-exp_type", 0,            "define exposure type", NULL); 
+    psMetadataAddBool(pendingexpArgs, PS_LIST_TAIL, "-desc", 0,            "sort ouput in descending format", false);
+    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);
+
+    // -pendingimfile
+    psMetadata *pendingimfileArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-exp_name", 0,            "define exposure ID", NULL); 
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-inst", 0,            "define camera ID", NULL); 
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-telescope", 0,            "define telescope ID", NULL); 
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-exp_type", 0,            "define exposure type", NULL); 
+    psMetadataAddBool(pendingimfileArgs, PS_LIST_TAIL, "-desc", 0,            "sort ouput in descending format", false);
+    psMetadataAddU64(pendingimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(pendingimfileArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+
+    // -copydone
+    psMetadata *copydoneArgs = psMetadataAlloc();
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-exp_name", 0,            "define exposure ID (required)", NULL); 
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-inst", 0,            "define camera ID (required)", NULL); 
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-telescope", 0,            "define telescope ID (required)", NULL); 
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-class", 0,            "define class", NULL);
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-class_id", 0,            "define class_id", NULL);
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-uri", 0,            "define storage uri", NULL);
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-workdir",  0,        "define the \"default\" workdir for this exposure", NULL);
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-dvodb",  0,        "define the dvodb for the next processing step", NULL);
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-tess_id",  0,        "define the tess_id for the next processing step", NULL);
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-end_stage",  0,        "define the end goal processing step", NULL);
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-label",  0,        "define the label for the chip stage", NULL);
+    psMetadataAddS16(copydoneArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+
+    // -copied
+    psMetadata *copiedArgs = psMetadataAlloc();
+    psMetadataAddStr(copiedArgs, PS_LIST_TAIL, "-exp_name", 0,            "define exposure ID", NULL); 
+    psMetadataAddStr(copiedArgs, PS_LIST_TAIL, "-inst", 0,            "define camera ID", NULL); 
+    psMetadataAddStr(copiedArgs, PS_LIST_TAIL, "-telescope", 0,            "define telescope ID", NULL); 
+    psMetadataAddStr(copiedArgs, PS_LIST_TAIL, "-class", 0,            "define class", NULL);
+    psMetadataAddStr(copiedArgs, PS_LIST_TAIL, "-class_id", 0,            "define class_id", NULL);
+    psMetadataAddBool(copiedArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
+    psMetadataAddU64(copiedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(copiedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -updatecopied
+    psMetadata *updatecopiedArgs = psMetadataAlloc();
+    psMetadataAddStr(updatecopiedArgs, PS_LIST_TAIL, "-exp_name", 0,            "define exposure ID (required)", NULL); 
+    psMetadataAddStr(updatecopiedArgs, PS_LIST_TAIL, "-inst", 0,            "define camera ID (required)", NULL); 
+    psMetadataAddStr(updatecopiedArgs, PS_LIST_TAIL, "-telescope", 0,            "define telescope ID (required)", NULL); 
+    psMetadataAddStr(updatecopiedArgs, PS_LIST_TAIL, "-class", 0,            "define class", NULL);
+    psMetadataAddStr(updatecopiedArgs, PS_LIST_TAIL, "-class_id", 0,            "define class_id", NULL);
+    psMetadataAddS16(updatecopiedArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -revertcopied
+    psMetadata *revertcopiedArgs = psMetadataAlloc();
+    psMetadataAddStr(revertcopiedArgs, PS_LIST_TAIL, "-exp_name", 0,            "define exposure ID (required)", NULL); 
+    psMetadataAddStr(revertcopiedArgs, PS_LIST_TAIL, "-inst", 0,            "define camera ID (required)", NULL); 
+    psMetadataAddStr(revertcopiedArgs, PS_LIST_TAIL, "-telescope", 0,            "define telescope ID (required)", NULL); 
+    psMetadataAddStr(revertcopiedArgs, PS_LIST_TAIL, "-class", 0,            "define class", NULL);
+    psMetadataAddStr(revertcopiedArgs, PS_LIST_TAIL, "-class_id", 0,            "define class_id", NULL);
+    psMetadataAddS16(revertcopiedArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -clearcommonfaults
+    psMetadata *clearcommonfaultsArgs = psMetadataAlloc();
+
+    // -advance
+    psMetadata *advanceArgs = psMetadataAlloc();
+    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);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-adddatastore",    "", PZTOOL_MODE_ADDDATASTORE, adddatastoreArgs);
+    PXOPT_ADD_MODE("-datastore",       "", PZTOOL_MODE_DATASTORE,    datastoreArgs);
+    PXOPT_ADD_MODE("-seen",            "", PZTOOL_MODE_SEEN,         seenArgs);
+    PXOPT_ADD_MODE("-pendingexp",      "", PZTOOL_MODE_PENDINGEXP,   pendingexpArgs);
+    PXOPT_ADD_MODE("-pendingimfile",   "", PZTOOL_MODE_PENDINGIMFILE,pendingimfileArgs);
+    PXOPT_ADD_MODE("-copydone",        "", PZTOOL_MODE_COPYDONE,     copydoneArgs);
+    PXOPT_ADD_MODE("-copied",          "", PZTOOL_MODE_COPIED,      copiedArgs);
+    PXOPT_ADD_MODE("-updatecopied",    "", PZTOOL_MODE_UPDATECOPIED,updatecopiedArgs);
+    PXOPT_ADD_MODE("-revertcopied",    "", PZTOOL_MODE_REVERTCOPIED,revertcopiedArgs);
+    PXOPT_ADD_MODE("-clearcommonfaults","", PZTOOL_MODE_CLEARCOMMONFAULTS,clearcommonfaultsArgs);
+    PXOPT_ADD_MODE("-advance",          "", PZTOOL_MODE_ADVANCE,    advanceArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/regtool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/regtool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/regtool.c	(revision 22322)
@@ -0,0 +1,1097 @@
+/*
+ * regtool.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "pxtools.h"
+#include "pxdata.h"
+#include "regtool.h"
+#include "chiptool.h"
+
+// imfile
+static bool pendingimfileMode(pxConfig *config);
+static bool addprocessedimfileMode(pxConfig *config);
+static bool processedimfileMode(pxConfig *config);
+static bool revertprocessedimfileMode(pxConfig *config);
+static bool updateprocessedimfileMode(pxConfig *config);
+// exp
+static bool pendingexpMode(pxConfig *config);
+static bool addprocessedexpMode(pxConfig *config);
+static bool processedexpMode(pxConfig *config);
+static bool revertprocessedexpMode(pxConfig *config);
+static bool updateprocessedexpMode(pxConfig *config);
+static bool cleardupexpMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = regtoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        // imfile
+        MODECASE(REGTOOL_MODE_PENDINGIMFILE,         pendingimfileMode);
+        MODECASE(REGTOOL_MODE_ADDPROCESSEDIMFILE,    addprocessedimfileMode);
+        MODECASE(REGTOOL_MODE_PROCESSEDIMFILE,       processedimfileMode);
+        MODECASE(REGTOOL_MODE_REVERTPROCESSEDIMFILE, revertprocessedimfileMode);
+        MODECASE(REGTOOL_MODE_UPDATEPROCESSEDIMFILE, updateprocessedimfileMode);
+        // exp
+        MODECASE(REGTOOL_MODE_PENDINGEXP,            pendingexpMode);
+        MODECASE(REGTOOL_MODE_ADDPROCESSEDEXP,       addprocessedexpMode);
+        MODECASE(REGTOOL_MODE_PROCESSEDEXP,          processedexpMode);
+        MODECASE(REGTOOL_MODE_REVERTPROCESSEDEXP,    revertprocessedexpMode);
+        MODECASE(REGTOOL_MODE_UPDATEPROCESSEDEXP,    updateprocessedexpMode);
+        MODECASE(REGTOOL_MODE_CLEARDUPEXP,           cleardupexpMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+static bool pendingimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // select newImfiles that:
+    // exp_id is in newExp
+    // don't have their exp_id in rawExp
+    // XXX having the same exp_id in newExp and raw*Exp is probably an error
+    // that should be checked for
+
+    psString query = pxDataGet("regtool_pendingimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        // XXX PS_EXIT_PROG_ERROR (incorrect SQL) or SYS_ERROR (database comms)
+        psError(PXTOOLS_ERR_PROG, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("regtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "regPendingImfile", !simple)) {
+        psError(PXTOOLS_ERR_PROG, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(exp_name, config->args, "-exp_name", true, false);
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", true, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", true, false);
+    PXOPT_LOOKUP_STR(tmp_class_id, config->args, "-tmp_class_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(exp_type, config->args, "-exp_type", false, false);
+    PXOPT_LOOKUP_STR(filelevel, config->args, "-filelevel", false, false);
+    PXOPT_LOOKUP_STR(filter, config->args, "-filter", false, false);
+    PXOPT_LOOKUP_STR(comment, config->args, "-comment", false, false);
+
+    PXOPT_LOOKUP_F32(airmass, config->args, "-airmass", false, false);
+    PXOPT_LOOKUP_F64(ra, config->args, "-ra", false, false);
+    PXOPT_LOOKUP_F64(decl, config->args, "-decl", false, false);
+    PXOPT_LOOKUP_F32(exp_time, config->args, "-exp_time", false, false);
+    PXOPT_LOOKUP_F32(sat_pixel_frac, config->args, "-sat_pixel_frac", false, false);
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(alt, config->args, "-alt", false, false);
+    PXOPT_LOOKUP_F64(az, config->args, "-az", false, false);
+    PXOPT_LOOKUP_F32(ccd_temp, config->args, "-ccd_temp", false, false);
+    PXOPT_LOOKUP_F64(posang, config->args, "-posang", false, false);
+    PXOPT_LOOKUP_F32(m1_x, config->args, "-m1_x", false, false);
+    PXOPT_LOOKUP_F32(m1_y, config->args, "-m1_y", false, false);
+    PXOPT_LOOKUP_F32(m1_z, config->args, "-m1_z", false, false);
+    PXOPT_LOOKUP_F32(m1_tip, config->args, "-m1_tip", false, false);
+    PXOPT_LOOKUP_F32(m1_tilt, config->args, "-m1_tilt", false, false);
+    PXOPT_LOOKUP_F32(m2_x, config->args, "-m2_x", false, false);
+    PXOPT_LOOKUP_F32(m2_y, config->args, "-m2_y", false, false);
+    PXOPT_LOOKUP_F32(m2_z, config->args, "-m2_z", false, false);
+    PXOPT_LOOKUP_F32(m2_tip, config->args, "-m2_tip", false, false);
+    PXOPT_LOOKUP_F32(m2_tilt, config->args, "-m2_tilt", false, false);
+
+    PXOPT_LOOKUP_F32(env_temp, config->args, "-env_temperature", false, false);
+    PXOPT_LOOKUP_F32(env_humid, config->args, "-env_humidity", false, false);
+    PXOPT_LOOKUP_F32(env_wind, config->args, "-env_wind_speed", false, false);
+    PXOPT_LOOKUP_F32(env_dir, config->args, "-env_wind_dir", false, false);
+    PXOPT_LOOKUP_F32(teltemp_m1, config->args, "-teltemp_m1", false, false);
+    PXOPT_LOOKUP_F32(teltemp_m1cell, config->args, "-teltemp_m1cell", false, false);
+    PXOPT_LOOKUP_F32(teltemp_m2, config->args, "-teltemp_m2", false, false);
+    PXOPT_LOOKUP_F32(teltemp_spider, config->args, "-teltemp_spider", false, false);
+    PXOPT_LOOKUP_F32(teltemp_truss, config->args, "-teltemp_truss", false, false);
+    PXOPT_LOOKUP_F32(teltemp_extra, config->args, "-teltemp_extra", false, false);
+    PXOPT_LOOKUP_F32(pon_time, config->args, "-pon_time", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(object, config->args, "-object", false, false);
+    PXOPT_LOOKUP_F32(sun_angle,  config->args, "-sun_angle", false, false);
+    PXOPT_LOOKUP_F32(sun_alt,    config->args, "-sun_alt",   false, false);
+    PXOPT_LOOKUP_F32(moon_angle, config->args, "-moon_angle", false, false);
+    PXOPT_LOOKUP_F32(moon_alt,   config->args, "-moon_alt",   false, false);
+    PXOPT_LOOKUP_F32(moon_phase, config->args, "-moon_phase", false, false);
+    PXOPT_LOOKUP_TIME(dateobs, config->args, "-dateobs", false, false);
+    PXOPT_LOOKUP_STR(hostname, config->args, "-hostname", false, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    if (!rawImfileInsert(
+        config->dbh,
+        exp_id,
+        exp_name,
+        camera,
+        telescope,
+        dateobs,
+        tmp_class_id,
+        class_id,
+        uri,
+        exp_type,
+        filelevel,
+        filter,
+        comment,
+        airmass,
+        ra,
+        decl,
+        exp_time,
+        sat_pixel_frac,
+        bg,
+        bg_stdev,
+        bg_mean_stdev,
+        alt,
+        az,
+        ccd_temp,
+        posang,
+        m1_x,
+        m1_y,
+        m1_z,
+        m1_tip,
+        m1_tilt,
+        m2_x,
+        m2_y,
+        m2_z,
+        m2_tip,
+        m2_tilt,
+        env_temp,
+        env_humid,
+        env_wind,
+        env_dir,
+        teltemp_m1,
+        teltemp_m1cell,
+        teltemp_m2,
+        teltemp_spider,
+        teltemp_truss,
+        teltemp_extra,
+        pon_time,
+        user_1,
+        user_2,
+        user_3,
+        user_4,
+        user_5,
+        object,
+	sun_angle,
+	sun_alt,
+	moon_angle,
+	moon_alt,
+	moon_phase,
+        hostname,
+        code,
+        NULL
+    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool processedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    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_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("regtool_processedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "rawImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND rawImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND rawImfile.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("regtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "rawImfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-exp_id",       "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where,  "-tmp_class_id", "tmp_class_id", "==");
+    PXOPT_COPY_STR(config->args, where,  "-class_id",     "class_id", "==");
+    PXOPT_COPY_S16(config->args, where,  "-code",         "fault", "==");
+
+    psString query = pxDataGet("regtool_revertprocessedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+	psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "rawImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool updateprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-exp_id",       "exp_id", "==");
+    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;
+    }
+    psFree (where);
+
+    return true;
+}
+
+
+static bool pendingexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // return only exps that:
+    // are not in rawExp
+    // have ALL of their imfiles in rawImfile (by count)
+    // and have no associated imfiles left in newImfile
+    psString query = pxDataGet("regtool_pendingexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // treat limit == 0 as "no limit"
+    psString limitString = NULL;
+    if (limit) {
+        limitString = psDBGenerateLimitSQL(limit);
+        // skip past the "hook" comment
+        psStringPrepend(&limitString, "\n");
+    }
+
+    // 1st arg: where hook, 2nd arg: limit hook
+    if (!p_psDBRunQuery(config->dbh, query, "", limitString ? limitString : "")) {
+        // XXX PS_EXIT_PROG_ERROR (incorrect SQL) or SYS_ERROR (database comms)
+        psError(PXTOOLS_ERR_PROG, false, "database error");
+        psFree(limitString);
+        psFree(query);
+        return false;
+    }
+    psFree(limitString);
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("regtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "regPendingExp", !simple)) {
+        psError(PXTOOLS_ERR_PROG, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // make sure that the exp_id(s) are ready to be updated based on:
+    // exp_id is not in rawExp
+    // exp_id is not in newImfile
+    // that the correct count of imfiles is in rawImfile
+
+    // required
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(exp_name, config->args, "-exp_name", true, false);
+    PXOPT_LOOKUP_STR(camera, config->args, "-inst", true, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", true, false);
+    PXOPT_LOOKUP_STR(exp_tag, config->args, "-exp_tag", true, false);
+    PXOPT_LOOKUP_STR(filelevel, config->args, "-filelevel", true, false);
+
+    // optional
+    PXOPT_LOOKUP_TIME(dateobs, config->args, "-dateobs", false, false);
+    PXOPT_LOOKUP_STR(exp_type, config->args, "-exp_type", false, false);
+    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(reduction, config->args, "-reduction", false, false);
+    PXOPT_LOOKUP_STR(filter, config->args, "-filter", false, false);
+    PXOPT_LOOKUP_STR(comment, config->args, "-comment", false, false);
+    PXOPT_LOOKUP_F32(airmass, config->args, "-airmass", false, false);
+    PXOPT_LOOKUP_F64(ra, config->args, "-ra", false, false);
+    PXOPT_LOOKUP_F64(decl, config->args, "-decl", false, false);
+    PXOPT_LOOKUP_F32(exp_time, config->args, "-exp_time", false, false);
+    PXOPT_LOOKUP_F32(sat_pixel_frac, config->args, "-sat_pixel_frac", false, false);
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(alt, config->args, "-alt", false, false);
+    PXOPT_LOOKUP_F64(az, config->args, "-az", false, false);
+    PXOPT_LOOKUP_F32(ccd_temp, config->args, "-ccd_temp", false, false);
+    PXOPT_LOOKUP_F64(posang, config->args, "-posang", false, false);
+    PXOPT_LOOKUP_F32(m1_x, config->args, "-m1_x", false, false);
+    PXOPT_LOOKUP_F32(m1_y, config->args, "-m1_y", false, false);
+    PXOPT_LOOKUP_F32(m1_z, config->args, "-m1_z", false, false);
+    PXOPT_LOOKUP_F32(m1_tip, config->args, "-m1_tip", false, false);
+    PXOPT_LOOKUP_F32(m1_tilt, config->args, "-m1_tilt", false, false);
+    PXOPT_LOOKUP_F32(m2_x, config->args, "-m2_x", false, false);
+    PXOPT_LOOKUP_F32(m2_y, config->args, "-m2_y", false, false);
+    PXOPT_LOOKUP_F32(m2_z, config->args, "-m2_z", false, false);
+    PXOPT_LOOKUP_F32(m2_tip, config->args, "-m2_tip", false, false);
+    PXOPT_LOOKUP_F32(m2_tilt, config->args, "-m2_tilt", false, false);
+
+    PXOPT_LOOKUP_F32(env_temp, config->args, "-env_temperature", false, false);
+    PXOPT_LOOKUP_F32(env_humid, config->args, "-env_humidity", false, false);
+    PXOPT_LOOKUP_F32(env_wind, config->args, "-env_wind_speed", false, false);
+    PXOPT_LOOKUP_F32(env_dir, config->args, "-env_wind_dir", false, false);
+    PXOPT_LOOKUP_F32(teltemp_m1, config->args, "-teltemp_m1", false, false);
+    PXOPT_LOOKUP_F32(teltemp_m1cell, config->args, "-teltemp_m1cell", false, false);
+    PXOPT_LOOKUP_F32(teltemp_m2, config->args, "-teltemp_m2", false, false);
+    PXOPT_LOOKUP_F32(teltemp_spider, config->args, "-teltemp_spider", false, false);
+    PXOPT_LOOKUP_F32(teltemp_truss, config->args, "-teltemp_truss", false, false);
+    PXOPT_LOOKUP_F32(teltemp_extra, config->args, "-teltemp_extra", false, false);
+    PXOPT_LOOKUP_F32(pon_time, config->args, "-pon_time", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(object, config->args, "-object", false, false);
+    PXOPT_LOOKUP_F32(sun_angle,  config->args, "-sun_angle", false, false);
+    PXOPT_LOOKUP_F32(sun_alt,    config->args, "-sun_alt",   false, false);
+    PXOPT_LOOKUP_F32(moon_angle, config->args, "-moon_angle", false, false);
+    PXOPT_LOOKUP_F32(moon_alt,   config->args, "-moon_alt",   false, false);
+    PXOPT_LOOKUP_F32(moon_phase, config->args, "-moon_phase", false, false);
+    PXOPT_LOOKUP_STR(label,  config->args, "-label", false, false);
+    PXOPT_LOOKUP_STR(hostname, config->args, "-hostname", false, false);
+
+    // default
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    psString query = pxDataGet("regtool_pendingexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    psString whereClause = NULL;
+    {
+        // build a query to search by exp_id
+        psMetadata *where = psMetadataAlloc();
+        if (!psMetadataAddS64(where, PS_LIST_TAIL, "newExp.exp_id", 0, "==", exp_id)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+            psFree(where);
+            psFree(query);
+            return false;
+        }
+
+        whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psFree(where);
+        if (whereClause) {
+            // skip past comment "hook"
+            psStringPrepend(&whereClause, "\n AND ");
+        }
+    }
+
+    // 1st arg: where hook, 2nd arg: limit hook
+    if (!p_psDBRunQuery(config->dbh, query, whereClause ? whereClause : "", "")) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(whereClause);
+        psFree(query);
+        return false;
+    }
+    psFree(whereClause);
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psError(PS_ERR_UNKNOWN, false, "no pending newExp rows found");
+        psFree(output);
+        return false;
+    }
+    // sanity check that we only got one row
+    if (psArrayLength(output) != 1) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "should have gotten 1 row but %lu rows were returned", psArrayLength(output));
+        psFree(output);
+        return NULL;
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+
+    // insert the exp into rawExp
+    psMetadata *row = output->data[0];
+    // convert metadata into a newExp object
+    psMetadataConfigPrint(stdout, row);
+    newExpRow *newExp = newExpObjectFromMetadata(row);
+    psFree(output);
+    if (!newExp) {
+        psError(PS_ERR_UNKNOWN, false, "this should not happen");
+        return false;
+    }
+
+    // carry through these values
+    // new CLI options overwrite existing values
+    workdir   = workdir   ? workdir   : newExp->workdir;
+    reduction = reduction ? reduction : newExp->reduction;
+    dvodb     = dvodb     ? dvodb     : newExp->dvodb;
+    tess_id   = tess_id   ? tess_id   : newExp->tess_id;
+    end_stage = end_stage ? end_stage : newExp->end_stage;
+    label     = label     ? label     : newExp->label;
+    // don't free newExp until just before we return, or these refs will break
+
+    if (!rawExpInsert(config->dbh,
+        exp_id,
+        exp_name,
+        camera,
+        telescope,
+        dateobs,
+        exp_tag,
+        exp_type,
+        filelevel,
+        workdir,
+        reduction,
+        dvodb,
+        tess_id,
+        end_stage,
+        filter,
+        comment,
+        airmass,
+        ra,
+        decl,
+        exp_time,
+        sat_pixel_frac,
+        bg,
+        bg_stdev,
+        bg_mean_stdev,
+        alt,
+        az,
+        ccd_temp,
+        posang,
+        m1_x,
+        m1_y,
+        m1_z,
+        m1_tip,
+        m1_tilt,
+        m2_x,
+        m2_y,
+        m2_z,
+        m2_tip,
+        m2_tilt,
+        env_temp,
+        env_humid,
+        env_wind,
+        env_dir,
+        teltemp_m1,
+        teltemp_m1cell,
+        teltemp_m2,
+        teltemp_spider,
+        teltemp_truss,
+        teltemp_extra,
+        pon_time,
+        user_1,
+        user_2,
+        user_3,
+        user_4,
+        user_5,
+        object,
+	sun_angle,
+	sun_alt,
+	moon_angle,
+	moon_alt,
+	moon_phase,
+        hostname,
+        code,
+        NULL
+    )) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(newExp);
+        return false;
+    }
+
+    // set the state for the newExp to stop
+    if (!pxnewExpSetState(config, exp_id, "stop")) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "failed to change newExp.state for exp_id: %"PRId64, exp_id);
+    psFree(newExp);
+        return false;
+    }
+
+    // should we stop here and proceed on to the chip stage?
+    // NULL for end_stage means go as far as possible
+    if (end_stage && psStrcasestr(end_stage, "reg")) {
+        // then we are done here
+        if (!psDBCommit(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+
+    psFree(newExp);
+        return true;
+    }
+    // else continue on...
+
+
+    // insert an entry into the chipPendingExp table
+    // this can only be run as the newExp's state has been set to stop
+    if (!pxchipQueueByExpTag(config,
+                exp_id,
+                workdir,
+            label,
+                reduction,
+                NULL, // expgroup
+                dvodb,
+                tess_id,
+                end_stage
+    )) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "failed to queue chipPendingExp");
+    psFree(newExp);
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(newExp);
+        return false;
+    }
+
+    psFree(newExp);
+    return true;
+}
+
+
+static bool processedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psString query = pxDataGet("regtool_processedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // XX test this out; need to make this consistent with the list in regtoolConfig.c
+    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,  "-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", "bg", ">=");
+    PXOPT_COPY_F64(config->args, where,  "-bg_max", "bg", "<");
+    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", "<");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "rawExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND rawExp.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND rawExp.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("regtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "rawExp", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool revertprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-exp_id",       "exp_id", "==");
+    PXOPT_COPY_S16(config->args, where,  "-code",         "fault", "==");
+
+    psString query = pxDataGet("regtool_revertprocessedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+	psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "rawExp");
+        psStringAppend(&query, " AND %s", whereClause);
+	psFree(where);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool updateprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-exp_id",       "exp_id", "==");
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", true, false);
+
+    if (!pxSetFaultCode(config->dbh, "rawExp", where, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+	psFree(where);
+        return false;
+    }
+    psFree(where);
+
+    return true;
+}
+
+
+static bool cleardupexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+// create temp table of exp_ids to be removed
+{
+    psString query = pxDataGet("regtool_create_dup_table.sql");
+    if (!query) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+}
+
+// populate that table
+// XXX note that this query currently doesn't not correctly handle the case
+// where there is more than one duplicate
+{
+    psString query = pxDataGet("regtool_populate_dup_table.sql");
+    if (!query) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+}
+
+    if (!p_psDBRunQuery(config->dbh, "DELETE FROM rawImfile USING rawImfile, duplicate WHERE duplicate.exp_id = rawImfile.exp_id")) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, "DELETE FROM rawExp USING rawExp, duplicate WHERE duplicate.exp_id = rawExp.exp_id")) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, "DELETE FROM newImfile USING newImfile, duplicate WHERE duplicate.exp_id = newImfile.exp_id")) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, "DELETE FROM newExp USING newExp, duplicate WHERE duplicate.exp_id = newExp.exp_id")) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/regtool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/regtool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/regtool.h	(revision 22322)
@@ -0,0 +1,42 @@
+/*
+ * regtool.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef REGTOOL_H
+#define REGTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    REGTOOL_MODE_NONE      = 0x0,
+    REGTOOL_MODE_PENDINGIMFILE,
+    REGTOOL_MODE_ADDPROCESSEDIMFILE,
+    REGTOOL_MODE_PROCESSEDIMFILE,
+    REGTOOL_MODE_REVERTPROCESSEDIMFILE,
+    REGTOOL_MODE_UPDATEPROCESSEDIMFILE,
+    REGTOOL_MODE_PENDINGEXP,
+    REGTOOL_MODE_ADDPROCESSEDEXP,
+    REGTOOL_MODE_PROCESSEDEXP,
+    REGTOOL_MODE_REVERTPROCESSEDEXP,
+    REGTOOL_MODE_UPDATEPROCESSEDEXP,
+    REGTOOL_MODE_CLEARDUPEXP,
+} regtoolMode;
+
+pxConfig *regtoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // REGTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/regtoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/regtoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/regtoolConfig.c	(revision 22322)
@@ -0,0 +1,312 @@
+/*
+ * regtoolConfig.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <stdint.h>
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "regtool.h"
+
+#define ADD_OPT(TYPE,TARG,NAME,COMMENT,DEFAULT) psMetadataAdd##TYPE(TARG, PS_LIST_TAIL, NAME, 0, COMMENT, DEFAULT)
+
+pxConfig *regtoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PXTOOLS_ERR_CONFIG, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    // -pendingimfile
+    psMetadata *pendingimfileArgs = psMetadataAlloc();
+    ADD_OPT(U64,  pendingimfileArgs, "-limit",     "limit result set to N items",  0);
+    ADD_OPT(Bool, pendingimfileArgs, "-simple",    "use the simple output format", false);
+
+    // -addprocessedimfile
+    psMetadata *addprocessedimfileArgs = psMetadataAlloc();
+    ADD_OPT(S64,  addprocessedimfileArgs, "-exp_id",         "define exposure ID (required)",        0);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-exp_name",       "define the exp_name (required)",         NULL);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-inst",           "define the camera name (required)",     NULL);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-telescope",      "define the telescope name (required)",     NULL);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-tmp_class_id",   "define temp. class ID (required)",     NULL);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-class_id",       "define class ID (required)",         NULL);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-uri",            "define URI (required)",             NULL);
+
+    ADD_OPT(F32,  addprocessedimfileArgs, "-longitude",      "specify the observatory longitude (NOTE: not saved in db)", NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-latitude",       "specify the observatory latitude (NOTE: not saved in db)", NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-elevation",       "specify the elevation (NOTE: not saved in db)", NAN);
+
+    ADD_OPT(Str,  addprocessedimfileArgs, "-exp_type",       "define exposure type",             NULL);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-filelevel",      "define filelevel",             NULL);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-filter",         "define filter ",                 NULL);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-comment",        "define comment ",             NULL);
+
+    ADD_OPT(F32,  addprocessedimfileArgs, "-airmass",        "define airmass",                 NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-ra",             "define RA",                 NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-decl",           "define DEC",                 NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-exp_time",       "define exposure time",             NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-sat_pixel_frac", "define fraction of saturated pixels",     NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-bg",             "define exposue background",          NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-bg_stdev",       "define exposue background stdev",     NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-bg_mean_stdev",  "define exposue background mean stdev",     NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-alt",            "define altitute",                  NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-az",             "define azimuth",                      NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-ccd_temp",       "define ccd tempature",                NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-posang",         "define rotator position angle",         NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-m1_x",           "define M1 X position",                NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-m1_y",           "define M1 Y position",                NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-m1_z",           "define M1 Z position",                NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-m1_tip",         "define M1 TIP position",                NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-m1_tilt",        "define M1 TILT position",            NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-m2_x",           "define M2 X position",                NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-m2_y",           "define M2 Y position",                NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-m2_z",           "define M2 Z position",                NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-m2_tip",         "define M2 TIP position",                NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-m2_tilt",        "define M2 TILT position",         NAN);
+
+    ADD_OPT(F32,  addprocessedimfileArgs, "-env_temperature","define Environmental Temperature",     NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-env_humidity",   "define Environmental Humidity",         NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-env_wind_speed", "define Environmental Wind Speed",     NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-env_wind_dir",   "define Environmental Wind Direction",     NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-teltemp_m1",     "define Telescope Temperature : M1",     NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-teltemp_m1cell", "define Telescope Temperature : M1 Cell",     NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-teltemp_m2",     "define Telescope Temperature : M2",     NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-teltemp_spider", "define Telescope Temperature : Spider",     NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-teltemp_truss",  "define Telescope Temperature : Truss",     NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-teltemp_extra",  "define Telescope Temperature : Extra",     NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-pon_time",       "define time to last Power On",         NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-user_1",         "define user statistic (1)",         NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-user_2",         "define user statistic (2)",         NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-user_3",         "define user statistic (3)",         NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-user_4",         "define user statistic (4)",         NAN);
+    ADD_OPT(F64,  addprocessedimfileArgs, "-user_5",         "define user statistic (5)",         NAN);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-object",         "define exposure object",             NULL);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-sun_angle",      "define angle to sun",             NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-sun_alt",        "define sun altitude (neg = below horizon)", NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-moon_angle",     "define angle to moon",             NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-moon_alt",       "define moon altitude (neg = below horizon)", NAN);
+    ADD_OPT(F32,  addprocessedimfileArgs, "-moon_phase",     "define moon phase (0.0 = new)",   NAN);
+    ADD_OPT(Time, addprocessedimfileArgs, "-dateobs",        "define observation time",         NULL);
+    ADD_OPT(Str,  addprocessedimfileArgs, "-hostname",       "define host name",                NULL);
+    ADD_OPT(S16,  addprocessedimfileArgs, "-code",           "set fault code",                  0);
+
+    // -processedimfile
+    psMetadata *processedimfileArgs = psMetadataAlloc();
+    ADD_OPT(S64,  processedimfileArgs, "-exp_id",    "search by exposure ID",                 0);
+    ADD_OPT(Str,  processedimfileArgs, "-exp_name",  "search by exposure name",               NULL);
+    ADD_OPT(Str,  processedimfileArgs, "-class_id",  "search by class ID",                    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);
+    ADD_OPT(Bool, processedimfileArgs, "-simple",    "use the simple output format",          false);
+
+    // -revertprocessedimfile
+    psMetadata *revertprocessedimfileArgs = psMetadataAlloc();
+    ADD_OPT(S64, revertprocessedimfileArgs, "-exp_id",        "search by exposure ID (required)", 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);
+
+    // -updateprocessedimfile
+    psMetadata *updateprocessedimfileArgs = psMetadataAlloc();
+    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);
+
+    // -pendingexp
+    psMetadata *pendingexpArgs = psMetadataAlloc();
+    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);
+
+    // -addprocessedexp
+    psMetadata *addprocessedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(addprocessedexpArgs, PS_LIST_TAIL, "-exp_id",           0,        "exp_id to operate on (required)", 0);
+    psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-exp_name",         0,        "define the exp_name (required)", NULL);
+    psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-inst",             0,        "define the camera name (required)", NULL);
+    psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-telescope",        0,        "define the telescope name (required)", NULL);
+    psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-exp_tag",          0,        "define the external exposure tag name (required)", NULL);
+    psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-filelevel",        0,        "define the data partitioning level of this file (required)", NULL);
+
+    ADD_OPT(F32,  addprocessedexpArgs, "-longitude", 	    "specify the observatory longitude (NOTE: not saved in db)", 0.0);
+    ADD_OPT(F32,  addprocessedexpArgs, "-latitude",  	    "specify the observatory latitude (NOTE: not saved in db)", 0.0);
+
+    ADD_OPT(Time, addprocessedexpArgs, "-dateobs",          "define observation time", NULL);
+    ADD_OPT(Str,  addprocessedexpArgs, "-exp_type",         "define exposure type", NULL);
+    ADD_OPT(Str,  addprocessedexpArgs, "-workdir",          "define the \"default\" workdir for this exposure", NULL);
+    ADD_OPT(Str,  addprocessedexpArgs, "-dvodb",            "define the dvodb for the next processing step", NULL);
+    ADD_OPT(Str,  addprocessedexpArgs, "-tess_id",          "define the tess_id for the next processing step", NULL);
+    ADD_OPT(Str,  addprocessedexpArgs, "-end_stage",        "define the end goal processing step", NULL);
+    ADD_OPT(Str,  addprocessedexpArgs, "-reduction",        "define the \"default\" reduction class for this exposure", NULL);
+    ADD_OPT(Str,  addprocessedexpArgs, "-filter",           "define filter ", NULL);
+    ADD_OPT(Str,  addprocessedexpArgs, "-comment",          "define comment ", NULL);
+    ADD_OPT(F32,  addprocessedexpArgs, "-airmass",          "define airmass", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-ra",               "define RA", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-decl",             "define DEC", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-exp_time",         "define exposure time", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-sat_pixel_frac",   "define fraction of saturated pixels", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-bg",               "define exposue background", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-bg_stdev",         "define exposue background stdev", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-bg_mean_stdev",    "define exposue background mean stdev", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-alt",              "define altitute", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-az",               "define azimuth", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-ccd_temp",         "define ccd tempature", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-posang",           "define rotator position angle", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-m1_x",             "define M1 X position", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-m1_y",             "define M1 Y position", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-m1_z",             "define M1 Z position", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-m1_tip",           "define M1 TIP position", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-m1_tilt",          "define M1 TILT position", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-m2_x",             "define M2 X position", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-m2_y",             "define M2 Y position", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-m2_z",             "define M2 Z position", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-m2_tip",           "define M2 TIP position", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-m2_tilt",          "define M2 TILT position", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-env_temperature",  "define Environmental Temperature", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-env_humidity",     "define Environmental Humidity", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-env_wind_speed",   "define Environmental Wind Speed", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-env_wind_dir",     "define Environmental Wind Direction", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-teltemp_m1",       "define Telescope Temperature : M1", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-teltemp_m1cell",   "define Telescope Temperature : M1 Cell", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-teltemp_m2",       "define Telescope Temperature : M2", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-teltemp_spider",   "define Telescope Temperature : Spider", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-teltemp_truss",    "define Telescope Temperature : Truss", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-teltemp_extra",    "define Telescope Temperature : Extra", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-pon_time",         "define time to last Power On", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-user_1",           "define user statistic (1)", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-user_2",           "define user statistic (2)", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-user_3",           "define user statistic (3)", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-user_4",           "define user statistic (4)", NAN);
+    ADD_OPT(F64,  addprocessedexpArgs, "-user_5",           "define user statistic (5)", NAN);
+    ADD_OPT(Str,  addprocessedexpArgs, "-object",           "define exposure object", NULL);
+    ADD_OPT(F32,  addprocessedexpArgs, "-sun_angle",        "define angle to sun",             NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-sun_alt",          "define sun altitude (neg = below horizon)", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-moon_angle",       "define angle to moon",             NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-moon_alt",         "define moon altitude (neg = below horizon)", NAN);
+    ADD_OPT(F32,  addprocessedexpArgs, "-moon_phase",       "define moon phase (0.0 = new)",   NAN);
+    ADD_OPT(Str,  addprocessedexpArgs, "-label",            "define label for chip stage (non-detrend data only)", NULL);
+    ADD_OPT(Str,  addprocessedexpArgs, "-hostname",         "define host name", NULL);
+    ADD_OPT(S16,  addprocessedexpArgs, "-code",             "set fault code", 0);
+
+    // -processedexp
+    psMetadata *processedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(processedexpArgs,  PS_LIST_TAIL, "-exp_id",        0,            "search by exposure ID", 0);
+    psMetadataAddStr(processedexpArgs,  PS_LIST_TAIL, "-exp_name",      0,            "search by exp_name", NULL);
+    psMetadataAddStr(processedexpArgs,  PS_LIST_TAIL, "-inst",          0,            "search for camera", NULL);
+    psMetadataAddStr(processedexpArgs,  PS_LIST_TAIL, "-telescope",     0,            "search for telescope", NULL);
+    psMetadataAddTime(processedexpArgs, PS_LIST_TAIL, "-dateobs_begin", 0,            "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(processedexpArgs, PS_LIST_TAIL, "-dateobs_end",   0,            "search for exposures by time (<)", NULL);
+    psMetadataAddStr(processedexpArgs,  PS_LIST_TAIL, "-exp_tag",       0,            "search by exp_tag", NULL);
+    psMetadataAddStr(processedexpArgs,  PS_LIST_TAIL, "-exp_type",      0,            "search by exp_type", "object");
+    psMetadataAddStr(processedexpArgs,  PS_LIST_TAIL, "-filelevel",     0,            "search by filelevel", NULL);
+    psMetadataAddStr(processedexpArgs,  PS_LIST_TAIL, "-reduction",     0,            "search by reduction class", NULL);
+    psMetadataAddStr(processedexpArgs,  PS_LIST_TAIL, "-filter",        0,            "search for filter", NULL);
+    psMetadataAddF32(processedexpArgs,  PS_LIST_TAIL, "-airmass_min",   0,            "search by min airmass", NAN);
+    psMetadataAddF32(processedexpArgs,  PS_LIST_TAIL, "-airmass_max",   0,            "search by max airmass", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-ra_min",        0,            "search by min", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-ra_max",        0,            "search by max", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-decl_min",      0,            "search by min", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-decl_max",      0,            "search by max", NAN);
+    psMetadataAddF32(processedexpArgs,  PS_LIST_TAIL, "-exp_time_min",  0,            "search by min", NAN);
+    psMetadataAddF32(processedexpArgs,  PS_LIST_TAIL, "-exp_time_max",  0,            "search by max", NAN);
+    psMetadataAddF32(processedexpArgs,  PS_LIST_TAIL, "-sat_pixel_frac_min",  0,      "search by max fraction of saturated pixels", NAN);
+    psMetadataAddF32(processedexpArgs,  PS_LIST_TAIL, "-sat_pixel_frac_max",  0,      "search by min fraction of saturated pixels", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-bg_min",        0,            "search by max", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-bg_max",        0,            "search by max", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-bg_stdev_min",  0,            "search by max", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-bg_stdev_max",  0,            "search by max", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-bg_mean_stdev_min",  0,       "search by max", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-bg_mean_stdev_max",  0,       "search by max", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-alt_min",       0,            "search by min", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-alt_max",       0,            "search by max", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-az_min",        0,            "search by min", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-az_max",        0,            "search by max", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-ccd_temp_min",  0,            "search by min ccd tempature", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-ccd_temp_max",  0,            "search by max ccd tempature", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-posang_min",    0,            "search by min rotator position angle", NAN);
+    psMetadataAddF64(processedexpArgs,  PS_LIST_TAIL, "-posang_max",    0,            "search by max rotator position angle", NAN);
+    psMetadataAddStr(processedexpArgs,  PS_LIST_TAIL, "-object",        0,            "search by exposure object", NULL);
+    psMetadataAddF32(processedexpArgs,  PS_LIST_TAIL, "-solang_min",    0,            "define min solar angle", NAN);
+    psMetadataAddF32(processedexpArgs,  PS_LIST_TAIL, "-solang_max",    0,            "define max solar angle", NAN);
+
+    psMetadataAddU64(processedexpArgs,  PS_LIST_TAIL, "-limit",         0,            "limit result set to N items", 0);
+    psMetadataAddBool(processedexpArgs, PS_LIST_TAIL, "-faulted",       0,            "only return imfiles with a fault status set", false);
+    psMetadataAddBool(processedexpArgs, PS_LIST_TAIL, "-simple",        0,            "use the simple output format", false);
+
+    // -revertprocessedexp
+    psMetadata *revertprocessedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(revertprocessedexpArgs, PS_LIST_TAIL, "-exp_id",   0,            "search by exposure ID (required)", 0);
+    psMetadataAddS16(revertprocessedexpArgs, PS_LIST_TAIL, "-code",     0,            "search by fault code", 0);
+
+    // -updatedprocessedexp
+    psMetadata *updatedprocessedexpArgs = psMetadataAlloc();
+    psMetadataAddS64(updatedprocessedexpArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exposure ID", 0);
+    psMetadataAddS16(updatedprocessedexpArgs, PS_LIST_TAIL, "-code",    0,            "set fault code (required)", INT16_MAX);
+
+    // -cleardupexp
+    psMetadata *cleardupexpArgs = psMetadataAlloc();
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-pendingimfile",           "", REGTOOL_MODE_PENDINGIMFILE, pendingimfileArgs);
+    PXOPT_ADD_MODE("-addprocessedimfile",      "", REGTOOL_MODE_ADDPROCESSEDIMFILE, addprocessedimfileArgs);
+    PXOPT_ADD_MODE("-processedimfile",         "", REGTOOL_MODE_PROCESSEDIMFILE, processedimfileArgs);
+    PXOPT_ADD_MODE("-revertprocessedimfile",   "", REGTOOL_MODE_REVERTPROCESSEDIMFILE, revertprocessedimfileArgs);
+    PXOPT_ADD_MODE("-updateprocessedimfile",   "", REGTOOL_MODE_UPDATEPROCESSEDIMFILE, updateprocessedimfileArgs);
+    PXOPT_ADD_MODE("-pendingexp",              "", REGTOOL_MODE_PENDINGEXP,pendingexpArgs);
+    PXOPT_ADD_MODE("-addprocessedexp",         "", REGTOOL_MODE_ADDPROCESSEDEXP, addprocessedexpArgs);
+    PXOPT_ADD_MODE("-processedexp",            "", REGTOOL_MODE_PROCESSEDEXP, processedexpArgs);
+    PXOPT_ADD_MODE("-revertprocessedexp",      "", REGTOOL_MODE_REVERTPROCESSEDEXP, revertprocessedexpArgs);
+    PXOPT_ADD_MODE("-updateprocessedexp",      "", REGTOOL_MODE_UPDATEPROCESSEDEXP,      updatedprocessedexpArgs);
+    PXOPT_ADD_MODE("-cleardupexp",             "", REGTOOL_MODE_CLEARDUPEXP,      cleardupexpArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PXTOOLS_ERR_SYS, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/stacktool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/stacktool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/stacktool.c	(revision 22322)
@@ -0,0 +1,1179 @@
+/*
+ * stacktool.c
+ *
+ * Copyright (C) 2007-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ippdb.h>
+
+#include "pxtools.h"
+#include "stacktool.h"
+
+static bool definebyqueryMode(pxConfig *config);
+static bool definerunMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool addinputskyfileMode(pxConfig *config);
+static bool inputskyfileMode(pxConfig *config);
+static bool tosumMode(pxConfig *config);
+static bool addsumskyfileMode(pxConfig *config);
+static bool sumskyfileMode(pxConfig *config);
+static bool revertsumskyfileMode(pxConfig *config);
+static bool pendingcleanuprunMode(pxConfig *config);
+static bool pendingcleanupskyfileMode(pxConfig *config);
+static bool donecleanupMode(pxConfig *config);
+static bool updatesumskyfileMode(pxConfig *config);
+
+static bool setstackRunState(pxConfig *config, psS64 stack_id, const char *state);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = stacktoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(STACKTOOL_MODE_DEFINEBYQUERY,         definebyqueryMode);
+        MODECASE(STACKTOOL_MODE_DEFINERUN,             definerunMode);
+        MODECASE(STACKTOOL_MODE_UPDATERUN,             updaterunMode);
+        MODECASE(STACKTOOL_MODE_ADDINPUTSKYFILE,       addinputskyfileMode);
+        MODECASE(STACKTOOL_MODE_INPUTSKYFILE,          inputskyfileMode);
+        MODECASE(STACKTOOL_MODE_TOSUM,                 tosumMode);
+        MODECASE(STACKTOOL_MODE_ADDSUMSKYFILE,         addsumskyfileMode);
+        MODECASE(STACKTOOL_MODE_SUMSKYFILE,            sumskyfileMode);
+        MODECASE(STACKTOOL_MODE_REVERTSUMSKYFILE,      revertsumskyfileMode);
+        MODECASE(STACKTOOL_MODE_PENDINGCLEANUPRUN,     pendingcleanuprunMode);
+        MODECASE(STACKTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupskyfileMode);
+        MODECASE(STACKTOOL_MODE_DONECLEANUP,           donecleanupMode);
+        MODECASE(STACKTOOL_MODE_UPDATESUMSKYFILE,      updatesumskyfileMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required options
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-reduction", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-dvodb", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+    psMetadata *having = psMetadataAlloc(); // HAVING clause
+
+    // select based on properties of the raw exposures
+    PXOPT_COPY_STR(config->args,  where, "-select_inst",               "rawExp.camera", "==");
+    PXOPT_COPY_STR(config->args,  where, "-select_telescope",          "rawExp.telescope", "==");
+    PXOPT_COPY_STR(config->args,  where, "-select_filter",             "rawExp.filter", "==");
+    PXOPT_COPY_STR(config->args,  where, "-select_uri",                "rawExp.uri", "==");
+    PXOPT_COPY_TIME(config->args, where, "-select_dateobs_begin",      "rawExp.dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, where, "-select_dateobs_end",        "rawExp.dateobs", "<=");
+    PXOPT_COPY_F32(config->args,  where, "-select_airmass_min",        "rawExp.airmass", ">=");
+    PXOPT_COPY_F32(config->args,  where, "-select_airmass_max",        "rawExp.airmass", "<=");
+    PXOPT_COPY_F32(config->args,  where, "-select_sat_pixel_frac_max", "rawExp.sat_pixel_frac", "<=");
+    PXOPT_COPY_F32(config->args,  where, "-select_exp_time_min",       "rawExp.exp_time", ">=");
+    PXOPT_COPY_F32(config->args,  where, "-select_exp_time_max",       "rawExp.exp_time", "<=");
+    PXOPT_COPY_F32(config->args,  where, "-select_ccd_temp_min",       "rawExp.ccd_temp", ">=");
+    PXOPT_COPY_F32(config->args,  where, "-select_ccd_temp_max",       "rawExp.ccd_temp", "<=");
+    PXOPT_COPY_F32(config->args,  where, "-select_posang_min",         "rawExp.posang", ">=");
+    PXOPT_COPY_F32(config->args,  where, "-select_posang_max",         "rawExp.posang", "<=");
+    PXOPT_COPY_F32(config->args,  where, "-select_solang_min",         "rawExp.solang", ">=");
+    PXOPT_COPY_F32(config->args,  where, "-select_solang_max",         "rawExp.solang", "<=");
+    PXOPT_COPY_STR(config->args,  where, "-select_exp_type",           "rawExp.exp_type", "==");
+    PXOPT_COPY_F32(config->args,  where, "-select_good_frac_min",      "warpSkyfile.good_frac", ">=");
+    PXOPT_COPY_STR(config->args,  where, "-select_skycell_id",         "warpSkyfile.skycell_id", "==");
+    PXOPT_COPY_STR(config->args,  where, "-select_label",              "warpRun.label", "==");
+
+    // these are used to build the HAVING restriction
+    PXOPT_COPY_S32(config->args, having, "-min_num", "num_warp", ">=");
+
+    // other options applied outside of the WHERE
+    PXOPT_LOOKUP_S32(randomLimit, config->args, "-random",   false, false);
+    PXOPT_LOOKUP_S32(min_new,     config->args, "-min_new",  false, false);
+    PXOPT_LOOKUP_F32(min_frac,    config->args, "-min_frac", false, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+
+    if (!psListLength(where->list) &&
+        !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    psString select = pxDataGet("stacktool_definebyquery_part1.sql");
+    if (!select) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&select, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    psString groupby = pxDataGet("stacktool_definebyquery_part2.sql");
+    if (!groupby) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+    psStringAppend(&select, " %s", groupby);
+    psFree(groupby);
+
+    // Restriction on aggregated quantities using HAVING
+    {
+        psString havingClause = NULL;   // HAVING string
+        if (psListLength(having->list)) {
+            havingClause = psDBGenerateWhereConditionSQL(having, NULL);
+        }
+
+        if (min_new > 0) {
+            if (havingClause) {
+                psStringAppend(&havingClause, " AND");
+            }
+            psStringAppend(&havingClause,
+                           " (num_warp - num_stack >= %d OR (num_warp >= %d AND num_stack IS NULL))",
+                           min_new, min_new);
+        }
+        if (isfinite(min_frac)) {
+            if (havingClause) {
+                psStringAppend(&havingClause, " AND");
+            }
+            // Avoiding division by zero
+            psStringAppend(&havingClause, " (num_warp >= %f * num_stack OR num_stack IS NULL)",
+                           (double)min_frac);
+        }
+        if (havingClause) {
+            psStringAppend(&select, " HAVING %s", havingClause);
+            psFree(havingClause);
+        }
+    }
+    psFree(having);
+
+    if (!p_psDBRunQuery(config->dbh, select)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(select);
+        psFree(where);
+        return false;
+    }
+    psFree(select);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+        psFree(where);
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        psFree(where);
+        return true;
+    }
+
+    psString insert = NULL;             // Insertion query
+    if (randomLimit > 0) {
+        insert = pxDataGet("stacktool_definebyquery_insert_random_part1.sql");
+    } else {
+        insert = pxDataGet("stacktool_definebyquery_insert.sql");
+    }
+    if (!insert) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&insert, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (randomLimit > 0) {
+        psString part2 = pxDataGet("stacktool_definebyquery_insert_random_part2.sql");
+        if (!part2) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            psFree(insert);
+            return false;
+        }
+        psStringAppend(&insert, "%s", part2);
+        psFree(part2);
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(where);
+        return false;
+    }
+
+    psArray *list = psArrayAllocEmpty(16); // List of runs, to print
+    for (long i = 0; i < output->n; i++) {
+        psMetadata *row = output->data[i]; // Row from select
+        bool status;
+
+        // pull out the skycell_id, tess_id, filter
+        psString skycell_id = psMetadataLookupStr(&status, row, "skycell_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup skycell_id");
+            psFree(output);
+            psFree(insert);
+            psFree(list);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        psString tess_id = psMetadataLookupStr(&status, row, "tess_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup tess_id");
+            psFree(output);
+            psFree(insert);
+            psFree(list);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        psString filter = psMetadataLookupStr(&status, row, "filter");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup filter");
+            psFree(output);
+            psFree(insert);
+            psFree(list);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        // create a new stackRun for this stack
+        stackRunRow *run = stackRunRowAlloc(
+            0,                          // ID
+            "new",                      // state
+            workdir,
+            label,
+            reduction,
+            dvodb,
+            registered,
+            skycell_id,
+            tess_id,
+            filter);
+
+        if (!stackRunInsertObject(config->dbh, run)) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(output);
+            psFree(run);
+            psFree(insert);
+            psFree(list);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        // figure out the new stack_id
+        psS64 stack_id = psDBLastInsertID(config->dbh);
+        run->stack_id = stack_id;
+
+        psArrayAdd(list, list->n, run);
+        psFree(run);
+
+        // Create a suitable insertion query for this run
+        psString thisInsert = psStringCopy(insert);
+        psString idString = NULL;
+        psStringAppend(&idString, "%" PRId64, stack_id);
+        psStringSubstitute(&thisInsert, idString, "@STACK_ID@");
+        psFree(idString);
+
+        // XXX this insert uses a select to generate the list of warp_ids for the stack,
+        // we have applied a set of criteria above (WHERE) to select the relevant warps
+        // this insert needs to use exactly the same restrictions (race condition is probably not critical)
+        // the insert below seems to only restrict matches to the skycell, tess, and filter
+        if ((randomLimit > 0 && !p_psDBRunQuery(config->dbh, thisInsert, skycell_id, filter, randomLimit)) ||
+            (randomLimit <= 0 && !p_psDBRunQuery(config->dbh, thisInsert, skycell_id, filter))) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(thisInsert);
+            psFree(insert);
+            psFree(output);
+            psFree(list);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+        psFree(thisInsert);
+    }
+    psFree(output);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(list);
+        return false;
+    }
+
+    if (!stackRunPrintObjects(stdout, list, !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print object");
+        psFree(list);
+        return false;
+    }
+    psFree(list);
+    return true;
+}
+
+
+static bool definerunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required options
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+    PXOPT_LOOKUP_STR(skycell_id, config->args, "-skycell_id", true, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-tess_id", true, false);
+    PXOPT_LOOKUP_STR(filter, config->args, "-filter", true, false);
+
+    // default
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+
+    // options
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-reduction", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-dvodb", false, false);
+
+    // we have to support multipe exp_ids
+    psMetadataItem *warp_ids = psMetadataLookup(config->args, "-warp_id");
+    if (!warp_ids) {
+        // this shouldn't actually happen when using psArgs
+        psError(PS_ERR_UNKNOWN, true, "-warp_id is required");
+        return false;
+    }
+
+    stackRunRow *run = stackRunRowAlloc(
+        0,                              // ID
+        "new",                          // state
+        workdir,
+        label,
+        reduction,
+        dvodb,
+        registered,
+        skycell_id,
+        tess_id,
+        filter);
+
+    if (!run) {
+        psError(PS_ERR_UNKNOWN, false, "failed to alloc stackRun object");
+        return true;
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!stackRunInsertObject(config->dbh, run)) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(run);
+        return true;
+    }
+
+    // get the assigned warp_id
+    run->stack_id = psDBLastInsertID(config->dbh);
+
+    // insert the stackInputSkyfile rows
+    psListIterator *iter = psListIteratorAlloc(warp_ids->data.list, 0, false);
+    psMetadataItem *item = NULL;
+    while ((item = psListGetAndIncrement(iter))) {
+        // if the value is NULL this is probably the first pass through the
+        // loop and -warp_id was not specified at all
+        if (!item->data.V) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -warp_id");
+            return false;
+        }
+        if (!stackInputSkyfileInsert(config->dbh, run->stack_id, item->data.S64)) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to insert stackInputSkyfile rows");
+            return false;
+        }
+    }
+    psFree(iter);
+
+    // point of no return
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!stackRunPrintObject(stdout, run, !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print object");
+            psFree(run);
+            return false;
+    }
+
+    psFree(run);
+
+    return true;
+}
+
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(stack_id, config->args, "-stack_id", true, false);
+    PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
+
+    if (state) {
+        // set detRun.state to state
+        return setstackRunState(config, stack_id, state);
+    }
+
+    return true;
+}
+
+
+static bool addinputskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(stack_id, config->args, "-stack_id", true, false);
+    PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", true, false);
+
+    // XXX need to validate the warp_id here
+    // XXX instead of validiting it here we should just use forgein key
+    // constrants
+    if (!stackInputSkyfileInsert(config->dbh,
+            stack_id,
+            warp_id
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool inputskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // XXX require at least a stack id (add better search options)
+    PXOPT_LOOKUP_S64(stack_id, config->args, "-stack_id", true, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-stack_id", "stack_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("stacktool_inputskyfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "stackInputSkyfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "stackInputSkyfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool tosumMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-stack_id", "stack_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "stackRun.label", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("stacktool_tosum.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "stackSumSkyfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addsumskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(stack_id, config->args, "-stack_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F32(dtime_stack, config->args, "-dtime_stack", false, false);
+    PXOPT_LOOKUP_F32(dtime_match_mean, config->args, "-dtime_match_mean", false, false);
+    PXOPT_LOOKUP_F32(dtime_match_stdev, config->args, "-dtime_match_stdev", false, false);
+    PXOPT_LOOKUP_F32(dtime_initial, config->args, "-dtime_initial", false, false);
+    PXOPT_LOOKUP_F32(dtime_reject, config->args, "-dtime_reject", false, false);
+    PXOPT_LOOKUP_F32(dtime_final, config->args, "-dtime_final", false, false);
+    PXOPT_LOOKUP_F32(dtime_phot, config->args, "-dtime_phot", false, false);
+    PXOPT_LOOKUP_F32(match_mean, config->args, "-match_mean", false, false);
+    PXOPT_LOOKUP_F32(match_stdev, config->args, "-match_stdev", false, false);
+    PXOPT_LOOKUP_F32(match_rms, config->args, "-match_rms", false, false);
+    PXOPT_LOOKUP_F32(stamps_mean, config->args, "-stamps_mean", false, false);
+    PXOPT_LOOKUP_F32(stamps_stdev, config->args, "-stamps_stdev", false, false);
+    PXOPT_LOOKUP_S32(stamps_min, config->args, "-stamps_min", false, false);
+    PXOPT_LOOKUP_S32(reject_images, config->args, "-reject_images", false, false);
+    PXOPT_LOOKUP_F32(reject_pix_mean, config->args, "-reject_pix_mean", false, false);
+    PXOPT_LOOKUP_F32(reject_pix_stdev, config->args, "-reject_pix_stdev", false, false);
+    PXOPT_LOOKUP_S32(sources, config->args, "-sources", false, false);
+    PXOPT_LOOKUP_STR(hostname, config->args, "-hostname", false, false);
+    PXOPT_LOOKUP_F32(good_frac, config->args, "-good_frac", false, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // XXX need to validate the stack_id here
+    // XXX instead of validiting it here we should just use forgein key
+    // constrants
+    if (!stackSumSkyfileInsert(config->dbh,
+                               stack_id,
+                               uri,
+                               path_base,
+                               bg,
+                               bg_stdev,
+                               dtime_stack,
+                               dtime_match_mean,
+                               dtime_match_stdev,
+                               dtime_initial,
+                               dtime_reject,
+                               dtime_final,
+                               dtime_phot,
+                               match_mean,
+                               match_stdev,
+                               match_rms,
+                               stamps_mean,
+                               stamps_stdev,
+                               stamps_min,
+                               reject_images,
+                               reject_pix_mean,
+                               reject_pix_stdev,
+                               sources,
+                               hostname,
+                               good_frac,
+                               code
+          )) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!setstackRunState(config, stack_id, "full")) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "failed to change stackRun's state");
+        return false;
+    }
+
+    // point of no return
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool sumskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warpRun.warp_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-stack_id", "stackSumSkyfile.stack_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "rawExp.exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_name", "rawExp.exp_name", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("stacktool_sumskyfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        if (!ippdbPrintMetadatas(stdout, output, "stackSumSkyfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertsumskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-stack_id", "stackSumSkyfile.stack_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "stackRun.label", "==");
+    PXOPT_COPY_S16(config->args, where, "-code", "stackSumSkyfile.fault", "==");
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(where);
+        return false;
+    }
+
+    int numStacks;                      // Number of stacks affected
+
+    // Update state to 'new'
+    {
+        psString update = pxDataGet("stacktool_revertsumskyfile_update.sql");
+        if (!update) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&update, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, update)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(update);
+            psFree(where);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+        psFree(update);
+
+        numStacks = psDBAffectedRows(config->dbh);
+
+        if (numStacks < 1) {
+            psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+    }
+
+    // Delete product
+    {
+        psString delete = pxDataGet("stacktool_revertsumskyfile_delete.sql");
+        if (!delete) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&delete, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, delete)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(delete);
+            psFree(where);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+        psFree(delete);
+
+        if (psDBAffectedRows(config->dbh) != numStacks) {
+            psError(PS_ERR_UNKNOWN, true, "Updated and deleted different number of entries!");
+            psFree(where);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(where);
+
+    return true;
+}
+
+
+static bool setstackRunState(pxConfig *config, psS64 stack_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid stackRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE stackRun SET state = '%s' WHERE stack_id = %"PRId64;
+    if (!p_psDBRunQuery(config->dbh, query, state, stack_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for stack_id %"PRId64, stack_id);
+        return false;
+    }
+
+    return true;
+}
+
+static bool pendingcleanuprunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("stacktool_pendingcleanuprun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "stackPendingCleanupRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool pendingcleanupskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(stack_id, config->args, "-stack_id", false, false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    if (stack_id) {
+        PXOPT_COPY_S64(config->args, where, "-stack_id", "stack_id", "==");
+    }
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("stacktool_pendingcleanupskyfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "stackPendingCleanupSkyfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool donecleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("stacktool_donecleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "stackDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+static bool updatesumskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", true, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-stack_id",   "stack_id",   "==");
+
+    if (!pxSetFaultCode(config->dbh, "stackSumSkyfile", where, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        psFree (where);
+        return false;
+    }
+    psFree (where);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/stacktool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/stacktool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/stacktool.h	(revision 22322)
@@ -0,0 +1,44 @@
+/*
+ * stacktool.h
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef STACKTOOL_H
+#define STACKTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    STACKTOOL_MODE_NONE           = 0x0,
+    STACKTOOL_MODE_DEFINEBYQUERY,
+    STACKTOOL_MODE_DEFINERUN,
+    STACKTOOL_MODE_UPDATERUN,
+    STACKTOOL_MODE_ADDINPUTSKYFILE,
+    STACKTOOL_MODE_INPUTSKYFILE,
+    STACKTOOL_MODE_TOSUM,
+    STACKTOOL_MODE_ADDSUMSKYFILE,
+    STACKTOOL_MODE_SUMSKYFILE,
+    STACKTOOL_MODE_REVERTSUMSKYFILE,
+    STACKTOOL_MODE_PENDINGCLEANUPRUN,
+    STACKTOOL_MODE_PENDINGCLEANUPSKYFILE,
+    STACKTOOL_MODE_DONECLEANUP,
+    STACKTOOL_MODE_UPDATESUMSKYFILE,
+} stacktoolMode;
+
+pxConfig *stacktoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // STACKTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/stacktoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/stacktoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/stacktoolConfig.c	(revision 22322)
@@ -0,0 +1,229 @@
+/*
+ * stacktoolConfig.c
+ *
+ * Copyright (C) 2007-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "stacktool.h"
+
+pxConfig *stacktoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    psTime *now = psTimeGetNow(PS_TIME_TAI);
+
+    // -definebyquery
+    psMetadata *definebyqueryArgs = psMetadataAlloc();
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-workdir", 0, "define workdir (required)", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label", 0, "define label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-reduction", 0, "define reduction", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-dvodb", 0, "define dvodb", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-registered", 0, "time detrend run was registered", now);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_skycell_id", 0, "search for skycell_id", NULL);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_good_frac_min", 0, "define min good_frac", 0.0);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_exp_type", 0, "search for exp_type", "object");
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_label", 0, "search for label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_inst", 0, "search for camera", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_telescope", 0, "search for telescope", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_filter", 0, "search for filter", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-select_uri", 0, "search for uri", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-select_dateobs_begin", 0, "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-select_dateobs_end", 0, "search for exposures by time (<)", NULL);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_airmass_min", 0, "define min airmass", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_airmass_max", 0, "define max airmass", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_sat_pixel_frac_max", 0, "define max fraction of saturated pixels", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_exp_time_min", 0, "define min exposure time", NAN);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-select_exp_time_max", 0, "define max exposure time", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_ccd_temp_min", 0, "define min ccd tempature", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_ccd_temp_max", 0, "define max ccd tempature", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_posang_min", 0, "define min rotator position angle", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_posang_max", 0, "define max rotator position angle", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_solang_min", 0, "define min solar angle", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_solang_max", 0, "define max solar angle", NAN);
+    psMetadataAddS32(definebyqueryArgs, PS_LIST_TAIL, "-random", 0, "use this number of random elements", 0);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-all", 0, "allow everything to be queued without search terms", false);
+    psMetadataAddS32(definebyqueryArgs, PS_LIST_TAIL, "-min_num", 0, "minimum number of inputs", 0);
+    psMetadataAddS32(definebyqueryArgs, PS_LIST_TAIL, "-min_new", 0, "minimum number of new inputs", 0);
+    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-min_frac", 0, "minumum fraction of new inputs", NAN);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -definerun
+    psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-workdir", 0,            "define workdir (required)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-label", 0, "define label", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-reduction", 0, "define reduction", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-dvodb", 0, "define dvodb", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-registered",  0,            "time detrend run was registered", now);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-skycell_id",  0,            "define skycell ID (required)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-tess_id",  0,            "define tessellation ID (required)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-filter", 0, "define filter (required)", NULL);
+    psMetadataAddS64(definerunArgs, PS_LIST_TAIL, "-warp_id",             PS_META_DUPLICATE_OK,             "include this warp ID (multiple OK, required)", 0);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-stack_id", 0,            "define stack ID (required)", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0,            "set state (required)", NULL);
+
+    // -addinputskyfile
+    psMetadata *addinputskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addinputskyfileArgs, PS_LIST_TAIL, "-stack_id", 0,            "define stack ID (required)", 0);
+    psMetadataAddS64(addinputskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,            "define warp ID (required)", 0);
+
+    // -inputskyfile
+    // XXX add additional search terms
+    psMetadata *inputskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(inputskyfileArgs, PS_LIST_TAIL, "-stack_id", 0,            "search by stack ID", 0);
+    psMetadataAddS64(inputskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warp ID", 0);
+    psMetadataAddU64(inputskyfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(inputskyfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -tosum
+    psMetadata *tosumArgs = psMetadataAlloc();
+    psMetadataAddS64(tosumArgs, PS_LIST_TAIL, "-stack_id", 0,            "search by stack ID", 0);
+    psMetadataAddStr(tosumArgs, PS_LIST_TAIL, "-label", 0, "search by label", NULL);
+    psMetadataAddS64(tosumArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warp ID", 0);
+    psMetadataAddU64(tosumArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tosumArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addsumskyfile
+    psMetadata *addsumskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addsumskyfileArgs, PS_LIST_TAIL, "-stack_id", 0,            "define stack ID (required)", 0);
+    psMetadataAddStr(addsumskyfileArgs, PS_LIST_TAIL, "-uri", 0,            "define URI of file", 0);
+    psMetadataAddStr(addsumskyfileArgs, PS_LIST_TAIL, "-path_base", 0,            "define base output location", 0);
+    psMetadataAddF64(addsumskyfileArgs, PS_LIST_TAIL, "-bg",  0,            "define exposue background", NAN);
+    psMetadataAddF64(addsumskyfileArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposue background mean stdev", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-dtime_stack",  0,            "define elapsed processing time", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-dtime_match_mean", 0, "define mean matching time", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-dtime_match_stdev", 0, "define stdev matching time", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-dtime_initial", 0, "define initial stack time", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-dtime_reject", 0, "define rejection time", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-dtime_final", 0, "define final stack time", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-dtime_phot", 0, "define photometry time", NAN);
+
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-match_mean", 0, "define mean matching deviation", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-match_stdev", 0, "define stdev matching deviation", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-match_rms", 0, "define mean rms of deviation", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-stamps_mean", 0, "define mean number of stamps", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-stamps_stdev", 0, "define stdev of number of stamps", NAN);
+    psMetadataAddS32(addsumskyfileArgs, PS_LIST_TAIL, "-stamps_min", 0, "define minimum number of stamps", 0);
+    psMetadataAddS32(addsumskyfileArgs, PS_LIST_TAIL, "-reject_images", 0, "number of images rejected", 0);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-reject_pix_mean", 0, "mean number of pixels rejected", NAN);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-reject_pix_stdev", 0, "stdev number of pixels rejected", NAN);
+    psMetadataAddS32(addsumskyfileArgs, PS_LIST_TAIL, "-sources", 0, "number of sources", 0);
+    psMetadataAddStr(addsumskyfileArgs, PS_LIST_TAIL, "-hostname", 0,            "define hostname", 0);
+    psMetadataAddF32(addsumskyfileArgs, PS_LIST_TAIL, "-good_frac",  0,            "define %% of good pixels", NAN);
+    psMetadataAddS16(addsumskyfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+
+    // -sumskyfile
+    psMetadata *sumskyfileArgs= psMetadataAlloc();
+    psMetadataAddS64(sumskyfileArgs, PS_LIST_TAIL, "-stack_id", 0,            "search by stack ID", 0);
+    psMetadataAddS64(sumskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warp ID", 0);
+    psMetadataAddS64(sumskyfileArgs, PS_LIST_TAIL, "-exp_id", 0,            "search by exposure ID", 0);
+    psMetadataAddStr(sumskyfileArgs, PS_LIST_TAIL, "-exp_name", 0,          "search by exposure name", NULL);
+    psMetadataAddU64(sumskyfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(sumskyfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -revertsumskyfile
+    psMetadata *revertsumskyfileArgs= psMetadataAlloc();
+    psMetadataAddS64(revertsumskyfileArgs, PS_LIST_TAIL, "-stack_id", 0,            "search by stack ID", 0);
+    psMetadataAddStr(revertsumskyfileArgs, PS_LIST_TAIL, "-label", 0, "search by label", 0);
+    psMetadataAddS16(revertsumskyfileArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
+
+    // -pendingcleanuprun
+    psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanuprunArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(pendingcleanuprunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanuprunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -pendingcleanupskyfile
+    psMetadata *pendingcleanupskyfileArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanupskyfileArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddS64(pendingcleanupskyfileArgs, PS_LIST_TAIL, "-stack_id", 0,          "search by stack ID", 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);
+
+    // -donecleanup
+    psMetadata *donecleanupArgs = psMetadataAlloc();
+    psMetadataAddStr(donecleanupArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(donecleanupArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanupArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    // -updatesumskyfile
+    psMetadata *updatesumskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(updatesumskyfileArgs, PS_LIST_TAIL, "-stack_id", 0,            "define stack ID (required)", 0);
+    psMetadataAddS16(updatesumskyfileArgs, PS_LIST_TAIL, "-code", 0,            "set fault code (required)", 0);
+
+    psFree(now);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes   = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definebyquery", "Define a new stackRun by searching for warp IDs", STACKTOOL_MODE_DEFINEBYQUERY,  definebyqueryArgs);
+    PXOPT_ADD_MODE("-definerun",       "", STACKTOOL_MODE_DEFINERUN,      definerunArgs);
+    PXOPT_ADD_MODE("-updaterun",       "", STACKTOOL_MODE_UPDATERUN,      updaterunArgs);
+    PXOPT_ADD_MODE("-addinputskyfile", "", STACKTOOL_MODE_ADDINPUTSKYFILE, addinputskyfileArgs);
+    PXOPT_ADD_MODE("-inputskyfile",    "", STACKTOOL_MODE_INPUTSKYFILE,    inputskyfileArgs);
+    PXOPT_ADD_MODE("-tosum",           "", STACKTOOL_MODE_TOSUM,          tosumArgs);
+    PXOPT_ADD_MODE("-addsumskyfile",   "", STACKTOOL_MODE_ADDSUMSKYFILE,   addsumskyfileArgs);
+    PXOPT_ADD_MODE("-sumskyfile",      "list results of stackRun", STACKTOOL_MODE_SUMSKYFILE,      sumskyfileArgs);
+    PXOPT_ADD_MODE("-revertsumskyfile","", STACKTOOL_MODE_REVERTSUMSKYFILE,      revertsumskyfileArgs);
+    PXOPT_ADD_MODE("-pendingcleanuprun",     "show runs that need to be cleaned up", STACKTOOL_MODE_PENDINGCLEANUPRUN,    pendingcleanuprunArgs);
+    PXOPT_ADD_MODE("-pendingcleanupskyfile", "show runs that need to be cleaned up", STACKTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupskyfileArgs);
+    PXOPT_ADD_MODE("-donecleanup",           "show runs that have been cleaned",     STACKTOOL_MODE_DONECLEANUP,          donecleanupArgs);
+    PXOPT_ADD_MODE("-updatesumskyfile",      "update fault code for sumskyfile",     STACKTOOL_MODE_UPDATESUMSKYFILE,          updatesumskyfileArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/warptool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/warptool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/warptool.c	(revision 22322)
@@ -0,0 +1,1593 @@
+/*
+ * warptool.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ippdb.h>
+
+#include "pxtools.h"
+#include "warptool.h"
+#include "pxwarp.h"
+
+static psS64 definerunMode(pxConfig *config);
+static bool definebyqueryMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool expMode(pxConfig *config);
+static bool imfileMode(pxConfig *config);
+static bool tooverlapMode(pxConfig *config);
+static bool addoverlapMode(pxConfig *config);
+static bool scmapMode(pxConfig *config);
+static bool towarpedMode(pxConfig *config);
+static bool addwarpedMode(pxConfig *config);
+static bool warpedMode(pxConfig *config);
+static bool revertwarpedMode(pxConfig *config);
+static bool blockMode(pxConfig *config);
+static bool maskedMode(pxConfig *config);
+static bool unblockMode(pxConfig *config);
+static bool pendingcleanuprunMode(pxConfig *config);
+static bool pendingcleanupwarpMode(pxConfig *config);
+static bool donecleanupMode(pxConfig *config);
+static bool tocleanedskyfileMode(pxConfig *config);
+static bool topurgedskyfileMode(pxConfig *config);
+static bool tofullskyfileMode(pxConfig *config);
+static bool updateskyfileMode(pxConfig *config);
+
+static bool parseAndInsertSkyCellMap(pxConfig *config, const char *mapfile);
+static bool isValidMode(pxConfig *config, const char *mode);
+bool warpCompletedRuns(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = warptoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(WARPTOOL_MODE_DEFINERUN,          definerunMode);
+        MODECASE(WARPTOOL_MODE_DEFINEBYQUERY,      definebyqueryMode);
+        MODECASE(WARPTOOL_MODE_UPDATERUN,          updaterunMode);
+        MODECASE(WARPTOOL_MODE_EXP,                expMode);
+        MODECASE(WARPTOOL_MODE_IMFILE,             imfileMode);
+        MODECASE(WARPTOOL_MODE_TOOVERLAP,          tooverlapMode);
+        MODECASE(WARPTOOL_MODE_ADDOVERLAP,         addoverlapMode);
+        MODECASE(WARPTOOL_MODE_SCMAP,              scmapMode);
+        MODECASE(WARPTOOL_MODE_TOWARPED,           towarpedMode);
+        MODECASE(WARPTOOL_MODE_ADDWARPED,          addwarpedMode);
+        MODECASE(WARPTOOL_MODE_WARPED,             warpedMode);
+        MODECASE(WARPTOOL_MODE_REVERTWARPED,       revertwarpedMode);
+        MODECASE(WARPTOOL_MODE_BLOCK,              blockMode);
+        MODECASE(WARPTOOL_MODE_MASKED,             maskedMode);
+        MODECASE(WARPTOOL_MODE_UNBLOCK,            unblockMode);
+        MODECASE(WARPTOOL_MODE_PENDINGCLEANUPRUN,  pendingcleanuprunMode);
+        MODECASE(WARPTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupwarpMode);
+        MODECASE(WARPTOOL_MODE_DONECLEANUP,        donecleanupMode);
+        MODECASE(WARPTOOL_MODE_TOCLEANEDSKYFILE,   tocleanedskyfileMode);
+        MODECASE(WARPTOOL_MODE_TOPURGEDSKYFILE,    topurgedskyfileMode);
+        MODECASE(WARPTOOL_MODE_TOFULLSKYFILE,      tofullskyfileMode);
+        MODECASE(WARPTOOL_MODE_UPDATESKYFILE,      updateskyfileMode);
+
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+static psS64 definerunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(fake_id, config->args, "-fake_id", true, false); // required
+    PXOPT_LOOKUP_STR(mode, config->args, "-mode", true, false); // required
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false); // required
+    PXOPT_LOOKUP_STR(label, config->args, "-label", 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_TIME(registered, config->args, "-registered", false, false);
+    PXOPT_LOOKUP_BOOL(magiced, config->args, "-magiced", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // check mode
+    if (mode && !isValidMode(config, mode)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid mode");
+        return false;
+    }
+
+    warpRunRow *warpRun = warpRunRowAlloc(
+            0,          // ID
+            fake_id,
+            mode,
+            "new",      // state
+            workdir,
+            "dirty",    // workdir_state
+            label,
+            dvodb,
+            tess_id,
+            end_stage,
+            registered,
+            magiced
+    );
+    if (!warpRun) {
+        psError(PS_ERR_UNKNOWN, false, "failed to alloc warpRun object");
+        return true;
+    }
+    if (!warpRunInsertObject(config->dbh, warpRun)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(warpRun);
+        return true;
+    }
+
+    // get the assigned warp_id
+    psS64 warp_id = psDBLastInsertID(config->dbh);
+    warpRun->warp_id = warp_id;
+
+    if (!warpRunPrintObject(stdout, warpRun, !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print object");
+            psFree(warpRun);
+            return false;
+    }
+
+    psFree(warpRun);
+
+    return warp_id;
+}
+
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    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", "==");
+
+    if (!psListLength(where->list) &&
+        !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(mode, config->args, "-set_mode", true, false); // required
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb", false, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-set_tess_id", false, false);
+    PXOPT_LOOKUP_STR(end_stage, config->args, "-set_end_stage", false, false);
+
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+    PXOPT_LOOKUP_BOOL(magiced, config->args, "-magiced", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // check mode
+    if (mode && !isValidMode(config, mode)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid mode");
+        return false;
+    }
+
+    // find the exp_id of all the exposures that we want to queue up.
+    psString query = pxDataGet("warptool_definebyquery.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, "%s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    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;
+    }
+
+    // would could do this "all in the database" if we didn't want the option
+    // of changing the label/reduction/expgroup/dvodb/etc.  So we're pulling the
+    // data out so we have the option of changing these values or leaving the
+    // old values in place (i.e., passing the values through).
+
+    // loop over our list of fakeRun rows
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+
+        fakeRunRow *row = fakeRunObjectFromMetadata(md);
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into fakeRun");
+            psFree(output);
+            return false;
+        }
+
+        // queue the exp
+        if (!pxwarpQueueByFakeID(config,
+                    row->fake_id,
+                    workdir     ? workdir   : row->workdir,
+                    label       ? label     : row->label,
+                    dvodb       ? dvodb     : row->dvodb,
+                    tess_id     ? tess_id   : row->tess_id,
+                    end_stage   ? end_stage : row->end_stage))
+          {
+            psError(PS_ERR_UNKNOWN, false, "failed to trying to queue fake_id: %" PRId64, row->fake_id);
+            psFree(row);
+            psFree(output);
+            return false;
+        }
+        psFree(row);
+    }
+    psFree(output);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-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", "<");
+
+    if (!psListLength(where->list)
+        && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        where = NULL;
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(state, config->args, "-state", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+
+    if ((!state) && (!label)) {
+        psError(PXTOOLS_ERR_DATA, false, "parameters (-state or -label) are required");
+        psFree(where);
+        return false;
+    }
+
+    if (state) {
+        // set warpRun.state to state
+        if (!pxwarpRunSetStateByQuery(config, where, state)) {
+            psFree(where);
+            return false;
+        }
+    }
+
+    if (label) {
+        // set chipRun.label to label
+        if (!pxwarpRunSetLabelByQuery(config, where, label)) {
+            psFree(where);
+            return false;
+        }
+    }
+
+    psFree(where);
+
+    return true;
+}
+
+
+static bool expMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find all rawImfiles matching the default query
+    psString query = pxDataGet("warptool_exp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "warpRun");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("warptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "warpRun", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool imfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find all rawImfiles matching the default query
+    psString query = pxDataGet("warptool_imfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "warpRun");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("warptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "warpInputImfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool tooverlapMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find all rawImfiles matching the default query
+    psString query = pxDataGet("warptool_tooverlap.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "warpRun");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("warptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "warpRun", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addoverlapMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(mapfile, config->args, "-mapfile", false, false);
+    PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", false, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (code == 0) {
+        if (!parseAndInsertSkyCellMap(config, mapfile)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to inject mapfile: %s into the database", mapfile);
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+    } else {
+        warpSkyCellMapInsert(config->dbh,
+            warp_id,
+            NULL,   // skycell_id
+            NULL,   // tess_id
+            NULL,   // class_id
+            code    // fault
+        );
+    }
+
+    // point of no return
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool parseAndInsertSkyCellMap(pxConfig *config, const char *mapfile)
+{
+    unsigned int nFail = 0;
+    psMetadata *skycells = psMetadataConfigRead(NULL, &nFail, mapfile, false);
+    if (!skycells) {
+        psError(PS_ERR_UNKNOWN, false, "failed to parse mapfile: %s", mapfile);
+        return false;
+    }
+    if (nFail) {
+        psError(PS_ERR_UNKNOWN, false, "there were %d errors parsing mapfile: %s", nFail, mapfile);
+        psFree(skycells);
+        return false;
+    }
+
+    psMetadataItem *item = NULL;
+    psMetadataIterator *iter = psMetadataIteratorAlloc(skycells, 0, NULL);
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (item->type != PS_DATA_METADATA) {
+            psError(PS_ERR_UNKNOWN, false, "mapfile: %s is in the wrong format", mapfile);
+            psFree(iter);
+            psFree(skycells);
+            return false;
+        }
+
+        psMetadata *sc = item->data.md;
+        // this conversion isn't strictly nessicary but it's an easy way of
+        // validating the format
+        warpSkyCellMapRow *row = warpSkyCellMapObjectFromMetadata(sc);
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert mapfile: %s metdata entry into a warpSkyCellMap object", mapfile);
+            psFree(iter);
+            psFree(skycells);
+            return false;
+        }
+
+        if (!warpSkyCellMapInsertObject(config->dbh, row)) {
+            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");
+            }
+            psFree(row);
+            psFree(iter);
+            psFree(skycells);
+            return false;
+        }
+
+        psFree(row);
+    }
+    psFree(iter);
+    psFree(skycells);
+
+    return true;
+}
+
+
+static bool scmapMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "skycell_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-tess_id", "tess_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find all rawImfiles matching the default query
+    psString query = pxDataGet("warptool_scmap.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "warpSkyCellMap");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("warptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "warpSkyCellMap", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool towarpedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warpSkyCellMap.warp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "warpRun.label", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find all rawImfiles matching the default query
+    psString query = pxDataGet("warptool_towarped.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("warptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "warpPendingSkyCell", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addwarpedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", true, false);
+    PXOPT_LOOKUP_STR(skycell_id, config->args, "-skycell_id", true, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-tess_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F32(dtime_warp, config->args, "-dtime_warp", false, false);
+    PXOPT_LOOKUP_S32(xmin, config->args, "-xmin", false, false);
+    PXOPT_LOOKUP_S32(xmax, config->args, "-xmax", false, false);
+    PXOPT_LOOKUP_S32(ymin, config->args, "-ymin", false, false);
+    PXOPT_LOOKUP_S32(ymax, config->args, "-ymax", false, false);
+    PXOPT_LOOKUP_STR(hostname, config->args, "-hostname", false, false);
+    PXOPT_LOOKUP_F32(good_frac, config->args, "-good_frac", false, false);
+    PXOPT_LOOKUP_BOOL(ignore, config->args, "-ignore", false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // we don't want to insert the last skyfile in a run but then not mark the
+    // run as 'stop'
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // XXX need to validate that this coresponds to an warpInputImfile
+    if (!warpSkyfileInsert(config->dbh,
+                           warp_id,
+                           skycell_id,
+                           tess_id,
+                           uri,
+                           path_base,
+                           "full",      // data_state
+                           bg,
+                           bg_stdev,
+                           dtime_warp,
+                           hostname,
+                           good_frac,
+                           xmin,
+                           xmax,
+                           ymin,
+                           ymax,
+                           ignore,
+                           code
+        )) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!warpCompletedRuns(config)) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // point of no return
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+bool warpCompletedRuns(pxConfig *config)
+{
+    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";
+
+    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
+
+    return true;
+}
+
+static bool warpedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    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, "-exp_id",     "rawExp.exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_name",   "rawExp.exp_name", "==");
+    PXOPT_COPY_S64(config->args, where, "-fake_id",    "fakeRun.fake_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find all rawImfiles matching the default query
+    psString query = pxDataGet("warptool_warped.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("warptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "warpSkyfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertwarpedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    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", "==");
+
+    if (!psListLength(where->list)
+        && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    psString query = pxDataGet("warptool_revertwarped.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+
+static bool blockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    if (!warpMaskInsert(config->dbh, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool maskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM warpMask");
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("warpool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "warpMask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool unblockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    char *query = "DELETE FROM warpMask WHERE label = '%s'";
+
+    if (!p_psDBRunQuery(config->dbh, query, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool pendingcleanuprunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("warptool_pendingcleanuprun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("warptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "warpPendingCleanupRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool pendingcleanupwarpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", false, 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_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("warptool_pendingcleanupskyfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("warptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "warpPendingCleanupWarp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool donecleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("warptool_donecleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("warptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "warpDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool isValidMode(pxConfig *config, const char *mode)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(mode, false);
+
+    // check that state is a valid string value
+    if (!(
+            (strncmp(mode, "warp", 5) == 0)
+            || (strncmp(mode, "diff", 5) == 0)
+            || (strncmp(mode, "stack", 6) == 0)
+            || (strncmp(mode, "magic", 6) == 0)
+        )
+    ) {
+        psError(PS_ERR_UNKNOWN, false,
+                "invalid warpRun mode: %s", mode);
+        return false;
+    }
+
+    return true;
+}
+
+// update warpSkyfile.data_state to given value.
+// afterwards, if all skfyiles in the run have the new state, update the state for the run as well
+// shared code for the modes -tocleanedskyfile -tofullskyfile -topurgedskyfile
+
+static bool change_skyfile_data_state(pxConfig *config, psString data_state, psString run_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // warp_id, skycell_id are required
+    PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", true, false);
+    PXOPT_LOOKUP_STR(skycell_id, config->args, "-skycell_id", true, false);
+
+    psString query = pxDataGet("warptool_change_skyfile_data_state.sql");
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // note only updates if warpRun.state = run_state
+    if (!p_psDBRunQuery(config->dbh, query, data_state, warp_id, skycell_id, run_state)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+
+    query = pxDataGet("warptool_change_run_state.sql");
+    if (!p_psDBRunQuery(config->dbh, query, data_state, warp_id, data_state)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+static bool tocleanedskyfileMode(pxConfig *config)
+{
+    return change_skyfile_data_state(config, "cleaned", "goto_cleaned");
+}
+static bool tofullskyfileMode(pxConfig *config)
+{
+    return change_skyfile_data_state(config, "full", "update");
+}
+static bool topurgedskyfileMode(pxConfig *config)
+{
+    return change_skyfile_data_state(config, "purged", "goto_purged");
+}
+
+static bool updateskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // warp_id, skycell_id, code are required
+    PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", true, false);
+    PXOPT_LOOKUP_STR(skycell_id, config->args, "-skycell_id", true, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    psString query = pxDataGet("warptool_updateskyfile.sql");
+
+    if (!p_psDBRunQuery(config->dbh, query, code, warp_id, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/warptool.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/warptool.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/warptool.h	(revision 22322)
@@ -0,0 +1,55 @@
+/*
+ * warptool.h
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef WARPTOOL_H
+#define WARPTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    WARPTOOL_MODE_NONE           = 0x0,
+    WARPTOOL_MODE_DEFINEBYQUERY,
+    WARPTOOL_MODE_DEFINERUN,
+    WARPTOOL_MODE_RUNONE,
+    WARPTOOL_MODE_UPDATERUN,
+    WARPTOOL_MODE_ADDINPUTEXP,
+    WARPTOOL_MODE_EXP,
+    WARPTOOL_MODE_IMFILE,
+    WARPTOOL_MODE_TOOVERLAP,
+    WARPTOOL_MODE_ADDOVERLAP,
+    WARPTOOL_MODE_SCMAP,
+    WARPTOOL_MODE_TOWARPED,
+    WARPTOOL_MODE_ADDWARPED,
+    WARPTOOL_MODE_WARPED,
+    WARPTOOL_MODE_REVERTWARPED,
+    WARPTOOL_MODE_BLOCK,
+    WARPTOOL_MODE_MASKED,
+    WARPTOOL_MODE_UNBLOCK,
+    WARPTOOL_MODE_PENDINGCLEANUPRUN,
+    WARPTOOL_MODE_PENDINGCLEANUPSKYFILE,
+    WARPTOOL_MODE_DONECLEANUP,
+    WARPTOOL_MODE_TOCLEANEDSKYFILE,
+    WARPTOOL_MODE_TOPURGEDSKYFILE,
+    WARPTOOL_MODE_TOFULLSKYFILE,
+    WARPTOOL_MODE_UPDATESKYFILE,
+} warptoolMode;
+
+pxConfig *warptoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // WARPTOOL_H
Index: /tags/sj_tags/sj_root_20080929/ippTools/src/warptoolConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippTools/src/warptoolConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippTools/src/warptoolConfig.c	(revision 22322)
@@ -0,0 +1,405 @@
+/*
+ * warptoolConfig.c
+ *
+ * Copyright (C) 2006-2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "warptool.h"
+
+pxConfig *warptoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    psTime *now = psTimeGetNow(PS_TIME_TAI);
+
+    // -definebyquery
+    // XXX need to allow multiple chip_ids
+    // 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);
+
+    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);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_label",          0, "define label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_dvodb",          0, "define DVO db", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_tess_id",        0, "define tess ID", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_end_stage",      0, "define end stage", NULL);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-all",               0, "allow everything to be queued without search terms", false);
+
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-registered",  0,            "time detrend run was registered", now);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-magiced",  0,            "has this exposure been magiced", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -definerun
+    psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddS64(definerunArgs, PS_LIST_TAIL, "-fake_id", 0,            "define camtool ID (required)", 0);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-mode", 0,            "define mode (required)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-workdir", 0,            "define workdir (required)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-label", 0,            "define label", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-dvodb", 0,            "define dvodb", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-tess_id", 0,            "define tess_id", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-end_stage", 0,            "define end stage", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-registered",  0,            "time detrend run was registered", now);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-magiced",  0,            "has this exposure been magiced", false);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -updaterun
+    // XXX need to allow multiple fake_ids
+    // XXX need to allow multiple fake_ids
+    // XXX need to allow multiple chip_ids
+    // 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
+
+    // -exp
+    psMetadata *expArgs = psMetadataAlloc();
+    psMetadataAddS64(expArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warptool ID", 0);
+    psMetadataAddS64(expArgs, PS_LIST_TAIL, "-fake_id", 0,            "search by camtool ID", 0);
+    psMetadataAddU64(expArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(expArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -imfile
+    psMetadata *imfileArgs = psMetadataAlloc();
+    psMetadataAddS64(imfileArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warptool ID", 0);
+    psMetadataAddS64(imfileArgs, PS_LIST_TAIL, "-fake_id", 0,            "search by camtool ID", 0);
+    psMetadataAddU64(imfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(imfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -tooverlap
+    psMetadata *tooverlapArgs = psMetadataAlloc();
+    psMetadataAddS64(tooverlapArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warp ID", 0);
+    psMetadataAddStr(tooverlapArgs, PS_LIST_TAIL, "-label", 0, "search by label", NULL);
+    psMetadataAddU64(tooverlapArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tooverlapArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addoverlap
+    psMetadata *addoverlapArgs = psMetadataAlloc();
+    psMetadataAddStr(addoverlapArgs, PS_LIST_TAIL, "-mapfile", 0,            "path to skycell <-> imfile mapping file", NULL);
+    psMetadataAddS64(addoverlapArgs, PS_LIST_TAIL, "-warp_id",  0,            "set warp ID", 0);
+    psMetadataAddS16(addoverlapArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+
+    // -scmap
+    psMetadata *scmapArgs = psMetadataAlloc();
+    psMetadataAddS64(scmapArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warptool ID", 0);
+    psMetadataAddStr(scmapArgs, PS_LIST_TAIL, "-skycell_id", 0,            "search by skycell ID", NULL);
+    psMetadataAddStr(scmapArgs, PS_LIST_TAIL, "-tess_id", 0,            "search by tess ID", NULL);
+    psMetadataAddU64(scmapArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(scmapArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -towarped
+    psMetadata *towarpedArgs = psMetadataAlloc();
+    psMetadataAddS64(towarpedArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warptool ID", 0);
+    psMetadataAddStr(towarpedArgs, PS_LIST_TAIL, "-label", 0, "search by label", NULL);
+    psMetadataAddU64(towarpedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(towarpedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addwarped
+    psMetadata *addwarpedArgs = psMetadataAlloc();
+    psMetadataAddS64(addwarpedArgs, PS_LIST_TAIL, "-warp_id", 0,            "define warptool ID (required)", 0);
+    psMetadataAddStr(addwarpedArgs, PS_LIST_TAIL, "-skycell_id",  0,            "define skycell ID (required)", NULL);
+    psMetadataAddStr(addwarpedArgs, PS_LIST_TAIL, "-tess_id",  0,            "define tessellation ID (required)", NULL);
+    psMetadataAddStr(addwarpedArgs, PS_LIST_TAIL, "-uri", 0,            "define URI of file", 0);
+    psMetadataAddStr(addwarpedArgs, PS_LIST_TAIL, "-path_base", 0,            "define base output location", 0);
+    psMetadataAddF64(addwarpedArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF64(addwarpedArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF32(addwarpedArgs, PS_LIST_TAIL, "-dtime_warp",  0,            "define elapsed processing time", NAN);
+    psMetadataAddS32(addwarpedArgs, PS_LIST_TAIL, "-xmin",  0,            "define minimum x value", INT_MAX);
+    psMetadataAddS32(addwarpedArgs, PS_LIST_TAIL, "-xmax",  0,            "define maximum x value", -INT_MAX);
+    psMetadataAddS32(addwarpedArgs, PS_LIST_TAIL, "-ymin",  0,            "define minimum y value", INT_MAX);
+    psMetadataAddS32(addwarpedArgs, PS_LIST_TAIL, "-ymax",  0,            "define maximum y value", -INT_MAX);
+    psMetadataAddStr(addwarpedArgs, PS_LIST_TAIL, "-hostname", 0,            "define hostname", 0);
+    psMetadataAddF32(addwarpedArgs, PS_LIST_TAIL, "-good_frac",  0,            "define %% of good pixels", NAN);
+    psMetadataAddBool(addwarpedArgs, PS_LIST_TAIL, "-ignore",  0,            "define if this skycell should be ignored", false);
+    psMetadataAddS16(addwarpedArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+
+    // -warped
+    psMetadata *warpedArgs = psMetadataAlloc();
+    psMetadataAddS64(warpedArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warptool ID", 0);
+    psMetadataAddStr(warpedArgs, PS_LIST_TAIL, "-skycell_id",  0,            "define skycell ID", NULL);
+    psMetadataAddStr(warpedArgs, PS_LIST_TAIL, "-tess_id",  0,            "define tessellation ID", NULL);
+    psMetadataAddS64(warpedArgs, PS_LIST_TAIL, "-exp_id", 0,            "define exposure tag", 0);
+    psMetadataAddStr(warpedArgs, PS_LIST_TAIL, "-exp_name", 0,          "define exposure tag", 0);
+    psMetadataAddS64(warpedArgs, PS_LIST_TAIL, "-fake_id", 0,            "define phase 3 version of exposure tag", 0);
+    psMetadataAddU64(warpedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(warpedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -revertwarped
+    // XXX need to allow multiple fake_ids
+    // XXX need to allow multiple fake_ids
+    // XXX need to allow multiple chip_ids
+    // XXX need to allow multiple exp_ids
+    psMetadata *revertwarpedArgs = psMetadataAlloc();
+    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);
+
+    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);
+
+    // -block
+    psMetadata *blockArgs = psMetadataAlloc();
+    psMetadataAddStr(blockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to mask out (required)", NULL);
+
+    // -masked
+    psMetadata *maskedArgs = psMetadataAlloc();
+    psMetadataAddBool(maskedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -unblock
+    psMetadata *unblockArgs = psMetadataAlloc();
+    psMetadataAddStr(unblockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to unmask (required)", NULL);
+
+    // -pendingcleanuprun
+    psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanuprunArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(pendingcleanuprunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanuprunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -pendingcleanupskyfile
+    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);
+    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);
+
+    // -donecleanup
+    psMetadata *donecleanupArgs = psMetadataAlloc();
+    psMetadataAddStr(donecleanupArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(donecleanupArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanupArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -tocleanedskyfile
+    psMetadata *tocleanedskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tocleanedskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,    "warptool ID to update", 0);
+    psMetadataAddStr(tocleanedskyfileArgs, PS_LIST_TAIL, "-skycell_id", 0, "skycell ID to update", NULL);
+
+    // -topurgedskyfile
+    psMetadata *topurgedskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(topurgedskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,    "warptool ID to update", 0);
+    psMetadataAddStr(topurgedskyfileArgs, PS_LIST_TAIL, "-skycell_id", 0, "skycell ID to update", NULL);
+
+    // -tofullskyfile
+    psMetadata *tofullskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tofullskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,    "warptool ID to update", 0);
+    psMetadataAddStr(tofullskyfileArgs, PS_LIST_TAIL, "-skycell_id", 0, "skycell ID to update", NULL);
+
+    // -toupdateskyfile
+    psMetadata *updateskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(updateskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,    "warptool ID to update", 0);
+    psMetadataAddStr(updateskyfileArgs, PS_LIST_TAIL, "-skycell_id", 0, "skycell ID to update", NULL);
+    psMetadataAddS16(updateskyfileArgs, PS_LIST_TAIL, "-code",  0,      "new fault code", 0);
+
+    psFree(now);
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes   = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definebyquery",   "", WARPTOOL_MODE_DEFINEBYQUERY,  definebyqueryArgs);
+    PXOPT_ADD_MODE("-definerun",       "", WARPTOOL_MODE_DEFINERUN,      definerunArgs);
+    PXOPT_ADD_MODE("-updaterun",       "", WARPTOOL_MODE_UPDATERUN,      updaterunArgs);
+    PXOPT_ADD_MODE("-exp",             "", WARPTOOL_MODE_EXP,            expArgs);
+    PXOPT_ADD_MODE("-imfile",          "", WARPTOOL_MODE_IMFILE,         imfileArgs);
+    PXOPT_ADD_MODE("-tooverlap",       "", WARPTOOL_MODE_TOOVERLAP,      tooverlapArgs);
+    PXOPT_ADD_MODE("-addoverlap",      "", WARPTOOL_MODE_ADDOVERLAP,     addoverlapArgs);
+    PXOPT_ADD_MODE("-scmap",           "", WARPTOOL_MODE_SCMAP,          scmapArgs);
+    PXOPT_ADD_MODE("-towarped",        "", WARPTOOL_MODE_TOWARPED,       towarpedArgs);
+    PXOPT_ADD_MODE("-addwarped",       "", WARPTOOL_MODE_ADDWARPED,      addwarpedArgs);
+    PXOPT_ADD_MODE("-warped",          "", WARPTOOL_MODE_WARPED,         warpedArgs);
+    PXOPT_ADD_MODE("-revertwarped",    "", WARPTOOL_MODE_REVERTWARPED,   revertwarpedArgs);
+    PXOPT_ADD_MODE("-block",                 "set a label block",                    WARPTOOL_MODE_BLOCK,          blockArgs);
+    PXOPT_ADD_MODE("-masked",                "show blocked lables",                  WARPTOOL_MODE_MASKED,         maskedArgs);
+    PXOPT_ADD_MODE("-unblock",               "remove a label block",                 WARPTOOL_MODE_UNBLOCK,        unblockArgs);
+    PXOPT_ADD_MODE("-pendingcleanuprun",     "show runs that need to be cleaned up", WARPTOOL_MODE_PENDINGCLEANUPRUN,    pendingcleanuprunArgs);
+    PXOPT_ADD_MODE("-pendingcleanupskyfile", "show runs that need to be cleaned up", WARPTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupskyfileArgs);
+    PXOPT_ADD_MODE("-donecleanup",           "show runs that have been cleaned",     WARPTOOL_MODE_DONECLEANUP,          donecleanupArgs);
+    PXOPT_ADD_MODE("-tocleanedskyfile", "set skyfile as cleaned", WARPTOOL_MODE_TOCLEANEDSKYFILE, tocleanedskyfileArgs);
+    PXOPT_ADD_MODE("-topurgedskyfile", "set skyfile as purged", WARPTOOL_MODE_TOPURGEDSKYFILE, topurgedskyfileArgs);
+    PXOPT_ADD_MODE("-tofullskyfile", "set skyfile as full (updated)", WARPTOOL_MODE_TOFULLSKYFILE, tofullskyfileArgs);
+    PXOPT_ADD_MODE("-updateskyfile", "update fault code for skyfile", WARPTOOL_MODE_UPDATESKYFILE, updateskyfileArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ippconfig/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/.cvsignore	(revision 22322)
@@ -0,0 +1,14 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.log
+config.status
+configure
+dvo.site
+install-sh
+ippconfig-0.0.1.tar.bz2
+ippconfig-0.0.1.tar.gz
+missing
+ipprc.config
+site.config
Index: /tags/sj_tags/sj_root_20080929/ippconfig/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/Makefile.am	(revision 22322)
@@ -0,0 +1,57 @@
+SUBDIRS = \
+	recipes \
+	isp \
+	gpc1 \
+	cfh12k \
+	megacam \
+	mosaic2 \
+	simple \
+	simmosaic \
+	simtest \
+	vysos5 \
+	tc3 \
+	skycell \
+	sdssmosaic \
+	sdss \
+	esowfi \
+	lbc_red \
+	lulin \
+	uh8k
+
+install_files = \
+	system.config \
+	GSCregions.tbl \
+	dvo.photcodes
+
+built_sources = \
+	ipprc.config.in \
+	dvo.site.in
+
+built_files = \
+	ipprc.config \
+	dvo.site
+
+installdir = $(datadir)/ippconfig
+
+install_DATA = $(install_files) $(built_files)
+
+install-data-hook:
+	chmod 0755 $(installdir) 
+
+EXTRA_DIST = $(install_files) $(built_sources)
+
+BUILT_SOURCES = $(built_files)
+
+#ACLOCAL_AMFLAGS = -I m4
+
+# From the autoconf manual, section 4.7.2 "Installation Directory Variables"
+edit = sed \
+	-e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \
+	-e 's|@prefix[@]|$(prefix)|g'
+
+ipprc.config dvo.site: Makefile
+	rm -f $@
+	$(edit) '$(srcdir)/$@.in' > $@
+
+ipprc.config: $(srcdir)/ipprc.config.in
+dvo.site: $(srcdir)/dvo.site.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/README	(revision 22322)
@@ -0,0 +1,18 @@
+
+Please read installation section of the IPP User's Guide for more details.
+
+To build and install the IPP configuration scripts, run the following:
+
+configure
+make
+make install
+
+After installation:
+
+Copy dvo.site to ~/.ptolemyrc
+
+Copy ipprc.config to ~/.ipprc
+
+Edit this file:
+  - Set the DBUSER and DBPASSWORD to your mysql username and password
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/Recipes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/Recipes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/Recipes.txt	(revision 22322)
@@ -0,0 +1,20 @@
+
+The recipes may be defined by files at two levels: system and camera.
+
+A single recipe (eg, PPIMAGE) must have a system-level file which
+defines all of the recipe variables.  The camera-level recipe may
+superceed values define in the system-level file.  
+
+Any recipe file may also define alternative symbolic names for
+recipes by including metadata folders named by the symbolic name.  
+
+The order of precedence is: 
+
+* primary system-level values
+* symbolic system-level values
+* primary camera-level values
+* symbolic camera-level values
+
+XXX the MULTI RESET thing is broken: the flag is not being carried
+through to the psMetadataItemAdd function...
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ippConfig
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+#($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+#        DIE=1
+#}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+#($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+#        DIE=1
+#}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+#$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+#$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/Makefile.am	(revision 22322)
@@ -0,0 +1,27 @@
+
+installdir = $(datadir)/ippconfig/cfh12k
+
+install_files = \
+	dvo.config \
+	dvo.layout \
+	rejections.config \
+	camera.config \
+	format_mef.config \
+	format_mef_early.config \
+	format_split.config \
+	format_split_early.config \
+	format_split_cmf.config \
+	ppImage.config \
+	ppMerge.config \
+	psastro.config \
+	psphot.config \
+	dark.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/camera.config	(revision 22322)
@@ -0,0 +1,135 @@
+# Camera configuration file for Cfh12k: describes the camera
+
+# File formats that we know about
+FORMATS		METADATA
+#	EARLY	STR	cfh12k/format_mef_early.config
+#	ESPLIT	STR	cfh12k/format_split_early.config
+	MEF	STR	cfh12k/format_mef.config
+	SPLIT	STR	cfh12k/format_split.config
+	CMF	STR	cfh12k/format_split_cmf.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA	METADATA
+	ccd00	STR	Amp
+	ccd01	STR	Amp
+	ccd02	STR	Amp
+	ccd03	STR	Amp
+	ccd04	STR	Amp
+	ccd05	STR	Amp
+	ccd06	STR	Amp
+	ccd07	STR	Amp
+	ccd08	STR	Amp
+	ccd09	STR	Amp
+	ccd10	STR	Amp
+	ccd11	STR	Amp
+END
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+   B       STR    B           	
+   V       STR    V           	
+   R       STR    R           	
+   I       STR    I           	
+   	              		
+   Z       MULTI
+   Z       STR    Z           	
+   Z       STR    Zp           
+   Z       STR    Zprime       
+   	              		
+   Ha      MULTI
+   Ha      STR    Ha           
+   Ha      STR    Ha.MP7605    
+   Ha      STR    Halpha       
+   Ha      STR    Haalpha.on   
+   	              		
+   HaOff   MULTI
+   HaOff   STR    HaOFF        
+   HaOff   STR    HaOFF.MP7604 
+   HaOff   STR    Halpha.off   
+   	              		
+   Hb      STR    Hb           
+   HbOff   STR    HbOff        
+   	              		
+   CN      MULTI
+   CN      STR    CN           
+   CN      STR    CN.MP780     
+   CN      STR    cn.MP7803    
+   CN      STR    CN.MP7803    
+   	              		
+   TiO     MULTI
+   TiO     STR    TiO          
+   TiO     STR    TiO.MP77     
+   TiO     STR    tio.MP7701   
+   TiO     STR    TiO.MP7701   
+   	              		
+   NB920   STR    NB920        
+   	              		
+   B2F     MULTI
+   B2F     STR    B2F          
+   B2F     STR    UKN:B2F
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	fpa	STR	fpa
+	chip	STR	{CHIP.NAME}
+	cell	STR	{CHIP.NAME}:{CELL.NAME}
+	FPA	STR	fpa
+	CHIP	STR	{CHIP.NAME}
+	CELL	STR	{CHIP.NAME}:{CELL.NAME}
+END
+
+DVO.CAMERADIR	STR	cfh12k		# Camera directory for DVO
+
+DARK.NORM 	STR cfh12k/dark.config
+DARK.NORM.KEY 	STR fpa_{CHIP.NAME}_Amp
+# DARK.NORM.KEY STR BASIC ## rough version from global stats
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES		METADATA
+	# Other recipes
+        PSPHOT          STR     cfh12k/psphot.config           # psphot details
+        PSASTRO         STR     cfh12k/psastro.config          # psastro details
+	PPIMAGE         STR     cfh12k/ppImage.config		# ppImage recipe
+	REJECTIONS      STR     cfh12k/rejections.config	# rejection recipe
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES	STR	recipes/filerules-mef.mdc		# File rules appropriate for MEF format
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR {CHIP.NAME}.hdr
+  CMF.DATA STR {CHIP.NAME}.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD  STR {CHIP.NAME}.hdr
+  PSF.TABLE STR {CHIP.NAME}.psf_model
+  PSF.RESID STR {CHIP.NAME}.psf_resid
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBS		STR	EXPNUM
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.N}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/dark.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/dark.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/dark.config	(revision 22322)
@@ -0,0 +1,106 @@
+
+# basic fit to mosaic-level bg
+BASIC METADATA
+   NORDER_X         S32       2                # number of x orders
+   VAL_X00          F64      -1.366
+   VAL_X01          F64       8.974e-03
+   VAL_X02          F64       7.864e-06
+   NELEMENTS        S32       3                # number of unmasked components
+END
+
+fpa_ccd00_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -2.42585939637 
+   VAL_X01   F64 0.0218932386738 
+   VAL_X02   F64 1.63910422999e-05 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd01_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -0.673311998929 
+   VAL_X01   F64 0.00351724865032 
+   VAL_X02   F64 3.82106321907e-06 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd02_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -0.516624899086 
+   VAL_X01   F64 0.002322522999 
+   VAL_X02   F64 3.24144163863e-06 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd03_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -0.571626302054 
+   VAL_X01   F64 0.00233441005459 
+   VAL_X02   F64 2.35198601494e-06 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd04_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -2.06687190434 
+   VAL_X01   F64 0.00214124485304 
+   VAL_X02   F64 2.26141245447e-06 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd05_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -0.938425768984 
+   VAL_X01   F64 0.00787794459813 
+   VAL_X02   F64 6.18544058024e-06 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd06_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -1.01351177035 
+   VAL_X01   F64 0.00665585528024 
+   VAL_X02   F64 6.34327926043e-06 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd07_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -0.475706521997 
+   VAL_X01   F64 0.00151765156459 
+   VAL_X02   F64 3.19325558938e-06 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd08_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -0.555167026441 
+   VAL_X01   F64 0.00287603928732 
+   VAL_X02   F64 4.31016033628e-06 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd09_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -0.999503837142 
+   VAL_X01   F64 0.00530642880692 
+   VAL_X02   F64 5.64208407175e-06 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd10_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -1.66026335274 
+   VAL_X01   F64 0.0163728070101 
+   VAL_X02   F64 1.28840808261e-05 
+   NELEMENTS S32 3     
+END 
+
+fpa_ccd11_Amp METADATA 
+   NORDER_X  S32 2    
+   VAL_X00   F64 -4.47794069907 
+   VAL_X01   F64 0.0347579009973 
+   VAL_X02   F64 2.78713468204e-05 
+   NELEMENTS S32 3     
+END 
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/dvo.config	(revision 22322)
@@ -0,0 +1,64 @@
+
+# location of DVO database tables
+CATDIR			/data/alala.0/eugene/isp/catdir
+
+# keywords used by DVO to interpret the headers
+
+# keyword abstractions:
+#DATE-KEYWORD		DATE-OBS
+#DATE-MODE		yyyy-mm-dd
+#UT-KEYWORD		UTC-OBS
+JD-KEYWORD		NONE
+MJD-KEYWORD		MJD-OBS
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+EXTNAME-KEYWORD		IMAGEID
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		0
+CAL_INSTMAG_MIN		0
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		1.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/dvo.layout
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/dvo.layout	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/dvo.layout	(revision 22322)
@@ -0,0 +1,29 @@
+# this file defines the layout of the mosaic imager:
+
+NCCD 12
+NAXIS1 2080
+NAXIS2 4128
+
+MOSAIC_X 6
+MOSAIC_Y 2
+
+# lines need to contain:
+
+CHIPID_KEYWORD CHIPID
+
+# ID  CHIPID xoffset yoffset xflip yflip datasec         biassec                 Xo    Yo     theta
+CCD.0  chip00      0       0     0     0 [6:2049,5:4100] [2050:2080,2:4128]     0.0    0.0   0.00
+CCD.1  chip01      1       0     0     0 [6:2049,5:4100] [2050:2080,2:4128]  2000.0    0.0   0.00
+CCD.2  chip02      2       0     1     0 [6:2049,5:4100] [2050:2080,2:4128]  6000.0    0.0   0.00
+CCD.3  chip03      3       0     1     0 [6:2049,5:4100] [2050:2080,2:4128]  8000.0    0.0   0.00
+CCD.4  chip04      4       0     0     0 [6:2049,5:4100] [2050:2080,2:4128]  8000.0    0.0   0.00
+CCD.5  chip05      5       0     0     0 [6:2049,5:4100] [2050:2080,2:4128] 10000.0    0.0   0.00
+CCD.6  chip06      0       1     0     1 [6:2049,5:4100] [2050:2080,2:4128]     0.0 8000.0   1.00
+CCD.7  chip07      1       1     0     1 [6:2049,5:4100] [2050:2080,2:4128]  2000.0 8000.0   0.00
+CCD.8  chip08      2       1     0     1 [6:2049,5:4100] [2050:2080,2:4128]  4000.0 8000.0   0.00
+CCD.9  chip09      3       1     0     1 [6:2049,5:4100] [2050:2080,2:4128]  6000.0 8000.0   0.00
+CCD.10 chip10      4       1     0     1 [6:2049,5:4100] [2050:2080,2:4128]  8000.0 8000.0   0.00
+CCD.11 chip11      5       1     0     1 [6:2049,5:4100] [2050:2080,2:4128] 10000.0 8000.0   0.00
+
+# can we use header to find biassec
+USE_BIASSEC 1
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_mef.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_mef.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_mef.config	(revision 22322)
@@ -0,0 +1,166 @@
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The spliced MecaCam data is stored in single extensions for each chip
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	CFH12K
+	INSTRUME	STR	CFH12K Mosaic
+	EXTEND		BOOL	T
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CHIP	# The extensions represent chips
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier
+	CHIP.NAME	STR	IMAGEID	# An extension keyword for unique identifie
+	EXTWORD         STR     IMAGEID
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:component cell
+	 0		STR	ccd00:MIT-LL
+	 1		STR	ccd01:MIT-LL
+	 2		STR	ccd02:MIT-LL
+	 3		STR	ccd03:MIT-LL
+	 4		STR	ccd04:MIT-LL
+	 5		STR	ccd05:MIT-LL
+	 6		STR	ccd06:MIT-LL
+	 7		STR	ccd07:MIT-LL
+	 8		STR	ccd08:MIT-LL
+	 9		STR	ccd09:MIT-LL
+	10		STR	ccd10:MIT-LL
+	11		STR	ccd11:MIT-LL
+END
+
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	MIT-LL	STR	Amp:cell
+END
+
+# Specify the cells
+CELLS		METADATA
+	cell		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.X0			S32	0
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBJECT
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTERID    STR     FILTER
+        FPA.FILTER      STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	TELALT
+	FPA.AZ		STR	TELAZ
+	FPA.TEMP	STR	DETTEM
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+	CELL.TIME	STR	MJD-OBS
+        CELL.GAIN       STR     GAIN
+        CELL.SATURATION STR     SATURATE
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	CFH12K		# this value is used in ipprc.config to find the recipes
+	FPA.DETECTOR	        STR	CFH12K
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4096
+	CELL.XSIZE		S32	2048
+	CELL.YSIZE		S32	4096
+	CELL.READNOISE          F32     10.0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.BAD                S32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	10240
+		ccd01	S32	8192
+		ccd02	S32	6144
+		ccd03	S32	4096
+		ccd04	S32	2048
+		ccd05	S32	0
+		ccd06	S32	10240
+		ccd07	S32	8192 
+		ccd08	S32	6144 
+		ccd09	S32	4096 
+		ccd10	S32	2048 
+		ccd11	S32	0    
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	0
+		ccd01	S32	0
+		ccd02	S32	0
+		ccd03	S32	0
+		ccd04	S32	0
+		ccd05	S32	0
+		ccd06	S32	0
+		ccd07	S32	0
+		ccd08	S32	0
+		ccd09	S32	0
+		ccd10	S32	0
+		ccd11	S32	0
+	END
+	CHIP.XPARITY		S32	-1
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	+1
+		ccd07	S32	+1
+		ccd08	S32	+1
+		ccd09	S32	+1
+		ccd10	S32	+1
+		ccd11	S32	+1
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_mef_early.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_mef_early.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_mef_early.config	(revision 22322)
@@ -0,0 +1,184 @@
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The spliced MecaCam data is stored in single extensions for each chip
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	CFH12K
+	INSTRUME	STR	CFH12K Mosaic
+	EXTEND		BOOL	T
+        IMAGESWV        MULTI
+	IMAGESWV	STR     CFHT DetCom v3.25 (Jun 13 2001)    
+	IMAGESWV	STR     CFHT DetCom v3.28 (Jul 11 2001)    
+	IMAGESWV	STR     CFHT DetCom v3.35 (May 01 2002)
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CHIP	# The extensions represent chips
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier
+	CHIP.NAME	STR	IMAGEID	# An extension keyword for unique identifie
+	EXTWORD         STR     IMAGEID
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:component cell
+	 0		STR	ccd00:MIT-LL
+	 1		STR	ccd01:MIT-LL
+	 2		STR	ccd02:MIT-LL
+	 3		STR	ccd03:MIT-LL
+	 4		STR	ccd04:MIT-LL
+	 5		STR	ccd05:MIT-LL
+	 6		STR	ccd06:MIT-LL
+	 7		STR	ccd07:MIT-LL
+	 8		STR	ccd08:MIT-LL
+	 9		STR	ccd09:MIT-LL
+	10		STR	ccd10:MIT-LL
+	11		STR	ccd11:MIT-LL
+END
+
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	MIT-LL	STR	Amp:cell
+END
+
+# Specify the cells
+CELLS		METADATA
+	cell		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.X0			S32	0
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+        FPA.AIRMASS     STR     AIRMASS
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBJECT
+        FPA.FILTERID    STR     FILTER
+        FPA.FILTER      STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	TELALT
+	FPA.AZ		STR	TELAZ
+	FPA.TEMP	STR	DETTEM
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+	CELL.TIME	STR	MJD-OBS
+        CELL.GAIN       STR     GAIN
+        CELL.SATURATION STR     SATURATE
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	CFH12K		# this value is used in ipprc.config to find the recipes
+	FPA.DETECTOR	        STR	CFH12K
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4096
+	CELL.XSIZE		S32	2048
+	CELL.YSIZE		S32	4096
+	CELL.READNOISE          F32     10.0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.BAD                S32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	10
+		ccd01	S32	2048
+		ccd02	S32	4096
+		ccd03	S32	6144
+		ccd04	S32	8192
+		ccd05	S32	10240
+		ccd06	S32	0    
+		ccd07	S32	2048 
+		ccd08	S32	4096 
+		ccd09	S32	6144 
+		ccd10	S32	8192 
+		ccd11	S32	10240
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	10
+		ccd01	S32	10
+		ccd02	S32	10
+		ccd03	S32	10
+		ccd04	S32	10
+		ccd05	S32	10
+		ccd06	S32	4128
+		ccd07	S32	4128
+		ccd08	S32	4128
+		ccd09	S32	4128
+		ccd10	S32	4128
+		ccd11	S32	4128
+	END
+	CHIP.XPARITY		S32	1
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	+1
+		ccd01	S32	+1
+		ccd02	S32	+1
+		ccd03	S32	+1
+		ccd04	S32	+1
+		ccd05	S32	+1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
+#  fits  CFHT 12kcom v2 2 (Sep 28 1999)  
+#  fits  CFHT 12kcom v2 6 (Dec 01 1999)  
+#  fits  CFHT DetCom v3 1 (Oct 19 2000)  
+#  fits  CFHT DetCom v3 2 (Dec 23 2000)  
+#  fits  CFHT DetCom v3 22 (Apr 12 2001)  
+#  fits  CFHT DetCom v3 25 (Jun 13 2001)  
+#  fits  CFHT DetCom v3 32 (Oct 20 2001)  
+#  fits  CFHT DetCom v3 33 (Dec 09 2001)  
+#  fits  CFHT DetCom v3 35 (Jan 07 2002)  
+#  fits  CFHT DetCom v3 35 (Mar 09 2002)  
+#  fits  CFHT DetCom v3 35 (May 01 2002)  
+#  fits  CFHT DetCom v3 7 (Jun 29 2000)  
+#  fits  CFHT DetCom v3 8 (Aug 02 2000)  
+#  fits  CFHT DetCom v3 9 (Sep 15 2000)  
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_split.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_split.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_split.config	(revision 22322)
@@ -0,0 +1,169 @@
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The spliced MecaCam data is stored in single extensions for each chip
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	CFH12K
+	INSTRUME	STR	CFH12K Mosaic
+	NAXIS		S32	2
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	CHIP	 # The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	 # The extensions represent chips
+	FPA.OBS		STR	EXPNUM	 # A PHU keyword for unique identifier
+	CHIP.NAME	STR	IMAGEID	 # An extension keyword for unique identifie
+	EXTWORD         STR     IMAGEID
+	CONTENT		STR	IMAGEID	 # Key to the CONTENTS menu
+	CONTENT.RULE	STR	{CHIP.N} # How to derive the CONTENT when writing
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Content key   ->      chip name:component cell
+	0		STR	ccd00:MIT-LL
+	1		STR	ccd01:MIT-LL
+	2		STR	ccd02:MIT-LL
+	3		STR	ccd03:MIT-LL
+	4		STR	ccd04:MIT-LL
+	5		STR	ccd05:MIT-LL
+	6		STR	ccd06:MIT-LL
+	7		STR	ccd07:MIT-LL
+	8		STR	ccd08:MIT-LL
+	9		STR	ccd09:MIT-LL
+	10		STR	ccd10:MIT-LL
+	11		STR	ccd11:MIT-LL
+END
+
+# Specify the chips
+CHIPS		METADATA
+	MIT-LL	STR	Amp:cell
+END
+
+
+# Specify the cells
+CELLS		METADATA
+	cell		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.X0			S32	0
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAIN
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTERID    STR     FILTER
+        FPA.FILTER      STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	TELALT
+	FPA.AZ		STR	TELAZ
+	FPA.TEMP	STR	DETTEM
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.SATURATION STR     SATURATE
+	CELL.TIME	STR	MJD-OBS
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	CFH12K		# this value is used in ipprc.config to find the recipes
+	FPA.DETECTOR	        STR	CFH12K
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4096
+	CELL.XSIZE		S32	2048
+	CELL.YSIZE		S32	4096
+	CELL.READNOISE          F32     10.0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.BAD                S32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	0
+		ccd01	S32	2048
+		ccd02	S32	4096
+		ccd03	S32	6144
+		ccd04	S32	8192
+		ccd05	S32	10240
+		ccd06	S32	0    
+		ccd07	S32	2048 
+		ccd08	S32	4096 
+		ccd09	S32	6144 
+		ccd10	S32	8192 
+		ccd11	S32	10240
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	0
+		ccd01	S32	0
+		ccd02	S32	0
+		ccd03	S32	0
+		ccd04	S32	0
+		ccd05	S32	0
+		ccd06	S32	4128
+		ccd07	S32	4128
+		ccd08	S32	4128
+		ccd09	S32	4128
+		ccd10	S32	4128
+		ccd11	S32	4128
+	END
+	CHIP.XPARITY		S32	1
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	+1
+		ccd01	S32	+1
+		ccd02	S32	+1
+		ccd03	S32	+1
+		ccd04	S32	+1
+		ccd05	S32	+1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_split_cmf.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_split_cmf.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_split_cmf.config	(revision 22322)
@@ -0,0 +1,169 @@
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The spliced MecaCam data is stored in single extensions for each chip
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	CFH12K
+	INSTRUME	STR	CFH12K Mosaic
+	NAXIS		S32	0
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	CHIP	 # The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	 # The extensions represent chips
+	FPA.OBS		STR	EXPNUM	 # A PHU keyword for unique identifier
+	CHIP.NAME	STR	IMAGEID	 # An extension keyword for unique identifie
+	EXTWORD         STR     IMAGEID
+	CONTENT		STR	IMAGEID	 # Key to the CONTENTS menu
+	CONTENT.RULE	STR	{CHIP.N} # How to derive the CONTENT when writing
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Content key   ->      chip name:component cell
+	ccd00.hdr	STR	ccd00:MIT-LL
+	ccd01.hdr	STR	ccd01:MIT-LL
+	ccd02.hdr	STR	ccd02:MIT-LL
+	ccd03.hdr	STR	ccd03:MIT-LL
+	ccd04.hdr	STR	ccd04:MIT-LL
+	ccd05.hdr	STR	ccd05:MIT-LL
+	ccd06.hdr	STR	ccd06:MIT-LL
+	ccd07.hdr	STR	ccd07:MIT-LL
+	ccd08.hdr	STR	ccd08:MIT-LL
+	ccd09.hdr	STR	ccd09:MIT-LL
+	ccd10.hdr	STR	ccd10:MIT-LL
+	ccd11.hdr	STR	ccd11:MIT-LL
+END
+
+# Specify the chips
+CHIPS		METADATA
+	MIT-LL	STR	Amp:cell
+END
+
+
+# Specify the cells
+CELLS		METADATA
+	cell		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.X0			S32	0
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAIN
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTERID    STR     FILTER
+        FPA.FILTER      STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	TELALT
+	FPA.AZ		STR	TELAZ
+	FPA.TEMP	STR	DETTEM
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.SATURATION STR     SATURATE
+	CELL.TIME	STR	MJD-OBS
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	CFH12K		# this value is used in ipprc.config to find the recipes
+	FPA.DETECTOR	        STR	CFH12K
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4096
+	CELL.XSIZE		S32	2048
+	CELL.YSIZE		S32	4096
+	CELL.READNOISE          F32     10.0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.BAD                S32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	0
+		ccd01	S32	2048
+		ccd02	S32	4096
+		ccd03	S32	6144
+		ccd04	S32	8192
+		ccd05	S32	10240
+		ccd06	S32	0    
+		ccd07	S32	2048 
+		ccd08	S32	4096 
+		ccd09	S32	6144 
+		ccd10	S32	8192 
+		ccd11	S32	10240
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	0
+		ccd01	S32	0
+		ccd02	S32	0
+		ccd03	S32	0
+		ccd04	S32	0
+		ccd05	S32	0
+		ccd06	S32	4128
+		ccd07	S32	4128
+		ccd08	S32	4128
+		ccd09	S32	4128
+		ccd10	S32	4128
+		ccd11	S32	4128
+	END
+	CHIP.XPARITY		S32	1
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	+1
+		ccd01	S32	+1
+		ccd02	S32	+1
+		ccd03	S32	+1
+		ccd04	S32	+1
+		ccd05	S32	+1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_split_early.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_split_early.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/format_split_early.config	(revision 22322)
@@ -0,0 +1,173 @@
+# This is the format file for split CFH12K images from the period when the 
+# EXTNAME field was not being populated
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	CFH12K
+	INSTRUME	STR	CFH12K Mosaic
+	NAXIS		S32	2
+        IMAGESWV        MULTI
+	IMAGESWV	STR     CFHT DetCom v3.25 (Jun 13 2001)    
+	IMAGESWV	STR     CFHT DetCom v3.28 (Jul 11 2001)    
+	IMAGESWV	STR     CFHT DetCom v3.35 (May 01 2002)
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	CHIP	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# The extensions represent chips
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier
+	CHIP.NAME	STR	IMAGEID	# An extension keyword for unique identifie
+	EXTWORD         STR     IMAGEID
+	CONTENT		STR	IMAGEID
+	CONTENT.RULE	STR	{CHIP.N}
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:component cell
+	 0		STR	ccd00:MIT-LL
+	 1		STR	ccd01:MIT-LL
+	 2		STR	ccd02:MIT-LL
+	 3		STR	ccd03:MIT-LL
+	 4		STR	ccd04:MIT-LL
+	 5		STR	ccd05:MIT-LL
+	 6		STR	ccd06:MIT-LL
+	 7		STR	ccd07:MIT-LL
+	 8		STR	ccd08:MIT-LL
+	 9		STR	ccd09:MIT-LL
+	10		STR	ccd10:MIT-LL
+	11		STR	ccd11:MIT-LL
+END
+
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	MIT-LL	STR	Amp:cell
+END
+
+# Specify the cells
+CELLS		METADATA
+	cell		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.X0			S32	0
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAIN
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTERID    STR     FILTER
+        FPA.FILTER      STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	TELALT
+	FPA.AZ		STR	TELAZ
+	FPA.TEMP	STR	DETTEM
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.SATURATION STR     SATURATE
+	CELL.TIME	STR	MJD-OBS
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	CFH12K		# this value is used in ipprc.config to find the recipes
+	FPA.DETECTOR	        STR	CFH12K
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4096
+	CELL.XSIZE		S32	2048
+	CELL.YSIZE		S32	4096
+	CELL.READNOISE          F32     10.0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.BAD                S32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	0
+		ccd01	S32	2048
+		ccd02	S32	4096
+		ccd03	S32	6144
+		ccd04	S32	8192
+		ccd05	S32	10240
+		ccd06	S32	0    
+		ccd07	S32	2048 
+		ccd08	S32	4096 
+		ccd09	S32	6144 
+		ccd10	S32	8192 
+		ccd11	S32	10240
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	0
+		ccd01	S32	0
+		ccd02	S32	0
+		ccd03	S32	0
+		ccd04	S32	0
+		ccd05	S32	0
+		ccd06	S32	4128
+		ccd07	S32	4128
+		ccd08	S32	4128
+		ccd09	S32	4128
+		ccd10	S32	4128
+		ccd11	S32	4128
+	END
+	CHIP.XPARITY		S32	1
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	+1
+		ccd01	S32	+1
+		ccd02	S32	+1
+		ccd03	S32	+1
+		ccd04	S32	+1
+		ccd05	S32	+1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/ppImage.config	(revision 22322)
@@ -0,0 +1,58 @@
+### ppImage recipe configuration file for cfh12k
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	3		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEDIAN		# MEAN | MEDIAN
+
+# for a test, turn off selected detrend types
+OVERSCAN	BOOL	FALSE		# Overscan subtraction
+BIAS		BOOL	FALSE		# Bias subtraction
+DARK		BOOL	TRUE		# Dark subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+
+# binned output image options
+BIN1.XBIN		S32	12
+BIN1.YBIN		S32	12
+BIN2.XBIN		S32	96
+BIN2.YBIN		S32	96
+
+# cfh12k needs fringe subtraction
+FRINGE		BOOL	TRUE		# Fringe subtraction
+
+# this table lists extra constraints which should be applied when
+# selecting the detrend images.
+# note: camera and time are always applied
+
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+#   EXPTIME STR FPA.EXPOSURE
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+#   DETTYPE STR DOMEFLAT.RAW
+#   VERSION STR SKYFLAT.RAW
+#   VERSION STR DOMEFLAT.CORR.XX
+#   VERSION STR SKYFLAT.CORR
+  END
+  FLAT_CORRECTION METADATA
+    FILTER   STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+#   AIRMASS  STR FPA.AIRMASS
+#   TWILIGHT STR FPA.TWILIGHT
+  END
+  SHUTTER METADATA
+  END	
+END
+
+# only apply the fringe for these filters
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS	STR r
+FRINGE.FILTERS	STR i
+FRINGE.FILTERS	STR z
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/ppMerge.config	(revision 22322)
@@ -0,0 +1,43 @@
+
+# Bias combination --- don't want min/max rejection
+PPMERGE_BIAS	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
+   WEIGHTS	BOOL	FALSE		# Use image weights?
+   COMBINE	STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Dark combination --- don't want min/max rejection
+# More aggressive clipping than bias, so as to remove CRs
+PPMERGE_DARK	METADATA
+  REJ		F32	2.0		# Rejection threshold (sigma)
+  ITER		S32	4		# 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
+  WEIGHTS	BOOL	FALSE		# Use image weights?
+  COMBINE	STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Flat combination --- use min/max rejection
+PPMERGE_FLAT	METADATA
+	REJ		F32	3.0		# Rejection threshold (sigma)
+	ITER		S32	1		# Number of rejection iterations
+	FRACHIGH	F32	0.3		# Fraction of high pixels to reject immediately
+	FRACLOW		F32	0.1		# Fraction of low pixels to reject immediately
+	NKEEP		S32	5		# Minimum number of pixels in stack to keep
+	WEIGHTS		BOOL	TRUE		# Use image weights?
+	COMBINE		STR	MEAN		# Statistic to use for combination: 
+END
+
+
+# Fringe combination --- already included in default, above
+PPMERGE_FRINGE	METADATA
+	FRACHIGH	F32	0.1		# Fraction of high pixels to reject immediately
+	WEIGHTS		BOOL	TRUE		# Use image weights?
+END
+
+# Mask generation --- already included in default, above
+PPMERGE_MASK	METADATA
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/psastro.config	(revision 22322)
@@ -0,0 +1,79 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (mm / pixel)
+PSASTRO.PIXEL.SCALE    F32  1.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32   2500.0
+PSASTRO.GRID.SCALE     F32     50.0
+
+# extra field for ref stars:
+PSASTRO.FIELD.PADDING  F32 1.0
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# single-chip radius match in pixels
+PSASTRO.MATCH.FIT.NITER S32   3
+PSASTRO.MATCH.RADIUS    F32   12.0
+PSASTRO.MATCH.RADIUS.N0 F32   15.0
+PSASTRO.MATCH.RADIUS.N1 F32   10.0
+PSASTRO.MATCH.RADIUS.N2 F32    5.0
+
+PSASTRO.MAX.NRAW       S32      1500   # max stars accepted for fitting (0 for all)
+PSASTRO.MAX.NREF       S32      1500   # max stars accepted for fitting (0 for all)
+
+PSASTRO.MIN.INST.MAG.RAW       F32      -17.0  # min instrumental magnitude for stars accepted for fitting
+PSASTRO.MAX.INST.MAG.RAW       F32       -8.0  # max instrumental magnitude for stars accepted for fitting
+
+PSASTRO.MAX.ERROR      F32      10.0 # max error in pixels
+PSASTRO.MIN.NSTAR      S32      10   # min fitted stars in solution
+
+PSASTRO.MOSAIC.MODE           BOOL     FALSE
+
+PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+PSASTRO.MOSAIC.RADIUS.N0    F32   10.0 # initial match (after chip astrom)
+PSASTRO.MOSAIC.RADIUS.N1    F32    8.0 # (after first mosaic pass)
+PSASTRO.MOSAIC.RADIUS.N2    F32    8.0 # do not refine the match
+PSASTRO.MOSAIC.RADIUS.N3    F32    4.0 # 
+
+PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      3 # fit order (-1 means use default)
+
+PSASTRO.MOSAIC.GRADIENT.NX    S32     2   # number of x-dir cells per chip
+PSASTRO.MOSAIC.GRADIENT.NY    S32     4   # number of y-dir cells per chip
+
+# use this recipe to set a tight constraint
+PSASTRO.FINE METADATA
+  PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+  PSASTRO.MOSAIC.RADIUS.N0    F32    8.0
+  PSASTRO.MOSAIC.RADIUS.N1    F32    0.0
+  PSASTRO.MOSAIC.RADIUS.N2    F32    4.0
+  PSASTRO.MOSAIC.RADIUS.N3    F32    4.0
+
+  PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      3 # fit order (-1 means use default)
+END
+
+PSASTRO.GRID.MIN.SCALE F32  0.98
+PSASTRO.GRID.MAX.SCALE F32  1.04
+PSASTRO.GRID.DEL.SCALE F32  0.02
+PSASTRO.GRID.MIN.SIGMA F32  5.0
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/psphot.config	(revision 22322)
@@ -0,0 +1,40 @@
+
+# turn these on to see specific outputs
+SAVE.OUTPUT	BOOL 	TRUE
+SAVE.BACKMDL	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+SAVE.PLOTS	BOOL 	TRUE
+
+BACKGROUND.XBIN	    S32   128           # size of background superpixels
+BACKGROUND.YBIN	    S32   128           # size of background superpixels
+
+# image background parameters
+IMSTATS_NPIX        S32  10000    	 # number of pixels to use for sky estimate boxes:
+
+PSF_SN_LIM          F32  50              # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+PEAKS_NSIGMA_LIMIT     F32   25.0            # peak significance threshold
+PEAKS_NSIGMA_LIMIT_2   F32   10.0            # peak significance threshold
+
+# PSF model parameters : choose the PSF model desired
+PSF_MODEL           STR  PS_MODEL_QGAUSS
+
+MOMENTS_SN_MIN       F32  20.0           # min S/N to measure moments
+EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+FULL_FIT_SN_LIM      F32  50.0
+AP_MIN_SN            F32  25.0
+MEASURE.APTREND	                    BOOL  FALSE
+
+# BREAK_POINT may be one of (PEAKS, MOMENTS, PSFMODEL, ENSEMBLE)
+# BREAK_POINT         STR ENSEMBLE
+
+OUTPUT.FORMAT       STR PS1_DEV_1
+
+# masking parameters (XXX EAM : rework this to use psRegion like ANALYSIS_REGION)
+XMIN                F32   +10        	 # minimum valid x-coord
+XMAX                F32   -10        	 # maximum valid x-coord
+YMIN                F32   +10        	 # minimum valid y-coord
+YMAX                F32   -10        	 # maximum valid y-coord
+
+USE_FOOTPRINTS                      BOOL  TRUE       	  # use new pmFootprint peak packaging
Index: /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/cfh12k/rejections.config	(revision 22322)
@@ -0,0 +1,49 @@
+
+BIAS METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0
+  IMFILE.STDEV       F32 10.0
+  IMFILE.MEANSTDEV   F32  5.0
+  IMFILE.SKEWNESS    F32  0.0
+  IMFILE.KURTOSIS    F32  0.0
+  IMFILE.SNR         F32  0.0
+  IMFILE.BIN.STDEV   F32  5.0
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  2.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  5.0
+  EXP.SNR            F32  0.0
+  EXP.BIN.STDEV      F32  5.0
+  EXP.BIN.SNR        F32  0.0
+  EXP.FLUX           F32  0.0
+  ENSEMBLE.MEAN      F32  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+DARK METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  0.0
+  IMFILE.STDEV       F32 10.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  2.0
+  EXP.MEAN           F32  2.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  2.0
+  EXP.SNR            F32  0.0
+  EXP.BIN.STDEV      F32  0.0
+  EXP.BIN.SNR        F32  0.0
+  EXP.FLUX           F32  2.0
+  ENSEMBLE.MEAN      F32  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/configure.ac	(revision 22322)
@@ -0,0 +1,32 @@
+AC_PREREQ(2.59)
+
+AC_INIT([ippConfig], [1.1.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([ipprc.config.in])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_MAINTAINER_MODE
+
+AC_PROG_INSTALL
+
+AC_CONFIG_FILES([
+  Makefile
+  recipes/Makefile
+  isp/Makefile
+  gpc1/Makefile
+  cfh12k/Makefile
+  megacam/Makefile
+  mosaic2/Makefile
+  simple/Makefile
+  simmosaic/Makefile
+  simtest/Makefile
+  vysos5/Makefile
+  tc3/Makefile
+  skycell/Makefile
+  sdssmosaic/Makefile
+  sdss/Makefile
+  esowfi/Makefile
+  lbc_red/Makefile
+  lulin/Makefile
+  uh8k/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ippconfig/dvo.photcodes
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/dvo.photcodes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/dvo.photcodes	(revision 22322)
@@ -0,0 +1,885 @@
+
+# this file contains the list of valid photometry codes.  there should
+# be one code for each combination of CCD, filter, and telescope (and
+# possibly additional ones if filter transmission reduces with time).
+# The code on each line is a unique number, while the name is a
+# representative name.  The type defines how the photometry is
+# treated.  There can only be one primary photcode.  There should only
+# be a few secondary photcodes: these are the calibrated values for
+# stars.  The dependent photcodes are those which are equivalent to a
+# primary or secondary photcode, and are equated after photometric
+# solutions.  Reference photcodes are for anything without a dynamic
+# solution: the reference values are provided externally.
+
+# NOTE: the photcodes defined below supplement CFHT Elixir values.
+# Do not use this photcode file with old CFHT DVO databases.
+
+# camera ranges used in this file:
+# megacam:  u: 100-139, g: 200-239, r: 300-339, i: 400-439, z: 500-539
+# cfh12k:   B: 140-152, V: 240-252, R: 340-352, I: 440-452, Z: 540-552, NB: 600-693
+# mosaic2:  B: 160-167, V: 260-267, VR: 360-367, R: 460-467, I: 560-567
+# PS1 ISP:  g: 3200,      r: 3300,      i: 3400,      z: 3500,      y: 3600
+# PS1 TC3:  g: 3280-3296, r: 3380-3396, i: 3480-3496, z: 3580-3596, y: 3680-3696, 
+# PS1 GPC1: g: 3201-3264, r: 3301-3364, i: 3401-3464, z: 3501-3564, y: 3601-3664
+
+# external references: 1000 - 2999
+
+#                                                                                       astrometry  photometry     astr mask      phot mask
+# code name		    type  zero  airmass offset  c1    c2  slope  <color> eq     sys  scale  sys  scale     poor   bad     poor   bad 
+  1     g                    sec   0.000  0.000 0.000  1003  1004 0.0160     0  1051   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  2     r                    sec   0.000  0.000 0.000  1003  1004 0.0080     0  1052   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3     i                    sec   0.000  0.000 0.000  1003  1004 0.0280     0  1053   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4     z                    sec   0.000  0.000 0.000  1003  1004 0.1070     0  1054   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  5     y                    sec   0.000  0.000 0.000     -     - 0.0000     0  1054   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  1010  U_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1011  B_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1012  V_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1013  R_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1014  I_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  1020  U_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1021  B_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1022  V_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1023  R_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1024  I_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  1050  u_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1051  g_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1052  r_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1053  i_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1054  z_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  1055  U_SDSS               dep  25.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1056  G_SDSS               dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1057  R_SDSS               dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1058  I_SDSS               dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1059  Z_SDSS               dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+# the 30 different SDSS photcodes for the SDSSmosaic camera... 6 columns of CCDs (camcols) x 5 filters.
+# Added by Sebastian Jester jester@mpia.de
+# These likely need work!
+  1060  SDSS.r.11            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1061  SDSS.r.12            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1062  SDSS.r.13            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1063  SDSS.r.14            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1064  SDSS.r.15            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1065  SDSS.r.16            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1066  SDSS.i.21            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1067  SDSS.i.22            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1068  SDSS.i.23            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1069  SDSS.i.24            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1070  SDSS.i.25            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1071  SDSS.i.26            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1072  SDSS.u.31            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1073  SDSS.u.32            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1074  SDSS.u.33            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1075  SDSS.u.34            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1076  SDSS.u.35            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1077  SDSS.u.36            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1078  SDSS.z.41            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1079  SDSS.z.42            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1080  SDSS.z.43            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1081  SDSS.z.44            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1082  SDSS.z.45            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1083  SDSS.z.46            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1084  SDSS.g.51            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1085  SDSS.g.52            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1086  SDSS.g.53            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1087  SDSS.g.54            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1088  SDSS.g.55            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1089  SDSS.g.56            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+													       																		
+  1100  GSC                  ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  1000  USNO_BLUE            ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1001  USNO_RED             ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1002  USNO_J               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1003  USNO_F               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1004  USNO_N               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  1110  USNO.098.RED         dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1111  USNO.098-0.RED70     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1112  USNO.098-0.RG630     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1113  USNO.103AD.MULTI     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1114  USNO.103AD.YEL3      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1115  USNO.103AD.YEL8      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1116  USNO.103AE.#12       dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1117  USNO.103AE.AMB2      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1118  USNO.103AE.AMB3      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1119  USNO.103AE.AMB4      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1120  USNO.103AE.AMB5      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1121  USNO.103AE.AMB6      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1122  USNO.103AE.AMB7      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1123  USNO.103AE.AMB8      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1124  USNO.103AE.NONE      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1125  USNO.103AE.RED66     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1126  USNO.103AE.RED67     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1127  USNO.103AE.RED68     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1128  USNO.103AE.RED69     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1129  USNO.103AE.RED70     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1130  USNO.103AE.RED71     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1131  USNO.103AE.RED73     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1132  USNO.103AE.RG2444    dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1133  USNO.103AE.RP2444    dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1134  USNO.103AO.NONE      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1135  USNO.IIIAF.OG590     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1136  USNO.IIIAF.RG600     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1137  USNO.IIIAF.RG610     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1138  USNO.IIIAF.RG630     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1139  USNO.IIIAJ.GG358     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1140  USNO.IIIAJ.GG385     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1141  USNO.IIIAJ.GG395     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1142  USNO.IVN.RG715       dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1143  USNO.IVN.RG9         dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1144  USNO.IVN.WR88A       dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  2020  TYCHO_B              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  2021  TYCHO_V              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  2011  2MASS_J              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.050 0.000 1.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  2012  2MASS_H              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.050 0.000 1.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  2013  2MASS_K              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.050 0.000 1.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+# these were used for the synthetic photometry catalog based on 2mass					       										
+  3001  PS_g                 ref   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3002  PS_r                 ref   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3003  PS_i                 ref   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3004  PS_z                 ref   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3005  PS_y                 ref   0.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+### megacam photcodes:											       																
+  100   MEGACAM.u.00         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  101   MEGACAM.u.01         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  102   MEGACAM.u.02         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  103   MEGACAM.u.03         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  104   MEGACAM.u.04         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  105   MEGACAM.u.05         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  106   MEGACAM.u.06         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  107   MEGACAM.u.07         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  108   MEGACAM.u.08         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  109   MEGACAM.u.09         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  110   MEGACAM.u.10         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  111   MEGACAM.u.11         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  112   MEGACAM.u.12         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  113   MEGACAM.u.13         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  114   MEGACAM.u.14         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  115   MEGACAM.u.15         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  116   MEGACAM.u.16         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  117   MEGACAM.u.17         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  118   MEGACAM.u.18         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  119   MEGACAM.u.19         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  120   MEGACAM.u.20         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  121   MEGACAM.u.21         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  122   MEGACAM.u.22         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  123   MEGACAM.u.23         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  124   MEGACAM.u.24         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  125   MEGACAM.u.25         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  126   MEGACAM.u.26         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  127   MEGACAM.u.27         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  128   MEGACAM.u.28         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  129   MEGACAM.u.29         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  130   MEGACAM.u.30         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  131   MEGACAM.u.31         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  132   MEGACAM.u.32         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  133   MEGACAM.u.33         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  134   MEGACAM.u.34         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  135   MEGACAM.u.35         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  136   MEGACAM.u.36         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  137   MEGACAM.u.37         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  138   MEGACAM.u.38         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  139   MEGACAM.u.39         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+													       																		
+  200   MEGACAM.g.00         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  201   MEGACAM.g.01         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  202   MEGACAM.g.02         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  203   MEGACAM.g.03         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  204   MEGACAM.g.04         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  205   MEGACAM.g.05         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  206   MEGACAM.g.06         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  207   MEGACAM.g.07         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  208   MEGACAM.g.08         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  209   MEGACAM.g.09         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  210   MEGACAM.g.10         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  211   MEGACAM.g.11         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  212   MEGACAM.g.12         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  213   MEGACAM.g.13         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  214   MEGACAM.g.14         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  215   MEGACAM.g.15         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  216   MEGACAM.g.16         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  217   MEGACAM.g.17         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  218   MEGACAM.g.18         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  219   MEGACAM.g.19         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  220   MEGACAM.g.20         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  221   MEGACAM.g.21         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  222   MEGACAM.g.22         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  223   MEGACAM.g.23         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  224   MEGACAM.g.24         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  225   MEGACAM.g.25         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  226   MEGACAM.g.26         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  227   MEGACAM.g.27         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  228   MEGACAM.g.28         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  229   MEGACAM.g.29         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  230   MEGACAM.g.30         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  231   MEGACAM.g.31         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  232   MEGACAM.g.32         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  233   MEGACAM.g.33         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  234   MEGACAM.g.34         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  235   MEGACAM.g.35         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  236   MEGACAM.g.36         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  237   MEGACAM.g.37         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  238   MEGACAM.g.38         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  239   MEGACAM.g.39         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+													       																		
+  300   MEGACAM.r.00         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  301   MEGACAM.r.01         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  302   MEGACAM.r.02         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  303   MEGACAM.r.03         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  304   MEGACAM.r.04         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  305   MEGACAM.r.05         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  306   MEGACAM.r.06         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  307   MEGACAM.r.07         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  308   MEGACAM.r.08         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  309   MEGACAM.r.09         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  310   MEGACAM.r.10         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  311   MEGACAM.r.11         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  312   MEGACAM.r.12         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  313   MEGACAM.r.13         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  314   MEGACAM.r.14         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  315   MEGACAM.r.15         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  316   MEGACAM.r.16         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  317   MEGACAM.r.17         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  318   MEGACAM.r.18         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  319   MEGACAM.r.19         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  320   MEGACAM.r.20         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  321   MEGACAM.r.21         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  322   MEGACAM.r.22         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  323   MEGACAM.r.23         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  324   MEGACAM.r.24         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  325   MEGACAM.r.25         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  326   MEGACAM.r.26         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  327   MEGACAM.r.27         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  328   MEGACAM.r.28         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  329   MEGACAM.r.29         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  330   MEGACAM.r.30         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  331   MEGACAM.r.31         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  332   MEGACAM.r.32         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  333   MEGACAM.r.33         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  334   MEGACAM.r.34         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  335   MEGACAM.r.35         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  336   MEGACAM.r.36         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  337   MEGACAM.r.37         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  338   MEGACAM.r.38         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  339   MEGACAM.r.39         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+													       																		
+  400   MEGACAM.i.00         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  401   MEGACAM.i.01         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  402   MEGACAM.i.02         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  403   MEGACAM.i.03         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  404   MEGACAM.i.04         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  405   MEGACAM.i.05         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  406   MEGACAM.i.06         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  407   MEGACAM.i.07         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  408   MEGACAM.i.08         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  409   MEGACAM.i.09         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  410   MEGACAM.i.10         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  411   MEGACAM.i.11         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  412   MEGACAM.i.12         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  413   MEGACAM.i.13         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  414   MEGACAM.i.14         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  415   MEGACAM.i.15         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  416   MEGACAM.i.16         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  417   MEGACAM.i.17         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  418   MEGACAM.i.18         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  419   MEGACAM.i.19         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  420   MEGACAM.i.20         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  421   MEGACAM.i.21         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  422   MEGACAM.i.22         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  423   MEGACAM.i.23         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  424   MEGACAM.i.24         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  425   MEGACAM.i.25         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  426   MEGACAM.i.26         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  427   MEGACAM.i.27         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  428   MEGACAM.i.28         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  429   MEGACAM.i.29         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  430   MEGACAM.i.30         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  431   MEGACAM.i.31         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  432   MEGACAM.i.32         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  433   MEGACAM.i.33         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  434   MEGACAM.i.34         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  435   MEGACAM.i.35         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  436   MEGACAM.i.36         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  437   MEGACAM.i.37         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  438   MEGACAM.i.38         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  439   MEGACAM.i.39         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+													       																		
+  500   MEGACAM.z.00         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  501   MEGACAM.z.01         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  502   MEGACAM.z.02         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  503   MEGACAM.z.03         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  504   MEGACAM.z.04         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  505   MEGACAM.z.05         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  506   MEGACAM.z.06         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  507   MEGACAM.z.07         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  508   MEGACAM.z.08         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  509   MEGACAM.z.09         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  510   MEGACAM.z.10         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  511   MEGACAM.z.11         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  512   MEGACAM.z.12         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  513   MEGACAM.z.13         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  514   MEGACAM.z.14         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  515   MEGACAM.z.15         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  516   MEGACAM.z.16         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  517   MEGACAM.z.17         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  518   MEGACAM.z.18         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  519   MEGACAM.z.19         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  520   MEGACAM.z.20         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  521   MEGACAM.z.21         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  522   MEGACAM.z.22         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  523   MEGACAM.z.23         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  524   MEGACAM.z.24         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  525   MEGACAM.z.25         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  526   MEGACAM.z.26         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  527   MEGACAM.z.27         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  528   MEGACAM.z.28         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  529   MEGACAM.z.29         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  530   MEGACAM.z.30         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  531   MEGACAM.z.31         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  532   MEGACAM.z.32         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  533   MEGACAM.z.33         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  534   MEGACAM.z.34         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  535   MEGACAM.z.35         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  536   MEGACAM.z.36         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  537   MEGACAM.z.37         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  538   MEGACAM.z.38         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  539   MEGACAM.z.39         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+													       																		
+### cfh12k photcodes											       																
+  140   CFH12K.B.00          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  141   CFH12K.B.01          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  142   CFH12K.B.02          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  143   CFH12K.B.03          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  144   CFH12K.B.04          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  145   CFH12K.B.05          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  146   CFH12K.B.06          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  147   CFH12K.B.07          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  148   CFH12K.B.08          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  149   CFH12K.B.09          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  150   CFH12K.B.10          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  151   CFH12K.B.11          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+													       																		
+  240   CFH12K.V.00          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  241   CFH12K.V.01          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  242   CFH12K.V.02          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  243   CFH12K.V.03          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  244   CFH12K.V.04          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  245   CFH12K.V.05          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  246   CFH12K.V.06          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  247   CFH12K.V.07          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  248   CFH12K.V.08          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  249   CFH12K.V.09          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  250   CFH12K.V.10          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  251   CFH12K.V.11          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+													       																		
+  340   CFH12K.R.00          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  341   CFH12K.R.01          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  342   CFH12K.R.02          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  343   CFH12K.R.03          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  344   CFH12K.R.04          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  345   CFH12K.R.05          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  346   CFH12K.R.06          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  347   CFH12K.R.07          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  348   CFH12K.R.08          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  349   CFH12K.R.09          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  350   CFH12K.R.10          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  351   CFH12K.R.11          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+													       																		
+  440   CFH12K.I.00          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  441   CFH12K.I.01          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  442   CFH12K.I.02          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  443   CFH12K.I.03          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  444   CFH12K.I.04          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  445   CFH12K.I.05          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  446   CFH12K.I.06          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  447   CFH12K.I.07          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  448   CFH12K.I.08          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  449   CFH12K.I.09          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  450   CFH12K.I.10          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  451   CFH12K.I.11          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+													       																		
+  540   CFH12K.Z.00          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  541   CFH12K.Z.01          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  542   CFH12K.Z.02          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  543   CFH12K.Z.03          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  544   CFH12K.Z.04          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  545   CFH12K.Z.05          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  546   CFH12K.Z.06          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  547   CFH12K.Z.07          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  548   CFH12K.Z.08          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  549   CFH12K.Z.09          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  550   CFH12K.Z.10          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  551   CFH12K.Z.11          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+													       																		
+  600   CFH12K.Ha.00         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  601   CFH12K.Ha.01         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  602   CFH12K.Ha.02         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  603   CFH12K.Ha.03         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  604   CFH12K.Ha.04         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  605   CFH12K.Ha.05         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  606   CFH12K.Ha.06         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  607   CFH12K.Ha.07         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  608   CFH12K.Ha.08         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  609   CFH12K.Ha.09         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  610   CFH12K.Ha.10         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  611   CFH12K.Ha.11         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  612   CFH12K.HaOff.00      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  613   CFH12K.HaOff.01      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  614   CFH12K.HaOff.02      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  615   CFH12K.HaOff.03      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  616   CFH12K.HaOff.04      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  617   CFH12K.HaOff.05      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  618   CFH12K.HaOff.06      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  619   CFH12K.HaOff.07      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  620   CFH12K.HaOff.08      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  621   CFH12K.HaOff.09      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  622   CFH12K.HaOff.10      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  623   CFH12K.HaOff.11      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  624   CFH12K.CN.00         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  625   CFH12K.CN.01         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  626   CFH12K.CN.02         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  627   CFH12K.CN.03         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  628   CFH12K.CN.04         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  629   CFH12K.CN.05         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  630   CFH12K.CN.06         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  631   CFH12K.CN.07         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  632   CFH12K.CN.08         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  633   CFH12K.CN.09         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  634   CFH12K.CN.10         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  635   CFH12K.CN.11         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  636   CFH12K.TiO.00        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  637   CFH12K.TiO.01        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  638   CFH12K.TiO.02        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  639   CFH12K.TiO.03        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  640   CFH12K.TiO.04        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  641   CFH12K.TiO.05        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  642   CFH12K.TiO.06        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  643   CFH12K.TiO.07        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  644   CFH12K.TiO.08        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  645   CFH12K.TiO.09        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  646   CFH12K.TiO.10        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  647   CFH12K.TiO.11        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  648   CFH12K.NB920.00      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  649   CFH12K.NB920.01      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  650   CFH12K.NB920.02      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  651   CFH12K.NB920.03      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  652   CFH12K.NB920.04      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  653   CFH12K.NB920.05      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  654   CFH12K.NB920.06      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  655   CFH12K.NB920.07      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  678   CFH12K.NB920.08      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  679   CFH12K.NB920.09      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  680   CFH12K.NB920.10      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  681   CFH12K.NB920.11      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  682   CFH12K.B2F.00        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  683   CFH12K.B2F.01        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  684   CFH12K.B2F.02        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  685   CFH12K.B2F.03        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  686   CFH12K.B2F.04        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  687   CFH12K.B2F.05        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  688   CFH12K.B2F.06        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  689   CFH12K.B2F.07        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  690   CFH12K.B2F.08        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  691   CFH12K.B2F.09        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  692   CFH12K.B2F.10        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  693   CFH12K.B2F.11        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  3200  ISP-Apogee-01.g      dep  19.250  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3300  ISP-Apogee-01.r      dep  19.150  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3400  ISP-Apogee-01.i      dep  18.500  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3500  ISP-Apogee-01.z      dep  17.250  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3600  ISP-Apogee-01.y      dep  14.150  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  4100  SIMTEST.g            dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4200  SIMTEST.r            dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4300  SIMTEST.i            dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4400  SIMTEST.z            dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4500  SIMTEST.y            dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+  4101  SIMMOSAIC.g.00       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4102  SIMMOSAIC.g.01       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4103  SIMMOSAIC.g.02       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4104  SIMMOSAIC.g.03       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4201  SIMMOSAIC.r.00       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4202  SIMMOSAIC.r.01       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4203  SIMMOSAIC.r.02       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4204  SIMMOSAIC.r.03       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4301  SIMMOSAIC.i.00       dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4302  SIMMOSAIC.i.01       dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4303  SIMMOSAIC.i.02       dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4304  SIMMOSAIC.i.03       dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4401  SIMMOSAIC.z.00       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4402  SIMMOSAIC.z.01       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4403  SIMMOSAIC.z.02       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4404  SIMMOSAIC.z.03       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4501  SIMMOSAIC.y.00       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4502  SIMMOSAIC.y.01       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4503  SIMMOSAIC.y.02       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4504  SIMMOSAIC.y.03       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+# 2007.10.02 : predicted g zero-points from 2006 NSF proposal						       											
+  10001 GPC1.g.XY01          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10002 GPC1.g.XY02          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10003 GPC1.g.XY03          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10004 GPC1.g.XY04          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10005 GPC1.g.XY05          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10006 GPC1.g.XY06          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10010 GPC1.g.XY10          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10011 GPC1.g.XY11          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10012 GPC1.g.XY12          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10013 GPC1.g.XY13          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10014 GPC1.g.XY14          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10015 GPC1.g.XY15          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10016 GPC1.g.XY16          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10017 GPC1.g.XY17          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10020 GPC1.g.XY20          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10021 GPC1.g.XY21          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10022 GPC1.g.XY22          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10023 GPC1.g.XY23          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10024 GPC1.g.XY24          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10025 GPC1.g.XY25          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10026 GPC1.g.XY26          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10027 GPC1.g.XY27          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10030 GPC1.g.XY30          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10031 GPC1.g.XY31          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10032 GPC1.g.XY32          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10033 GPC1.g.XY33          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10034 GPC1.g.XY34          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10035 GPC1.g.XY35          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10036 GPC1.g.XY36          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10037 GPC1.g.XY37          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10040 GPC1.g.XY40          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10041 GPC1.g.XY41          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10042 GPC1.g.XY42          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10043 GPC1.g.XY43          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10044 GPC1.g.XY44          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10045 GPC1.g.XY45          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10046 GPC1.g.XY46          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10047 GPC1.g.XY47          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10050 GPC1.g.XY50          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10051 GPC1.g.XY51          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10052 GPC1.g.XY52          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10053 GPC1.g.XY53          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10054 GPC1.g.XY54          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10055 GPC1.g.XY55          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10056 GPC1.g.XY56          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10057 GPC1.g.XY57          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10060 GPC1.g.XY60          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10061 GPC1.g.XY61          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10062 GPC1.g.XY62          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10063 GPC1.g.XY63          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10064 GPC1.g.XY64          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10065 GPC1.g.XY65          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10066 GPC1.g.XY66          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10067 GPC1.g.XY67          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10071 GPC1.g.XY71          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10072 GPC1.g.XY72          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10073 GPC1.g.XY73          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10074 GPC1.g.XY74          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10075 GPC1.g.XY75          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10076 GPC1.g.XY76          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+# 2007.10.02 : predicted r zero-points from 2006 NSF proposal						       											
+  10101 GPC1.r.XY01          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10102 GPC1.r.XY02          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10103 GPC1.r.XY03          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10104 GPC1.r.XY04          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10105 GPC1.r.XY05          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10106 GPC1.r.XY06          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10110 GPC1.r.XY10          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10111 GPC1.r.XY11          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10112 GPC1.r.XY12          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10113 GPC1.r.XY13          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10114 GPC1.r.XY14          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10115 GPC1.r.XY15          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10116 GPC1.r.XY16          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10117 GPC1.r.XY17          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10120 GPC1.r.XY20          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10121 GPC1.r.XY21          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10122 GPC1.r.XY22          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10123 GPC1.r.XY23          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10124 GPC1.r.XY24          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10125 GPC1.r.XY25          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10126 GPC1.r.XY26          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10127 GPC1.r.XY27          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10130 GPC1.r.XY30          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10131 GPC1.r.XY31          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10132 GPC1.r.XY32          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10133 GPC1.r.XY33          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10134 GPC1.r.XY34          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10135 GPC1.r.XY35          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10136 GPC1.r.XY36          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10137 GPC1.r.XY37          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10140 GPC1.r.XY40          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10141 GPC1.r.XY41          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10142 GPC1.r.XY42          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10143 GPC1.r.XY43          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10144 GPC1.r.XY44          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10145 GPC1.r.XY45          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10146 GPC1.r.XY46          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10147 GPC1.r.XY47          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10150 GPC1.r.XY50          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10151 GPC1.r.XY51          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10152 GPC1.r.XY52          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10153 GPC1.r.XY53          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10154 GPC1.r.XY54          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10155 GPC1.r.XY55          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10156 GPC1.r.XY56          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10157 GPC1.r.XY57          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10160 GPC1.r.XY60          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10161 GPC1.r.XY61          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10162 GPC1.r.XY62          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10163 GPC1.r.XY63          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10164 GPC1.r.XY64          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10165 GPC1.r.XY65          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10166 GPC1.r.XY66          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10167 GPC1.r.XY67          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10171 GPC1.r.XY71          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10172 GPC1.r.XY72          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10173 GPC1.r.XY73          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10174 GPC1.r.XY74          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10175 GPC1.r.XY75          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10176 GPC1.r.XY76          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+# 2007.10.02 : predicted i zero-points from 2006 NSF proposal						       											
+  10201 GPC1.i.XY01          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10202 GPC1.i.XY02          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10203 GPC1.i.XY03          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10204 GPC1.i.XY04          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10205 GPC1.i.XY05          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10206 GPC1.i.XY06          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10210 GPC1.i.XY10          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10211 GPC1.i.XY11          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10212 GPC1.i.XY12          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10213 GPC1.i.XY13          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10214 GPC1.i.XY14          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10215 GPC1.i.XY15          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10216 GPC1.i.XY16          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10217 GPC1.i.XY17          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10220 GPC1.i.XY20          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10221 GPC1.i.XY21          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10222 GPC1.i.XY22          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10223 GPC1.i.XY23          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10224 GPC1.i.XY24          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10225 GPC1.i.XY25          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10226 GPC1.i.XY26          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10227 GPC1.i.XY27          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10230 GPC1.i.XY30          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10231 GPC1.i.XY31          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10232 GPC1.i.XY32          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10233 GPC1.i.XY33          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10234 GPC1.i.XY34          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10235 GPC1.i.XY35          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10236 GPC1.i.XY36          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10237 GPC1.i.XY37          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10240 GPC1.i.XY40          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10241 GPC1.i.XY41          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10242 GPC1.i.XY42          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10243 GPC1.i.XY43          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10244 GPC1.i.XY44          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10245 GPC1.i.XY45          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10246 GPC1.i.XY46          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10247 GPC1.i.XY47          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10250 GPC1.i.XY50          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10251 GPC1.i.XY51          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10252 GPC1.i.XY52          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10253 GPC1.i.XY53          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10254 GPC1.i.XY54          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10255 GPC1.i.XY55          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10256 GPC1.i.XY56          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10257 GPC1.i.XY57          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10260 GPC1.i.XY60          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10261 GPC1.i.XY61          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10262 GPC1.i.XY62          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10263 GPC1.i.XY63          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10264 GPC1.i.XY64          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10265 GPC1.i.XY65          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10266 GPC1.i.XY66          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10267 GPC1.i.XY67          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10271 GPC1.i.XY71          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10272 GPC1.i.XY72          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10273 GPC1.i.XY73          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10274 GPC1.i.XY74          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10275 GPC1.i.XY75          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10276 GPC1.i.XY76          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       																		
+# 2007.10.02 : predicted z zero-points from 2006 NSF proposal						       											
+  10301 GPC1.z.XY01          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10302 GPC1.z.XY02          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10303 GPC1.z.XY03          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10304 GPC1.z.XY04          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10305 GPC1.z.XY05          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10306 GPC1.z.XY06          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10310 GPC1.z.XY10          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10311 GPC1.z.XY11          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10312 GPC1.z.XY12          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10313 GPC1.z.XY13          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10314 GPC1.z.XY14          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10315 GPC1.z.XY15          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10316 GPC1.z.XY16          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10317 GPC1.z.XY17          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10320 GPC1.z.XY20          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10321 GPC1.z.XY21          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10322 GPC1.z.XY22          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10323 GPC1.z.XY23          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10324 GPC1.z.XY24          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10325 GPC1.z.XY25          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10326 GPC1.z.XY26          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10327 GPC1.z.XY27          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10330 GPC1.z.XY30          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10331 GPC1.z.XY31          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10332 GPC1.z.XY32          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10333 GPC1.z.XY33          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10334 GPC1.z.XY34          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10335 GPC1.z.XY35          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10336 GPC1.z.XY36          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10337 GPC1.z.XY37          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10340 GPC1.z.XY40          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10341 GPC1.z.XY41          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10342 GPC1.z.XY42          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10343 GPC1.z.XY43          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10344 GPC1.z.XY44          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10345 GPC1.z.XY45          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10346 GPC1.z.XY46          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10347 GPC1.z.XY47          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10350 GPC1.z.XY50          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10351 GPC1.z.XY51          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10352 GPC1.z.XY52          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10353 GPC1.z.XY53          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10354 GPC1.z.XY54          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10355 GPC1.z.XY55          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10356 GPC1.z.XY56          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10357 GPC1.z.XY57          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10360 GPC1.z.XY60          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10361 GPC1.z.XY61          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10362 GPC1.z.XY62          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10363 GPC1.z.XY63          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10364 GPC1.z.XY64          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10365 GPC1.z.XY65          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10366 GPC1.z.XY66          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10367 GPC1.z.XY67          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10371 GPC1.z.XY71          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10372 GPC1.z.XY72          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10373 GPC1.z.XY73          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10374 GPC1.z.XY74          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10375 GPC1.z.XY75          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10376 GPC1.z.XY76          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       										
+# 2007.10.02 : predicted y zero-points from 2006 NSF proposal						       											
+  10401 GPC1.y.XY01          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10402 GPC1.y.XY02          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10403 GPC1.y.XY03          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10404 GPC1.y.XY04          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10405 GPC1.y.XY05          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10406 GPC1.y.XY06          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10410 GPC1.y.XY10          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10411 GPC1.y.XY11          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10412 GPC1.y.XY12          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10413 GPC1.y.XY13          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10414 GPC1.y.XY14          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10415 GPC1.y.XY15          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10416 GPC1.y.XY16          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10417 GPC1.y.XY17          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10420 GPC1.y.XY20          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10421 GPC1.y.XY21          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10422 GPC1.y.XY22          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10423 GPC1.y.XY23          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10424 GPC1.y.XY24          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10425 GPC1.y.XY25          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10426 GPC1.y.XY26          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10427 GPC1.y.XY27          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10430 GPC1.y.XY30          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10431 GPC1.y.XY31          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10432 GPC1.y.XY32          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10433 GPC1.y.XY33          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10434 GPC1.y.XY34          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10435 GPC1.y.XY35          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10436 GPC1.y.XY36          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10437 GPC1.y.XY37          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10440 GPC1.y.XY40          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10441 GPC1.y.XY41          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10442 GPC1.y.XY42          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10443 GPC1.y.XY43          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10444 GPC1.y.XY44          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10445 GPC1.y.XY45          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10446 GPC1.y.XY46          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10447 GPC1.y.XY47          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10450 GPC1.y.XY50          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10451 GPC1.y.XY51          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10452 GPC1.y.XY52          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10453 GPC1.y.XY53          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10454 GPC1.y.XY54          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10455 GPC1.y.XY55          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10456 GPC1.y.XY56          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10457 GPC1.y.XY57          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10460 GPC1.y.XY60          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10461 GPC1.y.XY61          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10462 GPC1.y.XY62          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10463 GPC1.y.XY63          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10464 GPC1.y.XY64          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10465 GPC1.y.XY65          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10466 GPC1.y.XY66          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10467 GPC1.y.XY67          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10471 GPC1.y.XY71          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10472 GPC1.y.XY72          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10473 GPC1.y.XY73          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10474 GPC1.y.XY74          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10475 GPC1.y.XY75          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10476 GPC1.y.XY76          dep  23.600  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+# mosaic2 photcodes
+
+  160  MOSAIC2.B.00          dep  25.107  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  161  MOSAIC2.B.01          dep  25.120  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  162  MOSAIC2.B.02          dep  25.115  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  163  MOSAIC2.B.03          dep  25.111  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  164  MOSAIC2.B.04          dep  25.111  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  165  MOSAIC2.B.05          dep  25.101  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  166  MOSAIC2.B.06          dep  25.106  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  167  MOSAIC2.B.07          dep  25.104  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  260  MOSAIC2.V.00          dep  25.448  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  261  MOSAIC2.V.01          dep  25.472  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  262  MOSAIC2.V.02          dep  25.472  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  263  MOSAIC2.V.03          dep  25.476  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  264  MOSAIC2.V.04          dep  25.475  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  265  MOSAIC2.V.05          dep  25.471  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  266  MOSAIC2.V.06          dep  25.472  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  267  MOSAIC2.V.07          dep  25.475  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  360  MOSAIC2.VR.00         dep  25.636  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  361  MOSAIC2.VR.01         dep  25.633  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  362  MOSAIC2.VR.02         dep  25.648  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  363  MOSAIC2.VR.03         dep  25.632  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  364  MOSAIC2.VR.04         dep  25.628  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  365  MOSAIC2.VR.05         dep  25.616  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  366  MOSAIC2.VR.06         dep  25.629  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  367  MOSAIC2.VR.07         dep  25.624  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  460  MOSAIC2.R.00          dep  25.636  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  461  MOSAIC2.R.01          dep  25.633  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  462  MOSAIC2.R.02          dep  25.648  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  463  MOSAIC2.R.03          dep  25.632  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  464  MOSAIC2.R.04          dep  25.628  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  465  MOSAIC2.R.05          dep  25.616  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  466  MOSAIC2.R.06          dep  25.629  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  467  MOSAIC2.R.07          dep  25.624  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  560  MOSAIC2.I.00          dep  24.911  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  561  MOSAIC2.I.01          dep  24.882  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  562  MOSAIC2.I.02          dep  24.924  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  563  MOSAIC2.I.03          dep  24.906  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  564  MOSAIC2.I.04          dep  24.906  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  565  MOSAIC2.I.05          dep  24.877  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  566  MOSAIC2.I.06          dep  24.887  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  567  MOSAIC2.I.07          dep  24.912  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
Index: /tags/sj_tags/sj_root_20080929/ippconfig/dvo.site.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/dvo.site.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/dvo.site.in	(revision 22322)
@@ -0,0 +1,40 @@
+
+CONFDIR                 @pkgdatadir@
+CAMERA                  default
+
+# location of DVO database tables
+CATDIR			default
+
+# location of possible data sources
+2MASS_DIR_AS		NONE
+2MASS_DIR_DR2		NONE
+GSCDIR			NONE
+USNO_A_DIR		NONE
+USNO_B_DIR		NONE
+TYCHO_DIR		NONE
+
+PHOTCODE_FILE		$CONFDIR/dvo.photcodes
+GSCFILE			$CONFDIR/GSCregions.tbl
+CATMODE			SPLIT
+CATFORMAT		PS1_DEV_1
+SKY_DEPTH               3
+# SKY_TABLE		this may be used to override GSCFILE
+ZERO_PT			25.0
+
+# access control for client/server addstar mode 
+# PASSWORD
+# HOSTNAME
+# VALID_IP
+
+# camera dependent parameter files:
+FILTER_LIST             $CONFDIR/$CAMERA/dvo.filters
+CAMERA_CONFIG           $CONFDIR/$CAMERA/dvo.layout
+DETREND_RECIPES         $CONFDIR/$CAMERA/dvo.recipe
+
+# load the camera-specific configuration files
+input                   $CONFDIR/$CAMERA/dvo.config
+
+PANTASKS_SERVER         XXXXX.ifa.hawaii.edu
+PASSWORD                XXXXX
+#PANTASKS_SERVER_STDOUT  pantasks.stdout.log
+#PANTASKS_SERVER_STDERR  pantasks.stderr.log
Index: /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/Makefile.am	(revision 22322)
@@ -0,0 +1,21 @@
+
+installdir = $(datadir)/ippconfig/esowfi
+
+install_files = \
+	dvo.config \
+	camera.config \
+	format_split.config \
+	format_together.config \
+	ppImage.config \
+	psphot.config \
+	psastro.config \
+	rejections.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/camera.config	(revision 22322)
@@ -0,0 +1,103 @@
+# Camera configuration file for simulated mosaic
+
+# File formats that we know about
+FORMATS         METADATA
+  TOGETHER STR  esowfi/format_together.config
+  SPLIT    STR  esowfi/format_split.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        Chip50    STR  Cell00
+        Chip51    STR  Cell00
+        Chip52    STR  Cell00
+        Chip53    STR  Cell00
+        Chip54    STR  Cell00
+        Chip55    STR  Cell00
+        Chip56    STR  Cell00
+        Chip57    STR  Cell00
+END
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+  NONE  STR  NONE
+  416  STR    416/29_ESO872
+  U    STR    U/50_ESO877
+  B    STR    B/123_ESO878
+  V    STR    V/89_ESO843
+  R    STR    Rc/162_ESO844
+  I    STR    I/203_ESO879
+  #g  STR    g
+  #r  STR    r
+  #i  STR    i
+  #z  STR    z
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID    METADATA
+  FPA   STR  fpa
+  CHIP  STR  {CHIP.NAME}
+  CELL  STR  {CHIP.NAME}.{CELL.NAME}
+  fpa   STR  fpa
+  chip  STR  {CHIP.NAME}
+  cell  STR  {CHIP.NAME}.{CELL.NAME}
+END
+
+DVO.CAMERADIR  STR  esowfi    # Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias     STR BIAS
+  skyflat  STR FLAT,SKY
+  domeflat STR FLAT,SCREEN
+  object   STR OBJECT
+  science  STR OBJECT
+  #zero    STR BIAS
+  #dark    STR DARK
+  #flat    STR SKYFLAT
+END
+
+# Recipe options
+RECIPES    METADATA
+  PPIMAGE     STR    esowfi/ppImage.config     # Default: all (normal) options on
+  PSPHOT      STR    esowfi/psphot.config      # psphot details
+  PSASTRO     STR    esowfi/psastro.config     # psastro details
+  REJECTIONS  STR    esowfi/rejections.config  # Rejection limits
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES	STR	recipes/filerules-mef.mdc		# File rules appropriate for MEF format
+
+# FPA file defines properties of a possible input|output object
+# user can set the filename (I|O), filename rules (O), or abstract source (@FILES, @DETDB) (I)
+# user can set the extension name, if used
+# user can set the file type (IMAGE, JPEG, RAW, SX, OBJ, CMP, CMF) : but these are not variable in most cases!
+# user can set the file depth: only valid for output files
+# user can set the data depth: must be >= file depth
+# user can set the file format: only valid for newly created FPAs
+# user can set the colormap, scaling method, scaling range (JPEG only)
+# user can set the extension name for the data and header segments (CMF only)
+
+
+EXTNAME.RULES  METADATA
+  CMF.HEAD   STR  {CHIP.NAME}.hdr
+  CMF.DATA   STR  {CHIP.NAME}.psf
+  CMF.XSRC   STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT   STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD   STR  {CHIP.NAME}.hdr
+  PSF.TABLE  STR  {CHIP.NAME}.psf_model
+  PSF.RESID  STR  {CHIP.NAME}.psf_resid
+END
+
+BLANK.HEADERS  METADATA
+  FPA.TIME      STR  MJD-OBS
+  FPA.EXPOSURE  STR  EXPTIME
+  FPA.AIRMASS   STR  AIRMASS
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.N}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/dvo.config	(revision 22322)
@@ -0,0 +1,58 @@
+
+# keywords used by DVO to interpret the headers
+
+# keyword abstractions:
+DATE-KEYWORD		NONE
+UT-KEYWORD		NONE
+JD-KEYWORD		NONE
+MJD-KEYWORD		MJD-OBS
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		0
+CAL_INSTMAG_MIN		0
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		1.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
Index: /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/format_split.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/format_split.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/format_split.config	(revision 22322)
@@ -0,0 +1,117 @@
+# Simulation test camera with single chip with single cell
+
+# How to identify this type
+RULE	METADATA
+	ORIGIN	STR		ESO
+	NAXIS			S32	2
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	CHIP		# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE		# There are no extensions
+	FPA.OBS		STR	ORIGIN		# A PHU keyword for unique FPA identifier
+	CONTENT		STR	EXTNAME 	# Key to the CONTENTS menu
+	CONTENT.RULE	STR	{CHIP.NAME}	# How to derive the CONTENT when writing
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# File identifier with corresponding file layout name
+	WIN1.CHIP1.OUT1		STR	Chip51:Chip
+	WIN1.CHIP2.OUT1		STR	Chip52:Chip
+	WIN1.CHIP3.OUT1		STR	Chip53:Chip
+	WIN1.CHIP4.OUT1		STR	Chip54:Chip
+	WIN1.CHIP5.OUT1		STR	Chip55:Chip
+	WIN1.CHIP6.OUT1		STR	Chip56:Chip
+	WIN1.CHIP7.OUT1		STR	Chip57:Chip
+	WIN1.CHIP8.OUT1		STR	Chip58:Chip
+END
+
+# Specify the layout
+CHIPS	METADATA
+	Chip		STR		Cell00:Cell
+END
+
+
+# Specify the cell data
+CELLS	METADATA
+	Cell		METADATA
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC				STR	[49:2094,1:4098]
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC				STR	[1:48,1:4098]
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE		STR		TELESCOP
+	FPA.INSTRUMENT	STR		INSTRUME
+	FPA.OBSTYPE			STR		IMAGETYPE
+	FPA.AIRMASS			STR		AIRMASS
+	FPA.FILTER			STR		FILTER1
+	FPA.FILTERID		STR		FILTER1
+	FPA.RA					STR		RA
+	FPA.DEC					STR		DEC
+	FPA.OBJECT			STR		OBJECT
+	FPA.TIME				STR		MJD-OBS
+	FPA.EXPOSURE		STR		EXPTIME
+	CELL.EXPOSURE		STR		EXPTIME
+	CELL.DARKTIME		STR		EXPTIME
+	CELL.TIME				STR		MJD-OBS
+	#CELL.XBIN			STR		XBIN
+	#CELL.YBIN			STR		YBIN
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.DETECTOR		STR		WFI
+	FPA.TIMESYS			STR		UTC
+	FPA.RADECSYS		STR		ICRS
+	CELL.SATURATION	F32		65535
+	CELL.BAD				F32		0
+	CELL.READDIR		S32		1
+	CELL.TIMESYS		STR		UTC
+	CELL.XPARITY		S32		1
+	CELL.YPARITY		S32		1
+	CHIP.XPARITY		S32		1
+	CHIP.YPARITY		S32		1
+	CELL.X0					S32   0
+	CELL.Y0					S32   0
+	CHIP.X0.DEPEND	STR	CHIP.NAME
+	CHIP.X0		METADATA
+		Chip50	S32	1
+		Chip51	S32	2047
+		Chip52	S32	4093
+		Chip53	S32	6138
+		Chip54	S32	1
+		Chip55	S32	2047
+		Chip56	S32	4093
+		Chip57	S32	6138
+	END
+	CHIP.Y0.DEPEND	STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		Chip50	S32	8196
+		Chip51	S32	8196
+		Chip52	S32	8196
+		Chip53	S32	8196
+		Chip54	S32	1
+		Chip55	S32	1
+		Chip56	S32	1
+		Chip57	S32	1
+	END
+	CELL.XSIZE			S32	2142
+	CELL.YSIZE			S32	4128
+	CELL.XWINDOW		S32	0
+	CELL.YWINDOW		S32	0
+	CELL.GAIN				F32	2.0
+	CELL.READNOISE	F32	3.0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/format_together.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/format_together.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/format_together.config	(revision 22322)
@@ -0,0 +1,117 @@
+# Simulation test camera with single chip with single cell
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	MPI-2.2
+	INSTRUME	STR	WFI
+	NAXIS		S32	0
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR		FPA		# The FITS file represents an entire FPA
+	EXTENSIONS	STR		CHIP		# There are no extensions
+	FPA.OBS		STR		ESO OBS NAME	# A PHU keyword for unique FPA identifier
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name ---> chip_name:chip_type
+	WIN1.CHIP1.OUT1		STR		Chip50:WFIChip
+	WIN1.CHIP2.OUT1		STR		Chip51:WFIChip
+	WIN1.CHIP3.OUT1		STR		Chip52:WFIChip
+	WIN1.CHIP4.OUT1		STR		Chip53:WFIChip
+	WIN1.CHIP5.OUT2		STR		Chip54:WFIChip
+	WIN1.CHIP6.OUT1		STR		Chip55:WFIChip
+	WIN1.CHIP7.OUT1		STR		Chip56:WFIChip
+	WIN1.CHIP8.OUT1		STR		Chip57:WFIChip
+END
+
+# Specify the layout
+CHIPS	METADATA
+	# Chip type ---> cell_name:cell_type
+	WFIChip		STR	Cell00:WFICell
+END
+
+
+# Specify the cell data
+CELLS	METADATA
+	WFICell		METADATA
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC		STR	[49:2094,1:4098]
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[1:48,1:4098]
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE		STR		TELESCOP
+	FPA.INSTRUMENT		STR		INSTRUME
+	FPA.OBSTYPE		STR		IMAGETYP
+	FPA.AIRMASS		STR		AIRMASS
+	FPA.FILTER		STR		FILTER1
+	FPA.FILTERID		STR		FILTER1
+	FPA.RA			STR		RA
+	FPA.DEC			STR		DEC
+	FPA.OBJECT		STR		OBJECT
+	FPA.TIME		STR		MJD-OBS
+	FPA.EXPOSURE		STR		EXPTIME
+	CELL.EXPOSURE		STR		EXPTIME
+	CELL.DARKTIME		STR		EXPTIME
+	CELL.TIME		STR		MJD-OBS
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.DETECTOR		STR		WFI
+	FPA.TIMESYS		STR		UTC
+	FPA.RADECSYS		STR		ICRS
+	CELL.SATURATION		F32		65535
+	CELL.BAD		F32		0
+	CELL.READDIR		S32		1
+	CELL.TIMESYS		STR		UTC
+	CELL.XPARITY		S32		1
+	CELL.YPARITY		S32		1
+	CHIP.XPARITY		S32		1
+	CHIP.YPARITY		S32		1
+	CELL.XBIN		S32		1
+	CELL.YBIN		S32		1
+	CELL.X0			S32   0
+	CELL.Y0			S32   0
+	CHIP.X0.DEPEND	STR	CHIP.NAME
+	CHIP.X0		METADATA
+		Chip50	S32	1
+		Chip51	S32	2047
+		Chip52	S32	4093
+		Chip53	S32	6138
+		Chip54	S32	1
+		Chip55	S32	2047
+		Chip56	S32	4093
+		Chip57	S32	6138
+	END
+	CHIP.Y0.DEPEND	STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		Chip50	S32	8196
+		Chip51	S32	8196
+		Chip52	S32	8196
+		Chip53	S32	8196
+		Chip54	S32	1
+		Chip55	S32	1
+		Chip56	S32	1
+		Chip57	S32	1
+	END
+	CELL.XSIZE		S32	2142
+	CELL.YSIZE		S32	4128
+	CELL.XWINDOW		S32	0
+	CELL.YWINDOW		S32	0
+	CELL.GAIN		F32	2.0
+	CELL.READNOISE		F32	3.0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	DEGREES
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/ppImage.config	(revision 22322)
@@ -0,0 +1,222 @@
+### ppImage recipe configuration file for simulated test camera
+
+# List of tasks to perform
+
+BASE.FITS       BOOL    FALSE           # Save base detrended image?
+CHIP.FITS       BOOL    TRUE            # Save chip-mosaicked image?
+BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+
+# binned output image options
+BIN1.XBIN               S32     8
+BIN1.YBIN               S32     8
+BIN2.XBIN               S32     64
+BIN2.YBIN               S32     64
+
+# Overscan subtraction
+OVERSCAN.SINGLE         BOOL    FALSE           # Reduce overscan to a single value?
+OVERSCAN.FIT            STR     POLYNOMIAL      # NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER          S32     7               # Order of polynomial fit
+OVERSCAN.STAT           STR     MEAN            # MEAN | MEDIAN
+
+# How to select the appropriate detrend image
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  MASK METADATA
+  END
+  DARK METADATA
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+  END
+  SHUTTER METADATA
+  END   
+END
+
+########################################################################################
+# Need the following in order to turn off overscan and shutter for the site-level recipe
+########################################################################################
+
+# Overscan subtraction only
+PPIMAGE_O         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Dark subtraction only
+PPIMAGE_D         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Shutter correction only
+PPIMAGE_S         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark
+PPIMAGE_OBD      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF     METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # 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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR    METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # 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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP   METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA   METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/psastro.config	(revision 22322)
@@ -0,0 +1,4 @@
+PSASTRO.CATDIR	STR	SYNTH.SIMTEST
+
+#PSASTRO.MOSAIC.GRADIENT.NX    S32     1   # number of x-dir cells per chip
+#PSASTRO.MOSAIC.GRADIENT.NY    S32     1   # number of y-dir cells per chip
Index: /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/psphot.config	(revision 22322)
@@ -0,0 +1,30 @@
+
+SAVE.BACKMDL	BOOL 	TRUE
+SAVE.BACKMDL.STDEV BOOL 	TRUE
+SAVE.BACKGND	BOOL 	TRUE
+SAVE.BACKSUB	BOOL 	TRUE
+SAVE.PLOTS      BOOL    FALSE
+SAVE.RESID	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+LOAD.PSF	BOOL 	FALSE
+
+IMSTATS_NPIX        S32  3000    	 # number of pixels to use for sky estimate boxes:
+
+#PSPHOT_TEST METADATA
+# SAVE.BACKSUB	BOOL 	FALSE
+# SAVE.RESID	BOOL 	TRUE
+#END
+#
+#PEAKS_OUTPUT_FILE   STR  peaks.dat
+#MOMENTS_OUTPUT_FILE STR  moments.dat
+
+#PSF_SN_LIM          F32  20.0            # minimum S/N for stars used for PSF model
+MOMENTS_SN_MIN      F32  20.0            # min S/N to measure moments
+FULL_FIT_SN_LIM     F32  100.0
+EXT_MIN_SN          F32  100.0           # fit galaxies above this S/N limit
+
+PEAKS_NSIGMA_LIMIT  F32  10.0 	   	 # peak significance threshold
+BREAK_POINT         STR  ENSEMBLE        # limit total processing for now (for speed)
+
+
+SKY_STAT            STR  ROBUST_MEDIAN   # statistic used to measure background
Index: /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/esowfi/rejections.config	(revision 22322)
@@ -0,0 +1,45 @@
+
+DARK METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0
+  IMFILE.STDEV       F32  0.0
+  IMFILE.MEANSTDEV   F32  0.0
+  IMFILE.SN          F32  0.0
+  IMFILE.BIN.STDEV   F32  1.0
+  IMFILE.BIN.SN      F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0
+  EXP.STDEV          F32  0.0
+  EXP.MEANSTDEV      F32  0.5
+  EXP.SN             F32  0.0
+  EXP.BIN.STDEV      F32  0.0
+  EXP.BIN.SN         F32  0.0
+  EXP.FLUX           F32  0.0
+  ENSEMBLE.MEAN      F32  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+
+SHUTTER METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  0.0
+  IMFILE.STDEV       F32  0.0
+  IMFILE.MEANSTDEV   F32  0.0
+  IMFILE.SN          F32  0.0
+  IMFILE.BIN.STDEV   F32  0.0
+  IMFILE.BIN.SN      F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  0.0
+  EXP.STDEV          F32  0.0
+  EXP.MEANSTDEV      F32  0.0
+  EXP.SN             F32  0.0
+  EXP.BIN.STDEV      F32  0.0
+  EXP.BIN.SN         F32  0.0
+  EXP.FLUX           F32  0.0
+  ENSEMBLE.MEAN      F32  0.0
+  ENSEMBLE.STDEV     F32  0.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/Makefile.am	(revision 22322)
@@ -0,0 +1,24 @@
+
+installdir = $(datadir)/ippconfig/gpc1
+
+install_files = \
+	dvo.config \
+	dvo.layout \
+	camera.config \
+	format_raw.config \
+	format_mef.config \
+	ppImage.config \
+        ppMerge.config \
+	psphot.config \
+	psastro.config \
+        pswarp.config \
+	ppStack.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/camera.config	(revision 22322)
@@ -0,0 +1,127 @@
+# Camera configuration file for GPC1: describes the camera
+
+# File formats that we know about
+FORMATS		METADATA
+	MEF	STR	gpc1/format_mef.config
+	RAW	STR	gpc1/format_raw.config
+END
+
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA	METADATA
+        XY01  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY02  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY03  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY04  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY05  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY06  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY10  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY11  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY12  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY13  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY14  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY15  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY16  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY17  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY20  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY21  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY22  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY23  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY24  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY25  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY26  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY27  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY30  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY31  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY32  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY33  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY34  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY35  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY36  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY37  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY40  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY41  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY42  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY43  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY44  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY45  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY46  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY47  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY50  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY51  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY52  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY53  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY54  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY55  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY56  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY57  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY60  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY61  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY62  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY63  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY64  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY65  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY66  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY67  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY71  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY72  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY73  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY74  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY75  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+        XY76  STR xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+END	   
+
+# internal to external filter names
+FILTER.ID       METADATA
+   g        STR   g.00000
+   r        STR   r.00000
+   i        STR   i.00000
+   z        STR   z.00000
+   y        STR   y.00000
+   OPEN     STR   OPEN
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID         METADATA
+        FPA     STR     fpa
+        CHIP    STR     {CHIP.NAME}
+        CELL    STR     {CHIP.NAME}:{CELL.NAME}
+END
+
+DVO.CAMERADIR	STR	gpc1		# Camera directory for DVO
+
+# Recipe options
+RECIPES		METADATA
+	PPIMAGE		STR	gpc1/ppImage.config
+	PPMERGE		STR	gpc1/ppMerge.config
+	PSASTRO		STR	gpc1/psastro.config
+	PSPHOT		STR	gpc1/psphot.config
+	PSWARP		STR	gpc1/pswarp.config
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES	STR	recipes/filerules-split.mdc
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR {CHIP.NAME}.hdr
+  CMF.DATA STR {CHIP.NAME}.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD  STR	{CHIP.NAME}.hdr
+  PSF.TABLE STR {CHIP.NAME}.psf_model
+  PSF.RESID STR {CHIP.NAME}.psf_resid
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBS		STR	EXPNUM
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.NAME}	# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/chips-layout.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/chips-layout.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/chips-layout.txt	(revision 22322)
@@ -0,0 +1,62 @@
+# 1st: from focal-plane layout gif
+# 2nd: from filename (*NM.fits)
+  CCID58-1-01b1  2  5    CCID58-1-01b1	2 5  
+  CCID58-1-01b2  3  5    CCID58-1-01b2	3 5  
+  CCID58-1-02a1  2  3    CCID58-1-02a1	2 3  
+  CCID58-1-02a2  3  3    CCID58-1-02a2	3 3  
+  CCID58-1-02b1  3  6    CCID58-1-02b1	3 6  
+  CCID58-1-02b2  6  3    CCID58-1-02b2	6 3  
+  CCID58-1-03b1  6  1    CCID58-1-03b1	6 1  
+  CCID58-1-03b2  4  1    CCID58-1-03b2	4 1  
+  CCID58-1-04a1  2  1    CCID58-1-04a1	2 1  
+  CCID58-1-04a2  2  4    CCID58-1-04a2	2 4  ** (not same as GIF)
+  CCID58-1-04b1  5  2    CCID58-1-04b1	5 2  
+  CCID58-1-05a2  4  4    CCID58-1-05a2	4 4  
+  CCID58-1-05b2  5  3    CCID58-1-05b2	5 3  
+  CCID58-1-07a1  6  4    CCID58-1-07a1	6 4  
+  CCID58-1-07b1  4  2    CCID58-1-07b1	4 2  
+  CCID58-1-09a1  0  4    CCID58-1-09a1	0 4  
+  CCID58-1-09a2  1  4    CCID58-1-09a2	1 4  
+  CCID58-1-09b1  1  6    CCID58-1-09b1	1 6  
+  CCID58-1-10a2  5  6    CCID58-1-10a2	5 6  
+  CCID58-1-12a1  3  0    CCID58-1-12a1	3 0  
+  CCID58-1-13a1  0  3    CCID58-1-13a1	0 3  
+  CCID58-1-14a2  1  3    CCID58-1-14a2	1 3  
+  CCID58-1-14b1  7  2    CCID58-1-14b1	7 2  
+  CCID58-1-17a2  1  2    CCID58-1-17a2	1 2  
+  CCID58-1-17b1  5  0    CCID58-1-17b1	5 0  
+  CCID58-1-18a1  7  4    CCID58-1-18a1	7 4  
+  CCID58-1-18b1  2  7    CCID58-1-18b1	2 7  
+  CCID58-1-19a1  2  0    CCID58-1-19a1	2 0  
+  CCID58-1-21a1  4  7    CCID58-1-21a1	4 7  
+  CCID58-1-21a2  4  5    CCID58-1-21a2	4 5  
+  CCID58-1-25b1  7  3    CCID58-1-25b1	7 3  
+  CCID58-2-01a1  3  1    CCID58-2-01a1	3 1  
+  CCID58-2-01a2  3  2    CCID58-2-01a2	3 2  
+  CCID58-2-04a1  3  4    CCID58-2-04a1	3 4  
+  CCID58-2-04b2  7  1    CCID58-2-04b2	7 1  
+  CCID58-2-05a1  7  6    CCID58-2-05a1	7 6  
+  CCID58-2-07a2  4  6    CCID58-2-07a2	4 6  
+  CCID58-2-07b2  2  6    CCID58-2-07b2	2 6  
+  CCID58-2-09a1  5  4    CCID58-2-09a1	5 4  
+  CCID58-2-09a2  2  2    CCID58-2-09a2	2 2  
+  CCID58-2-09b1  0  6    CCID58-2-09b1	0 6  
+  CCID58-2-09b2  5  1    CCID58-2-09b2	5 1  
+  CCID58-2-10b2  1  7    CCID58-2-10b2	3 7  **
+  CCID58-2-11b2  4  3    CCID58-2-11b2	4 3  
+  CCID58-2-12a1  5  7    CCID58-2-12a1	5 7  
+  CCID58-2-12a2  5  5    CCID58-2-12a2	5 5  
+  CCID58-2-13a2  1  1    CCID58-2-13a2 	1 1  
+  CCID58-2-13b1  1  5    CCID58-2-13b1	1 5  
+  CCID58-2-14a1  6  6    CCID58-2-14a1	6 6  
+  CCID58-2-15a1  0  1    CCID58-2-15a1	0 1  
+  CCID58-2-16a1  7  5    CCID58-2-16a1	7 5  
+  CCID58-2-16a2  6  7    CCID58-2-16a2	6 7  
+  CCID58-2-16b1  0  5    CCID58-2-16b1	0 5  
+  CCID58-2-16b2  3  7    CCID58-2-16b2	1 7  **
+  CCID58-2-17a2  6  5    CCID58-2-17a2	6 5  
+  CCID58-2-18a1  0  2    CCID58-2-18a1	0 2  
+  CCID58-2-18b2  4  0    CCID58-2-18b2	4 0  
+  CCID58-2-22a1  1  0    CCID58-2-22a1	1 0  
+  CCID58-2-22b2  6  0    CCID58-2-22b2	6 0  
+  CCID58-2-23b2  6  2    CCID58-2-23b2	6 2  
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/chips-script.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/chips-script.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/chips-script.txt	(revision 22322)
@@ -0,0 +1,11 @@
+
+# generate the offsets
+awk '{x=$2}{dx=157}($2 < 4){{x=$2+1}{dx=0}}(NR>2){printf "          XY%d%d  S32     %d\n", $2, $3, x*4971+dx}' chips-layout.txt | sort -k 1 > tmp
+awk '{y=$3}{dy=306}($2 < 4){{y=$3+1}{dy=0}}(NR>2){printf "          XY%d%d  S32     %d\n", $2, $3, y*5143+dy}' chips-layout.txt | sort -k 1 > tmp
+
+# generate the parities
+awk '{x=1}($2 < 4){x=-1}(NR>2){printf "          XY%d%d  S32     %d\n", $2, $3, x}' chips-layout.txt | sort -k 1 > tmp
+awk '{y=1}($2 < 4){y=-1}(NR>2){printf "          XY%d%d  S32     %d\n", $2, $3, y}' chips-layout.txt | sort -k 1 > tmp
+
+# generate the CONTENT table
+awk '(NR > 2){printf "        %s  STR  XY%d%d:GPCChip\n", $1, $2, $3}' chips-layout.txt | sort -k 3 > tmp
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/dvo.config	(revision 22322)
@@ -0,0 +1,67 @@
+
+# location of DVO database tables
+CATDIR			/data/alala.0/eugene/isp/catdir
+
+# keywords used by DVO to interpret the headers
+
+# keyword abstractions:
+#DATE-KEYWORD		DATE-OBS
+#DATE-MODE		yyyy-mm-dd
+#UT-KEYWORD		UTC-OBS
+JD-KEYWORD		NONE
+MJD-KEYWORD		MJD-OBS
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		0
+CAL_INSTMAG_MIN		0
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		1.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
+
+PM_DT_MIN               0.25
+PM_TOOFEW               4
+POS_TOOFEW              1
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/dvo.layout
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/dvo.layout	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/dvo.layout	(revision 22322)
@@ -0,0 +1,58 @@
+# this file defines the layout of the mosaic imager:
+
+# NCCD 40  # this needs work: sometimes 36, sometimes 40
+NCCD 36
+NAXIS1 2112
+NAXIS2 4644
+
+MOSAIC_X 11
+MOSAIC_Y  4
+
+# lines need to contain:
+
+CHIPID_KEYWORD EXTNAME
+
+# ID     CHIPID xoffset yoffset xflip yflip datasec         biassec            Xo      Yo      theta
+CCD.0     ccd00       1       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.1     ccd01       2       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.2     ccd02       3       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.3     ccd03       4       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.4     ccd04       5       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.5     ccd05       6       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.6     ccd06       7       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.7     ccd07       8       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.8     ccd08       9       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.9     ccd09       1       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.10    ccd10       2       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.11    ccd11       3       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.12    ccd12       4       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.13    ccd13       5       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.14    ccd14       6       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.15    ccd15       7       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.16    ccd16       8       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.17    ccd17       9       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.18    ccd18       1       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.19    ccd19       2       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.20    ccd20       3       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.21    ccd21       4       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.22    ccd22       5       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.23    ccd23       6       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.24    ccd24       7       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.25    ccd25       8       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.26    ccd26       9       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.27    ccd27       1       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.28    ccd28       2       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.29    ccd29       3       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.30    ccd30       4       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.31    ccd31       5       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.32    ccd32       6       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.33    ccd33       7       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.34    ccd34       8       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.35    ccd35       9       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.36    ccd36       0       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.37    ccd37      10       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.38    ccd38       0       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.39    ccd39      10       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+
+# can we use header to find biassec
+USE_BIASSEC 1
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/format_mef.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/format_mef.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/format_mef.config	(revision 22322)
@@ -0,0 +1,503 @@
+# The raw GPC data comes off the telescope with each of the chips stored in separate files
+
+# How to identify this type
+RULE	METADATA
+#	TELESCOP	STR	PS1
+#	DETECTOR	STR	GPC1
+	CONTROLR	STR	STARGRASP
+	PSFORMAT        STR     MEF
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	   # The FITS file represents a complete camera fpa
+	EXTENSIONS	STR	CHIP	   # The extensions represent chips
+	FPA.OBS		STR	CONTROLR   # A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# CONTENT      =    chip name : type
+        CCID58-2-15a1  STR  XY01:GPCChip
+        CCID58-2-18a1  STR  XY02:GPCChip
+        CCID58-1-13a1  STR  XY03:GPCChip
+        CCID58-1-09a1  STR  XY04:GPCChip
+        CCID58-2-16b1  STR  XY05:GPCChip
+        CCID58-2-09b1  STR  XY06:GPCChip
+        CCID58-2-22a1  STR  XY10:GPCChip
+        CCID58-2-13a2  STR  XY11:GPCChip
+        CCID58-1-17a2  STR  XY12:GPCChip
+        CCID58-1-14a2  STR  XY13:GPCChip
+        CCID58-1-09a2  STR  XY14:GPCChip
+        CCID58-2-13b1  STR  XY15:GPCChip
+        CCID58-1-09b1  STR  XY16:GPCChip
+        CCID58-2-10b2  STR  XY17:GPCChip
+        CCID58-1-19a1  STR  XY20:GPCChip
+        CCID58-1-04a1  STR  XY21:GPCChip
+        CCID58-2-09a2  STR  XY22:GPCChip
+        CCID58-1-02a1  STR  XY23:GPCChip
+        CCID58-1-04a2  STR  XY24:GPCChip
+        CCID58-1-01b1  STR  XY25:GPCChip
+        CCID58-2-07b2  STR  XY26:GPCChip
+        CCID58-1-18b1  STR  XY27:GPCChip
+        CCID58-1-12a1  STR  XY30:GPCChip
+        CCID58-2-01a1  STR  XY31:GPCChip
+        CCID58-2-01a2  STR  XY32:GPCChip
+        CCID58-1-02a2  STR  XY33:GPCChip
+        CCID58-2-04a1  STR  XY34:GPCChip
+        CCID58-1-01b2  STR  XY35:GPCChip
+        CCID58-1-02b1  STR  XY36:GPCChip
+        CCID58-2-16b2  STR  XY37:GPCChip
+        CCID58-2-18b2  STR  XY40:GPCChip
+        CCID58-1-03b2  STR  XY41:GPCChip
+        CCID58-1-07b1  STR  XY42:GPCChip
+        CCID58-2-11b2  STR  XY43:GPCChip
+        CCID58-1-05a2  STR  XY44:GPCChip
+        CCID58-1-21a2  STR  XY45:GPCChip
+        CCID58-2-07a2  STR  XY46:GPCChip
+        CCID58-1-21a1  STR  XY47:GPCChip
+        CCID58-1-17b1  STR  XY50:GPCChip
+        CCID58-2-09b2  STR  XY51:GPCChip
+        CCID58-1-04b1  STR  XY52:GPCChip
+        CCID58-1-05b2  STR  XY53:GPCChip
+        CCID58-2-09a1  STR  XY54:GPCChip
+        CCID58-2-12a2  STR  XY55:GPCChip
+        CCID58-1-10a2  STR  XY56:GPCChip
+        CCID58-2-12a1  STR  XY57:GPCChip
+        CCID58-2-22b2  STR  XY60:GPCChip
+        CCID58-1-03b1  STR  XY61:GPCChip
+        CCID58-2-23b2  STR  XY62:GPCChip
+        CCID58-1-02b2  STR  XY63:GPCChip
+        CCID58-1-07a1  STR  XY64:GPCChip
+        CCID58-2-17a2  STR  XY65:GPCChip
+        CCID58-2-14a1  STR  XY66:GPCChip
+        CCID58-2-16a2  STR  XY67:GPCChip
+        CCID58-2-04b2  STR  XY71:GPCChip
+        CCID58-1-14b1  STR  XY72:GPCChip
+        CCID58-1-25b1  STR  XY73:GPCChip
+        CCID58-1-18a1  STR  XY74:GPCChip
+        CCID58-2-16a1  STR  XY75:GPCChip
+        CCID58-2-05a1  STR  XY76:GPCChip
+END
+
+CHIPS   METADATA
+	GPCChip		METADATA
+		# Extension name, cellName:cellType
+		xy00   STR  xy00:GPCCell
+		xy10   STR  xy10:GPCCell
+		xy20   STR  xy20:GPCCell
+		xy30   STR  xy30:GPCCell
+		xy40   STR  xy40:GPCCell
+		xy50   STR  xy50:GPCCell
+		xy60   STR  xy60:GPCCell
+		xy70   STR  xy70:GPCCell
+		xy01   STR  xy01:GPCCell
+		xy11   STR  xy11:GPCCell
+		xy21   STR  xy21:GPCCell
+		xy31   STR  xy31:GPCCell
+		xy41   STR  xy41:GPCCell
+		xy51   STR  xy51:GPCCell
+		xy61   STR  xy61:GPCCell
+		xy71   STR  xy71:GPCCell
+		xy02   STR  xy02:GPCCell
+		xy12   STR  xy12:GPCCell
+		xy22   STR  xy22:GPCCell
+		xy32   STR  xy32:GPCCell
+		xy42   STR  xy42:GPCCell
+		xy52   STR  xy52:GPCCell
+		xy62   STR  xy62:GPCCell
+		xy72   STR  xy72:GPCCell
+		xy03   STR  xy03:GPCCell
+		xy13   STR  xy13:GPCCell
+		xy23   STR  xy23:GPCCell
+		xy33   STR  xy33:GPCCell
+		xy43   STR  xy43:GPCCell
+		xy53   STR  xy53:GPCCell
+		xy63   STR  xy63:GPCCell
+		xy73   STR  xy73:GPCCell
+		xy04   STR  xy04:GPCCell
+		xy14   STR  xy14:GPCCell
+		xy24   STR  xy24:GPCCell
+		xy34   STR  xy34:GPCCell
+		xy44   STR  xy44:GPCCell
+		xy54   STR  xy54:GPCCell
+		xy64   STR  xy64:GPCCell
+		xy74   STR  xy74:GPCCell
+		xy05   STR  xy05:GPCCell
+		xy15   STR  xy15:GPCCell
+		xy25   STR  xy25:GPCCell
+		xy35   STR  xy35:GPCCell
+		xy45   STR  xy45:GPCCell
+		xy55   STR  xy55:GPCCell
+		xy65   STR  xy65:GPCCell
+		xy75   STR  xy75:GPCCell
+		xy06   STR  xy06:GPCCell
+		xy16   STR  xy16:GPCCell
+		xy26   STR  xy26:GPCCell
+		xy36   STR  xy36:GPCCell
+		xy46   STR  xy46:GPCCell
+		xy56   STR  xy56:GPCCell
+		xy66   STR  xy66:GPCCell
+		xy76   STR  xy76:GPCCell
+		xy07   STR  xy07:GPCCell
+		xy17   STR  xy17:GPCCell
+		xy27   STR  xy27:GPCCell
+		xy37   STR  xy37:GPCCell
+		xy47   STR  xy47:GPCCell
+		xy57   STR  xy57:GPCCell
+		xy67   STR  xy67:GPCCell
+		xy77   STR  xy77:GPCCell
+	END
+END
+
+# Specify the cell data
+CELLS	METADATA
+	GPCCell		METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+        FPA.FILTERID    STR     FILTERID
+        FPA.FILTER      STR     FILTERID
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        # FPA.RA          STR     CRVAL1
+        # FPA.DEC         STR     CRVAL2
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.OBSTYPE     STR     OBSTYPE
+	FPA.OBJECT	STR	OBJECT
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.POSANGLE	STR	POSANGLE
+	FPA.FOCUS	STR	M2Z
+	FPA.TIME	STR	MJD-OBS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.TEMP	STR	DETTEM
+	CHIP.ID         STR	DETECTOR
+	CELL.XBIN	STR	CCDSUM
+	CELL.YBIN	STR	CCDSUM
+	CELL.X0		STR	IMNPIX1
+	CELL.Y0		STR	IMNPIX2
+ 	CELL.XPARITY	STR	ATM1_1
+	CELL.YPARITY	STR	ATM2_2
+#	CELL.SATURATION	STR	SATURATE
+	FPA.EXPOSURE	STR	EXPREQ		# Requested exposure time, presumably camera exposure time
+	CELL.EXPOSURE	STR	EXPTIME		# Exposure time measured by shutter
+	CELL.DARKTIME	STR	DARKTIME	# Exposure time for camera
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TELESCOPE	STR	PS1
+	FPA.INSTRUMENT	STR	GPC1
+	FPA.DETECTOR	STR	GPC1
+	FPA.OBS	S32	12345
+	FPA.TIMESYS	STR	UTC
+	CHIP.XSIZE		S32	4846
+	CHIP.YSIZE		S32	4868
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+          XY01  S32     4971
+          XY02  S32     4971
+          XY03  S32     4971
+          XY04  S32     4971
+          XY05  S32     4971
+          XY06  S32     4971
+          XY10  S32     9942
+          XY11  S32     9942
+          XY12  S32     9942
+          XY13  S32     9942
+          XY14  S32     9942
+          XY15  S32     9942
+          XY16  S32     9942
+          XY17  S32     9942
+          XY20  S32     14913
+          XY21  S32     14913
+          XY22  S32     14913
+          XY23  S32     14913
+          XY24  S32     14913
+          XY25  S32     14913
+          XY26  S32     14913
+          XY27  S32     14913
+          XY30  S32     19884
+          XY31  S32     19884
+          XY32  S32     19884
+          XY33  S32     19884
+          XY34  S32     19884
+          XY35  S32     19884
+          XY36  S32     19884
+          XY37  S32     19884
+          XY40  S32     20041
+          XY41  S32     20041
+          XY42  S32     20041
+          XY43  S32     20041
+          XY44  S32     20041
+          XY45  S32     20041
+          XY46  S32     20041
+          XY47  S32     20041
+          XY50  S32     25012
+          XY51  S32     25012
+          XY52  S32     25012
+          XY53  S32     25012
+          XY54  S32     25012
+          XY55  S32     25012
+          XY56  S32     25012
+          XY57  S32     25012
+          XY60  S32     29983
+          XY61  S32     29983
+          XY62  S32     29983
+          XY63  S32     29983
+          XY64  S32     29983
+          XY65  S32     29983
+          XY66  S32     29983
+          XY67  S32     29983
+          XY71  S32     34954
+          XY72  S32     34954
+          XY73  S32     34954
+          XY74  S32     34954
+          XY75  S32     34954
+          XY76  S32     34954
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+          XY01  S32     10286
+          XY02  S32     15429
+          XY03  S32     20572
+          XY04  S32     25715
+          XY05  S32     30858
+          XY06  S32     36001
+          XY10  S32     5143
+          XY11  S32     10286
+          XY12  S32     15429
+          XY13  S32     20572
+          XY14  S32     25715
+          XY15  S32     30858
+          XY16  S32     36001
+          XY17  S32     41144
+          XY20  S32     5143
+          XY21  S32     10286
+          XY22  S32     15429
+          XY23  S32     20572
+          XY24  S32     25715
+          XY25  S32     30858
+          XY26  S32     36001
+          XY27  S32     41144
+          XY30  S32     5143
+          XY31  S32     10286
+          XY32  S32     15429
+          XY33  S32     20572
+          XY34  S32     25715
+          XY35  S32     30858
+          XY36  S32     36001
+          XY37  S32     41144
+          XY40  S32     306
+          XY41  S32     5449
+          XY42  S32     10592
+          XY43  S32     15735
+          XY44  S32     20878
+          XY45  S32     26021
+          XY46  S32     31164
+          XY47  S32     36307
+          XY50  S32     306
+          XY51  S32     5449
+          XY52  S32     10592
+          XY53  S32     15735
+          XY54  S32     20878
+          XY55  S32     26021
+          XY56  S32     31164
+          XY57  S32     36307
+          XY60  S32     306
+          XY61  S32     5449
+          XY62  S32     10592
+          XY63  S32     15735
+          XY64  S32     20878
+          XY65  S32     26021
+          XY66  S32     31164
+          XY67  S32     36307
+          XY71  S32     5449
+          XY72  S32     10592
+          XY73  S32     15735
+          XY74  S32     20878
+          XY75  S32     26021
+          XY76  S32     31164
+        END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+          XY01  S32     -1
+          XY02  S32     -1
+          XY03  S32     -1
+          XY04  S32     -1
+          XY05  S32     -1
+          XY06  S32     -1
+          XY10  S32     -1
+          XY11  S32     -1
+          XY12  S32     -1
+          XY13  S32     -1
+          XY14  S32     -1
+          XY15  S32     -1
+          XY16  S32     -1
+          XY17  S32     -1
+          XY20  S32     -1
+          XY21  S32     -1
+          XY22  S32     -1
+          XY23  S32     -1
+          XY24  S32     -1
+          XY25  S32     -1
+          XY26  S32     -1
+          XY27  S32     -1
+          XY30  S32     -1
+          XY31  S32     -1
+          XY32  S32     -1
+          XY33  S32     -1
+          XY34  S32     -1
+          XY35  S32     -1
+          XY36  S32     -1
+          XY37  S32     -1
+          XY40  S32     1
+          XY41  S32     1
+          XY42  S32     1
+          XY43  S32     1
+          XY44  S32     1
+          XY45  S32     1
+          XY46  S32     1
+          XY47  S32     1
+          XY50  S32     1
+          XY51  S32     1
+          XY52  S32     1
+          XY53  S32     1
+          XY54  S32     1
+          XY55  S32     1
+          XY56  S32     1
+          XY57  S32     1
+          XY60  S32     1
+          XY61  S32     1
+          XY62  S32     1
+          XY63  S32     1
+          XY64  S32     1
+          XY65  S32     1
+          XY66  S32     1
+          XY67  S32     1
+          XY71  S32     1
+          XY72  S32     1
+          XY73  S32     1
+          XY74  S32     1
+          XY75  S32     1
+          XY76  S32     1
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+          XY01  S32     -1
+          XY02  S32     -1
+          XY03  S32     -1
+          XY04  S32     -1
+          XY05  S32     -1
+          XY06  S32     -1
+          XY10  S32     -1
+          XY11  S32     -1
+          XY12  S32     -1
+          XY13  S32     -1
+          XY14  S32     -1
+          XY15  S32     -1
+          XY16  S32     -1
+          XY17  S32     -1
+          XY20  S32     -1
+          XY21  S32     -1
+          XY22  S32     -1
+          XY23  S32     -1
+          XY24  S32     -1
+          XY25  S32     -1
+          XY26  S32     -1
+          XY27  S32     -1
+          XY30  S32     -1
+          XY31  S32     -1
+          XY32  S32     -1
+          XY33  S32     -1
+          XY34  S32     -1
+          XY35  S32     -1
+          XY36  S32     -1
+          XY37  S32     -1
+          XY40  S32     1
+          XY41  S32     1
+          XY42  S32     1
+          XY43  S32     1
+          XY44  S32     1
+          XY45  S32     1
+          XY46  S32     1
+          XY47  S32     1
+          XY50  S32     1
+          XY51  S32     1
+          XY52  S32     1
+          XY53  S32     1
+          XY54  S32     1
+          XY55  S32     1
+          XY56  S32     1
+          XY57  S32     1
+          XY60  S32     1
+          XY61  S32     1
+          XY62  S32     1
+          XY63  S32     1
+          XY64  S32     1
+          XY65  S32     1
+          XY66  S32     1
+          XY67  S32     1
+          XY71  S32     1
+          XY72  S32     1
+          XY73  S32     1
+          XY74  S32     1
+          XY75  S32     1
+          XY76  S32     1
+	END
+	CELL.GAIN	F32	1.0
+	CELL.READNOISE	F32	0.0
+	CELL.READDIR	S32	1
+	CELL.BAD	S32	0
+	CELL.XSIZE		S32	590
+	CELL.YSIZE		S32	598
+#	CELL.TIME	STR	MJD-OBS
+#	CELL.TIMESYS	STR	TIMESYS
+#	CELL.SATURATION	STR	60000
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP,CELL
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP,CELL
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	DEGREES
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+	CELL.BINNING	STR	TOGETHER
+	CELL.X0		STR	FORTRAN
+	CELL.Y0		STR	FORTRAN
+END
+ 
+# Recipe options
+RECIPES		METADATA
+END
+ 
+# How to get the supplementary stuff: mask and weight
+SUPPLEMENTARY	METADATA
+	MASK.SOURCE	STR	FILE		# Source type for mask: EXT | FILE
+	MASK.NAME	STR	%a_mask.fits	# Name for mask extension or filename
+	WEIGHT.SOURCE	STR	FILE		# Source type for weight: EXT | FILE
+	WEIGHT.NAME	STR	%a_weight.fits	# Name for weight extension or filename
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/format_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/format_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/format_raw.config	(revision 22322)
@@ -0,0 +1,599 @@
+# The raw GPC data comes off the telescope with each of the chips stored in separate files
+
+# How to identify this type
+RULE    METADATA
+#	ORIGIN		STR	PS1
+#        TELESCOP        STR     PS1
+###	SOMETHING	STR	GPC1
+        CONTROLR        STR     STARGRASP
+        NAMPS           S32     64
+END
+
+# How to read this data
+FILE    METADATA
+        PHU             STR     CHIP       # The FITS file represents a single chip
+        EXTENSIONS      STR     CELL       # The extensions represent cells
+        FPA.OBS         STR     CONTROLR   # A PHU keyword for unique identifier within the hierarchy level
+        CONTENT         STR     DETECTOR   # How to determine content of FITS file
+        CONTENT.RULE    STR     {CHIP.ID}  # How to derive the CONTENT when writing
+END
+
+# What's in the FITS file?
+CONTENTS        METADATA
+        # CONTENT      =    chip name : type
+        CCID58-2-15a1  STR  XY01:GPCChip  # chip 00
+        CCID58-2-18a1  STR  XY02:GPCChip  # chip 01
+        CCID58-1-13a1  STR  XY03:GPCChip  # chip 02
+        CCID58-1-09a1  STR  XY04:GPCChip  # chip 03
+        CCID58-2-16b1  STR  XY05:GPCChip  # chip 04
+        CCID58-2-09b1  STR  XY06:GPCChip  # chip 05
+        CCID58-2-22a1  STR  XY10:GPCChip  # chip 06
+        CCID58-2-13a2  STR  XY11:GPCChip  # chip 07
+        CCID58-1-17a2  STR  XY12:GPCChip  # chip 08
+        CCID58-1-14a2  STR  XY13:GPCChip  # chip 09
+        CCID58-1-09a2  STR  XY14:GPCChip  # chip 10
+        CCID58-2-13b1  STR  XY15:GPCChip  # chip 11
+        CCID58-1-09b1  STR  XY16:GPCChip  # chip 12
+        CCID58-2-10b2  STR  XY17:GPCChip  # chip 13
+        CCID58-1-19a1  STR  XY20:GPCChip  # chip 14
+        CCID58-1-04a1  STR  XY21:GPCChip  # chip 15
+        CCID58-2-09a2  STR  XY22:GPCChip  # chip 16
+        CCID58-1-02a1  STR  XY23:GPCChip  # chip 17
+        CCID58-1-04a2  STR  XY24:GPCChip  # chip 18
+        CCID58-1-01b1  STR  XY25:GPCChip  # chip 19
+        CCID58-2-07b2  STR  XY26:GPCChip  # chip 20
+        CCID58-1-18b1  STR  XY27:GPCChip  # chip 21
+        CCID58-1-12a1  STR  XY30:GPCChip  # chip 22
+        CCID58-2-01a1  STR  XY31:GPCChip  # chip 23
+        CCID58-2-01a2  STR  XY32:GPCChip  # chip 24
+        CCID58-1-02a2  STR  XY33:GPCChip  # chip 25
+        CCID58-2-04a1  STR  XY34:GPCChip  # chip 26
+        CCID58-1-01b2  STR  XY35:GPCChip  # chip 27
+        CCID58-1-02b1  STR  XY36:GPCChip  # chip 28
+        CCID58-2-16b2  STR  XY37:GPCChip  # chip 29
+        CCID58-2-18b2  STR  XY40:GPCChip  # chip 30
+        CCID58-1-03b2  STR  XY41:GPCChip  # chip 31
+        CCID58-1-07b1  STR  XY42:GPCChip  # chip 32
+        CCID58-2-11b2  STR  XY43:GPCChip  # chip 33
+        CCID58-1-05a2  STR  XY44:GPCChip  # chip 34
+        CCID58-1-21a2  STR  XY45:GPCChip  # chip 35
+        CCID58-2-07a2  STR  XY46:GPCChip  # chip 36
+        CCID58-1-21a1  STR  XY47:GPCChip  # chip 37
+        CCID58-1-17b1  STR  XY50:GPCChip  # chip 38
+        CCID58-2-09b2  STR  XY51:GPCChip  # chip 39
+        CCID58-1-04b1  STR  XY52:GPCChip  # chip 40
+        CCID58-1-05b2  STR  XY53:GPCChip  # chip 41
+        CCID58-2-09a1  STR  XY54:GPCChip  # chip 42
+        CCID58-2-12a2  STR  XY55:GPCChip  # chip 43
+        CCID58-1-10a2  STR  XY56:GPCChip  # chip 44
+        CCID58-2-12a1  STR  XY57:GPCChip  # chip 45
+        CCID58-2-22b2  STR  XY60:GPCChip  # chip 46
+        CCID58-1-03b1  STR  XY61:GPCChip  # chip 47
+        CCID58-2-23b2  STR  XY62:GPCChip  # chip 48
+        CCID58-1-02b2  STR  XY63:GPCChip  # chip 49
+        CCID58-1-07a1  STR  XY64:GPCChip  # chip 50
+        CCID58-2-17a2  STR  XY65:GPCChip  # chip 51
+        CCID58-2-14a1  STR  XY66:GPCChip  # chip 52
+        CCID58-2-16a2  STR  XY67:GPCChip  # chip 53
+        CCID58-2-04b2  STR  XY71:GPCChip  # chip 54
+        CCID58-1-14b1  STR  XY72:GPCChip  # chip 55
+        CCID58-1-25b1  STR  XY73:GPCChip  # chip 56
+        CCID58-1-18a1  STR  XY74:GPCChip  # chip 57
+        CCID58-2-16a1  STR  XY75:GPCChip  # chip 58
+        CCID58-2-05a1  STR  XY76:GPCChip  # chip 59
+END
+
+CHIPS   METADATA
+        GPCChip         METADATA
+                # Extension name, cellName:cellType
+                xy00   STR  xy00:GPCCell
+                xy10   STR  xy10:GPCCell
+                xy20   STR  xy20:GPCCell
+                xy30   STR  xy30:GPCCell
+                xy40   STR  xy40:GPCCell
+                xy50   STR  xy50:GPCCell
+                xy60   STR  xy60:GPCCell
+                xy70   STR  xy70:GPCCell
+                xy01   STR  xy01:GPCCell
+                xy11   STR  xy11:GPCCell
+                xy21   STR  xy21:GPCCell
+                xy31   STR  xy31:GPCCell
+                xy41   STR  xy41:GPCCell
+                xy51   STR  xy51:GPCCell
+                xy61   STR  xy61:GPCCell
+                xy71   STR  xy71:GPCCell
+                xy02   STR  xy02:GPCCell
+                xy12   STR  xy12:GPCCell
+                xy22   STR  xy22:GPCCell
+                xy32   STR  xy32:GPCCell
+                xy42   STR  xy42:GPCCell
+                xy52   STR  xy52:GPCCell
+                xy62   STR  xy62:GPCCell
+                xy72   STR  xy72:GPCCell
+                xy03   STR  xy03:GPCCell
+                xy13   STR  xy13:GPCCell
+                xy23   STR  xy23:GPCCell
+                xy33   STR  xy33:GPCCell
+                xy43   STR  xy43:GPCCell
+                xy53   STR  xy53:GPCCell
+                xy63   STR  xy63:GPCCell
+                xy73   STR  xy73:GPCCell
+                xy04   STR  xy04:GPCCell
+                xy14   STR  xy14:GPCCell
+                xy24   STR  xy24:GPCCell
+                xy34   STR  xy34:GPCCell
+                xy44   STR  xy44:GPCCell
+                xy54   STR  xy54:GPCCell
+                xy64   STR  xy64:GPCCell
+                xy74   STR  xy74:GPCCell
+                xy05   STR  xy05:GPCCell
+                xy15   STR  xy15:GPCCell
+                xy25   STR  xy25:GPCCell
+                xy35   STR  xy35:GPCCell
+                xy45   STR  xy45:GPCCell
+                xy55   STR  xy55:GPCCell
+                xy65   STR  xy65:GPCCell
+                xy75   STR  xy75:GPCCell
+                xy06   STR  xy06:GPCCell
+                xy16   STR  xy16:GPCCell
+                xy26   STR  xy26:GPCCell
+                xy36   STR  xy36:GPCCell
+                xy46   STR  xy46:GPCCell
+                xy56   STR  xy56:GPCCell
+                xy66   STR  xy66:GPCCell
+                xy76   STR  xy76:GPCCell
+                xy07   STR  xy07:GPCCell
+                xy17   STR  xy17:GPCCell
+                xy27   STR  xy27:GPCCell
+                xy37   STR  xy37:GPCCell
+                xy47   STR  xy47:GPCCell
+                xy57   STR  xy57:GPCCell
+                xy67   STR  xy67:GPCCell
+                xy77   STR  xy77:GPCCell
+        END
+END
+
+# Specify the cell data
+CELLS   METADATA
+        GPCCell         METADATA
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC            STR     DATASEC
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.BIASSEC            STR     BIASSEC
+        END
+END
+
+
+# How to translate PS concepts into FITS headers
+TRANSLATION     METADATA
+        FPA.FILTERID    STR     FILTERID
+        FPA.FILTER      STR     FILTERID
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        # FPA.RA          STR     COMRA
+        # FPA.DEC         STR     COMDEC
+        FPA.RADECSYS    STR     RADECSYS
+        FPA.OBSTYPE     STR     OBSTYPE
+        FPA.OBJECT      STR     OBJECT
+        FPA.COMMENT     STR     CMMTOBS
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.POSANGLE    STR     POSANGLE
+        FPA.FOCUS       STR     M2Z
+        FPA.TIME        STR     SHUTOUTC
+        FPA.ALT         STR     ALT
+        FPA.AZ          STR     AZ
+        FPA.TEMP        STR     DETTEM
+        FPA.M1X         STR     M1X
+        FPA.M1Y         STR     M1Y   
+        FPA.M1Z         STR     M1Z   
+        FPA.M1TIP       STR     M1TIP 
+        FPA.M1TILT      STR     M1TILT
+        FPA.M2X         STR     M2X
+        FPA.M2Y         STR     M2Y   
+        FPA.M2Z         STR     M2Z   
+        FPA.M2TIP       STR     M2TIP 
+        FPA.M2TILT      STR     M2TILT
+        FPA.ENV.TEMP    STR     ENVTEM
+        FPA.ENV.HUMID   STR     ENVHUM
+        FPA.ENV.WIND    STR     ENVWIN
+        FPA.ENV.DIR     STR     ENVDIR
+
+        FPA.TELTEMP.M1     STR  TELTEMM1 # Primary mirror temps (C) 
+        FPA.TELTEMP.M1CELL STR  TELTEMMS # Primary mirror support temps (C)   
+        FPA.TELTEMP.M2     STR  TELTEMM2 # Secondary mirror temps (C
+        FPA.TELTEMP.SPIDER STR  TELTEMSP # Spider temperatures (C)  
+        FPA.TELTEMP.TRUSS  STR  TELTEMTR # Mid truss temperatures (C
+        FPA.TELTEMP.EXTRA  STR  TELTEMEX # Miscellaneous temperatures (C)     
+
+        FPA.PON.TIME    STR     PONTIME
+
+        CHIP.ID         STR     DETECTOR
+        CELL.XBIN       STR     CCDSUM
+        CELL.YBIN       STR     CCDSUM
+        CELL.X0         STR     IMNPIX1
+        CELL.Y0         STR     IMNPIX2
+        CELL.XPARITY    STR     ATM1_1
+        CELL.YPARITY    STR     ATM2_2
+#       CELL.SATURATION STR     SATURATE
+
+#        CHIP.TEMP       STR     DETTEM	# XY24 is bad, so get it from the database; all others from header
+	CHIP.TEMP.DEPEND	STR	CHIP.NAME
+	CHIP.TEMP	METADATA
+		XY01	STR	DETTEM
+		XY02	STR	DETTEM
+		XY03	STR	DETTEM
+		XY04	STR	DETTEM
+		XY05	STR	DETTEM
+		XY06	STR	DETTEM
+		XY10	STR	DETTEM
+		XY11	STR	DETTEM
+		XY12	STR	DETTEM
+		XY13	STR	DETTEM
+		XY14	STR	DETTEM
+		XY15	STR	DETTEM
+		XY16	STR	DETTEM
+		XY17	STR	DETTEM
+		XY20	STR	DETTEM
+		XY21	STR	DETTEM
+		XY22	STR	DETTEM
+		XY23	STR	DETTEM
+#		XY24	STR	DETTEM	# This is currently bad --- get it from the database
+		XY25	STR	DETTEM
+		XY26	STR	DETTEM
+		XY27	STR	DETTEM
+		XY30	STR	DETTEM
+		XY31	STR	DETTEM
+		XY32	STR	DETTEM
+		XY33	STR	DETTEM
+		XY34	STR	DETTEM
+		XY35	STR	DETTEM
+		XY36	STR	DETTEM
+		XY37	STR	DETTEM
+		XY40	STR	DETTEM
+		XY41	STR	DETTEM
+		XY42	STR	DETTEM
+		XY43	STR	DETTEM
+		XY44	STR	DETTEM
+		XY45	STR	DETTEM
+		XY46	STR	DETTEM
+		XY47	STR	DETTEM
+		XY50	STR	DETTEM
+		XY51	STR	DETTEM
+		XY52	STR	DETTEM
+		XY53	STR	DETTEM
+		XY54	STR	DETTEM
+		XY55	STR	DETTEM
+		XY56	STR	DETTEM
+		XY57	STR	DETTEM
+		XY60	STR	DETTEM
+		XY61	STR	DETTEM
+		XY62	STR	DETTEM
+		XY63	STR	DETTEM
+		XY64	STR	DETTEM
+		XY65	STR	DETTEM
+		XY66	STR	DETTEM
+		XY67	STR	DETTEM
+		XY71	STR	DETTEM
+		XY72	STR	DETTEM
+		XY73	STR	DETTEM
+		XY74	STR	DETTEM
+		XY75	STR	DETTEM
+		XY76	STR	DETTEM
+	END
+
+        FPA.EXPOSURE    STR     EXPREQ          # Requested exposure time, presumably camera exposure time
+        CELL.EXPOSURE   STR     EXPTIME         # Exposure time measured by shutter
+        CELL.DARKTIME   STR     DARKTIME        # Exposure time for camera
+        CELL.TIME       STR     SHUTOUTC		# Observation time
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+        FPA.TELESCOPE   STR     PS1
+        FPA.INSTRUMENT  STR     GPC1
+        FPA.LONGITUDE   STR     +10:25:01.417 # West Longitude in Hours 10.4170608521
+        FPA.LATITUDE    STR     +20:42:25.558 # North Latitude in Degrees 20.7070999146
+        # FPA.LONGITUDE   F32     10.4170608521 # West Longitude in Hours 
+        # FPA.LATITUDE    F32     20.7070999146 # North Latitude in Degrees 
+        FPA.ELEVATION   F32     3048.0 # altitude in meters
+        FPA.DETECTOR    STR     GPC1
+        FPA.TIMESYS     STR     UTC
+	CHIP.XSIZE		S32	4846
+	CHIP.YSIZE		S32	4868
+        CHIP.XPARITY    S32     1
+        CHIP.YPARITY    S32     1
+        CHIP.X0.DEPEND          STR     CHIP.NAME
+        CHIP.X0         METADATA
+          XY01  S32     4971
+          XY02  S32     4971
+          XY03  S32     4971
+          XY04  S32     4971
+          XY05  S32     4971
+          XY06  S32     4971
+          XY10  S32     9942
+          XY11  S32     9942
+          XY12  S32     9942
+          XY13  S32     9942
+          XY14  S32     9942
+          XY15  S32     9942
+          XY16  S32     9942
+          XY17  S32     9942
+          XY20  S32     14913
+          XY21  S32     14913
+          XY22  S32     14913
+          XY23  S32     14913
+          XY24  S32     14913
+          XY25  S32     14913
+          XY26  S32     14913
+          XY27  S32     14913
+          XY30  S32     19884
+          XY31  S32     19884
+          XY32  S32     19884
+          XY33  S32     19884
+          XY34  S32     19884
+          XY35  S32     19884
+          XY36  S32     19884
+          XY37  S32     19884
+          XY40  S32     20041
+          XY41  S32     20041
+          XY42  S32     20041
+          XY43  S32     20041
+          XY44  S32     20041
+          XY45  S32     20041
+          XY46  S32     20041
+          XY47  S32     20041
+          XY50  S32     25012
+          XY51  S32     25012
+          XY52  S32     25012
+          XY53  S32     25012
+          XY54  S32     25012
+          XY55  S32     25012
+          XY56  S32     25012
+          XY57  S32     25012
+          XY60  S32     29983
+          XY61  S32     29983
+          XY62  S32     29983
+          XY63  S32     29983
+          XY64  S32     29983
+          XY65  S32     29983
+          XY66  S32     29983
+          XY67  S32     29983
+          XY71  S32     34954
+          XY72  S32     34954
+          XY73  S32     34954
+          XY74  S32     34954
+          XY75  S32     34954
+          XY76  S32     34954
+        END
+        CHIP.Y0.DEPEND          STR     CHIP.NAME
+        CHIP.Y0         METADATA
+          XY01  S32     10286
+          XY02  S32     15429
+          XY03  S32     20572
+          XY04  S32     25715
+          XY05  S32     30858
+          XY06  S32     36001
+          XY10  S32     5143
+          XY11  S32     10286
+          XY12  S32     15429
+          XY13  S32     20572
+          XY14  S32     25715
+          XY15  S32     30858
+          XY16  S32     36001
+          XY17  S32     41144
+          XY20  S32     5143
+          XY21  S32     10286
+          XY22  S32     15429
+          XY23  S32     20572
+          XY24  S32     25715
+          XY25  S32     30858
+          XY26  S32     36001
+          XY27  S32     41144
+          XY30  S32     5143
+          XY31  S32     10286
+          XY32  S32     15429
+          XY33  S32     20572
+          XY34  S32     25715
+          XY35  S32     30858
+          XY36  S32     36001
+          XY37  S32     41144
+          XY40  S32     306
+          XY41  S32     5449
+          XY42  S32     10592
+          XY43  S32     15735
+          XY44  S32     20878
+          XY45  S32     26021
+          XY46  S32     31164
+          XY47  S32     36307
+          XY50  S32     306
+          XY51  S32     5449
+          XY52  S32     10592
+          XY53  S32     15735
+          XY54  S32     20878
+          XY55  S32     26021
+          XY56  S32     31164
+          XY57  S32     36307
+          XY60  S32     306
+          XY61  S32     5449
+          XY62  S32     10592
+          XY63  S32     15735
+          XY64  S32     20878
+          XY65  S32     26021
+          XY66  S32     31164
+          XY67  S32     36307
+          XY71  S32     5449
+          XY72  S32     10592
+          XY73  S32     15735
+          XY74  S32     20878
+          XY75  S32     26021
+          XY76  S32     31164
+        END
+        CHIP.XPARITY.DEPEND     STR     CHIP.NAME
+        CHIP.XPARITY    METADATA
+          XY01  S32     -1
+          XY02  S32     -1
+          XY03  S32     -1
+          XY04  S32     -1
+          XY05  S32     -1
+          XY06  S32     -1
+          XY10  S32     -1
+          XY11  S32     -1
+          XY12  S32     -1
+          XY13  S32     -1
+          XY14  S32     -1
+          XY15  S32     -1
+          XY16  S32     -1
+          XY17  S32     -1
+          XY20  S32     -1
+          XY21  S32     -1
+          XY22  S32     -1
+          XY23  S32     -1
+          XY24  S32     -1
+          XY25  S32     -1
+          XY26  S32     -1
+          XY27  S32     -1
+          XY30  S32     -1
+          XY31  S32     -1
+          XY32  S32     -1
+          XY33  S32     -1
+          XY34  S32     -1
+          XY35  S32     -1
+          XY36  S32     -1
+          XY37  S32     -1
+          XY40  S32     1
+          XY41  S32     1
+          XY42  S32     1
+          XY43  S32     1
+          XY44  S32     1
+          XY45  S32     1
+          XY46  S32     1
+          XY47  S32     1
+          XY50  S32     1
+          XY51  S32     1
+          XY52  S32     1
+          XY53  S32     1
+          XY54  S32     1
+          XY55  S32     1
+          XY56  S32     1
+          XY57  S32     1
+          XY60  S32     1
+          XY61  S32     1
+          XY62  S32     1
+          XY63  S32     1
+          XY64  S32     1
+          XY65  S32     1
+          XY66  S32     1
+          XY67  S32     1
+          XY71  S32     1
+          XY72  S32     1
+          XY73  S32     1
+          XY74  S32     1
+          XY75  S32     1
+          XY76  S32     1
+        END
+        CHIP.YPARITY.DEPEND     STR     CHIP.NAME
+        CHIP.YPARITY    METADATA
+          XY01  S32     -1
+          XY02  S32     -1
+          XY03  S32     -1
+          XY04  S32     -1
+          XY05  S32     -1
+          XY06  S32     -1
+          XY10  S32     -1
+          XY11  S32     -1
+          XY12  S32     -1
+          XY13  S32     -1
+          XY14  S32     -1
+          XY15  S32     -1
+          XY16  S32     -1
+          XY17  S32     -1
+          XY20  S32     -1
+          XY21  S32     -1
+          XY22  S32     -1
+          XY23  S32     -1
+          XY24  S32     -1
+          XY25  S32     -1
+          XY26  S32     -1
+          XY27  S32     -1
+          XY30  S32     -1
+          XY31  S32     -1
+          XY32  S32     -1
+          XY33  S32     -1
+          XY34  S32     -1
+          XY35  S32     -1
+          XY36  S32     -1
+          XY37  S32     -1
+          XY40  S32     1
+          XY41  S32     1
+          XY42  S32     1
+          XY43  S32     1
+          XY44  S32     1
+          XY45  S32     1
+          XY46  S32     1
+          XY47  S32     1
+          XY50  S32     1
+          XY51  S32     1
+          XY52  S32     1
+          XY53  S32     1
+          XY54  S32     1
+          XY55  S32     1
+          XY56  S32     1
+          XY57  S32     1
+          XY60  S32     1
+          XY61  S32     1
+          XY62  S32     1
+          XY63  S32     1
+          XY64  S32     1
+          XY65  S32     1
+          XY66  S32     1
+          XY67  S32     1
+          XY71  S32     1
+          XY72  S32     1
+          XY73  S32     1
+          XY74  S32     1
+          XY75  S32     1
+          XY76  S32     1
+        END
+        CELL.GAIN       F32     1.0
+        CELL.READNOISE  F32     15.0
+        CELL.READDIR    S32     1
+        CELL.BAD        S32     -100
+	CELL.XSIZE	S32	590
+	CELL.YSIZE	S32	598
+        CELL.SATURATION F32     40000.0
+        CELL.TIMESYS    STR     UTC
+END
+
+# How to translation PS concepts into database lookups
+DATABASE        METADATA
+	CHIP.TEMP.DEPEND	STR	CHIP.NAME
+	CHIP.TEMP		METADATA
+		XY24	STR	SELECT AVG(ccd_temp) FROM rawImfile WHERE dateobs = '{FPA.TIME}' AND class_id != 'XY24'
+	END
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS         METADATA
+        FPA.RA          STR     DEGREES
+        FPA.DEC         STR     DEGREES
+	FPA.LONGITUDE	STR	HOURS
+	FPA.LATITUDE	STR	DEGREES
+        FPA.TIME        STR     YEAR.FIRST
+        CELL.TIME       STR     YEAR.FIRST
+        CELL.BINNING    STR     TOGETHER
+        CELL.X0         STR     FORTRAN
+        CELL.Y0         STR     FORTRAN
+END
+ 
+# Recipe options
+RECIPES         METADATA
+END
+ 
+# How to get the supplementary stuff: mask and weight
+SUPPLEMENTARY   METADATA
+        MASK.SOURCE     STR     FILE            # Source type for mask: EXT | FILE
+        MASK.NAME       STR     %a_mask.fits    # Name for mask extension or filename
+        WEIGHT.SOURCE   STR     FILE            # Source type for weight: EXT | FILE
+        WEIGHT.NAME     STR     %a_weight.fits  # Name for weight extension or filename
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/ppImage.config	(revision 22322)
@@ -0,0 +1,319 @@
+
+# binned output image options
+BIN1.XBIN               S32      16
+BIN1.YBIN               S32      16
+BIN2.XBIN               S32     150
+BIN2.YBIN               S32     150
+
+OVERSCAN.SINGLE         BOOL    FALSE            # Reduce overscan to a single value?
+OVERSCAN.STAT           STR     MEDIAN
+OVERSCAN.BOXCAR         S32     3
+
+OLDDARK                 BOOL    FALSE
+
+## XXX use these local variations until we are building real detrend images
+
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+    EXPTIME STR FPA.EXPOSURE
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+  END
+  FLAT_CORRECTION METADATA
+    FILTER   STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+  END
+  SHUTTER METADATA
+  END
+  MASK METADATA
+  END	
+  ASTROM METADATA
+  END	
+END
+
+# Overscan, bias
+PPIMAGE_OB         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark
+PPIMAGE_OBD        METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS       METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDSF      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_DET_ONLY   METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  REPLACE.MASKED   BOOL    TRUE            # Fill in masked pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  FPA1.FITS        BOOL    TRUE            # Save 1st binned fpa image? 
+  FPA2.FITS        BOOL    TRUE            # Save 2nd binned fpa image? 
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR     METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  REPLACE.MASKED   BOOL    TRUE            # Fill in masked pixels
+  REPLACE.MODE     STR     MODEL           # VALUE, MODEL
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRP    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  REPLACE.MASKED   BOOL    TRUE            # Fill in masked pixels
+  REPLACE.MODE     STR     MODEL           # VALUE, MODEL
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  REPLACE.MASKED   BOOL    TRUE            # Fill in masked pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Photometry and astrometry only (for pre-reduced data)
+PPIMAGE_PA         METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    TRUE            # Save 2nd binned jpeg?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# JPEG images for different types of residual images
+# Save JPEG from BIN1 for bias
+PPIMAGE_J1_RESID_B      METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    FALSE           # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+
+  # GPC1 has significant dark structures: use a wide greyscale range
+  PPIMAGE.JPEG1    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     VALUE
+    SCALE.MIN      F32     -50.0
+    SCALE.MAX      F32     +50.0
+  END
+END
+
+# Save JPEG from BIN2 for bias
+PPIMAGE_J2_RESID_B        METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    TRUE            # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+
+  # GPC1 has significant dark structures: use a wide greyscale range
+  PPIMAGE.JPEG2    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     VALUE
+    SCALE.MIN      F32     -50.0
+    SCALE.MAX      F32     +50.0
+  END
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/ppMerge.config	(revision 22322)
@@ -0,0 +1,29 @@
+# Mask generation --- already included in default, above
+PPMERGE_DARKMASK METADATA
+  ITER           S32    3
+  MASK.SUSPECT	 F32	3.0		# Threshold for suspect pixels (sigma)
+  MASK.BAD	 F32	0.1		# Threshold for bad pixels 
+  MASK.MODE	 STR	FRACTION	# Meaning of threshold : value, sigma
+  SAMPLE         S32    100000          # Sampling factor for measuring the background
+END
+
+PPMERGE_FLATMASK METADATA
+  ITER           S32    3
+  MASK.SUSPECT	 F32	3.0		# Threshold for suspect pixels (sigma)
+  MASK.BAD	 F32	0.1		# Threshold for bad pixels (sigma)
+  MASK.MODE	 STR	FRACTION	# Threshold for bad pixels (sigma)
+  SAMPLE         S32    100000          # Sampling factor for measuring the background
+END
+
+ROWS		S32	32		# Number of rows to read at once
+
+# Ordinates for fitting dark current
+DARK.ORDINATES	METADATA
+	CELL.DARKTIME	S32	1
+	CHIP.TEMP	METADATA
+		ORDER	S32	1
+		SCALE	BOOL	TRUE
+		MIN	F32	-95
+		MAX	F32	-50
+	END
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/ppStack.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/ppStack.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/ppStack.config	(revision 22322)
@@ -0,0 +1,33 @@
+# Recipe configuration for ppStack (image combination)
+
+ITER		S32	1		# Number of rejection iterations
+COMBINE.REJ	F32	4.0		# Rejection threshold in combination (sigma)
+MASK.VAL	STR	MASK.VALUE,BAD.WARP	# 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
+POOR.FRACTION	F32	0.1		# Maximum fraction of bad weight for poor pixels
+THRESHOLD.MASK	F32	0.8		# Threshold for mask deconvolution (0..1)
+IMAGE.REJ	F32	0.2		# Rejected pixel fraction threshold for rejecting entire image
+ROWS		S32	64		# Number of rows to read at once
+VARIANCE	BOOL	TRUE		# Use variance in rejection?
+SAFE		BOOL	FALSE		# Play safe when combining small number of values?
+
+RENORM		BOOL	FALSE		# Renormalise variance maps?
+RENORM.MEAN	STR	ROBUST_MEDIAN	# Statistic to use for mean in renormalisation
+RENORM.STDEV	STR	ROBUST_STDEV	# Statistic to use for stdev in renormalisation
+RENORM.WIDTH	S32	300		# Size of renormalisation boxes (pixels)
+
+SOURCE.RADIUS	F32	1.0		# Radius (pixels) for matching sources
+SOURCE.MIN	S32	15		# Minimum number of sources for merging source lists
+SOURCE.ITER	S32	2		# Number of rejection iterations for magnitude difference
+SOURCE.REJ	F32	2.0		# Rejection limit (sigma) for magnitude difference
+
+PSF.INSTANCES	S32	9		# Number of instances for PSF generation
+PSF.RADIUS	F32	20.0		# Radius for PSF generation
+PSF.ORDER	S32	2		# Order of spatial variation for PSF generation
+PSF.MODEL	STR	PS_MODEL_GAUSS	# Model for PSF generation
+
+TEMP.IMAGE	STR	conv.im.fits	# Suffix for convolved images
+TEMP.MASK	STR	conv.mk.fits	# Suffix for convolved masks
+TEMP.WEIGHT	STR	conv.wt.fits	# Suffix for convolved weight maps
+TEMP.DELETE	BOOL	FALSE		# Delete temporary files on completion?
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/psastro.config	(revision 22322)
@@ -0,0 +1,108 @@
+	
+# astrometry matching parameters
+
+# nominal plate scale (units / pixel)
+# XXX we are using pixels here because of the limited dynamic range for 
+# NAXIS1,2 when loaded by addstar.  
+PSASTRO.PIXEL.SCALE    F32  1.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32    1000.
+PSASTRO.GRID.SCALE     F32      50
+PSASTRO.GRID.NSTAR.MAX S32     800 # max stars accepted for fitting
+
+PSASTRO.MAX.NRAW      S32       200   # max stars accepted for fitting (0 for all)
+PSASTRO.MAX.NREF      S32      5000   # max stars accepted for fitting (0 for all)
+
+# extra field for ref stars:
+PSASTRO.FIELD.PADDING  F32     0.3   # pad by this fraction of the full mosaic (may need to be larger for single chips)
+
+# 
+PSASTRO.MIN.INST.MAG.RAW       F32     -17.0   # min instrumental magnitude for stars accepted for fitting
+PSASTRO.MAX.INST.MAG.RAW       F32     -10.0   # max instrumental magnitude for stars accepted for fitting
+
+#
+PSASTRO.GRID.MIN.ANGLE F32 -1.0 # start angle (degrees)
+PSASTRO.GRID.MAX.ANGLE F32 +1.0
+PSASTRO.GRID.DEL.ANGLE F32  0.5
+
+#
+PSASTRO.GRID.MIN.SCALE F32  1.00
+PSASTRO.GRID.MAX.SCALE F32  1.00
+# PSASTRO.GRID.MIN.SCALE F32  0.98
+# PSASTRO.GRID.MAX.SCALE F32  1.021
+PSASTRO.GRID.DEL.SCALE F32  0.01
+
+# single-chip radius match in pixels
+PSASTRO.MATCH.RADIUS.N0 F32    90
+PSASTRO.MATCH.RADIUS.N1 F32    60
+PSASTRO.MATCH.RADIUS.N2 F32    30
+PSASTRO.MATCH.RADIUS.N3 F32    20
+PSASTRO.MATCH.RADIUS.N4 F32    20
+PSASTRO.MATCH.RADIUS.N5 F32    20
+PSASTRO.MATCH.RADIUS.N6 F32    10
+PSASTRO.MATCH.RADIUS.N7 F32    10
+PSASTRO.MATCH.FIT.NITER S32     8
+
+# XXX Test for taurus
+PSASTRO.GRID.MIN.SIGMA  F32  1.0
+PSASTRO.IGNORE          STR  SATURATED,CRLIMIT,DEFECT,BLEND,FAIL
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32    150
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+PSASTRO.USE.MODEL             BOOL     TRUE
+
+# PSASTRO.FIX.CHIPS             BOOL     TRUE
+PSASTRO.PIXEL.TOLERANCE       F32      20.0
+PSASTRO.ANGLE.TOLERANCE       F32      1.0
+
+# Mosaic Astrometry options
+PSASTRO.MOSAIC.MODE         BOOL   FALSE
+# PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.80 # max allow error for valid solution (arcsec)
+# PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.20 # max allow error for valid solution (arcsec)
+# PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.80 # max allow error for valid solution (arcsec)
+# PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.50 # max allow error for valid solution (arcsec)
+
+PSASTRO.MOSAIC.MAX.ERROR.N0 F32    5.00 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N1 F32    0.00 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.00 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.00 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N4 F32    5.00 # max allow error for valid solution (arcsec)
+
+# mosaic radius match in pixels
+PSASTRO.MOSAIC.RADIUS.N0    F32    5
+PSASTRO.MOSAIC.RADIUS.N1    F32    0
+PSASTRO.MOSAIC.RADIUS.N2    F32    0
+PSASTRO.MOSAIC.RADIUS.N3    F32    0
+PSASTRO.MOSAIC.RADIUS.N4    F32    20
+
+PSASTRO.MOSAIC.CHIP.ORDER     S32      1  # limit chip-fit order to 1
+PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N4  S32      3 # fit order (-1 means use default)
+
+# we need to allow a loose fit if we are fitting to 2mass (50mas internal error -> 100mas limit?)
+# if we are fitting against quality digital data, we can require tighter constraints
+
+PSASTRO.MODEL.REF.CHIP        STR      XY33
+
+PSASTRO.MODEL.SET.BORESITE    BOOL     FALSE
+PSASTRO.MODEL.BORESITE.X      F32      -62.0
+PSASTRO.MODEL.BORESITE.Y      F32     -139.0
+#PSASTRO.MODEL.BORESITE.X      F32      -200.0
+#PSASTRO.MODEL.BORESITE.Y      F32     -300.0
+
+
+PSASTRO.CATDIR              STR      2MASS
+DVO.GETSTAR.PHOTCODE        STR      2MASS_J
+DVO.GETSTAR.MAG.MAX         F32      13.0
+# DVO.GETSTAR.MAG.MAX         F32      20.0
+# XXX need to be able to limit the density!
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/psphot.config	(revision 22322)
@@ -0,0 +1,51 @@
+
+# turn these on to see specific outputs
+SAVE.OUTPUT	BOOL 	TRUE
+SAVE.BACKMDL	BOOL 	TRUE
+#SAVE.RESID	BOOL 	TRUE
+#SAVE.BACKGND	BOOL 	TRUE
+#SAVE.BACKSUB	BOOL 	TRUE
+#SAVE.PSF	BOOL 	TRUE
+#LOAD.PSF	BOOL 	FALSE
+#SAVE.PLOTS     	BOOL    TRUE
+
+BACKGROUND.XBIN	    S32  256            # size of background superpixels
+BACKGROUND.YBIN	    S32  256            # size of background superpixels
+
+# image background parameters
+IMSTATS_NPIX        S32  10000    	 # number of pixels to use for sky estimate boxes:
+SKY_STAT            STR  FITTED_MEAN_V4  # statistic used to measure background
+SKY_CLIP_SIGMA      F32  2.0             # statistic used to measure background
+
+PEAKS_SMOOTH_SIGMA  F32   2.5            # smoothing kernel sigma in pixels
+PEAKS_NMAX          S32   5000           # on first pass, only keep NMAX peaks (0 == all)
+PEAKS_NSIGMA_LIMIT  F32   25.0            # peak significance threshold
+PEAKS_NSIGMA_LIMIT_2 F32  10.0             # peak significance threshold
+
+PSF_SN_LIM          F32  25              # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+# PSF model parameters : choose the PSF model desired
+PSF_MODEL           STR  PS_MODEL_QGAUSS
+
+MOMENTS_SN_MIN      F32   30.0
+EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+FULL_FIT_SN_LIM      F32  50.0
+AP_MIN_SN            F32  50.0
+
+OUTPUT.FORMAT       STR PS1_DEV_1
+
+PSF_SHAPE_NSIGMA     F32  3.0		 # max significance for shape variation
+PSF_MAX_CHI          F32  50.0		 # reject objects worse that this
+APTREND.ORDER.MAX    S32  1
+MEASURE.APTREND      BOOL  FALSE  ### XXX for now, skip this (too many errors)
+
+# BREAK_POINT STR ENSEMBLE
+
+DIAGNOSTIC.PLOTS                    METADATA
+  IMAGE.BACKGROUND.CELL.HISTOGRAM   BOOL  FALSE
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.X S32   32
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.Y S32   15
+END
+
+USE_FOOTPRINTS                      BOOL  TRUE       	  # use new pmFootprint peak packaging
Index: /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/pswarp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/pswarp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/gpc1/pswarp.config	(revision 22322)
@@ -0,0 +1,1 @@
+ASTROM.SOURCE		STR	PSASTRO.OUTPUT	# Source file rule for astrometry, or NULL
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ipprc.config.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ipprc.config.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ipprc.config.in	(revision 22322)
@@ -0,0 +1,24 @@
+## Example .ipprc file : copy to your home directory and modify as desired
+
+# Default search path for configuration files (add $HOME if desired)
+# Note: do not include $HOME in the distributed copy used by ippMonitor
+# PATH            STR     @pkgdatadir@:$HOME/.ipp:.
+PATH              STR     @pkgdatadir@:.
+
+# load the site-specific information from here
+SITE              STR     site.config
+
+# load the system configuration information from here
+SYSTEM            STR     system.config
+
+# logging verbosity levels
+LOGLEVEL	S32	9			# Logging level; 3=INFO
+LOGFORMAT	STR	THLNM			# Log format
+LOGDEST		STR	STDERR			# Log destination
+TRACEDEST	STR	STDERR			# Trace destination
+
+# place default trace lines here
+TRACE		METADATA			# Trace levels
+  err		S32	10
+# psLib.db      S32	10
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/Makefile.am	(revision 22322)
@@ -0,0 +1,24 @@
+
+installdir = $(datadir)/ippconfig/isp
+
+install_files = \
+	dvo.config \
+	dvo.layout \
+	camera.config \
+	format.config \
+	cmp.config \
+	cmf.config \
+	ppImage.config \
+	ppMerge.config \
+	psastro.config \
+	psphot.config \
+	rejections.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/camera.config	(revision 22322)
@@ -0,0 +1,76 @@
+# Camera configuration file for the Pan-STARRS Imaging Sky Probe
+
+# File formats that we know about
+FORMATS         METADATA
+        SIMPLE  STR     isp/format.config
+        CMP     STR     isp/cmp.config
+        CMF     STR     isp/cmf.config
+END
+ 
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        Chip            STR     Cell
+END
+
+# valid filter names and corresponding IDs
+FILTER.ID       METADATA
+        g       STR     g
+        r       STR     r
+        i       STR     i
+        z       STR     z
+        y       STR     y
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	FPA	STR	fpa
+END
+
+DVO.CAMERADIR	STR	isp		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES         METADATA
+        PSPHOT          STR     isp/psphot.config               # psphot details
+        PSASTRO         STR     isp/psastro.config              # psastro details
+        PPIMAGE         STR     isp/ppImage.config              # Recipes for ppImage
+        PPMERGE         STR     isp/ppMerge.config              # Recipes for ppMerge
+	REJECTIONS	STR     isp/rejections.config
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES	STR	recipes/filerules-simple.mdc
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR cmf.hdr
+  CMF.DATA STR cmf.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD  STR	hdr
+  PSF.TABLE STR psf_model
+  PSF.RESID STR psf_resid
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/cmf.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/cmf.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/cmf.config	(revision 22322)
@@ -0,0 +1,84 @@
+# Pan-STARRS Imaging Sky Probe
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+	NAXIS		S32	0
+	TELESCOP	STR	ISP-1 
+	INSTRUME	STR	ISP-Apogee
+	DETECTOR	STR	ISP-Apogee-01
+	ISPCAMER	STR	Apogee U42
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	SEQID	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+	FPA.FILTER	STR	FILTNAME
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.TIME	STR	MJD-OBS
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.GAIN	STR	GAIN
+	CELL.READNOISE	STR	RDNOISE
+	CELL.XBIN	STR	XBIN
+	CELL.YBIN	STR	YBIN
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	65535
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/cmp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/cmp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/cmp.config	(revision 22322)
@@ -0,0 +1,84 @@
+# Pan-STARRS Imaging Sky Probe
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	FALSE
+	NAXIS		S32	0
+	TELESCOP	STR	ISP-1 
+	INSTRUME	STR	ISP-Apogee
+	DETECTOR	STR	ISP-Apogee-01
+	ISPCAMER	STR	Apogee U42
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	SEQID	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+	FPA.FILTER	STR	FILTNAME
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.TIME	STR	MJD-OBS
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.GAIN	STR	GAIN
+	CELL.READNOISE	STR	RDNOISE
+	CELL.XBIN	STR	XBIN
+	CELL.YBIN	STR	YBIN
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	65535
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/dvo.config	(revision 22322)
@@ -0,0 +1,50 @@
+
+# location of DVO database tables
+CATDIR			/data/alala/eugene/isp/catdir
+
+# keywords used by DVO to interpret the headers
+
+# used by parse_time to find time-related keywords
+MJD-KEYWORD		MJD-OBS
+DATE-KEYWORD		NONE
+DATE-MODE		NONE
+UT-KEYWORD		NONE
+JD-KEYWORD		NONE
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		-7.0
+CAL_INSTMAG_MIN		-9.5
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# allowed astrometry error (arcseconds)
+ADDSTAR_MAX_CERROR	5.0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		5.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+CATMODE			MEF
+CATFORMAT		PANSTARRS_DEV_0
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/dvo.layout
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/dvo.layout	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/dvo.layout	(revision 22322)
@@ -0,0 +1,58 @@
+# this file defines the layout of the mosaic imager:
+
+# NCCD 40  # this needs work: sometimes 36, sometimes 40
+NCCD 36
+NAXIS1 2112
+NAXIS2 4644
+
+MOSAIC_X 11
+MOSAIC_Y  4
+
+# lines need to contain:
+
+CHIPID_KEYWORD EXTNAME
+
+# ID     CHIPID xoffset yoffset xflip yflip datasec         biassec            Xo      Yo      theta
+CCD.0     ccd00       1       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.1     ccd01       2       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.2     ccd02       3       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.3     ccd03       4       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.4     ccd04       5       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.5     ccd05       6       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.6     ccd06       7       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.7     ccd07       8       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.8     ccd08       9       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.9     ccd09       1       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.10    ccd10       2       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.11    ccd11       3       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.12    ccd12       4       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.13    ccd13       5       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.14    ccd14       6       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.15    ccd15       7       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.16    ccd16       8       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.17    ccd17       9       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.18    ccd18       1       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.19    ccd19       2       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.20    ccd20       3       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.21    ccd21       4       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.22    ccd22       5       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.23    ccd23       6       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.24    ccd24       7       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.25    ccd25       8       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.26    ccd26       9       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.27    ccd27       1       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.28    ccd28       2       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.29    ccd29       3       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.30    ccd30       4       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.31    ccd31       5       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.32    ccd32       6       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.33    ccd33       7       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.34    ccd34       8       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.35    ccd35       9       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.36    ccd36       0       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.37    ccd37      10       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.38    ccd38       0       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.39    ccd39      10       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+
+# can we use header to find biassec
+USE_BIASSEC 1
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/format.config	(revision 22322)
@@ -0,0 +1,101 @@
+# Pan-STARRS Imaging Sky Probe
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+	NAXIS		S32	2
+	TELESCOP	STR	ISP-1 
+	INSTRUME	STR	ISP-Apogee
+	DETECTOR	STR	ISP-Apogee-01
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS	STR	SEQID	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.DETECTOR	STR	DETECTOR
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+	FPA.FILTERID	STR	FILTER
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+#	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.TEMP	STR	CCDTEMP
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.GAIN	STR	GAIN
+	CELL.READNOISE	STR	RDNOISE
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+#	CELL.TIMESYS	STR	TIMESYS
+	CELL.XBIN	STR	CCDBIN1
+	CELL.YBIN	STR	CCDBIN2
+# these were used for some early data
+#	CELL.XBIN	STR	XBIN
+#	CELL.YBIN	STR	YBIN
+	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	CHIP.XSIZE	S32	2048
+	CHIP.YSIZE	S32	2048
+	CELL.XSIZE	S32	2048
+	CELL.YSIZE	S32	2048
+	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	60000
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+#	CELL.XBIN	S32	1
+#	CELL.YBIN	S32	1
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	DEGREES
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/ppImage.config	(revision 22322)
@@ -0,0 +1,118 @@
+### ppImage recipe configuration file
+
+# Inherit everything from the top-level recipe file, except for what's below.
+
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS	STR z
+FRINGE.FILTERS	STR y
+
+# apply the following constraints when selecting a detrend image
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+  END
+  SHUTTER METADATA
+  END	
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+#   VERSION STR RAW
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTERID
+#   AIRMASS  STR FPA.AIRMASS
+#   TWILIGHT STR TWILIGHT:FPA.TIME
+  END
+END
+
+## these science-image analysis recipes are placed here set MASK = FALSE
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_DET_ONLY  METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Overscan, bias, photometry, astrometry
+PPIMAGE_OA   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL	  TRUE 		# Save 1st binned jpeg?
+  BIN2.JPEG       BOOL	  TRUE 		# Save 2nd binned jpeg?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/ppMerge.config	(revision 22322)
@@ -0,0 +1,3 @@
+### ppImage recipe configuration file
+
+# Inherit everything from the top-level recipe file, except for what's below.
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/psastro.config	(revision 22322)
@@ -0,0 +1,38 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+PSASTRO.PIXEL.SCALE    F32  10.0
+
+# pmAstromGridMatch:
+PSASTRO.GRID.MIN.ANGLE F32 -2.0
+PSASTRO.GRID.MAX.ANGLE F32 +2.0
+PSASTRO.GRID.DEL.ANGLE F32  0.25
+PSASTRO.GRID.MIN.SIGMA F32  5.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32   10000.
+PSASTRO.GRID.SCALE     F32     500
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# match radius in pixels for CHIP astrometry
+PSASTRO.MATCH.RADIUS   F32    8
+
+# pmAstromMatchFit
+PSASTRO.CHIP.ORDER     S32      3  # fit order
+PSASTRO.CHIP.NITER     S32      3  # fit clipping iterations
+PSASTRO.CHIP.NSIGMA    F32      3  # fit clipping sigmas
+
+PSASTRO.MAX.ERROR      F32      10.0 # max error in pixels
+PSASTRO.MIN.NSTAR      S32      10   # min fitted stars in solution
+
+MAG_MAX                F32      10
+PSASTRO.CATDIR         STR      SYNTH.BRIGHT
+PSASTRO.MATCH.LUMFUNC  BOOL     TRUE
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/psphot.config	(revision 22322)
@@ -0,0 +1,76 @@
+
+# turn these on to see specific outputs
+SAVE.BACKMDL	BOOL 	TRUE
+SAVE.BACKGND	BOOL 	TRUE
+SAVE.BACKSUB	BOOL 	TRUE
+SAVE.RESID	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+SAVE.PLOTS      BOOL    TRUE
+
+# image statistics parameters
+IMSTATS_NPIX        S32  5000    	 # number of pixels to use for sky estimate boxes:
+SKY_STAT            STR  FITTED_MEAN_V4  # statistic used to measure background
+
+PSF_SN_LIM          F32  30              # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+PSF_MODEL           STR  PS_MODEL_PGAUSS
+
+MOMENTS_SN_MIN      F32   30.0
+MOMENTS_AR_MAX      F32   2.0		 # maximum axial ratio: 1 / AR < (sx / sy) < AR
+
+#EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+#FULL_FIT_SN_LIM      F32  50.0
+#AP_MIN_SN            F32  20.0
+PSF_CLUMP_NSIGMA   F32  3.5             # region of Sx,Sy plane to use for selecting PSF stars
+
+# PSFTREND must be a 2D polynomial
+# the specified values are ignored but define the active components of the polynomial
+PSF.TREND.MASK  METADATA  
+   NORDER_X         S32       3                # number of x orders
+   NORDER_Y         S32       3                # number of y orders
+   VAL_X00_Y00      F64       1                # polynomial coefficient
+
+   VAL_X01_Y00      F64       1                # polynomial coefficient
+   VAL_X00_Y01      F64       1                # polynomial coefficient
+
+   VAL_X02_Y00      F64       1                # polynomial coefficient
+   VAL_X01_Y01      F64       1                # polynomial coefficient
+   VAL_X00_Y02      F64       1                # polynomial coefficient
+
+   VAL_X03_Y00      F64       1                # polynomial coefficient
+   VAL_X02_Y01      F64       1                # polynomial coefficient
+   VAL_X01_Y02      F64       1                # polynomial coefficient
+   VAL_X00_Y03      F64       1                # polynomial coefficient
+   NELEMENTS        S32       10               # number of unmasked components
+END  # folder for 4D polynomial
+
+PSF.TREND.NX S32 4
+PSF.TREND.NY S32 4
+
+XMIN F32 15
+
+PSF.RESIDUALS       BOOL true
+PSF.RESIDUALS.SPATIAL_ORDER S32 1
+
+BREAK_POINT         STR  ENSEMBLE
+OUTPUT.FORMAT       STR  PS1_DEV_0
+
+PSPHOT.SUMMIT METADATA
+ PEAKS_SMOOTH_SIGMA  F32  0.8 	   	 # peak significance threshold
+ PEAKS_NSIGMA_LIMIT  F32  50.0 	   	 # peak significance threshold
+ PEAKS_NMAX          S32  1000
+ SAVE.RESID	BOOL 	FALSE
+ PSF_SN_LIM          F32  25              # minimum S/N for stars used for PSF model
+ MOMENTS_SN_MIN      F32   25.0
+ PSF_MODEL         STR  PS_MODEL_PGAUSS
+END
+
+DIAGNOSTIC.PLOTS		METADATA
+  IMAGE.BACKGROUND.CELL.HISTOGRAM   BOOL FALSE
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.X S32  4
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.Y S32  7
+END
+
+SKY_BIAS F32 0.5
+AP_MIN_SN            F32  30.0
Index: /tags/sj_tags/sj_root_20080929/ippconfig/isp/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/isp/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/isp/rejections.config	(revision 22322)
@@ -0,0 +1,49 @@
+
+DARK 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  2.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  2.0
+  ENSEMBLE.MEAN      F32  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+FRINGE 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 10.0
+  EXP.FLUX           F32  0.0
+  ENSEMBLE.MEAN      F32  0.0
+  ENSEMBLE.STDEV     F32  0.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/Makefile.am	(revision 22322)
@@ -0,0 +1,23 @@
+
+installdir = $(datadir)/ippconfig/lbc_red
+
+install_files = \
+	dvo.config \
+	dvo.layout \
+	camera.config \
+	format.config \
+	ppImage.config \
+	ppMerge.config \
+	psastro.config \
+	psphot.config \
+	filerules.mdc \
+	rejections.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/camera.config	(revision 22322)
@@ -0,0 +1,79 @@
+# Camera configuration file for LBCred: describes the camera
+
+# File formats that we know about
+FORMATS         METADATA
+        MEF 	STR     lbc_red/format.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+	CHIP1	STR	Cell
+	CHIP2	STR	Cell
+	CHIP3	STR	Cell
+	CHIP4	STR	Cell
+END
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+   Y	  STR	Y-FAN
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID         METADATA
+        FPA     STR     fpa
+        CHIP    STR     {CHIP.NAME}
+        CELL    STR     {CHIP.NAME}:{CELL.NAME}
+END
+
+DVO.CAMERADIR	STR	isp		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  BinoBias STR BIAS
+  bias     STR BIAS
+  zero     STR BIAS
+  dark     STR DARK
+  flat     STR SKYFLAT
+  SkyFlat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES         METADATA
+        # Other recipes
+        PSPHOT          STR     lbc_red/psphot.config           # psphot details
+        PSASTRO         STR     lbc_red/psastro.config          # psastro details
+        PPIMAGE         STR     lbc_red/ppImage.config          # ppImage recipe
+	REJECTIONS      STR     lbc_red/rejections.config       # rejection recipe
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES	STR	lbc_red/filerules.mdc	# File rules
+
+EXTNAME.RULES METADATA
+  CMF.HEAD   STR {CHIP.NAME}.hdr
+  CMF.DATA   STR {CHIP.NAME}.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD   STR {CHIP.NAME}.hdr
+  PSF.TABLE  STR {CHIP.NAME}.psf_model
+  PSF.RESID  STR {CHIP.NAME}.psf_resid
+
+  REF.ASTROM STR {CHIP.NAME}.ref_astrom
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBS		STR	EXPNUM
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.N}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/dvo.config	(revision 22322)
@@ -0,0 +1,50 @@
+
+# location of DVO database tables
+CATDIR			/data/ipp001.0/jester/stripe82/coadd/catdir
+
+# keywords used by DVO to interpret the headers
+
+# used by parse_time to find time-related keywords
+MJD-KEYWORD		NONE
+DATE-KEYWORD		DATE-OBS
+DATE-MODE		YYYY-MM-DD
+UT-KEYWORD		TAIHMS
+JD-KEYWORD		NONE
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		-7.0
+CAL_INSTMAG_MIN		-9.5
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# allowed astrometry error (arcseconds)
+ADDSTAR_MAX_CERROR	5.0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		5.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+CATMODE			MEF
+CATFORMAT		PANSTARRS_DEV_0
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/dvo.layout
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/dvo.layout	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/dvo.layout	(revision 22322)
@@ -0,0 +1,58 @@
+# this file defines the layout of the mosaic imager:
+
+# NCCD 40  # this needs work: sometimes 36, sometimes 40
+NCCD 36
+NAXIS1 2112
+NAXIS2 4644
+
+MOSAIC_X 11
+MOSAIC_Y  4
+
+# lines need to contain:
+
+CHIPID_KEYWORD EXTNAME
+
+# ID     CHIPID xoffset yoffset xflip yflip datasec         biassec            Xo      Yo      theta
+CCD.0     ccd00       1       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.1     ccd01       2       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.2     ccd02       3       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.3     ccd03       4       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.4     ccd04       5       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.5     ccd05       6       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.6     ccd06       7       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.7     ccd07       8       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.8     ccd08       9       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.9     ccd09       1       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.10    ccd10       2       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.11    ccd11       3       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.12    ccd12       4       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.13    ccd13       5       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.14    ccd14       6       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.15    ccd15       7       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.16    ccd16       8       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.17    ccd17       9       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.18    ccd18       1       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.19    ccd19       2       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.20    ccd20       3       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.21    ccd21       4       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.22    ccd22       5       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.23    ccd23       6       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.24    ccd24       7       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.25    ccd25       8       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.26    ccd26       9       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.27    ccd27       1       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.28    ccd28       2       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.29    ccd29       3       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.30    ccd30       4       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.31    ccd31       5       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.32    ccd32       6       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.33    ccd33       7       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.34    ccd34       8       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.35    ccd35       9       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.36    ccd36       0       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.37    ccd37      10       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.38    ccd38       0       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.39    ccd39      10       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+
+# can we use header to find biassec
+USE_BIASSEC 1
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/filerules.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/filerules.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/filerules.mdc	(revision 22322)
@@ -0,0 +1,169 @@
+### File rules for PHU=FPA, EXT=CHIP
+
+### Redirections
+PPIMAGE.OUTPUT        STR PPIMAGE.OUTPUT.MEF
+PPIMAGE.OUTPUT.MASK   STR PPIMAGE.OUT.MK.MEF
+PPIMAGE.OUTPUT.WEIGHT STR PPIMAGE.OUT.WT.MEF
+PPIMAGE.CHIP          STR PPIMAGE.CHIP.MEF
+PPIMAGE.CHIP.MASK     STR PPIMAGE.CHIP.MK.MEF
+PPIMAGE.CHIP.WEIGHT   STR PPIMAGE.CHIP.WT.MEF
+PPIMAGE.OUTPUT.FPA1   STR PPIMAGE.OUTPUT.FPA1.MEF
+PPIMAGE.OUTPUT.FPA2   STR PPIMAGE.OUTPUT.FPA2.MEF
+PPIMAGE.BIN1          STR PPIMAGE.BIN1.MEF
+PPIMAGE.BIN2          STR PPIMAGE.BIN2.MEF
+
+PPSTAMP.CHIP          STR PPSTAMP.CHIP.MEF
+
+PSASTRO.INPUT         STR PSASTRO.INPUT.CMF
+PSASTRO.OUTPUT        STR PSASTRO.OUT.CMF.MEF
+PSASTRO.OUTPUT.MEF    STR PSASTRO.OUT.CMF.MEF
+PSPHOT.OUTPUT         STR PSPHOT.OUT.CMF.MEF
+DVOCORR.OUTPUT        STR DVOCORR.MEF.OUTPUT
+DVOFLAT.OUTPUT        STR DVOFLAT.MEF.OUTPUT
+
+### input file definitions
+### use @DETDB entries to get the detrend images from the database
+### replace @DETDB with @FILES if you want to require it from the 
+### command line, or with an explicit name to require a specific file
+TYPE               INPUT FILENAME.RULE DATA.LEVEL FILE.TYPE
+
+## files used by ppImage
+PPIMAGE.INPUT      INPUT @FILES        CHIP       IMAGE
+PPIMAGE.MASK       INPUT @DETDB        CHIP       MASK
+PPIMAGE.BIAS       INPUT @DETDB        CHIP       IMAGE
+PPIMAGE.DARK       INPUT @DETDB        CHIP       IMAGE
+PPIMAGE.FLAT       INPUT @DETDB        CHIP       IMAGE
+PPIMAGE.FRINGE     INPUT @DETDB        CHIP       FRINGE
+PPIMAGE.SHUTTER    INPUT @DETDB        CHIP       IMAGE     
+
+## files used to build and apply the flat-field correction images
+DVOCORR.INPUT      INPUT @FILES        CHIP       IMAGE
+DVOCORR.REFHEAD    INPUT @FILES        CHIP       HEADER
+DVOFLAT.INPUT      INPUT @FILES        CHIP       IMAGE
+DVOFLAT.CORR       INPUT @DETDB        CHIP       IMAGE
+
+## files used by psphot 
+PSPHOT.LOAD        INPUT @FILES        CHIP       IMAGE
+PSPHOT.INPUT       INPUT @FILES        CHIP       IMAGE
+PSPHOT.MASK        INPUT @FILES        CHIP       MASK     
+PSPHOT.WEIGHT      INPUT @FILES        CHIP       WEIGHT     
+PSPHOT.PSF.LOAD    INPUT @FILES        CHIP       PSF       
+
+## files used by psastro 
+PSASTRO.INPUT.CMP  INPUT @FILES        CHIP       CMP
+PSASTRO.INPUT.CMF  INPUT @FILES        CHIP       CMF
+
+## files used by pswarp
+PSWARP.INPUT       INPUT @FILES        CHIP       IMAGE
+PSWARP.WEIGHT      INPUT @FILES        CHIP       WEIGHT
+PSWARP.MASK        INPUT @FILES        CHIP       MASK
+PSWARP.SKYCELL     INPUT @FILES        CHIP       IMAGE
+PSWARP.ASTROM      INPUT @FILES        CHIP       CMF
+
+PPSUB.INPUT        INPUT @FILES        FPA        IMAGE
+PPSUB.INPUT.MASK   INPUT @FILES        FPA        MASK
+PPSUB.INPUT.WEIGHT INPUT @FILES        FPA        WEIGHT
+PPSUB.REF          INPUT @FILES        FPA        IMAGE
+PPSUB.REF.MASK     INPUT @FILES        FPA        MASK
+PPSUB.REF.WEIGHT   INPUT @FILES        FPA        WEIGHT
+PPSUB.SOURCES      INPUT @FILES        FPA        CMF
+
+PPSTACK.INPUT      INPUT @FILES        FPA        IMAGE
+PPSTACK.INPUT.MASK INPUT @FILES        FPA        MASK
+PPSTACK.INPUT.WEIGHT INPUT @FILES      FPA        WEIGHT
+PPSTACK.SOURCES    INPUT @FILES        FPA        CMF
+
+PPSTAMP.INPUT      INPUT @FILES        CHIP       IMAGE
+
+PPARITH.INPUT.IMAGE INPUT @FILES        CHIP       IMAGE
+PPARITH.INPUT.MASK  INPUT @FILES        CHIP       MASK
+
+### output file definitions
+TYPE                  OUTPUT FILENAME.RULE                    FILE.TYPE FITS.TYPE DATA.LEVEL FILE.SAVE FILE.FORMAT
+PPIMAGE.OUTPUT.MEF    OUTPUT {OUTPUT}.b0.fits                 IMAGE     NONE      CHIP       TRUE      MEF
+PPIMAGE.OUT.MK.MEF    OUTPUT {OUTPUT}.mk.fits                 MASK      NONE      CHIP       TRUE      MEF
+PPIMAGE.OUT.WT.MEF    OUTPUT {OUTPUT}.wt.fits                 WEIGHT    NONE      CHIP       TRUE      MEF
+PPIMAGE.OUTPUT.SPL    OUTPUT {OUTPUT}.{CHIP.NAME}.b0.fits     IMAGE     NONE      CHIP       TRUE      SPLIT
+PPIMAGE.OUT.MK.SPL    OUTPUT {OUTPUT}.{CHIP.NAME}.mk.fits     MASK      NONE      CHIP       TRUE      SPLIT
+PPIMAGE.OUT.WT.SPL    OUTPUT {OUTPUT}.{CHIP.NAME}.wt.fits     WEIGHT    NONE      CHIP       TRUE      SPLIT
+PPIMAGE.CONFIG        OUTPUT {OUTPUT}.{CHIP.NAME}.ppImage.mdc TEXT      NONE      CHIP       TRUE      NONE
+
+PPIMAGE.CHIP.MEF      OUTPUT {OUTPUT}.ch.fits                 IMAGE     NONE      CHIP       TRUE      MEF
+PPIMAGE.CHIP.MK.MEF   OUTPUT {OUTPUT}.ch.mk.fits              MASK      NONE      CHIP       TRUE      MEF
+PPIMAGE.CHIP.WT.MEF   OUTPUT {OUTPUT}.ch.wt.fits              WEIGHT    NONE      CHIP       TRUE      MEF
+PPIMAGE.CHIP.SPL      OUTPUT {OUTPUT}.{CHIP.NAME}.ch.fits     IMAGE     NONE      CHIP       TRUE      SPLIT
+PPIMAGE.CHIP.MK.SPL   OUTPUT {OUTPUT}.{CHIP.NAME}.ch.mk.fits  MASK      NONE      CHIP       TRUE      SPLIT
+PPIMAGE.CHIP.WT.SPL   OUTPUT {OUTPUT}.{CHIP.NAME}.ch.wt.fits  WEIGHT    NONE      CHIP       TRUE      SPLIT
+
+PPIMAGE.OUTPUT.FPA1.MEF OUTPUT {OUTPUT}.b1.fits               IMAGE     NONE      FPA        TRUE      MEF
+PPIMAGE.OUTPUT.FPA2.MEF OUTPUT {OUTPUT}.b2.fits               IMAGE     NONE      FPA        TRUE      MEF
+PPIMAGE.OUTPUT.FPA1.SPL OUTPUT {OUTPUT}.{CHIP.NAME}.b1.fits   IMAGE     NONE      FPA        TRUE      SPLIT
+PPIMAGE.OUTPUT.FPA2.SPL OUTPUT {OUTPUT}.{CHIP.NAME}.b2.fits   IMAGE     NONE      FPA        TRUE      SPLIT
+
+PPIMAGE.STATS         OUTPUT {OUTPUT}.stats                   STATS     NONE      FPA        TRUE      MEF
+
+PPIMAGE.BIN1.MEF      OUTPUT {OUTPUT}.b1c.fits                IMAGE     NONE      CHIP       TRUE      MEF
+PPIMAGE.BIN2.MEF      OUTPUT {OUTPUT}.b2c.fits                IMAGE     NONE      CHIP       TRUE      MEF
+PPIMAGE.BIN1.SPL      OUTPUT {OUTPUT}.{CHIP.NAME}.b1.fits     IMAGE     NONE      CHIP       TRUE      SPLIT
+PPIMAGE.BIN2.SPL      OUTPUT {OUTPUT}.{CHIP.NAME}.b2.fits     IMAGE     NONE      CHIP       TRUE      SPLIT
+
+PPIMAGE.JPEG1         OUTPUT {OUTPUT}.b1.jpg                  JPEG      NONE      FPA        TRUE      NONE
+PPIMAGE.JPEG2         OUTPUT {OUTPUT}.b2.jpg                  JPEG      NONE      FPA        TRUE      NONE
+
+DVOCORR.MEF.OUTPUT    OUTPUT {OUTPUT}.fc.fits                 IMAGE     NONE      CHIP       TRUE      MEF
+DVOCORR.SPL.OUTPUT    OUTPUT {OUTPUT}.{CHIP.NAME}.fc.fits     IMAGE     NONE      CHIP       TRUE      SPLIT
+DVOFLAT.MEF.OUTPUT    OUTPUT {OUTPUT}.co.fits                 IMAGE     NONE      CHIP       TRUE      MEF
+DVOFLAT.SPL.OUTPUT    OUTPUT {OUTPUT}.{CHIP.NAME}.co.fits     IMAGE     NONE      CHIP       TRUE      SPLIT
+
+PSPHOT.RESID          OUTPUT {OUTPUT}.res.fits                IMAGE     NONE      CHIP       FALSE     NONE
+PSPHOT.BACKGND        OUTPUT {OUTPUT}.bck.fits                IMAGE     NONE      CHIP       FALSE     NONE
+PSPHOT.BACKSUB        OUTPUT {OUTPUT}.sub.fits                IMAGE     NONE      CHIP       FALSE     NONE
+PSPHOT.BACKMDL        OUTPUT {OUTPUT}.mdl.fits                IMAGE     NONE      CHIP       FALSE     NONE
+
+PSPHOT.OUTPUT.RAW     OUTPUT {OUTPUT}.{CHIP.NAME}             RAW       NONE      CHIP       TRUE      NONE
+PSPHOT.OUTPUT.SX      OUTPUT {OUTPUT}.sx                      SX        NONE      FPA        TRUE      NONE
+PSPHOT.OUTPUT.OBJ     OUTPUT {OUTPUT}.obj                     OBJ       NONE      FPA        TRUE      NONE
+PSPHOT.OUTPUT.CMP     OUTPUT {OUTPUT}.{CHIP.NAME}.cmp         CMP       NONE      CHIP       TRUE      NONE
+PSPHOT.OUT.CMF.SPL    OUTPUT {OUTPUT}.{CHIP.NAME}.cmf         CMF       NONE      CHIP       TRUE      SPLIT
+PSPHOT.OUT.CMF.MEF    OUTPUT {OUTPUT}.cmf                     CMF       NONE      CHIP       TRUE      NONE
+
+PSPHOT.PSF.SAVE       OUTPUT {OUTPUT}.psf                     PSF       NONE      CHIP       TRUE      NONE
+
+SOURCE.PLOT.MOMENTS   OUTPUT {OUTPUT}.mnt.png                 KAPA      NONE      FPA        TRUE      NONE
+SOURCE.PLOT.PSFMODEL  OUTPUT {OUTPUT}.psf.png                 KAPA      NONE      FPA        TRUE      NONE
+SOURCE.PLOT.APRESID   OUTPUT {OUTPUT}.dap.png                 KAPA      NONE      FPA        TRUE      NONE
+
+PSASTRO.OUTPUT.CMP    OUTPUT {OUTPUT}.{CHIP.NAME}.smp         CMP       NONE      CHIP       TRUE      NONE
+PSASTRO.OUT.CMF.SPL   OUTPUT {OUTPUT}.{CHIP.NAME}.smf         CMF       NONE      CHIP       TRUE      NONE
+PSASTRO.OUT.CMF.MEF   OUTPUT {OUTPUT}.smf                     CMF       NONE      FPA        TRUE      NONE
+PSASTRO.OUT.ASTROM    OUTPUT {OUTPUT}.ast                     ASTROM    NONE      FPA        TRUE      NONE
+
+PSWARP.OUTPUT         OUTPUT {OUTPUT}.fits                    IMAGE     NONE      FPA        TRUE      NONE
+PSWARP.OUTPUT.MASK    OUTPUT {OUTPUT}.mk.fits                 MASK      NONE      FPA        TRUE      NONE
+PSWARP.OUTPUT.WEIGHT  OUTPUT {OUTPUT}.wt.fits                 WEIGHT    NONE      FPA        TRUE      NONE
+PSWARP.OUTPUT.SOURCES OUTPUT {OUTPUT}.cmf                     CMF       NONE      FPA        TRUE      NONE
+PSWARP.BIN1           OUTPUT {OUTPUT}.b1.fits                 IMAGE     NONE      FPA        TRUE      NONE
+PSWARP.BIN2           OUTPUT {OUTPUT}.b2.fits                 IMAGE     NONE      FPA        TRUE      NONE
+PSWARP.CONFIG         OUTPUT {OUTPUT}.pswarp.mdc              TEXT      NONE      FPA        TRUE      NONE
+
+PPSUB.OUTPUT          OUTPUT {OUTPUT}.fits                    IMAGE     NONE      FPA        TRUE      NONE
+PPSUB.OUTPUT.MASK     OUTPUT {OUTPUT}.mk.fits                 MASK      NONE      FPA        TRUE      NONE
+PPSUB.OUTPUT.WEIGHT   OUTPUT {OUTPUT}.wt.fits                 WEIGHT    NONE      FPA        TRUE      NONE
+PPSUB.CONFIG          OUTPUT {OUTPUT}.ppSub.mdc               TEXT      NONE      FPA        TRUE      NONE
+
+PPSTACK.OUTPUT        OUTPUT {OUTPUT}.fits                    IMAGE     NONE      FPA        TRUE      NONE
+PPSTACK.OUTPUT.MASK   OUTPUT {OUTPUT}.mk.fits                 MASK      NONE      FPA        TRUE      NONE
+PPSTACK.OUTPUT.WEIGHT OUTPUT {OUTPUT}.wt.fits                 WEIGHT    NONE      FPA        TRUE      NONE
+PPSTACK.CONFIG        OUTPUT {OUTPUT}.ppStack.mdc             TEXT      NONE      FPA        TRUE      NONE
+
+PPSTAMP.OUTPUT        OUTPUT {OUTPUT}.fits                    IMAGE     NONE      FPA        TRUE      NONE
+PPSTAMP.CHIP.MEF      OUTPUT {OUTPUT}.ch.fits                 IMAGE     NONE      CHIP       FALSE     MEF
+
+PPSIM.OUTPUT          OUTPUT {OUTPUT}.{CHIP.NAME}.fits        IMAGE     NONE      CHIP       TRUE      SPLIT
+
+PPARITH.OUTPUT.IMAGE  OUTPUT {OUTPUT}.fits                    IMAGE     NONE      CHIP       TRUE      NONE
+PPARITH.OUTPUT.MASK   OUTPUT {OUTPUT}.fits                    MASK      NONE      CHIP       TRUE      NONE
+
+LOG.IMFILE            OUTPUT {OUTPUT}.{CHIP.NAME}.log         TEXT      NONE      CHIP       TRUE      NONE
+LOG.EXP               OUTPUT {OUTPUT}.log                     TEXT      NONE      FPA        TRUE      NONE
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/format.config	(revision 22322)
@@ -0,0 +1,154 @@
+# LBCred format.config, copied from MegaCam format_mef.config
+# Like Megacam, LBC has an FPA in a multi-extension fits file, with one chip per extension
+
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	LBT-DX
+	INSTRUME	STR	LBC-RED
+	NAXIS           S32     0
+	EXTEND		BOOL	T
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CHIP	# The extensions represent chips
+	FPA.OBS	STR	OBS_ID	# A PHU keyword for unique identifier
+	CHIP.NAME	STR	EXTNAME	# An extension keyword for unique identifie
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chipName:chipType
+	LBCCHIP1	STR	CHIP1:Chip
+	LBCCHIP2	STR	CHIP2:Chip
+	LBCCHIP3	STR	CHIP3:Chip
+	LBCCHIP4	STR	CHIP4:Chip
+END
+
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	Chip	STR	Cell:Cell
+END
+
+
+# Specify the cells
+CELLS		METADATA
+	# Cell type, concepts to use
+	Cell	METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	TRIMSEC
+	# Not sure about these...
+		CELL.X0			S32	0
+		CELL.Y0			S32	0
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAIN
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTERID    STR     FILTER
+        FPA.FILTER      STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     OBSRA
+        FPA.DEC         STR     OBSDEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.OBSTYPE	STR	IMAGETYPE
+	FPA.OBJECT	STR	OBJECT
+	FPA.FOCUS	STR	Z4 # ???
+	FPA.TIME	STR	MJD_OBS
+#	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	TELALT
+	FPA.AZ		STR	TELAZ
+	FPA.TEMP	STR	CCDTEM
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     EXPTIME
+        CELL.READNOISE  STR     RDNOISE
+	CELL.TIME	STR	MJD_OBS
+#	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     LBCBIN
+        CELL.YBIN       STR     LBCBIN
+	CELL.SATURATION	STR	SATURATE
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TELESCOPE		STR	LBT-DX
+	FPA.INSTRUMENT		STR	LBC-RED		# this value is used in ipprc.config to find the recipes
+	FPA.DETECTOR	        STR	LBC-RED
+	CELL.TIMESYS		STR	UTC
+	FPA.TIMESYS		STR	UTC
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4608
+	CELL.XSIZE		S32	2048
+	CELL.YSIZE		S32	4608
+	CELL.XWINDOW		S32	0
+	CELL.YWINDOW		S32	0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.SATURATION		F32	65536.0
+        CELL.BAD                F32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	# Do the x0, y0 positions need to account for the gaps??? 
+	# How do I specify rotation?
+	# 
+	# LBC layout is
+	#
+	#   444444444
+	#   444444444
+	#   444444444
+	#
+	#  333 222 111
+	#  333 222 111
+	#  333 222 111
+	#  333 222 111
+	#  333 222 111
+	#  333 222 111
+	# 
+	CHIP.X0		METADATA
+		CHIP1	S32	4096
+		CHIP2	S32	2048
+		CHIP3	S32	0
+	# This is probably wrong!
+		CHIP4	S32	1024
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		CHIP1	S32	0
+		CHIP2	S32	0
+		CHIP3	S32	0
+		CHIP4	S32	4608
+	END
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+END
+
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/ppImage.config	(revision 22322)
@@ -0,0 +1,170 @@
+### ppImage recipe configuration file for Megacam
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+OVERSCAN.FIT		STR	NONE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	0		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# for a test, turn off selected detrend types
+OVERSCAN	BOOL	FALSE		# Overscan subtraction
+BIAS		BOOL	TRUE		# Bias subtraction
+DARK		BOOL	FALSE		# Dark subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+
+# binned output image options
+BIN1.XBIN               S32     18
+BIN1.YBIN               S32     18
+BIN2.XBIN               S32     144
+BIN2.YBIN               S32     144
+
+# LBCred  needs fringe subtraction (?)
+FRINGE		BOOL	TRUE		# Fringe subtraction
+
+# this table lists extra constraints which should be applied when
+# selecting the detrend images.
+# note: camera and time are always applied
+
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+#   EXPTIME STR FPA.EXPOSURE
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+#   DETTYPE STR DOMEFLAT.RAW
+#   VERSION STR SKYFLAT.RAW
+#   VERSION STR DOMEFLAT.CORR.XX
+#   VERSION STR SKYFLAT.CORR
+  END
+  FLAT_CORRECTION METADATA
+    FILTER   STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+#   AIRMASS  STR FPA.AIRMASS
+#   TWILIGHT STR FPA.TWILIGHT
+  END
+  SHUTTER METADATA
+  END
+  MASK METADATA
+  END	
+END
+
+# only apply the fringe for these filters
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS	STR i
+FRINGE.FILTERS	STR z
+FRINGE.FILTERS	STR y
+
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS       METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR     METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/ppMerge.config	(revision 22322)
@@ -0,0 +1,43 @@
+
+# Bias combination --- don't want min/max rejection
+PPMERGE_BIAS	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
+   WEIGHTS	BOOL	FALSE		# Use image weights?
+   COMBINE	STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Dark combination --- don't want min/max rejection
+# More aggressive clipping than bias, so as to remove CRs
+PPMERGE_DARK	METADATA
+  REJ		F32	2.0		# Rejection threshold (sigma)
+  ITER		S32	4		# 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
+  WEIGHTS	BOOL	FALSE		# Use image weights?
+  COMBINE	STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Flat combination --- use min/max rejection
+PPMERGE_FLAT	METADATA
+	REJ		F32	3.0		# Rejection threshold (sigma)
+	ITER		S32	1		# Number of rejection iterations
+	FRACHIGH	F32	0.3		# Fraction of high pixels to reject immediately
+	FRACLOW		F32	0.1		# Fraction of low pixels to reject immediately
+	NKEEP		S32	5		# Minimum number of pixels in stack to keep
+	WEIGHTS		BOOL	TRUE		# Use image weights?
+	COMBINE		STR	MEAN		# Statistic to use for combination: 
+END
+
+
+# Fringe combination --- already included in default, above
+PPMERGE_FRINGE	METADATA
+	FRACHIGH	F32	0.1		# Fraction of high pixels to reject immediately
+	WEIGHTS		BOOL	TRUE		# Use image weights?
+END
+
+# Mask generation --- already included in default, above
+PPMERGE_MASK	METADATA
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/psastro.config	(revision 22322)
@@ -0,0 +1,67 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+PSASTRO.PIXEL.SCALE    F32  1.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32    1000.
+PSASTRO.GRID.SCALE     F32      50.
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+PSASTRO.MATCH.RADIUS   F32     12.0
+
+PSASTRO.MAX.NRAW       S32      1500   # max stars accepted for fitting (0 for all)
+PSASTRO.MAX.NREF       S32      1500   # max stars accepted for fitting (0 for all)
+
+PSASTRO.MIN.INST.MAG.RAW       F32      -14.5  # min instrumental magnitude for stars accepted for fitting
+PSASTRO.MAX.INST.MAG.RAW       F32       -8.0  # max instrumental magnitude for stars accepted for fitting
+
+PSASTRO.MAX.ERROR      F32      10.0 # max error in pixels
+PSASTRO.MIN.NSTAR      S32      10   # min fitted stars in solution
+
+PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+PSASTRO.MOSAIC.RADIUS.N0    F32    12.0
+PSASTRO.MOSAIC.RADIUS.N1    F32    0.0 # do not refine the match
+PSASTRO.MOSAIC.RADIUS.N2    F32    0.0 # do not refine the match
+PSASTRO.MOSAIC.RADIUS.N3    F32    6.0 
+
+PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      1 # fit order (-1 means use default)
+
+PSASTRO.MOSAIC.GRADIENT.NX    S32      2   # number of x-dir cells per chip
+PSASTRO.MOSAIC.GRADIENT.NY    S32      4   # number of y-dir cells per chip
+
+# use this recipe to set a tight constraint
+PSASTRO.FINE METADATA
+  PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+  PSASTRO.MOSAIC.RADIUS.N0    F32    8.0
+  PSASTRO.MOSAIC.RADIUS.N1    F32    0.0
+  PSASTRO.MOSAIC.RADIUS.N2    F32    4.0
+  PSASTRO.MOSAIC.RADIUS.N3    F32    2.0
+
+  PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      3 # fit order (-1 means use default)
+END
+
+# XXX Test for taurus
+# PSASTRO.GRID.MIN.SIGMA F32  3.0
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/psphot.config	(revision 22322)
@@ -0,0 +1,42 @@
+
+# turn these on to see specific outputs
+SAVE.OUTPUT	BOOL 	TRUE
+SAVE.BACKMDL	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+SAVE.PLOTS     	BOOL    TRUE
+
+BACKGROUND.XBIN	    S32  128            # size of background superpixels
+BACKGROUND.YBIN	    S32  128            # size of background superpixels
+
+# image background parameters
+IMSTATS_NPIX        S32  10000    	 # number of pixels to use for sky estimate boxes:
+SKY_STAT            STR  FITTED_MEAN_V4  # statistic used to measure background
+SKY_CLIP_SIGMA      F32  2.0             # statistic used to measure background
+
+PSF_SN_LIM          F32  100             # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+# PSF model parameters : choose the PSF model desired
+PSF_MODEL           STR  PS_MODEL_QGAUSS
+
+MOMENTS_SN_MIN      F32   30.0
+EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+FULL_FIT_SN_LIM      F32  50.0
+AP_MIN_SN            F32  50.0
+
+# OUTPUT.FORMAT       STR SMPDATA
+OUTPUT.FORMAT       STR PS1_DEV_1
+
+PSF_SHAPE_NSIGMA     F32  3.0		 # max significance for shape variation
+PSF_MAX_CHI          F32  50.0		 # reject objects worse that this
+
+#PSF.TREND.MODE STR MAP
+#PSF.TREND.NX   S32 1
+#PSF.TREND.NY   S32 1
+
+DIAGNOSTIC.PLOTS		METADATA
+  IMAGE.BACKGROUND.CELL.HISTOGRAM   BOOL FALSE
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.X S32 -1
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.Y S32 12
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lbc_red/rejections.config	(revision 22322)
@@ -0,0 +1,38 @@
+
+BIAS METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0
+  IMFILE.STDEV       F32 10.0
+  IMFILE.MEANSTDEV   F32  0.5
+  IMFILE.SKEWNESS    F32  0.0
+  IMFILE.KURTOSIS    F32  0.0
+  IMFILE.SNR         F32  0.0
+  IMFILE.BIN.STDEV   F32  0.5
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  0.5
+  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  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+# FILTER is an additional qualifier, and may be "*" (or absent!), in which case it matches everything
+# EXPECTED is the expected mean value
+# IMFILE.MEAN is the maximum permitted mean value for an imfile, relative to the standard deviation
+# IMFILE.STDEV is the maximum permitted standard deviation for an imfile
+# EXP.MEAN is the maximum permitted mean value for an exposure, relative to the standard deviation
+# EXP.STDEV is the maximum permitted standard deviation for an exposure
+# EXP.MEANSTDEV is the maximum permitted mean standard deviation for an exposure relative to the mean
+# ENSEMBLE.MEAN is the maximum permitted mean for an ensemble of exposures
+# ENSEMBLE.STDEV is the maximum permitted standard deviation for an ensemble of exposures
+# ENSEMBLE.MEANSTDEV is the maximum permitted mean standard deviation for an ensemble of exposures
+# IMFILE.SNR is the minimum permitted signal-to-noise for an imfile
+# EXP.SNR is the minimum permitted signal-to-noise for an exposure
+# These values (all except FILTER) may be zero, in which case no clipping is applied.
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lris_blue/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lris_blue/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lris_blue/camera.config	(revision 22322)
@@ -0,0 +1,20 @@
+# Camera configuration file for LRIS Blue: describes the camera
+
+# File formats that we know about
+FORMATS         METADATA
+	RAW	STR	lris_blue/format_raw.config
+	MEF	STR	lris_blue/format_mef.config
+END
+                                                                                
+                                                                                
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        LeftChip	STR	LeftAmp RightAmp
+        RightChip	STR	LeftAmp RightAmp
+END
+
+
+# Recipe options
+RECIPES		METADATA
+#	PSPHOT		STR	psphot.config		# psphot details
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lris_blue/format_mef.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lris_blue/format_mef.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lris_blue/format_mef.config	(revision 22322)
@@ -0,0 +1,79 @@
+# The Low Resolution Imager and Spectrograph (LRIS) blue side
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	Keck I
+	INSTRUME	STR	LRISBLUE
+	FORMAT		STR	MEF
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CHIP	# There are no extensions
+	FPA.OBS		STR	OBSNUM	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	ccd1	STR	LeftChip:LeftAmp:left LeftChip:RightAmp:right
+	ccd2	STR	RightChip:LeftAmp:left RightChip:RightAmp:right
+END
+
+# Specify the cell data
+CELLS	METADATA
+	left		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECL1 BSECL2
+		CELL.TRIMSEC		STR	TSECL
+		CELL.GAIN		STR	GAINL
+		CELL.X0			S32	0
+	END
+
+	right	METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECR1 BSECR2
+		CELL.TRIMSEC		STR	TSECR
+		CELL.GAIN		STR	GAINR
+		CELL.X0			S32	1024
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS		STR	AIRMASS
+	FPA.FILTER		STR	BLUFILT
+	FPA.POSANGLE		STR	ROTPOSN
+	FPA.RA			STR	RA
+	FPA.DEC			STR	DEC
+	CELL.EXPOSURE		STR	EXPOSURE
+	CELL.DARKTIME		STR	EXPOSURE	# No special darktime header; use exposure time
+	CELL.TIME		STR	MJD-OBS
+	CELL.XBIN		STR	CCDSUM
+	CELL.YBIN		STR	CCDSUM
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.RADECSYS		STR	ICRS
+	CELL.TIMESYS		STR	UTC
+	CELL.READDIR		S32	1
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CELL.READNOISE		F32	2.5
+	CELL.SATURATION		F32	65536
+	CELL.BAD		F32	0
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	CELL.TIME	STR	MJD
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lris_blue/format_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lris_blue/format_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lris_blue/format_raw.config	(revision 22322)
@@ -0,0 +1,113 @@
+# The Low Resolution Imager and Spectrograph (LRIS) blue side
+
+# We have no choice but to hard-code the various regions, because Keck
+# only stores them as:
+# WINDOW  = '1,0,0,2048,4096'
+# PREPIX  =                   51
+# POSTPIX =                   80
+# BINNING = '1,1     '
+# AMPPSIZE= '[1:1024,1:4096]'
+
+# I don't know how we would get the IPP to react to changes in the
+# windowing on the fly --- we have no mechanism for setting the region
+# sizes on the basis of the above keywords.  Therefore, we hard-code
+# the regions and assert on our assumptions in the RULE.
+
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	Keck I
+	INSTRUME	STR	LRISBLUE
+	AMPLIST		STR	1,4,0,0
+	WINDOW		STR	1,0,0,2048,4096
+	PREPIX		S32	51
+	POSTPIX		S32	80
+	BINNING		STR	1,1
+	AMPPSIZE	STR	[1:1024,1:4096]
+	NAXIS1		S32	4620
+	NAXIS2		S32	4096
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	OBSNUM	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	LeftChip:LeftAmp:amp1 LeftChip:RightAmp:amp2 RightChip:LeftAmp:amp3 RightChip:RightAmp:amp4
+
+# Specify the cell data
+CELLS	METADATA
+	amp1		METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[1:51,1:4096];[4301:4380,1:4096]
+		CELL.TRIMSEC		STR	[205:1228,1:4096]
+		CELL.GAIN		F32	1.542
+		CELL.X0			S32	0
+	END
+
+	amp2	METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[52:102,1:4096];[4381:4460,1:4096]
+		CELL.TRIMSEC		STR	[1229:2252,1:4096]
+		CELL.GAIN		F32	1.546
+		CELL.X0			S32	1024
+	END
+
+	amp3		METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[103:153,1:4096];[4461:4540,1:4096]
+		CELL.TRIMSEC		STR	[2253:3276,1:4096]
+		CELL.GAIN		F32	1.632
+		CELL.X0			S32	0
+	END
+
+	amp4	METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[154:204,1:4096];[4541:4620,1:4096]
+		CELL.TRIMSEC		STR	[3277:4300,1:4096]
+		CELL.GAIN		F32	1.633
+		CELL.X0			S32	1024
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS		STR	AIRMASS
+	FPA.FILTER		STR	BLUFILT
+	FPA.POSANGLE		STR	ROTPOSN
+	FPA.RA			STR	RA
+	FPA.DEC			STR	DEC
+	CELL.EXPOSURE		STR	EXPOSURE
+	CELL.DARKTIME		STR	EXPOSURE	# No special darktime header; use exposure time
+	CELL.TIME		STR	MJD-OBS
+	CELL.XBIN		STR	CCDSUM
+	CELL.YBIN		STR	CCDSUM
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.RADECSYS		STR	ICRS
+	CELL.TIMESYS		STR	UTC
+	CELL.READDIR		S32	1
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CELL.READNOISE		F32	2.5
+	CELL.SATURATION		F32	65536
+	CELL.BAD		F32	0
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	CELL.TIME	STR	MJD
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lris_red/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lris_red/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lris_red/camera.config	(revision 22322)
@@ -0,0 +1,19 @@
+# Camera configuration file for LRIS-Red: describes the camera
+
+# File formats that we know about
+FORMATS         METADATA
+        RAW     STR     lris_red/format_raw.config
+	SPEC	STR	lris_red/format_spec.config
+END
+ 
+ 
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        RedChip		STR	LeftSide RightSide
+END
+
+
+# Recipes to use when processing this camera
+RECIPES		METADATA
+	PPMERGE		STR	ppMerge.config		# Recipe to use for ppMerge
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lris_red/format_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lris_red/format_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lris_red/format_raw.config	(revision 22322)
@@ -0,0 +1,95 @@
+# The Low Resolution Imager and Spectrograph (LRIS) red side
+
+# We have no choice but to hard-code the various regions, because Keck
+# only stores them as:
+# WINDOW  = '0,0,0,2048,2048'
+# PREPIX  =                   20
+# POSTPIX =                   80
+# BINNING = '1,1     '
+# AMPPSIZE= '[1:1024,1:4096]'
+
+# I don't know how we would get the IPP to react to changes in the
+# windowing on the fly --- we have no mechanism for setting the region
+# sizes on the basis of the above keywords.  Therefore, we hard-code
+# the regions and assert on our assumptions in the RULE.
+
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	Keck I
+	INSTRUME	STR	LRIS
+	AMPLIST		STR	2,1,0,0
+	WINDOW		STR	0,0,0,2048,2048
+	PREPIX		S32	20
+	POSTPIX		S32	80
+	BINNING		STR	1, 1
+	CCDPSIZE	STR	[1:2048,1:2048]
+	NAXIS1		S32	2248
+	NAXIS2		S32	2048
+	IMTYPE		STR	TWOAMPTOP
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents a single chip
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	OBSNUM	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	RedChip:LeftSide:left RedChip:RightSide:right
+
+# Specify the cell data
+CELLS	METADATA
+	left	METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[1:20,1:2048];[2089:2168,1:2048]
+		CELL.TRIMSEC		STR	[41:1064,1:2048]
+		CELL.GAIN		F32	1.98
+		CELL.READNOISE		F32	6.1
+		CELL.X0			S32	0
+	END
+
+	right	METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[21:40,1:2048];[2169:2248,1:2048]
+		CELL.TRIMSEC		STR	[1065:2088,1:2048]
+		CELL.GAIN		F32	2.17
+		CELL.READNOISE		F32	6.3
+		CELL.X0			S32	1023
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	POSANG
+	FPA.RA		STR	OBJ-RA
+	FPA.DEC		STR	OBJ-DEC
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.RADECSYS	STR	ICRS
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.SATURATION	F32	65535
+	CELL.BAD	F32	0
+	CELL.XBIN	S32	1
+	CELL.YBIN	S32	1
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+END
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+        FPA.RA          STR     HOURS
+        FPA.DEC         STR     DEGREES
+        CELL.TIME       STR     MJD
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lris_red/format_spec.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lris_red/format_spec.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lris_red/format_spec.config	(revision 22322)
@@ -0,0 +1,96 @@
+# The Low Resolution Imager and Spectrograph (LRIS) red side
+
+# We have no choice but to hard-code the various regions, because Keck
+# only stores them as:
+# WINDOW  = '0,0,550,2048,1000'
+# PREPIX  =                   20
+# POSTPIX =                   80
+# BINNING = '1,1     '
+# AMPPSIZE= '[1:1024,1:4096]'
+
+# I don't know how we would get the IPP to react to changes in the
+# windowing on the fly --- we have no mechanism for setting the region
+# sizes on the basis of the above keywords.  Therefore, we hard-code
+# the regions and assert on our assumptions in the RULE.
+
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	Keck I
+	INSTRUME	STR	LRIS
+	AMPLIST		STR	2,1,0,0
+	WINDOW		STR	0,0,550,2048,1000	# Narrow spectroscopy format
+	PREPIX		S32	20
+	POSTPIX		S32	80
+	BINNING		STR	1, 1
+	CCDPSIZE	STR	[1:2048,1:2048]
+	NAXIS1		S32	2248
+	NAXIS2		S32	1000
+	IMTYPE		STR	TWOAMPTOP
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents a single chip
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	OBSNUM	# A PHU keyword for unique identifier within the hierarchy level
+	CHIP.NAME	STR	INSTRUME	# An extension keyword for unique identifier
+END
+
+# What's in the FITS file?
+CONTENTS	STR	RedChip:LeftSide:left RedChip:RightSide:right
+
+# Specify the cell data
+CELLS	METADATA
+	left	METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[1:20,1:1000];[2089:2168,1:1000]
+		CELL.TRIMSEC		STR	[41:1064,1:1000]
+		CELL.GAIN		F32	1.98
+		CELL.READNOISE		F32	6.1
+		CELL.X0			S32	0
+	END
+
+	right	METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[21:40,1:1000];[2169:2248,1:1000]
+		CELL.TRIMSEC		STR	[1065:2088,1:1000]
+		CELL.GAIN		F32	2.17
+		CELL.READNOISE		F32	6.3
+		CELL.X0			S32	1023
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	POSANG
+	FPA.RA		STR	OBJ-RA
+	FPA.DEC		STR	OBJ-DEC
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.RADECSYS	STR	ICRS
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.SATURATION	F32	65535
+	CELL.BAD	F32	0
+	CELL.XBIN	S32	1
+	CELL.YBIN	S32	1
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+END
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+        FPA.RA          STR     HOURS
+        FPA.DEC         STR     DEGREES
+        CELL.TIME       STR     MJD
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/Makefile.am	(revision 22322)
@@ -0,0 +1,22 @@
+
+installdir = $(datadir)/ippconfig/lulin
+
+install_files = \
+	dvo.config \
+	camera.config \
+	format.config \
+	ppImage.config \
+	ppMerge.config \
+	psastro.config \
+	psphot.config \
+	pswarp.config \
+	rejections.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/camera.config	(revision 22322)
@@ -0,0 +1,80 @@
+# Camera configuration file for Lulin 1m Optical Telescope
+
+# File formats that we know about
+FORMATS         METADATA
+        SIMPLE	STR     lulin/format.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        Chip	STR	Cell
+END
+
+# Lookup table for filter name to filter abstract name
+FILTER.ID       METADATA 
+	UNKNOWN	STR	UNKNOWN
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID         METADATA
+        FPA     STR     fpa
+        CHIP    STR     {CHIP.NAME}
+        CELL    STR     {CHIP.NAME}:{CELL.NAME}
+        fpa     STR     fpa
+        chip    STR     {CHIP.NAME}
+        cell    STR     {CHIP.NAME}:{CELL.NAME}
+END
+
+DVO.CAMERADIR	STR	lulin		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias     STR BIAS
+  zero     STR BIAS
+  dark     STR DARK
+  flat     STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR LIGHT
+  science  STR LIGHT
+END
+
+# Recipe options
+RECIPES         METADATA
+        # Other recipes
+        PSPHOT          STR     lulin/psphot.config           # psphot details
+        PSASTRO         STR     lulin/psastro.config          # psastro details
+	PSWARP		STR	lulin/pswarp.config		# pswarp details
+        PPIMAGE         STR     lulin/ppImage.config          # ppImage recipe
+	REJECTIONS      STR     lulin/rejections.config       # rejection recipe
+	PPMERGE		STR	lulin/ppMerge.config		# ppMerge recipe
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES	STR	recipes/filerules-simple.mdc		# File rules appropriate for MEF format
+
+EXTNAME.RULES METADATA
+  CMF.HEAD   STR {CHIP.NAME}.hdr
+  CMF.DATA   STR {CHIP.NAME}.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD   STR {CHIP.NAME}.hdr
+  PSF.TABLE  STR {CHIP.NAME}.psf_model
+  PSF.RESID  STR {CHIP.NAME}.psf_resid
+
+  REF.ASTROM STR {CHIP.NAME}.ref_astrom
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBS		STR	EXPNUM
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.N}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/dvo.config	(revision 22322)
@@ -0,0 +1,67 @@
+
+# location of DVO database tables
+CATDIR			/data/alala.0/eugene/isp/catdir
+
+# keywords used by DVO to interpret the headers
+
+# keyword abstractions:
+#DATE-KEYWORD		DATE-OBS
+#DATE-MODE		yyyy-mm-dd
+#UT-KEYWORD		UTC-OBS
+JD-KEYWORD		NONE
+MJD-KEYWORD		MJD-OBS
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		0
+CAL_INSTMAG_MIN		0
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		1.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
+
+PM_DT_MIN               0.25
+PM_TOOFEW               4
+POS_TOOFEW              1
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/format.config	(revision 22322)
@@ -0,0 +1,100 @@
+# The LOT1m data is a single chip with single cell
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	LOT1m
+	INSTRUME	STR	PI1300B
+	OBSERVAT	STR	NCU Lulin observatory, Taiwan
+	CAMERA		STR	Princeton Instruments 1300B
+	DETECTOR	STR	EEV 1340x1300 CCD36-4
+	NAXIS		S32	2
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	OWNER	# A PHU keyword for unique identifier
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:Amplifier
+
+# Specify the cells
+CELLS		METADATA
+	# Cell type, concepts to use
+	Amplifier	METADATA
+		CELL.BIASSEC.SOURCE	STR	NONE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC		STR	[0:0,0:0]
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.DETECTOR	STR	DETECTOR
+	FPA.OBSTYPE	STR	IMAGETYP
+	FPA.TIME	STR	DATE-OBS TIME-OBS
+	FPA.TIMESYS	STR	TIMESYS
+        FPA.RA          STR     OBJCTRA
+        FPA.DEC         STR     OBJCTDEC
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.TEMP	STR	CCD-TEMP
+	CHIP.TEMP	STR	CCD-TEMP
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     EXPTIME
+        CELL.READNOISE  STR     SLOWRN
+	CELL.GAIN	STR	SLOWGAIN
+	CELL.TIME	STR	DATE-OBS TIME-OBS
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     XBINNING
+        CELL.YBIN       STR     YBINNING
+	CELL.XWINDOW	STR	XORGSUBF
+	CELL.YWINDOW	STR	YORGSUBF
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+        FPA.AIRMASS     	F32	0.0
+        FPA.FILTERID    	STR     UNKNOWN
+        FPA.FILTER      	STR     UNKNOWN
+	FPA.OBJECT		STR	Unknown object
+        FPA.RADECSYS    	STR     ICRS
+        FPA.POSANGLE    	STR     0.0
+	FPA.FOCUS		STR	0.0
+	FPA.ALT			STR	0.0
+	FPA.AZ			STR	0.0
+
+	CHIP.XSIZE		S32	1340
+	CHIP.YSIZE		S32	1300
+	CELL.XSIZE		S32	1340
+	CELL.YSIZE		S32	1300
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.SATURATION		F32	50000.0
+        CELL.BAD                F32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.X0			S32	0
+	CELL.Y0			S32	0
+	CHIP.X0			S32	0
+	CHIP.Y0			S32	0
+	CHIP.XPARITY		S32	1
+	CHIP.YPARITY		S32	1
+END
+
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+#	CELL.GAIN	STR	SELECT gain FROM camera WHERE chip_id = '{CHIP.NAME}' AND cell_id = '{CELL.NAME}'
+END		
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	SEPARATE YEAR.FIRST
+	CELL.TIME	STR	SEPARATE YEAR.FIRST
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/ppImage.config	(revision 22322)
@@ -0,0 +1,169 @@
+### ppImage recipe configuration file for Megacam
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+OVERSCAN.FIT		STR	NONE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	0		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# for a test, turn off selected detrend types
+OVERSCAN	BOOL	FALSE		# Overscan subtraction
+BIAS		BOOL	FALSE		# Bias subtraction
+DARK		BOOL	TRUE		# Dark subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+
+# binned output image options
+BIN1.XBIN               S32     18
+BIN1.YBIN               S32     18
+BIN2.XBIN               S32     144
+BIN2.YBIN               S32     144
+
+# megacam needs fringe subtraction
+FRINGE		BOOL	TRUE		# Fringe subtraction
+
+# this table lists extra constraints which should be applied when
+# selecting the detrend images.
+# note: camera and time are always applied
+
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+#   EXPTIME STR FPA.EXPOSURE
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+#   DETTYPE STR DOMEFLAT.RAW
+#   VERSION STR SKYFLAT.RAW
+#   VERSION STR DOMEFLAT.CORR.XX
+#   VERSION STR SKYFLAT.CORR
+  END
+  FLAT_CORRECTION METADATA
+    FILTER   STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+#   AIRMASS  STR FPA.AIRMASS
+#   TWILIGHT STR FPA.TWILIGHT
+  END
+  SHUTTER METADATA
+  END
+  MASK METADATA
+  END	
+END
+
+# only apply the fringe for these filters
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS	STR i
+FRINGE.FILTERS	STR z
+
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS       METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # 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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR     METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/ppMerge.config	(revision 22322)
@@ -0,0 +1,40 @@
+
+# Bias combination --- don't want min/max rejection
+PPMERGE_BIAS	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
+   WEIGHTS	BOOL	FALSE		# Use image weights?
+   COMBINE	STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Dark combination --- don't want min/max rejection
+# More aggressive clipping than bias, so as to remove CRs
+PPMERGE_DARK	METADATA
+  REJ		F32	2.0		# Rejection threshold (sigma)
+  ITER		S32	4		# 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
+  WEIGHTS	BOOL	FALSE		# Use image weights?
+  COMBINE	STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Flat combination --- use min/max rejection
+PPMERGE_FLAT	METADATA
+	REJ		F32	3.0		# Rejection threshold (sigma)
+	ITER		S32	1		# Number of rejection iterations
+	FRACHIGH	F32	0.3		# Fraction of high pixels to reject immediately
+	FRACLOW		F32	0.1		# Fraction of low pixels to reject immediately
+	NKEEP		S32	5		# Minimum number of pixels in stack to keep
+	WEIGHTS		BOOL	FALSE		# Use image weights?
+	COMBINE		STR	MEAN		# Statistic to use for combination: 
+END
+
+
+# Fringe combination --- already included in default, above
+PPMERGE_FRINGE	METADATA
+	FRACHIGH	F32	0.1		# Fraction of high pixels to reject immediately
+	WEIGHTS		BOOL	FALSE		# Use image weights?
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/psastro.config	(revision 22322)
@@ -0,0 +1,74 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+PSASTRO.PIXEL.SCALE    F32  1.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32    2500.
+PSASTRO.GRID.SCALE     F32      50.
+
+# extra field for ref stars:
+PSASTRO.FIELD.PADDING  F32 1.0
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# single-chip radius match in pixels
+PSASTRO.MATCH.FIT.NITER S32   3
+PSASTRO.MATCH.RADIUS    F32   12.0
+PSASTRO.MATCH.RADIUS.N0 F32   15.0
+PSASTRO.MATCH.RADIUS.N1 F32   10.0
+PSASTRO.MATCH.RADIUS.N2 F32    5.0
+
+PSASTRO.MAX.NRAW       S32      1500   # max stars accepted for fitting (0 for all)
+PSASTRO.MAX.NREF       S32      1500   # max stars accepted for fitting (0 for all)
+
+PSASTRO.MIN.INST.MAG.RAW       F32      -15.5  # min instrumental magnitude for stars accepted for fitting
+PSASTRO.MAX.INST.MAG.RAW       F32       -8.0  # max instrumental magnitude for stars accepted for fitting
+
+PSASTRO.MAX.ERROR      F32      10.0 # max error in pixels
+PSASTRO.MIN.NSTAR      S32      10   # min fitted stars in solution
+
+PSASTRO.MOSAIC.MODE           BOOL     FALSE
+
+PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+PSASTRO.MOSAIC.RADIUS.N0    F32    10.0
+PSASTRO.MOSAIC.RADIUS.N1    F32     8.0 # do not refine the match
+PSASTRO.MOSAIC.RADIUS.N2    F32     8.0 # do not refine the match
+PSASTRO.MOSAIC.RADIUS.N3    F32     4.0 
+
+PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      3 # fit order (-1 means use default)
+
+PSASTRO.MOSAIC.GRADIENT.NX    S32      2   # number of x-dir cells per chip
+PSASTRO.MOSAIC.GRADIENT.NY    S32      4   # number of y-dir cells per chip
+
+# use this recipe to set a tight constraint
+PSASTRO.FINE METADATA
+  PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+  PSASTRO.MOSAIC.RADIUS.N0    F32    8.0
+  PSASTRO.MOSAIC.RADIUS.N1    F32    0.0
+  PSASTRO.MOSAIC.RADIUS.N2    F32    4.0
+  PSASTRO.MOSAIC.RADIUS.N3    F32    4.0
+
+  PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      1 # fit order (-1 means use default)
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/psphot.config	(revision 22322)
@@ -0,0 +1,43 @@
+
+# turn these on to see specific outputs
+SAVE.OUTPUT	BOOL 	TRUE
+SAVE.BACKMDL	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+SAVE.PLOTS     	BOOL    TRUE
+
+BACKGROUND.XBIN	    S32  128            # size of background superpixels
+BACKGROUND.YBIN	    S32  128            # size of background superpixels
+
+# image background parameters
+IMSTATS_NPIX        S32  10000    	 # number of pixels to use for sky estimate boxes:
+SKY_STAT            STR  FITTED_MEAN_V4  # statistic used to measure background
+SKY_CLIP_SIGMA      F32  2.0             # statistic used to measure background
+
+PSF_SN_LIM          F32  100             # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+# PSF model parameters : choose the PSF model desired
+PSF_MODEL           STR  PS_MODEL_QGAUSS
+
+MOMENTS_SN_MIN      F32   30.0
+EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+FULL_FIT_SN_LIM      F32  50.0
+AP_MIN_SN            F32  50.0
+
+# OUTPUT.FORMAT       STR SMPDATA
+OUTPUT.FORMAT       STR PS1_DEV_1
+
+PSF_SHAPE_NSIGMA     F32  3.0		 # max significance for shape variation
+PSF_MAX_CHI          F32  50.0		 # reject objects worse that this
+
+#PSF.TREND.MODE STR MAP
+#PSF.TREND.NX   S32 1
+#PSF.TREND.NY   S32 1
+
+DIAGNOSTIC.PLOTS		METADATA
+  IMAGE.BACKGROUND.CELL.HISTOGRAM   BOOL FALSE
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.X S32 -1
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.Y S32 12
+END
+
+USE_FOOTPRINTS                      BOOL  TRUE       	  # use new pmFootprint peak packaging
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/pswarp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/pswarp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/pswarp.config	(revision 22322)
@@ -0,0 +1,3 @@
+### Megacam recipe for pswarp
+
+ASTROM.SOURCE	STR	PSASTRO.OUTPUT	# Source file rule for astrometry, or NULL
Index: /tags/sj_tags/sj_root_20080929/ippconfig/lulin/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/lulin/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/lulin/rejections.config	(revision 22322)
@@ -0,0 +1,38 @@
+
+BIAS METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0
+  IMFILE.STDEV       F32 10.0
+  IMFILE.MEANSTDEV   F32  0.5
+  IMFILE.SKEWNESS    F32  0.0
+  IMFILE.KURTOSIS    F32  0.0
+  IMFILE.SNR         F32  0.0
+  IMFILE.BIN.STDEV   F32  0.5
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  0.5
+  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  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+# FILTER is an additional qualifier, and may be "*" (or absent!), in which case it matches everything
+# EXPECTED is the expected mean value
+# IMFILE.MEAN is the maximum permitted mean value for an imfile, relative to the standard deviation
+# IMFILE.STDEV is the maximum permitted standard deviation for an imfile
+# EXP.MEAN is the maximum permitted mean value for an exposure, relative to the standard deviation
+# EXP.STDEV is the maximum permitted standard deviation for an exposure
+# EXP.MEANSTDEV is the maximum permitted mean standard deviation for an exposure relative to the mean
+# ENSEMBLE.MEAN is the maximum permitted mean for an ensemble of exposures
+# ENSEMBLE.STDEV is the maximum permitted standard deviation for an ensemble of exposures
+# ENSEMBLE.MEANSTDEV is the maximum permitted mean standard deviation for an ensemble of exposures
+# IMFILE.SNR is the minimum permitted signal-to-noise for an imfile
+# EXP.SNR is the minimum permitted signal-to-noise for an exposure
+# These values (all except FILTER) may be zero, in which case no clipping is applied.
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/camera.config	(revision 22322)
@@ -0,0 +1,103 @@
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# Camera configuration file for MegaCam: describes the camera
+
+# File formats that we know about
+FORMATS		METADATA
+	RAW	STR	mcshort/format_raw.config
+	SPLICED	STR	mcshort/format_spliced.config
+	SPLIT	STR	mcshort/format_split.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA	METADATA
+#	ccd00	STR	LeftAmp RightAmp
+#	ccd01	STR	LeftAmp RightAmp
+#	ccd02	STR	LeftAmp RightAmp
+#	ccd03	STR	LeftAmp RightAmp
+#	ccd04	STR	LeftAmp RightAmp
+#	ccd05	STR	LeftAmp RightAmp
+#	ccd06	STR	LeftAmp RightAmp
+#	ccd07	STR	LeftAmp RightAmp
+#	ccd08	STR	LeftAmp RightAmp
+#	ccd09	STR	LeftAmp RightAmp
+#	ccd10	STR	LeftAmp RightAmp
+#	ccd11	STR	LeftAmp RightAmp
+	ccd12	STR	LeftAmp RightAmp
+	ccd13	STR	LeftAmp RightAmp
+	ccd14	STR	LeftAmp RightAmp
+#	ccd15	STR	LeftAmp RightAmp
+#	ccd16	STR	LeftAmp RightAmp
+#	ccd17	STR	LeftAmp RightAmp
+#	ccd18	STR	LeftAmp RightAmp
+#	ccd19	STR	LeftAmp RightAmp
+#	ccd20	STR	LeftAmp RightAmp
+	ccd21	STR	LeftAmp RightAmp
+	ccd22	STR	LeftAmp RightAmp
+	ccd23	STR	LeftAmp RightAmp
+#	ccd24	STR	LeftAmp RightAmp
+#	ccd25	STR	LeftAmp RightAmp
+#	ccd26	STR	LeftAmp RightAmp
+#	ccd27	STR	LeftAmp RightAmp
+#	ccd28	STR	LeftAmp RightAmp
+#	ccd29	STR	LeftAmp RightAmp
+#	ccd30	STR	LeftAmp RightAmp
+#	ccd31	STR	LeftAmp RightAmp
+#	ccd32	STR	LeftAmp RightAmp
+#	ccd33	STR	LeftAmp RightAmp
+#	ccd34	STR	LeftAmp RightAmp
+#	ccd35	STR	LeftAmp RightAmp
+END
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+   u        STR   u.MP9301     
+   g        STR   g.MP9401     
+   r        STR   r.MP9601     
+   i        STR   i.MP9701     
+   z        MULTI
+   z        STR   z.MP9801     
+   z        STR   Zp           
+   z        STR   Zprime       
+
+   Ha       MULTI
+   Ha       STR   Ha.MP7605    
+   Ha       STR   Halpha       
+   Ha       STR   Haalpha.on   
+   HaOff    STR   HaOFF.MP7604 
+   	              		
+   CN       MULTI
+   CN       STR   CN.MP780     
+   CN       STR   cn.MP7803    
+   CN       STR   CN.MP7803    
+   	              		
+   TiO      MULTI
+   TiO      STR   TiO.MP77     
+   TiO      STR   tio.MP7701   
+   TiO      STR   TiO.MP7701   
+   NB920    STR   NB920        
+   	              		
+   B2F      STR   B2F          
+   Bj       STR   Bj           
+   Vj       STR   Vj           
+   Rj       STR   Rj           
+   Ij       STR   Ij           
+   Hb       STR   Hb           
+   HbOff    STR   HbOff        
+END
+
+# Recipe options
+RECIPES		METADATA
+	# Other recipes
+        PSPHOT          STR     megacam/psphot.config           # psphot details
+        PSASTRO         STR     megacam/psastro.config          # psastro details
+	PPSTATS		STR	megacam/ppStats.config		# ppStats recipe
+	PPIMAGE         STR     megacam/ppImage.config		# ppImage recipe
+END
+
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+FILERULES       STR     recipes/filerules-mef.mdc
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/format_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/format_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/format_raw.config	(revision 22322)
@@ -0,0 +1,336 @@
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The raw MegaCam data comes off the telescope with each of the chips stored in extensions of a MEF file.
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	MegaCam
+	EXTEND		BOOL	T
+	NEXTEND		S32	72
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CELL	# The extensions represent cells
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:type
+#	amp00		STR	ccd00:LeftAmp:left
+#	amp01		STR	ccd00:RightAmp:right
+#	amp02		STR	ccd01:LeftAmp:left
+#	amp03		STR	ccd01:RightAmp:right
+#	amp04		STR	ccd02:LeftAmp:left
+#	amp05		STR	ccd02:RightAmp:right
+#	amp06		STR	ccd03:LeftAmp:left
+#	amp07		STR	ccd03:RightAmp:right
+#	amp08		STR	ccd04:LeftAmp:left
+#	amp09		STR	ccd04:RightAmp:right
+#	amp10		STR	ccd05:LeftAmp:left
+#	amp11		STR	ccd05:RightAmp:right
+#	amp12		STR	ccd06:LeftAmp:left
+#	amp13		STR	ccd06:RightAmp:right
+#	amp14		STR	ccd07:LeftAmp:left
+#	amp15		STR	ccd07:RightAmp:right
+#	amp16		STR	ccd08:LeftAmp:left
+#	amp17		STR	ccd08:RightAmp:right
+#	amp18		STR	ccd09:LeftAmp:left
+#	amp19		STR	ccd09:RightAmp:right
+#	amp20		STR	ccd10:LeftAmp:left
+#	amp21		STR	ccd10:RightAmp:right
+#	amp22		STR	ccd11:LeftAmp:left
+#	amp23		STR	ccd11:RightAmp:right
+	amp24		STR	ccd12:LeftAmp:left
+	amp25		STR	ccd12:RightAmp:right
+	amp26		STR	ccd13:LeftAmp:left
+	amp27		STR	ccd13:RightAmp:right
+	amp28		STR	ccd14:LeftAmp:left
+	amp29		STR	ccd14:RightAmp:right
+#	amp30		STR	ccd15:LeftAmp:left
+#	amp31		STR	ccd15:RightAmp:right
+#	amp32		STR	ccd16:LeftAmp:left
+#	amp33		STR	ccd16:RightAmp:right
+#	amp34		STR	ccd17:LeftAmp:left
+#	amp35		STR	ccd17:RightAmp:right
+#	amp36		STR	ccd18:LeftAmp:left
+#	amp37		STR	ccd18:RightAmp:right
+#	amp38		STR	ccd19:LeftAmp:left
+#	amp39		STR	ccd19:RightAmp:right
+#	amp40		STR	ccd20:LeftAmp:left
+#	amp41		STR	ccd20:RightAmp:right
+	amp42		STR	ccd21:LeftAmp:left
+	amp43		STR	ccd21:RightAmp:right
+	amp44		STR	ccd22:LeftAmp:left
+	amp45		STR	ccd22:RightAmp:right
+	amp46		STR	ccd23:LeftAmp:left
+	amp47		STR	ccd23:RightAmp:right
+#	amp48		STR	ccd24:LeftAmp:left
+#	amp49		STR	ccd24:RightAmp:right
+#	amp50		STR	ccd25:LeftAmp:left
+#	amp51		STR	ccd25:RightAmp:right
+#	amp52		STR	ccd26:LeftAmp:left
+#	amp53		STR	ccd26:RightAmp:right
+#	amp54		STR	ccd27:LeftAmp:left
+#	amp55		STR	ccd27:RightAmp:right
+#	amp56		STR	ccd28:LeftAmp:left
+#	amp57		STR	ccd28:RightAmp:right
+#	amp58		STR	ccd29:LeftAmp:left
+#	amp59		STR	ccd29:RightAmp:right
+#	amp60		STR	ccd30:LeftAmp:left
+#	amp61		STR	ccd30:RightAmp:right
+#	amp62		STR	ccd31:LeftAmp:left
+#	amp63		STR	ccd31:RightAmp:right
+#	amp64		STR	ccd32:LeftAmp:left
+#	amp65		STR	ccd32:RightAmp:right
+#	amp66		STR	ccd33:LeftAmp:left
+#	amp67		STR	ccd33:RightAmp:right
+#	amp68		STR	ccd34:LeftAmp:left
+#	amp69		STR	ccd34:RightAmp:right
+#	amp70		STR	ccd35:LeftAmp:left
+#	amp71		STR	ccd35:RightAmp:right
+END
+
+# Specify the cell data
+CELLS	METADATA
+	left	METADATA	# Left amplifier
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XPARITY		S32	1 # We could have specified this as a DEFAULT, but this works
+		CELL.X0			S32	1
+		CELL.Y0			S32	1
+	END
+	right	METADATA	# Right amplifier
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XPARITY		S32	-1 # This cell is read out in the opposite direction
+		CELL.X0			S32	2048
+		CELL.Y0			S32	1
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBS		STR	EXPNUM
+	FPA.AIRMASS		STR	AIRMASS
+	FPA.FILTER		STR	FILTER
+	FPA.POSANGLE		STR	ROTANGLE
+	FPA.RA			STR	RA
+	FPA.DEC			STR	DEC
+	FPA.RADECSYS		STR	RADECSYS
+	FPA.OBSTYPE		STR	OBSTYPE
+	FPA.OBJECT		STR	CMMTOBS
+	FPA.TIME		STR	MJD-OBS
+	FPA.TIMESYS		STR	TIMESYS
+	FPA.ALT			STR	TELALT
+	FPA.AZ			STR	TELAZ
+	CHIP.TEMP		STR	DETTEM
+	CELL.EXPOSURE		STR	EXPTIME
+	CELL.DARKTIME		STR	DARKTIME
+	CELL.GAIN		STR	GAIN
+	CELL.READNOISE		STR	RDNOISE
+	CELL.SATURATION		STR	SATURATE
+	CELL.TIME		STR	MJD-OBS
+	CELL.TIMESYS		STR	TIMESYS
+	CELL.XBIN		STR	CCDBIN1
+	CELL.YBIN		STR	CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	CELL.READDIR		S32	1		# Cell is read in x direction
+	CELL.BAD		S32	0
+	CELL.YPARITY		S32	1
+
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+#		ccd00	S32	0
+#		ccd01	S32	2048
+#		ccd02	S32	4096
+#		ccd03	S32	6144
+#		ccd04	S32	8192
+#		ccd05	S32	10240
+#		ccd06	S32	12288
+#		ccd07	S32	14336
+#		ccd08	S32	16384
+#		ccd09	S32	0
+#		ccd10	S32	2048
+#		ccd11	S32	4096
+		ccd12	S32	6144
+		ccd13	S32	8192
+		ccd14	S32	10240
+#		ccd15	S32	12288
+#		ccd16	S32	14336
+#		ccd17	S32	16384
+#		ccd18	S32	0
+#		ccd19	S32	2048
+#		ccd20	S32	4096
+		ccd21	S32	6144
+		ccd22	S32	8192
+		ccd23	S32	10240
+#		ccd24	S32	12288
+#		ccd25	S32	14336
+#		ccd26	S32	16384
+#		ccd27	S32	0
+#		ccd28	S32	2048
+#		ccd29	S32	4096
+#		ccd30	S32	6144
+#		ccd31	S32	8192
+#		ccd32	S32	10240
+#		ccd33	S32	12288
+#		ccd34	S32	14336
+#		ccd35	S32	16384
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+#		ccd00	S32	18447
+#		ccd01	S32	18447
+#		ccd02	S32	18447
+#		ccd03	S32	18447
+#		ccd04	S32	18447
+#		ccd05	S32	18447
+#		ccd06	S32	18447
+#		ccd07	S32	18447
+#		ccd08	S32	18447
+#		ccd09	S32	13835
+#		ccd10	S32	13835
+#		ccd11	S32	13835
+		ccd12	S32	13835
+		ccd13	S32	13835
+		ccd14	S32	13835
+#		ccd15	S32	13835
+#		ccd16	S32	13835
+#		ccd17	S32	13835
+#		ccd18	S32	13835
+#		ccd19	S32	4612
+#		ccd20	S32	4612
+		ccd21	S32	4612
+		ccd22	S32	4612
+		ccd23	S32	4612
+#		ccd24	S32	4612
+#		ccd25	S32	4612
+#		ccd26	S32	4612
+#		ccd27	S32	0
+#		ccd28	S32	0
+#		ccd29	S32	0
+#		ccd30	S32	0
+#		ccd31	S32	0
+#		ccd32	S32	0
+#		ccd33	S32	0
+#		ccd34	S32	0
+#		ccd35	S32	0
+	END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+#		ccd00	S32	1
+#		ccd01	S32	1
+#		ccd02	S32	1
+#		ccd03	S32	1
+#		ccd04	S32	1
+#		ccd05	S32	1
+#		ccd06	S32	1
+#		ccd07	S32	1
+#		ccd08	S32	1
+#		ccd09	S32	1
+#		ccd10	S32	1
+#		ccd11	S32	1
+		ccd12	S32	1
+		ccd13	S32	1
+		ccd14	S32	1
+#		ccd15	S32	1
+#		ccd16	S32	1
+#		ccd17	S32	1
+#		ccd18	S32	1
+#		ccd19	S32	1
+#		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+#		ccd24	S32	1
+#		ccd25	S32	1
+#		ccd26	S32	1
+#		ccd27	S32	1
+#		ccd28	S32	1
+#		ccd29	S32	1
+#		ccd30	S32	1
+#		ccd31	S32	1
+#		ccd32	S32	1
+#		ccd33	S32	1
+#		ccd34	S32	1
+#		ccd35	S32	1
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+#		ccd00	S32	-1
+#		ccd01	S32	-1
+#		ccd02	S32	-1
+#		ccd03	S32	-1
+#		ccd04	S32	-1
+#		ccd05	S32	-1
+#		ccd06	S32	-1
+#		ccd07	S32	-1
+#		ccd08	S32	-1
+#		ccd09	S32	-1
+#		ccd10	S32	-1
+#		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+#		ccd15	S32	-1
+#		ccd16	S32	-1
+#		ccd17	S32	-1
+#		ccd18	S32	1
+#		ccd19	S32	1
+#		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+#		ccd24	S32	1
+#		ccd25	S32	1
+#		ccd26	S32	1
+#		ccd27	S32	1
+#		ccd28	S32	1
+#		ccd29	S32	1
+#		ccd30	S32	1
+#		ccd31	S32	1
+#		ccd32	S32	1
+#		ccd33	S32	1
+#		ccd34	S32	1
+#		ccd35	S32	1
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	FPA.BIAS	METADATA
+#		TABLE	STR	Camera
+#		COLUMN	STR	gain
+#		chipId	STR	{CHIP.NAME}
+#		cellId	STR	{CELL.NAME}
+#		time	STR	{CELL.TIME}
+#	END
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+	CELL.X0		STR	FORTRAN
+	CELL.Y0		STR	FORTRAN
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/format_spliced.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/format_spliced.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/format_spliced.config	(revision 22322)
@@ -0,0 +1,304 @@
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The spliced MecaCam data is stored in single extensions for each chip
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	MegaCam
+	EXTEND		BOOL	T
+	NEXTEND		S32	36
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CHIP	# The extensions represent chips
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier
+	CHIP.NAME	STR	EXTNAME	# An extension keyword for unique identifie
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chipName:chipType
+#	ccd00		STR	ccd00:CCD42-90
+#	ccd01		STR	ccd01:CCD42-90
+#	ccd02		STR	ccd02:CCD42-90
+#	ccd03		STR	ccd03:CCD42-90
+#	ccd04		STR	ccd04:CCD42-90
+#	ccd05		STR	ccd05:CCD42-90
+#	ccd06		STR	ccd06:CCD42-90
+#	ccd07		STR	ccd07:CCD42-90
+#	ccd08		STR	ccd08:CCD42-90
+#	ccd09		STR	ccd09:CCD42-90
+#	ccd10		STR	ccd10:CCD42-90
+#	ccd11		STR	ccd11:CCD42-90
+	ccd12		STR	ccd12:CCD42-90
+	ccd13		STR	ccd13:CCD42-90
+	ccd14		STR	ccd14:CCD42-90
+#	ccd15		STR	ccd15:CCD42-90
+#	ccd16		STR	ccd16:CCD42-90
+#	ccd17		STR	ccd17:CCD42-90
+#	ccd18		STR	ccd18:CCD42-90
+#	ccd19		STR	ccd19:CCD42-90
+#	ccd20		STR	ccd20:CCD42-90
+	ccd21		STR	ccd21:CCD42-90
+	ccd22		STR	ccd22:CCD42-90
+	ccd23		STR	ccd23:CCD42-90
+#	ccd24		STR	ccd24:CCD42-90
+#	ccd25		STR	ccd25:CCD42-90
+#	ccd26		STR	ccd26:CCD42-90
+#	ccd27		STR	ccd27:CCD42-90
+#	ccd28		STR	ccd28:CCD42-90
+#	ccd29		STR	ccd29:CCD42-90
+#	ccd30		STR	ccd30:CCD42-90
+#	ccd31		STR	ccd31:CCD42-90
+#	ccd32		STR	ccd32:CCD42-90
+#	ccd33		STR	ccd33:CCD42-90
+#	ccd34		STR	ccd34:CCD42-90
+#	ccd35		STR	ccd35:CCD42-90
+END
+
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	CCD42-90	STR	LeftAmp:left RightAmp:right
+END
+
+# Specify the cells
+CELLS		METADATA
+	left		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECA
+		CELL.TRIMSEC		STR	TSECA
+		CELL.X0			S32	0
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAINA
+	END
+
+	right		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECB
+		CELL.TRIMSEC		STR	TSECB
+		CELL.X0			S32	1024
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAINB
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+        FPA.OBS        STR     EXPNUM
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTER      STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	CMMTOBS
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	TELALT
+	FPA.AZ		STR	TELAZ
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.READNOISE  STR     RDNOISE
+        CELL.SATURATION STR     SATURATE
+	CELL.TIME	STR	MJD-OBS
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TIMESYS		STR	UTC
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.BAD                S32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+#	PPMERGE.SCALE		F32	1.0
+#	PPMERGE.ZERO		F32	0.0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+#		ccd00	S32	
+#		ccd01	S32
+#		ccd02	S32
+#		ccd03	S32
+#		ccd04	S32
+#		ccd05	S32
+#		ccd06	S32
+#		ccd07	S32
+#		ccd08	S32
+#		ccd09	S32
+#		ccd10	S32
+#		ccd11	S32
+		ccd12	S32	0
+		ccd13	S32	2048
+		ccd14	S32	4096
+#		ccd15	S32
+#		ccd16	S32
+#		ccd17	S32
+#		ccd18	S32
+#		ccd19	S32
+#		ccd20	S32
+		ccd21	S32	0
+		ccd22	S32	2048
+		ccd23	S32	4096
+#		ccd24	S32
+#		ccd25	S32
+#		ccd26	S32
+#		ccd27	S32
+#		ccd28	S32
+#		ccd29	S32
+#		ccd30	S32
+#		ccd31	S32
+#		ccd32	S32
+#		ccd33	S32
+#		ccd34	S32
+#		ccd35	S32
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+#		ccd00	S32	
+#		ccd01	S32
+#		ccd02	S32
+#		ccd03	S32
+#		ccd04	S32
+#		ccd05	S32
+#		ccd06	S32
+#		ccd07	S32
+#		ccd08	S32
+#		ccd09	S32
+#		ccd10	S32
+#		ccd11	S32
+		ccd12	S32	9223
+		ccd13	S32	9223
+		ccd14	S32	9223
+#		ccd15	S32
+#		ccd16	S32
+#		ccd17	S32
+#		ccd18	S32
+#		ccd19	S32
+#		ccd20	S32
+		ccd21	S32	0
+		ccd22	S32	0
+		ccd23	S32	0
+#		ccd24	S32
+#		ccd25	S32
+#		ccd26	S32
+#		ccd27	S32
+#		ccd28	S32
+#		ccd29	S32
+#		ccd30	S32
+#		ccd31	S32
+#		ccd32	S32
+#		ccd33	S32
+#		ccd34	S32
+#		ccd35	S32
+	END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+#		ccd00	S32	
+#		ccd01	S32
+#		ccd02	S32
+#		ccd03	S32
+#		ccd04	S32
+#		ccd05	S32
+#		ccd06	S32
+#		ccd07	S32
+#		ccd08	S32
+#		ccd09	S32
+#		ccd10	S32
+#		ccd11	S32
+		ccd12	S32	1
+		ccd13	S32	1
+		ccd14	S32	1
+#		ccd15	S32
+#		ccd16	S32
+#		ccd17	S32
+#		ccd18	S32
+#		ccd19	S32
+#		ccd20	S32
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+#		ccd24	S32
+#		ccd25	S32
+#		ccd26	S32
+#		ccd27	S32
+#		ccd28	S32
+#		ccd29	S32
+#		ccd30	S32
+#		ccd31	S32
+#		ccd32	S32
+#		ccd33	S32
+#		ccd34	S32
+#		ccd35	S32
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+#		ccd00	S32	
+#		ccd01	S32
+#		ccd02	S32
+#		ccd03	S32
+#		ccd04	S32
+#		ccd05	S32
+#		ccd06	S32
+#		ccd07	S32
+#		ccd08	S32
+#		ccd09	S32
+#		ccd10	S32
+#		ccd11	S32
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+#		ccd15	S32
+#		ccd16	S32
+#		ccd17	S32
+#		ccd18	S32
+#		ccd19	S32
+#		ccd20	S32
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+#		ccd24	S32
+#		ccd25	S32
+#		ccd26	S32
+#		ccd27	S32
+#		ccd28	S32
+#		ccd29	S32
+#		ccd30	S32
+#		ccd31	S32
+#		ccd32	S32
+#		ccd33	S32
+#		ccd34	S32
+#		ccd35	S32
+	END
+END
+
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/format_split.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/format_split.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mcshort/format_split.config	(revision 22322)
@@ -0,0 +1,301 @@
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The spliced MecaCam data is stored in single extensions for each chip
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	MegaCam
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	CHIP	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# The extensions represent chips
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier
+	CONTENT		STR	EXTNAME	# Key to the CONTENTS menu
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:component cell
+#	ccd00		STR	ccd00:CCD42-90
+#	ccd01		STR	ccd01:CCD42-90
+#	ccd02		STR	ccd02:CCD42-90
+#	ccd03		STR	ccd03:CCD42-90
+#	ccd04		STR	ccd04:CCD42-90
+#	ccd05		STR	ccd05:CCD42-90
+#	ccd06		STR	ccd06:CCD42-90
+#	ccd07		STR	ccd07:CCD42-90
+#	ccd08		STR	ccd08:CCD42-90
+#	ccd09		STR	ccd09:CCD42-90
+#	ccd10		STR	ccd10:CCD42-90
+#	ccd11		STR	ccd11:CCD42-90
+	ccd12		STR	ccd12:CCD42-90
+	ccd13		STR	ccd13:CCD42-90
+	ccd14		STR	ccd14:CCD42-90
+#	ccd15		STR	ccd15:CCD42-90
+#	ccd16		STR	ccd16:CCD42-90
+#	ccd17		STR	ccd17:CCD42-90
+#	ccd18		STR	ccd18:CCD42-90
+#	ccd19		STR	ccd19:CCD42-90
+#	ccd20		STR	ccd20:CCD42-90
+	ccd21		STR	ccd21:CCD42-90
+	ccd22		STR	ccd22:CCD42-90
+	ccd23		STR	ccd23:CCD42-90
+#	ccd24		STR	ccd24:CCD42-90
+#	ccd25		STR	ccd25:CCD42-90
+#	ccd26		STR	ccd26:CCD42-90
+#	ccd27		STR	ccd27:CCD42-90
+#	ccd28		STR	ccd28:CCD42-90
+#	ccd29		STR	ccd29:CCD42-90
+#	ccd30		STR	ccd30:CCD42-90
+#	ccd31		STR	ccd31:CCD42-90
+#	ccd32		STR	ccd32:CCD42-90
+#	ccd33		STR	ccd33:CCD42-90
+#	ccd34		STR	ccd34:CCD42-90
+#	ccd35		STR	ccd35:CCD42-90
+END
+
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	CCD42-90	STR	LeftAmp:left RightAmp:right
+END
+
+# Specify the cells
+CELLS		METADATA
+	left		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECA
+		CELL.TRIMSEC		STR	TSECA
+		CELL.X0			S32	0
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAINA
+	END
+
+	right		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECB
+		CELL.TRIMSEC		STR	TSECB
+		CELL.X0			S32	1024
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAINB
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+        FPA.OBS        STR     EXPNUM
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTER      STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	CMMTOBS
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	TELALT
+	FPA.AZ		STR	TELAZ
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.READNOISE  STR     RDNOISE
+        CELL.SATURATION STR     SATURATE
+	CELL.TIME	STR	MJD-OBS
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.BAD                S32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+#	PPMERGE.SCALE		F32	1.0
+#	PPMERGE.ZERO		F32	0.0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+#		ccd00	S32	
+#		ccd01	S32
+#		ccd02	S32
+#		ccd03	S32
+#		ccd04	S32
+#		ccd05	S32
+#		ccd06	S32
+#		ccd07	S32
+#		ccd08	S32
+#		ccd09	S32
+#		ccd10	S32
+#		ccd11	S32
+		ccd12	S32	0
+		ccd13	S32	2048
+		ccd14	S32	4096
+#		ccd15	S32
+#		ccd16	S32
+#		ccd17	S32
+#		ccd18	S32
+#		ccd19	S32
+#		ccd20	S32
+		ccd21	S32	0
+		ccd22	S32	2048
+		ccd23	S32	4096
+#		ccd24	S32
+#		ccd25	S32
+#		ccd26	S32
+#		ccd27	S32
+#		ccd28	S32
+#		ccd29	S32
+#		ccd30	S32
+#		ccd31	S32
+#		ccd32	S32
+#		ccd33	S32
+#		ccd34	S32
+#		ccd35	S32
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+#		ccd00	S32	
+#		ccd01	S32
+#		ccd02	S32
+#		ccd03	S32
+#		ccd04	S32
+#		ccd05	S32
+#		ccd06	S32
+#		ccd07	S32
+#		ccd08	S32
+#		ccd09	S32
+#		ccd10	S32
+#		ccd11	S32
+		ccd12	S32	9223
+		ccd13	S32	9223
+		ccd14	S32	9223
+#		ccd15	S32
+#		ccd16	S32
+#		ccd17	S32
+#		ccd18	S32
+#		ccd19	S32
+#		ccd20	S32
+		ccd21	S32	0
+		ccd22	S32	0
+		ccd23	S32	0
+#		ccd24	S32
+#		ccd25	S32
+#		ccd26	S32
+#		ccd27	S32
+#		ccd28	S32
+#		ccd29	S32
+#		ccd30	S32
+#		ccd31	S32
+#		ccd32	S32
+#		ccd33	S32
+#		ccd34	S32
+#		ccd35	S32
+	END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+#		ccd00	S32	
+#		ccd01	S32
+#		ccd02	S32
+#		ccd03	S32
+#		ccd04	S32
+#		ccd05	S32
+#		ccd06	S32
+#		ccd07	S32
+#		ccd08	S32
+#		ccd09	S32
+#		ccd10	S32
+#		ccd11	S32
+		ccd12	S32	1
+		ccd13	S32	1
+		ccd14	S32	1
+#		ccd15	S32
+#		ccd16	S32
+#		ccd17	S32
+#		ccd18	S32
+#		ccd19	S32
+#		ccd20	S32
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+#		ccd24	S32
+#		ccd25	S32
+#		ccd26	S32
+#		ccd27	S32
+#		ccd28	S32
+#		ccd29	S32
+#		ccd30	S32
+#		ccd31	S32
+#		ccd32	S32
+#		ccd33	S32
+#		ccd34	S32
+#		ccd35	S32
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+#		ccd00	S32	
+#		ccd01	S32
+#		ccd02	S32
+#		ccd03	S32
+#		ccd04	S32
+#		ccd05	S32
+#		ccd06	S32
+#		ccd07	S32
+#		ccd08	S32
+#		ccd09	S32
+#		ccd10	S32
+#		ccd11	S32
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+#		ccd15	S32
+#		ccd16	S32
+#		ccd17	S32
+#		ccd18	S32
+#		ccd19	S32
+#		ccd20	S32
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+#		ccd24	S32
+#		ccd25	S32
+#		ccd26	S32
+#		ccd27	S32
+#		ccd28	S32
+#		ccd29	S32
+#		ccd30	S32
+#		ccd31	S32
+#		ccd32	S32
+#		ccd33	S32
+#		ccd34	S32
+#		ccd35	S32
+	END
+END
+
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/Makefile.am	(revision 22322)
@@ -0,0 +1,27 @@
+
+installdir = $(datadir)/ippconfig/megacam
+
+install_files = \
+	dvo.config \
+	dvo.layout \
+	camera.config \
+	format_raw.config \
+	format_mef.config \
+	format_pre_03Am04_raw.config \
+	format_pre_03Am04_mef.config \
+	format_split.config \
+	ppImage.config \
+	ppMerge.config \
+	psastro.config \
+	psphot.config \
+	pswarp.config \
+	rejections.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/camera.config	(revision 22322)
@@ -0,0 +1,151 @@
+# Camera configuration file for MegaCam: describes the camera
+
+# File formats that we know about
+FORMATS         METADATA
+        MEF 	STR     megacam/format_mef.config
+        RAW     STR     megacam/format_raw.config
+        MEF_03 	STR     megacam/format_pre_03Am04_mef.config
+        RAW_03  STR     megacam/format_pre_03Am04_raw.config
+        SPLIT   STR     megacam/format_split.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        ccd00   STR     LeftAmp RightAmp
+        ccd01   STR     LeftAmp RightAmp
+        ccd02   STR     LeftAmp RightAmp
+        ccd03   STR     LeftAmp RightAmp
+        ccd04   STR     LeftAmp RightAmp
+        ccd05   STR     LeftAmp RightAmp
+        ccd06   STR     LeftAmp RightAmp
+        ccd07   STR     LeftAmp RightAmp
+        ccd08   STR     LeftAmp RightAmp
+        ccd09   STR     LeftAmp RightAmp
+        ccd10   STR     LeftAmp RightAmp
+        ccd11   STR     LeftAmp RightAmp
+        ccd12   STR     LeftAmp RightAmp
+        ccd13   STR     LeftAmp RightAmp
+        ccd14   STR     LeftAmp RightAmp
+        ccd15   STR     LeftAmp RightAmp
+        ccd16   STR     LeftAmp RightAmp
+        ccd17   STR     LeftAmp RightAmp
+        ccd18   STR     LeftAmp RightAmp
+        ccd19   STR     LeftAmp RightAmp
+        ccd20   STR     LeftAmp RightAmp
+        ccd21   STR     LeftAmp RightAmp
+        ccd22   STR     LeftAmp RightAmp
+        ccd23   STR     LeftAmp RightAmp
+        ccd24   STR     LeftAmp RightAmp
+        ccd25   STR     LeftAmp RightAmp
+        ccd26   STR     LeftAmp RightAmp
+        ccd27   STR     LeftAmp RightAmp
+        ccd28   STR     LeftAmp RightAmp
+        ccd29   STR     LeftAmp RightAmp
+        ccd30   STR     LeftAmp RightAmp
+        ccd31   STR     LeftAmp RightAmp
+        ccd32   STR     LeftAmp RightAmp
+        ccd33   STR     LeftAmp RightAmp
+        ccd34   STR     LeftAmp RightAmp
+        ccd35   STR     LeftAmp RightAmp
+END
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+   u        STR   u.MP9301     
+   g        STR   g.MP9401     
+   r        STR   r.MP9601     
+   i        STR   i.MP9701     
+   z        MULTI
+   z        STR   z.MP9801     
+   z        STR   Zp           
+   z        STR   Zprime       
+
+   Ha       MULTI
+   Ha       STR   Ha.MP7605    
+   Ha       STR   Halpha       
+   Ha       STR   Haalpha.on   
+   HaOff    STR   HaOFF.MP7604 
+   	              		
+   CN       MULTI
+   CN       STR   CN.MP780     
+   CN       STR   cn.MP7803    
+   CN       STR   CN.MP7803    
+   	              		
+   TiO      MULTI
+   TiO      STR   TiO.MP77     
+   TiO      STR   tio.MP7701   
+   TiO      STR   TiO.MP7701   
+   NB920    STR   NB920        
+   	              		
+   B2F      STR   B2F          
+   Bj       STR   Bj           
+   Vj       STR   Vj           
+   Rj       STR   Rj           
+   Ij       STR   Ij           
+   Hb       STR   Hb           
+   HbOff    STR   HbOff        
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID         METADATA
+        FPA     STR     fpa
+        CHIP    STR     {CHIP.NAME}
+        CELL    STR     {CHIP.NAME}:{CELL.NAME}
+        fpa     STR     fpa
+        chip    STR     {CHIP.NAME}
+        cell    STR     {CHIP.NAME}:{CELL.NAME}
+END
+
+DVO.CAMERADIR	STR	megacam		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias     STR BIAS
+  zero     STR BIAS
+  dark     STR DARK
+  flat     STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES         METADATA
+        # Other recipes
+        PSPHOT          STR     megacam/psphot.config           # psphot details
+        PSASTRO         STR     megacam/psastro.config          # psastro details
+	PSWARP		STR	megacam/pswarp.config		# pswarp details
+        PPIMAGE         STR     megacam/ppImage.config          # ppImage recipe
+	REJECTIONS      STR     megacam/rejections.config       # rejection recipe
+	PPMERGE		STR	megacam/ppMerge.config		# ppMerge recipe
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES	STR	recipes/filerules-mef.mdc		# File rules appropriate for MEF format
+
+EXTNAME.RULES METADATA
+  CMF.HEAD   STR {CHIP.NAME}.hdr
+  CMF.DATA   STR {CHIP.NAME}.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD   STR {CHIP.NAME}.hdr
+  PSF.TABLE  STR {CHIP.NAME}.psf_model
+  PSF.RESID  STR {CHIP.NAME}.psf_resid
+
+  REF.ASTROM STR {CHIP.NAME}.ref_astrom
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBS		STR	EXPNUM
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.N}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/dvo.config	(revision 22322)
@@ -0,0 +1,67 @@
+
+# location of DVO database tables
+CATDIR			/data/alala.0/eugene/isp/catdir
+
+# keywords used by DVO to interpret the headers
+
+# keyword abstractions:
+#DATE-KEYWORD		DATE-OBS
+#DATE-MODE		yyyy-mm-dd
+#UT-KEYWORD		UTC-OBS
+JD-KEYWORD		NONE
+MJD-KEYWORD		MJD-OBS
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		0
+CAL_INSTMAG_MIN		0
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		1.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
+
+PM_DT_MIN               0.25
+PM_TOOFEW               4
+POS_TOOFEW              1
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/dvo.layout
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/dvo.layout	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/dvo.layout	(revision 22322)
@@ -0,0 +1,58 @@
+# this file defines the layout of the mosaic imager:
+
+# NCCD 40  # this needs work: sometimes 36, sometimes 40
+NCCD 36
+NAXIS1 2112
+NAXIS2 4644
+
+MOSAIC_X 11
+MOSAIC_Y  4
+
+# lines need to contain:
+
+CHIPID_KEYWORD EXTNAME
+
+# ID     CHIPID xoffset yoffset xflip yflip datasec         biassec            Xo      Yo      theta
+CCD.0     ccd00       1       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.1     ccd01       2       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.2     ccd02       3       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.3     ccd03       4       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.4     ccd04       5       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.5     ccd05       6       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.6     ccd06       7       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.7     ccd07       8       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.8     ccd08       9       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.9     ccd09       1       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.10    ccd10       2       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.11    ccd11       3       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.12    ccd12       4       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.13    ccd13       5       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.14    ccd14       6       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.15    ccd15       7       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.16    ccd16       8       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.17    ccd17       9       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.18    ccd18       1       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.19    ccd19       2       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.20    ccd20       3       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.21    ccd21       4       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.22    ccd22       5       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.23    ccd23       6       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.24    ccd24       7       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.25    ccd25       8       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.26    ccd26       9       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.27    ccd27       1       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.28    ccd28       2       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.29    ccd29       3       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.30    ccd30       4       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.31    ccd31       5       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.32    ccd32       6       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.33    ccd33       7       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.34    ccd34       8       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.35    ccd35       9       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.36    ccd36       0       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.37    ccd37      10       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.38    ccd38       0       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.39    ccd39      10       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+
+# can we use header to find biassec
+USE_BIASSEC 1
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_mef.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_mef.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_mef.config	(revision 22322)
@@ -0,0 +1,308 @@
+# The spliced MecaCam data is stored in single extensions for each chip
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m # added at start of 03Am04
+	DETECTOR	STR	MegaCam
+	NAXIS           S32     0
+	EXTEND		BOOL	T
+        HISTORY         STR     imsplice: Splicing results in all detectors as if read from A amplifier
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CHIP	# The extensions represent chips
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier
+	# CHIP.NAME	STR	EXTNAME	# An extension keyword for unique identifie
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chipName:chipType
+	ccd00		STR	ccd00:CCD42-90
+	ccd01		STR	ccd01:CCD42-90
+	ccd02		STR	ccd02:CCD42-90
+	ccd03		STR	ccd03:CCD42-90
+	ccd04		STR	ccd04:CCD42-90
+	ccd05		STR	ccd05:CCD42-90
+	ccd06		STR	ccd06:CCD42-90
+	ccd07		STR	ccd07:CCD42-90
+	ccd08		STR	ccd08:CCD42-90
+	ccd09		STR	ccd09:CCD42-90
+	ccd10		STR	ccd10:CCD42-90
+	ccd11		STR	ccd11:CCD42-90
+	ccd12		STR	ccd12:CCD42-90
+	ccd13		STR	ccd13:CCD42-90
+	ccd14		STR	ccd14:CCD42-90
+	ccd15		STR	ccd15:CCD42-90
+	ccd16		STR	ccd16:CCD42-90
+	ccd17		STR	ccd17:CCD42-90
+	ccd18		STR	ccd18:CCD42-90
+	ccd19		STR	ccd19:CCD42-90
+	ccd20		STR	ccd20:CCD42-90
+	ccd21		STR	ccd21:CCD42-90
+	ccd22		STR	ccd22:CCD42-90
+	ccd23		STR	ccd23:CCD42-90
+	ccd24		STR	ccd24:CCD42-90
+	ccd25		STR	ccd25:CCD42-90
+	ccd26		STR	ccd26:CCD42-90
+	ccd27		STR	ccd27:CCD42-90
+	ccd28		STR	ccd28:CCD42-90
+	ccd29		STR	ccd29:CCD42-90
+	ccd30		STR	ccd30:CCD42-90
+	ccd31		STR	ccd31:CCD42-90
+	ccd32		STR	ccd32:CCD42-90
+	ccd33		STR	ccd33:CCD42-90
+	ccd34		STR	ccd34:CCD42-90
+	ccd35		STR	ccd35:CCD42-90
+END
+
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	CCD42-90	STR	LeftAmp:left RightAmp:right
+END
+
+
+# Specify the cells
+CELLS		METADATA
+	# Cell type, concepts to use
+	left		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECA
+		CELL.TRIMSEC		STR	TSECA
+		CELL.X0			S32	0
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAINA
+	END
+
+	right		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECB
+		CELL.TRIMSEC		STR	TSECB
+		CELL.X0			S32	1024
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAINB
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTERID    STR     FILTER
+        FPA.FILTER      STR     FILTER
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBJECT
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+        FPA.POSANGLE    STR     ROTANGLE # added at start of 03Am04
+	FPA.FOCUS	STR	TELFOCUS # added at start of 03Am04
+	FPA.ALT		STR	TELALT	 # added at start of 03Am04
+	FPA.AZ		STR	TELAZ	 # added at start of 03Am04
+	FPA.TEMP	STR	DETTEM
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.READNOISE  STR     RDNOISE
+	CELL.TIME	STR	MJD-OBS
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	MEGACAM		# this value is used in ipprc.config to find the recipes
+	FPA.DETECTOR	        STR	MEGACAM
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4612
+	CELL.XSIZE		S32	1024
+	CELL.YSIZE		S32	4612
+	CELL.XWINDOW		S32	0
+	CELL.YWINDOW		S32	0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.SATURATION		F32	50000.0
+        CELL.BAD                F32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	0
+		ccd01	S32	2048
+		ccd02	S32	4096
+		ccd03	S32	6144
+		ccd04	S32	8192
+		ccd05	S32	10240
+		ccd06	S32	12288
+		ccd07	S32	14336
+		ccd08	S32	16384
+		ccd09	S32	0
+		ccd10	S32	2048
+		ccd11	S32	4096
+		ccd12	S32	6144
+		ccd13	S32	8192
+		ccd14	S32	10240
+		ccd15	S32	12288
+		ccd16	S32	14336
+		ccd17	S32	16384
+		ccd18	S32    -2048
+		ccd19	S32	0
+		ccd20	S32	2048 
+		ccd21	S32	4096 
+		ccd22	S32	6144 
+		ccd23	S32	8192 
+		ccd24	S32	10240
+		ccd25	S32	12288
+		ccd26	S32	14336
+		ccd27	S32    -2048
+		ccd28	S32	0
+		ccd29	S32	2048 
+		ccd30	S32	4096 
+		ccd31	S32	6144 
+		ccd32	S32	8192 
+		ccd33	S32	10240
+		ccd34	S32	12288
+		ccd35	S32	14336
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	18447
+		ccd01	S32	18447
+		ccd02	S32	18447
+		ccd03	S32	18447
+		ccd04	S32	18447
+		ccd05	S32	18447
+		ccd06	S32	18447
+		ccd07	S32	18447
+		ccd08	S32	18447
+		ccd09	S32	13835
+		ccd10	S32	13835
+		ccd11	S32	13835
+		ccd12	S32	13835
+		ccd13	S32	13835
+		ccd14	S32	13835
+		ccd15	S32	13835
+		ccd16	S32	13835
+		ccd17	S32	13835
+		ccd18	S32	4612
+		ccd19	S32	4612
+		ccd20	S32	4612
+		ccd21	S32	4612
+		ccd22	S32	4612
+		ccd23	S32	4612
+		ccd24	S32	4612
+		ccd25	S32	4612
+		ccd26	S32	4612
+		ccd27	S32	0
+		ccd28	S32	0
+		ccd29	S32	0
+		ccd30	S32	0
+		ccd31	S32	0
+		ccd32	S32	0
+		ccd33	S32	0
+		ccd34	S32	0
+		ccd35	S32	0
+	END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+END
+
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+#	CELL.GAIN	STR	SELECT gain FROM camera WHERE chip_id = '{CHIP.NAME}' AND cell_id = '{CELL.NAME}'
+END		
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_pre_03Am04_mef.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_pre_03Am04_mef.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_pre_03Am04_mef.config	(revision 22322)
@@ -0,0 +1,313 @@
+# The spliced MecaCam data is stored in single extensions for each chip
+
+# How to recognise this type
+RULE	METADATA
+	DETECTOR	STR	MegaCam
+	NAXIS           S32     0
+	EXTEND		BOOL	T
+        HISTORY         STR     imsplice: Splicing results in all detectors as if read from A amplifier
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CHIP	# The extensions represent chips
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier
+	# CHIP.NAME	STR	EXTNAME	# An extension keyword for unique identifie
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chipName:chipType
+	ccd00		STR	ccd00:CCD42-90
+	ccd01		STR	ccd01:CCD42-90
+	ccd02		STR	ccd02:CCD42-90
+	ccd03		STR	ccd03:CCD42-90
+	ccd04		STR	ccd04:CCD42-90
+	ccd05		STR	ccd05:CCD42-90
+	ccd06		STR	ccd06:CCD42-90
+	ccd07		STR	ccd07:CCD42-90
+	ccd08		STR	ccd08:CCD42-90
+	ccd09		STR	ccd09:CCD42-90
+	ccd10		STR	ccd10:CCD42-90
+	ccd11		STR	ccd11:CCD42-90
+	ccd12		STR	ccd12:CCD42-90
+	ccd13		STR	ccd13:CCD42-90
+	ccd14		STR	ccd14:CCD42-90
+	ccd15		STR	ccd15:CCD42-90
+	ccd16		STR	ccd16:CCD42-90
+	ccd17		STR	ccd17:CCD42-90
+	ccd18		STR	ccd18:CCD42-90
+	ccd19		STR	ccd19:CCD42-90
+	ccd20		STR	ccd20:CCD42-90
+	ccd21		STR	ccd21:CCD42-90
+	ccd22		STR	ccd22:CCD42-90
+	ccd23		STR	ccd23:CCD42-90
+	ccd24		STR	ccd24:CCD42-90
+	ccd25		STR	ccd25:CCD42-90
+	ccd26		STR	ccd26:CCD42-90
+	ccd27		STR	ccd27:CCD42-90
+	ccd28		STR	ccd28:CCD42-90
+	ccd29		STR	ccd29:CCD42-90
+	ccd30		STR	ccd30:CCD42-90
+	ccd31		STR	ccd31:CCD42-90
+	ccd32		STR	ccd32:CCD42-90
+	ccd33		STR	ccd33:CCD42-90
+	ccd34		STR	ccd34:CCD42-90
+	ccd35		STR	ccd35:CCD42-90
+END
+
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	CCD42-90	STR	LeftAmp:left RightAmp:right
+END
+
+
+# Specify the cells
+CELLS		METADATA
+	# Cell type, concepts to use
+	left		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECA
+		CELL.TRIMSEC		STR	TSECA
+		CELL.X0			S32	0
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAINA
+	END
+
+	right		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECB
+		CELL.TRIMSEC		STR	TSECB
+		CELL.X0			S32	1024
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAINB
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTERID    STR     FILTER
+        FPA.FILTER      STR     FILTER
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBJECT
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.TEMP	STR	DETTEM
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.READNOISE  STR     RDNOISE
+	CELL.TIME	STR	MJD-OBS
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	MEGACAM		# this value is used in ipprc.config to find the recipes
+	FPA.DETECTOR	        STR	MEGACAM
+	FPA.POSANGLE		F32	0.0     # not reported until 03Am04
+	FPA.FOCUS		F32	0.0     # not reported until 03Am04
+	FPA.ALT			F32	0.0     # not reported until 03Am04
+	FPA.AZ			F32	0.0     # not reported until 03Am04
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4612
+	CELL.XSIZE		S32	1024
+	CELL.YSIZE		S32	4612
+	CELL.XWINDOW		S32	0
+	CELL.YWINDOW		S32	0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.SATURATION		F32	50000.0
+        CELL.BAD                F32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	0
+		ccd01	S32	2048
+		ccd02	S32	4096
+		ccd03	S32	6144
+		ccd04	S32	8192
+		ccd05	S32	10240
+		ccd06	S32	12288
+		ccd07	S32	14336
+		ccd08	S32	16384
+		ccd09	S32	0
+		ccd10	S32	2048
+		ccd11	S32	4096
+		ccd12	S32	6144
+		ccd13	S32	8192
+		ccd14	S32	10240
+		ccd15	S32	12288
+		ccd16	S32	14336
+		ccd17	S32	16384
+		ccd18	S32    -2048
+		ccd19	S32	0
+		ccd20	S32	2048 
+		ccd21	S32	4096 
+		ccd22	S32	6144 
+		ccd23	S32	8192 
+		ccd24	S32	10240
+		ccd25	S32	12288
+		ccd26	S32	14336
+		ccd27	S32    -2048
+		ccd28	S32	0
+		ccd29	S32	2048 
+		ccd30	S32	4096 
+		ccd31	S32	6144 
+		ccd32	S32	8192 
+		ccd33	S32	10240
+		ccd34	S32	12288
+		ccd35	S32	14336
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	18447
+		ccd01	S32	18447
+		ccd02	S32	18447
+		ccd03	S32	18447
+		ccd04	S32	18447
+		ccd05	S32	18447
+		ccd06	S32	18447
+		ccd07	S32	18447
+		ccd08	S32	18447
+		ccd09	S32	13835
+		ccd10	S32	13835
+		ccd11	S32	13835
+		ccd12	S32	13835
+		ccd13	S32	13835
+		ccd14	S32	13835
+		ccd15	S32	13835
+		ccd16	S32	13835
+		ccd17	S32	13835
+		ccd18	S32	4612
+		ccd19	S32	4612
+		ccd20	S32	4612
+		ccd21	S32	4612
+		ccd22	S32	4612
+		ccd23	S32	4612
+		ccd24	S32	4612
+		ccd25	S32	4612
+		ccd26	S32	4612
+		ccd27	S32	0
+		ccd28	S32	0
+		ccd29	S32	0
+		ccd30	S32	0
+		ccd31	S32	0
+		ccd32	S32	0
+		ccd33	S32	0
+		ccd34	S32	0
+		ccd35	S32	0
+	END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+END
+
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_pre_03Am04_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_pre_03Am04_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_pre_03Am04_raw.config	(revision 22322)
@@ -0,0 +1,347 @@
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The raw MegaCam data comes off the telescope with each of the chips stored in extensions of a MEF file.
+
+# How to identify this type
+RULE	METADATA
+	DETECTOR	STR	MegaCam
+	NAXIS           S32     0
+	EXTEND		BOOL	T
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CELL	# The extensions represent cells
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:type
+	amp00		STR	ccd00:LeftAmp:left
+	amp01		STR	ccd00:RightAmp:right
+	amp02		STR	ccd01:LeftAmp:left
+	amp03		STR	ccd01:RightAmp:right
+	amp04		STR	ccd02:LeftAmp:left
+	amp05		STR	ccd02:RightAmp:right
+	amp06		STR	ccd03:LeftAmp:left
+	amp07		STR	ccd03:RightAmp:right
+	amp08		STR	ccd04:LeftAmp:left
+	amp09		STR	ccd04:RightAmp:right
+	amp10		STR	ccd05:LeftAmp:left
+	amp11		STR	ccd05:RightAmp:right
+	amp12		STR	ccd06:LeftAmp:left
+	amp13		STR	ccd06:RightAmp:right
+	amp14		STR	ccd07:LeftAmp:left
+	amp15		STR	ccd07:RightAmp:right
+	amp16		STR	ccd08:LeftAmp:left
+	amp17		STR	ccd08:RightAmp:right
+	amp18		STR	ccd09:LeftAmp:left
+	amp19		STR	ccd09:RightAmp:right
+	amp20		STR	ccd10:LeftAmp:left
+	amp21		STR	ccd10:RightAmp:right
+	amp22		STR	ccd11:LeftAmp:left
+	amp23		STR	ccd11:RightAmp:right
+	amp24		STR	ccd12:LeftAmp:left
+	amp25		STR	ccd12:RightAmp:right
+	amp26		STR	ccd13:LeftAmp:left
+	amp27		STR	ccd13:RightAmp:right
+	amp28		STR	ccd14:LeftAmp:left
+	amp29		STR	ccd14:RightAmp:right
+	amp30		STR	ccd15:LeftAmp:left
+	amp31		STR	ccd15:RightAmp:right
+	amp32		STR	ccd16:LeftAmp:left
+	amp33		STR	ccd16:RightAmp:right
+	amp34		STR	ccd17:LeftAmp:left
+	amp35		STR	ccd17:RightAmp:right
+	amp36		STR	ccd18:LeftAmp:left
+	amp37		STR	ccd18:RightAmp:right
+	amp38		STR	ccd19:LeftAmp:left
+	amp39		STR	ccd19:RightAmp:right
+	amp40		STR	ccd20:LeftAmp:left
+	amp41		STR	ccd20:RightAmp:right
+	amp42		STR	ccd21:LeftAmp:left
+	amp43		STR	ccd21:RightAmp:right
+	amp44		STR	ccd22:LeftAmp:left
+	amp45		STR	ccd22:RightAmp:right
+	amp46		STR	ccd23:LeftAmp:left
+	amp47		STR	ccd23:RightAmp:right
+	amp48		STR	ccd24:LeftAmp:left
+	amp49		STR	ccd24:RightAmp:right
+	amp50		STR	ccd25:LeftAmp:left
+	amp51		STR	ccd25:RightAmp:right
+	amp52		STR	ccd26:LeftAmp:left
+	amp53		STR	ccd26:RightAmp:right
+	amp54		STR	ccd27:LeftAmp:left
+	amp55		STR	ccd27:RightAmp:right
+	amp56		STR	ccd28:LeftAmp:left
+	amp57		STR	ccd28:RightAmp:right
+	amp58		STR	ccd29:LeftAmp:left
+	amp59		STR	ccd29:RightAmp:right
+	amp60		STR	ccd30:LeftAmp:left
+	amp61		STR	ccd30:RightAmp:right
+	amp62		STR	ccd31:LeftAmp:left
+	amp63		STR	ccd31:RightAmp:right
+	amp64		STR	ccd32:LeftAmp:left
+	amp65		STR	ccd32:RightAmp:right
+	amp66		STR	ccd33:LeftAmp:left
+	amp67		STR	ccd33:RightAmp:right
+	amp68		STR	ccd34:LeftAmp:left
+	amp69		STR	ccd34:RightAmp:right
+	amp70		STR	ccd35:LeftAmp:left
+	amp71		STR	ccd35:RightAmp:right
+END
+
+# Specify the cell data
+CELLS	METADATA
+	left	METADATA	# Left amplifier
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XPARITY		S32	1 # We could have specified this as a DEFAULT, but this works
+		CELL.X0			S32	1
+		CELL.Y0			S32	1
+	END
+	right	METADATA	# Right amplifier
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XPARITY		S32	-1 # This cell is read out in the opposite direction
+		CELL.X0			S32	2048
+		CELL.Y0			S32	1
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS		STR	AIRMASS
+	FPA.FILTERID		STR	FILTER
+	FPA.FILTER		STR	FILTER
+	FPA.OBSTYPE		STR	OBSTYPE
+	FPA.OBJECT		STR	OBJECT
+	FPA.TIME		STR	MJD-OBS
+	FPA.TIMESYS		STR	TIMESYS
+	FPA.RA			STR	RA
+	FPA.DEC			STR	DEC
+	FPA.RADECSYS		STR	RADECSYS
+	FPA.TEMP		STR	DETTEM
+	FPA.EXPOSURE		STR	EXPTIME
+	CHIP.TEMP		STR	DETTEM
+	CELL.EXPOSURE		STR	EXPTIME
+	CELL.DARKTIME		STR	DARKTIME
+	CELL.GAIN		STR	GAIN     # only in raw; replaced by GAINA, GAINB in mef
+	CELL.READNOISE		STR	RDNOISE
+	CELL.TIME		STR	MJD-OBS
+	CELL.TIMESYS		STR	TIMESYS
+	CELL.XBIN		STR	CCDBIN1
+	CELL.YBIN		STR	CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	MEGACAM
+	FPA.DETECTOR		STR	MEGACAM
+	FPA.POSANGLE		F32	0.0     # not reported until 03Am04
+	FPA.FOCUS		F32	0.0     # not reported until 03Am04
+	FPA.ALT			F32	0.0     # not reported until 03Am04
+	FPA.AZ			F32	0.0     # not reported until 03Am04
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4612
+	CELL.XSIZE		S32	1024
+	CELL.YSIZE		S32	4612
+	CELL.XWINDOW		S32	0
+	CELL.YWINDOW		S32	0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.SATURATION		F32	50000.0
+	CELL.BAD		F32	0
+	CELL.YPARITY		S32	1
+
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	0
+		ccd01	S32	2048
+		ccd02	S32	4096
+		ccd03	S32	6144
+		ccd04	S32	8192
+		ccd05	S32	10240
+		ccd06	S32	12288
+		ccd07	S32	14336
+		ccd08	S32	16384
+		ccd09	S32	0
+		ccd10	S32	2048
+		ccd11	S32	4096
+		ccd12	S32	6144
+		ccd13	S32	8192
+		ccd14	S32	10240
+		ccd15	S32	12288
+		ccd16	S32	14336
+		ccd17	S32	16384
+		ccd18	S32    -2048
+		ccd19	S32	0    
+		ccd20	S32	2048 
+		ccd21	S32	4096 
+		ccd22	S32	6144 
+		ccd23	S32	8192 
+		ccd24	S32	10240
+		ccd25	S32	12288
+		ccd26	S32	14336
+		ccd27	S32    -2048
+		ccd28	S32	0    
+		ccd29	S32	2048 
+		ccd30	S32	4096 
+		ccd31	S32	6144 
+		ccd32	S32	8192 
+		ccd33	S32	10240
+		ccd34	S32	12288
+		ccd35	S32	14336
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	18447
+		ccd01	S32	18447
+		ccd02	S32	18447
+		ccd03	S32	18447
+		ccd04	S32	18447
+		ccd05	S32	18447
+		ccd06	S32	18447
+		ccd07	S32	18447
+		ccd08	S32	18447
+		ccd09	S32	13835
+		ccd10	S32	13835
+		ccd11	S32	13835
+		ccd12	S32	13835
+		ccd13	S32	13835
+		ccd14	S32	13835
+		ccd15	S32	13835
+		ccd16	S32	13835
+		ccd17	S32	13835
+		ccd18	S32	4612
+		ccd19	S32	4612
+		ccd20	S32	4612
+		ccd21	S32	4612
+		ccd22	S32	4612
+		ccd23	S32	4612
+		ccd24	S32	4612
+		ccd25	S32	4612
+		ccd26	S32	4612
+		ccd27	S32	0
+		ccd28	S32	0
+		ccd29	S32	0
+		ccd30	S32	0
+		ccd31	S32	0
+		ccd32	S32	0
+		ccd33	S32	0
+		ccd34	S32	0
+		ccd35	S32	0
+	END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	FPA.BIAS	METADATA
+#		TABLE	STR	Camera
+#		COLUMN	STR	gain
+#		chipId	STR	{CHIP.NAME}
+#		cellId	STR	{CELL.NAME}
+#		time	STR	{CELL.TIME}
+#	END
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+	CELL.X0		STR	FORTRAN
+	CELL.Y0		STR	FORTRAN
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_raw.config	(revision 22322)
@@ -0,0 +1,348 @@
+# "mcshort" is a MegaCam camera with only the central six chips --- it's faster than the entire FPA.
+# The raw MegaCam data comes off the telescope with each of the chips stored in extensions of a MEF file.
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m # added at start of 03Am04
+	DETECTOR	STR	MegaCam
+	NAXIS           S32     0
+	EXTEND		BOOL	T
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CELL	# The extensions represent cells
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:type
+	amp00		STR	ccd00:LeftAmp:left
+	amp01		STR	ccd00:RightAmp:right
+	amp02		STR	ccd01:LeftAmp:left
+	amp03		STR	ccd01:RightAmp:right
+	amp04		STR	ccd02:LeftAmp:left
+	amp05		STR	ccd02:RightAmp:right
+	amp06		STR	ccd03:LeftAmp:left
+	amp07		STR	ccd03:RightAmp:right
+	amp08		STR	ccd04:LeftAmp:left
+	amp09		STR	ccd04:RightAmp:right
+	amp10		STR	ccd05:LeftAmp:left
+	amp11		STR	ccd05:RightAmp:right
+	amp12		STR	ccd06:LeftAmp:left
+	amp13		STR	ccd06:RightAmp:right
+	amp14		STR	ccd07:LeftAmp:left
+	amp15		STR	ccd07:RightAmp:right
+	amp16		STR	ccd08:LeftAmp:left
+	amp17		STR	ccd08:RightAmp:right
+	amp18		STR	ccd09:LeftAmp:left
+	amp19		STR	ccd09:RightAmp:right
+	amp20		STR	ccd10:LeftAmp:left
+	amp21		STR	ccd10:RightAmp:right
+	amp22		STR	ccd11:LeftAmp:left
+	amp23		STR	ccd11:RightAmp:right
+	amp24		STR	ccd12:LeftAmp:left
+	amp25		STR	ccd12:RightAmp:right
+	amp26		STR	ccd13:LeftAmp:left
+	amp27		STR	ccd13:RightAmp:right
+	amp28		STR	ccd14:LeftAmp:left
+	amp29		STR	ccd14:RightAmp:right
+	amp30		STR	ccd15:LeftAmp:left
+	amp31		STR	ccd15:RightAmp:right
+	amp32		STR	ccd16:LeftAmp:left
+	amp33		STR	ccd16:RightAmp:right
+	amp34		STR	ccd17:LeftAmp:left
+	amp35		STR	ccd17:RightAmp:right
+	amp36		STR	ccd18:LeftAmp:left
+	amp37		STR	ccd18:RightAmp:right
+	amp38		STR	ccd19:LeftAmp:left
+	amp39		STR	ccd19:RightAmp:right
+	amp40		STR	ccd20:LeftAmp:left
+	amp41		STR	ccd20:RightAmp:right
+	amp42		STR	ccd21:LeftAmp:left
+	amp43		STR	ccd21:RightAmp:right
+	amp44		STR	ccd22:LeftAmp:left
+	amp45		STR	ccd22:RightAmp:right
+	amp46		STR	ccd23:LeftAmp:left
+	amp47		STR	ccd23:RightAmp:right
+	amp48		STR	ccd24:LeftAmp:left
+	amp49		STR	ccd24:RightAmp:right
+	amp50		STR	ccd25:LeftAmp:left
+	amp51		STR	ccd25:RightAmp:right
+	amp52		STR	ccd26:LeftAmp:left
+	amp53		STR	ccd26:RightAmp:right
+	amp54		STR	ccd27:LeftAmp:left
+	amp55		STR	ccd27:RightAmp:right
+	amp56		STR	ccd28:LeftAmp:left
+	amp57		STR	ccd28:RightAmp:right
+	amp58		STR	ccd29:LeftAmp:left
+	amp59		STR	ccd29:RightAmp:right
+	amp60		STR	ccd30:LeftAmp:left
+	amp61		STR	ccd30:RightAmp:right
+	amp62		STR	ccd31:LeftAmp:left
+	amp63		STR	ccd31:RightAmp:right
+	amp64		STR	ccd32:LeftAmp:left
+	amp65		STR	ccd32:RightAmp:right
+	amp66		STR	ccd33:LeftAmp:left
+	amp67		STR	ccd33:RightAmp:right
+	amp68		STR	ccd34:LeftAmp:left
+	amp69		STR	ccd34:RightAmp:right
+	amp70		STR	ccd35:LeftAmp:left
+	amp71		STR	ccd35:RightAmp:right
+END
+
+# Specify the cell data
+CELLS	METADATA
+	left	METADATA	# Left amplifier
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XPARITY		S32	1 # We could have specified this as a DEFAULT, but this works
+		CELL.X0			S32	1
+		CELL.Y0			S32	1
+	END
+	right	METADATA	# Right amplifier
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XPARITY		S32	-1 # This cell is read out in the opposite direction
+		CELL.X0			S32	2048
+		CELL.Y0			S32	1
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS		STR	AIRMASS
+	FPA.FILTERID		STR	FILTER
+	FPA.FILTER		STR	FILTER
+	FPA.OBSTYPE		STR	OBSTYPE
+	FPA.OBJECT		STR	OBJECT
+	FPA.TIME		STR	MJD-OBS
+	FPA.TIMESYS		STR	TIMESYS
+	FPA.RA			STR	RA
+	FPA.DEC			STR	DEC
+	FPA.RADECSYS		STR	RADECSYS
+	FPA.POSANGLE		STR	ROTANGLE # added at start of 03Am04
+	FPA.FOCUS		STR	TELFOCUS # added at start of 03Am04
+	FPA.ALT			STR	TELALT   # added at start of 03Am04
+	FPA.AZ			STR	TELAZ    # added at start of 03Am04
+	FPA.TEMP		STR	DETTEM
+	FPA.EXPOSURE		STR	EXPTIME
+	CHIP.TEMP		STR	DETTEM
+	CELL.EXPOSURE		STR	EXPTIME
+	CELL.DARKTIME		STR	DARKTIME
+	CELL.GAIN		STR	GAIN     # only in raw; replaced by GAINA, GAINB in mef
+	CELL.READNOISE		STR	RDNOISE
+	CELL.TIME		STR	MJD-OBS
+	CELL.TIMESYS		STR	TIMESYS
+	CELL.XBIN		STR	CCDBIN1
+	CELL.YBIN		STR	CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	MEGACAM
+	FPA.DETECTOR		STR	MEGACAM
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4612
+	CELL.XSIZE		S32	1024
+	CELL.YSIZE		S32	4612
+	CELL.XWINDOW		S32	0
+	CELL.YWINDOW		S32	0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.SATURATION		F32	50000.0         # probably need to define this by cell
+	CELL.BAD		F32	0
+	CELL.YPARITY		S32	1
+
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	0
+		ccd01	S32	2048
+		ccd02	S32	4096
+		ccd03	S32	6144
+		ccd04	S32	8192
+		ccd05	S32	10240
+		ccd06	S32	12288
+		ccd07	S32	14336
+		ccd08	S32	16384
+		ccd09	S32	0
+		ccd10	S32	2048
+		ccd11	S32	4096
+		ccd12	S32	6144
+		ccd13	S32	8192
+		ccd14	S32	10240
+		ccd15	S32	12288
+		ccd16	S32	14336
+		ccd17	S32	16384
+		ccd18	S32    -2048
+		ccd19	S32	0    
+		ccd20	S32	2048 
+		ccd21	S32	4096 
+		ccd22	S32	6144 
+		ccd23	S32	8192 
+		ccd24	S32	10240
+		ccd25	S32	12288
+		ccd26	S32	14336
+		ccd27	S32    -2048
+		ccd28	S32	0    
+		ccd29	S32	2048 
+		ccd30	S32	4096 
+		ccd31	S32	6144 
+		ccd32	S32	8192 
+		ccd33	S32	10240
+		ccd34	S32	12288
+		ccd35	S32	14336
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	18447
+		ccd01	S32	18447
+		ccd02	S32	18447
+		ccd03	S32	18447
+		ccd04	S32	18447
+		ccd05	S32	18447
+		ccd06	S32	18447
+		ccd07	S32	18447
+		ccd08	S32	18447
+		ccd09	S32	13835
+		ccd10	S32	13835
+		ccd11	S32	13835
+		ccd12	S32	13835
+		ccd13	S32	13835
+		ccd14	S32	13835
+		ccd15	S32	13835
+		ccd16	S32	13835
+		ccd17	S32	13835
+		ccd18	S32	4612
+		ccd19	S32	4612
+		ccd20	S32	4612
+		ccd21	S32	4612
+		ccd22	S32	4612
+		ccd23	S32	4612
+		ccd24	S32	4612
+		ccd25	S32	4612
+		ccd26	S32	4612
+		ccd27	S32	0
+		ccd28	S32	0
+		ccd29	S32	0
+		ccd30	S32	0
+		ccd31	S32	0
+		ccd32	S32	0
+		ccd33	S32	0
+		ccd34	S32	0
+		ccd35	S32	0
+	END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	FPA.BIAS	METADATA
+#		TABLE	STR	Camera
+#		COLUMN	STR	gain
+#		chipId	STR	{CHIP.NAME}
+#		cellId	STR	{CELL.NAME}
+#		time	STR	{CELL.TIME}
+#	END
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+	CELL.X0		STR	FORTRAN
+	CELL.Y0		STR	FORTRAN
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_split.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_split.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/format_split.config	(revision 22322)
@@ -0,0 +1,316 @@
+# The split MecaCam data is stored as single files for each chip, each a simple fits image
+
+# How to recognise this type
+RULE	METADATA
+	TELESCOP	STR	CFHT 3.6m
+	DETECTOR	STR	MegaCam
+	NAXIS           S32     2
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	CHIP	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# The extensions represent chips
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier
+	CONTENT		STR	EXTNAME	# Key to the CONTENTS menu
+	CONTENT.RULE	STR	{CHIP.NAME} # How to derive the CONTENT when writing
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Content name, chipType
+	ccd00		STR	ccd00:CCD42-90
+	ccd01		STR	ccd01:CCD42-90
+	ccd02		STR	ccd02:CCD42-90
+	ccd03		STR	ccd03:CCD42-90
+	ccd04		STR	ccd04:CCD42-90
+	ccd05		STR	ccd05:CCD42-90
+	ccd06		STR	ccd06:CCD42-90
+	ccd07		STR	ccd07:CCD42-90
+	ccd08		STR	ccd08:CCD42-90
+	ccd09		STR	ccd09:CCD42-90
+	ccd10		STR	ccd10:CCD42-90
+	ccd11		STR	ccd11:CCD42-90
+	ccd12		STR	ccd12:CCD42-90
+	ccd13		STR	ccd13:CCD42-90
+	ccd14		STR	ccd14:CCD42-90
+	ccd15		STR	ccd15:CCD42-90
+	ccd16		STR	ccd16:CCD42-90
+	ccd17		STR	ccd17:CCD42-90
+	ccd18		STR	ccd18:CCD42-90
+	ccd19		STR	ccd19:CCD42-90
+	ccd20		STR	ccd20:CCD42-90
+	ccd21		STR	ccd21:CCD42-90
+	ccd22		STR	ccd22:CCD42-90
+	ccd23		STR	ccd23:CCD42-90
+	ccd24		STR	ccd24:CCD42-90
+	ccd25		STR	ccd25:CCD42-90
+	ccd26		STR	ccd26:CCD42-90
+	ccd27		STR	ccd27:CCD42-90
+	ccd28		STR	ccd28:CCD42-90
+	ccd29		STR	ccd29:CCD42-90
+	ccd30		STR	ccd30:CCD42-90
+	ccd31		STR	ccd31:CCD42-90
+	ccd32		STR	ccd32:CCD42-90
+	ccd33		STR	ccd33:CCD42-90
+	ccd34		STR	ccd34:CCD42-90
+	ccd35		STR	ccd35:CCD42-90
+END
+
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	CCD42-90	STR	LeftAmp:left RightAmp:right
+END
+
+
+# Specify the cells
+CELLS		METADATA
+	# Cell type, concepts for this type
+	left		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECA
+		CELL.TRIMSEC		STR	TSECA
+		CELL.X0			S32	0
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAINA
+	END
+
+	right		METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BSECB
+		CELL.TRIMSEC		STR	TSECB
+		CELL.X0			S32	1024
+		CELL.GAIN.SOURCE	STR	HEADER
+		CELL.GAIN		STR	GAINB
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+        FPA.OBS        STR     EXPNUM
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTER      STR     FILTER
+        FPA.FILTERID    STR     FILTER
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBJECT
+	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	TELALT
+	FPA.AZ		STR	TELAZ
+	FPA.TEMP	STR	DETTEM
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+        CELL.READNOISE  STR     RDNOISE
+	CELL.TIME	STR	MJD-OBS
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	MEGACAM
+	FPA.DETECTOR	        STR	MEGACAM
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4612
+	CELL.XSIZE		S32	1024
+	CELL.YSIZE		S32	4612
+	CELL.XWINDOW		S32	0
+	CELL.YWINDOW		S32	0
+        CELL.SATURATION         S32     60000
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.SATURATION		F32	50000.0
+        CELL.BAD                F32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	0
+		ccd01	S32	2048
+		ccd02	S32	4096
+		ccd03	S32	6144
+		ccd04	S32	8192
+		ccd05	S32	10240
+		ccd06	S32	12288
+		ccd07	S32	14336
+		ccd08	S32	16384
+		ccd09	S32	0
+		ccd10	S32	2048
+		ccd11	S32	4096
+		ccd12	S32	6144
+		ccd13	S32	8192
+		ccd14	S32	10240
+		ccd15	S32	12288
+		ccd16	S32	14336
+		ccd17	S32	16384
+		ccd18	S32    -2048
+		ccd19	S32	0    
+		ccd20	S32	2048 
+		ccd21	S32	4096 
+		ccd22	S32	6144 
+		ccd23	S32	8192 
+		ccd24	S32	10240
+		ccd25	S32	12288
+		ccd26	S32	14336
+		ccd27	S32    -2048
+		ccd28	S32	0    
+		ccd29	S32	2048 
+		ccd30	S32	4096 
+		ccd31	S32	6144 
+		ccd32	S32	8192 
+		ccd33	S32	10240
+		ccd34	S32	12288
+		ccd35	S32	14336
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	18447
+		ccd01	S32	18447
+		ccd02	S32	18447
+		ccd03	S32	18447
+		ccd04	S32	18447
+		ccd05	S32	18447
+		ccd06	S32	18447
+		ccd07	S32	18447
+		ccd08	S32	18447
+		ccd09	S32	13835
+		ccd10	S32	13835
+		ccd11	S32	13835
+		ccd12	S32	13835
+		ccd13	S32	13835
+		ccd14	S32	13835
+		ccd15	S32	13835
+		ccd16	S32	13835
+		ccd17	S32	13835
+		ccd18	S32	4612
+		ccd19	S32	4612
+		ccd20	S32	4612
+		ccd21	S32	4612
+		ccd22	S32	4612
+		ccd23	S32	4612
+		ccd24	S32	4612
+		ccd25	S32	4612
+		ccd26	S32	4612
+		ccd27	S32	0
+		ccd28	S32	0
+		ccd29	S32	0
+		ccd30	S32	0
+		ccd31	S32	0
+		ccd32	S32	0
+		ccd33	S32	0
+		ccd34	S32	0
+		ccd35	S32	0
+	END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	-1
+		ccd01	S32	-1
+		ccd02	S32	-1
+		ccd03	S32	-1
+		ccd04	S32	-1
+		ccd05	S32	-1
+		ccd06	S32	-1
+		ccd07	S32	-1
+		ccd08	S32	-1
+		ccd09	S32	-1
+		ccd10	S32	-1
+		ccd11	S32	-1
+		ccd12	S32	-1
+		ccd13	S32	-1
+		ccd14	S32	-1
+		ccd15	S32	-1
+		ccd16	S32	-1
+		ccd17	S32	-1
+		ccd18	S32	1
+		ccd19	S32	1
+		ccd20	S32	1
+		ccd21	S32	1
+		ccd22	S32	1
+		ccd23	S32	1
+		ccd24	S32	1
+		ccd25	S32	1
+		ccd26	S32	1
+		ccd27	S32	1
+		ccd28	S32	1
+		ccd29	S32	1
+		ccd30	S32	1
+		ccd31	S32	1
+		ccd32	S32	1
+		ccd33	S32	1
+		ccd34	S32	1
+		ccd35	S32	1
+	END
+END
+
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/ppImage.config	(revision 22322)
@@ -0,0 +1,169 @@
+### ppImage recipe configuration file for Megacam
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+OVERSCAN.FIT		STR	NONE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	0		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# for a test, turn off selected detrend types
+OVERSCAN	BOOL	FALSE		# Overscan subtraction
+BIAS		BOOL	FALSE		# Bias subtraction
+DARK		BOOL	TRUE		# Dark subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+
+# binned output image options
+BIN1.XBIN               S32     18
+BIN1.YBIN               S32     18
+BIN2.XBIN               S32     144
+BIN2.YBIN               S32     144
+
+# megacam needs fringe subtraction
+FRINGE		BOOL	TRUE		# Fringe subtraction
+
+# this table lists extra constraints which should be applied when
+# selecting the detrend images.
+# note: camera and time are always applied
+
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+#   EXPTIME STR FPA.EXPOSURE
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+#   DETTYPE STR DOMEFLAT.RAW
+#   VERSION STR SKYFLAT.RAW
+#   VERSION STR DOMEFLAT.CORR.XX
+#   VERSION STR SKYFLAT.CORR
+  END
+  FLAT_CORRECTION METADATA
+    FILTER   STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+#   AIRMASS  STR FPA.AIRMASS
+#   TWILIGHT STR FPA.TWILIGHT
+  END
+  SHUTTER METADATA
+  END
+  MASK METADATA
+  END	
+END
+
+# only apply the fringe for these filters
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS	STR i
+FRINGE.FILTERS	STR z
+
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS       METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # 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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR     METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/ppMerge.config	(revision 22322)
@@ -0,0 +1,40 @@
+
+# Bias combination --- don't want min/max rejection
+PPMERGE_BIAS	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
+   WEIGHTS	BOOL	FALSE		# Use image weights?
+   COMBINE	STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Dark combination --- don't want min/max rejection
+# More aggressive clipping than bias, so as to remove CRs
+PPMERGE_DARK	METADATA
+  REJ		F32	2.0		# Rejection threshold (sigma)
+  ITER		S32	4		# 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
+  WEIGHTS	BOOL	FALSE		# Use image weights?
+  COMBINE	STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Flat combination --- use min/max rejection
+PPMERGE_FLAT	METADATA
+	REJ		F32	3.0		# Rejection threshold (sigma)
+	ITER		S32	1		# Number of rejection iterations
+	FRACHIGH	F32	0.3		# Fraction of high pixels to reject immediately
+	FRACLOW		F32	0.1		# Fraction of low pixels to reject immediately
+	NKEEP		S32	5		# Minimum number of pixels in stack to keep
+	WEIGHTS		BOOL	FALSE		# Use image weights?
+	COMBINE		STR	MEAN		# Statistic to use for combination: 
+END
+
+
+# Fringe combination --- already included in default, above
+PPMERGE_FRINGE	METADATA
+	FRACHIGH	F32	0.1		# Fraction of high pixels to reject immediately
+	WEIGHTS		BOOL	FALSE		# Use image weights?
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/psastro.config	(revision 22322)
@@ -0,0 +1,74 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+PSASTRO.PIXEL.SCALE    F32  1.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32    2500.
+PSASTRO.GRID.SCALE     F32      50.
+
+# extra field for ref stars:
+PSASTRO.FIELD.PADDING  F32 1.0
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# single-chip radius match in pixels
+PSASTRO.MATCH.FIT.NITER S32   3
+PSASTRO.MATCH.RADIUS    F32   12.0
+PSASTRO.MATCH.RADIUS.N0 F32   15.0
+PSASTRO.MATCH.RADIUS.N1 F32   10.0
+PSASTRO.MATCH.RADIUS.N2 F32    5.0
+
+PSASTRO.MAX.NRAW       S32      1500   # max stars accepted for fitting (0 for all)
+PSASTRO.MAX.NREF       S32      1500   # max stars accepted for fitting (0 for all)
+
+PSASTRO.MIN.INST.MAG.RAW       F32      -15.5  # min instrumental magnitude for stars accepted for fitting
+PSASTRO.MAX.INST.MAG.RAW       F32       -8.0  # max instrumental magnitude for stars accepted for fitting
+
+PSASTRO.MAX.ERROR      F32      10.0 # max error in pixels
+PSASTRO.MIN.NSTAR      S32      10   # min fitted stars in solution
+
+PSASTRO.MOSAIC.MODE           BOOL     FALSE
+
+PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+PSASTRO.MOSAIC.RADIUS.N0    F32    10.0
+PSASTRO.MOSAIC.RADIUS.N1    F32     8.0 # do not refine the match
+PSASTRO.MOSAIC.RADIUS.N2    F32     8.0 # do not refine the match
+PSASTRO.MOSAIC.RADIUS.N3    F32     4.0 
+
+PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      3 # fit order (-1 means use default)
+
+PSASTRO.MOSAIC.GRADIENT.NX    S32      2   # number of x-dir cells per chip
+PSASTRO.MOSAIC.GRADIENT.NY    S32      4   # number of y-dir cells per chip
+
+# use this recipe to set a tight constraint
+PSASTRO.FINE METADATA
+  PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+  PSASTRO.MOSAIC.RADIUS.N0    F32    8.0
+  PSASTRO.MOSAIC.RADIUS.N1    F32    0.0
+  PSASTRO.MOSAIC.RADIUS.N2    F32    4.0
+  PSASTRO.MOSAIC.RADIUS.N3    F32    4.0
+
+  PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      1 # fit order (-1 means use default)
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/psphot.config	(revision 22322)
@@ -0,0 +1,43 @@
+
+# turn these on to see specific outputs
+SAVE.OUTPUT	BOOL 	TRUE
+SAVE.BACKMDL	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+SAVE.PLOTS     	BOOL    TRUE
+
+BACKGROUND.XBIN	    S32  128            # size of background superpixels
+BACKGROUND.YBIN	    S32  128            # size of background superpixels
+
+# image background parameters
+IMSTATS_NPIX        S32  10000    	 # number of pixels to use for sky estimate boxes:
+SKY_STAT            STR  FITTED_MEAN_V4  # statistic used to measure background
+SKY_CLIP_SIGMA      F32  2.0             # statistic used to measure background
+
+PSF_SN_LIM          F32  100             # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+# PSF model parameters : choose the PSF model desired
+PSF_MODEL           STR  PS_MODEL_QGAUSS
+
+MOMENTS_SN_MIN      F32   30.0
+EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+FULL_FIT_SN_LIM      F32  50.0
+AP_MIN_SN            F32  50.0
+
+# OUTPUT.FORMAT       STR SMPDATA
+OUTPUT.FORMAT       STR PS1_DEV_1
+
+PSF_SHAPE_NSIGMA     F32  3.0		 # max significance for shape variation
+PSF_MAX_CHI          F32  50.0		 # reject objects worse that this
+
+#PSF.TREND.MODE STR MAP
+#PSF.TREND.NX   S32 1
+#PSF.TREND.NY   S32 1
+
+DIAGNOSTIC.PLOTS		METADATA
+  IMAGE.BACKGROUND.CELL.HISTOGRAM   BOOL FALSE
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.X S32 -1
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.Y S32 12
+END
+
+USE_FOOTPRINTS                      BOOL  TRUE       	  # use new pmFootprint peak packaging
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/pswarp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/pswarp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/pswarp.config	(revision 22322)
@@ -0,0 +1,3 @@
+### Megacam recipe for pswarp
+
+ASTROM.SOURCE	STR	PSASTRO.OUTPUT	# Source file rule for astrometry, or NULL
Index: /tags/sj_tags/sj_root_20080929/ippconfig/megacam/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/megacam/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/megacam/rejections.config	(revision 22322)
@@ -0,0 +1,38 @@
+
+BIAS METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0
+  IMFILE.STDEV       F32 10.0
+  IMFILE.MEANSTDEV   F32  0.5
+  IMFILE.SKEWNESS    F32  0.0
+  IMFILE.KURTOSIS    F32  0.0
+  IMFILE.SNR         F32  0.0
+  IMFILE.BIN.STDEV   F32  0.5
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  0.5
+  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  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+# FILTER is an additional qualifier, and may be "*" (or absent!), in which case it matches everything
+# EXPECTED is the expected mean value
+# IMFILE.MEAN is the maximum permitted mean value for an imfile, relative to the standard deviation
+# IMFILE.STDEV is the maximum permitted standard deviation for an imfile
+# EXP.MEAN is the maximum permitted mean value for an exposure, relative to the standard deviation
+# EXP.STDEV is the maximum permitted standard deviation for an exposure
+# EXP.MEANSTDEV is the maximum permitted mean standard deviation for an exposure relative to the mean
+# ENSEMBLE.MEAN is the maximum permitted mean for an ensemble of exposures
+# ENSEMBLE.STDEV is the maximum permitted standard deviation for an ensemble of exposures
+# ENSEMBLE.MEANSTDEV is the maximum permitted mean standard deviation for an ensemble of exposures
+# IMFILE.SNR is the minimum permitted signal-to-noise for an imfile
+# EXP.SNR is the minimum permitted signal-to-noise for an exposure
+# These values (all except FILTER) may be zero, in which case no clipping is applied.
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/Makefile.am	(revision 22322)
@@ -0,0 +1,22 @@
+
+installdir = $(datadir)/ippconfig/mosaic2
+
+install_files = \
+	dvo.config \
+	dvo.layout \
+	camera.config \
+	format.config \
+	ppImage.config \
+        psastro.config \
+	psphot.config \
+	ppMerge.config \
+	rejections.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/camera.config	(revision 22322)
@@ -0,0 +1,104 @@
+# Camera configuration file for the CTIO MOSAIC2 : describes the camera
+
+# File formats that we know about
+FORMATS		METADATA
+    MEF		STR	mosaic2/format.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA	METADATA
+	ccd00	STR	LeftAmp RightAmp
+	ccd01	STR	LeftAmp RightAmp
+	ccd02	STR	LeftAmp RightAmp
+	ccd03	STR	LeftAmp RightAmp
+	ccd04	STR	LeftAmp RightAmp
+	ccd05	STR	LeftAmp RightAmp
+	ccd06	STR	LeftAmp RightAmp
+	ccd07	STR	LeftAmp RightAmp
+END
+
+# menu of possible filter names:  internal  STR  external name
+FILTER.ID       METADATA
+   VR MULTI
+   VR STR VR.Supermacho.c6027 
+   VR STR VR Supermacho c6027 
+   VR STR VR SuperMacho c6027 
+   R  STR R Harris c6004      
+   I  STR I c6028             
+   U  STR U c6001
+   B  STR B Harris c6002
+   V  STR V Harris c6026
+   # Minor filters 
+   g  MULTI
+   g  STR g SDSS c6017
+   g  STR SDSS g c6017
+   r  MULTI
+   r  STR SDSS r c6018
+   r  STR r SDSS c6018
+   i  MULTI
+   i  STR i SDSS c6019
+   i  STR SDSS i c6019
+   # Unused filters (only used in zeros, and therefore irrelevent)
+   NULL MULTI
+   NULL STR z SDSS c6020
+   NULL STR SDSS z c6020
+   NULL STR ha8 H-alpha+8nm c6011
+   NULL STR M Washington c6007
+   NULL STR u SDSS c6021
+   NULL STR VRr Supermacho c6027 
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	FPA	STR	fpa
+END
+
+DVO.CAMERADIR	STR	mosaic2		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES		METADATA
+	# Recipes for ppImage
+        PPIMAGE         STR     mosaic2/ppImage.config
+	PPMERGE		STR	mosaic2/ppMerge.config
+        PSPHOT          STR     mosaic2/psphot.config
+	REJECTIONS	STR	mosaic2/rejections.config	# Detrend rejection limits
+END
+
+# Reduction classes
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+# Set file rules appropriate for mef format (common to all mef cameras)
+FILERULES   STR   recipes/filerules-mef.mdc 
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR {CHIP.NAME}.hdr
+  CMF.DATA STR {CHIP.NAME}.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD  STR {CHIP.NAME}.hdr
+  PSF.TABLE STR {CHIP.NAME}.psf_model
+  PSF.RESID STR {CHIP.NAME}.psf_resid
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.N}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/dvo.config	(revision 22322)
@@ -0,0 +1,63 @@
+
+# location of DVO database tables
+CATDIR			/data/alala.0/eugene/isp/catdir
+
+# keywords used by DVO to interpret the headers
+
+# keyword abstractions:
+DATE-KEYWORD		DATE-OBS
+DATE-MODE		yyyy-mm-dd
+UT-KEYWORD		NONE
+JD-KEYWORD		NONE
+MJD-KEYWORD		MJD-OBS
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		0
+CAL_INSTMAG_MIN		0
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		1.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/dvo.layout
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/dvo.layout	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/dvo.layout	(revision 22322)
@@ -0,0 +1,29 @@
+# this file defines the layout of the mosaic imager:
+
+NCCD 12
+NAXIS1 2080
+NAXIS2 4128
+
+MOSAIC_X 6
+MOSAIC_Y 2
+
+# lines need to contain:
+
+CHIPID_KEYWORD CHIPID
+
+# ID  CHIPID xoffset yoffset xflip yflip datasec         biassec                 Xo    Yo     theta
+CCD.0  chip00      0       0     0     0 [6:2049,5:4100] [2050:2080,2:4128]     0.0    0.0   0.00
+CCD.1  chip01      1       0     0     0 [6:2049,5:4100] [2050:2080,2:4128]  2000.0    0.0   0.00
+CCD.2  chip02      2       0     1     0 [6:2049,5:4100] [2050:2080,2:4128]  6000.0    0.0   0.00
+CCD.3  chip03      3       0     1     0 [6:2049,5:4100] [2050:2080,2:4128]  8000.0    0.0   0.00
+CCD.4  chip04      4       0     0     0 [6:2049,5:4100] [2050:2080,2:4128]  8000.0    0.0   0.00
+CCD.5  chip05      5       0     0     0 [6:2049,5:4100] [2050:2080,2:4128] 10000.0    0.0   0.00
+CCD.6  chip06      0       1     0     1 [6:2049,5:4100] [2050:2080,2:4128]     0.0 8000.0   1.00
+CCD.7  chip07      1       1     0     1 [6:2049,5:4100] [2050:2080,2:4128]  2000.0 8000.0   0.00
+CCD.8  chip08      2       1     0     1 [6:2049,5:4100] [2050:2080,2:4128]  4000.0 8000.0   0.00
+CCD.9  chip09      3       1     0     1 [6:2049,5:4100] [2050:2080,2:4128]  6000.0 8000.0   0.00
+CCD.10 chip10      4       1     0     1 [6:2049,5:4100] [2050:2080,2:4128]  8000.0 8000.0   0.00
+CCD.11 chip11      5       1     0     1 [6:2049,5:4100] [2050:2080,2:4128] 10000.0 8000.0   0.00
+
+# can we use header to find biassec
+USE_BIASSEC 1
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/format.config	(revision 22322)
@@ -0,0 +1,162 @@
+# Format file for CTIO Mosaic2 camera used by ESSENCE
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	CTIO 4.0 meter telescope
+	DETECTOR	STR	Mosaic2
+	EXTEND		BOOL	T
+	NEXTEND		S32	16
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	 # The FITS file represents an entire FPA
+	EXTENSIONS	STR	CELL	 # The extensions represent cells
+	FPA.OBS	STR	FILENAME # A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:type
+	im1		STR	ccd00:LeftAmp:left
+	im2		STR	ccd00:RightAmp:right
+	im3		STR	ccd01:LeftAmp:left  
+	im4		STR	ccd01:RightAmp:right
+	im5		STR	ccd02:LeftAmp:left  
+	im6		STR	ccd02:RightAmp:right
+	im7		STR	ccd03:LeftAmp:left  
+	im8		STR	ccd03:RightAmp:right
+	im9		STR	ccd04:LeftAmp:left  
+	im10		STR	ccd04:RightAmp:right
+	im11		STR	ccd05:LeftAmp:left  
+	im12		STR	ccd05:RightAmp:right
+	im13		STR	ccd06:LeftAmp:left  
+	im14		STR	ccd06:RightAmp:right
+	im15		STR	ccd07:LeftAmp:left  
+	im16		STR	ccd07:RightAmp:right
+END
+
+# Specify the cell data
+CELLS	METADATA
+	left	METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.XPARITY		S32	1
+		CELL.X0			S32	0
+	END
+	right	METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.XPARITY		S32	1
+		CELL.X0			S32	1024
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS		STR	AIRMASS
+	FPA.FILTERID		STR	FILTER
+	FPA.FILTER		STR	FILTER
+	FPA.RA			STR	RA
+	FPA.DEC			STR	DEC
+	FPA.RADECSYS		STR	RADECSYS
+	FPA.OBSTYPE		STR	OBSTYPE
+	FPA.OBJECT		STR	OBJECT
+	FPA.TIME		STR	MJD-OBS
+	CHIP.TEMP		STR	CCDTEM
+	CELL.EXPOSURE		STR	EXPTIME
+	CELL.DARKTIME		STR	DARKTIME
+	CELL.GAIN		STR	GAIN
+	CELL.READNOISE		STR	RDNOISE
+	CELL.SATURATION		STR	SATURATE
+	CELL.TIME		STR	MJD-OBS
+	CELL.XBIN		STR	CCDSUM
+	CELL.YBIN		STR	CCDSUM
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+        FPA.TELESCOPE           STR     BLANCO
+        FPA.INSTRUMENT          STR     MOSAIC2
+        FPA.DETECTOR            STR     MOSAIC2
+	FPA.POSANGLE		F32	0.0
+	FPA.TIMESYS		STR	UTC		# Header says "UTC approximate"
+	FPA.ALT			F32	0.0		# Not present in header
+	FPA.AZ			F32	0.0		# Not present in header
+	CELL.TIMESYS		STR	UTC		# Header says "UTC approximate"
+	CELL.READDIR		S32	1		# Cell is read in x direction
+	CELL.BAD		S32	0
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		ccd00	S32	0
+		ccd01	S32	2048
+		ccd02	S32	4096
+		ccd03	S32	6144
+		ccd04	S32	0
+		ccd05	S32	2048
+		ccd06	S32	4096
+		ccd07	S32	6144
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		ccd00	S32	0
+		ccd01	S32	0
+		ccd02	S32	0
+		ccd03	S32	0
+		ccd04	S32	4096
+		ccd05	S32	4096
+		ccd06	S32	4096
+		ccd07	S32	4096
+	END
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		ccd00	S32	1
+		ccd01	S32	1
+		ccd02	S32	1
+		ccd03	S32	1
+		ccd04	S32	1
+		ccd05	S32	1
+		ccd06	S32	1
+		ccd07	S32	1
+	END
+
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	FPA.BIAS	METADATA
+#		TABLE	STR	Camera
+#		COLUMN	STR	gain
+#		chipId	STR	{CHIP.NAME}
+#		cellId	STR	{CELL.NAME}
+#		time	STR	{CELL.TIME}
+#	END
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+#	CHIP.X0		STR	FORTRAN
+#	CHIP.Y0		STR	FORTRAN
+#	CELL.X0		STR	FORTRAN
+#	CELL.Y0		STR	FORTRAN
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/ppImage.config	(revision 22322)
@@ -0,0 +1,150 @@
+### ppImage recipe configuration file for CTIO MOSAIC2
+
+# List of tasks to perform
+SHUTTER         BOOL    FALSE           # Shutter correction
+DARK		BOOL	TRUE		# Dark correction
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+CHIP.FITS       BOOL    TRUE            # Save base detrended image?
+BIN1.FITS	BOOL    TRUE            # Save 1st binned chip image?
+BIN2.FITS	BOOL    TRUE		# Save 2nd binned chip image?
+
+# binned output image options
+BIN1.XBIN               S32     8
+BIN1.YBIN               S32     8
+BIN2.XBIN               S32     64
+BIN2.YBIN               S32     64
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	3		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+#   EXPTIME STR FPA.EXPOSURE
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+#   DETTYPE STR DOMEFLAT.RAW
+#   VERSION STR SKYFLAT.RAW
+#   VERSION STR DOMEFLAT.CORR.XX
+#   VERSION STR SKYFLAT.CORR
+  END
+  FLAT_CORRECTION METADATA
+    FILTER   STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+#   AIRMASS  STR FPA.AIRMASS
+#   TWILIGHT STR FPA.TWILIGHT
+  END
+  SHUTTER METADATA
+  END
+  MASK METADATA
+  END	
+END
+
+####################################################################################
+# Need the following in order to turn off options from the site-level recipes...
+#
+#  ESSENCE data does not need a fringe or a shutter correction.
+####################################################################################
+
+# Dark subtraction only
+PPIMAGE_D         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           #  Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Shutter correction only
+PPIMAGE_S         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           #  Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark
+PPIMAGE_OBD      METADATA
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           #  Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS      METADATA
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           #  Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF     METADATA
+  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
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR    METADATA
+  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
+END
+
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photometry
+PPIMAGE_OBDSFRP   METADATA
+  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    TRUE            # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photometry & astrometry
+PPIMAGE_OBDSFRA   METADATA
+  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    TRUE            # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    TRUE            # Astrometry for mosaic?
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/ppMerge.config	(revision 22322)
@@ -0,0 +1,4 @@
+# Ordinates for fitting dark current
+DARK.ORDINATES	METADATA
+END
+DARK.NORM	STR	CELL.DARKTIME		# Dark normalisation concept
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/psastro.config	(revision 22322)
@@ -0,0 +1,76 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (mm / pixel)
+PSASTRO.PIXEL.SCALE    F32  1.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32   1000.0
+PSASTRO.GRID.SCALE     F32     50.0
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# single-chip radius match in pixels
+PSASTRO.MATCH.FIT.NITER S32   3
+PSASTRO.MATCH.RADIUS    F32   12.0
+PSASTRO.MATCH.RADIUS.N0 F32   15.0
+PSASTRO.MATCH.RADIUS.N1 F32   10.0
+PSASTRO.MATCH.RADIUS.N2 F32    5.0
+
+PSASTRO.MAX.NRAW       S32      1500   # max stars accepted for fitting (0 for all)
+PSASTRO.MAX.NREF       S32      1500   # max stars accepted for fitting (0 for all)
+
+PSASTRO.MIN.INST.MAG.RAW       F32      -17.0  # min instrumental magnitude for stars accepted for fitting
+PSASTRO.MAX.INST.MAG.RAW       F32       -8.0  # max instrumental magnitude for stars accepted for fitting
+
+PSASTRO.MAX.ERROR      F32      10.0 # max error in pixels
+PSASTRO.MIN.NSTAR      S32      10   # min fitted stars in solution
+
+PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+PSASTRO.MOSAIC.RADIUS.N0    F32   10.0 # initial match (after chip astrom)
+PSASTRO.MOSAIC.RADIUS.N1    F32    8.0 # (after first mosaic pass)
+PSASTRO.MOSAIC.RADIUS.N2    F32    8.0 # do not refine the match
+PSASTRO.MOSAIC.RADIUS.N3    F32    4.0 # 
+
+PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      3 # fit order (-1 means use default)
+
+PSASTRO.MOSAIC.GRADIENT.NX    S32     2   # number of x-dir cells per chip
+PSASTRO.MOSAIC.GRADIENT.NY    S32     4   # number of y-dir cells per chip
+
+PSASTRO.MOSAIC.MODE           BOOL     FALSE
+
+# use this recipe to set a tight constraint
+PSASTRO.FINE METADATA
+  PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+  PSASTRO.MOSAIC.RADIUS.N0    F32    8.0
+  PSASTRO.MOSAIC.RADIUS.N1    F32    0.0
+  PSASTRO.MOSAIC.RADIUS.N2    F32    4.0
+  PSASTRO.MOSAIC.RADIUS.N3    F32    4.0
+
+  PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      3 # fit order (-1 means use default)
+END
+
+PSASTRO.GRID.MIN.SCALE F32  0.98
+PSASTRO.GRID.MAX.SCALE F32  1.04
+PSASTRO.GRID.DEL.SCALE F32  0.02
+PSASTRO.GRID.MIN.SIGMA F32  5.0
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/psphot.config	(revision 22322)
@@ -0,0 +1,42 @@
+	
+# turn these on to see specific outputs
+SAVE.RESID	BOOL 	TRUE
+
+IMSTATS_NPIX        S32  1000    	 # number of pixels to use for sky estimate boxes:
+
+PSF_SN_LIM          F32  100             # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+# PSF model parameters : choose the PSF model
+# list as many PSF_MODEL options as desired
+# if you want to list multiple entries, uncomment 'MULTI' here
+# PSF_MODEL           MULTI
+PSF_MODEL           STR  PS_MODEL_QGAUSS
+# PSF_MODEL           STR  PS_MODEL_GAUSS
+# PSF_MODEL           STR  PS_MODEL_PGAUSS
+# PSF_MODEL           STR  PS_MODEL_TGAUSS  ## not well tested, not very successful
+
+MOMENTS_SN_MIN      F32  20.0           # min S/N to measure moments
+EXT_MIN_SN           F32  20.0           # fit galaxies above this S/N limit
+FULL_FIT_SN_LIM      F32  20.0
+AP_MIN_SN            F32  25.0
+
+USE_FOOTPRINTS                      BOOL  TRUE       	  # use new pmFootprint peak packaging
+
+## gene's overrides for testing:
+PEAKS_NSIGMA_LIMIT_2 F32  10.0 	   	 # peak significance threshold
+
+EXT_MODEL           STR  PS_MODEL_QGAUSS
+
+TEST_FIT            BOOL FALSE
+TEST_FIT_X          F32  781
+TEST_FIT_Y          F32  830
+
+TEST_FIT_MODE       STR  CONV
+#TEST_FIT_MODE       STR PSF
+#TEST_FIT_MODE       STR  EXT
+
+#TEST_FIT_MODEL      STR  PS_MODEL_SERSIC
+TEST_FIT_MODEL      STR  PS_MODEL_QGAUSS
+TEST_MOMENTS_RADIUS F32 9.0
+OUTPUT.FORMAT       STR PS1_DEV_1
Index: /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/mosaic2/rejections.config	(revision 22322)
@@ -0,0 +1,184 @@
+
+BIAS METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0
+  IMFILE.STDEV       F32 10.0
+  IMFILE.MEANSTDEV   F32  1.0
+  IMFILE.SKEWNESS    F32  0.0
+  IMFILE.KURTOSIS    F32  0.0
+  IMFILE.SNR         F32  0.0
+  IMFILE.BIN.STDEV   F32  0.5
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  0.5
+  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  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+DARK METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0
+  IMFILE.STDEV       F32 10.0
+  IMFILE.MEANSTDEV   F32  1.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  1.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  0.5
+  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  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+SHUTTER 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 20.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
+
+
+FLAT 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  3.0
+END
+
+
+FRINGE 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
+
+DARKMASK 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
+
+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
+
+# FILTER is an additional qualifier, and may be "*" (or absent!), in which case it matches everything
+# EXPECTED is the expected mean value
+# IMFILE.MEAN is the maximum permitted mean value for an imfile, relative to the standard deviation
+# IMFILE.STDEV is the maximum permitted standard deviation for an imfile
+# EXP.MEAN is the maximum permitted mean value for an exposure, relative to the standard deviation
+# EXP.STDEV is the maximum permitted standard deviation for an exposure
+# EXP.MEANSTDEV is the maximum permitted mean standard deviation for an exposure relative to the mean
+# ENSEMBLE.MEAN is the maximum permitted mean for an ensemble of exposures
+# ENSEMBLE.STDEV is the maximum permitted standard deviation for an ensemble of exposures
+# ENSEMBLE.MEANSTDEV is the maximum permitted mean standard deviation for an ensemble of exposures
+# IMFILE.SNR is the minimum permitted signal-to-noise for an imfile
+# EXP.SNR is the minimum permitted signal-to-noise for an exposure
+# These values (all except FILTER) may be zero, in which case no clipping is applied.
Index: /tags/sj_tags/sj_root_20080929/ippconfig/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/notes.txt	(revision 22322)
@@ -0,0 +1,16 @@
+
+creating a new camera:
+
+* add the camera to the CAMERAS list (ipp/ippconfig/ipprc.config)
+* create the camera directory (ipp/ippconfig/CAMERA)
+* copy example recipe files from a similar camera (eg, ipp/ippconfig/megacam)
+* copy Makefile.am from a similar camera (eg, ipp/ippconfig/megacam)
+* edit camera.config to describe your camera
+  - list the ccds and chips in FPA METADATA
+  - list the filters of interest (header name STR internal name)
+    give all varients used in the headers
+  - fix paths which refer to the original paths.
+* edit the format*.config files to match your camera:
+  - set up a rule to recognize the camera
+    - define keywords which will uniquely recognize your headers
+     
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/Makefile.am	(revision 22322)
@@ -0,0 +1,32 @@
+
+installdir = $(datadir)/ippconfig/recipes
+
+install_files = \
+	filerules-simple.mdc \
+	filerules-mef.mdc \
+	filerules-split.mdc \
+	fitstypes.mdc \
+	addstar.config \
+	masks.config \
+	reductionClasses.mdc \
+	rejections.config \
+	ppImage.config \
+	ppMerge.config \
+	ppStack.config \
+	ppStats.config \
+	psastro.config \
+	psphot.config \
+	pswarp.config \
+	ppSim.config \
+	ppSub.config \
+	jpeg.mdc \
+        ppStatsFromMetadata.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/addstar.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/addstar.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/addstar.config	(revision 22322)
@@ -0,0 +1,11 @@
+# configurable options for addstar
+
+IMAGES.ONLY  BOOL  TRUE
+
+FLATCORR METADATA
+    IMAGES.ONLY  BOOL  FALSE
+END
+
+ADDSTAR	METADATA
+    IMAGES.ONLY  BOOL  TRUE
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/filerules-mef.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/filerules-mef.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/filerules-mef.mdc	(revision 22322)
@@ -0,0 +1,241 @@
+### File rules for PHU=FPA, EXT=CHIP (eg, Megacam)
+### this file specifies compression types for various output files
+
+### Redirections (MEF)
+PPIMAGE.OUTPUT        STR PPIMAGE.OUTPUT.MEF
+PPIMAGE.OUTPUT.MASK   STR PPIMAGE.OUT.MK.MEF
+PPIMAGE.OUTPUT.WEIGHT STR PPIMAGE.OUT.WT.MEF
+
+PPIMAGE.CHIP          STR PPIMAGE.CHIP.MEF
+PPIMAGE.CHIP.MASK     STR PPIMAGE.CHIP.MK.MEF
+PPIMAGE.CHIP.WEIGHT   STR PPIMAGE.CHIP.WT.MEF
+
+PPIMAGE.BIN1          STR PPIMAGE.BIN1.MEF
+PPIMAGE.BIN2          STR PPIMAGE.BIN2.MEF
+
+PSASTRO.OUTPUT        STR PSASTRO.OUT.CMF.MEF
+PSPHOT.OUTPUT         STR PSPHOT.OUT.CMF.MEF
+PSPHOT.PSF.SAVE       STR PSPHOT.PSF.RAW.SAVE
+
+### Redirections (SPL)
+# PPIMAGE.OUTPUT        STR PPIMAGE.OUTPUT.SPL
+# PPIMAGE.OUTPUT.MASK   STR PPIMAGE.OUT.MK.SPL
+# PPIMAGE.OUTPUT.WEIGHT STR PPIMAGE.OUT.WT.SPL
+# 
+# PPIMAGE.CHIP          STR PPIMAGE.CHIP.SPL
+# PPIMAGE.CHIP.MASK     STR PPIMAGE.CHIP.MK.SPL
+# PPIMAGE.CHIP.WEIGHT   STR PPIMAGE.CHIP.WT.SPL
+# 
+# PPIMAGE.BIN1          STR PPIMAGE.BIN1.SPL
+# PPIMAGE.BIN2          STR PPIMAGE.BIN2.SPL
+# 
+# PSASTRO.OUTPUT        STR PSASTRO.OUT.CMF.SPL
+# PSPHOT.OUTPUT         STR PSPHOT.OUT.CMF.SPL
+
+### Redirections (common)
+
+PPIMAGE.OUTPUT.FPA1   STR PPIMAGE.OUTPUT.FPA1.MEF
+PPIMAGE.OUTPUT.FPA2   STR PPIMAGE.OUTPUT.FPA2.MEF
+PPSTAMP.CHIP          STR PPSTAMP.CHIP.MEF
+PSASTRO.INPUT         STR PSASTRO.INPUT.CMF
+PSASTRO.OUTPUT.MEF    STR PSASTRO.OUT.CMF.MEF
+DVOCORR.OUTPUT        STR DVOCORR.MEF.OUTPUT
+DVOFLAT.OUTPUT        STR DVOFLAT.MEF.OUTPUT
+
+### input file definitions
+### use @DETDB entries to get the detrend images from the database
+### replace @DETDB with @FILES if you want to require it from the 
+### command line, or with an explicit name to require a specific file
+TYPE                    INPUT    FILENAME.RULE DATA.LEVEL FILE.TYPE
+
+## files used by ppImage
+PPIMAGE.INPUT           INPUT    @FILES        CHIP       IMAGE
+PPIMAGE.INPUT.MASK      INPUT    @FILES        CHIP       MASK
+PPIMAGE.INPUT.WEIGHT    INPUT    @FILES        CHIP       WEIGHT
+PPIMAGE.INPUT.PSF       INPUT    @FILES        CHIP       PSF
+PPIMAGE.INPUT.SRC       INPUT    @FILES        CHIP       CMF
+PPIMAGE.MASK       	INPUT 	 @DETDB        CHIP       MASK
+PPIMAGE.BIAS       	INPUT 	 @DETDB        CHIP       IMAGE
+PPIMAGE.DARK       	INPUT 	 @DETDB        CHIP       DARK
+PPIMAGE.FLAT       	INPUT 	 @DETDB        CHIP       IMAGE
+PPIMAGE.FRINGE     	INPUT 	 @DETDB        CHIP       FRINGE
+PPIMAGE.SHUTTER    	INPUT 	 @DETDB        CHIP       IMAGE     
+
+## Files used by ppMerge
+PPMERGE.INPUT           INPUT 	 @FILES        CHIP       IMAGE
+PPMERGE.INPUT.MASK      INPUT 	 @FILES        CHIP       MASK
+PPMERGE.INPUT.WEIGHT    INPUT 	 @FILES        CHIP       WEIGHT
+
+## files used to build and apply the flat-field correction images
+DVOCORR.INPUT      	INPUT 	 @FILES        CHIP       IMAGE
+DVOCORR.REFHEAD    	INPUT 	 @FILES        CHIP       HEADER
+DVOFLAT.INPUT      	INPUT 	 @FILES        CHIP       IMAGE
+DVOFLAT.CORR       	INPUT 	 @DETDB        CHIP       IMAGE
+
+## files used by psphot 
+PSPHOT.LOAD        	INPUT 	 @FILES        CHIP       IMAGE
+PSPHOT.INPUT       	INPUT 	 @FILES        CHIP       IMAGE
+PSPHOT.MASK        	INPUT 	 @FILES        CHIP       MASK     
+PSPHOT.WEIGHT      	INPUT 	 @FILES        CHIP       WEIGHT     
+PSPHOT.PSF.LOAD    	INPUT 	 @FILES        CHIP       PSF       
+PSPHOT.INPUT.CMF   	INPUT 	 @FILES        CHIP       CMF       
+
+## files used by psastro 
+PSASTRO.INPUT.CMP  	INPUT 	 @FILES        CHIP       CMP
+PSASTRO.INPUT.CMF  	INPUT 	 @FILES        CHIP       CMF
+PSASTRO.WCS        	INPUT 	 @FILES        CHIP       CMF
+PSASTRO.MODEL      	INPUT 	 @DETDB        FPA        ASTROM
+
+## files used by pswarp
+PSWARP.INPUT       	INPUT 	 @FILES        CHIP       IMAGE
+PSWARP.WEIGHT      	INPUT 	 @FILES        CHIP       WEIGHT
+PSWARP.MASK        	INPUT 	 @FILES        CHIP       MASK
+PSWARP.SKYCELL     	INPUT 	 @FILES        CHIP       IMAGE
+PSWARP.ASTROM      	INPUT 	 @FILES        CHIP       CMF
+
+## files used by ppsub
+PPSUB.INPUT        	INPUT 	 @FILES        FPA        IMAGE
+PPSUB.INPUT.MASK   	INPUT 	 @FILES        FPA        MASK
+PPSUB.INPUT.WEIGHT 	INPUT 	 @FILES        FPA        WEIGHT
+PPSUB.REF          	INPUT 	 @FILES        FPA        IMAGE
+PPSUB.REF.MASK     	INPUT 	 @FILES        FPA        MASK
+PPSUB.REF.WEIGHT   	INPUT 	 @FILES        FPA        WEIGHT
+PPSUB.SOURCES      	INPUT 	 @FILES        FPA        CMF
+
+## files used by ppstack
+PPSTACK.INPUT      	INPUT 	 @FILES        FPA        IMAGE
+PPSTACK.INPUT.MASK 	INPUT 	 @FILES        FPA        MASK
+PPSTACK.INPUT.WEIGHT    INPUT 	 @FILES        FPA        WEIGHT
+PPSTACK.INPUT.PSF  	INPUT 	 @FILES        CHIP       PSF
+PPSTACK.INPUT.SOURCES  	INPUT 	 @FILES        FPA        CMF
+
+## files used by ppstamp
+PPSTAMP.INPUT           INPUT 	 @FILES        CHIP       IMAGE
+
+# files used by pparith
+PPARITH.INPUT.IMAGE 	INPUT 	 @FILES        CHIP       IMAGE
+PPARITH.INPUT.MASK  	INPUT 	 @FILES        CHIP       MASK
+
+### output file definitions
+TYPE                    OUTPUT FILENAME.RULE                     FILE.TYPE FITS.TYPE  DATA.LEVEL FILE.SAVE FILE.FORMAT
+PPIMAGE.OUTPUT.MEF      OUTPUT {OUTPUT}.b0.fits                  IMAGE     COMP_IMG   CHIP       TRUE      MEF
+PPIMAGE.OUT.MK.MEF      OUTPUT {OUTPUT}.mk.fits                  MASK      COMP_MASK  CHIP       TRUE      MEF
+PPIMAGE.OUT.WT.MEF      OUTPUT {OUTPUT}.wt.fits                  WEIGHT    COMP_WT    CHIP       TRUE      MEF
+PPIMAGE.OUTPUT.SPL      OUTPUT {OUTPUT}.{CHIP.NAME}.b0.fits      IMAGE     NONE       CHIP       TRUE      SPLIT
+PPIMAGE.OUT.MK.SPL      OUTPUT {OUTPUT}.{CHIP.NAME}.mk.fits      MASK      NONE       CHIP       TRUE      SPLIT
+PPIMAGE.OUT.WT.SPL      OUTPUT {OUTPUT}.{CHIP.NAME}.wt.fits      WEIGHT    NONE       CHIP       TRUE      SPLIT
+PPIMAGE.OUTPUT.DETMASK  OUTPUT {OUTPUT}.fits                     IMAGE     MASK       CHIP       TRUE      MEF
+PPIMAGE.OUTPUT.RESID    OUTPUT {OUTPUT}.b0.fits                  IMAGE     COMP_SUB   CHIP       TRUE      MEF
+PPIMAGE.CONFIG          OUTPUT {OUTPUT}.{CHIP.NAME}.ppImage.mdc  TEXT      NONE       CHIP       TRUE      NONE
+		        					        		     
+PPIMAGE.CHIP.MEF        OUTPUT {OUTPUT}.ch.fits                  IMAGE     COMP_IMG   CHIP       TRUE      MEF
+PPIMAGE.CHIP.MK.MEF     OUTPUT {OUTPUT}.ch.mk.fits               MASK      COMP_MASK  CHIP       TRUE      MEF
+PPIMAGE.CHIP.WT.MEF     OUTPUT {OUTPUT}.ch.wt.fits               WEIGHT    COMP_WT    CHIP       TRUE      MEF
+PPIMAGE.CHIP.SPL        OUTPUT {OUTPUT}.{CHIP.NAME}.ch.fits      IMAGE     NONE       CHIP       TRUE      SPLIT
+PPIMAGE.CHIP.MK.SPL     OUTPUT {OUTPUT}.{CHIP.NAME}.ch.mk.fits   MASK      NONE       CHIP       TRUE      SPLIT
+PPIMAGE.CHIP.WT.SPL     OUTPUT {OUTPUT}.{CHIP.NAME}.ch.wt.fits   WEIGHT    NONE       CHIP       TRUE      SPLIT
+							        		     
+PPIMAGE.OUTPUT.FPA1.MEF OUTPUT {OUTPUT}.b1.fits                  IMAGE     COMP_IMG   FPA        TRUE      MEF
+PPIMAGE.OUTPUT.FPA2.MEF OUTPUT {OUTPUT}.b2.fits                  IMAGE     COMP_IMG   FPA        TRUE      MEF
+PPIMAGE.OUTPUT.FPA1.SPL OUTPUT {OUTPUT}.{CHIP.NAME}.b1.fits      IMAGE     NONE       CHIP       TRUE      SPLIT
+PPIMAGE.OUTPUT.FPA2.SPL OUTPUT {OUTPUT}.{CHIP.NAME}.b2.fits      IMAGE     NONE       CHIP       TRUE      SPLIT
+							        		     
+PPIMAGE.BIN1.MEF      	OUTPUT {OUTPUT}.b1c.fits                 IMAGE     COMP_IMG   CHIP       TRUE      MEF
+PPIMAGE.BIN2.MEF      	OUTPUT {OUTPUT}.b2c.fits                 IMAGE     COMP_IMG   CHIP       TRUE      MEF
+PPIMAGE.BIN1.SPL      	OUTPUT {OUTPUT}.{CHIP.NAME}.b1.fits      IMAGE     COMP_IMG   CHIP       TRUE      SPLIT
+PPIMAGE.BIN2.SPL      	OUTPUT {OUTPUT}.{CHIP.NAME}.b2.fits      IMAGE     COMP_IMG   CHIP       TRUE      SPLIT
+		      						       		     
+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}.stats                    STATS     NONE       FPA        TRUE      MEF
+
+
+## note: these use the same output naming convention since they are used for different ppMerge runs
+PPMERGE.OUTPUT.MASK     OUTPUT {OUTPUT}.fits                   	 MASK      NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.BIAS     OUTPUT {OUTPUT}.fits                   	 IMAGE     NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.DARK     OUTPUT {OUTPUT}.fits                   	 DARK      NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.SHUTTER  OUTPUT {OUTPUT}.fits                   	 IMAGE     NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.FLAT     OUTPUT {OUTPUT}.fits                   	 IMAGE     NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.FRINGE   OUTPUT {OUTPUT}.fits                   	 FRINGE    NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.SIGMA    OUTPUT {OUTPUT}.sigma.fits             	 IMAGE     NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.COUNT    OUTPUT {OUTPUT}.count.fits             	 IMAGE     NONE       CHIP       TRUE      NONE
+							        		     
+DVOCORR.MEF.OUTPUT    	OUTPUT {OUTPUT}.fc.fits                  IMAGE     NONE       CHIP       TRUE      MEF
+DVOCORR.SPL.OUTPUT    	OUTPUT {OUTPUT}.{CHIP.NAME}.fc.fits      IMAGE     NONE       CHIP       TRUE      SPLIT
+DVOFLAT.MEF.OUTPUT    	OUTPUT {OUTPUT}.co.fits                  IMAGE     NONE       CHIP       TRUE      MEF
+DVOFLAT.SPL.OUTPUT    	OUTPUT {OUTPUT}.{CHIP.NAME}.co.fits      IMAGE     NONE       CHIP       TRUE      SPLIT
+		      						       		     
+PSPHOT.RESID          	OUTPUT {OUTPUT}.res.fits                 IMAGE     NONE       CHIP       FALSE     MEF
+PSPHOT.BACKGND        	OUTPUT {OUTPUT}.bck.fits                 IMAGE     NONE       CHIP       FALSE     MEF
+PSPHOT.BACKSUB        	OUTPUT {OUTPUT}.sub.fits                 IMAGE     NONE       CHIP       FALSE     MEF
+PSPHOT.BACKMDL        	OUTPUT {OUTPUT}.mdl.fits                 IMAGE     NONE       CHIP       FALSE     MEF
+PSPHOT.BACKMDL.MEF    	OUTPUT {OUTPUT}.mdl.fits                 IMAGE     NONE       CHIP       FALSE     MEF
+		      						       		     
+PSPHOT.OUTPUT.RAW     	OUTPUT {OUTPUT}.{CHIP.NAME}              RAW       NONE       CHIP       TRUE      NONE
+PSPHOT.OUTPUT.SX      	OUTPUT {OUTPUT}.{CHIP.NAME}.sx           SX        NONE       CHIP       TRUE      NONE
+PSPHOT.OUTPUT.OBJ     	OUTPUT {OUTPUT}.{CHIP.NAME}.obj          OBJ       NONE       CHIP       TRUE      NONE
+PSPHOT.OUTPUT.CMP     	OUTPUT {OUTPUT}.{CHIP.NAME}.cmp          CMP       NONE       CHIP       TRUE      NONE
+PSPHOT.OUT.CMF.SPL    	OUTPUT {OUTPUT}.{CHIP.NAME}.cmf          CMF       NONE       CHIP       TRUE      SPLIT
+PSPHOT.OUT.CMF.MEF    	OUTPUT {OUTPUT}.cmf                      CMF       NONE       CHIP       TRUE      MEF
+PSPHOT.PSF.RAW.SAVE    	OUTPUT {OUTPUT}.psf                      PSF       NONE       CHIP       TRUE      MEF
+PSPHOT.PSF.SKY.SAVE    	OUTPUT {OUTPUT}.psf                      PSF       NONE       CHIP       TRUE      MEF
+		      						       		     
+SOURCE.PLOT.MOMENTS   	OUTPUT {OUTPUT}.mnt.png                  KAPA      NONE       FPA        TRUE      NONE
+SOURCE.PLOT.PSFMODEL  	OUTPUT {OUTPUT}.psf.png                  KAPA      NONE       FPA        TRUE      NONE
+SOURCE.PLOT.APRESID   	OUTPUT {OUTPUT}.dap.png                  KAPA      NONE       FPA        TRUE      NONE
+		      						       		     
+PSASTRO.OUTPUT.CMP    	OUTPUT {OUTPUT}.{CHIP.NAME}.smp          CMP       NONE       CHIP       TRUE      NONE
+PSASTRO.OUT.CMF.SPL   	OUTPUT {OUTPUT}.{CHIP.NAME}.smf          CMF       NONE       CHIP       TRUE      SPLIT
+PSASTRO.OUT.CMF.MEF   	OUTPUT {OUTPUT}.smf                      CMF       NONE       FPA        TRUE      MEF
+PSASTRO.OUT.MODEL     	OUTPUT {OUTPUT}.asm                ASTROM.MODEL    NONE       FPA        TRUE      NONE
+PSASTRO.OUT.REFSTARS  	OUTPUT {OUTPUT}.aref.fits          ASTROM.REFSTARS NONE       FPA        TRUE      NONE
+PSASTRO.STATS           OUTPUT {OUTPUT}.stats                    STATS     NONE       FPA        TRUE      NONE
+PSASTRO.CONFIG          OUTPUT {OUTPUT}.psastro.mdc              TEXT      NONE       FPA        TRUE      NONE
+	      									     
+PSWARP.OUTPUT         	OUTPUT {OUTPUT}.fits                     IMAGE     COMP_IMG   FPA        TRUE      NONE
+PSWARP.OUTPUT.MASK    	OUTPUT {OUTPUT}.mk.fits                  MASK      COMP_MASK  FPA        TRUE      NONE
+PSWARP.OUTPUT.WEIGHT  	OUTPUT {OUTPUT}.wt.fits                  WEIGHT    COMP_WT    FPA        TRUE      NONE
+PSWARP.OUTPUT.SOURCES 	OUTPUT {OUTPUT}.cmf                      CMF       NONE       FPA        TRUE      NONE
+PSWARP.BIN1           	OUTPUT {OUTPUT}.b1.fits                  IMAGE     COMP_IMG   FPA        TRUE      NONE
+PSWARP.BIN2           	OUTPUT {OUTPUT}.b2.fits                  IMAGE     COMP_IMG   FPA        TRUE      NONE
+PSWARP.CONFIG           OUTPUT {OUTPUT}.pswarp.mdc               TEXT      NONE       FPA        TRUE      NONE
+		      						       		     
+SKYCELL.STATS         	OUTPUT {OUTPUT}.stats                    STATS     NONE       FPA        TRUE      NONE
+SKYCELL.TEMPLATE      	OUTPUT {OUTPUT}.skycell                  SKYCELL   NONE       FPA        TRUE      NONE
+		      						       		     
+PPSUB.OUTPUT          	OUTPUT {OUTPUT}.fits                     IMAGE     COMP_IMG   FPA        TRUE      NONE
+PPSUB.OUTPUT.MASK     	OUTPUT {OUTPUT}.mk.fits                  MASK      COMP_MASK  FPA        TRUE      NONE
+PPSUB.OUTPUT.WEIGHT   	OUTPUT {OUTPUT}.wt.fits                  WEIGHT    COMP_WT    FPA        TRUE      NONE
+PPSUB.OUTPUT.KERNELS    OUTPUT {OUTPUT}.subkernel                SUBKERNEL NONE       FPA        TRUE      NONE
+PPSUB.OUTPUT.JPEG1	OUTPUT {OUTPUT}.b1.jpg                   JPEG      NONE       FPA        TRUE      NONE
+PPSUB.OUTPUT.JPEG2	OUTPUT {OUTPUT}.b2.jpg                   JPEG      NONE       FPA        TRUE      NONE
+PPSUB.CONFIG            OUTPUT {OUTPUT}.ppSub.mdc                TEXT      NONE       FPA        TRUE      NONE
+		      						       		     
+PPSTACK.OUTPUT        	OUTPUT {OUTPUT}.fits                     IMAGE     COMP_IMG   FPA        TRUE      NONE
+PPSTACK.OUTPUT.MASK   	OUTPUT {OUTPUT}.mk.fits                  MASK      COMP_MASK  FPA        TRUE      NONE
+PPSTACK.OUTPUT.WEIGHT 	OUTPUT {OUTPUT}.wt.fits                  WEIGHT    COMP_WT    FPA        TRUE      NONE
+PPSTACK.TARGET.PSF      OUTPUT {OUTPUT}.target.psf               PSF       NONE       CHIP       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
+PPSTACK.CONFIG          OUTPUT {OUTPUT}.ppStack.mdc              TEXT      NONE       FPA        TRUE      NONE
+		      						       		     
+PPSTAMP.OUTPUT        	OUTPUT {OUTPUT}.fits                     IMAGE     NONE       FPA        TRUE      NONE
+PPSTAMP.CHIP.MEF      	OUTPUT {OUTPUT}.ch.fits                  IMAGE     NONE       CHIP       FALSE     MEF
+		      						       		     
+PPSIM.OUTPUT.MEF      	OUTPUT {OUTPUT}.fits                     IMAGE     NONE       CHIP       TRUE      MEF
+PPSIM.OUTPUT.SPL      	OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     NONE       CHIP       TRUE      SPLIT
+PPSIM.FAKE.CHIP        	OUTPUT {OUTPUT}.fake.fits     		 IMAGE     NONE       FPA        TRUE      SIMPLE
+PPSIM.FORCE.CHIP       	OUTPUT {OUTPUT}.force.fits     		 IMAGE     NONE       FPA        TRUE      SIMPLE
+PPSIM.SOURCES         	OUTPUT {OUTPUT}.cmf                      CMF       NONE       FPA        TRUE      MEF
+PPSIM.FAKE.SOURCES     	OUTPUT {OUTPUT}.fake.cmf                 CMF       NONE       FPA        TRUE      NONE
+PPSIM.FORCE.SOURCES    	OUTPUT {OUTPUT}.force.cmf                CMF       NONE       FPA        TRUE      NONE
+		      						       		     
+PPARITH.OUTPUT.IMAGE  	OUTPUT {OUTPUT}.fits                     IMAGE     COMP_IMG   CHIP       TRUE      NONE
+PPARITH.OUTPUT.MASK   	OUTPUT {OUTPUT}.fits                     MASK      COMP_MASK  CHIP       TRUE      NONE
+		      						       		     
+LOG.IMFILE            	OUTPUT {OUTPUT}.{CHIP.NAME}.log          TEXT      NONE       CHIP       TRUE      NONE
+LOG.EXP               	OUTPUT {OUTPUT}.log                      TEXT      NONE       FPA        TRUE      NONE
+		      						       		     
+TRACE.IMFILE          	OUTPUT {OUTPUT}.{CHIP.NAME}.trace        TEXT      NONE       CHIP       TRUE      NONE
+TRACE.EXP             	OUTPUT {OUTPUT}.trace                    TEXT      NONE       FPA        TRUE      NONE
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/filerules-simple.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/filerules-simple.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/filerules-simple.mdc	(revision 22322)
@@ -0,0 +1,193 @@
+### File rules for PHU=FPA, EXT=NONE
+
+PSASTRO.INPUT      STR PSASTRO.INPUT.CMF
+PSASTRO.OUTPUT     STR PSASTRO.OUTPUT.CMF
+PSPHOT.OUTPUT      STR PSPHOT.OUTPUT.CMF
+PSPHOT.PSF.SAVE    STR PSPHOT.PSF.RAW.SAVE
+
+### input file definitions
+### use @DETDB entries to get the detrend images from the database
+### replace @DETDB with @FILES if you want to require it from the 
+### command line, or with an explicit name to require a specific file
+TYPE                    INPUT    FILENAME.RULE DATA.LEVEL FILE.TYPE
+
+## files used by ppImage
+PPIMAGE.INPUT           INPUT    @FILES        FPA        IMAGE     
+PPIMAGE.INPUT.MASK      INPUT    @FILES        FPA        MASK      
+PPIMAGE.INPUT.WEIGHT    INPUT    @FILES        FPA        WEIGHT    
+PPIMAGE.INPUT.PSF       INPUT    @FILES        READOUT    PSF       
+PPIMAGE.INPUT.SRC       INPUT    @FILES        READOUT    CMF       
+PPIMAGE.MASK       	INPUT    @DETDB        CHIP       IMAGE     
+PPIMAGE.BIAS       	INPUT    @DETDB        CHIP       IMAGE     
+PPIMAGE.DARK       	INPUT    @DETDB        CHIP       DARK     
+PPIMAGE.FLAT       	INPUT    @DETDB        CHIP       IMAGE     
+PPIMAGE.FRINGE     	INPUT 	 @DETDB        CHIP       FRINGE
+PPIMAGE.SHUTTER    	INPUT    @DETDB        CHIP       IMAGE     
+
+## Files used by ppMerge
+PPMERGE.INPUT      	INPUT    @FILES        CHIP       IMAGE
+PPMERGE.INPUT.MASK 	INPUT    @FILES        CHIP       MASK
+PPMERGE.INPUT.WEIGHT    INPUT    @FILES        CHIP       WEIGHT
+
+## files used to build and apply the flat-field correction images
+DVOCORR.INPUT      	INPUT 	 @FILES        CHIP       IMAGE
+DVOCORR.REFHEAD    	INPUT 	 @FILES        CHIP       HEADER
+DVOFLAT.INPUT      	INPUT 	 @FILES        CHIP       IMAGE
+DVOFLAT.CORR       	INPUT 	 @DETDB        CHIP       IMAGE
+
+## files used by psphot 
+PSPHOT.LOAD        	INPUT    @FILES        CHIP       IMAGE
+PSPHOT.INPUT       	INPUT    @FILES        CHIP       IMAGE     
+PSPHOT.MASK             INPUT    @FILES        CHIP       MASK     
+PSPHOT.WEIGHT           INPUT    @FILES        CHIP       WEIGHT     
+PSPHOT.PSF.LOAD         INPUT    @FILES        CHIP       PSF       
+PSPHOT.INPUT.CMF        INPUT    @FILES        CHIP       CMF       
+
+## files used by psastro 
+PSASTRO.INPUT.CMP  	INPUT    @FILES        CHIP       CMP       
+PSASTRO.INPUT.CMF  	INPUT    @FILES        CHIP       CMF       
+PSASTRO.WCS             INPUT    @FILES        CHIP       CMF
+PSASTRO.MODEL           INPUT    @DETDB        FPA        ASTROM
+
+## files used by pswarp
+PSWARP.INPUT       	INPUT    @FILES        FPA        IMAGE     
+PSWARP.WEIGHT      	INPUT    @FILES        FPA        WEIGHT
+PSWARP.MASK        	INPUT    @FILES        FPA        MASK
+PSWARP.SKYCELL     	INPUT    @FILES        FPA        IMAGE     
+PSWARP.ASTROM      	INPUT    @FILES        FPA        CMF
+
+## files used by ppsub
+PPSUB.INPUT        	INPUT    @FILES        FPA        IMAGE
+PPSUB.INPUT.MASK   	INPUT    @FILES        FPA        MASK
+PPSUB.INPUT.WEIGHT 	INPUT    @FILES        FPA        WEIGHT
+PPSUB.REF          	INPUT    @FILES        FPA        IMAGE
+PPSUB.REF.MASK     	INPUT    @FILES        FPA        MASK
+PPSUB.REF.WEIGHT   	INPUT    @FILES        FPA        WEIGHT
+PPSUB.SOURCES      	INPUT 	 @FILES        FPA        CMF
+
+## files used by ppstack
+PPSTACK.INPUT      	INPUT    @FILES        FPA        IMAGE
+PPSTACK.INPUT.MASK 	INPUT    @FILES        FPA        MASK
+PPSTACK.INPUT.WEIGHT    INPUT    @FILES        FPA        WEIGHT
+PPSTACK.INPUT.PSF  	INPUT 	 @FILES        CHIP       PSF
+PPSTACK.INPUT.SOURCES   INPUT    @FILES        FPA        CMF
+
+## files used by ppstamp
+PPSTAMP.INPUT           INPUT    @FILES        FPA        IMAGE
+
+# files used by pparith
+PPARITH.INPUT.IMAGE    	INPUT    @FILES        CHIP       IMAGE
+PPARITH.INPUT.MASK     	INPUT    @FILES        CHIP       MASK
+
+PPSIM.INPUT             INPUT    @FILES        FPA        IMAGE     
+PPSIM.REAL.SOURCES      INPUT    @FILES        CHIP       CMF       
+
+### output file definitions
+TYPE                    OUTPUT FILENAME.RULE         FILE.TYPE FITS.TYPE  DATA.LEVEL FILE.SAVE FILE.FORMAT
+PPIMAGE.OUTPUT          OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
+PPIMAGE.OUTPUT.MASK     OUTPUT {OUTPUT}.mk.fits      MASK      NONE       FPA        TRUE      SIMPLE
+PPIMAGE.OUTPUT.WEIGHT   OUTPUT {OUTPUT}.wt.fits      WEIGHT    NONE       FPA        TRUE      SIMPLE
+PPIMAGE.OUTPUT.DETMASK  OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
+PPIMAGE.OUTPUT.RESID    OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
+PPIMAGE.CONFIG          OUTPUT {OUTPUT}.ppImage.mdc  TEXT      NONE       FPA        TRUE      NONE
+						     
+PPIMAGE.CHIP        	OUTPUT {OUTPUT}.ch.fits      IMAGE     NONE       FPA        TRUE      SIMPLE
+PPIMAGE.CHIP.MASK   	OUTPUT {OUTPUT}.ch.mk.fits   MASK      NONE       FPA        TRUE      SIMPLE
+PPIMAGE.CHIP.WEIGHT 	OUTPUT {OUTPUT}.ch.wt.fits   WEIGHT    NONE       FPA        TRUE      SIMPLE
+						     
+PPIMAGE.OUTPUT.FPA1 	OUTPUT {OUTPUT}.b1.fits      IMAGE     NONE       FPA        TRUE      SIMPLE
+PPIMAGE.OUTPUT.FPA2 	OUTPUT {OUTPUT}.b2.fits      IMAGE     NONE       FPA        TRUE      SIMPLE
+						     
+PPIMAGE.BIN1        	OUTPUT {OUTPUT}.b1.fits      IMAGE     NONE       FPA        TRUE      SIMPLE
+PPIMAGE.BIN2        	OUTPUT {OUTPUT}.b2.fits      IMAGE     NONE       FPA        TRUE      SIMPLE
+						     
+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}.stats        STATS     NONE       FPA        TRUE      NONE
+
+
+## note: these use the  same output naming convention since they are used for different ppMerge runs
+PPMERGE.OUTPUT.MASK   	OUTPUT {OUTPUT}.fits         MASK      NONE       FPA        TRUE      SIMPLE
+PPMERGE.OUTPUT.BIAS   	OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
+PPMERGE.OUTPUT.DARK   	OUTPUT {OUTPUT}.fits         DARK      NONE       FPA        TRUE      SIMPLE
+PPMERGE.OUTPUT.SHUTTER	OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
+PPMERGE.OUTPUT.FLAT   	OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
+PPMERGE.OUTPUT.FRINGE 	OUTPUT {OUTPUT}.fits         FRINGE    NONE       FPA        TRUE      SIMPLE
+PPMERGE.OUTPUT.SIGMA  	OUTPUT {OUTPUT}.sigma.fits   IMAGE     NONE       FPA        TRUE      SIMPLE
+PPMERGE.OUTPUT.COUNT  	OUTPUT {OUTPUT}.count.fits   IMAGE     NONE       FPA        TRUE      SIMPLE
+						     
+DVOCORR.OUTPUT      	OUTPUT {OUTPUT}.fc.fits      IMAGE     NONE       FPA        TRUE      SIMPLE
+DVOFLAT.OUTPUT      	OUTPUT {OUTPUT}.co.fits      IMAGE     NONE       FPA        TRUE      SIMPLE
+						     
+PSPHOT.RESID        	OUTPUT {OUTPUT}.res.fits     IMAGE     NONE       FPA        TRUE      SIMPLE
+PSPHOT.BACKGND      	OUTPUT {OUTPUT}.bck.fits     IMAGE     NONE       FPA        TRUE      SIMPLE
+PSPHOT.BACKSUB      	OUTPUT {OUTPUT}.sub.fits     IMAGE     NONE       FPA        TRUE      SIMPLE
+PSPHOT.BACKMDL      	OUTPUT {OUTPUT}.mdl.fits     IMAGE     NONE       FPA        TRUE      SIMPLE
+PSPHOT.BACKMDL.MEF     	OUTPUT {OUTPUT}.mdl.fits     IMAGE     NONE       FPA        TRUE      SIMPLE
+						     
+PSPHOT.OUTPUT.RAW   	OUTPUT {OUTPUT}              RAW       NONE       FPA        TRUE      NONE
+PSPHOT.OUTPUT.SX    	OUTPUT {OUTPUT}.sx           SX        NONE       FPA        TRUE      NONE
+PSPHOT.OUTPUT.OBJ   	OUTPUT {OUTPUT}.obj          OBJ       NONE       FPA        TRUE      NONE
+PSPHOT.OUTPUT.CMP   	OUTPUT {OUTPUT}.cmp          CMP       NONE       FPA        TRUE      NONE
+PSPHOT.OUTPUT.CMF   	OUTPUT {OUTPUT}.cmf          CMF       NONE       CHIP       TRUE      NONE
+PSPHOT.OUT.CMF.MEF  	OUTPUT {OUTPUT}.cmf          CMF       NONE       CHIP       TRUE      NONE
+PSPHOT.PSF.RAW.SAVE    	OUTPUT {OUTPUT}.psf          PSF       NONE       FPA        TRUE      NONE
+PSPHOT.PSF.SKY.SAVE    	OUTPUT {OUTPUT}.psf          PSF       NONE       FPA        TRUE      NONE
+						     
+SOURCE.PLOT.MOMENTS  	OUTPUT {OUTPUT}.mnt.png      KAPA      NONE       FPA        TRUE      NONE
+SOURCE.PLOT.PSFMODEL 	OUTPUT {OUTPUT}.psf.png      KAPA      NONE       FPA        TRUE      NONE
+SOURCE.PLOT.APRESID  	OUTPUT {OUTPUT}.dap.png      KAPA      NONE       FPA        TRUE      NONE
+						     
+PSASTRO.OUTPUT.CMP  	OUTPUT {OUTPUT}.smp          CMP       NONE       FPA        TRUE      NONE
+PSASTRO.OUTPUT.CMF  	OUTPUT {OUTPUT}.smf          CMF       NONE       FPA        TRUE      NONE
+PSASTRO.OUT.MODEL       OUTPUT {OUTPUT}.asm          ASTROM.MODEL    NONE FPA        TRUE      NONE
+PSASTRO.OUT.REFSTARS    OUTPUT {OUTPUT}.aref.fits    ASTROM.REFSTARS NONE FPA        TRUE      NONE
+PSASTRO.STATS       	OUTPUT {OUTPUT}.stats        STATS     NONE       FPA        TRUE      NONE
+PSASTRO.CONFIG          OUTPUT {OUTPUT}.psastro.mdc  TEXT      NONE       FPA        TRUE      NONE
+						     
+PSWARP.OUTPUT       	OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      NONE
+PSWARP.OUTPUT.MASK  	OUTPUT {OUTPUT}.mask.fits    MASK      NONE       FPA        TRUE      NONE
+PSWARP.OUTPUT.WEIGHT    OUTPUT {OUTPUT}.wt.fits      WEIGHT    NONE       FPA        TRUE      NONE
+PSWARP.OUTPUT.SOURCES   OUTPUT {OUTPUT}.cmf          CMF       NONE       FPA        TRUE      NONE
+PSWARP.BIN1         	OUTPUT {OUTPUT}.b1.fits      IMAGE     NONE       FPA        TRUE      NONE
+PSWARP.BIN2         	OUTPUT {OUTPUT}.b2.fits      IMAGE     NONE       FPA        TRUE      NONE
+PSWARP.CONFIG           OUTPUT {OUTPUT}.pswarp.mdc   TEXT      NONE       FPA        TRUE      NONE
+						     
+SKYCELL.STATS       	OUTPUT {OUTPUT}.stats        STATS     NONE       FPA        TRUE      NONE
+SKYCELL.TEMPLATE    	OUTPUT {OUTPUT}.skycell      SKYCELL   NONE       FPA        TRUE      NONE
+						     
+PPSUB.OUTPUT        	OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      NONE
+PPSUB.OUTPUT.MASK   	OUTPUT {OUTPUT}.mask.fits    MASK      NONE       FPA        TRUE      NONE
+PPSUB.OUTPUT.WEIGHT 	OUTPUT {OUTPUT}.wt.fits      WEIGHT    NONE       FPA        TRUE      NONE
+PPSUB.OUTPUT.KERNELS    OUTPUT {OUTPUT}.subkernel    SUBKERNEL NONE       FPA        TRUE      NONE
+PPSUB.OUTPUT.JPEG1	OUTPUT {OUTPUT}.b1.jpg       JPEG      NONE       FPA        TRUE      NONE
+PPSUB.OUTPUT.JPEG2	OUTPUT {OUTPUT}.b2.jpg       JPEG      NONE       FPA        TRUE      NONE
+PPSUB.CONFIG            OUTPUT {OUTPUT}.ppSub.mdc    TEXT      NONE       FPA        TRUE      NONE
+					     
+PPSTACK.OUTPUT      	OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      NONE
+PPSTACK.OUTPUT.MASK 	OUTPUT {OUTPUT}.mask.fits    MASK      NONE       FPA        TRUE      NONE
+PPSTACK.OUTPUT.WEIGHT   OUTPUT {OUTPUT}.weight.fits  WEIGHT    NONE       FPA        TRUE      NONE
+PPSTACK.TARGET.PSF      OUTPUT {OUTPUT}.target.psf   PSF       NONE       CHIP       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
+PPSTACK.CONFIG          OUTPUT {OUTPUT}.ppStack.mdc  TEXT      NONE       FPA        TRUE      NONE
+					     
+PPSTAMP.OUTPUT      	OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
+PPSTAMP.CHIP        	OUTPUT {OUTPUT}.ch.fits      IMAGE     NONE       CHIP       FALSE     SIMPLE
+						     
+PPSIM.OUTPUT        	OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
+PPSIM.FAKE.CHIP        	OUTPUT {OUTPUT}.fake.fits    IMAGE     NONE       FPA        TRUE      SIMPLE
+PPSIM.FORCE.CHIP       	OUTPUT {OUTPUT}.force.fits   IMAGE     NONE       FPA        TRUE      SIMPLE
+PPSIM.SOURCES       	OUTPUT {OUTPUT}.cmf          CMF       NONE       FPA        TRUE      NONE
+PPSIM.FAKE.SOURCES     	OUTPUT {OUTPUT}.fake.cmf     CMF       NONE       FPA        TRUE      NONE
+PPSIM.FORCE.SOURCES    	OUTPUT {OUTPUT}.force.cmf    CMF       NONE       FPA        TRUE      NONE
+						     
+PPARITH.OUTPUT.IMAGE  	OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
+PPARITH.OUTPUT.MASK   	OUTPUT {OUTPUT}.fits         MASK      NONE       FPA        TRUE      SIMPLE
+
+LOG.IMFILE            	OUTPUT {OUTPUT}.imfile.log   TEXT      NONE       FPA        TRUE      NONE
+LOG.EXP               	OUTPUT {OUTPUT}.exp.log      TEXT      NONE       FPA        TRUE      NONE
+
+TRACE.IMFILE          	OUTPUT {OUTPUT}.imfile.trace TEXT      NONE       FPA        TRUE      NONE
+TRACE.EXP             	OUTPUT {OUTPUT}.exp.trace    TEXT      NONE       FPA        TRUE      NONE
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/filerules-split.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/filerules-split.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/filerules-split.mdc	(revision 22322)
@@ -0,0 +1,221 @@
+### File rules for PHU=CHIP, EXT=CELL (eg, GPC1)
+### this file specifies compression types for various output files
+
+# for basic chip analysis, psphot should produce SPLIT output
+# for basic camera analysis, psastro should produce MEF output
+PSPHOT.OUTPUT      STR PSPHOT.OUT.CMF.SPL
+PSASTRO.INPUT      STR PSASTRO.INPUT.CMF
+PSPHOT.PSF.SAVE    STR PSPHOT.PSF.RAW.SAVE
+
+# PSASTRO.OUTPUT     STR PSASTRO.OUT.CMF.MEF
+# PSASTRO.OUTPUT.SPL STR PSASTRO.OUT.CMF.SPL
+# PSASTRO.OUTPUT.MEF STR PSASTRO.OUT.CMF.MEF
+
+### input file definitions
+### use @DETDB entries to get the detrend images from the database
+### replace @DETDB with @FILES if you want to require it from the 
+### command line, or with an explicit name to require a specific file
+TYPE                    INPUT    FILENAME.RULE DATA.LEVEL FILE.TYPE
+
+## files used by ppImage
+PPIMAGE.INPUT           INPUT    @FILES        CHIP       IMAGE
+PPIMAGE.INPUT.MASK      INPUT    @FILES        CHIP       MASK
+PPIMAGE.INPUT.WEIGHT    INPUT    @FILES        CHIP       WEIGHT
+PPIMAGE.INPUT.PSF       INPUT    @FILES        CHIP       PSF
+PPIMAGE.INPUT.SRC       INPUT    @FILES        CHIP       CMF
+PPIMAGE.MASK            INPUT    @DETDB        CHIP       MASK
+PPIMAGE.BIAS            INPUT    @DETDB        CHIP       IMAGE
+PPIMAGE.DARK            INPUT    @DETDB        CHIP       DARK
+PPIMAGE.FLAT            INPUT    @DETDB        CHIP       IMAGE
+PPIMAGE.FRINGE     	INPUT 	 @DETDB        CHIP       FRINGE
+PPIMAGE.SHUTTER         INPUT    @DETDB        CHIP       IMAGE
+
+## Files used by ppMerge
+PPMERGE.INPUT           INPUT    @FILES        CHIP       IMAGE
+PPMERGE.INPUT.MASK      INPUT    @FILES        CHIP       MASK
+PPMERGE.INPUT.WEIGHT    INPUT    @FILES        CHIP       WEIGHT
+
+## files used to build and apply the flat-field correction images
+DVOCORR.INPUT      	INPUT 	 @FILES        CHIP       IMAGE
+DVOCORR.REFHEAD    	INPUT 	 @FILES        CHIP       HEADER
+DVOFLAT.INPUT      	INPUT 	 @FILES        CHIP       IMAGE
+DVOFLAT.CORR       	INPUT 	 @DETDB        CHIP       IMAGE
+
+## files used by psphot
+PSPHOT.LOAD             INPUT    @FILES        CHIP       IMAGE
+PSPHOT.INPUT            INPUT    @FILES        CHIP       IMAGE
+PSPHOT.MASK             INPUT    @FILES        CHIP       MASK     
+PSPHOT.WEIGHT           INPUT    @FILES        CHIP       WEIGHT     
+PSPHOT.PSF.LOAD         INPUT    @FILES        CHIP       PSF       
+PSPHOT.INPUT.CMF        INPUT    @FILES        CHIP       CMF       
+
+## files used by psastro 
+PSASTRO.INPUT.CMP       INPUT    @FILES        CHIP       CMP
+PSASTRO.INPUT.CMF       INPUT    @FILES        CHIP       CMF
+PSASTRO.WCS             INPUT    @FILES        CHIP       WCS
+PSASTRO.MODEL           INPUT    @DETDB        FPA        ASTROM
+
+## files used by pswarp
+PSWARP.INPUT            INPUT    @FILES        CHIP       IMAGE
+PSWARP.WEIGHT           INPUT    @FILES        CHIP       WEIGHT
+PSWARP.MASK             INPUT    @FILES        CHIP       MASK
+PSWARP.SKYCELL          INPUT    @FILES        FPA        IMAGE
+PSWARP.ASTROM           INPUT    @FILES        CHIP       CMF
+
+## files used by ppsub
+PPSUB.INPUT             INPUT    @FILES        FPA        IMAGE
+PPSUB.INPUT.MASK        INPUT    @FILES        FPA        MASK
+PPSUB.INPUT.WEIGHT      INPUT    @FILES        FPA        WEIGHT
+PPSUB.REF               INPUT    @FILES        FPA        IMAGE
+PPSUB.REF.MASK          INPUT    @FILES        FPA        MASK
+PPSUB.REF.WEIGHT        INPUT    @FILES        FPA        WEIGHT
+PPSUB.SOURCES      	INPUT 	 @FILES        FPA        CMF
+
+## files used by ppstack
+PPSTACK.INPUT           INPUT    @FILES        FPA        IMAGE
+PPSTACK.INPUT.MASK      INPUT    @FILES        FPA        MASK
+PPSTACK.INPUT.WEIGHT    INPUT    @FILES        FPA        WEIGHT
+PPSTACK.INPUT.PSF  	INPUT 	 @FILES        CHIP       PSF
+PPSTACK.INPUT.SOURCES   INPUT    @FILES        FPA        CMF
+
+## files used by ppstamp
+PPSTAMP.INPUT           INPUT    @FILES        CHIP       IMAGE
+
+# files used by pparith
+PPARITH.INPUT.IMAGE     INPUT    @FILES        CHIP       IMAGE
+PPARITH.INPUT.MASK      INPUT    @FILES        CHIP       MASK
+
+### output file definitions
+TYPE                    OUTPUT FILENAME.RULE                     FILE.TYPE FITS.TYPE  DATA.LEVEL FILE.SAVE FILE.FORMAT
+PPIMAGE.OUTPUT          OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     COMP_IMG   CHIP       TRUE      NONE
+PPIMAGE.OUTPUT.MASK     OUTPUT {OUTPUT}.{CHIP.NAME}.mk.fits      MASK      COMP_MASK  CHIP       TRUE      NONE
+PPIMAGE.OUTPUT.WEIGHT   OUTPUT {OUTPUT}.{CHIP.NAME}.wt.fits      WEIGHT    COMP_WT    CHIP       TRUE      NONE
+PPIMAGE.OUTPUT.DETMASK  OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     DET_MASK   CHIP       TRUE      NONE
+PPIMAGE.OUTPUT.RESID    OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     COMP_SUB   CHIP       TRUE      NONE
+PPIMAGE.CONFIG          OUTPUT {OUTPUT}.{CHIP.NAME}.ppImage.mdc  TEXT      NONE       CHIP       TRUE      NONE
+
+#PPIMAGE.OUTPUT          OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     NONE  CHIP       TRUE      NONE
+#PPIMAGE.OUTPUT.MASK     OUTPUT {OUTPUT}.{CHIP.NAME}.mk.fits      MASK      NONE  CHIP       TRUE      NONE
+#PPIMAGE.OUTPUT.WEIGHT   OUTPUT {OUTPUT}.{CHIP.NAME}.wt.fits      WEIGHT    NONE  CHIP       TRUE      NONE
+#PPIMAGE.OUTPUT.DETMASK  OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     NONE  CHIP       TRUE      NONE
+	        									        
+PPIMAGE.CHIP            OUTPUT {OUTPUT}.{CHIP.NAME}.ch.fits      IMAGE     COMP_IMG   CHIP       TRUE      NONE
+PPIMAGE.CHIP.DETMASK    OUTPUT {OUTPUT}.{CHIP.NAME}.ch.fits      IMAGE     NONE       CHIP       TRUE      NONE
+PPIMAGE.CHIP.MASK       OUTPUT {OUTPUT}.{CHIP.NAME}.ch.mk.fits   MASK      COMP_MASK  CHIP       TRUE      NONE
+PPIMAGE.CHIP.WEIGHT     OUTPUT {OUTPUT}.{CHIP.NAME}.ch.wt.fits   WEIGHT    COMP_WT    CHIP       TRUE      NONE
+PPIMAGE.CHIP.RESID      OUTPUT {OUTPUT}.{CHIP.NAME}.ch.fits      IMAGE     COMP_SUB   CHIP       TRUE      NONE
+		        									        
+PPIMAGE.OUTPUT.FPA1     OUTPUT {OUTPUT}.b1.fits                  IMAGE     NONE       FPA        TRUE      NONE
+PPIMAGE.OUTPUT.FPA2     OUTPUT {OUTPUT}.b2.fits                  IMAGE     NONE       FPA        TRUE      NONE
+		        									        
+PPIMAGE.BIN1            OUTPUT {OUTPUT}.{CHIP.NAME}.b1.fits      IMAGE     NONE       CHIP       TRUE      NONE
+PPIMAGE.BIN2            OUTPUT {OUTPUT}.{CHIP.NAME}.b2.fits      IMAGE     NONE       CHIP       TRUE      NONE
+		        									        
+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
+		        									        
+## note: these use the  same output naming convention since they are used for different ppMerge runs
+PPMERGE.OUTPUT.MASK     OUTPUT {OUTPUT}.{CHIP.NAME}.fits         MASK      NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.BIAS     OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.DARK     OUTPUT {OUTPUT}.{CHIP.NAME}.fits         DARK      NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.SHUTTER  OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.FLAT     OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.FRINGE   OUTPUT {OUTPUT}.{CHIP.NAME}.fits         FRINGE    NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.SIGMA    OUTPUT {OUTPUT}.{CHIP.NAME}.sigma.fits   IMAGE     NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.COUNT    OUTPUT {OUTPUT}.{CHIP.NAME}.count.fits   IMAGE     NONE       CHIP       TRUE      NONE
+		        									        
+DVOCORR.OUTPUT          OUTPUT {OUTPUT}.{CHIP.NAME}.fc.fits      IMAGE     NONE       CHIP       TRUE      NONE
+DVOFLAT.OUTPUT          OUTPUT {OUTPUT}.{CHIP.NAME}.co.fits      IMAGE     NONE       CHIP       TRUE      NONE
+		        									        
+PSPHOT.RESID            OUTPUT {OUTPUT}.{CHIP.NAME}.res.fits     IMAGE     COMP_SUB   CHIP       TRUE      NONE
+PSPHOT.BACKGND          OUTPUT {OUTPUT}.{CHIP.NAME}.bck.fits     IMAGE     COMP_IMG   CHIP       TRUE      NONE
+PSPHOT.BACKSUB          OUTPUT {OUTPUT}.{CHIP.NAME}.sub.fits     IMAGE     COMP_SUB   CHIP       TRUE      NONE
+PSPHOT.BACKMDL          OUTPUT {OUTPUT}.{CHIP.NAME}.mdl.fits     IMAGE     COMP_IMG   CHIP       TRUE      NONE
+PSPHOT.BACKMDL.MEF      OUTPUT {OUTPUT}.mdl.fits                 IMAGE     COMP_IMG   CHIP       TRUE      NONE
+		        									        
+PSPHOT.OUTPUT.RAW       OUTPUT {OUTPUT}.{CHIP.NAME}              RAW       NONE       CHIP       TRUE      NONE
+PSPHOT.OUTPUT.SX        OUTPUT {OUTPUT}.{CHIP.NAME}.sx           SX        NONE       CHIP       TRUE      NONE
+PSPHOT.OUTPUT.OBJ       OUTPUT {OUTPUT}.{CHIP.NAME}.obj          OBJ       NONE       CHIP       TRUE      NONE
+PSPHOT.OUTPUT.CMP       OUTPUT {OUTPUT}.{CHIP.NAME}.cmp          CMP       NONE       CHIP       TRUE      NONE
+PSPHOT.OUT.CMF.SPL      OUTPUT {OUTPUT}.{CHIP.NAME}.cmf          CMF       NONE       CHIP       TRUE      NONE
+PSPHOT.OUT.CMF.MEF      OUTPUT {OUTPUT}.cmf                      CMF       NONE       FPA        TRUE      MEF
+PSPHOT.PSF.RAW.SAVE     OUTPUT {OUTPUT}.{CHIP.NAME}.psf          PSF       NONE       CHIP       TRUE      MEF
+PSPHOT.PSF.SKY.SAVE     OUTPUT {OUTPUT}.psf          		 PSF       NONE       FPA        TRUE      MEF
+		        									        
+SOURCE.PLOT.MOMENTS     OUTPUT {OUTPUT}.{CHIP.NAME}.mnt.png      KAPA      NONE       CHIP       TRUE      NONE
+SOURCE.PLOT.PSFMODEL    OUTPUT {OUTPUT}.{CHIP.NAME}.psf.png      KAPA      NONE       CHIP       TRUE      NONE
+SOURCE.PLOT.APRESID     OUTPUT {OUTPUT}.{CHIP.NAME}.dap.png      KAPA      NONE       CHIP       TRUE      NONE
+		        									        
+# PSASTRO.OUTPUT.CMP      OUTPUT {OUTPUT}.{CHIP.NAME}.smp          CMP       NONE       CHIP       TRUE      NONE
+# PSASTRO.OUT.CMF.SPL     OUTPUT {OUTPUT}.{CHIP.NAME}.smf          CMF       NONE       CHIP       TRUE      NONE
+# PSASTRO.OUT.CMF.MEF     OUTPUT {OUTPUT}.smf                      CMF       NONE       FPA        TRUE      MEF
+PSASTRO.OUT.MODEL       OUTPUT {OUTPUT}.asm               ASTROM.MODEL     NONE       FPA        TRUE      NONE
+PSASTRO.OUT.REFSTARS    OUTPUT {OUTPUT}.aref.fits         ASTROM.REFSTARS  NONE       FPA        TRUE      NONE
+PSASTRO.OUTPUT          OUTPUT {OUTPUT}.smf                      CMF       NONE       FPA        TRUE      MEF
+PSASTRO.STATS           OUTPUT {OUTPUT}.stats                    STATS     NONE       FPA        TRUE      NONE
+PSASTRO.CONFIG          OUTPUT {OUTPUT}.psastro.mdc              TEXT      NONE       FPA        TRUE      NONE
+		        									        
+PSWARP.OUTPUT           OUTPUT {OUTPUT}.fits                     IMAGE     COMP_IMG   FPA        TRUE      NONE
+PSWARP.OUTPUT.MASK      OUTPUT {OUTPUT}.mask.fits                MASK      COMP_MASK  FPA        TRUE      NONE
+PSWARP.OUTPUT.WEIGHT    OUTPUT {OUTPUT}.wt.fits                  WEIGHT    COMP_WT    FPA        TRUE      NONE
+PSWARP.OUTPUT.SOURCES   OUTPUT {OUTPUT}.cmf                      CMF       NONE       FPA        TRUE      NONE
+PSWARP.BIN1             OUTPUT {OUTPUT}.b1.fits                  IMAGE     NONE       FPA        TRUE      NONE
+PSWARP.BIN2             OUTPUT {OUTPUT}.b2.fits                  IMAGE     NONE       FPA        TRUE      NONE
+PSWARP.CONFIG           OUTPUT {OUTPUT}.pswarp.mdc               TEXT      NONE       FPA        TRUE      NONE
+		        									        
+SKYCELL.STATS           OUTPUT {OUTPUT}.stats                    STATS     NONE       FPA        TRUE      NONE
+SKYCELL.TEMPLATE        OUTPUT {OUTPUT}.skycell                  SKYCELL   NONE       FPA        TRUE      NONE
+		        									        
+PPSUB.OUTPUT            OUTPUT {OUTPUT}.fits                     IMAGE     COMP_SUB   FPA        TRUE      NONE
+PPSUB.OUTPUT.MASK       OUTPUT {OUTPUT}.mask.fits                MASK      COMP_MASK  FPA        TRUE      NONE
+PPSUB.OUTPUT.WEIGHT     OUTPUT {OUTPUT}.wt.fits                  WEIGHT    COMP_WT    FPA        TRUE      NONE
+PPSUB.OUTPUT.KERNELS    OUTPUT {OUTPUT}.subkernel                SUBKERNEL NONE       FPA        TRUE      NONE
+PPSUB.OUTPUT.JPEG1	OUTPUT {OUTPUT}.b1.jpg                   JPEG      NONE       FPA        TRUE      NONE
+PPSUB.OUTPUT.JPEG2	OUTPUT {OUTPUT}.b2.jpg                   JPEG      NONE       FPA        TRUE      NONE
+PPSUB.CONFIG            OUTPUT {OUTPUT}.ppSub.mdc                TEXT      NONE       FPA        TRUE      NONE
+											        
+PPSTACK.OUTPUT          OUTPUT {OUTPUT}.fits                     IMAGE     COMP_IMG   FPA        TRUE      NONE
+PPSTACK.OUTPUT.MASK     OUTPUT {OUTPUT}.mask.fits                MASK      COMP_MASK  FPA        TRUE      NONE
+PPSTACK.OUTPUT.WEIGHT   OUTPUT {OUTPUT}.wt.fits                  WEIGHT    COMP_WT    FPA        TRUE      NONE
+PPSTACK.TARGET.PSF      OUTPUT {OUTPUT}.target.psf               PSF       NONE       CHIP       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
+PPSTACK.CONFIG          OUTPUT {OUTPUT}.ppStack.mdc              TEXT      NONE       FPA        TRUE      NONE
+
+PPSTAMP.OUTPUT          OUTPUT {OUTPUT}.fits                     IMAGE     NONE       FPA        TRUE      NONE
+PPSTAMP.CHIP            OUTPUT {OUTPUT}.ch.fits                  IMAGE     NONE       CHIP       FALSE     MEF
+		        									        
+PPSIM.OUTPUT            OUTPUT {OUTPUT}.fits                     IMAGE     NONE       FPA        TRUE      MEF
+PPSIM.FAKE.CHIP        	OUTPUT {OUTPUT}.fake.fits     		 IMAGE     NONE       FPA        TRUE      SIMPLE
+PPSIM.FORCE.CHIP       	OUTPUT {OUTPUT}.force.fits     		 IMAGE     NONE       FPA        TRUE      SIMPLE
+PPSIM.SOURCES           OUTPUT {OUTPUT}.cmf                      CMF       NONE       FPA        TRUE      MEF
+PPSIM.FAKE.SOURCES     	OUTPUT {OUTPUT}.fake.cmf                 CMF       NONE       FPA        TRUE      NONE
+PPSIM.FORCE.SOURCES    	OUTPUT {OUTPUT}.force.cmf                CMF       NONE       FPA        TRUE      NONE
+											        
+PPARITH.OUTPUT.IMAGE   	OUTPUT {OUTPUT}.fits                     IMAGE     COMP_IMG   CHIP       TRUE      NONE
+PPARITH.OUTPUT.MASK    	OUTPUT {OUTPUT}.fits                     MASK      COMP_MASK  CHIP       TRUE      NONE
+		       										        
+LOG.IMFILE             	OUTPUT {OUTPUT}.{CHIP.NAME}.log          TEXT      NONE       CHIP       TRUE      NONE
+LOG.EXP                	OUTPUT {OUTPUT}.log                      TEXT      NONE       FPA        TRUE      NONE
+		       										        
+TRACE.IMFILE           	OUTPUT {OUTPUT}.{CHIP.NAME}.trace        TEXT      NONE       CHIP       TRUE      NONE
+TRACE.EXP              	OUTPUT {OUTPUT}.trace                    TEXT      NONE       FPA        TRUE      NONE
+
+# {FPA.OBS}
+# {FPA.NAME}
+# {CHIP.NAME}
+# {CHIP.ID}
+# {CHIP.N}
+# {CHIP.NUM}
+# {CELL.NAME}
+# {CELL.N}
+# {CELL.NUM}
+# {EXTNAME}
+# {FILTER}
+# {FILTER.ID}
+# {CAMERA}
+# {INSTRUMENT}
+# {DETECTOR}
+# {TELESCOPE}
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/fitstypes.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/fitstypes.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/fitstypes.mdc	(revision 22322)
@@ -0,0 +1,87 @@
+
+# BITPIX is the bits per pixel for writing the output data
+# COMP = NONE|RICE|GZIP|HCOMPRESS|PLIO is the compression algorithm
+# TILE.[XYZ] are the tile sizes.  0 means entire the dimension, so (0,1,1) forms tiles from rows
+# NOISE [0..16] is the number of "noise bits" to preserve when quantising floating point data; 16 for no loss
+# HSCALE is the scale factor for lossy compression with HCOMPRESS; 0 or 1 for none; 2*RMS --> 10x compression
+# HSMOOTH is the smoothing to apply to HCOMPRESSed data when decompressing; 0 for none
+
+# BITPIX(S32) is the bits per pixel for writing the output data
+# COMPRESSION(STR) = NONE|RICE|GZIP|HCOMPRESS|PLIO is the compression algorithm
+# TILE.[XYZ](S32) are the tile sizes.  0 means entire the dimension, so (0,1,1) forms tiles from rows
+# NOISE(S32) [0..16] is the number of "noise bits" to preserve when quantising floating point data
+# HSCALE(S32) is the scale factor for lossy compression with HCOMPRESS; 0 or 1 for none; 2*RMS --> 10x compression
+# HSMOOTH(S32) is the smoothing to apply to HCOMPRESSed data when decompressing; 0 for none
+# SCALING(STR) = NONE|RANGE|STDEV_POSITIVE|STDEV_NEGATIVE|STDEV_BOTH|MANUAL is the scaling scheme
+# BSCALE(F32) is the manual scaling to apply (when SCALING = MANUAL)
+# BZERO(F32) is the manual zero-point to apply (when SCALING = MANUAL)
+# STDEV.BITS(S32) is the number of bits to map to a standard deviation (when SCALING = STDEV_*)
+# STDEV.NUM(F32) is the number of standard deviations to the edge (when SCALING = STDEV_NEGATIVE|STDEV_POSITIVE)
+# FLOAT(STR) is the name of a custom floating-point type
+
+DET_IMAGE	METADATA
+	BITPIX		S32	-32
+END
+DET_MASK	METADATA
+	BITPIX		S32	8
+END
+DET_WEIGHT	METADATA
+	BITPIX		S32	-32
+END
+
+SKY_IMAGE	METADATA
+	BITPIX		S32	-32
+END
+SKY_MASK	METADATA
+	BITPIX		S32	8
+END
+SKY_WEIGHT	METADATA
+	BITPIX		S32	-32
+END
+
+# Compressed positive image
+COMP_IMG	METADATA
+	BITPIX		S32	16
+	SCALING		STR	STDEV_POSITIVE
+	STDEV.BITS	S32	4
+	STDEV.NUM	F32	10
+	COMPRESSION	STR	RICE
+	TILE.X		S32	0
+	TILE.Y		S32	1
+	TILE.Z		S32	1
+	NOISE		S32	8
+END
+# Compressed mask
+COMP_MASK	METADATA
+	BITPIX		S32	8
+	COMPRESSION	STR	PLIO
+	TILE.X		S32	0
+	TILE.Y		S32	1
+	TILE.Z		S32	1
+	NOISE		S32	8
+END
+# Compressed weight image
+COMP_WT		METADATA
+	BITPIX		S32	16
+	SCALING		STR	STDEV_POSITIVE
+	STDEV.BITS	S32	4
+	STDEV.NUM	F32	10
+	COMPRESSION	STR	RICE
+	TILE.X		S32	0
+	TILE.Y		S32	1
+	TILE.Z		S32	1
+	NOISE		S32	8
+END
+# Compressed subtraction image
+COMP_SUB	METADATA
+	BITPIX		S32	16
+	SCALING		STR	STDEV_BOTH
+	STDEV.BITS	S32	4
+	STDEV.NUM	F32	5
+	COMPRESSION	STR	RICE
+	TILE.X		S32	0
+	TILE.Y		S32	1
+	TILE.Z		S32	1
+	NOISE		S32	8
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/jpeg.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/jpeg.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/jpeg.mdc	(revision 22322)
@@ -0,0 +1,183 @@
+# JPEG options for files
+# Each METADATA entry should contain:
+# * COLORMAP: Name of colormap [grayscale,-grayscale,rainbow,heat]
+# * SCALE.MODE: Mode for scaling [RANGE,FRACTION,VALUE]
+# * SCALE.MIN: Minimum for scaling
+# * SCALE.MAX: Maximum for scaling
+
+PPIMAGE.JPEG1	METADATA
+	COLORMAP	STR	-greyscale
+	SCALE.MODE	STR	RANGE
+	SCALE.MIN	F32	 -5.0
+	SCALE.MAX	F32	+10.0
+END
+
+PPIMAGE.JPEG2	METADATA
+	COLORMAP	STR	-greyscale
+	SCALE.MODE	STR	RANGE
+	SCALE.MIN	F32	 -5.0
+	SCALE.MAX	F32	+10.0
+END
+
+PPSUB.OUTPUT.JPEG1	METADATA
+	COLORMAP	STR	-greyscale
+	SCALE.MODE	STR	RANGE
+	SCALE.MIN	F32	 -5.0
+	SCALE.MAX	F32	+10.0
+END
+
+PPSUB.OUTPUT.JPEG2	METADATA
+	COLORMAP	STR	-greyscale
+	SCALE.MODE	STR	RANGE
+	SCALE.MIN	F32	 -5.0
+	SCALE.MAX	F32	+10.0
+END
+
+PPSTACK.OUTPUT.JPEG1	METADATA
+	COLORMAP	STR	-greyscale
+	SCALE.MODE	STR	FRACTION
+	SCALE.MIN	F32	0.90
+	SCALE.MAX	F32	1.10
+END
+
+PPSTACK.OUTPUT.JPEG2	METADATA
+	COLORMAP	STR	-greyscale
+	SCALE.MODE	STR	FRACTION
+	SCALE.MIN	F32	0.95
+	SCALE.MAX	F32	1.05
+END
+
+
+
+
+BIAS	METADATA
+	PPIMAGE.JPEG1	 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	RANGE
+		SCALE.MIN	F32	-5.0
+		SCALE.MAX	F32	+5.0
+	END
+
+	PPIMAGE.JPEG2	 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	RANGE
+		SCALE.MIN	F32	-5.0
+		SCALE.MAX	F32	+5.0
+	END
+END
+
+
+FLAT	METADATA
+	PPIMAGE.JPEG1	 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	RANGE
+		SCALE.MIN	F32	-5.0
+		SCALE.MAX	F32	+5.0
+	END
+
+	PPIMAGE.JPEG2	 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	RANGE
+		SCALE.MIN	F32	-5.0
+		SCALE.MAX	F32	+5.0
+	END
+END
+
+
+FRINGE	METADATA
+	PPIMAGE.JPEG1	 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	RANGE
+		SCALE.MIN	F32	-2.0
+		SCALE.MAX	F32	+3.0
+	END
+
+	PPIMAGE.JPEG2	 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	RANGE
+		SCALE.MIN	F32	-2.0
+		SCALE.MAX	F32	+3.0
+	END
+END
+
+
+MASK	METADATA
+	PPIMAGE.JPEG1		 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	FRACTION
+		SCALE.MIN	F32	0.90
+		SCALE.MAX	F32	1.10
+	END
+
+	PPIMAGE.JPEG2		 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	FRACTION
+		SCALE.MIN	F32	0.95
+		SCALE.MAX	F32	1.05
+	END
+END
+
+BIAS_RESID	METADATA
+	PPIMAGE.JPEG1		 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	VALUE
+		SCALE.MIN	F32	-5.0
+		SCALE.MAX	F32	+5.0
+	END
+
+	PPIMAGE.JPEG2		 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	VALUE
+		SCALE.MIN	F32	-5.0
+		SCALE.MAX	F32	+5.0
+	END
+END
+
+FLAT_RESID	METADATA
+	PPIMAGE.JPEG1		 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	FRACTION
+		SCALE.MIN	F32	0.95
+		SCALE.MAX	F32	1.05
+	END
+
+	PPIMAGE.JPEG2		 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	FRACTION
+		SCALE.MIN	F32	0.95
+		SCALE.MAX	F32	1.05
+	END
+END
+
+
+FRINGE_RESID	METADATA
+	PPIMAGE.JPEG1		 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	RANGE
+		SCALE.MIN	F32	-2.0
+		SCALE.MAX	F32	+3.0
+	END
+
+	PPIMAGE.JPEG2		 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	RANGE
+		SCALE.MIN	F32	-2.0
+		SCALE.MAX	F32	+3.0
+	END
+END
+
+MASK_RESID	METADATA
+	PPIMAGE.JPEG1		 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	FRACTION
+		SCALE.MIN	F32	0.95
+		SCALE.MAX	F32	1.05
+	END
+
+	PPIMAGE.JPEG2		 METADATA
+		COLORMAP	STR	-greyscale
+		SCALE.MODE	STR	FRACTION
+		SCALE.MIN	F32	0.95
+		SCALE.MAX	F32	1.05
+	END
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/masks.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/masks.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/masks.config	(revision 22322)
@@ -0,0 +1,48 @@
+### Recipe specifying values for various mask concepts
+
+# This file defines bit values for various abstract mask concepts.  It
+# is not necessary for all bit values to be unique.
+
+# Note that it is necessary to leave at least a single unassigned bit
+# for internal marking (or ppImage will complain)
+
+
+# The following values are TEMPORARY, to be used only until we can get
+# the masking situation sorted out (PAP, 2008-09-09)
+# The following values are constrained by the fact that the GPC1 mask values are set:
+DETECTOR	U8	0x02		# Something is wrong with the detector =2
+FLAT		U8	0x04		# Pixel doesn't flat-field properly =4
+DARK		U8	0x40		# Pixel doesn't dark-subtract properly =64
+# Filling in the rest with whatever makes some sense
+BLANK		U8	0x01		# Pixel doesn't contain valid data
+RANGE		U8	0x01		# Pixel is out-of-range of linearity
+SAT		U8	0x01		# Pixel is saturated
+BAD		U8	0x01		# Pixel is low
+POOR.WARP	U8	0x08		# Pixel is poor after convolution with a bad pixel
+BAD.WARP	U8	0x10		# Pixel is bad after convolution with a bad pixel
+CR		U8	0x20		# Pixel contains a cosmic ray
+GHOST		U8	0x20		# Pixel contains an optical ghost
+
+
+###### The following values are what I'm aiming to have in the long term (PAP, 2008-09-09)
+###
+#### Detector-intrinsic; these will default to DETECTOR
+###DETECTOR	U8	0x01		# Something is wrong with the detector
+###DARK		U8	0x02		# Pixel doesn't dark-subtract properly
+###FLAT		U8	0x01		# Pixel doesn't flat-field properly
+###BLANK	U8	0x01		# Pixel doesn't contain valid data
+###
+#### Detector-extrinsic; these will default to RANGE
+###RANGE	U8	0x04		# Pixel is out-of-range of linearity
+###SAT		U8	0x04		# Pixel is saturated
+###LOW		U8	0x04		# Pixel is low
+###
+#### Convolution: pixels that touched a bad pixel
+###POOR		U8	0x08		# Pixel is poor after convolution with a bad pixel
+###CONV		U8	0x10		# Pixel is bad after convolution with a bad pixel
+###
+#### Objects: real signal that should be ignored
+###CR		U8	0x20		# Pixel contains a cosmic ray
+###GHOST	U8	0x40		# Pixel contains an optical ghost
+###
+####MARK		U8	0x80		# RESERVED for MARK
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppImage.config	(revision 22322)
@@ -0,0 +1,1340 @@
+### ppImage recipe configuration file
+### Here we have turned on our 'best guess' defaults for a simple camera
+### Camera-specific options may be turned on by recipes in the camera configuration
+
+# List of tasks to perform
+NONLIN           BOOL    FALSE           # Non-linearity correction; not implemented
+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
+
+# XXX EAM : this is being deprecated
+# MASK.VALUE       STR     SAT,BAD,DETECTOR # Mask pixels with these attributes
+
+MASK.BUILD       BOOL    FALSE           # Build internal mask image
+REPLACE.MASKED   BOOL    FALSE           # Fill in masked pixels
+REPLACE.MODE     STR     VALUE           # VALUE, MODEL
+REPLACE.VALUE    F32     0.0             # Fill in masked pixels with this value (if mode == value)
+
+WEIGHT.BUILD     BOOL    FALSE           # Build internal weight image
+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?
+
+# output data formats to save
+BASE.FITS        BOOL    FALSE           # Save base detrended image?
+BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+FPA1.FITS        BOOL    FALSE           # Save 1st binned fpa image? 
+FPA2.FITS        BOOL    FALSE           # Save 2nd binned fpa image? 
+BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+BIN1.JPEG        BOOL    FALSE           # Save 1st binned jpeg?
+BIN2.JPEG        BOOL    FALSE           # Save 2nd binned jpeg?
+
+## if we use multithreaded detrending, detrend this number of rows per thread
+SCAN.ROWS        S32     100
+
+# Non-linearity correction
+NONLIN.SOURCE           STR     CHIP.NAME       # How to determine the source
+NONLIN.DATA             STR     nonlin.dat      # Filename for lookup table
+
+OLDDARK		BOOL	FALSE		# Use old-style darks?
+
+# examples of other possible non-linearity correction representations
+#@NONLIN.DATA           F32     0.0 1.001 0.001 # A polynomial
+#NONLIN.DATA            METADATA                # Source of non-linearity data
+#       ccd00           STR     nonlin00.dat    # A lookup table 
+#       @ccd01          F32     0.0 1.001 0.001 # A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.SINGLE         BOOL    TRUE            # Reduce overscan to a single value?
+OVERSCAN.FIT            STR     NONE            # NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER          S32     5               # Order of polynomial fit
+OVERSCAN.STAT           STR     MEAN            # MEAN | MEDIAN
+OVERSCAN.BOXCAR		S32	0		# Boxcar smoothing radius
+OVERSCAN.GAUSS		F32	0.0		# Gaussian smoothing sigma
+OVERSCAN.CONSTANT	BOOL	FALSE		# Apply a known, fixed value?
+OVERSCAN.VALUE	        F32	0.0		# value to apply, if requested
+
+# Fringe subtraction options
+FRINGE.ITER     S32     10              # Number of rejection iterations for fringe solution
+FRINGE.REJ      F32     2.0             # Rejection threshold for fringe solution
+FRINGE.KEEP     F32     0.5             # Minimum fraction to keep in fringe solution
+
+# binned output image options
+BIN1.XBIN               S32      4
+BIN1.YBIN               S32      4
+BIN2.XBIN               S32     16
+BIN2.YBIN               S32     16
+
+PPIMAGE.JPEG1  METADATA
+  COLORMAP      STR     -greyscale
+  SCALE.MODE    STR     RANGE
+  SCALE.MIN     F32      -5.0
+  SCALE.MAX     F32     +10.0
+END
+
+PPIMAGE.JPEG2  METADATA
+  COLORMAP      STR     -greyscale
+  SCALE.MODE    STR     RANGE
+  SCALE.MIN     STR      -5.0
+  SCALE.MAX     STR     +10.0
+END
+
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS  STR UNDEF
+
+DETREND.CONSTRAINTS  METADATA
+END
+
+################################################################################
+# Diffferent processing options, which may be loaded symbolically
+################################################################################
+
+# No operation except rebinning
+PPIMAGE_BIN        METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# No operation except rebinning and potential normalisation
+PPIMAGE_N          METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan subtraction only
+PPIMAGE_O          METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Bias subtraction only
+PPIMAGE_B          METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Dark subtraction only
+PPIMAGE_D          METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Dark subtraction only
+PPIMAGE_M          METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  REPLACE.MASKED   BOOL    TRUE            # Fill in masked pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Shutter correction only
+PPIMAGE_S          METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    TRUE            # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Flat-fielding only
+PPIMAGE_F          METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # 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?
+END
+
+# Fringe correction only
+PPIMAGE_R          METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Photometry only
+PPIMAGE_P          METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Photometry & Astrometry
+PPIMAGE_A          METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astromtery per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias
+PPIMAGE_OB         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark
+PPIMAGE_OBD        METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS       METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR     METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_DET_ONLY   METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_FLATCORR    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+
+  DETREND.CONSTRAINTS  METADATA
+    FLAT METADATA
+      DETTYPE STR FLAT_RAW
+    END
+  END   
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_MASKPHOT   METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Save JPEG from BIN1
+PPIMAGE_JPEG       METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    TRUE            # Save 2nd binned jpeg?
+END
+
+# Save JPEG from BIN1
+PPIMAGE_J1         METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    FALSE           # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+END
+
+# Save JPEG from BIN2
+PPIMAGE_J2         METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    TRUE            # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+END
+
+# Overscan, bias, photometry, astrometry (for, eg, summit ISP analysis)
+PPIMAGE_OA         METADATA
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE          # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    TRUE          # Save 2nd binned jpeg?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, photometry (for quick analysis)
+PPIMAGE_OP         METADATA
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    TRUE            # Save 2nd binned jpeg?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Photometry and astrometry only (for pre-reduced data)
+PPIMAGE_PA         METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    TRUE            # Save 2nd binned jpeg?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# JPEG images for different types of residual images
+# Save JPEG from BIN1 for bias
+PPIMAGE_J1_RESID_B      METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    FALSE           # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+
+  PPIMAGE.JPEG1    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     VALUE
+    SCALE.MIN      F32     -5.0
+    SCALE.MAX      F32     +5.0
+  END
+END
+
+# Save JPEG from BIN2 for bias
+PPIMAGE_J2_RESID_B        METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    TRUE            # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+
+  PPIMAGE.JPEG2    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     VALUE
+    SCALE.MIN      F32     -5.0
+    SCALE.MAX      F32     +5.0
+  END
+END
+
+# Save JPEG from BIN1 for flat
+PPIMAGE_J1_RESID_F      METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    FALSE           # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+
+  PPIMAGE.JPEG1    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     FRACTION
+    SCALE.MIN      F32     0.95
+    SCALE.MAX      F32     1.05
+  END
+END
+
+# Save JPEG from BIN2 for flat
+PPIMAGE_J2_RESID_F        METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    TRUE            # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+		   
+  PPIMAGE.JPEG2    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     FRACTION
+    SCALE.MIN      F32     0.95
+    SCALE.MAX      F32     1.05
+  END
+END
+
+# Save JPEG from BIN1 for flat
+PPIMAGE_J1_RESID_R      METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    FALSE           # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+		   
+  PPIMAGE.JPEG1    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     RANGE
+    SCALE.MIN      F32     -2.0
+    SCALE.MAX      F32     +3.0
+  END
+END
+
+# Save JPEG from BIN2 for flat
+PPIMAGE_J2_RESID_R        METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    TRUE            # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+		   
+  PPIMAGE.JPEG2    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     RANGE
+    SCALE.MIN      F32     -2.0
+    SCALE.MAX      F32     +3.0
+  END
+END
+
+# JPEG images for different types of positive images
+# Save JPEG from BIN1 for bias
+PPIMAGE_J1_IMAGE_B      METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    FALSE           # Save 2nd binned jpeg?
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+		   
+  PPIMAGE.JPEG1    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     RANGE
+    SCALE.MIN      F32     -5.0
+    SCALE.MAX      F32     +5.0
+  END
+END
+
+# Save JPEG from BIN2 for bias
+PPIMAGE_J2_IMAGE_B        METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS  BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS  BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    TRUE            # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+
+  PPIMAGE.JPEG2  METADATA
+    COLORMAP      STR     -greyscale
+    SCALE.MODE    STR     RANGE
+    SCALE.MIN     F32     -5.0
+    SCALE.MAX     F32     +5.0
+  END
+END
+
+# Save JPEG from BIN1 for flat
+PPIMAGE_J1_IMAGE_F      METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS  BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS  BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    FALSE           # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+
+  PPIMAGE.JPEG1  METADATA
+    COLORMAP      STR     -greyscale
+    SCALE.MODE    STR     RANGE
+    SCALE.MIN     F32     -5.0
+    SCALE.MAX     F32     +5.0
+  END
+END
+
+# Save JPEG from BIN2 for flat
+PPIMAGE_J2_IMAGE_F        METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS  BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS  BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    TRUE            # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+
+  PPIMAGE.JPEG2  METADATA
+    COLORMAP      STR     -greyscale
+    SCALE.MODE    STR     RANGE
+    SCALE.MIN     F32     -5.0
+    SCALE.MAX     F32     +5.0
+  END
+END
+
+# Save JPEG from BIN1 for flat
+PPIMAGE_J1_IMAGE_R      METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS  BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS  BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    FALSE           # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+
+  PPIMAGE.JPEG1  METADATA
+    COLORMAP      STR     -greyscale
+    SCALE.MODE    STR     RANGE
+    SCALE.MIN     F32     -2.0
+    SCALE.MAX     F32     +3.0
+  END
+END
+
+# Save JPEG from BIN2 for flat
+PPIMAGE_J2_IMAGE_R        METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS  BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS  BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    TRUE            # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+
+  PPIMAGE.JPEG2  METADATA
+    COLORMAP      STR     -greyscale
+    SCALE.MODE    STR     RANGE
+    SCALE.MIN     F32     -2.0
+    SCALE.MAX     F32     +3.0
+  END
+END
+
+# Save JPEG from BIN1 for mask
+PPIMAGE_J1_IMAGE_M        METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS  BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS  BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    FALSE           # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+
+  PPIMAGE.JPEG1    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     FRACTION
+    SCALE.MIN      F32     0.90
+    SCALE.MAX      F32     1.10
+  END
+END
+
+# Save JPEG from BIN2 for mask
+PPIMAGE_J2_IMAGE_M        METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS  BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS  BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    TRUE            # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+
+  PPIMAGE.JPEG2    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     FRACTION
+    SCALE.MIN      F32     0.95
+    SCALE.MAX      F32     1.05
+  END
+END
+
+# Save JPEG from BIN1 for mask
+PPIMAGE_J1_RESID_M        METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS  BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS  BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    TRUE            # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    FALSE           # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+
+  PPIMAGE.JPEG1    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     FRACTION
+    SCALE.MIN      F32     0.95
+    SCALE.MAX      F32     1.05
+  END
+END
+
+# Save JPEG from BIN2 for mask
+PPIMAGE_J2_RESID_M        METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS  BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS  BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    TRUE            # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+
+  PPIMAGE.JPEG2    METADATA
+    COLORMAP       STR     -greyscale
+    SCALE.MODE     STR     FRACTION
+    SCALE.MIN      F32     0.95
+    SCALE.MAX      F32     1.05
+  END
+END
+
+# JPEG images for different types of residual images
+# Save JPEG from BIN1 for bias
+PPIMAGE_MOSAIC     METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    FALSE           # Save 2nd binned jpeg?
+  FPA1.FITS        BOOL    TRUE            # Save 1st binned fpa image? 
+  FPA2.FITS        BOOL    FALSE           # Save 2nd binned fpa image? 
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+END
+
+# generate chip-mosaiced image
+PPIMAGE_CHIPMOSAIC     METADATA
+  OVERSCAN         BOOL    FALSE           # Overscan subtraction
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS        BOOL    FALSE           # Save base image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  BIN1.FITS        BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS        BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG        BOOL    FALSE           # Save 1st binned jpeg?
+  BIN2.JPEG        BOOL    FALSE           # Save 2nd binned jpeg?
+  FPA1.FITS        BOOL    FALSE            # Save 1st binned fpa image? 
+  FPA2.FITS        BOOL    FALSE           # Save 2nd binned fpa image? 
+  BIN1.XBIN        S32     1               # Image is already binned
+  BIN1.YBIN        S32     1               # Image is already binned
+  BIN2.XBIN        S32     1               # Image is already binned
+  BIN2.YBIN        S32     1               # Image is already binned
+END
+
+# For SDSS cameras
+PPIMAGE_OA_SOFT   METADATA
+END
+
+PPIMAGE_OP_SOFT   METADATA
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppMerge.config	(revision 22322)
@@ -0,0 +1,100 @@
+# Recipe configuration for ppMerge
+
+ROWS            S32     128		# Number of rows to read at once
+ELECTRONS       F32     100.0           # Minimum number of electrons for useful signal
+SAMPLE          S32     100000          # Sampling factor for measuring the background
+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
+NKEEP		S32	5		# Minimum number of pixels in stack to keep
+WEIGHTS		BOOL	FALSE		# Use image weights in combination?
+FRINGE.NUM	S32	10000		# Number of fringe regions
+FRINGE.SIZE	S32	5		# Half-size of fringe regions
+FRINGE.XSMOOTH	S32	5		# Number of smoothing regions in x
+FRINGE.YSMOOTH	S32	11		# Number of smoothing regions in y
+SHUTTER.SIZE	S32	128		# Size for shutter measurement regions
+MASK.SUSPECT	F32	5.0		# Threshold for suspect pixels (sigma)
+MASK.BAD	F32	0.2		# Threshold for bad pixels
+MASK.MODE	STR	FRACTION	# Mode for identifying bad pixels in the suspect map
+MASK.CHIPSTATS	BOOL	TRUE		# Measure stats for masking by chip (otherwise by readout)?
+MASK.GROW	S32	0		# Grow bad pixels by this radius
+MASK.SET.VALUE	STR	FLAT		# set this bit in the output mask
+
+COMBINE		STR	CLIPPED		# Statistic to use for combination
+MEAN		STR	ROBUST_MEDIAN	# Statistic to use to measure the mean
+STDEV		STR	ROBUST_STDEV	# Statistic to use to measure the stdev
+
+MASK.SMOOTH.SUSPECT BOOL TRUE           # smooth the suspect-pixel image before making mask
+MASK.SMOOTH.SCALE   F32  3.0            # sigma (pixels) of smoothing kernel
+
+STATS.BY.CHIP   BOOL    TRUE            # measure stats for masking by chip (or by readout)
+MASK.GROW.NPIX  S32     3               # measure stats for masking by chip (or by readout)
+
+# Ordinates for fitting dark current
+DARK.ORDINATES	METADATA
+	CELL.DARKTIME	S32	1	# Traditional dark current term
+END
+DARK.NORM	STR	NONE		# Dark normalisation concept
+
+# Bias combination --- don't want min/max rejection
+PPMERGE_BIAS	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
+	WEIGHTS		BOOL	FALSE		# Use image weights?
+	COMBINE		STR	CLIPPED		# Statistic to use for combination: 
+END
+
+
+# Dark combination --- don't want min/max rejection
+# More aggressive clipping than bias, so as to remove CRs
+PPMERGE_DARK	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
+	WEIGHTS		BOOL	TRUE		# Use image weights?
+	COMBINE		STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Flat combination --- use min/max rejection
+PPMERGE_FLAT	METADATA
+	REJ		F32	3.0		# Rejection threshold (sigma)
+	ITER		S32	1		# Number of rejection iterations
+	FRACHIGH	F32	0.3		# Fraction of high pixels to reject immediately
+	FRACLOW		F32	0.1		# Fraction of low pixels to reject immediately
+	NKEEP		S32	5		# Minimum number of pixels in stack to keep
+	WEIGHTS		BOOL	TRUE		# Use image weights?
+	COMBINE		STR	MEAN		# Statistic to use for combination: 
+END
+
+
+# Fringe combination --- already included in default, above
+PPMERGE_FRINGE	METADATA
+	FRACHIGH	F32	0.1		# Fraction of high pixels to reject immediately
+	WEIGHTS		BOOL	TRUE		# Use image weights?
+END
+
+# Mask generation --- already included in default, above
+PPMERGE_DARKMASK METADATA
+	ITER		S32	2		# Number of iterations
+	MASK.BAD	F32	0.2		# Threshold for bad pixels (sigma)
+	MASK.MODE	STR	FRACTION	# Mode for identifying bad pixels in the suspect map
+        MASK.SET.VALUE	STR	DARK		# set this bit in the output mask
+END
+
+# Mask generation --- already included in default, above
+PPMERGE_FLATMASK METADATA
+	ITER		S32	2		# Number of iterations
+	MASK.BAD	F32	0.2		# Threshold for bad pixels (sigma)
+	MASK.MODE	STR	FRACTION	# Mode for identifying bad pixels in the suspect map
+        MASK.SET.VALUE	STR	FLAT		# set this bit in the output mask
+END
+
+# Shutter generation --- already included in default, above
+PPMERGE_SHUTTER	METADATA
+	REJ		F32	2.0		# Rejection threshold (sigma)
+	ITER		S32	1		# Number of rejection iterations
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppSim.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppSim.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppSim.config	(revision 22322)
@@ -0,0 +1,146 @@
+### Recipe values for ppSim, the Pan-STARRS data simulator
+
+## XXX need to consider this better: used to run / skip fake, should be a function of chip (and context?)
+SKIP.FAKE       BOOL    TRUE
+
+IMAGE.TYPE      STR     BIAS            # default image type
+
+BIAS            BOOL    TRUE
+BIAS.LEVEL	F32	1000.0		# Bias level (e)
+BIAS.RANGE	F32	20.0		# Range of bias values (e)
+BIAS.ORDER	S32	7		# Order for overscan polynomial
+
+DARK            BOOL    TRUE
+DARK.RATE	F32	5.0		# Dark current (e/s)
+
+FLAT            BOOL    TRUE
+FLAT.SIGMA	F32	3.0		# Flat gaussian width (FPA sizes)
+FLAT.RATE	F32	2000.0		# Flat illumination rate (e/s)
+
+SHUTTER.TIME	F32	1.0		# Time for shutter to fully open
+SCATTER.FRAC    F32     0.0
+
+SKY		BOOL	TRUE		# Sky illumination rate (e/s)
+SKY.RATE	F32	20.0		# Sky illumination rate (e/s)
+
+PHOTOM          BOOL    FALSE           # perform photometry on the faked stars?
+
+FORCED.PHOT     BOOL    FALSE           # measure forced photometry for specific points?
+FORCED.CATDIR   STR     NONE            # source of the forced photometry positions
+FORCED.PHOTCODE STR     r               # source of the forced photometry positions
+
+STARS.REAL	BOOL	TRUE		# Add stars from a catalogue?
+MATCH.DENSITY   BOOL    TRUE            # significance of faintest sources
+
+STARS.FAKE      BOOL    TRUE		# Add fake stars, randomly distributed?
+STARS.LUM	F32	0.6		# Stellar luminosity function slope (magnitude slope)
+STARS.MAG	F32	15.5		# Brightest magnitude for fake stars
+STARS.DENSITY	F32     50.0		# Stellar density (per square degree) at the brightest magnitude
+STARS.SIGMA.LIM F32      5.0            # significance of faintest sources
+
+STARS.FLAT.LUM  BOOL    FALSE           # use a (non-physical) constant number of stars per mag bin?
+STARS.FLAT.NUM  S32     5000            # number of stars to generate for flat magnitude distribution
+PSF.MODEL       STR     PS_MODEL_QGAUSS
+
+GALAXY.FAKE     BOOL    FALSE		# Generate fake galaxies?
+
+GALAXY.LUM	  F32	-0.8		# Stellar luminosity function slope
+GALAXY.MAG	  F32	11.0		# Brightest magnitude for fake stars
+GALAXY.DENSITY	  F32	6.0		# Galaxy density (per square degree) at the brightest magnitude
+
+GALAXY.RMAJOR.MIN F32	2.0		# Minimum semi-major axis for galaxy models
+GALAXY.RMAJOR.MAX F32	10.0		# Maximum semi-major axis for galaxy models
+
+GALAXY.ARATIO.MIN F32	1.0		# Minimum axis ratio for galaxy models
+GALAXY.ARATIO.MAX F32	0.2		# Maximum axis ratio for galaxy models
+
+GALAXY.INDEX.MIN F32    0.25		# Minimum Sersic index for galaxy models
+GALAXY.INDEX.MAX F32	0.500           # Maximum Sersic index for galaxy models
+
+GALAXY.THETA.MIN F32    0.0		# Minimum position angle for galaxy models
+GALAXY.THETA.MAX F32	3.14            # Maximum position angle for galaxy models
+
+GALAXY.GRID      BOOL   FALSE		# Generate a (regular) grid of galaxy models?
+GALAXY.GRID.DX   S32    200             # Spacing between galaxies in grid in x
+GALAXY.GRID.DY   S32    200             # Spacing between galaxies in grid in y
+
+GALAXY.MODEL     STR    PS_MODEL_SERSIC
+
+GALAXY.MODEL     STR    PS_MODEL_SERSIC
+
+BADPIX.SEED	U64	123456789	# Seed for RNG in creating deterministic bad pixels
+BADPIX.FRAC	F32	0.0001		# Fraction of bad pixels
+
+### The following options are used if not defined by the concepts (e.g., usually read from headers)
+GAIN		F32	1.0		# Default gain (e/ADU)
+READNOISE	F32	10.0		# Default read noise (e)
+OVERSCAN.SIZE	S32	32		# Default overscan columns
+SATURATION	F32	65535		# Default saturation level (ADU)
+
+PIXEL.SCALE     F32     0.26            # pixel size in arcsec
+
+# filter-dependent parameters
+ZEROPTS MULTI
+
+ZEROPTS  METADATA
+  FILTER    STR  g
+  ZERO_PT   F32  24.0
+END
+ZEROPTS  METADATA
+  FILTER    STR  r
+  ZERO_PT   F32  25.15
+END
+ZEROPTS  METADATA
+  FILTER    STR  i
+  ZERO_PT   F32  25.00
+END
+ZEROPTS  METADATA
+  FILTER    STR  z
+  ZERO_PT   F32  24.50
+END
+ZEROPTS  METADATA
+  FILTER    STR  y
+  ZERO_PT   F32  24.00
+END
+
+# the following values are not set by default, but supplied if needed from arguments
+EXPTIME     F32     NAN                 # exposure time (seconds)
+RA          F32     NAN                 # exposure time (seconds)
+DEC         F32     NAN                 # exposure time (seconds)
+PA          F32     NAN                 # exposure time (seconds)
+SEEING      F32     NAN                 # exposure time (seconds)
+SCALE       F32     NAN                 # exposure time (seconds)
+ZEROPOINT   F32     NAN                 # exposure time (seconds)
+SKY.MAGS    F32     NAN                 # exposure time (seconds)
+
+FILTER      STR     NONE                # exposure time (seconds)
+BINNING     S32     1                   # exposure time (seconds)
+
+ADDFAKES    METADATA
+ BIAS            BOOL    FALSE
+ DARK            BOOL    FALSE
+ FLAT            BOOL    FALSE
+ SKY		 BOOL	 FALSE		# Sky illumination rate (e/s)
+ STARS.REAL  	 BOOL	 FALSE		# Add stars from a catalogue?
+ STARS.FAKE  	 BOOL	 TRUE		# Add stars from a catalogue?
+END
+
+FAKEPHOT    METADATA
+ BIAS            BOOL    FALSE
+ DARK            BOOL    FALSE
+ FLAT            BOOL    FALSE
+ SKY		 BOOL	 FALSE		# Sky illumination rate (e/s)
+ STARS.REAL  	 BOOL	 FALSE		# Add stars from a catalogue?
+ STARS.FAKE  	 BOOL	 TRUE		# Add stars from a catalogue?
+ PHOTOM          BOOL    FALSE           # perform photometry on the faked stars?
+ FORCED.PHOT     BOOL    TRUE
+ FORCED.CATDIR   STR     DVO.FORCE
+END
+
+# for a quick test, we are going to skip shutter and dark corrections
+FLATCORR    METADATA
+ SHUTTER.TIME	 F32	 0.0
+ DARK.RATE       F32     0.0         
+ SCATTER.FRAC    F32     0.1
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppStack.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppStack.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppStack.config	(revision 22322)
@@ -0,0 +1,36 @@
+# Recipe configuration for ppStack (image combination)
+
+ITER		S32	1		# Number of rejection iterations
+COMBINE.REJ	F32	4.0		# Rejection threshold in combination (sigma)
+MASK.VAL	STR	MASK.VALUE,BAD.WARP	# 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
+POOR.FRACTION	F32	0.1		# Maximum fraction of bad weight for poor pixels
+THRESHOLD.MASK	F32	0.8		# Threshold for mask deconvolution (0..1)
+IMAGE.REJ	F32	0.2		# Rejected pixel fraction threshold for rejecting entire image
+ROWS		S32	64		# Number of rows to read at once
+VARIANCE	BOOL	TRUE		# Use variance in rejection?
+SAFE		BOOL	TRUE		# Play safe when combining small number of values?
+BIN1		S32	4		# Binning factor for first level
+BIN2		S32	4		# Binning factor for second level
+
+RENORM		BOOL	FALSE		# Renormalise variance maps?
+RENORM.MEAN	STR	ROBUST_MEDIAN	# Statistic to use for mean in renormalisation
+RENORM.STDEV	STR	ROBUST_STDEV	# Statistic to use for stdev in renormalisation
+RENORM.WIDTH	S32	300		# Size of renormalisation boxes (pixels)
+
+SOURCE.RADIUS	F32	1.0		# Radius (pixels) for matching sources
+SOURCE.MIN	S32	15		# Minimum number of sources for merging source lists
+SOURCE.ITER	S32	2		# Number of rejection iterations for magnitude difference
+SOURCE.REJ	F32	2.0		# Rejection limit (sigma) for magnitude difference
+
+PSF.INSTANCES	S32	9		# Number of instances for PSF generation
+PSF.RADIUS	F32	20.0		# Radius for PSF generation
+PSF.ORDER	S32	2		# Order of spatial variation for PSF generation
+PSF.MODEL	STR	PS_MODEL_GAUSS	# Model for PSF generation
+
+TEMP.DIR	STR	/tmp		# Directory for temporary images
+TEMP.IMAGE	STR	conv.im.fits	# Suffix for temporary convolved images
+TEMP.MASK	STR	conv.mk.fits	# Suffix for temporary convolved masks
+TEMP.WEIGHT	STR	conv.wt.fits	# Suffix for temporary convolved weight maps
+TEMP.DELETE	BOOL	FALSE		# Delete temporary files on completion?
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppStats.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppStats.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppStats.config	(revision 22322)
@@ -0,0 +1,164 @@
+### Default ppStats recipe
+
+# Options governing statistics
+SAMPLE          F32     0.1     # Fraction of cell to sample
+DO.FIRST.READOUT.3D BOOL FALSE  # for 3D images, measure stats of first readout
+
+# Mask value to use for statistics.  this is only used for stand-alone
+# operations.  ppStats library calls pass a mask value as needed
+MASKVAL         STR     SAT,BAD 
+
+
+# Define the outputs as MULTI
+HEADER          MULTI
+CONCEPT         MULTI
+STAT            MULTI
+SUMMARY         MULTI
+
+# Add dummy values, to define the MULTI --- due to limitations in the MDC language
+HEADER		STR	_UNDEF
+CONCEPT		STR	_UNDEF
+STAT		STR	_UNDEF
+SUMMARY		STR	_UNDEF
+ANALYSIS        STR     _UNDEF
+
+# basic stats for output images
+CHIPSTATS	METADATA
+  STAT          MULTI
+  STAT		STR	ROBUST_MEDIAN
+  STAT          STR     ROBUST_STDEV   # Background statistics estimators
+
+  HEADER        MULTI
+  HEADER        STR     OVER_VAL
+  HEADER        STR     FWHM_X # major axis FWHM (in pixels)
+  HEADER        STR     FWHM_Y # minor axis FWHM (in pixels)
+  HEADER        STR     DT_DET   # elapsed time in detrend processing
+  HEADER        STR     DT_PHOT  # elapsed time in photometry processing
+  HEADER        STR     DT_ASTR  # elapsed time in astrometry processing
+  HEADER        STR     APMIFIT
+  HEADER        STR     DAPMIFIT
+  HEADER        STR     CERROR
+  HEADER        STR     NSTARS
+  HEADER        STR     NASTRO
+
+  ANALYSIS      MULTI   # metadata blocks to search in chip/cell/readout->analysis
+  ANALYSIS      STR     PSPHOT.HEADER
+  ANALYSIS      STR     PSASTRO.HEADER
+END
+
+# basic stats for output images
+DARKSTATS	METADATA
+  DO.FIRST.READOUT.3D BOOL TRUE  # for 3D images, measure stats of first readout
+
+  STAT          MULTI
+  STAT		STR	ROBUST_MEDIAN
+  STAT          STR     ROBUST_STDEV   # Background statistics estimators
+
+  HEADER        MULTI
+  HEADER        STR     OVER_VAL
+  HEADER        STR     FWHM_X # major axis FWHM (in pixels)
+  HEADER        STR     FWHM_Y # minor axis FWHM (in pixels)
+  HEADER        STR     DT_DET   # elapsed time in detrend processing
+  HEADER        STR     DT_PHOT  # elapsed time in photometry processing
+  HEADER        STR     DT_ASTR  # elapsed time in astrometry processing
+  HEADER        STR     APMIFIT
+  HEADER        STR     DAPMIFIT
+  HEADER        STR     CERROR
+  HEADER        STR     NSTARS
+  HEADER        STR     NASTRO
+
+  ANALYSIS      MULTI   # metadata blocks to search in chip/cell/readout->analysis
+  ANALYSIS      STR     PSPHOT.HEADER
+  ANALYSIS      STR     PSASTRO.HEADER
+END
+
+# basic stats for residual images
+RESIDUAL	METADATA
+  STAT          MULTI
+  STAT		STR	ROBUST_MEDIAN
+  STAT          STR     ROBUST_STDEV   # Background statistics estimators
+  STAT          STR     SAMPLE_SKEWNESS
+  STAT          STR     SAMPLE_KURTOSIS
+  HEADER        MULTI
+  HEADER        STR     FRNG_00        # fringe amplitude, if measured
+  HEADER        STR     FRNG_00D       # fringe error, if measured
+  HEADER        STR     FRNG_01        # fringe amplitude, if measured
+  HEADER        STR     FRNG_01D       # fringe error, if measured
+  HEADER        STR     FRNG_02        # fringe amplitude, if measured
+  HEADER        STR     FRNG_02D       # fringe error, if measured
+END
+
+### ppStats recipe for injection
+INJECT  METADATA
+  CONCEPT       MULTI
+END
+
+### ppStats recipe for registration
+REGISTER  	METADATA
+  CONCEPT       MULTI
+  STAT          MULTI
+  SUMMARY       MULTI
+
+  CONCEPT       STR     FPA.OBJECT      # Object name
+  CONCEPT       STR     FPA.OBSTYPE     # Observation type
+  CONCEPT       STR     FPA.FILTER      # Filter
+  CONCEPT       STR     FPA.RA FPA.DEC  # Telescope pointing
+
+  CONCEPT       STR     FPA.LONGITUDE
+  CONCEPT       STR     FPA.LATITUDE
+  CONCEPT       STR     FPA.ELEVATION
+
+  CONCEPT       STR     FPA.COMMENT     # Obs Comment
+
+  CONCEPT       STR     FPA.AIRMASS     # Airmass
+  CONCEPT       STR     FPA.ALT FPA.AZ  # Telescopy alt/az
+  CONCEPT       STR     FPA.POSANGLE    # Rotator angle
+  CONCEPT       STR     FPA.TIME        # Time of exposure
+  CONCEPT       STR     FPA.TELESCOPE   # Telescope (eg, CFHT)
+  CONCEPT       STR     FPA.CAMERA      # Instrument (eg, CFH12K)
+  CONCEPT       STR     CHIP.TEMP       # Detector temperature
+  CONCEPT       STR     CELL.EXPOSURE   # Exposure time
+
+  CONCEPT       STR     FPA.M1X         # Primary x position
+  CONCEPT       STR	FPA.M1Y         # Primary y position
+  CONCEPT       STR	FPA.M1Z         # Primary z position
+  CONCEPT       STR	FPA.M1TIP       # Primary tip
+  CONCEPT       STR	FPA.M1TILT      # Primary tilt
+  CONCEPT       STR	FPA.M2X         # Secondary x position
+  CONCEPT       STR	FPA.M2Y         # Secondary y position
+  CONCEPT       STR	FPA.M2Z         # Secondary z position
+  CONCEPT       STR	FPA.M2TIP       # Secondary tip position
+  CONCEPT       STR	FPA.M2TILT      # Secondary tilt position
+  CONCEPT       STR	FPA.ENV.TEMP    # external temperature
+  CONCEPT       STR	FPA.ENV.HUMID   # external humidity
+  CONCEPT       STR	FPA.ENV.WIND    # external wind speed
+  CONCEPT       STR	FPA.ENV.DIR     # external wind direction
+
+  CONCEPT       STR     FPA.TELTEMP.M1     # Primary mirror temps (C) 
+  CONCEPT       STR     FPA.TELTEMP.M1CELL # Primary mirror support temps (C)   
+  CONCEPT       STR     FPA.TELTEMP.M2     # Secondary mirror temps (C
+  CONCEPT       STR     FPA.TELTEMP.SPIDER # Spider temperatures (C)  
+  CONCEPT       STR     FPA.TELTEMP.TRUSS  # Mid truss temperatures (C
+  CONCEPT       STR     FPA.TELTEMP.EXTRA  # Miscellaneous temperatures (C)     
+
+  CONCEPT       STR	FPA.PON.TIME    # time since last power on
+
+  STAT          STR     ROBUST_MEDIAN   # Background estimator
+  STAT          STR     ROBUST_STDEV    # Background standard deviation estimator
+  SUMMARY	STR	SAT_PIXEL_NUM
+  SUMMARY	STR	SAT_PIXEL_FRAC
+END
+
+
+# basic stats for warped images
+WARPSTATS	METADATA
+  HEADER        MULTI
+  CONCEPT       MULTI
+  STAT          MULTI
+  SUMMARY       MULTI
+
+  STAT		STR	ROBUST_MEDIAN
+  STAT          STR     ROBUST_STDEV    # Background statistics estimators
+  SUMMARY       STR     GOOD_PIXEL_NUM  # Number of good pixels
+  SUMMARY       STR     GOOD_PIXEL_FRAC # Fraction of good pixels
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppStatsFromMetadata.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppStatsFromMetadata.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppStatsFromMetadata.config	(revision 22322)
@@ -0,0 +1,61 @@
+
+DETREND_NORM_EXP METADATA
+  ENTRY MULTI
+
+  TYPE   VAL  KEYWORD             TYPE STATISTIC         FLAG 
+  ENTRY  VAL  bg                  F64  ROBUST_MEDIAN     -bg
+  ENTRY  VAL  bg                  F64  ROBUST_STDEV      -bg_mean_stdev
+  ENTRY  VAL  bg_stdev            F64  RMS               -bg_stdev
+END
+
+DETREND_NORM_APPLY METADATA
+  ENTRY MULTI
+
+  TYPE   VAL  KEYWORD             TYPE STATISTIC         FLAG 
+  ENTRY  VAL  ROBUST_MEDIAN       F64  ROBUST_MEDIAN     -bg
+  ENTRY  VAL  ROBUST_MEDIAN       F64  ROBUST_STDEV      -bg_mean_stdev
+  ENTRY  VAL  ROBUST_STDEV        F64  RMS               -bg_stdev
+END
+
+DETREND_RESID_IMFILE METADATA
+  ENTRY MULTI
+  
+  TYPE   VAL  KEYWORD             TYPE STATISTIC         FLAG 
+  ENTRY  VAL  ROBUST_MEDIAN       F32  ROBUST_MEDIAN     -bg                
+  ENTRY  VAL  ROBUST_MEDIAN       F32  ROBUST_STDEV      -bg_mean_stdev     
+  ENTRY  VAL  SAMPLE_SKEWNESS     F32  ROBUST_MEDIAN     -bg_skewness       
+  ENTRY  VAL  SAMPLE_KURTOSIS     F32  ROBUST_MEDIAN     -bg_kurtosis       
+  ENTRY  VAL  FRINGE_0            F32  ROBUST_MEDIAN     -fringe_0          
+  ENTRY  VAL  FRINGE_0            F32  ROBUST_STDEV      -fringe_2          
+  ENTRY  VAL  FRINGE_RESID_0      F32  ROBUST_MEDIAN     -fringe_resid_0    
+  ENTRY  VAL  FRINGE_RESID_0      F32  ROBUST_STDEV      -fringe_resid_2    
+  ENTRY  VAL  ROBUST_STDEV        F32  RMS               -bg_stdev          
+  ENTRY  VAL  FRINGE_RESID_ERR_0  F32  RMS               -fringe_resid_1    
+  ENTRY  VAL  FRINGE_ERR_0        F32  RMS               -fringe_1          
+END
+
+DETREND_RESID_IMFILE_BINNED METADATA
+  ENTRY MULTI
+  
+  TYPE   VAL  KEYWORD             TYPE STATISTIC         FLAG 
+  ENTRY  VAL  ROBUST_STDEV        F32  RMS               -bin_stdev          
+END
+
+DETREND_RESID_EXP METADATA
+  ENTRY MULTI
+  
+  TYPE   VAL  KEYWORD             TYPE STATISTIC         FLAG 
+  ENTRY  VAL  bg                  F64  ROBUST_MEDIAN     -bg           
+  ENTRY  VAL  bg_mean_stdev       F64  ROBUST_STDEV      -bg_mean_stdev
+  ENTRY  VAL  bg_stdev            F64  RMS               -bg_stdev     
+  ENTRY  VAL  bg_skewness         F64  ROBUST_MEDIAN     -bg_skewness  
+  ENTRY  VAL  bg_kurtosis         F64  ROBUST_MEDIAN     -bg_kurtosis  
+  ENTRY  VAL  bin_stdev           F64  RMS               -bin_stdev    
+  ENTRY  VAL  fringe_0            F64  ROBUST_MEDIAN     -fringe_0     
+  ENTRY  VAL  fringe_1            F64  RMS               -fringe_1     
+  ENTRY  VAL  fringe_2            F64  ROBUST_STDEV      -fringe_2     
+  ENTRY  VAL  user_1              F64  ROBUST_MEDIAN     -user_1       
+  ENTRY  VAL  user_2              F64  RMS               -user_2       
+  ENTRY  VAL  user_1              F64  ROBUST_STDEV      -user_3       
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppSub.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppSub.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/ppSub.config	(revision 22322)
@@ -0,0 +1,44 @@
+### Recipe file for ppSub
+
+KERNEL.TYPE	STR	RINGS		# Kernel type to use (POIS|ISIS|SPAM|FRIES|GUNK|RINGS)
+KERNEL.SIZE     S32	35		# Kernel half-size (pixels)
+SPATIAL.ORDER   S32	0		# Spatial polynomial order
+REGION.SIZE	F32	0		# Iso-kernel region size (pixels)
+STAMP.SPACING   F32	200		# Typical spacing between stamps (pixels)
+STAMP.FOOTPRINT S32	35		# Size of stamps (pixels)
+STAMP.THRESHOLD F32	0		# Flux threshold for stamps (ADU)
+ITER            S32	1		# Number of rejection iterations
+REJ             F32	2.5		# Rejection level (std dev)
+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
+POOR.FRACTION	F32	0.1		# Maximum fraction of bad weight for poor pixels
+BADFRAC		F32	0.95		# Maximum fraction of bad pixels
+@ISIS.WIDTHS	F32	1 3 5		# Gaussian FWHMs for ISIS kernels
+@ISIS.ORDERS	S32	2 2 2		# Polynomial orders for ISIS kernels
+SPAM.BINNING	S32	2		# Binning in outer region for SPAM kernels
+INNER		S32	5		# Inner half-size for SPAM and FRIES kernels
+RINGS.ORDER	S32	2		# Polynomial order for RINGS kernels
+PENALTY		F32	1.0		# Penalty for wideness
+BIN1		S32	4		# Binning factor for first level
+BIN2		S32	4		# Binning factor for second level
+
+OPTIMUM		BOOL	FALSE		# Derive optimum parameters for ISIS and GUNK kernels
+OPTIMUM.MIN	F32	1.0		# Minimum width for optimum FWHM search
+OPTIMUM.MAX	F32	11.0		# Maximum width for optimum FWHM search
+OPTIMUM.STEP	F32	1.0		# Step in width for optimum FWHM search
+OPTIMUM.TOL	F32	3.0e-3		# Maximum difference in chi^2 between iterations to settle for
+OPTIMUM.ORDER	S32	2		# Maximum polynomial order for optimum search
+
+RENORM		BOOL	FALSE		# Renormalise weight maps?
+RENORM.WIDTH	S32	500		# Size of renormalisation boxes
+
+INTERPOLATION	STR	LANCZOS3	# Interpolation mode for bad pixels
+
+PHOTOMETRY	BOOL	FALSE		# Perform photometry?
+
+### Modifications to use when stacking data
+STACK	METADATA
+	KERNEL.TYPE	STR	RINGS	# Kernel type to use (POIS|ISIS|SPAM|FRIES|GUNK|RINGS)
+	KERNEL.SIZE     S32     35      # Kernel half-size (pixels)
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/psastro.config	(revision 22322)
@@ -0,0 +1,148 @@
+
+PSASTRO.SAVE.REFSTARS      BOOL FALSE
+PSASTRO.ONLY.REFSTARS      BOOL FALSE
+
+# perform single-chip astrometry?
+PSASTRO.CHIP.MODE           BOOL     TRUE
+
+# nominal plate scale (microns / pixel)
+PSASTRO.PIXEL.SCALE    F32  13.5
+
+# plotting options
+PSASTRO.PLOT.INST.MAG.MAX F32 -10.0
+PSASTRO.PLOT.INST.MAG.MIN F32 -17.0
+PSASTRO.PLOT.REF.MAG.MIN  F32 +10.0
+PSASTRO.PLOT.REF.MAG.MAX  F32 +20.0
+
+# extra field for ref stars:
+PSASTRO.FIELD.PADDING  F32 0.25
+
+# pmAstromGridMatch:
+PSASTRO.GRID.SEARCH    BOOL TRUE
+
+PSASTRO.GRID.MIN.ANGLE F32 -2.0 # start angle (degrees)
+PSASTRO.GRID.MAX.ANGLE F32 +2.0
+PSASTRO.GRID.DEL.ANGLE F32  0.5
+
+PSASTRO.GRID.MIN.SCALE F32  0.98
+PSASTRO.GRID.MAX.SCALE F32  1.04
+PSASTRO.GRID.DEL.SCALE F32  0.02
+
+PSASTRO.GRID.MIN.SIGMA F32  5.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32  10000.
+PSASTRO.GRID.SCALE     F32    500.
+PSASTRO.GRID.NSTAR.MAX S32    300 # max stars accepted for fitting
+
+# sources with these mask flags raised should be ignored
+PSASTRO.IGNORE         STR    CRLIMIT,SATURATED,DEFECT,SATSTAR,BLEND,FAIL
+PSASTRO.ROUGH.MODEL    STR    none
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# single-chip radius match in pixels
+PSASTRO.MATCH.RADIUS   F32    8
+PSASTRO.MATCH.RADIUS.N0 F32   0
+PSASTRO.MATCH.RADIUS.N1 F32   0
+PSASTRO.MATCH.RADIUS.N2 F32   0
+PSASTRO.MATCH.RADIUS.N3 F32   0
+PSASTRO.MATCH.RADIUS.N4 F32   0
+PSASTRO.MATCH.RADIUS.N5 F32   0
+PSASTRO.MATCH.RADIUS.N6 F32   0
+PSASTRO.MATCH.RADIUS.N7 F32   0
+PSASTRO.MATCH.FIT.NITER S32   1
+
+# pmAstromMatchFit
+PSASTRO.CHIP.ORDER     S32      1  # fit order
+PSASTRO.CHIP.NITER     S32      3  # fit clipping iterations
+PSASTRO.CHIP.NSIGMA    F32      3  # fit clipping sigmas
+
+PSASTRO.MAX.ERROR      F32      1.5 # max allow error for valid solution (UNITS?)
+PSASTRO.MIN.NSTAR      S32      3   # min fitted stars in solution
+PSASTRO.MAX.NSTAR      S32      300   # max fitted stars in solution
+
+PSASTRO.MAX.NRAW       S32      0   # max stars accepted for fitting (0 for all)
+PSASTRO.MAX.NREF       S32      0   # max stars accepted for fitting (0 for all)
+
+PSASTRO.MIN.INST.MAG.RAW       F32      0.0   # min instrumental magnitude for stars accepted for fitting
+PSASTRO.MAX.INST.MAG.RAW       F32      0.0   # max instrumental magnitude for stars accepted for fitting
+
+PSASTRO.MATCH.LUMFUNC  BOOL     FALSE
+
+# option may be MAX, MIN, or VALUE. for VALUE, look up 
+# plate scale for each chip in the concepts
+PSASTRO.COMMON.SCALE.OPTION	STR	MAX
+
+# mosaic-mode radius match in pixels?
+PSASTRO.MOSAIC.RADIUS.N0   F32    8
+PSASTRO.MOSAIC.RADIUS.N1   F32    6
+PSASTRO.MOSAIC.RADIUS.N2   F32    4
+PSASTRO.MOSAIC.RADIUS.N3   F32    0.0
+PSASTRO.MOSAIC.RADIUS.N4   F32    0.0
+
+# Mosaic Astrometry options
+PSASTRO.MOSAIC.MODE           BOOL     FALSE
+PSASTRO.MOSAIC.ORDER          S32      3  # fit order
+
+PSASTRO.MOSAIC.CHIP.ORDER     S32       1 # fit order
+PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      -1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      -1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      -1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      -1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N4  S32      -1 # fit order (-1 means use default)
+
+PSASTRO.MOSAIC.CHIP.NITER     S32      3  # fit clipping iterations
+PSASTRO.MOSAIC.CHIP.NSIGMA    F32      3  # fit clipping sigmas
+
+PSASTRO.MOSAIC.MAX.ERROR.N0   F32      1.5 # max allow error for valid solution (UNITS?)
+PSASTRO.MOSAIC.MAX.ERROR.N1   F32      1.5 # max allow error for valid solution (UNITS?)
+PSASTRO.MOSAIC.MAX.ERROR.N2   F32      1.5 # max allow error for valid solution (UNITS?)
+PSASTRO.MOSAIC.MAX.ERROR.N3   F32      1.5 # max allow error for valid solution (UNITS?)
+PSASTRO.MOSAIC.MAX.ERROR.N4   F32      1.5 # max allow error for valid solution (UNITS?)
+PSASTRO.MOSAIC.MIN.NSTAR      S32      6   # min fitted stars in solution
+
+PSASTRO.MOSAIC.GRADIENT.NX    S32      2   # number of x-dir cells per chip
+PSASTRO.MOSAIC.GRADIENT.NY    S32      2   # number of y-dir cells per chip
+
+# 2MASS default configuration (use 2MASS_J as ref magnitude)
+PSASTRO.CATDIR                STR      2MASS
+DVO.GETSTAR                   STR      getstar
+DVO.GETSTAR.OUTFORMAT         STR      PS1_DEV_0
+DVO.GETSTAR.PHOTCODE          STR      2MASS_J
+DVO.GETSTAR.MAG.MAX           F32      22.0
+
+PSASTRO.2MASS                 METADATA
+  PSASTRO.CATDIR              STR      2MASS
+  DVO.GETSTAR.PHOTCODE        STR      2MASS_J
+  DVO.GETSTAR.MAG.MAX         F32      17.0
+END
+
+PSASTRO.SYNTH                 METADATA
+  PSASTRO.CATDIR              STR      SYNTH.GRIZY
+  DVO.GETSTAR.PHOTCODE        STR      r
+END
+
+LOAD.REF.ASTROM               BOOL     FALSE
+SAVE.REF.ASTROM               BOOL     FALSE
+
+PSASTRO.MODEL.REF.CHIP        STR      NONE
+PSASTRO.MODEL.FIT.BORESITE    BOOL     FALSE
+PSASTRO.MODEL.SET.BORESITE    BOOL     FALSE
+PSASTRO.MODEL.BORESITE.X      F32      0.0
+PSASTRO.MODEL.BORESITE.Y      F32      0.0
+
+PSASTRO.FIX.CHIPS             BOOL     FALSE
+PSASTRO.USE.MODEL             BOOL     FALSE
+
+PSASTRO.PIXEL.TOLERANCE       F32      0.1
+PSASTRO.ANGLE.TOLERANCE       F32      0.1
+
+PSASTRO.FINE METADATA
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/psphot.config	(revision 22322)
@@ -0,0 +1,254 @@
+
+# these options turn on different inputs and/or outputs
+SAVE.OUTPUT                         BOOL  TRUE
+SAVE.BACKMDL                        BOOL  FALSE
+SAVE.BACKMDL.STDEV                  BOOL  FALSE
+SAVE.BACKGND                        BOOL  FALSE
+SAVE.BACKSUB                        BOOL  FALSE
+SAVE.RESID                          BOOL  FALSE
+SAVE.PSF                            BOOL  FALSE
+LOAD.PSF                            BOOL  FALSE
+SAVE.PLOTS                          BOOL  FALSE
+
+# the zero point is used to set a basic scale for DVO
+# XXX it may not currently be read : double check this (EAM)
+ZERO_POINT                          F32   25.000          # zero point used by DVO
+ZERO_PT                             F32   25.000          # zero point used by DVO
+
+OUTPUT.FORMAT                       STR   PS1_DEV_1
+
+# these parameter govern how the background is measured
+BACKGROUND.XBIN                     S32   128             # size of background superpixels
+BACKGROUND.YBIN                     S32   128             # size of background superpixels
+IMSTATS_NPIX                        S32   10000           # number of pixels to use for sky estimate boxes:
+
+SKY_BIAS                            F32   0.0             # offset applied to measured sky (FOR TESTING)
+SKY_FIT_ORDER                       S32   0
+SKY_FIT_LINEAR                      BOOL  FALSE
+SKY_STAT                            STR   FITTED_MEAN_V4  # statistic used to measure background
+SKY_CLIP_SIGMA                      F32   2.0             # statistic used to measure background
+SKY_SIG                             F32   1.0             # optional sky error for 
+
+# allowed values for SKY_STAT: 
+# SAMPLE_MEAN, SAMPLE_MEDIAN, CLIPPED_MEAN, ROBUST_MEAN, ROBUST_QUARTILE, FITTED_MEAN
+
+# masking parameters (XXX EAM : rework this to use psRegion like ANALYSIS_REGION)
+XMIN                                F32   0               # minimum valid x-coord
+XMAX                                F32   0               # maximum valid x-coord
+YMIN                                F32   0               # minimum valid y-coord
+YMAX                                F32   0               # maximum valid y-coord
+
+# peak finding 
+PEAKS_SMOOTH_SIGMA                  F32   1.0             # smoothing kernel sigma in pixels
+PEAKS_SMOOTH_NSIGMA                 F32   2.0             # smoothing kernel width in sigmas
+PEAKS_NSIGMA_LIMIT                  F32   25.0            # peak significance threshold
+PEAKS_NSIGMA_LIMIT_2                F32   5.0             # peak significance threshold
+PEAKS_NMAX                          S32   0               # on first pass, only keep NMAX peaks (0 == all)
+
+# parameters to control the selection of the peak in the Sx,Sy plane
+MOMENTS_SCALE                       F32   0.25       
+MOMENTS_SN_MIN                      F32   100.0           # min S/N to measure moments
+MOMENTS_SX_MAX                      F32   5.0
+MOMENTS_SY_MAX                      F32   5.0
+MOMENTS_AR_MAX                      F32   1.5             # maximum axial ratio: 1 / AR < (sx / sy) < AR
+
+# basic object statistics
+SKY_INNER_RADIUS                    F32   15              # square annulus for local sky measurement
+SKY_OUTER_RADIUS                    F32   25              # square annulus for local sky measurement
+PSF_MOMENTS_RADIUS                  F32   3               # calculate initial source moments with this radius
+PSF_SN_LIM                          F32   50              # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS                      S32   200             # limit number of stars used for PSF model
+PSF_CLUMP_NSIGMA                    F32   1.5             # region of Sx,Sy plane to use for selecting PSF stars
+PSF_MIN_DS                          F32   0.01
+PSF_PARAM_WEIGHTS                   BOOL  FALSE
+
+PSF_FIT_ITER                        S32   50              # Maximum number of fitting iterations for PSF
+PSF_FIT_TOL                         F32   0.01            # Fit tolerance for PSF
+
+# PSF model parameters : choose the PSF model
+# list as many PSF_MODEL options as desired
+# PSF_MODEL                         MULTI
+PSF_MODEL                           STR   PS_MODEL_GAUSS
+# PSF_MODEL                         STR   PS_MODEL_PGAUSS
+# PSF_MODEL                         STR   PS_MODEL_QGAUSS
+# PSF_MODEL                         STR   PS_MODEL_TGAUSS # not well tested, not very successful
+
+# PSF.TREND.MASK must be a 2D polynomial
+# the specified values are ignored but define the active components of the polynomial
+PSF.TREND.MASK                      METADATA  
+   NORDER_X                         S32   0               # number of x orders
+   NORDER_Y                         S32   0               # number of y orders
+   VAL_X00_Y00                      F64   1               # polynomial coefficient
+   NELEMENTS                        S32   1               # number of unmasked components
+END  # folder for 2D polynomial
+
+PSF.TREND.MODE                      STR POLY_ORD         
+PSF.TREND.NX                        S32   0 
+PSF.TREND.NY                        S32   0
+
+PSF.FAKE.ALLOW                      BOOL  FALSE           # Allow use of fake PSFs when having trouble?
+
+PSF_FIT_RADIUS                      F32   15.0            # fitting radius for test PSF model
+PSF_REF_RADIUS                      F32   25.0            # aperture magnitudes are scaled via 
+                                         # curve-of-growth to this radius
+# PSF-like source model parameters
+PSF_FIT_NSIGMA                      F32   1.0             # significance for pixel included in fit
+PSF_FIT_PADDING                     F32   2.0             # extra annulus to use for fit 
+PSF_SHAPE_NSIGMA                    F32   3.0             # max significance for shape variation
+PSF_MIN_SN                          F32   2.0             # reject objects below this significance
+PSF_MAX_CHI                         F32   50.0            # reject objects worse that this
+FULL_FIT_SN_LIM                     F32   50.0
+
+# the RESIDUALS are pixelized psf residual tables
+PSF.RESIDUALS                       BOOL  TRUE            # generate the residuals?
+PSF.RESIDUALS.XBIN                  S32   1               # Nx(residual) = Nx(input)*XBIN
+PSF.RESIDUALS.YBIN                  S32   1               # Ny(residual) = Ny(input)*YBIN
+PSF.RESIDUALS.NSIGMA                F32   3.0             # clip input stack of NSIGMA outliers
+PSF.RESIDUALS.INTERPOLATION         STR   BILINEAR        # interpolation to use when reconstructing residual
+PSF.RESIDUALS.STATISTIC             STR   ROBUST_MEDIAN   # statistic to use for generating the residual
+PSF.RESIDUALS.SPATIAL_ORDER         S32   0               # fit spatial variations of the residuals at this order (0,1)
+PSF.RESIDUALS.PIX.SN                F32   0.0             # keep this pixel if residual is more significant than this
+ 
+# this model is used for approximate subtraction in the main object analysis step
+EXT_MODEL                           STR   PS_MODEL_QGAUSS
+EXT_MIN_SN                          F32   50.0            # fit galaxies above this S/N limit
+EXT_FIT_NSIGMA                      F32   1               # significance for pixel included in fit
+EXT_FIT_PADDING                     F32   5               # extra annulus to use for fit 
+EXT_MOMENTS_RADIUS                  F32   9
+
+EXT_FIT_ITER                        S32   20              # Maximum number of fitting iterations for EXT
+EXT_FIT_TOL                         F32   0.01            # Fit tolerance for EXT
+
+# extended source aperture-like measurements
+EXTENDED_SOURCE_ANALYSIS            BOOL  FALSE  # perform any of the aperture-like measurements?
+EXTENDED_SOURCE_SN_LIM              F32   20.0
+EXTENDED_SOURCE_PETROSIAN           BOOL  FALSE
+EXTENDED_SOURCE_ISOPHOTAL           BOOL  FALSE
+EXTENDED_SOURCE_ANNULI              BOOL  FALSE
+EXTENDED_SOURCE_KRON                BOOL  FALSE
+
+# Extended source fit parameters
+# these models are used for high-quality shape analysis after all sources have been removed
+EXTENDED_SOURCE_FITS                BOOL  FALSE  # perform the detailed extended sourc fits?
+
+# extended source models applied to sources
+EXTENDED_SOURCE_MODELS METADATA 
+ TYPE  	    EXTENDED_SOURCE_MODEL  MODEL            SNLIM   PSF_CONVOLVED
+ #GAUSS_RAW   EXTENDED_SOURCE_MODEL  PS_MODEL_GAUSS    20.0    FALSE
+ #PGAUSS_RAW  EXTENDED_SOURCE_MODEL  PS_MODEL_PGAUSS   20.0    FALSE
+ #QGAUSS_RAW  EXTENDED_SOURCE_MODEL   PS_MODEL_QGAUSS   20.0    FALSE
+ #SERSIC_RAW  EXTENDED_SOURCE_MODEL   PS_MODEL_SERSIC   20.0    FALSE
+ #GAUSS_PSF   EXTENDED_SOURCE_MODEL  PS_MODEL_GAUSS    20.0    TRUE
+ #PGAUSS_PSF  EXTENDED_SOURCE_MODEL  PS_MODEL_PGAUSS   20.0    TRUE
+ #QGAUSS_PSF  EXTENDED_SOURCE_MODEL  PS_MODEL_QGAUSS   20.0    TRUE
+ SERSIC_PSF  EXTENDED_SOURCE_MODEL  PS_MODEL_SERSIC   20.0    TRUE
+END
+
+# define the annuli (in pixels) for surface brightness measurements
+@RADIAL.ANNULAR.BINS.LOWER           F32     0.0   5.0  10.0  20.0  40.0  80.0
+@RADIAL.ANNULAR.BINS.UPPER           F32     5.0  10.0  20.0  40.0  80.0 160.0
+
+# Extended source fit parameters
+EXTENDED_TEST                       METADATA
+  EXTENDED_SOURCE_ANALYSIS            BOOL  TRUE  # perform any of the aperture-like measurements?
+  EXTENDED_SOURCE_SN_LIM              F32   20.0
+  EXTENDED_SOURCE_PETROSIAN           BOOL  TRUE
+  EXTENDED_SOURCE_ISOPHOTAL           BOOL  TRUE
+  EXTENDED_SOURCE_ANNULI              BOOL  TRUE
+  EXTENDED_SOURCE_KRON                BOOL  FALSE
+END
+
+ISOPHOTAL_FLUX                      F32   20.0
+PETROSIAN_R0                        F32    5.0 # reference radius in pixels
+PETROSIAN_FLUX_RATIO                F32    0.1 # we measure out to where flux = R0 * FLUX_RATIO
+
+FITMODE                             STR   BLEND  
+DEBLEND_PEAK_FRACTION               F32   0.1
+DEBLEND_SKY_NSIGMA                  F32   10.0
+
+# APTREND                           STR   NONE, CONSTANT, SKYBIAS, SKYSAT, XY_LIN, SKY_XY_LIN, SKYSAT_XY_LIN, ALL
+MEASURE.APTREND	                    BOOL  FALSE		### Gene says there's bugs in this part
+APTREND                             STR   CONSTANT
+AP_MIN_SN                           F32   25.0
+APTREND.NSTAR.MIN                   S32   5
+APTREND.ORDER.MAX                   S32   5
+
+# test options
+# BREAK_POINT may be one of (PEAKS, MOMENTS, PSFMODEL, ENSEMBLE)
+BREAK_POINT                         STR   NONE     
+PEAKS_OUTPUT_FILE                   STR   NONE
+MOMENTS_OUTPUT_FILE                 STR   NONE
+
+# ANALYSIS_REGION                   STR   [1000:1600,2800:3400]
+
+# optional parameter to limit the actual analysis to a fraction of the image
+# do not uncomment this in the master psphot.config file
+# ANALYSIS_REGION                   STR   [1000:1600,2800:3400]
+
+IGNORE_GROWTH                       BOOL  FALSE
+CONSTANT_PHOTOMETRIC_WEIGHTS        BOOL  TRUE 		  # Should the photometric code [currently only ensemblePSF] refuse to weight each pixel by it's significance?
+INTERPOLATE_AP                      BOOL  TRUE
+
+POISSON.ERRORS.PHOT.LMM             BOOL  TRUE   
+POISSON.ERRORS.PHOT.LIN             BOOL  FALSE
+POISSON.ERRORS.PARAMS               BOOL  TRUE
+
+PCM_BOX_SIZE                        S32   8
+
+NOISE.FACTOR                        F32   5.0
+NOISE.SIZE                          F32   2.0
+
+USE_FOOTPRINTS                      BOOL  F       	  # use new pmFootprint peak packaging
+FOOTPRINT_NPIXMIN                   S32   5       	  # Minimum size of a pmFootprint
+FOOTPRINT_NSIGMA_LIMIT              F32   20      	  # threshold for bright pmFootprint detection
+FOOTPRINT_NSIGMA_LIMIT_2            F32   4       	  # threshold for faint pmFootprint detection
+FOOTPRINT_GROW_RADIUS               S32   3       	  # How much to grow bright footprints
+FOOTPRINT_GROW_RADIUS_2             S32   5       	  # How much to grow faint footprints
+FOOTPRINT_CULL_NSIGMA_DELTA         F32   4       	  # Cull peaks that aren't nsigma above coll to neighbour
+FOOTPRINT_CULL_NSIGMA_MIN           F32   1       	  # Minimum height of colls in units of skyStdev
+
+# alternate PSPHOT-type recipes
+PSPHOT.FIXED.PSF METADATA
+END
+
+# alternate PSPHOT-type recipes
+PSPHOT.SUMMIT METADATA
+END
+
+# alternate PSPHOT-type recipes
+PSPHOT.SEEING METADATA
+END
+
+TEST_FIT                            BOOL  FALSE
+TEST_FIT_MODE                       STR   DEFAULT # PSF, CONV, DEFAULT
+TEST_FIT_MODEL                      STR   DEFAULT # PS_MODEL_SERSIC
+TEST_FIT_INNER_RADIUS               F32   NAN 
+TEST_FIT_OUTER_RADIUS               F32   NAN
+TEST_FIT_RADIUS                     F32   NAN
+TEST_MOMENTS_RADIUS                 F32   NAN
+TEST_FIT_X                          F32   NAN
+TEST_FIT_Y                          F32   NAN
+
+TEST_FIT_PAR0                       F32   NAN
+TEST_FIT_PAR1                       F32   NAN
+TEST_FIT_PAR2                       F32   NAN
+TEST_FIT_PAR3                       F32   NAN
+TEST_FIT_PAR4                       F32   NAN
+TEST_FIT_PAR5                       F32   NAN
+TEST_FIT_PAR6                       F32   NAN
+TEST_FIT_PAR7                       F32   NAN
+TEST_FIT_PAR8                       F32   NAN
+TEST_FIT_PAR                        F32   NAN
+
+DIAGNOSTIC.PLOTS                    METADATA
+  IMAGE.BACKGROUND.CELL.HISTOGRAM   BOOL  FALSE
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.X S32   35
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.Y S32   -1
+END
+
+PSF.FLUXSCALE.NX                    S32   5 		  # number cells to measure flux scale variations
+PSF.FLUXSCALE.NY                    S32   5 		  # number cells to measure flux scale variations
+
+PSPHOT.CR.NSIGMA.LIMIT              F32   3.0  # sources with crNsigma greater that this get tagged as likely cosmic rays
+PSPHOT.EXT.NSIGMA.LIMIT             F32   4.0  # sources with extNsigma greater that this get tagged as likely extended sources
+PSPHOT.CR.GROW                      S32   1               # Number of pixels to grow CR mask
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/pswarp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/pswarp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/pswarp.config	(revision 22322)
@@ -0,0 +1,10 @@
+### Recipe for pswarp
+
+GRID.NX			S32	128		# Iso-astrom grid size in x (pixels)
+GRID.NY			S32	128		# Iso-astrom grid size in y (pixels)
+INTERPOLATION.MODE	STR	LANCZOS3	# Interpolation mode to use
+POOR.FRAC		F32	0.01		# Max fraction of bad flux for a "poor" warped pixel
+ASTROM.SOURCE		STR	PSASTRO.OUTPUT	# Source file rule for astrometry, or NULL
+ASTROM.ACCEPT		BOOL	FALSE		# Accept astrometric solution unconditionally?
+ASTROM.DEPTH		STR	MEF             # Source file rule for astrometry, or NULL
+ACCEPT.FRAC		F32	0.1		# Minimum fraction of good pixels to accept result
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/reductionClasses.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/reductionClasses.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/reductionClasses.mdc	(revision 22322)
@@ -0,0 +1,169 @@
+# These reduction classes define recipes that go together as a group
+
+DETREND         METADATA
+	BIAS_PROCESS		STR	PPIMAGE_O
+	BIAS_RESID		STR	PPIMAGE_B
+	BIAS_VERIFY		STR	PPIMAGE_B
+	BIAS_STACK		STR	PPMERGE_BIAS
+	BIAS_JPEG_IMAGE		STR	BIAS
+	BIAS_JPEG_RESID		STR	BIAS_RESID
+
+	DARK_PREMASK_PROCESS	STR	PPIMAGE_OB
+	DARK_PREMASK_RESID	STR	PPIMAGE_D
+	DARK_PREMASK_VERIFY	STR	PPIMAGE_D
+	DARK_PREMASK_STACK	STR	PPMERGE_DARK
+	DARK_PREMASK_JPEG_IMAGE	STR	BIAS
+	DARK_PREMASK_JPEG_RESID	STR	BIAS_RESID
+
+	DARK_PROCESS		STR	PPIMAGE_OB
+	DARK_RESID		STR	PPIMAGE_D
+	DARK_VERIFY		STR	PPIMAGE_D
+	DARK_STACK		STR	PPMERGE_DARK
+	DARK_JPEG_IMAGE		STR	BIAS
+	DARK_JPEG_RESID		STR	BIAS_RESID
+
+	SHUTTER_PROCESS 	STR	PPIMAGE_OBD
+	SHUTTER_RESID		STR	PPIMAGE_S
+	SHUTTER_VERIFY		STR	PPIMAGE_S
+	SHUTTER_STACK		STR	PPMERGE_SHUTTER
+	SHUTTER_JPEG_IMAGE	STR	FLAT
+	SHUTTER_JPEG_RESID	STR	FLAT_RESID
+
+	# *** premask (unmasked) flat-field types
+	FLAT_PREMASK_PROCESS	STR	PPIMAGE_OBDS
+	FLAT_PREMASK_RESID	STR	PPIMAGE_F
+	FLAT_PREMASK_VERIFY	STR	PPIMAGE_F
+	FLAT_PREMASK_STACK	STR	PPMERGE_FLAT
+	FLAT_PREMASK_JPEG_IMAGE	STR	FLAT
+	FLAT_PREMASK_JPEG_RESID	STRE	FLAT_RESID
+
+	DOMEFLAT_PREMASK_PROCESS	STR	PPIMAGE_OBDS
+	DOMEFLAT_PREMASK_RESID		STR	PPIMAGE_F
+	DOMEFLAT_PREMASK_VERIFY		STR	PPIMAGE_F
+	DOMEFLAT_PREMASK_STACK		STR	PPMERGE_FLAT
+	DOMEFLAT_PREMASK_JPEG_IMAGE	STR	FLAT
+	DOMEFLAT_PREMASK_JPEG_RESID	STR	FLAT_RESID
+
+	SKYFLAT_PREMASK_PROCESS		STR	PPIMAGE_OBDS
+	SKYFLAT_PREMASK_RESID		STR	PPIMAGE_F
+	SKYFLAT_PREMASK_VERIFY		STR	PPIMAGE_F
+	SKYFLAT_PREMASK_STACK		STR	PPMERGE_FLAT
+	SKYFLAT_PREMASK_JPEG_IMAGE	STR	FLAT
+	SKYFLAT_PREMASK_JPEG_RESID	STR	FLAT_RESID
+
+	# *** raw (uncorrected) flat-field types
+	FLAT_RAW_PROCESS	STR	PPIMAGE_OBDS
+	FLAT_RAW_RESID		STR	PPIMAGE_F
+	FLAT_RAW_VERIFY		STR	PPIMAGE_F
+	FLAT_RAW_STACK		STR	PPMERGE_FLAT
+	FLAT_RAW_JPEG_IMAGE	STR	FLAT
+	FLAT_RAW_JPEG_RESID	STR	FLAT_RESID
+
+	DOMEFLAT_RAW_PROCESS	STR	PPIMAGE_OBDS
+	DOMEFLAT_RAW_RESID	STR	PPIMAGE_F
+	DOMEFLAT_RAW_VERIFY	STR	PPIMAGE_F
+	DOMEFLAT_RAW_STACK	STR	PPMERGE_FLAT
+	DOMEFLAT_RAW_JPEG_IMAGE	STR	FLAT
+	DOMEFLAT_RAW_JPEG_RESID	STR	FLAT_RESID
+
+	SKYFLAT_RAW_PROCESS	STR	PPIMAGE_OBDS
+	SKYFLAT_RAW_RESID	STR	PPIMAGE_F
+	SKYFLAT_RAW_VERIFY	STR	PPIMAGE_F
+	SKYFLAT_RAW_STACK	STR	PPMERGE_FLAT
+	SKYFLAT_RAW_JPEG_IMAGE	STR	FLAT
+	SKYFLAT_RAW_JPEG_RESID	STR	FLAT_RESID
+
+	# *** Standard flat-field types
+	FLAT_PROCESS		STR	PPIMAGE_OBDS
+	FLAT_RESID		STR	PPIMAGE_F
+	FLAT_VERIFY		STR	PPIMAGE_F
+	FLAT_STACK		STR	PPMERGE_FLAT
+	FLAT_JPEG_IMAGE		STR	FLAT
+	FLAT_JPEG_RESID		STR	FLAT_RESID
+
+	DOMEFLAT_PROCESS	STR	PPIMAGE_OBDS
+	DOMEFLAT_RESID		STR	PPIMAGE_F
+	DOMEFLAT_VERIFY		STR	PPIMAGE_F
+	DOMEFLAT_STACK		STR	PPMERGE_FLAT
+	DOMEFLAT_JPEG_IMAGE	STR	FLAT
+	DOMEFLAT_JPEG_RESID	STR	FLAT_RESID
+
+	SKYFLAT_PROCESS		STR	PPIMAGE_OBDS
+	SKYFLAT_RESID		STR	PPIMAGE_F
+	SKYFLAT_VERIFY		STR	PPIMAGE_F
+	SKYFLAT_STACK		STR	PPMERGE_FLAT
+	SKYFLAT_JPEG_IMAGE	STR	FLAT
+	SKYFLAT_JPEG_RESID	STR	FLAT_RESID
+
+	# Generation of pixel masks from darks and flats
+	DARKMASK_PROCESS	STR	PPIMAGE_OBD
+	DARKMASK_RESID		STR	PPIMAGE_M
+	DARKMASK_VERIFY		STR	PPIMAGE_M
+	DARKMASK_STACK		STR	PPMERGE_DARKMASK
+	DARKMASK_JPEG_IMAGE	STR	BIAS_RESID
+	DARKMASK_JPEG_RESID	STR	BIAS_RESID
+
+	FLATMASK_PROCESS	STR	PPIMAGE_OBDSF
+	FLATMASK_RESID		STR	PPIMAGE_M
+	FLATMASK_VERIFY		STR	PPIMAGE_M
+	FLATMASK_STACK		STR	PPMERGE_FLATMASK
+	FLATMASK_JPEG_IMAGE	STR	FLAT_RESID
+	FLATMASK_JPEG_RESID	STR	FLAT_RESID
+
+	FRINGE_PROCESS		STR	PPIMAGE_OBDSF
+	FRINGE_RESID		STR	PPIMAGE_R
+	FRINGE_VERIFY		STR	PPIMAGE_R
+	FRINGE_STACK		STR	PPMERGE_FRINGE
+	FRINGE_JPEG_IMAGE	STR	FRINGE
+	FRINGE_JPEG_RESID	STR	FRINGE_RESID
+END
+
+# basic science analysis
+DEFAULT		METADATA
+	CHIP		STR	PPIMAGE_OBDSFRP
+	JPEG_BIN1	STR	PPIMAGE_J1
+	JPEG_BIN2	STR	PPIMAGE_J2
+	FAKEPHOT	STR	FAKEPHOT
+	ADDSTAR		STR	ADDSTAR
+END
+
+# photometry for pre-detrended images
+PROCESSED	METADATA
+	CHIP		STR	PPIMAGE_P
+	JPEG_BIN1	STR	PPIMAGE_J1
+	JPEG_BIN2	STR	PPIMAGE_J2
+	FAKEPHOT	STR	FAKEPHOT
+	ADDSTAR		STR	ADDSTAR
+END
+
+# photometry to make flat-field correction (apply flat_raw)
+FLATCORR	METADATA
+	CHIP		STR	PPIMAGE_FLATCORR
+	JPEG_BIN1	STR	PPIMAGE_J1
+	JPEG_BIN2	STR	PPIMAGE_J2
+	FAKEPHOT	STR	FAKEPHOT
+	ADDSTAR		STR	FLATCORR
+END
+
+# Detrend Processing only for raw data
+DETREND_ONLY		METADATA
+	CHIP		STR	PPIMAGE_DET_ONLY
+	JPEG_BIN1	STR	PPIMAGE_J1
+	JPEG_BIN2	STR	PPIMAGE_J2
+	FAKEPHOT	STR	FAKEPHOT
+END
+
+NO_PHOTOM	METADATA
+	CHIP		STR	PPIMAGE_OBDSF
+	JPEG_BIN1	STR	PPIMAGE_J1
+	JPEG_BIN2	STR	PPIMAGE_J2
+	FAKEPHOT	STR	FAKEPHOT
+END
+
+# Detrend Processing only for raw data
+MASKPHOT		METADATA
+	CHIP		STR	PPIMAGE_MASKPHOT
+	JPEG_BIN1	STR	PPIMAGE_J1
+	JPEG_BIN2	STR	PPIMAGE_J2
+	FAKEPHOT	STR	FAKEPHOT
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/recipes/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/recipes/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/recipes/rejections.config	(revision 22322)
@@ -0,0 +1,403 @@
+
+BIAS METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0
+  IMFILE.STDEV       F32 10.0
+  IMFILE.MEANSTDEV   F32  0.5
+  IMFILE.SKEWNESS    F32  0.0
+  IMFILE.KURTOSIS    F32  0.0
+  IMFILE.SNR         F32  0.0
+  IMFILE.BIN.STDEV   F32  0.5
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  0.5
+  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  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+DARK METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0
+  IMFILE.STDEV       F32 10.0
+  IMFILE.MEANSTDEV   F32  0.5
+  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  1.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  0.5
+  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  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+DARK_PREMASK 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
+
+SHUTTER 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 20.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
+
+FLAT 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  3.0
+END
+
+FLAT_RAW 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  3.0
+END
+
+FLAT_PREMASK 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  3.0
+END
+
+DOMEFLAT 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  3.0
+END
+
+DOMEFLAT_RAW 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  3.0
+END
+
+DOMEFLAT_PREMASK 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  3.0
+END
+
+SKYFLAT 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  3.0
+END
+
+SKYFLAT_RAW 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  3.0
+END
+
+SKYFLAT_PREMASK 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  3.0
+END
+
+FRINGE 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
+
+DARKMASK 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
+
+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
+
+# FILTER is an additional qualifier, and may be "*" (or absent!), in which case it matches everything
+# EXPECTED is the expected mean value
+# IMFILE.MEAN is the maximum permitted mean value for an imfile, relative to the standard deviation
+# IMFILE.STDEV is the maximum permitted standard deviation for an imfile
+# EXP.MEAN is the maximum permitted mean value for an exposure, relative to the standard deviation
+# EXP.STDEV is the maximum permitted standard deviation for an exposure
+# EXP.MEANSTDEV is the maximum permitted mean standard deviation for an exposure relative to the mean
+# ENSEMBLE.MEAN is the maximum permitted mean for an ensemble of exposures
+# ENSEMBLE.STDEV is the maximum permitted standard deviation for an ensemble of exposures
+# ENSEMBLE.MEANSTDEV is the maximum permitted mean standard deviation for an ensemble of exposures
+# IMFILE.SNR is the minimum permitted signal-to-noise for an imfile
+# EXP.SNR is the minimum permitted signal-to-noise for an exposure
+# These values (all except FILTER) may be zero, in which case no clipping is applied.
+
+
+ITERATION	BOOL	TRUE		# Are iterations permitted?
+MASTER		BOOL	FALSE		# Force stacks to be accepted as a master?
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/Makefile.am	(revision 22322)
@@ -0,0 +1,25 @@
+
+installdir = $(datadir)/ippconfig/sdss
+
+install_files = \
+	dvo.config \
+	dvo.layout \
+	camera.config \
+	format.config \
+	cmp.config \
+	cmf.config \
+	ppImage.config \
+	ppMerge.config \
+	psastro.config \
+	psphot.config \
+	pswarp.config \
+	rejections.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/camera.config	(revision 22322)
@@ -0,0 +1,203 @@
+# Camera configuration file for co-adding reduced SDSS images of stripe82
+# Copied from ISP config files
+# Sebastian Jester jester at mpia.de August 7, 2007
+
+# File formats that we know about
+FORMATS         METADATA
+        SDSS     STR     sdss/format.config
+END
+ 
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        Chip            STR     Cell
+END
+
+# valid filter names and corresponding IDs
+FILTER.ID       METADATA
+        g       STR     g
+        r       STR     r
+        i       STR     i
+        z       STR     z
+	u       STR     u
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	FPA	STR	fpa
+END
+
+DVO.CAMERADIR	STR	sdss		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES         METADATA
+        PSPHOT          STR     sdss/psphot.config               # psphot details
+        PSASTRO         STR     sdss/psastro.config              # psastro details
+        PPIMAGE         STR     sdss/ppImage.config              # Recipes for ppImage
+        PPMERGE         STR     sdss/ppMerge.config              # Recipes for ppMerge
+	PSWARP		STR	sdss/pswarp.config		# Recipes for pswarp
+	REJECTIONS	STR     sdss/rejections.config
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+## XXX replace with appropriate common filerules?
+FILERULES METADATA
+   PSASTRO.INPUT       STR PSASTRO.INPUT.CMF
+   PSASTRO.OUTPUT      STR PSASTRO.OUTPUT.CMF
+   PSPHOT.OUTPUT       STR PSPHOT.OUTPUT.CMF
+
+   ### input file definitions
+   ### use @DETDB entries to get the detrend images from the database
+   ### replace @DETDB with @FILES if you want to require it from the 
+   ### command line, or with an explicit name to require a specific file
+   TYPE               INPUT FILENAME.RULE DATA.LEVEL FILE.TYPE 
+
+   ## files used by ppImage
+   PPIMAGE.INPUT      INPUT @FILES        FPA        IMAGE     
+   PPIMAGE.MASK       INPUT @FILES        FPA        IMAGE     
+   PPIMAGE.BIAS       INPUT @DETDB        FPA        IMAGE     
+   PPIMAGE.DARK       INPUT @DETDB        FPA        DARK
+   PPIMAGE.FLAT       INPUT @DETDB        FPA        IMAGE     
+   PPIMAGE.FRINGE     INPUT @DETDB        FPA        FRINGE
+   PPIMAGE.SHUTTER    INPUT @DETDB        FPA        IMAGE     
+
+   ## files used to build and apply the flat-field correction images
+   DVOCORR.INPUT      INPUT @FILES        FPA        IMAGE
+   DVOCORR.REFHEAD    INPUT @FILES        FPA        HEADER
+   DVOFLAT.INPUT      INPUT @FILES        FPA        IMAGE
+   DVOFLAT.CORR       INPUT @DETDB        FPA        IMAGE
+
+   ## files used by psphot 
+   PSPHOT.LOAD        INPUT @FILES        FPA        IMAGE
+   PSPHOT.INPUT       INPUT @FILES        FPA        IMAGE     
+   PSPHOT.MASK        INPUT @FILES        FPA        MASK     
+   PSPHOT.WEIGHT      INPUT @FILES        FPA        WEIGHT     
+   PSPHOT.PSF.LOAD    INPUT @FILES        FPA        PSF       
+
+   ## files used by psastro 
+   PSASTRO.INPUT.CMP  INPUT @FILES        FPA        CMP       
+   PSASTRO.INPUT.CMF  INPUT @FILES        FPA        CMF       
+
+   ## files used by pswarp
+   PSWARP.INPUT       INPUT @FILES        FPA        IMAGE
+   PSWARP.WEIGHT      INPUT @FILES        FPA        WEIGHT
+   PSWARP.MASK        INPUT @FILES        FPA        MASK
+   PSWARP.SKYCELL     INPUT @FILES        FPA        IMAGE
+   PSWARP.ASTROM      INPUT @FILES        FPA        CMF
+
+   PPSUB.INPUT        INPUT @FILES        FPA	     IMAGE
+   PPSUB.INPUT.MASK   INPUT @FILES        FPA	     MASK
+   PPSUB.INPUT.WEIGHT INPUT @FILES        FPA	     WEIGHT
+   PPSUB.REF          INPUT @FILES        FPA	     IMAGE
+   PPSUB.REF.MASK     INPUT @FILES        FPA	     MASK
+   PPSUB.REF.WEIGHT   INPUT @FILES        FPA	     WEIGHT
+
+   PPSTACK.INPUT      INPUT @FILES        FPA	     IMAGE
+   PPSTACK.INPUT.MASK INPUT @FILES        FPA	     MASK
+   PPSTACK.INPUT.WEIGHT INPUT @FILES      FPA	     WEIGHT
+
+   PPARITH.INPUT.IMAGE INPUT @FILES        CHIP       IMAGE
+   PPARITH.INPUT.MASK  INPUT @FILES        CHIP       MASK
+
+   ### output file definitions
+   TYPE                  OUTPUT  FILENAME.RULE        FILE.TYPE FITS.TYPE DATA.LEVEL FILE.SAVE FILE.FORMAT
+   PPIMAGE.OUTPUT      	 OUTPUT  {OUTPUT}.sdss.fits   IMAGE     NONE      FPA        TRUE      NONE
+   PPIMAGE.OUTPUT.MASK 	 OUTPUT  {OUTPUT}.mask.fits   MASK      NONE      FPA        TRUE      NONE
+   PPIMAGE.OUTPUT.WEIGHT OUTPUT  {OUTPUT}.wt.fits     WEIGHT    NONE      FPA        TRUE      NONE
+   PPIMAGE.CHIP 	 OUTPUT  {OUTPUT}.chip.fits   IMAGE     NONE      FPA        TRUE      NONE
+   PPIMAGE.CHIP.MASK 	 OUTPUT  {OUTPUT}.chip.mask.fits MASK   NONE      FPA        TRUE      NONE
+   PPIMAGE.CHIP.WEIGHT 	 OUTPUT  {OUTPUT}.chip.wt.fits WEIGHT   NONE      FPA        TRUE      NONE
+   PPIMAGE.OUTPUT.FPA1 	 OUTPUT  {OUTPUT}.b1.fits     IMAGE     NONE      FPA        TRUE      NONE
+   PPIMAGE.OUTPUT.FPA2 	 OUTPUT  {OUTPUT}.b2.fits     IMAGE     NONE      FPA        TRUE      NONE
+   PPIMAGE.STATS 	 OUTPUT  {OUTPUT}.stats       STATS     NONE      FPA        TRUE      NONE
+
+   PPIMAGE.BIN1        	 OUTPUT  {OUTPUT}.b1c.fits    IMAGE     NONE      FPA        TRUE      NONE
+   PPIMAGE.BIN2        	 OUTPUT  {OUTPUT}.b2c.fits    IMAGE     NONE      FPA        TRUE      NONE
+
+   PPIMAGE.JPEG1       	 OUTPUT  {OUTPUT}.b1.jpg      JPEG      NONE      FPA        TRUE      NONE
+   PPIMAGE.JPEG2       	 OUTPUT  {OUTPUT}.b2.jpg      JPEG      NONE      FPA        TRUE      NONE
+
+   PPMERGE.OUTPUT        OUTPUT   {OUTPUT}.fits       IMAGE     NONE      FPA        TRUE      NONE
+
+   DVOCORR.OUTPUT    	 OUTPUT  {OUTPUT}.fc.fits     IMAGE     NONE      FPA        TRUE      NONE
+   DVOFLAT.OUTPUT    	 OUTPUT  {OUTPUT}.co.fits     IMAGE     NONE      FPA        TRUE      NONE
+
+   PSPHOT.RESID        	 OUTPUT  {OUTPUT}.res.fits    IMAGE     NONE      FPA        TRUE      NONE
+   PSPHOT.BACKGND      	 OUTPUT  {OUTPUT}.bck.fits    IMAGE     NONE      FPA        TRUE      NONE
+   PSPHOT.BACKSUB      	 OUTPUT  {OUTPUT}.sub.fits    IMAGE     NONE      FPA        TRUE      NONE
+   PSPHOT.BACKMDL      	 OUTPUT  {OUTPUT}.mdl.fits    IMAGE     NONE      FPA        TRUE      NONE
+
+   PSPHOT.OUTPUT.RAW   	 OUTPUT  {OUTPUT}             RAW       NONE      FPA        TRUE      NONE
+   PSPHOT.OUTPUT.SX    	 OUTPUT  {OUTPUT}.sx          SX        NONE      FPA        TRUE      NONE
+   PSPHOT.OUTPUT.OBJ   	 OUTPUT  {OUTPUT}.obj         OBJ       NONE      FPA        TRUE      NONE
+   PSPHOT.OUTPUT.CMP   	 OUTPUT  {OUTPUT}.cmp         CMP       NONE      FPA        TRUE      NONE
+   PSPHOT.OUTPUT.CMF   	 OUTPUT  {OUTPUT}.cmf         CMF       NONE      FPA        TRUE      NONE
+
+   PSPHOT.PSF.SAVE     	 OUTPUT  {OUTPUT}.psf         PSF       NONE      FPA        TRUE      NONE
+
+   SOURCE.PLOT.MOMENTS   OUTPUT  {OUTPUT}.mnt.png     KAPA      NONE      FPA        TRUE      NONE
+   SOURCE.PLOT.PSFMODEL  OUTPUT  {OUTPUT}.psf.png     KAPA      NONE      FPA        TRUE      NONE
+   SOURCE.PLOT.APRESID   OUTPUT  {OUTPUT}.dap.png     KAPA      NONE      FPA        TRUE      NONE
+
+   PSASTRO.OUTPUT.CMP    OUTPUT  {OUTPUT}.smp         CMP       NONE      FPA        TRUE      NONE
+   PSASTRO.OUTPUT.CMF    OUTPUT  {OUTPUT}.smf         CMF       NONE      FPA        TRUE      NONE
+
+   PSWARP.OUTPUT         OUTPUT {OUTPUT}.fits         IMAGE     NONE      FPA        TRUE      NONE
+   PSWARP.OUTPUT.MASK    OUTPUT {OUTPUT}.mask.fits    MASK      NONE      FPA        TRUE      NONE
+   PSWARP.OUTPUT.WEIGHT  OUTPUT {OUTPUT}.wt.fits      WEIGHT    NONE      FPA        TRUE      NONE
+   PSWARP.BIN1           OUTPUT {OUTPUT}.b1.fits      IMAGE     NONE      FPA        TRUE      NONE
+   PSWARP.BIN2           OUTPUT {OUTPUT}.b2.fits      IMAGE     NONE      FPA        TRUE      NONE
+
+   SKYCELL.STATS         OUTPUT {OUTPUT}.stats        STATS     NONE      FPA        TRUE      NONE
+   SKYCELL.TEMPLATE      OUTPUT {OUTPUT}.skycell      SKYCELL   NONE      FPA        TRUE      NONE
+
+   PPSUB.OUTPUT          OUTPUT {OUTPUT}.fits         IMAGE     NONE      FPA        TRUE      NONE
+   PPSUB.OUTPUT.MASK     OUTPUT {OUTPUT}.mask.fits    MASK      NONE      FPA        TRUE      NONE
+   PPSUB.OUTPUT.WEIGHT   OUTPUT {OUTPUT}.wt.fits      WEIGHT    NONE      FPA        TRUE      NONE
+
+   PPSTACK.OUTPUT        OUTPUT {OUTPUT}.fits         IMAGE     NONE      FPA        TRUE      NONE
+   PPSTACK.OUTPUT.MASK   OUTPUT {OUTPUT}.mask.fits    MASK      NONE      FPA        TRUE      NONE
+   PPSTACK.OUTPUT.WEIGHT OUTPUT {OUTPUT}.weight.fits  WEIGHT    NONE      FPA        TRUE      NONE
+
+   PPSIM.OUTPUT          OUTPUT {OUTPUT}.fits         IMAGE     NONE      FPA        TRUE      NONE
+
+   PPARITH.OUTPUT.IMAGE  OUTPUT {OUTPUT}.fits         IMAGE     NONE      CHIP       TRUE      NONE
+   PPARITH.OUTPUT.MASK   OUTPUT {OUTPUT}.fits         MASK      NONE      CHIP       TRUE      NONE
+
+   LOG.IMFILE            OUTPUT {OUTPUT}.{CHIP.NAME}.log TEXT   NONE      CHIP       TRUE      NONE
+   LOG.EXP               OUTPUT {OUTPUT}.log          TEXT      NONE      FPA        TRUE      NONE
+END
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR cmf.hdr
+  CMF.DATA STR cmf.psf # use .PSF and .EXT?
+  CMF.XSRC STR xsrc # use .PSF and .EXT?
+  CMF.XFIT STR xfit # use .PSF and .EXT?
+
+  PSF.HEAD  STR	hdr
+  PSF.TABLE STR psf_model
+  PSF.RESID STR psf_resid
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	TAI
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+END
+
+PHOTCODE.RULE           STR     {CAMERA}.{FILTER.ID}.{CHIP.N}	# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/cmf.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/cmf.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/cmf.config	(revision 22322)
@@ -0,0 +1,84 @@
+# Pan-STARRS Imaging Sky Probe
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+	NAXIS		S32	0
+	TELESCOP	STR	ISP-1 
+	INSTRUME	STR	ISP-Apogee
+	DETECTOR	STR	ISP-Apogee-01
+	ISPCAMER	STR	Apogee U42
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	SEQID	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+	FPA.FILTER	STR	FILTNAME
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.TIME	STR	MJD-OBS
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.GAIN	STR	GAIN
+	CELL.READNOISE	STR	RDNOISE
+	CELL.XBIN	STR	XBIN
+	CELL.YBIN	STR	YBIN
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	65535
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/cmp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/cmp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/cmp.config	(revision 22322)
@@ -0,0 +1,84 @@
+# Pan-STARRS Imaging Sky Probe
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	FALSE
+	NAXIS		S32	0
+	TELESCOP	STR	ISP-1 
+	INSTRUME	STR	ISP-Apogee
+	DETECTOR	STR	ISP-Apogee-01
+	ISPCAMER	STR	Apogee U42
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	SEQID	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+	FPA.FILTER	STR	FILTNAME
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.TIME	STR	MJD-OBS
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.GAIN	STR	GAIN
+	CELL.READNOISE	STR	RDNOISE
+	CELL.XBIN	STR	XBIN
+	CELL.YBIN	STR	YBIN
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	65535
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/dvo.config	(revision 22322)
@@ -0,0 +1,50 @@
+
+# location of DVO database tables
+CATDIR			/data/ipp001.0/jester/stripe82/coadd/catdir
+
+# keywords used by DVO to interpret the headers
+
+# used by parse_time to find time-related keywords
+MJD-KEYWORD		NONE
+DATE-KEYWORD		DATE-OBS
+DATE-MODE		YYYY-MM-DD
+UT-KEYWORD		TAIHMS
+JD-KEYWORD		NONE
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		-7.0
+CAL_INSTMAG_MIN		-9.5
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# allowed astrometry error (arcseconds)
+ADDSTAR_MAX_CERROR	5.0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		5.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+CATMODE			MEF
+CATFORMAT		PANSTARRS_DEV_0
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/dvo.layout
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/dvo.layout	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/dvo.layout	(revision 22322)
@@ -0,0 +1,58 @@
+# this file defines the layout of the mosaic imager:
+
+# NCCD 40  # this needs work: sometimes 36, sometimes 40
+NCCD 36
+NAXIS1 2112
+NAXIS2 4644
+
+MOSAIC_X 11
+MOSAIC_Y  4
+
+# lines need to contain:
+
+CHIPID_KEYWORD EXTNAME
+
+# ID     CHIPID xoffset yoffset xflip yflip datasec         biassec            Xo      Yo      theta
+CCD.0     ccd00       1       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.1     ccd01       2       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.2     ccd02       3       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.3     ccd03       4       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.4     ccd04       5       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.5     ccd05       6       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.6     ccd06       7       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.7     ccd07       8       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.8     ccd08       9       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.9     ccd09       1       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.10    ccd10       2       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.11    ccd11       3       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.12    ccd12       4       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.13    ccd13       5       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.14    ccd14       6       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.15    ccd15       7       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.16    ccd16       8       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.17    ccd17       9       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.18    ccd18       1       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.19    ccd19       2       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.20    ccd20       3       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.21    ccd21       4       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.22    ccd22       5       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.23    ccd23       6       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.24    ccd24       7       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.25    ccd25       8       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.26    ccd26       9       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.27    ccd27       1       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.28    ccd28       2       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.29    ccd29       3       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.30    ccd30       4       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.31    ccd31       5       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.32    ccd32       6       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.33    ccd33       7       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.34    ccd34       8       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.35    ccd35       9       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.36    ccd36       0       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.37    ccd37      10       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.38    ccd38       0       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.39    ccd39      10       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+
+# can we use header to find biassec
+USE_BIASSEC 1
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/format.config	(revision 22322)
@@ -0,0 +1,112 @@
+# SDSS camera, starting from Pan-STARRS Imaging Sky Probe
+# Sebastian Jester jester at mpia.de August 7, 2007
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+#	NAXIS		S32	2
+	ORIGIN		STR	SDSS
+	TELESCOP	STR	2.5m
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS	STR	FRAME	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+#		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+#		CELL.BIASSEC.SOURCE	STR	HEADER
+#		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.TRIMSEC		STR	[1:2048,1:1361]
+#		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	ORIGIN
+# Need the following?
+	FPA.DETECTOR	STR	CCDLOC
+#	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBSTYPE	STR	FLAVOR
+	FPA.OBJECT	STR	OBJECT
+	FPA.FILTERID	STR	FILTER
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	IPA
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+#	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME    	STR     DATE-OBS TAIHMS # Two values are interpreted as date and time
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+#	FPA.TEMP	STR	CCDTEMP
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	EXPTIME
+	CELL.TIME	STR	DATE-OBS TAIHMS
+#	CELL.GAIN	STR	GAIN
+#	CELL.READNOISE	STR	RDNOISE
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+#	CELL.TIMESYS	STR	TIMESYS
+	CELL.XBIN	STR	COLBIN
+	CELL.YBIN	STR	ROWBIN
+# these were used for some early data
+#	CELL.XBIN	STR	XBIN
+#	CELL.YBIN	STR	YBIN
+#	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+# The gain is only roughly correct...
+	CELL.GAIN	F32	5.0
+# readnoise is from http://www.sdss.org/dr6/instruments/imager/index.html
+# Note that I have emails from Jim Gunn saying dark noise *is* important...
+	CELL.READNOISE	F32	5.0
+	FPA.AIRMASS	F32	1.3
+	CHIP.XSIZE	S32	2048
+	CHIP.YSIZE	S32	1361
+	CELL.XSIZE	S32	2048
+	CELL.YSIZE	S32	2048
+#	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	60000
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+#	CELL.XBIN	S32	1
+#	CELL.YBIN	S32	1
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	DEGREES
+	FPA.DEC		STR	DEGREES
+	FPA.TIME    	STR     SEPARATE YEAR.FIRST
+#	FPA.TIME	STR	MJD
+	CELL.TIME    	STR     SEPARATE YEAR.FIRST
+#	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/format_skycell.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/format_skycell.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/format_skycell.config	(revision 22322)
@@ -0,0 +1,110 @@
+# SDSS camera, starting from Pan-STARRS Imaging Sky Probe
+# Sebastian Jester jester at mpia.de August 7, 2007
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+#	NAXIS		S32	2
+	ORIGIN		STR	SDSS
+	TELESCOP	STR	2.5m
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	FRAME	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+#		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+#		CELL.BIASSEC.SOURCE	STR	HEADER
+#		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.TRIMSEC		STR	[1:2048,1:1361]
+#		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	ORIGIN
+# Need the following?
+	FPA.DETECTOR	STR	CCDLOC
+#	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBSTYPE	STR	FLAVOR
+	FPA.OBJECT	STR	OBJECT
+	FPA.FILTERID	STR	FILTER
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	IPA
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+#	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	TAI
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+#	FPA.TEMP	STR	CCDTEMP
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	EXPTIME
+	CELL.TIME	STR	TAI
+#	CELL.GAIN	STR	GAIN
+#	CELL.READNOISE	STR	RDNOISE
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+#	CELL.TIMESYS	STR	TIMESYS
+	CELL.XBIN	STR	COLBIN
+	CELL.YBIN	STR	ROWBIN
+# these were used for some early data
+#	CELL.XBIN	STR	XBIN
+#	CELL.YBIN	STR	YBIN
+#	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+# The gain is only roughly correct...
+	CELL.GAIN	F32	5.0
+# readnoise is from http://www.sdss.org/dr6/instruments/imager/index.html
+# Note that I have emails from Jim Gunn saying dark noise *is* important...
+	CELL.READNOISE	F32	5.0
+	FPA.AIRMASS	F32	1.3
+	CHIP.XSIZE	S32	2048
+	CHIP.YSIZE	S32	1361
+	CELL.XSIZE	S32	2048
+	CELL.YSIZE	S32	2048
+#	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	60000
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+#	CELL.XBIN	S32	1
+#	CELL.YBIN	S32	1
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	DEGREES
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/ppImage.config	(revision 22322)
@@ -0,0 +1,145 @@
+### ppImage recipe configuration file
+
+# Inherit everything from the top-level recipe file, except for what's below.
+
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS	STR z
+FRINGE.FILTERS	STR y
+
+# apply the following constraints when selecting a detrend image
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+  END
+  SHUTTER METADATA
+  END	
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+#   VERSION STR RAW
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTERID
+#   AIRMASS  STR FPA.AIRMASS
+#   TWILIGHT STR TWILIGHT:FPA.TIME
+  END
+END
+
+## these science-image analysis recipes are placed here set MASK = FALSE
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_DET_ONLY  METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# SDSS Softbias and photometry
+PPIMAGE_OP   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save base detrended image?
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save base detrended image?
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  OVERSCAN.CONSTANT BOOL  TRUE
+  OVERSCAN.VALUE  F32     1000.0
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL	  TRUE 		  # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL	  TRUE 		  # Save 2nd binned jpeg?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE            # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+
+# Overscan, bias, photometry, astrometry
+PPIMAGE_OA   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  CHIP.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL	  TRUE 		# Save 1st binned jpeg?
+  BIN2.JPEG       BOOL	  TRUE 		# Save 2nd binned jpeg?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/ppMerge.config	(revision 22322)
@@ -0,0 +1,3 @@
+### ppImage recipe configuration file
+
+# Inherit everything from the top-level recipe file, except for what's below.
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/psastro.config	(revision 22322)
@@ -0,0 +1,38 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+PSASTRO.PIXEL.SCALE    F32  10.0
+
+# pmAstromGridMatch:
+PSASTRO.GRID.MIN.ANGLE F32 -2.0
+PSASTRO.GRID.MAX.ANGLE F32 +2.0
+PSASTRO.GRID.DEL.ANGLE F32  0.25
+PSASTRO.GRID.MIN.SIGMA F32  5.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32   10000.
+PSASTRO.GRID.SCALE     F32     500
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# match radius in pixels for CHIP astrometry
+PSASTRO.MATCH.RADIUS   F32    8
+
+# pmAstromMatchFit
+PSASTRO.CHIP.ORDER     S32      3  # fit order
+PSASTRO.CHIP.NITER     S32      3  # fit clipping iterations
+PSASTRO.CHIP.NSIGMA    F32      3  # fit clipping sigmas
+
+PSASTRO.MAX.ERROR      F32      10.0 # max error in pixels
+PSASTRO.MIN.NSTAR      S32      10   # min fitted stars in solution
+
+MAG_MAX                F32      10
+PSASTRO.CATDIR         STR      SYNTH.BRIGHT ## XXX this should probably be using SYNTH.GRIZY
+PSASTRO.MATCH.LUMFUNC  BOOL     TRUE
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/psphot.config	(revision 22322)
@@ -0,0 +1,61 @@
+
+# turn these on to see specific outputs
+SAVE.BACKMDL	BOOL 	TRUE
+#SAVE.BACKGND	BOOL 	TRUE
+#SAVE.BACKSUB	BOOL 	TRUE
+#SAVE.RESID	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+SAVE.PLOTS      BOOL    TRUE
+
+# image statistics parameters
+IMSTATS_NPIX        S32  3000    	 # number of pixels to use for sky estimate boxes:
+
+PSF_SN_LIM          F32  10              # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+PSF_MODEL         STR  PS_MODEL_QGAUSS
+
+MOMENTS_SN_MIN      F32   10.0
+#EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+#FULL_FIT_SN_LIM      F32  50.0
+#AP_MIN_SN            F32  20.0
+PSF_CLUMP_NSIGMA   F32  2.5             # region of Sx,Sy plane to use for selecting PSF stars
+
+# PSFTREND must be a 2D polynomial
+# the specified values are ignored but define the active components of the polynomial
+PSF.TREND.MASK  METADATA  
+   NORDER_X         S32       3                # number of x orders
+   NORDER_Y         S32       3                # number of y orders
+   VAL_X00_Y00      F64       1                # polynomial coefficient
+
+   VAL_X01_Y00      F64       1                # polynomial coefficient
+   VAL_X00_Y01      F64       1                # polynomial coefficient
+
+   VAL_X02_Y00      F64       1                # polynomial coefficient
+   VAL_X01_Y01      F64       1                # polynomial coefficient
+   VAL_X00_Y02      F64       1                # polynomial coefficient
+
+   VAL_X03_Y00      F64       1                # polynomial coefficient
+   VAL_X02_Y01      F64       1                # polynomial coefficient
+   VAL_X01_Y02      F64       1                # polynomial coefficient
+   VAL_X00_Y03      F64       1                # polynomial coefficient
+   NELEMENTS        S32       10               # number of unmasked components
+END  # folder for 4D polynomial
+
+XMIN F32 15
+
+PSF.RESIDUALS       BOOL true
+PSF.RESIDUALS.SPATIAL_ORDER S32 1
+
+# BREAK_POINT         STR  ENSEMBLE
+OUTPUT.FORMAT       STR  PS1_DEV_0
+
+PSPHOT.SUMMIT METADATA
+ PEAKS_SMOOTH_SIGMA  F32  0.8 	   	 # peak significance threshold
+ PEAKS_NSIGMA_LIMIT  F32  50.0 	   	 # peak significance threshold
+ PEAKS_NMAX          S32  1000
+ SAVE.RESID	BOOL 	FALSE
+ PSF_SN_LIM          F32  25              # minimum S/N for stars used for PSF model
+ MOMENTS_SN_MIN      F32   25.0
+ PSF_MODEL         STR  PS_MODEL_PGAUSS
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/pswarp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/pswarp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/pswarp.config	(revision 22322)
@@ -0,0 +1,2 @@
+ASTROM.SOURCE	STR	NULL	# Source file rule for astrometry; NULL to use existing WCS
+ASTROM.ACCEPT	BOOL	TRUE	# Accept astrometric solution unconditionally?
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdss/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdss/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdss/rejections.config	(revision 22322)
@@ -0,0 +1,49 @@
+
+DARK 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.SN          F32  0.0
+  IMFILE.BIN.STDEV   F32  0.0
+  IMFILE.BIN.SN      F32  0.0
+  IMFILE.FLUX        F32  2.0
+  EXP.MEAN           F32  0.0
+  EXP.STDEV          F32  0.0
+  EXP.MEANSTDEV      F32  0.0
+  EXP.SN             F32  0.0
+  EXP.BIN.STDEV      F32  0.0
+  EXP.BIN.SN         F32  0.0
+  EXP.FLUX           F32  2.0
+  ENSEMBLE.MEAN      F32  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+FRINGE 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.SN          F32  0.0
+  IMFILE.BIN.STDEV   F32  0.0
+  IMFILE.BIN.SN      F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  0.0
+  EXP.STDEV          F32  0.0
+  EXP.MEANSTDEV      F32  0.0
+  EXP.SN             F32  0.0
+  EXP.BIN.STDEV      F32  0.0
+  EXP.BIN.SN         F32 10.0
+  EXP.FLUX           F32  0.0
+  ENSEMBLE.MEAN      F32  0.0
+  ENSEMBLE.STDEV     F32  0.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/Makefile.am	(revision 22322)
@@ -0,0 +1,26 @@
+
+installdir = $(datadir)/ippconfig/sdssmosaic/
+
+install_files = \
+	dvo.config \
+	dvo.layout \
+	camera.config \
+	format.config \
+	format_mef.config \
+	cmp.config \
+	cmf.config \
+	ppImage.config \
+	ppMerge.config \
+	psastro.config \
+	psphot.config \
+	pswarp.config \
+	rejections.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/camera.config	(revision 22322)
@@ -0,0 +1,84 @@
+# Camera configuration file for co-adding reduced SDSS images of stripe82
+# Attempt to designate all 6 camcols as a single mosaic camera
+# Copied from SDSS camera config files, which in turn were copied 
+# from ISP config files 
+# Sebastian Jester jester at mpia.de August 27, 2007
+
+# File formats that we know about
+FORMATS         METADATA
+	MEF		STR	sdssmosaic/format_mef.config	# MEF version, required for psastro
+        SDSSmosaic	STR     sdssmosaic/format.config
+END
+ 
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+	CHIP1	STR	Cell
+	CHIP2	STR	Cell
+	CHIP3	STR	Cell
+	CHIP4	STR	Cell
+	CHIP5	STR	Cell
+	CHIP6	STR	Cell
+END
+
+# valid filter names and corresponding IDs
+FILTER.ID       METADATA
+        g       STR     g
+        r       STR     r
+        i       STR     i
+        z       STR     z
+	u       STR     u
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	FPA	STR	fpa
+END
+
+DVO.CAMERADIR	STR	sdss		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES         METADATA
+        PSPHOT          STR     sdssmosaic/psphot.config               # psphot details
+        PSASTRO         STR     sdssmosaic/psastro.config              # psastro details
+        PPIMAGE         STR     sdssmosaic/ppImage.config              # Recipes for ppImage
+        PPMERGE         STR     sdssmosaic/ppMerge.config              # Recipes for ppMerge
+        PSWARP          STR     sdssmosaic/pswarp.config              # Recipes for pswarp
+	REJECTIONS	STR     sdssmosaic/rejections.config
+END
+
+REDUCTIONS	STR	sdssmosaic/reductionClasses.mdc
+
+FILERULES	STR	recipes/filerules-split.mdc
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR cmf.hdr
+  CMF.DATA STR cmf.psf
+  CMF.XSRC STR cmf.xsrc
+  CMF.XFIT STR cmf.xfit
+
+  PSF.HEAD  STR	hdr
+  PSF.TABLE STR psf_model
+  PSF.RESID STR psf_resid
+
+  REF.ASTROM STR ref_astrom
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	TAI
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+END
+
+PHOTCODE.RULE           STR     SDSS.{FILTER.ID}.{CHIP.N}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/cmf.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/cmf.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/cmf.config	(revision 22322)
@@ -0,0 +1,84 @@
+# Pan-STARRS Imaging Sky Probe
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+	NAXIS		S32	0
+	TELESCOP	STR	ISP-1 
+	INSTRUME	STR	ISP-Apogee
+	DETECTOR	STR	ISP-Apogee-01
+	ISPCAMER	STR	Apogee U42
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	SEQID	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+	FPA.FILTER	STR	FILTNAME
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.TIME	STR	MJD-OBS
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.GAIN	STR	GAIN
+	CELL.READNOISE	STR	RDNOISE
+	CELL.XBIN	STR	XBIN
+	CELL.YBIN	STR	YBIN
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	65535
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/cmp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/cmp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/cmp.config	(revision 22322)
@@ -0,0 +1,84 @@
+# Pan-STARRS Imaging Sky Probe
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	FALSE
+	NAXIS		S32	0
+	TELESCOP	STR	ISP-1 
+	INSTRUME	STR	ISP-Apogee
+	DETECTOR	STR	ISP-Apogee-01
+	ISPCAMER	STR	Apogee U42
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	SEQID	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+	FPA.FILTER	STR	FILTNAME
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.TIME	STR	MJD-OBS
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.GAIN	STR	GAIN
+	CELL.READNOISE	STR	RDNOISE
+	CELL.XBIN	STR	XBIN
+	CELL.YBIN	STR	YBIN
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	65535
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/dvo.config	(revision 22322)
@@ -0,0 +1,71 @@
+DVO.CAMERADIR		/home/jester/IPP/ippconfig/sdssmosaic
+
+# location of DVO database tables
+CATDIR			/disk1/jester/data/SDSS/stripe82/coadd/catdir
+
+# keywords used by DVO to interpret the headers
+
+# used by parse_time to find time-related keywords
+MJD-KEYWORD		NONE
+DATE-KEYWORD		DATE-OBS
+DATE-MODE		YYYY-MM-DD
+UT-KEYWORD		TAIHMS
+JD-KEYWORD		NONE
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		-7.0
+CAL_INSTMAG_MIN		-9.5
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# allowed astrometry error (arcseconds)
+ADDSTAR_MAX_CERROR	2.0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		2.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
+
+PM_DT_MIN               0.25
+PM_TOOFEW               4
+POS_TOOFEW              1
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/dvo.layout
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/dvo.layout	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/dvo.layout	(revision 22322)
@@ -0,0 +1,59 @@
+# this file defines the layout of the mosaic imager:
+# 
+# SJ: I don't know what it does... help!
+
+NCCD 6
+NAXIS1 2048
+NAXIS2 1361
+
+MOSAIC_X  6
+MOSAIC_Y  1
+
+# lines need to contain:
+
+CHIPID_KEYWORD EXTNAME
+
+# ID     CHIPID xoffset yoffset xflip yflip datasec         biassec            Xo      Yo      theta
+CCD.0     ccd00       1       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.1     ccd01       2       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.2     ccd02       3       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.3     ccd03       4       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.4     ccd04       5       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.5     ccd05       6       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.6     ccd06       7       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.7     ccd07       8       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.8     ccd08       9       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.9     ccd09       1       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.10    ccd10       2       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.11    ccd11       3       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.12    ccd12       4       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.13    ccd13       5       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.14    ccd14       6       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.15    ccd15       7       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.16    ccd16       8       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.17    ccd17       9       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.18    ccd18       1       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.19    ccd19       2       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.20    ccd20       3       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.21    ccd21       4       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.22    ccd22       5       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.23    ccd23       6       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.24    ccd24       7       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.25    ccd25       8       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.26    ccd26       9       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.27    ccd27       1       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.28    ccd28       2       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.29    ccd29       3       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.30    ccd30       4       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.31    ccd31       5       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.32    ccd32       6       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.33    ccd33       7       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.34    ccd34       8       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.35    ccd35       9       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.36    ccd36       0       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.37    ccd37      10       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.38    ccd38       0       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.39    ccd39      10       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+
+# can we use header to find biassec
+USE_BIASSEC 1
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/format.config	(revision 22322)
@@ -0,0 +1,146 @@
+# SDSSmosaic camera, starting from SDSS from Pan-STARRS Imaging Sky Probe
+# Sebastian Jester jester at mpia.de August 7, 2007
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+#	NAXIS		S32	2
+	ORIGIN		STR	SDSS
+	TELESCOP	STR	2.5m
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	CHIP	# The FITS file represents one camcol = chip, 
+					# 6 of which make the FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	FRAME	# A PHU keyword for unique identifier within 
+					# the hierarchy level - just needs to be the same
+					# for all 6 files belonging to the same "exposure"
+	CHIP.NAME	STR	CAMCOL	# Distinguishes chip name
+	CONTENT		STR	CAMCOL
+ # Is the following line the origin of the weirdly named output files?
+	CONTENT.RULE	STR	{CHIP.NUM} # How to derive the CONTENT when writing
+END
+
+# What's in the FITS file?
+# CONTENTS	STR	Chip:Cell:amplifier
+CONTENTS	METADATA
+	# Content name, chipType
+	1		STR	CHIP1:Chip
+	2		STR	CHIP2:Chip
+	3		STR	CHIP3:Chip
+	4		STR	CHIP4:Chip
+	5		STR	CHIP5:Chip
+	6		STR	CHIP6:Chip
+END
+	
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	Chip	STR	Cell:Cell
+END
+
+
+# Specify the cell data
+CELLS	METADATA
+	Cell	METADATA
+#		CELL.TRIMSEC.SOURCE	STR	HEADER
+#		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC		STR	[1:2048,1:1361]
+#		CELL.BIASSEC.SOURCE	STR	HEADER
+#		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+# NB: If we could look up the gains etc. separately for both amplifiers,
+# we could split the chips into cells for each amp
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+# FPA.INSTRUMENT actually needs to be identical to the CAMERA, 
+# so I'm moving it from here down to the defaults to make sure that
+# this is ture
+#	FPA.INSTRUMENT	STR	ORIGIN
+	FPA.DETECTOR	STR	CCDLOC
+#	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBSTYPE	STR	FLAVOR
+	FPA.OBJECT	STR	OBJECT
+	FPA.FILTERID	STR	FILTER
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	IPA
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+#	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME    	STR     DATE-OBS TAIHMS # Two values are interpreted as date and time
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+#	FPA.TEMP	STR	CCDTEMP
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	EXPTIME
+	CELL.TIME	STR	DATE-OBS TAIHMS
+#	CELL.GAIN	STR	GAIN
+#	CELL.READNOISE	STR	RDNOISE
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+#	CELL.TIMESYS	STR	TIMESYS
+	CELL.XBIN	STR	COLBIN
+	CELL.YBIN	STR	ROWBIN
+# these were used for some early data
+#	CELL.XBIN	STR	XBIN
+#	CELL.YBIN	STR	YBIN
+#	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+# FPA.CAMERA contains the name of the camera according to the config system (i.e. SDSSmosaic)
+# FPA.INSTRUMENT contains the name of the camera according to the fits headers (i.e. itself)
+# Normally, this will be in the header keyword INSTRUME but for this one, we override it to 
+# SDSSmosaic to avoid confusion between this and the non-mosaic SDSS configuration.
+	INSTRUME	STR	SDSSmosaic
+	FPA.INSTRUMENT	STR	INSTRUME
+# The gain is only roughly correct...
+	CELL.GAIN	F32	5.0
+# readnoise is from http://www.sdss.org/dr6/instruments/imager/index.html
+# Note that I have emails from Jim Gunn saying dark noise *is* important...
+	CELL.READNOISE	F32	5.0
+	FPA.AIRMASS	F32	1.3
+	CHIP.XSIZE	S32	2048
+	CHIP.YSIZE	S32	1361
+	CELL.XSIZE	S32	2048
+	CELL.YSIZE	S32	1361
+#	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	60000
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+#	CELL.XBIN	S32	1
+#	CELL.YBIN	S32	1
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	DEGREES
+	FPA.DEC		STR	DEGREES
+	FPA.TIME    	STR     SEPARATE YEAR.FIRST
+#	FPA.TIME	STR	MJD
+	CELL.TIME    	STR     SEPARATE YEAR.FIRST
+#	CELL.TIME	STR	MJD
+	CHIP.NUM	STR	FORTRAN
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/format_mef.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/format_mef.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/format_mef.config	(revision 22322)
@@ -0,0 +1,140 @@
+# SDSSmosaic camera, starting from SDSS from Pan-STARRS Imaging Sky Probe
+# Sebastian Jester jester at mpia.de August 7, 2007
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+	ORIGIN		STR	SDSS
+	TELESCOP	STR	2.5m
+	PSFORMAT	STR	MEF
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA
+	EXTENSIONS	STR	CHIP
+	FPA.OBS		STR	FRAME	# A PHU keyword for unique identifier within 
+					# the hierarchy level - just needs to be the same
+					# for all 6 files belonging to the same "exposure"
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Chips name, chipType
+	chip1		STR	CHIP1:Chip
+	chip2		STR	CHIP2:Chip
+	chip3		STR	CHIP3:Chip
+	chip4		STR	CHIP4:Chip
+	chip5		STR	CHIP5:Chip
+	chip6		STR	CHIP6:Chip
+END
+	
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	Chip	STR	Cell:Cell
+END
+
+
+# Specify the cell data
+CELLS	METADATA
+	Cell	METADATA
+#		CELL.TRIMSEC.SOURCE	STR	HEADER
+#		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC		STR	[1:2048,1:1361]
+#		CELL.BIASSEC.SOURCE	STR	HEADER
+#		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+# NB: If we could look up the gains etc. separately for both amplifiers,
+# we could split the chips into cells for each amp
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+# FPA.INSTRUMENT actually needs to be identical to the CAMERA, 
+# so I'm moving it from here down to the defaults to make sure that
+# this is ture
+#	FPA.INSTRUMENT	STR	ORIGIN
+	FPA.DETECTOR	STR	CCDLOC
+#	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBSTYPE	STR	FLAVOR
+	FPA.OBJECT	STR	OBJECT
+	FPA.FILTERID	STR	FILTER
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	IPA
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+#	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME    	STR     DATE-OBS TAIHMS # Two values are interpreted as date and time
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+#	FPA.TEMP	STR	CCDTEMP
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	EXPTIME
+	CELL.TIME	STR	DATE-OBS TAIHMS
+#	CELL.GAIN	STR	GAIN
+#	CELL.READNOISE	STR	RDNOISE
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+#	CELL.TIMESYS	STR	TIMESYS
+	CELL.XBIN	STR	COLBIN
+	CELL.YBIN	STR	ROWBIN
+# these were used for some early data
+#	CELL.XBIN	STR	XBIN
+#	CELL.YBIN	STR	YBIN
+#	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+# FPA.CAMERA contains the name of the camera according to the config system (i.e. SDSSmosaic)
+# FPA.INSTRUMENT contains the name of the camera according to the fits headers (i.e. itself)
+# Normally, this will be in the header keyword INSTRUME but for this one, we override it to 
+# SDSSmosaic to avoid confusion between this and the non-mosaic SDSS configuration.
+	INSTRUME	STR	SDSSmosaic
+	FPA.INSTRUMENT	STR	INSTRUME
+# The gain is only roughly correct...
+	CELL.GAIN	F32	5.0
+# readnoise is from http://www.sdss.org/dr6/instruments/imager/index.html
+# Note that I have emails from Jim Gunn saying dark noise *is* important...
+	CELL.READNOISE	F32	5.0
+	FPA.AIRMASS	F32	1.3
+	CHIP.XSIZE	S32	2048
+	CHIP.YSIZE	S32	1361
+	CELL.XSIZE	S32	2048
+	CELL.YSIZE	S32	1361
+#	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	60000
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+#	CELL.XBIN	S32	1
+#	CELL.YBIN	S32	1
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	DEGREES
+	FPA.DEC		STR	DEGREES
+	FPA.TIME    	STR     SEPARATE YEAR.FIRST
+#	FPA.TIME	STR	MJD
+	CELL.TIME    	STR     SEPARATE YEAR.FIRST
+#	CELL.TIME	STR	MJD
+	CHIP.NUM	STR	FORTRAN
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/format_skycell.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/format_skycell.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/format_skycell.config	(revision 22322)
@@ -0,0 +1,110 @@
+# SDSS camera, starting from Pan-STARRS Imaging Sky Probe
+# Sebastian Jester jester at mpia.de August 7, 2007
+
+# How to identify this type - if the following header values match, it's accepted.
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+#	NAXIS		S32	2
+	ORIGIN		STR	SDSS
+	TELESCOP	STR	2.5m
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	FRAME	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+#		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+#		CELL.BIASSEC.SOURCE	STR	HEADER
+#		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.TRIMSEC		STR	[1:2048,1:1361]
+#		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	ORIGIN
+# Need the following?
+	FPA.DETECTOR	STR	CCDLOC
+#	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBSTYPE	STR	FLAVOR
+	FPA.OBJECT	STR	OBJECT
+	FPA.FILTERID	STR	FILTER
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	IPA
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+#	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	TAI
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+#	FPA.TEMP	STR	CCDTEMP
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	EXPTIME
+	CELL.TIME	STR	TAI
+#	CELL.GAIN	STR	GAIN
+#	CELL.READNOISE	STR	RDNOISE
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+#	CELL.TIMESYS	STR	TIMESYS
+	CELL.XBIN	STR	COLBIN
+	CELL.YBIN	STR	ROWBIN
+# these were used for some early data
+#	CELL.XBIN	STR	XBIN
+#	CELL.YBIN	STR	YBIN
+#	CELL.BAD	STR	BADLEVEL
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+# The gain is only roughly correct...
+	CELL.GAIN	F32	5.0
+# readnoise is from http://www.sdss.org/dr6/instruments/imager/index.html
+# Note that I have emails from Jim Gunn saying dark noise *is* important...
+	CELL.READNOISE	F32	5.0
+	FPA.AIRMASS	F32	1.3
+	CHIP.XSIZE	S32	2048
+	CHIP.YSIZE	S32	1361
+	CELL.XSIZE	S32	2048
+	CELL.YSIZE	S32	2048
+#	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	60000
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+#	CELL.XBIN	S32	1
+#	CELL.YBIN	S32	1
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	DEGREES
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/ppImage.config	(revision 22322)
@@ -0,0 +1,155 @@
+### ppImage recipe configuration file
+
+# Inherit everything from the top-level recipe file, except for what's below.
+
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS	STR z
+# FRINGE.FILTERS	STR y
+
+# apply the following constraints when selecting a detrend image
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+  END
+  SHUTTER METADATA
+  END	
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+#   VERSION STR RAW
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTERID
+#   AIRMASS  STR FPA.AIRMASS
+#   TWILIGHT STR TWILIGHT:FPA.TIME
+  END
+  MASK METADATA
+    FILTER   STR FPA.FILTERID
+  END
+END
+
+## these science-image analysis recipes are placed here set MASK = FALSE
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_DET_ONLY  METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    TRUE            # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# SDSS Softbias and photometry
+PPIMAGE_OP_SOFT   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save base detrended image?
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save base detrended image?
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  OVERSCAN.CONSTANT BOOL  TRUE
+  OVERSCAN.VALUE  F32     1000.0
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE            # Mask bad pixels
+# To turn on masking, use the following lines:
+#  MASK            BOOL    TRUE            # Mask bad pixels
+#  MASK.VALUE	  U8	  1		  # Bad-pixel value in my SDSS masks (see IPPwiki/stripe82)
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL	  TRUE 		  # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL	  TRUE 		  # Save 2nd binned jpeg?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE            # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# SDSS Softbias, photometry, astrometry
+PPIMAGE_OA_SOFT   METADATA
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL   FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL   FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL   TRUE            # Save base detrended image?
+  CHIP.WEIGHT.FITS BOOL   TRUE            # Save base detrended image?
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  OVERSCAN.CONSTANT BOOL  TRUE
+  OVERSCAN.VALUE  F32     1000.0
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE            # Mask bad pixels
+# To turn on masking, use the following lines:
+#  MASK            BOOL    TRUE            # Mask bad pixels
+#  MASK.VALUE	  U8	  1		  # Bad-pixel value in my SDSS masks (see IPPwiki/stripe82)
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL	  TRUE 		  # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL	  TRUE 		  # Save 2nd binned jpeg?
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    TRUE            # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/ppMerge.config	(revision 22322)
@@ -0,0 +1,3 @@
+### ppImage recipe configuration file
+
+# Inherit everything from the top-level recipe file, except for what's below.
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/psastro.config	(revision 22322)
@@ -0,0 +1,108 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+PSASTRO.PIXEL.SCALE    F32  10.0
+
+# pmAstromGridMatch:
+PSASTRO.GRID.MIN.ANGLE F32 -2.0
+PSASTRO.GRID.MAX.ANGLE F32 +2.0
+PSASTRO.GRID.DEL.ANGLE F32  0.25
+PSASTRO.GRID.MIN.SIGMA F32  5.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+# PSASTRO.GRID.OFFSET    F32     10000.
+PSASTRO.GRID.OFFSET    F32     100.
+PSASTRO.GRID.SCALE     F32     500
+PSASTRO.GRID.NSTAR.MAX S32     800 # max stars accepted for fitting
+
+PSASTRO.MAX.NRAW      S32       800   # max stars accepted for fitting (0 for all)
+PSASTRO.MAX.NREF      S32      5000   # max stars accepted for fitting (0 for all)
+
+# extra field for ref stars:
+PSASTRO.FIELD.PADDING  F32     0.15
+
+# 
+PSASTRO.MIN.INST.MAG.RAW       F32     -15.0   # min instrumental magnitude for stars accepted for fitting
+PSASTRO.MAX.INST.MAG.RAW       F32       0.0   # max instrumental magnitude for stars accepted for fitting
+
+#
+PSASTRO.GRID.MIN.ANGLE F32 -2.0 # start angle (degrees)
+PSASTRO.GRID.MAX.ANGLE F32 +2.0
+PSASTRO.GRID.DEL.ANGLE F32  0.5
+
+#
+PSASTRO.GRID.MIN.SCALE F32  1.00
+PSASTRO.GRID.MAX.SCALE F32  1.00
+PSASTRO.GRID.DEL.SCALE F32  0.01
+
+# single-chip radius match in pixels
+PSASTRO.MATCH.RADIUS.N0 F32    90
+PSASTRO.MATCH.RADIUS.N1 F32    60
+PSASTRO.MATCH.RADIUS.N2 F32    30
+PSASTRO.MATCH.RADIUS.N3 F32    20
+PSASTRO.MATCH.RADIUS.N4 F32    20
+PSASTRO.MATCH.RADIUS.N5 F32    20
+PSASTRO.MATCH.RADIUS.N6 F32    10
+PSASTRO.MATCH.RADIUS.N7 F32    10
+PSASTRO.MATCH.FIT.NITER S32     8
+
+# XXX Test for taurus
+PSASTRO.GRID.MIN.SIGMA  F32  1.0
+PSASTRO.IGNORE          STR  BLEND
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# This used to be true...
+PSASTRO.USE.MODEL             BOOL     FALSE
+
+# PSASTRO.FIX.CHIPS             BOOL     TRUE
+PSASTRO.PIXEL.TOLERANCE       F32      20.0
+PSASTRO.ANGLE.TOLERANCE       F32      1.0
+
+# now use PSASTRO.CATDIR, it seems - and probably should only use it
+# once anyway - used to have both synth.grizy and 2mass listed here...
+# 2MASS is a symbolic name that needs to be defined in site.config
+# in the PSASTRO.CATDIRS METADATA section. SYNTH.GRIZY is currently
+# the other option.
+PSASTRO.CATDIR STR 2MASS
+
+# Mosaic Astrometry options
+PSASTRO.MOSAIC.MODE         BOOL   FALSE
+# PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.80 # max allow error for valid solution (arcsec)
+# PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.20 # max allow error for valid solution (arcsec)
+# PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.80 # max allow error for valid solution (arcsec)
+# PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.50 # max allow error for valid solution (arcsec)
+
+PSASTRO.MOSAIC.MAX.ERROR.N0 F32    5.00 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N1 F32    5.00 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N2 F32    5.00 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N3 F32    5.00 # max allow error for valid solution (arcsec)
+
+# mosaic radius match in pixels
+PSASTRO.MOSAIC.RADIUS.N0    F32    10
+PSASTRO.MOSAIC.RADIUS.N1    F32    5
+PSASTRO.MOSAIC.RADIUS.N2    F32    3
+
+PSASTRO.MOSAIC.CHIP.ORDER     S32      1  # limit chip-fit order to 1
+PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      1 # fit order (-1 means use default)
+
+# DVO.GETSTAR STR /home/kiawe/eugene/psconfig/dev.lin64/bin/getstar
+
+# we need to allow a loose fit if we are fitting to 2mass (50mas internal error -> 100mas limit?)
+# if we are fitting against quality digital data, we can require tighter constraints
+
+PSASTRO.MODEL.REF.CHIP        STR      XY33
+
+# Do I need to change this photcode now that I'm using 30 SDSS photcodes?
+DVO.GETSTAR.PHOTCODE        STR      g
+DVO.GETSTAR.MAG.MAX         F32      17.0
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/psphot.config	(revision 22322)
@@ -0,0 +1,72 @@
+
+# turn these on to see specific outputs
+SAVE.BACKMDL	BOOL 	TRUE
+#SAVE.BACKGND	BOOL 	TRUE
+#SAVE.BACKSUB	BOOL 	TRUE
+#SAVE.RESID	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+SAVE.PLOTS      BOOL    TRUE
+
+# image statistics parameters
+IMSTATS_NPIX        S32  3000    	 # number of pixels to use for sky estimate boxes:
+
+PSF_SN_LIM          F32  10              # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+PSF_MODEL         STR  PS_MODEL_QGAUSS
+
+MOMENTS_SN_MIN      F32   10.0
+#EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+#FULL_FIT_SN_LIM      F32  50.0
+#AP_MIN_SN            F32  20.0
+MEASURE.APTREND	                    BOOL  FALSE
+PSF_CLUMP_NSIGMA   F32  2.5             # region of Sx,Sy plane to use for selecting PSF stars
+
+## # PSFTREND must be a 2D polynomial
+## # the specified values are ignored but define the active components of the polynomial
+## PSF.TREND.MASK  METADATA  
+##    NORDER_X         S32       3                # number of x orders
+##    NORDER_Y         S32       3                # number of y orders
+##    VAL_X00_Y00      F64       1                # polynomial coefficient
+## 
+##    VAL_X01_Y00      F64       1                # polynomial coefficient
+##    VAL_X00_Y01      F64       1                # polynomial coefficient
+## 
+##    VAL_X02_Y00      F64       1                # polynomial coefficient
+##    VAL_X01_Y01      F64       1                # polynomial coefficient
+##    VAL_X00_Y02      F64       1                # polynomial coefficient
+## 
+##    VAL_X03_Y00      F64       1                # polynomial coefficient
+##    VAL_X02_Y01      F64       1                # polynomial coefficient
+##    VAL_X01_Y02      F64       1                # polynomial coefficient
+##    VAL_X00_Y03      F64       1                # polynomial coefficient
+##    NELEMENTS        S32       10               # number of unmasked components
+## END  # folder for 4D polynomial
+
+XMIN F32 15
+
+PSF.RESIDUALS       BOOL true
+PSF.RESIDUALS.SPATIAL_ORDER S32 1
+
+# The parameter BREAK_POINT stopped the analysis after first linear
+# fit to sources.  Should be NONE or commented out.
+# BREAK_POINT         STR  ENSEMBLE
+OUTPUT.FORMAT       STR  PS1_DEV_1
+
+PSPHOT.SUMMIT METADATA
+ PEAKS_SMOOTH_SIGMA  F32  0.8 	   	 # peak significance threshold
+ PEAKS_NSIGMA_LIMIT  F32  50.0 	   	 # peak significance threshold
+ PEAKS_NMAX          S32  1000
+ SAVE.RESID	BOOL 	FALSE
+ PSF_SN_LIM          F32  25              # minimum S/N for stars used for PSF model
+ MOMENTS_SN_MIN      F32   25.0
+ PSF_MODEL         STR  PS_MODEL_PGAUSS
+END
+
+# Turn on use of footprints to avoid objectcs being found in
+# diffraction spikes etc. of bright stars
+USE_FOOTPRINTS			BOOL	T	# use new pmFootprint peak packaging
+
+# Set aperture correction radius to same 7.4" as SDSS (see EDR paper)
+PSF_REF_RADIUS      F32  18.7            # aperture magnitudes are scaled via 
+					 # curve-of-growth to this radius
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/pswarp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/pswarp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/pswarp.config	(revision 22322)
@@ -0,0 +1,11 @@
+### Recipe for pswarp
+
+GRID.NX			S32	128		# Iso-astrom grid size in x (pixels)
+GRID.NY			S32	128		# Iso-astrom grid size in y (pixels)
+INTERPOLATION.MODE	STR	LANCZOS3	# Interpolation mode to use
+POOR.FRAC		F32	0.01		# Max fraction of bad flux for a "poor" warped pixel
+ASTROM.SOURCE		STR	PSASTRO.OUTPUT	# Source file rule for astrometry, or NULL
+#ASTROM.SOURCE		STR	NULL		# Source file rule for astrometry, or NULL
+ASTROM.ACCEPT		BOOL	TRUE		# Accept astrometric solution unconditionally?
+ASTROM.DEPTH		STR	SPLIT           # Source file rule for astrometry, or NULL
+ACCEPT.FRAC		F32	0.1		# Minimum fraction of good pixels to accept result
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/reductionClasses.mdc
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/reductionClasses.mdc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/reductionClasses.mdc	(revision 22322)
@@ -0,0 +1,21 @@
+# Reduction classes
+# REDUCTION	METADATA
+	# Detrend processing not needed for these
+        # photometry only for pre-detrended images
+	# This one used to be called PROCESSED
+	SDSSphotonly	METADATA
+		CHIP		STR	PPIMAGE_OP_SOFT
+ 		JPEG_BIN1       STR     PPIMAGE_J1
+ 		JPEG_BIN2       STR     PPIMAGE_J2
+		FAKEPHOT	STR	FAKEPHOT
+		ADDSTAR		STR	ADDSTAR
+	END
+        # photometry and astrometry for pre-detrended images
+	SDSSphotast	METADATA
+		CHIP		STR	PPIMAGE_OA_SOFT
+ 		JPEG_BIN1       STR     PPIMAGE_J1
+ 		JPEG_BIN2       STR     PPIMAGE_J2
+		FAKEPHOT	STR	FAKEPHOT
+		ADDSTAR		STR	ADDSTAR
+	END
+# END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/sdssmosaic/rejections.config	(revision 22322)
@@ -0,0 +1,45 @@
+
+DARK METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  0.0
+  IMFILE.STDEV       F32  0.0
+  IMFILE.MEANSTDEV   F32  0.0
+  IMFILE.SN          F32  0.0
+  IMFILE.BIN.STDEV   F32  0.0
+  IMFILE.BIN.SN      F32  0.0
+  IMFILE.FLUX        F32  2.0
+  EXP.MEAN           F32  0.0
+  EXP.STDEV          F32  0.0
+  EXP.MEANSTDEV      F32  0.0
+  EXP.SN             F32  0.0
+  EXP.BIN.STDEV      F32  0.0
+  EXP.BIN.SN         F32  0.0
+  EXP.FLUX           F32  2.0
+  ENSEMBLE.MEAN      F32  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+FRINGE METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  0.0
+  IMFILE.STDEV       F32  0.0
+  IMFILE.MEANSTDEV   F32  0.0
+  IMFILE.SN          F32  0.0
+  IMFILE.BIN.STDEV   F32  0.0
+  IMFILE.BIN.SN      F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  0.0
+  EXP.STDEV          F32  0.0
+  EXP.MEANSTDEV      F32  0.0
+  EXP.SN             F32  0.0
+  EXP.BIN.STDEV      F32  0.0
+  EXP.BIN.SN         F32 10.0
+  EXP.FLUX           F32  0.0
+  ENSEMBLE.MEAN      F32  0.0
+  ENSEMBLE.STDEV     F32  0.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/Makefile.am	(revision 22322)
@@ -0,0 +1,21 @@
+
+installdir = $(datadir)/ippconfig/simmosaic
+
+install_files = \
+	camera.config \
+	format_split.config \
+	format_together.config \
+	ppImage.config \
+	psastro.config \
+	psphot.config \
+	rejections.config \
+	dvo.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/camera.config	(revision 22322)
@@ -0,0 +1,86 @@
+# Camera configuration file for simulated mosaic
+
+# File formats that we know about
+FORMATS         METADATA
+	TOGETHER STR	simmosaic/format_together.config
+	SPLIT	STR	simmosaic/format_split.config
+END
+ 
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        Chip00		STR	Cell00 Cell01 Cell10 Cell11
+        Chip01		STR	Cell00 Cell01 Cell10 Cell11
+        Chip10		STR	Cell00 Cell01 Cell10 Cell11
+        Chip11		STR	Cell00 Cell01 Cell10 Cell11
+END
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+	NONE	STR	NONE
+	B	STR	B
+	V	STR	V
+	R	STR	R
+	I	STR	I
+	g	STR	g
+	r	STR	r
+	i	STR	i
+	z	STR	z
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	FPA	STR	fpa
+	CHIP	STR	{CHIP.NAME}
+	CELL	STR	{CHIP.NAME}.{CELL.NAME}
+	fpa	STR	fpa
+	chip	STR	{CHIP.NAME}
+	cell	STR	{CHIP.NAME}.{CELL.NAME}
+END
+
+DVO.CAMERADIR	STR	simmosaic		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES		METADATA
+        PPIMAGE         STR     simmosaic/ppImage.config	# Default: all (normal) options on
+	PSPHOT		STR	simmosaic/psphot.config		# psphot details
+	PSASTRO		STR	simmosaic/psastro.config	# psastro details
+	REJECTIONS	STR	simmosaic/rejections.config	# Rejection limits
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES       STR     recipes/filerules-mef.mdc
+
+EXTNAME.RULES	METADATA
+	CMF.HEAD	STR	{CHIP.NAME}.hdr
+	CMF.DATA	STR	{CHIP.NAME}.psf
+        CMF.XSRC        STR     {CHIP.NAME}.xsrc # use .PSF and .EXT?
+        CMF.XFIT        STR     {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+	PSF.HEAD	STR	{CHIP.NAME}.hdr
+	PSF.TABLE	STR	{CHIP.NAME}.psf_model
+	PSF.RESID	STR	{CHIP.NAME}.psf_resid
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.N}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/dvo.config	(revision 22322)
@@ -0,0 +1,58 @@
+
+# keywords used by DVO to interpret the headers
+
+# keyword abstractions:
+DATE-KEYWORD		NONE
+UT-KEYWORD		NONE
+JD-KEYWORD		NONE
+MJD-KEYWORD		MJD-OBS
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		0
+CAL_INSTMAG_MIN		0
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		1.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/format_split.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/format_split.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/format_split.config	(revision 22322)
@@ -0,0 +1,124 @@
+# Simulation test camera with single chip with single cell
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	SimScope
+	INSTRUME	STR	SIMMOSAIC
+	FORMAT		STR	SPLIT
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	CHIP		# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CELL		# There are no extensions
+	FPA.OBS		STR	FPANAME		# A PHU keyword for unique FPA identifier
+	CONTENT	        STR     CHIPNAME	# How to determine content of FITS file
+	CONTENT.RULE	STR	{CHIP.NAME}	# How to derive the CONTENT when writing
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# File identifier with corresponding file layout name
+	Chip00		STR	Chip00:SimChip
+	Chip01		STR	Chip01:SimChip
+	Chip10		STR	Chip10:SimChip
+	Chip11		STR	Chip11:SimChip
+END
+
+# Specify the layout
+CHIPS	METADATA
+	SimChip		METADATA
+		# Extension name, cellName:cellType
+		Cell00	STR	Cell00:SimCell
+		Cell01	STR	Cell01:SimCell
+		Cell10	STR	Cell10:SimCell
+		Cell11	STR	Cell11:SimCell
+	END
+END
+
+# Specify the cell data
+CELLS	METADATA
+	SimCell		METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.FILTER	STR	FILTER
+	FPA.FILTERID	STR	FILTER
+	FPA.POSANGLE	STR	POSANGLE
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.OBJECT	STR	OBJECT
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.XBIN	STR	XBIN
+	CELL.YBIN	STR	YBIN
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.DETECTOR	STR	SIMMOSAIC
+	FPA.TIMESYS	STR	UTC
+	FPA.RADECSYS	STR	ICRS
+	CELL.SATURATION	F32	65535
+	CELL.BAD	F32	0
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CELL.X0.DEPEND	STR	CELL.NAME
+	CELL.X0		METADATA
+		Cell00	S32	0
+		Cell01	S32	0
+		Cell10	S32	1024
+		Cell11	S32	1024
+	END
+	CELL.Y0.DEPEND	STR	CELL.NAME
+	CELL.Y0		METADATA
+		Cell00	S32	0
+		Cell01	S32	1024
+		Cell10	S32	0
+		Cell11	S32	1024
+	END
+	CHIP.X0.DEPEND	STR	CHIP.NAME
+	CHIP.X0		METADATA
+		Chip00	S32	0
+		Chip01	S32	0
+		Chip10	S32	2072
+		Chip11	S32	2072
+	END
+	CHIP.Y0.DEPEND	STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		Chip00	S32	0
+		Chip01	S32	2072
+		Chip10	S32	0
+		Chip11	S32	2072
+	END
+	CELL.XSIZE	S32	1000
+	CELL.YSIZE	S32	1000
+	CELL.XWINDOW	S32	0
+	CELL.YWINDOW	S32	0
+	CELL.GAIN	F32	1.0
+	CELL.READNOISE	F32	10.0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/format_together.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/format_together.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/format_together.config	(revision 22322)
@@ -0,0 +1,119 @@
+# Simulation test camera with single chip with single cell
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	SimScope
+	INSTRUME	STR	SIMMOSAIC
+	FORMAT		STR	TOGETHER
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA		# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CHIP		# There are no extensions
+	FPA.OBS		STR	FPANAME		# A PHU keyword for unique FPA identifier
+	CONTENT	        STR     CHIPNAME	# How to determine content of FITS file
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name ---> chip_name:chip_type
+	Chip00		STR	Chip00:SimChip
+	Chip01		STR	Chip01:SimChip
+	Chip10		STR	Chip10:SimChip
+	Chip11		STR	Chip11:SimChip
+END
+
+# Specify the layout
+CHIPS	METADATA
+	# Chip type ---> cell_name:cell_type
+	SimChip		STR	Cell00:SimCell Cell01:SimCell Cell10:SimCell Cell11:SimCell
+END
+
+
+# Specify the cell data
+CELLS	METADATA
+	SimCell		METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.FILTER	STR	FILTER
+	FPA.FILTERID	STR	FILTER
+	FPA.POSANGLE	STR	POSANGLE
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.OBJECT	STR	OBJECT
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.XBIN	STR	XBIN
+	CELL.YBIN	STR	YBIN
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.DETECTOR	STR	SIMMOSAIC
+	FPA.TIMESYS	STR	UTC
+	FPA.RADECSYS	STR	ICRS
+	CELL.SATURATION	F32	65535
+	CELL.BAD	F32	0
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CELL.X0.DEPEND	STR	CELL.NAME
+	CELL.X0		METADATA
+		Cell00	S32	0
+		Cell01	S32	0
+		Cell10	S32	1024
+		Cell11	S32	1024
+	END
+	CELL.Y0.DEPEND	STR	CELL.NAME
+	CELL.Y0		METADATA
+		Cell00	S32	0
+		Cell01	S32	1024
+		Cell10	S32	0
+		Cell11	S32	1024
+	END
+	CHIP.X0.DEPEND	STR	CHIP.NAME
+	CHIP.X0		METADATA
+		Chip00	S32	0
+		Chip01	S32	0
+		Chip10	S32	2072
+		Chip11	S32	2072
+	END
+	CHIP.Y0.DEPEND	STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		Chip00	S32	0
+		Chip01	S32	2072
+		Chip10	S32	0
+		Chip11	S32	2072
+	END
+	CELL.XSIZE	S32	1000
+	CELL.YSIZE	S32	1000
+	CELL.XWINDOW	S32	0
+	CELL.YWINDOW	S32	0
+	CELL.GAIN	F32	1.0
+	CELL.READNOISE	F32	10.0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/ppImage.config	(revision 22322)
@@ -0,0 +1,222 @@
+### ppImage recipe configuration file for simulated test camera
+
+# List of tasks to perform
+
+BASE.FITS       BOOL    FALSE           # Save base detrended image?
+CHIP.FITS       BOOL    TRUE            # Save chip-mosaicked image?
+BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+
+# binned output image options
+BIN1.XBIN               S32     8
+BIN1.YBIN               S32     8
+BIN2.XBIN               S32     64
+BIN2.YBIN               S32     64
+
+# Overscan subtraction
+OVERSCAN.SINGLE         BOOL    FALSE           # Reduce overscan to a single value?
+OVERSCAN.FIT            STR     POLYNOMIAL      # NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER          S32     7               # Order of polynomial fit
+OVERSCAN.STAT           STR     MEAN            # MEAN | MEDIAN
+
+# How to select the appropriate detrend image
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  MASK METADATA
+  END
+  DARK METADATA
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+  END
+  SHUTTER METADATA
+  END   
+END
+
+########################################################################################
+# Need the following in order to turn off overscan and shutter for the site-level recipe
+########################################################################################
+
+# Overscan subtraction only
+PPIMAGE_O         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Dark subtraction only
+PPIMAGE_D         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Shutter correction only
+PPIMAGE_S         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark
+PPIMAGE_OBD      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF     METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # 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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR    METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # 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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP   METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA   METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/psastro.config	(revision 22322)
@@ -0,0 +1,4 @@
+PSASTRO.CATDIR	STR	SYNTH.SIMTEST
+
+#PSASTRO.MOSAIC.GRADIENT.NX    S32     1   # number of x-dir cells per chip
+#PSASTRO.MOSAIC.GRADIENT.NY    S32     1   # number of y-dir cells per chip
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/psphot.config	(revision 22322)
@@ -0,0 +1,30 @@
+
+SAVE.BACKMDL	BOOL 	TRUE
+SAVE.BACKMDL.STDEV BOOL 	TRUE
+SAVE.BACKGND	BOOL 	TRUE
+SAVE.BACKSUB	BOOL 	TRUE
+SAVE.PLOTS      BOOL    FALSE
+SAVE.RESID	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+LOAD.PSF	BOOL 	FALSE
+
+IMSTATS_NPIX        S32  3000    	 # number of pixels to use for sky estimate boxes:
+
+#PSPHOT_TEST METADATA
+# SAVE.BACKSUB	BOOL 	FALSE
+# SAVE.RESID	BOOL 	TRUE
+#END
+#
+#PEAKS_OUTPUT_FILE   STR  peaks.dat
+#MOMENTS_OUTPUT_FILE STR  moments.dat
+
+#PSF_SN_LIM          F32  20.0            # minimum S/N for stars used for PSF model
+MOMENTS_SN_MIN      F32  20.0            # min S/N to measure moments
+FULL_FIT_SN_LIM     F32  100.0
+EXT_MIN_SN          F32  100.0           # fit galaxies above this S/N limit
+
+PEAKS_NSIGMA_LIMIT  F32  10.0 	   	 # peak significance threshold
+BREAK_POINT         STR  ENSEMBLE        # limit total processing for now (for speed)
+
+
+SKY_STAT            STR  ROBUST_MEDIAN   # statistic used to measure background
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simmosaic/rejections.config	(revision 22322)
@@ -0,0 +1,49 @@
+
+DARK METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.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  1.0
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0
+  EXP.STDEV          F32  0.0
+  EXP.MEANSTDEV      F32  0.5
+  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  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+
+SHUTTER 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
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simple/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simple/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simple/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simple/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simple/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simple/Makefile.am	(revision 22322)
@@ -0,0 +1,18 @@
+
+installdir = $(datadir)/ippconfig/simple
+
+install_files = \
+	camera.config \
+	format.config \
+	ppImage.config \
+	psastro.config \
+	psphot.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simple/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simple/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simple/camera.config	(revision 22322)
@@ -0,0 +1,67 @@
+# Camera configuration file for drop-dead boring camera, consisting of a single chip with single cell.
+
+# File formats that we know about
+FORMATS         METADATA
+	SIMPLE	STR	simple/format.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        Chip		STR	Cell
+END
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+	B	STR	B
+	V	STR	V
+	R	STR	R
+	I	STR	I
+	z	STR	z
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	FPA	STR	fpa
+END
+
+DVO.CAMERADIR	STR	megacam		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES		METADATA
+        PPIMAGE         STR     simple/ppImage.config   # Default: all (normal) options on
+	PSPHOT		STR	simple/psphot.config	# psphot details
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.NAME}	# Rule for generating photcode
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES       STR     recipes/filerules-simple.mdc
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR hdr
+  CMF.DATA STR psf
+  CMF.XSRC STR xsrc
+  CMF.XFIT STR xfit
+
+  PSF.HEAD  STR hdr
+  PSF.TABLE STR psf_model
+  PSF.RESID STR psf_resid
+END
+
+MASK.FORCE	BOOL	TRUE	# Force reliance on MASKS recipe if headers not present
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simple/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simple/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simple/format.config	(revision 22322)
@@ -0,0 +1,75 @@
+# Drop-dead simple camera with single chip with single cell
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+###	NAXIS		S32	2
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	NAXIS	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC		STR	[0:0,0:0]
+		# alternatives for getting these from the headers
+		# CELL.TRIMSEC.SOURCE	STR	HEADER
+		# CELL.BIASSEC.SOURCE	STR	VALUE
+		# CELL.TRIMSEC		STR	DATASEC
+		# CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	POSANGLE
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.OBJECT	STR	OBJECT
+	FPA.TIME	STR	DATE-OBS UTC-OBS	# Date and time
+	FPA.EXPOSURE	STR	EXPTIME
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+#	CELL.TIME	STR	DATE-OBS TIME-OBS	# Date and time
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	CELL.XBIN	S32	1
+	CELL.YBIN	S32	1
+	FPA.TIMESYS	STR	UTC
+	FPA.RADECSYS	STR	ICRS
+	CELL.GAIN	F32	1.0
+	CELL.READNOISE	F32	2.0
+	CELL.BAD	F32	0
+	CELL.SATURATION	F32	65535
+#	CELL.BAD	F32	-65535
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	SEPARATE,YEAR.FIRST
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simple/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simple/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simple/ppImage.config	(revision 22322)
@@ -0,0 +1,120 @@
+### ppImage recipe configuration file for drop-dead simple camera
+
+# List of tasks to perform
+SHUTTER         BOOL    FALSE           # Shutter correction
+DARK		BOOL	TRUE		# Dark correction
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+BIN1.FITS	BOOL    TRUE            # Save 1st binned chip image?
+BIN2.FITS	BOOL    TRUE		# Save 2nd binned chip image?
+
+# binned output image options
+BIN1.XBIN               S32     8
+BIN1.YBIN               S32     8
+BIN2.XBIN               S32     64
+BIN2.YBIN               S32     64
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	3		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# How to select the appropriate detrend image
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  MASK METADATA
+  END
+  DARK METADATA
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+  END
+  SHUTTER METADATA
+  END	
+END
+
+########################################################################################
+# Need the following in order to turn off overscan and shutter for the site-level recipe
+########################################################################################
+
+# Overscan subtraction only
+PPIMAGE_O         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Dark subtraction only
+PPIMAGE_D         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Shutter correction only
+PPIMAGE_S         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark
+PPIMAGE_OBD      METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS      METADATA
+  OVERSCAN        BOOL    FALSE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF     METADATA
+  OVERSCAN        BOOL    FALSE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE            # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR    METADATA
+  OVERSCAN        BOOL    FALSE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simple/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simple/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simple/psastro.config	(revision 22322)
@@ -0,0 +1,36 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+# PSASTRO.PLATE.SCALE    F32  13.5
+PSASTRO.PLATE.SCALE    F32  1.0
+
+# pmAstromGridMatch:
+PSASTRO.GRID.MIN.ANGLE F32 -0.0851
+PSASTRO.GRID.MAX.ANGLE F32 +0.0851
+PSASTRO.GRID.DEL.ANGLE F32  0.0170
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+# PSASTRO.GRID.OFFSET    F32  10000.
+# PSASTRO.GRID.SCALE     F32    500.
+PSASTRO.GRID.OFFSET    F32   1000.
+PSASTRO.GRID.SCALE     F32     50
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# pmAstromRadiusMatch
+# use plate-scale to make this in pixels?
+PSASTRO.MATCH.RADIUS   F32    8
+
+# pmAstromMatchFit
+PSASTRO.CHIP.NX        S32      1  # fit order
+PSASTRO.CHIP.NY        S32      1  # fit order
+PSASTRO.CHIP.NITER     S32      3  # fit clipping iterations
+PSASTRO.CHIP.NSIGMA    F32      3  # fit clipping sigmas
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simple/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simple/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simple/psphot.config	(revision 22322)
@@ -0,0 +1,17 @@
+
+SAVE.BACKMDL	BOOL 	FALSE
+SAVE.BACKGND	BOOL 	FALSE
+SAVE.BACKSUB	BOOL 	FALSE
+SAVE.PLOTS      BOOL    TRUE
+SAVE.RESID	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+LOAD.PSF	BOOL 	FALSE
+
+IMSTATS_NPIX        S32  3000    	 # number of pixels to use for sky estimate boxes:
+
+MOMENTS_SN_MIN      F32  20.0           # min S/N to measure moments
+FULL_FIT_SN_LIM     F32  100.0
+EXT_MIN_SN          F32  100.0           # fit galaxies above this S/N limit
+
+PEAKS_NSIGMA_LIMIT  F32  10.0 	   	 # peak significance threshold
+USE_FOOTPRINTS      BOOL  T       	  # use new pmFootprint peak packaging
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile.in
+Makefile
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/Makefile.am	(revision 22322)
@@ -0,0 +1,22 @@
+
+installdir = $(datadir)/ippconfig/simtest
+
+install_files = \
+	camera.config \
+	format.config \
+	ppImage.config \
+	psastro.config \
+	psphot.config \
+	rejections.config \
+	dvo.config \
+	ppSim.config \
+	ppSub.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/camera.config	(revision 22322)
@@ -0,0 +1,75 @@
+# Camera configuration file for simulation test
+
+# File formats that we know about
+FORMATS         METADATA
+	SIMPLE	STR	simtest/format.config
+END
+ 
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        Chip		STR	Cell
+END
+
+# valid filter names and corresponding IDs
+FILTER.ID       METADATA
+	NONE	STR	NONE
+	g	STR	g
+	r	STR	r
+	i	STR	i
+	z	STR	z
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	FPA	STR	fpa
+END
+
+DVO.CAMERADIR	STR	simtest		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES		METADATA
+        PPIMAGE         STR     simtest/ppImage.config   # Default: all (normal) options on
+	PSPHOT		STR	simtest/psphot.config	# psphot details
+	PPSIM		STR	simtest/ppSim.config	# ppSim details
+	PPSUB		STR	simtest/ppSub.config	# ppSub details
+	PSASTRO		STR	simtest/psastro.config	# psastro details
+	REJECTIONS	STR	simtest/rejections.config # Rejection for detrend creation
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES	STR	recipes/filerules-simple.mdc
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR {CHIP.NAME}.hdr
+  CMF.DATA STR {CHIP.NAME}.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD  STR	{CHIP.NAME}.hdr
+  PSF.TABLE STR {CHIP.NAME}.psf_model
+  PSF.RESID STR {CHIP.NAME}.psf_resid
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/dvo.config	(revision 22322)
@@ -0,0 +1,59 @@
+
+# keywords used by DVO to interpret the headers
+
+# keyword abstractions:
+DATE-KEYWORD		DATE-OBS
+UT-KEYWORD		UTC-OBS
+DATE-MODE		YYYY-MM-DD
+JD-KEYWORD		NONE
+MJD-KEYWORD		NONE
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		0
+CAL_INSTMAG_MIN		0
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		1.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/format.config	(revision 22322)
@@ -0,0 +1,79 @@
+# Simulation test camera with single chip with single cell
+
+# How to identify this type
+RULE	METADATA
+	INSTRUME	STR	SIMTEST
+	TELESCOP	STR	SimScope
+#	NAXIS		S32	2
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	OBJECT	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	TRIMSEC
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.AIRMASS	STR	AIRMASS
+        FPA.FILTERID    STR     FILTER
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	POSANGLE
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.OBJECT	STR	OBJECT
+	FPA.TIME	STR	DATE-OBS UTC-OBS	# Date and time
+	FPA.EXPOSURE	STR	EXPTIME
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	DATE-OBS UTC-OBS	# Date and time
+	CELL.XBIN	STR	XBIN
+	CELL.YBIN	STR	YBIN
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TIMESYS	STR	UTC
+	FPA.RADECSYS	STR	ICRS
+	FPA.DETECTOR	STR	SIMTEST
+	CELL.SATURATION	F32	65535
+	CELL.BAD	F32	-100 ## XXX this value is bogus, but needs to be context based so residual images do not get half-masked
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+	CELL.XSIZE	S32	2048
+	CELL.YSIZE	S32	2048
+	CELL.XWINDOW	S32	0
+	CELL.YWINDOW	S32	0
+	CELL.GAIN	F32	1.0
+	CELL.READNOISE	F32	10.0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	SEPARATE,YEAR.FIRST
+	CELL.TIME	STR	SEPARATE,YEAR.FIRST
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/ppImage.config	(revision 22322)
@@ -0,0 +1,252 @@
+### ppImage recipe configuration file for simulated test camera
+
+# List of tasks to perform
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+BIN1.FITS	BOOL    TRUE            # Save 1st binned chip image?
+BIN2.FITS	BOOL    TRUE		# Save 2nd binned chip image?
+
+# binned output image options
+BIN1.XBIN               S32     8
+BIN1.YBIN               S32     8
+BIN2.XBIN               S32     64
+BIN2.YBIN               S32     64
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	7		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# How to select the appropriate detrend image
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  MASK METADATA
+  END
+  DARK METADATA
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+  END
+  SHUTTER METADATA
+  END	
+END
+
+
+########################################################################################
+# Need the following in order to turn off overscan and shutter for the site-level recipe
+########################################################################################
+
+# Overscan subtraction only
+PPIMAGE_O         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Dark subtraction only
+PPIMAGE_D         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Shutter correction only
+PPIMAGE_S         METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark
+PPIMAGE_OBD      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF     METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # 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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR    METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # 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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP   METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE           # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA   METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    TRUE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    TRUE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+  CHIP.MASK.FITS  BOOL    TRUE            # Save mask
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_FLATCORR    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+
+  DETREND.CONSTRAINTS  METADATA
+    FLAT METADATA
+      DETTYPE STR FLAT_RAW
+    END
+  END   
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/ppSim.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/ppSim.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/ppSim.config	(revision 22322)
@@ -0,0 +1,2 @@
+### Recipe values for ppSim, the Pan-STARRS data simulator
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/ppSub.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/ppSub.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/ppSub.config	(revision 22322)
@@ -0,0 +1,27 @@
+### Recipe file for ppSub
+
+KERNEL.TYPE	STR	RINGS		# Kernel type to use (POIS|ISIS|SPAM|FRIES|GUNK|RINGS)
+KERNEL.SIZE     S32	15		# Kernel half-size (pixels)
+SPATIAL.ORDER   S32	1 		# Spatial polynomial order
+REGION.SIZE	F32	0		# Iso-kernel region size (pixels)
+STAMP.SPACING   F32	200		# Typical spacing between stamps (pixels)
+STAMP.FOOTPRINT S32	35		# Size of stamps (pixels)
+STAMP.THRESHOLD F32	0		# Flux threshold for stamps (ADU)
+ITER            S32	2		# Number of rejection iterations
+REJ             F32	2.		# Rejection level (std dev)
+MASK.IN		STR	MASK.VALUE	# 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
+POOR.FRACTION	F32	0.1		# Maximum fraction of bad weight for poor pixels
+BADFRAC		F32	0.8		# Maximum fraction of bad pixels
+@ISIS.WIDTHS	F32	1 3 5		# Gaussian FWHMs for ISIS kernels
+@ISIS.ORDERS	S32	2 2 2		# Polynomial orders for ISIS kernels
+INNER		S32	5		# Inner half-size for SPAM and FRIES kernels
+RINGS.ORDER	S32	2		# Polynomial order for RINGS kernels
+PENALTY		F32	1.0		# Penalty for wideness
+
+### Modifications to use when stacking data
+STACK	METADATA
+	KERNEL.TYPE	STR	RINGS	# Kernel type to use (POIS|ISIS|SPAM|FRIES|GUNK|RINGS)
+	KERNEL.SIZE     S32     15      # Kernel half-size (pixels)
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/psastro.config	(revision 22322)
@@ -0,0 +1,3 @@
+PSASTRO.CATDIR		STR	SYNTH.SIMTEST
+DVO.GETSTAR.PHOTCODE	STR	r
+PSASTRO.MAX.NSTAR	S32	50	# max stars accepted for fitting
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/psphot.config	(revision 22322)
@@ -0,0 +1,30 @@
+
+SAVE.BACKMDL	BOOL 	TRUE
+SAVE.BACKGND	BOOL 	TRUE
+SAVE.BACKSUB	BOOL 	TRUE
+SAVE.PLOTS      BOOL    TRUE
+SAVE.RESID	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+LOAD.PSF	BOOL 	FALSE
+
+IMSTATS_NPIX        S32  3000    	 # number of pixels to use for sky estimate boxes:
+
+#PSPHOT_TEST METADATA
+# SAVE.BACKSUB	BOOL 	FALSE
+# SAVE.RESID	BOOL 	TRUE
+#END
+#
+#PEAKS_OUTPUT_FILE   STR  peaks.dat
+#MOMENTS_OUTPUT_FILE STR  moments.dat
+
+#PSF_SN_LIM          F32  20.0            # minimum S/N for stars used for PSF model
+MOMENTS_SN_MIN      F32  10.0            # min S/N to measure moments
+FULL_FIT_SN_LIM     F32  100.0
+EXT_MIN_SN          F32  100.0           # fit galaxies above this S/N limit
+
+PEAKS_NSIGMA_LIMIT   F32  25.0 	 	 # peak significance threshold
+PEAKS_NSIGMA_LIMIT_2 F32  10.0 	   	 # peak significance threshold
+BREAK_POINT         STR  NONE            # limit total processing for now (for speed)
+
+
+SKY_STAT            STR  ROBUST_MEDIAN   # statistic used to measure background
Index: /tags/sj_tags/sj_root_20080929/ippconfig/simtest/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/simtest/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/simtest/rejections.config	(revision 22322)
@@ -0,0 +1,78 @@
+
+BIAS METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0 # 2 sigma from expected mean
+  IMFILE.STDEV       F32 20.0 # per-image sigma of < 20.0 (supplied read-noise is 10.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.5 # binned stdev < 0.5 (binning is 64x64 : 10.0 / 64.0 = 0.15)
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0 
+  EXP.STDEV          F32 20.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  5.0
+  ENSEMBLE.STDEV     F32  5.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+DARK METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.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  1.0
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0
+  EXP.STDEV          F32  0.0
+  EXP.MEANSTDEV      F32  0.5
+  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  5.0
+  ENSEMBLE.STDEV     F32  5.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+
+SHUTTER 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
+
+
+ITERATION	BOOL	FALSE		# Are iterations permitted?
+MASTER		BOOL	TRUE		# Force stacks to be accepted as a master?
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/site.config.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/site.config.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/site.config.in	(revision 22322)
@@ -0,0 +1,63 @@
+## Site Configuration : use this as an example for your installation
+## Supply locations of interest
+
+# place your data directories here and refer to as path://PATH/remainder
+DATAPATH        METADATA
+        ALALA   STR     /data/alala.0/
+        ISP     STR     /data/ipp000.0/isp
+        ISP_V2  STR     /data/ipp000.0/isp_v2
+        ISP_V3  STR     /data/ipp000.0/isp_v3
+        ISP_TEST STR    /data/ipp000.0/isp_test
+        CFH12K  STR     /data/ipp003.0/eugene/cfh12k
+
+        SRWORK  STR     /data/ipp003.0/rodney/WORK
+        SRDATA  STR     /data/ipp003.0/rodney/DATA
+
+        SRESSWORK  STR     /data/ipp003.0/rodney/WORK
+        SRESSDATA  STR     /data/ipp003.0/rodney/DATA
+
+        SIMDATA STR     /data/ipp002.0/SIM/DATA
+        SIMWORK STR     /data/ipp002.0/SIM/WORK
+
+        MOPS    STR     /data/ipp002.0/MOPS/
+        TALCS   STR     /data/ipp003.0/TALCS/
+        GPC1    STR     /data/ipp002.0/koppenh/
+END
+
+# List of tessellations, and their DVO CATDIR
+TESSELLATIONS   METADATA
+        MOPS            STR     /data/ipp002.0/MOPS/skycells/
+        FIXNS           STR     /data/ipp002.0/FIXNS/skycells/
+END
+
+# dvo databases used for output
+DVO.CATDIRS     METADATA
+        MOPS            STR     /data/ipp002.0/MOPS/catdir/
+        FIXNS           STR     /data/ipp002.0/FIXNS/catdir/
+END
+
+# dvo databases used for psastro reference
+PSASTRO.CATDIRS METADATA
+        2MASS           STR     /data/alala.0/ipp/ippRefs/catdir.2mass
+        SYNTH.GRIZY     STR     /data/alala.0/ipp/ippRefs/catdir.synth.grizy
+        SYNTH.BRIGHT    STR     /data/alala.0/ipp/ippRefs/catdir.synth.bright
+        SYNTH.SIMTEST   STR     /data/alala.0/ipp/ippRefs/catdir.synth.simtest
+END
+
+# nebulous server
+NEB_SERVER      STR     http://alala:80/nebulous        # Nebulous server
+
+# Database configuration
+DBSERVER        STR     XXX                     # Database host name (for psDBInit)
+DBNAME          STR     XXX                     # Database name (for psDBInit)
+DBUSER          STR     XXX                     # Database user name (for psDBInit)
+DBPASSWORD      STR     XXX                     # Database password (for psDBInit)
+DBPORT          S32     0                       # Database port (for psDBInit); 0 = default
+
+# other basic values:
+# XXX is TIME still needed / used?
+# XXX use autoconf to put this in a known location?
+# TIME          STR     psTime.config   # Time configuration file
+
+PSTAMP_DATA_STORE_ROOT STR /data/ipp000.0/datastore/dsroot # Data store root directory
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/skycell/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/skycell/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/skycell/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/skycell/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/skycell/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/skycell/Makefile.am	(revision 22322)
@@ -0,0 +1,14 @@
+
+installdir = $(datadir)/ippconfig/skycell
+
+install_files = \
+	dvo.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/skycell/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/skycell/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/skycell/dvo.config	(revision 22322)
@@ -0,0 +1,15 @@
+# DVO configuration for skycells of all cameras
+# Contains keywords used by DVO to interpret the headers
+
+# Date keyword abstractions:
+DATE-KEYWORD		NONE
+UT-KEYWORD		NONE
+DATE-MODE		YYYY-MM-DD
+JD-KEYWORD		NONE
+MJD-KEYWORD		MJD-OBS
+
+# Other important keyword abstractions:
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		NONE
+ST-KEYWORD		NONE
Index: /tags/sj_tags/sj_root_20080929/ippconfig/system.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/system.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/system.config	(revision 22322)
@@ -0,0 +1,52 @@
+## system-wide options : these are concepts not specific to any camera or recipe
+
+### Setups for each camera system
+CAMERAS		METADATA
+	MEGACAM			STR	megacam/camera.config		# CFHT MegaCam
+ 	CFH12K			STR	cfh12k/camera.config		# CFHT 12K
+ 	ISP-Apogee     		STR	isp/camera.config		# Pan-STARRS Imaging sky probe
+ 	MOSAIC2 		STR	mosaic2/camera.config	        # CTIO MOSAIC2 camera, for ESSENCE
+ 	SDSSmosaic		STR	sdssmosaic/camera.config	# SDSS, mosaic version
+ 	SDSS			STR	sdss/camera.config		# Sloan Digital Sky Survey
+ 	GPC1			STR	gpc1/camera.config		# Pan-STARRS GPC1
+ 	ESOWFI			STR	esowfi/camera.config		# ESO Wide-Field Imager
+ 	LBCRED                  STR     lbc_red/camera.config           # Large Binocular Camera Red
+ 	LULIN			STR	lulin/camera.config		# Lulin Optical Telescope 1m
+ 	UH8K			STR	uh8k/camera.config		# WFI/8k on UH88
+ 	VYSOS-5			STR	vysos5/camera.config		# VYSOS 5in
+ 	SIMMOSAIC		STR	simmosaic/camera.config		# Simulated mosaic, for testing
+ 	SIMTEST			STR	simtest/camera.config		# Simulation test camera
+ 	SIMPLE			STR	simple/camera.config		# If all else fails....
+END
+
+## directories exist for other camera, introduce if they are tested and working
+#	UCAM			STR	ucam/camera.config
+#	LRIS_BLUE		STR	lris_blue/camera.config
+#	TC3			STR	tc3/camera.config		# Pan-STARRS Test Camera III
+#	LRIS_RED		STR	lris_red/camera.config
+
+# Header keywords for skycell concepts; required because DVO doesn't read HIERARCH
+SKYCELLS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	CELL.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	CELL.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+END
+
+RECIPES		METADATA		# Site-level recipes
+	MASKS		STR		recipes/masks.config	# Mask values
+	REJECTIONS	STR		recipes/rejections.config # Rejection for detrend creation
+	PPIMAGE		STR		recipes/ppImage.config  # Image reduction
+	PPMERGE		STR		recipes/ppMerge.config	# Image combination
+ 	PPSTATS		STR		recipes/ppStats.config	# Image statistics
+	PSPHOT		STR     	recipes/psphot.config	# Photometry
+	PSASTRO		STR		recipes/psastro.config	# Astrometry
+	PPSTACK		STR		recipes/ppStack.config	# Combination
+	PSWARP		STR		recipes/pswarp.config   # Warping
+	PPSIM		STR		recipes/ppSim.config	# Simulations
+	PPSUB		STR		recipes/ppSub.config	# Subtraction
+	ADDSTAR         STR             recipes/addstar.config  # addstar to dvodb
+	JPEG		STR		recipes/jpeg.mdc	# JPEG creation
+ 	PPSTATS_METADATA STR		recipes/ppStatsFromMetadata.config # Image statistics
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tc3/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tc3/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tc3/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tc3/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tc3/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tc3/Makefile.am	(revision 22322)
@@ -0,0 +1,17 @@
+
+installdir = $(datadir)/ippconfig/tc3
+
+install_files = \
+	dvo.config \
+	camera.config \
+	format.config \
+	ppImage.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tc3/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tc3/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tc3/camera.config	(revision 22322)
@@ -0,0 +1,76 @@
+# Camera configuration file for Cfh12k: describes the camera
+
+# File formats that we know about
+FORMATS		METADATA
+	RAW	STR	tc3/format.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA	METADATA
+	CCID58-1-06b2	STR	xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+	CCID45-1-14A 	STR	xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+	CCID45-1-11A 	STR	xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+	CCID45-1-22A 	STR	xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+	CCID45-1-04C 	STR	xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+	CCID45-1-13A 	STR	xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+	CCID45-1-05A 	STR	xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+	CCID45-1-19A 	STR	xy00 xy01 xy02 xy03 xy04 xy05 xy06 xy07 xy10 xy11 xy12 xy13 xy14 xy15 xy16 xy17 xy20 xy21 xy22 xy23 xy24 xy25 xy26 xy27 xy30 xy31 xy32 xy33 xy34 xy35 xy36 xy37 xy40 xy41 xy42 xy43 xy44 xy45 xy46 xy47 xy50 xy51 xy52 xy53 xy54 xy55 xy56 xy57 xy60 xy61 xy62 xy63 xy64 xy65 xy66 xy67 xy70 xy71 xy72 xy73 xy74 xy75 xy76 xy77
+END
+
+# ccd00
+# ccd01
+# ccd10
+# ccd11
+# ccd20
+# ccd21
+# ccd30
+# ccd31
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+	NONE	STR	NONE
+	B	STR	B
+	V	STR	V
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	FPA	STR	fpa
+	CHIP	STR	{CHIP.NAME}
+	CELL	STR	{CHIP.NAME}:{CELL.NAME}
+	fpa	STR	fpa
+	chip	STR	{CHIP.NAME}
+	cell	STR	{CHIP.NAME}:{CELL.NAME}
+END
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES		METADATA
+	# Other recipes
+#        PSPHOT          STR     tc3/psphot.config           # psphot details
+#        PSASTRO         STR     tc3/psastro.config          # psastro details
+#	PPSTATS		STR	tc3/ppStats.config		# ppStats recipe
+	PPIMAGE         STR     tc3/ppImage.config		# ppImage recipe
+END
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES       STR     recipes/filerules-split.mdc
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBS		STR	EXPNUM
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tc3/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tc3/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tc3/dvo.config	(revision 22322)
@@ -0,0 +1,63 @@
+
+# location of DVO database tables
+CATDIR			/data/alala.0/eugene/isp/catdir
+
+# keywords used by DVO to interpret the headers
+
+# keyword abstractions:
+DATE-KEYWORD		DATE-OBS
+DATE-MODE		yyyy-mm-dd
+UT-KEYWORD		UTC-OBS
+JD-KEYWORD		NONE
+MJD-KEYWORD		NONE
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		0
+CAL_INSTMAG_MIN		0
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		1.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tc3/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tc3/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tc3/format.config	(revision 22322)
@@ -0,0 +1,361 @@
+# Camera format configuration for Pan-STARRS Test Camera 3
+
+# How to recognise this type
+RULE	METADATA
+#        IMAGESWV        STR     GPC DaqCom v2.05 (Mar 08 2007)
+	ORIGIN		STR	PS1
+	CONTROLR	STR	STARGRASP
+	EXTEND		BOOL	T
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	CHIP	# The FITS file represents a chip
+	EXTENSIONS	STR	CELL	# The extensions represent cells
+	FPA.OBS	STR	OBSTYPE	# A PHU keyword for unique identifier
+	CONTENT	        STR     DETECTOR # How to determine content of FITS file
+	CONTENT.RULE	STR	{CHIP.NAME}	# How to derive the CONTENT when writing
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# File identifier with corresponding file layout name
+	CCID58-1-06b2	STR	CCID58-1-06b2:CCD_10u
+	CCID45-1-14A	STR	CCID45-1-14A:CCD_12u
+	CCID45-1-11A	STR	CCID45-1-11A:CCD_12u
+	CCID45-1-22A	STR	CCID45-1-22A:CCD_12u
+	CCID45-1-04C	STR	CCID45-1-04C:CCD_10u
+	CCID45-1-13A	STR	CCID45-1-13A:CCD_12u
+	CCID45-1-05A	STR	CCID45-1-05A:CCD_12u
+	CCID45-1-19A	STR	CCID45-1-19A:CCD_12u
+END
+
+# Specify the layout
+CHIPS	METADATA
+	CCD_10u		METADATA
+		# Extension name, cellName:cellType
+		xy00	STR	xy00:pitch10u
+		xy01	STR	xy01:pitch10u
+		xy02	STR	xy02:pitch10u
+		xy03	STR	xy03:pitch10u
+		xy04	STR	xy04:pitch10u
+		xy05	STR	xy05:pitch10u
+		xy06	STR	xy06:pitch10u
+		xy07	STR	xy07:pitch10u
+		xy10	STR	xy10:pitch10u
+		xy11	STR	xy11:pitch10u
+		xy12	STR	xy12:pitch10u
+		xy13	STR	xy13:pitch10u
+		xy14	STR	xy14:pitch10u
+		xy15	STR	xy15:pitch10u
+		xy16	STR	xy16:pitch10u
+		xy17	STR	xy17:pitch10u
+		xy20	STR	xy20:pitch10u
+		xy21	STR	xy21:pitch10u
+		xy22	STR	xy22:pitch10u
+		xy23	STR	xy23:pitch10u
+		xy24	STR	xy24:pitch10u
+		xy25	STR	xy25:pitch10u
+		xy26	STR	xy26:pitch10u
+		xy27	STR	xy27:pitch10u
+		xy30	STR	xy30:pitch10u
+		xy31	STR	xy31:pitch10u
+		xy32	STR	xy32:pitch10u
+		xy33	STR	xy33:pitch10u
+		xy34	STR	xy34:pitch10u
+		xy35	STR	xy35:pitch10u
+		xy36	STR	xy36:pitch10u
+		xy37	STR	xy37:pitch10u
+		xy40	STR	xy40:pitch10u
+		xy41	STR	xy41:pitch10u
+		xy42	STR	xy42:pitch10u
+		xy43	STR	xy43:pitch10u
+		xy44	STR	xy44:pitch10u
+		xy45	STR	xy45:pitch10u
+		xy46	STR	xy46:pitch10u
+		xy47	STR	xy47:pitch10u
+		xy50	STR	xy50:pitch10u
+		xy51	STR	xy51:pitch10u
+		xy52	STR	xy52:pitch10u
+		xy53	STR	xy53:pitch10u
+		xy54	STR	xy54:pitch10u
+		xy55	STR	xy55:pitch10u
+		xy56	STR	xy56:pitch10u
+		xy57	STR	xy57:pitch10u
+		xy60	STR	xy60:pitch10u
+		xy61	STR	xy61:pitch10u
+		xy62	STR	xy62:pitch10u
+		xy63	STR	xy63:pitch10u
+		xy64	STR	xy64:pitch10u
+		xy65	STR	xy65:pitch10u
+		xy66	STR	xy66:pitch10u
+		xy67	STR	xy67:pitch10u
+		xy70	STR	xy70:pitch10u
+		xy71	STR	xy71:pitch10u
+		xy72	STR	xy72:pitch10u
+		xy73	STR	xy73:pitch10u
+		xy74	STR	xy74:pitch10u
+		xy75	STR	xy75:pitch10u
+		xy76	STR	xy76:pitch10u
+		xy77	STR	xy77:pitch10u
+	END
+	CCD_12u		METADATA
+		# Extension name, cellName:cellType
+		xy00	STR	xy00:pitch12u
+		xy01	STR	xy01:pitch12u
+		xy02	STR	xy02:pitch12u
+		xy03	STR	xy03:pitch12u
+		xy04	STR	xy04:pitch12u
+		xy05	STR	xy05:pitch12u
+		xy06	STR	xy06:pitch12u
+		xy07	STR	xy07:pitch12u
+		xy10	STR	xy10:pitch12u
+		xy11	STR	xy11:pitch12u
+		xy12	STR	xy12:pitch12u
+		xy13	STR	xy13:pitch12u
+		xy14	STR	xy14:pitch12u
+		xy15	STR	xy15:pitch12u
+		xy16	STR	xy16:pitch12u
+		xy17	STR	xy17:pitch12u
+		xy20	STR	xy20:pitch12u
+		xy21	STR	xy21:pitch12u
+		xy22	STR	xy22:pitch12u
+		xy23	STR	xy23:pitch12u
+		xy24	STR	xy24:pitch12u
+		xy25	STR	xy25:pitch12u
+		xy26	STR	xy26:pitch12u
+		xy27	STR	xy27:pitch12u
+		xy30	STR	xy30:pitch12u
+		xy31	STR	xy31:pitch12u
+		xy32	STR	xy32:pitch12u
+		xy33	STR	xy33:pitch12u
+		xy34	STR	xy34:pitch12u
+		xy35	STR	xy35:pitch12u
+		xy36	STR	xy36:pitch12u
+		xy37	STR	xy37:pitch12u
+		xy40	STR	xy40:pitch12u
+		xy41	STR	xy41:pitch12u
+		xy42	STR	xy42:pitch12u
+		xy43	STR	xy43:pitch12u
+		xy44	STR	xy44:pitch12u
+		xy45	STR	xy45:pitch12u
+		xy46	STR	xy46:pitch12u
+		xy47	STR	xy47:pitch12u
+		xy50	STR	xy50:pitch12u
+		xy51	STR	xy51:pitch12u
+		xy52	STR	xy52:pitch12u
+		xy53	STR	xy53:pitch12u
+		xy54	STR	xy54:pitch12u
+		xy55	STR	xy55:pitch12u
+		xy56	STR	xy56:pitch12u
+		xy57	STR	xy57:pitch12u
+		xy60	STR	xy60:pitch12u
+		xy61	STR	xy61:pitch12u
+		xy62	STR	xy62:pitch12u
+		xy63	STR	xy63:pitch12u
+		xy64	STR	xy64:pitch12u
+		xy65	STR	xy65:pitch12u
+		xy66	STR	xy66:pitch12u
+		xy67	STR	xy67:pitch12u
+		xy70	STR	xy70:pitch12u
+		xy71	STR	xy71:pitch12u
+		xy72	STR	xy72:pitch12u
+		xy73	STR	xy73:pitch12u
+		xy74	STR	xy74:pitch12u
+		xy75	STR	xy75:pitch12u
+		xy76	STR	xy76:pitch12u
+		xy77	STR	xy77:pitch12u
+	END
+END		
+
+# Specify the cell data
+CELLS	METADATA
+	pitch10u	METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XSIZE		S32	590
+		CELL.YSIZE		S32	598
+	END
+
+	pitch12u	METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XSIZE		S32	477
+		CELL.YSIZE		S32	496
+	END
+	pitch10u	METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[575:606,1:594]
+		CELL.TRIMSEC		STR	[1:574,1:594]
+	#	CELL.BIASSEC		STR	BIASSEC
+	#	CELL.TRIMSEC		STR	DATASEC
+	END
+
+	# This is just in here for fun
+	pitch12u	METADATA
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[1:10,1:512];[523:574,1:512]
+		CELL.TRIMSEC		STR	[11:522,1:512]
+	#	CELL.BIASSEC		STR	BIASSEC
+	#	CELL.TRIMSEC		STR	TRIMSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.DETECTOR	STR	DETECTOR
+        FPA.AIRMASS     STR     AIRMASS
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+#	FPA.OBJECT	STR	OBJECT		# Not always available
+#        FPA.FILTERID    STR     FILTER		# Not available from OTIS
+#        FPA.FILTER      STR     FILTER		# Not available from OTIS
+        FPA.POSANGLE    STR     ROTANGLE
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+        FPA.RADECSYS    STR     RADECSYS
+	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	MJD-OBS
+	FPA.ALT		STR	TELALT
+	FPA.AZ		STR	TELAZ
+	FPA.TEMP	STR	DETTEM
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	DETTEM
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+	CELL.TIME	STR	MJD-OBS
+        CELL.SATURATION STR     SATURATE
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+#	CELL.BAD	STR	BADLEVEL
+
+#### How to get the cell coordiantes from readout coordinates
+#	READOUT.X0		STR	CNPIX1
+#	READOUT.Y0		STR	CNPIX2
+
+### How to get the chip coordinates from cell coordinates
+	CELL.X0		STR	IMNPIX1
+	CELL.Y0		STR	IMNPIX2
+	CELL.XPARITY	STR	IMTM1_1
+	CELL.YPARITY	STR	IMTM2_2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+	FPA.TIMESYS		STR	UTC
+	CELL.TIMESYS		STR	UTC
+	FPA.FILTERID		STR	NONE
+	FPA.FILTER		STR	NONE
+	CELL.GAIN		F32	1.0
+	CELL.READNOISE          F32     10.0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.BAD                S32     0
+
+	CHIP.XSIZE.DEPEND	STR	CHIP.NAME
+	CHIP.XSIZE	METADATA
+		CCID58-1-06b2	S32	4837		# 10 micron pitch
+		CCID45-1-14A	S32	4033
+		CCID45-1-11A	S32	4033
+		CCID45-1-22A	S32	4033
+		CCID45-1-04C	S32	4837		# 10 micron pitch
+		CCID45-1-13A	S32	4033
+		CCID45-1-05A	S32	4033    
+		CCID45-1-19A	S32	4033 
+	END
+
+	CHIP.YSIZE.DEPEND	STR	CHIP.NAME
+	CHIP.YSIZE	METADATA
+		CCID58-1-06b2	S32	4829		# 10 micron pitch
+		CCID45-1-14A	S32	4031
+		CCID45-1-11A	S32	4031
+		CCID45-1-22A	S32	4031
+		CCID45-1-04C	S32	4829		# 10 micron pitch
+		CCID45-1-13A	S32	4031
+		CCID45-1-05A	S32	4031    
+		CCID45-1-19A	S32	4031 
+	END
+
+#		CCID58-1-06b2	0,0
+#		CCID45-1-14A	0,1
+#		CCID45-1-11A	1,0
+#		CCID45-1-22A	1,1
+#		CCID45-1-04C	2,0
+#		CCID45-1-13A	2,1
+#		CCID45-1-05A	3,0
+#		CCID45-1-19A	3,1
+
+	### How to get FPA coordinates from chip coordinates
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		CCID58-1-06b2	S32	0
+		CCID45-1-14A	S32	0
+		CCID45-1-11A	S32	4900
+		CCID45-1-22A	S32	4900
+		CCID45-1-04C	S32	9800
+		CCID45-1-13A	S32	9800
+		CCID45-1-05A	S32	14700
+		CCID45-1-19A	S32	14700 
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		CCID58-1-06b2	S32	0
+		CCID45-1-14A	S32	4900
+		CCID45-1-11A	S32	0
+		CCID45-1-22A	S32	4900
+		CCID45-1-04C	S32	0
+		CCID45-1-13A	S32	4900
+		CCID45-1-05A	S32	0
+		CCID45-1-19A	S32	4900
+	END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+		CCID58-1-06b2	S32	+1
+		CCID45-1-14A	S32	+1
+		CCID45-1-11A	S32	+1
+		CCID45-1-22A	S32	+1
+		CCID45-1-04C	S32	+1
+		CCID45-1-13A	S32	+1
+		CCID45-1-05A	S32	+1
+		CCID45-1-19A	S32	+1
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		CCID58-1-06b2	S32	+1
+		CCID45-1-14A	S32	+1
+		CCID45-1-11A	S32	+1
+		CCID45-1-22A	S32	+1
+		CCID45-1-04C	S32	+1
+		CCID45-1-13A	S32	+1
+		CCID45-1-05A	S32	+1
+		CCID45-1-19A	S32	+1
+	END
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+#	CELL.GAIN	dbEntry		Camera		gain		chipId,cellId	CHIP.NAME,CELL.NAME
+#	CELL.READNOISE	dbEntry		Camera		readNoise	chipId,cellId	CHIP.NAME,CELL.NAME
+
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END		
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tc3/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tc3/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tc3/ppImage.config	(revision 22322)
@@ -0,0 +1,300 @@
+### ppImage recipe configuration file for Megacam
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+OVERSCAN.FIT		STR	NONE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	0		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+OVERSCAN	BOOL	TRUE		# Overscan subtraction
+BIAS		BOOL	TRUE		# Bias subtraction
+DARK		BOOL	TRUE		# Dark subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+SHUTTER		BOOL	FALSE		# Shutter correction
+FRINGE		BOOL	FALSE		# Fringe subtraction
+
+# this table lists extra constraints which should be applied when
+# selecting the detrend images.
+# note: camera and time are always applied
+
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+  END
+  FLAT_CORRECTION METADATA
+    FILTER   STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+  END
+  SHUTTER METADATA
+  END	
+END
+
+# only apply the fringe for these filters
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS	STR r
+FRINGE.FILTERS	STR i
+FRINGE.FILTERS	STR z
+
+
+################################################################################
+# Diffferent processing options, which may be loaded symbolically
+################################################################################
+
+# No operation except potential normalisation
+PPIMAGE_N         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan subtraction only
+PPIMAGE_O         METADATA
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Bias subtraction only
+PPIMAGE_B         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Dark subtraction only
+PPIMAGE_D         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Shutter correction only
+PPIMAGE_S         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Flat-fielding only
+PPIMAGE_F         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    TRUE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Fringe correction only
+PPIMAGE_R         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Photometry only
+PPIMAGE_P         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+END
+
+# Photometry & Astrometry
+PPIMAGE_A         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Source identification and photometry
+END
+
+# Overscan, bias
+PPIMAGE_OB        METADATA
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark
+PPIMAGE_OBD      METADATA
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS      METADATA
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF     METADATA
+  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
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR    METADATA
+  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
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP   METADATA
+  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
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    FALSE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA   METADATA
+  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
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    FALSE		  # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE		  # Astrometry for mosaic?
+END
+
+# Save JPEG from BIN1
+PPIMAGE_J1        METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    TRUE		  # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    FALSE		  # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+END
+
+# Save JPEG from BIN2
+PPIMAGE_J2        METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BASE.FITS       BOOL    FALSE           # Save base image?
+  BIN1.FITS       BOOL    FALSE           # Save 1st binned chip image?
+  BIN2.FITS       BOOL    FALSE           # Save 2nd binned chip image?
+  BIN1.JPEG       BOOL    FALSE		  # Save 1st binned jpeg?
+  BIN2.JPEG       BOOL    TRUE		  # Save 2nd binned jpeg?
+  BIN1.XBIN       S32     1               # Image is already binned
+  BIN1.YBIN       S32     1               # Image is already binned
+  BIN2.XBIN       S32     1               # Image is already binned
+  BIN2.YBIN       S32     1               # Image is already binned
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/camera.config	(revision 22322)
@@ -0,0 +1,68 @@
+# Camera configuration file for Tek
+
+# File formats that we know about
+FORMATS         METADATA
+        SIMPLE  STR     tek/format.config
+        CMP     STR     tek/cmp.config
+        CMF     STR     tek/cmf.config
+END
+ 
+ 
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        Chip            STR     Cell
+END
+
+# Recipe options
+RECIPES         METADATA
+        PSPHOT          STR     tek/psphot.config               # psphot details
+        PSASTRO         STR     tek/psastro.config              # psastro details
+        PPIMAGE         STR     tek/ppImage.config              # Recipes for ppImage
+        PPMERGE         STR     tek/ppMerge.config              # Recipes for ppMerge
+	REJECTIONS	STR     tek/rejections.config
+END
+
+FILTER.ID       METADATA
+        g       STR     g
+        r       STR     r
+        i       STR     i
+        z       STR     z
+        y       STR     y
+	R	STR	R
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	FPA	STR	fpa
+END
+
+DVO.CAMERADIR	STR	tek		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES       STR     recipes/filerules-simple.mdc
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR cmf.hdr
+  CMF.DATA STR cmf.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD  STR	hdr
+  PSF.TABLE STR psf_model
+  PSF.RESID STR psf_resid
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}		# Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/cmf.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/cmf.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/cmf.config	(revision 22322)
@@ -0,0 +1,82 @@
+# Tek
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+	NAXIS		S32	0
+	TELESCOP	STR	2.2m-UH
+	INSTRUME	STR	TEK
+	DETECTOR	STR   	Tektronix-2048^2-CCD
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	NAXIS	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	[21:2068,1:2048]		 			CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+	FPA.FILTER	STR	FILTER
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.TIME	STR	MJD-OBS
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.GAIN	STR	GAIN
+	CELL.XBIN	STR	CCDBIN1
+	CELL.YBIN	STR	CCDBIN2
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	CELL.READNOISE	S32	10
+	CELL.BAD	S32	0
+	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	65535
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/cmp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/cmp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/cmp.config	(revision 22322)
@@ -0,0 +1,82 @@
+# Pan-STARRS Imaging Sky Probe
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	FALSE
+	NAXIS		S32	0
+	TELESCOP	STR	2.2m-UH
+	INSTRUME	STR	TEK
+	DETECTOR	STR	Tektronix-2048^2-CCD
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	NAXIS	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	[21:2068,1:2048]		 			CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+	FPA.FILTER	STR	FILTER
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.TIME	STR	MJD-OBS
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.GAIN	STR	GAIN
+	CELL.XBIN	STR	CCDBIN1
+	CELL.YBIN	STR	CCDBIN2
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???L
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	CELL.READNOISE	S32	10
+	CELL.BAD	S32	0
+	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	65535
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/dvo.config	(revision 22322)
@@ -0,0 +1,50 @@
+
+# location of DVO database tables
+CATDIR			/media/usbdisk/scr1/catdir.synth.grizy/
+
+# keywords used by DVO to interpret the headers
+
+# used by parse_time to find time-related keywords
+MJD-KEYWORD		MJD-OBS
+DATE-KEYWORD		DATE-OBS
+DATE-MODE		YYYY-MM-DD
+UT-KEYWORD		UT
+JD-KEYWORD		JD
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		-7.0
+CAL_INSTMAG_MIN		-9.5
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# allowed astrometry error (arcseconds)
+ADDSTAR_MAX_CERROR	5.0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		5.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+CATMODE			MEF
+CATFORMAT		PANSTARRS_DEV_0
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/dvo.layout
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/dvo.layout	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/dvo.layout	(revision 22322)
@@ -0,0 +1,58 @@
+# this file defines the layout of the mosaic imager:
+
+# NCCD 40  # this needs work: sometimes 36, sometimes 40
+NCCD 36
+NAXIS1 2112
+NAXIS2 4644
+
+MOSAIC_X 11
+MOSAIC_Y  4
+
+# lines need to contain:
+
+CHIPID_KEYWORD EXTNAME
+
+# ID     CHIPID xoffset yoffset xflip yflip datasec         biassec            Xo      Yo      theta
+CCD.0     ccd00       1       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.1     ccd01       2       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.2     ccd02       3       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.3     ccd03       4       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.4     ccd04       5       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.5     ccd05       6       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.6     ccd06       7       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.7     ccd07       8       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.8     ccd08       9       3     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.9     ccd09       1       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.10    ccd10       2       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.11    ccd11       3       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.12    ccd12       4       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.13    ccd13       5       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.14    ccd14       6       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.15    ccd15       7       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.16    ccd16       8       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.17    ccd17       9       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.18    ccd18       1       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.19    ccd19       2       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.20    ccd20       3       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.21    ccd21       4       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.22    ccd22       5       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.23    ccd23       6       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.24    ccd24       7       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.25    ccd25       8       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.26    ccd26       9       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.27    ccd27       1       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.28    ccd28       2       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.29    ccd29       3       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.30    ccd30       4       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.31    ccd31       5       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.32    ccd32       6       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.33    ccd33       7       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.34    ccd34       8       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.35    ccd35       9       0     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.36    ccd36       0       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.37    ccd37      10       2     1     1 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.38    ccd38       0       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+CCD.39    ccd39      10       1     0     0 [6:2049,5:4100] [2085:2110,2:4400] 0       0       0
+
+# can we use header to find biassec
+USE_BIASSEC 1
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/format.config	(revision 22322)
@@ -0,0 +1,101 @@
+# Pan-STARRS Imaging Sky Probe
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+	NAXIS		S32	2
+	TELESCOP	STR	2.2m-UH 
+	INSTRUME	STR	TEK
+	DETECTOR	STR	Tektronix-2048^2-CCD
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS	STR	NAXIS	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC		STR	[21:2068,1:2048]
+		CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.DETECTOR	STR	DETECTOR
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBSTYPE
+	FPA.FILTERID	STR	FILTER
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	ROTANGLE
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.RADECSYS	STR	RADECSYS
+#	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	MJD-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.ALT		STR	ALT
+	FPA.AZ		STR	AZ
+	FPA.TEMP	STR	CCDTEMP
+	FPA.EXPOSURE	STR	EXPTIME
+	CHIP.TEMP	STR	CCDTEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	MJD-OBS
+	CELL.GAIN	STR	GAIN
+#	CELL.SATURATION	STR	SATURATE	### Currently set to 0 ???
+#	CELL.TIMESYS	STR	TIMESYS
+	CELL.XBIN	STR	CCDBIN1
+	CELL.YBIN	STR	CCDBIN2
+# these were used for some early data
+#	CELL.XBIN	STR	XBIN
+#	CELL.YBIN	STR	YBIN
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	CELL.READNOISE	S32	10
+	CELL.BAD	S32	0
+	CHIP.XSIZE	S32	2048
+	CHIP.YSIZE	S32	2048
+	CELL.XSIZE	S32	2048
+	CELL.YSIZE	S32	2048
+	FPA.TIMESYS	STR	UTC
+	CELL.SATURATION	F32	60000
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CHIP.X0		S32	0
+	CHIP.Y0		S32	0
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+#	CELL.XBIN	S32	1
+#	CELL.YBIN	S32	1
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	DEGREES
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	MJD
+	CELL.TIME	STR	MJD
+END
+
+# PS Concepts to get from the database
+DATABASE	METADATA
+# None.
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/ppImage.config	(revision 22322)
@@ -0,0 +1,271 @@
+### ppImage recipe configuration file
+
+# List of tasks to perform
+#NONLIN          BOOL    FALSE           # Non-linearity correction; not implemented
+#OVERSCAN        BOOL    TRUE            # Overscan subtraction
+#BIAS            BOOL    TRUE            # Bias subtraction
+#DARK            BOOL    FALSE            # Dark subtraction
+#SHUTTER         BOOL    FALSE           # Shutter correction
+#FLAT            BOOL    TRUE            # Flat-field normalisation
+#MASK            BOOL    FALSE           # Mask bad pixels
+#MASK.VALUE      STR     SAT,BAD         # Mask pixels with these attributes
+#FRINGE          BOOL    TRUE           # Fringe subtraction
+#PHOTOM          BOOL    TRUE           # Source identification and photometry
+#ASTROM.CHIP     BOOL    FALSE          # Astrometry per chip?
+#ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+
+# Inherit everything from the top-level recipe file, except for what's below.
+
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS	STR z
+FRINGE.FILTERS	STR y
+
+# apply the following constraints when selecting a detrend image
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+  END
+  SHUTTER METADATA
+  END	
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+#   VERSION STR RAW
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTERID
+#   AIRMASS  STR FPA.AIRMASS
+#   TWILIGHT STR TWILIGHT:FPA.TIME
+  END
+END
+
+
+# Overscan subtraction only
+PPIMAGE_O         METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Bias subtraction only
+PPIMAGE_B         METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Dark subtraction only
+PPIMAGE_D         METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Shutter correction only
+PPIMAGE_S         METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Flat-fielding only
+PPIMAGE_F         METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    TRUE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Fringe correction only
+PPIMAGE_R         METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Photometry only
+PPIMAGE_P         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    TRUE           # Source identification and photometry
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE           # Save chip-mosaic-ed image? 
+END
+
+# Photometry & Astrometry
+PPIMAGE_A         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  PHOTOM          BOOL    TRUE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Source identification and photometry
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE           # Save chip-mosaic-ed image? 
+END
+
+# Overscan, bias
+PPIMAGE_OB        METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark
+PPIMAGE_OBD      METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    FALSE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS      METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    FALSE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF     METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    FALSE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE            # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR    METADATA
+  BASE.FITS       BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS       BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    FALSE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE            # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP   METADATA
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    FALSE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE            # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    TRUE           # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE            # Save chip-mosaic-ed image? 
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA   METADATA
+  OVERSCAN        BOOL    TRUE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    FALSE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    FALSE            # Flat-field normalisation
+  MASK            BOOL    FALSE            # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS       BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS       BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM          BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP     BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC   BOOL    FALSE           # Astrometry for mosaic?
+  BASE.FITS       BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS       BOOL    TRUE           # Save chip-mosaic-ed image? 
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/ppMerge.config	(revision 22322)
@@ -0,0 +1,3 @@
+### ppImage recipe configuration file
+
+# Inherit everything from the top-level recipe file, except for what's below.
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/psastro.config	(revision 22322)
@@ -0,0 +1,39 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+PSASTRO.PIXEL.SCALE    F32  10.0
+
+# pmAstromGridMatch:
+PSASTRO.GRID.MIN.ANGLE F32 -300.0
+PSASTRO.GRID.MAX.ANGLE F32 +300.0
+PSASTRO.GRID.DEL.ANGLE F32  0.5
+PSASTRO.GRID.MIN.SIGMA F32  5.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32   300000.
+PSASTRO.GRID.SCALE     F32     500
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# match radius in pixels for CHIP astrometry
+PSASTRO.MATCH.RADIUS   F32    8
+
+# pmAstromMatchFit
+PSASTRO.CHIP.ORDER     S32      1  # fit order
+PSASTRO.CHIP.NITER     S32      3  # fit clipping iterations
+PSASTRO.CHIP.NSIGMA    F32      3  # fit clipping sigmas
+
+PSASTRO.MAX.ERROR      F32      10.0 # max error in pixels
+PSASTRO.MIN.NSTAR      S32      0   # min fitted stars in solution
+PSASTRO.MAX.NSTAR      S32      300 # max stars accepted for fitting
+
+MAG_MAX F32 10
+PSASTRO.CATDIR	       STR      SYNTH.GRIZY
+PSASTRO.MATCH.LUMFUNC  BOOL     TRUE
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/psphot.config	(revision 22322)
@@ -0,0 +1,57 @@
+
+# turn these on to see specific outputs
+#SAVE.BACKMDL	BOOL 	TRUE
+#SAVE.BACKGND	BOOL 	TRUE
+#SAVE.BACKSUB	BOOL 	TRUE
+SAVE.RESID	BOOL 	TRUE
+#SAVE.PSF	BOOL 	TRUE
+
+# image statistics parameters
+IMSTATS_NPIX        S32  3000    	 # number of pixels to use for sky estimate boxes:
+
+PSF_SN_LIM          F32  10              # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+PSF_MODEL         STR  PS_MODEL_QGAUSS
+
+MOMENTS_SN_MIN      F32   10.0
+#EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+#FULL_FIT_SN_LIM      F32  50.0
+#AP_MIN_SN            F32  20.0
+PSF_CLUMP_NSIGMA   F32  2.5             # region of Sx,Sy plane to use for selecting PSF stars
+
+# PSFTREND must be a 2D polynomial
+# the specified values are ignored but define the active components of the polynomial
+PSF.TREND.MASK  METADATA  
+   NORDER_X         S32       3                # number of x orders
+   NORDER_Y         S32       3                # number of y orders
+   VAL_X00_Y00      F64       1                # polynomial coefficient
+
+   VAL_X01_Y00      F64       1                # polynomial coefficient
+   VAL_X00_Y01      F64       1                # polynomial coefficient
+
+   VAL_X02_Y00      F64       1                # polynomial coefficient
+   VAL_X01_Y01      F64       1                # polynomial coefficient
+   VAL_X00_Y02      F64       1                # polynomial coefficient
+
+   VAL_X03_Y00      F64       1                # polynomial coefficient
+   VAL_X02_Y01      F64       1                # polynomial coefficient
+   VAL_X01_Y02      F64       1                # polynomial coefficient
+   VAL_X00_Y03      F64       1                # polynomial coefficient
+   NELEMENTS        S32       10               # number of unmasked components
+END  # folder for 4D polynomial
+
+XMIN F32 15
+PSF.RESIDUALS       BOOL false
+BREAK_POINT         STR  ENSEMBLE
+OUTPUT.FORMAT       STR  PS1_DEV_0
+
+PSPHOT.SUMMIT METADATA
+ PEAKS_SMOOTH_SIGMA  F32  0.8 	   	 # peak significance threshold
+ PEAKS_NSIGMA_LIMIT  F32  50.0 	   	 # peak significance threshold
+ PEAKS_NMAX          S32  1000
+ SAVE.RESID	BOOL 	FALSE
+ PSF_SN_LIM          F32  25              # minimum S/N for stars used for PSF model
+ MOMENTS_SN_MIN      F32   25.0
+ PSF_MODEL         STR  PS_MODEL_PGAUSS
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/tek/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/tek/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/tek/rejections.config	(revision 22322)
@@ -0,0 +1,47 @@
+BIAS METADATA
+  FILTER             STR  *
+  EXPECTED           F32  0.0
+  IMFILE.MEAN        F32  2.0
+  IMFILE.STDEV       F32 10.0
+  IMFILE.MEANSTDEV   F32  0.5
+  IMFILE.SKEWNESS    F32  0.0
+  IMFILE.KURTOSIS    F32  0.0
+  IMFILE.SN          F32  0.0
+  IMFILE.BIN.STDEV   F32  5.0
+  IMFILE.BIN.SN      F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  0.5
+  EXP.SN             F32  0.0
+  EXP.BIN.STDEV      F32  0.0
+  EXP.BIN.SN         F32  0.0
+  EXP.FLUX           F32  0.0
+  ENSEMBLE.MEAN      F32  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+DARK 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.SN          F32  0.0
+  IMFILE.BIN.STDEV   F32  0.0
+  IMFILE.BIN.SN      F32  0.0
+  IMFILE.FLUX        F32  2.0
+  EXP.MEAN           F32  0.0
+  EXP.STDEV          F32  0.0
+  EXP.MEANSTDEV      F32  0.0
+  EXP.SN             F32  0.0
+  EXP.BIN.STDEV      F32  0.0
+  EXP.BIN.SN         F32  0.0
+  EXP.FLUX           F32  2.0
+  ENSEMBLE.MEAN      F32  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/camera.config	(revision 22322)
@@ -0,0 +1,50 @@
+# Camera configuration file for MegaCam: describes the camera
+
+# File formats that we know about (these are relative to PATH)
+FORMATS         METADATA
+        SIMPLE  STR     ucam/format_raw.config
+END
+
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+	chip	STR	xy00 xy10 xy20 xy30 xy40 xy50 xy60 xy70 xy01 xy11 xy21 xy31 xy41 xy51 xy61 xy71 xy02 xy12 xy22 xy32 xy42 xy52 xy62 xy72 xy03 xy13 xy23 xy33 xy43 xy53 xy63 xy73 xy04 xy14 xy24 xy34 xy44 xy54 xy64 xy74 xy05 xy15 xy25 xy35 xy45 xy55 xy65 xy75 xy06 xy16 xy26 xy36 xy46 xy56 xy66 xy76 xy07 xy17 xy27 xy37 xy47 xy57 xy67 xy77
+END
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+   g        STR   g
+   r        STR   r
+   i        STR   i
+   z        STR   z
+   y        STR   y
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	fpa	STR	fpa
+	chip	STR	{CHIP.NAME}
+	cell	STR	{CHIP.NAME}:{CELL.NAME}
+END
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES         METADATA
+	# Recipes for ppImage
+        # PPIMAGE         STR     ucam/ppImage.config     # Default: all (normal) options on
+END
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES       STR     recipes/filerules-simple.mdc
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/format_raw.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/format_raw.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/format_raw.config	(revision 22322)
@@ -0,0 +1,145 @@
+### Raw data from microCam
+
+# How to identify this type
+RULE	METADATA
+	ORIGIN		STR	PS0
+#	INSTRUME	STR	ucam1
+	EXTEND		BOOL	T
+	NEXTEND		S32	64
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CELL	# The extensions represent cells
+	FPA.OBS		STR	OBSTYPE	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	xy00	STR	chip:xy00:otCell
+	xy10	STR	chip:xy10:otCell
+	xy20	STR	chip:xy20:otCell
+	xy30	STR	chip:xy30:otCell
+	xy40	STR	chip:xy40:otCell
+	xy50	STR	chip:xy50:otCell
+	xy60	STR	chip:xy60:otCell
+	xy70	STR	chip:xy70:otCell
+	xy01	STR	chip:xy01:otCell
+	xy11	STR	chip:xy11:otCell
+	xy21	STR	chip:xy21:otCell
+	xy31	STR	chip:xy31:otCell
+	xy41	STR	chip:xy41:otCell
+	xy51	STR	chip:xy51:otCell
+	xy61	STR	chip:xy61:otCell
+	xy71	STR	chip:xy71:otCell
+	xy02	STR	chip:xy02:otCell
+	xy12	STR	chip:xy12:otCell
+	xy22	STR	chip:xy22:otCell
+	xy32	STR	chip:xy32:otCell
+	xy42	STR	chip:xy42:otCell
+	xy52	STR	chip:xy52:otCell
+	xy62	STR	chip:xy62:otCell
+	xy72	STR	chip:xy72:otCell
+	xy03	STR	chip:xy03:otCell
+	xy13	STR	chip:xy13:otCell
+	xy23	STR	chip:xy23:otCell
+	xy33	STR	chip:xy33:otCell
+	xy43	STR	chip:xy43:otCell
+	xy53	STR	chip:xy53:otCell
+	xy63	STR	chip:xy63:otCell
+	xy73	STR	chip:xy73:otCell
+	xy04	STR	chip:xy04:otCell
+	xy14	STR	chip:xy14:otCell
+	xy24	STR	chip:xy24:otCell
+	xy34	STR	chip:xy34:otCell
+	xy44	STR	chip:xy44:otCell
+	xy54	STR	chip:xy54:otCell
+	xy64	STR	chip:xy64:otCell
+	xy74	STR	chip:xy74:otCell
+	xy05	STR	chip:xy05:otCell
+	xy15	STR	chip:xy15:otCell
+	xy25	STR	chip:xy25:otCell
+	xy35	STR	chip:xy35:otCell
+	xy45	STR	chip:xy45:otCell
+	xy55	STR	chip:xy55:otCell
+	xy65	STR	chip:xy65:otCell
+	xy75	STR	chip:xy75:otCell
+	xy06	STR	chip:xy06:otCell
+	xy16	STR	chip:xy16:otCell
+	xy26	STR	chip:xy26:otCell
+	xy36	STR	chip:xy36:otCell
+	xy46	STR	chip:xy46:otCell
+	xy56	STR	chip:xy56:otCell
+	xy66	STR	chip:xy66:otCell
+	xy76	STR	chip:xy76:otCell
+	xy07	STR	chip:xy07:otCell
+	xy17	STR	chip:xy17:otCell
+	xy27	STR	chip:xy27:otCell
+	xy37	STR	chip:xy37:otCell
+	xy47	STR	chip:xy47:otCell
+	xy57	STR	chip:xy57:otCell
+	xy67	STR	chip:xy67:otCell
+	xy77	STR	chip:xy77:otCell
+END
+
+# Specify the cell data
+CELLS	METADATA
+	otCell	METADATA	# Orthogonal transfer cell
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS		STR	AIRMASS
+	FPA.FILTER		STR	FILTER
+	FPA.POSANGLE		STR	ROTANGLE
+	FPA.RA			STR	RA
+	FPA.DEC			STR	DEC
+	FPA.RADECSYS		STR	RADECSYS
+	CELL.TIME		STR	DATE-OBS
+	CELL.EXPOSURE		STR	EXPTIME
+	CELL.DARKTIME		STR	DARKTIME
+	CELL.XBIN		STR	CCDBIN1
+	CELL.YBIN		STR	CCDBIN2
+	CELL.XPARITY		STR	IMTM1_1
+	CELL.YPARITY		STR	IMTM2_2
+	CELL.X0			STR	IMNPIX1
+	CELL.Y0			STR	IMNPIX2
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	CELL.GAIN		F32	1
+	CELL.READNOISE		F32	10.0
+	CELL.SATURATION		F32	65535
+	CELL.BAD		F32	0
+	CELL.READDIR		S32	1
+	CELL.TIMESYS		STR	UTC
+	CHIP.X0			S32	0
+	CHIP.Y0			S32	0
+	CHIP.XPARITY		S32	1
+	CHIP.YPARITY		S32	1
+END
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+	TYPE		dbEntry		TABLE		COLUMN		GIVENDBCOL	GIVENPS
+# A database entry refers to a particular column (COLUMN) in a
+# particular table (TABLE), given certain PS concepts (GIVENPS) that
+# match certain database columns (GIVENDBCOL).
+END
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	CELL.TIME	STR	MJD
+	CELL.X0		STR	FORTRAN
+	CELL.Y0		STR	FORTRAN
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage.config	(revision 22322)
@@ -0,0 +1,81 @@
+### Phase 2 recipe configuration file
+
+# List of tasks to perform
+MASK		BOOL	FALSE		# Mask bad pixels
+MASK.VALUE      U8      0x10            # only mask pixels matching this bitmask
+NONLIN		BOOL	FALSE		# Non-linearity correction
+OVERSCAN	BOOL	TRUE		# Overscan subtraction
+BIAS		BOOL	TRUE		# Bias subtraction
+DARK		BOOL	TRUE		# Dark subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+PHOTOM		BOOL	FALSE		# Source identification and photometry
+ASTROM.CHIP	BOOL	FALSE
+ASTROM.MOSAIC	BOOL	FALSE		# Astrometry
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+CHIP.FITS       BOOL   	TRUE            # Save chip-mosaic-ed image? 
+FPA1.FITS       BOOL   	TRUE            # Save 1st binned fpa image? 
+FPA2.FITS       BOOL   	TRUE            # Save 2nd binned fpa image? 
+BIN1.FITS	BOOL	TRUE		# Save 1st binned chip image?
+BIN2.FITS	BOOL	TRUE		# Save 2nd binned chip image?
+BIN1.JPEG	BOOL	TRUE		# Save 1st binned jpeg?
+BIN2.JPEG	BOOL	FALSE		# Save 2nd binned jpeg?
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+#@NONLIN.DATA		F32	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		STR	nonlin.dat	# Filename for lookup table
+#NONLIN.DATA		METADATA		# Source of non-linearity data
+#	ccd00		STR	nonlin00.dat	# A lookup table 
+#	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd02		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd03		F32	1.2345 		# A polynomial
+#	@ccd04		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd05		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd06		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd07		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd08		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd09		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd11		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd12		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd13		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd14		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd15		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd16		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd17		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd18		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd19		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd21		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd22		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd23		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd24		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd25		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd26		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd27		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd28		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd29		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd30		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd31		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd32		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd33		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd34		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd35		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd36		F32	0.0 1.001 0.001	# A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+#OVERSCAN.FIT		STR	SPLINE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	5		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# binned output image options
+BIN1.XBIN		S32	8
+BIN1.YBIN		S32	8
+BIN2.XBIN		S32	64
+BIN2.YBIN		S32	64
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_b.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_b.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_b.config	(revision 22322)
@@ -0,0 +1,82 @@
+### Phase 2 recipe configuration file
+
+# List of tasks to perform
+MASK		BOOL	FALSE		# Mask bad pixels
+MASK.VALUE      U8      0x10            # only mask pixels matching this bitmask
+NONLIN		BOOL	FALSE		# Non-linearity correction
+OVERSCAN	BOOL	FALSE		# Overscan subtraction
+BIAS		BOOL	TRUE		# Bias subtraction
+DARK		BOOL	FALSE		# Dark subtraction
+FLAT		BOOL	FALSE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+PHOTOM		BOOL	FALSE		# Source identification and photometry
+ASTROM.CHIP	BOOL	FALSE
+ASTROM.MOSAIC	BOOL	FALSE		# Astrometry
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+CHIP.FITS       BOOL   	FALSE           # Save chip-mosaic-ed image? 
+FPA1.FITS       BOOL   	FALSE           # Save 1st binned fpa image? 
+FPA2.FITS       BOOL   	FALSE           # Save 2nd binned fpa image? 
+BIN1.FITS	BOOL	TRUE		# Save 1st binned chip image?
+BIN2.FITS	BOOL	TRUE		# Save 2nd binned chip image?
+BIN1.JPEG	BOOL	FALSE		# Save 1st binned jpeg?
+BIN2.JPEG	BOOL	FALSE		# Save 2nd binned jpeg?
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+#@NONLIN.DATA		F32	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		STR	nonlin.dat	# Filename for lookup table
+#NONLIN.DATA		METADATA		# Source of non-linearity data
+#	ccd00		STR	nonlin00.dat	# A lookup table 
+#	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd02		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd03		F32	1.2345 		# A polynomial
+#	@ccd04		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd05		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd06		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd07		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd08		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd09		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd11		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd12		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd13		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd14		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd15		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd16		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd17		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd18		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd19		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd21		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd22		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd23		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd24		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd25		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd26		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd27		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd28		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd29		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd30		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd31		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd32		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd33		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd34		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd35		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd36		F32	0.0 1.001 0.001	# A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+#OVERSCAN.FIT		STR	SPLINE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	5		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# binned output image options
+BIN1.XBIN		S32	8
+BIN1.YBIN		S32	8
+BIN2.XBIN		S32	64
+BIN2.YBIN		S32	64
+
+PHOTCODE.RULE		STR	{CAMERA}.{FILTER.ID}.{CHIP.N}
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_d.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_d.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_d.config	(revision 22322)
@@ -0,0 +1,82 @@
+### Phase 2 recipe configuration file
+
+# List of tasks to perform
+MASK		BOOL	FALSE		# Mask bad pixels
+MASK.VALUE      U8      0x10            # only mask pixels matching this bitmask
+NONLIN		BOOL	FALSE		# Non-linearity correction
+OVERSCAN	BOOL	FALSE		# Overscan subtraction
+BIAS		BOOL	FALSE		# Bias subtraction
+DARK		BOOL	TRUE		# Dark subtraction
+FLAT		BOOL	FALSE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+PHOTOM		BOOL	FALSE		# Source identification and photometry
+ASTROM.CHIP	BOOL	FALSE
+ASTROM.MOSAIC	BOOL	FALSE		# Astrometry
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+CHIP.FITS       BOOL   	FALSE           # Save chip-mosaic-ed image? 
+FPA1.FITS       BOOL   	FALSE           # Save 1st binned fpa image? 
+FPA2.FITS       BOOL   	FALSE           # Save 2nd binned fpa image? 
+BIN1.FITS	BOOL	TRUE		# Save 1st binned chip image?
+BIN2.FITS	BOOL	TRUE		# Save 2nd binned chip image?
+BIN1.JPEG	BOOL	FALSE		# Save 1st binned jpeg?
+BIN2.JPEG	BOOL	FALSE		# Save 2nd binned jpeg?
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+#@NONLIN.DATA		F32	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		STR	nonlin.dat	# Filename for lookup table
+#NONLIN.DATA		METADATA		# Source of non-linearity data
+#	ccd00		STR	nonlin00.dat	# A lookup table 
+#	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd02		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd03		F32	1.2345 		# A polynomial
+#	@ccd04		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd05		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd06		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd07		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd08		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd09		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd11		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd12		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd13		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd14		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd15		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd16		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd17		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd18		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd19		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd21		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd22		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd23		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd24		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd25		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd26		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd27		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd28		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd29		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd30		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd31		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd32		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd33		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd34		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd35		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd36		F32	0.0 1.001 0.001	# A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+#OVERSCAN.FIT		STR	SPLINE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	5		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# binned output image options
+BIN1.XBIN		S32	8
+BIN1.YBIN		S32	8
+BIN2.XBIN		S32	64
+BIN2.YBIN		S32	64
+
+PHOTCODE.RULE		STR	{CAMERA}.{FILTER.ID}.{CHIP.N}
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_f.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_f.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_f.config	(revision 22322)
@@ -0,0 +1,82 @@
+### Phase 2 recipe configuration file
+
+# List of tasks to perform
+MASK		BOOL	FALSE		# Mask bad pixels
+MASK.VALUE      U8      0x10            # only mask pixels matching this bitmask
+NONLIN		BOOL	FALSE		# Non-linearity correction
+OVERSCAN	BOOL	FALSE		# Overscan subtraction
+BIAS		BOOL	FALSE		# Bias subtraction
+DARK		BOOL	FALSE		# Dark subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+PHOTOM		BOOL	FALSE		# Source identification and photometry
+ASTROM.CHIP	BOOL	FALSE
+ASTROM.MOSAIC	BOOL	FALSE		# Astrometry
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+CHIP.FITS       BOOL   	FALSE           # Save chip-mosaic-ed image? 
+FPA1.FITS       BOOL   	FALSE           # Save 1st binned fpa image? 
+FPA2.FITS       BOOL   	FALSE           # Save 2nd binned fpa image? 
+BIN1.FITS	BOOL	TRUE		# Save 1st binned chip image?
+BIN2.FITS	BOOL	TRUE		# Save 2nd binned chip image?
+BIN1.JPEG	BOOL	FALSE		# Save 1st binned jpeg?
+BIN2.JPEG	BOOL	FALSE		# Save 2nd binned jpeg?
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+#@NONLIN.DATA		F32	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		STR	nonlin.dat	# Filename for lookup table
+#NONLIN.DATA		METADATA		# Source of non-linearity data
+#	ccd00		STR	nonlin00.dat	# A lookup table 
+#	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd02		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd03		F32	1.2345 		# A polynomial
+#	@ccd04		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd05		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd06		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd07		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd08		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd09		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd11		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd12		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd13		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd14		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd15		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd16		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd17		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd18		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd19		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd21		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd22		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd23		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd24		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd25		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd26		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd27		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd28		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd29		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd30		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd31		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd32		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd33		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd34		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd35		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd36		F32	0.0 1.001 0.001	# A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+#OVERSCAN.FIT		STR	SPLINE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	5		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# binned output image options
+BIN1.XBIN		S32	8
+BIN1.YBIN		S32	8
+BIN2.XBIN		S32	64
+BIN2.YBIN		S32	64
+
+PHOTCODE.RULE		STR	{CAMERA}.{FILTER.ID}.{CHIP.N}
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_j.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_j.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_j.config	(revision 22322)
@@ -0,0 +1,85 @@
+### Phase 2 recipe configuration file
+
+### A little bit of a cheat to make a mosaicked binned JPEG from the binned FITS file.
+### Set binning to 1, since the input will already be binned.
+
+# List of tasks to perform
+MASK		BOOL	FALSE		# Mask bad pixels
+MASK.VALUE      U8      0xff            # only mask pixels matching this bitmask
+NONLIN		BOOL	FALSE		# Non-linearity correction
+OVERSCAN	BOOL	FALSE		# Overscan subtraction
+BIAS		BOOL	FALSE		# Bias subtraction
+DARK		BOOL	FALSE		# Dark subtraction
+FLAT		BOOL	FALSE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+PHOTOM		BOOL	FALSE		# Source identification and photometry
+ASTROM.CHIP	BOOL	FALSE
+ASTROM.MOSAIC	BOOL	FALSE		# Astrometry
+
+BASE.FITS       BOOL    FALSE            # Save base detrended image?
+CHIP.FITS       BOOL   	FALSE            # Save chip-mosaic-ed image? 
+FPA1.FITS       BOOL   	FALSE            # Save 1st binned fpa image? 
+FPA2.FITS       BOOL   	FALSE            # Save 2nd binned fpa image? 
+BIN1.FITS	BOOL	FALSE		# Save 1st binned chip image?
+BIN2.FITS	BOOL	FALSE		# Save 2nd binned chip image?
+BIN1.JPEG	BOOL	TRUE		# Save 1st binned jpeg?
+BIN2.JPEG	BOOL	FALSE		# Save 2nd binned jpeg?
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+#@NONLIN.DATA		F32	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		STR	nonlin.dat	# Filename for lookup table
+#NONLIN.DATA		METADATA		# Source of non-linearity data
+#	ccd00		STR	nonlin00.dat	# A lookup table 
+#	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd02		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd03		F32	1.2345 		# A polynomial
+#	@ccd04		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd05		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd06		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd07		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd08		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd09		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd11		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd12		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd13		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd14		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd15		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd16		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd17		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd18		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd19		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd21		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd22		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd23		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd24		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd25		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd26		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd27		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd28		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd29		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd30		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd31		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd32		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd33		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd34		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd35		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd36		F32	0.0 1.001 0.001	# A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+#OVERSCAN.FIT		STR	SPLINE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	5		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# binned output image options
+BIN1.XBIN		S32	1
+BIN1.YBIN		S32	1
+BIN2.XBIN		S32	64
+BIN2.YBIN		S32	64
+
+PHOTCODE.RULE		STR	{CAMERA}.{FILTER.ID}.{CHIP.N}
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_o.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_o.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_o.config	(revision 22322)
@@ -0,0 +1,82 @@
+### Phase 2 recipe configuration file
+
+# List of tasks to perform
+MASK		BOOL	FALSE		# Mask bad pixels
+MASK.VALUE      U8      0x10            # only mask pixels matching this bitmask
+NONLIN		BOOL	FALSE		# Non-linearity correction
+OVERSCAN	BOOL	TRUE		# Overscan subtraction
+BIAS		BOOL	FALSE		# Bias subtraction
+DARK		BOOL	FALSE		# Dark subtraction
+FLAT		BOOL	FALSE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+PHOTOM		BOOL	FALSE		# Source identification and photometry
+ASTROM.CHIP	BOOL	FALSE
+ASTROM.MOSAIC	BOOL	FALSE		# Astrometry
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+CHIP.FITS       BOOL   	FALSE           # Save chip-mosaic-ed image? 
+FPA1.FITS       BOOL   	FALSE           # Save 1st binned fpa image? 
+FPA2.FITS       BOOL   	FALSE           # Save 2nd binned fpa image? 
+BIN1.FITS	BOOL	FALSE		# Save 1st binned chip image?
+BIN2.FITS	BOOL	FALSE		# Save 2nd binned chip image?
+BIN1.JPEG	BOOL	FALSE		# Save 1st binned jpeg?
+BIN2.JPEG	BOOL	FALSE		# Save 2nd binned jpeg?
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+#@NONLIN.DATA		F32	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		STR	nonlin.dat	# Filename for lookup table
+#NONLIN.DATA		METADATA		# Source of non-linearity data
+#	ccd00		STR	nonlin00.dat	# A lookup table 
+#	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd02		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd03		F32	1.2345 		# A polynomial
+#	@ccd04		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd05		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd06		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd07		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd08		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd09		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd11		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd12		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd13		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd14		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd15		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd16		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd17		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd18		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd19		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd21		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd22		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd23		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd24		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd25		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd26		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd27		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd28		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd29		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd30		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd31		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd32		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd33		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd34		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd35		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd36		F32	0.0 1.001 0.001	# A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+#OVERSCAN.FIT		STR	SPLINE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	5		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# binned output image options
+BIN1.XBIN		S32	8
+BIN1.YBIN		S32	8
+BIN2.XBIN		S32	64
+BIN2.YBIN		S32	64
+
+PHOTCODE.RULE		STR	{CAMERA}.{FILTER.ID}.{CHIP.N}
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_ob.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_ob.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_ob.config	(revision 22322)
@@ -0,0 +1,82 @@
+### Phase 2 recipe configuration file
+
+# List of tasks to perform
+MASK		BOOL	FALSE		# Mask bad pixels
+MASK.VALUE      U8      0x10            # only mask pixels matching this bitmask
+NONLIN		BOOL	FALSE		# Non-linearity correction
+OVERSCAN	BOOL	TRUE		# Overscan subtraction
+BIAS		BOOL	TRUE		# Bias subtraction
+DARK		BOOL	FALSE		# Dark subtraction
+FLAT		BOOL	FALSE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+PHOTOM		BOOL	FALSE		# Source identification and photometry
+ASTROM.CHIP	BOOL	FALSE
+ASTROM.MOSAIC	BOOL	FALSE		# Astrometry
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+CHIP.FITS       BOOL   	FALSE           # Save chip-mosaic-ed image? 
+FPA1.FITS       BOOL   	FALSE           # Save 1st binned fpa image? 
+FPA2.FITS       BOOL   	FALSE           # Save 2nd binned fpa image? 
+BIN1.FITS	BOOL	FALSE		# Save 1st binned chip image?
+BIN2.FITS	BOOL	FALSE		# Save 2nd binned chip image?
+BIN1.JPEG	BOOL	FALSE		# Save 1st binned jpeg?
+BIN2.JPEG	BOOL	FALSE		# Save 2nd binned jpeg?
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+#@NONLIN.DATA		F32	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		STR	nonlin.dat	# Filename for lookup table
+#NONLIN.DATA		METADATA		# Source of non-linearity data
+#	ccd00		STR	nonlin00.dat	# A lookup table 
+#	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd02		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd03		F32	1.2345 		# A polynomial
+#	@ccd04		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd05		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd06		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd07		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd08		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd09		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd11		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd12		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd13		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd14		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd15		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd16		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd17		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd18		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd19		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd21		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd22		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd23		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd24		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd25		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd26		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd27		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd28		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd29		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd30		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd31		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd32		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd33		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd34		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd35		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd36		F32	0.0 1.001 0.001	# A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+#OVERSCAN.FIT		STR	SPLINE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	5		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# binned output image options
+BIN1.XBIN		S32	8
+BIN1.YBIN		S32	8
+BIN2.XBIN		S32	64
+BIN2.YBIN		S32	64
+
+PHOTCODE.RULE		STR	{CAMERA}.{FILTER.ID}.{CHIP.N}
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_obd.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_obd.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_obd.config	(revision 22322)
@@ -0,0 +1,82 @@
+### Phase 2 recipe configuration file
+
+# List of tasks to perform
+MASK		BOOL	FALSE		# Mask bad pixels
+MASK.VALUE      U8      0x10            # only mask pixels matching this bitmask
+NONLIN		BOOL	FALSE		# Non-linearity correction
+OVERSCAN	BOOL	TRUE		# Overscan subtraction
+BIAS		BOOL	TRUE		# Bias subtraction
+DARK		BOOL	FALSE		# Dark subtraction
+FLAT		BOOL	FALSE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+PHOTOM		BOOL	FALSE		# Source identification and photometry
+ASTROM.CHIP	BOOL	FALSE
+ASTROM.MOSAIC	BOOL	FALSE		# Astrometry
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+CHIP.FITS       BOOL   	FALSE           # Save chip-mosaic-ed image? 
+FPA1.FITS       BOOL   	FALSE           # Save 1st binned fpa image? 
+FPA2.FITS       BOOL   	FALSE           # Save 2nd binned fpa image? 
+BIN1.FITS	BOOL	FALSE		# Save 1st binned chip image?
+BIN2.FITS	BOOL	FALSE		# Save 2nd binned chip image?
+BIN1.JPEG	BOOL	FALSE		# Save 1st binned jpeg?
+BIN2.JPEG	BOOL	FALSE		# Save 2nd binned jpeg?
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+#@NONLIN.DATA		F32	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		STR	nonlin.dat	# Filename for lookup table
+#NONLIN.DATA		METADATA		# Source of non-linearity data
+#	ccd00		STR	nonlin00.dat	# A lookup table 
+#	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd02		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd03		F32	1.2345 		# A polynomial
+#	@ccd04		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd05		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd06		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd07		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd08		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd09		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd11		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd12		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd13		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd14		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd15		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd16		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd17		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd18		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd19		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd21		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd22		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd23		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd24		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd25		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd26		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd27		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd28		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd29		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd30		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd31		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd32		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd33		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd34		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd35		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd36		F32	0.0 1.001 0.001	# A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+#OVERSCAN.FIT		STR	SPLINE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	5		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# binned output image options
+BIN1.XBIN		S32	8
+BIN1.YBIN		S32	8
+BIN2.XBIN		S32	64
+BIN2.YBIN		S32	64
+
+PHOTCODE.RULE		STR	{CAMERA}.{FILTER.ID}.{CHIP.N}
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_obdf.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_obdf.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppImage_obdf.config	(revision 22322)
@@ -0,0 +1,82 @@
+### Phase 2 recipe configuration file
+
+# List of tasks to perform
+MASK		BOOL	FALSE		# Mask bad pixels
+MASK.VALUE      U8      0x10            # only mask pixels matching this bitmask
+NONLIN		BOOL	FALSE		# Non-linearity correction
+OVERSCAN	BOOL	TRUE		# Overscan subtraction
+BIAS		BOOL	TRUE		# Bias subtraction
+DARK		BOOL	TRUE		# Dark subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+FRINGE		BOOL	FALSE		# Fringe subtraction
+PHOTOM		BOOL	FALSE		# Source identification and photometry
+ASTROM.CHIP	BOOL	FALSE
+ASTROM.MOSAIC	BOOL	FALSE		# Astrometry
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+CHIP.FITS       BOOL   	FALSE           # Save chip-mosaic-ed image? 
+FPA1.FITS       BOOL   	FALSE           # Save 1st binned fpa image? 
+FPA2.FITS       BOOL   	FALSE           # Save 2nd binned fpa image? 
+BIN1.FITS	BOOL	FALSE		# Save 1st binned chip image?
+BIN2.FITS	BOOL	FALSE		# Save 2nd binned chip image?
+BIN1.JPEG	BOOL	TRUE		# Save 1st binned jpeg?
+BIN2.JPEG	BOOL	TRUE		# Save 2nd binned jpeg?
+
+# Non-linearity correction
+NONLIN.SOURCE		STR	CHIP.NAME	# How to determine the source
+#@NONLIN.DATA		F32	0.0 1.001 0.001	# A polynomial
+NONLIN.DATA		STR	nonlin.dat	# Filename for lookup table
+#NONLIN.DATA		METADATA		# Source of non-linearity data
+#	ccd00		STR	nonlin00.dat	# A lookup table 
+#	@ccd01		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd02		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd03		F32	1.2345 		# A polynomial
+#	@ccd04		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd05		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd06		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd07		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd08		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd09		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd11		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd12		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd13		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd14		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd15		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd16		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd17		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd18		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd19		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd10		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd21		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd22		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd23		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd24		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd25		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd26		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd27		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd28		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd29		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd30		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd31		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd32		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd33		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd34		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd35		F32	0.0 1.001 0.001	# A polynomial
+#	@ccd36		F32	0.0 1.001 0.001	# A polynomial
+#END
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+#OVERSCAN.FIT		STR	SPLINE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	5		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# binned output image options
+BIN1.XBIN		S32	8
+BIN1.YBIN		S32	8
+BIN2.XBIN		S32	64
+BIN2.YBIN		S32	64
+
+PHOTCODE.RULE		STR	{CAMERA}.{FILTER.ID}.{CHIP.N}
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppMerge_bias.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppMerge_bias.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppMerge_bias.config	(revision 22322)
@@ -0,0 +1,14 @@
+# Recipe configuration for ppMerge
+ 
+ROWS            S32     128		# Number of rows to read at once
+ELECTRONS       F32     100.0           # Minimum number of electrons for useful signal
+SAMPLE          S32     100             # Sampling factor for measuring the background
+REJ		F32	3.0		# Rejection threshold (sigma)
+ITER		S32	1		# Number of rejection iterations
+FRACHIGH	F32	0.3		# Fraction of high pixels to reject immediately
+FRACLOW		F32	0.1		# Fraction of low pixels to reject immediately
+NKEEP		S32	5		# Minimum number of pixels in stack to keep
+MASKVAL		S32	0xff		# Mask value for input data
+### Statistics options: MEAN | MEDIAN | ROBUST | FITTED | CLIPPED
+COMBINE		STR	MEAN		# Statistic to use for combination: 
+BACKGROUND	STR	MEDIAN		# Statistic to use to measure the background
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppMerge_dark.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppMerge_dark.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppMerge_dark.config	(revision 22322)
@@ -0,0 +1,14 @@
+# Recipe configuration for ppMerge
+ 
+ROWS            S32     128		# Number of rows to read at once
+ELECTRONS       F32     100.0           # Minimum number of electrons for useful signal
+SAMPLE          S32     100             # Sampling factor for measuring the background
+REJ		F32	3.0		# Rejection threshold (sigma)
+ITER		S32	1		# Number of rejection iterations
+FRACHIGH	F32	0.3		# Fraction of high pixels to reject immediately
+FRACLOW		F32	0.1		# Fraction of low pixels to reject immediately
+NKEEP		S32	5		# Minimum number of pixels in stack to keep
+MASKVAL		S32	0xff		# Mask value for input data
+### Statistics options: MEAN | MEDIAN | ROBUST | FITTED | CLIPPED
+COMBINE		STR	MEAN		# Statistic to use for combination: 
+BACKGROUND	STR	MEDIAN		# Statistic to use to measure the background
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppMerge_flat.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppMerge_flat.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/ppMerge_flat.config	(revision 22322)
@@ -0,0 +1,14 @@
+# Recipe configuration for ppMerge
+ 
+ROWS            S32     128		# Number of rows to read at once
+ELECTRONS       F32     100.0           # Minimum number of electrons for useful signal
+SAMPLE          S32     100             # Sampling factor for measuring the background
+REJ		F32	3.0		# Rejection threshold (sigma)
+ITER		S32	1		# Number of rejection iterations
+FRACHIGH	F32	0.3		# Fraction of high pixels to reject immediately
+FRACLOW		F32	0.1		# Fraction of low pixels to reject immediately
+NKEEP		S32	5		# Minimum number of pixels in stack to keep
+MASKVAL		S32	0xff		# Mask value for input data
+### Statistics options: MEAN | MEDIAN | ROBUST | FITTED | CLIPPED
+COMBINE		STR	MEAN		# Statistic to use for combination: 
+BACKGROUND	STR	MEDIAN		# Statistic to use to measure the background
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/psastro.config	(revision 22322)
@@ -0,0 +1,36 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+# PSASTRO.PLATE.SCALE    F32  13.5
+PSASTRO.PLATE.SCALE    F32  1.0
+
+# pmAstromGridMatch:
+PSASTRO.GRID.MIN.ANGLE F32 -0.0851
+PSASTRO.GRID.MAX.ANGLE F32 +0.0851
+PSASTRO.GRID.DEL.ANGLE F32  0.0170
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+# PSASTRO.GRID.OFFSET    F32  10000.
+# PSASTRO.GRID.SCALE     F32    500.
+PSASTRO.GRID.OFFSET    F32   1000.
+PSASTRO.GRID.SCALE     F32     50
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# pmAstromRadiusMatch
+# use plate-scale to make this in pixels?
+PSASTRO.MATCH.RADIUS   F32    8
+
+# pmAstromMatchFit
+PSASTRO.CHIP.NX        S32      1  # fit order
+PSASTRO.CHIP.NY        S32      1  # fit order
+PSASTRO.CHIP.NITER     S32      3  # fit clipping iterations
+PSASTRO.CHIP.NSIGMA    F32      3  # fit clipping sigmas
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/ucam/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/ucam/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/ucam/psphot.config	(revision 22322)
@@ -0,0 +1,27 @@
+
+# turn these on to see specific outputs
+#SAVE.BACKMDL	BOOL 	TRUE
+#SAVE.BACKGND	BOOL 	TRUE
+#SAVE.BACKSUB	BOOL 	TRUE
+SAVE.RESID	BOOL 	TRUE
+#SAVE.PSF	BOOL 	TRUE
+#LOAD.PSF	BOOL 	FALSE
+
+IMSTATS_NPIX        S32  1000    	 # number of pixels to use for sky estimate boxes:
+
+PSF_SN_LIM          F32  100             # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+# PSF model parameters : choose the PSF model
+# list as many PSF_MODEL options as desired
+# if you want to list multiple entries, uncomment 'MULTI' here
+# PSF_MODEL           MULTI
+PSF_MODEL           STR  PS_MODEL_QGAUSS
+# PSF_MODEL           STR  PS_MODEL_GAUSS
+# PSF_MODEL           STR  PS_MODEL_PGAUSS
+# PSF_MODEL           STR  PS_MODEL_TGAUSS  ## not well tested, not very successful
+
+MOMENTS_SN_MIN      F32  20.0           # min S/N to measure moments
+EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+FULL_FIT_SN_LIM      F32  50.0
+AP_MIN_SN            F32  25.0
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/Makefile.am	(revision 22322)
@@ -0,0 +1,22 @@
+
+installdir = $(datadir)/ippconfig/uh8k
+
+install_files = \
+	dvo.config \
+	camera.config \
+	format_split.config \
+	ppImage.config \
+	ppMerge.config \
+	psastro.config \
+	psphot.config \
+	pswarp.config \
+	rejections.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/camera.config	(revision 22322)
@@ -0,0 +1,88 @@
+# Camera configuration file for WFI/8k on UH88
+
+# File formats that we know about
+FORMATS         METADATA
+        SPLIT   STR     uh8k/format_split.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        chip00   STR     Cell
+        chip01   STR     Cell
+        chip02   STR     Cell
+        chip03   STR     Cell
+        chip04   STR     Cell
+        chip05   STR     Cell
+        chip06   STR     Cell
+        chip07   STR     Cell
+END
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+	UKNOWN	STR	UNKNOWN
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID         METADATA
+        FPA     STR     fpa
+        CHIP    STR     {CHIP.NAME}
+        CELL    STR     {CHIP.NAME}:{CELL.NAME}
+        fpa     STR     fpa
+        chip    STR     {CHIP.NAME}
+        cell    STR     {CHIP.NAME}:{CELL.NAME}
+END
+
+DVO.CAMERADIR	STR	uh8k		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias     STR BIAS
+  zero     STR BIAS
+  dark     STR DARK
+  flat     STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES         METADATA
+        # Other recipes
+        PSPHOT          STR     uh8k/psphot.config           # psphot details
+        PSASTRO         STR     uh8k/psastro.config          # psastro details
+	PSWARP		STR	uh8k/pswarp.config		# pswarp details
+        PPIMAGE         STR     uh8k/ppImage.config          # ppImage recipe
+	REJECTIONS      STR     uh8k/rejections.config       # rejection recipe
+	PPMERGE		STR	uh8k/ppMerge.config		# ppMerge recipe
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES	STR	recipes/filerules-split.mdc		# File rules appropriate for MEF format
+
+EXTNAME.RULES METADATA
+  CMF.HEAD   STR {CHIP.NAME}.hdr
+  CMF.DATA   STR {CHIP.NAME}.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD   STR {CHIP.NAME}.hdr
+  PSF.TABLE  STR {CHIP.NAME}.psf_model
+  PSF.RESID  STR {CHIP.NAME}.psf_resid
+
+  REF.ASTROM STR {CHIP.NAME}.ref_astrom
+END
+
+BLANK.HEADERS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.OBS		STR	EXPNUM
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.N}		# Rule for generating photcode
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/dvo.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/dvo.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/dvo.config	(revision 22322)
@@ -0,0 +1,67 @@
+
+# location of DVO database tables
+CATDIR			/data/alala.0/eugene/isp/catdir
+
+# keywords used by DVO to interpret the headers
+
+# keyword abstractions:
+#DATE-KEYWORD		DATE-OBS
+#DATE-MODE		yyyy-mm-dd
+#UT-KEYWORD		UTC-OBS
+JD-KEYWORD		NONE
+MJD-KEYWORD		MJD-OBS
+
+# other keyword abstractions
+EXPTIME-KEYWORD		EXPTIME
+AIRMASS-KEYWORD		AIRMASS
+CCDNUM-KEYWORD		EXTNAME
+ST-KEYWORD		NONE
+OBSERVATORY-LATITUDE	NONE
+OBSERVATORY-LONGITUDE	NONE
+SUBPIX_DATAFILE		NONE
+
+# instrumental magnitude range for calibration mode 
+CAL_INSTMAG_MAX		0
+CAL_INSTMAG_MIN		0
+
+# exclude overscan region from the dB image boundaries
+XOVERSCAN		0
+YOVERSCAN		0
+
+# only upload stars within region; a value of 0 means ignore the limit
+ADDSTAR_XMIN		0
+ADDSTAR_XMAX		0
+ADDSTAR_YMIN		0
+ADDSTAR_YMAX		0
+
+# exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT)
+ADDSTAR_SNLIMIT		0
+
+# correlation radius (arcseconds)
+ADDSTAR_RADIUS		1.0
+
+# scaled correlation radius 
+ADDSTAR_NSIGMA		0
+
+IMAGE_SCATTER           0.075  # mark images POOR if stdev(Mcal) > IMAGE_SCATTER
+STAR_SCATTER            0.005  # mark stars POOR if stdev(Mrel) > STAR_SCATTER
+IMAGE_OFFSET            0.100  # mark images POOR if abs(delta(Mcal)) > IMAGE_OFFSET
+STAR_CHISQ              10.0   # mark stars POOR if Xm > STAR_CHISQ
+STAR_TOOFEW              3     # mark star FEW if N(good) < STAR_TOOFEW
+IMAGE_TOOFEW            10     # mark image FEW if N(good) < IMAGE_TOOFEW
+IMAGE_GOOD_FRACTION     0.05   # mark image FEW if N(good) < IMAGE_GOOD_FRACTION * Nstars
+
+SCATTER_LIM             15.0
+MAG_LIM                 20.0   # select stars brighter than this for relphot analysis
+SIGMA_LIM               0.015  # select measurements with dM < SIGMA_LIM for relphot analysis
+# INST_MAG_MIN         -17     # optional constraints on magnitude ranges
+# INST_MAG_MAX         -13     # optional constraints on magnitude ranges
+
+RELPHOT_GRID_BINNING    256
+RELPHOT_GRID_X 6
+RELPHOT_GRID_Y 14
+
+PM_DT_MIN               0.25
+PM_TOOFEW               4
+POS_TOOFEW              1
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/format_split.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/format_split.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/format_split.config	(revision 22322)
@@ -0,0 +1,156 @@
+# Camera format for WFI/8k on the UH88
+
+# How to recognise this type
+RULE	METADATA
+	ORIGIN		STR	UH 2.2m Telescope
+	CCD		STR	8k
+	DETECTOR	STR	8k
+	INSTRUME	STR	8k/WFI
+	IMAGESWV	STR	CFHT DetCom v3.41 (Jun 10 2008)
+	NAXIS           S32     2
+END
+
+FILE	METADATA
+	# How to read this data
+	PHU		STR	CHIP	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# The extensions represent chips
+	FPA.OBS		STR	EXPNUM	# A PHU keyword for unique identifier
+	CONTENT		STR	EXTNAME	# Key to the CONTENTS menu
+	CONTENT.RULE	STR	{CHIP.NAME} # How to derive the CONTENT when writing
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Content name, chipType
+	chip00		STR	chip00:SingleAmp
+	chip01		STR	chip01:SingleAmp
+	chip02		STR	chip02:SingleAmp
+	chip03		STR	chip03:SingleAmp
+	chip04		STR	chip04:SingleAmp
+	chip05		STR	chip05:SingleAmp
+	chip06		STR	chip06:SingleAmp
+	chip07		STR	chip07:SingleAmp
+END
+
+# Specify the chips
+CHIPS		METADATA
+	# Chip type, cellName:cellType
+	SingleAmp	STR	Cell:amplifier
+END
+
+
+# Specify the cells
+CELLS		METADATA
+	# Cell type, concepts for this type
+	amplifier	METADATA
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.DETECTOR    STR	DETECTOR
+        FPA.AIRMASS     STR     AIRMASS
+        FPA.FILTER      STR     FILTER
+        FPA.FILTERID    STR     FILTER
+        FPA.RA          STR     RA
+        FPA.DEC         STR     DEC
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.OBJECT	STR	OBJECT
+	FPA.FOCUS	STR	TELFOCUS
+	FPA.TIME	STR	DATE-OBS TIME-OBS
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.EXPOSURE	STR	EXPTIME
+        CELL.EXPOSURE   STR     EXPTIME
+        CELL.DARKTIME   STR     DARKTIME
+	CELL.TIME	STR	DATE-OBS TIME-OBS
+	CELL.TIMESYS	STR	TIMESYS
+        CELL.XBIN       STR     CCDBIN1
+        CELL.YBIN       STR     CCDBIN2
+        CELL.SATURATION STR     MAXLIN
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS        METADATA
+        FPA.POSANGLE    	F32	0.0
+        FPA.RADECSYS    	STR     ICRS
+        CELL.READNOISE  	F32	10.0	# A guess
+	CELL.GAIN		F32	1.0	# A guess
+
+	FPA.TELESCOPE		STR	UH 88-inch
+	CHIP.XSIZE		S32	2048
+	CHIP.YSIZE		S32	4096
+	CELL.XSIZE		S32	2048
+	CELL.YSIZE		S32	4096
+	CELL.XWINDOW		S32	0
+	CELL.YWINDOW		S32	0
+	CELL.READDIR		S32	1		# Cell is read in x direction
+        CELL.BAD                F32     0
+	CELL.XPARITY		S32	1
+	CELL.YPARITY		S32	1
+	CELL.Y0			S32	0
+	CELL.X0			S32	0
+	CHIP.X0.DEPEND		STR	CHIP.NAME
+	CHIP.X0		METADATA
+		chip00	S32	0
+		chip01	S32	2048
+		chip02	S32	4096
+		chip03	S32	6144
+		chip04	S32	0   
+		chip05	S32	2048
+		chip06	S32	4096
+		chip07	S32	6144
+	END
+	CHIP.Y0.DEPEND		STR	CHIP.NAME
+	CHIP.Y0		METADATA
+		chip00	S32	8192
+		chip01	S32	8192
+		chip02	S32	8192
+		chip03	S32	8192
+		chip04	S32	0
+		chip05	S32	0
+		chip06	S32	0
+		chip07	S32	0
+	END
+	CHIP.XPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.XPARITY	METADATA
+		chip00	S32	-1
+		chip01	S32	-1
+		chip02	S32	-1
+		chip03	S32	-1
+		chip04	S32	1
+		chip05	S32	1
+		chip06	S32	1
+		chip07	S32	1
+	END
+	CHIP.YPARITY.DEPEND	STR	CHIP.NAME
+	CHIP.YPARITY	METADATA
+		chip00	S32	-1
+		chip01	S32	-1
+		chip02	S32	-1
+		chip03	S32	-1
+		chip04	S32	1
+		chip05	S32	1
+		chip06	S32	1
+		chip07	S32	1
+	END
+END
+
+
+# How to translation PS concepts into database lookups
+DATABASE	METADATA
+END		
+
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	SEPARATE YEAR.FIRST
+	CELL.TIME	STR	SEPARATE YEAR.FIRST
+END
+ 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/ppImage.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/ppImage.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/ppImage.config	(revision 22322)
@@ -0,0 +1,169 @@
+### ppImage recipe configuration file for Megacam
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+OVERSCAN.FIT		STR	NONE		# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	0		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# for a test, turn off selected detrend types
+OVERSCAN	BOOL	FALSE		# Overscan subtraction
+BIAS		BOOL	FALSE		# Bias subtraction
+DARK		BOOL	TRUE		# Dark subtraction
+FLAT		BOOL	TRUE		# Flat-field normalisation
+
+# binned output image options
+BIN1.XBIN               S32     18
+BIN1.YBIN               S32     18
+BIN2.XBIN               S32     144
+BIN2.YBIN               S32     144
+
+# megacam needs fringe subtraction
+FRINGE		BOOL	TRUE		# Fringe subtraction
+
+# this table lists extra constraints which should be applied when
+# selecting the detrend images.
+# note: camera and time are always applied
+
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  DARK METADATA
+#   EXPTIME STR FPA.EXPOSURE
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTERID
+#   DETTYPE STR DOMEFLAT.RAW
+#   VERSION STR SKYFLAT.RAW
+#   VERSION STR DOMEFLAT.CORR.XX
+#   VERSION STR SKYFLAT.CORR
+  END
+  FLAT_CORRECTION METADATA
+    FILTER   STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+#   AIRMASS  STR FPA.AIRMASS
+#   TWILIGHT STR FPA.TWILIGHT
+  END
+  SHUTTER METADATA
+  END
+  MASK METADATA
+  END	
+END
+
+# only apply the fringe for these filters
+FRINGE.FILTERS  MULTI
+FRINGE.FILTERS	STR i
+FRINGE.FILTERS	STR z
+
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS       METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    FALSE           # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF      METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # 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?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR     METADATA
+  BASE.FITS        BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.WEIGHT.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.WEIGHT.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  PHOTOM           BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom
+PPIMAGE_OBDSFRP    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe, photom, astrom
+PPIMAGE_OBDSFRA    METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.WEIGHT.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.WEIGHT.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    FALSE           # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  FRINGE           BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+END 
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/ppMerge.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/ppMerge.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/ppMerge.config	(revision 22322)
@@ -0,0 +1,40 @@
+
+# Bias combination --- don't want min/max rejection
+PPMERGE_BIAS	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
+   WEIGHTS	BOOL	FALSE		# Use image weights?
+   COMBINE	STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Dark combination --- don't want min/max rejection
+# More aggressive clipping than bias, so as to remove CRs
+PPMERGE_DARK	METADATA
+  REJ		F32	2.0		# Rejection threshold (sigma)
+  ITER		S32	4		# 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
+  WEIGHTS	BOOL	FALSE		# Use image weights?
+  COMBINE	STR	CLIPPED		# Statistic to use for combination: 
+END
+
+# Flat combination --- use min/max rejection
+PPMERGE_FLAT	METADATA
+	REJ		F32	3.0		# Rejection threshold (sigma)
+	ITER		S32	1		# Number of rejection iterations
+	FRACHIGH	F32	0.3		# Fraction of high pixels to reject immediately
+	FRACLOW		F32	0.1		# Fraction of low pixels to reject immediately
+	NKEEP		S32	5		# Minimum number of pixels in stack to keep
+	WEIGHTS		BOOL	FALSE		# Use image weights?
+	COMBINE		STR	MEAN		# Statistic to use for combination: 
+END
+
+
+# Fringe combination --- already included in default, above
+PPMERGE_FRINGE	METADATA
+	FRACHIGH	F32	0.1		# Fraction of high pixels to reject immediately
+	WEIGHTS		BOOL	FALSE		# Use image weights?
+END
+
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/psastro.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/psastro.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/psastro.config	(revision 22322)
@@ -0,0 +1,74 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+PSASTRO.PIXEL.SCALE    F32  1.0
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+PSASTRO.GRID.OFFSET    F32    2500.
+PSASTRO.GRID.SCALE     F32      50.
+
+# extra field for ref stars:
+PSASTRO.FIELD.PADDING  F32 1.0
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# single-chip radius match in pixels
+PSASTRO.MATCH.FIT.NITER S32   3
+PSASTRO.MATCH.RADIUS    F32   12.0
+PSASTRO.MATCH.RADIUS.N0 F32   15.0
+PSASTRO.MATCH.RADIUS.N1 F32   10.0
+PSASTRO.MATCH.RADIUS.N2 F32    5.0
+
+PSASTRO.MAX.NRAW       S32      1500   # max stars accepted for fitting (0 for all)
+PSASTRO.MAX.NREF       S32      1500   # max stars accepted for fitting (0 for all)
+
+PSASTRO.MIN.INST.MAG.RAW       F32      -15.5  # min instrumental magnitude for stars accepted for fitting
+PSASTRO.MAX.INST.MAG.RAW       F32       -8.0  # max instrumental magnitude for stars accepted for fitting
+
+PSASTRO.MAX.ERROR      F32      10.0 # max error in pixels
+PSASTRO.MIN.NSTAR      S32      10   # min fitted stars in solution
+
+PSASTRO.MOSAIC.MODE           BOOL     FALSE
+
+PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+PSASTRO.MOSAIC.RADIUS.N0    F32    10.0
+PSASTRO.MOSAIC.RADIUS.N1    F32     8.0 # do not refine the match
+PSASTRO.MOSAIC.RADIUS.N2    F32     8.0 # do not refine the match
+PSASTRO.MOSAIC.RADIUS.N3    F32     4.0 
+
+PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      3 # fit order (-1 means use default)
+
+PSASTRO.MOSAIC.GRADIENT.NX    S32      2   # number of x-dir cells per chip
+PSASTRO.MOSAIC.GRADIENT.NY    S32      4   # number of y-dir cells per chip
+
+# use this recipe to set a tight constraint
+PSASTRO.FINE METADATA
+  PSASTRO.MOSAIC.MAX.ERROR.N0 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N1 F32    1.50 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N2 F32    0.90 # max allow error for valid solution (arcsec)
+  PSASTRO.MOSAIC.MAX.ERROR.N3 F32    0.90 # max allow error for valid solution (arcsec)
+
+  PSASTRO.MOSAIC.RADIUS.N0    F32    8.0
+  PSASTRO.MOSAIC.RADIUS.N1    F32    0.0
+  PSASTRO.MOSAIC.RADIUS.N2    F32    4.0
+  PSASTRO.MOSAIC.RADIUS.N3    F32    4.0
+
+  PSASTRO.MOSAIC.CHIP.ORDER.N0  S32      0 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N1  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N2  S32      1 # fit order (-1 means use default)
+  PSASTRO.MOSAIC.CHIP.ORDER.N3  S32      1 # fit order (-1 means use default)
+END
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/psphot.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/psphot.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/psphot.config	(revision 22322)
@@ -0,0 +1,43 @@
+
+# turn these on to see specific outputs
+SAVE.OUTPUT	BOOL 	TRUE
+SAVE.BACKMDL	BOOL 	TRUE
+SAVE.PSF	BOOL 	TRUE
+SAVE.PLOTS     	BOOL    TRUE
+
+BACKGROUND.XBIN	    S32  128            # size of background superpixels
+BACKGROUND.YBIN	    S32  128            # size of background superpixels
+
+# image background parameters
+IMSTATS_NPIX        S32  10000    	 # number of pixels to use for sky estimate boxes:
+SKY_STAT            STR  FITTED_MEAN_V4  # statistic used to measure background
+SKY_CLIP_SIGMA      F32  2.0             # statistic used to measure background
+
+PSF_SN_LIM          F32  100             # minimum S/N for stars used for PSF model
+PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
+
+# PSF model parameters : choose the PSF model desired
+PSF_MODEL           STR  PS_MODEL_QGAUSS
+
+MOMENTS_SN_MIN      F32   30.0
+EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
+FULL_FIT_SN_LIM      F32  50.0
+AP_MIN_SN            F32  50.0
+
+# OUTPUT.FORMAT       STR SMPDATA
+OUTPUT.FORMAT       STR PS1_DEV_1
+
+PSF_SHAPE_NSIGMA     F32  3.0		 # max significance for shape variation
+PSF_MAX_CHI          F32  50.0		 # reject objects worse that this
+
+#PSF.TREND.MODE STR MAP
+#PSF.TREND.NX   S32 1
+#PSF.TREND.NY   S32 1
+
+DIAGNOSTIC.PLOTS		METADATA
+  IMAGE.BACKGROUND.CELL.HISTOGRAM   BOOL FALSE
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.X S32 -1
+  IMAGE.BACKGROUND.CELL.HISTOGRAM.Y S32 12
+END
+
+USE_FOOTPRINTS                      BOOL  TRUE       	  # use new pmFootprint peak packaging
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/pswarp.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/pswarp.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/pswarp.config	(revision 22322)
@@ -0,0 +1,3 @@
+### uh8k recipe for pswarp
+
+ASTROM.SOURCE	STR	PSASTRO.OUTPUT	# Source file rule for astrometry, or NULL
Index: /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/rejections.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/rejections.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/uh8k/rejections.config	(revision 22322)
@@ -0,0 +1,38 @@
+
+BIAS METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  2.0
+  IMFILE.STDEV       F32 10.0
+  IMFILE.MEANSTDEV   F32  0.5
+  IMFILE.SKEWNESS    F32  0.0
+  IMFILE.KURTOSIS    F32  0.0
+  IMFILE.SNR         F32  0.0
+  IMFILE.BIN.STDEV   F32  0.5
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  1.0
+  EXP.STDEV          F32 10.0
+  EXP.MEANSTDEV      F32  0.5
+  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  3.0
+  ENSEMBLE.STDEV     F32  3.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+# FILTER is an additional qualifier, and may be "*" (or absent!), in which case it matches everything
+# EXPECTED is the expected mean value
+# IMFILE.MEAN is the maximum permitted mean value for an imfile, relative to the standard deviation
+# IMFILE.STDEV is the maximum permitted standard deviation for an imfile
+# EXP.MEAN is the maximum permitted mean value for an exposure, relative to the standard deviation
+# EXP.STDEV is the maximum permitted standard deviation for an exposure
+# EXP.MEANSTDEV is the maximum permitted mean standard deviation for an exposure relative to the mean
+# ENSEMBLE.MEAN is the maximum permitted mean for an ensemble of exposures
+# ENSEMBLE.STDEV is the maximum permitted standard deviation for an ensemble of exposures
+# ENSEMBLE.MEANSTDEV is the maximum permitted mean standard deviation for an ensemble of exposures
+# IMFILE.SNR is the minimum permitted signal-to-noise for an imfile
+# EXP.SNR is the minimum permitted signal-to-noise for an exposure
+# These values (all except FILTER) may be zero, in which case no clipping is applied.
Index: /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/Makefile.am	(revision 22322)
@@ -0,0 +1,15 @@
+
+installdir = $(datadir)/ippconfig/vysos5
+
+install_files = \
+	camera.config \
+	format.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/camera.config	(revision 22322)
@@ -0,0 +1,75 @@
+# Camera configuration file for simulation test
+
+# File formats that we know about
+FORMATS         METADATA
+        SIMPLE  STR     vysos5/format.config
+END
+ 
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        Chip            STR     Cell
+END
+
+# valid filter names and corresponding IDs
+FILTER.ID       METADATA
+        NONE    STR     NONE
+        g       STR     PSg
+        r       STR     PSr
+        i       STR     PSi
+        z       STR     PSz
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID         METADATA
+        FPA     STR     fpa
+END
+
+DVO.CAMERADIR   STR     vysos5          # Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias     STR BIAS
+  zero     STR BIAS
+  dark     STR DARK
+  flat     STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES         METADATA
+        # PPIMAGE         STR     vysos5/ppImage.config   # Default: all (normal) options on
+        # PSPHOT          STR     vysos5/psphot.config    # psphot details
+        # PPSIM           STR     vysos5/ppSim.config     # ppSim details
+        # PPSUB           STR     vysos5/ppSub.config     # ppSub details
+        # PSASTRO         STR     vysos5/psastro.config   # psastro details
+        # REJECTIONS      STR     vysos5/rejections.config # Rejection for detrend creation
+END
+
+# reduction classes (recipes which are grouped together)
+REDUCTION       STR     recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES       STR     recipes/filerules-simple.mdc
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR {CHIP.NAME}.hdr
+  CMF.DATA STR {CHIP.NAME}.psf # use .PSF and .EXT?
+  CMF.XSRC STR {CHIP.NAME}.xsrc # use .PSF and .EXT?
+  CMF.XFIT STR {CHIP.NAME}.xfit # use .PSF and .EXT?
+
+  PSF.HEAD  STR {CHIP.NAME}.hdr
+  PSF.TABLE STR {CHIP.NAME}.psf_model
+  PSF.RESID STR {CHIP.NAME}.psf_resid
+END
+
+BLANK.HEADERS   METADATA
+        FPA.TIME        STR     MJD-OBS
+        FPA.EXPOSURE    STR     EXPTIME
+        FPA.AIRMASS     STR     AIRMASS
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}          # Rule for generating photcode
Index: /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippconfig/vysos5/format.config	(revision 22322)
@@ -0,0 +1,81 @@
+# Simulation test camera with single chip with single cell
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	VYSOS-5
+	INSTRUME	STR	Apogee USB/Net
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	OBJECT	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		CELL.TRIMSEC		STR	[1:4098,10:4098]
+		CELL.BIASSEC.SOURCE	STR	VALUE
+		CELL.BIASSEC		STR	[1:4098,1:9]
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.OBSTYPE	STR	IMAGETYP
+        FPA.FILTERID    STR     FILTER
+	FPA.FILTER	STR	FILTER
+	FPA.RA		STR	OBJCTRA
+	FPA.DEC		STR	OBJCTDEC
+	FPA.OBJECT	STR	OBJECT
+        FPA.COMMENT     STR     NOTES
+	FPA.TIME	STR	DATE-OBS	# Date and time
+	FPA.EXPOSURE	STR	EXPTIME
+        FPA.TEMP        STR     CCD-TEMP
+	CHIP.TEMP	STR     CCD-TEMP
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	CELL.TIME	STR	DATE-OBS	# Date and time
+	CELL.XBIN	STR	XBINNING
+	CELL.YBIN	STR	YBINNING
+	FPA.TIMESYS	STR	TIMESYS
+	FPA.RADECSYS	STR	RADECSYS
+	FPA.AIRMASS	STR	AIRMASS ## XXX need to fix this! (ALT, AZ, LAT, LONG are provided)
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.DETECTOR	STR	VYSOS-5
+	FPA.POSANGLE	STR	0.0
+	CELL.SATURATION	F32	65535
+	CELL.BAD	F32	-100 ## XXX this value is bogus, but needs to be context based so residual images do not get half-masked
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+	CELL.XSIZE	S32	2048
+	CELL.YSIZE	S32	2048
+	CELL.XWINDOW	S32	0
+	CELL.YWINDOW	S32	0
+	CELL.GAIN	F32	1.0
+	CELL.READNOISE	F32	10.0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	SEPARATE,YEAR.FIRST
+	CELL.TIME	STR	SEPARATE,YEAR.FIRST
+END
Index: /tags/sj_tags/sj_root_20080929/ippdb/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/.cvsignore	(revision 22322)
@@ -0,0 +1,21 @@
+Doxyfile
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+ippdb.pc
+libtool
+ltmain.sh
+missing
+stamp-h1
+docs
Index: /tags/sj_tags/sj_root_20080929/ippdb/COPYING
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/COPYING	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/COPYING	(revision 22322)
@@ -0,0 +1,345 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /tags/sj_tags/sj_root_20080929/ippdb/Doxyfile.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/Doxyfile.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/Doxyfile.in	(revision 22322)
@@ -0,0 +1,1219 @@
+# Doxyfile 1.4.2
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = @PACKAGE_NAME@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = @PACKAGE_VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+# @top_builddir@ doesn't work for some reason
+OUTPUT_DIRECTORY       = @builddir@/docs
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like the Qt-style comments (thus requiring an 
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation.
+
+SHOW_DIRECTORIES       = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from the 
+# version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the progam writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = @top_srcdir@/src
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+
+FILE_PATTERNS          = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = @PERL@
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that a graph may be further truncated if the graph's 
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH 
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), 
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, which results in a white background. 
+# Warning: Depending on the platform used, enabling this option may lead to 
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to 
+# read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
Index: /tags/sj_tags/sj_root_20080929/ippdb/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/Makefile.am	(revision 22322)
@@ -0,0 +1,75 @@
+SUBDIRS = src tests
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= ippdb.pc
+
+EXTRA_DIST = ippdb.pc.in Doxyfile.in
+
+if HAVE_DOXYGEN
+
+man_MANS = \
+    $(top_builddir)/docs/man/man3/ippdb.3 \
+    $(top_builddir)/docs/man/man3/pzDataStoreRow.3 \
+    $(top_builddir)/docs/man/man3/summitExpRow.3 \
+    $(top_builddir)/docs/man/man3/summitImfileRow.3 \
+    $(top_builddir)/docs/man/man3/pzDownloadExpRow.3 \
+    $(top_builddir)/docs/man/man3/pzDownloadImfileRow.3 \
+    $(top_builddir)/docs/man/man3/newExpRow.3 \
+    $(top_builddir)/docs/man/man3/newImfileRow.3 \
+    $(top_builddir)/docs/man/man3/rawExpRow.3 \
+    $(top_builddir)/docs/man/man3/rawImfileRow.3 \
+    $(top_builddir)/docs/man/man3/guidePendingExpRow.3 \
+    $(top_builddir)/docs/man/man3/chipRunRow.3 \
+    $(top_builddir)/docs/man/man3/chipProcessedImfileRow.3 \
+    $(top_builddir)/docs/man/man3/chipMaskRow.3 \
+    $(top_builddir)/docs/man/man3/camRunRow.3 \
+    $(top_builddir)/docs/man/man3/camProcessedExpRow.3 \
+    $(top_builddir)/docs/man/man3/camMaskRow.3 \
+    $(top_builddir)/docs/man/man3/fakeRunRow.3 \
+    $(top_builddir)/docs/man/man3/fakeProcessedImfileRow.3 \
+    $(top_builddir)/docs/man/man3/fakeMaskRow.3 \
+    $(top_builddir)/docs/man/man3/warpRunRow.3 \
+    $(top_builddir)/docs/man/man3/warpSkyCellMapRow.3 \
+    $(top_builddir)/docs/man/man3/warpSkyfileRow.3 \
+    $(top_builddir)/docs/man/man3/warpMaskRow.3 \
+    $(top_builddir)/docs/man/man3/diffRunRow.3 \
+    $(top_builddir)/docs/man/man3/diffInputSkyfileRow.3 \
+    $(top_builddir)/docs/man/man3/diffSkyfileRow.3 \
+    $(top_builddir)/docs/man/man3/stackRunRow.3 \
+    $(top_builddir)/docs/man/man3/stackInputSkyfileRow.3 \
+    $(top_builddir)/docs/man/man3/stackSumSkyfileRow.3 \
+    $(top_builddir)/docs/man/man3/detRunRow.3 \
+    $(top_builddir)/docs/man/man3/detInputExpRow.3 \
+    $(top_builddir)/docs/man/man3/detProcessedImfileRow.3 \
+    $(top_builddir)/docs/man/man3/detProcessedExpRow.3 \
+    $(top_builddir)/docs/man/man3/detStackedImfileRow.3 \
+    $(top_builddir)/docs/man/man3/detNormalizedStatImfileRow.3 \
+    $(top_builddir)/docs/man/man3/detNormalizedImfileRow.3 \
+    $(top_builddir)/docs/man/man3/detNormalizedExpRow.3 \
+    $(top_builddir)/docs/man/man3/detResidImfileRow.3 \
+    $(top_builddir)/docs/man/man3/detResidExpRow.3 \
+    $(top_builddir)/docs/man/man3/detRunSummaryRow.3 \
+    $(top_builddir)/docs/man/man3/detRegisteredImfileRow.3 \
+    $(top_builddir)/docs/man/man3/detCorrectedExpRow.3 \
+    $(top_builddir)/docs/man/man3/detCorrectedImfileRow.3 \
+    $(top_builddir)/docs/man/man3/magicRunRow.3 \
+    $(top_builddir)/docs/man/man3/magicInputSkyfileRow.3 \
+    $(top_builddir)/docs/man/man3/magicTreeRow.3 \
+    $(top_builddir)/docs/man/man3/magicNodeResultRow.3 \
+    $(top_builddir)/docs/man/man3/magicMaskRow.3 \
+    $(top_builddir)/docs/man/man3/calDBRow.3 \
+    $(top_builddir)/docs/man/man3/calRunRow.3 \
+    $(top_builddir)/docs/man/man3/flatcorrRunRow.3 \
+    $(top_builddir)/docs/man/man3/flatcorrExpRow.3 \
+    $(top_builddir)/docs/man/man3/pstampDataStoreRow.3 \
+    $(top_builddir)/docs/man/man3/pstampRequestRow.3 \
+    $(top_builddir)/docs/man/man3/pstampJobRow.3 
+
+
+docs/man/man3/ippdb.3 docs/man/man3/pzDataStoreRow.3 docs/man/man3/summitExpRow.3 docs/man/man3/summitImfileRow.3 docs/man/man3/pzDownloadExpRow.3 docs/man/man3/pzDownloadImfileRow.3 docs/man/man3/newExpRow.3 docs/man/man3/newImfileRow.3 docs/man/man3/rawExpRow.3 docs/man/man3/rawImfileRow.3 docs/man/man3/guidePendingExpRow.3 docs/man/man3/chipRunRow.3 docs/man/man3/chipProcessedImfileRow.3 docs/man/man3/chipMaskRow.3 docs/man/man3/camRunRow.3 docs/man/man3/camProcessedExpRow.3 docs/man/man3/camMaskRow.3 docs/man/man3/fakeRunRow.3 docs/man/man3/fakeProcessedImfileRow.3 docs/man/man3/fakeMaskRow.3 docs/man/man3/warpRunRow.3 docs/man/man3/warpSkyCellMapRow.3 docs/man/man3/warpSkyfileRow.3 docs/man/man3/warpMaskRow.3 docs/man/man3/diffRunRow.3 docs/man/man3/diffInputSkyfileRow.3 docs/man/man3/diffSkyfileRow.3 docs/man/man3/stackRunRow.3 docs/man/man3/stackInputSkyfileRow.3 docs/man/man3/stackSumSkyfileRow.3 docs/man/man3/detRunRow.3 docs/man/man3/detInputExpRow.3 docs/man/man3/detProcessedImfileRow.3 docs/man/man3/detProcessedExpRow.3 docs/man/man3/detStackedImfileRow.3 docs/man/man3/detNormalizedStatImfileRow.3 docs/man/man3/detNormalizedImfileRow.3 docs/man/man3/detNormalizedExpRow.3 docs/man/man3/detResidImfileRow.3 docs/man/man3/detResidExpRow.3 docs/man/man3/detRunSummaryRow.3 docs/man/man3/detRegisteredImfileRow.3 docs/man/man3/detCorrectedExpRow.3 docs/man/man3/detCorrectedImfileRow.3 docs/man/man3/magicRunRow.3 docs/man/man3/magicInputSkyfileRow.3 docs/man/man3/magicTreeRow.3 docs/man/man3/magicNodeResultRow.3 docs/man/man3/magicMaskRow.3 docs/man/man3/calDBRow.3 docs/man/man3/calRunRow.3 docs/man/man3/flatcorrRunRow.3 docs/man/man3/flatcorrExpRow.3 docs/man/man3/pstampDataStoreRow.3 docs/man/man3/pstampRequestRow.3 docs/man/man3/pstampJobRow.3:
+	$(DOXYGEN)
+
+endif
+
+clean-local:
+	-rm -rf docs
Index: /tags/sj_tags/sj_root_20080929/ippdb/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ippdb
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ippdb/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/configure.ac	(revision 22322)
@@ -0,0 +1,63 @@
+dnl
+dnl This file was generated by glueforge 1.04
+dnl
+dnl Do NOT directly edit this file.
+dnl
+
+AC_PREREQ(2.61)
+
+AC_INIT([ippdb], [1.1.39], [pan-starrs.ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([ippdb.pc.in])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([config.h])
+AM_MAINTAINER_MODE
+
+AC_CONFIG_TESTDIR([tests])
+AC_CONFIG_FILES([tests/Makefile])
+AM_MISSING_PROG([AUTOM4TE], [autom4te])
+
+AC_LANG(C)
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 0.9.0]) 
+ 
+dnl check to make sure that pslib declares HAVE_PSDB
+TMP_CFLAGS=${CFLAGS}
+CFLAGS="${CFLAGS=} ${PSLIB_CFLAGS}"
+
+AC_MSG_CHECKING([pslib's psDB support])
+AC_RUN_IFELSE(
+  [AC_LANG_PROGRAM([], [dnl
+#ifndef HAVE_PSDB
+    return 1;
+#endif])],
+  [AC_MSG_RESULT([yes])],
+  [AC_MSG_FAILURE([pslib was built without psDB support (HAVE_PSDB)])])
+
+CFLAGS=${TMP_CFLAGS}
+
+
+AC_PATH_PROG([PERL], [perl], [missing])
+if test "$PERL" = "missing" ; then
+  AC_MSG_ERROR([perl is required])
+fi
+
+AC_PATH_PROG([DOXYGEN], [doxygen], [])
+AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
+
+dnl is this the best was to setup recursive CFLAGS?
+IPP_STDOPTS
+dnl -Werror causes problems on OSX as inttypes.h uses some non standard C types
+ippdb_CFLAGS="-Wall -pedantic -fno-strict-aliasing"
+AC_SUBST([ippdb_CFLAGS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  ippdb.pc
+  Doxyfile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ippdb/ippdb.pc.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/ippdb.pc.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/ippdb.pc.in	(revision 22322)
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: @PACKAGE@
+Description: some library
+Version: @VERSION@
+Requires: pslib
+Libs: -L${libdir} -l@PACKAGE@
+Cflags: -I${includedir}
Index: /tags/sj_tags/sj_root_20080929/ippdb/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/src/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+.deps
+Makefile
+Makefile.in
+.libs
+libippdb.la
+libippdb_la-ippdb.lo
Index: /tags/sj_tags/sj_root_20080929/ippdb/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/src/Makefile.am	(revision 22322)
@@ -0,0 +1,10 @@
+AM_CFLAGS = @ippdb_CFLAGS@
+
+include_HEADERS = ippdb.h
+lib_LTLIBRARIES = libippdb.la
+libippdb_la_CFLAGS  = $(PSLIB_CFLAGS) $(AM_CFLAGS)
+libippdb_la_LIBS    = $(PSLIB_LIBS) $(AM_LIBS)
+libippdb_la_LDFLAGS = -release $(PACKAGE_VERSION)
+libippdb_la_SOURCES = \
+    ippdb.h \
+    ippdb.c
Index: /tags/sj_tags/sj_root_20080929/ippdb/src/ippdb.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/src/ippdb.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/src/ippdb.c	(revision 22322)
@@ -0,0 +1,27489 @@
+/*
+ * code.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ *
+ * This file was generated by glueforge 1.04
+ *
+ * Do NOT directly edit this file.
+ *
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "ippdb.h"
+
+#define PZDATASTORE_TABLE_NAME "pzDataStore"
+#define SUMMITEXP_TABLE_NAME "summitExp"
+#define SUMMITIMFILE_TABLE_NAME "summitImfile"
+#define PZDOWNLOADEXP_TABLE_NAME "pzDownloadExp"
+#define PZDOWNLOADIMFILE_TABLE_NAME "pzDownloadImfile"
+#define NEWEXP_TABLE_NAME "newExp"
+#define NEWIMFILE_TABLE_NAME "newImfile"
+#define RAWEXP_TABLE_NAME "rawExp"
+#define RAWIMFILE_TABLE_NAME "rawImfile"
+#define GUIDEPENDINGEXP_TABLE_NAME "guidePendingExp"
+#define CHIPRUN_TABLE_NAME "chipRun"
+#define CHIPPROCESSEDIMFILE_TABLE_NAME "chipProcessedImfile"
+#define CHIPMASK_TABLE_NAME "chipMask"
+#define CAMRUN_TABLE_NAME "camRun"
+#define CAMPROCESSEDEXP_TABLE_NAME "camProcessedExp"
+#define CAMMASK_TABLE_NAME "camMask"
+#define FAKERUN_TABLE_NAME "fakeRun"
+#define FAKEPROCESSEDIMFILE_TABLE_NAME "fakeProcessedImfile"
+#define FAKEMASK_TABLE_NAME "fakeMask"
+#define WARPRUN_TABLE_NAME "warpRun"
+#define WARPSKYCELLMAP_TABLE_NAME "warpSkyCellMap"
+#define WARPSKYFILE_TABLE_NAME "warpSkyfile"
+#define WARPMASK_TABLE_NAME "warpMask"
+#define DIFFRUN_TABLE_NAME "diffRun"
+#define DIFFINPUTSKYFILE_TABLE_NAME "diffInputSkyfile"
+#define DIFFSKYFILE_TABLE_NAME "diffSkyfile"
+#define STACKRUN_TABLE_NAME "stackRun"
+#define STACKINPUTSKYFILE_TABLE_NAME "stackInputSkyfile"
+#define STACKSUMSKYFILE_TABLE_NAME "stackSumSkyfile"
+#define DETRUN_TABLE_NAME "detRun"
+#define DETINPUTEXP_TABLE_NAME "detInputExp"
+#define DETPROCESSEDIMFILE_TABLE_NAME "detProcessedImfile"
+#define DETPROCESSEDEXP_TABLE_NAME "detProcessedExp"
+#define DETSTACKEDIMFILE_TABLE_NAME "detStackedImfile"
+#define DETNORMALIZEDSTATIMFILE_TABLE_NAME "detNormalizedStatImfile"
+#define DETNORMALIZEDIMFILE_TABLE_NAME "detNormalizedImfile"
+#define DETNORMALIZEDEXP_TABLE_NAME "detNormalizedExp"
+#define DETRESIDIMFILE_TABLE_NAME "detResidImfile"
+#define DETRESIDEXP_TABLE_NAME "detResidExp"
+#define DETRUNSUMMARY_TABLE_NAME "detRunSummary"
+#define DETREGISTEREDIMFILE_TABLE_NAME "detRegisteredImfile"
+#define DETCORRECTEDEXP_TABLE_NAME "detCorrectedExp"
+#define DETCORRECTEDIMFILE_TABLE_NAME "detCorrectedImfile"
+#define MAGICRUN_TABLE_NAME "magicRun"
+#define MAGICINPUTSKYFILE_TABLE_NAME "magicInputSkyfile"
+#define MAGICTREE_TABLE_NAME "magicTree"
+#define MAGICNODERESULT_TABLE_NAME "magicNodeResult"
+#define MAGICMASK_TABLE_NAME "magicMask"
+#define CALDB_TABLE_NAME "calDB"
+#define CALRUN_TABLE_NAME "calRun"
+#define FLATCORRRUN_TABLE_NAME "flatcorrRun"
+#define FLATCORREXP_TABLE_NAME "flatcorrExp"
+#define PSTAMPDATASTORE_TABLE_NAME "pstampDataStore"
+#define PSTAMPREQUEST_TABLE_NAME "pstampRequest"
+#define PSTAMPJOB_TABLE_NAME "pstampJob"
+#define MAX_STRING_LENGTH 1024
+
+psDB *ippdbInit(const char *host, const char *user, const char *passwd, const char *dbname, unsigned int port)
+{
+    return psDBInit(host, user, passwd, dbname, port);
+}
+
+void ippdbCleanup(psDB *dbh)
+{
+    psDBCleanup(dbh);
+}
+
+bool ippdbPrintMetadata(FILE *stream, psMetadata *md, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(md, false);
+
+    psMetadata *clean = psMetadataCopy(NULL, md);
+
+    if (!ippdbPrintMetadataRaw(stream, clean, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(clean);
+        return false;
+    }
+    psFree(clean);
+
+    return true;
+}
+
+bool ippdbPrintMetadataRaw(FILE *stream, psMetadata *md, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(md, false);
+
+    if (mdcf) {
+        psString str = psMetadataConfigFormat(md);
+        if (!str) {
+            psError(PS_ERR_UNKNOWN, false, "failed to format data into a string");
+            psFree(str);
+            return false;
+        }
+        fprintf(stream, "%s\n", str);
+        psFree(str);
+
+        return true;
+    }
+
+#define METADATAITEM_STRIFY_CASE(ptype, format, type) \
+case ptype: \
+    psStringAppend(&str, format, item->data.type); \
+    break;
+
+    // else
+    // flatten the metadata into | separated values
+    psString str = NULL;
+    psMetadataItem *item = NULL;
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, 0, NULL);
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        switch (item->type) {
+            METADATAITEM_STRIFY_CASE(PS_DATA_S8, "%hhd", S8);
+            METADATAITEM_STRIFY_CASE(PS_DATA_S16, "%hd", S16);
+            METADATAITEM_STRIFY_CASE(PS_DATA_S32, "%d", S32);
+            METADATAITEM_STRIFY_CASE(PS_DATA_S64, "%" PRId64, S64);
+            METADATAITEM_STRIFY_CASE(PS_DATA_U8, "%hhu", U8);
+            METADATAITEM_STRIFY_CASE(PS_DATA_U16, "%hu", U16);
+            METADATAITEM_STRIFY_CASE(PS_DATA_U32, "%u", U32);
+            METADATAITEM_STRIFY_CASE(PS_DATA_U64, "%" PRIu64, U64);
+            METADATAITEM_STRIFY_CASE(PS_DATA_F32, "%f", F32);
+            METADATAITEM_STRIFY_CASE(PS_DATA_F64, "%f", F64);
+            case PS_DATA_STRING:
+                if (item->data.str) {
+                    psString tmpStr = psStringCopy(item->data.str);
+                    psStringSubstitute(&tmpStr, "_", " ");
+                    psStringAppend(&str, "%s", tmpStr);
+                    psFree(tmpStr);
+                }
+                break;
+            case PS_DATA_BOOL:
+                if (item->data.B) {
+                    psStringAppend(&str, "T");
+                } else {
+                    psStringAppend(&str, "F");
+                }
+                break;
+            case PS_DATA_METADATA:
+                if (!ippdbPrintMetadataRaw(stream, item->data.md, mdcf)) {
+                    psError(PS_ERR_UNKNOWN, false ,"failed to print nested metadata");
+                }
+                // a metadata is a special case. We don't want a | seperating
+                // the metadata from any other output so we need to skip the
+                // !->offEnd test
+                continue;
+            case PS_DATA_TIME:
+               // pass through NULLs as "NULL"
+                if (item->data.V) {
+                    psString time = psTimeToISO(item->data.V);
+                    psStringAppend(&str, "%s", time);
+                psFree(time);
+                } else {
+                    psStringAppend(&str, "NULL");
+                }
+                break;
+            default:
+                psError(PS_ERR_UNKNOWN, true,"unsupported psMetadataItem type");
+                psFree(iter);
+                psFree(str);
+                return false;
+        }
+        if (!iter->iter->offEnd) {
+            psStringAppend(&str, " ");
+        }
+    }
+    psFree(iter);
+
+    // if we did nothing but handle recursive metadatas str will be NULL
+    if (str) {
+        fprintf(stream, "%s\n", str);
+        psFree(str);
+    }
+
+    return true;
+}
+
+bool ippdbPrintMetadatas(FILE *stream, psArray *mds, const char *mdname, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(mds, false);
+    PS_ASSERT_PTR_NON_NULL(mdname, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(mds); i++) {
+        psMetadata *md = psMetadataCopy(NULL, mds->data[i]);
+        if (!psMetadataAddMetadata(output, PS_LIST_TAIL, mdname, PS_META_DUPLICATE_OK, NULL, md)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add a metadata item");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadata(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+bool ippdbPrintMetadatasRaw(FILE *stream, psArray *mds, const char *mdname, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(mds, false);
+    PS_ASSERT_PTR_NON_NULL(mdname, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(mds); i++) {
+        psMetadata *md = mds->data[i];
+        if (!psMetadataAddMetadata(output, PS_LIST_TAIL, mdname, PS_META_DUPLICATE_OK, NULL, md)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add a metadata item");
+            psFree(output);
+            return false;
+        }
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static void pzDataStoreRowFree(pzDataStoreRow *object);
+
+pzDataStoreRow *pzDataStoreRowAlloc(const char *camera, const char *telescope, const char *uri, psTime* epoch)
+{
+    pzDataStoreRow  *_object;
+
+    _object = psAlloc(sizeof(pzDataStoreRow));
+    psMemSetDeallocator(_object, (psFreeFunc)pzDataStoreRowFree);
+
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+    _object->uri = psStringCopy(uri);
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void pzDataStoreRowFree(pzDataStoreRow *object)
+{
+    psFree(object->camera);
+    psFree(object->telescope);
+    psFree(object->uri);
+    psFree(object->epoch);
+}
+
+bool pzDataStoreCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, PZDATASTORE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool pzDataStoreDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, PZDATASTORE_TABLE_NAME);
+}
+
+bool pzDataStoreInsert(psDB * dbh, const char *camera, const char *telescope, const char *uri, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, PZDATASTORE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long pzDataStoreDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, PZDATASTORE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDataStore");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pzDataStoreInsertObject(psDB *dbh, pzDataStoreRow *object)
+{
+    return pzDataStoreInsert(dbh, object->camera, object->telescope, object->uri, object->epoch);
+}
+
+bool pzDataStoreInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!pzDataStoreInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool pzDataStoreInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  PZDATASTORE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, PZDATASTORE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", PZDATASTORE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, PZDATASTORE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool pzDataStoreSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, PZDATASTORE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, PZDATASTORE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *pzDataStoreMetadataFromObject(const pzDataStoreRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+pzDataStoreRow *pzDataStoreObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return pzDataStoreRowAlloc(camera, telescope, uri, epoch);
+}
+psArray *pzDataStoreSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, PZDATASTORE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        pzDataStoreRow *object = pzDataStoreObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool pzDataStoreDeleteObject(psDB *dbh, const pzDataStoreRow *object)
+{
+    psMetadata *where = pzDataStoreMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, PZDATASTORE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDataStore");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "pzDataStoreRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long pzDataStoreDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        pzDataStoreRow *object = objects->data[i];
+        psMetadata *where = pzDataStoreMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, PZDATASTORE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDataStore");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pzDataStorePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = pzDataStoreMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            PZDATASTORE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool pzDataStorePrintObject(FILE *stream, pzDataStoreRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = pzDataStoreMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void summitExpRowFree(summitExpRow *object);
+
+summitExpRow *summitExpRowAlloc(const char *exp_name, const char *camera, const char *telescope, psTime* dateobs, const char *exp_type, const char *uri, psS32 imfiles, psS16 fault, psTime* epoch)
+{
+    summitExpRow    *_object;
+
+    _object = psAlloc(sizeof(summitExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)summitExpRowFree);
+
+    _object->exp_name = psStringCopy(exp_name);
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+    _object->dateobs = psTimeCopy(dateobs);
+    _object->exp_type = psStringCopy(exp_type);
+    _object->uri = psStringCopy(uri);
+    _object->imfiles = imfiles;
+    _object->fault = fault;
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void summitExpRowFree(summitExpRow *object)
+{
+    psFree(object->exp_name);
+    psFree(object->camera);
+    psFree(object->telescope);
+    psFree(object->dateobs);
+    psFree(object->exp_type);
+    psFree(object->uri);
+    psFree(object->epoch);
+}
+
+bool summitExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, SUMMITEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool summitExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, SUMMITEXP_TABLE_NAME);
+}
+
+bool summitExpInsert(psDB * dbh, const char *exp_name, const char *camera, const char *telescope, psTime* dateobs, const char *exp_type, const char *uri, psS32 imfiles, psS16 fault, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, dateobs)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, exp_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, imfiles)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, SUMMITEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long summitExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, SUMMITEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from summitExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool summitExpInsertObject(psDB *dbh, summitExpRow *object)
+{
+    return summitExpInsert(dbh, object->exp_name, object->camera, object->telescope, object->dateobs, object->exp_type, object->uri, object->imfiles, object->fault, object->epoch);
+}
+
+bool summitExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!summitExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool summitExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  SUMMITEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, SUMMITEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", SUMMITEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, SUMMITEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool summitExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, SUMMITEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, SUMMITEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *summitExpMetadataFromObject(const summitExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, object->exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, object->dateobs)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, object->exp_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, object->imfiles)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+summitExpRow *summitExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* exp_name = psMetadataLookupPtr(&status, md, "exp_name");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_name");
+        return false;
+    }
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+    psTime* dateobs = psMetadataLookupPtr(&status, md, "dateobs");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dateobs");
+        return false;
+    }
+    char* exp_type = psMetadataLookupPtr(&status, md, "exp_type");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_type");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psS32 imfiles = psMetadataLookupS32(&status, md, "imfiles");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item imfiles");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return summitExpRowAlloc(exp_name, camera, telescope, dateobs, exp_type, uri, imfiles, fault, epoch);
+}
+psArray *summitExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, SUMMITEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        summitExpRow *object = summitExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool summitExpDeleteObject(psDB *dbh, const summitExpRow *object)
+{
+    psMetadata *where = summitExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, SUMMITEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from summitExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "summitExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long summitExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        summitExpRow *object = objects->data[i];
+        psMetadata *where = summitExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, SUMMITEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from summitExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool summitExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = summitExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            SUMMITEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool summitExpPrintObject(FILE *stream, summitExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = summitExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void summitImfileRowFree(summitImfileRow *object);
+
+summitImfileRow *summitImfileRowAlloc(const char *exp_name, const char *camera, const char *telescope, const char *file_id, psS32 bytes, const char *md5sum, const char *class, const char *class_id, const char *uri, psTime* epoch)
+{
+    summitImfileRow *_object;
+
+    _object = psAlloc(sizeof(summitImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)summitImfileRowFree);
+
+    _object->exp_name = psStringCopy(exp_name);
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+    _object->file_id = psStringCopy(file_id);
+    _object->bytes = bytes;
+    _object->md5sum = psStringCopy(md5sum);
+    _object->class = psStringCopy(class);
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void summitImfileRowFree(summitImfileRow *object)
+{
+    psFree(object->exp_name);
+    psFree(object->camera);
+    psFree(object->telescope);
+    psFree(object->file_id);
+    psFree(object->md5sum);
+    psFree(object->class);
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->epoch);
+}
+
+bool summitImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, "Primary Key fkey(exp_name, camera, telescope) ref summitExp(exp_name, camera, telescope)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "file_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item file_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bytes", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "md5sum", PS_DATA_STRING, NULL, "32")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item md5sum");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, SUMMITIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool summitImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, SUMMITIMFILE_TABLE_NAME);
+}
+
+bool summitImfileInsert(psDB * dbh, const char *exp_name, const char *camera, const char *telescope, const char *file_id, psS32 bytes, const char *md5sum, const char *class, const char *class_id, const char *uri, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "file_id", PS_DATA_STRING, NULL, file_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item file_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bytes", PS_DATA_S32, NULL, bytes)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "md5sum", PS_DATA_STRING, NULL, md5sum)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item md5sum");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, class)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, SUMMITIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long summitImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, SUMMITIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from summitImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool summitImfileInsertObject(psDB *dbh, summitImfileRow *object)
+{
+    return summitImfileInsert(dbh, object->exp_name, object->camera, object->telescope, object->file_id, object->bytes, object->md5sum, object->class, object->class_id, object->uri, object->epoch);
+}
+
+bool summitImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!summitImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool summitImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  SUMMITIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, SUMMITIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", SUMMITIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, SUMMITIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool summitImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, SUMMITIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, SUMMITIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *summitImfileMetadataFromObject(const summitImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, object->exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "file_id", PS_DATA_STRING, NULL, object->file_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item file_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bytes", PS_DATA_S32, NULL, object->bytes)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "md5sum", PS_DATA_STRING, NULL, object->md5sum)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item md5sum");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, object->class)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+summitImfileRow *summitImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* exp_name = psMetadataLookupPtr(&status, md, "exp_name");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_name");
+        return false;
+    }
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+    char* file_id = psMetadataLookupPtr(&status, md, "file_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item file_id");
+        return false;
+    }
+    psS32 bytes = psMetadataLookupS32(&status, md, "bytes");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bytes");
+        return false;
+    }
+    char* md5sum = psMetadataLookupPtr(&status, md, "md5sum");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item md5sum");
+        return false;
+    }
+    char* class = psMetadataLookupPtr(&status, md, "class");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return summitImfileRowAlloc(exp_name, camera, telescope, file_id, bytes, md5sum, class, class_id, uri, epoch);
+}
+psArray *summitImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, SUMMITIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        summitImfileRow *object = summitImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool summitImfileDeleteObject(psDB *dbh, const summitImfileRow *object)
+{
+    psMetadata *where = summitImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, SUMMITIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from summitImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "summitImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long summitImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        summitImfileRow *object = objects->data[i];
+        psMetadata *where = summitImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, SUMMITIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from summitImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool summitImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = summitImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            SUMMITIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool summitImfilePrintObject(FILE *stream, summitImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = summitImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void pzDownloadExpRowFree(pzDownloadExpRow *object);
+
+pzDownloadExpRow *pzDownloadExpRowAlloc(const char *exp_name, const char *camera, const char *telescope, const char *state, psTime* epoch)
+{
+    pzDownloadExpRow *_object;
+
+    _object = psAlloc(sizeof(pzDownloadExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)pzDownloadExpRowFree);
+
+    _object->exp_name = psStringCopy(exp_name);
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+    _object->state = psStringCopy(state);
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void pzDownloadExpRowFree(pzDownloadExpRow *object)
+{
+    psFree(object->exp_name);
+    psFree(object->camera);
+    psFree(object->telescope);
+    psFree(object->state);
+    psFree(object->epoch);
+}
+
+bool pzDownloadExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, "Primary Key fkey(exp_name, camera, telescope) ref summitExp(exp_name, camera, telescope)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, PZDOWNLOADEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool pzDownloadExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, PZDOWNLOADEXP_TABLE_NAME);
+}
+
+bool pzDownloadExpInsert(psDB * dbh, const char *exp_name, const char *camera, const char *telescope, const char *state, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, PZDOWNLOADEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long pzDownloadExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, PZDOWNLOADEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDownloadExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pzDownloadExpInsertObject(psDB *dbh, pzDownloadExpRow *object)
+{
+    return pzDownloadExpInsert(dbh, object->exp_name, object->camera, object->telescope, object->state, object->epoch);
+}
+
+bool pzDownloadExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!pzDownloadExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool pzDownloadExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  PZDOWNLOADEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, PZDOWNLOADEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", PZDOWNLOADEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, PZDOWNLOADEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool pzDownloadExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, PZDOWNLOADEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, PZDOWNLOADEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *pzDownloadExpMetadataFromObject(const pzDownloadExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, object->exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+pzDownloadExpRow *pzDownloadExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* exp_name = psMetadataLookupPtr(&status, md, "exp_name");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_name");
+        return false;
+    }
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return pzDownloadExpRowAlloc(exp_name, camera, telescope, state, epoch);
+}
+psArray *pzDownloadExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, PZDOWNLOADEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        pzDownloadExpRow *object = pzDownloadExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool pzDownloadExpDeleteObject(psDB *dbh, const pzDownloadExpRow *object)
+{
+    psMetadata *where = pzDownloadExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, PZDOWNLOADEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDownloadExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "pzDownloadExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long pzDownloadExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        pzDownloadExpRow *object = objects->data[i];
+        psMetadata *where = pzDownloadExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, PZDOWNLOADEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDownloadExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pzDownloadExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = pzDownloadExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            PZDOWNLOADEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool pzDownloadExpPrintObject(FILE *stream, pzDownloadExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = pzDownloadExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void pzDownloadImfileRowFree(pzDownloadImfileRow *object);
+
+pzDownloadImfileRow *pzDownloadImfileRowAlloc(const char *exp_name, const char *camera, const char *telescope, const char *class, const char *class_id, const char *uri, psS16 fault, psTime* epoch)
+{
+    pzDownloadImfileRow *_object;
+
+    _object = psAlloc(sizeof(pzDownloadImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)pzDownloadImfileRowFree);
+
+    _object->exp_name = psStringCopy(exp_name);
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+    _object->class = psStringCopy(class);
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->fault = fault;
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void pzDownloadImfileRowFree(pzDownloadImfileRow *object)
+{
+    psFree(object->exp_name);
+    psFree(object->camera);
+    psFree(object->telescope);
+    psFree(object->class);
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->epoch);
+}
+
+bool pzDownloadImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, "Primary Key fkey(exp_name, camera, telescope) ref pzDownloadExp(exp_name, camera, telescope)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, "Primary Key fkey(exp_name, camera, telescope, class, class_id) ref summitImfile(exp_name, camera, telescope, class, class_id)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, PZDOWNLOADIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool pzDownloadImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, PZDOWNLOADIMFILE_TABLE_NAME);
+}
+
+bool pzDownloadImfileInsert(psDB * dbh, const char *exp_name, const char *camera, const char *telescope, const char *class, const char *class_id, const char *uri, psS16 fault, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, class)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, PZDOWNLOADIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long pzDownloadImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, PZDOWNLOADIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDownloadImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pzDownloadImfileInsertObject(psDB *dbh, pzDownloadImfileRow *object)
+{
+    return pzDownloadImfileInsert(dbh, object->exp_name, object->camera, object->telescope, object->class, object->class_id, object->uri, object->fault, object->epoch);
+}
+
+bool pzDownloadImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!pzDownloadImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool pzDownloadImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  PZDOWNLOADIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, PZDOWNLOADIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", PZDOWNLOADIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, PZDOWNLOADIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool pzDownloadImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, PZDOWNLOADIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, PZDOWNLOADIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *pzDownloadImfileMetadataFromObject(const pzDownloadImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, object->exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, object->class)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+pzDownloadImfileRow *pzDownloadImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* exp_name = psMetadataLookupPtr(&status, md, "exp_name");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_name");
+        return false;
+    }
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+    char* class = psMetadataLookupPtr(&status, md, "class");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return pzDownloadImfileRowAlloc(exp_name, camera, telescope, class, class_id, uri, fault, epoch);
+}
+psArray *pzDownloadImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, PZDOWNLOADIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        pzDownloadImfileRow *object = pzDownloadImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool pzDownloadImfileDeleteObject(psDB *dbh, const pzDownloadImfileRow *object)
+{
+    psMetadata *where = pzDownloadImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, PZDOWNLOADIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDownloadImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "pzDownloadImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long pzDownloadImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        pzDownloadImfileRow *object = objects->data[i];
+        psMetadata *where = pzDownloadImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, PZDOWNLOADIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDownloadImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pzDownloadImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = pzDownloadImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            PZDOWNLOADIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool pzDownloadImfilePrintObject(FILE *stream, pzDownloadImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = pzDownloadImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void newExpRowFree(newExpRow *object);
+
+newExpRow *newExpRowAlloc(psS64 exp_id, const char *tmp_exp_name, const char *tmp_camera, const char *tmp_telescope, const char *state, const char *workdir, const char *workdir_state, const char *reduction, const char *dvodb, const char *tess_id, const char *end_stage, const char *label, psTime* epoch)
+{
+    newExpRow       *_object;
+
+    _object = psAlloc(sizeof(newExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)newExpRowFree);
+
+    _object->exp_id = exp_id;
+    _object->tmp_exp_name = psStringCopy(tmp_exp_name);
+    _object->tmp_camera = psStringCopy(tmp_camera);
+    _object->tmp_telescope = psStringCopy(tmp_telescope);
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->workdir_state = psStringCopy(workdir_state);
+    _object->reduction = psStringCopy(reduction);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->end_stage = psStringCopy(end_stage);
+    _object->label = psStringCopy(label);
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void newExpRowFree(newExpRow *object)
+{
+    psFree(object->tmp_exp_name);
+    psFree(object->tmp_camera);
+    psFree(object->tmp_telescope);
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->workdir_state);
+    psFree(object->reduction);
+    psFree(object->dvodb);
+    psFree(object->tess_id);
+    psFree(object->end_stage);
+    psFree(object->label);
+    psFree(object->epoch);
+}
+
+bool newExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_exp_name", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_camera", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_telescope", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, "destination for output files", "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, "key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, "Reduction class", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, NEWEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool newExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, NEWEXP_TABLE_NAME);
+}
+
+bool newExpInsert(psDB * dbh, psS64 exp_id, const char *tmp_exp_name, const char *tmp_camera, const char *tmp_telescope, const char *state, const char *workdir, const char *workdir_state, const char *reduction, const char *dvodb, const char *tess_id, const char *end_stage, const char *label, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_exp_name", PS_DATA_STRING, NULL, tmp_exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_camera", PS_DATA_STRING, NULL, tmp_camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_telescope", PS_DATA_STRING, NULL, tmp_telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, NULL, workdir_state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, NEWEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long newExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, NEWEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from newExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool newExpInsertObject(psDB *dbh, newExpRow *object)
+{
+    return newExpInsert(dbh, object->exp_id, object->tmp_exp_name, object->tmp_camera, object->tmp_telescope, object->state, object->workdir, object->workdir_state, object->reduction, object->dvodb, object->tess_id, object->end_stage, object->label, object->epoch);
+}
+
+bool newExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!newExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool newExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  NEWEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, NEWEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", NEWEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, NEWEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool newExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, NEWEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, NEWEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *newExpMetadataFromObject(const newExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_exp_name", PS_DATA_STRING, NULL, object->tmp_exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_camera", PS_DATA_STRING, NULL, object->tmp_camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_telescope", PS_DATA_STRING, NULL, object->tmp_telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, NULL, object->workdir_state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, object->reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, object->end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+newExpRow *newExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* tmp_exp_name = psMetadataLookupPtr(&status, md, "tmp_exp_name");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tmp_exp_name");
+        return false;
+    }
+    char* tmp_camera = psMetadataLookupPtr(&status, md, "tmp_camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tmp_camera");
+        return false;
+    }
+    char* tmp_telescope = psMetadataLookupPtr(&status, md, "tmp_telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tmp_telescope");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* workdir_state = psMetadataLookupPtr(&status, md, "workdir_state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir_state");
+        return false;
+    }
+    char* reduction = psMetadataLookupPtr(&status, md, "reduction");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item reduction");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* end_stage = psMetadataLookupPtr(&status, md, "end_stage");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item end_stage");
+        return false;
+    }
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return newExpRowAlloc(exp_id, tmp_exp_name, tmp_camera, tmp_telescope, state, workdir, workdir_state, reduction, dvodb, tess_id, end_stage, label, epoch);
+}
+psArray *newExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, NEWEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        newExpRow *object = newExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool newExpDeleteObject(psDB *dbh, const newExpRow *object)
+{
+    psMetadata *where = newExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, NEWEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from newExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "newExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long newExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        newExpRow *object = objects->data[i];
+        psMetadata *where = newExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, NEWEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from newExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool newExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = newExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            NEWEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool newExpPrintObject(FILE *stream, newExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = newExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void newImfileRowFree(newImfileRow *object);
+
+newImfileRow *newImfileRowAlloc(psS64 exp_id, const char *tmp_class_id, const char *uri, psTime* epoch)
+{
+    newImfileRow    *_object;
+
+    _object = psAlloc(sizeof(newImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)newImfileRowFree);
+
+    _object->exp_id = exp_id;
+    _object->tmp_class_id = psStringCopy(tmp_class_id);
+    _object->uri = psStringCopy(uri);
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void newImfileRowFree(newImfileRow *object)
+{
+    psFree(object->tmp_class_id);
+    psFree(object->uri);
+    psFree(object->epoch);
+}
+
+bool newImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key fkey(exp_id) ref newExp(exp_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, NEWIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool newImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, NEWIMFILE_TABLE_NAME);
+}
+
+bool newImfileInsert(psDB * dbh, psS64 exp_id, const char *tmp_class_id, const char *uri, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_class_id", PS_DATA_STRING, NULL, tmp_class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, NEWIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long newImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, NEWIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from newImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool newImfileInsertObject(psDB *dbh, newImfileRow *object)
+{
+    return newImfileInsert(dbh, object->exp_id, object->tmp_class_id, object->uri, object->epoch);
+}
+
+bool newImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!newImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool newImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  NEWIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, NEWIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", NEWIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, NEWIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool newImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, NEWIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, NEWIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *newImfileMetadataFromObject(const newImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_class_id", PS_DATA_STRING, NULL, object->tmp_class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+newImfileRow *newImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* tmp_class_id = psMetadataLookupPtr(&status, md, "tmp_class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tmp_class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return newImfileRowAlloc(exp_id, tmp_class_id, uri, epoch);
+}
+psArray *newImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, NEWIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        newImfileRow *object = newImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool newImfileDeleteObject(psDB *dbh, const newImfileRow *object)
+{
+    psMetadata *where = newImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, NEWIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from newImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "newImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long newImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        newImfileRow *object = objects->data[i];
+        psMetadata *where = newImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, NEWIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from newImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool newImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = newImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            NEWIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool newImfilePrintObject(FILE *stream, newImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = newImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void rawExpRowFree(rawExpRow *object);
+
+rawExpRow *rawExpRowAlloc(psS64 exp_id, const char *exp_name, const char *camera, const char *telescope, psTime* dateobs, const char *exp_tag, const char *exp_type, const char *filelevel, const char *workdir, const char *reduction, const char *dvodb, const char *tess_id, const char *end_stage, const char *filter, const char *comment, psF32 airmass, psF64 ra, psF64 decl, psF32 exp_time, psF32 sat_pixel_frac, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 alt, psF64 az, psF32 ccd_temp, psF64 posang, psF32 m1_x, psF32 m1_y, psF32 m1_z, psF32 m1_tip, psF32 m1_tilt, psF32 m2_x, psF32 m2_y, psF32 m2_z, psF32 m2_tip, psF32 m2_tilt, psF32 env_temperature, psF32 env_humidity, psF32 env_wind_speed, psF32 env_wind_dir, psF32 teltemp_m1, psF32 teltemp_m1cell, psF32 teltemp_m2, psF32 teltemp_spider, psF32 teltemp_truss, psF32 teltemp_extra, psF32 pon_time, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *object, psF32 solang, const char *hostname, psS16 fault, psTime* epoch)
+{
+    rawExpRow       *_object;
+
+    _object = psAlloc(sizeof(rawExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)rawExpRowFree);
+
+    _object->exp_id = exp_id;
+    _object->exp_name = psStringCopy(exp_name);
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+    _object->dateobs = psTimeCopy(dateobs);
+    _object->exp_tag = psStringCopy(exp_tag);
+    _object->exp_type = psStringCopy(exp_type);
+    _object->filelevel = psStringCopy(filelevel);
+    _object->workdir = psStringCopy(workdir);
+    _object->reduction = psStringCopy(reduction);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->end_stage = psStringCopy(end_stage);
+    _object->filter = psStringCopy(filter);
+    _object->comment = psStringCopy(comment);
+    _object->airmass = airmass;
+    _object->ra = ra;
+    _object->decl = decl;
+    _object->exp_time = exp_time;
+    _object->sat_pixel_frac = sat_pixel_frac;
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->alt = alt;
+    _object->az = az;
+    _object->ccd_temp = ccd_temp;
+    _object->posang = posang;
+    _object->m1_x = m1_x;
+    _object->m1_y = m1_y;
+    _object->m1_z = m1_z;
+    _object->m1_tip = m1_tip;
+    _object->m1_tilt = m1_tilt;
+    _object->m2_x = m2_x;
+    _object->m2_y = m2_y;
+    _object->m2_z = m2_z;
+    _object->m2_tip = m2_tip;
+    _object->m2_tilt = m2_tilt;
+    _object->env_temperature = env_temperature;
+    _object->env_humidity = env_humidity;
+    _object->env_wind_speed = env_wind_speed;
+    _object->env_wind_dir = env_wind_dir;
+    _object->teltemp_m1 = teltemp_m1;
+    _object->teltemp_m1cell = teltemp_m1cell;
+    _object->teltemp_m2 = teltemp_m2;
+    _object->teltemp_spider = teltemp_spider;
+    _object->teltemp_truss = teltemp_truss;
+    _object->teltemp_extra = teltemp_extra;
+    _object->pon_time = pon_time;
+    _object->user_1 = user_1;
+    _object->user_2 = user_2;
+    _object->user_3 = user_3;
+    _object->user_4 = user_4;
+    _object->user_5 = user_5;
+    _object->object = psStringCopy(object);
+    _object->solang = solang;
+    _object->hostname = psStringCopy(hostname);
+    _object->fault = fault;
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void rawExpRowFree(rawExpRow *object)
+{
+    psFree(object->exp_name);
+    psFree(object->camera);
+    psFree(object->telescope);
+    psFree(object->dateobs);
+    psFree(object->exp_tag);
+    psFree(object->exp_type);
+    psFree(object->filelevel);
+    psFree(object->workdir);
+    psFree(object->reduction);
+    psFree(object->dvodb);
+    psFree(object->tess_id);
+    psFree(object->end_stage);
+    psFree(object->filter);
+    psFree(object->comment);
+    psFree(object->object);
+    psFree(object->hostname);
+    psFree(object->epoch);
+}
+
+bool rawExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key fkey(exp_id) ref newExp(exp_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filelevel", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filelevel");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, "destination for output files", "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, "Reduction class", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "comment", PS_DATA_STRING, NULL, "80")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item comment");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ra", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "decl", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item decl");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sat_pixel_frac", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sat_pixel_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "alt", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item alt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "az", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item az");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_x", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_y", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_z", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tip", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tilt", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_x", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_y", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_z", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tip", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tilt", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_temperature", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_temperature");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_humidity", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_humidity");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_speed", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_speed");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_dir", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_dir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1cell", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1cell");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m2", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_spider", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_spider");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_truss", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_truss");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_extra", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_extra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "pon_time", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item pon_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "object", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item object");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "solang", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item solang");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, RAWEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool rawExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, RAWEXP_TABLE_NAME);
+}
+
+bool rawExpInsert(psDB * dbh, psS64 exp_id, const char *exp_name, const char *camera, const char *telescope, psTime* dateobs, const char *exp_tag, const char *exp_type, const char *filelevel, const char *workdir, const char *reduction, const char *dvodb, const char *tess_id, const char *end_stage, const char *filter, const char *comment, psF32 airmass, psF64 ra, psF64 decl, psF32 exp_time, psF32 sat_pixel_frac, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 alt, psF64 az, psF32 ccd_temp, psF64 posang, psF32 m1_x, psF32 m1_y, psF32 m1_z, psF32 m1_tip, psF32 m1_tilt, psF32 m2_x, psF32 m2_y, psF32 m2_z, psF32 m2_tip, psF32 m2_tilt, psF32 env_temperature, psF32 env_humidity, psF32 env_wind_speed, psF32 env_wind_dir, psF32 teltemp_m1, psF32 teltemp_m1cell, psF32 teltemp_m2, psF32 teltemp_spider, psF32 teltemp_truss, psF32 teltemp_extra, psF32 pon_time, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *object, psF32 solang, const char *hostname, psS16 fault, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, dateobs)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, exp_tag)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, exp_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filelevel", PS_DATA_STRING, NULL, filelevel)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filelevel");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, filter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "comment", PS_DATA_STRING, NULL, comment)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item comment");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass", PS_DATA_F32, NULL, airmass)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ra", PS_DATA_F64, NULL, ra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "decl", PS_DATA_F64, NULL, decl)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item decl");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time", PS_DATA_F32, NULL, exp_time)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sat_pixel_frac", PS_DATA_F32, NULL, sat_pixel_frac)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sat_pixel_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "alt", PS_DATA_F64, NULL, alt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item alt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "az", PS_DATA_F64, NULL, az)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item az");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp", PS_DATA_F32, NULL, ccd_temp)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang", PS_DATA_F64, NULL, posang)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_x", PS_DATA_F32, NULL, m1_x)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_y", PS_DATA_F32, NULL, m1_y)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_z", PS_DATA_F32, NULL, m1_z)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tip", PS_DATA_F32, NULL, m1_tip)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tilt", PS_DATA_F32, NULL, m1_tilt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_x", PS_DATA_F32, NULL, m2_x)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_y", PS_DATA_F32, NULL, m2_y)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_z", PS_DATA_F32, NULL, m2_z)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tip", PS_DATA_F32, NULL, m2_tip)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tilt", PS_DATA_F32, NULL, m2_tilt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_temperature", PS_DATA_F32, NULL, env_temperature)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_temperature");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_humidity", PS_DATA_F32, NULL, env_humidity)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_humidity");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_speed", PS_DATA_F32, NULL, env_wind_speed)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_speed");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_dir", PS_DATA_F32, NULL, env_wind_dir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_dir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1", PS_DATA_F32, NULL, teltemp_m1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1cell", PS_DATA_F32, NULL, teltemp_m1cell)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1cell");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m2", PS_DATA_F32, NULL, teltemp_m2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_spider", PS_DATA_F32, NULL, teltemp_spider)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_spider");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_truss", PS_DATA_F32, NULL, teltemp_truss)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_truss");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_extra", PS_DATA_F32, NULL, teltemp_extra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_extra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "pon_time", PS_DATA_F32, NULL, pon_time)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item pon_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "object", PS_DATA_STRING, NULL, object)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item object");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "solang", PS_DATA_F32, NULL, solang)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item solang");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, RAWEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long rawExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, RAWEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from rawExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool rawExpInsertObject(psDB *dbh, rawExpRow *object)
+{
+    return rawExpInsert(dbh, object->exp_id, object->exp_name, object->camera, object->telescope, object->dateobs, object->exp_tag, object->exp_type, object->filelevel, object->workdir, object->reduction, object->dvodb, object->tess_id, object->end_stage, object->filter, object->comment, object->airmass, object->ra, object->decl, object->exp_time, object->sat_pixel_frac, object->bg, object->bg_stdev, object->bg_mean_stdev, object->alt, object->az, object->ccd_temp, object->posang, object->m1_x, object->m1_y, object->m1_z, object->m1_tip, object->m1_tilt, object->m2_x, object->m2_y, object->m2_z, object->m2_tip, object->m2_tilt, object->env_temperature, object->env_humidity, object->env_wind_speed, object->env_wind_dir, object->teltemp_m1, object->teltemp_m1cell, object->teltemp_m2, object->teltemp_spider, object->teltemp_truss, object->teltemp_extra, object->pon_time, object->user_1, object->user_2, object->user_3, object->user_4, object->user_5, object->object, object->solang, object->hostname, object->fault, object->epoch);
+}
+
+bool rawExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!rawExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool rawExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  RAWEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, RAWEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", RAWEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, RAWEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool rawExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, RAWEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, RAWEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *rawExpMetadataFromObject(const rawExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, object->exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, object->dateobs)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, object->exp_tag)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, object->exp_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filelevel", PS_DATA_STRING, NULL, object->filelevel)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filelevel");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, object->reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, object->end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, object->filter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "comment", PS_DATA_STRING, NULL, object->comment)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item comment");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass", PS_DATA_F32, NULL, object->airmass)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ra", PS_DATA_F64, NULL, object->ra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "decl", PS_DATA_F64, NULL, object->decl)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item decl");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time", PS_DATA_F32, NULL, object->exp_time)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sat_pixel_frac", PS_DATA_F32, NULL, object->sat_pixel_frac)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sat_pixel_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "alt", PS_DATA_F64, NULL, object->alt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item alt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "az", PS_DATA_F64, NULL, object->az)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item az");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp", PS_DATA_F32, NULL, object->ccd_temp)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang", PS_DATA_F64, NULL, object->posang)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_x", PS_DATA_F32, NULL, object->m1_x)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_y", PS_DATA_F32, NULL, object->m1_y)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_z", PS_DATA_F32, NULL, object->m1_z)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tip", PS_DATA_F32, NULL, object->m1_tip)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tilt", PS_DATA_F32, NULL, object->m1_tilt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_x", PS_DATA_F32, NULL, object->m2_x)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_y", PS_DATA_F32, NULL, object->m2_y)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_z", PS_DATA_F32, NULL, object->m2_z)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tip", PS_DATA_F32, NULL, object->m2_tip)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tilt", PS_DATA_F32, NULL, object->m2_tilt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_temperature", PS_DATA_F32, NULL, object->env_temperature)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_temperature");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_humidity", PS_DATA_F32, NULL, object->env_humidity)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_humidity");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_speed", PS_DATA_F32, NULL, object->env_wind_speed)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_speed");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_dir", PS_DATA_F32, NULL, object->env_wind_dir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_dir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1", PS_DATA_F32, NULL, object->teltemp_m1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1cell", PS_DATA_F32, NULL, object->teltemp_m1cell)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1cell");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m2", PS_DATA_F32, NULL, object->teltemp_m2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_spider", PS_DATA_F32, NULL, object->teltemp_spider)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_spider");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_truss", PS_DATA_F32, NULL, object->teltemp_truss)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_truss");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_extra", PS_DATA_F32, NULL, object->teltemp_extra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_extra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "pon_time", PS_DATA_F32, NULL, object->pon_time)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item pon_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, object->user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, object->user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, object->user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, object->user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, object->user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "object", PS_DATA_STRING, NULL, object->object)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item object");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "solang", PS_DATA_F32, NULL, object->solang)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item solang");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, object->hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+rawExpRow *rawExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* exp_name = psMetadataLookupPtr(&status, md, "exp_name");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_name");
+        return false;
+    }
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+    psTime* dateobs = psMetadataLookupPtr(&status, md, "dateobs");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dateobs");
+        return false;
+    }
+    char* exp_tag = psMetadataLookupPtr(&status, md, "exp_tag");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_tag");
+        return false;
+    }
+    char* exp_type = psMetadataLookupPtr(&status, md, "exp_type");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_type");
+        return false;
+    }
+    char* filelevel = psMetadataLookupPtr(&status, md, "filelevel");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item filelevel");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* reduction = psMetadataLookupPtr(&status, md, "reduction");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item reduction");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* end_stage = psMetadataLookupPtr(&status, md, "end_stage");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item end_stage");
+        return false;
+    }
+    char* filter = psMetadataLookupPtr(&status, md, "filter");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item filter");
+        return false;
+    }
+    char* comment = psMetadataLookupPtr(&status, md, "comment");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item comment");
+        return false;
+    }
+    psF32 airmass = psMetadataLookupF32(&status, md, "airmass");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item airmass");
+        return false;
+    }
+    psF64 ra = psMetadataLookupF64(&status, md, "ra");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ra");
+        return false;
+    }
+    psF64 decl = psMetadataLookupF64(&status, md, "decl");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item decl");
+        return false;
+    }
+    psF32 exp_time = psMetadataLookupF32(&status, md, "exp_time");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_time");
+        return false;
+    }
+    psF32 sat_pixel_frac = psMetadataLookupF32(&status, md, "sat_pixel_frac");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item sat_pixel_frac");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF64 alt = psMetadataLookupF64(&status, md, "alt");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item alt");
+        return false;
+    }
+    psF64 az = psMetadataLookupF64(&status, md, "az");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item az");
+        return false;
+    }
+    psF32 ccd_temp = psMetadataLookupF32(&status, md, "ccd_temp");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ccd_temp");
+        return false;
+    }
+    psF64 posang = psMetadataLookupF64(&status, md, "posang");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item posang");
+        return false;
+    }
+    psF32 m1_x = psMetadataLookupF32(&status, md, "m1_x");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m1_x");
+        return false;
+    }
+    psF32 m1_y = psMetadataLookupF32(&status, md, "m1_y");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m1_y");
+        return false;
+    }
+    psF32 m1_z = psMetadataLookupF32(&status, md, "m1_z");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m1_z");
+        return false;
+    }
+    psF32 m1_tip = psMetadataLookupF32(&status, md, "m1_tip");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m1_tip");
+        return false;
+    }
+    psF32 m1_tilt = psMetadataLookupF32(&status, md, "m1_tilt");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m1_tilt");
+        return false;
+    }
+    psF32 m2_x = psMetadataLookupF32(&status, md, "m2_x");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m2_x");
+        return false;
+    }
+    psF32 m2_y = psMetadataLookupF32(&status, md, "m2_y");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m2_y");
+        return false;
+    }
+    psF32 m2_z = psMetadataLookupF32(&status, md, "m2_z");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m2_z");
+        return false;
+    }
+    psF32 m2_tip = psMetadataLookupF32(&status, md, "m2_tip");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m2_tip");
+        return false;
+    }
+    psF32 m2_tilt = psMetadataLookupF32(&status, md, "m2_tilt");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m2_tilt");
+        return false;
+    }
+    psF32 env_temperature = psMetadataLookupF32(&status, md, "env_temperature");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item env_temperature");
+        return false;
+    }
+    psF32 env_humidity = psMetadataLookupF32(&status, md, "env_humidity");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item env_humidity");
+        return false;
+    }
+    psF32 env_wind_speed = psMetadataLookupF32(&status, md, "env_wind_speed");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item env_wind_speed");
+        return false;
+    }
+    psF32 env_wind_dir = psMetadataLookupF32(&status, md, "env_wind_dir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item env_wind_dir");
+        return false;
+    }
+    psF32 teltemp_m1 = psMetadataLookupF32(&status, md, "teltemp_m1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_m1");
+        return false;
+    }
+    psF32 teltemp_m1cell = psMetadataLookupF32(&status, md, "teltemp_m1cell");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_m1cell");
+        return false;
+    }
+    psF32 teltemp_m2 = psMetadataLookupF32(&status, md, "teltemp_m2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_m2");
+        return false;
+    }
+    psF32 teltemp_spider = psMetadataLookupF32(&status, md, "teltemp_spider");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_spider");
+        return false;
+    }
+    psF32 teltemp_truss = psMetadataLookupF32(&status, md, "teltemp_truss");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_truss");
+        return false;
+    }
+    psF32 teltemp_extra = psMetadataLookupF32(&status, md, "teltemp_extra");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_extra");
+        return false;
+    }
+    psF32 pon_time = psMetadataLookupF32(&status, md, "pon_time");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item pon_time");
+        return false;
+    }
+    psF64 user_1 = psMetadataLookupF64(&status, md, "user_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_1");
+        return false;
+    }
+    psF64 user_2 = psMetadataLookupF64(&status, md, "user_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_2");
+        return false;
+    }
+    psF64 user_3 = psMetadataLookupF64(&status, md, "user_3");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_3");
+        return false;
+    }
+    psF64 user_4 = psMetadataLookupF64(&status, md, "user_4");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_4");
+        return false;
+    }
+    psF64 user_5 = psMetadataLookupF64(&status, md, "user_5");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_5");
+        return false;
+    }
+    char* object = psMetadataLookupPtr(&status, md, "object");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item object");
+        return false;
+    }
+    psF32 solang = psMetadataLookupF32(&status, md, "solang");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item solang");
+        return false;
+    }
+    char* hostname = psMetadataLookupPtr(&status, md, "hostname");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item hostname");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return rawExpRowAlloc(exp_id, exp_name, camera, telescope, dateobs, exp_tag, exp_type, filelevel, workdir, reduction, dvodb, tess_id, end_stage, filter, comment, airmass, ra, decl, exp_time, sat_pixel_frac, bg, bg_stdev, bg_mean_stdev, alt, az, ccd_temp, posang, m1_x, m1_y, m1_z, m1_tip, m1_tilt, m2_x, m2_y, m2_z, m2_tip, m2_tilt, env_temperature, env_humidity, env_wind_speed, env_wind_dir, teltemp_m1, teltemp_m1cell, teltemp_m2, teltemp_spider, teltemp_truss, teltemp_extra, pon_time, user_1, user_2, user_3, user_4, user_5, object, solang, hostname, fault, epoch);
+}
+psArray *rawExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, RAWEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        rawExpRow *object = rawExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool rawExpDeleteObject(psDB *dbh, const rawExpRow *object)
+{
+    psMetadata *where = rawExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, RAWEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from rawExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "rawExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long rawExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        rawExpRow *object = objects->data[i];
+        psMetadata *where = rawExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, RAWEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from rawExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool rawExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = rawExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            RAWEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool rawExpPrintObject(FILE *stream, rawExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = rawExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void rawImfileRowFree(rawImfileRow *object);
+
+rawImfileRow *rawImfileRowAlloc(psS64 exp_id, const char *exp_name, const char *camera, const char *telescope, psTime* dateobs, const char *tmp_class_id, const char *class_id, const char *uri, const char *exp_type, const char *filelevel, const char *filter, const char *comment, psF32 airmass, psF64 ra, psF64 decl, psF32 exp_time, psF32 sat_pixel_frac, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 alt, psF64 az, psF32 ccd_temp, psF64 posang, psF32 m1_x, psF32 m1_y, psF32 m1_z, psF32 m1_tip, psF32 m1_tilt, psF32 m2_x, psF32 m2_y, psF32 m2_z, psF32 m2_tip, psF32 m2_tilt, psF32 env_temperature, psF32 env_humidity, psF32 env_wind_speed, psF32 env_wind_dir, psF32 teltemp_m1, psF32 teltemp_m1cell, psF32 teltemp_m2, psF32 teltemp_spider, psF32 teltemp_truss, psF32 teltemp_extra, psF32 pon_time, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *object, const char *hostname, psS16 fault, psTime* epoch)
+{
+    rawImfileRow    *_object;
+
+    _object = psAlloc(sizeof(rawImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)rawImfileRowFree);
+
+    _object->exp_id = exp_id;
+    _object->exp_name = psStringCopy(exp_name);
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+    _object->dateobs = psTimeCopy(dateobs);
+    _object->tmp_class_id = psStringCopy(tmp_class_id);
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->exp_type = psStringCopy(exp_type);
+    _object->filelevel = psStringCopy(filelevel);
+    _object->filter = psStringCopy(filter);
+    _object->comment = psStringCopy(comment);
+    _object->airmass = airmass;
+    _object->ra = ra;
+    _object->decl = decl;
+    _object->exp_time = exp_time;
+    _object->sat_pixel_frac = sat_pixel_frac;
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->alt = alt;
+    _object->az = az;
+    _object->ccd_temp = ccd_temp;
+    _object->posang = posang;
+    _object->m1_x = m1_x;
+    _object->m1_y = m1_y;
+    _object->m1_z = m1_z;
+    _object->m1_tip = m1_tip;
+    _object->m1_tilt = m1_tilt;
+    _object->m2_x = m2_x;
+    _object->m2_y = m2_y;
+    _object->m2_z = m2_z;
+    _object->m2_tip = m2_tip;
+    _object->m2_tilt = m2_tilt;
+    _object->env_temperature = env_temperature;
+    _object->env_humidity = env_humidity;
+    _object->env_wind_speed = env_wind_speed;
+    _object->env_wind_dir = env_wind_dir;
+    _object->teltemp_m1 = teltemp_m1;
+    _object->teltemp_m1cell = teltemp_m1cell;
+    _object->teltemp_m2 = teltemp_m2;
+    _object->teltemp_spider = teltemp_spider;
+    _object->teltemp_truss = teltemp_truss;
+    _object->teltemp_extra = teltemp_extra;
+    _object->pon_time = pon_time;
+    _object->user_1 = user_1;
+    _object->user_2 = user_2;
+    _object->user_3 = user_3;
+    _object->user_4 = user_4;
+    _object->user_5 = user_5;
+    _object->object = psStringCopy(object);
+    _object->hostname = psStringCopy(hostname);
+    _object->fault = fault;
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void rawImfileRowFree(rawImfileRow *object)
+{
+    psFree(object->exp_name);
+    psFree(object->camera);
+    psFree(object->telescope);
+    psFree(object->dateobs);
+    psFree(object->tmp_class_id);
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->exp_type);
+    psFree(object->filelevel);
+    psFree(object->filter);
+    psFree(object->comment);
+    psFree(object->object);
+    psFree(object->hostname);
+    psFree(object->epoch);
+}
+
+bool rawImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key fkey(exp_id, tmp_class_id) ref newImfile(exp_id, tmp_class_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, "UINDEX(exp_id, tmp_class_id)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_class_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filelevel", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filelevel");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "comment", PS_DATA_STRING, NULL, "80")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item comment");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ra", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "decl", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item decl");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sat_pixel_frac", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sat_pixel_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "alt", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item alt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "az", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item az");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_x", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_y", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_z", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tip", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tilt", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_x", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_y", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_z", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tip", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tilt", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_temperature", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_temperature");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_humidity", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_humidity");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_speed", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_speed");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_dir", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_dir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1cell", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1cell");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m2", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_spider", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_spider");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_truss", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_truss");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_extra", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_extra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "pon_time", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item pon_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "object", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item object");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, RAWIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool rawImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, RAWIMFILE_TABLE_NAME);
+}
+
+bool rawImfileInsert(psDB * dbh, psS64 exp_id, const char *exp_name, const char *camera, const char *telescope, psTime* dateobs, const char *tmp_class_id, const char *class_id, const char *uri, const char *exp_type, const char *filelevel, const char *filter, const char *comment, psF32 airmass, psF64 ra, psF64 decl, psF32 exp_time, psF32 sat_pixel_frac, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 alt, psF64 az, psF32 ccd_temp, psF64 posang, psF32 m1_x, psF32 m1_y, psF32 m1_z, psF32 m1_tip, psF32 m1_tilt, psF32 m2_x, psF32 m2_y, psF32 m2_z, psF32 m2_tip, psF32 m2_tilt, psF32 env_temperature, psF32 env_humidity, psF32 env_wind_speed, psF32 env_wind_dir, psF32 teltemp_m1, psF32 teltemp_m1cell, psF32 teltemp_m2, psF32 teltemp_spider, psF32 teltemp_truss, psF32 teltemp_extra, psF32 pon_time, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *object, const char *hostname, psS16 fault, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, dateobs)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_class_id", PS_DATA_STRING, NULL, tmp_class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, exp_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filelevel", PS_DATA_STRING, NULL, filelevel)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filelevel");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, filter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "comment", PS_DATA_STRING, NULL, comment)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item comment");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass", PS_DATA_F32, NULL, airmass)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ra", PS_DATA_F64, NULL, ra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "decl", PS_DATA_F64, NULL, decl)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item decl");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time", PS_DATA_F32, NULL, exp_time)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sat_pixel_frac", PS_DATA_F32, NULL, sat_pixel_frac)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sat_pixel_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "alt", PS_DATA_F64, NULL, alt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item alt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "az", PS_DATA_F64, NULL, az)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item az");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp", PS_DATA_F32, NULL, ccd_temp)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang", PS_DATA_F64, NULL, posang)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_x", PS_DATA_F32, NULL, m1_x)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_y", PS_DATA_F32, NULL, m1_y)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_z", PS_DATA_F32, NULL, m1_z)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tip", PS_DATA_F32, NULL, m1_tip)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tilt", PS_DATA_F32, NULL, m1_tilt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_x", PS_DATA_F32, NULL, m2_x)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_y", PS_DATA_F32, NULL, m2_y)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_z", PS_DATA_F32, NULL, m2_z)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tip", PS_DATA_F32, NULL, m2_tip)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tilt", PS_DATA_F32, NULL, m2_tilt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_temperature", PS_DATA_F32, NULL, env_temperature)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_temperature");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_humidity", PS_DATA_F32, NULL, env_humidity)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_humidity");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_speed", PS_DATA_F32, NULL, env_wind_speed)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_speed");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_dir", PS_DATA_F32, NULL, env_wind_dir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_dir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1", PS_DATA_F32, NULL, teltemp_m1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1cell", PS_DATA_F32, NULL, teltemp_m1cell)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1cell");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m2", PS_DATA_F32, NULL, teltemp_m2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_spider", PS_DATA_F32, NULL, teltemp_spider)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_spider");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_truss", PS_DATA_F32, NULL, teltemp_truss)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_truss");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_extra", PS_DATA_F32, NULL, teltemp_extra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_extra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "pon_time", PS_DATA_F32, NULL, pon_time)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item pon_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "object", PS_DATA_STRING, NULL, object)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item object");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, RAWIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long rawImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, RAWIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from rawImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool rawImfileInsertObject(psDB *dbh, rawImfileRow *object)
+{
+    return rawImfileInsert(dbh, object->exp_id, object->exp_name, object->camera, object->telescope, object->dateobs, object->tmp_class_id, object->class_id, object->uri, object->exp_type, object->filelevel, object->filter, object->comment, object->airmass, object->ra, object->decl, object->exp_time, object->sat_pixel_frac, object->bg, object->bg_stdev, object->bg_mean_stdev, object->alt, object->az, object->ccd_temp, object->posang, object->m1_x, object->m1_y, object->m1_z, object->m1_tip, object->m1_tilt, object->m2_x, object->m2_y, object->m2_z, object->m2_tip, object->m2_tilt, object->env_temperature, object->env_humidity, object->env_wind_speed, object->env_wind_dir, object->teltemp_m1, object->teltemp_m1cell, object->teltemp_m2, object->teltemp_spider, object->teltemp_truss, object->teltemp_extra, object->pon_time, object->user_1, object->user_2, object->user_3, object->user_4, object->user_5, object->object, object->hostname, object->fault, object->epoch);
+}
+
+bool rawImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!rawImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool rawImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  RAWIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, RAWIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", RAWIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, RAWIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool rawImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, RAWIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, RAWIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *rawImfileMetadataFromObject(const rawImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_name", PS_DATA_STRING, NULL, object->exp_name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, object->dateobs)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tmp_class_id", PS_DATA_STRING, NULL, object->tmp_class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tmp_class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, object->exp_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filelevel", PS_DATA_STRING, NULL, object->filelevel)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filelevel");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, object->filter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "comment", PS_DATA_STRING, NULL, object->comment)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item comment");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass", PS_DATA_F32, NULL, object->airmass)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ra", PS_DATA_F64, NULL, object->ra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "decl", PS_DATA_F64, NULL, object->decl)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item decl");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time", PS_DATA_F32, NULL, object->exp_time)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sat_pixel_frac", PS_DATA_F32, NULL, object->sat_pixel_frac)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sat_pixel_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "alt", PS_DATA_F64, NULL, object->alt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item alt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "az", PS_DATA_F64, NULL, object->az)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item az");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp", PS_DATA_F32, NULL, object->ccd_temp)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang", PS_DATA_F64, NULL, object->posang)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_x", PS_DATA_F32, NULL, object->m1_x)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_y", PS_DATA_F32, NULL, object->m1_y)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_z", PS_DATA_F32, NULL, object->m1_z)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tip", PS_DATA_F32, NULL, object->m1_tip)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m1_tilt", PS_DATA_F32, NULL, object->m1_tilt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m1_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_x", PS_DATA_F32, NULL, object->m2_x)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_x");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_y", PS_DATA_F32, NULL, object->m2_y)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_y");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_z", PS_DATA_F32, NULL, object->m2_z)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_z");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tip", PS_DATA_F32, NULL, object->m2_tip)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tip");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "m2_tilt", PS_DATA_F32, NULL, object->m2_tilt)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item m2_tilt");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_temperature", PS_DATA_F32, NULL, object->env_temperature)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_temperature");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_humidity", PS_DATA_F32, NULL, object->env_humidity)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_humidity");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_speed", PS_DATA_F32, NULL, object->env_wind_speed)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_speed");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "env_wind_dir", PS_DATA_F32, NULL, object->env_wind_dir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item env_wind_dir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1", PS_DATA_F32, NULL, object->teltemp_m1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m1cell", PS_DATA_F32, NULL, object->teltemp_m1cell)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m1cell");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_m2", PS_DATA_F32, NULL, object->teltemp_m2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_m2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_spider", PS_DATA_F32, NULL, object->teltemp_spider)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_spider");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_truss", PS_DATA_F32, NULL, object->teltemp_truss)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_truss");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "teltemp_extra", PS_DATA_F32, NULL, object->teltemp_extra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item teltemp_extra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "pon_time", PS_DATA_F32, NULL, object->pon_time)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item pon_time");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, object->user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, object->user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, object->user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, object->user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, object->user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "object", PS_DATA_STRING, NULL, object->object)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item object");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, object->hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+rawImfileRow *rawImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* exp_name = psMetadataLookupPtr(&status, md, "exp_name");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_name");
+        return false;
+    }
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+    psTime* dateobs = psMetadataLookupPtr(&status, md, "dateobs");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dateobs");
+        return false;
+    }
+    char* tmp_class_id = psMetadataLookupPtr(&status, md, "tmp_class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tmp_class_id");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    char* exp_type = psMetadataLookupPtr(&status, md, "exp_type");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_type");
+        return false;
+    }
+    char* filelevel = psMetadataLookupPtr(&status, md, "filelevel");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item filelevel");
+        return false;
+    }
+    char* filter = psMetadataLookupPtr(&status, md, "filter");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item filter");
+        return false;
+    }
+    char* comment = psMetadataLookupPtr(&status, md, "comment");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item comment");
+        return false;
+    }
+    psF32 airmass = psMetadataLookupF32(&status, md, "airmass");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item airmass");
+        return false;
+    }
+    psF64 ra = psMetadataLookupF64(&status, md, "ra");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ra");
+        return false;
+    }
+    psF64 decl = psMetadataLookupF64(&status, md, "decl");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item decl");
+        return false;
+    }
+    psF32 exp_time = psMetadataLookupF32(&status, md, "exp_time");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_time");
+        return false;
+    }
+    psF32 sat_pixel_frac = psMetadataLookupF32(&status, md, "sat_pixel_frac");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item sat_pixel_frac");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF64 alt = psMetadataLookupF64(&status, md, "alt");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item alt");
+        return false;
+    }
+    psF64 az = psMetadataLookupF64(&status, md, "az");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item az");
+        return false;
+    }
+    psF32 ccd_temp = psMetadataLookupF32(&status, md, "ccd_temp");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ccd_temp");
+        return false;
+    }
+    psF64 posang = psMetadataLookupF64(&status, md, "posang");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item posang");
+        return false;
+    }
+    psF32 m1_x = psMetadataLookupF32(&status, md, "m1_x");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m1_x");
+        return false;
+    }
+    psF32 m1_y = psMetadataLookupF32(&status, md, "m1_y");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m1_y");
+        return false;
+    }
+    psF32 m1_z = psMetadataLookupF32(&status, md, "m1_z");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m1_z");
+        return false;
+    }
+    psF32 m1_tip = psMetadataLookupF32(&status, md, "m1_tip");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m1_tip");
+        return false;
+    }
+    psF32 m1_tilt = psMetadataLookupF32(&status, md, "m1_tilt");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m1_tilt");
+        return false;
+    }
+    psF32 m2_x = psMetadataLookupF32(&status, md, "m2_x");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m2_x");
+        return false;
+    }
+    psF32 m2_y = psMetadataLookupF32(&status, md, "m2_y");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m2_y");
+        return false;
+    }
+    psF32 m2_z = psMetadataLookupF32(&status, md, "m2_z");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m2_z");
+        return false;
+    }
+    psF32 m2_tip = psMetadataLookupF32(&status, md, "m2_tip");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m2_tip");
+        return false;
+    }
+    psF32 m2_tilt = psMetadataLookupF32(&status, md, "m2_tilt");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item m2_tilt");
+        return false;
+    }
+    psF32 env_temperature = psMetadataLookupF32(&status, md, "env_temperature");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item env_temperature");
+        return false;
+    }
+    psF32 env_humidity = psMetadataLookupF32(&status, md, "env_humidity");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item env_humidity");
+        return false;
+    }
+    psF32 env_wind_speed = psMetadataLookupF32(&status, md, "env_wind_speed");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item env_wind_speed");
+        return false;
+    }
+    psF32 env_wind_dir = psMetadataLookupF32(&status, md, "env_wind_dir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item env_wind_dir");
+        return false;
+    }
+    psF32 teltemp_m1 = psMetadataLookupF32(&status, md, "teltemp_m1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_m1");
+        return false;
+    }
+    psF32 teltemp_m1cell = psMetadataLookupF32(&status, md, "teltemp_m1cell");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_m1cell");
+        return false;
+    }
+    psF32 teltemp_m2 = psMetadataLookupF32(&status, md, "teltemp_m2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_m2");
+        return false;
+    }
+    psF32 teltemp_spider = psMetadataLookupF32(&status, md, "teltemp_spider");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_spider");
+        return false;
+    }
+    psF32 teltemp_truss = psMetadataLookupF32(&status, md, "teltemp_truss");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_truss");
+        return false;
+    }
+    psF32 teltemp_extra = psMetadataLookupF32(&status, md, "teltemp_extra");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item teltemp_extra");
+        return false;
+    }
+    psF32 pon_time = psMetadataLookupF32(&status, md, "pon_time");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item pon_time");
+        return false;
+    }
+    psF64 user_1 = psMetadataLookupF64(&status, md, "user_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_1");
+        return false;
+    }
+    psF64 user_2 = psMetadataLookupF64(&status, md, "user_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_2");
+        return false;
+    }
+    psF64 user_3 = psMetadataLookupF64(&status, md, "user_3");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_3");
+        return false;
+    }
+    psF64 user_4 = psMetadataLookupF64(&status, md, "user_4");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_4");
+        return false;
+    }
+    psF64 user_5 = psMetadataLookupF64(&status, md, "user_5");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_5");
+        return false;
+    }
+    char* object = psMetadataLookupPtr(&status, md, "object");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item object");
+        return false;
+    }
+    char* hostname = psMetadataLookupPtr(&status, md, "hostname");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item hostname");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return rawImfileRowAlloc(exp_id, exp_name, camera, telescope, dateobs, tmp_class_id, class_id, uri, exp_type, filelevel, filter, comment, airmass, ra, decl, exp_time, sat_pixel_frac, bg, bg_stdev, bg_mean_stdev, alt, az, ccd_temp, posang, m1_x, m1_y, m1_z, m1_tip, m1_tilt, m2_x, m2_y, m2_z, m2_tip, m2_tilt, env_temperature, env_humidity, env_wind_speed, env_wind_dir, teltemp_m1, teltemp_m1cell, teltemp_m2, teltemp_spider, teltemp_truss, teltemp_extra, pon_time, user_1, user_2, user_3, user_4, user_5, object, hostname, fault, epoch);
+}
+psArray *rawImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, RAWIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        rawImfileRow *object = rawImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool rawImfileDeleteObject(psDB *dbh, const rawImfileRow *object)
+{
+    psMetadata *where = rawImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, RAWIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from rawImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "rawImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long rawImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        rawImfileRow *object = objects->data[i];
+        psMetadata *where = rawImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, RAWIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from rawImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool rawImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = rawImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            RAWIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool rawImfilePrintObject(FILE *stream, rawImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = rawImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void guidePendingExpRowFree(guidePendingExpRow *object);
+
+guidePendingExpRow *guidePendingExpRowAlloc(psS64 guide_id, psS64 exp_id, const char *recipe)
+{
+    guidePendingExpRow *_object;
+
+    _object = psAlloc(sizeof(guidePendingExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)guidePendingExpRowFree);
+
+    _object->guide_id = guide_id;
+    _object->exp_id = exp_id;
+    _object->recipe = psStringCopy(recipe);
+
+    return _object;
+}
+
+static void guidePendingExpRowFree(guidePendingExpRow *object)
+{
+    psFree(object->recipe);
+}
+
+bool guidePendingExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "guide_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item guide_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Key", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, GUIDEPENDINGEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool guidePendingExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, GUIDEPENDINGEXP_TABLE_NAME);
+}
+
+bool guidePendingExpInsert(psDB * dbh, psS64 guide_id, psS64 exp_id, const char *recipe)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "guide_id", PS_DATA_S64, NULL, guide_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item guide_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, GUIDEPENDINGEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long guidePendingExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, GUIDEPENDINGEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from guidePendingExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool guidePendingExpInsertObject(psDB *dbh, guidePendingExpRow *object)
+{
+    return guidePendingExpInsert(dbh, object->guide_id, object->exp_id, object->recipe);
+}
+
+bool guidePendingExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!guidePendingExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool guidePendingExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  GUIDEPENDINGEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, GUIDEPENDINGEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", GUIDEPENDINGEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, GUIDEPENDINGEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool guidePendingExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, GUIDEPENDINGEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, GUIDEPENDINGEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *guidePendingExpMetadataFromObject(const guidePendingExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "guide_id", PS_DATA_S64, NULL, object->guide_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item guide_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, object->recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+guidePendingExpRow *guidePendingExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 guide_id = psMetadataLookupS64(&status, md, "guide_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item guide_id");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* recipe = psMetadataLookupPtr(&status, md, "recipe");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item recipe");
+        return false;
+    }
+
+    return guidePendingExpRowAlloc(guide_id, exp_id, recipe);
+}
+psArray *guidePendingExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, GUIDEPENDINGEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        guidePendingExpRow *object = guidePendingExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool guidePendingExpDeleteObject(psDB *dbh, const guidePendingExpRow *object)
+{
+    psMetadata *where = guidePendingExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, GUIDEPENDINGEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from guidePendingExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "guidePendingExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long guidePendingExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        guidePendingExpRow *object = objects->data[i];
+        psMetadata *where = guidePendingExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, GUIDEPENDINGEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from guidePendingExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool guidePendingExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = guidePendingExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            GUIDEPENDINGEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool guidePendingExpPrintObject(FILE *stream, guidePendingExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = guidePendingExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void chipRunRowFree(chipRunRow *object);
+
+chipRunRow *chipRunRowAlloc(psS64 chip_id, psS64 exp_id, const char *state, const char *workdir, const char *workdir_state, const char *label, const char *reduction, const char *expgroup, const char *dvodb, const char *tess_id, const char *end_stage)
+{
+    chipRunRow      *_object;
+
+    _object = psAlloc(sizeof(chipRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)chipRunRowFree);
+
+    _object->chip_id = chip_id;
+    _object->exp_id = exp_id;
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->workdir_state = psStringCopy(workdir_state);
+    _object->label = psStringCopy(label);
+    _object->reduction = psStringCopy(reduction);
+    _object->expgroup = psStringCopy(expgroup);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->end_stage = psStringCopy(end_stage);
+
+    return _object;
+}
+
+static void chipRunRowFree(chipRunRow *object)
+{
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->workdir_state);
+    psFree(object->label);
+    psFree(object->reduction);
+    psFree(object->expgroup);
+    psFree(object->dvodb);
+    psFree(object->tess_id);
+    psFree(object->end_stage);
+}
+
+bool chipRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Key INDEX(chip_id, exp_id) fkey (exp_id) ref rawExp(exp_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, "Reduction class", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "expgroup", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item expgroup");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, CHIPRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool chipRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, CHIPRUN_TABLE_NAME);
+}
+
+bool chipRunInsert(psDB * dbh, psS64 chip_id, psS64 exp_id, const char *state, const char *workdir, const char *workdir_state, const char *label, const char *reduction, const char *expgroup, const char *dvodb, const char *tess_id, const char *end_stage)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, NULL, chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, NULL, workdir_state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "expgroup", PS_DATA_STRING, NULL, expgroup)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item expgroup");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, CHIPRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long chipRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, CHIPRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from chipRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool chipRunInsertObject(psDB *dbh, chipRunRow *object)
+{
+    return chipRunInsert(dbh, object->chip_id, object->exp_id, object->state, object->workdir, object->workdir_state, object->label, object->reduction, object->expgroup, object->dvodb, object->tess_id, object->end_stage);
+}
+
+bool chipRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!chipRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool chipRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  CHIPRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, CHIPRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", CHIPRUN_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, CHIPRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool chipRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, CHIPRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, CHIPRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *chipRunMetadataFromObject(const chipRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, NULL, object->chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, NULL, object->workdir_state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, object->reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "expgroup", PS_DATA_STRING, NULL, object->expgroup)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item expgroup");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, object->end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+chipRunRow *chipRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 chip_id = psMetadataLookupS64(&status, md, "chip_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item chip_id");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* workdir_state = psMetadataLookupPtr(&status, md, "workdir_state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir_state");
+        return false;
+    }
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+    char* reduction = psMetadataLookupPtr(&status, md, "reduction");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item reduction");
+        return false;
+    }
+    char* expgroup = psMetadataLookupPtr(&status, md, "expgroup");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item expgroup");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* end_stage = psMetadataLookupPtr(&status, md, "end_stage");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item end_stage");
+        return false;
+    }
+
+    return chipRunRowAlloc(chip_id, exp_id, state, workdir, workdir_state, label, reduction, expgroup, dvodb, tess_id, end_stage);
+}
+psArray *chipRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, CHIPRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        chipRunRow *object = chipRunObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool chipRunDeleteObject(psDB *dbh, const chipRunRow *object)
+{
+    psMetadata *where = chipRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, CHIPRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from chipRun");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "chipRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long chipRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        chipRunRow *object = objects->data[i];
+        psMetadata *where = chipRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, CHIPRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from chipRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool chipRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = chipRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            CHIPRUN_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool chipRunPrintObject(FILE *stream, chipRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = chipRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void chipProcessedImfileRowFree(chipProcessedImfileRow *object);
+
+chipProcessedImfileRow *chipProcessedImfileRowAlloc(psS64 chip_id, psS64 exp_id, const char *class_id, const char *uri, psF32 bg, psF32 bg_stdev, psF32 bg_mean_stdev, psF32 bias, psF32 bias_stdev, psF32 fringe_0, psF32 fringe_1, psF32 fringe_2, psF32 sigma_ra, psF32 sigma_dec, psF32 ap_resid, psF32 ap_resid_stdev, psF32 zp_mean, psF32 zp_stdev, psF32 fwhm_major, psF32 fwhm_minor, psF32 dtime_detrend, psF32 dtime_photom, psF32 dtime_astrom, const char *hostname, psS32 n_stars, psS32 n_extended, psS32 n_cr, psS32 n_astrom, const char *path_base, psS16 fault)
+{
+    chipProcessedImfileRow *_object;
+
+    _object = psAlloc(sizeof(chipProcessedImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)chipProcessedImfileRowFree);
+
+    _object->chip_id = chip_id;
+    _object->exp_id = exp_id;
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->bias = bias;
+    _object->bias_stdev = bias_stdev;
+    _object->fringe_0 = fringe_0;
+    _object->fringe_1 = fringe_1;
+    _object->fringe_2 = fringe_2;
+    _object->sigma_ra = sigma_ra;
+    _object->sigma_dec = sigma_dec;
+    _object->ap_resid = ap_resid;
+    _object->ap_resid_stdev = ap_resid_stdev;
+    _object->zp_mean = zp_mean;
+    _object->zp_stdev = zp_stdev;
+    _object->fwhm_major = fwhm_major;
+    _object->fwhm_minor = fwhm_minor;
+    _object->dtime_detrend = dtime_detrend;
+    _object->dtime_photom = dtime_photom;
+    _object->dtime_astrom = dtime_astrom;
+    _object->hostname = psStringCopy(hostname);
+    _object->n_stars = n_stars;
+    _object->n_extended = n_extended;
+    _object->n_cr = n_cr;
+    _object->n_astrom = n_astrom;
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void chipProcessedImfileRowFree(chipProcessedImfileRow *object)
+{
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->hostname);
+    psFree(object->path_base);
+}
+
+bool chipProcessedImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key fkey (chip_id, exp_id) ref chipRun(chip_id, exp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key fkey (exp_id, class_id) ref rawImfile(exp_id, class_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias_stdev", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_ra", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_dec", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_dec");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid_stdev", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_mean", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_mean");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_stdev", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_major", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_major");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_minor", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_minor");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_detrend", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_detrend");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_photom", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_photom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_astrom", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_stars", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_stars");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_extended", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_extended");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_cr", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_cr");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_astrom", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, CHIPPROCESSEDIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool chipProcessedImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, CHIPPROCESSEDIMFILE_TABLE_NAME);
+}
+
+bool chipProcessedImfileInsert(psDB * dbh, psS64 chip_id, psS64 exp_id, const char *class_id, const char *uri, psF32 bg, psF32 bg_stdev, psF32 bg_mean_stdev, psF32 bias, psF32 bias_stdev, psF32 fringe_0, psF32 fringe_1, psF32 fringe_2, psF32 sigma_ra, psF32 sigma_dec, psF32 ap_resid, psF32 ap_resid_stdev, psF32 zp_mean, psF32 zp_stdev, psF32 fwhm_major, psF32 fwhm_minor, psF32 dtime_detrend, psF32 dtime_photom, psF32 dtime_astrom, const char *hostname, psS32 n_stars, psS32 n_extended, psS32 n_cr, psS32 n_astrom, const char *path_base, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, NULL, chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F32, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F32, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F32, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias", PS_DATA_F32, NULL, bias)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias_stdev", PS_DATA_F32, NULL, bias_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F32, NULL, fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F32, NULL, fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F32, NULL, fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_ra", PS_DATA_F32, NULL, sigma_ra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_dec", PS_DATA_F32, NULL, sigma_dec)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_dec");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid", PS_DATA_F32, NULL, ap_resid)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid_stdev", PS_DATA_F32, NULL, ap_resid_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_mean", PS_DATA_F32, NULL, zp_mean)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_mean");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_stdev", PS_DATA_F32, NULL, zp_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_major", PS_DATA_F32, NULL, fwhm_major)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_major");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_minor", PS_DATA_F32, NULL, fwhm_minor)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_minor");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_detrend", PS_DATA_F32, NULL, dtime_detrend)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_detrend");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_photom", PS_DATA_F32, NULL, dtime_photom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_photom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_astrom", PS_DATA_F32, NULL, dtime_astrom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_stars", PS_DATA_S32, NULL, n_stars)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_stars");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_extended", PS_DATA_S32, NULL, n_extended)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_extended");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_cr", PS_DATA_S32, NULL, n_cr)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_cr");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_astrom", PS_DATA_S32, NULL, n_astrom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, CHIPPROCESSEDIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long chipProcessedImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, CHIPPROCESSEDIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from chipProcessedImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool chipProcessedImfileInsertObject(psDB *dbh, chipProcessedImfileRow *object)
+{
+    return chipProcessedImfileInsert(dbh, object->chip_id, object->exp_id, object->class_id, object->uri, object->bg, object->bg_stdev, object->bg_mean_stdev, object->bias, object->bias_stdev, object->fringe_0, object->fringe_1, object->fringe_2, object->sigma_ra, object->sigma_dec, object->ap_resid, object->ap_resid_stdev, object->zp_mean, object->zp_stdev, object->fwhm_major, object->fwhm_minor, object->dtime_detrend, object->dtime_photom, object->dtime_astrom, object->hostname, object->n_stars, object->n_extended, object->n_cr, object->n_astrom, object->path_base, object->fault);
+}
+
+bool chipProcessedImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!chipProcessedImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool chipProcessedImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  CHIPPROCESSEDIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, CHIPPROCESSEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", CHIPPROCESSEDIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, CHIPPROCESSEDIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool chipProcessedImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, CHIPPROCESSEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, CHIPPROCESSEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *chipProcessedImfileMetadataFromObject(const chipProcessedImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, NULL, object->chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F32, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F32, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F32, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias", PS_DATA_F32, NULL, object->bias)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias_stdev", PS_DATA_F32, NULL, object->bias_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F32, NULL, object->fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F32, NULL, object->fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F32, NULL, object->fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_ra", PS_DATA_F32, NULL, object->sigma_ra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_dec", PS_DATA_F32, NULL, object->sigma_dec)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_dec");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid", PS_DATA_F32, NULL, object->ap_resid)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid_stdev", PS_DATA_F32, NULL, object->ap_resid_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_mean", PS_DATA_F32, NULL, object->zp_mean)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_mean");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_stdev", PS_DATA_F32, NULL, object->zp_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_major", PS_DATA_F32, NULL, object->fwhm_major)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_major");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_minor", PS_DATA_F32, NULL, object->fwhm_minor)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_minor");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_detrend", PS_DATA_F32, NULL, object->dtime_detrend)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_detrend");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_photom", PS_DATA_F32, NULL, object->dtime_photom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_photom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_astrom", PS_DATA_F32, NULL, object->dtime_astrom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, object->hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_stars", PS_DATA_S32, NULL, object->n_stars)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_stars");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_extended", PS_DATA_S32, NULL, object->n_extended)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_extended");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_cr", PS_DATA_S32, NULL, object->n_cr)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_cr");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_astrom", PS_DATA_S32, NULL, object->n_astrom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+chipProcessedImfileRow *chipProcessedImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 chip_id = psMetadataLookupS64(&status, md, "chip_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item chip_id");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psF32 bg = psMetadataLookupF32(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF32 bg_stdev = psMetadataLookupF32(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF32 bg_mean_stdev = psMetadataLookupF32(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF32 bias = psMetadataLookupF32(&status, md, "bias");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bias");
+        return false;
+    }
+    psF32 bias_stdev = psMetadataLookupF32(&status, md, "bias_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bias_stdev");
+        return false;
+    }
+    psF32 fringe_0 = psMetadataLookupF32(&status, md, "fringe_0");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_0");
+        return false;
+    }
+    psF32 fringe_1 = psMetadataLookupF32(&status, md, "fringe_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_1");
+        return false;
+    }
+    psF32 fringe_2 = psMetadataLookupF32(&status, md, "fringe_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_2");
+        return false;
+    }
+    psF32 sigma_ra = psMetadataLookupF32(&status, md, "sigma_ra");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item sigma_ra");
+        return false;
+    }
+    psF32 sigma_dec = psMetadataLookupF32(&status, md, "sigma_dec");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item sigma_dec");
+        return false;
+    }
+    psF32 ap_resid = psMetadataLookupF32(&status, md, "ap_resid");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ap_resid");
+        return false;
+    }
+    psF32 ap_resid_stdev = psMetadataLookupF32(&status, md, "ap_resid_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ap_resid_stdev");
+        return false;
+    }
+    psF32 zp_mean = psMetadataLookupF32(&status, md, "zp_mean");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item zp_mean");
+        return false;
+    }
+    psF32 zp_stdev = psMetadataLookupF32(&status, md, "zp_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item zp_stdev");
+        return false;
+    }
+    psF32 fwhm_major = psMetadataLookupF32(&status, md, "fwhm_major");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fwhm_major");
+        return false;
+    }
+    psF32 fwhm_minor = psMetadataLookupF32(&status, md, "fwhm_minor");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fwhm_minor");
+        return false;
+    }
+    psF32 dtime_detrend = psMetadataLookupF32(&status, md, "dtime_detrend");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dtime_detrend");
+        return false;
+    }
+    psF32 dtime_photom = psMetadataLookupF32(&status, md, "dtime_photom");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dtime_photom");
+        return false;
+    }
+    psF32 dtime_astrom = psMetadataLookupF32(&status, md, "dtime_astrom");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dtime_astrom");
+        return false;
+    }
+    char* hostname = psMetadataLookupPtr(&status, md, "hostname");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item hostname");
+        return false;
+    }
+    psS32 n_stars = psMetadataLookupS32(&status, md, "n_stars");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item n_stars");
+        return false;
+    }
+    psS32 n_extended = psMetadataLookupS32(&status, md, "n_extended");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item n_extended");
+        return false;
+    }
+    psS32 n_cr = psMetadataLookupS32(&status, md, "n_cr");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item n_cr");
+        return false;
+    }
+    psS32 n_astrom = psMetadataLookupS32(&status, md, "n_astrom");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item n_astrom");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return chipProcessedImfileRowAlloc(chip_id, exp_id, class_id, uri, bg, bg_stdev, bg_mean_stdev, bias, bias_stdev, fringe_0, fringe_1, fringe_2, sigma_ra, sigma_dec, ap_resid, ap_resid_stdev, zp_mean, zp_stdev, fwhm_major, fwhm_minor, dtime_detrend, dtime_photom, dtime_astrom, hostname, n_stars, n_extended, n_cr, n_astrom, path_base, fault);
+}
+psArray *chipProcessedImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, CHIPPROCESSEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        chipProcessedImfileRow *object = chipProcessedImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool chipProcessedImfileDeleteObject(psDB *dbh, const chipProcessedImfileRow *object)
+{
+    psMetadata *where = chipProcessedImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, CHIPPROCESSEDIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from chipProcessedImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "chipProcessedImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long chipProcessedImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        chipProcessedImfileRow *object = objects->data[i];
+        psMetadata *where = chipProcessedImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, CHIPPROCESSEDIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from chipProcessedImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool chipProcessedImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = chipProcessedImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            CHIPPROCESSEDIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool chipProcessedImfilePrintObject(FILE *stream, chipProcessedImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = chipProcessedImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void chipMaskRowFree(chipMaskRow *object);
+
+chipMaskRow *chipMaskRowAlloc(const char *label)
+{
+    chipMaskRow     *_object;
+
+    _object = psAlloc(sizeof(chipMaskRow));
+    psMemSetDeallocator(_object, (psFreeFunc)chipMaskRowFree);
+
+    _object->label = psStringCopy(label);
+
+    return _object;
+}
+
+static void chipMaskRowFree(chipMaskRow *object)
+{
+    psFree(object->label);
+}
+
+bool chipMaskCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, CHIPMASK_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool chipMaskDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, CHIPMASK_TABLE_NAME);
+}
+
+bool chipMaskInsert(psDB * dbh, const char *label)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, CHIPMASK_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long chipMaskDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, CHIPMASK_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from chipMask");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool chipMaskInsertObject(psDB *dbh, chipMaskRow *object)
+{
+    return chipMaskInsert(dbh, object->label);
+}
+
+bool chipMaskInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!chipMaskInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool chipMaskInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  CHIPMASK_TABLE_NAME
+    if (!psFitsMoveExtName(fits, CHIPMASK_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", CHIPMASK_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, CHIPMASK_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool chipMaskSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, CHIPMASK_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, CHIPMASK_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *chipMaskMetadataFromObject(const chipMaskRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+chipMaskRow *chipMaskObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+
+    return chipMaskRowAlloc(label);
+}
+psArray *chipMaskSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, CHIPMASK_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        chipMaskRow *object = chipMaskObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool chipMaskDeleteObject(psDB *dbh, const chipMaskRow *object)
+{
+    psMetadata *where = chipMaskMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, CHIPMASK_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from chipMask");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "chipMaskRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long chipMaskDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        chipMaskRow *object = objects->data[i];
+        psMetadata *where = chipMaskMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, CHIPMASK_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from chipMask");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool chipMaskPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = chipMaskMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            CHIPMASK_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool chipMaskPrintObject(FILE *stream, chipMaskRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = chipMaskMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void camRunRowFree(camRunRow *object);
+
+camRunRow *camRunRowAlloc(psS64 cam_id, psS64 chip_id, const char *state, const char *workdir, const char *workdir_state, const char *label, const char *reduction, const char *expgroup, const char *dvodb, const char *tess_id, const char *end_stage)
+{
+    camRunRow       *_object;
+
+    _object = psAlloc(sizeof(camRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)camRunRowFree);
+
+    _object->cam_id = cam_id;
+    _object->chip_id = chip_id;
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->workdir_state = psStringCopy(workdir_state);
+    _object->label = psStringCopy(label);
+    _object->reduction = psStringCopy(reduction);
+    _object->expgroup = psStringCopy(expgroup);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->end_stage = psStringCopy(end_stage);
+
+    return _object;
+}
+
+static void camRunRowFree(camRunRow *object)
+{
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->workdir_state);
+    psFree(object->label);
+    psFree(object->reduction);
+    psFree(object->expgroup);
+    psFree(object->dvodb);
+    psFree(object->tess_id);
+    psFree(object->end_stage);
+}
+
+bool camRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Key INDEX(cam_id, chip_id) fkey(chip_id) ref chipRun(chip_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, "key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "expgroup", PS_DATA_STRING, "key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item expgroup");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, CAMRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool camRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, CAMRUN_TABLE_NAME);
+}
+
+bool camRunInsert(psDB * dbh, psS64 cam_id, psS64 chip_id, const char *state, const char *workdir, const char *workdir_state, const char *label, const char *reduction, const char *expgroup, const char *dvodb, const char *tess_id, const char *end_stage)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, NULL, chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, NULL, workdir_state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "expgroup", PS_DATA_STRING, NULL, expgroup)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item expgroup");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, CAMRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long camRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, CAMRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from camRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool camRunInsertObject(psDB *dbh, camRunRow *object)
+{
+    return camRunInsert(dbh, object->cam_id, object->chip_id, object->state, object->workdir, object->workdir_state, object->label, object->reduction, object->expgroup, object->dvodb, object->tess_id, object->end_stage);
+}
+
+bool camRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!camRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool camRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  CAMRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, CAMRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", CAMRUN_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, CAMRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool camRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, CAMRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, CAMRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *camRunMetadataFromObject(const camRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, NULL, object->chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, NULL, object->workdir_state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, object->reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "expgroup", PS_DATA_STRING, NULL, object->expgroup)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item expgroup");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, object->end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+camRunRow *camRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
+        return false;
+    }
+    psS64 chip_id = psMetadataLookupS64(&status, md, "chip_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item chip_id");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* workdir_state = psMetadataLookupPtr(&status, md, "workdir_state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir_state");
+        return false;
+    }
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+    char* reduction = psMetadataLookupPtr(&status, md, "reduction");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item reduction");
+        return false;
+    }
+    char* expgroup = psMetadataLookupPtr(&status, md, "expgroup");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item expgroup");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* end_stage = psMetadataLookupPtr(&status, md, "end_stage");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item end_stage");
+        return false;
+    }
+
+    return camRunRowAlloc(cam_id, chip_id, state, workdir, workdir_state, label, reduction, expgroup, dvodb, tess_id, end_stage);
+}
+psArray *camRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, CAMRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        camRunRow *object = camRunObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool camRunDeleteObject(psDB *dbh, const camRunRow *object)
+{
+    psMetadata *where = camRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, CAMRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from camRun");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "camRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long camRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        camRunRow *object = objects->data[i];
+        psMetadata *where = camRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, CAMRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from camRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool camRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = camRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            CAMRUN_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool camRunPrintObject(FILE *stream, camRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = camRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void camProcessedExpRowFree(camProcessedExpRow *object);
+
+camProcessedExpRow *camProcessedExpRowAlloc(psS64 cam_id, const char *uri, psF32 bg, psF32 bg_stdev, psF32 bg_mean_stdev, psF32 bias, psF32 bias_stdev, psF32 fringe_0, psF32 fringe_1, psF32 fringe_2, psF32 sigma_ra, psF32 sigma_dec, psF32 ap_resid, psF32 ap_resid_stdev, psF32 zp_mean, psF32 zp_stdev, psF32 fwhm_major, psF32 fwhm_minor, psF32 dtime_detrend, psF32 dtime_photom, psF32 dtime_astrom, const char *hostname, psS32 n_stars, psS32 n_extended, psS32 n_cr, psS32 n_astrom, const char *path_base, psS16 fault)
+{
+    camProcessedExpRow *_object;
+
+    _object = psAlloc(sizeof(camProcessedExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)camProcessedExpRowFree);
+
+    _object->cam_id = cam_id;
+    _object->uri = psStringCopy(uri);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->bias = bias;
+    _object->bias_stdev = bias_stdev;
+    _object->fringe_0 = fringe_0;
+    _object->fringe_1 = fringe_1;
+    _object->fringe_2 = fringe_2;
+    _object->sigma_ra = sigma_ra;
+    _object->sigma_dec = sigma_dec;
+    _object->ap_resid = ap_resid;
+    _object->ap_resid_stdev = ap_resid_stdev;
+    _object->zp_mean = zp_mean;
+    _object->zp_stdev = zp_stdev;
+    _object->fwhm_major = fwhm_major;
+    _object->fwhm_minor = fwhm_minor;
+    _object->dtime_detrend = dtime_detrend;
+    _object->dtime_photom = dtime_photom;
+    _object->dtime_astrom = dtime_astrom;
+    _object->hostname = psStringCopy(hostname);
+    _object->n_stars = n_stars;
+    _object->n_extended = n_extended;
+    _object->n_cr = n_cr;
+    _object->n_astrom = n_astrom;
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void camProcessedExpRowFree(camProcessedExpRow *object)
+{
+    psFree(object->uri);
+    psFree(object->hostname);
+    psFree(object->path_base);
+}
+
+bool camProcessedExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Primary Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, "fkey(cam_id) ref camRun(cam_id)", "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias_stdev", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_ra", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_dec", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_dec");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid_stdev", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_mean", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_mean");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_stdev", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_major", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_major");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_minor", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_minor");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_detrend", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_detrend");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_photom", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_photom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_astrom", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_stars", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_stars");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_extended", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_extended");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_cr", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_cr");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_astrom", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, CAMPROCESSEDEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool camProcessedExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, CAMPROCESSEDEXP_TABLE_NAME);
+}
+
+bool camProcessedExpInsert(psDB * dbh, psS64 cam_id, const char *uri, psF32 bg, psF32 bg_stdev, psF32 bg_mean_stdev, psF32 bias, psF32 bias_stdev, psF32 fringe_0, psF32 fringe_1, psF32 fringe_2, psF32 sigma_ra, psF32 sigma_dec, psF32 ap_resid, psF32 ap_resid_stdev, psF32 zp_mean, psF32 zp_stdev, psF32 fwhm_major, psF32 fwhm_minor, psF32 dtime_detrend, psF32 dtime_photom, psF32 dtime_astrom, const char *hostname, psS32 n_stars, psS32 n_extended, psS32 n_cr, psS32 n_astrom, const char *path_base, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F32, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F32, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F32, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias", PS_DATA_F32, NULL, bias)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias_stdev", PS_DATA_F32, NULL, bias_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F32, NULL, fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F32, NULL, fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F32, NULL, fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_ra", PS_DATA_F32, NULL, sigma_ra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_dec", PS_DATA_F32, NULL, sigma_dec)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_dec");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid", PS_DATA_F32, NULL, ap_resid)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid_stdev", PS_DATA_F32, NULL, ap_resid_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_mean", PS_DATA_F32, NULL, zp_mean)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_mean");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_stdev", PS_DATA_F32, NULL, zp_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_major", PS_DATA_F32, NULL, fwhm_major)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_major");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_minor", PS_DATA_F32, NULL, fwhm_minor)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_minor");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_detrend", PS_DATA_F32, NULL, dtime_detrend)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_detrend");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_photom", PS_DATA_F32, NULL, dtime_photom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_photom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_astrom", PS_DATA_F32, NULL, dtime_astrom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_stars", PS_DATA_S32, NULL, n_stars)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_stars");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_extended", PS_DATA_S32, NULL, n_extended)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_extended");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_cr", PS_DATA_S32, NULL, n_cr)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_cr");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_astrom", PS_DATA_S32, NULL, n_astrom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, CAMPROCESSEDEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long camProcessedExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, CAMPROCESSEDEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from camProcessedExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool camProcessedExpInsertObject(psDB *dbh, camProcessedExpRow *object)
+{
+    return camProcessedExpInsert(dbh, object->cam_id, object->uri, object->bg, object->bg_stdev, object->bg_mean_stdev, object->bias, object->bias_stdev, object->fringe_0, object->fringe_1, object->fringe_2, object->sigma_ra, object->sigma_dec, object->ap_resid, object->ap_resid_stdev, object->zp_mean, object->zp_stdev, object->fwhm_major, object->fwhm_minor, object->dtime_detrend, object->dtime_photom, object->dtime_astrom, object->hostname, object->n_stars, object->n_extended, object->n_cr, object->n_astrom, object->path_base, object->fault);
+}
+
+bool camProcessedExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!camProcessedExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool camProcessedExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  CAMPROCESSEDEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, CAMPROCESSEDEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", CAMPROCESSEDEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, CAMPROCESSEDEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool camProcessedExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, CAMPROCESSEDEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, CAMPROCESSEDEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *camProcessedExpMetadataFromObject(const camProcessedExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F32, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F32, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F32, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias", PS_DATA_F32, NULL, object->bias)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bias_stdev", PS_DATA_F32, NULL, object->bias_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bias_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F32, NULL, object->fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F32, NULL, object->fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F32, NULL, object->fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_ra", PS_DATA_F32, NULL, object->sigma_ra)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_ra");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sigma_dec", PS_DATA_F32, NULL, object->sigma_dec)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sigma_dec");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid", PS_DATA_F32, NULL, object->ap_resid)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ap_resid_stdev", PS_DATA_F32, NULL, object->ap_resid_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ap_resid_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_mean", PS_DATA_F32, NULL, object->zp_mean)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_mean");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "zp_stdev", PS_DATA_F32, NULL, object->zp_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item zp_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_major", PS_DATA_F32, NULL, object->fwhm_major)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_major");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fwhm_minor", PS_DATA_F32, NULL, object->fwhm_minor)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fwhm_minor");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_detrend", PS_DATA_F32, NULL, object->dtime_detrend)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_detrend");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_photom", PS_DATA_F32, NULL, object->dtime_photom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_photom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_astrom", PS_DATA_F32, NULL, object->dtime_astrom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, object->hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_stars", PS_DATA_S32, NULL, object->n_stars)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_stars");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_extended", PS_DATA_S32, NULL, object->n_extended)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_extended");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_cr", PS_DATA_S32, NULL, object->n_cr)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_cr");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "n_astrom", PS_DATA_S32, NULL, object->n_astrom)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item n_astrom");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+camProcessedExpRow *camProcessedExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psF32 bg = psMetadataLookupF32(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF32 bg_stdev = psMetadataLookupF32(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF32 bg_mean_stdev = psMetadataLookupF32(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF32 bias = psMetadataLookupF32(&status, md, "bias");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bias");
+        return false;
+    }
+    psF32 bias_stdev = psMetadataLookupF32(&status, md, "bias_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bias_stdev");
+        return false;
+    }
+    psF32 fringe_0 = psMetadataLookupF32(&status, md, "fringe_0");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_0");
+        return false;
+    }
+    psF32 fringe_1 = psMetadataLookupF32(&status, md, "fringe_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_1");
+        return false;
+    }
+    psF32 fringe_2 = psMetadataLookupF32(&status, md, "fringe_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_2");
+        return false;
+    }
+    psF32 sigma_ra = psMetadataLookupF32(&status, md, "sigma_ra");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item sigma_ra");
+        return false;
+    }
+    psF32 sigma_dec = psMetadataLookupF32(&status, md, "sigma_dec");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item sigma_dec");
+        return false;
+    }
+    psF32 ap_resid = psMetadataLookupF32(&status, md, "ap_resid");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ap_resid");
+        return false;
+    }
+    psF32 ap_resid_stdev = psMetadataLookupF32(&status, md, "ap_resid_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ap_resid_stdev");
+        return false;
+    }
+    psF32 zp_mean = psMetadataLookupF32(&status, md, "zp_mean");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item zp_mean");
+        return false;
+    }
+    psF32 zp_stdev = psMetadataLookupF32(&status, md, "zp_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item zp_stdev");
+        return false;
+    }
+    psF32 fwhm_major = psMetadataLookupF32(&status, md, "fwhm_major");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fwhm_major");
+        return false;
+    }
+    psF32 fwhm_minor = psMetadataLookupF32(&status, md, "fwhm_minor");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fwhm_minor");
+        return false;
+    }
+    psF32 dtime_detrend = psMetadataLookupF32(&status, md, "dtime_detrend");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dtime_detrend");
+        return false;
+    }
+    psF32 dtime_photom = psMetadataLookupF32(&status, md, "dtime_photom");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dtime_photom");
+        return false;
+    }
+    psF32 dtime_astrom = psMetadataLookupF32(&status, md, "dtime_astrom");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dtime_astrom");
+        return false;
+    }
+    char* hostname = psMetadataLookupPtr(&status, md, "hostname");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item hostname");
+        return false;
+    }
+    psS32 n_stars = psMetadataLookupS32(&status, md, "n_stars");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item n_stars");
+        return false;
+    }
+    psS32 n_extended = psMetadataLookupS32(&status, md, "n_extended");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item n_extended");
+        return false;
+    }
+    psS32 n_cr = psMetadataLookupS32(&status, md, "n_cr");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item n_cr");
+        return false;
+    }
+    psS32 n_astrom = psMetadataLookupS32(&status, md, "n_astrom");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item n_astrom");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return camProcessedExpRowAlloc(cam_id, uri, bg, bg_stdev, bg_mean_stdev, bias, bias_stdev, fringe_0, fringe_1, fringe_2, sigma_ra, sigma_dec, ap_resid, ap_resid_stdev, zp_mean, zp_stdev, fwhm_major, fwhm_minor, dtime_detrend, dtime_photom, dtime_astrom, hostname, n_stars, n_extended, n_cr, n_astrom, path_base, fault);
+}
+psArray *camProcessedExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, CAMPROCESSEDEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        camProcessedExpRow *object = camProcessedExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool camProcessedExpDeleteObject(psDB *dbh, const camProcessedExpRow *object)
+{
+    psMetadata *where = camProcessedExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, CAMPROCESSEDEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from camProcessedExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "camProcessedExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long camProcessedExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        camProcessedExpRow *object = objects->data[i];
+        psMetadata *where = camProcessedExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, CAMPROCESSEDEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from camProcessedExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool camProcessedExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = camProcessedExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            CAMPROCESSEDEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool camProcessedExpPrintObject(FILE *stream, camProcessedExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = camProcessedExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void camMaskRowFree(camMaskRow *object);
+
+camMaskRow *camMaskRowAlloc(const char *label)
+{
+    camMaskRow      *_object;
+
+    _object = psAlloc(sizeof(camMaskRow));
+    psMemSetDeallocator(_object, (psFreeFunc)camMaskRowFree);
+
+    _object->label = psStringCopy(label);
+
+    return _object;
+}
+
+static void camMaskRowFree(camMaskRow *object)
+{
+    psFree(object->label);
+}
+
+bool camMaskCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, CAMMASK_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool camMaskDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, CAMMASK_TABLE_NAME);
+}
+
+bool camMaskInsert(psDB * dbh, const char *label)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, CAMMASK_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long camMaskDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, CAMMASK_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from camMask");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool camMaskInsertObject(psDB *dbh, camMaskRow *object)
+{
+    return camMaskInsert(dbh, object->label);
+}
+
+bool camMaskInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!camMaskInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool camMaskInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  CAMMASK_TABLE_NAME
+    if (!psFitsMoveExtName(fits, CAMMASK_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", CAMMASK_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, CAMMASK_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool camMaskSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, CAMMASK_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, CAMMASK_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *camMaskMetadataFromObject(const camMaskRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+camMaskRow *camMaskObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+
+    return camMaskRowAlloc(label);
+}
+psArray *camMaskSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, CAMMASK_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        camMaskRow *object = camMaskObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool camMaskDeleteObject(psDB *dbh, const camMaskRow *object)
+{
+    psMetadata *where = camMaskMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, CAMMASK_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from camMask");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "camMaskRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long camMaskDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        camMaskRow *object = objects->data[i];
+        psMetadata *where = camMaskMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, CAMMASK_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from camMask");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool camMaskPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = camMaskMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            CAMMASK_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool camMaskPrintObject(FILE *stream, camMaskRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = camMaskMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void fakeRunRowFree(fakeRunRow *object);
+
+fakeRunRow *fakeRunRowAlloc(psS64 fake_id, psS64 cam_id, const char *state, const char *workdir, const char *label, const char *reduction, const char *expgroup, const char *dvodb, const char *tess_id, const char *end_stage, psTime* epoch)
+{
+    fakeRunRow      *_object;
+
+    _object = psAlloc(sizeof(fakeRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)fakeRunRowFree);
+
+    _object->fake_id = fake_id;
+    _object->cam_id = cam_id;
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->label = psStringCopy(label);
+    _object->reduction = psStringCopy(reduction);
+    _object->expgroup = psStringCopy(expgroup);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->end_stage = psStringCopy(end_stage);
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void fakeRunRowFree(fakeRunRow *object)
+{
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->label);
+    psFree(object->reduction);
+    psFree(object->expgroup);
+    psFree(object->dvodb);
+    psFree(object->tess_id);
+    psFree(object->end_stage);
+    psFree(object->epoch);
+}
+
+bool fakeRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fake_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fake_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Key INDEX(fake_id, cam_id) fkey (cam_id) ref camRun(cam_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, "Reduction class", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "expgroup", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item expgroup");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, FAKERUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool fakeRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, FAKERUN_TABLE_NAME);
+}
+
+bool fakeRunInsert(psDB * dbh, psS64 fake_id, psS64 cam_id, const char *state, const char *workdir, const char *label, const char *reduction, const char *expgroup, const char *dvodb, const char *tess_id, const char *end_stage, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fake_id", PS_DATA_S64, NULL, fake_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fake_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "expgroup", PS_DATA_STRING, NULL, expgroup)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item expgroup");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, FAKERUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long fakeRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, FAKERUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from fakeRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool fakeRunInsertObject(psDB *dbh, fakeRunRow *object)
+{
+    return fakeRunInsert(dbh, object->fake_id, object->cam_id, object->state, object->workdir, object->label, object->reduction, object->expgroup, object->dvodb, object->tess_id, object->end_stage, object->epoch);
+}
+
+bool fakeRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!fakeRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool fakeRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  FAKERUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, FAKERUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", FAKERUN_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, FAKERUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool fakeRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, FAKERUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, FAKERUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *fakeRunMetadataFromObject(const fakeRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fake_id", PS_DATA_S64, NULL, object->fake_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fake_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, object->reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "expgroup", PS_DATA_STRING, NULL, object->expgroup)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item expgroup");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, object->end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+fakeRunRow *fakeRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 fake_id = psMetadataLookupS64(&status, md, "fake_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fake_id");
+        return false;
+    }
+    psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+    char* reduction = psMetadataLookupPtr(&status, md, "reduction");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item reduction");
+        return false;
+    }
+    char* expgroup = psMetadataLookupPtr(&status, md, "expgroup");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item expgroup");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* end_stage = psMetadataLookupPtr(&status, md, "end_stage");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item end_stage");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return fakeRunRowAlloc(fake_id, cam_id, state, workdir, label, reduction, expgroup, dvodb, tess_id, end_stage, epoch);
+}
+psArray *fakeRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, FAKERUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        fakeRunRow *object = fakeRunObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool fakeRunDeleteObject(psDB *dbh, const fakeRunRow *object)
+{
+    psMetadata *where = fakeRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, FAKERUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from fakeRun");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "fakeRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long fakeRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        fakeRunRow *object = objects->data[i];
+        psMetadata *where = fakeRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, FAKERUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from fakeRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool fakeRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = fakeRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            FAKERUN_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool fakeRunPrintObject(FILE *stream, fakeRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = fakeRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void fakeProcessedImfileRowFree(fakeProcessedImfileRow *object);
+
+fakeProcessedImfileRow *fakeProcessedImfileRowAlloc(psS64 fake_id, psS64 exp_id, const char *class_id, const char *uri, psF32 dtime_fake, const char *hostname, const char *path_base, psS16 fault, psTime* epoch)
+{
+    fakeProcessedImfileRow *_object;
+
+    _object = psAlloc(sizeof(fakeProcessedImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)fakeProcessedImfileRowFree);
+
+    _object->fake_id = fake_id;
+    _object->exp_id = exp_id;
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->dtime_fake = dtime_fake;
+    _object->hostname = psStringCopy(hostname);
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+    _object->epoch = psTimeCopy(epoch);
+
+    return _object;
+}
+
+static void fakeProcessedImfileRowFree(fakeProcessedImfileRow *object)
+{
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->hostname);
+    psFree(object->path_base);
+    psFree(object->epoch);
+}
+
+bool fakeProcessedImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fake_id", PS_DATA_S64, "Primary Key fkey (fake_id) ref fakeRun(fake_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fake_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key fkey (exp_id, class_id) ref rawImfile(exp_id, class_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_fake", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_fake");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, FAKEPROCESSEDIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool fakeProcessedImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, FAKEPROCESSEDIMFILE_TABLE_NAME);
+}
+
+bool fakeProcessedImfileInsert(psDB * dbh, psS64 fake_id, psS64 exp_id, const char *class_id, const char *uri, psF32 dtime_fake, const char *hostname, const char *path_base, psS16 fault, psTime* epoch)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fake_id", PS_DATA_S64, NULL, fake_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fake_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_fake", PS_DATA_F32, NULL, dtime_fake)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_fake");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, FAKEPROCESSEDIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long fakeProcessedImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, FAKEPROCESSEDIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from fakeProcessedImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool fakeProcessedImfileInsertObject(psDB *dbh, fakeProcessedImfileRow *object)
+{
+    return fakeProcessedImfileInsert(dbh, object->fake_id, object->exp_id, object->class_id, object->uri, object->dtime_fake, object->hostname, object->path_base, object->fault, object->epoch);
+}
+
+bool fakeProcessedImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!fakeProcessedImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool fakeProcessedImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  FAKEPROCESSEDIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, FAKEPROCESSEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", FAKEPROCESSEDIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, FAKEPROCESSEDIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool fakeProcessedImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, FAKEPROCESSEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, FAKEPROCESSEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *fakeProcessedImfileMetadataFromObject(const fakeProcessedImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fake_id", PS_DATA_S64, NULL, object->fake_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fake_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_fake", PS_DATA_F32, NULL, object->dtime_fake)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_fake");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, object->hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "epoch", PS_DATA_TIME, NULL, object->epoch)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item epoch");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+fakeProcessedImfileRow *fakeProcessedImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 fake_id = psMetadataLookupS64(&status, md, "fake_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fake_id");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psF32 dtime_fake = psMetadataLookupF32(&status, md, "dtime_fake");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dtime_fake");
+        return false;
+    }
+    char* hostname = psMetadataLookupPtr(&status, md, "hostname");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item hostname");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+    psTime* epoch = psMetadataLookupPtr(&status, md, "epoch");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item epoch");
+        return false;
+    }
+
+    return fakeProcessedImfileRowAlloc(fake_id, exp_id, class_id, uri, dtime_fake, hostname, path_base, fault, epoch);
+}
+psArray *fakeProcessedImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, FAKEPROCESSEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        fakeProcessedImfileRow *object = fakeProcessedImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool fakeProcessedImfileDeleteObject(psDB *dbh, const fakeProcessedImfileRow *object)
+{
+    psMetadata *where = fakeProcessedImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, FAKEPROCESSEDIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from fakeProcessedImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "fakeProcessedImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long fakeProcessedImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        fakeProcessedImfileRow *object = objects->data[i];
+        psMetadata *where = fakeProcessedImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, FAKEPROCESSEDIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from fakeProcessedImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool fakeProcessedImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = fakeProcessedImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            FAKEPROCESSEDIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool fakeProcessedImfilePrintObject(FILE *stream, fakeProcessedImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = fakeProcessedImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void fakeMaskRowFree(fakeMaskRow *object);
+
+fakeMaskRow *fakeMaskRowAlloc(const char *label)
+{
+    fakeMaskRow     *_object;
+
+    _object = psAlloc(sizeof(fakeMaskRow));
+    psMemSetDeallocator(_object, (psFreeFunc)fakeMaskRowFree);
+
+    _object->label = psStringCopy(label);
+
+    return _object;
+}
+
+static void fakeMaskRowFree(fakeMaskRow *object)
+{
+    psFree(object->label);
+}
+
+bool fakeMaskCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, FAKEMASK_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool fakeMaskDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, FAKEMASK_TABLE_NAME);
+}
+
+bool fakeMaskInsert(psDB * dbh, const char *label)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, FAKEMASK_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long fakeMaskDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, FAKEMASK_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from fakeMask");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool fakeMaskInsertObject(psDB *dbh, fakeMaskRow *object)
+{
+    return fakeMaskInsert(dbh, object->label);
+}
+
+bool fakeMaskInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!fakeMaskInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool fakeMaskInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  FAKEMASK_TABLE_NAME
+    if (!psFitsMoveExtName(fits, FAKEMASK_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", FAKEMASK_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, FAKEMASK_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool fakeMaskSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, FAKEMASK_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, FAKEMASK_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *fakeMaskMetadataFromObject(const fakeMaskRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+fakeMaskRow *fakeMaskObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+
+    return fakeMaskRowAlloc(label);
+}
+psArray *fakeMaskSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, FAKEMASK_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        fakeMaskRow *object = fakeMaskObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool fakeMaskDeleteObject(psDB *dbh, const fakeMaskRow *object)
+{
+    psMetadata *where = fakeMaskMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, FAKEMASK_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from fakeMask");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "fakeMaskRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long fakeMaskDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        fakeMaskRow *object = objects->data[i];
+        psMetadata *where = fakeMaskMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, FAKEMASK_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from fakeMask");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool fakeMaskPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = fakeMaskMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            FAKEMASK_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool fakeMaskPrintObject(FILE *stream, fakeMaskRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = fakeMaskMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void warpRunRowFree(warpRunRow *object);
+
+warpRunRow *warpRunRowAlloc(psS64 warp_id, psS64 fake_id, const char *mode, const char *state, const char *workdir, const char *workdir_state, const char *label, const char *dvodb, const char *tess_id, const char *end_stage, psTime* registered, bool magiced)
+{
+    warpRunRow      *_object;
+
+    _object = psAlloc(sizeof(warpRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)warpRunRowFree);
+
+    _object->warp_id = warp_id;
+    _object->fake_id = fake_id;
+    _object->mode = psStringCopy(mode);
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->workdir_state = psStringCopy(workdir_state);
+    _object->label = psStringCopy(label);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->end_stage = psStringCopy(end_stage);
+    _object->registered = psTimeCopy(registered);
+    _object->magiced = magiced;
+
+    return _object;
+}
+
+static void warpRunRowFree(warpRunRow *object)
+{
+    psFree(object->mode);
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->workdir_state);
+    psFree(object->label);
+    psFree(object->dvodb);
+    psFree(object->tess_id);
+    psFree(object->end_stage);
+    psFree(object->registered);
+}
+
+bool warpRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fake_id", PS_DATA_S64, "Key INDEX(warp_id, fake_id) fkey(fake_id) ref camProcessedExp(fake_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fake_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, WARPRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool warpRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, WARPRUN_TABLE_NAME);
+}
+
+bool warpRunInsert(psDB * dbh, psS64 warp_id, psS64 fake_id, const char *mode, const char *state, const char *workdir, const char *workdir_state, const char *label, const char *dvodb, const char *tess_id, const char *end_stage, psTime* registered, bool magiced)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fake_id", PS_DATA_S64, NULL, fake_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fake_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, mode)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, NULL, workdir_state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, magiced)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, WARPRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long warpRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpRunInsertObject(psDB *dbh, warpRunRow *object)
+{
+    return warpRunInsert(dbh, object->warp_id, object->fake_id, object->mode, object->state, object->workdir, object->workdir_state, object->label, object->dvodb, object->tess_id, object->end_stage, object->registered, object->magiced);
+}
+
+bool warpRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!warpRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool warpRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  WARPRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, WARPRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPRUN_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, WARPRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool warpRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, WARPRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, WARPRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *warpRunMetadataFromObject(const warpRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fake_id", PS_DATA_S64, NULL, object->fake_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fake_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, object->mode)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, NULL, object->workdir_state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "end_stage", PS_DATA_STRING, NULL, object->end_stage)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item end_stage");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, object->magiced)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+warpRunRow *warpRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+    psS64 fake_id = psMetadataLookupS64(&status, md, "fake_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fake_id");
+        return false;
+    }
+    char* mode = psMetadataLookupPtr(&status, md, "mode");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item mode");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* workdir_state = psMetadataLookupPtr(&status, md, "workdir_state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir_state");
+        return false;
+    }
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* end_stage = psMetadataLookupPtr(&status, md, "end_stage");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item end_stage");
+        return false;
+    }
+    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
+        return false;
+    }
+    bool magiced = psMetadataLookupBool(&status, md, "magiced");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item magiced");
+        return false;
+    }
+
+    return warpRunRowAlloc(warp_id, fake_id, mode, state, workdir, workdir_state, label, dvodb, tess_id, end_stage, registered, magiced);
+}
+psArray *warpRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, WARPRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        warpRunRow *object = warpRunObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool warpRunDeleteObject(psDB *dbh, const warpRunRow *object)
+{
+    psMetadata *where = warpRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "warpRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long warpRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        warpRunRow *object = objects->data[i];
+        psMetadata *where = warpRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = warpRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            WARPRUN_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool warpRunPrintObject(FILE *stream, warpRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = warpRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void warpSkyCellMapRowFree(warpSkyCellMapRow *object);
+
+warpSkyCellMapRow *warpSkyCellMapRowAlloc(psS64 warp_id, const char *skycell_id, const char *tess_id, const char *class_id, psS16 fault)
+{
+    warpSkyCellMapRow *_object;
+
+    _object = psAlloc(sizeof(warpSkyCellMapRow));
+    psMemSetDeallocator(_object, (psFreeFunc)warpSkyCellMapRowFree);
+
+    _object->warp_id = warp_id;
+    _object->skycell_id = psStringCopy(skycell_id);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->class_id = psStringCopy(class_id);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void warpSkyCellMapRowFree(warpSkyCellMapRow *object)
+{
+    psFree(object->skycell_id);
+    psFree(object->tess_id);
+    psFree(object->class_id);
+}
+
+bool warpSkyCellMapCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key fkey(warp_id) ref warpRun(warp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, WARPSKYCELLMAP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool warpSkyCellMapDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, WARPSKYCELLMAP_TABLE_NAME);
+}
+
+bool warpSkyCellMapInsert(psDB * dbh, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *class_id, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, WARPSKYCELLMAP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long warpSkyCellMapDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpSkyCellMapInsertObject(psDB *dbh, warpSkyCellMapRow *object)
+{
+    return warpSkyCellMapInsert(dbh, object->warp_id, object->skycell_id, object->tess_id, object->class_id, object->fault);
+}
+
+bool warpSkyCellMapInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!warpSkyCellMapInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool warpSkyCellMapInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  WARPSKYCELLMAP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, WARPSKYCELLMAP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPSKYCELLMAP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, WARPSKYCELLMAP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool warpSkyCellMapSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, WARPSKYCELLMAP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *warpSkyCellMapMetadataFromObject(const warpSkyCellMapRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+warpSkyCellMapRow *warpSkyCellMapObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return warpSkyCellMapRowAlloc(warp_id, skycell_id, tess_id, class_id, fault);
+}
+psArray *warpSkyCellMapSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        warpSkyCellMapRow *object = warpSkyCellMapObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool warpSkyCellMapDeleteObject(psDB *dbh, const warpSkyCellMapRow *object)
+{
+    psMetadata *where = warpSkyCellMapMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "warpSkyCellMapRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long warpSkyCellMapDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        warpSkyCellMapRow *object = objects->data[i];
+        psMetadata *where = warpSkyCellMapMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpSkyCellMapPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = warpSkyCellMapMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            WARPSKYCELLMAP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool warpSkyCellMapPrintObject(FILE *stream, warpSkyCellMapRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = warpSkyCellMapMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void warpSkyfileRowFree(warpSkyfileRow *object);
+
+warpSkyfileRow *warpSkyfileRowAlloc(psS64 warp_id, const char *skycell_id, const char *tess_id, const char *uri, const char *path_base, psF64 bg, psF64 bg_stdev, psF32 dtime_warp, const char *hostname, psF32 good_frac, psS32 xmin, psS32 xmax, psS32 ymin, psS32 ymax, bool ignored, psS16 fault)
+{
+    warpSkyfileRow  *_object;
+
+    _object = psAlloc(sizeof(warpSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)warpSkyfileRowFree);
+
+    _object->warp_id = warp_id;
+    _object->skycell_id = psStringCopy(skycell_id);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->uri = psStringCopy(uri);
+    _object->path_base = psStringCopy(path_base);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->dtime_warp = dtime_warp;
+    _object->hostname = psStringCopy(hostname);
+    _object->good_frac = good_frac;
+    _object->xmin = xmin;
+    _object->xmax = xmax;
+    _object->ymin = ymin;
+    _object->ymax = ymax;
+    _object->ignored = ignored;
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void warpSkyfileRowFree(warpSkyfileRow *object)
+{
+    psFree(object->skycell_id);
+    psFree(object->tess_id);
+    psFree(object->uri);
+    psFree(object->path_base);
+    psFree(object->hostname);
+}
+
+bool warpSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key fkey(warp_id, skycell_id, tess_id) ref warpSkyCellMap(warp_id, skycell_id, tess_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_warp", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_warp");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "good_frac", PS_DATA_F32, "Key", 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item good_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "xmin", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item xmin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "xmax", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item xmax");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ymin", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ymin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ymax", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ymax");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ignored", PS_DATA_BOOL, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ignored");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, WARPSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool warpSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, WARPSKYFILE_TABLE_NAME);
+}
+
+bool warpSkyfileInsert(psDB * dbh, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *uri, const char *path_base, psF64 bg, psF64 bg_stdev, psF32 dtime_warp, const char *hostname, psF32 good_frac, psS32 xmin, psS32 xmax, psS32 ymin, psS32 ymax, bool ignored, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_warp", PS_DATA_F32, NULL, dtime_warp)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_warp");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "good_frac", PS_DATA_F32, NULL, good_frac)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item good_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "xmin", PS_DATA_S32, NULL, xmin)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item xmin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "xmax", PS_DATA_S32, NULL, xmax)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item xmax");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ymin", PS_DATA_S32, NULL, ymin)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ymin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ymax", PS_DATA_S32, NULL, ymax)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ymax");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ignored", PS_DATA_BOOL, NULL, ignored)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ignored");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, WARPSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long warpSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpSkyfileInsertObject(psDB *dbh, warpSkyfileRow *object)
+{
+    return warpSkyfileInsert(dbh, object->warp_id, object->skycell_id, object->tess_id, object->uri, object->path_base, object->bg, object->bg_stdev, object->dtime_warp, object->hostname, object->good_frac, object->xmin, object->xmax, object->ymin, object->ymax, object->ignored, object->fault);
+}
+
+bool warpSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!warpSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool warpSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  WARPSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, WARPSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPSKYFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, WARPSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool warpSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, WARPSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *warpSkyfileMetadataFromObject(const warpSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_warp", PS_DATA_F32, NULL, object->dtime_warp)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_warp");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, object->hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "good_frac", PS_DATA_F32, NULL, object->good_frac)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item good_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "xmin", PS_DATA_S32, NULL, object->xmin)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item xmin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "xmax", PS_DATA_S32, NULL, object->xmax)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item xmax");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ymin", PS_DATA_S32, NULL, object->ymin)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ymin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ymax", PS_DATA_S32, NULL, object->ymax)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ymax");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ignored", PS_DATA_BOOL, NULL, object->ignored)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ignored");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+warpSkyfileRow *warpSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF32 dtime_warp = psMetadataLookupF32(&status, md, "dtime_warp");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dtime_warp");
+        return false;
+    }
+    char* hostname = psMetadataLookupPtr(&status, md, "hostname");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item hostname");
+        return false;
+    }
+    psF32 good_frac = psMetadataLookupF32(&status, md, "good_frac");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item good_frac");
+        return false;
+    }
+    psS32 xmin = psMetadataLookupS32(&status, md, "xmin");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item xmin");
+        return false;
+    }
+    psS32 xmax = psMetadataLookupS32(&status, md, "xmax");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item xmax");
+        return false;
+    }
+    psS32 ymin = psMetadataLookupS32(&status, md, "ymin");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ymin");
+        return false;
+    }
+    psS32 ymax = psMetadataLookupS32(&status, md, "ymax");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ymax");
+        return false;
+    }
+    bool ignored = psMetadataLookupBool(&status, md, "ignored");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ignored");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return warpSkyfileRowAlloc(warp_id, skycell_id, tess_id, uri, path_base, bg, bg_stdev, dtime_warp, hostname, good_frac, xmin, xmax, ymin, ymax, ignored, fault);
+}
+psArray *warpSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        warpSkyfileRow *object = warpSkyfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool warpSkyfileDeleteObject(psDB *dbh, const warpSkyfileRow *object)
+{
+    psMetadata *where = warpSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "warpSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long warpSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        warpSkyfileRow *object = objects->data[i];
+        psMetadata *where = warpSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = warpSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            WARPSKYFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool warpSkyfilePrintObject(FILE *stream, warpSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = warpSkyfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void warpMaskRowFree(warpMaskRow *object);
+
+warpMaskRow *warpMaskRowAlloc(const char *label)
+{
+    warpMaskRow     *_object;
+
+    _object = psAlloc(sizeof(warpMaskRow));
+    psMemSetDeallocator(_object, (psFreeFunc)warpMaskRowFree);
+
+    _object->label = psStringCopy(label);
+
+    return _object;
+}
+
+static void warpMaskRowFree(warpMaskRow *object)
+{
+    psFree(object->label);
+}
+
+bool warpMaskCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, WARPMASK_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool warpMaskDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, WARPMASK_TABLE_NAME);
+}
+
+bool warpMaskInsert(psDB * dbh, const char *label)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, WARPMASK_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long warpMaskDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, WARPMASK_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpMask");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpMaskInsertObject(psDB *dbh, warpMaskRow *object)
+{
+    return warpMaskInsert(dbh, object->label);
+}
+
+bool warpMaskInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!warpMaskInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool warpMaskInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  WARPMASK_TABLE_NAME
+    if (!psFitsMoveExtName(fits, WARPMASK_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPMASK_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, WARPMASK_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool warpMaskSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, WARPMASK_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, WARPMASK_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *warpMaskMetadataFromObject(const warpMaskRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+warpMaskRow *warpMaskObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+
+    return warpMaskRowAlloc(label);
+}
+psArray *warpMaskSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, WARPMASK_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        warpMaskRow *object = warpMaskObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool warpMaskDeleteObject(psDB *dbh, const warpMaskRow *object)
+{
+    psMetadata *where = warpMaskMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, WARPMASK_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpMask");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "warpMaskRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long warpMaskDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        warpMaskRow *object = objects->data[i];
+        psMetadata *where = warpMaskMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, WARPMASK_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpMask");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpMaskPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = warpMaskMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            WARPMASK_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool warpMaskPrintObject(FILE *stream, warpMaskRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = warpMaskMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void diffRunRowFree(diffRunRow *object);
+
+diffRunRow *diffRunRowAlloc(psS64 diff_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
+{
+    diffRunRow      *_object;
+
+    _object = psAlloc(sizeof(diffRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)diffRunRowFree);
+
+    _object->diff_id = diff_id;
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->registered = psTimeCopy(registered);
+    _object->skycell_id = psStringCopy(skycell_id);
+    _object->tess_id = psStringCopy(tess_id);
+
+    return _object;
+}
+
+static void diffRunRowFree(diffRunRow *object)
+{
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->dvodb);
+    psFree(object->registered);
+    psFree(object->skycell_id);
+    psFree(object->tess_id);
+}
+
+bool diffRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DIFFRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool diffRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DIFFRUN_TABLE_NAME);
+}
+
+bool diffRunInsert(psDB * dbh, psS64 diff_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DIFFRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long diffRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffRunInsertObject(psDB *dbh, diffRunRow *object)
+{
+    return diffRunInsert(dbh, object->diff_id, object->state, object->workdir, object->dvodb, object->registered, object->skycell_id, object->tess_id);
+}
+
+bool diffRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!diffRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool diffRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DIFFRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DIFFRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFRUN_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DIFFRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool diffRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *diffRunMetadataFromObject(const diffRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+diffRunRow *diffRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
+        return false;
+    }
+    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+
+    return diffRunRowAlloc(diff_id, state, workdir, dvodb, registered, skycell_id, tess_id);
+}
+psArray *diffRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        diffRunRow *object = diffRunObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool diffRunDeleteObject(psDB *dbh, const diffRunRow *object)
+{
+    psMetadata *where = diffRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "diffRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long diffRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        diffRunRow *object = objects->data[i];
+        psMetadata *where = diffRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = diffRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DIFFRUN_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool diffRunPrintObject(FILE *stream, diffRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = diffRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void diffInputSkyfileRowFree(diffInputSkyfileRow *object);
+
+diffInputSkyfileRow *diffInputSkyfileRowAlloc(psS64 diff_id, bool template, psS64 stack_id, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *kind)
+{
+    diffInputSkyfileRow *_object;
+
+    _object = psAlloc(sizeof(diffInputSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)diffInputSkyfileRowFree);
+
+    _object->diff_id = diff_id;
+    _object->template = template;
+    _object->stack_id = stack_id;
+    _object->warp_id = warp_id;
+    _object->skycell_id = psStringCopy(skycell_id);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->kind = psStringCopy(kind);
+
+    return _object;
+}
+
+static void diffInputSkyfileRowFree(diffInputSkyfileRow *object)
+{
+    psFree(object->skycell_id);
+    psFree(object->tess_id);
+    psFree(object->kind);
+}
+
+bool diffInputSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key fkey(diff_id) ref diffRun(diff_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, "Primary Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "fkey(stack_id) ref stackSumSkyfile(stack_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "fkey(warp_id, skycell_id, tess_id) ref warpSkyfile(warp_id, skycell_id, tess_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DIFFINPUTSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool diffInputSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DIFFINPUTSKYFILE_TABLE_NAME);
+}
+
+bool diffInputSkyfileInsert(psDB * dbh, psS64 diff_id, bool template, psS64 stack_id, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *kind)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, template)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, NULL, kind)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DIFFINPUTSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long diffInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffInputSkyfileInsertObject(psDB *dbh, diffInputSkyfileRow *object)
+{
+    return diffInputSkyfileInsert(dbh, object->diff_id, object->template, object->stack_id, object->warp_id, object->skycell_id, object->tess_id, object->kind);
+}
+
+bool diffInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!diffInputSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool diffInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DIFFINPUTSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DIFFINPUTSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFINPUTSKYFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool diffInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFINPUTSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *diffInputSkyfileMetadataFromObject(const diffInputSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, object->template)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, NULL, object->kind)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+diffInputSkyfileRow *diffInputSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
+        return false;
+    }
+    bool template = psMetadataLookupBool(&status, md, "template");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item template");
+        return false;
+    }
+    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
+        return false;
+    }
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* kind = psMetadataLookupPtr(&status, md, "kind");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item kind");
+        return false;
+    }
+
+    return diffInputSkyfileRowAlloc(diff_id, template, stack_id, warp_id, skycell_id, tess_id, kind);
+}
+psArray *diffInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        diffInputSkyfileRow *object = diffInputSkyfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool diffInputSkyfileDeleteObject(psDB *dbh, const diffInputSkyfileRow *object)
+{
+    psMetadata *where = diffInputSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "diffInputSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long diffInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        diffInputSkyfileRow *object = objects->data[i];
+        psMetadata *where = diffInputSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffInputSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = diffInputSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DIFFINPUTSKYFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool diffInputSkyfilePrintObject(FILE *stream, diffInputSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = diffInputSkyfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void diffSkyfileRowFree(diffSkyfileRow *object);
+
+diffSkyfileRow *diffSkyfileRowAlloc(psS64 diff_id, const char *uri, const char *path_base, psF64 bg, psF64 bg_stdev, psS32 stamps_num, psF32 stamps_rms, psS32 sources, psF32 dtime_diff, const char *hostname, psF32 good_frac, psS16 fault)
+{
+    diffSkyfileRow  *_object;
+
+    _object = psAlloc(sizeof(diffSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)diffSkyfileRowFree);
+
+    _object->diff_id = diff_id;
+    _object->uri = psStringCopy(uri);
+    _object->path_base = psStringCopy(path_base);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->stamps_num = stamps_num;
+    _object->stamps_rms = stamps_rms;
+    _object->sources = sources;
+    _object->dtime_diff = dtime_diff;
+    _object->hostname = psStringCopy(hostname);
+    _object->good_frac = good_frac;
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void diffSkyfileRowFree(diffSkyfileRow *object)
+{
+    psFree(object->uri);
+    psFree(object->path_base);
+    psFree(object->hostname);
+}
+
+bool diffSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key fkey(diff_id) ref diffRun(diff_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stamps_num", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stamps_num");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stamps_rms", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stamps_rms");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sources", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sources");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_diff", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_diff");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "good_frac", PS_DATA_F32, "Key", 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item good_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DIFFSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool diffSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DIFFSKYFILE_TABLE_NAME);
+}
+
+bool diffSkyfileInsert(psDB * dbh, psS64 diff_id, const char *uri, const char *path_base, psF64 bg, psF64 bg_stdev, psS32 stamps_num, psF32 stamps_rms, psS32 sources, psF32 dtime_diff, const char *hostname, psF32 good_frac, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stamps_num", PS_DATA_S32, NULL, stamps_num)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stamps_num");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stamps_rms", PS_DATA_F32, NULL, stamps_rms)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stamps_rms");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sources", PS_DATA_S32, NULL, sources)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sources");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_diff", PS_DATA_F32, NULL, dtime_diff)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_diff");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "good_frac", PS_DATA_F32, NULL, good_frac)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item good_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DIFFSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long diffSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffSkyfileInsertObject(psDB *dbh, diffSkyfileRow *object)
+{
+    return diffSkyfileInsert(dbh, object->diff_id, object->uri, object->path_base, object->bg, object->bg_stdev, object->stamps_num, object->stamps_rms, object->sources, object->dtime_diff, object->hostname, object->good_frac, object->fault);
+}
+
+bool diffSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!diffSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool diffSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DIFFSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DIFFSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFSKYFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DIFFSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool diffSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *diffSkyfileMetadataFromObject(const diffSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stamps_num", PS_DATA_S32, NULL, object->stamps_num)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stamps_num");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stamps_rms", PS_DATA_F32, NULL, object->stamps_rms)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stamps_rms");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "sources", PS_DATA_S32, NULL, object->sources)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item sources");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_diff", PS_DATA_F32, NULL, object->dtime_diff)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_diff");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, object->hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "good_frac", PS_DATA_F32, NULL, object->good_frac)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item good_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+diffSkyfileRow *diffSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psS32 stamps_num = psMetadataLookupS32(&status, md, "stamps_num");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stamps_num");
+        return false;
+    }
+    psF32 stamps_rms = psMetadataLookupF32(&status, md, "stamps_rms");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stamps_rms");
+        return false;
+    }
+    psS32 sources = psMetadataLookupS32(&status, md, "sources");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item sources");
+        return false;
+    }
+    psF32 dtime_diff = psMetadataLookupF32(&status, md, "dtime_diff");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dtime_diff");
+        return false;
+    }
+    char* hostname = psMetadataLookupPtr(&status, md, "hostname");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item hostname");
+        return false;
+    }
+    psF32 good_frac = psMetadataLookupF32(&status, md, "good_frac");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item good_frac");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return diffSkyfileRowAlloc(diff_id, uri, path_base, bg, bg_stdev, stamps_num, stamps_rms, sources, dtime_diff, hostname, good_frac, fault);
+}
+psArray *diffSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        diffSkyfileRow *object = diffSkyfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool diffSkyfileDeleteObject(psDB *dbh, const diffSkyfileRow *object)
+{
+    psMetadata *where = diffSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "diffSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long diffSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        diffSkyfileRow *object = objects->data[i];
+        psMetadata *where = diffSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = diffSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DIFFSKYFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool diffSkyfilePrintObject(FILE *stream, diffSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = diffSkyfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void stackRunRowFree(stackRunRow *object);
+
+stackRunRow *stackRunRowAlloc(psS64 stack_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id, const char *filter)
+{
+    stackRunRow     *_object;
+
+    _object = psAlloc(sizeof(stackRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)stackRunRowFree);
+
+    _object->stack_id = stack_id;
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->registered = psTimeCopy(registered);
+    _object->skycell_id = psStringCopy(skycell_id);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->filter = psStringCopy(filter);
+
+    return _object;
+}
+
+static void stackRunRowFree(stackRunRow *object)
+{
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->dvodb);
+    psFree(object->registered);
+    psFree(object->skycell_id);
+    psFree(object->tess_id);
+    psFree(object->filter);
+}
+
+bool stackRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, STACKRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool stackRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, STACKRUN_TABLE_NAME);
+}
+
+bool stackRunInsert(psDB * dbh, psS64 stack_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id, const char *filter)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, filter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, STACKRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long stackRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackRunInsertObject(psDB *dbh, stackRunRow *object)
+{
+    return stackRunInsert(dbh, object->stack_id, object->state, object->workdir, object->dvodb, object->registered, object->skycell_id, object->tess_id, object->filter);
+}
+
+bool stackRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!stackRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool stackRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  STACKRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, STACKRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKRUN_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, STACKRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool stackRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, STACKRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, STACKRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *stackRunMetadataFromObject(const stackRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, object->filter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+stackRunRow *stackRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
+        return false;
+    }
+    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* filter = psMetadataLookupPtr(&status, md, "filter");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item filter");
+        return false;
+    }
+
+    return stackRunRowAlloc(stack_id, state, workdir, dvodb, registered, skycell_id, tess_id, filter);
+}
+psArray *stackRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, STACKRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        stackRunRow *object = stackRunObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool stackRunDeleteObject(psDB *dbh, const stackRunRow *object)
+{
+    psMetadata *where = stackRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "stackRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long stackRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        stackRunRow *object = objects->data[i];
+        psMetadata *where = stackRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = stackRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            STACKRUN_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool stackRunPrintObject(FILE *stream, stackRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = stackRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void stackInputSkyfileRowFree(stackInputSkyfileRow *object);
+
+stackInputSkyfileRow *stackInputSkyfileRowAlloc(psS64 stack_id, psS64 warp_id)
+{
+    stackInputSkyfileRow *_object;
+
+    _object = psAlloc(sizeof(stackInputSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)stackInputSkyfileRowFree);
+
+    _object->stack_id = stack_id;
+    _object->warp_id = warp_id;
+
+    return _object;
+}
+
+static void stackInputSkyfileRowFree(stackInputSkyfileRow *object)
+{
+}
+
+bool stackInputSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key fkey(stack_id) ref stackRun(stack_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key fkey(warp_id) ref warpSkyfile(warp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, STACKINPUTSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool stackInputSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, STACKINPUTSKYFILE_TABLE_NAME);
+}
+
+bool stackInputSkyfileInsert(psDB * dbh, psS64 stack_id, psS64 warp_id)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, STACKINPUTSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long stackInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackInputSkyfileInsertObject(psDB *dbh, stackInputSkyfileRow *object)
+{
+    return stackInputSkyfileInsert(dbh, object->stack_id, object->warp_id);
+}
+
+bool stackInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!stackInputSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool stackInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  STACKINPUTSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, STACKINPUTSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKINPUTSKYFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool stackInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, STACKINPUTSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *stackInputSkyfileMetadataFromObject(const stackInputSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+stackInputSkyfileRow *stackInputSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
+        return false;
+    }
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+
+    return stackInputSkyfileRowAlloc(stack_id, warp_id);
+}
+psArray *stackInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        stackInputSkyfileRow *object = stackInputSkyfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool stackInputSkyfileDeleteObject(psDB *dbh, const stackInputSkyfileRow *object)
+{
+    psMetadata *where = stackInputSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "stackInputSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long stackInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        stackInputSkyfileRow *object = objects->data[i];
+        psMetadata *where = stackInputSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackInputSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = stackInputSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            STACKINPUTSKYFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool stackInputSkyfilePrintObject(FILE *stream, stackInputSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = stackInputSkyfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void stackSumSkyfileRowFree(stackSumSkyfileRow *object);
+
+stackSumSkyfileRow *stackSumSkyfileRowAlloc(psS64 stack_id, const char *uri, const char *path_base, psF64 bg, psF64 bg_stdev, psF32 dtime_stack, const char *hostname, psF32 good_frac, psS16 fault)
+{
+    stackSumSkyfileRow *_object;
+
+    _object = psAlloc(sizeof(stackSumSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)stackSumSkyfileRowFree);
+
+    _object->stack_id = stack_id;
+    _object->uri = psStringCopy(uri);
+    _object->path_base = psStringCopy(path_base);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->dtime_stack = dtime_stack;
+    _object->hostname = psStringCopy(hostname);
+    _object->good_frac = good_frac;
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void stackSumSkyfileRowFree(stackSumSkyfileRow *object)
+{
+    psFree(object->uri);
+    psFree(object->path_base);
+    psFree(object->hostname);
+}
+
+bool stackSumSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key fkey(stack_id) ref stackRun(stack_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_stack", PS_DATA_F32, "Key", 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_stack");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "good_frac", PS_DATA_F32, "Key", 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item good_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, STACKSUMSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool stackSumSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, STACKSUMSKYFILE_TABLE_NAME);
+}
+
+bool stackSumSkyfileInsert(psDB * dbh, psS64 stack_id, const char *uri, const char *path_base, psF64 bg, psF64 bg_stdev, psF32 dtime_stack, const char *hostname, psF32 good_frac, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_stack", PS_DATA_F32, NULL, dtime_stack)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_stack");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "good_frac", PS_DATA_F32, NULL, good_frac)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item good_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, STACKSUMSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long stackSumSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackSumSkyfileInsertObject(psDB *dbh, stackSumSkyfileRow *object)
+{
+    return stackSumSkyfileInsert(dbh, object->stack_id, object->uri, object->path_base, object->bg, object->bg_stdev, object->dtime_stack, object->hostname, object->good_frac, object->fault);
+}
+
+bool stackSumSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!stackSumSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool stackSumSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  STACKSUMSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, STACKSUMSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKSUMSKYFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, STACKSUMSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool stackSumSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, STACKSUMSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *stackSumSkyfileMetadataFromObject(const stackSumSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dtime_stack", PS_DATA_F32, NULL, object->dtime_stack)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dtime_stack");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "hostname", PS_DATA_STRING, NULL, object->hostname)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item hostname");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "good_frac", PS_DATA_F32, NULL, object->good_frac)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item good_frac");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+stackSumSkyfileRow *stackSumSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF32 dtime_stack = psMetadataLookupF32(&status, md, "dtime_stack");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dtime_stack");
+        return false;
+    }
+    char* hostname = psMetadataLookupPtr(&status, md, "hostname");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item hostname");
+        return false;
+    }
+    psF32 good_frac = psMetadataLookupF32(&status, md, "good_frac");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item good_frac");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return stackSumSkyfileRowAlloc(stack_id, uri, path_base, bg, bg_stdev, dtime_stack, hostname, good_frac, fault);
+}
+psArray *stackSumSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        stackSumSkyfileRow *object = stackSumSkyfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool stackSumSkyfileDeleteObject(psDB *dbh, const stackSumSkyfileRow *object)
+{
+    psMetadata *where = stackSumSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "stackSumSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long stackSumSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        stackSumSkyfileRow *object = objects->data[i];
+        psMetadata *where = stackSumSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackSumSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = stackSumSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            STACKSUMSKYFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool stackSumSkyfilePrintObject(FILE *stream, stackSumSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = stackSumSkyfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detRunRowFree(detRunRow *object);
+
+detRunRow *detRunRowAlloc(psS64 det_id, psS32 iteration, const char *det_type, const char *mode, const char *state, const char *filelevel, const char *workdir, const char *camera, const char *telescope, const char *exp_type, const char *reduction, const char *filter, psF32 airmass_min, psF32 airmass_max, psF32 exp_time_min, psF32 exp_time_max, psF32 ccd_temp_min, psF32 ccd_temp_max, psF64 posang_min, psF64 posang_max, psTime* registered, psTime* time_begin, psTime* time_end, psTime* use_begin, psTime* use_end, psF32 solang_min, psF32 solang_max, const char *label, psS32 parent)
+{
+    detRunRow       *_object;
+
+    _object = psAlloc(sizeof(detRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detRunRowFree);
+
+    _object->det_id = det_id;
+    _object->iteration = iteration;
+    _object->det_type = psStringCopy(det_type);
+    _object->mode = psStringCopy(mode);
+    _object->state = psStringCopy(state);
+    _object->filelevel = psStringCopy(filelevel);
+    _object->workdir = psStringCopy(workdir);
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+    _object->exp_type = psStringCopy(exp_type);
+    _object->reduction = psStringCopy(reduction);
+    _object->filter = psStringCopy(filter);
+    _object->airmass_min = airmass_min;
+    _object->airmass_max = airmass_max;
+    _object->exp_time_min = exp_time_min;
+    _object->exp_time_max = exp_time_max;
+    _object->ccd_temp_min = ccd_temp_min;
+    _object->ccd_temp_max = ccd_temp_max;
+    _object->posang_min = posang_min;
+    _object->posang_max = posang_max;
+    _object->registered = psTimeCopy(registered);
+    _object->time_begin = psTimeCopy(time_begin);
+    _object->time_end = psTimeCopy(time_end);
+    _object->use_begin = psTimeCopy(use_begin);
+    _object->use_end = psTimeCopy(use_end);
+    _object->solang_min = solang_min;
+    _object->solang_max = solang_max;
+    _object->label = psStringCopy(label);
+    _object->parent = parent;
+
+    return _object;
+}
+
+static void detRunRowFree(detRunRow *object)
+{
+    psFree(object->det_type);
+    psFree(object->mode);
+    psFree(object->state);
+    psFree(object->filelevel);
+    psFree(object->workdir);
+    psFree(object->camera);
+    psFree(object->telescope);
+    psFree(object->exp_type);
+    psFree(object->reduction);
+    psFree(object->filter);
+    psFree(object->registered);
+    psFree(object->time_begin);
+    psFree(object->time_end);
+    psFree(object->use_begin);
+    psFree(object->use_end);
+    psFree(object->label);
+}
+
+bool detRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, "Key INDEX(det_id, iteration)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_type", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filelevel", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filelevel");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, "destination for output files", "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, "XXX this should be dropped", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, "Reduction clas", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass_min", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass_max", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time_min", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time_max", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp_min", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp_max", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang_min", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang_max", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "time_begin", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item time_begin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "time_end", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item time_end");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "use_begin", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item use_begin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "use_end", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item use_end");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "solang_min", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item solang_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "solang_max", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item solang_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "parent", PS_DATA_S32, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item parent");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETRUN_TABLE_NAME);
+}
+
+bool detRunInsert(psDB * dbh, psS64 det_id, psS32 iteration, const char *det_type, const char *mode, const char *state, const char *filelevel, const char *workdir, const char *camera, const char *telescope, const char *exp_type, const char *reduction, const char *filter, psF32 airmass_min, psF32 airmass_max, psF32 exp_time_min, psF32 exp_time_max, psF32 ccd_temp_min, psF32 ccd_temp_max, psF64 posang_min, psF64 posang_max, psTime* registered, psTime* time_begin, psTime* time_end, psTime* use_begin, psTime* use_end, psF32 solang_min, psF32 solang_max, const char *label, psS32 parent)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_type", PS_DATA_STRING, NULL, det_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, mode)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filelevel", PS_DATA_STRING, NULL, filelevel)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filelevel");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, exp_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, filter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass_min", PS_DATA_F32, NULL, airmass_min)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass_max", PS_DATA_F32, NULL, airmass_max)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time_min", PS_DATA_F32, NULL, exp_time_min)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time_max", PS_DATA_F32, NULL, exp_time_max)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp_min", PS_DATA_F32, NULL, ccd_temp_min)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp_max", PS_DATA_F32, NULL, ccd_temp_max)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang_min", PS_DATA_F64, NULL, posang_min)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang_max", PS_DATA_F64, NULL, posang_max)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "time_begin", PS_DATA_TIME, NULL, time_begin)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item time_begin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "time_end", PS_DATA_TIME, NULL, time_end)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item time_end");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "use_begin", PS_DATA_TIME, NULL, use_begin)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item use_begin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "use_end", PS_DATA_TIME, NULL, use_end)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item use_end");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "solang_min", PS_DATA_F32, NULL, solang_min)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item solang_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "solang_max", PS_DATA_F32, NULL, solang_max)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item solang_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "parent", PS_DATA_S32, NULL, parent)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item parent");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detRunInsertObject(psDB *dbh, detRunRow *object)
+{
+    return detRunInsert(dbh, object->det_id, object->iteration, object->det_type, object->mode, object->state, object->filelevel, object->workdir, object->camera, object->telescope, object->exp_type, object->reduction, object->filter, object->airmass_min, object->airmass_max, object->exp_time_min, object->exp_time_max, object->ccd_temp_min, object->ccd_temp_max, object->posang_min, object->posang_max, object->registered, object->time_begin, object->time_end, object->use_begin, object->use_end, object->solang_min, object->solang_max, object->label, object->parent);
+}
+
+bool detRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETRUN_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detRunMetadataFromObject(const detRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, object->iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_type", PS_DATA_STRING, NULL, object->det_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, object->mode)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filelevel", PS_DATA_STRING, NULL, object->filelevel)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filelevel");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, object->exp_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reduction", PS_DATA_STRING, NULL, object->reduction)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reduction");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, object->filter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass_min", PS_DATA_F32, NULL, object->airmass_min)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "airmass_max", PS_DATA_F32, NULL, object->airmass_max)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item airmass_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time_min", PS_DATA_F32, NULL, object->exp_time_min)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_time_max", PS_DATA_F32, NULL, object->exp_time_max)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp_min", PS_DATA_F32, NULL, object->ccd_temp_min)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ccd_temp_max", PS_DATA_F32, NULL, object->ccd_temp_max)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang_min", PS_DATA_F64, NULL, object->posang_min)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "posang_max", PS_DATA_F64, NULL, object->posang_max)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item posang_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "time_begin", PS_DATA_TIME, NULL, object->time_begin)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item time_begin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "time_end", PS_DATA_TIME, NULL, object->time_end)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item time_end");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "use_begin", PS_DATA_TIME, NULL, object->use_begin)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item use_begin");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "use_end", PS_DATA_TIME, NULL, object->use_end)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item use_end");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "solang_min", PS_DATA_F32, NULL, object->solang_min)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item solang_min");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "solang_max", PS_DATA_F32, NULL, object->solang_max)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item solang_max");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "parent", PS_DATA_S32, NULL, object->parent)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item parent");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detRunRow *detRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, md, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item iteration");
+        return false;
+    }
+    char* det_type = psMetadataLookupPtr(&status, md, "det_type");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_type");
+        return false;
+    }
+    char* mode = psMetadataLookupPtr(&status, md, "mode");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item mode");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* filelevel = psMetadataLookupPtr(&status, md, "filelevel");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item filelevel");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+    char* exp_type = psMetadataLookupPtr(&status, md, "exp_type");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_type");
+        return false;
+    }
+    char* reduction = psMetadataLookupPtr(&status, md, "reduction");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item reduction");
+        return false;
+    }
+    char* filter = psMetadataLookupPtr(&status, md, "filter");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item filter");
+        return false;
+    }
+    psF32 airmass_min = psMetadataLookupF32(&status, md, "airmass_min");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item airmass_min");
+        return false;
+    }
+    psF32 airmass_max = psMetadataLookupF32(&status, md, "airmass_max");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item airmass_max");
+        return false;
+    }
+    psF32 exp_time_min = psMetadataLookupF32(&status, md, "exp_time_min");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_time_min");
+        return false;
+    }
+    psF32 exp_time_max = psMetadataLookupF32(&status, md, "exp_time_max");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_time_max");
+        return false;
+    }
+    psF32 ccd_temp_min = psMetadataLookupF32(&status, md, "ccd_temp_min");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ccd_temp_min");
+        return false;
+    }
+    psF32 ccd_temp_max = psMetadataLookupF32(&status, md, "ccd_temp_max");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ccd_temp_max");
+        return false;
+    }
+    psF64 posang_min = psMetadataLookupF64(&status, md, "posang_min");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item posang_min");
+        return false;
+    }
+    psF64 posang_max = psMetadataLookupF64(&status, md, "posang_max");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item posang_max");
+        return false;
+    }
+    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
+        return false;
+    }
+    psTime* time_begin = psMetadataLookupPtr(&status, md, "time_begin");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item time_begin");
+        return false;
+    }
+    psTime* time_end = psMetadataLookupPtr(&status, md, "time_end");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item time_end");
+        return false;
+    }
+    psTime* use_begin = psMetadataLookupPtr(&status, md, "use_begin");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item use_begin");
+        return false;
+    }
+    psTime* use_end = psMetadataLookupPtr(&status, md, "use_end");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item use_end");
+        return false;
+    }
+    psF32 solang_min = psMetadataLookupF32(&status, md, "solang_min");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item solang_min");
+        return false;
+    }
+    psF32 solang_max = psMetadataLookupF32(&status, md, "solang_max");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item solang_max");
+        return false;
+    }
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+    psS32 parent = psMetadataLookupS32(&status, md, "parent");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item parent");
+        return false;
+    }
+
+    return detRunRowAlloc(det_id, iteration, det_type, mode, state, filelevel, workdir, camera, telescope, exp_type, reduction, filter, airmass_min, airmass_max, exp_time_min, exp_time_max, ccd_temp_min, ccd_temp_max, posang_min, posang_max, registered, time_begin, time_end, use_begin, use_end, solang_min, solang_max, label, parent);
+}
+psArray *detRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detRunRow *object = detRunObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detRunDeleteObject(psDB *dbh, const detRunRow *object)
+{
+    psMetadata *where = detRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detRun");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detRunRow *object = objects->data[i];
+        psMetadata *where = detRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETRUN_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detRunPrintObject(FILE *stream, detRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detInputExpRowFree(detInputExpRow *object);
+
+detInputExpRow *detInputExpRowAlloc(psS64 det_id, psS32 iteration, psS64 exp_id, bool include)
+{
+    detInputExpRow  *_object;
+
+    _object = psAlloc(sizeof(detInputExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detInputExpRowFree);
+
+    _object->det_id = det_id;
+    _object->iteration = iteration;
+    _object->exp_id = exp_id;
+    _object->include = include;
+
+    return _object;
+}
+
+static void detInputExpRowFree(detInputExpRow *object)
+{
+}
+
+bool detInputExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id) ref detRun(det_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, "Primary Key fkey(exp_id) ref rawExp(exp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key INDEX(det_id, exp_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "include", PS_DATA_BOOL, "INDEX(det_id, iteration)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item include");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETINPUTEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detInputExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETINPUTEXP_TABLE_NAME);
+}
+
+bool detInputExpInsert(psDB * dbh, psS64 det_id, psS32 iteration, psS64 exp_id, bool include)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "include", PS_DATA_BOOL, NULL, include)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item include");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETINPUTEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detInputExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETINPUTEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detInputExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detInputExpInsertObject(psDB *dbh, detInputExpRow *object)
+{
+    return detInputExpInsert(dbh, object->det_id, object->iteration, object->exp_id, object->include);
+}
+
+bool detInputExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detInputExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detInputExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETINPUTEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETINPUTEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETINPUTEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETINPUTEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detInputExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETINPUTEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETINPUTEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detInputExpMetadataFromObject(const detInputExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, object->iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "include", PS_DATA_BOOL, NULL, object->include)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item include");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detInputExpRow *detInputExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, md, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item iteration");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    bool include = psMetadataLookupBool(&status, md, "include");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item include");
+        return false;
+    }
+
+    return detInputExpRowAlloc(det_id, iteration, exp_id, include);
+}
+psArray *detInputExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETINPUTEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detInputExpRow *object = detInputExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detInputExpDeleteObject(psDB *dbh, const detInputExpRow *object)
+{
+    psMetadata *where = detInputExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETINPUTEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detInputExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detInputExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detInputExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detInputExpRow *object = objects->data[i];
+        psMetadata *where = detInputExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETINPUTEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detInputExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detInputExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detInputExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETINPUTEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detInputExpPrintObject(FILE *stream, detInputExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detInputExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detProcessedImfileRowFree(detProcessedImfileRow *object);
+
+detProcessedImfileRow *detProcessedImfileRowAlloc(psS64 det_id, psS64 exp_id, const char *class_id, const char *uri, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 fringe_0, psF64 fringe_1, psF64 fringe_2, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    detProcessedImfileRow *_object;
+
+    _object = psAlloc(sizeof(detProcessedImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detProcessedImfileRowFree);
+
+    _object->det_id = det_id;
+    _object->exp_id = exp_id;
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->recipe = psStringCopy(recipe);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->fringe_0 = fringe_0;
+    _object->fringe_1 = fringe_1;
+    _object->fringe_2 = fringe_2;
+    _object->user_1 = user_1;
+    _object->user_2 = user_2;
+    _object->user_3 = user_3;
+    _object->user_4 = user_4;
+    _object->user_5 = user_5;
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detProcessedImfileRowFree(detProcessedImfileRow *object)
+{
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->recipe);
+    psFree(object->path_base);
+}
+
+bool detProcessedImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, exp_id) ref detInputExp(det_id, exp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key fkey(exp_id, class_id) ref rawImfile(exp_id, class_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key INDEX(det_id, class_id)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, "INDEX(det_id, exp_id)", "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETPROCESSEDIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detProcessedImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETPROCESSEDIMFILE_TABLE_NAME);
+}
+
+bool detProcessedImfileInsert(psDB * dbh, psS64 det_id, psS64 exp_id, const char *class_id, const char *uri, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 fringe_0, psF64 fringe_1, psF64 fringe_2, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETPROCESSEDIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detProcessedImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETPROCESSEDIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detProcessedImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detProcessedImfileInsertObject(psDB *dbh, detProcessedImfileRow *object)
+{
+    return detProcessedImfileInsert(dbh, object->det_id, object->exp_id, object->class_id, object->uri, object->recipe, object->bg, object->bg_stdev, object->bg_mean_stdev, object->fringe_0, object->fringe_1, object->fringe_2, object->user_1, object->user_2, object->user_3, object->user_4, object->user_5, object->path_base, object->fault);
+}
+
+bool detProcessedImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detProcessedImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detProcessedImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETPROCESSEDIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETPROCESSEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETPROCESSEDIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETPROCESSEDIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detProcessedImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETPROCESSEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETPROCESSEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detProcessedImfileMetadataFromObject(const detProcessedImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, object->recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, object->fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, object->fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, object->fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, object->user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, object->user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, object->user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, object->user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, object->user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detProcessedImfileRow *detProcessedImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    char* recipe = psMetadataLookupPtr(&status, md, "recipe");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item recipe");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF64 fringe_0 = psMetadataLookupF64(&status, md, "fringe_0");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_0");
+        return false;
+    }
+    psF64 fringe_1 = psMetadataLookupF64(&status, md, "fringe_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_1");
+        return false;
+    }
+    psF64 fringe_2 = psMetadataLookupF64(&status, md, "fringe_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_2");
+        return false;
+    }
+    psF64 user_1 = psMetadataLookupF64(&status, md, "user_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_1");
+        return false;
+    }
+    psF64 user_2 = psMetadataLookupF64(&status, md, "user_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_2");
+        return false;
+    }
+    psF64 user_3 = psMetadataLookupF64(&status, md, "user_3");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_3");
+        return false;
+    }
+    psF64 user_4 = psMetadataLookupF64(&status, md, "user_4");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_4");
+        return false;
+    }
+    psF64 user_5 = psMetadataLookupF64(&status, md, "user_5");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_5");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detProcessedImfileRowAlloc(det_id, exp_id, class_id, uri, recipe, bg, bg_stdev, bg_mean_stdev, fringe_0, fringe_1, fringe_2, user_1, user_2, user_3, user_4, user_5, path_base, fault);
+}
+psArray *detProcessedImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETPROCESSEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detProcessedImfileRow *object = detProcessedImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detProcessedImfileDeleteObject(psDB *dbh, const detProcessedImfileRow *object)
+{
+    psMetadata *where = detProcessedImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETPROCESSEDIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detProcessedImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detProcessedImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detProcessedImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detProcessedImfileRow *object = objects->data[i];
+        psMetadata *where = detProcessedImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETPROCESSEDIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detProcessedImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detProcessedImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detProcessedImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETPROCESSEDIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detProcessedImfilePrintObject(FILE *stream, detProcessedImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detProcessedImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detProcessedExpRowFree(detProcessedExpRow *object);
+
+detProcessedExpRow *detProcessedExpRowAlloc(psS64 det_id, psS64 exp_id, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 fringe_0, psF64 fringe_1, psF64 fringe_2, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    detProcessedExpRow *_object;
+
+    _object = psAlloc(sizeof(detProcessedExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detProcessedExpRowFree);
+
+    _object->det_id = det_id;
+    _object->exp_id = exp_id;
+    _object->recipe = psStringCopy(recipe);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->fringe_0 = fringe_0;
+    _object->fringe_1 = fringe_1;
+    _object->fringe_2 = fringe_2;
+    _object->user_1 = user_1;
+    _object->user_2 = user_2;
+    _object->user_3 = user_3;
+    _object->user_4 = user_4;
+    _object->user_5 = user_5;
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detProcessedExpRowFree(detProcessedExpRow *object)
+{
+    psFree(object->recipe);
+    psFree(object->path_base);
+}
+
+bool detProcessedExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, exp_id) ref detInputExp(det_id, exp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key fkey(det_id, exp_id) ref detProcessedImfile(det_id, exp_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETPROCESSEDEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detProcessedExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETPROCESSEDEXP_TABLE_NAME);
+}
+
+bool detProcessedExpInsert(psDB * dbh, psS64 det_id, psS64 exp_id, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 fringe_0, psF64 fringe_1, psF64 fringe_2, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETPROCESSEDEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detProcessedExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETPROCESSEDEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detProcessedExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detProcessedExpInsertObject(psDB *dbh, detProcessedExpRow *object)
+{
+    return detProcessedExpInsert(dbh, object->det_id, object->exp_id, object->recipe, object->bg, object->bg_stdev, object->bg_mean_stdev, object->fringe_0, object->fringe_1, object->fringe_2, object->user_1, object->user_2, object->user_3, object->user_4, object->user_5, object->path_base, object->fault);
+}
+
+bool detProcessedExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detProcessedExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detProcessedExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETPROCESSEDEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETPROCESSEDEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETPROCESSEDEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETPROCESSEDEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detProcessedExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETPROCESSEDEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETPROCESSEDEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detProcessedExpMetadataFromObject(const detProcessedExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, object->recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, object->fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, object->fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, object->fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, object->user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, object->user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, object->user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, object->user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, object->user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detProcessedExpRow *detProcessedExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* recipe = psMetadataLookupPtr(&status, md, "recipe");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item recipe");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF64 fringe_0 = psMetadataLookupF64(&status, md, "fringe_0");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_0");
+        return false;
+    }
+    psF64 fringe_1 = psMetadataLookupF64(&status, md, "fringe_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_1");
+        return false;
+    }
+    psF64 fringe_2 = psMetadataLookupF64(&status, md, "fringe_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_2");
+        return false;
+    }
+    psF64 user_1 = psMetadataLookupF64(&status, md, "user_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_1");
+        return false;
+    }
+    psF64 user_2 = psMetadataLookupF64(&status, md, "user_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_2");
+        return false;
+    }
+    psF64 user_3 = psMetadataLookupF64(&status, md, "user_3");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_3");
+        return false;
+    }
+    psF64 user_4 = psMetadataLookupF64(&status, md, "user_4");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_4");
+        return false;
+    }
+    psF64 user_5 = psMetadataLookupF64(&status, md, "user_5");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_5");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detProcessedExpRowAlloc(det_id, exp_id, recipe, bg, bg_stdev, bg_mean_stdev, fringe_0, fringe_1, fringe_2, user_1, user_2, user_3, user_4, user_5, path_base, fault);
+}
+psArray *detProcessedExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETPROCESSEDEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detProcessedExpRow *object = detProcessedExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detProcessedExpDeleteObject(psDB *dbh, const detProcessedExpRow *object)
+{
+    psMetadata *where = detProcessedExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETPROCESSEDEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detProcessedExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detProcessedExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detProcessedExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detProcessedExpRow *object = objects->data[i];
+        psMetadata *where = detProcessedExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETPROCESSEDEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detProcessedExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detProcessedExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detProcessedExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETPROCESSEDEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detProcessedExpPrintObject(FILE *stream, detProcessedExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detProcessedExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detStackedImfileRowFree(detStackedImfileRow *object);
+
+detStackedImfileRow *detStackedImfileRowAlloc(psS64 det_id, psS32 iteration, const char *class_id, const char *uri, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, psS16 fault)
+{
+    detStackedImfileRow *_object;
+
+    _object = psAlloc(sizeof(detStackedImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detStackedImfileRowFree);
+
+    _object->det_id = det_id;
+    _object->iteration = iteration;
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->recipe = psStringCopy(recipe);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->user_1 = user_1;
+    _object->user_2 = user_2;
+    _object->user_3 = user_3;
+    _object->user_4 = user_4;
+    _object->user_5 = user_5;
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detStackedImfileRowFree(detStackedImfileRow *object)
+{
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->recipe);
+}
+
+bool detStackedImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, iteration) ref detInputExp(det_id, iteration)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, "Primary Key fkey(det_id, class_id) ref detProcessedImfile(det_id, class_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETSTACKEDIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detStackedImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETSTACKEDIMFILE_TABLE_NAME);
+}
+
+bool detStackedImfileInsert(psDB * dbh, psS64 det_id, psS32 iteration, const char *class_id, const char *uri, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETSTACKEDIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detStackedImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETSTACKEDIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detStackedImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detStackedImfileInsertObject(psDB *dbh, detStackedImfileRow *object)
+{
+    return detStackedImfileInsert(dbh, object->det_id, object->iteration, object->class_id, object->uri, object->recipe, object->bg, object->bg_stdev, object->bg_mean_stdev, object->user_1, object->user_2, object->user_3, object->user_4, object->user_5, object->fault);
+}
+
+bool detStackedImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detStackedImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detStackedImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETSTACKEDIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETSTACKEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETSTACKEDIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETSTACKEDIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detStackedImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETSTACKEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETSTACKEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detStackedImfileMetadataFromObject(const detStackedImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, object->iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, object->recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, object->user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, object->user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, object->user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, object->user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, object->user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detStackedImfileRow *detStackedImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, md, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item iteration");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    char* recipe = psMetadataLookupPtr(&status, md, "recipe");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item recipe");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF64 user_1 = psMetadataLookupF64(&status, md, "user_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_1");
+        return false;
+    }
+    psF64 user_2 = psMetadataLookupF64(&status, md, "user_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_2");
+        return false;
+    }
+    psF64 user_3 = psMetadataLookupF64(&status, md, "user_3");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_3");
+        return false;
+    }
+    psF64 user_4 = psMetadataLookupF64(&status, md, "user_4");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_4");
+        return false;
+    }
+    psF64 user_5 = psMetadataLookupF64(&status, md, "user_5");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_5");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detStackedImfileRowAlloc(det_id, iteration, class_id, uri, recipe, bg, bg_stdev, bg_mean_stdev, user_1, user_2, user_3, user_4, user_5, fault);
+}
+psArray *detStackedImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETSTACKEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detStackedImfileRow *object = detStackedImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detStackedImfileDeleteObject(psDB *dbh, const detStackedImfileRow *object)
+{
+    psMetadata *where = detStackedImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETSTACKEDIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detStackedImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detStackedImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detStackedImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detStackedImfileRow *object = objects->data[i];
+        psMetadata *where = detStackedImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETSTACKEDIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detStackedImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detStackedImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detStackedImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETSTACKEDIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detStackedImfilePrintObject(FILE *stream, detStackedImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detStackedImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detNormalizedStatImfileRowFree(detNormalizedStatImfileRow *object);
+
+detNormalizedStatImfileRow *detNormalizedStatImfileRowAlloc(psS64 det_id, psS32 iteration, const char *class_id, psF32 norm, psS16 fault)
+{
+    detNormalizedStatImfileRow *_object;
+
+    _object = psAlloc(sizeof(detNormalizedStatImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detNormalizedStatImfileRowFree);
+
+    _object->det_id = det_id;
+    _object->iteration = iteration;
+    _object->class_id = psStringCopy(class_id);
+    _object->norm = norm;
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detNormalizedStatImfileRowFree(detNormalizedStatImfileRow *object)
+{
+    psFree(object->class_id);
+}
+
+bool detNormalizedStatImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, iteration) ref detInputExp(det_id, iteration)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, "Primary Key fkey(det_id, iteration, class_id) ref detStackedImfile(det_id, iteration, class_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "norm", PS_DATA_F32, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item norm");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETNORMALIZEDSTATIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detNormalizedStatImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETNORMALIZEDSTATIMFILE_TABLE_NAME);
+}
+
+bool detNormalizedStatImfileInsert(psDB * dbh, psS64 det_id, psS32 iteration, const char *class_id, psF32 norm, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "norm", PS_DATA_F32, NULL, norm)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item norm");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETNORMALIZEDSTATIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detNormalizedStatImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETNORMALIZEDSTATIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detNormalizedStatImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detNormalizedStatImfileInsertObject(psDB *dbh, detNormalizedStatImfileRow *object)
+{
+    return detNormalizedStatImfileInsert(dbh, object->det_id, object->iteration, object->class_id, object->norm, object->fault);
+}
+
+bool detNormalizedStatImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detNormalizedStatImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detNormalizedStatImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETNORMALIZEDSTATIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETNORMALIZEDSTATIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETNORMALIZEDSTATIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETNORMALIZEDSTATIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detNormalizedStatImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETNORMALIZEDSTATIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETNORMALIZEDSTATIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detNormalizedStatImfileMetadataFromObject(const detNormalizedStatImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, object->iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "norm", PS_DATA_F32, NULL, object->norm)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item norm");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detNormalizedStatImfileRow *detNormalizedStatImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, md, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item iteration");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    psF32 norm = psMetadataLookupF32(&status, md, "norm");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item norm");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detNormalizedStatImfileRowAlloc(det_id, iteration, class_id, norm, fault);
+}
+psArray *detNormalizedStatImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETNORMALIZEDSTATIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detNormalizedStatImfileRow *object = detNormalizedStatImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detNormalizedStatImfileDeleteObject(psDB *dbh, const detNormalizedStatImfileRow *object)
+{
+    psMetadata *where = detNormalizedStatImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETNORMALIZEDSTATIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detNormalizedStatImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detNormalizedStatImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detNormalizedStatImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detNormalizedStatImfileRow *object = objects->data[i];
+        psMetadata *where = detNormalizedStatImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETNORMALIZEDSTATIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detNormalizedStatImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detNormalizedStatImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detNormalizedStatImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETNORMALIZEDSTATIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detNormalizedStatImfilePrintObject(FILE *stream, detNormalizedStatImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detNormalizedStatImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detNormalizedImfileRowFree(detNormalizedImfileRow *object);
+
+detNormalizedImfileRow *detNormalizedImfileRowAlloc(psS64 det_id, psS32 iteration, const char *class_id, const char *uri, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    detNormalizedImfileRow *_object;
+
+    _object = psAlloc(sizeof(detNormalizedImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detNormalizedImfileRowFree);
+
+    _object->det_id = det_id;
+    _object->iteration = iteration;
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->user_1 = user_1;
+    _object->user_2 = user_2;
+    _object->user_3 = user_3;
+    _object->user_4 = user_4;
+    _object->user_5 = user_5;
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detNormalizedImfileRowFree(detNormalizedImfileRow *object)
+{
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->path_base);
+}
+
+bool detNormalizedImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id) ref detInputExp(det_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, "Primary Key fkey(det_id, iteration, class_id) ref detNormalizedStatImfile(det_id, iteration, class_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key INDEX(det_id, iteration)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETNORMALIZEDIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detNormalizedImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETNORMALIZEDIMFILE_TABLE_NAME);
+}
+
+bool detNormalizedImfileInsert(psDB * dbh, psS64 det_id, psS32 iteration, const char *class_id, const char *uri, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETNORMALIZEDIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detNormalizedImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETNORMALIZEDIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detNormalizedImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detNormalizedImfileInsertObject(psDB *dbh, detNormalizedImfileRow *object)
+{
+    return detNormalizedImfileInsert(dbh, object->det_id, object->iteration, object->class_id, object->uri, object->bg, object->bg_stdev, object->bg_mean_stdev, object->user_1, object->user_2, object->user_3, object->user_4, object->user_5, object->path_base, object->fault);
+}
+
+bool detNormalizedImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detNormalizedImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detNormalizedImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETNORMALIZEDIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETNORMALIZEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETNORMALIZEDIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETNORMALIZEDIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detNormalizedImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETNORMALIZEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETNORMALIZEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detNormalizedImfileMetadataFromObject(const detNormalizedImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, object->iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, object->user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, object->user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, object->user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, object->user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, object->user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detNormalizedImfileRow *detNormalizedImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, md, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item iteration");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF64 user_1 = psMetadataLookupF64(&status, md, "user_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_1");
+        return false;
+    }
+    psF64 user_2 = psMetadataLookupF64(&status, md, "user_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_2");
+        return false;
+    }
+    psF64 user_3 = psMetadataLookupF64(&status, md, "user_3");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_3");
+        return false;
+    }
+    psF64 user_4 = psMetadataLookupF64(&status, md, "user_4");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_4");
+        return false;
+    }
+    psF64 user_5 = psMetadataLookupF64(&status, md, "user_5");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_5");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detNormalizedImfileRowAlloc(det_id, iteration, class_id, uri, bg, bg_stdev, bg_mean_stdev, user_1, user_2, user_3, user_4, user_5, path_base, fault);
+}
+psArray *detNormalizedImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETNORMALIZEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detNormalizedImfileRow *object = detNormalizedImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detNormalizedImfileDeleteObject(psDB *dbh, const detNormalizedImfileRow *object)
+{
+    psMetadata *where = detNormalizedImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETNORMALIZEDIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detNormalizedImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detNormalizedImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detNormalizedImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detNormalizedImfileRow *object = objects->data[i];
+        psMetadata *where = detNormalizedImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETNORMALIZEDIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detNormalizedImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detNormalizedImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detNormalizedImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETNORMALIZEDIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detNormalizedImfilePrintObject(FILE *stream, detNormalizedImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detNormalizedImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detNormalizedExpRowFree(detNormalizedExpRow *object);
+
+detNormalizedExpRow *detNormalizedExpRowAlloc(psS64 det_id, psS32 iteration, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    detNormalizedExpRow *_object;
+
+    _object = psAlloc(sizeof(detNormalizedExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detNormalizedExpRowFree);
+
+    _object->det_id = det_id;
+    _object->iteration = iteration;
+    _object->recipe = psStringCopy(recipe);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->user_1 = user_1;
+    _object->user_2 = user_2;
+    _object->user_3 = user_3;
+    _object->user_4 = user_4;
+    _object->user_5 = user_5;
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detNormalizedExpRowFree(detNormalizedExpRow *object)
+{
+    psFree(object->recipe);
+    psFree(object->path_base);
+}
+
+bool detNormalizedExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, iteration) ref detInputExp(det_id, iteration)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, "Primary Key fkey(det_id, iteration) ref detNormalizedImfile(det_id, iteration)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETNORMALIZEDEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detNormalizedExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETNORMALIZEDEXP_TABLE_NAME);
+}
+
+bool detNormalizedExpInsert(psDB * dbh, psS64 det_id, psS32 iteration, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETNORMALIZEDEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detNormalizedExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETNORMALIZEDEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detNormalizedExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detNormalizedExpInsertObject(psDB *dbh, detNormalizedExpRow *object)
+{
+    return detNormalizedExpInsert(dbh, object->det_id, object->iteration, object->recipe, object->bg, object->bg_stdev, object->bg_mean_stdev, object->user_1, object->user_2, object->user_3, object->user_4, object->user_5, object->path_base, object->fault);
+}
+
+bool detNormalizedExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detNormalizedExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detNormalizedExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETNORMALIZEDEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETNORMALIZEDEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETNORMALIZEDEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETNORMALIZEDEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detNormalizedExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETNORMALIZEDEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETNORMALIZEDEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detNormalizedExpMetadataFromObject(const detNormalizedExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, object->iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, object->recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, object->user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, object->user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, object->user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, object->user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, object->user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detNormalizedExpRow *detNormalizedExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, md, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item iteration");
+        return false;
+    }
+    char* recipe = psMetadataLookupPtr(&status, md, "recipe");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item recipe");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF64 user_1 = psMetadataLookupF64(&status, md, "user_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_1");
+        return false;
+    }
+    psF64 user_2 = psMetadataLookupF64(&status, md, "user_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_2");
+        return false;
+    }
+    psF64 user_3 = psMetadataLookupF64(&status, md, "user_3");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_3");
+        return false;
+    }
+    psF64 user_4 = psMetadataLookupF64(&status, md, "user_4");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_4");
+        return false;
+    }
+    psF64 user_5 = psMetadataLookupF64(&status, md, "user_5");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_5");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detNormalizedExpRowAlloc(det_id, iteration, recipe, bg, bg_stdev, bg_mean_stdev, user_1, user_2, user_3, user_4, user_5, path_base, fault);
+}
+psArray *detNormalizedExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETNORMALIZEDEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detNormalizedExpRow *object = detNormalizedExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detNormalizedExpDeleteObject(psDB *dbh, const detNormalizedExpRow *object)
+{
+    psMetadata *where = detNormalizedExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETNORMALIZEDEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detNormalizedExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detNormalizedExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detNormalizedExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detNormalizedExpRow *object = objects->data[i];
+        psMetadata *where = detNormalizedExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETNORMALIZEDEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detNormalizedExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detNormalizedExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detNormalizedExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETNORMALIZEDEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detNormalizedExpPrintObject(FILE *stream, detNormalizedExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detNormalizedExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detResidImfileRowFree(detResidImfileRow *object);
+
+detResidImfileRow *detResidImfileRowAlloc(psS64 det_id, psS32 iteration, psS64 exp_id, const char *class_id, const char *uri, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 bg_skewness, psF64 bg_kurtosis, psF64 bin_stdev, psF64 fringe_0, psF64 fringe_1, psF64 fringe_2, psF64 fringe_resid_0, psF64 fringe_resid_1, psF64 fringe_resid_2, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    detResidImfileRow *_object;
+
+    _object = psAlloc(sizeof(detResidImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detResidImfileRowFree);
+
+    _object->det_id = det_id;
+    _object->iteration = iteration;
+    _object->exp_id = exp_id;
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->recipe = psStringCopy(recipe);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->bg_skewness = bg_skewness;
+    _object->bg_kurtosis = bg_kurtosis;
+    _object->bin_stdev = bin_stdev;
+    _object->fringe_0 = fringe_0;
+    _object->fringe_1 = fringe_1;
+    _object->fringe_2 = fringe_2;
+    _object->fringe_resid_0 = fringe_resid_0;
+    _object->fringe_resid_1 = fringe_resid_1;
+    _object->fringe_resid_2 = fringe_resid_2;
+    _object->user_1 = user_1;
+    _object->user_2 = user_2;
+    _object->user_3 = user_3;
+    _object->user_4 = user_4;
+    _object->user_5 = user_5;
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detResidImfileRowFree(detResidImfileRow *object)
+{
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->recipe);
+    psFree(object->path_base);
+}
+
+bool detResidImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, iteration, exp_id) ref detInputExp(det_id, iteration, exp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, "Primary Key fkey(det_id, exp_id, class_id) ref detProcessedImfile(det_id, exp_id, class_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key fkey(det_id, iteration) ref detNormalizedExp(det_id, iteration)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key INDEX(det_id, iteration, exp_id)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_skewness", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_skewness");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_kurtosis", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_kurtosis");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bin_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bin_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_0", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETRESIDIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detResidImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETRESIDIMFILE_TABLE_NAME);
+}
+
+bool detResidImfileInsert(psDB * dbh, psS64 det_id, psS32 iteration, psS64 exp_id, const char *class_id, const char *uri, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 bg_skewness, psF64 bg_kurtosis, psF64 bin_stdev, psF64 fringe_0, psF64 fringe_1, psF64 fringe_2, psF64 fringe_resid_0, psF64 fringe_resid_1, psF64 fringe_resid_2, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_skewness", PS_DATA_F64, NULL, bg_skewness)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_skewness");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_kurtosis", PS_DATA_F64, NULL, bg_kurtosis)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_kurtosis");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bin_stdev", PS_DATA_F64, NULL, bin_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bin_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_0", PS_DATA_F64, NULL, fringe_resid_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_1", PS_DATA_F64, NULL, fringe_resid_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_2", PS_DATA_F64, NULL, fringe_resid_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETRESIDIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detResidImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETRESIDIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detResidImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detResidImfileInsertObject(psDB *dbh, detResidImfileRow *object)
+{
+    return detResidImfileInsert(dbh, object->det_id, object->iteration, object->exp_id, object->class_id, object->uri, object->recipe, object->bg, object->bg_stdev, object->bg_mean_stdev, object->bg_skewness, object->bg_kurtosis, object->bin_stdev, object->fringe_0, object->fringe_1, object->fringe_2, object->fringe_resid_0, object->fringe_resid_1, object->fringe_resid_2, object->user_1, object->user_2, object->user_3, object->user_4, object->user_5, object->path_base, object->fault);
+}
+
+bool detResidImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detResidImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detResidImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETRESIDIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETRESIDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETRESIDIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETRESIDIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detResidImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETRESIDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETRESIDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detResidImfileMetadataFromObject(const detResidImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, object->iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, object->recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_skewness", PS_DATA_F64, NULL, object->bg_skewness)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_skewness");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_kurtosis", PS_DATA_F64, NULL, object->bg_kurtosis)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_kurtosis");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bin_stdev", PS_DATA_F64, NULL, object->bin_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bin_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, object->fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, object->fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, object->fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_0", PS_DATA_F64, NULL, object->fringe_resid_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_1", PS_DATA_F64, NULL, object->fringe_resid_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_2", PS_DATA_F64, NULL, object->fringe_resid_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, object->user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, object->user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, object->user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, object->user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, object->user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detResidImfileRow *detResidImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, md, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item iteration");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    char* recipe = psMetadataLookupPtr(&status, md, "recipe");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item recipe");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF64 bg_skewness = psMetadataLookupF64(&status, md, "bg_skewness");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_skewness");
+        return false;
+    }
+    psF64 bg_kurtosis = psMetadataLookupF64(&status, md, "bg_kurtosis");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_kurtosis");
+        return false;
+    }
+    psF64 bin_stdev = psMetadataLookupF64(&status, md, "bin_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bin_stdev");
+        return false;
+    }
+    psF64 fringe_0 = psMetadataLookupF64(&status, md, "fringe_0");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_0");
+        return false;
+    }
+    psF64 fringe_1 = psMetadataLookupF64(&status, md, "fringe_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_1");
+        return false;
+    }
+    psF64 fringe_2 = psMetadataLookupF64(&status, md, "fringe_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_2");
+        return false;
+    }
+    psF64 fringe_resid_0 = psMetadataLookupF64(&status, md, "fringe_resid_0");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_resid_0");
+        return false;
+    }
+    psF64 fringe_resid_1 = psMetadataLookupF64(&status, md, "fringe_resid_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_resid_1");
+        return false;
+    }
+    psF64 fringe_resid_2 = psMetadataLookupF64(&status, md, "fringe_resid_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_resid_2");
+        return false;
+    }
+    psF64 user_1 = psMetadataLookupF64(&status, md, "user_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_1");
+        return false;
+    }
+    psF64 user_2 = psMetadataLookupF64(&status, md, "user_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_2");
+        return false;
+    }
+    psF64 user_3 = psMetadataLookupF64(&status, md, "user_3");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_3");
+        return false;
+    }
+    psF64 user_4 = psMetadataLookupF64(&status, md, "user_4");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_4");
+        return false;
+    }
+    psF64 user_5 = psMetadataLookupF64(&status, md, "user_5");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_5");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detResidImfileRowAlloc(det_id, iteration, exp_id, class_id, uri, recipe, bg, bg_stdev, bg_mean_stdev, bg_skewness, bg_kurtosis, bin_stdev, fringe_0, fringe_1, fringe_2, fringe_resid_0, fringe_resid_1, fringe_resid_2, user_1, user_2, user_3, user_4, user_5, path_base, fault);
+}
+psArray *detResidImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETRESIDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detResidImfileRow *object = detResidImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detResidImfileDeleteObject(psDB *dbh, const detResidImfileRow *object)
+{
+    psMetadata *where = detResidImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETRESIDIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detResidImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detResidImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detResidImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detResidImfileRow *object = objects->data[i];
+        psMetadata *where = detResidImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETRESIDIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detResidImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detResidImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detResidImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETRESIDIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detResidImfilePrintObject(FILE *stream, detResidImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detResidImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detResidExpRowFree(detResidExpRow *object);
+
+detResidExpRow *detResidExpRowAlloc(psS64 det_id, psS32 iteration, psS64 exp_id, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 bg_skewness, psF64 bg_kurtosis, psF64 bin_stdev, psF64 fringe_0, psF64 fringe_1, psF64 fringe_2, psF64 fringe_resid_0, psF64 fringe_resid_1, psF64 fringe_resid_2, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, bool accept, psS16 fault)
+{
+    detResidExpRow  *_object;
+
+    _object = psAlloc(sizeof(detResidExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detResidExpRowFree);
+
+    _object->det_id = det_id;
+    _object->iteration = iteration;
+    _object->exp_id = exp_id;
+    _object->recipe = psStringCopy(recipe);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->bg_skewness = bg_skewness;
+    _object->bg_kurtosis = bg_kurtosis;
+    _object->bin_stdev = bin_stdev;
+    _object->fringe_0 = fringe_0;
+    _object->fringe_1 = fringe_1;
+    _object->fringe_2 = fringe_2;
+    _object->fringe_resid_0 = fringe_resid_0;
+    _object->fringe_resid_1 = fringe_resid_1;
+    _object->fringe_resid_2 = fringe_resid_2;
+    _object->user_1 = user_1;
+    _object->user_2 = user_2;
+    _object->user_3 = user_3;
+    _object->user_4 = user_4;
+    _object->user_5 = user_5;
+    _object->path_base = psStringCopy(path_base);
+    _object->accept = accept;
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detResidExpRowFree(detResidExpRow *object)
+{
+    psFree(object->recipe);
+    psFree(object->path_base);
+}
+
+bool detResidExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, iteration, exp_id) ref detInputExp(det_id, iteration, exp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, "Primary Key fkey(det_id, iteration, exp_id) ref detResidImfile(det_id, iteration, exp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key INDEX(det_id, iteration)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_skewness", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_skewness");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_kurtosis", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_kurtosis");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bin_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bin_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_0", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "accept", PS_DATA_BOOL, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item accept");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETRESIDEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detResidExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETRESIDEXP_TABLE_NAME);
+}
+
+bool detResidExpInsert(psDB * dbh, psS64 det_id, psS32 iteration, psS64 exp_id, const char *recipe, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 bg_skewness, psF64 bg_kurtosis, psF64 bin_stdev, psF64 fringe_0, psF64 fringe_1, psF64 fringe_2, psF64 fringe_resid_0, psF64 fringe_resid_1, psF64 fringe_resid_2, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, bool accept, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_skewness", PS_DATA_F64, NULL, bg_skewness)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_skewness");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_kurtosis", PS_DATA_F64, NULL, bg_kurtosis)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_kurtosis");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bin_stdev", PS_DATA_F64, NULL, bin_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bin_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_0", PS_DATA_F64, NULL, fringe_resid_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_1", PS_DATA_F64, NULL, fringe_resid_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_2", PS_DATA_F64, NULL, fringe_resid_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "accept", PS_DATA_BOOL, NULL, accept)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item accept");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETRESIDEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detResidExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETRESIDEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detResidExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detResidExpInsertObject(psDB *dbh, detResidExpRow *object)
+{
+    return detResidExpInsert(dbh, object->det_id, object->iteration, object->exp_id, object->recipe, object->bg, object->bg_stdev, object->bg_mean_stdev, object->bg_skewness, object->bg_kurtosis, object->bin_stdev, object->fringe_0, object->fringe_1, object->fringe_2, object->fringe_resid_0, object->fringe_resid_1, object->fringe_resid_2, object->user_1, object->user_2, object->user_3, object->user_4, object->user_5, object->path_base, object->accept, object->fault);
+}
+
+bool detResidExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detResidExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detResidExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETRESIDEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETRESIDEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETRESIDEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETRESIDEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detResidExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETRESIDEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETRESIDEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detResidExpMetadataFromObject(const detResidExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, object->iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, object->recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_skewness", PS_DATA_F64, NULL, object->bg_skewness)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_skewness");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_kurtosis", PS_DATA_F64, NULL, object->bg_kurtosis)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_kurtosis");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bin_stdev", PS_DATA_F64, NULL, object->bin_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bin_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_0", PS_DATA_F64, NULL, object->fringe_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_1", PS_DATA_F64, NULL, object->fringe_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_2", PS_DATA_F64, NULL, object->fringe_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_0", PS_DATA_F64, NULL, object->fringe_resid_0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_0");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_1", PS_DATA_F64, NULL, object->fringe_resid_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fringe_resid_2", PS_DATA_F64, NULL, object->fringe_resid_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fringe_resid_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, object->user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, object->user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, object->user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, object->user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, object->user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "accept", PS_DATA_BOOL, NULL, object->accept)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item accept");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detResidExpRow *detResidExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, md, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item iteration");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* recipe = psMetadataLookupPtr(&status, md, "recipe");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item recipe");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF64 bg_skewness = psMetadataLookupF64(&status, md, "bg_skewness");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_skewness");
+        return false;
+    }
+    psF64 bg_kurtosis = psMetadataLookupF64(&status, md, "bg_kurtosis");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_kurtosis");
+        return false;
+    }
+    psF64 bin_stdev = psMetadataLookupF64(&status, md, "bin_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bin_stdev");
+        return false;
+    }
+    psF64 fringe_0 = psMetadataLookupF64(&status, md, "fringe_0");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_0");
+        return false;
+    }
+    psF64 fringe_1 = psMetadataLookupF64(&status, md, "fringe_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_1");
+        return false;
+    }
+    psF64 fringe_2 = psMetadataLookupF64(&status, md, "fringe_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_2");
+        return false;
+    }
+    psF64 fringe_resid_0 = psMetadataLookupF64(&status, md, "fringe_resid_0");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_resid_0");
+        return false;
+    }
+    psF64 fringe_resid_1 = psMetadataLookupF64(&status, md, "fringe_resid_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_resid_1");
+        return false;
+    }
+    psF64 fringe_resid_2 = psMetadataLookupF64(&status, md, "fringe_resid_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fringe_resid_2");
+        return false;
+    }
+    psF64 user_1 = psMetadataLookupF64(&status, md, "user_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_1");
+        return false;
+    }
+    psF64 user_2 = psMetadataLookupF64(&status, md, "user_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_2");
+        return false;
+    }
+    psF64 user_3 = psMetadataLookupF64(&status, md, "user_3");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_3");
+        return false;
+    }
+    psF64 user_4 = psMetadataLookupF64(&status, md, "user_4");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_4");
+        return false;
+    }
+    psF64 user_5 = psMetadataLookupF64(&status, md, "user_5");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_5");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    bool accept = psMetadataLookupBool(&status, md, "accept");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item accept");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detResidExpRowAlloc(det_id, iteration, exp_id, recipe, bg, bg_stdev, bg_mean_stdev, bg_skewness, bg_kurtosis, bin_stdev, fringe_0, fringe_1, fringe_2, fringe_resid_0, fringe_resid_1, fringe_resid_2, user_1, user_2, user_3, user_4, user_5, path_base, accept, fault);
+}
+psArray *detResidExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETRESIDEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detResidExpRow *object = detResidExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detResidExpDeleteObject(psDB *dbh, const detResidExpRow *object)
+{
+    psMetadata *where = detResidExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETRESIDEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detResidExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detResidExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detResidExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detResidExpRow *object = objects->data[i];
+        psMetadata *where = detResidExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETRESIDEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detResidExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detResidExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detResidExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETRESIDEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detResidExpPrintObject(FILE *stream, detResidExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detResidExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detRunSummaryRowFree(detRunSummaryRow *object);
+
+detRunSummaryRow *detRunSummaryRowAlloc(psS64 det_id, psS32 iteration, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, bool accept, psS16 fault)
+{
+    detRunSummaryRow *_object;
+
+    _object = psAlloc(sizeof(detRunSummaryRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detRunSummaryRowFree);
+
+    _object->det_id = det_id;
+    _object->iteration = iteration;
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->accept = accept;
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detRunSummaryRowFree(detRunSummaryRow *object)
+{
+}
+
+bool detRunSummaryCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, iteration) ref detInputExp(det_id, iteration)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, "Primary Key fkey(det_id, iteration) ref detResidExp(det_id, iteration)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "accept", PS_DATA_BOOL, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item accept");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETRUNSUMMARY_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detRunSummaryDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETRUNSUMMARY_TABLE_NAME);
+}
+
+bool detRunSummaryInsert(psDB * dbh, psS64 det_id, psS32 iteration, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, bool accept, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "accept", PS_DATA_BOOL, NULL, accept)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item accept");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETRUNSUMMARY_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detRunSummaryDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETRUNSUMMARY_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detRunSummary");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detRunSummaryInsertObject(psDB *dbh, detRunSummaryRow *object)
+{
+    return detRunSummaryInsert(dbh, object->det_id, object->iteration, object->bg, object->bg_stdev, object->bg_mean_stdev, object->accept, object->fault);
+}
+
+bool detRunSummaryInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detRunSummaryInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detRunSummaryInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETRUNSUMMARY_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETRUNSUMMARY_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETRUNSUMMARY_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETRUNSUMMARY_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detRunSummarySelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETRUNSUMMARY_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETRUNSUMMARY_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detRunSummaryMetadataFromObject(const detRunSummaryRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, object->iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "accept", PS_DATA_BOOL, NULL, object->accept)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item accept");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detRunSummaryRow *detRunSummaryObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, md, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item iteration");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    bool accept = psMetadataLookupBool(&status, md, "accept");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item accept");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detRunSummaryRowAlloc(det_id, iteration, bg, bg_stdev, bg_mean_stdev, accept, fault);
+}
+psArray *detRunSummarySelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETRUNSUMMARY_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detRunSummaryRow *object = detRunSummaryObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detRunSummaryDeleteObject(psDB *dbh, const detRunSummaryRow *object)
+{
+    psMetadata *where = detRunSummaryMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETRUNSUMMARY_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detRunSummary");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detRunSummaryRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detRunSummaryDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detRunSummaryRow *object = objects->data[i];
+        psMetadata *where = detRunSummaryMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETRUNSUMMARY_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detRunSummary");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detRunSummaryPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detRunSummaryMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETRUNSUMMARY_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detRunSummaryPrintObject(FILE *stream, detRunSummaryRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detRunSummaryMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detRegisteredImfileRowFree(detRegisteredImfileRow *object);
+
+detRegisteredImfileRow *detRegisteredImfileRowAlloc(psS64 det_id, psS32 iteration, const char *class_id, const char *uri, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    detRegisteredImfileRow *_object;
+
+    _object = psAlloc(sizeof(detRegisteredImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detRegisteredImfileRowFree);
+
+    _object->det_id = det_id;
+    _object->iteration = iteration;
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+    _object->bg_mean_stdev = bg_mean_stdev;
+    _object->user_1 = user_1;
+    _object->user_2 = user_2;
+    _object->user_3 = user_3;
+    _object->user_4 = user_4;
+    _object->user_5 = user_5;
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detRegisteredImfileRowFree(detRegisteredImfileRow *object)
+{
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->path_base);
+}
+
+bool detRegisteredImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, iteration) ref detRun(det_id, iteration)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, "Primary Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETREGISTEREDIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detRegisteredImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETREGISTEREDIMFILE_TABLE_NAME);
+}
+
+bool detRegisteredImfileInsert(psDB * dbh, psS64 det_id, psS32 iteration, const char *class_id, const char *uri, psF64 bg, psF64 bg_stdev, psF64 bg_mean_stdev, psF64 user_1, psF64 user_2, psF64 user_3, psF64 user_4, psF64 user_5, const char *path_base, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETREGISTEREDIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detRegisteredImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETREGISTEREDIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detRegisteredImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detRegisteredImfileInsertObject(psDB *dbh, detRegisteredImfileRow *object)
+{
+    return detRegisteredImfileInsert(dbh, object->det_id, object->iteration, object->class_id, object->uri, object->bg, object->bg_stdev, object->bg_mean_stdev, object->user_1, object->user_2, object->user_3, object->user_4, object->user_5, object->path_base, object->fault);
+}
+
+bool detRegisteredImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detRegisteredImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detRegisteredImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETREGISTEREDIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETREGISTEREDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETREGISTEREDIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETREGISTEREDIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detRegisteredImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETREGISTEREDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETREGISTEREDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detRegisteredImfileMetadataFromObject(const detRegisteredImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "iteration", PS_DATA_S32, NULL, object->iteration)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_mean_stdev", PS_DATA_F64, NULL, object->bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_1", PS_DATA_F64, NULL, object->user_1)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_1");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_2", PS_DATA_F64, NULL, object->user_2)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_2");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_3", PS_DATA_F64, NULL, object->user_3)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_3");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_4", PS_DATA_F64, NULL, object->user_4)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_4");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "user_5", PS_DATA_F64, NULL, object->user_5)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item user_5");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detRegisteredImfileRow *detRegisteredImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS32 iteration = psMetadataLookupS32(&status, md, "iteration");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item iteration");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, md, "bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_mean_stdev");
+        return false;
+    }
+    psF64 user_1 = psMetadataLookupF64(&status, md, "user_1");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_1");
+        return false;
+    }
+    psF64 user_2 = psMetadataLookupF64(&status, md, "user_2");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_2");
+        return false;
+    }
+    psF64 user_3 = psMetadataLookupF64(&status, md, "user_3");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_3");
+        return false;
+    }
+    psF64 user_4 = psMetadataLookupF64(&status, md, "user_4");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_4");
+        return false;
+    }
+    psF64 user_5 = psMetadataLookupF64(&status, md, "user_5");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item user_5");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detRegisteredImfileRowAlloc(det_id, iteration, class_id, uri, bg, bg_stdev, bg_mean_stdev, user_1, user_2, user_3, user_4, user_5, path_base, fault);
+}
+psArray *detRegisteredImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETREGISTEREDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detRegisteredImfileRow *object = detRegisteredImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detRegisteredImfileDeleteObject(psDB *dbh, const detRegisteredImfileRow *object)
+{
+    psMetadata *where = detRegisteredImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETREGISTEREDIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detRegisteredImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detRegisteredImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detRegisteredImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detRegisteredImfileRow *object = objects->data[i];
+        psMetadata *where = detRegisteredImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETREGISTEREDIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detRegisteredImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detRegisteredImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detRegisteredImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETREGISTEREDIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detRegisteredImfilePrintObject(FILE *stream, detRegisteredImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detRegisteredImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detCorrectedExpRowFree(detCorrectedExpRow *object);
+
+detCorrectedExpRow *detCorrectedExpRowAlloc(psS64 det_id, psS64 exp_id, const char *uri, psS64 corr_id, const char *corr_type, const char *recipe, const char *path_base, psS16 fault)
+{
+    detCorrectedExpRow *_object;
+
+    _object = psAlloc(sizeof(detCorrectedExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detCorrectedExpRowFree);
+
+    _object->det_id = det_id;
+    _object->exp_id = exp_id;
+    _object->uri = psStringCopy(uri);
+    _object->corr_id = corr_id;
+    _object->corr_type = psStringCopy(corr_type);
+    _object->recipe = psStringCopy(recipe);
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detCorrectedExpRowFree(detCorrectedExpRow *object)
+{
+    psFree(object->uri);
+    psFree(object->corr_type);
+    psFree(object->recipe);
+    psFree(object->path_base);
+}
+
+bool detCorrectedExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, exp_id) ref detInputExp(det_id, exp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key fkey(exp_id, class_id) ref rawImfile(exp_id, class_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, "INDEX(det_id, exp_id)", "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_id", PS_DATA_S64, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_type", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETCORRECTEDEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detCorrectedExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETCORRECTEDEXP_TABLE_NAME);
+}
+
+bool detCorrectedExpInsert(psDB * dbh, psS64 det_id, psS64 exp_id, const char *uri, psS64 corr_id, const char *corr_type, const char *recipe, const char *path_base, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_id", PS_DATA_S64, NULL, corr_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_type", PS_DATA_STRING, NULL, corr_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETCORRECTEDEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detCorrectedExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETCORRECTEDEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detCorrectedExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detCorrectedExpInsertObject(psDB *dbh, detCorrectedExpRow *object)
+{
+    return detCorrectedExpInsert(dbh, object->det_id, object->exp_id, object->uri, object->corr_id, object->corr_type, object->recipe, object->path_base, object->fault);
+}
+
+bool detCorrectedExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detCorrectedExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detCorrectedExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETCORRECTEDEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETCORRECTEDEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETCORRECTEDEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETCORRECTEDEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detCorrectedExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETCORRECTEDEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETCORRECTEDEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detCorrectedExpMetadataFromObject(const detCorrectedExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_id", PS_DATA_S64, NULL, object->corr_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_type", PS_DATA_STRING, NULL, object->corr_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "recipe", PS_DATA_STRING, NULL, object->recipe)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detCorrectedExpRow *detCorrectedExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psS64 corr_id = psMetadataLookupS64(&status, md, "corr_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item corr_id");
+        return false;
+    }
+    char* corr_type = psMetadataLookupPtr(&status, md, "corr_type");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item corr_type");
+        return false;
+    }
+    char* recipe = psMetadataLookupPtr(&status, md, "recipe");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item recipe");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detCorrectedExpRowAlloc(det_id, exp_id, uri, corr_id, corr_type, recipe, path_base, fault);
+}
+psArray *detCorrectedExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETCORRECTEDEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detCorrectedExpRow *object = detCorrectedExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detCorrectedExpDeleteObject(psDB *dbh, const detCorrectedExpRow *object)
+{
+    psMetadata *where = detCorrectedExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETCORRECTEDEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detCorrectedExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detCorrectedExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detCorrectedExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detCorrectedExpRow *object = objects->data[i];
+        psMetadata *where = detCorrectedExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETCORRECTEDEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detCorrectedExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detCorrectedExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detCorrectedExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETCORRECTEDEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detCorrectedExpPrintObject(FILE *stream, detCorrectedExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detCorrectedExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void detCorrectedImfileRowFree(detCorrectedImfileRow *object);
+
+detCorrectedImfileRow *detCorrectedImfileRowAlloc(psS64 det_id, psS64 exp_id, const char *class_id, const char *uri, const char *path_base, psS16 fault)
+{
+    detCorrectedImfileRow *_object;
+
+    _object = psAlloc(sizeof(detCorrectedImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)detCorrectedImfileRowFree);
+
+    _object->det_id = det_id;
+    _object->exp_id = exp_id;
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
+    _object->path_base = psStringCopy(path_base);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void detCorrectedImfileRowFree(detCorrectedImfileRow *object)
+{
+    psFree(object->class_id);
+    psFree(object->uri);
+    psFree(object->path_base);
+}
+
+bool detCorrectedImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, "Primary Key fkey(det_id, exp_id) ref detInputExp(det_id, exp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Primary Key fkey(exp_id, class_id) ref rawImfile(exp_id, class_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key INDEX(det_id, class_id)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, "INDEX(det_id, exp_id)", "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key NOT NULL", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DETCORRECTEDIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool detCorrectedImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DETCORRECTEDIMFILE_TABLE_NAME);
+}
+
+bool detCorrectedImfileInsert(psDB * dbh, psS64 det_id, psS64 exp_id, const char *class_id, const char *uri, const char *path_base, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DETCORRECTEDIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long detCorrectedImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DETCORRECTEDIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detCorrectedImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detCorrectedImfileInsertObject(psDB *dbh, detCorrectedImfileRow *object)
+{
+    return detCorrectedImfileInsert(dbh, object->det_id, object->exp_id, object->class_id, object->uri, object->path_base, object->fault);
+}
+
+bool detCorrectedImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!detCorrectedImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool detCorrectedImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DETCORRECTEDIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DETCORRECTEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DETCORRECTEDIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, DETCORRECTEDIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool detCorrectedImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DETCORRECTEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DETCORRECTEDIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *detCorrectedImfileMetadataFromObject(const detCorrectedImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "det_id", PS_DATA_S64, NULL, object->det_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "path_base", PS_DATA_STRING, NULL, object->path_base)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+detCorrectedImfileRow *detCorrectedImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 det_id = psMetadataLookupS64(&status, md, "det_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item det_id");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    char* path_base = psMetadataLookupPtr(&status, md, "path_base");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item path_base");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return detCorrectedImfileRowAlloc(det_id, exp_id, class_id, uri, path_base, fault);
+}
+psArray *detCorrectedImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DETCORRECTEDIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        detCorrectedImfileRow *object = detCorrectedImfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool detCorrectedImfileDeleteObject(psDB *dbh, const detCorrectedImfileRow *object)
+{
+    psMetadata *where = detCorrectedImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DETCORRECTEDIMFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from detCorrectedImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "detCorrectedImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long detCorrectedImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        detCorrectedImfileRow *object = objects->data[i];
+        psMetadata *where = detCorrectedImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DETCORRECTEDIMFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from detCorrectedImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool detCorrectedImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = detCorrectedImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DETCORRECTEDIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool detCorrectedImfilePrintObject(FILE *stream, detCorrectedImfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = detCorrectedImfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void magicRunRowFree(magicRunRow *object);
+
+magicRunRow *magicRunRowAlloc(psS64 magic_id, psS64 exp_id, const char *state, const char *workdir, const char *workdir_state, const char *label, const char *dvodb, psTime* registered, psS16 fault)
+{
+    magicRunRow     *_object;
+
+    _object = psAlloc(sizeof(magicRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)magicRunRowFree);
+
+    _object->magic_id = magic_id;
+    _object->exp_id = exp_id;
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->workdir_state = psStringCopy(workdir_state);
+    _object->label = psStringCopy(label);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->registered = psTimeCopy(registered);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void magicRunRowFree(magicRunRow *object)
+{
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->workdir_state);
+    psFree(object->label);
+    psFree(object->dvodb);
+    psFree(object->registered);
+}
+
+bool magicRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, "Key", "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, "key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, MAGICRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool magicRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, MAGICRUN_TABLE_NAME);
+}
+
+bool magicRunInsert(psDB * dbh, psS64 magic_id, psS64 exp_id, const char *state, const char *workdir, const char *workdir_state, const char *label, const char *dvodb, psTime* registered, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, NULL, magic_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, NULL, workdir_state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, MAGICRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long magicRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, MAGICRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool magicRunInsertObject(psDB *dbh, magicRunRow *object)
+{
+    return magicRunInsert(dbh, object->magic_id, object->exp_id, object->state, object->workdir, object->workdir_state, object->label, object->dvodb, object->registered, object->fault);
+}
+
+bool magicRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!magicRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool magicRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  MAGICRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, MAGICRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", MAGICRUN_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, MAGICRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool magicRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, MAGICRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, MAGICRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *magicRunMetadataFromObject(const magicRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, NULL, object->magic_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_S64, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir_state", PS_DATA_STRING, NULL, object->workdir_state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir_state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+magicRunRow *magicRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 magic_id = psMetadataLookupS64(&status, md, "magic_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item magic_id");
+        return false;
+    }
+    psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* workdir_state = psMetadataLookupPtr(&status, md, "workdir_state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir_state");
+        return false;
+    }
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return magicRunRowAlloc(magic_id, exp_id, state, workdir, workdir_state, label, dvodb, registered, fault);
+}
+psArray *magicRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, MAGICRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        magicRunRow *object = magicRunObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool magicRunDeleteObject(psDB *dbh, const magicRunRow *object)
+{
+    psMetadata *where = magicRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, MAGICRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicRun");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "magicRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long magicRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        magicRunRow *object = objects->data[i];
+        psMetadata *where = magicRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, MAGICRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool magicRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = magicRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            MAGICRUN_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool magicRunPrintObject(FILE *stream, magicRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = magicRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void magicInputSkyfileRowFree(magicInputSkyfileRow *object);
+
+magicInputSkyfileRow *magicInputSkyfileRowAlloc(psS64 magic_id, psS64 diff_id, const char *node)
+{
+    magicInputSkyfileRow *_object;
+
+    _object = psAlloc(sizeof(magicInputSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)magicInputSkyfileRowFree);
+
+    _object->magic_id = magic_id;
+    _object->diff_id = diff_id;
+    _object->node = psStringCopy(node);
+
+    return _object;
+}
+
+static void magicInputSkyfileRowFree(magicInputSkyfileRow *object)
+{
+    psFree(object->node);
+}
+
+bool magicInputSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, "Primary Key fkey(magic_id) ref magicRun(magic_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key fkey(diff_id) ref diffRun(diff_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "node", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item node");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, MAGICINPUTSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool magicInputSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, MAGICINPUTSKYFILE_TABLE_NAME);
+}
+
+bool magicInputSkyfileInsert(psDB * dbh, psS64 magic_id, psS64 diff_id, const char *node)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, NULL, magic_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "node", PS_DATA_STRING, NULL, node)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item node");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, MAGICINPUTSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long magicInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, MAGICINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicInputSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool magicInputSkyfileInsertObject(psDB *dbh, magicInputSkyfileRow *object)
+{
+    return magicInputSkyfileInsert(dbh, object->magic_id, object->diff_id, object->node);
+}
+
+bool magicInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!magicInputSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool magicInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  MAGICINPUTSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, MAGICINPUTSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", MAGICINPUTSKYFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, MAGICINPUTSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool magicInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, MAGICINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, MAGICINPUTSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *magicInputSkyfileMetadataFromObject(const magicInputSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, NULL, object->magic_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "node", PS_DATA_STRING, NULL, object->node)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item node");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+magicInputSkyfileRow *magicInputSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 magic_id = psMetadataLookupS64(&status, md, "magic_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item magic_id");
+        return false;
+    }
+    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
+        return false;
+    }
+    char* node = psMetadataLookupPtr(&status, md, "node");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item node");
+        return false;
+    }
+
+    return magicInputSkyfileRowAlloc(magic_id, diff_id, node);
+}
+psArray *magicInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, MAGICINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        magicInputSkyfileRow *object = magicInputSkyfileObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool magicInputSkyfileDeleteObject(psDB *dbh, const magicInputSkyfileRow *object)
+{
+    psMetadata *where = magicInputSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, MAGICINPUTSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicInputSkyfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "magicInputSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long magicInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        magicInputSkyfileRow *object = objects->data[i];
+        psMetadata *where = magicInputSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, MAGICINPUTSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicInputSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool magicInputSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = magicInputSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            MAGICINPUTSKYFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool magicInputSkyfilePrintObject(FILE *stream, magicInputSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = magicInputSkyfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void magicTreeRowFree(magicTreeRow *object);
+
+magicTreeRow *magicTreeRowAlloc(psS64 magic_id, const char *node, const char *dep)
+{
+    magicTreeRow    *_object;
+
+    _object = psAlloc(sizeof(magicTreeRow));
+    psMemSetDeallocator(_object, (psFreeFunc)magicTreeRowFree);
+
+    _object->magic_id = magic_id;
+    _object->node = psStringCopy(node);
+    _object->dep = psStringCopy(dep);
+
+    return _object;
+}
+
+static void magicTreeRowFree(magicTreeRow *object)
+{
+    psFree(object->node);
+    psFree(object->dep);
+}
+
+bool magicTreeCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, "Key fkey(magic_id) ref magicRun(magic_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "node", PS_DATA_STRING, "Key INDEX(magic_id, node)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item node");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dep", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dep");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, MAGICTREE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool magicTreeDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, MAGICTREE_TABLE_NAME);
+}
+
+bool magicTreeInsert(psDB * dbh, psS64 magic_id, const char *node, const char *dep)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, NULL, magic_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "node", PS_DATA_STRING, NULL, node)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item node");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dep", PS_DATA_STRING, NULL, dep)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dep");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, MAGICTREE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long magicTreeDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, MAGICTREE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicTree");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool magicTreeInsertObject(psDB *dbh, magicTreeRow *object)
+{
+    return magicTreeInsert(dbh, object->magic_id, object->node, object->dep);
+}
+
+bool magicTreeInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!magicTreeInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool magicTreeInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  MAGICTREE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, MAGICTREE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", MAGICTREE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, MAGICTREE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool magicTreeSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, MAGICTREE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, MAGICTREE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *magicTreeMetadataFromObject(const magicTreeRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, NULL, object->magic_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "node", PS_DATA_STRING, NULL, object->node)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item node");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dep", PS_DATA_STRING, NULL, object->dep)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dep");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+magicTreeRow *magicTreeObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 magic_id = psMetadataLookupS64(&status, md, "magic_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item magic_id");
+        return false;
+    }
+    char* node = psMetadataLookupPtr(&status, md, "node");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item node");
+        return false;
+    }
+    char* dep = psMetadataLookupPtr(&status, md, "dep");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dep");
+        return false;
+    }
+
+    return magicTreeRowAlloc(magic_id, node, dep);
+}
+psArray *magicTreeSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, MAGICTREE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        magicTreeRow *object = magicTreeObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool magicTreeDeleteObject(psDB *dbh, const magicTreeRow *object)
+{
+    psMetadata *where = magicTreeMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, MAGICTREE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicTree");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "magicTreeRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long magicTreeDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        magicTreeRow *object = objects->data[i];
+        psMetadata *where = magicTreeMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, MAGICTREE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicTree");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool magicTreePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = magicTreeMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            MAGICTREE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool magicTreePrintObject(FILE *stream, magicTreeRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = magicTreeMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void magicNodeResultRowFree(magicNodeResultRow *object);
+
+magicNodeResultRow *magicNodeResultRowAlloc(psS64 magic_id, const char *node, const char *uri, psS16 fault)
+{
+    magicNodeResultRow *_object;
+
+    _object = psAlloc(sizeof(magicNodeResultRow));
+    psMemSetDeallocator(_object, (psFreeFunc)magicNodeResultRowFree);
+
+    _object->magic_id = magic_id;
+    _object->node = psStringCopy(node);
+    _object->uri = psStringCopy(uri);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void magicNodeResultRowFree(magicNodeResultRow *object)
+{
+    psFree(object->node);
+    psFree(object->uri);
+}
+
+bool magicNodeResultCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, "Primary Key fkey(magic_id) ref magicRun(magic_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "node", PS_DATA_STRING, "Primary Key fkey(magic_id, node) ref magicTree(magic_id, node)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item node");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, MAGICNODERESULT_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool magicNodeResultDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, MAGICNODERESULT_TABLE_NAME);
+}
+
+bool magicNodeResultInsert(psDB * dbh, psS64 magic_id, const char *node, const char *uri, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, NULL, magic_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "node", PS_DATA_STRING, NULL, node)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item node");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, MAGICNODERESULT_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long magicNodeResultDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, MAGICNODERESULT_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicNodeResult");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool magicNodeResultInsertObject(psDB *dbh, magicNodeResultRow *object)
+{
+    return magicNodeResultInsert(dbh, object->magic_id, object->node, object->uri, object->fault);
+}
+
+bool magicNodeResultInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!magicNodeResultInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool magicNodeResultInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  MAGICNODERESULT_TABLE_NAME
+    if (!psFitsMoveExtName(fits, MAGICNODERESULT_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", MAGICNODERESULT_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, MAGICNODERESULT_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool magicNodeResultSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, MAGICNODERESULT_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, MAGICNODERESULT_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *magicNodeResultMetadataFromObject(const magicNodeResultRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, NULL, object->magic_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "node", PS_DATA_STRING, NULL, object->node)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item node");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+magicNodeResultRow *magicNodeResultObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 magic_id = psMetadataLookupS64(&status, md, "magic_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item magic_id");
+        return false;
+    }
+    char* node = psMetadataLookupPtr(&status, md, "node");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item node");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return magicNodeResultRowAlloc(magic_id, node, uri, fault);
+}
+psArray *magicNodeResultSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, MAGICNODERESULT_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        magicNodeResultRow *object = magicNodeResultObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool magicNodeResultDeleteObject(psDB *dbh, const magicNodeResultRow *object)
+{
+    psMetadata *where = magicNodeResultMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, MAGICNODERESULT_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicNodeResult");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "magicNodeResultRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long magicNodeResultDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        magicNodeResultRow *object = objects->data[i];
+        psMetadata *where = magicNodeResultMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, MAGICNODERESULT_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicNodeResult");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool magicNodeResultPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = magicNodeResultMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            MAGICNODERESULT_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool magicNodeResultPrintObject(FILE *stream, magicNodeResultRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = magicNodeResultMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void magicMaskRowFree(magicMaskRow *object);
+
+magicMaskRow *magicMaskRowAlloc(psS64 magic_id, const char *uri, psS32 streaks, psS16 fault)
+{
+    magicMaskRow    *_object;
+
+    _object = psAlloc(sizeof(magicMaskRow));
+    psMemSetDeallocator(_object, (psFreeFunc)magicMaskRowFree);
+
+    _object->magic_id = magic_id;
+    _object->uri = psStringCopy(uri);
+    _object->streaks = streaks;
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void magicMaskRowFree(magicMaskRow *object)
+{
+    psFree(object->uri);
+}
+
+bool magicMaskCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, "Primary Key fkey(magic_id) ref magicRun(magic_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "streaks", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item streaks");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, MAGICMASK_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool magicMaskDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, MAGICMASK_TABLE_NAME);
+}
+
+bool magicMaskInsert(psDB * dbh, psS64 magic_id, const char *uri, psS32 streaks, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, NULL, magic_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "streaks", PS_DATA_S32, NULL, streaks)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item streaks");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, MAGICMASK_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long magicMaskDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, MAGICMASK_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicMask");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool magicMaskInsertObject(psDB *dbh, magicMaskRow *object)
+{
+    return magicMaskInsert(dbh, object->magic_id, object->uri, object->streaks, object->fault);
+}
+
+bool magicMaskInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!magicMaskInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool magicMaskInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  MAGICMASK_TABLE_NAME
+    if (!psFitsMoveExtName(fits, MAGICMASK_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", MAGICMASK_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, MAGICMASK_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool magicMaskSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, MAGICMASK_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, MAGICMASK_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *magicMaskMetadataFromObject(const magicMaskRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magic_id", PS_DATA_S64, NULL, object->magic_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magic_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "streaks", PS_DATA_S32, NULL, object->streaks)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item streaks");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+magicMaskRow *magicMaskObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 magic_id = psMetadataLookupS64(&status, md, "magic_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item magic_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psS32 streaks = psMetadataLookupS32(&status, md, "streaks");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item streaks");
+        return false;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return magicMaskRowAlloc(magic_id, uri, streaks, fault);
+}
+psArray *magicMaskSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, MAGICMASK_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        magicMaskRow *object = magicMaskObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool magicMaskDeleteObject(psDB *dbh, const magicMaskRow *object)
+{
+    psMetadata *where = magicMaskMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, MAGICMASK_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicMask");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "magicMaskRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long magicMaskDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        magicMaskRow *object = objects->data[i];
+        psMetadata *where = magicMaskMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, MAGICMASK_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from magicMask");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool magicMaskPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = magicMaskMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            MAGICMASK_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool magicMaskPrintObject(FILE *stream, magicMaskRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = magicMaskMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void calDBRowFree(calDBRow *object);
+
+calDBRow *calDBRowAlloc(psS64 cal_id, const char *dvodb, const char *state)
+{
+    calDBRow        *_object;
+
+    _object = psAlloc(sizeof(calDBRow));
+    psMemSetDeallocator(_object, (psFreeFunc)calDBRowFree);
+
+    _object->cal_id = cal_id;
+    _object->dvodb = psStringCopy(dvodb);
+    _object->state = psStringCopy(state);
+
+    return _object;
+}
+
+static void calDBRowFree(calDBRow *object)
+{
+    psFree(object->dvodb);
+    psFree(object->state);
+}
+
+bool calDBCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, CALDB_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool calDBDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, CALDB_TABLE_NAME);
+}
+
+bool calDBInsert(psDB * dbh, psS64 cal_id, const char *dvodb, const char *state)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, NULL, cal_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, CALDB_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long calDBDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, CALDB_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from calDB");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool calDBInsertObject(psDB *dbh, calDBRow *object)
+{
+    return calDBInsert(dbh, object->cal_id, object->dvodb, object->state);
+}
+
+bool calDBInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!calDBInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool calDBInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  CALDB_TABLE_NAME
+    if (!psFitsMoveExtName(fits, CALDB_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", CALDB_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, CALDB_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool calDBSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, CALDB_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, CALDB_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *calDBMetadataFromObject(const calDBRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, NULL, object->cal_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+calDBRow *calDBObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 cal_id = psMetadataLookupS64(&status, md, "cal_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cal_id");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+
+    return calDBRowAlloc(cal_id, dvodb, state);
+}
+psArray *calDBSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, CALDB_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        calDBRow *object = calDBObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool calDBDeleteObject(psDB *dbh, const calDBRow *object)
+{
+    psMetadata *where = calDBMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, CALDB_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from calDB");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "calDBRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long calDBDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        calDBRow *object = objects->data[i];
+        psMetadata *where = calDBMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, CALDB_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from calDB");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool calDBPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = calDBMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            CALDB_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool calDBPrintObject(FILE *stream, calDBRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = calDBMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void calRunRowFree(calRunRow *object);
+
+calRunRow *calRunRowAlloc(psS64 cal_id, const char *region, const char *last_step, const char *state)
+{
+    calRunRow       *_object;
+
+    _object = psAlloc(sizeof(calRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)calRunRowFree);
+
+    _object->cal_id = cal_id;
+    _object->region = psStringCopy(region);
+    _object->last_step = psStringCopy(last_step);
+    _object->state = psStringCopy(state);
+
+    return _object;
+}
+
+static void calRunRowFree(calRunRow *object)
+{
+    psFree(object->region);
+    psFree(object->last_step);
+    psFree(object->state);
+}
+
+bool calRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "region", PS_DATA_STRING, "success or failure", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item region");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "last_step", PS_DATA_STRING, "fkey(cal_id) ref calDB(cal_id)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item last_step");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, CALRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool calRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, CALRUN_TABLE_NAME);
+}
+
+bool calRunInsert(psDB * dbh, psS64 cal_id, const char *region, const char *last_step, const char *state)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, NULL, cal_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "region", PS_DATA_STRING, NULL, region)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item region");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "last_step", PS_DATA_STRING, NULL, last_step)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item last_step");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, CALRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long calRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, CALRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from calRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool calRunInsertObject(psDB *dbh, calRunRow *object)
+{
+    return calRunInsert(dbh, object->cal_id, object->region, object->last_step, object->state);
+}
+
+bool calRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!calRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool calRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  CALRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, CALRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", CALRUN_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, CALRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool calRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, CALRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, CALRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *calRunMetadataFromObject(const calRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, NULL, object->cal_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "region", PS_DATA_STRING, NULL, object->region)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item region");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "last_step", PS_DATA_STRING, NULL, object->last_step)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item last_step");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+calRunRow *calRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 cal_id = psMetadataLookupS64(&status, md, "cal_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cal_id");
+        return false;
+    }
+    char* region = psMetadataLookupPtr(&status, md, "region");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item region");
+        return false;
+    }
+    char* last_step = psMetadataLookupPtr(&status, md, "last_step");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item last_step");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+
+    return calRunRowAlloc(cal_id, region, last_step, state);
+}
+psArray *calRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, CALRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        calRunRow *object = calRunObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool calRunDeleteObject(psDB *dbh, const calRunRow *object)
+{
+    psMetadata *where = calRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, CALRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from calRun");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "calRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long calRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        calRunRow *object = objects->data[i];
+        psMetadata *where = calRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, CALRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from calRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool calRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = calRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            CALRUN_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool calRunPrintObject(FILE *stream, calRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = calRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void flatcorrRunRowFree(flatcorrRunRow *object);
+
+flatcorrRunRow *flatcorrRunRowAlloc(psS64 corr_id, const char *dvodb, const char *filter, const char *state, const char *workdir, const char *label, const char *stats, const char *region)
+{
+    flatcorrRunRow  *_object;
+
+    _object = psAlloc(sizeof(flatcorrRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)flatcorrRunRowFree);
+
+    _object->corr_id = corr_id;
+    _object->dvodb = psStringCopy(dvodb);
+    _object->filter = psStringCopy(filter);
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->label = psStringCopy(label);
+    _object->stats = psStringCopy(stats);
+    _object->region = psStringCopy(region);
+
+    return _object;
+}
+
+static void flatcorrRunRowFree(flatcorrRunRow *object)
+{
+    psFree(object->dvodb);
+    psFree(object->filter);
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->label);
+    psFree(object->stats);
+    psFree(object->region);
+}
+
+bool flatcorrRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stats", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stats");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "region", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item region");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, FLATCORRRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool flatcorrRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, FLATCORRRUN_TABLE_NAME);
+}
+
+bool flatcorrRunInsert(psDB * dbh, psS64 corr_id, const char *dvodb, const char *filter, const char *state, const char *workdir, const char *label, const char *stats, const char *region)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_id", PS_DATA_S64, NULL, corr_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, filter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stats", PS_DATA_STRING, NULL, stats)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stats");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "region", PS_DATA_STRING, NULL, region)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item region");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, FLATCORRRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long flatcorrRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, FLATCORRRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from flatcorrRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool flatcorrRunInsertObject(psDB *dbh, flatcorrRunRow *object)
+{
+    return flatcorrRunInsert(dbh, object->corr_id, object->dvodb, object->filter, object->state, object->workdir, object->label, object->stats, object->region);
+}
+
+bool flatcorrRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!flatcorrRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool flatcorrRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  FLATCORRRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, FLATCORRRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", FLATCORRRUN_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, FLATCORRRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool flatcorrRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, FLATCORRRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, FLATCORRRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *flatcorrRunMetadataFromObject(const flatcorrRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_id", PS_DATA_S64, NULL, object->corr_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "filter", PS_DATA_STRING, NULL, object->filter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "label", PS_DATA_STRING, NULL, object->label)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item label");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stats", PS_DATA_STRING, NULL, object->stats)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stats");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "region", PS_DATA_STRING, NULL, object->region)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item region");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+flatcorrRunRow *flatcorrRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 corr_id = psMetadataLookupS64(&status, md, "corr_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item corr_id");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    char* filter = psMetadataLookupPtr(&status, md, "filter");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item filter");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* label = psMetadataLookupPtr(&status, md, "label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item label");
+        return false;
+    }
+    char* stats = psMetadataLookupPtr(&status, md, "stats");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stats");
+        return false;
+    }
+    char* region = psMetadataLookupPtr(&status, md, "region");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item region");
+        return false;
+    }
+
+    return flatcorrRunRowAlloc(corr_id, dvodb, filter, state, workdir, label, stats, region);
+}
+psArray *flatcorrRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, FLATCORRRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        flatcorrRunRow *object = flatcorrRunObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool flatcorrRunDeleteObject(psDB *dbh, const flatcorrRunRow *object)
+{
+    psMetadata *where = flatcorrRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, FLATCORRRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from flatcorrRun");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "flatcorrRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long flatcorrRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        flatcorrRunRow *object = objects->data[i];
+        psMetadata *where = flatcorrRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, FLATCORRRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from flatcorrRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool flatcorrRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = flatcorrRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            FLATCORRRUN_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool flatcorrRunPrintObject(FILE *stream, flatcorrRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = flatcorrRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void flatcorrExpRowFree(flatcorrExpRow *object);
+
+flatcorrExpRow *flatcorrExpRowAlloc(psS64 corr_id, psS64 chip_id)
+{
+    flatcorrExpRow  *_object;
+
+    _object = psAlloc(sizeof(flatcorrExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)flatcorrExpRowFree);
+
+    _object->corr_id = corr_id;
+    _object->chip_id = chip_id;
+
+    return _object;
+}
+
+static void flatcorrExpRowFree(flatcorrExpRow *object)
+{
+}
+
+bool flatcorrExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_id", PS_DATA_S64, "Primary Key fkey(corr_id) ref flatcorrRun(corr_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key fkey(chip_id) ref chipRun(chip_id)", 64)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, FLATCORREXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool flatcorrExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, FLATCORREXP_TABLE_NAME);
+}
+
+bool flatcorrExpInsert(psDB * dbh, psS64 corr_id, psS64 chip_id)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_id", PS_DATA_S64, NULL, corr_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, NULL, chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, FLATCORREXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long flatcorrExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, FLATCORREXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from flatcorrExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool flatcorrExpInsertObject(psDB *dbh, flatcorrExpRow *object)
+{
+    return flatcorrExpInsert(dbh, object->corr_id, object->chip_id);
+}
+
+bool flatcorrExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!flatcorrExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool flatcorrExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  FLATCORREXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, FLATCORREXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", FLATCORREXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, FLATCORREXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool flatcorrExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, FLATCORREXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, FLATCORREXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *flatcorrExpMetadataFromObject(const flatcorrExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "corr_id", PS_DATA_S64, NULL, object->corr_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item corr_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, NULL, object->chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+flatcorrExpRow *flatcorrExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 corr_id = psMetadataLookupS64(&status, md, "corr_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item corr_id");
+        return false;
+    }
+    psS64 chip_id = psMetadataLookupS64(&status, md, "chip_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item chip_id");
+        return false;
+    }
+
+    return flatcorrExpRowAlloc(corr_id, chip_id);
+}
+psArray *flatcorrExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, FLATCORREXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        flatcorrExpRow *object = flatcorrExpObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool flatcorrExpDeleteObject(psDB *dbh, const flatcorrExpRow *object)
+{
+    psMetadata *where = flatcorrExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, FLATCORREXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from flatcorrExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "flatcorrExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long flatcorrExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        flatcorrExpRow *object = objects->data[i];
+        psMetadata *where = flatcorrExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, FLATCORREXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from flatcorrExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool flatcorrExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = flatcorrExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            FLATCORREXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool flatcorrExpPrintObject(FILE *stream, flatcorrExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = flatcorrExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void pstampDataStoreRowFree(pstampDataStoreRow *object);
+
+pstampDataStoreRow *pstampDataStoreRowAlloc(psS64 ds_id, const char *state, const char *lastFileset, const char *outProduct, const char *uri)
+{
+    pstampDataStoreRow *_object;
+
+    _object = psAlloc(sizeof(pstampDataStoreRow));
+    psMemSetDeallocator(_object, (psFreeFunc)pstampDataStoreRowFree);
+
+    _object->ds_id = ds_id;
+    _object->state = psStringCopy(state);
+    _object->lastFileset = psStringCopy(lastFileset);
+    _object->outProduct = psStringCopy(outProduct);
+    _object->uri = psStringCopy(uri);
+
+    return _object;
+}
+
+static void pstampDataStoreRowFree(pstampDataStoreRow *object)
+{
+    psFree(object->state);
+    psFree(object->lastFileset);
+    psFree(object->outProduct);
+    psFree(object->uri);
+}
+
+bool pstampDataStoreCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ds_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ds_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "lastFileset", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item lastFileset");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "outProduct", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item outProduct");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, PSTAMPDATASTORE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool pstampDataStoreDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, PSTAMPDATASTORE_TABLE_NAME);
+}
+
+bool pstampDataStoreInsert(psDB * dbh, psS64 ds_id, const char *state, const char *lastFileset, const char *outProduct, const char *uri)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ds_id", PS_DATA_S64, NULL, ds_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ds_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "lastFileset", PS_DATA_STRING, NULL, lastFileset)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item lastFileset");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "outProduct", PS_DATA_STRING, NULL, outProduct)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item outProduct");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, PSTAMPDATASTORE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long pstampDataStoreDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, PSTAMPDATASTORE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pstampDataStore");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pstampDataStoreInsertObject(psDB *dbh, pstampDataStoreRow *object)
+{
+    return pstampDataStoreInsert(dbh, object->ds_id, object->state, object->lastFileset, object->outProduct, object->uri);
+}
+
+bool pstampDataStoreInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!pstampDataStoreInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool pstampDataStoreInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  PSTAMPDATASTORE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, PSTAMPDATASTORE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", PSTAMPDATASTORE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, PSTAMPDATASTORE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool pstampDataStoreSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, PSTAMPDATASTORE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, PSTAMPDATASTORE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *pstampDataStoreMetadataFromObject(const pstampDataStoreRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ds_id", PS_DATA_S64, NULL, object->ds_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ds_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "lastFileset", PS_DATA_STRING, NULL, object->lastFileset)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item lastFileset");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "outProduct", PS_DATA_STRING, NULL, object->outProduct)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item outProduct");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+pstampDataStoreRow *pstampDataStoreObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 ds_id = psMetadataLookupS64(&status, md, "ds_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ds_id");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* lastFileset = psMetadataLookupPtr(&status, md, "lastFileset");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item lastFileset");
+        return false;
+    }
+    char* outProduct = psMetadataLookupPtr(&status, md, "outProduct");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item outProduct");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+
+    return pstampDataStoreRowAlloc(ds_id, state, lastFileset, outProduct, uri);
+}
+psArray *pstampDataStoreSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, PSTAMPDATASTORE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        pstampDataStoreRow *object = pstampDataStoreObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool pstampDataStoreDeleteObject(psDB *dbh, const pstampDataStoreRow *object)
+{
+    psMetadata *where = pstampDataStoreMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, PSTAMPDATASTORE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pstampDataStore");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "pstampDataStoreRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long pstampDataStoreDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        pstampDataStoreRow *object = objects->data[i];
+        psMetadata *where = pstampDataStoreMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, PSTAMPDATASTORE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from pstampDataStore");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pstampDataStorePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = pstampDataStoreMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            PSTAMPDATASTORE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool pstampDataStorePrintObject(FILE *stream, pstampDataStoreRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = pstampDataStoreMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void pstampRequestRowFree(pstampRequestRow *object);
+
+pstampRequestRow *pstampRequestRowAlloc(psS64 req_id, psS64 ds_id, const char *state, const char *name, const char *reqType, const char *outProduct, const char *uri, psS32 fault)
+{
+    pstampRequestRow *_object;
+
+    _object = psAlloc(sizeof(pstampRequestRow));
+    psMemSetDeallocator(_object, (psFreeFunc)pstampRequestRowFree);
+
+    _object->req_id = req_id;
+    _object->ds_id = ds_id;
+    _object->state = psStringCopy(state);
+    _object->name = psStringCopy(name);
+    _object->reqType = psStringCopy(reqType);
+    _object->outProduct = psStringCopy(outProduct);
+    _object->uri = psStringCopy(uri);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void pstampRequestRowFree(pstampRequestRow *object)
+{
+    psFree(object->state);
+    psFree(object->name);
+    psFree(object->reqType);
+    psFree(object->outProduct);
+    psFree(object->uri);
+}
+
+bool pstampRequestCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "req_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item req_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ds_id", PS_DATA_S64, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ds_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "name", PS_DATA_STRING, "UNIQUE", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reqType", PS_DATA_STRING, NULL, "16")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reqType");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "outProduct", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item outProduct");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, PSTAMPREQUEST_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool pstampRequestDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, PSTAMPREQUEST_TABLE_NAME);
+}
+
+bool pstampRequestInsert(psDB * dbh, psS64 req_id, psS64 ds_id, const char *state, const char *name, const char *reqType, const char *outProduct, const char *uri, psS32 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "req_id", PS_DATA_S64, NULL, req_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item req_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ds_id", PS_DATA_S64, NULL, ds_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ds_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "name", PS_DATA_STRING, NULL, name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reqType", PS_DATA_STRING, NULL, reqType)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reqType");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "outProduct", PS_DATA_STRING, NULL, outProduct)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item outProduct");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S32, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, PSTAMPREQUEST_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long pstampRequestDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, PSTAMPREQUEST_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pstampRequest");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pstampRequestInsertObject(psDB *dbh, pstampRequestRow *object)
+{
+    return pstampRequestInsert(dbh, object->req_id, object->ds_id, object->state, object->name, object->reqType, object->outProduct, object->uri, object->fault);
+}
+
+bool pstampRequestInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!pstampRequestInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool pstampRequestInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  PSTAMPREQUEST_TABLE_NAME
+    if (!psFitsMoveExtName(fits, PSTAMPREQUEST_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", PSTAMPREQUEST_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, PSTAMPREQUEST_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool pstampRequestSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, PSTAMPREQUEST_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, PSTAMPREQUEST_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *pstampRequestMetadataFromObject(const pstampRequestRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "req_id", PS_DATA_S64, NULL, object->req_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item req_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "ds_id", PS_DATA_S64, NULL, object->ds_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item ds_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "name", PS_DATA_STRING, NULL, object->name)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item name");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "reqType", PS_DATA_STRING, NULL, object->reqType)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item reqType");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "outProduct", PS_DATA_STRING, NULL, object->outProduct)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item outProduct");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S32, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+pstampRequestRow *pstampRequestObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 req_id = psMetadataLookupS64(&status, md, "req_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item req_id");
+        return false;
+    }
+    psS64 ds_id = psMetadataLookupS64(&status, md, "ds_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item ds_id");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* name = psMetadataLookupPtr(&status, md, "name");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item name");
+        return false;
+    }
+    char* reqType = psMetadataLookupPtr(&status, md, "reqType");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item reqType");
+        return false;
+    }
+    char* outProduct = psMetadataLookupPtr(&status, md, "outProduct");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item outProduct");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    psS32 fault = psMetadataLookupS32(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return pstampRequestRowAlloc(req_id, ds_id, state, name, reqType, outProduct, uri, fault);
+}
+psArray *pstampRequestSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, PSTAMPREQUEST_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        pstampRequestRow *object = pstampRequestObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool pstampRequestDeleteObject(psDB *dbh, const pstampRequestRow *object)
+{
+    psMetadata *where = pstampRequestMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, PSTAMPREQUEST_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pstampRequest");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "pstampRequestRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long pstampRequestDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        pstampRequestRow *object = objects->data[i];
+        psMetadata *where = pstampRequestMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, PSTAMPREQUEST_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from pstampRequest");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pstampRequestPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = pstampRequestMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            PSTAMPREQUEST_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool pstampRequestPrintObject(FILE *stream, pstampRequestRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = pstampRequestMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void pstampJobRowFree(pstampJobRow *object);
+
+pstampJobRow *pstampJobRowAlloc(psS64 job_id, psS64 req_id, const char *rownum, const char *state, const char *jobType, psS32 fault, const char *uri, const char *outputBase, const char *args)
+{
+    pstampJobRow    *_object;
+
+    _object = psAlloc(sizeof(pstampJobRow));
+    psMemSetDeallocator(_object, (psFreeFunc)pstampJobRowFree);
+
+    _object->job_id = job_id;
+    _object->req_id = req_id;
+    _object->rownum = psStringCopy(rownum);
+    _object->state = psStringCopy(state);
+    _object->jobType = psStringCopy(jobType);
+    _object->fault = fault;
+    _object->uri = psStringCopy(uri);
+    _object->outputBase = psStringCopy(outputBase);
+    _object->args = psStringCopy(args);
+
+    return _object;
+}
+
+static void pstampJobRowFree(pstampJobRow *object)
+{
+    psFree(object->rownum);
+    psFree(object->state);
+    psFree(object->jobType);
+    psFree(object->uri);
+    psFree(object->outputBase);
+    psFree(object->args);
+}
+
+bool pstampJobCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "job_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item job_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "req_id", PS_DATA_S64, "Primary Key fkey(req_id) ref pstampRequest(req_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item req_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "rownum", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item rownum");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "jobType", PS_DATA_STRING, NULL, "16")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item jobType");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "outputBase", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item outputBase");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "args", PS_DATA_STRING, NULL, "511")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item args");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, PSTAMPJOB_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool pstampJobDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, PSTAMPJOB_TABLE_NAME);
+}
+
+bool pstampJobInsert(psDB * dbh, psS64 job_id, psS64 req_id, const char *rownum, const char *state, const char *jobType, psS32 fault, const char *uri, const char *outputBase, const char *args)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "job_id", PS_DATA_S64, NULL, job_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item job_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "req_id", PS_DATA_S64, NULL, req_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item req_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "rownum", PS_DATA_STRING, NULL, rownum)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item rownum");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "jobType", PS_DATA_STRING, NULL, jobType)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item jobType");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S32, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "outputBase", PS_DATA_STRING, NULL, outputBase)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item outputBase");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "args", PS_DATA_STRING, NULL, args)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item args");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, PSTAMPJOB_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long pstampJobDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, PSTAMPJOB_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pstampJob");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pstampJobInsertObject(psDB *dbh, pstampJobRow *object)
+{
+    return pstampJobInsert(dbh, object->job_id, object->req_id, object->rownum, object->state, object->jobType, object->fault, object->uri, object->outputBase, object->args);
+}
+
+bool pstampJobInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!pstampJobInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool pstampJobInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  PSTAMPJOB_TABLE_NAME
+    if (!psFitsMoveExtName(fits, PSTAMPJOB_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", PSTAMPJOB_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, PSTAMPJOB_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool pstampJobSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, PSTAMPJOB_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, PSTAMPJOB_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *pstampJobMetadataFromObject(const pstampJobRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "job_id", PS_DATA_S64, NULL, object->job_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item job_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "req_id", PS_DATA_S64, NULL, object->req_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item req_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "rownum", PS_DATA_STRING, NULL, object->rownum)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item rownum");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "jobType", PS_DATA_STRING, NULL, object->jobType)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item jobType");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S32, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "outputBase", PS_DATA_STRING, NULL, object->outputBase)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item outputBase");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "args", PS_DATA_STRING, NULL, object->args)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item args");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+pstampJobRow *pstampJobObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 job_id = psMetadataLookupS64(&status, md, "job_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item job_id");
+        return false;
+    }
+    psS64 req_id = psMetadataLookupS64(&status, md, "req_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item req_id");
+        return false;
+    }
+    char* rownum = psMetadataLookupPtr(&status, md, "rownum");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item rownum");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* jobType = psMetadataLookupPtr(&status, md, "jobType");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item jobType");
+        return false;
+    }
+    psS32 fault = psMetadataLookupS32(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+    char* outputBase = psMetadataLookupPtr(&status, md, "outputBase");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item outputBase");
+        return false;
+    }
+    char* args = psMetadataLookupPtr(&status, md, "args");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item args");
+        return false;
+    }
+
+    return pstampJobRowAlloc(job_id, req_id, rownum, state, jobType, fault, uri, outputBase, args);
+}
+psArray *pstampJobSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, PSTAMPJOB_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        pstampJobRow *object = pstampJobObjectFromMetadata(rowSet->data[i]);
+        if (!object) {
+            psFree(object);
+            psFree(returnSet);
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return NULL;
+        }
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool pstampJobDeleteObject(psDB *dbh, const pstampJobRow *object)
+{
+    psMetadata *where = pstampJobMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, PSTAMPJOB_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pstampJob");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "pstampJobRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long pstampJobDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        pstampJobRow *object = objects->data[i];
+        psMetadata *where = pstampJobMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, PSTAMPJOB_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from pstampJob");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pstampJobPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = pstampJobMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            PSTAMPJOB_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+bool pstampJobPrintObject(FILE *stream, pstampJobRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = pstampJobMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/src/ippdb.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/src/ippdb.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/src/ippdb.h	(revision 22322)
@@ -0,0 +1,12521 @@
+/*
+ * header.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ *
+ * This file was generated by glueforge 1.04
+ *
+ * Do NOT directly edit this file.
+ *
+ */
+
+#ifndef IPPDB_H
+#define IPPDB_H 1
+
+#include <pslib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// @addtogroup ippdb
+/// @{
+
+/** Opens a new database connection
+ *
+ *  @return A new psDB object if the database connection is successful or NULL
+ *  on failure.
+ */
+
+psDB *ippdbInit(
+    const char      *host,              ///< Database server hostname
+    const char      *user,              ///< Database username
+    const char      *passwd,            ///< Database password
+    const char      *dbname,            ///< Database table.namespace
+    unsigned int    port                ///< Database port
+);
+
+/** Closes a database connection
+ */
+
+void ippdbCleanup(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Formats and prints a metadata
+ *
+ * Any internal use fields are stripped.
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool ippdbPrintMetadata(
+    FILE            *stream,            ///< a stream
+    psMetadata      *md,                ///< An array of metadata
+    bool            mdcf                ///< format as mdconfig or simple
+);
+
+/** Formats and prints a metadata
+ *
+ * The metadata is printed verbatium without removing any fields.
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool ippdbPrintMetadataRaw(
+    FILE            *stream,            ///< a stream
+    psMetadata      *md,                ///< An array of metadata
+    bool            mdcf                ///< format as mdconfig or simple
+);
+
+/** Formats and prints an array of metadata
+ *
+ * Any internal use fields are stripped.
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool ippdbPrintMetadatas(
+    FILE            *stream,            ///< a stream
+    psArray         *mds,               ///< An array of metadata
+    const char      *mdname,            ///< name of the metadata(s)
+    bool            mdcf                ///< format as mdconfig or simple
+);
+
+/** Formats and prints an array of metadata
+ *
+ * The metadatas are printed verbatium without removing any fields.
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool ippdbPrintMetadatasRaw(
+    FILE            *stream,            ///< a stream
+    psArray         *mds,               ///< An array of metadata
+    const char      *mdname,            ///< name of the metadata(s)
+    bool            mdcf                ///< format as mdconfig or simple
+);
+
+/** pzDataStoreRow data structure
+ *
+ * Structure for representing a single row of pzDataStore table data.
+ */
+
+typedef struct {
+    char            *camera;
+    char            *telescope;
+    char            *uri;
+    psTime*         epoch;
+} pzDataStoreRow;
+
+/** Creates a new pzDataStoreRow object
+ *
+ *  @return A new pzDataStoreRow object or NULL on failure.
+ */
+
+pzDataStoreRow *pzDataStoreRowAlloc(
+    const char      *camera,
+    const char      *telescope,
+    const char      *uri,
+    psTime*         epoch
+);
+
+/** Creates a new pzDataStore table
+ *
+ * @return true on success
+ */
+
+bool pzDataStoreCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a pzDataStore table
+ *
+ * @return true on success
+ */
+
+bool pzDataStoreDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pzDataStoreInsert(
+    psDB            *dbh,               ///< Database handle
+    const char      *camera,
+    const char      *telescope,
+    const char      *uri,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pzDataStoreDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single pzDataStoreRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pzDataStoreInsertObject(
+    psDB            *dbh,               ///< Database handle
+    pzDataStoreRow  *object             ///< pzDataStoreRow object
+);
+
+/** Insert an array of pzDataStoreRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pzDataStoreInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of pzDataStoreRow objects
+);
+
+/** Insert data from a binary FITS table pzDataStoreRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool pzDataStoreInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool pzDataStoreSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a pzDataStoreRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *pzDataStoreMetadataFromObject(
+    const pzDataStoreRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A pzDataStoreRow pointer or NULL on error
+ */
+
+pzDataStoreRow *pzDataStoreObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as pzDataStoreRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *pzDataStoreSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an pzDataStore
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool pzDataStoreDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const pzDataStoreRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pzDataStoreDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of pzDataStoreRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pzDataStorePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of pzDataStoreRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an pzDataStoreRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pzDataStorePrintObject(
+    FILE            *stream,            ///< a stream
+    pzDataStoreRow *object,    ///< an pzDataStoreRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** summitExpRow data structure
+ *
+ * Structure for representing a single row of summitExp table data.
+ */
+
+typedef struct {
+    char            *exp_name;
+    char            *camera;
+    char            *telescope;
+    psTime*         dateobs;
+    char            *exp_type;
+    char            *uri;
+    psS32           imfiles;
+    psS16           fault;
+    psTime*         epoch;
+} summitExpRow;
+
+/** Creates a new summitExpRow object
+ *
+ *  @return A new summitExpRow object or NULL on failure.
+ */
+
+summitExpRow *summitExpRowAlloc(
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    psTime*         dateobs,
+    const char      *exp_type,
+    const char      *uri,
+    psS32           imfiles,
+    psS16           fault,
+    psTime*         epoch
+);
+
+/** Creates a new summitExp table
+ *
+ * @return true on success
+ */
+
+bool summitExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a summitExp table
+ *
+ * @return true on success
+ */
+
+bool summitExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool summitExpInsert(
+    psDB            *dbh,               ///< Database handle
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    psTime*         dateobs,
+    const char      *exp_type,
+    const char      *uri,
+    psS32           imfiles,
+    psS16           fault,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long summitExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single summitExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool summitExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    summitExpRow    *object             ///< summitExpRow object
+);
+
+/** Insert an array of summitExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool summitExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of summitExpRow objects
+);
+
+/** Insert data from a binary FITS table summitExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool summitExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool summitExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a summitExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *summitExpMetadataFromObject(
+    const summitExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A summitExpRow pointer or NULL on error
+ */
+
+summitExpRow *summitExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as summitExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *summitExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an summitExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool summitExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const summitExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long summitExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of summitExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool summitExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of summitExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an summitExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool summitExpPrintObject(
+    FILE            *stream,            ///< a stream
+    summitExpRow *object,    ///< an summitExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** summitImfileRow data structure
+ *
+ * Structure for representing a single row of summitImfile table data.
+ */
+
+typedef struct {
+    char            *exp_name;
+    char            *camera;
+    char            *telescope;
+    char            *file_id;
+    psS32           bytes;
+    char            *md5sum;
+    char            *class;
+    char            *class_id;
+    char            *uri;
+    psTime*         epoch;
+} summitImfileRow;
+
+/** Creates a new summitImfileRow object
+ *
+ *  @return A new summitImfileRow object or NULL on failure.
+ */
+
+summitImfileRow *summitImfileRowAlloc(
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    const char      *file_id,
+    psS32           bytes,
+    const char      *md5sum,
+    const char      *class,
+    const char      *class_id,
+    const char      *uri,
+    psTime*         epoch
+);
+
+/** Creates a new summitImfile table
+ *
+ * @return true on success
+ */
+
+bool summitImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a summitImfile table
+ *
+ * @return true on success
+ */
+
+bool summitImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool summitImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    const char      *file_id,
+    psS32           bytes,
+    const char      *md5sum,
+    const char      *class,
+    const char      *class_id,
+    const char      *uri,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long summitImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single summitImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool summitImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    summitImfileRow *object             ///< summitImfileRow object
+);
+
+/** Insert an array of summitImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool summitImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of summitImfileRow objects
+);
+
+/** Insert data from a binary FITS table summitImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool summitImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool summitImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a summitImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *summitImfileMetadataFromObject(
+    const summitImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A summitImfileRow pointer or NULL on error
+ */
+
+summitImfileRow *summitImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as summitImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *summitImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an summitImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool summitImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const summitImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long summitImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of summitImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool summitImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of summitImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an summitImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool summitImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    summitImfileRow *object,    ///< an summitImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** pzDownloadExpRow data structure
+ *
+ * Structure for representing a single row of pzDownloadExp table data.
+ */
+
+typedef struct {
+    char            *exp_name;
+    char            *camera;
+    char            *telescope;
+    char            *state;
+    psTime*         epoch;
+} pzDownloadExpRow;
+
+/** Creates a new pzDownloadExpRow object
+ *
+ *  @return A new pzDownloadExpRow object or NULL on failure.
+ */
+
+pzDownloadExpRow *pzDownloadExpRowAlloc(
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    const char      *state,
+    psTime*         epoch
+);
+
+/** Creates a new pzDownloadExp table
+ *
+ * @return true on success
+ */
+
+bool pzDownloadExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a pzDownloadExp table
+ *
+ * @return true on success
+ */
+
+bool pzDownloadExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadExpInsert(
+    psDB            *dbh,               ///< Database handle
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    const char      *state,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pzDownloadExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single pzDownloadExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    pzDownloadExpRow *object             ///< pzDownloadExpRow object
+);
+
+/** Insert an array of pzDownloadExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of pzDownloadExpRow objects
+);
+
+/** Insert data from a binary FITS table pzDownloadExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a pzDownloadExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *pzDownloadExpMetadataFromObject(
+    const pzDownloadExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A pzDownloadExpRow pointer or NULL on error
+ */
+
+pzDownloadExpRow *pzDownloadExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as pzDownloadExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *pzDownloadExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an pzDownloadExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool pzDownloadExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const pzDownloadExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pzDownloadExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of pzDownloadExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of pzDownloadExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an pzDownloadExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadExpPrintObject(
+    FILE            *stream,            ///< a stream
+    pzDownloadExpRow *object,    ///< an pzDownloadExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** pzDownloadImfileRow data structure
+ *
+ * Structure for representing a single row of pzDownloadImfile table data.
+ */
+
+typedef struct {
+    char            *exp_name;
+    char            *camera;
+    char            *telescope;
+    char            *class;
+    char            *class_id;
+    char            *uri;
+    psS16           fault;
+    psTime*         epoch;
+} pzDownloadImfileRow;
+
+/** Creates a new pzDownloadImfileRow object
+ *
+ *  @return A new pzDownloadImfileRow object or NULL on failure.
+ */
+
+pzDownloadImfileRow *pzDownloadImfileRowAlloc(
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    const char      *class,
+    const char      *class_id,
+    const char      *uri,
+    psS16           fault,
+    psTime*         epoch
+);
+
+/** Creates a new pzDownloadImfile table
+ *
+ * @return true on success
+ */
+
+bool pzDownloadImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a pzDownloadImfile table
+ *
+ * @return true on success
+ */
+
+bool pzDownloadImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    const char      *class,
+    const char      *class_id,
+    const char      *uri,
+    psS16           fault,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pzDownloadImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single pzDownloadImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    pzDownloadImfileRow *object             ///< pzDownloadImfileRow object
+);
+
+/** Insert an array of pzDownloadImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of pzDownloadImfileRow objects
+);
+
+/** Insert data from a binary FITS table pzDownloadImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a pzDownloadImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *pzDownloadImfileMetadataFromObject(
+    const pzDownloadImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A pzDownloadImfileRow pointer or NULL on error
+ */
+
+pzDownloadImfileRow *pzDownloadImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as pzDownloadImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *pzDownloadImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an pzDownloadImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool pzDownloadImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const pzDownloadImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pzDownloadImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of pzDownloadImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of pzDownloadImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an pzDownloadImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pzDownloadImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    pzDownloadImfileRow *object,    ///< an pzDownloadImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** newExpRow data structure
+ *
+ * Structure for representing a single row of newExp table data.
+ */
+
+typedef struct {
+    psS64           exp_id;
+    char            *tmp_exp_name;
+    char            *tmp_camera;
+    char            *tmp_telescope;
+    char            *state;
+    char            *workdir;
+    char            *workdir_state;
+    char            *reduction;
+    char            *dvodb;
+    char            *tess_id;
+    char            *end_stage;
+    char            *label;
+    psTime*         epoch;
+} newExpRow;
+
+/** Creates a new newExpRow object
+ *
+ *  @return A new newExpRow object or NULL on failure.
+ */
+
+newExpRow *newExpRowAlloc(
+    psS64           exp_id,
+    const char      *tmp_exp_name,
+    const char      *tmp_camera,
+    const char      *tmp_telescope,
+    const char      *state,
+    const char      *workdir,
+    const char      *workdir_state,
+    const char      *reduction,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage,
+    const char      *label,
+    psTime*         epoch
+);
+
+/** Creates a new newExp table
+ *
+ * @return true on success
+ */
+
+bool newExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a newExp table
+ *
+ * @return true on success
+ */
+
+bool newExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool newExpInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           exp_id,
+    const char      *tmp_exp_name,
+    const char      *tmp_camera,
+    const char      *tmp_telescope,
+    const char      *state,
+    const char      *workdir,
+    const char      *workdir_state,
+    const char      *reduction,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage,
+    const char      *label,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long newExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single newExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool newExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    newExpRow       *object             ///< newExpRow object
+);
+
+/** Insert an array of newExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool newExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of newExpRow objects
+);
+
+/** Insert data from a binary FITS table newExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool newExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool newExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a newExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *newExpMetadataFromObject(
+    const newExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A newExpRow pointer or NULL on error
+ */
+
+newExpRow *newExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as newExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *newExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an newExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool newExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const newExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long newExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of newExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool newExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of newExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an newExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool newExpPrintObject(
+    FILE            *stream,            ///< a stream
+    newExpRow *object,    ///< an newExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** newImfileRow data structure
+ *
+ * Structure for representing a single row of newImfile table data.
+ */
+
+typedef struct {
+    psS64           exp_id;
+    char            *tmp_class_id;
+    char            *uri;
+    psTime*         epoch;
+} newImfileRow;
+
+/** Creates a new newImfileRow object
+ *
+ *  @return A new newImfileRow object or NULL on failure.
+ */
+
+newImfileRow *newImfileRowAlloc(
+    psS64           exp_id,
+    const char      *tmp_class_id,
+    const char      *uri,
+    psTime*         epoch
+);
+
+/** Creates a new newImfile table
+ *
+ * @return true on success
+ */
+
+bool newImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a newImfile table
+ *
+ * @return true on success
+ */
+
+bool newImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool newImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           exp_id,
+    const char      *tmp_class_id,
+    const char      *uri,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long newImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single newImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool newImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    newImfileRow    *object             ///< newImfileRow object
+);
+
+/** Insert an array of newImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool newImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of newImfileRow objects
+);
+
+/** Insert data from a binary FITS table newImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool newImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool newImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a newImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *newImfileMetadataFromObject(
+    const newImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A newImfileRow pointer or NULL on error
+ */
+
+newImfileRow *newImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as newImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *newImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an newImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool newImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const newImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long newImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of newImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool newImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of newImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an newImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool newImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    newImfileRow *object,    ///< an newImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** rawExpRow data structure
+ *
+ * Structure for representing a single row of rawExp table data.
+ */
+
+typedef struct {
+    psS64           exp_id;
+    char            *exp_name;
+    char            *camera;
+    char            *telescope;
+    psTime*         dateobs;
+    char            *exp_tag;
+    char            *exp_type;
+    char            *filelevel;
+    char            *workdir;
+    char            *reduction;
+    char            *dvodb;
+    char            *tess_id;
+    char            *end_stage;
+    char            *filter;
+    char            *comment;
+    psF32           airmass;
+    psF64           ra;
+    psF64           decl;
+    psF32           exp_time;
+    psF32           sat_pixel_frac;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    psF64           alt;
+    psF64           az;
+    psF32           ccd_temp;
+    psF64           posang;
+    psF32           m1_x;
+    psF32           m1_y;
+    psF32           m1_z;
+    psF32           m1_tip;
+    psF32           m1_tilt;
+    psF32           m2_x;
+    psF32           m2_y;
+    psF32           m2_z;
+    psF32           m2_tip;
+    psF32           m2_tilt;
+    psF32           env_temperature;
+    psF32           env_humidity;
+    psF32           env_wind_speed;
+    psF32           env_wind_dir;
+    psF32           teltemp_m1;
+    psF32           teltemp_m1cell;
+    psF32           teltemp_m2;
+    psF32           teltemp_spider;
+    psF32           teltemp_truss;
+    psF32           teltemp_extra;
+    psF32           pon_time;
+    psF64           user_1;
+    psF64           user_2;
+    psF64           user_3;
+    psF64           user_4;
+    psF64           user_5;
+    char            *object;
+    psF32           solang;
+    char            *hostname;
+    psS16           fault;
+    psTime*         epoch;
+} rawExpRow;
+
+/** Creates a new rawExpRow object
+ *
+ *  @return A new rawExpRow object or NULL on failure.
+ */
+
+rawExpRow *rawExpRowAlloc(
+    psS64           exp_id,
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    psTime*         dateobs,
+    const char      *exp_tag,
+    const char      *exp_type,
+    const char      *filelevel,
+    const char      *workdir,
+    const char      *reduction,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage,
+    const char      *filter,
+    const char      *comment,
+    psF32           airmass,
+    psF64           ra,
+    psF64           decl,
+    psF32           exp_time,
+    psF32           sat_pixel_frac,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           alt,
+    psF64           az,
+    psF32           ccd_temp,
+    psF64           posang,
+    psF32           m1_x,
+    psF32           m1_y,
+    psF32           m1_z,
+    psF32           m1_tip,
+    psF32           m1_tilt,
+    psF32           m2_x,
+    psF32           m2_y,
+    psF32           m2_z,
+    psF32           m2_tip,
+    psF32           m2_tilt,
+    psF32           env_temperature,
+    psF32           env_humidity,
+    psF32           env_wind_speed,
+    psF32           env_wind_dir,
+    psF32           teltemp_m1,
+    psF32           teltemp_m1cell,
+    psF32           teltemp_m2,
+    psF32           teltemp_spider,
+    psF32           teltemp_truss,
+    psF32           teltemp_extra,
+    psF32           pon_time,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *object,
+    psF32           solang,
+    const char      *hostname,
+    psS16           fault,
+    psTime*         epoch
+);
+
+/** Creates a new rawExp table
+ *
+ * @return true on success
+ */
+
+bool rawExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a rawExp table
+ *
+ * @return true on success
+ */
+
+bool rawExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool rawExpInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           exp_id,
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    psTime*         dateobs,
+    const char      *exp_tag,
+    const char      *exp_type,
+    const char      *filelevel,
+    const char      *workdir,
+    const char      *reduction,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage,
+    const char      *filter,
+    const char      *comment,
+    psF32           airmass,
+    psF64           ra,
+    psF64           decl,
+    psF32           exp_time,
+    psF32           sat_pixel_frac,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           alt,
+    psF64           az,
+    psF32           ccd_temp,
+    psF64           posang,
+    psF32           m1_x,
+    psF32           m1_y,
+    psF32           m1_z,
+    psF32           m1_tip,
+    psF32           m1_tilt,
+    psF32           m2_x,
+    psF32           m2_y,
+    psF32           m2_z,
+    psF32           m2_tip,
+    psF32           m2_tilt,
+    psF32           env_temperature,
+    psF32           env_humidity,
+    psF32           env_wind_speed,
+    psF32           env_wind_dir,
+    psF32           teltemp_m1,
+    psF32           teltemp_m1cell,
+    psF32           teltemp_m2,
+    psF32           teltemp_spider,
+    psF32           teltemp_truss,
+    psF32           teltemp_extra,
+    psF32           pon_time,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *object,
+    psF32           solang,
+    const char      *hostname,
+    psS16           fault,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long rawExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single rawExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool rawExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    rawExpRow       *object             ///< rawExpRow object
+);
+
+/** Insert an array of rawExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool rawExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of rawExpRow objects
+);
+
+/** Insert data from a binary FITS table rawExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool rawExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool rawExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a rawExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *rawExpMetadataFromObject(
+    const rawExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A rawExpRow pointer or NULL on error
+ */
+
+rawExpRow *rawExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as rawExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *rawExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an rawExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool rawExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const rawExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long rawExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of rawExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool rawExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of rawExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an rawExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool rawExpPrintObject(
+    FILE            *stream,            ///< a stream
+    rawExpRow *object,    ///< an rawExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** rawImfileRow data structure
+ *
+ * Structure for representing a single row of rawImfile table data.
+ */
+
+typedef struct {
+    psS64           exp_id;
+    char            *exp_name;
+    char            *camera;
+    char            *telescope;
+    psTime*         dateobs;
+    char            *tmp_class_id;
+    char            *class_id;
+    char            *uri;
+    char            *exp_type;
+    char            *filelevel;
+    char            *filter;
+    char            *comment;
+    psF32           airmass;
+    psF64           ra;
+    psF64           decl;
+    psF32           exp_time;
+    psF32           sat_pixel_frac;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    psF64           alt;
+    psF64           az;
+    psF32           ccd_temp;
+    psF64           posang;
+    psF32           m1_x;
+    psF32           m1_y;
+    psF32           m1_z;
+    psF32           m1_tip;
+    psF32           m1_tilt;
+    psF32           m2_x;
+    psF32           m2_y;
+    psF32           m2_z;
+    psF32           m2_tip;
+    psF32           m2_tilt;
+    psF32           env_temperature;
+    psF32           env_humidity;
+    psF32           env_wind_speed;
+    psF32           env_wind_dir;
+    psF32           teltemp_m1;
+    psF32           teltemp_m1cell;
+    psF32           teltemp_m2;
+    psF32           teltemp_spider;
+    psF32           teltemp_truss;
+    psF32           teltemp_extra;
+    psF32           pon_time;
+    psF64           user_1;
+    psF64           user_2;
+    psF64           user_3;
+    psF64           user_4;
+    psF64           user_5;
+    char            *object;
+    char            *hostname;
+    psS16           fault;
+    psTime*         epoch;
+} rawImfileRow;
+
+/** Creates a new rawImfileRow object
+ *
+ *  @return A new rawImfileRow object or NULL on failure.
+ */
+
+rawImfileRow *rawImfileRowAlloc(
+    psS64           exp_id,
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    psTime*         dateobs,
+    const char      *tmp_class_id,
+    const char      *class_id,
+    const char      *uri,
+    const char      *exp_type,
+    const char      *filelevel,
+    const char      *filter,
+    const char      *comment,
+    psF32           airmass,
+    psF64           ra,
+    psF64           decl,
+    psF32           exp_time,
+    psF32           sat_pixel_frac,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           alt,
+    psF64           az,
+    psF32           ccd_temp,
+    psF64           posang,
+    psF32           m1_x,
+    psF32           m1_y,
+    psF32           m1_z,
+    psF32           m1_tip,
+    psF32           m1_tilt,
+    psF32           m2_x,
+    psF32           m2_y,
+    psF32           m2_z,
+    psF32           m2_tip,
+    psF32           m2_tilt,
+    psF32           env_temperature,
+    psF32           env_humidity,
+    psF32           env_wind_speed,
+    psF32           env_wind_dir,
+    psF32           teltemp_m1,
+    psF32           teltemp_m1cell,
+    psF32           teltemp_m2,
+    psF32           teltemp_spider,
+    psF32           teltemp_truss,
+    psF32           teltemp_extra,
+    psF32           pon_time,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *object,
+    const char      *hostname,
+    psS16           fault,
+    psTime*         epoch
+);
+
+/** Creates a new rawImfile table
+ *
+ * @return true on success
+ */
+
+bool rawImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a rawImfile table
+ *
+ * @return true on success
+ */
+
+bool rawImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool rawImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           exp_id,
+    const char      *exp_name,
+    const char      *camera,
+    const char      *telescope,
+    psTime*         dateobs,
+    const char      *tmp_class_id,
+    const char      *class_id,
+    const char      *uri,
+    const char      *exp_type,
+    const char      *filelevel,
+    const char      *filter,
+    const char      *comment,
+    psF32           airmass,
+    psF64           ra,
+    psF64           decl,
+    psF32           exp_time,
+    psF32           sat_pixel_frac,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           alt,
+    psF64           az,
+    psF32           ccd_temp,
+    psF64           posang,
+    psF32           m1_x,
+    psF32           m1_y,
+    psF32           m1_z,
+    psF32           m1_tip,
+    psF32           m1_tilt,
+    psF32           m2_x,
+    psF32           m2_y,
+    psF32           m2_z,
+    psF32           m2_tip,
+    psF32           m2_tilt,
+    psF32           env_temperature,
+    psF32           env_humidity,
+    psF32           env_wind_speed,
+    psF32           env_wind_dir,
+    psF32           teltemp_m1,
+    psF32           teltemp_m1cell,
+    psF32           teltemp_m2,
+    psF32           teltemp_spider,
+    psF32           teltemp_truss,
+    psF32           teltemp_extra,
+    psF32           pon_time,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *object,
+    const char      *hostname,
+    psS16           fault,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long rawImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single rawImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool rawImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    rawImfileRow    *object             ///< rawImfileRow object
+);
+
+/** Insert an array of rawImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool rawImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of rawImfileRow objects
+);
+
+/** Insert data from a binary FITS table rawImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool rawImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool rawImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a rawImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *rawImfileMetadataFromObject(
+    const rawImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A rawImfileRow pointer or NULL on error
+ */
+
+rawImfileRow *rawImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as rawImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *rawImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an rawImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool rawImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const rawImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long rawImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of rawImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool rawImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of rawImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an rawImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool rawImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    rawImfileRow *object,    ///< an rawImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** guidePendingExpRow data structure
+ *
+ * Structure for representing a single row of guidePendingExp table data.
+ */
+
+typedef struct {
+    psS64           guide_id;
+    psS64           exp_id;
+    char            *recipe;
+} guidePendingExpRow;
+
+/** Creates a new guidePendingExpRow object
+ *
+ *  @return A new guidePendingExpRow object or NULL on failure.
+ */
+
+guidePendingExpRow *guidePendingExpRowAlloc(
+    psS64           guide_id,
+    psS64           exp_id,
+    const char      *recipe
+);
+
+/** Creates a new guidePendingExp table
+ *
+ * @return true on success
+ */
+
+bool guidePendingExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a guidePendingExp table
+ *
+ * @return true on success
+ */
+
+bool guidePendingExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool guidePendingExpInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           guide_id,
+    psS64           exp_id,
+    const char      *recipe
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long guidePendingExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single guidePendingExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool guidePendingExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    guidePendingExpRow *object             ///< guidePendingExpRow object
+);
+
+/** Insert an array of guidePendingExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool guidePendingExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of guidePendingExpRow objects
+);
+
+/** Insert data from a binary FITS table guidePendingExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool guidePendingExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool guidePendingExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a guidePendingExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *guidePendingExpMetadataFromObject(
+    const guidePendingExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A guidePendingExpRow pointer or NULL on error
+ */
+
+guidePendingExpRow *guidePendingExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as guidePendingExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *guidePendingExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an guidePendingExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool guidePendingExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const guidePendingExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long guidePendingExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of guidePendingExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool guidePendingExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of guidePendingExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an guidePendingExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool guidePendingExpPrintObject(
+    FILE            *stream,            ///< a stream
+    guidePendingExpRow *object,    ///< an guidePendingExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** chipRunRow data structure
+ *
+ * Structure for representing a single row of chipRun table data.
+ */
+
+typedef struct {
+    psS64           chip_id;
+    psS64           exp_id;
+    char            *state;
+    char            *workdir;
+    char            *workdir_state;
+    char            *label;
+    char            *reduction;
+    char            *expgroup;
+    char            *dvodb;
+    char            *tess_id;
+    char            *end_stage;
+} chipRunRow;
+
+/** Creates a new chipRunRow object
+ *
+ *  @return A new chipRunRow object or NULL on failure.
+ */
+
+chipRunRow *chipRunRowAlloc(
+    psS64           chip_id,
+    psS64           exp_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *workdir_state,
+    const char      *label,
+    const char      *reduction,
+    const char      *expgroup,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage
+);
+
+/** Creates a new chipRun table
+ *
+ * @return true on success
+ */
+
+bool chipRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a chipRun table
+ *
+ * @return true on success
+ */
+
+bool chipRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool chipRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           chip_id,
+    psS64           exp_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *workdir_state,
+    const char      *label,
+    const char      *reduction,
+    const char      *expgroup,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long chipRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single chipRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool chipRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    chipRunRow      *object             ///< chipRunRow object
+);
+
+/** Insert an array of chipRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool chipRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of chipRunRow objects
+);
+
+/** Insert data from a binary FITS table chipRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool chipRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool chipRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a chipRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *chipRunMetadataFromObject(
+    const chipRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A chipRunRow pointer or NULL on error
+ */
+
+chipRunRow *chipRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as chipRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *chipRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an chipRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool chipRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const chipRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long chipRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of chipRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool chipRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of chipRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an chipRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool chipRunPrintObject(
+    FILE            *stream,            ///< a stream
+    chipRunRow *object,    ///< an chipRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** chipProcessedImfileRow data structure
+ *
+ * Structure for representing a single row of chipProcessedImfile table data.
+ */
+
+typedef struct {
+    psS64           chip_id;
+    psS64           exp_id;
+    char            *class_id;
+    char            *uri;
+    psF32           bg;
+    psF32           bg_stdev;
+    psF32           bg_mean_stdev;
+    psF32           bias;
+    psF32           bias_stdev;
+    psF32           fringe_0;
+    psF32           fringe_1;
+    psF32           fringe_2;
+    psF32           sigma_ra;
+    psF32           sigma_dec;
+    psF32           ap_resid;
+    psF32           ap_resid_stdev;
+    psF32           zp_mean;
+    psF32           zp_stdev;
+    psF32           fwhm_major;
+    psF32           fwhm_minor;
+    psF32           dtime_detrend;
+    psF32           dtime_photom;
+    psF32           dtime_astrom;
+    char            *hostname;
+    psS32           n_stars;
+    psS32           n_extended;
+    psS32           n_cr;
+    psS32           n_astrom;
+    char            *path_base;
+    psS16           fault;
+} chipProcessedImfileRow;
+
+/** Creates a new chipProcessedImfileRow object
+ *
+ *  @return A new chipProcessedImfileRow object or NULL on failure.
+ */
+
+chipProcessedImfileRow *chipProcessedImfileRowAlloc(
+    psS64           chip_id,
+    psS64           exp_id,
+    const char      *class_id,
+    const char      *uri,
+    psF32           bg,
+    psF32           bg_stdev,
+    psF32           bg_mean_stdev,
+    psF32           bias,
+    psF32           bias_stdev,
+    psF32           fringe_0,
+    psF32           fringe_1,
+    psF32           fringe_2,
+    psF32           sigma_ra,
+    psF32           sigma_dec,
+    psF32           ap_resid,
+    psF32           ap_resid_stdev,
+    psF32           zp_mean,
+    psF32           zp_stdev,
+    psF32           fwhm_major,
+    psF32           fwhm_minor,
+    psF32           dtime_detrend,
+    psF32           dtime_photom,
+    psF32           dtime_astrom,
+    const char      *hostname,
+    psS32           n_stars,
+    psS32           n_extended,
+    psS32           n_cr,
+    psS32           n_astrom,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Creates a new chipProcessedImfile table
+ *
+ * @return true on success
+ */
+
+bool chipProcessedImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a chipProcessedImfile table
+ *
+ * @return true on success
+ */
+
+bool chipProcessedImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool chipProcessedImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           chip_id,
+    psS64           exp_id,
+    const char      *class_id,
+    const char      *uri,
+    psF32           bg,
+    psF32           bg_stdev,
+    psF32           bg_mean_stdev,
+    psF32           bias,
+    psF32           bias_stdev,
+    psF32           fringe_0,
+    psF32           fringe_1,
+    psF32           fringe_2,
+    psF32           sigma_ra,
+    psF32           sigma_dec,
+    psF32           ap_resid,
+    psF32           ap_resid_stdev,
+    psF32           zp_mean,
+    psF32           zp_stdev,
+    psF32           fwhm_major,
+    psF32           fwhm_minor,
+    psF32           dtime_detrend,
+    psF32           dtime_photom,
+    psF32           dtime_astrom,
+    const char      *hostname,
+    psS32           n_stars,
+    psS32           n_extended,
+    psS32           n_cr,
+    psS32           n_astrom,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long chipProcessedImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single chipProcessedImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool chipProcessedImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    chipProcessedImfileRow *object             ///< chipProcessedImfileRow object
+);
+
+/** Insert an array of chipProcessedImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool chipProcessedImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of chipProcessedImfileRow objects
+);
+
+/** Insert data from a binary FITS table chipProcessedImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool chipProcessedImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool chipProcessedImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a chipProcessedImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *chipProcessedImfileMetadataFromObject(
+    const chipProcessedImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A chipProcessedImfileRow pointer or NULL on error
+ */
+
+chipProcessedImfileRow *chipProcessedImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as chipProcessedImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *chipProcessedImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an chipProcessedImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool chipProcessedImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const chipProcessedImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long chipProcessedImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of chipProcessedImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool chipProcessedImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of chipProcessedImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an chipProcessedImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool chipProcessedImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    chipProcessedImfileRow *object,    ///< an chipProcessedImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** chipMaskRow data structure
+ *
+ * Structure for representing a single row of chipMask table data.
+ */
+
+typedef struct {
+    char            *label;
+} chipMaskRow;
+
+/** Creates a new chipMaskRow object
+ *
+ *  @return A new chipMaskRow object or NULL on failure.
+ */
+
+chipMaskRow *chipMaskRowAlloc(
+    const char      *label
+);
+
+/** Creates a new chipMask table
+ *
+ * @return true on success
+ */
+
+bool chipMaskCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a chipMask table
+ *
+ * @return true on success
+ */
+
+bool chipMaskDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool chipMaskInsert(
+    psDB            *dbh,               ///< Database handle
+    const char      *label
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long chipMaskDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single chipMaskRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool chipMaskInsertObject(
+    psDB            *dbh,               ///< Database handle
+    chipMaskRow     *object             ///< chipMaskRow object
+);
+
+/** Insert an array of chipMaskRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool chipMaskInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of chipMaskRow objects
+);
+
+/** Insert data from a binary FITS table chipMaskRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool chipMaskInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool chipMaskSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a chipMaskRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *chipMaskMetadataFromObject(
+    const chipMaskRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A chipMaskRow pointer or NULL on error
+ */
+
+chipMaskRow *chipMaskObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as chipMaskRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *chipMaskSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an chipMask
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool chipMaskDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const chipMaskRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long chipMaskDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of chipMaskRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool chipMaskPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of chipMaskRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an chipMaskRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool chipMaskPrintObject(
+    FILE            *stream,            ///< a stream
+    chipMaskRow *object,    ///< an chipMaskRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** camRunRow data structure
+ *
+ * Structure for representing a single row of camRun table data.
+ */
+
+typedef struct {
+    psS64           cam_id;
+    psS64           chip_id;
+    char            *state;
+    char            *workdir;
+    char            *workdir_state;
+    char            *label;
+    char            *reduction;
+    char            *expgroup;
+    char            *dvodb;
+    char            *tess_id;
+    char            *end_stage;
+} camRunRow;
+
+/** Creates a new camRunRow object
+ *
+ *  @return A new camRunRow object or NULL on failure.
+ */
+
+camRunRow *camRunRowAlloc(
+    psS64           cam_id,
+    psS64           chip_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *workdir_state,
+    const char      *label,
+    const char      *reduction,
+    const char      *expgroup,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage
+);
+
+/** Creates a new camRun table
+ *
+ * @return true on success
+ */
+
+bool camRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a camRun table
+ *
+ * @return true on success
+ */
+
+bool camRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool camRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           cam_id,
+    psS64           chip_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *workdir_state,
+    const char      *label,
+    const char      *reduction,
+    const char      *expgroup,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long camRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single camRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool camRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    camRunRow       *object             ///< camRunRow object
+);
+
+/** Insert an array of camRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool camRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of camRunRow objects
+);
+
+/** Insert data from a binary FITS table camRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool camRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool camRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a camRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *camRunMetadataFromObject(
+    const camRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A camRunRow pointer or NULL on error
+ */
+
+camRunRow *camRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as camRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *camRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an camRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool camRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const camRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long camRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of camRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool camRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of camRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an camRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool camRunPrintObject(
+    FILE            *stream,            ///< a stream
+    camRunRow *object,    ///< an camRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** camProcessedExpRow data structure
+ *
+ * Structure for representing a single row of camProcessedExp table data.
+ */
+
+typedef struct {
+    psS64           cam_id;
+    char            *uri;
+    psF32           bg;
+    psF32           bg_stdev;
+    psF32           bg_mean_stdev;
+    psF32           bias;
+    psF32           bias_stdev;
+    psF32           fringe_0;
+    psF32           fringe_1;
+    psF32           fringe_2;
+    psF32           sigma_ra;
+    psF32           sigma_dec;
+    psF32           ap_resid;
+    psF32           ap_resid_stdev;
+    psF32           zp_mean;
+    psF32           zp_stdev;
+    psF32           fwhm_major;
+    psF32           fwhm_minor;
+    psF32           dtime_detrend;
+    psF32           dtime_photom;
+    psF32           dtime_astrom;
+    char            *hostname;
+    psS32           n_stars;
+    psS32           n_extended;
+    psS32           n_cr;
+    psS32           n_astrom;
+    char            *path_base;
+    psS16           fault;
+} camProcessedExpRow;
+
+/** Creates a new camProcessedExpRow object
+ *
+ *  @return A new camProcessedExpRow object or NULL on failure.
+ */
+
+camProcessedExpRow *camProcessedExpRowAlloc(
+    psS64           cam_id,
+    const char      *uri,
+    psF32           bg,
+    psF32           bg_stdev,
+    psF32           bg_mean_stdev,
+    psF32           bias,
+    psF32           bias_stdev,
+    psF32           fringe_0,
+    psF32           fringe_1,
+    psF32           fringe_2,
+    psF32           sigma_ra,
+    psF32           sigma_dec,
+    psF32           ap_resid,
+    psF32           ap_resid_stdev,
+    psF32           zp_mean,
+    psF32           zp_stdev,
+    psF32           fwhm_major,
+    psF32           fwhm_minor,
+    psF32           dtime_detrend,
+    psF32           dtime_photom,
+    psF32           dtime_astrom,
+    const char      *hostname,
+    psS32           n_stars,
+    psS32           n_extended,
+    psS32           n_cr,
+    psS32           n_astrom,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Creates a new camProcessedExp table
+ *
+ * @return true on success
+ */
+
+bool camProcessedExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a camProcessedExp table
+ *
+ * @return true on success
+ */
+
+bool camProcessedExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool camProcessedExpInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           cam_id,
+    const char      *uri,
+    psF32           bg,
+    psF32           bg_stdev,
+    psF32           bg_mean_stdev,
+    psF32           bias,
+    psF32           bias_stdev,
+    psF32           fringe_0,
+    psF32           fringe_1,
+    psF32           fringe_2,
+    psF32           sigma_ra,
+    psF32           sigma_dec,
+    psF32           ap_resid,
+    psF32           ap_resid_stdev,
+    psF32           zp_mean,
+    psF32           zp_stdev,
+    psF32           fwhm_major,
+    psF32           fwhm_minor,
+    psF32           dtime_detrend,
+    psF32           dtime_photom,
+    psF32           dtime_astrom,
+    const char      *hostname,
+    psS32           n_stars,
+    psS32           n_extended,
+    psS32           n_cr,
+    psS32           n_astrom,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long camProcessedExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single camProcessedExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool camProcessedExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    camProcessedExpRow *object             ///< camProcessedExpRow object
+);
+
+/** Insert an array of camProcessedExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool camProcessedExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of camProcessedExpRow objects
+);
+
+/** Insert data from a binary FITS table camProcessedExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool camProcessedExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool camProcessedExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a camProcessedExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *camProcessedExpMetadataFromObject(
+    const camProcessedExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A camProcessedExpRow pointer or NULL on error
+ */
+
+camProcessedExpRow *camProcessedExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as camProcessedExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *camProcessedExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an camProcessedExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool camProcessedExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const camProcessedExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long camProcessedExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of camProcessedExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool camProcessedExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of camProcessedExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an camProcessedExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool camProcessedExpPrintObject(
+    FILE            *stream,            ///< a stream
+    camProcessedExpRow *object,    ///< an camProcessedExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** camMaskRow data structure
+ *
+ * Structure for representing a single row of camMask table data.
+ */
+
+typedef struct {
+    char            *label;
+} camMaskRow;
+
+/** Creates a new camMaskRow object
+ *
+ *  @return A new camMaskRow object or NULL on failure.
+ */
+
+camMaskRow *camMaskRowAlloc(
+    const char      *label
+);
+
+/** Creates a new camMask table
+ *
+ * @return true on success
+ */
+
+bool camMaskCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a camMask table
+ *
+ * @return true on success
+ */
+
+bool camMaskDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool camMaskInsert(
+    psDB            *dbh,               ///< Database handle
+    const char      *label
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long camMaskDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single camMaskRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool camMaskInsertObject(
+    psDB            *dbh,               ///< Database handle
+    camMaskRow      *object             ///< camMaskRow object
+);
+
+/** Insert an array of camMaskRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool camMaskInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of camMaskRow objects
+);
+
+/** Insert data from a binary FITS table camMaskRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool camMaskInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool camMaskSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a camMaskRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *camMaskMetadataFromObject(
+    const camMaskRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A camMaskRow pointer or NULL on error
+ */
+
+camMaskRow *camMaskObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as camMaskRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *camMaskSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an camMask
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool camMaskDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const camMaskRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long camMaskDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of camMaskRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool camMaskPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of camMaskRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an camMaskRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool camMaskPrintObject(
+    FILE            *stream,            ///< a stream
+    camMaskRow *object,    ///< an camMaskRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** fakeRunRow data structure
+ *
+ * Structure for representing a single row of fakeRun table data.
+ */
+
+typedef struct {
+    psS64           fake_id;
+    psS64           cam_id;
+    char            *state;
+    char            *workdir;
+    char            *label;
+    char            *reduction;
+    char            *expgroup;
+    char            *dvodb;
+    char            *tess_id;
+    char            *end_stage;
+    psTime*         epoch;
+} fakeRunRow;
+
+/** Creates a new fakeRunRow object
+ *
+ *  @return A new fakeRunRow object or NULL on failure.
+ */
+
+fakeRunRow *fakeRunRowAlloc(
+    psS64           fake_id,
+    psS64           cam_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *label,
+    const char      *reduction,
+    const char      *expgroup,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage,
+    psTime*         epoch
+);
+
+/** Creates a new fakeRun table
+ *
+ * @return true on success
+ */
+
+bool fakeRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a fakeRun table
+ *
+ * @return true on success
+ */
+
+bool fakeRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool fakeRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           fake_id,
+    psS64           cam_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *label,
+    const char      *reduction,
+    const char      *expgroup,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long fakeRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single fakeRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool fakeRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    fakeRunRow      *object             ///< fakeRunRow object
+);
+
+/** Insert an array of fakeRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool fakeRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of fakeRunRow objects
+);
+
+/** Insert data from a binary FITS table fakeRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool fakeRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool fakeRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a fakeRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *fakeRunMetadataFromObject(
+    const fakeRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A fakeRunRow pointer or NULL on error
+ */
+
+fakeRunRow *fakeRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as fakeRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *fakeRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an fakeRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool fakeRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const fakeRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long fakeRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of fakeRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool fakeRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of fakeRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an fakeRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool fakeRunPrintObject(
+    FILE            *stream,            ///< a stream
+    fakeRunRow *object,    ///< an fakeRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** fakeProcessedImfileRow data structure
+ *
+ * Structure for representing a single row of fakeProcessedImfile table data.
+ */
+
+typedef struct {
+    psS64           fake_id;
+    psS64           exp_id;
+    char            *class_id;
+    char            *uri;
+    psF32           dtime_fake;
+    char            *hostname;
+    char            *path_base;
+    psS16           fault;
+    psTime*         epoch;
+} fakeProcessedImfileRow;
+
+/** Creates a new fakeProcessedImfileRow object
+ *
+ *  @return A new fakeProcessedImfileRow object or NULL on failure.
+ */
+
+fakeProcessedImfileRow *fakeProcessedImfileRowAlloc(
+    psS64           fake_id,
+    psS64           exp_id,
+    const char      *class_id,
+    const char      *uri,
+    psF32           dtime_fake,
+    const char      *hostname,
+    const char      *path_base,
+    psS16           fault,
+    psTime*         epoch
+);
+
+/** Creates a new fakeProcessedImfile table
+ *
+ * @return true on success
+ */
+
+bool fakeProcessedImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a fakeProcessedImfile table
+ *
+ * @return true on success
+ */
+
+bool fakeProcessedImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool fakeProcessedImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           fake_id,
+    psS64           exp_id,
+    const char      *class_id,
+    const char      *uri,
+    psF32           dtime_fake,
+    const char      *hostname,
+    const char      *path_base,
+    psS16           fault,
+    psTime*         epoch
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long fakeProcessedImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single fakeProcessedImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool fakeProcessedImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    fakeProcessedImfileRow *object             ///< fakeProcessedImfileRow object
+);
+
+/** Insert an array of fakeProcessedImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool fakeProcessedImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of fakeProcessedImfileRow objects
+);
+
+/** Insert data from a binary FITS table fakeProcessedImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool fakeProcessedImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool fakeProcessedImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a fakeProcessedImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *fakeProcessedImfileMetadataFromObject(
+    const fakeProcessedImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A fakeProcessedImfileRow pointer or NULL on error
+ */
+
+fakeProcessedImfileRow *fakeProcessedImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as fakeProcessedImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *fakeProcessedImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an fakeProcessedImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool fakeProcessedImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const fakeProcessedImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long fakeProcessedImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of fakeProcessedImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool fakeProcessedImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of fakeProcessedImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an fakeProcessedImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool fakeProcessedImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    fakeProcessedImfileRow *object,    ///< an fakeProcessedImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** fakeMaskRow data structure
+ *
+ * Structure for representing a single row of fakeMask table data.
+ */
+
+typedef struct {
+    char            *label;
+} fakeMaskRow;
+
+/** Creates a new fakeMaskRow object
+ *
+ *  @return A new fakeMaskRow object or NULL on failure.
+ */
+
+fakeMaskRow *fakeMaskRowAlloc(
+    const char      *label
+);
+
+/** Creates a new fakeMask table
+ *
+ * @return true on success
+ */
+
+bool fakeMaskCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a fakeMask table
+ *
+ * @return true on success
+ */
+
+bool fakeMaskDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool fakeMaskInsert(
+    psDB            *dbh,               ///< Database handle
+    const char      *label
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long fakeMaskDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single fakeMaskRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool fakeMaskInsertObject(
+    psDB            *dbh,               ///< Database handle
+    fakeMaskRow     *object             ///< fakeMaskRow object
+);
+
+/** Insert an array of fakeMaskRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool fakeMaskInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of fakeMaskRow objects
+);
+
+/** Insert data from a binary FITS table fakeMaskRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool fakeMaskInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool fakeMaskSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a fakeMaskRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *fakeMaskMetadataFromObject(
+    const fakeMaskRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A fakeMaskRow pointer or NULL on error
+ */
+
+fakeMaskRow *fakeMaskObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as fakeMaskRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *fakeMaskSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an fakeMask
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool fakeMaskDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const fakeMaskRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long fakeMaskDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of fakeMaskRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool fakeMaskPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of fakeMaskRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an fakeMaskRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool fakeMaskPrintObject(
+    FILE            *stream,            ///< a stream
+    fakeMaskRow *object,    ///< an fakeMaskRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** warpRunRow data structure
+ *
+ * Structure for representing a single row of warpRun table data.
+ */
+
+typedef struct {
+    psS64           warp_id;
+    psS64           fake_id;
+    char            *mode;
+    char            *state;
+    char            *workdir;
+    char            *workdir_state;
+    char            *label;
+    char            *dvodb;
+    char            *tess_id;
+    char            *end_stage;
+    psTime*         registered;
+    bool            magiced;
+} warpRunRow;
+
+/** Creates a new warpRunRow object
+ *
+ *  @return A new warpRunRow object or NULL on failure.
+ */
+
+warpRunRow *warpRunRowAlloc(
+    psS64           warp_id,
+    psS64           fake_id,
+    const char      *mode,
+    const char      *state,
+    const char      *workdir,
+    const char      *workdir_state,
+    const char      *label,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage,
+    psTime*         registered,
+    bool            magiced
+);
+
+/** Creates a new warpRun table
+ *
+ * @return true on success
+ */
+
+bool warpRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a warpRun table
+ *
+ * @return true on success
+ */
+
+bool warpRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           warp_id,
+    psS64           fake_id,
+    const char      *mode,
+    const char      *state,
+    const char      *workdir,
+    const char      *workdir_state,
+    const char      *label,
+    const char      *dvodb,
+    const char      *tess_id,
+    const char      *end_stage,
+    psTime*         registered,
+    bool            magiced
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long warpRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single warpRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    warpRunRow      *object             ///< warpRunRow object
+);
+
+/** Insert an array of warpRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of warpRunRow objects
+);
+
+/** Insert data from a binary FITS table warpRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool warpRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool warpRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a warpRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *warpRunMetadataFromObject(
+    const warpRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A warpRunRow pointer or NULL on error
+ */
+
+warpRunRow *warpRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as warpRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *warpRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an warpRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool warpRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const warpRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long warpRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of warpRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool warpRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of warpRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an warpRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool warpRunPrintObject(
+    FILE            *stream,            ///< a stream
+    warpRunRow *object,    ///< an warpRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** warpSkyCellMapRow data structure
+ *
+ * Structure for representing a single row of warpSkyCellMap table data.
+ */
+
+typedef struct {
+    psS64           warp_id;
+    char            *skycell_id;
+    char            *tess_id;
+    char            *class_id;
+    psS16           fault;
+} warpSkyCellMapRow;
+
+/** Creates a new warpSkyCellMapRow object
+ *
+ *  @return A new warpSkyCellMapRow object or NULL on failure.
+ */
+
+warpSkyCellMapRow *warpSkyCellMapRowAlloc(
+    psS64           warp_id,
+    const char      *skycell_id,
+    const char      *tess_id,
+    const char      *class_id,
+    psS16           fault
+);
+
+/** Creates a new warpSkyCellMap table
+ *
+ * @return true on success
+ */
+
+bool warpSkyCellMapCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a warpSkyCellMap table
+ *
+ * @return true on success
+ */
+
+bool warpSkyCellMapDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpSkyCellMapInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           warp_id,
+    const char      *skycell_id,
+    const char      *tess_id,
+    const char      *class_id,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long warpSkyCellMapDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single warpSkyCellMapRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpSkyCellMapInsertObject(
+    psDB            *dbh,               ///< Database handle
+    warpSkyCellMapRow *object             ///< warpSkyCellMapRow object
+);
+
+/** Insert an array of warpSkyCellMapRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpSkyCellMapInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of warpSkyCellMapRow objects
+);
+
+/** Insert data from a binary FITS table warpSkyCellMapRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool warpSkyCellMapInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool warpSkyCellMapSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a warpSkyCellMapRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *warpSkyCellMapMetadataFromObject(
+    const warpSkyCellMapRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A warpSkyCellMapRow pointer or NULL on error
+ */
+
+warpSkyCellMapRow *warpSkyCellMapObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as warpSkyCellMapRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *warpSkyCellMapSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an warpSkyCellMap
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool warpSkyCellMapDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const warpSkyCellMapRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long warpSkyCellMapDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of warpSkyCellMapRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool warpSkyCellMapPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of warpSkyCellMapRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an warpSkyCellMapRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool warpSkyCellMapPrintObject(
+    FILE            *stream,            ///< a stream
+    warpSkyCellMapRow *object,    ///< an warpSkyCellMapRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** warpSkyfileRow data structure
+ *
+ * Structure for representing a single row of warpSkyfile table data.
+ */
+
+typedef struct {
+    psS64           warp_id;
+    char            *skycell_id;
+    char            *tess_id;
+    char            *uri;
+    char            *path_base;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF32           dtime_warp;
+    char            *hostname;
+    psF32           good_frac;
+    psS32           xmin;
+    psS32           xmax;
+    psS32           ymin;
+    psS32           ymax;
+    bool            ignored;
+    psS16           fault;
+} warpSkyfileRow;
+
+/** Creates a new warpSkyfileRow object
+ *
+ *  @return A new warpSkyfileRow object or NULL on failure.
+ */
+
+warpSkyfileRow *warpSkyfileRowAlloc(
+    psS64           warp_id,
+    const char      *skycell_id,
+    const char      *tess_id,
+    const char      *uri,
+    const char      *path_base,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF32           dtime_warp,
+    const char      *hostname,
+    psF32           good_frac,
+    psS32           xmin,
+    psS32           xmax,
+    psS32           ymin,
+    psS32           ymax,
+    bool            ignored,
+    psS16           fault
+);
+
+/** Creates a new warpSkyfile table
+ *
+ * @return true on success
+ */
+
+bool warpSkyfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a warpSkyfile table
+ *
+ * @return true on success
+ */
+
+bool warpSkyfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpSkyfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           warp_id,
+    const char      *skycell_id,
+    const char      *tess_id,
+    const char      *uri,
+    const char      *path_base,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF32           dtime_warp,
+    const char      *hostname,
+    psF32           good_frac,
+    psS32           xmin,
+    psS32           xmax,
+    psS32           ymin,
+    psS32           ymax,
+    bool            ignored,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long warpSkyfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single warpSkyfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpSkyfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    warpSkyfileRow  *object             ///< warpSkyfileRow object
+);
+
+/** Insert an array of warpSkyfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpSkyfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of warpSkyfileRow objects
+);
+
+/** Insert data from a binary FITS table warpSkyfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool warpSkyfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool warpSkyfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a warpSkyfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *warpSkyfileMetadataFromObject(
+    const warpSkyfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A warpSkyfileRow pointer or NULL on error
+ */
+
+warpSkyfileRow *warpSkyfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as warpSkyfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *warpSkyfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an warpSkyfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool warpSkyfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const warpSkyfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long warpSkyfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of warpSkyfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool warpSkyfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of warpSkyfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an warpSkyfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool warpSkyfilePrintObject(
+    FILE            *stream,            ///< a stream
+    warpSkyfileRow *object,    ///< an warpSkyfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** warpMaskRow data structure
+ *
+ * Structure for representing a single row of warpMask table data.
+ */
+
+typedef struct {
+    char            *label;
+} warpMaskRow;
+
+/** Creates a new warpMaskRow object
+ *
+ *  @return A new warpMaskRow object or NULL on failure.
+ */
+
+warpMaskRow *warpMaskRowAlloc(
+    const char      *label
+);
+
+/** Creates a new warpMask table
+ *
+ * @return true on success
+ */
+
+bool warpMaskCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a warpMask table
+ *
+ * @return true on success
+ */
+
+bool warpMaskDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpMaskInsert(
+    psDB            *dbh,               ///< Database handle
+    const char      *label
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long warpMaskDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single warpMaskRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpMaskInsertObject(
+    psDB            *dbh,               ///< Database handle
+    warpMaskRow     *object             ///< warpMaskRow object
+);
+
+/** Insert an array of warpMaskRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool warpMaskInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of warpMaskRow objects
+);
+
+/** Insert data from a binary FITS table warpMaskRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool warpMaskInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool warpMaskSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a warpMaskRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *warpMaskMetadataFromObject(
+    const warpMaskRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A warpMaskRow pointer or NULL on error
+ */
+
+warpMaskRow *warpMaskObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as warpMaskRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *warpMaskSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an warpMask
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool warpMaskDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const warpMaskRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long warpMaskDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of warpMaskRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool warpMaskPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of warpMaskRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an warpMaskRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool warpMaskPrintObject(
+    FILE            *stream,            ///< a stream
+    warpMaskRow *object,    ///< an warpMaskRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** diffRunRow data structure
+ *
+ * Structure for representing a single row of diffRun table data.
+ */
+
+typedef struct {
+    psS64           diff_id;
+    char            *state;
+    char            *workdir;
+    char            *dvodb;
+    psTime*         registered;
+    char            *skycell_id;
+    char            *tess_id;
+} diffRunRow;
+
+/** Creates a new diffRunRow object
+ *
+ *  @return A new diffRunRow object or NULL on failure.
+ */
+
+diffRunRow *diffRunRowAlloc(
+    psS64           diff_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *dvodb,
+    psTime*         registered,
+    const char      *skycell_id,
+    const char      *tess_id
+);
+
+/** Creates a new diffRun table
+ *
+ * @return true on success
+ */
+
+bool diffRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a diffRun table
+ *
+ * @return true on success
+ */
+
+bool diffRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool diffRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           diff_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *dvodb,
+    psTime*         registered,
+    const char      *skycell_id,
+    const char      *tess_id
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long diffRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single diffRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool diffRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    diffRunRow      *object             ///< diffRunRow object
+);
+
+/** Insert an array of diffRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool diffRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of diffRunRow objects
+);
+
+/** Insert data from a binary FITS table diffRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool diffRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool diffRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a diffRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *diffRunMetadataFromObject(
+    const diffRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A diffRunRow pointer or NULL on error
+ */
+
+diffRunRow *diffRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as diffRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *diffRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an diffRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool diffRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const diffRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long diffRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of diffRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool diffRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of diffRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an diffRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool diffRunPrintObject(
+    FILE            *stream,            ///< a stream
+    diffRunRow *object,    ///< an diffRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** diffInputSkyfileRow data structure
+ *
+ * Structure for representing a single row of diffInputSkyfile table data.
+ */
+
+typedef struct {
+    psS64           diff_id;
+    bool            template;
+    psS64           stack_id;
+    psS64           warp_id;
+    char            *skycell_id;
+    char            *tess_id;
+    char            *kind;
+} diffInputSkyfileRow;
+
+/** Creates a new diffInputSkyfileRow object
+ *
+ *  @return A new diffInputSkyfileRow object or NULL on failure.
+ */
+
+diffInputSkyfileRow *diffInputSkyfileRowAlloc(
+    psS64           diff_id,
+    bool            template,
+    psS64           stack_id,
+    psS64           warp_id,
+    const char      *skycell_id,
+    const char      *tess_id,
+    const char      *kind
+);
+
+/** Creates a new diffInputSkyfile table
+ *
+ * @return true on success
+ */
+
+bool diffInputSkyfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a diffInputSkyfile table
+ *
+ * @return true on success
+ */
+
+bool diffInputSkyfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool diffInputSkyfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           diff_id,
+    bool            template,
+    psS64           stack_id,
+    psS64           warp_id,
+    const char      *skycell_id,
+    const char      *tess_id,
+    const char      *kind
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long diffInputSkyfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single diffInputSkyfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool diffInputSkyfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    diffInputSkyfileRow *object             ///< diffInputSkyfileRow object
+);
+
+/** Insert an array of diffInputSkyfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool diffInputSkyfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of diffInputSkyfileRow objects
+);
+
+/** Insert data from a binary FITS table diffInputSkyfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool diffInputSkyfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool diffInputSkyfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a diffInputSkyfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *diffInputSkyfileMetadataFromObject(
+    const diffInputSkyfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A diffInputSkyfileRow pointer or NULL on error
+ */
+
+diffInputSkyfileRow *diffInputSkyfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as diffInputSkyfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *diffInputSkyfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an diffInputSkyfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool diffInputSkyfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const diffInputSkyfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long diffInputSkyfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of diffInputSkyfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool diffInputSkyfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of diffInputSkyfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an diffInputSkyfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool diffInputSkyfilePrintObject(
+    FILE            *stream,            ///< a stream
+    diffInputSkyfileRow *object,    ///< an diffInputSkyfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** diffSkyfileRow data structure
+ *
+ * Structure for representing a single row of diffSkyfile table data.
+ */
+
+typedef struct {
+    psS64           diff_id;
+    char            *uri;
+    char            *path_base;
+    psF64           bg;
+    psF64           bg_stdev;
+    psS32           stamps_num;
+    psF32           stamps_rms;
+    psS32           sources;
+    psF32           dtime_diff;
+    char            *hostname;
+    psF32           good_frac;
+    psS16           fault;
+} diffSkyfileRow;
+
+/** Creates a new diffSkyfileRow object
+ *
+ *  @return A new diffSkyfileRow object or NULL on failure.
+ */
+
+diffSkyfileRow *diffSkyfileRowAlloc(
+    psS64           diff_id,
+    const char      *uri,
+    const char      *path_base,
+    psF64           bg,
+    psF64           bg_stdev,
+    psS32           stamps_num,
+    psF32           stamps_rms,
+    psS32           sources,
+    psF32           dtime_diff,
+    const char      *hostname,
+    psF32           good_frac,
+    psS16           fault
+);
+
+/** Creates a new diffSkyfile table
+ *
+ * @return true on success
+ */
+
+bool diffSkyfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a diffSkyfile table
+ *
+ * @return true on success
+ */
+
+bool diffSkyfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool diffSkyfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           diff_id,
+    const char      *uri,
+    const char      *path_base,
+    psF64           bg,
+    psF64           bg_stdev,
+    psS32           stamps_num,
+    psF32           stamps_rms,
+    psS32           sources,
+    psF32           dtime_diff,
+    const char      *hostname,
+    psF32           good_frac,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long diffSkyfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single diffSkyfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool diffSkyfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    diffSkyfileRow  *object             ///< diffSkyfileRow object
+);
+
+/** Insert an array of diffSkyfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool diffSkyfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of diffSkyfileRow objects
+);
+
+/** Insert data from a binary FITS table diffSkyfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool diffSkyfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool diffSkyfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a diffSkyfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *diffSkyfileMetadataFromObject(
+    const diffSkyfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A diffSkyfileRow pointer or NULL on error
+ */
+
+diffSkyfileRow *diffSkyfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as diffSkyfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *diffSkyfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an diffSkyfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool diffSkyfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const diffSkyfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long diffSkyfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of diffSkyfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool diffSkyfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of diffSkyfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an diffSkyfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool diffSkyfilePrintObject(
+    FILE            *stream,            ///< a stream
+    diffSkyfileRow *object,    ///< an diffSkyfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** stackRunRow data structure
+ *
+ * Structure for representing a single row of stackRun table data.
+ */
+
+typedef struct {
+    psS64           stack_id;
+    char            *state;
+    char            *workdir;
+    char            *dvodb;
+    psTime*         registered;
+    char            *skycell_id;
+    char            *tess_id;
+    char            *filter;
+} stackRunRow;
+
+/** Creates a new stackRunRow object
+ *
+ *  @return A new stackRunRow object or NULL on failure.
+ */
+
+stackRunRow *stackRunRowAlloc(
+    psS64           stack_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *dvodb,
+    psTime*         registered,
+    const char      *skycell_id,
+    const char      *tess_id,
+    const char      *filter
+);
+
+/** Creates a new stackRun table
+ *
+ * @return true on success
+ */
+
+bool stackRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a stackRun table
+ *
+ * @return true on success
+ */
+
+bool stackRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool stackRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           stack_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *dvodb,
+    psTime*         registered,
+    const char      *skycell_id,
+    const char      *tess_id,
+    const char      *filter
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long stackRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single stackRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool stackRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    stackRunRow     *object             ///< stackRunRow object
+);
+
+/** Insert an array of stackRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool stackRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of stackRunRow objects
+);
+
+/** Insert data from a binary FITS table stackRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool stackRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool stackRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a stackRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *stackRunMetadataFromObject(
+    const stackRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A stackRunRow pointer or NULL on error
+ */
+
+stackRunRow *stackRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as stackRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *stackRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an stackRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool stackRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const stackRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long stackRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of stackRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool stackRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of stackRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an stackRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool stackRunPrintObject(
+    FILE            *stream,            ///< a stream
+    stackRunRow *object,    ///< an stackRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** stackInputSkyfileRow data structure
+ *
+ * Structure for representing a single row of stackInputSkyfile table data.
+ */
+
+typedef struct {
+    psS64           stack_id;
+    psS64           warp_id;
+} stackInputSkyfileRow;
+
+/** Creates a new stackInputSkyfileRow object
+ *
+ *  @return A new stackInputSkyfileRow object or NULL on failure.
+ */
+
+stackInputSkyfileRow *stackInputSkyfileRowAlloc(
+    psS64           stack_id,
+    psS64           warp_id
+);
+
+/** Creates a new stackInputSkyfile table
+ *
+ * @return true on success
+ */
+
+bool stackInputSkyfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a stackInputSkyfile table
+ *
+ * @return true on success
+ */
+
+bool stackInputSkyfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool stackInputSkyfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           stack_id,
+    psS64           warp_id
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long stackInputSkyfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single stackInputSkyfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool stackInputSkyfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    stackInputSkyfileRow *object             ///< stackInputSkyfileRow object
+);
+
+/** Insert an array of stackInputSkyfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool stackInputSkyfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of stackInputSkyfileRow objects
+);
+
+/** Insert data from a binary FITS table stackInputSkyfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool stackInputSkyfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool stackInputSkyfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a stackInputSkyfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *stackInputSkyfileMetadataFromObject(
+    const stackInputSkyfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A stackInputSkyfileRow pointer or NULL on error
+ */
+
+stackInputSkyfileRow *stackInputSkyfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as stackInputSkyfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *stackInputSkyfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an stackInputSkyfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool stackInputSkyfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const stackInputSkyfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long stackInputSkyfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of stackInputSkyfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool stackInputSkyfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of stackInputSkyfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an stackInputSkyfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool stackInputSkyfilePrintObject(
+    FILE            *stream,            ///< a stream
+    stackInputSkyfileRow *object,    ///< an stackInputSkyfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** stackSumSkyfileRow data structure
+ *
+ * Structure for representing a single row of stackSumSkyfile table data.
+ */
+
+typedef struct {
+    psS64           stack_id;
+    char            *uri;
+    char            *path_base;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF32           dtime_stack;
+    char            *hostname;
+    psF32           good_frac;
+    psS16           fault;
+} stackSumSkyfileRow;
+
+/** Creates a new stackSumSkyfileRow object
+ *
+ *  @return A new stackSumSkyfileRow object or NULL on failure.
+ */
+
+stackSumSkyfileRow *stackSumSkyfileRowAlloc(
+    psS64           stack_id,
+    const char      *uri,
+    const char      *path_base,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF32           dtime_stack,
+    const char      *hostname,
+    psF32           good_frac,
+    psS16           fault
+);
+
+/** Creates a new stackSumSkyfile table
+ *
+ * @return true on success
+ */
+
+bool stackSumSkyfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a stackSumSkyfile table
+ *
+ * @return true on success
+ */
+
+bool stackSumSkyfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool stackSumSkyfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           stack_id,
+    const char      *uri,
+    const char      *path_base,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF32           dtime_stack,
+    const char      *hostname,
+    psF32           good_frac,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long stackSumSkyfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single stackSumSkyfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool stackSumSkyfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    stackSumSkyfileRow *object             ///< stackSumSkyfileRow object
+);
+
+/** Insert an array of stackSumSkyfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool stackSumSkyfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of stackSumSkyfileRow objects
+);
+
+/** Insert data from a binary FITS table stackSumSkyfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool stackSumSkyfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool stackSumSkyfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a stackSumSkyfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *stackSumSkyfileMetadataFromObject(
+    const stackSumSkyfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A stackSumSkyfileRow pointer or NULL on error
+ */
+
+stackSumSkyfileRow *stackSumSkyfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as stackSumSkyfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *stackSumSkyfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an stackSumSkyfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool stackSumSkyfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const stackSumSkyfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long stackSumSkyfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of stackSumSkyfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool stackSumSkyfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of stackSumSkyfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an stackSumSkyfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool stackSumSkyfilePrintObject(
+    FILE            *stream,            ///< a stream
+    stackSumSkyfileRow *object,    ///< an stackSumSkyfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detRunRow data structure
+ *
+ * Structure for representing a single row of detRun table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS32           iteration;
+    char            *det_type;
+    char            *mode;
+    char            *state;
+    char            *filelevel;
+    char            *workdir;
+    char            *camera;
+    char            *telescope;
+    char            *exp_type;
+    char            *reduction;
+    char            *filter;
+    psF32           airmass_min;
+    psF32           airmass_max;
+    psF32           exp_time_min;
+    psF32           exp_time_max;
+    psF32           ccd_temp_min;
+    psF32           ccd_temp_max;
+    psF64           posang_min;
+    psF64           posang_max;
+    psTime*         registered;
+    psTime*         time_begin;
+    psTime*         time_end;
+    psTime*         use_begin;
+    psTime*         use_end;
+    psF32           solang_min;
+    psF32           solang_max;
+    char            *label;
+    psS32           parent;
+} detRunRow;
+
+/** Creates a new detRunRow object
+ *
+ *  @return A new detRunRow object or NULL on failure.
+ */
+
+detRunRow *detRunRowAlloc(
+    psS64           det_id,
+    psS32           iteration,
+    const char      *det_type,
+    const char      *mode,
+    const char      *state,
+    const char      *filelevel,
+    const char      *workdir,
+    const char      *camera,
+    const char      *telescope,
+    const char      *exp_type,
+    const char      *reduction,
+    const char      *filter,
+    psF32           airmass_min,
+    psF32           airmass_max,
+    psF32           exp_time_min,
+    psF32           exp_time_max,
+    psF32           ccd_temp_min,
+    psF32           ccd_temp_max,
+    psF64           posang_min,
+    psF64           posang_max,
+    psTime*         registered,
+    psTime*         time_begin,
+    psTime*         time_end,
+    psTime*         use_begin,
+    psTime*         use_end,
+    psF32           solang_min,
+    psF32           solang_max,
+    const char      *label,
+    psS32           parent
+);
+
+/** Creates a new detRun table
+ *
+ * @return true on success
+ */
+
+bool detRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detRun table
+ *
+ * @return true on success
+ */
+
+bool detRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS32           iteration,
+    const char      *det_type,
+    const char      *mode,
+    const char      *state,
+    const char      *filelevel,
+    const char      *workdir,
+    const char      *camera,
+    const char      *telescope,
+    const char      *exp_type,
+    const char      *reduction,
+    const char      *filter,
+    psF32           airmass_min,
+    psF32           airmass_max,
+    psF32           exp_time_min,
+    psF32           exp_time_max,
+    psF32           ccd_temp_min,
+    psF32           ccd_temp_max,
+    psF64           posang_min,
+    psF64           posang_max,
+    psTime*         registered,
+    psTime*         time_begin,
+    psTime*         time_end,
+    psTime*         use_begin,
+    psTime*         use_end,
+    psF32           solang_min,
+    psF32           solang_max,
+    const char      *label,
+    psS32           parent
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detRunRow       *object             ///< detRunRow object
+);
+
+/** Insert an array of detRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detRunRow objects
+);
+
+/** Insert data from a binary FITS table detRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detRunMetadataFromObject(
+    const detRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detRunRow pointer or NULL on error
+ */
+
+detRunRow *detRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detRunPrintObject(
+    FILE            *stream,            ///< a stream
+    detRunRow *object,    ///< an detRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detInputExpRow data structure
+ *
+ * Structure for representing a single row of detInputExp table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS32           iteration;
+    psS64           exp_id;
+    bool            include;
+} detInputExpRow;
+
+/** Creates a new detInputExpRow object
+ *
+ *  @return A new detInputExpRow object or NULL on failure.
+ */
+
+detInputExpRow *detInputExpRowAlloc(
+    psS64           det_id,
+    psS32           iteration,
+    psS64           exp_id,
+    bool            include
+);
+
+/** Creates a new detInputExp table
+ *
+ * @return true on success
+ */
+
+bool detInputExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detInputExp table
+ *
+ * @return true on success
+ */
+
+bool detInputExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detInputExpInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS32           iteration,
+    psS64           exp_id,
+    bool            include
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detInputExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detInputExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detInputExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detInputExpRow  *object             ///< detInputExpRow object
+);
+
+/** Insert an array of detInputExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detInputExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detInputExpRow objects
+);
+
+/** Insert data from a binary FITS table detInputExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detInputExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detInputExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detInputExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detInputExpMetadataFromObject(
+    const detInputExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detInputExpRow pointer or NULL on error
+ */
+
+detInputExpRow *detInputExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detInputExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detInputExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detInputExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detInputExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detInputExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detInputExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detInputExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detInputExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detInputExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detInputExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detInputExpPrintObject(
+    FILE            *stream,            ///< a stream
+    detInputExpRow *object,    ///< an detInputExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detProcessedImfileRow data structure
+ *
+ * Structure for representing a single row of detProcessedImfile table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS64           exp_id;
+    char            *class_id;
+    char            *uri;
+    char            *recipe;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    psF64           fringe_0;
+    psF64           fringe_1;
+    psF64           fringe_2;
+    psF64           user_1;
+    psF64           user_2;
+    psF64           user_3;
+    psF64           user_4;
+    psF64           user_5;
+    char            *path_base;
+    psS16           fault;
+} detProcessedImfileRow;
+
+/** Creates a new detProcessedImfileRow object
+ *
+ *  @return A new detProcessedImfileRow object or NULL on failure.
+ */
+
+detProcessedImfileRow *detProcessedImfileRowAlloc(
+    psS64           det_id,
+    psS64           exp_id,
+    const char      *class_id,
+    const char      *uri,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           fringe_0,
+    psF64           fringe_1,
+    psF64           fringe_2,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Creates a new detProcessedImfile table
+ *
+ * @return true on success
+ */
+
+bool detProcessedImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detProcessedImfile table
+ *
+ * @return true on success
+ */
+
+bool detProcessedImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detProcessedImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS64           exp_id,
+    const char      *class_id,
+    const char      *uri,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           fringe_0,
+    psF64           fringe_1,
+    psF64           fringe_2,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detProcessedImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detProcessedImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detProcessedImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detProcessedImfileRow *object             ///< detProcessedImfileRow object
+);
+
+/** Insert an array of detProcessedImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detProcessedImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detProcessedImfileRow objects
+);
+
+/** Insert data from a binary FITS table detProcessedImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detProcessedImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detProcessedImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detProcessedImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detProcessedImfileMetadataFromObject(
+    const detProcessedImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detProcessedImfileRow pointer or NULL on error
+ */
+
+detProcessedImfileRow *detProcessedImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detProcessedImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detProcessedImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detProcessedImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detProcessedImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detProcessedImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detProcessedImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detProcessedImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detProcessedImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detProcessedImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detProcessedImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detProcessedImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    detProcessedImfileRow *object,    ///< an detProcessedImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detProcessedExpRow data structure
+ *
+ * Structure for representing a single row of detProcessedExp table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS64           exp_id;
+    char            *recipe;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    psF64           fringe_0;
+    psF64           fringe_1;
+    psF64           fringe_2;
+    psF64           user_1;
+    psF64           user_2;
+    psF64           user_3;
+    psF64           user_4;
+    psF64           user_5;
+    char            *path_base;
+    psS16           fault;
+} detProcessedExpRow;
+
+/** Creates a new detProcessedExpRow object
+ *
+ *  @return A new detProcessedExpRow object or NULL on failure.
+ */
+
+detProcessedExpRow *detProcessedExpRowAlloc(
+    psS64           det_id,
+    psS64           exp_id,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           fringe_0,
+    psF64           fringe_1,
+    psF64           fringe_2,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Creates a new detProcessedExp table
+ *
+ * @return true on success
+ */
+
+bool detProcessedExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detProcessedExp table
+ *
+ * @return true on success
+ */
+
+bool detProcessedExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detProcessedExpInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS64           exp_id,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           fringe_0,
+    psF64           fringe_1,
+    psF64           fringe_2,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detProcessedExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detProcessedExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detProcessedExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detProcessedExpRow *object             ///< detProcessedExpRow object
+);
+
+/** Insert an array of detProcessedExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detProcessedExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detProcessedExpRow objects
+);
+
+/** Insert data from a binary FITS table detProcessedExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detProcessedExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detProcessedExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detProcessedExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detProcessedExpMetadataFromObject(
+    const detProcessedExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detProcessedExpRow pointer or NULL on error
+ */
+
+detProcessedExpRow *detProcessedExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detProcessedExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detProcessedExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detProcessedExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detProcessedExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detProcessedExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detProcessedExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detProcessedExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detProcessedExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detProcessedExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detProcessedExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detProcessedExpPrintObject(
+    FILE            *stream,            ///< a stream
+    detProcessedExpRow *object,    ///< an detProcessedExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detStackedImfileRow data structure
+ *
+ * Structure for representing a single row of detStackedImfile table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS32           iteration;
+    char            *class_id;
+    char            *uri;
+    char            *recipe;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    psF64           user_1;
+    psF64           user_2;
+    psF64           user_3;
+    psF64           user_4;
+    psF64           user_5;
+    psS16           fault;
+} detStackedImfileRow;
+
+/** Creates a new detStackedImfileRow object
+ *
+ *  @return A new detStackedImfileRow object or NULL on failure.
+ */
+
+detStackedImfileRow *detStackedImfileRowAlloc(
+    psS64           det_id,
+    psS32           iteration,
+    const char      *class_id,
+    const char      *uri,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    psS16           fault
+);
+
+/** Creates a new detStackedImfile table
+ *
+ * @return true on success
+ */
+
+bool detStackedImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detStackedImfile table
+ *
+ * @return true on success
+ */
+
+bool detStackedImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detStackedImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS32           iteration,
+    const char      *class_id,
+    const char      *uri,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detStackedImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detStackedImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detStackedImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detStackedImfileRow *object             ///< detStackedImfileRow object
+);
+
+/** Insert an array of detStackedImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detStackedImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detStackedImfileRow objects
+);
+
+/** Insert data from a binary FITS table detStackedImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detStackedImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detStackedImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detStackedImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detStackedImfileMetadataFromObject(
+    const detStackedImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detStackedImfileRow pointer or NULL on error
+ */
+
+detStackedImfileRow *detStackedImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detStackedImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detStackedImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detStackedImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detStackedImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detStackedImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detStackedImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detStackedImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detStackedImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detStackedImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detStackedImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detStackedImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    detStackedImfileRow *object,    ///< an detStackedImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detNormalizedStatImfileRow data structure
+ *
+ * Structure for representing a single row of detNormalizedStatImfile table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS32           iteration;
+    char            *class_id;
+    psF32           norm;
+    psS16           fault;
+} detNormalizedStatImfileRow;
+
+/** Creates a new detNormalizedStatImfileRow object
+ *
+ *  @return A new detNormalizedStatImfileRow object or NULL on failure.
+ */
+
+detNormalizedStatImfileRow *detNormalizedStatImfileRowAlloc(
+    psS64           det_id,
+    psS32           iteration,
+    const char      *class_id,
+    psF32           norm,
+    psS16           fault
+);
+
+/** Creates a new detNormalizedStatImfile table
+ *
+ * @return true on success
+ */
+
+bool detNormalizedStatImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detNormalizedStatImfile table
+ *
+ * @return true on success
+ */
+
+bool detNormalizedStatImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedStatImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS32           iteration,
+    const char      *class_id,
+    psF32           norm,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detNormalizedStatImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detNormalizedStatImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedStatImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detNormalizedStatImfileRow *object             ///< detNormalizedStatImfileRow object
+);
+
+/** Insert an array of detNormalizedStatImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedStatImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detNormalizedStatImfileRow objects
+);
+
+/** Insert data from a binary FITS table detNormalizedStatImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedStatImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedStatImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detNormalizedStatImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detNormalizedStatImfileMetadataFromObject(
+    const detNormalizedStatImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detNormalizedStatImfileRow pointer or NULL on error
+ */
+
+detNormalizedStatImfileRow *detNormalizedStatImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detNormalizedStatImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detNormalizedStatImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detNormalizedStatImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detNormalizedStatImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detNormalizedStatImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detNormalizedStatImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detNormalizedStatImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedStatImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detNormalizedStatImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detNormalizedStatImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedStatImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    detNormalizedStatImfileRow *object,    ///< an detNormalizedStatImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detNormalizedImfileRow data structure
+ *
+ * Structure for representing a single row of detNormalizedImfile table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS32           iteration;
+    char            *class_id;
+    char            *uri;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    psF64           user_1;
+    psF64           user_2;
+    psF64           user_3;
+    psF64           user_4;
+    psF64           user_5;
+    char            *path_base;
+    psS16           fault;
+} detNormalizedImfileRow;
+
+/** Creates a new detNormalizedImfileRow object
+ *
+ *  @return A new detNormalizedImfileRow object or NULL on failure.
+ */
+
+detNormalizedImfileRow *detNormalizedImfileRowAlloc(
+    psS64           det_id,
+    psS32           iteration,
+    const char      *class_id,
+    const char      *uri,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Creates a new detNormalizedImfile table
+ *
+ * @return true on success
+ */
+
+bool detNormalizedImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detNormalizedImfile table
+ *
+ * @return true on success
+ */
+
+bool detNormalizedImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS32           iteration,
+    const char      *class_id,
+    const char      *uri,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detNormalizedImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detNormalizedImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detNormalizedImfileRow *object             ///< detNormalizedImfileRow object
+);
+
+/** Insert an array of detNormalizedImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detNormalizedImfileRow objects
+);
+
+/** Insert data from a binary FITS table detNormalizedImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detNormalizedImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detNormalizedImfileMetadataFromObject(
+    const detNormalizedImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detNormalizedImfileRow pointer or NULL on error
+ */
+
+detNormalizedImfileRow *detNormalizedImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detNormalizedImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detNormalizedImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detNormalizedImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detNormalizedImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detNormalizedImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detNormalizedImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detNormalizedImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detNormalizedImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detNormalizedImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    detNormalizedImfileRow *object,    ///< an detNormalizedImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detNormalizedExpRow data structure
+ *
+ * Structure for representing a single row of detNormalizedExp table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS32           iteration;
+    char            *recipe;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    psF64           user_1;
+    psF64           user_2;
+    psF64           user_3;
+    psF64           user_4;
+    psF64           user_5;
+    char            *path_base;
+    psS16           fault;
+} detNormalizedExpRow;
+
+/** Creates a new detNormalizedExpRow object
+ *
+ *  @return A new detNormalizedExpRow object or NULL on failure.
+ */
+
+detNormalizedExpRow *detNormalizedExpRowAlloc(
+    psS64           det_id,
+    psS32           iteration,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Creates a new detNormalizedExp table
+ *
+ * @return true on success
+ */
+
+bool detNormalizedExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detNormalizedExp table
+ *
+ * @return true on success
+ */
+
+bool detNormalizedExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedExpInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS32           iteration,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detNormalizedExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detNormalizedExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detNormalizedExpRow *object             ///< detNormalizedExpRow object
+);
+
+/** Insert an array of detNormalizedExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detNormalizedExpRow objects
+);
+
+/** Insert data from a binary FITS table detNormalizedExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detNormalizedExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detNormalizedExpMetadataFromObject(
+    const detNormalizedExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detNormalizedExpRow pointer or NULL on error
+ */
+
+detNormalizedExpRow *detNormalizedExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detNormalizedExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detNormalizedExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detNormalizedExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detNormalizedExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detNormalizedExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detNormalizedExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detNormalizedExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detNormalizedExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detNormalizedExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detNormalizedExpPrintObject(
+    FILE            *stream,            ///< a stream
+    detNormalizedExpRow *object,    ///< an detNormalizedExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detResidImfileRow data structure
+ *
+ * Structure for representing a single row of detResidImfile table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS32           iteration;
+    psS64           exp_id;
+    char            *class_id;
+    char            *uri;
+    char            *recipe;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    psF64           bg_skewness;
+    psF64           bg_kurtosis;
+    psF64           bin_stdev;
+    psF64           fringe_0;
+    psF64           fringe_1;
+    psF64           fringe_2;
+    psF64           fringe_resid_0;
+    psF64           fringe_resid_1;
+    psF64           fringe_resid_2;
+    psF64           user_1;
+    psF64           user_2;
+    psF64           user_3;
+    psF64           user_4;
+    psF64           user_5;
+    char            *path_base;
+    psS16           fault;
+} detResidImfileRow;
+
+/** Creates a new detResidImfileRow object
+ *
+ *  @return A new detResidImfileRow object or NULL on failure.
+ */
+
+detResidImfileRow *detResidImfileRowAlloc(
+    psS64           det_id,
+    psS32           iteration,
+    psS64           exp_id,
+    const char      *class_id,
+    const char      *uri,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           bg_skewness,
+    psF64           bg_kurtosis,
+    psF64           bin_stdev,
+    psF64           fringe_0,
+    psF64           fringe_1,
+    psF64           fringe_2,
+    psF64           fringe_resid_0,
+    psF64           fringe_resid_1,
+    psF64           fringe_resid_2,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Creates a new detResidImfile table
+ *
+ * @return true on success
+ */
+
+bool detResidImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detResidImfile table
+ *
+ * @return true on success
+ */
+
+bool detResidImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detResidImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS32           iteration,
+    psS64           exp_id,
+    const char      *class_id,
+    const char      *uri,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           bg_skewness,
+    psF64           bg_kurtosis,
+    psF64           bin_stdev,
+    psF64           fringe_0,
+    psF64           fringe_1,
+    psF64           fringe_2,
+    psF64           fringe_resid_0,
+    psF64           fringe_resid_1,
+    psF64           fringe_resid_2,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detResidImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detResidImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detResidImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detResidImfileRow *object             ///< detResidImfileRow object
+);
+
+/** Insert an array of detResidImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detResidImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detResidImfileRow objects
+);
+
+/** Insert data from a binary FITS table detResidImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detResidImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detResidImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detResidImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detResidImfileMetadataFromObject(
+    const detResidImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detResidImfileRow pointer or NULL on error
+ */
+
+detResidImfileRow *detResidImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detResidImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detResidImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detResidImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detResidImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detResidImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detResidImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detResidImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detResidImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detResidImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detResidImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detResidImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    detResidImfileRow *object,    ///< an detResidImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detResidExpRow data structure
+ *
+ * Structure for representing a single row of detResidExp table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS32           iteration;
+    psS64           exp_id;
+    char            *recipe;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    psF64           bg_skewness;
+    psF64           bg_kurtosis;
+    psF64           bin_stdev;
+    psF64           fringe_0;
+    psF64           fringe_1;
+    psF64           fringe_2;
+    psF64           fringe_resid_0;
+    psF64           fringe_resid_1;
+    psF64           fringe_resid_2;
+    psF64           user_1;
+    psF64           user_2;
+    psF64           user_3;
+    psF64           user_4;
+    psF64           user_5;
+    char            *path_base;
+    bool            accept;
+    psS16           fault;
+} detResidExpRow;
+
+/** Creates a new detResidExpRow object
+ *
+ *  @return A new detResidExpRow object or NULL on failure.
+ */
+
+detResidExpRow *detResidExpRowAlloc(
+    psS64           det_id,
+    psS32           iteration,
+    psS64           exp_id,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           bg_skewness,
+    psF64           bg_kurtosis,
+    psF64           bin_stdev,
+    psF64           fringe_0,
+    psF64           fringe_1,
+    psF64           fringe_2,
+    psF64           fringe_resid_0,
+    psF64           fringe_resid_1,
+    psF64           fringe_resid_2,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    bool            accept,
+    psS16           fault
+);
+
+/** Creates a new detResidExp table
+ *
+ * @return true on success
+ */
+
+bool detResidExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detResidExp table
+ *
+ * @return true on success
+ */
+
+bool detResidExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detResidExpInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS32           iteration,
+    psS64           exp_id,
+    const char      *recipe,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           bg_skewness,
+    psF64           bg_kurtosis,
+    psF64           bin_stdev,
+    psF64           fringe_0,
+    psF64           fringe_1,
+    psF64           fringe_2,
+    psF64           fringe_resid_0,
+    psF64           fringe_resid_1,
+    psF64           fringe_resid_2,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    bool            accept,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detResidExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detResidExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detResidExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detResidExpRow  *object             ///< detResidExpRow object
+);
+
+/** Insert an array of detResidExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detResidExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detResidExpRow objects
+);
+
+/** Insert data from a binary FITS table detResidExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detResidExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detResidExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detResidExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detResidExpMetadataFromObject(
+    const detResidExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detResidExpRow pointer or NULL on error
+ */
+
+detResidExpRow *detResidExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detResidExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detResidExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detResidExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detResidExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detResidExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detResidExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detResidExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detResidExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detResidExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detResidExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detResidExpPrintObject(
+    FILE            *stream,            ///< a stream
+    detResidExpRow *object,    ///< an detResidExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detRunSummaryRow data structure
+ *
+ * Structure for representing a single row of detRunSummary table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS32           iteration;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    bool            accept;
+    psS16           fault;
+} detRunSummaryRow;
+
+/** Creates a new detRunSummaryRow object
+ *
+ *  @return A new detRunSummaryRow object or NULL on failure.
+ */
+
+detRunSummaryRow *detRunSummaryRowAlloc(
+    psS64           det_id,
+    psS32           iteration,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    bool            accept,
+    psS16           fault
+);
+
+/** Creates a new detRunSummary table
+ *
+ * @return true on success
+ */
+
+bool detRunSummaryCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detRunSummary table
+ *
+ * @return true on success
+ */
+
+bool detRunSummaryDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detRunSummaryInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS32           iteration,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    bool            accept,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detRunSummaryDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detRunSummaryRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detRunSummaryInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detRunSummaryRow *object             ///< detRunSummaryRow object
+);
+
+/** Insert an array of detRunSummaryRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detRunSummaryInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detRunSummaryRow objects
+);
+
+/** Insert data from a binary FITS table detRunSummaryRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detRunSummaryInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detRunSummarySelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detRunSummaryRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detRunSummaryMetadataFromObject(
+    const detRunSummaryRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detRunSummaryRow pointer or NULL on error
+ */
+
+detRunSummaryRow *detRunSummaryObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detRunSummaryRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detRunSummarySelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detRunSummary
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detRunSummaryDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detRunSummaryRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detRunSummaryDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detRunSummaryRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detRunSummaryPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detRunSummaryRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detRunSummaryRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detRunSummaryPrintObject(
+    FILE            *stream,            ///< a stream
+    detRunSummaryRow *object,    ///< an detRunSummaryRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detRegisteredImfileRow data structure
+ *
+ * Structure for representing a single row of detRegisteredImfile table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS32           iteration;
+    char            *class_id;
+    char            *uri;
+    psF64           bg;
+    psF64           bg_stdev;
+    psF64           bg_mean_stdev;
+    psF64           user_1;
+    psF64           user_2;
+    psF64           user_3;
+    psF64           user_4;
+    psF64           user_5;
+    char            *path_base;
+    psS16           fault;
+} detRegisteredImfileRow;
+
+/** Creates a new detRegisteredImfileRow object
+ *
+ *  @return A new detRegisteredImfileRow object or NULL on failure.
+ */
+
+detRegisteredImfileRow *detRegisteredImfileRowAlloc(
+    psS64           det_id,
+    psS32           iteration,
+    const char      *class_id,
+    const char      *uri,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Creates a new detRegisteredImfile table
+ *
+ * @return true on success
+ */
+
+bool detRegisteredImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detRegisteredImfile table
+ *
+ * @return true on success
+ */
+
+bool detRegisteredImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detRegisteredImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS32           iteration,
+    const char      *class_id,
+    const char      *uri,
+    psF64           bg,
+    psF64           bg_stdev,
+    psF64           bg_mean_stdev,
+    psF64           user_1,
+    psF64           user_2,
+    psF64           user_3,
+    psF64           user_4,
+    psF64           user_5,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detRegisteredImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detRegisteredImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detRegisteredImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detRegisteredImfileRow *object             ///< detRegisteredImfileRow object
+);
+
+/** Insert an array of detRegisteredImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detRegisteredImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detRegisteredImfileRow objects
+);
+
+/** Insert data from a binary FITS table detRegisteredImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detRegisteredImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detRegisteredImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detRegisteredImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detRegisteredImfileMetadataFromObject(
+    const detRegisteredImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detRegisteredImfileRow pointer or NULL on error
+ */
+
+detRegisteredImfileRow *detRegisteredImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detRegisteredImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detRegisteredImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detRegisteredImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detRegisteredImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detRegisteredImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detRegisteredImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detRegisteredImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detRegisteredImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detRegisteredImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detRegisteredImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detRegisteredImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    detRegisteredImfileRow *object,    ///< an detRegisteredImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detCorrectedExpRow data structure
+ *
+ * Structure for representing a single row of detCorrectedExp table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS64           exp_id;
+    char            *uri;
+    psS64           corr_id;
+    char            *corr_type;
+    char            *recipe;
+    char            *path_base;
+    psS16           fault;
+} detCorrectedExpRow;
+
+/** Creates a new detCorrectedExpRow object
+ *
+ *  @return A new detCorrectedExpRow object or NULL on failure.
+ */
+
+detCorrectedExpRow *detCorrectedExpRowAlloc(
+    psS64           det_id,
+    psS64           exp_id,
+    const char      *uri,
+    psS64           corr_id,
+    const char      *corr_type,
+    const char      *recipe,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Creates a new detCorrectedExp table
+ *
+ * @return true on success
+ */
+
+bool detCorrectedExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detCorrectedExp table
+ *
+ * @return true on success
+ */
+
+bool detCorrectedExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedExpInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS64           exp_id,
+    const char      *uri,
+    psS64           corr_id,
+    const char      *corr_type,
+    const char      *recipe,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detCorrectedExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detCorrectedExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detCorrectedExpRow *object             ///< detCorrectedExpRow object
+);
+
+/** Insert an array of detCorrectedExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detCorrectedExpRow objects
+);
+
+/** Insert data from a binary FITS table detCorrectedExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detCorrectedExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detCorrectedExpMetadataFromObject(
+    const detCorrectedExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detCorrectedExpRow pointer or NULL on error
+ */
+
+detCorrectedExpRow *detCorrectedExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detCorrectedExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detCorrectedExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detCorrectedExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detCorrectedExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detCorrectedExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detCorrectedExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detCorrectedExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detCorrectedExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detCorrectedExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedExpPrintObject(
+    FILE            *stream,            ///< a stream
+    detCorrectedExpRow *object,    ///< an detCorrectedExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** detCorrectedImfileRow data structure
+ *
+ * Structure for representing a single row of detCorrectedImfile table data.
+ */
+
+typedef struct {
+    psS64           det_id;
+    psS64           exp_id;
+    char            *class_id;
+    char            *uri;
+    char            *path_base;
+    psS16           fault;
+} detCorrectedImfileRow;
+
+/** Creates a new detCorrectedImfileRow object
+ *
+ *  @return A new detCorrectedImfileRow object or NULL on failure.
+ */
+
+detCorrectedImfileRow *detCorrectedImfileRowAlloc(
+    psS64           det_id,
+    psS64           exp_id,
+    const char      *class_id,
+    const char      *uri,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Creates a new detCorrectedImfile table
+ *
+ * @return true on success
+ */
+
+bool detCorrectedImfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a detCorrectedImfile table
+ *
+ * @return true on success
+ */
+
+bool detCorrectedImfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedImfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           det_id,
+    psS64           exp_id,
+    const char      *class_id,
+    const char      *uri,
+    const char      *path_base,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detCorrectedImfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single detCorrectedImfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedImfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    detCorrectedImfileRow *object             ///< detCorrectedImfileRow object
+);
+
+/** Insert an array of detCorrectedImfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedImfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of detCorrectedImfileRow objects
+);
+
+/** Insert data from a binary FITS table detCorrectedImfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedImfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedImfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a detCorrectedImfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *detCorrectedImfileMetadataFromObject(
+    const detCorrectedImfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A detCorrectedImfileRow pointer or NULL on error
+ */
+
+detCorrectedImfileRow *detCorrectedImfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as detCorrectedImfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *detCorrectedImfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an detCorrectedImfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool detCorrectedImfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const detCorrectedImfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long detCorrectedImfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of detCorrectedImfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedImfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of detCorrectedImfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an detCorrectedImfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool detCorrectedImfilePrintObject(
+    FILE            *stream,            ///< a stream
+    detCorrectedImfileRow *object,    ///< an detCorrectedImfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** magicRunRow data structure
+ *
+ * Structure for representing a single row of magicRun table data.
+ */
+
+typedef struct {
+    psS64           magic_id;
+    psS64           exp_id;
+    char            *state;
+    char            *workdir;
+    char            *workdir_state;
+    char            *label;
+    char            *dvodb;
+    psTime*         registered;
+    psS16           fault;
+} magicRunRow;
+
+/** Creates a new magicRunRow object
+ *
+ *  @return A new magicRunRow object or NULL on failure.
+ */
+
+magicRunRow *magicRunRowAlloc(
+    psS64           magic_id,
+    psS64           exp_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *workdir_state,
+    const char      *label,
+    const char      *dvodb,
+    psTime*         registered,
+    psS16           fault
+);
+
+/** Creates a new magicRun table
+ *
+ * @return true on success
+ */
+
+bool magicRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a magicRun table
+ *
+ * @return true on success
+ */
+
+bool magicRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           magic_id,
+    psS64           exp_id,
+    const char      *state,
+    const char      *workdir,
+    const char      *workdir_state,
+    const char      *label,
+    const char      *dvodb,
+    psTime*         registered,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long magicRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single magicRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    magicRunRow     *object             ///< magicRunRow object
+);
+
+/** Insert an array of magicRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of magicRunRow objects
+);
+
+/** Insert data from a binary FITS table magicRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool magicRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool magicRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a magicRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *magicRunMetadataFromObject(
+    const magicRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A magicRunRow pointer or NULL on error
+ */
+
+magicRunRow *magicRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as magicRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *magicRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an magicRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool magicRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const magicRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long magicRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of magicRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool magicRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of magicRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an magicRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool magicRunPrintObject(
+    FILE            *stream,            ///< a stream
+    magicRunRow *object,    ///< an magicRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** magicInputSkyfileRow data structure
+ *
+ * Structure for representing a single row of magicInputSkyfile table data.
+ */
+
+typedef struct {
+    psS64           magic_id;
+    psS64           diff_id;
+    char            *node;
+} magicInputSkyfileRow;
+
+/** Creates a new magicInputSkyfileRow object
+ *
+ *  @return A new magicInputSkyfileRow object or NULL on failure.
+ */
+
+magicInputSkyfileRow *magicInputSkyfileRowAlloc(
+    psS64           magic_id,
+    psS64           diff_id,
+    const char      *node
+);
+
+/** Creates a new magicInputSkyfile table
+ *
+ * @return true on success
+ */
+
+bool magicInputSkyfileCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a magicInputSkyfile table
+ *
+ * @return true on success
+ */
+
+bool magicInputSkyfileDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicInputSkyfileInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           magic_id,
+    psS64           diff_id,
+    const char      *node
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long magicInputSkyfileDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single magicInputSkyfileRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicInputSkyfileInsertObject(
+    psDB            *dbh,               ///< Database handle
+    magicInputSkyfileRow *object             ///< magicInputSkyfileRow object
+);
+
+/** Insert an array of magicInputSkyfileRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicInputSkyfileInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of magicInputSkyfileRow objects
+);
+
+/** Insert data from a binary FITS table magicInputSkyfileRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool magicInputSkyfileInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool magicInputSkyfileSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a magicInputSkyfileRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *magicInputSkyfileMetadataFromObject(
+    const magicInputSkyfileRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A magicInputSkyfileRow pointer or NULL on error
+ */
+
+magicInputSkyfileRow *magicInputSkyfileObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as magicInputSkyfileRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *magicInputSkyfileSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an magicInputSkyfile
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool magicInputSkyfileDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const magicInputSkyfileRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long magicInputSkyfileDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of magicInputSkyfileRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool magicInputSkyfilePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of magicInputSkyfileRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an magicInputSkyfileRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool magicInputSkyfilePrintObject(
+    FILE            *stream,            ///< a stream
+    magicInputSkyfileRow *object,    ///< an magicInputSkyfileRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** magicTreeRow data structure
+ *
+ * Structure for representing a single row of magicTree table data.
+ */
+
+typedef struct {
+    psS64           magic_id;
+    char            *node;
+    char            *dep;
+} magicTreeRow;
+
+/** Creates a new magicTreeRow object
+ *
+ *  @return A new magicTreeRow object or NULL on failure.
+ */
+
+magicTreeRow *magicTreeRowAlloc(
+    psS64           magic_id,
+    const char      *node,
+    const char      *dep
+);
+
+/** Creates a new magicTree table
+ *
+ * @return true on success
+ */
+
+bool magicTreeCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a magicTree table
+ *
+ * @return true on success
+ */
+
+bool magicTreeDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicTreeInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           magic_id,
+    const char      *node,
+    const char      *dep
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long magicTreeDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single magicTreeRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicTreeInsertObject(
+    psDB            *dbh,               ///< Database handle
+    magicTreeRow    *object             ///< magicTreeRow object
+);
+
+/** Insert an array of magicTreeRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicTreeInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of magicTreeRow objects
+);
+
+/** Insert data from a binary FITS table magicTreeRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool magicTreeInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool magicTreeSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a magicTreeRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *magicTreeMetadataFromObject(
+    const magicTreeRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A magicTreeRow pointer or NULL on error
+ */
+
+magicTreeRow *magicTreeObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as magicTreeRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *magicTreeSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an magicTree
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool magicTreeDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const magicTreeRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long magicTreeDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of magicTreeRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool magicTreePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of magicTreeRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an magicTreeRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool magicTreePrintObject(
+    FILE            *stream,            ///< a stream
+    magicTreeRow *object,    ///< an magicTreeRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** magicNodeResultRow data structure
+ *
+ * Structure for representing a single row of magicNodeResult table data.
+ */
+
+typedef struct {
+    psS64           magic_id;
+    char            *node;
+    char            *uri;
+    psS16           fault;
+} magicNodeResultRow;
+
+/** Creates a new magicNodeResultRow object
+ *
+ *  @return A new magicNodeResultRow object or NULL on failure.
+ */
+
+magicNodeResultRow *magicNodeResultRowAlloc(
+    psS64           magic_id,
+    const char      *node,
+    const char      *uri,
+    psS16           fault
+);
+
+/** Creates a new magicNodeResult table
+ *
+ * @return true on success
+ */
+
+bool magicNodeResultCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a magicNodeResult table
+ *
+ * @return true on success
+ */
+
+bool magicNodeResultDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicNodeResultInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           magic_id,
+    const char      *node,
+    const char      *uri,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long magicNodeResultDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single magicNodeResultRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicNodeResultInsertObject(
+    psDB            *dbh,               ///< Database handle
+    magicNodeResultRow *object             ///< magicNodeResultRow object
+);
+
+/** Insert an array of magicNodeResultRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicNodeResultInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of magicNodeResultRow objects
+);
+
+/** Insert data from a binary FITS table magicNodeResultRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool magicNodeResultInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool magicNodeResultSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a magicNodeResultRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *magicNodeResultMetadataFromObject(
+    const magicNodeResultRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A magicNodeResultRow pointer or NULL on error
+ */
+
+magicNodeResultRow *magicNodeResultObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as magicNodeResultRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *magicNodeResultSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an magicNodeResult
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool magicNodeResultDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const magicNodeResultRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long magicNodeResultDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of magicNodeResultRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool magicNodeResultPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of magicNodeResultRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an magicNodeResultRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool magicNodeResultPrintObject(
+    FILE            *stream,            ///< a stream
+    magicNodeResultRow *object,    ///< an magicNodeResultRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** magicMaskRow data structure
+ *
+ * Structure for representing a single row of magicMask table data.
+ */
+
+typedef struct {
+    psS64           magic_id;
+    char            *uri;
+    psS32           streaks;
+    psS16           fault;
+} magicMaskRow;
+
+/** Creates a new magicMaskRow object
+ *
+ *  @return A new magicMaskRow object or NULL on failure.
+ */
+
+magicMaskRow *magicMaskRowAlloc(
+    psS64           magic_id,
+    const char      *uri,
+    psS32           streaks,
+    psS16           fault
+);
+
+/** Creates a new magicMask table
+ *
+ * @return true on success
+ */
+
+bool magicMaskCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a magicMask table
+ *
+ * @return true on success
+ */
+
+bool magicMaskDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicMaskInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           magic_id,
+    const char      *uri,
+    psS32           streaks,
+    psS16           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long magicMaskDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single magicMaskRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicMaskInsertObject(
+    psDB            *dbh,               ///< Database handle
+    magicMaskRow    *object             ///< magicMaskRow object
+);
+
+/** Insert an array of magicMaskRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool magicMaskInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of magicMaskRow objects
+);
+
+/** Insert data from a binary FITS table magicMaskRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool magicMaskInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool magicMaskSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a magicMaskRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *magicMaskMetadataFromObject(
+    const magicMaskRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A magicMaskRow pointer or NULL on error
+ */
+
+magicMaskRow *magicMaskObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as magicMaskRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *magicMaskSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an magicMask
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool magicMaskDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const magicMaskRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long magicMaskDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of magicMaskRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool magicMaskPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of magicMaskRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an magicMaskRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool magicMaskPrintObject(
+    FILE            *stream,            ///< a stream
+    magicMaskRow *object,    ///< an magicMaskRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** calDBRow data structure
+ *
+ * Structure for representing a single row of calDB table data.
+ */
+
+typedef struct {
+    psS64           cal_id;
+    char            *dvodb;
+    char            *state;
+} calDBRow;
+
+/** Creates a new calDBRow object
+ *
+ *  @return A new calDBRow object or NULL on failure.
+ */
+
+calDBRow *calDBRowAlloc(
+    psS64           cal_id,
+    const char      *dvodb,
+    const char      *state
+);
+
+/** Creates a new calDB table
+ *
+ * @return true on success
+ */
+
+bool calDBCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a calDB table
+ *
+ * @return true on success
+ */
+
+bool calDBDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calDBInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           cal_id,
+    const char      *dvodb,
+    const char      *state
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long calDBDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single calDBRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calDBInsertObject(
+    psDB            *dbh,               ///< Database handle
+    calDBRow        *object             ///< calDBRow object
+);
+
+/** Insert an array of calDBRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calDBInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of calDBRow objects
+);
+
+/** Insert data from a binary FITS table calDBRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool calDBInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool calDBSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a calDBRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *calDBMetadataFromObject(
+    const calDBRow  *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A calDBRow pointer or NULL on error
+ */
+
+calDBRow *calDBObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as calDBRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *calDBSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an calDB
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool calDBDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const calDBRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long calDBDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of calDBRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool calDBPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of calDBRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an calDBRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool calDBPrintObject(
+    FILE            *stream,            ///< a stream
+    calDBRow *object,    ///< an calDBRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** calRunRow data structure
+ *
+ * Structure for representing a single row of calRun table data.
+ */
+
+typedef struct {
+    psS64           cal_id;
+    char            *region;
+    char            *last_step;
+    char            *state;
+} calRunRow;
+
+/** Creates a new calRunRow object
+ *
+ *  @return A new calRunRow object or NULL on failure.
+ */
+
+calRunRow *calRunRowAlloc(
+    psS64           cal_id,
+    const char      *region,
+    const char      *last_step,
+    const char      *state
+);
+
+/** Creates a new calRun table
+ *
+ * @return true on success
+ */
+
+bool calRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a calRun table
+ *
+ * @return true on success
+ */
+
+bool calRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           cal_id,
+    const char      *region,
+    const char      *last_step,
+    const char      *state
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long calRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single calRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    calRunRow       *object             ///< calRunRow object
+);
+
+/** Insert an array of calRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of calRunRow objects
+);
+
+/** Insert data from a binary FITS table calRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool calRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool calRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a calRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *calRunMetadataFromObject(
+    const calRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A calRunRow pointer or NULL on error
+ */
+
+calRunRow *calRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as calRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *calRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an calRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool calRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const calRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long calRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of calRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool calRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of calRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an calRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool calRunPrintObject(
+    FILE            *stream,            ///< a stream
+    calRunRow *object,    ///< an calRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** flatcorrRunRow data structure
+ *
+ * Structure for representing a single row of flatcorrRun table data.
+ */
+
+typedef struct {
+    psS64           corr_id;
+    char            *dvodb;
+    char            *filter;
+    char            *state;
+    char            *workdir;
+    char            *label;
+    char            *stats;
+    char            *region;
+} flatcorrRunRow;
+
+/** Creates a new flatcorrRunRow object
+ *
+ *  @return A new flatcorrRunRow object or NULL on failure.
+ */
+
+flatcorrRunRow *flatcorrRunRowAlloc(
+    psS64           corr_id,
+    const char      *dvodb,
+    const char      *filter,
+    const char      *state,
+    const char      *workdir,
+    const char      *label,
+    const char      *stats,
+    const char      *region
+);
+
+/** Creates a new flatcorrRun table
+ *
+ * @return true on success
+ */
+
+bool flatcorrRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a flatcorrRun table
+ *
+ * @return true on success
+ */
+
+bool flatcorrRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool flatcorrRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           corr_id,
+    const char      *dvodb,
+    const char      *filter,
+    const char      *state,
+    const char      *workdir,
+    const char      *label,
+    const char      *stats,
+    const char      *region
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long flatcorrRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single flatcorrRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool flatcorrRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    flatcorrRunRow  *object             ///< flatcorrRunRow object
+);
+
+/** Insert an array of flatcorrRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool flatcorrRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of flatcorrRunRow objects
+);
+
+/** Insert data from a binary FITS table flatcorrRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool flatcorrRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool flatcorrRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a flatcorrRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *flatcorrRunMetadataFromObject(
+    const flatcorrRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A flatcorrRunRow pointer or NULL on error
+ */
+
+flatcorrRunRow *flatcorrRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as flatcorrRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *flatcorrRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an flatcorrRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool flatcorrRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const flatcorrRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long flatcorrRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of flatcorrRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool flatcorrRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of flatcorrRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an flatcorrRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool flatcorrRunPrintObject(
+    FILE            *stream,            ///< a stream
+    flatcorrRunRow *object,    ///< an flatcorrRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** flatcorrExpRow data structure
+ *
+ * Structure for representing a single row of flatcorrExp table data.
+ */
+
+typedef struct {
+    psS64           corr_id;
+    psS64           chip_id;
+} flatcorrExpRow;
+
+/** Creates a new flatcorrExpRow object
+ *
+ *  @return A new flatcorrExpRow object or NULL on failure.
+ */
+
+flatcorrExpRow *flatcorrExpRowAlloc(
+    psS64           corr_id,
+    psS64           chip_id
+);
+
+/** Creates a new flatcorrExp table
+ *
+ * @return true on success
+ */
+
+bool flatcorrExpCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a flatcorrExp table
+ *
+ * @return true on success
+ */
+
+bool flatcorrExpDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool flatcorrExpInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           corr_id,
+    psS64           chip_id
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long flatcorrExpDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single flatcorrExpRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool flatcorrExpInsertObject(
+    psDB            *dbh,               ///< Database handle
+    flatcorrExpRow  *object             ///< flatcorrExpRow object
+);
+
+/** Insert an array of flatcorrExpRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool flatcorrExpInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of flatcorrExpRow objects
+);
+
+/** Insert data from a binary FITS table flatcorrExpRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool flatcorrExpInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool flatcorrExpSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a flatcorrExpRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *flatcorrExpMetadataFromObject(
+    const flatcorrExpRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A flatcorrExpRow pointer or NULL on error
+ */
+
+flatcorrExpRow *flatcorrExpObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as flatcorrExpRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *flatcorrExpSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an flatcorrExp
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool flatcorrExpDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const flatcorrExpRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long flatcorrExpDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of flatcorrExpRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool flatcorrExpPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of flatcorrExpRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an flatcorrExpRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool flatcorrExpPrintObject(
+    FILE            *stream,            ///< a stream
+    flatcorrExpRow *object,    ///< an flatcorrExpRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** pstampDataStoreRow data structure
+ *
+ * Structure for representing a single row of pstampDataStore table data.
+ */
+
+typedef struct {
+    psS64           ds_id;
+    char            *state;
+    char            *lastFileset;
+    char            *outProduct;
+    char            *uri;
+} pstampDataStoreRow;
+
+/** Creates a new pstampDataStoreRow object
+ *
+ *  @return A new pstampDataStoreRow object or NULL on failure.
+ */
+
+pstampDataStoreRow *pstampDataStoreRowAlloc(
+    psS64           ds_id,
+    const char      *state,
+    const char      *lastFileset,
+    const char      *outProduct,
+    const char      *uri
+);
+
+/** Creates a new pstampDataStore table
+ *
+ * @return true on success
+ */
+
+bool pstampDataStoreCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a pstampDataStore table
+ *
+ * @return true on success
+ */
+
+bool pstampDataStoreDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pstampDataStoreInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           ds_id,
+    const char      *state,
+    const char      *lastFileset,
+    const char      *outProduct,
+    const char      *uri
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pstampDataStoreDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single pstampDataStoreRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pstampDataStoreInsertObject(
+    psDB            *dbh,               ///< Database handle
+    pstampDataStoreRow *object             ///< pstampDataStoreRow object
+);
+
+/** Insert an array of pstampDataStoreRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pstampDataStoreInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of pstampDataStoreRow objects
+);
+
+/** Insert data from a binary FITS table pstampDataStoreRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool pstampDataStoreInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool pstampDataStoreSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a pstampDataStoreRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *pstampDataStoreMetadataFromObject(
+    const pstampDataStoreRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A pstampDataStoreRow pointer or NULL on error
+ */
+
+pstampDataStoreRow *pstampDataStoreObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as pstampDataStoreRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *pstampDataStoreSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an pstampDataStore
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool pstampDataStoreDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const pstampDataStoreRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pstampDataStoreDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of pstampDataStoreRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pstampDataStorePrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of pstampDataStoreRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an pstampDataStoreRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pstampDataStorePrintObject(
+    FILE            *stream,            ///< a stream
+    pstampDataStoreRow *object,    ///< an pstampDataStoreRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** pstampRequestRow data structure
+ *
+ * Structure for representing a single row of pstampRequest table data.
+ */
+
+typedef struct {
+    psS64           req_id;
+    psS64           ds_id;
+    char            *state;
+    char            *name;
+    char            *reqType;
+    char            *outProduct;
+    char            *uri;
+    psS32           fault;
+} pstampRequestRow;
+
+/** Creates a new pstampRequestRow object
+ *
+ *  @return A new pstampRequestRow object or NULL on failure.
+ */
+
+pstampRequestRow *pstampRequestRowAlloc(
+    psS64           req_id,
+    psS64           ds_id,
+    const char      *state,
+    const char      *name,
+    const char      *reqType,
+    const char      *outProduct,
+    const char      *uri,
+    psS32           fault
+);
+
+/** Creates a new pstampRequest table
+ *
+ * @return true on success
+ */
+
+bool pstampRequestCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a pstampRequest table
+ *
+ * @return true on success
+ */
+
+bool pstampRequestDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pstampRequestInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           req_id,
+    psS64           ds_id,
+    const char      *state,
+    const char      *name,
+    const char      *reqType,
+    const char      *outProduct,
+    const char      *uri,
+    psS32           fault
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pstampRequestDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single pstampRequestRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pstampRequestInsertObject(
+    psDB            *dbh,               ///< Database handle
+    pstampRequestRow *object             ///< pstampRequestRow object
+);
+
+/** Insert an array of pstampRequestRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pstampRequestInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of pstampRequestRow objects
+);
+
+/** Insert data from a binary FITS table pstampRequestRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool pstampRequestInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool pstampRequestSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a pstampRequestRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *pstampRequestMetadataFromObject(
+    const pstampRequestRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A pstampRequestRow pointer or NULL on error
+ */
+
+pstampRequestRow *pstampRequestObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as pstampRequestRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *pstampRequestSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an pstampRequest
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool pstampRequestDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const pstampRequestRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pstampRequestDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of pstampRequestRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pstampRequestPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of pstampRequestRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an pstampRequestRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pstampRequestPrintObject(
+    FILE            *stream,            ///< a stream
+    pstampRequestRow *object,    ///< an pstampRequestRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** pstampJobRow data structure
+ *
+ * Structure for representing a single row of pstampJob table data.
+ */
+
+typedef struct {
+    psS64           job_id;
+    psS64           req_id;
+    char            *rownum;
+    char            *state;
+    char            *jobType;
+    psS32           fault;
+    char            *uri;
+    char            *outputBase;
+    char            *args;
+} pstampJobRow;
+
+/** Creates a new pstampJobRow object
+ *
+ *  @return A new pstampJobRow object or NULL on failure.
+ */
+
+pstampJobRow *pstampJobRowAlloc(
+    psS64           job_id,
+    psS64           req_id,
+    const char      *rownum,
+    const char      *state,
+    const char      *jobType,
+    psS32           fault,
+    const char      *uri,
+    const char      *outputBase,
+    const char      *args
+);
+
+/** Creates a new pstampJob table
+ *
+ * @return true on success
+ */
+
+bool pstampJobCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a pstampJob table
+ *
+ * @return true on success
+ */
+
+bool pstampJobDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pstampJobInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           job_id,
+    psS64           req_id,
+    const char      *rownum,
+    const char      *state,
+    const char      *jobType,
+    psS32           fault,
+    const char      *uri,
+    const char      *outputBase,
+    const char      *args
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pstampJobDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single pstampJobRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pstampJobInsertObject(
+    psDB            *dbh,               ///< Database handle
+    pstampJobRow    *object             ///< pstampJobRow object
+);
+
+/** Insert an array of pstampJobRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool pstampJobInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of pstampJobRow objects
+);
+
+/** Insert data from a binary FITS table pstampJobRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool pstampJobInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool pstampJobSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a pstampJobRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *pstampJobMetadataFromObject(
+    const pstampJobRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A pstampJobRow pointer or NULL on error
+ */
+
+pstampJobRow *pstampJobObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as pstampJobRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *pstampJobSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an pstampJob
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool pstampJobDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const pstampJobRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long pstampJobDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of pstampJobRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pstampJobPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of pstampJobRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an pstampJobRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool pstampJobPrintObject(
+    FILE            *stream,            ///< a stream
+    pstampJobRow *object,    ///< an pstampJobRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+
+/// @}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PSTAMPJOB_DB_H
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/.cvsignore	(revision 22322)
@@ -0,0 +1,25 @@
+.deps
+Makefile
+Makefile.in
+atconfig
+.libs
+alloc
+cleanup
+createtable
+dbcleanup
+dbsetup
+droptable
+init
+insert
+insertfits
+insertobject
+metadatafromobject
+objectfrommetadata
+package.m4
+pop
+popfits
+popobject
+selectrowsfits
+testsuite
+testsuite.dir
+testsuite.log
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/Makefile.am	(revision 22322)
@@ -0,0 +1,64 @@
+# copied from bison-1.875d
+
+EXTRA_DIST = $(TESTSUITE_AT) testsuite package.m4
+
+DISTCLEANFILES       = atconfig $(check_SCRIPTS)
+MAINTAINERCLEANFILES = Makefile.in $(TESTSUITE)
+
+## ------------ ##
+## package.m4.  ##
+## ------------ ##
+
+$(srcdir)/package.m4: $(top_srcdir)/configure.ac
+	{					\
+	  echo '# Signature of the current package.'; \
+	  echo 'm4_define([AT_PACKAGE_NAME],      [@PACKAGE_NAME@])'; \
+	  echo 'm4_define([AT_PACKAGE_TARNAME],   [@PACKAGE_TARNAME@])'; \
+	  echo 'm4_define([AT_PACKAGE_VERSION],   [@PACKAGE_VERSION@])'; \
+	  echo 'm4_define([AT_PACKAGE_STRING],    [@PACKAGE_STRING@])'; \
+	  echo 'm4_define([AT_PACKAGE_BUGREPORT], [@PACKAGE_BUGREPORT@])'; \
+	} >$(srcdir)/package.m4
+
+## ------------ ##
+## Test suite.  ##
+## ------------ ##
+
+TESTSUITE = $(srcdir)/testsuite
+
+AUTOTEST = $(AUTOM4TE) --language=autotest
+$(TESTSUITE): package.m4 $(TESTSUITE_AT)
+	$(AUTOTEST) -I $(srcdir) testsuite.at -o $@.tmp
+	mv $@.tmp $@
+
+atconfig: $(top_builddir)/config.status
+	cd $(top_builddir) && ./config.status tests/$@
+
+clean-local:
+	test ! -f $(TESTSUITE) || $(SHELL) $(TESTSUITE) --clean
+
+check-local: atconfig $(TESTSUITE)
+	$(SHELL) $(TESTSUITE)
+
+#
+# END BISON
+#
+
+UNITS = \
+	alloc \
+	init \
+	cleanup \
+	createtable \
+    droptable \
+    insert \
+    insertobject \
+    insertfits \
+    selectrowsfits \
+    metadatafromobject \
+    objectfrommetadata
+
+AM_CPPFLAGS = -I$(top_srcdir)/src$
+AM_CFLAGS   = @ippdb_CFLAGS@ $(PSLIB_CFLAGS)$
+AM_LDFLAGS  = $(PSLIB_LIBS)$
+LDADD       = $(top_builddir)/src/libippdb.la$
+
+check_PROGRAMS = dbsetup dbcleanup $(UNITS)
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/alloc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/alloc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/alloc.c	(revision 22322)
@@ -0,0 +1,3139 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_STRING_LENGTH 1024
+
+int main ()
+{
+    {
+        pzDataStoreRow  *object;
+
+        object = pzDataStoreRowAlloc("a string", "a string", "a string", "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        summitExpRow    *object;
+
+        object = summitExpRowAlloc("a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", -32, -16, "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->imfiles == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        summitImfileRow *object;
+
+        object = summitImfileRowAlloc("a string", "a string", "a string", "a string", -32, "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->file_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bytes == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->md5sum, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        pzDownloadExpRow *object;
+
+        object = pzDownloadExpRowAlloc("a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        pzDownloadImfileRow *object;
+
+        object = pzDownloadImfileRowAlloc("a string", "a string", "a string", "a string", "a string", "a string", -16, "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        newExpRow       *object;
+
+        object = newExpRowAlloc(-64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tmp_exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tmp_camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tmp_telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir_state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        newImfileRow    *object;
+
+        object = newImfileRowAlloc(-64, "a string", "a string", "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tmp_class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        rawExpRow       *object;
+
+        object = rawExpRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 64.64, 64.64, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, 32.32, 64.64, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", 32.32, "a string", -16, "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_tag, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filelevel, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filter, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->comment, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->airmass == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ra == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->decl == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_time == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sat_pixel_frac == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->alt == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->az == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ccd_temp == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->posang == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_x == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_y == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_z == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_tip == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_tilt == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_x == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_y == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_z == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_tip == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_tilt == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_temperature == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_humidity == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_wind_speed == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_wind_dir == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m1 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m1cell == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m2 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_spider == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_truss == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_extra == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->pon_time == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->object, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->solang == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        rawImfileRow    *object;
+
+        object = rawImfileRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 64.64, 64.64, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, 32.32, 64.64, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", "a string", -16, "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tmp_class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filelevel, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filter, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->comment, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->airmass == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ra == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->decl == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_time == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sat_pixel_frac == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->alt == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->az == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ccd_temp == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->posang == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_x == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_y == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_z == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_tip == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_tilt == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_x == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_y == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_z == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_tip == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_tilt == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_temperature == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_humidity == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_wind_speed == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_wind_dir == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m1 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m1cell == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m2 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_spider == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_truss == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_extra == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->pon_time == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->object, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        guidePendingExpRow *object;
+
+        object = guidePendingExpRowAlloc(-64, -64, "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->guide_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        chipRunRow      *object;
+
+        object = chipRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->chip_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir_state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->expgroup, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        chipProcessedImfileRow *object;
+
+        object = chipProcessedImfileRowAlloc(-64, -64, "a string", "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, "a string", -32, -32, -32, -32, "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->chip_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bias == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bias_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sigma_ra == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sigma_dec == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ap_resid == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ap_resid_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->zp_mean == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->zp_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fwhm_major == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fwhm_minor == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_detrend == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_photom == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_astrom == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_stars == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_extended == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_cr == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_astrom == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        chipMaskRow     *object;
+
+        object = chipMaskRowAlloc("a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        camRunRow       *object;
+
+        object = camRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->cam_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->chip_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir_state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->expgroup, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        camProcessedExpRow *object;
+
+        object = camProcessedExpRowAlloc(-64, "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, "a string", -32, -32, -32, -32, "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->cam_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bias == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bias_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sigma_ra == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sigma_dec == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ap_resid == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ap_resid_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->zp_mean == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->zp_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fwhm_major == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fwhm_minor == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_detrend == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_photom == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_astrom == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_stars == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_extended == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_cr == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_astrom == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        camMaskRow      *object;
+
+        object = camMaskRowAlloc("a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        fakeRunRow      *object;
+
+        object = fakeRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->fake_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->cam_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->expgroup, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        fakeProcessedImfileRow *object;
+
+        object = fakeProcessedImfileRowAlloc(-64, -64, "a string", "a string", 32.32, "a string", "a string", -16, "0001-01-01T00:00:00Z"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->fake_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_fake == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        fakeMaskRow     *object;
+
+        object = fakeMaskRowAlloc("a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        warpRunRow      *object;
+
+        object = warpRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z", true    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->warp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fake_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->mode, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir_state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->magiced == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        warpSkyCellMapRow *object;
+
+        object = warpSkyCellMapRowAlloc(-64, "a string", "a string", "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->warp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->skycell_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        warpSkyfileRow  *object;
+
+        object = warpSkyfileRowAlloc(-64, "a string", "a string", "a string", "a string", 64.64, 64.64, 32.32, "a string", 32.32, -32, -32, -32, -32, true, -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->warp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->skycell_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_warp == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->good_frac == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->xmin == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->xmax == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ymin == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ymax == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ignored == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        warpMaskRow     *object;
+
+        object = warpMaskRowAlloc("a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        diffRunRow      *object;
+
+        object = diffRunRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->diff_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->skycell_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        diffInputSkyfileRow *object;
+
+        object = diffInputSkyfileRowAlloc(-64, true, -64, -64, "a string", "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->diff_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->template == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->stack_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->warp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->skycell_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->kind, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        diffSkyfileRow  *object;
+
+        object = diffSkyfileRowAlloc(-64, "a string", "a string", 64.64, 64.64, -32, 32.32, -32, 32.32, "a string", 32.32, -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->diff_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->stamps_num == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->stamps_rms == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sources == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_diff == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->good_frac == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        stackRunRow     *object;
+
+        object = stackRunRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->stack_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->skycell_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filter, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        stackInputSkyfileRow *object;
+
+        object = stackInputSkyfileRowAlloc(-64, -64    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->stack_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->warp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        stackSumSkyfileRow *object;
+
+        object = stackSumSkyfileRowAlloc(-64, "a string", "a string", 64.64, 64.64, 32.32, "a string", 32.32, -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->stack_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_stack == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->good_frac == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detRunRow       *object;
+
+        object = detRunRowAlloc(-64, -32, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", 32.32, 32.32, "a string", -32    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->det_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->mode, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filelevel, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filter, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->airmass_min == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->airmass_max == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_time_min == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_time_max == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ccd_temp_min == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ccd_temp_max == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->posang_min == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->posang_max == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->solang_min == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->solang_max == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->parent == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detInputExpRow  *object;
+
+        object = detInputExpRowAlloc(-64, -32, -64, true    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->include == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detProcessedImfileRow *object;
+
+        object = detProcessedImfileRowAlloc(-64, -64, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detProcessedExpRow *object;
+
+        object = detProcessedExpRowAlloc(-64, -64, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detStackedImfileRow *object;
+
+        object = detStackedImfileRowAlloc(-64, -32, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detNormalizedStatImfileRow *object;
+
+        object = detNormalizedStatImfileRowAlloc(-64, -32, "a string", 32.32, -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->norm == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detNormalizedImfileRow *object;
+
+        object = detNormalizedImfileRowAlloc(-64, -32, "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detNormalizedExpRow *object;
+
+        object = detNormalizedExpRowAlloc(-64, -32, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detResidImfileRow *object;
+
+        object = detResidImfileRowAlloc(-64, -32, -64, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_skewness == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_kurtosis == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bin_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detResidExpRow  *object;
+
+        object = detResidExpRowAlloc(-64, -32, -64, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", true, -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_skewness == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_kurtosis == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bin_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->accept == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detRunSummaryRow *object;
+
+        object = detRunSummaryRowAlloc(-64, -32, 64.64, 64.64, 64.64, true, -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->accept == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detRegisteredImfileRow *object;
+
+        object = detRegisteredImfileRowAlloc(-64, -32, "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detCorrectedExpRow *object;
+
+        object = detCorrectedExpRowAlloc(-64, -64, "a string", -64, "a string", "a string", "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->corr_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->corr_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        detCorrectedImfileRow *object;
+
+        object = detCorrectedImfileRowAlloc(-64, -64, "a string", "a string", "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->det_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        magicRunRow     *object;
+
+        object = magicRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->magic_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir_state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        magicInputSkyfileRow *object;
+
+        object = magicInputSkyfileRowAlloc(-64, -64, "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->magic_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->diff_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->node, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        magicTreeRow    *object;
+
+        object = magicTreeRowAlloc(-64, "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->magic_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->node, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dep, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        magicNodeResultRow *object;
+
+        object = magicNodeResultRowAlloc(-64, "a string", "a string", -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->magic_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->node, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        magicMaskRow    *object;
+
+        object = magicMaskRowAlloc(-64, "a string", -32, -16    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->magic_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->streaks == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -16) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        calDBRow        *object;
+
+        object = calDBRowAlloc(-64, "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->cal_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        calRunRow       *object;
+
+        object = calRunRowAlloc(-64, "a string", "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->cal_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->region, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->last_step, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        flatcorrRunRow  *object;
+
+        object = flatcorrRunRowAlloc(-64, "a string", "a string", "a string", "a string", "a string", "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->corr_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filter, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->stats, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->region, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        flatcorrExpRow  *object;
+
+        object = flatcorrExpRowAlloc(-64, -64    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->corr_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->chip_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        pstampDataStoreRow *object;
+
+        object = pstampDataStoreRowAlloc(-64, "a string", "a string", "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->ds_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->lastFileset, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->outProduct, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        pstampRequestRow *object;
+
+        object = pstampRequestRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", -32    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->req_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ds_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reqType, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->outProduct, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        pstampJobRow    *object;
+
+        object = pstampJobRowAlloc(-64, -64, "a string", "a string", "a string", -32, "a string", "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->job_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->req_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->rownum, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->jobType, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->outputBase, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->args, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/cleanup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/cleanup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/cleanup.c	(revision 22322)
@@ -0,0 +1,17 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+
+int main ()
+{
+    psDB            *dbh;
+
+    dbh = psDBInit("localhost", "test", NULL, "test");
+    if (!dbh) {
+        exit(EXIT_FAILURE);
+    }
+
+    ippdbCleanup(dbh);
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/createtable.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/createtable.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/createtable.c	(revision 22322)
@@ -0,0 +1,833 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+
+int main ()
+{
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!pzDataStoreCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!summitExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!summitImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!pzDownloadExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!pzDownloadImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!newExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!newImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!rawExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!rawImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!guidePendingExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!chipRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!chipProcessedImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!chipMaskCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!camRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!camProcessedExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!camMaskCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!fakeRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!fakeProcessedImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!fakeMaskCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!warpRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!warpSkyCellMapCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!warpSkyfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!warpMaskCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!diffRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!diffInputSkyfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!diffSkyfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!stackRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!stackInputSkyfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!stackSumSkyfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detInputExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detProcessedImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detProcessedExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detStackedImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detNormalizedStatImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detNormalizedImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detNormalizedExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detResidImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detResidExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detRunSummaryCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detRegisteredImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detCorrectedExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!detCorrectedImfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!magicRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!magicInputSkyfileCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!magicTreeCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!magicNodeResultCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!magicMaskCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!calDBCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!calRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!flatcorrRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!flatcorrExpCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!pstampDataStoreCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!pstampRequestCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!pstampJobCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/dbcleanup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/dbcleanup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/dbcleanup.c	(revision 22322)
@@ -0,0 +1,72 @@
+#include <pslib.h>
+#include <stdlib.h>
+
+int main ()
+{
+    psDB            *dbh;
+
+    dbh = psDBInit("localhost", "test", NULL, "test", 0);
+    if (!dbh) {
+        exit(EXIT_FAILURE);
+    }
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pzDataStore");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS summitExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS summitImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pzDownloadExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pzDownloadImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS newExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS newImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS rawExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS rawImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS guidePendingExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS chipRun");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS chipProcessedImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS chipMask");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS camRun");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS camProcessedExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS camMask");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS fakeRun");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS fakeProcessedImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS fakeMask");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS warpRun");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS warpSkyCellMap");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS warpSkyfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS warpMask");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS diffRun");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS diffInputSkyfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS diffSkyfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS stackRun");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS stackInputSkyfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS stackSumSkyfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detRun");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detInputExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detProcessedImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detProcessedExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detStackedImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detNormalizedStatImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detNormalizedImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detNormalizedExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detResidImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detResidExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detRunSummary");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detRegisteredImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detCorrectedExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detCorrectedImfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicRun");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicInputSkyfile");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicTree");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicNodeResult");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicMask");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS calDB");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS calRun");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS flatcorrRun");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS flatcorrExp");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pstampDataStore");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pstampRequest");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pstampJob");
+
+    psDBCleanup(dbh);
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/dbsetup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/dbsetup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/dbsetup.c	(revision 22322)
@@ -0,0 +1,183 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+
+int main ()
+{
+    psDB            *dbh;
+
+    dbh = psDBInit("localhost", "test", NULL, "test", 0);
+    if (!dbh) {
+        exit(EXIT_FAILURE);
+    }
+
+    // remove the table if it already exists
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pzDataStore");
+    pzDataStoreCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS summitExp");
+    summitExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS summitImfile");
+    summitImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pzDownloadExp");
+    pzDownloadExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pzDownloadImfile");
+    pzDownloadImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS newExp");
+    newExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS newImfile");
+    newImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS rawExp");
+    rawExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS rawImfile");
+    rawImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS guidePendingExp");
+    guidePendingExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS chipRun");
+    chipRunCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS chipProcessedImfile");
+    chipProcessedImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS chipMask");
+    chipMaskCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS camRun");
+    camRunCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS camProcessedExp");
+    camProcessedExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS camMask");
+    camMaskCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS fakeRun");
+    fakeRunCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS fakeProcessedImfile");
+    fakeProcessedImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS fakeMask");
+    fakeMaskCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS warpRun");
+    warpRunCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS warpSkyCellMap");
+    warpSkyCellMapCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS warpSkyfile");
+    warpSkyfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS warpMask");
+    warpMaskCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS diffRun");
+    diffRunCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS diffInputSkyfile");
+    diffInputSkyfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS diffSkyfile");
+    diffSkyfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS stackRun");
+    stackRunCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS stackInputSkyfile");
+    stackInputSkyfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS stackSumSkyfile");
+    stackSumSkyfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detRun");
+    detRunCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detInputExp");
+    detInputExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detProcessedImfile");
+    detProcessedImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detProcessedExp");
+    detProcessedExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detStackedImfile");
+    detStackedImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detNormalizedStatImfile");
+    detNormalizedStatImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detNormalizedImfile");
+    detNormalizedImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detNormalizedExp");
+    detNormalizedExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detResidImfile");
+    detResidImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detResidExp");
+    detResidExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detRunSummary");
+    detRunSummaryCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detRegisteredImfile");
+    detRegisteredImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detCorrectedExp");
+    detCorrectedExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS detCorrectedImfile");
+    detCorrectedImfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicRun");
+    magicRunCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicInputSkyfile");
+    magicInputSkyfileCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicTree");
+    magicTreeCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicNodeResult");
+    magicNodeResultCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicMask");
+    magicMaskCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS calDB");
+    calDBCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS calRun");
+    calRunCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS flatcorrRun");
+    flatcorrRunCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS flatcorrExp");
+    flatcorrExpCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pstampDataStore");
+    pstampDataStoreCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pstampRequest");
+    pstampRequestCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS pstampJob");
+    pstampJobCreateTable(dbh);
+
+    psDBCleanup(dbh);
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/droptable.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/droptable.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/droptable.c	(revision 22322)
@@ -0,0 +1,833 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+
+int main ()
+{
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDataStoreDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!summitExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!summitImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDownloadExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDownloadImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!newExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!newImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!rawExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!rawImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!guidePendingExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipProcessedImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipMaskDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camProcessedExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camMaskDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeProcessedImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeMaskDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpSkyCellMapDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpSkyfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpMaskDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffInputSkyfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffSkyfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackInputSkyfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackSumSkyfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detInputExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detProcessedImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detProcessedExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detStackedImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedStatImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detResidImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detResidExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRunSummaryDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRegisteredImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detCorrectedExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detCorrectedImfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicInputSkyfileDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicTreeDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicNodeResultDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicMaskDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calDBDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!flatcorrRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!flatcorrExpDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampDataStoreDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampRequestDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampJobDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/init.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/init.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/init.c	(revision 22322)
@@ -0,0 +1,17 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+
+int main ()
+{
+    psDB            *dbh;
+
+    dbh = ippdbInit("localhost", "test", NULL, "test");
+    if (!dbh) {
+        exit(EXIT_FAILURE);
+    }
+
+    psDBCleanup(dbh);
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/insert.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/insert.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/insert.c	(revision 22322)
@@ -0,0 +1,833 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+
+int main ()
+{
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDataStoreInsert(dbh, "a string", "a string", "a string", "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!summitExpInsert(dbh, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", -32, -16, "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!summitImfileInsert(dbh, "a string", "a string", "a string", "a string", -32, "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDownloadExpInsert(dbh, "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDownloadImfileInsert(dbh, "a string", "a string", "a string", "a string", "a string", "a string", -16, "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!newExpInsert(dbh, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!newImfileInsert(dbh, -64, "a string", "a string", "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!rawExpInsert(dbh, -64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 64.64, 64.64, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, 32.32, 64.64, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", 32.32, "a string", -16, "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!rawImfileInsert(dbh, -64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 64.64, 64.64, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, 32.32, 64.64, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", "a string", -16, "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!guidePendingExpInsert(dbh, -64, -64, "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipRunInsert(dbh, -64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipProcessedImfileInsert(dbh, -64, -64, "a string", "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, "a string", -32, -32, -32, -32, "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipMaskInsert(dbh, "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camRunInsert(dbh, -64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camProcessedExpInsert(dbh, -64, "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, "a string", -32, -32, -32, -32, "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camMaskInsert(dbh, "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeRunInsert(dbh, -64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeProcessedImfileInsert(dbh, -64, -64, "a string", "a string", 32.32, "a string", "a string", -16, "0001-01-01T00:00:00Z")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeMaskInsert(dbh, "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpRunInsert(dbh, -64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z", true)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpSkyCellMapInsert(dbh, -64, "a string", "a string", "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpSkyfileInsert(dbh, -64, "a string", "a string", "a string", "a string", 64.64, 64.64, 32.32, "a string", 32.32, -32, -32, -32, -32, true, -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpMaskInsert(dbh, "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffRunInsert(dbh, -64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffInputSkyfileInsert(dbh, -64, true, -64, -64, "a string", "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffSkyfileInsert(dbh, -64, "a string", "a string", 64.64, 64.64, -32, 32.32, -32, 32.32, "a string", 32.32, -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackRunInsert(dbh, -64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackInputSkyfileInsert(dbh, -64, -64)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackSumSkyfileInsert(dbh, -64, "a string", "a string", 64.64, 64.64, 32.32, "a string", 32.32, -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRunInsert(dbh, -64, -32, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", 32.32, 32.32, "a string", -32)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detInputExpInsert(dbh, -64, -32, -64, true)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detProcessedImfileInsert(dbh, -64, -64, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detProcessedExpInsert(dbh, -64, -64, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detStackedImfileInsert(dbh, -64, -32, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedStatImfileInsert(dbh, -64, -32, "a string", 32.32, -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedImfileInsert(dbh, -64, -32, "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedExpInsert(dbh, -64, -32, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detResidImfileInsert(dbh, -64, -32, -64, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detResidExpInsert(dbh, -64, -32, -64, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", true, -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRunSummaryInsert(dbh, -64, -32, 64.64, 64.64, 64.64, true, -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRegisteredImfileInsert(dbh, -64, -32, "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detCorrectedExpInsert(dbh, -64, -64, "a string", -64, "a string", "a string", "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detCorrectedImfileInsert(dbh, -64, -64, "a string", "a string", "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicRunInsert(dbh, -64, -64, "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicInputSkyfileInsert(dbh, -64, -64, "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicTreeInsert(dbh, -64, "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicNodeResultInsert(dbh, -64, "a string", "a string", -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicMaskInsert(dbh, -64, "a string", -32, -16)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calDBInsert(dbh, -64, "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calRunInsert(dbh, -64, "a string", "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!flatcorrRunInsert(dbh, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!flatcorrExpInsert(dbh, -64, -64)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampDataStoreInsert(dbh, -64, "a string", "a string", "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampRequestInsert(dbh, -64, -64, "a string", "a string", "a string", "a string", "a string", -32)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampJobInsert(dbh, -64, -64, "a string", "a string", "a string", -32, "a string", "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/insertfits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/insertfits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/insertfits.c	(revision 22322)
@@ -0,0 +1,1442 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define TMP_FILENAME "./blargh"
+
+int main ()
+{
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDataStoreInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!summitExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!summitImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDownloadExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDownloadImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!newExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!newImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!rawExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!rawImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!guidePendingExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipProcessedImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipMaskInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camProcessedExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camMaskInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeProcessedImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeMaskInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpSkyCellMapInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpSkyfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpMaskInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffInputSkyfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffSkyfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackInputSkyfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackSumSkyfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detInputExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detProcessedImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detProcessedExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detStackedImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedStatImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detResidImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detResidExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRunSummaryInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRegisteredImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detCorrectedExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detCorrectedImfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicInputSkyfileInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicTreeInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicNodeResultInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicMaskInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calDBInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!flatcorrRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!flatcorrExpInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampDataStoreInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampRequestInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampJobInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/insertobject.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/insertobject.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/insertobject.c	(revision 22322)
@@ -0,0 +1,1218 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+
+int main ()
+{
+    {
+        psDB            *dbh;
+        pzDataStoreRow  *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = pzDataStoreRowAlloc("a string", "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDataStoreInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        summitExpRow    *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = summitExpRowAlloc("a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", -32, -16, "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!summitExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        summitImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = summitImfileRowAlloc("a string", "a string", "a string", "a string", -32, "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!summitImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        pzDownloadExpRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = pzDownloadExpRowAlloc("a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDownloadExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        pzDownloadImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = pzDownloadImfileRowAlloc("a string", "a string", "a string", "a string", "a string", "a string", -16, "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDownloadImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        newExpRow       *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = newExpRowAlloc(-64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!newExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        newImfileRow    *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = newImfileRowAlloc(-64, "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!newImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        rawExpRow       *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = rawExpRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 64.64, 64.64, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, 32.32, 64.64, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", 32.32, "a string", -16, "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!rawExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        rawImfileRow    *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = rawImfileRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 64.64, 64.64, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, 32.32, 64.64, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", "a string", -16, "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!rawImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        guidePendingExpRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = guidePendingExpRowAlloc(-64, -64, "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!guidePendingExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        chipRunRow      *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = chipRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        chipProcessedImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = chipProcessedImfileRowAlloc(-64, -64, "a string", "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, "a string", -32, -32, -32, -32, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipProcessedImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        chipMaskRow     *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = chipMaskRowAlloc("a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipMaskInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        camRunRow       *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = camRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        camProcessedExpRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = camProcessedExpRowAlloc(-64, "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, "a string", -32, -32, -32, -32, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camProcessedExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        camMaskRow      *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = camMaskRowAlloc("a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camMaskInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        fakeRunRow      *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = fakeRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        fakeProcessedImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = fakeProcessedImfileRowAlloc(-64, -64, "a string", "a string", 32.32, "a string", "a string", -16, "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeProcessedImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        fakeMaskRow     *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = fakeMaskRowAlloc("a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeMaskInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        warpRunRow      *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = warpRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z", true);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        warpSkyCellMapRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = warpSkyCellMapRowAlloc(-64, "a string", "a string", "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpSkyCellMapInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        warpSkyfileRow  *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = warpSkyfileRowAlloc(-64, "a string", "a string", "a string", "a string", 64.64, 64.64, 32.32, "a string", 32.32, -32, -32, -32, -32, true, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpSkyfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        warpMaskRow     *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = warpMaskRowAlloc("a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpMaskInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        diffRunRow      *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = diffRunRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        diffInputSkyfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = diffInputSkyfileRowAlloc(-64, true, -64, -64, "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffInputSkyfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        diffSkyfileRow  *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = diffSkyfileRowAlloc(-64, "a string", "a string", 64.64, 64.64, -32, 32.32, -32, 32.32, "a string", 32.32, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffSkyfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        stackRunRow     *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = stackRunRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        stackInputSkyfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = stackInputSkyfileRowAlloc(-64, -64);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackInputSkyfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        stackSumSkyfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = stackSumSkyfileRowAlloc(-64, "a string", "a string", 64.64, 64.64, 32.32, "a string", 32.32, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackSumSkyfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detRunRow       *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detRunRowAlloc(-64, -32, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", 32.32, 32.32, "a string", -32);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detInputExpRow  *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detInputExpRowAlloc(-64, -32, -64, true);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detInputExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detProcessedImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detProcessedImfileRowAlloc(-64, -64, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detProcessedImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detProcessedExpRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detProcessedExpRowAlloc(-64, -64, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detProcessedExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detStackedImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detStackedImfileRowAlloc(-64, -32, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detStackedImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detNormalizedStatImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detNormalizedStatImfileRowAlloc(-64, -32, "a string", 32.32, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedStatImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detNormalizedImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detNormalizedImfileRowAlloc(-64, -32, "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detNormalizedExpRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detNormalizedExpRowAlloc(-64, -32, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detResidImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detResidImfileRowAlloc(-64, -32, -64, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detResidImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detResidExpRow  *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detResidExpRowAlloc(-64, -32, -64, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", true, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detResidExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detRunSummaryRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detRunSummaryRowAlloc(-64, -32, 64.64, 64.64, 64.64, true, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRunSummaryInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detRegisteredImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detRegisteredImfileRowAlloc(-64, -32, "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRegisteredImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detCorrectedExpRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detCorrectedExpRowAlloc(-64, -64, "a string", -64, "a string", "a string", "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detCorrectedExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        detCorrectedImfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = detCorrectedImfileRowAlloc(-64, -64, "a string", "a string", "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detCorrectedImfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        magicRunRow     *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = magicRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        magicInputSkyfileRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = magicInputSkyfileRowAlloc(-64, -64, "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicInputSkyfileInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        magicTreeRow    *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = magicTreeRowAlloc(-64, "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicTreeInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        magicNodeResultRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = magicNodeResultRowAlloc(-64, "a string", "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicNodeResultInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        magicMaskRow    *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = magicMaskRowAlloc(-64, "a string", -32, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicMaskInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        calDBRow        *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = calDBRowAlloc(-64, "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calDBInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        calRunRow       *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = calRunRowAlloc(-64, "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        flatcorrRunRow  *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = flatcorrRunRowAlloc(-64, "a string", "a string", "a string", "a string", "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!flatcorrRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        flatcorrExpRow  *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = flatcorrExpRowAlloc(-64, -64);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!flatcorrExpInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        pstampDataStoreRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = pstampDataStoreRowAlloc(-64, "a string", "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampDataStoreInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        pstampRequestRow *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = pstampRequestRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", -32);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampRequestInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        pstampJobRow    *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = pstampJobRowAlloc(-64, -64, "a string", "a string", "a string", -32, "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampJobInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/metadatafromobject.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/metadatafromobject.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/metadatafromobject.c	(revision 22322)
@@ -0,0 +1,3484 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_STRING_LENGTH 1024
+
+int main ()
+{
+    {
+        psMetadata      *md;
+        pzDataStoreRow  *object;
+        bool            status;
+
+        object = pzDataStoreRowAlloc("a string", "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = pzDataStoreMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+        if (strncmp(psMetadataLookupPtr(&status, md, "camera"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "telescope"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        summitExpRow    *object;
+        bool            status;
+
+        object = summitExpRowAlloc("a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", -32, -16, "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = summitExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_name"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "camera"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "telescope"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_type"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "imfiles") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        summitImfileRow *object;
+        bool            status;
+
+        object = summitImfileRowAlloc("a string", "a string", "a string", "a string", -32, "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = summitImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_name"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "camera"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "telescope"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "file_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "bytes") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "md5sum"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        pzDownloadExpRow *object;
+        bool            status;
+
+        object = pzDownloadExpRowAlloc("a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = pzDownloadExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_name"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "camera"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "telescope"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        pzDownloadImfileRow *object;
+        bool            status;
+
+        object = pzDownloadImfileRowAlloc("a string", "a string", "a string", "a string", "a string", "a string", -16, "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = pzDownloadImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_name"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "camera"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "telescope"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        newExpRow       *object;
+        bool            status;
+
+        object = newExpRowAlloc(-64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = newExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tmp_exp_name"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tmp_camera"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tmp_telescope"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir_state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "reduction"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "end_stage"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        newImfileRow    *object;
+        bool            status;
+
+        object = newImfileRowAlloc(-64, "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = newImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tmp_class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        rawExpRow       *object;
+        bool            status;
+
+        object = rawExpRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 64.64, 64.64, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, 32.32, 64.64, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", 32.32, "a string", -16, "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = rawExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_name"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "camera"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "telescope"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_tag"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_type"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "filelevel"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "reduction"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "end_stage"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "filter"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "comment"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "airmass") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "ra") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "decl") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "exp_time") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "sat_pixel_frac") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "alt") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "az") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "ccd_temp") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "posang") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m1_x") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m1_y") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m1_z") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m1_tip") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m1_tilt") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m2_x") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m2_y") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m2_z") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m2_tip") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m2_tilt") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "env_temperature") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "env_humidity") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "env_wind_speed") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "env_wind_dir") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_m1") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_m1cell") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_m2") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_spider") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_truss") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_extra") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "pon_time") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_3") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_4") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_5") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "object"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "solang") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "hostname"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        rawImfileRow    *object;
+        bool            status;
+
+        object = rawImfileRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 64.64, 64.64, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, 32.32, 64.64, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", "a string", -16, "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = rawImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_name"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "camera"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "telescope"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tmp_class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_type"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "filelevel"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "filter"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "comment"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "airmass") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "ra") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "decl") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "exp_time") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "sat_pixel_frac") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "alt") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "az") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "ccd_temp") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "posang") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m1_x") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m1_y") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m1_z") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m1_tip") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m1_tilt") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m2_x") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m2_y") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m2_z") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m2_tip") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "m2_tilt") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "env_temperature") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "env_humidity") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "env_wind_speed") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "env_wind_dir") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_m1") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_m1cell") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_m2") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_spider") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_truss") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "teltemp_extra") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "pon_time") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_3") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_4") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_5") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "object"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "hostname"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        guidePendingExpRow *object;
+        bool            status;
+
+        object = guidePendingExpRowAlloc(-64, -64, "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = guidePendingExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "recipe"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        chipRunRow      *object;
+        bool            status;
+
+        object = chipRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = chipRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir_state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "reduction"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "expgroup"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "end_stage"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        chipProcessedImfileRow *object;
+        bool            status;
+
+        object = chipProcessedImfileRowAlloc(-64, -64, "a string", "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, "a string", -32, -32, -32, -32, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = chipProcessedImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "bg") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "bg_stdev") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "bg_mean_stdev") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "bias") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "bias_stdev") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "fringe_0") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "fringe_1") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "fringe_2") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "sigma_ra") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "sigma_dec") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "ap_resid") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "ap_resid_stdev") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "zp_mean") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "zp_stdev") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "fwhm_major") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "fwhm_minor") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "dtime_detrend") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "dtime_photom") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "dtime_astrom") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "hostname"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "n_stars") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "n_extended") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "n_cr") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "n_astrom") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        chipMaskRow     *object;
+        bool            status;
+
+        object = chipMaskRowAlloc("a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = chipMaskMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        camRunRow       *object;
+        bool            status;
+
+        object = camRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = camRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir_state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "reduction"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "expgroup"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "end_stage"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        camProcessedExpRow *object;
+        bool            status;
+
+        object = camProcessedExpRowAlloc(-64, "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, "a string", -32, -32, -32, -32, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = camProcessedExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "bg") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "bg_stdev") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "bg_mean_stdev") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "bias") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "bias_stdev") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "fringe_0") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "fringe_1") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "fringe_2") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "sigma_ra") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "sigma_dec") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "ap_resid") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "ap_resid_stdev") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "zp_mean") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "zp_stdev") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "fwhm_major") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "fwhm_minor") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "dtime_detrend") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "dtime_photom") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "dtime_astrom") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "hostname"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "n_stars") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "n_extended") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "n_cr") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "n_astrom") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        camMaskRow      *object;
+        bool            status;
+
+        object = camMaskRowAlloc("a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = camMaskMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        fakeRunRow      *object;
+        bool            status;
+
+        object = fakeRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = fakeRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "reduction"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "expgroup"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "end_stage"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        fakeProcessedImfileRow *object;
+        bool            status;
+
+        object = fakeProcessedImfileRowAlloc(-64, -64, "a string", "a string", 32.32, "a string", "a string", -16, "0001-01-01T00:00:00Z");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = fakeProcessedImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "dtime_fake") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "hostname"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        fakeMaskRow     *object;
+        bool            status;
+
+        object = fakeMaskRowAlloc("a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = fakeMaskMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        warpRunRow      *object;
+        bool            status;
+
+        object = warpRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z", true);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = warpRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "mode"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir_state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "end_stage"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupBool(&status, md, "magiced") == true) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        warpSkyCellMapRow *object;
+        bool            status;
+
+        object = warpSkyCellMapRowAlloc(-64, "a string", "a string", "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = warpSkyCellMapMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "skycell_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        warpSkyfileRow  *object;
+        bool            status;
+
+        object = warpSkyfileRowAlloc(-64, "a string", "a string", "a string", "a string", 64.64, 64.64, 32.32, "a string", 32.32, -32, -32, -32, -32, true, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = warpSkyfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "skycell_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "dtime_warp") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "hostname"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "good_frac") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "xmin") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "xmax") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "ymin") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "ymax") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupBool(&status, md, "ignored") == true) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        warpMaskRow     *object;
+        bool            status;
+
+        object = warpMaskRowAlloc("a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = warpMaskMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        diffRunRow      *object;
+        bool            status;
+
+        object = diffRunRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = diffRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "skycell_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        diffInputSkyfileRow *object;
+        bool            status;
+
+        object = diffInputSkyfileRowAlloc(-64, true, -64, -64, "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = diffInputSkyfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupBool(&status, md, "template") == true) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "skycell_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "kind"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        diffSkyfileRow  *object;
+        bool            status;
+
+        object = diffSkyfileRowAlloc(-64, "a string", "a string", 64.64, 64.64, -32, 32.32, -32, 32.32, "a string", 32.32, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = diffSkyfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "stamps_num") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "stamps_rms") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "sources") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "dtime_diff") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "hostname"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "good_frac") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        stackRunRow     *object;
+        bool            status;
+
+        object = stackRunRowAlloc(-64, "a string", "a string", "a string", "0001-01-01T00:00:00Z", "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = stackRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "skycell_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "tess_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "filter"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        stackInputSkyfileRow *object;
+        bool            status;
+
+        object = stackInputSkyfileRowAlloc(-64, -64);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = stackInputSkyfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        stackSumSkyfileRow *object;
+        bool            status;
+
+        object = stackSumSkyfileRowAlloc(-64, "a string", "a string", 64.64, 64.64, 32.32, "a string", 32.32, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = stackSumSkyfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "dtime_stack") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "hostname"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "good_frac") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detRunRow       *object;
+        bool            status;
+
+        object = detRunRowAlloc(-64, -32, "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", "a string", 32.32, 32.32, 32.32, 32.32, 32.32, 32.32, 64.64, 64.64, "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", 32.32, 32.32, "a string", -32);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "iteration") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "det_type"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "mode"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "filelevel"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "camera"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "telescope"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "exp_type"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "reduction"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "filter"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "airmass_min") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "airmass_max") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "exp_time_min") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "exp_time_max") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "ccd_temp_min") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "ccd_temp_max") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "posang_min") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "posang_max") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "solang_min") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "solang_max") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "parent") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detInputExpRow  *object;
+        bool            status;
+
+        object = detInputExpRowAlloc(-64, -32, -64, true);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detInputExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "iteration") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupBool(&status, md, "include") == true) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detProcessedImfileRow *object;
+        bool            status;
+
+        object = detProcessedImfileRowAlloc(-64, -64, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detProcessedImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "recipe"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_0") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_3") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_4") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_5") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detProcessedExpRow *object;
+        bool            status;
+
+        object = detProcessedExpRowAlloc(-64, -64, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detProcessedExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "recipe"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_0") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_3") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_4") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_5") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detStackedImfileRow *object;
+        bool            status;
+
+        object = detStackedImfileRowAlloc(-64, -32, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detStackedImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "iteration") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "recipe"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_3") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_4") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_5") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detNormalizedStatImfileRow *object;
+        bool            status;
+
+        object = detNormalizedStatImfileRowAlloc(-64, -32, "a string", 32.32, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detNormalizedStatImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "iteration") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF32(&status, md, "norm") == 32.32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detNormalizedImfileRow *object;
+        bool            status;
+
+        object = detNormalizedImfileRowAlloc(-64, -32, "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detNormalizedImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "iteration") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_3") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_4") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_5") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detNormalizedExpRow *object;
+        bool            status;
+
+        object = detNormalizedExpRowAlloc(-64, -32, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detNormalizedExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "iteration") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "recipe"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_3") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_4") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_5") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detResidImfileRow *object;
+        bool            status;
+
+        object = detResidImfileRowAlloc(-64, -32, -64, "a string", "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detResidImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "iteration") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "recipe"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_skewness") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_kurtosis") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bin_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_0") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_resid_0") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_resid_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_resid_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_3") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_4") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_5") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detResidExpRow  *object;
+        bool            status;
+
+        object = detResidExpRowAlloc(-64, -32, -64, "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", true, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detResidExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "iteration") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "recipe"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_skewness") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_kurtosis") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bin_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_0") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_resid_0") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_resid_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "fringe_resid_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_3") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_4") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_5") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupBool(&status, md, "accept") == true) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detRunSummaryRow *object;
+        bool            status;
+
+        object = detRunSummaryRowAlloc(-64, -32, 64.64, 64.64, 64.64, true, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detRunSummaryMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "iteration") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupBool(&status, md, "accept") == true) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detRegisteredImfileRow *object;
+        bool            status;
+
+        object = detRegisteredImfileRowAlloc(-64, -32, "a string", "a string", 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, 64.64, "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detRegisteredImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "iteration") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "bg_mean_stdev") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_1") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_2") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_3") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_4") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupF64(&status, md, "user_5") == 64.64) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detCorrectedExpRow *object;
+        bool            status;
+
+        object = detCorrectedExpRowAlloc(-64, -64, "a string", -64, "a string", "a string", "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detCorrectedExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "corr_type"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "recipe"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        detCorrectedImfileRow *object;
+        bool            status;
+
+        object = detCorrectedImfileRowAlloc(-64, -64, "a string", "a string", "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = detCorrectedImfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "class_id"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "path_base"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        magicRunRow     *object;
+        bool            status;
+
+        object = magicRunRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", "0001-01-01T00:00:00Z", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = magicRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir_state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        magicInputSkyfileRow *object;
+        bool            status;
+
+        object = magicInputSkyfileRowAlloc(-64, -64, "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = magicInputSkyfileMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "node"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        magicTreeRow    *object;
+        bool            status;
+
+        object = magicTreeRowAlloc(-64, "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = magicTreeMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "node"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dep"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        magicNodeResultRow *object;
+        bool            status;
+
+        object = magicNodeResultRowAlloc(-64, "a string", "a string", -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = magicNodeResultMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "node"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        magicMaskRow    *object;
+        bool            status;
+
+        object = magicMaskRowAlloc(-64, "a string", -32, -16);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = magicMaskMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "streaks") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        calDBRow        *object;
+        bool            status;
+
+        object = calDBRowAlloc(-64, "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = calDBMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        calRunRow       *object;
+        bool            status;
+
+        object = calRunRowAlloc(-64, "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = calRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "region"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "last_step"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        flatcorrRunRow  *object;
+        bool            status;
+
+        object = flatcorrRunRowAlloc(-64, "a string", "a string", "a string", "a string", "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = flatcorrRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "dvodb"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "filter"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "workdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "label"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "stats"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "region"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        flatcorrExpRow  *object;
+        bool            status;
+
+        object = flatcorrExpRowAlloc(-64, -64);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = flatcorrExpMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        pstampDataStoreRow *object;
+        bool            status;
+
+        object = pstampDataStoreRowAlloc(-64, "a string", "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = pstampDataStoreMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "lastFileset"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "outProduct"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        pstampRequestRow *object;
+        bool            status;
+
+        object = pstampRequestRowAlloc(-64, -64, "a string", "a string", "a string", "a string", "a string", -32);
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = pstampRequestMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "name"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "reqType"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "outProduct"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "fault") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        pstampJobRow    *object;
+        bool            status;
+
+        object = pstampJobRowAlloc(-64, -64, "a string", "a string", "a string", -32, "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = pstampJobMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "rownum"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "jobType"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataLookupS32(&status, md, "fault") == -32) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "uri"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "outputBase"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "args"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/objectfrommetadata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/objectfrommetadata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/objectfrommetadata.c	(revision 22322)
@@ -0,0 +1,5637 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_STRING_LENGTH 1024
+
+int main ()
+{
+    {
+        psMetadata      *md;
+        pzDataStoreRow  *object;
+
+        md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "camera", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "telescope", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = pzDataStoreObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        summitExpRow    *object;
+
+        md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_name", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "camera", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "telescope", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_type", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "imfiles", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = summitExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->imfiles == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        summitImfileRow *object;
+
+        md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_name", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "camera", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "telescope", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "file_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "bytes", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "md5sum", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = summitImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->file_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bytes == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->md5sum, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        pzDownloadExpRow *object;
+
+        md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_name", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "camera", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "telescope", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = pzDownloadExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        pzDownloadImfileRow *object;
+
+        md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_name", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "camera", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "telescope", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = pzDownloadImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        newExpRow       *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tmp_exp_name", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tmp_camera", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tmp_telescope", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir_state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "reduction", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "end_stage", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = newExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tmp_exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tmp_camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tmp_telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir_state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        newImfileRow    *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tmp_class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = newImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tmp_class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        rawExpRow       *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_name", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "camera", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "telescope", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_tag", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_type", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "filelevel", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "reduction", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "end_stage", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "filter", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "comment", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "airmass", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "ra", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "decl", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "exp_time", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "sat_pixel_frac", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "alt", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "az", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "ccd_temp", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "posang", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m1_x", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m1_y", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m1_z", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m1_tip", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m1_tilt", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m2_x", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m2_y", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m2_z", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m2_tip", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m2_tilt", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "env_temperature", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "env_humidity", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "env_wind_speed", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "env_wind_dir", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_m1", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_m1cell", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_m2", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_spider", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_truss", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_extra", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "pon_time", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_3", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_4", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_5", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "object", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "solang", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "hostname", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = rawExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_tag, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filelevel, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filter, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->comment, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->airmass == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ra == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->decl == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_time == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sat_pixel_frac == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->alt == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->az == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ccd_temp == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->posang == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_x == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_y == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_z == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_tip == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_tilt == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_x == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_y == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_z == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_tip == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_tilt == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_temperature == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_humidity == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_wind_speed == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_wind_dir == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m1 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m1cell == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m2 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_spider == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_truss == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_extra == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->pon_time == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->object, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->solang == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        rawImfileRow    *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_name", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "camera", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "telescope", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tmp_class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_type", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "filelevel", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "filter", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "comment", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "airmass", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "ra", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "decl", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "exp_time", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "sat_pixel_frac", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "alt", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "az", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "ccd_temp", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "posang", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m1_x", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m1_y", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m1_z", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m1_tip", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m1_tilt", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m2_x", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m2_y", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m2_z", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m2_tip", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "m2_tilt", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "env_temperature", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "env_humidity", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "env_wind_speed", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "env_wind_dir", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_m1", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_m1cell", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_m2", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_spider", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_truss", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "teltemp_extra", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "pon_time", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_3", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_4", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_5", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "object", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "hostname", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = rawImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tmp_class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filelevel, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filter, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->comment, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->airmass == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ra == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->decl == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_time == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sat_pixel_frac == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->alt == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->az == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ccd_temp == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->posang == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_x == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_y == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_z == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_tip == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m1_tilt == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_x == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_y == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_z == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_tip == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->m2_tilt == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_temperature == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_humidity == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_wind_speed == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->env_wind_dir == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m1 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m1cell == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_m2 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_spider == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_truss == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->teltemp_extra == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->pon_time == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->object, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        guidePendingExpRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "recipe", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = guidePendingExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        chipRunRow      *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir_state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "reduction", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "expgroup", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "end_stage", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = chipRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir_state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->expgroup, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        chipProcessedImfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "bg", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "bias", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "bias_stdev", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "fringe_0", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "fringe_1", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "fringe_2", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "sigma_ra", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "sigma_dec", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "ap_resid", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "ap_resid_stdev", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "zp_mean", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "zp_stdev", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "fwhm_major", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "fwhm_minor", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "dtime_detrend", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "dtime_photom", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "dtime_astrom", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "hostname", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "n_stars", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "n_extended", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "n_cr", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "n_astrom", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = chipProcessedImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bias == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bias_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sigma_ra == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sigma_dec == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ap_resid == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ap_resid_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->zp_mean == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->zp_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fwhm_major == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fwhm_minor == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_detrend == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_photom == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_astrom == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_stars == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_extended == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_cr == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_astrom == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        chipMaskRow     *object;
+
+        md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = chipMaskObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        camRunRow       *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir_state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "reduction", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "expgroup", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "end_stage", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = camRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir_state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->expgroup, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        camProcessedExpRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "bg", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "bias", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "bias_stdev", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "fringe_0", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "fringe_1", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "fringe_2", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "sigma_ra", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "sigma_dec", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "ap_resid", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "ap_resid_stdev", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "zp_mean", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "zp_stdev", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "fwhm_major", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "fwhm_minor", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "dtime_detrend", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "dtime_photom", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "dtime_astrom", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "hostname", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "n_stars", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "n_extended", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "n_cr", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "n_astrom", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = camProcessedExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bias == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bias_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sigma_ra == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sigma_dec == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ap_resid == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ap_resid_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->zp_mean == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->zp_stdev == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fwhm_major == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fwhm_minor == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_detrend == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_photom == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_astrom == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_stars == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_extended == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_cr == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->n_astrom == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        camMaskRow      *object;
+
+        md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = camMaskObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        fakeRunRow      *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "reduction", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "expgroup", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "end_stage", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = fakeRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->expgroup, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        fakeProcessedImfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "dtime_fake", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "hostname", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = fakeProcessedImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_fake == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        fakeMaskRow     *object;
+
+        md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = fakeMaskObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        warpRunRow      *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "mode", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir_state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "end_stage", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, true)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = warpRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->mode, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir_state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->end_stage, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->magiced == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        warpSkyCellMapRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "skycell_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = warpSkyCellMapObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->skycell_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        warpSkyfileRow  *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "skycell_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "dtime_warp", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "hostname", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "good_frac", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "xmin", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "xmax", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "ymin", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "ymax", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAdd(md, PS_LIST_TAIL, "ignored", PS_DATA_BOOL, NULL, true)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = warpSkyfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->skycell_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_warp == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->good_frac == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->xmin == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->xmax == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ymin == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ymax == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ignored == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        warpMaskRow     *object;
+
+        md = psMetadataAlloc();
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = warpMaskObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        diffRunRow      *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "skycell_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = diffRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->skycell_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        diffInputSkyfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, true)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "skycell_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "kind", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = diffInputSkyfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->template == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->skycell_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->kind, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        diffSkyfileRow  *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "stamps_num", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "stamps_rms", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "sources", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "dtime_diff", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "hostname", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "good_frac", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = diffSkyfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->stamps_num == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->stamps_rms == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->sources == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_diff == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->good_frac == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        stackRunRow     *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "skycell_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "tess_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "filter", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = stackRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->skycell_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->tess_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filter, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        stackInputSkyfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = stackInputSkyfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        stackSumSkyfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "dtime_stack", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "hostname", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "good_frac", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = stackSumSkyfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->dtime_stack == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->hostname, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->good_frac == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detRunRow       *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "iteration", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "det_type", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "mode", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "filelevel", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "camera", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "telescope", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "exp_type", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "reduction", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "filter", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "airmass_min", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "airmass_max", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "exp_time_min", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "exp_time_max", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "ccd_temp_min", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "ccd_temp_max", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "posang_min", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "posang_max", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "solang_min", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "solang_max", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "parent", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->det_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->mode, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filelevel, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->camera, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->telescope, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->exp_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reduction, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filter, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->airmass_min == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->airmass_max == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_time_min == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->exp_time_max == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ccd_temp_min == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->ccd_temp_max == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->posang_min == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->posang_max == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->solang_min == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->solang_max == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->parent == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detInputExpRow  *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "iteration", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAdd(md, PS_LIST_TAIL, "include", PS_DATA_BOOL, NULL, true)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detInputExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->include == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detProcessedImfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "recipe", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_0", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_3", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_4", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_5", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detProcessedImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detProcessedExpRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "recipe", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_0", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_3", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_4", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_5", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detProcessedExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detStackedImfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "iteration", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "recipe", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_3", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_4", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_5", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detStackedImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detNormalizedStatImfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "iteration", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF32(md, PS_LIST_TAIL, "norm", 0, NULL, 32.32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detNormalizedStatImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->norm == 32.32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detNormalizedImfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "iteration", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_3", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_4", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_5", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detNormalizedImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detNormalizedExpRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "iteration", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "recipe", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_3", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_4", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_5", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detNormalizedExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detResidImfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "iteration", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "recipe", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_skewness", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_kurtosis", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bin_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_0", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_resid_0", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_resid_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_resid_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_3", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_4", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_5", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detResidImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_skewness == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_kurtosis == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bin_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detResidExpRow  *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "iteration", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "recipe", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_skewness", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_kurtosis", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bin_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_0", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_resid_0", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_resid_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "fringe_resid_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_3", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_4", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_5", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAdd(md, PS_LIST_TAIL, "accept", PS_DATA_BOOL, NULL, true)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detResidExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_skewness == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_kurtosis == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bin_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_0 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fringe_resid_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->accept == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detRunSummaryRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "iteration", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAdd(md, PS_LIST_TAIL, "accept", PS_DATA_BOOL, NULL, true)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detRunSummaryObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->accept == true) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detRegisteredImfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "iteration", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "bg_mean_stdev", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_1", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_2", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_3", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_4", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddF64(md, PS_LIST_TAIL, "user_5", 0, NULL, 64.64)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detRegisteredImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->iteration == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->bg_mean_stdev == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_1 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_2 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_3 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_4 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->user_5 == 64.64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detCorrectedExpRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "corr_type", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "recipe", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detCorrectedExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->corr_type, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->recipe, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        detCorrectedImfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "class_id", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "path_base", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = detCorrectedImfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->class_id, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->path_base, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        magicRunRow     *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir_state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = magicRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir_state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        magicInputSkyfileRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "node", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = magicInputSkyfileObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->node, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        magicTreeRow    *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "node", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dep", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = magicTreeObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->node, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dep, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        magicNodeResultRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "node", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = magicNodeResultObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->node, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        magicMaskRow    *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "streaks", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = magicMaskObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->streaks == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        calDBRow        *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = calDBObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        calRunRow       *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "region", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "last_step", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = calRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->region, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->last_step, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        flatcorrRunRow  *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "dvodb", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "filter", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "workdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "label", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "stats", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "region", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = flatcorrRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->dvodb, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->filter, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->workdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->label, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->stats, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->region, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        flatcorrExpRow  *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = flatcorrExpObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        pstampDataStoreRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "lastFileset", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "outProduct", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = pstampDataStoreObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->lastFileset, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->outProduct, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        pstampRequestRow *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "name", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "reqType", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "outProduct", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "fault", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = pstampRequestObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->name, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->reqType, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->outProduct, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        pstampJobRow    *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "rownum", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "jobType", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddS32(md, PS_LIST_TAIL, "fault", 0, NULL, -32)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "uri", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "outputBase", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "args", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = pstampJobObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->rownum, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->jobType, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (!object->fault == -32) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->uri, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->outputBase, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->args, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/selectrowsfits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/selectrowsfits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/selectrowsfits.c	(revision 22322)
@@ -0,0 +1,1220 @@
+#include <pslib.h>
+#include <ippdb.h>
+#include <stdlib.h>
+
+#define TMP_FILENAME "./blargh"
+
+int main ()
+{
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDataStoreSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!summitExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!summitImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDownloadExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pzDownloadImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!newExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!newImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!rawExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!rawImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!guidePendingExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipProcessedImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!chipMaskSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camProcessedExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!camMaskSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeProcessedImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!fakeMaskSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpSkyCellMapSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpSkyfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!warpMaskSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffInputSkyfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!diffSkyfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackInputSkyfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!stackSumSkyfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detInputExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detProcessedImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detProcessedExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detStackedImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedStatImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detNormalizedExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detResidImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detResidExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRunSummarySelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detRegisteredImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detCorrectedExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!detCorrectedImfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicInputSkyfileSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicTreeSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicNodeResultSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!magicMaskSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calDBSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!flatcorrRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!flatcorrExpSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampDataStoreSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampRequestSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!pstampJobSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ippdb/tests/testsuite.at
===================================================================
--- /tags/sj_tags/sj_root_20080929/ippdb/tests/testsuite.at	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ippdb/tests/testsuite.at	(revision 22322)
@@ -0,0 +1,119 @@
+m4_version_prereq([2.59])
+
+AT_INIT
+
+###
+AT_SETUP([Alloc()])
+AT_KEYWORDS([Alloc])
+
+AT_CHECK([alloc])
+
+AT_CLEANUP
+
+###
+AT_SETUP([Init()])
+AT_KEYWORDS([Init])
+
+AT_CHECK([init])
+
+AT_CLEANUP
+
+###
+AT_SETUP([Cleanup()])
+AT_KEYWORDS([Cleanup])
+
+AT_CHECK([cleanup])
+
+AT_CLEANUP
+
+###
+AT_SETUP([CreateTable()])
+AT_KEYWORDS([CreateTable])
+
+# make sure the table is not there.
+AT_CHECK([dbcleanup])
+AT_CHECK([createtable])
+AT_CHECK([dbcleanup])
+
+AT_CLEANUP
+
+###
+AT_SETUP([DropTable()])
+AT_KEYWORDS([DropTable])
+
+# make sure the table is not there.
+AT_CHECK([dbsetup])
+AT_CHECK([droptable])
+AT_CHECK([dbcleanup])
+
+AT_CLEANUP
+
+###
+AT_SETUP([Insert()])
+AT_KEYWORDS([Insert])
+
+AT_CHECK([dbsetup])
+AT_CHECK([insert])
+AT_CHECK([dbcleanup])
+
+AT_CLEANUP
+
+###
+AT_SETUP([InsertObject()])
+AT_KEYWORDS([InsertObject])
+
+AT_CHECK([dbsetup])
+AT_CHECK([insertobject])
+AT_CHECK([dbcleanup])
+
+AT_CLEANUP
+
+###
+AT_SETUP([InsertFits()])
+AT_KEYWORDS([InsertFits])
+
+AT_CHECK([dbsetup])
+# run insert so there is a row in the table
+AT_CHECK([insert])
+# run popfis so there is afits file to read
+AT_CHECK([selectrowsfits])
+AT_CHECK([insertfits])
+AT_CHECK([dbcleanup])
+if [ test -f "blargh" ] ; then
+    rm -f "blargh"
+fi
+
+AT_CLEANUP
+
+###
+AT_SETUP([SelectRowsFits()])
+AT_KEYWORDS([SelectRowsFits])
+
+AT_CHECK([dbsetup])
+# run insert so there is a row in the table
+AT_CHECK([insert])
+AT_CHECK([selectrowsfits])
+AT_CHECK([dbcleanup])
+if [ test -f "blargh" ] ; then
+    rm -f "blargh"
+fi
+
+AT_CLEANUP
+
+###
+AT_SETUP([MetadataFromObject()])
+AT_KEYWORDS([MetadataFromObject])
+
+AT_CHECK([metadatafromobject])
+
+AT_CLEANUP
+
+###
+AT_SETUP([ObjectFromMetadata()])
+AT_KEYWORDS([ObjectFromMetadata])
+
+AT_CHECK([objectfrommetadata])
+
+AT_CLEANUP
+
+###
Index: /tags/sj_tags/sj_root_20080929/megacamTools/mcshort.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/megacamTools/mcshort.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/megacamTools/mcshort.sh	(revision 22322)
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+for name in *.fits ; do
+    short=`echo $name | sed 's/\.fits//'`
+    for chipNum in 12 13 14 21 22 23; do
+      chipName=`printf 'ccd%02d' $chipNum`
+      imcopy $name\[$chipName\] $short.$chipName.fits
+    done
+done
+    
+
Index: /tags/sj_tags/sj_root_20080929/megacamTools/split.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/megacamTools/split.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/megacamTools/split.sh	(revision 22322)
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+for name in *.fits ; do
+    short=`echo $name | sed 's/\.fits//'`
+    chipNum=0
+    while [ $chipNum -lt 36 ] ; do
+      chipName=`printf 'ccd%02d' $chipNum`
+      imcopy $name\[$chipName\] $short.$chipName.fits
+      chipNum=$(( $chipNum + 1 ))
+    done
+done
+    
+
Index: /tags/sj_tags/sj_root_20080929/pedestal/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pedestal/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pedestal/.cvsignore	(revision 22322)
@@ -0,0 +1,15 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.guess
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+ltmain.sh
+missing
+test
Index: /tags/sj_tags/sj_root_20080929/pedestal/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/pedestal/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pedestal/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/pedestal/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/pedestal/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pedestal/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppImage
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/pedestal/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/pedestal/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pedestal/configure.ac	(revision 22322)
@@ -0,0 +1,28 @@
+AC_PREREQ(2.61)
+
+AC_INIT([pedestal], [1.1.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/pedestal/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pedestal/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pedestal/src/.cvsignore	(revision 22322)
@@ -0,0 +1,7 @@
+.deps
+Makefile
+Makefile.in
+config.h
+config.h.in
+pedestal
+stamp-h1
Index: /tags/sj_tags/sj_root_20080929/pedestal/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/pedestal/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pedestal/src/Makefile.am	(revision 22322)
@@ -0,0 +1,14 @@
+bin_PROGRAMS = pedestal
+
+pedestal_CPPFLAGS = $(PSLIB_CFLAGS) $(pedestal_CFLAGS)
+pedestal_LDFLAGS = $(PSLIB_LIBS)
+pedestal_SOURCES = \
+	pedestal.c
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/pedestal/src/pedestal.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pedestal/src/pedestal.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pedestal/src/pedestal.c	(revision 22322)
@@ -0,0 +1,85 @@
+#include <stdio.h>
+#include <assert.h>
+#include <pslib.h>
+
+static bool subtract(psFits *inFile, psFits *outFile, const char *extname, psMetadata *header)
+{
+    assert(inFile);
+    assert(outFile);
+    assert(extname);
+    assert(header);
+
+    int naxis = psMetadataLookupS32(NULL, header, "NAXIS"); // NAXIS from the header
+    if (naxis == 0) {
+        // No image attached
+        return psFitsWriteBlank(outFile, header, extname);
+    }
+
+    if (naxis != 3) {
+        psWarning("Extension %s has NAXIS == %d --- unable to subtract pedestal.\n", extname, naxis);
+        return false;
+    }
+
+    int naxis3 = psMetadataLookupS32(NULL, header, "NAXIS3"); // NAXIS3 from the header
+    if (naxis3 != 2) {
+        psWarning("Extension %s has NAXIS3 == %d --- unable to subtract pedestal.\n", extname, naxis3);
+        return false;
+    }
+
+    psFitsMoveExtName(inFile, extname);
+    psArray *cube = psFitsReadImageCube(inFile, psRegionSet(0, 0, 0, 0)); // Image cube
+    assert(cube->n == 2);               // We checked NAXIS3 earlier
+
+    psImage *image = cube->data[0];     // The image data
+    psImage *pedestal = cube->data[1];  // The pedestal data
+
+    image = (psImage*)psBinaryOp(image, image, "-", pedestal);
+
+    bool status = psFitsWriteImage(outFile, header, image, 0, extname); // Status of write
+    psFree(cube);
+
+    return status;
+}
+
+int main(int argc, char *argv[])
+{
+    if (argc != 3) {
+        printf("Subtract pedestal from FITS images.\n\n"
+               "Usage: %s IN.fits OUT.fits\n\n", argv[0]);
+        exit(argc == 1 ? PS_EXIT_SUCCESS : PS_EXIT_UNKNOWN_ERROR);
+    }
+
+    const char *inName = argv[1];       // Input file name
+    psFits *inFile = psFitsOpen(inName, "r"); // Input file
+    if (!inFile) {
+        psErrorStackPrint(stderr, "Unable to open input file %s", inName);
+        exit(PS_EXIT_DATA_ERROR);
+    }
+
+    const char *outName = argv[2];      // Output file name
+    psFits *outFile = psFitsOpen(outName, "w"); // Output file
+    if (!outFile) {
+        psErrorStackPrint(stderr, "Unable to open output file %s", outName);
+        exit(PS_EXIT_DATA_ERROR);
+    }
+
+    psMetadata *headers = psFitsReadHeaderSet(NULL, inFile); // Headers in the input
+
+    psMetadata *phu = psMetadataLookupMetadata(NULL, headers, "PHU"); // The PHU
+    subtract(inFile, outFile, "PHU", phu);
+    psMetadataRemoveKey(headers, "PHU");
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(headers, PS_LIST_HEAD, NULL); // Iterator for headers
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        assert(item->type == PS_DATA_METADATA);
+        subtract(inFile, outFile, item->name, item->data.V);
+    }
+    psFree(iter);
+    psFree(headers);
+
+    psFitsClose(inFile);
+    psFitsClose(outFile);
+
+    return PS_EXIT_SUCCESS;
+}
Index: /tags/sj_tags/sj_root_20080929/pois/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/.cvsignore	(revision 22322)
@@ -0,0 +1,7 @@
+Makefile Makefile.in
+aclocal.m4 autom4te.cache
+config.* configure
+depcomp install-sh libtool ltmain.sh missing mkinstalldirs compile stamp-h*
+pois.pc
+pois-*.tar.bz2
+pois-*.tar.gz
Index: /tags/sj_tags/sj_root_20080929/pois/GPL.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/GPL.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/GPL.txt	(revision 22322)
@@ -0,0 +1,345 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /tags/sj_tags/sj_root_20080929/pois/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/INSTALL	(revision 22322)
@@ -0,0 +1,62 @@
+$ cvs co jhbuild
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+Setup $HOME/.jhbuildrc to install in (say) $HOME/JHB
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+I found that I needed to say
+
+$ jhbuild boostrap
+
+in order to get my autoconf macros recognised
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+$ jhbuild build pslib
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+Install
+	ac_pkg_swig.m4      ac_python_devel.m4
+into
+	$HOME/JHB/share/aclocal
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+To build/use the code, you'll have to source some script such as:
+
+export JHTREE=$HOME/JHB/Linux
+export PYTHONPATH=$HOME/Python:$HOME/Pan-STARRS/Pois/python:$HOME/Pan-STARRS/Pois/swig:$JHTREE/lib
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JHTREE/lib
+export PATH=$JHTREE/bin:$(echo $PATH | perl -pe "s|$JHTREE/bin||")
+export PKG_CONFIG_PATH=$JHTREE/lib/pkgconfig:$PKG_CONFIG_PATH
+hash -r
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+You need to set XPA_DIR to somewhere that the xpa libraries can be found;
+or (better) install the xpa stuff in $JHTREE/{lib,include}
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+To build from scratch:
+
+./autogen.sh --prefix=$JHTREE
+
+(note that you set JHTREE above)
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+To be able to display images, ensure that ds9 and xpans are in your path (xpans
+is a utility used to communicate with ds9)
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+When all is setup, say:
+
+$ python
+>>> import pois
+>>> help(pois.run)
+>>> pois.run(fileDir="data", verbose=3, display={'stamps':1, 'reference':1, 'subtracted':1})
Index: /tags/sj_tags/sj_root_20080929/pois/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/Makefile.am	(revision 22322)
@@ -0,0 +1,5 @@
+SUBDIRS = src #swig
+
+CLEANFILES = *.pyc *~ core core.*
+
+EXTRA_DIST = autogen.sh
Index: /tags/sj_tags/sj_root_20080929/pois/NOTES
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/NOTES	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/NOTES	(revision 22322)
@@ -0,0 +1,153 @@
+Installed
+	ac_python_devel.m4
+	ac_pkg_swig.m4
+in ~/JHB/Tree/share/aclocal
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+Added
+	AC_PROG_SWIG(1.3.17)
+	SWIG_MULTI_MODULE_SUPPORT
+	SWIG_PYTHON
+
+...	
+
+	AC_CONFIG_FILES(pois.pc Makefile src/Makefile swig/Makefile)
+	
+to configure.ac
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+Need to fix swig autoconf so that the configure chooses the target language[s],
+but the same Makefile.am works for both
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+Initially running make fails due to:
+	  Makefile:261: .deps/libswigrun..Po: No such file or directory
+	  make: *** No rule to make target `.deps/libswigrun..Po'.  Stop.
+Changing the
+include ./$(DEPDIR)/libswigrun.$(SO).Po
+in Makefile (not Makefile.am) to
+-include ./$(DEPDIR)/libswigrun.$(SO).Po
+and running make successfully bootstrapped things.
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+cp ~/Pan-STARRS/psLib/cvs/src/parseErrorCodes.pl ~/JHB/Tree/bin
+
+(N.b. this is the RHL version)
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+autogen.sh --prefix=$HOME/JHB/Tree 
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+Need autoconf for xpa
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+Need swig package (1.3.24?)
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+Need to add Tree/lib to PYTHONPATH (and also PERL5LIB?)
+
+export PYTHONPATH=$PYTHONPATH:$HOME/Pan-STARRS/Price/python:$HOME/Pan-STARRS/Price/swig:$HOME/JHB/Tree/lib
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+How do I make a .so file LOCALLY, without having to do a make install?
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+Why do things suddenly get installed as _xpa.0.so??
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+If I try to build pois as well as libpois.a, the compiler fails to find pslib
+while building pois.c
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+
+psBinaryOp isn't implemented for & and |
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+psBinaryOp &/| can be faked with a cast to long, but this will fail for some
+unsigned types.  Casting to unsigned will fail on some machines. Sigh.
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+psImageSubset SDRS should allow -ve row0/col0 as well as row1/col1
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+psBinaryOp fails if e.g. data1 is U8 and the (scalar) data2 is U32.
+Shouldn't it handle promotions?
+
+The SDRS reads:
+ Operations between data structures with different types (e.g., psS32
+ and psF32) are not allowed and must raise an error (it is the
+ responsibility of calling functions to perform type conversions).
+
+but I think that integers should all be handled via the usual C rules
+(which are only tricky for unsigned).
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+The file src/libpois.a has to exist
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+psArray* psArrayAlloc(psU32 nalloc)
+
+fails to initialise the array to NULLs (not specified in the SDRS)
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+"bool psListAdd(psList *list, int location, void *data);
+
+the first function, psListAdd, adds an entry to the list and returns a
+boolean giving the success or failure of the operation."
+
+(SDRS).  It'd be better to define
+
+psList *psListAdd(psList *list, int location, void *data);
+and to make
+    psListAdd(NULL, location, data);
+equivalent to
+    psListAlloc(data);
+
+That makes the code to append to a list simpler; instead of
+
+     list = NULL;
+     while(foo) {
+        ...
+	foo = psFooAlloc();
+	if(list == NULL) {
+	   list = psListAlloc(foo);
+	} else {
+	   bool ok = psListAdd(list, PS_LIST_TAIL, foo);
+	   assert(ok);
+	}
+	psFree(foo);
+     }
+
+I can simply write:
+
+     list = NULL;
+     while(foo) {
+        ...
+	foo = psFooAlloc();
+        psListAdd(list, PS_LIST_TAIL, foo);
+	psFree(foo);
+     }
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
Index: /tags/sj_tags/sj_root_20080929/pois/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=pois
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL -I m4 || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE  failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/pois/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/configure.ac	(revision 22322)
@@ -0,0 +1,48 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.61)
+
+AC_INIT([pois], [0.1.1], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([autogen.sh])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+
+AC_C_BIGENDIAN([PS_BIGENDIAN=1],[PS_BIGENDIAN=0])
+
+dnl AC_PROG_SWIG(1.3.24)
+dnl SWIG_MULTI_MODULE_SUPPORT
+dnl SWIG_PYTHON
+
+dnl lifted from autoconf 2.59's configure.ac
+AC_PATH_PROG([PERL], perl, no)
+AC_SUBST([PERL])
+if test "$PERL" = no; then
+  AC_MSG_ERROR([perl is not found])
+fi
+$PERL -e 'require 5.005_03;' || {
+   AC_MSG_ERROR([Perl 5.005_03 or better is required])
+}
+
+dnl Set CFLAGS for build
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+dnl swig/Makefile
+dnl python/Makefile
+
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/pois/m4/ac_pkg_swig.m4
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/m4/ac_pkg_swig.m4	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/m4/ac_pkg_swig.m4	(revision 22322)
@@ -0,0 +1,160 @@
+dnl @synopsis AC_PROG_SWIG([major.minor.micro])
+dnl
+dnl This macro searches for a SWIG installation on your system. If
+dnl found you should call SWIG via $(SWIG). You can use the optional
+dnl first argument to check if the version of the available SWIG is
+dnl greater than or equal to the value of the argument. It should have
+dnl the format: N[.N[.N]] (N is a number between 0 and 999. Only the
+dnl first N is mandatory.)
+dnl
+dnl If the version argument is given (e.g. 1.3.17), AC_PROG_SWIG checks
+dnl that the swig package is this version number or higher.
+dnl
+dnl In configure.in, use as:
+dnl
+dnl             AC_PROG_SWIG(1.3.17)
+dnl             SWIG_ENABLE_CXX
+dnl             SWIG_MULTI_MODULE_SUPPORT
+dnl             SWIG_PYTHON
+dnl
+dnl @category InstalledPackages
+dnl @author Sebastian Huber <sebastian-huber@web.de>
+dnl @author Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+dnl @author Rafael Laboissiere <rafael@laboissiere.net>
+dnl @author Andrew Collier <abcollier@yahoo.com>
+dnl @version 2004-09-20
+dnl @license GPLWithACException
+
+AC_DEFUN([AC_PROG_SWIG],[
+        AC_PATH_PROG([SWIG],[swig])
+        if test -z "$SWIG" ; then
+                AC_MSG_WARN([cannot find 'swig' program. You should look at http://www.swig.org])
+                SWIG='echo "Error: SWIG is not installed. You should look at http://www.swig.org" ; false'
+        elif test -n "$1" ; then
+                AC_MSG_CHECKING([for SWIG version])
+                [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`]
+                AC_MSG_RESULT([$swig_version])
+                if test -n "$swig_version" ; then
+                        # Calculate the required version number components
+                        [required=$1]
+                        [required_major=`echo $required | sed 's/[^0-9].*//'`]
+                        if test -z "$required_major" ; then
+                                [required_major=0]
+                        fi
+                        [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+                        [required_minor=`echo $required | sed 's/[^0-9].*//'`]
+                        if test -z "$required_minor" ; then
+                                [required_minor=0]
+                        fi
+                        [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+                        [required_patch=`echo $required | sed 's/[^0-9].*//'`]
+                        if test -z "$required_patch" ; then
+                                [required_patch=0]
+                        fi
+                        # Calculate the available version number components
+                        [available=$swig_version]
+                        [available_major=`echo $available | sed 's/[^0-9].*//'`]
+                        if test -z "$available_major" ; then
+                                [available_major=0]
+                        fi
+                        [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
+                        [available_minor=`echo $available | sed 's/[^0-9].*//'`]
+                        if test -z "$available_minor" ; then
+                                [available_minor=0]
+                        fi
+                        [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
+                        [available_patch=`echo $available | sed 's/[^0-9].*//'`]
+                        if test -z "$available_patch" ; then
+                                [available_patch=0]
+                        fi
+                        if test $available_major -ne $required_major \
+                                -o $available_minor -ne $required_minor \
+                                -o $available_patch -lt $required_patch ; then
+                                AC_MSG_WARN([SWIG version >= $1 is required.  You have $swig_version.  You should look at http://www.swig.org])
+                                SWIG='echo "Error: SWIG version >= $1 is required.  You have '"$swig_version"'.  You should look at http://www.swig.org" ; false'
+                        else
+                                AC_MSG_NOTICE([SWIG executable is '$SWIG'])
+                                SWIG_LIB=`$SWIG -swiglib`
+                                AC_MSG_NOTICE([SWIG library directory is '$SWIG_LIB'])
+                        fi
+                else
+                        AC_MSG_WARN([cannot determine SWIG version])
+                        SWIG='echo "Error: Cannot determine SWIG version.  You should look at http://www.swig.org" ; false'
+                fi
+        fi
+        AC_SUBST([SWIG_LIB])
+])
+
+# SWIG_ENABLE_CXX()
+#
+# Enable SWIG C++ support.  This affects all invocations of $(SWIG).
+AC_DEFUN([SWIG_ENABLE_CXX],[
+        AC_REQUIRE([AC_PROG_SWIG])
+        AC_REQUIRE([AC_PROG_CXX])
+        SWIG="$SWIG -c++"
+])
+
+# SWIG_MULTI_MODULE_SUPPORT()
+#
+# Enable support for multiple modules.  This effects all invocations
+# of $(SWIG).  You have to link all generated modules against the
+# appropriate SWIG runtime library.  If you want to build Python
+# modules for example, use the SWIG_PYTHON() macro and link the
+# modules against $(SWIG_LANG_LIBS).
+#
+AC_DEFUN([SWIG_MULTI_MODULE_SUPPORT],[
+        AC_REQUIRE([AC_PROG_SWIG])
+        #SWIG="$SWIG -noruntime"  # Not needed for SWIG >= 1.3.24
+])
+
+# SWIG_PYTHON([use-shadow-classes = {no, yes}])
+#
+# Checks for Python and provides the $(SWIG_PYTHON_CPPFLAGS),
+# and $(SWIG_PYTHON_OPT) output variables.
+#
+# $(SWIG_PYTHON_OPT) contains all necessary SWIG options to generate
+# code for Python.  Shadow classes are enabled unless the value of the
+# optional first argument is exactly 'no'.  If you need multi module
+# support (provided by the SWIG_MULTI_MODULE_SUPPORT() macro) use
+# $(SWIG_PYTHON_LIBS) to link against the appropriate library.  It
+# contains the SWIG Python runtime library that is needed by the type
+# check system for example.
+AC_DEFUN([SWIG_PYTHON],[
+        AC_REQUIRE([AC_PROG_SWIG])
+        AC_REQUIRE([AC_PYTHON_DEVEL])
+        test "x$1" != "xno" || swig_shadow=" -noproxy"
+        AC_SUBST([SWIG_PYTHON_OPT],[-python$swig_shadow])
+        AC_SUBST([SWIG_PYTHON_CPPFLAGS],[$PYTHON_CPPFLAGS])
+        AC_SUBST([SWIG_PYTHON_LIBS],[$PYTHON_LIBS])
+
+        AC_SUBST([SWIG_TARGET_OPT],     [$SWIG_PYTHON_OPT])
+        AC_SUBST([SWIG_TARGET_CPPFLAGS],[$SWIG_PYTHON_CPPFLAGS])
+        AC_SUBST([SWIG_TARGET_LIBS],    [$SWIG_PYTHON_LIBS])
+])
+
+
+dnl @synopsis AC_LIB_WAD
+dnl
+dnl This macro searches for installed WAD library.
+dnl
+AC_DEFUN([AC_LIB_WAD],
+[
+        AC_REQUIRE([AC_PYTHON_DEVEL])
+        AC_ARG_ENABLE(wad,
+        AC_HELP_STRING([--enable-wad], [enable wad module]),
+        [
+                case "${enableval}" in
+                        no)     ;;
+                        *)      if test "x${enableval}" = xyes;
+                                then
+                                        check_wad="yes"
+                                fi ;;
+                esac
+        ], [])
+
+        if test -n "$check_wad";
+        then
+                AC_CHECK_LIB(wadpy, _init, [WADPY=-lwadpy], [], $PYTHON_LDFLAGS $PYTHON_EXTRA_LIBS)
+                AC_SUBST(WADPY)
+        fi
+])
Index: /tags/sj_tags/sj_root_20080929/pois/m4/ac_python_devel.m4
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/m4/ac_python_devel.m4	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/m4/ac_python_devel.m4	(revision 22322)
@@ -0,0 +1,64 @@
+dnl @synopsis AC_PYTHON_DEVEL
+dnl
+dnl Checks for Python and tries to get the include path to 'Python.h'.
+dnl It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS) output
+dnl variable.
+dnl
+dnl @category InstalledPackages
+dnl @author Sebastian Huber <sebastian-huber@web.de>
+dnl @author Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+dnl @author Rafael Laboissiere <laboissiere@psy.mpg.de>
+dnl @author Andrew Collier <colliera@nu.ac.za>
+dnl @version 2004-07-14
+dnl @license GPLWithACException
+
+AC_DEFUN([AC_PYTHON_DEVEL],[
+	#
+	# should allow for checking of python version here...
+	#
+	AC_REQUIRE([AM_PATH_PYTHON])
+
+	# Check for Python include path
+	AC_MSG_CHECKING([for Python include path])
+	python_path=`echo $PYTHON | sed "s,/bin.*$,,"`
+	for i in "$python_path/include/python$PYTHON_VERSION/" "$python_path/include/python/" "$python_path/" ; do
+		python_path=`find $i -type f -name Python.h -print | sed "1q"`
+		if test -n "$python_path" ; then
+			break
+		fi
+	done
+	python_path=`echo $python_path | sed "s,/Python.h$,,"`
+	AC_MSG_RESULT([$python_path])
+	if test -z "$python_path" ; then
+		AC_MSG_ERROR([cannot find Python include path])
+	fi
+	AC_SUBST([PYTHON_CPPFLAGS],[-I$python_path])
+
+	# Check for Python library path
+	AC_MSG_CHECKING([for Python library path])
+	python_path=`echo $PYTHON | sed "s,/bin.*$,,"`
+	for i in "$python_path/lib/python$PYTHON_VERSION/config/" "$python_path/lib/python$PYTHON_VERSION/" "$python_path/lib/python/config/" "$python_path/lib/python/" "$python_path/" ; do
+		python_path=`find $i -type f -name libpython$PYTHON_VERSION.* -print | sed "1q"`
+		if test -n "$python_path" ; then
+			break
+		fi
+	done
+	python_path=`echo $python_path | sed "s,/libpython.*$,,"`
+	AC_MSG_RESULT([$python_path])
+	if test -z "$python_path" ; then
+		AC_MSG_ERROR([cannot find Python library path])
+	fi
+	AC_SUBST([PYTHON_LDFLAGS],["-L$python_path -lpython$PYTHON_VERSION"])
+	#
+	python_site=`echo $python_path | sed "s/config/site-packages/"`
+	AC_SUBST([PYTHON_SITE_PKG],[$python_site])
+	#
+	# libraries which must be linked in when embedding
+	#
+	AC_MSG_CHECKING(python extra libraries)
+	PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
+                conf = distutils.sysconfig.get_config_var; \
+                print conf('LOCALMODLIBS')+' '+conf('LIBS')"
+	AC_MSG_RESULT($PYTHON_EXTRA_LIBS)`
+	AC_SUBST(PYTHON_EXTRA_LIBS)
+])
Index: /tags/sj_tags/sj_root_20080929/pois/python/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/python/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/python/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile Makefile.in
+*.pyc
Index: /tags/sj_tags/sj_root_20080929/pois/python/ds9.py
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/python/ds9.py	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/python/ds9.py	(revision 22322)
@@ -0,0 +1,163 @@
+#
+# XPA
+#
+import os, re, math, sys, time
+
+try: import xpaSwig as xpa
+except: print "Cannot import xpa"
+
+class Ds9Error(IOError):
+    """Some problem talking to ds9"""
+
+def ds9Cmd(cmd):
+   """Issue a ds9 command, raising errors as appropriate"""
+   
+   try:
+      xpa.XPASet(None, "ds9", cmd, "", "", 0)
+   except IOError:
+      raise Ds9Error, "XPA: (%s)" % cmd
+
+def initDS9(execDs9 = 1):
+   try:
+      ds9Cmd("iconify no; raise")
+   except IOError:
+      if execDs9:
+         print "ds9 doesn't appear to be running, I'll exec it for you"
+         if not re.search('xpa', os.environ['PATH']):
+            raise Ds9Error, 'You need the xpa binaries in your path to use ds9 with python'
+
+         os.system('ds9 &')
+         for i in range(0,10):
+            try:
+               ds9Cmd("frame 0; scale histequ; scale mode minmax")
+               return
+            except IOError:
+               print "waiting for ds9...\r",
+               time.sleep(0.5)
+            else:
+               break
+
+         print "                  \r",
+         sys.stdout.flush();
+
+      raise Ds9Error
+
+def mtv(data, meta = None, frame = 0, init = 1, WCS = ""):
+   """Display an OTA or Cell on a DS9 display"""
+	
+   if frame == None:
+      return
+   
+   if init:
+      for i in range(0,3):
+         try:
+            initDS9(i == 0)
+         except IOError:
+            print "waiting for ds9...\r", ; sys.stdout.flush();
+            time.sleep(0.5)
+         else:
+            break
+         
+   ds9Cmd("frame %d" % frame)
+
+   if 1:
+      xpa_cmd = "xpaset ds9 fits"
+      pfd = os.popen(xpa_cmd, "w")
+   else:
+      pfd = file("foo.fits", "w")
+      
+   xpa.rhlWriteFits(pfd.fileno(), data, meta, WCS)
+
+   try:
+       pass
+   except:
+       print "Error"
+       pass
+
+   try:
+       pfd.close()
+   except:
+       pass
+#
+# Graphics commands
+#
+def erase(frame = 0):
+   """Erase the specified DS9 frame"""
+   if frame == None:
+      return
+
+   ds9Cmd("frame %d; regions delete all" % frame)
+
+def dot(symb, c, r, frame = 0, size = 2, ctype = 'green'):
+   """Draw a symbol onto the specfied DS9 frame at (row,col) = (r,c) [0-based coordinates]
+Possible values are:
+	+	Draw a +
+	x	Draw an x
+        o	Draw a circle
+Any other value is interpreted as a string to be drawn
+"""
+   if frame == None:
+      return
+
+   cmd = "frame %d; regions physical; " % frame
+   r += 1; c += 1;                      # ds9 uses 1-based coordinates
+   if (symb == '+'):
+      cmd += 'regions line %g %g %g %g # color=%s; ' % (c, r+size, c, r-size, ctype)
+      cmd += 'regions line %g %g %g %g ' % (c-size, r, c+size, r)
+   elif (symb == 'x'):
+      size = size/math.sqrt(2)
+      cmd += 'regions line %g %g %g %g # color=%s; ; ' % (c+size, r+size, c-size, r-size, ctype)
+      cmd += 'regions line %g %g %g %g ' % (c-size, r+size, c+size, r-size)
+   elif (symb == 'o'):
+      cmd += 'regions circle %g %g %g ' % (c, r, size)
+   else:
+      cmd += 'regions text %g %g \"%s\"' % (c, r, symb)
+
+   cmd += ' # color=%s' % ctype
+
+   ds9Cmd(cmd)
+
+def line(points, frame = 0, symbs = False, ctype = 'green'):
+   """Draw a set of symbols or connect the points, a list of (row,col)
+If symbs is True, draw points at the specified points using the desired symbol,
+otherwise connect the dots.  Ctype is the name of a colour (e.g. 'red')"""
+   
+   if frame == None:
+      return
+
+   if symbs:
+      for (r, c) in points:
+         dot(symbs, c, r, frame = frame, size = 0.5, ctype = ctype)
+   else:
+      cmd = "frame %d; regions image; regions line " % (frame)
+
+      for (r, c) in points:
+         r += 1; c += 1;                   # ds9 uses 1-based coordinates
+         cmd += '%g %g ' % (c, r)
+         
+      cmd += ' # color=%s' % ctype
+         
+      ds9Cmd(cmd)
+#
+# Zoom and Pan
+#
+def zoom(zoomfac = None, rowc = None, colc = None, frame = 0):
+   """Zoom frame by specified amount, optionally panning also"""
+
+   if frame == None:
+      return
+
+   if (rowc and not colc) or (not rowc and colc):
+      raise Ds9Error, "Please specify row and column center to pan about"
+   
+   if zoomfac == None and rowc == None:
+      zoomfac = 2
+
+   cmd = ""
+   if zoomfac != None:
+      cmd += "zoom to %d; " % zoomfac
+
+   if rowc != None:
+      cmd += "pan to %g %g physical; " % (colc + 1, rowc + 1) # ds9 is 1-indexed. Grrr
+
+   ds9Cmd(cmd)
Index: /tags/sj_tags/sj_root_20080929/pois/python/pois.py
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/python/pois.py	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/python/pois.py	(revision 22322)
@@ -0,0 +1,360 @@
+#
+# POIS: Pan-STARRS (or Pricey's) Optimal Image Subtraction
+#
+import os, re, sys, time
+import ds9
+import ps
+import psLibSwig as psLib; from psLibSwig import psTraceSetLevel, psTrace
+import poisSwig as pois
+
+def getTime():
+    return time.clock()
+
+def displayImage(image, frame = 0, caption = None, ctype = "green", init = False):
+    global init_display
+    if init_display:
+        init = True
+        
+    ds9.mtv(image, init = init, frame = frame)
+    if caption:
+        ds9.dot(caption, 10, image.numRows + 5, ctype = ctype, frame = frame)
+
+    init_display = False
+
+def run(showLeaks = True, *args, **nargs):
+    #"""Wrapper for run, with a check on memory leaks after out-of-scope
+    #objects have been garbage collected."""
+    #
+    # Only look for leaks of memory allocated after this point
+    #
+    id0 = psLib.psMemGetId()
+    #
+    # Do the work
+    #
+    value = _run(*args, **nargs)
+    #
+    # Look for otherwise-undetected problems
+    #
+    ps.raisePsError()
+
+    nleak = psLib.psMemCheckLeaks(id0, None, None, False)
+    if nleak:
+        if showLeaks != False:
+            ps.memCheckLeaks(sys.stderr, id0 = id0, addresses = True)
+            
+        raise IOError, "Memory Leak (%d pointers); id0 = %d" % (nleak, id0)
+
+    return value
+
+def _run(fileDir = ".", refFile = "test1.fits", inFile = "test2.fits", outFile=None,
+         section = "[100:500,100:500]",
+         testing = False, verbose = 0, psLib_verbose = 0,
+         display = {}, setTrace = True):
+    """POIS: Pan-STARRS (or Pricey's) Optimal Image Subtraction.
+
+Attempts to fit for a general convolution kernel that will match the PSFs between two images.
+Arguments:
+   fileDir	Directory for input and output files (default: .)
+   refFile      Reference image (default: test1.fits)
+   inFile       File to be analysed (default: test2.fits)
+   outFile	Write output to this file (default: none)
+   section	Section of datafiles to process, or None (=> all) (default: [100:500,100:500])
+
+   testing	Write some extra test information/files (default: False)
+   verbose      Verbosity passed to psTrace (default: 0).0, psLib_verbose = 0,
+   setTrace	Should the code fiddle with trace levels? (default: True)
+
+   display	Dictionary of things that can be displayed; use display="help" for help (default: {})
+
+E.g.
+   pois.run(fileDir="data", verbose=3, display={'stamps':1, 'reference':1, 'subtracted':1})
+"""
+
+    startTime = getTime();
+
+    if setTrace:
+        psTraceSetLevel(".", 0)
+        psTraceSetLevel("pois", verbose)
+        psTraceSetLevel("pois.config", verbose)
+        psTraceSetLevel("pois.time", verbose + 2)
+        psTraceSetLevel("pois.solution", 0)
+        psTraceSetLevel("pois.kernelBasisFunctions", verbose)
+        psTraceSetLevel("pois.calculateEquation", verbose)
+        psTraceSetLevel("pois.convolveImage", verbose)
+        psTraceSetLevel("pois.findStamps", verbose)
+        psTraceSetLevel("pois.parseConfig", verbose)
+        psTraceSetLevel("pois.makeMask", verbose)
+        psTraceSetLevel("pois.extractKernel", verbose)
+        psTraceSetLevel("pois.calculateDeviations", verbose)
+
+        psTraceSetLevel("ps.utils", psLib_verbose)
+
+        if verbose > 3:
+            print "Trace levels:"
+            psLib.psTracePrintLevels()
+
+    # Set logging level
+    psLib.psLogSetDestination("dest:stderr")
+
+    #
+    # Set missing keys in display
+    keys = ['all', 'pause']            # permitted keywords in display
+    subkeys = ['convolved', 'input', 'kernel', 'mask', 'reference', 'stamps', 'subtracted']
+
+    if display == "help":
+        print """Usage:
+        display={'key1':1, 'key2':True, 'key3':0, ...}   (1==True, 0==False)
+Keys:
+    all		Display everything that can be displayed
+    pause	Pause after every iteration over the stamp list
+
+    convolved	Show the kernel-convolved reference image
+    input	Show the input image
+    kernel	Show the convolution kernel
+    mask	Show the image mask
+    reference	Show the reference image
+    stamps	Overlay the reference image with the centroids of stamp stars (circle bad ones in red)
+    subtracted	Show the subtracted image
+        """
+        return            
+
+    for k in (keys + subkeys):
+        if not k in display:
+            display[k] = False
+
+    # Are illegal keys present in display[]?
+    illegal = []
+    for k in display.keys():
+        if not k in keys + subkeys:
+            illegal.append(k)
+
+    if illegal:
+        raise ("Unrecognised keywords %s are present in display[]" % (illegal.__str__()))
+
+    # Deal with display values
+    if display['all']:
+        for k in subkeys:
+            display[k] = True
+
+    if display['stamps']:
+        display['input'] = True
+
+    global init_display;
+    init_display = True            # have we initialised the display?
+
+    # Set desired variables from config file
+    if not re.search("/$", fileDir):
+        fileDir += "/"
+
+    config = pois.poisConfigAlloc()      # Configuration values
+    config.inFile = fileDir + inFile
+    config.refFile = fileDir + refFile
+    try:
+        config.outFile = fileDir + outFile
+    except:
+        config.outFile = outFile
+
+    # Read inputs
+    imageRegion = psLib.psRegionAlloc(0, 0, 0, 0)
+
+    fd = psLib.psFitsAlloc(config.refFile)
+    refImage = psLib.psFitsReadImage(None, fd, imageRegion, 0)
+    del fd
+    psTrace("pois", 3, ("Read %s: %dx%d\n" % (config.refFile, refImage.numCols, refImage.numRows)))
+
+    fd = psLib.psFitsAlloc(config.inFile)
+    inImage = psLib.psFitsReadImage(None, fd, imageRegion, 0)
+    del fd
+    psTrace("pois", 3, ("Read %s: %dx%d\n" % (config.inFile, inImage.numCols, inImage.numRows)))
+    
+    del imageRegion
+
+    if section:
+        inImage = psLib.psImageSubsection(inImage, section)
+        refImage = psLib.psImageSubsection(refImage, section)
+
+    if display['reference']:
+        displayImage(refImage, 1, "Reference Image", "red")
+    if display['input']:
+        displayImage(inImage, 2, "Input Image", "red")
+
+    if (refImage.numCols != inImage.numCols) or (refImage.numRows != inImage.numRows):
+	raise IOError, \
+              ("Reference and input images must have the same dimensions: %dx%d vs %dx%d\n" %
+               (refImage.numCols, refImage.numRows, inImage.numCols, inImage.numRows))
+
+    config.xImage = refImage.numCols
+    config.yImage = refImage.numRows
+
+    psTrace("pois.time", 1, ("Inputs read at %f sec\n" % (getTime() - startTime)))
+
+    # Add a background offset to get the image all above zero
+    # An image that goes negative (e.g., a background-subtracted image) produces
+    # horrible results --- the matrix goes NaN because the noise is determined
+    # as the square root of the value.
+    if config.background != 0.0:
+            psBinaryOp(refImage, refImage, "+", psScalarAlloc(config.background, PS_TYPE_F32))
+            psBinaryOp(inImage, inImage, "+", psScalarAlloc(config.background, PS_TYPE_F32))
+
+    # Generate the kernel parameters
+    kernelBasisFunctions = pois.poisKernelBasisFunctions(config)
+
+    # Generate a mask
+    mask = pois.poisMakeMask(None, refImage, inImage, config)
+
+    stamps = None                       # Array of stamps
+    stampMask = None                    # Mask for stamps
+    solution = None                     # Solution vector
+
+    psLib.psMemCheckCorruption(1)
+
+    # Iterate for a good solution
+    for iterNum in range(0, config.numIter):
+	psTrace("pois", 1, ("Iteration %d...\n" % (iterNum)))
+
+	# Find stamps
+        stamps = pois.poisFindStamps(stamps, refImage, mask, config);
+        
+	numStamps = 0;
+        for s in range(0, stamps.n):
+	    stamp = pois.poisStamp_Cast(stamps.get_data(s)) # Stamp of interest
+            if stamp:
+                if stamp.status == pois.POIS_STAMP_BAD:
+                    ctype = "red"; dotType = "x"
+                else:
+                    ctype = "green"; dotType = "+"
+                    numStamps += 1
+                    
+                if display['stamps']:
+                    ds9.dot(dotType, stamp.x, stamp.y, size = 5, ctype = ctype, frame = 1)
+
+	psTrace("pois.time", 1, ("%d stamps found at %f sec\n" % (numStamps, getTime() - startTime)))
+        if numStamps == 0:
+            raise IOError, "No stamps found.  Check input parameters"
+
+	# Calculate equation
+	pois.poisCalculateEquation(stamps, refImage, inImage, kernelBasisFunctions, config)
+	psTrace("pois.time", 1, ("Equation calculated at %f sec\n" % (getTime() - startTime)))
+
+	# Solve the equation
+	solution = pois.poisSolveEquation(solution, stamps, config)
+	psTrace("pois.time", 1, ("Matrix equation solved at %f sec\n" % (getTime() - startTime)))
+
+        if testing:
+            # Print solution
+            print "Solution:"
+            for i in range(0, solution.n - 1):
+                kernel = kernelBasisFunctions.get_data(i) # The i-th kernel basis function
+                kernel = pois.poisKernelBasis_Cast(kernel)
+                print "%d: (%d,%d) x^%d y^%d - %f" % \
+                      (i, kernel.u, kernel.v, kernel.xOrder, kernel.yOrder, solution.get_data_F64()[i])
+
+
+        if testing or display['kernel']:
+            kernelImage = pois.poisExtractKernel(solution, kernelBasisFunctions, 0.0, 0.0, config)
+
+            if display['kernel']:
+                displayImage(kernelImage, 5, "Kernel")
+
+            if testing:
+                # Save the kernel postage stamp
+                kernelName = "kernel%d.fits" % iterNum
+                kernelFile = psLib.psFitsAlloc(kernelName)
+                psLib.psFitsWriteImage(kernelFile, None, kernelImage, 0, None)
+                psTrace("pois", 2, ("Kernel written to %s\n" %kernelName))
+
+	# Calculate deviations for the stamps
+	deviations = pois.poisCalculateDeviations(None, stamps, refImage, inImage, mask,
+						       kernelBasisFunctions, solution, config)
+        # Have we converged?
+        rejected = pois.poisRejectStamps(stamps, mask, deviations, config)
+
+        if rejected == None:
+            break
+        
+        if display['stamps']:
+            iter = psLib.psListIteratorAlloc(rejected, psLib.PS_LIST_HEAD, False)
+            while True:
+                stamp = pois.poisStamp_Cast(psLib.psListGetAndIncrement(iter))
+                if not stamp:
+                    break
+                ds9.dot("o", stamp.x, stamp.y, size = 5, ctype = "red", frame = 1)
+
+        if display['pause']:
+            if raw_input("Iteration %d completed: continue? (q to abort) " % iterNum) == "q":
+                raise "XXX"
+
+    # If there was rejection on the last round, re-solve the equation using only the good stamps
+    if iterNum == config.numIter:
+        psTrace("pois", 2, ("Failed to converge in %d iterations\n" % (config.numIter)))
+	solution = pois.poisSolveEquation(solution, stamps, config);
+
+    # Clear the mask for bad stamp areas
+    if display['mask']:
+        displayImage(mask, 0, "Stamps mask")
+
+    psLib.psBinaryOp(mask, mask, "&", psLib.psScalarAlloc(~pois.POIS_MASK_STAMP, psLib.PS_TYPE_U8))
+
+    # Convolve the input image
+    convImage = pois.poisConvolveImage(refImage, mask, solution, kernelBasisFunctions, config)
+    psTrace("pois.time", 1, ("Convolution completed at %f sec\n" % (getTime() - startTime)))
+
+    if testing and config.outFile:
+        # Save the convolved image
+        convName = "%s.conv" % config.outFile
+        convFile = psLib.psFitsAlloc(convName)
+        psLib.psFitsWriteImage(convFile, None, convImage, 0, None)
+        psTrace("pois", 2, ("Convolved image written to %s\n" % (convName)))
+
+    if display['convolved']:
+        displayImage(convImage, 4, "Convolved image", "red")
+
+    # Do the subtraction
+    subImage = psLib.psImageAlloc(config.xImage, config.yImage, psLib.PS_TYPE_F32)
+    psLib.psBinaryOp(subImage, inImage, "-", convImage)
+    psTrace("pois.time", 1, ("Subtraction completed at %f sec\n" % (getTime() - startTime)))
+
+    if testing:
+        stats = psLib.psStatsAlloc(psLib.PS_STAT_SAMPLE_MEAN | psLib.PS_STAT_SAMPLE_STDEV)
+	psLib.psImageStats(stats, subImage, mask, 0)
+	print "Subtracted image statistics: %f %f\n" % (stats.sampleMean, stats.sampleStdev)
+	#psFree(stats);
+
+    # Mask out the edges
+    X0 = config.xKernel; X1 = mask.numCols - config.xKernel # non-masked area is (X0,Y0) -- (X1-1, Y1-1)
+    Y0 = config.yKernel; Y1 = mask.numRows - config.yKernel
+
+    tmp = psLib.psImageSubset(mask, 0, 0, mask.numCols, Y0)
+    psLib.psBinaryOp(tmp, tmp, "|", psLib.psScalarAlloc(pois.POIS_MASK_BAD, psLib.PS_TYPE_U8))
+
+    tmp = psLib.psImageSubset(mask, 0, Y1, mask.numCols, mask.numRows)
+    psLib.psBinaryOp(tmp, tmp, "|", psLib.psScalarAlloc(pois.POIS_MASK_BAD, psLib.PS_TYPE_U8))
+
+    tmp = psLib.psImageSubset(mask, 0, Y0, X0, Y1)
+    psLib.psBinaryOp(tmp, tmp, "|", psLib.psScalarAlloc(pois.POIS_MASK_BAD, psLib.PS_TYPE_U8))
+
+    tmp = psLib.psImageSubset(mask, X1, Y0, mask.numCols, Y1)
+    psLib.psBinaryOp(tmp, tmp, "|", psLib.psScalarAlloc(pois.POIS_MASK_BAD, psLib.PS_TYPE_U8))
+    del tmp
+
+    pois.poisImageSetValInMask(subImage, subImage, mask, 0, pois.POIS_MASK_BAD)
+
+    if False:
+        for y in range(config.yKernel, subImage.numRows - config.yKernel):
+            for x in range(config.xKernel, subImage.numCols - config.xKernel):
+                if mask.data.U8[y][x]:
+                    psLib.subImage.data.F32[y][x] = 0.0
+
+
+    if display['subtracted']:
+        displayImage(subImage, 3, "Subtracted image", "red")
+
+    if config.outFile:
+        subFile = psLib.psFitsAlloc(config.outFile)
+        psLib.psFitsWriteImage(subFile, None, subImage, 0, None)
+        psTrace("pois", 2, ("Subtracted image written to %s\n" % (config.outFile)))
+
+    psTrace("pois.time", 1, ("I/O completed at %f sec\n" % (getTime() - startTime)))
+
+if not run.__doc__:
+    run.__doc__ = _run.__doc__
Index: /tags/sj_tags/sj_root_20080929/pois/python/ps.py
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/python/ps.py	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/python/ps.py	(revision 22322)
@@ -0,0 +1,151 @@
+import psLibSwig as psLib
+import sys
+
+def logcntl(what = "NM"):
+    """Control logging; disable if what == \"\""""
+    
+    if what == "":
+        psLib.psLogSetDestination("none")   # turn off logging
+    else:
+        psLib.psLogSetDestination("dest:stderr")
+        psLib.psLogSetFormat(what)
+
+logcntl("NM")
+
+class PsError(StandardError):
+    """An error signalled via a call to psError"""
+
+def raisePsError(clear = True):
+    """Raise the most recently signalled psError (the psError stack is cleared if clear is True)"""
+    
+    err = psLib.psErrorLast()
+    if err.code != 0:
+        if clear:                       # clear the error?
+            psLib.psErrorClear()
+        raise PsError, "psError was called without an error return: (%s) %s" % (err.name, err.msg)
+
+def errStack():
+    """Print the current error stack"""
+    
+    i = 0;
+    while 1:
+        err = psLib.psErrorGetForSwig(i)
+        if err == None :
+            return
+        
+        errString = psLib.psErrorCodeString(err.code)
+        if not errString:
+            errString = "(unknown code: %d)" % err.code
+        print "%s\t%-40s %s" %(err.name, errString + ':', err.msg)
+        i += 1
+
+def printMD(meta, match=None, file=sys.stdout, hdr=""):
+    """Print a set of psMetadata"""
+
+    for i in range(0,meta.list.size):
+        md = psLib.psMetadataGet(meta, i)
+
+        if md.type == psLib.PS_META_STR:
+            fmt = "%s"
+        elif md.type == psLib.PS_META_S32:
+            fmt = "%d"
+        elif md.type == psLib.PS_META_F32 or md.type == psLib.PS_META_F64:
+            fmt = "%g"
+        psLib.psMetadataItemPrint(file, md.name + " " + fmt + "\n", md)
+
+def memCheckLeaks(fd = None, id0 = 0, getList = 0, unique = 0, addresses = 0, persistent = False):
+    """Check for memory leaks.
+
+    If file (an fd or a filename) is provided, write the list of
+    leaked pointers to it.  If id0 is specified, only check for leaked
+    memory allocated after that memory id. 
+
+    If getList is true, return a list of tuples for each leaked block:
+        (isMagic0, id, file, lineNo, refCounter, isMagic)
+
+    If unique is true, print/return a list of places where memory is leaking, along with
+    the number of pointers leaked at each point.  If addresses is true, include the addresses
+    of the pointers
+    """
+
+    if (unique and addresses):
+        raise "Please choose unique _or_ addresses"
+
+    if isinstance(fd, str):
+        fd = file(fd, "w")
+
+    if addresses or getList or unique:
+        arr = []
+    else:
+        arr = None
+
+    if addresses or unique:
+        nleak = psLib.psMemCheckLeaks(id0, arr, None, persistent)
+    else:
+        nleak = psLib.psMemCheckLeaks(id0, arr, fd, persistent)
+
+    if fd == None and not (addresses or unique):
+        if nleak == 0:
+            print "No leaks"
+        else:
+            print "%d leaked pointers" % (nleak)
+
+    if addresses or unique:
+        un = {}
+        for el in arr:
+            if addresses:
+                key = "%s:%d:%d" % (el[2], el[3], el[6])
+                un[key] = el[1]
+            else:
+                key = "%s:%d:" % (el[2], el[3])
+
+                if un.get(key):
+                    un[key] += 1
+                else:
+                    un[key] = 1
+
+        keys = un.keys()
+
+        if unique:
+            def cmpKeys(a,b):               # sort keys by file then linenumber
+                a=a.split(':')
+                b=b.split(':')
+                c= cmp(a[0],b[0])
+                if c:
+                    return c
+                else:
+                    return int(a[1]) - int(b[1])
+        else:
+            def cmpKeys(a,b):               # sort keys by ID
+                return un[a] - un[b]
+
+        keys.sort(cmpKeys)
+
+        arr = []
+        for el in keys:
+            arr.append((el, un[el]))
+
+        if fd == None and not getList:
+            fd = sys.stdout
+
+        if fd != None:
+            if len(arr) == 0:
+                fd.write("No leaks\n")
+            else:
+                if unique:
+                    fd.write("%-20s Nptr\n" % "file:line")
+                    for el in arr:
+                        fd.write("%-20s %d\n" % el)
+                else:
+                    fd.write("%-15s %4s   %-10s   ID\n" % ("file", "line", "ADDR"))
+                    for el in arr:
+                        el = el[0].split(":") + [el[1]]
+                        fd.write("%-15s %4s   0x%08x   %d\n" % (el[0], el[1], int(el[2]), el[3]))
+
+    try:
+        if fd != sys.stderr and fd != sys.stdout:
+            fd.close();
+    except: pass
+
+    if getList:
+        return arr
Index: /tags/sj_tags/sj_root_20080929/pois/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/.cvsignore	(revision 22322)
@@ -0,0 +1,8 @@
+Makefile Makefile.in
+.deps .libs
+errorCodes.c poisErrorCodes.h
+*.la *.lo
+pois
+config.h
+config.h.in
+stamp-h1
Index: /tags/sj_tags/sj_root_20080929/pois/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/Makefile.am	(revision 22322)
@@ -0,0 +1,58 @@
+AM_CFLAGS = $(PSLIB_CFLAGS)
+AM_LDFLAGS = $(PSLIB_LIBS)
+
+pois_CPPFLAGS = $(PSLIB_CFLAGS)
+pois_LDFLAGS = $(PSLIB_LIBS)
+
+noinst_HEADERS = pois.h poisErrorCodes.h
+bin_PROGRAMS = pois
+
+BUILT_SOURCES = \
+	poisErrorCodes.h errorCodes.c
+
+pois_SOURCES = \
+	pois.c \
+	poisAddPenalty.c \
+	poisCalculateDeviations.c \
+	poisCalculateEquation.c \
+	poisCheckKernel.c \
+	poisConvolveImage.c \
+	poisExtractKernel.c \
+	poisFindStamps.c \
+	poisKernelBasisFunctions.c \
+	poisMakeMask.c \
+	poisReadStamps.c \
+	poisRejectStamps.c \
+	poisParseConfig.c \
+	poisSolveEquation.c \
+	poisStamp.c \
+	errorCodes.c
+###	poisMaskOps.c
+
+#
+# Error codes
+#
+poisErrorCodes.h : errorCodes.dat errorCodes-skl.h
+	$(PERL) parseErrorCodes.pl --data=errorCodes.dat --outfile=poisErrorCodes.h \
+			errorCodes-skl.h
+errorCodes.c : errorCodes.dat errorCodes-skl.c poisErrorCodes.h
+	$(PERL) parseErrorCodes.pl --data=errorCodes.dat --outfile=errorCodes.c \
+			errorCodes-skl.c
+
+EXTRA_DIST = \
+	parseErrorCodes.pl \
+	errorCodes.dat \
+	errorCodes-skl.h \
+	errorCodes-skl.c \
+	poisErrorCodes.h
+
+#
+# test program
+#
+test: pois test1.fits test2.fits
+	-$(RM) testout.fits stamp*.fits kernel*.fits
+	./pois -v test1.fits test2.fits testout.fits
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/pois/src/errorCodes-skl.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/errorCodes-skl.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/errorCodes-skl.c	(revision 22322)
@@ -0,0 +1,36 @@
+/*
+ * The lines between
+ *   //~Start TEMPLATE
+ * and
+ *   //~End
+ * will be machine generated from errorCodes.dat
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "pslib.h"
+#include "poisErrorCodes.h"
+
+void poisErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { POIS_ERR_BASE, "First value we use; lower values belong to psLib" },
+       //~Start       {POIS_ERR_$1, "$2"},
+       {POIS_ERR_UNKNOWN, "Unknown POIS error code"},
+       {POIS_ERR_NOT_IMPLEMENTED, "Desired feature is not yet implemented"},
+       {POIS_ERR_FITS, "Problem in FITS I/O"},
+       {POIS_ERR_FITS_WCS, "Error interpreting FITS WCS information"},
+       //~End
+    };
+    static int nerror = POIS_ERR_NERROR - POIS_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);                     /* it's on the internal list */
+    }
+    nerror = 0;                                 // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/errorCodes-skl.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/errorCodes-skl.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/errorCodes-skl.h	(revision 22322)
@@ -0,0 +1,23 @@
+#if !defined(POIS_ERROR_CODES_H)
+#define POIS_ERROR_CODES_H
+/*
+ * The lines between
+ *   //~Start TEMPLATE
+ * and
+ *   //~End
+ * will be machine generated from errorCodes.dat
+ */
+typedef enum {
+    POIS_ERR_BASE = 512,
+    //~Start    POIS_ERR_$1,
+    POIS_ERR_UNKNOWN,
+    POIS_ERR_NOT_IMPLEMENTED,
+    POIS_ERR_FITS,
+    POIS_ERR_FITS_WCS,
+    //~End
+    POIS_ERR_NERROR
+} poisErrorCode;
+
+void poisErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/pois/src/errorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/errorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/errorCodes.dat	(revision 22322)
@@ -0,0 +1,11 @@
+#
+# This file is used to generate poisErrorClasses.h
+#
+BASE = 512		First value we use; lower values belong to psLib
+UNKNOWN			Unknown POIS error code
+NOT_IMPLEMENTED		Desired feature is not yet implemented
+FITS			Problem in FITS I/O
+FITS_WCS		Error interpreting FITS WCS information
+SWIG			Problem in SWIG bindings
+UNSUPPORTED_TYPE	The datatype is not supported for this operation
+
Index: /tags/sj_tags/sj_root_20080929/pois/src/parseErrorCodes.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/parseErrorCodes.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/parseErrorCodes.pl	(revision 22322)
@@ -0,0 +1,109 @@
+#!/usr/bin/perl
+
+# Provides functions for handling long command line options
+use Getopt::Long;
+
+my @ErrorCodes        = ();
+my @ErrorDescriptions = ();
+
+my $data = "psErrorCodes.dat";
+
+# Assign variables based on the presence of command line options to the script
+GetOptions(
+    "data=s"  => \$data,
+    "outfile=s"  => \$outputfile,
+    "verbose" => \$verbose,
+    "help"    => \$help
+	   )  or exit(1);
+
+if ($help) {
+    print "Usage: parseErrorCodes ", "[--data=$data] ", "[--outfile=$file] ", "[--help] ",
+      "[--verbose] [filename [filename ...]]\n\n";
+    exit(0);
+}
+
+if(defined($outputfile)) {
+   if (@ARGV > 1) {
+      die "You may only specify one input filename if you use --outfile\n";
+   }
+}
+
+print "Using data file '$data'\n" if $verbose;
+unless ( open( DATAFILE, "<", $data ) ) {
+    die "Can not open data file $data.";
+}
+
+print "Datafile:\n" if $verbose;
+while (<DATAFILE>) {
+    chop;
+    if (/^\s*\#/) {
+        print "C $_\n" if $verbose;
+    }
+    else {
+        if (/^\s*(\w+)\s+([\%\w].*)/) {
+            my $ErrorCode        = $1;
+            my $ErrorDescription = $2;
+            print "  $ErrorCode: '$ErrorDescription'\n" if $verbose;
+            push( @ErrorCodes,        $ErrorCode );
+            push( @ErrorDescriptions, $ErrorDescription );
+        } else {
+            print "I $_\n" if $verbose;
+        }
+    }
+}
+close(DATAFILE);
+
+my $found = $#ErrorCodes + 1;
+print "\nFound $found error codes.\n" if $verbose;
+
+foreach (@ARGV) {
+    my $filename = $_;
+    my @result   = ();
+
+    die "Failed to open input file '$filename'"
+      if !open( INFILE, "<", $filename );
+
+    print "\nOutput File:\n" if $verbose;
+    while (<INFILE>) {
+        chop;
+        push( @result, $_ );
+        if (/^\s*\/\/~Start(.*)$/) {
+            $line = $1;
+            for ( $n = 0 ; $n < $found ; $n++ ) {
+                $_ = $line;
+                s/\$1/$ErrorCodes[$n]/g;
+                s/\$2/$ErrorDescriptions[$n]/g;
+                s/\$n/$n/g;
+                push( @result, $_ );
+                print "$_\n" if $verbose;
+            }
+
+            $break = 0;
+            while ( ( $break == 0 ) && ( $_ = <INFILE> ) ) {
+                if (/^\s*\/\/~End/) {
+                    $break = 1;
+                }
+            }
+            chop;
+            push( @result, $_ );
+        }
+    }
+
+    close(INFILE);
+
+    if (defined($outputfile)) {
+       $ofile = $outputfile;
+    } else {
+       $ofile = $filename;
+    }
+    die "Failed to overwrite input file"
+	if !open( OUTFILE, ">", $ofile );
+
+    foreach (@result) {
+        print OUTFILE "$_\n";
+        print "$_\n" if $verbose;
+    }
+
+    close(OUTFILE);
+
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/pois.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/pois.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/pois.c	(revision 22322)
@@ -0,0 +1,354 @@
+// POIS: Pan-STARRS (or Pricey's) Optimal Image Subtraction
+//
+// Attempts to fit for a general convolution kernel that will match the PSFs between two images.
+//
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include "pslib.h"
+#include "pois.h"
+
+#define MAXCHAR 80
+
+// Gets the current time.
+double getTime(void)
+{
+    struct timeval tv;
+
+    gettimeofday(&tv, NULL);
+    return(tv.tv_sec + 1.e-6 * tv.tv_usec);
+}
+
+
+int main(int argc, char **argv)
+{
+    double startTime;
+    startTime = getTime();
+
+    // Set trace levels
+    (void)psTraceSetLevel(".", 0);
+
+    // Parse the command line
+    poisConfig *config = poisParseConfig(argc, argv); // Configuration values
+
+    if (config->verbose) {
+        (void)psTraceSetLevel("pois", 10);
+        (void)psTraceSetLevel("pois.config", 10);
+        (void)psTraceSetLevel("pois.time", 10);
+        (void)psTraceSetLevel("pois.solution", 0);
+        (void)psTraceSetLevel("pois.kernelBasisFunctions", 10);
+        (void)psTraceSetLevel("pois.calculateEquation", 10);
+        (void)psTraceSetLevel("pois.convolveImage", 10);
+        (void)psTraceSetLevel("pois.findStamps", 10);
+        (void)psTraceSetLevel("pois.parseConfig", 10);
+        (void)psTraceSetLevel("pois.makeMask", 10);
+        (void)psTraceSetLevel("pois.extractKernel", 10);
+        (void)psTraceSetLevel("pois.calculateDeviations", 10);
+        (void)psTraceSetLevel("pois.checkKernel", 10);
+        if (config->stampFile) {
+            (void)psTraceSetLevel("pois.checkStamp", 10);
+        } else {
+            (void)psTraceSetLevel("pois.checkStamp", 0);
+        }
+        // Set logging level
+        psLogSetLevel(9);
+    }
+
+    // Read inputs
+    psMetadata *header = NULL;          // Header for output image
+
+    psRegion imageRegion = {0, 0, 0, 0};
+    psFits *reference = psFitsOpen(config->refFile, "r");
+    if (config->reverse) {
+        header = psFitsReadHeader(NULL, reference);
+    }
+    psImage *refImage = psFitsReadImage(reference, imageRegion, 0);
+    if (refImage == NULL) {
+        psErrorStackPrint(stderr, "Fatal error: unable to read %s\n", config->refFile);
+        exit(EXIT_FAILURE);
+    }
+    psTrace("pois", 3, "Read %s: %dx%d\n", config->refFile, refImage->numCols, refImage->numRows);
+    psFitsClose(reference);
+    // Convert to 32-bit floating point, in necessary
+    if (refImage->type.type != PS_TYPE_F32) {
+        psTrace("pois", 3, "Converting %s to floating point in memory....\n", config->refFile);
+        psImage *temp = psImageCopy(NULL, refImage, PS_TYPE_F32);
+        psFree(refImage);
+        refImage = temp;
+    }
+
+    psFits *input = psFitsOpen(config->inFile, "r");
+    if (! config->reverse) {
+        header = psFitsReadHeader(NULL, input);
+    }
+    psImage *inImage = psFitsReadImage(input, imageRegion, 0);
+    if (inImage == NULL) {
+        psErrorStackPrint(stderr, "Fatal error: unable to read %s\n", config->inFile);
+        exit(EXIT_FAILURE);
+    }
+    psTrace("pois", 3, "Read %s: %dx%d\n", config->inFile, inImage->numCols, inImage->numRows);
+    psFitsClose(input);
+    // Convert to 32-bit floating point, in necessary
+    if (inImage->type.type != PS_TYPE_F32) {
+        psTrace("pois", 3, "Converting %s to floating point in memory....\n", config->inFile);
+        psImage *temp = psImageCopy(NULL, inImage, PS_TYPE_F32);
+        psFree(inImage);
+        inImage = temp;
+    }
+
+    if ((refImage->numCols != inImage->numCols) || (refImage->numRows != inImage->numRows)) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Reference and input images must have the same dimensions: %dx%d vs %dx%d\n",
+                refImage->numCols, refImage->numRows, inImage->numCols, inImage->numRows);
+        exit(EXIT_FAILURE);
+    }
+    config->xImage = refImage->numCols;
+    config->yImage = refImage->numRows;
+
+    psTrace("pois.time", 1, "Inputs read at %f sec\n", getTime() - startTime);
+
+    // Add a background offset to get the image all above zero
+    // An image that goes negative (e.g., a background-subtracted image) produces
+    // horrible results --- the matrix goes NaN because the noise is determined
+    // as the square root of the value.
+    if (config->background != 0.0) {
+        (void)psBinaryOp(refImage, refImage, "+", psScalarAlloc(config->background, PS_TYPE_F32));
+        (void)psBinaryOp(inImage, inImage, "+", psScalarAlloc(config->background, PS_TYPE_F32));
+    }
+
+    // Generate the kernel parameters
+    psArray *kernelBasisFunctions = poisKernelBasisFunctions(config);
+
+    // Generate a mask
+    psImage *mask = poisMakeMask(NULL, refImage, inImage, config);
+
+#ifdef TESTING
+    // Write the mask out
+    psFits *maskFile = psFitsOpen("mask.fits", "w");
+    if (!psFitsWriteImage(maskFile, NULL, mask, 0, NULL)) {
+        psErrorStackPrint(stderr, "Unable to write mask: mask.fits\n");
+    }
+    psTrace("pois", 1, "Mask written to mask.fits\n");
+    psFitsClose(maskFile);
+#endif
+
+    psArray *stamps = NULL;             // Array of stamps
+    psVector *solution = NULL;          // Solution vector
+
+    FILE *stampsFP = NULL;              // File pointer for stamps
+    if (config->stampFile) {
+        psTrace("pois", 5, "Opening stamp file, %s\n", config->stampFile);
+        stampsFP = fopen(config->stampFile, "r");
+        if (! stampsFP) {
+            psError(PS_ERR_IO, true, "Unable to open stamps file, %s!\n", config->stampFile);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    // Iterate for a good solution
+    bool badStamps = true;              // Do we have bad stamps, such that we need to continue to iterate?
+    for (int iterNum = 0; iterNum < config->numIter && badStamps; iterNum++) {
+        psTrace("pois", 1, "Iteration %d...\n", iterNum);
+
+        // Find stamps
+        if (config->stampFile) {
+            stamps = poisReadStamps(stamps, &stampsFP, refImage, mask, config);
+        } else {
+            stamps = poisFindStamps(stamps, refImage, mask, config);
+        }
+        int numStamps = 0;
+        for (int s = 0; s < stamps->n; s++) {
+            poisStamp *stamp = stamps->data[s]; // Stamp of interest
+            if (stamp->status != POIS_STAMP_BAD) {
+                numStamps++;
+            }
+        }
+        psTrace("pois.time", 1, "%d stamps found at %f sec\n", numStamps, getTime() - startTime);
+        if (numStamps == 0) {
+            fprintf(stderr, "No stamps found.  Check input parameters.\n");
+            exit(EXIT_FAILURE);
+        }
+
+        // Calculate equation
+        (void)poisCalculateEquation(stamps, refImage, inImage, kernelBasisFunctions, config);
+        psTrace("pois.time", 1, "Equation calculated at %f sec\n", getTime() - startTime);
+
+        // Solve the equation
+        solution = poisSolveEquation(solution, stamps, config);
+        psTrace("pois.time", 1, "Matrix equation solved at %f sec\n", getTime() - startTime);
+
+#ifdef TESTING
+        // Print solution
+        psTrace("pois.solution", 5, "Solution:\n");
+        for (int i = 0; i < solution->n - 1; i++) {
+            poisKernelBasis *kernel = kernelBasisFunctions->data[i]; // The i-th kernel basis function
+            psTrace("pois.solution", 7, "%d: (%d,%d) x^%d y^%d --> %f\n", i, kernel->u, kernel->v,
+                    kernel->xOrder, kernel->yOrder, solution->data.F64[i]);
+        }
+#endif
+
+#ifdef TESTING
+        // Save the kernel postage stamp
+        char kernelName[MAXCHAR];               // File name for kernel
+        snprintf(kernelName, MAXCHAR, "kernel%d.fits", iterNum);
+        psImage *kernelImage = poisExtractKernel(solution, kernelBasisFunctions, 0.0, 0.0, config);
+        psFits *kernelFile = psFitsOpen(kernelName, "w");
+        if (!psFitsWriteImage(kernelFile, NULL, kernelImage, 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write kernel: %s\n", kernelName);
+        }
+        psTrace("pois", 1, "Kernel written to %s\n", kernelName);
+        psFitsClose(kernelFile);
+        psFree(kernelImage);
+#endif
+
+        // Calculate deviations for the stamps
+        psVector *deviations = poisCalculateDeviations(NULL, stamps, refImage, inImage, mask,
+                                                       kernelBasisFunctions, solution, config);
+        badStamps = poisRejectStamps(stamps, mask, deviations, config);
+
+        psFree(deviations);
+    } // Iterating for good solution
+
+    // If there was rejection on the last round, re-solve the equation using only the good stamps
+    if (badStamps) {
+        solution = poisSolveEquation(solution, stamps, config);
+    }
+
+    if (stampsFP) {
+        fclose(stampsFP);
+    }
+
+    // Undo the mask for bad stamp area
+    for (int j = 0; j < mask->numRows; j++) {
+        for (int i = 0; i < mask->numCols; i++) {
+            if (mask->data.U8[j][i] & POIS_MASK_STAMP) {
+                mask->data.U8[j][i] &= ~POIS_MASK_STAMP;
+            }
+        }
+    }
+
+    // Check the result
+    (void)poisCheckKernel(solution, kernelBasisFunctions, config);
+
+    // Convolve the input image
+    psImage *convImage = poisConvolveImage(refImage, mask, solution, kernelBasisFunctions, config);
+    psTrace("pois.time", 1, "Convolution completed at %f sec\n", getTime() - startTime);
+
+#ifdef TESTING
+    // Save the convolved image
+    char convName[MAXCHAR];             // Filename for convolved image
+    snprintf(convName, MAXCHAR, "%s.conv", config->outFile);
+    psFits *convFile = psFitsOpen(convName, "w");
+    if (!psFitsWriteImage(convFile, NULL, convImage, 0, NULL)) {
+        psErrorStackPrint(stderr, "Unable to write convolved image: %s\n", convName);
+    }
+    psFitsClose(convFile);
+    psTrace("pois", 1, "Convolved image written to %s\n", convName);
+#endif
+
+    // Do the subtraction
+    psImage *subImage = psImageAlloc(config->xImage, config->yImage, PS_TYPE_F32);
+    if (config->reverse) {
+        (void)psBinaryOp(subImage, convImage, "-", inImage);
+    } else {
+        (void)psBinaryOp(subImage, inImage, "-", convImage);
+    }
+    psTrace("pois.time", 1, "Subtraction completed at %f sec\n", getTime() - startTime);
+
+#ifdef TESTING
+    {
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        (void)psImageStats(stats, subImage, mask, 0);
+        psTrace("pois", 5, "Subtracted image statistics: %f %f\n", stats->sampleMean, stats->sampleStdev);
+        psFree(stats);
+    }
+#endif
+
+    // Mask out the edges
+    for (int y = config->yKernel; y < subImage->numRows - config->yKernel; y++) {
+        for (int x = config->xKernel; x < subImage->numCols - config->xKernel; x++) {
+
+            for (int x = 0; x < config->xKernel; x++) {
+                subImage->data.F32[y][x] = 0.0;
+            }
+            for (int x = subImage->numCols - config->xKernel; x < subImage->numCols; x++) {
+                subImage->data.F32[y][x] = 0.0;
+            }
+        }
+    }
+    for (int y = 0; y < config->yKernel; y++) {
+        for (int x = 0; x < subImage->numCols; x++) {
+            subImage->data.F32[y][x] = 0.0;
+        }
+    }
+    for (int y = subImage->numRows - config->yKernel; y < subImage->numRows; y++) {
+        for (int x = 0; x < subImage->numCols; x++) {
+            subImage->data.F32[y][x] = 0.0;
+        }
+    }
+
+
+    // Mask out bad pixels
+    for (int y = config->yKernel; y < subImage->numRows - config->yKernel; y++) {
+        for (int x = config->xKernel; x < subImage->numCols - config->xKernel; x++) {
+            if (mask->data.U8[y][x]) {
+                subImage->data.F32[y][x] = 0.0;
+            }
+        }
+    }
+
+    if (config->mask) {
+        char maskName[80];              // Name of mask image
+        sprintf(maskName, "%s.mask", config->outFile);
+        psFits *maskFile = psFitsOpen(maskName, "w");
+        if (!psFitsWriteImage(maskFile, NULL, mask, 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", maskName);
+        }
+        psTrace("pois", 1, "Mask image written to %s\n", maskName);
+        psFitsClose(maskFile);
+    }
+
+
+    int numNaN = psImageClipNaN(subImage, 0.0);
+    if (numNaN > 0) {
+        printf("Masked %d NAN pixels to zero.\n", numNaN);
+    }
+
+    psFits *subFile = psFitsOpen(config->outFile, "w");
+    if (!psFitsWriteImage(subFile, header, subImage, 0, NULL)) {
+        psErrorStackPrint(stderr, "Unable to write subtracted image: %s\n", config->outFile);
+    }
+    psFitsClose(subFile);
+    psTrace("pois", 1, "Subtracted image written to %s\n", config->outFile);
+    psTrace("pois.time", 1, "I/O completed at %f sec\n", getTime() - startTime);
+
+    // Clean up
+    psFree(header);
+    psFree(kernelBasisFunctions);
+    psFree(refImage);
+    psFree(inImage);
+    psFree(convImage);
+    psFree(subImage);
+    psFree(config);
+}
+
Index: /tags/sj_tags/sj_root_20080929/pois/src/pois.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/pois.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/pois.h	(revision 22322)
@@ -0,0 +1,234 @@
+#if !defined(POIS_H)
+#define POIS_H
+
+#include "pslib.h"
+
+#define abs(x) ((x) >= 0 ? (x) : (-(x)))
+
+/************************************************************************************************************/
+
+/* Configuration options */
+typedef struct {
+    int verbose;			// Verbosity level
+    char *refFile;			// Reference image
+    char *inFile;			// Input image filename
+    char *outFile;			// Output convolved image filename
+    int xKernel, yKernel;		// Half the size of the kernel in x and y
+    int xImage, yImage;			// Size of the images in x and y
+    float threshold;			// Threshold for pixel to include in calculation
+    int footprint;			// Footprint size for stamp
+    int nsx, nsy;			// Number of stamps in x and y
+    float refSat, inSat;		// Saturation values for reference and input images
+    float refBad, inBad;		// Bad values for reference and input images
+    float background;			// Value to add to the background in images to get above zero
+    int spatialOrder;			// Order of spatial variations in the kernel
+    int numIter;			// Number of rejection iterations
+    float sigmaRej;			// Limit (in std dev) for rejection
+    float penalty;			// Penalty value
+    char *stampFile;			// Filename for file with stamps
+    bool reverse;			// Reverse the subtraction?
+    bool mask;				// Output the mask?
+} poisConfig;
+
+/************************************************************************************************************/
+
+// A kernel basis function
+typedef struct {
+    int u, v;				// Offset
+    int xOrder, yOrder;			// x^xOrder * y^yOrder
+} poisKernelBasis;
+
+/************************************************************************************************************/
+
+typedef enum {
+    POIS_STAMP_USED,			// Use this stamp
+    POIS_STAMP_RESET,			// This stamp has been rejected --- reset
+    POIS_STAMP_RECALC,			// Having been reset, this stamp needs to be recalculated
+    POIS_STAMP_BAD			// No stamp in this region
+} poisStampStatus;
+
+// A stamp
+typedef struct {
+    int x, y;				// Position
+    psImage *matrix;			// Associated matrix
+    psVector *vector;			// Associated vector
+    poisStampStatus status;		// Status of stamp
+} poisStamp;
+
+/************************************************************************************************************/
+
+// poisStamp.c
+
+// Construct a stamp
+poisStamp *poisStampAlloc(void);
+
+// Destroy a stamp
+void poisStampFree(poisStamp *stamp);
+
+// Return true if the stamp is OK
+bool poisCheckStamp(const psImage *image, // Image to check for threshold
+		    const psImage *mask, // Mask to check for bad pixels
+		    int x, int y,	// Pixel coordinates
+		    const poisConfig *config // Configuration
+    );
+
+/************************************************************************************************************/
+
+typedef enum {
+    POIS_MASK_OK       = 0x00,		// Fine
+    POIS_MASK_BAD      = 0x01,		// Bad pixels
+    POIS_MASK_NEAR_BAD = 0x02,		// Near bad pixels
+    POIS_MASK_STAMP    = 0x04		// Masking a stamp
+} poisMask;
+
+/************************************************************************************************************/
+
+// poisParseConfig.c
+
+// Print usage information
+void help(void);
+
+// Parse the command line arguments
+poisConfig *poisParseConfig(int argc,	// Number of command-line arguments
+			    char **argv	// Command-line arguments
+    );
+
+/************************************************************************************************************/
+
+// poisKernelBasisFunctions.c
+
+// Generate the kernel basis functions for ease of reference in the construction
+psArray *poisKernelBasisFunctions(const poisConfig *config // Configuration
+    );
+
+/************************************************************************************************************/
+
+// poisMakeMask.c
+
+// Generate a mask of bad pixels in the reference and input images --- don't use these pixels for stamps!
+psImage *poisMakeMask(psImage *mask,	// Mask to update, or NULL
+		      const psImage *refImage, // Reference image
+		      const psImage *inImage, // Input image
+		      const poisConfig *config // Configuration
+		      );
+
+/************************************************************************************************************/
+
+// poisFindStamps.c
+
+// Find stamps
+psArray *poisFindStamps(psArray *stamps, // Existing list of stamps, or NULL
+			const psImage *image, // Image for which to find stamps
+			const psImage *mask, // Mask image
+			const poisConfig *config	// Configuration values
+    );
+
+/************************************************************************************************************/
+
+// poisExtractKernel.c
+
+// Create a kernel image from the solution vector
+psImage *poisExtractKernel(const psVector *solution, // Vector containing the kernel elements
+			   const psArray *kernelParams, // The kernel parameters
+			   float x,	// The relative (-1 to 1) x position for which to get the kernel
+			   float y,	// The relative (-1 to 1) y position for which to get the kernel
+			   const poisConfig *config // Configuration
+    );
+
+/************************************************************************************************************/
+
+// poisCalculateEquation.c
+
+// Calculate the matrix and vector for the matrix equation
+int poisCalculateEquation(psArray *stamps, // The stamps
+			  const psImage *refImage, // The reference image
+			  const psImage *inImage, // The input image
+			  const psArray *kernelParams, // Kernel parameters
+			  const poisConfig *config // Configuration
+    );
+
+/************************************************************************************************************/
+
+// poisConvolveImage.c
+
+// Convolve an image by the kernel
+psImage *poisConvolveImage(const psImage *in, // Input image, to be convolved
+			   const psImage *mask,	// Mask image
+			   const psVector *solution, // The solution vector, containing the coefficients
+			   const psArray *kernelParams, // The kernel parameters
+			   const poisConfig *config // The configuration
+    );
+
+/************************************************************************************************************/
+
+// poisCalculateDeviations.c
+
+// Calculate deviations from the best fit for the stamps
+psVector *poisCalculateDeviations(psVector *deviations,	// Output array of deviations, or NULL
+				  psArray *stamps, // Array of stamps
+				  psImage *refImage, // Reference image
+				  psImage *inImage, // Input image
+				  psImage *mask, // Mask image
+				  psArray *kernelParams, // Array of kernel parameters
+				  psVector *solution, // Solution vector
+				  poisConfig *config // Configuration
+    );
+
+/************************************************************************************************************/
+
+// poisRejectStamps.c
+
+// Reject stamps, based on the deviations
+bool poisRejectStamps(psArray *stamps,	// Array of stamps
+		      psImage *mask,	// Mask image
+		      const psVector *deviations, // Vector of deviations for the stamps
+		      const poisConfig *config // Configuration
+    );
+
+/************************************************************************************************************/
+
+// poisSolveEquation.c
+
+// Solve the matrix equation
+psVector *poisSolveEquation(psVector *solution,	// Solution vector, or NULL
+			    const psArray *stamps, // Array of stamps
+			    const poisConfig *config
+    );
+
+/************************************************************************************************************/
+
+// poisAddPenalty.c
+
+// Generate penalty matrix and vector
+void poisAddPenalty(psImage *matrix,	// The matrix
+		    psVector *vector,	// The vector
+		    float penalty,	// Penalty value
+		    const psArray *kernels, // Kernel basis functions
+		    const poisConfig *config // Configuration
+    );
+
+/************************************************************************************************************/
+
+// poisReadStamps.c
+
+// Read stamps from a file
+psArray *poisReadStamps(psArray *stamps,// Stamps
+			FILE **stampFP, // File pointer for stamps file
+			const psImage *image, // Image for which to find stamps
+			const psImage *mask, // Mask image
+			const poisConfig *config // Configuration
+    );
+
+/************************************************************************************************************/
+
+// poisCheckKernel.c
+
+// Check kernel for power at large radii
+bool poisCheckKernel(const psVector *solution,// Kernel solution
+		     const psArray *kernels, // Kernel basis functions
+		     const poisConfig *config // Configuration
+    );
+
+/************************************************************************************************************/
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisAddPenalty.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisAddPenalty.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisAddPenalty.c	(revision 22322)
@@ -0,0 +1,77 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "pois.h"
+
+void poisAddPenalty(psImage *matrix,    // The matrix
+                    psVector *vector,   // The vector
+                    float penalty,      // Penalty value
+                    const psArray *kernels, // Kernel basis functions
+                    const poisConfig *config // Configuration
+    )
+{
+    // Check inputs
+    assert(matrix);
+    assert(vector);
+    assert(kernels);
+    assert(config);
+    assert(matrix->numRows == matrix->numCols);
+    assert(vector->n == matrix->numRows);
+    assert(vector->n == kernels->n + 1);// Additional space for constant background
+    assert(matrix->type.type == PS_TYPE_F64);
+    assert(vector->type.type == PS_TYPE_F64);
+
+    int numKernels = kernels->n;
+
+    for (int j = 0; j < numKernels; j++) {
+        poisKernelBasis *kernel = kernels->data[j];
+        if (kernel->xOrder == 0 && kernel->yOrder == 0) {
+            for (int k = j + 1; k < numKernels; k++) {
+                poisKernelBasis *compare = kernels->data[k];
+                if (compare->xOrder == 0 && compare->yOrder == 0 &&
+                    (compare->u == kernel->u + 1 || compare->u == kernel->u - 1 ||
+                     compare->v == kernel->v + 1 || compare->v == kernel->v - 1)) {
+                    matrix->data.F64[j][k] -= penalty;
+                    matrix->data.F64[k][j] -= penalty;
+                }
+            }
+        }
+
+        if (abs(kernel->u) == config->xKernel) {
+            if (abs(kernel->v) == config->yKernel) {
+                vector->data.F64[j] -= 3.0 * penalty;
+            } else {
+                vector->data.F64[j] -= 5.0 * penalty;
+            }
+        } else {
+            if (abs(kernel->v) == config->yKernel) {
+                vector->data.F64[j] -= 5.0 * penalty;
+            } else {
+                vector->data.F64[j] -= 8.0 * penalty;
+            }
+        }
+
+    }
+
+
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisCalculateDeviations.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisCalculateDeviations.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisCalculateDeviations.c	(revision 22322)
@@ -0,0 +1,125 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "pois.h"
+
+#define MAXCHAR 80
+
+psVector *poisCalculateDeviations(psVector *deviations, // Output array of deviations, or NULL
+                                  psArray *stamps, // Array of stamps
+                                  psImage *refImage, // Reference image
+                                  psImage *inImage, // Input image
+                                  psImage *mask, // Mask image
+                                  psArray *kernelParams, // Array of kernel parameters
+                                  psVector *solution, // Solution vector
+                                  poisConfig *config // Configuration
+    )
+{
+    // Check inputs
+    assert(!deviations || deviations->type.type == PS_TYPE_F32);
+    assert(!deviations || deviations->n == stamps->n);
+    assert(stamps);
+    assert(refImage->type.type == PS_TYPE_F32);
+    assert(inImage->type.type == PS_TYPE_F32);
+    assert(refImage->numRows == inImage->numRows);
+    assert(refImage->numCols == inImage->numCols);
+    assert(mask->type.type == PS_TYPE_U8);
+    assert(refImage->numRows == mask->numRows);
+    assert(refImage->numCols == mask->numCols);
+    assert(solution->n == kernelParams->n + 1);
+    assert(solution->type.type == PS_TYPE_F64);
+    assert(config);
+
+    if (!deviations) {
+        deviations = psVectorAlloc(stamps->n, PS_TYPE_F32);
+    }
+
+    int footprint = config->footprint;  // Size of stamp footprint
+    int xSize = footprint + config->xKernel; // (Half) size of subimage in x
+    int ySize = footprint + config->yKernel; // (Half) size of subimage in y
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // Statistics
+
+    // Pre-allocate for optimisation:
+    psImage *subStamp = psImageAlloc(2 * xSize, 2 * ySize, PS_TYPE_F32); // Subtraction of stamp
+
+    for (int s = 0; s < stamps->n; s++) {
+        poisStamp *stamp = stamps->data[s]; // The coordinates of the stamp of interest
+        int x = stamp->x;               // Stamp x coord
+        int y = stamp->y;               // Stamp y coord
+        if (stamp->status == POIS_STAMP_USED) {
+            psRegion stampRegion = {x - xSize, x + xSize, y - ySize, y + ySize};
+            psImage *refStamp = psImageSubset(refImage, stampRegion);
+            psImage *inStamp = psImageSubset(inImage, stampRegion);
+            psImage *maskStamp = psImageSubset(mask, stampRegion);
+            psImage *convRefStamp = poisConvolveImage(refStamp, maskStamp, solution, kernelParams, config);
+            // Calculate chi^2
+            (void)psBinaryOp(subStamp, inStamp, "-", convRefStamp);
+            (void)psBinaryOp(subStamp, subStamp, "*", subStamp);
+            (void)psBinaryOp(subStamp, subStamp, "/", inStamp);
+
+            // Trim regions.  Need separate regions for the subtraction and the mask, since the subtraction
+            // isn't a child image (it's the subtraction of two child images, but placed in a pre-allocated
+            // image which isn't a child image), but the mask *is* a child image.  Therefore the regions are
+            // different, since they refer to the region on the parent image (if it exists).
+            psRegion stampTrim = { config->xKernel, config->xKernel + 2 * footprint, config->yKernel,
+                                   config->yKernel + 2 * footprint };
+#if 1
+            psRegion maskTrim = { x - xSize + config->xKernel, x + xSize - config->xKernel,
+                                  y - ySize + config->yKernel, y + ySize - config->yKernel };
+            psImage *maskStampTrim = psImageSubset(maskStamp, maskTrim);
+#else
+            psImage *maskStampTrim = psImageSubset(maskStamp, stampTrim);
+#endif
+            psImage *subStampTrim = psImageSubset(subStamp, stampTrim);
+            (void)psImageStats(stats, subStampTrim, maskStampTrim, POIS_MASK_BAD | POIS_MASK_NEAR_BAD);
+
+            deviations->data.F32[s] = sqrtf(stats->sampleMean / 2.0);
+            psTrace("pois.calculateDeviations", 5, "Deviation of stamp %d (%d,%d) is %f\n", s, x, y,
+                    deviations->data.F32[s]);
+
+#ifdef TESTING
+            char stampName[MAXCHAR];            // File name for stamp
+            snprintf(stampName, MAXCHAR, "stamp%d.fits", s);
+            psFits *stampFile = psFitsOpen(stampName, "w");
+            if (!psFitsWriteImage(stampFile, NULL, subStampTrim, 0, NULL)) {
+                psErrorStackPrint(stderr, "Unable to write stamp: %s\n", stampName);
+            }
+            psFitsClose(stampFile);
+#endif
+
+#if 0
+            psFree(convRefStamp);
+            psFree(maskStampTrim);
+            psFree(subStampTrim);
+            psFree(maskStamp);
+            psFree(refStamp);
+            psFree(inStamp);
+#endif
+        }
+    }
+
+    psFree(stats);
+    psFree(subStamp);
+
+    return deviations;
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisCalculateEquation.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisCalculateEquation.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisCalculateEquation.c	(revision 22322)
@@ -0,0 +1,198 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+#include "pslib.h"
+#include "pois.h"
+
+/* Calculate the matrix and vector for the matrix equation */
+int poisCalculateEquation(psArray *stamps, // The stamps
+                          const psImage *refImage, // The reference image
+                          const psImage *inImage, // The input image
+                          const psArray *kernelParams, // Kernel parameters
+                          const poisConfig *config // Configuration
+    )
+{
+    // Check inputs
+    assert(stamps);
+    assert(refImage);
+    assert(inImage);
+    assert(kernelParams);
+    assert(config);
+
+    // In addition to the kernel parameters, need one extra parameter to fit the (assumed constant) difference
+    // in the background levels.
+    int numSolveParams = kernelParams->n + 1;
+    int bgIndex = kernelParams->n;      // Index in matrix for the background (it's the last one, after the
+                                        // kernel parameters)
+    float **reference = refImage->data.F32; // The reference pixels
+    float **input = inImage->data.F32;  // The input pixels
+    int nPix = 0;                       // Number of pixels used in calculation (for interest)
+    float nx = 0.5 * (float)refImage->numCols; // Half-size of input image in x
+    float ny = 0.5 * (float)refImage->numRows; // Half-size of input image in y
+    int numKernelParams = kernelParams->n; // Number of kernel parameters
+
+    psPolynomial2D *poly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, config->spatialOrder + 1,
+                                               config->spatialOrder + 1); // Polynomial for evaluation
+    for (int j = 0; j < config->spatialOrder + 1; j++) {
+        for (int i = 0; i < config->spatialOrder + 1; i++) {
+            poly->coeff[j][i] = 1.0;
+            poly->mask[j][i] = 1;       // Mask all coefficients; unmask to evaluate
+        }
+    }
+
+    psImage *polyValues = psImageAlloc(config->spatialOrder + 1, config->spatialOrder + 1,
+                                       PS_TYPE_F64); // Evaluations for each of the polynomial orders
+
+    /* Iterate over the stamps */
+    for (int s = 0; s < stamps->n; s++) {
+        poisStamp *stamp = stamps->data[s]; // The stamp
+
+        if (stamp->status == POIS_STAMP_RECALC) {
+            psTrace("pois.calculateEquation", 5, "Calculating for stamp %d: %d,%d\n", s, stamp->x, stamp->y);
+
+            // Initialise the matrix and vector if required
+            if (! stamp->matrix) {
+                psTrace("pois.calculateEquation", 9, "Allocating matrix: %dx%d\n", numSolveParams,
+                        numSolveParams);
+                stamp->matrix = psImageAlloc(numSolveParams, numSolveParams, PS_TYPE_F64);
+            }
+            if (! stamp->vector) {
+                psTrace("pois.calculateEquation", 3, "Allocating vector: %d\n", numSolveParams);
+                stamp->vector = psVectorAlloc(numSolveParams, PS_TYPE_F64);
+                stamp->vector->n = numSolveParams;
+            }
+
+            // Dereference, for convenience
+            psImage *matrix = stamp->matrix;
+            psVector *vector = stamp->vector;
+
+            // Will assert on the type, since can't do much about it if it's wrong.
+            assert(matrix->type.type == PS_TYPE_F64);
+            assert(vector->type.type == PS_TYPE_F64);
+
+            // Initialise the matrix and vector
+            for (int j = 0; j < numSolveParams; j++) {
+                for (int i = 0; i < numSolveParams; i++) {
+                    matrix->data.F64[j][i] = 0.0;
+                }
+                vector->data.F64[j] = 0.0;
+            }
+
+            // Evaluate the polynomial for each order
+            for (int j = 0; j < config->spatialOrder + 1; j++) {
+                for (int i = 0; i < config->spatialOrder + 1 - j; i++) {
+                    poly->mask[j][i] = 0;
+                    polyValues->data.F64[j][i] = psPolynomial2DEval(poly, ((double)stamp->x - nx)/nx,
+                                                                    ((double)stamp->y - ny)/ny);
+                    poly->mask[j][i] = 1;
+                }
+            }
+
+            // Iterate over the pixels
+            for (int y = stamp->y - config->footprint; y < stamp->y + config->footprint; y++) {
+                for (int x = stamp->x - config->footprint; x < stamp->x + config->footprint; x++) {
+                    float invNoise2 = 1.0/reference[y][x]; // The inverse of the noise, squared.
+                    nPix++;
+
+                    /* Iterate over the first convolution */
+                    for (int k1 = 0; k1 < numKernelParams; k1++) {
+                        poisKernelBasis *kernel1 = kernelParams->data[k1]; // Kernel basis #1
+
+                        int u1 = kernel1->u; // Offset in x
+                        int v1 = kernel1->v; // Offset in y
+                        int i1 = kernel1->xOrder; // Polynomial order in x
+                        int j1 = kernel1->yOrder; // Polynomial order in y
+                        // First convolution
+                        float conv1 = polyValues->data.F64[j1][i1] * reference[y - v1][x - u1];
+
+                        // Assuming that the first kernel component is 0 order in x and y, and 0 offset
+                        if (k1 != 0) {
+                            conv1 -= reference[y][x];
+                        }
+
+                        /* Iterate over the second convolution */
+                        for (int k2 = k1; k2 < numKernelParams; k2++) {
+                            poisKernelBasis *kernel2 = kernelParams->data[k2]; // Kernel basis #2
+
+                            int u2 = kernel2->u; // Offset in x
+                            int v2 = kernel2->v; // Offset in y
+                            int i2 = kernel2->xOrder; // Polynomial order in x
+                            int j2 = kernel2->yOrder; // Polynomial order in y
+                            // Second convolution
+                            float conv2 = polyValues->data.F64[j2][i2] * reference[y - v2][x - u2];
+
+                            // Assuming that the first kernel component is 0 order in x and y, and 0 offset
+                            if (k2 != 0) {
+                                conv2 -= reference[y][x];
+                            }
+
+                            // Add into the matrix element
+                            matrix->data.F64[k1][k2] += conv1 * // First convolution
+                                conv2 * // Second convolution
+                                invNoise2; // Divide by sigma^2, ~ poisson.
+                        } // Second convolution
+
+                        // Add into the vector element
+                        vector->data.F64[k1] += input[y][x] * // Input image, no convolution
+                            conv1 *     // Convolved reference image
+                            invNoise2; // Divide by sigma^2, ~ poisson.
+
+                        /* Background term */
+                        matrix->data.F64[k1][bgIndex] += conv1 * // Convolved reference image
+                            invNoise2; // Divide by sigma^2, ~ poisson.
+
+                    } // First convolution
+
+                    /* Background only terms */
+                    matrix->data.F64[bgIndex][bgIndex] += invNoise2;
+                    vector->data.F64[bgIndex] += input[y][x] * invNoise2;
+                }
+            } // Iterating over pixels in stamp
+
+            // Fill in other side of symmetric matrix
+            for (int k1 = 0; k1 < kernelParams->n; k1++) {
+                for (int k2 = 0; k2 < k1; k2++) {
+                    matrix->data.F64[k1][k2] = matrix->data.F64[k2][k1];
+                }
+                matrix->data.F64[bgIndex][k1] = matrix->data.F64[k1][bgIndex];
+            }
+
+            for (int k = 0; k < kernelParams->n; k++) {
+                poisKernelBasis *kernel = kernelParams->data[k]; // Kernel basis #2
+                int u = kernel->u; // Offset in x
+                int v = kernel->v; // Offset in y
+                matrix->data.F64[k][k] += config->penalty * (float)(u*u + v*v);
+            }
+
+            stamp->status = POIS_STAMP_USED; // We've calculated it now, so don't need to do so again.
+
+        } // Legitimate stamps
+
+    } // Iterating over stamps
+
+    psFree(polyValues);
+    psFree(poly);
+
+    // Return number of pixels over which matrix has been calculated
+    return nPix;
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisCheckKernel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisCheckKernel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisCheckKernel.c	(revision 22322)
@@ -0,0 +1,83 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "pois.h"
+
+bool poisCheckKernel(const psVector *solution,// Kernel solution
+                     const psArray *kernels, // Kernel basis functions
+                     const poisConfig *config // Configuration
+    )
+{
+    psTrace("pois.checkKernel", 1, "Checking kernel....\n");
+
+    // Get kernel value as a function of radius
+    psVector *radKernel = psVectorAlloc((2*config->xKernel + 1) * (2*config->yKernel + 1), PS_TYPE_F32);
+    psVector *radii = psVectorAlloc((2*config->xKernel + 1) * (2*config->yKernel + 1), PS_TYPE_F32); // Radius
+    for (int i = 0; i < solution->n - 1; i++) {
+        poisKernelBasis *kernel = kernels->data[i]; // The kernel basis function
+        if (kernel->xOrder == 0 && kernel->yOrder == 0) {
+            int u = kernel->u;          // x offset
+            int v = kernel->v;          // y offset
+            float distance = sqrtf((float)(u*u) + (float)(v*v)); // Distance from the centre
+            radKernel->data.F32[i] = solution->data.F64[i];
+            radii->data.F32[i] = distance;
+        }
+    }
+
+    psVector *sortIndex = psVectorSortIndex(NULL, radii); // Indices from sort
+    float distance = 0.0;               // Distance from the centre that's being examined
+    float sum = 0.0;                    // Sum of kernel for that distance
+    int num = 0;                        // Number of pixels at that distance
+    int centreIndex = sortIndex->data.U32[0]; // Index of the centre pixel
+    int numSuspect = 0;                 // Number of radii considered suspect
+    for (int i = 0; i < (2*config->xKernel + 1) * (2*config->yKernel + 1); i++) {
+        unsigned int index = sortIndex->data.U32[i];
+        if (radii->data.F32[index] != distance) {
+            psTrace("pois.checkKernel", 7, "Radius: %f\tMean: %f\n", distance, sum/(float)num);
+            if (fabs(distance*sum/(float)num) > radKernel->data.F32[centreIndex]) {
+                numSuspect++;
+            }
+            sum = 0.0;
+            num = 0;
+            distance = radii->data.F32[index];
+        }
+        sum += radKernel->data.F32[index];
+        num++;
+    }
+    psTrace("pois.checkKernel", 7, "Radius: %f\tMean: %f\n", distance, sum/(float)num);
+    if (fabs(distance*sum/(float)num) > radKernel->data.F32[centreIndex]) {
+        numSuspect++;
+    }
+
+    psFree(radKernel);
+    psFree(radii);
+    psFree(sortIndex);
+
+    psTrace("pois.checkKernel", 5, "%d radii considered suspect.\n", numSuspect);
+
+    if (numSuspect > 0) {
+        psWarning("There is significant power at large radii.  The kernel may be bad.\n");
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisConvolveImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisConvolveImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisConvolveImage.c	(revision 22322)
@@ -0,0 +1,122 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "pois.h"
+
+
+/* Convolve an image by the kernel */
+psImage *poisConvolveImage(const psImage *input, // Input image, to be convolved
+                           const psImage *mask, // Mask image
+                           const psVector *solution, // The solution vector, containing the coefficients
+                           const psArray *kernelParams, // The kernel parameters
+                           const poisConfig *config // The configuration
+    )
+{
+    assert(solution->n == kernelParams->n + 1); // Extra parameter for background
+    assert(solution->type.type == PS_TYPE_F64);
+    assert(input->numCols == mask->numCols && input->numRows == mask->numRows);
+    assert(mask->type.type == PS_TYPE_U8);
+    assert(input->type.type == PS_TYPE_F32);
+
+    int nx = input->numCols;            // The size of the image in x
+    int ny = input->numRows;            // The size of the image in y
+    float nxHalf = 0.5 * (float)nx;     // Half the size of the image
+    float nyHalf = 0.5 * (float)ny;     // Half the size of the image
+    int xKernel = config->xKernel;      // The half-size of the kernel in x
+    int yKernel = config->yKernel;      // The half-size of the kernel in y
+    float background = solution->data.F64[solution->n-1]; // The difference in background
+    int numParams = kernelParams->n;    // Number of kernel parameters
+
+    psImage *convolved = psImageAlloc(nx, ny, PS_TYPE_F32); // The convolved image, to be returned
+
+    psPolynomial2D *poly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, config->spatialOrder + 1,
+                                               config->spatialOrder + 1); // Polynomial
+    for (int i = 0; i < config->spatialOrder + 1; i++) {
+        for (int j = 0; j < config->spatialOrder + 1; j++) {
+            poly->coeff[i][j] = 1.0;
+            poly->mask[i][j] = 1;       // Mask all coefficients; unmask to evaluate
+        }
+    }
+
+    // Convolve only the middle part
+    for (int y = yKernel; y < ny - yKernel; y++) {
+        for (int x = xKernel; x < nx - xKernel; x++) {
+            if (! mask->data.U8[y][x] & (POIS_MASK_BAD | POIS_MASK_NEAR_BAD)) {
+                double conv = background; // Convolved value
+
+                float imageX = (x - nxHalf) / nxHalf; // Normalised position in x
+                float imageY = (y - nyHalf) / nyHalf; // Normalised position in y
+
+                // Iterate over the kernel basis functions
+                for (int k = 0; k < numParams; k++) {
+                    poisKernelBasis *kernel = kernelParams->data[k]; // The kernel basis function
+                    int u = kernel->u;
+                    int v = kernel->v;
+                    int xOrder = kernel->xOrder;
+                    int yOrder = kernel->yOrder;
+
+                    double polyVal = 1.0;
+                    if (xOrder != 0 || yOrder != 0) {
+                        // Evaluate the polynomial
+                        poly->mask[xOrder][yOrder] = 0;
+                        polyVal = psPolynomial2DEval(poly, imageX, imageY);
+                        poly->mask[xOrder][yOrder] = 1;
+                    }
+
+                    if (k == 0) {
+                        conv += solution->data.F64[k] * input->data.F32[y - v][x - u] * polyVal;
+                    } else {
+                        conv += solution->data.F64[k] *
+                            (input->data.F32[y - v][x - u] * polyVal - input->data.F32[y][x]);
+                    }
+                }
+                convolved->data.F32[y][x] = (float)conv;
+            }
+        }
+
+        /* Pad the rest with zeros */
+        for (int x = 0; x < xKernel; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+        for (int x = nx - xKernel; x < nx; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+    }
+
+    for (int y = 0; y < yKernel; y++) {
+        for (int x = 0; x < nx; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+    }
+    for (int y = ny - yKernel; y < ny; y++) {
+        for (int x = 0; x < nx; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+    }
+
+    psFree(poly);
+
+    return convolved;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisExtractKernel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisExtractKernel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisExtractKernel.c	(revision 22322)
@@ -0,0 +1,90 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "pois.h"
+
+/* Create a kernel image from the solution vector */
+psImage *poisExtractKernel(const psVector *solution, // Vector containing the kernel elements
+                           const psArray *kernelParams, // The kernel parameters
+                           float x,     // The relative (-1 to 1) x position for which to get the kernel
+                           float y,     // The relative (-1 to 1) y position for which to get the kernel
+                           const poisConfig *config // Configuration
+    )
+{
+    assert(solution->n == kernelParams->n + 1);
+    assert(solution->type.type == PS_TYPE_F64);
+    assert(config);
+    assert(x >= -1.0 && x <= 1.0);
+    assert(y >= -1.0 && y <= 1.0);
+
+    int xKernel = config->xKernel;      // Half-size of kernel in x
+    int yKernel = config->yKernel;      // Half-size of kernel in y
+    float kernelSum = 0.0;              // Sum of the kernel
+
+    /* Allocate the image */
+    psImage *image = psImageAlloc(2*xKernel + 1, 2*yKernel + 1, PS_TYPE_F32); // The kernel image
+    for (int j = 0; j < (2*yKernel + 1); j++) {
+        for (int i = 0; i < (2*xKernel + 1); i++) {
+            image->data.F32[j][i] = 0.0;
+        }
+    }
+
+    psPolynomial2D *poly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, config->spatialOrder + 1,
+                                               config->spatialOrder + 1); // Polynomial for evaluation
+    for (int j = 0; j < config->spatialOrder + 1; j++) {
+        for (int i = 0; i < config->spatialOrder + 1; i++) {
+            poly->coeff[j][i] = 1.0;
+            poly->mask[j][i] = 1;       // Mask all coefficients; unmask to evaluate
+        }
+    }
+
+    psTrace("pois.extractKernel", 4, "Evaluating kernel at %f,%f\n", x, y);
+    /* Iterate over the kernel parameters */
+    for (int k = 0; k < kernelParams->n; k++) {
+        poisKernelBasis *kernel = kernelParams->data[k]; // The kernel basis function
+
+        int u = kernel->u;
+        int v = kernel->v;
+        int xOrder = kernel->xOrder;
+        int yOrder = kernel->yOrder;
+
+        // Evaluate polynomial
+        poly->mask[xOrder][yOrder] = 0;
+        double evaluation = solution->data.F64[k] * psPolynomial2DEval(poly, x, y);
+        poly->mask[xOrder][yOrder] = 1;
+
+        image->data.F32[v + yKernel][u + xKernel] += (float)evaluation;
+        kernelSum += (float)evaluation;
+    }
+
+    // Subtract from the centre pixel to remove the effect of the flux-conservation treatment
+    // This assumes that the "reference" is the centre pixel: u = 0, v = 0, xOrder = 0, yOrder = 0
+    image->data.F32[yKernel][xKernel] -= kernelSum - (float)solution->data.F64[0];
+
+    psTrace("pois.extractKernel", 4, "Kernel sum: %f\n", kernelSum);
+
+    psFree(poly);
+
+    return image;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisFindStamps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisFindStamps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisFindStamps.c	(revision 22322)
@@ -0,0 +1,101 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+#include "pslib.h"
+#include "pois.h"
+
+
+psArray *poisFindStamps(psArray *stamps, // Existing list of stamps, or NULL
+                        const psImage *image, // Image for which to find stamps
+                        const psImage *mask, // Mask image
+                        const poisConfig *config // Configuration values
+    )
+{
+    int nx = config->xImage;            // Size of image in x
+    int ny = config->yImage;            // Size of image in y
+
+    assert(image->numCols == nx && image->numRows == ny);
+    assert(mask->numCols == nx && mask->numRows == ny);
+    assert(image->type.type == PS_TYPE_F32);
+    assert(mask->type.type == PS_TYPE_U8);
+    assert(!stamps || stamps->n == config->nsx * config->nsy);
+
+    int footprint = config->footprint;  // Footprint for stamps
+    int borderx = footprint + config->xKernel; // Border around image in x
+    int bordery = footprint + config->yKernel; // Border around image in y
+    int nsx = config->nsx;              // Number of stamps in x
+    int nsy = config->nsy;              // Number of stamps in y
+
+    psF32 **pixels = image->data.F32;   // The image pixels
+
+    if (stamps == NULL) {
+        stamps = psArrayAlloc(nsx * nsy);
+        // Initialise
+        for (int i = 0; i < nsx * nsy; i++) {
+            stamps->data[i] = poisStampAlloc();
+        }
+    }
+
+    // Iterate over the image sections
+    int num = 0;
+    for (int i = 0; i < nsx; i++) {
+        for (int j = 0; j < nsy; j++) {
+            poisStamp *stamp = stamps->data[num]; // The stamp
+
+            // Only find a new stamp if we need to
+            if ((stamp->x == 0 && stamp->y == 0) || stamp->status == POIS_STAMP_RESET) {
+                // Find maximum non-masked value in the image section, but don't include a footprint around
+                // the edge
+                float max = - INFINITY;         // Negative infinity
+                int bestx = 0, besty = 0;       // Position of maximum
+
+                for (int y = bordery + j * (ny - 2.0 * bordery) / nsy;
+                     y < bordery + (j + 1) * (ny - 2.0 * bordery) / nsy; y++) {
+                    for (int x = borderx + i * (nx - 2.0 * borderx) / nsx;
+                         x < borderx + (i + 1) * (nx - 2.0 * borderx) / nsx; x++) {
+                        if (pixels[y][x] > max && poisCheckStamp(image, mask, x, y, config)) {
+                            max = pixels[y][x];
+                            bestx = x;
+                            besty = y;
+                        }
+                    }
+                } // Done iterating over this image section
+
+                // Store the stamp
+                if (max != - INFINITY) {
+                    stamp->x = bestx;
+                    stamp->y = besty;
+                    stamp->status = POIS_STAMP_RECALC;
+                } else {
+                    // No stamp in this section
+                    stamp->status = POIS_STAMP_BAD;
+                }
+            } // Finding a new stamp
+
+            psTrace("pois.findStamps", 7, "Stamp %d: %d,%d\n", num, stamp->x, stamp->y);
+            num++;
+        }
+    } // Done iterating over sections
+
+    return stamps;
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisKernelBasisFunctions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisKernelBasisFunctions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisKernelBasisFunctions.c	(revision 22322)
@@ -0,0 +1,97 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "pois.h"
+
+psArray *poisKernelBasisFunctions(const poisConfig *config // Configuration
+    )
+{
+    assert(config);
+
+    psTrace("pois.kernelBasisFunctions", 1, "Setting kernel basis functions....\n");
+
+    int xKernelHalfSize = config->xKernel; // Half size of kernel in x
+    int yKernelHalfSize = config->yKernel; // Half size of kernel in y
+    int spatialOrder = config->spatialOrder; // Order of spatial variations
+
+    // Number of basis functions
+    int nBF = (2 * xKernelHalfSize + 1) * (2 * yKernelHalfSize + 1) * (spatialOrder + 1) *
+        (spatialOrder + 2) / 2;
+
+    psArray *array = psArrayAlloc(nBF); // Array to hold the basis functions
+
+    int num = 0;                        // Kernel parameter number
+
+    // Put the (u,v) = (0,0) component right at the start of the list for convenience
+    for (int order = 0; order <= spatialOrder; order++) {
+        for (int i = 0; i <= order; i++) {
+            int j = order - i;
+            poisKernelBasis *kernel = psAlloc(sizeof(poisKernelBasis));
+            kernel->u = 0;
+            kernel->v = 0;
+            kernel->xOrder = i;
+            kernel->yOrder = j;
+
+            // Stuff into the array
+            array->data[num] = kernel;
+
+            psTrace("pois.kernelBasisFunctions", 3, "--> %d: (%d,%d) x^%d y^%d\n", i, kernel->u,
+                    kernel->v, kernel->xOrder, kernel->yOrder);
+            num++;
+        }
+    }
+
+    // Iterate over (u,v)
+    for (int u = -xKernelHalfSize; u <= xKernelHalfSize; u++) {
+        for (int v = -yKernelHalfSize; v <= yKernelHalfSize; v++) {
+
+            // Already did (u,v) = (0,0): it's at the start, so skip it now.
+            if ((u != 0) || (v != 0)) {
+
+                // Iterate over spatial order
+                for (int order = 0; order <= spatialOrder; order++) {
+
+                    for (int i = 0; i <= order; i++) {
+                        int j = order - i;
+                        poisKernelBasis *kernel = psAlloc(sizeof(poisKernelBasis));
+
+                        kernel->u = u;
+                        kernel->v = v;
+                        kernel->xOrder = i;
+                        kernel->yOrder = j;
+
+                        // Stuff into the array
+                        array->data[num] = kernel;
+
+                        psTrace("pois.kernelBasisFunctions", 3, "--> %d: (%d,%d) x^%d y^%d\n", num,
+                                kernel->u, kernel->v, kernel->xOrder, kernel->yOrder);
+                        num++;
+                    }
+                } // Iterating over spatial order
+            } // Except (u,v) = (0,0)
+        }
+    } // Iterating over (u,v)
+
+    return array;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisMakeMask.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisMakeMask.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisMakeMask.c	(revision 22322)
@@ -0,0 +1,83 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "pois.h"
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+psImage *poisMakeMask(psImage *mask,    // Mask to update, or NULL
+                      const psImage *refImage, // Reference image
+                      const psImage *inImage, // Input image
+                      const poisConfig *config // Configuration
+    )
+{
+    assert(!mask || mask->type.type == PS_TYPE_U8);
+    assert(!mask || (mask->numCols == refImage->numCols && mask->numRows == refImage->numRows));
+    assert(refImage->numCols == inImage->numCols && refImage->numRows == inImage->numRows);
+    assert(refImage->type.type == PS_TYPE_F32);
+    assert(inImage->type.type == PS_TYPE_F32);
+    assert(config);
+
+    int nx = config->xImage;            // Size in x
+    int ny = config->yImage;            // Size in y
+
+    psTrace("pois.makeMask", 5, "Creating mask: %dx%d\n", nx, ny);
+
+    // Allocate the mask image, if necessary
+    if (mask == NULL) {
+        mask = psImageAlloc(nx, ny, PS_TYPE_U8);
+        // Initialise to zero
+        for (int j = 0; j < ny; j++) {
+            for (int i = 0; i < nx; i++) {
+                mask->data.U8[j][i] = POIS_MASK_OK;
+            }
+        }
+    }
+
+    // Iterate over the images
+    for (int y = config->yKernel; y < ny - config->yKernel; y++) {
+        for (int x = config->xKernel; x < nx - config->xKernel; x++) {
+            // Mask all around something that's saturated in the reference, since it gets convolved
+            // But only mask single pixel in the input image, since it doesn't get convolved
+            if (refImage->data.F32[y][x] >= config->refSat || refImage->data.F32[y][x] <= config->refBad ||
+                isnan(refImage->data.F32[y][x])) {
+                mask->data.U8[y][x] |= POIS_MASK_BAD;
+                int xlow = MAX(0, x - config->xKernel);
+                int xhigh = MIN(nx, x + config->xKernel);
+                int ylow = MAX(0, y - config->yKernel);
+                int yhigh = MIN(ny, y + config->yKernel);
+                for (int v = ylow; v <= yhigh; v++) {
+                    for (int u = xlow; u <= xhigh; u++) {
+                        mask->data.U8[v][u] |= POIS_MASK_NEAR_BAD;
+                    }
+                }
+            } else if (inImage->data.F32[y][x] >= config->inSat || inImage->data.F32[y][x] <= config->inBad ||
+                isnan(inImage->data.F32[y][x])) {
+                mask->data.U8[y][x] |= POIS_MASK_BAD;
+            }
+        }
+    }
+
+    return mask;
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisMaskOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisMaskOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisMaskOps.c	(revision 22322)
@@ -0,0 +1,112 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*
+ * Some utility routines that should be in psLib
+ */
+#include <stdio.h>
+#include <memory.h>
+#include <assert.h>
+#include "pslib.h"
+#include "pois.h"
+
+psImage *poisImageSetVal(psImage *restrict in,       // Input image
+                         const psC64 val             // set to this value
+    )
+{
+    assert (in != NULL);
+
+    switch (in->type.type) {
+      case PS_TYPE_U8:
+        {
+            const int numCols = in->numCols;
+            psU8 *row0 = in->data.U8[0];
+            for (int c = 0; c < numCols; c++) {
+                row0[c] = val;
+            }
+            for (int r = 1; r < in->numRows; r++) {
+                memcpy(in->data.U8[r], row0, numCols*sizeof(psU8));
+            }
+        }
+        break;
+      case PS_TYPE_F32:
+        {
+            const int numCols = in->numCols;
+            psF32 *row0 = in->data.F32[0];
+            for (int c = 0; c < numCols; c++) {
+                row0[c] = val;
+            }
+            for (int r = 1; r < in->numRows; r++) {
+                memcpy(in->data.F32[r], row0, numCols*sizeof(psF32));
+            }
+        }
+        break;
+      default:
+        psError(POIS_ERR_UNSUPPORTED_TYPE, true,
+                "Type %d is not supported", in->type.type);
+        return NULL;
+    }
+
+    return in;
+}
+
+psImage *poisImageSetValInMask(psImage *out,                    // Image to update, or NULL
+                               const psImage *restrict in,      // Input image
+                               const psImage *mask,             // mask for image
+                               const psC64 val,                 // set to this value
+                               const unsigned long bits         // set pixels where mask & bits != 0
+    )
+{
+    assert(mask != NULL);
+    assert(mask->type.type == PS_TYPE_U8);
+    const int numCols = mask->numCols;
+    const int numRows = mask->numRows;
+    assert(in != NULL);
+    assert(in->type.type == PS_TYPE_F32);
+    assert(in->numCols == numCols && in->numRows == numRows);
+
+    // Allocate the output image, if necessary
+    if (out == NULL) {
+        out = psImageCopy(NULL, in, in->type.type);
+    }
+
+    switch (in->type.type) {
+      case PS_TYPE_F32:
+        {
+            const int numCols = in->numCols;
+            for (int r = 0; r < in->numRows; r++) {
+                psF32 *row = out->data.F32[r]; // n.b. a copy of in
+                psU8 *mrow = mask->data.U8[r];
+                for (int c = 0; c < numCols; c++) {
+                    if (mrow[c] & bits) {
+                        row[c] = val;
+                    }
+                }
+            }
+        }
+        break;
+      default:
+        psError(POIS_ERR_UNSUPPORTED_TYPE, true,
+                "Type %d is not supported", in->type.type);
+        return NULL;
+    }
+
+    return out;
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisParseConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisParseConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisParseConfig.c	(revision 22322)
@@ -0,0 +1,207 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "pois.h"
+
+void help(void)
+{
+    fprintf (stderr, "POIS: Pan-STARRS (or Pricey's) Optimal Image Subtraction\n"
+             "Usage: pois [-h] [-v] [-k X Y] [-t T] [-s S1 S2] [-b B1 B2] [-B BG] [-o N] [-n NSTAMPS] [-S STAMPS] [-i ITER] [-r REJ] [-m] REF IN OUT\n"
+             "where\n"
+             "\t-h         Help (this info)\n"
+             "\t-v         Increase verbosity level\n"
+             "\t-k X Y     Kernel half-size in x and y (5)\n"
+             "\t-t T       Threshold for stamps on reference image (0)\n"
+             "\t-s S1 S2   Saturation level for reference (S1) and input (S2) (50000)\n"
+             "\t-b B1 B2   Bad level for both images (0)\n"
+             "\t-B BG      Level to add to background to get above zero (0)\n"
+             "\t-o ORDER   Order of spatial variations in the kernel (0)\n"
+             "\t-n NSTAMPS Number of stamps in each dimension (5)\n"
+             "\t-S STAMPS  File from which to read stamps\n"
+             "\t-R         Reverse the sense of the subtraction (REF - IN)\n"
+             "\t-i ITER    Number of rejection iterations (10)\n"
+             "\t-r REJ     Rejection level in standard deviations (2.5)\n"
+             "\t-m         Output mask frame (OUT.mask)\n"
+             "\tREF        Reference image\n"
+             "\tIN         Input image (to be matched to REF)\n"
+             "\tOUT        Output image\n"
+        );
+}
+
+poisConfig *poisParseConfig(int argc, // Number of command-line arguments
+                            char **argv // Command-line arguments
+    )
+{
+    poisConfig *config;                 // Configuration values
+
+    /* Variables for getopt */
+    int opt;   /* Option, from getopt */
+    extern char *optarg;   /* Argument accompanying switch */
+    extern int optind;   /* getopt variables */
+
+    /* Initialise configuration */
+    config = (poisConfig *) psAlloc(sizeof(poisConfig));
+    config->verbose = 0;
+    config->refFile = NULL;
+    config->inFile = NULL;
+    config->outFile = NULL;
+    config->xKernel = 5;
+    config->yKernel = 5;
+    config->footprint = 15;
+    config->threshold = 0.0;
+    config->refSat = 60000.0;
+    config->inSat = 60000.0;
+    config->refBad = 0.0;
+    config->inBad = 0.0;
+    config->background = 0.0;
+    config->spatialOrder = 0;
+    config->nsx = 5;
+    config->nsy = 5;
+    config->numIter = 10;
+    config->sigmaRej = 2.5;
+    config->penalty = 0.0;
+    config->stampFile = NULL;
+    config->reverse = false;
+    config->mask = false;
+
+    /* Parse command-line arguments using getopt */
+    while ((opt = getopt(argc, argv, "hvRmk:f:q:t:s:o:b:B:n:i:r:p:S:")) != -1) {
+        switch (opt) {
+          case 'h':
+            help();
+            exit(EXIT_SUCCESS);
+          case 'v':
+            config->verbose++;
+            break;
+          case 'k':
+            if ((sscanf(argv[optind-1], "%d", &config->xKernel) != 1) ||
+                (sscanf(argv[optind++], "%d", &config->yKernel) != 1)) {
+                /*
+                  Note: incrementing optind, so I can read more than
+                  one parameter.
+                */
+                help();
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'f':
+            if (sscanf(optarg, "%d", &config->footprint) != 1) {
+                help();
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 't':
+            if (sscanf(optarg, "%f", &config->threshold) != 1) {
+                help();
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 's':
+            if ((sscanf(argv[optind-1], "%f", &config->refSat) != 1) ||
+                (sscanf(argv[optind++], "%f", &config->inSat) != 1)) {
+                /*
+                  Note: incrementing optind, so I can read more than
+                  one parameter.
+                */
+                help();
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'o':
+            if (sscanf(optarg, "%d", &config->spatialOrder) != 1) {
+                help();
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'b':
+            if ((sscanf(argv[optind-1], "%f", &config->refBad) != 1) ||
+                (sscanf(argv[optind++], "%f", &config->inBad) != 1)) {
+                /*
+                  Note: incrementing optind, so I can read more than
+                  one parameter.
+                */
+                help();
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'B':
+            if (sscanf(optarg, "%f", &config->background) != 1) {
+                help();
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'n':
+            if (sscanf(argv[optind-1], "%d", &config->nsx) != 1) {
+                help();
+                exit(EXIT_FAILURE);
+            }
+            config->nsy = config->nsx;
+            break;
+          case 'i':
+            if (sscanf(argv[optind-1], "%d", &config->numIter) != 1) {
+                help();
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'r':
+            if (sscanf(argv[optind-1], "%f", &config->sigmaRej) != 1) {
+                help();
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'p':
+            if (sscanf(argv[optind-1], "%f", &config->penalty) != 1) {
+                help();
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'S':
+            config->stampFile = argv[optind-1];
+            config->threshold = -INFINITY;
+            break;
+          case 'R':
+            config->reverse = true;
+            break;
+          case 'm':
+            config->mask = true;
+            break;
+          default:
+            help();
+        }
+    }
+
+    /* Get the remaining, mandatory values */
+    argc -= optind;
+    argv += optind;
+    if (argc < 3) {
+        help();
+        exit(EXIT_FAILURE);
+    }
+
+    config->refFile = argv[0];
+    config->inFile = argv[1];
+    config->outFile = argv[2];
+
+    return config;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisReadStamps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisReadStamps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisReadStamps.c	(revision 22322)
@@ -0,0 +1,81 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "pois.h"
+
+#define MAXLINE 128                     // Maximum length of a line in the stamps file
+
+
+psArray *poisReadStamps(psArray *stamps,// Stamps
+                        FILE **stampFP, // File pointer for stamps file
+                        const psImage *image, // Image for which to find stamps
+                        const psImage *mask, // Mask image
+                        const poisConfig *config // Configuration
+    )
+{
+    FILE *fp = *stampFP;                // File pointer
+
+    if (stamps == NULL) {
+        stamps = psArrayAlloc(config->nsx);
+        // Initialise
+        for (int i = 0; i < config->nsx; i++) {
+            stamps->data[i] = poisStampAlloc();
+        }
+    }
+
+    for (int i = 0; i < stamps->n; i++) {
+        poisStamp *stamp = stamps->data[i];
+
+        if (stamp->status == POIS_STAMP_RESET) {
+            if (fp) {
+                float x, y;             // Position of stamp
+                int xPixel, yPixel;     // Integer position
+                int result = 0;         // Result of fscanf
+                char line[MAXLINE];     // The line from the stamps file
+                char *lineBack;         // The line back from fgets
+                do {
+                    lineBack = fgets(line, MAXLINE, fp);
+                    result = sscanf(line, "%f %f", &x, &y);
+
+                    // Add 0.5 for rounding, and subtract 1.0 for unit-offset to zero-offset
+                    xPixel = (int)(x - 0.5);
+                    yPixel = (int)(y - 0.5);
+                } while (result == 2 && lineBack && ! poisCheckStamp(image, mask, xPixel, yPixel, config));
+                if (result == 2 && lineBack) {
+                    stamp->x = xPixel;
+                    stamp->y = yPixel;
+                    stamp->status = POIS_STAMP_RECALC;
+                    psTrace("pois.readStamps", 7, "Stamp %d: %d,%d\n", i, stamp->x, stamp->y);
+                } else {
+                    // We've run out of stamps in the file --- probably got EOF
+                    stamp->status = POIS_STAMP_BAD;
+                }
+            } else {
+                // Stamp file isn't open!
+                stamp->status = POIS_STAMP_BAD;
+            }
+        }
+    }
+
+    return stamps;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisRejectStamps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisRejectStamps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisRejectStamps.c	(revision 22322)
@@ -0,0 +1,86 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "pois.h"
+
+#define SQUARE(x) ((x)*(x))
+
+bool poisRejectStamps(psArray *stamps,  // Array of stamps
+                      psImage *mask,    // Mask image
+                      const psVector *deviations, // Vector of deviations for the stamps
+                      const poisConfig *config // Configuration
+    )
+{
+    assert(stamps);
+    assert(mask);
+    assert(deviations);
+    assert(config);
+    assert(stamps->n == deviations->n);
+    assert(mask->numCols == config->xImage && mask->numRows == config->yImage);
+    assert(mask->type.type == PS_TYPE_U8);
+    assert(deviations->type.type == PS_TYPE_F32);
+
+    bool masked = false;                // Have we masked any stamps?
+
+    // Don't want the standard deviation, but rather the deviation from zero
+    double sum = 0.0;
+    int numDev = 0;
+    for (int i = 0; i < deviations->n; i++) {
+        poisStamp *stamp = stamps->data[i];     // The stamp
+        // Only interested in the stamps that we're actually using
+        if (stamp->status == POIS_STAMP_USED) {
+            sum += SQUARE(deviations->data.F32[i]);
+            numDev++;
+        }
+    }
+    float meanDev = sqrtf(sum / (float)numDev);
+    float limit = meanDev * config->sigmaRej;
+    psTrace("pois.rejectStamps", 2, "Mean RMS deviation: %f\n", meanDev);
+    psTrace("pois.rejectStamps", 2, "Rejection limit: %f\n", limit);
+
+    // Reject stamps
+    for (int s = 0; s < deviations->n; s++) {
+        poisStamp *stamp = stamps->data[s];
+        if (stamp->status == POIS_STAMP_USED &&
+            (fabsf(deviations->data.F32[s]) > limit || ! isfinite(deviations->data.F32[s]))) {
+            masked = true;
+            psTrace("pois.rejectStamps", 1, "Rejecting stamp %d (%d,%d): %f\n", s, stamp->x, stamp->y,
+                    deviations->data.F32[s]);
+
+            // Mask out the stamp in the image so you don't find it again
+            for (int y = stamp->y - config->footprint; y < stamp->y + config->footprint; y++) {
+                for (int x = stamp->x - config->footprint; x < stamp->x + config->footprint; x++) {
+                    mask->data.U8[y][x] |= POIS_MASK_STAMP;
+                }
+            }
+
+            // Set stamp for replacement
+            stamp->x = 0;
+            stamp->y = 0;
+            stamp->status = POIS_STAMP_RESET;
+
+        } // Bad stamps
+    } // Iterating over stamps
+
+    return masked;
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisSolveEquation.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisSolveEquation.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisSolveEquation.c	(revision 22322)
@@ -0,0 +1,89 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "pois.h"
+
+psVector *poisSolveEquation(psVector *solution, // Solution vector, or NULL
+                            const psArray *stamps, // Array of stamps
+                            const poisConfig *config
+    )
+{
+    assert(stamps);
+    assert(config);
+    assert(!solution || solution->type.type == PS_TYPE_F64);
+
+    int size = (2 * config->xKernel + 1) * (2 * config->yKernel + 1) * (config->spatialOrder + 1) *
+        (config->spatialOrder + 2) / 2 + 1; // Size of matrix and vector
+
+    assert(!solution || solution->n == size);
+
+    psImage *matrix = psImageAlloc(size, size, PS_TYPE_F64);
+    psVector *vector = psVectorAlloc(size, PS_TYPE_F64);
+    for (int j = 0; j < size; j++) {
+        for (int i = 0; i < size; i++) {
+            matrix->data.F64[j][i] = 0.0;
+        }
+        vector->data.F64[j] = 0.0;
+    }
+
+    for (int s = 0; s < stamps->n; s++) {
+        poisStamp *stamp = stamps->data[s]; // Stamp of interest
+        psImage *stampMatrix = stamp->matrix; // Corresponding matrix
+        psVector *stampVector = stamp->vector; // Corresponding vector
+
+        // Check inputs
+        assert(matrix->type.type == PS_TYPE_F64);
+        assert(vector->type.type == PS_TYPE_F64);
+        assert(matrix->numCols == size && matrix->numRows == size);
+        assert(vector->n == size);
+
+        if (stamp->status == POIS_STAMP_USED) {
+            (void)psBinaryOp(matrix, matrix, "+", stampMatrix);
+            (void)psBinaryOp(vector, vector, "+", stampVector);
+        }
+    }
+
+    // Solution via LU Decomposition
+    psVector *permutation = NULL; // Permutation vector for LU decomposition
+    psImage *luMatrix = psMatrixLUD(NULL, &permutation, matrix); // LU decomposed matrix
+    solution = psMatrixLUSolve(solution, luMatrix, vector, permutation); // Solution in x
+    psTrace("pois.solveEquation", 1, "Background difference is %f\n", solution->data.F64[solution->n - 1]);
+
+#if 0
+    printf("Matrix:\n");
+    for (int i = 0; i < matrix->numCols; i++) {
+        for (int j = 0; j < matrix->numRows; j++) {
+            printf("%f ", matrix->data.F64[i][j]);
+        }
+        printf("\n");
+    }
+#endif
+
+    // Clean up
+    psFree(luMatrix);
+    psFree(permutation);
+    psFree(matrix);
+    psFree(vector);
+
+    return solution;
+}
Index: /tags/sj_tags/sj_root_20080929/pois/src/poisStamp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/src/poisStamp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/src/poisStamp.c	(revision 22322)
@@ -0,0 +1,81 @@
+// Copyright (C) 2006  Paul A. Price (price@ifa.hawaii.edu)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "pois.h"
+
+poisStamp *poisStampAlloc(void)
+{
+    poisStamp *stamp = (poisStamp*)psAlloc(sizeof(poisStamp));
+    stamp->x = 0;
+    stamp->y = 0;
+    stamp->status = POIS_STAMP_RESET;
+    stamp->matrix = NULL;
+    stamp->vector = NULL;
+    psMemSetDeallocator(stamp, (psFreeFunc)poisStampFree);
+
+    return stamp;
+}
+
+void poisStampFree(poisStamp *stamp)
+{
+    if (stamp->matrix) {
+        psFree(stamp->matrix);
+    }
+    if (stamp->vector) {
+        psFree(stamp->vector);
+    }
+    psFree(stamp);
+}
+
+
+// Return true if the stamp is OK
+bool poisCheckStamp(const psImage *image, // Image to check for threshold
+                    const psImage *mask, // Mask to check for bad pixels
+                    int x, int y,       // Pixel coordinates
+                    const poisConfig *config // Configuration
+    )
+{
+    if (x < config->footprint + config->xKernel ||
+        y < config->footprint + config->yKernel ||
+        x > config->xImage - config->footprint - config->xKernel ||
+        y > config->yImage - config->footprint - config->yKernel ||
+        image->data.F32[y][x] < config->threshold) {
+        psTrace("pois.checkStamp", 9, "Rejecting stamp at %d,%d (border/threshold)\n", x, y);
+        return false;
+    }
+
+    // Check the footprint
+    bool ok = true;                     // Is the footprint OK?
+    int footprint = config->footprint;  // Footprint size
+    for (int v = y - footprint; v <= y + footprint && ok; v++) {
+        for (int u = x - footprint; u <= x + footprint && ok; u++) {
+            if (mask->data.U8[v][u] &
+                (POIS_MASK_BAD | POIS_MASK_NEAR_BAD | POIS_MASK_STAMP)) {
+                psTrace("pois.checkStamp", 9, "Rejecting stamp at %d,%d (bad footprint)\n", x, y);
+                ok = false;
+            }
+        }
+    }
+
+    return ok;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pois/swig/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/swig/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/swig/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+Makefile Makefile.in
+*.pm *.pyc
+*_wrap.c
+poisSwig.py psLibSwig.py xpaSwig.py
+swigrun.py
+xpa.py
+.Makefile.depend
+*.la *.lo
+.deps .libs
Index: /tags/sj_tags/sj_root_20080929/pois/swig/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/swig/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/swig/Makefile.am	(revision 22322)
@@ -0,0 +1,82 @@
+AM_CFLAGS = @pois_CFLAGS@
+
+AM_CFLAGS += $(PSLIB_CFLAGS)
+AM_CFLAGS += $(SWIG_TARGET_CPPFLAGS)
+AM_CFLAGS += -DPS_ALLOW_MALLOC
+AM_CFLAGS += -I../src
+
+LIBS       = $(AM_LIBS)
+LIBS      += $(SWIG_TARGET_LIBS)
+
+SWIG += -w451
+SWIG += $(SWIG_TARGET_OPT)
+SWIG += $(PSLIB_CFLAGS)
+SWIG += -Drestrict= -Dinline=
+
+AM_CFLAGS += -I../src
+#
+# Libraries to install
+#
+SWIG_LIB_NAMES =     poisSwig       psLibSwig       xpaSwig
+lib_LTLIBRARIES = libpoisSwig.la libpsLibSwig.la libxpaSwig.la
+
+BUILT_SOURCES = \
+	psLibSwig.py psLibSwig_wrap.c \
+	xpaSwig.py xpaSwig_wrap.c
+#
+# PSLib wrappers
+#
+libpsLibSwig_la_LDFLAGS  = -module
+libpsLibSwig_la_LIBADD   = $(PSLIB_LIBS)
+
+libpsLibSwig_la_SOURCES = \
+	psLibSwig_wrap.c \
+	psLibForSwig.c psLibForSwig.h
+
+psLibSwig.py psLibSwig_wrap.c : psLibSwig.i p_psSwig.i
+	$(SWIG) $(PS_INCLUDES) psLibSwig.i
+#
+# Now for XPA
+#
+libxpaSwig_la_LIBADD       = $(PSLIB_LIBS)
+libxpaSwig_la_LIBADD      += -lxpa
+
+libxpaSwig_la_LDFLAGS = -module
+libxpaSwig_la_SOURCES = \
+	xpaSwig_wrap.c \
+	simpleFits.c
+
+xpaSwig.py xpaSwig_wrap.c : xpaSwig.i p_psSwig.i
+	$(SWIG) $(PS_INCLUDES) xpaSwig.i
+	@case "$(SWIG)" in \
+	  *-perl*) \
+		perl -pi -e 's/([a-zA-Z0-9_]+Array)/_xpa_\1/g' xpaSwig_wrap.c;; \
+	esac
+#
+# And the pois modules
+#
+libpoisSwig_la_LDFLAGS = -module
+libpoisSwig_la_SOURCES = \
+	poisSwig_wrap.c
+libpoisSwig_la_LIBADD = ../src/libpois.la
+
+poisSwig.py poisSwig_wrap.c : poisSwig.i ../src/pois.h p_psSwig.i
+	$(SWIG) $(PS_INCLUDES) -I../src poisSwig.i
+	@case "$(SWIG)" in \
+	  *-perl*) \
+		perl -pi -e 's/([a-zA-Z0-9_]+Array)/_pois_\1/g' poisLib_wrap.c;; \
+	esac
+#
+# Patch up name of .so file for the sake of python
+#
+install-exec-hook:
+	cd $(prefix)/lib && \
+	for f in $(SWIG_LIB_NAMES); do \
+	   $(LN_S) -f `perl -ne "if(/dlname='([^']+)'/) { print \\$$1; exit }" lib$$f.la` _$$f.so; \
+	done
+
+uninstall-local:
+	for f in $(SWIG_LIB_NAMES); do $(RM) $(prefix)/lib/_$$f.so; done
+
+CLEANFILES = Makefile Makefile.in \
+	*_wrap.c *.pyc poisSwig.py psLibSwig.py xpaSwig.py *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/pois/swig/p_psSwig.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/swig/p_psSwig.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/swig/p_psSwig.i	(revision 22322)
@@ -0,0 +1,267 @@
+// -*- panstarrs-c -*-
+%nodefault;
+
+%include "exception.i"
+#if !defined(SWIGXML)
+%include "typemaps.i"
+#endif
+%include "carrays.i"
+
+%array_class(int, intArray);
+%array_class(float, floatArray);
+%array_class(double, doubleArray);
+
+/************************************************************************************************************/
+
+#if defined(SWIGPYTHON)
+%pythoncode {
+class PsUtilsError(StandardError):
+	"""An error generated by psUtils"""
+}
+#endif
+
+/************************************************************************************************************/
+
+%{
+#  include "pslib.h"
+
+static void *exception(int exception_type, const char *fmt, ...)
+{
+    char message[256];
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsnprintf(message, 256, fmt, ap);
+    va_end(ap);
+
+    SWIG_exception(exception_type, message)
+
+ fail:					// used by SWIG_exception
+    return NULL;
+} 
+
+static void *ps_exception(void)
+{
+    psErr *err = psErrorLast();
+    char message[256];
+    snprintf(message, 256, "(%s) %s", err->name, err->msg);
+    psFree(err);
+
+    SWIG_exception(SWIG_RuntimeError, message)
+
+ fail:					// used by SWIG_exception
+    return NULL;
+} 
+
+#if 0
+static PyObject *set_thisown(PyObject *resultobj, int val)
+{
+    PyObject *n = PyInt_FromLong(val);
+    PyObject_SetAttrString(resultobj, (char *)"thisown", n);
+    Py_DECREF(n);
+
+    return resultobj;
+}
+#endif
+
+%}
+
+/************************************************************************************************************/
+/*
+ * Typemaps
+ */
+%typemap(python, out) psStatus {
+    if (result == 0) {
+	Py_INCREF(Py_None); resultobj = Py_None;
+    } else {
+	ps_exception();
+	return NULL;
+    }
+}
+
+%typemap(python,in) FILE * {
+    if ($input == Py_None) {
+	$1 = NULL;
+    } else if (!PyFile_Check($input)) {
+	PyErr_SetString(PyExc_TypeError, "Need a file!");
+	goto fail;
+    } else {
+	$1 = PyFile_AsFile($input);
+    }
+}
+
+%typemap(python,in) char * {
+    if ($input == Py_None) {
+	$1 = NULL;
+    } else if (!PyString_Check($input)) {
+	PyErr_SetString(PyExc_TypeError, "Need a string!");
+	goto fail;
+    } else {
+	$1 = PyString_AsString($input);
+    }
+}
+
+/************************************************************************************************************/
+/*
+ * Tell our extension language that this function returns a new object (that can't be NULL)
+ */
+%define NEWOBJECT(func)
+    %newobject func;
+    NOTNULL(func);
+%enddef
+
+/************************************************************************************************************/
+/*
+ * Tell our extension language that it should call psFree to delete an object
+ */
+%define EXPORT_TYPE(type)
+    %extend type {
+	~type() {
+	    if (psMemGetRefCounter(self) == 1) {
+		const psMemBlock *mb = ((psMemBlock *)self) - 1;
+
+#if !defined(PS_NO_TRACE)
+		p_psTrace("ps.utils.memory", 6, "Freeing %p (%ld: %s:%d)\n", self, (long)mb->id,
+			  mb->file, mb->lineno);
+#endif
+	    }
+	    psFree(self);
+	}
+    }
+
+    %{
+        type *type ## _Cast(void *ptr) {
+	    return (type *)ptr;
+	}
+    %}
+    type *type ## _Cast(void *ptr);
+
+    NEWOBJECT(type ## Alloc);
+%enddef
+
+/************************************************************************************************************/
+/*
+ * Throw an exception if func returns NULL
+ */
+%define NOTNULL(func)
+    %exception func {
+        $action;
+	if (result == NULL) {
+	    $cleanup;
+#if defined(SWIGPERL) 
+	    ps_exception();
+#else
+	    return ps_exception();
+#endif
+	}
+    }
+%enddef
+
+/*
+ * Throw an exception if func returns a negative value
+ */
+%define NOTNEGATIVE(func)
+    %exception func {
+        $action;
+	if (result < 0) {
+	    $cleanup;
+#if defined(SWIGPERL) 
+	    ps_exception();
+#else
+	    return ps_exception();
+#endif
+	}
+    }
+%enddef
+
+/*
+ * Throw an exception if func returns non-zero
+ */
+%define ISZERO(func)
+    %exception func {
+        $action;
+	if (result != 0) {
+	    $cleanup;
+#if defined(SWIGPERL) 
+	    ps_exception();
+#else
+	    return ps_exception();
+#endif
+	}
+    }
+%enddef
+
+/************************************************************************************************************/
+/*
+ * If an argument is passed in, and also returned by the function, we need to declare
+ * the function %newobject, and also ensure that the input function has its refCounter
+ * incremented (as perl/python etc. will think that it's gone out of scope)
+ *
+ * This macro defined a typemap to handle the refcounter.
+ *
+ * Do this as an argout map as we don't get another reference to the image if there's an error
+ */
+%define TYPE_INOUT(type, argname)
+    %typemap(argout) type argname {
+        if ($1 != NULL) {
+	    const psMemBlock *mb = ((psMemBlock *)$1) - 1;
+	    p_psTrace("ps.utils.memory", 4, "Incrementing reference counter for %p (%ld %s:%d)\n",
+		      $1, (long)mb->id, mb->file, mb->lineno);
+	    psMemIncrRefCounter($1);
+	}
+    }
+%enddef
+
+%define CLEAR_TYPE_INOUT(type, argname)
+    %typemap(argout) type argname;
+%enddef
+
+/************************************************************************************************************/
+
+%exception;
+
+/************************************************************************************************************/
+/*
+ * Accept a python list of floats if the routine wants an array
+ */
+%typemap(in) (const float *floatArr, int nel) {
+    /* Check if is a list */
+    if (PyList_Check($input)) {
+	$2 = PyList_Size($input);
+	if ($2 == 0) {
+	    $1 = NULL;
+	} else {
+	    $1 = psAlloc($2*sizeof(float));
+	
+	    for (int i = 0; i < $2; i++) {
+		PyObject *o = PyList_GetItem($input,i);
+		if (PyFloat_Check(o))
+		    $1[i] = PyFloat_AsDouble(PyList_GetItem($input, i));
+		else {
+		    PyErr_SetString(PyExc_TypeError, "list must contain floats");
+		    psFree($1);
+		    return NULL;
+		}
+	    }
+	}
+    } else {
+	PyErr_SetString(PyExc_TypeError,"not a list");
+	return NULL;
+    }
+}
+
+%typemap(freearg) (const float *floatArr, int nel) {
+    psFree($1);
+}
+
+/************************************************************************************************************/
+
+%apply double *OUTPUT { double *irow };
+%apply double *OUTPUT { double *icol };
+%apply double *OUTPUT { double *xPos };
+%apply double *OUTPUT { double *yPos };
+%apply double *OUTPUT { double *alpha };
+%apply double *OUTPUT { double *delta };
+%apply double *OUTPUT { double *alt };
+%apply double *OUTPUT { double *az };
+%apply double *OUTPUT { double *lst };
Index: /tags/sj_tags/sj_root_20080929/pois/swig/poisSwig.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/swig/poisSwig.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/swig/poisSwig.i	(revision 22322)
@@ -0,0 +1,33 @@
+// -*- C -*-
+/*
+ */
+%module poisSwig
+
+%{
+#include "pois.h"
+%}
+
+%include "p_psSwig.i"
+%import "psLibSwig.i"
+
+EXPORT_TYPE(poisConfig);
+EXPORT_TYPE(poisStamp);
+EXPORT_TYPE(poisKernelBasis);
+NEWOBJECT(poisKernelBasisFunctions);
+NEWOBJECT(poisMakeMask);
+NEWOBJECT(poisFindStamps);
+NEWOBJECT(poisExtractKernel);
+NEWOBJECT(poisConvolveImage);
+NEWOBJECT(poisCalculateDeviations);
+NEWOBJECT(poisSolveEquation);
+%newobject poisRejectStamps;
+NEWOBJECT(poisImageSetVal);
+NEWOBJECT(poisImageSetValInMask);
+
+TYPE_INOUT(psArray *, stampsIO);
+TYPE_INOUT(psImage *, IO);
+TYPE_INOUT(psVector *, solutionIO)
+%include "pois.h"
+CLEAR_TYPE_INOUT(psArray *, stampsIO);
+CLEAR_TYPE_INOUT(psImage *, IO);
+CLEAR_TYPE_INOUT(psVector *, solutionIO)
Index: /tags/sj_tags/sj_root_20080929/pois/swig/psLibForSwig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/swig/psLibForSwig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/swig/psLibForSwig.c	(revision 22322)
@@ -0,0 +1,41 @@
+/*
+ * Wrappers for psLib
+ */
+#include <stdio.h>
+#include "pslib.h"
+
+void
+psErrorStackPrintForSwig(void)
+{
+    psErrorStackPrint(stdout, "");
+}
+
+const psErr *psErrorGetForSwig(int i)
+{
+    const psErr *err = psErrorGet(i);
+
+    return (err->code == PS_ERR_NONE ? NULL : err);
+}
+
+/************************************************************************************************************/
+
+#if defined(psTrace)
+#   undef psTrace
+#endif
+
+void psTrace(const char *comp,		// component being traced
+	     int level,			// desired trace level
+	     char *str)			// output string
+{
+    p_psTrace(comp, level, str);
+}
+
+/************************************************************************************************************/
+/*
+ * SWIG doesn't like varargs functions; so provide non-varargs versions of psMetadataAdd
+ */
+bool psMetadataAddBool(psMetadata* restrict md, int where, const char *name, const char *comment, int val)
+{
+    return psMetadataAdd(md, where, name, PS_TYPE_BOOL, comment, val);
+}
+
Index: /tags/sj_tags/sj_root_20080929/pois/swig/psLibForSwig.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/swig/psLibForSwig.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/swig/psLibForSwig.h	(revision 22322)
@@ -0,0 +1,14 @@
+#if !defined(PS_LIB_FOR_SWIG_H)
+#define PS_LIB_FOR_SWIG_H
+
+void psErrorStackPrintForSwig(void);
+const psErr *psErrorGetForSwig(int i);
+
+#if defined(psTrace)
+#   undef psTrace
+#endif
+void psTrace(const char *comp, int level, char *str);
+
+bool psMetadataAddBool(psMetadata* restrict md, int where, const char *name, const char *comment, int val);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/pois/swig/psLibSwig.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/swig/psLibSwig.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/swig/psLibSwig.i	(revision 22322)
@@ -0,0 +1,352 @@
+// -*- C -*-
+%module psLibSwig
+
+%include "p_psSwig.i"
+
+%{
+#  include "pslib.h"
+#if 0
+#  include "Private/p_psMemory.h"	/* not available from prototype psLib include files (in src dir) */
+#else
+#  define P_PS_MEMMAGIC (void *)0xdeadbeef // Magic number in psMemBlock header
+#endif
+#  include "psLibForSwig.h"
+%}
+
+/************************************************************************************************************/
+/*
+ * Typemaps
+ */
+%typemap(in) FILE * {
+#if defined(SWIGPERL)
+   if (!SvOK($input)) {
+      $1 = NULL;      
+   } else {
+      $1 = PerlIO_findFILE(IoIFP(sv_2io($input)));
+   }
+#elif defined(SWIGPYTHON)
+    if ($input == Py_None) {
+	$1 = NULL;
+    } else if (!PyFile_Check($input)) {
+	PyErr_SetString(PyExc_TypeError, "Need a file!");
+	goto fail;
+    } else {
+	$1 = PyFile_AsFile($input);
+    }
+#endif
+}
+
+/************************************************************************************************************/
+
+%typemap(in) psMemBlock ***arr (psMemBlock **arr) {
+#if defined(SWIGPERL)
+   if (!SvOK($input)) {
+      $1 = NULL;
+   } else {
+      arr = NULL;
+      $1 = &arr;
+   }
+
+#elif defined(SWIGPYTHON)
+    if ($input == Py_None) {
+	$1 = NULL;
+    } else {
+	if (!PyList_Check($input)) {
+	    SWIG_exception(SWIG_RuntimeError, "\"$1_name\" isn't a list");
+	}
+	
+	arr = NULL;
+	$1 = &arr;
+    }
+#endif
+}
+
+%typemap(in) psMetadata ** (psMetadata *) {
+    if
+#if defined(SWIGPYTHON)
+       ($input == Py_None)
+#else
+       ($input == NULL)
+#endif
+    {
+	$1 = NULL;
+    } else {
+        static $*1_type arr[1];
+        if ((SWIG_ConvertPtr($input, (void **) &$1, $*1_descriptor, 1)) == -1) return NULL;
+	arr[0] = ($*1_type)$1;
+	$1 = arr;
+    }
+}
+
+/*
+ * Return a (psMemBlock ***) as a list of tuples; they are _appended_ to the pre-existing list. E.g.
+ *
+ *     arr = []; IPP.psMemCheckLeaks(0, arr, None, False)
+ *     for el in arr:
+ *         print el
+ */
+%typemap(argout) psMemBlock ***arr {
+    if ($1 != NULL) {
+	psMemBlock **arr = *$1;
+	
+	for (int i = 0; i < result; i++) {
+	    const psMemBlock *m = arr[i];
+#if defined(SWIGPERL)
+	    SV *sv = newSVpvf("%d:%ld:%s:%ld:%d:%d:%ld",
+			      (m->startblock == P_PS_MEMMAGIC) ? 1 : 0, (long)m->id,
+			      m->file, (long)m->lineno, (int)m->refCounter,
+			      (m->endblock == P_PS_MEMMAGIC) ? 1 : 0, (long)(m + 1));
+
+	    if (SvROK($input)) {
+	       AV *av = (AV *)SvRV($input);
+	       if (SvTYPE(av) != SVt_PVAV) {
+		  SWIG_croak("Type error in argument $argnum of $symname; Expected an array of psMemBlock");
+	       }
+
+	       av_push(av, sv);
+	    } else {
+	       SWIG_croak("Type error in argument $argnum of $symname; Expected a reference to an array of psMemBlock");
+	    }
+#elif defined(SWIGPYTHON)
+	    const int nel = 7;
+	    PyObject *ty = PyTuple_New(nel);
+	    int j = 0;
+	    PyTuple_SET_ITEM(ty, j++, PyInt_FromLong(m->startblock == P_PS_MEMMAGIC));
+	    PyTuple_SET_ITEM(ty, j++, PyInt_FromLong(m->id));
+	    PyTuple_SET_ITEM(ty, j++, PyString_FromString(m->file));
+	    PyTuple_SET_ITEM(ty, j++, PyInt_FromLong(m->lineno));
+	    PyTuple_SET_ITEM(ty, j++, PyInt_FromLong(m->refCounter));
+	    PyTuple_SET_ITEM(ty, j++, PyInt_FromLong(m->endblock == P_PS_MEMMAGIC));
+	    PyTuple_SET_ITEM(ty, j++, PyInt_FromLong((long)(m + 1)));
+	    assert (j == nel);
+	    
+	    PyList_Append($input, ty);
+#endif
+	}
+	
+	psFree(arr);
+    }
+}
+
+/************************************************************************************************************/
+%{
+typedef struct { float re, im; } swig_psC32;  ///< 32-bit complex floating point
+typedef struct { double re, im; } swig_psC64; ///< 64-bit complex floating point
+%}
+
+#if 0
+%include <stdint.h>			// SWIG has trouble with nested typedefs in this file
+					// at least on Darwin machines
+#else
+typedef unsigned char  uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int   uint32_t;
+typedef unsigned long  uint64_t;
+typedef char           int8_t;
+typedef short          int16_t;
+typedef int            int32_t;
+typedef long           int64_t;
+#endif
+
+typedef int psBool;
+typedef void *psPtr;
+typedef uint8_t psU8;                  ///< 8-bit unsigned int
+typedef uint16_t psU16;                ///< 16-bit unsigned int
+typedef uint32_t psU32;                ///< 32-bit unsigned int
+typedef uint64_t psU64;                ///< 64-bit unsigned int
+typedef int8_t psS8;                   ///< 8-bit signed int
+typedef int16_t psS16;                 ///< 16-bit signed int
+typedef int32_t psS32;                 ///< 32-bit signed int
+typedef int64_t psS64;                 ///< 64-bit signed int
+typedef float psF32;                   ///< 32-bit floating point
+typedef double psF64;                  ///< 64-bit floating point
+typedef struct { float re, im; } swig_psC32;  ///< 32-bit complex floating point
+typedef struct { double re, im; } swig_psC64; ///< 64-bit complex floating point
+
+%typemap(in) psC32 = float;
+%typemap(in) psC64 = double;
+
+typedef enum {
+    PS_TYPE_S8 = 0x0101,               ///< Character.
+    PS_TYPE_S16 = 0x0102,              ///< Short integer.
+    PS_TYPE_S32 = 0x0104,              ///< Integer.
+    PS_TYPE_S64 = 0x0108,              ///< Long integer.
+    PS_TYPE_U8 = 0x0301,               ///< Unsigned character.
+    PS_TYPE_U16 = 0x0302,              ///< Unsigned short integer.
+    PS_TYPE_U32 = 0x0304,              ///< Unsigned integer.
+    PS_TYPE_U64 = 0x0308,              ///< Unsigned long integer.
+    PS_TYPE_F32 = 0x0404,              ///< Single-precision Floating point.
+    PS_TYPE_F64 = 0x0408,              ///< Double-precision floating point.
+    PS_TYPE_C32 = 0x0808,              ///< Complex numbers consisting of single-precision floating point.
+    PS_TYPE_C64 = 0x0810              ///< Complex numbers consisting of double-precision floating point.
+} psElemType;
+
+typedef enum {
+    PS_DIMEN_SCALAR,            ///< Scalar.
+    PS_DIMEN_VECTOR,            ///< Vector.
+    PS_DIMEN_TRANSV,            ///< Transposed vector.
+    PS_DIMEN_IMAGE,             ///< Image.
+    PS_DIMEN_OTHER              ///< Something else that's not supported for arithmetic.
+} psDimen;
+
+typedef struct {
+    psElemType type;            ///< Primitive type.
+    psDimen dimen;              ///< Dimensionality.
+}
+psType;
+
+%include "psErrorCodes.h"
+%include "psLogMsg.h"
+%include "psMemory.h"
+%ignore psTraceFree;
+%include "psTrace.h"
+
+EXPORT_TYPE(psList);
+%include "psList.h"
+
+EXPORT_TYPE(psImage);
+EXPORT_TYPE(psRegion);
+%typemap(memberin) psImage * {
+   psFree($1);
+   $1 = $input;
+#if defined(SWIGPERL)
+   p_psMemIncrRefCounter($1, "memberin(psImage *)", 0);
+#endif
+}
+NEWOBJECT(psImageReadSection);
+NEWOBJECT(psImageSubset);
+NEWOBJECT(psImageSubsection);
+NEWOBJECT(psImageCopy);
+NOTNULL(psImageTrim);
+%include "psImage.h"
+%include "psImageIO.h"
+%include "psImageExtraction.h"
+
+EXPORT_TYPE(psMetadata)
+%typemap(memberin) psMetadata * {
+   psFree($1);
+   $1 = $input;
+#if defined(SWIGPERL)
+   p_psMemIncrRefCounter($1, "memberin(psMetadata *)", 0);
+#endif
+}
+
+%include "psMetadata.h"
+
+%apply int *OUTPUT { psU32 *nFail }
+TYPE_INOUT(psMetadata *, md);
+NEWOBJECT(psMetadataParseConfig);
+NOTNULL(psMetadataLookup);
+%include "psMetadataIO.h"
+CLEAR_TYPE_INOUT(psMetadata *, md);
+
+%extend psMetadataItem {
+    const char *get_STR(void) {
+       if (self->type != PS_META_STR) {
+	  return NULL;
+       } else {
+	  return self->data.V;
+       }
+    }
+}
+
+%include "psLibForSwig.h"
+
+EXPORT_TYPE(psHash);
+%include "psHash.h"
+
+EXPORT_TYPE(psErr);
+NEWOBJECT(psErrorGet);
+NEWOBJECT(psErrorLast);
+%include "psError.h"
+
+EXPORT_TYPE(psArray);
+%extend psArray {
+    const void *get_data(int i) {
+	return self->data[i];
+    }
+
+    const psCell *get_cell(int i) {
+        return (psCell *)(self->data[i]);
+    }
+    const psChip *get_chip(int i) {
+        return (psChip *)(self->data[i]);
+    }
+    const psReadout *get_readout(int i) {
+        return (psReadout *)(self->data[i]);
+    }
+}
+%include "psArray.h"
+
+// N.b. assumes e.g. %array_class(int, intArray);
+%define GET_CARRAY(structType, ctype, typeName)
+   %extend structType {
+      ctype ## Array *get_data_ ## typeName(void) {
+	 if (self->type.type == PS_TYPE_ ## typeName) {
+	    return self->data.typeName;
+	 } else {
+	    psError(PS_ERR_BAD_PARAMETER_TYPE, 1, "psVector is of type %d, not PS_TYPE_" "typeName",
+		    self->type.type);
+	    return NULL;
+	 }
+      }
+   }
+%enddef
+
+EXPORT_TYPE(psVector);
+%include "psVector.h"
+GET_CARRAY(psVector, float,  F32);
+GET_CARRAY(psVector, double, F64);
+   
+EXPORT_TYPE(psObservatory);
+EXPORT_TYPE(psFPA);
+EXPORT_TYPE(psChip);
+EXPORT_TYPE(psCell);
+EXPORT_TYPE(psReadout);
+EXPORT_TYPE(psExposure);
+%include "psAstrometry.h"
+
+%extend psFPA {
+    void set_chip(int i, psChip *chip) {
+        self->chips->data[i] = chip;
+	p_psMemIncrRefCounter(chip, "set_chip", 0);
+    }
+}
+%extend psChip {
+    void set_cell(int i, psCell *cell) {
+        self->cells->data[i] = cell;
+	p_psMemIncrRefCounter(cell, "set_cell", 0);	
+    }
+}
+%extend psCell {
+   char *extname;
+    void set_readout(int i, psReadout *readout) {
+        self->readouts->data[i] = readout;
+	p_psMemIncrRefCounter(readout, "set_readout", 0);	
+    }
+}
+%{
+   char *psCell_extname_get(psCell *cell) { return NULL; }
+   void psCell_extname_set(psCell *cell, char *val) { return; }
+%}
+
+NEWOBJECT(psBinaryOp);
+TYPE_INOUT(void *, out);
+psImage *psBinaryOp(void *out, void *in1, char *op, void *in2);
+CLEAR_TYPE_INOUT(void *, out);
+%include "psScalar.h"
+
+EXPORT_TYPE(psTime);
+NEWOBJECT(psTimeGetTime);
+%include "psTime.h"
+
+EXPORT_TYPE(psFits);
+NEWOBJECT(psFitsReadImage);
+TYPE_INOUT(psImage *, out);
+%include "psFits.h"
+CLEAR_TYPE_INOUT(psImage *, out);
+
+EXPORT_TYPE(psStats);
+EXPORT_TYPE(psHistogram);
+%include "psStats.h"
+%include "psImageStats.h"
Index: /tags/sj_tags/sj_root_20080929/pois/swig/simpleFits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/swig/simpleFits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/swig/simpleFits.c	(revision 22322)
@@ -0,0 +1,678 @@
+/*
+ * Write a fits image to a file descriptor; useful for talking to DS9
+ *
+ * This version knows about psLib data structures
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "pslib.h"
+#include "poisErrorCodes.h"
+
+#include "simpleFits.h"
+   
+#define FITS_SIZE 2880
+
+typedef struct {
+    char keyword[9];
+    enum {
+	UNKNOWN_CARD = -1,
+	LOGICAL_CARD,
+	STRING_CARD,
+	INTEGER_CARD,
+	DOUBLE_CARD,
+	FLOAT_CARD,
+    } type;
+    union {
+	char *l;
+	char *s;
+	int i;
+	double d;
+	float f;      
+    } val;
+    char *commnt;
+} CARD;
+      
+
+/*
+ * Write a string value
+ */
+static int
+write_card_s(int fd,
+	     int ncard,
+	     char *record,
+	     const char *keyword,
+	     const char *val,
+	     const char *commnt)
+{
+    char *card = &record[80*ncard];
+    char value[20];			/* blank-padded val, if needed */
+
+    if(strlen(val) < 8) {		/* FITS requires at least 8 chars */
+	sprintf(value, "%-8s", val);
+	val = value;
+    }
+
+    if(*keyword == '\0' ||
+       !strcmp(keyword,"COMMENT") || !strcmp(keyword,"END") || !strcmp(keyword,"HISTORY")) {
+	sprintf(card,"%-8.8s%-72s",keyword,val);
+    } else {
+	sprintf(card,"%-8.8s= '%s' %c%-*s",keyword,val,
+		(commnt[0] == '\0' ? ' ' : '/'),
+		(int)(80 - 14 - strlen(val)), commnt);
+    }
+/*
+ * Write record if full
+ */
+    if(++ncard == 36) {
+	if(write(fd, record, FITS_SIZE) != FITS_SIZE) {
+	    psError(POIS_ERR_FITS, true,"Cannot write header record\n");
+	}
+	ncard = 0;
+    }
+   
+    return ncard;
+}   
+
+/*****************************************************************************/
+/*
+ * Write an integer (%d)
+ */
+static int
+write_card_i(int fd,
+	     int ncard,
+	     char *record,
+	     const char *keyword,
+	     const int val,
+	     const char *commnt)
+{
+    char *card = &record[80*ncard];
+
+    sprintf(card,"%-8.8s= %20d %c%-48s",keyword,val,
+	    (commnt[0] == '\0' ? ' ' : '/'),commnt);
+/*
+ * Write record if full
+ */
+    if(++ncard == 36) {
+	if(write(fd, record, FITS_SIZE) != FITS_SIZE) {
+	    psError(POIS_ERR_FITS, true,"Cannot write header record\n");
+	}
+	ncard = 0;
+    }
+   
+    return ncard;
+}   
+
+/*****************************************************************************/
+/*
+ * Write a logical value
+ */
+static int
+write_card_l(int fd,
+	     int ncard,
+	     char *record,
+	     const char *keyword,
+	     const char *val,
+	     const char *commnt)
+{
+    char *card = &record[80*ncard];
+
+    if(strcmp(val,"T") != 0 && strcmp(val,"F") != 0) {
+	psError(POIS_ERR_FITS, true,"Invalid logical %s for keyword %s\n",val,keyword);
+	val = "?";
+    }
+    sprintf(card,"%-8.8s= %20s %c%-48s",keyword,val,
+	    (commnt[0] == '\0' ? ' ' : '/'),commnt);
+/*
+ * Write record if full
+ */
+    if(++ncard == 36) {
+	if(write(fd, record, FITS_SIZE) != FITS_SIZE) {
+	    psError(POIS_ERR_FITS, true,"Cannot write header record\n");
+	}
+	ncard = 0;
+    }
+   
+    return ncard;
+}   
+
+/*****************************************************************************/
+/*
+ * Write a double floating value
+ */
+static int
+write_card_d(int fd,
+	     int ncard,
+	     char *record,
+	     const char *keyword,
+	     const double val,
+	     const char *commnt)
+{
+    char *card = &record[80*ncard];
+
+    sprintf(card,"%-8.8s= %20g %c%-48s",keyword,val,
+	    (commnt[0] == '\0' ? ' ' : '/'),commnt);
+/*
+ * Write record if full
+ */
+    if(++ncard == 36) {
+	if(write(fd, record, FITS_SIZE) != FITS_SIZE) {
+	    psError(POIS_ERR_FITS, true,"Cannot write header record\n");
+	}
+	ncard = 0;
+    }
+   
+    return ncard;
+}   
+
+/*****************************************************************************/
+/*
+ * Write a floating value
+ */
+static int
+write_card_f(int fd,
+	     int ncard,
+	     char *record,
+	     const char *keyword,
+	     const float val,
+	     const char *commnt)
+{
+    char *card = &record[80*ncard];
+
+    sprintf(card,"%-8.8s= %20g %c%-48s",keyword,val,
+	    (commnt[0] == '\0' ? ' ' : '/'),commnt);
+/*
+ * Write record if full
+ */
+    if(++ncard == 36) {
+	if(write(fd, record, FITS_SIZE) != FITS_SIZE) {
+	    psError(POIS_ERR_FITS, true,"Cannot write header record\n");
+	}
+	ncard = 0;
+    }
+   
+    return ncard;
+}   
+
+/*****************************************************************************/
+/*
+ * Write a CARD
+ */
+static int
+write_card(int fd,
+	   int ncard,
+	   char *record,
+	   const CARD *card)
+{
+    switch (card->type) {
+      case UNKNOWN_CARD:		// we should already have complained
+	return ncard;
+      case LOGICAL_CARD:
+	return write_card_l(fd, ncard, record, card->keyword, card->val.l, card->commnt);
+      case STRING_CARD:
+	return write_card_s(fd, ncard, record, card->keyword, card->val.s, card->commnt);
+      case INTEGER_CARD:
+	return write_card_i(fd, ncard, record, card->keyword, card->val.i, card->commnt);
+      case DOUBLE_CARD:
+	return write_card_d(fd, ncard, record, card->keyword, card->val.d, card->commnt);
+      case FLOAT_CARD:
+	return write_card_f(fd, ncard, record, card->keyword, card->val.f, card->commnt);
+    }
+    printf("Impossible type for card %s, %d\n", card->keyword, card->type);
+    abort();
+    return ncard;			/* NOTREACHED */
+}
+
+/*****************************************************************************/
+/*
+ * Byte swap ABABAB -> BABABAB in place
+ */
+static void swap_2(char *arr,		// array to swap
+		   int n)		// number of bytes
+{
+    char *end,
+	t;
+
+    if(n%2 != 0) {
+	psError(POIS_ERR_FITS, true,"Attempt to byte swap odd number of bytes: %d\n",n);
+	n = 2*(int)(n/2);
+    }
+
+    for(end = arr + n;arr < end;arr += 2) {
+	t = arr[0];
+	arr[0] = arr[1];
+	arr[1] = t;
+    }
+}
+
+/*
+ * Byte swap ABCDABCD -> DCBADCBA in place (e.g. sun <--> vax)
+ */
+static void swap_4(char *arr,		// array to swap
+		   int n)		// number of bytes
+{
+    char *end,
+	t;
+
+    if(n%4 != 0) {
+	psError(POIS_ERR_FITS, true,"Attempt to byte swap non-multiple of 4 bytes: %d\n",n);
+	n = 4*(int)(n/4);
+    }
+
+    for(end = arr + n;arr < end;arr += 4) {
+	t = arr[0];
+	arr[0] = arr[3];
+	arr[3] = t;
+	t = arr[1];
+	arr[1] = arr[2];
+	arr[2] = t;
+    }
+}
+
+/*
+ * Byte swap ABCDEFGH -> HGFEDCBA in place (e.g. sun <--> vax)
+ */
+static void swap_8(char *arr,		// array to swap
+		   int n)		// number of bytes
+{
+    char *end,
+	t;
+
+    if(n%8 != 0) {
+	psError(POIS_ERR_FITS, true,"Attempt to byte swap non-multiple of 8 bytes: %d\n",n);
+	n = 8*(int)(n/8);
+    }
+
+    for(end = arr + n;arr < end;arr += 8) {
+	t = arr[0];
+	arr[0] = arr[7];
+	arr[7] = t;
+	t = arr[1];
+	arr[1] = arr[6];
+	arr[6] = t;
+	t = arr[2];
+	arr[2] = arr[5];
+	arr[5] = t;
+	t = arr[3];
+	arr[3] = arr[4];
+	arr[4] = t;
+    }
+}
+
+/*****************************************************************************/
+
+static int
+write_fits_hdr(int fd,
+	       int bitpix,
+	       int naxis,
+	       int *naxes,
+	       CARD **cards,		/* extra header cards */
+	       const psMetadata *metaData, /* metadata to write; or NULL */
+	       int primary)		/* is this the primary HDU? */
+{
+    int i;
+    int ncard;				/* number of cards written */
+    char record[FITS_SIZE + 1];		/* write buffer */
+   
+    ncard = 0;
+    if(primary) {
+	ncard = write_card_l(fd,ncard,record,"SIMPLE","T","");
+    } else {
+	ncard = write_card_s(fd,ncard,record,"XTENSION","IMAGE","");
+    }
+    ncard = write_card_i(fd,ncard,record,"BITPIX",bitpix,"");
+    ncard = write_card_i(fd,ncard,record,"NAXIS",naxis,"");
+    for(i = 0; i < naxis; i++) {
+	char key[] = "NAXIS.";
+	sprintf(key, "NAXIS%d", i + 1);
+	ncard = write_card_i(fd,ncard,record,key,naxes[i],"");
+    }
+    if(primary) {
+	ncard = write_card_l(fd,ncard,record,
+			     "EXTEND","T","There may be extensions");
+    }
+/*
+ * Write extra header cards
+ */
+    if(cards != NULL) {
+	while(*cards != NULL) {
+	    ncard = write_card(fd,ncard,record,*cards);
+	    cards++;
+	}
+    }
+    /*
+     * Is there metadata to write?
+     */
+    if (metaData != NULL) {
+	CARD card;			    // used to write metadata to
+	card.keyword[8] = '\0';		    // we\'ll be using strncpy, so need to guarantee NUL termination
+	psMetadataItem *meta = NULL;	    // used to traverse a set of metadata
+	int new_error = 1;		    // is this a new error?
+
+
+	psListIterator *iter = psListIteratorAlloc(metaData->list, PS_LIST_HEAD, false); 
+	while ((meta = psListGetAndIncrement(iter)) != NULL) {
+	    strncpy(card.keyword, meta->name, 8);
+	    card.commnt = meta->comment;
+	    switch (meta->type) {
+	      case PS_META_F64:
+		card.type = DOUBLE_CARD;
+		card.val.d = meta->data.F64;
+		break;
+	      case PS_META_F32:
+		card.type = FLOAT_CARD;
+		card.val.f = meta->data.F32;
+		break;
+	      case PS_META_S32:
+		card.type = INTEGER_CARD;
+		card.val.i = meta->data.S32;
+		break;
+	      case PS_META_STR:
+		card.type = STRING_CARD;
+		card.val.s = meta->data.V;
+		break;
+	      default:
+		card.type = UNKNOWN_CARD;
+		psError(POIS_ERR_FITS, new_error,
+			"I don't know how to write metadata of type %d", meta->type);
+		new_error = 0;
+		break;
+	    }
+	    if (card.type != UNKNOWN_CARD) {
+		ncard = write_card(fd, ncard, record, &card);
+	    }
+	}
+	psFree(iter);
+    }
+
+    ncard = write_card_s(fd,ncard,record,"END","","");
+    while(ncard != 0) {
+	ncard = write_card_s(fd,ncard,record,"","","");
+    }
+
+    return 0;
+}
+
+/*
+ * Pad out to a FITS record boundary
+ */
+static void pad_to_fits_record(int fd,		// output file descriptor
+			       int npixel,	// number of pixels already written to HDU
+			       int bitpix)	// bitpix for this datatype
+{
+    const int bytes_per_pixel = (bitpix > 0 ? bitpix : -bitpix)/8;
+    int nbyte = npixel*bytes_per_pixel;
+    
+    if(nbyte%FITS_SIZE != 0) {
+	char record[FITS_SIZE + 1];	/* write buffer */
+	
+	nbyte = FITS_SIZE - nbyte%FITS_SIZE;
+	memset(record, ' ', nbyte);
+	if(write(fd, record, nbyte) != nbyte) {
+	    psError(POIS_ERR_FITS, true, "error padding file to multiple of fits block size\n");
+	}	       
+    }
+}
+
+static int write_fits_data(int fd,
+			   int bitpix,
+			   int npix,
+			   char *data,
+			   int pad_record) // Pad data out to a FITS record boundary?
+{
+    char *buff = NULL;			/* I/O buffer */
+    const int bytes_per_pixel = (bitpix > 0 ? bitpix : -bitpix)/8;
+    int nbyte = npix*bytes_per_pixel;
+    int swap_bytes = 0;			/* the default */
+    static int warned = 0;		/* Did we warn about BZERO/BSCALE? */
+
+#if PS_BIGENDIAN == 0			/* we'll need to byte swap FITS */
+    if(bytes_per_pixel > 1) {
+	swap_bytes = 1;
+    }
+#endif
+
+    if(swap_bytes) {
+	if((buff = psAlloc(FITS_SIZE*bytes_per_pixel)) == NULL) {
+	    psError(POIS_ERR_FITS, true,"Can't allocate storage for buff\n");
+	    return -1;
+	}
+    }
+    
+    if(bytes_per_pixel == 2 && !warned) {
+	warned = 1;
+	fprintf(stderr,"Worry about BZERO/BSCALE\n");
+    }
+   
+    while(nbyte > 0) {
+	int nwrite = (nbyte > FITS_SIZE) ? FITS_SIZE : nbyte;
+      
+	if(swap_bytes) {
+	    memcpy(buff, data, nwrite);
+	    if(bytes_per_pixel == 2) {
+		swap_2((char *)buff, nwrite);
+	    } else if(bytes_per_pixel == 4) {
+		swap_4((char *)buff, nwrite);
+	    } else if(bytes_per_pixel == 8) {
+	        swap_8((char *)buff, nwrite);
+	    } else {
+		fprintf(stderr,"You cannot get here\n");
+		abort();
+	    }
+	} else {
+	    buff = data;
+	}
+      
+	if(write(fd, buff, nwrite) != nwrite) {
+	    perror("Error writing image: ");
+	    break;
+	}
+
+	nbyte -= nwrite;
+	data += nwrite;
+    }
+    if (nbyte == 0) {			/* no write errors */
+	if (pad_record) {
+	    pad_to_fits_record(fd, npix, bitpix);
+	}
+    }
+   
+    if(swap_bytes) {
+	psFree(buff);
+    }
+
+    return (nbyte == 0 ? 0 : -1);
+}
+
+int
+rhlWriteFits(int fd,			/* file descriptor to write to */
+	     const psImage *data,	/* The data to write */
+	     const psMetadata *meta,	/* associated metadata */
+	     const char *WCS)		/* name of WCS for pixel coordinates */
+{
+    CARD **cards = NULL;		/* extra header info */
+    int CRVAL1 = 0, CRVAL2 = 0;		/* card indices for keywords */
+    int i;
+    const int naxis = 2;		// == NAXIS
+    int naxes[2];			/* values of NAXIS1 etc */
+    int ncard = 12;			/* number of extra header cards */
+
+    psErrorClear();			// clear error stack
+    /*
+     * Does the data have a WCS? If so, call our pixel-based WCS "A"
+     */
+    if (WCS != NULL && *WCS == '\0') {
+	WCS = NULL;
+    }
+    if (WCS == NULL &&
+	meta != NULL && psMetadataLookup((psMetadata *)meta, "CTYPE1") != NULL) {
+	WCS = "A";
+    } else {
+	psErrorClear();			// set by psMetadataLookup XXX
+	WCS = "";
+    }
+    /*
+     * What sort if image is it?
+     */
+    int bitpix;				// == BITPIX
+    char **rows = NULL;
+    switch (data->type.type) {
+      case PS_TYPE_F64:
+	bitpix = -64;
+	rows = (char **)data->data.F64;
+	break;
+      case PS_TYPE_F32:
+	bitpix = -32;
+	rows = (char **)data->data.F32;
+	break;
+      case PS_TYPE_U16:
+	bitpix = -16;
+	rows = (char **)data->data.U16;
+	break;
+      case PS_TYPE_S8:
+	bitpix = 8;
+	rows = (char **)data->data.S8;
+	break;
+      case PS_TYPE_U8:
+	bitpix = 8;
+	rows = (char **)data->data.U8;
+	break;
+      default:
+	bitpix = 0;			// make gcc happy
+	psAbort(__func__, "Unsupported image data type: %d", data->type.type);
+	break;				// NOTREACHED
+    }
+    assert (rows != NULL);
+    
+    /*
+     * Allocate cards for FITS headers
+     */
+    if (WCS != NULL) {
+       cards = psAlloc((ncard + 1)*sizeof(CARD *));
+       assert(cards != NULL);
+       
+       cards[0] = psAlloc((ncard + 1)*sizeof(CARD));
+       assert(cards[0] != NULL);
+       
+       for(i = 0; i < ncard; i++) {
+	  cards[i] = cards[0] + i;
+       }
+       cards[i] = NULL;
+       /*
+	* Generate cards for WCS, so that pixel (0,0) is correctly labelled
+	*/
+       i = 0;
+       sprintf(cards[i]->keyword, "CRVAL1%s", WCS); CRVAL1 = i;
+       cards[i]->type = INTEGER_CARD;
+       cards[i]->val.i = 0;
+       cards[i]->commnt = "(output) Column pixel of Reference Pixel";
+       
+       i++;
+       sprintf(cards[i]->keyword, "CRVAL2%s", WCS); CRVAL2 = i;
+       cards[i]->type = INTEGER_CARD;
+       cards[i]->val.i = 0;
+       cards[i]->commnt = "(output) Row pixel of Reference Pixel";
+       
+       i++;
+       sprintf(cards[i]->keyword, "CRPIX1%s", WCS); CRVAL1 = i;
+       cards[i]->type = INTEGER_CARD;
+       cards[i]->val.i = 1;
+       cards[i]->commnt = "Column Pixel Coordinate of Reference";
+       
+       i++;
+       sprintf(cards[i]->keyword, "CRPIX2%s", WCS); CRVAL1 = i;
+       cards[i]->type = INTEGER_CARD;
+       cards[i]->val.i = 1;
+       cards[i]->commnt = "Row Pixel Coordinate of Reference";
+       
+       i++;
+       sprintf(cards[i]->keyword, "CTYPE1%s", WCS); CRVAL1 = i;
+       cards[i]->type = STRING_CARD;
+       cards[i]->val.s = "LINEAR";
+       cards[i]->commnt = "Type of projection";
+       
+       i++;
+       sprintf(cards[i]->keyword, "CTYPE1%s", WCS); CRVAL1 = i;
+       cards[i]->type = STRING_CARD;
+       cards[i]->val.s = "LINEAR";
+       cards[i]->commnt = "Type of projection";
+       
+       i++;
+       sprintf(cards[i]->keyword, "CUNIT1%s", WCS); CRVAL1 = i;
+       cards[i]->type = STRING_CARD;
+       cards[i]->val.s = "PIXEL";
+       cards[i]->commnt = "Column unit";
+       
+       i++;
+       sprintf(cards[i]->keyword, "CUNIT2%s", WCS); CRVAL1 = i;
+       cards[i]->type = STRING_CARD;
+       cards[i]->val.s = "PIXEL";
+       cards[i]->commnt = "Row unit";
+      
+       cards[++i] = NULL;
+       assert(i <= ncard);
+    }
+
+    naxes[0] = data->numCols;
+    naxes[1] = data->numRows;
+    
+    write_fits_hdr(fd, bitpix, naxis, naxes, cards, meta, 1);
+    for (int r = 0; r < data->numRows; r++) {
+	if(write_fits_data(fd, bitpix, data->numCols, rows[r], ((r==data->numRows - 1) ? 1 : 0)) < 0) {
+	    psError(POIS_ERR_UNKNOWN, 1, "Error writing data for row %d", r);
+	    break;
+	}
+    }
+
+    if (cards != NULL) {
+       psFree(cards[0]);
+       psFree(cards);
+    }
+
+    psErr *err = psErrorLast();
+    int code = err->code == PS_ERR_NONE ? 0 : -1;
+    psFree(err);
+    
+    return code;
+}   
+
+/************************************************************************************************************/
+
+int
+rhlWriteFitsFile(const char *filename,	/* file to write to (or "| cmd"); NULL => "|xpaset ds9 fits"*/
+		 const psImage *data,	/* The data to write */
+		 const psMetadata *meta, /* associated metadata */
+		 const char *WCS)	/* name of WCS for pixel coordinates */
+{
+    if (filename == NULL) {
+	filename =  "| xpaset ds9 fits"; // i.e. display on DS9
+    }
+
+    int fd;
+
+    if (filename[0] == '|') {		// a command
+	const char *cmd = filename + 1;
+	while (isspace(*cmd)) {
+	    cmd++;
+	}
+
+	fd = fileno(popen(cmd, "w"));
+    } else {
+	fd = creat(filename, 777);
+    }
+
+    if (fd < 0) {
+	psError(POIS_ERR_FITS, 1, "Cannot open \"%s\"", filename);
+	return -1;
+    }
+
+    int ret = rhlWriteFits(fd, data, meta, WCS);
+
+    (void)close(fd);
+
+    return ret;
+}
Index: /tags/sj_tags/sj_root_20080929/pois/swig/simpleFits.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/swig/simpleFits.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/swig/simpleFits.h	(revision 22322)
@@ -0,0 +1,15 @@
+#if !defined(SIMPLE_FITS_H)
+#define SIMPLE_FITS_H 1
+
+int
+rhlWriteFits(int fd,			/* file descriptor to write to */
+	     const psImage *data,	/* The data to write */
+	     const psMetadata *meta,	/* associated metadata */
+	     const char *WCS);		/* which WCS to use for pixel coordinates */
+int
+rhlWriteFitsFile(const char *filename,	/* file to write to (or "| cmd"); NULL => "|xpaset ds9 fits" */
+		 const psImage *data,	/* The data to write */
+		 const psMetadata *meta, /* associated metadata */
+		 const char *WCS);	/* name of WCS for pixel coordinates */
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/pois/swig/xpaSwig.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/pois/swig/xpaSwig.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pois/swig/xpaSwig.i	(revision 22322)
@@ -0,0 +1,148 @@
+// -*- C -*-
+/*
+ * This would be a very simple module, except that I didn't want to
+ * deal with (char **) in SWIG; so I wrote C wrappers to pass a single
+ * (char *) instead.
+ *
+ * I also include some utility routines used to write psImages to
+ * a file descriptor; these are used to display images on ds9 via xpa
+ */
+%module xpaSwig
+
+%include "p_psSwig.i"
+%import "psLibSwig.i"
+
+%rename(XPAGet) XPAGet1;
+%rename(XPASet) XPASet1;
+%rename(XPASetFd) XPASetFd1;
+
+%{
+#include "xpa.h"
+
+static char *
+xmalloc(long n)
+{
+    char *ptr = malloc(n);
+    assert(ptr != NULL);
+
+    return(ptr);
+}
+
+/*
+ * A binding for XPAGet that talks to only one server, but doesn't have to talk (char **) with SWIG
+ */
+char *
+XPAGet1(XPA xpa,
+	char *xtemplate,
+	char *paramlist,
+	char *mode)
+{
+    char *buf = NULL;			/* desired response */
+    int len = 0;			/* length of buf; ignored */
+    char *error = NULL;			/* returned error if any*/
+
+    int n = XPAGet(xpa, xtemplate, paramlist, mode,
+		   &buf, &len, NULL, &error, 1);
+
+    if(n == 0) {
+	return(NULL);
+    }
+    if(error != NULL) {
+	char *errStr = xmalloc(strlen(error) + 1);
+	strcpy(errStr, error);
+	return(errStr);
+    }
+
+    return(buf);
+}
+
+/*****************************************************************************/
+
+char *
+XPASet1(XPA xpa,
+	char *xtemplate,
+	char *paramlist,
+	char *mode,
+	char *buf,			// desired extra data
+	int len)			// length of buf (or -1)
+{
+    if(len < 0) {
+	len = strlen(buf);		// length of buf
+    }
+    char *error = NULL;			// returned error if any
+
+    int n = XPASet(xpa, xtemplate, paramlist, mode,
+		   buf, len, NULL, &error, 1);
+
+    if(n == 0) {
+	return(NULL);
+    }
+    if(error != NULL) {
+	char *errStr = xmalloc(strlen(error) + 1);
+	strcpy(errStr, error);
+	return(errStr);
+    }
+
+    return "";
+}
+
+
+/*****************************************************************************/
+
+char *
+XPASetFd1(XPA xpa,
+	  char *xtemplate,
+	  char *paramlist,
+	  char *mode,
+	  int fd)			/* file descriptor for xpa to read */
+{
+    char *error = NULL;			/* returned error if any*/
+
+    int n = XPASetFd(xpa, xtemplate, paramlist, mode,
+		     fd, NULL, &error, 1);
+
+    if(n == 0) {
+	return(NULL);
+    }
+    if(error != NULL) {
+	char *errStr = xmalloc(strlen(error) + 1);
+	strcpy(errStr, error);
+
+	return(errStr);
+    }
+
+    return NULL;
+}
+%}
+
+%rename(XPA_in) in;			// avoid conflict with python keyword in xpa.h
+
+%import "prsetup.h"
+%import "xpa.h"
+
+%include "exception.i"
+
+%exception {
+    $action
+    if (result == NULL) {
+       SWIG_exception(SWIG_IOError, "XPA returned NULL")
+    }
+}
+
+char *XPAGet1(XPA xpa, char *xtemplate, char *paramlist, char *mode);
+char *XPASet1(XPA xpa, char *xtemplate, char *paramlist, char *mode, char *buf, int len);	
+char *XPASetFd1(XPA xpa, char *xtemplate, char *paramlist, char *mode, int fd);
+
+/************************************************************************************************************/
+/*
+ * Talk to ds9
+ */
+%{
+#   include "pslib.h"
+#   include "simpleFits.h"
+%}
+
+NOTNEGATIVE(rhlWriteFits);
+NOTNEGATIVE(rhlWriteFitsFile);
+
+%include "simpleFits.h"
Index: /tags/sj_tags/sj_root_20080929/ppArith/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/.cvsignore	(revision 22322)
@@ -0,0 +1,17 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+missing
+config.guess
+libtool
+ltmain.sh
+stamp-h1
+config.sub
+test
Index: /tags/sj_tags/sj_root_20080929/ppArith/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *.pyc *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/ppArith/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppArith
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE  failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ppArith/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/configure.ac	(revision 22322)
@@ -0,0 +1,36 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.61)
+
+AC_INIT([ppArith], [0.1.1], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+PKG_CHECK_MODULES([PPSTATS], [ppStats >= 1.0.0]) 
+
+dnl Set CFLAGS for build
+IPP_STDOPTS
+CFLAGS="${CFLAGS} -Wall -Werror"
+
+AC_SUBST([PPARITH_CFLAGS])
+AC_SUBST([PPARITH_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ppArith/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/src/.cvsignore	(revision 22322)
@@ -0,0 +1,8 @@
+config.h
+config.h.in
+Makefile
+Makefile.in
+stamp-h1
+.deps
+.libs
+ppArith
Index: /tags/sj_tags/sj_root_20080929/ppArith/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/src/Makefile.am	(revision 22322)
@@ -0,0 +1,21 @@
+bin_PROGRAMS = ppArith
+ppArith_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPARITH_CFLAGS)
+ppArith_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPSTATS_LIBS)   $(PSPHOT_LIBS)   $(PPARITH_LIBS)
+
+ppArith_SOURCES =		\
+	ppArith.c		\
+	ppArithArguments.c	\
+	ppArithLoop.c		\
+	ppArithReadout.c	\
+	ppArithVersion.c            
+
+noinst_HEADERS = \
+	ppArith.h
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
Index: /tags/sj_tags/sj_root_20080929/ppArith/src/ppArith.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/src/ppArith.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/src/ppArith.c	(revision 22322)
@@ -0,0 +1,45 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppArith.h"
+
+int main(int argc, char *argv[])
+{
+    psExit exitValue = PS_EXIT_SUCCESS; // Exit value
+    psTimerStart("ppArith");
+    psLibInit(NULL);
+
+    pmConfig *config = pmConfigRead(&argc, argv, PPARITH_RECIPE); // Configuration
+    if (!config) {
+        psErrorStackPrint(stderr, "Error reading configuration.");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!ppArithArguments(argc, argv, config)) {
+        psErrorStackPrint(stderr, "Error reading arguments.\n");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!ppArithLoop(config)) {
+        psErrorStackPrint(stderr, "Error performing arithmetic.\n");
+        exitValue = PS_EXIT_PROG_ERROR;
+        goto die;
+    }
+
+ die:
+    psTrace("ppArith", 1, "Finished at %f sec\n", psTimerMark("ppArith"));
+    psTimerStop();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exitValue);
+}
Index: /tags/sj_tags/sj_root_20080929/ppArith/src/ppArith.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/src/ppArith.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/src/ppArith.h	(revision 22322)
@@ -0,0 +1,32 @@
+#ifndef PP_ARITH_H
+#define PP_ARITH_H
+
+#define PPARITH_RECIPE "PPARITH"            /// Name of the recipe to use
+
+/// Parse the arguments
+bool ppArithArguments(int argc, char *argv[], ///< Command-line arguments
+                      pmConfig *config    ///< Configuration
+    );
+
+/// Parse the camera input
+bool ppArithCamera(pmConfig *config       ///< Configuration
+    );
+
+/// Loop over the FPA hierarchy
+bool ppArithLoop(pmConfig *config         ///< Configuration
+    );
+
+/// Perform arithmetic on the readout
+bool ppArithReadout(pmReadout *output,  ///< Output readout
+                    const pmReadout *input1, ///< Input readout
+                    const pmReadout *input2, ///< Input readout
+                    const pmConfig *config, ///< Configuration
+                    const pmFPAview *view ///< View of readout on which to operate
+    );
+
+/// Put the program version information into a metadata
+void ppArithVersionMetadata(psMetadata *metadata ///< Metadata to populate
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithArguments.c	(revision 22322)
@@ -0,0 +1,145 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppArith.h"
+
+// Print usage information and die
+static void usage(const char *program,  // Name of the program
+                  psMetadata *arguments, // Command-line arguments
+                  pmConfig *config      // Configuration
+    )
+{
+    fprintf(stderr, "\nPan-STARRS image arithmetic\n\n");
+    fprintf(stderr, "Usage: %s -file1 INPUT1.fits -op OP -file2 INPUT2.fits OUTPUT_ROOT\n\n",
+            program);
+    fprintf(stderr, "\n");
+    psArgumentHelp(arguments);
+    psFree(arguments);
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+    exit(PS_EXIT_CONFIG_ERROR);
+}
+
+// Get a string value from the command-line and add it to the target
+static bool valueArgStr(psMetadata *arguments, // Command-line arguments
+                        const char *argName, // Argument name in the command-line arguments
+                        const char *mdName, // Name for value in the metadata
+                        psMetadata *target // Target metadata to which to add value
+                        )
+{
+    psString value = psMetadataLookupStr(NULL, arguments, argName); // Value of interest
+    if (value && strlen(value) > 0) {
+        return psMetadataAddStr(target, PS_LIST_TAIL, mdName, 0, NULL, value);
+    }
+    return false;
+}
+
+// Add a single filename to the arguments as an array, so that it can be used with pmFPAfileBindFromArgs, etc
+static void fileList(const char *file, // The symbolic name for the file
+                     const char *name, // The name of the file
+                     const char *comment, // Description of the file
+                     pmConfig *config // Configuration
+    )
+{
+    psArray *files = psArrayAlloc(1); // Array with file names
+    files->data[0] = psStringCopy(name);
+    psMetadataAddArray(config->arguments, PS_LIST_TAIL, file, 0, comment, files);
+    psFree(files);
+    return;
+}
+
+bool ppArithArguments(int argc, char *argv[], pmConfig *config)
+{
+    assert(config);
+
+    psMetadata *arguments = psMetadataAlloc(); // Command-line arguments
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-file1", 0, "First image", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-op", 0, "Operation to perform", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-file2", 0, "Second image", NULL);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-mask", 0, "Treat images as masks", false);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stats", 0, "Statistics file", NULL);
+
+    if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 2) {
+        usage(argv[0], arguments, config);
+    }
+
+    bool isMask = psMetadataLookupBool(NULL, arguments, "-mask"); // Are we dealing with masks?
+    psMetadataAddBool(config->arguments, PS_LIST_TAIL, "MASK", 0, "Produce a mask image?", isMask);
+    const char *inFilerule = isMask ? "PPARITH.INPUT.MASK" : "PPARITH.INPUT.IMAGE"; // Input file rule
+    const char *outFilerule = isMask ? "PPARITH.OUTPUT.MASK" : "PPARITH.OUTPUT.IMAGE"; // Output file rule
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "FILERULE.INPUT", 0,
+                     "File rule for input", inFilerule);
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "FILERULE.OUTPUT", 0,
+                     "File rule for output", outFilerule);
+
+    bool status = false;                // Status for file definition
+
+    // First file
+    const char *name1 = psMetadataLookupStr(NULL, arguments, "-file1"); // Name of first image
+    if (!name1 || strlen(name1) == 0) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "No input image specified.");
+        goto ERROR;
+    }
+    fileList("INPUT1", name1, "Name of the first input image", config);
+    pmFPAfile *file1 = pmFPAfileDefineFromArgs(&status, config, inFilerule, "INPUT1");
+    if (!status || !file1) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from %s", inFilerule);
+        goto ERROR;
+    }
+    if ((isMask && file1->type != PM_FPA_FILE_MASK) || (!isMask && file1->type != PM_FPA_FILE_IMAGE)) {
+        psError(PS_ERR_IO, true, "File rule %s is not of the expected type", inFilerule);
+        goto ERROR;
+    }
+
+    // Second file is optional (won't be one for unary operations)
+    const char *name2 = psMetadataLookupStr(NULL, arguments, "-file2"); // Name of second image
+    if (name2 && strlen(name2) > 0) {
+        fileList("INPUT2", name2, "Name of the second input image", config);
+        pmFPAfile *file2 = pmFPAfileDefineFromArgs(&status, config, inFilerule, "INPUT2");
+        if (!status || !file2) {
+            psError(PS_ERR_IO, false, "Failed to build FPA from %s", inFilerule);
+            goto ERROR;
+        }
+        if ((isMask && file2->type != PM_FPA_FILE_MASK) || (!isMask && file2->type != PM_FPA_FILE_IMAGE)) {
+            psError(PS_ERR_IO, true, "File rule %s is not of the expected type", inFilerule);
+            goto ERROR;
+        }
+        if (file2->fpa->camera != file1->fpa->camera) {
+            psError(PS_ERR_IO, true, "Cameras for inputs differ.");
+            goto ERROR;
+        }
+    }
+
+    // Output image
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
+    pmFPAfile *output = pmFPAfileDefineOutput(config, file1->fpa, outFilerule);
+    if (!output) {
+        psError(PS_ERR_IO, false, "Unable to generate output file from %s", outFilerule);
+        goto ERROR;
+    }
+    if ((isMask && output->type != PM_FPA_FILE_MASK) || (!isMask && output->type != PM_FPA_FILE_IMAGE)) {
+        psError(PS_ERR_IO, true, "%s is not of the expected type", outFilerule);
+        goto ERROR;
+    }
+    output->save = true;
+
+    valueArgStr(arguments, "-op",    "OPERATION", config->arguments);
+    valueArgStr(arguments, "-stats", "STATS",     config->arguments);
+
+    psTrace("ppArith", 1, "Done reading command-line arguments\n");
+    psFree(arguments);
+    return true;
+
+ERROR:
+    psFree(arguments);
+    return false;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithLoop.c	(revision 22322)
@@ -0,0 +1,174 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+#include <psphot.h>
+
+#include "ppArith.h"
+
+bool ppArithLoop(pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool mdok;                          // Status of MD lookup
+    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
+    psMetadata *stats = NULL;           // Container for statistics
+    FILE *statsFile = NULL;             // File stream for statistics
+    if (statsName && strlen(statsName) > 0) {
+        psString resolved = pmConfigConvertFilename(statsName, config, true, true); // Resolved filename
+        statsFile = fopen(resolved, "w");
+        if (!statsFile) {
+            psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
+            psFree(resolved);
+            return false;
+        } else {
+            stats = psMetadataAlloc();
+        }
+        psFree(resolved);
+    }
+
+    const char *outName = psMetadataLookupStr(NULL, config->arguments, "FILERULE.OUTPUT"); // Output filerule
+    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, outName); // Output file
+    assert(output);                     // We added it earlier
+
+    const char *inName = psMetadataLookupStr(NULL, config->arguments, "FILERULE.INPUT"); // Input filerule
+    pmFPAfile *input1 = NULL, *input2 = NULL; // Input files
+    psString fileRegex = NULL;          // Regular expression to find input files
+    psStringAppend(&fileRegex, "^%s$", inName);
+    psMetadataIterator *iter = psMetadataIteratorAlloc(config->files, PS_LIST_HEAD, fileRegex); // Iterator
+    psMetadataItem *item = psMetadataGetAndIncrement(iter); // Item from iteration
+    input1 = item->data.V;
+    assert(input1);                     // It should be there!
+    if ((item = psMetadataGetAndIncrement(iter))) {
+        input2 = item->data.V;
+        assert(input2);
+    }
+    psFree(iter);
+
+    pmFPAview *view = pmFPAviewAlloc(0); // Pointer into FPA hierarchy
+    pmHDU *lastHDU = NULL;              // Last HDU that was updated
+
+    // Iterate over the FPA hierarchy
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return false;
+    }
+
+    pmChip *outChip;                    // Output chip of interest
+    while ((outChip = pmFPAviewNextChip(view, output->fpa, 1)) != NULL) {
+        pmChip *inChip1 = pmFPAviewThisChip(view, input1->fpa); // Input chip of interest
+        pmChip *inChip2 = input2 ? pmFPAviewThisChip(view, input2->fpa) : NULL; // Input chip of interest
+        if (inChip2 && ((!inChip1->file_exists && inChip2->file_exists) ||
+                        (inChip1->file_exists && !inChip2->file_exists))) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "FPA format discrepency between inputs");
+            psFree(view);
+            return false;
+        }
+
+        if (!inChip1->file_exists) {
+            continue;
+        }
+
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+            return false;
+        }
+
+        pmCell *outCell;                // Cell of interest
+        while ((outCell = pmFPAviewNextCell(view, output->fpa, 1)) != NULL) {
+            pmCell *inCell1 = pmFPAviewThisCell(view, input1->fpa); // Input cell of interest
+            pmCell *inCell2 = input2 ? pmFPAviewThisCell(view, input2->fpa) : NULL; // Input cell of interest
+            if (inCell2 && ((!inCell1->file_exists && inCell2->file_exists) ||
+                            (inCell1->file_exists && !inCell2->file_exists))) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "FPA format discrepency between inputs");
+                psFree(view);
+                return false;
+            }
+            if (!inCell1->file_exists) {
+                continue;
+            }
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                return false;
+            }
+
+            // Put version information into the header
+            pmHDU *hdu = pmHDUFromCell(outCell);
+            if (hdu && hdu != lastHDU) {
+                if (!hdu->header) {
+                    hdu->header = psMetadataAlloc();
+                }
+                ppArithVersionMetadata(hdu->header);
+                lastHDU = hdu;
+            }
+
+            pmReadout *outRO;           // Readout of interest
+            while ((outRO = pmFPAviewNextReadout(view, output->fpa, 1))) {
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                    return false;
+                }
+                pmReadout *inRO1 = pmFPAviewThisReadout(view, input1->fpa);// Input readout of interest
+                pmReadout *inRO2 = input2 ? pmFPAviewThisReadout(view, input2->fpa) :
+                    NULL;// Input readout of interest
+
+                if (inRO2 && ((!inRO1->data_exists && inRO2->data_exists) ||
+                              (inRO1->data_exists && !inRO2->data_exists))) {
+                    psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                            "FPA format discrepency between inputs");
+                    psFree(view);
+                    return false;
+                }
+                if (!inRO1->data_exists) {
+                    continue;
+                }
+
+                // Perform the analysis
+                if (!ppArithReadout(outRO, inRO1, inRO2, config, view)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to perform arithmetic.\n");
+                    return false;
+                }
+
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                    return false;
+                }
+            }
+
+            // Perform statistics on the cell
+            if (stats) {
+                ppStatsFPA(stats, output->fpa, view, 0, config);
+            }
+
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+                return false;
+            }
+        }
+
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            return false;
+        }
+    }
+
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        return false;
+    }
+
+    psFree(view);
+
+    // Write out summary statistics
+    if (stats) {
+        const char *statsMDC = psMetadataConfigFormat(stats);
+        if (!statsMDC || strlen(statsMDC) == 0) {
+            psWarning("Unable to get statistics MDC file.\n");
+        } else {
+            fprintf(statsFile, "%s", statsMDC);
+        }
+        psFree((void *)statsMDC);
+        fclose(statsFile);
+
+        psFree(stats);
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithReadout.c	(revision 22322)
@@ -0,0 +1,72 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppArith.h"
+
+bool ppArithReadout(pmReadout *output, const pmReadout *input1, const pmReadout *input2,
+                    const pmConfig *config, const pmFPAview *view)
+{
+    PS_ASSERT_PTR_NON_NULL(output, false);
+    PS_ASSERT_PTR_NON_NULL(input1, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    bool mdok;                          // Status of MD lookup
+    bool isMask = psMetadataLookupBool(&mdok, config->arguments, "MASK");
+
+    psImage *inImage1;  // Input image 1
+    psImage *outImage;  // Output image
+    if (isMask) {
+        inImage1 = input1->mask;
+        outImage = output->mask;
+        if (!outImage) {
+            output->mask = outImage = psImageAlloc(inImage1->numCols, inImage1->numRows, PS_TYPE_MASK);
+        }
+        if (!output->image) {
+            // Generate an image to serve as a backdrop for the mask when doing statistics
+            output->image = psImageAlloc(inImage1->numCols, inImage1->numRows, PS_TYPE_F32);
+            psImageInit(output->image, 1.0);
+        }
+    } else {
+        inImage1 = input1->image;
+        outImage = output->image;
+        if (!outImage) {
+            output->mask = outImage = psImageAlloc(inImage1->numCols, inImage1->numRows, PS_TYPE_MASK);
+        }
+    }
+    PS_ASSERT_IMAGE_NON_NULL(inImage1, false);
+
+    psImage *inImage2 = NULL;           // Input image 2
+    if (input2) {
+        inImage2 = isMask ? input2->mask : input2->image;
+        PS_ASSERT_IMAGE_NON_NULL(inImage2, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(inImage2, inImage1, false);
+        PS_ASSERT_IMAGE_TYPE(inImage2, inImage1->type.type, false);
+    }
+
+#if 0
+    pmCell *outCell = output->parent;   // Output cell
+    pmChip *outChip = outCell->parent;  // Output chip
+    pmFPA *outFPA = outChip->parent;    // Output FPA
+    pmHDU *outHDU = pmHDUGetLowest(outFPA, outChip, outCell); // Output HDU
+    if (!outHDU->header) {
+        outHDU->header = psMetadataAlloc();
+    }
+#endif
+
+    // Look up appropriate values
+    const char *op = psMetadataLookupStr(NULL, config->arguments, "OPERATION"); // Operation to perform
+
+    if (input2) {
+        psBinaryOp(outImage, inImage1, op, inImage2);
+    } else {
+        psUnaryOp(outImage, inImage1, op);
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppArith/src/ppArithVersion.c	(revision 22322)
@@ -0,0 +1,60 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppArith.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString ppArithVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString ppArithVersionLong(void)
+{
+    psString version = ppArithVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
+
+void ppArithVersionMetadata(psMetadata *metadata)
+{
+    PS_ASSERT_METADATA_NON_NULL(metadata,);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppStats = ppStatsVersionLong(); // ppStats version
+    psString ppArith = ppArithVersionLong(); // ppArith version
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString head = NULL;               // Head string
+    psStringAppend(&head, "ppArith processing at %s. Component information:", timeString);
+    psFree(timeString);
+
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppStats, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppArith, "");
+
+    psFree(head);
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(ppStats);
+    psFree(ppArith);
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/ppConfigDump/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppConfigDump/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppConfigDump/.cvsignore	(revision 22322)
@@ -0,0 +1,15 @@
+aclocal.m4
+autom4te.cache
+compile
+config.guess
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+ltmain.sh
+Makefile
+Makefile.in
+missing
+libtool
Index: /tags/sj_tags/sj_root_20080929/ppConfigDump/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppConfigDump/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppConfigDump/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/ppConfigDump/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppConfigDump/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppConfigDump/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppConfigDump
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ppConfigDump/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppConfigDump/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppConfigDump/configure.ac	(revision 22322)
@@ -0,0 +1,29 @@
+AC_PREREQ(2.61)
+
+AC_INIT([ppConfigDump], [1.1.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ppConfigDump/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppConfigDump/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppConfigDump/src/.cvsignore	(revision 22322)
@@ -0,0 +1,8 @@
+.libs
+config.h
+config.h.in
+Makefile
+Makefile.in
+ppConfigDump
+stamp-h1
+.deps
Index: /tags/sj_tags/sj_root_20080929/ppConfigDump/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppConfigDump/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppConfigDump/src/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+bin_PROGRAMS = ppConfigDump
+
+ppConfigDump_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(ppConfigDump_CFLAGS)
+ppConfigDump_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS)
+ppConfigDump_SOURCES = ppConfigDump.c
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/ppConfigDump/src/ppConfigDump.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppConfigDump/src/ppConfigDump.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppConfigDump/src/ppConfigDump.c	(revision 22322)
@@ -0,0 +1,155 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+pmConfig *config = NULL;                // Configuration; declared globally for convenience
+psMetadata *arguments = NULL;           // Command-line arguments; declared globally for convenience
+
+// Clean up and die
+void die(int code                       // Exit code
+         )
+{
+    psFree(arguments);
+    psFree(config);
+    pmConceptsDone();
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(code);
+}
+
+void dump(const char *filename,     // Filename to which to dump
+          const char *description,  // Description of what's being dumped
+          psMetadata *md            // Metadata to dump
+          )
+{
+    if (!filename || strlen(filename) == 0) {
+        return;
+    }
+    if (strcmp(filename, "-") == 0) {
+        psString string = psMetadataConfigFormat(md); // String to dump
+        if (!string) {
+            psErrorStackPrint(stderr, "Can't write %s to STDOUT\n", description);
+            die(PS_EXIT_SYS_ERROR);
+        }
+        fprintf(stdout, "%s", string);
+        psFree(string);
+    } else if (!psMetadataConfigWrite(md, filename)) {
+        psErrorStackPrint(stderr, "Can't write %s to %s\n", description, filename);
+        die(PS_EXIT_SYS_ERROR);
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    psLibInit(NULL);
+    psTraceSetLevel("err", 10);
+
+    // load the site-wide configuration information
+    config = pmConfigRead(&argc, argv, NULL);
+    if (!config) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        die(PS_EXIT_CONFIG_ERROR);
+    }
+
+    //    psMetadata *options = pmConfigRecipeOptions(config, RECIPE_NAME);
+
+    arguments = psMetadataAlloc();
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-file", 0, "FITS file to use for camera determination", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dump-user", 0, "Filename for user configuration", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dump-site", 0, "Filename for site configuration", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dump-system", 0, "Filename for system configuration", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dump-camera", 0, "Filename for camera configuration", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dump-format", 0, "Filename for camera format", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dump-recipes", 0, "Filename for recipes", NULL);
+    psMetadata *recipeArgs = psMetadataAlloc(); // Options for dumping a single recipe
+    psMetadataAddStr(recipeArgs, PS_LIST_TAIL, "recipe", 0, "Name of recipe", NULL);
+    psMetadataAddStr(recipeArgs, PS_LIST_TAIL, "filename", 0, "Filename", NULL);
+    psMetadataAddMetadata(arguments, PS_LIST_TAIL, "-dump-recipe", 0, "Dump a single recipe", recipeArgs);
+    psFree(recipeArgs);
+
+    if (argc == 1 || !psArgumentParse(arguments, &argc, argv)) {
+        fprintf(stderr, "\nPan-STARRS IPP configuration dumper\n\n");
+        fprintf(stderr, "Usage: %s [-file INPUT.fits] [-dump-site FILE.mdc]\n", argv[0]);
+        fprintf(stderr, "       [-dump-camera FILE.mdc] [-dump-format FILE.mdc] [-dump-recipes FILE.mdc]\n");
+        fprintf(stderr, "       [-dump-recipe RECIPE FILE.mdc]\n");
+        fprintf(stderr, "\n");
+        fprintf(stderr, "       FILE.mdc may be \"-\", in which case the file is written to stdout.\n");
+        fprintf(stderr, "\n");
+        psArgumentHelp(arguments);
+        die(PS_EXIT_CONFIG_ERROR);
+    }
+
+    const char *inName = psMetadataLookupStr(NULL, arguments, "-file"); // Name of input FITS file
+    if (inName) {
+        psString resolved = pmConfigConvertFilename(inName, config, false, false); // Resolved filename
+        psFits *inFile = psFitsOpen(resolved, "r"); // File handle for FITS file
+        if (!inFile) {
+            psErrorStackPrint(stderr, "Can't open input image: %s\n", resolved);
+            die(PS_EXIT_DATA_ERROR);
+        }
+        psFree(resolved);
+
+        psMetadata *phu = psFitsReadHeader(NULL, inFile); // FITS primary header
+        psFitsClose(inFile);
+        if (!phu) {
+            psErrorStackPrint(stderr, "Can't read PHU of input image: %s\n", inName);
+            die(PS_EXIT_DATA_ERROR);
+        }
+
+        psMetadata *format = pmConfigCameraFormatFromHeader(NULL, NULL, config, phu, true); // Camera format
+        if (!format || !config->camera) {
+            psErrorStackPrint(stderr, "Can't find suitable camera configuration!\n");
+            psFree(format);
+            psFree(phu);
+            die(PS_EXIT_SYS_ERROR);
+        }
+        psFree(format);
+        psFree(phu);
+    } else {
+        // Now we have the camera, we can read the recipes
+        if (!pmConfigReadRecipes(config, PM_RECIPE_SOURCE_CAMERA | PM_RECIPE_SOURCE_CL)) {
+            psError(PS_ERR_IO, false, "Error reading recipes from camera config for %s", config->cameraName);
+            psErrorStackPrint(stderr, "problem read recipes!\n");
+            die(PS_EXIT_SYS_ERROR);
+        }
+    }
+
+    const char *userName = psMetadataLookupStr(NULL, arguments, "-dump-user"); // User filename
+    dump(userName, "user configuration", config->user);
+
+    const char *siteName = psMetadataLookupStr(NULL, arguments, "-dump-site"); // Site filename
+    dump(siteName, "site configuration", config->site);
+
+    const char *systemName = psMetadataLookupStr(NULL, arguments, "-dump-system"); // System filename
+    dump(systemName, "system configuration", config->system);
+
+    const char *camName = psMetadataLookupStr(NULL, arguments, "-dump-camera"); // Camera filename
+    dump(camName, "camera configuration", config->camera);
+
+    const char *formatName = psMetadataLookupStr(NULL, arguments, "-dump-format"); // Format filename
+    dump(formatName, "camera format", config->format);
+
+    const char *recipesName = psMetadataLookupStr(NULL, arguments, "-dump-recipes"); // Recipes filename
+    dump(recipesName, "recipes", config->recipes);
+
+    recipeArgs = psMetadataLookupMetadata(NULL, arguments, "-dump-recipe");
+    const char *recipeName = psMetadataLookupStr(NULL, recipeArgs, "recipe"); // Name of recipe
+    if (recipeName) {
+        psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, recipeName); // Recipe desired
+        if (!recipe) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find recipe %s", recipeName);
+            die(PS_EXIT_CONFIG_ERROR);
+        }
+        const char *recipeFile = psMetadataLookupStr(NULL, recipeArgs, "filename"); // Filename for recipe
+        dump(recipeFile, "recipe", recipe);
+    }
+
+    die(PS_EXIT_SUCCESS);
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppImage/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/.cvsignore	(revision 22322)
@@ -0,0 +1,17 @@
+bin
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+Makefile.in
+missing
+Makefile
+config.cache
+config.guess
+config.sub
+ltmain.sh
+libtool
Index: /tags/sj_tags/sj_root_20080929/ppImage/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/ppImage/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppImage
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ppImage/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/configure.ac	(revision 22322)
@@ -0,0 +1,35 @@
+AC_PREREQ(2.61)
+
+AC_INIT([ppImage], [1.1.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB],    [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+PKG_CHECK_MODULES([PSPHOT],   [psphot >= 0.8.0]) 
+PKG_CHECK_MODULES([PSASTRO],  [psastro >= 0.8.0]) 
+PKG_CHECK_MODULES([PPSTATS],  [ppStats >= 1.0.0]) 
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_SUBST([PPIMAGE_CFLAGS])
+AC_SUBST([PPIMAGE_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ppImage/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/notes.txt	(revision 22322)
@@ -0,0 +1,23 @@
+
+ppImage pmFPAfiles:
+
+    pmFPAfile *input = pmFPAfileDefineFromArgs (&status, config, "PPIMAGE.INPUT", "INPUT");
+    pmFPAfile *inputMask = pmFPAfileBindFromArgs(&status, input, config, "PPIMAGE.INPUT.MASK", "INPUT.MASK");
+    pmFPAfile *inputWeight = pmFPAfileBindFromArgs(&status, input, config, "PPIMAGE.INPUT.WEIGHT", "INPUT.WEIGHT");
+    pmFPAfile *output = pmFPAfileDefineOutput(config, input->fpa, "PPIMAGE.OUTPUT");
+    pmFPAfile *outMask = pmFPAfileDefineOutput(config, input->fpa, "PPIMAGE.OUTPUT.MASK");
+    pmFPAfile *outWeight = pmFPAfileDefineOutput(config, input->fpa, "PPIMAGE.OUTPUT.WEIGHT");
+    pmFPAfile *chipImage = pmFPAfileDefineChipMosaic(config, input->fpa, "PPIMAGE.CHIP");
+    pmFPAfile *chipMask = pmFPAfileDefineOutput(config, chipImage->fpa, "PPIMAGE.CHIP.MASK");
+    pmFPAfile *chipWeight = pmFPAfileDefineOutput(config, chipImage->fpa, "PPIMAGE.CHIP.WEIGHT");
+    pmFPAfile *byFPA1 = pmFPAfileDefineFPAMosaic(config, input->fpa, "PPIMAGE.OUTPUT.FPA1");
+    pmFPAfile *byFPA2 = pmFPAfileDefineFPAMosaic(config, input->fpa, "PPIMAGE.OUTPUT.FPA2");
+
+        pmFPAfile *psphotInput = pmFPAfileDefineFromFPA (config, chipImage->fpa, 1, 1, "PSPHOT.INPUT");
+        pmFPAfile *psphotOutput = psMetadataLookupPtr (&status, config->files, "PSPHOT.OUTPUT");
+        pmFPAfile *psastroInput = pmFPAfileDefineInput (config, psphotOutput->fpa, "PSASTRO.INPUT");
+
+    pmFPAfile *bin1 = pmFPAfileDefineFromFPA (config, chipImage->fpa, options->xBin1, options->yBin1, "PPIMAGE.BIN1");
+    pmFPAfile *bin2 = pmFPAfileDefineFromFPA (config, chipImage->fpa, options->xBin2, options->yBin2, "PPIMAGE.BIN2");
+    pmFPAfile *jpg1 = pmFPAfileDefineOutput (config, byFPA1->fpa, "PPIMAGE.JPEG1");
+    pmFPAfile *jpg2 = pmFPAfileDefineOutput (config, byFPA2->fpa, "PPIMAGE.JPEG2");
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+ppImage
+ppTest
+Makefile
+Makefile.in
+.deps
+ppFocus
+config.h
+config.h.in
+stamp-h1
+.libs
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/Makefile.am	(revision 22322)
@@ -0,0 +1,77 @@
+bin_PROGRAMS = ppImage ppFocus
+
+noinst_PROGRAMS = ppTest
+
+noinst_HEADERS = \
+	ppImage.h 
+
+ppImage_CFLAGS = $(PPIMAGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSASTRO_CFLAGS) $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+ppImage_LDFLAGS = $(PPIMAGE_LIBS) $(PSASTRO_LIBS) $(PPSTATS_LIBS) $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+ppImage_SOURCES = \
+	ppImage.c \
+	ppImageArguments.c \
+	ppImageParseCamera.c \
+	ppImageLoop.c \
+	ppImageCleanup.c \
+	ppImageOptions.c \
+	ppImageDetrendReadout.c \
+	ppImageDetrendNonLinear.c \
+	ppImageDetrendFringe.c \
+	ppImageRebinReadout.c \
+	ppImageMosaic.c \
+	ppImagePhotom.c \
+	ppImageAstrom.c \
+	ppImageAddstar.c \
+	ppImageStats.c \
+	ppImageStatsOutput.c \
+	ppImagePixelStats.c \
+	ppImageMetadataStats.c \
+	ppImageReplaceBackground.c \
+	ppImageDefineFile.c \
+	ppImageSetMaskBits.c \
+	ppImageFileCheck.c \
+	ppImageVersion.c \
+	ppImageMemory.c
+
+ppFocus_CFLAGS = $(PPIMAGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSASTRO_CFLAGS) $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+ppFocus_LDFLAGS = $(PPIMAGE_LIBS) $(PSASTRO_LIBS) $(PPSTATS_LIBS) $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+ppFocus_SOURCES = \
+	ppFocus.c \
+	ppFocusArguments.c \
+	ppFocusParseCamera.c \
+	ppFocusGetFWHM.c \
+	ppFocusFitFWHM.c \
+	ppImageLoop.c \
+	ppImageCleanup.c \
+	ppImageOptions.c \
+	ppImageDetrendReadout.c \
+	ppImageDetrendNonLinear.c \
+	ppImageDetrendFringe.c \
+	ppImageRebinReadout.c \
+	ppImageMosaic.c \
+	ppImagePhotom.c \
+	ppImageAstrom.c \
+	ppImageAddstar.c \
+	ppImageStats.c \
+	ppImageStatsOutput.c \
+	ppImagePixelStats.c \
+	ppImageMetadataStats.c \
+	ppImageReplaceBackground.c \
+	ppImageDefineFile.c \
+	ppImageSetMaskBits.c \
+	ppImageFileCheck.c \
+	ppImageVersion.c \
+	ppImageMemory.c
+
+ppTest_CFLAGS = $(PPIMAGE_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+ppTest_LDFLAGS = $(PPIMAGE_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+ppTest_SOURCES = \
+	ppTest.c
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocus.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocus.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocus.c	(revision 22322)
@@ -0,0 +1,74 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+int main(int argc, char **argv) {
+
+    psLibInit(NULL);
+
+    psTimerStart(TIMERNAME);
+
+    // Parse the configuration and arguments
+    // Open the input image(s)
+    // Determine camera, format from header if not already defined
+    // Construct camera in preparation for reading
+    pmConfig *config = ppFocusArguments(argc, argv);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, " ");
+        exit(1);
+    }
+
+    // we search the argument data for the named fileset (argname)
+    psArray *infiles = psMetadataLookupPtr(NULL, config->arguments, "INPUT");
+    if (!infiles) {
+        psTrace("pmFPAfile", 5, "Failed to find INPUT in argument list");
+        exit(1);
+    }
+
+    // allocate vectors for analysis
+    psVector *focus = psVectorAllocEmpty(infiles->n, PS_TYPE_F32);
+    psVector *fwhm = psVectorAllocEmpty(infiles->n, PS_TYPE_F32);
+
+    for (int i = 0; i < infiles->n; i++) {
+
+        // define recipe options
+        // define the active I/O files
+        ppImageOptions *options = ppFocusParseCamera(config, i);
+        if (options == NULL) {
+            psErrorStackPrint(stderr, " ");
+            exit(1);
+        }
+
+        // Image Arithmetic Loop
+        // XXX ppFocus REQUIRES photom: for it to be true?
+        //
+        if (!ppImageLoop(config, options)) {
+            psErrorStackPrint(stderr, " ");
+            exit(1);
+        }
+
+        // determine FWHM at reference location in image
+        // (also removes PPIMAGE.INPUT from config->files)
+        ppFocusGetFWHM (config, focus, fwhm);
+
+        ppFocusDropCamera (config);
+        psFree (options);
+    }
+
+    ppFocusFitFWHM (config, focus, fwhm);
+
+    psLogMsg ("ppFocus", 3, "complete ppFocus run: %f sec\n", psTimerMark (TIMERNAME));
+
+    // Cleaning up
+    psFree (focus);
+    psFree (fwhm);
+    ppImageCleanup(config, NULL);
+    return EXIT_SUCCESS;
+}
+
+// ppFocus is a lot like ppImage, but with a few important differences:
+// - the input list is a set of independent images (not multiple files for a single image)
+// - each pass to ppImageLoop performs the analysis on a different pmFPAfile
+// - after each ppImageLoop, grap the input pmFPAfile and extract the FWHM stats
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusArguments.c	(revision 22322)
@@ -0,0 +1,72 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+static void usage (void) {
+    fprintf (stderr, "USAGE: ppFocus [-file focus.*.fits] [-list INPUT.txt] OUTPUT\n");
+    exit (2);
+}
+
+pmConfig *ppFocusArguments(int argc, char **argv) {
+
+    int N;
+    bool status;
+
+    if (argc == 1) usage ();
+
+    // load the site-wide configuration information
+    pmConfig *config = pmConfigRead(&argc, argv, RECIPE_NAME);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // save the following additional recipe values based on command-line options
+    // these options override the PPIMAGE recipe values loaded from recipe files
+    psMetadata *options = pmConfigRecipeOptions (config, RECIPE_NAME);
+
+    // save these recipe options until we have loaded the options
+    // psMetadata *options = psMetadataAlloc ();
+    // psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "PPIMAGE.OPTIONS",  PS_DATA_METADATA, "", options);
+
+    // the following options override the PPIMAGE recipe options
+
+    // recipe option: -usemask : override MASK setting in phase2.recipe
+    if ((N = psArgumentGet(argc, argv, "-usemask"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddBool (options, PS_LIST_TAIL, "MASK", PS_META_REPLACE, "", true);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // XXX add other PPIMAGE recipe options here
+
+    // the input file is a required argument; if not found, we will exit
+    status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
+    if (!status) { usage ();}
+
+    // if these command-line options are supplied, load the file name lists into config->arguments
+    // override any configuration-specified source for these files
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "BIAS", "-bias", "-biaslist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "DARK", "-dark", "-darklist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "FLAT", "-flat", "-flatlist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "MASK", "-mask", "-masklist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "FRINGE", "-fringe", "-fringelist");
+
+    // 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_DATA_STRING, "",
+                          argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    if (argc != 2) usage ();
+
+    // Add the input and output images (which remain on the command-line) to the arguments list
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image",
+                     argv[1]);
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusFitFWHM.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusFitFWHM.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusFitFWHM.c	(revision 22322)
@@ -0,0 +1,30 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppImage.h"
+
+bool ppFocusFitFWHM (pmConfig *config, psVector *focus, psVector *fwhm) {
+
+    float minFocus;
+
+    psPolynomial1D *poly = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 2);
+
+    if (!psVectorFitPolynomial1D (poly, NULL, 0, fwhm, NULL, focus)) {
+	psError (PS_ERR_UNKNOWN, false, "failed to fit focus/fwhm trend; invalid image data?");
+	return false;
+    }
+
+    if (poly->coeff[2] <= 0.0) {
+	psLogMsg ("ppFocus", 3, "poor focus fit: zero or negative curvature\n");
+	psLogMsg ("ppFocus", 3, "fit coeffs: %f  %f  %f\n", 
+		  poly->coeff[0], poly->coeff[1], poly->coeff[2]);
+    }
+    
+    minFocus = -0.5 * poly->coeff[1] / poly->coeff[2];
+    psLogMsg ("ppFocus", 3, "best fit focus: %f\n", minFocus);
+    psLogMsg ("ppFocus", 3, "fwhm @ min: %f\n", psPolynomial1DEval (poly, minFocus));
+    
+    psFree (poly);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusGetFWHM.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusGetFWHM.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusGetFWHM.c	(revision 22322)
@@ -0,0 +1,79 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppImage.h"
+
+bool ppFocusGetFWHM (pmConfig *config, psVector *focus, psVector *fwhm) {
+
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+    psMetadata *header;
+    float FOCUS, FWHM, FWHM_X, FWHM_Y, FWHMsum;
+    int FWHMnum;
+
+    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PSPHOT.OUTPUT");
+    if (!status) {
+	psErrorStackPrint(stderr, "Can't find input data!\n");
+	exit(EXIT_FAILURE);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // - find readouts with measured PSFs
+    // - measure the average central FWHM for each PSF
+    FWHMsum = 0.0;
+    FWHMnum = 0;
+
+    while ((chip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+        psLogMsg ("ppImageLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) { continue; }
+
+	while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
+            psLogMsg ("ppImageLoop", 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, input->fpa, 1)) != NULL) {
+		if (!readout->data_exists) { continue; }
+
+		// get average FWHM
+		// psphotReadout writes the FWHM values into the PSPHOT.HEADER table
+		// the source of this value depends on the psphot options.
+		// - if breakPoint is set to PEAKS, the value will not be defined
+		// - if breakPoint is set to MOMENTS, the PSFSTAR moments are used
+		// - in all other cases, the psf model is used
+		header = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.HEADER");
+		if (header == NULL) {
+		    psError(PS_ERR_IO, false, "Missing header in ppFocus");
+		    continue;
+		}
+
+		FWHM_X = psMetadataLookupF32 (&status, header, "FWHM_X");
+		FWHM_Y = psMetadataLookupF32 (&status, header, "FWHM_Y");
+
+		FWHMsum += 0.5*(FWHM_X + FWHM_Y);
+		FWHMnum ++;
+
+		psLogMsg ("ppFocus", 4, "focus pt: %f,%f, fwhm sum: %f, fwhm num: %d\n", FWHM_X, FWHM_Y, FWHMsum, FWHMnum);
+	    }
+	}
+    }
+
+    FWHM = FWHMsum / FWHMnum;
+
+    FOCUS = psMetadataLookupF32 (&status, input->fpa->concepts, "FPA.FOCUS");
+
+    fwhm->data.F32[fwhm->n] = FWHM;
+    focus->data.F32[focus->n] = FOCUS;
+
+    psVectorExtend (fwhm, 10, 1);
+    psVectorExtend (focus, 10, 1);
+
+    psLogMsg ("ppFocus", 4, "focus: %f, fwhm: %f (%d)\n", FOCUS, FWHM, FWHMnum);
+
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusParseCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusParseCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppFocusParseCamera.c	(revision 22322)
@@ -0,0 +1,133 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppImage.h"
+
+// XXX clean up error checks: return NULL, not psAbort
+ppImageOptions *ppFocusParseCamera (pmConfig *config, int entry) {
+
+    bool status = false;
+
+    // the first input image defines the camera, and all recipes and options that follow
+    // select only the first file from the INPUT array
+    pmFPAfile *input = pmFPAfileDefineSingleFromArgs (&status, config, "PPIMAGE.INPUT", "INPUT", entry);
+    if (!status) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from PPIMAGE.INPUT");
+        return NULL;
+    }
+
+    // parse the options from the metadata format to the ppImageOptions structure
+    ppImageOptions *options = ppImageOptionsParse (config);
+
+    // the following are defined from the argument list, if given,
+    // otherwise they revert to the config information or detrend database if specified
+    // not all input or output images are used in a given recipe
+    if (options->doBias) {
+	if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.BIAS", "BIAS", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_BIAS)) {
+	    psError (PS_ERR_IO, false, "Can't find a bias image source");
+	    return NULL;
+	}
+    }
+    if (options->doDark) {
+	if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.DARK", "DARK", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_DARK)) {
+	    psError (PS_ERR_IO, false, "Can't find a dark image source");
+	    return NULL;
+	}
+    }
+    if (options->doMask) {
+	if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.MASK", "MASK", PM_FPA_FILE_MASK, PM_DETREND_TYPE_MASK)) {
+	    psError (PS_ERR_IO, false, "Can't find a mask image source");
+	    return NULL;
+	}
+    }
+    if (options->doFlat) {
+	if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.FLAT", "FLAT", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_FLAT)) {
+	    psError (PS_ERR_IO, false, "Can't find a shutter image source");
+	    return NULL;
+	}
+    }
+
+    // the following files are output targets
+    pmFPAfile *output = pmFPAfileDefineOutput (config, input->fpa, "PPIMAGE.OUTPUT");
+    pmFPAfile *byChip = pmFPAfileDefineNewCamera (config, "PPIMAGE.OUTPUT.CHIP");
+    pmFPAfile *byFPA1 = pmFPAfileDefineNewCamera (config, "PPIMAGE.OUTPUT.FPA1");
+    pmFPAfile *byFPA2 = pmFPAfileDefineNewCamera (config, "PPIMAGE.OUTPUT.FPA2");
+
+    // save any of these files?
+    output->save = options->BaseFITS;
+    byChip->save = options->ChipFITS;
+    byFPA1->save = options->FPA1FITS;
+    byFPA2->save = options->FPA2FITS;
+
+    // output is used as a carrier: input to byChip
+    output->freeLevel = PM_FPA_LEVEL_CHIP;
+
+    // define the binned target files (which may just be carriers for some camera configurations)
+    pmFPAfile *bin1 = pmFPAfileDefineFromFPA (config, byChip->fpa, options->xBin1, options->yBin1, "PPIMAGE.BIN1");
+    pmFPAfile *bin2 = pmFPAfileDefineFromFPA (config, byChip->fpa, options->xBin2, options->yBin2, "PPIMAGE.BIN2");
+
+    // bin1 and bin2 are used as carriers: input for byFPA1, byFPA2
+    bin1->freeLevel = PM_FPA_LEVEL_FPA;
+    bin2->freeLevel = PM_FPA_LEVEL_FPA;
+
+    pmFPAfile *jpg1 = pmFPAfileDefineOutput (config, byFPA1->fpa, "PPIMAGE.JPEG1");
+    pmFPAfile *jpg2 = pmFPAfileDefineOutput (config, byFPA2->fpa, "PPIMAGE.JPEG2");
+
+    // XXX we could potentially not define these pmFPAfiles if no output is requested...
+    bin1->save = options->Bin1FITS;
+    bin2->save = options->Bin2FITS;
+    jpg1->save = options->Bin1JPEG;
+    jpg2->save = options->Bin2JPEG;
+
+    // Chip selection: turn on only the chips specified (pass status to suppress missing-key log msg)
+    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS");
+    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    if (chips->n > 0) {
+        pmFPASelectChip (input->fpa, -1, true); // deselect all chips
+        for (int i = 0; i < chips->n; i++) {
+            int chipNum = atoi(chips->data[i]);
+            if (! pmFPASelectChip(input->fpa, chipNum, false)) {
+                psError(PS_ERR_IO, false, "Chip number %d doesn't exist in camera.\n", chipNum);
+                return false;
+            }
+        }
+    }
+    psFree (chips);
+
+    return (options);
+}
+
+// remove from config all pmFPAfiles which could have been added (ignore missing entries)
+void ppFocusDropCamera (pmConfig *config) {
+
+    psMetadataRemoveKey (config->files, "PPIMAGE.INPUT");
+
+    psMetadataRemoveKey (config->files, "PPIMAGE.BIAS");
+    psMetadataRemoveKey (config->files, "PPIMAGE.DARK");
+    psMetadataRemoveKey (config->files, "PPIMAGE.MASK");
+    psMetadataRemoveKey (config->files, "PPIMAGE.FLAT");
+
+    psMetadataRemoveKey (config->files, "PPIMAGE.OUTPUT");
+    psMetadataRemoveKey (config->files, "PPIMAGE.OUTPUT.CHIP");
+    psMetadataRemoveKey (config->files, "PPIMAGE.OUTPUT.FPA1");
+    psMetadataRemoveKey (config->files, "PPIMAGE.OUTPUT.FPA2");
+
+    psMetadataRemoveKey (config->files, "PPIMAGE.BIN1");
+    psMetadataRemoveKey (config->files, "PPIMAGE.BIN2");
+
+    psMetadataRemoveKey (config->files, "PPIMAGE.JPEG1");
+    psMetadataRemoveKey (config->files, "PPIMAGE.JPEG2");
+
+    psMetadataRemoveKey (config->files, "PSPHOT.INPUT");
+    psMetadataRemoveKey (config->files, "PSPHOT.OUTPUT");
+    psMetadataRemoveKey (config->files, "PSPHOT.RESID");
+    psMetadataRemoveKey (config->files, "PSPHOT.BACKMDL");
+    psMetadataRemoveKey (config->files, "PSPHOT.BACKGND");
+    psMetadataRemoveKey (config->files, "PSPHOT.BACKSUB");
+    psMetadataRemoveKey (config->files, "PSPHOT.PSF.LOAD");
+    psMetadataRemoveKey (config->files, "PSPHOT.PSF.SAVE");
+
+    return;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImage.c	(revision 22322)
@@ -0,0 +1,46 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+int main(int argc, char **argv) {
+
+    psLibInit(NULL);
+
+    psTimerStart(TIMERNAME);
+
+    // Parse the configuration and arguments
+    // Open the input image(s)
+    // Determine camera, format from header if not already defined
+    // Construct camera in preparation for reading
+    pmConfig *config = ppImageArguments(argc, argv);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse command-line arguments.");
+        ppImageCleanup(config, NULL);
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // define recipe options
+    // define the active I/O files
+    ppImageOptions *options = ppImageParseCamera(config);
+    if (options == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse camera.");
+        ppImageCleanup(config, options);
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // Image Arithmetic Loop
+    if (!ppImageLoop(config, options)) {
+        psErrorStackPrint(stderr, "Unable to loop over input");
+        ppImageCleanup(config, options);
+        exit(PS_EXIT_SYS_ERROR);
+    }
+
+    psLogMsg ("ppImage", 3, "Complete ppImage run: %f sec\n", psTimerMark(TIMERNAME));
+
+    // Cleaning up
+    ppImageCleanup(config, options);
+
+    return PS_EXIT_SUCCESS;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImage.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImage.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImage.h	(revision 22322)
@@ -0,0 +1,209 @@
+#ifndef PP_IMAGE_H
+#define PP_IMAGE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>  // for strcasecmp
+#include <unistd.h>   // for unlink
+#include "pslib.h"
+#include "psmodules.h"
+#include "psphot.h"
+#include "psastro.h"
+#include "ppStats.h"
+
+#define RECIPE_NAME "PPIMAGE"           // Name of the recipe to use
+#define TIMERNAME "ppImage"             // Name of timer
+
+// Options for ppImage processing
+typedef struct {
+    // actions which ppImage should perform
+    bool doMaskBuild;                   // Build internal mask
+    bool doWeightBuild;                   // Build internal mask
+    bool doMask;                        // Mask bad pixels
+    bool doNonLin;                      // Non-linearity correction
+    bool doOverscan;                    // Overscan subtraction
+    bool doBias;                        // Bias subtraction
+    bool doDark;                        // Dark subtraction
+    bool doShutter;                     // Shutter correction
+    bool doFlat;                        // Flat-field normalisation
+    bool doFringe;                      // Fringe subtraction
+    bool doPhotom;                      // Source identification and photometry
+    bool doAstromChip;                  // per-chip Astrometry
+    bool doAstromMosaic;                // full-mosaic Astrometry
+    bool doStats;                       // call ppStats on the image
+    bool replaceMasked;                 // fill in masked values with background model
+
+    // output files requested
+    bool BaseFITS;
+    bool BaseMaskFITS;
+    bool BaseWeightFITS;
+
+    bool ChipFITS;
+    bool ChipMaskFITS;
+    bool ChipWeightFITS;
+
+    bool FPA1FITS;
+    bool FPA2FITS;
+    bool Bin1FITS;
+    bool Bin1JPEG;
+    bool Bin2FITS;
+    bool Bin2JPEG;
+
+    // make values for abstract concepts of masking
+    psMaskType maskValue;               // apply this bit-mask to choose masked bits
+    psMaskType markValue;               // apply this bit-mask to choose masked bits
+    psMaskType satMask;                 // Mask value to give saturated pixels
+    psMaskType badMask;                 // Mask value to give bad pixels
+    psMaskType flatMask;                // Mask value to give bad flat pixels
+    psMaskType blankMask;               // Mask value to give blank pixels
+
+    // non-linear correction parameters
+    psDataType nonLinearType;
+    psMetadataItem *nonLinearData;
+    void *nonLinearSource;
+
+    // options for the analysis
+    pmOverscanOptions *overscan;        // Overscan options
+
+    // binning parameters
+    int xBin1;                          // x-binning, scale 1
+    int yBin1;                          // y-binning, scale 1
+    int xBin2;                          // x-binning, scale 2
+    int yBin2;                          // y-binning, scale 2
+
+    // parameters used by the fringe analysis
+    float fringeRej;                    // Fringe rejection limit
+    int fringeIter;                     // Fringe iterations
+    float fringeKeep;                   // Fringe keep fraction
+
+} ppImageOptions;
+
+// Cells to be used in the detrend
+typedef struct {
+    pmCell *input;                      // The input cell, to be operated upon
+    pmCell *mask;                       // The bad pixel mask
+    pmCell *bias;                       // The bias correction
+    pmCell *dark;                       // The dark correction
+    pmCell *flat;                       // The flat-field correction
+} ppImageDetrend;
+
+ppImageOptions *ppImageOptionsAlloc(void);
+
+// Determine the processing options
+ppImageOptions *ppImageOptionsParse(pmConfig *config);
+
+// Get the configuration
+pmConfig *ppImageArguments(int argc, char **argv);
+
+// Determine what type of camera, and initialise
+ppImageOptions *ppImageParseCamera(pmConfig *config);
+bool ppImageSetMaskBits (pmConfig *config, ppImageOptions *options);
+
+// Loop over the input
+bool ppImageLoop(pmConfig *config, ppImageOptions *options);
+
+// free memory, check for leaks
+void ppImageCleanup (pmConfig *config, ppImageOptions *options);
+
+// perform the detrend analysis on the current readout
+bool ppImageDetrendReadout (pmConfig *config, ppImageOptions *options, pmFPAview *view);
+
+bool ppImageDetrendBias(pmReadout *inputReadout, pmReadout *bias, pmReadout *dark, ppImageOptions *options);
+
+bool ppImageDetrendNonLinear(pmReadout *input, ppImageOptions *options);
+bool ppImageDetrendNonLinearLookup(pmReadout *input, psMetadataItem *dataItem);
+bool ppImageDetrendNonLinearPolynomial(pmReadout *input, psMetadataItem *dataItem);
+
+bool ppImageDetrendCell(ppImageDetrend *detrend, ppImageOptions *options, pmConfig *config);
+bool ppImageDetrendBias(pmReadout *inputReadout, pmReadout *bias, pmReadout *dark, ppImageOptions *options);
+pmReadout* ppImageDetrendSelectFirst(pmCell *cell, char *name, bool doThis);
+
+bool ppImageRebinChip (pmConfig *config, pmFPAview *view, ppImageOptions *options, char *outName);
+bool ppImageRebinReadout (pmReadout *output, pmReadout *input, pmFPAfile *outFile, ppImageOptions *options);
+
+bool ppImagePhotom (pmConfig *config, pmFPAview *view);
+bool ppImageAstrom (pmConfig *config);
+bool ppImageAddstar (pmConfig *config);
+
+bool ppImageReplaceBackground (pmConfig *config, pmFPAview *view, ppImageOptions *options);
+
+bool ppImageMosaicChip (pmConfig *config, const ppImageOptions *options, const pmFPAview *view,
+                        const char *outFile, const char *inFile);
+bool ppImageMosaicFPA (pmConfig *config, const ppImageOptions *options,
+                       const char *outFile, const char *inFile);
+
+bool ppImageSetMaskBits (pmConfig *config, ppImageOptions *options);
+
+void ppImageFileCheck (pmConfig *config);
+
+// functions used by ppFocus
+pmConfig *ppFocusArguments(int argc, char **argv);
+ppImageOptions *ppFocusParseCamera (pmConfig *config, int entry);
+bool ppFocusGetFWHM (pmConfig *config, psVector *focus, psVector *fwhm);
+bool ppFocusFitFWHM (pmConfig *config, psVector *focus, psVector *fwhm);
+
+void ppFocusDropCamera (pmConfig *config);
+
+bool ppImageDefineFile (pmConfig *config, pmFPA *input, char *filerule, char *argname, pmFPAfileType fileType, pmDetrendType detrendType);
+
+// calculate stats, including MD5
+bool ppImageStats (pmConfig *config, pmChip *chip, const pmFPAview *inputView, const ppImageOptions *options);
+
+// write stats to output file
+bool ppImageStatsOutput (pmConfig *config, const ppImageOptions *options);
+
+
+// Measure fringes
+bool ppImageDetrendFringeMeasure(pmReadout *readout, // Readout to measure
+                                 pmCell *fringe, // Fringe cell (each readout is a different component)
+                                 const bool isResidual,
+                                 const ppImageOptions *options // Options
+    );
+
+// Solve the fringe system
+bool ppImageDetrendFringeSolve(pmChip *scienceChip, // Chip with science
+                               const pmChip *refChip, // Chip with reference fringes
+                               const bool isResidual,
+                               const ppImageOptions *options // Options
+    );
+
+// Generate fringe frame
+bool ppImageDetrendFringeGenerate(pmCell *science, // Science cell
+                                  pmCell *fringes, // Fringe cell, one readout per fringe component
+                                  const ppImageOptions *options // Options
+    );
+
+
+bool ppImageDetrendFringeApply (pmConfig *config, // config
+                                pmChip *chip, // science chip
+                                const pmFPAview *inputView, // current view
+                                const ppImageOptions *options // options
+    );
+
+/// Return short version information
+psString ppImageVersion(void);
+
+/// Return long version information
+psString ppImageVersionLong(void);
+
+/// Update the metadata with version information for all dependencies
+void ppImageVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
+    );
+
+
+// calculate stats, including MD5
+bool ppImagePixelStats (pmConfig *config, const ppImageOptions *options, const pmFPAview *inputView);
+
+// calculate stats from headers and concepts
+bool ppImageMetadataStats (pmConfig *config, const ppImageOptions *options);
+
+void ppImageFileCheck (pmConfig *config);
+
+/// Dump memory summary to text file
+void ppImageMemoryDump(const char *description);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageAddstar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageAddstar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageAddstar.c	(revision 22322)
@@ -0,0 +1,120 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppImage.h"
+
+bool setFilename (pmFPAfile *file, pmFPAview *view);
+bool addstarFile (char *addstarCommand, char *filename);
+
+bool ppImageAddstar (pmConfig *config) {
+
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    // select recipe options supplied on command line
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, RECIPE_NAME);
+
+    // find a pmFPAfile PSASTRO.OUTPUT
+    pmFPAfile *file = psMetadataLookupPtr (&status, config->files, "PSASTRO.OUTPUT");
+    if (!status) {
+        psError (PS_ERR_IO, true, "no PSASTRO.OUTPUT file defined");
+        return false;
+    }
+
+    // find the addstar command: %s is replaced with name of output file
+    char *addstarCommand = psMetadataLookupStr (&status, recipe, "ADDSTAR.COMMAND");
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+    if (file->fileLevel == PM_FPA_LEVEL_CHIP) {
+        // call addstar on this file
+        setFilename (file, view);
+        addstarFile (addstarCommand, file->filename);
+        return true;
+    }
+
+    while ((chip = pmFPAviewNextChip (view, file->fpa, 1)) != NULL) {
+        psLogMsg ("ppImageAddstar", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) { continue; }
+
+        if (file->fileLevel == PM_FPA_LEVEL_CHIP) {
+            // call addstar on this file
+            setFilename (file, view);
+            addstarFile (addstarCommand, file->filename);
+            continue;
+        }
+
+        while ((cell = pmFPAviewNextCell (view, file->fpa, 1)) != NULL) {
+            psLogMsg ("ppImageAddstar", 5, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (! cell->process || ! cell->file_exists) { continue; }
+
+            if (file->fileLevel == PM_FPA_LEVEL_CELL) {
+                // call addstar on this file
+                setFilename (file, view);
+                addstarFile (addstarCommand, file->filename);
+                continue;
+            }
+
+            // process each of the readouts
+            while ((readout = pmFPAviewNextReadout (view, file->fpa, 1)) != NULL) {
+                if (! readout->data_exists) { continue; }
+
+                if (file->fileLevel == PM_FPA_LEVEL_READOUT) {
+                    // call addstar on this file
+                    setFilename (file, view);
+                    addstarFile (addstarCommand, file->filename);
+                    continue;
+                } else {
+                    psError (PS_ERR_IO, true, "inconsistent fileLevel for %s: %d\n", file->name, file->fileLevel);
+                    return false;
+                }
+            }
+        }
+    }
+    return true;
+}
+
+bool setFilename (pmFPAfile *file, pmFPAview *view) {
+
+    // 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 are not allowed for output
+    if (!strcasecmp (file->filename, "@FILES")) {
+        psError(PS_ERR_IO, true, "indirect filenames are not allowed for output : %s\n", file->name);
+        return false;
+    }
+    if (!strcasecmp (file->filename, "@DETDB")) {
+        psError(PS_ERR_IO, true, "detrend db filenames are not allowed for output : %s\n", file->name);
+        return false;
+    }
+    return true;
+}
+
+bool addstarFile (char *addstarCommand, char *filename) {
+
+    bool status;
+    char *addstarLine = psStringCopy(addstarCommand);
+    psStringSubstitute(&addstarCommand, filename, "%s");
+
+    // catch addstar stderr/stdout and do what?
+    psIOBuffer *buffer = psIOBufferAlloc (512);
+    psPipe *pipe = psPipeOpen (addstarLine);
+    status = psIOBufferReadEmpty (buffer, 100, pipe->fd_stdout);
+    if (!status) {
+        psError (PS_ERR_IO, false, "detselect is not responding");
+    }
+    psFree (addstarLine);
+    psFree (buffer);
+    psPipeClose (pipe);
+    psFree (pipe);
+    return status;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageArguments.c	(revision 22322)
@@ -0,0 +1,147 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+static void usage (void) {
+    fprintf(stderr, "USAGE: ppImage [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n\n");
+    fprintf(stderr, "Optional arguments:\n");
+    fprintf(stderr, "\t-stats STATS.mdc: Output statistics into STATS.mdc\n");
+    fprintf(stderr, "\t-isfringe: The input image contains fringe data.\n");
+    fprintf(stderr, "\t-isdark: The input image contains dark data.\n");
+    fprintf(stderr, "\t-usemask MASKVAL: Use this mask value (override recipe).\n");
+    fprintf(stderr, "\t-chip CHIPNUM: Only process this chip number.\n");
+    fprintf(stderr, "\t-norm VALUE: Divide through by this value when done.\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "Input options (single file / file list):\n");
+    fprintf(stderr, "\t-bias/-biaslist: Bias image.\n");
+    fprintf(stderr, "\t-dark/-darklist: Dark image.\n");
+    fprintf(stderr, "\t-shutter/-shutterlist: Shutter image.\n");
+    fprintf(stderr, "\t-flat/-flatlist: Flat image.\n");
+    fprintf(stderr, "\t-mask/-masklist: Mask image.\n");
+    fprintf(stderr, "\t-fringe/-fringelist: Fringe image and data.\n");
+    fprintf(stderr, "\n");
+    exit (2);
+}
+
+pmConfig *ppImageArguments(int argc, char **argv)
+{
+    int argnum;                         // Argument number of interest
+
+    if (argc == 1) {
+        usage();
+    }
+
+    if (psArgumentGet (argc, argv, "-version")) {
+        psString version;
+        version = ppImageVersionLong();   fprintf (stdout, "%s\n", version); psFree (version);
+        version = ppStatsVersionLong();   fprintf (stdout, "%s\n", version); psFree (version);
+        version = psphotVersionLong();    fprintf (stdout, "%s\n", version); psFree (version);
+        version = psastroVersionLong();   fprintf (stdout, "%s\n", version); psFree (version);
+        version = psModulesVersionLong(); fprintf (stdout, "%s\n", version); psFree (version);
+        version = psLibVersionLong();     fprintf (stdout, "%s\n", version); psFree (version);
+        exit (0);
+    }
+
+    // load the site-wide configuration information
+    pmConfig *config = pmConfigRead(&argc, argv, RECIPE_NAME);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // save the following additional recipe values based on command-line options
+    // these options override the PPIMAGE recipe values loaded from recipe files
+    psMetadata *options = pmConfigRecipeOptions (config, RECIPE_NAME);
+
+    // save these recipe options until we have loaded the options
+    // psMetadata *options = psMetadataAlloc ();
+    // psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "PPIMAGE.OPTIONS",  PS_DATA_METADATA, "", options);
+
+    if ((argnum = psArgumentGet(argc, argv, "-stats"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "STATS", PS_META_REPLACE,
+                         "Filename for summary statistics", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-isfringe"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddBool(config->arguments, PS_LIST_TAIL, "INPUT_IS_FRINGE", PS_META_REPLACE,
+                          "Input is fringe image", true);
+    }
+    if ((argnum = psArgumentGet(argc, argv, "-isdark"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddBool(config->arguments, PS_LIST_TAIL, "INPUT_IS_DARK", PS_META_REPLACE,
+                          "Input is dark image", true);
+    }
+
+    // Number of threads
+    if ((argnum = psArgumentGet(argc, argv, "-threads"))) {
+        psArgumentRemove(argnum, &argc, argv);
+	int nThreads = atoi(argv[argnum]);
+        psMetadataAddS32(config->arguments, PS_LIST_TAIL, "NTHREADS", 0, "number of warp threads", nThreads);
+        psArgumentRemove(argnum, &argc, argv);
+
+	// create the thread pool with number of desired threads, supplying our thread launcher function
+	// XXX need to determine the number of threads from the config data
+	psThreadPoolInit (nThreads);
+    }
+
+    // the following options override the PPIMAGE recipe options
+
+    // recipe option: -usemask : override MASK setting in recipe
+    if ((argnum = psArgumentGet(argc, argv, "-usemask"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddBool(options, PS_LIST_TAIL, "MASK", PS_META_REPLACE, "", true);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    // XXX add other PPIMAGE recipe options here
+
+    // the input file is a required argument; if not found, we will exit
+    bool status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
+    if (!status) {
+        usage ();
+    }
+
+    // if these command-line options are supplied, load the file name lists into config->arguments
+    // override any configuration-specified source for these files
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "BIAS", "-bias", "-biaslist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "DARK", "-dark", "-darklist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "SHUTTER", "-shutter", "-shutterlist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "FLAT", "-flat", "-flatlist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "MASK", "-mask", "-masklist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "FRINGE", "-fringe", "-fringelist");
+
+    // chip selection is used to limit chips to be processed
+    if ((argnum = psArgumentGet (argc, argv, "-chip"))) {
+        psArgumentRemove (argnum, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_DATA_STRING, "", argv[argnum]);
+        psArgumentRemove (argnum, &argc, argv);
+    }
+
+    // Optional normalisation factor
+    if ((argnum = psArgumentGet(argc, argv, "-norm"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        float norm = atof(argv[argnum]);
+        psMetadataAddF32(config->arguments, PS_LIST_TAIL, "NORMALISATION", 0,
+                         "Normalisation to apply", norm);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-dumpconfig"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "DUMP_CONFIG", PS_META_REPLACE,
+                         "Filename for configuration dump", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    if (argc != 2) usage ();
+
+    // Add the input and output images (which remain on the command-line) to the arguments list
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageAstrom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageAstrom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageAstrom.c	(revision 22322)
@@ -0,0 +1,66 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppImage.h"
+
+// this function is mostly equivalent to the top-level of psastro, with some
+// modifications since the data has already been loaded.
+bool ppImageAstrom (pmConfig *config) {
+
+    bool status;
+
+    // select recipe options supplied on command line
+    // XXX move these options to the "PSASTRO" recipe?
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSASTRO_RECIPE);
+
+    // find or define a pmFPAfile PSPHOT.INPUT
+    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PSASTRO.INPUT");
+    PS_ASSERT (input, false);
+
+    ppImageMemoryDump("astrom");
+
+    // convert the output sources created by psphot into astrometry objects
+    if (!psastroConvertFPA (input->fpa, recipe)) {
+        psError (PSASTRO_ERR_UNKNOWN, false, "error reading input data\n");
+        return false;
+    }
+
+    if (!psastroAnalysis (config)) {
+        psError (PSASTRO_ERR_UNKNOWN, false, "failure in psastro analysis\n");
+        return false;
+    }
+
+    // deactivate the psastro files, reactive when needed
+    pmFPAfileActivate (config->files, false, NULL);
+    pmFPAfileActivate (config->files, true, "PSASTRO.OUTPUT");
+
+    // loop over all chips and perform IO needed
+    pmChip *chip = NULL;
+    pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
+    while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
+        psLogMsg ("ppImageLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+
+        ppImageMemoryDump("astrom");
+
+        // Output and Close at Chip level
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            psFree(view);
+            return false;
+        }
+    }
+
+    // Output and Close FPA
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        psFree(view);
+        return false;
+    }
+
+    // deactivate the PSASTRO files, re-active all else
+    // XXX do we need a way to activate / deactivate other groups?
+    pmFPAfileActivate (config->files, true, NULL);
+    pmFPAfileActivate (config->files, false, "PSASTRO.OUTPUT");
+
+    psFree(view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageCleanup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageCleanup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageCleanup.c	(revision 22322)
@@ -0,0 +1,28 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppImage.h"
+
+void ppImageCleanup (pmConfig *config, ppImageOptions *options)
+{
+    // Free memory used by ppImage
+    psFree(options);
+    psFree(config);
+
+    // Free memory used by psModules
+    pmConceptsDone();
+    pmConfigDone();
+    pmModelClassCleanup();
+
+    // Free memory used by psLib
+    psLibFinalize();
+
+    // psMemBlock **memblocks;
+    // int Nleaks = psMemCheckLeaks (0, &memblocks, stderr, false);
+    // fprintf (stderr, "Found %d leaks at %s\n", Nleaks, "ppImage");
+
+    fprintf (stderr, "Found %d leaks at %s\n", psMemCheckLeaks (0, NULL, NULL, false), "ppImage");
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDefineFile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDefineFile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDefineFile.c	(revision 22322)
@@ -0,0 +1,55 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppImage.h"
+
+bool ppImageDefineFile (pmConfig *config, pmFPA *input, char *filerule, char *argname, pmFPAfileType fileType, pmDetrendType detrendType) {
+
+    bool status;
+    pmFPAfile *file;
+
+    // look for the file on the argument list
+    file = pmFPAfileDefineFromArgs  (&status, config, filerule, argname);
+    if (!status) {
+	psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+	return false;
+    }
+    if (file) {
+	if (file->type != fileType) {
+	    psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType (fileType));
+	    return false;
+	}
+	return true;
+    }
+
+    // look for the file in the camera config table
+    file = pmFPAfileDefineFromConf  (&status, config, filerule);
+    if (!status) {
+	psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+	return false;
+    }
+    if (file) {
+	if (file->type != fileType) {
+	    psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType (fileType));
+	    return false;
+	}
+	return true;
+    }
+
+    // look for the file to be loaded from the detrend database
+    file = pmFPAfileDefineFromDetDB (&status, config, filerule, input, detrendType);
+    if (!status) {
+	psError (PS_ERR_UNKNOWN, false, "failed to load file definition");
+	return false;
+    }
+    if (file) {
+	if (file->type != fileType) {
+	    psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType (fileType));
+	    return false;
+	}
+	return true;
+    }
+    return false;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDetrendFringe.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDetrendFringe.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDetrendFringe.c	(revision 22322)
@@ -0,0 +1,351 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+bool ppImageDetrendFringeMeasure(pmReadout *readout, pmCell *fringe, const bool isResidual, const ppImageOptions *options)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(fringe, false);
+    PS_ASSERT_PTR_NON_NULL(options, false);
+
+    // Reference fringe measurements
+    psArray *references = psMemIncrRefCounter(psMetadataLookupPtr(NULL, fringe->analysis,
+                                                                  "FRINGE.MEASUREMENTS"));
+    if (!references) {
+        references = pmFringesParse(fringe); // Reference fringes
+        if (!references) {
+            psError(PS_ERR_IO, false, "Unable to find fringe references.\n");
+            return false;
+        }
+        psMetadataAdd(fringe->analysis, PS_LIST_TAIL, "FRINGE.MEASUREMENTS", PS_DATA_UNKNOWN,
+                      "Fringe measurements", references);
+    }
+
+    pmFringeStats *reference = references->data[0]; // Take the first as representative
+    pmFringeRegions *regions = reference->regions; // Regions to measure
+    pmFringeStats *measurements = pmFringeStatsMeasure(regions, readout, options->maskValue); // Fringe stats
+
+    // Normalise measurements by the exposure time
+    bool mdok;                          // Status of MD lookup
+    float expTime = psMetadataLookupF32(&mdok, readout->parent->concepts, "CELL.EXPOSURE"); // Exp. time
+    if (!mdok || !isfinite(expTime)) {
+        psError(PS_ERR_UNKNOWN, false, "CELL.EXPOSURE is not set for --- can't normalise fringes\n");
+        psFree(measurements);
+        return false;
+    }
+    if (expTime == 0) {
+        psWarning("Exposure time is zero --- are you sure you want fringe subtraction?\n");
+        expTime = 1.0;
+    }
+    // normalize by exposure time : NOTE df is 1/sigma
+    psBinaryOp(measurements->f, measurements->f, "*", psScalarAlloc(1.0 / expTime, PS_TYPE_F32));
+    psBinaryOp(measurements->df, measurements->df, "*", psScalarAlloc(expTime, PS_TYPE_F32));
+
+    char *scienceFringes = NULL;
+    if (isResidual) {
+	scienceFringes = psStringCopy ("FRINGE.RESIDUALS");
+    } else {
+	scienceFringes = psStringCopy ("FRINGE.MEASUREMENTS");
+    }
+
+    // Science fringe measurements
+    pmFringeStats *previous = psMetadataLookupPtr(NULL, readout->parent->analysis, scienceFringes);
+    if (previous) {
+        // Multiple readouts: concatenate
+        psArray *concatenate = psArrayAlloc(2); // Array to hold fringes
+
+        // Concatenate science measurements
+        concatenate->data[0] = previous;
+        concatenate->data[1] = measurements;
+        pmFringeStats *new = pmFringeStatsConcatenate(concatenate, NULL, NULL); // New measurements
+        psFree(measurements);
+        measurements = new;
+
+        // Concatenate reference measurements (duplication, so the science and reference line up)
+        for (int i = 0; i < references->n; i++) {
+            concatenate->data[0] = concatenate->data[1] = references->data[i];
+            pmFringeStats *refNew = pmFringeStatsConcatenate(concatenate, NULL, NULL);
+            psFree(references->data[i]);
+            references->data[i] = refNew;
+        }
+        concatenate->data[0] = concatenate->data[1] = NULL;
+        psFree(concatenate);
+    }
+
+    psMetadataAdd(readout->parent->analysis, PS_LIST_TAIL, scienceFringes,
+                  PS_DATA_UNKNOWN | PS_META_REPLACE, "Fringe measurements", measurements);
+    psFree(measurements);
+    psFree(scienceFringes);
+    psFree(references);
+
+    return true;
+}
+
+
+// Pull the fringes out of the cell analysis FRINGE.MEASUREMENTS for a chip
+// XXX need some error checks
+static psArray *getFringes(const pmChip *chip, const char *source)
+{
+    psArray *cells = chip->cells;       // Component cells
+    psArray *fringes = psArrayAlloc(cells->n); // Fringes, to return
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+
+	// XXX for now, skip the video cells (cell->readouts->n > 1)
+	if (cell->readouts->n > 1) {
+	  psWarning ("Skipping Video Cell for ppImageDetrendFringe.c:getFringes");
+	  continue;
+	}
+
+        fringes->data[i] = psMemIncrRefCounter(psMetadataLookupPtr(NULL, cell->analysis, source));
+    }
+
+    return fringes;
+}
+
+
+// Solve the fringe system: we have science fringe measurements for each cell, and an array of reference
+// fringe measurements for each cell.  Need to concatenate these together first, and then solve.
+bool ppImageDetrendFringeSolve(pmChip *scienceChip, const pmChip *refChip, const bool isResidual, const ppImageOptions *options)
+{
+    PS_ASSERT_PTR_NON_NULL(scienceChip, NULL);
+    PS_ASSERT_PTR_NON_NULL(refChip, NULL);
+    PS_ASSERT_PTR_NON_NULL(options, NULL);
+
+    psArray *science = NULL;
+    if (isResidual) {
+	science = getFringes(scienceChip, "FRINGE.RESIDUALS"); // Fringe residuals on science chip
+    } else {
+	science = getFringes(scienceChip, "FRINGE.MEASUREMENTS"); // Fringe measurements on science chip
+    }
+
+    pmFringeStats *scienceCat = pmFringeStatsConcatenate(science, NULL, NULL); // Science fringes
+    psFree(science);
+
+    // Need to transform the array of cells each with an array of fringes --> array of fringes for the chip as
+    // a whole
+    psArray *references = getFringes(refChip, "FRINGE.MEASUREMENTS"); // Fringe measurements on reference chip
+    int numRefs = ((psArray*)references->data[0])->n; // Number of reference fringes
+    psArray *referencesCat = psArrayAlloc(numRefs); // Reference fringes
+    for (int i = 0; i < numRefs; i++) { // Iterate over fringes
+        psArray *refs = psArrayAlloc(references->n); // Array of fringes for each cell
+        for (int j = 0; j < references->n; j++) { // Iterate over cells
+            psArray *ref = references->data[j]; // Array of references for this cell
+            refs->data[j] = psMemIncrRefCounter(ref->data[i]);
+        }
+        referencesCat->data[i] = pmFringeStatsConcatenate(refs, NULL, NULL);
+        psFree(refs);
+    }
+    psFree(references);
+
+    // Now we can solve
+    psTrace("ppImage", 3, "Solving fringe system...\n");
+    pmFringeScale *solution = pmFringeScaleMeasure(scienceCat, referencesCat, options->fringeRej,
+                                                   options->fringeIter, options->fringeKeep);
+
+    if (isResidual) {
+	psMetadataAdd(scienceChip->analysis, PS_LIST_TAIL, "FRINGE.RESIDUAL.SOLUTION", PS_DATA_UNKNOWN, "Fringe solution", solution);
+    } else {
+	psMetadataAdd(scienceChip->analysis, PS_LIST_TAIL, "FRINGE.SOLUTION", PS_DATA_UNKNOWN, "Fringe solution", solution);
+    }
+
+# if (0)
+    // write the fringe amplitude or residual amplitude to the header
+    // XXX this is measured per cell, but we only have headers per chip
+    pmHDU *hdu = pmHDUFromCell(science);// HDU  of interest
+    for (int i = 0; i < solution->nFringeFrames; i++) {
+	// write metadata header value
+	psString keyword = NULL;
+	if (isResidual) {
+	    psStringAppend (&keyword, "FRES_%02dV", i);
+	} else {
+	    psStringAppend (&keyword, "FRNG_%02dV", i);
+	}
+	psMetadataAddF32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, "Fringe Amplitude", solution->coeff->data.F32[i + 1]);
+	psFree (keyword);
+
+	keyword = NULL;
+	if (isResidual) {
+	    psStringAppend (&keyword, "FRES_%02dE", i);
+	} else {
+	    psStringAppend (&keyword, "FRNG_%02dE", i);
+	}
+	psMetadataAddF32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, "Fringe Amplitude error", solution->coeffErr->data.F32[i + 1]);
+	psFree (keyword);
+    }
+# endif
+
+    psFree(solution);
+    psFree(scienceCat);
+    psFree(referencesCat);
+
+    return true;
+}
+
+
+bool ppImageDetrendFringeGenerate(pmCell *science, pmCell *fringes, const ppImageOptions *options)
+{
+    PS_ASSERT_PTR_NON_NULL(science, false);
+    PS_ASSERT_PTR_NON_NULL(fringes, false);
+
+    pmFringeScale *solution = psMetadataLookupPtr(NULL, science->parent->analysis, "FRINGE.SOLUTION");
+    if (!solution) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find fringe solution.\n");
+        return false;
+    }
+    assert(fringes->readouts->n == solution->nFringeFrames);
+
+    bool mdok;                          // Status of MD lookup
+    float expTime = psMetadataLookupF32(&mdok, science->concepts, "CELL.EXPOSURE"); // Exp. time
+    if (!mdok || !isfinite(expTime)) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.EXPOSURE is not set --- can't renormalise fringes\n");
+        return false;
+    }
+    if (expTime == 0) {
+        psWarning("Exposure time is zero --- are you sure you want fringe subtraction?\n");
+        expTime = 1.0;
+    }
+
+    pmHDU *hdu = pmHDUFromCell(science);// HDU  of interest
+
+    // Construct the fringe image from the scale
+    psTrace("ppImage", 3, "Generating fringe correction...\n");
+    psImage *sumFringe = NULL; // Sum of the fringes
+    for (int i = 0; i < solution->nFringeFrames; i++) {
+        pmReadout *fringeRO = fringes->readouts->data[i]; // Fringe readout
+        psImage *fringe = fringeRO->image; // Fringe image
+        psTrace("ppImage", 5, "Scale for fringe component %d is %f\n",
+                i, solution->coeff->data.F32[i + 1]);
+        psBinaryOp(fringe, fringe, "*", psScalarAlloc(solution->coeff->data.F32[i + 1], PS_TYPE_F32));
+        if (!sumFringe) {
+            sumFringe = psImageCopy(NULL, fringe, PS_TYPE_F32);
+        } else {
+            psBinaryOp(sumFringe, sumFringe, "+", fringe);
+        }
+
+        psVector *md5 = psImageMD5(fringe); // md5 hash
+        psString md5string = psMD5toString(md5); // String
+        psFree(md5);
+        psStringPrepend(&md5string, "Fringe image %d (scale %.3f) MD5: ",
+                        i, solution->coeff->data.F32[i + 1]);
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                         md5string, "");
+        psFree(md5string);
+
+# if (0)
+	// write metadata header value
+	// XXX this is measured per cell, but we only have headers per chip
+	psString keyword = NULL;
+	psStringAppend (&keyword, "FRNG_%02dV", i);
+	psMetadataAddF32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, "Fringe Amplitude", solution->coeff->data.F32[i + 1]);
+	psFree (keyword);
+
+	keyword = NULL;
+	psStringAppend (&keyword, "FRNG_%02dE", i);
+	psMetadataAddF32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, "Fringe Amplitude error", solution->coeffErr->data.F32[i + 1]);
+	psFree (keyword);
+# endif
+
+    }
+    if (expTime != 1.0) {
+        psBinaryOp(sumFringe, sumFringe, "*", psScalarAlloc(expTime, PS_TYPE_F32));
+    }
+
+    // Apply the correction to all readouts
+    psArray *readouts = science->readouts; // Array of science readouts
+    for (int i = 0; i < readouts->n; i++) {
+        pmReadout *readout = readouts->data[i]; // Readout of interest
+        if (!readout || !readout->data_exists) {
+            continue;
+        }
+
+        // XXX: Make generic, so subregions may be subtracted as well
+        if (!psBinaryOp(readout->image, readout->image, "-", sumFringe)) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to subtract fringe.\n");
+            return false;
+        }
+
+	// measure residual fringe amplitude. results go to FRINGE.RESIDUALS
+	ppImageDetrendFringeMeasure (readout, fringes, true, options);
+    }
+    psFree(sumFringe);
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now, used for reporting
+    psString timeString = psTimeToISO(time); // String with time
+    psFree(time);
+    psStringPrepend(&timeString, "Fringe correction completed at ");
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                     timeString, "");
+    psFree(timeString);
+
+    return sumFringe;
+}
+
+bool ppImageDetrendFringeApply (pmConfig *config, pmChip *chip, const pmFPAview *inputView, const ppImageOptions *options) {
+
+    pmCell *cell = NULL;
+
+    assert (options->doFringe); // do not call if not needed
+    assert (inputView->chip != -1);
+    assert (inputView->cell == -1);
+    assert (inputView->readout == -1);
+
+    pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
+    *view = *inputView;
+
+    // select the reference chip
+    pmChip *fringe = pmFPAfileThisChip(config->files, view, "PPIMAGE.FRINGE");
+    if (!fringe) {
+	psError(PS_ERR_UNKNOWN, false, "missing fringe reference data.\n");
+	psFree (view);
+	return false;
+    }
+
+    // Solve the fringe system
+    if (!ppImageDetrendFringeSolve(chip, fringe, false, options)) {
+	psError(PS_ERR_UNKNOWN, false, "failed to solve the fringe system.\n");
+	psFree (view);
+	return false;
+    }
+
+    // Go back over the cells to apply the fringe correction
+    view->cell = view->readout = -1;
+    while ((cell = pmFPAviewNextCell(view, chip->parent, 1)) != NULL) {
+	if (!cell->process || !cell->file_exists) {
+	    continue;
+	}
+
+	// Apply the fringe correction
+	psTrace("ppImage", 3, "Applying fringe correction...\n");
+	pmCell *fringeCell = pmFPAfileThisCell(config->files, view, "PPIMAGE.FRINGE");
+	if (!fringeCell) {
+	    psError(PS_ERR_UNKNOWN, false, "missing fringe reference data.\n");
+	    psFree (view);
+	    return false;
+	}
+
+	// XXX for now, skip the video cells (cell->readouts->n > 1)
+	if (cell->readouts->n > 1) {
+	  psWarning ("Skipping Video Cell for ppImageDetrendFringeApply");
+	  continue;
+	}
+
+	if (!ppImageDetrendFringeGenerate(cell, fringeCell, options)) {
+	    psError(PS_ERR_UNKNOWN, false, "failed to apply fringe image.\n");
+	    psFree (view);
+	    return false;
+	}
+    }
+
+    // Solve the residual fringe system
+    if (!ppImageDetrendFringeSolve(chip, fringe, true, options)) {
+	psError(PS_ERR_UNKNOWN, false, "failed to solve the residual fringe system.\n");
+	psFree (view);
+	return false;
+    }
+
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDetrendNonLinear.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDetrendNonLinear.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDetrendNonLinear.c	(revision 22322)
@@ -0,0 +1,114 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+bool ppImageDetrendNonLinearPolynomial(pmReadout *input, psMetadataItem *dataItem) {
+
+    // These are the polynomial coefficients
+    psVector *coeff = dataItem->data.V; // The coefficient vector
+    if (coeff->type.type != PS_TYPE_F64) {
+        psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64); // F64 version
+        psFree (coeff);
+        coeff = temp;
+    }
+    psPolynomial1D *correction = psPolynomial1DAlloc(coeff->n - 1, PS_POLYNOMIAL_ORD);
+    psFree(correction->coeff);
+    correction->coeff = psMemIncrRefCounter(coeff->data.F64);
+    pmNonLinearityPolynomial(input, correction);
+    psFree(coeff);
+    psFree(correction);
+    return true;
+}
+
+bool ppImageDetrendNonLinearLookup(pmReadout *input, psMetadataItem *dataItem) {
+
+    // This is a filename: lookup table
+    char *name = dataItem->data.V;       // Filename
+    psLookupTable *table = psLookupTableAlloc(name, "%f %f", 0);
+    if (psLookupTableRead(table) <= 0) {
+        psErrorStackPrint(stderr, "Unable to read non-linearity correction file "
+                          "%s --- ignored\n", name);
+        return false;
+    }
+#ifdef PRODUCTION
+    pmNonLinearityLookup(input, table);
+#else
+    psVector *influx = table->values->data[0];
+    psVector *outflux = table->values->data[1];
+    pmNonLinearityLookup(input, influx, outflux);
+#endif
+    psFree(table);
+    return true;
+}
+
+bool ppImageDetrendNonLinear(pmReadout *input, ppImageOptions *options) {
+
+    psMetadataItem *concept;
+    pmCell *cell = input->parent;
+
+    switch (options->nonLinearType) {
+      case PS_DATA_VECTOR:
+        ppImageDetrendNonLinearPolynomial (input, options->nonLinearData);
+        return true;
+
+      case PS_DATA_STRING:
+        ppImageDetrendNonLinearLookup (input, options->nonLinearData);
+        return true;
+
+      case PS_DATA_METADATA:
+        // XXX EAM: this is somewhat confusing : let's wrap in a function when i understand it
+
+        // Go looking for the value in the hierarchy
+        concept = psMetadataLookup(cell->concepts, options->nonLinearSource);
+        if (! concept) {
+            pmChip *chip = cell->parent;// Parent chip
+            concept = psMetadataLookup(chip->concepts, options->nonLinearSource);
+            if (! concept) {
+                pmFPA *fpa = chip->parent; // Parent FPA
+                concept = psMetadataLookup(fpa->concepts, options->nonLinearSource);
+                if (! concept) {
+                    psLogMsg("phase2", PS_LOG_WARN, "Unable to find value of concept %s "
+                             "for non-linearity correction --- ignored.\n", (char *)options->nonLinearSource);
+                    return false;
+                }
+            }
+        }
+
+        if (concept->type != PS_DATA_STRING) {
+            psLogMsg("phase2", PS_LOG_WARN, "Type for concept %p isn't STRING, as"
+                     " expected for non-linearity correction --- ignored.\n",
+                     concept);
+            return false;
+        }
+
+        // Get the value of the concept
+        psString conceptValue = concept->data.V;
+        psMetadata *folder = (psMetadata *)options->nonLinearData->data.V;
+        psMetadataItem *optionItem = psMetadataLookup(folder, conceptValue);
+        if (!optionItem) {
+            psLogMsg("phase2", PS_LOG_WARN, "Unable to find %s in NONLIN.DATA"
+                     " --- ignored.\n", conceptValue);
+            return false;
+        }
+
+        switch (optionItem->type) {
+          case PS_DATA_VECTOR:
+            ppImageDetrendNonLinearPolynomial (input, optionItem);
+            return true;
+          case PS_DATA_STRING:
+            ppImageDetrendNonLinearLookup (input, optionItem);
+            return true;
+          default:
+            psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction "
+                     "desired but unable to interpret NONLIN.DATA for %s"
+                     " --- ignored\n", conceptValue);
+            return false;
+        }
+      default:
+        psAbort("Invalid options->nonLinearType");
+    }
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDetrendReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDetrendReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageDetrendReadout.c	(revision 22322)
@@ -0,0 +1,113 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+bool ppImageDetrendReadout(pmConfig *config, ppImageOptions *options, pmFPAview *view)
+{
+    // construct a view for the detrend images (which have only one readout)
+    pmFPAview *detview = pmFPAviewAlloc(0);
+    *detview = *view;
+    detview->readout = 0;
+
+    // find the currently selected readout
+    pmReadout *input = pmFPAfileThisReadout(config->files, view, "PPIMAGE.INPUT");
+
+    // Masking on the basis of pixel value needs to be done before anything else, so the values are pristine.
+    if (options->doMaskBuild) {
+        pmReadoutGenerateMask(input, options->satMask, options->badMask);
+    }
+    // apply the externally supplied mask to the input->mask pixels
+    if (options->doMask) {
+        pmReadout *mask = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.MASK");
+        pmMaskBadPixels(input, mask, options->maskValue);
+    }
+
+# if 0
+    // Non-linearity correction
+    if (options->doNonLin) {
+        ppImageDetrendNonLinear(detrend->input, input, options);
+    }
+# endif
+
+    // set up the dark and bias
+    pmCell *dark = NULL;                // Multi-dark
+    pmReadout *oldDark = NULL;          // Old-fashioned dark
+    pmReadout  *bias = NULL;
+    if (options->doBias) {
+        bias = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.BIAS");
+    }
+    if (options->doDark) {
+        bool mdok;                      // Status of MD lookup
+        psMetadata *recipe = psMetadataLookupPtr (&mdok, config->recipes, RECIPE_NAME);
+        assert(mdok && recipe);
+        if (psMetadataLookupBool(&mdok, recipe, "OLDDARK")) {
+            oldDark = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.DARK");
+        } else {
+            dark = pmFPAfileThisCell(config->files, detview, "PPIMAGE.DARK");
+        }
+    }
+
+    // Bias, dark and overscan subtraction are all merged.
+    if (options->doBias || options->doOverscan) {
+        if (!pmBiasSubtract(input, options->overscan, bias, oldDark, view)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to subtract bias.");
+            return false;
+        }
+    }
+
+    // Weight on the basis of pixel value needs to be done after the overscan has been subtracted
+    if (options->doWeightBuild) {
+        // create the target mask and weight images
+        pmReadoutGenerateWeight(input, true);
+    }
+
+    if (options->doDark && dark) {
+        if (!pmDarkApply(input, dark, options->maskValue)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to subtract dark.");
+            return false;
+        }
+    }
+
+    // Shutter correction
+    if (options->doShutter) {
+        pmReadout *shutter = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.SHUTTER");
+        if (!pmShutterCorrectionApply(input, shutter, pmConfigMaskGet("FLAT", config))) {
+            return false;
+        }
+    }
+
+    // Flat-field correction (no options used?)
+    if (options->doFlat) {
+        pmReadout *flat = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.FLAT");
+        if (!pmFlatField(input, flat, options->flatMask)) {
+            return false;
+        }
+    }
+
+    // Normalisation by a (known) constant
+    bool mdok;                          // Status of MD lookup
+    float norm = psMetadataLookupF32(&mdok, config->arguments, "NORMALISATION");
+    if (mdok && isfinite(norm) && norm != 1.0) {
+        pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest
+        psString comment = NULL;        // Comment to add
+        psStringAppend(&comment, "Normalization: %f", norm);
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
+        psFree(comment);
+
+        psBinaryOp(input->image, input->image, "*", psScalarAlloc(norm, PS_TYPE_F32));
+    }
+
+    if (options->doFringe) {
+        pmCell *fringe = pmFPAfileThisCell(config->files, detview, "PPIMAGE.FRINGE");
+        if (!ppImageDetrendFringeMeasure(input, fringe, false, options)) {
+            return false;
+        }
+    }
+
+    ppImageMemoryDump("detrend");
+
+    psFree(detview);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,26 @@
+/*
+ * The line
+    { PPIMAGE_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+#include "pslib.h"
+#include "ppImageErrorCodes.h"
+
+void ppImageErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { PPIMAGE_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PPIMAGE_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = PPIMAGE_ERR_NERROR - PPIMAGE_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       p_psMemSetPersistent(tmp, true);
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageErrorCodes.dat	(revision 22322)
@@ -0,0 +1,10 @@
+#
+# This file is used to generate ppImageErrorClasses.h
+#
+BASE = 500		First value we use; lower values belong to psLib
+# these errors correspond to standard exit conditions
+ARGUMENTS               Incorrect arguments
+SYS                     System error
+CONFIG                  Problem in configure files
+PROG                    Programming error
+DATA                    invalid data
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,18 @@
+#if !defined(PPIMAGE_ERROR_CODES_H)
+#define PPIMAGE_ERROR_CODES_H
+/*
+ * The line
+ *  PPIMAGE_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    PPIMAGE_ERR_BASE = 512,
+    PPIMAGE_ERR_${ErrorCode},
+    PPIMAGE_ERR_NERROR
+} ppImageErrorCode;
+
+void ppImageErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageFileCheck.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageFileCheck.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageFileCheck.c	(revision 22322)
@@ -0,0 +1,60 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+void ppImageFileCheck (pmConfig *config) {
+
+    // add the output names to the output-type files
+    psMetadataItem *item = NULL;
+    psMetadataIterator *iter = psMetadataIteratorAlloc (config->files, PS_LIST_HEAD, NULL);
+    while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
+        pmFPAfile *file = item->data.V;
+        pmFPA *fpa = file->fpa;
+        fprintf (stderr, "file %s\n", file->name);
+        if (!fpa) {
+            fprintf (stderr, "  has no FPA\n");
+            continue;
+        }
+        if (fpa->hdu) {
+            if (fpa->hdu->images) fprintf (stderr, "  (%d,%d) images\n", -1, -1);
+            if (fpa->hdu->weights) fprintf (stderr, "  (%d,%d) weights\n", -1, -1);
+            if (fpa->hdu->masks) fprintf (stderr, "  (%d,%d) masks\n", -1, -1);
+            if (fpa->hdu->header) fprintf (stderr, "  (%d,%d) header\n", -1, -1);
+        } else {
+            // fprintf (stderr, "  has no fpa data (%d,%d)\n", -1, -1);
+        }
+        for (int i = 0; i < fpa->chips->n; i++) {
+            pmChip *chip = fpa->chips->data[i];
+            if (chip->hdu) {
+                if (chip->hdu->images) fprintf (stderr, "  (%d,%d) images\n", i, -1);
+                if (chip->hdu->weights) fprintf (stderr, "  (%d,%d) weights\n", i, -1);
+                if (chip->hdu->masks) fprintf (stderr, "  (%d,%d) masks\n", i, -1);
+                if (chip->hdu->header) fprintf (stderr, "  (%d,%d) header\n", i, -1);
+            } else {
+                // fprintf (stderr, "  has no chip data (%d,%d)\n", i, -1);
+            }
+            for (int j = 0; j < chip->cells->n; j++) {
+                pmCell *cell = chip->cells->data[j];
+                if (cell->hdu) {
+                    if (cell->hdu->images) fprintf (stderr, "  (%d,%d) images\n", i, j);
+                    if (cell->hdu->weights) fprintf (stderr, "  (%d,%d) weights\n", i, j);
+                    if (cell->hdu->masks) fprintf (stderr, "  (%d,%d) masks\n", i, j);
+                    if (cell->hdu->header) fprintf (stderr, "  (%d,%d) header\n", i, j);
+                } else {
+                    // fprintf (stderr, "  has no cell data (%d,%d)\n", i, j);
+                }
+                for (int k = 0; k < cell->readouts->n; k++) {
+                    pmReadout *readout = cell->readouts->data[k];
+                    if (readout) {
+                        if (readout->image) fprintf (stderr, "  (%d,%d,%d) image\n", i, j, k);
+                        if (readout->weight) fprintf (stderr, "  (%d,%d,%d) weight\n", i, j, k);
+                        if (readout->mask) fprintf (stderr, "  (%d,%d,%d) masks\n", i, j, k);
+                    }
+                }
+            }
+        }
+    }
+    psFree (iter);
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageLoop.c	(revision 22322)
@@ -0,0 +1,158 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+# define ESCAPE(MESSAGE) { \
+  psError(PS_ERR_UNKNOWN, false, MESSAGE); \
+  psFree (view); \
+  return false; \
+}
+
+bool ppImageLoop (pmConfig *config, ppImageOptions *options)
+{
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    // measure the total elapsed time in ppImageLoop.
+    psTimerStart ("ppImageLoop");
+
+    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPIMAGE.INPUT");
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find input data!\n");
+        ppImageCleanup(config, options);
+        exit(PS_EXIT_PROG_ERROR);
+    }
+
+    psString dump_file = psMetadataLookupStr(&status, config->arguments, "DUMP_CONFIG");
+    if (dump_file) {
+        pmConfigCamerasCull(config, NULL);
+        pmConfigRecipesCull(config, "PPIMAGE,PPSTATS,PSPHOT,MASKS,PSASTRO");
+
+        pmConfigDump(config, input->fpa, dump_file);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
+    pmHDU *lastHDU = NULL;              // Last HDU that was updated
+
+    // files associated with the science image
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for FPA");
+
+    while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
+        psLogMsg ("ppImageLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) {
+            continue;
+        }
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for Chip");
+
+        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
+            psLogMsg ("ppImageLoop", 5, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) {
+                continue;
+            }
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for Cell");
+
+            // Put version information into the header
+            pmHDU *hdu = pmHDUFromCell(cell);
+            if (hdu && hdu != lastHDU) {
+                ppImageVersionMetadata(hdu->header);
+                lastHDU = hdu;
+            }
+
+            // XXX for now, skip the video cells (cell->readouts->n > 1)
+            if (cell->readouts->n > 1) {
+              psWarning ("Skipping Video Cell for ppImageDetrendReadout");
+              continue;
+            }
+
+            // process each of the readouts
+            while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for Readout");
+                if (!readout->data_exists) {
+                    continue;
+                }
+
+                // XXX set the options->*Mask values here (after the mask images have been loaded
+                // and before any of the value are used)
+                if (!ppImageSetMaskBits (config, options))
+                    ESCAPE ("Unable to set bit masks");
+
+                // perform the detrend analysis
+                if (!ppImageDetrendReadout(config, options, view))
+                    ESCAPE ("Unable to detrend readout");
+            }
+        }
+
+        // Apply the fringe correction
+        if (options->doFringe) {
+            if (!ppImageDetrendFringeApply (config, chip, view, options))
+                ESCAPE ("Unable to defringe");
+        }
+
+        // measure various statistics for this image
+        if (!ppImagePixelStats (config, options, view))
+            ESCAPE ("Unable to measures stats for image");
+
+        if (!ppImageMosaicChip(config, options, view, "PPIMAGE.CHIP", "PPIMAGE.OUTPUT"))
+            ESCAPE ("Unable to mosaic chip");
+
+        // we perform photometry on the readouts of this chip in the output
+        if (options->doPhotom) {
+            if (!ppImagePhotom(config, view)) ESCAPE ("error running photometry.");
+        }
+
+        // replace the masked pixels with the background level
+        if (options->replaceMasked) {
+            if (!ppImageReplaceBackground (config, view, options))
+                ESCAPE ("Unable to replace masked pixels with background level");
+        }
+
+        // binning (used for display) must take place after the background is replaced, if desired
+        if (!ppImageRebinChip(config, view, options, "PPIMAGE.BIN1"))
+            ESCAPE ("Unable to bin chip (level 1).");
+
+        if (!ppImageRebinChip(config, view, options, "PPIMAGE.BIN2"))
+            ESCAPE ("Unable to bin chip (level 2).");
+
+        // Close cells (XXX shouldn't pmFPAfileClose iterate down as needed?)
+        view->cell = -1;
+        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
+            if (!cell->process || !cell->file_exists) {
+                continue;
+            }
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) ESCAPE ("save failure for Cell");
+        }
+
+        // Close chip
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) ESCAPE ("save failure for Chip");
+    }
+
+    // generate the full-scale FPA mosaic
+    if (!ppImageMosaicFPA(config, options, "PPIMAGE.OUTPUT.FPA1", "PPIMAGE.BIN1")) ESCAPE ("failure in FPA Mosaic (level 1)");
+    if (!ppImageMosaicFPA(config, options, "PPIMAGE.OUTPUT.FPA2", "PPIMAGE.BIN2")) ESCAPE ("failure in FPA Mosaic (level 2)");
+
+    // we perform astrometry on all chips after sources have been detected
+    // this also performs the psastro file IO
+    if (options->doAstromChip || options->doAstromMosaic) {
+        if (!ppImageAstrom(config)) ESCAPE ("error running astrometry.");
+    }
+
+    if (psTraceGetLevel("ppImage") >= 3) {
+        ppImageFileCheck (config);
+    }
+
+    // Write out summary statistics
+    if (!ppImageMetadataStats (config, options)) ESCAPE ("Unable to write statistics file.");
+
+    // Write out summary statistics
+    if (!ppImageStatsOutput (config, options)) ESCAPE ("Unable to write statistics file.");
+
+    // Output and Close FPA
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) ESCAPE ("save failure for FPA");
+
+    psFree(view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageLoop_Threaded.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageLoop_Threaded.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageLoop_Threaded.c	(revision 22322)
@@ -0,0 +1,141 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+# define ESCAPE(MESSAGE) { \
+  psError(PS_ERR_UNKNOWN, false, MESSAGE); \
+  psFree (view); \
+  return false; \
+}
+
+bool ppImageLoop (pmConfig *config, ppImageOptions *options) {
+
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    // measure the total elapsed time in ppImageLoop.
+    psTimerStart ("ppImageLoop");
+
+    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPIMAGE.INPUT");
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find input data!\n");
+        ppImageCleanup(config, options);
+        exit(PS_EXIT_PROG_ERROR);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
+    pmHDU *lastHDU = NULL;              // Last HDU that was updated
+
+    // files associated with the science image
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for FPA");
+
+    while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
+        psLogMsg ("ppImageLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) {
+            continue;
+        }
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for Chip");
+
+        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
+            psLogMsg ("ppImageLoop", 5, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) {
+                continue;
+            }
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for Cell");
+
+            // Put version information into the header
+            pmHDU *hdu = pmHDUFromCell(cell);
+            if (hdu && hdu != lastHDU) {
+                ppImageVersionMetadata(hdu->header);
+                lastHDU = hdu;
+            }
+
+            // process each of the readouts
+            while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for Readout");
+                if (!readout->data_exists) {
+                    continue;
+                }
+
+		// XXX set the options->*Mask values here (after the mask images have been loaded
+		// and before any of the value are used)
+		if (!ppImageSetMaskBits (config, options)) ESCAPE ("Unable to set bit masks");
+		
+		if (!ppImageDetrendReadout(config, options, view)) ESCAPE ("Unable to detrend readout");
+            }
+        }
+
+        // Apply the fringe correction
+        if (options->doFringe) {
+	    if (!ppImageDetrendFringeApply (config, chip, view, options)) 
+		ESCAPE ("Unable to defringe");
+	}
+	
+	// measure various statistics for this image
+	if (!ppImagePixelStats (config, options, view)) 
+	    ESCAPE ("Unable to measures stats for image");
+
+        if (!ppImageMosaicChip(config, options, view, "PPIMAGE.CHIP", "PPIMAGE.OUTPUT"))
+            ESCAPE ("Unable to mosaic chip");
+
+        // we perform photometry on the readouts of this chip in the output
+        if (options->doPhotom) {
+            if (!ppImagePhotom(config, view)) ESCAPE ("error running photometry.");
+        }
+
+	// replace the masked pixels with the background level
+	if (options->replaceMasked) {
+	    if (!ppImageReplaceBackground (config, view, options)) 
+		ESCAPE ("Unable to replace masked pixels with background level");
+	}
+
+	// binning (used for display) must take place after the background is replaced, if desired
+        if (!ppImageRebinChip(config, view, options, "PPIMAGE.BIN1"))
+            ESCAPE ("Unable to bin chip (level 1).");
+
+        if (!ppImageRebinChip(config, view, options, "PPIMAGE.BIN2"))
+            ESCAPE ("Unable to bin chip (level 2).");
+
+        // Close cells (XXX shouldn't pmFPAfileClose iterate down as needed?)
+        view->cell = -1;
+        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
+            if (!cell->process || !cell->file_exists) {
+                continue;
+            }
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) ESCAPE ("save failure for Cell");
+        }
+
+        // Close chip
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) ESCAPE ("save failure for Chip");
+    }
+
+    // generate the full-scale FPA mosaic
+    if (!ppImageMosaicFPA(config, options, "PPIMAGE.OUTPUT.FPA1", "PPIMAGE.BIN1")) ESCAPE ("failure in FPA Mosaic (level 1)");
+    if (!ppImageMosaicFPA(config, options, "PPIMAGE.OUTPUT.FPA2", "PPIMAGE.BIN2")) ESCAPE ("failure in FPA Mosaic (level 2)");
+
+    // we perform astrometry on all chips after sources have been detected
+    // this also performs the psastro file IO
+    if (options->doAstromChip || options->doAstromMosaic) {
+        if (!ppImageAstrom(config)) ESCAPE ("error running astrometry.");
+    }
+
+    if (psTraceGetLevel("ppImage") >= 3) {
+	ppImageFileCheck (config);
+    }
+
+    // Write out summary statistics
+    if (!ppImageMetadataStats (config, options)) ESCAPE ("Unable to write statistics file.");
+
+    // Write out summary statistics
+    if (!ppImageStatsOutput (config, options)) ESCAPE ("Unable to write statistics file.");
+
+    // Output and Close FPA
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) ESCAPE ("save failure for FPA");
+
+    psFree(view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageMemory.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageMemory.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageMemory.c	(revision 22322)
@@ -0,0 +1,38 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "ppImage.h"
+
+//#define TESTING
+
+void ppImageMemoryDump(const char *description)
+{
+#ifndef TESTING
+    return;
+#else
+    psMemBlock **leaks = NULL;
+    int numLeaks = psMemCheckLeaks(0, &leaks, NULL, true);
+
+    static int num = 0;
+    psString filename = NULL;
+    psStringAppend(&filename, "%s_%d.txt", description, num++);
+    FILE *fp = fopen(filename, "w");
+    psFree(filename);
+
+    for (int i = 0; i < numLeaks; i++) {
+        psMemBlock *memBlock = leaks[i];
+        fprintf(fp, "%ld : %s at (%s:%d)  ID: %lu  Ref: %lu\n",
+                (unsigned long)memBlock->userMemorySize, memBlock->func, memBlock->file,
+                (int)memBlock->lineno, (unsigned long)memBlock->id, memBlock->refCounter);
+    }
+    psFree(leaks);
+
+    fclose(fp);
+
+    return;
+#endif
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageMetadataStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageMetadataStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageMetadataStats.c	(revision 22322)
@@ -0,0 +1,60 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+// calculate stats from headers and concepts
+bool ppImageMetadataStats (pmConfig *config, const ppImageOptions *options) {
+
+    bool mdok;              // Status of MD lookup
+    
+    if (!options->doStats) {
+	return true;
+    }
+
+    // Extract statistics from the last output fpa
+    pmFPAfile *outImage  = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
+    pmFPAfile *outPhotom = psMetadataLookupPtr(&mdok, config->files, "PSPHOT.OUTPUT");
+    pmFPAfile *outAstrom = psMetadataLookupPtr(&mdok, config->files, "PSASTRO.OUTPUT");
+
+    if (!outImage && !outPhotom && !outAstrom) {
+	psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find any output file (PPIMAGE.OUTPUT, PSPHOT.OUTPUT, PSASTRO.OUTPUT).");
+	return false;
+    }
+
+    // get the latest output product available
+    pmFPAfile *output = outAstrom;
+    if (!output) {
+	output = outPhotom;
+    }
+    if (!output) {
+	output = outImage;
+    }
+
+    // select or create the fpa-level analysis stats metadata for PPIMAGE.OUTPUT:
+    psMetadata *stats = psMetadataLookupPtr (&mdok, outImage->fpa->analysis, "PPIMAGE.STATS");
+    psMemIncrRefCounter (stats);
+    if (!stats) {
+	stats = psMetadataAlloc ();
+	if (!psMetadataAdd (outImage->fpa->analysis, PS_LIST_TAIL, "PPIMAGE.STATS", PS_DATA_METADATA, "stats container", stats)) {
+	    psError(PS_ERR_UNKNOWN, false, "Unable to push stats on fpa.analysis.");
+	    psFree (stats);
+	    return false;
+	}
+    }
+
+    // extract stats for the complete fpa 
+    pmFPAview *view = pmFPAviewAlloc(0);
+
+    if (!ppStatsMetadata(stats, output->fpa, view, options->maskValue, config)) {
+	psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
+	psFree(view);
+	psFree(stats);
+	return false;
+    }
+
+    psFree(view);
+    psFree(stats);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageMosaic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageMosaic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageMosaic.c	(revision 22322)
@@ -0,0 +1,77 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+bool ppImageMosaicChip(pmConfig *config, const ppImageOptions *options, const pmFPAview *view,
+                       const char *outFile, const char *inFile)
+{
+    bool status;                        // Status of MD lookup
+
+    pmFPAfile *in = psMetadataLookupPtr(&status, config->files, inFile); // Input file
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find required I/O file!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAfile *out = psMetadataLookupPtr(&status, config->files, outFile); // Output file
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find required I/O file!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmChip *outChip = pmFPAviewThisChip(view, out->fpa);
+    pmChip *inChip = pmFPAviewThisChip(view, in->fpa);
+    if (!outChip->hdu && !outChip->parent->hdu) {
+        const char *name = psMetadataLookupStr(&status, in->fpa->concepts, "FPA.OBS"); // Name of FPA
+        pmFPAAddSourceFromView(out->fpa, name, view, out->format);
+    }
+
+    psTrace("pmChipMosaic", 5, "mosaic chip %s to %s (xbin,ybin: %d,%d to %d,%d)\n",
+            in->name, out->name, in->xBin, in->yBin, out->xBin, out->yBin);
+
+    // Mosaic the chip, making a deep copy.  This has the side effect of making the output
+    // image products pure trimmed images, but also increases the memory footprint.
+    status = pmChipMosaic(outChip, inChip, true, options->blankMask);
+    return status;
+}
+
+bool ppImageMosaicFPA (pmConfig *config, const ppImageOptions *options, const char *outFile,
+                       const char *inFile)
+{
+    bool status;                        // Status of MD lookup
+
+    pmFPAfile *in = psMetadataLookupPtr(&status, config->files, inFile); // Input file
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find required I/O file!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAfile *out = psMetadataLookupPtr(&status, config->files, outFile); // Output file
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find required I/O file!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // XXX test printing of all concepts
+    #if 0
+    for (int i = 0; i < in->fpa->chips->n; i++) {
+        pmChip *chip = in->fpa->chips->data[i];
+        for (int j = 0; j < chip->cells->n; j++) {
+            pmCell *cell = chip->cells->data[j];
+            psMetadataPrint(stdout, cell->concepts, 2);
+        }
+    }
+    #endif
+
+    const char *name = psMetadataLookupStr(&status, in->fpa->concepts, "FPA.OBS"); // Name of FPA
+
+    pmFPAview *view = pmFPAviewAlloc(0);
+    pmFPAAddSourceFromView(out->fpa, name, view, out->format);
+    psFree(view);
+
+    psTrace ("pmFPAMosaic", 5, "mosaic fpa %s to %s (xbin,ybin: %d,%d to %d,%d)\n",
+             in->name, out->name, in->xBin, in->yBin, out->xBin, out->yBin);
+    return pmFPAMosaic(out->fpa, in->fpa, false, options->blankMask);
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageOptions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageOptions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageOptions.c	(revision 22322)
@@ -0,0 +1,272 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+static void imageOptionsFree(ppImageOptions *options)
+{
+    psFree(options->overscan);
+    psFree(options->nonLinearData);
+    psFree(options->nonLinearSource);
+}
+
+ppImageOptions *ppImageOptionsAlloc(void)
+{
+    ppImageOptions *options = psAlloc(sizeof(ppImageOptions));
+    psMemSetDeallocator(options, (psFreeFunc)imageOptionsFree);
+
+    // actions which ppImage should perform
+    options->doMaskBuild     = false;   // Build internal mask
+    options->doWeightBuild   = false;   // Build internal weight
+    options->doMask          = false;   // Mask bad pixels
+    options->doNonLin        = false;   // Non-linearity correction
+    options->doOverscan      = false;   // Overscan subtraction
+    options->doBias          = false;   // Bias subtraction
+    options->doDark          = false;   // Dark subtraction
+    options->doShutter       = false;   // Shutter correction
+    options->doFlat          = false;   // Flat-field normalisation
+    options->doFringe        = false;   // Fringe subtraction
+    options->doPhotom        = false;   // Source identification and photometry
+    options->doAstromChip    = false;   // Astrometry (per-chip)
+    options->doAstromMosaic  = false;   // Astrometry (full-mosaic)
+    options->doStats         = false;   // Measure and save image statistics
+
+    // output files requested
+    options->BaseFITS        = false;   // create output image
+    options->BaseMaskFITS    = false;   // create output mask image
+    options->BaseWeightFITS  = false;   // create output weight image
+
+    options->ChipFITS        = false;   // create output chip-mosaic image
+    options->ChipMaskFITS    = false;   // create output chip-mosaic mask image
+    options->ChipWeightFITS  = false;   // create output chip-mosaic weight image
+
+    options->FPA1FITS        = false;   // create fpa-mosaic binned image (scale 1)
+    options->FPA2FITS        = false;   // create fpa-mosaic binned image (scale 2)
+    options->Bin1FITS        = false;   // create binned image (scale 1)
+    options->Bin2FITS        = false;   // create binned image (scale 2)
+    options->Bin1JPEG        = false;   // create jpeg of binned image (scale 1)
+    options->Bin2JPEG        = false;   // create jpeg of binned image (scale 2)
+
+    // default flags for various activities
+    options->maskValue       = 0x00;    // Default mask value (used to skip / ignore pixels)
+    options->satMask         = 0x00;    // Saturated pixels (supplied to pmReadoutGenerateMask)
+    options->badMask         = 0x00;    // Bad (low) pixels (supplied to pmReadoutGenerateMask)
+    options->flatMask        = 0x00;    // Bad flat pixels (supplied to pmFlatField)
+    options->blankMask       = 0x00;    // Blank (no data, cell gap) pixels (supplied to pmChipMosaic, pmFPAMosaic)
+    options->markValue       = 0x00;    // A safe bit for internal marking
+
+    // Non-linearity default options
+    options->nonLinearType   = 0;       // Type of non-linearity data (vector, string or metadata)
+    options->nonLinearData   = NULL;    // The non-linearity data
+    options->nonLinearSource = NULL;    // If the non-linearity data is a menu, this provides the key
+
+    // Overscan defaults
+    options->overscan        = NULL;    // Overscan options
+
+    // binning parameters
+    options->xBin1           = 16;      // x-binning, scale 1
+    options->yBin1           = 16;      // y-binning, scale 1
+    options->xBin2           = 16;      // x-binning, scale 2
+    options->yBin2           = 16;      // y-binning, scale 2
+
+    // Fringe defaults
+    options->fringeRej       = NAN;     // Fringe rejection limit
+    options->fringeIter      = 0;       // Fringe iterations
+    options->fringeKeep      = 1.0;     // Fringe keep fraction
+
+    return options;
+}
+
+ppImageOptions *ppImageOptionsParse(pmConfig *config)
+{
+    bool status;
+    ppImageOptions *options = ppImageOptionsAlloc ();
+
+    // select the recipe for this analysis
+    bool mdStatus = false;              // Result of MD lookup
+    psMetadata *recipe = psMetadataLookupMetadata(&mdStatus, config->recipes, RECIPE_NAME);
+    if (! mdStatus || !recipe) {
+        psLogMsg("ppImage", PS_LOG_ERROR, "Can't find recipe %s in the RECIPES.\n", RECIPE_NAME);
+        exit(EXIT_FAILURE);
+    }
+
+    // Non-linearity recipe options
+    if (psMetadataLookupBool(NULL, recipe, "NONLIN")) {
+        psMetadataItem *dataItem = psMetadataLookup(recipe, "NONLIN.DATA");
+        if (! dataItem) {
+            psLogMsg(__func__, PS_LOG_ERROR, "Non-linearity correction desired, but unable to "
+                     "find NONLIN.DATA in recipe %s.", RECIPE_NAME);
+            exit(EXIT_FAILURE);
+        }
+
+        options->doNonLin = true;
+        options->nonLinearType = dataItem->type;
+        options->nonLinearData = dataItem;
+
+        switch (dataItem->type) {
+            // No immediate action required
+          case PS_DATA_VECTOR:
+          case PS_DATA_STRING:
+            break;
+
+            // This is a menu; we need the key
+          case PS_DATA_METADATA:
+            {
+                bool status;
+                options->nonLinearSource = psMetadataLookupStr(&status, recipe, "NONLIN.SOURCE");
+                if (! status || ! options->nonLinearSource) {
+                    psLogMsg(__func__, PS_LOG_ERROR, "Non-linearity correction desired, but unable to "
+                            "find NONLIN.SOURCE in recipe %s.", RECIPE_NAME);
+                    exit(EXIT_FAILURE);
+                }
+            }
+            break;
+          default:
+            psLogMsg(__func__, PS_LOG_ERROR, "Non-linearity correction desired, but "
+                    "NONLIN.DATA is of invalid type in recipe %s.", RECIPE_NAME);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    // XXX PAP: The overscan stuff needs to be updated following the reworked API
+
+    // Overscan recipe options
+    // XXX EAM : we should abort on invalid options. default options?
+    if (psMetadataLookupBool(NULL, recipe, "OVERSCAN")) {
+        options->doOverscan = true;
+
+        // Do the overscan as a single value?
+        bool overscanSingle = psMetadataLookupBool(NULL, recipe, "OVERSCAN.SINGLE");
+
+        // How do we fit it?
+        pmFit overscanFit = PM_FIT_NONE; // Fit type for overscan
+        int overscanOrder = 0;          // Order for overscan fit
+        psString fit = psMetadataLookupStr(NULL, recipe, "OVERSCAN.FIT");
+        if (! strcasecmp(fit, "POLYNOMIAL")) {
+            overscanFit = PM_FIT_POLY_ORD;
+            overscanOrder = psMetadataLookupS32(NULL, recipe, "OVERSCAN.ORDER");
+        } else if (! strcasecmp(fit, "CHEBYSHEV")) {
+            overscanFit = PM_FIT_POLY_CHEBY;
+            overscanOrder = psMetadataLookupS32(NULL, recipe, "OVERSCAN.ORDER");
+        } else if (! strcasecmp(fit, "SPLINE")) {
+            overscanFit = PM_FIT_SPLINE;
+        } else if (strcasecmp(fit, "NONE")) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "OVERSCAN.FIT (%s) in recipe %s is not one of NONE, POLYNOMIAL, or SPLINE",
+                     fit, RECIPE_NAME);
+            exit(EXIT_FAILURE);
+        }
+
+        // What method do we use to measure the overscan statistics?
+        // XXX allow user to specify psStats types by name
+        psStats *overscanStats = NULL;  // Statistics for overscan
+        psString stat = psMetadataLookupStr(NULL, recipe, "OVERSCAN.STAT");
+        if (! strcasecmp(stat, "MEAN")) {
+            // overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+            overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        } else if (! strcasecmp(stat, "MEDIAN")) {
+            overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+        } else {
+            psErrorStackPrint(stderr, "OVERSCAN.STAT (%s) in recipe %s is not one of MEAN or MEDIAN",
+                              stat, RECIPE_NAME);
+            exit(EXIT_FAILURE);
+        }
+
+        int boxcar = psMetadataLookupS32(NULL, recipe, "OVERSCAN.BOXCAR");
+        float gauss = psMetadataLookupF32(NULL, recipe, "OVERSCAN.GAUSS");
+
+        // Fill in the options
+        options->overscan = pmOverscanOptionsAlloc(overscanSingle, overscanFit, overscanOrder,
+                                                   overscanStats, boxcar, gauss);
+
+        options->overscan->constant = psMetadataLookupBool(NULL, recipe, "OVERSCAN.CONSTANT");
+        options->overscan->value = psMetadataLookupF32(NULL, recipe, "OVERSCAN.VALUE");
+
+        psFree(overscanStats);
+    }
+
+    // for these images, even if not required otherwise
+    options->doMaskBuild = psMetadataLookupBool(NULL, recipe, "MASK.BUILD");
+    options->doWeightBuild = psMetadataLookupBool(NULL, recipe, "WEIGHT.BUILD");
+
+    // Mask recipe options (note that mask bit values are set in ppImageSetMaskBits.c)
+    options->doMask = psMetadataLookupBool(NULL, recipe, "MASK");
+
+    options->doBias = psMetadataLookupBool(NULL, recipe, "BIAS");
+    options->doDark = psMetadataLookupBool(NULL, recipe, "DARK");
+    options->doFlat = psMetadataLookupBool(NULL, recipe, "FLAT");
+    options->doFringe = psMetadataLookupBool(NULL, recipe, "FRINGE");
+    options->doShutter = psMetadataLookupBool(NULL, recipe, "SHUTTER");
+
+    options->doStats = false;
+    char *statsName = psMetadataLookupStr(&status, config->arguments, "STATS"); // Filename for statistics
+    if (statsName) {
+        options->doStats = true;
+    }
+
+    // binned image options
+    options->xBin1 = psMetadataLookupS32(&status, recipe, "BIN1.XBIN");
+    if (!status) {
+        psWarning("BIN1.XBIN not found in recipe: setting to default value.\n");
+    }
+    options->yBin1 = psMetadataLookupS32(&status, recipe, "BIN1.YBIN");
+    if (!status) {
+        psWarning("BIN1.YBIN not found in recipe: setting to default value.\n");
+        options->yBin1 = 16;
+    }
+
+    options->xBin2 = psMetadataLookupS32(&status, recipe, "BIN2.XBIN");
+    if (!status) {
+        psWarning("BIN2.XBIN not found in recipe: setting to default value.\n");
+       options->xBin1 = 16;
+    }
+    options->yBin2 = psMetadataLookupS32(&status, recipe, "BIN2.YBIN");
+    if (!status) {
+        psWarning("BIN2.YBIN not found in recipe: setting to default value.\n");
+        options->yBin1 = 16;
+    }
+
+    options->BaseFITS       = psMetadataLookupBool(NULL, recipe, "BASE.FITS");
+    options->BaseMaskFITS   = psMetadataLookupBool(NULL, recipe, "BASE.MASK.FITS");
+    options->BaseWeightFITS = psMetadataLookupBool(NULL, recipe, "BASE.WEIGHT.FITS");
+
+    options->ChipFITS       = psMetadataLookupBool(NULL, recipe, "CHIP.FITS");
+    options->ChipMaskFITS   = psMetadataLookupBool(NULL, recipe, "CHIP.MASK.FITS");
+    options->ChipWeightFITS = psMetadataLookupBool(NULL, recipe, "CHIP.WEIGHT.FITS");
+
+    options->FPA1FITS       = psMetadataLookupBool(NULL, recipe, "FPA1.FITS");
+    options->FPA2FITS       = psMetadataLookupBool(NULL, recipe, "FPA2.FITS");
+
+    options->Bin1FITS       = psMetadataLookupBool(NULL, recipe, "BIN1.FITS");
+    options->Bin1JPEG       = psMetadataLookupBool(NULL, recipe, "BIN1.JPEG");
+    options->Bin2FITS       = psMetadataLookupBool(NULL, recipe, "BIN2.FITS");
+    options->Bin2JPEG       = psMetadataLookupBool(NULL, recipe, "BIN2.JPEG");
+
+    options->doPhotom       = psMetadataLookupBool(NULL, recipe, "PHOTOM");
+    options->doAstromChip   = psMetadataLookupBool(NULL, recipe, "ASTROM.CHIP");
+    options->doAstromMosaic = psMetadataLookupBool(NULL, recipe, "ASTROM.MOSAIC");
+
+    // even if not requested explicitly, if any of these are set, build an internal mask and weight:
+    if (options->doBias || options->doOverscan || options->doDark || options->doShutter || options->doFlat ||
+        options->doPhotom) {
+        options->doMaskBuild = true;
+        options->doWeightBuild = true;
+    } else if (options->doMask) {
+        options->doMaskBuild = true;
+    }
+
+    if ((options->doAstromChip || options->doAstromMosaic) && !options->doPhotom) {
+        psLogMsg(__func__, PS_LOG_ERROR, "Invalid PPIMAGE options: cannot do ASTROMetry without PHOTOMetry");
+        exit(EXIT_FAILURE);
+    }
+
+    // Fringe options
+    options->fringeRej = psMetadataLookupF32(NULL, recipe, "FRINGE.REJ");
+    options->fringeIter = psMetadataLookupS32(NULL, recipe, "FRINGE.ITER");
+    options->fringeKeep = psMetadataLookupF32(NULL, recipe, "FRINGE.KEEP");
+
+    options->replaceMasked  = psMetadataLookupBool(NULL, recipe, "REPLACE.MASKED");
+
+    return options;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageParseCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageParseCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageParseCamera.c	(revision 22322)
@@ -0,0 +1,478 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppImage.h"
+
+ppImageOptions *ppImageParseCamera(pmConfig *config)
+{
+    bool status = false;
+
+    // the input image defines the camera, and all recipes and options the follow
+    pmFPAfile *input = pmFPAfileDefineFromArgs (&status, config, "PPIMAGE.INPUT", "INPUT");
+    if (!status || !input) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from PPIMAGE.INPUT");
+        return NULL;
+    }
+    if (input->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPIMAGE.INPUT is not of type IMAGE");
+        return NULL;
+    }
+
+    // if MASK or WEIGHT was supplied on command line, bind files to 'input'.
+    // the mask and weight will be mosaicked with the image
+    pmFPAfile *inputMask = pmFPAfileBindFromArgs(&status, input, config, "PPIMAGE.INPUT.MASK", "PPIMAGE.INPUT.MASK");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+        return NULL;
+    }
+    if (inputMask) {
+      if (inputMask->type != PM_FPA_FILE_MASK) {
+        psError(PS_ERR_IO, true, "PPIMAGE.INPUT.MASK is not of type MASK");
+        return NULL;
+      }
+    }
+
+    pmFPAfile *inputWeight = pmFPAfileBindFromArgs(&status, input, config, "PPIMAGE.INPUT.WEIGHT", "PPIMAGE.INPUT.WEIGHT");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+        return NULL;
+    }
+    if (inputWeight && inputWeight->type != PM_FPA_FILE_WEIGHT) {
+        psError(PS_ERR_IO, true, "PPIMAGE.INPUT.WEIGHT is not of type WEIGHT");
+        return NULL;
+    }
+
+    // add recipe options supplied on command line
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, RECIPE_NAME);
+
+    // parse the options from the metadata format to the ppImageOptions structure
+    ppImageOptions *options = ppImageOptionsParse (config);
+
+    // the following are defined from the argument list, if given,
+    // otherwise they revert to the config information
+    // not all input or output images are used in a given recipe
+    if (options->doBias) {
+        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.BIAS", "BIAS", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_BIAS)) {
+            psError (PS_ERR_IO, false, "Can't find a bias image source");
+            psFree (options);
+            return NULL;
+        }
+    }
+    if (options->doDark) {
+        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.DARK", "DARK", PM_FPA_FILE_DARK, PM_DETREND_TYPE_DARK)) {
+            psError (PS_ERR_IO, false, "Can't find a dark image source");
+            psFree (options);
+            return NULL;
+        }
+    }
+    if (options->doMask) {
+
+        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.MASK", "MASK", PM_FPA_FILE_MASK, PM_DETREND_TYPE_MASK)) {
+            psError (PS_ERR_IO, false, "Can't find a mask image source");
+            psFree (options);
+            return NULL;
+        }
+	// XXX have ppImageDefineFile return the pmFPAfile?
+	pmFPAfile *mask = psMetadataLookupPtr(&status, config->files, "PPIMAGE.MASK");
+	psAssert (mask, "mask not defined?  not possible!");
+
+	// Need to read the names of bit masks from the mask header and set them in the
+	// recipe.  If we are loading this from the detrend db, this action will happen
+	// when the file is resolved.
+	if (!mask->detrend) {
+	    // XXX need to load the mask bit names from one of the headers
+	    // this grabs the first available hdu : no guarantee that it will be valid, though
+	    pmHDU *hdu = pmHDUGetFirst (mask->fpa);
+	    if (!hdu) {
+		psError(PS_ERR_IO, true, "no valid HDU for PPIMAGE.INPUT.MASK");
+		return NULL;
+	    }
+	    // XXX is this consistent with the pmConfigMaskReadHeader call above?
+	    if (!pmConfigMaskReadHeader (config, hdu->header)) {
+		psError(PS_ERR_IO, false, "error in mask bits");
+		return NULL;
+	    }
+	}
+    }
+    if (options->doShutter) {
+        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.SHUTTER", "SHUTTER", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_SHUTTER)) {
+            psError (PS_ERR_IO, false, "Can't find a shutter image source");
+            psFree (options);
+            return NULL;
+        }
+    }
+
+    if (options->doFlat) {
+        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.FLAT", "FLAT", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_FLAT)) {
+            psError (PS_ERR_IO, false, "Can't find a flat image source");
+            psFree (options);
+            return NULL;
+        }
+    }
+
+    int nThreads = psMetadataLookupS32 (&status, config->arguments, "NTHREADS");
+    if (nThreads > 0) {
+	int nScanRows = psMetadataLookupS32 (&status, recipe, "SCAN.ROWS");
+	pmDetrendSetThreadTasks (nScanRows);
+    }
+
+    // fringe frame are only applied for a subset of the filters.
+    // if the filter is one of those identified by a FRINGE.FILTERS metadata entry
+    // in ppImage.config, apply the fringe frame
+    if (options->doFringe) {
+        // determine filter from the concepts
+        const char *filter = psMetadataLookupStr(&status, input->fpa->concepts, "FPA.FILTER");
+        if (!status || !filter || strlen(filter) == 0) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find FPA.FILTER.\n");
+            psFree(options);
+            return NULL;
+        }
+
+        // select FRINGE.FILTERS from the recipe and test if the filter matches
+        // is the mdi saved with psList or string?
+        psMetadataItem *mdi = psMetadataLookup (recipe, "FRINGE.FILTERS");
+        if (mdi == NULL) {
+            // no valid filters for fringe data for this camera
+            options->doFringe = false;
+            goto skip_fringe;
+        }
+        // place entry on a list regardless of type
+        psList *filters = NULL;
+        if (mdi->type == PS_DATA_STRING) {
+            filters = psListAlloc(NULL);
+            psListAdd (filters, PS_LIST_HEAD, mdi);
+        } else if (mdi->type == PS_DATA_METADATA_MULTI) {
+            filters = psMemIncrRefCounter(mdi->data.list);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    "FRINGE.FILTERS in recipe %s is not of type METADATA", RECIPE_NAME);
+            psFree(options);
+            return NULL;
+        }
+
+        // search through list to find the current filter
+        psListIterator *iter = psListIteratorAlloc (filters, PS_LIST_HEAD, FALSE);
+        options->doFringe = false;
+        for (int i = 0; !options->doFringe && (i < filters->n); i++) {
+            psMetadataItem *item = psListGetAndIncrement (iter);
+            char *validFilter = item->data.V;
+            if (strcmp (validFilter, filter)) continue;
+            options->doFringe = true;
+        }
+        psFree(iter);
+        psFree(filters);
+    }
+skip_fringe:
+    if (options->doFringe) {
+        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.FRINGE", "FRINGE", PM_FPA_FILE_FRINGE, PM_DETREND_TYPE_FRINGE)) {
+            psError (PS_ERR_IO, false, "Can't find a fringe image source");
+            return NULL;
+        }
+    }
+
+    // the following files are output targets
+    pmFPAfile *outImage = pmFPAfileDefineOutput(config, input->fpa, "PPIMAGE.OUTPUT");
+    if (!outImage) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPIMAGE.OUTPUT"));
+        psFree(options);
+        return NULL;
+    }
+    if (outImage->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPIMAGE.OUTPUT is not of type IMAGE");
+        psFree(options);
+        return NULL;
+    }
+    pmFPAfile *outMask = pmFPAfileDefineOutput(config, input->fpa, "PPIMAGE.OUTPUT.MASK");
+    if (!outMask) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPIMAGE.OUTPUT.MASK"));
+        psFree(options);
+        return NULL;
+    }
+    if (outMask->type != PM_FPA_FILE_MASK) {
+        psError(PS_ERR_IO, true, "PPIMAGE.OUTPUT.MASK is not of type MASK");
+        psFree(options);
+        return NULL;
+    }
+    pmFPAfile *outWeight = pmFPAfileDefineOutput(config, input->fpa, "PPIMAGE.OUTPUT.WEIGHT");
+    if (!outWeight) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPIMAGE.OUTPUT.WEIGHT"));
+        psFree(options);
+        return NULL;
+    }
+    if (outWeight->type != PM_FPA_FILE_WEIGHT) {
+        psError(PS_ERR_IO, true, "PPIMAGE.OUTPUT.WEIGHT is not of type WEIGHT");
+        psFree(options);
+        return NULL;
+    }
+
+    // XXX should these be bound explicitly to the outImage->fpa rather than the input->fpa?
+    pmFPAfile *chipImage = pmFPAfileDefineChipMosaic(config, input->fpa, "PPIMAGE.CHIP");
+    if (!chipImage) {
+        psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.CHIP"));
+        psFree(options);
+        return NULL;
+    }
+    if (chipImage->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPIMAGE.CHIP is not of type IMAGE");
+        psFree(options);
+        return NULL;
+    }
+    pmFPAfile *chipMask = pmFPAfileDefineOutput(config, chipImage->fpa, "PPIMAGE.CHIP.MASK");
+    if (!chipMask) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPIMAGE.CHIP.MASK"));
+        psFree(options);
+        return NULL;
+    }
+    if (chipMask->type != PM_FPA_FILE_MASK) {
+        psError(PS_ERR_IO, true, "PPIMAGE.CHIP.MASK is not of type MASK");
+        psFree(options);
+        return NULL;
+    }
+    pmFPAfile *chipWeight = pmFPAfileDefineOutput(config, chipImage->fpa, "PPIMAGE.CHIP.WEIGHT");
+    if (!chipWeight) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPIMAGE.CHIP.WEIGHT"));
+        psFree(options);
+        return NULL;
+    }
+    if (chipWeight->type != PM_FPA_FILE_WEIGHT) {
+        psError(PS_ERR_IO, true, "PPIMAGE.CHIP.WEIGHT is not of type WEIGHT");
+        psFree(options);
+        return NULL;
+    }
+
+    pmFPAfile *byFPA1 = pmFPAfileDefineFPAMosaic(config, input->fpa, "PPIMAGE.OUTPUT.FPA1");
+    if (!byFPA1) {
+        psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.OUTPUT.FPA1"));
+        psFree(options);
+        return NULL;
+    }
+    if (byFPA1->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPIMAGE.OUTPUT.FPA1 is not of type IMAGE");
+        psFree(options);
+        return NULL;
+    }
+    pmFPAfile *byFPA2 = pmFPAfileDefineFPAMosaic(config, input->fpa, "PPIMAGE.OUTPUT.FPA2");
+    if (!byFPA2) {
+        psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.OUTPUT.FPA2"));
+        psFree(options);
+        return NULL;
+    }
+    if (byFPA2->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPIMAGE.OUTPUT.FPA2 is not of type IMAGE");
+        psFree(options);
+        return NULL;
+    }
+
+    // chipImage    -> psphotInput  (pmFPAfileDefineFromFile)       : fpa is constructed
+    // psphotInput  -> psphotOutput (pmFPAfileDefineOutputFromFile) : fpa is equated
+    // psphotOutput -> psastroInput (pmFPAfileDefineInput)          : fpa is ref-copied
+    // psastroInput -> psastroModel (pmFPAfileDefineFromArgs        : fpa is ref-copied
+    // psastroInput -> psastroModel (pmFPAfileDefineFromConf        : fpa is constructed
+    // psastroInput -> psastroModel (pmFPAfileDefineFromDetDB       : fpa is constructed (pmDetrendSelect uses input concepts )
+
+    // For photometry, we operate on the chip-mosaicked image
+    // we create a copy of the mosaicked image for psphot so we can write out a clean image
+    if (options->doPhotom) {
+
+        // this file is just used as a carrier; output files (eg, PSPHOT.RESID) are defined by
+        // psphotDefineFiles
+        pmFPAfile *psphotInput = pmFPAfileDefineFromFile (config, chipImage, 1, 1, "PSPHOT.INPUT");
+        PS_ASSERT (psphotInput, false);
+
+        // define associated psphot input/output files
+        if (!psphotDefineFiles (config, psphotInput)) {
+            psError(PSPHOT_ERR_CONFIG, false, "Trouble defining the additional input/output files for psphot");
+            return false;
+        }
+    }
+
+    // For photometry, we operate on the chip-mosaicked image
+    if (options->doAstromChip || options->doAstromMosaic) {
+        if (!options->doPhotom) {
+            psError (PSASTRO_ERR_CONFIG, false, "photometry mode is not selected; it is required for astrometry");
+            return false;
+        }
+
+        pmFPAfile *psphotOutput = psMetadataLookupPtr (&status, config->files, "PSPHOT.OUTPUT");
+        PS_ASSERT (psphotOutput, false);
+
+        pmFPAfile *psastroInput = pmFPAfileDefineInput (config, psphotOutput->fpa, "PSASTRO.INPUT");
+        PS_ASSERT (psastroInput, false);
+        psastroInput->mode = PM_FPA_MODE_REFERENCE;
+
+        // define associated psphot input/output files
+        if (!psastroDefineFiles (config, psastroInput)) {
+            psError(PSPHOT_ERR_CONFIG, false, "Trouble defining the additional input/output files for psastro");
+            return false;
+        }
+
+        // deactivate the psastro files, reactive when needed
+        pmFPAfileActivate (config->files, false, "PSASTRO.OUTPUT");
+    }
+
+    // save any of these files?
+    outImage->save   = options->BaseFITS;
+    outMask->save    = options->BaseMaskFITS;
+    outWeight->save  = options->BaseWeightFITS;
+
+    chipImage->save  = options->ChipFITS;
+    chipMask->save   = options->ChipMaskFITS;
+    chipWeight->save = options->ChipWeightFITS;
+
+    byFPA1->save     = options->FPA1FITS;
+    byFPA2->save     = options->FPA2FITS;
+
+    // outImage is used as a carrier: input to chipImage -> require the data to remain at the CHIP level
+    outImage->freeLevel = PS_MIN (outImage->freeLevel, PM_FPA_LEVEL_CHIP);
+    outImage->dataLevel = outImage->freeLevel;
+    outImage->fileLevel = PS_MIN (outImage->fileLevel, outImage->dataLevel);
+
+    // outMask and outWeight must be freed at the same level as outImage (all freed by pmFPAFreeData)
+    outMask->freeLevel   = outImage->freeLevel;
+    outWeight->freeLevel = outImage->freeLevel;
+    outMask->dataLevel   = outImage->dataLevel;
+    outWeight->dataLevel = outImage->dataLevel;
+
+    // Ditto for the chip-mosaicked version
+    chipMask->freeLevel   = chipImage->freeLevel;
+    chipWeight->freeLevel = chipImage->freeLevel;
+    chipMask->dataLevel   = chipImage->dataLevel;
+    chipWeight->dataLevel = chipImage->dataLevel;
+
+    // the input data is the same as the outImage data : force the free levels to match
+    input->freeLevel = PS_MIN (outImage->freeLevel, input->freeLevel);
+
+    // define the binned target files (which may just be carriers for some camera configurations)
+    pmFPAfile *bin1 = pmFPAfileDefineFromFPA (config, chipImage->fpa, options->xBin1, options->yBin1, "PPIMAGE.BIN1");
+    if (!bin1) {
+        psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.BIN1"));
+        psFree(options);
+        return NULL;
+    }
+    if (bin1->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPIMAGE.OUTPUT.BIN1 is not of type IMAGE");
+        psFree(options);
+        return NULL;
+    }
+
+    pmFPAfile *bin2 = pmFPAfileDefineFromFPA (config, chipImage->fpa, options->xBin2, options->yBin2, "PPIMAGE.BIN2");
+    if (!bin2) {
+        psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.BIN2"));
+        psFree(options);
+        return NULL;
+    }
+    if (bin2->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPIMAGE.OUTPUT.BIN2 is not of type IMAGE");
+        psFree(options);
+        return NULL;
+    }
+
+    // bin1 and bin2 are used as carriers: input for byFPA1, byFPA2
+    bin1->freeLevel = PM_FPA_LEVEL_FPA;
+    bin2->freeLevel = PM_FPA_LEVEL_FPA;
+
+    pmFPAfile *jpg1 = pmFPAfileDefineOutput (config, byFPA1->fpa, "PPIMAGE.JPEG1");
+    if (!jpg1) {
+        psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.JPEG1"));
+        psFree(options);
+        return NULL;
+    }
+    if (jpg1->type != PM_FPA_FILE_JPEG) {
+        psError(PS_ERR_IO, true, "PPIMAGE.OUTPUT.JPEG1 is not of type JPEG");
+        psFree(options);
+        return NULL;
+    }
+    pmFPAfile *jpg2 = pmFPAfileDefineOutput (config, byFPA2->fpa, "PPIMAGE.JPEG2");
+    if (!jpg2) {
+        psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.JPEG2"));
+        psFree(options);
+        return NULL;
+    }
+    if (jpg2->type != PM_FPA_FILE_JPEG) {
+        psError(PS_ERR_IO, true, "PPIMAGE.OUTPUT.JPEG2 is not of type JPEG");
+        psFree(options);
+        return NULL;
+    }
+
+    // XXX we could potentially not define these pmFPAfiles if no output is requested...
+    bin1->save = options->Bin1FITS;
+    bin2->save = options->Bin2FITS;
+    jpg1->save = options->Bin1JPEG;
+    jpg2->save = options->Bin2JPEG;
+
+    // Chip selection: turn on only the chips specified (pass status to suppress missing-key log msg)
+    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS");
+    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    if (chips->n > 0) {
+        pmFPASelectChip (input->fpa, -1, true); // deselect all chips
+        for (int i = 0; i < chips->n; i++) {
+            int chipNum = atoi(chips->data[i]);
+            if (! pmFPASelectChip(input->fpa, chipNum, false)) {
+                psError(PS_ERR_IO, false, "Chip number %d doesn't exist in camera.\n", chipNum);
+                psFree(options);
+                return false;
+            }
+        }
+    }
+    psFree (chips);
+
+    if (psMetadataLookupBool(NULL, config->arguments, "INPUT_IS_FRINGE")) {
+        // It's a fringe file, so change the file type
+        input->type = PM_FPA_FILE_FRINGE;
+        outImage->type = PM_FPA_FILE_FRINGE;
+    }
+    if (psMetadataLookupBool(NULL, config->arguments, "INPUT_IS_DARK")) {
+        // It's a dark file, so change the file type
+        input->type = PM_FPA_FILE_DARK;
+        outImage->type = PM_FPA_FILE_DARK;
+        // Turn off compression --- there are just too many nasties that can happen
+        psFree(outImage->compression);
+        outImage->compression = NULL;
+        psFree(outImage->options);
+        outImage->options = NULL;
+    }
+
+    // Turn off mask and weight output if we're not doing anything interesting
+    if (!options->doMaskBuild && outMask->save) {
+        psWarning("output mask image (BASE.MASK.FITS) requested, but not generated: skipping.\n");
+        outMask->save = false;
+    }
+    if (!options->doWeightBuild && outWeight->save) {
+        psWarning("output weight image (BASE.WEIGHT.FITS) requested, but not generated: skipping.\n");
+        outWeight->save = false;
+    }
+    if (!options->doMaskBuild && chipMask->save) {
+        psWarning("output mask image (CHIP.MASK.FITS) requested, but not generated: skipping.\n");
+        chipMask->save = false;
+    }
+    if (!options->doWeightBuild && chipWeight->save) {
+        psWarning("output weight image (CHIP.WEIGHT.FITS) requested, but not generated: skipping.\n");
+        chipWeight->save = false;
+    }
+
+    if (psTraceGetLevel("ppImage.config") > 0) {
+        // Get a look inside all the files.
+        psMetadataIterator *filesIter = psMetadataIteratorAlloc(config->files, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *item;               // Item from iteration
+        fprintf(stderr, "Files:\n");
+        while ((item = psMetadataGetAndIncrement(filesIter))) {
+            pmFPAfile *file = item->data.V; // File of interest
+            fprintf(stderr, "%s: %p %p %p (%p) %p\n", file->name,
+                    file->src, file->fpa,
+                    file->camera, file->fpa->camera, file->format);
+        }
+        psFree(filesIter);
+    }
+
+    // Change the input dark type between the old (IMAGE) and new (multi-DARK).
+    // Hopefully this is a temporary change until we all move over to using the new dark types
+    bool mdok;                          // Status of MD lookup
+    if (options->doDark && psMetadataLookupBool(&mdok, recipe, "OLDDARK")) {
+        pmFPAfile *dark = psMetadataLookupPtr(NULL, config->files, "PPIMAGE.DARK");
+        assert(dark);
+        dark->type = PM_FPA_FILE_IMAGE;
+    }
+
+    return (options);
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImagePhotom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImagePhotom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImagePhotom.c	(revision 22322)
@@ -0,0 +1,61 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppImage.h"
+
+// In this function, we perform the psphot analysis routine for the chip-mosaicked images
+bool ppImagePhotom (pmConfig *config, pmFPAview *view) {
+
+    bool status;
+    pmCell *cell;
+    pmReadout *readout;
+
+    psphotModelClassInit ();
+
+    // find or define a pmFPAfile PSPHOT.INPUT
+    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PSPHOT.INPUT");
+    if (!status) {
+        psError(PSPHOT_ERR_CONFIG, false, "PSPHOT.INPUT I/O file is not defined");
+        return false;
+    }
+
+    // we make a new copy of the output chip to keep psphot from modifying the output image
+    pmChip *oldChip = pmFPAviewThisChip (view, input->src);
+    pmChip *newChip = pmFPAviewThisChip (view, input->fpa);
+    pmChipCopy (newChip, oldChip);
+
+    // iterate over the cells and readout for this chip
+    while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
+        psLogMsg ("ppImagePhotom", 5, "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, input->fpa, 1)) != NULL) {
+            if (! readout->data_exists) { continue; }
+
+            // run the actual photometry analysis
+            if (!psphotReadout (config, view)) {
+                psError(psErrorCodeLast(), false, "failure in psphotReadout for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
+                return false;
+            }
+
+	    // we want to save the MASK as modified by psphot, but not the data or weight
+	    // free the old mask and replace with a memory copy of the new mask
+	    pmReadout *oldReadout = pmFPAviewThisReadout (view, input->src);
+	    pmReadout *newReadout = pmFPAviewThisReadout (view, input->fpa);
+	    psFree (oldReadout->mask);
+	    oldReadout->mask = psMemIncrRefCounter (newReadout->mask);
+        }
+    }
+
+    ppImageMemoryDump("photom");
+
+    // the PSPHOT.INPUT file is a temporary file used to carry PPIMAGE.CHIP to psphotReadout
+    // XXX not sure that this is needed...
+//    pmFPAfileActivate (config->files, false, "PSPHOT.INPUT");
+
+    return true;
+}
+
+// XXX do we need to deactivate all files and activate the psphot ones explicitly?
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImagePixelStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImagePixelStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImagePixelStats.c	(revision 22322)
@@ -0,0 +1,104 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+// calculate stats, including MD5
+bool ppImagePixelStats (pmConfig *config, const ppImageOptions *options, const pmFPAview *inputView) {
+
+    bool mdok;              // Status of MD lookup
+
+    // loop over the cells/readouts for this chip
+    pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
+    *view = *inputView;
+    
+    // perform the analysis of for this FPA
+    pmFPAfile *output = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
+    if (!output) {
+	psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find output file (PPIMAGE.OUTPUT).");
+	psFree (view);
+	return false;
+    }
+
+    // select the corresponding chip
+    pmChip *chip = pmFPAviewThisChip (view, output->fpa);
+    if (!chip) {
+	psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find requested chip.");
+	psFree (view);
+	return false;
+    }
+    
+    // Perform statistics for this chip
+    if (options->doStats) {
+
+	// select or create the fpa-level analysis stats metadata (save on PPIMAGE.OUPUT:PPIMAGE.STATS)
+	psMetadata *stats = psMetadataLookupPtr (&mdok, output->fpa->analysis, "PPIMAGE.STATS");
+	psMemIncrRefCounter (stats);
+	if (!stats) {
+	    stats = psMetadataAlloc ();
+	    if (!psMetadataAdd (output->fpa->analysis, PS_LIST_TAIL, "PPIMAGE.STATS", PS_DATA_METADATA, "stats container", stats)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to push stats on fpa.analysis.");
+		psFree (view);
+		psFree (stats);
+		return false;
+	    }
+	}
+
+	if (!ppStatsPixels(stats, output->fpa, view, options->maskValue, config)) {
+	    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
+	    psFree (view);
+	    psFree (stats);
+	    return false;
+	}
+
+	// extract the fringe amplitude from the analysis
+	if (options->doFringe && !ppStatsFringe(stats, chip, "FRINGE", "FRINGE.SOLUTION")) {
+	    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to extract fringe solution for image.");
+	    psFree (view);
+	    psFree (stats);
+	    return false;
+	}
+
+	// extract the fringe residual amplitude from the analysis
+	if (options->doFringe && !ppStatsFringe(stats, chip, "FRINGE_RESID", "FRINGE.RESIDUAL.SOLUTION")) {
+	    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to extract fringe solution for image.");
+	    psFree (view);
+	    psFree (stats);
+	    return false;
+	}
+	
+	psFree (stats);
+    }
+
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+
+    view->cell = view->readout = -1;
+    while ((cell = pmFPAviewNextCell(view, output->fpa, 1)) != NULL) {
+	if (!cell->process || !cell->file_exists) {
+	    continue;
+	}
+
+	// Add MD5 information for cell
+	pmHDU *hdu = pmHDUFromCell(cell); // HDU that owns the cell
+	while ((readout = pmFPAviewNextReadout(view, output->fpa, 1)) != NULL) {
+	    const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
+	    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME");
+
+	    psString headerName = NULL; // Header name for MD5
+	    psStringAppend(&headerName, "MD5_%s_%s_%d", chipName, cellName, view->readout);
+
+	    psVector *md5 = psImageMD5(readout->image); // md5 hash
+	    psString md5string = psMD5toString(md5); // String
+	    psFree (md5);
+	    psMetadataAddStr(hdu->header, PS_LIST_TAIL, headerName, PS_META_REPLACE,
+			     "Image MD5", md5string);
+	    psFree (md5string);
+	    psFree (headerName);
+	}
+    }
+
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageRebinReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageRebinReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageRebinReadout.c	(revision 22322)
@@ -0,0 +1,114 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+bool ppImageRebinChip (pmConfig *config, pmFPAview *view, ppImageOptions *options, char *outName) {
+
+    pmCell *cell;
+    pmReadout *inReadout, *outReadout;
+
+    pmFPAfile *outFile = psMetadataLookupPtr (NULL, config->files, outName);
+    if (outFile == NULL) return false;
+
+    // XXX double check that chip != -1?
+
+    pmChip *inChip  = pmFPAviewThisChip (view, outFile->src);
+    pmChip *outChip = pmFPAviewThisChip (view, outFile->fpa);
+    if (!pmChipCopyStructure (outChip, inChip, outFile->xBin, outFile->yBin)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to copy chip structure.");
+        return false;
+    }
+
+    while ((cell = pmFPAviewNextCell (view, outFile->src, 1)) != NULL) {
+        psLogMsg ("ppImageRebinChip", 5, "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 ((inReadout = pmFPAviewNextReadout (view, outFile->src, 1)) != NULL) {
+            if (! inReadout->data_exists) { continue; }
+
+            outReadout = pmFPAviewThisReadout (view, outFile->fpa);
+
+            // run the rebin code
+            ppImageRebinReadout (outReadout, inReadout, outFile, options);
+        }
+
+        // Get the original values for the binning
+        bool mdok = true;               // Status of MD lookup
+        int xBin = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XBIN"); // The binning in x
+        if (!mdok || xBin <= 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "CELL.XBIN is not set --- assuming 1.\n");
+            xBin = 1;
+        }
+        int yBin = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YBIN"); // The binning in y
+        if (!mdok || yBin <= 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "CELL.YBIN is not set --- assuming 1.\n");
+            yBin = 1;
+        }
+
+        // Update the concepts with the new values for the binning
+        pmCell *outCell = pmFPAviewThisCell(view, outFile->fpa); // The output cell
+        psMetadataItem *binItem = psMetadataLookup(outCell->concepts, "CELL.XBIN");
+        binItem->data.S32 = xBin * outFile->xBin;
+        binItem = psMetadataLookup(outCell->concepts, "CELL.YBIN");
+        binItem->data.S32 = yBin * outFile->yBin;
+    }
+
+    return true;
+}
+
+// XXX this should be made consistent with psImageBinning
+bool ppImageRebinReadout (pmReadout *output, pmReadout *input, pmFPAfile *outFile, ppImageOptions *options)
+{
+    PS_ASSERT_PTR_NON_NULL(output, false);
+    PS_ASSERT_IMAGE_NON_NULL(output->image, false);
+    PS_ASSERT_PTR_NON_NULL(input, false);
+    PS_ASSERT_IMAGE_NON_NULL(input->image, false);
+    PS_ASSERT_PTR_NON_NULL(outFile, false);
+
+    // the binning process must not change the size of the output image...
+    // psStats *stats = psStatsAlloc (PS_STATS_SAMPLE_MEAN);
+    // psImageRebin (output->image, input->image, NULL, 0, scale, stats);
+    // psFree (stats);
+
+    int dX = outFile->xBin;
+    int dY = outFile->yBin;
+
+    int nX = input->image->numCols;
+    int nY = input->image->numRows;
+
+    // we should *either* skip masked pixels or replace masked pixels with the specified value 
+    
+
+    // do the rebinning by hand, mean only for test
+    for (int yOut = 0; yOut < output->image->numRows; yOut++) {
+        for (int xOut = 0; xOut < output->image->numCols; xOut++) {
+	    float maskedValue = 0.0;
+            float value = 0;
+            int nPix = 0;
+            for (int yIn = yOut * dY; (yIn < yOut * dY + dY) && (yIn < nY); yIn ++) {
+                for (int xIn = xOut * dX; (xIn < xOut * dX + dX) && (xIn < nX); xIn ++) {
+		    if (input->mask && input->mask->data.U8[yIn][xIn]) {
+		      maskedValue = input->image->data.F32[yIn][xIn];
+		      continue;
+		    }
+                    value += input->image->data.F32[yIn][xIn];
+                    nPix ++;
+                }
+            }
+	    if (nPix > 0) {
+	      output->image->data.F32[yOut][xOut] = value / nPix;
+	    } else {
+	      if (options->replaceMasked) {
+		output->image->data.F32[yOut][xOut] = maskedValue;
+	      } else {
+		output->image->data.F32[yOut][xOut] = 0.0;
+	      }
+	    }
+        }
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageReplaceBackground.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageReplaceBackground.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageReplaceBackground.c	(revision 22322)
@@ -0,0 +1,111 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppImage.h"
+
+enum {MODE_NONE, MODE_MODEL, MODE_VALUE};
+
+// In this function, we perform the psphot analysis routine for the chip-mosaicked images
+bool ppImageReplaceBackground (pmConfig *config, pmFPAview *view, ppImageOptions *options) {
+
+    bool status;
+    pmCell *cell;
+    pmReadout *readout;
+
+    // find the reference source image
+    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PPIMAGE.CHIP");
+    if (!status) {
+        psError(PSPHOT_ERR_CONFIG, false, "PSPHOT.INPUT I/O file is not defined");
+        return false;
+    }
+
+    // select the appropriate recipe information
+    psMetadata *ppImageRecipe  = psMetadataLookupPtr (&status, config->recipes, "PPIMAGE");
+
+    // select the appropriate recipe information (needed by psphotModelBackground)
+    psMetadata *psphotRecipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+
+    char *replaceSkyModeWord = psMetadataLookupStr (&status, ppImageRecipe, "REPLACE.MODE");
+    float replaceSkyValue = 0.0;
+
+    int replaceSkyMode = MODE_NONE;
+    if (!strcasecmp (replaceSkyModeWord, "MODEL")) {
+	replaceSkyMode = MODE_MODEL;
+    }
+    if (!strcasecmp (replaceSkyModeWord, "VALUE")) {
+	replaceSkyMode = MODE_VALUE;
+	replaceSkyValue = psMetadataLookupF32 (&status, ppImageRecipe, "REPLACE.VALUE");
+    }
+    psAssert (replaceSkyMode != MODE_NONE, "replace sky mode not defined");
+
+    // XXX Should this be options->maskValue or & ~options->satMask? the latter will leave saturated pixels high
+    // psMaskType maskVal  = options->maskValue & ~options->satMask;
+    psMaskType maskVal  = options->maskValue;
+
+    pmFPAfile *modelFile = NULL;
+    if (replaceSkyMode == MODE_MODEL) {
+	// find the model data, if it exists yet (if not, we build it below)
+	modelFile = psMetadataLookupPtr(&status, config->files, "PSPHOT.BACKMDL");
+
+	// user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+	psMetadataAddU8 (psphotRecipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "user-defined mask", maskVal);
+    }
+
+    // iterate over the cells and readout for this chip
+    while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
+	psLogMsg ("ppImagePhotom", 5, "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, input->fpa, 1)) != NULL) {
+	    if (! readout->data_exists) { continue; }
+	    if (! readout->mask) { continue; }
+
+	    // replace masked pixels with values from model (unbinning not needed)
+	    pmReadout *modelReadout = NULL;
+	    psImageBinning *binning = NULL;
+	    psImage *model = NULL;
+	    if (replaceSkyMode == MODE_MODEL) {
+		// we are using this pmFPAfile as an I/O file: select readout or create
+		if (modelFile) {
+		    modelReadout = pmFPAviewThisReadout (view, modelFile->fpa);
+		}
+		if (!modelReadout) {
+		    psphotModelBackground (config, view, "PPIMAGE.CHIP");
+		    modelFile = psMetadataLookupPtr(&status, config->files, "PSPHOT.BACKMDL");
+		    assert (modelFile);
+		    if (modelFile->mode == PM_FPA_MODE_INTERNAL) {
+			modelReadout = modelFile->readout;
+		    } else {
+			modelReadout = pmFPAviewThisReadout (view, modelFile->fpa);
+		    }
+		    assert (modelReadout);
+		}
+		binning = psMetadataLookupPtr(&status, psphotRecipe, "PSPHOT.BACKGROUND.BINNING");
+		model = modelReadout->image;
+	    }
+
+	    psImage *mask = readout->mask;
+	    psImage *image = readout->image;
+
+	    for (int iy = 0; iy < image->numRows; iy++) {
+		for (int ix = 0; ix < image->numCols; ix++) {
+		    if (!(mask->data.U8[iy][ix] && maskVal)) continue;
+		    if (replaceSkyMode == MODE_MODEL) {
+			image->data.F32[iy][ix] = psImageUnbinPixel_V2(ix, iy, model, binning);
+		    } else {
+			image->data.F32[iy][ix] = replaceSkyValue;
+		    }
+		}
+	    }
+	}
+    }
+
+    if (replaceSkyMode == MODE_MODEL) {
+	pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL");
+	pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL.STDEV");
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageSetMaskBits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageSetMaskBits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageSetMaskBits.c	(revision 22322)
@@ -0,0 +1,44 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+bool ppImageSetMaskBits (pmConfig *config, ppImageOptions *options) {
+
+    if (!pmConfigMaskSetBits (&options->maskValue, &options->markValue, config)) {
+	psError (PS_ERR_UNKNOWN, true, "Unable to define the mask bit values");
+	return false;
+    }
+
+    // at this point we know we have valid values for required entries SAT, BAD, FLAT, BLANK:
+
+    // mask for non-linear flat corrections
+    options->flatMask = pmConfigMaskGet("FLAT", config); 
+    psAssert (options->flatMask, "flat mask not set");
+
+    // mask for non-existent data  (default to DETECTOR if not defined)
+    options->blankMask = pmConfigMaskGet("BLANK", config); 
+    psAssert (options->blankMask, "blank mask not set");
+
+    // mask for saturated data  (default to RANGE if not defined)
+    options->satMask = pmConfigMaskGet("SAT", config);
+    psAssert (options->satMask, "sat mask not set");
+    
+    // mask for below-range data  (default to RANGE if not defined)
+    options->badMask = pmConfigMaskGet("BAD", config);
+    psAssert (options->badMask, "bad mask not set");
+
+    // save MASK and MARK on the PSPHOT recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // set maskValue and markValue in the psphot recipe
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "user-defined mask", options->maskValue);
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, "MARK.PSPHOT", PS_META_REPLACE, "user-defined mask", options->markValue);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageSetThreads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageSetThreads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageSetThreads.c	(revision 22322)
@@ -0,0 +1,26 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+bool ppImageThread_ppImageDetrendReadout (psThreadJob *job) {
+
+    pmConfig *config        = job->args->data[0];
+    ppImageOptions *options = job->args->data[1];
+    pmFPAview *view         = job->args->data[2];
+
+    bool status = ppImageDetrendReadout(config, options, view);
+    return status;
+}
+
+bool ppImageSetThreads () {
+
+    psThreadTask *task = NULL;
+
+    task = psThreadTaskAlloc ("PPIMAGE_DETREND_READOUT", 3);
+    task->function = &ppImageThread_ppImageDetrendReadout;
+    psThreadTaskAdd (task);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageStats.c	(revision 22322)
@@ -0,0 +1,104 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+// calculate stats, including MD5
+bool ppImageStats (pmConfig *config, pmChip *chip, const pmFPAview *inputView, const ppImageOptions *options) {
+
+    bool mdok;              // Status of MD lookup
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+
+    pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
+    *view = *inputView;
+    
+    // select or create the fpa-level analysis stats metadata:
+    psMetadata *stats = psMetadataLookupPtr (&mdok, chip->parent->analysis, "PPIMAGE.STATS");
+    psMemIncrRefCounter (stats);
+    if (!stats) {
+	stats = psMetadataAlloc ();
+	if (!psMetadataAdd (chip->parent->analysis, PS_LIST_TAIL, "PPIMAGE.STATS", PS_DATA_METADATA, "stats container", stats)) {
+	    psError(PS_ERR_UNKNOWN, false, "Unable to push stats on fpa.analysis.");
+	    psFree (view);
+	    psFree (stats);
+	    return false;
+	}
+    }
+
+    view->cell = view->readout = -1;
+    while ((cell = pmFPAviewNextCell(view, chip->parent, 1)) != NULL) {
+	if (!cell->process || !cell->file_exists) {
+	    continue;
+	}
+
+	// Perform statistics on the detrended cell
+	if (options->doStats) {
+
+	    pmFPAfile *outImage = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
+	    pmFPAfile *outPhotom = psMetadataLookupPtr(&mdok, config->files, "PSPHOT.OUTPUT");
+	    pmFPAfile *outAstrom = psMetadataLookupPtr(&mdok, config->files, "PSASTRO.OUTPUT");
+
+	    if (!outImage && !outPhotom && !outAstrom) {
+		psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find any output file (PPIMAGE.OUTPUT, PSPHOT.OUTPUT, PSASTRO.OUTPUT).");
+		psFree (view);
+		psFree(stats);
+		return false;
+	    }
+
+	    // get the latest output product available
+	    pmFPAfile *output = outAstrom;
+	    if (!output) {
+		output = outPhotom;
+	    }
+	    if (!output) {
+		output = outImage;
+	    }
+
+	    if (!ppStatsFPA(stats, output->fpa, view, options->maskValue, config)) {
+		psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
+		psFree(view);
+		psFree(stats);
+		return false;
+	    }
+
+	    // extract the fringe amplitude from the analysis
+	    if (options->doFringe && !ppStatsFringe(stats, chip, "FRINGE", "FRINGE.SOLUTION")) {
+		psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to extract fringe solution for image.");
+		psFree(view);
+		psFree(stats);
+		return false;
+	    }
+
+	    // extract the fringe residual amplitude from the analysis
+	    if (options->doFringe && !ppStatsFringe(stats, chip, "FRINGE_RESID", "FRINGE.RESIDUAL.SOLUTION")) {
+		psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to extract fringe solution for image.");
+		psFree(view);
+		psFree(stats);
+		return false;
+	    }
+	}
+
+	// Add MD5 information for cell
+	pmHDU *hdu = pmHDUFromCell(cell); // HDU that owns the cell
+	while ((readout = pmFPAviewNextReadout(view, chip->parent, 1)) != NULL) {
+	    const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
+	    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME");
+
+	    psString headerName = NULL; // Header name for MD5
+	    psStringAppend(&headerName, "MD5_%s_%s_%d", chipName, cellName, view->readout);
+
+	    psVector *md5 = psImageMD5(readout->image); // md5 hash
+	    psString md5string = psMD5toString(md5); // String
+	    psFree(md5);
+	    psMetadataAddStr(hdu->header, PS_LIST_TAIL, headerName, PS_META_REPLACE,
+			     "Image MD5", md5string);
+	    psFree(md5string);
+	    psFree(headerName);
+	}
+    }
+    psFree(view);
+    psFree(stats);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageStatsOutput.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageStatsOutput.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageStatsOutput.c	(revision 22322)
@@ -0,0 +1,62 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+// write stats to output file
+bool ppImageStatsOutput (pmConfig *config, const ppImageOptions *options) {
+
+    bool mdok;
+
+    // measure statistics, or ignore?
+    if (!options->doStats) return true;
+
+    // PPIMAGE.STATS is stored on PPIMAGE.OUTPUT
+    pmFPAfile *output = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
+    if (!output) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find PPIMAGE.OUTPUT entry in config file.\n");
+        return false;
+    }
+
+    // select the fpa-level analysis stats metadata:
+    psMetadata *stats = psMetadataLookupPtr (&mdok, output->fpa->analysis, "PPIMAGE.STATS");
+    if (!stats) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find PPIMAGE.STATS entry in output fpa analysis.\n");
+        return false;
+    }
+
+    // convert the stats MDC to a string
+    char *statsMDC = psMetadataConfigFormat(stats);
+    if (!statsMDC || strlen(statsMDC) == 0) {
+        psError(PS_ERR_IO, false, "Unable to serialize stats metadata.\n");
+        return false;
+    }
+
+    // get the output stats filename
+    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
+    if (!statsName && !strlen(statsName)) {
+        psError (PS_ERR_UNEXPECTED_NULL, false, "missing STATS entry in arguments list.");
+        psFree(statsMDC);
+        return false;
+    }
+
+    // convert to a real UNIX filename
+    psString resolved = pmConfigConvertFilename(statsName, config, true, true); // Resolved filename
+    FILE *statsFile = fopen (resolved, "w");
+    if (!statsFile) {
+        psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
+        psFree(statsMDC);
+        psFree(resolved);
+        return false;
+    }
+    psFree(resolved);
+
+    // write the stats MDC to a file
+    // XXX why does this not call psMetadataConfigPrint?
+    fprintf(statsFile, "%s", statsMDC);
+
+    psFree(statsMDC);
+    fclose(statsFile);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppImageVersion.c	(revision 22322)
@@ -0,0 +1,61 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString ppImageVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString ppImageVersionLong(void)
+{
+    psString version = ppImageVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
+
+void ppImageVersionMetadata(psMetadata *metadata)
+{
+    PS_ASSERT_METADATA_NON_NULL(metadata,);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString psphot = psphotVersionLong(); // psphot version
+    psString psastro = psastroVersionLong(); // psastro version
+    psString ppStats = ppStatsVersionLong(); // ppStats version
+    psString ppImage = ppImageVersionLong(); // ppImage version
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString head = NULL;               // Head string
+    psStringAppend(&head, "ppImage processing at %s. Component information:", timeString);
+    psFree(timeString);
+
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psphot, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psastro, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppStats, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppImage, "");
+
+    psFree(head);
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(psphot);
+    psFree(psastro);
+    psFree(ppStats);
+    psFree(ppImage);
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/src/ppTest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/src/ppTest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/src/ppTest.c	(revision 22322)
@@ -0,0 +1,190 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include "pslib.h"
+#include "psmodules.h"
+
+int main(int argc, char *argv[])
+{
+    psLibInit(NULL);
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ppImageConfig.c
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    pmConfig *config = pmConfigRead(&argc, argv, "PPIMAGE");
+    if (! config) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // Parse other command-line arguments
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-key", 0, "exposure ID", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-bias", 0, "Name of the bias image", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-dark", 0, "Name of the dark image", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-flat", 0, "Name of the flat-field image", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-mask", 0, "Name of the mask image", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-fringe", 0, "Name of the fringe image", "");
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-chip", 0, "Chip number to process (if positive)", -1);
+
+    if (! psArgumentParse(config->arguments, &argc, argv) || argc != 3) {
+        printf("\nPan-STARRS Phase 2 processing\n\n");
+        printf("Usage: %s INPUT.fits OUTPUT.fits\n\n", argv[0]);
+        psArgumentHelp(config->arguments);
+        psFree(config->arguments);
+        exit(EXIT_FAILURE);
+    }
+
+    // Add the input and output images (which remain on the command-line) to the arguments list
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-input",  0, "Name of the input image", argv[1]);
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-output", 0, "Name of the output image", argv[2]);
+
+    // Define database handle, if used
+#if 0
+    config->database = pmConfigDB(config->site);
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ppImageParseCamera.c extract with some alterations
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    const char *inName = psMetadataLookupStr(NULL, config->arguments, "-input");
+    psLogMsg("ppImage", PS_LOG_INFO, "Opening input image: %s\n", inName);
+    psFits *inFile = psFitsOpen(inName, "r"); // File handle for FITS file
+    if (! inFile) {
+        // There's no point in continuing if we can't open the input
+        psErrorStackPrint(stderr, "Can't open input image: %s\n", inName);
+        exit(EXIT_FAILURE);
+    }
+    psMetadata *phu = psFitsReadHeader(NULL, inFile); // FITS primary header
+
+    psMetadata *cameraFormat = pmConfigCameraFormatFromHeader(NULL, NULL, config, phu, true);
+    if (! config->camera) {
+        cameraFormat = pmConfigCameraFormatFromHeader(NULL, NULL, config, phu, true);
+        if (! config->camera) {
+             // There's no point in continuing if we can't recognise what we've got
+            psErrorStackPrint(stderr, "Can't find camera configuration!\n");
+            exit(EXIT_FAILURE);
+        }
+    }
+    // Determine the correct recipe to use
+    if (! config->recipes && !pmConfigReadRecipes(config, PM_RECIPE_SOURCE_CAMERA | PM_RECIPE_SOURCE_CL)) {
+        // There's no point in continuing if we can't work out what recipes to use
+        psErrorStackPrint(stderr, "Can't find recipe configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+#if 1
+    const char *outName = psMetadataLookupStr(NULL, config->arguments, "-output");
+    psLogMsg("ppImage", PS_LOG_INFO, "Opening output image: %s\n", outName);
+    psFits *outFile = psFitsOpen(outName, "w");
+    if (!outFile) {
+        // There's no point in continuing if we can't open the output
+        psErrorStackPrint(stderr, "Can't open output image: %s\n", outName);
+        exit(EXIT_FAILURE);
+    }
+#endif
+
+    // Construct camera in preparation for reading
+    pmFPA *fpa = pmFPAConstruct(config->camera, config->cameraName);
+    pmFPAview *view = pmFPAAddSourceFromHeader(fpa, phu, cameraFormat);
+    printf("View chip: %d\n", view->chip);
+    printf("View cell: %d\n", view->cell);
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// The action happens here
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if 0
+    pmFPAPrint(stdout, fpa, false, false);
+    exit(0);
+#endif
+
+#if 0
+    // A mosaic
+    psMetadata *mosaicCamera = psMetadataConfigRead(NULL, NULL, "/home/mithrandir/price/ipp/config/mcshort_mosaic/camera.config", true);
+    pmFPA *mosaicFPA = pmFPAConstruct(mosaicCamera);
+    psMetadata *mosaicFormat = psMetadataConfigRead(NULL, NULL, "/home/mithrandir/price/ipp/config/mcshort_mosaic/format_mosaic.config", true);
+    pmFPAview *mosaicView = pmFPAviewAlloc(0);
+    pmFPAAddSourceFromView(mosaicFPA, mosaicView, mosaicFormat);
+    psFree(mosaicView);
+#endif
+
+    // Read the FPA
+    pmFPARead(fpa, inFile, NULL);
+
+#if 1
+    psArray *chips = fpa->chips;
+    pmFPAWrite(fpa, outFile, NULL, true, false);
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];
+#if 1
+        //pmChipRead(chip, inFile, NULL);
+        pmChipWrite(chip, outFile, NULL, true, false);
+        psArray *cells = chip->cells;
+        for (int j = 0; j < cells->n; j++) {
+            pmCell *cell = cells->data[j];
+            pmCellWrite(cell, outFile, NULL, true);
+#if 0 // Read bit by bit
+            pmReadout *readout = pmReadoutAlloc(cell);
+            for (int z = 0; pmReadoutReadNext(readout, inFile, z, 512); z++) {
+                do {
+                    printf("Chip %d, Cell %d, Plane %d, row0 = %d\n", i, j, z, readout->row0);
+                    pmReadoutWriteNext(readout, outFile, z);
+                    //pmFPAPrint(stdout, fpa, false, true);
+                } while (pmReadoutReadNext(readout, inFile, z, 512));
+            }
+            psFree(readout);
+#else // Read the whole cell at once
+            //            pmCellRead(cell, inFile, NULL);
+            //            pmCellWrite(cell, outFile, NULL, false);
+            //            pmCellFreeData(cell);
+#endif
+        }
+        pmChipWrite(chip, outFile, NULL, false, false);
+#endif
+
+#if 0
+        pmChipMosaic(mosaicFPA->chips->data[i], chip);
+        pmChipWrite(mosaicFPA->chips->data[i], outFile, NULL, true, true);
+        pmChipFreeData(chip);
+        pmChipFreeData(mosaicFPA->chips->data[i]);
+#endif
+    }
+    pmFPAWrite(fpa, outFile, NULL, false, false);
+#endif
+
+#if 0
+    pmFPAMosaic(mosaicFPA, fpa);
+    pmFPAWrite(mosaicFPA, outFile, NULL, true, true);
+    //pmFPAPrint(stdout, mosaicFPA, true, true);
+    psFree(mosaicCamera);
+    psFree(mosaicFormat);
+    psFree(mosaicView);
+    //psFree(mosaicFPA);
+#endif
+
+#if 1
+    pmFPAPrint(stdout, fpa, false, false);
+#endif
+
+#if 1
+    psFitsClose(outFile);
+#endif
+
+    psFree(view);
+    psFree(phu);
+    psFree(fpa);
+    psFree(config);
+    psFitsClose(inFile);
+    psFree(cameraFormat);
+
+    pmConceptsDone();
+    pmConfigDone();
+    psLibFinalize();
+
+    // Pau.
+}
Index: /tags/sj_tags/sj_root_20080929/ppImage/test/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppImage/test/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppImage/test/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*
Index: /tags/sj_tags/sj_root_20080929/ppMerge/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/.cvsignore	(revision 22322)
@@ -0,0 +1,16 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+missing
+test
+config.guess
+config.sub
+libtool
+ltmain.sh
Index: /tags/sj_tags/sj_root_20080929/ppMerge/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/ppMerge/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppMerge
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ppMerge/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/configure.ac	(revision 22322)
@@ -0,0 +1,30 @@
+AC_PREREQ(2.61)
+
+AC_INIT([ppMerge], [1.1.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+PKG_CHECK_MODULES([PPSTATS], [ppStats >= 1.0.0]) 
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/.cvsignore	(revision 22322)
@@ -0,0 +1,8 @@
+.deps
+Makefile
+Makefile.in
+ppMerge
+config.h
+config.h.in
+stamp-h1
+.libs
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/Makefile.am	(revision 22322)
@@ -0,0 +1,29 @@
+bin_PROGRAMS = ppMerge
+
+ppMerge_CFLAGS = $(PPMERGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+ppMerge_LDFLAGS = $(PPMERGE_LIBS) $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+
+ppMerge_SOURCES =		\
+	ppMerge.c		\
+	ppMergeArguments.c	\
+	ppMergeCamera.c		\
+	ppMergeFiles.c		\
+	ppMergeScaleZero.c	\
+	ppMergeFileGroup.c	\
+	ppMergeReadChunk.c	\
+	ppMergeLoop_Threaded.c  \
+	ppMergeSetThreads.c	\
+	ppMergeMask.c
+
+#	ppMergeLoop.c		
+
+noinst_HEADERS =		\
+	ppMerge.h
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMerge.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMerge.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMerge.c	(revision 22322)
@@ -0,0 +1,143 @@
+#include "ppMerge.h"
+
+// Yet to do:
+//
+// 1. Mask pixels with less than the minimum number of electrons
+// 2. Sampling factor for background measurement
+// 3. On/off pairs
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+    psTimerStart(TIMERNAME);
+
+    psExit exitValue = PS_EXIT_SUCCESS; // Exit value for program
+
+    pmConfig *config = pmConfigRead(&argc, argv, PPMERGE_RECIPE); // Configuration
+    if (!config) {
+        psErrorStackPrint(stderr, "Error reading configuration.");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!ppMergeArguments(argc, argv, config)) {
+        psErrorStackPrint(stderr, "Error reading arguments.");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    ppMergeType type = psMetadataLookupS32(NULL, config->arguments, "TYPE"); // Type of frame
+    switch (type) {
+      case PPMERGE_TYPE_MASK:
+        if (!ppMergeMask(config)) {
+            psErrorStackPrint(stderr, "Error generating mask.");
+            exitValue = PS_EXIT_DATA_ERROR;
+            goto die;
+        }
+        break;
+      case PPMERGE_TYPE_BIAS:
+      case PPMERGE_TYPE_DARK:
+      case PPMERGE_TYPE_SHUTTER:
+      case PPMERGE_TYPE_FLAT:
+      case PPMERGE_TYPE_FRINGE:
+        if (!ppMergeScaleZero(config)) {
+            psErrorStackPrint(stderr, "Error getting scale and zero-points.");
+            exitValue = PS_EXIT_DATA_ERROR;
+            goto die;
+        }
+        if (!ppMergeLoop(config)) {
+            psErrorStackPrint(stderr, "Error performing merge.");
+            exitValue = PS_EXIT_PROG_ERROR;
+            goto die;
+        }
+        break;
+      default:
+        psAbort("Invalid frame type: %x", type);
+    }
+
+
+    // Output the statistics
+    bool mdok;                          // Status of MD lookup
+    psString statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS.NAME"); // Statistics file name
+    if (mdok && statsName && strlen(statsName) > 0) {
+        psString resolved = pmConfigConvertFilename(statsName, config, true, true); // Resolved filename
+        FILE *statsFile = fopen(resolved, "w"); // Output statistics file
+        if (!statsFile) {
+            psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.", resolved);
+            psFree(resolved);
+            exitValue = PS_EXIT_CONFIG_ERROR;
+            goto die;
+        }
+        psFree(resolved);
+        psMetadata *stats = psMetadataLookupMetadata(&mdok, config->arguments, "STATS.DATA"); // Statistics
+        if (!stats) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find statistics");
+            exitValue = PS_EXIT_PROG_ERROR;
+            goto die;
+        }
+        psString statsOut = psMetadataConfigFormat(stats); // String to write out
+        fprintf(statsFile, "%s", statsOut);
+        psFree(statsOut);
+        fclose(statsFile);
+    }
+
+
+
+
+#if 0
+    // Set various tasks (define optional operations)
+    ppMergeOptions *options = ppMergeOptionsParse(config);
+    if (!options) {
+        psErrorStackPrint(stderr, "Unable to parse options.");
+        exit(EXIT_FAILURE);
+    }
+
+    // Check the inputs
+    ppMergeData *data = ppMergeCheckInputs(options, config);
+    if (!data) {
+        psErrorStackPrint(stderr, "Not enough valid input files.");
+        exit(EXIT_FAILURE);
+    }
+
+    if (options->mask) {
+        // Generate a mask
+        ppMergeMask(data, options, config);
+    } else {
+
+        psImage *scale = NULL;              // The scalings
+        psImage *zero = NULL;               // The zeros
+        psArray *shutters = NULL;           // The shutter correction data
+
+        // Measure the background in each image
+        ppMergeScaleZero(&scale, &zero, &shutters, data, options, config);
+
+        // Do the combination and write
+        ppMergeCombine(scale, zero, shutters, data, options, config);
+
+        psFree(scale);
+        psFree(zero);
+        psFree(shutters);
+    }
+
+    // Output the statistics
+    if (data->statsFile && data->stats) {
+        psString statsOut = psMetadataConfigFormat(data->stats); // String to write out
+        fprintf(data->statsFile, "%s", statsOut);
+        psFree(statsOut);
+    }
+
+    // Cleaning up
+    psFree(data);
+    psFree(options);
+#endif
+
+ die:
+    psTrace("ppSub", 1, "Finished at %f sec\n", psTimerMark("ppSub"));
+    psTimerStop();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exitValue);
+}
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMerge.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMerge.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMerge.h	(revision 22322)
@@ -0,0 +1,119 @@
+#ifndef PP_MERGE_H
+#define PP_MERGE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#define TIMERNAME "ppMerge"             // Name for timer
+#define PPMERGE_RECIPE "PPMERGE"        // Recipe name
+#define THREADED 1                      // Compile with threads?
+
+// Type of frame to merge
+typedef enum {
+    PPMERGE_TYPE_UNKNOWN,               // Unknown type
+    PPMERGE_TYPE_BIAS,                  // Bias frame
+    PPMERGE_TYPE_DARK,                  // (Multi-)Dark frame
+    PPMERGE_TYPE_MASK,                  // Mask frame
+    PPMERGE_TYPE_SHUTTER,               // Shutter frame
+    PPMERGE_TYPE_FLAT,                  // Flat-field frame (dome or sky)
+    PPMERGE_TYPE_FRINGE,                // Fringe frame
+} ppMergeType;
+
+// Files, for activation
+typedef enum {
+    PPMERGE_FILES_ALL,                  // All files
+    PPMERGE_FILES_INPUT,                // Input files
+    PPMERGE_FILES_OUTPUT                // Output files
+} ppMergeFiles;
+
+// Group of files to read
+//
+// Each file contributes a readout, into which is read a chunk from that file
+typedef struct {
+    psArray *readouts;                  // Input readouts
+    bool read;                          // Has the scan been read?
+    bool busy;                          // Is the scan being processed?
+    int firstScan;                      // First row of the chunk to be read for this group
+    int lastScan;                       // Last row of the chunk to be read for this group
+} ppMergeFileGroup;
+
+// Parse command-line arguments and recipe
+bool ppMergeArguments(int argc, char *argv[], // Command-line arguments
+                      pmConfig *config  // Configuration
+    );
+
+// Set up camera files
+bool ppMergeCamera(pmConfig *config     // Configuration
+    );
+
+// Measure scale and zero-points
+bool ppMergeScaleZero(pmConfig *config  // Configuration
+    );
+
+// Main loop to do the merging
+bool ppMergeLoop(pmConfig *config       // Configuration
+    );
+
+// Main loop for masks
+bool ppMergeMask(pmConfig *config       // Configuration
+    );
+
+// Read nominated input file
+bool ppMergeFileReadInput(pmConfig *config, // Configuration
+                          pmReadout *readout, // Readout into which to read
+                          int num,      // Number of file in sequence
+                          int rows      // Number of rows to read at once
+    );
+
+// Open nominated input file
+bool ppMergeFileOpenInput(pmConfig *config, // Configuration
+                          const pmFPAview *view, // View to open
+                          int num       // Number of file in sequence
+    );
+
+// Set the data level for files specified by name; return an array of the files
+psArray *ppMergeFileDataLevel(const pmConfig *config, // Configuration
+                              const char *name, // Name of files
+                              pmFPALevel level // Level for file data level
+    );
+
+// Activate/deactivate a list of files
+bool ppMergeFileActivate(const pmConfig *config, // Configuration
+                         ppMergeFiles files, // Files to turn on/off
+                         bool state     // Activation state
+    );
+
+// Activate/deactivate a single element for a list; return array of files
+psArray *ppMergeFileActivateSingle(const pmConfig *config, // Configuration
+                                   ppMergeFiles files, // Files to turn on/off
+                                   bool state,   // Activation state
+                                   int num // Number of file in sequence
+    );
+
+// Return name of output pmFPAfile
+psString ppMergeOutputFile(const pmConfig *config // Configuration
+    );
+
+
+// Allocator for group of files
+ppMergeFileGroup *ppMergeFileGroupAlloc(void);
+
+// Read chunk into the first available file group
+ppMergeFileGroup *ppMergeReadChunk(bool *status, // Status of read
+                                   psArray *fileGroups, // All groups
+                                   pmConfig *config, // Configuration
+                                   int numChunk // Chunk number (only for interest)
+    );
+
+// Set up thread handling
+bool ppMergeSetThreads(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeArguments.c	(revision 22322)
@@ -0,0 +1,391 @@
+#include "ppMerge.h"
+
+// Print usage information and die
+static void usage(const char *program,  // Name of the program
+                  psMetadata *arguments // Command-line arguments
+    )
+{
+    fprintf(stderr, "\nPan-STARRS Detrend Merging\n\n");
+    fprintf(stderr, "Usage: %s INPUT.mdc OUTPUT_ROOT\n"
+            "where INPUTS.mdc contains various METADATAs, each with:\n"
+            "\tIMAGE(STR):     Image filename\n"
+            "\tMASK(STR):      Mask filename\n"
+            "\tWEIGHT(STR)     Weight map filename\n"
+            "where MASK and WEIGHT are optional.",
+            program);
+    fprintf(stderr, "\n");
+    psArgumentHelp(arguments);
+}
+
+// Get a float-point value from the command-line or recipe, and add it to the arguments
+#define VALUE_ARG_RECIPE_FLOAT(ARGNAME, RECIPENAME, TYPE) { \
+    ps##TYPE value = psMetadataLookup##TYPE(NULL, arguments, ARGNAME); \
+    if (isnan(value)) { \
+        bool mdok; \
+        value = psMetadataLookup##TYPE(&mdok, recipe, RECIPENAME); \
+        if (!mdok) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
+                RECIPENAME, PPMERGE_RECIPE); \
+            goto ERROR; \
+        } \
+    } \
+    psMetadataAdd##TYPE(config->arguments, PS_LIST_TAIL, RECIPENAME, 0, NULL, value); \
+}
+
+// Get an integer value from the command-line or recipe, and add it to the arguments
+#define VALUE_ARG_RECIPE_INT(ARGNAME, RECIPENAME, TYPE, UNSET) { \
+    ps##TYPE value = psMetadataLookup##TYPE(NULL, arguments, ARGNAME); \
+    if (value == UNSET) { \
+        bool mdok; \
+        value = psMetadataLookup##TYPE(&mdok, recipe, RECIPENAME); \
+        if (!mdok) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
+                RECIPENAME, PPMERGE_RECIPE); \
+            goto ERROR; \
+        } \
+    } \
+    psMetadataAdd##TYPE(config->arguments, PS_LIST_TAIL, RECIPENAME, 0, NULL, value); \
+}
+
+// Get a boolean from the command-line or recipe, and add it to the arguments if either is set
+#define VALUE_ARG_RECIPE_BOOL(ARGNAME, RECIPENAME) { \
+    bool value = (psMetadataLookupBool(NULL, arguments, ARGNAME) || \
+                  psMetadataLookupBool(NULL, recipe, RECIPENAME)); \
+    psMetadataAddBool(config->arguments, PS_LIST_TAIL, RECIPENAME, 0, NULL, value); \
+}
+
+// Get a statistic name from the command-line or recipe, and add the enum to the arguments
+#define VALUE_ARG_RECIPE_STAT(ARGNAME, RECIPENAME) { \
+    const char *stat = psMetadataLookupStr(NULL, arguments, ARGNAME); \
+    if (!stat) { \
+        stat = psMetadataLookupStr(NULL, recipe, RECIPENAME); \
+        if (!stat) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find %s in recipe %s", \
+                    RECIPENAME, PPMERGE_RECIPE); \
+            goto ERROR; \
+        } \
+    } \
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, RECIPENAME, 0, NULL, psStatsOptionFromString(stat)); \
+}
+
+// Get a string from the command-line or recipe, and add to the arguments
+#define VALUE_ARG_RECIPE_STR(ARGNAME, RECIPENAME) { \
+    const char *str = psMetadataLookupStr(NULL, arguments, ARGNAME); \
+    if (!str) { \
+        str = psMetadataLookupStr(NULL, recipe, RECIPENAME); \
+        if (!str) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find %s in recipe %s", \
+                    RECIPENAME, PPMERGE_RECIPE); \
+            goto ERROR; \
+        } \
+    } \
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, RECIPENAME, 0, NULL, str); \
+}
+
+// Get a string from the command-line or recipe, and add the appropriate mask value to the arguments
+#define VALUE_ARG_RECIPE_MASK(ARGNAME, RECIPENAME) { \
+    const char *str = psMetadataLookupStr(NULL, arguments, ARGNAME); \
+    if (!str) { \
+        str = psMetadataLookupStr(NULL, recipe, RECIPENAME); \
+        if (!str) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find %s in recipe %s", \
+                    RECIPENAME, PPMERGE_RECIPE); \
+            goto ERROR; \
+        } \
+    } \
+    psMaskType mask = pmConfigMask(str, config); \
+    psMetadataAddU8(config->arguments, PS_LIST_TAIL, RECIPENAME, 0, NULL, mask); \
+}
+
+// Get a string value from the command-line and add it to the target
+static bool valueArgStr(psMetadata *arguments, // Command-line arguments
+                        const char *argName, // Argument name in the command-line arguments
+                        const char *mdName, // Name for value in the metadata
+                        psMetadata *target // Target metadata to which to add value
+                        )
+{
+    psString value = psMetadataLookupStr(NULL, arguments, argName); // Value of interest
+    if (value && strlen(value) > 0) {
+        return psMetadataAddStr(target, PS_LIST_TAIL, mdName, 0, NULL, value);
+    }
+    return false;
+}
+
+bool ppMergeArguments(int argc, char *argv[], pmConfig *config)
+{
+    assert(config);
+
+    psMetadata *arguments = psMetadataAlloc(); // Command-line arguments
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-type", 0, "Type of calibration frame", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stats", 0, "MDC file to hold statistics ", NULL);
+    // Standard combination parameters
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-rows",     0, "Rows to read per scan", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-sample",   0, "Sampling factor for background", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-iter",     0, "Number of rejection iterations", 0);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-rej",      0, "Rejection threshold (sigma)", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-fraclow",  0, "Fraction of low pixels to discard", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-frachigh", 0, "Fraction of low pixels to discard", NAN);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-nkeep",    0, "Minimum number of pixels in stack to keep", 0);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-weights", 0, "Use image weights in combination?", false);
+
+    // XXX EAM : not clear this should be allowed on the command line.
+    // psMetadataAddStr(arguments, PS_LIST_TAIL, "-maskval",  0, "Mask value for input data", NULL);
+
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-combine",  0, "Statistic to use for combination", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-mean",     0, "Statistic to use to measure the mean", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stdev",    0, "Statistic to use to measure the stdev", NULL);
+
+    // Fringe construction parameters
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-fringe-num",     0, "Number of fringe regions", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-fringe-size",    0, "Half-size of fringe regions", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-fringe-xsmooth", 0, "Number of smoothing regions in x", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-fringe-ysmooth", 0, "Number of smoothing regions in y", 0);
+
+    // Shutter construction parameters
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-shutter-size", 0, "Size for shutter measurement regions", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-shutter-iter", 0, "Number of iterations for shutter", 0);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-shutter-rej",  0, "Rejection limit for shutter", NAN);
+
+    // Mask construction parameters
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-mask-suspect",  0, "Threshold for suspect pixels (sigma)", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-mask-bad",      0, "Threshold for bad pixels (sigma)", NAN);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-mask-mode",     0, "Mode to identify bad pixels", NULL);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-mask-grow",     0, "Number of pixels to grow final mask", 0);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-mask-set-value",0, "Value to set for output mask pixels", NULL);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-mask-chip",    0, "Measure mask statistics by chip?", false);
+
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-mask-smooth-suspect",  0, "smooth suspect pixels?", false);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-mask-smooth-scale",  0, "smoothing sigma for suspect pixels", NAN);
+
+    // chip & cell selections are used to limit chips to be processed
+    int argnum;
+    if ((argnum = psArgumentGet (argc, argv, "-chip"))) {
+        psArgumentRemove (argnum, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_DATA_STRING, "", argv[argnum]);
+        psArgumentRemove (argnum, &argc, argv);
+    }
+    if ((argnum = psArgumentGet (argc, argv, "-cell"))) {
+        psArgumentRemove (argnum, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CELL_SELECTIONS", PS_DATA_STRING, "", argv[argnum]);
+        psArgumentRemove (argnum, &argc, argv);
+    }
+
+    // Number of threads
+    if ((argnum = psArgumentGet(argc, argv, "-threads"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        int nThreads = atoi(argv[argnum]);
+        psMetadataAddS32(config->arguments, PS_LIST_TAIL, "NTHREADS", 0, "number of warp threads", nThreads);
+        psArgumentRemove(argnum, &argc, argv);
+
+        // create the thread pool with number of desired threads, supplying our thread launcher function
+        // XXX need to determine the number of threads from the config data
+        psThreadPoolInit (nThreads);
+    }
+    ppMergeSetThreads();
+
+    if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 3) {
+        usage(argv[0], arguments);
+        goto ERROR;
+    }
+
+    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.");
+        goto ERROR;
+    }
+    psMetadataAddMetadata(config->arguments, PS_LIST_TAIL, "INPUTS", 0,
+                          "Metadata with input details", inputs);
+    psFree(inputs);
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0,
+                     "Root name of the output image list", argv[2]);
+
+    valueArgStr(arguments, "-stats", "STATS.NAME", config->arguments);
+
+
+    // Set the type of calibration frame
+    const char *typeStr = psMetadataLookupStr(NULL, arguments, "-type"); // Type of calibration
+    if (strlen(typeStr) <= 0) {
+      psError(PS_ERR_UNKNOWN, false, "No -type specified.");
+      goto ERROR;
+    }
+    ppMergeType type = PPMERGE_TYPE_UNKNOWN; // Enumerated type for frame type
+    if (strcasecmp(typeStr, "BIAS") == 0) {
+      type = PPMERGE_TYPE_BIAS;
+      goto VALID;
+    }
+    if (strcasecmp(typeStr, "DARK") == 0 ||
+        strcasecmp(typeStr, "DARK_PREMASK") == 0) {
+      type = PPMERGE_TYPE_DARK;
+      goto VALID;
+    }
+    if (!strcasecmp(typeStr, "FLAT") ||
+        !strcasecmp(typeStr, "DOMEFLAT") ||
+        !strcasecmp(typeStr, "SKYFLAT") ||
+        !strcasecmp(typeStr, "FLAT_RAW") ||
+        !strcasecmp(typeStr, "SKYFLAT_RAW") ||
+        !strcasecmp(typeStr, "DOMEFLAT_RAW") ||
+        !strcasecmp(typeStr, "FLAT_PREMASK") ||
+        !strcasecmp(typeStr, "SKYFLAT_PREMASK") ||
+        !strcasecmp(typeStr, "DOMEFLAT_PREMASK")) {
+      type = PPMERGE_TYPE_FLAT;
+      goto VALID;
+    }
+    if (strcasecmp(typeStr, "FRINGE") == 0) {
+      type = PPMERGE_TYPE_FRINGE;
+      goto VALID;
+    }
+    if (strcasecmp(typeStr, "SHUTTER") == 0) {
+      type = PPMERGE_TYPE_SHUTTER;
+      goto VALID;
+    }
+    if (strcasecmp(typeStr, "MASK") == 0 ||
+        strcasecmp(typeStr, "DARKMASK") == 0 ||
+        strcasecmp(typeStr, "FLATMASK") == 0) {
+      type = PPMERGE_TYPE_MASK;
+      goto VALID;
+    }
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised image type: %s", typeStr);
+    goto ERROR;
+
+ VALID:
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "TYPE", 0, "Type of calibration frame", type);
+
+    // Need to set the camera before the recipes can be read
+    if (!ppMergeCamera(config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to setup cameras.");
+        goto ERROR;
+    }
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPMERGE_RECIPE); // Recipe for ppSim
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPMERGE_RECIPE);
+        goto ERROR;
+    }
+
+    // Standard combination parameters
+    VALUE_ARG_RECIPE_INT("-rows",       "ROWS",     S32, 0);
+    VALUE_ARG_RECIPE_INT("-sample",     "SAMPLE",   S32, 0);
+    VALUE_ARG_RECIPE_INT("-iter",       "ITER",     S32, 0);
+    VALUE_ARG_RECIPE_FLOAT("-rej",      "REJ",      F32);
+    VALUE_ARG_RECIPE_FLOAT("-fraclow",  "FRACLOW",  F32);
+    VALUE_ARG_RECIPE_FLOAT("-frachigh", "FRACHIGH", F32);
+    VALUE_ARG_RECIPE_INT("-nkeep",      "NKEEP",    S32, 0);
+    VALUE_ARG_RECIPE_BOOL("-weights",   "WEIGHTS");
+
+    // XXX we do not supply this on the command line
+    // VALUE_ARG_RECIPE_MASK("-maskval",   "MASKVAL");
+
+    VALUE_ARG_RECIPE_STAT("-combine",   "COMBINE");
+    VALUE_ARG_RECIPE_STAT("-mean",      "MEAN");
+    VALUE_ARG_RECIPE_STAT("-stdev",     "STDEV");
+
+    // Fringe construction parameters
+    VALUE_ARG_RECIPE_INT("-fringe-num",     "FRINGE.NUM",     S32, 0);
+    VALUE_ARG_RECIPE_INT("-fringe-size",    "FRINGE.SIZE",    S32, 0);
+    VALUE_ARG_RECIPE_INT("-fringe-xsmooth", "FRINGE.XSMOOTH", S32, 0);
+    VALUE_ARG_RECIPE_INT("-fringe-ysmooth", "FRINGE.YSMOOTH", S32, 0);
+
+    // Shutter construction parameters
+    VALUE_ARG_RECIPE_INT("-shutter-size",  "SHUTTER.SIZE", S32, 0);
+
+    // Mask construction parameters
+    VALUE_ARG_RECIPE_FLOAT("-mask-suspect", "MASK.SUSPECT", F32);
+    VALUE_ARG_RECIPE_FLOAT("-mask-bad",     "MASK.BAD",     F32);
+    VALUE_ARG_RECIPE_INT("-mask-grow",      "MASK.GROW",    S32, 0);
+    VALUE_ARG_RECIPE_STR("-mask-set-value", "MASK.SET.VALUE");
+    VALUE_ARG_RECIPE_BOOL("-mask-chip",     "MASK.CHIPSTATS");
+
+    VALUE_ARG_RECIPE_BOOL("-mask-smooth-suspect", "MASK.SMOOTH.SUSPECT");
+    VALUE_ARG_RECIPE_FLOAT("-mask-smooth-scale", "MASK.SMOOTH.SCALE", F32);
+
+    const char *maskModeStr = psMetadataLookupStr(NULL, arguments, "-mask-mode"); // Mode to identify bad pix
+    if (!maskModeStr) {
+        maskModeStr = psMetadataLookupStr(NULL, recipe, "MASK.MODE");
+        if (!maskModeStr) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "No mask mode specified in recipe.");
+            goto ERROR;
+        }
+    }
+    pmMaskIdentifyMode maskMode = pmMaskIdentifyModeFromString(maskModeStr);
+    if (maskMode == PM_MASK_ID_NONE) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Invalid mask mode %s", maskModeStr);
+        goto ERROR;
+    }
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "MASK.MODE", 0, "Mode for mask identification",
+                     maskMode);
+
+
+    if (type == PPMERGE_TYPE_DARK) {
+        psMetadata *ordinates = psMetadataLookupMetadata(NULL, recipe, "DARK.ORDINATES"); // Ordinates info
+        psArray *translated = psArrayAllocEmpty(psListLength(ordinates->list)); // Translated version
+
+        psMetadataIterator *iter = psMetadataIteratorAlloc(ordinates, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *item;           // Item from iteration
+        while ((item = psMetadataGetAndIncrement(iter))) {
+            int order = 0;              // Polynomial order
+            bool scale = false;         // Scale values?
+            float min = NAN, max = NAN; // Minimum and maximum values for scaling
+            switch (item->type) {
+              case PS_TYPE_S32:
+                order = item->data.S32;
+                break;
+              case PS_DATA_METADATA:
+                order = psMetadataLookupS32(NULL, item->data.md, "ORDER");
+                bool mdok;                  // Status of MD lookup
+                scale = psMetadataLookupBool(&mdok, item->data.md, "SCALE");
+                min = psMetadataLookupF32(&mdok, item->data.md, "MIN");
+                max = psMetadataLookupF32(&mdok, item->data.md, "MAX");
+                break;
+              default:
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        "Type of DARK.ORDINATES entry %s (%x) is not METADATA or S32",
+                        item->name, item->type);
+                return false;
+            }
+            if (order <= 0) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "ORDER not positive (%d) for DARK.ORDINATES %s",
+                        order, item->name);
+                return false;
+            }
+
+            pmDarkOrdinate *ord = pmDarkOrdinateAlloc(item->name, order);
+            ord->scale = scale;
+            ord->min = min;
+            ord->max = max;
+            psArrayAdd(translated, translated->n, ord);
+            psFree(ord);
+        }
+        psFree(iter);
+
+        psMetadataAddArray(config->arguments, PS_LIST_TAIL, "DARK.ORDINATES", 0,
+                           "Ordinates to fit for dark", translated);
+        psFree(translated);             // Drop reference
+
+        psString darkNorm = psMetadataLookupStr(NULL, recipe, "DARK.NORM"); // Normalisation concept
+        if (darkNorm && strcmp(darkNorm, "NONE") != 0) {
+            psMetadataAddStr(config->arguments, PS_LIST_TAIL, "DARK.NORM", 0,
+                             "Normalisation concept for dark", darkNorm);
+        }
+    }
+
+
+#if 0
+    // Add concepts for scale and zero
+    // XXX These have never been used
+    psMetadataItem *scaleItem = psMetadataItemAllocF32("PPMERGE.SCALE", "Scaling for ppMerge", NAN);
+    psMetadataItem *zeroItem = psMetadataItemAllocF32("PPMERGE.ZERO", "Zero offset for ppMerge", NAN);
+    pmConceptRegister(scaleItem, NULL, NULL, false, PM_FPA_LEVEL_CELL);
+    pmConceptRegister(zeroItem, NULL, NULL, false, PM_FPA_LEVEL_CELL);
+    psFree(scaleItem);
+    psFree(zeroItem);
+#endif
+
+    psFree(arguments);
+    return true;
+
+ERROR:
+    psFree(arguments);
+    return false;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeCamera.c	(revision 22322)
@@ -0,0 +1,345 @@
+#include "ppMerge.h"
+
+// Define an output file, with its own FPA
+bool outputFile(pmConfig *config,       // Configuration
+                const char *name,       // Name of output file
+                pmFPAfileType type,     // Type of file
+                const char *description, // Description of file
+                psMetadata *format,     // Camera format
+                pmFPAview *view         // View for PHU
+    )
+{
+    assert(config);
+    assert(name && strlen(name) > 0);
+    assert(view);
+
+    // Output image
+    pmFPA *fpa = pmFPAConstruct(config->camera, config->cameraName); // FPA to contain the output
+    if (!fpa) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to construct an FPA from camera configuration.");
+        return false;
+    }
+
+    // Chip selection: turn on only the chips specified.
+    bool status;
+    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS");
+    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    if (chips->n > 0) {
+        pmFPASelectChip (fpa, -1, true); // deselect all chips
+        for (int i = 0; i < chips->n; i++) {
+            int chipNum = atoi(chips->data[i]);
+            if (! pmFPASelectChip(fpa, chipNum, false)) {
+                psError(PS_ERR_IO, false, "Chip number %d doesn't exist in camera.\n", chipNum);
+                psFree (chips);
+                return false;
+            }
+        }
+    }
+    psFree (chips);
+
+    // Cell selection: turn on only the cells specified.  Note that this affects the same cell
+    // for all chips
+    char *cellLine = psMetadataLookupStr(&status, config->arguments, "CELL_SELECTIONS");
+    psArray *cells = psStringSplitArray (cellLine, ",", false);
+    if (cells->n > 0) {
+        for (int i = 0; i < fpa->chips->n; i++) {
+            pmChip *chip = fpa->chips->data[i];
+            pmChipSelectCell (chip, -1, true); // deselect all cells
+            for (int j = 0; j < cells->n; j++) {
+                int cellNum = atoi(cells->data[j]);
+                if (! pmChipSelectCell(chip, cellNum, false)) {
+                    psError(PS_ERR_IO, false, "Cell number %d doesn't exist in camera.\n", cellNum);
+                    psFree (cells);
+                    return false;
+                }
+            }
+        }
+    }
+    psFree (cells);
+
+    pmFPAfile *output = pmFPAfileDefineOutput(config, fpa, name);
+    psFree(fpa);                        // Drop reference
+    if (!output) {
+        psError(PS_ERR_IO, false, "Unable to generate output file from %s", name);
+        return false;
+    }
+    if (output->type != type) {
+        psError(PS_ERR_IO, true, "%s is not of type %s", name, pmFPAfileStringFromType(type));
+        return false;
+    }
+    output->save = true;
+
+    if (!pmFPAAddSourceFromView(fpa, description, view, format)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate output FPA.");
+        return false;
+    }
+
+    return true;
+}
+
+
+bool ppMergeCamera(pmConfig *config)
+{
+    bool haveMasks = false;             // Do we have masks?
+    bool haveWeights = false;           // Do we have weight maps?
+
+    ppMergeType type = psMetadataLookupS32(NULL, config->arguments, "TYPE"); // Type of frame
+    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 numFiles = 0;                   // Number of files
+    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 weight = psMetadataLookupStr(&mdok, input, "WEIGHT"); // Name of weight map
+
+        // 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, "PPMERGE.INPUT", "IMAGE.FILENAMES");
+        if (!imageFile || !found) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define file from image %d (%s)", numFiles, image);
+            return false;
+        }
+        if (imageFile->type != PM_FPA_FILE_IMAGE) {
+            psError(PS_ERR_IO, true, "PPMERGE.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, "PPMERGE.INPUT.MASK",
+                                                        "MASK.FILENAMES");
+            if (!status) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to define file from mask %d (%s)", numFiles, mask);
+                return false;
+            }
+            if (maskFile->type != PM_FPA_FILE_MASK) {
+                psError(PS_ERR_IO, true, "PPMERGE.INPUT.MASK is not of type MASK");
+                return false;
+            }
+            haveMasks = true;
+        }
+
+        // Optionally add the weight file
+        if (weight && strlen(weight) > 0) {
+            haveWeights = true;
+            psArray *weightFiles = psArrayAlloc(1); // Array of filenames for this FPA
+            weightFiles->data[0] = psMemIncrRefCounter(weight);
+            psMetadataAddArray(config->arguments, PS_LIST_TAIL, "WEIGHT.FILENAMES", PS_META_REPLACE,
+                               "Filenames of weight files", weightFiles);
+            psFree(weightFiles);
+
+            bool status;
+            pmFPAfile *weightFile = pmFPAfileBindFromArgs(&status, imageFile, config, "PPMERGE.INPUT.WEIGHT",
+                                                          "WEIGHT.FILENAMES");
+            if (!status) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to define file from weight %d (%s)", numFiles, weight);
+                return false;
+            }
+            if (weightFile->type != PM_FPA_FILE_WEIGHT) {
+                psError(PS_ERR_IO, true, "PPMERGE.INPUT.WEIGHT is not of type WEIGHT");
+                return false;
+            }
+            haveWeights = true;
+        }
+
+        numFiles++;
+    }
+    psFree(iter);
+    psMetadataRemoveKey(config->arguments, "IMAGE.FILENAMES");
+    if (psMetadataLookup(config->arguments, "MASK.FILENAMES")) {
+        psMetadataRemoveKey(config->arguments, "MASK.FILENAMES");
+    }
+    if (psMetadataLookup(config->arguments, "WEIGHT.FILENAMES")) {
+        psMetadataRemoveKey(config->arguments, "WEIGHT.FILENAMES");
+    }
+    if (psMetadataLookup(config->arguments, "PSF.FILENAMES")) {
+        psMetadataRemoveKey(config->arguments, "PSF.FILENAMES");
+    }
+
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "INPUTS.NUM", 0, "Number of input files", numFiles);
+    psMetadataAddBool(config->arguments, PS_LIST_TAIL, "INPUTS.MASKS", 0, "Got input masks?", haveMasks);
+    psMetadataAddBool(config->arguments, PS_LIST_TAIL, "INPUTS.WEIGHTS", 0,
+                      "Got input weights?", haveWeights);
+
+
+    // Check that all the inputs are consistent
+
+// Check an FPA level to ensure the camera format and PHU are consistent across all input files
+#define CHECK_LEVEL(HDU, CHIP, CELL) { \
+    if (HDU) { \
+        if (!phuView) { \
+            phuView = pmFPAviewAlloc(0); \
+            phuView->chip = CHIP; \
+            phuView->cell = CELL; \
+        } else if ((phuView->chip != (CHIP)) && (phuView->cell != (CELL))) { \
+            psError(PS_ERR_UNKNOWN, true, "Differing PHU for input %d", i); \
+            psFree(phuView); \
+            return false; \
+        } \
+        if (!format) { \
+            format = (HDU)->format; \
+        } else if (format != (HDU)->format) { \
+            psError(PS_ERR_UNKNOWN, true, "Camera format %d doesn't match: %p vs %p", \
+                    i, format, (HDU)->format); \
+            psFree(phuView); \
+            return false; \
+        } \
+        continue; \
+    } \
+}
+
+    psMetadata *format = NULL;          // Camera format
+    pmFPAview *phuView = NULL;          // View to PHU
+    for (int i = 0; i < numFiles; i++) {
+        pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i); // File of interest
+        pmFPA *fpa = input->fpa;        // FPA of interest
+        CHECK_LEVEL(fpa->hdu, -1, -1);
+        psArray *chips = fpa->chips;    // Array of chips
+        for (int j = 0; j < chips->n; j++) {
+            pmChip *chip = chips->data[j]; // Chip of interest
+            CHECK_LEVEL(chip->hdu, j, -1);
+            psArray *cells = chip->cells;   // Array of cells
+            for (int k = 0; k < cells->n; k++) {
+                pmCell *cell = cells->data[k]; // Cell of interest
+                CHECK_LEVEL(cell->hdu, j, k);
+            }
+        }
+    }
+    if (!phuView || !format) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find PHU for input files.");
+        psFree(phuView);
+        return false;
+    }
+
+    // Cull chips and cells that don't have data
+    // Otherwise the abundance of metadata in the concepts (esp. for GPC) can overload the memory
+    for (int i = 0; i < numFiles; i++) {
+        pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i); // File of interest
+        pmFPA *fpa = input->fpa;        // FPA of interest
+        psArray *chips = fpa->chips; // Array of chips in output
+        for (int i = 0; i < chips->n; i++) {
+            pmChip *chip = chips->data[i]; // Chip of interest
+            psArray *cells = chip->cells; // Array of cells
+            int culled = 0;             // Number of culled cells
+            for (int j = 0; j < cells->n; j++) {
+                pmCell *cell = cells->data[j];
+                pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // HDU for cell
+                if (!hdu || hdu->blankPHU) {
+                    cell->data_exists = false;
+                    cell->file_exists = false;
+                    culled++;
+                    if (cell->concepts) {
+                        psFree(cell->concepts);
+                        cell->concepts = NULL;
+                    }
+                }
+            }
+            if (culled == cells->n) {
+                chip->data_exists = false;
+                chip->file_exists = false;
+                if (chip->concepts) {
+                    psFree(chip->concepts);
+                    chip->concepts = NULL;
+                }
+            }
+        }
+    }
+
+    // Count the cells
+    {
+        int numCells = 0;               // Number of cells
+        pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", 0); // Representative file
+        pmFPA *fpa = input->fpa;        // FPA for file
+        psArray *chips = fpa->chips; // Array of chips
+        for (int i = 0; i < chips->n; i++) {
+            pmChip *chip = chips->data[i]; // Chip of interest
+            psArray *cells = chip->cells; // Array of cells
+            for (int j = 0; j < cells->n; j++) {
+                pmCell *cell = cells->data[j]; // Cell of interest
+                pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // HDU that would have data
+                if (hdu && !hdu->blankPHU) {
+                    numCells++;
+                }
+            }
+        }
+
+        psMetadataAddS32(config->arguments, PS_LIST_TAIL, "INPUTS.CELLS", 0, "Number of cells in input",
+                         numCells);
+    }
+
+    psString outName = ppMergeOutputFile(config); // Name of output file
+
+    pmFPAfileType fileType = PM_FPA_FILE_NONE; // Type of output file
+    switch (type) {
+      case PPMERGE_TYPE_BIAS:
+        fileType = PM_FPA_FILE_IMAGE;
+        break;
+      case PPMERGE_TYPE_DARK:
+        fileType = PM_FPA_FILE_DARK;
+        break;
+      case PPMERGE_TYPE_MASK:
+        fileType = PM_FPA_FILE_MASK;
+        break;
+      case PPMERGE_TYPE_SHUTTER:
+        fileType = PM_FPA_FILE_IMAGE;
+        break;
+      case PPMERGE_TYPE_FLAT:
+        fileType = PM_FPA_FILE_IMAGE;
+        break;
+      case PPMERGE_TYPE_FRINGE:
+        fileType = PM_FPA_FILE_FRINGE;
+      default:
+        psAbort("Unknown frame type: %x", type);
+    }
+
+    if (!outputFile(config, outName, fileType, "Merged detrend", format, phuView)) {
+        psFree(outName);
+        psFree(phuView);
+        return false;
+    }
+    psFree(outName);
+
+    if (!outputFile(config, "PPMERGE.OUTPUT.SIGMA", PM_FPA_FILE_IMAGE, "Merge sigma", format, phuView)) {
+        psFree(phuView);
+        return false;
+    }
+
+    if (!outputFile(config, "PPMERGE.OUTPUT.COUNT", PM_FPA_FILE_IMAGE, "Merged count", format, phuView)) {
+        psFree(phuView);
+        return false;
+    }
+
+    psFree(phuView);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,26 @@
+/*
+ * The line
+    { PPMERGE_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+#include "pslib.h"
+#include "ppMergeErrorCodes.h"
+
+void ppMergeErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { PPMERGE_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PPMERGE_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = PPMERGE_ERR_NERROR - PPMERGE_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       p_psMemSetPersistent(tmp, true);
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeErrorCodes.dat	(revision 22322)
@@ -0,0 +1,10 @@
+#
+# This file is used to generate ppMergeErrorClasses.h
+#
+BASE = 700		First value we use; lower values belong to psLib
+# these errors correspond to standard exit conditions
+ARGUMENTS               Incorrect arguments
+SYS                     System error
+CONFIG                  Problem in configure files
+PROG                    Programming error
+DATA                    invalid data
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,18 @@
+#if !defined(PPMERGE_ERROR_CODES_H)
+#define PPMERGE_ERROR_CODES_H
+/*
+ * The line
+ *  PPMERGE_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    PPMERGE_ERR_BASE = 512,
+    PPMERGE_ERR_${ErrorCode},
+    PPMERGE_ERR_NERROR
+} ppMergeErrorCode;
+
+void ppMergeErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeFileGroup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeFileGroup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeFileGroup.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "ppMerge.h"
+
+static void mergeFileGroupFree(ppMergeFileGroup *group)
+{
+    psFree(group->readouts);
+    return;
+}
+
+ppMergeFileGroup *ppMergeFileGroupAlloc(void)
+{
+    ppMergeFileGroup *group = psAlloc(sizeof(ppMergeFileGroup));
+    psMemSetDeallocator(group, (psFreeFunc)mergeFileGroupFree);
+
+    group->readouts = NULL;
+    group->read = false;
+    group->busy = false;
+    return group;
+}
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeFiles.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeFiles.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeFiles.c	(revision 22322)
@@ -0,0 +1,205 @@
+#include "ppMerge.h"
+
+const char *allFiles[] = { "PPMERGE.INPUT", "PPMERGE.INPUT.MASK", "PPMERGE.INPUT.WEIGHT",
+                           "PPMERGE.OUTPUT", "PPMERGE.OUTPUT.COUNT", "PPMERGE.OUTPUT.SIGMA",
+                           NULL };      // All files
+const char *inputFiles[] = { "PPMERGE.INPUT", "PPMERGE.INPUT.MASK", "PPMERGE.INPUT.WEIGHT",
+                             NULL };    // Input files
+const char *outputFiles[] = { "PPMERGE.OUTPUT", "PPMERGE.OUTPUT.COUNT", "PPMERGE.OUTPUT.SIGMA",
+                              NULL };   // Output files
+
+// Select file list based on enum
+static const char **selectFiles(ppMergeFiles files)
+{
+    switch (files) {
+      case PPMERGE_FILES_ALL:    return allFiles;
+      case PPMERGE_FILES_INPUT:  return inputFiles;
+      case PPMERGE_FILES_OUTPUT: return outputFiles;
+      default:
+        psAbort("Invalid file option");
+    }
+    return NULL;
+}
+
+bool ppMergeFileReadInput(pmConfig *config, pmReadout *readout, int num, int rows)
+{
+    pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", num);
+    if (!pmReadoutReadChunk(readout, file->fits, 0, rows, 0, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read readout.");
+        return false;
+    }
+    bool mdok;          // Status of MD lookup
+    if (psMetadataLookupBool(&mdok, config->arguments, "INPUTS.MASKS")) {
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT.MASK", num);
+        if (!pmReadoutReadChunkMask(readout, file->fits, 0, rows, 0, config)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read readout mask.");
+            return false;
+        }
+    }
+    if (psMetadataLookupBool(&mdok, config->arguments, "INPUTS.WEIGHTS")) {
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT.WEIGHT", num);
+        if (!pmReadoutReadChunkWeight(readout, file->fits, 0, rows, 0, config)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read readout weight.");
+            return false;
+        }
+    }
+    return true;
+}
+
+bool ppMergeFileOpenInput(pmConfig *config, const pmFPAview *view, int num)
+{
+    {
+        pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", num);
+        pmFPAview *fileView = pmFPAviewForLevel(input->fileLevel, view);
+        if (!pmFPAfileOpen(input, fileView, config)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to open image file %d", num);
+            psFree(fileView);
+            return false;
+        }
+        psFree(fileView);
+    }
+    bool mdok;          // Status of MD lookup
+    if (psMetadataLookupBool(&mdok, config->arguments, "INPUTS.MASKS")) {
+        pmFPAfile *mask = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT.MASK", num); // Mask file
+        pmFPAview *fileView = pmFPAviewForLevel(mask->fileLevel, view);
+        if (!pmFPAfileOpen(mask, fileView, config)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to open mask file %d", num);
+            psFree(fileView);
+            return false;
+        }
+        psFree(fileView);
+    }
+    if (psMetadataLookupBool(&mdok, config->arguments, "INPUTS.WEIGHTS")) {
+        pmFPAfile *weight = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT.WEIGHT", num); // Weight file
+        pmFPAview *fileView = pmFPAviewForLevel(weight->fileLevel, view);
+        if (!pmFPAfileOpen(weight, fileView, config)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to open weight file %d", num);
+            psFree(fileView);
+            return false;
+        }
+        psFree(fileView);
+    }
+    return true;
+}
+
+psArray *ppMergeFileDataLevel(const pmConfig *config, const char *name, pmFPALevel level)
+{
+    assert(config);
+    assert(name);
+
+    int numFiles = psMetadataLookupS32(NULL, config->arguments, "INPUTS.NUM"); // Number of input files
+    assert(numFiles > 0);
+    psArray *files = psArrayAlloc(numFiles); // Files of interest
+    for (int i = 0; i < numFiles; i++) {
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, name, i); // Image file
+        file->dataLevel = level;
+        file->freeLevel = level;
+        files->data[i] = psMemIncrRefCounter(file);
+    }
+    return files;
+}
+
+bool ppMergeFileActivate(const pmConfig *config, ppMergeFiles files, bool state)
+{
+    assert(config);
+
+    bool mdok;                          // Status of MD lookup
+    bool haveMasks = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.MASKS"); // Do we have masks?
+    bool haveWeights = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.WEIGHTS"); // Got weights?
+
+    const char **fileList = selectFiles(files); // Files to activate
+    for (int i = 0; fileList[i] != NULL; i++) {
+        if (!haveMasks && strcmp(fileList[i], "PPMERGE.INPUT.MASK") == 0) {
+            continue;
+        }
+        if (!haveWeights && strcmp(fileList[i], "PPMERGE.INPUT.WEIGHT") == 0) {
+            continue;
+        }
+        psString name = NULL;           // Name of file
+        if (strcmp(fileList[i], "PPMERGE.OUTPUT") == 0) {
+            name = ppMergeOutputFile(config);
+        } else {
+            name = psStringCopy(fileList[i]);
+        }
+
+        if (!pmFPAfileActivate(config->files, state, name)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to activate file %s", name);
+            psFree(name);
+            return false;
+        }
+        psFree(name);
+    }
+
+    return true;
+}
+
+
+
+psArray *ppMergeFileActivateSingle(const pmConfig *config, ppMergeFiles files, bool state, int num)
+{
+    assert(config);
+
+    bool mdok;                          // Status of MD lookup
+    bool haveMasks = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.MASKS"); // Do we have masks?
+    bool haveWeights = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.WEIGHTS"); // Got weights?
+
+    psList *list = psListAlloc(NULL);   // List of files
+    const char **fileList = selectFiles(files); // Files to activate
+    for (int i = 0; fileList[i] != NULL; i++) {
+        if (!haveMasks && strcmp(fileList[i], "PPMERGE.INPUT.MASK") == 0) {
+            continue;
+        }
+        if (!haveWeights && strcmp(fileList[i], "PPMERGE.INPUT.WEIGHT") == 0) {
+            continue;
+        }
+
+        psString name = NULL;           // Name of file
+        if (strcmp(fileList[i], "PPMERGE.OUTPUT") == 0) {
+            name = ppMergeOutputFile(config);
+        } else {
+            name = psStringCopy(fileList[i]);
+        }
+
+        pmFPAfile *file = pmFPAfileActivateSingle(config->files, state, name, num); // Activated file
+        psFree(name);
+        psListAdd(list, PS_LIST_TAIL, file);
+    }
+
+    psArray *array = psListToArray(list);
+    psFree(list);
+
+    return array;
+}
+
+
+psString ppMergeOutputFile(const pmConfig *config)
+{
+    ppMergeType type = psMetadataLookupS32(NULL, config->arguments, "TYPE"); // Type of frame
+    const char *outSuffix = NULL;         // Suffix for output file
+    switch (type) {
+      case PPMERGE_TYPE_BIAS:
+        outSuffix = "BIAS";
+        break;
+      case PPMERGE_TYPE_DARK:
+        outSuffix = "DARK";
+        break;
+      case PPMERGE_TYPE_MASK:
+        outSuffix = "MASK";
+        break;
+      case PPMERGE_TYPE_SHUTTER:
+        outSuffix = "SHUTTER";
+        break;
+      case PPMERGE_TYPE_FLAT:
+        outSuffix = "FLAT";
+        break;
+      case PPMERGE_TYPE_FRINGE:
+        outSuffix = "FRINGE";
+      default:
+        psAbort("Unknown frame type: %x", type);
+    }
+
+    psString outName = NULL;            // Name of output file
+    psStringAppend(&outName, "PPMERGE.OUTPUT.%s", outSuffix);
+
+    return outName;
+}
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeLoop.c	(revision 22322)
@@ -0,0 +1,358 @@
+#include "ppMerge.h"
+
+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 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
+    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
+
+    // set the mask and mark bit values based on the named masks
+    psMaskType maskVal;
+    psMaskType 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->weights = useWeights;
+
+    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
+    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;
+            }
+
+            pmReadout *outRO = pmReadoutAlloc(outCell);
+
+            psArray *readouts = psArrayAlloc(numFiles); // Input readouts
+            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;
+                }
+
+                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);
+            }
+            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);
+
+            psFree(readouts);
+
+            // 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);
+
+                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);
+            }
+
+            // 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 (!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(weights);
+    psFree(stats);
+    return true;
+
+ERROR:
+    psFree(view);
+    psFree(combination);
+    psFree(inputs);
+    psFree(masks);
+    psFree(weights);
+    psFree(stats);
+    return false;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeLoop_Threaded.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeLoop_Threaded.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeLoop_Threaded.c	(revision 22322)
@@ -0,0 +1,436 @@
+#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 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
+    if (haveMasks) {
+        masks = ppMergeFileDataLevel(config, "PPMERGE.INPUT.MASK", PM_FPA_LEVEL_READOUT);
+    }
+    if (haveWeights) {
+        weights = ppMergeFileDataLevel(config, "PPMERGE.INPUT.WEIGHT", 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 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
+
+    // set the mask and mark bit values based on the named masks
+    psMaskType maskVal;
+    psMaskType 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->weights = useWeights;
+
+    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
+    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;
+            }
+
+            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_U8));
+
+                      // 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_U8));
+
+                      // 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);
+
+                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);
+            }
+
+            // 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(weights);
+    psFree(stats);
+    return true;
+
+ERROR:
+    psFree(view);
+    psFree(combination);
+    psFree(inputs);
+    psFree(masks);
+    psFree(weights);
+    psFree(stats);
+    return false;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeMask.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeMask.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeMask.c	(revision 22322)
@@ -0,0 +1,457 @@
+#include "ppMerge.h"
+
+static bool mergeMask(pmConfig *config, // Configuration
+                      const pmFPAview *view, // View to chip
+                      bool writeOut,     // Write output?
+                      psRandom *rng,    // Random number generator
+                      psMetadata *stats // Statistics output
+    )
+{
+    assert(config);
+    assert(view);
+    assert(view->chip != -1 && view->cell == -1 && view->readout == -1);
+
+    bool mdok;                          // Status of MD lookup
+    int numFiles = psMetadataLookupS32(NULL, config->arguments, "INPUTS.NUM"); // Number of input files
+    psStatsOptions meanStat = psMetadataLookupS32(NULL, config->arguments, "MEAN"); // Statistic for mean
+    psStatsOptions stdevStat = psMetadataLookupS32(NULL, config->arguments, "STDEV"); // Statistic for stdev
+    int sample = psMetadataLookupS32(NULL, config->arguments, "SAMPLE"); // Size of sample for statistics
+    bool chipStats = psMetadataLookupBool(&mdok, config->arguments, "MASK.CHIPSTATS"); // Statistics on chip?
+    float maskSuspect = psMetadataLookupF32(NULL, config->arguments, "MASK.SUSPECT"); // Threshold for suspect pixels
+    float maskBad = psMetadataLookupF32(NULL, config->arguments, "MASK.BAD"); // Threshold for bad pixels
+    pmMaskIdentifyMode maskMode = psMetadataLookupS32(NULL, config->arguments, "MASK.MODE"); // Mode for identifying bad pixels
+    int maskGrow = psMetadataLookupS32(NULL, config->arguments, "MASK.GROW"); // Radius to grow mask
+
+    bool smoothSuspect = psMetadataLookupBool(&mdok, config->arguments, "MASK.SMOOTH.SUSPECT"); // Radius to grow mask
+    float smoothScale = psMetadataLookupF32(&mdok, config->arguments, "MASK.SMOOTH.SCALE"); // Radius to grow mask
+
+    psMaskType markVal;
+    psMaskType maskValRaw;
+    if (!pmConfigMaskSetBits (&maskValRaw, &markVal, config)) {
+	psError (PS_ERR_UNKNOWN, true, "Unable to define the mask bit values");
+	return false;
+    }
+
+    char *maskOutName = psMetadataLookupStr (&mdok, config->arguments, "MASK.SET.VALUE");
+    psMaskType maskValOut = pmConfigMaskGet (maskOutName, config);
+    if (!maskValOut) {
+	psError (PS_ERR_UNKNOWN, true, "Undefined output mask bit value");
+	return false;
+    }
+    
+    psStats *statistics = psStatsAlloc(meanStat | stdevStat); // Statistics for background
+
+    psString outName = ppMergeOutputFile(config); // Name of output file
+    pmChip *outChip = pmFPAfileThisChip(config->files, view, outName); // Output chip
+    psFree(outName);
+    
+    int numCells = 1;
+    if (chipStats) {
+	// count the number of active cells for this chip:
+	numCells = 0;
+	for (int i = 0; i < outChip->cells->n; i++) {
+	    pmCell *cell = outChip->cells->data[i];
+	    if (!cell->process) continue;
+	    numCells ++;
+	}
+    }
+
+    // For each input file, get the statistics, which can be calculated at the chip or cell levels
+    psVector *values = psVectorAlloc(sample, PS_TYPE_F32); // Pixel values for statistics
+    pmFPAview *inView = pmFPAviewAlloc(0); // View for input
+    for (int i = 0; i < numFiles; i++) {
+        pmFPAfileActivate(config->files, false, NULL);
+        psArray *files = ppMergeFileActivateSingle(config, PPMERGE_FILES_INPUT, true, i); // Input files
+        pmFPAfile *input = files->data[0]; // Input file
+        psFree(files);
+        pmFPA *inFPA = input->fpa;  // Input FPA
+        *inView = *view;
+
+        int valueIndex = 0;             // Index for vector of pixel values
+
+        pmCell *inCell;                 // Input cell
+        while ((inCell = pmFPAviewNextCell(inView, inFPA, 1))) {
+
+	    // the output FPA structure carries the information about which cells to process
+            pmCell *outCell = pmFPAfileThisCell(config->files, inView, "PPMERGE.OUTPUT.MASK"); // Output cell
+	    if (!outCell->process) continue;
+
+            pmHDU *hdu = pmHDUFromCell(inCell); // HDU for cell
+            if (!hdu || hdu->blankPHU) {
+                // No data here
+                continue;
+            }
+            psTrace("ppMerge", 1, "Getting suspect pixels for file %d chip %d cell %d",
+                    i, inView->chip, inView->cell);
+
+            if (!pmFPAfileIOChecks(config, inView, PM_FPA_BEFORE)) {
+                psFree(inView);
+                goto MERGE_MASK_ERROR;
+            }
+
+            if (!ppMergeFileOpenInput(config, view, i)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to open file %d", i);
+                psFree(inView);
+                goto MERGE_MASK_ERROR;
+            }
+
+            if (inCell->readouts->n > 1) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                        "File %d chip %d cell %d contains more than one readout (%ld)",
+                        i, inView->chip, inView->cell, inCell->readouts->n);
+                psFree(inView);
+                goto MERGE_MASK_ERROR;
+            }
+
+            pmReadout *readout;
+            if (inCell->readouts && inCell->readouts->n == 1) {
+                readout = psMemIncrRefCounter(inCell->readouts->data[0]); // Input readout
+            } else {
+                readout = pmReadoutAlloc(inCell);
+            }
+
+            if (!ppMergeFileReadInput(config, readout, i, 0)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to read readout %d", i);
+                psFree(inView);
+                psFree(readout);
+                goto MERGE_MASK_ERROR;
+            }
+
+            pmReadout *outRO = NULL;    // Output readout
+            if (outCell->readouts && outCell->readouts->n == 1) {
+                outRO = psMemIncrRefCounter(outCell->readouts->data[0]);
+            } else {
+                outRO = pmReadoutAlloc(outCell);
+            }
+            psImage *outMask = outRO->mask;    // Output mask image (for iterative generation of mask)
+
+            psImage *image = readout->image, *mask = readout->mask; // Image and mask
+            int numCols = readout->image->numCols, numRows = readout->image->numRows; // Image size
+            int numPix = numCols * numRows; // Number of pixels
+            int num = PS_MIN(numPix, sample / numCells); // Number of values to add
+            if (!chipStats) {
+                valueIndex = 0;
+            }
+            for (int i = 0; i < num; i++) {
+                int pixel = numPix * psRandomUniform(rng);
+                int x = pixel % numCols;
+                int y = pixel / numCols;
+                if (mask && (mask->data.PS_TYPE_MASK_DATA[y][x] & maskValRaw)) continue;
+		if (outMask && (outMask->data.PS_TYPE_MASK_DATA[y][x] & maskValOut)) continue;
+		if (!isfinite(image->data.F32[y][x])) continue;
+
+                values->data.F32[valueIndex++] = image->data.F32[y][x];
+            }
+
+            if (!chipStats) {
+                values->n = valueIndex;
+                if (!psVectorStats(statistics, values, NULL, NULL, 0)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to do statistics on readout.");
+                    psFree(inView);
+                    psFree(readout);
+                    goto MERGE_MASK_ERROR;
+                }
+
+		float mean = psStatsGetValue(statistics, meanStat);
+		float stdev = psStatsGetValue(statistics, stdevStat);
+
+		// this function increments the count for each suspect pixel in each input plane
+		// maskValRaw is used to test for valid input pixels
+                if (!pmMaskFlagSuspectPixels(outRO, readout, mean, stdev, maskSuspect, maskValRaw)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to find suspect values in file %d", i);
+                    psFree(inView);
+                    psFree(readout);
+                    goto MERGE_MASK_ERROR;
+                }
+                pmCellFreeData(inCell);
+
+                if (!pmFPAfileIOChecks(config, inView, PM_FPA_AFTER)) {
+                    psFree(inView);
+                    psFree(readout);
+                    goto MERGE_MASK_ERROR;
+                }
+            }
+            psFree(readout);
+            psFree(outRO);
+        }
+
+        // Additional run through cells if we want chip-level statistics
+        if (chipStats && valueIndex > 0) {
+            values->n = valueIndex;
+            if (!psVectorStats(statistics, values, NULL, NULL, 0)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to do statistics on chip.");
+                goto MERGE_MASK_ERROR;
+            }
+            inView->cell = -1;
+            while ((inCell = pmFPAviewNextCell(inView, inFPA, 1))) {
+
+		// the output FPA structure carries the information about which cells to process
+		pmCell *outCell = pmFPAfileThisCell(config->files, inView, "PPMERGE.OUTPUT.MASK"); // Output cell
+		if (!outCell->process) continue;
+
+                pmHDU *hdu = pmHDUFromCell(inCell); // HDU for cell
+                if (!hdu || hdu->blankPHU) continue;
+
+                pmReadout *readout = inCell->readouts->data[0]; // Readout of interest
+
+                inView->readout = 0;
+                pmReadout *outRO = pmFPAfileThisReadout(config->files, inView, "PPMERGE.OUTPUT.MASK");
+
+		float mean = psStatsGetValue(statistics, meanStat);
+		float stdev = psStatsGetValue(statistics, stdevStat);
+
+		if (!pmMaskFlagSuspectPixels(outRO, readout, mean, stdev, maskSuspect, maskValRaw)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to find suspect values in file %d", i);
+                    goto MERGE_MASK_ERROR;
+                }
+                pmCellFreeData(inCell);
+
+                inView->readout = -1;
+                if (!pmFPAfileIOChecks(config, inView, PM_FPA_AFTER)) {
+                    psFree(inView);
+                    goto MERGE_MASK_ERROR;
+                }
+            }
+        }
+    }
+    psFree(inView);
+    psFree(statistics); statistics = NULL;
+    psFree(values); values = NULL;
+
+
+    // Another run through the chip to threshold on the suspects
+    pmFPAfileActivate(config->files, false, NULL);
+    if (writeOut) {
+        ppMergeFileActivate(config, PPMERGE_FILES_OUTPUT, true);
+    }
+    pmFPA *outFPA = outChip->parent;    // Output FPA
+    pmCell *outCell;                    // Output cell
+    pmFPAview *outView = pmFPAviewAlloc(0); // View into output FPA
+    *outView = *view;
+    while ((outCell = pmFPAviewNextCell(outView, outFPA, 1))) {
+
+	// skip inactive cells
+	if (!outCell->process) continue;
+
+        pmHDU *hdu = pmHDUFromCell(outCell); // HDU for cell
+        if (!hdu || hdu->blankPHU) continue;
+
+        psTrace("ppMerge", 1, "Getting bad pixels for chip %d cell %d", outView->chip, outView->cell);
+
+        assert(outCell->readouts && outCell->readouts->n == 1);
+        pmReadout *outRO = outCell->readouts->data[0]; // Output readout
+
+	if (smoothSuspect) {
+	    // XXX test output of suspect pixel image
+	    psImage *suspects = psMetadataLookupPtr(NULL, outRO->analysis, PM_MASK_ANALYSIS_SUSPECT); // Suspect img
+	    assert (suspects);
+	    psImageSmooth (suspects, smoothScale, 3); // extend smoothing region to 3-sigma
+	}
+
+	// set the bad pixels to the value 'maskVal'
+        if (!pmMaskIdentifyBadPixels(outRO, maskValOut, maskBad, maskMode)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to mask bad pixels");
+            goto MERGE_MASK_ERROR;
+        }
+
+        // Supplementary outputs
+        {
+            // The counts image is fairly useless, but it preserves the model
+            pmCell *countsCell = pmFPAfileThisCell(config->files, outView, "PPMERGE.OUTPUT.COUNT");
+            pmReadout *countsRO = pmReadoutAlloc(countsCell); // Readout with count of inputs per pixel
+            countsRO->image = psImageAlloc(outRO->mask->numCols, outRO->mask->numRows, PS_TYPE_F32);
+            psImageInit(countsRO->image, numFiles);
+            countsRO->data_exists = countsCell->data_exists = countsCell->parent->data_exists = true;
+            psFree(countsRO);
+
+            pmCell *sigmaCell = pmFPAfileThisCell(config->files, outView, "PPMERGE.OUTPUT.SIGMA");
+            pmReadout *sigmaRO = pmReadoutAlloc(sigmaCell); // Readout with suspect image
+            psImage *suspect = psMetadataLookupPtr(NULL, outRO->analysis, PM_MASK_ANALYSIS_SUSPECT);
+            sigmaRO->image = psImageCopy(sigmaRO->image, suspect, PS_TYPE_F32);
+            psMetadataRemoveKey(outRO->analysis, PM_MASK_ANALYSIS_SUSPECT);
+            sigmaRO->data_exists = sigmaCell->data_exists = sigmaCell->parent->data_exists = true;
+            psFree(sigmaRO);
+        }
+
+        if (maskGrow > 0) {
+            psImage *grown = psImageGrowMask(NULL, outRO->mask, maskValOut, maskGrow, maskValOut); // Grown mask
+            psFree(outRO->mask);
+            outRO->mask = grown;
+        }
+
+        if (writeOut) {
+            if (!pmFPAfileIOChecks(config, outView, PM_FPA_BEFORE)) {
+                psFree(outView);
+                goto MERGE_MASK_ERROR;
+            }
+
+            outRO->data_exists = outCell->data_exists = outChip->data_exists = true;
+
+            // Average concepts
+            psList *cells = psListAlloc(NULL); // List of cells, for concept averaging
+            for (int i = 0; i < numFiles; i++) {
+                pmFPAfile *inFile = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i); // Input file
+                pmCell *inCell = pmFPAviewThisCell(outView, inFile->fpa); // Input cell
+                psListAdd(cells, PS_LIST_TAIL, inCell);
+            }
+            if (!pmConceptsAverageCells(outCell, cells, NULL, NULL, true)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to average cell concepts.");
+                psFree(cells);
+                psFree(outRO);
+                psFree(outView);
+                return false;
+            }
+            psFree(cells);
+
+            // Statistics on the merged cell using a fake image
+            outRO->image = psImageAlloc(outRO->mask->numCols, outRO->mask->numRows, PS_TYPE_F32);
+            psImageInit(outRO->image, 1.0);
+            if (!ppStatsFPA(stats, outRO->parent->parent->parent, outView, maskValOut, config)) {
+                psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to generate stats for image.");
+                psFree(outRO);
+                psFree(outView);
+                return false;
+            }
+            psFree(outRO->image);
+            outRO->image = NULL;
+
+            // Write
+            if (!pmFPAfileIOChecks(config, outView, PM_FPA_AFTER)) {
+                psFree(outView);
+                goto MERGE_MASK_ERROR;
+            }
+        }
+    }
+    psFree(outView);
+
+    ppMergeFileActivate(config, PPMERGE_FILES_ALL, true);
+
+    return true;
+
+
+MERGE_MASK_ERROR:
+    psFree(statistics);
+    psFree(values);
+    return false;
+}
+
+bool ppMergeMask(pmConfig *config)
+{
+    assert(config);
+
+    bool mdok;                          // Status of MD lookup
+    int numFiles = psMetadataLookupS32(NULL, config->arguments, "INPUTS.NUM"); // Number of inputs
+    bool haveMasks = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.MASKS"); // Do we have masks?
+    bool haveWeights = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.WEIGHTS"); // Do we have weights?
+    int iter = psMetadataLookupS32(NULL, config->arguments, "ITER"); // Number of rejection iterations
+
+    PS_ASSERT_INT_POSITIVE(iter, false);
+
+    pmFPAview *view = pmFPAviewAlloc(0); // View to component of interest
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+
+    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);
+    }
+
+    psArray *inputs = ppMergeFileDataLevel(config, "PPMERGE.INPUT", PM_FPA_LEVEL_READOUT); // Input images
+    psFree(inputs);
+    if (haveMasks) {
+        psArray *masks = ppMergeFileDataLevel(config, "PPMERGE.INPUT.MASK", PM_FPA_LEVEL_READOUT);
+        psFree(masks);
+    }
+    if (haveWeights) {
+        psArray *weights = ppMergeFileDataLevel(config, "PPMERGE.INPUT.WEIGHT", PM_FPA_LEVEL_READOUT);
+        psFree(weights);
+    }
+
+    if (!ppMergeFileActivate(config, PPMERGE_FILES_INPUT, true)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to activate files.");
+        goto PPMERGE_MASK_ERROR;
+    }
+    if (!ppMergeFileActivate(config, PPMERGE_FILES_OUTPUT, true)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to activate files.");
+        goto PPMERGE_MASK_ERROR;
+    }
+
+    // XXX this function should use pmConfigMaskReadHeader () to get the named values defined
+    // for the input masks.
+
+    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
+
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        goto PPMERGE_MASK_ERROR;
+    }
+    pmChip *outChip;                    // Chip of interest
+    while ((outChip = pmFPAviewNextChip(view, outFPA, 1))) {
+
+        if (!outChip->process) continue;
+
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+            goto PPMERGE_MASK_ERROR;
+        }
+
+        for (int i = 0; i < iter; i++) {
+            if (!mergeMask(config, view, (i == iter - 1), rng, stats)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to merge chip %d", view->chip);
+                goto PPMERGE_MASK_ERROR;
+            }
+        }
+
+	if (outChip->data_exists) {
+	    psList *inChips = psListAlloc(NULL);
+	    for (int i=0; i < numFiles; i++) {
+		pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i); // Input file
+		pmChip *chip = pmFPAviewThisChip(view, file->fpa);
+		psListAdd(inChips, PS_LIST_TAIL, chip);
+	    }
+
+	    // XXX I need to call pmConfigMaskWriteHeader for the PHU somewhere, after it is created!
+
+	    if (!pmConceptsAverageChips(outChip, inChips, true)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to average Chip concepts.");
+		psFree(inChips);
+		goto PPMERGE_MASK_ERROR;
+	    }
+	    psFree(inChips);
+	}
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            goto PPMERGE_MASK_ERROR;
+        }
+    }
+
+    psList *fpaList = psListAlloc(NULL);// List of FPAs for concept averaging
+    for (int i = 0; i < numFiles; i++) {
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i); // Input file
+        psListAdd(fpaList, PS_LIST_TAIL, file->fpa);
+    }
+    if (!pmConceptsAverageFPAs(outFPA, fpaList)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to average FPA concepts.");
+        psFree(fpaList);
+        goto PPMERGE_MASK_ERROR;
+    }
+    psFree(fpaList);
+
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        goto PPMERGE_MASK_ERROR;
+    }
+
+    psFree(stats);
+    psFree(view);
+    psFree(rng);
+
+    return true;
+
+PPMERGE_MASK_ERROR:
+    psFree(stats);
+    psFree(view);
+    psFree(rng);
+    return false;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeReadChunk.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeReadChunk.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeReadChunk.c	(revision 22322)
@@ -0,0 +1,106 @@
+# include "ppMerge.h"
+
+#define THREAD_WAIT 10000               // Microseconds to wait if thread is not available
+
+ppMergeFileGroup *ppMergeReadChunk(bool *status, psArray *fileGroups, pmConfig *config, int numChunk)
+{
+    *status = true;
+
+    bool mdok;
+    bool haveMasks = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.MASKS"); // Do we have masks?
+    bool haveWeights = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.WEIGHTS");// Do we have weights?
+    int rows = psMetadataLookupS32(NULL, config->arguments, "ROWS"); // Number of rows to read per chunk
+
+    // select an available fileGroup
+    while (1) {
+        // check for any fileGroups which can read data
+        for (int j = 0; j < fileGroups->n; j++) {
+            ppMergeFileGroup *fileGroup = fileGroups->data[j];
+            if (fileGroup->read) {
+                continue;
+            }
+
+            // find max last scan so far
+            int lastScan = 0;
+            for (int i = 0; i < fileGroups->n; i++) {
+                ppMergeFileGroup *fileGroup = fileGroups->data[i];
+                lastScan = PS_MAX(fileGroup->lastScan, lastScan);
+            }
+            fileGroup->firstScan = lastScan;
+            fileGroup->lastScan = lastScan + rows;
+
+            psArray *readouts = fileGroup->readouts;
+
+            psTimerStart ("ppMergeReadChunk");
+
+            psTrace("ppStack", 2, "Reading data for chunk %d into fileGroup %d....n", numChunk, j);
+            for (int i = 0; i < readouts->n; i++) {
+                pmReadout *inRO = readouts->data[i]; // Input readout
+
+                // override the recorded last scan
+                inRO->thisImageScan  = fileGroup->firstScan;
+                inRO->thisWeightScan = fileGroup->firstScan;
+                inRO->thisMaskScan   = fileGroup->firstScan;
+                inRO->forceScan      = true;
+
+                // Read a chunk from a file
+                pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i);
+
+                bool keepReading = false;
+                if (pmReadoutMore(inRO, file->fits, 0, rows, config)) {
+                    keepReading = true;
+                    if (!pmReadoutReadChunk(inRO, file->fits, 0, rows, 0, config)) {
+                        psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPMERGE.INPUT %d",
+                                numChunk, i);
+                        *status = false;
+                        return NULL;
+                    }
+                }
+
+                if (haveMasks && pmReadoutMoreMask(inRO, file->fits, 0, rows, config)) {
+                    keepReading = true;
+                    pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT.MASK", i);
+                    if (!pmReadoutReadChunkMask(inRO, file->fits, 0, rows, 0, config)) {
+                        psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPMERGE.INPUT.MASK %d",
+                                numChunk, i);
+                        *status = false;
+                        return NULL;
+                    }
+                }
+
+                if (haveWeights && pmReadoutMoreWeight(inRO, file->fits, 0, rows, config)) {
+                    keepReading = true;
+                    pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT.WEIGHT", i);
+                    if (!pmReadoutReadChunkWeight(inRO, file->fits, 0, rows, 0, config)) {
+                        psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPMERGE.INPUT.WEIGHT %d",
+                                numChunk, i);
+                        *status = false;
+                        return NULL;
+                    }
+                }
+                if (!keepReading) {
+                    return NULL;
+                }
+            }
+
+            fileGroup->read = fileGroup->busy = true;
+            return fileGroup;
+        }
+
+        // Check for threads that are ready to read
+        bool wait = true;
+        for (int j = 0; j < fileGroups->n; j++) {
+            ppMergeFileGroup *fileGroup = fileGroups->data[j];
+            if (fileGroup->busy) {
+                continue;
+            }
+            fileGroup->read = false;
+            wait = false;
+        }
+        if (wait) {
+            // No threads currently available
+            usleep(THREAD_WAIT);
+        }
+    }
+    return NULL;
+}
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeScaleZero.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeScaleZero.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeScaleZero.c	(revision 22322)
@@ -0,0 +1,211 @@
+#include "ppMerge.h"
+
+// Get the scale and zero for each chip of each input
+bool ppMergeScaleZero(pmConfig *config)
+{
+    assert(config);
+
+    ppMergeType type = psMetadataLookupS32(NULL, config->arguments, "TYPE"); // Type of frame
+    int numInputs = psMetadataLookupS32(NULL, config->arguments, "INPUTS.NUM"); // Number of inputs
+    int numCells = psMetadataLookupS32(NULL, config->arguments, "INPUTS.CELLS"); // Number of cells
+    psStatsOptions meanStat = psMetadataLookupS32(NULL, config->arguments, "MEAN"); // Statistic for mean
+    psStatsOptions stdevStat = psMetadataLookupS32(NULL, config->arguments, "STDEV"); // Statistic for stdev
+    int shutterSize = psMetadataLookupS32(NULL, config->arguments, "SHUTTER.SIZE"); // Size of shutter region
+
+    psVector *gains = NULL;             // Gains for each cell
+    psArray *shutters = NULL;           // Shutter data for each cell
+    psStats *stats = NULL;              // Statistics for background
+    psImage *background = NULL;         // Background measurements per cell per file
+
+    switch (type) {
+      case PPMERGE_TYPE_BIAS:
+      case PPMERGE_TYPE_DARK:
+        // Nothing to measure
+        return true;
+      case PPMERGE_TYPE_FLAT:
+      case PPMERGE_TYPE_FRINGE:
+        gains = psVectorAlloc(numCells, PS_TYPE_F32);
+        background = psImageAlloc(numCells, numInputs, PS_TYPE_F32);
+        psImageInit(background, NAN);
+        stats = psStatsAlloc(meanStat);
+        break;
+      case PPMERGE_TYPE_SHUTTER:
+        shutters = psArrayAlloc(numCells);
+        break;
+      case PPMERGE_TYPE_MASK:
+      default:
+        break;
+    }
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+    pmFPAview *view = NULL;             // View into FPA
+
+    for (int i = 0; i < numInputs; i++) {
+        pmFPAfileActivate(config->files, false, NULL);
+        psArray *files = ppMergeFileActivateSingle(config, PPMERGE_FILES_INPUT, true, i); // Activated files
+        pmFPAfile *input = files->data[0]; // Representative file; should be the image (not mask or weight)
+        pmFPA *fpa = input->fpa;        // FPA of interest
+        view = pmFPAviewAlloc(0);       // View to component of interest
+        int cellNum = 0;                // Index for cell
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+            goto ERROR;
+        }
+        pmChip *chip;                   // Chip of interest
+        while ((chip = pmFPAviewNextChip(view, fpa, 1))) {
+            if (!chip->file_exists) {
+                continue;
+            }
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                goto ERROR;
+            }
+
+            pmCell *cell;               // Cell of interest
+            while ((cell = pmFPAviewNextCell(view, fpa, 1))) {
+                if (!cell->file_exists) {
+                    continue;
+                }
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                    goto ERROR;
+                }
+
+                if (!cell->data_exists) {
+                    continue;
+                }
+
+                if (cell->readouts->n > 1) {
+                    psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                            "File %d chip %d cell %d contains more than one readout (%ld)",
+                            i, view->chip, view->cell, cell->readouts->n);
+                    goto ERROR;
+                }
+                pmReadout *readout = cell->readouts->data[0]; // Readout of interest
+
+                psMaskType maskVal = pmConfigMaskGet("MASK.VALUE", config); // Value to mask
+
+                switch (type) {
+                  case PPMERGE_TYPE_FLAT:
+                  case PPMERGE_TYPE_FRINGE: {
+                      // Extract the gain
+                      float gain = psMetadataLookupF32(NULL, cell->concepts, "CELL.GAIN"); // Cell gain
+                      if (!isfinite(gain)) {
+                        // psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                        // "CELL.GAIN for file %d chip %d cell %d is not set.",
+                        // i, view->chip, view->cell);
+                        // goto ERROR;
+                        psWarning ("CELL.GAIN for file %d chip %d cell %d is NaN", i, view->chip, view->cell);
+                      }
+                      gains->data.F32[cellNum] = gain;
+
+                      // Measure the background
+                      if (!psImageBackground(stats, NULL, readout->image, readout->mask, maskVal, rng)) {
+                        // psError(PS_ERR_UNKNOWN, false,
+                        // "Unable to get statistics for file %d chip %d cell %d",
+                        // i, view->chip, view->cell);
+                        // goto ERROR;
+                        psWarning ("Unable to get statistics for file %d chip %d cell %d", i, view->chip, view->cell);
+                        background->data.F32[i][cellNum] = NAN;
+                      } else {
+                        background->data.F32[i][cellNum] = psStatsGetValue(stats, meanStat);
+                      }
+                      break;
+                  }
+                  case PPMERGE_TYPE_SHUTTER: {
+                      pmShutterCorrectionData *shutter = shutters->data[cellNum]; // Shutter correction data
+                      if (!shutter) {
+                          shutter = pmShutterCorrectionDataAlloc(readout->image->numCols,
+                                                                 readout->image->numRows,
+                                                                 shutterSize);
+                          shutters->data[cellNum] = shutter;
+                      }
+                      if (!pmShutterCorrectionAddReadout(shutter, readout, meanStat, stdevStat,
+                                                         maskVal, rng)) {
+                          psError(PS_ERR_UNKNOWN, false,
+                                  "Can't add file %d chip %d cell %d to shutter correction.",
+                                  i, view->chip, view->cell);
+                          goto ERROR;
+                      }
+                      break;
+                  }
+                  case PPMERGE_TYPE_MASK:
+                  default:
+                    psAbort("Should never get here.");
+                }
+
+                cellNum++;
+
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+                    goto ERROR;
+                }
+            }
+
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+                goto ERROR;
+            }
+        }
+
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            goto ERROR;
+        }
+
+        psFree(view);
+
+#if 0
+        // Reset files for reading again
+        for (int i = 0; i < files->n; i++) {
+            pmFPAfile *file = files->data[i]; // File of interest
+        }
+#endif
+        psFree(files);
+    }
+
+    psFree(rng); rng = NULL;
+    psFree(stats); stats = NULL;
+
+    // Store results
+    switch (type) {
+      case PPMERGE_TYPE_FRINGE:
+        psMetadataAddImage(config->arguments, PS_LIST_TAIL, "ZEROS", 0,
+                           "Zero to subtract from each input cell", background);
+        // Flow through
+      case PPMERGE_TYPE_FLAT: {
+          // Need to normalize over the focal plane
+          if (psTraceGetLevel("ppMerge") > 9) {
+              for (int i = 0; i < gains->n; i++) {
+                  psTrace("ppMerge", 10, "Gain for cell %d is %f\n", i, gains->data.F32[i]);
+              }
+          }
+          psVector *fluxes = NULL;        // Solution to fluxes
+          if (!pmFlatNormalize(&fluxes, &gains, background)) {
+              psError(PS_ERR_UNKNOWN, false, "Normalisation failed to converge --- continuing anyway.");
+              psFree(fluxes);
+              goto ERROR;
+          }
+
+          psMetadataAddVector(config->arguments, PS_LIST_TAIL, "SCALES", 0,
+                              "Scale to divide into each input file", fluxes);
+          psFree(fluxes);               // Drop reference
+          break;
+      }
+      case PPMERGE_TYPE_SHUTTER:
+        psMetadataAddArray(config->arguments, PS_LIST_TAIL, "SHUTTER", 0,
+                           "Shutter data", shutters);
+        break;
+      case PPMERGE_TYPE_MASK:
+      default:
+        psAbort("Should never get here.");
+    }
+
+    psFree(background);
+    psFree(shutters);
+    return true;
+
+ERROR:
+    // Common path for errors
+    psFree(gains);
+    psFree(background);
+    psFree(shutters);
+    psFree(rng);
+    psFree(stats);
+    psFree(view);
+    return false;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeScaleZero.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeScaleZero.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeScaleZero.h	(revision 22322)
@@ -0,0 +1,19 @@
+#ifndef PP_MERGE_SCALE_ZERO_H
+#define PP_MERGE_SCALE_ZERO_H
+
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppMergeData.h"
+#include "ppMergeOptions.h"
+
+// Get the scale and zero for each chip of each input
+bool ppMergeScaleZero(psImage **scales, // The scales for each integration/cell
+                      psImage **zeros, // The zeroes for each integration/cell
+                      psArray **shutter,// The shutter correction data for each cell
+                      ppMergeData *data,// The data
+                      const ppMergeOptions *options, // The options
+                      const pmConfig *config // The configuration
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeSetThreads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeSetThreads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeSetThreads.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "ppMerge.h"
+
+// "PPMERGE_READOUT_COMBINE", 5
+bool ppMergeThread_pmReadoutCombine(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    pmReadout *output           = job->args->data[0];
+    ppMergeFileGroup *fileGroup = job->args->data[1];
+    psVector *zero              = job->args->data[2];
+    psVector *scale             = job->args->data[3];
+    pmCombineParams *params     = job->args->data[4];
+
+    bool status = pmReadoutCombine (output, fileGroup->readouts, zero, scale, params);
+
+    // after we are done, tell the I/O system that this file group is done
+    fileGroup->busy = false;
+    return status;
+}
+
+bool ppMergeThread_pmDarkCombine(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    pmCell *outCell             = job->args->data[0];
+    ppMergeFileGroup *fileGroup = job->args->data[1];
+    psScalar *iter              = job->args->data[2];
+    psScalar *rej               = job->args->data[3];
+    psScalar *maskVal           = job->args->data[4];
+
+    bool status = pmDarkCombine(outCell, fileGroup->readouts, iter->data.S32, rej->data.F32, maskVal->data.U8);
+
+    // after we are done, tell the I/O system that this file group is done
+    fileGroup->busy = false;
+    return status;
+}
+
+bool ppMergeThread_pmShutterCorrectionGenerate(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    pmReadout *output             = job->args->data[0];
+    pmReadout *pattern            = job->args->data[1];
+    ppMergeFileGroup *fileGroup   = job->args->data[2];
+    psScalar *shutterRef          = job->args->data[3];
+    pmShutterCorrectionData *data = job->args->data[4];
+    psScalar *iter                = job->args->data[5];
+    psScalar *rej                 = job->args->data[6];
+    psScalar *maskVal             = job->args->data[7];
+
+    bool status = pmShutterCorrectionGenerate(output, pattern, fileGroup->readouts, shutterRef->data.F32, data, iter->data.S32, rej->data.F32, maskVal->data.U8);
+
+    // after we are done, tell the I/O system that this file group is done
+    fileGroup->busy = false;
+    return status;
+}
+
+bool ppMergeSetThreads(void)
+{
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PPMERGE_READOUT_COMBINE", 5);
+        task->function = &ppMergeThread_pmReadoutCombine;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PPMERGE_DARK_COMBINE", 5);
+        task->function = &ppMergeThread_pmDarkCombine;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PPMERGE_SHUTTER_CORRECTION", 8);
+        task->function = &ppMergeThread_pmShutterCorrectionGenerate;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeThreadLauncher.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeThreadLauncher.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeThreadLauncher.c	(revision 22322)
@@ -0,0 +1,94 @@
+# include "ppMerge.h"
+
+// each thread runs this function, starting a new job when it finished with an old one
+// it is called with a (void *) pointer to its own thread pointer
+void *ppMergeThreadLauncher (void *data) {
+
+    psThread *self = data;
+    psThreadJob *job = NULL;
+
+    while (1) {
+
+	// if we get an error, just wait until we are cleared or killed
+	while (self->fault) {
+	    usleep (10000);
+	}
+
+	// request a new job, if there are none available, sleep a bit
+	// we have to lock here so the job queue cannot be empty yet no threads busy
+	psThreadLock();
+	while ((job = psThreadJobGetPending ()) == NULL) {
+	    psThreadUnlock();
+	    usleep (10000);
+	}
+	self->busy = true;
+	psThreadUnlock();
+
+	// list all allowed job types here
+
+	// pmReadoutCombine
+	if (!strcmp (job->type, "PPMERGE_READOUT_COMBINE")) {
+	    psAssert (job->args->n == 5, "invalid number of arguments to pmReadoutCombine");
+
+	    pmReadout *output           = job->args->data[0];
+	    ppMergeFileGroup *fileGroup = job->args->data[1];
+	    psVector *zero              = job->args->data[2];
+	    psVector *scale             = job->args->data[3];
+	    pmCombineParams *params     = job->args->data[4];
+
+	    bool status = pmReadoutCombine (output, fileGroup->readouts, zero, scale, params);
+	    if (!status) {
+		self->fault = true;
+	    }
+
+	    // we do not have to lock here because this transition is not tied to the job queue
+	    fileGroup->busy = false;
+	    self->busy = false;  
+	    continue;
+	}
+
+	// pmDarkCombine
+	if (!strcmp (job->type, "PPMERGE_DARK_COMBINE")) {
+	    psAssert (job->args->n == 7, "invalid number of arguments to pmDarkCombine");
+
+	    pmCell *outCell             = job->args->data[0];
+	    ppMergeFileGroup *fileGroup = job->args->data[1];
+	    psArray *darkOrdinates      = job->args->data[2];
+	    psString darkNorm           = job->args->data[3];
+	    psScalar *iter     	        = job->args->data[4];
+	    psScalar *rej     	        = job->args->data[5];
+	    psScalar *maskVal     	= job->args->data[6];
+
+	    bool status = pmDarkCombine(outCell, fileGroup->readouts, darkOrdinates, darkNorm, iter->data.S32, rej->data.F32, maskVal->data.U8);
+	    if (!status) {
+		self->fault = true;
+	    }
+	    // we do not have to lock here because this transition is not tied to the job queue
+	    fileGroup->busy = false;
+	    self->busy = false;  
+	    continue;
+	}
+
+	// pmShutterCorrectionGenerate
+	if (!strcmp (job->type, "PPMERGE_SHUTTER_CORRECTION")) {
+	    psAssert (job->args->n == 7, "invalid number of arguments to pmDarkCombine");
+
+	    pmReadout *output             = job->args->data[0];
+	    ppMergeFileGroup *fileGroup   = job->args->data[1];
+	    psScalar *shutterRef          = job->args->data[2];
+	    pmShutterCorrectionData *data = job->args->data[3];
+	    psScalar *iter     	          = job->args->data[4];
+	    psScalar *rej     	          = job->args->data[5];
+	    psScalar *maskVal     	  = job->args->data[6];
+
+	    bool status = pmShutterCorrectionGenerate(output, NULL, fileGroup->readouts, shutterRef->data.F32, data, iter->data.S32, rej->data.F32, maskVal->data.U8);
+	    if (!status) {
+		self->fault = true;
+	    }
+	    // we do not have to lock here because this transition is not tied to the job queue
+	    fileGroup->busy = false;
+	    self->busy = false;  
+	    continue;
+	}
+    }  
+}
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeVersion.c	(revision 22322)
@@ -0,0 +1,60 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppMergeVersion.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString ppMergeVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString ppMergeVersionLong(void)
+{
+    psString version = ppMergeVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
+
+void ppMergeVersionMetadata(psMetadata *metadata)
+{
+    PS_ASSERT_METADATA_NON_NULL(metadata,);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppStats = ppStatsVersionLong(); // ppStats version
+    psString ppMerge = ppMergeVersionLong(); // ppMerge version
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString head = NULL;               // Head string
+    psStringAppend(&head, "ppMerge processing at %s. Component information:", timeString);
+    psFree(timeString);
+
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppStats, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppMerge, "");
+
+    psFree(head);
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(ppStats);
+    psFree(ppMerge);
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeVersion.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeVersion.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppMerge/src/ppMergeVersion.h	(revision 22322)
@@ -0,0 +1,14 @@
+#ifndef PP_MERGE_VERSION_H
+#define PP_MERGE_VERSION_H
+
+/// Return short version information
+psString ppMergeVersion(void);
+
+/// Return long version information
+psString ppMergeVersionLong(void);
+
+/// Update the metadata with version information for all dependencies
+void ppMergeVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppNorm/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppNorm/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppNorm/.cvsignore	(revision 22322)
@@ -0,0 +1,16 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+missing
+test
+config.guess
+config.sub
+libtool
+ltmain.sh
Index: /tags/sj_tags/sj_root_20080929/ppNorm/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppNorm/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppNorm/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/ppNorm/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppNorm/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppNorm/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppNorm
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ppNorm/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppNorm/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppNorm/configure.ac	(revision 22322)
@@ -0,0 +1,29 @@
+AC_PREREQ(2.61)
+
+AC_INIT([ppNorm], [1.1.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ppNorm/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppNorm/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppNorm/src/.cvsignore	(revision 22322)
@@ -0,0 +1,8 @@
+Makefile
+Makefile.in
+ppNormCalc
+.deps
+config.h
+config.h.in
+stamp-h1
+.libs
Index: /tags/sj_tags/sj_root_20080929/ppNorm/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppNorm/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppNorm/src/Makefile.am	(revision 22322)
@@ -0,0 +1,17 @@
+bin_PROGRAMS = ppNormCalc
+
+ppNormCalc_CFLAGS = $(PPNORM_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+ppNormCalc_LDFLAGS = $(PPNORM_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+
+ppNormCalc_SOURCES =		\
+	ppNormCalc.c
+
+noinst_HEADERS =
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormCalc.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormCalc.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormCalc.c	(revision 22322)
@@ -0,0 +1,155 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+void helpAndDie(const char *programName)
+{
+    printf("Calculate normalisation for flat fields.\n\n"
+           "Usage: %s [IN.mdc [OUT.mdc]]\n\n"
+           "where IN.mdc is a metadata config file containing the background\n"
+           "      value for each component of each exposure;\n"
+           "and   OUT.mdc is a metadata config file containing the output\n"
+           "      normalisation factors.\n"
+           "\n", programName);
+    exit(EXIT_FAILURE);
+}
+
+
+int main(int argc, char *argv[])
+{
+    psLibInit(NULL);
+    pmConfig *config = pmConfigRead(&argc, argv, NULL);
+    psFree(config);
+
+    if (argc > 3 ||
+        psArgumentGet(argc, argv, "-h") ||
+        psArgumentGet(argc, argv, "-help") ||
+        psArgumentGet(argc, argv, "--help") ||
+        psArgumentGet(argc, argv, "-?")) {
+        helpAndDie(argv[0]);
+    }
+
+    FILE *inFile = stdin;               // Input file stream
+    FILE *outFile = stdout;             // Output file stream
+
+    if (argc >= 2) {
+        psString resolved = pmConfigConvertFilename(argv[1], config, false, false); // Resolved filename
+        inFile = fopen(resolved, "r");
+        if (!inFile) {
+            psError(PS_ERR_IO, true, "Unable to open input file: %s\n\n", resolved);
+            psFree(resolved);
+            helpAndDie(argv[0]);
+        }
+        psFree(resolved);
+    }
+    if (argc == 3) {
+        psString resolved = pmConfigConvertFilename(argv[2], config, true, true); // Resolved filename
+        outFile = fopen(resolved, "w");
+        if (!outFile) {
+            psError(PS_ERR_IO, true, "Unable to open output file: %s\n\n", resolved);
+            psFree(resolved);
+            helpAndDie(argv[0]);
+        }
+        psFree(resolved);
+    }
+
+    psString inputMDC = psSlurpFile(inFile); // Input metadata config stuff
+    if (argc >= 2) {
+        fclose(inFile);
+    }
+
+    psU32 badLines = 0;                   // Number of bad lines
+    psMetadata *exposures = psMetadataConfigParse(NULL, &badLines, inputMDC, false); // Exposure statistics
+    if (badLines > 0) {
+        psWarning("%d bad lines found when reading input\n", badLines);
+    }
+
+    // Get a list of all the components
+    psMetadata *components = psMetadataAlloc(); // Components of the exposures
+    psMetadataIterator *expIter = psMetadataIteratorAlloc(exposures, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *expItem;            // Item from iteration
+    while ((expItem = psMetadataGetAndIncrement(expIter))) {
+        if (expItem->type != PS_DATA_METADATA) {
+            psLogMsg("ppNormCalc", PS_LOG_WARN,
+                     "Metadata item %s is not of type METADATA --- ignored.\n",
+                     expItem->name);
+            continue;
+        }
+
+        // Inspect each component for this exposure; add it if we don't know about it
+        psMetadata *comps = expItem->data.V; // The components
+        psMetadataIterator *compsIter = psMetadataIteratorAlloc(comps, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *compsItem;      // Item from iteration
+        while ((compsItem = psMetadataGetAndIncrement(compsIter))) {
+            if (compsItem->type != PS_TYPE_F32) {
+                psLogMsg("ppNormCalc", PS_LOG_WARN,
+                         "Component %s within exposure %s is not of type F32 --- ignored.\n",
+                         compsItem->name, expItem->name);
+                continue;
+            }
+            if (!psMetadataLookup(components, compsItem->name)) {
+                psMetadataAddBool(components, PS_LIST_TAIL, compsItem->name, 0, NULL, true);
+            }
+        }
+        psFree(compsIter);
+    }
+
+    // Convert to a matrix
+    int numComps = psListLength(components->list); // Number of components
+    int numExps = psListLength(exposures->list); // Number of exposures
+    psImage *matrix = psImageAlloc(numComps, numExps, PS_TYPE_F32); // Matrix of backgrounds
+    psMetadataIteratorSet(expIter, PS_LIST_HEAD);
+    for (int expNum = 0; (expItem = psMetadataGetAndIncrement(expIter)); expNum++) {
+        if (expItem->type != PS_DATA_METADATA) {
+            continue;
+        }
+        psMetadata *comps = expItem->data.V; // The components
+        psMetadataIterator *compsIter = psMetadataIteratorAlloc(components, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *compsItem;      // Item from iteration
+        for (int compNum = 0; (compsItem = psMetadataGetAndIncrement(compsIter)); compNum++) {
+            matrix->data.F32[expNum][compNum] = psMetadataLookupF32(NULL, comps, compsItem->name);
+        }
+        psFree(compsIter);
+    }
+    psFree(expIter);
+
+    // Do the normalisation
+    psVector *gains = psVectorAlloc(numComps, PS_TYPE_F32); // Vector of gains for each component
+    psVectorInit(gains, 100.0);
+    if (!pmFlatNormalize(NULL, &gains, matrix)) {
+        psLogMsg("ppNormCalc", PS_LOG_ERROR, "Normalisation didn't converge.\n");
+        exit(EXIT_FAILURE);
+    }
+    psFree(matrix);
+
+
+    // Output a MDC format to stdout
+    psMetadata *outputMD = psMetadataAlloc(); // Output metadata
+    psMetadataIterator *compsIter = psMetadataIteratorAlloc(components, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *compsItem;          // Item from iteration
+    for (int compNum = 0; (compsItem = psMetadataGetAndIncrement(compsIter)); compNum++) {
+        psMetadataAddF32(outputMD, PS_LIST_TAIL, compsItem->name, 0, NULL, gains->data.F32[compNum]);
+    }
+    psFree(compsIter);
+    psString outputString = psMetadataConfigFormat(outputMD);
+    fprintf(outFile, "%s", outputString);
+    psFree(outputString);
+    psFree(outputMD);
+    if (argc == 3) {
+        fclose(outFile);
+    }
+
+    // Clean up
+    psFree(gains);
+    psFree(components);
+    psFree(exposures);
+
+    pmConfigDone();
+    psLibFinalize();
+
+    return EXIT_SUCCESS;
+}
Index: /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,26 @@
+/*
+ * The line
+    { PPNORM_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+#include "pslib.h"
+#include "ppNormErrorCodes.h"
+
+void ppNormErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { PPNORM_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PPNORM_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = PPNORM_ERR_NERROR - PPNORM_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       p_psMemSetPersistent(tmp, true);
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormErrorCodes.dat	(revision 22322)
@@ -0,0 +1,10 @@
+#
+# This file is used to generate ppNormErrorClasses.h
+#
+BASE = 600		First value we use; lower values belong to psLib
+# these errors correspond to standard exit conditions
+ARGUMENTS               Incorrect arguments
+SYS                     System error
+CONFIG                  Problem in configure files
+PROG                    Programming error
+DATA                    invalid data
Index: /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppNorm/src/ppNormErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,18 @@
+#if !defined(PPNORM_ERROR_CODES_H)
+#define PPNORM_ERROR_CODES_H
+/*
+ * The line
+ *  PPNORM_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    PPNORM_ERR_BASE = 512,
+    PPNORM_ERR_${ErrorCode},
+    PPNORM_ERR_NERROR
+} ppNormErrorCode;
+
+void ppNormErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppSim/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/.cvsignore	(revision 22322)
@@ -0,0 +1,15 @@
+aclocal.m4
+autom4te.cache
+config.guess
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libtool
+ltmain.sh
+Makefile
+Makefile.in
+missing
+test
Index: /tags/sj_tags/sj_root_20080929/ppSim/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/ppSim/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppConfigDump
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ppSim/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/configure.ac	(revision 22322)
@@ -0,0 +1,31 @@
+AC_PREREQ(2.61)
+
+AC_INIT([ppConfigDump], [1.1.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB],    [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+PKG_CHECK_MODULES([PSPHOT],   [psphot >= 0.8.0]) 
+PKG_CHECK_MODULES([PSASTRO],  [psastro >= 0.9.0])
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ppSim/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/notes.txt	(revision 22322)
@@ -0,0 +1,128 @@
+
+
+2008.05.17
+
+  pmFPAfiles used in ppSim:
+
+  PPSIM.INPUT : an input image loaded by ppSim; the simulated features
+  		may be added to this image.  In fact, the features are
+  		generated in the output image, and the two images are
+  		added (as images) together.
+
+  PPSIM.OUTPUT  : the output image.  this file is used for the main
+  		  ppSimLoop function
+
+  PPSIM.CHIP    : chip-mosaiced version of the output image.  only
+  		  generated if photometry is requested.  Is this
+  		  actually used as an output image?
+
+  PPSIM.REAL.SOURCES : real sources loaded by PPSIM.  these sources
+  		       are subtracted from the image before testing
+  		       for the detectability of the fake sources and
+  		       before measuring the flux of fake or forced
+  		       soures.  They should be the sources measured in
+  		       a previous psphot run, though any input list
+  		       could be used.  Results are very ill-defined if
+  		       the sources do not correspond to actual image
+  		       sources!
+
+  PSPHOT.PSF.LOAD : input PSF model used to measure the input sources
+
+  PPSIM.SOURCES : output fake sources (injected fake sources with real values) 
+  PPSIM.FAKE.SOURCES : output fake photometry (measured photometry for all fake sources)
+  PPSIM.FORCE.SOURCES : output force photometry
+  
+  ** used within psphot functions:
+  PSPHOT.BACKMDL : model background generated by psphotModelBackground
+  PSPHOT.BACKMDL.STDEV : model background error generated by psphotModelBackground
+  PSPHOT.BACKGND : full-scale model background generated by psphotSubtractBackground
+  PSPHOT.BACKSUB : background-subtracted image
+
+2008.05.15
+
+  For fake and force photometry, we need to load the known existing
+  sources, then perform a complete linear fit solution to the complete
+  set of both real (known) sources plus the fake and/or forced
+  photometry positions (as PSFs).  The fake and forced photometry
+  positions need to be measured independently.  This provides the real
+  photometry for these sources ** in the presence of the other real
+  sources **.  In order to test the detectability of the fake sources
+  (or the forced sources, for that matter), we need to generate the
+  smoothed detection image, and determine the peak value at the
+  positions of the sources.  
+
+  ppSimPhotomReadout outline:
+
+  * we have three source lists (loaded before the function is called):
+    * realSources : these are loaded from a CMF-style file (or equiv)
+    * fakeSources : these are defined internally, and need a link from
+		    the input fake source to the measured fake source
+    * forceSources : these are loaded from a DVO database via getstar
+
+  * we require a supplied PSF
+
+  * need to subtract the background (before or after subtracting the
+    sources?)
+
+  * subtract the real sources
+  * model the background
+  * build the detection image
+  * measure the detectability of the fake and force sources
+  * replace the real sources
+
+  * merge real + fake sources
+  * linear fit to merged source list
+  * replace the sources (make 'no-subtract' option?)
+
+  * merge real + forced sources
+  * linear fit to merged source list
+  * replace the sources (make 'no-subtract' option?)
+
+
+
+ppSim development work still needed:
+
+* save sources in output files
+* load input image
+
+* optionally apply detrend data (bias, dark, etc).  when adding data
+  to a loaded image, we only need to inject fake stars.
+
+* examine the range of star fluxes.  (too many are being generated).  
+* check the overlap between the real and random star fluxes
+
+* generate stars with / without known exposure correction (ie, stars
+  could be generated with known fluxes or know magnitudes)
+
+* generate galaxies (objects with random values for major, minor, theta)
+* generate convolved galaxies
+
+* should not have to supply ZP, Seeing, PA, Scale on command line for all ppSim runs
+* DVO.CATDIR path://SIMTEST is not being interpolated?
+* -D PSASTRO:DVO.CATDIR is not being interpretted?
+
+* fake stars do not have random magnitudes?
+* add generated stars to an output file (pmSource list?)
+* make star source an option (random / dvo database)
+* add the gain to the metadata
+
+ppSimLoop:
+
+  * load catalogs stars (ra, dec, mag, x, y)
+  * generate random stars (ra, dec, mag, x, y)
+  * loop over chip,cell,readout
+  ** generate output images (signal, variance)
+  ** add the bias
+  ** add the dark
+  ** add the sky signal 
+  ** add the stars (modified by the flat, shutter)
+  ** add the galaxies (modified by the flat, shutter)
+  ** add the poisson noise
+  ** add the overscan + readnoise
+  ** save the stars
+  ** save the galaxies
+
+  ** update the concept info
+  ** update the header wcs info
+  ** write out the image data
+
Index: /tags/sj_tags/sj_root_20080929/ppSim/psf.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/psf.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/psf.txt	(revision 22322)
@@ -0,0 +1,59 @@
+
+Code to load a psf model and construct psf images.  This assumes there
+is an analysis loop which is performing pmFPAfileIOChecks at the
+appropriate levels. 
+
+** to supply a psf model from the command, define the command-line arguments (ppSimArguments.c)
+
+   pmConfigFileSetsMD (config->arguments, &argc, argv, "PSPHOT.PSF", "-psf", "-psflist");
+
+** if a psf model is supplied, generate a pmFPAfile to carry it. bind
+   it to the pmFPAfile in your main loop (ppSimCreate.c):
+
+    // have we supplied a psf model?
+    if (psMetadataLookupPtr(NULL, config->arguments, "PSPHOT.PSF")) {
+	bool status = false;
+
+	// tie the psf file to the chipMosaic 
+	// file is the pmFPAfile used for the analysis loop
+        pmFPAfileBindFromArgs(&status, file, config, "PSPHOT.PSF.LOAD", "PSPHOT.PSF");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to find/build PSPHOT.PSF.LOAD");
+	    // XXX free things to be freed...
+            return NULL;
+        }
+    }
+
+** generate source models from the psf and insert into a readout:
+
+   // load the psf model from the corresponding chip.  note: you may
+   // need to select the chip from the current readout, eg: (ppSimInsertSources.c)
+   // pmCell *cell = readout->parent;
+   // pmChip *chip = cell->parent;
+   pmPSF *psf = psMetadataLookupPtr (&mdok, chip->analysis, "PSPHOT.PSF");
+
+
+   // instantiate a model for the PSF at this location, set desired flux
+
+   // note that pmModelFromPSFforXY takes the chip coordinate.  you many
+   // need to convert the x,y coordinate in the readout to/from the x,y
+   // coordinate. (ppSimInsertSources.c)
+   pmModel *model = pmModelFromPSFforXY (psf, xChip, yChip, 1.0);
+   pmModelSetFlux (model, flux);
+
+   // define the radius of valid pixels
+   float radius = model->modelRadius (model->params, noise);
+   radius = PS_MAX (radius, 1.0);
+
+   // construct a source, with model flux pixels set, based on the model
+   pmSource *source = pmSourceFromModel (model, readout, radius, PM_SOURCE_TYPE_STAR);
+
+   // insert the source flux in the image.  dX,dY is the coordinate of
+   // the readout 0,0 pixel in the chip frame.
+   pmSourceAddWithOffset (source, PM_MODEL_OP_FULL, 0xff, dX, dY);
+
+** given a pmSource, generate the its cached model flux:
+
+   // this fills in the pixels in the image source->modelFlux
+   pmSourceCacheModel (source, maskVal);
+
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+config.h
+config.h.in
+Makefile
+Makefile.in
+ppSim
+stamp-h1
+.deps
+.libs
+ppSimSequence
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/Makefile.am	(revision 22322)
@@ -0,0 +1,59 @@
+bin_PROGRAMS = ppSim ppSimSequence
+
+ppSim_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PSASTRO_CFLAGS) $(ppSim_CFLAGS)
+ppSim_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS) $(PSPHOT_LIBS) $(PSASTRO_LIBS)
+ppSim_SOURCES = \
+	ppSim.c			  \
+	ppSimArguments.c	  \
+	ppSimCreate.c		  \
+	ppSimLoadStars.c          \
+	ppSimMakeStars.c          \
+	ppSimMakeGalaxies.c       \
+	ppSimMakeBiassec.c        \
+	ppSimMakeBias.c           \
+	ppSimMakeDark.c           \
+	ppSimMakeSky.c            \
+	ppSimInsertStars.c        \
+	ppSimInsertGalaxies.c     \
+	ppSimAddOverscan.c        \
+	ppSimAddNoise.c           \
+	ppSimSaturate.c           \
+	ppSimBounds.c             \
+	ppSimStars.c              \
+	ppSimSetPSF.c             \
+	ppSimUtils.c              \
+	ppSimLoop.c		  \
+	ppSimLoadSpots.c	  \
+	ppSimPhotom.c		  \
+	ppSimPhotomReadoutFake.c  \
+	ppSimPhotomReadoutForce.c \
+	ppSimPhotomFiles.c	  \
+	ppSimLoadForceSources.c	  \
+	ppSimMergeReadouts.c	  \
+	ppSimDetections.c	  \
+	ppSimMergeSources.c	  \
+	ppSimMosaicChip.c	  \
+	ppSimRandomGaussian.c	  \
+	ppSimBadPixels.c
+
+ppSimSequence_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSASTRO_CFLAGS) $(ppSim_CFLAGS)
+ppSimSequence_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS) $(PSASTRO_LIBS)
+ppSimSequence_SOURCES = \
+	ppSimSequence.c		\
+	ppSimSequenceBias.c	\
+	ppSimSequenceDark.c	\
+	ppSimSequenceFlat.c	\
+	ppSimSequenceObject.c
+
+noinst_HEADERS = \
+	ppSim.h \
+	ppSimSequence.h
+
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSim.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSim.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSim.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "ppSim.h"
+
+int failure (pmConfig *config, psExit status, char *message) { 
+    ppSimRandomGaussianFree ();
+    psErrorStackPrint(stderr, message);
+    psFree(config);
+    pmModelClassCleanup();
+    psLibFinalize();
+    exit (status);
+}
+
+int main(int argc, char *argv[])
+{
+    psLibInit(NULL);
+    if (!pmModelClassInit ()) abort();
+
+    pmConfig *config = pmConfigRead(&argc, argv, PPSIM_RECIPE); // Configuration
+    if (!config) {
+	failure (config, PS_EXIT_CONFIG_ERROR, "Unable to read configurations.");
+    }
+
+    if (!ppSimArguments(argc, argv, config)) {
+	failure (config, PS_EXIT_CONFIG_ERROR, "Error parsing command-line arguments");
+    }
+
+    if (!ppSimCreate(config)) {
+	failure (config, PS_EXIT_CONFIG_ERROR, "Unable to create output file.");
+    }
+
+    if (!ppSimLoop(config)) {
+	failure (config, PS_EXIT_SYS_ERROR, "Unable to generate data.");
+    }
+
+    ppSimRandomGaussianFree ();
+    psFree(config);
+    pmModelClassCleanup();
+    pmConfigDone();
+    pmConceptsDone();
+    psLibFinalize();
+
+    exit (PS_EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSim.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSim.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSim.h	(revision 22322)
@@ -0,0 +1,201 @@
+#ifndef PP_SIM_H
+#define PP_SIM_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>   // for unlink
+#include <pslib.h>
+#include <psmodules.h>
+#include <psastro.h>
+#include <psphot.h>
+
+#define PPSIM_RECIPE "PPSIM"
+// #define OUTPUT_FILE "PPSIM.OUTPUT"
+
+// Compare a value with minimum and maximum values, replacing where required.
+#define COMPARE(VALUE,MIN,MAX) {		\
+        if (VALUE < MIN) { MIN = VALUE; }	\
+        if (VALUE > MAX) { MAX = VALUE; }	\
+    }
+
+// Return cell position, given an FPA position; calculations are all done in pixel units
+#define PPSIM_FPA_TO_CELL(pos, cell0, cellParity, binning, chip0, chipParity) \
+    (((pos) - (chip0))*(chipParity) - (cell0))*(cellParity) / (binning)
+
+
+// Return FPA position, given a cell position; calculations are all done in pixel units
+#define PPSIM_CELL_TO_FPA(pos, cell0, cellParity, binning, chip0, chipParity) \
+    ((chip0) + (binning)*(chipParity)*((cell0) + (cellParity)*(pos)))
+
+// Type of image to simulate
+typedef enum {
+    PPSIM_TYPE_NONE,                    // No type set
+    PPSIM_TYPE_BIAS,                    // Bias image
+    PPSIM_TYPE_DARK,                    // Dark image
+    PPSIM_TYPE_FLAT,                    // Flat-field image
+    PPSIM_TYPE_OBJECT                   // Object image
+} ppSimType;
+
+typedef struct {
+    double ra;
+    double dec;
+    float mag;
+    float x;
+    float y;
+    float flux;
+    float peak;
+} ppSimStar;
+
+typedef struct {
+    double ra;
+    double dec;
+    float mag;
+    float x;
+    float y;
+    float flux;
+
+    float peak;
+    float Rmaj;
+    float Rmin;
+    float theta;
+    float index;
+} ppSimGalaxy;
+
+ppSimStar *ppSimStarAlloc ();
+ppSimGalaxy *ppSimGalaxyAlloc ();
+
+/// Parse command-line arguments
+bool ppSimArguments(int argc, char **argv, ///< Command-line arguments
+                    pmConfig *config ///< Configuration
+    );
+
+/// Create output file
+///
+/// Returns a borrowed pointer to the FPA file.
+pmFPAfile *ppSimCreate(pmConfig *config ///< Configuration
+    );
+
+// Return bounds of a chip, based on the concepts
+psRegion *ppSimChipBounds(const pmChip *chip, // Chip for which to determine size
+                          pmFPAview *view // View for chip
+    );
+
+// Return bounds of an FPA, based on the concepts
+psRegion *ppSimFPABounds(const pmFPA *fpa       // FPA for which to determine size
+    );
+
+/// Loop over the output file, generating simulated data
+bool ppSimLoop(pmConfig *config ///< Configuration
+    );
+
+psVector *ppSimMakeBiassec (pmCell *cell, pmConfig *config);
+psVector *ppSimMakeBias (bool *status, pmReadout *readout, pmConfig *config, const psRandom *rng) ;
+bool ppSimMakeDark (pmReadout *readout, pmConfig *config);
+bool ppSimMakeSky (pmReadout *readout, psImage *expCorr, ppSimType type, pmConfig *config);
+
+bool ppSimLoadSpots (pmFPA *fpa, pmConfig *config);
+
+bool ppSimLoadStars (psArray *stars, pmFPA *fpa, pmConfig *config);
+bool ppSimMakeStars(psArray *stars, pmFPA *fpa, pmConfig *config, const psRandom *rng);
+bool ppSimInsertStars (pmReadout *readout, psImage *expCorr, psArray *stars, pmConfig *config);
+
+bool ppSimInitHeader(pmConfig *config,
+                     pmFPA *fpa,
+                     pmChip *chip,
+                     pmCell *cell);
+
+bool ppSimSaturate(pmReadout *readout,  // Image to apply saturation
+                   const pmConfig *config); // Saturation level
+
+bool ppSimUpdateConceptsFPA (pmFPA *fpa, pmConfig *config);
+bool ppSimUpdateConceptsCell (pmCell *cell, pmConfig *config);
+
+bool ppSimAddOverscan (pmReadout *readout, pmConfig *config, psVector *biasCols, psVector *biasRows, psRandom *rng);
+
+bool ppSimAddNoise(psImage *signal,
+                   psImage *variance,
+                   const pmCell *cell,
+                   const pmConfig *config,
+                   const psRandom *rng // Random number generator
+    );
+
+bool ppSimSetPSF (pmChip *chip, pmConfig *config);
+
+bool ppSimMakeGalaxies(psArray *galaxies, pmFPA *fpa, pmConfig *config, const psRandom *rng);
+bool ppSimInsertGalaxies (pmReadout *readout, psImage *expCorr, psArray *galaxies, pmConfig *config);
+
+bool ppSimMosaicChip(pmConfig *config, const psMaskType blankMask, const pmFPAview *view,
+		     const char *outFile, const char *inFile);
+
+bool ppSimPhotom (pmConfig *config, pmFPAview *view);
+
+
+/// Add bad pixels to an image
+bool ppSimBadPixels(pmReadout *readout, ///< Readout for which to generate bad pixels
+                    const pmConfig *config, ///< Configuration
+                    psRandom *rng       ///< Random number generator
+    );
+
+float ppSimStarSkyNoise (float skySigma, float seeingSigma);
+float ppSimStarPeakToFlux (float peak, float seeingSigma);
+float ppSimStarFluxToPeak (float flux, float seeingSigma);
+float ppSimFluxToMag (float flux, float zp);
+float ppSimMagToFlux (float mag, float zp);
+
+float ppSimArgToRecipeF32(bool *status, 
+			  psMetadata *options,    // Target to which to add value
+			  const char *recipeName, // Name for value in the recipe
+			  psMetadata *arguments,  // Command-line arguments
+			  const char *argName	 // Argument name in the command-line arguments
+    );
+
+int ppSimArgToRecipeS32(bool *status,
+			psMetadata *options,    // Target to which to add value
+			const char *recipeName, // Name for value in the recipe
+			psMetadata *arguments,  // Command-line arguments
+			const char *argName	 // Argument name in the command-line arguments
+    );
+
+char *ppSimArgToRecipeStr(bool *status,
+			  psMetadata *options,    // Target to which to add value
+			  const char *recipeName, // Name for value in the recipe
+			  psMetadata *arguments,  // Command-line arguments
+			  const char *argName	 // Argument name in the command-line arguments
+    );
+
+bool ppSimArgToRecipeBool(bool *status,
+			  psMetadata *options,    // Target to which to add value
+			  const char *recipeName, // Name for value in the recipe
+			  psMetadata *arguments,  // Command-line arguments
+			  const char *argName	    // Argument name in the command-line arguments
+    );
+
+ppSimType ppSimTypeFromString (char *typeStr);
+char *ppSimTypeToString (ppSimType type);
+
+float ppSimGetZeroPoint (psMetadata *recipe, char *filter);
+
+bool ppSimMergeReadouts (pmConfig *config, pmFPAview *view);
+
+double ppSimRandomGaussian (const psRandom *rnd, double mean, double sigma);
+double ppSimRandomGaussianNorm (const psRandom *rnd);
+void ppSimRandomGaussianFree();
+
+bool ppSimPhotomFiles (pmConfig *config, pmFPAfile *fakeFile, pmFPAfile *forceFile);
+
+bool ppSimPhotomReadoutFake(pmConfig *config, const pmFPAview *view);
+bool ppSimPhotomReadoutForce(pmConfig *config, const pmFPAview *view);
+
+psArray *ppSimLoadForceSources(pmConfig *config, const pmFPAview *view);
+bool ppSimDetections (psImage *significance, psMetadata *recipe, psArray *sources);
+psArray *ppSimMergeSources (psArray *in1, psArray *in2);
+
+psArray *ppSimSelectSources (pmConfig *config, const pmFPAview *view, const char *filename);
+bool ppSimDefinePixels (psArray *sources, pmReadout *readout, psMetadata *recipe);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimAddNoise.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimAddNoise.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimAddNoise.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "ppSim.h"
+
+// Add noise to an image
+bool ppSimAddNoise(psImage *signal, // Signal image, modified and returned
+		   psImage *variance,
+		   const pmCell *cell,
+		   const pmConfig *config,		       
+		   const psRandom *rng // Random number generator
+    )
+{
+    assert(signal->type.type == PS_TYPE_F32);
+    assert(signal->numCols == variance->numCols && signal->numRows == variance->numRows);
+
+    bool mdok;
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
+
+    float gain = psMetadataLookupF32(NULL, cell->concepts, "CELL.GAIN"); // CCD gain, e/ADU
+    if (isnan(gain)) {
+	psWarning("CELL.GAIN is not set; reverting to recipe value GAIN.");
+	gain = psMetadataLookupF32(&mdok, recipe, "GAIN");
+	if (!mdok) {
+	    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find GAIN in recipe.");
+	    return false;
+	}
+    }
+
+    // Add the noise into the image
+    for (int y = 0; y < signal->numRows; y++) {
+        for (int x = 0; x < signal->numCols; x++) {
+	    // XXX is psRandomGaussian doing this reasonally optimally?
+	    // (generate a static array with the cumulative distribution, use the
+	    // random number to select a bin from the histogram)
+            signal->data.F32[y][x] += sqrtf(variance->data.F32[y][x]) * ppSimRandomGaussianNorm(rng);
+            signal->data.F32[y][x] /= gain; // Converting to ADU
+        }
+    }
+
+    // XXX why return this??
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimAddOverscan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimAddOverscan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimAddOverscan.c	(revision 22322)
@@ -0,0 +1,42 @@
+#include "ppSim.h"
+
+bool ppSimAddOverscan (pmReadout *readout, pmConfig *config, psVector *biasCols, psVector *biasRows, psRandom *rng) {
+
+    bool mdok;
+
+    // skip this step if we did not generate a bias level
+    if (biasCols == NULL) return true;
+    if (biasRows == NULL) return true;
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
+
+    float readnoise = psMetadataLookupF32(NULL, readout->parent->concepts, "CELL.READNOISE");// CCD read noise, e
+    if (isnan(readnoise)) {
+	psWarning("CELL.READNOISE is not set; reverting to recipe value READNOISE.");
+	readnoise = psMetadataLookupF32(&mdok, recipe, "READNOISE");
+	if (!mdok) {
+	    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find READNOISE in recipe.");
+	    return NULL;
+	}
+    }
+
+    // Add overscan
+    // XXX put this in a wrapper
+    for (int j = 0; j < biasCols->n; j++) {
+	psImage *signal = psImageAlloc(biasCols->data.S32[j], biasRows->n, PS_TYPE_F32); // Overscan
+	for (int y = 0; y < signal->numRows; y++) {
+	    for (int x = 0; x < signal->numCols; x++) {
+		signal->data.F32[y][x] = biasRows->data.F32[y];
+	    }
+	}
+	psImage *variance = psImageAlloc(biasCols->data.S32[j], biasRows->n, PS_TYPE_F32); // Variance
+	psImageInit(variance, PS_SQR(readnoise));
+
+	ppSimAddNoise(signal, variance, readout->parent, config, rng);
+	psListAdd(readout->bias, PS_LIST_TAIL, signal);
+
+	psFree(variance);
+	psFree(signal);     // Drop reference
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimArguments.c	(revision 22322)
@@ -0,0 +1,229 @@
+# include "ppSim.h"
+
+// Print usage information and die
+static void usage(const char *program,  // Name of the program
+                  psMetadata *arguments, // Command-line arguments
+                  pmConfig *config      // Configuration
+    )
+{
+    fprintf(stderr, "\nPan-STARRS data simulator\n\n");
+    fprintf(stderr, "Usage: %s -camera CAMERA_NAME (output)\n", program);
+    fprintf(stderr, "\n");
+    psArgumentHelp(arguments);
+    psFree(arguments);
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+    exit(PS_EXIT_CONFIG_ERROR);
+}
+
+// this function supplements the RECIPE:OPTIONS folder with command-line options
+bool ppSimArguments(int argc, char **argv, pmConfig *config)
+{
+    bool status;
+
+    assert(config);
+
+    // save the following command-line options in the arguments structure.  these will later be
+    // parsed and moved to the config->recipes:PPSIM_RECIPE folder
+
+    psMetadata *arguments = psMetadataAlloc(); // Command-line arguments
+    psMetadataAddStr(arguments,  PS_LIST_TAIL, "-format", 0, "Camera format name", NULL);
+    psMetadataAddStr(arguments,  PS_LIST_TAIL, "-type", 0, "Exposure type (BIAS|DARK|FLAT|OBJECT)", NULL);
+    psMetadataAddStr(arguments,  PS_LIST_TAIL, "-filter", 0, "Filter name", NULL);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-exptime", 0, "Exposure time (s)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-biaslevel", 0, "Bias level (e)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-biasrange", 0, "Bias range (e)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-darkrate", 0, "Dark rate (e/s)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-flatsigma", 0, "Flat sigma", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-flatrate", 0, "Flat rate (e/s)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-shuttertime", 0, "Shutter time (s)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-skyrate", 0, "Sky rate (e/s)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-skymags", 0, "Sky brightness in mags / square arcsec", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-ra", 0, "RA (degrees)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-dec", 0, "Dec (degrees)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-pa", 0, "Position angle (degrees)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-scale", 0, "Plate scale (arcsec/pixel)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-zp", 0, "Photometric zero point", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-seeing", 0, "Seeing FWHM (arcsec)", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-starslum", 0, "Fake star luminosity function slope", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-starsmag", 0, "Brightest magnitude for fake stars", NAN);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-starsdensity", 0, "Density of fake stars at magnitude", NAN);
+    psMetadataAddStr(arguments,  PS_LIST_TAIL, "-psfclass", 0, "Type of PSF model", NULL);
+    psMetadataAddStr(arguments,  PS_LIST_TAIL, "-galmodel", 0, "Type of Galaxy model", NULL);
+    psMetadataAddS32(arguments,  PS_LIST_TAIL, "-bin", 0, "Binning in x and y", 1);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "+photom", 0, "Perform photometry on fake sources", false);
+
+    if (psArgumentGet (argc, argv, "-h")) { usage(argv[0], arguments, config); }
+    if (psArgumentGet (argc, argv, "--h")) { usage(argv[0], arguments, config); }
+    if (psArgumentGet (argc, argv, "--help")) { usage(argv[0], arguments, config); }
+
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "PSPHOT.PSF", "-psf", "-psflist");
+    if (psErrorCodeLast () != PS_ERR_NONE) { psAbort ("problem with -psf or -psflist option"); }
+
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT.SOURCES", "-cmf", "-cmflist");
+    if (psErrorCodeLast () != PS_ERR_NONE) { psAbort ("problem with -cmf or -cmflist option"); }
+
+    // Only one of -camera and -file is needed or allowed.  The -camera option would have been
+    // already parsed by pmConfigRead in ppSim.c and resulted in a value for config->camera
+    bool loadImage = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-input", "-inputlist");
+    if (!config->camera && !loadImage) {
+	psError(PS_ERR_IO, true, "A camera name (-camera NAME) or an input image (-input NAME) must be specified");
+        psFree(arguments);
+	return false;
+    }
+    if (config->camera && loadImage) {
+        psError(PS_ERR_IO, true, "Only one of (-camera NAME) and (-file NAME) may be specified");
+        psFree(arguments);
+	return false;
+    }
+
+    if (!psArgumentParse(arguments, &argc, argv)) { 
+        psError(PS_ERR_IO, false, "error in command-line arguments");
+        psFree(arguments);
+	return false;
+    }
+
+    if (argc != 2) { 
+	psError(PS_ERR_IO, true, "Missing output filename");
+        psFree(arguments);
+	return false;
+    }
+
+    // output filename
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
+
+    // save the additional recipe values based on command-line options. These options override
+    // the PPSIM recipe values loaded from recipe files
+    psMetadata *options = pmConfigRecipeOptions (config, PPSIM_RECIPE);
+    if (!options) {
+        psError(PS_ERR_IO, false, "Unable to find recipe options for %s", PPSIM_RECIPE);
+        psFree(arguments);
+	return false;
+    }
+
+    // these arguments may be used whether the input image is created or loaded
+    ppSimArgToRecipeF32(&status, options, "STARS.LUM",     arguments, "-starslum");
+    ppSimArgToRecipeF32(&status, options, "STARS.MAG",     arguments, "-starsmag");
+    ppSimArgToRecipeF32(&status, options, "STARS.DENSITY", arguments, "-starsdensity");
+    ppSimArgToRecipeBool(&status, options, "PHOTOM",        arguments, "+photom");
+
+    // if we are loading the input image (not creating it), then we can skip the remaining arguments
+    if (loadImage) {
+	// if we are supplying an input image, it is so we may supply fake stars; force it to
+	// be treated as an OBJECT image. 
+	psMetadataAddStr(options, PS_LIST_TAIL, "IMAGE.TYPE", 0, "Exposure type", "OBJECT");
+
+	// check for these options as well
+        ppSimArgToRecipeStr(&status, options, "PSF.MODEL",    arguments, "-psfclass"); // PSF model class
+        ppSimArgToRecipeStr(&status, options, "GALAXY.MODEL", arguments, "-galmodel"); // Galaxy model name
+
+	// 'seeing' is not required: we can load a psf-model instead; but if not, it is allowed
+        ppSimArgToRecipeF32(&status, options, "SEEING", arguments, "-seeing"); // seeing (FWHM in arcsec)
+
+	// 'scale' is not required: we can use the WCS instead; but if not, it is allowed
+        ppSimArgToRecipeF32(&status, options, "PIXEL.SCALE", arguments, "-scale"); // Plate scale
+
+	// XXX we need to be more flexible: get this from the input image header or WCS
+        float ra0     = psMetadataLookupF32(NULL, arguments, "-ra"); // Right Ascension of boresight
+        float dec0    = psMetadataLookupF32(NULL, arguments, "-dec"); // Declination of boresight
+        psMetadataAddF32(options, PS_LIST_TAIL, "RA", 0, "Boresight RA (radians)", ra0 * M_PI / 180.0);
+        psMetadataAddF32(options, PS_LIST_TAIL, "DEC", 0, "Boresight Declination (radians)", dec0 * M_PI / 180.0);
+
+	psFree (arguments);
+	return true;
+    }
+
+    // apply an alternate camera format
+    psString formatName = psMetadataLookupStr(NULL, arguments, "-format"); // Name of format
+    if (formatName) {
+        // XXX delay the config below until ppSimCreate?
+        config->formatName = psMemIncrRefCounter(formatName);
+
+        psMetadata *formats = psMetadataLookupMetadata(NULL, config->camera, "FORMATS"); // The camera formats
+        if (!formats) {
+	    psError(PS_ERR_IO, false, "Unable to find FORMATS in camera configuration.");
+            psFree(arguments);
+	    return false;
+        }
+        psMetadata *format = psMetadataLookupMetadata(NULL, formats, formatName); // Format of interest
+        if (!format) {
+	    psError(PS_ERR_IO, false, "Unable to find format %s in camera FORMATS.", formatName);
+            psFree(arguments);
+	    return false;
+        }
+        config->format = psMemIncrRefCounter(format);
+    }
+
+    char *typeStr = ppSimArgToRecipeStr (&status, options, "IMAGE.TYPE", arguments, "-type"); // Requested exposure type
+    if (typeStr == NULL) {
+	psError(PS_ERR_IO, false, "An exposure type must be specified using -type");
+        psFree(arguments);
+	return false;
+    }
+    ppSimType type = ppSimTypeFromString (typeStr);
+
+    ppSimArgToRecipeStr(&status, options, "FILTER", arguments, "-filter"); // Filter name
+
+    // set the exposure time 
+    if (type == PPSIM_TYPE_BIAS) {
+	psMetadataAddF32(options, PS_LIST_TAIL, "EXPTIME", 0, "Exposure time (s)", 0.0);
+    } else {
+	ppSimArgToRecipeF32(&status, options, "EXPTIME", arguments, "-exptime");
+	if (!status) {
+	    psError(PS_ERR_IO, false, "The exposure time must be specified using -exptime");
+	    psFree(arguments);
+	    return false;
+        }
+    }
+
+    // these values all get moved from arguments to RECIPE:OPTIONS
+    ppSimArgToRecipeF32(&status,  options, "BIAS.LEVEL",    arguments, "-biaslevel");
+    ppSimArgToRecipeF32(&status,  options, "BIAS.RANGE",    arguments, "-biasrange");
+    ppSimArgToRecipeF32(&status,  options, "DARK.RATE",     arguments, "-darkrate");
+    ppSimArgToRecipeF32(&status,  options, "FLAT.SIGMA",    arguments, "-flatsigma");
+    ppSimArgToRecipeF32(&status,  options, "FLAT.RATE",     arguments, "-flatrate");
+    ppSimArgToRecipeF32(&status,  options, "SHUTTER.TIME",  arguments, "-shuttertime");
+    ppSimArgToRecipeF32(&status,  options, "SKY.RATE",      arguments, "-skyrate");
+    ppSimArgToRecipeS32(&status,  options, "BINNING",       arguments, "-bin");
+
+    if (type == PPSIM_TYPE_OBJECT) {
+        // Load values required for adding stars
+        float ra0     = psMetadataLookupF32(NULL, arguments, "-ra"); // Right Ascension of boresight
+        float dec0    = psMetadataLookupF32(NULL, arguments, "-dec"); // Declination of boresight
+        float pa      = psMetadataLookupF32(NULL, arguments, "-pa");  // Position angle
+        float seeing  = psMetadataLookupF32(NULL, arguments, "-seeing"); // seeing (FWHM in arcsec)
+
+	// XXX scale and zp should be supplied by the config file (allow override, but this is camera-dependent)
+        if (isnan(ra0) || isnan(dec0) || isnan(pa) || isnan(seeing)) {
+	    psError(PS_ERR_IO, false, "-ra, -dec, -pa, -scale, -zp, -seeing must be specified for OBJECT type");
+	    psFree(arguments);
+	    return false;
+        }
+
+        ppSimArgToRecipeF32(&status, options, "PIXEL.SCALE", arguments, "-scale"); // Plate scale (arcsec / pixel)
+        ppSimArgToRecipeF32(&status, options, "SKY.MAGS", arguments, "-skymags"); // Plate scale
+        ppSimArgToRecipeStr(&status, options, "PSF.MODEL",    arguments, "-psfclass"); // PSF model class
+        ppSimArgToRecipeStr(&status, options, "GALAXY.MODEL", arguments, "-galmodel"); // Galaxy model name
+
+	// the user supplies FWHM in arcsec; here we convert to Sigma in pixels
+        psMetadataAddF32(options, PS_LIST_TAIL, "SEEING", 0, "Seeing FWHM (arcsec)", seeing);
+        psMetadataAddF32(options, PS_LIST_TAIL, "RA", 0, "Boresight RA (radians)", ra0 * M_PI / 180.0);
+        psMetadataAddF32(options, PS_LIST_TAIL, "DEC", 0, "Boresight Declination (radians)", dec0 * M_PI / 180.0);
+        psMetadataAddF32(options, PS_LIST_TAIL, "PA", 0, "Boresight position angle (radians)",pa * M_PI / 180.0);
+
+        ppSimArgToRecipeF32(&status, options, "ZEROPOINT", arguments, "-zp"); // Zero point
+    }
+
+    psFree(arguments);
+    return true;
+}
+
+/* the following elements come from the config->arguments:
+   
+   PSPHOT.PSF
+   INPUT
+   OUTPUT
+
+   all other values should come from the recipe
+*/
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimBadPixels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimBadPixels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimBadPixels.c	(revision 22322)
@@ -0,0 +1,64 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppSim.h"
+
+
+// The idea is that the pattern should be completely *deterministic* (despite the use of 'random' numbers ---
+// with the seed specified, they should be deterministic) so that multiple calls of this function will result
+// in the same pattern.  These are set to (really) random values so that they do not dark-subtract or
+// flat-field.
+
+bool ppSimBadPixels(pmReadout *readout, const pmConfig *config, psRandom *rng)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_IMAGE_NON_NULL(readout->image, false);
+    PS_ASSERT_PTR_NON_NULL(readout->parent, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSIM_RECIPE);
+        return false;
+    }
+
+    psU64 seed = psMetadataLookupU64(&mdok, recipe, "BADPIX.SEED"); // Seed for RNG
+    if (seed == 0 || !mdok) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "BADPIX.SEED not set, or zero.");
+        return false;
+    }
+
+    float frac = psMetadataLookupF32(&mdok, recipe, "BADPIX.FRAC"); // Fraction of bad pixels
+    if (!mdok) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "BADPIX.FRAC not set.");
+        return false;
+    }
+    if (frac == 0.0) {
+        // Nothing to do
+        return true;
+    }
+
+    psRandom *pseudoRNG = psRandomAlloc(PS_RANDOM_TAUS, seed); // Pseudo-random number generator
+
+    psImage *image = readout->image;    // Image of interest
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            if (psRandomUniform(pseudoRNG) < frac) {
+                image->data.F32[y][x] *= psRandomGaussian(rng);
+            }
+        }
+    }
+
+    psFree(pseudoRNG);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimBounds.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimBounds.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimBounds.c	(revision 22322)
@@ -0,0 +1,98 @@
+#include "ppSim.h"
+
+// Return bounds of a chip, based on the concepts
+psRegion *ppSimChipBounds(const pmChip *chip, // Chip for which to determine size
+                            pmFPAview *view // View for chip
+    )
+{
+    assert(chip);
+    assert(view);
+
+    // Bounds of chip
+    int xMin = +INT_MAX;
+    int xMax = -INT_MAX;
+    int yMin = +INT_MAX;
+    int yMax = -INT_MAX;
+
+    int x0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.X0");
+    int y0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.Y0");
+    int xParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.XPARITY");
+    int yParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY");
+
+    if ((xParityChip != 1 && xParityChip != -1) || (yParityChip != 1 && yParityChip != -1)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Chip parities are not set.");
+        psFree(view);
+        return NULL;
+    }
+
+    pmCell *cell;                   // Cell from chip
+    while ((cell = pmFPAviewNextCell(view, chip->parent, 1))) {
+        int xSize = psMetadataLookupS32(NULL, cell->concepts, "CELL.XSIZE");
+        int ySize = psMetadataLookupS32(NULL, cell->concepts, "CELL.YSIZE");
+
+        if (xSize == 0 || ySize == 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cell sizes are not set.");
+            psFree(view);
+            return NULL;
+        }
+
+        int x0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.X0");
+        int y0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0");
+        int xParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY");
+        int yParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY");
+
+        if ((xParityCell != 1 && xParityCell != -1) || (yParityCell != 1 && yParityCell != -1)) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cell parities are not set.");
+            psFree(view);
+            return NULL;
+        }
+
+        // "Left", "Right", "Bottom" and "Top" don't take into account the parity
+        int cellLeft   = PPSIM_CELL_TO_FPA(0,     x0Cell, xParityCell, 1, x0Chip, xParityChip);
+        int cellRight  = PPSIM_CELL_TO_FPA(xSize, x0Cell, xParityCell, 1, x0Chip, xParityChip);
+        int cellBottom = PPSIM_CELL_TO_FPA(0,     y0Cell, yParityCell, 1, y0Chip, yParityChip);
+        int cellTop    = PPSIM_CELL_TO_FPA(ySize, y0Cell, yParityCell, 1, y0Chip, yParityChip);
+
+        COMPARE(cellLeft,   xMin, xMax);
+        COMPARE(cellRight,  xMin, xMax);
+        COMPARE(cellBottom, yMin, yMax);
+        COMPARE(cellTop,    yMin, yMax);
+    }
+
+    return psRegionAlloc(xMin, xMax, yMin, yMax);
+}
+
+// Return bounds of an FPA, based on the concepts
+psRegion *ppSimFPABounds(const pmFPA *fpa       // FPA for which to determine size
+    )
+{
+    assert(fpa);
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View for iterating over FPA
+
+    // Bounds of focal plane
+    int xMin = +INT_MAX;
+    int xMax = -INT_MAX;
+    int yMin = +INT_MAX;
+    int yMax = -INT_MAX;
+
+    pmChip *chip;                       // Chip from FPA
+    while ((chip = pmFPAviewNextChip(view, fpa, 1))) {
+        psRegion *bounds = ppSimChipBounds(chip, view); // Bounds for chip
+        COMPARE(bounds->x0, xMin, xMax);
+        COMPARE(bounds->x1, xMin, xMax);
+        COMPARE(bounds->y0, yMin, yMax);
+        COMPARE(bounds->y1, yMin, yMax);
+        psFree(bounds);
+    }
+
+    psFree(view);
+    psRegion *bounds = psRegionAlloc(xMin, xMax, yMin, yMax);
+
+    if (!bounds || (bounds->x0 == 0.0 && bounds->x1 == 0.0) || (bounds->y0 == 0.0 && bounds->y1 == 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine bounds of FPA");
+        return NULL;
+    }
+
+    return bounds;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimCreate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimCreate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimCreate.c	(revision 22322)
@@ -0,0 +1,231 @@
+# include "ppSim.h"
+
+pmFPAfile *ppSimCreate(pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    bool status;
+    pmFPA *fpa = NULL;
+
+    // the input image defines the camera.  if it is not supplied, the user must have
+    // supplied a camera and other metadata on the command line
+    pmFPAfile *input = pmFPAfileDefineFromArgs (&status, config, "PPSIM.INPUT", "INPUT");
+    if (!input) {
+	// if we have not specified the camera already, we need to interpolate the recipes associated with this camera, and read other command-line recipes
+	if (!pmConfigReadRecipes(config, PM_RECIPE_SOURCE_CL)) {
+	    psError(PS_ERR_IO, false, "Error merging recipes from camera config for %s", config->cameraName);
+	    return NULL;
+	}
+    } else {
+	// If an image is supplied, we still generate a fake image and merge them together downstream
+	// (otherwise, we get the variance wrong).
+	if (input->type != PM_FPA_FILE_IMAGE) {
+	    psError(PS_ERR_IO, true, "PPSIM.INPUT is not of type IMAGE");
+	    return NULL;
+	}
+    }
+
+    // generate the fpa structure used by the output camera (determined from INPUT or specified)
+    assert (config->camera);
+    fpa = pmFPAConstruct(config->camera, config->cameraName); // FPA to contain the observation
+    if (!fpa) {
+	psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to construct an FPA from camera configuration.");
+	return NULL;
+    }
+
+    // define the output image file -- this is the basis for the ppSimLoop
+    pmFPAfile *output = pmFPAfileDefineOutput(config, fpa, "PPSIM.OUTPUT");
+    if (!output) {
+	psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to create output file from PPSIM.OUTPUT. Did you forget to specify the format?");
+	return NULL;
+    }
+    if (output->type != PM_FPA_FILE_IMAGE) {
+	psError(PS_ERR_BAD_PARAMETER_TYPE, true, "PPSIM.OUTPUT type is not IMAGE");
+	psFree(fpa);
+	return NULL;
+    }
+    // XXX we should not require the output image to be written
+    output->save = true;
+
+    config->format = psMemIncrRefCounter (output->format);
+    config->formatName = psStringCopy (output->formatName);
+
+    // the recipe is now fully realized for the desired camera
+    psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, PPSIM_RECIPE); // Recipe
+
+    char *typeStr = psMetadataLookupStr(NULL, recipe, "IMAGE.TYPE"); // Type of image to simulate
+    ppSimType type = ppSimTypeFromString (typeStr); // Type of image to simulate
+
+    if (type == PPSIM_TYPE_OBJECT) {
+	// adjust the seeing by the scale
+	float seeing = psMetadataLookupF32(&status, recipe, "SEEING");
+	if (isnan(seeing)) {
+	    psError(PS_ERR_BAD_PARAMETER_TYPE, true, "seeing is not defined");
+	    psFree(fpa);
+	    return NULL;
+	}
+	float scale = psMetadataLookupF32(&status, recipe, "PIXEL.SCALE");
+	if (isnan(scale)) {
+	    psError(PS_ERR_BAD_PARAMETER_TYPE, true, "pixel scale is not defined");
+	    psFree(fpa);
+	    return NULL;
+	}
+	psMetadataAddF32(recipe, PS_LIST_TAIL, "SEEING", PS_META_REPLACE, "Seeing SIGMA (pixels)", seeing / 2.0 / sqrt(2.0 * log(2.0)) / scale);
+
+	// if we have been supplied an input image, but no ra & dec, use the input image values
+	if (input) {
+	    float ra = psMetadataLookupF32(&status, recipe, "RA");
+	    if (isnan(ra)) {
+		ra = psMetadataLookupF64(&status, input->fpa->concepts, "FPA.RA");
+		psMetadataAddF32(recipe, PS_LIST_TAIL, "RA", PS_META_REPLACE, "ra (radians)", ra);
+	    }
+	    float dec = psMetadataLookupF32(&status, recipe, "DEC");
+	    if (isnan(dec)) {
+		dec = psMetadataLookupF64(&status, input->fpa->concepts, "FPA.DEC");
+		psMetadataAddF32(recipe, PS_LIST_TAIL, "DEC", PS_META_REPLACE, "dec (radians)", dec);
+	    }
+	}
+    }
+
+    if ((type == PPSIM_TYPE_OBJECT) || (type == PPSIM_TYPE_FLAT)) {
+	// determine the zeropoint from the filter
+	float zp = psMetadataLookupF32(&status, recipe, "ZEROPOINT");
+	if (isnan(zp)) {
+	    char *filter = psMetadataLookupStr(&status, recipe, "FILTER");
+	    float zp = ppSimGetZeroPoint (recipe, filter);
+	    psMetadataAddF32(recipe, PS_LIST_TAIL, "ZEROPOINT", PS_META_REPLACE, "Photometric zeropoint", zp);
+	}
+    }
+
+    // For photometry, we operate on the chip-mosaicked image.  we create a copy of the mosaicked
+    // image for psphot so we can write out a clean image
+    bool doPhotom = psMetadataLookupBool(&status, recipe, "PHOTOM"); // Density of fakes
+    if (doPhotom) {
+
+	// XXX at the moment, we can perform photometry on the fake sources and the forced
+	// photometry positions, but not one or the other.  Also, we only support photometry in
+	// the context of an image previously analysed by psphot.  Add support for:
+	// * photometry of a pure fake image (requires peak detection and psf creation)
+	// * photometry of forced source positions without fake image (this might work, it just
+	// requires not generating any fake features).
+
+	if (!input) {
+	    psError(PS_ERR_UNKNOWN, false, "input image not found; currently required for photometry");
+	    return NULL;
+	}
+
+	// we need a chip image if we perform photometry (is it necessary to build it if we don't use it?)
+	pmFPAfile *fakeImage = pmFPAfileDefineChipMosaic(config, output->fpa, "PPSIM.FAKE.CHIP");
+	if (!fakeImage) {
+	    psError(PS_ERR_IO, false, _("Unable to generate new file from PPSIM.FAKE.CHIP"));
+	    psFree(fpa);
+	    return NULL;
+	}
+	if (fakeImage->type != PM_FPA_FILE_IMAGE) {
+	    psError(PS_ERR_IO, true, "PPSIM.FAKE.CHIP is not of type IMAGE");
+	    psFree(fpa);
+	    return NULL;
+	}
+
+	// we need a chip image if we perform photometry (is it necessary to build it if we don't use it?)
+	pmFPAfile *forceImage = NULL;
+	if (input) {
+	    forceImage = pmFPAfileDefineChipMosaic(config, input->fpa, "PPSIM.FORCE.CHIP");
+	    if (!forceImage) {
+		psError(PS_ERR_IO, false, _("Unable to generate new file from PPSIM.FORCE.CHIP"));
+		psFree(fpa);
+		return NULL;
+	    }
+	    if (forceImage->type != PM_FPA_FILE_IMAGE) {
+		psError(PS_ERR_IO, true, "PPSIM.FORCE.CHIP is not of type IMAGE");
+		psFree(fpa);
+		return NULL;
+	    }
+	}
+
+	// define associated psphot input/output files
+	if (!ppSimPhotomFiles (config, fakeImage, forceImage)) {
+	    psError(PSPHOT_ERR_CONFIG, false, "Trouble defining the additional input/output files for psphot");
+	    psFree(fpa);
+	    return NULL;
+	}
+    } else {
+	// have we supplied a psf model?  this happens in ppSimPhotomFiles if we request a photometry
+	// analysis.  however, even if we do not, a psf model may be used to generate the fake
+	// sources.
+	if (psMetadataLookupPtr(NULL, config->arguments, "PSPHOT.PSF")) {
+	    // tie the psf file to the chipMosaic 
+	    pmFPAfileBindFromArgs(&status, output, config, "PSPHOT.PSF.LOAD", "PSPHOT.PSF");
+	    if (!status) {
+		psError(PS_ERR_UNKNOWN, false, "Failed to find/build PSPHOT.PSF.LOAD");
+		psFree(fpa);
+		return NULL;
+	    }
+	}
+    }
+
+    // PPSIM.SOURCES carries the constructed, fake sources with their true parameters
+    // XXX only invoke this code for OBJECT types of images?
+    pmFPAfile *simSources = pmFPAfileDefineOutput (config, output->fpa, "PPSIM.SOURCES");
+    if (!simSources) {
+	psError(PS_ERR_UNKNOWN, false, "Cannot find a rule for PPSIM.SOURCES");
+	return false;
+    }
+    simSources->save = true;
+
+    // if we have loaded an input image, we derive certain values from the image, if possible
+    if (input) {
+	// we need to extract certain metadata from the image and populate the recipe.
+	// or else we need to set the fpa concepts based on the recipe options...
+
+	psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, PPSIM_RECIPE); // Recipe
+
+	ppSimArgToRecipeF32(&status, recipe, "EXPTIME", input->fpa->concepts, "FPA.EXPOSURE");
+	char *filter = ppSimArgToRecipeStr(&status, recipe, "FILTER", input->fpa->concepts, "FPA.FILTER");
+
+	float zp = ppSimGetZeroPoint (recipe, filter);
+	psMetadataAddF32(recipe, PS_LIST_TAIL, "ZEROPOINT", PS_META_REPLACE, "Photometric zeropoint", zp);
+    }
+
+    pmFPALevel phuLevel = pmFPAPHULevel(output->format); // Level at which PHU goes
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View for current level
+
+    if (phuLevel == PM_FPA_LEVEL_FPA) {
+	if (!pmFPAAddSourceFromView(fpa, "Simulation", view, output->format)) {
+	    psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
+	    psFree(fpa);
+	    psFree(view);
+	    return NULL;
+	}
+    }
+
+    pmChip *chip;                       // Chip from FPA
+    while ((chip = pmFPAviewNextChip(view, fpa, 1))) {
+	if (phuLevel == PM_FPA_LEVEL_CHIP) {
+	    if (!pmFPAAddSourceFromView(fpa, "Simulation", view, output->format)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
+		psFree(fpa);
+		psFree(view);
+		return NULL;
+	    }
+	}
+
+	pmCell *cell;                   // Cell from chip
+	while ((cell = pmFPAviewNextCell(view, fpa, 1))) {
+	    if (phuLevel == PM_FPA_LEVEL_CELL) {
+		if (!pmFPAAddSourceFromView(fpa, "Simulation", view, output->format)) {
+		    psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
+		    psFree(fpa);
+		    psFree(view);
+		    return NULL;
+		}
+	    }
+	}
+    }
+
+    psFree(fpa);
+    psFree(view);
+
+    return output;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimDetectionLimits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimDetectionLimits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimDetectionLimits.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "ppSim.h"
+
+// compare injected sources (PPSIM.SOURCES) and measured fake sources (PPSIM.FAKE.SOURCES) 
+// to determine detection limits and recovered magnitude errors
+bool ppSimDetectionLimits (pmConfig *config, pmFPAview *view) {
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // XXX do we need to ask if the analysis was performed to 1st or 2nd detection limit?
+    float NSIGMA_PEAK = psMetadataLookupF32 (&status, recipe, "PEAKS_NSIGMA_LIMIT_2");
+
+    // find the currently selected readout. 
+    // we always perform photometry on the mosaiced chip
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PPSIM.FAKE.CHIP");
+    PS_ASSERT_PTR_NON_NULL (readout, false);
+
+    psArray *injectedSources = psMetadataLookupPtr (NULL, readout->analysis, "PPSIM.SOURCES");
+    psArray *measuredSources = psMetadataLookupPtr (NULL, readout->analysis, "PPSIM.FAKE.SOURCES");
+    psAssert (injectedSources->n == measuredSources->n, "mis-match between injected and measured sources");
+  
+    psVector *mag    = psVectorAlloc (injectedSources->n, PS_TYPE_F32);
+    psVector *dmag   = psVectorAlloc (injectedSources->n, PS_TYPE_F32);
+    psVector *SN     = psVectorAlloc (injectedSources->n, PS_TYPE_F32);
+    psVector *detect = psVectorAlloc (injectedSources->n, PS_TYPE_BOOL);
+
+    // first measure, for each source, if it was detected (SN > limit)
+    // and the magnitude offset (dM = M_inject - M_measure)
+    for (int i = 0; i < injectedSources->n; i++) {
+      pmSource *injectSource = injectedSources->data[i];
+      pmSource *measureSource = measuredSources->data[i];
+
+      SN->data.F32[i] = measureSource->peak->SN;
+      dmag->data.F32[i] = injectSource->psfMag - measureSource->psfMag;
+      mag->data.F32[i] = injectSource->psfMag;
+      detect->data.Bool[i] = (measureSource->peak->SN >= NSIGMA_PEAK);
+    }
+
+    // generate a histogram consisting of the instrumental magnitude bin, the 
+    // XXX what is resolution? (user parameter?)
+
+  return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimDetections.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimDetections.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimDetections.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "ppSim.h"
+
+bool ppSimDetections (psImage *significance, psMetadata *recipe, psArray *sources) {
+    psAssert (sources, "programming error: ppSimDetections passed NULL sources");
+
+    bool status;
+
+    // for each source, measure the significance of the peak at the given coordinate
+    // where does this get stored?
+
+    // XXX need to get the effective Area from the PSF sigma
+    float SIGMA_SMTH  = psMetadataLookupF32 (&status, recipe, "SIGMA_SMOOTH"); 
+    psAssert (status, "SIGMA_SMOOTH missing: call psphotSignificanceImage first"); 
+    float effArea = 4.0*M_PI*PS_SQR(SIGMA_SMTH);
+
+    int row0 = significance->row0;
+    int col0 = significance->col0;
+
+    for (int i = 0; i < sources->n; i++) {
+
+	pmSource *source = sources->data[i];
+	pmPeak *peak = source->peak;
+	psAssert (peak, "peak is not defined for the source");
+
+	peak->value = significance->data.F32[peak->y-row0][peak->x-col0];
+	peak->SN = sqrt(peak->value*effArea);
+
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimInsertGalaxies.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimInsertGalaxies.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimInsertGalaxies.c	(revision 22322)
@@ -0,0 +1,189 @@
+# include "ppSim.h"
+static char *defaultModel = "PS_MODEL_SERSIC";
+
+bool ppSimInsertGalaxies (pmReadout *readout, psImage *expCorr, psArray *galaxies, pmConfig *config) {
+
+    bool mdok;
+
+    assert (readout);
+    assert (galaxies);
+    
+    if (!galaxies->n) { return true; }
+
+    pmCell *cell = readout->parent;
+    pmChip *chip = cell->parent;
+
+    // XXX this is an estimate of the sky noise based on the inputs to the image simulation.
+    // XXX update this to allow the estimate based on the measured sky background
+    // XXX this is missing the gain.
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
+
+    float expTime   = psMetadataLookupF32(NULL, recipe, "EXPTIME"); // Exposure time
+    float darkRate  = psMetadataLookupF32(NULL, recipe, "DARK.RATE"); // Dark rate
+
+    float readnoise = psMetadataLookupF32(NULL, cell->concepts, "CELL.READNOISE");// CCD read noise, e
+    if (isnan(readnoise)) {
+	psWarning("CELL.READNOISE is not set; reverting to recipe value READNOISE.");
+	readnoise = psMetadataLookupF32(&mdok, recipe, "READNOISE");
+	if (!mdok) {
+	    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find READNOISE in recipe.");
+	    return false;
+	}
+    }
+
+    float skyRate = psMetadataLookupF32(NULL, recipe, "SKY.RATE"); // Sky rate
+    if (isnan(skyRate)) {
+	float zp      = psMetadataLookupF32(&mdok, recipe, "ZEROPOINT"); assert (mdok);
+	float scale   = psMetadataLookupF32(&mdok, recipe, "PIXEL.SCALE"); assert (mdok);
+	float skyMags = psMetadataLookupF32(&mdok, recipe, "SKY.MAGS");  assert (mdok);
+	skyRate = scale * scale * ppSimMagToFlux (skyMags, zp);
+    }
+    
+    // Rough noise estimate, appropriate for entire cell (use for source radius?)
+    float roughNoise = sqrtf(PS_SQR(readnoise) + (darkRate + skyRate) * expTime);
+
+    int x0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.X0");
+    int y0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.Y0");
+    int xParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.XPARITY");
+    int yParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY");
+
+    int x0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.X0");
+    int y0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0");
+    int xParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY");
+    int yParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY");
+
+    int binning = psMetadataLookupS32(NULL, recipe, "BINNING"); // Binning in x and y
+
+    // determine the galaxy model
+    char *modelName = psMetadataLookupStr(&mdok, recipe, "GALAXY.MODEL"); // galaxy model name
+    if (modelName == NULL) {
+	modelName = defaultModel;
+    }
+    pmModelType type = pmModelClassGetType (modelName);
+    if (type == -1) {
+	psError (PS_ERR_UNKNOWN, false, "invalid model name");
+        return false;
+    }
+
+    int nParam = pmModelClassParameterCount (type);
+
+    pmPSF *psf = psMetadataLookupPtr (&mdok, chip->analysis, "PSPHOT.PSF");
+    assert (psf);
+
+    int dX = PM_CELL_TO_CHIP (0.0, x0Cell, xParityCell, binning);
+    int dY = PM_CELL_TO_CHIP (0.0, y0Cell, yParityCell, binning);
+
+    // psMetadataLookupPtr (readout->analysis, "PSPHOT.SOURCES", 0);
+
+    psArray *sources = psArrayAllocEmpty (galaxies->n);
+
+
+    // add sources to the readout image & weight
+    for (long i = 0; i < galaxies->n; i++) {
+	ppSimGalaxy *galaxy = galaxies->data[i];
+
+	// galaxy->x,y are in fpa coordinates
+
+	// Position on the cell and peak flux
+	float xChip = PM_FPA_TO_CHIP(galaxy->x, x0Chip, xParityChip);
+	float yChip = PM_FPA_TO_CHIP(galaxy->y, y0Chip, yParityChip);
+
+	// Position on the cell and peak flux
+	float xCell = PM_CHIP_TO_CELL(xChip, x0Cell, xParityCell, binning);
+	float yCell = PM_CHIP_TO_CELL(yChip, y0Cell, yParityCell, binning);
+
+	if (xCell < 0) continue;
+	if (yCell < 0) continue;
+	if (xCell > readout->image->numCols) continue;
+	if (yCell > readout->image->numRows) continue;
+	// XXX need to apply col0, row0 if readout is a subarray
+
+	// XXX apply the expCorr to the galaxy->flux before setting the model flux
+
+	// instantiate a model for the PSF at this location, set desired flux
+	pmModel *model = pmModelAlloc (type);
+	
+	psF32 *PAR = model->params->data.F32;
+
+	PAR[PM_PAR_I0]   = galaxy->peak;
+	PAR[PM_PAR_SKY]  = 0.0;
+	PAR[PM_PAR_XPOS] = xChip;
+	PAR[PM_PAR_YPOS] = yChip;
+
+	psEllipseAxes axes;
+        axes.major       = galaxy->Rmaj;
+        axes.minor       = galaxy->Rmin;
+        axes.theta       = galaxy->theta;
+	pmPSF_AxesToModel (PAR, axes);
+	psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+	if (nParam == 8) {
+	    PAR[PM_PAR_7] = galaxy->index;
+	}
+
+	// set the normalization to get the desired flux
+	// XXX for now, use peak: pmModelSetFlux (model, galaxy->flux);
+
+	// XXX let the flux limit be a user-defined number of sky sigmas (not just 1.0)
+	float radius = model->modelRadius (model->params, roughNoise);
+	radius = PS_MAX (radius, 1.0);
+	// XXX the exp(-r^0.25) models can go way out if allowed...
+	radius = PS_MIN (radius, 150.0); 
+
+	// construct a source, with model flux pixels set, based on the model
+	pmSource *source = pmSourceFromModel (model, readout, radius, PM_SOURCE_TYPE_EXTENDED);
+
+	// XXX set the mag & err values (should this be done in pmSourceFromModel?)
+	// XXX i should be applying the gain and the correct effective area
+	source->psfMag = -2.5*log10(galaxy->flux);
+	source->extMag = -2.5*log10(galaxy->flux);
+	source->errMag = sqrt(Area*PS_SQR(roughNoise) + galaxy->flux) / galaxy->flux;	
+	
+	// XXX add the sources to a source array
+
+# if (0)	
+	if (CONVOLVED_FIT) {
+
+	    // select the PSF (XXX : move out of loop)
+	    pmPSF *psfModel = psMetadataLookupPtr (&mdok, chip->analysis, "PSPHOT.PSF");
+	    assert (psf);
+	    
+	    // supply the psf to the source (normalized?)
+	    source->modelPSF = pmModelFromPSFforXY (psfModel, xChip, yChip, 1.0);
+
+	    // instantiate the psf flux
+	    pmSourceCachePSF (source, maskVal);
+
+	    // convert the cached cached psf model for this source to a psKernel
+	    // XXX for the moment, hard-wire the kernel to be 5x5 (2 pix radius)
+	    // XXX for the moment, hard-wire the kernel to be 9x9 (4 pix radius)
+	    psKernel *psf = psphotKernelFromPSF (source, psfSize);
+
+	    // instantiate the source model flux
+	    // XXX this may need to handle the offset?
+	    pmSourceCacheModel (source, maskVal);
+
+	    // make a storage buffer for the output image
+	    psImage *buffer = psImageCopy (source->modelFlux, PS_TYPE_F32);
+
+	    // convolve the psf image with the model image
+	    psImageConvolveDirect (buffer, source->modelFlux, psf);
+
+	    // save the result back in the source
+	    psFree (source->modelFlux);
+	    source->modelFlux = buffer;
+	}
+# endif
+
+	// insert the source flux in the image
+	pmSourceAddWithOffset (source, PM_MODEL_OP_FULL, 0xff, dX, dY);
+	psArrayAdd (sources, 100,source);
+    }
+
+    // NOTE: readout must be part of the pmFPAfile named "PPSIM.OUTPUT"
+    // psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY | PS_META_REPLACE, "psphot sources", sources);
+
+    // XXX many leaks in here, i think 
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimInsertStars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimInsertStars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimInsertStars.c	(revision 22322)
@@ -0,0 +1,140 @@
+# include "ppSim.h"
+
+// Reset a pointer: free and set to NULL
+#define RESET(PTR) \
+    psFree(PTR); \
+    PTR = NULL;
+
+
+bool ppSimInsertStars (pmReadout *readout, psImage *expCorr, psArray *stars, pmConfig *config) {
+
+    bool mdok;
+
+    assert (readout);
+    assert (stars);
+
+    if (!stars->n) { return true; }
+
+    pmCell *cell = readout->parent;
+    pmChip *chip = cell->parent;
+
+    // XXX this is an estimate of the sky noise based on the inputs to the image simulation.
+    // XXX update this to allow the estimate based on the measured sky background
+    // XXX this is missing the gain.
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
+
+    float expTime   = psMetadataLookupF32(NULL, recipe, "EXPTIME"); // Exposure time
+    float darkRate  = psMetadataLookupF32(NULL, recipe, "DARK.RATE"); // Dark rate
+
+    float readnoise = psMetadataLookupF32(NULL, cell->concepts, "CELL.READNOISE");// CCD read noise, e
+    if (isnan(readnoise)) {
+        psWarning("CELL.READNOISE is not set; reverting to recipe value READNOISE.");
+        readnoise = psMetadataLookupF32(&mdok, recipe, "READNOISE");
+        if (!mdok) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find READNOISE in recipe.");
+            return false;
+        }
+    }
+
+    float skyRate = psMetadataLookupF32(NULL, recipe, "SKY.RATE"); // Sky rate
+    if (isnan(skyRate)) {
+	float zp      = psMetadataLookupF32(&mdok, recipe, "ZEROPOINT"); assert (mdok);
+	float scale   = psMetadataLookupF32(&mdok, recipe, "PIXEL.SCALE");     assert (mdok);
+	float skyMags = psMetadataLookupF32(&mdok, recipe, "SKY.MAGS");  assert (mdok);
+        skyRate = scale * scale * ppSimMagToFlux (skyMags, zp);
+    }
+
+    // Rough noise estimate, appropriate for entire cell (use for source radius?)
+    float roughNoise = sqrtf(PS_SQR(readnoise) + (darkRate + skyRate) * expTime);
+
+    int x0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.X0");
+    int y0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.Y0");
+    int xParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.XPARITY");
+    int yParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY");
+
+    int x0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.X0");
+    int y0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0");
+    int xParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY");
+    int yParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY");
+
+    int binning = psMetadataLookupS32(NULL, recipe, "BINNING"); // Binning in x and y
+
+    pmPSF *psf = psMetadataLookupPtr (&mdok, chip->analysis, "PSPHOT.PSF");
+    assert (psf);
+
+    int dX = PM_CELL_TO_CHIP (0.0, x0Cell, xParityCell, binning);
+    int dY = PM_CELL_TO_CHIP (0.0, y0Cell, yParityCell, binning);
+
+    psArray *sources = psArrayAllocEmpty (stars->n);
+
+    // add sources to the readout image & weight
+    psTrace("ppSim", 1, "Inserting %ld stars...\n", stars->n);
+    for (long i = 0; i < stars->n; i++) {
+        ppSimStar *star = stars->data[i];
+        psTrace("ppSim", 10, "Inserting star at %.1f,%.1f --> %.2f\n", star->x, star->y, star->flux);
+
+        // star->x,y are in fpa coordinates
+
+        // Position on the cell and peak flux
+        float xChip = PM_FPA_TO_CHIP(star->x, x0Chip, xParityChip);
+        float yChip = PM_FPA_TO_CHIP(star->y, y0Chip, yParityChip);
+
+        // Position on the cell and peak flux
+        float xCell = PM_CHIP_TO_CELL(xChip, x0Cell, xParityCell, binning);
+        float yCell = PM_CHIP_TO_CELL(yChip, y0Cell, yParityCell, binning);
+
+        // XXX Note, the below does not put the edges of stars on the readout if they fall slightly off
+        // This will be visible as cut-off stars at amplifier boundaries (e.g., Megacam)
+        if (xCell < 0) continue;
+        if (yCell < 0) continue;
+        if (xCell > readout->image->numCols) continue;
+        if (yCell > readout->image->numRows) continue;
+        // XXX need to apply col0, row0 if readout is a subarray
+
+        // Apply the expCorr to the star->flux before setting the model flux
+        float flux = star->flux * expCorr->data.F32[(int)yCell][(int)xCell];
+
+        // instantiate a model for the PSF at this location, set desired flux
+        pmModel *model = pmModelFromPSFforXY (psf, xChip, yChip, 1.0);
+        pmModelSetFlux (model, flux);
+
+        // XXX let the flux limit be a user-defined number of sky sigmas (not just 1.0)
+        float radius = model->modelRadius (model->params, roughNoise);
+        radius = PS_MAX (radius, 1.0);
+
+        // construct a source, with model flux pixels set, based on the model
+        pmSource *source = pmSourceFromModel (model, readout, radius, PM_SOURCE_TYPE_STAR);
+
+        // XXX set the mag & err values (should this be done in pmSourceFromModel?)
+        // XXX i should be applying the gain and the correct effective area
+        psEllipseAxes axes = pmPSF_ModelToAxes (model->params->data.F32, 20.0);
+        psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+        source->psfMag = -2.5*log10(flux);
+        source->errMag = sqrt(Area*PS_SQR(roughNoise) + flux) / flux;
+
+        // insert the source flux in the image
+        pmSourceAddWithOffset (source, PM_MODEL_OP_FULL, 0xff, dX, dY);
+        pmSourceAddWithOffset (source, PM_MODEL_OP_FULL | PM_MODEL_OP_NOISE, 0xff, dX, dY);
+
+        // Blow away the image parts of the source, which makes the memory explode
+        RESET(source->pixels);
+        RESET(source->weight);
+        RESET(source->maskObj);
+        RESET(source->maskView);
+        RESET(source->modelFlux);
+        RESET(source->psfFlux);
+        RESET(source->blends);
+
+        // add the sources to the source array
+        psArrayAdd (sources, 100,source);
+        psFree(source);                 // Drop reference
+    }
+
+    // NOTE: the pmFPAfile "PPSIM.OUTPUT" points at these sources
+    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY, "fake sources", sources);
+    psFree(sources);
+
+    // XXX many leaks in here, i think
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoadForceSources.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoadForceSources.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoadForceSources.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "ppSim.h"
+
+// Reset a pointer: free and set to NULL
+#define RESET(PTR) \
+    psFree(PTR); \
+    PTR = NULL;
+
+psArray *ppSimLoadForceSources(pmConfig *config, const pmFPAview *view) {
+
+    bool status;
+
+    pmFPAfile *outFile = psMetadataLookupPtr(NULL, config->files, "PPSIM.OUTPUT");
+    psAssert(outFile, "missing PPSIM.OUTPUT");
+    psAssert(outFile->fpa, "missing fpr for PPSIM.OUTPUT");
+
+    pmChip *outChip = pmFPAviewThisChip (view, outFile->fpa);
+    pmPSF *psf = psMetadataLookupPtr (&status, outChip->analysis, "PSPHOT.PSF");
+    assert (psf);
+
+    psArray *spots = psMetadataLookupPtr(&status, outFile->fpa->analysis, "FORCED.SPOTS");
+    PS_ASSERT_PTR_NON_NULL (spots, NULL);
+
+    // in this program, we are looping over the output image, rather than the input as in ppImage
+    pmFPAfile *srcFile = psMetadataLookupPtr(NULL, config->files, "PPSIM.REAL.SOURCES");
+    psAssert(srcFile, "missing PPSIM.REAL.SOURCES");
+
+    // select the fpa and chip from PPSIM.REAL.SOURCES, which carries the astrometry in the headers
+    pmFPA     *fpa     = srcFile->fpa;
+    pmChip    *chip    = pmFPAviewThisChip (view, fpa);
+
+    // we use the readout for the PPSIM.FORCE.CHIP, which contains the chip-mosaicked image(s)
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PPSIM.FORCE.CHIP");
+
+    // XXX we should have only one cell and readout per chip, right?
+
+    // read WCS from existing output from psphot & psastro
+    // XXX can we be more flexible?  ie, use the header of PPSIM.FORCE.CHIP?
+    bool bilevelAstrometry = false;
+    status = psastroAstromGuessSetFPA (fpa, &bilevelAstrometry);
+    psAssert (status, "failed to set astrometry");
+
+    // the pixel scale is only used to set the focal-plane coordinate system
+    // setting this value to 1 makes this system have the units of pixels
+    float pixelScale = 1.0;
+    status = psastroAstromGuessSetChip (fpa, chip, view, pixelScale, bilevelAstrometry);
+    psAssert (status, "failed to set astrometry");
+
+    float minX = 0.0;
+    float maxX = readout->image->numCols;
+    float minY = 0.0;
+    float maxY = readout->image->numRows;
+
+    // the sources is a subset within range of this chip
+    psArray *sources = psArrayAllocEmpty (100);
+
+    // Select the spots within range of this readout.  Project the spots to this chip
+    for (int i = 0; i < spots->n; i++) {
+	pmAstromObj *obj = spots->data[i];
+
+	psProject (obj->TP, obj->sky, fpa->toSky);
+	psPlaneTransformApply (obj->FP, fpa->fromTPA, obj->TP);
+	psPlaneTransformApply (obj->chip, chip->fromFPA, obj->FP);
+
+	// limit the X,Y range of the objs to the selected chip
+	if (obj->chip->x < minX) continue;
+	if (obj->chip->x > maxX) continue;
+	if (obj->chip->y < minY) continue;
+	if (obj->chip->y > maxY) continue;
+
+	// convert the pmAstromObj to pmSource
+
+        // instantiate a model for the PSF at this location, Io = 1.0
+        pmModel *model = pmModelFromPSFforXY (psf, obj->chip->x, obj->chip->y, 1.0);
+
+        // XXX let the flux limit be a user-defined number of sky sigmas (not just 1.0)
+	// XXX use a fixed radius??
+        // float radius = model->modelRadius (model->params, roughNoise);
+        // radius = PS_MAX (radius, 1.0);
+	float radius = 5.0;
+
+        // construct a source, with model flux pixels set, based on the model
+        pmSource *source = pmSourceFromModel (model, readout, radius, PM_SOURCE_TYPE_STAR);
+
+        // XXX set the mag & err values (should this be done in pmSourceFromModel?)
+        // XXX i should be applying the gain and the correct effective area
+        // psEllipseAxes axes = pmPSF_ModelToAxes (model->params->data.F32, 20.0);
+        // psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+	// these are not really needed since we will be fitting for them
+        source->psfMag = NAN;
+        source->errMag = NAN;
+
+        // insert the source flux in the image
+	// XXX not sure the offset is really 0,0
+        pmSourceAddWithOffset (source, PM_MODEL_OP_FULL, 0xff, 0.0, 0.0);
+        pmSourceAddWithOffset (source, PM_MODEL_OP_FULL | PM_MODEL_OP_NOISE, 0xff, 0.0, 0.0);
+
+        // Blow away the image parts of the source, which makes the memory explode
+        RESET(source->pixels);
+        RESET(source->weight);
+        RESET(source->maskObj);
+        RESET(source->maskView);
+        RESET(source->modelFlux);
+        RESET(source->psfFlux);
+        RESET(source->blends);
+
+	psArrayAdd (sources, 100, source);
+        psFree(source);                 // Drop local reference
+    }
+    psTrace ("psastro", 4, "Added %ld sources\n", sources->n);
+
+    return sources;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoadSpots.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoadSpots.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoadSpots.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "ppSim.h"
+
+// Load the relevant forced-photometry positions for this field.  This function does not determine
+// the pixel coordinates, merely the celestial coordinates for sources in the general vicinity.  The
+// sources are saved on the fpa->analysis metadata as FORCED.SPOTS.  We always create an entry; it
+// will be empty if no sources were selected or forced photometry is not desired.
+bool ppSimLoadSpots (pmFPA *fpa, pmConfig *config) {
+
+    bool status;
+
+
+    psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, PPSIM_RECIPE); // Recipe
+
+    bool forcedPhot = psMetadataLookupBool(&status, recipe, "FORCED.PHOT"); // Density of fakes
+    if (!forcedPhot) {
+	psArray *spots = psArrayAllocEmpty (1);
+	psMetadataAddArray (fpa->analysis, PS_LIST_TAIL, "FORCED.SPOTS", PS_META_REPLACE, "forced photometry positions", spots);
+	psFree (spots); // free the extra (local) reference; a copy remains on fpa->analysis
+	return true;
+    }
+
+    // We read the catalogue stars using psastroLoadRefstars
+    psMetadata *astroRecipe = psMetadataLookupPtr(NULL, config->recipes, PSASTRO_RECIPE);
+    if (!astroRecipe) {
+        psError(PSASTRO_ERR_CONFIG, false, "Can't find recipe %s", PSASTRO_RECIPE);
+        return false;
+    }
+
+    float ra0     = psMetadataLookupF32(NULL, recipe, "RA");        // Boresight RA (radians)
+    float dec0    = psMetadataLookupF32(NULL, recipe, "DEC");       // Boresight Dec (radians)
+    float scale   = psMetadataLookupF32(NULL, recipe, "PIXEL.SCALE") * M_PI / 3600.0 / 180.0; // Plate scale (radians/pixel)
+
+    if (isnan(ra0) || isnan(dec0)) {
+        psError(PS_ERR_UNKNOWN, false, "image boresite coords not defined.");
+	return false;
+    }
+
+    char *catdir = psMetadataLookupStr(NULL, recipe, "FORCED.CATDIR");
+
+    // Size of FPA
+    psRegion *bounds = ppSimFPABounds (fpa);
+    float radius = 0.5 * PS_MAX(bounds->x1 - bounds->x0, bounds->y1 - bounds->y0) * scale;
+    psFree(bounds);
+
+    // modify the PSASTRO recipe to use the values desired for FORCED.PHOT.  we can use a view on
+    // the PSASTRO recipe because we are not calling psastro elsewhere in this program.  XXX need to
+    // use the WCS to define the overlap region
+    psMetadataAddStr(astroRecipe, PS_LIST_TAIL, "PSASTRO.CATDIR",  PS_META_REPLACE, "", catdir);
+    psMetadataAddF32(astroRecipe, PS_LIST_TAIL, "RA_MIN",  PS_META_REPLACE, "", ra0 - radius);
+    psMetadataAddF32(astroRecipe, PS_LIST_TAIL, "RA_MAX",  PS_META_REPLACE, "", ra0 + radius);
+    psMetadataAddF32(astroRecipe, PS_LIST_TAIL, "DEC_MIN", PS_META_REPLACE, "", dec0 - radius);
+    psMetadataAddF32(astroRecipe, PS_LIST_TAIL, "DEC_MAX", PS_META_REPLACE, "", dec0 + radius);
+    
+    // tell psastroLoadRefstars to ignore photcode and maglim
+    psMetadataAddStr(astroRecipe, PS_LIST_TAIL, "DVO.GETSTAR.PHOTCODE",  PS_META_REPLACE, "", "NONE");
+    psMetadataAddF32(astroRecipe, PS_LIST_TAIL, "DVO.GETSTAR.MAG.MAX",  PS_META_REPLACE, "", NAN);
+
+    // load refstars from the catalog
+    psArray *spots = psastroLoadRefstars(config);
+    if (!spots || spots->n == 0) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find reference stars.");
+        psFree(spots);
+        return false;
+    }
+    psLogMsg("ppSim", PS_LOG_INFO, "Adding %ld reference stars", spots->n);
+
+    psMetadataAddArray (fpa->analysis, PS_LIST_TAIL, "FORCED.SPOTS", PS_META_REPLACE, "forced photometry positions", spots);
+    psFree (spots); // free the extra (local) reference; a copy remains on fpa->analysis
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoadStars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoadStars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoadStars.c	(revision 22322)
@@ -0,0 +1,105 @@
+# include "ppSim.h"
+
+bool ppSimLoadStars (psArray *stars, pmFPA *fpa, pmConfig *config) {
+
+    bool mdok;
+    assert (stars);
+
+    psMetadata *recipe = psMetadataLookupMetadata(&mdok, config->recipes, PPSIM_RECIPE); // Recipe
+
+    bool starsReal = psMetadataLookupBool(&mdok, recipe, "STARS.REAL"); // Density of fakes
+    if (!starsReal) return true;
+
+    // Read catalogue stars using psastro
+    psMetadata *astroRecipe = psMetadataLookupPtr(NULL, config->recipes, PSASTRO_RECIPE);
+    if (!astroRecipe) {
+        psError(PSASTRO_ERR_CONFIG, false, "Can't find recipe %s", PSASTRO_RECIPE);
+        return NULL;
+    }
+
+    // relevant metadata
+    float zp      = psMetadataLookupF32(NULL, recipe, "ZEROPOINT"); // Photometric zero point
+    float ra0     = psMetadataLookupF32(NULL, recipe, "RA");        // Boresight RA (radians)
+    float dec0    = psMetadataLookupF32(NULL, recipe, "DEC");       // Boresight Dec (radians)
+    float pa      = psMetadataLookupF32(NULL, recipe, "PA");        // Position angle (radians)
+    float seeing  = psMetadataLookupF32(NULL, recipe, "SEEING");    // Seeing SIGMA (pixels)
+    float scale   = psMetadataLookupF32(NULL, recipe, "PIXEL.SCALE") * M_PI / 3600.0 / 180.0; // Plate scale (radians/pixel)
+    float expTime = psMetadataLookupF32(NULL, recipe, "EXPTIME");   // Exposure time (sec)
+
+    // Size of FPA
+    psRegion *bounds = ppSimFPABounds (fpa);
+    float radius = 0.5 * PS_MAX(bounds->x1 - bounds->x0, bounds->y1 - bounds->y0) * scale;
+    psFree(bounds);
+
+    float x0fpa = 0.5*(bounds->x0 + bounds->x1);
+    float y0fpa = 0.5*(bounds->y0 + bounds->y1);
+
+    psMetadataAdd(astroRecipe, PS_LIST_TAIL, "RA_MIN",  PS_DATA_F32 | PS_META_REPLACE, "",
+                  ra0 - radius / cos(dec0));
+    psMetadataAdd(astroRecipe, PS_LIST_TAIL, "RA_MAX",  PS_DATA_F32 | PS_META_REPLACE, "",
+                  ra0 + radius / cos(dec0));
+    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);
+    if (!refStars) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find reference stars.");
+        psFree(refStars);
+        return NULL;
+    }
+    psLogMsg("ppSim", PS_LOG_INFO, "Adding %ld reference stars", refStars->n);
+
+    long oldSize = stars->n;
+    stars = psArrayRealloc (stars, refStars->n);
+
+    psProjection *proj = psProjectionAlloc(ra0, dec0, scale, scale, PS_PROJ_TAN); // Projection
+
+    // Conversion loop
+    for (long i = 0; i < refStars->n; i++) {
+        ppSimStar *star = ppSimStarAlloc ();
+
+        pmAstromObj *ref = refStars->data[i]; // Reference star
+        star->ra  = ref->sky->r; // RA of star
+        star->dec = ref->sky->d; // Dec of star
+        star->mag = ref->Mag;       // Magnitude of star
+
+        // Convert to x,y position on tangent plane, in pixels
+        psPlane plane;                  // Plane (detector) coordinates
+        psSphere sphere;                // Sphere (sky) coordinates
+        sphere.r = star->ra;
+        sphere.d = star->dec;
+        psProject(&plane, &sphere, proj);
+
+        // Apply rotation, make FPA center of boresite
+        star->x = cos(pa) * plane.x - sin(pa) * plane.y + x0fpa;
+        star->y = sin(pa) * plane.x + cos(pa) * plane.y + y0fpa;
+
+        // Convert magnitude to peak flux
+        star->flux = ppSimMagToFlux (star->mag, zp) * expTime;
+        star->peak = ppSimStarFluxToPeak (star->flux, seeing);
+
+        stars->data[oldSize + i] = star;
+
+        psTrace("ppSim", 10, "Adding catalogue star: %.1f,%.1f --> %.2f\n", star->x, star->y, star->flux);
+    }
+    stars->n = oldSize + refStars->n;
+    psFree(proj);
+
+    pmLumFunc *lumfunc = psastroLuminosityFunction (refStars);
+    psFree(refStars);
+
+    psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "FPA.RA", PS_META_REPLACE, "Right ascension", ra0);
+    psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "FPA.DEC", PS_META_REPLACE, "Declination", dec0);
+
+    if (lumfunc) {
+        psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "STARS.REAL.MAG.MIN",   PS_META_REPLACE, "min valid magnitude",             lumfunc->mMin);
+        psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "STARS.REAL.MAG.MAX",   PS_META_REPLACE, "max valid magnitude",             lumfunc->mMax);
+        psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "STARS.REAL.LF.SLOPE",  PS_META_REPLACE, "log-mag histogram slope",         lumfunc->slope);
+        psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "STARS.REAL.LF.OFFSET", PS_META_REPLACE, "log-mag histogram offset",        lumfunc->offset);
+        psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "STARS.REAL.MAG.PEAK",  PS_META_REPLACE, "magnitude of peak bin",           lumfunc->mPeak);
+        psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "STARS.REAL.NUM.PEAK",  PS_META_REPLACE, "number of stars in peak bin", lumfunc->nPeak);
+        psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "STARS.REAL.SUM.PEAK",  PS_META_REPLACE, "sum of stars up to peak bin", lumfunc->sPeak);
+        psFree (lumfunc);
+    }
+
+    return stars;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimLoop.c	(revision 22322)
@@ -0,0 +1,220 @@
+#include "ppSim.h"
+
+# define ESCAPE(CODE,MSG) { \
+  psError(CODE, false, MSG); \
+  psFree (rng); \
+  return false; }
+
+bool ppSimLoop(pmConfig *config)
+{
+    bool status;
+
+    // XXX if we are supplying a PSF, then we should use that to specify the seeing.
+    // we will need to force the psf to be loaded here (deactivate everyone, activate psf, load
+    // it, then calculate seeing as appropriate).
+
+    PS_ASSERT_PTR_NON_NULL(config, PS_EXIT_PROG_ERROR);
+
+    // in this program, we are looping over the output image, rather than the input as in ppImage
+    pmFPAfile *file = psMetadataLookupPtr(NULL, config->files, "PPSIM.OUTPUT"); // Output file
+    assert(file);
+
+    pmFPA *fpa = file->fpa;             // FPA for file
+    assert(fpa);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
+
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+
+    char *typeStr = psMetadataLookupStr(NULL, recipe, "IMAGE.TYPE"); // Type of image to simulate
+    ppSimType type = ppSimTypeFromString (typeStr); // Type of image to simulate
+    int binning = psMetadataLookupS32(NULL, recipe, "BINNING"); // Binning in x and y
+
+    psArray *stars = psArrayAllocEmpty (1);
+    psArray *galaxies = psArrayAllocEmpty (1);
+    if (type == PPSIM_TYPE_OBJECT) {
+	// Load forced-photometry positions (these are placed on fpa->analysis for use in ppSimPhotomReadout)
+	if (!ppSimLoadSpots (fpa, config)) ESCAPE (PS_ERR_UNKNOWN, "failed to load forced-photometry spots");
+
+	// Load catalogue stars
+        if (!ppSimLoadStars (stars, fpa, config)) ESCAPE (PS_ERR_UNKNOWN, "failed to load catalog stars");
+
+	// Add random stars
+        if (!ppSimMakeStars (stars, fpa, config, rng)) ESCAPE (PS_ERR_UNKNOWN, "failed to make random stars");
+
+	// Add random galaxies
+        if (!ppSimMakeGalaxies (galaxies, fpa, config, rng)) ESCAPE (PS_ERR_UNKNOWN, "failed to make random galaxies");
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View for iterating over FPA
+
+    // XXX if we include the psphot analysis, we will need to activate the correct pmFPAfiles
+    // at the correct times.  ppSim operates on PPSIM.OUTPUT and PPSIM.SOURCES
+
+    // load any needed files (eg, input image, PSF)
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) {
+        psError(PS_ERR_UNKNOWN, false, "failed IO for fpa in ppSim\n");
+        psFree(view);
+        return false;
+    }
+
+    ppSimUpdateConceptsFPA (fpa, config);
+    if (fpa->hdu) { // XXX only do this if there is no INPUT image
+        if (!ppSimInitHeader(config, fpa, NULL, NULL)) ESCAPE (PS_ERR_UNKNOWN, "problem setting output header");
+    }
+
+    pmChip *chip;                       // Chip from FPA
+    while ((chip = pmFPAviewNextChip(view, fpa, 1))) {
+
+        // load any needed files (eg, input image, PSF)
+        if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) {
+            psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d in ppSim\n", view->chip);
+            psFree (view);
+            return false;
+        }
+
+        if (type == PPSIM_TYPE_OBJECT) {
+            if (!ppSimSetPSF (chip, config)) {
+                psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d in ppSim\n", view->chip);
+                psFree (view);
+                return false;
+            }
+        }
+
+        pmCell *cell;                   // Cell from chip
+        while ((cell = pmFPAviewNextCell(view, fpa, 1))) {
+
+            // check that we are able to work with this cell (readdir must be 1)
+            int readdir = psMetadataLookupS32(NULL, cell->concepts, "CELL.READDIR"); // Read direction
+            if (readdir != 1) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CELL.READDIR = 1 is the only supported mode.");
+                psFree(rng);
+                return false;
+            }
+
+            // if no readout exists, generate a single readout (upgrade to allow multiple
+            // readouts?)
+            if (cell->readouts->n == 0) {
+                // Size, position and orientation of cell
+                int numCols = psMetadataLookupS32(NULL, cell->concepts, "CELL.XSIZE") / binning;
+                int numRows = psMetadataLookupS32(NULL, cell->concepts, "CELL.YSIZE") / binning;
+
+                // generate a new readout for this cell
+                pmReadout *readout = pmReadoutAlloc(cell); // Readout within cell
+
+                // TO DO: Decide if cell is to be windowed, reduce numCols, numRows appropriately
+                readout->image = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Signal in pixels
+                readout->weight = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Noise in pixels
+
+                psImageInit (readout->image, 0.0);
+                psImageInit (readout->weight, 0.0);
+
+                psFree(readout);        // Drop reference
+            }
+
+            psVector *biasCols = ppSimMakeBiassec (cell, config);
+
+	    pmReadout *readout;
+	    while ((readout = pmFPAviewNextReadout (view, fpa, 1))) {
+
+                // if we have not read in a weight or generated a fake image above, we need to
+                // build one here
+                if (!readout->weight) {
+                    if (!pmReadoutGenerateWeight(readout, true)) {
+                        psError (PS_ERR_UNKNOWN, false, "trouble creating weight");
+                        return false;
+                    }
+                }
+
+                psImage *expCorr = NULL; // Exposure correction per pixel, for adding objects
+                if (type == PPSIM_TYPE_OBJECT) {
+                    expCorr = psImageAlloc(readout->image->numCols, readout->image->numRows, PS_TYPE_F32);
+                }
+
+                psVector *biasRows = ppSimMakeBias (&status, readout, config, rng);
+		if (!status) ESCAPE (PS_ERR_UNKNOWN, "problem generating dark structure");
+                if (type == PPSIM_TYPE_BIAS) goto done;
+
+		if (!ppSimMakeDark (readout, config)) ESCAPE (PS_ERR_UNKNOWN, "problem generating dark structure");
+                if (type == PPSIM_TYPE_DARK) goto done;
+
+                if (!ppSimMakeSky (readout, expCorr, type, config)) ESCAPE (PS_ERR_UNKNOWN, "problem generating sky background");
+                if (type == PPSIM_TYPE_FLAT) goto done;
+
+                if (type == PPSIM_TYPE_OBJECT) {
+                    if (!ppSimInsertStars (readout, expCorr, stars, config)) ESCAPE (PS_ERR_UNKNOWN, "problem inserting stars");
+                }
+
+                if (type == PPSIM_TYPE_OBJECT) {
+                    if (!ppSimInsertGalaxies (readout, expCorr, galaxies, config)) ESCAPE (PS_ERR_UNKNOWN, "problem inserting galaxies");
+                }
+
+                psFree(expCorr);
+
+            done:
+                if (!ppSimAddNoise(readout->image, readout->weight, cell, config, rng)) ESCAPE (PS_ERR_UNKNOWN, "problem adding noise");
+                if (!ppSimSaturate(readout, config)) ESCAPE (PS_ERR_UNKNOWN, "problem setting saturation levels");
+
+                if (!ppSimBadPixels(readout, config, rng)) ESCAPE (PS_ERR_UNKNOWN, "problem adding bad pixels");
+
+                if (!ppSimAddOverscan (readout, config, biasCols, biasRows, rng)) ESCAPE (PS_ERR_UNKNOWN, "problem adding overscan region");
+                psFree(biasRows);
+
+                readout->data_exists = true;
+                readout->parent->data_exists = true;
+                readout->parent->parent->data_exists = true;
+
+		// if there is an input image, merge it with the simulated image
+		if (!ppSimMergeReadouts (config, view)) ESCAPE (PS_ERR_UNKNOWN, "problem merging input image with simulated image");
+            }
+            psFree(biasCols);
+
+            if (!ppSimUpdateConceptsCell (cell, config)) ESCAPE (PS_ERR_UNKNOWN, "problem updating cell concepts");
+
+            if (cell->hdu) {
+		// XXX only do this if there is no INPUT image?
+                if (!ppSimInitHeader(config, NULL, NULL, cell)) ESCAPE (PS_ERR_UNKNOWN, "problem setting output header");
+            }
+
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+                psError(PS_ERR_IO, false, "Unable to write file.");
+                psFree(rng);
+                psFree(view);
+                // return PS_EXIT_SYS_ERROR;
+                return false;
+            }
+        }
+
+	// XXX why no UpdateConceptsChip??
+
+        if (chip->hdu) {
+	    // XXX only do this if there is no INPUT image
+            if (!ppSimInitHeader(config, NULL, chip, NULL)) ESCAPE (PS_ERR_UNKNOWN, "problem setting output header");
+        }
+
+        // we perform photometry on the readouts of this chip in the output
+	if (!ppSimPhotom (config, view)) ESCAPE (PS_ERR_UNKNOWN, "problem performing photometry");
+
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            psError(PS_ERR_IO, false, "Unable to write file.");
+            psFree(rng);
+            psFree(view);
+            return false;
+        }
+    }
+
+    psFree(stars);
+    psFree(galaxies);
+
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        psError(PS_ERR_IO, false, "Unable to write file.");
+        psFree(rng);
+        psFree(view);
+        return false;
+    }
+
+    psFree(rng);
+    psFree(view);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeBias.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeBias.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeBias.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "ppSim.h"
+
+psVector *ppSimMakeBias (bool *status, pmReadout *readout, pmConfig *config, const psRandom *rng) {
+
+    bool mdok;
+
+    if (status) *status = true;
+
+    pmCell *cell = readout->parent;
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
+
+    bool bias = psMetadataLookupBool(&mdok, recipe, "BIAS"); // Generate a Bias?
+    if (!bias) return NULL;
+
+    float biasLevel = psMetadataLookupF32(NULL, recipe, "BIAS.LEVEL"); // Bias level
+    float biasRange = psMetadataLookupF32(NULL, recipe, "BIAS.RANGE"); // Bias range
+    int biasOrder   = psMetadataLookupS32(NULL, recipe, "BIAS.ORDER"); // Bias order
+
+    float readnoise = psMetadataLookupF32(NULL, cell->concepts, "CELL.READNOISE");// CCD read noise, e
+    if (isnan(readnoise)) {
+	psWarning("CELL.READNOISE is not set; reverting to recipe value READNOISE.");
+	readnoise = psMetadataLookupF32(&mdok, recipe, "READNOISE");
+	if (!mdok) {
+	    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find READNOISE in recipe.");
+	    *status = false;
+	    return NULL;
+	}
+    }
+
+    psImage *signal = readout->image;
+    psImage *variance = readout->weight;
+
+    int numRows = signal->numRows;
+    int numCols = signal->numCols;
+
+    // Polynomial for bias
+    psPolynomial1D *biasPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, biasOrder);
+    for (int j = 0; j < biasOrder + 1; j++) {
+	biasPoly->coeff[j] = biasRange * psRandomGaussian(rng);
+    }
+
+    psVector *biasRows = psVectorAlloc(numRows, PS_TYPE_F32); // Bias value, per row
+    int biasOffset = 0.5 * numRows * psRandomUniform(rng); // Offset to prevent common pattern
+
+    for (int y = 0; y < numRows; y++) {
+	// Adjust bias level for this row
+	biasRows->data.F32[y] = psPolynomial1DEval(biasPoly, (float)(y + biasOffset) /
+						  (float)numRows - 0.5) + biasLevel;
+
+	for (int x = 0; x < numCols; x++) {
+
+	    // Bias level
+	    signal->data.F32[y][x] += biasRows->data.F32[y];
+	    variance->data.F32[y][x] += PS_SQR(readnoise);
+
+	}
+    }
+    psFree(biasPoly);
+
+    return biasRows;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeBiassec.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeBiassec.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeBiassec.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "ppSim.h"
+
+psVector *ppSimMakeBiassec (pmCell *cell, pmConfig *config) {
+
+    bool mdok;
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
+
+    bool bias = psMetadataLookupBool(&mdok, recipe, "BIAS"); // Generate a Bias?
+    if (!bias) return NULL;
+
+    psList *biassec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.BIASSEC"); // Bias regions
+    int numBias = psListLength(biassec); // Number of bias sections
+
+    psVector *biasCols;
+    if (numBias > 0) {
+	biasCols = psVectorAlloc(numBias, PS_TYPE_S32);
+	psListIterator *iter = psListIteratorAlloc(biassec, PS_LIST_HEAD, false); // Iterator
+	psRegion *bias;         // Bias region, from iteration
+	int i = 0;              // Counter
+	while ((bias = psListGetAndIncrement(iter))) {
+	    biasCols->data.S32[i++] = bias->x1 = bias->x0;
+	}
+    } else {
+	biasCols = psVectorAlloc(1, PS_TYPE_S32);
+	biasCols->data.S32[0] = psMetadataLookupS32(&mdok, recipe, "OVERSCAN.SIZE");
+	if (!mdok) {
+	    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find OVERSCAN.SIZE in recipe.");
+	    psFree(biasCols);
+	    return NULL;
+	}
+	psRegion *newBias = psRegionAlloc(NAN, NAN, NAN, NAN); // New bias region, to be set later
+	biassec = psListAlloc(newBias);
+	psFree(newBias);
+	psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.BIASSEC", PS_DATA_LIST | PS_META_REPLACE,
+		      "Bias sections", biassec);
+	psFree(biassec);
+	numBias = 1;
+    }
+
+    return biasCols;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeDark.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeDark.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeDark.c	(revision 22322)
@@ -0,0 +1,41 @@
+# include "ppSim.h"
+
+// XXX add bounds to the inputs?
+bool ppSimMakeDark (pmReadout *readout, pmConfig *config) {
+
+    bool mdok;
+
+    psImage *signal = readout->image;
+    psImage *variance = readout->weight;
+
+    psMetadata *recipe = psMetadataLookupMetadata(&mdok, config->recipes, PPSIM_RECIPE); // Recipe
+
+    bool dark = psMetadataLookupBool(&mdok, recipe, "DARK"); // Generate a DARK?
+    if (!dark) return true;
+
+    float darkRate = psMetadataLookupF32(&mdok, recipe, "DARK.RATE"); // Dark rate
+    if (isnan(darkRate)) {
+      psError(PS_ERR_UNKNOWN, false, "missing DARK.RATE\n");
+      return false;
+    }
+
+    float expTime  = psMetadataLookupF32(&mdok, recipe, "EXPTIME"); // Exposure time
+    if (isnan(expTime)) {
+      psError(PS_ERR_UNKNOWN, false, "missing EXPTIME\n");
+      return false;
+    }
+
+    psTrace("ppSim", 6, "darkRate: %f, expTime: %f\n", darkRate, expTime);
+
+    for (int y = 0; y < signal->numRows; y++) {
+	for (int x = 0; x < signal->numCols; x++) {
+			
+	    // Dark current
+	    float darkCurrent = darkRate * expTime; // Dark current accumulated
+	    signal->data.F32[y][x] += darkCurrent;
+	    variance->data.F32[y][x] += darkCurrent;
+	}
+    }
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeGalaxies.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeGalaxies.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeGalaxies.c	(revision 22322)
@@ -0,0 +1,141 @@
+# include "ppSim.h"
+
+bool ppSimMakeGalaxies(psArray *galaxies, pmFPA *fpa, pmConfig *config, const psRandom *rng) {
+
+    bool mdok;
+    assert (galaxies);
+
+    psMetadata *recipe = psMetadataLookupMetadata(&mdok, config->recipes, PPSIM_RECIPE); // Recipe
+
+    bool galaxyFake = psMetadataLookupBool(&mdok, recipe, "GALAXY.FAKE"); // Density of fakes
+    if (!galaxyFake) return true;
+
+    float galaxyLum       = psMetadataLookupF32(&mdok, recipe, "GALAXY.LUM"); // Galaxy luminosity func slope
+    float galaxyMag       = psMetadataLookupF32(&mdok, recipe, "GALAXY.MAG"); // Galaxy brightest magnitude
+    float galaxyDensity   = psMetadataLookupF32(&mdok, recipe, "GALAXY.DENSITY"); // Density of fakes
+
+    bool galaxyGrid  	  = psMetadataLookupBool(&mdok, recipe, "GALAXY.GRID"); // Density of fakes
+    int galaxyGridDX 	  = psMetadataLookupS32(&mdok, recipe, "GALAXY.GRID.DX"); // Density of fakes
+    int galaxyGridDY 	  = psMetadataLookupS32(&mdok, recipe, "GALAXY.GRID.DY"); // Density of fakes
+    
+    float galaxyRmajorMax = psMetadataLookupF32(&mdok, recipe, "GALAXY.RMAJOR.MAX"); // Density of fakes
+    float galaxyRmajorMin = psMetadataLookupF32(&mdok, recipe, "GALAXY.RMAJOR.MIN"); // Density of fakes
+
+    float galaxyARatioMax = psMetadataLookupF32(&mdok, recipe, "GALAXY.ARATIO.MAX"); // Density of fakes
+    float galaxyARatioMin = psMetadataLookupF32(&mdok, recipe, "GALAXY.ARATIO.MIN"); // Density of fakes
+
+    float galaxyThetaMax  = psMetadataLookupF32(&mdok, recipe, "GALAXY.THETA.MAX"); // Density of fakes
+    float galaxyThetaMin  = psMetadataLookupF32(&mdok, recipe, "GALAXY.THETA.MIN"); // Density of fakes
+
+    float galaxyIndexMin  = psMetadataLookupF32(&mdok, recipe, "GALAXY.INDEX.MIN"); // Density of fakes
+    float galaxyIndexMax  = psMetadataLookupF32(&mdok, recipe, "GALAXY.INDEX.MAX"); // Density of fakes
+
+    float darkRate 	  = psMetadataLookupF32(&mdok, recipe, "DARK.RATE"); // Dark rate
+    float expTime  	  = psMetadataLookupF32(&mdok, recipe, "EXPTIME"); // Exposure time
+    float zp       	  = psMetadataLookupF32(&mdok, recipe, "ZEROPOINT"); // Photometric zero point
+    float seeing   	  = psMetadataLookupF32(&mdok, recipe, "SEEING"); // Seeing sigma (pix)
+    float scale    	  = psMetadataLookupF32(&mdok, recipe, "PIXEL.SCALE") * M_PI / 3600.0 / 180.0; // Plate scale (radians/pixel)
+    float skyRate  	  = psMetadataLookupF32(&mdok, recipe, "SKY.RATE"); // Sky rate
+    if (isnan(skyRate)) {
+	float skyMags = psMetadataLookupF32(&mdok, recipe, "SKY.MAGS");  assert (mdok);
+	skyRate = scale * scale * ppSimMagToFlux (skyMags, zp);
+    }
+
+    if (galaxyDensity <= 0) return true;
+
+    // Size of FPA
+    psRegion *bounds = ppSimFPABounds (fpa);
+    // Size of focal plane (can't I get this from the config?)
+    int xSize = bounds->x1 - bounds->x0;
+    int ySize = bounds->y1 - bounds->y0;
+    psFree(bounds);
+
+    // Grabbing read noise from the recipe rather than the cell, which is a potential danger, but it
+    // shouldn't be too bad.
+    float readnoise = psMetadataLookupF32(&mdok, recipe, "READNOISE"); // Default read noise
+    if (!mdok) {
+	psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find READNOISE in recipe.");
+	psFree(bounds);
+	return false;
+    }
+
+    // Peak fluxes: faintest and brightest levels for random galaxy
+    float faint = 0.1 * 10.0 * sqrt(2.0*M_PI) * seeing * sqrtf(PS_SQR(readnoise) + (darkRate + skyRate) * expTime);
+    float bright = powf(10.0, -0.4 * (galaxyMag - zp)) / sqrt(2.0*M_PI) / seeing * expTime;
+    if (bright < faint) {
+	psLogMsg("ppSim", PS_LOG_INFO,
+		 "Image noise is above brightest random galaxy --- no random galaxy added.");
+	return true;
+    }
+
+    // Normalisation, set by the specified stellar density at the specified bright magnitude
+    float norm = galaxyDensity * xSize * ySize * PS_SQR(scale * 180.0 / M_PI) /
+	powf(bright, galaxyLum);
+
+    // Total number of galaxy down to the faint flux end
+    long num = expf(logf(norm) + galaxyLum * logf(faint)) + 0.5;
+
+    if (galaxyGrid) {
+	num = (int)(xSize / galaxyGridDX) * (int)(ySize / galaxyGridDY);
+    
+	psLogMsg("ppSim", PS_LOG_INFO, "Adding grid of %ld galaxies\n", num);
+
+	float galaxyIndexSlope = (galaxyIndexMax - galaxyIndexMin) / num;
+	float galaxyThetaSlope = (galaxyThetaMax - galaxyThetaMin) / num;
+	float galaxyARatioSlope = (galaxyARatioMax - galaxyARatioMin) / num;
+	float galaxyRmajorSlope = (galaxyRmajorMax - galaxyRmajorMin) / num;
+
+	int i = 0;
+
+	for (long iy = 0.5*galaxyGridDY; iy < ySize; iy += galaxyGridDY) {
+	    for (long ix = 0.5*galaxyGridDX; ix < xSize; ix += galaxyGridDX) {
+		ppSimGalaxy *galaxy = ppSimGalaxyAlloc ();
+
+		// make fpa center of distribution
+		galaxy->x    = ix;
+		galaxy->y    = iy;
+
+		galaxy->peak = 20000;
+
+		galaxy->index = (galaxyIndexMin  + i * galaxyIndexSlope);
+		galaxy->Rmaj  = (galaxyRmajorMin + i * galaxyRmajorSlope);
+		galaxy->Rmin  = (galaxyARatioMin + i * galaxyARatioSlope) * galaxy->Rmaj;
+		galaxy->theta = (galaxyThetaMin  + i * galaxyThetaSlope);
+
+		psArrayAdd (galaxies, 100, galaxy);
+		i++;
+	    }
+	}
+    } else {    
+
+	float galaxyIndexSlope = (galaxyIndexMax - galaxyIndexMin);
+	float galaxyThetaSlope = (galaxyThetaMax - galaxyThetaMin);
+	float galaxyARatioSlope = (galaxyARatioMax - galaxyARatioMin);
+	float galaxyRmajorSlope = (galaxyRmajorMax - galaxyRmajorMin);
+
+	psLogMsg("ppSim", PS_LOG_INFO, "Adding %ld galaxies down to %f mag\n", num,
+		 -2.5 * log10(faint * sqrt(2.0*M_PI) * seeing / expTime) + zp);
+
+	for (long i = 0; i < num; i++) {
+	    ppSimGalaxy *galaxy = ppSimGalaxyAlloc ();
+
+	    // make fpa center of distribution
+	    galaxy->x    = psRandomUniform(rng) * xSize; // x position
+	    galaxy->y    = psRandomUniform(rng) * ySize; // y position
+
+	    galaxy->flux = pow ((double)((i + 1) / norm), (double) (1.0 / galaxyLum));
+
+	    galaxy->index = (psRandomUniform(rng) * galaxyIndexSlope  + galaxyIndexMin);
+	    galaxy->theta = (psRandomUniform(rng) * galaxyThetaSlope  + galaxyThetaMin);
+	    galaxy->Rmaj  = (psRandomUniform(rng) * galaxyRmajorSlope + galaxyRmajorMin);
+	    galaxy->Rmin  = (psRandomUniform(rng) * galaxyARatioSlope + galaxyARatioMin) * galaxy->Rmaj;
+
+	    galaxy->flux = expf((logf(i + 1) - logf(norm)) / galaxyLum); // Peak flux
+	    galaxy->peak = galaxy->flux / (2.0*M_PI*PS_SQR(seeing));
+	    // galaxy->peak = 5000;
+
+	    psArrayAdd (galaxies, 100, galaxy);
+	}
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeSky.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeSky.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeSky.c	(revision 22322)
@@ -0,0 +1,106 @@
+# include "ppSim.h"
+
+// this function sets the skyRate to a value for the night sky (SKY.RATE or SKY.MAGS) or for a
+// flat-field image (FLAT.RATE).  Include a shutter correction and a scattered light source
+
+bool ppSimMakeSky (pmReadout *readout, psImage *expCorr, ppSimType type, pmConfig *config) {
+
+    bool status;
+
+    psImage *signal = readout->image;
+    psImage *variance = readout->weight;
+
+    pmCell *cell = readout->parent;
+    pmChip *chip = cell->parent;
+    pmFPA  *fpa  = chip->parent;
+
+    psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, PPSIM_RECIPE); // Recipe
+
+    bool sky  = psMetadataLookupBool(&status, recipe, "SKY"); // Generate a SKY flux?
+    bool flat = psMetadataLookupBool(&status, recipe, "FLAT"); // Apply flat-field term?
+ 
+    float expTime      = psMetadataLookupF32(&status, recipe, "EXPTIME"); // Exposure time
+
+    float flatSigma    = psMetadataLookupF32(&status, recipe, "FLAT.SIGMA"); // Flat-field coefficient
+    float flatRate     = psMetadataLookupF32(&status, recipe, "FLAT.RATE"); // Flat-field rate
+    float shutterTime  = psMetadataLookupF32(&status, recipe, "SHUTTER.TIME"); // Shutter time
+    float scatterFrac  = psMetadataLookupF32(&status, recipe, "SCATTER.FRAC"); // scattered light fraction (max)
+    float skyRate      = psMetadataLookupF32(&status, recipe, "SKY.RATE"); // Sky rate
+    float skyMags      = psMetadataLookupF32(&status, recipe, "SKY.MAGS");  assert (status);
+    if (!isnan(skyMags)) {
+	float zp       = psMetadataLookupF32(&status, recipe, "ZEROPOINT"); assert (status);
+	float scale    = psMetadataLookupF32(&status, recipe, "PIXEL.SCALE"); assert (status);
+	skyRate = scale * scale * ppSimMagToFlux (skyMags, zp);
+    }
+    if (type == PPSIM_TYPE_FLAT) {
+      skyRate = flatRate;
+    }
+
+    int x0Chip 	      = psMetadataLookupS32(&status, chip->concepts, "CHIP.X0");
+    int y0Chip 	      = psMetadataLookupS32(&status, chip->concepts, "CHIP.Y0");
+    int xParityChip   = psMetadataLookupS32(&status, chip->concepts, "CHIP.XPARITY");
+    int yParityChip   = psMetadataLookupS32(&status, chip->concepts, "CHIP.YPARITY");
+
+    int x0Cell 	      = psMetadataLookupS32(&status, cell->concepts, "CELL.X0");
+    int y0Cell 	      = psMetadataLookupS32(&status, cell->concepts, "CELL.Y0");
+    int xParityCell   = psMetadataLookupS32(&status, cell->concepts, "CELL.XPARITY");
+    int yParityCell   = psMetadataLookupS32(&status, cell->concepts, "CELL.YPARITY");
+
+    int binning = psMetadataLookupS32(NULL, recipe, "BINNING"); // Binning in x and y
+
+    // Size of FPA
+    psRegion *bounds = ppSimFPABounds (fpa);
+    int dXfpa = bounds->x1 - bounds->x0;
+    int dYfpa = bounds->y1 - bounds->y0;
+
+    // Correct chip offsets so that boresight is in the middle of the FPA
+    x0Chip -= 0.5 * dXfpa;
+    y0Chip -= 0.5 * dYfpa;
+
+    for (int y = 0; y < signal->numRows; y++) {
+
+        float yFPA = PPSIM_CELL_TO_FPA(y, y0Cell, yParityCell, binning, y0Chip, yParityChip) * 2.0 /
+            (bounds->y1 - bounds->y0); // Relative y position in FPA
+
+        for (int x = 0; x < signal->numCols; x++) {
+            float xFPA = PPSIM_CELL_TO_FPA(x, x0Cell, xParityCell, binning, x0Chip, xParityChip) * 2.0 /
+                (bounds->x1 - bounds->x0); // Relative x position in FPA
+
+            // Shutter: adjust exposure time
+            float realExpTime = expTime + shutterTime * (xFPA + yFPA + 2.0) / 4.0;
+
+            // Gaussian flat-field over the FPA with flatValue = 1.0 at the field center
+            float flatValue = 1.0;
+	    if (flat) {
+		// we make the flat-field have a response of 1.0 at the field center (like a vignetting)
+		flatValue = expf(-0.5 / PS_SQR(flatSigma) * (PS_SQR(yFPA) + PS_SQR(xFPA)));
+	    }
+
+	    float scatterRate = 0.0;
+
+	    if (sky) {
+	      // add a scattered light term to the flat-field images
+	      if (type == PPSIM_TYPE_FLAT) {
+		float xF = 2.0*(xFPA / dXfpa) - 1.0;
+		scatterRate = scatterFrac * PS_SQR(xF);
+	      }
+
+	      // Sky background
+	      float skyFlux = (skyRate * (flatValue + scatterRate)) * realExpTime; // Flux from sky
+	      signal->data.F32[y][x] += skyFlux;
+	      variance->data.F32[y][x] += skyFlux;
+	    }
+
+	    // used later to modify the star and galaxy photometry 
+	    if (expCorr) {
+	      expCorr->data.F32[y][x] = realExpTime / expTime;
+	    }
+
+            // TO DO: Add fringes
+
+        }
+    }
+    psFree(bounds);
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeStars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeStars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMakeStars.c	(revision 22322)
@@ -0,0 +1,147 @@
+# include "ppSim.h"
+
+# define ESCAPE(MSG) { \
+  psError(PS_ERR_BAD_PARAMETER_VALUE, true, MSG); \
+  return false; }
+
+bool ppSimMakeStars(psArray *stars, pmFPA *fpa, pmConfig *config, const psRandom *rng) {
+
+    bool status;
+    assert (stars);
+
+    psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, PPSIM_RECIPE); // Recipe
+
+    bool starsFake = psMetadataLookupBool(&status, recipe, "STARS.FAKE"); // Density of fakes
+    if (!starsFake) return true;
+
+    bool starsReal     = psMetadataLookupBool(&status, recipe, "STARS.REAL"); // were real stars generated?
+    bool matchDensity  = psMetadataLookupBool(&status, recipe, "MATCH.DENSITY"); // match real star density?
+
+    float starsAlpha   = psMetadataLookupF32(&status, recipe, "STARS.LUM"); // Star luminosity func slope
+    float starsDensity = psMetadataLookupF32(&status, recipe, "STARS.DENSITY"); // Density of fakes
+    float brightMag    = psMetadataLookupF32(&status, recipe, "STARS.MAG"); // Star brightest magnitude
+    float nSigmaLim    = psMetadataLookupF32(&status, recipe, "STARS.SIGMA.LIM"); // significance of faintest stars
+
+    float darkRate     = psMetadataLookupF32(&status, recipe, "DARK.RATE"); // Dark rate
+    float expTime      = psMetadataLookupF32(&status, recipe, "EXPTIME"); // Exposure time
+    float zp           = psMetadataLookupF32(&status, recipe, "ZEROPOINT"); // Photometric zero point
+    float seeing       = psMetadataLookupF32(&status, recipe, "SEEING"); // Seeing SIGMA (pixels)
+    float scale        = psMetadataLookupF32(&status, recipe, "PIXEL.SCALE") * M_PI / 3600.0 / 180.0; // Plate scale (radians/pixel)
+
+    if (isnan(darkRate)) darkRate = 0.0;
+    if (isnan(expTime))  ESCAPE("EXPTIME is not defined");
+    if (isnan(zp))       ESCAPE("ZEROPOINT is not defined");
+    if (isnan(seeing))   ESCAPE("SEEING is not defined");
+    if (isnan(scale))    ESCAPE("PIXEL.SCALE is not defined");
+
+    bool flatLum       = psMetadataLookupBool(&status,recipe, "STARS.FLAT.LUM"); // were real stars generated?
+    float flatNum      = psMetadataLookupS32(&status, recipe, "STARS.FLAT.NUM"); // were real stars generated?
+
+    float skyRate = psMetadataLookupF32(&status, recipe, "SKY.RATE"); // Sky rate
+    if (isnan(skyRate)) {
+	float skyMags = psMetadataLookupF32(&status, recipe, "SKY.MAGS");  assert (status);
+	skyRate = scale * scale * ppSimMagToFlux (skyMags, zp);
+    }
+
+    // Size of FPA
+    psRegion *bounds = ppSimFPABounds (fpa);
+    // Size of focal plane (can't I get this from the config?)
+    int xSize = bounds->x1 - bounds->x0;
+    int ySize = bounds->y1 - bounds->y0;
+    psFree(bounds);
+
+    // choose reference magnitude & density to set normalization
+    float refMag = 0;
+    float refSum = 0;
+    if (starsReal && matchDensity) {
+	refMag = psMetadataLookupF32(&status, fpa->concepts, "STARS.REAL.MAG.PEAK"); // Star brightest magnitude
+	refSum = psMetadataLookupF32(&status, fpa->concepts, "STARS.REAL.SUM.PEAK"); // Star brightest magnitude
+
+	// if we tried and failed to load reference stars, set more artificial limits
+	if (!status) {
+	    refMag = brightMag;
+	    refSum = 1;
+	}
+    } else {
+	refMag = brightMag;
+	refSum = starsDensity * xSize * ySize * PS_SQR(scale * 180.0 / M_PI);
+    }
+    psTrace("ppSim", 6, "refMag: %f, refSum: %f\n", refMag, refSum);
+
+    if (refSum <= 0) return true;
+
+    // Grabbing read noise from the recipe rather than the cell, which is a potential danger, but it
+    // shouldn't be too bad.
+    float readnoise = psMetadataLookupF32(&status, recipe, "READNOISE"); // Default read noise
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find READNOISE in recipe.");
+        psFree(bounds);
+        return false;
+    }
+
+    // Faintest and brightest (integrated) fluxes (actually fluence, since integrated) for random stars
+
+    // faint limit is set by detection limit, in turn set by sky noise
+    float skySigma = sqrtf(PS_SQR(readnoise) + (darkRate + skyRate) * expTime);
+    float skyNoise = ppSimStarSkyNoise (skySigma, seeing);
+
+    // total flux of faintest star:
+    float faintCounts = nSigmaLim * skyNoise;
+    float faintMag = ppSimFluxToMag ((faintCounts / expTime), zp);
+
+    float brightCounts = ppSimMagToFlux (brightMag, zp) * expTime; // Bright limit is specified by user as mag
+
+    psTrace("ppSim", 6, "Faint limit: %f counts = %f mags\n", faintCounts, faintMag);
+    psTrace("ppSim", 6, "Bright limit: %f counts = %f mags\n", brightCounts, brightMag);
+    if (brightCounts < faintCounts) {
+        psLogMsg("ppSim", PS_LOG_INFO,
+                 "Image noise is above brightest random star --- no random stars added.");
+        return true;
+    }
+
+    // given log_10 (dN / dmag) = alpha mag + beta
+    // or dN / dmag = No 10^(alpha mag), then:
+    // N(m < Mo) = alpha * No * log(10.0) * 10^(alpha*Mo)
+
+    // XXX this needs to handle the case of a flat distribution for efficiency testing
+
+    // Normalization, set by the specified stellar density at the specified bright magnitude
+    float norm = refSum / (starsAlpha * logf(10.0) * powf(10.0, (starsAlpha * refMag)));
+    float normScale = norm * starsAlpha * logf(10.0);
+
+    // Total number of stars down to the faint flux end
+    long nTotal = 0;
+    if (flatLum) {
+	nTotal = flatNum;
+    } else {
+	nTotal = normScale * powf (10.0, (starsAlpha * faintMag));
+    }
+
+    psLogMsg("ppSim", PS_LOG_INFO, "Adding %ld stars between %f and %f mag\n", nTotal, brightMag, faintMag);
+
+    long oldSize = stars->n;
+    psArrayRealloc (stars, stars->n + nTotal);
+
+    for (long i = 0; i < nTotal; i++) {
+        ppSimStar *star = ppSimStarAlloc ();
+
+        // make fpa center of distribution
+        star->x    = psRandomUniform(rng) * xSize; // x position
+        star->y    = psRandomUniform(rng) * ySize; // y position
+
+	float starMag = 0;
+	if (flatLum) {
+	    starMag = psRandomUniform(rng) * (faintMag - brightMag) + brightMag;
+	} else {
+	    starMag = PS_MIN (faintMag, PS_MAX (brightMag, (log10((i + 1.0) / normScale) / starsAlpha)));
+	}
+	
+        star->flux = ppSimMagToFlux (starMag, zp) * expTime;
+        star->peak = ppSimStarFluxToPeak (star->flux, seeing);
+
+        stars->data[oldSize + i] = star;
+    }
+    stars->n = oldSize + nTotal;
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMergeReadouts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMergeReadouts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMergeReadouts.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "ppSim.h"
+
+// XXX add bounds to the inputs?
+bool ppSimMergeReadouts (pmConfig *config, pmFPAview *view) {
+
+    // bool mdok;
+
+    // if we have an input image, we need to add the synthetic data on top of it below
+    // XXX we may potentially use this input file to skip missing elements
+    pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSIM.INPUT");
+    if (!input) return true;
+
+    pmReadout *inReadout = pmFPAviewThisReadout (view, input->fpa);
+    if (!inReadout) return true;
+
+    if (!inReadout->weight) {
+      if (!pmReadoutGenerateWeight(inReadout, true)) {
+	psError (PS_ERR_UNKNOWN, false, "trouble creating weight");
+	return false;
+      }
+    }
+
+    // output must exist or we made a programming error
+    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PPSIM.OUTPUT"); // Output file
+    assert(output);
+
+    // XXX require outReadout?
+    pmReadout *outReadout = pmFPAviewThisReadout (view, output->fpa);
+    if (!outReadout) return true;
+
+    psImage *inSignal = inReadout->image;
+    psImage *inVariance = inReadout->weight;
+
+    psImage *outSignal = outReadout->image;
+    psImage *outVariance = outReadout->weight;
+
+    assert (inSignal->numRows == outSignal->numRows);
+    assert (inSignal->numCols == outSignal->numCols);
+
+    for (int y = 0; y < inSignal->numRows; y++) {
+	for (int x = 0; x < inSignal->numCols; x++) {
+	    outSignal->data.F32[y][x] += inSignal->data.F32[y][x];
+	    outVariance->data.F32[y][x] += inVariance->data.F32[y][x];
+	}
+    }
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMergeSources.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMergeSources.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMergeSources.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "ppSim.h"
+
+psArray *ppSimMergeSources (psArray *in1, psArray *in2) {
+
+  psArray *out = psArrayAlloc (in1->n + in2->n);
+
+  for (int i = 0; i < in1->n; i++) {
+    out->data[i] = psMemIncrRefCounter (in1->data[i]);
+  }
+
+  int nOff = in1->n;
+  for (int i = nOff; i < out->n; i++) {
+    out->data[i] = psMemIncrRefCounter (in2->data[i-nOff]);
+  }
+
+  return out;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMosaicChip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMosaicChip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimMosaicChip.c	(revision 22322)
@@ -0,0 +1,36 @@
+#include "ppSim.h"
+
+// XXX this is essentially identical to ppImageMosaicChip
+bool ppSimMosaicChip(pmConfig *config, const psMaskType blankMask, const pmFPAview *view,
+                       const char *outFile, const char *inFile)
+{
+    bool status;                        // Status of MD lookup
+
+    pmFPAfile *in = psMetadataLookupPtr(&status, config->files, inFile); // Input file
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find required I/O file!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAfile *out = psMetadataLookupPtr(&status, config->files, outFile); // Output file
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find required I/O file!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmChip *outChip = pmFPAviewThisChip(view, out->fpa);
+    pmChip *inChip = pmFPAviewThisChip(view, in->fpa);
+    if (!outChip->hdu && !outChip->parent->hdu) {
+        const char *name = psMetadataLookupStr(&status, in->fpa->concepts, "FPA.OBS"); // Name of FPA
+        pmFPAAddSourceFromView(out->fpa, name, view, out->format);
+    }
+
+    psTrace("pmChipMosaic", 5, "mosaic chip %s to %s (xbin,ybin: %d,%d to %d,%d)\n",
+            in->name, out->name, in->xBin, in->yBin, out->xBin, out->yBin);
+
+    // XXX mosaic the chip, making a deep copy.  this has the side effect of making the
+    // output image products pure trimmed images, but also increases the memory footprint.
+    status = pmChipMosaic(outChip, inChip, true, blankMask);
+    return status;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotom.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "ppSim.h"
+
+// In this function, we perform the psphot analysis routine for the chip-mosaicked images
+bool ppSimPhotom (pmConfig *config, pmFPAview *view) {
+
+    bool status;
+    pmCell *cell;
+    pmReadout *readout;
+
+    psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, PPSIM_RECIPE); // Recipe
+
+    bool doPhotom = psMetadataLookupBool(&status, recipe, "PHOTOM"); // Density of fakes
+    if (!doPhotom) return true;
+    
+    psphotModelClassInit ();
+
+    int blankMask = 0;		// XXX not sure what this should be set to...
+    ppSimMosaicChip(config, blankMask, view, "PPSIM.FORCE.CHIP", "PPSIM.INPUT");
+    ppSimMosaicChip(config, blankMask, view, "PPSIM.FAKE.CHIP", "PPSIM.OUTPUT");
+
+    // use PPSIM.FAKE.CHIP as the base since it is guaranteed to exist (derived from PPSIM.OUTPUT)
+    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PPSIM.FAKE.CHIP");
+    if (!status) {
+        psError(PSPHOT_ERR_CONFIG, false, "PPSIM.FAKE.CHIP I/O file is not defined");
+        return false;
+    }
+
+    // XXX If we want to be able to write out both the positive and residual images, we will
+    // need to define an additional pmFPAfile container, and then make a copy as is done for
+    // PPIMAGE.CHIP -> PSPHOT.INPUT in ppImage.  At the moment, PPSIM.CHIP will write out the
+    // mosaicked image with the sources subtracted.
+
+    // XXX if input -> PSPHOT.INPUT, which is tied to PPSIM.CHIP with pmFPAfileDefineFromFile,
+    // then the following code creates the chip copy:
+    // pmChip *oldChip = pmFPAviewThisChip (view, input->src);
+    // pmChip *newChip = pmFPAviewThisChip (view, input->fpa);
+    // pmChipCopy (newChip, oldChip);
+
+    // iterate over the cells and readout for this chip
+    while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
+        psLogMsg ("ppSimPhotom", 5, "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, input->fpa, 1)) != NULL) {
+            if (! readout->data_exists) { continue; }
+
+            // run the actual photometry analysis
+            if (!ppSimPhotomReadoutFake (config, view)) {
+                psError(psErrorCodeLast(), false, "failure in psphotReadout for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
+                return false;
+            }
+            if (!ppSimPhotomReadoutForce (config, view)) {
+                psError(psErrorCodeLast(), false, "failure in psphotReadout for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
+                return false;
+            }
+	    // ppSimDetectionLimits (config, view);
+        }
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomFiles.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomFiles.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomFiles.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "ppSim.h"
+
+// define the needed / desired I/O files
+bool ppSimPhotomFiles (pmConfig *config, pmFPAfile *fakeFile, pmFPAfile *forceFile) {
+
+    bool status = false;
+
+    // select recipe options supplied on command line
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+
+    // optionally save the background model (small FITS image)
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.BACKMDL")) {
+        int DX = psMetadataLookupS32 (&status, recipe, "BACKGROUND.XBIN");
+        int DY = psMetadataLookupS32 (&status, recipe, "BACKGROUND.YBIN");
+        pmFPAfile *output = pmFPAfileDefineFromFile (config, fakeFile, DX, DY, "PSPHOT.BACKMDL");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.BACKMDL");
+            return false;
+        }
+        output->save = true;
+    }
+    // optionally save the background model's standard deviation (small FITS image)
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.BACKMDL.STDEV")) {
+        int DX = psMetadataLookupS32 (&status, recipe, "BACKGROUND.XBIN");
+        int DY = psMetadataLookupS32 (&status, recipe, "BACKGROUND.YBIN");
+        pmFPAfile *output = pmFPAfileDefineFromFile (config, fakeFile, DX, DY, "PSPHOT.BACKMDL.STDEV");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.BACKMDL.STDEV");
+            return false;
+        }
+        output->save = true;
+    }
+    // optionally save the full background image
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.BACKGND")) {
+        pmFPAfile *output = pmFPAfileDefineFromFile (config, fakeFile,  1,  1, "PSPHOT.BACKGND");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.BACKGND");
+            return false;
+        }
+        output->save = true;
+    }
+    // optionally save the background-subtracted image
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.BACKSUB")) {
+        pmFPAfile *output = pmFPAfileDefineFromFile (config, fakeFile,  1,  1, "PSPHOT.BACKSUB");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.BACKSUB");
+            return false;
+        }
+        output->save = true;
+    }
+
+    // the input sources are required for sensible measurements of the fake or force sources
+    pmFPAfile *cmffile = pmFPAfileDefineFromArgs (&status, config, "PPSIM.REAL.SOURCES", "INPUT.SOURCES");
+    if (!cmffile) {
+      psError(PS_ERR_UNKNOWN, false, "input sources not found; required for photometry (or error with PPSIM.REAL.SOURCES filerule)");
+      return false;
+    }
+
+    // the fake sources are carried on the fakeFile->fpa structures (PPSIM.FAKE.CHIP)
+    pmFPAfile *outsources = pmFPAfileDefineOutput (config, fakeFile->fpa, "PPSIM.FAKE.SOURCES");
+    if (!outsources) {
+        psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PPSIM.FAKE.SOURCES");
+        return false;
+    }
+    outsources->save = true;
+
+    // the forced sources are carried on the forceFile->fpa structures (PPSIM.FORCE.CHIP)
+    pmFPAfile *outforced = pmFPAfileDefineOutput (config, forceFile->fpa, "PPSIM.FORCE.SOURCES");
+    if (!outforced) {
+        psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PPSIM.FORCE.SOURCES");
+        return false;
+    }
+    outforced->save = true;
+
+    // we bind the psffile to the chip-mosaicked image so we have a reference to the image 
+    pmFPAfile *psffile = pmFPAfileBindFromArgs(&status, fakeFile, config, "PSPHOT.PSF.LOAD", "PSPHOT.PSF");
+    if (!psffile) {
+      psError(PSPHOT_ERR_CONFIG, false, "Failed to find/build PSPHOT.PSF.LOAD");
+      return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomReadout.c	(revision 22322)
@@ -0,0 +1,184 @@
+# include "ppSim.h"
+
+psArray *ppSimSelectSources (pmConfig *config, const pmFPAview *view, const char *filename) {
+
+    pmReadout *readout = pmFPAfileThisReadout (config->files, view, filename);
+    PS_ASSERT_PTR_NON_NULL (readout, NULL);
+
+    psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
+    return sources;
+}
+
+bool ppSimDefinePixels (psArray *sources, pmReadout *readout, psMetadata *recipe) {
+
+    bool status;
+
+    float OUTER = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
+    if (!status) return NULL;
+
+    for (int i = 0; i < sources->n; i++) {
+	pmSource *source = sources->data[i];
+
+        // allocate image, weight, mask arrays for each peak (square of radius OUTER)
+        pmSourceDefinePixels (source, readout, source->peak->x, source->peak->y, OUTER);
+    }
+    return true;
+}
+
+bool ppSimPhotomReadout(pmConfig *config, const pmFPAview *view) {
+
+    psTimerStart ("psphotReadout");
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+# if 0    
+    // set the photcode for this image
+    if (!psphotAddPhotcode (recipe, config, view, "PPSIM.CHIP")) {
+        psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
+        return false;
+    }
+# endif
+
+    // find the currently selected readout. 
+    // we always perform photometry on the mosaiced chip
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PPSIM.CHIP");
+    PS_ASSERT_PTR_NON_NULL (readout, false);
+
+    // the previously measured real sources are loaded into the PPSIM.REAL.SOURCES pmFPAfile
+    psArray *realSources = ppSimSelectSources (config, view, "PPSIM.REAL.SOURCES");
+    PS_ASSERT_PTR_NON_NULL (realSources, false);
+
+    // the newly injected fake sources are saved on the PPSIM.OUTPUT pmFPAfile
+    psArray *injectedSources = ppSimSelectSources (config, view, "PPSIM.OUTPUT");
+    PS_ASSERT_PTR_NON_NULL (injectedSources, false);
+
+    // XXX need to define the source pixels
+    ppSimDefinePixels (realSources, readout, recipe);
+    ppSimDefinePixels (injectedSources, readout, recipe);
+
+    // make a copy of the fake-source parameters so these can be saved independently from the
+    // measured fake-source parameters
+    psArray *fakeSources = psArrayAlloc (injectedSources->n);
+    for (int i = 0; i < injectedSources->n; i++) {
+      fakeSources->data[i] = pmSourceCopy (injectedSources->data[i]);
+    }
+
+    // load the forced source lists
+    psArray *forceSources = ppSimLoadForceSources (config, view);
+    psAssert (forceSources, "failed to load force photometry sources");
+
+    // Generate the mask and weight images, including the user-defined analysis region of interest
+    psphotSetMaskAndWeight (config, readout, recipe);
+
+    // load the psf model, if suppled.  FWHM_X,FWHM_Y,etc are saved in the recipe
+    // this function uses PSPHOT.PSF.LOAD as the pmFPAfile 
+    pmPSF *psf = psphotLoadPSF (config, view, recipe);
+    assert (psf);
+
+    // remove all sources 
+    psphotRemoveAllSources (realSources, recipe);
+
+    // generate a background model (median, smoothed image)
+    if (!psphotModelBackground (config, view, "PPSIM.CHIP")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+    if (!psphotSubtractBackground (config, view, "PPSIM.CHIP")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+
+    // add noise for real (subtracted) objects
+    psphotAddNoise (readout, realSources, recipe);
+
+    // XXX fake sources should measure peak->x,y, force sources should not
+    psMaskType maskVal = 0xff; 
+    psImage *significance = psphotSignificanceImage (readout, recipe, 1, maskVal);
+    ppSimDetections (significance, recipe, fakeSources);
+    ppSimDetections (significance, recipe, forceSources);
+    psFree (significance);
+
+    // remove noise for subtracted objects (ie, return to normal noise level)
+    psphotSubNoise (readout, realSources, recipe);
+
+    // replace all sources
+    psphotReplaceAllSources (realSources, recipe);
+
+    // construct an initial model for each object
+    psphotGuessModels (readout, realSources, recipe, psf);
+    psphotGuessModels (readout, fakeSources, recipe, psf);
+    psphotGuessModels (readout, forceSources, recipe, psf);
+    
+    psArray *sources = NULL;
+
+    // linear fit to real + fake sources
+    sources = ppSimMergeSources (realSources, fakeSources);
+    psphotFitSourcesLinear (readout, sources, recipe, psf, FALSE); // XXX option to NOT subtract
+    psphotReplaceAllSources (sources, recipe);
+    psFree (sources); // only frees the merged references
+
+    // linear fit to real + forced sources (if any were selected)
+    if (forceSources->n) {
+      sources = ppSimMergeSources (realSources, forceSources);
+      psphotFitSourcesLinear (readout, sources, recipe, psf, FALSE); // XXX option to NOT subtract
+      psphotReplaceAllSources (sources, recipe);
+      psFree (sources); // only frees the merged references
+    }
+
+    // XXX do we need to measure aperture photometry corrections?
+    // XXX do we store the pre and post correction magnitudes?
+    // XXX for the fake sources, these must be identically zero
+    // XXX for the force sources, need the apresid to put the mags on the correct system
+    if (0) {
+	if (!psphotApResid (readout, forceSources, recipe, psf)) {
+	    psLogMsg ("psphot", 3, "failed on psphotApResid");
+	    return psphotReadoutCleanup (config, readout, recipe, NULL, psf, NULL);
+	}
+    }
+
+    // calculate source magnitudes (for which set??)
+    pmReadout *background = psphotSelectBackground (config, view, false);
+    psphotMagnitudes(fakeSources, recipe, psf, background);
+    psphotMagnitudes(forceSources, recipe, psf, background);
+
+    // drop the references to the image pixels held by each source
+    psphotSourceFreePixels (realSources);
+    psphotSourceFreePixels (fakeSources);
+    psphotSourceFreePixels (forceSources);
+
+    // create the exported-metadata and free local data
+    // XXX this places the sources on readout->analysis as PSPHOT.SOURCES.  modify?
+    // (or don't supply the sources, and do this with a different function)
+    psphotReadoutCleanup(config, readout, recipe, NULL, psf, NULL);
+
+    // add forceSources to the readout (real and fake already there)
+    // these are outputs: we need to generate the fpa structure
+    
+    pmCell    *forceCell    = pmFPAfileThisCell (config->files, view, "PPSIM.FORCE.SOURCES"); psAssert (forceCell, "no cell?");
+    pmChip    *forceChip    = pmFPAfileThisChip (config->files, view, "PPSIM.FORCE.SOURCES"); psAssert (forceChip, "no chip?");
+    pmReadout *forceReadout = pmFPAfileThisReadout (config->files, view, "PPSIM.FORCE.SOURCES");
+    if (!forceReadout) {
+	forceReadout = pmReadoutAlloc (forceCell);
+	psFree (forceReadout); // there is a copy on 'cell' as well
+    }
+    psAssert (forceReadout, "no forceReadout?");
+    pmChipSetDataStatus (forceChip, true);
+    psMetadataAddArray (forceReadout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_META_REPLACE, "forced photometry ", forceSources);
+    psFree (forceSources);
+
+    pmCell    *fakeCell    = pmFPAfileThisCell (config->files, view, "PPSIM.FAKE.SOURCES"); psAssert (fakeCell, "no cell?");
+    pmChip    *fakeChip    = pmFPAfileThisChip (config->files, view, "PPSIM.FAKE.SOURCES"); psAssert (fakeChip, "no chip?");
+    pmReadout *fakeReadout = pmFPAfileThisReadout (config->files, view, "PPSIM.FAKE.SOURCES");
+    if (!fakeReadout) {
+	fakeReadout = pmReadoutAlloc (fakeCell);
+	psFree (fakeReadout); // there is a copy on 'cell' as well
+    }
+    psAssert (fakeReadout, "no fakeReadout?");
+    pmChipSetDataStatus (fakeChip, true);
+    psMetadataAddArray (fakeReadout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_META_REPLACE, "fake photometry ", fakeSources);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomReadoutFake.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomReadoutFake.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomReadoutFake.c	(revision 22322)
@@ -0,0 +1,124 @@
+# include "ppSim.h"
+
+bool ppSimPhotomReadoutFake(pmConfig *config, const pmFPAview *view) {
+
+    psTimerStart ("psphotReadout");
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+# if 0    
+    // set the photcode for this image
+    if (!psphotAddPhotcode (recipe, config, view, "PPSIM.FAKE.CHIP")) {
+        psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
+        return false;
+    }
+# endif
+
+    // *** in this section, perform the photometry for real + fake sources on PPSIM.FAKE.CHIP ***
+
+    // find the currently selected readout. 
+    // we always perform photometry on the mosaiced chip
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PPSIM.FAKE.CHIP");
+    PS_ASSERT_PTR_NON_NULL (readout, false);
+
+    // the previously measured real sources are loaded into the PPSIM.REAL.SOURCES pmFPAfile
+    psArray *realMeasuredSources = ppSimSelectSources (config, view, "PPSIM.REAL.SOURCES");
+    PS_ASSERT_PTR_NON_NULL (realMeasuredSources, false);
+
+    // the newly injected fake sources are saved on the PPSIM.OUTPUT pmFPAfile
+    psArray *injectedSources = ppSimSelectSources (config, view, "PPSIM.OUTPUT");
+    PS_ASSERT_PTR_NON_NULL (injectedSources, false);
+
+    // Generate the mask and weight images, including the user-defined analysis region of interest
+    psphotSetMaskAndWeight (config, readout, recipe);
+
+    // XXX need to define the source pixels
+    ppSimDefinePixels (realMeasuredSources, readout, recipe);
+    ppSimDefinePixels (injectedSources, readout, recipe);
+
+    // make a copy of the injected fake-source parameters so these can be saved independently
+    // from the measured fake-source parameters
+    psArray *fakeSources = psArrayAlloc (injectedSources->n);
+    for (int i = 0; i < injectedSources->n; i++) {
+      fakeSources->data[i] = pmSourceCopy (injectedSources->data[i]);
+    }
+
+    // make a copy of the measured real-source parameters so these can be fitted here and in
+    // ppSimPhotomReadoutForce, if desired.
+    psArray *realSources = psArrayAlloc (realMeasuredSources->n);
+    for (int i = 0; i < realMeasuredSources->n; i++) {
+	realSources->data[i] = pmSourceCopy (realMeasuredSources->data[i]);
+    }
+
+    // load the psf model, if suppled.  FWHM_X,FWHM_Y,etc are saved in the recipe
+    // this function uses PSPHOT.PSF.LOAD as the pmFPAfile 
+    pmPSF *psf = psphotLoadPSF (config, view, recipe);
+    assert (psf);
+
+    // remove all sources 
+    psphotRemoveAllSources (realSources, recipe);
+
+    // generate a background model (median, smoothed image)
+    if (!psphotModelBackground (config, view, "PPSIM.FAKE.CHIP")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+    if (!psphotSubtractBackground (config, view, "PPSIM.FAKE.CHIP")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+
+    // add noise for real (subtracted) objects
+    psphotAddNoise (readout, realSources, recipe);
+
+    // XXX fake sources should measure peak->x,y, force sources should not
+    psMaskType maskVal = 0xff; 
+    psImage *significance = psphotSignificanceImage (readout, recipe, 1, maskVal);
+    ppSimDetections (significance, recipe, fakeSources);
+    psFree (significance);
+
+    // remove noise for subtracted objects (ie, return to normal noise level)
+    psphotSubNoise (readout, realSources, recipe);
+
+    // replace all sources
+    psphotReplaceAllSources (realSources, recipe);
+
+    // construct an initial model for each object
+    psphotGuessModels (readout, realSources, recipe, psf);
+    psphotGuessModels (readout, fakeSources, recipe, psf);
+    
+    // linear fit to real + fake sources
+    psArray *sources = ppSimMergeSources (realSources, fakeSources);
+    psphotFitSourcesLinear (readout, sources, recipe, psf, FALSE); // XXX option to NOT subtract
+    psphotReplaceAllSources (sources, recipe);
+    psFree (sources); // only frees the merged references
+
+    // calculate source magnitudes (for which set??)
+    pmReadout *background = psphotSelectBackground (config, view, false);
+    psphotMagnitudes(fakeSources, recipe, psf, background);
+
+    // drop the references to the image pixels held by each source
+    psphotSourceFreePixels (realSources);
+    psphotSourceFreePixels (fakeSources);
+
+    // create the exported-metadata and free local data
+    // XXX this places the sources on readout->analysis as PSPHOT.SOURCES.  modify?
+    // (or don't supply the sources, and do this with a different function)
+    psphotReadoutCleanup(config, readout, recipe, NULL, psf, NULL);
+
+    pmCell    *fakeCell    = pmFPAfileThisCell (config->files, view, "PPSIM.FAKE.SOURCES"); psAssert (fakeCell, "no cell?");
+    pmChip    *fakeChip    = pmFPAfileThisChip (config->files, view, "PPSIM.FAKE.SOURCES"); psAssert (fakeChip, "no chip?");
+    pmReadout *fakeReadout = pmFPAfileThisReadout (config->files, view, "PPSIM.FAKE.SOURCES");
+    if (!fakeReadout) {
+	fakeReadout = pmReadoutAlloc (fakeCell);
+	psFree (fakeReadout); // there is a copy on 'cell' as well
+    }
+    psAssert (fakeReadout, "no fakeReadout?");
+    pmChipSetDataStatus (fakeChip, true);
+    psMetadataAddArray (fakeReadout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_META_REPLACE, "fake photometry ", fakeSources);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomReadoutForce.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomReadoutForce.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimPhotomReadoutForce.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include "ppSim.h"
+
+bool ppSimPhotomReadoutForce(pmConfig *config, const pmFPAview *view) {
+
+    psTimerStart ("psphotReadout");
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+# if 0    
+    // set the photcode for this image
+    if (!psphotAddPhotcode (recipe, config, view, "PPSIM.FAKE.CHIP")) {
+        psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
+        return false;
+    }
+# endif
+
+    // *** in this section, perform the photometry for real + force sources on PPSIM.FORCE.CHIP ***
+
+    // find the currently selected readout. 
+    // we always perform photometry on the mosaiced chip
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PPSIM.FORCE.CHIP");
+    PS_ASSERT_PTR_NON_NULL (readout, false);
+
+    // the previously measured real sources are loaded into the PPSIM.REAL.SOURCES pmFPAfile
+    psArray *realMeasuredSources = ppSimSelectSources (config, view, "PPSIM.REAL.SOURCES");
+    PS_ASSERT_PTR_NON_NULL (realMeasuredSources, false);
+
+    // load the forced source lists
+    psArray *forceSources = ppSimLoadForceSources (config, view);
+    psAssert (forceSources, "failed to load force photometry sources");
+
+    // Generate the mask and weight images, including the user-defined analysis region of interest
+    psphotSetMaskAndWeight (config, readout, recipe);
+
+    // XXX need to define the source pixels
+    ppSimDefinePixels (realMeasuredSources, readout, recipe);
+    ppSimDefinePixels (forceSources, readout, recipe);
+
+    // make a copy of the measured real-source parameters so these can be fitted here and in
+    // ppSimPhotomReadoutForce, if desired.
+    psArray *realSources = psArrayAlloc (realMeasuredSources->n);
+    for (int i = 0; i < realMeasuredSources->n; i++) {
+	realSources->data[i] = pmSourceCopy (realMeasuredSources->data[i]);
+    }
+
+    // load the psf model, if suppled.  FWHM_X,FWHM_Y,etc are saved in the recipe
+    // this function uses PSPHOT.PSF.LOAD as the pmFPAfile 
+    pmPSF *psf = psphotLoadPSF (config, view, recipe);
+    assert (psf);
+
+    // remove all sources 
+    psphotRemoveAllSources (realSources, recipe);
+
+    // generate a background model (median, smoothed image)
+    if (!psphotModelBackground (config, view, "PPSIM.FORCE.CHIP")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+    if (!psphotSubtractBackground (config, view, "PPSIM.FORCE.CHIP")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+
+    // add noise for real (subtracted) objects
+    psphotAddNoise (readout, realSources, recipe);
+
+    // XXX fake sources should measure peak->x,y, force sources should not
+    psMaskType maskVal = 0xff; 
+    psImage *significance = psphotSignificanceImage (readout, recipe, 1, maskVal);
+    ppSimDetections (significance, recipe, forceSources);
+    psFree (significance);
+
+    // remove noise for subtracted objects (ie, return to normal noise level)
+    psphotSubNoise (readout, realSources, recipe);
+
+    // replace all sources
+    psphotReplaceAllSources (realSources, recipe);
+
+    // construct an initial model for each object
+    psphotGuessModels (readout, realSources, recipe, psf);
+    psphotGuessModels (readout, forceSources, recipe, psf);
+    
+    // linear fit to real + force sources
+    psArray *sources = ppSimMergeSources (realSources, forceSources);
+    psphotFitSourcesLinear (readout, sources, recipe, psf, FALSE); // XXX option to NOT subtract
+    psphotReplaceAllSources (sources, recipe);
+    psFree (sources); // only frees the merged references
+
+    // calculate source magnitudes (for which set??)
+    pmReadout *background = psphotSelectBackground (config, view, false);
+    psphotMagnitudes(forceSources, recipe, psf, background);
+
+    // drop the references to the image pixels held by each source
+    psphotSourceFreePixels (realSources);
+    psphotSourceFreePixels (forceSources);
+
+    // create the exported-metadata and free local data
+    // XXX this places the sources on readout->analysis as PSPHOT.SOURCES.  modify?
+    // (or don't supply the sources, and do this with a different function)
+    psphotReadoutCleanup(config, readout, recipe, NULL, psf, NULL);
+
+    pmCell    *forceCell    = pmFPAfileThisCell (config->files, view, "PPSIM.FORCE.SOURCES"); psAssert (forceCell, "no cell?");
+    pmChip    *forceChip    = pmFPAfileThisChip (config->files, view, "PPSIM.FORCE.SOURCES"); psAssert (forceChip, "no chip?");
+    pmReadout *forceReadout = pmFPAfileThisReadout (config->files, view, "PPSIM.FORCE.SOURCES");
+    if (!forceReadout) {
+	forceReadout = pmReadoutAlloc (forceCell);
+	psFree (forceReadout); // there is a copy on 'cell' as well
+    }
+    psAssert (forceReadout, "no forceReadout?");
+    pmChipSetDataStatus (forceChip, true);
+    psMetadataAddArray (forceReadout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_META_REPLACE, "force photometry ", forceSources);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimRandomGaussian.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimRandomGaussian.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimRandomGaussian.c	(revision 22322)
@@ -0,0 +1,102 @@
+# include "ppSim.h"
+
+static int Ngaussint = 0;
+static double *gaussint = NULL;
+
+extern double drand48();
+
+double p_ppSimGaussian (double x, double mean, double sigma) {
+
+  double f;
+
+  f = exp (-0.5 * PS_SQR(x - mean) / PS_SQR(sigma)) / sqrt(2 * M_PI * PS_SQR(sigma));
+
+  return (f);
+
+}
+
+void ppSimRandomGaussianFree()
+{
+    psFree (gaussint);
+    return;
+}
+
+void ppSimRandomGaussianAlloc (int Nbin) {
+
+    gaussint = (double *) psAlloc(Nbin*sizeof(double));
+    return;
+}
+
+/* integrate a gaussian from -5 sigma to +5 sigma */
+void p_ppSimRandomGaussianInit () {
+ 
+  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;
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  Ngaussint = 0x1000;
+  ppSimRandomGaussianAlloc (Ngaussint + 1);
+
+  val = 0;
+  dx = 1.0 / Ngaussint;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+  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) + 
+          9.0*p_ppSimGaussian(x+dx1, mean, sigma) +
+          9.0*p_ppSimGaussian(x+dx2, mean, sigma) + 
+          3.0*p_ppSimGaussian(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+    if (val > (i + 0.5) / (double) Ngaussint) {
+      gaussint[i] = x + dx / 2.0;
+      i++;
+    }
+  }
+}
+
+// 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 ();
+  }
+
+  y = drand48();
+  i = Ngaussint*y;
+  y = gaussint[i]*sigma + mean;
+ 
+  return (y);
+ 
+}
+ 
+// 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 ();
+  }
+
+  y = drand48();
+  i = Ngaussint*y;
+  y = gaussint[i];
+ 
+  return (y);
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSaturate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSaturate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSaturate.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "ppSim.h"
+
+// Apply saturation limit to image
+bool ppSimSaturate(pmReadout *readout, // Image to apply saturation
+		   const pmConfig *config // configuration data
+    )
+{
+    bool mdok;
+
+    psImage *image = readout->image;
+    pmCell *cell = readout->parent;
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
+
+    float saturation = psMetadataLookupF32(NULL, cell->concepts, "CELL.SATURATION");
+    if (isnan(saturation)) {
+	psWarning("CELL.SATURATION is not set; reverting to recipe value SATURATION.");
+	saturation = psMetadataLookupF32(&mdok, recipe, "SATURATION");
+	if (!mdok) {
+	    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find SATURATION in recipe.");
+	    return false;
+	}
+    }
+
+    for (int y = 0; y < image->numRows; y++) {
+        for (int x = 0; x < image->numCols; x++) {
+            if (image->data.F32[y][x] > saturation) {
+                image->data.F32[y][x] = saturation;
+            }
+        }
+    }
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequence.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequence.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequence.c	(revision 22322)
@@ -0,0 +1,205 @@
+# include "ppSimSequence.h"
+# include <sys/stat.h>
+
+// XXX Memory leaks in string variables
+
+int main (int argc, char **argv) {
+
+    bool status;
+    int argNum;
+    unsigned int nFail;
+
+    psLibInit(NULL);
+
+    char *dbname = NULL;
+    if ((argNum = psArgumentGet (argc, argv, "-dbname"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        dbname = psStringCopy (argv[argNum]);
+        psArgumentRemove(argNum, &argc, argv);
+    }
+
+    char *path = NULL;
+    if ((argNum = psArgumentGet (argc, argv, "-path"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        path = psStringCopy (argv[argNum]);
+        psArgumentRemove(argNum, &argc, argv);
+        psString line = NULL;           // Line to execute
+        psStringAppend(&line, "mkdir -p %s", path);
+        if (!system(line)) {
+            psWarning("Unable to create directory %s", path);
+        }
+        psFree(line);
+    }
+
+    char *camera = NULL;
+    if ((argNum = psArgumentGet (argc, argv, "-camera"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        camera = psStringCopy (argv[argNum]);
+        psArgumentRemove(argNum, &argc, argv);
+    }
+
+    char *workdir = NULL;
+    if ((argNum = psArgumentGet (argc, argv, "-workdir"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        workdir = psStringCopy (argv[argNum]);
+        psArgumentRemove(argNum, &argc, argv);
+    }
+
+    char *ppsim_recipe = NULL;
+    if ((argNum = psArgumentGet (argc, argv, "-ppsim_recipe"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        ppsim_recipe = psStringCopy (argv[argNum]);
+        psArgumentRemove(argNum, &argc, argv);
+    }
+
+    char *dvodb = NULL;
+    if ((argNum = psArgumentGet (argc, argv, "-dvodb"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        dvodb = psStringCopy (argv[argNum]);
+        psArgumentRemove(argNum, &argc, argv);
+    }
+
+    char *tess_id = NULL;
+    if ((argNum = psArgumentGet (argc, argv, "-tess_id"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        tess_id = psStringCopy (argv[argNum]);
+        psArgumentRemove(argNum, &argc, argv);
+    }
+
+    char *basename = NULL;
+    if ((argNum = psArgumentGet (argc, argv, "-basename"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        basename = psStringCopy (argv[argNum]);
+        psArgumentRemove(argNum, &argc, argv);
+    } else {
+        basename = psStringCopy ("simtest");
+    }
+
+    char *label = NULL;
+    if ((argNum = psArgumentGet (argc, argv, "-label"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        label = psStringCopy (argv[argNum]);
+        psArgumentRemove(argNum, &argc, argv);
+    }
+
+    if (argc != 4) {
+        fprintf (stderr, "USAGE: ppSimSequence (sequence) (simulate) (inject) [options]\n");
+        fprintf (stderr, "generates a set of simulated data defined by the sequence file\n");
+        fprintf (stderr, " (sequence) : a mdc-file describing the desired image sequences\n");
+        fprintf (stderr, " (simulate) : an output file with commands to generate the images\n");
+        fprintf (stderr, " (inject)   : an output file with commands to inject the images into the pipeline\n");
+        fprintf (stderr, "options:\n");
+        fprintf (stderr, " -camera (camera) [otherwise must be set in sequences file]\n");
+        fprintf (stderr, " -ppsim_recipe (recipe)\n");
+        fprintf (stderr, " -dbname (dbname)\n");
+        fprintf (stderr, " -path (path)\n");
+        fprintf (stderr, " -workdir (workdir)\n");
+        fprintf (stderr, " -basename (basename)\n");
+        fprintf (stderr, " -label (label)\n");
+        fprintf (stderr, " -dvodb (dvodb)\n");
+        fprintf (stderr, " -tess_id (tess_id)\n");
+        exit (2);
+    }
+
+    // load the sequence description
+    psMetadata *config = psMetadataConfigRead (NULL, &nFail, argv[1], false);
+    if (!config) {
+        psLogMsg ("ppSimSequence", PS_LOG_WARN, "unable to read sequence description from %s", argv[1]);
+        exit (1);
+    }
+
+    FILE *simfile = fopen (argv[2], "w");
+    if (!simfile) {
+        psLogMsg ("ppSimSequence", PS_LOG_WARN, "unable to open %s for output", argv[2]);
+        exit (1);
+    }
+
+    FILE *inject = fopen (argv[3], "w");
+    if (!inject) {
+        psLogMsg ("ppSimSequence", PS_LOG_WARN, "unable to open %s for output", argv[3]);
+        exit (1);
+    }
+
+    // build the base injectCommand string
+    psString injectCommand = psStringCopy ("ipp_inject_fileset.pl --telescope SIMTEST");
+    if (dbname)  psStringAppend (&injectCommand, " --dbname %s",  dbname);
+    if (workdir) psStringAppend (&injectCommand, " --workdir %s", workdir);
+    if (label)   psStringAppend (&injectCommand, " --label %s", label);
+    if (dvodb)   psStringAppend (&injectCommand, " --dvodb %s", dvodb);
+    if (tess_id) psStringAppend (&injectCommand, " --tess_id %s", tess_id);
+
+    // build the base ppSimCommand string
+    psString ppSimCommand = psStringCopy ("ppSim");
+    if (ppsim_recipe) psStringAppend (&ppSimCommand, " -recipe PPSIM %s", ppsim_recipe);
+
+    unsigned long seed = psMetadataLookupS32 (&status, config, "RND_SEED");
+    if (!status) seed = 0;
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, seed);
+
+    psMetadataItem *item = psMetadataLookup (config, "SEQUENCE");
+    if (item == NULL) {
+        psLogMsg ("ppSimSequence", PS_LOG_WARN, "missing SEQUENCE description");
+        exit (1);
+    }
+
+    psArray *sequences = NULL;
+    if (item->type == PS_DATA_METADATA) {
+        sequences = psArrayAlloc(1);
+        sequences->data[0] = psMemIncrRefCounter (item->data.V);
+    } else {
+        if (item->type != PS_DATA_METADATA_MULTI)  {
+            psLogMsg ("ppSimSequence", PS_LOG_WARN, "SEQUENCE is not MULTI or METADATA");
+            exit (1);
+        }
+        sequences = psListToArray (item->data.list);
+    }
+
+    for (int i = 0; i < sequences->n; i++) {
+
+        // XXX this is not obvious: the entry on the list is a metadata item
+        // containing the psMetadata :: why is this not just a metadata?
+        psMetadataItem *item = sequences->data[i];
+        psMetadata *sequence = item->data.V;
+        // double check item type?
+
+        // determine the sequence type
+        char *type = psMetadataLookupStr (&status, sequence, "OBSTYPE");
+        if (!status) {
+            psLogMsg ("ppSimSequence", PS_LOG_WARN, "SEQUENCE %d is missing a type", i);
+            exit (1);
+        }
+
+	// determine the camera for the sequence and define the ppSim command
+	if (camera == NULL) {
+	    camera = psMetadataLookupStr (&status, sequence, "CAMERA");
+	}
+
+	psString injectCommandReal = NULL;
+	psString ppSimCommandReal = NULL;
+
+	psStringAppend (&injectCommandReal, "%s --camera %s", injectCommand, camera);
+	psStringAppend (&ppSimCommandReal, "%s -camera %s", ppSimCommand, camera);
+
+        if (!strcasecmp (type, "BIAS")) {
+            ppSimSequenceBias (simfile, inject, sequence, i, rng, path, basename, ppSimCommandReal, injectCommandReal);
+            continue;
+        }
+        if (!strcasecmp (type, "DARK")) {
+            ppSimSequenceDark (simfile, inject, sequence, i, rng, path, basename, ppSimCommandReal, injectCommandReal);
+            continue;
+        }
+        if (!strcasecmp (type, "FLAT")) {
+            ppSimSequenceFlat (simfile, inject, sequence, i, rng, path, basename, ppSimCommandReal, injectCommandReal);
+            continue;
+        }
+        if (!strcasecmp (type, "OBJECT")) {
+            ppSimSequenceObject (simfile, inject, sequence, i, rng, path, basename, ppSimCommandReal, injectCommandReal);
+            continue;
+        }
+
+        psLogMsg ("ppSimSequence", PS_LOG_WARN, "SEQUENCE %d has an unknown type: %s", i, type);
+        exit (1);
+    }
+
+    exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequence.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequence.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequence.h	(revision 22322)
@@ -0,0 +1,19 @@
+#ifndef PP_SIM_SEQUENCE_H
+#define PP_SIM_SEQUENCE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <strings.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <psastro.h>
+
+bool ppSimSequenceBias 	 (FILE *simfile, FILE *inject, psMetadata *sequence, int nSeq, psRandom *rng, const char *path, const char *basename, const char *ppSimCommand, const char *injectCommand);
+bool ppSimSequenceDark 	 (FILE *simfile, FILE *inject, psMetadata *sequence, int nSeq, psRandom *rng, const char *path, const char *basename, const char *ppSimCommand, const char *injectCommand);
+bool ppSimSequenceFlat 	 (FILE *simfile, FILE *inject, psMetadata *sequence, int nSeq, psRandom *rng, const char *path, const char *basename, const char *ppSimCommand, const char *injectCommand);
+bool ppSimSequenceObject (FILE *simfile, FILE *inject, psMetadata *sequence, int nSeq, psRandom *rng, const char *path, const char *basename, const char *ppSimCommand, const char *injectCommand);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceBias.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceBias.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceBias.c	(revision 22322)
@@ -0,0 +1,49 @@
+# include "ppSimSequence.h"
+
+bool ppSimSequenceBias (FILE *simfile, FILE *inject, psMetadata *sequence, int nSeq, psRandom *rng, const char *path, const char *basename, const char *ppSimCommand, const char *injectCommand) {
+
+    bool status, setLevel, setRange;
+
+    // optional details
+    float level = psMetadataLookupF32 (&setLevel, sequence, "BIAS.LEVEL");
+    float range = psMetadataLookupF32 (&setRange, sequence, "BIAS.RANGE");
+
+    int nImages = psMetadataLookupS32 (&status, sequence, "NIMAGES");
+
+    // loop over the filters & exposure times
+    int nImage = 0;
+    for (int i = 0; i < nImages; i++) {
+	    
+	// define the output filename
+	psString filename = NULL;
+	if (path) {
+	    psStringAppend (&filename, "%s/%s.%03d.%03d", path, basename, nSeq, nImage);
+	} else {
+	    psStringAppend (&filename, "%s.%03d.%03d", basename, nSeq, nImage);
+	}
+
+	// define the ppSim command
+	psString command = NULL;
+
+	psStringAppend (&command, "%s -type BIAS", ppSimCommand);
+
+	if (setLevel) psStringAppend (&command, " -biaslevel %f", level);
+	if (setRange) psStringAppend (&command, " -biasrange %f", range);
+      
+	psStringAppend (&command, " %s", filename);
+
+	fprintf (simfile, "%s\n", command);
+	psFree (command);
+			    
+	// define the inject command
+	// path should be dirname/filename
+	command = psStringCopy (injectCommand);
+	psStringAppend (&command, " %s*.fits",    filename);
+	fprintf (inject, "%s\n", command);
+	psFree (command);
+	psFree (filename);
+
+	nImage ++;
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceDark.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceDark.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceDark.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include "ppSimSequence.h"
+
+bool ppSimSequenceDark (FILE *simfile, FILE *inject, psMetadata *sequence, int nSeq, psRandom *rng, const char *path, const char *basename, const char *ppSimCommand, const char *injectCommand) {
+
+    bool status, setRate;
+    float min, max = 0;
+
+    setRate = false;
+    min = psMetadataLookupF32 (&status, sequence, "DARK.MIN");
+    if (status) {
+	max = psMetadataLookupF32 (&status, sequence, "DARK.MAX");
+	setRate = true;
+    }
+
+    psVector *exptimes = psMetadataLookupPtr (&status, sequence, "EXPTIMES");
+    psVector *nImages = psMetadataLookupPtr (&status, sequence, "NIMAGES");
+
+    assert (exptimes->n == nImages->n);
+
+    // loop over the filters & exposure times
+    int nImage = 0;
+    for (int i = 0; i < nImages->n; i++) {
+	    
+	float exptime = exptimes->data.F32[i];
+	float n = nImages->data.S32[i];
+
+	for (int j = 0; j < n; j++) {
+
+	    // XXX need to add output filename
+	    psString filename = NULL;
+	    if (path) {
+		psStringAppend (&filename, "%s/%s.%03d.%03d", path, basename, nSeq, nImage);
+	    } else {
+		psStringAppend (&filename, "%s.%03d.%03d", basename, nSeq, nImage);
+	    }
+
+	    // define the ppSim command
+	    psString command = NULL;
+
+	    psStringAppend (&command, "%s -type DARK", ppSimCommand);
+      
+	    if (setRate) {
+		double frnd = psRandomUniform(rng);
+		float rate = min + (max - min)*frnd;
+		psStringAppend (&command, " -darkrate %f", rate);
+	    }
+
+	    psStringAppend (&command, " -exptime %f", exptime);
+
+	    psStringAppend (&command, " %s", filename);
+
+	    fprintf (simfile, "%s\n", command);
+	    psFree (command);
+			    
+	    // define the inject command
+	    // path should be dirname/filename
+	    command = psStringCopy (injectCommand);
+	    psStringAppend (&command, " %s*.fits",    filename);
+	    fprintf (inject, "%s\n", command);
+	    psFree (command);
+	    psFree (filename);
+
+	    nImage ++;
+	}
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceFlat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceFlat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceFlat.c	(revision 22322)
@@ -0,0 +1,62 @@
+# include "ppSimSequence.h"
+
+bool ppSimSequenceFlat (FILE *simfile, FILE *inject, psMetadata *sequence, int nSeq, psRandom *rng, const char *path, const char *basename, const char *ppSimCommand, const char *injectCommand) {
+
+    bool status;
+
+    // determine the filters & exposure times
+    char *filterList = psMetadataLookupStr (&status, sequence, "FILTERS");
+    psArray *filters = psStringSplitArray (filterList, ",: ", false);
+
+    psVector *exptimes = psMetadataLookupPtr (&status, sequence, "EXPTIMES");
+
+    if (filters->n != exptimes->n) {
+	psLogMsg ("ppSimSequence", PS_LOG_WARN, "mis-match in filter and exptime lists");
+	exit (1);
+    }
+
+    // number of images for each filter, exptime set
+    int nSetup = psMetadataLookupS32 (&status, sequence, "NSETUP");
+
+    int nImage = 0;
+
+    // loop over the filters & exposure times
+    for (int i = 0; i < filters->n; i++) {
+	    
+	// loop over the filters & exposure times
+	for (int j = 0; j < nSetup; j++) {
+	    
+	    // define the output filename
+	    psString filename = NULL;
+	    if (path) {
+		psStringAppend (&filename, "%s/%s.%03d.%03d", path, basename, nSeq, nImage);
+	    } else {
+		psStringAppend (&filename, "%s.%03d.%03d", basename, nSeq, nImage);
+	    }
+
+	    // define the ppSim comand
+	    psString command = NULL;
+
+	    psStringAppend (&command, "%s -type FLAT", ppSimCommand);
+
+	    psStringAppend (&command, " -filter %s", (char *) filters->data[i]);
+	    psStringAppend (&command, " -exptime %f", exptimes->data.F32[i]);
+
+	    psStringAppend (&command, " %s", filename);
+
+	    fprintf (simfile, "%s\n", command);
+	    psFree (command);
+
+	    // define the inject command
+	    // path should be dirname/filename
+	    command = psStringCopy (injectCommand);
+	    psStringAppend (&command, " %s*.fits",    filename);
+	    fprintf (inject, "%s\n", command);
+	    psFree (command);
+	    psFree (filename);
+
+	    nImage ++;
+	}
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceObject.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceObject.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSequenceObject.c	(revision 22322)
@@ -0,0 +1,124 @@
+# include "ppSimSequence.h"
+
+bool ppSimSequenceObject (FILE *simfile, FILE *inject, psMetadata *sequence, int nSeq, psRandom *rng, const char *path, const char *basename, const char *ppSimCommand, const char *injectCommand) {
+
+    bool status;
+
+    // generate ppSim lines that look like:
+    // ppSim -camera $camera -type OBJECT -filter $filter -exptime $exptime
+    //       -skyrate $sky -ra $ra -dec $dec -pa $pa -scale $scale -zp $zp -seeing $seeing $filename",
+
+    // sequence reference coordinate
+    float Ro = psMetadataLookupF32 (&status, sequence, "CENTER.RA");
+    float Do = psMetadataLookupF32 (&status, sequence, "CENTER.DEC");
+
+    // determine the filters & exposure times
+    char *filterList = psMetadataLookupStr (&status, sequence, "FILTERS");
+    psArray *filters = psStringSplitArray (filterList, ",: ", false);
+
+    psVector *exptimes = psMetadataLookupPtr (&status, sequence, "EXPTIMES");
+
+    psVector *skymags = psMetadataLookupPtr (&status, sequence, "SKYMAGS");
+
+    float IQmin = psMetadataLookupF32 (&status, sequence, "IQ_MIN");
+    float IQmax = psMetadataLookupF32 (&status, sequence, "IQ_MAX");
+
+    if (filters->n != exptimes->n) {
+	psLogMsg ("ppSimSequence", PS_LOG_WARN, "mis-match in filter and exptime lists");
+	exit (1);
+    }
+
+    // track the number of files produced
+    int nImage = 0;
+
+    // loop over the filters & exposure times
+    for (int i = 0; i < filters->n; i++) {
+	    
+	// offset parameters
+	float dR = psMetadataLookupF32 (&status, sequence, "OFFSET.RA");
+	float dD = psMetadataLookupF32 (&status, sequence, "OFFSET.DEC");
+  
+	int nR = psMetadataLookupS32 (&status, sequence, "OFFSET.NR");
+	int nD = psMetadataLookupS32 (&status, sequence, "OFFSET.ND");
+  
+	// loop over the offset sequence
+	for (int iR = 0; iR < nR; iR++) {
+	    for (int iD = 0; iD < nD; iD++) {
+
+		// RA & DEC in degrees XXX (should be radians...)
+		// offsets are in arcseconds
+		float R = Ro + dR*(iR - 0.5*nR + 0.5) / cos (RAD_DEG*Do) / 3600.0;
+		float D = Do + dD*(iD - 0.5*nD + 0.5) / 3600.0;
+      
+		// dither parameters
+		float dr = psMetadataLookupF32 (&status, sequence, "DITHER.RA");
+		float dd = psMetadataLookupF32 (&status, sequence, "DITHER.DEC");
+  
+		int nr = psMetadataLookupS32 (&status, sequence, "DITHER.NR");
+		int nd = psMetadataLookupS32 (&status, sequence, "DITHER.ND");
+  
+		// loop over the dither sequence
+		for (int ir = 0; ir < nr; ir++) {
+		    for (int id = 0; id < nd; id++) {
+
+			// ra, dec in degrees; offsets in arcsec
+			float ra = R + dr*(ir - 0.5*nr + 0.5) / cos (RAD_DEG*D) / 3600.0;
+			float dec = D + dd*(id - 0.5*nd + 0.5) / 3600.0;
+	  
+			// rotation sequence parameters
+			float pos_min   = psMetadataLookupF32 (&status, sequence, "POS_MIN");
+			float pos_max   = psMetadataLookupF32 (&status, sequence, "POS_MAX");
+			float pos_delta = psMetadataLookupF32 (&status, sequence, "POS_DELTA");
+			assert (pos_delta > 0.0);
+			assert (pos_max >= pos_min);
+	    
+			// loop over rotation sequence
+			for (float pos = pos_min; pos <= pos_max; pos += pos_delta) {
+	      
+			    // define the output filename
+			    psString filename = NULL;
+			    if (path) {
+				psStringAppend (&filename, "%s/%s.%03d.%03d", path, basename, nSeq, nImage);
+			    } else {
+				psStringAppend (&filename, "%s.%03d.%03d", basename, nSeq, nImage);
+			    }
+
+			    // define the ppSim command
+			    psString command = NULL;
+
+			    psStringAppend (&command, "%s -type OBJECT", ppSimCommand);
+			    psStringAppend (&command, " -filter %s", (char *) filters->data[i]);
+			    psStringAppend (&command, " -exptime %f", exptimes->data.F32[i]);
+			    psStringAppend (&command, " -skymags %f", skymags->data.F32[i]);
+
+			    psStringAppend (&command, " -ra %f", ra);
+			    psStringAppend (&command, " -dec %f", dec);
+			    psStringAppend (&command, " -pa %f", pos);
+
+			    double frnd = psRandomUniform(rng);
+			    float seeing = IQmin + (IQmax - IQmin)*frnd;
+	      
+			    psStringAppend (&command, " -seeing %f", seeing);
+
+			    psStringAppend (&command, " %s", filename);
+
+			    fprintf (simfile, "%s\n", command);
+			    psFree (command);
+			    
+			    // define the inject command
+			    // path should be dirname/filename
+			    command = psStringCopy (injectCommand);
+			    psStringAppend (&command, " %s*.fits",    filename);
+			    fprintf (inject, "%s\n", command);
+			    psFree (command);
+			    psFree (filename);
+
+			    nImage ++;
+			}
+		    }
+		}
+	    }
+	}
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSetPSF.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSetPSF.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimSetPSF.c	(revision 22322)
@@ -0,0 +1,85 @@
+# include "ppSim.h"
+static char *defaultModel = "PS_MODEL_QGAUSS";
+
+bool ppSimSetPSF (pmChip *chip, pmConfig *config) {
+
+    bool status, mdok;
+    pmPSF *psf = NULL;
+    pmTrend2D *param = NULL;
+
+    psMetadata *recipe = psMetadataLookupMetadata(&mdok, config->recipes, PPSIM_RECIPE); // Recipe
+
+    // the pmPSF IO functions stores the PSF on the chip->analysis
+    psf = psMetadataLookupPtr (&status, chip->analysis, "PSPHOT.PSF");
+    if (psf) {
+        return true;
+    }
+
+    // no supplied PSF, build one using supplied value for seeing seeing is already corrected
+    // for the pixel scale, and is converted from FWHM to SIGMA (this is done in
+    // ppSimArguments)
+    float seeing   = psMetadataLookupF32(&status, recipe, "SEEING"); // Seeing SIGMA (pixels)
+
+    char *psfModelName = psMetadataLookupStr(&status, recipe, "PSF.MODEL"); // Name of PSF model
+    if (psfModelName == NULL) {
+        psfModelName = defaultModel;
+    }
+
+    // structure to store user options defining the psf
+    pmPSFOptions *options = pmPSFOptionsAlloc ();
+    options->type = pmModelClassGetType (psfModelName);
+    if (options->type == -1) {
+        psError (PS_ERR_UNKNOWN, false, "invalid model name");
+        return false;
+    }
+
+    // XXX this is messed up:  CHIP.XSIZE and CHIP.YSIZE are not seta
+    int xSize = psMetadataLookupS32(NULL, chip->concepts, "CHIP.XSIZE");
+    int ySize = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YSIZE");
+    xSize = 1000;
+    ySize = 1000;
+
+    // no spatial variation
+    options->psfTrendMode = PM_TREND_POLY_ORD;
+    options->psfTrendNx = 0;
+    options->psfTrendNy = 0;
+    options->psfFieldNx = xSize;
+    options->psfFieldNy = ySize;
+
+    // generate the psf
+    psf = pmPSFAlloc (options);
+
+    psEllipseAxes axes;
+    psEllipsePol pol;
+
+    // supply the semi-major axis (these are SIGMA values in PIXELS)
+    axes.major = seeing;
+    axes.minor = seeing;
+    axes.theta = 0.0;
+
+    pol = psEllipseAxesToPol (axes);
+    
+    param = psf->params->data[PM_PAR_E0];
+    param->poly->coeff[0][0] = pol.e0;
+
+    param = psf->params->data[PM_PAR_E1];
+    param->poly->coeff[0][0] = pol.e1;
+
+    param = psf->params->data[PM_PAR_E2];
+    param->poly->coeff[0][0] = pol.e2;
+
+    if (!strcasecmp (psfModelName, "PS_MODEL_QGAUSS")) {
+        param = psf->params->data[PM_PAR_7];
+        param->poly->coeff[0][0] = 1.0;
+    }
+
+    if (!strcasecmp (psfModelName, "PS_MODEL_RGAUSS")) {
+        param = psf->params->data[PM_PAR_7];
+        param->poly->coeff[0][0] = 1.0;
+    }
+
+    psMetadataAdd (chip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN,  "psphot psf", psf);
+    psFree(psf);                        // Drop reference
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimStars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimStars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimStars.c	(revision 22322)
@@ -0,0 +1,59 @@
+# include "ppSim.h"
+
+void ppSimStarFree(ppSimStar *star)
+{
+    return;
+}
+
+ppSimStar *ppSimStarAlloc () {
+
+    ppSimStar *star = (ppSimStar *) psAlloc(sizeof(ppSimStar));
+    psMemSetDeallocator(star, (psFreeFunc) ppSimStarFree);
+
+    return star;
+}
+
+void ppSimGalaxyFree(ppSimGalaxy *galaxy)
+{
+    return;
+}
+
+ppSimGalaxy *ppSimGalaxyAlloc () {
+
+    ppSimGalaxy *galaxy = (ppSimGalaxy *) psAlloc(sizeof(ppSimGalaxy));
+    psMemSetDeallocator(galaxy, (psFreeFunc) ppSimGalaxyFree);
+
+    return galaxy;
+}
+
+float ppSimStarSkyNoise (float skySigma, float seeingSigma) {
+
+    float skyNoise = skySigma * sqrt(4*M_PI*PS_SQR(seeingSigma));
+    return skyNoise;
+}
+
+float ppSimStarPeakToFlux (float peak, float seeingSigma) {
+
+    float psfArea = 2.0*M_PI*PS_SQR(seeingSigma);
+    float flux = peak * psfArea;
+    return flux;
+}
+
+float ppSimStarFluxToPeak (float flux, float seeingSigma) {
+
+    float psfArea = 2.0*M_PI*PS_SQR(seeingSigma);
+    float peak = flux / psfArea;
+    return peak;
+}
+
+float ppSimFluxToMag (float flux, float zp) {
+
+    float mag = -2.5*log10(flux) + zp;
+    return mag;
+}
+
+float ppSimMagToFlux (float mag, float zp) {
+
+    float flux = powf (10.0, -0.4*(mag - zp));
+    return flux;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSim/src/ppSimUtils.c	(revision 22322)
@@ -0,0 +1,301 @@
+# include "ppSim.h"
+
+// Generate a header containing WCS keywords
+// this function is called with only one of fpa, chip, cell not NULL
+bool ppSimInitHeader(pmConfig *config,
+                     pmFPA *fpa,
+                     pmChip *chip,
+                     pmCell *cell)
+{
+    bool mdok;
+
+    psMetadata *recipe = psMetadataLookupMetadata(&mdok, config->recipes, PPSIM_RECIPE); // Recipe
+
+    float ra0   = psMetadataLookupF32(NULL, recipe, "RA");  // Boresight RA (radians)
+    float dec0  = psMetadataLookupF32(NULL, recipe, "DEC"); // Boresight Dec (radians)
+    float pa    = psMetadataLookupF32(NULL, recipe, "PA");  // Position angle (radians)
+    float scale = psMetadataLookupF32(NULL, recipe, "PIXEL.SCALE"); // plate scale in arcsec / pixel
+    scale *= M_PI / 3600.0 / 180.0; // convert plate scale to radians/pixel
+
+    int binning = psMetadataLookupS32(NULL, recipe, "BINNING"); // Binning in x and y
+
+    float x0 = 0.0, y0 = 0.0;
+    int xParity = 0, yParity = 0;
+    if (cell) {
+        int x0Chip = psMetadataLookupS32(NULL, cell->parent->concepts, "CHIP.X0");
+        int y0Chip = psMetadataLookupS32(NULL, cell->parent->concepts, "CHIP.Y0");
+        int xParityChip = psMetadataLookupS32(NULL, cell->parent->concepts, "CHIP.XPARITY");
+        int yParityChip = psMetadataLookupS32(NULL, cell->parent->concepts, "CHIP.YPARITY");
+
+        int x0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.X0");
+        int y0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0");
+        int xParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY");
+        int yParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY");
+
+        x0 = PPSIM_FPA_TO_CELL(0.0, x0Cell, xParityCell, binning, x0Chip, xParityChip);
+        y0 = PPSIM_FPA_TO_CELL(0.0, y0Cell, yParityCell, binning, y0Chip, yParityChip);
+        xParity = xParityCell * xParityChip;
+        yParity = yParityCell * yParityChip;
+    }
+    if (chip) {
+        int x0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.X0");
+        int y0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.Y0");
+        int xParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.XPARITY");
+        int yParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY");
+
+        x0 = PPSIM_FPA_TO_CELL(0.0, 0, 1, binning, x0Chip, xParityChip);
+        y0 = PPSIM_FPA_TO_CELL(0.0, 0, 1, binning, y0Chip, yParityChip);
+        xParity = xParityChip;
+        yParity = yParityChip;
+    }
+    if (fpa) {
+        psRegion *bounds = ppSimFPABounds (fpa);
+        x0 = 0.5 * (bounds->x1 - bounds->x0);
+        y0 = 0.5 * (bounds->y1 - bounds->y0);
+        xParity = 1;
+        yParity = 1;
+        psFree (bounds);
+    }
+    assert(xParity != 0 && yParity != 0);
+
+    psMetadata *header = psMetadataAlloc(); // Header, to return
+    pmAstromWCS *wcs = pmAstromWCSAlloc(1, 1); // WCS structure
+    wcs->toSky = psProjectionAlloc(ra0, dec0, scale * xParity, scale * yParity, PS_PROJ_TAN);
+    wcs->cdelt1 = scale * PM_DEG_RAD * xParity;
+    wcs->cdelt2 = scale * PM_DEG_RAD * yParity;
+    wcs->crpix1 = x0;
+    wcs->crpix2 = y0;
+    wcs->trans->x->coeff[1][0] = cos(pa) * wcs->cdelt1;
+    wcs->trans->x->coeff[0][1] = -sin(pa) * wcs->cdelt1;
+    wcs->trans->y->coeff[1][0] = sin(pa) * wcs->cdelt2;
+    wcs->trans->y->coeff[0][1] = cos(pa) * wcs->cdelt2;
+
+    // These aren't used by pmAstromWCStoHeader, but set them anyway
+    wcs->trans->x->coeff[0][0] = ra0;
+    wcs->trans->y->coeff[0][0] = dec0;
+    wcs->trans->x->coeff[1][1] = 0.0;
+    wcs->trans->y->coeff[1][1] = 0.0;
+
+    pmAstromWCStoHeader(header, wcs);
+    psFree(wcs);
+
+    if (cell) {
+        cell->hdu->header = header;
+        cell->data_exists = true;
+    }
+    if (chip) {
+        chip->hdu->header = header;
+        chip->data_exists = true;
+    }
+    if (fpa) {
+        fpa->hdu->header = header;
+    }
+
+    return true;
+}
+
+char *ppSimTypeToString (ppSimType type) {
+
+    char *typeStr;
+
+    switch (type) {
+      case PPSIM_TYPE_BIAS:   typeStr = psStringCopy ("BIAS");   break;
+      case PPSIM_TYPE_DARK:   typeStr = psStringCopy ("DARK");   break;
+      case PPSIM_TYPE_FLAT:   typeStr = psStringCopy ("FLAT");   break;
+      case PPSIM_TYPE_OBJECT: typeStr = psStringCopy ("OBJECT"); break;
+      default:
+        psAbort("Should never get here.");
+    }
+    return (typeStr);
+}
+
+ppSimType ppSimTypeFromString (char *typeStr) {
+
+    if (!strcasecmp (typeStr, "BIAS")) 	 return PPSIM_TYPE_BIAS;
+    if (!strcasecmp (typeStr, "DARK")) 	 return PPSIM_TYPE_DARK;
+    if (!strcasecmp (typeStr, "FLAT")) 	 return PPSIM_TYPE_FLAT;
+    if (!strcasecmp (typeStr, "OBJECT")) return PPSIM_TYPE_OBJECT;
+    psAbort("Should never get here.");
+}
+
+bool ppSimUpdateConceptsFPA (pmFPA *fpa, pmConfig *config) {
+
+    bool mdok;
+
+    psMetadata *recipe = psMetadataLookupMetadata(&mdok, config->recipes, PPSIM_RECIPE); // Recipe
+
+    float expTime = psMetadataLookupF32(NULL, recipe, "EXPTIME"); // Exposure time
+
+    const char *filter = psMetadataLookupStr(NULL, recipe, "FILTER"); // Filter name
+    if (!filter) {
+        filter = "NONE";
+    }
+
+    char *typeStr = psMetadataLookupStr(NULL, recipe, "IMAGE.TYPE"); // Type of image to simulate
+
+    psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.OBSTYPE", PS_META_REPLACE, "Observation type", typeStr);
+    psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.OBJECT", PS_META_REPLACE, "Observation name", typeStr);
+    psMetadataAddF32(fpa->concepts, PS_LIST_TAIL, "FPA.EXPOSURE", PS_META_REPLACE, "Exposure time (sec)", expTime);
+    psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.FILTERID", PS_META_REPLACE, "Filter name", filter);
+    psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.FILTER", PS_META_REPLACE, "Filter name", filter);
+
+    return true;
+}
+
+bool ppSimUpdateConceptsCell (pmCell *cell, pmConfig *config) {
+
+    bool mdok;
+
+    psMetadata *recipe = psMetadataLookupMetadata(&mdok, config->recipes, PPSIM_RECIPE); // Recipe
+
+    int binning = psMetadataLookupS32(NULL, recipe, "BINNING"); // Binning in x and y
+    float expTime = psMetadataLookupF32(NULL, recipe, "EXPTIME"); // Exposure time
+
+    psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.EXPOSURE", PS_META_REPLACE,
+                     "Exposure time (sec)", expTime);
+    psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.DARKTIME", PS_META_REPLACE,
+                     "Dark time (sec)", expTime);
+    psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.XBIN", PS_META_REPLACE,
+                     "Binning in x", binning);
+    psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.YBIN", PS_META_REPLACE,
+                     "Binning in y", binning);
+
+    return true;
+}
+
+bool ppSimRecipeValidation (pmConfig *config) {
+
+    bool status;
+
+    psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, PPSIM_RECIPE); // Recipe
+
+    int binning = psMetadataLookupS32(&status, recipe, "BINNING"); // Binning in x and y
+    if (binning <= 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Binning (%d) is non-positive.", binning);
+	exit(PS_EXIT_CONFIG_ERROR);
+    }
+    return true;
+}
+
+// Get a value from the command-line arguments and add it to recipe options
+float ppSimArgToRecipeF32(bool *status,
+			  psMetadata *options,    // Target to which to add value
+			  const char *recipeName, // Name for value in the recipe
+			  psMetadata *arguments,  // Command-line arguments
+			  const char *argName	    // Argument name in the command-line arguments
+    )
+{
+    bool myStatus;
+    float value = psMetadataLookupF32(&myStatus, arguments, argName); // Value of interest
+    if (status) { *status = myStatus; }
+    if (isnan(value)) return value;
+
+    psMetadataAddF32(options, PS_LIST_TAIL, recipeName, PS_META_REPLACE, NULL, value);
+    return value;
+}
+
+// Get a value from the command-line arguments and add it to recipe options
+int ppSimArgToRecipeS32(bool *status,
+			psMetadata *options,    // Target to which to add value
+			const char *recipeName, // Name for value in the recipe
+			psMetadata *arguments,  // Command-line arguments
+			const char *argName	    // Argument name in the command-line arguments
+    )
+{
+    bool myStatus;
+    int value = psMetadataLookupS32(&myStatus, arguments, argName); // Value of interest
+    if (status) { *status = myStatus; }
+
+    psMetadataAddS32(options, PS_LIST_TAIL, recipeName, PS_META_REPLACE, NULL, value);
+    return value;
+}
+
+// Get a value from the command-line arguments and add it to recipe options
+bool ppSimArgToRecipeBool(bool *status,
+			  psMetadata *options,    // Target to which to add value
+			  const char *recipeName, // Name for value in the recipe
+			  psMetadata *arguments,  // Command-line arguments
+			  const char *argName	    // Argument name in the command-line arguments
+    )
+{
+    bool myStatus;
+    bool value = psMetadataLookupS32(&myStatus, arguments, argName); // Value of interest
+    if (status) { *status = myStatus; }
+
+    psMetadataAddBool(options, PS_LIST_TAIL, recipeName, PS_META_REPLACE, NULL, value);
+    return value;
+}
+
+// Get a value from the command-line arguments and add it to recipe options
+char *ppSimArgToRecipeStr(bool *status,
+			  psMetadata *options,    // Target to which to add value
+			  const char *recipeName, // Name for value in the recipe
+			  psMetadata *arguments,  // Command-line arguments
+			  const char *argName	    // Argument name in the command-line arguments
+    )
+{
+    bool myStatus;
+
+    char *value = psMetadataLookupStr(&myStatus, arguments, argName); // Value of interest
+    if (status) {
+	*status = myStatus;
+    }
+    psMetadataAddStr(options, PS_LIST_TAIL, recipeName, PS_META_REPLACE, NULL, value);
+    return value;
+}
+
+float ppSimGetZeroPoint (psMetadata *recipe, char *filter) {
+
+    bool mdok;
+    float zp;
+
+    // use the filter to get the zeropoint from the recipe
+    psMetadataItem *zpItem = psMetadataLookup (recipe, "ZEROPTS");
+    // check that item is multi...
+	    
+    psArray *entries = psListToArray (zpItem->data.list);
+	  
+    // search for matching filter
+    for (int i = 0; i < entries->n; i++) {
+	psMetadataItem *item = entries->data[i];
+	psMetadata *entry = item->data.V;
+
+	char *filterName = psMetadataLookupStr (&mdok, entry, "FILTER");
+	assert (filterName);
+
+	if (strcmp(filterName, filter)) continue;
+
+	zp = psMetadataLookupF32 (&mdok, entry, "ZERO_PT");
+	assert (mdok);
+	psFree (entries);
+	return zp;
+    }
+    psFree (entries);
+    return NAN;
+}
+
+psArray *ppSimSelectSources (pmConfig *config, const pmFPAview *view, const char *filename) {
+
+    pmReadout *readout = pmFPAfileThisReadout (config->files, view, filename);
+    PS_ASSERT_PTR_NON_NULL (readout, NULL);
+
+    psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
+    return sources;
+}
+
+bool ppSimDefinePixels (psArray *sources, pmReadout *readout, psMetadata *recipe) {
+
+    bool status;
+
+    float OUTER = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
+    if (!status) return NULL;
+
+    for (int i = 0; i < sources->n; i++) {
+	pmSource *source = sources->data[i];
+
+        // allocate image, weight, mask arrays for each peak (square of radius OUTER)
+        pmSourceDefinePixels (source, readout, source->peak->x, source->peak->y, OUTER);
+    }
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppStack/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/.cvsignore	(revision 22322)
@@ -0,0 +1,17 @@
+aclocal.m4
+compile
+config.guess
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libtool
+ltmain.sh
+Makefile
+Makefile.in
+missing
+ppStac.pc
+test
+autom4te.cache
Index: /tags/sj_tags/sj_root_20080929/ppStack/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/ppStack/README.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/README.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/README.txt	(revision 22322)
@@ -0,0 +1,25 @@
+ppStack needs to be optimised, principally to reduce the memory
+footprint (10 or so images will get it to over 4 GB).  The proposed
+strategy follows, more or less, the same as for ppMerge.
+
+* Convolution steps:
+  - For each file:
+    + read entire image
+    + convolve to reference PSF
+    + write convolved image
+
+* Combination step:
+  - For each chunk:
+    + For each file, read chunk
+    + combine chunk
+  - Probably have to repeat, to do the multi-pass rejection
+
+
+To do:
+* Need file PPSTACK.OUTPUT.CONV (image, mask, weight versions)
+* Need file PPSTACK.INPUT.CONV (image, mask, weight versions)
+* Check: does pmFPAfileChecks do piece-by-piece read?
+* Can we reset a file, so that we can read it twice through?
+* pmStack functions probably need work so that they operate on a piece
+  of the image at a time.  Will there be issues with the masks in the
+  second pass?
Index: /tags/sj_tags/sj_root_20080929/ppStack/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppStack
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ppStack/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/configure.ac	(revision 22322)
@@ -0,0 +1,34 @@
+AC_PREREQ(2.61)
+
+AC_INIT([ppStack], [0.1.1], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE 
+
+PKG_CHECK_MODULES([PSLIB],    [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+PKG_CHECK_MODULES([PSPHOT],   [psphot >= 0.8.0]) 
+PKG_CHECK_MODULES([PPSTATS],  [ppStats >= 1.0.0]) 
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_SUBST([PPSTACK_CFLAGS])
+AC_SUBST([PPSTACK_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+Makefile
+Makefile.in
+.libs
+*.lo
+*.la
+ppStack
+config.h
+config.h.in
+stamp-h1
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/Makefile.am	(revision 22322)
@@ -0,0 +1,28 @@
+bin_PROGRAMS = ppStack
+
+ppStack_CFLAGS 	= $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PPSTATS_CFLAGS) $(PPSTACK_CFLAGS)
+ppStack_LDFLAGS = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PSPHOT_LIBS)   $(PPSTATS_LIBS)   $(PPSTACK_LIBS)
+
+ppStack_SOURCES =		\
+	ppStack.c		\
+	ppStackArguments.c	\
+	ppStackCamera.c		\
+	ppStackLoop.c		\
+	ppStackPSF.c		\
+	ppStackReadout.c	\
+	ppStackPhotometry.c	\
+	ppStackVersion.c	\
+	ppStackMatch.c		\
+	ppStackSources.c	\
+	ppStackThread.c
+
+noinst_HEADERS = 		\
+	ppStack.h
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStack.c	(revision 22322)
@@ -0,0 +1,71 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+
+#define TIMER_NAME "PPSTACK"            // Name of timer
+
+int main(int argc, char *argv[])
+{
+    psExit exitValue = PS_EXIT_SUCCESS; // Exit value
+    psTimerStart(TIMER_NAME);
+    psLibInit(NULL);
+
+    pmConfig *config = pmConfigRead(&argc, argv, PPSTACK_RECIPE); // Configuration
+    if (!config) {
+        psErrorStackPrint(stderr, "Error reading configuration.");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    (void) psTraceSetLevel("ppStack", 5);
+
+    if (!ppStackArgumentsSetup(argc, argv, config)) {
+        psErrorStackPrint(stderr, "Error reading arguments.\n");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!pmModelClassInit()) {
+        psErrorStackPrint(stderr, "Error initialising model classes.\n");
+        exitValue = PS_EXIT_PROG_ERROR;
+        goto die;
+    }
+
+    if (!ppStackCamera(config)) {
+        psErrorStackPrint(stderr, "Error setting up input files.\n");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!ppStackArgumentsParse(config)) {
+        psErrorStackPrint(stderr, "Error reading arguments.\n");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!ppStackLoop(config)) {
+        psErrorStackPrint(stderr, "Error performing combination.\n");
+        exitValue = PS_EXIT_DATA_ERROR;
+        goto die;
+    }
+
+
+     // Common code for the death.
+die:
+    psTrace("ppStack", 1, "Finished at %f sec\n", psTimerMark(TIMER_NAME));
+    psTimerStop();
+
+    psFree(config);
+    pmModelClassCleanup();
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exitValue);
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStack.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStack.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStack.h	(revision 22322)
@@ -0,0 +1,161 @@
+#ifndef PP_STACK_H
+#define PP_STACK_H
+
+#define PPSTACK_RECIPE "PPSTACK"        // Name of the recipe
+#define PPSTACK_INSPECT_PIXELS "PPSTACK.PIXELS" // Name of rejected pixels metadata items
+
+#include <pslib.h>
+#include <psmodules.h>
+
+// Mask values for inputs
+typedef enum {
+    PPSTACK_MASK_MATCH  = 0x01,         // PSF-matching failed
+    PPSTACK_MASK_REJECT = 0x02,         // Rejection failed
+    PPSTACK_MASK_BAD    = 0x04,         // Bad image (too many pixels rejected)
+    PPSTACK_MASK_ALL    = 0xff          // All errors
+} ppStackMask;
+
+// Thread for stacking chunks
+//
+// Each input file contributes a readout, into which is read a chunk from that file
+typedef struct {
+    psArray *readouts;                  // Input readouts to read and stack
+    bool read;                          // Has the scan been read?
+    bool busy;                          // Is the scan being processed?
+    int firstScan;                      // First row of the chunk to be read for this group
+    int lastScan;                       // Last row of the chunk to be read for this group
+} ppStackThread;
+
+// Allocator
+ppStackThread *ppStackThreadAlloc(psArray *readouts // Inputs readouts to read and stack
+    );
+
+// Data for threads
+typedef struct {
+    psArray *threads;                   // Threads doing stacking
+    int lastScan;                       // Last row that's been read
+    psArray *imageFits;                 // FITS file pointers for images
+    psArray *maskFits;                  // FITS file pointers for masks
+    psArray *weightFits;                // FITS file pointers for weights
+} ppStackThreadData;
+
+// Set up thread data
+ppStackThreadData *ppStackThreadDataSetup(const psArray *cells, // Array of input cells
+                                          const psArray *imageNames, // Names of images to read
+                                          const psArray *maskNames, // Names of masks to read
+                                          const psArray *weightNames, // Names of weight maps to read
+                                          const pmConfig *config // Configuration
+    );
+
+// Read chunk into the first available file thread
+ppStackThread *ppStackThreadRead(bool *status, // Status of read
+                                 ppStackThreadData *stack, // Stacks available for reading
+                                 pmConfig *config, // Configuration
+                                 int numChunk, // Chunk number (only for interest)
+                                 int overlap // Overlap between subsequent scans
+    );
+
+// Initialise the threads
+void ppStackThreadInit(void);
+
+// Setup command-line arguments
+bool ppStackArgumentsSetup(int argc, char *argv[], // Command-line arguments
+                           pmConfig *config  // Configuration
+    );
+
+// Parse command-line arguments
+bool ppStackArgumentsParse(pmConfig *config  // Configuration
+    );
+
+// Parse cameras
+bool ppStackCamera(pmConfig *config     // Configuration
+    );
+
+// Loop over the inputs, doing the combination
+bool ppStackLoop(pmConfig *config       // Configuration
+    );
+
+// Determine target PSF for input images
+pmPSF *ppStackPSF(const pmConfig *config, // Configuration
+                  int numCols, int numRows, // Size of image
+                  const psArray *psfs   // List of input PSFs
+    );
+
+// Perform stacking on a readout
+//
+// Returns an array of pixels to inspect for each input image
+psArray *ppStackReadoutInitial(const pmConfig *config,   // Configuration
+                               pmReadout *outRO,   // Output readout
+                               const psArray *readouts, // Input readouts
+                               const psArray *regions, // Array with array of regions used in each PSF match
+                               const psArray *kernels, // Array with array of kernels used in each PSF match
+                               const psVector *addVariance // Additional variance for rejection
+    );
+
+// Thread entry point for ppStackReadoutInitial
+bool ppStackReadoutInitialThread(psThreadJob *job // Job to process
+    );
+
+// Concatenate inspection lists for each input image
+bool ppStackInspect(psThreadJob *job    // Job to process
+    );
+
+// Perform stacking on a readout
+bool ppStackReadoutFinal(const pmConfig *config,   // Configuration
+                         pmReadout *outRO,   // Output readout
+                         const psArray *readouts, // Input readouts
+                         const psArray *rejected // Array with pixels rejected in each image
+    );
+
+// Thread entry point for ppStackReadoutFinal
+bool ppStackReadoutFinalThread(psThreadJob *job // Job to process
+    );
+
+// Perform photometry on stack
+bool ppStackPhotometry(pmConfig *config, // Configuration
+                       const pmReadout *readout, // Readout to be photometered
+                       const pmFPAview *view // View to readout
+    );
+
+// Return software version
+psString ppStackVersion(void);
+
+// Return long description of software version
+psString ppStackVersionLong(void);
+
+// Supplement metadata with software version
+void ppStackVersionMetadata(psMetadata *metadata // Metadata to supplement
+    );
+
+/// 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
+                  const psArray *sources, // Array of sources
+                  const pmPSF *psf,     // Target PSF
+                  psRandom *rng,        // Random number generator
+                  const pmConfig *config // Configuration
+    );
+
+
+/// Add sources to source lists, removing duplicates and solving for magnitude differences
+///
+/// Corrects the sources to have consistent magnitudes.  Returns the source lists.
+psArray *ppStackSourceListAdd(psArray *lists, // List to which to add, or NULL
+                              psArray *sources, // Sources to add
+                              const pmConfig *config // Configuration
+    );
+
+/// Combine source lists to yield a set of unique sources.
+///
+/// Corrects the sources to have consistent magnitudes where possible.  Returns the sources
+psArray *ppStackSourceListCombine(psArray *lists, // Source lists
+                                  const pmConfig *config // Configuration
+    );
+
+/// Print sources into a ds9 regions file
+void ppStackSourcesPrint(const psArray *sources // Sources to print
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackArguments.c	(revision 22322)
@@ -0,0 +1,298 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+
+// XXX add in the version info as in ppImage
+
+// Print usage information and die
+static void usage(const char *program,  // Name of the program
+                  psMetadata *arguments, // Command-line arguments
+                  pmConfig *config      // Configuration
+    )
+{
+    fprintf(stderr, "\nPan-STARRS Image combination\n\n");
+    fprintf(stderr,
+            "Usage: %s 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"
+            "\tMASK(STR):      Mask filename\n"
+            "\tWEIGHT(STR):    Weight map filename\n"
+            "\tPSF(STR):       PSF filename\n"
+            "\tSOURCES(STR):   Sources filename\n"
+            "\tWEIGHTING(F32): Relative weighting to be applied\n",
+            program);
+    fprintf(stderr, "\n");
+    psArgumentHelp(arguments);
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+    exit(PS_EXIT_CONFIG_ERROR);
+}
+
+// Get a float-point value from the command-line or recipe, and add it to the arguments
+#define VALUE_ARG_RECIPE_FLOAT(ARGNAME, RECIPENAME, TYPE) { \
+    ps##TYPE value = psMetadataLookup##TYPE(NULL, config->arguments, ARGNAME); \
+    if (isnan(value)) { \
+        bool mdok; \
+        value = psMetadataLookup##TYPE(&mdok, recipe, RECIPENAME); \
+        if (!mdok) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
+                RECIPENAME, PPSTACK_RECIPE); \
+            goto ERROR; \
+        } \
+    } \
+    psMetadataAdd##TYPE(recipe, PS_LIST_TAIL, RECIPENAME, PS_META_REPLACE, NULL, value); \
+}
+
+// Get an integer value from the command-line or recipe, and add it to the arguments
+#define VALUE_ARG_RECIPE_INT(ARGNAME, RECIPENAME, TYPE, UNSET) { \
+    ps##TYPE value = psMetadataLookup##TYPE(NULL, config->arguments, ARGNAME); \
+    if (value == UNSET) { \
+        bool mdok; \
+        value = psMetadataLookup##TYPE(&mdok, recipe, RECIPENAME); \
+        if (!mdok) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
+                RECIPENAME, PPSTACK_RECIPE); \
+            goto ERROR; \
+        } \
+    } \
+    psMetadataAdd##TYPE(recipe, PS_LIST_TAIL, RECIPENAME, PS_META_REPLACE, NULL, value); \
+}
+
+// Get a mask value from the command-line or recipe, and add it to the arguments
+#define VALUE_ARG_RECIPE_MASK(ARGNAME, RECIPENAME) { \
+    bool mdok; \
+    const char *name = psMetadataLookupStr(&mdok, config->arguments, ARGNAME); \
+    if (!mdok || !name || strlen(name) == 0) { \
+        name = psMetadataLookupStr(NULL, recipe, RECIPENAME); \
+        if (!name) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
+                RECIPENAME, PPSTACK_RECIPE); \
+            goto ERROR; \
+        } \
+    } \
+    psMaskType value = pmConfigMaskGet(name, config); \
+    psMetadataAddU8(recipe, PS_LIST_TAIL, RECIPENAME, PS_META_REPLACE, NULL, value); \
+}
+
+// Get a string value from the command-line and add it to the target
+static bool valueArgStr(psMetadata *arguments, // Command-line arguments
+                        const char *argName, // Argument name in the command-line arguments
+                        const char *mdName, // Name for value in the metadata
+                        psMetadata *target // Target metadata to which to add value
+                        )
+{
+    psString value = psMetadataLookupStr(NULL, arguments, argName); // Value of interest
+    if (value && strlen(value) > 0) {
+        return psMetadataAddStr(target, PS_LIST_TAIL, mdName, PS_META_REPLACE, NULL, value);
+    }
+    return false;
+}
+
+// Get a string value from the command-line or recipe and add it to the target
+static bool valueArgRecipeStr(psMetadata *arguments, // Command-line arguments
+                              psMetadata *recipe, // Recipe
+                              const char *argName, // Argument name in the command-line arguments
+                              const char *mdName, // Name for value in the metadata and recipe
+                              psMetadata *target // Target metadata to which to add value
+                              )
+{
+    psString value = psMetadataLookupStr(NULL, arguments, argName); // Value of interest
+    if (!value) {
+        value = psMetadataLookupStr(NULL, recipe, mdName);
+        if (!value) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s",
+                    mdName, PPSTACK_RECIPE);
+            return false;
+        }
+    }
+    return psMetadataAddStr(target, PS_LIST_TAIL, mdName, PS_META_REPLACE, NULL, value);
+}
+
+bool ppStackArgumentsSetup(int argc, char *argv[], pmConfig *config)
+{
+    assert(config);
+
+    // This capability makes things much faster when debugging
+    bool debugStack = false;            // Read old convolutions to debug the stacking?
+    int argNum = psArgumentGet(argc, argv, "-debug-stack"); // Argument number
+    if (argNum > 0) {
+        debugStack = true;
+        psArgumentRemove(argNum, &argc, argv);
+    }
+
+    pmConfigFileSetsMD(config->arguments, &argc, argv, "PPSTACK.SOURCES", "-sources", NULL);
+
+    if ((argNum = psArgumentGet(argc, argv, "-dumpconfig"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "DUMP_CONFIG", PS_META_REPLACE,
+                         "Filename for configuration dump", argv[argNum]);
+        psArgumentRemove(argNum, &argc, argv);
+    }
+
+
+
+    psMetadata *arguments = config->arguments; // Command-line arguments
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stamps", 0, "Stamps file with x,y,flux per line", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stats", 0, "Statistics file", NULL);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-iter", 0, "Number of rejection iterations", 0);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-combine-rej", 0,
+                     "Combination rejection thresold (sigma)", NAN);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-mask-val", 0, "Mask value of input bad pixels", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-mask-bad", 0, "Mask value to give bad pixels", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-mask-poor", 0, "Mask value to give poor pixels", NULL);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-threshold-mask", 0, "Threshold for mask deconvolution", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-poor-frac", 0, "Fraction of weight for poor pixels", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-image-rej", 0,
+                     "Pixel rejection fraction threshold for rejecting entire image", NAN);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-rows", 0, "Rows to read at once", 0);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-photometry", 0, "Do photometry on stacked image?", false);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-psf-instances", 0,
+                     "Number of instances for PSF generation", 0);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-psf-radius", 0, "Radius for PSF generation", NAN);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-psf-model", 0, "Model name for PSF generation", NULL);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-psf-order", 0, "Spatial order for PSF generation", 0);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-variance", 0, "Use variance for rejection?", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-safe", 0,
+                      "Play safe with small numbers of pixels to combine?", false);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-source-radius", 0, "Source exclusion radius", NAN);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-source-iter", 0, "Source clipping iterations", 0);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-source-rej", 0, "Source clipping rejection level", NAN);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-source-min", 0, "Source minimum overlap", 0);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-renorm", 0, "Renormalise variance maps?", false);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-renorm-mean", 0,
+                     "Statistic for mean in renormalisation", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-renorm-stdev", 0,
+                     "Statistic for stdev in renormalisation", NULL);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-renorm-width", 0, "Width of renormalisation boxes", 0);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-temp-image", 0, "Suffix for temporary images", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-temp-mask", 0, "Suffix for temporary masks", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-temp-weight", 0, "Suffix for temporary weight maps", NULL);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-temp-delete", 0,
+                      "Delete temporary files on completion?", false);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-threads", 0, "Number of threads to use", 0);
+
+    if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 3) {
+        usage(argv[0], arguments, config);
+    }
+
+    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);
+
+    int numThreads = psMetadataLookupS32(NULL, arguments, "-threads"); // Number of threads
+    if (numThreads > 0 && !psThreadPoolInit(numThreads)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to setup %d threads", numThreads);
+        return false;
+    }
+
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "PPSTACK.DEBUG.STACK", 0,
+                      "Read old convolved images to debug stack?", debugStack);
+
+    return true;
+}
+
+bool ppStackArgumentsParse(pmConfig *config)
+{
+    assert(config);
+
+    psMetadata *arguments = config->arguments; // Command-line arguments
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // Recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSTACK_RECIPE);
+        goto ERROR;
+    }
+
+    VALUE_ARG_RECIPE_INT("-iter",             "ITER",           S32, 0);
+    VALUE_ARG_RECIPE_FLOAT("-combine-rej",    "COMBINE.REJ",    F32);
+    VALUE_ARG_RECIPE_FLOAT("-threshold-mask", "THRESHOLD.MASK", F32);
+    VALUE_ARG_RECIPE_FLOAT("-image-rej",      "IMAGE.REJ",      F32);
+    VALUE_ARG_RECIPE_INT("-rows",             "ROWS",           S32, 0);
+    VALUE_ARG_RECIPE_FLOAT("-poor-frac",      "POOR.FRACTION",  F32);
+
+    valueArgRecipeStr(arguments, recipe, "-mask-val",  "MASK.VAL",  recipe);
+    valueArgRecipeStr(arguments, recipe, "-mask-bad",  "MASK.BAD",  recipe);
+    valueArgRecipeStr(arguments, recipe, "-mask-poor", "MASK.POOR", recipe);
+
+    VALUE_ARG_RECIPE_FLOAT("-source-radius", "SOURCE.RADIUS", F32);
+    VALUE_ARG_RECIPE_INT("-source-iter",     "SOURCE.ITER",   S32, 0);
+    VALUE_ARG_RECIPE_FLOAT("-source-rej",    "SOURCE.REJ",    F32);
+    VALUE_ARG_RECIPE_INT("-source-min",      "SOURCE.MIN",    S32, 0);
+
+    VALUE_ARG_RECIPE_INT("-psf-instances", "PSF.INSTANCES", S32, 0);
+    VALUE_ARG_RECIPE_FLOAT("-psf-radius",  "PSF.RADIUS",    F32);
+    VALUE_ARG_RECIPE_INT("-psf-order",     "PSF.ORDER",     S32, 0);
+    valueArgRecipeStr(arguments, recipe, "-psf-model", "PSF.MODEL", recipe);
+
+    if (psMetadataLookupBool(NULL, arguments, "-photometry") ||
+        psMetadataLookupBool(NULL, recipe, "PHOTOMETRY")) {
+        psMetadataAddBool(recipe, PS_LIST_TAIL, "PHOTOMETRY", PS_META_REPLACE,
+                          "Do photometry on stacked image?", true);
+    }
+
+    if (psMetadataLookupBool(NULL, arguments, "-variance") ||
+        psMetadataLookupBool(NULL, recipe, "VARIANCE")) {
+        psMetadataAddBool(arguments, PS_LIST_TAIL, "VARIANCE", 0, "Use variance for rejection?", true);
+    }
+
+    if (psMetadataLookupBool(NULL, arguments, "-safe") ||
+        psMetadataLookupBool(NULL, recipe, "SAFE")) {
+        psMetadataAddBool(arguments, PS_LIST_TAIL, "SAFE", 0,
+                          "Play safe with small number of pixels to combine?", true);
+    }
+
+    if (psMetadataLookupBool(NULL, arguments, "-renorm") ||
+        psMetadataLookupBool(NULL, recipe, "RENORM")) {
+        psMetadataAddBool(arguments, PS_LIST_TAIL, "RENORM", 0, "Renormalise variance maps?", true);
+    }
+    VALUE_ARG_RECIPE_INT("-renorm-width", "RENORM.WIDTH", S32, 0);
+    valueArgRecipeStr(arguments, recipe, "-renorm-mean", "RENORM.MEAN", recipe);
+    valueArgRecipeStr(arguments, recipe, "-renorm-stdev", "RENORM.STDEV", recipe);
+
+    valueArgRecipeStr(arguments, recipe, "-temp-image",  "TEMP.IMAGE",  recipe);
+    valueArgRecipeStr(arguments, recipe, "-temp-mask",   "TEMP.MASK",   recipe);
+    valueArgRecipeStr(arguments, recipe, "-temp-weight", "TEMP.WEIGHT", recipe);
+
+    if (psMetadataLookupBool(NULL, arguments, "-temp-delete") ||
+        psMetadataLookupBool(NULL, recipe, "TEMP.DELETE")) {
+        psMetadataAddBool(arguments, PS_LIST_TAIL, "TEMP.DELETE", 0,
+                          "Delete temporary files on completion?", true);
+    }
+
+    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");
+
+        pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSTACK.INPUT"); // Input file
+        pmConfigDump(config, input->fpa, dump_file);
+    }
+
+    return true;
+
+ERROR:
+    return false;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackCamera.c	(revision 22322)
@@ -0,0 +1,429 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <psphot.h>
+
+#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
+    )
+{
+    pmFPAfile *file = pmFPAfileDefineOutput(config, fpa, name);
+    if (!file) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to define output convolved file %s", name);
+        return NULL;
+    }
+    if (file->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPSTACK.OUTCONV is not of type %s", pmFPAfileStringFromType(type));
+        return NULL;
+    }
+
+    return file;
+}
+
+// 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 haveWeights = false;           // Do we have weight maps?
+    bool havePSFs = false;              // Do we have PSFs?
+    bool haveSources = (bool)psMetadataLookup(config->arguments, "PPSTACK.SOURCES"); // Have global sources?
+
+    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 weight = psMetadataLookupStr(&mdok, input, "WEIGHT"); // Name of weight map
+        psString psf = psMetadataLookupStr(&mdok, input, "PSF"); // Name of PSF
+        psString sources = psMetadataLookupStr(&mdok, input, "SOURCES"); // Name of sources
+
+        float weighting = psMetadataLookupF32(&mdok, input, "WEIGHTING"); // Relative weighting
+        if (!mdok || isnan(weighting) || weighting == 0.0) {
+            psWarning("Component %s lacks WEIGHTING of type F32 --- assuming 1.0", item->name);
+            weighting = 1.0;
+        }
+
+        // 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");
+            if (!status) {
+                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 weight file
+        if (weight && strlen(weight) > 0) {
+            haveWeights = true;
+            psArray *weightFiles = psArrayAlloc(1); // Array of filenames for this FPA
+            weightFiles->data[0] = psMemIncrRefCounter(weight);
+            psMetadataAddArray(config->arguments, PS_LIST_TAIL, "WEIGHT.FILENAMES", PS_META_REPLACE,
+                               "Filenames of weight files", weightFiles);
+            psFree(weightFiles);
+
+            bool status;
+            pmFPAfile *weightFile = pmFPAfileBindFromArgs(&status, imageFile, config, "PPSTACK.INPUT.WEIGHT",
+                                                          "WEIGHT.FILENAMES");
+            if (!status) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to define file from weight %d (%s)", i, weight);
+                return false;
+            }
+            if (weightFile->type != PM_FPA_FILE_WEIGHT) {
+                psError(PS_ERR_IO, true, "PPSTACK.INPUT.WEIGHT is not of type WEIGHT");
+                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);
+                    return false;
+                }
+                if (psfFile->type != PM_FPA_FILE_PSF) {
+                    psError(PS_ERR_IO, true, "PPSTACK.INPUT.PSF is not of type PSF");
+                    return false;
+                }
+                havePSFs = true;
+            }
+        }
+
+        // Add the sources file
+        if (!haveSources) {
+            if (!sources || strlen(sources) == 0) {
+                if (havePSFs) {
+                    psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find SOURCES %d", i);
+                    return false;
+                }
+            } else {
+                if (!havePSFs) {
+                    psWarning("SOURCES provided without PSF --- ignoring.");
+                } else {
+                    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 *outconvWeight = defineOutputConvolved("PPSTACK.OUTCONV.WEIGHT", imageFile->fpa, config,
+                                                         PM_FPA_FILE_WEIGHT);
+        if (!outconvImage || !outconvMask || !outconvWeight) {
+            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 *inconvWeight = defineInputConvolved("PPSTACK.INCONV.WEIGHT", outconvWeight, config,
+                                                       PM_FPA_FILE_WEIGHT);
+        if (!inconvImage || !inconvMask || !inconvWeight) {
+            return false;
+        }
+#endif
+
+        psMetadataAddF32(imageFile->fpa->analysis, PS_LIST_TAIL, "PPSTACK.WEIGHTING", 0,
+                         "Relative weighting for image", weighting);
+
+        i++;
+    }
+    psFree(iter);
+    psMetadataRemoveKey(config->arguments, "IMAGE.FILENAMES");
+    if (psMetadataLookup(config->arguments, "MASK.FILENAMES")) {
+        psMetadataRemoveKey(config->arguments, "MASK.FILENAMES");
+    }
+    if (psMetadataLookup(config->arguments, "WEIGHT.FILENAMES")) {
+        psMetadataRemoveKey(config->arguments, "WEIGHT.FILENAMES");
+    }
+    if (psMetadataLookup(config->arguments, "PSF.FILENAMES")) {
+        psMetadataRemoveKey(config->arguments, "PSF.FILENAMES");
+    }
+    if (psMetadataLookup(config->arguments, "SOURCES.FILENAMES")) {
+        psMetadataRemoveKey(config->arguments, "SOURCES.FILENAMES");
+    }
+
+    if (haveSources) {
+        // Global list of sources for use as stamps
+        bool status = false;            // Found the file?
+        if (havePSFs) {
+            pmFPAfile *sources = pmFPAfileDefineFromArgs(&status, config, "PPSTACK.INPUT.SOURCES",
+                                                         "PPSTACK.SOURCES");
+            if (!status) {
+                psError(PS_ERR_IO, false, "Failed to load file definition PPSTACK.INPUT.SOURCES");
+                return false;
+            }
+            if (sources && sources->type != PM_FPA_FILE_CMF) {
+                psError(PS_ERR_IO, true, "PPSTACK.SOURCES is not of type CMF");
+                return false;
+            }
+        }
+    }
+
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "INPUTS.NUM", 0, "Number of input files", i);
+    psMetadataAddBool(config->arguments, PS_LIST_TAIL, "HAVE.PSF", 0, "Have PSFs available?", havePSFs);
+    psMetadataAddBool(config->arguments, PS_LIST_TAIL, "HAVE.SOURCES", 0, "Have global sources?",
+                      haveSources);
+
+    // Output image
+    pmFPA *fpa = pmFPAConstruct(config->camera, config->cameraName); // FPA to contain the output
+    if (!fpa) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to construct an FPA from camera configuration.");
+        return false;
+    }
+    pmFPAfile *output = pmFPAfileDefineOutput(config, fpa, "PPSTACK.OUTPUT");
+    psFree(fpa);                        // Drop reference
+    if (!output) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.OUTPUT"));
+        return false;
+    }
+    if (output->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPSTACK.OUTPUT is not of type IMAGE");
+        return false;
+    }
+    output->save = true;
+
+    if (!pmFPAAddSourceFromFormat(fpa, "Stack", output->format)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate output FPA.");
+        psFree(fpa);
+        return false;
+    }
+
+    // Output mask
+    pmFPAfile *outMask = pmFPAfileDefineOutput(config, output->fpa, "PPSTACK.OUTPUT.MASK");
+    if (!outMask) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.OUTPUT.MASK"));
+        return false;
+    }
+    if (outMask->type != PM_FPA_FILE_MASK) {
+        psError(PS_ERR_IO, true, "PPSTACK.OUTPUT.MASK is not of type MASK");
+        return false;
+    }
+    outMask->save = true;
+
+    // Output weight
+    if (haveWeights) {
+        pmFPAfile *outWeight = pmFPAfileDefineOutput(config, output->fpa, "PPSTACK.OUTPUT.WEIGHT");
+        if (!outWeight) {
+            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.OUTPUT.WEIGHT"));
+            return false;
+        }
+        if (outWeight->type != PM_FPA_FILE_WEIGHT) {
+            psError(PS_ERR_IO, true, "PPSTACK.OUTPUT.WEIGHT is not of type WEIGHT");
+            return false;
+        }
+        outWeight->save = true;
+    }
+
+    if (havePSFs) {
+        pmFPAfile *targetPSF = pmFPAfileDefineOutput(config, output->fpa, "PPSTACK.TARGET.PSF");
+        if (!targetPSF) {
+            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.TARGET.PSF"));
+            return false;
+        }
+        if (targetPSF->type != PM_FPA_FILE_PSF) {
+            psError(PS_ERR_IO, true, "PPSTACK.TARGET.PSF is not of type PSF");
+            return false;
+        }
+        targetPSF->save = true;
+    }
+
+    // Output JPEGs
+    pmFPAfile *jpeg1 = pmFPAfileDefineOutput(config, NULL, "PPSTACK.OUTPUT.JPEG1");
+    if (!jpeg1) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.OUTPUT.JPEG1"));
+        return false;
+    }
+    if (jpeg1->type != PM_FPA_FILE_JPEG) {
+        psError(PS_ERR_IO, true, "PPSTACK.OUTPUT.JPEG1 is not of type JPEG");
+        return false;
+    }
+    jpeg1->save = true;
+    pmFPAfile *jpeg2 = pmFPAfileDefineOutput(config, NULL, "PPSTACK.OUTPUT.JPEG2");
+    if (!jpeg2) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.OUTPUT.JPEG2"));
+        return false;
+    }
+    if (jpeg2->type != PM_FPA_FILE_JPEG) {
+        psError(PS_ERR_IO, true, "PPSTACK.OUTPUT.JPEG2 is not of type JPEG");
+        return false;
+    }
+    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
+    // we create a copy of the mosaicked image for psphot so we can write out a clean image
+    bool mdok = false;
+    bool doPhotom = psMetadataLookupBool(&mdok, recipe, "PHOTOMETRY") ||
+        psMetadataLookupBool(&mdok, config->arguments, "-photometry"); // perform photometry
+    if (doPhotom) {
+        // This file, PSPHOT.INPUT, is just used as a carrier; output files (eg, PSPHOT.RESID) are defined by
+        // psphotDefineFiles
+        pmFPAfile *psphotInput = pmFPAfileDefineFromFPA(config, output->fpa, 1, 1, "PSPHOT.INPUT");
+        if (!psphotInput) {
+            psError(PS_ERR_IO, false, _("Unable to generate output file from PSPHOT.INPUT"));
+            return false;
+        }
+
+        // Define associated psphot input/output files
+        if (!psphotDefineFiles(config, psphotInput)) {
+            psError(PSPHOT_ERR_CONFIG, false,
+                    "Trouble defining the additional input/output files for psphot");
+            return false;
+        }
+    } else {
+        // Output PSF --- only required if photometry is not being performed
+        pmFPAfile *outPSF = pmFPAfileDefineOutputFromFile(config, output, "PSPHOT.PSF.SAVE");
+        if (!outPSF) {
+            psError(PS_ERR_IO, false, _("Unable to generate output file from PSPHOT.PSF.SAVE"));
+            return false;
+        }
+        if (outPSF->type != PM_FPA_FILE_PSF) {
+            psError(PS_ERR_IO, true, "PSPHOT.PSF.SAVE is not of type PSF");
+            return false;
+        }
+        outPSF->save = true;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackLoop.c	(revision 22322)
@@ -0,0 +1,1073 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <libgen.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppStack.h"
+
+//#define TESTING
+
+#define WCS_TOLERANCE 0.001             // Tolerance for WCS
+#define PIXELS_BUFFER 1024              // Initial size of pixel lists
+
+// Here follows lists of files for activation/deactivation at various stages.  Each must be NULL-terminated.
+
+// Files required in preparation for convolution
+static char *prepareFiles[] = { "PPSTACK.INPUT.PSF", "PPSTACK.INPUT.SOURCES", "PPSTACK.TARGET.PSF", NULL };
+
+// Files required for the convolution
+static char *convolveFiles[] = { "PPSTACK.INPUT", "PPSTACK.INPUT.MASK", "PPSTACK.INPUT.WEIGHT", NULL };
+
+// Output files for the combination
+static char *combineFiles[] = { "PPSTACK.OUTPUT", "PPSTACK.OUTPUT.MASK", "PPSTACK.OUTPUT.WEIGHT",
+                                "PPSTACK.OUTPUT.JPEG1", "PPSTACK.OUTPUT.JPEG2", NULL };
+
+// Files for photometry
+static char *photFiles[] = { "PSPHOT.INPUT", "PSPHOT.OUTPUT", "PSPHOT.RESID", "PSPHOT.BACKMDL",
+                             "PSPHOT.BACKMDL.STDEV", "PSPHOT.BACKGND", "PSPHOT.BACKSUB",
+                             "SOURCE.PLOT.MOMENTS", "SOURCE.PLOT.PSFMODEL", "SOURCE.PLOT.APRESID",
+                             "PSPHOT.INPUT.CMF", NULL };
+
+static void memDump(const char *name)
+{
+    return;
+
+    static int num = 0;                 // Counter, to make files unique and give an idea of sequence
+
+    psString filename = NULL;           // Name of file
+    psStringAppend(&filename, "memdump_%s_%03d.txt", name, num);
+    FILE *memFile = fopen(filename, "w");
+    psFree(filename);
+
+    psMemBlock **leaks = NULL;
+    int numLeaks = psMemCheckLeaks(0, &leaks, NULL, true);
+    fprintf(memFile, "# MemBlock Size Source\n");
+    unsigned long total = 0;            // Total memory used
+    for (int i = 0; i < numLeaks; i++) {
+        psMemBlock *mb = leaks[i];
+        fprintf(memFile, "%12lu\t%12zd\t%s:%d\n", mb->id, mb->userMemorySize,
+                mb->file, mb->lineno);
+        total += mb->userMemorySize;
+    }
+    fclose(memFile);
+    psFree(leaks);
+
+    fprintf(stderr, "Memdump %s %d: Memory use: %ld, sbrk: %p\n", name, num, total, sbrk(0));
+    num++;
+}
+
+
+
+// Activate/deactivate a list of files
+static void fileActivation(pmConfig *config, // Configuration
+                           char **files, // Files to turn on/off
+                           bool state   // Activation state
+    )
+{
+    assert(config);
+    assert(files);
+
+    for (int i = 0; files[i] != NULL; i++) {
+        pmFPAfileActivate(config->files, state, files[i]);
+    }
+    return;
+}
+
+// Activate/deactivate a single element for a list
+static void fileActivationSingle(pmConfig *config, // Configuration
+                                 char **files, // Files to turn on/off
+                                 bool state,   // Activation state
+                                 int num // Number of file in sequence
+                                 )
+{
+    assert(config);
+    assert(files);
+
+    for (int i = 0; files[i] != NULL; i++) {
+        pmFPAfileActivateSingle(config->files, state, files[i], num); // Activated file
+    }
+    return;
+}
+
+// Iterate down the hierarchy, loading files; we can get away with this because we're working on skycells
+static pmFPAview *filesIterateDown(pmConfig *config // Configuration
+                                  )
+{
+    assert(config);
+
+    pmFPAview *view = pmFPAviewAlloc(0);// Pointer into FPA hierarchy
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+    view->chip = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+    view->cell = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+    view->readout = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+    return view;
+}
+
+// Iterate up the hierarchy, writing files; we can get away with this because we're working on skycells
+static bool filesIterateUp(pmConfig *config // Configuration
+                           )
+{
+    assert(config);
+
+    pmFPAview *view = pmFPAviewAlloc(0);// Pointer into FPA hierarchy
+    view->chip = view->cell = view->readout = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        return false;
+    }
+    view->readout = -1;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        return false;
+    }
+    view->cell = -1;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        return false;
+    }
+    view->chip = -1;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        return false;
+    }
+    psFree(view);
+    return true;
+}
+
+// Write an image to a FITS file
+static bool writeImage(const char *name, // Name of image
+                       psMetadata *header, // Header
+                       const psImage *image, // Image
+                       pmConfig *config // Configuration
+                       )
+{
+    assert(name);
+    assert(image);
+
+    psString resolved = pmConfigConvertFilename(name, config, true, true); // Resolved file name
+    psFits *fits = psFitsOpen(resolved, "w");
+    if (!fits) {
+        psError(PS_ERR_IO, false, "Unable to open FITS file %s to write image.", resolved);
+        psFree(resolved);
+        return false;
+    }
+    if (!psFitsWriteImage(fits, header, image, 0, NULL)) {
+        psError(PS_ERR_IO, false, "Unable to write FITS image %s.", resolved);
+        psFitsClose(fits);
+        psFree(resolved);
+        return false;
+    }
+    psFitsClose(fits);
+    psFree(resolved);
+    return true;
+}
+
+
+bool ppStackLoop(pmConfig *config)
+{
+    assert(config);
+
+    psTimerStart("PPSTACK_TOTAL");
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    bool mdok;                          // Status of MD lookup
+    bool tempDelete = psMetadataLookupBool(&mdok, recipe, "TEMP.DELETE"); // Delete temporary files?
+    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.");
+        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 *tempWeight = psMetadataLookupStr(NULL, recipe, "TEMP.WEIGHT"); // Suffix for temp weight maps
+    if (!tempImage || !tempMask || !tempWeight) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to find TEMP.IMAGE, TEMP.MASK and TEMP.WEIGHT in recipe");
+        return false;
+    }
+
+    float threshold = psMetadataLookupF32(NULL, recipe, "THRESHOLD.MASK"); // Threshold for mask deconvolution
+    float imageRej = psMetadataLookupF32(NULL, recipe, "IMAGE.REJ"); // Maximum fraction of image to reject
+                                                                     // before rejecting entire image
+    float poorFrac = psMetadataLookupF32(&mdok, recipe, "POOR.FRACTION"); // Fraction for "poor"
+
+    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
+    psMetadata *stats = NULL;           // Container for statistics
+    FILE *statsFile = NULL;             // File stream for statistics
+    if (statsName && strlen(statsName) > 0) {
+        psString resolved = pmConfigConvertFilename(statsName, config, true, true); // Resolved filename
+        statsFile = fopen(resolved, "w");
+        if (!statsFile) {
+            psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
+            psFree(resolved);
+            return false;
+        } else {
+            stats = psMetadataAlloc();
+        }
+        psFree(resolved);
+    }
+
+    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PPSTACK.OUTPUT"); // Output file
+    if (!output) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find output data!");
+        return false;
+    }
+    int num = psMetadataLookupS32(NULL, config->arguments, "INPUTS.NUM"); // Number of inputs
+
+    psMetadata *ppsub = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
+    int overlap = 2 * psMetadataLookupS32(NULL, ppsub,
+                                          "KERNEL.SIZE"); // Overlap by kernel size between consecutive scans
+
+    if (!pmConfigMaskSetBits(NULL, NULL, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine mask value.");
+        return false;
+    }
+
+    memDump("start");
+
+    // Preparation iteration: Load the sources, and get a target PSF model
+    psTrace("ppStack", 1, "Determining target PSF....\n");
+    psArray *globalSources = NULL;      // Global list of sources for matching (haveSources = TRUE)
+    psArray *indSources = psArrayAlloc(num); // Individual lists of sources for matching (haveSources = FALSE)
+    pmPSF *targetPSF = NULL;            // Target PSF
+    bool haveSources = psMetadataLookupBool(NULL, config->arguments, "HAVE.SOURCES"); // Global sources?
+    if (psMetadataLookupBool(NULL, config->arguments, "HAVE.PSF")) {
+        pmFPAfileActivate(config->files, false, NULL);
+        fileActivation(config, prepareFiles, true);
+        pmFPAview *view = filesIterateDown(config);
+        if (!view) {
+            return false;
+        }
+
+        // Generate list of PSFs
+        psMetadataIterator *fileIter = psMetadataIteratorAlloc(config->files, PS_LIST_HEAD,
+                                                               "^PPSTACK.INPUT$"); // Iterator
+        psMetadataItem *fileItem; // Item from iteration
+        psArray *psfs = psArrayAlloc(num); // PSFs for PSF envelope
+        psArray *sourceLists = NULL;    // Source lists for merging sources from multiple readouts
+        int numCols = 0, numRows = 0;   // Size of image
+        int index = 0;                  // Index for file
+        while ((fileItem = psMetadataGetAndIncrement(fileIter))) {
+            assert(fileItem->type == PS_DATA_UNKNOWN);
+            pmFPAfile *inputFile = fileItem->data.V; // An input file
+            pmChip *chip = pmFPAviewThisChip(view, inputFile->fpa); // The chip: holds the PSF
+            pmPSF *psf = psMetadataLookupPtr(NULL, chip->analysis, "PSPHOT.PSF"); // PSF
+            if (!psf) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to find PSF.");
+                psFree(view);
+                psFree(globalSources);
+                psFree(indSources);
+                psFree(sourceLists);
+                psFree(fileIter);
+                psFree(psfs);
+                return false;
+            }
+            psfs->data[index] = psMemIncrRefCounter(psf);
+            psMetadataRemoveKey(chip->analysis, "PSPHOT.PSF");
+
+            pmCell *cell = pmFPAviewThisCell(view, inputFile->fpa); // Cell of interest
+            pmHDU *hdu = pmHDUFromCell(cell);
+            assert(hdu && hdu->header);
+            int naxis1 = psMetadataLookupS32(NULL, hdu->header, "NAXIS1"); // Number of columns
+            int naxis2 = psMetadataLookupS32(NULL, hdu->header, "NAXIS2"); // Number of rows
+            if (naxis1 <= 0 || naxis2 <= 0) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to determine size of image from PSF.");
+                psFree(view);
+                psFree(globalSources);
+                psFree(indSources);
+                psFree(sourceLists);
+                psFree(fileIter);
+                psFree(psfs);
+                return false;
+            }
+            if (numCols == 0 && numRows == 0) {
+                numCols = naxis1;
+                numRows = naxis2;
+            }
+
+            if (!haveSources) {
+                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;
+                }
+                indSources->data[index] = psMemIncrRefCounter(sources);
+#ifdef TESTING
+                ppStackSourcesPrint(sources);
+#endif
+                sourceLists = ppStackSourceListAdd(sourceLists, sources, config);
+                if (!sourceLists) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to add sources to list.");
+                    psFree(view);
+                    psFree(globalSources);
+                    psFree(indSources);
+                    psFree(fileIter);
+                    psFree(psfs);
+                    return false;
+                }
+            }
+
+            index++;
+        }
+        psFree(fileIter);
+
+        targetPSF = ppStackPSF(config, numCols, numRows, psfs);
+        psFree(psfs);
+        if (!targetPSF) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine output PSF.");
+            psFree(globalSources);
+            psFree(indSources);
+            psFree(view);
+            return false;
+        }
+
+        pmChip *outChip = pmFPAviewThisChip(view, output->fpa); // Output chip
+        psMetadataAddPtr(outChip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN,
+                         "Target PSF", targetPSF);
+        outChip->data_exists = true;
+
+        if (haveSources) {
+            // We want to hang on to the 'sources' even when its host FPA is blown away
+            pmReadout *sourcesRO = pmFPAfileThisReadout(config->files, view, "PPSTACK.INPUT.SOURCES");
+            globalSources = psMetadataLookupPtr(NULL, sourcesRO->analysis, "PSPHOT.SOURCES");
+            psMemIncrRefCounter(globalSources);
+            if (!globalSources) {
+                psError(PS_ERR_UNKNOWN, true, "Unable to find sources.");
+                psFree(view);
+                return false;
+            }
+        } else {
+            globalSources = ppStackSourceListCombine(sourceLists, config);
+            psFree(sourceLists);
+            if (!globalSources) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to add sources to list.");
+                psFree(view);
+                psFree(psfs);
+                return false;
+            }
+#ifdef TESTING
+            ppStackSourcesPrint(globalSources);
+#endif
+        }
+
+        psFree(view);
+        filesIterateUp(config);
+    }
+
+    memDump("psf");
+
+    psArray *imageNames = psArrayAlloc(num);
+    psArray *maskNames = psArrayAlloc(num);
+    psArray *weightNames = psArrayAlloc(num);
+    for (int i = 0; i < num; i++) {
+        psString imageName = NULL, maskName = NULL, weightName = 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(&weightName, "%s/%s.%d.%s", tempDir, tempName, i, tempWeight);
+        psTrace("ppStack", 5, "Temporary files: %s %s %s\n", imageName, maskName, weightName);
+        imageNames->data[i] = imageName;
+        maskNames->data[i] = maskName;
+        weightNames->data[i] = weightName;
+    }
+    // Free the outputName that we grabbed above to set up tempName.
+    psFree(outputName);
+
+    // Generate convolutions and write them to disk
+    psTrace("ppStack", 1, "Convolving inputs to target PSF....\n");
+    psArray *cells = psArrayAlloc(num); // Cells for convolved images --- a handle for reading again
+    psArray *subKernels = psArrayAlloc(num); // Subtraction kernels --- required in the stacking
+    psArray *subRegions = psArrayAlloc(num); // Subtraction regions --- required in the stacking
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+    int numGood = 0;                    // Number of good frames
+    int numCols = 0, numRows = 0;       // Size of image
+    psVector *inputMask = psVectorAlloc(num, PS_TYPE_U8); // Mask for inputs
+    psVectorInit(inputMask, 0);
+    psVector *matchChi2 = psVectorAlloc(num, PS_TYPE_F32); // chi^2 for stamps when matching
+    psVectorInit(matchChi2, NAN);
+    for (int i = 0; i < num; i++) {
+        psTrace("ppStack", 2, "Convolving input %d of %d to target PSF....\n", i, num);
+        pmFPAfileActivate(config->files, false, NULL);
+        fileActivationSingle(config, convolveFiles, true, i);
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT", i); // File of interest
+        pmFPAview *view = filesIterateDown(config);
+        if (!view) {
+            psFree(globalSources);
+            psFree(indSources);
+            psFree(targetPSF);
+            psFree(rng);
+            psFree(inputMask);
+            psFree(matchChi2);
+            return false;
+        }
+
+        pmReadout *readout = pmFPAviewThisReadout(view, file->fpa); // Input readout
+        psFree(view);
+
+        if (numCols == 0 && numRows == 0) {
+            numCols = readout->image->numCols;
+            numRows = readout->image->numRows;
+        } else if (numCols != readout->image->numCols || numRows != readout->image->numRows) {
+            psError(PS_ERR_UNKNOWN, true, "Sizes of input images don't match: %dx%d vs %dx%d",
+                    readout->image->numCols, readout->image->numRows, numCols, numRows);
+            psFree(globalSources);
+            psFree(indSources);
+            psFree(targetPSF);
+            psFree(rng);
+            psFree(inputMask);
+            psFree(matchChi2);
+            return false;
+        }
+
+        // Background subtraction, scaling and normalisation is performed automatically by the image matching
+        psArray *regions = NULL, *kernels = NULL; // Regions and kernels used in subtraction
+        psArray *sources = haveSources ? globalSources : indSources->data[i]; // Sources for matching
+        psTimerStart("PPSTACK_MATCH");
+        if (!ppStackMatch(readout, &regions, &kernels, &matchChi2->data.F32[i],
+                          sources, targetPSF, rng, config)) {
+            psErrorStackPrint(stderr, "Unable to match image %d --- ignoring.", i);
+            inputMask->data.U8[i] = PPSTACK_MASK_MATCH;
+            psErrorClear();
+            continue;
+        }
+
+        if (stats) {
+            pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, readout->analysis,
+                                                                PM_SUBTRACTION_ANALYSIS_KERNEL);// Conv kernel
+            psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_MATCH", PS_META_DUPLICATE_OK,
+                             "Time to match PSF", psTimerMark("PPSTACK_MATCH"));
+            psMetadataAddF32(stats, PS_LIST_TAIL, "STAMP.MEAN", PS_META_DUPLICATE_OK,
+                             "Mean deviation for stamps", kernels->mean);
+            psMetadataAddF32(stats, PS_LIST_TAIL, "STAMP.RMS", PS_META_DUPLICATE_OK,
+                             "RMS deviation for stamps", kernels->rms);
+            psMetadataAddF32(stats, PS_LIST_TAIL, "STAMP.NUM", PS_META_DUPLICATE_OK,
+                             "Number of stamps", kernels->numStamps);
+
+        }
+        psLogMsg("ppStack", PS_LOG_INFO, "Time to match image %d: %f sec", i, psTimerClear("PPSTACK_MATCH"));
+
+        subRegions->data[i] = regions;
+        subKernels->data[i] = kernels;
+
+        // Write the temporary convolved files
+        pmHDU *hdu = readout->parent->parent->parent->hdu; // HDU for convolved image
+        assert(hdu);
+        writeImage(imageNames->data[i],  hdu->header, readout->image, config);
+        writeImage(maskNames->data[i],   hdu->header, readout->mask, config);
+        writeImage(weightNames->data[i], hdu->header, readout->weight, config);
+
+        cells->data[i] = psMemIncrRefCounter(readout->parent);
+        filesIterateUp(config);
+        numGood++;
+
+        memDump("match");
+    }
+    psFree(globalSources);
+    psFree(indSources);
+    psFree(targetPSF);
+    psFree(rng);
+
+    if (numGood == 0) {
+        psError(PS_ERR_UNKNOWN, false, "No good images to combine.");
+        psFree(subKernels);
+        psFree(subRegions);
+        psFree(cells);
+        psFree(inputMask);
+        psFree(inputMask);
+        psFree(matchChi2);
+        return false;
+    }
+
+#ifdef TESTING
+    psTraceSetLevel("psModules.imcombine", 7);
+#endif
+
+
+    // Start threading
+    ppStackThreadInit();
+    ppStackThreadData *stack = ppStackThreadDataSetup(cells, imageNames, maskNames, weightNames, config);
+
+    psTimerStart("PPSTACK_INITIAL");
+
+    memDump("preinitial");
+
+    // Stack the convolved files
+    psTrace("ppStack", 1, "Initial stack of convolved images....\n");
+    pmReadout *outRO = NULL;            // Output readout
+    pmFPAview *view = NULL;             // View to readout
+    psArray *inspect = NULL;            // Array of arrays of pixels to inspect
+    psVector *exptimes = psVectorAlloc(num, PS_TYPE_F32); // Exposure times for each input
+    {
+        int row0, col0;                 // Offset for readout
+        int numCols, numRows;           // Size of readout
+        ppStackThread *thread = stack->threads->data[0]; // Representative thread
+        if (!pmReadoutStackSetOutputSize(&col0, &row0, &numCols, &numRows, thread->readouts)) {
+            psError(PS_ERR_UNKNOWN, false, "problem setting output readout size.");
+            psFree(subKernels);
+            psFree(subRegions);
+            psFree(stack);
+            psFree(inputMask);
+            psFree(matchChi2);
+            psFree(exptimes);
+            psFree(cells);
+            return false;
+        }
+
+        pmFPAfileActivate(config->files, false, NULL);
+        fileActivation(config, combineFiles, true);
+        view = filesIterateDown(config);
+        if (!view) {
+            psFree(subKernels);
+            psFree(subRegions);
+            psFree(stack);
+            psFree(inputMask);
+            psFree(matchChi2);
+            psFree(exptimes);
+            psFree(cells);
+            return false;
+        }
+
+        pmCell *outCell = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT"); // Output cell
+        outRO = pmReadoutAlloc(outCell); // Output readout
+
+        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
+        psMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+        if (!pmReadoutStackDefineOutput(outRO, col0, row0, numCols, numRows, true, true, maskBad)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to prepare output.");
+            psFree(subKernels);
+            psFree(subRegions);
+            psFree(stack);
+            psFree(inputMask);
+            psFree(matchChi2);
+            psFree(view);
+            psFree(outRO);
+            psFree(exptimes);
+            psFree(cells);
+            return false;
+        }
+
+        psList *fpaList = psListAlloc(NULL); // List of input FPAs, for concept averaging
+        psList *cellList = psListAlloc(NULL); // List of input cells, for concept averaging
+        for (int i = 0; i < num; i++) {
+            pmCell *inCell = cells->data[i]; // Input cell
+            if (!inCell) {
+                exptimes->data.F32[i] = 0.0;
+                continue;
+            }
+            exptimes->data.F32[i] = psMetadataLookupF32(NULL, inCell->concepts, "CELL.EXPOSURE");
+            psListAdd(cellList, PS_LIST_TAIL, inCell);
+            psListAdd(fpaList, PS_LIST_TAIL, inCell->parent->parent);
+        }
+
+        // Copy concepts
+        pmChip *outChip = outCell->parent;  // Output chip
+        pmFPA *outFPA = outChip->parent;    // Output FPA
+        pmConceptsAverageFPAs(outFPA, fpaList);
+        pmConceptsAverageCells(outCell, cellList, NULL, NULL, true);
+        psFree(fpaList);
+        psFree(cellList);
+
+        psFree(cells);
+
+        bool status;                    // Status of read
+        int numChunk;                   // Number of chunks
+        for (numChunk = 0; true; numChunk++) {
+            ppStackThread *thread = ppStackThreadRead(&status, stack, config, numChunk, overlap);
+            if (!status) {
+                // Something went wrong
+                psError(PS_ERR_UNKNOWN, false, "Unable to read chunk %d", numChunk);
+                psFree(subKernels);
+                psFree(subRegions);
+                psFree(stack);
+                psFree(inputMask);
+                psFree(matchChi2);
+                psFree(view);
+                psFree(outRO);
+                psFree(exptimes);
+                return false;
+            }
+            if (!thread) {
+                // Nothing more to read
+                break;
+            }
+
+            // call: ppStackReadoutInitial(config, outRO, readouts, subRegions, subKernels)
+            psThreadJob *job = psThreadJobAlloc("PPSTACK_INITIAL_COMBINE"); // Job to start
+            psArrayAdd(job->args, 1, thread);
+            psArrayAdd(job->args, 1, config);
+            psArrayAdd(job->args, 1, outRO);
+            psArrayAdd(job->args, 1, subRegions);
+            psArrayAdd(job->args, 1, subKernels);
+            psArrayAdd(job->args, 1, matchChi2);
+            if (!psThreadJobAddPending(job)) {
+                psFree(job);
+                psFree(subKernels);
+                psFree(subRegions);
+                psFree(stack);
+                psFree(inputMask);
+                psFree(matchChi2);
+                psFree(view);
+                psFree(outRO);
+                psFree(exptimes);
+                return false;
+            }
+            psFree(job);
+        }
+
+        if (!psThreadPoolWait(false)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to do initial combination.");
+            psFree(subKernels);
+            psFree(subRegions);
+            psFree(stack);
+            psFree(inputMask);
+            psFree(matchChi2);
+            psFree(view);
+            psFree(outRO);
+            psFree(exptimes);
+            return false;
+        }
+        psFree(matchChi2);
+
+        // Harvest the jobs, gathering the inspection lists
+        inspect = psArrayAlloc(num);
+        for (int i = 0; i < num; i++) {
+            if (inputMask->data.U8[i]) {
+                continue;
+            }
+            inspect->data[i] = psArrayAllocEmpty(numChunk);
+        }
+        psThreadJob *job;               // Completed job
+        while ((job = psThreadJobGetDone())) {
+            psArray *results = job->results; // Results of job
+            for (int i = 0; i < num; i++) {
+                if (inputMask->data.U8[i]) {
+                    continue;
+                }
+                inspect->data[i] = psArrayAdd(inspect->data[i], 1, results->data[i]);
+            }
+            psFree(job);
+        }
+
+        memDump("initial");
+    }
+
+    if (stats) {
+        psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_INITIAL", 0,
+                         "Time to make initial stack", psTimerMark("PPSTACK_INITIAL"));
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Time to make initial stack: %f sec", psTimerClear("PPSTACK_INITIAL"));
+
+    psTimerStart("PPSTACK_REJECT");
+
+    // Pixel rejection
+    psArray *rejected = psArrayAlloc(num);
+    {
+        int numRejected = 0;        // Number of inputs rejected completely
+
+        // Count images rejected out of hand
+        for (int i = 0; i < num; i++) {
+            if (inputMask->data.U8[i]) {
+                numRejected++;
+            }
+        }
+
+        for (int i = 0; i < num; i++) {
+            if (inputMask->data.U8[i]) {
+                continue;
+            }
+
+            psThreadJob *job = psThreadJobAlloc("PPSTACK_INSPECT"); // Job to start
+            psArrayAdd(job->args, 1, inspect);
+            PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
+            if (!psThreadJobAddPending(job)) {
+                psFree(job);
+                psFree(subKernels);
+                psFree(subRegions);
+                psFree(stack);
+                psFree(inputMask);
+                psFree(view);
+                psFree(outRO);
+                psFree(inspect);
+                psFree(rejected);
+                psFree(exptimes);
+                return false;
+            }
+            psFree(job);
+        }
+
+        if (!psThreadPoolWait(false)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to concatenate inspection lists.");
+            psFree(subKernels);
+            psFree(subRegions);
+            psFree(stack);
+            psFree(inputMask);
+            psFree(view);
+            psFree(outRO);
+            psFree(inspect);
+            psFree(rejected);
+            psFree(exptimes);
+            return false;
+        }
+
+        if (psMetadataLookupS32(NULL, config->arguments, "-threads") > 0) {
+            pmStackRejectThreadsInit();
+        }
+
+        // Reject bad pixels
+        for (int i = 0; i < num; i++) {
+            psTimerStart("PPSTACK_REJECT");
+
+#ifdef TESTING
+            {
+                psImage *mask = psPixelsToMask(NULL, inspect->data[i],
+                                               psRegionSet(0, numCols - 1, 0, numRows - 1),
+                                               0xff); // Mask image
+                psString name = NULL;           // Name of image
+                psStringAppend(&name, "inspect_%03d.fits", i);
+                psFits *fits = psFitsOpen(name, "w");
+                psFree(name);
+                psFitsWriteImage(fits, NULL, mask, 0, NULL);
+                psFree(mask);
+                psFitsClose(fits);
+            }
+#endif
+
+            psPixels *reject = pmStackReject(inspect->data[i], numCols, numRows, threshold, poorFrac,
+                                             subRegions->data[i], subKernels->data[i]); // Rejected pixels
+
+#ifdef TESTING
+            {
+                psImage *mask = psPixelsToMask(NULL, reject, psRegionSet(0, numCols - 1, 0, numRows - 1),
+                                               0xff); // Mask image
+                psString name = NULL;           // Name of image
+                psStringAppend(&name, "reject_%03d.fits", i);
+                psFits *fits = psFitsOpen(name, "w");
+                psFree(name);
+                psFitsWriteImage(fits, NULL, mask, 0, NULL);
+                psFree(mask);
+                psFitsClose(fits);
+            }
+#endif
+
+            psFree(inspect->data[i]);
+            inspect->data[i] = NULL;
+
+            if (!reject) {
+                psWarning("Rejection on image %d didn't work --- reject entire image.", i);
+                numRejected++;
+                inputMask->data.U8[i] = PPSTACK_MASK_REJECT;
+            } else {
+                float frac = reject->n / (float)(numCols * numRows); // Pixel fraction
+                psLogMsg("ppStack", PS_LOG_INFO, "%ld pixels rejected from image %d (%.1f%%)",
+                         reject->n, i, frac * 100.0);
+                if (frac > imageRej) {
+                    psWarning("Image %d rejected completely because rejection fraction (%.3f) "
+                              "exceeds limit (%.3f)", i, frac, imageRej);
+                    psFree(reject);
+                    // reject == NULL means reject image completely
+                    reject = NULL;
+                    inputMask->data.U8[i] = PPSTACK_MASK_BAD;
+                    numRejected++;
+                }
+            }
+            rejected->data[i] = reject;
+
+            if (stats) {
+                psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_REJECT", PS_META_DUPLICATE_OK,
+                                 "Time to perform rejection", psTimerMark("PPSTACK_REJECT"));
+                psMetadataAddS32(stats, PS_LIST_TAIL, "REJECT_PIXELS", PS_META_DUPLICATE_OK,
+                                 "Number of pixels rejected", reject ? reject->n : 0);
+            }
+            psLogMsg("ppStack", PS_LOG_INFO, "Time to perform rejection on image %d: %f sec", i,
+                     psTimerClear("PPSTACK_REJECT"));
+        }
+
+        psFree(inspect);
+        psFree(subKernels);
+        psFree(subRegions);
+
+        if (numRejected == num) {
+            psError(PS_ERR_UNKNOWN, true, "All inputs completely rejected; unable to proceed.");
+            psFree(stack);
+            psFree(rejected);
+            psFree(inputMask);
+            psFree(view);
+            psFree(outRO);
+            psFree(exptimes);
+            return false;
+        }
+
+        if (stats) {
+            psMetadataAddS32(stats, PS_LIST_TAIL, "REJECT_IMAGES", 0,
+                             "Number of images rejected completely", numRejected);
+        }
+   }
+
+    psTimerStart("PPSTACK_FINAL");
+
+    memDump("reject");
+
+    // Final combination
+    psTrace("ppStack", 2, "Final stack of convolved images....\n");
+    {
+        stack->lastScan = 0;            // Reset read
+        bool status;                    // Status of read
+        for (int numChunk = 0; true; numChunk++) {
+            ppStackThread *thread = ppStackThreadRead(&status, stack, config, numChunk, 0);
+            if (!status) {
+                // Something went wrong
+                psError(PS_ERR_UNKNOWN, false, "Unable to read chunk %d", numChunk);
+                psFree(stack);
+                psFree(rejected);
+                psFree(inputMask);
+                psFree(view);
+                psFree(outRO);
+                psFree(exptimes);
+                return false;
+            }
+            if (!thread) {
+                // Nothing more to read
+                break;
+            }
+
+            // call: ppStackReadoutFinal(config, outRO, readouts, rejected)
+            psThreadJob *job = psThreadJobAlloc("PPSTACK_FINAL_COMBINE"); // Job to start
+            psArrayAdd(job->args, 1, thread);
+            psArrayAdd(job->args, 1, config);
+            psArrayAdd(job->args, 1, outRO);
+            psArrayAdd(job->args, 1, rejected);
+            if (!psThreadJobAddPending(job)) {
+                psFree(job);
+                psFree(stack);
+                psFree(rejected);
+                psFree(inputMask);
+                psFree(view);
+                psFree(outRO);
+                psFree(exptimes);
+                return false;
+            }
+            psFree(job);
+        }
+
+        if (!psThreadPoolWait(true)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to do final combination.");
+            psFree(stack);
+            psFree(inputMask);
+            psFree(rejected);
+            psFree(view);
+            psFree(outRO);
+            psFree(exptimes);
+            return false;
+        }
+    }
+
+    psThreadPoolFinalize();
+
+    memDump("final");
+
+    if (stats) {
+        psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_FINAL", 0,
+                         "Time to make final stack", psTimerMark("PPSTACK_FINAL"));
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Time to make final stack: %f sec", psTimerClear("PPSTACK_FINAL"));
+
+    psTrace("ppStack", 2, "Cleaning up after combination....\n");
+
+    // Ensure masked regions really look masked
+    {
+        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
+        psMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+        if (!pmReadoutMaskApply(outRO, maskBad)) {
+            psWarning("Unable to apply mask");
+        }
+    }
+
+    // Close up
+    bool wcsDone = false;           // Have we done the WCS?
+    float totExposure = 0.0;            // Total exposure time
+    for (int i = 0; i < num; i++) {
+        if (inputMask->data.U8[i]) {
+            continue;
+        }
+        totExposure += exptimes->data.F32[i];
+
+        ppStackThread *thread = stack->threads->data[0]; // Representative stack
+        pmReadout *inRO = thread->readouts->data[i]; // Template readout
+        if (inRO && !wcsDone) {
+            // Copy astrometry over
+            wcsDone = true;
+            pmHDU *inHDU = pmHDUFromCell(inRO->parent); // Template HDU
+            pmHDU *outHDU = pmHDUFromCell(outRO->parent); // Output HDU
+            pmChip *outChip = outRO->parent->parent; // Output chip
+            pmFPA *outFPA = outChip->parent; // Output FPA
+            if (!outHDU || !inHDU) {
+                psWarning("Unable to find HDU at FPA level to copy astrometry.");
+            } else {
+                if (!pmAstromReadWCS(outFPA, outChip, inHDU->header, 1.0)) {
+                    psErrorClear();
+                    psWarning("Unable to read WCS astrometry from input FPA.");
+                    wcsDone = false;
+                } else {
+                    if (!outHDU->header) {
+                        outHDU->header = psMetadataAlloc();
+                    }
+                    if (!pmAstromWriteWCS(outHDU->header, outFPA, outChip, WCS_TOLERANCE)) {
+                        psErrorClear();
+                        psWarning("Unable to write WCS astrometry to output FPA.");
+                        wcsDone = false;
+                    }
+                }
+            }
+        }
+
+        if (tempDelete) {
+            psString imageResolved = pmConfigConvertFilename(imageNames->data[i], config, false, false);
+            psString maskResolved = pmConfigConvertFilename(maskNames->data[i], config, false, false);
+            psString weightResolved = pmConfigConvertFilename(weightNames->data[i], config, false, false);
+            if (unlink(imageResolved) == -1 || unlink(maskResolved) == -1 ||
+                unlink(weightResolved) == -1) {
+                psWarning("Unable to delete temporary files for image %d", i);
+            }
+            psFree(imageResolved);
+            psFree(maskResolved);
+            psFree(weightResolved);
+        }
+    }
+    psFree(imageNames);
+    psFree(maskNames);
+    psFree(weightNames);
+
+    psFree(exptimes);
+    psFree(inputMask);
+    psFree(stack);
+
+    memDump("cleanup");
+
+    // Generate binned JPEGs
+    {
+        int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
+        int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
+
+        // Target cells
+        pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG1");
+        pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG2");
+        psMaskType maskValue = pmConfigMaskGet("BLANK", config); // Bits to mask
+
+        pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
+        if (!pmReadoutRebin(ro1, outRO, maskValue, bin1, bin1) || !pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to bin output.");
+            psFree(ro1);
+            psFree(ro2);
+            psFree(outRO);
+            return false;
+        }
+        psFree(ro1);
+        psFree(ro2);
+    }
+
+    if (psMetadataLookupBool(&mdok, recipe, "PHOTOMETRY")) {
+        psTrace("ppStack", 1, "Photometering stacked image....\n");
+
+        psTimerStart("PPSTACK_PHOT");
+
+        fileActivation(config, combineFiles, false);
+        fileActivation(config, photFiles, true);
+        pmFPAview *photView = filesIterateDown(config);
+        if (!ppStackPhotometry(config, outRO, photView)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry on output.");
+            psFree(outRO);
+            psFree(photView);
+            return false;
+        }
+        psFree(photView);
+
+        fileActivation(config, combineFiles, true);
+
+        if (stats) {
+            pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT"); // File
+            pmReadout *photRO = pmFPAviewThisReadout(view, photFile->fpa); // Readout with the sources
+            psArray *sources = psMetadataLookupPtr(NULL, photRO->analysis, "PSPHOT.SOURCES"); // Sources
+            psMetadataAddS32(stats, PS_LIST_TAIL, "NUM_SOURCES", 0, "Number of sources detected", sources->n);
+            psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_PHOT", 0,
+                             "Time to do photometry", psTimerMark("PPSTACK_PHOT"));
+        }
+        psLogMsg("ppStack", PS_LOG_INFO, "Time to do photometry: %f sec", psTimerClear("PPSTACK_PHOT"));
+    }
+
+    memDump("phot");
+
+    // Statistics on output
+    if (stats) {
+        psTrace("ppStack", 1, "Gathering statistics on stacked image....\n");
+        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
+        psMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+
+        ppStatsFPA(stats, outRO->parent->parent->parent, view, maskBad, config);
+    }
+
+    memDump("stats");
+
+    // Put version information into the header
+    pmHDU *hdu = pmHDUFromCell(outRO->parent);
+    if (!hdu) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find HDU for output.");
+        psFree(outRO);
+        psFree(view);
+        return false;
+    }
+    if (!hdu->header) {
+        hdu->header = psMetadataAlloc();
+    }
+    ppStackVersionMetadata(hdu->header);
+
+    // Write out the output files
+    filesIterateUp(config);
+
+    psFree(outRO);
+    psFree(view);
+
+    // Write out summary statistics
+    if (stats) {
+        psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_STACK", 0,
+                         "Time to do photometry", psTimerClear("PPSTACK_TOTAL"));
+
+        const char *statsMDC = psMetadataConfigFormat(stats);
+        if (!statsMDC || strlen(statsMDC) == 0) {
+            psError(PS_ERR_IO, false, "Unable to get statistics MDC file.\n");
+        } else {
+            fprintf(statsFile, "%s", statsMDC);
+        }
+        psFree((void *)statsMDC);
+        fclose(statsFile);
+
+        psFree(stats);
+    }
+
+    memDump("finish");
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackMatch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackMatch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackMatch.c	(revision 22322)
@@ -0,0 +1,396 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+
+#define ARRAY_BUFFER 16                 // Number to add to array at a time
+#define MAG_IGNORE 50                   // Ignore magnitudes fainter than this --- they're not real!
+#define FAKE_SIZE 1                     // Size of fake convolution kernel
+#define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | \
+                     PM_SOURCE_MODE_CR_LIMIT) // Mask to apply to input sources
+
+//#define TESTING                         // Enable debugging output
+
+
+
+#ifdef TESTING
+// Read a FITS image
+static bool readImage(psImage **target, // Target for image
+                      const char *name, // Name of FITS file
+                      const pmConfig *config // Configuration
+    )
+{
+    psString resolved = pmConfigConvertFilename(name, config, false, false); // Resolved filename
+    psFits *fits = psFitsOpen(resolved, "r");
+    psFree(resolved);
+    if (!fits) {
+        psError(PS_ERR_IO, false, "Unable to open previously produced image: %s", name);
+        return false;
+    }
+    psImage *image = psFitsReadImage(fits, psRegionSet(0,0,0,0), 0); // Image of interest
+    if (!image) {
+        psError(PS_ERR_IO, false, "Unable to read previously produced image: %s", name);
+        psFitsClose(fits);
+        return false;
+    }
+    psFitsClose(fits);
+
+    psFree(*target);
+    *target = image;
+
+    return true;
+}
+#endif
+
+bool ppStackMatch(pmReadout *readout, psArray **regions, psArray **kernels, float *chi2,
+                  const psArray *sources, const pmPSF *psf, psRandom *rng, const pmConfig *config)
+{
+    assert(readout);
+    assert(regions && !*regions);
+    assert(kernels && !*kernels);
+    assert(config);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    // Look up appropriate values from the ppSub recipe
+    psMetadata *ppsub = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
+    int size = psMetadataLookupS32(NULL, ppsub, "KERNEL.SIZE"); // Kernel half-size
+
+    psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask going in
+    psMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
+    psString maskPoorStr = psMetadataLookupStr(NULL, recipe, "MASK.POOR"); // Name of bits to mask for poor
+    psMaskType maskPoor = pmConfigMaskGet(maskPoorStr, config); // Bits to mask for poor pixels
+    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
+    psMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+
+    bool mdok;                          // Status of MD lookup
+    bool renorm = psMetadataLookupBool(&mdok, recipe, "RENORM"); // Renormalise variances?
+    float penalty = psMetadataLookupF32(NULL, ppsub, "PENALTY"); // Penalty for wideness
+    int threads = psMetadataLookupS32(NULL, config->arguments, "-threads"); // Number of threads
+
+    if (!pmReadoutMaskNonfinite(readout, maskVal)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to mask non-finite pixels in readout.");
+        return false;
+    }
+
+    pmReadout *output = pmReadoutAlloc(NULL); // Output readout, for holding results temporarily
+
+    static int numInput = -1;            // Index of input file
+    numInput++;
+#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");
+                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, weight
+        const char *tempImage = psMetadataLookupStr(NULL, recipe, "TEMP.IMAGE"); // Suffix for image
+        const char *tempMask = psMetadataLookupStr(NULL, recipe, "TEMP.MASK"); // Suffix for mask
+        const char *tempWeight = psMetadataLookupStr(NULL, recipe, "TEMP.WEIGHT"); // Suffix for weight map
+        psString imageName = NULL, maskName = NULL, weightName = NULL; // Names for convolved images
+        psStringAppend(&imageName, "%s.%d.%s", outName, numInput, tempImage);
+        psStringAppend(&maskName, "%s.%d.%s", outName, numInput, tempMask);
+        psStringAppend(&weightName, "%s.%d.%s", outName, numInput, tempWeight);
+
+        if (!readImage(&readout->image, imageName, config) || !readImage(&readout->mask, maskName, config) ||
+            !readImage(&readout->weight, weightName, config)) {
+            psError(PS_ERR_IO, false, "Unable to read previously produced image.");
+            psFree(imageName);
+            psFree(maskName);
+            psFree(weightName);
+            return false;
+        }
+        psFree(imageName);
+        psFree(maskName);
+        psFree(weightName);
+    } else {
+#endif
+
+        // Normal operations here
+        if (psMetadataLookupBool(&mdok, config->arguments, "HAVE.PSF")) {
+            assert(psf);
+            assert(sources);
+
+            int order = psMetadataLookupS32(NULL, ppsub, "SPATIAL.ORDER"); // Spatial polynomial order
+            float regionSize = psMetadataLookupF32(NULL, ppsub, "REGION.SIZE"); // Size of iso-kernel regs
+            float spacing = psMetadataLookupF32(NULL, ppsub, "STAMP.SPACING"); // Typical stamp spacing
+            int footprint = psMetadataLookupS32(NULL, ppsub, "STAMP.FOOTPRINT"); // Stamp half-size
+            float threshold = psMetadataLookupF32(NULL, ppsub, "STAMP.THRESHOLD"); // Threshold for stmps
+            int iter = psMetadataLookupS32(NULL, ppsub, "ITER"); // Rejection iterations
+            float rej = psMetadataLookupF32(NULL, ppsub, "REJ"); // Rejection threshold
+            pmSubtractionKernelsType type = pmSubtractionKernelsTypeFromString(
+                psMetadataLookupStr(NULL, ppsub, "KERNEL.TYPE")); // Kernel type
+            psVector *widths = psMetadataLookupPtr(NULL, ppsub, "ISIS.WIDTHS"); // ISIS Gaussian widths
+            psVector *orders = psMetadataLookupPtr(NULL, ppsub, "ISIS.ORDERS"); // ISIS Polynomial orders
+            int inner = psMetadataLookupS32(NULL, ppsub, "INNER"); // Inner radius
+            int ringsOrder = psMetadataLookupS32(NULL, ppsub, "RINGS.ORDER"); // RINGS polynomial order
+            int binning = psMetadataLookupS32(NULL, ppsub, "SPAM.BINNING"); // Binning for SPAM kernel
+            float badFrac = psMetadataLookupF32(NULL, ppsub, "BADFRAC"); // Maximum bad fraction
+            bool optimum = psMetadataLookupBool(&mdok, ppsub, "OPTIMUM"); // Derive optimum parameters?
+            float optMin = psMetadataLookupF32(&mdok, ppsub, "OPTIMUM.MIN"); // Minimum width for search
+            float optMax = psMetadataLookupF32(&mdok, ppsub, "OPTIMUM.MAX"); // Maximum width for search
+            float optStep = psMetadataLookupF32(&mdok, ppsub, "OPTIMUM.STEP"); // Step for search
+            float optThresh = psMetadataLookupF32(&mdok, ppsub, "OPTIMUM.TOL"); // Tolerance for search
+            int optOrder = psMetadataLookupS32(&mdok, ppsub, "OPTIMUM.ORDER"); // Order for search
+            float poorFrac = psMetadataLookupF32(&mdok, ppsub, "POOR.FRACTION"); // Fraction for "poor"
+
+            // These values are specified specifically for stacking
+            const char *stampsName = psMetadataLookupStr(NULL, config->arguments, "STAMPS");// Stamps filename
+
+            psVector *optWidths = NULL;         // Vector with FWHMs for optimum search
+            if (optimum) {
+                optWidths = psVectorCreate(optWidths, optMin, optMax, optStep, PS_TYPE_F32);
+            }
+
+            float minFlux = INFINITY;       // Minimum flux for fake image
+            {
+                psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for bg
+                if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal, rng)) {
+                    psWarning("Can't measure background for image.");
+                    psErrorClear();
+                } else {
+                    minFlux = 0.1 * bg->robustStdev;
+                }
+                psFree(bg);
+            }
+
+            // Generate a fake image to match to
+            if (!isfinite(minFlux)) {
+                float maxMag = -INFINITY;   // Maximum magnitude of sources
+                for (int i = 0; i < sources->n; i++) {
+                    pmSource *source = sources->data[i]; // Source of interest
+                    if (source->psfMag > maxMag && source->psfMag <= MAG_IGNORE &&
+                        !(source->mode & SOURCE_MASK)) {
+                        maxMag = source->psfMag;
+                    }
+                }
+                minFlux = 0.01 * powf(10.0, -0.4 * maxMag);
+            }
+
+            pmReadout *fake = pmReadoutAlloc(NULL); // Fake readout with target PSF
+
+            if (!pmReadoutFakeFromSources(fake, readout->image->numCols, readout->image->numRows, sources,
+                                          NULL, NULL, psf, minFlux, 0, false)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to generate fake image with target PSF.");
+                psFree(fake);
+                psFree(optWidths);
+                psFree(output);
+                return false;
+            }
+
+#ifdef TESTING
+            {
+                psString name = NULL;
+                psStringAppend(&name, "fake_%03d.fits", numInput);
+                psFits *fits = psFitsOpen(name, "w");
+                psFree(name);
+                psFitsWriteImage(fits, NULL, fake->image, 0, NULL);
+                psFitsClose(fits);
+            }
+#endif
+
+            if (threads > 0) {
+                pmSubtractionThreadsInit(readout, fake);
+            }
+
+            // Do the image matching
+            if (!pmSubtractionMatch(output, NULL, readout, fake, footprint, regionSize, spacing, threshold,
+                                    sources, stampsName, type, size, order, widths, orders, inner, ringsOrder,
+                                    binning, penalty, optimum, optWidths, optOrder, optThresh, iter, rej,
+                                    maskVal, maskBad, maskPoor, poorFrac, badFrac, PM_SUBTRACTION_MODE_1)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to match images.");
+                psFree(fake);
+                psFree(optWidths);
+                psFree(output);
+                return false;
+            }
+            psFree(fake);
+            psFree(optWidths);
+
+            if (threads > 0) {
+                pmSubtractionThreadsFinalize(readout, fake);
+            }
+
+            // Set the variance factor
+            psMetadataItem *vfItem = psMetadataLookup(readout->parent->concepts, "CELL.VARFACTOR");
+            float vf = psMetadataLookupF32(NULL, output->analysis, PM_SUBTRACTION_ANALYSIS_VARFACTOR);
+            if (!isfinite(vf)) {
+                vf = 1.0;
+            }
+            if (isfinite(vfItem->data.F32)) {
+                vfItem->data.F32 *= vf;
+            } else {
+                vfItem->data.F32 = vf;
+            }
+
+            // Replace original images with convolved
+            psFree(readout->image);
+            psFree(readout->mask);
+            psFree(readout->weight);
+            readout->image  = psMemIncrRefCounter(output->image);
+            readout->mask   = psMemIncrRefCounter(output->mask);
+            readout->weight = psMemIncrRefCounter(output->weight);
+
+            readout->analysis = psMetadataCopy(readout->analysis, output->analysis);
+        } 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);
+        }
+
+        // Write convolution kernel
+        {
+            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);
+        }
+#ifdef TESTING
+    }
+#endif
+
+// 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);
+
+    // Correct the variance for the 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);
+
+        float vf = psMetadataLookupF32(NULL, readout->parent->concepts, "CELL.VARFACTOR"); // Variance factor
+        *chi2 /= vf * num;
+    }
+
+    // Renormalise the variances if desired
+    if (renorm) {
+        // Statistics for renormalisation
+        psStatsOptions renormMean = psStatsOptionFromString(psMetadataLookupStr(&mdok, recipe,
+                                                                                "RENORM.MEAN"));
+        psStatsOptions renormStdev = psStatsOptionFromString(psMetadataLookupStr(&mdok, recipe,
+                                                                                 "RENORM.STDEV"));
+        int renormWidth = psMetadataLookupS32(&mdok, recipe, "RENORM.WIDTH"); // Width for renormalisation box
+
+        psLogMsg("ppStack", PS_LOG_INFO, "Renormalising variance map.");
+        if (!pmReadoutWeightRenorm(readout, maskBad, renormMean, renormStdev, renormWidth, rng)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to renormalise variances.");
+            psFree(output);
+            return false;
+        }
+    }
+
+    // Ensure the background value is zero
+    psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for background
+    if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal | maskBad, rng)) {
+        psWarning("Can't measure background for image.");
+        psErrorClear();
+    } else {
+        psLogMsg("ppStack", PS_LOG_INFO, "Correcting convolved image background by %lf (+/- %lf)",
+                 psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN), psStatsGetValue(bg, PS_STAT_ROBUST_STDEV));
+        (void)psBinaryOp(readout->image, readout->image, "+",
+                         psScalarAlloc(- psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN), PS_TYPE_F32));
+    }
+    psFree(bg);
+
+    psFree(output);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackPSF.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackPSF.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackPSF.c	(revision 22322)
@@ -0,0 +1,31 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+
+pmPSF *ppStackPSF(const pmConfig *config, int numCols, int numRows, const psArray *psfs)
+{
+    // Get the recipe values
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    int psfInstances = psMetadataLookupS32(NULL, recipe, "PSF.INSTANCES"); // Number of instances for PSF
+    float psfRadius = psMetadataLookupF32(NULL, recipe, "PSF.RADIUS"); // Radius for PSF
+    const char *psfModel = psMetadataLookupStr(NULL, recipe, "PSF.MODEL"); // Model for PSF
+    int psfOrder = psMetadataLookupS32(NULL, recipe, "PSF.ORDER"); // Spatial order for PSF
+
+    // Solve for the target PSF
+    pmPSF *psf = pmPSFEnvelope(numCols, numRows, psfs, psfInstances, psfRadius, psfModel,
+                               psfOrder, psfOrder);
+    if (!psf) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine output PSF.");
+        return NULL;
+    }
+
+    return psf;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackPhotometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackPhotometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackPhotometry.c	(revision 22322)
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <psphot.h>
+
+#include "ppStack.h"
+
+bool ppStackPhotometry(pmConfig *config, const pmReadout *readout, const pmFPAview *view)
+{
+    pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT");
+    pmFPACopy(photFile->fpa, readout->parent->parent->parent);
+
+    // Need to ensure aperture residual is not calculated
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PSPHOT_RECIPE); // Recipe
+    psMetadataItem *item = psMetadataLookup(recipe, "MEASURE.APTREND"); // Item determining aptrend
+    if (!item) {
+        psWarning("Unable to find MEASURE.APTREND in psphot recipe");
+        psErrorClear();
+    } else {
+        item->data.B = false;
+    }
+
+    // set maskValue and markValue in the psphot recipe
+    psMaskType maskValue = pmConfigMaskGet("BLANK", config); // Bits to mask
+    psMaskType markValue = pmConfigMaskGet("MARK.VALUE", config); // Bits to use for marking
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "Bits to mask", maskValue);
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, "MARK.PSPHOT", PS_META_REPLACE, "Bits to use for mark", markValue);
+
+    if (!psphotReadout(config, view)) {
+        // Clear the error, so that the output files are written.
+        psWarning("Unable to perform photometry on stacked image.");
+        psErrorStackPrint(stderr, "Error stack from photometry:");
+        psErrorClear();
+    }
+
+    pmFPAfileActivate(config->files, false, "PSPHOT.INPUT");
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackReadout.c	(revision 22322)
@@ -0,0 +1,261 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <psphot.h>
+
+#include "ppStack.h"
+
+//#define TESTING                  // Write debugging output?
+
+bool ppStackReadoutInitialThread(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    psArray *args = job->args;          // Arguments
+    ppStackThread *thread = args->data[0]; // Thread
+    pmConfig *config = args->data[1];   // Configuration
+    pmReadout *outRO = args->data[2];   // Output readout
+    psArray *subRegions = args->data[3]; // Regions for PSF-matching
+    psArray *subKernels = args->data[4]; // Kernels for PSF-matching
+    psVector *addVariance = args->data[5]; // Additional variance when rejecting
+
+    psArray *inspect = ppStackReadoutInitial(config, outRO, thread->readouts,
+                                             subRegions, subKernels, addVariance);
+
+    job->results = inspect;
+    thread->busy = false;
+
+    return inspect ? true : false;
+}
+
+bool ppStackReadoutFinalThread(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    psArray *args = job->args;          // Arguments
+    ppStackThread *thread = args->data[0]; // Thread
+    pmConfig *config = args->data[1];   // Configuration
+    pmReadout *outRO = args->data[2];   // Output readout
+    psArray *rejected = args->data[3];  // Rejected pixels
+
+    bool status = ppStackReadoutFinal(config, outRO, thread->readouts, rejected); // Status of operation
+
+    thread->busy = false;
+
+    return status;
+}
+
+
+bool ppStackInspect(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    psArray *args = job->args;  // Input arguments
+    psArray *inspect = args->data[0]; // Array of pixel arrays
+    int index = PS_SCALAR_VALUE(args->data[1], S32); // Index of interest
+
+    psArray *inputs = inspect->data[index]; // Array of interest
+    psPixels *output = NULL;    // Output pixel list
+    for (int i = 0; i < inputs->n; i++) {
+        psPixels *input = inputs->data[i]; // Input pixel list
+        if (!input || input->n == 0) {
+            continue;
+        }
+        output = psPixelsConcatenate(output, input);
+    }
+
+    psFree(inputs);
+    inspect->data[index] = output;
+
+    return true;
+}
+
+
+psArray *ppStackReadoutInitial(const pmConfig *config, pmReadout *outRO, const psArray *readouts,
+                               const psArray *regions, const psArray *kernels, const psVector *addVariance)
+{
+    assert(config);
+    assert(outRO);
+    assert(readouts);
+    assert(regions);
+    assert(kernels);
+    assert(readouts->n == regions->n);
+    assert(regions->n == kernels->n);
+    assert(addVariance && addVariance->n == readouts->n && addVariance->type.type == PS_TYPE_F32);
+    static int sectionNum = 0;          // Section number; for debugging outputs
+
+    // Get the recipe values
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    bool mdok;                          // Status of MD lookup
+    int iter = psMetadataLookupS32(NULL, recipe, "ITER"); // Rejection iterations
+    float combineRej = psMetadataLookupF32(NULL, recipe, "COMBINE.REJ"); // Combination threshold
+    bool useVariance = psMetadataLookupBool(&mdok, recipe, "VARIANCE"); // Use variance for rejection?
+    bool safe = psMetadataLookupBool(&mdok, recipe, "SAFE"); // Be safe when combining small numbers of pixels
+
+    psMetadata *ppsub = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
+    int kernelSize = psMetadataLookupS32(NULL, ppsub, "KERNEL.SIZE"); // Kernel half-size
+
+    psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask going in
+    psMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
+    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
+    psMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+
+    int num = readouts->n;              // Number of inputs
+    psArray *stack = psArrayAlloc(num); // Array for stacking
+
+    for (int i = 0; i < num; i++) {
+        pmReadout *ro = readouts->data[i];
+        if (!ro) {
+            // Bad image
+            continue;
+        }
+        pmFPA *fpa = ro->parent->parent->parent; // Parent FPA
+
+        float weighting = psMetadataLookupF32(&mdok, fpa->analysis, "PPSTACK.WEIGHTING"); // Relative weight
+        if (!mdok || !isfinite(weighting)) {
+            psWarning("No WEIGHTING supplied for image %d --- set to unity.", i);
+            weighting = 1.0;
+        }
+
+        // Ensure there is a mask, or pmStackCombine will complain
+        if (!ro->mask) {
+            ro->mask = psImageAlloc(ro->image->numCols, ro->image->numRows, PS_TYPE_MASK);
+            psImageInit(ro->mask, 0);
+        }
+
+        stack->data[i] = pmStackDataAlloc(ro, weighting, addVariance->data.F32[i]);
+    }
+
+    if (!pmStackCombine(outRO, stack, maskVal | maskBad, maskBad, kernelSize, iter, combineRej, true,
+                        useVariance, safe)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to combine input readouts with rejection.");
+        psFree(stack);
+        return false;
+    }
+
+#ifdef TESTING
+    {
+        psString name = NULL;           // Name of image
+        psStringAppend(&name, "combined_initial_%03d.fits", sectionNum);
+        psFits *fits = psFitsOpen(name, "w");
+        psFree(name);
+        psFitsWriteImage(fits, NULL, outRO->image, 0, NULL);
+        psFitsClose(fits);
+    }
+#endif
+
+    // Save list of pixels to inspect
+    psArray *inspect = psArrayAlloc(num); // List of pixels to inspect
+    for (int i = 0; i < num; i++) {
+        pmStackData *data = stack->data[i]; // Data for this image
+        if (!data) {
+            continue;
+        }
+        pmReadout *readout = data->readout; // Readout of interest
+        if (!readout) {
+            continue;
+        }
+        inspect->data[i] = psMemIncrRefCounter(data->inspect);
+    }
+    psFree(stack);
+
+    sectionNum++;
+
+    return inspect;
+}
+
+
+
+bool ppStackReadoutFinal(const pmConfig *config, pmReadout *outRO, const psArray *readouts,
+                         const psArray *rejected)
+{
+    assert(config);
+    assert(outRO);
+    assert(readouts);
+    assert(rejected);
+    assert(readouts->n == rejected->n);
+
+    static int sectionNum = 0;
+
+    // Get the recipe values
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    bool mdok;                          // Status of MD lookup
+    bool useVariance = psMetadataLookupBool(&mdok, recipe, "VARIANCE"); // Use variance for rejection?
+
+    psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask going in
+    psMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
+    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
+    psMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+
+    int num = readouts->n;              // Number of inputs
+    psArray *stack = psArrayAlloc(num); // Array for stacking
+
+    int numGood = num;                  // Number of good inputs: images that haven't been completely rejected
+    for (int i = 0; i < num; i++) {
+        if (!rejected->data[i]) {
+            // Image completely rejected
+            numGood--;
+            continue;
+        }
+
+        pmReadout *ro = readouts->data[i];
+        assert(ro);
+        pmFPA *fpa = ro->parent->parent->parent; // Parent FPA
+
+        bool mdok;                      // Status of MD lookup
+        float weighting = psMetadataLookupF32(&mdok, fpa->analysis, "PPSTACK.WEIGHTING"); // Relative weight
+        if (!mdok || !isfinite(weighting)) {
+            psWarning("No WEIGHTING supplied for image %d --- set to unity.", i);
+            weighting = 1.0;
+        }
+
+        // Ensure there is a mask, or pmStackCombine will complain
+        if (!ro->mask) {
+            ro->mask = psImageAlloc(ro->image->numCols, ro->image->numRows, PS_TYPE_MASK);
+            psImageInit(ro->mask, 0);
+        }
+
+        pmStackData *data = pmStackDataAlloc(ro, weighting, NAN);
+        data->reject = psMemIncrRefCounter(rejected->data[i]);
+        stack->data[i] = data;
+    }
+
+    if (!pmStackCombine(outRO, stack, maskVal | maskBad, maskBad, 0, 0, NAN,
+                        numGood != num, useVariance, false)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to combine input readouts.");
+        psFree(stack);
+        return false;
+    }
+
+#ifdef TESTING
+    {
+        psString name = NULL;           // Name of image
+        psStringAppend(&name, "combined_final_%03d.fits", sectionNum);
+        psFits *fits = psFitsOpen(name, "w");
+        psFree(name);
+        psFitsWriteImage(fits, NULL, outRO->image, 0, NULL);
+        psFitsClose(fits);
+    }
+#endif
+
+    pmCell *outCell = outRO->parent;    // Output cell
+    pmChip *outChip = outCell->parent;  // Output chip
+
+    outRO->data_exists = true;
+    outCell->data_exists = true;
+    outChip->data_exists = true;
+
+    psFree(stack);
+
+    sectionNum++;
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackSources.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackSources.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackSources.c	(revision 22322)
@@ -0,0 +1,497 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+
+#define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_SATSTAR | PM_SOURCE_MODE_BLEND | \
+                     PM_SOURCE_MODE_BADPSF | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | \
+                     PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply to input sources
+#define SOURCE_FAINTEST 50.0            // Faintest magnitude to consider
+#define SOURCES_MAX_LEAF 2              // Maximum number of points on a tree leaf
+
+//#define TESTING                         // Enable test output
+
+// Stack of sources
+typedef struct {
+    psRegion *bounds;                   // Bounding box for sources
+    psArray *sources;                   // Sources within region
+    psVector *indices;                  // Indices from tree to sources
+    psArray *duplicates;                // Duplicate sources (from overlaps)
+    psTree *tree;                       // kd tree with sources
+} ppStackSourceList;
+
+
+// Extract coordinates from a source
+static inline void coordsFromSource(float *x, float *y, // Coordinates to return
+                                    pmSource *source // Source of interest
+    )
+{
+    if (!source) {
+        *x = NAN;
+        *y = NAN;
+    } else if (source->modelPSF) {
+        *x = source->modelPSF->params->data.F32[PM_PAR_XPOS];
+        *y = source->modelPSF->params->data.F32[PM_PAR_YPOS];
+    } else {
+        *x = source->peak->xf;
+        *y = source->peak->yf;
+    }
+    return;
+}
+
+// Deallocator
+static void stackSourceListFree(ppStackSourceList *list)
+{
+    psFree(list->bounds);
+    psFree(list->sources);
+    psFree(list->indices);
+    psFree(list->duplicates);
+    psFree(list->tree);
+}
+
+// Parse the sources into vectors for each coordinate, and a bounding box
+// Returns number of valid sources
+static long stackSourcesParse(psRegion **bounds, // Region to update with bounding box
+                              psVector **x, psVector **y, // Coordinate vectors to return
+                              psVector **indices, // Source indices to return
+                              const psArray *sources // Input sources
+    )
+{
+    psAssert(bounds, "Must be given a region for bounding box");
+    psAssert(x && y, "Must be given vectors");
+    psAssert(indices, "Must be given indices");
+    psAssert(sources, "Must be given sources");
+
+    long numSources = sources->n;              // Number of sources
+    *x = psVectorRecycle(*x, numSources, PS_TYPE_F32);
+    *y = psVectorRecycle(*y, numSources, PS_TYPE_F32);
+    *indices = psVectorRecycle(*indices, numSources, PS_TYPE_U32);
+    float xMin = INFINITY, xMax = -INFINITY, yMin = INFINITY, yMax = -INFINITY; // Bounds of sources
+    long num = 0;                       // Number of valid sources
+    for (long i = 0; i < numSources; i++) {
+        pmSource *source = sources->data[i]; // Source of interest
+        if (!source || (source->mode & SOURCE_MASK) || !isfinite(source->psfMag) ||
+            source->psfMag > SOURCE_FAINTEST) {
+            continue;
+        }
+
+        float xSrc, ySrc;               // Coordinates of source
+        coordsFromSource(&xSrc, &ySrc, source);
+        if (xSrc < xMin) xMin = xSrc;
+        if (xSrc > xMax) xMax = xSrc;
+        if (ySrc < yMin) yMin = ySrc;
+        if (ySrc > yMax) yMax = ySrc;
+
+        (*x)->data.F32[num] = xSrc;
+        (*y)->data.F32[num] = ySrc;
+        (*indices)->data.U32[num] = i;
+        num++;
+    }
+    (*x)->n = num;
+    (*y)->n = num;
+    (*indices)->n = num;
+
+    if (*bounds) {
+        (*bounds)->x0 = xMin;
+        (*bounds)->x1 = xMax;
+        (*bounds)->y0 = yMin;
+        (*bounds)->y1 = yMax;
+    } else {
+        *bounds = psRegionAlloc(xMin, xMax, yMin, yMax);
+    }
+
+    psTrace("ppStack.sources", 8, "%ld sources: bounds are [%.2f:%.2f,%.2f:%.2f]\n",
+            num, xMin, xMax, yMin, yMax);
+
+    return num;
+}
+
+
+// Allocator for ppStackSourceList
+ppStackSourceList *ppStackSourceListAlloc(psArray *sources // Sources to add
+    )
+{
+    psAssert(sources, "Must be given sources");
+
+    ppStackSourceList *list = psAlloc(sizeof(ppStackSourceList));
+    psMemSetDeallocator(list, (psFreeFunc)stackSourceListFree);
+
+    // Copy the sources onto a new array, so there's no confusion between the inputs and outputs
+    long num = sources->n;              // Number of sources
+    list->sources = psArrayAlloc(num);
+    for (long i = 0; i < num; i++) {
+        list->sources->data[i] = psMemIncrRefCounter(sources->data[i]);
+    }
+
+    list->bounds = NULL;
+    list->indices = NULL;
+    list->duplicates = psArrayAllocEmpty(0);
+
+    psVector *x = NULL, *y = NULL;      // Source coordinates
+    stackSourcesParse(&list->bounds, &x, &y, &list->indices, sources);
+    list->tree = psTreePlant(2, SOURCES_MAX_LEAF, x, y); // kd Tree with sources
+    psFree(x);
+    psFree(y);
+
+    return list;
+}
+
+// Extend a list of sources
+bool ppStackSourceListExtend(ppStackSourceList *list, // List to extend
+                             const psArray *newSources, // Sources to add
+                             const psArray *newDups // Duplicate sources to add
+    )
+{
+    psAssert(list, "Must be given a list");
+    psAssert(newSources, "Must be given sources");
+
+    {
+        psArray *oldSources = list->sources;// Old list of sources
+        long numOld = oldSources->n;        // Number of old sources
+        long numNew = newSources->n;        // Number of new sources
+
+        list->sources = oldSources = psArrayRealloc(oldSources, numOld + numNew);
+        for (long i = 0; i < numNew; i++) {
+            psArrayAdd(oldSources, 1, newSources->data[i]);
+        }
+        psFree(list->tree);
+
+        psVector *x = NULL, *y = NULL;      // Source coordinates
+        stackSourcesParse(&list->bounds, &x, &y, &list->indices, list->sources);
+        list->tree = psTreePlant(2, SOURCES_MAX_LEAF, x, y); // kd Tree with sources
+        psFree(x);
+        psFree(y);
+    }
+
+    if (newDups) {
+        psArray *oldDups = list->duplicates; // Old list of duplicates
+        long numOld = oldDups ? oldDups->n : 0; // Number of old duplicates
+        long numNew = newDups->n;       // Number of new duplicates
+
+        list->duplicates = oldDups = psArrayRealloc(oldDups, numOld + numNew);
+        for (long i = 0; i < numNew; i++) {
+            psArrayAdd(oldDups, 1, newDups->data[i]);
+        }
+    }
+
+    return list;
+}
+
+// Compare an array of sources with a list
+// Return number inside the list
+static long stackSourceListCompare(ppStackSourceList **merge, // Merge target, modified
+                                   psArray *lists, // Array of source lists
+                                   int listIndex, // Index of list to compare with sources
+                                   psArray **sourcesPtr, // Sources to compare with list, modified
+                                   float radius, // Matching radius
+                                   int minOverlap, // Minimum number of sources
+                                   int iter, // Clipping iterations
+                                   float rej // Clipping rejection level
+    )
+{
+    psAssert(merge, "Expected a source list pointer");
+    psAssert(radius > 0, "Silly radius: %f", radius);
+    psAssert(lists, "Expected an array of source lists");
+    psAssert(sourcesPtr && *sourcesPtr, "Expected a source array");
+
+    ppStackSourceList *list = lists->data[listIndex]; // List of interest
+    if (!list) {
+        return 0;
+    }
+
+    psArray *sources = *sourcesPtr;     // Sources to compare with list
+    psVector *x = NULL, *y = NULL;      // Coordinates of sources
+    psVector *indices = NULL;           // Indices for sources
+    psRegion *bounds = NULL;            // Bounding box for sources
+    long numSources = stackSourcesParse(&bounds, &x, &y, &indices, sources); // Number of (good) sources
+
+    if (bounds->x0 > list->bounds->x1 || bounds->x1 < list->bounds->x0 ||
+        bounds->x0 > list->bounds->x1 || bounds->x1 < list->bounds->x0) {
+        // Bounds don't overlap, so the sources don't either
+        psTrace("ppStack.sources", 7, "Bounds don't overlap\n");
+        psFree(x);
+        psFree(y);
+        psFree(indices);
+        psFree(bounds);
+        return 0;
+    }
+    psFree(bounds);
+
+    long numIn = 0;                     // Number of sources in list
+    long numOut = 0;                    // Number of sources outside list
+
+#ifdef TESTING
+    static int fileNum = 0;             // Number of file
+    psString filename = NULL;   // Name of file
+    psStringAppend(&filename, "source_match_%d.txt", fileNum++);
+    FILE *file = fopen(filename, "w"); // File to write
+    psFree(filename);
+    fprintf(file, "# x y mag1 mag2\n");
+#endif
+
+    psVector *magDiff = psVectorAlloc(numSources, PS_TYPE_F32); // Magnitude differences
+    psVector *coords = psVectorAlloc(2, PS_TYPE_F32); // Coordinates of source
+    psArray *outside = psArrayAllocEmpty(numSources); // Sources that don't correlate
+    psArray *inside = psArrayAllocEmpty(numSources); // Sources that do correlate
+    for (long i = 0; i < numSources; i++) {
+        coords->data.F32[0] = x->data.F32[i];
+        coords->data.F32[1] = y->data.F32[i];
+        long match = psTreeNearestWithin(list->tree, coords, radius); // Index of match
+        if (match < 0) {
+            numOut++;
+            psArrayAdd(outside, 1, sources->data[indices->data.U32[i]]);
+            continue;
+        }
+
+        pmSource *listSource = list->sources->data[list->indices->data.U32[match]]; // Source in list
+        pmSource *source = sources->data[indices->data.U32[i]]; // Source of interest
+        float listMag = listSource->psfMag; // Magnitude of source in list
+        float sourceMag = source->psfMag; // Magnitude of source
+        if (!(listSource->mode & SOURCE_MASK) && !(source->mode & SOURCE_MASK) &&
+            isfinite(listMag) && isfinite(sourceMag)) {
+#ifdef TESTING
+            fprintf(file, "%f %f %f %f %.4x %.4x\n", x->data.F32[i], y->data.F32[i],
+                    listSource->psfMag, source->psfMag, listSource->mode, source->mode);
+#endif
+            magDiff->data.F32[numIn] = listSource->psfMag - source->psfMag;
+            psArrayAdd(inside, 1, source);
+            numIn++;
+        }
+    }
+    magDiff->n = numIn;
+
+#ifdef TESTING
+    fclose(file);
+#endif
+
+    psFree(coords);
+    psFree(x);
+    psFree(y);
+    psFree(indices);
+
+    psTrace("ppStack.sources", 7, "%ld sources (vs %d) overlap list %d\n", numIn, minOverlap, listIndex);
+
+    if (numIn > minOverlap) {
+        // There's sufficient overlap --- calculate the magnitude difference
+        psStats *stats = psStatsAlloc(iter == 0 ? (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV) :
+                                      (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV));
+        stats->clipSigma = rej;
+        stats->clipIter = iter;
+        if (!psVectorStats(stats, magDiff, NULL, NULL, 0)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine mean magnitude difference");
+            psFree(magDiff);
+            psFree(stats);
+            return -1;
+        }
+        float meanDiff = iter == 0 ? stats->sampleMean : stats->clippedMean; // Mean magnitude difference
+        float stdev = iter == 0 ? stats->sampleStdev : stats->clippedStdev; // Standard deviation
+        long numStats = iter == 0 ? numIn : stats->clippedNvalues; // Number used for statistics
+        psLogMsg("ppStack.sources", PS_LOG_INFO, "Magnitude difference from %ld overlaps is %f +/- %f\n",
+                 numStats, meanDiff, stdev);
+        psFree(stats);
+
+        // Merge our sources into the list
+
+        if (*merge) {
+            // These sources act as a bridge from one list to another
+            // Correct magnitudes to be on the system defined by the first source list
+            // Correct the list to 'sources' (because 'sources' has been corrected to the merge target)
+            long num = list->sources->n; // Number of sources
+            for (long j = 0; j < num; j++) {
+                pmSource *source = list->sources->data[j];
+                source->psfMag -= meanDiff;
+            }
+            for (long j = 0; j < list->duplicates->n; j++) {
+                pmSource *source = list->duplicates->data[j];
+                source->psfMag -= meanDiff;
+            }
+
+            // Suck sources from this list into the first one we found.
+            psTrace("ppStack.sources", 4, "Bridge: Merging lists.");
+
+            ppStackSourceListExtend(*merge, list->sources, NULL);
+            psFree(list);
+            lists->data[listIndex] = NULL;
+        } else {
+            // Correct 'sources' magnitudes to match the list magnitudes
+            for (long j = 0; j < numOut; j++) {
+                pmSource *source = outside->data[j]; // New source
+                source->psfMag += meanDiff;
+            }
+            for (long j = 0; j < numIn; j++) {
+                pmSource *source = inside->data[j]; // New source
+                source->psfMag += meanDiff;
+            }
+            // Put the sources that didn't correlate into the list
+            psTrace("ppStack.sources", 4, "Overlap: Extending list");
+            ppStackSourceListExtend(list, outside, inside);
+            *merge = list;
+        }
+
+        // We only need to consider sources outside the list, since the lists are (mostly) disjoint
+        psFree(*sourcesPtr);
+        *sourcesPtr = outside;
+    } else {
+        psFree(outside);
+    }
+    psFree(inside);
+    psFree(magDiff);
+
+    return numIn;
+}
+
+
+psArray *ppStackSourceListAdd(psArray *lists, psArray *sources, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(sources, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    if (!lists) {
+        lists = psArrayAllocEmpty(1);
+    }
+    if (lists->n == 0) {
+        ppStackSourceList *list = ppStackSourceListAlloc(sources);
+        psArrayAdd(lists, 1, list);
+        psFree(list);                   // Drop reference
+        return lists;
+    }
+
+    // Read recipe values
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+    float radius = psMetadataLookupF32(NULL, recipe, "SOURCE.RADIUS"); // Exclusion radius
+    int minOverlap = psMetadataLookupS32(NULL, recipe, "SOURCE.MIN"); // Minimum number
+    int iter = psMetadataLookupS32(NULL, recipe, "SOURCE.ITER"); // Clipping iterations
+    float rej = psMetadataLookupF32(NULL, recipe, "SOURCE.REJ"); // Clipping rejection
+
+    // Look for overlaps with existing lists
+    int numLists = lists->n;            // Number of source lists on the stack
+    ppStackSourceList *merge = NULL;    // Merged source list
+
+    psMemIncrRefCounter(sources);       // So we can play with the pointer
+    for (int i = 0; i < numLists; i++) {
+        long numIn = stackSourceListCompare(&merge, lists, i, &sources, radius, minOverlap,
+                                            iter, rej); // Number of overlapping sources
+        if (numIn == -1) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to compare sources.");
+            psFree(sources);
+            psFree(lists);
+            return NULL;
+        }
+    }
+    psFree(sources);
+
+    if (!merge) {
+        // The source list is disjoint, so throw them into a new list
+        psTrace("ppStack.sources", 4, "Disjoint source: Creating new list");
+        ppStackSourceList *list = ppStackSourceListAlloc(sources);
+        psArrayAdd(lists, 1, list);
+        psFree(list);                   // Drop reference
+    }
+
+    return lists;
+}
+
+
+
+psArray *ppStackSourceListCombine(psArray *lists, const pmConfig *config)
+{
+    PS_ASSERT_ARRAY_NON_NULL(lists, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+    float radius = psMetadataLookupF32(NULL, recipe, "SOURCE.RADIUS"); // Exclusion radius
+
+    int numLists = 0;                   // Number of lists
+    ppStackSourceList *firstList = NULL; // Last list
+    for (int i = 0; i < lists->n; i++) {
+        ppStackSourceList *list = lists->data[i]; // List of interest
+        if (list) {
+            numLists++;
+            if (!firstList) {
+                firstList = list;
+            }
+        }
+    }
+
+    if (!firstList || numLists == 0) {
+        psError(PS_ERR_UNKNOWN, true, "No lists found.");
+        return NULL;
+    }
+
+#if 0
+    if (numLists == 1) {
+        // Trivial case
+        return psMemIncrRefCounter(firstList->sources);
+    }
+#endif
+
+    for (int i = lists->n - 1; lists->data[i] != firstList; i--) {
+        // There may be some small overlap (we know it's less than SOURCE.MIN), so attempt to correct
+        // magnitudes of these first
+        ppStackSourceList *lastList = lists->data[i]; // Last source list in the array
+        if (!lastList) {
+            continue;
+        }
+        psArray *sources = psMemIncrRefCounter(lastList->sources); // Sources of interest
+        ppStackSourceList *merge = NULL; // Merge target
+        for (int j = 0; j < i; j++) {
+            ppStackSourceList *list = lists->data[i]; // List to compare
+            if (!list) {
+                continue;
+            }
+            // Note: setting iterations = 0, so no clipping (figure not enough values to do clipping)
+            long numIn = stackSourceListCompare(&merge, lists, j, &sources, radius, 0,
+                                                1, NAN); // Number of overlapping sources
+            if (numIn < 0) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to compare sources.");
+                psFree(sources);
+                return NULL;
+            }
+            if (numIn == 0) {
+                // It doesn't match anything at all.  Merge it in to the first list.
+                psTrace("ppStack.sources", 4, "Disjoint list: Merging into first");
+                ppStackSourceListExtend(firstList, list->sources, NULL);
+            }
+        }
+        psFree(sources);
+    }
+
+    return psMemIncrRefCounter(firstList->sources);
+}
+
+
+
+void ppStackSourcesPrint(const psArray *sources)
+{
+    static int num = 0;                 // Number of source array
+    psString filename = NULL;           // Name of file
+    psStringAppend(&filename, "sources_%d.reg", num++);
+
+    const char *goodColour = "green";   // Colour for good sources
+    const char *badColour = "red";      // Colour for bad sources
+    float radius = 5;                   // Radius for circle
+
+    FILE *file = fopen(filename, "w");  // File to which to write
+    psFree(filename);
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i]; // Source of interest
+        if (!source) {
+            continue;
+        }
+
+        float x, y;                     // Source coordinates
+        coordsFromSource(&x, &y, source);
+
+        bool bad = (source->mode & SOURCE_MASK) || !isfinite(source->psfMag) ||
+            source->psfMag > SOURCE_FAINTEST; // Is this a bad source?
+
+        fprintf(file, "image; circle(%f,%f,%f) # color = %s\n", x + 1.0, y + 1.0, radius,
+                bad ? badColour : goodColour);
+    }
+    fclose(file);
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackThread.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackThread.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackThread.c	(revision 22322)
@@ -0,0 +1,258 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+
+#define THREAD_WAIT 10000               // Microseconds to wait if thread is not available
+
+
+static void stackThreadFree(ppStackThread *thread)
+{
+    psFree(thread->readouts);
+    return;
+}
+
+ppStackThread *ppStackThreadAlloc(psArray *readouts)
+{
+    ppStackThread *thread = psAlloc(sizeof(ppStackThread));
+    psMemSetDeallocator(thread, (psFreeFunc)stackThreadFree);
+
+    thread->readouts = psMemIncrRefCounter(readouts);
+    thread->read = false;
+    thread->busy = false;
+    thread->firstScan = 0;
+    thread->lastScan = 0;
+    return thread;
+}
+
+static void stackThreadDataFree(ppStackThreadData *stack)
+{
+    psFree(stack->threads);
+    for (int i = 0; i < stack->imageFits->n; i++) {
+        psFitsClose(stack->imageFits->data[i]);
+        psFitsClose(stack->maskFits->data[i]);
+        psFitsClose(stack->weightFits->data[i]);
+        stack->imageFits->data[i] = stack->maskFits->data[i] = stack->weightFits->data[i] = NULL;
+    }
+    psFree(stack->imageFits);
+    psFree(stack->maskFits);
+    psFree(stack->weightFits);
+    return;
+}
+
+ppStackThreadData *ppStackThreadDataSetup(const psArray *cells, const psArray *imageNames,
+                                          const psArray *maskNames, const psArray *weightNames,
+                                          const pmConfig *config)
+{
+    PS_ASSERT_ARRAY_NON_NULL(cells, NULL);
+    PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, imageNames, NULL);
+    PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, maskNames, NULL);
+    PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, weightNames, NULL);
+
+    ppStackThreadData *stack = psAlloc(sizeof(ppStackThreadData)); // Thread data, to return
+    psMemSetDeallocator(stack, (psFreeFunc)stackThreadDataFree);
+
+    stack->lastScan = 0;
+
+    int numInputs = cells->n;           // Number of inputs
+    stack->imageFits  = psArrayAlloc(numInputs);
+    stack->maskFits   = psArrayAlloc(numInputs);
+    stack->weightFits = psArrayAlloc(numInputs);
+    for (int i = 0; i < numInputs; i++) {
+        if (!cells->data[i]) {
+            // Bad image
+            continue;
+        }
+        // Resolved names
+        psString imageResolved = pmConfigConvertFilename(imageNames->data[i], config, false, false);
+        psString maskResolved = pmConfigConvertFilename(maskNames->data[i], config, false, false);
+        psString weightResolved = pmConfigConvertFilename(weightNames->data[i], config, false, false);
+        stack->imageFits->data[i] = psFitsOpen(imageResolved, "r");
+        stack->maskFits->data[i] = psFitsOpen(maskResolved, "r");
+        stack->weightFits->data[i] = psFitsOpen(weightResolved, "r");
+        psFree(imageResolved);
+        psFree(maskResolved);
+        psFree(weightResolved);
+        if (!stack->imageFits->data[i] || !stack->maskFits->data[i] || !stack->weightFits->data[i]) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to open convolved files %s, %s, %s",
+                    (char*)imageNames->data[i], (char*)maskNames->data[i], (char*)weightNames->data[i]);
+            return false;
+        }
+    }
+
+    int numThreads = psMetadataLookupS32(NULL, config->arguments, "-threads"); // Number of threads
+
+    // Generate readouts for each input file in each file group
+    psArray *threads = psArrayAlloc(numThreads + 1); // A stack for each thread
+    for (int i = 0; i < threads->n; i++) {
+        psArray *readouts = psArrayAlloc(numInputs); // Input readouts
+        for (int j = 0; j < numInputs; j++) {
+            pmCell *cell = cells->data[j]; // Cell with data
+            if (!cell) {
+                continue;
+            }
+            readouts->data[j] = pmReadoutAlloc(cell);
+        }
+        threads->data[i] = ppStackThreadAlloc(readouts);
+        psFree(readouts);               // Drop reference
+    }
+    stack->threads = threads;
+
+    return stack;
+}
+
+
+ppStackThread *ppStackThreadRead(bool *status, ppStackThreadData *stack, pmConfig *config, int numChunk,
+                                 int overlap)
+{
+    assert(status);
+    PS_ASSERT_PTR_NON_NULL(stack, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    *status = true;
+
+    psArray *threads = stack->threads;  // Threads for reading
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // Recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSTACK_RECIPE);
+        return NULL;
+    }
+    int rows = psMetadataLookupS32(NULL, recipe, "ROWS"); // Number of rows to read per chunk
+    if (rows <= 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "ROWS is not set in the recipe.");
+        return NULL;
+    }
+
+    // Select an available group
+    while (true) {
+        // check for any groups which can read data
+        for (int j = 0; j < threads->n; j++) {
+            ppStackThread *thread = threads->data[j];
+            if (thread->read) {
+                continue;
+            }
+
+            thread->firstScan = stack->lastScan; // Overlap is taken care of in pmReadoutReadChunk
+            thread->lastScan = stack->lastScan + rows;
+            stack->lastScan = thread->lastScan;
+
+            psArray *readouts = thread->readouts;
+
+            psTimerStart ("ppStackReadChunk");
+
+            psTrace("ppStack", 2, "Reading data for chunk %d into group %d....\n", numChunk, j);
+            for (int i = 0; i < readouts->n; i++) {
+                pmReadout *ro = readouts->data[i]; // Input readout
+                if (!ro) {
+                    continue;
+                }
+
+                // override the recorded last scan
+                ro->thisImageScan  = thread->firstScan;
+                ro->thisWeightScan = thread->firstScan;
+                ro->thisMaskScan   = thread->firstScan;
+                ro->lastImageScan  = thread->lastScan;
+                ro->lastMaskScan   = thread->lastScan;
+                ro->lastWeightScan = thread->lastScan;
+                ro->forceScan      = true;
+
+                psFits *imageFits  = stack->imageFits->data[i]; // FITS file for image
+                psFits *maskFits   = stack->maskFits->data[i]; // FITS file for mask
+                psFits *weightFits = stack->weightFits->data[i]; // FITS file for weight
+
+
+                bool keepReading = false;
+                if (pmReadoutMore(ro, imageFits, 0, rows, config)) {
+                    keepReading = true;
+                    if (!pmReadoutReadChunk(ro, imageFits, 0, rows, overlap, config)) {
+                        psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPSTACK.INPUT %d",
+                                numChunk, i);
+                        *status = false;
+                        return NULL;
+                    }
+                }
+
+                if (pmReadoutMoreMask(ro, maskFits, 0, rows, config)) {
+                    keepReading = true;
+                    if (!pmReadoutReadChunkMask(ro, maskFits, 0, rows, overlap, config)) {
+                        psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPSTACK.INPUT.MASK %d",
+                                numChunk, i);
+                        *status = false;
+                        return NULL;
+                    }
+                }
+
+                if (pmReadoutMoreWeight(ro, weightFits, 0, rows, config)) {
+                    keepReading = true;
+                    if (!pmReadoutReadChunkWeight(ro, weightFits, 0, rows, overlap, config)) {
+                        psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPSTACK.INPUT.WEIGHT %d",
+                                numChunk, i);
+                        *status = false;
+                        return NULL;
+                    }
+                }
+                if (!keepReading) {
+                    return NULL;
+                }
+            }
+
+            thread->read = thread->busy = true;
+            return thread;
+        }
+
+        // Check for threads that are ready to read
+        bool wait = true;
+        for (int j = 0; j < threads->n; j++) {
+            ppStackThread *thread = threads->data[j];
+            if (thread->busy) {
+                continue;
+            }
+            thread->read = false;
+            wait = false;
+        }
+        if (wait) {
+            // No threads currently available
+            usleep(THREAD_WAIT);
+        }
+    }
+    return NULL;
+}
+
+
+void ppStackThreadInit(void)
+{
+    static bool threaded = false;       // Are we running threaded?
+    if (threaded) {
+        psAbort("Already running threaded.");
+    }
+    threaded = true;
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PPSTACK_INITIAL_COMBINE", 6);
+        task->function = &ppStackReadoutInitialThread;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PPSTACK_INSPECT", 2);
+        task->function = &ppStackInspect;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PPSTACK_FINAL_COMBINE", 4);
+        task->function = &ppStackReadoutFinalThread;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStack/src/ppStackVersion.c	(revision 22322)
@@ -0,0 +1,60 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppStack.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString ppStackVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString ppStackVersionLong(void)
+{
+    psString version = ppStackVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
+
+void ppStackVersionMetadata(psMetadata *metadata)
+{
+    PS_ASSERT_METADATA_NON_NULL(metadata,);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppStats = ppStatsVersionLong(); // ppStats version
+    psString ppStack = ppStackVersionLong(); // ppStack version
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString head = NULL;               // Head string
+    psStringAppend(&head, "ppStack processing at %s. Component information:", timeString);
+    psFree(timeString);
+
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, "", head);
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, "", pslib);
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, "", psmodules);
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, "", ppStats);
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, "", ppStack);
+
+    psFree(head);
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(ppStats);
+    psFree(ppStack);
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/.cvsignore	(revision 22322)
@@ -0,0 +1,16 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+missing
+config.guess
+config.sub
+libtool
+ltmain.sh
+ppStats.pc
Index: /tags/sj_tags/sj_root_20080929/ppStats/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/Makefile.am	(revision 22322)
@@ -0,0 +1,9 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= ppStats.pc
+
+EXTRA_DIST = \
+	ppStats.pc.in
Index: /tags/sj_tags/sj_root_20080929/ppStats/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppStats
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ppStats/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/configure.ac	(revision 22322)
@@ -0,0 +1,33 @@
+AC_PREREQ(2.61)
+
+AC_INIT([ppStats], [1.1.1], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE 
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror -std=c99"
+
+AC_SUBST([PPSTATS_CFLAGS])
+AC_SUBST([PPSTATS_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  ppStats.pc
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ppStats/ppStats.pc.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/ppStats.pc.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/ppStats.pc.in	(revision 22322)
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libppStats
+Description: Pan-STARRS Image Statistics
+Version: @VERSION@
+Requires: pslib psmodules
+Libs: -L${libdir} -lppStats
+Cflags: -I${includedir}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+*.lo
+*.la
+ppStats
+ppStatsFromMetadata
+config.h
+config.h.in
+stamp-h1
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/Makefile.am	(revision 22322)
@@ -0,0 +1,51 @@
+lib_LTLIBRARIES = libppStats.la
+libppStats_la_CFLAGS = $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+libppStats_la_LDFLAGS = $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+
+bin_PROGRAMS = ppStats ppStatsFromMetadata
+
+ppStats_CFLAGS 	= $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+ppStats_LDFLAGS = $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+ppStats_LDADD 	= libppStats.la
+
+ppStatsFromMetadata_CFLAGS  = $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+ppStatsFromMetadata_LDFLAGS = $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+
+ppStats_SOURCES =		\
+	ppStats.c	        
+
+ppStatsFromMetadata_SOURCES =		\
+	ppStatsFromMetadata.c	        \
+	ppStatsFromMetadataEntries.c	\
+	ppStatsFromMetadataParse.c	\
+	ppStatsFromMetadataStats.c	\
+	ppStatsFromMetadataPrint.c	
+
+libppStats_la_SOURCES = 		\
+	ppStatsFPA.c			\
+	ppStatsData.c			\
+	ppStatsFringe.c			\
+	ppStatsLoop.c			\
+	ppStatsChip.c			\
+	ppStatsCell.c			\
+	ppStatsReadout.c		\
+	ppStatsUtils.c  		\
+	ppStatsPixels.c			\
+	ppStatsMetadata.c		\
+	ppStatsSetupFromRecipe.c	\
+	ppStatsSetupFromArgs.c          \
+	ppStatsVersion.c
+
+include_HEADERS = 			\
+	ppStats.h
+
+noinst_HEADERS = 		\
+	ppStatsInternal.h
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStats.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "ppStatsInternal.h"
+
+int main(int argc, char **argv) {
+
+    psExit status = PS_EXIT_SUCCESS;
+
+    psLibInit(NULL);
+    psTimerStart("PPSTATS");
+
+    // Parse the configuration and arguments
+    pmConfig *config = pmConfigRead(&argc, argv, PPSTATS_RECIPE);
+    if (!config) {
+        psErrorStackPrint(stderr, "Unable to read configuration.\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // Get the options, open the files
+    ppStatsData *data = ppStatsSetupFromArgs(&argc, argv, config);
+    if (!data) {
+        psErrorStackPrint(stderr, "Unable to parse command-line arguments.\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // Output filename is optional
+    const char *outName = NULL;         // Output file name
+    FILE *outFile = stdout;             // Output file
+    if (argc == 2) {
+        outName = argv[1];
+        psString resolved = pmConfigConvertFilename(outName, config, true, true); // Resolved filename
+
+        if (resolved && strlen(resolved) > 0) {
+            outFile = fopen(resolved, "w");
+            if (!outFile) {
+                psLogMsg("ppStats", PS_LOG_ERROR, "Unable to open output file %s\n", resolved);
+                psFree(resolved);
+                // XXX this could be a system or config error, but not a data error
+                status = PS_EXIT_CONFIG_ERROR;
+                goto die;
+            }
+        } else {
+            psErrorStackPrint(stderr, "Unable to open output file %s.\n", resolved);
+            exit(PS_EXIT_CONFIG_ERROR);
+        }
+        psFree(resolved);
+    }
+
+    // Go through the FPA and do the hard work
+    psMetadata *results = ppStatsLoop(&status, data, config);
+    if (status != PS_EXIT_SUCCESS) {
+        psErrorStackPrint(stderr, "Error in stats loop.\n");
+        exit (status);
+    }
+
+    // report on the file disposition
+    if (data->fileLevel) {
+        pmFPALevel level = pmFPAPHULevel(config->format);
+
+        const char *levelName = pmFPALevelToName(level); // Level for file
+        psMetadataAddStr(results, PS_LIST_HEAD, "FILE.LEVEL", 0, "File level", levelName);
+
+        char *classID = NULL;
+        switch (level) {
+          case PM_FPA_LEVEL_FPA:
+            assert (data->fpa != NULL);
+            assert (data->fileView->chip == -1);
+            classID = pmFPANameFromRule ("{FPA.NAME}", data->fpa, data->fileView);
+            break;
+          case PM_FPA_LEVEL_CHIP:
+            assert (data->fpa != NULL);
+            assert (data->fileView->chip != -1);
+            assert (data->fileView->cell == -1);
+            classID = pmFPANameFromRule ("{CHIP.NAME}", data->fpa, data->fileView);
+            break;
+          case PM_FPA_LEVEL_CELL:
+            assert (data->fpa != NULL);
+            assert (data->fileView->chip != -1);
+            assert (data->fileView->cell != -1);
+            classID = pmFPANameFromRule ("{CELL.NAME}", data->fpa, data->fileView);
+            break;
+          default:
+            psErrorStackPrint(stderr, "Error in file level.\n");
+            exit (PS_EXIT_CONFIG_ERROR);
+        }
+        psMetadataAddStr(results, PS_LIST_HEAD, "CLASS.ID", 0, "name for element at file level", classID);
+        psFree (classID);
+    }
+
+    // did we actually request any data?
+    if (psListLength(results->list) == 0) {
+        psErrorStackPrint(stderr, "No output.\n");
+        exit (status);
+    }
+
+    // Format and print the output
+    psString output = psMetadataConfigFormat(results);
+    if (!output) {
+        psErrorStackPrint(stderr, "Unable to generate configuration file with result.\n");
+        psFree(results);
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+    fprintf(outFile, "%s", output);
+    psFree(output);
+
+    // Clean up
+    psFree(results);
+    if (outName) {
+        fclose(outFile);
+    }
+
+    // Common code for the death.
+die:
+    if (status) {
+        psErrorStackPrint (stderr, "failure in %s", __func__);
+    }
+    psFree(data);
+    psFree(config);
+    pmConceptsDone();
+    pmConfigDone();
+    psLibFinalize();
+
+    return status;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStats.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStats.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStats.config	(revision 22322)
@@ -0,0 +1,25 @@
+### Example ppStats recipe
+
+MULTI	HEADERS		# May specify multiple HEADERS
+HEADER		STR	HEADER1,HEADER2			# Read these headers
+HEADER		STR	HEADER3				# Read these headers too
+
+MULTI	STATS		# May specify multiple STATS
+STAT		STR	SAMPLE_MEAN,SAMPLE_STDEV	# Calculate these statistics
+STAT		STR	ROBUST_MEDIAN			# Calculate these statistics too
+
+MULTI   ANALYSIS        # metadata blocks to search in chip/cell/readout->analysis
+ANALYSIS        STR     PSPHOT.HEADER
+ANALYSIS        STR     PSASTRO.HEADER
+
+SAMPLE		F32	0.1				# Fraction of cell to sample
+
+MULTI	CHIPS		# May specify multiple CHIPS
+CHIPS		STR	chip00, chip01			# Look at these chips
+CHIPS		STR	chip22				# Look at this chip too
+
+MULTI	CELLS		# May specify multiple CELLS
+CELLS		STR	LeftCell,RightCell		# Look at these cells
+CELLS		STR	UpsideDownCell			# Look at this cell too
+
+MASKVAL		U8	0xff
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStats.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStats.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStats.h	(revision 22322)
@@ -0,0 +1,150 @@
+
+#ifndef PP_STATS_H
+#define PP_STATS_H
+
+#define PPSTATS_RECIPE "PPSTATS"
+#define PPSTATS_MD_RECIPE "PPSTATS_METADATA"
+
+typedef struct {
+    // Inputs
+    psFits *fits;                       // Input file handle
+    pmFPA *fpa;                         // FPA to analyse
+    pmFPAview *view;                    // View to analyse
+    // Stuff to output
+    psStats *stats;                     // pixel Statistics to calculate
+    bool doStats;                       // Do pixel statistics?
+    bool fileLevel;                     // Output file level?
+    pmFPAview *fileView;                // View to analyse
+
+    psList *headers;                    // Headers to read
+    psList *concepts;                   // Concepts to read
+    psList *analysis;                   // Analysis entries to read
+    psList *summary;                    // Summary statistics to calculate
+    // Options for input data
+    bool doFirstReadout3D;		// for 3D data, use the first readout?
+    float sample;                       // Fraction of cell to sample for statistics
+    psMaskType maskVal;                 // Mask value for images
+    psList *chips;                      // Chips to look at
+    psList *cells;                      // Cells to look at
+} ppStatsData;
+
+typedef struct {
+    char *keyword;
+    psDataType type;
+    char *statistic;
+    char *flag;
+    psMetadataItem *value;
+    psVector *vector;
+} ppStatsEntry;
+
+// Allocator
+ppStatsData *ppStatsDataAlloc(void);
+
+/// Perform the ppStats steps on the given FPA (optionally for specified view)
+psMetadata *ppStatsFPA(psMetadata *out,
+                       pmFPA *fpa,         // FPA for which to get statistics
+                       pmFPAview *view,    // View for analysis
+                       psMaskType maskVal, // Value to mask
+                       pmConfig *config    // Configuration
+    );
+
+psExit ppStatsChip(psMetadata *fpaResults, // Metadata holding the fpa results
+                   pmChip *chip,     // Chip for which to get statistics
+                   psFits *fits,     // FITS file handle
+                   pmFPAview *view,  // View for analysis
+                   ppStatsData *data,// The data
+                   pmConfig *config // Configuration
+    );
+
+psExit ppStatsCell(psMetadata *chipResults, // Metadata holding the chip results
+                   pmCell *cell,     // Cell for which to get statistics
+                   psFits *fits,     // FITS file handle
+                   pmFPAview *view,  // View for analysis
+                   ppStatsData *data,// The data
+                   pmConfig *config  // Configuration
+    );
+
+/// Supplement the statistics with the fringe solution
+bool ppStatsFringe(psMetadata *stats,     ///< Statistics metadata to supplement
+                   const pmChip *chip,    ///< The chip containing the solution
+                   const char *root,      ///< Name of output entry
+                   const char *fringeName ///< Name of the solution in the chip->analysis
+    );
+
+
+// measure only the pixel-related statistics (stats, summary)
+psMetadata *ppStatsPixels(psMetadata *out,
+                          pmFPA *fpa,         // FPA for which to get statistics
+                          pmFPAview *view,    // View for analysis
+                          psMaskType maskVal, // Value to mask
+                          pmConfig *config    // Configuration
+    );
+
+// measure only the non-pixel-related statistics (headers, concepts)
+psMetadata *ppStatsMetadata(psMetadata *out,
+                            pmFPA *fpa,         // FPA for which to get statistics
+                            pmFPAview *view,    // View for analysis
+                            psMaskType maskVal, // Value to mask
+                            pmConfig *config    // Configuration
+    );
+
+/// Loop over the input image and do all the hard work
+psMetadata *ppStatsLoop(psExit *result,
+                        ppStatsData *data, // The data
+                        pmConfig *config // Configuration
+    );
+
+/// Set up the options and input/output files
+ppStatsData *ppStatsSetupFromArgs(int *argc, char *argv[], // Command-line arguments
+                                  pmConfig *config // Configuration
+    );
+
+bool ppStatsSetupFromRecipe(ppStatsData *data, // Data for running ppStats
+                            pmConfig *config // Configuration
+    );
+
+
+/// Return short version information
+psString ppStatsVersion(void);
+
+/// Return long version information
+psString ppStatsVersionLong(void);
+
+void p_ppStatsGetMetadata(psMetadata *target, // Target for metadata
+                          psMetadata *source, // Source for metadata
+                          psList *list    // List containing keywords
+    );
+
+void p_ppStatsGetAnalysis(psMetadata *target, // Output Target for metadata
+                          psList *headers,    // List containing desired keywords
+                          psMetadata *source, // Input Source for metadata
+                          psList *list        // List containing analysis blocks
+    );
+
+bool p_ppStatsDoThis(psList *toDoList,    // List of things to do
+                     const char *this     // The name of "this"
+    );
+
+void p_ppStatsAddToHierarchy(psMetadata *source, // Source to add
+                             psMetadata *target, // Target to which to add
+                             const char *name, // Name of source
+                             const char *comment // Comment for source
+    );
+
+psExit ppStatsReadout(psMetadata *cellResults, // Metadata holding the chip results
+                      pmReadout *readout,       // Cell for which to get statistics
+                      int nReadout,     // readout number
+                      ppStatsData *data,        // The data
+                      const pmConfig *config // Configuration
+    );
+
+// used by ppStatsFromMetadata:
+psDataType psDataTypeFromString (char *typename);
+ppStatsEntry *ppStatsEntryAlloc ();
+
+psArray *ppStatsFromMetadataEntries (psMetadata *recipe);
+bool ppStatsFromMetadataParse (psMetadata *input, psArray *entries);
+bool ppStatsFromMetadataStats (psArray *entries);
+bool ppStatsFromMetadataPrint (psArray *entries, char *filename);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsCell.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsCell.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsCell.c	(revision 22322)
@@ -0,0 +1,119 @@
+# include "ppStatsInternal.h"
+
+psExit ppStatsCell(psMetadata *chipResults, // Metadata holding the chip results
+                   pmCell *cell,        // Cell for which to get statistics
+                   psFits *fits,        // FITS file handle
+                   pmFPAview *view,  // View for analysis
+                   ppStatsData *data,   // The data
+                   pmConfig *config // Configuration
+    )
+{
+    assert(chipResults);
+    assert(cell);
+    assert(data);
+    assert(config);
+
+    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
+
+    // Check to see if this is a cell of interest
+    if (!p_ppStatsDoThis(data->cells, cellName)) {
+        return PS_EXIT_SUCCESS;
+    }
+
+#if 0
+    // select the header unit for this cell
+    // XXX why is this needed for Cell but not Chip?
+    pmHDU *hdu = pmHDUFromCell(cell); // HDU for cell
+    if (!hdu || hdu->blankPHU) {
+        if (fits) {
+            // No HDU means there's no data in this cell
+            return PS_EXIT_SUCCESS;
+        } else {
+            psError(PS_ERR_UNKNOWN, false, "Can't find HDU for cell\n");
+            return PS_EXIT_CONFIG_ERROR;
+        }
+    }
+#endif
+
+    // Extract Header and Concept values from the Cell and Readout->analysis level
+
+    // find or generate a metadata to hold the results
+    bool mdok;                          // Status of MD lookup
+    psMetadata *cellResults = psMemIncrRefCounter(psMetadataLookupMetadata(&mdok, chipResults, cellName));
+    if (!mdok || !cellResults) {
+        cellResults = psMetadataAlloc();
+    }
+
+    // Extract Header values
+    if (psListLength(data->headers)) {
+        // extract from existing headers
+        if (cell->hdu) {
+            if (fits && !pmCellReadHeader(cell, fits, config)) {
+                psError (PS_ERR_IO, false, "trouble reading cell header\n");
+                psFree(cellResults);
+                return PS_EXIT_DATA_ERROR;
+            }
+            pmHDU *hdu = cell->hdu;     // HDU for headers
+            p_ppStatsGetMetadata(cellResults, hdu->header, data->headers);
+        }
+        // extract from data->analysis output MD entries
+        if (psListLength(data->analysis)) {
+            p_ppStatsGetAnalysis (cellResults, data->headers, cell->analysis, data->analysis);
+        }
+    }
+
+    // Extract Concept values
+    if (psListLength(data->concepts) > 0) {
+        if (fits && cell->hdu && !pmCellReadHeader(cell, fits, config)) {
+            psError (PS_ERR_IO, false, "trouble reading cell header\n");
+            psFree(cellResults);
+            return PS_EXIT_DATA_ERROR;
+        }
+        pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_ALL, false, config);
+        p_ppStatsGetMetadata(cellResults, cell->concepts, data->concepts);
+    }
+
+    // If we want to measure pixel statistics, we must read (or have) the pixel data
+    if (data->doStats || psListLength(data->summary)) {
+        // Read the image pixel data
+        if (fits && !pmCellRead(cell, fits, config)) {
+            psError (PS_ERR_IO, false, "trouble reading cell data\n");
+            psFree(cellResults);
+            return PS_EXIT_DATA_ERROR;
+        }
+    }
+
+    // check if we can legitimately iterate to the readout level
+    psArray *readouts = cell->readouts; // Array of component readouts
+    if (view->readout >= readouts->n) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Desired readout view (%d) doesn't match "
+                "number of readouts (%ld)\n", view->readout, readouts->n);
+        return PS_EXIT_CONFIG_ERROR;
+    }
+    if ((view->readout == -1) && (readouts->n > 1)) {
+        psWarning("Multiple readouts (%ld) present in cell %s\n", readouts->n, cellName);
+    }
+
+    // Iterate over readouts
+    for (int i = 0; i < readouts->n; i++) {
+        if ((view->readout >= 0) && (i != view->readout)) continue;
+        pmReadout *readout = readouts->data[i];  // Cell of interest
+        psExit result = ppStatsReadout(cellResults, readout, i, data, config);
+        if (result != PS_EXIT_SUCCESS) {
+            psError(PS_ERR_UNKNOWN, false, "trouble with readout stats for %d\n", i);
+            if (fits) {
+                pmCellFreeData(cell);
+            }
+            psFree(cellResults);
+            return result;
+        }
+    }
+
+    // Add the cell results to the chip
+    p_ppStatsAddToHierarchy(cellResults, chipResults, cellName, "Results for cell");
+    psFree (cellResults);
+    if (fits) {
+        pmCellFreeData(cell);
+    }
+    return PS_EXIT_SUCCESS;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsChip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsChip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsChip.c	(revision 22322)
@@ -0,0 +1,103 @@
+# include "ppStatsInternal.h"
+
+psExit ppStatsChip(psMetadata *fpaResults, // Metadata holding the fpa results
+                   pmChip *chip,     // Chip for which to get statistics
+                   psFits *fits,     // FITS file handle
+                   pmFPAview *view,  // View for analysis
+                   ppStatsData *data,// The data
+                   pmConfig *config // Configuration
+    )
+{
+    assert(fpaResults);
+    assert(chip);
+    assert(view);
+    assert(data);
+    assert(config);
+
+    const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); // Name of chip
+
+    // Check to see if this is a chip of interest
+    if (!p_ppStatsDoThis(data->chips, chipName)) {
+        return PS_EXIT_SUCCESS;
+    }
+    if (!chip->file_exists && !chip->data_exists) {
+        return PS_EXIT_SUCCESS;
+    }
+
+    // Extract Header and Concept values from the Chip level
+
+    // find or generate a metadata to hold the results
+    bool mdok;                          // Status of MD lookup
+    psMetadata *chipResults = psMemIncrRefCounter(psMetadataLookupMetadata(&mdok, fpaResults, chipName));
+    if (!mdok || !chipResults) {
+        chipResults = psMetadataAlloc();
+    }
+
+    // Extract Header values
+    if (psListLength(data->headers)) {
+        // extract from existing headers
+        if (chip->hdu) {
+            if (fits && !pmChipReadHeader(chip, fits, config)) {
+                psError (PS_ERR_IO, false, "trouble reading chip header\n");
+                psFree(chipResults);
+                return PS_EXIT_DATA_ERROR;
+            }
+            pmHDU *hdu = chip->hdu;     // HDU for headers
+            p_ppStatsGetMetadata(chipResults, hdu->header, data->headers);
+        }
+        // extract from data->analysis output MD entries
+        if (psListLength(data->analysis)) {
+            p_ppStatsGetAnalysis (chipResults, data->headers, chip->analysis, data->analysis);
+        }
+    }
+
+    // Extract Concept values
+    if (psListLength(data->concepts) > 0) {
+        if (fits && chip->hdu && !pmChipReadHeader(chip, fits, config)) {
+            psError (PS_ERR_IO, false, "trouble reading chip header\n");
+            psFree(chipResults);
+            return PS_EXIT_DATA_ERROR;
+        }
+        pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_ALL, false, false, config);
+        p_ppStatsGetMetadata(chipResults, chip->concepts, data->concepts);
+    }
+
+    // check if we can legitimately iterate to the cell level
+    psArray *cells = chip->cells;       // Array of cells
+    if (view->cell >= cells->n) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Desired cell view (%d) doesn't match "
+                "number of cells (%ld)\n", view->cell, cells->n);
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    // Iterate over cells (if view->cell is set, skip all others)
+    for (int i = 0; i < cells->n; i++) {
+        if ((view->cell >= 0) && (i != view->cell)) continue;
+        pmCell *cell = cells->data[i];  // Cell of interest
+
+	// XXX for now, skip the video cells (cell->readouts->n > 1)
+	if ((cell->readouts->n > 1) && !data->doFirstReadout3D){
+	  psWarning ("Skipping Video Cell for ppStatsCell");
+	  continue;
+	}
+
+        psExit result = ppStatsCell(chipResults, cell, fits, view, data, config);
+        if (result != PS_EXIT_SUCCESS) {
+            psError(PS_ERR_UNKNOWN, false, "trouble with cell stats for %d\n", i);
+            if (fits) {
+                pmChipFreeData(chip);
+            }
+            psFree(chipResults);
+            return result;
+        }
+    }
+
+    if (fits) {
+        pmChipFreeData(chip);
+    }
+
+    p_ppStatsAddToHierarchy(chipResults, fpaResults, chipName, "Results for chip");
+
+    psFree (chipResults);
+    return PS_EXIT_SUCCESS;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsData.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsData.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsData.c	(revision 22322)
@@ -0,0 +1,53 @@
+#include "ppStatsInternal.h"
+
+static void statsDataFree(ppStatsData *data // Data to free
+    )
+{
+    if (data->fits) {
+        psFitsClose(data->fits);
+        data->fits = NULL;
+    }
+    psFree(data->fpa);
+    psFree(data->view);
+    psFree(data->headers);
+    psFree(data->concepts);
+    psFree(data->analysis);
+    psFree(data->summary);
+
+    psFree(data->stats);
+    psFree(data->fileView);
+
+    psFree(data->chips);
+    psFree(data->cells);
+
+    return;
+}
+
+
+ppStatsData *ppStatsDataAlloc(void)
+{
+    ppStatsData *data = psAlloc(sizeof(ppStatsData)); // Newly allocated data
+    psMemSetDeallocator(data, (psFreeFunc)statsDataFree);
+
+    data->fits = NULL;
+    data->fpa = NULL;
+    data->view = NULL;
+
+    data->headers = psListAlloc(NULL);
+    data->concepts = psListAlloc(NULL);
+    data->analysis = psListAlloc(NULL);
+    data->summary = psListAlloc(NULL);
+    data->stats = psStatsAlloc(0);
+    data->doStats = false;
+    data->fileLevel = false;
+    data->fileView = NULL;
+
+    data->sample = 0;
+    data->maskVal = 0;
+    data->doFirstReadout3D = false;
+    data->chips = psListAlloc(NULL);
+    data->cells = psListAlloc(NULL);
+
+    return data;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,26 @@
+/*
+ * The line
+    { PPSTATS_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+#include "pslib.h"
+#include "ppStatsErrorCodes.h"
+
+void ppStatsErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { PPSTATS_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PPSTATS_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = PPSTATS_ERR_NERROR - PPSTATS_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       p_psMemSetPersistent(tmp, true);
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsErrorCodes.dat	(revision 22322)
@@ -0,0 +1,10 @@
+#
+# This file is used to generate ppStatsErrorClasses.h
+#
+BASE = 400		First value we use; lower values belong to psLib
+# these errors correspond to standard exit conditions
+ARGUMENTS               Incorrect arguments
+SYS                     System error
+CONFIG                  Problem in configure files
+PROG                    Programming error
+DATA                    invalid data
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,18 @@
+#if !defined(PPSTATS_ERROR_CODES_H)
+#define PPSTATS_ERROR_CODES_H
+/*
+ * The line
+ *  PPSTATS_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    PPSTATS_ERR_BASE = 512,
+    PPSTATS_ERR_${ErrorCode},
+    PPSTATS_ERR_NERROR
+} ppStatsErrorCode;
+
+void ppStatsErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFPA.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFPA.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFPA.c	(revision 22322)
@@ -0,0 +1,55 @@
+#include "ppStatsInternal.h"
+
+psMetadata *ppStatsFPA(psMetadata *out,
+		       pmFPA *fpa,         // FPA for which to get statistics
+		       pmFPAview *view,    // View for analysis
+		       psMaskType maskVal, // Value to mask
+		       pmConfig *config    // Configuration
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL)
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    ppStatsData *data = ppStatsDataAlloc(); // All the input data
+
+    // Get the options, open the files
+    if (!ppStatsSetupFromRecipe(data, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to get ppStats options from recipe.");
+        psFree(data);
+        return NULL;
+    }
+
+    // Override recipe mask value
+    data->maskVal = maskVal;
+
+    if (data->fpa) {
+        psFree(data->fpa);
+    }
+    data->fpa = psMemIncrRefCounter(fpa);
+
+    if (data->view) {
+        psFree(data->view);
+    }
+    data->view = psMemIncrRefCounter(view);
+
+    // Go through the FPA and do the hard work
+    psExit status;                      // Status of statistics loop
+    psMetadata *result = ppStatsLoop(&status, data, config);
+    if (status != PS_EXIT_SUCCESS) {
+        psError (PS_ERR_UNKNOWN, false, "Not able to measure FPA statistics.\n");
+        psFree(result);
+        psFree(data);
+        return (NULL);
+    }
+
+    if (out != NULL) {
+	psMetadataOverlay (out, result);
+	psFree(result);
+	psFree(data);
+	return out;
+    }
+
+    psFree(data);
+    return result;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFringe.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFringe.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFringe.c	(revision 22322)
@@ -0,0 +1,37 @@
+#include "ppStatsInternal.h"
+
+bool ppStatsFringe(psMetadata *stats, const pmChip *chip, const char *root, const char *fringeName)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_STRING_NON_EMPTY(fringeName, false);
+
+    bool mdok;                          // Status of MD lookup
+    const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); // Name of chip
+
+    psMetadata *chipStats = psMetadataLookupMetadata(&mdok, stats, chipName); // Chip statistics
+    if (!mdok || !chipStats) {
+        chipStats = psMetadataAlloc();
+        psMetadataAddMetadata(stats, PS_LIST_TAIL, chipName, 0, NULL, chipStats);
+    } else {
+        psMemIncrRefCounter(chipStats);
+    }
+
+    pmFringeScale *fringes = psMetadataLookupPtr(NULL, chip->analysis, fringeName); // Solution for fringes
+    for (int i = 0; i < fringes->nFringeFrames; i++) {
+        psString name = NULL; // Name of statistic
+        psStringAppend(&name, "%s_%d", root, i);
+        psMetadataAddF32(chipStats, PS_LIST_TAIL, name, 0, "Fringe amplitude",
+                         fringes->coeff->data.F32[i + 1]);
+        psFree(name);
+        name = NULL;
+        psStringAppend(&name, "%s_ERR_%d", root, i);
+        psMetadataAddF32(chipStats, PS_LIST_TAIL, name, 0, "Fringe amplitude error",
+                         fringes->coeffErr->data.F32[i + 1]);
+        psFree(name);
+    }
+
+    psFree(chipStats);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadata.c	(revision 22322)
@@ -0,0 +1,69 @@
+# include "ppStatsInternal.h"
+
+// USAGE: ppStatsFromMetadata (input) (output) [-recipe PPSTATS_METADATA recipe]
+int main(int argc, char **argv) {
+
+    unsigned int nFail;
+    bool status;
+
+    psLibInit(NULL);
+
+    // Parse the configuration and arguments
+    pmConfig *config = pmConfigRead(&argc, argv, PPSTATS_MD_RECIPE);
+    if (!config) {
+        psErrorStackPrint(stderr, "Unable to read configuration.\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    if (argc != 4) {
+        psErrorStackPrint(stderr, "USAGE: ppStatsFromMetadata (input) (output) (recipe)\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    psMetadata *input = NULL;
+    if (!strcmp (argv[1], "-")) {
+	psString string = psSlurpFile (stdin);
+	input = psMetadataConfigParse (NULL, &nFail, string, false);
+	psFree (string);
+    } else {
+	input = psMetadataConfigRead (NULL, &nFail, argv[1], false);
+    }
+
+    // parse the recipe to determine the fields of interest
+    psMetadata *recipes = psMetadataLookupPtr (&status, config->recipes, PPSTATS_MD_RECIPE);
+    if (!recipes) {
+        psErrorStackPrint(stderr, "missing recipe set %s\n", PPSTATS_MD_RECIPE);
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    psMetadata *recipe = psMetadataLookupPtr (&status, recipes, argv[3]);
+    if (!recipes) {
+        psErrorStackPrint(stderr, "missing ppStatsFromMetadata recipe %s\n", argv[3]);
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    psArray *entries = ppStatsFromMetadataEntries (recipe);
+    if (!entries) {
+        psErrorStackPrint(stderr, "problem with recipe %s\n", PPSTATS_MD_RECIPE);
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    if (!ppStatsFromMetadataParse (input, entries)) {
+        psErrorStackPrint(stderr, "problem with input data\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+    
+    // calculate the stats for the non-constant entries (already calculated)
+    if (!ppStatsFromMetadataStats (entries)) {
+        psErrorStackPrint(stderr, "problem calculating statistics\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // calculate the stats for the non-constant entries (already calculated)
+    if (!ppStatsFromMetadataPrint (entries, argv[2])) {
+        psErrorStackPrint(stderr, "problem calculating statistics\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataEntries.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataEntries.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataEntries.c	(revision 22322)
@@ -0,0 +1,110 @@
+# include "ppStatsInternal.h"
+
+psArray *ppStatsFromMetadataEntries (psMetadata *recipe) {
+
+    bool status = false;
+
+    psMetadataItem *item = NULL;
+
+    psArray *entries = psArrayAllocEmpty(16);
+
+    // the recipe file should include a collection of psMetadata items with the name ENTRY
+    
+    // psMetadataPrint (stderr, recipe, 1);
+
+    // loop over the items, selecting those with the name "ENTRY"
+    psMetadataIterator *iter = psMetadataIteratorAlloc(recipe, PS_LIST_HEAD, NULL); // Iterator
+    while ((item = psMetadataGetAndIncrement(iter))) {
+	if (strcmp (item->name, "ENTRY")) continue;
+	
+	if (item->type != PS_DATA_METADATA) {
+	    psError(PS_ERR_UNKNOWN, false, "ppStatsFromMetadata ENTRY not of type METADATA\n");
+	    return NULL;
+	}
+
+	psMetadata *entryMD = item->data.md;
+
+	ppStatsEntry *entry = ppStatsEntryAlloc ();
+
+	entry->keyword = psMetadataLookupStr (&status, entryMD, "KEYWORD");
+	if (!entry->keyword) {
+	    psError(PS_ERR_UNKNOWN, true, "ppStatsFromMetadata recipe ENTRY missing KEYWORD value\n");
+	    return NULL;
+	}
+	
+	entry->statistic = psMetadataLookupStr (&status, entryMD, "STATISTIC");
+	if (!entry->statistic) {
+	    psError(PS_ERR_UNKNOWN, true, "ppStatsFromMetadata recipe ENTRY %s missing STATISTIC\n", entry->keyword);
+	    return NULL;
+	}
+
+	entry->flag = psMetadataLookupStr (&status, entryMD, "FLAG");
+	if (!entry->flag) {
+	    psError(PS_ERR_UNKNOWN, true, "ppStatsFromMetadata recipe ENTRY %s missing FLAG\n", entry->keyword);
+	    return NULL;
+	}
+
+	char *typename = psMetadataLookupStr (&status, entryMD, "TYPE");
+	if (!typename) {
+	    psError(PS_ERR_UNKNOWN, true, "ppStatsFromMetadata recipe ENTRY %s missing TYPE\n", entry->keyword);
+	    return NULL;
+	}
+
+	entry->type = psDataTypeFromString (typename);
+	if (!entry->type) {
+	    psError(PS_ERR_UNKNOWN, true, "ppStatsFromMetadata recipe ENTRY %s has invalid TYPE %s\n", entry->keyword, typename);
+	    return NULL;
+	}
+	
+	// fprintf (stderr, "adding %s to array of %ld\n", entry->keyword, entries->n);
+	psArrayAdd (entries, 16, entry);
+	psFree (entry);
+    }
+    
+    return entries;
+}
+
+void ppStatsEntryFree (ppStatsEntry *entry) {
+
+    if (!entry) return;
+
+    psFree (entry->keyword);
+    psFree (entry->statistic);
+    psFree (entry->flag);
+    psFree (entry->value);
+    psFree (entry->vector);
+    return;
+}
+
+ppStatsEntry *ppStatsEntryAlloc () {
+
+    ppStatsEntry *entry = (ppStatsEntry *) psAlloc (sizeof(ppStatsEntry));
+    psMemSetDeallocator(entry, (psFreeFunc) ppStatsEntryFree);
+    
+    entry->keyword = NULL;
+    entry->statistic = NULL;
+    entry->flag = NULL;
+    entry->value = NULL;
+    entry->vector = NULL;
+    return entry;
+}
+
+psDataType psDataTypeFromString (char *typename) {
+
+    if (!strcmp (typename, "U8"))  return PS_DATA_U8;
+    if (!strcmp (typename, "U16")) return PS_DATA_U16;
+    if (!strcmp (typename, "U32")) return PS_DATA_U32;
+    if (!strcmp (typename, "U64")) return PS_DATA_U64;
+
+    if (!strcmp (typename, "S8"))  return PS_DATA_S8;
+    if (!strcmp (typename, "S16")) return PS_DATA_S16;
+    if (!strcmp (typename, "S32")) return PS_DATA_S32;
+    if (!strcmp (typename, "S64")) return PS_DATA_S64;
+
+    if (!strcmp (typename, "F32")) return PS_DATA_F32;
+    if (!strcmp (typename, "F64")) return PS_DATA_F64;
+
+    if (!strcmp (typename, "STR")) return PS_DATA_STRING;
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataParse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataParse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataParse.c	(revision 22322)
@@ -0,0 +1,141 @@
+# include "ppStatsInternal.h"
+
+// loop over the metadata, adding entries of interest to their data arrays
+bool ppStatsFromMetadataParse (psMetadata *input, psArray *entries) {
+
+    psMetadataItem *item = NULL;
+
+    // loop over the items, selecting those with the name "ENTRY"
+    psMetadataIterator *iter = psMetadataIteratorAlloc(input, PS_LIST_HEAD, NULL); // Iterator
+
+    while ((item = psMetadataGetAndIncrement(iter))) {
+	if (item->type == PS_DATA_METADATA) {
+	    psMetadata *folder = item->data.md;
+	    if (!ppStatsFromMetadataParse (folder, entries)) {
+		psError(PS_ERR_UNKNOWN, false, "error parse metadata folder %s\n", item->name);
+		return false;
+	    }
+	    continue;
+	}
+
+	// find the matching entry, if one exists (if not, this is not an error: we are not
+	// obliged to determine stats for all entries).  we may have more than one entry for a
+	// given keyword (multiple stats for a single input item are allowed).
+	for (int i = 0; i < entries->n; i++) {
+	    ppStatsEntry *entry = entries->data[i];
+	    if (strcmp (entry->keyword, item->name)) continue;
+
+	    // check if the types match?
+	    if (entry->type != item->type) {
+		fprintf (stderr, "WARNING?  mismatched type, skipping\n");
+		continue;
+	    }
+
+	    // save the value
+
+	    // if constant, save or compare with existing value
+	    if (!strcasecmp (entry->statistic, "constant")) {
+		if (entry->value) {
+		    // check that they match
+		} else {
+		    entry->value = psMemIncrRefCounter (item);
+		}
+		continue;
+	    }
+
+	    bool useRMS = false;
+	    if (!strcasecmp (entry->statistic, "rms")) {
+		useRMS = true;
+	    }
+
+	    // only numerical values can have stats; all others must be 'constant'
+	    if (entry->type >= PS_DATA_BOOL) {
+		psError(PS_ERR_UNKNOWN, false, "only numerical types can have non-constant stats: %s\n", entry->keyword);
+		return false;
+	    }
+
+	    if (!entry->vector) {
+		entry->vector = psVectorAllocEmpty (16, entry->type);
+	    }
+
+	    switch (item->type) {
+	      case PS_DATA_U8:
+		if (useRMS) {
+		    psVectorAppend (entry->vector, PS_SQR(item->data.U8));
+		} else {
+		    psVectorAppend (entry->vector, item->data.U8);
+		}
+		break;
+	      case PS_DATA_U16:
+		if (useRMS) {
+		    psVectorAppend (entry->vector, PS_SQR(item->data.U16));
+		} else {
+		    psVectorAppend (entry->vector, item->data.U16);
+		}
+		break;
+	      case PS_DATA_U32:
+		if (useRMS) {
+		    psVectorAppend (entry->vector, PS_SQR(item->data.U32));
+		} else {
+		    psVectorAppend (entry->vector, item->data.U32);
+		}
+		break;
+	      case PS_DATA_U64:
+		if (useRMS) {
+		    psVectorAppend (entry->vector, PS_SQR(item->data.U64));
+		} else {
+		    psVectorAppend (entry->vector, item->data.U64);
+		}
+		break;
+
+	      case PS_DATA_S8:
+		if (useRMS) {
+		    psVectorAppend (entry->vector, PS_SQR(item->data.S8));
+		} else {
+		    psVectorAppend (entry->vector, item->data.S8);
+		}
+		break;
+	      case PS_DATA_S16:
+		if (useRMS) {
+		    psVectorAppend (entry->vector, PS_SQR(item->data.S16));
+		} else {
+		    psVectorAppend (entry->vector, item->data.S16);
+		}
+		break;
+	      case PS_DATA_S32:
+		if (useRMS) {
+		    psVectorAppend (entry->vector, PS_SQR(item->data.S32));
+		} else {
+		    psVectorAppend (entry->vector, item->data.S32);
+		}
+		break;
+	      case PS_DATA_S64:
+		if (useRMS) {
+		    psVectorAppend (entry->vector, PS_SQR(item->data.S64));
+		} else {
+		    psVectorAppend (entry->vector, item->data.S64);
+		}
+		break;
+
+	      case PS_DATA_F32:
+		if (useRMS) {
+		    psVectorAppend (entry->vector, PS_SQR(item->data.F32));
+		} else {
+		    psVectorAppend (entry->vector, item->data.F32);
+		}
+		break;
+	      case PS_DATA_F64:
+		if (useRMS) {
+		    psVectorAppend (entry->vector, PS_SQR(item->data.F64));
+		} else {
+		    psVectorAppend (entry->vector, item->data.F64);
+		}
+		break;
+	      default:
+		psError(PS_ERR_UNKNOWN, true, "ppStatsFromMetadata recipe ENTRY %s has invalid type\n", entry->keyword);
+		return false;
+	    }
+	}
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataPrint.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataPrint.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataPrint.c	(revision 22322)
@@ -0,0 +1,35 @@
+# include "ppStatsInternal.h"
+
+// calculate the stats for the non-constant entries (already calculated)
+bool ppStatsFromMetadataPrint (psArray *entries, char *filename) {
+
+    FILE *f = NULL;
+    if (!strcmp (filename, "-")) {
+	f = stdout;
+    } else {
+	f = fopen (filename, "w");
+	if (f == NULL) {
+	    psError(PS_ERR_UNKNOWN, false, "ppStatsFromMetadata cannot open output file %s\n", filename);
+	    return false;
+	}
+    }
+
+    // at this point, we have entries with values (of type STR or F32) or NULL
+    for (int i = 0; i < entries->n; i++) {
+	ppStatsEntry *entry = entries->data[i];
+
+	if (!entry->value) continue;
+
+	if (entry->value->type == PS_DATA_STRING) {
+	    fprintf (f, "%s %s ", entry->flag, entry->value->data.str);
+	}
+
+	if (entry->value->type == PS_DATA_F32) {
+	    fprintf (f, "%s %f ", entry->flag, entry->value->data.F32);
+	}
+    }
+    fprintf (f, "\n");
+
+    if (f != stdout) fclose (f);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsFromMetadataStats.c	(revision 22322)
@@ -0,0 +1,34 @@
+# include "ppStatsInternal.h"
+
+// calculate the stats for the non-constant entries (already calculated)
+bool ppStatsFromMetadataStats (psArray *entries) {
+
+    for (int i = 0; i < entries->n; i++) {
+	ppStatsEntry *entry = entries->data[i];
+
+	if (!strcasecmp (entry->statistic, "constant")) continue;
+	
+	// XXX skip or warn on missing stats?
+	if (!entry->vector) continue;
+
+	psStatsOptions option;
+	if (!strcasecmp (entry->statistic, "rms")) {
+	    option = psStatsOptionFromString ("ROBUST_MEDIAN");
+	} else {
+	    option = psStatsOptionFromString (entry->statistic);
+	}
+	
+	psStats *stats = psStatsAlloc (option);
+
+	psVectorStats (stats, entry->vector, NULL, NULL, 0);
+
+	double value = psStatsGetValue (stats, option);
+
+	if (!strcasecmp (entry->statistic, "rms")) {
+	    value = sqrt(value);
+	}
+
+	entry->value = psMetadataItemAllocF32 (entry->keyword, entry->statistic, value);
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsInternal.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsInternal.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsInternal.h	(revision 22322)
@@ -0,0 +1,17 @@
+
+# ifdef HAVE_CONFIG_H
+# include <config.h>
+# endif
+
+#ifndef PP_STATS_STAND_ALONE_H
+#define PP_STATS_STAND_ALONE_H
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStats.h"
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsLoop.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "ppStatsInternal.h"
+
+psMetadata *ppStatsLoop(psExit *result,
+                        ppStatsData *data, // The data
+                        pmConfig *config // Configuration
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(data, NULL);
+    pmFPA *fpa = data->fpa;             // FPA to analyse
+    psFits *fits = data->fits;          // FITS file handle
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    *result = PS_EXIT_SUCCESS;
+
+    // allocate or bump the ref counter (so we can just free below)
+    pmFPAview *view = (data->view) ? psMemIncrRefCounter(data->view) : pmFPAviewAlloc(0);
+
+    // allocate a new one if needed
+    psMetadata *newResults = psMetadataAlloc();
+
+    // Iterate through the FPA
+    if (psListLength(data->headers) > 0 && fpa->hdu) {
+        if (fits && !pmFPAReadHeader(fpa, fits, config)) {
+            psError(PS_ERR_IO, false, "Unable to read header for FPA.");
+            psFree(view);
+            psFree(newResults);
+            *result = PS_EXIT_DATA_ERROR;
+            return NULL;
+        }
+        pmHDU *hdu = fpa->hdu;          // HDU for headers
+        p_ppStatsGetMetadata(newResults, hdu->header, data->headers);
+    }
+    if (psListLength(data->concepts) > 0) {
+        if (fits && fpa->hdu && !pmFPAReadHeader(fpa, fits, config)) {
+            psError(PS_ERR_IO, false, "Unable to read header for FPA.");
+            psFree(view);
+            psFree(newResults);
+            *result = PS_EXIT_DATA_ERROR;
+            return NULL;
+        }
+        pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_ALL, false, config);
+        p_ppStatsGetMetadata(newResults, fpa->concepts, data->concepts);
+    }
+
+    // check if we can legitimately iterate to the chip level
+    psArray *chips = fpa->chips;        // Array of chips
+    if (view->chip >= chips->n) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Desired chip view (%d) doesn't match "
+                "number of chips (%ld)\n", view->chip, chips->n);
+        return NULL;
+    }
+
+    // Iterate over chips (if view->chip is set, skip all others)
+    for (int i = 0; i < chips->n; i++) {
+        if ((view->chip >= 0) && (i != view->chip)) continue;
+        pmChip *chip = chips->data[i];  // Chip of interest
+        *result = ppStatsChip(newResults, chip, fits, view, data, config);
+        if (*result != PS_EXIT_SUCCESS) {
+            psError(PS_ERR_UNKNOWN, false, "trouble with stats for chip %d\n", i);
+            psFree (view);
+            psFree (newResults);
+            return NULL;
+        }
+    }
+
+    if (fits) {
+        pmFPAFreeData(fpa);
+    }
+
+    psFree(view);
+    return newResults;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsMetadata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsMetadata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsMetadata.c	(revision 22322)
@@ -0,0 +1,63 @@
+#include "ppStatsInternal.h"
+
+// measure only the non-pixel-related statistics (headers, concepts)
+psMetadata *ppStatsMetadata(psMetadata *out,
+			    pmFPA *fpa,         // FPA for which to get statistics
+			    pmFPAview *view,    // View for analysis
+			    psMaskType maskVal, // Value to mask
+			    pmConfig *config    // Configuration
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL)
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    ppStatsData *data = ppStatsDataAlloc(); // All the input data
+
+    // Get the options, open the files
+    if (!ppStatsSetupFromRecipe(data, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to get ppStats options from recipe.");
+        psFree(data);
+        return NULL;
+    }
+
+    // drop the 'concept' and 'header' elements of data:
+    psFree (data->summary);    
+    psFree (data->stats);
+    data->summary = psListAlloc(NULL);
+    data->stats = psStatsAlloc(0);
+    data->doStats = false;
+
+    // Override recipe mask value
+    data->maskVal = maskVal;
+
+    if (data->fpa) {
+        psFree(data->fpa);
+    }
+    data->fpa = psMemIncrRefCounter(fpa);
+
+    if (data->view) {
+        psFree(data->view);
+    }
+    data->view = psMemIncrRefCounter(view);
+
+    // Go through the FPA and do the hard work
+    psExit status;                      // Status of statistics loop
+    psMetadata *result = ppStatsLoop(&status, data, config);
+    if (status != PS_EXIT_SUCCESS) {
+        psError (PS_ERR_UNKNOWN, false, "Not able to measure FPA statistics.\n");
+        psFree(result);
+        psFree(data);
+        return (NULL);
+    }
+
+    if (out != NULL) {
+	psMetadataOverlay (out, result);
+	psFree(result);
+	psFree(data);
+	return out;
+    }
+
+    psFree(data);
+    return result;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsPixels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsPixels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsPixels.c	(revision 22322)
@@ -0,0 +1,62 @@
+#include "ppStatsInternal.h"
+
+// measure only the pixel-related statistics (stats, summary)
+psMetadata *ppStatsPixels(psMetadata *out,
+			  pmFPA *fpa,         // FPA for which to get statistics
+			  pmFPAview *view,    // View for analysis
+			  psMaskType maskVal, // Value to mask
+			  pmConfig *config    // Configuration
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL)
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    ppStatsData *data = ppStatsDataAlloc(); // All the input data
+
+    // Get the options, open the files
+    if (!ppStatsSetupFromRecipe(data, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to get ppStats options from recipe.");
+        psFree(data);
+        return NULL;
+    }
+
+    // drop the 'concept' and 'header' elements of data:
+    psFree (data->headers);    
+    psFree (data->concepts);
+    data->headers = psListAlloc(NULL);
+    data->concepts = psListAlloc(NULL);
+
+    // Override recipe mask value 
+    data->maskVal = maskVal;
+
+    if (data->fpa) {
+        psFree(data->fpa);
+    }
+    data->fpa = psMemIncrRefCounter(fpa);
+
+    if (data->view) {
+        psFree(data->view);
+    }
+    data->view = psMemIncrRefCounter(view);
+
+    // Go through the FPA and do the hard work
+    psExit status;                      // Status of statistics loop
+    psMetadata *result = ppStatsLoop(&status, data, config);
+    if (status != PS_EXIT_SUCCESS) {
+        psError (PS_ERR_UNKNOWN, false, "Not able to measure FPA statistics.\n");
+        psFree(result);
+        psFree(data);
+        return (NULL);
+    }
+
+    if (out != NULL) {
+	psMetadataOverlay (out, result);
+	psFree(result);
+	psFree(data);
+	return out;
+    }
+
+    psFree(data);
+    return result;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsReadout.c	(revision 22322)
@@ -0,0 +1,207 @@
+# include "ppStatsInternal.h"
+
+static bool warnNonFinite = false;     // Have we warned about non-finite values?
+
+psExit ppStatsReadout(psMetadata *cellResults, // Metadata holding the chip results
+                      pmReadout *readout,       // Cell for which to get statistics
+                      int nReadout,     // readout number
+                      ppStatsData *data,        // The data
+                      const pmConfig *config // Configuration
+    )
+{
+    assert(cellResults);
+    assert(readout);
+    assert(data);
+    assert(config);
+
+    /*** psphot and psastro put their results on the readout->analysis metadata (PSPHOT.HEADER,
+         PSASTRO.HEADER).  we need to pull quantities of interest from those locations. to do
+         this, we need to select the appropriate readout.  ***/
+
+    // Extract Header and Concept values from the Cell and Readout->analysis level
+
+    psString readoutName = NULL;
+    psStringAppend (&readoutName, "READOUT.%02d", nReadout);
+
+    // find or generate a metadata to hold the results
+    bool mdok;                          // Status of MD lookup
+    psMetadata *readoutResults = psMemIncrRefCounter(psMetadataLookupMetadata(&mdok, cellResults, readoutName));
+    if (!mdok || !readoutResults) {
+        readoutResults = psMetadataAlloc();
+    }
+
+    // Extract Header values
+    if (psListLength(data->headers)) {
+        // extract from data->analysis output MD entries
+        if (psListLength(data->analysis)) {
+            p_ppStatsGetAnalysis (readoutResults, data->headers, readout->analysis, data->analysis);
+        }
+    }
+
+    // Do we want to measure pixel statistics?
+    if (!data->doStats && !psListLength(data->summary)) {
+        // Nothing further to do --- don't want to waste our time reading the data
+        goto readoutDone;
+    }
+
+    if (!readout->image) {
+        psLogMsg(__func__, PS_LOG_WARN, "No image associated with readout --- ignored.\n");
+        goto readoutDone;
+    }
+
+    // Measure basic image statistics (means, stdevs, etc).
+    if (data->sample <= 0.0) {
+        if (!psImageStats(data->stats, readout->image, readout->mask, data->maskVal)) {
+            psWarning("Unable to perform statistics on readout %s --- ignored.\n", readoutName);
+            goto statsDone;
+        }
+    } else {
+        // Apply sampling
+        psImage *image = readout->image; // The image of interest
+        psImage *mask = readout->mask; // The mask image
+        int numSamples = data->sample * image->numCols * image->numRows; // Number of samples
+        int sampleSpace = 1.0 / data->sample; // Space between samples
+        psVector *sampleValues = psVectorAlloc(numSamples, PS_TYPE_F32); // Vector of samples
+        psVector *sampleMask = psVectorAlloc(numSamples, PS_TYPE_MASK);  // Corresponding mask
+        if (!mask) {
+            psVectorInit(sampleMask, 0);
+        }
+        for (int i = 0; i < numSamples; i++) {
+            int j = i * sampleSpace;
+            int y = j / image->numCols;
+            int x = j % image->numCols;
+            sampleValues->data.F32[i] = image->data.F32[y][x];
+            if ((!mask || !(mask->data.PS_TYPE_MASK_DATA[y][x] & data->maskVal)) &&
+                !isfinite(sampleValues->data.F32[i])) {
+                if (!warnNonFinite) {
+                    psWarning("Unmasked non-finite value detected at %d,%d; suppressing further warnings",
+                              x, y);
+                    warnNonFinite = true;
+                }
+                sampleMask->data.PS_TYPE_MASK_DATA[i] = data->maskVal;
+            } else if (mask) {
+                sampleMask->data.PS_TYPE_MASK_DATA[i] = mask->data.PS_TYPE_MASK_DATA[y][x];
+            }
+        }
+        if (!psVectorStats(data->stats, sampleValues, NULL, sampleMask, data->maskVal)) {
+            psWarning("Unable to perform statistics on readout %s.\n", readoutName);
+            psErrorClear();
+        }
+        psFree(sampleValues);
+        psFree(sampleMask);
+    }
+
+#define WRITE_STAT(SYMBOL, NAME, SOURCE) \
+    if (data->stats->options & SYMBOL) { \
+        psMetadataAddF32(readoutResults, PS_LIST_TAIL, NAME, 0, NULL, data->stats->SOURCE); \
+    }
+
+    WRITE_STAT(PS_STAT_SAMPLE_MEAN,     "SAMPLE_MEAN",     sampleMean);
+    WRITE_STAT(PS_STAT_SAMPLE_MEDIAN,   "SAMPLE_MEDIAN",   sampleMedian);
+    WRITE_STAT(PS_STAT_SAMPLE_STDEV,    "SAMPLE_STDEV",    sampleStdev);
+    WRITE_STAT(PS_STAT_SAMPLE_SKEWNESS, "SAMPLE_SKEWNESS", sampleSkewness);
+    WRITE_STAT(PS_STAT_SAMPLE_KURTOSIS, "SAMPLE_KURTOSIS", sampleKurtosis);
+    WRITE_STAT(PS_STAT_SAMPLE_QUARTILE, "SAMPLE_LQ",       sampleLQ);
+    WRITE_STAT(PS_STAT_SAMPLE_QUARTILE, "SAMPLE_UQ",       sampleUQ);
+    WRITE_STAT(PS_STAT_ROBUST_MEDIAN,   "ROBUST_MEDIAN",   robustMedian);
+    WRITE_STAT(PS_STAT_ROBUST_STDEV,    "ROBUST_STDEV",    robustStdev);
+    WRITE_STAT(PS_STAT_ROBUST_QUARTILE, "ROBUST_LQ",       robustLQ);
+    WRITE_STAT(PS_STAT_ROBUST_QUARTILE, "ROBUST_UQ",       robustUQ);
+    WRITE_STAT(PS_STAT_ROBUST_QUARTILE, "ROBUST_N50",      robustN50);
+    WRITE_STAT(PS_STAT_FITTED_MEAN,     "FITTED_MEAN",     fittedMean);
+    WRITE_STAT(PS_STAT_FITTED_STDEV,    "FITTED_STDEV",    fittedStdev);
+    WRITE_STAT(PS_STAT_CLIPPED_MEAN,    "CLIPPED_MEAN",    clippedMean);
+    WRITE_STAT(PS_STAT_CLIPPED_STDEV,   "CLIPPED_STDEV",   clippedStdev);
+
+    // measure other types of statistics tests
+
+statsDone:
+    // count saturated pixels
+    if (psListLength(data->summary) > 0) {
+        bool get_nSatPixels = false;
+        bool get_fSatPixels = false;
+        bool findNumGood = false;       // Return the number of good pixels?
+        bool findFracGood = false;      // Return the fraction of good pixels?
+
+        psListIterator *iterator = psListIteratorAlloc(data->summary, PS_LIST_HEAD, false);
+        psString choice;
+        while ((choice = psListGetAndIncrement(iterator))) {
+            if (!strcasecmp(choice, "SAT_PIXEL_NUM"))  get_nSatPixels = true;
+            if (!strcasecmp(choice, "SAT_PIXEL_FRAC")) get_fSatPixels = true;
+            if (!strcasecmp(choice, "GOOD_PIXEL_NUM")) findNumGood = true;
+            if (!strcasecmp(choice, "GOOD_PIXEL_FRAC")) findFracGood = true;
+        }
+
+        if (get_nSatPixels || get_fSatPixels) {
+            // Find the saturation point
+            float saturation = psMetadataLookupF32(&mdok, readout->parent->concepts,
+                                                   "CELL.SATURATION"); // Saturation level
+            if (!mdok || isnan(saturation)) {
+                psWarning("CELL.SATURATION is not set --- unable to measure N_SAT_PIXELS.\n");
+                if (get_nSatPixels) {
+                    psMetadataAddS32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_NUM", 0, NULL, 0);
+                }
+                if (get_fSatPixels) {
+                    psMetadataAddF32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_FRAC", 0, NULL, NAN);
+                }
+            } else {
+                int nSatPixels = 0;
+                for (int j = 0; j < readout->image->numRows; j++) {
+                    for (int i = 0; i < readout->image->numCols; i++) {
+                        if (readout->image->data.F32[j][i] >= saturation) {
+                            nSatPixels ++;
+                        }
+                    }
+                }
+                if (get_nSatPixels) {
+                    psMetadataAddS32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_NUM", 0,
+                                     "Number of saturated pixels", nSatPixels);
+                }
+                if (get_fSatPixels) {
+                    psMetadataAddF32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_FRAC", 0,
+                                     "Fraction of saturated pixels",
+                                     nSatPixels / (float)(readout->image->numRows * readout->image->numCols));
+                }
+            }
+        }
+
+        if (findNumGood || findFracGood) {
+            if (!readout->mask || data->maskVal == 0) {
+                psWarning("Number or fraction of good pixels requested, but no mask provided");
+                if (findNumGood) {
+                    psMetadataAddS32(readoutResults, PS_LIST_TAIL, "GOOD_PIXEL_NUM", 0, NULL, 0);
+                }
+                if (findFracGood) {
+                    psMetadataAddF32(readoutResults, PS_LIST_TAIL, "GOOD_PIXEL_FRAC", 0, NULL, NAN);
+                }
+            } else {
+                int numBad = 0;            // Number of bad pixels
+                for (int j = 0; j < readout->mask->numRows; j++) {
+                    for (int i = 0; i < readout->mask->numCols; i++) {
+                        if (readout->mask->data.PS_TYPE_MASK_DATA[j][i] & data->maskVal) {
+                            numBad++;
+                        }
+                    }
+                }
+
+                int numTotal = readout->mask->numRows * readout->mask->numCols; // Total number of pixels
+                int numGood = numTotal - numBad; // Number of good pixels
+                if (findNumGood) {
+                    psMetadataAddS32(readoutResults, PS_LIST_TAIL, "GOOD_PIXEL_NUM", 0,
+                                     "Number of good pixels", numGood);
+                }
+                if (findFracGood) {
+                    psMetadataAddF32(readoutResults, PS_LIST_TAIL, "GOOD_PIXEL_FRAC", 0,
+                                     "Fraction of good pixels", numGood / (float)numTotal);
+                }
+            }
+        }
+    }
+
+readoutDone:
+    // Add the readout results to the cell
+    p_ppStatsAddToHierarchy(readoutResults, cellResults, readoutName, "Results for readout");
+    psFree (readoutResults);
+    psFree (readoutName);
+    return PS_EXIT_SUCCESS;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsSetupFromArgs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsSetupFromArgs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsSetupFromArgs.c	(revision 22322)
@@ -0,0 +1,226 @@
+#include "ppStatsInternal.h"
+
+// This file is for setting up the required inputs from the command-line
+
+// Print usage information and die
+static void usageAndDie(char *argv[],   // Command-line arguments: only need the first which is always present
+                        pmConfig *config // Configuration
+    )
+{
+    printf("Return headers, concepts and/or image statistics.\n\n"
+           "Usage:\n"
+           "\t%s INPUT.fits [OUTPUT_NAME]\n"
+           "\n", argv[0]);
+    psArgumentHelp(config->arguments);
+    psFree(config);
+    psLibFinalize();
+    pmConceptsDone();
+    pmConfigDone();
+    exit(EXIT_FAILURE);
+}
+
+// Generate a list from the arguments
+static void listFromArguments(psMetadata *arguments, // Arguments to parse
+                              const char *name, // Name of the item, for error message
+                              const char *flag, // The flag for the argument of interest
+                              psList *target // The target list
+    )
+{
+    psString regex = NULL;              // Regular expression for the flag
+    psStringAppend(&regex, "^%s$", flag);
+    psMetadataIterator *iterator = psMetadataIteratorAlloc(arguments, PS_LIST_HEAD, regex); // Iterator
+    psFree(regex);
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iterator))) {
+        if (item->type != PS_DATA_STRING) {
+            psLogMsg(__func__, PS_LOG_WARN, "%s name is not of type STRING (%x) --- ignored.\n",
+                     name, item->type);
+            continue;
+        }
+        if (item->data.V && strlen(item->data.V) > 0) {
+            psListAdd(target, PS_LIST_TAIL, item->data.V);
+        }
+    }
+    psFree(iterator);
+
+    return;
+}
+
+// Set the statistics option; for arguments
+static inline void statsOptionArguments(psMetadata *arguments, // Arguments to parse
+                                        const char *name, // Name for option
+                                        ppStatsData *data, // Configuration data
+                                        psStatsOptions option // Option to check for
+    )
+{
+    if (psMetadataLookupBool(NULL, arguments, name)) {
+        data->stats->options |= option;
+        data->doStats = true;
+    }
+    return;
+}
+
+// Print out what we're going to do, from the list
+static void checkList(psList *list,     // List
+                      const char *name  // Name of list
+    )
+{
+    psString value;
+    psListIterator *iterator = psListIteratorAlloc(list, PS_LIST_HEAD, false);
+    while ((value = psListGetAndIncrement(iterator))) {
+        printf("%s: %s\n", name, value);
+    }
+    psFree(iterator);
+    return;
+}
+
+
+ppStatsData *ppStatsSetupFromArgs(int *argc, char *argv[], // Command-line arguments
+                                  pmConfig *config // Configuration
+    )
+{
+    // Setup and parse command-line arguments
+    psMetadata *arguments = config->arguments; // Arguments
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-chip", PS_META_DUPLICATE_OK, "Chip to inspect", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-cell", PS_META_DUPLICATE_OK, "Cell to inspect", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-header", PS_META_DUPLICATE_OK, "Header to look up", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-concept", PS_META_DUPLICATE_OK, "Concept to look up", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-summary", PS_META_DUPLICATE_OK, "Summary statistic to calculate", NULL);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-mean", 0, "Calculate sample mean", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-stdev", 0, "Calculate sample standard deviation", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-median", 0, "Calculate sample median", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-quartile", 0, "Calculate sample quartiles", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-skewness", 0, "Calculate sample skewness", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-kurtosis", 0, "Calculate sample kurtosis", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-robust-median", 0, "Calculate robust median", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-robust-stdev", 0, "Calculate robust standard deviation", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-robust-quartile", 0, "Calculate robust quartile range", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-fitted-mean", 0, "Calculate fitted mean", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-fitted-stdev", 0, "Calculate fitted standard deviation", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-clipped-mean", 0, "Calculate clipped median", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-clipped-stdev", 0, "Calculate clipped standard deviation", false);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-iter", 0, "Clipping iterations", 0);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-rej", 0, "Clipping level", 0.0);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-sample", 0, "Sampling fraction", 0.0);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-level", 0, "File level", 0);
+
+    if (*argc == 1) {
+        // No command-line arguments: print the help
+        usageAndDie(argv, config);
+    }
+    if (!psArgumentParse(arguments, argc, argv) ||
+        (*argc != 2 && *argc != 3)) {
+        printf("Unable to parse command-line arguments.\n\n");
+        usageAndDie(argv, config);
+    }
+
+    // Parse the command-line options
+    ppStatsData *data = ppStatsDataAlloc(); // The data
+    const char *inName = argv[1]; // Input file name
+    psArgumentRemove(1, argc, argv);
+
+    listFromArguments(arguments, "Chip", "-chip", data->chips);
+    listFromArguments(arguments, "Cell", "-cell", data->cells);
+    listFromArguments(arguments, "Header", "-header", data->headers);
+    listFromArguments(arguments, "Concept", "-concept", data->concepts);
+    listFromArguments(arguments, "Summary", "-summary", data->summary);
+
+    // Set the statistics options
+    statsOptionArguments(arguments, "-mean", data,     PS_STAT_SAMPLE_MEAN);
+    statsOptionArguments(arguments, "-stdev", data,    PS_STAT_SAMPLE_STDEV);
+    statsOptionArguments(arguments, "-median", data,   PS_STAT_SAMPLE_MEDIAN);
+    statsOptionArguments(arguments, "-quartile", data, PS_STAT_SAMPLE_QUARTILE);
+    statsOptionArguments(arguments, "-skewness", data, PS_STAT_SAMPLE_SKEWNESS);
+    statsOptionArguments(arguments, "-kurtosis", data, PS_STAT_SAMPLE_KURTOSIS);
+    statsOptionArguments(arguments, "-robust-median", data,   PS_STAT_ROBUST_MEDIAN);
+    statsOptionArguments(arguments, "-robust-stdev", data,    PS_STAT_ROBUST_STDEV);
+    statsOptionArguments(arguments, "-robust-quartile", data, PS_STAT_ROBUST_QUARTILE);
+    statsOptionArguments(arguments, "-fitted-mean", data,   PS_STAT_FITTED_MEAN);
+    statsOptionArguments(arguments, "-fitted-stdev", data,  PS_STAT_FITTED_STDEV);
+    statsOptionArguments(arguments, "-clipped-mean", data,  PS_STAT_CLIPPED_MEAN);
+    statsOptionArguments(arguments, "-clipped-stdev", data, PS_STAT_CLIPPED_STDEV);
+    data->stats->clipSigma = psMetadataLookupF32(NULL, arguments, "-rej");
+    data->stats->clipIter = psMetadataLookupS32(NULL, arguments, "-iter");
+    data->sample = psMetadataLookupF32(NULL, arguments, "-sample");
+    data->fileLevel = psMetadataLookupBool(NULL, arguments, "-level");
+    data->fileView = NULL;
+
+    // Open the input file, determine the camera
+    int result = PS_EXIT_UNKNOWN_ERROR;
+    {
+        psString resolved = pmConfigConvertFilename(inName, config, false, false); // Resolved filename
+        data->fits = psFitsOpen(resolved, "r");
+        if (!data->fits) {
+            psError(PS_ERR_IO, false, "Unable to open input file %s\n", resolved);
+            psFree(resolved);
+            result = PS_EXIT_DATA_ERROR;
+            goto die;
+        }
+        psFree(resolved);
+
+        psMetadata *header = psFitsReadHeader(NULL, data->fits); // The FITS (primary) header
+        psMetadata *format = pmConfigCameraFormatFromHeader(NULL, NULL, config, header, true);
+        if (!format) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine camera format for %s\n", inName);
+            psFree(header);
+            result = PS_EXIT_DATA_ERROR;
+            goto die;
+        }
+
+        // need to handle optional alternate EXTWORD value
+        psMetadata *fileMenu = psMetadataLookupMetadata (NULL, format, "FILE");
+        if (!fileMenu) {
+            psError (PS_ERR_IO, true, "FILE METADATA missing from camera format\n");
+            return false;
+        }
+        bool status = false;
+        char *extword = psMetadataLookupStr (&status, fileMenu, "EXTWORD");
+        if (status) {
+            psFitsSetExtnameWord (data->fits, extword);
+        }
+
+        data->fpa = pmFPAConstruct(config->camera, config->cameraName);
+        if (!data->fpa) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to construct FPA for %s\n", resolved);
+            psFree(header);
+            psFree(format);
+            result = PS_EXIT_CONFIG_ERROR;
+            goto die;
+        }
+        data->fileView = pmFPAAddSourceFromHeader(data->fpa, header, format);
+        psFree(header);
+        psFree(format);
+        if (!data->fileView) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to add input file %s to FPA.\n", inName);
+            result = PS_EXIT_CONFIG_ERROR;
+            goto die;
+        }
+    }
+
+    // Get the rest from the recipe
+    if (!ppStatsSetupFromRecipe(data, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read ppStats options from recipe.");
+        psFree(data);
+        return NULL;
+    }
+
+    // Print out what we're going to do
+    if (psTraceGetLevel("ppStats") > 9) {
+        checkList(data->chips, "CHIP");
+        checkList(data->cells, "CELL");
+        checkList(data->headers, "HEADER");
+        checkList(data->concepts, "CONCEPT");
+    }
+
+    return data;
+
+    // Common path for error conditions: clean up and exit.
+die:
+    psErrorStackPrint(stderr, "Unable to set ppStats parameters from command-line arguments");
+    psFree(config);
+    psFree(data);
+    pmConceptsDone();
+    pmConfigDone();
+    psLibFinalize();
+    exit(result);
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsSetupFromRecipe.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsSetupFromRecipe.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsSetupFromRecipe.c	(revision 22322)
@@ -0,0 +1,145 @@
+#include "ppStatsInternal.h"
+
+// Strings in a recipe may be defined multiply (with MULTI) or listed on a single line
+static void listFromRecipe(psList *target, // The target list
+                           const psMetadata *recipe, // Recipe to search
+                           const char *name // Name for item within recipe
+    )
+{
+    // If the list already has entries, don't read anything else
+    if (psListLength(target) > 0) {
+        return;
+    }
+
+    // First check that at least one item of interest exists
+    psMetadataItem *checkItem = psMetadataLookup(recipe, name);
+    if (!checkItem) {
+        // Nothing to see here
+        return;
+    }
+
+    psString regex = NULL;              // Regular expression for the flag
+    psStringAppend(&regex, "^%s$", name);
+    psMetadataIterator *iterator = psMetadataIteratorAlloc(recipe, PS_LIST_HEAD, regex);
+    psFree(regex);
+    psMetadataItem *item;
+    int numItem = 0; // Occurrence of the item in the recipe; to help the user in case of trouble
+    while ((item = psMetadataGetAndIncrement(iterator))) {
+        numItem++;
+        if (item->type != PS_DATA_STRING) {
+            psLogMsg(__func__, PS_LOG_WARN, "Occurrence %d of %s in the recipe is "
+                     "not of type STRING (%x) --- ignored.\n", numItem, name, item->type);
+            continue;
+        }
+        // Parse into a list of independent values
+        psList *values = psStringSplit(item->data.V, " ,;", false);
+        // Copy into the target
+        psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
+        psString valueString;
+        while ((valueString = psListGetAndIncrement(valuesIter))) {
+            // Remove the default _UNDEF entries in the recipe, which come from the limitation of the metadata
+            // config language to have an empty MULTI, combined with the constraint that higher-level recipes
+            // aren't allowed to add new values but only change what already exists.
+            if (strcmp(valueString, "_UNDEF") != 0) {
+                psListAdd(target, PS_LIST_TAIL, valueString);
+            }
+        }
+        psFree(valuesIter);
+        psFree(values);
+    }
+    psFree(iterator);
+
+    return;
+}
+
+
+bool ppStatsSetupFromRecipe(ppStatsData *data, // Data for running ppStats
+                            pmConfig *config // Configuration
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(data, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // Determine recipe parameters
+    bool mdok;                          // Status of MD lookup
+    psMetadata *recipe = psMetadataLookupMetadata(&mdok, config->recipes, PPSTATS_RECIPE);
+    if (!mdok || !recipe) {
+        psLogMsg(__func__, PS_LOG_WARN, "Unable to find recipe %s.\n", PPSTATS_RECIPE);
+        return data;
+    }
+
+    listFromRecipe(data->chips,    recipe, "CHIP");
+    listFromRecipe(data->cells,    recipe, "CELL");
+    listFromRecipe(data->headers,  recipe, "HEADER");
+    listFromRecipe(data->concepts, recipe, "CONCEPT");
+    listFromRecipe(data->analysis, recipe, "ANALYSIS");
+
+    // Parse SUMMARY statistics information
+    listFromRecipe(data->summary, recipe, "SUMMARY");
+
+    // Parse the statistics options
+    psList *recipeStats = psListAlloc(NULL); // List of statistics options
+    listFromRecipe(recipeStats, recipe, "STAT");
+
+    // validate STATs choices
+    if (psListLength(recipeStats) > 0) {
+        psListIterator *iterator = psListIteratorAlloc(recipeStats, PS_LIST_HEAD, false);
+        psString statString;            // Statistic string, from iteration
+        while ((statString = psListGetAndIncrement(iterator))) {
+            psStatsOptions stat = psStatsOptionFromString(statString);
+            if (stat == 0) {
+                psLogMsg(__func__, PS_LOG_WARN, "Can't interpret STATS entry in recipe: "
+                         "%s --- ignored.\n", statString);
+                continue;
+            }
+            data->stats->options |= stat;
+            data->doStats = true;
+        }
+    }
+    psFree(recipeStats);
+
+    // Clipping options
+    if (data->stats->clipIter == 0 && isnan(data->stats->clipSigma)) {
+        int iter = psMetadataLookupS32(&mdok, recipe, "ITER"); // Number of clipping iterations
+        if (mdok && iter > 0) {
+            data->stats->clipIter = iter;
+        } else {
+            psLogMsg(__func__, PS_LOG_WARN, "ITER in recipe is not of type S32 and positive --- "
+                     "retaining default.\n");
+        }
+        float rej = psMetadataLookupF32(&mdok, recipe, "REJ"); // Clipping level
+        if (mdok && rej > 0) {
+            data->stats->clipSigma = rej;
+        } else {
+            psLogMsg(__func__, PS_LOG_WARN, "REJ in recipe is not of type F32 and positive --- "
+                     "retaining default.\n");
+        }
+    }
+
+    if (data->sample == 0) {
+        float sample = psMetadataLookupF32(&mdok, recipe, "SAMPLE"); // Sample fraction
+        if (mdok && sample > 0) {
+            data->sample = sample;
+        } else {
+            psLogMsg(__func__, PS_LOG_WARN, "SAMPLE in recipe is not of type F32 and positive --- "
+                     "retaining default.\n");
+        }
+    }
+
+    bool doFirst = psMetadataLookupBool(&mdok, recipe, "DO.FIRST.READOUT.3D"); // Sample fraction
+    if (mdok) {
+	data->doFirstReadout3D = doFirst;
+    } 
+
+    // set the mask value used for stand-alone analyses.
+    if (data->maskVal == 0) {
+        const char *names = psMetadataLookupStr(&mdok, recipe, "MASKVAL"); // Names for mask value
+        if (mdok) {
+            data->maskVal = pmConfigMaskGet(names, config);
+        } else {
+            psWarning("MASKVAL in recipe is not of type STR --- retaining default.\n");
+        }
+    }
+
+    return data;
+}
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsUtils.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "ppStatsInternal.h"
+
+void p_ppStatsGetMetadata(psMetadata *target, // Target for metadata
+                          psMetadata *source, // Source for metadata
+                          psList *list    // List containing keywords
+    )
+{
+    assert(target);
+    assert(list);
+    if (!source) {
+        // Nothing to get from!
+        return;
+    }
+
+    psListIterator *iterator = psListIteratorAlloc(list, PS_LIST_HEAD, false); // Iterator
+    psString name;                      // Name from iteration
+    while ((name = psListGetAndIncrement(iterator))) {
+        psMetadataItem *item = psMetadataLookup(source, name); // Item of interest, or NULL
+        if (item) {
+            psMetadataAddItem(target, item, PS_LIST_TAIL, PS_META_REPLACE);
+        }
+    }
+    psFree(iterator);
+    return;
+}
+
+void p_ppStatsGetAnalysis(psMetadata *target, // Output Target for metadata
+                          psList *headers,    // List containing desired keywords
+                          psMetadata *source, // Input Source for metadata
+                          psList *list        // List containing analysis blocks
+    )
+{
+    bool status;
+
+    psListIterator *iterator = psListIteratorAlloc(list, PS_LIST_HEAD, false); // Iterator
+    psString name;                      // Name from iteration
+    while ((name = psListGetAndIncrement(iterator))) {
+        psMetadata *folder = psMetadataLookupMetadata(&status, source, name); // Item of interest, or NULL
+        if (folder) {
+            p_ppStatsGetMetadata (target, folder, headers);
+        }
+    }
+    psFree(iterator);
+    return;
+}
+
+bool p_ppStatsDoThis(psList *toDoList,    // List of things to do
+                     const char *this     // The name of "this"
+    )
+{
+    if (psListLength(toDoList) == 0) {
+        // No list --- do everything
+        return true;
+    }
+
+    psListIterator *iterator = psListIteratorAlloc(toDoList, PS_LIST_HEAD, false); // Iterator
+    psString test;                      // Test string, from iteration
+    while ((test = psListGetAndIncrement(iterator))) {
+        if (strcmp(this, test) == 0) {
+            // It's in the list --- do it
+            psFree(iterator);
+            return true;
+        }
+    }
+    psFree(iterator);
+    // Couldn't find it --- don't do it
+    return false;
+}
+
+void p_ppStatsAddToHierarchy(psMetadata *source, // Source to add
+                             psMetadata *target, // Target to which to add
+                             const char *name, // Name of source
+                             const char *comment // Comment for source
+    )
+{
+    if (psListLength(source->list) > 0 && !psMetadataLookup(target, name)) {
+        psMetadataAdd(target, PS_LIST_TAIL, name, PS_DATA_METADATA | PS_META_REPLACE,
+                      comment, source);
+    }
+    return;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppStats/src/ppStatsVersion.c	(revision 22322)
@@ -0,0 +1,19 @@
+#include "ppStatsInternal.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString ppStatsVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString ppStatsVersionLong(void)
+{
+    psString version = ppStatsVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSub/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/.cvsignore	(revision 22322)
@@ -0,0 +1,17 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+missing
+config.guess
+libtool
+ltmain.sh
+stamp-h1
+config.sub
+test
Index: /tags/sj_tags/sj_root_20080929/ppSub/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *.pyc *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/ppSub/TO_DO
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/TO_DO	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/TO_DO	(revision 22322)
@@ -0,0 +1,177 @@
+Some ideas for improving the image subtraction
+==============================================
+
+Paul Price
+15 August 2007
+
+
+1. Dual convolution
+
+The Alard & Lupton (1998) and Alard (2000) algorithms are unable to
+produce useful subtractions when the PSFs of the input and reference
+images have misaligned position angles (e.g., / vs \), since this
+would involve a deconvolution along one of the axes, which does not
+work well.  One way to solve this problem is through a dual
+convolution.  Instead of solving
+
+	I(x,y) = k(u,v) * R(x,y)
+
+for k(u,v), we can solve
+
+	k1(u,v) * I(x,y) = k2(u,v) * R(x,y)
+
+for k1(u,v) and k2(u,v).  If we write
+
+      k1(u,v) = sum_i a_i f_i(u,v)
+
+and
+
+      k2(u,v) = sum_i b_i g_i(u,v)
+
+where f_i(u,v) and g_i(u,v) are sets of basis functions, then the
+solution boils down to two equations:
+
+      sum_j a_j A_j A_i = sum_j b_j B_j A_i
+
+and
+
+      sum_j a_j A_j B_i = sum_j b_j B_j B_i
+
+where
+
+      A_i = sum_x,y f_i(u,v) * I(x,y) / sigma(x,y)
+
+and
+
+      B_i = sum_x,y g_i(u,v) * R(x,y) / sigma(x,y)
+
+Noting that the matrix terms of the RHS of the first equation and the
+LHS of the second equation (A_i B_j) are the same leads us to attempt
+to solve this system by iteration:
+
+  1. Set a_i = 1 if i = 0, otherwise a_i = 0; f_0 = delta(u,v); and
+     solve for b in the first equation.  This corresponds to doing the
+     usual Alard & Lupton solution.
+
+  2. Use b in the second equation, and solve for a.
+
+  3. Use a in the first equation, and solve for b.
+
+  4. Proceed in this manner until the change in b between iterations
+     is small.
+
+This is basically doing A&L to get the reference image to match the
+input image in the usual manner, then doing A&L on the input image to
+match the convolved reference image, then doing A&L on the reference
+image to match the convolved input image, etc., until the system
+settles down to the solution.  A more refined method of solving the
+equations may exist, but this should work.
+
+There is a need to normalise one of the kernels: the solution is
+currently non-unique because a and b can be scaled by some arbitrary
+value.  If we write
+
+      k1(u,v) = delta(u,v) + sum_i a_i [k_i - delta(u,v)]
+
+and the k_i are normalised to a sum of unity, then
+
+      sum_u,v k1(u,v) = 1.
+
+So k1 has a sum of unity, always.  This means that k2 supplies the
+scaling to match the photometry (as happens in Alard & Lupton), and k1
+is merely used to broaden the input image as required for the best
+subtraction.
+
+Note that the expense for this method over the usual Alard & Lupton
+roughly amounts to the extra image convolution, since that is
+typically the dominant factor.  Accumulation of the sums is not much
+more than Alard & Lupton, and the iteration should be fairly fast.
+
+
+2. Data-based kernel selection
+
+The quality of the subtraction is highly sensitive to the choice of
+basis functions.  In the case of ISIS kernels (which seem to be the
+most useful of those experimented with so far, because it takes a
+small number of parameters to generate a large kernel), the choice of
+the Gaussian widths is very important.  Some recipes exist for
+choosing these widths, but these are generally motivated by experience
+(through much trial and error) rather than directly from the data.  It
+would be nice to be able to throw down a large number of widths and
+allow the least-squares solution to choose the best for the data at
+hand (i.e., the most dominant contributors), but this is prohibitively
+expensive --- at least for a full solution.
+
+What might be possible is to do a quick and dirty solution by reducing
+the dimensionality.  Instead of working with the full two-dimensional
+kernel, k(u,v), let's work in one dimension, k(u).  For each of our
+stamps, let's take a cut through them in a consistent direction (e.g.,
+the x direction), and solve
+
+	I(x) = k(u) * R(x)
+
+This is not nearly as computationally expensive as the full solution,
+so we can pack k(u) with multiple Gaussian widths, solve the
+least-squares problem, and identify the most important kernel
+contributions which we will use (in suitable two-dimensional versions)
+in solving the full problem.  This method could even be applied with
+multiple direction cuts to ensure the full range of required Gaussian
+widths is obtained.
+
+Fitting the normalisation of a single kernel component, k(u) and the
+background, we obtain the least-squares matrix and vector:
+
+M = ( sum_x C(x)^2/sigma(x)^2    sum_x C(x)/sigma(x)^2 )
+    ( sum_x C(x)/sigma(x)^2      sum_x 1/sigma(x)^2    )
+
+v = ( sum_x I(x)C(x)/sigma(x)^2   sum_x I(x)/sigma(x)^2 )
+
+where C(x) = R(x) * k(u)
+
+Inverting the matrix, multiplying by the vector and taking the
+component corresponding to the kernel normalisation, we get:
+
+f = (ab - cd) / (ae - c^2)
+
+where
+
+a = sum_x 1/sigma(x)^2
+b = sum_x I(x)C(x)/sigma(x)^2
+c = sum_x C(x)/sigma(x)^2
+d = sum_x I(x)/simga(x)^2
+e = sum_x C(x)^2/sigma(x)^2
+
+Note that a and d are independent of the kernel, and so may be
+measured once only.  Note that we are ignoring any spatial variation
+of the kernel here --- we are only interested in which kernel
+components should be in the final solution, not the details of that
+solution.
+
+Assuming that the kernel components are normalised, the kernel
+component normalisation, f, is a measure of how important that kernel
+component is in the final solution of the full problem.  The following
+algorithm is suggested:
+
+(a) For each original kernel component, extract a vector at 0, 45 and
+    90 degrees (x, y and x-y axes).  Ignore one of these sub-kernels
+    in what follows if the standard deviation of the subkernel is
+    zero.  We use these three extraction angles to cover the u, v and
+    uv terms.
+
+(b) For each stamp, extract a vector at 0, 45, and 90 degrees (x, y,
+    and x-y axes).  These sub-stamps go with the corresponding
+    sub-kernel.
+
+(c) For each kernel component, measure b, c, e and therefore determine
+    the kernel normalisation, f.  Use the sub-kernel appropriate for
+    the sub-stamp (matched by extraction angle), and accumulate the
+    values together (sub-kernels all contribute to the same original
+    kernel component).
+
+(d) Take the kernel component with the largest |f| as the 'winner' of
+    this iteration.  Apply this kernel to the sub-stamps for R(x), and
+    mark this kernel component in the list.  It is part of the end
+    solution, but should not be used any more in the iteration.
+
+(e) Repeat from step (c) until the largest |f| is small, say 10^-3 of
+    the first obtained value of |f|.
Index: /tags/sj_tags/sj_root_20080929/ppSub/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=ppSub
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE  failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/ppSub/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/configure.ac	(revision 22322)
@@ -0,0 +1,37 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.61)
+
+AC_INIT([ppSub], [0.1.1], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+PKG_CHECK_MODULES([PPSTATS], [ppStats >= 1.0.0]) 
+PKG_CHECK_MODULES([PSPHOT], [psphot >= 0.9.0]) 
+
+dnl Set CFLAGS for build
+IPP_STDOPTS
+CFLAGS="${CFLAGS} -Wall -Werror"
+
+AC_SUBST([PPSUB_CFLAGS])
+AC_SUBST([PPSUB_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/ppSub/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/src/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+*.o
+*.lo
+.libs
+.deps
+Makefile
+Makefile.in
+ppSub
+config.h
+config.h.in
+stamp-h1
Index: /tags/sj_tags/sj_root_20080929/ppSub/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/src/Makefile.am	(revision 22322)
@@ -0,0 +1,22 @@
+bin_PROGRAMS = ppSub
+ppSub_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPSUB_CFLAGS)
+ppSub_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPSTATS_LIBS)   $(PSPHOT_LIBS)   $(PPSUB_LIBS)
+
+ppSub_SOURCES =			\
+	ppSub.c			\
+	ppSubArguments.c	\
+	ppSubCamera.c		\
+	ppSubLoop.c		\
+	ppSubReadout.c		\
+	ppSubVersion.c            
+
+noinst_HEADERS = \
+	ppSub.h
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
Index: /tags/sj_tags/sj_root_20080929/ppSub/src/ppSub.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/src/ppSub.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/src/ppSub.c	(revision 22322)
@@ -0,0 +1,64 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppSub.h"
+
+int main(int argc, char *argv[])
+{
+    psExit exitValue = PS_EXIT_SUCCESS; // Exit value
+    psTimerStart("ppSub");
+    psLibInit(NULL);
+
+    pmConfig *config = pmConfigRead(&argc, argv, PPSUB_RECIPE); // Configuration
+    if (!config) {
+        psErrorStackPrint(stderr, "Error reading configuration.");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!pmModelClassInit()) {
+        psErrorStackPrint(stderr, "Error initialising model classes.\n");
+        exitValue = PS_EXIT_PROG_ERROR;
+        goto die;
+    }
+
+    if (!ppSubArgumentsSetup(argc, argv, config)) {
+        psErrorStackPrint(stderr, "Error reading arguments.\n");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!ppSubCamera(config)) {
+        psErrorStackPrint(stderr, "Error setting up camera.\n");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!ppSubArgumentsParse(config)) {
+        psErrorStackPrint(stderr, "Error reading arguments.\n");
+        exitValue = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!ppSubLoop(config)) {
+        psErrorStackPrint(stderr, "Error performing subtraction.\n");
+        exitValue = PS_EXIT_SYS_ERROR;
+        goto die;
+    }
+
+ die:
+    psTrace("ppSub", 1, "Finished at %f sec\n", psTimerMark("ppSub"));
+    psTimerStop();
+
+    psFree(config);
+    pmModelClassCleanup();
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exitValue);
+}
Index: /tags/sj_tags/sj_root_20080929/ppSub/src/ppSub.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/src/ppSub.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/src/ppSub.h	(revision 22322)
@@ -0,0 +1,34 @@
+#ifndef PP_SUB_H
+#define PP_SUB_H
+
+#define PPSUB_RECIPE "PPSUB"            /// Name of the recipe to use
+
+/// Setup the arguments parsing
+bool ppSubArgumentsSetup(int argc, char *argv[], ///< Command-line arguments
+                    pmConfig *config    ///< Configuration
+    );
+
+/// Parse the arguments
+bool ppSubArgumentsParse(pmConfig *config ///< Configuration
+    );
+
+/// Parse the camera input
+bool ppSubCamera(pmConfig *config       ///< Configuration
+    );
+
+/// Loop over the FPA hierarchy
+bool ppSubLoop(pmConfig *config         ///< Configuration
+    );
+
+/// Perform PSF-matched image subtraction on the readout
+bool ppSubReadout(pmConfig *config,     ///< Configuration
+                  psMetadata *stats,    ///< Statistics, for output
+                  const pmFPAview *view ///< View of readout to subtract
+    );
+
+/// Put the program version information into a metadata
+void ppSubVersionMetadata(psMetadata *metadata ///< Metadata to populate
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubArguments.c	(revision 22322)
@@ -0,0 +1,366 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppSub.h"
+
+// Print usage information and die
+static void usage(const char *program,  // Name of the program
+                  psMetadata *arguments, // Command-line arguments
+                  pmConfig *config      // Configuration
+    )
+{
+    fprintf(stderr, "\nPan-STARRS PSF-matched image subtraction\n\n");
+    fprintf(stderr, "Usage: %s INPUT.fits REFERENCE.fits OUTPUT_ROOT \n"
+            "\t[-psf REFERENCE.psf.fits] [-sources REFERENCE.cmf]\n\n"
+            "This subtracts the convolved REFERENCE from the INPUT, by default.\n",
+            program);
+    fprintf(stderr, "\n");
+    psArgumentHelp(arguments);
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+    exit(PS_EXIT_CONFIG_ERROR);
+}
+
+// Get a float-point value from the command-line or recipe, and add it to the arguments
+#define VALUE_ARG_RECIPE_FLOAT(ARGNAME, RECIPENAME, TYPE) { \
+    ps##TYPE value = psMetadataLookup##TYPE(NULL, config->arguments, ARGNAME); \
+    if (isnan(value)) { \
+        bool mdok; \
+        value = psMetadataLookup##TYPE(&mdok, recipe, RECIPENAME); \
+        if (!mdok) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
+                RECIPENAME, PPSUB_RECIPE); \
+            goto ERROR; \
+        } \
+    } \
+    psMetadataAdd##TYPE(recipe, PS_LIST_TAIL, RECIPENAME, PS_META_REPLACE, NULL, value); \
+}
+
+// Get an integer value from the command-line or recipe, and add it to the arguments
+#define VALUE_ARG_RECIPE_INT(ARGNAME, RECIPENAME, TYPE, UNSET) { \
+    ps##TYPE value = psMetadataLookup##TYPE(NULL, config->arguments, ARGNAME); \
+    if (value == UNSET) { \
+        bool mdok; \
+        value = psMetadataLookup##TYPE(&mdok, recipe, RECIPENAME); \
+        if (!mdok) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
+                RECIPENAME, PPSUB_RECIPE); \
+            goto ERROR; \
+        } \
+    } \
+    psMetadataAdd##TYPE(recipe, PS_LIST_TAIL, RECIPENAME, PS_META_REPLACE, NULL, value); \
+}
+
+// Get a string value from the command-line and add it to the target
+static bool valueArgStr(psMetadata *arguments, // Command-line arguments
+                        const char *argName, // Argument name in the command-line arguments
+                        const char *mdName, // Name for value in the metadata
+                        psMetadata *target // Target metadata to which to add value
+                        )
+{
+    psString value = psMetadataLookupStr(NULL, arguments, argName); // Value of interest
+    if (value && strlen(value) > 0) {
+        return psMetadataAddStr(target, PS_LIST_TAIL, mdName, PS_META_REPLACE, NULL, value);
+    }
+    return false;
+}
+
+// Get a string value from the command-line or recipe and add it to the target
+static bool valueArgRecipeStr(psMetadata *arguments, // Command-line arguments
+                              psMetadata *recipe, // Recipe
+                              const char *argName, // Argument name in the command-line arguments
+                              const char *mdName, // Name for value in the metadata and recipe
+                              psMetadata *target // Target metadata to which to add value
+                              )
+{
+    bool mdok;                          // Status of MD lookup
+    psString value = psMetadataLookupStr(&mdok, arguments, argName); // Value of interest
+    if (!mdok) {
+        value = psMetadataLookupStr(&mdok, recipe, mdName);
+        if (!mdok) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s",
+                    mdName, PPSUB_RECIPE);
+            return false;
+        }
+    }
+    return psMetadataAddStr(target, PS_LIST_TAIL, mdName, PS_META_REPLACE, NULL, value);
+}
+
+// Get a vector from the command-line or recipe, and add it to the target
+static bool vectorArgRecipe(psMetadata *arguments, // Command-line arguments
+                            const char *argName, // Argument name in the command-line arguments
+                            const psMetadata *recipe, // Recipe
+                            const char *recipeName, // Name for value in the recipe
+                            psMetadata *target, // Target to which to add value
+                            psElemType type // Type for vector
+    )
+{
+    psVector *vector;                   // Vector
+    psString string = psMetadataLookupStr(NULL, arguments, argName); // String from arguments
+    if (string) {
+        psArray *array = psStringSplitArray(string, ", ", false); // Array of strings
+        vector = psVectorAlloc(array->n, type);
+        for (int i = 0; i < array->n; i++) {
+            const char *subString = array->data[i]; // String with a value
+            char *end;                  // Ptr to end of string parsed
+
+            switch (type) {
+              case PS_TYPE_F32:
+                vector->data.F32[i] = strtof(subString, &end);
+                break;
+              case PS_TYPE_S32: {
+                  long value = strtol(subString, &end, 10);
+                  if (value > PS_MAX_S32 || value < PS_MIN_S32) {
+                      psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                              "%s list includes value beyond S32 representation: %s",
+                              argName, string);
+                      psFree(vector);
+                      return false;
+                  }
+                  vector->data.S32[i] = value;
+                  break;
+              }
+              default:
+                psAbort("Unsupported type: %x\n", type);
+            }
+            if (end == subString) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to decipher %s list: %s",
+                        argName, string);
+                psFree(vector);
+                return false;
+            }
+        }
+        psFree(array);
+    } else {
+        vector = psMetadataLookupPtr(NULL, recipe, recipeName);
+        if (!psMemCheckVector(vector) || vector->type.type != type) {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, false, "%s in recipe %s is not a vector of type F32.",
+                    recipeName, PPSUB_RECIPE);
+            return false;
+        }
+        psMemIncrRefCounter(vector);
+    }
+
+    psMetadataAddVector(target, PS_LIST_TAIL, recipeName, PS_META_REPLACE, NULL, vector);
+    psFree(vector);                     // Drop reference
+
+    return true;
+}
+
+// Add a single filename to the arguments as an array, so that it can be used with pmFPAfileBindFromArgs, etc
+static void fileList(const char *file, // The symbolic name for the file
+                     const char *name, // The name of the file
+                     const char *comment, // Description of the file
+                     pmConfig *config // Configuration
+    )
+{
+    psArray *files = psArrayAlloc(1); // Array with file names
+    files->data[0] = psStringCopy(name);
+    psMetadataAddArray(config->arguments, PS_LIST_TAIL, file, 0, comment, files);
+    psFree(files);
+    return;
+}
+
+bool ppSubArgumentsSetup(int argc, char *argv[], pmConfig *config)
+{
+    assert(config);
+
+    pmConfigFileSetsMD(config->arguments, &argc, argv, "PPSUB.SOURCES", "-sources", NULL);
+
+    psMetadata *arguments = config->arguments; // Command-line arguments
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-inmask", 0, "Input mask image", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-inweight", 0, "Input weight image", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-refmask", 0, "Referencemask image", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-refweight", 0, "Referenceweight image", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stats", 0, "Statistics file", NULL);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-region", 0, "Size of iso-kernel region", NAN);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-size", 0, "Kernel half-size (pixels)", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-order", 0, "Spatial polynomial order", -1);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-type", 0,
+                     "Kernel type (ISIS|POIS|SPAM|FRIES|GUNK|RINGS)", NULL);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-penalty", 0, "Penalty for wideness", NAN);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-isis-widths", 0,
+                     "ISIS Gaussian FWHMs (comma-separated)", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-isis-orders", 0,
+                     "ISIS polynomial orders (comma-separated)", NULL);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-rings-order", 0, "RINGS polynomial order", -1);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-inner", 0, "SPAM and FRIES inner radius", -1);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-spam-binning", 0, "SPAM kernel binning", 0);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-spacing", 0, "Typical stamp spacing (pixels)", NAN);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-footprint", 0, "Stamp footprint half-size (pixels)", -1);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-threshold", 0, "Minimum threshold for stamps (ADU)", NAN);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-iter", 0, "Number of rejection iterations", -1);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-rej", 0, "Rejection thresold (sigma)", NAN);
+    psMetadataAddU8(arguments,  PS_LIST_TAIL, "-mask-bad", 0, "Mask value for bad pixels", 0);
+    psMetadataAddU8(arguments,  PS_LIST_TAIL, "-mask-poor", 0, "Mask value for poor pixels", 0);
+    psMetadataAddF32(arguments,  PS_LIST_TAIL, "-poor-frac", 0, "Fraction of weight for poor pixels", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-badfrac", 0, "Maximum fraction of bad pixels to accept", 1.0);
+    psMetadataAddBool(arguments,  PS_LIST_TAIL, "-reverse", 0, "Reverse sense of subtraction?", false);
+    psMetadataAddBool(arguments,  PS_LIST_TAIL, "-generate-mask", 0, "Generate mask if not supplied?", false);
+    psMetadataAddStr(arguments,  PS_LIST_TAIL, "-stamps", 0,
+                     "Stamps filename; file has x,y on each line", NULL);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-opt", 0,
+                      "Derive optimum parameters for ISIS kernels?", false);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-opt-min", 0, "Minimum value for optimum kernel search", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-opt-max", 0, "Minimum value for optimum kernel search", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-opt-step", 0, "Step value for optimum kernel search", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-opt-tol", 0, "Tolerance for optimum kernel search", NAN);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-opt-order", 0, "Maximum order for optimum kernel search", -1);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-dual", 0, "Dual convolution", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-renorm", 0, "Renormalise weights?", false);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-renorm-width", 0, "Renormalisation width", 0);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-photometry", 0, "Perform photometry?", false);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-threads", 0, "Number of threads", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-bin1", 0, "Binning factor for first level", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-bin2", 0, "Binning factor for second level", 0);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dumpconfig", 0, "file to dump configuration to", NULL);
+
+    if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 4) {
+        usage(argv[0], arguments, config);
+    }
+
+    fileList("INPUT", argv[1], "Name of the input image",     config);
+    fileList("REF",   argv[2], "Name of the reference image", config);
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[3]);
+
+    const char *inMask = psMetadataLookupStr(NULL, arguments, "-inmask"); // Name of input mask
+    if (inMask && strlen(inMask) > 0) {
+        fileList("INPUT.MASK", inMask, "Name of the input mask image", config);
+    }
+    const char *inWeight = psMetadataLookupStr(NULL, arguments, "-inweight"); // Name of input weight
+    if (inWeight && strlen(inWeight) > 0) {
+        fileList("INPUT.WEIGHT", inWeight, "Name of the input weight image", config);
+    }
+
+    const char *refMask = psMetadataLookupStr(NULL, arguments, "-refmask"); // Name of reference mask
+    if (refMask && strlen(refMask) > 0) {
+        fileList("REF.MASK", refMask, "Name of the reference mask image", config);
+    }
+    const char *refWeight = psMetadataLookupStr(NULL, arguments, "-refweight"); // Name of ref weight
+    if (refWeight && strlen(refWeight) > 0) {
+        fileList("REF.WEIGHT", refWeight, "Name of the reference weight image", config);
+    }
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSUB_RECIPE);
+        return false;
+    }
+
+    if (psMetadataLookupBool(NULL, arguments, "-photometry")) {
+        psMetadataAddBool(recipe, PS_LIST_TAIL, "PHOTOMETRY", PS_META_REPLACE, "Perform photometry?", true);
+    }
+
+    return true;
+}
+
+bool ppSubArgumentsParse(pmConfig *config)
+{
+    assert(config);
+
+    psMetadata *arguments = config->arguments; // Command-line arguments
+
+    valueArgStr(arguments, "-stats",  "STATS",  arguments);
+    valueArgStr(arguments, "-stamps", "STAMPS", arguments);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSUB_RECIPE);
+        goto ERROR;
+    }
+
+    VALUE_ARG_RECIPE_FLOAT("-region",     "REGION.SIZE",     F32);
+    VALUE_ARG_RECIPE_INT("-size",         "KERNEL.SIZE",     S32, 0);
+    VALUE_ARG_RECIPE_INT("-order",        "SPATIAL.ORDER",   S32, -1);
+    VALUE_ARG_RECIPE_FLOAT("-spacing",    "STAMP.SPACING",   F32);
+    VALUE_ARG_RECIPE_INT("-rings-order",  "RINGS.ORDER",     S32, -1);
+    VALUE_ARG_RECIPE_INT("-inner",        "INNER",           S32, -1);
+    VALUE_ARG_RECIPE_INT("-spam-binning", "SPAM.BINNING",    S32, 0);
+    VALUE_ARG_RECIPE_INT("-footprint",    "STAMP.FOOTPRINT", S32, -1);
+    VALUE_ARG_RECIPE_FLOAT("-threshold",  "STAMP.THRESHOLD", F32);
+    VALUE_ARG_RECIPE_INT("-iter",         "ITER",            S32, -1);
+    VALUE_ARG_RECIPE_FLOAT("-rej",        "REJ",             F32);
+    VALUE_ARG_RECIPE_FLOAT("-badfrac",    "BADFRAC",         F32);
+    VALUE_ARG_RECIPE_FLOAT("-penalty",    "PENALTY",         F32);
+    VALUE_ARG_RECIPE_FLOAT("-poor-frac",  "POOR.FRACTION",   F32);
+    VALUE_ARG_RECIPE_INT("-bin1",         "BIN1",            S32, 0);
+    VALUE_ARG_RECIPE_INT("-bin2",         "BIN2",            S32, 0);
+
+    valueArgRecipeStr(arguments, recipe, "-mask-in",   "MASK.IN",  recipe);
+    valueArgRecipeStr(arguments, recipe, "-mask-bad",  "MASK.BAD",  recipe);
+    valueArgRecipeStr(arguments, recipe, "-mask-poor", "MASK.POOR", recipe);
+
+    vectorArgRecipe(arguments, "-isis-widths", recipe, "ISIS.WIDTHS", recipe, PS_TYPE_F32);
+    vectorArgRecipe(arguments, "-isis-orders", recipe, "ISIS.ORDERS", recipe, PS_TYPE_S32);
+
+    psVector *widths = psMetadataLookupPtr(NULL, recipe, "ISIS.WIDTHS"); // ISIS Gaussian widths
+    psVector *orders = psMetadataLookupPtr(NULL, recipe, "ISIS.ORDERS"); // ISIS Polynomial orders
+    if (widths->n != orders->n) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Size of vectors for ISIS widths and orders do not match.");
+        goto ERROR;
+    }
+
+    if (psMetadataLookupBool(NULL, arguments, "-opt") || psMetadataLookupBool(NULL, recipe, "OPTIMUM")) {
+        psMetadataAddBool(recipe, PS_LIST_TAIL, "OPTIMUM", PS_META_REPLACE,
+                          "Derive optimum parameters for ISIS kernels?", true);
+        VALUE_ARG_RECIPE_FLOAT("-opt-min", "OPTIMUM.MIN",   F32);
+        VALUE_ARG_RECIPE_FLOAT("-opt-max", "OPTIMUM.MAX",   F32);
+        VALUE_ARG_RECIPE_FLOAT("-opt-step","OPTIMUM.STEP",  F32);
+        VALUE_ARG_RECIPE_FLOAT("-opt-tol", "OPTIMUM.TOL",   F32);
+        VALUE_ARG_RECIPE_INT("-opt-order", "OPTIMUM.ORDER", S32, -1);
+    }
+
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "REVERSE", 0, "Reverse sense of subtraction",
+                      psMetadataLookupBool(NULL, arguments, "-reverse"));
+    psMetadataAddBool(recipe, PS_LIST_TAIL, "MASK.GENERATE", PS_META_REPLACE, "Generate mask if not supplied",
+                      psMetadataLookupBool(NULL, arguments, "-generate-mask"));
+    psMetadataAddBool(recipe, PS_LIST_TAIL, "DUAL", PS_META_REPLACE, "Dual convolution?",
+                      psMetadataLookupBool(NULL, arguments, "-dual"));
+
+    if (psMetadataLookupBool(NULL, arguments, "-renorm") ||
+        psMetadataLookupBool(NULL, recipe, "RENORM")) {
+        psMetadataAddBool(recipe, PS_LIST_TAIL, "RENORM", PS_META_REPLACE, "Renormalise weights?", true);
+        VALUE_ARG_RECIPE_INT("-renorm-width", "RENORM.WIDTH", S32, 0);
+    }
+
+    // Need to update this because it could have been overwritten by the camera's own recipe
+    if (psMetadataLookupBool(NULL, arguments, "-photometry")) {
+        psMetadataAddBool(recipe, PS_LIST_TAIL, "PHOTOMETRY", PS_META_REPLACE, "Perform photometry?", true);
+    }
+
+    // Translate the kernel type
+    psString type = psMetadataLookupStr(NULL, arguments, "-type"); // Name of kernel type
+    if (!type || strlen(type) == 0) {
+        type = psMetadataLookupStr(NULL, recipe, "KERNEL.TYPE");
+        if (!type || strlen(type) == 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find KERNEL.TYPE specified.");
+            goto ERROR;
+        }
+    }
+    psMetadataAddStr(recipe, PS_LIST_TAIL, "KERNEL.TYPE", PS_META_REPLACE, "Type of kernel", type);
+
+    psTrace("ppSub", 1, "Done reading command-line arguments\n");
+
+    // Dump configuration, now that's it's settled
+    psBool status;
+    psString dump_file =  psMetadataLookupStr(&status, config->arguments, "-dumpconfig");
+    if (dump_file) {
+        pmConfigCamerasCull(config, NULL);
+        pmConfigRecipesCull(config, "PPSUB,PPSTATS,PSPHOT,MASKS");
+
+        pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSUB.INPUT"); // Input file
+        pmConfigDump(config, input->fpa, dump_file);
+    }
+
+    return true;
+
+ERROR:
+    return false;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubCamera.c	(revision 22322)
@@ -0,0 +1,211 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <psphot.h>
+
+#include "ppSub.h"
+
+bool ppSubCamera(pmConfig *config)
+{
+    bool status = false;                // Status of definition
+
+    // Input image
+    pmFPAfile *input = pmFPAfileDefineFromArgs(&status, config, "PPSUB.INPUT", "INPUT");
+    if (!status || !input) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from PPSUB.INPUT");
+        return false;
+    }
+    if (input->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPSUB.INPUT is not of type IMAGE");
+        return false;
+    }
+
+    // Input mask
+    pmFPAfile *inputMask = pmFPAfileBindFromArgs(&status, input, config, "PPSUB.INPUT.MASK", "INPUT.MASK");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.INPUT.MASK");
+        return NULL;
+    }
+    if (inputMask && inputMask->type != PM_FPA_FILE_MASK) {
+        psError(PS_ERR_IO, true, "PPSUB.INPUT.MASK is not of type MASK");
+        return false;
+    }
+
+    // Input weight map
+    pmFPAfile *inputWeight = pmFPAfileBindFromArgs(&status, input, config, "PPSUB.INPUT.WEIGHT", "INPUT.WEIGHT");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.INPUT.WEIGHT");
+        return NULL;
+    }
+    if (inputWeight && inputWeight->type != PM_FPA_FILE_WEIGHT) {
+        psError(PS_ERR_IO, true, "PPSUB.INPUT.WEIGHT is not of type WEIGHT");
+        return false;
+    }
+
+    // Reference image
+    status = false;
+    pmFPAfile *ref = pmFPAfileDefineFromArgs(&status, config, "PPSUB.REF", "REF");
+    if (!ref) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from PPSUB.REF");
+        return false;
+    }
+    if (ref->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPSUB.REF is not of type IMAGE");
+        return false;
+    }
+
+    // Reference mask
+    pmFPAfile *refMask = pmFPAfileBindFromArgs(&status, ref, config, "PPSUB.REF.MASK", "REF.MASK");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.REF.MASK");
+        return NULL;
+    }
+    if (refMask && refMask->type != PM_FPA_FILE_MASK) {
+        psError(PS_ERR_IO, true, "PPSUB.REF.MASK is not of type MASK");
+        return false;
+    }
+
+    // Reference weight map
+    pmFPAfile *refWeight = pmFPAfileBindFromArgs(&status, ref, config, "PPSUB.REF.WEIGHT", "REF.WEIGHT");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.REF.WEIGHT");
+        return NULL;
+    }
+    if (refWeight && refWeight->type != PM_FPA_FILE_WEIGHT) {
+        psError(PS_ERR_IO, true, "PPSUB.REF.WEIGHT is not of type WEIGHT");
+        return false;
+    }
+
+    // Output image
+    pmFPAfile *output = pmFPAfileDefineFromFile(config, input, 1, 1, "PPSUB.OUTPUT");
+    if (!output) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT"));
+        return false;
+    }
+    if (output->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPSUB.OUTPUT is not of type IMAGE");
+        return false;
+    }
+    output->save = true;
+
+    // Output mask
+    pmFPAfile *outMask = pmFPAfileDefineOutput(config, output->fpa, "PPSUB.OUTPUT.MASK");
+    if (!outMask) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.MASK"));
+        return false;
+    }
+    if (outMask->type != PM_FPA_FILE_MASK) {
+        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.MASK is not of type MASK");
+        return false;
+    }
+    outMask->save = true;
+
+    // Output weight
+    if (inputWeight && refWeight) {
+        pmFPAfile *outWeight = pmFPAfileDefineOutput(config, output->fpa, "PPSUB.OUTPUT.WEIGHT");
+        if (!outWeight) {
+            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.WEIGHT"));
+            return false;
+        }
+        if (outWeight->type != PM_FPA_FILE_WEIGHT) {
+            psError(PS_ERR_IO, true, "PPSUB.OUTPUT.WEIGHT is not of type WEIGHT");
+            return false;
+        }
+        outWeight->save = true;
+    }
+
+    // Output JPEGs
+    pmFPAfile *jpeg1 = pmFPAfileDefineOutput(config, NULL, "PPSUB.OUTPUT.JPEG1");
+    if (!jpeg1) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.JPEG1"));
+        return false;
+    }
+    if (jpeg1->type != PM_FPA_FILE_JPEG) {
+        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.JPEG1 is not of type JPEG");
+        return false;
+    }
+    jpeg1->save = true;
+    pmFPAfile *jpeg2 = pmFPAfileDefineOutput(config, NULL, "PPSUB.OUTPUT.JPEG2");
+    if (!jpeg2) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.JPEG2"));
+        return false;
+    }
+    if (jpeg2->type != PM_FPA_FILE_JPEG) {
+        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.JPEG2 is not of type JPEG");
+        return false;
+    }
+    jpeg2->save = true;
+
+    // Output subtraction kernel
+    pmFPAfile *outKernels = pmFPAfileDefineOutput(config, output->fpa, "PPSUB.OUTPUT.KERNELS");
+    if (!outKernels) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.KERNELS"));
+        return false;
+    }
+    if (outKernels->type != PM_FPA_FILE_SUBKERNEL) {
+        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.KERNELS is not of type SUBKERNEL");
+        return false;
+    }
+    outKernels->save = true;
+
+#if 0
+    if (!pmFPAAddSourceFromFormat(output->fpa, "Subtraction", output->format)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate output FPA.");
+        return false;
+    }
+#endif
+
+    pmFPAfile *sources = pmFPAfileDefineFromArgs(&status, config, "PPSUB.SOURCES", "PPSUB.SOURCES");
+    if (!status) {
+        psError(PS_ERR_IO, false, "Failed to load file definition PPSUB.SOURCES");
+        return false;
+    }
+    if (sources && sources->type != PM_FPA_FILE_CMF) {
+        psError(PS_ERR_IO, true, "PPSUB.SOURCES is not of type CMF");
+        return false;
+    }
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSUB_RECIPE);
+        return false;
+    }
+
+    // psPhot input
+    if (psMetadataLookupBool(NULL, recipe, "PHOTOMETRY")) {
+        psphotModelClassInit ();        // load implementation-specific models
+        pmFPAfile *psphot = pmFPAfileDefineFromFPA(config, output->fpa, 1, 1, "PSPHOT.INPUT");
+        if (!psphot) {
+            psError(PS_ERR_IO, false, "Failed to build FPA from PSPHOT.INPUT");
+            return false;
+        }
+        if (psphot->type != PM_FPA_FILE_IMAGE) {
+            psError(PS_ERR_IO, true, "PSPHOT.INPUT is not of type IMAGE");
+            return false;
+        }
+
+        if (!psphotDefineFiles(config, psphot)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to set up psphot files.");
+            return false;
+        }
+
+        // Internal-ish file for getting the PSF from the matched addition
+        pmFPAfile *psf = pmFPAfileDefineOutputFromFile(config, output, "PSPHOT.PSF.LOAD");
+        if (!psf) {
+            psError(PS_ERR_IO, false, "Failed to build FPA from PSPHOT.PSF.LOAD");
+            return false;
+        }
+        if (psf->type != PM_FPA_FILE_PSF) {
+            psError(PS_ERR_IO, true, "PSPHOT.PSF.LOAD is not of type PSF");
+            return false;
+        }
+        pmFPAfileActivate(config->files, false, "PSPHOT.PSF.LOAD");
+
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubKernel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubKernel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubKernel.c	(revision 22322)
@@ -0,0 +1,116 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#define KERNEL_MOSAIC 2                 // Half-number of kernel instances in the mosaic image
+
+
+int main(int argc, char *argv[])
+{
+    if (argc != 3) {
+        fprintf(stderr, "Usage: %s INPUT_KERNEL OUTPUT_IMAGE\n\n", argv[0]);
+        exit(PS_EXIT_SYS_ERROR);
+    }
+
+    const char *inName = argv[1];       // Input file name
+    const char *outName = argv[2];      // Output file name
+
+    psFits *inFile = psFitsOpen(inName, "r"); // Input file
+    if (!inFile) {
+        psErrorStackPrint(stderr, "Unable to open input kernel %s", inName);
+        exit(PS_EXIT_DATA_ERROR);
+    }
+
+    pmReadout *ro = pmReadoutAlloc(NULL); // Readout to hold kernel
+    if (!pmReadoutReadSubtractionKernels(ro, inFile)) {
+        psErrorStackPrint(stderr, "Unable to read input kernel");
+        psFree(ro);
+        psFitsClose(inFile);
+        exit(PS_EXIT_DATA_ERROR);
+    }
+    psFitsClose(inFile);
+
+    pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, ro->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL);
+    if (!psMemIncrRefCounter(kernels)) {
+        psErrorStackPrint(stderr, "Unable to find input kernel");
+        psFree(ro);
+        exit(PS_EXIT_PROG_ERROR);
+    }
+    psFree(ro);
+
+    pmSubtractionMode mode = kernels->mode; // Mode for subtraction
+    int fullSize = 2 * kernels->size + 1 + 1;    // Full size of kernel
+    int imageSize = (2 * KERNEL_MOSAIC + 1) * fullSize; // Size of image
+    psImage *image = psImageAlloc((mode == PM_SUBTRACTION_MODE_DUAL ? 2 : 1) * imageSize - 1 +
+                                  (mode == PM_SUBTRACTION_MODE_DUAL ? 4 : 0), imageSize - 1, PS_TYPE_F32);
+    psImageInit(image, NAN);
+    for (int j = -KERNEL_MOSAIC; j <= KERNEL_MOSAIC; j++) {
+        for (int i = -KERNEL_MOSAIC; i <= KERNEL_MOSAIC; i++) {
+            psImage *kernel = pmSubtractionKernelImage(kernels, (float)i / (float)KERNEL_MOSAIC,
+                                                       (float)j / (float)KERNEL_MOSAIC,
+                                                       false); // Image of the kernel
+            if (!kernel) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
+                psFree(image);
+                psFree(kernel);
+                psFree(kernels);
+                exit(PS_EXIT_SYS_ERROR);
+            }
+
+            if (psImageOverlaySection(image, kernel, (i + KERNEL_MOSAIC) * fullSize,
+                                      (j + KERNEL_MOSAIC) * fullSize, "=") == 0) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to overlay kernel image.");
+                psFree(kernel);
+                psFree(image);
+                psFree(kernels);
+                exit(PS_EXIT_PROG_ERROR);
+            }
+            psFree(kernel);
+
+            if (mode == PM_SUBTRACTION_MODE_DUAL) {
+                kernel = pmSubtractionKernelImage(kernels, (float)i / (float)KERNEL_MOSAIC,
+                                                  (float)j / (float)KERNEL_MOSAIC,
+                                                  true); // Image of the kernel
+                if (!kernel) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
+                    psFree(image);
+                    psFree(kernels);
+                    exit(PS_EXIT_SYS_ERROR);
+                }
+
+                if (psImageOverlaySection(image, kernel,
+                                          (2 * KERNEL_MOSAIC + 1 + i + KERNEL_MOSAIC) * fullSize + 4,
+                                          (j + KERNEL_MOSAIC) * fullSize, "=") == 0) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to overlay kernel image.");
+                    psFree(kernel);
+                    psFree(image);
+                    psFree(kernels);
+                    exit(PS_EXIT_PROG_ERROR);
+                }
+                psFree(kernel);
+            }
+        }
+    }
+    psFree(kernels);
+
+    psFits *outFile = psFitsOpen(outName, "w"); // Output file
+    if (!outFile) {
+        psErrorStackPrint(stderr, "Unable to open output file %s", outName);
+        psFree(image);
+        exit(PS_EXIT_SYS_ERROR);
+    }
+    if (!psFitsWriteImage(outFile, NULL, image, 0, NULL)) {
+        psErrorStackPrint(stderr, "Unable to write output file");
+        psFree(image);
+        psFitsClose(outFile);
+        exit(PS_EXIT_SYS_ERROR);
+    }
+    psFitsClose(outFile);
+    psFree(image);
+
+    exit(PS_EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubLoop.c	(revision 22322)
@@ -0,0 +1,181 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+#include <psphot.h>
+
+#include "ppSub.h"
+
+bool ppSubLoop(pmConfig *config)
+{
+    bool mdok;                          // Status of MD lookup
+    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
+    psMetadata *stats = NULL;           // Container for statistics
+    FILE *statsFile = NULL;             // File stream for statistics
+    if (statsName && strlen(statsName) > 0) {
+        psString resolved = pmConfigConvertFilename(statsName, config, true, true); // Resolved filename
+        statsFile = fopen(resolved, "w");
+        if (!statsFile) {
+            psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
+            psFree(resolved);
+            return false;
+        } else {
+            stats = psMetadataAlloc();
+        }
+        psFree(resolved);
+    }
+
+    if (!pmConfigMaskSetBits(NULL, NULL, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine mask value.");
+        return false;
+    }
+
+    pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSUB.INPUT");
+    if (!input) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find input data!\n");
+        return false;
+    }
+
+    pmFPAfile *reference = psMetadataLookupPtr(NULL, config->files, "PPSUB.REF");
+    if (!reference) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find reference data!\n");
+        return false;
+    }
+
+    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PPSUB.OUTPUT");
+    if (!output) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find output data!\n");
+        return false;
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0); // Pointer into FPA hierarchy
+    pmHDU *lastHDU = NULL;              // Last HDU that was updated
+
+    // Iterate over the FPA hierarchy
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return false;
+    }
+
+    pmChip *inChip;                    // Input chip of interest
+    while ((inChip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
+        pmChip *refChip = pmFPAviewThisChip(view, reference->fpa); // Reference chip of interest
+        if ((!inChip->file_exists && refChip->file_exists) ||
+            (inChip->file_exists && !refChip->file_exists)) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "FPA format discrepency between input and reference");
+            psFree(view);
+            return false;
+        }
+
+        if (!inChip->file_exists) {
+            continue;
+        }
+
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+            return false;
+        }
+
+        pmCell *inCell;                // Cell of interest
+        while ((inCell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
+            pmCell *refCell = pmFPAviewThisCell(view, reference->fpa); // Reference cell of interest
+            if ((!inCell->file_exists && refCell->file_exists) ||
+                (inCell->file_exists && !refCell->file_exists)) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                        "FPA format discrepency between input and reference");
+                psFree(view);
+                return false;
+            }
+            if (!inCell->file_exists) {
+                continue;
+            }
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                return false;
+            }
+
+            // Put version information into the header
+            pmHDU *hdu = pmHDUFromCell(inCell);
+            if (hdu && hdu != lastHDU) {
+                if (!hdu->header) {
+                    hdu->header = psMetadataAlloc();
+                }
+                ppSubVersionMetadata(hdu->header);
+                lastHDU = hdu;
+            }
+
+            pmReadout *inRO;           // Readin of interest
+            while ((inRO = pmFPAviewNextReadout(view, input->fpa, 1))) {
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                    return false;
+                }
+                pmReadout *refRO = pmFPAviewThisReadout(view, reference->fpa);// Reference readout of interest
+                if (!refRO || (!inRO->data_exists && refRO->data_exists) ||
+                    (inRO->data_exists && !refRO->data_exists)) {
+                    psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                            "FPA format discrepency between input and reference");
+                    psFree(view);
+                    return false;
+                }
+                if (!inRO->data_exists) {
+                    continue;
+                }
+
+                // Perform the analysis
+                if (!ppSubReadout(config, stats, view)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to subtract images.\n");
+                    return false;
+                }
+
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                    return false;
+                }
+            }
+
+            // Perform statistics on the cell
+            if (stats) {
+                pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PPSUB.OUTPUT"); // Output file
+                if (!output) {
+                    psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find file PPSUB.OUTPUT.\n");
+                    return false;
+                }
+                ppStatsFPA(stats, output->fpa, view, pmConfigMaskGet("MASK.VALUE", config), config);
+            }
+
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+                return false;
+            }
+        }
+
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            return false;
+        }
+    }
+
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        return false;
+    }
+
+    psFree(view);
+
+    // Write out summary statistics
+    if (stats) {
+        psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_SUB", 0, "Time for subtraction completion",
+                         psTimerMark("ppSub"));
+
+        const char *statsMDC = psMetadataConfigFormat(stats);
+        if (!statsMDC || strlen(statsMDC) == 0) {
+            psWarning("Unable to generate statistics MDC file.\n");
+        } else {
+            fprintf(statsFile, "%s", statsMDC);
+        }
+        psFree((void *)statsMDC);
+        fclose(statsFile);
+
+        psFree(stats);
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubReadout.c	(revision 22322)
@@ -0,0 +1,590 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <psphot.h>
+
+#include "ppSub.h"
+
+#define WCS_TOLERANCE 0.001             // Tolerance for WCS
+//#define TESTING                         // For test output
+
+#define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_SATSTAR | PM_SOURCE_MODE_BLEND | \
+                     PM_SOURCE_MODE_BADPSF | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | \
+                     PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply to input sources
+
+
+// Copy every instance of a single keyword from one metadata to another
+static bool metadataCopySingle(psMetadata *target, psMetadata *source, const char *name)
+{
+    PS_ASSERT_METADATA_NON_NULL(target, false);
+    PS_ASSERT_METADATA_NON_NULL(source, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    psString regex = NULL;      // Regular expression
+    psStringAppend(&regex, "^%s$", name);
+    psMetadataIterator *iter = psMetadataIteratorAlloc(source, PS_LIST_HEAD, regex); // Iterator
+    psFree(regex);
+    psMetadataItem *item;       // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        psMetadataAddItem(target, item, PS_LIST_TAIL, PS_META_DUPLICATE_OK);
+    }
+    psFree(iter);
+
+    return true;
+}
+
+
+bool ppSubReadout(pmConfig *config, psMetadata *stats, const pmFPAview *view)
+{
+    psTimerStart("PPSUB_MATCH");
+    pmReadout *inRO = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT"); // Input readout
+    pmReadout *refRO = pmFPAfileThisReadout(config->files, view, "PPSUB.REF"); // Reference readout
+    pmReadout *sourcesRO = pmFPAfileThisReadout(config->files, view, "PPSUB.SOURCES"); // Readout with sources
+    pmReadout *inConv = pmReadoutAlloc(NULL); // Convolved version of input
+    pmReadout *refConv = pmReadoutAlloc(NULL); // Convolved version of reference
+    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();
+    }
+
+    psImage *input = inRO->image;       // Input image
+    psImage *reference = refRO->image;  // Reference image
+    PS_ASSERT_IMAGES_SIZE_EQUAL(input, reference, false);
+
+    int threads = psMetadataLookupS32(NULL, config->arguments, "-threads"); // Number of threads
+    if (threads > 0) {
+        if (!psThreadPoolInit(threads)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to setup %d threads", threads);
+            return false;
+        }
+        pmSubtractionThreadsInit(inRO, refRO);
+    }
+
+    // Look up recipe values
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
+    psAssert(recipe, "We checked this earlier, so it should be here.");
+
+    const char *typeStr = psMetadataLookupStr(NULL, recipe, "KERNEL.TYPE"); // Kernel type
+    psAssert(typeStr, "We put it here in ppSubArguments.c");
+    pmSubtractionKernelsType type = pmSubtractionKernelsTypeFromString(typeStr); // Type of kernel
+    if (type == PM_SUBTRACTION_KERNEL_NONE) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised kernel type: %s", typeStr);
+        psFree(inConv);
+        psFree(refConv);
+        psFree(outRO);
+        return false;
+    }
+
+    bool mdok;                          // Status of MD lookup
+    int size = psMetadataLookupS32(NULL, recipe, "KERNEL.SIZE"); // Kernel half-size
+    int order = psMetadataLookupS32(NULL, recipe, "SPATIAL.ORDER"); // Spatial polynomial order
+    float regionSize = psMetadataLookupF32(NULL, recipe, "REGION.SIZE"); // Size of iso-kernel regs
+    float spacing = psMetadataLookupF32(NULL, recipe, "STAMP.SPACING"); // Typical stamp spacing
+    int footprint = psMetadataLookupS32(NULL, recipe, "STAMP.FOOTPRINT"); // Stamp half-size
+    float threshold = psMetadataLookupF32(NULL, recipe, "STAMP.THRESHOLD"); // Threshold for stmps
+    int iter = psMetadataLookupS32(NULL, recipe, "ITER"); // Rejection iterations
+    float rej = psMetadataLookupF32(NULL, recipe, "REJ"); // Rejection threshold
+    bool reverse = psMetadataLookupBool(NULL, config->arguments, "REVERSE"); // Reverse sense of subtraction?
+    psVector *widths = psMetadataLookupPtr(NULL, recipe, "ISIS.WIDTHS"); // ISIS Gaussian widths
+    psVector *orders = psMetadataLookupPtr(NULL, recipe, "ISIS.ORDERS"); // ISIS Polynomial orders
+    int inner = psMetadataLookupS32(NULL, recipe, "INNER"); // Inner radius
+    int ringsOrder = psMetadataLookupS32(NULL, recipe, "RINGS.ORDER"); // RINGS polynomial order
+    int binning = psMetadataLookupS32(NULL, recipe, "SPAM.BINNING"); // Binning for SPAM kernel
+    float penalty = psMetadataLookupF32(NULL, recipe, "PENALTY"); // Penalty for wideness
+    psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.IN"); // Name of bits to mask going in
+    psMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
+    psString maskPoorStr = psMetadataLookupStr(NULL, recipe, "MASK.POOR"); // Name of bits to mask for poor
+    psMaskType maskPoor = pmConfigMaskGet(maskPoorStr, config); // Bits to mask for poor pixels
+    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
+    psMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+    float badFrac = psMetadataLookupF32(NULL, recipe, "BADFRAC"); // Maximum bad fraction
+    const char *stampsName = psMetadataLookupStr(&mdok, config->arguments, "STAMPS"); // Filename for stamps
+
+    bool optimum = psMetadataLookupBool(&mdok, recipe, "OPTIMUM"); // Derive optimum parameters?
+    float optMin = psMetadataLookupF32(&mdok, recipe, "OPTIMUM.MIN"); // Minimum width for search
+    float optMax = psMetadataLookupF32(&mdok, recipe, "OPTIMUM.MAX"); // Maximum width for search
+    float optStep = psMetadataLookupF32(&mdok, recipe, "OPTIMUM.STEP"); // Step for search
+    float optThresh = psMetadataLookupF32(&mdok, recipe, "OPTIMUM.TOL"); // Tolerance for search
+    int optOrder = psMetadataLookupS32(&mdok, recipe, "OPTIMUM.ORDER"); // Order for search
+    bool dual = psMetadataLookupBool(&mdok, recipe, "DUAL"); // Dual convolution?
+    bool renorm = psMetadataLookupBool(&mdok, recipe, "RENORM"); // Renormalise weights?
+    int renormWidth = psMetadataLookupS32(&mdok, recipe, "RENORM.WIDTH"); // Width for renormalise
+
+    psString interpModeStr = psMetadataLookupStr(&mdok, recipe, "INTERPOLATION"); // Interpolation mode
+    psImageInterpolateMode interpMode = psImageInterpolateModeFromString(interpModeStr); // Interp
+    if (interpMode == PS_INTERPOLATE_NONE) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unknown interpolation mode: %s", interpModeStr);
+        return false;
+    }
+    float poorFrac = psMetadataLookupF32(&mdok, recipe, "POOR.FRACTION"); // Fraction for "poor"
+
+    pmSubtractionMode subMode = dual ? PM_SUBTRACTION_MODE_DUAL : PM_SUBTRACTION_MODE_UNSURE; // Subtracn mode
+
+    // Generate masks if they don't exist
+    int numCols = input->numCols, numRows = input->numRows; // Image dimensions
+    if (!inRO->mask) {
+        if (psMetadataLookupBool(NULL, recipe, "MASK.GENERATE")) {
+            pmReadoutSetMask(inRO, pmConfigMaskGet("SAT", config), pmConfigMaskGet("BAD", config));
+        } else {
+            inRO->mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+            psImageInit(inRO->mask, 0);
+        }
+    }
+    if (!refRO->mask) {
+        if (psMetadataLookupBool(NULL, recipe, "MASK.GENERATE")) {
+            pmReadoutSetMask(refRO, pmConfigMaskGet("SAT", config), pmConfigMaskGet("BAD", config));
+        } else {
+            refRO->mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+            psImageInit(refRO->mask, 0);
+        }
+    }
+
+    if (!pmReadoutMaskNonfinite(inRO, pmConfigMaskGet("SAT", config))) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to mask non-finite pixels in input.");
+        return false;
+    }
+    if (!pmReadoutMaskNonfinite(refRO, pmConfigMaskGet("SAT", config))) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to mask non-finite pixels in reference.");
+        return false;
+    }
+
+    psVector *optWidths = NULL;         // Vector with FWHMs for optimum search
+    if (optimum) {
+        optWidths = psVectorCreate(optWidths, optMin, optMax, optStep, PS_TYPE_F32);
+    }
+
+    psArray *sources = NULL;            // Sources in image
+    if (sourcesRO) {
+        sources = psMetadataLookupPtr(&mdok, sourcesRO->analysis, "PSPHOT.SOURCES");
+    }
+
+#if 0
+    // Interpolate over bad pixels, so the bad pixels don't explode
+    if (!pmReadoutInterpolateBadPixels(inRO, maskVal, interpMode, poorFrac, maskPoor, maskBad)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to interpolate bad pixels for input image.");
+        return false;
+    }
+    if (!pmReadoutInterpolateBadPixels(refRO, maskVal, interpMode, poorFrac, maskPoor, maskBad)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to interpolate bad pixels for reference image.");
+        return false;
+    }
+    maskVal |= maskBad;
+#endif
+
+    // Match the PSFs
+    if (!pmSubtractionMatch(inConv, refConv, inRO, refRO, footprint, regionSize, spacing, threshold, sources,
+                            stampsName, type, size, order, widths, orders, inner, ringsOrder, binning,
+                            penalty, optimum, optWidths, optOrder, optThresh, iter, rej, maskVal,
+                            maskBad, maskPoor, poorFrac, badFrac, subMode)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to match images.");
+        psFree(inConv);
+        psFree(refConv);
+        psFree(outRO);
+        return false;
+    }
+    psFree(optWidths);
+
+    pmSubtractionThreadsFinalize(inRO, refRO);
+
+    // Add kernel descrption to header
+    pmSubtractionKernels *kernels = psMetadataLookupPtr(&mdok, inConv->analysis,
+                                                        PM_SUBTRACTION_ANALYSIS_KERNEL); // Subtraction kernel
+    if (!kernels) {
+        kernels = psMetadataLookupPtr(&mdok, refConv->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL);
+    }
+    if (!kernels) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find SUBTRACTION.KERNEL");
+        psFree(inConv);
+        psFree(refConv);
+        psFree(outRO);
+        return false;
+    }
+    psMetadataAddStr(outHDU->header, PS_LIST_TAIL, "PPSUB.KERNEL", 0,
+                     "Subtraction kernel", kernels->description);
+
+    {
+        if (psMetadataLookup(inConv->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL)) {
+            outRO->analysis = psMetadataCopy(outRO->analysis, inConv->analysis);
+        } else if (psMetadataLookup(refConv->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL)) {
+            outRO->analysis = psMetadataCopy(outRO->analysis, refConv->analysis);
+        } else {
+            psWarning("Unable to find subtraction kernel in analysis metadata.");
+        }
+
+        // Update variance factors
+        // It's not possible to do this perfectly, since we're combining different images:
+        // vf_sum sigma_sum^2 = vf_1 * sigma_1^2 + vf_2 * sigma_2^2
+        // It's not possible to write vf_sum as a function of vf_1 and vf_2 with no reference to the sigmas.
+        // Instead, we're going to cheat.
+        bool mdok;                      // Status of MD lookup
+        pmSubtractionKernels *kernels = psMetadataLookupPtr(&mdok, outRO->analysis,
+                                                            PM_SUBTRACTION_ANALYSIS_KERNEL); // Kernels
+        if (!mdok) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find subtraction kernels.");
+            psFree(inConv);
+            psFree(refConv);
+            psFree(outRO);
+            return false;
+        }
+        float vfIn = psMetadataLookupF32(NULL, inRO->parent->concepts,
+                                         "CELL.VARFACTOR"); // Variance factor for input
+        if (!isfinite(vfIn)) {
+            vfIn = 1.0;
+        }
+        float vfRef = psMetadataLookupF32(NULL, refRO->parent->concepts,
+                                          "CELL.VARFACTOR"); // Variance factor for ref
+        if (!isfinite(vfRef)) {
+            vfRef = 1.0;
+        }
+        if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+            vfIn *= pmSubtractionVarianceFactor(kernels, 0.0, 0.0, false);
+        }
+        if (kernels->mode == PM_SUBTRACTION_MODE_2) {
+            vfRef *= pmSubtractionVarianceFactor(kernels, 0.0, 0.0, false);
+        } else if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+            vfRef *= pmSubtractionVarianceFactor(kernels, 0.0, 0.0, true);
+        }
+
+        psStats *vfStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+        if (!psImageBackground(vfStats, NULL, inConv->weight, inConv->mask, maskVal | maskBad, rng)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to measure mean variance for convolved input");
+            psFree(inConv);
+            psFree(refConv);
+            psFree(outRO);
+            psFree(vfStats);
+            psFree(rng);
+            return false;
+        }
+        float inMeanVar = vfStats->robustMedian; // Mean variance of input
+        if (!psImageBackground(vfStats, NULL, refConv->weight, refConv->mask, maskVal | maskBad, rng)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to measure mean variance for convolved reference");
+            psFree(inConv);
+            psFree(refConv);
+            psFree(outRO);
+            psFree(vfStats);
+            psFree(rng);
+            return false;
+        }
+        float refMeanVar = vfStats->robustMedian; // Mean variance of reference
+        psFree(rng);
+        psFree(vfStats);
+
+        float vf = (vfIn * inMeanVar + vfRef * refMeanVar) / (inMeanVar + refMeanVar); // Resulting var factor
+        psMetadataItem *vfItem = psMetadataLookup(outRO->parent->concepts, "CELL.VARFACTOR");
+        vfItem->data.F32 = vf;
+
+        // Statistics on the matching
+        if (stats) {
+            psMetadataAddF32(stats, PS_LIST_TAIL, "MATCH_TIME", 0, "Time to match PSFs",
+                             psTimerClear("PPSUB_MATCH"));
+            psMetadataAddS32(stats, PS_LIST_TAIL, "SUBTRACTION.NUM", 0, "Number of stamps",
+                             kernels->numStamps);
+            psMetadataAddF32(stats, PS_LIST_TAIL, "SUBTRACTION.MEAN", 0, "Mean stamp deviation",
+                             kernels->mean);
+            psMetadataAddF32(stats, PS_LIST_TAIL, "SUBTRACTION.RMS", 0, "RMS stamp deviation",
+                             kernels->rms);
+            metadataCopySingle(stats, outRO->analysis, PM_SUBTRACTION_ANALYSIS_MODE);
+        }
+    }
+
+
+#ifdef TESTING
+    psImage *kernelImage = psMetadataLookupPtr(&mdok, inConv->analysis,
+                                               "SUBTRACTION.KERNEL.IMAGE"); // Image of the kernels
+    if (!kernelImage) {
+        kernelImage = psMetadataLookupPtr(&mdok, refConv->analysis, "SUBTRACTION.KERNEL.IMAGE");
+    }
+    psFits *fits = psFitsOpen("kernel.fits", "w");
+    psFitsWriteImage(fits, NULL, kernelImage, 0, NULL);
+    psFitsClose(fits);
+#endif
+
+    // Subtraction is: minuend - subtrahend
+    pmReadout *minuend = inConv;
+    pmReadout *subtrahend = refConv;
+
+    if (reverse) {
+        pmReadout *temp = subtrahend;
+        subtrahend = minuend;
+        minuend = temp;
+    }
+
+#ifdef TESTING
+    {
+        pmReadoutMaskApply(minuend, maskVal);
+        psFits *fits = psFitsOpen("minuend.fits", "w");
+        psFitsWriteImage(fits, NULL, minuend->image, 0, NULL);
+        psFitsClose(fits);
+    }
+    {
+        pmReadoutMaskApply(subtrahend, maskVal);
+        psFits *fits = psFitsOpen("subtrahend.fits", "w");
+        psFitsWriteImage(fits, NULL, subtrahend->image, 0, NULL);
+            psFitsClose(fits);
+    }
+#endif
+
+    outRO->mask = (psImage*)psBinaryOp(outRO->mask, inConv->mask, "|", refConv->mask);
+    outRO->data_exists = outCell->data_exists = outCell->parent->data_exists = true; // It'll be there soon
+
+    // Photometry is to be performed in two stages:
+    // 1. Measure the PSF using the PSF-matched images
+    // 2. Find and measure sources on the subtracted image
+    psTimerStart("PPSUB_PHOT");
+
+    // Photometry stage 1: measure the PSF
+    pmPSF *psf = NULL;                  // PSF for photometry
+    if (psMetadataLookupBool(NULL, recipe, "PHOTOMETRY")) {
+        outRO->image = psImageCopy(outRO->image, minuend->image, PS_TYPE_F32);
+        if (minuend->weight) {
+            outRO->weight = psImageCopy(outRO->weight, minuend->weight, PS_TYPE_F32);
+        }
+
+        pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT");
+        pmFPACopy(photFile->fpa, outRO->parent->parent->parent);
+
+        // We have a list of sources, so we could use those to redetermine the PSF model.
+        // Until Gene makes the necessary adaptations to psphot, we will simply redetermine the PSF model from
+        // scratch.
+
+        // Need to ensure aperture residual is not calculated
+        psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PSPHOT_RECIPE); // Recipe
+        if (!recipe) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s recipe.", PSPHOT_RECIPE);
+            return false;
+        }
+        const char *breakpoint = psMetadataLookupStr(&mdok, recipe, "BREAK_POINT"); // Current break point
+        psMetadataAddStr(recipe, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE,
+                         "ALTERED break point for psphot operations", "PSFMODEL");
+
+        // set maskValue and markValue in the psphot recipe
+        psMaskType maskValue = maskVal;
+        psMaskType markValue = pmConfigMaskGet("MARK.VALUE", config); // Bits to use for marking
+        psMetadataAddU8(recipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "Bits to mask", maskValue);
+        psMetadataAddU8(recipe, PS_LIST_TAIL, "MARK.PSPHOT", PS_META_REPLACE, "Bits to use for marking",
+                        markValue);
+
+        if (!psphotReadout(config, view)) {
+            psWarning("Unable to perform photometry on subtracted image.");
+            psErrorStackPrint(stderr, "Error stack from photometry:");
+            psErrorClear();
+        }
+
+        if (breakpoint) {
+            psMetadataAddStr(recipe, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE,
+                             "RESTORED break point for psphot operations", breakpoint);
+        }
+
+        // Blow away the sources psphot found --- they're irrelevant for the subtraction
+        pmReadout *photRO = pmFPAviewThisReadout(view, photFile->fpa); // Readout with sources
+        psMetadataRemoveKey(photRO->analysis, "PSPHOT.SOURCES");
+        psMetadataRemoveKey(photRO->analysis, "PSPHOT.HEADER");
+
+        psf = psMemIncrRefCounter(psMetadataLookupPtr(NULL, photRO->parent->parent->analysis, "PSPHOT.PSF"));
+        if (!psf) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find PSF from psphot");
+            return false;
+        }
+
+        pmCell *photCell = pmFPAfileThisCell(config->files, view, "PSPHOT.INPUT");
+        pmCellFreeReadouts(photCell);
+    }
+
+    // Do the actual subtraction
+    outRO->image = (psImage*)psBinaryOp(outRO->image, minuend->image, "-", subtrahend->image);
+    if (inConv->weight && refConv->weight) {
+        outRO->weight = (psImage*)psBinaryOp(outRO->weight, inConv->weight, "+", refConv->weight);
+    }
+    outRO->data_exists = outCell->data_exists = outCell->parent->data_exists = true;
+
+    pmReadoutMaskApply(outRO, maskBad);
+
+    psFree(inConv);
+    psFree(refConv);
+
+#ifdef TESTING
+    for (int y = 0; y < outRO->image->numRows; y++) {
+        for (int x = 0; x < outRO->image->numCols; x++) {
+            if (isnan(outRO->image->data.F32[y][x]) && !(outRO->mask->data.U8[y][x] & maskVal)) {
+                printf("Unmasked NAN at %d %d --> %d\n", x, y, outRO->mask->data.U8[y][x]);
+            }
+        }
+    }
+#endif
+
+    if (!pmFPACopyConcepts(outCell->parent->parent, inRO->parent->parent->parent)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to copy concepts from input to output.");
+        psFree(outRO);
+        return false;
+    }
+
+    if (renorm) {
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+        if (!pmReadoutWeightRenorm(outRO, maskBad, PS_STAT_ROBUST_MEDIAN, PS_STAT_ROBUST_STDEV,
+                                   renormWidth, rng)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to renormalise weights for photometry.");
+            psFree(outRO);
+            return false;
+        }
+        psFree(rng);
+    }
+
+#if 0
+    // XXX Under development
+
+    // QA: photometry of known sources with measured PSF
+    if (sourcesRO && psMetadataLookupBool(NULL, recipe, "PHOTOMETRY")) {
+        sources = psMetadataLookupPtr(&mdok, sourcesRO->analysis, "PSPHOT.SOURCES");
+
+
+
+        // For each source, need to:
+        // * generate appropriate model
+        // * select pixels
+        // * define fit radius
+        // Don't need to measure sky (psphotFitSourcesLinear does that)
+
+        psArray *dummy = psArrayAlloc(1); // Dummy array of sources, for psphotFitSourcesLinear with 1 source
+        for (long i = 0; i < sources->n; i++) {
+            pmSource *source = sources->data[i];
+            if (!source || (source->mode & SOURCE_MASK)) {
+                continue;
+            }
+
+            float origMag = source->psfMag; // Original magnitude
+
+            // Coordinates of source
+            float x = source->modelPSF->params->data.F32[PM_PAR_XPOS];
+            float y = source->modelPSF->params->data.F32[PM_PAR_YPOS];
+
+            model = pmModelFromPSFforXY(psf, x, y, NAN);
+            if (!fakeModel) {
+                psWarning("Can't generate model");
+                continue;
+            }
+            if (!pmSourceDefinePixels(fakeSource, readout, x, y, fakeRadius)) {
+                psErrorClear();
+                continue;
+            }
+pmModel *pmModelFromPSFforXY (
+    const pmPSF *psf,
+    float Xo,
+    float Yo,
+    float Io
+    );
+
+bool pmSourceDefinePixels(
+    pmSource *mySource,                 ///< source to be re-defined
+    const pmReadout *readout,  ///< base the source on this readout
+    psF32 x,                            ///< center coords of source
+    psF32 y,                            ///< center coords of source
+    psF32 Radius                        ///< size of box on source
+);
+
+
+bool psphotFitSourcesLinear (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, bool final) {
+
+    }
+#endif
+
+    // Photometry stage 2: find and measure sources on the subtracted image
+    if (psMetadataLookupBool(NULL, recipe, "PHOTOMETRY")) {
+        // The PSF should already be stored for the readout
+        pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT");
+        pmFPACopy(photFile->fpa, outRO->parent->parent->parent);
+
+        pmReadout *psfRO = pmFPAfileThisReadout(config->files, view, "PSPHOT.PSF.LOAD");
+        if (!psfRO) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find readout for file PSPHOT.PSF.LOAD");
+            return false;
+        }
+        psMetadataAddPtr(psfRO->parent->parent->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN,
+                         "PSF from matched addition", psf);
+        psFree(psf);
+
+        // Need to ensure aperture residual is not calculated
+        psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PSPHOT_RECIPE); // Recipe
+        psMetadataItem *item = psMetadataLookup(recipe, "MEASURE.APTREND"); // Item determining aptrend
+        if (!item) {
+            psWarning("Unable to find MEASURE.APTREND in psphot recipe");
+            psErrorClear();
+        } else {
+            item->data.B = false;
+        }
+
+        if (!psphotReadout(config, view)) {
+            psWarning("Unable to perform photometry on subtracted image.");
+            psErrorStackPrint(stderr, "Error stack from photometry:");
+            psErrorClear();
+        }
+
+        pmFPAfileActivate(config->files, false, "PSPHOT.INPUT");
+        pmFPAfileActivate(config->files, false, "PSPHOT.LOAD.PSF");
+
+        if (stats) {
+            pmReadout *photRO = pmFPAviewThisReadout(view, photFile->fpa); // Readout with the sources
+            psArray *sources = psMetadataLookupPtr(NULL, photRO->analysis, "PSPHOT.SOURCES"); // Sources
+            psMetadataAddS32(stats, PS_LIST_TAIL, "NUM_SOURCES", 0, "Number of sources detected", sources->n);
+            psMetadataAddF32(stats, PS_LIST_TAIL, "MATCH_TIME", 0, "Time to do photometry",
+                             psTimerClear("PPSUB_PHOT"));
+        }
+    }
+
+    // Copy astrometry over
+    pmFPA *refFPA = refRO->parent->parent->parent; // Reference FPA
+    pmHDU *refHDU = refFPA->hdu;        // Reference HDU
+    if (!outHDU || !refHDU) {
+        psWarning("Unable to find HDU at FPA level to copy astrometry.");
+    } else {
+        if (!pmAstromReadWCS(outFPA, outCell->parent, refHDU->header, 1.0)) {
+            psWarning("Unable to read WCS astrometry from reference FPA.");
+            psErrorClear();
+        } else if (!pmAstromWriteWCS(outHDU->header, outFPA, outCell->parent, WCS_TOLERANCE)) {
+            psWarning("Unable to write WCS astrometry to output FPA.");
+            psErrorClear();
+        }
+    }
+
+    // Add additional data to the header
+    pmFPAfile *refFile = psMetadataLookupPtr(NULL, config->files, "PPSUB.REF"); // Reference file
+    pmFPAfile *inFile = psMetadataLookupPtr(NULL, config->files, "PPSUB.INPUT"); // Input file
+    psMetadataAddStr(outHDU->header, PS_LIST_TAIL, "PPSUB.REFERENCE", 0,
+                     "Subtraction reference", refFile->filename);
+    psMetadataAddStr(outHDU->header, PS_LIST_TAIL, "PPSUB.INPUT", 0,
+                     "Subtraction input", inFile->filename);
+
+
+    // Generate binned JPEGs
+    {
+        int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
+        int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
+
+        // Target cells
+        pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSUB.OUTPUT.JPEG1");
+        pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSUB.OUTPUT.JPEG2");
+
+        pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
+        if (!pmReadoutRebin(ro1, outRO, maskBad, bin1, bin1) || !pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to bin output.");
+            psFree(ro1);
+            psFree(ro2);
+            psFree(outRO);
+            return false;
+        }
+        psFree(ro1);
+        psFree(ro2);
+    }
+
+    psFree(outRO);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/src/ppSubVersion.c	(revision 22322)
@@ -0,0 +1,60 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppSub.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString ppSubVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString ppSubVersionLong(void)
+{
+    psString version = ppSubVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
+
+void ppSubVersionMetadata(psMetadata *metadata)
+{
+    PS_ASSERT_METADATA_NON_NULL(metadata,);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppStats = ppStatsVersionLong(); // ppStats version
+    psString ppSub = ppSubVersionLong(); // ppSub version
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString head = NULL;               // Head string
+    psStringAppend(&head, "ppSub processing at %s. Component information:", timeString);
+    psFree(timeString);
+
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppStats, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppSub, "");
+
+    psFree(head);
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(ppStats);
+    psFree(ppSub);
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/ppSub/test/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/test/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/test/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*
Index: /tags/sj_tags/sj_root_20080929/ppSub/test/fake.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/ppSub/test/fake.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/ppSub/test/fake.c	(revision 22322)
@@ -0,0 +1,182 @@
+#include <stdio.h>
+#include <pslib.h>
+
+// Quick and dirty program to generate cross-directed PSFs for testing ppSub.
+// To build: gcc fake.c -o fake -g -std=gnu99 `pslib-config --cflags --libs`
+
+
+// PSF for images
+#define X_AXIS_1 5.0                    // Length of x axis (FWHM) for image 1
+#define Y_AXIS_1 3.0                    // Length of y axis (FWHM) for image 1
+#define X_AXIS_2 5.0                    // Length of x axis (FWHM) for image 1
+#define Y_AXIS_2 5.0                    // Length of y axis (FWHM) for image 1
+
+// Image parameters
+#define NUMCOLS 1024                    // Number of columns
+#define NUMROWS 1024                    // Number of rows
+#define IMAGE_SCALE 0.2                 // Arcsec per pixel
+
+// Source parameters
+#define SOURCES_LUM 0.4                 // Luminosity function
+#define SOURCES_MAG 14.5                // Magnitude for density
+#define SOURCES_DENSITY 30000.0         // Source density at nominated magnitude (per deg^2)
+#define SOURCES_ZP 25.0                 // Magnitude ZP
+#define SOURCES_GAUSSIAN 0              // Gaussian sources (boolean)?
+
+// Noise properties
+#define NOISE_1 10.0                    // Noise in image 1
+#define NOISE_2 10.0                    // Noise in image 2
+#define NOISE_FRAC 0.1                  // Go out to this fraction of the noise
+
+// Conversion factor by which to multiply sigma to get FWHM.  Roughly 2.35
+#define SIGMA_FWHM (2.0*sqrt((double)2.0*(log((double)2.0))))
+
+// Add a star to an image
+static void addstar(float x, float y,   // Coordinates of source
+                    float flux,         // Total (integrated) flux of source
+                    float xSigma, float ySigma, // Gaussian width of source
+                    float noise,        // Lowest noise level to consider
+                    psImage *image      // Image to which to add source
+                    )
+{
+    float peak = flux / 2.0 / M_PI / sqrtf(PS_SQR(xSigma) + PS_SQR(ySigma)); // Peak flux
+    if (peak < noise) {
+        return;
+    }
+
+#if SOURCES_GAUSSIAN
+    // Gaussian
+    float radius = sqrtf(2.0 * (PS_SQR(xSigma) + PS_SQR(ySigma)) * logf(peak / noise)); // Radius for source
+#else
+    // Basic Waussian
+    float radius;
+    if (peak / noise > 10.0) {
+        // Need to treat the bright limit differently because the wings are much more noticeable
+        radius = sqrtf(2.0 * (PS_SQR(xSigma) + PS_SQR(ySigma)) * peak / noise);
+    } else {
+        radius = sqrtf(2.0) * hypot(1.0 / xSigma, 1.0 / ySigma) * sqrtf(2.0 * logf(peak / noise));
+    }
+#endif
+
+    // Bounds of source
+    int xMin = PS_MAX(floorf(x - radius), 0);
+    int xMax = PS_MIN(ceilf(x + radius), NUMCOLS - 1);
+    int yMin = PS_MAX(floorf(y - radius), 0);
+    int yMax = PS_MIN(ceilf(y + radius), NUMROWS - 1);
+
+    for (int j = yMin; j <= yMax; j++) {
+        float dy = j - y;
+        for (int i = xMin; i <= xMax; i++) {
+            float dx = i - x;
+#if SOURCES_GAUSSIAN
+            // Gaussian
+            float value = peak * expf(- PS_SQR(dx) / 2.0 / PS_SQR(xSigma) -
+                                      PS_SQR(dy) / 2.0 / PS_SQR(ySigma));
+#else
+            // Basic Waussian
+            float z = 0.5 * (PS_SQR(dx / xSigma) + PS_SQR(dy / ySigma));
+            float value = peak / (1.0 + z + PS_SQR(z));
+#endif
+
+            image->data.F32[j][i] += value;
+        }
+    }
+
+    return;
+}
+
+
+int main(int argc, char *argv[])
+{
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+
+    // Images to generate
+    psImage *image1 = psImageAlloc(NUMCOLS, NUMROWS, PS_TYPE_F32);
+    psImage *image2 = psImageAlloc(NUMCOLS, NUMROWS, PS_TYPE_F32);
+
+    psImageInit(image1, 0.0);
+    psImageInit(image2, 0.0);
+
+    float magNorm = SOURCES_DENSITY * NUMCOLS * NUMROWS * PS_SQR(IMAGE_SCALE / 3600.0) /
+        powf(10.0, (SOURCES_LUM * SOURCES_MAG)); // Normalisation for magnitudes
+
+    float faintMag = -2.5 * log10(PS_MIN(NOISE_1 * sqrtf(X_AXIS_1 * Y_AXIS_1),
+                                         NOISE_2 * sqrtf(X_AXIS_2 * Y_AXIS_2)) *
+                                  NOISE_FRAC) + SOURCES_ZP; // Faintest magnitude
+    printf("Faintest mag: %f\n", faintMag);
+
+    long numStars = magNorm * powf(10.0, SOURCES_LUM * faintMag); // Number of stars
+    printf("%ld stars\n", numStars);
+
+    // Gaussian widths, in sigma
+    float xSigma1 = X_AXIS_1 / SIGMA_FWHM;
+    float ySigma1 = Y_AXIS_1 / SIGMA_FWHM;
+    float xSigma2 = X_AXIS_2 / SIGMA_FWHM;
+    float ySigma2 = Y_AXIS_2 / SIGMA_FWHM;
+
+    for (int i = 0; i < numStars; i++) {
+        float x = psRandomUniform(rng) * NUMCOLS; // x coordinate
+        float y = psRandomUniform(rng) * NUMROWS; // y coordinate
+        float mag = log10((i + 1.0) / magNorm) / SOURCES_LUM; // Magnitude of source
+        float flux = powf(10.0, -0.4 * (mag - SOURCES_ZP)); // Total flux of source
+
+        addstar(x, y, flux, xSigma1, ySigma1, NOISE_1 * NOISE_FRAC, image1);
+        addstar(x, y, flux, xSigma2, ySigma2, NOISE_2 * NOISE_FRAC, image2);
+    }
+
+#if 1
+    psImage *var1 = (psImage*)psBinaryOp(NULL, image1, "+", psScalarAlloc(PS_SQR(NOISE_1), PS_TYPE_F32));
+    psImage *var2 = (psImage*)psBinaryOp(NULL, image2, "+", psScalarAlloc(PS_SQR(NOISE_2), PS_TYPE_F32));
+#else
+    psImage *var1 = psImageAlloc(NUMCOLS, NUMROWS, PS_TYPE_F32);
+    psImage *var2 = psImageAlloc(NUMCOLS, NUMROWS, PS_TYPE_F32);
+    psImageInit(var1, PS_SQR(NOISE_1));
+    psImageInit(var2, PS_SQR(NOISE_2));
+#endif
+
+    for (int y = 0; y < NUMROWS; y++) {
+        for (int x = 0; x < NUMCOLS; x++) {
+            image1->data.F32[y][x] += psRandomGaussian(rng) * sqrtf(var1->data.F32[y][x]);
+            image2->data.F32[y][x] += psRandomGaussian(rng) * sqrtf(var2->data.F32[y][x]);
+        }
+    }
+#if 1
+    var1 = (psImage*)psBinaryOp(var1, image1, "+", psScalarAlloc(PS_SQR(NOISE_1), PS_TYPE_F32));
+    var2 = (psImage*)psBinaryOp(var2, image2, "+", psScalarAlloc(PS_SQR(NOISE_2), PS_TYPE_F32));
+#else
+    psImageInit(var1, PS_SQR(NOISE_1));
+    psImageInit(var2, PS_SQR(NOISE_2));
+#endif
+
+    {
+        psFits *fits = psFitsOpen("image1.fits", "w");
+        psFitsWriteImage(fits, NULL, image1, 0, NULL);
+        psFitsClose(fits);
+    }
+    {
+        psFits *fits = psFitsOpen("image2.fits", "w");
+        psFitsWriteImage(fits, NULL, image2, 0, NULL);
+        psFitsClose(fits);
+    }
+
+    {
+        psFits *fits = psFitsOpen("variance1.fits", "w");
+        psFitsWriteImage(fits, NULL, var1, 0, NULL);
+        psFitsClose(fits);
+    }
+    {
+        psFits *fits = psFitsOpen("variance2.fits", "w");
+        psFitsWriteImage(fits, NULL, var2, 0, NULL);
+        psFitsClose(fits);
+    }
+
+    psFree(image1);
+    psFree(image2);
+
+    psFree(var1);
+    psFree(var2);
+
+    psFree(rng);
+
+    return PS_EXIT_SUCCESS;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/.cvsignore	(revision 22322)
@@ -0,0 +1,35 @@
+DoxygenLog
+Doxyfile
+ChangeLog
+bin
+lib
+include
+man
+include
+pslib.kdevelop.*
+docs
+Makefile.in
+aclocal.m4
+autom4te.cache
+configure
+Makefile
+config.log
+config.status
+libtool
+pslib-config
+pslib.pc
+pslib-*.tar.gz
+pslib-*.tar.bz2
+compile
+config.guess
+config.sub
+depcomp
+install-sh
+ltmain.sh
+missing
+pslib.kdevses
+.deps
+.libs
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/AUTHORS
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/AUTHORS	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/AUTHORS	(revision 22322)
@@ -0,0 +1,1 @@
+Copyright 2004 Maui High Performance Computing Center, University of Hawaii
Index: /tags/sj_tags/sj_root_20080929/psLib/COPYING
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/COPYING	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/COPYING	(revision 22322)
@@ -0,0 +1,345 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /tags/sj_tags/sj_root_20080929/psLib/DEV_NOTES
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/DEV_NOTES	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/DEV_NOTES	(revision 22322)
@@ -0,0 +1,175 @@
+---- Developing in the autotools environment for non-autotools developers. ----
+
+
+There are many detailed resources on the web about the autotools (autoconf,
+automake, libtools).  Being GNU tools, the primary resources are found there
+at:
+  o  http://www.gnu.org/software/autoconf/manual/autoconf-2.57/autoconf.html
+  o  http://www.gnu.org/software/automake/manual/automake.html
+  o  http://www.gnu.org/software/libtool/manual.html
+
+This document will deal with only the basic required tasks for developing in
+the autotools environment, namely:
+  o  Creating the configuration environment from CVS check-out,
+  o  Configuring the library,
+  o  Making the library,
+  o  Adding and removing a source code file, and
+  o  Adding and removing a test code file.
+
+
+Creating the configuration environment from CVS check-out
+===============================================================================
+
+The autotools as a group help you create software distributions which will
+build reliably on a large variety of platforms.  The minimum required contact
+with the autotools is to generate the files which will let you configure a
+freshly checked-out directory before working on it, since these configuration
+files are not checked in to the repository. The Makefile.cvs makefile will do
+this for you, but it can only do this if the autotools are installed (which is
+commonly included into every Linux Distribution as well as Mac OSX).
+
+The command sequence example for this step is as follows:
+  > cvs co psLib
+  > cd psLib
+  > make -f Makefile.cvs
+
+Configuring the library
+===============================================================================
+
+Running the configure script is basically very simple: ./configure.  There are,
+however, options and arguments which can help you, or catch you out.  You can
+see the full list of options with the command ./configure --help.
+
+You set the place where the configured component will be installed with the
+--prefix option. This names a directory which will end up with the standard
+directories bin, lib and so on.  The default is `pwd` currently, but that may
+change to something like $HOME in the future.
+
+The psLib autoconf adds a couple of extra options to ./configure that may be
+of interest to a developer.
+
+--enable-perlmodule
+  enables the building of the perl module using SWIG.  The default is to not
+  build the perl modules
+
+--enable-optimize
+  by default, the build disables compiler optimization for C files to speed up
+  the compile time.  By adding this, '-O2' is added to CFLAGS.
+
+--enable-static
+  by default, only a shared, dynamically-linked, library is built.  This option
+  enables the creation of a static library at the cost of doubling the compiling
+  time.  To create only a static library, include the --disable-shared option
+  as well.
+
+--with-cfitsio=DIR
+  specifies the location of the CFITSIO installation prefix, e.g., /usr/local.
+  To specify the include and/or library directories directly, use the options
+  --with-cfitsio-include=DIR and --with-cfitsio-lib=DIR respectively.  If not
+  specified, CFITSIO is assumed to have been installed in the standard paths
+  for the OS, e.g., /usr or /usr/local.
+
+--with-fftw3=DIR
+  specifies the location of the FFTW3 installation prefix, e.g., /usr/local.
+  To specify the include and/or library directories directly, use the options
+  --with-fftw3-include=DIR and --with-fftw3-lib=DIR respectively.  If not
+  specified, FFTW3 is assumed to have been installed in the standard paths
+  for the OS, e.g., /usr or /usr/local.
+
+--with-gsl-config=FILE
+  specifies the gsl-config script to use to gather the GSL library information.
+  This is the full path to the gsl-config script included in the package's
+  installation, e.g., /usr/local/bin/gsl-config.  If not specified, $PATH is
+  used to find the script.
+
+--with-xml2-config=FILE
+  specifies the xml2-config script to use to gather the XML2 library
+  information.  This is the full path to the xml2-config script included in
+  the package's installation, e.g., /usr/local/bin/xml2-config.  If not
+  specified, $PATH is used to find the script.
+
+--with-swig=FILE
+  specifies the swig executable to use to generate the SWIG wrapper code.
+  This is the full path to the swig executable, e.g., /usr/bin/swig.  If not
+  specified, $PATH is used to find the executable.
+
+--with-perl=FILE
+  specifies the perl executable to use to generate the SWIG wrapper code.
+  This is the full path to the perl executable, e.g., /usr/bin/perl.  If not
+  specified, $PATH is used to find the executable.
+
+--with-perlprefix=DIR
+  specifies the installation prefix for the perl module.  If not
+  specified, the PREFIX as specified by the --prefix option (or its default)
+  is used.
+
+
+The command sequence example for this step, using all default settings, is as
+follows:  
+  > ./configure
+
+  
+Adding and removing a source code file
+===============================================================================
+
+The library is built from a number of smaller, 'convienence' libraries, one
+convienence library for each subdirectory off of src.  To add a file to the
+pslib library, you need to add it to one of convienence libraries in the
+appropriate subdirectory
+
+To add functionality, you need to add the filenames to the Makefile.am in the
+subdirectory in which the file is being added.
+  o  The source filename needs to added to the libpslibSUBDIR_la_SOURCES
+     variable, where SUBDIR is the subdirectory name.
+  o  Any header files needed to be installed (which is generally going to be
+     all header files) should be added to the pslibinclude_HEADERS variable.
+  o  Any other file needed to be included in the distribution tarball but are
+     not to be installed should be included in the EXTRA_DIST variable.
+
+Upon saving Makefile.am, the existing Makefile will detect the change and
+automagically perform any executation of automake/autoconf, so a simple
+'make' should fold in these changes for you.
+
+
+Adding and removing a test code file
+===============================================================================
+
+Tests are added into a subdirectory of the test directory which corresponds to
+the functionality location under src being tested.  For example, if testing
+functionality found in src/astronomy/psFoo.c, you will need to add the
+corresponding tests in test/astronomy/tst_psFoo.c.  If the test driver code
+file does not current exist, use test/tst_template.c as a template on how to
+utilize the psLib test facilities.
+
+To add a test, you need to add the filenames to the Makefile.am in the
+subdirectory in which the file is being added.
+  o  The source filename minux the .c suffix needs to added to the
+     TESTS and variable.  This specifies the executable target name.
+  o  Using the name you added to TESTS, create a line of the form
+        tst_psFoo_SOURCES = tst_psFoo.c
+     This specifies the source files needed to create the test driver
+     executable (in the psLib test suite, this is generally just one file).
+  o  Any test data file needs to be added to the check_DATA variable and
+     a rule needs to be created that will copy the file from the source
+     directory to the build directory.  This is because the directory in
+     which your data file exists is not necessarily where the test executable
+     is built.  The simpliest way to do this is to place the data file in
+     a subdirectory like 'verified' and create a rule like:
+        fBiOut.fits: verified/fBiOut.fits
+                cp $? $@
+     Automake will determine that the target directory of the cp should be
+     the build directory, so the file will be copied to the same directory
+     as your executable (so that in the code, you can assume the path is '.')
+     
+
+Upon saving Makefile.am, the existing Makefile will detect the change and
+automagically perform any executation of automake/autoconf, so a simple
+'make' should fold in these changes for you.
+
+-------------------------------------------------------------------------------
+N.B. this file is not to be included in distribution packages.
+
+Gotchas
+===============================================================================
+
+libtap uses asprintf() which is non-portable
Index: /tags/sj_tags/sj_root_20080929/psLib/Doxyfile.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/Doxyfile.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/Doxyfile.in	(revision 22322)
@@ -0,0 +1,1080 @@
+# Doxyfile 1.3.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = @PACKAGE_NAME@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = @VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+# @top_builddir@ doesn't work for some reason
+OUTPUT_DIRECTORY       = @builddir@/docs
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
+# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese,
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH        =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP         = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
+# only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           = DoxygenLog
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = @top_srcdir@/src
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
+
+FILE_PATTERNS          = *.h *.dox
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = *_wrap.c
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER           =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet
+
+HTML_STYLESHEET        =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output dir.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = YES
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 10
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = YES
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = letter
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = times
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimised for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assigments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.  This is useful
+# if you want to understand what is going on.  On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED             = DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse the
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#   TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#   TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = @PERL@
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similiar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes that
+# lay further from the root node will be omitted. Note that setting this option to
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that a graph may be further truncated if the graph's image dimensions are
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT).
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
+
Index: /tags/sj_tags/sj_root_20080929/psLib/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/INSTALL	(revision 22322)
@@ -0,0 +1,167 @@
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes a while.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Type `make install' to install the programs and any data files and
+     documentation.
+
+  4. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
Index: /tags/sj_tags/sj_root_20080929/psLib/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/Makefile.am	(revision 22322)
@@ -0,0 +1,30 @@
+SUBDIRS = m4 $(SUBDIR)
+
+bin_SCRIPTS = pslib-config 
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= pslib.pc
+
+EXTRA_DIST = \
+    Doxyfile.in \
+    pslib-config.in \
+    pslib.pc.in \
+    autogen.sh
+
+if HAVE_DOXYGEN
+install-data-hook: doxygen
+	-$(mkdir_p) $(mandir)/man3
+	chmod 0755 $(mandir)/man3
+	-cp $(top_builddir)/docs/man/man3/* $(mandir)/man3
+
+doxygen:
+	$(DOXYGEN)
+endif
+
+CLEANFILES = *~ *.bb *.bbg *.da DoxygenLog
+
+clean-local:
+	-rm -rf docs
+
+test: check
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/psLib/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/README	(revision 22322)
@@ -0,0 +1,47 @@
+Pan-STARRS Image Processing Pipeline Library
+============================================
+
+The is psLib, the Pan-STARRS Image Processing Pipeline Library, a base
+collection of functions for the Pan-STARRS PS1 observatory.
+
+It was developed by the Maui High Performance Computing Center
+(http://www.mhpcc.edu) for the University of Hawai'i Institute for Astronomy
+(http://ifa.hawaii.edu).
+
+psLib is free software, you can redistribute it and/or modify it under
+the terms of the GNU General Public License.
+
+The GNU General Public License does not permit this software to be
+redistributed in proprietary programs.
+
+This library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+Availability
+============
+
+The current version of psLib is always available from
+http://mhpcc.pan-starrs.org/code.
+
+Installation
+============
+
+psLib follows the standard GNU installation procedure.  Please consult
+the INSTALL file in this distribution for more detailed instructions.
+
+For information about specific platforms and compilers see the
+"Compilation Notes" section in the INSTALL file.
+
+More information about Pan-STARRS
+=================================
+
+The project homepage is http://www.pan-starrs.org.
+
+Reporting Bugs
+==============
+
+If you find a bug, please report it via the project's Bugzilla webpage at
+http://www.pan-starrs.org/bugzilla or email robert.desonia@mhpcc.hpc.mil.
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/autogen.sh	(revision 22322)
@@ -0,0 +1,113 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=pslib
+TEST_TYPE=-f
+FILE=pslib.pc.in
+
+DIE=0
+
+if [ "`which glibtoolize 2> /dev/null`" != "" ]
+ then LIBTOOLIZE=glibtoolize
+ else LIBTOOLIZE=libtoolize
+fi
+
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL -I m4 || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+# bypass taps bootstrap.sh
+cd ./test/tap
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/psLib/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/configure.ac	(revision 22322)
@@ -0,0 +1,487 @@
+AC_PREREQ(2.61)
+
+AC_INIT([pslib],[1.1.1],[http://pan-starrs.ifa.hawaii.edu/bugzilla])
+AC_CONFIG_SRCDIR([pslib.pc.in])
+AC_CANONICAL_TARGET
+
+dnl this enables the building of libtap
+AC_CONFIG_SUBDIRS([test/tap])
+
+AM_INIT_AUTOMAKE([1.7 foreign dist-bzip2])
+AC_CONFIG_HEADERS([src/config.h])
+AM_MAINTAINER_MODE
+
+dnl So, libtool library versions are described by three integers:
+dnl 
+dnl current
+dnl     The most recent interface number that this library implements.
+dnl revision
+dnl     The implementation number of the current interface.
+dnl age
+dnl     The difference between the newest and oldest interfaces that this
+dnl library implements. In other words, the library implements all the
+dnl interface numbers in the range from number current - age to current
+
+PSLIB_LT_VERSION="1:1:0"
+AC_SUBST(PSLIB_LT_VERSION,$PSLIB_LT_VERSION)
+
+IPP_STDCFLAGS
+
+dnl ------------------- PERL options ---------------------
+  AC_ARG_WITH(perl,
+    [AS_HELP_STRING(--with-perl=FILE,Specify location of PERL executable.)],
+    [AC_CHECK_PROG(PERL, $withval, $withval)],
+    [AC_CHECK_PROG(PERL, perl, `which perl`)])
+    if test "$PERL" == ""
+    then
+      AC_MSG_ERROR([PERL is required.  Use --with-perl to specify its install location.])
+    fi
+    AC_SUBST(PERL,$PERL)
+
+SUBDIR="etc src share test utils" dnl don't include 'swig', as it is optional
+
+SRCPATH='${top_srcdir}/src'
+SRCDIRS="sys astro db fft fits imageops jpeg math mathtypes types"   dnl xml
+# escape two escapes at this level so \\ gets passed to the shell and \ to perl
+SRCINC=`echo "${SRCDIRS=}" | ${PERL} -pe "s|(\w+)|-I\\\\${SRCPATH=}/\1|g"`
+SRCINC="-I${SRCPATH=} ${SRCINC=}"
+SRCSUBLIBS=`echo "${SRCDIRS=}" | ${PERL} -pe "s|(\w+)|\1/libpslib\1.la|g"`
+AC_SUBST(SRCSUBLIBS,${SRCSUBLIBS=})
+AC_SUBST(SRCINC,${SRCINC=})
+AC_SUBST(ABS_SRCDIR,`pwd`) dnl assuming configure is run in top directory -- is there something better?
+AC_PROG_MAKE_SET
+
+dnl build tests at the same time as the source code
+AC_ARG_ENABLE(tests,
+  [AS_HELP_STRING(--enable-tests,build tests at same time as source)],
+  [AC_MSG_RESULT(test building enabled)
+   tests=true],
+   [tests=false])
+AM_CONDITIONAL(BUILD_TESTS, test x$tests = xtrue)
+
+dnl turn on mem leak backtracing 
+AC_ARG_ENABLE(backtrace,
+  [AS_HELP_STRING(--enable-backtrace, enable memory allocation backtracing)],
+  [AC_MSG_RESULT(memory allocation backtracing enabled)
+    AC_DEFINE([PS_MEM_BACKTRACE], 1, [Define to 1 if you want memory backtracing])
+    CFLAGS="${CFLAGS=} -rdynamic"
+  ]
+)
+AC_CHECK_FUNCS([backtrace])
+
+AC_LANG(C)
+AC_PROG_CC_C99
+dnl _GNU_SOURCE is needed for atleast strcasestr() with glibc
+AC_GNU_SOURCE
+AC_C_INLINE
+
+dnl enable largefile supports
+AC_SYS_LARGEFILE
+AC_FUNC_FSEEKO
+
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+dnl check the systems endianness
+AC_C_BIGENDIAN
+
+AC_PREFIX_DEFAULT([/usr/local])
+test "$prefix" = NONE && prefix=/usr/local
+
+dnl ----------------- pthread support --------------------
+
+dnl Check for how to link against pthreads
+dnl RHEL 4.0 was upset about the first arg to ACX_PTHREAD being NULL ([]) so
+dnl we've added in a worthless message to keep it happy
+dnl PTHREAD_CFLAGS /must/ also be used in the linking of an exe.  Without
+dnl adding PTHREAD_CFLAGS into PSLIB_LIBS we can't link exes on Linux.
+dnl Basically this means the entire method of only linking psLib's deps in the
+dnl final exe or library is flawed.
+
+ACX_PTHREAD(
+  [PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${PTHREAD_CFLAGS}"
+    PSLIB_LIBS="${PSLIB_LIBS=} ${PTHREAD_CFLAGS} ${PTHREAD_LIBS}"],
+  [AC_MSG_ERROR([pthread support is required to build psLib])]
+)
+
+dnl ----------------- MYSQL options --------------------
+
+AX_LIB_MYSQL([4.1.2],
+  [PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${MYSQL_CFLAGS} -DHAVE_PSDB"
+    PSLIB_LIBS="${PSLIB_LIBS=} ${MYSQL_LDFLAGS}"],
+  [PSLIB_CFLAGS="${PSLIB_CFLAGS=}"]
+)
+
+dnl ----------------- CFITSIO options --------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(cfitsio,
+[AS_HELP_STRING(--with-cfitsio=DIR,Specify location of CFITSIO.)],
+[CFITSIO_CFLAGS="-I$withval/include"
+ CFITSIO_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(cfitsio-include,
+[AS_HELP_STRING(--with-cfitsio-include=DIR,Specify CFITSIO include directory.)],
+[CFITSIO_CFLAGS="-I$withval"])
+AC_ARG_WITH(cfitsio-lib,
+[AS_HELP_STRING(--with-cfitsio-lib=DIR,Specify CFITSIO library directory.)],
+[CFITSIO_LDFLAGS="-L$withval"])
+PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${CFITSIO_CFLAGS}"
+PSLIB_LIBS="${PSLIB_LIBS=} $CFITSIO_LDFLAGS -lcfitsio -lm"
+
+CFLAGS="${CFLAGS=} ${CFITSIO_CFLAGS}"
+LDFLAGS="${LDFLAGS=} ${CFITSIO_LDFLAGS}"
+
+dnl Solaris needs to suck in these symbols from unusual locations
+AC_SEARCH_LIBS([gethostbyname], [nsl])
+AC_SEARCH_LIBS([socket], [socket])
+
+AC_CHECK_HEADERS([fitsio.h],[],
+   [AC_MSG_ERROR([CFITSIO headers not found.  Obtain CFITSIO at  http://heasarc.gsfc.nasa.gov/docs/software/fitsio or use --with-cfitsio to specify location.])]
+)
+TMP_LIBS=${LIBS}
+AC_CHECK_LIB(cfitsio,ffopen,[],
+  [AC_MSG_ERROR([CFITSIO library not found.  Obtain it at  http://heasarc.gsfc.nasa.gov/docs/software/fitsio or use --with-cfitsio to specify location.])],[-lm]
+)
+dnl Now check if CFITSIO supports fits_open_diskfile, i.e., is at least version 2.501
+AC_CHECK_LIB(cfitsio,ffdkopn,
+  [CFITSIO_DISKFILE=1],
+  [AC_MSG_WARN([The CFITSIO library version is rather old.  Suggested version is 2.501 or greater.])
+   CFITSIO_DISKFILE=0],[-lm]
+)
+
+AC_DEFINE_UNQUOTED([CFITSIO_DISKFILE],${CFITSIO_DISKFILE},[Define to 1 if you have fits_open_diskfile in CFITSIO])
+AC_SUBST([CFITSIO_CFLAGS])
+
+dnl restore the LIBS/CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ FFTW3 options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(fftw3,
+[AS_HELP_STRING(--with-fftw3=DIR,Specify location of FFTW version 3.)],
+[FFTW3_CFLAGS="-I$withval/include"
+ FFTW3_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(fftw3-include,
+[AS_HELP_STRING(--with-fftw3-include=DIR,Specify FFTW version 3 include directory.)],
+[FFTW3_CFLAGS="-I$withval"])
+AC_ARG_WITH(fftw3-lib,
+[AS_HELP_STRING(--with-fftw3-lib=DIR,Specify FFTW version 3 library directory.)],
+[FFTW3_LDFLAGS="-L$withval"])
+PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${FFTW3_CFLAGS}"
+PSLIB_LIBS="${PSLIB_LIBS=} $FFTW3_LDFLAGS -lfftw3f"
+
+CFLAGS="${CFLAGS} ${FFTW3_CFLAGS}"
+CPPFLAGS=${CFLAGS}
+LDFLAGS="${LDFLAGS} ${FFTW3_LDFLAGS}"
+
+AC_CHECK_LIB(fftw3f,fftwf_plan_dft_2d,[],
+  [AC_MSG_ERROR([FFTW version 3 (--enable-float) library not found.  Obtain it at http://www.fftw.org/ or use --with-fftw3 to specify location.])])
+
+FFTW_THREADS=0
+AC_CHECK_LIB(fftw3f,fftwf_init_threads,[FFTW_THREADS=1],
+  [AC_CHECK_LIB(fftw3f_threads,fftwf_init_threads, [
+    FFTW_THREADS=1
+    PSLIB_LIBS="${PSLIB_LIBS=} -lfftw3f_threads"],
+   AC_MSG_WARN([FFTW version 3 not compiled with thread support (--enable-threads)]),[-lm]
+   )],[-lm]
+)
+
+dnl AC_CHECK_LIB(fftw3f,fftwf_plan_dft_2d,[],
+dnl   [AC_MSG_ERROR([FFTW version 3 (--enable-float) library not found.  Obtain it at http://www.fftw.org/ or use --with-fftw3 to specify location.])],[-lm]
+dnl )
+
+AC_CHECK_HEADERS([fftw3.h],[],
+  [AC_MSG_ERROR([FFTW version 3 (--enable-float) headers not found.  Obtain it at http://www.fftw.org/ or use --with-fftw3 to specify location.])]
+)
+
+AC_DEFINE_UNQUOTED([HAVE_FFTW_THREADS],${FFTW_THREADS},[Define to 1 if you have FFTW compiled with thread support])
+
+AC_SUBST([FFTW3_CFLAGS])
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl -------------------- GSL options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(gsl-config,
+[AS_HELP_STRING(--with-gsl-config=FILE,Specify location of gsl-config.)],
+[GSL_CONFIG=$withval],
+[GSL_CONFIG=`which gsl-config`])
+AC_CHECK_FILE($GSL_CONFIG,[],
+    [AC_MSG_ERROR([GSL is required.  Obtain it at http://www.gnu.org/software/gsl or use --with-gsl-config to specify location.])])
+
+AC_MSG_CHECKING([GSL cflags])
+GSL_CFLAGS="`${GSL_CONFIG} --cflags`"
+AC_MSG_RESULT([${GSL_CFLAGS}])
+
+AC_MSG_CHECKING([GSL ldflags])
+GSL_LDFLAGS="`${GSL_CONFIG} --libs`"
+AC_MSG_RESULT([${GSL_LDFLAGS}])
+
+PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${GSL_CFLAGS}"
+PSLIB_LIBS="${PSLIB_LIBS=} ${GSL_LDFLAGS}"
+
+AC_SUBST([GSL_CFLAGS])
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ libjpeg options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(jpeg,
+[AS_HELP_STRING(--with-jpeg=DIR,Specify location of libjpeg.)],
+[JPEG_CFLAGS="-I$withval/include"
+ JPEG_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(jpeg-include,
+[AS_HELP_STRING(--with-jpeg-include=DIR,Specify libjpeg include directory.)],
+[JPEG_CFLAGS="-I$withval"])
+AC_ARG_WITH(jpeg-lib,
+[AS_HELP_STRING(--with-jpeg-lib=DIR,Specify libjpeg library directory.)],
+[JPEG_LDFLAGS="-L$withval"])
+
+CFLAGS="${CFLAGS} ${JPEG_CFLAGS}"
+CPPFLAGS=${CFLAGS}
+LDFLAGS="${LDFLAGS} ${JPEG_LDFLAGS}"
+
+AC_CHECK_HEADERS([jpeglib.h],[PSLIB_CFLAGS="$PSLIB_CFLAGS $JPEG_CFLAGS"
+			      AC_SUBST(JPEG_CFLAGS)],
+  [AC_MSG_ERROR([libjpeg headers not found.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
+)
+
+AC_CHECK_LIB(jpeg,jpeg_CreateCompress,[PSLIB_LIBS="$PSLIB_LIBS $JPEG_LDFLAGS -ljpeg"],
+  [AC_MSG_ERROR([libjpeg library not found.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
+)
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------- XML2 options ---------------------
+dnl AC_ARG_WITH(xml2-config,
+dnl [AS_HELP_STRING(--with-xml2-config=FILE,Specify location of xml2-config.)],
+dnl [XML_CONFIG=$withval],
+dnl [XML_CONFIG=`which xml2-config`])
+dnl AC_CHECK_FILE($XML_CONFIG,[],
+dnl     [AC_MSG_ERROR([GNOME XML C parser is required.  Obtain it at http://www.xmlsoft.org or use --with-xml2-config to specify location.])])
+dnl
+dnl AC_MSG_CHECKING([xml2 version])
+dnl XML_VERSION=`xml2-config --version`
+dnl XML_VERSION_major=`echo $XML_VERSION | ${PERL} -pe 's|^(\d+).*|\1|'`
+dnl XML_VERSION_minor=`echo $XML_VERSION | ${PERL} -pe 's|^(\d+)\.(\d+).*|\2|'`
+dnl dnl First test the minimum version of 2.6
+dnl if test $XML_VERSION_major -lt 2 || ( test $XML_VERSION_major -eq 2 && test $XML_VERSION_minor -lt 6 )
+dnl then
+dnl 	AC_MSG_ERROR([requires libxml2 2.6.0 or greater, found $XML_VERSION.  Install newer version or use --with-xml2-config to specify another location.])
+dnl else
+dnl     AC_MSG_RESULT([$XML_VERSION... yes])
+dnl fi
+dnl
+dnl AC_MSG_CHECKING([xml2 cflags])
+dnl XML_CFLAGS="`${XML_CONFIG} --cflags`"
+dnl AC_MSG_RESULT([${XML_CFLAGS}])
+dnl
+dnl AC_MSG_CHECKING([xml2 ldflags])
+dnl XML_LDFLAGS="`${XML_CONFIG} --libs`"
+dnl AC_MSG_RESULT([${XML_LDFLAGS}])
+dnl
+dnl PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${XML_CFLAGS}"
+dnl PSLIB_LIBS="${PSLIB_LIBS=} ${XML_LDFLAGS}"
+dnl
+dnl AC_SUBST([XML_CFLAGS])
+
+dnl ------------------- SWIG options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_ENABLE(perlmodule,
+ [AS_HELP_STRING(--enable-perlmodule,Enables creation of perl module)],
+ [SWIG_REQ="$enableval"],
+ [SWIG_REQ="no"])
+
+if test "$SWIG_REQ" == "yes"
+then
+  AC_MSG_RESULT([enable building perl module])
+  AC_ARG_WITH(swig,
+  [AS_HELP_STRING(--with-swig=FILE,Specify location of SWIG executable.)],
+  [SWIG=$withval],
+  [SWIG=`which swig`])
+  AC_SUBST(SWIG,$SWIG)
+
+  AC_CHECK_FILE($SWIG,[],
+     [AC_MSG_ERROR([SWIG is required.  Obtain it at www.swig.org or use --with-swig to specify its install location.])])
+  SUBDIR="${SUBDIR=} swig"
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ---------------- PERLPREFIX option -------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+  AC_ARG_WITH(perlprefix,
+    [AS_HELP_STRING(--with-perlprefix=DIR,Specify prefix of Perl Module's installation. (default=PREFIX))],
+    [PERL_PREFIX="$withval"],
+    [PERL_PREFIX="$prefix"])
+    AC_SUBST(PERL_PREFIX,$PERL_PREFIX)
+    AC_SUBST(PERL_INSTALLSYTLE,[`perl '-V:installstyle'`])
+
+else
+  AC_MSG_RESULT([disable building perl module])
+fi
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl for MacOSX suppport -- check if sqrtf is in mx library.
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_CHECK_LIB(mx,sqrtf,
+  [PSLIB_LIBS="${PSLIB_LIBS=} -lmx"])
+LIBS=${TMP_LIBS}
+AC_CHECK_FUNC(atoll, [],
+  [AC_MSG_ERROR([The C99 function, atoll, is required; update your compiler version?])])
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl doxygen doc generation is very, very slow so we're turing it off by default
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_ENABLE(doxygen,
+  [AS_HELP_STRING(--enable-doxygen ,enable manpage generation)],
+  [AC_MSG_RESULT(doxygen enabled)
+    AC_PATH_PROG([DOXYGEN], [doxygen], [])
+  ],
+  [AC_MSG_RESULT([doxygen disabled])
+    doxygen=off
+  ]
+)
+AM_CONDITIONAL([HAVE_DOXYGEN], test -n "$DOXYGEN" -a "x$doxygen" != "xoff")
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------- enable -Werror after all of the probes have run ---------
+IPP_STDOPTS
+
+CFLAGS="${CFLAGS} -Wall -Werror"
+dnl enable POSIX/XSI and C99 compliance
+CFLAGS="${CFLAGS=} -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L"
+
+dnl ---------------- export variables --------------------
+
+AC_SUBST(SUBDIR,[$SUBDIR])
+AC_SUBST(SRCDIRS,[$SRCDIRS])
+AC_SUBST(PSLIB_LIBS,[$PSLIB_LIBS])
+AC_SUBST(PSLIB_CFLAGS,[$PSLIB_CFLAGS])
+
+AC_SUBST(PS_LANG,[en])
+
+AC_CONFIG_FILES([
+  Doxyfile
+  Makefile
+  etc/Makefile
+  m4/Makefile
+  pslib-config
+  pslib.pc
+  share/Makefile
+  src/Makefile
+  src/astro/Makefile
+  src/db/Makefile
+  src/fft/Makefile
+  src/fits/Makefile
+  src/imageops/Makefile
+  src/jpeg/Makefile
+  src/math/Makefile
+  src/mathtypes/Makefile
+  src/sys/Makefile
+  src/types/Makefile
+  test/Makefile
+  test/astro/Makefile
+  test/db/Makefile
+  test/fft/Makefile
+  test/fits/Makefile
+  test/imageops/Makefile
+  test/jpeg/Makefile
+  test/math/Makefile
+  test/mathtypes/Makefile
+  test/pstap/Makefile
+  test/pstap/src/Makefile
+  test/sys/Makefile
+  test/types/Makefile
+  utils/Makefile
+])
+dnl src/xml/Makefile
+dnl test/xml/Makefile
+
+
+#if test "$SWIG_REQ" == "yes"
+#then
+#  AC_CONFIG_FILES([ swig/Makefile ])
+#fi
+
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/psLib/etc/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/etc/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/etc/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+pslib.config
Index: /tags/sj_tags/sj_root_20080929/psLib/etc/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/etc/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/etc/Makefile.am	(revision 22322)
@@ -0,0 +1,15 @@
+BUILT_SOURCES = pslib.config
+EXTRA_DIST = pslib.config.template
+
+# this is done instead of using autoconf to delay the expansion of datadir
+# until --prefix is known
+pslib.config: $(srcdir)/pslib.config.template
+	$(PERL) -pe 's|DATADIR|$(pkgdatadir)|' $? > $@
+
+pslibconfdir = $(sysconfdir)/@PACKAGE_NAME@
+pslibconf_DATA = pslib.config
+
+#install-data-hook:
+#	chmod 0755 $(pslibconfdir)
+
+CLEANFILES = pslib.config
Index: /tags/sj_tags/sj_root_20080929/psLib/etc/pslib.config.template
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/etc/pslib.config.template	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/etc/pslib.config.template	(revision 22322)
@@ -0,0 +1,43 @@
+# This configuration file specifies values required by psLib.
+#
+# psTime settings
+#
+psLib.time.tables.dir STR DATADIR
+psLib.time.tables.n S32 4                                                               # Number of time tables
+psLib.time.tables.files STR daily.dat eopc01_1900_2004.dat finals_all.dat tai_utc.dat
+psLib.time.tables.format STR %lf %lf %lf %lf %lf %lf %lf, %lf %lf %lf %lf, %lf %lf %lf %lf %lf %lf %lf, %lf %lf %lf %lf
+@psLib.time.tables.index S32 0, 0, 0, 0
+@psLib.time.tables.from F64 53403.0, 15020.0, 41684.0 2437300.5                         # Valid from these dates
+@psLib.time.tables.to F64 53583.0, 53258.0, 53627.0 2451179.5                           # Valid to these dates
+psLib.time.before.xp F64 0.0                                                            # Value of XP for before the earliest MJD
+psLib.time.before.yp F64 0.0                                                            # Value of YP for before the earliest MJD
+psLib.time.before.dut F64 0.0                                                           # Value of UT1-UTC for before the earliest MJD
+# Now follows formulae for predicting ahead of the most recent available table entry.
+# xp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# yp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# A = 2*pi*(MJD - pslib.time.predict.mjd)/365.25
+# C = 2*pi*(MJD - pslib.time.predict.mjd)/435.0
+# dut = ut1-utc = [0] + [1]*(MJD - [2]) - (UT2-UT1)
+# ut2-ut1 = 0.022 sin(2*pi*T) - 0.012 cos(2*pi*T) - 0.006 sin(4*pi*T) + 0.007 cos(4*pi*T)
+# T = 2000.0 + (MJD - 51544.03)/365.2422
+@psLib.time.predict.xp F64 0.0569, 0.0555, -0.0200, 0.0513, 0.1483
+@psLib.time.predict.yp F64 0.3498, -0.0176, -0.0498, 0.1483, -0.0513
+psLib.time.predict.mjd F64 53257.0
+@psLib.time.predict.dut F64 -0.4944, -0.00023, 53262.0
+#
+# psEOC settings
+#
+psLib.eoc.precession.table.format STR  %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf
+psLib.eoc.precession.iers.table.format STR %lf %lf %lf %lf %lf
+psLib.eoc.precession.finals.table.format STR %lf %lf %lf %lf %lf %lf %lf
+psLib.eoc.precession.table.file.x STR DATADIR/tab5.2a.dat
+psLib.eoc.precession.table.file.y STR DATADIR/tab5.2b.dat
+psLib.eoc.precession.table.file.s STR DATADIR/tab5.2c.dat
+psLib.eoc.precession.table.file.iers STR DATADIR/finals2000A.dat
+psLib.eoc.precession.table.file.final STR DATADIR/finals_all.dat
+@psLib.eoc.precession.poly.x F64 -16616.99 2004191742.88 -427219.05 -198620.54 -46.05 5.98
+@psLib.eoc.precession.poly.y F64 -6950.78 -25381.99 -22407250.99 1842.28 1113.06 0.99
+@psLib.eoc.precession.poly.s F64 94.0 3808.35 -119.94 -72574.09 27.70 15.61
+
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/m4/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/m4/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/m4/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/psLib/m4/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/m4/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/m4/Makefile.am	(revision 22322)
@@ -0,0 +1,9 @@
+installed_m4 = ipp_stdopts.m4
+
+EXTRA_DIST= \
+	acx_pthread.m4 \
+    ax_lib_mysql.m4 \
+	$(installed_m4)
+
+m4datadir = $(datadir)/aclocal
+m4data_DATA = $(installed_m4)
Index: /tags/sj_tags/sj_root_20080929/psLib/m4/acx_pthread.m4
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/m4/acx_pthread.m4	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/m4/acx_pthread.m4	(revision 22322)
@@ -0,0 +1,242 @@
+dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+dnl
+dnl @summary figure out how to build C programs using POSIX threads
+dnl
+dnl This macro figures out how to build C programs using POSIX threads.
+dnl It sets the PTHREAD_LIBS output variable to the threads library and
+dnl linker flags, and the PTHREAD_CFLAGS output variable to any special
+dnl C compiler flags that are needed. (The user can also force certain
+dnl compiler flags/libs to be tested by setting these environment
+dnl variables.)
+dnl
+dnl Also sets PTHREAD_CC to any special C compiler that is needed for
+dnl multi-threaded programs (defaults to the value of CC otherwise).
+dnl (This is necessary on AIX to use the special cc_r compiler alias.)
+dnl
+dnl NOTE: You are assumed to not only compile your program with these
+dnl flags, but also link it with them as well. e.g. you should link
+dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
+dnl $LIBS
+dnl
+dnl If you are only building threads programs, you may wish to use
+dnl these variables in your default LIBS, CFLAGS, and CC:
+dnl
+dnl        LIBS="$PTHREAD_LIBS $LIBS"
+dnl        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+dnl        CC="$PTHREAD_CC"
+dnl
+dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
+dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
+dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+dnl
+dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
+dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to
+dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the
+dnl default action will define HAVE_PTHREAD.
+dnl
+dnl Please let the authors know if this macro fails on any platform, or
+dnl if you have any other suggestions or comments. This macro was based
+dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with
+dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros
+dnl posted by Alejandro Forero Cuervo to the autoconf macro repository.
+dnl We are also grateful for the helpful feedback of numerous users.
+dnl
+dnl @category InstalledPackages
+dnl @author Steven G. Johnson <stevenj@alum.mit.edu>
+dnl @version 2006-05-29
+dnl @license GPLWithACException
+
+AC_DEFUN([ACX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_SAVE
+AC_LANG_C
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+        AC_MSG_RESULT($acx_pthread_ok)
+        if test x"$acx_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+#      ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+        *solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+        ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+		pthread-config)
+		AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
+		if test x"$acx_pthread_config" = xno; then continue; fi
+		PTHREAD_CFLAGS="`pthread-config --cflags`"
+		PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+		;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_TRY_LINK([#include <pthread.h>],
+                    [pthread_t th; pthread_join(th, 0);
+                     pthread_attr_init(0); pthread_cleanup_push(0, 0);
+                     pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+                    [acx_pthread_ok=yes])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT($acx_pthread_ok)
+        if test "x$acx_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+	AC_MSG_CHECKING([for joinable pthread attribute])
+	attr_name=unknown
+	for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+	    AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
+                        [attr_name=$attr; break])
+	done
+        AC_MSG_RESULT($attr_name)
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case "${host_cpu}-${host_os}" in
+            *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+            *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+        esac
+        AC_MSG_RESULT(${flag})
+        if test "x$flag" != xno; then
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: must compile with xlc_r or cc_r
+	if test x"$GCC" != xyes; then
+          AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+        else
+          PTHREAD_CC=$CC
+	fi
+else
+        PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        :
+else
+        acx_pthread_ok=no
+        $2
+fi
+AC_LANG_RESTORE
+])dnl ACX_PTHREAD
Index: /tags/sj_tags/sj_root_20080929/psLib/m4/ax_lib_mysql.m4
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/m4/ax_lib_mysql.m4	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/m4/ax_lib_mysql.m4	(revision 22322)
@@ -0,0 +1,158 @@
+##### http://autoconf-archive.cryp.to/ax_lib_mysql.html
+#
+# SYNOPSIS
+#
+#   AX_LIB_MYSQL([MINIMUM-VERSION[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]])
+#
+# DESCRIPTION
+#
+#   This macro provides tests of availability of MySQL client library
+#   of particular version or newer.
+#
+#   AX_LIB_MYSQL macro takes only one argument which is optional. If
+#   there is no required version passed, then macro does not run
+#   version test.
+#
+#   The --with-mysql option takes one of three possible values:
+#
+#   no - do not check for MySQL client library
+#
+#   yes - do check for MySQL library in standard locations
+#   (mysql_config should be in the PATH)
+#
+#   path - complete path to mysql_config utility, use this option if
+#   mysql_config can't be found in the PATH
+#
+#   This macro calls:
+#
+#     AC_SUBST(MYSQL_CFLAGS)
+#     AC_SUBST(MYSQL_LDFLAGS)
+#     AC_SUBST(MYSQL_VERSION)
+#
+#   And sets:
+#
+#     HAVE_MYSQL
+#
+# LAST MODIFICATION
+#
+#   2006-07-16
+#
+# COPYLEFT
+#
+#   Copyright (c) 2006 Mateusz Loskot <mateusz@loskot.net>
+#
+#   Copying and distribution of this file, with or without
+#   modification, are permitted in any medium without royalty provided
+#   the copyright notice and this notice are preserved.
+
+AC_DEFUN([AX_LIB_MYSQL],
+[
+    AC_ARG_WITH([mysql],
+        AC_HELP_STRING([--with-mysql=@<:@ARG@:>@],
+            [use MySQL client library @<:@default=yes@:>@, optionally specify path to mysql_config]
+        ),
+        [
+        if test "$withval" = "no"; then
+            want_mysql="no"
+        elif test "$withval" = "yes"; then
+            want_mysql="yes"
+        else
+            want_mysql="yes"
+            MYSQL_CONFIG="$withval"
+        fi
+        ],
+        [want_mysql="yes"]
+    )
+
+    MYSQL_CFLAGS=""
+    MYSQL_LDFLAGS=""
+    MYSQL_VERSION=""
+
+    dnl
+    dnl Check MySQL libraries (libpq)
+    dnl
+
+    if test "$want_mysql" = "yes"; then
+
+        if test -z "$MYSQL_CONFIG" -o test; then
+            AC_PATH_PROG([MYSQL_CONFIG], [mysql_config], [no])
+        fi
+
+        if test "$MYSQL_CONFIG" != "no"; then
+            AC_MSG_CHECKING([for MySQL libraries])
+
+            MYSQL_CFLAGS="`$MYSQL_CONFIG --cflags`"
+            MYSQL_LDFLAGS="`$MYSQL_CONFIG --libs`"
+
+            MYSQL_VERSION=`$MYSQL_CONFIG --version`
+
+            AC_DEFINE([HAVE_MYSQL], [1],
+                [Define to 1 if MySQL libraries are available])
+
+            found_mysql="yes"
+            AC_MSG_RESULT([yes])
+        else
+            found_mysql="no"
+            AC_MSG_RESULT([no])
+        fi
+    fi
+
+    dnl
+    dnl Check if required version of MySQL is available
+    dnl
+
+
+    mysql_version_req=ifelse([$1], [], [], [$1])
+
+    if test "$found_mysql" = "yes" -a -n "$mysql_version_req"; then
+
+        AC_MSG_CHECKING([if MySQL version is >= $mysql_version_req])
+
+        dnl Decompose required version string of MySQL
+        dnl and calculate its number representation
+        mysql_version_req_major=`expr $mysql_version_req : '\([[0-9]]*\)'`
+        mysql_version_req_minor=`expr $mysql_version_req : '[[0-9]]*\.\([[0-9]]*\)'`
+        mysql_version_req_micro=`expr $mysql_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
+        if test "x$mysql_version_req_micro" = "x"; then
+            mysql_version_req_micro="0"
+        fi
+
+        mysql_version_req_number=`expr $mysql_version_req_major \* 1000000 \
+                                   \+ $mysql_version_req_minor \* 1000 \
+                                   \+ $mysql_version_req_micro`
+
+        dnl Decompose version string of installed MySQL
+        dnl and calculate its number representation
+        mysql_version_major=`expr $MYSQL_VERSION : '\([[0-9]]*\)'`
+        mysql_version_minor=`expr $MYSQL_VERSION : '[[0-9]]*\.\([[0-9]]*\)'`
+        mysql_version_micro=`expr $MYSQL_VERSION : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
+        if test "x$mysql_version_micro" = "x"; then
+            mysql_version_micro="0"
+        fi
+
+        mysql_version_number=`expr $mysql_version_major \* 1000000 \
+                                   \+ $mysql_version_minor \* 1000 \
+                                   \+ $mysql_version_micro`
+
+        mysql_version_check=`expr $mysql_version_number \>\= $mysql_version_req_number`
+        if test "$mysql_version_check" = "1"; then
+            AC_MSG_RESULT([yes])
+        else
+            AC_MSG_RESULT([no])
+        fi
+    fi
+
+    AC_SUBST([MYSQL_VERSION])
+    AC_SUBST([MYSQL_CFLAGS])
+    AC_SUBST([MYSQL_LDFLAGS])
+
+dnl Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test "$mysql_version_check" = "1"; then
+    $2
+    :
+else
+    $3
+    :
+fi
+
+]) dnl ax_lib_mysql
Index: /tags/sj_tags/sj_root_20080929/psLib/m4/ipp_stdopts.m4
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/m4/ipp_stdopts.m4	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/m4/ipp_stdopts.m4	(revision 22322)
@@ -0,0 +1,38 @@
+
+AC_DEFUN([IPP_STDCFLAGS],
+[
+    AC_ARG_ENABLE(optimize,
+      [AS_HELP_STRING(--enable-optimize,enable compiler optimization)],
+      [AC_MSG_RESULT(compile optimization enabled)
+       CFLAGS="-pipe -O2 -g -DPS_NO_TRACE"],
+      [AC_MSG_RESULT([compile optimization disabled])
+        if test x"${CFLAGS}" == x; then
+          CFLAGS="-pipe -O0 -g"
+        fi
+      ]
+    )
+])
+
+AC_DEFUN([IPP_STDOPTS],
+[
+    dnl handle profiler building
+    AC_ARG_ENABLE(profile, [AS_HELP_STRING(--enable-profile,enable compiler profiler information inclusion)],
+      [AC_MSG_RESULT([profiling enabled, check that you also did --disable-shared])
+       CFLAGS="${CFLAGS=} -pg -g"
+       LDFLAGS="${LDFLAGS=} -pg"]
+    )
+
+    dnl handle path coverage checking
+    AC_ARG_ENABLE(coverage,
+      [AS_HELP_STRING(--enable-coverage,enable path coverage checking)],
+      [AC_MSG_RESULT(path coverage enabled)
+       CFLAGS="${CFLAGS=} -lgcov -fprofile-arcs -ftest-coverage -pg"]
+    )
+
+    dnl turn off trace messages
+    AC_ARG_ENABLE(trace,
+      [AS_HELP_STRING(--disable-trace,disable psTrace functionality)],
+      [AC_MSG_RESULT(psTrace disabled)
+       CFLAGS="${CFLAGS=} -DPS_NO_TRACE"]
+    )
+])
Index: /tags/sj_tags/sj_root_20080929/psLib/pslib-config.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/pslib-config.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/pslib-config.in	(revision 22322)
@@ -0,0 +1,92 @@
+#! /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/@PACKAGE_NAME@
+top_srcdir=@ABS_SRCDIR@
+@PERL_INSTALLSYTLE@
+
+usage()
+{
+    cat <<EOF
+Usage: pslib-config [OPTION]
+
+Known values for OPTION are:
+
+  --prefix		print psLib installation prefix
+  --perlmodule  	print psLib perl module's installation location
+  --libs		print library linking information
+  --cflags		print pre-processor and compiler flags
+  --build-libs		print library linking information to the build (non-installed) version
+  --build-cflags	print pre-processor and compiler flags to the build (non-installed) version
+  --help		display this help and exit
+  --version		output version information
+
+EOF
+
+    exit $1
+}
+
+if test $# -eq 0; then
+    usage 1
+fi
+
+cflags=false
+libs=false
+
+while test $# -gt 0; do
+    case "$1" in
+    -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+    *) optarg= ;;
+    esac
+
+    case "$1" in
+    --prefix=*)
+	prefix=$optarg
+	;;
+
+    --prefix)
+	echo $prefix
+	;;
+
+    --version)
+	echo @VERSION@
+	exit 0
+	;;
+
+    --help)
+	usage 0
+	;;
+
+    --cflags)
+       	echo -I${includedir} @PSLIB_CFLAGS@
+       	;;
+
+    --libs)
+       	echo -L${libdir} -lpslib @PSLIB_LIBS@
+       	;;
+
+    --build-cflags)
+       	echo @SRCINC@ @PSLIB_CFLAGS@
+       	;;
+
+    --build-libs)
+       	echo -L@ABS_SRCDIR@/src/.libs -lpslib @PSLIB_LIBS@
+       	;;
+
+    --deps)
+       	echo @PSLIB_LIBS@
+       	;;
+    --perlmodule)
+       	echo @PERL_PREFIX@/$installstyle
+       	;;
+    *)
+	usage
+	exit 1
+	;;
+    esac
+    shift
+done
+
+exit 0
Index: /tags/sj_tags/sj_root_20080929/psLib/pslib.kdevelop
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/pslib.kdevelop	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/pslib.kdevelop	(revision 22322)
@@ -0,0 +1,194 @@
+<?xml version = '1.0'?>
+<kdevelop>
+  <general>
+    <author>Robert Daniel DeSonia</author>
+    <email>robert.desonia@mhpcc.hpc.mil</email>
+    <version>1.6</version>
+    <projectmanagement>KDevAutoProject</projectmanagement>
+    <primarylanguage>C</primarylanguage>
+    <keywords>
+      <keyword>C</keyword>
+      <keyword>Code</keyword>
+    </keywords>
+    <ignoreparts/>
+    <projectdirectory>.</projectdirectory>
+    <absoluteprojectpath>false</absoluteprojectpath>
+    <description/>
+    <secondaryLanguages/>
+  </general>
+  <kdevautoproject>
+    <general>
+      <activetarget>src/pslib</activetarget>
+      <useconfiguration>default</useconfiguration>
+    </general>
+    <run>
+      <mainprogram>src/pslib</mainprogram>
+      <terminal>true</terminal>
+      <directoryradio>executable</directoryradio>
+    </run>
+    <configurations>
+      <optimized>
+        <builddir>optimized</builddir>
+        <ccompiler>kdevgccoptions</ccompiler>
+        <cxxcompiler>kdevgppoptions</cxxcompiler>
+        <f77compiler>kdevg77options</f77compiler>
+        <cflags>-O2 -g0 </cflags>
+      </optimized>
+      <debug>
+        <configargs>--enable-debug=full</configargs>
+        <builddir>debug</builddir>
+        <ccompiler>kdevgccoptions</ccompiler>
+        <cxxcompiler>kdevgppoptions</cxxcompiler>
+        <f77compiler>kdevg77options</f77compiler>
+        <cflags>-O0 -g3 </cflags>
+      </debug>
+    </configurations>
+    <make>
+      <envvars>
+        <envvar value="1" name="WANT_AUTOCONF_2_5" />
+        <envvar value="1" name="WANT_AUTOMAKE_1_6" />
+      </envvars>
+    </make>
+  </kdevautoproject>
+  <kdevdoctreeview>
+    <ignoretocs>
+      <toc>ada</toc>
+      <toc>ada_bugs_gcc</toc>
+      <toc>bash</toc>
+      <toc>bash_bugs</toc>
+      <toc>clanlib</toc>
+      <toc>w3c-dom-level2-html</toc>
+      <toc>fortran_bugs_gcc</toc>
+      <toc>gnome1</toc>
+      <toc>gnustep</toc>
+      <toc>gtk</toc>
+      <toc>gtk_bugs</toc>
+      <toc>haskell</toc>
+      <toc>haskell_bugs_ghc</toc>
+      <toc>java_bugs_gcc</toc>
+      <toc>java_bugs_sun</toc>
+      <toc>kde2book</toc>
+      <toc>libstdc++</toc>
+      <toc>opengl</toc>
+      <toc>pascal_bugs_fp</toc>
+      <toc>php</toc>
+      <toc>php_bugs</toc>
+      <toc>perl</toc>
+      <toc>perl_bugs</toc>
+      <toc>python</toc>
+      <toc>python_bugs</toc>
+      <toc>qt-kdev3</toc>
+      <toc>ruby</toc>
+      <toc>ruby_bugs</toc>
+      <toc>sdl</toc>
+      <toc>stl</toc>
+      <toc>w3c-svg</toc>
+      <toc>sw</toc>
+      <toc>w3c-uaag10</toc>
+      <toc>wxwidgets_bugs</toc>
+    </ignoretocs>
+    <ignoreqt_xml>
+      <toc>Guide to the Qt Translation Tools</toc>
+      <toc>Qt Assistant Manual</toc>
+      <toc>Qt Designer Manual</toc>
+      <toc>Qt Reference Documentation</toc>
+      <toc>qmake User Guide</toc>
+    </ignoreqt_xml>
+    <ignoredoxygen>
+      <toc>KDE Libraries (Doxygen)</toc>
+    </ignoredoxygen>
+  </kdevdoctreeview>
+  <kdevfilecreate>
+    <useglobaltypes>
+      <type ext="c" />
+      <type ext="h" />
+    </useglobaltypes>
+  </kdevfilecreate>
+  <kdevfileview>
+    <groups>
+      <group pattern="*.h" name="Header files" />
+      <group pattern="*.c" name="Source files" />
+      <hidenonprojectfiles>false</hidenonprojectfiles>
+      <hidenonlocation>false</hidenonlocation>
+    </groups>
+    <tree>
+      <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+      <hidenonprojectfiles>false</hidenonprojectfiles>
+      <showvcsfields>false</showvcsfields>
+    </tree>
+  </kdevfileview>
+  <kdevdocumentation>
+    <projectdoc>
+      <docsystem>Doxygen Documentation Collection</docsystem>
+      <docurl>${APPNAMELC}.tag</docurl>
+    </projectdoc>
+  </kdevdocumentation>
+  <substmap>
+    <APPNAME>psLib</APPNAME>
+    <APPNAMELC>pslib</APPNAMELC>
+    <APPNAMEUC>PSLIB</APPNAMEUC>
+    <AUTHOR>Robert Daniel DeSonia</AUTHOR>
+    <CFLAGS/>
+    <EMAIL>robert.desonia@mhpcc.hpc.mil</EMAIL>
+    <LICENSE>GPL</LICENSE>
+    <LICENSEFILE>COPYING</LICENSEFILE>
+    <OPT_INCS>-I/usr/include</OPT_INCS>
+    <OPT_LIBS>-lm</OPT_LIBS>
+    <VERSION>1.5</VERSION>
+    <dest>/home/desonia/pslib</dest>
+  </substmap>
+  <kdevcppsupport>
+    <references/>
+    <codecompletion>
+      <includeGlobalFunctions>true</includeGlobalFunctions>
+      <includeTypes>true</includeTypes>
+      <includeEnums>true</includeEnums>
+      <includeTypedefs>false</includeTypedefs>
+      <automaticCodeCompletion>true</automaticCodeCompletion>
+      <automaticArgumentsHint>true</automaticArgumentsHint>
+      <automaticHeaderCompletion>true</automaticHeaderCompletion>
+      <codeCompletionDelay>250</codeCompletionDelay>
+      <argumentsHintDelay>400</argumentsHintDelay>
+      <headerCompletionDelay>250</headerCompletionDelay>
+    </codecompletion>
+    <creategettersetter>
+      <prefixGet/>
+      <prefixSet>set</prefixSet>
+      <prefixVariable>m_,_</prefixVariable>
+      <parameterName>theValue</parameterName>
+      <inlineGet>true</inlineGet>
+      <inlineSet>true</inlineSet>
+    </creategettersetter>
+  </kdevcppsupport>
+  <cppsupportpart>
+    <filetemplates>
+      <interfacesuffix>.h</interfacesuffix>
+      <implementationsuffix>.cpp</implementationsuffix>
+    </filetemplates>
+  </cppsupportpart>
+  <kdevdebugger>
+    <general>
+      <programargs/>
+      <gdbpath/>
+      <dbgshell>libtool</dbgshell>
+      <configGdbScript/>
+      <runShellScript/>
+      <runGdbScript/>
+      <breakonloadinglibs>true</breakonloadinglibs>
+      <separatetty>false</separatetty>
+      <floatingtoolbar>false</floatingtoolbar>
+    </general>
+    <display>
+      <staticmembers>false</staticmembers>
+      <demanglenames>true</demanglenames>
+      <outputradix>10</outputradix>
+    </display>
+  </kdevdebugger>
+  <kdevcvsservice>
+    <recursivewhenupdate>true</recursivewhenupdate>
+    <prunedirswhenupdate>true</prunedirswhenupdate>
+    <createdirswhenupdate>true</createdirswhenupdate>
+    <recursivewhencommitremove>true</recursivewhencommitremove>
+    <revertoptions>-C</revertoptions>
+  </kdevcvsservice>
+</kdevelop>
Index: /tags/sj_tags/sj_root_20080929/psLib/pslib.pc.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/pslib.pc.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/pslib.pc.in	(revision 22322)
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/@PACKAGE_NAME@
+
+Name: @PACKAGE_NAME@
+Description: Pan-STARRS Library
+Version: @VERSION@
+Libs: -L${libdir} -lpslib
+Libs.private: @PSLIB_LIBS@
+Cflags: -I${includedir} @PSLIB_CFLAGS@
Index: /tags/sj_tags/sj_root_20080929/psLib/share/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+Makefile
+Makefile.in
+.libs
+.deps
+psTime.config
+psTime.xml
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/share/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/Makefile.am	(revision 22322)
@@ -0,0 +1,11 @@
+dist_pkgdata_DATA = \
+	daily.dat \
+	eopc01_1900_2004.dat \
+	finals_all.dat \
+	tai_utc.dat \
+	tab5.2a.dat \
+	tab5.2b.dat \
+	tab5.2c.dat \
+	finals2000A.dat \
+	finals_all.dat
+
Index: /tags/sj_tags/sj_root_20080929/psLib/share/daily.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/daily.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/daily.dat	(revision 22322)
@@ -0,0 +1,188 @@
+#
+#  Stripped version of finals.daily.orig file
+#
+#
+#          Bull A     Bull A      Bull A      Bull B      Bull B      Bull B
+#  MJD     PM-x       PM-y        UT1-UTC     PM-x        PM-y        UT1-UTC
+#
+53403.00   0.089198   0.207773  -0.5218861    0.089220    0.207360   -0.5219040
+53404.00   0.086551   0.206870  -0.5224171    0.086670    0.206850   -0.5224260
+53405.00   0.083583   0.206138  -0.5227683    0.083920    0.206040   -0.5227550
+53406.00   0.081544   0.205866  -0.5229579    0.081570    0.205850   -0.5229780
+53407.00   0.078824   0.205838  -0.5231935    0.078920    0.205580   -0.5231160
+53408.00   0.076009   0.205787  -0.5235614    0.076100    0.205770   -0.5237140
+53409.00   0.073081   0.205511  -0.5240823    0.073150    0.205280   -0.5240730
+53410.00   0.069881   0.205007  -0.5249001    0.070030    0.205020   -0.5248950
+53411.00   0.066804   0.204321  -0.5260474    0.066860    0.204090   -0.5260380
+53412.00   0.064032   0.203564  -0.5274687    0.064120    0.203610   -0.5274600
+53413.00   0.061534   0.203227  -0.5290349    0.061670    0.203150   -0.5290210
+53414.00   0.059261   0.203103  -0.5305665    0.059180    0.202910   -0.5305800
+53415.00   0.057167   0.203188  -0.5319092    0.057480    0.203020   -0.5318810
+53416.00   0.054644   0.203355  -0.5329893    0.054560    0.203270   -0.5329820
+53417.00   0.051723   0.203158  -0.5337631    0.052100    0.202990   -0.5337520
+53418.00   0.048514   0.202982  -0.5342942    0.048250    0.202830   -0.5342930
+53419.00   0.045299   0.203112  -0.5347071    0.045910    0.203060   -0.5347030
+53420.00   0.042672   0.203749  -0.5351354    0.042380    0.203710   -0.5351220
+53421.00   0.040104   0.204528  -0.5356628    0.040510    0.204360   -0.5356850
+53422.00   0.037711   0.205249  -0.5363046    0.037570    0.205130   -0.5362980
+53423.00   0.035491   0.205831  -0.5371187    0.035710    0.205590   -0.5371080
+53424.00   0.033293   0.206107  -0.5381067    0.033200    0.206030   -0.5380940
+53425.00   0.031547   0.206232  -0.5393036    0.031760    0.205990   -0.5392940
+53426.00   0.030088   0.206585  -0.5407214    0.030330    0.206570   -0.5407130
+53427.00   0.029197   0.207542  -0.5423144    0.029430    0.207400   -0.5422950
+53428.00   0.027670   0.208625  -0.5440523    0.027720    0.208530   -0.5440730
+53429.00   0.025817   0.209840  -0.5457450    0.025890    0.209650   -0.5457110
+53430.00   0.024127   0.211329  -0.5473574    0.024110    0.211090   -0.5473360
+53431.00   0.021777   0.212859  -0.5488360    0.021880    0.212750   -0.5488330
+53432.00   0.019378   0.214130  -0.5501178    0.019440    0.213970   -0.5500970
+53433.00   0.016950   0.214888  -0.5512259    0.017160    0.214930   -0.5512200
+53434.00   0.015134   0.215851  -0.5522278    0.015180    0.215700   -0.5522110
+53435.00   0.012738   0.216531  -0.5531713    0.000000    0.000000    0.0000000
+53436.00   0.010558   0.217070  -0.5541355    0.000000    0.000000    0.0000000
+53437.00   0.008806   0.217765  -0.5552417    0.000000    0.000000    0.0000000
+53438.00   0.006758   0.218630  -0.5565094    0.000000    0.000000    0.0000000
+53439.00   0.004646   0.219469  -0.5578989    0.000000    0.000000    0.0000000
+53440.00   0.002116   0.220380  -0.5593918    0.000000    0.000000    0.0000000
+53441.00   0.000386   0.221223  -0.5609127    0.000000    0.000000    0.0000000
+53442.00  -0.001411   0.221965  -0.5622917    0.000000    0.000000    0.0000000
+53443.00  -0.002676   0.222526  -0.5634066    0.000000    0.000000    0.0000000
+53444.00  -0.003697   0.223202  -0.5641684    0.000000    0.000000    0.0000000
+53445.00  -0.005587   0.223370  -0.5645880    0.000000    0.000000    0.0000000
+53446.00  -0.006897   0.223602  -0.5647672    0.000000    0.000000    0.0000000
+53447.00  -0.008129   0.223964  -0.5648197    0.000000    0.000000    0.0000000
+53448.00  -0.009000   0.224770  -0.5648551    0.000000    0.000000    0.0000000
+53449.00  -0.009853   0.225788  -0.5649488    0.000000    0.000000    0.0000000
+53450.00  -0.010752   0.226865  -0.5651214    0.000000    0.000000    0.0000000
+53451.00  -0.011836   0.228183  -0.5654543    0.000000    0.000000    0.0000000
+53452.00  -0.013357   0.230115  -0.5660328    0.000000    0.000000    0.0000000
+53453.00  -0.015359   0.231733  -0.5668880    0.000000    0.000000    0.0000000
+53454.00  -0.016630   0.233036  -0.5679461    0.000000    0.000000    0.0000000
+53455.00  -0.017847   0.234493  -0.5691352    0.000000    0.000000    0.0000000
+53456.00  -0.019260   0.236179  -0.5703273    0.000000    0.000000    0.0000000
+53457.00  -0.020917   0.237826  -0.5714205    0.000000    0.000000    0.0000000
+53458.00  -0.022446   0.239266  -0.5723283    0.000000    0.000000    0.0000000
+53459.00  -0.024822   0.240180  -0.5729862    0.000000    0.000000    0.0000000
+53460.00  -0.026796   0.241256  -0.5733634    0.000000    0.000000    0.0000000
+53461.00  -0.028996   0.242469  -0.5735448    0.000000    0.000000    0.0000000
+53462.00  -0.030749   0.243908  -0.5736219    0.000000    0.000000    0.0000000
+53463.00  -0.032355   0.244910  -0.5738480    0.000000    0.000000    0.0000000
+53464.00  -0.033549   0.245574  -0.5743129    0.000000    0.000000    0.0000000
+53465.00  -0.034314   0.245993  -0.5750873    0.000000    0.000000    0.0000000
+53466.00  -0.035092   0.246518  -0.5760683    0.000000    0.000000    0.0000000
+53467.00  -0.035603   0.247955  -0.5772363    0.000000    0.000000    0.0000000
+53468.00  -0.036716   0.250081  -0.5785305    0.000000    0.000000    0.0000000
+53469.00  -0.038502   0.252221  -0.5797696    0.000000    0.000000    0.0000000
+53470.00  -0.040638   0.253880  -0.5808118    0.000000    0.000000    0.0000000
+53471.00  -0.042584   0.255277  -0.5815764    0.000000    0.000000    0.0000000
+53472.00  -0.044043   0.256694  -0.5820791    0.000000    0.000000    0.0000000
+53473.00  -0.045469   0.258542  -0.5823466    0.000000    0.000000    0.0000000
+53474.00  -0.046115   0.260366  -0.5825923    0.000000    0.000000    0.0000000
+53475.00  -0.046845   0.262272  -0.5828567    0.000000    0.000000    0.0000000
+53476.00  -0.047872   0.264710  -0.5831790    0.000000    0.000000    0.0000000
+53477.00  -0.049363   0.267066  -0.5836570    0.000000    0.000000    0.0000000
+53478.00  -0.050998   0.269275  -0.5842978    0.000000    0.000000    0.0000000
+53479.00  -0.052488   0.271589  -0.5851290    0.000000    0.000000    0.0000000
+53480.00  -0.053852   0.273907  -0.5861727    0.000000    0.000000    0.0000000
+53481.00  -0.054932   0.276138  -0.5874000    0.000000    0.000000    0.0000000
+53482.00  -0.055785   0.278356  -0.5887508    0.000000    0.000000    0.0000000
+53483.00  -0.056771   0.280694  -0.5901790    0.000000    0.000000    0.0000000
+53484.00  -0.057675   0.283027  -0.5916199    0.000000    0.000000    0.0000000
+53485.00  -0.058387   0.284998  -0.5929329    0.000000    0.000000    0.0000000
+53486.00  -0.058807   0.286670  -0.5940239    0.000000    0.000000    0.0000000
+53487.00  -0.058834   0.288337  -0.5948463    0.000000    0.000000    0.0000000
+53488.00  -0.058468   0.290172  -0.5954614    0.000000    0.000000    0.0000000
+53489.00  -0.057836   0.292253  -0.5959457    0.000000    0.000000    0.0000000
+53490.00  -0.057279   0.294632  -0.5964394    0.000000    0.000000    0.0000000
+53491.00  -0.057256   0.297300  -0.5970769    0.000000    0.000000    0.0000000
+53492.00  -0.057639   0.299933  -0.5980051    0.000000    0.000000    0.0000000
+53493.00  -0.058151   0.302373  -0.5992687    0.000000    0.000000    0.0000000
+53494.00  -0.058700   0.304712  -0.6007248    0.000000    0.000000    0.0000000
+53495.00  -0.059272   0.306960  -0.6022420    0.000000    0.000000    0.0000000
+53496.00  -0.059856   0.309127  -0.6037093    0.000000    0.000000    0.0000000
+53497.00  -0.060443   0.311221  -0.6050339    0.000000    0.000000    0.0000000
+53498.00  -0.061026   0.313251  -0.6061046    0.000000    0.000000    0.0000000
+53499.00  -0.061597   0.315222  -0.6069056    0.000000    0.000000    0.0000000
+53500.00  -0.062154   0.317143  -0.6074547    0.000000    0.000000    0.0000000
+53501.00  -0.062691   0.319018  -0.6077615    0.000000    0.000000    0.0000000
+53502.00  -0.063205   0.320852  -0.6079384    0.000000    0.000000    0.0000000
+53503.00  -0.063695   0.322650  -0.6080889    0.000000    0.000000    0.0000000
+53504.00  -0.064157   0.324417  -0.6082912    0.000000    0.000000    0.0000000
+53505.00  -0.064591   0.326156  -0.6085899    0.000000    0.000000    0.0000000
+53506.00  -0.064995   0.327871  -0.6090260    0.000000    0.000000    0.0000000
+53507.00  -0.065369   0.329564  -0.6096200    0.000000    0.000000    0.0000000
+53508.00  -0.065711   0.331238  -0.6103620    0.000000    0.000000    0.0000000
+53509.00  -0.066022   0.332895  -0.6112057    0.000000    0.000000    0.0000000
+53510.00  -0.066301   0.334538  -0.6120710    0.000000    0.000000    0.0000000
+53511.00  -0.066547   0.336169  -0.6128774    0.000000    0.000000    0.0000000
+53512.00  -0.066762   0.337788  -0.6135321    0.000000    0.000000    0.0000000
+53513.00  -0.066944   0.339398  -0.6139426    0.000000    0.000000    0.0000000
+53514.00  -0.067094   0.340999  -0.6140704    0.000000    0.000000    0.0000000
+53515.00  -0.067212   0.342593  -0.6139338    0.000000    0.000000    0.0000000
+53516.00  -0.067298   0.344181  -0.6136271    0.000000    0.000000    0.0000000
+53517.00  -0.067353   0.345763  -0.6132988    0.000000    0.000000    0.0000000
+53518.00  -0.067376   0.347339  -0.6130910    0.000000    0.000000    0.0000000
+53519.00  -0.067368   0.348912  -0.6130983    0.000000    0.000000    0.0000000
+53520.00  -0.067329   0.350480  -0.6133377    0.000000    0.000000    0.0000000
+53521.00  -0.067259   0.352045  -0.6137530    0.000000    0.000000    0.0000000
+53522.00  -0.067158   0.353606  -0.6142476    0.000000    0.000000    0.0000000
+53523.00  -0.067028   0.355164  -0.6147019    0.000000    0.000000    0.0000000
+53524.00  -0.066867   0.356719  -0.6150115    0.000000    0.000000    0.0000000
+53525.00  -0.066677   0.358271  -0.6151081    0.000000    0.000000    0.0000000
+53526.00  -0.066458   0.359820  -0.6149565    0.000000    0.000000    0.0000000
+53527.00  -0.066209   0.361365  -0.6145663    0.000000    0.000000    0.0000000
+53528.00  -0.065932   0.362908  -0.6139974    0.000000    0.000000    0.0000000
+53529.00  -0.065626   0.364447  -0.6133312    0.000000    0.000000    0.0000000
+53530.00  -0.065291   0.365983  -0.6126499    0.000000    0.000000    0.0000000
+53531.00  -0.064929   0.367516  -0.6120298    0.000000    0.000000    0.0000000
+53532.00  -0.064539   0.369045  -0.6115355    0.000000    0.000000    0.0000000
+53533.00  -0.064122   0.370569  -0.6112118    0.000000    0.000000    0.0000000
+53534.00  -0.063678   0.372090  -0.6110774    0.000000    0.000000    0.0000000
+53535.00  -0.063206   0.373606  -0.6111281    0.000000    0.000000    0.0000000
+53536.00  -0.062731   0.375118  -0.6113333    0.000000    0.000000    0.0000000
+53537.00  -0.062204   0.376624  -0.6116363    0.000000    0.000000    0.0000000
+53538.00  -0.061652   0.378126  -0.6119672    0.000000    0.000000    0.0000000
+53539.00  -0.061075   0.379621  -0.6122419    0.000000    0.000000    0.0000000
+53540.00  -0.060472   0.381111  -0.6123670    0.000000    0.000000    0.0000000
+53541.00  -0.059845   0.382595  -0.6122687    0.000000    0.000000    0.0000000
+53542.00  -0.059194   0.384072  -0.6119327    0.000000    0.000000    0.0000000
+53543.00  -0.058518   0.385543  -0.6114171    0.000000    0.000000    0.0000000
+53544.00  -0.057818   0.387006  -0.6108419    0.000000    0.000000    0.0000000
+53545.00  -0.057095   0.388462  -0.6103562    0.000000    0.000000    0.0000000
+53546.00  -0.056348   0.389911  -0.6100804    0.000000    0.000000    0.0000000
+53547.00  -0.055579   0.391351  -0.6100654    0.000000    0.000000    0.0000000
+53548.00  -0.054787   0.392783  -0.6102740    0.000000    0.000000    0.0000000
+53549.00  -0.053973   0.394206  -0.6106035    0.000000    0.000000    0.0000000
+53550.00  -0.053137   0.395620  -0.6109243    0.000000    0.000000    0.0000000
+53551.00  -0.052279   0.397025  -0.6111159    0.000000    0.000000    0.0000000
+53552.00  -0.051400   0.398421  -0.6110951    0.000000    0.000000    0.0000000
+53553.00  -0.050500   0.399806  -0.6108246    0.000000    0.000000    0.0000000
+53554.00  -0.049580   0.401181  -0.6103105    0.000000    0.000000    0.0000000
+53555.00  -0.048640   0.402546  -0.6095938    0.000000    0.000000    0.0000000
+53556.00  -0.047679   0.403899  -0.6087372    0.000000    0.000000    0.0000000
+53557.00  -0.046700   0.405242  -0.6078156    0.000000    0.000000    0.0000000
+53558.00  -0.045701   0.406573  -0.6069068    0.000000    0.000000    0.0000000
+53559.00  -0.044683   0.407892  -0.6060834    0.000000    0.000000    0.0000000
+53560.00  -0.043647   0.409199  -0.6053990    0.000000    0.000000    0.0000000
+53561.00  -0.042594   0.410494  -0.6048821    0.000000    0.000000    0.0000000
+53562.00  -0.041522   0.411776  -0.6045336    0.000000    0.000000    0.0000000
+53563.00  -0.040434   0.413046  -0.6043315    0.000000    0.000000    0.0000000
+53564.00  -0.039328   0.414302  -0.6042312    0.000000    0.000000    0.0000000
+53565.00  -0.038206   0.415545  -0.6041737    0.000000    0.000000    0.0000000
+53566.00  -0.037068   0.416774  -0.6040854    0.000000    0.000000    0.0000000
+53567.00  -0.035915   0.417989  -0.6038884    0.000000    0.000000    0.0000000
+53568.00  -0.034746   0.419190  -0.6035134    0.000000    0.000000    0.0000000
+53569.00  -0.033562   0.420376  -0.6029275    0.000000    0.000000    0.0000000
+53570.00  -0.032364   0.421547  -0.6021579    0.000000    0.000000    0.0000000
+53571.00  -0.031152   0.422704  -0.6013029    0.000000    0.000000    0.0000000
+53572.00  -0.029926   0.423845  -0.6005091    0.000000    0.000000    0.0000000
+53573.00  -0.028687   0.424971  -0.5999238    0.000000    0.000000    0.0000000
+53574.00  -0.027434   0.426081  -0.5996363    0.000000    0.000000    0.0000000
+53575.00  -0.026170   0.427175  -0.5996429    0.000000    0.000000    0.0000000
+53576.00  -0.024893   0.428253  -0.5998473    0.000000    0.000000    0.0000000
+53577.00  -0.023604   0.429314  -0.6000987    0.000000    0.000000    0.0000000
+53578.00  -0.022304   0.430358  -0.6002451    0.000000    0.000000    0.0000000
+53579.00  -0.020993   0.431386  -0.6001768    0.000000    0.000000    0.0000000
+53580.00  -0.019672   0.432397  -0.5998445    0.000000    0.000000    0.0000000
+53581.00  -0.018341   0.433247  -0.5992544    0.000000    0.000000    0.0000000
+53582.00  -0.016999   0.434231  -0.5984530    0.000000    0.000000    0.0000000
+53583.00  -0.015649   0.435197  -0.5975086    0.000000    0.000000    0.0000000
Index: /tags/sj_tags/sj_root_20080929/psLib/share/eopc01_1900_2004.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/eopc01_1900_2004.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/eopc01_1900_2004.dat	(revision 22322)
@@ -0,0 +1,2103 @@
+#  eopc01_1900_2004.dat
+#
+#  This file comes from http://hpiers.obspm.fr/eoppc/eop/eopc01/eopc01.1900-2004. See readme.eopc01 (located
+#  in the psLib/data directory) for details.
+#
+#  @author Ross Harman, MHPCC
+#
+#  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+#  @date $Date: 2006-09-23 02:20:17 $
+#
+#
+#    Date       PM-x      PM-y     UT1-UTC
+#   (year)    (arcsec)   (arcsec)   (sec)
+#     psF64     psF64      psF64     psF64
+    1900.00  -.212818  -.097229   .0000000
+    1900.05  -.047461  -.065631   .0000000
+    1900.10  -.112006  -.040467   .0000000
+    1900.15  -.076710  -.009674   .0000000
+    1900.20  -.099850  -.066660   .0000000
+    1900.25  -.131173  -.072076   .0000000
+    1900.30  -.087390  -.089427   .0000000
+    1900.35  -.164078  -.131034   .0000000
+    1900.40  -.151407  -.105838   .0000000
+    1900.45  -.143764  -.179125   .0000000
+    1900.50  -.124341  -.105747   .0000000
+    1900.55  -.206229  -.144555   .0000000
+    1900.60  -.192711  -.095563   .0000000
+    1900.65  -.190299  -.067717   .0000000
+    1900.70  -.135718  -.081480   .0000000
+    1900.75  -.153807   .030568   .0000000
+    1900.80  -.173818   .001034   .0000000
+    1900.85  -.257716  -.006499   .0000000
+    1900.90  -.209085   .046511   .0000000
+    1900.95  -.133145   .004905   .0000000
+    1901.00  -.138242   .014255   .0000000
+    1901.05  -.162602   .006609   .0000000
+    1901.10  -.121242  -.000875   .0000000
+    1901.15  -.109773   .024691   .0000000
+    1901.20  -.103805   .039564   .0000000
+    1901.25  -.021306  -.011519   .0000000
+    1901.30  -.001300   .021700   .0000000
+    1901.35   .037156  -.049753   .0000000
+    1901.40   .003993  -.095314   .0000000
+    1901.45   .054079  -.081336   .0000000
+    1901.50   .089761  -.084901   .0000000
+    1901.55   .004864  -.187233   .0000000
+    1901.60  -.023099  -.179528   .0000000
+    1901.65  -.057910  -.197673   .0000000
+    1901.70  -.033170  -.202981   .0000000
+    1901.75  -.155604  -.187085   .0000000
+    1901.80  -.169658  -.183163   .0000000
+    1901.85  -.188623  -.181458   .0000000
+    1901.90  -.223086  -.144566   .0000000
+    1901.95  -.254958  -.114447   .0000000
+    1902.00  -.259243  -.078157   .0000000
+    1902.05  -.211792   .038633   .0000000
+    1902.10  -.216991   .064250   .0000000
+    1902.15  -.242537   .171246   .0000000
+    1902.20  -.188324   .158468   .0000000
+    1902.25  -.152570   .207171   .0000000
+    1902.30  -.062244   .171387   .0000000
+    1902.35  -.016045   .182141   .0000000
+    1902.40   .036032   .156268   .0000000
+    1902.45   .217293   .252814   .0000000
+    1902.50   .119283   .066262   .0000000
+    1902.55   .125538   .016781   .0000000
+    1902.60   .082496  -.058369   .0000000
+    1902.65   .051287  -.069631   .0000000
+    1902.70   .051239  -.093785   .0000000
+    1902.75  -.024248  -.151921   .0000000
+    1902.80  -.054789  -.179957   .0000000
+    1902.85  -.146320  -.146959   .0000000
+    1902.90  -.193061  -.169534   .0000000
+    1902.95  -.237694  -.119404   .0000000
+    1903.00  -.319420  -.043030   .0000000
+    1903.05  -.341204   .031366   .0000000
+    1903.10  -.345930  -.002271   .0000000
+    1903.15  -.363449   .022246   .0000000
+    1903.20  -.363639   .126744   .0000000
+    1903.25  -.275204   .088178   .0000000
+    1903.30  -.217973   .142752   .0000000
+    1903.35  -.133649   .193127   .0000000
+    1903.40  -.073538   .187785   .0000000
+    1903.45  -.033275   .242905   .0000000
+    1903.50  -.025665   .185828   .0000000
+    1903.55   .065994   .199396   .0000000
+    1903.60   .124642   .131603   .0000000
+    1903.65   .138085   .043707   .0000000
+    1903.70   .131036  -.010458   .0000000
+    1903.75   .129875  -.132885   .0000000
+    1903.80   .020188  -.149501   .0000000
+    1903.85  -.013164  -.224089   .0000000
+    1903.90   .017200  -.148400   .0000000
+    1903.95  -.054459  -.187411   .0000000
+    1904.00  -.148856  -.076687   .0000000
+    1904.05  -.254004  -.136031   .0000000
+    1904.10  -.260513  -.134266   .0000000
+    1904.15  -.250640  -.062308   .0000000
+    1904.20  -.318450  -.022118   .0000000
+    1904.25  -.293126   .024733   .0000000
+    1904.30  -.249230   .052166   .0000000
+    1904.35  -.263860   .072953   .0000000
+    1904.40  -.210918   .172463   .0000000
+    1904.45  -.152700   .188900   .0000000
+    1904.50  -.109676   .182564   .0000000
+    1904.55  -.082798   .158201   .0000000
+    1904.60   .001500   .164000   .0000000
+    1904.65   .042084   .134884   .0000000
+    1904.70   .062535   .070240   .0000000
+    1904.75   .083512   .063227   .0000000
+    1904.80   .042326   .010531   .0000000
+    1904.85   .042284  -.029751   .0000000
+    1904.90  -.008473  -.063250   .0000000
+    1904.95   .028258  -.057405   .0000000
+    1905.00  -.040538  -.099671   .0000000
+    1905.05  -.017099  -.158118   .0000000
+    1905.10  -.105713  -.176117   .0000000
+    1905.15  -.176213  -.100777   .0000000
+    1905.20  -.231493  -.136695   .0000000
+    1905.25  -.251440  -.151097   .0000000
+    1905.30  -.248000  -.108800   .0000000
+    1905.35  -.304428   .045206   .0000000
+    1905.40  -.236545   .042822   .0000000
+    1905.45  -.250592   .048859   .0000000
+    1905.50  -.185960   .112145   .0000000
+    1905.55  -.156532   .117102   .0000000
+    1905.60  -.109352   .166247   .0000000
+    1905.65  -.067846   .135598   .0000000
+    1905.70   .014037   .151407   .0000000
+    1905.75  -.034403   .175804   .0000000
+    1905.80  -.031721   .049970   .0000000
+    1905.85   .025746   .080453   .0000000
+    1905.90   .024021  -.056728   .0000000
+    1905.95   .040706  -.006383   .0000000
+    1906.00   .003853  -.026096   .0000000
+    1906.05  -.017613  -.075020   .0000000
+    1906.10  -.157406  -.092487   .0000000
+    1906.15  -.067506  -.080298   .0000000
+    1906.20  -.149450  -.119103   .0000000
+    1906.25  -.128247  -.061169   .0000000
+    1906.30  -.179648  -.057693   .0000000
+    1906.35  -.186449  -.044109   .0000000
+    1906.40  -.208575  -.019448   .0000000
+    1906.45  -.217870   .042944   .0000000
+    1906.50  -.199891   .058881   .0000000
+    1906.55  -.173543   .070934   .0000000
+    1906.60  -.182003   .113024   .0000000
+    1906.65  -.142776   .095154   .0000000
+    1906.70  -.093740   .095224   .0000000
+    1906.75  -.093900   .111200   .0000000
+    1906.80  -.102758   .078405   .0000000
+    1906.85  -.009641   .137918   .0000000
+    1906.90  -.126885   .077766   .0000000
+    1906.95  -.085940   .122947   .0000000
+    1907.00  -.079416   .150174   .0000000
+    1907.05  -.067350   .041498   .0000000
+    1907.10  -.027042   .024883   .0000000
+    1907.15  -.069913   .039794   .0000000
+    1907.20  -.059194  -.025833   .0000000
+    1907.25  -.032978  -.010025   .0000000
+    1907.30  -.032298  -.079952   .0000000
+    1907.35  -.012800  -.038790   .0000000
+    1907.40  -.094517  -.051596   .0000000
+    1907.45  -.089447  -.124297   .0000000
+    1907.50  -.131771  -.098748   .0000000
+    1907.55  -.109356  -.122489   .0000000
+    1907.60  -.134764  -.084340   .0000000
+    1907.65  -.150915  -.095204   .0000000
+    1907.70  -.183652  -.070567   .0000000
+    1907.75  -.247755  -.037271   .0000000
+    1907.80  -.276782   .006594   .0000000
+    1907.85  -.260675   .115176   .0000000
+    1907.90  -.279309   .066165   .0000000
+    1907.95  -.247411   .158512   .0000000
+    1908.00  -.179825   .190418   .0000000
+    1908.05  -.162104   .175855   .0000000
+    1908.10  -.073775   .244495   .0000000
+    1908.15  -.088534   .184551   .0000000
+    1908.20   .072181   .185212   .0000000
+    1908.25   .000811   .129285   .0000000
+    1908.30   .094290   .125029   .0000000
+    1908.35   .098031   .048028   .0000000
+    1908.40   .085376  -.037173   .0000000
+    1908.45   .110614  -.053618   .0000000
+    1908.50   .110175  -.121985   .0000000
+    1908.55   .062891  -.147259   .0000000
+    1908.60   .016036  -.174778   .0000000
+    1908.65  -.044507  -.258578   .0000000
+    1908.70  -.126698  -.236805   .0000000
+    1908.75  -.185227  -.279032   .0000000
+    1908.80  -.218671  -.234349   .0000000
+    1908.85  -.258124  -.193490   .0000000
+    1908.90  -.348498  -.117029   .0000000
+    1908.95  -.441656  -.087821   .0000000
+    1909.00  -.418026   .032515   .0000000
+    1909.05  -.464700   .053100   .0000000
+    1909.10  -.381402   .081369   .0000000
+    1909.15  -.331872   .208343   .0000000
+    1909.20  -.262800   .230900   .0000000
+    1909.25  -.180022   .262477   .0000000
+    1909.30  -.088674   .272316   .0000000
+    1909.35  -.017300   .301900   .0000000
+    1909.40   .082311   .293915   .0000000
+    1909.45   .145682   .242356   .0000000
+    1909.50   .159939   .145514   .0000000
+    1909.55   .219001   .050735   .0000000
+    1909.60   .251107   .038325   .0000000
+    1909.65   .216000  -.092100   .0000000
+    1909.70   .239771  -.129218   .0000000
+    1909.75   .122030  -.229024   .0000000
+    1909.80  -.027219  -.280542   .0000000
+    1909.85  -.097816  -.318227   .0000000
+    1909.90  -.212272  -.273213   .0000000
+    1909.95  -.262295  -.211841   .0000000
+    1910.00  -.323074  -.200846   .0000000
+    1910.05  -.442329  -.257217   .0000000
+    1910.10  -.448532  -.107453   .0000000
+    1910.15  -.408744   .030815   .0000000
+    1910.20  -.414020  -.006268   .0000000
+    1910.25  -.385676   .162890   .0000000
+    1910.30  -.368387   .272702   .0000000
+    1910.35  -.268774   .293213   .0000000
+    1910.40  -.147573   .342824   .0000000
+    1910.45  -.081273   .328529   .0000000
+    1910.50  -.020932   .388520   .0000000
+    1910.55   .105674   .327555   .0000000
+    1910.60   .176301   .312524   .0000000
+    1910.65   .191756   .171612   .0000000
+    1910.70   .222257   .091742   .0000000
+    1910.75   .211332  -.009012   .0000000
+    1910.80   .205000  -.105700   .0000000
+    1910.85   .120900  -.162494   .0000000
+    1910.90   .083916  -.253801   .0000000
+    1910.95  -.057477  -.280025   .0000000
+    1911.00  -.089579  -.260914   .0000000
+    1911.05  -.218786  -.263597   .0000000
+    1911.10  -.224700  -.257286   .0000000
+    1911.15  -.335860  -.182205   .0000000
+    1911.20  -.267000  -.050900   .0000000
+    1911.25  -.370633   .021540   .0000000
+    1911.30  -.353109   .047330   .0000000
+    1911.35  -.332727   .126340   .0000000
+    1911.40  -.234107   .168826   .0000000
+    1911.45  -.272718   .310644   .0000000
+    1911.50  -.193283   .331807   .0000000
+    1911.55  -.136796   .347029   .0000000
+    1911.60  -.056770   .304448   .0000000
+    1911.65   .060600   .363500   .0000000
+    1911.70   .112694   .325844   .0000000
+    1911.75   .129599   .242729   .0000000
+    1911.80   .130866   .155825   .0000000
+    1911.85   .158551   .088616   .0000000
+    1911.90   .145314  -.044498   .0000000
+    1911.95   .095603  -.041933   .0000000
+    1912.00   .083131  -.041367   .0000000
+    1912.05  -.038956  -.097988   .0000000
+    1912.10   .078300  -.165300   .0000000
+    1912.15  -.088995  -.223549   .0000000
+    1912.20  -.127587  -.102840   .0000000
+    1912.25  -.227272  -.194080   .0000000
+    1912.30  -.209405  -.109730   .0000000
+    1912.35  -.237021  -.041122   .0000000
+    1912.40  -.262082  -.006966   .0000000
+    1912.45  -.225320   .056285   .0000000
+    1912.50  -.263907   .044713   .0000000
+    1912.55  -.196951   .144625   .0000000
+    1912.60  -.198267   .185500   .0000000
+    1912.65  -.219124   .151146   .0000000
+    1912.70  -.175982   .183356   .0000000
+    1912.75  -.160280   .162811   .0000000
+    1912.80  -.103200   .187700   .0000000
+    1912.85  -.078852   .172599   .0000000
+    1912.90  -.076719   .222193   .0000000
+    1912.95  -.018200   .178000   .0000000
+    1913.00  -.088918   .098053   .0000000
+    1913.05  -.043449   .115101   .0000000
+    1913.10  -.001323   .124168   .0000000
+    1913.15  -.021744   .057252   .0000000
+    1913.20   .011122   .015761   .0000000
+    1913.25  -.010477  -.071666   .0000000
+    1913.30   .032491   .003044   .0000000
+    1913.35   .048101  -.050706   .0000000
+    1913.40   .032669  -.039496   .0000000
+    1913.45   .008311  -.068348   .0000000
+    1913.50   .000027  -.047918   .0000000
+    1913.55  -.076685  -.083122   .0000000
+    1913.60  -.108057  -.085386   .0000000
+    1913.65  -.100400  -.040800   .0000000
+    1913.70  -.162152  -.081823   .0000000
+    1913.75  -.223157  -.030051   .0000000
+    1913.80  -.224000  -.002700   .0000000
+    1913.85  -.226344   .078679   .0000000
+    1913.90  -.200560   .102525   .0000000
+    1913.95  -.313300   .074600   .0000000
+    1914.00  -.203986   .155067   .0000000
+    1914.05  -.260937   .252070   .0000000
+    1914.10  -.152037   .221001   .0000000
+    1914.15  -.093945   .191653   .0000000
+    1914.20  -.063798   .184214   .0000000
+    1914.25  -.013635   .132013   .0000000
+    1914.30   .045383   .170364   .0000000
+    1914.35   .034013   .110450   .0000000
+    1914.40   .073793   .089719   .0000000
+    1914.45   .106389   .069624   .0000000
+    1914.50   .104207   .003118   .0000000
+    1914.55   .085502  -.025467   .0000000
+    1914.60   .049258  -.008534   .0000000
+    1914.65  -.013923  -.060487   .0000000
+    1914.70  -.046141  -.151237   .0000000
+    1914.75  -.094789  -.177467   .0000000
+    1914.80  -.140169  -.214589   .0000000
+    1914.85  -.220007  -.164939   .0000000
+    1914.90  -.271487  -.080598   .0000000
+    1914.95  -.197162  -.003731   .0000000
+    1915.00  -.365371   .018627   .0000000
+    1915.05  -.311390   .018527   .0000000
+    1915.10  -.363577   .127944   .0000000
+    1915.15  -.230546   .091634   .0000000
+    1915.20  -.285678   .254673   .0000000
+    1915.25  -.300793   .364102   .0000000
+    1915.30  -.145488   .374588   .0000000
+    1915.35  -.084157   .325899   .0000000
+    1915.40  -.008400   .253100   .0000000
+    1915.45   .086674   .275352   .0000000
+    1915.50   .081245   .210673   .0000000
+    1915.55   .121650   .162508   .0000000
+    1915.60   .169064  -.052355   .0000000
+    1915.65   .112084   .002862   .0000000
+    1915.70   .099188  -.061928   .0000000
+    1915.75   .093628  -.172293   .0000000
+    1915.80  -.031363  -.267297   .0000000
+    1915.85  -.050020  -.262266   .0000000
+    1915.90  -.047776  -.265495   .0000000
+    1915.95  -.135461  -.219164   .0000000
+    1916.00  -.250486  -.127502   .0000000
+    1916.05  -.339030  -.192532   .0000000
+    1916.10  -.414793  -.366168   .0000000
+    1916.15  -.341536  -.026629   .0000000
+    1916.20  -.344556   .102289   .0000000
+    1916.25  -.312204   .143646   .0000000
+    1916.30  -.283589   .181301   .0000000
+    1916.35  -.251093   .263902   .0000000
+    1916.40  -.158543   .396971   .0000000
+    1916.45  -.236712   .246063   .0000000
+    1916.50  -.049133   .295525   .0000000
+    1916.55  -.019000   .215800   .0000000
+    1916.60   .091660   .193570   .0000000
+    1916.65   .171184   .142932   .0000000
+    1916.70   .135500   .053900   .0000000
+    1916.75   .159276  -.060418   .0000000
+    1916.80   .195249  -.060384   .0000000
+    1916.85   .041662  -.606821   .0000000
+    1916.90   .012991  -.219566   .0000000
+    1916.95  -.003679  -.318930   .0000000
+    1917.00  -.095516  -.232917   .0000000
+    1917.05  -.151536  -.182785   .0000000
+    1917.10  -.182049  -.136279   .0000000
+    1917.15  -.292198  -.114514   .0000000
+    1917.20  -.293211  -.077170   .0000000
+    1917.25  -.310501  -.047076   .0000000
+    1917.30  -.262144   .028782   .0000000
+    1917.35  -.195616   .145893   .0000000
+    1917.40  -.216823   .172434   .0000000
+    1917.45  -.195552   .154519   .0000000
+    1917.50  -.114387   .252819   .0000000
+    1917.55  -.014913   .095478   .0000000
+    1917.60  -.032948   .203552   .0000000
+    1917.65  -.004274   .202191   .0000000
+    1917.70  -.029300   .187800   .0000000
+    1917.75   .047363   .077854   .0000000
+    1917.80   .027454   .011902   .0000000
+    1917.85   .086395   .016630   .0000000
+    1917.90   .011086   .026413   .0000000
+    1917.95   .061347  -.033647   .0000000
+    1918.00  -.045800  -.126500   .0000000
+    1918.05  -.063352  -.187119   .0000000
+    1918.10  -.140380  -.029766   .0000000
+    1918.15  -.139590  -.046948   .0000000
+    1918.20  -.186229  -.012003   .0000000
+    1918.25  -.197272  -.022847   .0000000
+    1918.30  -.162233   .009864   .0000000
+    1918.35  -.209522   .106965   .0000000
+    1918.40  -.183315   .111698   .0000000
+    1918.45  -.186120   .161230   .0000000
+    1918.50  -.206139   .170211   .0000000
+    1918.55  -.140500   .134700   .0000000
+    1918.60  -.075624   .126939   .0000000
+    1918.65  -.035250   .149125   .0000000
+    1918.70  -.038388   .139481   .0000000
+    1918.75   .005607   .126979   .0000000
+    1918.80  -.039489   .133454   .0000000
+    1918.85  -.047400   .170000   .0000000
+    1918.90  -.061884   .112716   .0000000
+    1918.95  -.059354   .062420   .0000000
+    1919.00  -.058158   .016625   .0000000
+    1919.05  -.045037   .121493   .0000000
+    1919.10  -.064165   .072740   .0000000
+    1919.15  -.046142   .055541   .0000000
+    1919.20  -.076649   .084834   .0000000
+    1919.25  -.032000   .101400   .0000000
+    1919.30  -.032764   .042454   .0000000
+    1919.35  -.034002   .057361   .0000000
+    1919.40  -.081513   .071678   .0000000
+    1919.45  -.024396   .011394   .0000000
+    1919.50  -.005435   .014449   .0000000
+    1919.55  -.036700  -.072300   .0000000
+    1919.60  -.054480  -.035798   .0000000
+    1919.65  -.109833  -.018115   .0000000
+    1919.70  -.180209  -.046368   .0000000
+    1919.75  -.144050   .048583   .0000000
+    1919.80  -.181892  -.009524   .0000000
+    1919.85  -.159213   .054630   .0000000
+    1919.90  -.092562  -.197751   .0000000
+    1919.95  -.186227   .107207   .0000000
+    1920.00  -.097388   .112723   .0000000
+    1920.05  -.145513   .244405   .0000000
+    1920.10  -.066010   .127964   .0000000
+    1920.15  -.108000   .125400   .0000000
+    1920.20  -.066646   .249459   .0000000
+    1920.25   .062692   .241400   .0000000
+    1920.30   .077897   .212694   .0000000
+    1920.35   .112620   .177084   .0000000
+    1920.40   .081000   .157700   .0000000
+    1920.45   .148386   .115656   .0000000
+    1920.50   .111770   .088776   .0000000
+    1920.55   .063562  -.046004   .0000000
+    1920.60   .129866   .027842   .0000000
+    1920.65   .091341  -.043051   .0000000
+    1920.70   .068767  -.053549   .0000000
+    1920.75  -.008467  -.089633   .0000000
+    1920.80  -.052510  -.063097   .0000000
+    1920.85  -.131312  -.127174   .0000000
+    1920.90  -.098074  -.069955   .0000000
+    1920.95  -.202848  -.093388   .0000000
+    1921.00  -.275100   .041200   .0000000
+    1921.05  -.199380   .005397   .0000000
+    1921.10  -.269861   .062960   .0000000
+    1921.15  -.315700   .186534   .0000000
+    1921.20  -.190287   .164991   .0000000
+    1921.25  -.119466   .139311   .0000000
+    1921.30  -.014599   .217462   .0000000
+    1921.35   .031054   .201362   .0000000
+    1921.40   .091820   .160909   .0000000
+    1921.45   .100985   .083825   .0000000
+    1921.50   .212445   .182524   .0000000
+    1921.55   .138182   .088243   .0000000
+    1921.60   .178414   .025800   .0000000
+    1921.65   .167323   .004838   .0000000
+    1921.70   .073545  -.078915   .0000000
+    1921.75   .127179  -.080673   .0000000
+    1921.80  -.002474  -.169272   .0000000
+    1921.85  -.007518  -.143864   .0000000
+    1921.90  -.098209   .106262   .0000000
+    1921.95  -.133071  -.095128   .0000000
+    1922.00  -.045524  -.029486   .0000000
+    1922.05  -.194756  -.097510   .0000000
+    1922.10  -.208934   .039567   .0000000
+    1922.15  -.210300   .137300   .0000000
+    1922.20  -.165744   .125734   .0000000
+    1922.25  -.162390   .134218   .0000000
+    1922.30  -.132697   .221570   .0000000
+    1922.35  -.090259   .277564   .0000000
+    1922.40  -.047480   .260568   .0000000
+    1922.45   .009070   .222538   .0000000
+    1922.50   .111622   .240501   .0000000
+    1922.55   .088770   .114259   .0000000
+    1922.60   .163154   .076991   .0000000
+    1922.65   .151317   .014994   .0000000
+    1922.70   .141800   .014900   .0000000
+    1922.75   .120772  -.019145   .0000000
+    1922.80   .083381  -.139651   .0000000
+    1922.85   .063654  -.088851   .0000000
+    1922.90   .041976   .015059   .0000000
+    1922.95  -.163179  -.120346   .0000000
+    1923.00  -.162345  -.088042   .0000000
+    1923.05  -.166986  -.055441   .0000000
+    1923.10  -.332673  -.032441   .0000000
+    1923.15  -.262008  -.033456   .0000000
+    1923.20  -.207748   .016212   .0000000
+    1923.25  -.274792   .140175   .0000000
+    1923.30  -.179152   .157417   .0000000
+    1923.35  -.176779   .129676   .0000000
+    1923.40  -.097364   .300984   .0000000
+    1923.45  -.024124   .317510   .0000000
+    1923.50  -.019994   .239535   .0000000
+    1923.55   .060132   .240230   .0000000
+    1923.60   .112367   .311274   .0000000
+    1923.65   .142183   .249476   .0000000
+    1923.70   .151780   .226170   .0000000
+    1923.75   .161990   .129040   .0000000
+    1923.80   .130175   .020789   .0000000
+    1923.85   .136144   .082803   .0000000
+    1923.90   .088243   .024731   .0000000
+    1923.95   .035504  -.012841   .0000000
+    1924.00  -.005804   .073064   .0000000
+    1924.05  -.068517   .011667   .0000000
+    1924.10  -.029063   .003840   .0000000
+    1924.15  -.149500  -.020500   .0000000
+    1924.20  -.151107   .038096   .0000000
+    1924.25  -.170714   .072246   .0000000
+    1924.30  -.127400   .135100   .0000000
+    1924.35  -.165261   .139194   .0000000
+    1924.40  -.187563   .107926   .0000000
+    1924.45  -.081980   .195088   .0000000
+    1924.50  -.075229   .183050   .0000000
+    1924.55  -.059435   .142566   .0000000
+    1924.60   .014627   .135909   .0000000
+    1924.65   .029893   .185564   .0000000
+    1924.70  -.002107   .154303   .0000000
+    1924.75   .041539   .113626   .0000000
+    1924.80   .032319   .000498   .0000000
+    1924.85  -.079291   .056224   .0000000
+    1924.90  -.006172   .025546   .0000000
+    1924.95   .035597   .085593   .0000000
+    1925.00  -.012995   .036963   .0000000
+    1925.05  -.056182  -.049412   .0000000
+    1925.10  -.103481  -.003581   .0000000
+    1925.15  -.107312  -.041495   .0000000
+    1925.20  -.141179  -.046236   .0000000
+    1925.25  -.102647   .016748   .0000000
+    1925.30  -.130539   .119861   .0000000
+    1925.35  -.066774   .097721   .0000000
+    1925.40  -.004013   .055231   .0000000
+    1925.45  -.066873   .032174   .0000000
+    1925.50  -.055672   .061909   .0000000
+    1925.55  -.070311   .114851   .0000000
+    1925.60   .017853   .118506   .0000000
+    1925.65  -.015743   .056794   .0000000
+    1925.70   .001485   .004726   .0000000
+    1925.75  -.005631   .015549   .0000000
+    1925.80   .001128   .013928   .0000000
+    1925.85  -.243256  -.060509   .0000000
+    1925.90  -.025734   .007049   .0000000
+    1925.95  -.041648  -.013321   .0000000
+    1926.00  -.199605  -.083663   .0000000
+    1926.05  -.163818  -.029853   .0000000
+    1926.10  -.073051  -.074540   .0000000
+    1926.15  -.177265  -.060285   .0000000
+    1926.20  -.230084  -.024826   .0000000
+    1926.25  -.173813   .058121   .0000000
+    1926.30  -.052708   .122825   .0000000
+    1926.35  -.106664   .067451   .0000000
+    1926.40  -.129567   .112402   .0000000
+    1926.45  -.076581   .132865   .0000000
+    1926.50  -.051765   .131885   .0000000
+    1926.55  -.033933   .119936   .0000000
+    1926.60  -.053049   .122331   .0000000
+    1926.65  -.021532   .117705   .0000000
+    1926.70   .018915   .156256   .0000000
+    1926.75  -.001894   .104146   .0000000
+    1926.80  -.075772   .076597   .0000000
+    1926.85  -.120294   .111799   .0000000
+    1926.90  -.050061   .130391   .0000000
+    1926.95  -.098525   .073404   .0000000
+    1927.00  -.058200   .056215   .0000000
+    1927.05  -.142988   .109762   .0000000
+    1927.10  -.032237   .116674   .0000000
+    1927.15  -.095191   .115885   .0000000
+    1927.20  -.059447   .084129   .0000000
+    1927.25  -.115876   .071429   .0000000
+    1927.30  -.052232   .049356   .0000000
+    1927.35  -.054137   .027885   .0000000
+    1927.40  -.013624   .120684   .0000000
+    1927.45   .095331   .074304   .0000000
+    1927.50   .015128   .128662   .0000000
+    1927.55  -.016024   .158240   .0000000
+    1927.60  -.031647   .047004   .0000000
+    1927.65  -.020014   .048718   .0000000
+    1927.70  -.004146   .054252   .0000000
+    1927.75  -.052691   .005745   .0000000
+    1927.80  -.052571  -.025327   .0000000
+    1927.85  -.093446  -.044573   .0000000
+    1927.90  -.185615   .036816   .0000000
+    1927.95  -.159711  -.002380   .0000000
+    1928.00  -.008822  -.065239   .0000000
+    1928.05  -.145700  -.017500   .0000000
+    1928.10  -.134670   .100011   .0000000
+    1928.15  -.142914   .014838   .0000000
+    1928.20  -.130658   .109191   .0000000
+    1928.25  -.071799   .135022   .0000000
+    1928.30  -.077913   .109713   .0000000
+    1928.35  -.084562   .184119   .0000000
+    1928.40  -.070452   .122462   .0000000
+    1928.45  -.071262   .128166   .0000000
+    1928.50  -.023166   .138252   .0000000
+    1928.55  -.049392   .114652   .0000000
+    1928.60  -.132084  -.134582   .0000000
+    1928.65   .020427   .100107   .0000000
+    1928.70  -.028438   .032299   .0000000
+    1928.75  -.110100   .107500   .0000000
+    1928.80  -.143376  -.000841   .0000000
+    1928.85  -.115036   .011216   .0000000
+    1928.90  -.110841   .085727   .0000000
+    1928.95  -.072093   .151032   .0000000
+    1929.00  -.160303   .044210   .0000000
+    1929.05  -.288649   .003539   .0000000
+    1929.10  -.100071   .109711   .0000000
+    1929.15  -.169363   .171496   .0000000
+    1929.20  -.126222   .140853   .0000000
+    1929.25  -.057249   .149535   .0000000
+    1929.30   .003629   .164681   .0000000
+    1929.35   .131595   .332923   .0000000
+    1929.40   .031158   .160059   .0000000
+    1929.45   .069220   .115295   .0000000
+    1929.50   .103978   .187112   .0000000
+    1929.55   .082883   .085168   .0000000
+    1929.60   .049832  -.000080   .0000000
+    1929.65   .073767   .013919   .0000000
+    1929.70   .084972   .064950   .0000000
+    1929.75  -.015329  -.028934   .0000000
+    1929.80  -.093999  -.073280   .0000000
+    1929.85  -.161299  -.061529   .0000000
+    1929.90  -.165908  -.084395   .0000000
+    1929.95  -.205862   .028014   .0000000
+    1930.00  -.214153  -.044059   .0000000
+    1930.05  -.215942   .054329   .0000000
+    1930.10  -.225480   .057826   .0000000
+    1930.15  -.206525   .096138   .0000000
+    1930.20  -.166814   .146704   .0000000
+    1930.25  -.130922   .224514   .0000000
+    1930.30  -.049593   .179473   .0000000
+    1930.35  -.125200   .154900   .0000000
+    1930.40   .006745   .237422   .0000000
+    1930.45   .017674   .198183   .0000000
+    1930.50   .035577   .132240   .0000000
+    1930.55   .081022   .126161   .0000000
+    1930.60   .098407   .114242   .0000000
+    1930.65   .074379  -.037760   .0000000
+    1930.70   .062912  -.022243   .0000000
+    1930.75   .028598   .019082   .0000000
+    1930.80  -.011225  -.081563   .0000000
+    1930.85  -.084804  -.009200   .0000000
+    1930.90  -.116025  -.003658   .0000000
+    1930.95  -.158405  -.031682   .0000000
+    1931.00  -.353309   .027867   .0000000
+    1931.05  -.277671   .046550   .0000000
+    1931.10  -.239000  -.015100   .0000000
+    1931.15  -.355078   .116611   .0000000
+    1931.20  -.226864   .167103   .0000000
+    1931.25  -.248657   .182523   .0000000
+    1931.30  -.150564   .238762   .0000000
+    1931.35  -.151393   .231228   .0000000
+    1931.40  -.039529   .281355   .0000000
+    1931.45   .024987   .296176   .0000000
+    1931.50   .039600   .242500   .0000000
+    1931.55   .074415   .182812   .0000000
+    1931.60   .098426   .094944   .0000000
+    1931.65   .082462   .055652   .0000000
+    1931.70   .165785   .079323   .0000000
+    1931.75   .067878   .029704   .0000000
+    1931.80   .052973  -.106345   .0000000
+    1931.85  -.083590  -.105084   .0000000
+    1931.90  -.060868  -.054736   .0000000
+    1931.95  -.091079  -.143458   .0000000
+    1932.00  -.117202  -.090917   .0000000
+    1932.05  -.196529  -.057717   .0000000
+    1932.10  -.166200  -.015500   .0000000
+    1932.15  -.312357   .014781   .0000000
+    1932.20  -.324819   .020690   .0000000
+    1932.25  -.229236   .137146   .0000000
+    1932.30  -.199450   .181143   .0000000
+    1932.35  -.169007   .207207   .0000000
+    1932.40  -.138350   .219864   .0000000
+    1932.45  -.071670   .214482   .0000000
+    1932.50  -.075142   .230743   .0000000
+    1932.55  -.052070   .236227   .0000000
+    1932.60   .026338   .224720   .0000000
+    1932.65   .071800   .178200   .0000000
+    1932.70   .070092   .209808   .0000000
+    1932.75   .087369   .059518   .0000000
+    1932.80   .122666   .122560   .0000000
+    1932.85   .085699   .068358   .0000000
+    1932.90   .094388   .033874   .0000000
+    1932.95   .097006   .030486   .0000000
+    1933.00  -.073655  -.011865   .0000000
+    1933.05  -.058596   .014472   .0000000
+    1933.10  -.188030  -.012586   .0000000
+    1933.15  -.173476   .013624   .0000000
+    1933.20  -.221474   .048831   .0000000
+    1933.25  -.211408   .107715   .0000000
+    1933.30  -.186714   .112536   .0000000
+    1933.35  -.179116   .019117   .0000000
+    1933.40  -.132931   .166511   .0000000
+    1933.45  -.129933   .222792   .0000000
+    1933.50  -.065800   .247600   .0000000
+    1933.55  -.061257   .170077   .0000000
+    1933.60  -.092890   .168864   .0000000
+    1933.65  -.011311   .114462   .0000000
+    1933.70  -.041560   .155440   .0000000
+    1933.75  -.007268   .135250   .0000000
+    1933.80  -.048100   .064100   .0000000
+    1933.85  -.030166   .051458   .0000000
+    1933.90  -.059454   .050490   .0000000
+    1933.95  -.000334  -.055485   .0000000
+    1934.00  -.047994   .160539   .0000000
+    1934.05  -.128330   .132957   .0000000
+    1934.10  -.104712  -.032479   .0000000
+    1934.15  -.123185   .026628   .0000000
+    1934.20  -.156884   .046650   .0000000
+    1934.25  -.146878   .039678   .0000000
+    1934.30  -.212952   .066280   .0000000
+    1934.35  -.109680   .080647   .0000000
+    1934.40  -.064907   .218438   .0000000
+    1934.45  -.115818   .182058   .0000000
+    1934.50  -.093600   .146400   .0000000
+    1934.55  -.026049   .206821   .0000000
+    1934.60  -.036375   .197949   .0000000
+    1934.65  -.018200   .130900   .0000000
+    1934.70   .003251   .176660   .0000000
+    1934.75  -.019469   .192448   .0000000
+    1934.80   .016400   .142200   .0000000
+    1934.85   .017784   .028679   .0000000
+    1934.90  -.064537   .102035   .0000000
+    1934.95  -.095694   .072497   .0000000
+    1935.00  -.053671   .063917   .0000000
+    1935.05  -.169016   .020530   .0000000
+    1935.10   .005900   .019800   .0000000
+    1935.15  -.121309   .043629   .0000000
+    1935.20  -.067132   .059209   .0000000
+    1935.25  -.065954   .062433   .0000000
+    1935.30  -.066468   .107258   .0000000
+    1935.35  -.021635   .104029   .0000000
+    1935.40  -.033804   .063816   .0000000
+    1935.45  -.056658   .141422   .0000000
+    1935.50  -.052090   .108980   .0000000
+    1935.55  -.177790  -.002912   .0000000
+    1935.60  -.039043   .085087   .0000000
+    1935.65  -.133855   .062467   .0000000
+    1935.70  -.005002   .093252   .0000000
+    1935.75  -.055390   .041687   .0000000
+    1935.80  -.109561   .035449   .0000000
+    1935.85  -.173680   .091852   .0000000
+    1935.90  -.136077   .056437   .0000000
+    1935.95  -.015578   .047611   .0000000
+    1936.00   .023251   .090679   .0000000
+    1936.05  -.146877   .108152   .0000000
+    1936.10  -.047500   .098500   .0000000
+    1936.15  -.125964   .209295   .0000000
+    1936.20  -.159573   .182595   .0000000
+    1936.25  -.106700   .188100   .0000000
+    1936.30  -.080483   .175745   .0000000
+    1936.35  -.093917   .159713   .0000000
+    1936.40  -.037416   .107419   .0000000
+    1936.45   .060450   .167046   .0000000
+    1936.50  -.013617   .143961   .0000000
+    1936.55  -.000451   .115066   .0000000
+    1936.60  -.004315   .072471   .0000000
+    1936.65  -.043164   .033288   .0000000
+    1936.70  -.018826   .032928   .0000000
+    1936.75  -.074570  -.007560   .0000000
+    1936.80  -.106024  -.004717   .0000000
+    1936.85  -.100771  -.011306   .0000000
+    1936.90  -.188334   .029368   .0000000
+    1936.95  -.173300  -.047400   .0000000
+    1937.00  -.140145   .085152   .0000000
+    1937.05  -.193481   .094688   .0000000
+    1937.10  -.158811   .074182   .0000000
+    1937.15  -.245489   .179964   .0000000
+    1937.20  -.176313   .131564   .0000000
+    1937.25  -.121499   .208926   .0000000
+    1937.30  -.116696   .187814   .0000000
+    1937.35  -.040102   .222305   .0000000
+    1937.40  -.088573   .195445   .0000000
+    1937.45   .035320   .209660   .0000000
+    1937.50   .073386   .231432   .0000000
+    1937.55   .069100   .137800   .0000000
+    1937.60   .090711   .121711   .0000000
+    1937.65   .056677   .054539   .0000000
+    1937.70   .047138   .041472   .0000000
+    1937.75  -.015251  -.021479   .0000000
+    1937.80  -.008680  -.029054   .0000000
+    1937.85  -.107027  -.071074   .0000000
+    1937.90  -.105060  -.016349   .0000000
+    1937.95  -.217410  -.006067   .0000000
+    1938.00  -.204894   .021733   .0000000
+    1938.05  -.303156   .013057   .0000000
+    1938.10  -.147755   .071361   .0000000
+    1938.15  -.221442   .102093   .0000000
+    1938.20  -.226757   .130364   .0000000
+    1938.25  -.171954   .165869   .0000000
+    1938.30  -.138432   .225318   .0000000
+    1938.35  -.116911   .287363   .0000000
+    1938.40  -.046436   .227821   .0000000
+    1938.45  -.080914   .286589   .0000000
+    1938.50   .111257   .295958   .0000000
+    1938.55   .109700   .236000   .0000000
+    1938.60   .174186   .248989   .0000000
+    1938.65   .172035   .065415   .0000000
+    1938.70   .075919   .117887   .0000000
+    1938.75   .167309   .023607   .0000000
+    1938.80   .026111  -.003055   .0000000
+    1938.85  -.037869  -.061382   .0000000
+    1938.90  -.052264  -.072478   .0000000
+    1938.95  -.134539   .030821   .0000000
+    1939.00  -.301769   .106206   .0000000
+    1939.05  -.116858  -.001150   .0000000
+    1939.10  -.213896   .044637   .0000000
+    1939.15  -.161300   .101800   .0000000
+    1939.20  -.181054   .143917   .0000000
+    1939.25  -.192526   .184454   .0000000
+    1939.30  -.119371   .187626   .0000000
+    1939.35  -.079336   .224723   .0000000
+    1939.40  -.087263   .216206   .0000000
+    1939.45  -.044976   .211200   .0000000
+    1939.50   .023086   .214921   .0000000
+    1939.55   .072500   .233900   .0000000
+    1939.60   .118411   .224190   .0000000
+    1939.65   .146657   .195121   .0000000
+    1939.70   .095496   .180767   .0000000
+    1939.75   .089672   .076885   .0000000
+    1939.80   .069378   .045780   .0000000
+    1939.85   .042361   .018571   .0000000
+    1939.90   .005289   .055015   .0000000
+    1939.95  -.058344   .043141   .0000000
+    1940.00   .027411  -.055656   .0000000
+    1940.05  -.148565  -.007660   .0000000
+    1940.10  -.249170  -.018138   .0000000
+    1940.15  -.210741   .047170   .0000000
+    1940.20  -.226589   .078515   .0000000
+    1940.25  -.290100   .142200   .0000000
+    1940.30  -.223492   .078291   .0000000
+    1940.35  -.135189   .151824   .0000000
+    1940.40  -.175712   .271518   .0000000
+    1940.45  -.063144   .235042   .0000000
+    1940.50  -.029661   .260179   .0000000
+    1940.55   .019816   .250937   .0000000
+    1940.60   .162656   .241704   .0000000
+    1940.65   .098439   .147379   .0000000
+    1940.70   .065800   .175200   .0000000
+    1940.75   .125165   .119102   .0000000
+    1940.80   .070275   .085726   .0000000
+    1940.85   .053300   .043221   .0000000
+    1940.90   .004300  -.026993   .0000000
+    1940.95  -.049213   .034742   .0000000
+    1941.00   .033843   .015616   .0000000
+    1941.05   .033262  -.034927   .0000000
+    1941.10  -.043717   .028212   .0000000
+    1941.15  -.000032   .034220   .0000000
+    1941.20   .008262   .025372   .0000000
+    1941.25  -.118927   .084292   .0000000
+    1941.30  -.183857   .100975   .0000000
+    1941.35  -.159123   .204194   .0000000
+    1941.40  -.088480   .133512   .0000000
+    1941.45  -.092443   .191571   .0000000
+    1941.50  -.062260   .120954   .0000000
+    1941.55  -.073372   .253085   .0000000
+    1941.60   .012678   .219220   .0000000
+    1941.65  -.041165   .167041   .0000000
+    1941.70  -.086224   .093311   .0000000
+    1941.75   .001683   .150415   .0000000
+    1941.80   .068909   .186888   .0000000
+    1941.85  -.028851   .102187   .0000000
+    1941.90  -.020115   .119301   .0000000
+    1941.95   .145863   .081312   .0000000
+    1942.00  -.029700   .145400   .0000000
+    1942.05  -.086609   .107756   .0000000
+    1942.10   .034025   .019905   .0000000
+    1942.15  -.065983   .120029   .0000000
+    1942.20  -.026845   .031907   .0000000
+    1942.25  -.066569   .031627   .0000000
+    1942.30  -.008392   .098245   .0000000
+    1942.35  -.012154   .089180   .0000000
+    1942.40  -.068264   .122550   .0000000
+    1942.45  -.064544   .139302   .0000000
+    1942.50  -.032732   .122930   .0000000
+    1942.55  -.007371   .174390   .0000000
+    1942.60  -.089026   .117492   .0000000
+    1942.65  -.075180   .095255   .0000000
+    1942.70  -.036158   .121535   .0000000
+    1942.75  -.075817   .122674   .0000000
+    1942.80  -.089165   .085346   .0000000
+    1942.85  -.152931   .083401   .0000000
+    1942.90  -.103187   .103300   .0000000
+    1942.95  -.039915   .183470   .0000000
+    1943.00  -.090405   .234551   .0000000
+    1943.05  -.093652   .180949   .0000000
+    1943.10  -.061971   .090993   .0000000
+    1943.15  -.080573   .145031   .0000000
+    1943.20  -.051329   .138129   .0000000
+    1943.25   .001923   .184841   .0000000
+    1943.30  -.019745   .090795   .0000000
+    1943.35   .035917   .105605   .0000000
+    1943.40   .078661   .117427   .0000000
+    1943.45   .033384   .108682   .0000000
+    1943.50   .105491   .061063   .0000000
+    1943.55  -.047847  -.001805   .0000000
+    1943.60  -.036338   .050187   .0000000
+    1943.65   .022793   .034134   .0000000
+    1943.70   .070899   .024180   .0000000
+    1943.75   .004438   .011224   .0000000
+    1943.80  -.122806   .001491   .0000000
+    1943.85  -.144327   .028604   .0000000
+    1943.90  -.120953   .074676   .0000000
+    1943.95  -.253705   .095567   .0000000
+    1944.00  -.188535   .058157   .0000000
+    1944.05  -.213170   .082626   .0000000
+    1944.10  -.133388   .103606   .0000000
+    1944.15  -.243714   .045296   .0000000
+    1944.20  -.280687   .123924   .0000000
+    1944.25  -.099857   .132270   .0000000
+    1944.30  -.102089   .098582   .0000000
+    1944.35   .034173   .068935   .0000000
+    1944.40   .052475   .180058   .0000000
+    1944.45   .204300   .170300   .0000000
+    1944.50   .174930   .065429   .0000000
+    1944.55   .236527   .061957   .0000000
+    1944.60   .208915  -.039275   .0000000
+    1944.65   .171265  -.034213   .0000000
+    1944.70   .158830  -.127515   .0000000
+    1944.75  -.041184  -.081574   .0000000
+    1944.80  -.054199  -.141045   .0000000
+    1944.85  -.122864  -.138640   .0000000
+    1944.90  -.173922  -.079180   .0000000
+    1944.95  -.266129  -.029277   .0000000
+    1945.00  -.117527   .046084   .0000000
+    1945.05  -.172366   .076642   .0000000
+    1945.10  -.218168   .119657   .0000000
+    1945.15  -.269380   .084980   .0000000
+    1945.20  -.105476   .185324   .0000000
+    1945.25  -.252149   .252063   .0000000
+    1945.30  -.119100   .283700   .0000000
+    1945.35  -.098829   .296275   .0000000
+    1945.40   .041205   .299528   .0000000
+    1945.45   .070100   .328900   .0000000
+    1945.50   .161286   .243704   .0000000
+    1945.55   .111735   .157891   .0000000
+    1945.60   .279930   .129955   .0000000
+    1945.65   .210791   .086270   .0000000
+    1945.70   .167958  -.034212   .0000000
+    1945.75   .387534  -.060857   .0000000
+    1945.80   .203824  -.175689   .0000000
+    1945.85  -.009562  -.200723   .0000000
+    1945.90   .082043  -.254725   .0000000
+    1945.95  -.167775   .011828   .0000000
+    1946.00  -.105894  -.143593   .0000000
+    1946.05  -.238800  -.143600   .0000000
+    1946.10  -.161621  -.086524   .0000000
+    1946.15  -.292120  -.032154   .0000000
+    1946.20  -.369539   .005399   .0000000
+    1946.25  -.243748   .106804   .0000000
+    1946.30  -.260700   .154700   .0000000
+    1946.35  -.227834   .263600   .0000000
+    1946.40  -.105890   .285597   .0000000
+    1946.45  -.219288   .379684   .0000000
+    1946.50  -.063112   .324947   .0000000
+    1946.55   .056826   .295929   .0000000
+    1946.60   .082673   .302031   .0000000
+    1946.65   .134672   .242569   .0000000
+    1946.70   .188918   .179805   .0000000
+    1946.75   .230337   .153795   .0000000
+    1946.80   .230653   .126700   .0000000
+    1946.85   .324003  -.014854   .0000000
+    1946.90   .083430  -.030662   .0000000
+    1946.95   .071411  -.089697   .0000000
+    1947.00   .033741  -.095143   .0000000
+    1947.05  -.003804  -.062704   .0000000
+    1947.10  -.105253  -.159077   .0000000
+    1947.15  -.140851  -.129656   .0000000
+    1947.20  -.217901  -.081483   .0000000
+    1947.25  -.204585  -.117897   .0000000
+    1947.30  -.260050   .044441   .0000000
+    1947.35  -.358491   .174358   .0000000
+    1947.40  -.202905   .152120   .0000000
+    1947.45  -.254945   .153196   .0000000
+    1947.50  -.137802   .288460   .0000000
+    1947.55  -.094877   .287150   .0000000
+    1947.60   .023089   .335301   .0000000
+    1947.65   .060945   .357002   .0000000
+    1947.70   .113078   .313539   .0000000
+    1947.75   .120020   .273848   .0000000
+    1947.80   .175924   .294753   .0000000
+    1947.85   .206992   .222458   .0000000
+    1947.90   .140099   .174866   .0000000
+    1947.95   .157648   .156590   .0000000
+    1948.00   .225955   .143369   .0000000
+    1948.05   .157129   .071959   .0000000
+    1948.10   .150055   .021583   .0000000
+    1948.15   .171585  -.019271   .0000000
+    1948.20   .145400  -.041700   .0000000
+    1948.25   .147680  -.064178   .0000000
+    1948.30   .053983  -.075038   .0000000
+    1948.35  -.045837  -.072956   .0000000
+    1948.40  -.091431  -.067958   .0000000
+    1948.45  -.076060   .010010   .0000000
+    1948.50  -.106057   .014185   .0000000
+    1948.55  -.097516   .015430   .0000000
+    1948.60  -.130026   .063326   .0000000
+    1948.65  -.136613   .110269   .0000000
+    1948.70  -.236212   .081015   .0000000
+    1948.75  -.122022   .165015   .0000000
+    1948.80  -.082996   .181422   .0000000
+    1948.85  -.102718   .194496   .0000000
+    1948.90  -.056364   .288203   .0000000
+    1948.95  -.060517   .337067   .0000000
+    1949.00  -.038708   .272547   .0000000
+    1949.05   .050766   .309560   .0000000
+    1949.10  -.036412   .244629   .0000000
+    1949.15   .092035   .269182   .0000000
+    1949.20   .117000   .151400   .0000000
+    1949.25   .167243   .119693   .0000000
+    1949.30   .167879   .072692   .0000000
+    1949.35   .134722   .072089   .0000000
+    1949.40   .145230   .001256   .0000000
+    1949.45   .089326  -.093079   .0000000
+    1949.50   .154408  -.078539   .0000000
+    1949.55   .073064  -.060780   .0000000
+    1949.60   .043289  -.033614   .0000000
+    1949.65  -.013262  -.122149   .0000000
+    1949.70  -.164254  -.145566   .0000000
+    1949.75  -.163827  -.040926   .0000000
+    1949.80  -.236686  -.074967   .0000000
+    1949.85  -.237557   .040887   .0000000
+    1949.90  -.224455   .063927   .0000000
+    1949.95  -.290753   .157252   .0000000
+    1950.00  -.220930   .287191   .0000000
+    1950.05  -.222249   .248856   .0000000
+    1950.10  -.144425   .335072   .0000000
+    1950.15  -.061597   .365805   .0000000
+    1950.20   .023935   .338761   .0000000
+    1950.25   .046105   .332438   .0000000
+    1950.30   .127597   .297890   .0000000
+    1950.35   .258100   .317900   .0000000
+    1950.40   .238376   .209297   .0000000
+    1950.45   .300481   .140257   .0000000
+    1950.50   .287564   .015981   .0000000
+    1950.55   .286892   .093660   .0000000
+    1950.60   .270451  -.079337   .0000000
+    1950.65   .258830  -.098373   .0000000
+    1950.70   .116078  -.227489   .0000000
+    1950.75  -.010608  -.232154   .0000000
+    1950.80  -.087101  -.278552   .0000000
+    1950.85  -.183732  -.306109   .0000000
+    1950.90  -.306855  -.243256   .0000000
+    1950.95  -.321363  -.117027   .0000000
+    1951.00  -.365022   .014938   .0000000
+    1951.05  -.304225   .001015   .0000000
+    1951.10  -.369566   .099280   .0000000
+    1951.15  -.378609   .229045   .0000000
+    1951.20  -.253183   .324399   .0000000
+    1951.25  -.183608   .395804   .0000000
+    1951.30  -.113043   .407480   .0000000
+    1951.35   .025400   .357400   .0000000
+    1951.40   .169217   .366203   .0000000
+    1951.45   .251730   .336817   .0000000
+    1951.50   .268771   .266485   .0000000
+    1951.55   .325834   .218909   .0000000
+    1951.60   .416116   .097318   .0000000
+    1951.65   .369300   .007200   .0000000
+    1951.70   .311161  -.128485   .0000000
+    1951.75   .291955  -.216643   .0000000
+    1951.80   .143989  -.282893   .0000000
+    1951.85   .078168  -.298582   .0000000
+    1951.90  -.072498  -.293768   .0000000
+    1951.95  -.121266  -.283286   .0000000
+    1952.00  -.228941  -.289334   .0000000
+    1952.05  -.384844  -.280973   .0000000
+    1952.10  -.425432  -.085033   .0000000
+    1952.15  -.415187   .007147   .0000000
+    1952.20  -.438050   .104950   .0000000
+    1952.25  -.373937   .199595   .0000000
+    1952.30  -.214791   .330880   .0000000
+    1952.35  -.237011   .436678   .0000000
+    1952.40  -.206498   .424479   .0000000
+    1952.45  -.114247   .462331   .0000000
+    1952.50   .053909   .452330   .0000000
+    1952.55   .152162   .415113   .0000000
+    1952.60   .302914   .389455   .0000000
+    1952.65   .303583   .272217   .0000000
+    1952.70   .379665   .235664   .0000000
+    1952.75   .284255   .115197   .0000000
+    1952.80   .290673   .004058   .0000000
+    1952.85   .273678  -.093103   .0000000
+    1952.90   .201204  -.160920   .0000000
+    1952.95   .092473  -.181244   .0000000
+    1953.00   .123811  -.231433   .0000000
+    1953.05  -.059768  -.287803   .0000000
+    1953.10  -.099969  -.181194   .0000000
+    1953.15  -.117820  -.081862   .0000000
+    1953.20  -.288724  -.112507   .0000000
+    1953.25  -.256646  -.080086   .0000000
+    1953.30  -.275672   .047975   .0000000
+    1953.35  -.343300   .172800   .0000000
+    1953.40  -.325734   .244282   .0000000
+    1953.45  -.275838   .294124   .0000000
+    1953.50  -.079095   .448538   .0000000
+    1953.55  -.029260   .528535   .0000000
+    1953.60   .021387   .421200   .0000000
+    1953.65   .022300   .491500   .0000000
+    1953.70   .219443   .448367   .0000000
+    1953.75   .204160   .288329   .0000000
+    1953.80   .197400   .214300   .0000000
+    1953.85   .224082   .155717   .0000000
+    1953.90   .236336   .022065   .0000000
+    1953.95   .184200   .032600   .0000000
+    1954.00   .136603  -.090735   .0000000
+    1954.05   .015320  -.126354   .0000000
+    1954.10   .036575  -.126953   .0000000
+    1954.15  -.024887  -.150786   .0000000
+    1954.20  -.154021  -.147087   .0000000
+    1954.25  -.164556  -.108061   .0000000
+    1954.30  -.195623  -.112539   .0000000
+    1954.35  -.239513  -.079926   .0000000
+    1954.40  -.212137   .053878   .0000000
+    1954.45  -.133960   .116843   .0000000
+    1954.50  -.215416   .144317   .0000000
+    1954.55  -.127059   .218349   .0000000
+    1954.60  -.161882   .209542   .0000000
+    1954.65   .022753   .272072   .0000000
+    1954.70  -.093222   .296413   .0000000
+    1954.75  -.075965   .339411   .0000000
+    1954.80   .010000   .310600   .0000000
+    1954.85  -.019012   .279865   .0000000
+    1954.90  -.029023   .206222   .0000000
+    1954.95   .099499   .204533   .0000000
+    1955.00   .017291   .175152   .0000000
+    1955.05   .124344   .144779   .0000000
+    1955.10   .029988   .049576   .0000000
+    1955.15   .124568   .140522   .0000000
+    1955.20   .158988   .106674   .0000000
+    1955.25   .105321   .083753   .0000000
+    1955.30   .046473  -.027556   .0000000
+    1955.35   .065044  -.038566   .0000000
+    1955.40   .041687  -.021509   .0000000
+    1955.45  -.001269  -.080686   .0000000
+    1955.50  -.055935  -.006263   .0000000
+    1955.55   .003124   .039639   .0000000
+    1955.60  -.109785   .034162   .0000000
+    1955.65  -.137337   .039769   .0000000
+    1955.70  -.226992   .043228   .0000000
+    1955.75  -.229553   .066146   .0000000
+    1955.80  -.219600   .137300   .0000000
+    1955.85  -.189281   .159204   .0000000
+    1955.90  -.219306   .167037   .0000000
+    1955.95  -.203131   .233918   .0000000
+    1956.00  -.070247   .341707   .0000000
+    1956.05  -.007398   .192673   .0000000
+    1956.10   .016713   .339190   .0000000
+    1956.15   .038798   .279099   .0000000
+    1956.20   .117409   .334384   .0000000
+    1956.25   .083759   .248766   .0000000
+    1956.30   .152189   .209354   .0000000
+    1956.35   .160798   .161261   .0000000
+    1956.40   .211912   .132122   .0000000
+    1956.45   .219468   .113755   .0000000
+    1956.50   .212688   .021181   .0000000
+    1956.55   .169132  -.055280   .0000000
+    1956.60   .146149   .000797   .0000000
+    1956.65   .059496  -.049301   .0000000
+    1956.70   .018377  -.044723   .0000000
+    1956.75  -.050507  -.037579   .0000000
+    1956.80  -.204692  -.059809   .0000000
+    1956.85  -.228535   .004844   .0000000
+    1956.90  -.318792   .068497   .0000000
+    1956.95  -.290530   .108736   .0000000
+    1957.00  -.401787   .175879   .0000000
+    1957.05  -.340710   .217146   .0000000
+    1957.10  -.222754   .314496   .0000000
+    1957.15  -.224552   .381610   .0000000
+    1957.20  -.194858   .441895   .0000000
+    1957.25  -.099300   .411100   .0000000
+    1957.30   .007920   .425095   .0000000
+    1957.35   .076323   .453030   .0000000
+    1957.40   .152477   .481943   .0000000
+    1957.45   .235240   .383132   .0000000
+    1957.50   .240076   .308449   .0000000
+    1957.55   .292684   .272059   .0000000
+    1957.60   .345486   .208463   .0000000
+    1957.65   .271110   .018347   .0000000
+    1957.70   .229000  -.058924   .0000000
+    1957.75   .126351  -.051070   .0000000
+    1957.80   .039747  -.126282   .0000000
+    1957.85  -.003182  -.164541   .0000000
+    1957.90  -.069900  -.130259   .0000000
+    1957.95  -.209601  -.084401   .0000000
+    1958.00  -.305232  -.027916   .0000000
+    1958.05  -.343994   .036549   .0000000
+    1958.10  -.333001   .173344   .0000000
+    1958.15  -.335927   .215384   .0000000
+    1958.20  -.305676   .288831   .0000000
+    1958.25  -.300800   .338700   .0000000
+    1958.30  -.179030   .409116   .0000000
+    1958.35  -.101308   .407552   .0000000
+    1958.40  -.041921   .434062   .0000000
+    1958.45   .051783   .422110   .0000000
+    1958.50   .098517   .451402   .0000000
+    1958.55   .180900   .370800   .0000000
+    1958.60   .203233   .334686   .0000000
+    1958.65   .303286   .248765   .0000000
+    1958.70   .276253   .146280   .0000000
+    1958.75   .267226   .075078   .0000000
+    1958.80   .266128   .119921   .0000000
+    1958.85   .213073  -.055607   .0000000
+    1958.90   .164238  -.060513   .0000000
+    1958.95   .101815  -.044255   .0000000
+    1959.00   .016105  -.129490   .0000000
+    1959.05  -.095165  -.054368   .0000000
+    1959.10  -.153168  -.068371   .0000000
+    1959.15  -.195475  -.058794   .0000000
+    1959.20  -.253927   .017351   .0000000
+    1959.25  -.238076   .061599   .0000000
+    1959.30  -.240657   .198641   .0000000
+    1959.35  -.252739   .262649   .0000000
+    1959.40  -.214144   .308577   .0000000
+    1959.45  -.142987   .396049   .0000000
+    1959.50  -.037927   .401546   .0000000
+    1959.55  -.034800   .382000   .0000000
+    1959.60   .106849   .415365   .0000000
+    1959.65   .129032   .368627   .0000000
+    1959.70   .175804   .336333   .0000000
+    1959.75   .222541   .303817   .0000000
+    1959.80   .203199   .215274   .0000000
+    1959.85   .202366   .197922   .0000000
+    1959.90   .175341   .085444   .0000000
+    1959.95   .132030   .058528   .0000000
+    1960.00   .008173   .006252   .0000000
+    1960.05  -.003037  -.014016   .0000000
+    1960.10  -.096783  -.064746   .0000000
+    1960.15  -.049029  -.015531   .0000000
+    1960.20  -.070352   .005059   .0000000
+    1960.25  -.093472  -.033430   .0000000
+    1960.30  -.144743   .036124   .0000000
+    1960.35  -.136289   .091137   .0000000
+    1960.40  -.185600   .139573   .0000000
+    1960.45  -.144215   .156992   .0000000
+    1960.50  -.093892   .234106   .0000000
+    1960.55  -.141900   .251100   .0000000
+    1960.60  -.082108   .224170   .0000000
+    1960.65  -.054888   .246897   .0000000
+    1960.70   .000500   .272000   .0000000
+    1960.75   .027616   .261292   .0000000
+    1960.80   .028359   .217569   .0000000
+    1960.85  -.026324   .268219   .0000000
+    1960.90   .045255   .178998   .0000000
+    1960.95   .018041   .148022   .0000000
+    1961.00  -.010002   .178091   .0000000
+    1961.05   .014227   .129318   .0000000
+    1961.10  -.034519   .135146   .0000000
+    1961.15   .020541   .071804   .0000000
+    1961.20  -.008860   .093978   .0000000
+    1961.25  -.009493   .077921   .0000000
+    1961.30  -.013247   .104033   .0000000
+    1961.35  -.011871   .106863   .0000000
+    1961.40  -.055595   .050930   .0000000
+    1961.45  -.006478   .093211   .0000000
+    1961.50  -.052932   .075634   .0000000
+    1961.55  -.008206   .085606   .0000000
+    1961.60  -.051487   .123137   .0000000
+    1961.65  -.017757   .095707   .0000000
+    1961.70  -.062010   .097662   .0000000
+    1961.75  -.081090   .089862   .0000000
+    1961.80  -.110423   .077764   .0000000
+    1961.85  -.090300   .114700   .0000000
+    1961.90  -.167482   .169747   .0000000
+    1961.95  -.142626   .162677   .0000000
+    1962.00  -.033900   .203700   .0376300
+    1962.05  -.050170   .211840   .0338900
+    1962.10  -.002320   .229940   .0290470
+    1962.15   .006920   .252420   .0240100
+    1962.20   .024920   .267420   .0164880
+    1962.25   .036710   .249550   .0102410
+    1962.30   .072420   .225380  -.0005960
+    1962.35   .094690   .208290  -.0104660
+    1962.40   .093250   .193210  -.0187080
+    1962.45   .107960   .129190  -.0156280
+    1962.50   .095310   .127660  -.0096470
+    1962.55   .086920   .113560   .0013710
+    1962.60   .069240   .063740   .0066930
+    1962.65   .055400   .049150   .0122100
+    1962.70   .008500   .041810   .0142490
+    1962.75  -.014150   .060290   .0105620
+    1962.80  -.066110   .050990   .0038340
+    1962.85  -.113040   .105830  -.0066870
+    1962.90  -.153970   .139440  -.0175860
+    1962.95  -.160030   .176030  -.0286510
+    1963.00  -.205590   .212120  -.0332190
+    1963.05  -.201720   .264370  -.0339840
+    1963.10  -.173000   .287920  -.0309280
+    1963.15  -.148670   .335970  -.0308550
+    1963.20  -.099870   .335960  -.0392730
+    1963.25  -.044310   .360350  -.0507330
+    1963.30   .021450   .354760  -.0615850
+    1963.35   .093210   .324570  -.0762480
+    1963.40   .152910   .306570  -.0852720
+    1963.45   .175600   .263720  -.0898040
+    1963.50   .230800   .226120  -.0911400
+    1963.55   .218840   .193180  -.0850800
+    1963.60   .207420   .115940  -.0800220
+    1963.65   .148670   .085350  -.0829150
+    1963.70   .096090   .028620  -.0876470
+    1963.75   .053710  -.010670  -.0974610
+    1963.80  -.017030  -.023980  -.1141360
+    1963.85  -.106000  -.008970  -.0370110
+    1963.90  -.162800   .001510  -.0481380
+    1963.95  -.172400   .042730  -.0669830
+    1964.00  -.225800   .073470  -.0808460
+    1964.05  -.277190   .142370  -.0959270
+    1964.10  -.316290   .213450  -.1133170
+    1964.15  -.278600   .281690  -.1248710
+    1964.20  -.246040   .323490  -.1403400
+    1964.25  -.179450   .390780  -.0597570
+    1964.30  -.106780   .417820  -.0730700
+    1964.35  -.051130   .465850  -.0918930
+    1964.40  -.000180   .454570  -.1099970
+    1964.45   .070820   .457390  -.1212000
+    1964.50   .122250   .410720  -.1147420
+    1964.55   .188300   .371690  -.1164510
+    1964.60   .233650   .317960  -.1142910
+    1964.65   .243950   .251720  -.1118720
+    1964.70   .228520   .159640  -.0165730
+    1964.75   .202930   .093010  -.0291840
+    1964.80   .163200   .037870  -.0476800
+    1964.85   .107390   .018400  -.0671060
+    1964.90   .042310  -.014260  -.0837400
+    1964.95  -.022710  -.019620  -.1003350
+    1965.00  -.089890  -.005080  -.0182700
+    1965.05  -.109340  -.011770  -.0248210
+    1965.10  -.187520   .079690  -.0413790
+    1965.15  -.220180   .108850  -.0595530
+    1965.20  -.239960   .170890   .0201840
+    1965.25  -.239880   .232370  -.0057010
+    1965.30  -.221390   .286720  -.0330600
+    1965.35  -.177010   .329310  -.0487050
+    1965.40  -.151550   .380270  -.0661190
+    1965.45  -.115410   .406270  -.0832370
+    1965.50  -.033960   .439390   .0111050
+    1965.55   .047390   .425360   .0060030
+    1965.60   .084440   .411480  -.0046230
+    1965.65   .105520   .371940  -.0087120
+    1965.70   .137420   .304360   .0758570
+    1965.75   .168630   .248710   .0544290
+    1965.80   .200590   .176560   .0353090
+    1965.85   .178810   .143930   .0119610
+    1965.90   .158060   .087030  -.0139660
+    1965.95   .115300   .054420  -.0327310
+    1966.00   .047900   .005530  -.0467430
+    1966.05   .003210   .018400  -.0442320
+    1966.10  -.020370   .017330  -.0397280
+    1966.15  -.092720   .042830  -.0398940
+    1966.20  -.126630   .060540  -.0393290
+    1966.25  -.126250   .087680  -.0396900
+    1966.30  -.167720   .130720  -.0463990
+    1966.35  -.161730   .172470  -.0516050
+    1966.40  -.151510   .196200  -.0495510
+    1966.45  -.128550   .234880  -.0432800
+    1966.50  -.124150   .306450  -.0321410
+    1966.55  -.081600   .310620  -.0125560
+    1966.60  -.055550   .328730   .0003880
+    1966.65  -.017090   .338450   .0095020
+    1966.70   .006590   .333040   .0161450
+    1966.75   .042120   .311320   .0146070
+    1966.80   .091960   .283830   .0121820
+    1966.85   .094580   .252760   .0098330
+    1966.90   .116210   .220110   .0051870
+    1966.95   .135490   .172910   .0058710
+    1967.00   .097100   .162300   .0124750
+    1967.05   .070010   .139910   .0184380
+    1967.10   .048010   .130910   .0211400
+    1967.15   .032320   .139020   .0229620
+    1967.20   .014320   .136220   .0194950
+    1967.25   .005330   .130830   .0167670
+    1967.30  -.008570   .144330   .0133100
+    1967.35  -.013670   .164530   .0065720
+    1967.40  -.008560   .148640   .0071750
+    1967.45   .003850   .168250   .0176880
+    1967.50  -.011850   .176050   .0301300
+    1967.55  -.008350   .193960   .0477730
+    1967.60  -.011740   .189260   .0663160
+    1967.65  -.001430   .186370   .0768980
+    1967.70  -.029530   .191370   .0890110
+    1967.75  -.033830   .184480   .0963740
+    1967.80  -.030620   .195580   .0971770
+    1967.85  -.035120   .205980   .0953500
+    1967.90  -.026910   .212290   .0952730
+    1967.95  -.026200   .222600   .0931660
+    1968.00  -.034000   .239700   .0951090
+    1968.05  -.005100   .237510   .0987210
+    1968.10  -.004790   .238810  -.0023360
+    1968.15  -.001680   .237720  -.0036230
+    1968.20   .030920   .241420  -.0012590
+    1968.25   .015230   .230330  -.0024360
+    1968.30   .046430   .242330  -.0107430
+    1968.35   .060630   .223740  -.0118800
+    1968.40   .057340   .203640  -.0136970
+    1968.45   .059250   .198750  -.0100140
+    1968.50   .068150   .173950   .0057390
+    1968.55   .067460   .166360   .0170720
+    1968.60   .052060   .142860   .0302750
+    1968.65   .027070   .135670   .0354390
+    1968.70   .005570   .135770   .0390020
+    1968.75  -.013830   .118780   .0406750
+    1968.80  -.042020   .106380   .0419680
+    1968.85  -.084920   .124390   .0356310
+    1968.90  -.139410   .147490   .0341150
+    1968.95  -.126810   .185200   .0350180
+    1969.00  -.139900   .214900   .0335710
+    1969.05  -.124100   .251100   .0372640
+    1969.10  -.106290   .262510   .0391280
+    1969.15  -.095880   .326120   .0309210
+    1969.20  -.079080   .337320   .0246540
+    1969.25  -.036380   .347630   .0124870
+    1969.30  -.000970   .351930   .0000710
+    1969.35   .055030   .361340  -.0043960
+    1969.40   .090640   .316240  -.0064830
+    1969.45   .125150   .295450  -.0097100
+    1969.50   .133450   .267750   .0040040
+    1969.55   .150150   .228760   .0133570
+    1969.60   .134960   .187660   .0232700
+    1969.65   .117370   .136570   .0362230
+    1969.70   .095770   .114170   .0343170
+    1969.75   .058680   .100680   .0285300
+    1969.80   .011380   .065480   .0246530
+    1969.85  -.011720   .063480   .0178860
+    1969.90  -.062210   .070390   .0119090
+    1969.95  -.122410   .082300   .0067420
+    1970.00  -.163200   .117800   .0047260
+    1970.05  -.200400   .175210  -.0043710
+    1970.10  -.217990   .213110  -.0072880
+    1970.15  -.167580   .287720  -.0178350
+    1970.20  -.155880   .325820  -.0272820
+    1970.25  -.140180   .357830  -.0373190
+    1970.30  -.092070   .388230  -.0515660
+    1970.35  -.036570   .394540  -.0614930
+    1970.40   .009940   .407740  -.0624500
+    1970.45   .077750   .394750  -.0622170
+    1970.50   .130150   .370950  -.0542940
+    1970.55   .166660   .330060  -.0435010
+    1970.60   .200960   .262860  -.0298880
+    1970.65   .205070   .222970  -.0204550
+    1970.70   .196270   .160070  -.0125620
+    1970.75   .178680   .101880  -.0144890
+    1970.80   .141580   .043980  -.0185570
+    1970.85   .064680   .026980  -.0256240
+    1970.90   .013790   .006190  -.0358210
+    1970.95  -.045910  -.008500  -.0395080
+    1971.00  -.094800   .014300  -.0386560
+    1971.05  -.145000   .063100  -.0423130
+    1971.10  -.193590   .108210  -.0417900
+    1971.15  -.226190   .164220  -.0385280
+    1971.20  -.248080   .221720  -.0447450
+    1971.25  -.221480   .298620  -.0573030
+    1971.30  -.200070   .353230  -.0652400
+    1971.35  -.187870   .384540  -.0779880
+    1971.40  -.131860   .428740  -.0875360
+    1971.45  -.046460   .458050  -.0888030
+    1971.50   .029850   .466050  -.0875110
+    1971.55   .104850   .444960  -.0828490
+    1971.60   .162960   .400160  -.0790560
+    1971.65   .211970   .361270  -.0778240
+    1971.70   .233970   .304470  -.0821020
+    1971.75   .225880   .218780  -.0877900
+    1971.80   .208880   .167780  -.1047780
+    1971.85   .181790   .101490  -.1220760
+    1971.90   .140590   .071290  -.1384440
+    1971.95   .084400   .039100  -.1490720
+    1972.00   .031820   .019710  -.0474900
+    1972.05  -.029350   .019540  -.1025340
+    1972.10  -.085000   .023390  -.1641870
+    1972.15  -.113320   .038780  -.2239540
+    1972.20  -.170800   .100130  -.2844970
+    1972.25  -.178190   .157660  -.3489940
+    1972.30  -.193440   .216340  -.4137270
+    1972.35  -.177900   .273960  -.4751870
+    1972.40  -.148990   .302730  -.5400640
+    1972.45  -.101340   .350600  -.5937930
+    1972.50  -.048730   .379110   .3591600
+    1972.55   .000850   .403050   .3124850
+    1972.60   .057740   .404590   .2662670
+    1972.65   .089870   .408890   .2208620
+    1972.70   .119360   .367470   .1685090
+    1972.75   .125150   .330830   .1107640
+    1972.80   .161790   .292310   .0507720
+    1972.85   .164080   .229730  -.0089650
+    1972.90   .154490   .213280  -.0670860
+    1972.95   .142830   .172430  -.1269170
+    1973.00   .123610   .125710   .8114390
+    1973.05   .091980   .093390   .7545430
+    1973.10   .055100   .086390   .6973530
+    1973.15   .024830   .089030   .6317400
+    1973.20  -.023270   .093100   .5731350
+    1973.25  -.050430   .105840   .5070570
+    1973.30  -.090950   .145940   .4422160
+    1973.35  -.107840   .174450   .3817300
+    1973.40  -.116560   .201930   .3252920
+    1973.45  -.109350   .245230   .2701480
+    1973.50  -.097490   .273170   .2259990
+    1973.55  -.074020   .300780   .1814520
+    1973.60  -.053250   .315260   .1350890
+    1973.65  -.014030   .319880   .0897480
+    1973.70   .014500   .323150   .0381470
+    1973.75   .034570   .333410  -.0166510
+    1973.80   .069270   .331530  -.0721010
+    1973.85   .082930   .288210  -.1313010
+    1973.90   .094550   .270630  -.1950090
+    1973.95   .105000   .265120  -.2487100
+    1974.00   .113200   .225230   .7002310
+    1974.05   .091980   .203370   .6520660
+    1974.10   .077870   .176520   .6063570
+    1974.15   .078890   .171530   .5537260
+    1974.20   .050750   .157750   .4983770
+    1974.25   .029950   .179730   .4438800
+    1974.30   .029220   .187280   .3871170
+    1974.35   .015470   .182320   .3220300
+    1974.40   .005080   .179910   .2706650
+    1974.45  -.016260   .187160   .2230120
+    1974.50   .019490   .195670   .1828640
+    1974.55   .022340   .210720   .1474170
+    1974.60   .019950   .212310   .1109470
+    1974.65   .016610   .209630   .0663730
+    1974.70   .010980   .202280   .0252920
+    1974.75   .003730   .216240  -.0251410
+    1974.80  -.012130   .232720  -.0802570
+    1974.85  -.024480   .240310  -.1325930
+    1974.90  -.033410   .254200  -.1905590
+    1974.95  -.043730   .267890  -.2441870
+    1975.00  -.050120   .270650   .7083150
+    1975.05  -.053700   .285590   .6585310
+    1975.10  -.030920   .306400   .6055510
+    1975.15  -.019500   .329540   .5525990
+    1975.20   .010660   .335300   .5013940
+    1975.25   .043890   .337490   .4422900
+    1975.30   .070800   .313520   .3887900
+    1975.35   .089170   .296380   .3321200
+    1975.40   .103620   .270190   .2824180
+    1975.45   .125630   .246550   .2386220
+    1975.50   .146030   .234320   .1972230
+    1975.55   .132900   .199120   .1637560
+    1975.60   .126320   .176400   .1307220
+    1975.65   .119740   .152660   .0878550
+    1975.70   .092300   .118640   .0444550
+    1975.75   .062280   .103450  -.0020730
+    1975.80   .020950   .098770  -.0586960
+    1975.85  -.041700   .103980  -.1161850
+    1975.90  -.072420   .130280  -.1711520
+    1975.95  -.127410   .157660  -.2229470
+    1976.00  -.144340   .193270   .7251490
+    1976.05  -.151500   .247180   .6775030
+    1976.10  -.156180   .289100   .6214200
+    1976.15  -.145290   .329230   .5693700
+    1976.20  -.117400   .367510   .5147140
+    1976.25  -.095400   .393190   .4538780
+    1976.30  -.041730   .425870   .3908040
+    1976.35  -.008800   .433170   .3343790
+    1976.40   .046660   .423870   .2739120
+    1976.45   .111730   .401030   .2245910
+    1976.50   .156480   .370970   .1856130
+    1976.55   .195270   .330200   .1405230
+    1976.60   .229060   .289950   .0972370
+    1976.65   .235790   .241180   .0569540
+    1976.70   .220780   .189190   .0056210
+    1976.75   .172600   .157950  -.0542780
+    1976.80   .159550   .117960  -.1129530
+    1976.85   .113500   .093390  -.1756720
+    1976.90   .053590   .071240  -.2292200
+    1976.95  -.004070   .065610  -.2824520
+    1977.00  -.068560   .069220   .6657090
+    1977.05  -.139290   .118540   .6143360
+    1977.10  -.188740   .156640   .5651410
+    1977.15  -.226790   .231150   .5121100
+    1977.20  -.228460   .290850   .4600880
+    1977.25  -.216780   .362450   .4021850
+    1977.30  -.174880   .414280   .3418380
+    1977.35  -.133880   .458560   .2827490
+    1977.40  -.071690   .489080   .2309290
+    1977.45   .012030   .504510   .1838870
+    1977.50   .082770   .495390   .1468180
+    1977.55   .149380   .454360   .1142770
+    1977.60   .210730   .410780   .0768320
+    1977.65   .249250   .351780   .0408590
+    1977.70   .269380   .292600  -.0085350
+    1977.75   .263020   .222970  -.0636710
+    1977.80   .243710   .151260  -.1196200
+    1977.85   .195430   .095930  -.1771710
+    1977.90   .133680   .061560  -.2363140
+    1977.95   .063460   .030340  -.2912280
+    1978.00  -.000350   .027050   .6495220
+    1978.05  -.067600   .036990   .5903240
+    1978.10  -.136660   .072860   .5357610
+    1978.15  -.182100   .105900   .4701360
+    1978.20  -.214500   .163140   .4061470
+    1978.25  -.236230   .234600   .3442840
+    1978.30  -.229900   .312160   .2835510
+    1978.35  -.200940   .379730   .2243820
+    1978.40  -.157910   .433860   .1677820
+    1978.45  -.110240   .468530   .1209930
+    1978.50  -.044260   .492280   .0799780
+    1978.55   .033760   .496900   .0461430
+    1978.60   .098530   .478150   .0128410
+    1978.65   .150640   .444140  -.0312900
+    1978.70   .194320   .408640  -.0754340
+    1978.75   .221790   .353150  -.1252120
+    1978.80   .234780   .290520  -.1826230
+    1978.85   .228930   .229200  -.2362770
+    1978.90   .196110   .176960  -.2927440
+    1978.95   .169350   .117020  -.3501350
+    1979.00   .140600   .085500   .5990550
+    1979.05   .098400   .059560   .5436940
+    1979.10   .023200   .064460   .4873690
+    1979.15  -.017580   .069020   .4372970
+    1979.20  -.067290   .087350   .3838980
+    1979.25  -.111680   .125490   .3253590
+    1979.30  -.137550   .174350   .2698590
+    1979.35  -.152140   .218350   .2124640
+    1979.40  -.158240   .263800   .1596610
+    1979.45  -.145410   .301690   .1196450
+    1979.50  -.120340   .348910   .0786440
+    1979.55  -.093020   .378590   .0405690
+    1979.60  -.032280   .404160   .0082610
+    1979.65   .013040   .420310  -.0308800
+    1979.70   .053770   .423000  -.0723390
+    1979.75   .087730   .410920  -.1151000
+    1979.80   .103920   .386840  -.1669670
+    1979.85   .133930   .357770  -.2160390
+    1979.90   .132630   .322640  -.2606420
+    1979.95   .135250   .285040  -.3086520
+    1980.00   .142150   .250260   .6449390
+    1980.05   .121150   .224530   .5994770
+    1980.10   .089390   .199240   .5548120
+    1980.15   .064250   .182700   .5085250
+    1980.20   .043590   .179240   .4666600
+    1980.25   .012160   .187460   .4165130
+    1980.30  -.020440   .201760   .3666940
+    1980.35  -.043480   .221870   .3219240
+    1980.40  -.052250   .240890   .2763230
+    1980.45  -.054760   .259160   .2387700
+    1980.50  -.051620   .282750   .2063630
+    1980.55  -.039350   .301970   .1755970
+    1980.60  -.035550   .314750   .1452760
+    1980.65  -.030090   .328120   .1151990
+    1980.70  -.030200   .337170   .0750790
+    1980.75  -.023160   .345640   .0300980
+    1980.80  -.012370   .358870  -.0127940
+    1980.85   .000830   .366600  -.0624580
+    1980.90   .022640   .373160  -.1078580
+    1980.95   .049120   .373030  -.1500100
+    1981.00   .070580   .360660  -.1953270
+    1981.05   .077000   .348400  -.2391430
+    1981.10   .089260   .329090  -.2776990
+    1981.15   .084840   .315780  -.3202690
+    1981.20   .090210   .304230  -.3727280
+    1981.25   .101630   .289720  -.4159950
+    1981.30   .096310   .268760  -.4685540
+    1981.35   .093750   .257220  -.5165520
+    1981.40   .090250   .241670  -.5587390
+    1981.45   .078570   .226280  -.6009320
+    1981.50   .068150   .210210   .3698510
+    1981.55   .059540   .193490   .3498320
+    1981.60   .033080   .184820   .3198500
+    1981.65   .004150   .185370   .2918900
+    1981.70  -.024140   .193490   .2653000
+    1981.75  -.060920   .209640   .2228820
+    1981.80  -.084890   .235550   .1804970
+    1981.85  -.104520   .264190   .1393580
+    1981.90  -.110340   .298730   .0957520
+    1981.95  -.112180   .336860   .0571520
+    1982.00  -.093080   .376820   .0184280
+    1982.05  -.056710   .406900  -.0233770
+    1982.10  -.029250   .429470  -.0576400
+    1982.15   .016930   .440390  -.0968960
+    1982.20   .062800   .438790  -.1429420
+    1982.25   .108360   .429570  -.1860770
+    1982.30   .144250   .410160  -.2324500
+    1982.35   .175760   .379360  -.2826500
+    1982.40   .202440   .335180  -.3212050
+    1982.45   .227390   .284220  -.3589540
+    1982.50   .225400   .235390   .6073090
+    1982.55   .211690   .183440   .5824430
+    1982.60   .188000   .139540   .5595280
+    1982.65   .147580   .100920   .5266190
+    1982.70   .090980   .077680   .4890550
+    1982.75   .028040   .068920   .4484150
+    1982.80  -.036070   .079650   .4039600
+    1982.85  -.092580   .099410   .3645380
+    1982.90  -.143280   .139280   .3204710
+    1982.95  -.184870   .194480   .2722880
+    1983.00  -.202510   .257960   .2276730
+    1983.05  -.208590   .319420   .1807080
+    1983.10  -.188080   .386630   .1215960
+    1983.15  -.166980   .446850   .0712980
+    1983.20  -.130670   .499770   .0173990
+    1983.25  -.060990   .541150  -.0393690
+    1983.30   .018010   .561080  -.0893990
+    1983.35   .089520   .551750  -.1353850
+    1983.40   .165160   .528540  -.1815520
+    1983.45   .220450   .481900  -.2193950
+    1983.50   .270130   .421220   .7475800
+    1983.55   .307330   .346400   .7215090
+    1983.60   .323430   .271320   .6952120
+    1983.65   .311990   .193620   .6642850
+    1983.70   .270820   .123360   .6298510
+    1983.75   .212870   .065270   .5983220
+    1983.80   .144260   .027540   .5583590
+    1983.85   .061990   .014950   .5132280
+    1983.90  -.016890   .026220   .4716860
+    1983.95  -.085210   .050470   .4337780
+    1984.00  -.133280   .091680   .3954610
+    1984.05  -.178600   .145720   .3663980
+    1984.10  -.217680   .203540   .3393920
+    1984.15  -.239790   .267220   .3071050
+    1984.20  -.240330   .342950   .2769000
+    1984.25  -.215180   .414620   .2386810
+    1984.30  -.165880   .478570   .2012160
+    1984.35  -.103940   .529520   .1686240
+    1984.40  -.036610   .557690   .1381370
+    1984.45   .043530   .559030   .1142480
+    1984.50   .118910   .544370   .0977110
+    1984.55   .192030   .505220   .0854250
+    1984.60   .256550   .452690   .0683640
+    1984.65   .294920   .386890   .0497870
+    1984.70   .308190   .317250   .0228220
+    1984.75   .311850   .246370  -.0075050
+    1984.80   .284570   .180700  -.0363910
+    1984.85   .247250   .123390  -.0715010
+    1984.90   .198900   .081750  -.1028830
+    1984.95   .127310   .041390  -.1301440
+    1985.00   .045080   .022560  -.1575460
+    1985.05  -.021710   .036290  -.1889130
+    1985.10  -.079030   .063220  -.2116370
+    1985.15  -.133690   .102890  -.2407380
+    1985.20  -.185320   .156740  -.2736890
+    1985.25  -.201050   .230230  -.3070830
+    1985.30  -.195870   .298580  -.3429910
+    1985.35  -.182010   .364520  -.3771130
+    1985.40  -.138460   .418650  -.4025590
+    1985.45  -.095300   .459340  -.4317500
+    1985.50  -.043950   .483270   .5478600
+    1985.55   .017740   .494700   .5395290
+    1985.60   .076740   .494160   .5251140
+    1985.65   .135950   .477470   .5115900
+    1985.70   .182620   .445550   .4948280
+    1985.75   .211380   .402010   .4660470
+    1985.80   .227170   .347600   .4338690
+    1985.85   .228420   .297330   .4018540
+    1985.90   .228310   .251090   .3668480
+    1985.95   .209470   .210230   .3364070
+    1986.00   .185940   .170580   .3132290
+    1986.05   .148620   .136920   .2869110
+    1986.10   .097820   .120120   .2595480
+    1986.15   .050650   .114890   .2343500
+    1986.20  -.004480   .120220   .2069080
+    1986.25  -.045110   .137800   .1831500
+    1986.30  -.074620   .174240   .1553600
+    1986.35  -.091050   .210930   .1220280
+    1986.40  -.103510   .248370   .0984900
+    1986.45  -.097870   .286570   .0863460
+    1986.50  -.071490   .325960   .0698390
+    1986.55  -.044780   .356680   .0591900
+    1986.60  -.017440   .376640   .0494220
+    1986.65   .010780   .391720   .0327720
+    1986.70   .039560   .398150   .0179060
+    1986.75   .061300   .393670  -.0068750
+    1986.80   .085600   .389850  -.0389540
+    1986.85   .108410   .379180  -.0658570
+    1986.90   .121680   .358470  -.0899120
+    1986.95   .139690   .333040  -.1163700
+    1987.00   .146260   .313070  -.1380760
+    1987.05   .139250   .286810  -.1601160
+    1987.10   .129080   .265690  -.1865420
+    1987.15   .122130   .247910  -.2129500
+    1987.20   .110320   .223500  -.2463650
+    1987.25   .094220   .212870  -.2807030
+    1987.30   .072280   .204670  -.3086560
+    1987.35   .053320   .200540  -.3362570
+    1987.40   .030170   .199490  -.3639160
+    1987.45   .015310   .199420  -.3832670
+    1987.50  -.006420   .206770  -.3985660
+    1987.55  -.025810   .218770  -.4124200
+    1987.60  -.031970   .234330  -.4193230
+    1987.65  -.037620   .254830  -.4346680
+    1987.70  -.043540   .272020  -.4584800
+    1987.75  -.055190   .296620  -.4825460
+    1987.80  -.063220   .318760  -.5129600
+    1987.85  -.060180   .341610  -.5455880
+    1987.90  -.056330   .371220  -.5748670
+    1987.95  -.049860   .395210  -.6043940
+    1988.00  -.023960   .412730   .3641980
+    1988.05   .004500   .424730   .3384500
+    1988.10   .047020   .432110   .3144130
+    1988.15   .067110   .430020   .2785320
+    1988.20   .105530   .427540   .2471970
+    1988.25   .133210   .406300   .2166860
+    1988.30   .150780   .378320   .1792750
+    1988.35   .171170   .354020   .1522840
+    1988.40   .176460   .321580   .1225530
+    1988.45   .165470   .287990   .0988100
+    1988.50   .169520   .251610   .0898640
+    1988.55   .162560   .216260   .0807490
+    1988.60   .140250   .179670   .0689520
+    1988.65   .106640   .154640   .0603010
+    1988.70   .066470   .136560   .0448510
+    1988.75   .009150   .129100   .0228930
+    1988.80  -.050020   .138100  -.0018930
+    1988.85  -.100790   .165460  -.0330640
+    1988.90  -.138410   .207940  -.0653100
+    1988.95  -.164740   .261170  -.0918720
+    1989.00  -.160800   .315200  -.1150310
+    1989.05  -.142800   .359530  -.1376110
+    1989.10  -.118060   .400480  -.1614360
+    1989.15  -.076130   .436720  -.1886810
+    1989.20  -.027240   .466260  -.2181740
+    1989.25   .029070   .480900  -.2452640
+    1989.30   .085860   .481670  -.2761340
+    1989.35   .127570   .470310  -.3109290
+    1989.40   .169220   .445260  -.3388540
+    1989.45   .204420   .412890  -.3680090
+    1989.50   .239210   .364510  -.3859960
+    1989.55   .259150   .312920  -.4009490
+    1989.60   .259400   .255620  -.4214040
+    1989.65   .250970   .197720  -.4424920
+    1989.70   .217420   .146080  -.4611150
+    1989.75   .165120   .105110  -.4903590
+    1989.80   .098020   .083150  -.5228010
+    1989.85   .031480   .074730  -.5592470
+    1989.90  -.036820   .087780  -.6014820
+    1989.95  -.090410   .118340  -.6390910
+    1990.00  -.132820   .163800   .3294770
+    1990.05  -.165870   .215080   .2971590
+    1990.10  -.202780   .276390   .2614270
+    1990.15  -.201680   .344430   .2205110
+    1990.20  -.185180   .413230   .1791940
+    1990.25  -.153910   .471710   .1351550
+    1990.30  -.101550   .524360   .0948910
+    1990.35  -.049070   .556830   .0554040
+    1990.40   .025280   .572830   .0201240
+    1990.45   .096160   .568570  -.0096640
+    1990.50   .166980   .541670  -.0403750
+    1990.55   .230520   .499990  -.0614930
+    1990.60   .276540   .439320  -.0830990
+    1990.65   .306230   .379690  -.1154370
+    1990.70   .312300   .309110  -.1458280
+    1990.75   .295990   .241750  -.1816860
+    1990.80   .258290   .178550  -.2226650
+    1990.85   .214630   .130690  -.2630650
+    1990.90   .158460   .095450  -.3023270
+    1990.95   .092590   .076290  -.3458130
+    1991.00   .023070   .070420   .6189660
+    1991.05  -.040210   .085850   .5788470
+    1991.10  -.104560   .109710   .5375110
+    1991.15  -.159350   .156090   .4982140
+    1991.20  -.193710   .215880   .4563180
+    1991.25  -.218250   .286330   .4101900
+    1991.30  -.211540   .359670   .3695240
+    1991.35  -.196100   .427740   .3269160
+    1991.40  -.148030   .487300   .2852580
+    1991.45  -.087780   .535430   .2509990
+    1991.50  -.027600   .563210   .2244370
+    1991.55   .049470   .572690   .1994110
+    1991.60   .118890   .560660   .1792000
+    1991.65   .172940   .527310   .1523860
+    1991.70   .218130   .487510   .1145380
+    1991.75   .251390   .434860   .0781450
+    1991.80   .262130   .372840   .0419620
+    1991.85   .263160   .317840   .0002900
+    1991.90   .247290   .263630  -.0397540
+    1991.95   .217400   .209210  -.0833930
+    1992.00   .183430   .168250  -.1253090
+    1992.05   .132860   .139720  -.1664230
+    1992.10   .083410   .121340  -.2133580
+    1992.15   .026850   .116920  -.2592480
+    1992.20  -.034650   .126400  -.3037880
+    1992.25  -.082680   .163990  -.3574120
+    1992.30  -.115610   .203320  -.4102900
+    1992.35  -.145550   .245630  -.4539040
+    1992.40  -.156500   .291880  -.4959850
+    1992.45  -.153110   .334650  -.5303330
+    1992.50  -.140940   .381020   .4420910
+    1992.55  -.115160   .420770   .4184490
+    1992.60  -.077490   .459320   .3892290
+    1992.65  -.031830   .492120   .3596050
+    1992.70   .014530   .506730   .3231410
+    1992.75   .055600   .503780   .2854080
+    1992.80   .103120   .494250   .2458780
+    1992.85   .137780   .473110   .1994080
+    1992.90   .170750   .444760   .1538820
+    1992.95   .187750   .406300   .1096390
+    1993.00   .208200   .361780   .0639800
+    1993.05   .212870   .311750   .0163900
+    1993.10   .205710   .265790  -.0283100
+    1993.15   .186400   .225690  -.0785000
+    1993.20   .159450   .194090  -.1275600
+    1993.25   .113650   .170110  -.1750500
+    1993.30   .065420   .163930  -.2286500
+    1993.35   .025160   .166410  -.2813400
+    1993.40  -.005520   .177900  -.3249500
+    1993.45  -.036590   .190810  -.3670900
+    1993.50  -.063740   .211350   .5975800
+    1993.55  -.073070   .234400   .5709200
+    1993.60  -.091560   .266690   .5374700
+    1993.65  -.101560   .302050   .5021000
+    1993.70  -.099970   .336910   .4679700
+    1993.75  -.095410   .370980   .4245500
+    1993.80  -.080940   .403840   .3774500
+    1993.85  -.062500   .428470   .3322200
+    1993.90  -.041250   .448950   .2851400
+    1993.95  -.019600   .464960   .2441700
+    1994.00   .009200   .476210   .2006800
+    1994.05   .054050   .478850   .1576400
+    1994.10   .099840   .467200   .1161800
+    1994.15   .137660   .448700   .0759200
+    1994.20   .158340   .421910   .0247300
+    1994.25   .175010   .390000  -.0223200
+    1994.30   .183970   .356950  -.0662200
+    1994.35   .186340   .317810  -.1154700
+    1994.40   .180670   .281910  -.1586500
+    1994.45   .160850   .243850  -.1904900
+    1994.50   .134830   .210780   .7815600
+    1994.55   .101460   .184820   .7562100
+    1994.60   .067260   .172080   .7314100
+    1994.65   .026570   .168170   .7029600
+    1994.70  -.016290   .179410   .6665200
+    1994.75  -.066370   .200140   .6249200
+    1994.80  -.106130   .232880   .5793400
+    1994.85  -.141410   .271820   .5355300
+    1994.90  -.152620   .321150   .4935800
+    1994.95  -.153790   .368190   .4476700
+    1995.00  -.153720   .418090   .3991000
+    1995.05  -.127450   .461200   .3532000
+    1995.10  -.111580   .494070   .3028200
+    1995.15  -.075420   .524080   .2573700
+    1995.20  -.022770   .546010   .2056500
+    1995.25   .035210   .558400   .1532400
+    1995.30   .092910   .555900   .1027500
+    1995.35   .162220   .532900   .0547700
+    1995.40   .209220   .497340   .0067200
+    1995.45   .254650   .443560  -.0324300
+    1995.50   .281610   .378980  -.0635800
+    1995.55   .287570   .308430  -.0926700
+    1995.60   .273820   .237780  -.1136100
+    1995.65   .237560   .179860  -.1418400
+    1995.70   .189520   .133990  -.1803800
+    1995.75   .135580   .105900  -.2213600
+    1995.80   .066650   .085830  -.2632900
+    1995.85   .002730   .084730  -.3084000
+    1995.90  -.067070   .101140  -.3533700
+    1995.95  -.138200   .136100  -.3981800
+    1996.00  -.176160   .192180   .5551900
+    1996.05  -.203930   .246410   .5221600
+    1996.10  -.223380   .312950   .4899200
+    1996.15  -.220960   .378460   .4499500
+    1996.20  -.196520   .448570   .4092600
+    1996.25  -.150870   .508690   .3699700
+    1996.30  -.101690   .553340   .3258300
+    1996.35  -.033850   .584490   .2846000
+    1996.40   .040580   .595820   .2454400
+    1996.45   .115360   .581210   .2114200
+    1996.50   .181570   .545250   .1862900
+    1996.55   .239170   .495410   .1659300
+    1996.60   .267760   .436840   .1407800
+    1996.65   .297160   .371810   .1230500
+    1996.70   .290780   .296700   .0985300
+    1996.75   .267110   .228290   .0638000
+    1996.80   .227160   .171270   .0297800
+    1996.85   .175940   .122030  -.0071800
+    1996.90   .109730   .096200  -.0441600
+    1996.95   .039500   .084250  -.0749400
+    1997.00  -.020320   .095060  -.1096500
+    1997.05  -.073030   .119750  -.1459800
+    1997.10  -.124420   .156650  -.1730900
+    1997.15  -.161820   .203960  -.2056400
+    1997.20  -.186450   .263310  -.2482000
+    1997.25  -.190710   .331710  -.2929300
+    1997.30  -.174960   .396620  -.3387100
+    1997.35  -.145010   .452930  -.3794300
+    1997.40  -.098250   .498370  -.4162300
+    1997.45  -.047990   .525350  -.4516000
+    1997.50   .023090   .536790   .5261800
+    1997.55   .088210   .526330   .5094900
+    1997.60   .141170   .505000   .4812500
+    1997.65   .181140   .467130   .4540400
+    1997.70   .216860   .428130   .4276000
+    1997.75   .221240   .378750   .3908700
+    1997.80   .226280   .329150   .3513700
+    1997.85   .214670   .278540   .3151900
+    1997.90   .190380   .234080   .2784700
+    1997.95   .151700   .201890   .2466500
+    1998.00   .103371   .174919   .2187928
+    1998.05   .051655   .167526   .1878736
+    1998.10  -.005228   .173571   .1500342
+    1998.15  -.051800   .188169   .1146000
+    1998.20  -.079308   .220212   .0745974
+    1998.25  -.110294   .254246   .0360847
+    1998.30  -.113475   .301652   .0024942
+    1998.35  -.114391   .344204  -.0345923
+    1998.40  -.107406   .383854  -.0684825
+    1998.45  -.099535   .414513  -.0887736
+    1998.50  -.065007   .440850  -.1011400
+    1998.55  -.021674   .465308  -.1082697
+    1998.60   .017316   .478858  -.1146002
+    1998.65   .058055   .476585  -.1250252
+    1998.70   .097135   .465706  -.1418293
+    1998.75   .125857   .444588  -.1587117
+    1998.80   .149956   .418823  -.1862362
+    1998.85   .163218   .388340  -.2147711
+    1998.90   .154524   .350655  -.2401730
+    1998.95   .137225   .321181  -.2653190
+    1999.00   .138906   .296108   .7168562
+    1999.05   .117532   .271385   .7014530
+    1999.10   .100290   .252373   .6791687
+    1999.15   .077098   .244566   .6561574
+    1999.20   .054921   .239895   .6399826
+    1999.25   .023809   .240618   .6145151
+    1999.30   .001100   .248865   .5879676
+    1999.35  -.017471   .262305   .5664931
+    1999.40  -.023004   .281431   .5435885
+    1999.45  -.031745   .296763   .5267825
+    1999.50  -.031910   .311187   .5198637
+    1999.55  -.024948   .330418   .5120479
+    1999.60  -.014139   .345615   .5055648
+    1999.65  -.008467   .361644   .4977768
+    1999.70   .002079   .370914   .4819857
+    1999.75   .006586   .379679   .4688203
+    1999.80   .015741   .382597   .4506114
+    1999.85   .024443   .379545   .4230946
+    1999.90   .032087   .377378   .3974911
+    1999.95   .037562   .380865   .3782104
+    2000.00   .043300   .377661   .3554817
+    2000.05   .052375   .375702   .3413574
+    2000.10   .060632   .372427   .3250578
+    2000.15   .067994   .365396   .3071171
+    2000.20   .074610   .357979   .2899885
+    2000.25   .075437   .346051   .2762541
+    2000.30   .082353   .343278   .2552646
+    2000.35   .088049   .332016   .2358447
+    2000.40   .096303   .321406   .2198842
+    2000.45   .113565   .304386   .2064913
+    2000.50   .110079   .279086   .2040804
+    2000.55   .096137   .261702   .2004203
+    2000.60   .083088   .248498   .1987985
+    2000.65   .058388   .242745   .1949233
+    2000.70   .025972   .239588   .1871360
+    2000.75  -.005722   .246917   .1747298
+    2000.80  -.036045   .262831   .1600032
+    2000.85  -.059965   .292223   .1391654
+    2000.90  -.076915   .325404   .1205337
+    2000.95  -.082660   .357017   .1045817
+    2001.00  -.074006   .396764   .0936988
+    2001.05  -.060835   .427064   .0806986
+    2001.10  -.032401   .451341   .0762852
+    2001.15   .004327   .475921   .0624390
+    2001.20   .044840   .486196   .0388944
+    2001.25   .091974   .489418   .0251539
+    2001.30   .139116   .477702   .0096675
+    2001.35   .178791   .452008  -.0088652
+    2001.40   .215170   .409242  -.0228961
+    2001.45   .242795   .359595  -.0245445
+    2001.50   .254005   .304725  -.0278187
+    2001.55   .247620   .249184  -.0254430
+    2001.60   .220968   .199893  -.0206066
+    2001.65   .175053   .158811  -.0281492
+    2001.70   .125932   .132198  -.0304154
+    2001.75   .064697   .117778  -.0383976
+    2001.80   .001422   .120744  -.0558549
+    2001.85  -.061030   .140506  -.0697006
+    2001.90  -.103646   .180496  -.0868556
+    2001.95  -.153361   .228492  -.1008918
+    2002.00  -.176817   .292216  -.1154380
+    2002.05  -.178852   .348438  -.1269166
+    2002.10  -.163183   .406360  -.1417309
+    2002.15  -.135832   .461499  -.1552923
+    2002.20  -.088799   .513379  -.1739301
+    2002.25  -.027634   .540974  -.1901581
+    2002.30   .029239   .554816  -.2007113
+    2002.35   .092521   .550437  -.2137458
+    2002.40   .142157   .534179  -.2303514
+    2002.45   .192923   .504114  -.2287794
+    2002.50   .228889   .458092  -.2296246
+    2002.55   .244514   .404459  -.2327614
+    2002.60   .257521   .348296  -.2240428
+    2002.65   .254939   .295393  -.2248033
+    2002.70   .235509   .242129  -.2303263
+    2002.75   .197659   .199413  -.2327877
+    2002.80   .151551   .165804  -.2429725
+    2002.85   .084865   .146837  -.2549716
+    2002.90   .022450   .142124  -.2612171
+    2002.95  -.038228   .154264  -.2767537
+    2003.00  -.087743   .187387  -.2892390
+    2003.05  -.127578   .228891  -.2995032
+    2003.10  -.147530   .278647  -.3078795
+    2003.15  -.154775   .332102  -.3201905
+    2003.20  -.157108   .383491  -.3285379
+    2003.25  -.131007   .438825  -.3439523
+    2003.30  -.094644   .486399  -.3588043
+    2003.35  -.043183   .522695  -.3638535
+    2003.40   .001931   .541957  -.3745652
+    2003.45   .062632   .546600  -.3752839
+    2003.50   .135203   .538240  -.3661265
+    2003.55   .191134   .509318  -.3577046
+    2003.60   .232086   .463954  -.3537978
+    2003.65   .259508   .415127  -.3494880
+    2003.70   .264299   .356543  -.3529986
+    2003.75   .257771   .302232  -.3575570
+    2003.80   .226130   .250158  -.3628298
+    2003.85   .195950   .207643  -.3728456
+    2003.90   .147593   .174905  -.3807556
+    2003.95   .087282   .158884  -.3820300
+    2004.00   .031246   .153785  -.3896036
+    2004.05  -.014033   .166554  -.3994989
+    2004.10  -.057805   .191331  -.4041551
+    2004.15  -.101004   .228144  -.4153922
+    2004.20  -.126021   .268583  -.4225892
+    2004.25  -.140343   .321451  -.4338646
+    2004.30  -.132911   .375713  -.4512439
+    2004.35  -.112725   .423506  -.4596331
+    2004.40  -.090722   .458455  -.4683249
Index: /tags/sj_tags/sj_root_20080929/psLib/share/eopc01_1900_2004.raw
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/eopc01_1900_2004.raw	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/eopc01_1900_2004.raw	(revision 22322)
@@ -0,0 +1,2092 @@
+     Date         x       sx         y       sy      ut1-utc    sut1       dPsi     sdPsi     dEps      sEps
+     year        (")      (")       (")      (")        s         s         (")      (")       (")      (")
+
+    1900.00  -.212818  .053760  -.097229  .037446   .0000000  .0000000  -.333677  .126760  -.065408  .046343
+    1900.05  -.047461  .029853  -.065631  .023330   .0000000  .0000000  -.193398  .127676   .017394  .021636
+    1900.10  -.112006  .028504  -.040467  .024327   .0000000  .0000000   .521675  .123684   .068013  .029930
+    1900.15  -.076710  .021791  -.009674  .021233   .0000000  .0000000   .352564  .078239   .072061  .029062
+    1900.20  -.099850  .026908  -.066660  .025620   .0000000  .0000000   .318962  .097104   .007820  .057366
+    1900.25  -.131173  .039344  -.072076  .027851   .0000000  .0000000   .232096  .066951   .093766  .058547
+    1900.30  -.087390  .025660  -.089427  .024505   .0000000  .0000000   .307133  .066507   .003862  .042366
+    1900.35  -.164078  .026598  -.131034  .027439   .0000000  .0000000   .317237  .071973   .026743  .042837
+    1900.40  -.151407  .019173  -.105838  .020221   .0000000  .0000000   .202192  .096423   .017399  .023063
+    1900.45  -.143764  .021261  -.179125  .029817   .0000000  .0000000  -.028123  .129358  -.012124  .020663
+    1900.50  -.124341  .024357  -.105747  .019115   .0000000  .0000000   .041669  .127181   .044334  .017158
+    1900.55  -.206229  .021263  -.144555  .025505   .0000000  .0000000  -.041788  .102389   .095807  .029772
+    1900.60  -.192711  .025172  -.095563  .029564   .0000000  .0000000   .002017  .105331   .044212  .031575
+    1900.65  -.190299  .023335  -.067717  .020577   .0000000  .0000000   .194828  .076930   .085772  .045933
+    1900.70  -.135718  .022933  -.081480  .020157   .0000000  .0000000   .269845  .072594   .075357  .044405
+    1900.75  -.153807  .024518   .030568  .021592   .0000000  .0000000   .181946  .044492  -.154452  .052427
+    1900.80  -.173818  .022558   .001034  .018183   .0000000  .0000000   .170594  .039178  -.072797  .045202
+    1900.85  -.257716  .032834  -.006499  .028017   .0000000  .0000000   .236454  .094350  -.162464  .054953
+    1900.90  -.209085  .025040   .046511  .025197   .0000000  .0000000   .088373  .092346  -.089729  .055564
+    1900.95  -.133145  .034581   .004905  .026144   .0000000  .0000000  -.039864  .127792  -.039118  .041313
+    1901.00  -.138242  .027367   .014255  .024185   .0000000  .0000000  -.228636  .134455  -.003944  .038305
+    1901.05  -.162602  .024087   .006609  .023908   .0000000  .0000000  -.262902  .109076  -.069779  .020676
+    1901.10  -.121242  .025526  -.000875  .022222   .0000000  .0000000   .356041  .119705  -.014506  .031232
+    1901.15  -.109773  .028420   .024691  .024157   .0000000  .0000000   .373538  .100048   .018214  .036225
+    1901.20  -.103805  .026286   .039564  .021736   .0000000  .0000000   .309930  .078709  -.018732  .045262
+    1901.25  -.021306  .024508  -.011519  .023560   .0000000  .0000000   .226326  .046973  -.026068  .056093
+    1901.30  -.001300  .016000   .021700  .023300   .0000000  .0000000   .431800  .058100  -.012900  .035900
+    1901.35   .037156  .024692  -.049753  .024062   .0000000  .0000000   .430826  .066845  -.018088  .038805
+    1901.40   .003993  .019282  -.095314  .029126   .0000000  .0000000   .120522  .119007   .083355  .027210
+    1901.45   .054079  .019606  -.081336  .019143   .0000000  .0000000   .051073  .111137   .020471  .014069
+    1901.50   .089761  .031152  -.084901  .023841   .0000000  .0000000   .055714  .133759   .011540  .022405
+    1901.55   .004864  .024183  -.187233  .019218   .0000000  .0000000   .191254  .111589   .051708  .031744
+    1901.60  -.023099  .020532  -.179528  .016169   .0000000  .0000000  -.025500  .092583   .010166  .026232
+    1901.65  -.057910  .022283  -.197673  .018043   .0000000  .0000000   .127452  .059894   .095943  .032516
+    1901.70  -.033170  .021132  -.202981  .020887   .0000000  .0000000   .151218  .074568   .035224  .044759
+    1901.75  -.155604  .038076  -.187085  .030924   .0000000  .0000000   .009135  .074519  -.123734  .051462
+    1901.80  -.169658  .026229  -.183163  .023359   .0000000  .0000000   .110066  .049415  -.117592  .046192
+    1901.85  -.188623  .023529  -.181458  .023343   .0000000  .0000000   .176267  .083759  -.158104  .051685
+    1901.90  -.223086  .037997  -.144566  .029526   .0000000  .0000000   .074590  .108405  -.100008  .058672
+    1901.95  -.254958  .016090  -.114447  .017896   .0000000  .0000000   .038950  .101799  -.062510  .027550
+    1902.00  -.259243  .023993  -.078157  .021579   .0000000  .0000000   .062919  .117810  -.054074  .032269
+    1902.05  -.211792  .028790   .038633  .029579   .0000000  .0000000   .017451  .148456   .020130  .024810
+    1902.10  -.216991  .031892   .064250  .026547   .0000000  .0000000   .199832  .133961   .037086  .035402
+    1902.15  -.242537  .023685   .171246  .025297   .0000000  .0000000   .415690  .088702   .043663  .034234
+    1902.20  -.188324  .019932   .158468  .019402   .0000000  .0000000   .337145  .073499   .052572  .046916
+    1902.25  -.152570  .029794   .207171  .027372   .0000000  .0000000   .214367  .061488  -.046953  .060194
+    1902.30  -.062244  .016488   .171387  .022647   .0000000  .0000000   .256881  .057371   .006551  .033174
+    1902.35  -.016045  .025756   .182141  .020157   .0000000  .0000000   .265355  .062603  -.028692  .033970
+    1902.40   .036032  .019117   .156268  .020794   .0000000  .0000000   .211676  .098381  -.021285  .023510
+    1902.45   .217293  .048928   .252814  .059193   .0000000  .0000000   .146268  .124285  -.113359  .044438
+    1902.50   .119283  .025360   .066262  .021894   .0000000  .0000000   .024206  .116290   .019059  .018772
+    1902.55   .125538  .021669   .016781  .019109   .0000000  .0000000   .091941  .099925   .027890  .029268
+    1902.60   .082496  .025116  -.058369  .022971   .0000000  .0000000   .250234  .108900   .110308  .030400
+    1902.65   .051287  .024489  -.069631  .016515   .0000000  .0000000   .261163  .069618   .097323  .039865
+    1902.70   .051239  .015838  -.093785  .015438   .0000000  .0000000   .250384  .055426   .068387  .034338
+    1902.75  -.024248  .022473  -.151921  .025442   .0000000  .0000000   .149156  .045045  -.095975  .053371
+    1902.80  -.054789  .019443  -.179957  .016628   .0000000  .0000000   .124553  .035265  -.069583  .040068
+    1902.85  -.146320  .020063  -.146959  .023110   .0000000  .0000000   .078608  .078920  -.089216  .047165
+    1902.90  -.193061  .020857  -.169534  .022315   .0000000  .0000000   .286457  .076181  -.208670  .046902
+    1902.95  -.237694  .019690  -.119404  .024324   .0000000  .0000000   .109034  .129370  -.052789  .034514
+    1903.00  -.319420  .030492  -.043030  .024811   .0000000  .0000000  -.038528  .145631  -.047724  .038302
+    1903.05  -.341204  .033829   .031366  .029406   .0000000  .0000000  -.098874  .160686   .018102  .023703
+    1903.10  -.345930  .022972  -.002271  .021847   .0000000  .0000000   .351093  .102851   .012471  .027943
+    1903.15  -.363449  .026083   .022246  .026034   .0000000  .0000000   .188593  .093308  -.023174  .037211
+    1903.20  -.363639  .026221   .126744  .020945   .0000000  .0000000   .245152  .087844   .029758  .051375
+    1903.25  -.275204  .023659   .088178  .025161   .0000000  .0000000   .291261  .044592   .065540  .051947
+    1903.30  -.217973  .027174   .142752  .021557   .0000000  .0000000   .210847  .075501   .063304  .043755
+    1903.35  -.133649  .019233   .193127  .019408   .0000000  .0000000   .260252  .056126   .060431  .031402
+    1903.40  -.073538  .019722   .187785  .025512   .0000000  .0000000   .238595  .103039  -.018625  .024658
+    1903.45  -.033275  .020533   .242905  .022048   .0000000  .0000000   .056127  .129511   .027798  .017107
+    1903.50  -.025665  .019429   .185828  .015678   .0000000  .0000000  -.045779  .099979   .027960  .013960
+    1903.55   .065994  .027595   .199396  .021362   .0000000  .0000000   .091259  .113401   .039908  .032424
+    1903.60   .124642  .022783   .131603  .021652   .0000000  .0000000   .051145  .098411   .025315  .027842
+    1903.65   .138085  .017587   .043707  .024380   .0000000  .0000000   .246657  .066217   .079088  .036242
+    1903.70   .131036  .023145  -.010458  .018213   .0000000  .0000000   .227846  .071262   .028294  .042237
+    1903.75   .129875  .035187  -.132885  .050492   .0000000  .0000000   .250549  .112846  -.094268  .048466
+    1903.80   .020188  .021139  -.149501  .017604   .0000000  .0000000   .109261  .036528  -.073754  .040709
+    1903.85  -.013164  .021345  -.224089  .020262   .0000000  .0000000   .056925  .074780  -.070392  .044352
+    1903.90   .017200  .027500  -.148400  .021400   .0000000  .0000000   .365700  .082200  -.239900  .051000
+    1903.95  -.054459  .030056  -.187411  .022835   .0000000  .0000000   .143493  .154516  -.103709  .039701
+    1904.00  -.148856  .044509  -.076687  .044456   .0000000  .0000000  -.114333  .231473   .061858  .075829
+    1904.05  -.254004  .025206  -.136031  .021177   .0000000  .0000000   .004573  .125550  -.037187  .017298
+    1904.10  -.260513  .025120  -.134266  .024182   .0000000  .0000000   .245917  .113408  -.023869  .030194
+    1904.15  -.250640  .038286  -.062308  .029112   .0000000  .0000000   .153720  .139136  -.072025  .046748
+    1904.20  -.318450  .021790  -.022118  .038002   .0000000  .0000000   .343107  .107286   .029958  .054168
+    1904.25  -.293126  .023025   .024733  .019604   .0000000  .0000000   .257570  .041421   .099537  .048628
+    1904.30  -.249230  .043032   .052166  .022314   .0000000  .0000000   .250262  .082833   .005740  .053675
+    1904.35  -.263860  .019701   .072953  .018143   .0000000  .0000000   .249463  .055609   .056281  .034733
+    1904.40  -.210918  .029867   .172463  .020549   .0000000  .0000000   .242277  .114143   .012153  .028773
+    1904.45  -.152700  .018200   .188900  .018000   .0000000  .0000000  -.038200  .111600   .003300  .013500
+    1904.50  -.109676  .019307   .182564  .018938   .0000000  .0000000  -.148627  .111520   .030744  .015061
+    1904.55  -.082798  .020647   .158201  .020009   .0000000  .0000000   .046632  .096224   .074456  .028089
+    1904.60   .001500  .014600   .164000  .015300   .0000000  .0000000   .059100  .081200   .033100  .023200
+    1904.65   .042084  .017502   .134884  .014207   .0000000  .0000000   .299232  .053479   .160529  .030251
+    1904.70   .062535  .019610   .070240  .018761   .0000000  .0000000   .233434  .063943   .112999  .037691
+    1904.75   .083512  .024774   .063227  .023502   .0000000  .0000000   .100183  .048551   .039087  .030313
+    1904.80   .042326  .022076   .010531  .021354   .0000000  .0000000   .108009  .041403  -.080528  .043561
+    1904.85   .042284  .020582  -.029751  .021504   .0000000  .0000000   .096546  .079450  -.128975  .047377
+    1904.90  -.008473  .045473  -.063250  .044351   .0000000  .0000000   .231014  .135878  -.063402  .095840
+    1904.95   .028258  .028399  -.057405  .026815   .0000000  .0000000   .103781  .158414  -.073395  .041300
+    1905.00  -.040538  .025021  -.099671  .024519   .0000000  .0000000   .185220  .087951  -.032327  .026835
+    1905.05  -.017099  .027266  -.158118  .027398   .0000000  .0000000  -.161540  .107012   .074302  .024020
+    1905.10  -.105713  .031136  -.176117  .024035   .0000000  .0000000   .347522  .131488   .052168  .033695
+    1905.15  -.176213  .023287  -.100777  .021215   .0000000  .0000000   .290801  .072797   .017315  .027805
+    1905.20  -.231493  .023017  -.136695  .016189   .0000000  .0000000   .101899  .056885   .024298  .027994
+    1905.25  -.251440  .023914  -.151097  .022693   .0000000  .0000000   .183959  .044511   .085260  .040895
+    1905.30  -.248000  .029100  -.108800  .028300   .0000000  .0000000   .303300  .084900   .012900  .044900
+    1905.35  -.304428  .020727   .045206  .018309   .0000000  .0000000   .195471  .060148   .000919  .037654
+    1905.40  -.236545  .022973   .042822  .026077   .0000000  .0000000   .123361  .118627   .009977  .029220
+    1905.45  -.250592  .031413   .048859  .027624   .0000000  .0000000   .029364  .124702   .025729  .021942
+    1905.50  -.185960  .023539   .112145  .027395   .0000000  .0000000   .098352  .135807   .009326  .019311
+    1905.55  -.156532  .021895   .117102  .020138   .0000000  .0000000  -.081301  .087252  -.037089  .024732
+    1905.60  -.109352  .021554   .166247  .018456   .0000000  .0000000  -.052333  .098533  -.005779  .029043
+    1905.65  -.067846  .028256   .135598  .020885   .0000000  .0000000   .277618  .064061   .111046  .035631
+    1905.70   .014037  .023029   .151407  .022182   .0000000  .0000000   .282266  .062970   .052905  .033997
+    1905.75  -.034403  .030475   .175804  .021521   .0000000  .0000000   .255754  .049868  -.148491  .055323
+    1905.80  -.031721  .028070   .049970  .029042   .0000000  .0000000  -.006276  .049203  -.163042  .046545
+    1905.85   .025746  .026045   .080453  .024204   .0000000  .0000000   .318572  .083837  -.162672  .049900
+    1905.90   .024021  .023876  -.056728  .024447   .0000000  .0000000   .191888  .086082  -.081159  .052664
+    1905.95   .040706  .025939  -.006383  .031653   .0000000  .0000000   .176684  .151253  -.066028  .042122
+    1906.00   .003853  .027774  -.026096  .022568   .0000000  .0000000   .353052  .125609  -.085851  .034335
+    1906.05  -.017613  .037626  -.075020  .028373   .0000000  .0000000   .058236  .116066   .042743  .028389
+    1906.10  -.157406  .027525  -.092487  .019150   .0000000  .0000000   .270125  .098675   .087800  .028450
+    1906.15  -.067506  .039670  -.080298  .024017   .0000000  .0000000   .057244  .086367   .041170  .032989
+    1906.20  -.149450  .019538  -.119103  .021544   .0000000  .0000000   .113356  .066083   .003460  .039537
+    1906.25  -.128247  .021258  -.061169  .019210   .0000000  .0000000   .102973  .039658   .057831  .043484
+    1906.30  -.179648  .019342  -.057693  .015619   .0000000  .0000000   .175340  .057093  -.039542  .032940
+    1906.35  -.186449  .028318  -.044109  .017466   .0000000  .0000000   .140376  .060295  -.002299  .033838
+    1906.40  -.208575  .017756  -.019448  .017400   .0000000  .0000000   .015574  .074861   .005300  .018332
+    1906.45  -.217870  .027036   .042944  .019247   .0000000  .0000000   .248883  .103492   .000061  .019037
+    1906.50  -.199891  .026352   .058881  .022948   .0000000  .0000000   .227682  .100657   .031386  .018187
+    1906.55  -.173543  .014562   .070934  .014278   .0000000  .0000000   .117292  .076299   .023764  .021614
+    1906.60  -.182003  .014184   .113024  .020481   .0000000  .0000000   .238491  .076914   .064891  .023037
+    1906.65  -.142776  .020805   .095154  .018992   .0000000  .0000000   .200399  .060830   .052899  .036334
+    1906.70  -.093740  .023427   .095224  .019026   .0000000  .0000000   .165899  .057427   .024389  .033984
+    1906.75  -.093900  .030400   .111200  .029700   .0000000  .0000000   .123100  .056800  -.022900  .055700
+    1906.80  -.102758  .020273   .078405  .019941   .0000000  .0000000   .158671  .038107  -.057296  .034270
+    1906.85  -.009641  .037790   .137918  .023678   .0000000  .0000000   .110473  .086093  -.121337  .055949
+    1906.90  -.126885  .017403   .077766  .014954   .0000000  .0000000   .071042  .050965  -.101739  .029910
+    1906.95  -.085940  .024352   .122947  .033076   .0000000  .0000000  -.173911  .144919   .052592  .043974
+    1907.00  -.079416  .024620   .150174  .023710   .0000000  .0000000   .080001  .116688   .018143  .029500
+    1907.05  -.067350  .025143   .041498  .035409   .0000000  .0000000   .073095  .126257   .055036  .023696
+    1907.10  -.027042  .044273   .024883  .037921   .0000000  .0000000   .189029  .138258   .070923  .040212
+    1907.15  -.069913  .023457   .039794  .019305   .0000000  .0000000   .247270  .073147   .059702  .028726
+    1907.20  -.059194  .025568  -.025833  .019658   .0000000  .0000000  -.067453  .070577  -.036942  .042421
+    1907.25  -.032978  .027136  -.010025  .021118   .0000000  .0000000   .097977  .045607   .028819  .047104
+    1907.30  -.032298  .018376  -.079952  .018100   .0000000  .0000000   .091289  .058321   .098117  .035752
+    1907.35  -.012800  .019968  -.038790  .015828   .0000000  .0000000   .026669  .051078   .005099  .029065
+    1907.40  -.094517  .018850  -.051596  .019350   .0000000  .0000000   .154034  .095591  -.036263  .023594
+    1907.45  -.089447  .022296  -.124297  .019779   .0000000  .0000000   .029209  .101385   .026543  .015722
+    1907.50  -.131771  .019108  -.098748  .017119   .0000000  .0000000   .242100  .089088   .009307  .014240
+    1907.55  -.109356  .015221  -.122489  .014661   .0000000  .0000000   .138257  .080858   .007021  .022508
+    1907.60  -.134764  .018056  -.084340  .015443   .0000000  .0000000   .202735  .078593   .056406  .022873
+    1907.65  -.150915  .013218  -.095204  .011457   .0000000  .0000000   .186489  .042036   .065484  .024490
+    1907.70  -.183652  .013853  -.070567  .012711   .0000000  .0000000   .238161  .042637   .067956  .025900
+    1907.75  -.247755  .015944  -.037271  .014847   .0000000  .0000000   .069057  .030949  -.166534  .035037
+    1907.80  -.276782  .022683   .006594  .013921   .0000000  .0000000   .113611  .036307  -.078859  .035934
+    1907.85  -.260675  .020328   .115176  .026887   .0000000  .0000000   .036991  .076862  -.028790  .043520
+    1907.90  -.279309  .022735   .066165  .018080   .0000000  .0000000   .244677  .062092  -.110026  .038954
+    1907.95  -.247411  .024711   .158512  .031173   .0000000  .0000000   .062917  .124068   .010688  .030974
+    1908.00  -.179825  .022677   .190418  .019382   .0000000  .0000000   .045084  .106993   .062586  .027161
+    1908.05  -.162104  .025046   .175855  .028444   .0000000  .0000000   .103742  .155855   .042061  .019892
+    1908.10  -.073775  .028891   .244495  .024516   .0000000  .0000000   .245695  .110802   .095557  .031435
+    1908.15  -.088534  .027458   .184551  .017033   .0000000  .0000000   .202671  .076752   .059013  .031780
+    1908.20   .072181  .033438   .185212  .026915   .0000000  .0000000  -.041708  .089470  -.103296  .050987
+    1908.25   .000811  .023430   .129285  .025494   .0000000  .0000000   .206115  .053034   .050623  .041550
+    1908.30   .094290  .022523   .125029  .016876   .0000000  .0000000   .216543  .060033  -.027207  .036610
+    1908.35   .098031  .015570   .048028  .014659   .0000000  .0000000   .030976  .044622   .052821  .026959
+    1908.40   .085376  .019153  -.037173  .015904   .0000000  .0000000   .105038  .078267   .015997  .019045
+    1908.45   .110614  .025853  -.053618  .024285   .0000000  .0000000   .153787  .106634   .013544  .019968
+    1908.50   .110175  .026610  -.121985  .025563   .0000000  .0000000   .031077  .108516   .006967  .020390
+    1908.55   .062891  .022400  -.147259  .019060   .0000000  .0000000  -.082788  .089320  -.037852  .025333
+    1908.60   .016036  .021495  -.174778  .023275   .0000000  .0000000   .210112  .099015   .066788  .027598
+    1908.65  -.044507  .019073  -.258578  .021281   .0000000  .0000000   .179528  .051480   .053306  .026579
+    1908.70  -.126698  .019826  -.236805  .014026   .0000000  .0000000   .159666  .057586   .116131  .031215
+    1908.75  -.185227  .018908  -.279032  .016051   .0000000  .0000000  -.038867  .036568  -.059976  .030485
+    1908.80  -.218671  .014651  -.234349  .016957   .0000000  .0000000   .085591  .033574  -.055343  .025266
+    1908.85  -.258124  .022801  -.193490  .017680   .0000000  .0000000   .058307  .057282  -.155806  .034798
+    1908.90  -.348498  .023013  -.117029  .019986   .0000000  .0000000  -.044263  .072425  -.031398  .041662
+    1908.95  -.441656  .045827  -.087821  .024170   .0000000  .0000000  -.222508  .111975   .074175  .042506
+    1909.00  -.418026  .030202   .032515  .029485   .0000000  .0000000  -.124136  .114958   .001844  .034290
+    1909.05  -.464700  .023300   .053100  .021200   .0000000  .0000000   .142200  .122400  -.021900  .017600
+    1909.10  -.381402  .029397   .081369  .026583   .0000000  .0000000   .136482  .135028   .026300  .032872
+    1909.15  -.331872  .024908   .208343  .022250   .0000000  .0000000   .142551  .088861   .003686  .028690
+    1909.20  -.262800  .025300   .230900  .020400   .0000000  .0000000   .246000  .077700   .119000  .052700
+    1909.25  -.180022  .021604   .262477  .015586   .0000000  .0000000   .204810  .034370   .159389  .041898
+    1909.30  -.088674  .020876   .272316  .018026   .0000000  .0000000   .224372  .061549  -.013291  .039957
+    1909.35  -.017300  .024900   .301900  .020400   .0000000  .0000000   .115400  .068600  -.009000  .037100
+    1909.40   .082311  .016643   .293915  .015624   .0000000  .0000000   .161696  .084291   .000839  .021602
+    1909.45   .145682  .019466   .242356  .024029   .0000000  .0000000   .169328  .107429  -.009796  .017280
+    1909.50   .159939  .021629   .145514  .016260   .0000000  .0000000  -.073128  .091190   .030057  .015366
+    1909.55   .219001  .015849   .050735  .015308   .0000000  .0000000  -.011547  .079780  -.013148  .022447
+    1909.60   .251107  .017083   .038325  .012976   .0000000  .0000000   .142763  .076342   .011621  .021565
+    1909.65   .216000  .017200  -.092100  .014200   .0000000  .0000000   .209600  .053300   .068800  .031100
+    1909.70   .239771  .033367  -.129218  .018723   .0000000  .0000000   .327841  .068174   .031505  .035607
+    1909.75   .122030  .026859  -.229024  .021510   .0000000  .0000000   .118789  .051222  -.041929  .044658
+    1909.80  -.027219  .018091  -.280542  .014742   .0000000  .0000000   .105788  .032512  -.034222  .033019
+    1909.85  -.097816  .031222  -.318227  .020306   .0000000  .0000000   .102209  .083756  -.040327  .052036
+    1909.90  -.212272  .020651  -.273213  .013760   .0000000  .0000000   .155022  .055398  -.090967  .034028
+    1909.95  -.262295  .016361  -.211841  .019362   .0000000  .0000000  -.306349  .093272   .051436  .026436
+    1910.00  -.323074  .027015  -.200846  .037795   .0000000  .0000000   .099003  .103352  -.005730  .037950
+    1910.05  -.442329  .027265  -.257217  .023282   .0000000  .0000000   .036708  .143048  -.002189  .019658
+    1910.10  -.448532  .041123  -.107453  .027419   .0000000  .0000000   .043621  .162329   .045460  .040389
+    1910.15  -.408744  .021087   .030815  .032683   .0000000  .0000000   .172827  .103869  -.015696  .038230
+    1910.20  -.414020  .027029  -.006268  .022893   .0000000  .0000000  -.006953  .079653  -.073319  .048574
+    1910.25  -.385676  .029272   .162890  .037814   .0000000  .0000000   .131554  .075221   .115988  .049835
+    1910.30  -.368387  .014246   .272702  .023082   .0000000  .0000000   .168008  .058720   .013868  .035304
+    1910.35  -.268774  .014096   .293213  .017618   .0000000  .0000000   .102868  .050954   .015300  .031518
+    1910.40  -.147573  .033402   .342824  .031269   .0000000  .0000000   .108410  .144680   .021531  .038312
+    1910.45  -.081273  .036206   .328529  .024940   .0000000  .0000000   .268834  .133538  -.046158  .027171
+    1910.50  -.020932  .018243   .388520  .021784   .0000000  .0000000   .186364  .101418   .015650  .016119
+    1910.55   .105674  .015903   .327555  .015193   .0000000  .0000000  -.016453  .084989  -.015338  .023625
+    1910.60   .176301  .025502   .312524  .018735   .0000000  .0000000   .146119  .091503   .028960  .027777
+    1910.65   .191756  .047195   .171612  .029191   .0000000  .0000000   .176229  .097407   .121387  .055812
+    1910.70   .222257  .017196   .091742  .015870   .0000000  .0000000   .182638  .052853   .063604  .032838
+    1910.75   .211332  .016710  -.009012  .016112   .0000000  .0000000   .048463  .032674  -.060190  .037462
+    1910.80   .205000  .020700  -.105700  .017200   .0000000  .0000000   .062800  .036400  -.095100  .039600
+    1910.85   .120900  .020101  -.162494  .021135   .0000000  .0000000   .123726  .059606  -.019765  .042156
+    1910.90   .083916  .023956  -.253801  .024007   .0000000  .0000000   .013354  .073655  -.021508  .051519
+    1910.95  -.057477  .033626  -.280025  .025436   .0000000  .0000000   .072073  .128036  -.009321  .037844
+    1911.00  -.089579  .028621  -.260914  .027948   .0000000  .0000000   .169478  .116395   .037311  .032178
+    1911.05  -.218786  .029820  -.263597  .035792   .0000000  .0000000   .069459  .152501   .061462  .023480
+    1911.10  -.224700  .024170  -.257286  .021305   .0000000  .0000000   .325278  .115919   .068961  .028144
+    1911.15  -.335860  .021547  -.182205  .019739   .0000000  .0000000   .133424  .083961   .036224  .025924
+    1911.20  -.267000  .034500  -.050900  .036500   .0000000  .0000000  -.104000  .121900   .078500  .058700
+    1911.25  -.370633  .024922   .021540  .022191   .0000000  .0000000   .297392  .046601   .002211  .053352
+    1911.30  -.353109  .024050   .047330  .019022   .0000000  .0000000   .167158  .069713  -.013280  .040848
+    1911.35  -.332727  .014668   .126340  .014928   .0000000  .0000000   .252875  .048137  -.056184  .031637
+    1911.40  -.234107  .032662   .168826  .020525   .0000000  .0000000   .201020  .113961   .056925  .030206
+    1911.45  -.272718  .028763   .310644  .020913   .0000000  .0000000   .192952  .130471   .027320  .018467
+    1911.50  -.193283  .022444   .331807  .023636   .0000000  .0000000   .287728  .116745  -.006204  .019212
+    1911.55  -.136796  .026687   .347029  .020769   .0000000  .0000000   .100558  .095132  -.013315  .028295
+    1911.60  -.056770  .020828   .304448  .016125   .0000000  .0000000   .038851  .087052  -.008402  .025164
+    1911.65   .060600  .022900   .363500  .027100   .0000000  .0000000   .205100  .059600   .017900  .029800
+    1911.70   .112694  .032188   .325844  .023151   .0000000  .0000000   .299304  .082079   .040320  .047025
+    1911.75   .129599  .019997   .242729  .021387   .0000000  .0000000   .155515  .039558  -.094239  .038228
+    1911.80   .130866  .021743   .155825  .020309   .0000000  .0000000   .123438  .042282  -.010054  .034573
+    1911.85   .158551  .020434   .088616  .025134   .0000000  .0000000   .051214  .068383  -.010926  .041562
+    1911.90   .145314  .028595  -.044498  .021946   .0000000  .0000000  -.100129  .069086  -.007673  .044906
+    1911.95   .095603  .025949  -.041933  .034319   .0000000  .0000000   .023942  .151461  -.068062  .043736
+    1912.00   .083131  .028082  -.041367  .045117   .0000000  .0000000   .086921  .127853   .026547  .039122
+    1912.05  -.038956  .035506  -.097988  .024047   .0000000  .0000000  -.007819  .159622   .019820  .021583
+    1912.10   .078300  .036400  -.165300  .022900   .0000000  .0000000   .233500  .115600   .104200  .031000
+    1912.15  -.088995  .031251  -.223549  .023255   .0000000  .0000000   .220333  .093899   .077473  .038050
+    1912.20  -.127587  .022657  -.102840  .033381   .0000000  .0000000   .170145  .089310   .010912  .046724
+    1912.25  -.227272  .023288  -.194080  .022502   .0000000  .0000000   .186406  .044893  -.084389  .049865
+    1912.30  -.209405  .020707  -.109730  .020075   .0000000  .0000000   .055672  .058713   .017194  .031326
+    1912.35  -.237021  .016480  -.041122  .019962   .0000000  .0000000   .022407  .056949   .025940  .032792
+    1912.40  -.262082  .018289  -.006966  .017816   .0000000  .0000000   .029363  .090838   .043339  .022758
+    1912.45  -.225320  .035063   .056285  .028655   .0000000  .0000000   .180293  .125295  -.022633  .026061
+    1912.50  -.263907  .021742   .044713  .019951   .0000000  .0000000   .002937  .113586   .003018  .016324
+    1912.55  -.196951  .023121   .144625  .021044   .0000000  .0000000   .068149  .094223   .007916  .027238
+    1912.60  -.198267  .021321   .185500  .018223   .0000000  .0000000   .204439  .104019   .018183  .028277
+    1912.65  -.219124  .027649   .151146  .023604   .0000000  .0000000   .159364  .071235   .100388  .039220
+    1912.70  -.175982  .021677   .183356  .018936   .0000000  .0000000   .236280  .063918   .115620  .039114
+    1912.75  -.160280  .019553   .162811  .016252   .0000000  .0000000   .097070  .035523  -.038213  .039632
+    1912.80  -.103200  .021500   .187700  .021600   .0000000  .0000000   .069900  .040000  -.065100  .046800
+    1912.85  -.078852  .022537   .172599  .021871   .0000000  .0000000   .014043  .067846   .025894  .043515
+    1912.90  -.076719  .031453   .222193  .024394   .0000000  .0000000   .080993  .083189  -.048971  .054231
+    1912.95  -.018200  .033800   .178000  .021300   .0000000  .0000000  -.209300  .149500   .097800  .039400
+    1913.00  -.088918  .036157   .098053  .038779   .0000000  .0000000  -.130297  .147099   .004638  .045245
+    1913.05  -.043449  .024134   .115101  .031313   .0000000  .0000000  -.107525  .148353   .028795  .019234
+    1913.10  -.001323  .029225   .124168  .025147   .0000000  .0000000   .274923  .112863   .004371  .033360
+    1913.15  -.021744  .022356   .057252  .022649   .0000000  .0000000  -.015634  .080957  -.073590  .030838
+    1913.20   .011122  .019340   .015761  .020873   .0000000  .0000000   .202756  .058131   .031678  .036427
+    1913.25  -.010477  .016419  -.071666  .019034   .0000000  .0000000   .000632  .034529   .016072  .036418
+    1913.30   .032491  .025253   .003044  .016851   .0000000  .0000000  -.002118  .060835   .051937  .035678
+    1913.35   .048101  .019458  -.050706  .023830   .0000000  .0000000   .005973  .067868   .058074  .035138
+    1913.40   .032669  .022624  -.039496  .022980   .0000000  .0000000   .145719  .101370  -.023783  .025456
+    1913.45   .008311  .023950  -.068348  .019240   .0000000  .0000000   .095341  .112721   .009351  .016753
+    1913.50   .000027  .031816  -.047918  .022600   .0000000  .0000000   .254609  .113267  -.033878  .023651
+    1913.55  -.076685  .026892  -.083122  .018481   .0000000  .0000000  -.087757  .110497  -.002857  .031253
+    1913.60  -.108057  .017340  -.085386  .017913   .0000000  .0000000  -.016574  .091878  -.013581  .024005
+    1913.65  -.100400  .027900  -.040800  .019000   .0000000  .0000000   .371500  .067400   .106400  .035600
+    1913.70  -.162152  .019790  -.081823  .024953   .0000000  .0000000   .226011  .076275   .094102  .046150
+    1913.75  -.223157  .019148  -.030051  .015589   .0000000  .0000000   .140482  .033187  -.042264  .041169
+    1913.80  -.224000  .020000  -.002700  .028700   .0000000  .0000000   .069600  .047200   .013500  .044900
+    1913.85  -.226344  .023162   .078679  .023742   .0000000  .0000000   .094095  .068956  -.075759  .047116
+    1913.90  -.200560  .022160   .102525  .020236   .0000000  .0000000   .123629  .061773  -.061520  .034578
+    1913.95  -.313300  .039300   .074600  .021000   .0000000  .0000000  -.070900  .110300  -.070800  .034100
+    1914.00  -.203986  .042377   .155067  .052640   .0000000  .0000000  -.053388  .166647  -.005376  .060606
+    1914.05  -.260937  .067620   .252070  .062023   .0000000  .0000000   .482868  .261586  -.080710  .047209
+    1914.10  -.152037  .032358   .221001  .033547   .0000000  .0000000   .460108  .138175   .079547  .033998
+    1914.15  -.093945  .023823   .191653  .020597   .0000000  .0000000   .274282  .082321  -.003172  .029356
+    1914.20  -.063798  .022074   .184214  .017949   .0000000  .0000000   .368801  .055476   .034615  .036141
+    1914.25  -.013635  .021036   .132013  .036281   .0000000  .0000000   .182668  .064550  -.005456  .042311
+    1914.30   .045383  .017243   .170364  .018313   .0000000  .0000000   .027064  .052599   .079159  .031296
+    1914.35   .034013  .021428   .110450  .025557   .0000000  .0000000   .058854  .067678   .018142  .040045
+    1914.40   .073793  .015420   .089719  .013907   .0000000  .0000000   .195994  .076098  -.004195  .018039
+    1914.45   .106389  .029431   .069624  .018977   .0000000  .0000000   .150803  .132359   .044380  .016981
+    1914.50   .104207  .017302   .003118  .015383   .0000000  .0000000   .061613  .079659   .007552  .014041
+    1914.55   .085502  .013219  -.025467  .011498   .0000000  .0000000  -.005456  .066776  -.016331  .019034
+    1914.60   .049258  .014447  -.008534  .018946   .0000000  .0000000  -.039541  .090380   .020453  .024494
+    1914.65  -.013923  .021070  -.060487  .020484   .0000000  .0000000   .029862  .061791   .025569  .031829
+    1914.70  -.046141  .017808  -.151237  .014883   .0000000  .0000000   .000046  .048187  -.025157  .027803
+    1914.75  -.094789  .022287  -.177467  .016968   .0000000  .0000000   .017265  .038972  -.054229  .037879
+    1914.80  -.140169  .032081  -.214589  .017725   .0000000  .0000000  -.107241  .042845  -.020467  .037875
+    1914.85  -.220007  .022213  -.164939  .020100   .0000000  .0000000   .010800  .055608  -.127015  .043005
+    1914.90  -.271487  .019822  -.080598  .028419   .0000000  .0000000   .093833  .074114  -.119831  .044306
+    1914.95  -.197162  .054534  -.003731  .040356   .0000000  .0000000  -.033235  .234490   .126051  .065477
+    1915.00  -.365371  .033632   .018627  .024793   .0000000  .0000000   .050076  .137027   .007352  .039388
+    1915.05  -.311390  .034237   .018527  .036937   .0000000  .0000000   .470422  .190718  -.025907  .027027
+    1915.10  -.363577  .028404   .127944  .046152   .0000000  .0000000   .199111  .082412  -.005714  .031288
+    1915.15  -.230546  .034338   .091634  .039141   .0000000  .0000000   .217155  .152154   .062311  .052850
+    1915.20  -.285678  .027666   .254673  .030284   .0000000  .0000000   .366048  .083251   .064727  .056257
+    1915.25  -.300793  .035000   .364102  .032230   .0000000  .0000000   .480897  .069664  -.061038  .070711
+    1915.30  -.145488  .022572   .374588  .051362   .0000000  .0000000   .264689  .087911  -.030308  .039430
+    1915.35  -.084157  .022819   .325899  .025296   .0000000  .0000000   .251653  .074966   .026434  .040212
+    1915.40  -.008400  .021600   .253100  .027800   .0000000  .0000000   .261400  .120600   .015400  .027700
+    1915.45   .086674  .025374   .275352  .030762   .0000000  .0000000   .027152  .116818   .032016  .025083
+    1915.50   .081245  .018951   .210673  .023988   .0000000  .0000000   .020150  .110182   .042758  .016256
+    1915.55   .121650  .022685   .162508  .020369   .0000000  .0000000  -.076632  .111521   .060233  .031530
+    1915.60   .169064  .028416  -.052355  .038319   .0000000  .0000000   .017374  .111772  -.019577  .038525
+    1915.65   .112084  .021944   .002862  .022580   .0000000  .0000000   .038242  .062160   .031483  .035854
+    1915.70   .099188  .020367  -.061928  .021327   .0000000  .0000000  -.010568  .066166   .064707  .036261
+    1915.75   .093628  .029174  -.172293  .033092   .0000000  .0000000  -.095497  .050692  -.056246  .054216
+    1915.80  -.031363  .037232  -.267297  .036264   .0000000  .0000000   .011846  .072173  -.063122  .041518
+    1915.85  -.050020  .030742  -.262266  .023155   .0000000  .0000000  -.027821  .088036  -.038193  .050505
+    1915.90  -.047776  .025033  -.265495  .028877   .0000000  .0000000   .118091  .069539  -.019792  .039372
+    1915.95  -.135461  .023173  -.219164  .028298   .0000000  .0000000  -.012289  .086845  -.027399  .027321
+    1916.00  -.250486  .023204  -.127502  .022968   .0000000  .0000000  -.021577  .103420   .018951  .028163
+    1916.05  -.339030  .037330  -.192532  .045931   .0000000  .0000000   .024111  .137392  -.018940  .034175
+    1916.10  -.414793  .058039  -.366168  .141532   .0000000  .0000000  -.098174  .170431   .076541  .118153
+    1916.15  -.341536  .021953  -.026629  .029162   .0000000  .0000000   .169266  .088743  -.014264  .030157
+    1916.20  -.344556  .018814   .102289  .019703   .0000000  .0000000   .252240  .050378  -.029106  .028335
+    1916.25  -.312204  .024609   .143646  .020499   .0000000  .0000000   .180470  .047266   .015853  .027610
+    1916.30  -.283589  .023924   .181301  .020568   .0000000  .0000000   .143325  .058658   .063949  .026548
+    1916.35  -.251093  .018184   .263902  .019588   .0000000  .0000000   .138055  .053509   .020526  .024625
+    1916.40  -.158543  .020020   .396971  .033556   .0000000  .0000000   .158620  .085492   .044177  .026115
+    1916.45  -.236712  .062220   .246063  .050573   .0000000  .0000000   .136788  .152108   .143947  .057248
+    1916.50  -.049133  .028615   .295525  .022294   .0000000  .0000000  -.030297  .075359   .037090  .022295
+    1916.55  -.019000  .038200   .215800  .026700   .0000000  .0000000   .070100  .103300   .069400  .029700
+    1916.60   .091660  .018963   .193570  .018460   .0000000  .0000000  -.039878  .073489   .007240  .022693
+    1916.65   .171184  .018604   .142932  .016015   .0000000  .0000000   .230323  .045535   .085540  .025321
+    1916.70   .135500  .023400   .053900  .025100   .0000000  .0000000  -.046700  .057800  -.000100  .025700
+    1916.75   .159276  .025292  -.060418  .036456   .0000000  .0000000   .011834  .078911  -.008215  .026973
+    1916.80   .195249  .023248  -.060384  .023203   .0000000  .0000000   .031580  .041769  -.048167  .034018
+    1916.85   .041662  .033202  -.606821  .141969   .0000000  .0000000   .943431  .274302   .112475  .054856
+    1916.90   .012991  .025535  -.219566  .030003   .0000000  .0000000   .053736  .068762  -.061942  .039228
+    1916.95  -.003679  .034430  -.318930  .051949   .0000000  .0000000   .050138  .118290  -.018448  .050335
+    1917.00  -.095516  .031991  -.232917  .036690   .0000000  .0000000   .113371  .098425  -.038837  .039895
+    1917.05  -.151536  .032643  -.182785  .035632   .0000000  .0000000   .177793  .081918  -.049965  .028040
+    1917.10  -.182049  .018780  -.136279  .020541   .0000000  .0000000   .226590  .064294  -.029064  .017525
+    1917.15  -.292198  .021566  -.114514  .025900   .0000000  .0000000   .378588  .066745  -.025331  .022017
+    1917.20  -.293211  .023421  -.077170  .028302   .0000000  .0000000   .271657  .067886  -.007479  .030789
+    1917.25  -.310501  .018384  -.047076  .019377   .0000000  .0000000   .320842  .036052   .020607  .038323
+    1917.30  -.262144  .022858   .028782  .036242   .0000000  .0000000   .158905  .076173   .065957  .042121
+    1917.35  -.195616  .034674   .145893  .043448   .0000000  .0000000  -.131604  .107830   .175394  .060186
+    1917.40  -.216823  .016046   .172434  .021974   .0000000  .0000000   .205536  .089022   .029871  .023536
+    1917.45  -.195552  .023695   .154519  .024785   .0000000  .0000000   .094768  .093800   .041009  .017617
+    1917.50  -.114387  .021710   .252819  .017637   .0000000  .0000000   .160593  .109832   .011260  .015601
+    1917.55  -.014913  .026123   .095478  .050081   .0000000  .0000000   .119251  .121268  -.101896  .040784
+    1917.60  -.032948  .022517   .203552  .021771   .0000000  .0000000  -.017169  .093714  -.018236  .029087
+    1917.65  -.004274  .018041   .202191  .017393   .0000000  .0000000   .223112  .056366   .133130  .035319
+    1917.70  -.029300  .023400   .187800  .020200   .0000000  .0000000   .092500  .064100   .150900  .036000
+    1917.75   .047363  .042714   .077854  .035380   .0000000  .0000000  -.021470  .089513  -.046959  .056889
+    1917.80   .027454  .027359   .011902  .023281   .0000000  .0000000  -.023835  .047870  -.100501  .044000
+    1917.85   .086395  .033256   .016630  .018122   .0000000  .0000000   .164901  .073747  -.031988  .042757
+    1917.90   .011086  .026318   .026413  .034694   .0000000  .0000000   .093324  .100088  -.148308  .056746
+    1917.95   .061347  .020973  -.033647  .023957   .0000000  .0000000  -.024511  .101763   .006694  .027343
+    1918.00  -.045800  .028200  -.126500  .035300   .0000000  .0000000   .111100  .145300  -.004400  .040900
+    1918.05  -.063352  .035400  -.187119  .045038   .0000000  .0000000  -.004287  .122131   .022486  .027016
+    1918.10  -.140380  .021869  -.029766  .034383   .0000000  .0000000   .138678  .119162  -.033442  .029042
+    1918.15  -.139590  .025411  -.046948  .025044   .0000000  .0000000   .255759  .076958   .009428  .028052
+    1918.20  -.186229  .018686  -.012003  .022437   .0000000  .0000000   .099720  .046074  -.030547  .019541
+    1918.25  -.197272  .017486  -.022847  .021751   .0000000  .0000000   .138537  .040238   .001337  .024304
+    1918.30  -.162233  .019532   .009864  .026445   .0000000  .0000000   .072541  .049151  -.023444  .023410
+    1918.35  -.209522  .022531   .106965  .024980   .0000000  .0000000   .166654  .068932  -.049008  .032858
+    1918.40  -.183315  .017646   .111698  .021615   .0000000  .0000000   .030447  .078502   .025251  .021946
+    1918.45  -.186120  .023373   .161230  .026925   .0000000  .0000000   .057971  .095803   .032482  .020663
+    1918.50  -.206139  .033232   .170211  .036136   .0000000  .0000000   .083654  .106947   .070057  .023682
+    1918.55  -.140500  .023300   .134700  .019600   .0000000  .0000000   .227200  .082400   .039700  .023100
+    1918.60  -.075624  .019336   .126939  .021875   .0000000  .0000000   .214103  .104242   .041851  .030046
+    1918.65  -.035250  .017052   .149125  .023557   .0000000  .0000000   .188283  .065782   .040647  .035960
+    1918.70  -.038388  .018281   .139481  .017860   .0000000  .0000000   .132147  .048923  -.014313  .024805
+    1918.75   .005607  .032180   .126979  .025465   .0000000  .0000000   .182018  .068228  -.028571  .028371
+    1918.80  -.039489  .022068   .133454  .021575   .0000000  .0000000   .134319  .041766  -.041681  .031411
+    1918.85  -.047400  .036400   .170000  .021500   .0000000  .0000000   .337000  .084200  -.073900  .051500
+    1918.90  -.061884  .029362   .112716  .024389   .0000000  .0000000   .055984  .073331  -.062078  .035249
+    1918.95  -.059354  .023109   .062420  .030480   .0000000  .0000000   .077009  .074970  -.013496  .022825
+    1919.00  -.058158  .032137   .016625  .047864   .0000000  .0000000  -.016848  .102171  -.051654  .033902
+    1919.05  -.045037  .027163   .121493  .032572   .0000000  .0000000   .061742  .159161   .007806  .023268
+    1919.10  -.064165  .030520   .072740  .023797   .0000000  .0000000   .054161  .103195  -.030628  .028565
+    1919.15  -.046142  .019958   .055541  .023710   .0000000  .0000000   .175499  .058670   .000885  .017390
+    1919.20  -.076649  .021472   .084834  .024008   .0000000  .0000000   .192932  .054744   .038099  .024214
+    1919.25  -.032000  .023100   .101400  .024500   .0000000  .0000000   .019400  .044200  -.032300  .039800
+    1919.30  -.032764  .019670   .042454  .021259   .0000000  .0000000   .063473  .058489  -.018695  .035014
+    1919.35  -.034002  .016296   .057361  .029991   .0000000  .0000000  -.073542  .063390   .036074  .027766
+    1919.40  -.081513  .022556   .071678  .026159   .0000000  .0000000   .028931  .092000  -.007650  .026187
+    1919.45  -.024396  .025236   .011394  .055909   .0000000  .0000000  -.005223  .115240   .015397  .031016
+    1919.50  -.005435  .021494   .014449  .023472   .0000000  .0000000  -.038104  .127579  -.000757  .018115
+    1919.55  -.036700  .021400  -.072300  .031400   .0000000  .0000000   .269200  .086200   .023200  .025300
+    1919.60  -.054480  .015453  -.035798  .017133   .0000000  .0000000   .178399  .073800   .000860  .022478
+    1919.65  -.109833  .019620  -.018115  .022290   .0000000  .0000000   .165576  .057084   .045457  .031020
+    1919.70  -.180209  .028864  -.046368  .034048   .0000000  .0000000  -.171687  .073328  -.040531  .036099
+    1919.75  -.144050  .022290   .048583  .024879   .0000000  .0000000   .020057  .046725   .052546  .035444
+    1919.80  -.181892  .022085  -.009524  .029451   .0000000  .0000000   .105329  .047459   .021452  .057824
+    1919.85  -.159213  .031568   .054630  .033813   .0000000  .0000000   .116987  .080398  -.092610  .059791
+    1919.90  -.092562  .035587  -.197751  .085221   .0000000  .0000000   .508266  .132410  -.030663  .066537
+    1919.95  -.186227  .020600   .107207  .025198   .0000000  .0000000   .190069  .079840  -.028542  .020491
+    1920.00  -.097388  .030280   .112723  .034355   .0000000  .0000000   .077211  .158857   .019743  .045632
+    1920.05  -.145513  .024771   .244405  .039029   .0000000  .0000000   .138524  .118895   .020497  .021109
+    1920.10  -.066010  .025834   .127964  .037067   .0000000  .0000000   .069846  .071235   .039802  .023703
+    1920.15  -.108000  .026100   .125400  .030600   .0000000  .0000000   .066300  .061600  -.028500  .020500
+    1920.20  -.066646  .021442   .249459  .031772   .0000000  .0000000   .259358  .059627   .009901  .025076
+    1920.25   .062692  .073768   .241400  .067889   .0000000  .0000000  -.048321  .138016   .122761  .080310
+    1920.30   .077897  .026364   .212694  .025041   .0000000  .0000000   .062704  .072934   .037479  .040486
+    1920.35   .112620  .035137   .177084  .039181   .0000000  .0000000  -.098984  .079850  -.019825  .039959
+    1920.40   .081000  .026100   .157700  .026100   .0000000  .0000000  -.043700  .108300  -.015600  .028200
+    1920.45   .148386  .049244   .115656  .054235   .0000000  .0000000   .112764  .143248   .013350  .037333
+    1920.50   .111770  .030133   .088776  .036499   .0000000  .0000000   .028214  .158865   .019352  .026783
+    1920.55   .063562  .015232  -.046004  .020074   .0000000  .0000000   .165210  .082557   .043804  .025214
+    1920.60   .129866  .041977   .027842  .044099   .0000000  .0000000   .444455  .121075   .035385  .035705
+    1920.65   .091341  .025804  -.043051  .044110   .0000000  .0000000   .423298  .113613   .126527  .044771
+    1920.70   .068767  .019510  -.053549  .019567   .0000000  .0000000   .301134  .054516   .031598  .032433
+    1920.75  -.008467  .023590  -.089633  .026491   .0000000  .0000000   .246094  .050688  -.014311  .026566
+    1920.80  -.052510  .023572  -.063097  .026540   .0000000  .0000000   .236741  .048355   .000018  .029138
+    1920.85  -.131312  .033720  -.127174  .033493   .0000000  .0000000   .118162  .097614  -.029769  .052448
+    1920.90  -.098074  .032286  -.069955  .039897   .0000000  .0000000   .253234  .084957  -.026576  .049577
+    1920.95  -.202848  .015313  -.093388  .023228   .0000000  .0000000   .182078  .068140  -.040270  .013923
+    1921.00  -.275100  .066800   .041200  .042500   .0000000  .0000000  -.023400  .165100   .016600  .058000
+    1921.05  -.199380  .028586   .005397  .035333   .0000000  .0000000  -.091572  .106408   .013613  .024602
+    1921.10  -.269861  .029700   .062960  .035227   .0000000  .0000000   .056699  .084797   .008766  .026645
+    1921.15  -.315700  .072269   .186534  .055564   .0000000  .0000000   .005612  .166973  -.066640  .056276
+    1921.20  -.190287  .020595   .164991  .022318   .0000000  .0000000   .165225  .054654   .011393  .022283
+    1921.25  -.119466  .019502   .139311  .022964   .0000000  .0000000   .019224  .041993   .045599  .037519
+    1921.30  -.014599  .021085   .217462  .027666   .0000000  .0000000  -.095568  .071875   .053887  .036645
+    1921.35   .031054  .022785   .201362  .025135   .0000000  .0000000  -.092298  .078183   .029571  .039060
+    1921.40   .091820  .030242   .160909  .034076   .0000000  .0000000   .148554  .113642  -.091464  .030982
+    1921.45   .100985  .022216   .083825  .027543   .0000000  .0000000   .026744  .143369   .003906  .018410
+    1921.50   .212445  .039267   .182524  .042606   .0000000  .0000000   .140091  .125525  -.098040  .030768
+    1921.55   .138182  .021575   .088243  .019204   .0000000  .0000000   .229534  .076740   .023025  .021882
+    1921.60   .178414  .025674   .025800  .028487   .0000000  .0000000   .214034  .076980  -.028148  .027220
+    1921.65   .167323  .022291   .004838  .025249   .0000000  .0000000   .247682  .060809   .003549  .027454
+    1921.70   .073545  .013037  -.078915  .019119   .0000000  .0000000   .220099  .042119   .012353  .023753
+    1921.75   .127179  .026772  -.080673  .030632   .0000000  .0000000   .314240  .060325   .082886  .038983
+    1921.80  -.002474  .023205  -.169272  .027927   .0000000  .0000000   .209071  .046972  -.018626  .044988
+    1921.85  -.007518  .031000  -.143864  .026801   .0000000  .0000000   .203157  .091797  -.064613  .051434
+    1921.90  -.098209  .028421   .106262  .070691   .0000000  .0000000   .097249  .075872  -.090948  .061889
+    1921.95  -.133071  .031967  -.095128  .035219   .0000000  .0000000   .117126  .148240   .014827  .043509
+    1922.00  -.045524  .055991  -.029486  .044384   .0000000  .0000000   .307043  .104146  -.016902  .046439
+    1922.05  -.194756  .044532  -.097510  .058848   .0000000  .0000000   .106856  .218178   .015172  .035679
+    1922.10  -.208934  .027890   .039567  .023542   .0000000  .0000000  -.073805  .098981  -.066748  .026652
+    1922.15  -.210300  .032200   .137300  .030800   .0000000  .0000000   .054500  .114600  -.016000  .036100
+    1922.20  -.165744  .024812   .125734  .026711   .0000000  .0000000   .114625  .076649   .019807  .040009
+    1922.25  -.162390  .024060   .134218  .035829   .0000000  .0000000   .058232  .055785  -.023740  .059016
+    1922.30  -.132697  .018657   .221570  .018851   .0000000  .0000000  -.022634  .045866   .005230  .025428
+    1922.35  -.090259  .029663   .277564  .025982   .0000000  .0000000   .017361  .084752  -.018873  .040448
+    1922.40  -.047480  .019893   .260568  .022366   .0000000  .0000000   .175300  .084793  -.012303  .021789
+    1922.45   .009070  .019132   .222538  .023939   .0000000  .0000000  -.056816  .112701  -.019610  .016697
+    1922.50   .111622  .026181   .240501  .028555   .0000000  .0000000   .412616  .116971  -.064542  .019953
+    1922.55   .088770  .020152   .114259  .024598   .0000000  .0000000   .214716  .105492   .025888  .030218
+    1922.60   .163154  .024272   .076991  .025729   .0000000  .0000000  -.013639  .112548  -.019895  .030195
+    1922.65   .151317  .021950   .014994  .023451   .0000000  .0000000   .186478  .061686  -.002172  .033412
+    1922.70   .141800  .018300   .014900  .018700   .0000000  .0000000   .139000  .038700   .018300  .031200
+    1922.75   .120772  .026230  -.019145  .032331   .0000000  .0000000   .103344  .057611   .012603  .056829
+    1922.80   .083381  .019058  -.139651  .018579   .0000000  .0000000   .134790  .050035  -.044946  .030526
+    1922.85   .063654  .035885  -.088851  .036843   .0000000  .0000000   .060419  .131035  -.020871  .058891
+    1922.90   .041976  .038739   .015059  .046298   .0000000  .0000000   .137765  .082264   .022699  .035152
+    1922.95  -.163179  .049215  -.120346  .044629   .0000000  .0000000  -.088422  .186106   .057819  .039344
+    1923.00  -.162345  .024176  -.088042  .028195   .0000000  .0000000  -.164558  .105026   .022870  .021395
+    1923.05  -.166986  .029597  -.055441  .032552   .0000000  .0000000  -.155703  .122746  -.022616  .036478
+    1923.10  -.332673  .046590  -.032441  .038226   .0000000  .0000000   .118174  .100248   .127325  .057503
+    1923.15  -.262008  .031040  -.033456  .038152   .0000000  .0000000  -.075136  .091210  -.070144  .043722
+    1923.20  -.207748  .020628   .016212  .020991   .0000000  .0000000  -.002057  .040807  -.018829  .031774
+    1923.25  -.274792  .051456   .140175  .032202   .0000000  .0000000   .200018  .110268   .020372  .031238
+    1923.30  -.179152  .022040   .157417  .021359   .0000000  .0000000  -.096701  .057214   .020842  .030157
+    1923.35  -.176779  .038078   .129676  .046107   .0000000  .0000000   .232484  .110410   .012090  .038815
+    1923.40  -.097364  .026461   .300984  .030708   .0000000  .0000000   .012199  .114308  -.024269  .032124
+    1923.45  -.024124  .029729   .317510  .037800   .0000000  .0000000   .242278  .122543  -.026966  .025412
+    1923.50  -.019994  .014380   .239535  .021068   .0000000  .0000000   .097043  .096033   .007601  .012214
+    1923.55   .060132  .038504   .240230  .038033   .0000000  .0000000   .008628  .090767  -.027426  .036225
+    1923.60   .112367  .014229   .311274  .019214   .0000000  .0000000   .109885  .052765   .034879  .027024
+    1923.65   .142183  .016013   .249476  .023914   .0000000  .0000000   .086034  .050317   .040333  .029040
+    1923.70   .151780  .026662   .226170  .029457   .0000000  .0000000   .065406  .051934   .009481  .027690
+    1923.75   .161990  .027635   .129040  .031921   .0000000  .0000000  -.002354  .060430   .038004  .044922
+    1923.80   .130175  .022364   .020789  .024598   .0000000  .0000000   .076566  .061195  -.044418  .038181
+    1923.85   .136144  .036406   .082803  .038211   .0000000  .0000000  -.134067  .095774   .027947  .040494
+    1923.90   .088243  .027972   .024731  .025906   .0000000  .0000000  -.078050  .103716   .037490  .030391
+    1923.95   .035504  .031898  -.012841  .031608   .0000000  .0000000   .128937  .135153   .027011  .025480
+    1924.00  -.005804  .035143   .073064  .051468   .0000000  .0000000  -.102657  .167052   .001261  .032029
+    1924.05  -.068517  .027242   .011667  .025525   .0000000  .0000000   .059077  .111376   .021297  .030942
+    1924.10  -.029063  .036913   .003840  .046785   .0000000  .0000000   .232378  .154239   .074931  .048315
+    1924.15  -.149500  .033400  -.020500  .049200   .0000000  .0000000   .296300  .127700   .093800  .074500
+    1924.20  -.151107  .021433   .038096  .026611   .0000000  .0000000   .023358  .049099   .009227  .029539
+    1924.25  -.170714  .022449   .072246  .022869   .0000000  .0000000   .026865  .044276  -.077597  .042873
+    1924.30  -.127400  .020900   .135100  .025000   .0000000  .0000000  -.040200  .068600  -.015300  .035600
+    1924.35  -.165261  .035290   .139194  .034218   .0000000  .0000000  -.000914  .122546  -.014604  .040754
+    1924.40  -.187563  .014175   .107926  .015713   .0000000  .0000000   .089797  .091070  -.022451  .023568
+    1924.45  -.081980  .023478   .195088  .030162   .0000000  .0000000   .124901  .125047  -.048720  .019768
+    1924.50  -.075229  .031225   .183050  .046061   .0000000  .0000000   .102382  .137256  -.009329  .023857
+    1924.55  -.059435  .017414   .142566  .018273   .0000000  .0000000   .192221  .072824   .009825  .019926
+    1924.60   .014627  .017728   .135909  .020162   .0000000  .0000000   .138120  .065731   .009780  .030529
+    1924.65   .029893  .027133   .185564  .023512   .0000000  .0000000   .102141  .075815   .020640  .040568
+    1924.70  -.002107  .026548   .154303  .031812   .0000000  .0000000  -.026603  .057367  -.011327  .030482
+    1924.75   .041539  .036354   .113626  .034837   .0000000  .0000000   .151911  .071214   .043723  .033814
+    1924.80   .032319  .024384   .000498  .024990   .0000000  .0000000   .207029  .078052  -.085419  .044908
+    1924.85  -.079291  .038861   .056224  .034261   .0000000  .0000000   .076218  .110676  -.016385  .058046
+    1924.90  -.006172  .018051   .025546  .020137   .0000000  .0000000  -.054575  .085395   .029590  .022098
+    1924.95   .035597  .040982   .085593  .033254   .0000000  .0000000   .234642  .097196   .022594  .030779
+    1925.00  -.012995  .031013   .036963  .045216   .0000000  .0000000   .188166  .186937   .012966  .026431
+    1925.05  -.056182  .020519  -.049412  .028318   .0000000  .0000000   .285166  .064449   .087534  .023179
+    1925.10  -.103481  .027056  -.003581  .026594   .0000000  .0000000   .125679  .101046  -.029143  .030668
+    1925.15  -.107312  .034292  -.041495  .031768   .0000000  .0000000   .212551  .082271  -.011883  .035673
+    1925.20  -.141179  .021992  -.046236  .026297   .0000000  .0000000  -.026763  .049519  -.021964  .028860
+    1925.25  -.102647  .037056   .016748  .044842   .0000000  .0000000  -.039005  .088062  -.049630  .046314
+    1925.30  -.130539  .021781   .119861  .026310   .0000000  .0000000   .018221  .070534  -.089786  .034850
+    1925.35  -.066774  .024942   .097721  .023850   .0000000  .0000000  -.014274  .068507  -.016102  .033898
+    1925.40  -.004013  .035871   .055231  .029359   .0000000  .0000000   .224329  .117863   .022171  .035324
+    1925.45  -.066873  .019233   .032174  .023508   .0000000  .0000000   .259899  .137874  -.029799  .017789
+    1925.50  -.055672  .016072   .061909  .019348   .0000000  .0000000   .361168  .126978   .002462  .014184
+    1925.55  -.070311  .022033   .114851  .022917   .0000000  .0000000   .040450  .074518   .020575  .020834
+    1925.60   .017853  .031090   .118506  .030840   .0000000  .0000000   .070523  .090048   .005634  .031098
+    1925.65  -.015743  .021671   .056794  .019940   .0000000  .0000000   .035280  .051561   .027148  .024849
+    1925.70   .001485  .022114   .004726  .025001   .0000000  .0000000   .123941  .045601   .003221  .030968
+    1925.75  -.005631  .042884   .015549  .045587   .0000000  .0000000   .017215  .067382  -.048574  .041449
+    1925.80   .001128  .022221   .013928  .028038   .0000000  .0000000   .024302  .054538  -.002247  .034619
+    1925.85  -.243256  .079470  -.060509  .053110   .0000000  .0000000   .276019  .175165  -.019029  .077528
+    1925.90  -.025734  .030105   .007049  .023785   .0000000  .0000000   .121188  .105485   .002998  .040199
+    1925.95  -.041648  .066166  -.013321  .030918   .0000000  .0000000   .015089  .105117   .021729  .049347
+    1926.00  -.199605  .036146  -.083663  .038724   .0000000  .0000000   .245215  .109884   .039478  .035638
+    1926.05  -.163818  .039434  -.029853  .040669   .0000000  .0000000   .044359  .068495   .053898  .032339
+    1926.10  -.073051  .027728  -.074540  .032460   .0000000  .0000000   .213393  .066358  -.048102  .023696
+    1926.15  -.177265  .023116  -.060285  .021659   .0000000  .0000000   .105872  .050817  -.037163  .026399
+    1926.20  -.230084  .046104  -.024826  .045615   .0000000  .0000000   .025719  .090929   .040620  .034105
+    1926.25  -.173813  .019079   .058121  .020126   .0000000  .0000000   .132828  .041532  -.018853  .028000
+    1926.30  -.052708  .037348   .122825  .027319   .0000000  .0000000  -.076773  .071126  -.041129  .037601
+    1926.35  -.106664  .050808   .067451  .031627   .0000000  .0000000   .269221  .103461  -.035203  .041686
+    1926.40  -.129567  .034399   .112402  .028636   .0000000  .0000000  -.020163  .110908   .014179  .027107
+    1926.45  -.076581  .041883   .132865  .051608   .0000000  .0000000   .286320  .144497  -.043525  .025421
+    1926.50  -.051765  .019589   .131885  .021134   .0000000  .0000000   .052798  .090155   .047918  .015748
+    1926.55  -.033933  .030075   .119936  .029214   .0000000  .0000000   .315712  .098450   .006669  .028847
+    1926.60  -.053049  .024930   .122331  .026656   .0000000  .0000000   .154883  .074546   .015092  .039362
+    1926.65  -.021532  .025016   .117705  .028125   .0000000  .0000000   .160320  .076375   .063924  .039989
+    1926.70   .018915  .028880   .156256  .031268   .0000000  .0000000   .164856  .056655  -.000981  .030026
+    1926.75  -.001894  .051763   .104146  .035162   .0000000  .0000000   .018542  .092282  -.065648  .073984
+    1926.80  -.075772  .034355   .076597  .025218   .0000000  .0000000   .115426  .075478  -.006398  .036664
+    1926.85  -.120294  .031359   .111799  .022066   .0000000  .0000000   .009381  .067532   .019260  .032445
+    1926.90  -.050061  .047015   .130391  .040796   .0000000  .0000000  -.137747  .156385   .046403  .040460
+    1926.95  -.098525  .038333   .073404  .038221   .0000000  .0000000   .009895  .081858   .017527  .033499
+    1927.00  -.058200  .031028   .056215  .030066   .0000000  .0000000   .045607  .114872   .011025  .024078
+    1927.05  -.142988  .021528   .109762  .025240   .0000000  .0000000   .117355  .050786   .038633  .014167
+    1927.10  -.032237  .029414   .116674  .028615   .0000000  .0000000   .072703  .088337   .033588  .026881
+    1927.15  -.095191  .041669   .115885  .031413   .0000000  .0000000  -.020091  .084410  -.002517  .021791
+    1927.20  -.059447  .042723   .084129  .029841   .0000000  .0000000   .129499  .067080   .031133  .036799
+    1927.25  -.115876  .069511   .071429  .057155   .0000000  .0000000  -.078722  .128583  -.029423  .079680
+    1927.30  -.052232  .023950   .049356  .028721   .0000000  .0000000  -.131758  .063986  -.015955  .033137
+    1927.35  -.054137  .026269   .027885  .031093   .0000000  .0000000  -.111493  .097984  -.040770  .029515
+    1927.40  -.013624  .039136   .120684  .028181   .0000000  .0000000   .007610  .143696  -.035268  .039320
+    1927.45   .095331  .054825   .074304  .030591   .0000000  .0000000   .251516  .118766   .055469  .038560
+    1927.50   .015128  .025200   .128662  .022917   .0000000  .0000000   .098949  .079649  -.039049  .019701
+    1927.55  -.016024  .022647   .158240  .023726   .0000000  .0000000   .097476  .087155  -.044639  .022680
+    1927.60  -.031647  .018259   .047004  .021176   .0000000  .0000000   .125683  .060861   .048557  .029299
+    1927.65  -.020014  .030139   .048718  .035066   .0000000  .0000000   .149458  .064278   .009084  .029568
+    1927.70  -.004146  .030681   .054252  .029156   .0000000  .0000000   .114527  .062750  -.020703  .027155
+    1927.75  -.052691  .022091   .005745  .024345   .0000000  .0000000   .004075  .043762  -.019228  .034849
+    1927.80  -.052571  .021420  -.025327  .020197   .0000000  .0000000   .121377  .062185  -.034289  .031578
+    1927.85  -.093446  .029344  -.044573  .032284   .0000000  .0000000   .242147  .119038  -.103622  .049001
+    1927.90  -.185615  .022153   .036816  .025910   .0000000  .0000000  -.005638  .086229   .039597  .024397
+    1927.95  -.159711  .056181  -.002380  .036259   .0000000  .0000000   .080488  .138148  -.032442  .037062
+    1928.00  -.008822  .073018  -.065239  .048131   .0000000  .0000000   .060773  .116263  -.147510  .046945
+    1928.05  -.145700  .027000  -.017500  .028100   .0000000  .0000000   .270700  .055800  -.018800  .023000
+    1928.10  -.134670  .045726   .100011  .059042   .0000000  .0000000   .168304  .084694   .043459  .044433
+    1928.15  -.142914  .038281   .014838  .046056   .0000000  .0000000   .107359  .084557  -.028794  .052158
+    1928.20  -.130658  .024641   .109191  .027751   .0000000  .0000000   .026775  .050999  -.017275  .032088
+    1928.25  -.071799  .039508   .135022  .041766   .0000000  .0000000  -.192268  .090376  -.008559  .044952
+    1928.30  -.077913  .033120   .109713  .023627   .0000000  .0000000  -.035111  .069778   .018221  .041598
+    1928.35  -.084562  .016658   .184119  .017469   .0000000  .0000000  -.049343  .069065   .025439  .023154
+    1928.40  -.070452  .022088   .122462  .029237   .0000000  .0000000   .106064  .091812  -.013073  .025375
+    1928.45  -.071262  .019075   .128166  .024402   .0000000  .0000000   .173467  .102949  -.034195  .016324
+    1928.50  -.023166  .030166   .138252  .028569   .0000000  .0000000   .127043  .091048  -.010519  .024191
+    1928.55  -.049392  .022526   .114652  .023977   .0000000  .0000000   .083308  .071831  -.025202  .024135
+    1928.60  -.132084  .056938  -.134582  .083942   .0000000  .0000000  -.190699  .101467   .059541  .048184
+    1928.65   .020427  .019776   .100107  .024660   .0000000  .0000000   .087240  .054196   .014561  .032237
+    1928.70  -.028438  .018567   .032299  .019668   .0000000  .0000000  -.034806  .039592   .048647  .026722
+    1928.75  -.110100  .026400   .107500  .033000   .0000000  .0000000  -.023100  .060300   .111700  .072200
+    1928.80  -.143376  .025745  -.000841  .024534   .0000000  .0000000  -.069267  .061539  -.023213  .023665
+    1928.85  -.115036  .034254   .011216  .024412   .0000000  .0000000   .040894  .081254  -.065663  .034994
+    1928.90  -.110841  .024115   .085727  .033606   .0000000  .0000000   .007425  .148468  -.024778  .044137
+    1928.95  -.072093  .032112   .151032  .037101   .0000000  .0000000   .038861  .124679  -.002752  .024079
+    1929.00  -.160303  .029001   .044210  .030370   .0000000  .0000000   .121852  .128216  -.032636  .021252
+    1929.05  -.288649  .053456   .003539  .044103   .0000000  .0000000  -.023736  .147949  -.084211  .042433
+    1929.10  -.100071  .039581   .109711  .034391   .0000000  .0000000   .289148  .113132  -.033205  .044177
+    1929.15  -.169363  .033012   .171496  .030953   .0000000  .0000000   .241431  .111617  -.005452  .049005
+    1929.20  -.126222  .020713   .140853  .024303   .0000000  .0000000   .067108  .045114  -.056829  .037420
+    1929.25  -.057249  .016782   .149535  .017242   .0000000  .0000000   .031820  .032694  -.046550  .031388
+    1929.30   .003629  .032178   .164681  .031323   .0000000  .0000000   .092760  .083365   .035925  .044336
+    1929.35   .131595  .040910   .332923  .037479   .0000000  .0000000  -.356197  .110153   .059423  .033529
+    1929.40   .031158  .021226   .160059  .029831   .0000000  .0000000   .028882  .119329   .013646  .030434
+    1929.45   .069220  .021082   .115295  .019283   .0000000  .0000000   .196940  .112372   .007935  .014267
+    1929.50   .103978  .030232   .187112  .034392   .0000000  .0000000   .202257  .130538   .013072  .024857
+    1929.55   .082883  .017225   .085168  .023736   .0000000  .0000000   .082421  .084147   .017983  .020976
+    1929.60   .049832  .020908  -.000080  .026960   .0000000  .0000000   .138280  .081479   .046409  .034977
+    1929.65   .073767  .019423   .013919  .021340   .0000000  .0000000   .065310  .066193   .016860  .034935
+    1929.70   .084972  .061105   .064950  .075161   .0000000  .0000000   .161526  .109929  -.025795  .083002
+    1929.75  -.015329  .020990  -.028934  .031128   .0000000  .0000000  -.002841  .046003  -.101838  .056884
+    1929.80  -.093999  .017499  -.073280  .016597   .0000000  .0000000   .073164  .043236  -.044932  .023701
+    1929.85  -.161299  .029904  -.061529  .021325   .0000000  .0000000  -.006386  .072407   .008835  .041415
+    1929.90  -.165908  .024019  -.084395  .026630   .0000000  .0000000  -.121433  .117280   .028022  .031988
+    1929.95  -.205862  .034033   .028014  .040755   .0000000  .0000000   .026057  .137650  -.067140  .030804
+    1930.00  -.214153  .025399  -.044059  .024030   .0000000  .0000000   .202892  .112867   .004848  .020655
+    1930.05  -.215942  .020998   .054329  .022252   .0000000  .0000000   .153369  .082377  -.003220  .020741
+    1930.10  -.225480  .033817   .057826  .028905   .0000000  .0000000   .103087  .099115  -.045942  .038739
+    1930.15  -.206525  .026438   .096138  .031489   .0000000  .0000000   .032674  .078500   .019438  .036829
+    1930.20  -.166814  .026412   .146704  .021097   .0000000  .0000000   .070394  .052440  -.038621  .031795
+    1930.25  -.130922  .025349   .224514  .023384   .0000000  .0000000  -.037018  .048621   .040396  .033628
+    1930.30  -.049593  .026987   .179473  .025143   .0000000  .0000000   .135378  .074078  -.008703  .042877
+    1930.35  -.125200  .026100   .154900  .024500   .0000000  .0000000   .104500  .094000  -.024100  .028100
+    1930.40   .006745  .019346   .237422  .020329   .0000000  .0000000  -.048105  .087983   .035750  .025097
+    1930.45   .017674  .022192   .198183  .024829   .0000000  .0000000   .497903  .112541  -.047527  .017231
+    1930.50   .035577  .019204   .132240  .022916   .0000000  .0000000   .114673  .112661   .031958  .015928
+    1930.55   .081022  .028941   .126161  .035280   .0000000  .0000000   .178570  .113183   .044067  .033186
+    1930.60   .098407  .020433   .114242  .025772   .0000000  .0000000   .115599  .079567   .015376  .029606
+    1930.65   .074379  .020892  -.037760  .025658   .0000000  .0000000  -.079804  .064054   .053494  .038670
+    1930.70   .062912  .022160  -.022243  .022567   .0000000  .0000000   .042622  .042296  -.013889  .047025
+    1930.75   .028598  .018984   .019082  .021020   .0000000  .0000000   .040430  .039123  -.000354  .032659
+    1930.80  -.011225  .023863  -.081563  .021891   .0000000  .0000000   .037880  .062133  -.007597  .035154
+    1930.85  -.084804  .062224  -.009200  .038617   .0000000  .0000000  -.028399  .149308   .076616  .069941
+    1930.90  -.116025  .028558  -.003658  .026403   .0000000  .0000000   .122780  .124121  -.036343  .039668
+    1930.95  -.158405  .032485  -.031682  .028104   .0000000  .0000000   .230804  .094347  -.015785  .025706
+    1931.00  -.353309  .096479   .027867  .089982   .0000000  .0000000  -.059218  .147494   .047382  .083239
+    1931.05  -.277671  .037328   .046550  .032796   .0000000  .0000000   .029490  .181518   .039120  .037162
+    1931.10  -.239000  .080700  -.015100  .066000   .0000000  .0000000   .135500  .183700  -.036200  .061100
+    1931.15  -.355078  .047704   .116611  .027520   .0000000  .0000000  -.068468  .119503  -.011647  .037294
+    1931.20  -.226864  .040213   .167103  .029516   .0000000  .0000000   .008206  .083614   .006204  .048961
+    1931.25  -.248657  .048779   .182523  .025817   .0000000  .0000000  -.106053  .087283  -.017485  .048950
+    1931.30  -.150564  .025159   .238762  .019410   .0000000  .0000000  -.021250  .053953  -.038649  .032878
+    1931.35  -.151393  .023537   .231228  .027262   .0000000  .0000000   .005501  .079420   .004284  .027058
+    1931.40  -.039529  .026810   .281355  .018958   .0000000  .0000000   .011981  .079330  -.009609  .023743
+    1931.45   .024987  .022077   .296176  .025076   .0000000  .0000000   .034315  .105782   .013971  .017995
+    1931.50   .039600  .020200   .242500  .021800   .0000000  .0000000   .024100  .122600  -.003900  .015000
+    1931.55   .074415  .022490   .182812  .023523   .0000000  .0000000   .025877  .085360   .022079  .023021
+    1931.60   .098426  .027142   .094944  .022076   .0000000  .0000000   .019474  .068532   .038770  .032649
+    1931.65   .082462  .022539   .055652  .020618   .0000000  .0000000  -.131105  .059693   .032424  .033455
+    1931.70   .165785  .028535   .079323  .020027   .0000000  .0000000   .014870  .042304  -.046763  .030159
+    1931.75   .067878  .020433   .029704  .017501   .0000000  .0000000  -.026892  .036533  -.004535  .030519
+    1931.80   .052973  .019446  -.106345  .016117   .0000000  .0000000  -.001599  .049110   .000611  .027249
+    1931.85  -.083590  .035160  -.105084  .021113   .0000000  .0000000   .167197  .098585  -.038658  .042268
+    1931.90  -.060868  .023135  -.054736  .023489   .0000000  .0000000   .098301  .077842  -.032916  .026478
+    1931.95  -.091079  .025492  -.143458  .016987   .0000000  .0000000   .006878  .111090   .002060  .014193
+    1932.00  -.117202  .022857  -.090917  .014931   .0000000  .0000000   .032518  .104975  -.031310  .014357
+    1932.05  -.196529  .025311  -.057717  .024631   .0000000  .0000000   .020887  .093791  -.023320  .023453
+    1932.10  -.166200  .038500  -.015500  .021000   .0000000  .0000000   .148800  .080700  -.020600  .028300
+    1932.15  -.312357  .031985   .014781  .024170   .0000000  .0000000   .038023  .080383  -.026564  .031241
+    1932.20  -.324819  .072653   .020690  .050831   .0000000  .0000000   .031104  .170624  -.100605  .080162
+    1932.25  -.229236  .026510   .137146  .021528   .0000000  .0000000   .013136  .050132   .049470  .031438
+    1932.30  -.199450  .026455   .181143  .016335   .0000000  .0000000   .095308  .054191  -.026872  .023956
+    1932.35  -.169007  .034571   .207207  .032670   .0000000  .0000000   .044033  .102384   .025012  .037867
+    1932.40  -.138350  .036293   .219864  .040626   .0000000  .0000000   .060440  .140345   .036075  .038355
+    1932.45  -.071670  .043042   .214482  .046258   .0000000  .0000000  -.173260  .179819  -.021750  .028001
+    1932.50  -.075142  .023103   .230743  .021057   .0000000  .0000000   .103975  .124324   .021563  .016261
+    1932.55  -.052070  .017708   .236227  .018063   .0000000  .0000000  -.052630  .081070   .009506  .023476
+    1932.60   .026338  .029331   .224720  .020402   .0000000  .0000000   .121501  .071173   .029831  .028301
+    1932.65   .071800  .021400   .178200  .016200   .0000000  .0000000   .176200  .064800   .027600  .034400
+    1932.70   .070092  .031963   .209808  .019638   .0000000  .0000000   .157431  .046568  -.018677  .039795
+    1932.75   .087369  .026903   .059518  .018128   .0000000  .0000000   .041519  .042163   .044435  .039343
+    1932.80   .122666  .029306   .122560  .023321   .0000000  .0000000   .119499  .083814   .019577  .054465
+    1932.85   .085699  .034806   .068358  .021347   .0000000  .0000000   .049531  .108948  -.015296  .041625
+    1932.90   .094388  .040534   .033874  .026315   .0000000  .0000000  -.108647  .114058  -.060994  .031474
+    1932.95   .097006  .050740   .030486  .029273   .0000000  .0000000   .482296  .145305   .000628  .028292
+    1933.00  -.073655  .027436  -.011865  .031985   .0000000  .0000000   .162502  .157095  -.020578  .021128
+    1933.05  -.058596  .051280   .014472  .053169   .0000000  .0000000   .223380  .102794   .066612  .048012
+    1933.10  -.188030  .022039  -.012586  .033302   .0000000  .0000000   .250070  .092253   .044862  .039280
+    1933.15  -.173476  .033322   .013624  .023011   .0000000  .0000000   .194918  .072032   .048475  .039047
+    1933.20  -.221474  .041821   .048831  .041000   .0000000  .0000000   .119403  .071452  -.081642  .051296
+    1933.25  -.211408  .022399   .107715  .020713   .0000000  .0000000  -.020870  .044128  -.019603  .039541
+    1933.30  -.186714  .030754   .112536  .021771   .0000000  .0000000  -.002865  .075552   .026320  .049219
+    1933.35  -.179116  .026288   .019117  .087056   .0000000  .0000000  -.201524  .142281   .035768  .052919
+    1933.40  -.132931  .016888   .166511  .016395   .0000000  .0000000   .060002  .072293  -.004573  .020527
+    1933.45  -.129933  .016924   .222792  .014909   .0000000  .0000000   .286473  .091703  -.005444  .011969
+    1933.50  -.065800  .025900   .247600  .020300   .0000000  .0000000   .311900  .120900  -.017000  .016600
+    1933.55  -.061257  .021807   .170077  .017113   .0000000  .0000000   .039399  .081939  -.018902  .022170
+    1933.60  -.092890  .018754   .168864  .018460   .0000000  .0000000   .113285  .056890   .045761  .023309
+    1933.65  -.011311  .017613   .114462  .013330   .0000000  .0000000   .064859  .048461   .045019  .025788
+    1933.70  -.041560  .030493   .155440  .028708   .0000000  .0000000   .049236  .053531  -.015828  .042651
+    1933.75  -.007268  .017906   .135250  .018020   .0000000  .0000000  -.007973  .034135  -.000158  .033869
+    1933.80  -.048100  .019500   .064100  .017400   .0000000  .0000000   .017600  .058100  -.063400  .038000
+    1933.85  -.030166  .025628   .051458  .019159   .0000000  .0000000   .034753  .089541   .032965  .037829
+    1933.90  -.059454  .029041   .050490  .020860   .0000000  .0000000  -.164756  .096039   .047298  .032548
+    1933.95  -.000334  .034076  -.055485  .023257   .0000000  .0000000   .230726  .104399   .016229  .025451
+    1934.00  -.047994  .028916   .160539  .032204   .0000000  .0000000   .065271  .168737  -.021590  .027670
+    1934.05  -.128330  .116172   .132957  .078644   .0000000  .0000000  -.139608  .119120  -.007947  .065655
+    1934.10  -.104712  .044574  -.032479  .045279   .0000000  .0000000   .189751  .182927  -.016887  .044388
+    1934.15  -.123185  .034839   .026628  .023806   .0000000  .0000000  -.001909  .080452   .042261  .036319
+    1934.20  -.156884  .022421   .046650  .014031   .0000000  .0000000  -.077774  .035064  -.053603  .025990
+    1934.25  -.146878  .022878   .039678  .017834   .0000000  .0000000   .004010  .039645   .032796  .035823
+    1934.30  -.212952  .024627   .066280  .025561   .0000000  .0000000   .004615  .064923  -.011894  .041422
+    1934.35  -.109680  .021540   .080647  .019629   .0000000  .0000000  -.072650  .066917   .003651  .024807
+    1934.40  -.064907  .045330   .218438  .045155   .0000000  .0000000  -.371262  .146182   .036964  .043452
+    1934.45  -.115818  .023038   .182058  .018394   .0000000  .0000000   .101136  .124249  -.027130  .015075
+    1934.50  -.093600  .024600   .146400  .021300   .0000000  .0000000  -.052800  .123400  -.006600  .016700
+    1934.55  -.026049  .035938   .206821  .025948   .0000000  .0000000   .146997  .091495  -.058792  .031002
+    1934.60  -.036375  .029411   .197949  .023037   .0000000  .0000000   .099724  .078245  -.046107  .031760
+    1934.65  -.018200  .015900   .130900  .014700   .0000000  .0000000   .117400  .055500   .027800  .028700
+    1934.70   .003251  .029618   .176660  .036611   .0000000  .0000000   .038319  .058136   .000146  .038912
+    1934.75  -.019469  .019153   .192448  .019467   .0000000  .0000000   .035354  .039079  -.123725  .034856
+    1934.80   .016400  .020800   .142200  .015600   .0000000  .0000000   .110800  .041500  -.044200  .026500
+    1934.85   .017784  .039544   .028679  .035506   .0000000  .0000000   .034240  .080020  -.051485  .039956
+    1934.90  -.064537  .023501   .102035  .022977   .0000000  .0000000  -.032496  .110469   .037326  .032922
+    1934.95  -.095694  .072841   .072497  .043608   .0000000  .0000000   .066833  .100561   .004247  .038628
+    1935.00  -.053671  .027453   .063917  .025431   .0000000  .0000000   .530155  .161149  -.037620  .019547
+    1935.05  -.169016  .044842   .020530  .061196   .0000000  .0000000   .000152  .139986   .016457  .054370
+    1935.10   .005900  .041800   .019800  .023600   .0000000  .0000000   .105800  .093200  -.024400  .029100
+    1935.15  -.121309  .039625   .043629  .025194   .0000000  .0000000   .154113  .083605   .027210  .035617
+    1935.20  -.067132  .058136   .059209  .032996   .0000000  .0000000   .070527  .094074   .016045  .067279
+    1935.25  -.065954  .020239   .062433  .016524   .0000000  .0000000   .020752  .038651   .017978  .031286
+    1935.30  -.066468  .018206   .107258  .015299   .0000000  .0000000   .147676  .050194  -.030079  .029955
+    1935.35  -.021635  .032644   .104029  .022829   .0000000  .0000000   .127678  .090082  -.013917  .034060
+    1935.40  -.033804  .023187   .063816  .014742   .0000000  .0000000   .084520  .068957  -.018098  .020813
+    1935.45  -.056658  .021884   .141422  .022076   .0000000  .0000000   .067394  .119437  -.035622  .016660
+    1935.50  -.052090  .023446   .108980  .020627   .0000000  .0000000   .081002  .098951  -.009813  .017371
+    1935.55  -.177790  .028614  -.002912  .021799   .0000000  .0000000   .175896  .084399   .095563  .025291
+    1935.60  -.039043  .021399   .085087  .019792   .0000000  .0000000   .042098  .070193  -.024099  .027889
+    1935.65  -.133855  .029242   .062467  .018438   .0000000  .0000000   .015839  .059663  -.000203  .031181
+    1935.70  -.005002  .024380   .093252  .014874   .0000000  .0000000   .086950  .036097  -.071649  .026762
+    1935.75  -.055390  .044761   .041687  .032838   .0000000  .0000000   .035882  .084773   .030563  .038356
+    1935.80  -.109561  .032418   .035449  .020733   .0000000  .0000000   .044617  .062241   .051621  .032317
+    1935.85  -.173680  .063585   .091852  .041729   .0000000  .0000000   .191925  .110010  -.010510  .050761
+    1935.90  -.136077  .022508   .056437  .019658   .0000000  .0000000   .189125  .083693  -.032856  .027713
+    1935.95  -.015578  .049221   .047611  .030480   .0000000  .0000000   .212536  .151736  -.092986  .028781
+    1936.00   .023251  .075588   .090679  .029556   .0000000  .0000000  -.002397  .137610  -.040276  .034707
+    1936.05  -.146877  .035923   .108152  .031524   .0000000  .0000000  -.182166  .136466  -.078906  .033638
+    1936.10  -.047500  .045100   .098500  .029300   .0000000  .0000000   .059100  .095300  -.064600  .041600
+    1936.15  -.125964  .044336   .209295  .047551   .0000000  .0000000  -.013101  .148607   .038221  .034734
+    1936.20  -.159573  .031869   .182595  .024035   .0000000  .0000000   .086452  .052309   .002678  .050131
+    1936.25  -.106700  .021800   .188100  .024300   .0000000  .0000000   .083400  .045200   .004700  .036900
+    1936.30  -.080483  .037462   .175745  .023124   .0000000  .0000000   .182437  .068031  -.021893  .040778
+    1936.35  -.093917  .025288   .159713  .018555   .0000000  .0000000   .254458  .063727   .016423  .025453
+    1936.40  -.037416  .027242   .107419  .017531   .0000000  .0000000   .164477  .088120  -.023220  .024444
+    1936.45   .060450  .027773   .167046  .014396   .0000000  .0000000   .176408  .107607  -.063856  .013305
+    1936.50  -.013617  .028925   .143961  .025305   .0000000  .0000000   .236894  .130004   .035176  .020557
+    1936.55  -.000451  .017115   .115066  .017856   .0000000  .0000000   .122627  .081777  -.058150  .020610
+    1936.60  -.004315  .025184   .072471  .020889   .0000000  .0000000   .038232  .081985  -.058168  .031985
+    1936.65  -.043164  .028713   .033288  .021693   .0000000  .0000000   .094645  .064962   .018201  .035522
+    1936.70  -.018826  .029628   .032928  .023917   .0000000  .0000000   .155039  .054656  -.025877  .033828
+    1936.75  -.074570  .021101  -.007560  .019749   .0000000  .0000000   .080734  .039849  -.029856  .040096
+    1936.80  -.106024  .027836  -.004717  .023634   .0000000  .0000000  -.100276  .067963   .123125  .041897
+    1936.85  -.100771  .034341  -.011306  .033811   .0000000  .0000000   .091602  .124978   .002425  .049008
+    1936.90  -.188334  .046712   .029368  .036290   .0000000  .0000000   .144275  .148300   .051888  .054622
+    1936.95  -.173300  .034400  -.047400  .033100   .0000000  .0000000   .208300  .134200  -.087100  .029800
+    1937.00  -.140145  .030719   .085152  .035730   .0000000  .0000000  -.028907  .188436   .019138  .025183
+    1937.05  -.193481  .028051   .094688  .027126   .0000000  .0000000   .064059  .116828  -.054443  .026500
+    1937.10  -.158811  .048222   .074182  .024328   .0000000  .0000000  -.005732  .118084  -.057897  .037315
+    1937.15  -.245489  .037300   .179964  .024899   .0000000  .0000000  -.082441  .095065   .015064  .035935
+    1937.20  -.176313  .024347   .131564  .021480   .0000000  .0000000  -.011162  .047116   .007665  .035588
+    1937.25  -.121499  .027993   .208926  .020364   .0000000  .0000000   .055374  .052535   .003693  .031433
+    1937.30  -.116696  .019325   .187814  .017441   .0000000  .0000000   .031160  .051109   .025088  .031903
+    1937.35  -.040102  .019283   .222305  .018618   .0000000  .0000000  -.006141  .067700   .013902  .025889
+    1937.40  -.088573  .032312   .195445  .025157   .0000000  .0000000   .036933  .108610   .035897  .031983
+    1937.45   .035320  .032900   .209660  .037569   .0000000  .0000000   .286079  .120190  -.061321  .033034
+    1937.50   .073386  .027070   .231432  .034139   .0000000  .0000000   .280629  .125639   .000056  .019985
+    1937.55   .069100  .021000   .137800  .021600   .0000000  .0000000   .183700  .097800  -.010900  .022900
+    1937.60   .090711  .017787   .121711  .017775   .0000000  .0000000   .155485  .063704  -.047427  .028153
+    1937.65   .056677  .025156   .054539  .029534   .0000000  .0000000   .085924  .077674   .002358  .044134
+    1937.70   .047138  .020415   .041472  .019505   .0000000  .0000000   .109899  .040740  -.021845  .033649
+    1937.75  -.015251  .029698  -.021479  .026262   .0000000  .0000000   .240537  .052747   .030065  .054171
+    1937.80  -.008680  .024570  -.029054  .021966   .0000000  .0000000  -.011361  .071538   .032695  .042628
+    1937.85  -.107027  .052301  -.071074  .029844   .0000000  .0000000  -.010644  .121826  -.009140  .052614
+    1937.90  -.105060  .033224  -.016349  .021708   .0000000  .0000000   .000897  .097421   .024894  .028423
+    1937.95  -.217410  .042351  -.006067  .021722   .0000000  .0000000   .163760  .106926  -.045437  .024199
+    1938.00  -.204894  .067766   .021733  .040401   .0000000  .0000000  -.026212  .134573   .019500  .049776
+    1938.05  -.303156  .051189   .013057  .057781   .0000000  .0000000   .455009  .256240   .061652  .060953
+    1938.10  -.147755  .030762   .071361  .022609   .0000000  .0000000   .103134  .085082   .057226  .032009
+    1938.15  -.221442  .020302   .102093  .022050   .0000000  .0000000   .012967  .059919  -.070502  .029053
+    1938.20  -.226757  .021464   .130364  .026643   .0000000  .0000000   .001658  .043406  -.006758  .051291
+    1938.25  -.171954  .020038   .165869  .024298   .0000000  .0000000  -.015881  .041937   .027639  .048387
+    1938.30  -.138432  .027223   .225318  .026550   .0000000  .0000000   .005010  .071389   .049609  .036109
+    1938.35  -.116911  .032602   .287363  .030442   .0000000  .0000000   .023008  .087321  -.083163  .045743
+    1938.40  -.046436  .022237   .227821  .021177   .0000000  .0000000   .113723  .092236  -.024826  .026919
+    1938.45  -.080914  .049543   .286589  .026940   .0000000  .0000000   .413938  .153469  -.041876  .030065
+    1938.50   .111257  .046529   .295958  .032882   .0000000  .0000000   .105264  .144089  -.049291  .032748
+    1938.55   .109700  .023800   .236000  .032100   .0000000  .0000000   .188500  .136200   .070100  .035900
+    1938.60   .174186  .027008   .248989  .025076   .0000000  .0000000  -.058945  .089978   .001625  .036058
+    1938.65   .172035  .024829   .065415  .017812   .0000000  .0000000  -.020619  .065169  -.092811  .034848
+    1938.70   .075919  .028103   .117887  .029399   .0000000  .0000000   .080439  .054649   .078144  .057554
+    1938.75   .167309  .024278   .023607  .020665   .0000000  .0000000   .012905  .044571   .011731  .039332
+    1938.80   .026111  .026090  -.003055  .018168   .0000000  .0000000  -.050130  .064328   .032800  .035397
+    1938.85  -.037869  .024814  -.061382  .026313   .0000000  .0000000  -.139398  .101758   .068126  .036773
+    1938.90  -.052264  .036085  -.072478  .027939   .0000000  .0000000   .227134  .081018  -.049973  .031785
+    1938.95  -.134539  .074845   .030821  .034255   .0000000  .0000000   .203647  .139364   .006955  .034675
+    1939.00  -.301769  .052095   .106206  .041046   .0000000  .0000000   .097782  .134445   .126933  .040015
+    1939.05  -.116858  .031761  -.001150  .027067   .0000000  .0000000   .069219  .114100  -.043972  .026676
+    1939.10  -.213896  .052347   .044637  .029951   .0000000  .0000000   .100499  .115589  -.034090  .040064
+    1939.15  -.161300  .028000   .101800  .022400   .0000000  .0000000   .026600  .079200   .033400  .032600
+    1939.20  -.181054  .026344   .143917  .017714   .0000000  .0000000   .108271  .043838  -.012560  .032033
+    1939.25  -.192526  .031348   .184454  .017928   .0000000  .0000000   .063502  .044160  -.075863  .035745
+    1939.30  -.119371  .031056   .187626  .017082   .0000000  .0000000   .060381  .054162  -.000618  .035005
+    1939.35  -.079336  .031918   .224723  .019528   .0000000  .0000000   .100471  .072304  -.010194  .028022
+    1939.40  -.087263  .033604   .216206  .018087   .0000000  .0000000   .029625  .098189  -.024268  .026120
+    1939.45  -.044976  .026415   .211200  .017721   .0000000  .0000000   .127332  .116805   .012013  .015370
+    1939.50   .023086  .029357   .214921  .024272   .0000000  .0000000   .143133  .143921  -.008201  .020892
+    1939.55   .072500  .021500   .233900  .028500   .0000000  .0000000   .200600  .111800   .034800  .028200
+    1939.60   .118411  .034504   .224190  .022418   .0000000  .0000000   .287927  .083553   .059985  .030415
+    1939.65   .146657  .026608   .195121  .022452   .0000000  .0000000   .056615  .078763   .049268  .045366
+    1939.70   .095496  .035816   .180767  .028811   .0000000  .0000000  -.041206  .062114   .054138  .050547
+    1939.75   .089672  .036394   .076885  .031936   .0000000  .0000000   .103650  .054497   .044565  .059625
+    1939.80   .069378  .025942   .045780  .020362   .0000000  .0000000   .014679  .068846   .001210  .038438
+    1939.85   .042361  .022277   .018571  .019603   .0000000  .0000000   .116647  .076374  -.030570  .028495
+    1939.90   .005289  .024937   .055015  .021233   .0000000  .0000000   .263913  .091441  -.088895  .026766
+    1939.95  -.058344  .061163   .043141  .028428   .0000000  .0000000  -.027276  .144601  -.054993  .033244
+    1940.00   .027411  .077275  -.055656  .040309   .0000000  .0000000   .280673  .124019  -.035461  .049996
+    1940.05  -.148565  .048427  -.007660  .029624   .0000000  .0000000   .036827  .132048  -.063083  .034740
+    1940.10  -.249170  .031848  -.018138  .026098   .0000000  .0000000   .102082  .088814  -.092441  .032061
+    1940.15  -.210741  .037178   .047170  .021871   .0000000  .0000000   .010781  .074147  -.079933  .032336
+    1940.20  -.226589  .024698   .078515  .018759   .0000000  .0000000   .085455  .046548  -.040264  .028160
+    1940.25  -.290100  .050400   .142200  .033500   .0000000  .0000000   .134700  .071900   .042900  .053100
+    1940.30  -.223492  .021873   .078291  .021431   .0000000  .0000000   .027242  .064239  -.034537  .032075
+    1940.35  -.135189  .033253   .151824  .022526   .0000000  .0000000   .232845  .087503  -.051245  .033133
+    1940.40  -.175712  .026622   .271518  .024079   .0000000  .0000000   .114010  .103141   .010283  .029308
+    1940.45  -.063144  .024189   .235042  .021942   .0000000  .0000000   .478962  .120381  -.016662  .017980
+    1940.50  -.029661  .027901   .260179  .025258   .0000000  .0000000   .238529  .128248   .022554  .021672
+    1940.55   .019816  .031393   .250937  .020228   .0000000  .0000000   .354490  .091541   .059761  .024911
+    1940.60   .162656  .033993   .241704  .019243   .0000000  .0000000   .174897  .087947   .029852  .031332
+    1940.65   .098439  .030861   .147379  .026781   .0000000  .0000000   .240708  .082500   .081456  .045187
+    1940.70   .065800  .040900   .175200  .018000   .0000000  .0000000  -.035700  .042700   .085200  .040300
+    1940.75   .125165  .021480   .119102  .016993   .0000000  .0000000   .016575  .035997   .066860  .036856
+    1940.80   .070275  .044378   .085726  .026840   .0000000  .0000000  -.112729  .075342   .022843  .052931
+    1940.85   .053300  .039909   .043221  .022772   .0000000  .0000000   .036946  .099058   .016973  .049873
+    1940.90   .004300  .047102  -.026993  .022398   .0000000  .0000000   .087581  .123251  -.052575  .037425
+    1940.95  -.049213  .047073   .034742  .025669   .0000000  .0000000   .139401  .127260  -.014801  .025328
+    1941.00   .033843  .046840   .015616  .040643   .0000000  .0000000  -.091313  .169246  -.034822  .030156
+    1941.05   .033262  .065113  -.034927  .049589   .0000000  .0000000   .245435  .199929  -.094435  .040091
+    1941.10  -.043717  .034446   .028212  .023694   .0000000  .0000000  -.081372  .089249  -.088631  .024387
+    1941.15  -.000032  .040323   .034220  .023892   .0000000  .0000000  -.150594  .086706  -.096703  .041061
+    1941.20   .008262  .043291   .025372  .030470   .0000000  .0000000   .248876  .079912   .058207  .043168
+    1941.25  -.118927  .034295   .084292  .022975   .0000000  .0000000   .111681  .054924   .024383  .042665
+    1941.30  -.183857  .030309   .100975  .021078   .0000000  .0000000   .107066  .059447   .000181  .036107
+    1941.35  -.159123  .039022   .204194  .031221   .0000000  .0000000   .071615  .081202  -.068183  .040363
+    1941.40  -.088480  .032885   .133512  .017317   .0000000  .0000000   .070452  .086127   .044016  .024793
+    1941.45  -.092443  .034238   .191571  .017249   .0000000  .0000000   .117795  .113137  -.001774  .015851
+    1941.50  -.062260  .034985   .120954  .025898   .0000000  .0000000   .154337  .164516   .060142  .021607
+    1941.55  -.073372  .034124   .253085  .025499   .0000000  .0000000   .175314  .089621  -.032968  .031504
+    1941.60   .012678  .047325   .219220  .028237   .0000000  .0000000   .377508  .098725   .051572  .037240
+    1941.65  -.041165  .030633   .167041  .020566   .0000000  .0000000   .226991  .070394   .075100  .036381
+    1941.70  -.086224  .042656   .093311  .024666   .0000000  .0000000  -.065811  .076606   .024269  .038115
+    1941.75   .001683  .035139   .150415  .033889   .0000000  .0000000   .066674  .066441  -.051084  .071325
+    1941.80   .068909  .027624   .186888  .030051   .0000000  .0000000  -.062154  .087481   .111358  .049014
+    1941.85  -.028851  .042976   .102187  .034278   .0000000  .0000000  -.062261  .122940  -.000004  .047542
+    1941.90  -.020115  .032129   .119301  .036007   .0000000  .0000000   .083441  .141823   .000045  .040889
+    1941.95   .145863  .062788   .081312  .039067   .0000000  .0000000  -.007050  .135487  -.107320  .035369
+    1942.00  -.029700  .035000   .145400  .036400   .0000000  .0000000  -.023400  .145000   .047000  .030200
+    1942.05  -.086609  .043305   .107756  .043155   .0000000  .0000000   .130386  .156484  -.001875  .044788
+    1942.10   .034025  .099193   .019905  .065306   .0000000  .0000000   .143195  .150532  -.075786  .050544
+    1942.15  -.065983  .072737   .120029  .039191   .0000000  .0000000  -.010695  .103966  -.056494  .047800
+    1942.20  -.026845  .054073   .031907  .044318   .0000000  .0000000   .058500  .099629  -.057425  .067689
+    1942.25  -.066569  .033252   .031627  .021536   .0000000  .0000000   .235923  .052015   .010261  .035920
+    1942.30  -.008392  .035500   .098245  .025834   .0000000  .0000000  -.010612  .066911   .041308  .034586
+    1942.35  -.012154  .043636   .089180  .020492   .0000000  .0000000   .145144  .083706   .067848  .034201
+    1942.40  -.068264  .024748   .122550  .016674   .0000000  .0000000   .096755  .072277   .030454  .019928
+    1942.45  -.064544  .021997   .139302  .017836   .0000000  .0000000   .314458  .119425  -.015997  .014334
+    1942.50  -.032732  .023724   .122930  .023171   .0000000  .0000000   .154348  .125251  -.016508  .019523
+    1942.55  -.007371  .025769   .174390  .020405   .0000000  .0000000   .184138  .089648  -.053147  .023645
+    1942.60  -.089026  .027264   .117492  .027789   .0000000  .0000000   .171854  .105929   .063537  .043272
+    1942.65  -.075180  .025712   .095255  .017548   .0000000  .0000000   .079381  .066570   .051325  .035699
+    1942.70  -.036158  .026547   .121535  .020117   .0000000  .0000000   .056200  .051173   .051601  .032470
+    1942.75  -.075817  .024570   .122674  .020107   .0000000  .0000000   .044556  .041433   .073768  .042854
+    1942.80  -.089165  .025839   .085346  .023589   .0000000  .0000000  -.085206  .072822   .050192  .047934
+    1942.85  -.152931  .030206   .083401  .026797   .0000000  .0000000   .064794  .101785  -.119249  .040379
+    1942.90  -.103187  .039909   .103300  .029302   .0000000  .0000000  -.051792  .135763  -.004428  .042467
+    1942.95  -.039915  .046498   .183470  .030930   .0000000  .0000000  -.154219  .135897   .022603  .031303
+    1943.00  -.090405  .044945   .234551  .028097   .0000000  .0000000   .031918  .130831   .002072  .024383
+    1943.05  -.093652  .033425   .180949  .023112   .0000000  .0000000   .307004  .136349   .039921  .032842
+    1943.10  -.061971  .041347   .090993  .035224   .0000000  .0000000   .125377  .126839  -.037571  .041480
+    1943.15  -.080573  .025864   .145031  .020718   .0000000  .0000000   .010542  .065253  -.060381  .032471
+    1943.20  -.051329  .044348   .138129  .024337   .0000000  .0000000   .114304  .060216  -.071526  .042656
+    1943.25   .001923  .020443   .184841  .014117   .0000000  .0000000   .080154  .033484  -.028209  .025002
+    1943.30  -.019745  .047888   .090795  .029239   .0000000  .0000000   .286805  .085349  -.065939  .046350
+    1943.35   .035917  .046036   .105605  .027853   .0000000  .0000000  -.071916  .116350   .011802  .039509
+    1943.40   .078661  .029069   .117427  .015875   .0000000  .0000000  -.017176  .107309   .031813  .027567
+    1943.45   .033384  .028581   .108682  .016851   .0000000  .0000000   .211785  .118690  -.001950  .015077
+    1943.50   .105491  .034946   .061063  .017106   .0000000  .0000000   .162842  .133454   .015217  .015918
+    1943.55  -.047847  .103549  -.001805  .032245   .0000000  .0000000   .078989  .171419  -.055324  .049530
+    1943.60  -.036338  .029169   .050187  .016889   .0000000  .0000000   .270467  .067593   .080426  .035071
+    1943.65   .022793  .032813   .034134  .016076   .0000000  .0000000   .131087  .060479   .025598  .033468
+    1943.70   .070899  .035442   .024180  .017600   .0000000  .0000000  -.005569  .045031   .007882  .031512
+    1943.75   .004438  .048663   .011224  .022566   .0000000  .0000000  -.051189  .056973   .018685  .052347
+    1943.80  -.122806  .048296   .001491  .031982   .0000000  .0000000  -.112682  .090687   .019698  .065615
+    1943.85  -.144327  .059258   .028604  .023563   .0000000  .0000000  -.227848  .107147   .001673  .039855
+    1943.90  -.120953  .040528   .074676  .019274   .0000000  .0000000  -.181415  .090120   .034939  .027690
+    1943.95  -.253705  .054648   .095567  .033760   .0000000  .0000000  -.038519  .105800   .043310  .030581
+    1944.00  -.188535  .056504   .058157  .026927   .0000000  .0000000   .011710  .117984   .050033  .025258
+    1944.05  -.213170  .050255   .082626  .030045   .0000000  .0000000  -.043329  .159056   .015731  .038148
+    1944.10  -.133388  .045661   .103606  .024095   .0000000  .0000000   .229543  .103942   .175796  .038943
+    1944.15  -.243714  .051614   .045296  .028302   .0000000  .0000000   .177000  .105652   .113149  .053398
+    1944.20  -.280687  .055970   .123924  .030138   .0000000  .0000000  -.188279  .079808   .142494  .052159
+    1944.25  -.099857  .044604   .132270  .024510   .0000000  .0000000  -.025648  .058133   .069120  .050952
+    1944.30  -.102089  .049295   .098582  .026644   .0000000  .0000000  -.105879  .081537  -.005922  .046044
+    1944.35   .034173  .049135   .068935  .022447   .0000000  .0000000  -.023121  .090661  -.065807  .032003
+    1944.40   .052475  .031410   .180058  .020077   .0000000  .0000000  -.054212  .110366  -.009055  .033903
+    1944.45   .204300  .039900   .170300  .019200   .0000000  .0000000  -.041200  .149500  -.017000  .018300
+    1944.50   .174930  .034829   .065429  .018224   .0000000  .0000000   .283677  .123221   .036322  .017098
+    1944.55   .236527  .044383   .061957  .020707   .0000000  .0000000   .188768  .113215   .012042  .027235
+    1944.60   .208915  .031465  -.039275  .017646   .0000000  .0000000  -.005744  .077108   .019100  .031084
+    1944.65   .171265  .036525  -.034213  .019816   .0000000  .0000000  -.062495  .073567  -.102429  .040486
+    1944.70   .158830  .051306  -.127515  .024358   .0000000  .0000000  -.038891  .054838   .097809  .058068
+    1944.75  -.041184  .050911  -.081574  .028622   .0000000  .0000000   .155086  .088314  -.062691  .057585
+    1944.80  -.054199  .033951  -.141045  .019266   .0000000  .0000000  -.094099  .062789   .080547  .037140
+    1944.85  -.122864  .049362  -.138640  .027245   .0000000  .0000000  -.050906  .092832  -.001887  .045221
+    1944.90  -.173922  .041193  -.079180  .024982   .0000000  .0000000  -.034262  .124491   .051550  .040084
+    1944.95  -.266129  .053704  -.029277  .022854   .0000000  .0000000   .131877  .110016  -.008643  .020029
+    1945.00  -.117527  .098174   .046084  .041335   .0000000  .0000000   .087723  .217282   .013102  .036075
+    1945.05  -.172366  .049912   .076642  .030967   .0000000  .0000000   .267719  .179675  -.006805  .044952
+    1945.10  -.218168  .050084   .119657  .026439   .0000000  .0000000   .154283  .108024   .033489  .042789
+    1945.15  -.269380  .049677   .084980  .028834   .0000000  .0000000   .082804  .098489   .000447  .045397
+    1945.20  -.105476  .046714   .185324  .018697   .0000000  .0000000  -.039045  .050584   .029208  .036366
+    1945.25  -.252149  .085268   .252063  .049965   .0000000  .0000000  -.159686  .129068   .042012  .044566
+    1945.30  -.119100  .039700   .283700  .019900   .0000000  .0000000   .073600  .061300  -.050300  .036300
+    1945.35  -.098829  .042741   .296275  .018875   .0000000  .0000000   .039859  .081486   .017185  .031128
+    1945.40   .041205  .032181   .299528  .018137   .0000000  .0000000   .064232  .099406   .040381  .028527
+    1945.45   .070100  .036700   .328900  .019100   .0000000  .0000000   .396100  .134000  -.033000  .017800
+    1945.50   .161286  .035364   .243704  .019020   .0000000  .0000000   .203888  .143130   .028038  .016730
+    1945.55   .111735  .045860   .157891  .023770   .0000000  .0000000   .181839  .111435   .048925  .027434
+    1945.60   .279930  .033860   .129955  .019470   .0000000  .0000000  -.146537  .081354  -.012702  .036212
+    1945.65   .210791  .036056   .086270  .018333   .0000000  .0000000  -.033172  .070699   .023239  .035846
+    1945.70   .167958  .069541  -.034212  .029604   .0000000  .0000000  -.117022  .078929   .033399  .058465
+    1945.75   .387534  .081155  -.060857  .039921   .0000000  .0000000  -.163738  .085766   .026932  .080324
+    1945.80   .203824  .038877  -.175689  .021959   .0000000  .0000000  -.196190  .063085  -.030286  .035599
+    1945.85  -.009562  .068992  -.200723  .050136   .0000000  .0000000  -.100653  .130011  -.024714  .045668
+    1945.90   .082043  .098983  -.254725  .060995   .0000000  .0000000  -.152230  .134994  -.064263  .053177
+    1945.95  -.167775  .079020   .011828  .069978   .0000000  .0000000   .177563  .114048   .221283  .066533
+    1946.00  -.105894  .056205  -.143593  .034311   .0000000  .0000000   .199964  .143624  -.000929  .034314
+    1946.05  -.238800  .088800  -.143600  .064100   .0000000  .0000000   .131900  .121100  -.025600  .062000
+    1946.10  -.161621  .048683  -.086524  .026893   .0000000  .0000000   .231302  .121796  -.045219  .045241
+    1946.15  -.292120  .041826  -.032154  .024745   .0000000  .0000000  -.072356  .097341  -.079506  .047899
+    1946.20  -.369539  .095096   .005399  .055328   .0000000  .0000000   .052740  .148879  -.113594  .053330
+    1946.25  -.243748  .049762   .106804  .017998   .0000000  .0000000   .037891  .052028  -.000958  .033355
+    1946.30  -.260700  .043900   .154700  .024700   .0000000  .0000000   .146500  .060100   .015300  .028900
+    1946.35  -.227834  .043383   .263600  .027335   .0000000  .0000000   .054514  .099861  -.014064  .035241
+    1946.40  -.105890  .029765   .285597  .015086   .0000000  .0000000   .209512  .066402  -.021484  .018107
+    1946.45  -.219288  .045405   .379684  .028341   .0000000  .0000000   .452054  .158962  -.033761  .026542
+    1946.50  -.063112  .023701   .324947  .023954   .0000000  .0000000   .329269  .135513  -.005721  .018983
+    1946.55   .056826  .021226   .295929  .018350   .0000000  .0000000  -.002104  .085088  -.017459  .020736
+    1946.60   .082673  .031070   .302031  .017679   .0000000  .0000000   .128310  .076546   .041444  .030472
+    1946.65   .134672  .028275   .242569  .018267   .0000000  .0000000  -.028935  .065586  -.006894  .035842
+    1946.70   .188918  .042253   .179805  .024653   .0000000  .0000000  -.085803  .069558   .082797  .058786
+    1946.75   .230337  .027345   .153795  .018577   .0000000  .0000000  -.135090  .042785   .070652  .040411
+    1946.80   .230653  .029557   .126700  .021510   .0000000  .0000000  -.081513  .070902   .011646  .042602
+    1946.85   .324003  .050001  -.014854  .031141   .0000000  .0000000  -.035261  .123641   .093395  .055954
+    1946.90   .083430  .029570  -.030662  .030935   .0000000  .0000000   .022357  .123080  -.104915  .035679
+    1946.95   .071411  .037775  -.089697  .035099   .0000000  .0000000   .207114  .170228  -.059531  .032194
+    1947.00   .033741  .037012  -.095143  .035317   .0000000  .0000000   .042302  .138704  -.001326  .028954
+    1947.05  -.003804  .040478  -.062704  .036415   .0000000  .0000000   .057712  .144446   .028471  .038643
+    1947.10  -.105253  .032813  -.159077  .026207   .0000000  .0000000  -.130260  .128557  -.006703  .040601
+    1947.15  -.140851  .044726  -.129656  .031276   .0000000  .0000000   .151813  .087397  -.009561  .037416
+    1947.20  -.217901  .022739  -.081483  .019933   .0000000  .0000000   .194724  .038687  -.082918  .045835
+    1947.25  -.204585  .031621  -.117897  .024841   .0000000  .0000000   .088210  .050211   .043359  .050085
+    1947.30  -.260050  .024410   .044441  .020965   .0000000  .0000000   .143500  .059651  -.004458  .031726
+    1947.35  -.358491  .051825   .174358  .038713   .0000000  .0000000  -.150770  .096426  -.043413  .039711
+    1947.40  -.202905  .022247   .152120  .016348   .0000000  .0000000   .142834  .078507  -.003972  .021997
+    1947.45  -.254945  .036398   .153196  .024495   .0000000  .0000000   .397632  .136809   .050744  .019703
+    1947.50  -.137802  .039259   .288460  .018339   .0000000  .0000000   .293369  .123364  -.003044  .017321
+    1947.55  -.094877  .027323   .287150  .018613   .0000000  .0000000   .368362  .095231   .027479  .024367
+    1947.60   .023089  .026612   .335301  .022225   .0000000  .0000000   .150553  .077407   .061567  .034232
+    1947.65   .060945  .023461   .357002  .018134   .0000000  .0000000   .097432  .066202   .048073  .035423
+    1947.70   .113078  .020025   .313539  .016869   .0000000  .0000000  -.070258  .035820   .101461  .024940
+    1947.75   .120020  .028834   .273848  .017090   .0000000  .0000000  -.054866  .041783   .080301  .036715
+    1947.80   .175924  .021367   .294753  .021098   .0000000  .0000000  -.096099  .059629  -.025532  .033285
+    1947.85   .206992  .026904   .222458  .026059   .0000000  .0000000  -.169918  .093163   .019324  .036768
+    1947.90   .140099  .156501   .174866  .060855   .0000000  .0000000   .308350  .258739  -.060133  .111353
+    1947.95   .157648  .033763   .156590  .020766   .0000000  .0000000  -.193207  .100077  -.016713  .022002
+    1948.00   .225955  .060139   .143369  .036132   .0000000  .0000000  -.092621  .131439  -.043843  .036950
+    1948.05   .157129  .032190   .071959  .019313   .0000000  .0000000   .173710  .115614  -.018864  .031870
+    1948.10   .150055  .042780   .021583  .029804   .0000000  .0000000   .052631  .106024  -.078329  .042590
+    1948.15   .171585  .046287  -.019271  .026507   .0000000  .0000000   .106127  .104273   .019381  .050631
+    1948.20   .145400  .027900  -.041700  .017300   .0000000  .0000000   .061600  .043200  -.053500  .028000
+    1948.25   .147680  .042680  -.064178  .028623   .0000000  .0000000   .200452  .066728  -.018979  .038952
+    1948.30   .053983  .032261  -.075038  .023059   .0000000  .0000000   .129227  .065461  -.012305  .032841
+    1948.35  -.045837  .029177  -.072956  .018100   .0000000  .0000000   .188712  .075504   .014525  .029107
+    1948.40  -.091431  .027583  -.067958  .028678   .0000000  .0000000   .169771  .114458   .026487  .034330
+    1948.45  -.076060  .032359   .010010  .024096   .0000000  .0000000   .027309  .122213  -.001558  .022938
+    1948.50  -.106057  .029471   .014185  .023433   .0000000  .0000000   .331345  .133211   .006509  .020305
+    1948.55  -.097516  .035671   .015430  .022947   .0000000  .0000000   .126049  .105235  -.000300  .028138
+    1948.60  -.130026  .022269   .063326  .020675   .0000000  .0000000   .013796  .068888  -.005195  .026597
+    1948.65  -.136613  .027877   .110269  .016852   .0000000  .0000000   .035140  .061022   .025543  .033534
+    1948.70  -.236212  .036919   .081015  .020424   .0000000  .0000000  -.058798  .047650   .040632  .030948
+    1948.75  -.122022  .023509   .165015  .021825   .0000000  .0000000  -.061524  .043538   .001342  .044698
+    1948.80  -.082996  .027186   .181422  .022777   .0000000  .0000000  -.039394  .068076  -.033338  .040318
+    1948.85  -.102718  .027953   .194496  .030520   .0000000  .0000000  -.054000  .090691   .015084  .040774
+    1948.90  -.056364  .032781   .288203  .026870   .0000000  .0000000  -.055418  .127534   .046448  .038790
+    1948.95  -.060517  .032204   .337067  .027905   .0000000  .0000000   .111431  .081826   .008714  .026545
+    1949.00  -.038708  .049316   .272547  .039019   .0000000  .0000000   .165371  .196818   .017298  .033085
+    1949.05   .050766  .046642   .309560  .032725   .0000000  .0000000   .053360  .111299   .008811  .040210
+    1949.10  -.036412  .034106   .244629  .035866   .0000000  .0000000   .058169  .111719   .045433  .043521
+    1949.15   .092035  .026254   .269182  .028319   .0000000  .0000000   .047496  .079311  -.003430  .044089
+    1949.20   .117000  .030700   .151400  .021500   .0000000  .0000000   .121100  .057800  -.038500  .032400
+    1949.25   .167243  .019974   .119693  .019482   .0000000  .0000000   .029460  .038689   .002111  .032263
+    1949.30   .167879  .019742   .072692  .018970   .0000000  .0000000   .079695  .055856  -.065511  .031070
+    1949.35   .134722  .018528   .072089  .017990   .0000000  .0000000  -.005600  .064474  -.028475  .024032
+    1949.40   .145230  .020357   .001256  .019435   .0000000  .0000000  -.009693  .083433  -.012814  .022777
+    1949.45   .089326  .032677  -.093079  .026228   .0000000  .0000000   .145656  .151896  -.012762  .022385
+    1949.50   .154408  .024388  -.078539  .019318   .0000000  .0000000   .266006  .106446  -.037253  .018520
+    1949.55   .073064  .019811  -.060780  .019254   .0000000  .0000000   .142959  .090937  -.041160  .022053
+    1949.60   .043289  .020218  -.033614  .020112   .0000000  .0000000   .205821  .056832   .001724  .022392
+    1949.65  -.013262  .031902  -.122149  .021421   .0000000  .0000000   .142460  .071339   .053448  .035449
+    1949.70  -.164254  .018967  -.145566  .017345   .0000000  .0000000  -.015451  .034611   .066843  .027464
+    1949.75  -.163827  .021670  -.040926  .019401   .0000000  .0000000   .024694  .039400   .012926  .034357
+    1949.80  -.236686  .025116  -.074967  .021759   .0000000  .0000000  -.159054  .061893  -.000411  .033474
+    1949.85  -.237557  .024531   .040887  .019382   .0000000  .0000000  -.125444  .074771   .002484  .027168
+    1949.90  -.224455  .033530   .063927  .018810   .0000000  .0000000   .023909  .066713  -.057764  .022311
+    1949.95  -.290753  .033619   .157252  .026266   .0000000  .0000000   .122957  .121459   .002499  .023060
+    1950.00  -.220930  .031188   .287191  .027116   .0000000  .0000000   .096834  .072355   .057514  .028313
+    1950.05  -.222249  .025733   .248856  .019958   .0000000  .0000000   .104625  .074014  -.005680  .018913
+    1950.10  -.144425  .027227   .335072  .022605   .0000000  .0000000   .028561  .060323  -.026022  .026755
+    1950.15  -.061597  .027136   .365805  .021433   .0000000  .0000000   .024639  .059588  -.037465  .026662
+    1950.20   .023935  .031899   .338761  .020543   .0000000  .0000000   .059885  .052747   .066709  .027018
+    1950.25   .046105  .025279   .332438  .016279   .0000000  .0000000   .076271  .042306   .073850  .026697
+    1950.30   .127597  .020170   .297890  .015238   .0000000  .0000000   .056717  .042025  -.009493  .022675
+    1950.35   .258100  .018500   .317900  .015100   .0000000  .0000000  -.020400  .045700  -.037300  .017900
+    1950.40   .238376  .018584   .209297  .018693   .0000000  .0000000  -.076414  .079552   .026728  .021964
+    1950.45   .300481  .034000   .140257  .022063   .0000000  .0000000   .057815  .104537  -.019883  .022607
+    1950.50   .287564  .026024   .015981  .022256   .0000000  .0000000   .226741  .112107  -.052289  .020011
+    1950.55   .286892  .019714   .093660  .017169   .0000000  .0000000   .112008  .067624   .007534  .019107
+    1950.60   .270451  .020268  -.079337  .015524   .0000000  .0000000   .067069  .046463   .031933  .019940
+    1950.65   .258830  .028976  -.098373  .021452   .0000000  .0000000   .172046  .055504  -.006099  .028183
+    1950.70   .116078  .031592  -.227489  .022550   .0000000  .0000000   .003803  .057717   .008677  .031671
+    1950.75  -.010608  .020131  -.232154  .018407   .0000000  .0000000  -.004975  .037832   .033624  .028346
+    1950.80  -.087101  .027544  -.278552  .022565   .0000000  .0000000  -.063955  .066989  -.024757  .025075
+    1950.85  -.183732  .026532  -.306109  .019244   .0000000  .0000000  -.110177  .063411  -.030087  .026370
+    1950.90  -.306855  .023793  -.243256  .019914   .0000000  .0000000   .053527  .057910  -.056163  .022108
+    1950.95  -.321363  .042409  -.117027  .020603   .0000000  .0000000   .059400  .104950   .044587  .020723
+    1951.00  -.365022  .027994   .014938  .024338   .0000000  .0000000   .033574  .070227   .052409  .023757
+    1951.05  -.304225  .029966   .001015  .019948   .0000000  .0000000   .245382  .073638  -.006980  .023904
+    1951.10  -.369566  .026330   .099280  .020125   .0000000  .0000000   .016276  .066839  -.017562  .022936
+    1951.15  -.378609  .027648   .229045  .020079   .0000000  .0000000   .090817  .056998   .047240  .025224
+    1951.20  -.253183  .022810   .324399  .018429   .0000000  .0000000   .046964  .041849   .002459  .028010
+    1951.25  -.183608  .024150   .395804  .019034   .0000000  .0000000  -.037382  .044917   .057110  .031569
+    1951.30  -.113043  .023771   .407480  .014823   .0000000  .0000000   .062870  .045151  -.038716  .023368
+    1951.35   .025400  .022200   .357400  .017600   .0000000  .0000000   .124400  .055300   .024200  .022200
+    1951.40   .169217  .024409   .366203  .018242   .0000000  .0000000   .024260  .074441  -.045446  .022145
+    1951.45   .251730  .029607   .336817  .018573   .0000000  .0000000   .236504  .116554  -.033823  .017608
+    1951.50   .268771  .026562   .266485  .020994   .0000000  .0000000   .157566  .093858  -.016515  .020215
+    1951.55   .325834  .019822   .218909  .017563   .0000000  .0000000   .235511  .071342  -.004900  .019775
+    1951.60   .416116  .016950   .097318  .012584   .0000000  .0000000   .155524  .039133  -.041868  .017702
+    1951.65   .369300  .021400   .007200  .015000   .0000000  .0000000   .065100  .049500  -.037700  .025500
+    1951.70   .311161  .023969  -.128485  .015394   .0000000  .0000000   .039483  .040843   .057950  .022981
+    1951.75   .291955  .028273  -.216643  .022009   .0000000  .0000000   .089520  .052363   .037232  .033229
+    1951.80   .143989  .022624  -.282893  .015852   .0000000  .0000000   .040347  .048814   .001367  .022678
+    1951.85   .078168  .022632  -.298582  .017123   .0000000  .0000000  -.003139  .050304   .007860  .021614
+    1951.90  -.072498  .032917  -.293768  .018894   .0000000  .0000000   .125502  .076976   .015054  .025251
+    1951.95  -.121266  .022373  -.283286  .017006   .0000000  .0000000   .057744  .061298   .007417  .016688
+    1952.00  -.228941  .023453  -.289334  .015160   .0000000  .0000000   .065699  .092924   .003733  .013960
+    1952.05  -.384844  .040550  -.280973  .024273   .0000000  .0000000   .284884  .084576  -.039072  .026814
+    1952.10  -.425432  .029375  -.085033  .024938   .0000000  .0000000   .197790  .069953   .036197  .028108
+    1952.15  -.415187  .022758   .007147  .018201   .0000000  .0000000   .014303  .053656  -.042298  .024848
+    1952.20  -.438050  .021653   .104950  .013956   .0000000  .0000000   .103593  .038544   .013736  .021002
+    1952.25  -.373937  .019560   .199595  .015899   .0000000  .0000000   .082171  .034312  -.021995  .029667
+    1952.30  -.214791  .022330   .330880  .015622   .0000000  .0000000  -.000098  .045004   .010157  .021459
+    1952.35  -.237011  .019115   .436678  .016079   .0000000  .0000000   .085512  .059804  -.036097  .020952
+    1952.40  -.206498  .024081   .424479  .014987   .0000000  .0000000  -.106002  .058773  -.011340  .017563
+    1952.45  -.114247  .019359   .462331  .013963   .0000000  .0000000   .197230  .078764  -.027464  .013303
+    1952.50   .053909  .020944   .452330  .015244   .0000000  .0000000   .220087  .065328  -.021920  .014673
+    1952.55   .152162  .021907   .415113  .015937   .0000000  .0000000   .258642  .055326  -.015693  .018024
+    1952.60   .302914  .027762   .389455  .021787   .0000000  .0000000   .189218  .066538  -.038539  .026682
+    1952.65   .303583  .024397   .272217  .019567   .0000000  .0000000   .027558  .060530   .059113  .029543
+    1952.70   .379665  .028581   .235664  .018126   .0000000  .0000000   .074403  .052206   .018652  .023236
+    1952.75   .284255  .027142   .115197  .015107   .0000000  .0000000   .068113  .039440  -.013031  .024266
+    1952.80   .290673  .022339   .004058  .017613   .0000000  .0000000  -.026814  .055548  -.016646  .029117
+    1952.85   .273678  .020792  -.093103  .016658   .0000000  .0000000  -.034611  .056709  -.003478  .023533
+    1952.90   .201204  .024394  -.160920  .017352   .0000000  .0000000   .101205  .066918  -.021440  .021197
+    1952.95   .092473  .035990  -.181244  .018135   .0000000  .0000000   .170540  .070181   .035931  .018927
+    1953.00   .123811  .046112  -.231433  .020713   .0000000  .0000000   .146975  .077941   .022420  .020965
+    1953.05  -.059768  .026615  -.287803  .019405   .0000000  .0000000   .073539  .061229   .031161  .022222
+    1953.10  -.099969  .025918  -.181194  .018804   .0000000  .0000000   .323839  .059555   .015830  .022517
+    1953.15  -.117820  .027928  -.081862  .022704   .0000000  .0000000   .083580  .061634   .087507  .024412
+    1953.20  -.288724  .030705  -.112507  .019672   .0000000  .0000000   .199470  .052632   .023652  .024078
+    1953.25  -.256646  .026746  -.080086  .022173   .0000000  .0000000  -.022337  .051580   .003880  .031966
+    1953.30  -.275672  .020599   .047975  .014455   .0000000  .0000000  -.058750  .041994   .037409  .023317
+    1953.35  -.343300  .027500   .172800  .021700   .0000000  .0000000   .059600  .060800  -.079200  .026000
+    1953.40  -.325734  .024532   .244282  .019035   .0000000  .0000000  -.140676  .074294  -.025887  .022799
+    1953.45  -.275838  .026641   .294124  .022470   .0000000  .0000000  -.053471  .102814  -.008273  .021290
+    1953.50  -.079095  .026144   .448538  .019122   .0000000  .0000000  -.217507  .080168  -.055041  .019564
+    1953.55  -.029260  .034099   .528535  .024925   .0000000  .0000000  -.271311  .082407  -.112618  .028206
+    1953.60   .021387  .028585   .421200  .018422   .0000000  .0000000   .246134  .065248  -.032333  .023040
+    1953.65   .022300  .022600   .491500  .016100   .0000000  .0000000   .021900  .051500  -.053600  .025900
+    1953.70   .219443  .028716   .448367  .021305   .0000000  .0000000   .081104  .050590  -.103119  .029687
+    1953.75   .204160  .023329   .288329  .022475   .0000000  .0000000   .063711  .047750   .050719  .031526
+    1953.80   .197400  .022700   .214300  .018100   .0000000  .0000000  -.168400  .053500   .047700  .029700
+    1953.85   .224082  .032786   .155717  .022938   .0000000  .0000000  -.147230  .058603   .052039  .028865
+    1953.90   .236336  .031970   .022065  .020042   .0000000  .0000000  -.041651  .071485  -.009290  .026217
+    1953.95   .184200  .022800   .032600  .018100   .0000000  .0000000   .031900  .065100   .028800  .017000
+    1954.00   .136603  .026849  -.090735  .023182   .0000000  .0000000  -.050796  .061331  -.024245  .023569
+    1954.05   .015320  .026526  -.126354  .027416   .0000000  .0000000   .055392  .101392  -.020027  .026427
+    1954.10   .036575  .028672  -.126953  .021059   .0000000  .0000000   .049545  .062772   .017389  .023557
+    1954.15  -.024887  .026248  -.150786  .018394   .0000000  .0000000   .027980  .050050   .006061  .023351
+    1954.20  -.154021  .026855  -.147087  .022367   .0000000  .0000000   .099882  .049362  -.064110  .038953
+    1954.25  -.164556  .021461  -.108061  .015823   .0000000  .0000000   .013419  .035979   .034450  .032006
+    1954.30  -.195623  .019666  -.112539  .014869   .0000000  .0000000   .068849  .042354  -.004632  .022316
+    1954.35  -.239513  .027637  -.079926  .020540   .0000000  .0000000   .120193  .052482  -.023196  .024829
+    1954.40  -.212137  .021964   .053878  .015717   .0000000  .0000000  -.030530  .063171  -.023356  .019706
+    1954.45  -.133960  .026786   .116843  .020849   .0000000  .0000000   .148397  .093316  -.062326  .018748
+    1954.50  -.215416  .032565   .144317  .022605   .0000000  .0000000   .077216  .082065   .011241  .023161
+    1954.55  -.127059  .022877   .218349  .016607   .0000000  .0000000   .220575  .059648  -.058397  .018458
+    1954.60  -.161882  .021549   .209542  .014593   .0000000  .0000000   .143856  .050849  -.025255  .018115
+    1954.65   .022753  .033378   .272072  .017886   .0000000  .0000000   .370787  .069710  -.024009  .031002
+    1954.70  -.093222  .026299   .296413  .019708   .0000000  .0000000   .117923  .043945  -.004252  .024702
+    1954.75  -.075965  .029848   .339411  .018236   .0000000  .0000000   .090049  .048544  -.034750  .027623
+    1954.80   .010000  .028200   .310600  .017800   .0000000  .0000000   .081200  .052000   .034100  .027200
+    1954.85  -.019012  .031351   .279865  .019533   .0000000  .0000000  -.167084  .066989  -.023408  .025878
+    1954.90  -.029023  .024480   .206222  .019033   .0000000  .0000000   .033885  .058871  -.016160  .021829
+    1954.95   .099499  .037242   .204533  .031026   .0000000  .0000000  -.042950  .097768   .047091  .030742
+    1955.00   .017291  .021213   .175152  .014290   .0000000  .0000000   .033370  .060138  -.009317  .014478
+    1955.05   .124344  .031213   .144779  .017718   .0000000  .0000000   .155624  .053807   .046471  .021918
+    1955.10   .029988  .022320   .049576  .019991   .0000000  .0000000   .130361  .063377  -.044089  .022481
+    1955.15   .124568  .023803   .140522  .017538   .0000000  .0000000   .099333  .048360  -.019436  .019780
+    1955.20   .158988  .028047   .106674  .020956   .0000000  .0000000   .027132  .056954   .019986  .020199
+    1955.25   .105321  .020346   .083753  .013910   .0000000  .0000000   .145617  .037687  -.006161  .018209
+    1955.30   .046473  .022676  -.027556  .016976   .0000000  .0000000   .059051  .046376  -.011698  .024166
+    1955.35   .065044  .022393  -.038566  .018508   .0000000  .0000000   .141355  .053642   .015979  .020549
+    1955.40   .041687  .021698  -.021509  .017344   .0000000  .0000000   .049719  .058520  -.013052  .018222
+    1955.45  -.001269  .019033  -.080686  .017959   .0000000  .0000000   .157478  .077168  -.034083  .014725
+    1955.50  -.055935  .018975  -.006263  .016799   .0000000  .0000000   .168650  .068965  -.004592  .014438
+    1955.55   .003124  .021527   .039639  .017456   .0000000  .0000000   .285877  .055508  -.027040  .017711
+    1955.60  -.109785  .024289   .034162  .020938   .0000000  .0000000   .155096  .051168   .005073  .020720
+    1955.65  -.137337  .016833   .039769  .014406   .0000000  .0000000   .221681  .037552   .015682  .016835
+    1955.70  -.226992  .018502   .043228  .013887   .0000000  .0000000   .019429  .034500   .008777  .017654
+    1955.75  -.229553  .028384   .066146  .018650   .0000000  .0000000   .028042  .048891  -.015119  .021530
+    1955.80  -.219600  .034400   .137300  .022400   .0000000  .0000000   .055500  .068100  -.045900  .026000
+    1955.85  -.189281  .026314   .159204  .023051   .0000000  .0000000   .008612  .064603  -.018369  .029468
+    1955.90  -.219306  .015150   .167037  .014494   .0000000  .0000000  -.007341  .043047  -.058591  .015797
+    1955.95  -.203131  .020472   .233918  .019303   .0000000  .0000000  -.005547  .055439  -.053440  .017788
+    1956.00  -.070247  .032431   .341707  .031719   .0000000  .0000000   .135590  .076991   .014708  .031785
+    1956.05  -.007398  .035739   .192673  .028194   .0000000  .0000000   .279279  .089268   .011918  .030549
+    1956.10   .016713  .022564   .339190  .016817   .0000000  .0000000  -.016698  .043762   .026854  .019658
+    1956.15   .038798  .019068   .279099  .015588   .0000000  .0000000   .074850  .041926   .016647  .017933
+    1956.20   .117409  .024592   .334384  .022962   .0000000  .0000000   .116289  .049460  -.006365  .018888
+    1956.25   .083759  .020019   .248766  .015039   .0000000  .0000000   .150435  .038028   .011561  .018956
+    1956.30   .152189  .026345   .209354  .017636   .0000000  .0000000   .075592  .048774  -.003266  .022999
+    1956.35   .160798  .016985   .161261  .012469   .0000000  .0000000   .097469  .041497  -.003484  .014663
+    1956.40   .211912  .021913   .132122  .016197   .0000000  .0000000  -.051769  .059819   .008059  .017699
+    1956.45   .219468  .026982   .113755  .020920   .0000000  .0000000   .021605  .075035  -.053813  .017971
+    1956.50   .212688  .023269   .021181  .017172   .0000000  .0000000   .002238  .083659  -.027947  .015641
+    1956.55   .169132  .028754  -.055280  .025008   .0000000  .0000000   .141001  .088082  -.001245  .026443
+    1956.60   .146149  .017097   .000797  .015538   .0000000  .0000000   .153254  .042082  -.055786  .017332
+    1956.65   .059496  .017297  -.049301  .015542   .0000000  .0000000   .073822  .039023  -.068925  .019163
+    1956.70   .018377  .015792  -.044723  .014377   .0000000  .0000000   .116191  .033209   .042646  .018835
+    1956.75  -.050507  .015619  -.037579  .015578   .0000000  .0000000   .100776  .031217  -.026861  .016950
+    1956.80  -.204692  .020241  -.059809  .016760   .0000000  .0000000   .042062  .045466  -.058731  .018652
+    1956.85  -.228535  .015021   .004844  .015190   .0000000  .0000000   .101849  .044702  -.061427  .017412
+    1956.90  -.318792  .017388   .068497  .018115   .0000000  .0000000   .059661  .049314  -.004755  .019308
+    1956.95  -.290530  .022402   .108736  .015607   .0000000  .0000000   .073717  .070250  -.046158  .016280
+    1957.00  -.401787  .020041   .175879  .017971   .0000000  .0000000   .020759  .054482  -.038155  .017054
+    1957.05  -.340710  .013184   .217146  .014033   .0000000  .0000000   .043470  .039909   .013138  .013164
+    1957.10  -.222754  .020310   .314496  .018886   .0000000  .0000000   .133183  .046866   .069621  .020912
+    1957.15  -.224552  .015429   .381610  .015155   .0000000  .0000000   .043022  .041393  -.013743  .016201
+    1957.20  -.194858  .017215   .441895  .013724   .0000000  .0000000   .036351  .034424   .007720  .019962
+    1957.25  -.099300  .012700   .411100  .012500   .0000000  .0000000   .052800  .030400   .017200  .013700
+    1957.30   .007920  .019006   .425095  .016097   .0000000  .0000000  -.068439  .041536  -.043108  .021122
+    1957.35   .076323  .017320   .453030  .014204   .0000000  .0000000   .022407  .046976  -.013461  .017459
+    1957.40   .152477  .017111   .481943  .013762   .0000000  .0000000  -.008718  .049449  -.044238  .015537
+    1957.45   .235240  .019941   .383132  .014899   .0000000  .0000000   .054280  .061264  -.026198  .015178
+    1957.50   .240076  .018320   .308449  .014258   .0000000  .0000000   .127074  .057250  -.053032  .014600
+    1957.55   .292684  .018822   .272059  .014287   .0000000  .0000000   .164388  .051760  -.012495  .016205
+    1957.60   .345486  .021836   .208463  .017253   .0000000  .0000000   .144962  .046548  -.044037  .019490
+    1957.65   .271110  .017701   .018347  .016626   .0000000  .0000000   .186498  .041069  -.008776  .019808
+    1957.70   .229000  .019985  -.058924  .017160   .0000000  .0000000   .212200  .039170   .039827  .021540
+    1957.75   .126351  .013356  -.051070  .011486   .0000000  .0000000   .098832  .028027   .000193  .016418
+    1957.80   .039747  .017662  -.126282  .015886   .0000000  .0000000   .103719  .040471   .013898  .018150
+    1957.85  -.003182  .017681  -.164541  .014422   .0000000  .0000000   .103918  .045521  -.058888  .017935
+    1957.90  -.069900  .017277  -.130259  .016231   .0000000  .0000000   .044071  .049795   .025530  .017047
+    1957.95  -.209601  .016394  -.084401  .013534   .0000000  .0000000   .013751  .041087   .004369  .013311
+    1958.00  -.305232  .025889  -.027916  .020974   .0000000  .0000000   .082277  .079180  -.008332  .020418
+    1958.05  -.343994  .024077   .036549  .023193   .0000000  .0000000   .119698  .054790  -.000916  .021924
+    1958.10  -.333001  .029858   .173344  .021209   .0000000  .0000000   .184998  .061702   .025625  .021775
+    1958.15  -.335927  .021083   .215384  .017353   .0000000  .0000000  -.038407  .050054   .059727  .019629
+    1958.20  -.305676  .013074   .288831  .012035   .0000000  .0000000   .000673  .027834   .029655  .017165
+    1958.25  -.300800  .016700   .338700  .012700   .0000000  .0000000   .068600  .037200  -.012200  .014500
+    1958.30  -.179030  .015640   .409116  .012973   .0000000  .0000000   .000773  .035277  -.023103  .015460
+    1958.35  -.101308  .015426   .407552  .013248   .0000000  .0000000  -.082328  .041851  -.003564  .015349
+    1958.40  -.041921  .011854   .434062  .011071   .0000000  .0000000   .055644  .041768  -.053159  .012218
+    1958.45   .051783  .015866   .422110  .012064   .0000000  .0000000   .232517  .059118  -.058358  .011873
+    1958.50   .098517  .016172   .451402  .013366   .0000000  .0000000   .046882  .056090  -.034994  .012743
+    1958.55   .180900  .016500   .370800  .013300   .0000000  .0000000   .091200  .044200  -.047700  .014600
+    1958.60   .203233  .018061   .334686  .013407   .0000000  .0000000   .011579  .041113  -.035020  .017378
+    1958.65   .303286  .015997   .248765  .012940   .0000000  .0000000   .125521  .035626  -.012307  .016621
+    1958.70   .276253  .012998   .146280  .010124   .0000000  .0000000  -.015057  .027164  -.007869  .014747
+    1958.75   .267226  .014759   .075078  .011492   .0000000  .0000000   .047112  .029887   .006651  .014882
+    1958.80   .266128  .019581   .119921  .014314   .0000000  .0000000   .031926  .040676  -.005640  .019317
+    1958.85   .213073  .022286  -.055607  .017133   .0000000  .0000000   .187421  .049471  -.037917  .021103
+    1958.90   .164238  .017142  -.060513  .012750   .0000000  .0000000   .112400  .041721  -.007397  .014646
+    1958.95   .101815  .018071  -.044255  .014672   .0000000  .0000000  -.037404  .040891   .040556  .014509
+    1959.00   .016105  .016596  -.129490  .016078   .0000000  .0000000   .215527  .058118   .043330  .014219
+    1959.05  -.095165  .015366  -.054368  .013998   .0000000  .0000000  -.027941  .050308   .039826  .013974
+    1959.10  -.153168  .012186  -.068371  .010996   .0000000  .0000000  -.074850  .034602   .038500  .012638
+    1959.15  -.195475  .012676  -.058794  .010334   .0000000  .0000000  -.105669  .029582   .067018  .012900
+    1959.20  -.253927  .011124   .017351  .010816   .0000000  .0000000  -.142133  .024682  -.006852  .012762
+    1959.25  -.238076  .011033   .061599  .010326   .0000000  .0000000  -.131493  .023456  -.007203  .014765
+    1959.30  -.240657  .013727   .198641  .012008   .0000000  .0000000  -.101022  .033062  -.033398  .015979
+    1959.35  -.252739  .016361   .262649  .012664   .0000000  .0000000  -.062746  .043776  -.057614  .015424
+    1959.40  -.214144  .015160   .308577  .012240   .0000000  .0000000  -.021546  .048769  -.098482  .014808
+    1959.45  -.142987  .014320   .396049  .012083   .0000000  .0000000   .121990  .055278  -.110626  .011749
+    1959.50  -.037927  .015406   .401546  .013586   .0000000  .0000000   .080033  .055336  -.131910  .013763
+    1959.55  -.034800  .013400   .382000  .012900   .0000000  .0000000   .264400  .044500  -.023000  .012800
+    1959.60   .106849  .019451   .415365  .015951   .0000000  .0000000   .293255  .046029  -.042593  .020540
+    1959.65   .129032  .016194   .368627  .012976   .0000000  .0000000   .179256  .037492  -.054083  .016866
+    1959.70   .175804  .019261   .336333  .014357   .0000000  .0000000   .099735  .039610   .006099  .021384
+    1959.75   .222541  .020118   .303817  .015057   .0000000  .0000000   .192338  .042252   .040102  .019978
+    1959.80   .203199  .012170   .215274  .011218   .0000000  .0000000   .075466  .029823  -.025358  .016907
+    1959.85   .202366  .014406   .197922  .014152   .0000000  .0000000  -.028643  .033913   .086031  .018165
+    1959.90   .175341  .011394   .085444  .010511   .0000000  .0000000   .049587  .030991   .019198  .011465
+    1959.95   .132030  .014553   .058528  .014121   .0000000  .0000000  -.013851  .052494  -.002008  .012094
+    1960.00   .008173  .019278   .006252  .016657   .0000000  .0000000   .178324  .069497   .058431  .015235
+    1960.05  -.003037  .017665  -.014016  .013888   .0000000  .0000000  -.064124  .047557   .010047  .014619
+    1960.10  -.096783  .013952  -.064746  .012789   .0000000  .0000000   .106423  .042002  -.000985  .013848
+    1960.15  -.049029  .012919  -.015531  .012193   .0000000  .0000000  -.087043  .031708   .000092  .014702
+    1960.20  -.070352  .018905   .005059  .015333   .0000000  .0000000  -.105083  .037064  -.038903  .022636
+    1960.25  -.093472  .013986  -.033430  .010931   .0000000  .0000000  -.061937  .026397   .024171  .016309
+    1960.30  -.144743  .010967   .036124  .009691   .0000000  .0000000  -.067451  .026687  -.002796  .012403
+    1960.35  -.136289  .013576   .091137  .011939   .0000000  .0000000  -.076098  .039075  -.017355  .015119
+    1960.40  -.185600  .011906   .139573  .010679   .0000000  .0000000  -.002643  .039828  -.050680  .011802
+    1960.45  -.144215  .015781   .156992  .012210   .0000000  .0000000  -.015514  .055952  -.047725  .012257
+    1960.50  -.093892  .014294   .234106  .012054   .0000000  .0000000   .292676  .057465  -.098800  .011318
+    1960.55  -.141900  .011200   .251100  .009400   .0000000  .0000000   .151300  .037400  -.033800  .010100
+    1960.60  -.082108  .010316   .224170  .009024   .0000000  .0000000   .062250  .029440  -.026798  .011262
+    1960.65  -.054888  .012519   .246897  .011749   .0000000  .0000000   .045087  .032512  -.058084  .016015
+    1960.70   .000500  .017600   .272000  .014800   .0000000  .0000000   .124200  .038200  -.016300  .019400
+    1960.75   .027616  .012393   .261292  .010364   .0000000  .0000000   .052321  .024867   .015003  .016433
+    1960.80   .028359  .012729   .217569  .010841   .0000000  .0000000  -.008342  .029411   .025216  .015182
+    1960.85  -.026324  .013635   .268219  .012928   .0000000  .0000000   .038656  .041050   .005303  .016382
+    1960.90   .045255  .017858   .178998  .013681   .0000000  .0000000   .052844  .047472  -.010855  .017694
+    1960.95   .018041  .022609   .148022  .013632   .0000000  .0000000   .086354  .057597   .003814  .014939
+    1961.00  -.010002  .021576   .178091  .018578   .0000000  .0000000  -.109701  .072493   .033406  .017217
+    1961.05   .014227  .016229   .129318  .014031   .0000000  .0000000   .172641  .051102   .000182  .013001
+    1961.10  -.034519  .012398   .135146  .010864   .0000000  .0000000   .046666  .040969  -.001677  .012123
+    1961.15   .020541  .009690   .071804  .008953   .0000000  .0000000   .017130  .023800  -.002468  .011041
+    1961.20  -.008860  .013770   .093978  .011954   .0000000  .0000000  -.062540  .030784   .042272  .017489
+    1961.25  -.009493  .013722   .077921  .012877   .0000000  .0000000  -.064367  .027888  -.002371  .019533
+    1961.30  -.013247  .012140   .104033  .010537   .0000000  .0000000  -.027246  .029362  -.027553  .013240
+    1961.35  -.011871  .016049   .106863  .012333   .0000000  .0000000  -.042391  .043669  -.025470  .015685
+    1961.40  -.055595  .010554   .050930  .010174   .0000000  .0000000   .015492  .037524  -.030144  .010919
+    1961.45  -.006478  .013451   .093211  .011987   .0000000  .0000000  -.007698  .053913  -.067298  .011171
+    1961.50  -.052932  .012403   .075634  .010452   .0000000  .0000000   .044608  .049361  -.022091  .010010
+    1961.55  -.008206  .011519   .085606  .010050   .0000000  .0000000   .122173  .040208  -.064085  .011324
+    1961.60  -.051487  .015238   .123137  .012517   .0000000  .0000000   .076453  .040358  -.034798  .016069
+    1961.65  -.017757  .013816   .095707  .012173   .0000000  .0000000   .139748  .037766  -.009789  .017459
+    1961.70  -.062010  .014301   .097662  .010789   .0000000  .0000000   .151076  .028427   .021771  .016330
+    1961.75  -.081090  .015416   .089862  .012166   .0000000  .0000000   .050875  .029772  -.029162  .018757
+    1961.80  -.110423  .013068   .077764  .010812   .0000000  .0000000   .050749  .030057  -.051615  .016866
+    1961.85  -.090300  .016100   .114700  .012300   .0000000  .0000000  -.014600  .037900  -.010800  .016400
+    1961.90  -.167482  .018102   .169747  .012490   .0000000  .0000000  -.025160  .044893  -.020808  .015706
+    1961.95  -.142626  .013234   .162677  .012140   .0000000  .0000000   .096048  .047052   .003845  .011241
+    1962.00  -.033900  .017100   .203700  .013600   .0376300  .0146000   .000000  .000000   .000000  .000000
+    1962.05  -.050170  .014430   .211840  .012140   .0338900  .0133200   .000000  .000000   .000000  .000000
+    1962.10  -.002320  .017110   .229940  .012770   .0290470  .0134500   .000000  .000000   .000000  .000000
+    1962.15   .006920  .018700   .252420  .018200   .0240100  .0162000   .000000  .000000   .000000  .000000
+    1962.20   .024920  .016200   .267420  .015500   .0164880  .0149000   .000000  .000000   .000000  .000000
+    1962.25   .036710  .015210   .249550  .015050   .0102410  .0142600   .000000  .000000   .000000  .000000
+    1962.30   .072420  .017430   .225380  .017660  -.0005960  .0168200   .000000  .000000   .000000  .000000
+    1962.35   .094690  .018700   .208290  .017540  -.0104660  .0169000   .000000  .000000   .000000  .000000
+    1962.40   .093250  .017570   .193210  .017020  -.0187080  .0156600   .000000  .000000   .000000  .000000
+    1962.45   .107960  .018660   .129190  .018270  -.0156280  .0180200   .000000  .000000   .000000  .000000
+    1962.50   .095310  .017460   .127660  .017260  -.0096470  .0164200   .000000  .000000   .000000  .000000
+    1962.55   .086920  .016900   .113560  .015650   .0013710  .0151500   .000000  .000000   .000000  .000000
+    1962.60   .069240  .017840   .063740  .017960   .0066930  .0171500   .000000  .000000   .000000  .000000
+    1962.65   .055400  .016640   .049150  .016300   .0122100  .0149300   .000000  .000000   .000000  .000000
+    1962.70   .008500  .016540   .041810  .015810   .0142490  .0148400   .000000  .000000   .000000  .000000
+    1962.75  -.014150  .018820   .060290  .018700   .0105620  .0178200   .000000  .000000   .000000  .000000
+    1962.80  -.066110  .016250   .050990  .015590   .0038340  .0149800   .000000  .000000   .000000  .000000
+    1962.85  -.113040  .016610   .105830  .015680  -.0066870  .0155700   .000000  .000000   .000000  .000000
+    1962.90  -.153970  .021970   .139440  .019620  -.0175860  .0181300   .000000  .000000   .000000  .000000
+    1962.95  -.160030  .021360   .176030  .019750  -.0286510  .0182100   .000000  .000000   .000000  .000000
+    1963.00  -.205590  .024840   .212120  .022870  -.0332190  .0207100   .000000  .000000   .000000  .000000
+    1963.05  -.201720  .020230   .264370  .018510  -.0339840  .0173800   .000000  .000000   .000000  .000000
+    1963.10  -.173000  .017760   .287920  .016570  -.0309280  .0156300   .000000  .000000   .000000  .000000
+    1963.15  -.148670  .018450   .335970  .017880  -.0308550  .0159600   .000000  .000000   .000000  .000000
+    1963.20  -.099870  .017450   .335960  .016390  -.0392730  .0161400   .000000  .000000   .000000  .000000
+    1963.25  -.044310  .017840   .360350  .016930  -.0507330  .0157900   .000000  .000000   .000000  .000000
+    1963.30   .021450  .017350   .354760  .016500  -.0615850  .0150300   .000000  .000000   .000000  .000000
+    1963.35   .093210  .018140   .324570  .016940  -.0762480  .0155900   .000000  .000000   .000000  .000000
+    1963.40   .152910  .018670   .306570  .017860  -.0852720  .0170900   .000000  .000000   .000000  .000000
+    1963.45   .175600  .021310   .263720  .019770  -.0898040  .0196500   .000000  .000000   .000000  .000000
+    1963.50   .230800  .019620   .226120  .017420  -.0911400  .0167700   .000000  .000000   .000000  .000000
+    1963.55   .218840  .016860   .193180  .015570  -.0850800  .0147700   .000000  .000000   .000000  .000000
+    1963.60   .207420  .018510   .115940  .016960  -.0800220  .0155000   .000000  .000000   .000000  .000000
+    1963.65   .148670  .018730   .085350  .019220  -.0829150  .0174300   .000000  .000000   .000000  .000000
+    1963.70   .096090  .016040   .028620  .015490  -.0876470  .0147500   .000000  .000000   .000000  .000000
+    1963.75   .053710  .020020  -.010670  .018330  -.0974610  .0165900   .000000  .000000   .000000  .000000
+    1963.80  -.017030  .015520  -.023980  .014320  -.1141360  .0137500   .000000  .000000   .000000  .000000
+    1963.85  -.106000  .018730  -.008970  .018460  -.0370110  .0172000   .000000  .000000   .000000  .000000
+    1963.90  -.162800  .022040   .001510  .021360  -.0481380  .0197300   .000000  .000000   .000000  .000000
+    1963.95  -.172400  .018540   .042730  .017540  -.0669830  .0169800   .000000  .000000   .000000  .000000
+    1964.00  -.225800  .021630   .073470  .019990  -.0808460  .0189500   .000000  .000000   .000000  .000000
+    1964.05  -.277190  .019900   .142370  .019340  -.0959270  .0189700   .000000  .000000   .000000  .000000
+    1964.10  -.316290  .018270   .213450  .017350  -.1133170  .0162800   .000000  .000000   .000000  .000000
+    1964.15  -.278600  .020010   .281690  .017670  -.1248710  .0168000   .000000  .000000   .000000  .000000
+    1964.20  -.246040  .017960   .323490  .016880  -.1403400  .0160800   .000000  .000000   .000000  .000000
+    1964.25  -.179450  .016490   .390780  .015240  -.0597570  .0147800   .000000  .000000   .000000  .000000
+    1964.30  -.106780  .015610   .417820  .015450  -.0730700  .0143500   .000000  .000000   .000000  .000000
+    1964.35  -.051130  .017730   .465850  .017200  -.0918930  .0154900   .000000  .000000   .000000  .000000
+    1964.40  -.000180  .016200   .454570  .015860  -.1099970  .0146100   .000000  .000000   .000000  .000000
+    1964.45   .070820  .017660   .457390  .017230  -.1212000  .0157600   .000000  .000000   .000000  .000000
+    1964.50   .122250  .016730   .410720  .017010  -.1147420  .0153900   .000000  .000000   .000000  .000000
+    1964.55   .188300  .016040   .371690  .015870  -.1164510  .0145300   .000000  .000000   .000000  .000000
+    1964.60   .233650  .018730   .317960  .018160  -.1142910  .0164100   .000000  .000000   .000000  .000000
+    1964.65   .243950  .015300   .251720  .015260  -.1118720  .0137800   .000000  .000000   .000000  .000000
+    1964.70   .228520  .015130   .159640  .014810  -.0165730  .0137500   .000000  .000000   .000000  .000000
+    1964.75   .202930  .016080   .093010  .015660  -.0291840  .0142000   .000000  .000000   .000000  .000000
+    1964.80   .163200  .017480   .037870  .017150  -.0476800  .0153100   .000000  .000000   .000000  .000000
+    1964.85   .107390  .017250   .018400  .016220  -.0671060  .0146300   .000000  .000000   .000000  .000000
+    1964.90   .042310  .018970  -.014260  .017750  -.0837400  .0159100   .000000  .000000   .000000  .000000
+    1964.95  -.022710  .017480  -.019620  .017800  -.1003350  .0168300   .000000  .000000   .000000  .000000
+    1965.00  -.089890  .017540  -.005080  .017770  -.0182700  .0155700   .000000  .000000   .000000  .000000
+    1965.05  -.109340  .020910  -.011770  .019760  -.0248210  .0178900   .000000  .000000   .000000  .000000
+    1965.10  -.187520  .016700   .079690  .015740  -.0413790  .0153700   .000000  .000000   .000000  .000000
+    1965.15  -.220180  .015780   .108850  .015600  -.0595530  .0148100   .000000  .000000   .000000  .000000
+    1965.20  -.239960  .015850   .170890  .014540   .0201840  .0139800   .000000  .000000   .000000  .000000
+    1965.25  -.239880  .014860   .232370  .014700  -.0057010  .0133300   .000000  .000000   .000000  .000000
+    1965.30  -.221390  .016120   .286720  .014580  -.0330600  .0137300   .000000  .000000   .000000  .000000
+    1965.35  -.177010  .016430   .329310  .015110  -.0487050  .0144700   .000000  .000000   .000000  .000000
+    1965.40  -.151550  .014790   .380270  .013930  -.0661190  .0133300   .000000  .000000   .000000  .000000
+    1965.45  -.115410  .017280   .406270  .016860  -.0832370  .0153900   .000000  .000000   .000000  .000000
+    1965.50  -.033960  .018000   .439390  .016580   .0111050  .0152100   .000000  .000000   .000000  .000000
+    1965.55   .047390  .018020   .425360  .016460   .0060030  .0153800   .000000  .000000   .000000  .000000
+    1965.60   .084440  .020530   .411480  .019450  -.0046230  .0178800   .000000  .000000   .000000  .000000
+    1965.65   .105520  .016310   .371940  .015300  -.0087120  .0140700   .000000  .000000   .000000  .000000
+    1965.70   .137420  .016280   .304360  .015810   .0758570  .0142700   .000000  .000000   .000000  .000000
+    1965.75   .168630  .020750   .248710  .019550   .0544290  .0168100   .000000  .000000   .000000  .000000
+    1965.80   .200590  .016310   .176560  .016160   .0353090  .0145100   .000000  .000000   .000000  .000000
+    1965.85   .178810  .017050   .143930  .016190   .0119610  .0151900   .000000  .000000   .000000  .000000
+    1965.90   .158060  .021980   .087030  .020220  -.0139660  .0178700   .000000  .000000   .000000  .000000
+    1965.95   .115300  .019820   .054420  .018510  -.0327310  .0171400   .000000  .000000   .000000  .000000
+    1966.00   .047900  .021460   .005530  .019630  -.0467430  .0170800   .000000  .000000   .000000  .000000
+    1966.05   .003210  .021070   .018400  .019450  -.0442320  .0179400   .000000  .000000   .000000  .000000
+    1966.10  -.020370  .019000   .017330  .017320  -.0397280  .0152900   .000000  .000000   .000000  .000000
+    1966.15  -.092720  .018200   .042830  .016850  -.0398940  .0153300   .000000  .000000   .000000  .000000
+    1966.20  -.126630  .016810   .060540  .016230  -.0393290  .0144800   .000000  .000000   .000000  .000000
+    1966.25  -.126250  .016740   .087680  .015810  -.0396900  .0146800   .000000  .000000   .000000  .000000
+    1966.30  -.167720  .015830   .130720  .015330  -.0463990  .0140700   .000000  .000000   .000000  .000000
+    1966.35  -.161730  .017410   .172470  .016490  -.0516050  .0149100   .000000  .000000   .000000  .000000
+    1966.40  -.151510  .017850   .196200  .018220  -.0495510  .0164900   .000000  .000000   .000000  .000000
+    1966.45  -.128550  .016130   .234880  .015210  -.0432800  .0141600   .000000  .000000   .000000  .000000
+    1966.50  -.124150  .018800   .306450  .019320  -.0321410  .0172300   .000000  .000000   .000000  .000000
+    1966.55  -.081600  .016620   .310620  .015820  -.0125560  .0141500   .000000  .000000   .000000  .000000
+    1966.60  -.055550  .015510   .328730  .015120   .0003880  .0138900   .000000  .000000   .000000  .000000
+    1966.65  -.017090  .017080   .338450  .016220   .0095020  .0144900   .000000  .000000   .000000  .000000
+    1966.70   .006590  .015310   .333040  .014830   .0161450  .0135400   .000000  .000000   .000000  .000000
+    1966.75   .042120  .017470   .311320  .016490   .0146070  .0149000   .000000  .000000   .000000  .000000
+    1966.80   .091960  .017500   .283830  .016510   .0121820  .0146500   .000000  .000000   .000000  .000000
+    1966.85   .094580  .019200   .252760  .017990   .0098330  .0157100   .000000  .000000   .000000  .000000
+    1966.90   .116210  .023420   .220110  .021570   .0051870  .0180300   .000000  .000000   .000000  .000000
+    1966.95   .135490  .020350   .172910  .017450   .0058710  .0172200   .000000  .000000   .000000  .000000
+    1967.00   .097100  .016900   .162300  .014300   .0124750  .0140000   .000000  .000000   .000000  .000000
+    1967.05   .070010  .015300   .139910  .013600   .0184380  .0129000   .000000  .000000   .000000  .000000
+    1967.10   .048010  .014100   .130910  .012600   .0211400  .0121000   .000000  .000000   .000000  .000000
+    1967.15   .032320  .014100   .139020  .012500   .0229620  .0122000   .000000  .000000   .000000  .000000
+    1967.20   .014320  .014500   .136220  .013100   .0194950  .0124000   .000000  .000000   .000000  .000000
+    1967.25   .005330  .013900   .130830  .012200   .0167670  .0120000   .000000  .000000   .000000  .000000
+    1967.30  -.008570  .013300   .144330  .011700   .0133100  .0117000   .000000  .000000   .000000  .000000
+    1967.35  -.013670  .014300   .164530  .013100   .0065720  .0126000   .000000  .000000   .000000  .000000
+    1967.40  -.008560  .013800   .148640  .012200   .0071750  .0121000   .000000  .000000   .000000  .000000
+    1967.45   .003850  .014000   .168250  .012300   .0176880  .0122000   .000000  .000000   .000000  .000000
+    1967.50  -.011850  .014100   .176050  .012200   .0301300  .0124000   .000000  .000000   .000000  .000000
+    1967.55  -.008350  .013500   .193960  .011900   .0477730  .0121000   .000000  .000000   .000000  .000000
+    1967.60  -.011740  .013700   .189260  .011600   .0663160  .0120000   .000000  .000000   .000000  .000000
+    1967.65  -.001430  .014800   .186370  .012900   .0768980  .0127000   .000000  .000000   .000000  .000000
+    1967.70  -.029530  .013400   .191370  .011300   .0890110  .0117000   .000000  .000000   .000000  .000000
+    1967.75  -.033830  .013300   .184480  .011800   .0963740  .0118000   .000000  .000000   .000000  .000000
+    1967.80  -.030620  .014100   .195580  .012400   .0971770  .0125000   .000000  .000000   .000000  .000000
+    1967.85  -.035120  .013800   .205980  .012100   .0953500  .0121000   .000000  .000000   .000000  .000000
+    1967.90  -.026910  .014200   .212290  .013000   .0952730  .0126000   .000000  .000000   .000000  .000000
+    1967.95  -.026200  .014100   .222600  .012300   .0931660  .0121000   .000000  .000000   .000000  .000000
+    1968.00  -.034000  .015100   .239700  .012400   .0951090  .0127000   .000000  .000000   .000000  .000000
+    1968.05  -.005100  .016200   .237510  .014100   .0987210  .0143000   .000000  .000000   .000000  .000000
+    1968.10  -.004790  .014500   .238810  .012300  -.0023360  .0129000   .000000  .000000   .000000  .000000
+    1968.15  -.001680  .014200   .237720  .011900  -.0036230  .0123000   .000000  .000000   .000000  .000000
+    1968.20   .030920  .015500   .241420  .013500  -.0012590  .0137000   .000000  .000000   .000000  .000000
+    1968.25   .015230  .013900   .230330  .011900  -.0024360  .0123000   .000000  .000000   .000000  .000000
+    1968.30   .046430  .013500   .242330  .011700  -.0107430  .0121000   .000000  .000000   .000000  .000000
+    1968.35   .060630  .014400   .223740  .012300  -.0118800  .0127000   .000000  .000000   .000000  .000000
+    1968.40   .057340  .014100   .203640  .011900  -.0136970  .0125000   .000000  .000000   .000000  .000000
+    1968.45   .059250  .012900   .198750  .011400  -.0100140  .0119000   .000000  .000000   .000000  .000000
+    1968.50   .068150  .013800   .173950  .012800   .0057390  .0131000   .000000  .000000   .000000  .000000
+    1968.55   .067460  .012900   .166360  .011300   .0170720  .0117000   .000000  .000000   .000000  .000000
+    1968.60   .052060  .013300   .142860  .011400   .0302750  .0120000   .000000  .000000   .000000  .000000
+    1968.65   .027070  .014100   .135670  .012200   .0354390  .0123000   .000000  .000000   .000000  .000000
+    1968.70   .005570  .012700   .135770  .011500   .0390020  .0117000   .000000  .000000   .000000  .000000
+    1968.75  -.013830  .013300   .118780  .011800   .0406750  .0120000   .000000  .000000   .000000  .000000
+    1968.80  -.042020  .013400   .106380  .012200   .0419680  .0123000   .000000  .000000   .000000  .000000
+    1968.85  -.084920  .013700   .124390  .012000   .0356310  .0122000   .000000  .000000   .000000  .000000
+    1968.90  -.139410  .013500   .147490  .011800   .0341150  .0121000   .000000  .000000   .000000  .000000
+    1968.95  -.126810  .014100   .185200  .012800   .0350180  .0130000   .000000  .000000   .000000  .000000
+    1969.00  -.139900  .013900   .214900  .012500   .0335710  .0124000   .000000  .000000   .000000  .000000
+    1969.05  -.124100  .014400   .251100  .013200   .0372640  .0130000   .000000  .000000   .000000  .000000
+    1969.10  -.106290  .014000   .262510  .012300   .0391280  .0124000   .000000  .000000   .000000  .000000
+    1969.15  -.095880  .013200   .326120  .011800   .0309210  .0121000   .000000  .000000   .000000  .000000
+    1969.20  -.079080  .014000   .337320  .012600   .0246540  .0127000   .000000  .000000   .000000  .000000
+    1969.25  -.036380  .013000   .347630  .011600   .0124870  .0121000   .000000  .000000   .000000  .000000
+    1969.30  -.000970  .012900   .351930  .011700   .0000710  .0119000   .000000  .000000   .000000  .000000
+    1969.35   .055030  .013300   .361340  .011900  -.0043960  .0120000   .000000  .000000   .000000  .000000
+    1969.40   .090640  .013200   .316240  .011700  -.0064830  .0120000   .000000  .000000   .000000  .000000
+    1969.45   .125150  .012500   .295450  .011800  -.0097100  .0117000   .000000  .000000   .000000  .000000
+    1969.50   .133450  .013100   .267750  .012100   .0040040  .0121000   .000000  .000000   .000000  .000000
+    1969.55   .150150  .012800   .228760  .011500   .0133570  .0117000   .000000  .000000   .000000  .000000
+    1969.60   .134960  .012500   .187660  .011100   .0232700  .0114000   .000000  .000000   .000000  .000000
+    1969.65   .117370  .013900   .136570  .012200   .0362230  .0124000   .000000  .000000   .000000  .000000
+    1969.70   .095770  .012500   .114170  .011000   .0343170  .0114000   .000000  .000000   .000000  .000000
+    1969.75   .058680  .012700   .100680  .011200   .0285300  .0115000   .000000  .000000   .000000  .000000
+    1969.80   .011380  .013400   .065480  .012200   .0246530  .0122000   .000000  .000000   .000000  .000000
+    1969.85  -.011720  .012800   .063480  .011300   .0178860  .0116000   .000000  .000000   .000000  .000000
+    1969.90  -.062210  .013700   .070390  .011900   .0119090  .0122000   .000000  .000000   .000000  .000000
+    1969.95  -.122410  .013900   .082300  .012300   .0067420  .0122000   .000000  .000000   .000000  .000000
+    1970.00  -.163200  .013900   .117800  .012300   .0047260  .0123000   .000000  .000000   .000000  .000000
+    1970.05  -.200400  .013900   .175210  .012800  -.0043710  .0122000   .000000  .000000   .000000  .000000
+    1970.10  -.217990  .013300   .213110  .012200  -.0072880  .0119000   .000000  .000000   .000000  .000000
+    1970.15  -.167580  .013400   .287720  .012000  -.0178350  .0119000   .000000  .000000   .000000  .000000
+    1970.20  -.155880  .013700   .325820  .013200  -.0272820  .0127000   .000000  .000000   .000000  .000000
+    1970.25  -.140180  .013600   .357830  .012300  -.0373190  .0122000   .000000  .000000   .000000  .000000
+    1970.30  -.092070  .012800   .388230  .011900  -.0515660  .0116000   .000000  .000000   .000000  .000000
+    1970.35  -.036570  .014100   .394540  .012600  -.0614930  .0126000   .000000  .000000   .000000  .000000
+    1970.40   .009940  .012700   .407740  .011300  -.0624500  .0116000   .000000  .000000   .000000  .000000
+    1970.45   .077750  .013100   .394750  .011700  -.0622170  .0120000   .000000  .000000   .000000  .000000
+    1970.50   .130150  .013700   .370950  .012300  -.0542940  .0124000   .000000  .000000   .000000  .000000
+    1970.55   .166660  .012800   .330060  .011600  -.0435010  .0117000   .000000  .000000   .000000  .000000
+    1970.60   .200960  .012700   .262860  .011000  -.0298880  .0113000   .000000  .000000   .000000  .000000
+    1970.65   .205070  .013300   .222970  .011800  -.0204550  .0120000   .000000  .000000   .000000  .000000
+    1970.70   .196270  .012800   .160070  .011500  -.0125620  .0117000   .000000  .000000   .000000  .000000
+    1970.75   .178680  .012600   .101880  .011300  -.0144890  .0115000   .000000  .000000   .000000  .000000
+    1970.80   .141580  .013000   .043980  .011900  -.0185570  .0120000   .000000  .000000   .000000  .000000
+    1970.85   .064680  .012600   .026980  .011300  -.0256240  .0115000   .000000  .000000   .000000  .000000
+    1970.90   .013790  .012900   .006190  .011500  -.0358210  .0115000   .000000  .000000   .000000  .000000
+    1970.95  -.045910  .013500  -.008500  .012300  -.0395080  .0121000   .000000  .000000   .000000  .000000
+    1971.00  -.094800  .013300   .014300  .011800  -.0386560  .0118000   .000000  .000000   .000000  .000000
+    1971.05  -.145000  .013400   .063100  .011700  -.0423130  .0118000   .000000  .000000   .000000  .000000
+    1971.10  -.193590  .013100   .108210  .011500  -.0417900  .0117000   .000000  .000000   .000000  .000000
+    1971.15  -.226190  .012700   .164220  .011100  -.0385280  .0115000   .000000  .000000   .000000  .000000
+    1971.20  -.248080  .012700   .221720  .010900  -.0447450  .0112000   .000000  .000000   .000000  .000000
+    1971.25  -.221480  .012500   .298620  .010800  -.0573030  .0112000   .000000  .000000   .000000  .000000
+    1971.30  -.200070  .012400   .353230  .010700  -.0652400  .0112000   .000000  .000000   .000000  .000000
+    1971.35  -.187870  .013100   .384540  .011400  -.0779880  .0117000   .000000  .000000   .000000  .000000
+    1971.40  -.131860  .013100   .428740  .011000  -.0875360  .0115000   .000000  .000000   .000000  .000000
+    1971.45  -.046460  .012800   .458050  .010900  -.0888030  .0114000   .000000  .000000   .000000  .000000
+    1971.50   .029850  .012700   .466050  .011100  -.0875110  .0113000   .000000  .000000   .000000  .000000
+    1971.55   .104850  .012400   .444960  .010600  -.0828490  .0111000   .000000  .000000   .000000  .000000
+    1971.60   .162960  .012000   .400160  .010200  -.0790560  .0108000   .000000  .000000   .000000  .000000
+    1971.65   .211970  .012600   .361270  .011000  -.0778240  .0114000   .000000  .000000   .000000  .000000
+    1971.70   .233970  .012200   .304470  .010700  -.0821020  .0111000   .000000  .000000   .000000  .000000
+    1971.75   .225880  .012300   .218780  .010600  -.0877900  .0111000   .000000  .000000   .000000  .000000
+    1971.80   .208880  .012600   .167780  .011000  -.1047780  .0114000   .000000  .000000   .000000  .000000
+    1971.85   .181790  .012500   .101490  .010900  -.1220760  .0112000   .000000  .000000   .000000  .000000
+    1971.90   .140590  .012800   .071290  .011000  -.1384440  .0112000   .000000  .000000   .000000  .000000
+    1971.95   .084400  .014100   .039100  .012000  -.1490720  .0119000   .000000  .000000   .000000  .000000
+    1972.00   .031820  .013570   .019710  .011210  -.0474900  .0033800   .000000  .000000   .000000  .000000
+    1972.05  -.029350  .012770   .019540  .010910  -.1025340  .0037900   .000000  .000000   .000000  .000000
+    1972.10  -.085000  .013210   .023390  .011220  -.1641870  .0035000   .000000  .000000   .000000  .000000
+    1972.15  -.113320  .012570   .038780  .010590  -.2239540  .0021800   .000000  .000000   .000000  .000000
+    1972.20  -.170800  .012180   .100130  .010510  -.2844970  .0034200   .000000  .000000   .000000  .000000
+    1972.25  -.178190  .013190   .157660  .011180  -.3489940  .0024900   .000000  .000000   .000000  .000000
+    1972.30  -.193440  .012520   .216340  .010780  -.4137270  .0029400   .000000  .000000   .000000  .000000
+    1972.35  -.177900  .012220   .273960  .010460  -.4751870  .0043600   .000000  .000000   .000000  .000000
+    1972.40  -.148990  .013040   .302730  .011340  -.5400640  .0031600   .000000  .000000   .000000  .000000
+    1972.45  -.101340  .012210   .350600  .010680  -.5937930  .0110200   .000000  .000000   .000000  .000000
+    1972.50  -.048730  .012910   .379110  .010840   .3591600  .0042800   .000000  .000000   .000000  .000000
+    1972.55   .000850  .012690   .403050  .010910   .3124850  .0029100   .000000  .000000   .000000  .000000
+    1972.60   .057740  .012190   .404590  .010360   .2662670  .0026800   .000000  .000000   .000000  .000000
+    1972.65   .089870  .012660   .408890  .010920   .2208620  .0023600   .000000  .000000   .000000  .000000
+    1972.70   .119360  .012130   .367470  .010410   .1685090  .0057300   .000000  .000000   .000000  .000000
+    1972.75   .125150  .012250   .330830  .010670   .1107640  .0028700   .000000  .000000   .000000  .000000
+    1972.80   .161790  .013110   .292310  .011320   .0507720  .0115700   .000000  .000000   .000000  .000000
+    1972.85   .164080  .012730   .229730  .010910  -.0089650  .0112300   .000000  .000000   .000000  .000000
+    1972.90   .154490  .012570   .213280  .010660  -.0670860  .0029200   .000000  .000000   .000000  .000000
+    1972.95   .142830  .013030   .172430  .011240  -.1269170  .0037200   .000000  .000000   .000000  .000000
+    1973.00   .123610  .010590   .125710  .007920   .8114390  .0113200   .000000  .000000   .000000  .000000
+    1973.05   .091980  .008970   .093390  .007760   .7545430  .0028400   .000000  .000000   .000000  .000000
+    1973.10   .055100  .010360   .086390  .007830   .6973530  .0116100   .000000  .000000   .000000  .000000
+    1973.15   .024830  .008970   .089030  .007800   .6317400  .0036200   .000000  .000000   .000000  .000000
+    1973.20  -.023270  .009910   .093100  .007690   .5731350  .0039400   .000000  .000000   .000000  .000000
+    1973.25  -.050430  .010230   .105840  .007750   .5070570  .0041400   .000000  .000000   .000000  .000000
+    1973.30  -.090950  .010050   .145940  .007670   .4422160  .0030600   .000000  .000000   .000000  .000000
+    1973.35  -.107840  .007540   .174450  .006420   .3817300  .0030300   .000000  .000000   .000000  .000000
+    1973.40  -.116560  .007550   .201930  .005940   .3252920  .0036100   .000000  .000000   .000000  .000000
+    1973.45  -.109350  .011160   .245230  .005950   .2701480  .0031200   .000000  .000000   .000000  .000000
+    1973.50  -.097490  .007510   .273170  .005970   .2259990  .0034000   .000000  .000000   .000000  .000000
+    1973.55  -.074020  .009970   .300780  .007710   .1814520  .0071400   .000000  .000000   .000000  .000000
+    1973.60  -.053250  .009780   .315260  .007550   .1350890  .0030500   .000000  .000000   .000000  .000000
+    1973.65  -.014030  .009960   .319880  .007620   .0897480  .0035100   .000000  .000000   .000000  .000000
+    1973.70   .014500  .009970   .323150  .007660   .0381470  .0060700   .000000  .000000   .000000  .000000
+    1973.75   .034570  .009690   .333410  .006090  -.0166510  .0063800   .000000  .000000   .000000  .000000
+    1973.80   .069270  .007630   .331530  .007560  -.0721010  .0033300   .000000  .000000   .000000  .000000
+    1973.85   .082930  .008760   .288210  .007590  -.1313010  .0024900   .000000  .000000   .000000  .000000
+    1973.90   .094550  .009970   .270630  .007570  -.1950090  .0106800   .000000  .000000   .000000  .000000
+    1973.95   .105000  .009050   .265120  .007810  -.2487100  .0023500   .000000  .000000   .000000  .000000
+    1974.00   .113200  .010100   .225230  .007730   .7002310  .0073000   .000000  .000000   .000000  .000000
+    1974.05   .091980  .011510   .203370  .007650   .6520660  .0043200   .000000  .000000   .000000  .000000
+    1974.10   .077870  .008970   .176520  .007820   .6063570  .0031800   .000000  .000000   .000000  .000000
+    1974.15   .078890  .009940   .171530  .007620   .5537260  .0069500   .000000  .000000   .000000  .000000
+    1974.20   .050750  .010050   .157750  .007660   .4983770  .0044100   .000000  .000000   .000000  .000000
+    1974.25   .029950  .007680   .179730  .006060   .4438800  .0107400   .000000  .000000   .000000  .000000
+    1974.30   .029220  .009390   .187280  .007720   .3871170  .0107700   .000000  .000000   .000000  .000000
+    1974.35   .015470  .009500   .182320  .007730   .3220300  .0031000   .000000  .000000   .000000  .000000
+    1974.40   .005080  .009580   .179910  .007850   .2706650  .0115400   .000000  .000000   .000000  .000000
+    1974.45  -.016260  .009460   .187160  .007750   .2230120  .0031700   .000000  .000000   .000000  .000000
+    1974.50   .019490  .009480   .195670  .007770   .1828640  .0025100   .000000  .000000   .000000  .000000
+    1974.55   .022340  .009570   .210720  .007760   .1474170  .0038500   .000000  .000000   .000000  .000000
+    1974.60   .019950  .009360   .212310  .007650   .1109470  .0031300   .000000  .000000   .000000  .000000
+    1974.65   .016610  .009410   .209630  .007650   .0663730  .0027200   .000000  .000000   .000000  .000000
+    1974.70   .010980  .009550   .202280  .007770   .0252920  .0024300   .000000  .000000   .000000  .000000
+    1974.75   .003730  .009660   .216240  .007790  -.0251410  .0033500   .000000  .000000   .000000  .000000
+    1974.80  -.012130  .008500   .232720  .007750  -.0802570  .0059400   .000000  .000000   .000000  .000000
+    1974.85  -.024480  .009820   .240310  .007970  -.1325930  .0041100   .000000  .000000   .000000  .000000
+    1974.90  -.033410  .009690   .254200  .007970  -.1905590  .0028600   .000000  .000000   .000000  .000000
+    1974.95  -.043730  .009750   .267890  .008040  -.2441870  .0086400   .000000  .000000   .000000  .000000
+    1975.00  -.050120  .008590   .270650  .008000   .7083150  .0113300   .000000  .000000   .000000  .000000
+    1975.05  -.053700  .008370   .285590  .007710   .6585310  .0049400   .000000  .000000   .000000  .000000
+    1975.10  -.030920  .008550   .306400  .008030   .6055510  .0112100   .000000  .000000   .000000  .000000
+    1975.15  -.019500  .009350   .329540  .007790   .5525990  .0035600   .000000  .000000   .000000  .000000
+    1975.20   .010660  .009430   .335300  .007810   .5013940  .0068100   .000000  .000000   .000000  .000000
+    1975.25   .043890  .009600   .337490  .007980   .4422900  .0026300   .000000  .000000   .000000  .000000
+    1975.30   .070800  .009650   .313520  .007850   .3887900  .0025400   .000000  .000000   .000000  .000000
+    1975.35   .089170  .009480   .296380  .007840   .3321200  .0029200   .000000  .000000   .000000  .000000
+    1975.40   .103620  .009630   .270190  .007850   .2824180  .0024800   .000000  .000000   .000000  .000000
+    1975.45   .125630  .009450   .246550  .007760   .2386220  .0028800   .000000  .000000   .000000  .000000
+    1975.50   .146030  .009560   .234320  .007940   .1972230  .0039100   .000000  .000000   .000000  .000000
+    1975.55   .132900  .009470   .199120  .007760   .1637560  .0032000   .000000  .000000   .000000  .000000
+    1975.60   .126320  .009330   .176400  .007760   .1307220  .0105700   .000000  .000000   .000000  .000000
+    1975.65   .119740  .009380   .152660  .007800   .0878550  .0028400   .000000  .000000   .000000  .000000
+    1975.70   .092300  .009530   .118640  .007850   .0444550  .0039100   .000000  .000000   .000000  .000000
+    1975.75   .062280  .009540   .103450  .007720  -.0020730  .0026100   .000000  .000000   .000000  .000000
+    1975.80   .020950  .009400   .098770  .007780  -.0586960  .0024800   .000000  .000000   .000000  .000000
+    1975.85  -.041700  .009490   .103980  .007900  -.1161850  .0027700   .000000  .000000   .000000  .000000
+    1975.90  -.072420  .009650   .130280  .007910  -.1711520  .0025700   .000000  .000000   .000000  .000000
+    1975.95  -.127410  .008520   .157660  .007880  -.2229470  .0032300   .000000  .000000   .000000  .000000
+    1976.00  -.144340  .009600   .193270  .008000   .7251490  .0105200   .000000  .000000   .000000  .000000
+    1976.05  -.151500  .008420   .247180  .007820   .6775030  .0033100   .000000  .000000   .000000  .000000
+    1976.10  -.156180  .009510   .289100  .007920   .6214200  .0066400   .000000  .000000   .000000  .000000
+    1976.15  -.145290  .009630   .329230  .007930   .5693700  .0037300   .000000  .000000   .000000  .000000
+    1976.20  -.117400  .009560   .367510  .007930   .5147140  .0046300   .000000  .000000   .000000  .000000
+    1976.25  -.095400  .009540   .393190  .007880   .4538780  .0109900   .000000  .000000   .000000  .000000
+    1976.30  -.041730  .009490   .425870  .007790   .3908040  .0060800   .000000  .000000   .000000  .000000
+    1976.35  -.008800  .007870   .433170  .006840   .3343790  .0037900   .000000  .000000   .000000  .000000
+    1976.40   .046660  .004470   .423870  .006980   .2739120  .0041400   .000000  .000000   .000000  .000000
+    1976.45   .111730  .004720   .401030  .006880   .2245910  .0032300   .000000  .000000   .000000  .000000
+    1976.50   .156480  .005240   .370970  .006350   .1856130  .0037500   .000000  .000000   .000000  .000000
+    1976.55   .195270  .005570   .330200  .007560   .1405230  .0072500   .000000  .000000   .000000  .000000
+    1976.60   .229060  .008910   .289950  .009410   .0972370  .0027300   .000000  .000000   .000000  .000000
+    1976.65   .235790  .007970   .241180  .006750   .0569540  .0066700   .000000  .000000   .000000  .000000
+    1976.70   .220780  .008280   .189190  .006630   .0056210  .0033300   .000000  .000000   .000000  .000000
+    1976.75   .172600  .004740   .157950  .005040  -.0542780  .0057200   .000000  .000000   .000000  .000000
+    1976.80   .159550  .003200   .117960  .003370  -.1129530  .0032600   .000000  .000000   .000000  .000000
+    1976.85   .113500  .003710   .093390  .003730  -.1756720  .0035700   .000000  .000000   .000000  .000000
+    1976.90   .053590  .003540   .071240  .003560  -.2292200  .0041000   .000000  .000000   .000000  .000000
+    1976.95  -.004070  .003990   .065610  .003690  -.2824520  .0048500   .000000  .000000   .000000  .000000
+    1977.00  -.068560  .004880   .069220  .004230   .6657090  .0036700   .000000  .000000   .000000  .000000
+    1977.05  -.139290  .004730   .118540  .004370   .6143360  .0051800   .000000  .000000   .000000  .000000
+    1977.10  -.188740  .004460   .156640  .003860   .5651410  .0031900   .000000  .000000   .000000  .000000
+    1977.15  -.226790  .006250   .231150  .004950   .5121100  .0031000   .000000  .000000   .000000  .000000
+    1977.20  -.228460  .005680   .290850  .004120   .4600880  .0068900   .000000  .000000   .000000  .000000
+    1977.25  -.216780  .003080   .362450  .002890   .4021850  .0021700   .000000  .000000   .000000  .000000
+    1977.30  -.174880  .003600   .414280  .003440   .3418380  .0045300   .000000  .000000   .000000  .000000
+    1977.35  -.133880  .004150   .458560  .003960   .2827490  .0051900   .000000  .000000   .000000  .000000
+    1977.40  -.071690  .010490   .489080  .009550   .2309290  .0023600   .000000  .000000   .000000  .000000
+    1977.45   .012030  .003540   .504510  .003880   .1838870  .0041000   .000000  .000000   .000000  .000000
+    1977.50   .082770  .002520   .495390  .002840   .1468180  .0018000   .000000  .000000   .000000  .000000
+    1977.55   .149380  .003370   .454360  .003620   .1142770  .0019800   .000000  .000000   .000000  .000000
+    1977.60   .210730  .003320   .410780  .003600   .0768320  .0019700   .000000  .000000   .000000  .000000
+    1977.65   .249250  .004220   .351780  .004520   .0408590  .0024600   .000000  .000000   .000000  .000000
+    1977.70   .269380  .002950   .292600  .003710  -.0085350  .0021100   .000000  .000000   .000000  .000000
+    1977.75   .263020  .003500   .222970  .003510  -.0636710  .0025300   .000000  .000000   .000000  .000000
+    1977.80   .243710  .003790   .151260  .003990  -.1196200  .0033400   .000000  .000000   .000000  .000000
+    1977.85   .195430  .003850   .095930  .004190  -.1771710  .0026900   .000000  .000000   .000000  .000000
+    1977.90   .133680  .003770   .061560  .003970  -.2363140  .0019400   .000000  .000000   .000000  .000000
+    1977.95   .063460  .003070   .030340  .003390  -.2912280  .0026400   .000000  .000000   .000000  .000000
+    1978.00  -.000350  .006860   .027050  .006710   .6495220  .0045600   .000000  .000000   .000000  .000000
+    1978.05  -.067600  .005140   .036990  .007410   .5903240  .0049700   .000000  .000000   .000000  .000000
+    1978.10  -.136660  .005210   .072860  .006600   .5357610  .0070500   .000000  .000000   .000000  .000000
+    1978.15  -.182100  .005230   .105900  .006590   .4701360  .0039900   .000000  .000000   .000000  .000000
+    1978.20  -.214500  .003140   .163140  .003850   .4061470  .0056400   .000000  .000000   .000000  .000000
+    1978.25  -.236230  .003270   .234600  .003050   .3442840  .0041700   .000000  .000000   .000000  .000000
+    1978.30  -.229900  .003970   .312160  .003250   .2835510  .0022700   .000000  .000000   .000000  .000000
+    1978.35  -.200940  .002850   .379730  .002600   .2243820  .0069100   .000000  .000000   .000000  .000000
+    1978.40  -.157910  .004050   .433860  .003990   .1677820  .0022300   .000000  .000000   .000000  .000000
+    1978.45  -.110240  .003970   .468530  .003570   .1209930  .0031800   .000000  .000000   .000000  .000000
+    1978.50  -.044260  .003530   .492280  .003560   .0799780  .0027100   .000000  .000000   .000000  .000000
+    1978.55   .033760  .002730   .496900  .002830   .0461430  .0025300   .000000  .000000   .000000  .000000
+    1978.60   .098530  .002640   .478150  .002760   .0128410  .0034000   .000000  .000000   .000000  .000000
+    1978.65   .150640  .001940   .444140  .002040  -.0312900  .0030700   .000000  .000000   .000000  .000000
+    1978.70   .194320  .002790   .408640  .002980  -.0754340  .0036100   .000000  .000000   .000000  .000000
+    1978.75   .221790  .002910   .353150  .003140  -.1252120  .0037900   .000000  .000000   .000000  .000000
+    1978.80   .234780  .002740   .290520  .002920  -.1826230  .0020000   .000000  .000000   .000000  .000000
+    1978.85   .228930  .003190   .229200  .003160  -.2362770  .0029900   .000000  .000000   .000000  .000000
+    1978.90   .196110  .004580   .176960  .004140  -.2927440  .0029900   .000000  .000000   .000000  .000000
+    1978.95   .169350  .004230   .117020  .004390  -.3501350  .0030400   .000000  .000000   .000000  .000000
+    1979.00   .140600  .004850   .085500  .004540   .5990550  .0092100   .000000  .000000   .000000  .000000
+    1979.05   .098400  .006120   .059560  .005460   .5436940  .0079200   .000000  .000000   .000000  .000000
+    1979.10   .023200  .003360   .064460  .003420   .4873690  .0025000   .000000  .000000   .000000  .000000
+    1979.15  -.017580  .003950   .069020  .004470   .4372970  .0035200   .000000  .000000   .000000  .000000
+    1979.20  -.067290  .004260   .087350  .004270   .3838980  .0025100   .000000  .000000   .000000  .000000
+    1979.25  -.111680  .003470   .125490  .003760   .3253590  .0027200   .000000  .000000   .000000  .000000
+    1979.30  -.137550  .002330   .174350  .002950   .2698590  .0018600   .000000  .000000   .000000  .000000
+    1979.35  -.152140  .002870   .218350  .003250   .2124640  .0021400   .000000  .000000   .000000  .000000
+    1979.40  -.158240  .002720   .263800  .003010   .1596610  .0032200   .000000  .000000   .000000  .000000
+    1979.45  -.145410  .003090   .301690  .003070   .1196450  .0029900   .000000  .000000   .000000  .000000
+    1979.50  -.120340  .002530   .348910  .003130   .0786440  .0064600   .000000  .000000   .000000  .000000
+    1979.55  -.093020  .002590   .378590  .002700   .0405690  .0031000   .000000  .000000   .000000  .000000
+    1979.60  -.032280  .004930   .404160  .004640   .0082610  .0040400   .000000  .000000   .000000  .000000
+    1979.65   .013040  .003750   .420310  .003990  -.0308800  .0025600   .000000  .000000   .000000  .000000
+    1979.70   .053770  .002720   .423000  .003330  -.0723390  .0024500   .000000  .000000   .000000  .000000
+    1979.75   .087730  .002540   .410920  .002940  -.1151000  .0041800   .000000  .000000   .000000  .000000
+    1979.80   .103920  .001630   .386840  .001740  -.1669670  .0022100   .000000  .000000   .000000  .000000
+    1979.85   .133930  .003380   .357770  .003350  -.2160390  .0041200   .000000  .000000   .000000  .000000
+    1979.90   .132630  .001940   .322640  .002410  -.2606420  .0032900   .000000  .000000   .000000  .000000
+    1979.95   .135250  .001860   .285040  .001910  -.3086520  .0027600   .000000  .000000   .000000  .000000
+    1980.00   .142150  .002360   .250260  .002550   .6449390  .0010700   .000000  .000000   .000000  .000000
+    1980.05   .121150  .002430   .224530  .002000   .5994770  .0011100   .000000  .000000   .000000  .000000
+    1980.10   .089390  .002000   .199240  .001640   .5548120  .0012400   .000000  .000000   .000000  .000000
+    1980.15   .064250  .001990   .182700  .001610   .5085250  .0009700   .000000  .000000   .000000  .000000
+    1980.20   .043590  .001540   .179240  .001360   .4666600  .0011300   .000000  .000000   .000000  .000000
+    1980.25   .012160  .001970   .187460  .001550   .4165130  .0012000   .000000  .000000   .000000  .000000
+    1980.30  -.020440  .001920   .201760  .002020   .3666940  .0005600   .012390  .001200  -.003550  .000350
+    1980.35  -.043480  .002040   .221870  .001830   .3219240  .0014400   .000000  .000000   .000000  .000000
+    1980.40  -.052250  .001920   .240890  .001830   .2763230  .0012400   .000000  .000000   .000000  .000000
+    1980.45  -.054760  .001770   .259160  .001660   .2387700  .0013700   .000000  .000000   .000000  .000000
+    1980.50  -.051620  .001940   .282750  .001860   .2063630  .0015100   .000000  .000000   .000000  .000000
+    1980.55  -.039350  .001980   .301970  .001810   .1755970  .0002600   .002900  .000800  -.003360  .000230
+    1980.60  -.035550  .001470   .314750  .001410   .1452760  .0009200   .000000  .000000   .000000  .000000
+    1980.65  -.030090  .001510   .328120  .001390   .1151990  .0009800   .000000  .000000   .000000  .000000
+    1980.70  -.030200  .001490   .337170  .001410   .0750790  .0009700   .000000  .000000   .000000  .000000
+    1980.75  -.023160  .001500   .345640  .001450   .0300980  .0001600   .000590  .000360  -.002620  .000120
+    1980.80  -.012370  .001400   .358870  .001410  -.0127940  .0001400   .004850  .000300  -.001960  .000100
+    1980.85   .000830  .001430   .366600  .001360  -.0624580  .0012400  -.000310  .003340  -.001500  .000790
+    1980.90   .022640  .001990   .373160  .001740  -.1078580  .0004200   .007390  .001200  -.001790  .000320
+    1980.95   .049120  .002020   .373030  .001700  -.1500100  .0003600   .010830  .001030  -.000520  .000270
+    1981.00   .070580  .001670   .360660  .001840  -.1953270  .0040700   .004000  .002680  -.000470  .000650
+    1981.05   .077000  .001510   .348400  .001680  -.2391430  .0005100   .007830  .001280  -.000010  .000380
+    1981.10   .089260  .001270   .329090  .001060  -.2776990  .0011000   .010230  .002700  -.000250  .000960
+    1981.15   .084840  .001010   .315780  .001020  -.3202690  .0007000   .011050  .002700   .000040  .000580
+    1981.20   .090210  .000790   .304230  .000800  -.3727280  .0013700   .008890  .002840  -.002880  .000900
+    1981.25   .101630  .000890   .289720  .000940  -.4159950  .0016800   .000000  .000000   .000000  .000000
+    1981.30   .096310  .000870   .268760  .001200  -.4685540  .0013900   .000000  .000000   .000000  .000000
+    1981.35   .093750  .000980   .257220  .000920  -.5165520  .0015500   .010480  .004480  -.003860  .001390
+    1981.40   .090250  .001110   .241670  .001230  -.5587390  .0015600   .000000  .000000   .000000  .000000
+    1981.45   .078570  .000850   .226280  .001070  -.6009320  .0004300   .002930  .000690  -.003700  .000200
+    1981.50   .068150  .000810   .210210  .000810   .3698510  .0014900   .002610  .002490  -.004120  .000620
+    1981.55   .059540  .000770   .193490  .000780   .3498320  .0013400   .000750  .003130  -.002280  .000880
+    1981.60   .033080  .000950   .184820  .001110   .3198500  .0017100  -.003550  .003350  -.003460  .000910
+    1981.65   .004150  .000740   .185370  .000750   .2918900  .0012000   .000910  .002860  -.004060  .000710
+    1981.70  -.024140  .000710   .193490  .000710   .2653000  .0011700  -.005840  .002480  -.002860  .000720
+    1981.75  -.060920  .000710   .209640  .000700   .2228820  .0011100  -.005830  .002680  -.000670  .000950
+    1981.80  -.084890  .000690   .235550  .000670   .1804970  .0005200  -.001860  .001480  -.001690  .000470
+    1981.85  -.104520  .000780   .264190  .000800   .1393580  .0013800   .005070  .001970  -.000530  .000530
+    1981.90  -.110340  .001250   .298730  .000870   .0957520  .0002200   .001260  .000360   .000660  .000120
+    1981.95  -.112180  .001070   .336860  .000790   .0571520  .0020500   .006620  .002610   .000650  .000690
+    1982.00  -.093080  .001000   .376820  .000860   .0184280  .0017100   .005820  .001770   .001000  .000510
+    1982.05  -.056710  .001150   .406900  .000920  -.0233770  .0024400   .001600  .001660   .001710  .000480
+    1982.10  -.029250  .000960   .429470  .000810  -.0576400  .0017900   .004510  .002200   .001450  .000580
+    1982.15   .016930  .000960   .440390  .000860  -.0968960  .0018300   .002170  .002170   .000850  .000570
+    1982.20   .062800  .000970   .438790  .000820  -.1429420  .0004100   .004220  .001240  -.000430  .000340
+    1982.25   .108360  .000970   .429570  .000940  -.1860770  .0023100   .004830  .002410  -.000950  .000720
+    1982.30   .144250  .000790   .410160  .000790  -.2324500  .0006000   .007640  .001680  -.002570  .000390
+    1982.35   .175760  .000710   .379360  .000750  -.2826500  .0013700   .004140  .002320  -.002400  .000650
+    1982.40   .202440  .000650   .335180  .000690  -.3212050  .0009700   .006000  .002100  -.002670  .000570
+    1982.45   .227390  .000730   .284220  .000920  -.3589540  .0001600   .001880  .000460  -.001670  .000140
+    1982.50   .225400  .000620   .235390  .000680   .6073090  .0009800  -.003910  .002750  -.001360  .000780
+    1982.55   .211690  .000650   .183440  .000650   .5824430  .0010100  -.000930  .002400  -.001680  .000670
+    1982.60   .188000  .000700   .139540  .000730   .5595280  .0011600  -.003430  .003010   .000260  .000800
+    1982.65   .147580  .000710   .100920  .000670   .5266190  .0012700  -.004690  .002940  -.001270  .000780
+    1982.70   .090980  .000660   .077680  .000680   .4890550  .0005400  -.005130  .001680  -.000640  .000510
+    1982.75   .028040  .000650   .068920  .000670   .4484150  .0012400  -.008690  .003000   .000710  .000970
+    1982.80  -.036070  .000640   .079650  .000610   .4039600  .0002700  -.002760  .000740  -.000180  .000200
+    1982.85  -.092580  .000660   .099410  .000700   .3645380  .0009400  -.001670  .001640   .000520  .000530
+    1982.90  -.143280  .000890   .139280  .000840   .3204710  .0017700  -.000420  .002110   .002190  .000590
+    1982.95  -.184870  .000690   .194480  .000690   .2722880  .0001700   .000730  .000420   .002030  .000130
+    1983.00  -.202510  .000710   .257960  .000670   .2276730  .0011300   .004010  .001640   .002770  .000480
+    1983.05  -.208590  .000690   .319420  .000710   .1807080  .0011500   .002530  .001490   .002220  .000410
+    1983.10  -.188080  .000740   .386630  .000780   .1215960  .0004400   .002640  .000960   .001590  .000270
+    1983.15  -.166980  .000740   .446850  .000760   .0712980  .0004000   .007800  .001060   .000500  .000310
+    1983.20  -.130670  .000690   .499770  .000680   .0173990  .0004000   .004030  .001030   .000410  .000290
+    1983.25  -.060990  .000680   .541150  .000730  -.0393690  .0010800   .002480  .002210  -.000190  .000610
+    1983.30   .018010  .000730   .561080  .000730  -.0893990  .0010700   .005370  .001500  -.001230  .000430
+    1983.35   .089520  .000660   .551750  .000890  -.1353850  .0003100   .005970  .000760  -.001240  .000230
+    1983.40   .165160  .000700   .528540  .000850  -.1815520  .0010800   .003960  .002480  -.001540  .000630
+    1983.45   .220450  .000760   .481900  .000930  -.2193950  .0003800   .002630  .000840  -.001270  .000240
+    1983.50   .270130  .000770   .421220  .000920   .7475800  .0010100  -.003070  .001710   .000180  .000430
+    1983.55   .307330  .000670   .346400  .000780   .7215090  .0006600  -.006430  .001190  -.000660  .000270
+    1983.60   .323430  .000710   .271320  .000770   .6952120  .0007200  -.004220  .001220  -.000850  .000320
+    1983.65   .311990  .000680   .193620  .000840   .6642850  .0008600  -.006240  .001200  -.000820  .000310
+    1983.70   .270820  .000560   .123360  .000640   .6298510  .0008700  -.008450  .001710  -.000360  .000500
+    1983.75   .212870  .000570   .065270  .000690   .5983220  .0004400  -.006270  .000840  -.000160  .000250
+    1983.80   .144260  .000200   .027540  .000210   .5583590  .0004000  -.007880  .001040   .001710  .000580
+    1983.85   .061990  .000220   .014950  .000200   .5132280  .0059100  -.004500  .000960   .002060  .000720
+    1983.90  -.016890  .000210   .026220  .000210   .4716860  .0022000   .000610  .001070   .004000  .000750
+    1983.95  -.085210  .000200   .050470  .000200   .4337780  .0004600   .000050  .000660   .003990  .000490
+    1984.00  -.133280  .000230   .091680  .000230   .3954610  .0002500   .001240  .000900   .002650  .000490
+    1984.05  -.178600  .000200   .145720  .000190   .3663980  .0002200   .002630  .000710   .003010  .000320
+    1984.10  -.217680  .000210   .203540  .000210   .3393920  .0002100   .001960  .000650   .003760  .000390
+    1984.15  -.239790  .000210   .267220  .000190   .3071050  .0002200   .001970  .000640   .002560  .000280
+    1984.20  -.240330  .000210   .342950  .000210   .2769000  .0001600   .004310  .000360   .001640  .000190
+    1984.25  -.215180  .000210   .414620  .000210   .2386810  .0002000   .003990  .000530   .001510  .000320
+    1984.30  -.165880  .000180   .478570  .000170   .2012160  .0001500   .004670  .000270  -.000090  .000160
+    1984.35  -.103940  .000200   .529520  .000180   .1686240  .0001900   .005600  .000380  -.000670  .000250
+    1984.40  -.036610  .000170   .557690  .000160   .1381370  .0001700   .002970  .000330  -.000030  .000230
+    1984.45   .043530  .000180   .559030  .000170   .1142480  .0001700   .000400  .000350  -.000580  .000220
+    1984.50   .118910  .000170   .544370  .000170   .0977110  .0001500  -.000110  .000220  -.000540  .000130
+    1984.55   .192030  .000170   .505220  .000160   .0854250  .0001600  -.004690  .000250   .000080  .000150
+    1984.60   .256550  .000160   .452690  .000150   .0683640  .0001300  -.006200  .000210  -.000330  .000110
+    1984.65   .294920  .000130   .386890  .000130   .0497870  .0000800  -.007620  .000160  -.000590  .000090
+    1984.70   .308190  .000180   .317250  .000170   .0228220  .0001600  -.008140  .000320   .000970  .000190
+    1984.75   .311850  .000180   .246370  .000170  -.0075050  .0001700  -.009280  .000330   .000570  .000200
+    1984.80   .284570  .000170   .180700  .000160  -.0363910  .0001600  -.004020  .000290   .001860  .000200
+    1984.85   .247250  .000180   .123390  .000170  -.0715010  .0001600  -.004470  .000300   .002490  .000220
+    1984.90   .198900  .000180   .081750  .000170  -.1028830  .0001700  -.001400  .000320   .003070  .000250
+    1984.95   .127310  .000170   .041390  .000160  -.1301440  .0001500  -.000720  .000240   .003610  .000160
+    1985.00   .045080  .000180   .022560  .000170  -.1575460  .0001400   .000660  .000240   .004680  .000130
+    1985.05  -.021710  .000190   .036290  .000170  -.1889130  .0001500   .001040  .000300   .003940  .000130
+    1985.10  -.079030  .000170   .063220  .000160  -.2116370  .0001200   .002930  .000220   .003730  .000110
+    1985.15  -.133690  .000170   .102890  .000170  -.2407380  .0001200   .001780  .000220   .003340  .000110
+    1985.20  -.185320  .000170   .156740  .000160  -.2736890  .0001200   .002900  .000210   .000710  .000120
+    1985.25  -.201050  .000160   .230230  .000150  -.3070830  .0001200   .006560  .000200   .000520  .000110
+    1985.30  -.195870  .000170   .298580  .000150  -.3429910  .0001200   .005330  .000210   .000700  .000130
+    1985.35  -.182010  .000140   .364520  .000130  -.3771130  .0001000   .005260  .000180  -.000440  .000090
+    1985.40  -.138460  .000170   .418650  .000160  -.4025590  .0001300   .004820  .000210  -.000700  .000130
+    1985.45  -.095300  .000130   .459340  .000130  -.4317500  .0000800   .001180  .000170  -.000230  .000090
+    1985.50  -.043950  .000150   .483270  .000150   .5478600  .0001200  -.000110  .000190   .000060  .000110
+    1985.55   .017740  .000140   .494700  .000140   .5395290  .0001100  -.003020  .000180  -.000640  .000090
+    1985.60   .076740  .000150   .494160  .000140   .5251140  .0001200  -.006370  .000190   .000200  .000100
+    1985.65   .135950  .000150   .477470  .000150   .5115900  .0001300  -.008700  .000240  -.000310  .000120
+    1985.70   .182620  .000150   .445550  .000150   .4948280  .0001200  -.007150  .000210  -.000090  .000120
+    1985.75   .211380  .000150   .402010  .000140   .4660470  .0001200  -.008410  .000190   .001000  .000100
+    1985.80   .227170  .000160   .347600  .000150   .4338690  .0001200  -.006020  .000210   .000470  .000140
+    1985.85   .228420  .000150   .297330  .000150   .4018540  .0001200  -.003090  .000210   .001620  .000130
+    1985.90   .228310  .000130   .251090  .000130   .3668480  .0000800  -.003190  .000170   .003280  .000100
+    1985.95   .209470  .000170   .210230  .000160   .3364070  .0001400  -.002280  .000240   .003710  .000160
+    1986.00   .185940  .000180   .170580  .000170   .3132290  .0001600   .000210  .000280   .003660  .000180
+    1986.05   .148620  .000140   .136920  .000130   .2869110  .0001200  -.001040  .000190   .004400  .000100
+    1986.10   .097820  .000140   .120120  .000130   .2595480  .0001100  -.001060  .000190   .003540  .000090
+    1986.15   .050650  .000150   .114890  .000150   .2343500  .0001200   .000210  .000240   .002680  .000120
+    1986.20  -.004480  .000160   .120220  .000150   .2069080  .0001300   .001510  .000200   .002050  .000120
+    1986.25  -.045110  .000140   .137800  .000130   .1831500  .0001000   .001580  .000180   .000470  .000100
+    1986.30  -.074620  .000150   .174240  .000150   .1553600  .0001200   .005500  .000210   .000020  .000130
+    1986.35  -.091050  .000150   .210930  .000140   .1220280  .0001100   .003860  .000180  -.000070  .000100
+    1986.40  -.103510  .000170   .248370  .000160   .0984900  .0001400   .003140  .000240  -.000240  .000160
+    1986.45  -.097870  .000130   .286570  .000130   .0863460  .0000800   .002230  .000170   .000410  .000100
+    1986.50  -.071490  .000150   .325960  .000150   .0698390  .0001100  -.003030  .000180   .000210  .000100
+    1986.55  -.044780  .000140   .356680  .000140   .0591900  .0001000  -.004290  .000160   .000190  .000080
+    1986.60  -.017440  .000150   .376640  .000140   .0494220  .0001100  -.007430  .000170   .000050  .000090
+    1986.65   .010780  .000180   .391720  .000170   .0327720  .0001500  -.010920  .000310  -.000160  .000170
+    1986.70   .039560  .000160   .398150  .000150   .0179060  .0001200  -.008650  .000190   .000010  .000110
+    1986.75   .061300  .000170   .393670  .000160  -.0068750  .0001500  -.006850  .000330   .001200  .000200
+    1986.80   .085600  .000140   .389850  .000130  -.0389540  .0001000  -.006290  .000180   .001170  .000100
+    1986.85   .108410  .000130   .379180  .000130  -.0658570  .0000800  -.003770  .000160   .001570  .000090
+    1986.90   .121680  .000160   .358470  .000150  -.0899120  .0001200  -.001090  .000210   .002640  .000150
+    1986.95   .139690  .000150   .333040  .000140  -.1163700  .0001100  -.002590  .000190   .002420  .000110
+    1987.00   .146260  .000160   .313070  .000150  -.1380760  .0001100  -.001700  .000210   .002690  .000120
+    1987.05   .139250  .000160   .286810  .000140  -.1601160  .0001100  -.001080  .000190   .003260  .000100
+    1987.10   .129080  .000130   .265690  .000120  -.1865420  .0001100  -.002300  .000160   .002650  .000090
+    1987.15   .122130  .000130   .247910  .000120  -.2129500  .0001000  -.000630  .000160   .001850  .000090
+    1987.20   .110320  .000140   .223500  .000140  -.2463650  .0001100  -.000050  .000190   .001220  .000100
+    1987.25   .094220  .000140   .212870  .000130  -.2807030  .0001000  -.000080  .000170   .000190  .000090
+    1987.30   .072280  .000140   .204670  .000140  -.3086560  .0001100   .001600  .000180  -.000620  .000100
+    1987.35   .053320  .000130   .200540  .000130  -.3362570  .0001000   .002010  .000170  -.000810  .000090
+    1987.40   .030170  .000150   .199490  .000150  -.3639160  .0001100   .001280  .000190  -.000580  .000110
+    1987.45   .015310  .000140   .199420  .000140  -.3832670  .0001100  -.000210  .000180  -.000410  .000110
+    1987.50  -.006420  .000130   .206770  .000130  -.3985660  .0000800  -.002110  .000160   .000130  .000090
+    1987.55  -.025810  .000130   .218770  .000130  -.4124200  .0001000  -.006670  .000160  -.000360  .000090
+    1987.60  -.031970  .000140   .234330  .000130  -.4193230  .0001100  -.007390  .000160  -.000770  .000090
+    1987.65  -.037620  .000140   .254830  .000140  -.4346680  .0001100  -.009480  .000200   .000110  .000100
+    1987.70  -.043540  .000150   .272020  .000140  -.4584800  .0001100  -.010770  .000200  -.000250  .000110
+    1987.75  -.055190  .000150   .296620  .000150  -.4825460  .0001300  -.007710  .000220  -.000170  .000130
+    1987.80  -.063220  .000130   .318760  .000130  -.5129600  .0000900  -.006400  .000160   .000820  .000090
+    1987.85  -.060180  .000130   .341610  .000130  -.5455880  .0000800  -.005460  .000160   .001310  .000090
+    1987.90  -.056330  .000150   .371220  .000140  -.5748670  .0001200  -.003730  .000190   .001910  .000110
+    1987.95  -.049860  .000140   .395210  .000130  -.6043940  .0001000  -.001870  .000170   .002500  .000100
+    1988.00  -.023960  .000140   .412730  .000130   .3641980  .0001200  -.004140  .000190   .002660  .000100
+    1988.05   .004500  .000120   .424730  .000110   .3384500  .0001000  -.002530  .000160   .002120  .000090
+    1988.10   .047020  .000130   .432110  .000120   .3144130  .0001000  -.000740  .000180   .002150  .000100
+    1988.15   .067110  .000130   .430020  .000130   .2785320  .0001100  -.002550  .000190   .000600  .000100
+    1988.20   .105530  .000130   .427540  .000130   .2471970  .0001000  -.000980  .000170  -.000240  .000090
+    1988.25   .133210  .000130   .406300  .000130   .2166860  .0001100  -.000500  .000180  -.000430  .000100
+    1988.30   .150780  .000130   .378320  .000120   .1792750  .0000900  -.000370  .000160  -.001620  .000090
+    1988.35   .171170  .000140   .354020  .000140   .1522840  .0001100   .000780  .000190  -.001780  .000130
+    1988.40   .176460  .000130   .321580  .000130   .1225530  .0001000  -.000040  .000180  -.001500  .000100
+    1988.45   .165470  .000120   .287990  .000120   .0988100  .0000800  -.001720  .000160  -.001290  .000090
+    1988.50   .169520  .000140   .251610  .000130   .0898640  .0001200  -.003630  .000190  -.001180  .000100
+    1988.55   .162560  .000130   .216260  .000120   .0807490  .0001000  -.007890  .000160  -.000680  .000090
+    1988.60   .140250  .000120   .179670  .000110   .0689520  .0000800  -.010350  .000160  -.001420  .000080
+    1988.65   .106640  .000120   .154640  .000120   .0603010  .0001000  -.010090  .000180  -.001000  .000100
+    1988.70   .066470  .000120   .136560  .000120   .0448510  .0001000  -.010210  .000170  -.000500  .000100
+    1988.75   .009150  .000130   .129100  .000130   .0228930  .0001000  -.011320  .000200  -.000380  .000110
+    1988.80  -.050020  .000130   .138100  .000130  -.0018930  .0001100  -.007830  .000200  -.000750  .000110
+    1988.85  -.100790  .000110   .165460  .000110  -.0330640  .0000700  -.005600  .000150   .000990  .000080
+    1988.90  -.138410  .000120   .207940  .000120  -.0653100  .0000900  -.005220  .000170   .001020  .000100
+    1988.95  -.164740  .000120   .261170  .000110  -.0918720  .0000800  -.003500  .000160   .001530  .000080
+    1989.00  -.160800  .000130   .315200  .000140  -.1150310  .0000900  -.002340  .000170   .002030  .000090
+    1989.05  -.142800  .000120   .359530  .000110  -.1376110  .0000900  -.003600  .000170   .001370  .000090
+    1989.10  -.118060  .000120   .400480  .000110  -.1614360  .0000900  -.002340  .000170   .000860  .000090
+    1989.15  -.076130  .000120   .436720  .000110  -.1886810  .0000900  -.001680  .000160   .000380  .000080
+    1989.20  -.027240  .000130   .466260  .000120  -.2181740  .0001000  -.001820  .000180  -.001260  .000100
+    1989.25   .029070  .000120   .480900  .000120  -.2452640  .0000900  -.000610  .000170  -.002120  .000090
+    1989.30   .085860  .000120   .481670  .000120  -.2761340  .0000800  -.000780  .000150  -.002250  .000080
+    1989.35   .127570  .000120   .470310  .000120  -.3109290  .0001000  -.001920  .000170  -.003040  .000100
+    1989.40   .169220  .000120   .445260  .000120  -.3388540  .0000800  -.001680  .000150  -.003030  .000080
+    1989.45   .204420  .000120   .412890  .000120  -.3680090  .0000900  -.004720  .000170  -.002620  .000090
+    1989.50   .239210  .000120   .364510  .000120  -.3859960  .0000800  -.007490  .000160  -.002840  .000090
+    1989.55   .259150  .000120   .312920  .000120  -.4009490  .0000900  -.009020  .000170  -.002220  .000090
+    1989.60   .259400  .000120   .255620  .000120  -.4214040  .0000800  -.011270  .000150  -.002020  .000080
+    1989.65   .250970  .000120   .197720  .000120  -.4424920  .0000900  -.013980  .000160  -.002400  .000090
+    1989.70   .217420  .000120   .146080  .000120  -.4611150  .0000800  -.013140  .000160  -.002080  .000090
+    1989.75   .165120  .000120   .105110  .000110  -.4903590  .0000800  -.012600  .000160  -.000730  .000080
+    1989.80   .098020  .000110   .083150  .000110  -.5228010  .0000700  -.011010  .000150  -.001130  .000070
+    1989.85   .031480  .000110   .074730  .000110  -.5592470  .0000700  -.008550  .000150  -.000710  .000080
+    1989.90  -.036820  .000110   .087780  .000110  -.6014820  .0000800  -.007450  .000150   .000760  .000080
+    1989.95  -.090410  .000110   .118340  .000110  -.6390910  .0000700  -.006100  .000150   .000840  .000080
+    1990.00  -.132820  .000120   .163800  .000120   .3294770  .0000800  -.004230  .000160   .000950  .000090
+    1990.05  -.165870  .000110   .215080  .000110   .2971590  .0000800  -.005310  .000150   .000930  .000080
+    1990.10  -.202780  .000110   .276390  .000110   .2614270  .0000800  -.005390  .000150  -.000120  .000080
+    1990.15  -.201680  .000110   .344430  .000110   .2205110  .0000800  -.004370  .000160  -.000690  .000080
+    1990.20  -.185180  .000110   .413230  .000110   .1791940  .0000800  -.004430  .000150  -.002050  .000080
+    1990.25  -.153910  .000110   .471710  .000110   .1351550  .0000800  -.003760  .000150  -.003080  .000080
+    1990.30  -.101550  .000110   .524360  .000110   .0948910  .0000800  -.001280  .000150  -.003890  .000080
+    1990.35  -.049070  .000110   .556830  .000110   .0554040  .0000800  -.002860  .000150  -.003970  .000080
+    1990.40   .025280  .000110   .572830  .000110   .0201240  .0000700  -.004400  .000150  -.004260  .000080
+    1990.45   .096160  .000110   .568570  .000110  -.0096640  .0000800  -.004910  .000160  -.004050  .000080
+    1990.50   .166980  .000110   .541670  .000110  -.0403750  .0000800  -.009000  .000150  -.003880  .000080
+    1990.55   .230520  .000110   .499990  .000110  -.0614930  .0000800  -.011350  .000160  -.003850  .000080
+    1990.60   .276540  .000120   .439320  .000110  -.0830990  .0000800  -.013660  .000160  -.002920  .000090
+    1990.65   .306230  .000110   .379690  .000110  -.1154370  .0000700  -.015530  .000160  -.003320  .000080
+    1990.70   .312300  .000110   .309110  .000110  -.1458280  .0000800  -.015250  .000160  -.003160  .000080
+    1990.75   .295990  .000110   .241750  .000110  -.1816860  .0000800  -.014130  .000150  -.002630  .000080
+    1990.80   .258290  .000110   .178550  .000110  -.2226650  .0000800  -.013370  .000150  -.001910  .000080
+    1990.85   .214630  .000110   .130690  .000110  -.2630650  .0000800  -.010960  .000150  -.001740  .000080
+    1990.90   .158460  .000110   .095450  .000110  -.3023270  .0000800  -.009260  .000150  -.000700  .000080
+    1990.95   .092590  .000110   .076290  .000110  -.3458130  .0000800  -.008690  .000150   .000090  .000080
+    1991.00   .023070  .000120   .070420  .000120   .6189660  .0000800  -.007640  .000160   .000100  .000080
+    1991.05  -.040210  .000110   .085850  .000110   .5788470  .0000800  -.006390  .000160   .000290  .000080
+    1991.10  -.104560  .000110   .109710  .000100   .5375110  .0000700  -.007400  .000150  -.000780  .000080
+    1991.15  -.159350  .000110   .156090  .000110   .4982140  .0000800  -.007290  .000160  -.002160  .000080
+    1991.20  -.193710  .000110   .215880  .000110   .4563180  .0000700  -.005840  .000150  -.002750  .000080
+    1991.25  -.218250  .000110   .286330  .000110   .4101900  .0000800  -.005420  .000150  -.004230  .000080
+    1991.30  -.211540  .000110   .359670  .000110   .3695240  .0000700  -.004780  .000150  -.005250  .000080
+    1991.35  -.196100  .000120   .427740  .000110   .3269160  .0000800  -.003480  .000150  -.005350  .000080
+    1991.40  -.148030  .000110   .487300  .000110   .2852580  .0000700  -.006750  .000150  -.005630  .000080
+    1991.45  -.087780  .000120   .535430  .000110   .2509990  .0000800  -.007820  .000150  -.005310  .000080
+    1991.50  -.027600  .000110   .563210  .000110   .2244370  .0000700  -.010640  .000150  -.004690  .000080
+    1991.55   .049470  .000110   .572690  .000110   .1994110  .0000800  -.014450  .000150  -.004550  .000080
+    1991.60   .118890  .000110   .560660  .000110   .1792000  .0000700  -.015800  .000150  -.004890  .000080
+    1991.65   .172940  .000110   .527310  .000110   .1523860  .0000800  -.017410  .000150  -.004390  .000080
+    1991.70   .218130  .000110   .487510  .000110   .1145380  .0000700  -.019540  .000150  -.003880  .000080
+    1991.75   .251390  .000110   .434860  .000120   .0781450  .0000800  -.017070  .000150  -.004230  .000080
+    1991.80   .262130  .000100   .372840  .000100   .0419620  .0000700  -.015530  .000150  -.003410  .000070
+    1991.85   .263160  .000110   .317840  .000110   .0002900  .0000700  -.013880  .000150  -.002470  .000080
+    1991.90   .247290  .000110   .263630  .000110  -.0397540  .0000800  -.011140  .000150  -.001880  .000080
+    1991.95   .217400  .000100   .209210  .000100  -.0833930  .0000700  -.010160  .000150  -.000890  .000070
+    1992.00   .183430  .000110   .168250  .000110  -.1253090  .0000700  -.010560  .000150  -.001100  .000080
+    1992.05   .132860  .000110   .139720  .000110  -.1664230  .0000700  -.009680  .000150  -.001230  .000080
+    1992.10   .083410  .000110   .121340  .000110  -.2133580  .0000700  -.009250  .000150  -.001360  .000070
+    1992.15   .026850  .000110   .116920  .000100  -.2592480  .0000700  -.009810  .000150  -.002790  .000080
+    1992.20  -.034650  .000110   .126400  .000110  -.3037880  .0000700  -.008310  .000150  -.004080  .000080
+    1992.25  -.082680  .000120   .163990  .000110  -.3574120  .0000800  -.007420  .000160  -.004690  .000080
+    1992.30  -.115610  .000110   .203320  .000110  -.4102900  .0000700  -.007920  .000150  -.005450  .000080
+    1992.35  -.145550  .000110   .245630  .000110  -.4539040  .0000700  -.007330  .000150  -.006620  .000080
+    1992.40  -.156500  .000110   .291880  .000110  -.4959850  .0000700  -.008410  .000150  -.006170  .000080
+    1992.45  -.153110  .000120   .334650  .000120  -.5303330  .0000800  -.010970  .000170  -.005880  .000090
+    1992.50  -.140940  .000110   .381020  .000110   .4420910  .0000700  -.012890  .000150  -.006070  .000080
+    1992.55  -.115160  .000110   .420770  .000110   .4184490  .0000700  -.016380  .000140  -.005610  .000070
+    1992.60  -.077490  .000100   .459320  .000100   .3892290  .0000700  -.020190  .000140  -.005790  .000070
+    1992.65  -.031830  .000110   .492120  .000110   .3596050  .0000700  -.020360  .000150  -.006130  .000080
+    1992.70   .014530  .000110   .506730  .000110   .3231410  .0000700  -.021800  .000150  -.005180  .000080
+    1992.75   .055600  .000110   .503780  .000110   .2854080  .0000700  -.021980  .000150  -.005110  .000080
+    1992.80   .103120  .000110   .494250  .000100   .2458780  .0000700  -.018870  .000150  -.004950  .000080
+    1992.85   .137780  .000110   .473110  .000110   .1994080  .0000700  -.017150  .000150  -.004090  .000080
+    1992.90   .170750  .000110   .444760  .000110   .1538820  .0000700  -.015860  .000150  -.003210  .000080
+    1992.95   .187750  .000110   .406300  .000110   .1096390  .0000700  -.014370  .000150  -.002500  .000080
+    1993.00   .208200  .000170   .361780  .000190   .0639800  .0010000  -.012130  .000220  -.001820  .000110
+    1993.05   .212870  .000170   .311750  .000160   .0163900  .0010000  -.013700  .000240  -.001980  .000130
+    1993.10   .205710  .000220   .265790  .000200  -.0283100  .0010000  -.013700  .000160  -.003160  .000080
+    1993.15   .186400  .000210   .225690  .000200  -.0785000  .0010000  -.013380  .000160  -.003430  .000080
+    1993.20   .159450  .000220   .194090  .000150  -.1275600  .0010000  -.012960  .000160  -.005350  .000080
+    1993.25   .113650  .000210   .170110  .000230  -.1750500  .0010000  -.011070  .000170  -.006100  .000080
+    1993.30   .065420  .000250   .163930  .000160  -.2286500  .0010000  -.010500  .000170  -.006910  .000080
+    1993.35   .025160  .000210   .166410  .000190  -.2813400  .0010000  -.012890  .000170  -.007620  .000080
+    1993.40  -.005520  .000210   .177900  .000180  -.3249500  .0010000  -.012100  .000160  -.007690  .000080
+    1993.45  -.036590  .000150   .190810  .000200  -.3670900  .0010000  -.013970  .000160  -.007020  .000070
+    1993.50  -.063740  .000150   .211350  .000230   .5975800  .0010000  -.018750  .000150  -.006860  .000070
+    1993.55  -.073070  .000180   .234400  .000190   .5709200  .0010000  -.019920  .000150  -.007380  .000070
+    1993.60  -.091560  .000150   .266690  .000200   .5374700  .0010000  -.022820  .000160  -.006800  .000080
+    1993.65  -.101560  .000150   .302050  .000170   .5021000  .0010000  -.024430  .000160  -.007300  .000080
+    1993.70  -.099970  .000150   .336910  .000150   .4679700  .0010000  -.024360  .000160  -.007220  .000080
+    1993.75  -.095410  .000150   .370980  .000150   .4245500  .0010000  -.024680  .000150  -.006070  .000070
+    1993.80  -.080940  .000150   .403840  .000180   .3774500  .0010000  -.023390  .000150  -.005460  .000070
+    1993.85  -.062500  .000200   .428470  .000150   .3322200  .0010000  -.020420  .000150  -.005010  .000080
+    1993.90  -.041250  .000160   .448950  .000230   .2851400  .0010000  -.019020  .000150  -.004290  .000070
+    1993.95  -.019600  .000160   .464960  .000170   .2441700  .0010000  -.018560  .000150  -.003470  .000070
+    1994.00   .009200  .000170   .476210  .000190   .2006800  .0010000  -.016450  .000160  -.003840  .000070
+    1994.05   .054050  .000150   .478850  .000160   .1576400  .0010000  -.018260  .000150  -.002940  .000070
+    1994.10   .099840  .000190   .467200  .000180   .1161800  .0010000  -.019390  .000150  -.003820  .000070
+    1994.15   .137660  .000200   .448700  .000150   .0759200  .0010000  -.019230  .000160  -.005950  .000080
+    1994.20   .158340  .000190   .421910  .000150   .0247300  .0010000  -.018050  .000160  -.005840  .000070
+    1994.25   .175010  .000150   .390000  .000240  -.0223200  .0010000  -.017140  .000160  -.007230  .000080
+    1994.30   .183970  .000250   .356950  .000230  -.0662200  .0010000  -.015760  .000150  -.007610  .000070
+    1994.35   .186340  .000150   .317810  .000170  -.1154700  .0010000  -.017740  .000160  -.007860  .000080
+    1994.40   .180670  .000190   .281910  .000150  -.1586500  .0010000  -.017830  .000150  -.008150  .000070
+    1994.45   .160850  .000210   .243850  .000150  -.1904900  .0010000  -.019400  .000160  -.008520  .000080
+    1994.50   .134830  .000150   .210780  .000190   .7815600  .0010000  -.022820  .000150  -.007870  .000080
+    1994.55   .101460  .000170   .184820  .000180   .7562100  .0010000  -.026320  .000150  -.007960  .000070
+    1994.60   .067260  .000180   .172080  .000230   .7314100  .0010000  -.028350  .000160  -.008050  .000070
+    1994.65   .026570  .000160   .168170  .000230   .7029600  .0010000  -.029900  .000150  -.007730  .000070
+    1994.70  -.016290  .000150   .179410  .000190   .6665200  .0010000  -.030070  .000150  -.007650  .000070
+    1994.75  -.066370  .000150   .200140  .000190   .6249200  .0010000  -.029730  .000150  -.008050  .000070
+    1994.80  -.106130  .000140   .232880  .000190   .5793400  .0010000  -.028170  .000150  -.006430  .000070
+    1994.85  -.141410  .000170   .271820  .000170   .5355300  .0010000  -.026750  .000150  -.005780  .000080
+    1994.90  -.152620  .000190   .321150  .000180   .4935800  .0010000  -.024040  .000150  -.005160  .000070
+    1994.95  -.153790  .000170   .368190  .000160   .4476700  .0010000  -.022980  .000150  -.004610  .000070
+    1995.00  -.153720  .000170   .418090  .000170   .3991000  .0010000  -.023030  .000150  -.004120  .000070
+    1995.05  -.127450  .000130   .461200  .000150   .3532000  .0010000  -.022230  .000150  -.004420  .000070
+    1995.10  -.111580  .000130   .494070  .000160   .3028200  .0010000  -.024400  .000150  -.004660  .000070
+    1995.15  -.075420  .000170   .524080  .000150   .2573700  .0010000  -.023460  .000160  -.005540  .000070
+    1995.20  -.022770  .000140   .546010  .000130   .2056500  .0010000  -.022640  .000150  -.007170  .000070
+    1995.25   .035210  .000170   .558400  .000140   .1532400  .0010000  -.023210  .000150  -.007800  .000070
+    1995.30   .092910  .000130   .555900  .000200   .1027500  .0010000  -.021940  .000150  -.008930  .000070
+    1995.35   .162220  .000130   .532900  .000130   .0547700  .0010000  -.020550  .000150  -.009080  .000070
+    1995.40   .209220  .000170   .497340  .000210   .0067200  .0010000  -.023280  .000150  -.008740  .000080
+    1995.45   .254650  .000170   .443560  .000170  -.0324300  .0010000  -.024970  .000150  -.008710  .000070
+    1995.50   .281610  .000140   .378980  .000160  -.0635800  .0010000  -.026890  .000160  -.008000  .000080
+    1995.55   .287570  .000140   .308430  .000170  -.0926700  .0010000  -.031920  .000150  -.008630  .000070
+    1995.60   .273820  .000130   .237780  .000160  -.1136100  .0010000  -.034000  .000150  -.008830  .000070
+    1995.65   .237560  .000140   .179860  .000130  -.1418400  .0010000  -.035010  .000150  -.008040  .000070
+    1995.70   .189520  .000140   .133990  .000170  -.1803800  .0010000  -.036390  .000150  -.008250  .000070
+    1995.75   .135580  .000130   .105900  .000180  -.2213600  .0010000  -.035770  .000150  -.008060  .000070
+    1995.80   .066650  .000130   .085830  .000130  -.2632900  .0010000  -.033420  .000150  -.007150  .000070
+    1995.85   .002730  .000130   .084730  .000160  -.3084000  .0010000  -.032240  .000150  -.006220  .000070
+    1995.90  -.067070  .000140   .101140  .000160  -.3533700  .0010000  -.029910  .000150  -.005870  .000070
+    1995.95  -.138200  .000140   .136100  .000150  -.3981800  .0010000  -.029210  .000150  -.005260  .000070
+    1996.00  -.176160  .000140   .192180  .000160   .5551900  .0010000  -.029560  .000150  -.004540  .000070
+    1996.05  -.203930  .000140   .246410  .000130   .5221600  .0010000  -.028890  .000150  -.005090  .000070
+    1996.10  -.223380  .000140   .312950  .000130   .4899200  .0010000  -.028480  .000150  -.005710  .000070
+    1996.15  -.220960  .000130   .378460  .000210   .4499500  .0010000  -.030480  .000160  -.006550  .000070
+    1996.20  -.196520  .000130   .448570  .000150   .4092600  .0010000  -.029110  .000150  -.007690  .000070
+    1996.25  -.150870  .000130   .508690  .000130   .3699700  .0010000  -.028160  .000150  -.008330  .000070
+    1996.30  -.101690  .000140   .553340  .000160   .3258300  .0010000  -.028610  .000150  -.009150  .000070
+    1996.35  -.033850  .000170   .584490  .000150   .2846000  .0010000  -.029230  .000150  -.009690  .000070
+    1996.40   .040580  .000130   .595820  .000130   .2454400  .0010000  -.028510  .000160  -.009100  .000080
+    1996.45   .115360  .000170   .581210  .000130   .2114200  .0010000  -.032010  .000150  -.009350  .000070
+    1996.50   .181570  .000140   .545250  .000160   .1862900  .0010000  -.034130  .000150  -.008760  .000070
+    1996.55   .239170  .000170   .495410  .000130   .1659300  .0010000  -.036630  .000160  -.008420  .000070
+    1996.60   .267760  .000170   .436840  .000170   .1407800  .0010000  -.040480  .000150  -.008540  .000070
+    1996.65   .297160  .000240   .371810  .000140   .1230500  .0010000  -.041550  .000150  -.009250  .000070
+    1996.70   .290780  .000260   .296700  .000130   .0985300  .0010000  -.041950  .000150  -.008320  .000070
+    1996.75   .267110  .000130   .228290  .000130   .0638000  .0010000  -.042090  .000150  -.007370  .000070
+    1996.80   .227160  .000130   .171270  .000210   .0297800  .0010000  -.039090  .000150  -.007420  .000070
+    1996.85   .175940  .000140   .122030  .000150  -.0071800  .0010000  -.037160  .000150  -.007010  .000070
+    1996.90   .109730  .000140   .096200  .000170  -.0441600  .0010000  -.036960  .000150  -.005310  .000070
+    1996.95   .039500  .000140   .084250  .000160  -.0749400  .0010000  -.034810  .000150  -.005410  .000070
+    1997.00  -.020320  .000090   .095060  .000100  -.1096500  .0010000  -.034140  .000260  -.004920  .000100
+    1997.05  -.073030  .000100   .119750  .000090  -.1459800  .0010000  -.035390  .000290  -.004340  .000100
+    1997.10  -.124420  .000100   .156650  .000100  -.1730900  .0010000  -.035760  .000100  -.005370  .000100
+    1997.15  -.161820  .000090   .203960  .000100  -.2056400  .0010000  -.034520  .000260  -.006530  .000200
+    1997.20  -.186450  .000100   .263310  .000100  -.2482000  .0010000  -.035780  .000110  -.007860  .000200
+    1997.25  -.190710  .000100   .331710  .000090  -.2929300  .0010000  -.034320  .000100  -.008360  .000250
+    1997.30  -.174960  .000210   .396620  .000090  -.3387100  .0010000  -.033330  .000140  -.008810  .000200
+    1997.35  -.145010  .000100   .452930  .000100  -.3794300  .0010000  -.034730  .000310  -.009360  .000200
+    1997.40  -.098250  .000090   .498370  .000100  -.4162300  .0010000  -.036400  .000240  -.008940  .000250
+    1997.45  -.047990  .000090   .525350  .000090  -.4516000  .0010000  -.037960  .000130  -.008900  .000100
+    1997.50   .023090  .000090   .536790  .000100   .5261800  .0010000  -.041230  .000160  -.008490  .000200
+    1997.55   .088210  .000090   .526330  .000100   .5094900  .0010000  -.043740  .000130  -.007930  .000210
+    1997.60   .141170  .000100   .505000  .000100   .4812500  .0010000  -.045890  .000250  -.008370  .000090
+    1997.65   .181140  .000100   .467130  .000100   .4540400  .0010000  -.048080  .000130  -.008480  .000250
+    1997.70   .216860  .000100   .428130  .000100   .4276000  .0010000  -.047640  .000130  -.008290  .000090
+    1997.75   .221240  .000090   .378750  .000100   .3908700  .0010000  -.046790  .000110  -.007660  .000090
+    1997.80   .226280  .000100   .329150  .000100   .3513700  .0010000  -.047260  .000140  -.007180  .000140
+    1997.85   .214670  .000100   .278540  .000100   .3151900  .0010000  -.043960  .000230  -.006110  .000250
+    1997.90   .190380  .000100   .234080  .000100   .2784700  .0010000  -.041880  .000120  -.005170  .000100
+    1997.95   .151700  .000100   .201890  .000090   .2466500  .0010000  -.042270  .000270  -.004300  .000090
+    1998.00   .103371  .000034   .174919  .000027   .2187928  .0000860  -.040796  .000037  -.004526  .000010
+    1998.05   .051655  .000022   .167526  .000019   .1878736  .0000890  -.041014  .000035  -.004475  .000012
+    1998.10  -.005228  .000016   .173571  .000018   .1500342  .0000730  -.041089  .000022  -.005161  .000008
+    1998.15  -.051800  .000023   .188169  .000023   .1146000  .0001160  -.041022  .000032  -.005626  .000010
+    1998.20  -.079308  .000030   .220212  .000019   .0745974  .0000880  -.040480  .000039  -.006825  .000012
+    1998.25  -.110294  .000014   .254246  .000016   .0360847  .0001770  -.040068  .000027  -.007821  .000008
+    1998.30  -.113475  .000016   .301652  .000019   .0024942  .0001720  -.038470  .000023  -.008066  .000007
+    1998.35  -.114391  .000017   .344204  .000018  -.0345923  .0000870  -.039421  .000031  -.008546  .000010
+    1998.40  -.107406  .000018   .383854  .000019  -.0684825  .0000940  -.041256  .000027  -.008334  .000010
+    1998.45  -.099535  .000028   .414513  .000017  -.0887736  .0000800  -.041621  .000026  -.008251  .000008
+    1998.50  -.065007  .000030   .440850  .000025  -.1011400  .0000880  -.045405  .000032  -.008110  .000011
+    1998.55  -.021674  .000015   .465308  .000016  -.1082697  .0000780  -.048663  .000024  -.008757  .000008
+    1998.60   .017316  .000019   .478858  .000027  -.1146002  .0002360  -.050511  .000039  -.007435  .000011
+    1998.65   .058055  .000016   .476585  .000023  -.1250252  .0002150  -.052327  .000030  -.007918  .000009
+    1998.70   .097135  .000027   .465706  .000028  -.1418293  .0001130  -.053894  .000040  -.008104  .000011
+    1998.75   .125857  .000018   .444588  .000019  -.1587117  .0001640  -.051842  .000022  -.007051  .000007
+    1998.80   .149956  .000021   .418823  .000019  -.1862362  .0000880  -.050192  .000031  -.006161  .000011
+    1998.85   .163218  .000021   .388340  .000021  -.2147711  .0000750  -.049319  .000030  -.006025  .000010
+    1998.90   .154524  .000028   .350655  .000017  -.2401730  .0000940  -.047797  .000031  -.004755  .000011
+    1998.95   .137225  .000021   .321181  .000020  -.2653190  .0001500  -.047813  .000021  -.003519  .000007
+    1999.00   .138906  .000031   .296108  .000029   .7168562  .0000292  -.047632  .000102  -.004189  .000043
+    1999.05   .117532  .000023   .271385  .000020   .7014530  .0000153  -.046335  .000048  -.003666  .000020
+    1999.10   .100290  .000021   .252373  .000021   .6791687  .0000143  -.046361  .000049  -.004161  .000020
+    1999.15   .077098  .000025   .244566  .000019   .6561574  .0000165  -.046789  .000052  -.005200  .000021
+    1999.20   .054921  .000027   .239895  .000025   .6399826  .0000120  -.045030  .000034  -.006293  .000016
+    1999.25   .023809  .000025   .240618  .000020   .6145151  .0000125  -.044196  .000045  -.007261  .000018
+    1999.30   .001100  .000019   .248865  .000019   .5879676  .0000131  -.044112  .000036  -.007830  .000016
+    1999.35  -.017471  .000018   .262305  .000019   .5664931  .0000136  -.044623  .000039  -.008106  .000016
+    1999.40  -.023004  .000021   .281431  .000020   .5435885  .0000194  -.045556  .000056  -.007660  .000023
+    1999.45  -.031745  .000021   .296763  .000020   .5267825  .0000126  -.048393  .000040  -.007395  .000016
+    1999.50  -.031910  .000022   .311187  .000022   .5198637  .0000156  -.048944  .000060  -.007195  .000024
+    1999.55  -.024948  .000020   .330418  .000018   .5120479  .0000152  -.053295  .000049  -.007043  .000020
+    1999.60  -.014139  .000024   .345615  .000018   .5055648  .0000141  -.056278  .000043  -.007418  .000019
+    1999.65  -.008467  .000024   .361644  .000020   .4977768  .0000187  -.056558  .000058  -.008033  .000024
+    1999.70   .002079  .000023   .370914  .000021   .4819857  .0000165  -.056409  .000052  -.007053  .000020
+    1999.75   .006586  .000021   .379679  .000020   .4688203  .0000177  -.056851  .000056  -.006375  .000024
+    1999.80   .015741  .000023   .382597  .000021   .4506114  .0000177  -.054003  .000055  -.005858  .000024
+    1999.85   .024443  .000019   .379545  .000021   .4230946  .0000175  -.053148  .000040  -.004652  .000016
+    1999.90   .032087  .000018   .377378  .000022   .3974911  .0000145  -.051094  .000053  -.003961  .000022
+    1999.95   .037562  .000014   .380865  .000016   .3782104  .0000090  -.051070  .000033  -.003416  .000014
+    2000.00   .043300  .000014   .377661  .000021   .3554817  .0000572  -.050733  .000113  -.002289  .000040
+    2000.05   .052375  .000013   .375702  .000010   .3413574  .0000176  -.051501  .000042  -.002836  .000017
+    2000.10   .060632  .000009   .372427  .000010   .3250578  .0000117  -.050521  .000020  -.003334  .000008
+    2000.15   .067994  .000010   .365396  .000012   .3071171  .0000317  -.049754  .000059  -.004597  .000022
+    2000.20   .074610  .000010   .357979  .000010   .2899885  .0000127  -.049540  .000021  -.005112  .000008
+    2000.25   .075437  .000013   .346051  .000011   .2762541  .0000417  -.047537  .000065  -.005662  .000025
+    2000.30   .082353  .000009   .343278  .000012   .2552646  .0000219  -.047555  .000041  -.006187  .000015
+    2000.35   .088049  .000010   .332016  .000011   .2358447  .0000313  -.047653  .000056  -.006571  .000025
+    2000.40   .096303  .000010   .321406  .000009   .2198842  .0000179  -.048984  .000028  -.007124  .000011
+    2000.45   .113565  .000010   .304386  .000010   .2064913  .0000369  -.051715  .000058  -.006255  .000026
+    2000.50   .110079  .000010   .279086  .000010   .2040804  .0000193  -.054816  .000029  -.006360  .000013
+    2000.55   .096137  .000010   .261702  .000009   .2004203  .0000221  -.056391  .000040  -.006197  .000016
+    2000.60   .083088  .000009   .248498  .000010   .1987985  .0000409  -.058853  .000067  -.005982  .000024
+    2000.65   .058388  .000010   .242745  .000009   .1949233  .0000220  -.061330  .000046  -.006232  .000017
+    2000.70   .025972  .000011   .239588  .000009   .1871360  .0000185  -.059727  .000043  -.005855  .000016
+    2000.75  -.005722  .000011   .246917  .000009   .1747298  .0000378  -.060691  .000064  -.005000  .000026
+    2000.80  -.036045  .000011   .262831  .000008   .1600032  .0000134  -.057246  .000025  -.004209  .000010
+    2000.85  -.059965  .000009   .292223  .000009   .1391654  .0000181  -.055712  .000047  -.003613  .000017
+    2000.90  -.076915  .000010   .325404  .000010   .1205337  .0000302  -.054880  .000062  -.002841  .000022
+    2000.95  -.082660  .000008   .357017  .000009   .1045817  .0000131  -.053226  .000023  -.001982  .000008
+    2001.00  -.074006  .000010   .396764  .000009   .0936988  .0000299  -.051517  .000057  -.002267  .000022
+    2001.05  -.060835  .000011   .427064  .000009   .0806986  .0000145  -.051206  .000034  -.001604  .000013
+    2001.10  -.032401  .000008   .451341  .000009   .0762852  .0000112  -.054284  .000022  -.002328  .000008
+    2001.15   .004327  .000010   .475921  .000009   .0624390  .0000224  -.051152  .000049  -.002799  .000019
+    2001.20   .044840  .000008   .486196  .000008   .0388944  .0000105  -.050633  .000020  -.004153  .000008
+    2001.25   .091974  .000010   .489418  .000009   .0251539  .0000108  -.050009  .000020  -.005388  .000008
+    2001.30   .139116  .000011   .477702  .000011   .0096675  .0000143  -.049561  .000031  -.005489  .000013
+    2001.35   .178791  .000009   .452008  .000009  -.0088652  .0000125  -.051474  .000024  -.005586  .000009
+    2001.40   .215170  .000008   .409242  .000008  -.0228961  .0000160  -.051036  .000033  -.006224  .000014
+    2001.45   .242795  .000009   .359595  .000010  -.0245445  .0000168  -.053049  .000033  -.006319  .000013
+    2001.50   .254005  .000008   .304725  .000011  -.0278187  .0000118  -.055182  .000022  -.005536  .000008
+    2001.55   .247620  .000008   .249184  .000008  -.0254430  .0000161  -.059426  .000037  -.005789  .000014
+    2001.60   .220968  .000009   .199893  .000010  -.0206066  .0000143  -.060879  .000033  -.005547  .000012
+    2001.65   .175053  .000010   .158811  .000008  -.0281492  .0000148  -.062667  .000033  -.005926  .000013
+    2001.70   .125932  .000009   .132198  .000009  -.0304154  .0000126  -.062257  .000030  -.005599  .000012
+    2001.75   .064697  .000008   .117778  .000010  -.0383976  .0000155  -.060742  .000033  -.004371  .000013
+    2001.80   .001422  .000009   .120744  .000009  -.0558549  .0000201  -.059793  .000035  -.003611  .000015
+    2001.85  -.061030  .000010   .140506  .000009  -.0697006  .0000124  -.057258  .000022  -.002969  .000009
+    2001.90  -.103646  .000009   .180496  .000011  -.0868556  .0000215  -.055186  .000049  -.001724  .000020
+    2001.95  -.153361  .000010   .228492  .000010  -.1008918  .0000126  -.055469  .000028  -.001427  .000010
+    2002.00  -.176817  .000008   .292216  .000010  -.1154380  .0000264  -.053319  .000053  -.000648  .000022
+    2002.05  -.178852  .000009   .348438  .000008  -.1269166  .0000109  -.053365  .000021  -.001380  .000010
+    2002.10  -.163183  .000009   .406360  .000008  -.1417309  .0000151  -.053999  .000037  -.001540  .000014
+    2002.15  -.135832  .000008   .461499  .000008  -.1552923  .0000115  -.053015  .000033  -.003430  .000013
+    2002.20  -.088799  .000009   .513379  .000008  -.1739301  .0000084  -.051685  .000019  -.003023  .000007
+    2002.25  -.027634  .000009   .540974  .000007  -.1901581  .0000111  -.049814  .000027  -.004247  .000011
+    2002.30   .029239  .000008   .554816  .000008  -.2007113  .0000127  -.050367  .000032  -.005206  .000014
+    2002.35   .092521  .000009   .550437  .000008  -.2137458  .0000109  -.050343  .000020  -.004880  .000008
+    2002.40   .142157  .000008   .534179  .000008  -.2303514  .0000141  -.052550  .000037  -.004780  .000014
+    2002.45   .192923  .000007   .504114  .000008  -.2287794  .0000185  -.054054  .000036  -.005547  .000015
+    2002.50   .228889  .000009   .458092  .000008  -.2296246  .0000129  -.056387  .000033  -.005342  .000013
+    2002.55   .244514  .000007   .404459  .000007  -.2327614  .0000150  -.059477  .000026  -.005044  .000012
+    2002.60   .257521  .000010   .348296  .000008  -.2240428  .0000160  -.061751  .000039  -.005617  .000015
+    2002.65   .254939  .000008   .295393  .000008  -.2248033  .0000158  -.062523  .000045  -.004834  .000018
+    2002.70   .235509  .000007   .242129  .000007  -.2303263  .0000137  -.063071  .000027  -.004894  .000011
+    2002.75   .197659  .000007   .199413  .000007  -.2327877  .0000115  -.061560  .000022  -.004292  .000009
+    2002.80   .151551  .000010   .165804  .000007  -.2429725  .0000068  -.059948  .000013  -.003171  .000006
+    2002.85   .084865  .000008   .146837  .000008  -.2549716  .0000092  -.058425  .000020  -.002538  .000008
+    2002.90   .022450  .000008   .142124  .000009  -.2612171  .0000117  -.056420  .000026  -.002110  .000011
+    2002.95  -.038228  .000007   .154264  .000009  -.2767537  .0000111  -.055849  .000022  -.000854  .000010
+    2003.00  -.087743  .000008   .187387  .000009  -.2892390  .0000166  -.057149  .000032  -.000539  .000013
+    2003.05  -.127578  .000009   .228891  .000008  -.2995032  .0000119  -.054522  .000029  -.001356  .000012
+    2003.10  -.147530  .000007   .278647  .000008  -.3078795  .0000102  -.053909  .000022  -.001160  .000008
+    2003.15  -.154775  .000007   .332102  .000008  -.3201905  .0000109  -.053952  .000027  -.001913  .000010
+    2003.20  -.157108  .000008   .383491  .000008  -.3285379  .0000101  -.052108  .000022  -.003382  .000008
+    2003.25  -.131007  .000009   .438825  .000008  -.3439523  .0000120  -.051249  .000026  -.003305  .000010
+    2003.30  -.094644  .000008   .486399  .000008  -.3588043  .0000091  -.050741  .000021  -.004457  .000009
+    2003.35  -.043183  .000007   .522695  .000008  -.3638535  .0000108  -.050307  .000021  -.005323  .000009
+    2003.40   .001931  .000007   .541957  .000008  -.3745652  .0000102  -.051708  .000023  -.004153  .000009
+    2003.45   .062632  .000007   .546600  .000008  -.3752839  .0000160  -.054049  .000027  -.004697  .000012
+    2003.50   .135203  .000007   .538240  .000007  -.3661265  .0000115  -.056222  .000022  -.005013  .000009
+    2003.55   .191134  .000008   .509318  .000008  -.3577046  .0000128  -.060297  .000028  -.004614  .000012
+    2003.60   .232086  .000007   .463954  .000008  -.3537978  .0000190  -.062184  .000036  -.004744  .000015
+    2003.65   .259508  .000007   .415127  .000007  -.3494880  .0000129  -.062965  .000029  -.005055  .000011
+    2003.70   .264299  .000008   .356543  .000008  -.3529986  .0000103  -.063232  .000023  -.003950  .000009
+    2003.75   .257771  .000008   .302232  .000008  -.3575570  .0000116  -.062983  .000024  -.004229  .000010
+    2003.80   .226130  .000007   .250158  .000008  -.3628298  .0000113  -.059567  .000025  -.003622  .000011
+    2003.85   .195950  .000007   .207643  .000011  -.3728456  .0000145  -.058843  .000028  -.002215  .000013
+    2003.90   .147593  .000007   .174905  .000010  -.3807556  .0000134  -.057362  .000026  -.001897  .000011
+    2003.95   .087282  .000007   .158884  .000008  -.3820300  .0000099  -.054770  .000019  -.001608  .000008
+    2004.00   .031246  .000007   .153785  .000007  -.3896036  .0000155  -.056035  .000032  -.000255  .000014
+    2004.05  -.014033  .000009   .166554  .000010  -.3994989  .0000112  -.055954  .000030  -.000864  .000012
+    2004.10  -.057805  .000007   .191331  .000008  -.4041551  .0000156  -.054298  .000036  -.001804  .000014
+    2004.15  -.101004  .000007   .228144  .000008  -.4153922  .0000097  -.054532  .000024  -.002010  .000009
+    2004.20  -.126021  .000007   .268583  .000008  -.4225892  .0000165  -.053954  .000034  -.003026  .000014
+    2004.25  -.140343  .000008   .321451  .000008  -.4338646  .0000126  -.051273  .000029  -.004478  .000012
+    2004.30  -.132911  .000007   .375713  .000007  -.4512439  .0000137  -.051136  .000030  -.003701  .000012
+    2004.35  -.112725  .000007   .423506  .000008  -.4596331  .0000144  -.052649  .000025  -.005159  .000011
+    2004.40  -.090722  .000008   .458455  .000008  -.4683249  .0000165  -.051299  .000033  -.005609  .000015
Index: /tags/sj_tags/sj_root_20080929/psLib/share/finals2000A.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/finals2000A.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/finals2000A.dat	(revision 22322)
@@ -0,0 +1,5525 @@
+#
+#  Stripped version of finals.all.orig file
+#
+#
+#            Bull A      Bull A      Bull B     Bull B
+#  MJD         dX          dY          dX         dY
+#
+48622.00     -0.086      0.130       0.129      -0.653
+48623.00     -0.027      0.128       0.155      -0.575
+48624.00      0.014      0.154       0.135      -0.478
+48625.00      0.032      0.205      -0.006      -0.229
+48626.00      0.026      0.265      -0.100      -0.226
+48627.00      0.009      0.301      -0.174      -0.453
+48628.00      0.002      0.282      -0.229      -0.657
+48629.00      0.013      0.222      -0.255      -0.711
+48630.00      0.042      0.241      -0.169      -0.647
+48631.00      0.096      0.314      -0.108      -0.587
+48632.00      0.172      0.312      -0.087      -0.561
+48633.00      0.248      0.225      -0.085      -0.485
+48634.00      0.294      0.133      -0.041      -0.340
+48635.00      0.282      0.125       0.027      -0.178
+48636.00      0.201      0.193       0.135      -0.334
+48637.00      0.091      0.177       0.210      -0.566
+48638.00     -0.002      0.116       0.225      -0.558
+48639.00     -0.075      0.095       0.124      -0.240
+48640.00     -0.142      0.075      -0.096      -0.028
+48641.00     -0.187      0.023      -0.332      -0.004
+48642.00     -0.182      0.014      -0.452      -0.247
+48643.00     -0.127      0.101      -0.444      -0.380
+48644.00     -0.090      0.182      -0.430      -0.393
+48645.00     -0.089      0.204      -0.392      -0.499
+48646.00     -0.096      0.201      -0.276      -0.639
+48647.00     -0.101      0.215      -0.089      -0.809
+48648.00     -0.089      0.246       0.002      -0.771
+48649.00     -0.029      0.281       0.048      -0.529
+48650.00      0.094      0.213       0.125      -0.451
+48651.00      0.274      0.092       0.244      -0.365
+48652.00      0.452      0.034       0.295      -0.396
+48653.00      0.551      0.048       0.267      -0.337
+48654.00      0.558      0.123       0.095      -0.483
+48655.00      0.493      0.206       0.024      -0.568
+48656.00      0.380      0.224       0.013      -0.764
+48657.00      0.247      0.140       0.049      -0.895
+48658.00      0.136      0.013       0.139      -0.735
+48659.00      0.054     -0.093       0.135      -0.529
+48660.00     -0.012     -0.156       0.110      -0.514
+48661.00     -0.043     -0.177       0.017      -0.491
+48662.00     -0.041     -0.129      -0.012      -0.493
+48663.00     -0.033      0.037      -0.001      -0.519
+48664.00     -0.029      0.271       0.136      -0.603
+48665.00      0.006      0.334       0.277      -0.552
+48666.00      0.054      0.241       0.377      -0.365
+48667.00      0.048      0.146       0.365      -0.019
+48668.00     -0.043      0.126       0.120       0.377
+48669.00     -0.184      0.123      -0.180       0.293
+48670.00     -0.286      0.085      -0.424      -0.020
+48671.00     -0.290      0.044      -0.519      -0.409
+48672.00     -0.218      0.028      -0.579      -0.468
+48673.00     -0.105      0.066      -0.537      -0.493
+48674.00      0.022      0.183      -0.434      -0.410
+48675.00      0.105      0.208      -0.344      -0.380
+48676.00      0.121      0.083      -0.311      -0.137
+48677.00      0.083     -0.085      -0.376       0.046
+48678.00      0.014     -0.158      -0.340       0.141
+48679.00     -0.019     -0.039      -0.216       0.233
+48680.00     -0.016      0.141      -0.069       0.202
+48681.00      0.005      0.199      -0.032       0.174
+48682.00      0.033      0.152      -0.086       0.171
+48683.00      0.054      0.085      -0.146      -0.062
+48684.00      0.062      0.033      -0.165      -0.333
+48685.00      0.060      0.014       0.053      -0.473
+48686.00      0.059      0.018       0.064      -0.417
+48687.00      0.062      0.046       0.107      -0.279
+48688.00      0.077      0.090       0.165      -0.222
+48689.00      0.142      0.193       0.170      -0.303
+48690.00      0.242      0.308       0.170      -0.455
+48691.00      0.324      0.321       0.168      -0.584
+48692.00      0.335      0.119       0.226      -0.637
+48693.00      0.273     -0.024       0.349      -0.568
+48694.00      0.153     -0.059       0.384      -0.333
+48695.00      0.059     -0.066       0.315       0.004
+48696.00      0.041     -0.024       0.149       0.212
+48697.00      0.044      0.015      -0.003       0.155
+48698.00     -0.010     -0.035      -0.099      -0.330
+48699.00     -0.133     -0.172      -0.120      -0.836
+48700.00     -0.214     -0.252      -0.054      -0.980
+48701.00     -0.202     -0.200      -0.005      -0.942
+48702.00     -0.122     -0.035       0.088      -0.784
+48703.00     -0.033      0.064       0.180      -0.542
+48704.00      0.047      0.038       0.159      -0.467
+48705.00      0.129     -0.009       0.117      -0.366
+48706.00      0.206      0.010       0.109      -0.324
+48707.00      0.246      0.077       0.220      -0.282
+48708.00      0.230      0.118       0.263      -0.190
+48709.00      0.152      0.091       0.145      -0.078
+48710.00      0.029      0.035      -0.022      -0.038
+48711.00     -0.110     -0.000      -0.208      -0.220
+48712.00     -0.220     -0.019      -0.315      -0.354
+48713.00     -0.244     -0.047      -0.368      -0.509
+48714.00     -0.133     -0.097      -0.407      -0.531
+48715.00      0.013     -0.101      -0.315      -0.592
+48716.00      0.047      0.007      -0.238      -0.598
+48717.00     -0.017      0.171      -0.114      -0.679
+48718.00     -0.097      0.309      -0.048      -0.777
+48719.00     -0.110      0.350      -0.041      -0.822
+48720.00     -0.034      0.283      -0.037      -0.683
+48721.00      0.057      0.105      -0.077      -0.564
+48722.00      0.123     -0.034      -0.147      -0.285
+48723.00      0.146     -0.230      -0.263      -0.003
+48724.00      0.130     -0.469      -0.403       0.259
+48725.00      0.090     -0.633      -0.479       0.178
+48726.00      0.030     -0.584      -0.533      -0.037
+48727.00     -0.050     -0.286      -0.488      -0.292
+48728.00     -0.110     -0.004      -0.421      -0.457
+48729.00     -0.160      0.155      -0.374      -0.241
+48730.00     -0.209      0.204      -0.333       0.039
+48731.00     -0.252      0.184      -0.344       0.179
+48732.00     -0.288      0.137      -0.379       0.240
+48733.00     -0.322      0.103      -0.381       0.038
+48734.00     -0.333      0.048      -0.312      -0.191
+48735.00     -0.309     -0.051      -0.199      -0.304
+48736.00     -0.257     -0.152      -0.088      -0.368
+48737.00     -0.183     -0.226      -0.117      -0.285
+48738.00     -0.115     -0.253      -0.213      -0.294
+48739.00     -0.082     -0.241      -0.278      -0.435
+48740.00     -0.096     -0.242      -0.244      -0.623
+48741.00     -0.126     -0.285      -0.164      -0.654
+48742.00     -0.085     -0.283      -0.061      -0.723
+48743.00      0.011     -0.203       0.025      -0.710
+48744.00      0.095     -0.081       0.097      -0.861
+48745.00      0.149      0.020       0.088      -1.011
+48746.00      0.170      0.049       0.100      -0.963
+48747.00      0.147     -0.038       0.101      -0.909
+48748.00      0.090     -0.217       0.004      -0.738
+48749.00      0.060     -0.290      -0.072      -0.539
+48750.00      0.070     -0.238      -0.121      -0.510
+48751.00      0.117     -0.155      -0.130      -0.391
+48752.00      0.193     -0.081      -0.131      -0.368
+48753.00      0.288     -0.025      -0.150      -0.306
+48754.00      0.391      0.022      -0.132      -0.360
+48755.00      0.446      0.075      -0.073      -0.588
+48756.00      0.400      0.169      -0.000      -0.751
+48757.00      0.305      0.192       0.123      -0.629
+48758.00      0.194     -0.009       0.153      -0.437
+48759.00      0.096     -0.279       0.073      -0.229
+48760.00      0.071     -0.331       0.013      -0.172
+48761.00      0.126     -0.188      -0.083      -0.403
+48762.00      0.202     -0.053      -0.122      -0.587
+48763.00      0.211      0.035      -0.179      -0.646
+48764.00      0.094      0.060      -0.258      -0.522
+48765.00     -0.011      0.059      -0.344      -0.327
+48766.00     -0.049      0.063      -0.466      -0.227
+48767.00     -0.058      0.060      -0.621      -0.268
+48768.00     -0.094      0.038      -0.626      -0.342
+48769.00     -0.175     -0.028      -0.565      -0.335
+48770.00     -0.238     -0.113      -0.395      -0.280
+48771.00     -0.220     -0.160      -0.239      -0.319
+48772.00     -0.147     -0.062      -0.106      -0.402
+48773.00     -0.078      0.063      -0.029      -0.583
+48774.00     -0.052      0.041      -0.153      -0.870
+48775.00     -0.032     -0.033       0.099      -0.567
+48776.00      0.030     -0.108       0.164      -0.284
+48777.00      0.064     -0.239       0.182      -0.101
+48778.00      0.066     -0.311       0.243      -0.193
+48779.00      0.121     -0.316       0.339      -0.196
+48780.00      0.234     -0.257       0.403      -0.101
+48781.00      0.355     -0.143       0.342      -0.126
+48782.00      0.431      0.011       0.165      -0.146
+48783.00      0.437      0.193       0.009      -0.302
+48784.00      0.384      0.350      -0.130      -0.564
+48785.00      0.283      0.412      -0.128      -0.861
+48786.00      0.153      0.303      -0.052      -1.052
+48787.00      0.030     -0.034       0.078      -1.081
+48788.00     -0.059     -0.391       0.196      -1.166
+48789.00     -0.154     -0.524       0.309      -1.198
+48790.00     -0.225     -0.527       0.401      -1.250
+48791.00     -0.159     -0.485       0.469      -1.182
+48792.00     -0.040     -0.472       0.439      -0.975
+48793.00     -0.039     -0.507       0.391      -0.557
+48794.00     -0.099     -0.201       0.218      -0.270
+48795.00     -0.049      0.089      -0.032      -0.095
+48796.00      0.068      0.075      -0.323      -0.051
+48797.00      0.090     -0.023      -0.523      -0.008
+48798.00      0.047     -0.150      -0.597       0.015
+48799.00     -0.008     -0.228      -0.511      -0.162
+48800.00     -0.030     -0.199      -0.386      -0.448
+48801.00     -0.023     -0.104      -0.226      -0.892
+48802.00     -0.018     -0.010      -0.166      -1.033
+48803.00     -0.050      0.034      -0.180      -0.933
+48804.00     -0.126     -0.029      -0.154      -0.844
+48805.00     -0.192     -0.219      -0.128      -0.992
+48806.00     -0.204     -0.389      -0.221      -1.233
+48807.00     -0.167     -0.422      -0.227      -1.277
+48808.00     -0.113     -0.362      -0.209      -1.131
+48809.00     -0.067     -0.273      -0.241      -1.114
+48810.00     -0.058     -0.219      -0.344      -1.027
+48811.00     -0.090     -0.233      -0.456      -0.943
+48812.00     -0.094     -0.234      -0.528      -0.692
+48813.00     -0.099     -0.191      -0.562      -0.553
+48814.00     -0.108     -0.102      -0.419      -0.458
+48815.00     -0.106     -0.005      -0.185      -0.378
+48816.00     -0.093      0.068      -0.033      -0.303
+48817.00     -0.086      0.088       0.063      -0.300
+48818.00     -0.103      0.045      -0.036      -0.435
+48819.00     -0.129     -0.053      -0.172      -0.751
+48820.00     -0.136     -0.188      -0.340      -0.888
+48821.00     -0.099     -0.205      -0.441      -0.755
+48822.00     -0.051     -0.125      -0.601      -0.628
+48823.00     -0.013     -0.049      -0.862      -0.544
+48824.00      0.009     -0.066      -1.110      -0.520
+48825.00     -0.003     -0.147      -1.280      -0.453
+48826.00     -0.025     -0.144      -1.266      -0.304
+48827.00     -0.015     -0.095      -1.025      -0.297
+48828.00      0.042     -0.093      -0.706      -0.368
+48829.00      0.144     -0.141      -0.459      -0.637
+48830.00      0.237     -0.183      -0.370      -0.671
+48831.00      0.232     -0.207      -0.335      -0.512
+48832.00      0.149     -0.261      -0.300      -0.381
+48833.00      0.049     -0.309      -0.282      -0.274
+48834.00     -0.048     -0.297      -0.168      -0.403
+48835.00     -0.157     -0.217      -0.061      -0.372
+48836.00     -0.253     -0.172      -0.020      -0.274
+48837.00     -0.267     -0.169       0.007      -0.317
+48838.00     -0.194     -0.163       0.087      -0.348
+48839.00     -0.128     -0.168       0.000      -0.289
+48840.00     -0.103     -0.123      -0.162      -0.220
+48841.00     -0.106     -0.089      -0.394      -0.038
+48842.00     -0.115     -0.129      -0.391       0.071
+48843.00     -0.114     -0.150      -0.276      -0.076
+48844.00     -0.076     -0.142       0.023      -0.256
+48845.00     -0.018     -0.225       0.100      -0.233
+48846.00     -0.013     -0.365       0.027      -0.180
+48847.00     -0.034     -0.408      -0.142      -0.414
+48848.00     -0.104     -0.298      -0.304      -0.605
+48849.00     -0.200     -0.161      -0.471      -0.495
+48850.00     -0.291     -0.087      -0.599      -0.395
+48851.00     -0.350     -0.078      -0.728      -0.375
+48852.00     -0.340     -0.136      -0.901      -0.464
+48853.00     -0.259     -0.232      -0.975      -0.524
+48854.00     -0.178     -0.254      -0.798      -0.657
+48855.00     -0.146     -0.203      -0.420      -0.758
+48856.00     -0.174     -0.154       0.018      -0.810
+48857.00     -0.103     -0.178       0.300      -0.913
+48858.00      0.015     -0.290       0.421      -0.860
+48859.00      0.058     -0.394       0.365      -0.639
+48860.00      0.023     -0.380       0.222      -0.520
+48861.00     -0.066     -0.249       0.099      -0.622
+48862.00     -0.156     -0.109       0.058      -0.594
+48863.00     -0.192      0.113      -0.109      -0.612
+48864.00     -0.167      0.125      -0.355      -0.511
+48865.00     -0.157     -0.063      -0.549      -0.606
+48866.00     -0.156     -0.178      -0.600      -0.916
+48867.00     -0.118     -0.149      -0.493      -1.105
+48868.00     -0.070     -0.205      -0.479      -1.134
+48869.00     -0.003     -0.391      -0.587      -0.869
+48870.00      0.083     -0.513      -0.632      -0.860
+48871.00      0.143     -0.510      -0.378      -0.757
+48872.00      0.130     -0.459      -0.088      -0.719
+48873.00      0.020     -0.464       0.126      -0.537
+48874.00     -0.172     -0.538       0.118      -0.442
+48875.00     -0.339     -0.529       0.018      -0.587
+48876.00     -0.423     -0.431      -0.122      -0.611
+48877.00     -0.426     -0.359      -0.247      -0.688
+48878.00     -0.367     -0.319      -0.410      -0.596
+48879.00     -0.274     -0.270      -0.451      -0.478
+48880.00     -0.161     -0.187      -0.575      -0.590
+48881.00     -0.059     -0.075      -0.557      -0.938
+48882.00     -0.032      0.007      -0.468      -1.002
+48883.00     -0.062      0.020      -0.329      -1.083
+48884.00     -0.098     -0.010      -0.112      -1.082
+48885.00     -0.135     -0.067       0.102      -0.938
+48886.00     -0.168     -0.136       0.135      -0.776
+48887.00     -0.189     -0.183       0.063      -0.459
+48888.00     -0.192     -0.171       0.000      -0.417
+48889.00     -0.185     -0.048      -0.008      -0.497
+48890.00     -0.190      0.070       0.034      -0.625
+48891.00     -0.228      0.044      -0.020      -0.657
+48892.00     -0.280     -0.092      -0.208      -0.482
+48893.00     -0.320     -0.267      -0.383      -0.531
+48894.00     -0.328     -0.356      -0.399      -0.646
+48895.00     -0.315     -0.286      -0.252      -0.808
+48896.00     -0.296     -0.147      -0.116      -0.944
+48897.00     -0.265     -0.055      -0.101      -0.866
+48898.00     -0.220     -0.050      -0.091      -0.716
+48899.00     -0.189     -0.077      -0.003      -0.763
+48900.00     -0.194     -0.120      -0.005      -0.742
+48901.00     -0.242     -0.189      -0.013      -0.680
+48902.00     -0.328     -0.278      -0.200      -0.488
+48903.00     -0.428     -0.376      -0.407      -0.416
+48904.00     -0.508     -0.482      -0.653      -0.356
+48905.00     -0.534     -0.541      -0.833      -0.354
+48906.00     -0.501     -0.516      -0.877      -0.300
+48907.00     -0.425     -0.411      -0.857      -0.371
+48908.00     -0.311     -0.260      -0.749      -0.446
+48909.00     -0.178     -0.107      -0.529      -0.805
+48910.00     -0.092      0.018      -0.309      -1.044
+48911.00     -0.084      0.095      -0.062      -1.068
+48912.00     -0.145      0.100       0.066      -0.968
+48913.00     -0.234      0.055       0.207      -0.799
+48914.00     -0.298      0.005       0.260      -0.529
+48915.00     -0.284      0.004       0.226      -0.137
+48916.00     -0.149      0.044       0.154      -0.013
+48917.00      0.027      0.114       0.087       0.021
+48918.00      0.146      0.270      -0.002      -0.111
+48919.00      0.150      0.523      -0.091      -0.199
+48920.00      0.057      0.676      -0.240      -0.057
+48921.00     -0.075      0.650      -0.396      -0.008
+48922.00     -0.186      0.522      -0.417      -0.044
+48923.00     -0.245      0.369      -0.247      -0.230
+48924.00     -0.260      0.191      -0.056      -0.494
+48925.00     -0.222     -0.032       0.050      -0.740
+48926.00     -0.135     -0.247       0.140      -0.820
+48927.00     -0.060     -0.285       0.202      -0.925
+48928.00     -0.032     -0.196       0.223      -0.671
+48929.00     -0.057     -0.187       0.151      -0.592
+48930.00     -0.121     -0.324       0.308      -0.535
+48931.00     -0.175     -0.394       0.153      -0.520
+48932.00     -0.218     -0.322      -0.047      -0.632
+48933.00     -0.252     -0.259      -0.251      -0.659
+48934.00     -0.271     -0.252      -0.475      -0.830
+48935.00     -0.261     -0.219      -0.658      -0.987
+48936.00     -0.207     -0.103      -0.583      -1.116
+48937.00     -0.107      0.060      -0.403      -1.224
+48938.00     -0.003      0.070      -0.176      -1.115
+48939.00      0.084     -0.162      -0.072      -0.774
+48940.00      0.147     -0.622      -0.000      -0.469
+48941.00      0.119     -0.756      -0.016      -0.177
+48942.00     -0.002     -0.435      -0.012       0.071
+48943.00     -0.144     -0.085      -0.044       0.272
+48944.00     -0.260      0.008      -0.216       0.200
+48945.00     -0.351     -0.048      -0.460       0.020
+48946.00     -0.424     -0.125      -0.690      -0.342
+48947.00     -0.488     -0.111      -0.711      -0.662
+48948.00     -0.550     -0.003      -0.702      -0.916
+48949.00     -0.599      0.133      -0.646      -0.896
+48950.00     -0.618      0.244      -0.504      -1.034
+48951.00     -0.594      0.306      -0.306      -0.995
+48952.00     -0.540      0.322      -0.118      -1.141
+48953.00     -0.468      0.290       0.099      -1.294
+48954.00     -0.392      0.213       0.307      -1.394
+48955.00     -0.320      0.114       0.437      -1.132
+48956.00     -0.265      0.001       0.476      -0.725
+48957.00     -0.249     -0.131       0.448      -0.387
+48958.00     -0.253     -0.234       0.318      -0.221
+48959.00     -0.190     -0.187       0.184      -0.028
+48960.00     -0.137      0.010      -0.064      -0.041
+48961.00     -0.154      0.162      -0.292      -0.029
+48962.00     -0.213      0.211      -0.427      -0.259
+48963.00     -0.249      0.217      -0.502      -0.612
+48964.00     -0.216      0.216      -0.508      -0.847
+48965.00     -0.117      0.176      -0.490      -1.102
+48966.00     -0.034     -0.006      -0.434      -1.055
+48967.00     -0.007     -0.234      -0.393      -0.767
+48968.00     -0.047     -0.348      -0.375      -0.519
+48969.00     -0.128     -0.319      -0.352      -0.527
+48970.00     -0.210     -0.159      -0.239      -0.545
+48971.00     -0.254      0.138      -0.177      -0.659
+48972.00     -0.260      0.437      -0.116      -0.429
+48973.00     -0.227      0.790      -0.218      -0.508
+48974.00     -0.160      0.875      -0.306      -0.539
+48975.00     -0.074      0.629      -0.403      -0.686
+48976.00      0.003      0.291      -0.370      -0.752
+48977.00      0.025      0.057      -0.298      -0.585
+48978.00     -0.003     -0.047      -0.265      -0.537
+48979.00     -0.045     -0.061      -0.288      -0.440
+48980.00     -0.079     -0.055      -0.309      -0.402
+48981.00     -0.101     -0.061      -0.264      -0.513
+48982.00     -0.115     -0.081      -0.223      -0.546
+48983.00     -0.113     -0.105      -0.164      -0.553
+48984.00     -0.087     -0.123      -0.156      -0.292
+48985.00     -0.036     -0.139      -0.129      -0.060
+48986.00      0.043     -0.149      -0.013       0.013
+48987.00      0.158     -0.118       0.023       0.005
+48988.00      0.219     -0.049      -0.013      -0.087
+48989.00      0.187      0.020      -0.020      -0.087
+48990.00      0.099      0.063       0.035      -0.168
+48991.00     -0.001      0.074       0.064      -0.479
+48992.00     -0.071      0.042       0.150      -0.736
+48993.00     -0.088     -0.021       0.153      -0.941
+48994.00     -0.076     -0.025       0.100      -0.797
+48995.00     -0.067      0.036      -0.025      -0.476
+48996.00     -0.077      0.122      -0.193      -0.074
+48997.00     -0.102      0.188      -0.358       0.028
+48998.00     -0.140      0.214      -0.449      -0.046
+48999.00     -0.197      0.184      -0.488      -0.152
+49000.00     -0.269      0.073      -0.475      -0.205
+49001.00     -0.337     -0.078      -0.501      -0.289
+49002.00     -0.385     -0.193      -0.555      -0.371
+49003.00     -0.395     -0.182      -0.478      -0.532
+49004.00     -0.365     -0.069      -0.325      -0.641
+49005.00     -0.309      0.051      -0.115      -0.611
+49006.00     -0.254      0.084       0.053      -0.588
+49007.00     -0.213      0.006       0.059      -0.511
+49008.00     -0.181     -0.043       0.010      -0.414
+49009.00     -0.164     -0.018      -0.075      -0.319
+49010.00     -0.183      0.033      -0.173      -0.293
+49011.00     -0.229      0.093      -0.252      -0.335
+49012.00     -0.269      0.152      -0.346      -0.148
+49013.00     -0.274      0.189      -0.436      -0.034
+49014.00     -0.231      0.123      -0.496       0.002
+49015.00     -0.176     -0.063      -0.504      -0.018
+49016.00     -0.137     -0.094      -0.603      -0.145
+49017.00     -0.087     -0.059      -0.694      -0.279
+49018.00     -0.026     -0.064      -0.585      -0.250
+49019.00      0.024     -0.067      -0.443      -0.428
+49020.00      0.039     -0.036       5.621       2.700
+49021.00      0.022      0.029      -0.131      -0.926
+49022.00      0.048      0.096       0.004      -0.909
+49023.00      0.136      0.182       0.030      -0.459
+49024.00      0.145      0.191      -0.050      -0.119
+49025.00      0.081      0.138      -0.175       0.036
+49026.00     -0.005      0.065      -0.266      -0.247
+49027.00     -0.095      0.041      -0.197      -0.409
+49028.00     -0.183      0.060      -0.210      -0.510
+49029.00     -0.234      0.055      -0.273      -0.482
+49030.00     -0.224      0.054      -0.302      -0.532
+49031.00     -0.215      0.017      -0.384      -0.521
+49032.00     -0.227     -0.031      -0.406      -0.530
+49033.00     -0.241     -0.081      -0.377      -0.365
+49034.00     -0.253     -0.156      -0.309      -0.431
+49035.00     -0.251     -0.208      -0.160      -0.409
+49036.00     -0.212     -0.136      -0.150      -0.321
+49037.00     -0.165     -0.022      -0.173      -0.253
+49038.00     -0.159      0.049      -0.233      -0.148
+49039.00     -0.191      0.069      -0.288      -0.289
+49040.00     -0.227      0.066      -0.349      -0.357
+49041.00     -0.227      0.067      -0.405      -0.251
+49042.00     -0.172      0.098      -0.319      -0.220
+49043.00     -0.089      0.161      -0.300      -0.098
+49044.00     -0.019      0.245      -0.249      -0.241
+49045.00     -0.012      0.329      -0.308      -0.280
+49046.00     -0.064      0.394      -0.235      -0.168
+49047.00     -0.132      0.431      -0.102      -0.201
+49048.00     -0.175      0.413       0.048      -0.300
+49049.00     -0.172      0.319       0.101      -0.396
+49050.00     -0.155      0.237       0.240      -0.554
+49051.00     -0.163      0.243       0.096      -0.201
+49052.00     -0.197      0.214      -0.135      -0.004
+49053.00     -0.235      0.082      -0.424       0.043
+49054.00     -0.241     -0.084      -0.467      -0.358
+49055.00     -0.211     -0.144      -0.487      -0.619
+49056.00     -0.186     -0.039      -0.479      -0.552
+49057.00     -0.204      0.104      -0.485      -0.420
+49058.00     -0.231      0.211      -0.457      -0.306
+49059.00     -0.224      0.273      -0.416      -0.184
+49060.00     -0.179      0.289      -0.391      -0.188
+49061.00     -0.102      0.254      -0.282      -0.112
+49062.00     -0.007      0.163      -0.149       0.008
+49063.00      0.073      0.064       0.110      -0.113
+49064.00      0.100      0.076       0.232      -0.155
+49065.00      0.069      0.117       0.201      -0.168
+49066.00      0.016      0.152       0.060      -0.024
+49067.00     -0.045      0.202      -0.114      -0.160
+49068.00     -0.106      0.268      -0.171      -0.271
+49069.00     -0.150      0.349      -0.235      -0.302
+49070.00     -0.176      0.396      -0.351      -0.137
+49071.00     -0.213      0.257      -0.367      -0.030
+49072.00     -0.252      0.154      -0.464      -0.027
+49073.00     -0.254      0.120      -0.535      -0.226
+49074.00     -0.222      0.110      -0.475      -0.331
+49075.00     -0.172      0.123      -0.334      -0.416
+49076.00     -0.120      0.148      -0.086      -0.340
+49077.00     -0.082      0.147       0.067      -0.425
+49078.00     -0.064      0.111       0.133      -0.282
+49079.00     -0.060      0.069       0.065       0.032
+49080.00     -0.070      0.046       0.196       0.176
+49081.00     -0.087      0.024      -0.012       0.074
+49082.00     -0.092     -0.005      -0.209      -0.190
+49083.00     -0.069     -0.003      -0.230      -0.398
+49084.00     -0.036      0.058      -0.274      -0.277
+49085.00     -0.021      0.131      -0.290      -0.193
+49086.00     -0.033      0.156      -0.297      -0.191
+49087.00     -0.052      0.132      -0.341      -0.203
+49088.00     -0.056      0.096      -0.357      -0.222
+49089.00     -0.033      0.094      -0.358      -0.318
+49090.00      0.016      0.174      -0.191      -0.349
+49091.00      0.081      0.310       0.017      -0.328
+49092.00      0.146      0.341       0.181      -0.298
+49093.00      0.171      0.172       0.123      -0.253
+49094.00      0.176      0.015      -0.031      -0.167
+49095.00      0.156     -0.040      -0.159      -0.287
+49096.00      0.101     -0.054      -0.201      -0.425
+49097.00      0.019     -0.099      -0.183      -0.524
+49098.00     -0.053     -0.135      -0.102      -0.532
+49099.00     -0.020     -0.012      -0.050      -0.466
+49100.00      0.042      0.018       0.030      -0.383
+49101.00      0.028      0.031       0.064      -0.628
+49102.00     -0.075      0.082       0.126      -0.821
+49103.00     -0.226      0.117       0.188      -0.780
+49104.00     -0.373      0.082       0.339      -0.797
+49105.00     -0.443     -0.023       0.401      -0.612
+49106.00     -0.338     -0.040       0.308      -0.549
+49107.00     -0.141      0.017       0.108      -0.336
+49108.00      0.033      0.109      -0.155      -0.346
+49109.00      0.150      0.204      -0.357      -0.427
+49110.00      0.206      0.268      -0.485      -0.776
+49111.00      0.197      0.265      -0.567      -0.858
+49112.00      0.122      0.161      -0.653      -0.748
+49113.00     -0.018      0.006      -0.696      -0.580
+49114.00     -0.192     -0.068      -0.719      -0.503
+49115.00     -0.306     -0.128      -0.670      -0.525
+49116.00     -0.320     -0.227      -0.618      -0.757
+49117.00     -0.258     -0.310      -0.499      -0.756
+49118.00     -0.154     -0.316      -0.259      -0.742
+49119.00     -0.046     -0.212       0.056      -0.510
+49120.00      0.044     -0.015       0.315      -0.112
+49121.00      0.119      0.186       0.397       0.055
+49122.00      0.157      0.317       0.252       0.194
+49123.00      0.146      0.366       0.062       0.087
+49124.00      0.117      0.320      -0.038      -0.171
+49125.00      0.115      0.148      -0.116      -0.347
+49126.00      0.160     -0.123      -0.180      -0.493
+49127.00      0.205     -0.267      -0.211      -0.473
+49128.00      0.220     -0.229      -0.229      -0.579
+49129.00      0.201     -0.099      -0.241      -0.735
+49130.00      0.145      0.049      -0.236      -0.710
+49131.00      0.081      0.161      -0.197      -0.680
+49132.00      0.057      0.170      -0.103      -0.563
+49133.00      0.112      0.021       0.018      -0.382
+49134.00      0.190     -0.201       0.153      -0.337
+49135.00      0.251     -0.384       0.166      -0.124
+49136.00      0.297     -0.504       0.062      -0.070
+49137.00      0.330     -0.570      -0.103      -0.206
+49138.00      0.343     -0.569      -0.200      -0.378
+49139.00      0.325     -0.477      -0.191      -0.649
+49140.00      0.268     -0.305      -0.129      -0.609
+49141.00      0.175     -0.093      -0.068      -0.408
+49142.00      0.076      0.088      -0.083      -0.424
+49143.00     -0.002      0.185      -0.059      -0.513
+49144.00     -0.049      0.204      -0.082      -0.576
+49145.00     -0.057      0.153      -0.118      -0.765
+49146.00     -0.018      0.050      -0.019      -0.809
+49147.00      0.069     -0.087       0.097      -0.545
+49148.00      0.143     -0.204       0.156      -0.160
+49149.00      0.187     -0.278       0.120       0.075
+49150.00      0.207     -0.283      -0.055       0.132
+49151.00      0.204     -0.236      -0.182      -0.177
+49152.00      0.198     -0.182      -0.269      -0.434
+49153.00      0.206     -0.183      -0.271      -0.727
+49154.00      0.223     -0.257      -0.164      -0.945
+49155.00      0.236     -0.257      -0.052      -1.144
+49156.00      0.237     -0.153       0.060      -1.246
+49157.00      0.225     -0.016       0.074      -1.307
+49158.00      0.206      0.090       0.079      -1.201
+49159.00      0.179      0.121       0.003      -1.101
+49160.00      0.138      0.017      -0.033      -0.930
+49161.00      0.094     -0.223      -0.039      -0.833
+49162.00      0.112     -0.318       0.077      -0.911
+49163.00      0.150     -0.240       0.122      -0.819
+49164.00      0.127     -0.140       0.114      -0.698
+49165.00      0.060     -0.099       0.004      -0.614
+49166.00     -0.015     -0.113      -0.202      -0.547
+49167.00     -0.062     -0.127      -0.329      -0.692
+49168.00     -0.059     -0.099      -0.289      -0.672
+49169.00     -0.028     -0.096      -0.167      -0.641
+49170.00      0.020     -0.147      -0.058      -0.560
+49171.00      0.088     -0.214       0.022      -0.458
+49172.00      0.175     -0.254       0.064      -0.516
+49173.00      0.261     -0.237       0.031      -0.682
+49174.00      0.331     -0.141       0.062      -0.804
+49175.00      0.384      0.064       0.127      -0.664
+49176.00      0.421      0.371       0.163      -0.380
+49177.00      0.434      0.508       0.090       0.007
+49178.00      0.395      0.408       0.031       0.065
+49179.00      0.318      0.196      -0.109      -0.005
+49180.00      0.250     -0.028      -0.247      -0.086
+49181.00      0.214     -0.181      -0.235      -0.181
+49182.00      0.208     -0.214      -0.099      -0.216
+49183.00      0.196     -0.208       0.091      -0.394
+49184.00      0.202     -0.271       0.274      -0.540
+49185.00      0.225     -0.237       0.321      -0.648
+49186.00      0.218     -0.152       0.259      -0.530
+49187.00      0.171     -0.141       0.115      -0.374
+49188.00      0.105     -0.134       0.009      -0.296
+49189.00      0.046     -0.092      -0.060      -0.492
+49190.00      0.018     -0.072      -0.052      -0.710
+49191.00      0.013     -0.067       0.065      -0.894
+49192.00      0.022     -0.044       0.165      -0.924
+49193.00      0.038     -0.019       0.098      -0.678
+49194.00      0.055     -0.017      -0.071      -0.600
+49195.00      0.078     -0.040      -0.172      -0.466
+49196.00      0.110     -0.070      -0.212      -0.570
+49197.00      0.134     -0.102      -0.083      -0.478
+49198.00      0.132     -0.205       0.065      -0.481
+49199.00      0.133     -0.280       0.228      -0.415
+49200.00      0.160     -0.257       0.238      -0.436
+49201.00      0.189     -0.202       0.228      -0.470
+49202.00      0.185     -0.183       0.166      -0.461
+49203.00      0.135     -0.251       0.074      -0.501
+49204.00      0.065     -0.358      -0.004      -0.508
+49205.00      0.002     -0.396       0.155      -0.207
+49206.00     -0.030     -0.387       0.098      -0.202
+49207.00     -0.017     -0.362      -0.079      -0.246
+49208.00      0.032     -0.301      -0.236      -0.485
+49209.00      0.080     -0.206      -0.339      -0.538
+49210.00      0.086     -0.092      -0.222      -0.633
+49211.00      0.082      0.044       0.014      -0.813
+49212.00      0.076      0.135       0.207      -0.959
+49213.00      0.065      0.139       0.265      -1.171
+49214.00      0.046      0.067       0.156      -1.176
+49215.00      0.016     -0.055       0.034      -0.980
+49216.00     -0.014     -0.204      -0.054      -0.992
+49217.00     -0.021     -0.345      -0.105      -1.047
+49218.00      0.009     -0.342      -0.088      -1.213
+49219.00      0.016     -0.325      -0.057      -1.351
+49220.00      0.010     -0.297      -0.036      -1.290
+49221.00      0.037     -0.213      -0.019      -1.129
+49222.00      0.107     -0.129      -0.030      -1.035
+49223.00      0.216     -0.120      -0.055      -0.786
+49224.00      0.364     -0.250      -0.112      -0.698
+49225.00      0.478     -0.394      -0.083      -0.565
+49226.00      0.491     -0.453       0.049      -0.454
+49227.00      0.407     -0.423       0.186      -0.432
+49228.00      0.279     -0.340       0.316      -0.437
+49229.00      0.157     -0.243       0.342      -0.366
+49230.00      0.083     -0.158       0.326      -0.349
+49231.00      0.095     -0.056       0.259      -0.453
+49232.00      0.194      0.024       0.198      -0.563
+49233.00      0.318      0.045       0.178      -0.595
+49234.00      0.371      0.031       0.101      -0.501
+49235.00      0.338      0.005      -0.066      -0.299
+49236.00      0.268     -0.019      -0.163      -0.533
+49237.00      0.194     -0.052      -0.239      -0.646
+49238.00      0.135     -0.113      -0.094      -0.615
+49239.00      0.102     -0.170       0.205      -0.646
+49240.00      0.099     -0.207       0.452      -0.667
+49241.00      0.121     -0.236       0.485      -0.939
+49242.00      0.147     -0.267       0.430      -0.982
+49243.00      0.155     -0.293       0.271      -0.803
+49244.00      0.134     -0.312       0.189      -0.727
+49245.00      0.091     -0.332       0.103      -0.782
+49246.00      0.027     -0.339       0.048      -0.779
+49247.00     -0.045     -0.239      -0.117      -0.655
+49248.00     -0.100     -0.192      -0.356      -0.556
+49249.00     -0.108     -0.295      -0.471      -0.762
+49250.00     -0.063     -0.413      -0.429      -0.859
+49251.00      0.004     -0.406      -0.305      -0.918
+49252.00      0.066     -0.222      -0.270      -0.833
+49253.00      0.130      0.052      -0.354      -0.492
+49254.00      0.132      0.053      -0.387      -0.431
+49255.00      0.074     -0.085      -0.303      -0.467
+49256.00      0.017     -0.122      -0.148      -0.544
+49257.00     -0.028     -0.126      -0.081      -0.568
+49258.00     -0.055     -0.151      -0.113      -0.482
+49259.00     -0.059     -0.233      -0.163      -0.631
+49260.00     -0.018     -0.393      -0.203      -0.744
+49261.00      0.111     -0.434      -0.208      -0.803
+49262.00      0.178     -0.376      -0.184      -0.708
+49263.00      0.166     -0.393      -0.189      -0.694
+49264.00      0.109     -0.436      -0.240      -0.769
+49265.00      0.037     -0.458      -0.276      -0.898
+49266.00     -0.025     -0.407      -0.122      -0.896
+49267.00     -0.049     -0.245       0.085      -0.774
+49268.00     -0.003     -0.053       0.249      -0.745
+49269.00      0.041      0.045       0.340      -0.756
+49270.00      0.032      0.026       0.211      -0.816
+49271.00     -0.004     -0.052       0.060      -0.623
+49272.00     -0.040     -0.129      -0.050      -0.512
+49273.00     -0.054     -0.185      -0.089      -0.551
+49274.00     -0.060     -0.288      -0.134      -0.459
+49275.00     -0.096     -0.295      -0.361      -0.309
+49276.00     -0.129     -0.227      -0.651      -0.197
+49277.00     -0.113     -0.195      -0.854      -0.288
+49278.00     -0.055     -0.189      -0.758      -0.627
+49279.00     -0.006     -0.168      -0.489      -0.911
+49280.00     -0.018     -0.150      -0.269      -0.875
+49281.00     -0.053     -0.090      -0.200      -0.616
+49282.00     -0.060     -0.063      -0.203      -0.436
+49283.00     -0.041     -0.079      -0.139      -0.468
+49284.00     -0.019     -0.097      -0.012      -0.504
+49285.00      0.002     -0.129       0.041      -0.612
+49286.00      0.025     -0.176      -0.008      -0.592
+49287.00      0.044     -0.216      -0.073      -0.703
+49288.00      0.033     -0.252      -0.165      -0.728
+49289.00      0.003     -0.287      -0.191      -0.679
+49290.00     -0.029     -0.305      -0.226      -0.728
+49291.00     -0.065     -0.298      -0.212      -0.661
+49292.00     -0.092     -0.283      -0.207      -0.673
+49293.00     -0.090     -0.280      -0.214      -0.839
+49294.00     -0.042     -0.292      -0.163      -0.758
+49295.00      0.047     -0.338      -0.167      -0.888
+49296.00      0.140     -0.351      -0.101      -0.603
+49297.00      0.200     -0.307      -0.052      -0.400
+49298.00      0.206     -0.247      -0.126      -0.294
+49299.00      0.154     -0.184      -0.280      -0.248
+49300.00      0.041     -0.128      -0.401      -0.357
+49301.00     -0.127     -0.094      -0.430      -0.609
+49302.00     -0.270     -0.077      -0.443      -0.692
+49303.00     -0.358     -0.061      -0.561      -0.559
+49304.00     -0.395     -0.056      -0.736      -0.157
+49305.00     -0.373     -0.087      -0.845      -0.052
+49306.00     -0.303     -0.148      -0.771      -0.224
+49307.00     -0.213     -0.209      -0.487      -0.530
+49308.00     -0.136     -0.267      -0.210      -0.585
+49309.00     -0.081     -0.350      -0.128      -0.495
+49310.00     -0.037     -0.428      -0.108      -0.310
+49311.00     -0.006     -0.453      -0.048      -0.198
+49312.00      0.000     -0.423       0.027      -0.091
+49313.00     -0.012     -0.370       0.037      -0.240
+49314.00     -0.031     -0.314      -0.052      -0.320
+49315.00     -0.052     -0.256      -0.126      -0.537
+49316.00     -0.074     -0.206      -0.212      -0.675
+49317.00     -0.086     -0.170      -0.299      -0.788
+49318.00     -0.085     -0.135      -0.287      -0.857
+49319.00     -0.081     -0.085      -0.275      -0.814
+49320.00     -0.075     -0.042      -0.139      -0.784
+49321.00     -0.063     -0.041      -0.038      -0.732
+49322.00     -0.051     -0.090       0.027      -0.604
+49323.00     -0.071     -0.161       0.044      -0.399
+49324.00     -0.111     -0.198      -0.031      -0.194
+49325.00     -0.152     -0.198      -0.005       0.126
+49326.00     -0.193     -0.190      -0.060       0.031
+49327.00     -0.228     -0.181      -0.152      -0.161
+49328.00     -0.245     -0.180      -0.275      -0.332
+49329.00     -0.229     -0.202      -0.360      -0.748
+49330.00     -0.188     -0.260      -0.412      -0.999
+49331.00     -0.139     -0.267      -0.402      -1.235
+49332.00     -0.098     -0.113      -0.379      -1.127
+49333.00     -0.068      0.095      -0.372      -0.754
+49334.00     -0.054      0.135      -0.286      -0.664
+49335.00     -0.050      0.018      -0.105      -0.726
+49336.00     -0.044     -0.134       0.015      -0.787
+49337.00     -0.019     -0.179       0.136      -0.741
+49338.00      0.020     -0.145       0.109      -0.608
+49339.00      0.052     -0.132       0.119      -0.454
+49340.00      0.061     -0.167       0.126      -0.202
+49341.00      0.047     -0.214       0.080      -0.142
+49342.00      0.016     -0.211      -0.015      -0.184
+49343.00     -0.024     -0.132      -0.121      -0.374
+49344.00     -0.052     -0.070      -0.245      -0.546
+49345.00     -0.059     -0.061      -0.363      -0.680
+49346.00     -0.051     -0.091      -0.427      -0.803
+49347.00     -0.037     -0.121      -0.437      -0.943
+49348.00     -0.019     -0.137      -0.290      -0.966
+49349.00      0.003     -0.147      -0.149      -0.802
+49350.00      0.021     -0.147      -0.123      -0.535
+49351.00      0.023     -0.114      -0.141      -0.258
+49352.00      0.034     -0.083      -0.163      -0.103
+49353.00      0.067     -0.088      -0.177      -0.021
+49354.00      0.110     -0.124      -0.161      -0.144
+49355.00      0.145     -0.159      -0.134      -0.166
+49356.00      0.150     -0.169      -0.164      -0.343
+49357.00      0.107     -0.156      -0.231      -0.494
+49358.00      0.009     -0.124      -0.343      -0.613
+49359.00     -0.126     -0.190      -0.348      -0.722
+49360.00     -0.242     -0.240      -0.340      -0.553
+49361.00     -0.319     -0.184      -0.291      -0.268
+49362.00     -0.368     -0.101      -0.283      -0.056
+49363.00     -0.391     -0.042      -0.308      -0.076
+49364.00     -0.389     -0.034      -0.354      -0.317
+49365.00     -0.368     -0.069      -0.408      -0.499
+49366.00     -0.326     -0.111      -0.441      -0.522
+49367.00     -0.260     -0.129      -0.459      -0.425
+49368.00     -0.197     -0.114      -0.436      -0.210
+49369.00     -0.160     -0.080      -0.426      -0.036
+49370.00     -0.146     -0.013      -0.415      -0.074
+49371.00     -0.147      0.039      -0.434      -0.265
+49372.00     -0.150      0.039      -0.501      -0.403
+49373.00     -0.143     -0.053      -0.566      -0.464
+49374.00     -0.140     -0.124      -0.555      -0.434
+49375.00     -0.144     -0.092      -0.517      -0.698
+49376.00     -0.138     -0.050      -0.330      -0.717
+49377.00     -0.113     -0.020      -0.226      -0.753
+49378.00     -0.076     -0.001      -0.137      -0.447
+49379.00     -0.044     -0.028      -0.168      -0.130
+49380.00     -0.042     -0.071      -0.318      -0.017
+49381.00     -0.078     -0.112      -0.452      -0.075
+49382.00     -0.137     -0.147      -0.540      -0.332
+49383.00     -0.209     -0.148      -0.524      -0.545
+49384.00     -0.292     -0.098      -0.541      -0.428
+49385.00     -0.386     -0.015      -0.704      -0.183
+49386.00     -0.479      0.079      -0.763      -0.240
+49387.00     -0.535      0.167      -0.769      -0.421
+49388.00     -0.549      0.272      -0.607      -0.471
+49389.00     -0.528      0.348      -0.477      -0.367
+49390.00     -0.476      0.334      -0.298      -0.283
+49391.00     -0.378      0.193      -0.252      -0.332
+49392.00     -0.214     -0.044      -0.234      -0.456
+49393.00      0.003     -0.186      -0.167      -0.674
+49394.00      0.196     -0.215      -0.119      -0.729
+49395.00      0.272     -0.218      -0.131      -0.807
+49396.00      0.224     -0.218      -0.185      -0.720
+49397.00      0.099     -0.205      -0.313      -0.582
+49398.00     -0.057     -0.176      -0.396      -0.594
+49399.00     -0.190     -0.124      -0.508      -0.651
+49400.00     -0.255     -0.043      -0.626      -0.767
+49401.00     -0.234      0.080      -0.753      -0.706
+49402.00     -0.157      0.220      -0.799      -0.668
+49403.00     -0.059      0.348      -0.690      -0.804
+49404.00      0.034      0.428      -0.630      -0.924
+49405.00      0.087      0.401      -0.564      -1.039
+49406.00      0.075      0.235      -0.605      -0.960
+49407.00      0.024      0.047      -0.601      -0.542
+49408.00     -0.040     -0.103      -0.681      -0.363
+49409.00     -0.104     -0.213      -0.759      -0.492
+49410.00     -0.155     -0.277      -0.837      -0.656
+49411.00     -0.187     -0.265      -0.809      -0.829
+49412.00     -0.192     -0.177      -0.738      -0.782
+49413.00     -0.162     -0.043      -0.622      -0.567
+49414.00     -0.093      0.122      -0.640      -0.382
+49415.00     -0.002      0.311      -0.557      -0.382
+49416.00      0.088      0.502      -0.493      -0.423
+49417.00      0.158      0.647      -0.365      -0.288
+49418.00      0.180      0.679      -0.251      -0.296
+49419.00      0.129      0.540      -0.194      -0.267
+49420.00     -0.001      0.228      -0.226      -0.305
+49421.00     -0.170     -0.029      -0.214      -0.401
+49422.00     -0.320     -0.148      -0.170      -0.418
+49423.00     -0.389     -0.169      -0.150      -0.477
+49424.00     -0.363     -0.121      -0.172      -0.602
+49425.00     -0.280     -0.034      -0.193      -0.485
+49426.00     -0.188      0.052      -0.215      -0.410
+49427.00     -0.123      0.104      -0.224      -0.357
+49428.00     -0.131      0.143      -0.266      -0.427
+49429.00     -0.197      0.196      -0.246      -0.279
+49430.00     -0.244      0.219      -0.190      -0.260
+49431.00     -0.238      0.198       0.026      -0.213
+49432.00     -0.201      0.158       0.144      -0.415
+49433.00     -0.168      0.093       0.095      -0.711
+49434.00     -0.175      0.006       0.073      -0.725
+49435.00     -0.242     -0.056       0.018      -0.374
+49436.00     -0.314     -0.051      -0.093      -0.103
+49437.00     -0.354     -0.030      -0.250      -0.083
+49438.00     -0.361     -0.029      -0.421      -0.450
+49439.00     -0.345     -0.019      -0.497      -0.583
+49440.00     -0.322      0.002      -0.542      -0.627
+49441.00     -0.291     -0.003      -0.518      -0.640
+49442.00     -0.241     -0.050      -0.517      -0.620
+49443.00     -0.180     -0.102      -0.472      -0.668
+49444.00     -0.129     -0.125      -0.385      -0.612
+49445.00     -0.091     -0.122      -0.333      -0.581
+49446.00     -0.060     -0.105      -0.228      -0.645
+49447.00     -0.043     -0.087      -0.180      -0.588
+49448.00     -0.046     -0.073      -0.186      -0.537
+49449.00     -0.074     -0.063      -0.267      -0.580
+49450.00     -0.126     -0.046      -0.326      -0.566
+49451.00     -0.198     -0.014      -0.381      -0.690
+49452.00     -0.272      0.021      -0.420      -0.735
+49453.00     -0.330      0.043      -0.395      -0.686
+49454.00     -0.370      0.051      -0.350      -0.476
+49455.00     -0.384      0.051      -0.316      -0.357
+49456.00     -0.363      0.043      -0.321      -0.339
+49457.00     -0.316      0.021      -0.303      -0.300
+49458.00     -0.266     -0.008      -0.159      -0.090
+49459.00     -0.218     -0.023      -0.002      -0.058
+49460.00     -0.172     -0.014       0.129      -0.098
+49461.00     -0.138     -0.007       0.048      -0.273
+49462.00     -0.128     -0.026      -0.164      -0.382
+49463.00     -0.143     -0.048      -0.298      -0.180
+49464.00     -0.172     -0.037      -0.396       0.112
+49465.00     -0.200     -0.004      -0.387       0.062
+49466.00     -0.211      0.027      -0.435      -0.136
+49467.00     -0.201      0.060      -0.447      -0.336
+49468.00     -0.185      0.101      -0.450      -0.301
+49469.00     -0.170      0.111      -0.450      -0.390
+49470.00     -0.151      0.088      -0.394      -0.395
+49471.00     -0.152     -0.011      -0.377      -0.572
+49472.00     -0.162     -0.109      -0.388      -0.576
+49473.00     -0.162     -0.135      -0.364      -0.425
+49474.00     -0.153     -0.101      -0.346      -0.351
+49475.00     -0.141     -0.030      -0.249      -0.488
+49476.00     -0.134      0.042      -0.215      -0.447
+49477.00     -0.130      0.088      -0.316      -0.381
+49478.00     -0.131      0.125      -0.496      -0.346
+49479.00     -0.144      0.172      -0.656      -0.478
+49480.00     -0.161      0.204      -0.620      -0.578
+49481.00     -0.162      0.187      -0.568      -0.695
+49482.00     -0.154      0.118      -0.478      -0.615
+49483.00     -0.155      0.019      -0.436      -0.405
+49484.00     -0.190     -0.099      -0.384      -0.436
+49485.00     -0.219     -0.236      -0.294      -0.519
+49486.00     -0.235     -0.300      -0.178      -0.641
+49487.00     -0.254     -0.245       0.053      -0.640
+49488.00     -0.270     -0.117       0.158      -0.567
+49489.00     -0.283      0.004       0.121      -0.580
+49490.00     -0.303      0.026      -0.100      -0.648
+49491.00     -0.331     -0.089      -0.312      -0.600
+49492.00     -0.335     -0.200      -0.442      -0.552
+49493.00     -0.302     -0.224      -0.512      -0.674
+49494.00     -0.243     -0.188      -0.428      -0.913
+49495.00     -0.176     -0.108      -0.322      -0.887
+49496.00     -0.124     -0.004      -0.299      -0.721
+49497.00     -0.106      0.087      -0.278      -0.604
+49498.00     -0.096      0.144      -0.261      -0.653
+49499.00     -0.073      0.168      -0.206      -0.742
+49500.00     -0.033      0.174      -0.252      -0.875
+49501.00      0.017      0.178      -0.333      -0.673
+49502.00      0.059      0.193      -0.269      -0.533
+49503.00      0.073      0.210      -0.148      -0.354
+49504.00      0.048      0.188      -0.041      -0.231
+49505.00     -0.007      0.091      -0.152      -0.336
+49506.00     -0.067     -0.019      -0.382      -0.389
+49507.00     -0.117     -0.053      -0.538      -0.505
+49508.00     -0.147     -0.023      -0.568      -0.709
+49509.00     -0.135      0.014      -0.491      -0.684
+49510.00     -0.082      0.032      -0.376      -0.706
+49511.00     -0.011      0.034      -0.278      -0.593
+49512.00      0.057      0.006      -0.263      -0.591
+49513.00      0.107     -0.098      -0.244      -0.730
+49514.00      0.135     -0.263      -0.208      -0.818
+49515.00      0.140     -0.418      -0.103      -0.865
+49516.00      0.136     -0.489      -0.005      -0.772
+49517.00      0.144     -0.425       0.015      -0.601
+49518.00      0.175     -0.182      -0.081      -0.603
+49519.00      0.230      0.261      -0.291      -0.491
+49520.00      0.267      0.575      -0.521      -0.665
+49521.00      0.267      0.617      -0.603      -0.788
+49522.00      0.229      0.489      -0.578      -0.939
+49523.00      0.150      0.287      -0.448      -0.959
+49524.00      0.039      0.091      -0.340      -0.720
+49525.00     -0.095     -0.045      -0.277      -0.416
+49526.00     -0.247     -0.108      -0.196      -0.393
+49527.00     -0.392     -0.109      -0.208      -0.497
+49528.00     -0.479     -0.073      -0.236      -0.656
+49529.00     -0.459     -0.034      -0.377      -0.592
+49530.00     -0.330     -0.001      -0.394      -0.475
+49531.00     -0.180      0.052      -0.324      -0.398
+49532.00     -0.080      0.115      -0.273      -0.159
+49533.00     -0.041      0.115      -0.308      -0.207
+49534.00     -0.052     -0.045      -0.495      -0.349
+49535.00     -0.065     -0.222      -0.697       0.054
+49536.00     -0.064     -0.215      -0.588      -0.124
+49537.00     -0.042     -0.102      -0.382      -0.122
+49538.00     -0.003      0.025      -0.112      -0.142
+49539.00      0.027      0.098       0.061      -0.048
+49540.00      0.034      0.062       0.211      -0.001
+49541.00      0.018      0.006       0.302       0.093
+49542.00     -0.011      0.022       0.313       0.162
+49543.00     -0.046      0.052       0.265       0.216
+49544.00     -0.084      0.088       0.232       0.137
+49545.00     -0.110      0.097       0.262       0.222
+49546.00     -0.106      0.064       0.241       0.092
+49547.00     -0.069     -0.017       0.115      -0.032
+49548.00     -0.009     -0.124      -0.057      -0.161
+49549.00      0.028     -0.163      -0.255      -0.302
+49550.00      0.012     -0.108      -0.365      -0.501
+49551.00     -0.047     -0.019      -0.328      -0.649
+49552.00     -0.131      0.030      -0.139      -0.503
+49553.00     -0.213     -0.045       0.027      -0.279
+49554.00     -0.233     -0.197       0.173      -0.081
+49555.00     -0.151     -0.370       0.241      -0.226
+49556.00     -0.042     -0.444       0.248      -0.441
+49557.00      0.025     -0.378       0.175      -0.537
+49558.00      0.056     -0.244       0.139      -0.472
+49559.00      0.059     -0.103       0.136      -0.248
+49560.00      0.047     -0.028       0.121      -0.079
+49561.00      0.036     -0.088       0.086       0.118
+49562.00      0.019     -0.165      -0.020       0.114
+49563.00     -0.013     -0.070      -0.144       0.069
+49564.00     -0.051      0.142      -0.194      -0.039
+49565.00     -0.085      0.314      -0.214       0.009
+49566.00     -0.115      0.283      -0.041       0.065
+49567.00     -0.142     -0.040       0.095       0.075
+49568.00     -0.148     -0.451       0.235       0.057
+49569.00     -0.109     -0.781       0.386       0.071
+49570.00     -0.083     -0.917       0.110       0.427
+49571.00     -0.107     -0.831      -0.023       0.440
+49572.00     -0.148     -0.610      -0.088       0.308
+49573.00     -0.159     -0.364      -0.069       0.245
+49574.00     -0.090     -0.175       0.053      -0.113
+49575.00      0.042     -0.011       0.144      -0.323
+49576.00      0.225      0.290       0.141      -0.369
+49577.00      0.334      0.529       0.009      -0.351
+49578.00      0.307      0.519      -0.151      -0.359
+49579.00      0.207      0.340      -0.198      -0.479
+49580.00      0.098      0.101      -0.114      -0.344
+49581.00      0.029     -0.113       0.029      -0.151
+49582.00      0.016     -0.256       0.226       0.002
+49583.00      0.051     -0.321       0.349       0.093
+49584.00      0.118     -0.317       0.341       0.001
+49585.00      0.189     -0.264       0.302      -0.167
+49586.00      0.237     -0.193       0.237      -0.154
+49587.00      0.247     -0.122       0.188      -0.076
+49588.00      0.211     -0.064       0.100       0.031
+49589.00      0.115     -0.045       0.063       0.163
+49590.00     -0.004     -0.066      -0.011       0.153
+49591.00     -0.066     -0.098      -0.127       0.120
+49592.00     -0.040     -0.116      -0.229       0.021
+49593.00      0.034     -0.115      -0.240       0.056
+49594.00      0.101     -0.103      -0.093       0.050
+49595.00      0.127     -0.076       0.070       0.025
+49596.00      0.159     -0.043       0.283      -0.087
+49597.00      0.215     -0.087       0.414      -0.058
+49598.00      0.258     -0.157       0.368       0.092
+49599.00      0.259     -0.176       0.331       0.373
+49600.00      0.224     -0.150       0.149       0.403
+49601.00      0.166     -0.101       0.077       0.224
+49602.00      0.099     -0.047       0.027      -0.007
+49603.00      0.041      0.013       0.047      -0.177
+49604.00     -0.001      0.028       0.044      -0.133
+49605.00     -0.015      0.026       0.047      -0.194
+49606.00     -0.010      0.036      -0.038      -0.085
+49607.00     -0.001      0.028      -0.015      -0.123
+49608.00      0.012     -0.011      -0.025      -0.162
+49609.00      0.020     -0.080       0.043       0.009
+49610.00      0.011     -0.161       0.132       0.049
+49611.00     -0.006     -0.223       0.172       0.207
+49612.00      0.007     -0.244       0.213       0.165
+49613.00      0.050     -0.231       0.212       0.057
+49614.00      0.084     -0.216       0.217       0.056
+49615.00      0.078     -0.216       0.204      -0.155
+49616.00      0.023     -0.230       0.113      -0.131
+49617.00     -0.057     -0.178       0.098      -0.092
+49618.00     -0.119     -0.100       0.009      -0.010
+49619.00     -0.117     -0.071      -0.046       0.058
+49620.00     -0.051     -0.070      -0.146      -0.085
+49621.00      0.036     -0.072      -0.160      -0.028
+49622.00      0.089     -0.079      -0.070       0.081
+49623.00      0.075     -0.091       0.145       0.050
+49624.00      0.038     -0.116       0.324      -0.036
+49625.00      0.004     -0.147       0.360      -0.143
+49626.00     -0.029     -0.184       0.282      -0.003
+49627.00     -0.060     -0.216       0.230       0.288
+49628.00     -0.079     -0.231       0.217       0.440
+49629.00     -0.066     -0.216       0.131       0.399
+49630.00     -0.017     -0.188       0.034       0.345
+49631.00      0.022     -0.246      -0.146      -0.031
+49632.00     -0.019     -0.360      -0.267      -0.084
+49633.00     -0.062     -0.447      -0.226      -0.321
+49634.00     -0.044     -0.461      -0.042      -0.483
+49635.00     -0.012     -0.399       0.132      -0.474
+49636.00      0.012     -0.307       0.161      -0.349
+49637.00      0.044     -0.238       0.159      -0.121
+49638.00      0.090     -0.193       0.128      -0.022
+49639.00      0.131     -0.133       0.144       0.148
+49640.00      0.147     -0.046       0.240       0.085
+49641.00      0.144      0.025       0.303       0.147
+49642.00      0.133      0.033       0.351       0.121
+49643.00      0.127     -0.040       0.325       0.102
+49644.00      0.142     -0.182       0.364       0.047
+49645.00      0.161     -0.285       0.301       0.013
+49646.00      0.158     -0.306       0.237      -0.129
+49647.00      0.139     -0.274       0.142      -0.079
+49648.00      0.125     -0.211       0.057      -0.192
+49649.00      0.124     -0.133       0.019      -0.233
+49650.00      0.134     -0.079       0.077      -0.277
+49651.00      0.152     -0.083       0.221      -0.285
+49652.00      0.149     -0.121       0.293      -0.240
+49653.00      0.097     -0.176       0.258      -0.295
+49654.00     -0.001     -0.167       0.068      -0.379
+49655.00     -0.111     -0.061      -0.030      -0.091
+49656.00     -0.186      0.070      -0.101      -0.040
+49657.00     -0.180      0.142      -0.131       0.006
+49658.00     -0.077      0.098      -0.268      -0.139
+49659.00      0.025      0.035      -0.528      -0.220
+49660.00      0.079     -0.007      -0.928      -0.121
+49661.00      0.109     -0.046      -0.872      -0.297
+49662.00      0.136     -0.072      -0.531      -0.477
+49663.00      0.140     -0.045      -0.135      -0.454
+49664.00      0.094      0.001       0.126      -0.280
+49665.00      0.029     -0.015       0.127       0.178
+49666.00      0.014     -0.127       0.014       0.343
+49667.00      0.048     -0.249       0.000       0.466
+49668.00      0.076     -0.301       0.113       0.332
+49669.00      0.078     -0.297       0.127       0.293
+49670.00      0.068     -0.277       0.076       0.225
+49671.00      0.057     -0.242       0.057       0.040
+49672.00      0.052     -0.181       0.038      -0.157
+49673.00      0.060     -0.099       0.003      -0.189
+49674.00      0.075     -0.016      -0.068      -0.337
+49675.00      0.078      0.048      -0.063      -0.384
+49676.00      0.063      0.071      -0.093      -0.501
+49677.00      0.035      0.037      -0.084      -0.444
+49678.00     -0.006     -0.062       0.013      -0.427
+49679.00     -0.058     -0.207       0.124      -0.165
+49680.00     -0.092     -0.291       0.188       0.025
+49681.00     -0.104     -0.303       0.220       0.068
+49682.00     -0.109     -0.291       0.133       0.039
+49683.00     -0.106     -0.270       0.026       0.057
+49684.00     -0.092     -0.246       0.022      -0.069
+49685.00     -0.079     -0.244       0.060      -0.113
+49686.00     -0.083     -0.283       0.080      -0.239
+49687.00     -0.111     -0.300      -0.049      -0.403
+49688.00     -0.155     -0.269      -0.210      -0.303
+49689.00     -0.194     -0.228      -0.283      -0.268
+49690.00     -0.205     -0.205      -0.287      -0.561
+49691.00     -0.184     -0.188       0.037      -0.721
+49692.00     -0.155     -0.180       0.143      -0.589
+49693.00     -0.123     -0.215       0.002      -0.181
+49694.00     -0.112     -0.236      -0.037       0.147
+49695.00     -0.110     -0.190      -0.010       0.156
+49696.00     -0.114     -0.089       0.138       0.261
+49697.00     -0.119      0.007       0.185       0.138
+49698.00     -0.103      0.042       0.198       0.212
+49699.00     -0.057     -0.004       0.189       0.098
+49700.00      0.007     -0.133       0.158       0.051
+49701.00      0.052     -0.246       0.161      -0.090
+49702.00      0.066     -0.303       0.137      -0.393
+49703.00      0.049     -0.316       0.177      -0.493
+49704.00      0.013     -0.306       0.180      -0.531
+49705.00     -0.018     -0.299       0.215      -0.667
+49706.00     -0.025     -0.290       0.193      -0.520
+49707.00     -0.004     -0.254       0.171      -0.189
+49708.00      0.009     -0.184       0.130       0.214
+49709.00     -0.001     -0.110       0.138       0.470
+49710.00     -0.030     -0.068       0.059       0.566
+49711.00     -0.065     -0.064      -0.045       0.582
+49712.00     -0.086     -0.081      -0.054       0.493
+49713.00     -0.087     -0.112      -0.005       0.339
+49714.00     -0.077     -0.163       0.102       0.310
+49715.00     -0.059     -0.227       0.211       0.334
+49716.00     -0.035     -0.320       0.338       0.598
+49717.00     -0.014     -0.384       0.467       0.946
+49718.00     -0.001     -0.389       0.570       0.901
+49719.00      0.004     -0.357       0.723       0.680
+49720.00      0.006     -0.290       0.109      -0.047
+49721.00      0.008     -0.204      -0.056       0.019
+49722.00      0.011     -0.121      -0.187       0.022
+49723.00      0.001     -0.046      -0.186       0.105
+49724.00     -0.028      0.012      -0.093       0.116
+49725.00     -0.064      0.028      -0.108       0.013
+49726.00     -0.085     -0.002      -0.160      -0.054
+49727.00     -0.089     -0.061      -0.163      -0.193
+49728.00     -0.086     -0.137      -0.158      -0.434
+49729.00     -0.069     -0.177      -0.091      -0.591
+49730.00     -0.037     -0.167      -0.059      -0.768
+49731.00     -0.004     -0.110       0.013      -0.867
+49732.00      0.017     -0.037       0.161      -0.972
+49733.00      0.021     -0.007       0.274      -0.937
+49734.00     -0.006     -0.061       0.295      -0.632
+49735.00     -0.088     -0.212       0.226      -0.294
+49736.00     -0.226     -0.453       0.140      -0.019
+49737.00     -0.328     -0.605       0.092       0.224
+49738.00     -0.350     -0.599       0.025       0.249
+49739.00     -0.320     -0.492       0.007       0.121
+49740.00     -0.260     -0.335      -0.097       0.002
+49741.00     -0.190     -0.187      -0.068      -0.163
+49742.00     -0.144     -0.110       0.033      -0.200
+49743.00     -0.150     -0.104       0.151      -0.219
+49744.00     -0.198     -0.171       0.290       0.066
+49745.00     -0.252     -0.327       0.385       0.452
+49746.00     -0.293     -0.516       0.436       0.474
+49747.00     -0.310     -0.639       0.446       0.356
+49748.00     -0.290     -0.577       0.382      -0.004
+49749.00     -0.230     -0.301       0.226      -0.118
+49750.00     -0.169     -0.088      -0.011      -0.156
+49751.00     -0.127     -0.063      -0.057       0.092
+49752.00     -0.097     -0.104      -0.132       0.250
+49753.00     -0.071     -0.125      -0.171       0.417
+49754.00     -0.050     -0.127      -0.157       0.311
+49755.00     -0.046     -0.101      -0.163       0.203
+49756.00     -0.074     -0.061      -0.115       0.029
+49757.00     -0.141     -0.050      -0.116      -0.046
+49758.00     -0.234     -0.076      -0.128      -0.283
+49759.00     -0.331     -0.102      -0.100      -0.396
+49760.00     -0.412     -0.104      -0.061      -0.617
+49761.00     -0.449     -0.085       0.011      -0.556
+49762.00     -0.417     -0.058      -0.030      -0.305
+49763.00     -0.313     -0.033      -0.133      -0.169
+49764.00     -0.196     -0.104      -0.284      -0.067
+49765.00     -0.117     -0.179      -0.404      -0.012
+49766.00     -0.085     -0.191      -0.430      -0.189
+49767.00     -0.090     -0.157      -0.413      -0.175
+49768.00     -0.132     -0.090      -0.341      -0.142
+49769.00     -0.202     -0.031      -0.269      -0.152
+49770.00     -0.286     -0.028      -0.125      -0.044
+49771.00     -0.369     -0.094       0.050       0.020
+49772.00     -0.430     -0.166       0.288       0.176
+49773.00     -0.447     -0.185       0.504       0.452
+49774.00     -0.427     -0.178       0.620       0.580
+49775.00     -0.378     -0.154       0.645       0.459
+49776.00     -0.304     -0.108       0.627       0.276
+49777.00     -0.224     -0.065      -0.112       0.159
+49778.00     -0.152     -0.068      -0.145       0.195
+49779.00     -0.095     -0.098      -0.253       0.376
+49780.00     -0.048     -0.100      -0.288       0.633
+49781.00     -0.012     -0.071      -0.351       0.694
+49782.00      0.004     -0.028      -0.310       0.519
+49783.00     -0.003      0.015      -0.187       0.393
+49784.00     -0.024      0.036      -0.024       0.206
+49785.00     -0.035      0.012       0.076       0.043
+49786.00     -0.036     -0.052       0.089       0.008
+49787.00     -0.040     -0.115       0.131      -0.255
+49788.00     -0.053     -0.141       0.160      -0.370
+49789.00     -0.076     -0.122       0.190      -0.483
+49790.00     -0.111     -0.060       0.165      -0.234
+49791.00     -0.154      0.057       0.090      -0.050
+49792.00     -0.179      0.172      -0.011       0.074
+49793.00     -0.177      0.246      -0.150      -0.108
+49794.00     -0.152      0.272      -0.236      -0.221
+49795.00     -0.113      0.264      -0.218      -0.338
+49796.00     -0.073      0.238      -0.145      -0.119
+49797.00     -0.050      0.184      -0.074      -0.029
+49798.00     -0.056      0.092       0.076       0.164
+49799.00     -0.091     -0.018       0.156       0.142
+49800.00     -0.126     -0.098       0.311       0.203
+49801.00     -0.133     -0.129       0.384       0.201
+49802.00     -0.118     -0.139       0.396       0.383
+49803.00     -0.099     -0.147       0.300       0.464
+49804.00     -0.082     -0.138       0.123       0.334
+49805.00     -0.074     -0.107      -0.028       0.191
+49806.00     -0.063     -0.078      -0.159       0.161
+49807.00     -0.044     -0.063      -0.215       0.197
+49808.00     -0.013     -0.047      -0.348       0.340
+49809.00      0.020     -0.020      -0.368       0.374
+49810.00      0.036      0.003      -0.321       0.226
+49811.00      0.029      0.003      -0.185       0.110
+49812.00      0.008     -0.020      -0.038      -0.005
+49813.00     -0.033     -0.064       0.078      -0.098
+49814.00     -0.097     -0.116       0.184      -0.139
+49815.00     -0.179     -0.152       0.275      -0.218
+49816.00     -0.258     -0.157       0.252      -0.308
+49817.00     -0.320     -0.147       0.220      -0.444
+49818.00     -0.349     -0.141       0.133      -0.279
+49819.00     -0.329     -0.122       0.055       0.029
+49820.00     -0.272     -0.051      -0.040       0.140
+49821.00     -0.156      0.014      -0.141      -0.001
+49822.00     -0.044      0.043      -0.309      -0.250
+49823.00      0.004      0.051      -0.405      -0.409
+49824.00      0.003      0.053      -0.344      -0.173
+49825.00     -0.028      0.044      -0.294      -0.033
+49826.00     -0.074      0.028      -0.165       0.067
+49827.00     -0.124      0.039      -0.059       0.082
+49828.00     -0.192      0.039       0.099       0.030
+49829.00     -0.243      0.036       0.236       0.001
+49830.00     -0.254      0.043       0.323       0.097
+49831.00     -0.241      0.042       0.284       0.184
+49832.00     -0.214      0.039       0.131       0.148
+49833.00     -0.187      0.053       0.003       0.190
+49834.00     -0.185      0.091      -0.141       0.023
+49835.00     -0.204      0.146      -0.241      -0.010
+49836.00     -0.222      0.205      -0.269      -0.052
+49837.00     -0.228      0.247      -0.323      -0.093
+49838.00     -0.227      0.240      -0.322      -0.097
+49839.00     -0.217      0.157      -0.268      -0.050
+49840.00     -0.184      0.006      -0.113       0.029
+49841.00     -0.133     -0.114       0.021      -0.016
+49842.00     -0.086     -0.168       0.206       0.029
+49843.00     -0.054     -0.162       0.396       0.099
+49844.00     -0.028     -0.105       0.380       0.042
+49845.00     -0.011     -0.033       0.269      -0.061
+49846.00     -0.023      0.002       0.075      -0.045
+49847.00     -0.066     -0.009      -0.042       0.040
+49848.00     -0.131     -0.045      -0.039       0.223
+49849.00     -0.180     -0.064      -0.127       0.203
+49850.00     -0.201     -0.064      -0.171      -0.062
+49851.00     -0.208     -0.048      -0.252      -0.382
+49852.00     -0.216     -0.017      -0.254      -0.207
+49853.00     -0.224      0.005      -0.227       0.085
+49854.00     -0.211      0.003      -0.147       0.235
+49855.00     -0.165      0.007      -0.014       0.213
+49856.00     -0.104      0.052       0.108       0.252
+49857.00     -0.044      0.129       0.190       0.214
+49858.00      0.007      0.204       0.350       0.261
+49859.00      0.037      0.250       0.467       0.295
+49860.00      0.034      0.249       0.448       0.370
+49861.00     -0.013      0.197       0.309       0.293
+49862.00     -0.104      0.110       0.090       0.291
+49863.00     -0.196      0.048      -0.008       0.132
+49864.00     -0.250      0.026      -0.035       0.093
+49865.00     -0.258      0.014       0.010       0.080
+49866.00     -0.232      0.007       0.021       0.075
+49867.00     -0.196      0.013       0.025       0.140
+49868.00     -0.158      0.042       0.033       0.173
+49869.00     -0.127      0.080       0.126       0.123
+49870.00     -0.119      0.067       0.273       0.100
+49871.00     -0.137     -0.001       0.467       0.119
+49872.00     -0.152     -0.076       0.507       0.284
+49873.00     -0.147     -0.119       0.288       0.239
+49874.00     -0.128     -0.118       0.068       0.056
+49875.00     -0.095     -0.045      -0.142      -0.066
+49876.00     -0.036      0.143      -0.171      -0.059
+49877.00      0.041      0.377      -0.085      -0.232
+49878.00      0.105      0.519      -0.049      -0.454
+49879.00      0.136      0.531      -0.021      -0.624
+49880.00      0.110      0.454       0.019      -0.440
+49881.00      0.017      0.298       0.023      -0.190
+49882.00     -0.118      0.075       0.095      -0.016
+49883.00     -0.253     -0.165       0.158      -0.008
+49884.00     -0.310     -0.292       0.201       0.026
+49885.00     -0.282     -0.275       0.197       0.249
+49886.00     -0.207     -0.170       0.321       0.359
+49887.00     -0.117     -0.034       0.445       0.405
+49888.00     -0.033      0.071       0.452       0.477
+49889.00      0.051      0.096       0.339       0.540
+49890.00      0.184      0.079       0.052       0.629
+49891.00      0.349      0.070      -0.124       0.453
+49892.00      0.500      0.073      -0.087       0.332
+49893.00      0.610      0.052       0.028       0.269
+49894.00      0.637     -0.004       0.100       0.291
+49895.00      0.516     -0.072       0.133       0.288
+49896.00      0.225     -0.110       0.145       0.247
+49897.00     -0.058     -0.009       0.184       0.304
+49898.00     -0.234      0.133       0.309       0.308
+49899.00     -0.318      0.222       0.469       0.440
+49900.00     -0.333      0.274       0.582       0.725
+49901.00     -0.294      0.296       0.523       0.826
+49902.00     -0.235      0.288       0.296       0.697
+49903.00     -0.196      0.243       0.054       0.491
+49904.00     -0.209      0.167      -0.013       0.173
+49905.00     -0.293      0.065      -0.032      -0.025
+49906.00     -0.417     -0.052       0.054      -0.229
+49907.00     -0.531     -0.154       0.068      -0.326
+49908.00     -0.591     -0.201       0.066      -0.127
+49909.00     -0.556     -0.179       0.051       0.145
+49910.00     -0.400     -0.115      -0.003       0.161
+49911.00     -0.223     -0.148       0.062      -0.032
+49912.00     -0.078     -0.185       0.131      -0.080
+49913.00      0.031     -0.153       0.108       0.051
+49914.00      0.093     -0.085       0.105       0.155
+49915.00      0.105     -0.010       0.208       0.196
+49916.00      0.076      0.021       0.332       0.087
+49917.00      0.039     -0.028       0.274       0.015
+49918.00      0.024     -0.083      -0.037      -0.008
+49919.00      0.022     -0.078      -0.250      -0.041
+49920.00      0.016     -0.013      -0.347      -0.215
+49921.00      0.013      0.062      -0.308      -0.299
+49922.00      0.024      0.102      -0.248      -0.265
+49923.00      0.035      0.094      -0.243      -0.252
+49924.00      0.040      0.070      -0.290      -0.318
+49925.00      0.072      0.186      -0.216      -0.242
+49926.00      0.138      0.440      -0.109      -0.132
+49927.00      0.206      0.740       0.054      -0.022
+49928.00      0.246      0.986       0.153       0.155
+49929.00      0.245      1.074       0.218       0.305
+49930.00      0.191      0.898       0.264       0.266
+49931.00      0.073      0.428       0.126       0.140
+49932.00     -0.028      0.038      -0.043      -0.224
+49933.00     -0.075     -0.133      -0.169      -0.452
+49934.00     -0.086     -0.152      -0.141      -0.547
+49935.00     -0.076     -0.071      -0.105      -0.535
+49936.00     -0.059      0.058      -0.079      -0.177
+49937.00     -0.046      0.162      -0.015       0.104
+49938.00     -0.055      0.187      -0.035       0.196
+49939.00     -0.105      0.204       0.057       0.031
+49940.00     -0.145      0.227       0.097      -0.140
+49941.00     -0.146      0.232       0.048      -0.012
+49942.00     -0.119      0.220      -0.039       0.191
+49943.00     -0.084      0.208      -0.007       0.306
+49944.00     -0.059      0.197       0.056       0.332
+49945.00     -0.046      0.165       0.109       0.198
+49946.00     -0.045      0.110       0.019       0.080
+49947.00     -0.036      0.066      -0.074       0.009
+49948.00     -0.021      0.060      -0.103      -0.137
+49949.00     -0.008      0.074      -0.034      -0.096
+49950.00     -0.003      0.087       0.056      -0.086
+49951.00     -0.019      0.094       0.119      -0.106
+49952.00     -0.069      0.099       0.140       0.066
+49953.00     -0.139      0.092       0.204       0.153
+49954.00     -0.149      0.077       0.178       0.353
+49955.00     -0.103      0.138       0.120       0.432
+49956.00     -0.069      0.237       0.039       0.460
+49957.00     -0.089      0.325       0.065       0.351
+49958.00     -0.138      0.302       0.018       0.158
+49959.00     -0.210      0.139      -0.063       0.015
+49960.00     -0.328      0.073      -0.190      -0.192
+49961.00     -0.470      0.133      -0.379      -0.387
+49962.00     -0.540      0.178      -0.441      -0.544
+49963.00     -0.502      0.166      -0.358      -0.605
+49964.00     -0.395      0.139      -0.244      -0.473
+49965.00     -0.263      0.112      -0.138      -0.274
+49966.00     -0.153      0.076      -0.055      -0.274
+49967.00     -0.102      0.041       0.039      -0.203
+49968.00     -0.089      0.031       0.058      -0.406
+49969.00     -0.088      0.040      -0.028      -0.381
+49970.00     -0.090      0.049      -0.059      -0.159
+49971.00     -0.089      0.062      -0.070       0.059
+49972.00     -0.087      0.088      -0.018       0.229
+49973.00     -0.083      0.104       0.088       0.099
+49974.00     -0.075      0.084       0.149      -0.066
+49975.00     -0.058      0.038       0.171      -0.172
+49976.00     -0.034     -0.000       0.164      -0.296
+49977.00     -0.006     -0.018       0.168      -0.198
+49978.00      0.013     -0.015       0.189      -0.153
+49979.00      0.010      0.026       0.258      -0.066
+49980.00     -0.011      0.098       0.334       0.027
+49981.00     -0.024      0.052       0.387       0.182
+49982.00     -0.018     -0.070       0.387       0.562
+49983.00     -0.002     -0.110       0.296       0.742
+49984.00      0.014     -0.065       0.220       0.638
+49985.00      0.030      0.002       0.102       0.418
+49986.00      0.046      0.037       0.085       0.163
+49987.00      0.060      0.031       0.042       0.103
+49988.00      0.062      0.043      -0.021       0.104
+49989.00      0.046      0.075      -0.139       0.024
+49990.00      0.018      0.086      -0.147      -0.035
+49991.00     -0.004      0.072      -0.117      -0.043
+49992.00     -0.010      0.066      -0.043      -0.124
+49993.00     -0.015      0.086       0.041       0.026
+49994.00     -0.042      0.103       0.054       0.122
+49995.00     -0.086      0.040       0.076       0.197
+49996.00     -0.120     -0.055       0.022       0.088
+49997.00     -0.125     -0.087       0.004       0.114
+49998.00     -0.107     -0.059      -0.026       0.062
+49999.00     -0.087      0.009      -0.057       0.113
+50000.00     -0.076      0.105      -0.092       0.173
+50001.00     -0.092      0.194      -0.039      -0.028
+50002.00     -0.122      0.210       0.002      -0.173
+50003.00     -0.135      0.067       0.018      -0.285
+50004.00     -0.110     -0.060      -0.059      -0.319
+50005.00     -0.059     -0.097      -0.094      -0.210
+50006.00     -0.006     -0.081      -0.118      -0.143
+50007.00      0.029     -0.043      -0.057      -0.067
+50008.00      0.045      0.012      -0.029       0.067
+50009.00      0.045      0.072       0.043       0.252
+50010.00      0.028      0.117       0.042       0.503
+50011.00     -0.005      0.137       0.064       0.805
+50012.00     -0.043      0.133       0.047       0.716
+50013.00     -0.067      0.104       0.031       0.552
+50014.00     -0.066      0.050       0.012       0.124
+50015.00     -0.042     -0.013       0.025       0.037
+50016.00     -0.021     -0.044       0.030       0.177
+50017.00     -0.014     -0.034       0.075       0.143
+50018.00     -0.022     -0.007       0.179       0.173
+50019.00     -0.041      0.025       0.310       0.087
+50020.00     -0.056      0.062       0.367       0.094
+50021.00     -0.053      0.099       0.390       0.139
+50022.00     -0.038      0.121       0.275       0.188
+50023.00     -0.023      0.114       0.242       0.306
+50024.00     -0.009      0.079       0.206       0.408
+50025.00      0.012      0.027       0.224       0.427
+50026.00      0.034     -0.031       0.230       0.478
+50027.00      0.042     -0.061       0.245       0.346
+50028.00      0.030     -0.026       0.285       0.316
+50029.00     -0.009      0.089       0.340       0.282
+50030.00     -0.093      0.267       0.351      -0.051
+50031.00     -0.179      0.415       0.373      -0.170
+50032.00     -0.186      0.441       0.421      -0.252
+50033.00     -0.106      0.354       0.375      -0.291
+50034.00      0.002      0.210       0.393      -0.130
+50035.00      0.080      0.061       0.446      -0.145
+50036.00      0.100     -0.025       0.422      -0.058
+50037.00      0.078     -0.007       0.320       0.007
+50038.00      0.033      0.062       0.232       0.247
+50039.00     -0.014      0.112       0.187       0.345
+50040.00     -0.047      0.132       0.183       0.462
+50041.00     -0.055      0.128       0.111       0.324
+50042.00     -0.042      0.107       0.046      -0.030
+50043.00     -0.018      0.056      -0.064      -0.070
+50044.00      0.001     -0.078      -0.117       0.126
+50045.00      0.023     -0.293      -0.016       0.126
+50046.00      0.051     -0.526       0.219       0.040
+50047.00      0.066     -0.694       0.376      -0.077
+50048.00      0.052     -0.736       0.481      -0.171
+50049.00      0.020     -0.630       0.413      -0.120
+50050.00     -0.009     -0.386       0.262      -0.102
+50051.00     -0.036     -0.151       0.173      -0.115
+50052.00     -0.071      0.022       0.128       0.030
+50053.00     -0.106      0.122       0.086       0.269
+50054.00     -0.119      0.146       0.030       0.424
+50055.00     -0.104      0.122       0.029       0.302
+50056.00     -0.058      0.092      -0.004       0.207
+50057.00      0.026      0.067       0.056       0.111
+50058.00      0.139      0.042       0.068      -0.138
+50059.00      0.220      0.007       0.191      -0.163
+50060.00      0.246     -0.031       0.217      -0.282
+50061.00      0.237     -0.058       0.245      -0.265
+50062.00      0.203     -0.077       0.358      -0.191
+50063.00      0.159     -0.098       0.350      -0.025
+50064.00      0.125     -0.129       0.316       0.033
+50065.00      0.107     -0.168       0.141       0.079
+50066.00      0.083     -0.159       0.018       0.026
+50067.00      0.050     -0.095      -0.035       0.166
+50068.00      0.022     -0.012       0.043       0.141
+50069.00      0.007      0.046       0.128       0.031
+50070.00     -0.000      0.029       0.149      -0.120
+50071.00     -0.007     -0.073       0.152      -0.107
+50072.00     -0.020     -0.155       0.179       0.036
+50073.00     -0.038     -0.201       0.310       0.179
+50074.00     -0.048     -0.244       0.421       0.078
+50075.00     -0.046     -0.259       0.533      -0.106
+50076.00     -0.046     -0.221       0.522      -0.237
+50077.00     -0.056     -0.150       0.347      -0.056
+50078.00     -0.057     -0.087       0.164      -0.097
+50079.00     -0.046     -0.027       0.097      -0.108
+50080.00     -0.036      0.047       0.159      -0.057
+50081.00     -0.030      0.112       0.081       0.155
+50082.00     -0.011      0.143       0.011       0.441
+50083.00      0.024      0.147       0.021       0.435
+50084.00      0.060      0.142       0.062       0.391
+50085.00      0.093      0.131       0.263       0.199
+50086.00      0.125      0.109       0.395       0.048
+50087.00      0.149      0.074       0.504      -0.170
+50088.00      0.165      0.025       0.607      -0.392
+50089.00      0.177     -0.036       0.634      -0.486
+50090.00      0.186     -0.098       0.592      -0.291
+50091.00      0.184     -0.142       0.482      -0.234
+50092.00      0.175     -0.154       0.364      -0.144
+50093.00      0.155     -0.141       0.182      -0.109
+50094.00      0.123     -0.130      -0.063      -0.169
+50095.00      0.087     -0.136      -0.146      -0.192
+50096.00      0.066     -0.143      -0.104      -0.239
+50097.00      0.060     -0.147      -0.056      -0.183
+50098.00      0.054     -0.173       0.059      -0.240
+50099.00      0.048     -0.221       0.150      -0.284
+50100.00      0.044     -0.264       0.343      -0.047
+50101.00      0.034     -0.291       0.472       0.191
+50102.00      0.013     -0.328       0.614       0.177
+50103.00     -0.009     -0.376       0.688      -0.070
+50104.00     -0.020     -0.389       0.550      -0.276
+50105.00     -0.020     -0.332       0.320      -0.183
+50106.00     -0.011     -0.221       0.077      -0.106
+50107.00     -0.006     -0.139       0.020      -0.090
+50108.00     -0.019     -0.101      -0.012      -0.188
+50109.00     -0.042     -0.097      -0.037      -0.177
+50110.00     -0.049     -0.122      -0.112      -0.060
+50111.00     -0.029     -0.162      -0.097      -0.067
+50112.00      0.012     -0.204       0.057      -0.186
+50113.00      0.073     -0.250       0.250      -0.287
+50114.00      0.128     -0.281       0.416      -0.400
+50115.00      0.160     -0.272       0.412      -0.401
+50116.00      0.162     -0.226       0.453      -0.406
+50117.00      0.135     -0.177       0.434      -0.388
+50118.00      0.078     -0.139       0.414      -0.294
+50119.00     -0.022     -0.096       0.271      -0.128
+50120.00     -0.172     -0.045       0.189       0.013
+50121.00     -0.378     -0.067       0.223      -0.064
+50122.00     -0.516     -0.146       0.247      -0.249
+50123.00     -0.530     -0.232       0.298      -0.449
+50124.00     -0.446     -0.288       0.336      -0.595
+50125.00     -0.299     -0.282       0.305      -0.488
+50126.00     -0.148     -0.215       0.178      -0.340
+50127.00     -0.028     -0.092       0.043      -0.107
+50128.00      0.122      0.064       0.045       0.324
+50129.00      0.281      0.201       0.146       0.614
+50130.00      0.369      0.254       0.264       0.577
+50131.00      0.376      0.215       0.430       0.325
+50132.00      0.324      0.125       0.464       0.007
+50133.00      0.242      0.025       0.408      -0.160
+50134.00      0.147     -0.055       0.320      -0.030
+50135.00      0.049     -0.087       0.224       0.091
+50136.00     -0.047     -0.092       0.155      -0.003
+50137.00     -0.128     -0.112       0.014      -0.048
+50138.00     -0.180     -0.144      -0.103      -0.050
+50139.00     -0.208     -0.163      -0.172       0.001
+50140.00     -0.223     -0.170      -0.132      -0.150
+50141.00     -0.225     -0.185       0.024      -0.151
+50142.00     -0.206     -0.205       0.081      -0.208
+50143.00     -0.176     -0.196       0.120      -0.200
+50144.00     -0.149     -0.150       0.196      -0.297
+50145.00     -0.128     -0.100       0.257      -0.265
+50146.00     -0.107     -0.078       0.295      -0.091
+50147.00     -0.093     -0.079       0.239       0.000
+50148.00     -0.091     -0.091       0.118      -0.042
+50149.00     -0.086     -0.121       0.006      -0.036
+50150.00     -0.079     -0.163      -0.110      -0.149
+50151.00     -0.080     -0.198      -0.194      -0.135
+50152.00     -0.086     -0.210      -0.218      -0.184
+50153.00     -0.088     -0.193      -0.200      -0.214
+50154.00     -0.081     -0.158      -0.131      -0.224
+50155.00     -0.078     -0.114       0.014      -0.083
+50156.00     -0.081     -0.078       0.178      -0.098
+50157.00     -0.084     -0.061       0.283       0.030
+50158.00     -0.079     -0.070       0.381      -0.128
+50159.00     -0.073     -0.081       0.397      -0.358
+50160.00     -0.074     -0.077       0.325      -0.561
+50161.00     -0.086     -0.063       0.248      -0.527
+50162.00     -0.114     -0.067       0.142      -0.346
+50163.00     -0.166     -0.095       0.104       0.084
+50164.00     -0.233     -0.134       0.047       0.261
+50165.00     -0.299     -0.168       0.030       0.152
+50166.00     -0.352     -0.183       0.057       0.086
+50167.00     -0.383     -0.156       0.138      -0.001
+50168.00     -0.381     -0.085       0.209       0.020
+50169.00     -0.338      0.010       0.289       0.075
+50170.00     -0.311      0.078       0.311      -0.029
+50171.00     -0.298      0.126       0.215      -0.013
+50172.00     -0.290      0.175       0.214      -0.019
+50173.00     -0.289      0.212       0.191       0.013
+50174.00     -0.293      0.209       0.181       0.177
+50175.00     -0.305      0.174       0.007       0.297
+50176.00     -0.335      0.132      -0.150       0.228
+50177.00     -0.383      0.087      -0.266       0.025
+50178.00     -0.408      0.039      -0.411       0.008
+50179.00     -0.398     -0.001      -0.521       0.040
+50180.00     -0.370     -0.024      -0.565       0.051
+50181.00     -0.330     -0.031      -0.525       0.110
+50182.00     -0.275     -0.035      -0.419       0.155
+50183.00     -0.214     -0.029      -0.254       0.044
+50184.00     -0.168      0.001      -0.036       0.060
+50185.00     -0.151      0.031       0.071       0.095
+50186.00     -0.159      0.027       0.074       0.059
+50187.00     -0.179     -0.006       0.036       0.010
+50188.00     -0.204     -0.034      -0.014      -0.162
+50189.00     -0.233     -0.054      -0.017      -0.203
+50190.00     -0.269     -0.097      -0.020      -0.153
+50191.00     -0.315     -0.187      -0.092      -0.012
+50192.00     -0.361     -0.298      -0.217       0.046
+50193.00     -0.398     -0.395      -0.281      -0.164
+50194.00     -0.425     -0.457      -0.276      -0.267
+50195.00     -0.433     -0.471      -0.243      -0.417
+50196.00     -0.404     -0.413      -0.102      -0.332
+50197.00     -0.318     -0.262       0.030      -0.163
+50198.00     -0.193     -0.074       0.072       0.076
+50199.00     -0.111      0.031       0.092       0.215
+50200.00     -0.094      0.098       0.094       0.294
+50201.00     -0.119      0.150       0.083       0.297
+50202.00     -0.170      0.160       0.113       0.442
+50203.00     -0.228      0.122       0.072       0.341
+50204.00     -0.279      0.063       0.017       0.107
+50205.00     -0.301      0.004      -0.140      -0.207
+50206.00     -0.226      0.013      -0.346      -0.513
+50207.00     -0.065      0.095      -0.521      -0.467
+50208.00      0.107      0.195      -0.561      -0.250
+50209.00      0.211      0.250      -0.544      -0.108
+50210.00      0.171      0.191      -0.464      -0.020
+50211.00     -0.034      0.011      -0.271      -0.229
+50212.00     -0.221     -0.121      -0.064      -0.238
+50213.00     -0.326     -0.156       0.094      -0.121
+50214.00     -0.372     -0.146       0.075      -0.009
+50215.00     -0.375     -0.125       0.026       0.094
+50216.00     -0.346     -0.086      -0.068       0.146
+50217.00     -0.301     -0.014      -0.123       0.188
+50218.00     -0.262      0.072      -0.076       0.080
+50219.00     -0.239      0.143       0.010       0.131
+50220.00     -0.219      0.191      -0.022       0.196
+50221.00     -0.194      0.224      -0.040       0.029
+50222.00     -0.175      0.227      -0.029      -0.074
+50223.00     -0.165      0.185       0.036      -0.073
+50224.00     -0.151      0.097       0.111      -0.017
+50225.00     -0.122     -0.023       0.233       0.097
+50226.00     -0.105     -0.104       0.293       0.314
+50227.00     -0.107     -0.129       0.304       0.362
+50228.00     -0.117     -0.100       0.229       0.525
+50229.00     -0.125     -0.036       0.056       0.468
+50230.00     -0.142      0.024      -0.037       0.450
+50231.00     -0.172      0.050      -0.030       0.434
+50232.00     -0.208      0.055       0.039       0.212
+50233.00     -0.239      0.052       0.074      -0.040
+50234.00     -0.255      0.054      -0.012      -0.355
+50235.00     -0.257      0.072      -0.074      -0.317
+50236.00     -0.249      0.097      -0.117      -0.034
+50237.00     -0.231      0.096      -0.053       0.322
+50238.00     -0.204      0.032       0.067       0.291
+50239.00     -0.177     -0.092       0.098       0.100
+50240.00     -0.158     -0.181       0.251      -0.105
+50241.00     -0.148     -0.202       0.342      -0.138
+50242.00     -0.146     -0.191       0.349       0.002
+50243.00     -0.147     -0.174       0.229       0.118
+50244.00     -0.145     -0.145       0.008       0.132
+50245.00     -0.138     -0.087      -0.141       0.200
+50246.00     -0.124      0.037      -0.186       0.001
+50247.00     -0.092      0.333      -0.111      -0.241
+50248.00     -0.049      0.581      -0.044      -0.365
+50249.00     -0.003      0.622      -0.010      -0.422
+50250.00      0.035      0.512       0.068      -0.436
+50251.00      0.050      0.319       0.024      -0.280
+50252.00      0.044      0.116       0.063      -0.288
+50253.00      0.021     -0.034       0.114      -0.109
+50254.00     -0.029     -0.089       0.197       0.115
+50255.00     -0.117     -0.019       0.251       0.266
+50256.00     -0.220      0.158       0.180       0.271
+50257.00     -0.319      0.310      -0.024       0.313
+50258.00     -0.400      0.374      -0.219       0.251
+50259.00     -0.429      0.291      -0.181       0.123
+50260.00     -0.370      0.071      -0.026       0.065
+50261.00     -0.286     -0.062       0.056      -0.107
+50262.00     -0.219     -0.072       0.017      -0.323
+50263.00     -0.184     -0.034      -0.142      -0.378
+50264.00     -0.195      0.025      -0.273       0.000
+50265.00     -0.239      0.089      -0.291       0.303
+50266.00     -0.284      0.133      -0.285       0.377
+50267.00     -0.304      0.159      -0.222       0.241
+50268.00     -0.303      0.177      -0.139       0.026
+50269.00     -0.301      0.187      -0.019      -0.031
+50270.00     -0.307      0.172       0.060       0.116
+50271.00     -0.317      0.136       0.206      -0.003
+50272.00     -0.326      0.098       0.173       0.025
+50273.00     -0.332      0.074       0.076      -0.000
+50274.00     -0.336      0.080      -0.051       0.139
+50275.00     -0.352      0.143      -0.058      -0.030
+50276.00     -0.368      0.224       0.076      -0.063
+50277.00     -0.351      0.258       0.231       0.046
+50278.00     -0.296      0.235       0.240       0.216
+50279.00     -0.223      0.192       0.188       0.347
+50280.00     -0.146      0.163       0.111       0.353
+50281.00     -0.068      0.160       0.053       0.196
+50282.00     -0.025      0.189       0.126       0.323
+50283.00     -0.029      0.219       0.196       0.391
+50284.00     -0.057      0.239       0.161       0.503
+50285.00     -0.087      0.262       0.020       0.446
+50286.00     -0.118      0.268      -0.125       0.416
+50287.00     -0.150      0.207      -0.156       0.188
+50288.00     -0.171      0.070      -0.006      -0.020
+50289.00     -0.189     -0.010       0.174      -0.169
+50290.00     -0.202     -0.025       0.221      -0.464
+50291.00     -0.206     -0.043       0.204      -0.591
+50292.00     -0.214     -0.054       0.067      -0.479
+50293.00     -0.238     -0.055       0.007      -0.261
+50294.00     -0.263     -0.057      -0.007      -0.117
+50295.00     -0.264     -0.058      -0.059      -0.103
+50296.00     -0.236     -0.031      -0.146      -0.106
+50297.00     -0.203      0.012      -0.244       0.240
+50298.00     -0.185      0.049      -0.267       0.280
+50299.00     -0.186      0.081      -0.156       0.310
+50300.00     -0.209      0.114      -0.002       0.242
+50301.00     -0.249      0.125      -0.012       0.222
+50302.00     -0.304      0.090      -0.133       0.418
+50303.00     -0.386      0.026      -0.228       0.380
+50304.00     -0.451      0.007      -0.222       0.238
+50305.00     -0.462      0.041      -0.133       0.245
+50306.00     -0.417      0.086      -0.129       0.158
+50307.00     -0.339      0.132      -0.190       0.230
+50308.00     -0.254      0.171      -0.201       0.094
+50309.00     -0.171      0.178      -0.043      -0.012
+50310.00     -0.100      0.161       0.187       0.058
+50311.00     -0.056      0.129       0.341       0.197
+50312.00     -0.043      0.094       0.401       0.342
+50313.00     -0.054      0.073       0.398       0.455
+50314.00     -0.077      0.067       0.312       0.475
+50315.00     -0.111      0.064       0.191       0.346
+50316.00     -0.151      0.056       0.180       0.129
+50317.00     -0.191      0.038       0.319       0.033
+50318.00     -0.227      0.008       0.417      -0.150
+50319.00     -0.256     -0.022       0.380      -0.184
+50320.00     -0.270     -0.034       0.291      -0.171
+50321.00     -0.263     -0.024       0.185      -0.022
+50322.00     -0.228     -0.020       0.132      -0.149
+50323.00     -0.164     -0.026       0.081      -0.171
+50324.00     -0.112      0.007      -0.040      -0.072
+50325.00     -0.077      0.059      -0.186       0.207
+50326.00     -0.046      0.095      -0.242       0.465
+50327.00     -0.025      0.132      -0.114       0.521
+50328.00     -0.018      0.180       0.073       0.364
+50329.00     -0.016      0.213       0.186       0.136
+50330.00     -0.007      0.191       0.138       0.245
+50331.00     -0.025      0.114       0.135       0.284
+50332.00     -0.070     -0.015       0.092       0.292
+50333.00     -0.084     -0.025       0.099       0.301
+50334.00     -0.058      0.028       0.110       0.130
+50335.00     -0.024      0.044       0.034       0.197
+50336.00     -0.007      0.039      -0.032       0.095
+50337.00     -0.011      0.019      -0.092       0.039
+50338.00     -0.012     -0.016      -0.078       0.090
+50339.00      0.002     -0.022      -0.089       0.108
+50340.00      0.017      0.018      -0.087       0.196
+50341.00      0.024      0.067      -0.049       0.210
+50342.00      0.025      0.089      -0.011       0.217
+50343.00      0.016      0.060      -0.061       0.214
+50344.00     -0.010     -0.022      -0.026       0.010
+50345.00     -0.049     -0.074       0.145      -0.086
+50346.00     -0.082     -0.043       0.285      -0.052
+50347.00     -0.113      0.079       0.452      -0.065
+50348.00     -0.148      0.190       0.453       0.142
+50349.00     -0.173      0.215       0.432       0.155
+50350.00     -0.169      0.138       0.310       0.082
+50351.00     -0.133     -0.001       0.203       0.005
+50352.00     -0.102     -0.040       0.019      -0.065
+50353.00     -0.080      0.039      -0.224       0.175
+50354.00     -0.056      0.171      -0.379       0.482
+50355.00     -0.032      0.314      -0.358       0.533
+50356.00     -0.020      0.431      -0.215       0.497
+50357.00     -0.019      0.450      -0.023       0.263
+50358.00     -0.019      0.313       0.096       0.136
+50359.00     -0.020      0.165       0.168       0.242
+50360.00      0.005      0.110       0.146       0.339
+50361.00      0.032      0.057       0.178       0.264
+50362.00      0.015     -0.016       0.180       0.154
+50363.00     -0.048     -0.065       0.130       0.038
+50364.00     -0.131     -0.059       0.098       0.072
+50365.00     -0.201      0.017       0.034       0.073
+50366.00     -0.232      0.122      -0.027       0.119
+50367.00     -0.226      0.216      -0.108       0.094
+50368.00     -0.202      0.269      -0.102       0.137
+50369.00     -0.166      0.278      -0.071       0.142
+50370.00     -0.116      0.247      -0.009       0.188
+50371.00     -0.063      0.209       0.034       0.365
+50372.00     -0.025      0.195       0.108       0.474
+50373.00     -0.034      0.142       0.200       0.418
+50374.00     -0.079      0.072       0.318       0.288
+50375.00     -0.111      0.048       0.439       0.149
+50376.00     -0.120      0.126       0.495       0.239
+50377.00     -0.121      0.265       0.491       0.203
+50378.00     -0.110      0.337       0.371       0.154
+50379.00     -0.082      0.272       0.225       0.212
+50380.00     -0.058      0.199       0.148       0.223
+50381.00     -0.045      0.169       0.041       0.407
+50382.00     -0.036      0.155      -0.036       0.618
+50383.00     -0.029      0.147      -0.114       0.782
+50384.00     -0.019      0.151      -0.060       0.679
+50385.00     -0.007      0.162       0.036       0.376
+50386.00      0.009      0.146       0.144       0.156
+50387.00      0.032      0.091       0.196       0.138
+50388.00      0.058      0.029       0.233       0.275
+50389.00      0.082     -0.009       0.213       0.404
+50390.00      0.093     -0.011       0.185       0.304
+50391.00      0.083      0.047       0.186       0.173
+50392.00      0.047      0.169       0.212       0.188
+50393.00     -0.008      0.201       0.193       0.100
+50394.00     -0.052      0.146       0.175      -0.031
+50395.00     -0.051      0.081       0.146      -0.181
+50396.00     -0.050      0.099       0.115      -0.436
+50397.00     -0.074      0.185       0.089      -0.645
+50398.00     -0.096      0.250       0.132      -0.727
+50399.00     -0.087      0.222       0.147      -0.549
+50400.00     -0.035      0.077       0.179      -0.179
+50401.00      0.018      0.012       0.224      -0.050
+50402.00      0.073      0.049       0.352      -0.053
+50403.00      0.121      0.116       0.441      -0.184
+50404.00      0.142      0.174       0.487      -0.079
+50405.00      0.131      0.212       0.440      -0.077
+50406.00      0.090      0.206       0.294       0.065
+50407.00      0.031      0.133       0.137       0.187
+50408.00     -0.023      0.009       0.033       0.316
+50409.00     -0.110     -0.121       0.030       0.417
+50410.00     -0.284     -0.417       0.070       0.591
+50411.00     -0.505     -0.872       0.126       0.549
+50412.00     -0.658     -1.204       0.195       0.503
+50413.00     -0.627     -1.128       0.292       0.358
+50414.00     -0.356     -0.503       0.419       0.121
+50415.00     -0.090      0.076       0.589       0.001
+50416.00      0.065      0.360       0.679      -0.089
+50417.00      0.139      0.431       0.661      -0.039
+50418.00      0.151      0.343       0.669      -0.057
+50419.00      0.125      0.151       0.615      -0.185
+50420.00      0.089     -0.064       0.553      -0.274
+50421.00      0.061     -0.199       0.456      -0.350
+50422.00     -0.020     -0.101       0.302      -0.408
+50423.00     -0.135      0.134       0.238      -0.425
+50424.00     -0.196      0.301       0.154      -0.558
+50425.00     -0.173      0.341       0.110      -0.727
+50426.00     -0.099      0.292       0.085      -0.833
+50427.00     -0.020      0.197       0.079      -0.554
+50428.00      0.033      0.094       0.133      -0.117
+50429.00      0.061      0.029       0.255       0.077
+50430.00      0.071     -0.003       0.356       0.045
+50431.00      0.062     -0.010       0.466      -0.206
+50432.00      0.041      0.014       0.498      -0.317
+50433.00      0.019      0.066       0.476      -0.344
+50434.00     -0.004      0.120       0.353      -0.220
+50435.00     -0.029      0.156       0.182      -0.037
+50436.00     -0.055      0.163       0.028       0.167
+50437.00     -0.069      0.142      -0.092       0.279
+50438.00     -0.074      0.090      -0.164       0.463
+50439.00     -0.077      0.017      -0.202       0.371
+50440.00     -0.080     -0.049      -0.188       0.293
+50441.00     -0.074     -0.089      -0.156       0.078
+50442.00     -0.064     -0.114      -0.050      -0.010
+50443.00     -0.060     -0.142       0.126      -0.135
+50444.00     -0.059     -0.162       0.285      -0.095
+50445.00     -0.054     -0.151       0.333       0.087
+50446.00     -0.044     -0.114       0.350       0.074
+50447.00     -0.032     -0.075       0.313       0.016
+50448.00     -0.008     -0.042       0.234       0.002
+50449.00      0.031     -0.007       0.023      -0.023
+50450.00      0.069      0.022      -0.058       0.099
+50451.00      0.106      0.032      -0.073       0.147
+50452.00      0.154      0.028       0.002       0.012
+50453.00      0.217      0.026       0.086      -0.166
+50454.00      0.277      0.027       0.110      -0.364
+50455.00      0.311      0.022       0.162      -0.359
+50456.00      0.310      0.015       0.312      -0.015
+50457.00      0.269     -0.009       0.441       0.213
+50458.00      0.211     -0.041       0.519       0.354
+50459.00      0.149     -0.065       0.538       0.141
+50460.00      0.082     -0.062       0.420      -0.158
+50461.00      0.025     -0.038       0.298      -0.072
+50462.00     -0.007     -0.015       0.125      -0.144
+50463.00     -0.010      0.003       0.041      -0.054
+50464.00      0.002      0.030      -0.054       0.102
+50465.00      0.021      0.054      -0.200       0.221
+50466.00      0.052      0.052      -0.255       0.412
+50467.00      0.092      0.023      -0.263       0.275
+50468.00      0.124     -0.013      -0.147       0.118
+50469.00      0.139     -0.042      -0.042       0.045
+50470.00      0.133     -0.072       0.177      -0.067
+50471.00      0.097     -0.115       0.356      -0.035
+50472.00      0.061     -0.158       0.526      -0.104
+50473.00      0.045     -0.182       0.641      -0.065
+50474.00      0.040     -0.182       0.640      -0.075
+50475.00      0.033     -0.170       0.572      -0.045
+50476.00      0.025     -0.158       0.424      -0.044
+50477.00      0.016     -0.150       0.236      -0.095
+50478.00      0.000     -0.157       0.025      -0.135
+50479.00     -0.014     -0.177      -0.081      -0.013
+50480.00     -0.010     -0.185      -0.107      -0.010
+50481.00      0.008     -0.170      -0.149      -0.079
+50482.00      0.018     -0.155      -0.228      -0.318
+50483.00      0.021     -0.156      -0.247      -0.396
+50484.00      0.030     -0.132      -0.065      -0.209
+50485.00      0.043     -0.000       0.200       0.134
+50486.00      0.037      0.144       0.351       0.438
+50487.00      0.004      0.161       0.347       0.309
+50488.00     -0.036      0.085       0.195       0.240
+50489.00     -0.061     -0.020       0.063       0.192
+50490.00     -0.045     -0.118      -0.005       0.110
+50491.00      0.022     -0.157      -0.029       0.014
+50492.00      0.102     -0.087      -0.024      -0.025
+50493.00      0.143      0.025      -0.101      -0.010
+50494.00      0.131      0.072      -0.265       0.191
+50495.00      0.089      0.052      -0.354       0.310
+50496.00      0.032      0.003      -0.285       0.181
+50497.00     -0.027     -0.046      -0.131       0.086
+50498.00     -0.064     -0.063      -0.004       0.014
+50499.00     -0.062     -0.025       0.067       0.054
+50500.00     -0.035      0.031       0.135      -0.051
+50501.00     -0.001      0.045       0.189      -0.182
+50502.00      0.033      0.006       0.227      -0.149
+50503.00      0.054     -0.065       0.205      -0.115
+50504.00      0.058     -0.142       0.162      -0.153
+50505.00      0.057     -0.215       0.101      -0.147
+50506.00      0.057     -0.288       0.041      -0.181
+50507.00      0.065     -0.354       0.115      -0.178
+50508.00      0.090     -0.385       0.152      -0.245
+50509.00      0.117     -0.367       0.211      -0.303
+50510.00      0.113     -0.341       0.105      -0.327
+50511.00      0.076     -0.346       0.116      -0.493
+50512.00      0.039     -0.357       0.219      -0.400
+50513.00      0.021     -0.324       0.428      -0.135
+50514.00      0.001     -0.250       0.548       0.205
+50515.00     -0.035     -0.180       0.525       0.300
+50516.00     -0.079     -0.136       0.328       0.264
+50517.00     -0.114     -0.109       0.084       0.225
+50518.00     -0.135     -0.101       0.014       0.214
+50519.00     -0.143     -0.109       0.026       0.006
+50520.00     -0.138     -0.125       0.030      -0.244
+50521.00     -0.119     -0.142      -0.103      -0.461
+50522.00     -0.086     -0.160      -0.263      -0.399
+50523.00     -0.045     -0.174      -0.338      -0.293
+50524.00     -0.014     -0.173      -0.223      -0.188
+50525.00     -0.006     -0.155      -0.097      -0.105
+50526.00     -0.022     -0.124       0.004       0.010
+50527.00     -0.053     -0.136       0.013       0.181
+50528.00     -0.083     -0.132       0.033       0.094
+50529.00     -0.099     -0.102       0.046       0.073
+50530.00     -0.099     -0.087       0.112       0.003
+50531.00     -0.095     -0.089       0.117       0.042
+50532.00     -0.101     -0.090       0.058       0.059
+50533.00     -0.116     -0.094       0.027      -0.024
+50534.00     -0.125     -0.097       0.014      -0.134
+50535.00     -0.126     -0.098       0.042      -0.143
+50536.00     -0.116     -0.082       0.016      -0.232
+50537.00     -0.097     -0.042       0.022      -0.244
+50538.00     -0.087     -0.007      -0.014      -0.228
+50539.00     -0.100     -0.022      -0.073      -0.160
+50540.00     -0.118     -0.094      -0.115      -0.075
+50541.00     -0.107     -0.191      -0.124       0.031
+50542.00     -0.069     -0.247      -0.077       0.182
+50543.00     -0.029     -0.246      -0.063       0.070
+50544.00     -0.009     -0.211      -0.142      -0.075
+50545.00     -0.012     -0.164      -0.169       0.055
+50546.00     -0.043     -0.135      -0.229       0.066
+50547.00     -0.103     -0.137      -0.214       0.063
+50548.00     -0.166     -0.177      -0.239      -0.202
+50549.00     -0.208     -0.241      -0.368      -0.471
+50550.00     -0.222     -0.306      -0.480      -0.581
+50551.00     -0.215     -0.347      -0.533      -0.436
+50552.00     -0.200     -0.351      -0.440      -0.272
+50553.00     -0.176     -0.325      -0.294      -0.109
+50554.00     -0.137     -0.273      -0.250      -0.079
+50555.00     -0.091     -0.167      -0.298       0.053
+50556.00     -0.072     -0.051      -0.366       0.029
+50557.00     -0.080      0.023      -0.263      -0.014
+50558.00     -0.093      0.033      -0.173      -0.040
+50559.00     -0.101     -0.010      -0.115      -0.017
+50560.00     -0.106     -0.065       0.015      -0.024
+50561.00     -0.103     -0.115       0.069       0.041
+50562.00     -0.093     -0.149       0.115      -0.032
+50563.00     -0.084     -0.165       0.137       0.021
+50564.00     -0.087     -0.153       0.150       0.047
+50565.00     -0.101     -0.116       0.185       0.007
+50566.00     -0.119     -0.071       0.187      -0.091
+50567.00     -0.148     -0.038       0.133      -0.110
+50568.00     -0.195     -0.024      -0.013      -0.038
+50569.00     -0.245     -0.036      -0.216      -0.026
+50570.00     -0.250     -0.081      -0.422      -0.126
+50571.00     -0.207     -0.128      -0.519      -0.281
+50572.00     -0.153     -0.144      -0.423      -0.428
+50573.00     -0.116     -0.134      -0.305      -0.343
+50574.00     -0.112     -0.132      -0.114      -0.195
+50575.00     -0.141     -0.156      -0.029      -0.197
+50576.00     -0.174     -0.195       0.001      -0.288
+50577.00     -0.197     -0.231      -0.085      -0.604
+50578.00     -0.210     -0.250      -0.217      -0.860
+50579.00     -0.223     -0.246      -0.237      -0.845
+50580.00     -0.240     -0.218      -0.210      -0.612
+50581.00     -0.249     -0.185      -0.167      -0.404
+50582.00     -0.240     -0.164      -0.186      -0.218
+50583.00     -0.219     -0.154      -0.271      -0.049
+50584.00     -0.203     -0.131      -0.313       0.067
+50585.00     -0.194     -0.097      -0.367       0.172
+50586.00     -0.181     -0.076      -0.270       0.257
+50587.00     -0.166     -0.076      -0.226       0.101
+50588.00     -0.155     -0.077      -0.194       0.037
+50589.00     -0.150     -0.069      -0.158      -0.041
+50590.00     -0.145     -0.065      -0.209      -0.161
+50591.00     -0.140     -0.067      -0.261      -0.061
+50592.00     -0.145     -0.067      -0.354       0.116
+50593.00     -0.164     -0.070      -0.355       0.139
+50594.00     -0.189     -0.100      -0.250       0.002
+50595.00     -0.209     -0.168      -0.171      -0.080
+50596.00     -0.221     -0.257      -0.127       0.003
+50597.00     -0.225     -0.364      -0.241       0.045
+50598.00     -0.221     -0.488      -0.342       0.158
+50599.00     -0.208     -0.598      -0.471       0.008
+50600.00     -0.193     -0.639      -0.493      -0.021
+50601.00     -0.184     -0.580      -0.333      -0.015
+50602.00     -0.181     -0.431      -0.133       0.140
+50603.00     -0.172     -0.227      -0.020       0.127
+50604.00     -0.140     -0.073       0.032       0.212
+50605.00     -0.090      0.020      -0.010       0.071
+50606.00     -0.042      0.076      -0.010      -0.098
+50607.00     -0.012      0.100      -0.026      -0.185
+50608.00      0.003      0.108       0.063      -0.068
+50609.00      0.012      0.109       0.104       0.078
+50610.00      0.013      0.109       0.102       0.274
+50611.00     -0.000      0.117       0.028       0.300
+50612.00     -0.025      0.146      -0.157       0.489
+50613.00     -0.050      0.189      -0.248       0.566
+50614.00     -0.070      0.213      -0.303       0.702
+50615.00     -0.089      0.184      -0.227       0.644
+50616.00     -0.101      0.101      -0.083       0.394
+50617.00     -0.098     -0.016       0.031       0.160
+50618.00     -0.078     -0.097       0.073       0.096
+50619.00     -0.051     -0.126       0.081       0.147
+50620.00     -0.022     -0.111       0.090       0.366
+50621.00      0.001     -0.067       0.109       0.526
+50622.00      0.016     -0.026       0.152       0.332
+50623.00      0.021     -0.008       0.208       0.122
+50624.00      0.022     -0.002       0.196       0.126
+50625.00      0.020      0.001       0.124       0.241
+50626.00      0.011     -0.012      -0.090       0.377
+50627.00     -0.007     -0.053      -0.327       0.491
+50628.00     -0.030     -0.107      -0.516       0.335
+50629.00     -0.056     -0.154      -0.449       0.259
+50630.00     -0.087     -0.193      -0.229       0.222
+50631.00     -0.121     -0.232      -0.020       0.024
+50632.00     -0.142     -0.259       0.112       0.022
+50633.00     -0.146     -0.258       0.125      -0.021
+50634.00     -0.148     -0.229       0.093      -0.115
+50635.00     -0.166     -0.190       0.044       0.253
+50636.00     -0.196     -0.147       0.003       0.211
+50637.00     -0.229     -0.107      -0.061       0.190
+50638.00     -0.275     -0.092      -0.159       0.114
+50639.00     -0.337     -0.116      -0.258      -0.021
+50640.00     -0.348     -0.113      -0.458      -0.143
+50641.00     -0.286     -0.055      -0.663      -0.179
+50642.00     -0.193      0.012      -0.716      -0.260
+50643.00     -0.117      0.019      -0.610      -0.327
+50644.00     -0.085     -0.072      -0.334      -0.414
+50645.00     -0.103     -0.236      -0.066      -0.578
+50646.00     -0.128     -0.356       0.020      -0.671
+50647.00     -0.155     -0.402       0.035      -0.550
+50648.00     -0.184     -0.392       0.022      -0.249
+50649.00     -0.203     -0.337       0.068       0.099
+50650.00     -0.196     -0.257       0.073       0.305
+50651.00     -0.157     -0.163       0.070       0.136
+50652.00     -0.085     -0.049       0.070       0.037
+50653.00      0.015      0.085      -0.024       0.050
+50654.00      0.072      0.163      -0.067       0.103
+50655.00      0.059      0.162      -0.233       0.167
+50656.00     -0.005      0.104      -0.395      -0.006
+50657.00     -0.093      0.012      -0.474      -0.095
+50658.00     -0.183     -0.095      -0.391      -0.197
+50659.00     -0.264     -0.194      -0.189      -0.221
+50660.00     -0.316     -0.258       0.044      -0.257
+50661.00     -0.328     -0.280       0.234      -0.191
+50662.00     -0.303     -0.274       0.301       0.044
+50663.00     -0.264     -0.246       0.259       0.079
+50664.00     -0.216     -0.198       0.165      -0.006
+50665.00     -0.159     -0.146       0.059       0.088
+50666.00     -0.105     -0.127       0.026      -0.000
+50667.00     -0.088     -0.146      -0.073      -0.022
+50668.00     -0.104     -0.177      -0.245       0.043
+50669.00     -0.128     -0.182      -0.414       0.088
+50670.00     -0.156     -0.157      -0.540       0.074
+50671.00     -0.193     -0.142      -0.451       0.204
+50672.00     -0.224     -0.157      -0.191       0.155
+50673.00     -0.232     -0.193       0.039       0.091
+50674.00     -0.225     -0.248       0.107      -0.024
+50675.00     -0.226     -0.317      -0.032      -0.025
+50676.00     -0.247     -0.381      -0.097       0.079
+50677.00     -0.286     -0.419      -0.075       0.249
+50678.00     -0.331     -0.424      -0.101       0.265
+50679.00     -0.372     -0.377      -0.186       0.010
+50680.00     -0.415     -0.244      -0.274      -0.242
+50681.00     -0.471     -0.019      -0.317      -0.293
+50682.00     -0.508      0.165      -0.291      -0.330
+50683.00     -0.510      0.257      -0.170      -0.412
+50684.00     -0.485      0.286      -0.066      -0.333
+50685.00     -0.436      0.239      -0.072      -0.283
+50686.00     -0.356      0.084      -0.017      -0.023
+50687.00     -0.253     -0.160       0.119       0.090
+50688.00     -0.162     -0.363       0.440       0.186
+50689.00     -0.092     -0.476       0.689       0.109
+50690.00     -0.038     -0.523       0.862       0.092
+50691.00     -0.009     -0.515       0.851       0.179
+50692.00     -0.014     -0.453       0.766      -0.076
+50693.00     -0.048     -0.358       0.625      -0.284
+50694.00     -0.106     -0.264       0.392      -0.363
+50695.00     -0.194     -0.207       0.212      -0.315
+50696.00     -0.269     -0.177      -0.104      -0.028
+50697.00     -0.303     -0.140      -0.415       0.041
+50698.00     -0.300     -0.086      -0.681       0.228
+50699.00     -0.277     -0.037      -0.705       0.275
+50700.00     -0.243     -0.016      -0.525       0.105
+50701.00     -0.205     -0.023      -0.241       0.069
+50702.00     -0.177     -0.055      -0.066      -0.058
+50703.00     -0.166     -0.100       0.036      -0.181
+50704.00     -0.168     -0.130       0.124      -0.083
+50705.00     -0.179     -0.128       0.262      -0.010
+50706.00     -0.197     -0.103       0.355       0.180
+50707.00     -0.210     -0.061       0.328       0.154
+50708.00     -0.208      0.010       0.167       0.048
+50709.00     -0.203      0.080      -0.030       0.108
+50710.00     -0.200      0.125      -0.092      -0.012
+50711.00     -0.195      0.160      -0.001      -0.285
+50712.00     -0.189      0.215       0.114      -0.510
+50713.00     -0.186      0.275       0.130      -0.624
+50714.00     -0.169      0.283       0.055      -0.237
+50715.00     -0.128      0.219       0.067       0.184
+50716.00     -0.075      0.108       0.212       0.312
+50717.00     -0.022     -0.018       0.430       0.432
+50718.00      0.031     -0.153       0.559       0.436
+50719.00      0.082     -0.289       0.539       0.364
+50720.00      0.118     -0.401       0.437       0.200
+50721.00      0.134     -0.470       0.307      -0.016
+50722.00      0.136     -0.478       0.223      -0.254
+50723.00      0.119     -0.408       0.029      -0.214
+50724.00      0.076     -0.272      -0.147      -0.132
+50725.00      0.019     -0.100      -0.275      -0.032
+50726.00     -0.031      0.073      -0.380      -0.083
+50727.00     -0.064      0.208      -0.372       0.038
+50728.00     -0.072      0.274      -0.254       0.072
+50729.00     -0.050      0.256      -0.022       0.199
+50730.00     -0.025      0.216       0.124       0.275
+50731.00     -0.015      0.188       0.229       0.408
+50732.00     -0.014      0.185       0.226       0.641
+50733.00     -0.017      0.210       0.255       0.652
+50734.00     -0.032      0.233       0.162       0.575
+50735.00     -0.065      0.233       0.027       0.451
+50736.00     -0.109      0.209      -0.145       0.241
+50737.00     -0.149      0.174      -0.423       0.298
+50738.00     -0.178      0.127      -0.538       0.134
+50739.00     -0.191      0.075      -0.554      -0.136
+50740.00     -0.190      0.047      -0.408      -0.401
+50741.00     -0.173      0.051      -0.282      -0.652
+50742.00     -0.128      0.063      -0.228      -0.356
+50743.00     -0.052      0.070      -0.134       0.198
+50744.00      0.022      0.054       0.046       0.613
+50745.00      0.071      0.025       0.295       0.762
+50746.00      0.102     -0.016       0.442       0.713
+50747.00      0.119     -0.061       0.461       0.686
+50748.00      0.110     -0.090       0.329       0.534
+50749.00      0.067     -0.083       0.243       0.364
+50750.00     -0.006     -0.024       0.065       0.104
+50751.00     -0.106      0.081      -0.149      -0.092
+50752.00     -0.208      0.127      -0.310      -0.288
+50753.00     -0.269      0.140      -0.375      -0.293
+50754.00     -0.275      0.143      -0.454      -0.440
+50755.00     -0.241      0.134      -0.415      -0.363
+50756.00     -0.190      0.123      -0.345      -0.196
+50757.00     -0.136      0.103      -0.193      -0.068
+50758.00     -0.086      0.057       0.002       0.112
+50759.00     -0.049     -0.009       0.215       0.254
+50760.00     -0.028     -0.064       0.282       0.354
+50761.00     -0.019     -0.092       0.263       0.370
+50762.00     -0.014     -0.100       0.179       0.278
+50763.00     -0.012     -0.083       0.153      -0.024
+50764.00     -0.013     -0.008       0.090      -0.045
+50765.00     -0.011      0.138      -0.017       0.095
+50766.00     -0.012      0.223      -0.063       0.226
+50767.00     -0.015      0.197      -0.109       0.189
+50768.00     -0.010      0.121      -0.072      -0.072
+50769.00      0.015      0.049      -0.031      -0.247
+50770.00      0.074      0.025       0.065      -0.204
+50771.00      0.175      0.054       0.134       0.054
+50772.00      0.307      0.028       0.234       0.418
+50773.00      0.409     -0.044       0.369       0.697
+50774.00      0.422     -0.098       0.466       0.673
+50775.00      0.364     -0.124       0.400       0.562
+50776.00      0.265     -0.118       0.320       0.369
+50777.00      0.158     -0.087       0.197       0.200
+50778.00      0.076     -0.053       0.048       0.002
+50779.00      0.030     -0.039      -0.101      -0.438
+50780.00      0.015     -0.037      -0.230      -0.650
+50781.00      0.031     -0.039      -0.291      -0.725
+50782.00      0.072     -0.045      -0.247      -0.715
+50783.00      0.115     -0.036      -0.194      -0.398
+50784.00      0.126      0.021      -0.121      -0.124
+50785.00      0.086      0.127       0.008       0.095
+50786.00     -0.000      0.264       0.211       0.175
+50787.00     -0.107      0.403       0.418       0.214
+50788.00     -0.214      0.533       0.552       0.380
+50789.00     -0.304      0.627       0.498       0.451
+50790.00     -0.345      0.626       0.424       0.377
+50791.00     -0.299      0.483       0.291       0.296
+50792.00     -0.158      0.213       0.289       0.243
+50793.00     -0.026      0.000       0.252       0.298
+50794.00      0.057     -0.112       0.258       0.566
+50795.00      0.106     -0.161       0.223       0.529
+50796.00      0.137     -0.155       0.161       0.361
+50797.00      0.162     -0.100       0.155       0.155
+50798.00      0.185     -0.029       0.154       0.018
+50799.00      0.206      0.014       0.196       0.068
+50800.00      0.224     -0.023       0.232       0.233
+50801.00      0.241     -0.122       0.227       0.549
+50802.00      0.254     -0.244       0.206       0.533
+50803.00      0.254     -0.346       0.186       0.538
+50804.00      0.224     -0.369       0.164       0.459
+50805.00      0.151     -0.256       0.081       0.422
+50806.00      0.041     -0.014       0.002       0.253
+50807.00     -0.052      0.169      -0.035      -0.117
+50808.00     -0.100      0.248      -0.104      -0.451
+50809.00     -0.099      0.260      -0.097      -0.596
+50810.00     -0.055      0.229      -0.079      -0.615
+50811.00      0.007      0.174      -0.034      -0.236
+50812.00      0.065      0.116      -0.038       0.039
+50813.00      0.106      0.065      -0.016       0.194
+50814.00      0.132      0.008       0.157       0.091
+50815.00      0.155     -0.061       0.224      -0.107
+50816.00      0.177     -0.118       0.325      -0.300
+50817.00      0.192     -0.138       0.381      -0.228
+50818.00      0.194     -0.140       0.340      -0.172
+50819.00      0.184     -0.150       0.315      -0.114
+50820.00      0.173     -0.166       0.308      -0.240
+50821.00      0.161     -0.180       0.273      -0.105
+50822.00      0.142     -0.197       0.261       0.069
+50823.00      0.110     -0.219       0.200       0.081
+50824.00      0.075     -0.223       0.126       0.154
+50825.00      0.049     -0.182       0.030       0.026
+50826.00      0.033     -0.097       0.015       0.018
+50827.00      0.018      0.012       0.109       0.128
+50828.00      0.013      0.085       0.146       0.346
+50829.00      0.022      0.125       0.119       0.588
+50830.00      0.040      0.144       0.076       0.683
+50831.00      0.052      0.129       0.092       0.527
+50832.00      0.054      0.088       0.106       0.367
+50833.00      0.046      0.039       0.119       0.227
+50834.00      0.022     -0.006       0.092       0.070
+50835.00     -0.018     -0.043       0.120      -0.182
+50836.00     -0.059     -0.063       0.139      -0.463
+50837.00     -0.086     -0.053       0.144      -0.786
+50838.00     -0.099     -0.024       0.143      -0.872
+50839.00     -0.098     -0.003       0.071      -0.707
+50840.00     -0.068     -0.005       0.064      -0.288
+50841.00     -0.002     -0.037      -0.015       0.015
+50842.00      0.059     -0.082      -0.128       0.214
+50843.00      0.067     -0.105      -0.307       0.004
+50844.00      0.008     -0.056      -0.423      -0.044
+50845.00     -0.080      0.048      -0.359      -0.066
+50846.00     -0.155      0.154      -0.273       0.184
+50847.00     -0.184      0.195      -0.187       0.269
+50848.00     -0.146      0.150      -0.087       0.235
+50849.00     -0.070      0.111      -0.042       0.191
+50850.00      0.002      0.086       0.029       0.226
+50851.00      0.051      0.054       0.063       0.102
+50852.00      0.076      0.023       0.040       0.012
+50853.00      0.084      0.004      -0.005      -0.096
+50854.00      0.085     -0.015      -0.022      -0.171
+50855.00      0.079     -0.061      -0.002      -0.007
+50856.00      0.071     -0.132      -0.022      -0.029
+50857.00      0.060     -0.181      -0.057       0.052
+50858.00      0.033     -0.171      -0.072       0.249
+50859.00     -0.016     -0.133      -0.078       0.261
+50860.00     -0.073     -0.094      -0.058       0.372
+50861.00     -0.116     -0.062      -0.046       0.173
+50862.00     -0.136     -0.030      -0.069       0.170
+50863.00     -0.130      0.002      -0.071       0.034
+50864.00     -0.098      0.046      -0.063      -0.315
+50865.00     -0.054      0.108      -0.088      -0.715
+50866.00     -0.022      0.151      -0.229      -1.053
+50867.00     -0.011      0.132      -0.257      -0.992
+50868.00     -0.002      0.054      -0.064      -0.622
+50869.00      0.011     -0.050       0.056      -0.144
+50870.00      0.018     -0.112       0.016       0.211
+50871.00      0.009     -0.134      -0.244       0.249
+50872.00     -0.012     -0.131      -0.386       0.004
+50873.00     -0.029     -0.101      -0.430       0.020
+50874.00     -0.031     -0.054      -0.280       0.008
+50875.00     -0.024     -0.003      -0.108       0.053
+50876.00     -0.024      0.056       0.006       0.023
+50877.00     -0.035      0.112      -0.037       0.039
+50878.00     -0.045      0.140      -0.044       0.057
+50879.00     -0.042      0.129       0.024      -0.011
+50880.00     -0.034      0.097       0.085      -0.075
+50881.00     -0.033      0.064       0.127      -0.060
+50882.00     -0.040      0.038       0.131      -0.014
+50883.00     -0.056      0.013       0.048       0.120
+50884.00     -0.075     -0.010      -0.054       0.181
+50885.00     -0.076     -0.008      -0.163       0.199
+50886.00     -0.061      0.021      -0.197       0.176
+50887.00     -0.045      0.056      -0.196       0.259
+50888.00     -0.029      0.083      -0.181       0.294
+50889.00     -0.001      0.096      -0.089       0.211
+50890.00      0.036      0.092       0.053       0.118
+50891.00      0.070      0.075       0.218       0.151
+50892.00      0.098      0.064       0.380       0.057
+50893.00      0.113      0.078       0.356      -0.086
+50894.00      0.097      0.080       0.199      -0.298
+50895.00      0.060      0.027       0.019      -0.478
+50896.00      0.045     -0.052       0.002      -0.286
+50897.00      0.072     -0.082       0.050       0.044
+50898.00      0.105     -0.043       0.025       0.401
+50899.00      0.095      0.005      -0.204       0.476
+50900.00      0.050      0.022      -0.429       0.301
+50901.00      0.002      0.015      -0.445       0.222
+50902.00     -0.035     -0.012      -0.258       0.077
+50903.00     -0.047     -0.069      -0.014      -0.039
+50904.00     -0.020     -0.150       0.130      -0.129
+50905.00      0.053     -0.252       0.095      -0.212
+50906.00      0.125     -0.344      -0.000      -0.119
+50907.00      0.140     -0.388       0.011      -0.006
+50908.00      0.106     -0.382       0.156       0.040
+50909.00      0.040     -0.332       0.224      -0.016
+50910.00     -0.030     -0.247       0.137      -0.080
+50911.00     -0.077     -0.137      -0.042       0.040
+50912.00     -0.074     -0.022      -0.201      -0.015
+50913.00     -0.020      0.060      -0.274      -0.123
+50914.00      0.065      0.087      -0.317      -0.156
+50915.00      0.140      0.061      -0.256      -0.127
+50916.00      0.167      0.007      -0.198       0.015
+50917.00      0.124     -0.061      -0.104      -0.042
+50918.00      0.007     -0.132       0.039      -0.028
+50919.00     -0.123     -0.164       0.279      -0.088
+50920.00     -0.234     -0.140       0.459      -0.011
+50921.00     -0.318     -0.066       0.530       0.004
+50922.00     -0.378      0.014       0.378      -0.047
+50923.00     -0.407      0.033       0.184      -0.212
+50924.00     -0.368     -0.021       0.022      -0.217
+50925.00     -0.241     -0.092      -0.076      -0.092
+50926.00     -0.112     -0.106      -0.180       0.141
+50927.00     -0.035     -0.069      -0.324       0.066
+50928.00     -0.002     -0.018      -0.414       0.075
+50929.00      0.010      0.032      -0.409       0.125
+50930.00      0.007      0.079      -0.209       0.186
+50931.00     -0.009      0.105      -0.047       0.193
+50932.00     -0.017      0.101       0.040       0.022
+50933.00      0.005      0.065      -0.083       0.017
+50934.00      0.056      0.005      -0.168       0.046
+50935.00      0.112     -0.063      -0.146       0.182
+50936.00      0.146     -0.120      -0.010       0.167
+50937.00      0.142     -0.156       0.158       0.190
+50938.00      0.100     -0.158       0.141       0.149
+50939.00      0.021     -0.113      -0.025       0.106
+50940.00     -0.065     -0.091      -0.231       0.042
+50941.00     -0.134     -0.090      -0.369      -0.053
+50942.00     -0.167     -0.111      -0.337      -0.210
+50943.00     -0.166     -0.152      -0.287      -0.245
+50944.00     -0.148     -0.185      -0.182      -0.161
+50945.00     -0.121     -0.188      -0.056      -0.234
+50946.00     -0.078     -0.167       0.060      -0.223
+50947.00     -0.037     -0.134       0.255      -0.118
+50948.00     -0.013     -0.092       0.419       0.005
+50949.00     -0.006     -0.042       0.613       0.119
+50950.00     -0.006      0.002       0.626       0.156
+50951.00     -0.014      0.013       0.464       0.180
+50952.00     -0.029     -0.019       0.216       0.111
+50953.00     -0.041     -0.079      -0.062       0.082
+50954.00     -0.045     -0.144      -0.266      -0.083
+50955.00     -0.048     -0.177      -0.350      -0.214
+50956.00     -0.056     -0.175      -0.343      -0.268
+50957.00     -0.063     -0.156      -0.217      -0.070
+50958.00     -0.064     -0.140      -0.041       0.043
+50959.00     -0.061     -0.138       0.072       0.127
+50960.00     -0.054     -0.147       0.075      -0.003
+50961.00     -0.036     -0.151      -0.010      -0.183
+50962.00     -0.006     -0.148      -0.069      -0.232
+50963.00      0.022     -0.133      -0.072      -0.161
+50964.00      0.033     -0.108       0.107      -0.125
+50965.00      0.028     -0.089       0.310      -0.024
+50966.00      0.024     -0.092       0.293      -0.119
+50967.00      0.020     -0.110       0.144      -0.127
+50968.00      0.004     -0.114      -0.107      -0.220
+50969.00     -0.021     -0.104      -0.196      -0.134
+50970.00     -0.032     -0.104      -0.244      -0.185
+50971.00     -0.019     -0.124      -0.158      -0.334
+50972.00      0.007     -0.143      -0.100      -0.316
+50973.00      0.042     -0.147       0.042      -0.200
+50974.00      0.098     -0.142       0.182      -0.112
+50975.00      0.170     -0.128       0.288       0.090
+50976.00      0.230     -0.105       0.444       0.153
+50977.00      0.259     -0.080       0.539       0.261
+50978.00      0.258     -0.072       0.639       0.334
+50979.00      0.232     -0.087       0.616       0.287
+50980.00      0.161     -0.120       0.402       0.194
+50981.00      0.040     -0.152       0.078       0.112
+50982.00     -0.065     -0.111      -0.270      -0.156
+50983.00     -0.111      0.026      -0.415      -0.493
+50984.00     -0.108      0.225      -0.356      -0.626
+50985.00     -0.079      0.414      -0.235      -0.387
+50986.00     -0.036      0.489      -0.055      -0.217
+50987.00      0.006      0.361       0.020      -0.070
+50988.00      0.023      0.033       0.062      -0.175
+50989.00     -0.009     -0.286       0.002      -0.426
+50990.00     -0.086     -0.507      -0.020      -0.618
+50991.00     -0.185     -0.638      -0.024      -0.662
+50992.00     -0.281     -0.675       0.068      -0.636
+50993.00     -0.341     -0.620       0.205      -0.559
+50994.00     -0.336     -0.485       0.252      -0.458
+50995.00     -0.260     -0.279       0.194      -0.476
+50996.00     -0.196     -0.066       0.094      -0.181
+50997.00     -0.150      0.082      -0.034      -0.017
+50998.00     -0.096      0.139      -0.037       0.042
+50999.00     -0.035      0.120      -0.007      -0.004
+51000.00      0.024      0.055      -0.097      -0.254
+51001.00      0.072     -0.017       0.012      -0.269
+51002.00      0.113     -0.071       0.170      -0.093
+51003.00      0.142     -0.088       0.221       0.089
+51004.00      0.147     -0.062       0.257       0.319
+51005.00      0.122     -0.015       0.311       0.196
+51006.00      0.077      0.018       0.405       0.164
+51007.00      0.028      0.015       0.450      -0.054
+51008.00     -0.016     -0.012       0.403      -0.081
+51009.00     -0.059     -0.050       0.187      -0.157
+51010.00     -0.102     -0.111      -0.066      -0.167
+51011.00     -0.112     -0.229      -0.293      -0.318
+51012.00     -0.083     -0.377      -0.344      -0.512
+51013.00     -0.036     -0.490      -0.286      -0.458
+51014.00      0.004     -0.536      -0.156      -0.294
+51015.00      0.015     -0.490      -0.028      -0.170
+51016.00     -0.018     -0.320      -0.062      -0.105
+51017.00     -0.057     -0.137      -0.079      -0.291
+51018.00     -0.089     -0.006      -0.100      -0.415
+51019.00     -0.118      0.066      -0.090      -0.481
+51020.00     -0.136      0.083      -0.058      -0.481
+51021.00     -0.138      0.057      -0.015      -0.458
+51022.00     -0.130     -0.014      -0.038      -0.326
+51023.00     -0.127     -0.138      -0.152      -0.169
+51024.00     -0.127     -0.284      -0.413      -0.091
+51025.00     -0.118     -0.364      -0.697      -0.056
+51026.00     -0.096     -0.361      -0.890      -0.042
+51027.00     -0.072     -0.322      -0.846      -0.269
+51028.00     -0.052     -0.271      -0.626      -0.346
+51029.00     -0.031     -0.211      -0.280      -0.316
+51030.00     -0.006     -0.152       0.048      -0.164
+51031.00      0.015     -0.123       0.176       0.175
+51032.00      0.024     -0.130       0.248       0.515
+51033.00      0.019     -0.163       0.271       0.560
+51034.00      0.004     -0.219       0.337       0.378
+51035.00     -0.023     -0.290       0.285       0.034
+51036.00     -0.062     -0.340       0.222      -0.166
+51037.00     -0.113     -0.326       0.074      -0.277
+51038.00     -0.186     -0.243      -0.158      -0.219
+51039.00     -0.272     -0.135      -0.393      -0.219
+51040.00     -0.349     -0.039      -0.536      -0.378
+51041.00     -0.391      0.031      -0.536      -0.318
+51042.00     -0.389      0.056      -0.413      -0.300
+51043.00     -0.346      0.009      -0.212      -0.165
+51044.00     -0.266     -0.112      -0.086      -0.182
+51045.00     -0.195     -0.212      -0.017      -0.186
+51046.00     -0.146     -0.264      -0.044      -0.372
+51047.00     -0.116     -0.287      -0.015      -0.521
+51048.00     -0.097     -0.286       0.012      -0.696
+51049.00     -0.079     -0.266       0.037      -0.835
+51050.00     -0.062     -0.250       0.002      -0.768
+51051.00     -0.057     -0.260       0.001      -0.476
+51052.00     -0.074     -0.245      -0.128      -0.124
+51053.00     -0.079     -0.201      -0.318       0.088
+51054.00     -0.061     -0.146      -0.502       0.194
+51055.00     -0.042     -0.111      -0.480       0.195
+51056.00     -0.028     -0.115      -0.261       0.141
+51057.00     -0.009     -0.130      -0.028      -0.489
+51058.00      0.012     -0.124       0.080      -0.202
+51059.00      0.018     -0.104       0.104       0.344
+51060.00      0.000     -0.087       0.103       0.763
+51061.00     -0.028     -0.085       0.138       0.854
+51062.00     -0.056     -0.102       0.126       0.789
+51063.00     -0.079     -0.141       0.028       0.277
+51064.00     -0.102     -0.195      -0.066      -0.034
+51065.00     -0.124     -0.253      -0.085      -0.325
+51066.00     -0.141     -0.315      -0.111      -0.309
+51067.00     -0.151     -0.374      -0.135      -0.432
+51068.00     -0.155     -0.410      -0.161      -0.593
+51069.00     -0.149     -0.415      -0.246      -0.612
+51070.00     -0.126     -0.391      -0.213      -0.479
+51071.00     -0.098     -0.326      -0.115      -0.362
+51072.00     -0.083     -0.190      -0.032      -0.208
+51073.00     -0.080      0.026      -0.043      -0.014
+51074.00     -0.083      0.215      -0.092       0.118
+51075.00     -0.092      0.284      -0.124       0.250
+51076.00     -0.107      0.268      -0.141       0.178
+51077.00     -0.120      0.208      -0.139       0.001
+51078.00     -0.127      0.119      -0.061      -0.030
+51079.00     -0.129      0.007      -0.082       0.042
+51080.00     -0.121     -0.092      -0.188       0.122
+51081.00     -0.092     -0.134      -0.382       0.170
+51082.00     -0.053     -0.108      -0.522       0.130
+51083.00     -0.029     -0.053      -0.517       0.041
+51084.00     -0.023     -0.016      -0.373       0.032
+51085.00     -0.031      0.009      -0.123       0.082
+51086.00     -0.054      0.039      -0.013       0.091
+51087.00     -0.095      0.076       0.045       0.134
+51088.00     -0.135      0.106       0.179       0.462
+51089.00     -0.146      0.115       0.290       0.580
+51090.00     -0.131      0.090       0.381       0.631
+51091.00     -0.099      0.022       0.304       0.340
+51092.00     -0.069     -0.079       0.201       0.129
+51093.00     -0.066     -0.194       0.135      -0.077
+51094.00     -0.097     -0.271       0.072      -0.211
+51095.00     -0.143     -0.305       0.082      -0.432
+51096.00     -0.186     -0.289       0.118      -0.670
+51097.00     -0.217     -0.229       0.036      -0.755
+51098.00     -0.222     -0.182      -0.085      -0.599
+51099.00     -0.181     -0.195      -0.060      -0.495
+51100.00     -0.102     -0.255      -0.010      -0.166
+51101.00     -0.039     -0.301       0.119      -0.164
+51102.00     -0.001     -0.324       0.104       0.002
+51103.00      0.023     -0.338       0.092       0.228
+51104.00      0.033     -0.333       0.030       0.168
+51105.00      0.032     -0.299       0.027       0.065
+51106.00      0.032     -0.251       0.056      -0.137
+51107.00      0.039     -0.210       0.028      -0.043
+51108.00      0.052     -0.182      -0.057      -0.019
+51109.00      0.063     -0.150      -0.193      -0.007
+51110.00      0.067     -0.103      -0.354      -0.063
+51111.00      0.056     -0.048      -0.376      -0.031
+51112.00      0.035     -0.002      -0.249       0.024
+51113.00      0.019      0.045      -0.114       0.138
+51114.00      0.005      0.106      -0.005       0.129
+51115.00     -0.015      0.179       0.051       0.131
+51116.00     -0.050      0.238       0.156       0.353
+51117.00     -0.098      0.266       0.272       0.516
+51118.00     -0.152      0.244       0.311       0.525
+51119.00     -0.192      0.163       0.284       0.377
+51120.00     -0.193      0.048       0.109       0.148
+51121.00     -0.153     -0.055       0.059       0.142
+51122.00     -0.118     -0.094       0.012      -0.014
+51123.00     -0.105     -0.030       0.034      -0.349
+51124.00     -0.093      0.040       0.081      -0.700
+51125.00     -0.079      0.069       0.045      -0.794
+51126.00     -0.077      0.065      -0.034      -0.547
+51127.00     -0.097      0.019      -0.097      -0.184
+51128.00     -0.145     -0.040      -0.066      -0.044
+51129.00     -0.224     -0.074      -0.001       0.080
+51130.00     -0.302     -0.084      -0.003       0.054
+51131.00     -0.336     -0.081      -0.075       0.172
+51132.00     -0.326     -0.065      -0.128       0.196
+51133.00     -0.297     -0.036      -0.111       0.184
+51134.00     -0.254      0.001      -0.056       0.095
+51135.00     -0.201      0.038      -0.015      -0.082
+51136.00     -0.155      0.071      -0.042      -0.041
+51137.00     -0.119      0.091      -0.011      -0.066
+51138.00     -0.089      0.094      -0.027      -0.212
+51139.00     -0.071      0.083      -0.067      -0.162
+51140.00     -0.064      0.069      -0.024      -0.191
+51141.00     -0.059      0.064       0.014       0.054
+51142.00     -0.050      0.069       0.035       0.098
+51143.00     -0.042      0.074      -0.038       0.228
+51144.00     -0.036      0.075      -0.149       0.309
+51145.00     -0.028      0.076      -0.295       0.190
+51146.00     -0.021      0.066      -0.376       0.062
+51147.00     -0.023      0.032      -0.375      -0.142
+51148.00     -0.029     -0.007      -0.274      -0.182
+51149.00     -0.033     -0.006      -0.092       0.018
+51150.00     -0.045      0.033       0.155       0.076
+51151.00     -0.072      0.069       0.387       0.012
+51152.00     -0.102      0.079       0.554      -0.195
+51153.00     -0.114      0.079       0.606      -0.467
+51154.00     -0.103      0.075       0.485      -0.390
+51155.00     -0.069      0.060       0.312      -0.173
+51156.00     -0.027      0.047       0.120       0.018
+51157.00     -0.013      0.089      -0.005       0.083
+51158.00     -0.022      0.178      -0.193       0.153
+51159.00     -0.038      0.271      -0.424       0.188
+51160.00     -0.062      0.336      -0.358       0.209
+51161.00     -0.097      0.344      -0.316       0.238
+51162.00     -0.128      0.261      -0.219       0.230
+51163.00     -0.136      0.090      -0.115       0.030
+51164.00     -0.134     -0.062       0.002      -0.090
+51165.00     -0.121     -0.139       0.114      -0.213
+51166.00     -0.094     -0.160       0.167      -0.178
+51167.00     -0.062     -0.142       0.175      -0.143
+51168.00     -0.044     -0.089       0.121      -0.055
+51169.00     -0.041     -0.010       0.032       0.163
+51170.00     -0.040      0.065      -0.030       0.216
+51171.00     -0.041      0.105      -0.115       0.227
+51172.00     -0.053      0.111      -0.244       0.315
+51173.00     -0.071      0.090      -0.456       0.175
+51174.00     -0.080      0.040      -0.567       0.008
+51175.00     -0.071     -0.037      -0.629      -0.124
+51176.00     -0.045     -0.109      -0.472      -0.297
+51177.00     -0.003     -0.141      -0.191      -0.265
+51178.00      0.039     -0.137       0.126      -0.081
+51179.00      0.070     -0.126       0.220      -0.191
+51180.00      0.089     -0.120       0.321      -0.564
+51181.00      0.103     -0.117       0.333      -0.897
+51182.00      0.118     -0.113       0.302      -0.840
+51183.00      0.133     -0.108       0.214      -0.574
+51184.00      0.141     -0.092       0.164      -0.199
+51185.00      0.141     -0.060       0.200       0.211
+51186.00      0.141     -0.023       0.207       0.280
+51187.00      0.138      0.011       0.200       0.356
+51188.00      0.120      0.047       0.215       0.422
+51189.00      0.081      0.087       0.195       0.431
+51190.00      0.028      0.122       0.171       0.388
+51191.00     -0.034      0.154       0.120       0.083
+51192.00     -0.106      0.201       0.075      -0.091
+51193.00     -0.163      0.234       0.040      -0.129
+51194.00     -0.192      0.227       0.048      -0.024
+51195.00     -0.200      0.188       0.055       0.214
+51196.00     -0.204      0.143      -0.026       0.343
+51197.00     -0.207      0.111      -0.096       0.305
+51198.00     -0.195      0.096      -0.139       0.172
+51199.00     -0.164      0.098      -0.162       0.066
+51200.00     -0.128      0.126      -0.181       0.048
+51201.00     -0.103      0.170      -0.224       0.074
+51202.00     -0.082      0.199      -0.295       0.077
+51203.00     -0.051      0.186      -0.256       0.064
+51204.00     -0.017      0.144      -0.100       0.021
+51205.00     -0.004      0.078       0.116       0.098
+51206.00     -0.025     -0.020       0.305       0.216
+51207.00     -0.057     -0.053       0.382       0.218
+51208.00     -0.112     -0.010       0.405       0.047
+51209.00     -0.176      0.069       0.348      -0.181
+51210.00     -0.226      0.135      -0.101      -0.181
+51211.00     -0.243      0.123      -0.131      -0.097
+51212.00     -0.218      0.018      -0.129      -0.052
+51213.00     -0.176     -0.069      -0.192      -0.112
+51214.00     -0.124     -0.109      -0.374       0.176
+51215.00     -0.064     -0.127      -0.243       0.001
+51216.00     -0.011     -0.135       0.032      -0.534
+51217.00      0.022     -0.130       0.023      -0.422
+51218.00      0.024     -0.123       0.005      -0.134
+51219.00     -0.014     -0.150      -0.054      -0.013
+51220.00     -0.051     -0.345      -0.109      -0.007
+51221.00     -0.075     -0.580       0.004       0.103
+51222.00     -0.095     -0.653       0.108       0.084
+51223.00     -0.113     -0.588       0.229       0.365
+51224.00     -0.122     -0.444       0.295       0.470
+51225.00     -0.116     -0.265       0.202       0.262
+51226.00     -0.092     -0.119       0.047       0.070
+51227.00     -0.052     -0.067      -0.200      -0.126
+51228.00     -0.021     -0.071      -0.374      -0.155
+51229.00     -0.026     -0.058      -0.477      -0.008
+51230.00     -0.059     -0.029      -0.579       0.188
+51231.00     -0.100     -0.007      -0.512       0.319
+51232.00     -0.136      0.015      -0.368       0.187
+51233.00     -0.165      0.054      -0.175       0.161
+51234.00     -0.194      0.096      -0.013       0.057
+51235.00     -0.227      0.115       0.091      -0.069
+51236.00     -0.260      0.112       0.086      -0.146
+51237.00     -0.279      0.113       0.050      -0.327
+51238.00     -0.283      0.122       0.005      -0.242
+51239.00     -0.282      0.119      -0.019      -0.077
+51240.00     -0.279      0.102      -0.148       0.098
+51241.00     -0.272      0.095      -0.272       0.198
+51242.00     -0.262      0.105      -0.348       0.304
+51243.00     -0.255      0.111      -0.316       0.265
+51244.00     -0.244      0.095      -0.224       0.243
+51245.00     -0.211      0.071      -0.146       0.160
+51246.00     -0.155      0.055      -0.066       0.194
+51247.00     -0.086      0.048      -0.042       0.105
+51248.00     -0.019      0.066      -0.058       0.081
+51249.00      0.029      0.134      -0.108      -0.040
+51250.00      0.045      0.225      -0.191      -0.082
+51251.00      0.027      0.282      -0.161       0.100
+51252.00     -0.006      0.275      -0.020       0.233
+51253.00     -0.037      0.209       0.051       0.243
+51254.00     -0.070      0.090       0.048       0.123
+51255.00     -0.102     -0.030      -0.080      -0.119
+51256.00     -0.127     -0.122      -0.178      -0.227
+51257.00     -0.134     -0.179      -0.143      -0.168
+51258.00     -0.119     -0.195      -0.021       0.039
+51259.00     -0.089     -0.180       0.054       0.276
+51260.00     -0.054     -0.128       0.065       0.259
+51261.00     -0.017     -0.022       0.037       0.055
+51262.00      0.022      0.123       0.092      -0.056
+51263.00      0.055      0.200       0.158      -0.249
+51264.00      0.074      0.179       0.226      -0.220
+51265.00      0.081      0.105       0.243      -0.254
+51266.00      0.077      0.007       0.226      -0.262
+51267.00      0.064     -0.099       0.204      -0.103
+51268.00      0.044     -0.194       0.106      -0.131
+51269.00      0.028     -0.237       0.059      -0.226
+51270.00      0.012     -0.221      -0.094      -0.309
+51271.00     -0.012     -0.177      -0.181      -0.321
+51272.00     -0.037     -0.133      -0.185      -0.270
+51273.00     -0.046     -0.089      -0.181      -0.286
+51274.00     -0.040     -0.043      -0.085      -0.174
+51275.00     -0.038     -0.002       0.004       0.064
+51276.00     -0.048      0.038       0.135       0.178
+51277.00     -0.066      0.096       0.124       0.195
+51278.00     -0.101      0.155       0.038       0.121
+51279.00     -0.153      0.169      -0.020       0.184
+51280.00     -0.196      0.135       0.005       0.289
+51281.00     -0.198      0.104       0.049       0.350
+51282.00     -0.162      0.106      -0.054       0.257
+51283.00     -0.098      0.106      -0.210      -0.036
+51284.00     -0.018      0.081      -0.233      -0.395
+51285.00      0.071      0.045      -0.059      -0.389
+51286.00      0.154      0.016       0.201      -0.281
+51287.00      0.209     -0.009       0.440      -0.126
+51288.00      0.213     -0.018       0.462       0.051
+51289.00      0.155      0.002       0.336       0.129
+51290.00      0.074      0.015       0.308       0.202
+51291.00      0.018      0.055       0.325       0.275
+51292.00     -0.004      0.131       0.419       0.446
+51293.00     -0.007      0.211       0.425       0.449
+51294.00     -0.000      0.251       0.328       0.383
+51295.00      0.009      0.199       0.239       0.362
+51296.00      0.017      0.038       0.145       0.108
+51297.00      0.029     -0.105       0.164      -0.126
+51298.00      0.048     -0.188       0.212      -0.185
+51299.00      0.066     -0.230       0.244      -0.269
+51300.00      0.080     -0.237      -0.075      -0.227
+51301.00      0.102     -0.211       0.258      -0.069
+51302.00      0.132     -0.157       0.204      -0.121
+51303.00      0.151     -0.096       0.225      -0.043
+51304.00      0.151     -0.014       0.233       0.146
+51305.00      0.139      0.057       0.166       0.224
+51306.00      0.110      0.086       0.001       0.003
+51307.00      0.056      0.042      -0.162      -0.224
+51308.00     -0.008     -0.072      -0.156      -0.252
+51309.00     -0.057     -0.189      -0.079      -0.221
+51310.00     -0.091     -0.255      -0.012      -0.192
+51311.00     -0.118     -0.288      -0.132      -0.300
+51312.00     -0.132     -0.315      -0.165      -0.419
+51313.00     -0.127     -0.322      -0.067      -0.338
+51314.00     -0.114     -0.291       0.101      -0.192
+51315.00     -0.095     -0.233       0.207      -0.017
+51316.00     -0.053     -0.151       0.150      -0.015
+51317.00      0.023     -0.028       0.100      -0.057
+51318.00      0.118      0.129       0.104       0.065
+51319.00      0.176      0.225       0.281       0.191
+51320.00      0.181      0.233       0.553       0.339
+51321.00      0.144      0.184       0.671       0.253
+51322.00      0.082      0.106       0.601       0.309
+51323.00      0.005      0.024       0.283       0.181
+51324.00     -0.078     -0.034      -0.009       0.068
+51325.00     -0.150     -0.059      -0.170      -0.235
+51326.00     -0.189     -0.071      -0.263      -0.497
+51327.00     -0.196     -0.084      -0.309      -0.595
+51328.00     -0.188     -0.082      -0.309      -0.554
+51329.00     -0.167     -0.059      -0.314      -0.407
+51330.00     -0.131     -0.029      -0.188      -0.318
+51331.00     -0.092     -0.025       0.023      -0.237
+51332.00     -0.063     -0.065       0.300      -0.020
+51333.00     -0.038     -0.128       0.452       0.259
+51334.00     -0.010     -0.188       0.497       0.282
+51335.00      0.016     -0.260       0.285       0.145
+51336.00      0.037     -0.350       0.105      -0.013
+51337.00      0.057     -0.425       0.107      -0.268
+51338.00      0.074     -0.442       0.121      -0.338
+51339.00      0.074     -0.402       0.193      -0.390
+51340.00      0.064     -0.333       0.248      -0.478
+51341.00      0.055     -0.243       0.371      -0.314
+51342.00      0.038     -0.137       0.491       0.014
+51343.00      0.003     -0.043       0.467       0.096
+51344.00     -0.035      0.016       0.350       0.088
+51345.00     -0.053      0.039       0.096       0.187
+51346.00     -0.038      0.037      -0.072       0.247
+51347.00     -0.009      0.015      -0.099       0.476
+51348.00      0.011     -0.018       0.064       0.593
+51349.00      0.018     -0.052       0.260       0.486
+51350.00      0.018     -0.092       0.279       0.285
+51351.00      0.004     -0.127       0.159       0.165
+51352.00     -0.041     -0.131       0.013       0.056
+51353.00     -0.122     -0.084      -0.055      -0.126
+51354.00     -0.207     -0.014      -0.004      -0.353
+51355.00     -0.267      0.054       0.018      -0.519
+51356.00     -0.301      0.114      -0.036      -0.577
+51357.00     -0.303      0.156      -0.103      -0.391
+51358.00     -0.245      0.149      -0.102      -0.243
+51359.00     -0.117      0.079      -0.005      -0.040
+51360.00      0.010      0.015       0.166       0.163
+51361.00      0.085     -0.028       0.368       0.480
+51362.00      0.117     -0.059       0.518       0.664
+51363.00      0.126     -0.082       0.608       0.670
+51364.00      0.117     -0.101       0.501       0.508
+51365.00      0.093     -0.118       0.238       0.330
+51366.00      0.062     -0.127       0.164      -0.008
+51367.00      0.030     -0.119       0.117      -0.144
+51368.00     -0.001     -0.089       0.154      -0.212
+51369.00     -0.023     -0.040       0.236      -0.025
+51370.00     -0.032      0.008       0.255       0.252
+51371.00     -0.035      0.032       0.264       0.312
+51372.00     -0.038      0.026       0.199       0.242
+51373.00     -0.041     -0.006       0.113       0.172
+51374.00     -0.041     -0.058       0.010       0.300
+51375.00     -0.026     -0.069       0.074       0.471
+51376.00     -0.001     -0.017       0.190       0.572
+51377.00      0.021      0.068       0.319       0.511
+51378.00      0.036      0.132       0.355       0.295
+51379.00      0.035      0.131       0.195       0.260
+51380.00      0.004      0.068      -0.018       0.203
+51381.00     -0.028      0.016      -0.158       0.112
+51382.00     -0.035     -0.004      -0.215      -0.081
+51383.00     -0.019     -0.005      -0.233      -0.313
+51384.00     -0.001      0.003      -0.196      -0.468
+51385.00     -0.001      0.016      -0.099      -0.404
+51386.00     -0.010      0.021      -0.065      -0.187
+51387.00     -0.020      0.020      -0.065      -0.119
+51388.00     -0.043      0.020      -0.113      -0.050
+51389.00     -0.092      0.013      -0.160       0.062
+51390.00     -0.151     -0.018      -0.076       0.091
+51391.00     -0.199     -0.067      -0.077       0.075
+51392.00     -0.228     -0.117      -0.100       0.181
+51393.00     -0.239     -0.158      -0.199       0.049
+51394.00     -0.223     -0.198      -0.242      -0.127
+51395.00     -0.168     -0.233      -0.052      -0.265
+51396.00     -0.084     -0.239       0.132      -0.358
+51397.00      0.006     -0.205       0.362      -0.204
+51398.00      0.088     -0.152       0.435       0.046
+51399.00      0.148     -0.116       0.425       0.006
+51400.00      0.161     -0.102       0.301      -0.052
+51401.00      0.105     -0.091       0.160      -0.158
+51402.00      0.004     -0.064       0.047       0.075
+51403.00     -0.096     -0.076      -0.071       0.255
+51404.00     -0.171     -0.132      -0.038       0.535
+51405.00     -0.211     -0.185       0.049       0.505
+51406.00     -0.209     -0.207       0.116       0.413
+51407.00     -0.161     -0.172       0.199       0.281
+51408.00     -0.071     -0.057       0.109       0.138
+51409.00      0.009      0.082      -0.039      -0.009
+51410.00      0.066      0.205      -0.109      -0.260
+51411.00      0.099      0.282      -0.174      -0.591
+51412.00      0.104      0.286      -0.104      -0.773
+51413.00      0.091      0.214      -0.008      -0.796
+51414.00      0.086      0.063       0.101      -0.592
+51415.00      0.106     -0.154       0.104      -0.221
+51416.00      0.130     -0.337       0.135      -0.137
+51417.00      0.140     -0.441       0.133      -0.245
+51418.00      0.135     -0.492       0.102      -0.396
+51419.00      0.124     -0.511       0.190      -0.414
+51420.00      0.113     -0.492       0.198      -0.251
+51421.00      0.103     -0.426       0.186      -0.210
+51422.00      0.095     -0.337       0.176      -0.228
+51423.00      0.096     -0.260       0.204      -0.239
+51424.00      0.113     -0.197       0.269      -0.360
+51425.00      0.142     -0.122       0.602      -2.311
+51426.00      0.173     -0.039       0.858      -2.223
+51427.00      0.199      0.029       0.930      -1.797
+51428.00      0.220      0.072       0.816      -1.086
+51429.00      0.237      0.096       0.583      -0.135
+51430.00      0.245      0.092       0.324       0.948
+51431.00      0.242      0.047       0.049       1.792
+51432.00      0.230     -0.032      -0.252       2.250
+51433.00      0.214     -0.119      -0.469       2.185
+51434.00      0.192     -0.205      -0.501       1.734
+51435.00      0.160     -0.291      -0.402       0.858
+51436.00      0.123     -0.354      -0.236      -0.041
+51437.00      0.093     -0.352      -0.087      -1.148
+51438.00      0.073     -0.286       0.130      -2.144
+51439.00      0.052     -0.204       0.438      -2.720
+51440.00      0.024     -0.146       0.778      -2.881
+51441.00      0.009     -0.102       1.031      -2.333
+51442.00      0.028     -0.054       0.992      -1.383
+51443.00      0.078     -0.012       0.682      -0.153
+51444.00      0.104     -0.001       0.221       1.002
+51445.00      0.093     -0.029      -0.276       1.624
+51446.00      0.058     -0.083      -0.717       1.840
+51447.00      0.008     -0.154      -1.049       1.687
+51448.00     -0.048     -0.221      -1.136       1.319
+51449.00     -0.098     -0.252      -0.947       0.765
+51450.00     -0.130     -0.236      -0.515      -0.013
+51451.00     -0.133     -0.185      -0.027      -0.985
+51452.00     -0.108     -0.115       0.515      -2.015
+51453.00     -0.059     -0.035       0.998      -2.555
+51454.00      0.003      0.043       1.294      -2.614
+51455.00      0.055      0.088       1.891       0.284
+51456.00      0.079      0.070       1.236       0.281
+51457.00      0.078     -0.020       0.368       0.051
+51458.00      0.068     -0.119      -0.291      -0.115
+51459.00      0.052     -0.197      -0.645      -0.127
+51460.00      0.031     -0.250      -0.646      -0.287
+51461.00      0.012     -0.261      -0.488      -0.240
+51462.00      0.003     -0.231      -0.220      -0.299
+51463.00      0.005     -0.178       0.021      -0.125
+51464.00      0.019     -0.108       0.116       0.021
+51465.00      0.039     -0.038       0.335       0.133
+51466.00      0.057      0.027       0.802       0.256
+51467.00      0.058      0.056       1.384       0.416
+51468.00      0.045      0.021       1.766       0.366
+51469.00      0.035     -0.047       1.673       0.329
+51470.00      0.039     -0.103       1.125       0.091
+51471.00      0.047     -0.137       0.312       0.031
+51472.00      0.057     -0.174      -0.353      -0.194
+51473.00      0.077     -0.222      -0.694      -0.437
+51474.00      0.109     -0.265      -0.604      -0.580
+51475.00      0.143     -0.290      -0.297      -0.660
+51476.00      0.156     -0.285       0.038      -0.473
+51477.00      0.133     -0.250       0.265      -0.221
+51478.00      0.082     -0.209       0.111      -0.168
+51479.00      0.045     -0.207      -0.065      -0.264
+51480.00      0.038     -0.229      -0.188      -0.576
+51481.00      0.049     -0.241      -0.207      -0.879
+51482.00      0.067     -0.241      -0.152      -0.839
+51483.00      0.084     -0.248       0.013      -0.704
+51484.00      0.087     -0.255       0.110      -0.360
+51485.00      0.074     -0.252       0.098      -0.232
+51486.00      0.054     -0.243       0.168      -0.030
+51487.00      0.035     -0.234       0.203       0.100
+51488.00      0.014     -0.216       0.205       0.126
+51489.00     -0.007     -0.172       0.213      -0.083
+51490.00     -0.017     -0.112       0.194      -0.089
+51491.00     -0.010     -0.064       0.134      -0.125
+51492.00      0.011     -0.041       0.063       0.027
+51493.00      0.048     -0.020       0.031       0.029
+51494.00      0.093      0.020       0.064       0.033
+51495.00      0.128      0.057       0.157      -0.103
+51496.00      0.150      0.054       0.345      -0.139
+51497.00      0.169      0.014       0.477      -0.014
+51498.00      0.180     -0.042       0.424      -0.073
+51499.00      0.162     -0.114       0.236      -0.172
+51500.00      0.131     -0.181       0.067      -0.125
+51501.00      0.111     -0.228      -0.031      -0.040
+51502.00      0.105     -0.251      -0.144      -0.017
+51503.00      0.110     -0.250      -0.219      -0.345
+51504.00      0.118     -0.221      -0.192      -0.336
+51505.00      0.115     -0.166      -0.041      -0.183
+51506.00      0.094     -0.127       0.132      -0.083
+51507.00      0.074     -0.146       0.243      -0.220
+51508.00      0.081     -0.194       0.358      -0.480
+51509.00      0.110     -0.209       0.397      -0.680
+51510.00      0.138     -0.182       0.420      -0.662
+51511.00      0.156     -0.154       0.401      -0.455
+51512.00      0.165     -0.141       0.374      -0.230
+51513.00      0.166     -0.130       0.255      -0.078
+51514.00      0.167     -0.125       0.097      -0.003
+51515.00      0.172     -0.139      -0.062       0.195
+51516.00      0.175     -0.164      -0.128       0.233
+51517.00      0.166     -0.174      -0.069       0.200
+51518.00      0.151     -0.160       0.011       0.107
+51519.00      0.136     -0.133       0.069      -0.045
+51520.00      0.116     -0.110       0.147      -0.002
+51521.00      0.082     -0.120       0.237      -0.098
+51522.00      0.035     -0.158       0.265      -0.193
+51523.00     -0.023     -0.206       0.314      -0.224
+51524.00     -0.084     -0.255       0.386      -0.230
+51525.00     -0.127     -0.284       0.348      -0.151
+51526.00     -0.142     -0.275       0.180      -0.157
+51527.00     -0.132     -0.240      -0.024      -0.115
+51528.00     -0.099     -0.226      -0.198      -0.106
+51529.00     -0.040     -0.230      -0.333       0.001
+51530.00      0.032     -0.241      -0.442      -0.027
+51531.00      0.102     -0.273      -0.442      -0.188
+51532.00      0.172     -0.320      -0.323      -0.353
+51533.00      0.243     -0.337      -0.032      -0.329
+51534.00      0.283     -0.317       0.302      -0.281
+51535.00      0.268     -0.308       0.472      -0.271
+51536.00      0.220     -0.336       0.491      -0.466
+51537.00      0.170     -0.359       0.416      -0.625
+51538.00      0.121     -0.344       0.257      -0.368
+51539.00      0.063     -0.307       0.157      -0.135
+51540.00     -0.002     -0.269       0.014      -0.062
+51541.00     -0.064     -0.227      -0.033      -0.149
+51542.00     -0.108     -0.192      -0.151      -0.199
+51543.00     -0.130     -0.182      -0.260      -0.096
+51544.00     -0.135     -0.204      -0.252      -0.119
+51545.00      0.121     -0.258      -0.111      -0.079
+51546.00      0.105     -0.271       0.049      -0.072
+51547.00      0.104     -0.278       0.159      -0.312
+51548.00      0.116     -0.272       0.230      -0.279
+51549.00      0.211     -0.323       0.321      -0.157
+51550.00      0.225     -0.324       0.388      -0.121
+51551.00      0.236     -0.336       0.519      -0.046
+51552.00      0.233     -0.340       0.525       0.138
+51553.00      0.219     -0.327       0.575       0.344
+51554.00      0.203     -0.299       0.496       0.538
+51555.00      0.185     -0.279       0.413       0.616
+51556.00      0.142     -0.255       0.233       0.624
+51557.00      0.126     -0.260       0.037       0.675
+51558.00      0.115     -0.258      -0.168       0.590
+51559.00      0.099     -0.268      -0.259       0.398
+51560.00      0.084     -0.298      -0.202       0.081
+51561.00      0.093     -0.315       0.089      -0.001
+51562.00      0.122     -0.288       0.446       0.156
+51563.00      0.070     -0.143       0.629       0.265
+51564.00      0.064     -0.125       0.672       0.320
+51565.00      0.049     -0.140       0.540       0.263
+51566.00      0.040     -0.160       0.323       0.559
+51567.00      0.037     -0.170       0.094       0.920
+51568.00      0.031     -0.167      -0.053       1.015
+51569.00      0.018     -0.148      -0.081       0.793
+51570.00      0.044     -0.169      -0.203       0.652
+51571.00      0.034     -0.157      -0.264       0.396
+51572.00      0.023     -0.159      -0.276       0.326
+51573.00      0.010     -0.167      -0.199       0.297
+51574.00      0.003     -0.180      -0.063       0.207
+51575.00      0.011     -0.193      -0.005      -0.018
+51576.00      0.026     -0.189       0.011      -0.015
+51577.00      0.041     -0.189       0.071       0.108
+51578.00      0.052     -0.177       0.134       0.284
+51579.00      0.061     -0.186       0.218       0.367
+51580.00      0.059     -0.198       0.312       0.089
+51581.00      0.044     -0.192       0.366      -0.178
+51582.00      0.025     -0.173       0.444      -0.177
+51583.00      0.010     -0.158       0.350      -0.097
+51584.00      0.003     -0.179       0.190       0.024
+51585.00     -0.019     -0.183      -0.044      -0.078
+51586.00     -0.028     -0.188      -0.271      -0.151
+51587.00     -0.021     -0.195      -0.368      -0.281
+51588.00     -0.014     -0.201      -0.330      -0.483
+51589.00     -0.013     -0.195      -0.114      -0.374
+51590.00     -0.011     -0.179       0.060      -0.188
+51591.00     -0.003     -0.162       0.213      -0.048
+51592.00      0.005     -0.151       0.295      -0.015
+51593.00      0.005     -0.143       0.226      -0.087
+51594.00     -0.003     -0.136       0.149       0.062
+51595.00     -0.016     -0.138       0.046       0.285
+51596.00     -0.033     -0.141      -0.039       0.191
+51597.00     -0.052     -0.138      -0.062      -0.023
+51598.00     -0.066     -0.129      -0.060      -0.438
+51599.00     -0.076     -0.128      -0.032      -0.648
+51600.00     -0.084     -0.129       0.034      -0.669
+51601.00     -0.089     -0.124       0.096      -0.493
+51602.00     -0.082     -0.117       0.132      -0.352
+51603.00     -0.060     -0.116       0.118      -0.181
+51604.00     -0.037     -0.115       0.031      -0.122
+51605.00     -0.108     -0.058      -0.029       0.071
+51606.00     -0.106     -0.047      -0.010       0.162
+51607.00     -0.107     -0.054       0.022       0.264
+51608.00     -0.112     -0.073       0.024       0.145
+51609.00     -0.122     -0.083      -0.048      -0.022
+51610.00     -0.133     -0.079      -0.133      -0.200
+51611.00     -0.139     -0.078      -0.227      -0.208
+51612.00     -0.181     -0.084      -0.307      -0.086
+51613.00     -0.192     -0.084      -0.335      -0.066
+51614.00     -0.197     -0.085      -0.337      -0.055
+51615.00     -0.181     -0.092      -0.233      -0.047
+51616.00     -0.155     -0.090      -0.111      -0.143
+51617.00     -0.143     -0.071       0.005      -0.069
+51618.00     -0.150     -0.056       0.011       0.050
+51619.00     -0.051     -0.004      -0.012       0.130
+51620.00     -0.051     -0.035      -0.024       0.247
+51621.00     -0.041     -0.043      -0.005       0.177
+51622.00     -0.037     -0.024       0.047       0.201
+51623.00     -0.047     -0.009       0.094       0.292
+51624.00     -0.069     -0.011       0.063       0.221
+51625.00     -0.089     -0.017       0.042       0.091
+51626.00     -0.097     -0.070       0.067      -0.349
+51627.00     -0.107     -0.067       0.070      -0.557
+51628.00     -0.117     -0.069       0.170      -0.537
+51629.00     -0.121     -0.068       0.213      -0.517
+51630.00     -0.108     -0.059       0.228      -0.290
+51631.00     -0.084     -0.052       0.090      -0.111
+51632.00     -0.061     -0.049      -0.034       0.038
+51633.00     -0.084     -0.054      -0.072       0.298
+51634.00     -0.089     -0.036      -0.079       0.462
+51635.00     -0.104     -0.038       0.047       0.574
+51636.00     -0.122     -0.066       0.157       0.583
+51637.00     -0.135     -0.092       0.227       0.311
+51638.00     -0.143     -0.097       0.181       0.071
+51639.00     -0.150     -0.095       0.087      -0.005
+51640.00     -0.157     -0.098       0.008       0.024
+51641.00     -0.159     -0.116       0.007       0.151
+51642.00     -0.154     -0.108       0.042       0.191
+51643.00     -0.146     -0.107       0.123       0.136
+51644.00     -0.139     -0.115       0.163       0.039
+51645.00     -0.138     -0.113       0.148      -0.165
+51646.00     -0.141     -0.104       0.081      -0.198
+51647.00     -0.230     -0.105      -0.002      -0.076
+51648.00     -0.219     -0.122      -0.111       0.061
+51649.00     -0.205     -0.124      -0.208       0.207
+51650.00     -0.198     -0.103      -0.221       0.266
+51651.00     -0.206     -0.087      -0.159       0.307
+51652.00     -0.222     -0.090      -0.227       0.287
+51653.00     -0.233     -0.099      -0.306       0.101
+51654.00     -0.325     -0.066      -0.396      -0.191
+51655.00     -0.325     -0.057      -0.411      -0.416
+51656.00     -0.325     -0.061      -0.319      -0.453
+51657.00     -0.314     -0.071      -0.257      -0.536
+51658.00     -0.288     -0.078      -0.137      -0.413
+51659.00     -0.259     -0.079      -0.076      -0.304
+51660.00     -0.236     -0.074      -0.074      -0.022
+51661.00     -0.193     -0.055      -0.123       0.318
+51662.00     -0.190     -0.016      -0.207       0.511
+51663.00     -0.202     -0.007      -0.223       0.576
+51664.00     -0.220     -0.044      -0.090       0.728
+51665.00     -0.227     -0.088       0.019       0.650
+51666.00     -0.224     -0.099       0.017       0.533
+51667.00     -0.225     -0.087      -0.098       0.152
+51668.00     -0.232     -0.101      -0.168       0.140
+51669.00     -0.227     -0.102      -0.173       0.218
+51670.00     -0.208     -0.099      -0.038       0.208
+51671.00     -0.186     -0.101       0.075       0.190
+51672.00     -0.177     -0.117      -0.013      -0.066
+51673.00     -0.178     -0.130      -0.071      -0.188
+51674.00     -0.178     -0.130      -0.113      -0.209
+51675.00     -0.169     -0.121      -0.182      -0.099
+51676.00     -0.156     -0.107      -0.266       0.282
+51677.00     -0.148     -0.087      -0.388       0.468
+51678.00     -0.151     -0.061      -0.450       0.604
+51679.00     -0.167     -0.049      -0.456       0.635
+51680.00     -0.188     -0.057      -0.405       0.360
+51681.00     -0.200     -0.070      -0.369       0.069
+51682.00     -0.230     -0.109      -0.339      -0.309
+51683.00     -0.220     -0.111      -0.339      -0.427
+51684.00     -0.211     -0.118      -0.271      -0.460
+51685.00     -0.191     -0.128      -0.132      -0.420
+51686.00     -0.159     -0.130      -0.031      -0.435
+51687.00     -0.135     -0.131       0.162      -0.362
+51688.00     -0.127     -0.131       0.328      -0.190
+51689.00     -0.074     -0.077       0.329       0.118
+51690.00     -0.075     -0.037       0.184       0.218
+51691.00     -0.084     -0.020       0.077       0.177
+51692.00     -0.097     -0.055       0.091       0.211
+51693.00     -0.098     -0.104       0.235       0.327
+51694.00     -0.089     -0.112       0.277       0.201
+51695.00     -0.086     -0.089       0.234      -0.128
+51696.00      0.032     -0.076       0.145      -0.319
+51697.00      0.019     -0.075       0.259      -0.311
+51698.00      0.014     -0.075       0.368      -0.082
+51699.00      0.014     -0.081       0.412       0.021
+51700.00      0.017     -0.098       0.313      -0.093
+51701.00      0.019     -0.109       0.163      -0.064
+51702.00      0.015     -0.106       0.118       0.072
+51703.00      0.006     -0.095       0.106       0.204
+51704.00     -0.005     -0.079       0.156       0.347
+51705.00     -0.018     -0.050       0.060       0.579
+51706.00     -0.038     -0.021      -0.136       0.603
+51707.00     -0.065     -0.009      -0.258       0.472
+51708.00     -0.097     -0.015      -0.313       0.237
+51709.00     -0.120     -0.028      -0.252      -0.035
+51710.00     -0.237     -0.028      -0.172      -0.292
+51711.00     -0.212     -0.043      -0.137      -0.364
+51712.00     -0.189     -0.055      -0.137      -0.156
+51713.00     -0.166     -0.051      -0.199      -0.139
+51714.00     -0.139     -0.032      -0.143      -0.057
+51715.00     -0.122     -0.021      -0.102      -0.009
+51716.00     -0.127     -0.029      -0.040       0.186
+51717.00     -0.108     -0.005      -0.081       0.373
+51718.00     -0.117      0.005      -0.249       0.458
+51719.00     -0.118      0.012      -0.397       0.374
+51720.00     -0.115     -0.012      -0.423       0.375
+51721.00     -0.103     -0.045      -0.257       0.428
+51722.00     -0.091     -0.043      -0.106       0.408
+51723.00     -0.090     -0.018      -0.000       0.264
+51724.00     -0.072     -0.059       0.091      -0.039
+51725.00     -0.070     -0.058       0.216      -0.215
+51726.00     -0.065     -0.053       0.399      -0.156
+51727.00     -0.067     -0.052       0.449      -0.159
+51728.00     -0.064     -0.065       0.252      -0.315
+51729.00     -0.048     -0.075       0.093      -0.376
+51730.00     -0.030     -0.072       0.012      -0.219
+51731.00     -0.049     -0.127      -0.030      -0.044
+51732.00     -0.056     -0.118       0.057       0.009
+51733.00     -0.067     -0.094       0.017      -0.112
+51734.00     -0.075     -0.065      -0.080      -0.048
+51735.00     -0.083     -0.059      -0.248       0.030
+51736.00     -0.092     -0.073      -0.261       0.007
+51737.00     -0.098     -0.089      -0.108      -0.095
+51738.00     -0.080     -0.116       0.079      -0.202
+51739.00     -0.053     -0.123       0.264      -0.217
+51740.00     -0.027     -0.125       0.305      -0.076
+51741.00     -0.017     -0.112       0.303      -0.123
+51742.00     -0.012     -0.093       0.213      -0.127
+51743.00     -0.009     -0.082       0.185      -0.093
+51744.00     -0.018     -0.092       0.158      -0.087
+51745.00     -0.005     -0.099       0.041      -0.004
+51746.00     -0.023     -0.102      -0.087       0.162
+51747.00     -0.026     -0.095      -0.318       0.167
+51748.00     -0.022     -0.098      -0.403       0.089
+51749.00     -0.016     -0.110      -0.342       0.114
+51750.00     -0.017     -0.111      -0.240       0.108
+51751.00     -0.031     -0.099      -0.071      -0.026
+51752.00     -0.045     -0.093       0.033      -0.190
+51753.00     -0.040     -0.089       0.223      -0.203
+51754.00     -0.018     -0.077       0.288      -0.184
+51755.00     -0.004     -0.066       0.353      -0.172
+51756.00     -0.000     -0.073       0.277      -0.397
+51757.00      0.004     -0.082       0.128      -0.477
+51758.00      0.012     -0.081       0.012      -0.189
+51759.00      0.001     -0.088      -0.027       0.122
+51760.00     -0.006     -0.083       0.044       0.145
+51761.00     -0.016     -0.063       0.088       0.018
+51762.00     -0.022     -0.039       0.017      -0.105
+51763.00     -0.018     -0.040      -0.101       0.019
+51764.00     -0.009     -0.067      -0.147       0.069
+51765.00      0.002     -0.086      -0.059       0.042
+51766.00      0.082     -0.046       0.127      -0.121
+51767.00      0.102     -0.030       0.316      -0.228
+51768.00      0.116     -0.016       0.473      -0.304
+51769.00      0.109     -0.011       0.519      -0.196
+51770.00      0.093     -0.015       0.575      -0.187
+51771.00      0.084     -0.027       0.565      -0.169
+51772.00      0.077     -0.035       0.489      -0.139
+51773.00      0.121     -0.049       0.393       0.086
+51774.00      0.093     -0.034       0.229       0.191
+51775.00      0.073     -0.017       0.092       0.314
+51776.00      0.060     -0.008      -0.091       0.316
+51777.00      0.046     -0.009      -0.155       0.209
+51778.00      0.029     -0.016      -0.151       0.123
+51779.00      0.011     -0.028       0.003       0.076
+51780.00     -0.007     -0.031       0.155       0.085
+51781.00     -0.015     -0.019       0.212       0.124
+51782.00     -0.007      0.004       0.220       0.088
+51783.00      0.008      0.016       0.110       0.057
+51784.00      0.013      0.011      -0.016      -0.030
+51785.00      0.003      0.011      -0.134      -0.049
+51786.00     -0.018      0.021      -0.258       0.160
+51787.00     -0.082      0.096      -0.390       0.387
+51788.00     -0.097      0.096      -0.423       0.494
+51789.00     -0.108      0.102      -0.572       0.434
+51790.00     -0.114      0.113      -0.559       0.111
+51791.00     -0.113      0.104      -0.245       0.012
+51792.00     -0.104      0.078      -0.193      -0.015
+51793.00     -0.091      0.064      -0.132      -0.092
+51794.00      0.008      0.067       0.040      -0.154
+51795.00      0.024      0.090       0.217      -0.340
+51796.00      0.030      0.099       0.308      -0.404
+51797.00      0.019      0.090       0.366      -0.297
+51798.00      0.005      0.070       0.307      -0.103
+51799.00      0.005      0.052       0.187       0.077
+51800.00      0.010      0.054      -0.036       0.052
+51801.00     -0.002      0.048      -0.198       0.037
+51802.00     -0.017      0.066      -0.352       0.052
+51803.00     -0.029      0.066      -0.440       0.114
+51804.00     -0.032      0.058      -0.466       0.233
+51805.00     -0.033      0.054      -0.419       0.197
+51806.00     -0.033      0.050      -0.251       0.176
+51807.00     -0.026      0.035      -0.034       0.029
+51808.00      0.013     -0.011       0.284       0.034
+51809.00      0.024     -0.007       0.458       0.083
+51810.00      0.033      0.010       0.438       0.162
+51811.00      0.043      0.016       0.387      -0.035
+51812.00      0.053      0.010       0.324      -0.166
+51813.00      0.052      0.016       0.277      -0.240
+51814.00      0.037      0.039       0.235      -0.100
+51815.00      0.017      0.054       0.177       0.109
+51816.00     -0.009      0.078       0.101       0.308
+51817.00     -0.014      0.068       0.034       0.298
+51818.00     -0.012      0.064       0.055       0.165
+51819.00     -0.008      0.052       0.087       0.141
+51820.00     -0.001      0.035       0.137       0.222
+51821.00      0.013      0.034       0.213       0.159
+51822.00      0.031      0.101       0.198       0.035
+51823.00      0.047      0.110       0.291      -0.110
+51824.00      0.048      0.092       0.350      -0.127
+51825.00      0.037      0.063       0.402      -0.030
+51826.00      0.027      0.047       0.338       0.108
+51827.00      0.026      0.052       0.257       0.235
+51828.00      0.028      0.070       0.086       0.178
+51829.00     -0.010      0.086      -0.105       0.036
+51830.00     -0.020      0.091      -0.220      -0.095
+51831.00     -0.024      0.074      -0.277       0.019
+51832.00     -0.020      0.052      -0.306       0.225
+51833.00     -0.015      0.049      -0.273       0.433
+51834.00     -0.012      0.060      -0.158       0.377
+51835.00     -0.007      0.058       0.092       0.248
+51836.00     -0.034      0.045       0.296      -0.035
+51837.00     -0.015      0.026       0.448      -0.064
+51838.00      0.001      0.028       0.448      -0.143
+51839.00      0.008      0.034       0.374      -0.147
+51840.00      0.006      0.035       0.264      -0.099
+51841.00     -0.002      0.041       0.129      -0.134
+51842.00     -0.011      0.055       0.032      -0.076
+51843.00     -0.021      0.060      -0.020       0.071
+51844.00     -0.029      0.048      -0.088       0.133
+51845.00     -0.030      0.033      -0.211       0.162
+51846.00     -0.023      0.024      -0.225       0.085
+51847.00     -0.014      0.012      -0.155       0.050
+51848.00     -0.004     -0.008      -0.071       0.032
+51849.00      0.012     -0.014       0.038      -0.256
+51850.00      0.036      0.002       0.205      -0.164
+51851.00      0.055      0.009       0.295      -0.237
+51852.00      0.060     -0.019       0.370      -0.264
+51853.00      0.055     -0.056       0.388      -0.189
+51854.00      0.051     -0.065       0.312      -0.172
+51855.00      0.046     -0.043       0.156      -0.232
+51856.00      0.037     -0.020       0.002      -0.449
+51857.00      0.030     -0.013      -0.115      -0.687
+51858.00      0.031     -0.012      -0.279      -0.891
+51859.00      0.038     -0.014      -0.360      -0.889
+51860.00      0.042     -0.014      -0.429      -0.527
+51861.00      0.039     -0.009      -0.352      -0.155
+51862.00      0.035     -0.004      -0.251       0.076
+51863.00      0.035     -0.019      -0.125       0.035
+51864.00      0.044     -0.048       0.086      -0.265
+51865.00      0.060     -0.066       0.235      -0.400
+51866.00      0.082     -0.055       0.351      -0.460
+51867.00      0.098     -0.026       0.334      -0.499
+51868.00      0.097     -0.000       0.275      -0.320
+51869.00      0.082      0.015       0.176      -0.268
+51870.00      0.069      0.016       0.070      -0.142
+51871.00      0.062      0.003       0.050      -0.047
+51872.00      0.057     -0.018      -0.001       0.012
+51873.00      0.052     -0.028      -0.053       0.070
+51874.00      0.053     -0.024      -0.085       0.086
+51875.00      0.061     -0.031      -0.050       0.169
+51876.00      0.074     -0.061       0.104       0.242
+51877.00      0.091     -0.085       0.283       0.216
+51878.00      0.113     -0.081       0.425       0.087
+51879.00      0.128     -0.064       0.581      -0.054
+51880.00      0.155      0.008       0.595      -0.222
+51881.00      0.152     -0.017       0.602      -0.120
+51882.00      0.148     -0.025       0.480      -0.108
+51883.00      0.137     -0.015       0.277      -0.036
+51884.00      0.118     -0.012       0.189      -0.024
+51885.00      0.120     -0.057       0.159      -0.097
+51886.00      0.121     -0.056       0.097      -0.158
+51887.00      0.126     -0.046       0.056      -0.403
+51888.00      0.125     -0.034       0.054      -0.352
+51889.00      0.118     -0.027       0.146      -0.063
+51890.00      0.106     -0.035       0.220       0.149
+51891.00      0.100     -0.075       0.272       0.122
+51892.00      0.143     -0.100       0.327      -0.039
+51893.00      0.158     -0.117       0.362      -0.275
+51894.00      0.171     -0.087       0.484      -0.209
+51895.00      0.179     -0.043       0.495      -0.106
+51896.00      0.177     -0.010       0.471      -0.009
+51897.00      0.163      0.008       0.325      -0.058
+51898.00      0.144      0.012       0.204      -0.118
+51899.00      0.140     -0.049       0.127      -0.195
+51900.00      0.129     -0.078       0.065      -0.422
+51901.00      0.118     -0.090       0.031      -0.468
+51902.00      0.112     -0.081       0.031      -0.443
+51903.00      0.116     -0.079       0.013      -0.317
+51904.00      0.129     -0.102      -0.015      -0.183
+51905.00      0.145     -0.134       0.095      -0.167
+51906.00      0.161     -0.146       0.164      -0.071
+51907.00      0.166     -0.133       0.332      -0.071
+51908.00      0.158     -0.123       0.463      -0.074
+51909.00      0.149     -0.123       0.536       0.089
+51910.00      0.142     -0.123       0.506       0.036
+51911.00      0.128     -0.126       0.379      -0.042
+51912.00      0.106     -0.138       0.274      -0.196
+51913.00      0.090     -0.143       0.138      -0.265
+51914.00      0.078     -0.130      -0.027       0.110
+51915.00      0.065     -0.123      -0.167      -0.102
+51916.00      0.066     -0.140      -0.127      -0.365
+51917.00      0.089     -0.150       0.054      -0.303
+51918.00      0.112     -0.135       0.303      -0.181
+51919.00      0.110     -0.133       0.289      -0.153
+51920.00      0.192     -0.199       0.162      -0.417
+51921.00      0.182     -0.235       0.014      -0.799
+51922.00      0.180     -0.231      -0.019      -0.678
+51923.00      0.176     -0.209       0.017      -0.591
+51924.00      0.169     -0.196       0.093      -0.478
+51925.00      0.160     -0.184       0.140      -0.407
+51926.00      0.144     -0.169       0.121      -0.426
+51927.00      0.134     -0.168       0.100      -0.298
+51928.00      0.118     -0.190       0.111      -0.342
+51929.00      0.106     -0.211       0.199      -0.346
+51930.00      0.100     -0.216       0.247      -0.367
+51931.00      0.106     -0.213       0.189      -0.334
+51932.00      0.121     -0.215       0.159      -0.193
+51933.00      0.138     -0.222       0.196      -0.065
+51934.00      0.179     -0.188       0.260       0.024
+51935.00      0.181     -0.190       0.429      -0.102
+51936.00      0.168     -0.189       0.515      -0.115
+51937.00      0.151     -0.187       0.595      -0.025
+51938.00      0.138     -0.183       0.560      -0.125
+51939.00      0.125     -0.187       0.462      -0.147
+51940.00      0.110     -0.203       0.261      -0.334
+51941.00      0.122     -0.258       0.076      -0.493
+51942.00      0.109     -0.236      -0.131      -0.667
+51943.00      0.084     -0.229      -0.242      -1.062
+51944.00      0.071     -0.268      -0.248      -1.407
+51945.00      0.103     -0.307       0.033      -1.371
+51946.00      0.159     -0.277       0.263      -0.982
+51947.00      0.180     -0.210       0.268      -0.357
+51948.00      0.154     -0.156       0.009      -0.013
+51949.00      0.117     -0.192      -0.276      -0.004
+51950.00      0.101     -0.234      -0.408       0.291
+51951.00      0.098     -0.254      -0.333       0.303
+51952.00      0.094     -0.259      -0.155       0.218
+51953.00      0.088     -0.251       0.060      -0.118
+51954.00      0.079     -0.231       0.149      -0.284
+51955.00      0.061     -0.192       0.276      -0.176
+51956.00      0.050     -0.205       0.343      -0.139
+51957.00      0.042     -0.228       0.514      -0.188
+51958.00      0.043     -0.248       0.578      -0.178
+51959.00      0.058     -0.255       0.541      -0.226
+51960.00      0.080     -0.247       0.411      -0.189
+51961.00      0.097     -0.224       0.306      -0.225
+51962.00      0.147     -0.213       0.257      -0.278
+51963.00      0.145     -0.217       0.327      -0.271
+51964.00      0.132     -0.236       0.353      -0.362
+51965.00      0.111     -0.250       0.349      -0.300
+51966.00      0.094     -0.248       0.342      -0.245
+51967.00      0.086     -0.246       0.278      -0.279
+51968.00      0.079     -0.253       0.117      -0.423
+51969.00     -0.038     -0.305      -0.011      -0.579
+51970.00     -0.033     -0.295      -0.100      -0.795
+51971.00     -0.029     -0.284      -0.174      -1.114
+51972.00     -0.034     -0.301      -0.190      -1.259
+51973.00     -0.028     -0.330      -0.054      -1.255
+51974.00     -0.002     -0.322       0.100      -0.849
+51975.00      0.017     -0.274       0.098      -0.229
+51976.00      0.007     -0.237      -0.038       0.093
+51977.00     -0.020     -0.243      -0.216       0.244
+51978.00     -0.035     -0.272      -0.295       0.400
+51979.00     -0.038     -0.292      -0.271       0.498
+51980.00     -0.042     -0.291      -0.108       0.422
+51981.00     -0.051     -0.276       0.041       0.154
+51982.00     -0.059     -0.257       0.153      -0.224
+51983.00     -0.063     -0.252       0.269      -0.361
+51984.00     -0.066     -0.263       0.358      -0.339
+51985.00     -0.069     -0.276       0.504      -0.380
+51986.00     -0.062     -0.286       0.545      -0.407
+51987.00     -0.039     -0.292       0.438      -0.561
+51988.00     -0.012     -0.284       0.186      -0.440
+51989.00      0.000     -0.255       0.004      -0.427
+51990.00      0.002     -0.270      -0.093      -0.374
+51991.00     -0.010     -0.267      -0.084      -0.378
+51992.00     -0.025     -0.288      -0.074      -0.437
+51993.00     -0.045     -0.309      -0.022      -0.441
+51994.00     -0.059     -0.314       0.024      -0.373
+51995.00     -0.058     -0.311       0.014      -0.318
+51996.00     -0.054     -0.309       0.059      -0.289
+51997.00     -0.023     -0.292       0.105      -0.453
+51998.00     -0.018     -0.290       0.226      -0.701
+51999.00      0.002     -0.285       0.329      -0.773
+52000.00      0.021     -0.276       0.390      -0.802
+52001.00      0.022     -0.268       0.357      -0.664
+52002.00      0.008     -0.265       0.242      -0.516
+52003.00     -0.004     -0.273       0.028      -0.264
+52004.00     -0.008     -0.282      -0.088      -0.140
+52005.00     -0.012     -0.283      -0.263      -0.343
+52006.00     -0.021     -0.272      -0.274      -0.336
+52007.00     -0.032     -0.261      -0.215      -0.226
+52008.00     -0.044     -0.256      -0.176      -0.156
+52009.00     -0.055     -0.251      -0.128      -0.208
+52010.00     -0.060     -0.243      -0.060      -0.386
+52011.00     -0.108     -0.301       0.092      -0.344
+52012.00     -0.106     -0.313       0.266      -0.174
+52013.00     -0.105     -0.321       0.400      -0.010
+52014.00     -0.094     -0.317       0.546      -0.067
+52015.00     -0.069     -0.310       0.529      -0.224
+52016.00     -0.046     -0.300       0.386      -0.286
+52017.00     -0.042     -0.276       0.232      -0.314
+52018.00     -0.056     -0.247       0.124      -0.464
+52019.00     -0.074     -0.238       0.107      -0.593
+52020.00     -0.090     -0.256       0.110      -0.766
+52021.00     -0.103     -0.279       0.142      -0.862
+52022.00     -0.111     -0.289       0.089      -0.781
+52023.00     -0.107     -0.288       0.031      -0.641
+52024.00     -0.096     -0.287       0.036      -0.365
+52025.00     -0.141     -0.329       0.065      -0.186
+52026.00     -0.137     -0.321       0.187      -0.243
+52027.00     -0.127     -0.318       0.366      -0.267
+52028.00     -0.111     -0.314       0.476      -0.360
+52029.00     -0.104     -0.296       0.444      -0.199
+52030.00     -0.114     -0.277       0.251      -0.254
+52031.00     -0.128     -0.278       0.016      -0.309
+52032.00     -0.150     -0.319      -0.127      -0.359
+52033.00     -0.151     -0.324      -0.163      -0.596
+52034.00     -0.158     -0.304      -0.037      -0.625
+52035.00     -0.175     -0.288       0.088      -0.258
+52036.00     -0.190     -0.293       0.041      -0.195
+52037.00     -0.194     -0.306      -0.024      -0.205
+52038.00     -0.186     -0.307      -0.084      -0.451
+52039.00     -0.193     -0.332      -0.034      -0.547
+52040.00     -0.186     -0.339       0.055      -0.572
+52041.00     -0.177     -0.347       0.227      -0.583
+52042.00     -0.160     -0.343       0.313      -0.536
+52043.00     -0.139     -0.331       0.330      -0.517
+52044.00     -0.128     -0.317       0.253      -0.374
+52045.00     -0.134     -0.293       0.137      -0.305
+52046.00     -0.158     -0.246      -0.021      -0.075
+52047.00     -0.172     -0.234      -0.091      -0.052
+52048.00     -0.181     -0.255      -0.017      -0.127
+52049.00     -0.184     -0.286       0.026      -0.312
+52050.00     -0.185     -0.294       0.026      -0.394
+52051.00     -0.186     -0.284      -0.029      -0.414
+52052.00     -0.183     -0.277      -0.029      -0.308
+52053.00     -0.165     -0.261       0.051      -0.286
+52054.00     -0.148     -0.253       0.165      -0.268
+52055.00     -0.135     -0.249       0.336      -0.392
+52056.00     -0.134     -0.255       0.442      -0.488
+52057.00     -0.140     -0.257       0.401      -0.454
+52058.00     -0.146     -0.237       0.237      -0.360
+52059.00     -0.149     -0.207      -0.010      -0.257
+52060.00     -0.157     -0.224      -0.278      -0.099
+52061.00     -0.168     -0.215      -0.469      -0.079
+52062.00     -0.186     -0.211      -0.575      -0.063
+52063.00     -0.205     -0.220      -0.508      -0.036
+52064.00     -0.220     -0.246      -0.137       0.002
+52065.00     -0.221     -0.270      -0.039      -0.131
+52066.00     -0.209     -0.271      -0.022      -0.199
+52067.00     -0.168     -0.241       0.031      -0.400
+52068.00     -0.158     -0.236       0.162      -0.592
+52069.00     -0.145     -0.239       0.244      -0.738
+52070.00     -0.124     -0.236       0.297      -0.834
+52071.00     -0.110     -0.229       0.309      -0.916
+52072.00     -0.116     -0.222       0.178      -0.744
+52073.00     -0.135     -0.208       0.001      -0.363
+52074.00     -0.191     -0.146      -0.261      -0.157
+52075.00     -0.196     -0.129      -0.348       0.044
+52076.00     -0.198     -0.156      -0.359       0.081
+52077.00     -0.195     -0.200      -0.180       0.111
+52078.00     -0.190     -0.213      -0.050       0.069
+52079.00     -0.192     -0.192      -0.027      -0.100
+52080.00     -0.199     -0.172      -0.019      -0.124
+52081.00     -0.200     -0.111       0.062       0.002
+52082.00     -0.181     -0.106       0.225       0.141
+52083.00     -0.159     -0.100       0.233       0.018
+52084.00     -0.152     -0.107       0.179      -0.188
+52085.00     -0.158     -0.117       0.111      -0.324
+52086.00     -0.166     -0.111       0.003      -0.360
+52087.00     -0.169     -0.084      -0.059      -0.214
+52088.00     -0.212     -0.056      -0.253      -0.176
+52089.00     -0.224     -0.028      -0.426      -0.088
+52090.00     -0.242     -0.021      -0.520      -0.042
+52091.00     -0.258     -0.042      -0.457      -0.025
+52092.00     -0.267     -0.081      -0.277      -0.117
+52093.00     -0.264     -0.110      -0.028      -0.148
+52094.00     -0.247     -0.114       0.122      -0.151
+52095.00     -0.263     -0.096      -0.271      -0.153
+52096.00     -0.246     -0.089      -0.427      -0.091
+52097.00     -0.231     -0.082      -0.460      -0.085
+52098.00     -0.214     -0.068      -0.394      -0.107
+52099.00     -0.206     -0.057      -0.278      -0.213
+52100.00     -0.220     -0.066      -0.167      -0.219
+52101.00     -0.242     -0.079      -0.133      -0.088
+52102.00     -0.274     -0.074      -0.289      -0.131
+52103.00     -0.272     -0.060      -0.472      -0.152
+52104.00     -0.269     -0.078      -0.435      -0.250
+52105.00     -0.265     -0.121      -0.305      -0.367
+52106.00     -0.257     -0.138      -0.153      -0.267
+52107.00     -0.251     -0.117      -0.094      -0.209
+52108.00     -0.247     -0.091      -0.054      -0.109
+52109.00     -0.216     -0.088       0.193       0.158
+52110.00     -0.204     -0.074       0.426       0.521
+52111.00     -0.198     -0.063       0.493       0.650
+52112.00     -0.195     -0.067       0.434       0.453
+52113.00     -0.194     -0.078       0.255       0.297
+52114.00     -0.196     -0.079       0.100       0.054
+52115.00     -0.204     -0.074      -0.167      -0.116
+52116.00     -0.238     -0.045      -0.294      -0.230
+52117.00     -0.245     -0.020      -0.468      -0.375
+52118.00     -0.253      0.006      -0.595      -0.359
+52119.00     -0.260      0.005      -0.628      -0.176
+52120.00     -0.261     -0.025      -0.463      -0.209
+52121.00     -0.253     -0.051      -0.214      -0.179
+52122.00     -0.234     -0.056       0.024      -0.013
+52123.00     -0.214     -0.033       0.126       0.024
+52124.00     -0.186     -0.021       0.094       0.228
+52125.00     -0.179     -0.007       0.121       0.255
+52126.00     -0.178      0.019       0.135       0.208
+52127.00     -0.180      0.038       0.127      -0.040
+52128.00     -0.192      0.029       0.161      -0.044
+52129.00     -0.208      0.004       0.050      -0.041
+52130.00     -0.244     -0.012      -0.136      -0.066
+52131.00     -0.242     -0.003      -0.296      -0.247
+52132.00     -0.244     -0.010      -0.341      -0.272
+52133.00     -0.246     -0.039      -0.191      -0.256
+52134.00     -0.241     -0.054      -0.046      -0.085
+52135.00     -0.231     -0.042       0.026      -0.107
+52136.00     -0.214     -0.022       0.005      -0.115
+52137.00     -0.166      0.002      -0.050      -0.073
+52138.00     -0.147      0.027      -0.040       0.123
+52139.00     -0.151      0.042      -0.114       0.201
+52140.00     -0.165      0.030      -0.183      -0.005
+52141.00     -0.168      0.013      -0.235      -0.097
+52142.00     -0.165      0.011      -0.252       0.001
+52143.00     -0.174      0.009      -0.215       0.080
+52144.00     -0.179     -0.009      -0.263       0.038
+52145.00     -0.184     -0.007      -0.331      -0.081
+52146.00     -0.181      0.018      -0.494      -0.152
+52147.00     -0.176      0.032      -0.598      -0.007
+52148.00     -0.169      0.013      -0.581       0.166
+52149.00     -0.158     -0.010      -0.402       0.179
+52150.00     -0.142     -0.012      -0.132       0.046
+52151.00     -0.121     -0.000       0.057       0.094
+52152.00     -0.105      0.010       0.126       0.156
+52153.00     -0.107      0.021       0.185       0.038
+52154.00     -0.119      0.037       0.208       0.088
+52155.00     -0.128      0.051       0.030       0.609
+52156.00     -0.133      0.052      -0.028       0.257
+52157.00     -0.140      0.041      -0.071       0.055
+52158.00     -0.142      0.040      -0.098       0.096
+52159.00     -0.146      0.042      -0.065       0.200
+52160.00     -0.151      0.035       0.017       0.316
+52161.00     -0.154      0.014       0.134       0.326
+52162.00     -0.149     -0.000       0.285       0.304
+52163.00     -0.136      0.003       0.310       0.227
+52164.00     -0.118      0.019       0.241       0.035
+52165.00     -0.120      0.010       0.162      -0.133
+52166.00     -0.098      0.039       0.054      -0.270
+52167.00     -0.095      0.053      -0.032      -0.347
+52168.00     -0.109      0.035      -0.120      -0.627
+52169.00     -0.119      0.015      -0.066      -0.666
+52170.00     -0.121      0.024      -0.085      -0.486
+52171.00     -0.127      0.045      -0.136      -0.146
+52172.00     -0.139      0.044      -0.179       0.043
+52173.00     -0.141      0.038      -0.227      -0.053
+52174.00     -0.133      0.046      -0.266      -0.170
+52175.00     -0.121      0.051      -0.283       0.063
+52176.00     -0.108      0.031      -0.273       0.185
+52177.00     -0.093      0.007      -0.134       0.118
+52178.00     -0.077      0.008      -0.009      -0.104
+52179.00     -0.062      0.024       0.147      -0.418
+52180.00     -0.053      0.035       0.201      -0.363
+52181.00     -0.082      0.082       0.247      -0.350
+52182.00     -0.096      0.082       0.285      -0.288
+52183.00     -0.103      0.085       0.223      -0.200
+52184.00     -0.102      0.093       0.158      -0.188
+52185.00     -0.103      0.103       0.020      -0.054
+52186.00     -0.109      0.106       0.025       0.092
+52187.00     -0.111      0.097       0.051       0.173
+52188.00     -0.106      0.118       0.091       0.234
+52189.00     -0.104      0.095       0.121       0.182
+52190.00     -0.095      0.079       0.146       0.136
+52191.00     -0.073      0.075       0.158       0.183
+52192.00     -0.049      0.082       0.041       0.284
+52193.00     -0.034      0.100      -0.193       0.494
+52194.00     -0.029      0.118      -0.401       0.661
+52195.00     -0.050      0.190      -0.593       0.764
+52196.00     -0.058      0.160      -0.563       0.618
+52197.00     -0.068      0.138      -0.450       0.375
+52198.00     -0.075      0.151      -0.250       0.296
+52199.00     -0.082      0.181      -0.146       0.134
+52200.00     -0.086      0.192      -0.127      -0.137
+52201.00     -0.084      0.185      -0.072      -0.548
+52202.00     -0.079      0.160      -0.070      -1.022
+52203.00     -0.069      0.144       0.003      -1.053
+52204.00     -0.053      0.118       0.011      -0.584
+52205.00     -0.031      0.095       0.056      -0.233
+52206.00     -0.006      0.091       0.150      -0.002
+52207.00      0.017      0.097       0.313       0.041
+52208.00      0.027      0.093       0.464       0.185
+52209.00      0.021      0.085       0.551       0.264
+52210.00      0.009      0.084       0.632       0.341
+52211.00      0.004      0.095       0.605       0.187
+52212.00      0.004      0.112       0.455       0.025
+52213.00     -0.001      0.127       0.197      -0.095
+52214.00     -0.008      0.126       0.007       0.053
+52215.00     -0.007      0.101      -0.098       0.059
+52216.00     -0.033      0.126      -0.123       0.209
+52217.00     -0.026      0.108      -0.068       0.187
+52218.00     -0.021      0.107       0.069       0.069
+52219.00     -0.003      0.108       0.190       0.010
+52220.00      0.023      0.104       0.288       0.061
+52221.00      0.041      0.108       0.151       0.304
+52222.00      0.040      0.117       0.002       0.562
+52223.00      0.030      0.172      -0.056       0.794
+52224.00      0.018      0.140      -0.048       0.785
+52225.00      0.010      0.116       0.099       0.714
+52226.00      0.005      0.121       0.190       0.497
+52227.00      0.007      0.136       0.186       0.047
+52228.00      0.008      0.140       0.063       0.047
+52229.00      0.011      0.130       0.007       0.070
+52230.00      0.006      0.130       0.007      -0.112
+52231.00      0.014      0.113       0.031       0.041
+52232.00      0.027      0.089       0.089       0.052
+52233.00      0.052      0.077       0.126       0.120
+52234.00      0.082      0.085       0.121       0.080
+52235.00      0.107      0.089       0.177       0.008
+52236.00      0.115      0.074       0.225       0.336
+52237.00      0.109      0.063       0.260       0.584
+52238.00      0.095      0.066       0.229       0.719
+52239.00      0.085      0.090       0.205       0.784
+52240.00      0.076      0.112       0.149       0.465
+52241.00      0.065      0.117       0.148       0.115
+52242.00      0.057      0.102       0.154      -0.166
+52243.00      0.060      0.072       0.219      -0.293
+52244.00      0.089      0.049       0.269      -0.254
+52245.00      0.096      0.037       0.336      -0.041
+52246.00      0.099      0.047       0.366      -0.087
+52247.00      0.103      0.057       0.395      -0.084
+52248.00      0.113      0.054       0.438      -0.015
+52249.00      0.124      0.054       0.411       0.215
+52250.00      0.123      0.066       0.272       0.396
+52251.00      0.138      0.071       0.096       0.560
+52252.00      0.119      0.058      -0.032       0.684
+52253.00      0.107      0.037      -0.141       0.823
+52254.00      0.106      0.029      -0.163       0.834
+52255.00      0.077      0.038      -0.193       0.799
+52256.00      0.082      0.033      -0.257       0.654
+52257.00      0.081      0.026      -0.265       0.461
+52258.00      0.081      0.025      -0.220       0.249
+52259.00      0.088      0.015      -0.022       0.061
+52260.00      0.102     -0.004       0.170      -0.117
+52261.00      0.121     -0.014       0.324      -0.134
+52262.00      0.141      0.001       0.400       0.002
+52263.00      0.153      0.022       0.465       0.158
+52264.00      0.148      0.018       0.365       0.497
+52265.00      0.213     -0.095       0.223       0.893
+52266.00      0.195     -0.097       0.093       1.034
+52267.00      0.178     -0.078      -0.158       1.021
+52268.00      0.159     -0.066      -0.245       0.550
+52269.00      0.144     -0.078      -0.283       0.124
+52270.00      0.140     -0.099      -0.235      -0.208
+52271.00      0.159     -0.112      -0.173      -0.420
+52272.00      0.168     -0.131      -0.020      -0.190
+52273.00      0.177     -0.139       0.122       0.155
+52274.00      0.183     -0.129       0.206       0.489
+52275.00      0.185     -0.118       0.591       0.263
+52276.00      0.181     -0.118       0.445      -0.050
+52277.00      0.172     -0.115       0.307      -0.139
+52278.00      0.163     -0.095       0.205      -0.174
+52279.00      0.140     -0.071       0.137      -0.007
+52280.00      0.125     -0.067       0.057       0.149
+52281.00      0.108     -0.081       0.038       0.151
+52282.00      0.101     -0.096       0.043       0.224
+52283.00      0.115     -0.115       0.159       0.190
+52284.00      0.121     -0.123       0.242       0.058
+52285.00      0.119     -0.123       0.293      -0.036
+52286.00      0.134     -0.113       0.236      -0.152
+52287.00      0.141     -0.114       0.199      -0.204
+52288.00      0.155     -0.137       0.190      -0.178
+52289.00      0.170     -0.159       0.190      -0.248
+52290.00      0.180     -0.151       0.211      -0.289
+52291.00      0.178     -0.118       0.207      -0.299
+52292.00      0.162     -0.097       0.203      -0.121
+52293.00      0.141     -0.133       0.236       0.087
+52294.00      0.128     -0.138       0.208       0.227
+52295.00      0.110     -0.140       0.218       0.271
+52296.00      0.087     -0.153       0.229       0.167
+52297.00      0.073     -0.176       0.324       0.051
+52298.00      0.075     -0.188       0.274      -0.073
+52299.00      0.087     -0.193       0.216      -0.152
+52300.00      0.116     -0.188       0.230      -0.236
+52301.00      0.137     -0.190       0.324      -0.155
+52302.00      0.153     -0.166       0.345       0.091
+52303.00      0.149     -0.140       0.123      -0.037
+52304.00      0.130     -0.142      -0.008      -0.142
+52305.00      0.112     -0.155      -0.049      -0.270
+52306.00      0.100     -0.150       0.138      -0.131
+52307.00      0.089     -0.144       0.306      -0.006
+52308.00      0.080     -0.145       0.364       0.151
+52309.00      0.068     -0.160       0.336       0.165
+52310.00      0.055     -0.170      -0.078       0.181
+52311.00      0.050     -0.180      -0.052       0.124
+52312.00      0.052     -0.193       0.076       0.065
+52313.00      0.059     -0.201       0.255      -0.092
+52314.00      0.107     -0.190       0.305      -0.089
+52315.00      0.123     -0.190       0.321      -0.017
+52316.00      0.143     -0.203       0.264       0.025
+52317.00      0.159     -0.218       0.245       0.023
+52318.00      0.167     -0.214       0.242      -0.103
+52319.00      0.161     -0.188       0.252      -0.105
+52320.00      0.143     -0.165       0.237      -0.075
+52321.00      0.118     -0.179       0.241       0.036
+52322.00      0.108     -0.179       0.226       0.019
+52323.00      0.097     -0.188       0.215       0.062
+52324.00      0.081     -0.213       0.290       0.145
+52325.00      0.075     -0.238       0.366       0.184
+52326.00      0.084     -0.237       0.398       0.064
+52327.00      0.094     -0.229       0.325      -0.285
+52328.00      0.132     -0.277       0.314      -0.598
+52329.00      0.155     -0.291       0.377      -0.621
+52330.00      0.179     -0.251       0.403      -0.402
+52331.00      0.171     -0.183       0.213      -0.143
+52332.00      0.136     -0.166      -0.045      -0.238
+52333.00      0.112     -0.203      -0.103      -0.305
+52334.00      0.112     -0.231       0.158      -0.160
+52335.00      0.082     -0.213       0.359       0.113
+52336.00      0.069     -0.209       0.557       0.450
+52337.00      0.054     -0.216       0.547       0.465
+52338.00      0.044     -0.220       0.426       0.445
+52339.00      0.037     -0.225       0.340       0.334
+52340.00      0.036     -0.242       0.293       0.217
+52341.00      0.045     -0.265       0.331      -0.109
+52342.00      0.090     -0.268       0.374      -0.306
+52343.00      0.114     -0.265       0.354      -0.387
+52344.00      0.139     -0.253       0.268      -0.276
+52345.00      0.152     -0.233       0.213      -0.181
+52346.00      0.146     -0.207       0.255      -0.217
+52347.00      0.133     -0.191       0.263      -0.333
+52348.00      0.111     -0.190       0.289      -0.272
+52349.00      0.088     -0.197       0.263      -0.038
+52350.00      0.076     -0.198       0.234       0.041
+52351.00      0.072     -0.199       0.251       0.095
+52352.00      0.068     -0.215       0.287       0.174
+52353.00      0.074     -0.237       0.378       0.210
+52354.00      0.096     -0.235       0.414       0.208
+52355.00      0.114     -0.222       0.350      -0.053
+52356.00      0.117     -0.237       0.288      -0.498
+52357.00      0.120     -0.267       0.291      -0.685
+52358.00      0.131     -0.248       0.351      -0.540
+52359.00      0.123     -0.179       0.287      -0.235
+52360.00      0.091     -0.151       0.143      -0.172
+52361.00      0.070     -0.183       0.097      -0.173
+52362.00      0.080     -0.226       0.280      -0.199
+52363.00      0.090     -0.231       0.554      -0.013
+52364.00      0.073     -0.213       0.746       0.034
+52365.00      0.046     -0.201       0.735      -0.062
+52366.00      0.033     -0.201       0.589      -0.185
+52367.00      0.035     -0.212       0.431      -0.249
+52368.00      0.028     -0.232       0.265      -0.188
+52369.00      0.042     -0.258       0.249      -0.199
+52370.00      0.061     -0.270       0.202      -0.298
+52371.00      0.085     -0.264       0.148      -0.349
+52372.00      0.105     -0.240       0.007      -0.270
+52373.00      0.106     -0.200      -0.085      -0.119
+52374.00      0.090     -0.161      -0.051      -0.056
+52375.00      0.069     -0.150       0.030      -0.194
+52376.00      0.051     -0.171       0.108      -0.168
+52377.00     -0.003     -0.200       0.175      -0.116
+52378.00     -0.010     -0.209       0.240      -0.044
+52379.00     -0.003     -0.203       0.252       0.008
+52380.00      0.005     -0.205       0.244      -0.003
+52381.00      0.011     -0.199       0.236      -0.042
+52382.00      0.029     -0.208       0.246      -0.098
+52383.00      0.059     -0.200       0.215      -0.109
+52384.00      0.079     -0.194       0.190      -0.176
+52385.00      0.077     -0.200       0.115      -0.227
+52386.00      0.063     -0.200       0.067      -0.130
+52387.00      0.046     -0.182       0.035      -0.091
+52388.00      0.026     -0.171       0.026      -0.125
+52389.00      0.016     -0.185       0.069      -0.252
+52390.00      0.022     -0.203       0.196      -0.166
+52391.00      0.004     -0.204       0.339      -0.014
+52392.00     -0.008     -0.189       0.382       0.051
+52393.00     -0.030     -0.178       0.293      -0.070
+52394.00     -0.040     -0.178       0.154      -0.261
+52395.00     -0.031     -0.195      -0.038      -0.391
+52396.00     -0.011     -0.221      -0.123      -0.339
+52397.00      0.009     -0.237       0.002      -0.212
+52398.00      0.022     -0.206       0.181      -0.187
+52399.00      0.039     -0.189       0.312      -0.149
+52400.00      0.047     -0.172       0.274       0.130
+52401.00      0.034     -0.149       0.182       0.439
+52402.00      0.011     -0.130       0.093       0.389
+52403.00     -0.009     -0.128       0.067       0.180
+52404.00     -0.014     -0.151       0.053      -0.153
+52405.00      0.006     -0.179       0.103      -0.283
+52406.00      0.007     -0.187       0.173      -0.229
+52407.00      0.014     -0.185       0.201      -0.137
+52408.00      0.024     -0.187       0.216      -0.046
+52409.00      0.026     -0.206       0.181      -0.079
+52410.00      0.025     -0.222       0.202      -0.164
+52411.00      0.041     -0.224       0.235      -0.120
+52412.00      0.066     -0.204       0.306      -0.035
+52413.00      0.074     -0.175       0.245       0.036
+52414.00      0.053     -0.156       0.117       0.109
+52415.00      0.024     -0.167      -0.014       0.036
+52416.00      0.006     -0.198      -0.115      -0.234
+52417.00     -0.002     -0.221      -0.076      -0.357
+52418.00     -0.006     -0.221       0.028      -0.313
+52419.00     -0.012     -0.213       0.175      -0.032
+52420.00     -0.018     -0.215       0.258       0.137
+52421.00     -0.021     -0.223       0.260       0.115
+52422.00     -0.017     -0.226       0.245      -0.004
+52423.00     -0.004     -0.230       0.194       0.004
+52424.00      0.014     -0.242       0.156      -0.087
+52425.00      0.031     -0.245       0.111      -0.227
+52426.00      0.045     -0.227       0.225      -0.307
+52427.00      0.052     -0.204       0.312      -0.270
+52428.00      0.045     -0.193       0.307      -0.215
+52429.00      0.022     -0.192       0.262      -0.046
+52430.00     -0.005     -0.189       0.130       0.046
+52431.00     -0.018     -0.192       0.046      -0.079
+52432.00     -0.014     -0.209       0.001      -0.321
+52433.00      0.044     -0.249       0.045      -0.514
+52434.00      0.040     -0.257       0.140      -0.480
+52435.00      0.038     -0.259       0.162      -0.306
+52436.00      0.042     -0.268       0.143      -0.162
+52437.00      0.048     -0.282       0.131      -0.162
+52438.00      0.052     -0.289       0.122      -0.182
+52439.00      0.058     -0.285       0.179      -0.218
+52440.00      0.085     -0.286       0.276      -0.197
+52441.00      0.088     -0.254       0.325      -0.151
+52442.00      0.076     -0.216       0.238      -0.175
+52443.00      0.054     -0.204       0.123      -0.233
+52444.00      0.036     -0.227       0.000      -0.278
+52445.00      0.025     -0.259      -0.008      -0.289
+52446.00      0.015     -0.273       0.040      -0.177
+52447.00      0.029     -0.297       0.100      -0.060
+52448.00      0.027     -0.311       0.182      -0.014
+52449.00      0.038     -0.328       0.228      -0.077
+52450.00      0.056     -0.324       0.268      -0.207
+52451.00      0.073     -0.308       0.287      -0.266
+52452.00      0.084     -0.298       0.319      -0.392
+52453.00      0.091     -0.294       0.289      -0.376
+52454.00      0.074     -0.318       0.262      -0.455
+52455.00      0.070     -0.304       0.172      -0.380
+52456.00      0.051     -0.301       0.100      -0.191
+52457.00      0.022     -0.306       0.071      -0.116
+52458.00      0.001     -0.300       0.082      -0.235
+52459.00     -0.006     -0.294       0.159      -0.474
+52460.00     -0.007     -0.312       0.168      -0.740
+52461.00     -0.010     -0.348       0.250      -0.877
+52462.00     -0.016     -0.370       0.267      -0.796
+52463.00     -0.018     -0.369       0.170      -0.537
+52464.00     -0.012     -0.363       0.067      -0.233
+52465.00      0.003     -0.361       0.063      -0.022
+52466.00      0.020     -0.349       0.146      -0.074
+52467.00      0.028     -0.331       0.192      -0.158
+52468.00     -0.006     -0.317       0.242      -0.370
+52469.00     -0.021     -0.311       0.195      -0.419
+52470.00     -0.033     -0.291       0.147      -0.304
+52471.00     -0.041     -0.262       0.091      -0.200
+52472.00     -0.051     -0.246       0.062      -0.269
+52473.00     -0.065     -0.252       0.068      -0.293
+52474.00     -0.078     -0.272       0.109      -0.416
+52475.00     -0.080     -0.345       0.150      -0.396
+52476.00     -0.071     -0.371       0.129      -0.408
+52477.00     -0.053     -0.380       0.049      -0.330
+52478.00     -0.030     -0.364       0.019      -0.160
+52479.00     -0.010     -0.335       0.028      -0.058
+52480.00      0.000     -0.316       0.165      -0.036
+52481.00      0.001     -0.310       0.319      -0.166
+52482.00     -0.013     -0.302       0.477      -0.306
+52483.00     -0.023     -0.296       0.581      -0.335
+52484.00     -0.046     -0.305       0.549      -0.286
+52485.00     -0.071     -0.316       0.375      -0.214
+52486.00     -0.085     -0.304       0.076      -0.308
+52487.00     -0.089     -0.284      -0.125      -0.369
+52488.00     -0.095     -0.297      -0.177      -0.490
+52489.00     -0.102     -0.345       0.031      -0.569
+52490.00     -0.098     -0.378       0.214      -0.507
+52491.00     -0.080     -0.363       0.338      -0.379
+52492.00     -0.062     -0.325       0.363      -0.254
+52493.00     -0.076     -0.314       0.359      -0.085
+52494.00     -0.070     -0.295       0.330       0.054
+52495.00     -0.073     -0.284       0.274      -0.086
+52496.00     -0.088     -0.288       0.101      -0.216
+52497.00     -0.110     -0.302      -0.012      -0.341
+52498.00     -0.129     -0.307      -0.067      -0.269
+52499.00     -0.137     -0.299      -0.103      -0.114
+52500.00     -0.139     -0.286      -0.114      -0.046
+52501.00     -0.145     -0.274      -0.147      -0.105
+52502.00     -0.152     -0.272      -0.073      -0.313
+52503.00     -0.171     -0.276       0.031      -0.395
+52504.00     -0.159     -0.296       0.108      -0.425
+52505.00     -0.141     -0.302       0.175      -0.378
+52506.00     -0.123     -0.282       0.121      -0.239
+52507.00     -0.094     -0.278       0.026      -0.175
+52508.00     -0.084     -0.264      -0.047      -0.034
+52509.00     -0.088     -0.257      -0.106      -0.211
+52510.00     -0.092     -0.238      -0.043      -0.332
+52511.00     -0.103     -0.228       0.040      -0.502
+52512.00     -0.119     -0.238       0.062      -0.398
+52513.00     -0.135     -0.259       0.063      -0.280
+52514.00     -0.142     -0.261      -0.059      -0.172
+52515.00     -0.165     -0.234      -0.119      -0.186
+52516.00     -0.175     -0.233      -0.052      -0.215
+52517.00     -0.179     -0.264       0.183      -0.100
+52518.00     -0.164     -0.283       0.348       0.052
+52519.00     -0.134     -0.256       0.370      -0.016
+52520.00     -0.111     -0.208       0.220      -0.034
+52521.00     -0.105     -0.177       0.055      -0.114
+52522.00     -0.115     -0.174      -0.044      -0.131
+52523.00     -0.132     -0.191      -0.163      -0.252
+52524.00     -0.146     -0.215      -0.248      -0.529
+52525.00     -0.154     -0.223      -0.193      -0.638
+52526.00     -0.168     -0.210      -0.084      -0.458
+52527.00     -0.189     -0.207      -0.010      -0.183
+52528.00     -0.200     -0.245      -0.044      -0.089
+52529.00     -0.188     -0.262      -0.073      -0.188
+52530.00     -0.170     -0.246      -0.066      -0.273
+52531.00     -0.159     -0.223      -0.009      -0.232
+52532.00     -0.153     -0.221       0.021      -0.149
+52533.00     -0.146     -0.227       0.118      -0.119
+52534.00     -0.138     -0.219       0.157      -0.020
+52535.00     -0.130     -0.204       0.090      -0.009
+52536.00     -0.126     -0.200       0.048       0.049
+52537.00     -0.134     -0.198       0.003      -0.058
+52538.00     -0.112     -0.167       0.003      -0.210
+52539.00     -0.123     -0.150       0.058      -0.420
+52540.00     -0.128     -0.152       0.055      -0.320
+52541.00     -0.132     -0.175       0.057      -0.236
+52542.00     -0.136     -0.190       0.045      -0.113
+52543.00     -0.141     -0.185       0.045      -0.119
+52544.00     -0.151     -0.180       0.126      -0.177
+52545.00     -0.153     -0.191       0.263      -0.035
+52546.00     -0.136     -0.191       0.344       0.088
+52547.00     -0.111     -0.165       0.276       0.103
+52548.00     -0.098     -0.138       0.051      -0.021
+52549.00     -0.074     -0.104      -0.057      -0.025
+52550.00     -0.080     -0.119      -0.070       0.109
+52551.00     -0.094     -0.143      -0.031       0.043
+52552.00     -0.129     -0.172       0.053      -0.226
+52553.00     -0.131     -0.169       0.124      -0.502
+52554.00     -0.140     -0.121       0.132      -0.456
+52555.00     -0.170     -0.082      -0.013      -0.215
+52556.00     -0.199     -0.104      -0.153      -0.033
+52557.00     -0.189     -0.150      -0.251      -0.019
+52558.00     -0.151     -0.153      -0.197      -0.042
+52559.00     -0.121     -0.116      -0.093       0.101
+52560.00     -0.112     -0.095      -0.015       0.335
+52561.00     -0.109     -0.098       0.014       0.295
+52562.00     -0.106     -0.095       0.059       0.056
+52563.00     -0.144     -0.097      -0.029      -0.119
+52564.00     -0.146     -0.093      -0.114      -0.043
+52565.00     -0.156     -0.096      -0.180       0.006
+52566.00     -0.171     -0.094      -0.201      -0.045
+52567.00     -0.179     -0.084      -0.206      -0.110
+52568.00     -0.176     -0.081      -0.184      -0.225
+52569.00     -0.174     -0.091      -0.171      -0.145
+52570.00     -0.177     -0.104      -0.186       0.019
+52571.00     -0.181     -0.112      -0.175       0.053
+52572.00     -0.183     -0.122      -0.170      -0.044
+52573.00     -0.229     -0.146      -0.101      -0.021
+52574.00     -0.206     -0.139      -0.019       0.083
+52575.00     -0.179     -0.119      -0.027       0.050
+52576.00     -0.165     -0.107      -0.045       0.042
+52577.00     -0.166     -0.115      -0.075      -0.044
+52578.00     -0.169     -0.128      -0.039      -0.005
+52579.00     -0.176     -0.140      -0.029      -0.058
+52580.00     -0.206     -0.154      -0.002      -0.214
+52581.00     -0.217     -0.157       0.013      -0.227
+52582.00     -0.224     -0.120       0.029      -0.160
+52583.00     -0.241     -0.069      -0.096       0.131
+52584.00     -0.261     -0.062      -0.207       0.311
+52585.00     -0.259     -0.097      -0.206       0.116
+52586.00     -0.229     -0.118      -0.073      -0.048
+52587.00     -0.195     -0.102       0.086       0.073
+52588.00     -0.171     -0.084       0.133       0.338
+52589.00     -0.154     -0.077       0.135       0.321
+52590.00     -0.140     -0.066       0.076       0.123
+52591.00     -0.169     -0.052       0.008      -0.163
+52592.00     -0.174     -0.042      -0.164      -0.244
+52593.00     -0.187     -0.046      -0.249      -0.137
+52594.00     -0.200     -0.051      -0.265      -0.076
+52595.00     -0.205     -0.050      -0.195       0.007
+52596.00     -0.202     -0.050      -0.102      -0.064
+52597.00     -0.202     -0.052      -0.009      -0.062
+52598.00     -0.208     -0.061       0.068      -0.007
+52599.00     -0.207     -0.080       0.149      -0.023
+52600.00     -0.197     -0.100       0.147      -0.104
+52601.00     -0.173     -0.100       0.093      -0.155
+52602.00     -0.153     -0.087      -0.019      -0.144
+52603.00     -0.129     -0.062      -0.089      -0.060
+52604.00     -0.114     -0.047      -0.169      -0.015
+52605.00     -0.121     -0.049      -0.264      -0.111
+52606.00     -0.142     -0.059      -0.378      -0.052
+52607.00     -0.160     -0.074      -0.410      -0.057
+52608.00     -0.225     -0.089      -0.323      -0.004
+52609.00     -0.232     -0.102      -0.183       0.032
+52610.00     -0.233     -0.090      -0.131      -0.329
+52611.00     -0.228     -0.062      -0.111      -0.055
+52612.00     -0.221     -0.051      -0.091       0.134
+52613.00     -0.211     -0.065      -0.066       0.046
+52614.00     -0.197     -0.079       0.022      -0.053
+52615.00     -0.181     -0.077       0.074       0.029
+52616.00     -0.163     -0.067       0.025       0.129
+52617.00     -0.142     -0.052      -0.051       0.208
+52618.00     -0.125     -0.029      -0.112       0.051
+52619.00     -0.111     -0.011      -0.125      -0.108
+52620.00     -0.122     -0.011      -0.084      -0.178
+52621.00     -0.140     -0.023      -0.044      -0.038
+52622.00     -0.156     -0.024      -0.047       0.081
+52623.00     -0.162     -0.016      -0.029       0.090
+52624.00     -0.163     -0.014      -0.075       0.073
+52625.00     -0.169     -0.024      -0.117      -0.100
+52626.00     -0.185     -0.034      -0.090      -0.054
+52627.00     -0.179     -0.063      -0.064       0.021
+52628.00     -0.157     -0.085      -0.065      -0.043
+52629.00     -0.149     -0.038      -0.082      -0.150
+52630.00     -0.137     -0.009      -0.025      -0.277
+52631.00     -0.131      0.027       0.056      -0.429
+52632.00     -0.131      0.046       0.140      -0.339
+52633.00     -0.147      0.043       0.210      -0.225
+52634.00     -0.178      0.029       0.229      -0.057
+52635.00     -0.202      0.004       0.158       0.014
+52636.00     -0.148     -0.023       0.070       0.017
+52637.00     -0.138     -0.033       0.018       0.054
+52638.00     -0.128     -0.015      -0.003       0.112
+52639.00     -0.121      0.009      -0.071       0.177
+52640.00     -0.115      0.010      -0.083       0.154
+52641.00     -0.108     -0.008      -0.076       0.090
+52642.00     -0.101     -0.021      -0.001       0.035
+52643.00     -0.093     -0.021       0.073       0.055
+52644.00     -0.083     -0.012       0.061       0.113
+52645.00     -0.074      0.006       0.062       0.085
+52646.00     -0.071      0.035       0.035       0.012
+52647.00     -0.079      0.053      -0.005      -0.130
+52648.00     -0.097      0.043      -0.003      -0.124
+52649.00     -0.119      0.020      -0.003      -0.056
+52650.00     -0.126      0.051       0.002       0.083
+52651.00     -0.135      0.068      -0.031       0.130
+52652.00     -0.140      0.076      -0.037       0.047
+52653.00     -0.144      0.057      -0.069      -0.016
+52654.00     -0.143      0.026      -0.074      -0.162
+52655.00     -0.128     -0.001      -0.076      -0.139
+52656.00     -0.106     -0.017      -0.043      -0.004
+52657.00     -0.084     -0.012      -0.054      -0.015
+52658.00     -0.076      0.018      -0.065      -0.025
+52659.00     -0.083      0.051      -0.101       0.037
+52660.00     -0.099      0.060      -0.089       0.003
+52661.00     -0.121      0.045      -0.022       0.058
+52662.00     -0.144      0.025       0.022       0.081
+52663.00     -0.160      0.009       0.052       0.088
+52664.00     -0.149      0.009       0.072       0.025
+52665.00     -0.140      0.006       0.009       0.018
+52666.00     -0.130      0.025      -0.014       0.030
+52667.00     -0.124      0.047      -0.067      -0.001
+52668.00     -0.125      0.043      -0.081      -0.068
+52669.00     -0.126     -0.002      -0.070      -0.065
+52670.00     -0.119     -0.026      -0.058      -0.095
+52671.00     -0.103     -0.034      -0.004       0.004
+52672.00     -0.084     -0.035       0.010       0.153
+52673.00     -0.073     -0.026      -0.001       0.191
+52674.00     -0.075      0.001      -0.003       0.138
+52675.00     -0.087      0.031      -0.017       0.078
+52676.00     -0.107      0.038      -0.002       0.073
+52677.00     -0.126      0.022       0.046       0.098
+52678.00     -0.138      0.015       0.093       0.088
+52679.00     -0.146      0.024       0.074       0.131
+52680.00     -0.150      0.026       0.067       0.094
+52681.00     -0.145      0.010       0.045      -0.044
+52682.00     -0.115      0.005      -0.019      -0.061
+52683.00     -0.090      0.004      -0.058      -0.190
+52684.00     -0.070      0.005      -0.060      -0.085
+52685.00     -0.058      0.005      -0.004       0.077
+52686.00     -0.055      0.019       0.047       0.285
+52687.00     -0.061      0.038       0.001       0.368
+52688.00     -0.077      0.035      -0.078       0.211
+52689.00     -0.080     -0.001      -0.089       0.127
+52690.00     -0.091     -0.016      -0.044      -0.053
+52691.00     -0.096     -0.016       0.032      -0.004
+52692.00     -0.099     -0.016       0.018       0.000
+52693.00     -0.105     -0.022      -0.043       0.081
+52694.00     -0.108     -0.023      -0.079      -0.009
+52695.00     -0.102     -0.017      -0.021       0.021
+52696.00     -0.088     -0.018       0.038       0.030
+52697.00     -0.073     -0.029       0.123       0.044
+52698.00     -0.056     -0.039       0.147       0.047
+52699.00     -0.028     -0.015       0.174       0.256
+52700.00     -0.006     -0.021       0.121       0.249
+52701.00      0.004     -0.020       0.073       0.171
+52702.00     -0.000      0.001       0.020       0.103
+52703.00     -0.020      0.033      -0.051      -0.001
+52704.00     -0.042      0.059      -0.092       0.035
+52705.00     -0.061      0.060      -0.109       0.149
+52706.00     -0.069      0.053      -0.072       0.146
+52707.00     -0.071      0.045      -0.071       0.124
+52708.00     -0.070      0.030       0.013       0.060
+52709.00     -0.056      0.012       0.037       0.176
+52710.00     -0.025      0.018       0.048       0.189
+52711.00      0.001      0.043      -0.047       0.130
+52712.00      0.007      0.049      -0.039       0.065
+52713.00      0.002      0.030       0.118       0.201
+52714.00     -0.002      0.027       0.246       0.366
+52715.00     -0.010      0.045       0.242       0.313
+52716.00     -0.025      0.043       0.075       0.044
+52717.00     -0.033      0.013      -0.011      -0.298
+52718.00     -0.032     -0.006       0.003      -0.296
+52719.00     -0.033     -0.000       0.081      -0.132
+52720.00     -0.049     -0.004       0.044       0.058
+52721.00     -0.055     -0.021      -0.014       0.117
+52722.00     -0.054     -0.036      -0.046       0.107
+52723.00     -0.043     -0.037       0.028       0.032
+52724.00     -0.022     -0.034       0.127      -0.099
+52725.00      0.004     -0.029       0.162      -0.116
+52726.00      0.028     -0.016       0.141      -0.021
+52727.00      0.046     -0.002       0.021       0.155
+52728.00      0.057      0.008      -0.045       0.216
+52729.00      0.054      0.015      -0.033       0.194
+52730.00      0.041      0.033       0.056      -0.020
+52731.00      0.026      0.075       0.167      -0.116
+52732.00      0.003      0.086       0.236      -0.161
+52733.00     -0.013      0.080       0.271      -0.030
+52734.00     -0.014      0.071       0.212       0.012
+52735.00     -0.002      0.063       0.140       0.005
+52736.00      0.011      0.046       0.124       0.020
+52737.00      0.031      0.027       0.128       0.186
+52738.00      0.063      0.034       0.172       0.292
+52739.00      0.085      0.060       0.106       0.227
+52740.00      0.081      0.052       0.117      -0.016
+52741.00      0.067      0.010       0.243      -0.002
+52742.00      0.060     -0.005       0.385       0.099
+52743.00      0.050      0.030       0.327       0.282
+52744.00      0.032      0.049       0.140       0.172
+52745.00     -0.047      0.063       0.017      -0.077
+52746.00     -0.030      0.025       0.063      -0.052
+52747.00     -0.022      0.024       0.219       0.100
+52748.00     -0.027      0.066       0.282       0.272
+52749.00     -0.035      0.054       0.275       0.186
+52750.00     -0.021      0.039       0.198       0.007
+52751.00      0.002      0.036       0.164       0.018
+52752.00      0.022      0.035       0.174       0.017
+52753.00      0.040      0.036       0.196       0.058
+52754.00      0.055      0.049       0.151       0.151
+52755.00      0.063      0.075       0.118       0.199
+52756.00      0.060      0.093       0.074       0.221
+52757.00      0.044      0.109       0.062       0.258
+52758.00      0.022      0.124       0.097       0.137
+52759.00      0.004      0.128       0.033       0.042
+52760.00     -0.007      0.113      -0.041       0.138
+52761.00     -0.010      0.090      -0.099       0.278
+52762.00      0.007      0.072      -0.105       0.345
+52763.00      0.027      0.079      -0.050       0.188
+52764.00      0.047      0.079       0.055       0.038
+52765.00      0.060      0.065       0.152       0.083
+52766.00      0.076      0.058       0.247       0.102
+52767.00      0.091      0.065       0.204       0.104
+52768.00      0.091      0.054       0.067       0.017
+52769.00      0.113      0.034       0.062      -0.089
+52770.00      0.103      0.012       0.140       0.061
+52771.00      0.089      0.038       0.163       0.163
+52772.00      0.066      0.066       0.173       0.063
+52773.00      0.056      0.044       0.106      -0.169
+52774.00      0.074      0.004       0.176      -0.191
+52775.00      0.090     -0.005       0.260      -0.018
+52776.00      0.077      0.031       0.268       0.206
+52777.00      0.060      0.030       0.270       0.129
+52778.00      0.066      0.019       0.221       0.118
+52779.00      0.091      0.007       0.195       0.142
+52780.00      0.114     -0.007       0.191       0.234
+52781.00      0.128     -0.016       0.233       0.156
+52782.00      0.134     -0.003       0.210      -0.004
+52783.00      0.132      0.022       0.137      -0.036
+52784.00      0.119      0.036       0.040       0.011
+52785.00      0.094      0.041      -0.070       0.113
+52786.00      0.067      0.044      -0.009       0.110
+52787.00      0.070      0.048       0.063      -0.018
+52788.00      0.074      0.030       0.138      -0.117
+52789.00      0.083      0.011       0.138       0.027
+52790.00      0.091      0.010       0.171       0.125
+52791.00      0.103      0.022       0.201       0.264
+52792.00      0.116      0.026       0.254       0.197
+52793.00      0.118      0.011       0.301       0.140
+52794.00      0.115     -0.008       0.324       0.003
+52795.00      0.121     -0.011       0.314      -0.103
+52796.00      0.130     -0.003       0.192      -0.117
+52797.00      0.136      0.035       0.103      -0.047
+52798.00      0.121      0.022       0.008       0.005
+52799.00      0.100      0.011      -0.028       0.175
+52800.00      0.080     -0.001       0.067       0.107
+52801.00      0.061     -0.044       0.239      -0.010
+52802.00      0.069     -0.062       0.445      -0.180
+52803.00      0.085     -0.065       0.556      -0.135
+52804.00      0.091     -0.063       0.491      -0.209
+52805.00      0.089     -0.069       0.360      -0.308
+52806.00      0.094     -0.079       0.204      -0.353
+52807.00      0.109     -0.092       0.051      -0.263
+52808.00      0.155     -0.113       0.048      -0.078
+52809.00      0.167     -0.120       0.134       0.059
+52810.00      0.169     -0.099       0.289      -0.096
+52811.00      0.161     -0.068       0.450      -0.241
+52812.00      0.142     -0.059       0.425      -0.270
+52813.00      0.116     -0.075       0.372      -0.177
+52814.00      0.092     -0.089       0.281      -0.064
+52815.00      0.083     -0.093       0.238      -0.147
+52816.00      0.092     -0.097       0.183      -0.109
+52817.00      0.103     -0.102       0.156       0.036
+52818.00      0.121     -0.081       0.124       0.195
+52819.00      0.125     -0.079       0.183       0.255
+52820.00      0.137     -0.087       0.225       0.084
+52821.00      0.144     -0.104       0.229      -0.163
+52822.00      0.154     -0.143       0.264      -0.213
+52823.00      0.150     -0.137       0.242      -0.149
+52824.00      0.154     -0.116       0.227       0.108
+52825.00      0.154     -0.091       0.187       0.242
+52826.00      0.139     -0.081       0.101       0.247
+52827.00      0.118     -0.103       0.074       0.108
+52828.00      0.106     -0.145       0.204      -0.182
+52829.00      0.104     -0.179       0.352      -0.278
+52830.00      0.106     -0.182       0.464      -0.283
+52831.00      0.111     -0.173       0.480      -0.142
+52832.00      0.142     -0.238       0.439       0.004
+52833.00      0.163     -0.251       0.414       0.086
+52834.00      0.181     -0.253       0.381       0.073
+52835.00      0.188     -0.246       0.314      -0.060
+52836.00      0.187     -0.247       0.192      -0.188
+52837.00      0.181     -0.250       0.106      -0.268
+52838.00      0.173     -0.239       0.040      -0.242
+52839.00      0.160     -0.248       0.026      -0.374
+52840.00      0.141     -0.246       0.026      -0.309
+52841.00      0.120     -0.263      -0.031      -0.241
+52842.00      0.104     -0.275       0.008      -0.173
+52843.00      0.100     -0.270       0.099      -0.247
+52844.00      0.103     -0.266       0.244      -0.269
+52845.00      0.105     -0.273       0.337      -0.119
+52846.00      0.143     -0.308       0.355       0.091
+52847.00      0.153     -0.305       0.285       0.192
+52848.00      0.175     -0.302       0.207       0.108
+52849.00      0.194     -0.299       0.108      -0.194
+52850.00      0.214     -0.298       0.107      -0.385
+52851.00      0.195     -0.288       0.183      -0.496
+52852.00      0.174     -0.284       0.281      -0.367
+52853.00      0.158     -0.282       0.302      -0.181
+52854.00      0.148     -0.274       0.261      -0.068
+52855.00      0.141     -0.270       0.197      -0.389
+52856.00      0.135     -0.282       0.204      -0.182
+52857.00      0.131     -0.303       0.240      -0.227
+52858.00      0.127     -0.311       0.265      -0.253
+52859.00      0.129     -0.308       0.229      -0.285
+52860.00      0.146     -0.329       0.187      -0.202
+52861.00      0.172     -0.333       0.197      -0.241
+52862.00      0.196     -0.318       0.222      -0.288
+52863.00      0.204     -0.289       0.307      -0.237
+52864.00      0.195     -0.274       0.365      -0.338
+52865.00      0.178     -0.279       0.373      -0.342
+52866.00      0.161     -0.285       0.362      -0.317
+52867.00      0.145     -0.290       0.373      -0.254
+52868.00      0.131     -0.296       0.359      -0.177
+52869.00      0.118     -0.305       0.297      -0.130
+52870.00      0.113     -0.297       0.172      -0.115
+52871.00      0.116     -0.276       0.106      -0.255
+52872.00      0.115     -0.270       0.107      -0.462
+52873.00      0.112     -0.289       0.122      -0.432
+52874.00      0.082     -0.288       0.171      -0.277
+52875.00      0.102     -0.273       0.100      -0.127
+52876.00      0.130     -0.240       0.061      -0.024
+52877.00      0.149     -0.219       0.037      -0.115
+52878.00      0.145     -0.213       0.063      -0.224
+52879.00      0.122     -0.206       0.132      -0.315
+52880.00      0.087     -0.230       0.195      -0.276
+52881.00      0.058     -0.262       0.261      -0.182
+52882.00      0.050     -0.278       0.249       0.073
+52883.00      0.058     -0.264       0.253       0.154
+52884.00      0.064     -0.245       0.236       0.192
+52885.00      0.057     -0.240       0.218       0.039
+52886.00      0.048     -0.250       0.201      -0.102
+52887.00      0.052     -0.261       0.227      -0.271
+52888.00      0.109     -0.252       0.257      -0.265
+52889.00      0.129     -0.241       0.250      -0.309
+52890.00      0.140     -0.214       0.233      -0.244
+52891.00      0.141     -0.185       0.198      -0.140
+52892.00      0.132     -0.174       0.160      -0.168
+52893.00      0.114     -0.183       0.112      -0.223
+52894.00      0.094     -0.193       0.050      -0.286
+52895.00      0.052     -0.233       0.024      -0.204
+52896.00      0.042     -0.243       0.062      -0.032
+52897.00      0.034     -0.256       0.035       0.025
+52898.00      0.036     -0.249       0.027      -0.070
+52899.00      0.042     -0.220      -0.009      -0.234
+52900.00      0.042     -0.207       0.067      -0.376
+52901.00      0.040     -0.229       0.203      -0.329
+52902.00      0.036     -0.244       0.308      -0.257
+52903.00      0.055     -0.226       0.298      -0.183
+52904.00      0.073     -0.190       0.253      -0.161
+52905.00      0.077     -0.181       0.218      -0.242
+52906.00      0.069     -0.200       0.224      -0.358
+52907.00      0.054     -0.226       0.244      -0.367
+52908.00      0.033     -0.243       0.183      -0.393
+52909.00      0.009     -0.249       0.121      -0.336
+52910.00     -0.009     -0.245       0.058      -0.113
+52911.00     -0.010     -0.242       0.014      -0.018
+52912.00      0.004     -0.248      -0.018      -0.132
+52913.00      0.022     -0.252      -0.058      -0.316
+52914.00      0.032     -0.242      -0.014      -0.379
+52915.00      0.038     -0.228       0.056      -0.409
+52916.00      0.051     -0.222       0.166      -0.250
+52917.00      0.065     -0.212       0.221      -0.086
+52918.00      0.067     -0.193       0.240      -0.059
+52919.00      0.060     -0.178       0.195       0.092
+52920.00      0.038     -0.221       0.087       0.012
+52921.00      0.021     -0.234       0.067      -0.204
+52922.00      0.003     -0.236       0.141      -0.328
+52923.00     -0.012     -0.210       0.213      -0.370
+52924.00     -0.016     -0.217       0.209      -0.255
+52925.00     -0.016     -0.241       0.157      -0.108
+52926.00     -0.012     -0.257       0.078      -0.115
+52927.00     -0.006     -0.243       0.026      -0.225
+52928.00     -0.003     -0.226       0.042      -0.346
+52929.00      0.002     -0.230       0.150      -0.320
+52930.00      0.006     -0.249       0.224      -0.142
+52931.00      0.023     -0.239       0.218      -0.142
+52932.00      0.029     -0.229       0.152      -0.202
+52933.00      0.026     -0.245       0.135      -0.158
+52934.00      0.022     -0.273       0.152      -0.124
+52935.00      0.017     -0.287       0.181      -0.076
+52936.00      0.006     -0.279       0.100      -0.159
+52937.00     -0.030     -0.257       0.063      -0.238
+52938.00     -0.053     -0.218       0.051      -0.101
+52939.00     -0.076     -0.208      -0.001      -0.010
+52940.00     -0.073     -0.250      -0.038      -0.126
+52941.00     -0.031     -0.296      -0.038      -0.425
+52942.00      0.020     -0.282       0.088      -0.624
+52943.00      0.044     -0.224       0.169      -0.584
+52944.00      0.030     -0.193       0.215      -0.382
+52945.00      0.032     -0.188       0.120      -0.148
+52946.00      0.035     -0.184       0.088      -0.196
+52947.00      0.028     -0.175       0.072      -0.202
+52948.00      0.012     -0.177       0.056      -0.312
+52949.00     -0.006     -0.193       0.042      -0.464
+52950.00     -0.021     -0.197       0.038      -0.327
+52951.00     -0.059     -0.181       0.011      -0.158
+52952.00     -0.055     -0.184      -0.037      -0.146
+52953.00     -0.048     -0.207      -0.087      -0.069
+52954.00     -0.042     -0.233      -0.091      -0.127
+52955.00     -0.036     -0.236      -0.053      -0.221
+52956.00     -0.028     -0.222       0.028      -0.279
+52957.00     -0.016     -0.208       0.138      -0.116
+52958.00     -0.003     -0.176       0.155      -0.088
+52959.00      0.011     -0.166       0.099      -0.012
+52960.00      0.009     -0.175       0.038      -0.168
+52961.00     -0.001     -0.204       0.043      -0.179
+52962.00      0.011     -0.222       0.091      -0.153
+52963.00     -0.002     -0.207       0.141      -0.030
+52964.00     -0.026     -0.190       0.148      -0.160
+52965.00     -0.046     -0.188       0.175      -0.415
+52966.00     -0.051     -0.175       0.182      -0.444
+52967.00     -0.058     -0.160       0.111      -0.279
+52968.00     -0.064     -0.185       0.045      -0.209
+52969.00     -0.042     -0.240       0.045      -0.221
+52970.00      0.005     -0.253       0.187      -0.261
+52971.00      0.038     -0.205       0.357       0.048
+52972.00      0.031     -0.160       0.355       0.291
+52973.00      0.029     -0.154       0.256       0.341
+52974.00      0.032     -0.155       0.158       0.188
+52975.00      0.027     -0.139       0.055      -0.095
+52976.00      0.009     -0.128      -0.005      -0.114
+52977.00     -0.011     -0.136       0.012      -0.113
+52978.00     -0.022     -0.147       0.022      -0.201
+52979.00     -0.023     -0.146      -0.001      -0.252
+52980.00     -0.015     -0.154      -0.021      -0.352
+52981.00     -0.009     -0.170      -0.042      -0.194
+52982.00     -0.006     -0.183      -0.035      -0.129
+52983.00      0.000     -0.186       0.058      -0.100
+52984.00      0.013     -0.177       0.157      -0.194
+52985.00      0.027     -0.159       0.245      -0.207
+52986.00      0.040     -0.137       0.302      -0.205
+52987.00      0.045     -0.120       0.273      -0.146
+52988.00      0.031     -0.128       0.230      -0.155
+52989.00      0.007     -0.159       0.129      -0.154
+52990.00      0.010     -0.188       0.055      -0.292
+52991.00     -0.005     -0.171       0.024      -0.167
+52992.00     -0.028     -0.157       0.044      -0.200
+52993.00     -0.047     -0.177       0.115      -0.290
+52994.00     -0.036     -0.203       0.237      -0.276
+52995.00     -0.007     -0.198       0.308      -0.191
+52996.00      0.008     -0.186       0.196      -0.074
+52997.00      0.023     -0.175       0.090      -0.220
+52998.00      0.026     -0.195       0.022      -0.419
+52999.00      0.033     -0.188      -0.065      -0.395
+53000.00      0.032     -0.167      -0.102      -0.160
+53001.00      0.036     -0.159      -0.154       0.025
+53002.00      0.040     -0.148      -0.093      -0.067
+53003.00      0.032     -0.126      -0.042      -0.162
+53004.00      0.011     -0.112       0.034      -0.016
+53005.00     -0.011     -0.116       0.022       0.140
+53006.00     -0.022     -0.125       0.045       0.122
+53007.00     -0.034     -0.157       0.057      -0.047
+53008.00     -0.025     -0.169       0.020      -0.174
+53009.00     -0.019     -0.181       0.017      -0.271
+53010.00     -0.016     -0.187       0.057      -0.108
+53011.00     -0.005     -0.181       0.162      -0.037
+53012.00      0.014     -0.169       0.226      -0.029
+53013.00      0.027     -0.152       0.214      -0.112
+53014.00      0.024     -0.128       0.132      -0.278
+53015.00      0.012     -0.112       0.086      -0.389
+53016.00     -0.009     -0.118       0.059      -0.377
+53017.00     -0.037     -0.149       0.038      -0.183
+53018.00     -0.032     -0.185       0.050       0.167
+53019.00     -0.039     -0.191      -0.014       0.148
+53020.00     -0.034     -0.180      -0.097      -0.041
+53021.00     -0.032     -0.173      -0.075      -0.309
+53022.00     -0.031     -0.174      -0.027      -0.305
+53023.00     -0.021     -0.171       0.041      -0.194
+53024.00     -0.007     -0.170       0.063      -0.033
+53025.00     -0.007     -0.227       0.090      -0.093
+53026.00     -0.010     -0.239       0.058      -0.162
+53027.00     -0.015     -0.240       0.047      -0.226
+53028.00     -0.022     -0.247      -0.064      -0.189
+53029.00     -0.017     -0.228      -0.129      -0.072
+53030.00     -0.016     -0.202      -0.139      -0.097
+53031.00     -0.027     -0.179      -0.090      -0.099
+53032.00     -0.049     -0.178       0.006      -0.114
+53033.00     -0.070     -0.192       0.043      -0.070
+53034.00     -0.081     -0.193       0.080      -0.127
+53035.00     -0.082     -0.207       0.072      -0.275
+53036.00     -0.072     -0.205       0.072      -0.334
+53037.00     -0.059     -0.217       0.054      -0.386
+53038.00     -0.048     -0.226       0.048      -0.230
+53039.00     -0.030     -0.219       0.105      -0.104
+53040.00     -0.010     -0.205       0.129      -0.039
+53041.00     -0.000     -0.187       0.101      -0.232
+53042.00     -0.011     -0.166       0.033      -0.368
+53043.00     -0.036     -0.157      -0.016      -0.335
+53044.00     -0.057     -0.171      -0.083      -0.212
+53045.00     -0.075     -0.202      -0.049      -0.062
+53046.00     -0.089     -0.230      -0.054       0.039
+53047.00     -0.089     -0.239      -0.029       0.068
+53048.00     -0.071     -0.225       0.007      -0.027
+53049.00     -0.075     -0.189       0.015      -0.066
+53050.00     -0.074     -0.155       0.040      -0.092
+53051.00     -0.086     -0.141      -0.005      -0.020
+53052.00     -0.094     -0.164      -0.012      -0.134
+53053.00     -0.087     -0.204       0.015      -0.209
+53054.00     -0.072     -0.227       0.013      -0.256
+53055.00     -0.054     -0.251      -0.023      -0.234
+53056.00     -0.046     -0.222      -0.098      -0.231
+53057.00     -0.045     -0.190      -0.143      -0.166
+53058.00     -0.053     -0.158      -0.155      -0.119
+53059.00     -0.068     -0.140      -0.084      -0.117
+53060.00     -0.086     -0.149       0.027      -0.114
+53061.00     -0.101     -0.176       0.110      -0.160
+53062.00     -0.107     -0.185       0.188      -0.255
+53063.00     -0.141     -0.133       0.161      -0.324
+53064.00     -0.126     -0.121       0.125      -0.384
+53065.00     -0.105     -0.126       0.051      -0.231
+53066.00     -0.084     -0.131       0.007      -0.068
+53067.00     -0.064     -0.121       0.004      -0.004
+53068.00     -0.052     -0.108       0.039      -0.063
+53069.00     -0.055     -0.105       0.138      -0.180
+53070.00     -0.096     -0.083       0.165      -0.306
+53071.00     -0.119     -0.084       0.156      -0.252
+53072.00     -0.136     -0.103       0.087      -0.145
+53073.00     -0.144     -0.136       0.021      -0.050
+53074.00     -0.142     -0.158      -0.118      -0.085
+53075.00     -0.135     -0.155      -0.117      -0.104
+53076.00     -0.124     -0.140      -0.099      -0.148
+53077.00     -0.144     -0.102      -0.069      -0.213
+53078.00     -0.137     -0.084      -0.018      -0.164
+53079.00     -0.131     -0.071       0.058      -0.084
+53080.00     -0.122     -0.079       0.127       0.020
+53081.00     -0.109     -0.103       0.111       0.024
+53082.00     -0.094     -0.117       0.033      -0.001
+53083.00     -0.081     -0.105      -0.028      -0.091
+53084.00     -0.071     -0.072      -0.055      -0.165
+53085.00     -0.077     -0.048      -0.124      -0.185
+53086.00     -0.093     -0.026      -0.158      -0.161
+53087.00     -0.110     -0.011      -0.129      -0.115
+53088.00     -0.121     -0.016      -0.058       0.041
+53089.00     -0.125     -0.043       0.063       0.004
+53090.00     -0.118     -0.067       0.096      -0.111
+53091.00     -0.112     -0.066       0.129      -0.179
+53092.00     -0.090     -0.065       0.089      -0.291
+53093.00     -0.066     -0.067      -0.002      -0.186
+53094.00     -0.042     -0.055      -0.038      -0.015
+53095.00     -0.026     -0.028      -0.076       0.019
+53096.00     -0.028     -0.017      -0.004      -0.036
+53097.00     -0.043     -0.033       0.103      -0.071
+53098.00     -0.071      0.003       0.147      -0.104
+53099.00     -0.085     -0.001       0.080      -0.084
+53100.00     -0.099     -0.011      -0.136      -0.072
+53101.00     -0.104     -0.042      -0.218      -0.195
+53102.00     -0.093     -0.067      -0.092      -0.289
+53103.00     -0.072     -0.067       0.002      -0.079
+53104.00     -0.056     -0.058      -0.040       0.059
+53105.00     -0.049     -0.065      -0.084       0.196
+53106.00     -0.042     -0.076      -0.085       0.148
+53107.00     -0.024     -0.073      -0.039       0.089
+53108.00      0.004     -0.055       0.039       0.048
+53109.00      0.025     -0.032       0.049       0.009
+53110.00      0.030     -0.010       0.037       0.133
+53111.00      0.024      0.006       0.039       0.165
+53112.00      0.013      0.012       0.070       0.171
+53113.00     -0.003      0.012       0.094       0.201
+53114.00     -0.021      0.015       0.134       0.103
+53115.00     -0.035      0.025       0.141       0.219
+53116.00      0.003      0.068       0.146       0.243
+53117.00      0.006      0.050       0.208       0.316
+53118.00      0.023      0.024       0.230       0.186
+53119.00      0.078      0.009       0.233      -0.037
+53120.00      0.101     -0.003       0.212      -0.125
+53121.00      0.117     -0.011       0.189      -0.114
+53122.00      0.132      0.003       0.091       0.006
+53123.00      0.141      0.036       0.027       0.120
+53124.00      0.132      0.046       0.037       0.048
+53125.00      0.112      0.016       0.130       0.197
+53126.00      0.096     -0.010       0.276       0.350
+53127.00      0.081     -0.002       0.295       0.517
+53128.00      0.064      0.005       0.151       0.300
+53129.00      0.057     -0.020       0.061      -0.062
+53130.00      0.050     -0.067       0.100      -0.207
+53131.00      0.071     -0.070       0.183      -0.159
+53132.00      0.081     -0.064       0.132      -0.082
+53133.00      0.086     -0.078       0.000      -0.026
+53134.00      0.097     -0.103      -0.058      -0.103
+53135.00      0.114     -0.110      -0.038      -0.127
+53136.00      0.133     -0.096       0.112      -0.107
+53137.00      0.142     -0.085       0.219      -0.093
+53138.00      0.140     -0.045       0.237      -0.066
+53139.00      0.123     -0.016       0.174       0.022
+53140.00      0.100     -0.011       0.077       0.177
+53141.00      0.076     -0.021       0.039       0.107
+53142.00      0.056     -0.026       0.015       0.016
+53143.00      0.048     -0.023       0.029      -0.036
+53144.00      0.049     -0.027       0.036      -0.014
+53145.00      0.057     -0.042       0.027       0.005
+53146.00      0.075     -0.053      -0.006      -0.018
+53147.00      0.116     -0.041      -0.049      -0.045
+53148.00      0.133     -0.040      -0.066      -0.037
+53149.00      0.135     -0.047       0.022       0.029
+53150.00      0.134     -0.047       0.148       0.105
+53151.00      0.135     -0.027       0.255      -0.130
+53152.00      0.127     -0.020       0.287      -0.259
+53153.00      0.111     -0.050       0.259      -0.223
+53154.00      0.097     -0.025       0.255       0.120
+53155.00      0.077     -0.015       0.200       0.361
+53156.00      0.055     -0.002       0.052       0.306
+53157.00      0.052     -0.026      -0.027       0.128
+53158.00      0.075     -0.058       0.091       0.034
+53159.00      0.093     -0.058       0.191       0.069
+53160.00      0.083     -0.047       0.198       0.164
+53161.00      0.070     -0.065       0.009       0.129
+53162.00      0.082     -0.099      -0.150       0.168
+53163.00      0.109     -0.119      -0.195       0.178
+53164.00      0.127     -0.122      -0.070       0.085
+53165.00      0.131     -0.109       0.186      -0.237
+53166.00      0.097     -0.049       0.265      -0.317
+53167.00      0.080     -0.006       0.224      -0.142
+53168.00      0.054      0.010       0.189       0.092
+53169.00      0.028      0.001       0.221       0.223
+53170.00      0.011     -0.011       0.300       0.109
+53171.00      0.009     -0.016       0.297       0.034
+53172.00      0.023     -0.029       0.123       0.175
+53173.00      0.042     -0.045      -0.020       0.218
+53174.00      0.059     -0.049       0.033       0.109
+53175.00      0.093     -0.017       0.078       0.013
+53176.00      0.101     -0.002       0.155      -0.161
+53177.00      0.096     -0.006       0.237      -0.212
+53178.00      0.085     -0.016       0.313      -0.082
+53179.00      0.080     -0.010       0.271      -0.062
+53180.00      0.077     -0.003       0.191      -0.204
+53181.00      0.066     -0.020       0.060      -0.228
+53182.00      0.049     -0.053       0.024      -0.159
+53183.00      0.031     -0.069       0.009       0.112
+53184.00      0.015     -0.077       0.038       0.144
+53185.00      0.020     -0.098       0.093       0.014
+53186.00      0.036     -0.131       0.244      -0.120
+53187.00      0.062     -0.118       0.329      -0.082
+53188.00      0.055     -0.102       0.282      -0.105
+53189.00      0.055     -0.083       0.122      -0.298
+53190.00      0.060     -0.118       0.041      -0.376
+53191.00      0.086     -0.139      -0.017      -0.284
+53192.00      0.104     -0.147       0.023      -0.118
+53193.00      0.103     -0.143       0.082       0.011
+53194.00      0.089     -0.111       0.210      -0.063
+53195.00      0.069     -0.066       0.312      -0.062
+53196.00      0.032     -0.051       0.284       0.103
+53197.00      0.013     -0.071       0.173       0.246
+53198.00      0.004     -0.096       0.130       0.194
+53199.00      0.010     -0.104       0.116       0.026
+53200.00      0.031     -0.107       0.113      -0.157
+53201.00      0.058     -0.111       0.141      -0.149
+53202.00      0.076     -0.106       0.151      -0.120
+53203.00      0.076     -0.123       0.170      -0.145
+53204.00      0.079     -0.111       0.154      -0.196
+53205.00      0.074     -0.113       0.172      -0.169
+53206.00      0.060     -0.120       0.180      -0.106
+53207.00      0.049     -0.115       0.174      -0.018
+53208.00      0.046     -0.099       0.150      -0.019
+53209.00      0.039     -0.095       0.020      -0.069
+53210.00      0.024     -0.108      -0.068      -0.146
+53211.00      0.012     -0.147      -0.117      -0.117
+53212.00      0.011     -0.182      -0.045      -0.069
+53213.00      0.024     -0.197       0.057       0.051
+53214.00      0.048     -0.184       0.243      -0.001
+53215.00      0.070     -0.153       0.301       0.009
+53216.00      0.080     -0.136       0.320      -0.114
+53217.00      0.083     -0.151       0.271      -0.452
+53218.00      0.091     -0.171       0.234      -0.627
+53219.00      0.102     -0.171       0.217      -0.528
+53220.00      0.101     -0.164       0.095      -0.213
+53221.00      0.089     -0.158       0.022      -0.112
+53222.00      0.071     -0.138       0.040      -0.089
+53223.00      0.051     -0.107       0.130      -0.145
+53224.00      0.049     -0.129       0.191       0.099
+53225.00      0.042     -0.160       0.239       0.283
+53226.00      0.047     -0.195       0.249       0.317
+53227.00      0.062     -0.202       0.219       0.139
+53228.00      0.083     -0.191       0.212      -0.136
+53229.00      0.105     -0.179       0.140      -0.401
+53230.00      0.120     -0.167       0.105      -0.425
+53231.00      0.160     -0.161       0.164      -0.283
+53232.00      0.167     -0.151       0.260      -0.252
+53233.00      0.165     -0.150       0.298      -0.213
+53234.00      0.146     -0.153       0.258      -0.277
+53235.00      0.122     -0.153       0.046      -0.262
+53236.00      0.107     -0.150       0.015      -0.252
+53237.00      0.099     -0.150       0.153      -0.188
+53238.00      0.085     -0.168       0.166      -0.234
+53239.00      0.076     -0.192       0.131      -0.341
+53240.00      0.080     -0.225       0.154      -0.400
+53241.00      0.095     -0.241       0.179      -0.238
+53242.00      0.109     -0.226       0.227      -0.085
+53243.00      0.108     -0.169       0.249       0.024
+53244.00      0.120     -0.155       0.273       0.044
+53245.00      0.137     -0.163       0.307      -0.127
+53246.00      0.150     -0.162       0.377      -0.310
+53247.00      0.149     -0.141       0.440      -0.318
+53248.00      0.132     -0.121       0.342      -0.056
+53249.00      0.111     -0.118       0.198       0.122
+53250.00      0.091     -0.117       0.130      -0.071
+53251.00      0.076     -0.108       0.147      -0.165
+53252.00      0.060     -0.108       0.161      -0.276
+53253.00      0.061     -0.137       0.123      -0.354
+53254.00      0.074     -0.164       0.059      -0.424
+53255.00      0.094     -0.170       0.043      -0.412
+53256.00      0.112     -0.166       0.074      -0.293
+53257.00      0.122     -0.168       0.142      -0.136
+53258.00      0.129     -0.165       0.196       0.034
+53259.00      0.127     -0.130       0.253       0.157
+53260.00      0.143     -0.105       0.305       0.096
+53261.00      0.148     -0.090       0.334      -0.020
+53262.00      0.131     -0.091       0.290      -0.133
+53263.00      0.101     -0.103       0.178      -0.106
+53264.00      0.076     -0.128       0.120      -0.132
+53265.00      0.067     -0.154       0.153      -0.116
+53266.00      0.101     -0.163       0.164      -0.065
+53267.00      0.102     -0.157       0.154      -0.101
+53268.00      0.106     -0.160       0.218      -0.158
+53269.00      0.114     -0.175       0.217      -0.067
+53270.00      0.126     -0.186       0.271      -0.128
+53271.00      0.142     -0.182       0.282      -0.217
+53272.00      0.163     -0.192       0.292      -0.410
+53273.00      0.173     -0.188       0.265      -0.504
+53274.00      0.175     -0.177       0.257      -0.518
+53275.00      0.165     -0.156       0.211      -0.369
+53276.00      0.148     -0.142       0.104      -0.242
+53277.00      0.145     -0.157       0.014      -0.262
+53278.00      0.126     -0.168      -0.005      -0.235
+53279.00      0.113     -0.172       0.051      -0.336
+53280.00      0.107     -0.184       0.109      -0.185
+53281.00      0.111     -0.205       0.198      -0.071
+53282.00      0.126     -0.221       0.206      -0.080
+53283.00      0.147     -0.220       0.180      -0.183
+53284.00      0.163     -0.224       0.180      -0.349
+53285.00      0.169     -0.247       0.223      -0.383
+53286.00      0.172     -0.263       0.296      -0.332
+53287.00      0.208     -0.179       0.260      -0.143
+53288.00      0.219     -0.138       0.224      -0.124
+53289.00      0.220     -0.111       0.235      -0.157
+53290.00      0.205     -0.111       0.249      -0.232
+53291.00      0.179     -0.128       0.319      -0.235
+53292.00      0.154     -0.153       0.340      -0.198
+53293.00      0.140     -0.181       0.336      -0.024
+53294.00      0.163     -0.181       0.298       0.116
+53295.00      0.176     -0.170       0.264       0.116
+53296.00      0.190     -0.154       0.241       0.019
+53297.00      0.197     -0.154       0.244      -0.137
+53298.00      0.202     -0.166       0.272      -0.182
+53299.00      0.216     -0.173       0.307      -0.196
+53300.00      0.236     -0.169       0.339      -0.119
+53301.00      0.242     -0.161       0.322      -0.087
+53302.00      0.228     -0.151       0.283      -0.107
+53303.00      0.206     -0.145       0.225      -0.170
+53304.00      0.188     -0.151       0.163      -0.263
+53305.00      0.176     -0.181       0.133      -0.338
+53306.00      0.162     -0.191       0.081      -0.306
+53307.00      0.152     -0.193       0.082      -0.205
+53308.00      0.141     -0.224       0.119      -0.086
+53309.00      0.148     -0.252       0.161       0.002
+53310.00      0.160     -0.272       0.216      -0.051
+53311.00      0.175     -0.268       0.217      -0.207
+53312.00      0.188     -0.260       0.203      -0.332
+53313.00      0.195     -0.273       0.219      -0.263
+53314.00      0.202     -0.288       0.196      -0.187
+53315.00      0.230     -0.270       0.115      -0.235
+53316.00      0.227     -0.234       0.084      -0.234
+53317.00      0.212     -0.216       0.120      -0.394
+53318.00      0.191     -0.223       0.212      -0.438
+53319.00      0.182     -0.265       0.279      -0.364
+53320.00      0.166     -0.277       0.295      -0.347
+53321.00      0.154     -0.287       0.290      -0.281
+53322.00      0.150     -0.290       0.259      -0.016
+53323.00      0.158     -0.290       0.234       0.106
+53324.00      0.177     -0.297       0.178      -0.017
+53325.00      0.199     -0.303       0.208      -0.202
+53326.00      0.212     -0.291       0.266      -0.390
+53327.00      0.214     -0.266       0.325      -0.482
+53328.00      0.215     -0.252       0.318      -0.358
+53329.00      0.216     -0.251       0.220      -0.327
+53330.00      0.204     -0.251       0.111      -0.393
+53331.00      0.180     -0.247       0.100      -0.292
+53332.00      0.160     -0.255       0.176      -0.292
+53333.00      0.155     -0.302       0.288      -0.276
+53334.00      0.148     -0.308       0.345      -0.171
+53335.00      0.146     -0.304       0.356       0.016
+53336.00      0.152     -0.313       0.333       0.173
+53337.00      0.160     -0.343       0.309       0.248
+53338.00      0.166     -0.371       0.292       0.177
+53339.00      0.170     -0.370       0.273      -0.045
+53340.00      0.176     -0.373       0.284      -0.284
+53341.00      0.183     -0.355       0.261      -0.365
+53342.00      0.193     -0.346       0.247      -0.372
+53343.00      0.195     -0.333       0.188      -0.423
+53344.00      0.177     -0.322       0.164      -0.483
+53345.00      0.145     -0.332       0.259      -0.486
+53346.00      0.119     -0.349       0.402      -0.463
+53347.00      0.112     -0.399       0.470      -0.340
+53348.00      0.102     -0.408       0.481      -0.522
+53349.00      0.103     -0.425       0.471      -0.558
+53350.00      0.115     -0.428       0.436      -0.528
+53351.00      0.121     -0.414       0.363      -0.208
+53352.00      0.118     -0.419       0.236      -0.128
+53353.00      0.127     -0.447       0.104      -0.326
+53354.00      0.147     -0.445       0.159      -0.488
+53355.00      0.152     -0.400       0.246      -0.507
+53356.00      0.137     -0.364       0.245      -0.383
+53357.00      0.075     -0.376       0.155      -0.422
+53358.00      0.066     -0.387       0.062      -0.503
+53359.00      0.049     -0.375       0.007      -0.529
+53360.00      0.025     -0.362       0.035      -0.404
+53361.00      0.010     -0.367       0.107      -0.198
+53362.00      0.009     -0.378       0.196      -0.076
+53363.00      0.018     -0.385       0.184      -0.083
+53364.00      0.031     -0.395       0.115       0.043
+53365.00      0.040     -0.418       0.045       0.019
+53366.00      0.044     -0.435       0.062      -0.026
+53367.00      0.046     -0.429       0.078      -0.162
+53368.00      0.055     -0.433       0.126      -0.380
+53369.00      0.062     -0.403       0.119      -0.457
+53370.00      0.067     -0.379       0.078      -0.512
+53371.00      0.061     -0.367       0.041      -0.647
+53372.00      0.034     -0.377      -0.008      -0.741
+53373.00     -0.001     -0.411      -0.011      -0.737
+53374.00     -0.020     -0.435       0.012      -0.553
+53375.00     -0.006     -0.451       0.014      -0.348
+53376.00     -0.015     -0.447      -0.031      -0.316
+53377.00     -0.011     -0.478       0.060      -0.360
+53378.00      0.023     -0.503       0.190      -0.378
+53379.00      0.055     -0.472       0.307      -0.220
+53380.00      0.052     -0.430       0.253      -0.131
+53381.00      0.031     -0.432       0.107      -0.282
+53382.00      0.026     -0.453       0.035      -0.611
+53383.00      0.031     -0.438       0.092      -0.617
+53384.00      0.023     -0.405       0.097      -0.512
+53385.00      0.023     -0.411       0.073      -0.427
+53386.00      0.008     -0.414       0.041      -0.581
+53387.00     -0.009     -0.399       0.039      -0.631
+53388.00     -0.035     -0.380       0.027      -0.524
+53389.00     -0.054     -0.382       0.056      -0.384
+53390.00     -0.055     -0.402       0.064      -0.309
+53391.00     -0.042     -0.421       0.102      -0.319
+53392.00     -0.031     -0.444       0.068      -0.397
+53393.00     -0.018     -0.457       0.009      -0.409
+53394.00     -0.011     -0.453       0.004      -0.384
+53395.00     -0.005     -0.425       0.030      -0.340
+53396.00      0.001     -0.394       0.066      -0.327
+53397.00      0.002     -0.372       0.083      -0.293
+53398.00     -0.009     -0.360       0.079      -0.264
+53399.00     -0.028     -0.356       0.107      -0.193
+53400.00     -0.056     -0.370       0.082      -0.228
+53401.00     -0.083     -0.407       0.086      -0.317
+53402.00     -0.091     -0.436       0.015      -0.298
+53403.00     -0.080     -0.458      -0.013      -0.343
+53404.00     -0.071     -0.434      -0.081      -0.392
+53405.00     -0.073     -0.444      -0.058      -0.520
+53406.00     -0.085     -0.474       0.067      -0.622
+53407.00     -0.057     -0.464       0.172      -0.514
+53408.00     -0.039     -0.416       0.163      -0.425
+53409.00     -0.048     -0.387       0.069      -0.423
+53410.00     -0.061     -0.396      -0.037      -0.442
+53411.00     -0.064     -0.404      -0.055      -0.399
+53412.00     -0.067     -0.392      -0.058      -0.301
+53413.00     -0.078     -0.374      -0.041      -0.199
+53414.00     -0.093     -0.359      -0.039      -0.365
+53415.00     -0.112     -0.344       0.000      -0.446
+53416.00     -0.132     -0.344       0.012      -0.313
+53417.00     -0.158     -0.384       0.038      -0.280
+53418.00     -0.151     -0.405       0.072      -0.262
+53419.00     -0.131     -0.417       0.096      -0.512
+53420.00     -0.117     -0.446       0.071      -0.591
+53421.00     -0.097     -0.453       0.036      -0.588
+53422.00     -0.082     -0.437       0.018      -0.369
+53423.00     -0.074     -0.399       0.036      -0.284
+53424.00     -0.071     -0.363       0.037      -0.364
+53425.00     -0.080     -0.348       0.004      -0.377
+53426.00     -0.105     -0.351      -0.091      -0.374
+53427.00     -0.165     -0.347      -0.158      -0.334
+53428.00     -0.185     -0.361      -0.175      -0.334
+53429.00     -0.197     -0.385      -0.204      -0.337
+53430.00     -0.197     -0.410      -0.196      -0.347
+53431.00     -0.176     -0.412      -0.161      -0.449
+53432.00     -0.142     -0.390      -0.106      -0.449
+53433.00     -0.122     -0.368      -0.031      -0.436
+53434.00     -0.139     -0.355       0.027      -0.465
+53435.00     -0.146     -0.359       0.019      -0.337
+53436.00     -0.142     -0.359       0.015      -0.172
+53437.00     -0.129     -0.357      -0.012      -0.198
+53438.00     -0.119     -0.351      -0.029      -0.278
+53439.00     -0.117     -0.337      -0.097      -0.332
+53440.00     -0.122     -0.314      -0.168      -0.254
+53441.00     -0.137     -0.268      -0.212      -0.199
+53442.00     -0.151     -0.252      -0.209      -0.229
+53443.00     -0.166     -0.248      -0.160      -0.202
+53444.00     -0.173     -0.269      -0.075      -0.274
+53445.00     -0.168     -0.308      -0.003      -0.127
+53446.00     -0.151     -0.327       0.063      -0.196
+53447.00     -0.127     -0.319       0.098      -0.294
+53448.00     -0.116     -0.317       0.107      -0.492
+53449.00     -0.095     -0.313       0.010      -0.479
+53450.00     -0.084     -0.302      -0.030      -0.408
+53451.00     -0.084     -0.273      -0.071      -0.241
+53452.00     -0.092     -0.245      -0.059      -0.265
+53453.00     -0.110     -0.238      -0.109      -0.299
+53454.00     -0.137     -0.244      -0.147      -0.377
+53455.00     -0.206     -0.258      -0.202      -0.308
+53456.00     -0.217     -0.273      -0.244      -0.320
+53457.00     -0.215     -0.296      -0.185      -0.298
+53458.00     -0.208     -0.317      -0.121      -0.289
+53459.00     -0.192     -0.325      -0.036      -0.282
+53460.00     -0.161     -0.314       0.039      -0.244
+53461.00     -0.128     -0.292       0.070      -0.268
+53462.00     -0.118     -0.261       0.016      -0.281
+53463.00     -0.130     -0.248      -0.034      -0.238
+53464.00     -0.144     -0.259      -0.040      -0.191
+53465.00     -0.141     -0.283      -0.030      -0.264
+53466.00     -0.130     -0.285       0.026      -0.319
+53467.00     -0.126     -0.254       0.038      -0.376
+53468.00     -0.135     -0.213      -0.035      -0.276
+53469.00     -0.188     -0.188      -0.114      -0.298
+53470.00     -0.206     -0.180      -0.130      -0.336
+53471.00     -0.214     -0.188      -0.157      -0.391
+53472.00     -0.207     -0.216      -0.142      -0.301
+53473.00     -0.185     -0.255      -0.088      -0.251
+53474.00     -0.157     -0.276      -0.102      -0.201
+53475.00     -0.128     -0.262      -0.115      -0.304
+53476.00     -0.116     -0.212      -0.081      -0.242
+53477.00     -0.100     -0.197      -0.087      -0.258
+53478.00     -0.095     -0.183      -0.066      -0.138
+53479.00     -0.099     -0.159      -0.019      -0.041
+53480.00     -0.111     -0.140       0.043      -0.146
+53481.00     -0.126     -0.143       0.074      -0.192
+53482.00     -0.144     -0.156       0.023      -0.249
+53483.00     -0.145     -0.145      -0.040      -0.320
+53484.00     -0.153     -0.155      -0.095      -0.274
+53485.00     -0.149     -0.179      -0.087      -0.208
+53486.00     -0.133     -0.203      -0.045      -0.133
+53487.00     -0.110     -0.208       0.048      -0.094
+53488.00     -0.087     -0.199       0.076      -0.168
+53489.00     -0.069     -0.195       0.024      -0.212
+53490.00     -0.062     -0.176      -0.016      -0.340
+53491.00     -0.059     -0.160      -0.006      -0.335
+53492.00     -0.060     -0.143      -0.012      -0.375
+53493.00     -0.065     -0.137       0.021      -0.254
+53494.00     -0.072     -0.136       0.007      -0.180
+53495.00     -0.080     -0.122       0.004      -0.155
+53496.00     -0.093     -0.104      -0.020      -0.153
+53497.00     -0.143     -0.089      -0.025      -0.194
+53498.00     -0.161     -0.093      -0.027      -0.186
+53499.00     -0.168     -0.101      -0.004      -0.105
+53500.00     -0.157     -0.118       0.034       0.056
+53501.00     -0.132     -0.145       0.091       0.148
+53502.00     -0.101     -0.166       0.134       0.101
+53503.00     -0.073     -0.165       0.130      -0.012
+53504.00     -0.053     -0.152       0.112      -0.055
+53505.00     -0.046     -0.139       0.083      -0.096
+53506.00     -0.048     -0.119       0.024      -0.050
+53507.00     -0.055     -0.085      -0.042      -0.157
+53508.00     -0.111     -0.068      -0.065      -0.233
+53509.00     -0.128     -0.087      -0.053      -0.276
+53510.00     -0.141     -0.123      -0.005      -0.295
+53511.00     -0.171     -0.154      -0.080      -0.286
+53512.00     -0.172     -0.155      -0.187      -0.344
+53513.00     -0.167     -0.167      -0.217      -0.273
+53514.00     -0.151     -0.184      -0.145      -0.178
+53515.00     -0.132     -0.173      -0.013       0.071
+53516.00     -0.113     -0.165       0.047       0.057
+53517.00     -0.102     -0.176       0.030      -0.071
+53518.00     -0.095     -0.197      -0.015      -0.372
+53519.00     -0.081     -0.196       0.003      -0.612
+53520.00     -0.068     -0.160       0.029      -0.618
+53521.00     -0.067     -0.112       0.035      -0.405
+53522.00     -0.082     -0.079      -0.012      -0.213
+53523.00     -0.101     -0.072      -0.045      -0.069
+53524.00     -0.112     -0.085      -0.032      -0.062
+53525.00     -0.140     -0.084       0.004      -0.215
+53526.00     -0.143     -0.100      -0.032      -0.201
+53527.00     -0.139     -0.104      -0.121      -0.151
+53528.00     -0.123     -0.111      -0.226       0.038
+53529.00     -0.100     -0.126      -0.266       0.047
+53530.00     -0.075     -0.140      -0.199       0.020
+53531.00     -0.052     -0.140      -0.091      -0.190
+53532.00     -0.026     -0.097       0.036      -0.305
+53533.00     -0.025     -0.093       0.120      -0.232
+53534.00     -0.032     -0.080       0.135      -0.059
+53535.00     -0.040     -0.048       0.032      -0.109
+53536.00     -0.047     -0.022      -0.073      -0.149
+53537.00     -0.068     -0.055      -0.071      -0.219
+53538.00     -0.081     -0.117       0.009      -0.140
+53539.00     -0.077     -0.151       0.007      -0.024
+53540.00     -0.062     -0.148      -0.021       0.008
+53541.00     -0.044     -0.139      -0.080      -0.054
+53542.00     -0.027     -0.134      -0.014      -0.018
+53543.00     -0.011     -0.099       0.078       0.092
+53544.00     -0.017     -0.099       0.066       0.011
+53545.00     -0.016     -0.125       0.002      -0.135
+53546.00      0.004     -0.155       0.003      -0.423
+53547.00      0.026     -0.152       0.002      -0.449
+53548.00      0.033     -0.117       0.043      -0.194
+53549.00      0.022     -0.072       0.030      -0.065
+53550.00     -0.000     -0.033       0.041       0.013
+53551.00     -0.023     -0.016       0.052       0.010
+53552.00     -0.034     -0.029       0.118      -0.137
+53553.00     -0.020     -0.050       0.147      -0.334
+53554.00     -0.013     -0.071       0.189      -0.544
+53555.00      0.003     -0.076       0.151      -0.591
+53556.00      0.026     -0.081       0.145      -0.405
+53557.00      0.052     -0.095       0.123      -0.185
+53558.00      0.069     -0.103       0.197      -0.048
+53559.00      0.079     -0.093       0.289      -0.005
+53560.00      0.082     -0.078       0.346      -0.148
+53561.00      0.075     -0.077       0.382      -0.079
+53562.00      0.062     -0.080       0.324      -0.105
+53563.00      0.049     -0.070       0.175      -0.144
+53564.00      0.042     -0.050       0.023      -0.299
+53565.00      0.022     -0.083      -0.046      -0.321
+53566.00      0.013     -0.143       0.059      -0.224
+53567.00      0.004     -0.188       0.120      -0.051
+53568.00      0.029     -0.179       0.108      -0.023
+53569.00      0.059     -0.155       0.080      -0.140
+53570.00      0.084     -0.132       0.075      -0.109
+53571.00      0.082     -0.112       0.089       0.014
+53572.00      0.056     -0.117       0.065       0.028
+53573.00      0.045     -0.153       0.092      -0.169
+53574.00      0.097     -0.177       0.166      -0.361
+53575.00      0.125     -0.151       0.210      -0.420
+53576.00      0.118     -0.112       0.227      -0.153
+53577.00      0.084     -0.097       0.135      -0.132
+53578.00      0.054     -0.092       0.107      -0.187
+53579.00      0.039     -0.078       0.050      -0.193
+53580.00      0.035     -0.076       0.027      -0.112
+53581.00      0.041     -0.096       0.032      -0.035
+53582.00      0.055     -0.114       0.059      -0.091
+53583.00      0.075     -0.112       0.127      -0.161
+53584.00      0.101     -0.106       0.191      -0.077
+53585.00      0.129     -0.110       0.208       0.090
+53586.00      0.147     -0.110       0.293       0.151
+53587.00      0.149     -0.095       0.390       0.025
+53588.00      0.187     -0.056       0.418      -0.221
+53589.00      0.175     -0.058       0.408      -0.231
+53590.00      0.160     -0.070       0.388      -0.218
+53591.00      0.147     -0.072       0.280      -0.089
+53592.00      0.135     -0.069       0.191      -0.144
+53593.00      0.121     -0.085       0.148      -0.142
+53594.00      0.115     -0.126       0.222      -0.109
+53595.00      0.117     -0.168       0.304      -0.010
+53596.00      0.136     -0.168       0.354      -0.016
+53597.00      0.158     -0.147       0.348      -0.189
+53598.00      0.179     -0.124       0.296      -0.134
+53599.00      0.186     -0.103       0.238      -0.008
+53600.00      0.175     -0.107       0.220      -0.019
+53601.00      0.167     -0.138       0.290      -0.070
+53602.00      0.183     -0.149       0.412      -0.290
+53603.00      0.200     -0.106       0.507      -0.239
+53604.00      0.181     -0.057       0.409       0.012
+53605.00      0.139     -0.054       0.266      -0.030
+53606.00      0.111     -0.077       0.183      -0.167
+53607.00      0.105     -0.078       0.185      -0.316
+53608.00      0.111     -0.075       0.202      -0.138
+53609.00      0.124     -0.094       0.201      -0.023
+53610.00      0.144     -0.117       0.207       0.099
+53611.00      0.165     -0.114       0.219      -0.028
+53612.00      0.184     -0.094       0.291      -0.075
+53613.00      0.214     -0.076       0.289       0.026
+53614.00      0.225     -0.068       0.281       0.068
+53615.00      0.224     -0.058       0.333       0.021
+53616.00      0.216     -0.042       0.323      -0.254
+53617.00      0.202     -0.048       0.280      -0.433
+53618.00      0.184     -0.060       0.255      -0.463
+53619.00      0.168     -0.061       0.260      -0.295
+53620.00      0.156     -0.054       0.204      -0.191
+53621.00      0.146     -0.059       0.158      -0.143
+53622.00      0.139     -0.086       0.136      -0.229
+53623.00      0.144     -0.120       0.221      -0.210
+53624.00      0.163     -0.144       0.291      -0.207
+53625.00      0.187     -0.145       0.317      -0.255
+53626.00      0.204     -0.129       0.305      -0.167
+53627.00      0.212     -0.108       0.222       0.018
+53628.00      0.214     -0.106       0.143      -0.070
+53629.00      0.217     -0.125       0.139      -0.179
+53630.00      0.223     -0.130       0.278      -0.355
+53631.00      0.219     -0.092       0.375      -0.272
+53632.00      0.194     -0.045       0.275      -0.045
+53633.00      0.162     -0.037       0.165      -0.018
+53634.00      0.145     -0.053       0.149      -0.053
+53635.00      0.144     -0.057       0.240      -0.265
+53636.00      0.151     -0.056       0.295      -0.131
+53637.00      0.166     -0.077       0.292      -0.087
+53638.00      0.190     -0.107       0.260      -0.031
+53639.00      0.214     -0.110       0.234      -0.133
+53640.00      0.227     -0.094       0.241      -0.071
+53641.00      0.232     -0.078       0.291      -0.020
+53642.00      0.231     -0.068       0.333       0.167
+53643.00      0.227     -0.055       0.352       0.144
+53644.00      0.221     -0.045       0.372      -0.026
+53645.00      0.211     -0.047       0.323      -0.196
+53646.00      0.191     -0.055       0.285      -0.282
+53647.00      0.169     -0.061       0.271      -0.150
+53648.00      0.158     -0.066       0.268      -0.028
+53649.00      0.156     -0.077       0.320       0.029
+53650.00      0.157     -0.092       0.334      -0.029
+53651.00      0.164     -0.110       0.334      -0.142
+53652.00      0.187     -0.129       0.327      -0.228
+53653.00      0.219     -0.139       0.353      -0.166
+53654.00      0.240     -0.133       0.296      -0.007
+53655.00      0.243     -0.116       0.269       0.052
+53656.00      0.237     -0.111       0.257       0.141
+53657.00      0.232     -0.126       0.349       0.104
+53658.00      0.227     -0.134       0.485       0.012
+53659.00      0.215     -0.116       0.502       0.031
+53660.00      0.195     -0.088       0.389       0.248
+53661.00      0.180     -0.082       0.223       0.297
+53662.00      0.174     -0.090       0.120       0.260
+53663.00      0.176     -0.091       0.191       0.079
+53664.00      0.184     -0.090       0.343      -0.061
+53665.00      0.201     -0.102       0.465      -0.086
+53666.00      0.226     -0.118       0.492      -0.196
+53667.00      0.250     -0.119       0.443      -0.310
+53668.00      0.261     -0.114       0.421      -0.327
+53669.00      0.259     -0.120       0.392      -0.264
+53670.00      0.250     -0.123       0.388      -0.068
+53671.00      0.243     -0.103       0.391      -0.071
+53672.00      0.236     -0.072       0.388      -0.130
+53673.00      0.224     -0.059       0.342      -0.142
+53674.00      0.203     -0.070       0.297      -0.137
+53675.00      0.184     -0.094       0.275      -0.127
+53676.00      0.179     -0.121       0.270      -0.067
+53677.00      0.192     -0.144       0.267      -0.091
+53678.00      0.210     -0.148       0.263      -0.195
+53679.00      0.226     -0.128       0.261      -0.284
+53680.00      0.239     -0.103       0.000       0.000
+53681.00      0.251     -0.096       0.000       0.000
+53682.00      0.256     -0.101       0.000       0.000
+53683.00      0.254     -0.105       0.000       0.000
+53684.00      0.246     -0.110       0.000       0.000
+53685.00      0.235     -0.123       0.000       0.000
+53686.00      0.223     -0.133       0.000       0.000
+53687.00      0.214     -0.128       0.000       0.000
+53688.00      0.208     -0.117       0.000       0.000
+53689.00      0.203     -0.117       0.000       0.000
+53690.00      0.200     -0.126       0.000       0.000
+53691.00      0.202     -0.133       0.000       0.000
+53692.00      0.214     -0.136       0.000       0.000
+53693.00      0.234     -0.144       0.000       0.000
+53694.00      0.255     -0.146       0.000       0.000
+53695.00      0.271     -0.135       0.000       0.000
+53696.00      0.277     -0.128       0.000       0.000
+53697.00      0.275     -0.143       0.000       0.000
+53698.00      0.268     -0.161       0.000       0.000
+53699.00      0.258     -0.145       0.000       0.000
+53700.00      0.243     -0.108       0.000       0.000
+53701.00      0.222     -0.091       0.000       0.000
+53702.00      0.203     -0.113       0.000       0.000
+53703.00      0.197     -0.152       0.000       0.000
+53704.00      0.207     -0.187       0.000       0.000
+53705.00      0.227     -0.209       0.000       0.000
+53706.00      0.249     -0.206       0.000       0.000
+53707.00      0.258     -0.160       0.000       0.000
+53708.00      0.266     -0.120       0.000       0.000
+53709.00      0.260     -0.099       0.000       0.000
+53710.00      0.245     -0.101       0.000       0.000
+53711.00      0.233     -0.111       0.000       0.000
+53712.00      0.229     -0.119       0.000       0.000
+53713.00      0.226     -0.124       0.000       0.000
+53714.00      0.215     -0.124       0.000       0.000
+53715.00      0.202     -0.114       0.000       0.000
+53716.00      0.198     -0.104       0.000       0.000
+53717.00      0.196     -0.103       0.000       0.000
+53718.00      0.192     -0.111       0.000       0.000
+53719.00      0.192     -0.119       0.000       0.000
+53720.00      0.205     -0.131       0.000       0.000
+53721.00      0.225     -0.144       0.000       0.000
+53722.00      0.238     -0.150       0.000       0.000
+53723.00      0.244     -0.134       0.000       0.000
+53724.00      0.246     -0.110       0.000       0.000
+53725.00      0.248     -0.105       0.000       0.000
+53726.00      0.245     -0.110       0.000       0.000
+53727.00      0.233     -0.101       0.000       0.000
+53728.00      0.206     -0.082       0.000       0.000
+53729.00      0.175     -0.084       0.000       0.000
+53730.00      0.160     -0.113       0.000       0.000
+53731.00      0.171     -0.140       0.000       0.000
+53732.00      0.194     -0.153       0.000       0.000
+53733.00      0.215     -0.156       0.000       0.000
+53734.00      0.231     -0.148       0.000       0.000
+53735.00      0.239     -0.124       0.000       0.000
+53736.00      0.240     -0.103       0.000       0.000
+53737.00      0.238     -0.099       0.000       0.000
+53738.00      0.231     -0.095       0.000       0.000
+53739.00      0.217     -0.077       0.000       0.000
+53740.00      0.202     -0.065       0.000       0.000
+53741.00      0.192     -0.072       0.000       0.000
+53742.00      0.181     -0.078       0.000       0.000
+53743.00      0.164     -0.067       0.000       0.000
+53744.00      0.149     -0.052       0.000       0.000
+53745.00      0.145     -0.052       0.000       0.000
+53746.00      0.148     -0.061       0.000       0.000
+53747.00      0.157     -0.072       0.000       0.000
+53748.00      0.172     -0.088       0.000       0.000
+53749.00      0.191     -0.107       0.000       0.000
+53750.00      0.202     -0.118       0.000       0.000
+53751.00      0.206     -0.104       0.000       0.000
+53752.00      0.208     -0.073       0.000       0.000
+53753.00      0.210     -0.049       0.000       0.000
+53754.00      0.206     -0.037       0.000       0.000
+53755.00      0.189     -0.032       0.000       0.000
+53756.00      0.158     -0.039       0.000       0.000
+53757.00      0.129     -0.066       0.000       0.000
+53758.00      0.128     -0.093       0.000       0.000
+53759.00      0.149     -0.092       0.000       0.000
+53760.00      0.170     -0.080       0.000       0.000
+53761.00      0.185     -0.089       0.000       0.000
+53762.00      0.206     -0.096       0.000       0.000
+53763.00      0.226     -0.069       0.000       0.000
+53764.00      0.227     -0.032       0.000       0.000
+53765.00      0.216     -0.031       0.000       0.000
+53766.00      0.211     -0.044       0.000       0.000
+53767.00      0.202     -0.030       0.000       0.000
+53768.00      0.177     -0.007       0.000       0.000
+53769.00      0.153     -0.016       0.000       0.000
+53770.00      0.145     -0.039       0.000       0.000
+53771.00      0.141     -0.037       0.000       0.000
+53772.00      0.132     -0.018       0.000       0.000
+53773.00      0.129     -0.014       0.000       0.000
+53774.00      0.142     -0.027       0.000       0.000
+53775.00      0.163     -0.041       0.000       0.000
+53776.00      0.186     -0.056       0.000       0.000
+53777.00      0.205     -0.066       0.000       0.000
+53778.00      0.219     -0.063       0.000       0.000
+53779.00      0.226     -0.039       0.000       0.000
+53780.00      0.227     -0.008       0.000       0.000
+53781.00      0.220      0.011       0.000       0.000
+53782.00      0.204      0.014       0.000       0.000
+53783.00      0.181      0.012       0.000       0.000
+53784.00      0.158     -0.002       0.000       0.000
+53785.00      0.148     -0.032       0.000       0.000
+53786.00      0.166     -0.052       0.000       0.000
+53787.00      0.197     -0.034       0.000       0.000
+53788.00      0.209     -0.010       0.000       0.000
+53789.00      0.202     -0.034       0.000       0.000
+53790.00      0.215     -0.081       0.000       0.000
+53791.00      0.254     -0.070       0.000       0.000
+53792.00      0.277     -0.003       0.000       0.000
+53793.00      0.265      0.035       0.000       0.000
+53794.00      0.247      0.013       0.000       0.000
+53795.00      0.237     -0.018       0.000       0.000
+53796.00      0.223     -0.024       0.000       0.000
+53797.00      0.000      0.000       0.000       0.000
+53798.00      0.000      0.000       0.000       0.000
+53799.00      0.000      0.000       0.000       0.000
+53800.00      0.000      0.000       0.000       0.000
+53801.00      0.000      0.000       0.000       0.000
+53802.00      0.000      0.000       0.000       0.000
+53803.00      0.000      0.000       0.000       0.000
+53804.00      0.000      0.000       0.000       0.000
+53805.00      0.000      0.000       0.000       0.000
+53806.00      0.000      0.000       0.000       0.000
+53807.00      0.000      0.000       0.000       0.000
+53808.00      0.000      0.000       0.000       0.000
+53809.00      0.000      0.000       0.000       0.000
+53810.00      0.000      0.000       0.000       0.000
+53811.00      0.000      0.000       0.000       0.000
+53812.00      0.000      0.000       0.000       0.000
+53813.00      0.000      0.000       0.000       0.000
+53814.00      0.000      0.000       0.000       0.000
+53815.00      0.000      0.000       0.000       0.000
+53816.00      0.000      0.000       0.000       0.000
+53817.00      0.000      0.000       0.000       0.000
+53818.00      0.000      0.000       0.000       0.000
+53819.00      0.000      0.000       0.000       0.000
+53820.00      0.000      0.000       0.000       0.000
+53821.00      0.000      0.000       0.000       0.000
+53822.00      0.000      0.000       0.000       0.000
+53823.00      0.000      0.000       0.000       0.000
+53824.00      0.000      0.000       0.000       0.000
+53825.00      0.000      0.000       0.000       0.000
+53826.00      0.000      0.000       0.000       0.000
+53827.00      0.000      0.000       0.000       0.000
+53828.00      0.000      0.000       0.000       0.000
+53829.00      0.000      0.000       0.000       0.000
+53830.00      0.000      0.000       0.000       0.000
+53831.00      0.000      0.000       0.000       0.000
+53832.00      0.000      0.000       0.000       0.000
+53833.00      0.000      0.000       0.000       0.000
+53834.00      0.000      0.000       0.000       0.000
+53835.00      0.000      0.000       0.000       0.000
+53836.00      0.000      0.000       0.000       0.000
+53837.00      0.000      0.000       0.000       0.000
+53838.00      0.000      0.000       0.000       0.000
+53839.00      0.000      0.000       0.000       0.000
+53840.00      0.000      0.000       0.000       0.000
+53841.00      0.000      0.000       0.000       0.000
+53842.00      0.000      0.000       0.000       0.000
+53843.00      0.000      0.000       0.000       0.000
+53844.00      0.000      0.000       0.000       0.000
+53845.00      0.000      0.000       0.000       0.000
+53846.00      0.000      0.000       0.000       0.000
+53847.00      0.000      0.000       0.000       0.000
+53848.00      0.000      0.000       0.000       0.000
+53849.00      0.000      0.000       0.000       0.000
+53850.00      0.000      0.000       0.000       0.000
+53851.00      0.000      0.000       0.000       0.000
+53852.00      0.000      0.000       0.000       0.000
+53853.00      0.000      0.000       0.000       0.000
+53854.00      0.000      0.000       0.000       0.000
+53855.00      0.000      0.000       0.000       0.000
+53856.00      0.000      0.000       0.000       0.000
+53857.00      0.000      0.000       0.000       0.000
+53858.00      0.000      0.000       0.000       0.000
+53859.00      0.000      0.000       0.000       0.000
+53860.00      0.000      0.000       0.000       0.000
+53861.00      0.000      0.000       0.000       0.000
+53862.00      0.000      0.000       0.000       0.000
+53863.00      0.000      0.000       0.000       0.000
+53864.00      0.000      0.000       0.000       0.000
+53865.00      0.000      0.000       0.000       0.000
+53866.00      0.000      0.000       0.000       0.000
+53867.00      0.000      0.000       0.000       0.000
+53868.00      0.000      0.000       0.000       0.000
+53869.00      0.000      0.000       0.000       0.000
+53870.00      0.000      0.000       0.000       0.000
+53871.00      0.000      0.000       0.000       0.000
+53872.00      0.000      0.000       0.000       0.000
+53873.00      0.000      0.000       0.000       0.000
+53874.00      0.000      0.000       0.000       0.000
+53875.00      0.000      0.000       0.000       0.000
+53876.00      0.000      0.000       0.000       0.000
+53877.00      0.000      0.000       0.000       0.000
+53878.00      0.000      0.000       0.000       0.000
+53879.00      0.000      0.000       0.000       0.000
+53880.00      0.000      0.000       0.000       0.000
+53881.00      0.000      0.000       0.000       0.000
+53882.00      0.000      0.000       0.000       0.000
+53883.00      0.000      0.000       0.000       0.000
+53884.00      0.000      0.000       0.000       0.000
+53885.00      0.000      0.000       0.000       0.000
+53886.00      0.000      0.000       0.000       0.000
+53887.00      0.000      0.000       0.000       0.000
+53888.00      0.000      0.000       0.000       0.000
+53889.00      0.000      0.000       0.000       0.000
+53890.00      0.000      0.000       0.000       0.000
+53891.00      0.000      0.000       0.000       0.000
+53892.00      0.000      0.000       0.000       0.000
+53893.00      0.000      0.000       0.000       0.000
+53894.00      0.000      0.000       0.000       0.000
+53895.00      0.000      0.000       0.000       0.000
+53896.00      0.000      0.000       0.000       0.000
+53897.00      0.000      0.000       0.000       0.000
+53898.00      0.000      0.000       0.000       0.000
+53899.00      0.000      0.000       0.000       0.000
+53900.00      0.000      0.000       0.000       0.000
+53901.00      0.000      0.000       0.000       0.000
+53902.00      0.000      0.000       0.000       0.000
+53903.00      0.000      0.000       0.000       0.000
+53904.00      0.000      0.000       0.000       0.000
+53905.00      0.000      0.000       0.000       0.000
+53906.00      0.000      0.000       0.000       0.000
+53907.00      0.000      0.000       0.000       0.000
+53908.00      0.000      0.000       0.000       0.000
+53909.00      0.000      0.000       0.000       0.000
+53910.00      0.000      0.000       0.000       0.000
+53911.00      0.000      0.000       0.000       0.000
+53912.00      0.000      0.000       0.000       0.000
+53913.00      0.000      0.000       0.000       0.000
+53914.00      0.000      0.000       0.000       0.000
+53915.00      0.000      0.000       0.000       0.000
+53916.00      0.000      0.000       0.000       0.000
+53917.00      0.000      0.000       0.000       0.000
+53918.00      0.000      0.000       0.000       0.000
+53919.00      0.000      0.000       0.000       0.000
+53920.00      0.000      0.000       0.000       0.000
+53921.00      0.000      0.000       0.000       0.000
+53922.00      0.000      0.000       0.000       0.000
+53923.00      0.000      0.000       0.000       0.000
+53924.00      0.000      0.000       0.000       0.000
+53925.00      0.000      0.000       0.000       0.000
+53926.00      0.000      0.000       0.000       0.000
+53927.00      0.000      0.000       0.000       0.000
+53928.00      0.000      0.000       0.000       0.000
+53929.00      0.000      0.000       0.000       0.000
+53930.00      0.000      0.000       0.000       0.000
+53931.00      0.000      0.000       0.000       0.000
+53932.00      0.000      0.000       0.000       0.000
+53933.00      0.000      0.000       0.000       0.000
+53934.00      0.000      0.000       0.000       0.000
+53935.00      0.000      0.000       0.000       0.000
+53936.00      0.000      0.000       0.000       0.000
+53937.00      0.000      0.000       0.000       0.000
+53938.00      0.000      0.000       0.000       0.000
+53939.00      0.000      0.000       0.000       0.000
+53940.00      0.000      0.000       0.000       0.000
+53941.00      0.000      0.000       0.000       0.000
+53942.00      0.000      0.000       0.000       0.000
+53943.00      0.000      0.000       0.000       0.000
+53944.00      0.000      0.000       0.000       0.000
+53945.00      0.000      0.000       0.000       0.000
+53946.00      0.000      0.000       0.000       0.000
+53947.00      0.000      0.000       0.000       0.000
+53948.00      0.000      0.000       0.000       0.000
+53949.00      0.000      0.000       0.000       0.000
+53950.00      0.000      0.000       0.000       0.000
+53951.00      0.000      0.000       0.000       0.000
+53952.00      0.000      0.000       0.000       0.000
+53953.00      0.000      0.000       0.000       0.000
+53954.00      0.000      0.000       0.000       0.000
+53955.00      0.000      0.000       0.000       0.000
+53956.00      0.000      0.000       0.000       0.000
+53957.00      0.000      0.000       0.000       0.000
+53958.00      0.000      0.000       0.000       0.000
+53959.00      0.000      0.000       0.000       0.000
+53960.00      0.000      0.000       0.000       0.000
+53961.00      0.000      0.000       0.000       0.000
+53962.00      0.000      0.000       0.000       0.000
+53963.00      0.000      0.000       0.000       0.000
+53964.00      0.000      0.000       0.000       0.000
+53965.00      0.000      0.000       0.000       0.000
+53966.00      0.000      0.000       0.000       0.000
+53967.00      0.000      0.000       0.000       0.000
+53968.00      0.000      0.000       0.000       0.000
+53969.00      0.000      0.000       0.000       0.000
+53970.00      0.000      0.000       0.000       0.000
+53971.00      0.000      0.000       0.000       0.000
+53972.00      0.000      0.000       0.000       0.000
+53973.00      0.000      0.000       0.000       0.000
+53974.00      0.000      0.000       0.000       0.000
+53975.00      0.000      0.000       0.000       0.000
+53976.00      0.000      0.000       0.000       0.000
+53977.00      0.000      0.000       0.000       0.000
+53978.00      0.000      0.000       0.000       0.000
+53979.00      0.000      0.000       0.000       0.000
+53980.00      0.000      0.000       0.000       0.000
+53981.00      0.000      0.000       0.000       0.000
+53982.00      0.000      0.000       0.000       0.000
+53983.00      0.000      0.000       0.000       0.000
+53984.00      0.000      0.000       0.000       0.000
+53985.00      0.000      0.000       0.000       0.000
+53986.00      0.000      0.000       0.000       0.000
+53987.00      0.000      0.000       0.000       0.000
+53988.00      0.000      0.000       0.000       0.000
+53989.00      0.000      0.000       0.000       0.000
+53990.00      0.000      0.000       0.000       0.000
+53991.00      0.000      0.000       0.000       0.000
+53992.00      0.000      0.000       0.000       0.000
+53993.00      0.000      0.000       0.000       0.000
+53994.00      0.000      0.000       0.000       0.000
+53995.00      0.000      0.000       0.000       0.000
+53996.00      0.000      0.000       0.000       0.000
+53997.00      0.000      0.000       0.000       0.000
+53998.00      0.000      0.000       0.000       0.000
+53999.00      0.000      0.000       0.000       0.000
+54000.00      0.000      0.000       0.000       0.000
+54001.00      0.000      0.000       0.000       0.000
+54002.00      0.000      0.000       0.000       0.000
+54003.00      0.000      0.000       0.000       0.000
+54004.00      0.000      0.000       0.000       0.000
+54005.00      0.000      0.000       0.000       0.000
+54006.00      0.000      0.000       0.000       0.000
+54007.00      0.000      0.000       0.000       0.000
+54008.00      0.000      0.000       0.000       0.000
+54009.00      0.000      0.000       0.000       0.000
+54010.00      0.000      0.000       0.000       0.000
+54011.00      0.000      0.000       0.000       0.000
+54012.00      0.000      0.000       0.000       0.000
+54013.00      0.000      0.000       0.000       0.000
+54014.00      0.000      0.000       0.000       0.000
+54015.00      0.000      0.000       0.000       0.000
+54016.00      0.000      0.000       0.000       0.000
+54017.00      0.000      0.000       0.000       0.000
+54018.00      0.000      0.000       0.000       0.000
+54019.00      0.000      0.000       0.000       0.000
+54020.00      0.000      0.000       0.000       0.000
+54021.00      0.000      0.000       0.000       0.000
+54022.00      0.000      0.000       0.000       0.000
+54023.00      0.000      0.000       0.000       0.000
+54024.00      0.000      0.000       0.000       0.000
+54025.00      0.000      0.000       0.000       0.000
+54026.00      0.000      0.000       0.000       0.000
+54027.00      0.000      0.000       0.000       0.000
+54028.00      0.000      0.000       0.000       0.000
+54029.00      0.000      0.000       0.000       0.000
+54030.00      0.000      0.000       0.000       0.000
+54031.00      0.000      0.000       0.000       0.000
+54032.00      0.000      0.000       0.000       0.000
+54033.00      0.000      0.000       0.000       0.000
+54034.00      0.000      0.000       0.000       0.000
+54035.00      0.000      0.000       0.000       0.000
+54036.00      0.000      0.000       0.000       0.000
+54037.00      0.000      0.000       0.000       0.000
+54038.00      0.000      0.000       0.000       0.000
+54039.00      0.000      0.000       0.000       0.000
+54040.00      0.000      0.000       0.000       0.000
+54041.00      0.000      0.000       0.000       0.000
+54042.00      0.000      0.000       0.000       0.000
+54043.00      0.000      0.000       0.000       0.000
+54044.00      0.000      0.000       0.000       0.000
+54045.00      0.000      0.000       0.000       0.000
+54046.00      0.000      0.000       0.000       0.000
+54047.00      0.000      0.000       0.000       0.000
+54048.00      0.000      0.000       0.000       0.000
+54049.00      0.000      0.000       0.000       0.000
+54050.00      0.000      0.000       0.000       0.000
+54051.00      0.000      0.000       0.000       0.000
+54052.00      0.000      0.000       0.000       0.000
+54053.00      0.000      0.000       0.000       0.000
+54054.00      0.000      0.000       0.000       0.000
+54055.00      0.000      0.000       0.000       0.000
+54056.00      0.000      0.000       0.000       0.000
+54057.00      0.000      0.000       0.000       0.000
+54058.00      0.000      0.000       0.000       0.000
+54059.00      0.000      0.000       0.000       0.000
+54060.00      0.000      0.000       0.000       0.000
+54061.00      0.000      0.000       0.000       0.000
+54062.00      0.000      0.000       0.000       0.000
+54063.00      0.000      0.000       0.000       0.000
+54064.00      0.000      0.000       0.000       0.000
+54065.00      0.000      0.000       0.000       0.000
+54066.00      0.000      0.000       0.000       0.000
+54067.00      0.000      0.000       0.000       0.000
+54068.00      0.000      0.000       0.000       0.000
+54069.00      0.000      0.000       0.000       0.000
+54070.00      0.000      0.000       0.000       0.000
+54071.00      0.000      0.000       0.000       0.000
+54072.00      0.000      0.000       0.000       0.000
+54073.00      0.000      0.000       0.000       0.000
+54074.00      0.000      0.000       0.000       0.000
+54075.00      0.000      0.000       0.000       0.000
+54076.00      0.000      0.000       0.000       0.000
+54077.00      0.000      0.000       0.000       0.000
+54078.00      0.000      0.000       0.000       0.000
+54079.00      0.000      0.000       0.000       0.000
+54080.00      0.000      0.000       0.000       0.000
+54081.00      0.000      0.000       0.000       0.000
+54082.00      0.000      0.000       0.000       0.000
+54083.00      0.000      0.000       0.000       0.000
+54084.00      0.000      0.000       0.000       0.000
+54085.00      0.000      0.000       0.000       0.000
+54086.00      0.000      0.000       0.000       0.000
+54087.00      0.000      0.000       0.000       0.000
+54088.00      0.000      0.000       0.000       0.000
+54089.00      0.000      0.000       0.000       0.000
+54090.00      0.000      0.000       0.000       0.000
+54091.00      0.000      0.000       0.000       0.000
+54092.00      0.000      0.000       0.000       0.000
+54093.00      0.000      0.000       0.000       0.000
+54094.00      0.000      0.000       0.000       0.000
+54095.00      0.000      0.000       0.000       0.000
+54096.00      0.000      0.000       0.000       0.000
+54097.00      0.000      0.000       0.000       0.000
+54098.00      0.000      0.000       0.000       0.000
+54099.00      0.000      0.000       0.000       0.000
+54100.00      0.000      0.000       0.000       0.000
+54101.00      0.000      0.000       0.000       0.000
+54102.00      0.000      0.000       0.000       0.000
+54103.00      0.000      0.000       0.000       0.000
+54104.00      0.000      0.000       0.000       0.000
+54105.00      0.000      0.000       0.000       0.000
+54106.00      0.000      0.000       0.000       0.000
+54107.00      0.000      0.000       0.000       0.000
+54108.00      0.000      0.000       0.000       0.000
+54109.00      0.000      0.000       0.000       0.000
+54110.00      0.000      0.000       0.000       0.000
+54111.00      0.000      0.000       0.000       0.000
+54112.00      0.000      0.000       0.000       0.000
+54113.00      0.000      0.000       0.000       0.000
+54114.00      0.000      0.000       0.000       0.000
+54115.00      0.000      0.000       0.000       0.000
+54116.00      0.000      0.000       0.000       0.000
+54117.00      0.000      0.000       0.000       0.000
+54118.00      0.000      0.000       0.000       0.000
+54119.00      0.000      0.000       0.000       0.000
+54120.00      0.000      0.000       0.000       0.000
+54121.00      0.000      0.000       0.000       0.000
+54122.00      0.000      0.000       0.000       0.000
+54123.00      0.000      0.000       0.000       0.000
+54124.00      0.000      0.000       0.000       0.000
+54125.00      0.000      0.000       0.000       0.000
+54126.00      0.000      0.000       0.000       0.000
+54127.00      0.000      0.000       0.000       0.000
+54128.00      0.000      0.000       0.000       0.000
+54129.00      0.000      0.000       0.000       0.000
+54130.00      0.000      0.000       0.000       0.000
+54131.00      0.000      0.000       0.000       0.000
+54132.00      0.000      0.000       0.000       0.000
+54133.00      0.000      0.000       0.000       0.000
+54134.00      0.000      0.000       0.000       0.000
+54135.00      0.000      0.000       0.000       0.000
+54136.00      0.000      0.000       0.000       0.000
+54137.00      0.000      0.000       0.000       0.000
+54138.00      0.000      0.000       0.000       0.000
+54139.00      0.000      0.000       0.000       0.000
Index: /tags/sj_tags/sj_root_20080929/psLib/share/finals_all.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/finals_all.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/finals_all.dat	(revision 22322)
@@ -0,0 +1,12232 @@
+#
+#  Stripped version of finals.all.orig file
+#
+#
+#          Bull A     Bull A      Bull A      Bull B      Bull B      Bull B
+#  MJD     PM-x       PM-y        UT1-UTC     PM-x        PM-y        UT1-UTC
+#
+41684.00   0.120724   0.137066   0.8084319    0.143000    0.137000    0.8075000
+41685.00   0.118971   0.135756   0.8056304    0.141000    0.134000    0.8044000
+41686.00   0.117218   0.134448   0.8028036    0.139000    0.131000    0.8012000
+41687.00   0.115464   0.133144   0.7998870    0.137000    0.128000    0.7981000
+41688.00   0.113708   0.131846   0.7968285    0.136000    0.126000    0.7949000
+41689.00   0.111948   0.130558   0.7935963    0.134000    0.123000    0.7918000
+41690.00   0.110180   0.129282   0.7901851    0.132000    0.122000    0.7887000
+41691.00   0.108401   0.128020   0.7866194    0.130000    0.120000    0.7855000
+41692.00   0.106607   0.126775   0.7829530    0.128000    0.119000    0.7824000
+41693.00   0.104795   0.125551   0.7792628    0.126000    0.117000    0.7792000
+41694.00   0.102960   0.124351   0.7756359    0.124000    0.116000    0.7761000
+41695.00   0.101103   0.123180   0.7721500    0.122000    0.114000    0.7730000
+41696.00   0.099219   0.122040   0.7688519    0.120000    0.113000    0.7698000
+41697.00   0.097304   0.120936   0.7657376    0.118000    0.111000    0.7667000
+41698.00   0.095355   0.119870   0.7627466    0.116000    0.110000    0.7635000
+41699.00   0.093368   0.118846   0.7597749    0.114000    0.108000    0.7604000
+41700.00   0.091341   0.117865   0.7567072    0.112000    0.106000    0.7573000
+41701.00   0.089273   0.116926   0.7534582    0.110000    0.105000    0.7541000
+41702.00   0.087167   0.116031   0.7500021    0.107000    0.103000    0.7510000
+41703.00   0.085025   0.115178   0.7463792    0.105000    0.102000    0.7478000
+41704.00   0.082853   0.114370   0.7426762    0.103000    0.100000    0.7447000
+41705.00   0.080657   0.113605   0.7389931    0.101000    0.098000    0.7415000
+41706.00   0.078440   0.112884   0.7354139    0.099000    0.097000    0.7383000
+41707.00   0.076205   0.112202   0.7319919    0.096000    0.095000    0.7352000
+41708.00   0.073954   0.111559   0.7287489    0.094000    0.094000    0.7320000
+41709.00   0.071691   0.110952   0.7256811    0.092000    0.092000    0.7288000
+41710.00   0.069420   0.110378   0.7227643    0.090000    0.091000    0.7256000
+41711.00   0.067147   0.109836   0.7199568    0.088000    0.089000    0.7224000
+41712.00   0.064874   0.109325   0.7172012    0.086000    0.088000    0.7193000
+41713.00   0.062607   0.108846   0.7144307    0.084000    0.087000    0.7161000
+41714.00   0.060349   0.108400   0.7115769    0.082000    0.086000    0.7129000
+41715.00   0.058100   0.107987   0.7085797    0.079000    0.084000    0.7096000
+41716.00   0.055865   0.107608   0.7053972    0.077000    0.083000    0.7063000
+41717.00   0.053644   0.107263   0.7020146    0.075000    0.082000    0.7031000
+41718.00   0.051441   0.106954   0.6984511    0.073000    0.080000    0.6998000
+41719.00   0.049257   0.106681   0.6947614    0.071000    0.079000    0.6965000
+41720.00   0.047093   0.106442   0.6910290    0.069000    0.080000    0.6932000
+41721.00   0.044951   0.106239   0.6873489    0.067000    0.081000    0.6899000
+41722.00   0.042831   0.106070   0.6838052    0.066000    0.082000    0.6866000
+41723.00   0.040732   0.105935   0.6804490    0.064000    0.083000    0.6833000
+41724.00   0.038653   0.105832   0.6772827    0.062000    0.085000    0.6800000
+41725.00   0.036593   0.105760   0.6742568    0.060000    0.086000    0.6767000
+41726.00   0.034550   0.105717   0.6712808    0.058000    0.087000    0.6734000
+41727.00   0.032521   0.105702   0.6682469    0.057000    0.088000    0.6701000
+41728.00   0.030505   0.105715   0.6650607    0.055000    0.089000    0.6668000
+41729.00   0.028503   0.105755   0.6616712    0.053000    0.090000    0.6635000
+41730.00   0.026512   0.105824   0.6580860    0.051000    0.091000    0.6601000
+41731.00   0.024531   0.105922   0.6543676    0.049000    0.092000    0.6568000
+41732.00   0.022556   0.106048   0.6506111    0.047000    0.093000    0.6534000
+41733.00   0.020582   0.106203   0.6469136    0.045000    0.094000    0.6501000
+41734.00   0.018606   0.106384   0.6433499    0.044000    0.095000    0.6467000
+41735.00   0.016623   0.106592   0.6399606    0.042000    0.096000    0.6433000
+41736.00   0.014632   0.106828   0.6367535    0.040000    0.097000    0.6400000
+41737.00   0.012634   0.107093   0.6337102    0.038000    0.098000    0.6366000
+41738.00   0.010628   0.107386   0.6307923    0.036000    0.099000    0.6333000
+41739.00   0.008615   0.107709   0.6279462    0.034000    0.100000    0.6299000
+41740.00   0.006593   0.108060   0.6251074    0.032000    0.101000    0.6265000
+41741.00   0.004564   0.108438   0.6222069    0.030000    0.102000    0.6232000
+41742.00   0.002526   0.108842   0.6191790    0.028000    0.102000    0.6198000
+41743.00   0.000479   0.109270   0.6159706    0.026000    0.103000    0.6165000
+41744.00  -0.001578   0.109721   0.6125505    0.024000    0.104000    0.6131000
+41745.00  -0.003643   0.110195   0.6089196    0.022000    0.105000    0.6097000
+41746.00  -0.005713   0.110692   0.6051180    0.020000    0.106000    0.6063000
+41747.00  -0.007786   0.111214   0.6012241    0.018000    0.106000    0.6030000
+41748.00  -0.009857   0.111761   0.5973410    0.016000    0.107000    0.5996000
+41749.00  -0.011922   0.112336   0.5935706    0.014000    0.108000    0.5962000
+41750.00  -0.013977   0.112940   0.5899833    0.012000    0.109000    0.5928000
+41751.00  -0.016019   0.113573   0.5865962    0.009000    0.109000    0.5893000
+41752.00  -0.018040   0.114237   0.5833682    0.007000    0.110000    0.5859000
+41753.00  -0.020032   0.114933   0.5802133    0.004000    0.111000    0.5824000
+41754.00  -0.021990   0.115661   0.5770261    0.002000    0.112000    0.5790000
+41755.00  -0.023907   0.116421   0.5737111    0.000000    0.112000    0.5755000
+41756.00  -0.025780   0.117212   0.5702072   -0.003000    0.113000    0.5720000
+41757.00  -0.027610   0.118036   0.5665027   -0.005000    0.114000    0.5685000
+41758.00  -0.029400   0.118890   0.5626361   -0.008000    0.114000    0.5650000
+41759.00  -0.031155   0.119773   0.5586838   -0.010000    0.115000    0.5615000
+41760.00  -0.032878   0.120683   0.5547386   -0.012000    0.116000    0.5579000
+41761.00  -0.034573   0.121619   0.5508857   -0.015000    0.117000    0.5544000
+41762.00  -0.036242   0.122582   0.5471853   -0.017000    0.118000    0.5508000
+41763.00  -0.037885   0.123571   0.5436641   -0.020000    0.119000    0.5473000
+41764.00  -0.039504   0.124588   0.5403179   -0.022000    0.120000    0.5437000
+41765.00  -0.041099   0.125635   0.5371173   -0.024000    0.120000    0.5401000
+41766.00  -0.042673   0.126712   0.5340153   -0.027000    0.121000    0.5365000
+41767.00  -0.044229   0.127821   0.5309537   -0.029000    0.122000    0.5330000
+41768.00  -0.045771   0.128964   0.5278687   -0.032000    0.123000    0.5294000
+41769.00  -0.047302   0.130142   0.5246976   -0.034000    0.124000    0.5258000
+41770.00  -0.048828   0.131356   0.5213856   -0.036000    0.125000    0.5222000
+41771.00  -0.050351   0.132605   0.5178924   -0.039000    0.127000    0.5186000
+41772.00  -0.051875   0.133891   0.5142009   -0.041000    0.128000    0.5149000
+41773.00  -0.053404   0.135214   0.5103278   -0.044000    0.129000    0.5113000
+41774.00  -0.054940   0.136573   0.5063304   -0.046000    0.131000    0.5077000
+41775.00  -0.056488   0.137969   0.5023026   -0.048000    0.132000    0.5041000
+41776.00  -0.058053   0.139402   0.4983553   -0.051000    0.133000    0.5006000
+41777.00  -0.059639   0.140872   0.4945843   -0.053000    0.134000    0.4970000
+41778.00  -0.061247   0.142377   0.4910366   -0.056000    0.136000    0.4935000
+41779.00  -0.062874   0.143917   0.4876939   -0.058000    0.137000    0.4899000
+41780.00  -0.064517   0.145494   0.4844787   -0.061000    0.139000    0.4864000
+41781.00  -0.066174   0.147108   0.4812836   -0.063000    0.141000    0.4829000
+41782.00  -0.067840   0.148757   0.4780073   -0.066000    0.143000    0.4794000
+41783.00  -0.069512   0.150437   0.4745829   -0.068000    0.145000    0.4759000
+41784.00  -0.071188   0.152143   0.4709918   -0.071000    0.147000    0.4724000
+41785.00  -0.072868   0.153873   0.4672608   -0.073000    0.148000    0.4689000
+41786.00  -0.074547   0.155619   0.4634499   -0.076000    0.150000    0.4655000
+41787.00  -0.076226   0.157377   0.4596361   -0.078000    0.152000    0.4620000
+41788.00  -0.077898   0.159141   0.4558961   -0.081000    0.154000    0.4586000
+41789.00  -0.079559   0.160904   0.4522916   -0.083000    0.156000    0.4551000
+41790.00  -0.081206   0.162664   0.4488589   -0.085000    0.158000    0.4517000
+41791.00  -0.082836   0.164416   0.4456047   -0.086000    0.160000    0.4483000
+41792.00  -0.084445   0.166159   0.4425079   -0.088000    0.162000    0.4448000
+41793.00  -0.086031   0.167888   0.4395262   -0.090000    0.164000    0.4414000
+41794.00  -0.087594   0.169602   0.4366037   -0.092000    0.166000    0.4380000
+41795.00  -0.089132   0.171299   0.4336788   -0.093000    0.168000    0.4346000
+41796.00  -0.090646   0.172977   0.4306908   -0.095000    0.170000    0.4312000
+41797.00  -0.092135   0.174637   0.4275866   -0.097000    0.172000    0.4277000
+41798.00  -0.093600   0.176280   0.4243250   -0.098000    0.174000    0.4243000
+41799.00  -0.095039   0.177908   0.4208818   -0.100000    0.176000    0.4209000
+41800.00  -0.096452   0.179524   0.4172571   -0.101000    0.178000    0.4175000
+41801.00  -0.097839   0.181129   0.4134837   -0.102000    0.180000    0.4141000
+41802.00  -0.099202   0.182728   0.4096311   -0.103000    0.181000    0.4108000
+41803.00  -0.100543   0.184322   0.4057984   -0.104000    0.183000    0.4074000
+41804.00  -0.101860   0.185910   0.4020902   -0.106000    0.185000    0.4040000
+41805.00  -0.103149   0.187492   0.3985825   -0.107000    0.187000    0.4006000
+41806.00  -0.104401   0.189067   0.3952917   -0.108000    0.189000    0.3972000
+41807.00  -0.105607   0.190634   0.3921651   -0.109000    0.190000    0.3939000
+41808.00  -0.106760   0.192195   0.3891000   -0.110000    0.192000    0.3905000
+41809.00  -0.107853   0.193751   0.3859841   -0.111000    0.194000    0.3871000
+41810.00  -0.108882   0.195303   0.3827358   -0.112000    0.196000    0.3838000
+41811.00  -0.109843   0.196854   0.3793280   -0.112000    0.198000    0.3805000
+41812.00  -0.110735   0.198402   0.3757875   -0.113000    0.200000    0.3771000
+41813.00  -0.111557   0.199950   0.3721774   -0.113000    0.202000    0.3738000
+41814.00  -0.112313   0.201499   0.3685739   -0.114000    0.204000    0.3705000
+41815.00  -0.113006   0.203052   0.3650488   -0.114000    0.205000    0.3672000
+41816.00  -0.113643   0.204611   0.3616590   -0.115000    0.207000    0.3640000
+41817.00  -0.114229   0.206179   0.3584406   -0.115000    0.209000    0.3607000
+41818.00  -0.114769   0.207756   0.3554062   -0.116000    0.211000    0.3575000
+41819.00  -0.115267   0.209342   0.3525437   -0.116000    0.213000    0.3542000
+41820.00  -0.115729   0.210937   0.3498182   -0.116000    0.214000    0.3510000
+41821.00  -0.116160   0.212543   0.3471778   -0.116000    0.216000    0.3479000
+41822.00  -0.116560   0.214161   0.3445621   -0.116000    0.217000    0.3447000
+41823.00  -0.116928   0.215794   0.3419107   -0.116000    0.219000    0.3416000
+41824.00  -0.117260   0.217446   0.3391710   -0.117000    0.220000    0.3384000
+41825.00  -0.117551   0.219118   0.3363033   -0.117000    0.221000    0.3353000
+41826.00  -0.117796   0.220811   0.3332839   -0.117000    0.223000    0.3321000
+41827.00  -0.117988   0.222526   0.3301084   -0.117000    0.224000    0.3290000
+41828.00  -0.118122   0.224260   0.3267957   -0.117000    0.226000    0.3258000
+41829.00  -0.118191   0.226015   0.3233929   -0.117000    0.227000    0.3227000
+41830.00  -0.118187   0.227791   0.3199751   -0.117000    0.228000    0.3196000
+41831.00  -0.118104   0.229587   0.3166341   -0.117000    0.230000    0.3166000
+41832.00  -0.117943   0.231402   0.3134538   -0.117000    0.231000    0.3135000
+41833.00  -0.117703   0.233238   0.3104780   -0.117000    0.233000    0.3105000
+41834.00  -0.117388   0.235091   0.3076868   -0.117000    0.234000    0.3074000
+41835.00  -0.117002   0.236962   0.3049978   -0.117000    0.235000    0.3044000
+41836.00  -0.116549   0.238846   0.3022950   -0.117000    0.237000    0.3014000
+41837.00  -0.116036   0.240738   0.2994746   -0.117000    0.238000    0.2984000
+41838.00  -0.115466   0.242636   0.2964846   -0.117000    0.240000    0.2954000
+41839.00  -0.114846   0.244536   0.2933393   -0.117000    0.241000    0.2924000
+41840.00  -0.114180   0.246436   0.2901054   -0.117000    0.243000    0.2895000
+41841.00  -0.113474   0.248333   0.2868723   -0.116000    0.245000    0.2865000
+41842.00  -0.112728   0.250227   0.2837227   -0.116000    0.246000    0.2836000
+41843.00  -0.111943   0.252117   0.2807165   -0.116000    0.248000    0.2806000
+41844.00  -0.111124   0.254000   0.2778874   -0.116000    0.250000    0.2777000
+41845.00  -0.110279   0.255878   0.2752458   -0.115000    0.252000    0.2748000
+41846.00  -0.109416   0.257745   0.2727809   -0.115000    0.254000    0.2720000
+41847.00  -0.108543   0.259600   0.2704624   -0.115000    0.255000    0.2691000
+41848.00  -0.107669   0.261438   0.2682428   -0.114000    0.257000    0.2663000
+41849.00  -0.106798   0.263258   0.2660632   -0.114000    0.259000    0.2634000
+41850.00  -0.105935   0.265060   0.2638618   -0.113000    0.261000    0.2607000
+41851.00  -0.105082   0.266846   0.2615839   -0.112000    0.263000    0.2580000
+41852.00  -0.104241   0.268620   0.2591881   -0.111000    0.264000    0.2552000
+41853.00  -0.103414   0.270385   0.2566509   -0.110000    0.266000    0.2525000
+41854.00  -0.102602   0.272147   0.2539691   -0.110000    0.268000    0.2498000
+41855.00  -0.101802   0.273910   0.2511601   -0.109000    0.270000    0.2472000
+41856.00  -0.101014   0.275678   0.2482631   -0.108000    0.272000    0.2446000
+41857.00  -0.100233   0.277455   0.2453372   -0.107000    0.273000    0.2421000
+41858.00  -0.099456   0.279243   0.2424569   -0.106000    0.275000    0.2395000
+41859.00  -0.098677   0.281043   0.2396968   -0.105000    0.277000    0.2369000
+41860.00  -0.097889   0.282855   0.2371084   -0.103000    0.279000    0.2344000
+41861.00  -0.097085   0.284679   0.2346963   -0.102000    0.281000    0.2320000
+41862.00  -0.096256   0.286516   0.2324062   -0.100000    0.282000    0.2295000
+41863.00  -0.095395   0.288366   0.2301356   -0.099000    0.284000    0.2271000
+41864.00  -0.094499   0.290226   0.2277697   -0.097000    0.286000    0.2246000
+41865.00  -0.093566   0.292091   0.2252259   -0.095000    0.288000    0.2223000
+41866.00  -0.092596   0.293957   0.2224869   -0.094000    0.290000    0.2200000
+41867.00  -0.091588   0.295818   0.2196042   -0.092000    0.291000    0.2177000
+41868.00  -0.090547   0.297669   0.2166747   -0.091000    0.293000    0.2154000
+41869.00  -0.089478   0.299504   0.2138025   -0.089000    0.295000    0.2131000
+41870.00  -0.088387   0.301319   0.2110689   -0.087000    0.296000    0.2108000
+41871.00  -0.087280   0.303108   0.2085187   -0.085000    0.298000    0.2085000
+41872.00  -0.086167   0.304868   0.2061633   -0.083000    0.299000    0.2062000
+41873.00  -0.085056   0.306592   0.2039890   -0.081000    0.300000    0.2039000
+41874.00  -0.083952   0.308278   0.2019641   -0.080000    0.302000    0.2016000
+41875.00  -0.082861   0.309919   0.2000425   -0.078000    0.303000    0.1993000
+41876.00  -0.081780   0.311513   0.1981661   -0.076000    0.304000    0.1970000
+41877.00  -0.080706   0.313057   0.1962715   -0.074000    0.305000    0.1947000
+41878.00  -0.079639   0.314550   0.1942984   -0.072000    0.307000    0.1924000
+41879.00  -0.078576   0.315992   0.1921991   -0.070000    0.308000    0.1901000
+41880.00  -0.077516   0.317382   0.1899451   -0.068000    0.309000    0.1877000
+41881.00  -0.076456   0.318721   0.1875310   -0.066000    0.310000    0.1854000
+41882.00  -0.075393   0.320007   0.1849750   -0.064000    0.311000    0.1830000
+41883.00  -0.074324   0.321240   0.1823177   -0.062000    0.312000    0.1807000
+41884.00  -0.073249   0.322420   0.1796175   -0.061000    0.314000    0.1783000
+41885.00  -0.072165   0.323546   0.1769433   -0.059000    0.315000    0.1759000
+41886.00  -0.071074   0.324618   0.1743623   -0.057000    0.316000    0.1735000
+41887.00  -0.069974   0.325637   0.1719236   -0.055000    0.317000    0.1712000
+41888.00  -0.068865   0.326605   0.1696410   -0.053000    0.318000    0.1688000
+41889.00  -0.067746   0.327521   0.1674796   -0.051000    0.319000    0.1664000
+41890.00  -0.066615   0.328385   0.1653574   -0.049000    0.320000    0.1639000
+41891.00  -0.065470   0.329200   0.1631654   -0.047000    0.321000    0.1615000
+41892.00  -0.064308   0.329966   0.1608038   -0.046000    0.322000    0.1590000
+41893.00  -0.063127   0.330684   0.1582205   -0.044000    0.323000    0.1566000
+41894.00  -0.061922   0.331356   0.1554320   -0.042000    0.325000    0.1541000
+41895.00  -0.060690   0.331984   0.1525183   -0.040000    0.326000    0.1516000
+41896.00  -0.059426   0.332571   0.1495917   -0.038000    0.327000    0.1491000
+41897.00  -0.058129   0.333123   0.1467587   -0.037000    0.328000    0.1467000
+41898.00  -0.056796   0.333641   0.1440916   -0.035000    0.329000    0.1442000
+41899.00  -0.055426   0.334129   0.1416190   -0.033000    0.330000    0.1417000
+41900.00  -0.054018   0.334587   0.1393332   -0.031000    0.331000    0.1392000
+41901.00  -0.052571   0.335018   0.1372021   -0.030000    0.331000    0.1368000
+41902.00  -0.051087   0.335422   0.1351788   -0.028000    0.332000    0.1343000
+41903.00  -0.049566   0.335801   0.1332063   -0.027000    0.333000    0.1319000
+41904.00  -0.048009   0.336158   0.1312213   -0.025000    0.334000    0.1294000
+41905.00  -0.046418   0.336495   0.1291610   -0.023000    0.334000    0.1269000
+41906.00  -0.044793   0.336814   0.1269711   -0.022000    0.335000    0.1244000
+41907.00  -0.043138   0.337117   0.1246142   -0.020000    0.336000    0.1219000
+41908.00  -0.041457   0.337408   0.1220762   -0.019000    0.336000    0.1194000
+41909.00  -0.039753   0.337689   0.1193688   -0.017000    0.337000    0.1169000
+41910.00  -0.038027   0.337962   0.1165303   -0.016000    0.337000    0.1144000
+41911.00  -0.036279   0.338231   0.1136211   -0.014000    0.337000    0.1118000
+41912.00  -0.034509   0.338497   0.1107146   -0.013000    0.338000    0.1093000
+41913.00  -0.032716   0.338764   0.1078820   -0.011000    0.338000    0.1067000
+41914.00  -0.030904   0.339034   0.1051753   -0.010000    0.338000    0.1042000
+41915.00  -0.029072   0.339312   0.1026111   -0.009000    0.338000    0.1016000
+41916.00  -0.027225   0.339599   0.1001622   -0.007000    0.338000    0.0990000
+41917.00  -0.025364   0.339898   0.0977587   -0.006000    0.339000    0.0965000
+41918.00  -0.023491   0.340210   0.0953036   -0.004000    0.339000    0.0939000
+41919.00  -0.021609   0.340534   0.0926991   -0.003000    0.339000    0.0913000
+41920.00  -0.019719   0.340866   0.0898766   -0.002000    0.339000    0.0887000
+41921.00  -0.017823   0.341204   0.0868220   -0.001000    0.339000    0.0860000
+41922.00  -0.015923   0.341546   0.0835842    0.000000    0.339000    0.0834000
+41923.00  -0.014024   0.341889   0.0802614    0.001000    0.339000    0.0807000
+41924.00  -0.012126   0.342230   0.0769694    0.003000    0.340000    0.0781000
+41925.00  -0.010233   0.342570   0.0738075    0.004000    0.340000    0.0754000
+41926.00  -0.008345   0.342904   0.0708351    0.005000    0.340000    0.0727000
+41927.00  -0.006462   0.343233   0.0680673    0.006000    0.340000    0.0699000
+41928.00  -0.004582   0.343554   0.0654838    0.007000    0.340000    0.0672000
+41929.00  -0.002703   0.343867   0.0630419    0.008000    0.340000    0.0645000
+41930.00  -0.000823   0.344170   0.0606870    0.010000    0.340000    0.0617000
+41931.00   0.001061   0.344464   0.0583584    0.011000    0.340000    0.0589000
+41932.00   0.002946   0.344750   0.0559943    0.013000    0.341000    0.0561000
+41933.00   0.004833   0.345026   0.0535381    0.015000    0.341000    0.0533000
+41934.00   0.006719   0.345293   0.0509461    0.017000    0.341000    0.0505000
+41935.00   0.008606   0.345551   0.0481927    0.018000    0.341000    0.0476000
+41936.00   0.010491   0.345797   0.0452758    0.020000    0.341000    0.0447000
+41937.00   0.012375   0.346029   0.0422207    0.022000    0.342000    0.0418000
+41938.00   0.014258   0.346244   0.0390798    0.023000    0.342000    0.0389000
+41939.00   0.016138   0.346440   0.0359271    0.025000    0.342000    0.0360000
+41940.00   0.018015   0.346613   0.0328428    0.027000    0.342000    0.0331000
+41941.00   0.019888   0.346762   0.0298925    0.028000    0.343000    0.0301000
+41942.00   0.021754   0.346884   0.0271064    0.030000    0.343000    0.0272000
+41943.00   0.023611   0.346978   0.0244669    0.031000    0.344000    0.0242000
+41944.00   0.025456   0.347042   0.0219114    0.033000    0.344000    0.0213000
+41945.00   0.027289   0.347074   0.0193484    0.035000    0.344000    0.0183000
+41946.00   0.029108   0.347072   0.0166814    0.036000    0.345000    0.0153000
+41947.00   0.030916   0.347036   0.0138355    0.038000    0.345000    0.0123000
+41948.00   0.032711   0.346966   0.0107781    0.039000    0.346000    0.0093000
+41949.00   0.034492   0.346860   0.0075291    0.041000    0.346000    0.0063000
+41950.00   0.036259   0.346719   0.0041565    0.042000    0.346000    0.0033000
+41951.00   0.038008   0.346543   0.0007577    0.044000    0.346000    0.0002000
+41952.00   0.039739   0.346333  -0.0025677    0.045000    0.347000   -0.0028000
+41953.00   0.041450   0.346086  -0.0057439    0.047000    0.347000   -0.0059000
+41954.00   0.043140   0.345801  -0.0087342    0.048000    0.347000   -0.0089000
+41955.00   0.044808   0.345476  -0.0115428    0.049000    0.347000   -0.0120000
+41956.00   0.046453   0.345111  -0.0142048    0.051000    0.347000   -0.0151000
+41957.00   0.048076   0.344705  -0.0167739    0.052000    0.348000   -0.0181000
+41958.00   0.049673   0.344259  -0.0193120    0.054000    0.348000   -0.0212000
+41959.00   0.051242   0.343773  -0.0218818    0.055000    0.348000   -0.0243000
+41960.00   0.052777   0.343248  -0.0245414    0.057000    0.348000   -0.0274000
+41961.00   0.054276   0.342684  -0.0273383    0.058000    0.348000   -0.0305000
+41962.00   0.055736   0.342080  -0.0303046    0.060000    0.347000   -0.0336000
+41963.00   0.057156   0.341436  -0.0334515    0.061000    0.347000   -0.0367000
+41964.00   0.058539   0.340754  -0.0367661    0.063000    0.347000   -0.0398000
+41965.00   0.059888   0.340035  -0.0402090    0.065000    0.347000   -0.0429000
+41966.00   0.061203   0.339282  -0.0437149    0.066000    0.347000   -0.0460000
+41967.00   0.062488   0.338497  -0.0472011    0.068000    0.346000   -0.0492000
+41968.00   0.063742   0.337684  -0.0505867    0.069000    0.346000   -0.0523000
+41969.00   0.064969   0.336845  -0.0538171    0.071000    0.346000   -0.0554000
+41970.00   0.066169   0.335980  -0.0568853    0.072000    0.346000   -0.0586000
+41971.00   0.067343   0.335085  -0.0598382    0.074000    0.345000   -0.0618000
+41972.00   0.068487   0.334159  -0.0627630    0.075000    0.345000   -0.0650000
+41973.00   0.069599   0.333202  -0.0657581    0.077000    0.344000   -0.0682000
+41974.00   0.070676   0.332213  -0.0689022    0.078000    0.344000   -0.0714000
+41975.00   0.071714   0.331195  -0.0722334    0.079000    0.343000   -0.0746000
+41976.00   0.072709   0.330147  -0.0757409    0.081000    0.343000   -0.0779000
+41977.00   0.073660   0.329075  -0.0793711    0.082000    0.342000   -0.0811000
+41978.00   0.074564   0.327981  -0.0830434    0.084000    0.342000   -0.0844000
+41979.00   0.075421   0.326871  -0.0866712    0.085000    0.341000   -0.0876000
+41980.00   0.076229   0.325751  -0.0901797    0.086000    0.340000   -0.0909000
+41981.00   0.076990   0.324625  -0.0935217    0.088000    0.339000   -0.0941000
+41982.00   0.077706   0.323499  -0.0966847    0.089000    0.339000   -0.0974000
+41983.00   0.078381   0.322377  -0.0996889    0.091000    0.338000   -0.1006000
+41984.00   0.079016   0.321265  -0.1025792    0.092000    0.337000   -0.1039000
+41985.00   0.079614   0.320168  -0.1054139    0.093000    0.336000   -0.1072000
+41986.00   0.080175   0.319086  -0.1082551    0.095000    0.335000   -0.1105000
+41987.00   0.080703   0.318022  -0.1111607    0.096000    0.335000   -0.1139000
+41988.00   0.081201   0.316975  -0.1141787    0.098000    0.334000   -0.1172000
+41989.00   0.081671   0.315947  -0.1173433    0.099000    0.333000   -0.1205000
+41990.00   0.082118   0.314936  -0.1206712    0.100000    0.332000   -0.1238000
+41991.00   0.082543   0.313944  -0.1241596    0.101000    0.331000   -0.1271000
+41992.00   0.082951   0.312970  -0.1277842    0.101000    0.330000   -0.1305000
+41993.00   0.083347   0.312016  -0.1314973    0.102000    0.329000   -0.1338000
+41994.00   0.083732   0.311082  -0.1352296    0.103000    0.328000   -0.1371000
+41995.00   0.084109   0.310169  -0.1389005    0.104000    0.326000   -0.1404000
+41996.00   0.084479   0.309275  -0.1424400    0.105000    0.325000   -0.1437000
+41997.00   0.084843   0.308399  -0.1458132    0.105000    0.324000   -0.1470000
+41998.00   0.085205   0.307541  -0.1490397    0.106000    0.323000   -0.1503000
+41999.00   0.085567   0.306698  -0.1521912    0.107000    0.322000   -0.1536000
+42000.00   0.085934   0.305868  -0.1553673    0.108000    0.321000   -0.1569000
+42001.00   0.086310   0.305050  -0.1586579    0.108000    0.320000   -0.1603000
+42002.00   0.086696   0.304242  -0.1621106    0.109000    0.318000   -0.1636000
+42003.00   0.087092   0.303442  -0.1657170    0.109000    0.317000   -0.1670000
+42004.00   0.087500   0.302650  -0.1694219    0.110000    0.316000   -0.1703000
+42005.00   0.087923   0.301867  -0.1731446    0.110000    0.315000   -0.1736000
+42006.00   0.088360   0.301092  -0.1768022    0.111000    0.314000   -0.1768000
+42007.00   0.088813   0.300322  -0.1803275    0.111000    0.312000   -0.1801000
+42008.00   0.089282   0.299559  -0.1836772    0.112000    0.311000   -0.1833000
+42009.00   0.089767   0.298800  -0.1868358    0.112000    0.310000   -0.1866000
+42010.00   0.090270   0.298044  -0.1898150    0.113000    0.309000   -0.1898000
+42011.00   0.090789   0.297288  -0.1926504    0.113000    0.308000   -0.1930000
+42012.00   0.091322   0.296531  -0.1953941    0.114000    0.307000   -0.1963000
+42013.00   0.091866   0.295769  -0.1981060    0.114000    0.306000   -0.1995000
+42014.00   0.092419   0.295002  -0.2008443    0.115000    0.305000   -0.2027000
+42015.00   0.092978   0.294225  -0.2036580    0.116000    0.303000   -0.2059000
+42016.00   0.093541   0.293436  -0.2065818    0.116000    0.302000   -0.2090000
+42017.00   0.094106   0.292631  -0.2096332    0.117000    0.301000   -0.2122000
+42018.00   0.094671   0.291809  -0.2128122    0.117000    0.300000   -0.2153000
+42019.00   0.095233   0.290966  -0.2161014    0.118000    0.299000   -0.2185000
+42020.00   0.095789   0.290100  -0.2194657    0.119000    0.298000   -0.2217000
+42021.00   0.096336   0.289208  -0.2228531    0.120000    0.296000   -0.2248000
+42022.00   0.096871   0.288288  -0.2261984    0.120000    0.295000   -0.2280000
+42023.00   0.097393   0.287336  -0.2294359    0.121000    0.293000   -0.2311000
+42024.00   0.097902   0.286351  -0.2325201    0.122000    0.292000   -0.2343000
+42025.00   0.098396   0.285330  -0.2354485    0.123000    0.291000   -0.2374000
+42026.00   0.098876   0.284272  -0.2382734    0.124000    0.289000   -0.2405000
+42027.00   0.099341   0.283177  -0.2410897    0.124000    0.288000   -0.2436000
+42028.00   0.099789   0.282043  -0.2440007    0.125000    0.286000   -0.2467000
+42029.00   0.100218   0.280870  -0.2470769    0.126000    0.285000   -0.2498000
+42030.00   0.100626   0.279656  -0.2503280    0.127000    0.283000   -0.2528000
+42031.00   0.101013   0.278397  -0.2537002    0.127000    0.282000   -0.2558000
+42032.00   0.101377   0.277095  -0.2570999    0.128000    0.280000   -0.2589000
+42033.00   0.101720   0.275751  -0.2604279    0.128000    0.279000   -0.2619000
+42034.00   0.102043   0.274366  -0.2636073    0.129000    0.277000   -0.2649000
+42035.00   0.102347   0.272944  -0.2665943    0.129000    0.275000   -0.2678000
+42036.00   0.102630   0.271487  -0.2693777    0.130000    0.274000   -0.2708000
+42037.00   0.102890   0.270000  -0.2719721    0.130000    0.272000   -0.2737000
+42038.00   0.103124   0.268487  -0.2744117    0.131000    0.271000   -0.2767000
+42039.00   0.103328   0.266952  -0.2767452    0.131000    0.269000   -0.2796000
+42040.00   0.103502   0.265400  -0.2790302    0.131000    0.267000   -0.2823000
+42041.00   0.103641   0.263832  -0.2813262    0.131000    0.265000   -0.2850000
+42042.00   0.103746   0.262251  -0.2836854    0.131000    0.263000   -0.2876000
+42043.00   0.103816   0.260657  -0.2861463    0.131000    0.261000   -0.2903000
+42044.00   0.103849   0.259051  -0.2887288    0.131000    0.260000   -0.2930000
+42045.00   0.103845   0.257437  -0.2914337    0.131000    0.258000   -0.2952000
+42046.00   0.103805   0.255813  -0.2942442    0.131000    0.256000   -0.2974000
+42047.00   0.103728   0.254183  -0.2971285    0.131000    0.254000   -0.2997000
+42048.00   0.103613   0.252548   0.6999577    0.131000    0.252000    0.6981000
+42049.00   0.103459   0.250912   0.6970678    0.131000    0.250000    0.6959000
+42050.00   0.103264   0.249280   0.6942575    0.130000    0.248000    0.6934000
+42051.00   0.103028   0.247655   0.6915715    0.129000    0.246000    0.6909000
+42052.00   0.102751   0.246038   0.6890254    0.129000    0.243000    0.6885000
+42053.00   0.102434   0.244432   0.6865897    0.128000    0.241000    0.6860000
+42054.00   0.102077   0.242835   0.6841878    0.127000    0.239000    0.6835000
+42055.00   0.101678   0.241249   0.6817150    0.126000    0.237000    0.6807000
+42056.00   0.101236   0.239674   0.6790766    0.125000    0.235000    0.6778000
+42057.00   0.100750   0.238110   0.6762273    0.125000    0.232000    0.6750000
+42058.00   0.100222   0.236557   0.6731927    0.124000    0.230000    0.6721000
+42059.00   0.099653   0.235016   0.6700606    0.123000    0.228000    0.6693000
+42060.00   0.099045   0.233489   0.6669475    0.122000    0.226000    0.6666000
+42061.00   0.098399   0.231975   0.6639589    0.120000    0.224000    0.6639000
+42062.00   0.097719   0.230476   0.6611611    0.119000    0.222000    0.6611000
+42063.00   0.097006   0.228990   0.6585751    0.118000    0.220000    0.6584000
+42064.00   0.096261   0.227517   0.6561854    0.117000    0.218000    0.6557000
+42065.00   0.095487   0.226057   0.6539538    0.115000    0.216000    0.6534000
+42066.00   0.094684   0.224609   0.6518298    0.114000    0.214000    0.6511000
+42067.00   0.093852   0.223173   0.6497560    0.113000    0.212000    0.6489000
+42068.00   0.092995   0.221752   0.6476724    0.111000    0.210000    0.6466000
+42069.00   0.092117   0.220345   0.6455229    0.110000    0.208000    0.6443000
+42070.00   0.091222   0.218956   0.6432632    0.109000    0.207000    0.6420000
+42071.00   0.090317   0.217587   0.6408669    0.108000    0.205000    0.6397000
+42072.00   0.089404   0.216242   0.6383288    0.106000    0.204000    0.6375000
+42073.00   0.088485   0.214922   0.6356643    0.105000    0.202000    0.6352000
+42074.00   0.087567   0.213632   0.6329062    0.104000    0.201000    0.6329000
+42075.00   0.086653   0.212376   0.6301000    0.103000    0.200000    0.6302000
+42076.00   0.085750   0.211155   0.6272985    0.102000    0.198000    0.6276000
+42077.00   0.084863   0.209973   0.6245540    0.100000    0.197000    0.6249000
+42078.00   0.083998   0.208833   0.6219084    0.099000    0.195000    0.6223000
+42079.00   0.083160   0.207738   0.6193803    0.098000    0.194000    0.6196000
+42080.00   0.082351   0.206691   0.6169531    0.097000    0.193000    0.6167000
+42081.00   0.081573   0.205695   0.6145682    0.096000    0.193000    0.6139000
+42082.00   0.080822   0.204752   0.6121330    0.095000    0.192000    0.6110000
+42083.00   0.080096   0.203862   0.6095460    0.094000    0.192000    0.6082000
+42084.00   0.079390   0.203027   0.6067332    0.093000    0.191000    0.6053000
+42085.00   0.078702   0.202247   0.6036800    0.092000    0.190000    0.6024000
+42086.00   0.078027   0.201524   0.6004433    0.091000    0.190000    0.5996000
+42087.00   0.077360   0.200856   0.5971340    0.090000    0.189000    0.5967000
+42088.00   0.076698   0.200241   0.5938779    0.089000    0.189000    0.5939000
+42089.00   0.076036   0.199679   0.5907764    0.088000    0.188000    0.5910000
+42090.00   0.075371   0.199166   0.5878817    0.087000    0.188000    0.5883000
+42091.00   0.074696   0.198701   0.5851957    0.086000    0.187000    0.5856000
+42092.00   0.074011   0.198281   0.5826839    0.086000    0.187000    0.5830000
+42093.00   0.073312   0.197903   0.5802936    0.085000    0.186000    0.5803000
+42094.00   0.072601   0.197565   0.5779652    0.084000    0.186000    0.5776000
+42095.00   0.071874   0.197261   0.5756383    0.083000    0.186000    0.5750000
+42096.00   0.071131   0.196990   0.5732552    0.082000    0.185000    0.5724000
+42097.00   0.070373   0.196746   0.5707666    0.082000    0.185000    0.5697000
+42098.00   0.069596   0.196527   0.5681383    0.081000    0.184000    0.5671000
+42099.00   0.068798   0.196328   0.5653568    0.080000    0.184000    0.5645000
+42100.00   0.067976   0.196145   0.5624308    0.079000    0.184000    0.5617000
+42101.00   0.067127   0.195974   0.5593901    0.078000    0.183000    0.5589000
+42102.00   0.066248   0.195811   0.5562812    0.077000    0.183000    0.5562000
+42103.00   0.065336   0.195653   0.5531613    0.076000    0.182000    0.5534000
+42104.00   0.064391   0.195500   0.5500884    0.076000    0.182000    0.5506000
+42105.00   0.063408   0.195349   0.5471104    0.075000    0.182000    0.5476000
+42106.00   0.062390   0.195202   0.5442519    0.074000    0.181000    0.5446000
+42107.00   0.061338   0.195056   0.5415040    0.073000    0.181000    0.5416000
+42108.00   0.060251   0.194913   0.5388205    0.072000    0.180000    0.5386000
+42109.00   0.059132   0.194771   0.5361227    0.071000    0.180000    0.5356000
+42110.00   0.057982   0.194630   0.5333157    0.070000    0.180000    0.5325000
+42111.00   0.056803   0.194488   0.5303155    0.068000    0.180000    0.5293000
+42112.00   0.055601   0.194343   0.5270781    0.067000    0.180000    0.5262000
+42113.00   0.054379   0.194194   0.5236205    0.066000    0.180000    0.5230000
+42114.00   0.053147   0.194042   0.5200213    0.065000    0.180000    0.5199000
+42115.00   0.051913   0.193888   0.5163977    0.063000    0.179000    0.5166000
+42116.00   0.050682   0.193733   0.5128685    0.062000    0.179000    0.5134000
+42117.00   0.049458   0.193578   0.5095191    0.061000    0.179000    0.5101000
+42118.00   0.048242   0.193426   0.5063829    0.059000    0.179000    0.5069000
+42119.00   0.047038   0.193276   0.5034451    0.058000    0.179000    0.5036000
+42120.00   0.045847   0.193131   0.5006586    0.056000    0.179000    0.5004000
+42121.00   0.044672   0.192991   0.4979625    0.055000    0.179000    0.4972000
+42122.00   0.043517   0.192859   0.4952943    0.053000    0.180000    0.4940000
+42123.00   0.042385   0.192733   0.4925950    0.052000    0.180000    0.4908000
+42124.00   0.041274   0.192617   0.4898130    0.050000    0.180000    0.4876000
+42125.00   0.040185   0.192508   0.4869085    0.048000    0.180000    0.4846000
+42126.00   0.039119   0.192409   0.4838591    0.047000    0.180000    0.4815000
+42127.00   0.038076   0.192317   0.4806634    0.045000    0.181000    0.4785000
+42128.00   0.037057   0.192231   0.4773418    0.044000    0.181000    0.4754000
+42129.00   0.036067   0.192146   0.4739344    0.042000    0.181000    0.4724000
+42130.00   0.035104   0.192061   0.4704966    0.040000    0.182000    0.4695000
+42131.00   0.034171   0.191977   0.4670908    0.039000    0.182000    0.4666000
+42132.00   0.033267   0.191893   0.4637744    0.037000    0.183000    0.4636000
+42133.00   0.032395   0.191811   0.4605841    0.036000    0.183000    0.4607000
+42134.00   0.031552   0.191732   0.4575226    0.034000    0.184000    0.4578000
+42135.00   0.030738   0.191656   0.4545535    0.032000    0.184000    0.4549000
+42136.00   0.029953   0.191584   0.4516067    0.031000    0.185000    0.4520000
+42137.00   0.029197   0.191517   0.4485951    0.029000    0.185000    0.4491000
+42138.00   0.028470   0.191456   0.4454372    0.028000    0.186000    0.4462000
+42139.00   0.027772   0.191399   0.4420806    0.026000    0.186000    0.4433000
+42140.00   0.027104   0.191349   0.4385193    0.024000    0.187000    0.4404000
+42141.00   0.026463   0.191303   0.4347997    0.023000    0.187000    0.4375000
+42142.00   0.025851   0.191261   0.4310112    0.021000    0.188000    0.4347000
+42143.00   0.025266   0.191223   0.4272623    0.020000    0.188000    0.4318000
+42144.00   0.024707   0.191187   0.4236496    0.018000    0.189000    0.4289000
+42145.00   0.024174   0.191153   0.4202328    0.016000    0.190000    0.4259000
+42146.00   0.023660   0.191121   0.4170238    0.015000    0.190000    0.4229000
+42147.00   0.023163   0.191091   0.4139931    0.013000    0.191000    0.4199000
+42148.00   0.022678   0.191064   0.4110859    0.012000    0.191000    0.4169000
+42149.00   0.022199   0.191040   0.4082387    0.010000    0.192000    0.4139000
+42150.00   0.021724   0.191017   0.4053908    0.009000    0.193000    0.4106000
+42151.00   0.021250   0.190994   0.4024893    0.008000    0.193000    0.4074000
+42152.00   0.020770   0.190971   0.3994923    0.006000    0.194000    0.4041000
+42153.00   0.020280   0.190949   0.3963728    0.005000    0.194000    0.4009000
+42154.00   0.019776   0.190925   0.3931219    0.004000    0.195000    0.3976000
+42155.00   0.019254   0.190901   0.3897500    0.003000    0.196000    0.3941000
+42156.00   0.018715   0.190875   0.3862870    0.002000    0.196000    0.3906000
+42157.00   0.018156   0.190850   0.3827794    0.000000    0.197000    0.3872000
+42158.00   0.017577   0.190827   0.3792856   -0.001000    0.197000    0.3837000
+42159.00   0.016976   0.190808   0.3758664   -0.002000    0.198000    0.3802000
+42160.00   0.016356   0.190795   0.3725704   -0.003000    0.198000    0.3767000
+42161.00   0.015719   0.190790   0.3694168   -0.004000    0.199000    0.3732000
+42162.00   0.015068   0.190796   0.3663835   -0.005000    0.199000    0.3696000
+42163.00   0.014407   0.190812   0.3634087   -0.006000    0.200000    0.3661000
+42164.00   0.013735   0.190839   0.3604073   -0.007000    0.200000    0.3626000
+42165.00   0.013053   0.190877   0.3572975   -0.007000    0.200000    0.3592000
+42166.00   0.012362   0.190927   0.3540256   -0.008000    0.201000    0.3558000
+42167.00   0.011662   0.190986   0.3505813   -0.009000    0.201000    0.3524000
+42168.00   0.010954   0.191053   0.3470003   -0.010000    0.202000    0.3490000
+42169.00   0.010240   0.191129   0.3433539   -0.011000    0.202000    0.3456000
+42170.00   0.009519   0.191213   0.3397321   -0.012000    0.202000    0.3424000
+42171.00   0.008791   0.191305   0.3362223   -0.013000    0.203000    0.3392000
+42172.00   0.008058   0.191406   0.3328887   -0.014000    0.203000    0.3359000
+42173.00   0.007323   0.191517   0.3297594   -0.015000    0.203000    0.3327000
+42174.00   0.006592   0.191636   0.3268237   -0.016000    0.204000    0.3295000
+42175.00   0.005869   0.191765   0.3240393   -0.016000    0.204000    0.3264000
+42176.00   0.005159   0.191901   0.3213473   -0.017000    0.204000    0.3233000
+42177.00   0.004465   0.192046   0.3186855   -0.018000    0.204000    0.3201000
+42178.00   0.003788   0.192197   0.3159986   -0.019000    0.205000    0.3170000
+42179.00   0.003132   0.192354   0.3132432   -0.020000    0.205000    0.3139000
+42180.00   0.002499   0.192516   0.3103906   -0.021000    0.205000    0.3109000
+42181.00   0.001889   0.192683   0.3074284   -0.021000    0.206000    0.3079000
+42182.00   0.001303   0.192856   0.3043618   -0.022000    0.206000    0.3050000
+42183.00   0.000740   0.193037   0.3012128   -0.022000    0.206000    0.3020000
+42184.00   0.000202   0.193225   0.2980181   -0.023000    0.207000    0.2990000
+42185.00  -0.000310   0.193423   0.2948268   -0.023000    0.207000    0.2960000
+42186.00  -0.000794   0.193631   0.2916945   -0.024000    0.207000    0.2930000
+42187.00  -0.001250   0.193850   0.2886731   -0.024000    0.207000    0.2900000
+42188.00  -0.001679   0.194079   0.2857950   -0.025000    0.208000    0.2870000
+42189.00  -0.002080   0.194321   0.2830565   -0.025000    0.208000    0.2840000
+42190.00  -0.002456   0.194577   0.2804098   -0.025000    0.208000    0.2810000
+42191.00  -0.002805   0.194845   0.2777733   -0.026000    0.209000    0.2779000
+42192.00  -0.003130   0.195128   0.2750574   -0.026000    0.209000    0.2749000
+42193.00  -0.003430   0.195427   0.2721975   -0.026000    0.209000    0.2718000
+42194.00  -0.003708   0.195744   0.2691772   -0.027000    0.210000    0.2688000
+42195.00  -0.003966   0.196082   0.2660328   -0.027000    0.210000    0.2659000
+42196.00  -0.004202   0.196445   0.2628389   -0.027000    0.210000    0.2630000
+42197.00  -0.004414   0.196839   0.2596849   -0.027000    0.210000    0.2600000
+42198.00  -0.004600   0.197266   0.2566525   -0.028000    0.211000    0.2571000
+42199.00  -0.004756   0.197730   0.2538000   -0.028000    0.211000    0.2542000
+42200.00  -0.004877   0.198235   0.2511548   -0.028000    0.211000    0.2515000
+42201.00  -0.004960   0.198781   0.2487125   -0.027000    0.211000    0.2488000
+42202.00  -0.005000   0.199370   0.2464406   -0.027000    0.212000    0.2462000
+42203.00  -0.004992   0.200001   0.2442871   -0.027000    0.212000    0.2435000
+42204.00  -0.004932   0.200676   0.2421917   -0.027000    0.212000    0.2408000
+42205.00  -0.004816   0.201393   0.2400963   -0.026000    0.212000    0.2384000
+42206.00  -0.004643   0.202153   0.2379537   -0.026000    0.212000    0.2359000
+42207.00  -0.004410   0.202956   0.2357321   -0.026000    0.213000    0.2335000
+42208.00  -0.004117   0.203801   0.2334170   -0.025000    0.213000    0.2310000
+42209.00  -0.003764   0.204685   0.2310113   -0.025000    0.213000    0.2286000
+42210.00  -0.003348   0.205605   0.2285338   -0.024000    0.213000    0.2264000
+42211.00  -0.002866   0.206557   0.2260158   -0.024000    0.213000    0.2242000
+42212.00  -0.002321   0.207536   0.2234977   -0.023000    0.214000    0.2220000
+42213.00  -0.001713   0.208538   0.2210256   -0.023000    0.214000    0.2198000
+42214.00  -0.001045   0.209555   0.2186454   -0.022000    0.214000    0.2176000
+42215.00  -0.000315   0.210579   0.2163925   -0.021000    0.214000    0.2155000
+42216.00   0.000476   0.211604   0.2142759   -0.021000    0.214000    0.2135000
+42217.00   0.001331   0.212624   0.2122651   -0.020000    0.215000    0.2114000
+42218.00   0.002251   0.213636   0.2102885   -0.020000    0.215000    0.2094000
+42219.00   0.003234   0.214635   0.2082511   -0.019000    0.215000    0.2073000
+42220.00   0.004277   0.215616   0.2060678   -0.018000    0.215000    0.2053000
+42221.00   0.005373   0.216573   0.2036984   -0.017000    0.215000    0.2032000
+42222.00   0.006518   0.217503   0.2011655   -0.016000    0.215000    0.2012000
+42223.00   0.007703   0.218405   0.1985464   -0.015000    0.215000    0.1991000
+42224.00   0.008924   0.219276   0.1959452   -0.014000    0.216000    0.1971000
+42225.00   0.010171   0.220115   0.1934584   -0.013000    0.216000    0.1951000
+42226.00   0.011437   0.220919   0.1911510   -0.012000    0.216000    0.1930000
+42227.00   0.012711   0.221686   0.1890489   -0.011000    0.216000    0.1910000
+42228.00   0.013984   0.222416   0.1871428   -0.010000    0.216000    0.1889000
+42229.00   0.015250   0.223106   0.1853992   -0.009000    0.216000    0.1869000
+42230.00   0.016503   0.223758   0.1837679   -0.008000    0.216000    0.1848000
+42231.00   0.017736   0.224372   0.1821913   -0.007000    0.216000    0.1827000
+42232.00   0.018940   0.224947   0.1806110   -0.006000    0.216000    0.1805000
+42233.00   0.020113   0.225484   0.1789761   -0.005000    0.216000    0.1784000
+42234.00   0.021252   0.225982   0.1772496   -0.004000    0.216000    0.1763000
+42235.00   0.022358   0.226444   0.1754130   -0.002000    0.216000    0.1742000
+42236.00   0.023430   0.226867   0.1734666   -0.001000    0.216000    0.1721000
+42237.00   0.024465   0.227255   0.1714277    0.000000    0.216000    0.1699000
+42238.00   0.025459   0.227607   0.1693264    0.001000    0.216000    0.1678000
+42239.00   0.026407   0.227925   0.1672016    0.002000    0.216000    0.1657000
+42240.00   0.027305   0.228211   0.1650959    0.003000    0.216000    0.1636000
+42241.00   0.028151   0.228464   0.1630508    0.004000    0.216000    0.1616000
+42242.00   0.028942   0.228683   0.1610997    0.005000    0.216000    0.1595000
+42243.00   0.029674   0.228869   0.1592580    0.006000    0.216000    0.1575000
+42244.00   0.030340   0.229023   0.1575102    0.007000    0.217000    0.1554000
+42245.00   0.030935   0.229145   0.1558022    0.007000    0.217000    0.1534000
+42246.00   0.031459   0.229236   0.1540466    0.008000    0.217000    0.1515000
+42247.00   0.031910   0.229297   0.1521472    0.009000    0.217000    0.1495000
+42248.00   0.032289   0.229326   0.1500348    0.010000    0.217000    0.1476000
+42249.00   0.032598   0.229326   0.1476996    0.011000    0.217000    0.1456000
+42250.00   0.032838   0.229296   0.1452006    0.012000    0.217000    0.1437000
+42251.00   0.033012   0.229237   0.1426478    0.012000    0.217000    0.1418000
+42252.00   0.033124   0.229150   0.1401629    0.013000    0.218000    0.1399000
+42253.00   0.033179   0.229032   0.1378411    0.013000    0.218000    0.1380000
+42254.00   0.033183   0.228884   0.1357282    0.014000    0.218000    0.1361000
+42255.00   0.033140   0.228705   0.1338212    0.015000    0.218000    0.1342000
+42256.00   0.033052   0.228495   0.1320826    0.015000    0.218000    0.1323000
+42257.00   0.032919   0.228251   0.1304569    0.016000    0.219000    0.1305000
+42258.00   0.032742   0.227975   0.1288833    0.016000    0.219000    0.1286000
+42259.00   0.032525   0.227666   0.1273020    0.017000    0.219000    0.1267000
+42260.00   0.032268   0.227323   0.1256589    0.017000    0.219000    0.1247000
+42261.00   0.031975   0.226948   0.1239113    0.017000    0.219000    0.1227000
+42262.00   0.031645   0.226540   0.1220332    0.017000    0.220000    0.1206000
+42263.00   0.031279   0.226103   0.1200186    0.017000    0.220000    0.1186000
+42264.00   0.030879   0.225640   0.1178811    0.017000    0.220000    0.1166000
+42265.00   0.030451   0.225155   0.1156503    0.017000    0.220000    0.1143000
+42266.00   0.029999   0.224651   0.1133655    0.017000    0.220000    0.1121000
+42267.00   0.029528   0.224133   0.1110705    0.017000    0.221000    0.1098000
+42268.00   0.029038   0.223605   0.1088073    0.017000    0.221000    0.1076000
+42269.00   0.028531   0.223069   0.1066102    0.017000    0.221000    0.1053000
+42270.00   0.028009   0.222528   0.1044981    0.017000    0.221000    0.1029000
+42271.00   0.027475   0.221983   0.1024651    0.018000    0.220000    0.1005000
+42272.00   0.026931   0.221433   0.1004733    0.018000    0.220000    0.0980000
+42273.00   0.026380   0.220880   0.0984520    0.019000    0.220000    0.0956000
+42274.00   0.025824   0.220324   0.0963103    0.019000    0.220000    0.0932000
+42275.00   0.025264   0.219770   0.0939641    0.019000    0.219000    0.0908000
+42276.00   0.024699   0.219221   0.0913700    0.020000    0.219000    0.0884000
+42277.00   0.024131   0.218679   0.0885501    0.020000    0.219000    0.0860000
+42278.00   0.023558   0.218150   0.0855929    0.021000    0.218000    0.0836000
+42279.00   0.022981   0.217635   0.0826272    0.021000    0.218000    0.0812000
+42280.00   0.022400   0.217136   0.0797792    0.021000    0.218000    0.0789000
+42281.00   0.021818   0.216657   0.0771343    0.021000    0.218000    0.0766000
+42282.00   0.021237   0.216199   0.0747195    0.020000    0.218000    0.0743000
+42283.00   0.020664   0.215762   0.0725103    0.020000    0.218000    0.0720000
+42284.00   0.020105   0.215347   0.0704511    0.020000    0.218000    0.0697000
+42285.00   0.019567   0.214953   0.0684765    0.020000    0.218000    0.0675000
+42286.00   0.019055   0.214582   0.0665239    0.020000    0.218000    0.0653000
+42287.00   0.018573   0.214234   0.0645387    0.019000    0.218000    0.0632000
+42288.00   0.018120   0.213910   0.0624767    0.019000    0.218000    0.0610000
+42289.00   0.017700   0.213614   0.0603081    0.019000    0.218000    0.0588000
+42290.00   0.017314   0.213344   0.0580213    0.019000    0.218000    0.0566000
+42291.00   0.016963   0.213104   0.0556227    0.019000    0.218000    0.0543000
+42292.00   0.016648   0.212894   0.0531363    0.018000    0.218000    0.0521000
+42293.00   0.016370   0.212717   0.0505988    0.018000    0.218000    0.0498000
+42294.00   0.016129   0.212577   0.0480536    0.018000    0.218000    0.0476000
+42295.00   0.015927   0.212477   0.0455436    0.018000    0.218000    0.0452000
+42296.00   0.015762   0.212418   0.0431045    0.018000    0.218000    0.0428000
+42297.00   0.015632   0.212401   0.0407563    0.018000    0.218000    0.0403000
+42298.00   0.015532   0.212427   0.0384965    0.018000    0.218000    0.0379000
+42299.00   0.015456   0.212495   0.0362936    0.018000    0.218000    0.0355000
+42300.00   0.015400   0.212606   0.0340879    0.018000    0.218000    0.0330000
+42301.00   0.015358   0.212761   0.0317988    0.018000    0.218000    0.0304000
+42302.00   0.015324   0.212960   0.0293420    0.019000    0.218000    0.0279000
+42303.00   0.015293   0.213203   0.0266552    0.019000    0.218000    0.0253000
+42304.00   0.015256   0.213488   0.0237243    0.019000    0.218000    0.0228000
+42305.00   0.015206   0.213815   0.0205976    0.019000    0.218000    0.0201000
+42306.00   0.015133   0.214184   0.0173782    0.019000    0.218000    0.0175000
+42307.00   0.015032   0.214591   0.0141941    0.020000    0.219000    0.0148000
+42308.00   0.014897   0.215036   0.0111579    0.020000    0.219000    0.0122000
+42309.00   0.014725   0.215514   0.0083342    0.020000    0.219000    0.0095000
+42310.00   0.014510   0.216019   0.0057286    0.020000    0.219000    0.0068000
+42311.00   0.014246   0.216544   0.0032992    0.020000    0.220000    0.0041000
+42312.00   0.013932   0.217082   0.0009794    0.019000    0.220000    0.0013000
+42313.00   0.013565   0.217628  -0.0013001    0.019000    0.221000   -0.0014000
+42314.00   0.013142   0.218176  -0.0036001    0.019000    0.221000   -0.0041000
+42315.00   0.012664   0.218720  -0.0059688    0.019000    0.222000   -0.0068000
+42316.00   0.012129   0.219255  -0.0084405    0.019000    0.222000   -0.0095000
+42317.00   0.011540   0.219774  -0.0110335    0.018000    0.223000   -0.0122000
+42318.00   0.010901   0.220274  -0.0137479    0.018000    0.223000   -0.0149000
+42319.00   0.010218   0.220751  -0.0165659    0.018000    0.224000   -0.0176000
+42320.00   0.009497   0.221202  -0.0194541    0.018000    0.225000   -0.0203000
+42321.00   0.008739   0.221628  -0.0223698    0.018000    0.225000   -0.0230000
+42322.00   0.007946   0.222027  -0.0252664    0.017000    0.226000   -0.0258000
+42323.00   0.007119   0.222401  -0.0281018    0.017000    0.226000   -0.0285000
+42324.00   0.006262   0.222753  -0.0308468    0.017000    0.227000   -0.0312000
+42325.00   0.005380   0.223082  -0.0334946    0.016000    0.228000   -0.0341000
+42326.00   0.004477   0.223392  -0.0360675    0.016000    0.229000   -0.0370000
+42327.00   0.003560   0.223685  -0.0386168    0.015000    0.230000   -0.0398000
+42328.00   0.002633   0.223963  -0.0412151    0.015000    0.231000   -0.0427000
+42329.00   0.001704   0.224230  -0.0439406    0.014000    0.232000   -0.0456000
+42330.00   0.000778   0.224491  -0.0468567    0.013000    0.233000   -0.0487000
+42331.00  -0.000138   0.224747  -0.0499919    0.012000    0.234000   -0.0518000
+42332.00  -0.001038   0.225001  -0.0533268    0.012000    0.236000   -0.0548000
+42333.00  -0.001917   0.225253  -0.0567922    0.011000    0.237000   -0.0579000
+42334.00  -0.002775   0.225504  -0.0602843    0.010000    0.238000   -0.0610000
+42335.00  -0.003611   0.225755  -0.0636919    0.009000    0.239000   -0.0641000
+42336.00  -0.004427   0.226006  -0.0669295    0.008000    0.240000   -0.0673000
+42337.00  -0.005222   0.226259  -0.0699599    0.007000    0.242000   -0.0704000
+42338.00  -0.005998   0.226515  -0.0727978    0.006000    0.243000   -0.0736000
+42339.00  -0.006755   0.226777  -0.0754965    0.005000    0.244000   -0.0767000
+42340.00  -0.007498   0.227044  -0.0781253    0.004000    0.245000   -0.0798000
+42341.00  -0.008231   0.227318  -0.0807508    0.003000    0.246000   -0.0830000
+42342.00  -0.008959   0.227600  -0.0834263    0.002000    0.247000   -0.0861000
+42343.00  -0.009685   0.227891  -0.0861902    0.001000    0.248000   -0.0893000
+42344.00  -0.010412   0.228192  -0.0890648    0.000000    0.249000   -0.0924000
+42345.00  -0.011141   0.228506  -0.0920563   -0.001000    0.250000   -0.0955000
+42346.00  -0.011873   0.228834  -0.0951537   -0.002000    0.251000   -0.0986000
+42347.00  -0.012609   0.229178  -0.0983305   -0.003000    0.252000   -0.1017000
+42348.00  -0.013350   0.229538  -0.1015481   -0.004000    0.253000   -0.1048000
+42349.00  -0.014093   0.229916  -0.1047621   -0.005000    0.254000   -0.1079000
+42350.00  -0.014838   0.230312  -0.1079286   -0.006000    0.255000   -0.1110000
+42351.00  -0.015582   0.230727  -0.1110115   -0.007000    0.256000   -0.1140000
+42352.00  -0.016326   0.231161  -0.1139930   -0.008000    0.256000   -0.1171000
+42353.00  -0.017073   0.231616  -0.1168828   -0.009000    0.257000   -0.1201000
+42354.00  -0.017822   0.232090  -0.1197222   -0.010000    0.258000   -0.1232000
+42355.00  -0.018574   0.232584  -0.1225786   -0.011000    0.259000   -0.1262000
+42356.00  -0.019330   0.233097  -0.1255277   -0.012000    0.259000   -0.1293000
+42357.00  -0.020086   0.233628  -0.1286323   -0.012000    0.260000   -0.1323000
+42358.00  -0.020843   0.234176  -0.1319226   -0.013000    0.260000   -0.1354000
+42359.00  -0.021598   0.234741  -0.1353865   -0.014000    0.261000   -0.1384000
+42360.00  -0.022348   0.235320  -0.1389706   -0.015000    0.261000   -0.1415000
+42361.00  -0.023091   0.235914  -0.1425916   -0.015000    0.262000   -0.1445000
+42362.00  -0.023823   0.236519  -0.1461556   -0.016000    0.262000   -0.1476000
+42363.00  -0.024543   0.237136  -0.1495809   -0.016000    0.263000   -0.1506000
+42364.00  -0.025251   0.237761  -0.1528176   -0.017000    0.263000   -0.1537000
+42365.00  -0.025948   0.238390  -0.1558584   -0.018000    0.263000   -0.1568000
+42366.00  -0.026638   0.239023  -0.1587363   -0.018000    0.264000   -0.1599000
+42367.00  -0.027322   0.239655  -0.1615100   -0.019000    0.264000   -0.1629000
+42368.00  -0.027999   0.240288  -0.1642452   -0.019000    0.265000   -0.1660000
+42369.00  -0.028670   0.240920  -0.1669996   -0.020000    0.265000   -0.1691000
+42370.00  -0.029332   0.241553  -0.1698154   -0.021000    0.265000   -0.1722000
+42371.00  -0.029984   0.242187  -0.1727172   -0.021000    0.266000   -0.1753000
+42372.00  -0.030624   0.242822  -0.1757133   -0.022000    0.266000   -0.1783000
+42373.00  -0.031252   0.243455  -0.1787966   -0.022000    0.267000   -0.1814000
+42374.00  -0.031866   0.244087  -0.1819454   -0.023000    0.267000   -0.1845000
+42375.00  -0.032463   0.244716  -0.1851271   -0.024000    0.267000   -0.1876000
+42376.00  -0.033043   0.245339  -0.1883019   -0.025000    0.268000   -0.1906000
+42377.00  -0.033605   0.245957  -0.1914286   -0.025000    0.268000   -0.1937000
+42378.00  -0.034149   0.246568  -0.1944705   -0.026000    0.269000   -0.1967000
+42379.00  -0.034674   0.247172  -0.1974034   -0.027000    0.269000   -0.1998000
+42380.00  -0.035178   0.247771  -0.2002257   -0.028000    0.269000   -0.2029000
+42381.00  -0.035660   0.248366  -0.2029672   -0.029000    0.270000   -0.2059000
+42382.00  -0.036119   0.248959  -0.2056880   -0.029000    0.270000   -0.2090000
+42383.00  -0.036555   0.249549  -0.2084660   -0.030000    0.271000   -0.2120000
+42384.00  -0.036969   0.250136  -0.2113722   -0.031000    0.271000   -0.2151000
+42385.00  -0.037360   0.250720  -0.2144449   -0.031000    0.271000   -0.2180000
+42386.00  -0.037727   0.251301  -0.2176753   -0.032000    0.272000   -0.2210000
+42387.00  -0.038067   0.251878  -0.2210090   -0.032000    0.272000   -0.2239000
+42388.00  -0.038382   0.252448  -0.2243632   -0.033000    0.273000   -0.2269000
+42389.00  -0.038673   0.253012  -0.2276498   -0.033000    0.273000   -0.2298000
+42390.00  -0.038940   0.253568  -0.2307956   -0.033000    0.273000   -0.2326000
+42391.00  -0.039184   0.254115  -0.2337566   -0.034000    0.274000   -0.2354000
+42392.00  -0.039408   0.254656  -0.2365234   -0.034000    0.274000   -0.2381000
+42393.00  -0.039614   0.255193  -0.2391202   -0.035000    0.275000   -0.2409000
+42394.00  -0.039809   0.255726  -0.2415963   -0.035000    0.275000   -0.2437000
+42395.00  -0.039996   0.256257  -0.2440136   -0.035000    0.275000   -0.2463000
+42396.00  -0.040178   0.256789  -0.2464328   -0.035000    0.276000   -0.2490000
+42397.00  -0.040360   0.257323  -0.2489018   -0.036000    0.276000   -0.2516000
+42398.00  -0.040542   0.257858  -0.2514511   -0.036000    0.277000   -0.2543000
+42399.00  -0.040724   0.258394  -0.2540928   -0.036000    0.277000   -0.2569000
+42400.00  -0.040910   0.258930  -0.2568225   -0.036000    0.277000   -0.2595000
+42401.00  -0.041101   0.259466  -0.2596221   -0.036000    0.278000   -0.2621000
+42402.00  -0.041297   0.260000  -0.2624630   -0.037000    0.278000   -0.2648000
+42403.00  -0.041498   0.260534  -0.2653103   -0.037000    0.279000   -0.2674000
+42404.00  -0.041705   0.261069  -0.2681275   -0.037000    0.279000   -0.2700000
+42405.00  -0.041917   0.261606  -0.2708818   -0.037000    0.279000   -0.2726000
+42406.00  -0.042136   0.262144  -0.2735492   -0.037000    0.279000   -0.2752000
+42407.00  -0.042362   0.262684  -0.2761222   -0.038000    0.280000   -0.2777000
+42408.00  -0.042596   0.263227  -0.2786190   -0.038000    0.280000   -0.2803000
+42409.00  -0.042837   0.263773  -0.2810892   -0.038000    0.280000   -0.2829000
+42410.00  -0.043081   0.264322  -0.2836080   -0.038000    0.280000   -0.2855000
+42411.00  -0.043325   0.264876  -0.2862566   -0.038000    0.280000   -0.2882000
+42412.00  -0.043569   0.265436  -0.2890927   -0.039000    0.281000   -0.2908000
+42413.00  -0.043813   0.266001   0.7078756   -0.039000    0.281000    0.7065000
+42414.00  -0.044056   0.266573   0.7046980   -0.039000    0.281000    0.7039000
+42415.00  -0.044299   0.267153   0.7014674   -0.039000    0.281000    0.7012000
+42416.00  -0.044538   0.267744   0.6982891   -0.039000    0.282000    0.6986000
+42417.00  -0.044768   0.268350   0.6952497   -0.040000    0.282000    0.6959000
+42418.00  -0.044988   0.268975   0.6923984   -0.040000    0.283000    0.6933000
+42419.00  -0.045193   0.269624   0.6897425   -0.040000    0.283000    0.6906000
+42420.00  -0.045381   0.270298   0.6872556   -0.040000    0.283000    0.6879000
+42421.00  -0.045545   0.271002   0.6848888   -0.040000    0.284000    0.6853000
+42422.00  -0.045681   0.271735   0.6825825   -0.039000    0.284000    0.6826000
+42423.00  -0.045780   0.272497   0.6802763   -0.039000    0.285000    0.6800000
+42424.00  -0.045838   0.273288   0.6779183   -0.039000    0.285000    0.6773000
+42425.00  -0.045849   0.274108   0.6754717   -0.039000    0.286000    0.6746000
+42426.00  -0.045810   0.274956   0.6729187   -0.038000    0.286000    0.6719000
+42427.00  -0.045718   0.275833   0.6702599   -0.038000    0.287000    0.6691000
+42428.00  -0.045572   0.276739   0.6675120   -0.037000    0.287000    0.6664000
+42429.00  -0.045372   0.277675   0.6647030   -0.037000    0.288000    0.6637000
+42430.00  -0.045120   0.278643   0.6618672   -0.036000    0.289000    0.6610000
+42431.00  -0.044818   0.279643   0.6590403   -0.036000    0.289000    0.6582000
+42432.00  -0.044467   0.280677   0.6562544   -0.035000    0.290000    0.6555000
+42433.00  -0.044070   0.281746   0.6535334   -0.035000    0.290000    0.6527000
+42434.00  -0.043628   0.282851   0.6508876   -0.034000    0.291000    0.6500000
+42435.00  -0.043138   0.283988   0.6483073   -0.033000    0.292000    0.6472000
+42436.00  -0.042603   0.285155   0.6457559   -0.032000    0.292000    0.6443000
+42437.00  -0.042021   0.286350   0.6431682   -0.032000    0.293000    0.6415000
+42438.00  -0.041394   0.287570   0.6404612   -0.031000    0.293000    0.6386000
+42439.00  -0.040726   0.288813   0.6375585   -0.030000    0.294000    0.6358000
+42440.00  -0.040018   0.290078   0.6344222   -0.029000    0.295000    0.6328000
+42441.00  -0.039271   0.291366   0.6310758   -0.028000    0.295000    0.6299000
+42442.00  -0.038487   0.292676   0.6276040   -0.026000    0.296000    0.6269000
+42443.00  -0.037672   0.294007   0.6241280   -0.025000    0.296000    0.6240000
+42444.00  -0.036831   0.295362   0.6207651   -0.024000    0.297000    0.6210000
+42445.00  -0.035972   0.296738   0.6175948   -0.023000    0.298000    0.6181000
+42446.00  -0.035098   0.298134   0.6146428   -0.022000    0.299000    0.6151000
+42447.00  -0.034215   0.299551   0.6118869   -0.022000    0.300000    0.6122000
+42448.00  -0.033323   0.300984   0.6092753   -0.021000    0.301000    0.6092000
+42449.00  -0.032428   0.302432   0.6067448   -0.020000    0.302000    0.6063000
+42450.00  -0.031533   0.303890   0.6042334   -0.019000    0.303000    0.6034000
+42451.00  -0.030641   0.305355   0.6016879   -0.018000    0.304000    0.6005000
+42452.00  -0.029752   0.306822   0.5990674   -0.018000    0.306000    0.5975000
+42453.00  -0.028867   0.308289   0.5963479   -0.017000    0.307000    0.5946000
+42454.00  -0.027985   0.309752   0.5935241   -0.016000    0.308000    0.5917000
+42455.00  -0.027107   0.311208   0.5906088   -0.015000    0.309000    0.5888000
+42456.00  -0.026233   0.312651   0.5876293   -0.014000    0.310000    0.5859000
+42457.00  -0.025359   0.314078   0.5846213   -0.014000    0.312000    0.5830000
+42458.00  -0.024481   0.315489   0.5816232   -0.013000    0.313000    0.5801000
+42459.00  -0.023595   0.316880   0.5786704   -0.012000    0.314000    0.5772000
+42460.00  -0.022698   0.318250   0.5757901   -0.011000    0.315000    0.5743000
+42461.00  -0.021785   0.319596   0.5729971   -0.011000    0.316000    0.5714000
+42462.00  -0.020854   0.320913   0.5702878   -0.010000    0.318000    0.5686000
+42463.00  -0.019901   0.322199   0.5676357   -0.010000    0.319000    0.5657000
+42464.00  -0.018922   0.323451   0.5649878   -0.009000    0.320000    0.5628000
+42465.00  -0.017916   0.324668   0.5622687   -0.008000    0.321000    0.5600000
+42466.00  -0.016879   0.325847   0.5593957   -0.008000    0.322000    0.5572000
+42467.00  -0.015812   0.326987   0.5563054   -0.007000    0.323000    0.5543000
+42468.00  -0.014712   0.328087   0.5529823   -0.007000    0.324000    0.5515000
+42469.00  -0.013581   0.329149   0.5494757   -0.006000    0.325000    0.5487000
+42470.00  -0.012420   0.330173   0.5458914   -0.006000    0.326000    0.5459000
+42471.00  -0.011231   0.331158   0.5423600   -0.005000    0.327000    0.5432000
+42472.00  -0.010016   0.332105   0.5389947   -0.005000    0.327000    0.5404000
+42473.00  -0.008773   0.333012   0.5358580   -0.004000    0.328000    0.5377000
+42474.00  -0.007500   0.333880   0.5329514   -0.004000    0.329000    0.5349000
+42475.00  -0.006197   0.334705   0.5302293   -0.004000    0.330000    0.5321000
+42476.00  -0.004863   0.335489   0.5276233   -0.003000    0.331000    0.5292000
+42477.00  -0.003500   0.336232   0.5250636   -0.003000    0.331000    0.5264000
+42478.00  -0.002108   0.336934   0.5224911   -0.002000    0.332000    0.5235000
+42479.00  -0.000692   0.337597   0.5198611   -0.002000    0.333000    0.5207000
+42480.00   0.000744   0.338223   0.5171438   -0.002000    0.334000    0.5178000
+42481.00   0.002194   0.338812   0.5143260   -0.001000    0.334000    0.5149000
+42482.00   0.003653   0.339365   0.5114119   -0.001000    0.335000    0.5121000
+42483.00   0.005126   0.339885   0.5084225    0.000000    0.335000    0.5092000
+42484.00   0.006613   0.340372   0.5053905    0.000000    0.336000    0.5063000
+42485.00   0.008116   0.340829   0.5023538    0.001000    0.337000    0.5033000
+42486.00   0.009635   0.341255   0.4993497    0.001000    0.337000    0.5003000
+42487.00   0.011171   0.341651   0.4964084    0.002000    0.338000    0.4974000
+42488.00   0.012726   0.342017   0.4935487    0.002000    0.338000    0.4944000
+42489.00   0.014304   0.342352   0.4907725    0.003000    0.339000    0.4914000
+42490.00   0.015905   0.342660   0.4880607    0.004000    0.340000    0.4883000
+42491.00   0.017531   0.342942   0.4853706    0.005000    0.340000    0.4852000
+42492.00   0.019180   0.343198   0.4826378    0.005000    0.341000    0.4821000
+42493.00   0.020849   0.343430   0.4797859    0.006000    0.341000    0.4790000
+42494.00   0.022537   0.343638   0.4767436    0.007000    0.342000    0.4759000
+42495.00   0.024242   0.343820   0.4734686    0.008000    0.342000    0.4727000
+42496.00   0.025962   0.343976   0.4699702    0.009000    0.343000    0.4695000
+42497.00   0.027694   0.344104   0.4663166    0.010000    0.343000    0.4664000
+42498.00   0.029437   0.344201   0.4626221    0.011000    0.344000    0.4632000
+42499.00   0.031187   0.344266   0.4590129    0.012000    0.344000    0.4600000
+42500.00   0.032941   0.344294   0.4555869    0.013000    0.344000    0.4569000
+42501.00   0.034696   0.344283   0.4523866    0.015000    0.344000    0.4538000
+42502.00   0.036448   0.344232   0.4493950    0.016000    0.344000    0.4507000
+42503.00   0.038197   0.344141   0.4465544    0.018000    0.344000    0.4476000
+42504.00   0.039936   0.344009   0.4437925    0.019000    0.344000    0.4445000
+42505.00   0.041661   0.343836   0.4410437    0.020000    0.344000    0.4415000
+42506.00   0.043367   0.343622   0.4382584    0.021000    0.344000    0.4386000
+42507.00   0.045053   0.343367   0.4354043    0.023000    0.345000    0.4356000
+42508.00   0.046719   0.343070   0.4324646    0.024000    0.345000    0.4327000
+42509.00   0.048364   0.342730   0.4294378    0.025000    0.345000    0.4297000
+42510.00   0.049985   0.342348   0.4263381    0.026000    0.345000    0.4268000
+42511.00   0.051578   0.341921   0.4231931    0.027000    0.345000    0.4239000
+42512.00   0.053137   0.341449   0.4200387    0.029000    0.345000    0.4211000
+42513.00   0.054661   0.340931   0.4169125    0.030000    0.345000    0.4182000
+42514.00   0.056146   0.340369   0.4138476    0.031000    0.345000    0.4153000
+42515.00   0.057593   0.339764   0.4108669    0.032000    0.345000    0.4124000
+42516.00   0.058999   0.339119   0.4079785    0.033000    0.345000    0.4095000
+42517.00   0.060367   0.338435   0.4051706    0.035000    0.344000    0.4065000
+42518.00   0.061701   0.337716   0.4024088    0.036000    0.344000    0.4036000
+42519.00   0.063009   0.336966   0.3996382    0.037000    0.344000    0.4007000
+42520.00   0.064296   0.336186   0.3967917    0.038000    0.343000    0.3977000
+42521.00   0.065567   0.335382   0.3938035    0.039000    0.343000    0.3947000
+42522.00   0.066826   0.334554   0.3906270    0.041000    0.342000    0.3917000
+42523.00   0.068077   0.333704   0.3872515    0.042000    0.342000    0.3887000
+42524.00   0.069321   0.332833   0.3837133    0.043000    0.341000    0.3857000
+42525.00   0.070557   0.331940   0.3800930    0.044000    0.340000    0.3826000
+42526.00   0.071788   0.331024   0.3764981    0.045000    0.339000    0.3796000
+42527.00   0.073014   0.330086   0.3730323    0.046000    0.338000    0.3765000
+42528.00   0.074238   0.329124   0.3697646    0.047000    0.337000    0.3735000
+42529.00   0.075463   0.328141   0.3667112    0.048000    0.336000    0.3704000
+42530.00   0.076690   0.327137   0.3638376    0.049000    0.335000    0.3674000
+42531.00   0.077921   0.326113   0.3610792    0.050000    0.334000    0.3643000
+42532.00   0.079157   0.325068   0.3583660    0.051000    0.332000    0.3613000
+42533.00   0.080398   0.324001   0.3556415    0.052000    0.331000    0.3582000
+42534.00   0.081641   0.322911   0.3528694    0.053000    0.330000    0.3552000
+42535.00   0.082882   0.321798   0.3500319    0.054000    0.329000    0.3522000
+42536.00   0.084121   0.320663   0.3471267    0.055000    0.327000    0.3492000
+42537.00   0.085356   0.319504   0.3441649    0.055000    0.326000    0.3462000
+42538.00   0.086586   0.318325   0.3411700    0.056000    0.324000    0.3432000
+42539.00   0.087811   0.317127   0.3381746    0.057000    0.323000    0.3402000
+42540.00   0.089031   0.315913   0.3352143    0.058000    0.322000    0.3373000
+42541.00   0.090241   0.314689   0.3323221    0.059000    0.321000    0.3344000
+42542.00   0.091439   0.313459   0.3295229    0.059000    0.319000    0.3314000
+42543.00   0.092621   0.312226   0.3268285    0.060000    0.318000    0.3285000
+42544.00   0.093786   0.310993   0.3242325    0.061000    0.317000    0.3256000
+42545.00   0.094932   0.309761   0.3217068    0.062000    0.316000    0.3228000
+42546.00   0.096058   0.308531   0.3192016    0.063000    0.314000    0.3200000
+42547.00   0.097163   0.307303   0.3166529    0.064000    0.313000    0.3172000
+42548.00   0.098247   0.306076   0.3139979    0.065000    0.311000    0.3144000
+42549.00   0.099311   0.304848   0.3111915    0.066000    0.310000    0.3116000
+42550.00   0.100359   0.303619   0.3082210    0.067000    0.309000    0.3089000
+42551.00   0.101397   0.302388   0.3051124    0.068000    0.308000    0.3061000
+42552.00   0.102426   0.301158   0.3019278    0.069000    0.306000    0.3034000
+42553.00   0.103450   0.299929   0.2987524    0.070000    0.305000    0.3006000
+42554.00   0.104473   0.298699   0.2956753    0.071000    0.304000    0.2979000
+42555.00   0.105500   0.297467   0.2927661    0.072000    0.303000    0.2952000
+42556.00   0.106539   0.296233   0.2900570    0.073000    0.302000    0.2926000
+42557.00   0.107595   0.294997   0.2875349    0.074000    0.300000    0.2899000
+42558.00   0.108671   0.293758   0.2851496    0.075000    0.299000    0.2873000
+42559.00   0.109773   0.292518   0.2828336    0.076000    0.298000    0.2846000
+42560.00   0.110906   0.291275   0.2805229    0.077000    0.297000    0.2820000
+42561.00   0.112075   0.290031   0.2781713    0.079000    0.295000    0.2795000
+42562.00   0.113283   0.288785   0.2757543    0.080000    0.294000    0.2769000
+42563.00   0.114533   0.287537   0.2732663    0.082000    0.292000    0.2744000
+42564.00   0.115825   0.286288   0.2707160    0.083000    0.291000    0.2718000
+42565.00   0.117160   0.285035   0.2681232    0.084000    0.290000    0.2693000
+42566.00   0.118536   0.283780   0.2655170    0.086000    0.289000    0.2668000
+42567.00   0.119950   0.282521   0.2629321    0.087000    0.287000    0.2643000
+42568.00   0.121398   0.281258   0.2604028    0.089000    0.286000    0.2618000
+42569.00   0.122879   0.279990   0.2579562    0.090000    0.285000    0.2593000
+42570.00   0.124388   0.278717   0.2556079    0.092000    0.284000    0.2568000
+42571.00   0.125921   0.277438   0.2533572    0.093000    0.282000    0.2544000
+42572.00   0.127474   0.276154   0.2511834    0.095000    0.281000    0.2519000
+42573.00   0.129037   0.274864   0.2490428    0.096000    0.279000    0.2495000
+42574.00   0.130603   0.273567   0.2468736    0.098000    0.278000    0.2470000
+42575.00   0.132165   0.272260   0.2446096    0.100000    0.277000    0.2446000
+42576.00   0.133713   0.270945   0.2422005    0.101000    0.275000    0.2422000
+42577.00   0.135237   0.269625   0.2396310    0.103000    0.274000    0.2398000
+42578.00   0.136725   0.268302   0.2369286    0.104000    0.272000    0.2374000
+42579.00   0.138167   0.266980   0.2341584    0.106000    0.271000    0.2350000
+42580.00   0.139554   0.265660   0.2314051    0.108000    0.270000    0.2326000
+42581.00   0.140882   0.264345   0.2287524    0.109000    0.268000    0.2303000
+42582.00   0.142146   0.263038   0.2262630    0.111000    0.267000    0.2279000
+42583.00   0.143336   0.261738   0.2239675    0.112000    0.265000    0.2256000
+42584.00   0.144446   0.260444   0.2218601    0.114000    0.264000    0.2232000
+42585.00   0.145469   0.259152   0.2199022    0.115000    0.263000    0.2209000
+42586.00   0.146405   0.257856   0.2180341    0.117000    0.261000    0.2187000
+42587.00   0.147255   0.256553   0.2161921    0.118000    0.260000    0.2164000
+42588.00   0.148021   0.255237   0.2143237    0.120000    0.258000    0.2142000
+42589.00   0.148702   0.253903   0.2123966    0.121000    0.257000    0.2119000
+42590.00   0.149300   0.252549   0.2104005    0.122000    0.255000    0.2098000
+42591.00   0.149817   0.251173   0.2083430    0.123000    0.254000    0.2077000
+42592.00   0.150256   0.249773   0.2062449    0.125000    0.252000    0.2055000
+42593.00   0.150620   0.248350   0.2041341    0.126000    0.251000    0.2034000
+42594.00   0.150907   0.246906   0.2020430    0.127000    0.249000    0.2013000
+42595.00   0.151121   0.245438   0.2000033    0.128000    0.247000    0.1993000
+42596.00   0.151264   0.243945   0.1980417    0.129000    0.246000    0.1973000
+42597.00   0.151341   0.242428   0.1961746    0.129000    0.244000    0.1953000
+42598.00   0.151357   0.240886   0.1944039    0.130000    0.243000    0.1933000
+42599.00   0.151316   0.239321   0.1927134    0.131000    0.241000    0.1913000
+42600.00   0.151218   0.237734   0.1910650    0.131000    0.239000    0.1893000
+42601.00   0.151067   0.236130   0.1893995    0.132000    0.238000    0.1874000
+42602.00   0.150864   0.234510   0.1876449    0.132000    0.236000    0.1854000
+42603.00   0.150608   0.232880   0.1857359    0.133000    0.235000    0.1835000
+42604.00   0.150303   0.231240   0.1836383    0.133000    0.233000    0.1815000
+42605.00   0.149951   0.229592   0.1813664    0.133000    0.231000    0.1796000
+42606.00   0.149556   0.227935   0.1789845    0.133000    0.230000    0.1777000
+42607.00   0.149123   0.226270   0.1765882    0.134000    0.228000    0.1758000
+42608.00   0.148659   0.224596   0.1742749    0.134000    0.227000    0.1739000
+42609.00   0.148167   0.222915   0.1721174    0.134000    0.225000    0.1720000
+42610.00   0.147651   0.221225   0.1701488    0.134000    0.223000    0.1702000
+42611.00   0.147119   0.219529   0.1683616    0.134000    0.221000    0.1683000
+42612.00   0.146579   0.217826   0.1667168    0.135000    0.220000    0.1665000
+42613.00   0.146036   0.216118   0.1651568    0.135000    0.218000    0.1646000
+42614.00   0.145495   0.214404   0.1636182    0.135000    0.216000    0.1628000
+42615.00   0.144958   0.212684   0.1620442    0.135000    0.214000    0.1610000
+42616.00   0.144430   0.210958   0.1603935    0.135000    0.213000    0.1592000
+42617.00   0.143917   0.209226   0.1586456    0.135000    0.211000    0.1573000
+42618.00   0.143422   0.207487   0.1568008    0.135000    0.210000    0.1555000
+42619.00   0.142948   0.205744   0.1548759    0.135000    0.208000    0.1537000
+42620.00   0.142495   0.203995   0.1528981    0.135000    0.206000    0.1518000
+42621.00   0.142061   0.202241   0.1508998    0.135000    0.204000    0.1499000
+42622.00   0.141647   0.200481   0.1489135    0.136000    0.203000    0.1480000
+42623.00   0.141250   0.198715   0.1469678    0.136000    0.201000    0.1461000
+42624.00   0.140866   0.196941   0.1450827    0.136000    0.199000    0.1442000
+42625.00   0.140495   0.195159   0.1432652    0.136000    0.197000    0.1422000
+42626.00   0.140131   0.193370   0.1415062    0.136000    0.195000    0.1402000
+42627.00   0.139771   0.191574   0.1397771    0.136000    0.194000    0.1383000
+42628.00   0.139409   0.189773   0.1380281    0.136000    0.192000    0.1363000
+42629.00   0.139043   0.187967   0.1361916    0.136000    0.190000    0.1343000
+42630.00   0.138669   0.186160   0.1341949    0.136000    0.188000    0.1322000
+42631.00   0.138283   0.184355   0.1319829    0.136000    0.186000    0.1301000
+42632.00   0.137886   0.182552   0.1295436    0.137000    0.185000    0.1279000
+42633.00   0.137474   0.180753   0.1269223    0.137000    0.183000    0.1258000
+42634.00   0.137044   0.178960   0.1242150    0.137000    0.181000    0.1237000
+42635.00   0.136594   0.177173   0.1215397    0.137000    0.179000    0.1215000
+42636.00   0.136127   0.175389   0.1189993    0.137000    0.178000    0.1193000
+42637.00   0.135643   0.173609   0.1166529    0.137000    0.176000    0.1172000
+42638.00   0.135138   0.171833   0.1145063    0.137000    0.175000    0.1150000
+42639.00   0.134611   0.170059   0.1125222    0.137000    0.173000    0.1128000
+42640.00   0.134057   0.168287   0.1106400    0.137000    0.172000    0.1106000
+42641.00   0.133477   0.166517   0.1087940    0.137000    0.170000    0.1084000
+42642.00   0.132872   0.164748   0.1069262    0.136000    0.169000    0.1063000
+42643.00   0.132241   0.162980   0.1049929    0.136000    0.167000    0.1041000
+42644.00   0.131582   0.161210   0.1029686    0.136000    0.166000    0.1019000
+42645.00   0.130895   0.159439   0.1008470    0.136000    0.165000    0.0998000
+42646.00   0.130180   0.157663   0.0986407    0.136000    0.163000    0.0976000
+42647.00   0.129438   0.155884   0.0963759    0.135000    0.162000    0.0955000
+42648.00   0.128669   0.154099   0.0940867    0.135000    0.160000    0.0933000
+42649.00   0.127872   0.152307   0.0918087    0.135000    0.159000    0.0912000
+42650.00   0.127042   0.150509   0.0895730    0.134000    0.158000    0.0890000
+42651.00   0.126175   0.148704   0.0874023    0.134000    0.156000    0.0869000
+42652.00   0.125270   0.146895   0.0853067    0.133000    0.155000    0.0847000
+42653.00   0.124326   0.145082   0.0832809    0.133000    0.153000    0.0826000
+42654.00   0.123343   0.143270   0.0813027    0.132000    0.152000    0.0804000
+42655.00   0.122316   0.141461   0.0793306    0.131000    0.151000    0.0781000
+42656.00   0.121242   0.139659   0.0773052    0.130000    0.149000    0.0758000
+42657.00   0.120119   0.137868   0.0751549    0.128000    0.148000    0.0735000
+42658.00   0.118946   0.136090   0.0728116    0.127000    0.146000    0.0712000
+42659.00   0.117724   0.134331   0.0702342    0.126000    0.145000    0.0689000
+42660.00   0.116450   0.132594   0.0674318    0.125000    0.144000    0.0665000
+42661.00   0.115122   0.130881   0.0644718    0.123000    0.143000    0.0641000
+42662.00   0.113735   0.129198   0.0614672    0.122000    0.141000    0.0618000
+42663.00   0.112285   0.127549   0.0585415    0.120000    0.140000    0.0594000
+42664.00   0.110770   0.125938   0.0557892    0.119000    0.139000    0.0570000
+42665.00   0.109188   0.124368   0.0532491    0.117000    0.138000    0.0546000
+42666.00   0.107540   0.122843   0.0509018    0.116000    0.137000    0.0521000
+42667.00   0.105831   0.121362   0.0486878    0.114000    0.135000    0.0497000
+42668.00   0.104064   0.119928   0.0465338    0.113000    0.134000    0.0472000
+42669.00   0.102244   0.118541   0.0443734    0.111000    0.133000    0.0448000
+42670.00   0.100377   0.117203   0.0421573    0.109000    0.132000    0.0423000
+42671.00   0.098468   0.115914   0.0398549    0.107000    0.131000    0.0398000
+42672.00   0.096524   0.114671   0.0374535    0.106000    0.130000    0.0374000
+42673.00   0.094556   0.113472   0.0349574    0.104000    0.129000    0.0349000
+42674.00   0.092568   0.112315   0.0323854    0.102000    0.128000    0.0324000
+42675.00   0.090562   0.111197   0.0297676    0.100000    0.127000    0.0298000
+42676.00   0.088541   0.110116   0.0271382    0.098000    0.126000    0.0272000
+42677.00   0.086507   0.109068   0.0245291    0.097000    0.124000    0.0247000
+42678.00   0.084463   0.108051   0.0219647    0.095000    0.123000    0.0221000
+42679.00   0.082411   0.107063   0.0194578    0.093000    0.122000    0.0195000
+42680.00   0.080351   0.106103   0.0170073    0.091000    0.121000    0.0168000
+42681.00   0.078281   0.105169   0.0145964    0.089000    0.120000    0.0141000
+42682.00   0.076200   0.104262   0.0121912    0.087000    0.119000    0.0114000
+42683.00   0.074109   0.103379   0.0097417    0.085000    0.118000    0.0087000
+42684.00   0.072008   0.102523   0.0071859    0.083000    0.117000    0.0060000
+42685.00   0.069891   0.101693   0.0044591    0.081000    0.116000    0.0032000
+42686.00   0.067753   0.100891   0.0015094    0.079000    0.115000    0.0004000
+42687.00   0.065591   0.100115  -0.0016819    0.077000    0.114000   -0.0023000
+42688.00   0.063400   0.099364  -0.0050831    0.075000    0.113000   -0.0051000
+42689.00   0.061177   0.098637  -0.0086107    0.073000    0.112000   -0.0079000
+42690.00   0.058918   0.097935  -0.0121475    0.071000    0.111000   -0.0108000
+42691.00   0.056619   0.097260  -0.0155777    0.069000    0.110000   -0.0137000
+42692.00   0.054278   0.096614  -0.0188238    0.066000    0.110000   -0.0167000
+42693.00   0.051893   0.095998  -0.0218672    0.064000    0.109000   -0.0196000
+42694.00   0.049466   0.095416  -0.0247445    0.062000    0.108000   -0.0225000
+42695.00   0.046997   0.094868  -0.0275243    0.060000    0.107000   -0.0255000
+42696.00   0.044489   0.094358  -0.0302791    0.057000    0.107000   -0.0285000
+42697.00   0.041945   0.093886  -0.0330653    0.055000    0.106000   -0.0315000
+42698.00   0.039363   0.093452  -0.0359169    0.052000    0.106000   -0.0345000
+42699.00   0.036746   0.093054  -0.0388482    0.050000    0.105000   -0.0375000
+42700.00   0.034093   0.092693  -0.0418584    0.047000    0.105000   -0.0406000
+42701.00   0.031402   0.092367  -0.0449336    0.045000    0.104000   -0.0437000
+42702.00   0.028671   0.092077  -0.0480485    0.042000    0.104000   -0.0469000
+42703.00   0.025899   0.091825  -0.0511704    0.040000    0.103000   -0.0500000
+42704.00   0.023088   0.091609  -0.0542659    0.037000    0.103000   -0.0531000
+42705.00   0.020236   0.091433  -0.0573070    0.034000    0.103000   -0.0563000
+42706.00   0.017343   0.091295  -0.0602766    0.032000    0.103000   -0.0594000
+42707.00   0.014408   0.091196  -0.0631707    0.029000    0.102000   -0.0626000
+42708.00   0.011430   0.091136  -0.0660001    0.027000    0.102000   -0.0657000
+42709.00   0.008415   0.091113  -0.0687916    0.024000    0.102000   -0.0689000
+42710.00   0.005366   0.091128  -0.0715879    0.021000    0.102000   -0.0721000
+42711.00   0.002284   0.091180  -0.0744434    0.018000    0.102000   -0.0753000
+42712.00  -0.000828   0.091271  -0.0774171    0.016000    0.102000   -0.0785000
+42713.00  -0.003970   0.091400  -0.0805602    0.013000    0.102000   -0.0817000
+42714.00  -0.007140   0.091566  -0.0839023    0.010000    0.102000   -0.0849000
+42715.00  -0.010330   0.091769  -0.0874367    0.007000    0.102000   -0.0881000
+42716.00  -0.013536   0.092013  -0.0911128    0.004000    0.102000   -0.0913000
+42717.00  -0.016752   0.092297  -0.0948426    0.001000    0.103000   -0.0945000
+42718.00  -0.019973   0.092625  -0.0985208   -0.002000    0.103000   -0.0977000
+42719.00  -0.023191   0.092996  -0.1020561   -0.005000    0.103000   -0.1009000
+42720.00  -0.026399   0.093412  -0.1053988   -0.008000    0.104000   -0.1041000
+42721.00  -0.029590   0.093876  -0.1085538   -0.011000    0.105000   -0.1073000
+42722.00  -0.032760   0.094391  -0.1115712   -0.013000    0.105000   -0.1106000
+42723.00  -0.035902   0.094961  -0.1145216   -0.016000    0.106000   -0.1138000
+42724.00  -0.039010   0.095587  -0.1174698   -0.019000    0.107000   -0.1170000
+42725.00  -0.042077   0.096271  -0.1204590   -0.022000    0.108000   -0.1202000
+42726.00  -0.045090   0.097017  -0.1235075   -0.025000    0.110000   -0.1234000
+42727.00  -0.048044   0.097829  -0.1266150   -0.027000    0.111000   -0.1266000
+42728.00  -0.050932   0.098710  -0.1297683   -0.030000    0.113000   -0.1298000
+42729.00  -0.053751   0.099660  -0.1329450   -0.033000    0.114000   -0.1330000
+42730.00  -0.056499   0.100680  -0.1361156   -0.036000    0.116000   -0.1362000
+42731.00  -0.059173   0.101771  -0.1392475   -0.039000    0.117000   -0.1393000
+42732.00  -0.061773   0.102933  -0.1423112   -0.041000    0.119000   -0.1425000
+42733.00  -0.064300   0.104164  -0.1452861   -0.044000    0.120000   -0.1456000
+42734.00  -0.066758   0.105465  -0.1481645   -0.047000    0.122000   -0.1488000
+42735.00  -0.069154   0.106830  -0.1509530   -0.050000    0.124000   -0.1519000
+42736.00  -0.071496   0.108256  -0.1536738   -0.053000    0.126000   -0.1550000
+42737.00  -0.073792   0.109737  -0.1563645   -0.055000    0.127000   -0.1582000
+42738.00  -0.076051   0.111268  -0.1590747   -0.058000    0.129000   -0.1613000
+42739.00  -0.078276   0.112848  -0.1618592   -0.061000    0.131000   -0.1644000
+42740.00  -0.080474   0.114472  -0.1647655   -0.064000    0.133000   -0.1674000
+42741.00  -0.082652   0.116140  -0.1678220   -0.066000    0.135000   -0.1705000
+42742.00  -0.084820   0.117848  -0.1710272   -0.069000    0.136000   -0.1735000
+42743.00  -0.086983   0.119596  -0.1743461   -0.071000    0.138000   -0.1766000
+42744.00  -0.089145   0.121384  -0.1777136   -0.074000    0.140000   -0.1796000
+42745.00  -0.091308   0.123210  -0.1810477   -0.077000    0.142000   -0.1826000
+42746.00  -0.093472   0.125073  -0.1842687   -0.079000    0.144000   -0.1855000
+42747.00  -0.095638   0.126970  -0.1873216   -0.082000    0.145000   -0.1885000
+42748.00  -0.097804   0.128899  -0.1901925   -0.084000    0.147000   -0.1914000
+42749.00  -0.099969   0.130854  -0.1929110   -0.087000    0.149000   -0.1944000
+42750.00  -0.102128   0.132833  -0.1955375   -0.089000    0.151000   -0.1972000
+42751.00  -0.104281   0.134833  -0.1981401   -0.091000    0.152000   -0.2000000
+42752.00  -0.106423   0.136852  -0.2007731   -0.093000    0.154000   -0.2029000
+42753.00  -0.108550   0.138887  -0.2034657   -0.095000    0.155000   -0.2057000
+42754.00  -0.110656   0.140936  -0.2062227   -0.097000    0.157000   -0.2085000
+42755.00  -0.112735   0.142996  -0.2090323   -0.099000    0.159000   -0.2113000
+42756.00  -0.114783   0.145066  -0.2118730   -0.100000    0.160000   -0.2141000
+42757.00  -0.116794   0.147142  -0.2147182   -0.102000    0.162000   -0.2168000
+42758.00  -0.118762   0.149221  -0.2175390   -0.103000    0.163000   -0.2196000
+42759.00  -0.120682   0.151303  -0.2203080   -0.105000    0.165000   -0.2224000
+42760.00  -0.122549   0.153388  -0.2230042   -0.107000    0.167000   -0.2252000
+42761.00  -0.124360   0.155474  -0.2256178   -0.108000    0.169000   -0.2279000
+42762.00  -0.126111   0.157562  -0.2281532   -0.110000    0.170000   -0.2307000
+42763.00  -0.127800   0.159653  -0.2306297   -0.111000    0.172000   -0.2334000
+42764.00  -0.129423   0.161749  -0.2330821   -0.113000    0.174000   -0.2362000
+42765.00  -0.130977   0.163852  -0.2355587   -0.114000    0.176000   -0.2389000
+42766.00  -0.132461   0.165965  -0.2381153   -0.116000    0.178000   -0.2416000
+42767.00  -0.133879   0.168089  -0.2408033   -0.117000    0.180000   -0.2444000
+42768.00  -0.135231   0.170225  -0.2436542   -0.119000    0.182000   -0.2471000
+42769.00  -0.136520   0.172373  -0.2466668   -0.120000    0.184000   -0.2498000
+42770.00  -0.137749   0.174535  -0.2498037   -0.121000    0.186000   -0.2525000
+42771.00  -0.138922   0.176708  -0.2529990   -0.122000    0.188000   -0.2551000
+42772.00  -0.140046   0.178891  -0.2561747   -0.123000    0.191000   -0.2578000
+42773.00  -0.141125   0.181081  -0.2592587   -0.124000    0.193000   -0.2604000
+42774.00  -0.142162   0.183277  -0.2622010   -0.125000    0.195000   -0.2631000
+42775.00  -0.143157   0.185479  -0.2649847   -0.126000    0.197000   -0.2657000
+42776.00  -0.144114   0.187689  -0.2676290   -0.127000    0.199000   -0.2683000
+42777.00  -0.145035   0.189909  -0.2701822   -0.128000    0.202000   -0.2710000
+42778.00  -0.145922   0.192143   0.7272928   -0.129000    0.204000    0.7264000
+42779.00  -0.146779   0.194393   0.7247370   -0.130000    0.206000    0.7238000
+42780.00  -0.147599   0.196662   0.7221112   -0.131000    0.208000    0.7211000
+42781.00  -0.148380   0.198951   0.7194019   -0.131000    0.210000    0.7185000
+42782.00  -0.149117   0.201257   0.7166183   -0.132000    0.213000    0.7158000
+42783.00  -0.149810   0.203580   0.7137839   -0.132000    0.215000    0.7132000
+42784.00  -0.150451   0.205917   0.7109287   -0.133000    0.217000    0.7105000
+42785.00  -0.151041   0.208267   0.7080839   -0.133000    0.219000    0.7078000
+42786.00  -0.151577   0.210631   0.7052786   -0.134000    0.221000    0.7051000
+42787.00  -0.152057   0.213008   0.7025362   -0.134000    0.224000    0.7024000
+42788.00  -0.152481   0.215400   0.6998696   -0.135000    0.226000    0.6997000
+42789.00  -0.152852   0.217808   0.6972785   -0.135000    0.228000    0.6970000
+42790.00  -0.153173   0.220234   0.6947468   -0.135000    0.230000    0.6942000
+42791.00  -0.153446   0.222679   0.6922431   -0.135000    0.232000    0.6915000
+42792.00  -0.153675   0.225144   0.6897202   -0.136000    0.235000    0.6887000
+42793.00  -0.153862   0.227632   0.6871198   -0.136000    0.237000    0.6860000
+42794.00  -0.154011   0.230140   0.6843817   -0.136000    0.239000    0.6832000
+42795.00  -0.154124   0.232668   0.6814605   -0.136000    0.241000    0.6803000
+42796.00  -0.154204   0.235217   0.6783439   -0.136000    0.243000    0.6775000
+42797.00  -0.154253   0.237786   0.6750641   -0.137000    0.246000    0.6746000
+42798.00  -0.154273   0.240376   0.6716935   -0.137000    0.248000    0.6718000
+42799.00  -0.154264   0.242988   0.6683242   -0.137000    0.250000    0.6689000
+42800.00  -0.154226   0.245617   0.6650428   -0.137000    0.252000    0.6660000
+42801.00  -0.154162   0.248261   0.6619076   -0.137000    0.255000    0.6630000
+42802.00  -0.154076   0.250916   0.6589383   -0.138000    0.257000    0.6601000
+42803.00  -0.153970   0.253580   0.6561175   -0.138000    0.260000    0.6571000
+42804.00  -0.153848   0.256250   0.6533998   -0.138000    0.262000    0.6542000
+42805.00  -0.153715   0.258922   0.6507259   -0.138000    0.264000    0.6512000
+42806.00  -0.153574   0.261594   0.6480368   -0.138000    0.267000    0.6482000
+42807.00  -0.153428   0.264265   0.6452861   -0.138000    0.269000    0.6453000
+42808.00  -0.153279   0.266934   0.6424496   -0.138000    0.272000    0.6423000
+42809.00  -0.153131   0.269604   0.6395274   -0.138000    0.274000    0.6393000
+42810.00  -0.152986   0.272274   0.6365390   -0.138000    0.277000    0.6363000
+42811.00  -0.152842   0.274945   0.6335159   -0.138000    0.279000    0.6333000
+42812.00  -0.152695   0.277617   0.6304930   -0.138000    0.282000    0.6302000
+42813.00  -0.152540   0.280290   0.6275034   -0.138000    0.284000    0.6272000
+42814.00  -0.152375   0.282961   0.6245740   -0.138000    0.287000    0.6242000
+42815.00  -0.152197   0.285628   0.6217225   -0.138000    0.290000    0.6213000
+42816.00  -0.152000   0.288288   0.6189541   -0.138000    0.292000    0.6184000
+42817.00  -0.151781   0.290936   0.6162592   -0.137000    0.295000    0.6155000
+42818.00  -0.151533   0.293573   0.6136127   -0.137000    0.297000    0.6126000
+42819.00  -0.151251   0.296199   0.6109741   -0.137000    0.300000    0.6097000
+42820.00  -0.150933   0.298814   0.6082897   -0.137000    0.302000    0.6069000
+42821.00  -0.150575   0.301418   0.6054976   -0.136000    0.305000    0.6040000
+42822.00  -0.150174   0.304014   0.6025395   -0.136000    0.307000    0.6012000
+42823.00  -0.149726   0.306599   0.5993805   -0.135000    0.310000    0.5983000
+42824.00  -0.149229   0.309176   0.5960279   -0.135000    0.312000    0.5955000
+42825.00  -0.148684   0.311742   0.5925383   -0.134000    0.314000    0.5927000
+42826.00  -0.148094   0.314296   0.5890064   -0.134000    0.317000    0.5899000
+42827.00  -0.147460   0.316836   0.5855373   -0.133000    0.319000    0.5871000
+42828.00  -0.146784   0.319360   0.5822144   -0.133000    0.322000    0.5843000
+42829.00  -0.146073   0.321867   0.5790765   -0.132000    0.324000    0.5815000
+42830.00  -0.145331   0.324357   0.5761146   -0.131000    0.326000    0.5787000
+42831.00  -0.144566   0.326827   0.5732827   -0.131000    0.328000    0.5759000
+42832.00  -0.143778   0.329279   0.5705177   -0.130000    0.331000    0.5731000
+42833.00  -0.142971   0.331710   0.5677568   -0.130000    0.333000    0.5703000
+42834.00  -0.142145   0.334119   0.5649494   -0.129000    0.335000    0.5675000
+42835.00  -0.141302   0.336505   0.5620635   -0.128000    0.337000    0.5646000
+42836.00  -0.140442   0.338863   0.5590883   -0.128000    0.339000    0.5617000
+42837.00  -0.139563   0.341194   0.5560332   -0.127000    0.341000    0.5589000
+42838.00  -0.138660   0.343496   0.5529235   -0.127000    0.343000    0.5560000
+42839.00  -0.137733   0.345769   0.5497932   -0.126000    0.345000    0.5531000
+42840.00  -0.136781   0.348013   0.5466772   -0.125000    0.347000    0.5501000
+42841.00  -0.135807   0.350227   0.5436056   -0.124000    0.349000    0.5471000
+42842.00  -0.134811   0.352411   0.5405995   -0.124000    0.350000    0.5441000
+42843.00  -0.133796   0.354565   0.5376686   -0.123000    0.352000    0.5411000
+42844.00  -0.132760   0.356692   0.5348089   -0.122000    0.354000    0.5381000
+42845.00  -0.131707   0.358793   0.5320016   -0.121000    0.356000    0.5350000
+42846.00  -0.130636   0.360869   0.5292134   -0.120000    0.358000    0.5319000
+42847.00  -0.129551   0.362923   0.5263966   -0.120000    0.359000    0.5288000
+42848.00  -0.128452   0.364956   0.5234931   -0.119000    0.361000    0.5257000
+42849.00  -0.127341   0.366971   0.5204408   -0.118000    0.363000    0.5226000
+42850.00  -0.126221   0.368970   0.5171889   -0.117000    0.365000    0.5194000
+42851.00  -0.125094   0.370954   0.5137165   -0.116000    0.367000    0.5163000
+42852.00  -0.123959   0.372922   0.5100498   -0.116000    0.368000    0.5131000
+42853.00  -0.122815   0.374874   0.5062651   -0.115000    0.370000    0.5100000
+42854.00  -0.121658   0.376808   0.5024721   -0.114000    0.372000    0.5068000
+42855.00  -0.120484   0.378726   0.4987803   -0.113000    0.374000    0.5036000
+42856.00  -0.119284   0.380628   0.4952644   -0.112000    0.376000    0.5003000
+42857.00  -0.118052   0.382514   0.4919446   -0.112000    0.377000    0.4971000
+42858.00  -0.116778   0.384388   0.4887897   -0.111000    0.379000    0.4938000
+42859.00  -0.115450   0.386253   0.4857373   -0.110000    0.381000    0.4906000
+42860.00  -0.114059   0.388112   0.4827192   -0.109000    0.383000    0.4873000
+42861.00  -0.112595   0.389968   0.4796800   -0.108000    0.385000    0.4839000
+42862.00  -0.111052   0.391823   0.4765838   -0.107000    0.386000    0.4806000
+42863.00  -0.109424   0.393681   0.4734152   -0.106000    0.388000    0.4772000
+42864.00  -0.107709   0.395542   0.4701765   -0.105000    0.390000    0.4739000
+42865.00  -0.105906   0.397406   0.4668847   -0.104000    0.392000    0.4705000
+42866.00  -0.104018   0.399271   0.4635681   -0.103000    0.394000    0.4671000
+42867.00  -0.102047   0.401134   0.4602603   -0.102000    0.395000    0.4638000
+42868.00  -0.099995   0.402994   0.4569929   -0.101000    0.397000    0.4604000
+42869.00  -0.097866   0.404852   0.4537904   -0.100000    0.399000    0.4570000
+42870.00  -0.095669   0.406706   0.4506658   -0.099000    0.401000    0.4536000
+42871.00  -0.093413   0.408553   0.4476198   -0.097000    0.403000    0.4502000
+42872.00  -0.091109   0.410392   0.4446395   -0.096000    0.405000    0.4467000
+42873.00  -0.088767   0.412218   0.4416984   -0.094000    0.407000    0.4433000
+42874.00  -0.086392   0.414029   0.4387576   -0.093000    0.409000    0.4399000
+42875.00  -0.083990   0.415821   0.4357669   -0.091000    0.411000    0.4366000
+42876.00  -0.081562   0.417591   0.4326701   -0.090000    0.413000    0.4332000
+42877.00  -0.079110   0.419335   0.4294138   -0.088000    0.415000    0.4299000
+42878.00  -0.076635   0.421048   0.4259609   -0.087000    0.417000    0.4265000
+42879.00  -0.074138   0.422728   0.4223079   -0.085000    0.419000    0.4232000
+42880.00  -0.071621   0.424368   0.4184967   -0.083000    0.421000    0.4199000
+42881.00  -0.069088   0.425965   0.4146124   -0.081000    0.423000    0.4166000
+42882.00  -0.066544   0.427516   0.4107633   -0.080000    0.424000    0.4132000
+42883.00  -0.063992   0.429019   0.4070470   -0.078000    0.426000    0.4099000
+42884.00  -0.061439   0.430471   0.4035190   -0.076000    0.428000    0.4066000
+42885.00  -0.058886   0.431870   0.4001787   -0.074000    0.430000    0.4033000
+42886.00  -0.056337   0.433213   0.3969785   -0.072000    0.431000    0.4000000
+42887.00  -0.053792   0.434496   0.3938495   -0.071000    0.433000    0.3968000
+42888.00  -0.051253   0.435720   0.3907280   -0.069000    0.434000    0.3935000
+42889.00  -0.048720   0.436883   0.3875718   -0.067000    0.436000    0.3902000
+42890.00  -0.046197   0.437983   0.3843629   -0.065000    0.437000    0.3870000
+42891.00  -0.043688   0.439018   0.3811017   -0.063000    0.439000    0.3837000
+42892.00  -0.041199   0.439986   0.3778019   -0.061000    0.440000    0.3805000
+42893.00  -0.038736   0.440883   0.3744861   -0.059000    0.442000    0.3772000
+42894.00  -0.036303   0.441706   0.3711830   -0.057000    0.443000    0.3740000
+42895.00  -0.033903   0.442451   0.3679226   -0.055000    0.444000    0.3707000
+42896.00  -0.031534   0.443115   0.3647305   -0.053000    0.445000    0.3675000
+42897.00  -0.029191   0.443696   0.3616222   -0.050000    0.446000    0.3642000
+42898.00  -0.026867   0.444191   0.3586003   -0.048000    0.447000    0.3610000
+42899.00  -0.024555   0.444600   0.3556546   -0.046000    0.448000    0.3577000
+42900.00  -0.022248   0.444920   0.3527621   -0.044000    0.449000    0.3545000
+42901.00  -0.019943   0.445150   0.3498887   -0.042000    0.450000    0.3512000
+42902.00  -0.017638   0.445288   0.3469905   -0.039000    0.450000    0.3480000
+42903.00  -0.015335   0.445331   0.3440182   -0.037000    0.451000    0.3447000
+42904.00  -0.013036   0.445276   0.3409233   -0.035000    0.452000    0.3415000
+42905.00  -0.010745   0.445122   0.3376682   -0.032000    0.452000    0.3381000
+42906.00  -0.008465   0.444867   0.3342371   -0.030000    0.453000    0.3347000
+42907.00  -0.006202   0.444511   0.3306472   -0.027000    0.453000    0.3314000
+42908.00  -0.003954   0.444053   0.3269529   -0.025000    0.454000    0.3280000
+42909.00  -0.001718   0.443494   0.3232386   -0.022000    0.454000    0.3246000
+42910.00   0.000512   0.442837   0.3195973   -0.019000    0.454000    0.3212000
+42911.00   0.002734   0.442084   0.3161017   -0.017000    0.454000    0.3177000
+42912.00   0.004949   0.441237   0.3127807   -0.014000    0.454000    0.3143000
+42913.00   0.007154   0.440300   0.3096121   -0.012000    0.454000    0.3108000
+42914.00   0.009354   0.439276   0.3065373   -0.009000    0.454000    0.3074000
+42915.00   0.011554   0.438172   0.3034880   -0.006000    0.454000    0.3041000
+42916.00   0.013761   0.436993   0.3004110   -0.003000    0.454000    0.3007000
+42917.00   0.015983   0.435748   0.2972803    0.002000    0.453000    0.2940000
+42918.00   0.018227   0.434445   0.2940956    0.002000    0.453000    0.2940000
+42919.00   0.020497   0.433093   0.2908756    0.005000    0.453000    0.2907000
+42920.00   0.022791   0.431707   0.2876480    0.008000    0.452000    0.2876000
+42921.00   0.025112   0.430297   0.2844442    0.011000    0.452000    0.2845000
+42922.00   0.027462   0.428878   0.2812960    0.014000    0.451000    0.2813000
+42923.00   0.029845   0.427465   0.2782318    0.017000    0.451000    0.2782000
+42924.00   0.032266   0.426071   0.2752722    0.020000    0.450000    0.2751000
+42925.00   0.034735   0.424714   0.2724254    0.023000    0.449000    0.2721000
+42926.00   0.037259   0.423411   0.2696860    0.026000    0.448000    0.2692000
+42927.00   0.039850   0.422181   0.2670353    0.029000    0.447000    0.2662000
+42928.00   0.042519   0.421041   0.2644432    0.032000    0.446000    0.2633000
+42929.00   0.045268   0.419990   0.2618698    0.035000    0.445000    0.2603000
+42930.00   0.048095   0.419011   0.2592691    0.038000    0.444000    0.2575000
+42931.00   0.050998   0.418092   0.2565967    0.041000    0.443000    0.2547000
+42932.00   0.053973   0.417222   0.2538185    0.044000    0.441000    0.2518000
+42933.00   0.057023   0.416390   0.2509208    0.047000    0.440000    0.2490000
+42934.00   0.060145   0.415586   0.2479160    0.050000    0.439000    0.2462000
+42935.00   0.063339   0.414802   0.2448453    0.053000    0.438000    0.2436000
+42936.00   0.066601   0.414032   0.2417737    0.056000    0.436000    0.2411000
+42937.00   0.069929   0.413270   0.2387777    0.060000    0.435000    0.2385000
+42938.00   0.073319   0.412510   0.2359248    0.063000    0.433000    0.2360000
+42939.00   0.076764   0.411747   0.2332542    0.066000    0.432000    0.2334000
+42940.00   0.080261   0.410977   0.2307632    0.069000    0.430000    0.2310000
+42941.00   0.083804   0.410197   0.2284091    0.072000    0.428000    0.2286000
+42942.00   0.087388   0.409404   0.2261262    0.076000    0.427000    0.2262000
+42943.00   0.091007   0.408596   0.2238509    0.079000    0.425000    0.2238000
+42944.00   0.094657   0.407772   0.2215422    0.082000    0.423000    0.2214000
+42945.00   0.098336   0.406929   0.2191885    0.085000    0.421000    0.2192000
+42946.00   0.102046   0.406065   0.2168023    0.089000    0.419000    0.2170000
+42947.00   0.105785   0.405180   0.2144087    0.092000    0.417000    0.2147000
+42948.00   0.109553   0.404273   0.2120361    0.096000    0.415000    0.2125000
+42949.00   0.113344   0.403338   0.2097117    0.099000    0.413000    0.2103000
+42950.00   0.117150   0.402367   0.2074596    0.102000    0.411000    0.2082000
+42951.00   0.120965   0.401350   0.2052974    0.106000    0.409000    0.2060000
+42952.00   0.124783   0.400278   0.2032318    0.109000    0.407000    0.2039000
+42953.00   0.128599   0.399142   0.2012559    0.113000    0.405000    0.2017000
+42954.00   0.132409   0.397934   0.1993482    0.116000    0.403000    0.1996000
+42955.00   0.136210   0.396646   0.1974747    0.119000    0.401000    0.1974000
+42956.00   0.139998   0.395270   0.1955915    0.123000    0.399000    0.1952000
+42957.00   0.143772   0.393797   0.1936489    0.126000    0.396000    0.1931000
+42958.00   0.147529   0.392219   0.1915975    0.130000    0.394000    0.1909000
+42959.00   0.151267   0.390543   0.1893993    0.133000    0.392000    0.1887000
+42960.00   0.154984   0.388776   0.1870376    0.136000    0.390000    0.1865000
+42961.00   0.158679   0.386923   0.1845248    0.140000    0.387000    0.1843000
+42962.00   0.162352   0.384992   0.1819028    0.143000    0.385000    0.1821000
+42963.00   0.166004   0.382986   0.1792345    0.147000    0.382000    0.1799000
+42964.00   0.169636   0.380910   0.1765898    0.150000    0.380000    0.1777000
+42965.00   0.173247   0.378770   0.1740294    0.153000    0.378000    0.1753000
+42966.00   0.176837   0.376569   0.1715909    0.156000    0.376000    0.1730000
+42967.00   0.180401   0.374315   0.1692790    0.159000    0.373000    0.1706000
+42968.00   0.183934   0.372014   0.1670645    0.162000    0.371000    0.1683000
+42969.00   0.187428   0.369671   0.1648930    0.165000    0.369000    0.1659000
+42970.00   0.190877   0.367294   0.1627037    0.168000    0.367000    0.1635000
+42971.00   0.194274   0.364887   0.1604486    0.171000    0.365000    0.1611000
+42972.00   0.197611   0.362456   0.1581059    0.173000    0.362000    0.1586000
+42973.00   0.200882   0.360005   0.1556828    0.176000    0.360000    0.1562000
+42974.00   0.204078   0.357538   0.1532076    0.179000    0.358000    0.1538000
+42975.00   0.207189   0.355057   0.1507172    0.182000    0.356000    0.1514000
+42976.00   0.210205   0.352562   0.1482482    0.184000    0.354000    0.1489000
+42977.00   0.213113   0.350055   0.1458315    0.187000    0.351000    0.1465000
+42978.00   0.215903   0.347537   0.1434907    0.189000    0.349000    0.1440000
+42979.00   0.218565   0.345008   0.1412396    0.192000    0.347000    0.1416000
+42980.00   0.221086   0.342468   0.1390792    0.194000    0.345000    0.1392000
+42981.00   0.223456   0.339916   0.1369961    0.197000    0.343000    0.1368000
+42982.00   0.225666   0.337350   0.1349622    0.199000    0.340000    0.1343000
+42983.00   0.227725   0.334770   0.1329375    0.202000    0.338000    0.1319000
+42984.00   0.229645   0.332174   0.1308736    0.204000    0.336000    0.1295000
+42985.00   0.231435   0.329559   0.1287194    0.206000    0.334000    0.1271000
+42986.00   0.233101   0.326925   0.1264293    0.209000    0.332000    0.1247000
+42987.00   0.234651   0.324267   0.1239761    0.211000    0.329000    0.1223000
+42988.00   0.236088   0.321585   0.1213634    0.214000    0.327000    0.1199000
+42989.00   0.237420   0.318877   0.1186313    0.216000    0.325000    0.1175000
+42990.00   0.238655   0.316140   0.1158493    0.218000    0.323000    0.1151000
+42991.00   0.239798   0.313373   0.1130985    0.220000    0.320000    0.1128000
+42992.00   0.240858   0.310575   0.1104494    0.223000    0.318000    0.1104000
+42993.00   0.241840   0.307745   0.1079439    0.225000    0.315000    0.1081000
+42994.00   0.242751   0.304881   0.1055884    0.227000    0.313000    0.1057000
+42995.00   0.243595   0.301983   0.1033558    0.229000    0.310000    0.1034000
+42996.00   0.244380   0.299052   0.1011959    0.231000    0.308000    0.1011000
+42997.00   0.245108   0.296091   0.0990505    0.233000    0.305000    0.0989000
+42998.00   0.245781   0.293101   0.0968681    0.235000    0.303000    0.0966000
+42999.00   0.246402   0.290085   0.0946168    0.237000    0.300000    0.0943000
+43000.00   0.246970   0.287047   0.0922901    0.239000    0.297000    0.0921000
+43001.00   0.247487   0.283988   0.0899044    0.241000    0.294000    0.0898000
+43002.00   0.247953   0.280913   0.0874919    0.242000    0.292000    0.0876000
+43003.00   0.248371   0.277825   0.0850903    0.244000    0.289000    0.0853000
+43004.00   0.248745   0.274725   0.0827333    0.246000    0.286000    0.0831000
+43005.00   0.249075   0.271617   0.0804458    0.248000    0.283000    0.0809000
+43006.00   0.249363   0.268503   0.0782417    0.249000    0.280000    0.0786000
+43007.00   0.249611   0.265386   0.0761236    0.251000    0.278000    0.0764000
+43008.00   0.249819   0.262269   0.0740805    0.252000    0.275000    0.0741000
+43009.00   0.249988   0.259157   0.0720864    0.254000    0.272000    0.0719000
+43010.00   0.250115   0.256052   0.0701021    0.255000    0.269000    0.0696000
+43011.00   0.250199   0.252957   0.0680790    0.256000    0.266000    0.0673000
+43012.00   0.250237   0.249874   0.0659637    0.258000    0.263000    0.0649000
+43013.00   0.250226   0.246806   0.0637037    0.259000    0.260000    0.0626000
+43014.00   0.250164   0.243753   0.0612575    0.260000    0.257000    0.0603000
+43015.00   0.250047   0.240718   0.0586089    0.261000    0.254000    0.0579000
+43016.00   0.249873   0.237703   0.0557798    0.262000    0.251000    0.0555000
+43017.00   0.249635   0.234708   0.0528321    0.263000    0.248000    0.0530000
+43018.00   0.249330   0.231736   0.0498545    0.264000    0.245000    0.0506000
+43019.00   0.248952   0.228790   0.0469369    0.265000    0.242000    0.0482000
+43020.00   0.248499   0.225872   0.0441424    0.266000    0.239000    0.0456000
+43021.00   0.247962   0.222984   0.0414915    0.266000    0.236000    0.0431000
+43022.00   0.247335   0.220128   0.0389619    0.267000    0.233000    0.0405000
+43023.00   0.246611   0.217308   0.0365018    0.267000    0.230000    0.0380000
+43024.00   0.245781   0.214527   0.0340482    0.268000    0.227000    0.0354000
+43025.00   0.244837   0.211786   0.0315440    0.268000    0.224000    0.0326000
+43026.00   0.243773   0.209088   0.0289484    0.268000    0.222000    0.0299000
+43027.00   0.242580   0.206434   0.0262430    0.267000    0.219000    0.0271000
+43028.00   0.241249   0.203825   0.0234329    0.267000    0.217000    0.0244000
+43029.00   0.239773   0.201263   0.0205429    0.267000    0.214000    0.0216000
+43030.00   0.238153   0.198749   0.0176095    0.266000    0.212000    0.0186000
+43031.00   0.236405   0.196285   0.0146718    0.266000    0.209000    0.0157000
+43032.00   0.234546   0.193873   0.0117630    0.265000    0.207000    0.0127000
+43033.00   0.232589   0.191512   0.0089050    0.265000    0.204000    0.0098000
+43034.00   0.230549   0.189206   0.0061070    0.264000    0.202000    0.0068000
+43035.00   0.228436   0.186953   0.0033653    0.263000    0.200000    0.0037000
+43036.00   0.226262   0.184756   0.0006625    0.262000    0.197000    0.0006000
+43037.00   0.224040   0.182613  -0.0020313    0.262000    0.195000   -0.0024000
+43038.00   0.221780   0.180523  -0.0047568    0.261000    0.192000   -0.0055000
+43039.00   0.219493   0.178483  -0.0075618    0.260000    0.190000   -0.0086000
+43040.00   0.217189   0.176491  -0.0104971    0.259000    0.187000   -0.0118000
+43041.00   0.214879   0.174542  -0.0136096    0.258000    0.185000   -0.0150000
+43042.00   0.212572   0.172634  -0.0169307    0.257000    0.182000   -0.0183000
+43043.00   0.210280   0.170763  -0.0204622    0.256000    0.180000   -0.0215000
+43044.00   0.208015   0.168926  -0.0241649    0.255000    0.177000   -0.0247000
+43045.00   0.205788   0.167116  -0.0279605    0.254000    0.175000   -0.0280000
+43046.00   0.203607   0.165330  -0.0317496    0.252000    0.172000   -0.0313000
+43047.00   0.201482   0.163560  -0.0354420    0.251000    0.170000   -0.0345000
+43048.00   0.199423   0.161799  -0.0389851    0.249000    0.167000   -0.0378000
+43049.00   0.197443   0.160038  -0.0423772    0.248000    0.165000   -0.0411000
+43050.00   0.195556   0.158267  -0.0456595    0.246000    0.163000   -0.0444000
+43051.00   0.193761   0.156476  -0.0488942    0.245000    0.161000   -0.0477000
+43052.00   0.192049   0.154657  -0.0521409    0.243000    0.158000   -0.0509000
+43053.00   0.190412   0.152802  -0.0554420    0.242000    0.156000   -0.0542000
+43054.00   0.188844   0.150901  -0.0588172    0.240000    0.154000   -0.0575000
+43055.00   0.187338   0.148947  -0.0622645    0.238000    0.152000   -0.0609000
+43056.00   0.185878   0.146941  -0.0657649    0.236000    0.150000   -0.0642000
+43057.00   0.184444   0.144886  -0.0692867    0.233000    0.147000   -0.0676000
+43058.00   0.183018   0.142781  -0.0727925    0.231000    0.145000   -0.0709000
+43059.00   0.181581   0.140626  -0.0762483    0.229000    0.143000   -0.0743000
+43060.00   0.180116   0.138420  -0.0796288    0.227000    0.141000   -0.0777000
+43061.00   0.178626   0.136163  -0.0829199    0.224000    0.139000   -0.0811000
+43062.00   0.177119   0.133857  -0.0861213    0.222000    0.137000   -0.0846000
+43063.00   0.175602   0.131502  -0.0892462    0.219000    0.135000   -0.0880000
+43064.00   0.174075   0.129107  -0.0923197    0.217000    0.133000   -0.0914000
+43065.00   0.172531   0.126686  -0.0953760    0.214000    0.131000   -0.0948000
+43066.00   0.170964   0.124257  -0.0984571    0.212000    0.129000   -0.0982000
+43067.00   0.169366   0.121837  -0.1016087    0.209000    0.128000   -0.1017000
+43068.00   0.167730   0.119440  -0.1048764    0.207000    0.126000   -0.1051000
+43069.00   0.166052   0.117081  -0.1082973    0.204000    0.124000   -0.1085000
+43070.00   0.164323   0.114771  -0.1118907    0.201000    0.122000   -0.1119000
+43071.00   0.162540   0.112517  -0.1156453    0.198000    0.121000   -0.1153000
+43072.00   0.160699   0.110324  -0.1195118    0.196000    0.119000   -0.1188000
+43073.00   0.158798   0.108199  -0.1234077    0.193000    0.118000   -0.1222000
+43074.00   0.156836   0.106147  -0.1272383    0.190000    0.116000   -0.1256000
+43075.00   0.154807   0.104176  -0.1309265    0.187000    0.114000   -0.1290000
+43076.00   0.152708   0.102291  -0.1344389    0.184000    0.113000   -0.1324000
+43077.00   0.150532   0.100493  -0.1377922    0.181000    0.111000   -0.1358000
+43078.00   0.148275   0.098774  -0.1410399    0.178000    0.110000   -0.1392000
+43079.00   0.145933   0.097124  -0.1442462    0.175000    0.108000   -0.1426000
+43080.00   0.143504   0.095532  -0.1474621    0.172000    0.106000   -0.1459000
+43081.00   0.140986   0.093990  -0.1507129    0.169000    0.104000   -0.1492000
+43082.00   0.138378   0.092491  -0.1540004    0.165000    0.103000   -0.1526000
+43083.00   0.135685   0.091033  -0.1573108    0.162000    0.101000   -0.1559000
+43084.00   0.132909   0.089609  -0.1606205    0.159000    0.099000   -0.1592000
+43085.00   0.130046   0.088210  -0.1638992    0.156000    0.097000   -0.1624000
+43086.00   0.127091   0.086820  -0.1671159    0.152000    0.096000   -0.1656000
+43087.00   0.124040   0.085427  -0.1702435    0.149000    0.094000   -0.1687000
+43088.00   0.120887   0.084023  -0.1732639    0.145000    0.093000   -0.1719000
+43089.00   0.117629   0.082602  -0.1761719    0.142000    0.091000   -0.1751000
+43090.00   0.114259   0.081160  -0.1789766    0.139000    0.090000   -0.1782000
+43091.00   0.110789   0.079701  -0.1817006    0.135000    0.088000   -0.1813000
+43092.00   0.107237   0.078236  -0.1843763    0.132000    0.087000   -0.1843000
+43093.00   0.103622   0.076775  -0.1870431    0.128000    0.085000   -0.1874000
+43094.00   0.099976   0.075327  -0.1897447    0.125000    0.084000   -0.1905000
+43095.00   0.096332   0.073900  -0.1925243    0.122000    0.083000   -0.1935000
+43096.00   0.092723   0.072502  -0.1954200    0.118000    0.081000   -0.1965000
+43097.00   0.089176   0.071137  -0.1984565    0.115000    0.080000   -0.1995000
+43098.00   0.085713   0.069805  -0.2016370    0.111000    0.078000   -0.2025000
+43099.00   0.082352   0.068506  -0.2049362    0.108000    0.077000   -0.2055000
+43100.00   0.079093   0.067245  -0.2083012    0.105000    0.076000   -0.2085000
+43101.00   0.075922   0.066036  -0.2116598    0.101000    0.075000   -0.2115000
+43102.00   0.072820   0.064892  -0.2149386    0.098000    0.073000   -0.2145000
+43103.00   0.069783   0.063821  -0.2180867    0.094000    0.072000   -0.2175000
+43104.00   0.066805   0.062834  -0.2210963    0.091000    0.071000   -0.2205000
+43105.00   0.063885   0.061943  -0.2240040    0.088000    0.070000   -0.2235000
+43106.00   0.061019   0.061152  -0.2268716    0.084000    0.070000   -0.2265000
+43107.00   0.058197   0.060458  -0.2297588    0.081000    0.069000   -0.2294000
+43108.00   0.055414   0.059859  -0.2327027    0.077000    0.069000   -0.2324000
+43109.00   0.052661   0.059348  -0.2357106    0.074000    0.068000   -0.2354000
+43110.00   0.049933   0.058916  -0.2387657    0.071000    0.068000   -0.2384000
+43111.00   0.047221   0.058557  -0.2418399    0.068000    0.067000   -0.2414000
+43112.00   0.044512   0.058263  -0.2449021    0.064000    0.067000   -0.2444000
+43113.00   0.041779   0.058026  -0.2479232    0.061000    0.066000   -0.2474000
+43114.00   0.038999   0.057837  -0.2508785    0.058000    0.066000   -0.2504000
+43115.00   0.036147   0.057695  -0.2537501    0.054000    0.066000   -0.2534000
+43116.00   0.033197   0.057603  -0.2565310    0.051000    0.066000   -0.2564000
+43117.00   0.030129   0.057561  -0.2592275    0.047000    0.066000   -0.2595000
+43118.00   0.026940   0.057570  -0.2618595    0.044000    0.066000   -0.2625000
+43119.00   0.023629   0.057629  -0.2644568    0.040000    0.066000   -0.2655000
+43120.00   0.020197   0.057738  -0.2670559    0.036000    0.066000   -0.2685000
+43121.00   0.016660   0.057894  -0.2696957    0.033000    0.066000   -0.2715000
+43122.00   0.013042   0.058091  -0.2724142    0.029000    0.066000   -0.2745000
+43123.00   0.009367   0.058325  -0.2752438    0.026000    0.066000   -0.2775000
+43124.00   0.005650   0.058591  -0.2782053    0.022000    0.066000   -0.2805000
+43125.00   0.001900   0.058889  -0.2813018    0.018000    0.066000   -0.2834000
+43126.00  -0.001873   0.059217  -0.2845144    0.015000    0.066000   -0.2864000
+43127.00  -0.005671   0.059580  -0.2878015    0.011000    0.067000   -0.2893000
+43128.00  -0.009501   0.059986  -0.2911032    0.008000    0.067000   -0.2923000
+43129.00  -0.013371   0.060446  -0.2943538    0.004000    0.067000   -0.2952000
+43130.00  -0.017289   0.060970  -0.2974989    0.000000    0.067000   -0.2981000
+43131.00  -0.021259   0.061568  -0.3005132   -0.003000    0.068000   -0.3010000
+43132.00  -0.025285   0.062241  -0.3034101   -0.007000    0.068000   -0.3038000
+43133.00  -0.029368   0.062994  -0.3062365   -0.010000    0.069000   -0.3067000
+43134.00  -0.033512   0.063830  -0.3090520   -0.014000    0.069000   -0.3096000
+43135.00  -0.037719   0.064751  -0.3119048   -0.018000    0.070000   -0.3124000
+43136.00  -0.041989   0.065763  -0.3148158   -0.021000    0.070000   -0.3153000
+43137.00  -0.046322   0.066870  -0.3177753   -0.025000    0.071000   -0.3181000
+43138.00  -0.050717   0.068076  -0.3207530   -0.028000    0.071000   -0.3210000
+43139.00  -0.055176   0.069388  -0.3237125   -0.032000    0.072000   -0.3238000
+43140.00  -0.059698   0.070808  -0.3266212   -0.035000    0.073000   -0.3265000
+43141.00  -0.064273   0.072331  -0.3294534   -0.039000    0.074000   -0.3293000
+43142.00  -0.068888   0.073951  -0.3321916   -0.042000    0.074000   -0.3320000
+43143.00  -0.073529   0.075660  -0.3348284   -0.046000    0.075000   -0.3348000
+43144.00  -0.078182   0.077451   0.6626318   -0.049000    0.076000    0.6625000
+43145.00  -0.082831   0.079316   0.6601714   -0.052000    0.077000    0.6597000
+43146.00  -0.087456   0.081248   0.6577609   -0.056000    0.079000    0.6569000
+43147.00  -0.092040   0.083241   0.6553626   -0.059000    0.080000    0.6542000
+43148.00  -0.096564   0.085288   0.6529353   -0.063000    0.082000    0.6514000
+43149.00  -0.101010   0.087382   0.6504376   -0.066000    0.083000    0.6486000
+43150.00  -0.105357   0.089515   0.6478336   -0.069000    0.085000    0.6458000
+43151.00  -0.109588   0.091680   0.6450997   -0.073000    0.087000    0.6430000
+43152.00  -0.113685   0.093870   0.6422322   -0.076000    0.088000    0.6403000
+43153.00  -0.117633   0.096076   0.6392508   -0.080000    0.090000    0.6375000
+43154.00  -0.121416   0.098289   0.6361975   -0.083000    0.092000    0.6347000
+43155.00  -0.125019   0.100498   0.6331285   -0.086000    0.094000    0.6319000
+43156.00  -0.128428   0.102693   0.6301016   -0.090000    0.096000    0.6291000
+43157.00  -0.131651   0.104872   0.6271638   -0.093000    0.098000    0.6264000
+43158.00  -0.134713   0.107036   0.6243384   -0.097000    0.100000    0.6236000
+43159.00  -0.137636   0.109191   0.6216184   -0.100000    0.102000    0.6208000
+43160.00  -0.140438   0.111338   0.6189663   -0.103000    0.104000    0.6180000
+43161.00  -0.143137   0.113481   0.6163259   -0.106000    0.107000    0.6152000
+43162.00  -0.145749   0.115623   0.6136413   -0.110000    0.109000    0.6125000
+43163.00  -0.148288   0.117769   0.6108770   -0.113000    0.112000    0.6097000
+43164.00  -0.150769   0.119923   0.6080291   -0.116000    0.114000    0.6069000
+43165.00  -0.153209   0.122089   0.6051233   -0.119000    0.117000    0.6042000
+43166.00  -0.155619   0.124269   0.6022025   -0.122000    0.120000    0.6015000
+43167.00  -0.158001   0.126466   0.5993115   -0.126000    0.122000    0.5987000
+43168.00  -0.160358   0.128681   0.5964868   -0.129000    0.125000    0.5960000
+43169.00  -0.162693   0.130919   0.5937527   -0.132000    0.128000    0.5933000
+43170.00  -0.165008   0.133184   0.5911218   -0.135000    0.131000    0.5906000
+43171.00  -0.167302   0.135480   0.5885942   -0.138000    0.134000    0.5879000
+43172.00  -0.169575   0.137814   0.5861572   -0.142000    0.137000    0.5853000
+43173.00  -0.171822   0.140189   0.5837852   -0.145000    0.140000    0.5826000
+43174.00  -0.174043   0.142610   0.5814415   -0.148000    0.143000    0.5799000
+43175.00  -0.176244   0.145082   0.5790819   -0.151000    0.146000    0.5773000
+43176.00  -0.178434   0.147612   0.5766600   -0.154000    0.149000    0.5746000
+43177.00  -0.180620   0.150203   0.5741326   -0.157000    0.153000    0.5720000
+43178.00  -0.182809   0.152861   0.5714666   -0.160000    0.156000    0.5693000
+43179.00  -0.185006   0.155592   0.5686478   -0.163000    0.159000    0.5667000
+43180.00  -0.187216   0.158401   0.5656889   -0.166000    0.162000    0.5641000
+43181.00  -0.189444   0.161298   0.5626312   -0.169000    0.166000    0.5614000
+43182.00  -0.191697   0.164290   0.5595370   -0.172000    0.169000    0.5588000
+43183.00  -0.193973   0.167373   0.5564734   -0.175000    0.173000    0.5561000
+43184.00  -0.196266   0.170544   0.5534939   -0.178000    0.176000    0.5535000
+43185.00  -0.198571   0.173796   0.5506249   -0.181000    0.180000    0.5509000
+43186.00  -0.200881   0.177122   0.5478609   -0.184000    0.183000    0.5482000
+43187.00  -0.203191   0.180515   0.5451674   -0.186000    0.187000    0.5456000
+43188.00  -0.205494   0.183966   0.5424907   -0.189000    0.190000    0.5429000
+43189.00  -0.207785   0.187470   0.5397734   -0.192000    0.194000    0.5403000
+43190.00  -0.210053   0.191017   0.5369705   -0.194000    0.198000    0.5376000
+43191.00  -0.212281   0.194602   0.5340632   -0.197000    0.201000    0.5349000
+43192.00  -0.214452   0.198216   0.5310624   -0.199000    0.205000    0.5322000
+43193.00  -0.216553   0.201853   0.5280040   -0.202000    0.208000    0.5295000
+43194.00  -0.218572   0.205506   0.5249359   -0.204000    0.212000    0.5268000
+43195.00  -0.220498   0.209170   0.5219041   -0.206000    0.216000    0.5240000
+43196.00  -0.222323   0.212838   0.5189433   -0.208000    0.219000    0.5212000
+43197.00  -0.224035   0.216503   0.5160743   -0.210000    0.223000    0.5185000
+43198.00  -0.225626   0.220158   0.5133038   -0.212000    0.226000    0.5157000
+43199.00  -0.227088   0.223796   0.5106251   -0.214000    0.230000    0.5129000
+43200.00  -0.228413   0.227410   0.5080187   -0.216000    0.234000    0.5100000
+43201.00  -0.229592   0.230994   0.5054527   -0.217000    0.237000    0.5071000
+43202.00  -0.230618   0.234542   0.5028868   -0.219000    0.241000    0.5041000
+43203.00  -0.231493   0.238056   0.5002744   -0.220000    0.244000    0.5012000
+43204.00  -0.232225   0.241545   0.4975693   -0.222000    0.248000    0.4983000
+43205.00  -0.232824   0.245014   0.4947312   -0.223000    0.252000    0.4954000
+43206.00  -0.233299   0.248473   0.4917340   -0.224000    0.256000    0.4925000
+43207.00  -0.233660   0.251931   0.4885759   -0.226000    0.259000    0.4895000
+43208.00  -0.233915   0.255398   0.4852872   -0.227000    0.263000    0.4866000
+43209.00  -0.234077   0.258884   0.4819292   -0.228000    0.267000    0.4837000
+43210.00  -0.234156   0.262400   0.4785808   -0.229000    0.271000    0.4807000
+43211.00  -0.234164   0.265954   0.4753155   -0.230000    0.275000    0.4777000
+43212.00  -0.234116   0.269558   0.4721797   -0.231000    0.278000    0.4748000
+43213.00  -0.234025   0.273222   0.4691810   -0.232000    0.282000    0.4718000
+43214.00  -0.233903   0.276956   0.4662904   -0.233000    0.286000    0.4688000
+43215.00  -0.233757   0.280757   0.4634546   -0.234000    0.290000    0.4658000
+43216.00  -0.233593   0.284619   0.4606137   -0.235000    0.294000    0.4627000
+43217.00  -0.233417   0.288535   0.4577165   -0.235000    0.297000    0.4597000
+43218.00  -0.233234   0.292501   0.4547318   -0.236000    0.301000    0.4566000
+43219.00  -0.233050   0.296512   0.4516527   -0.237000    0.305000    0.4536000
+43220.00  -0.232852   0.300562   0.4484970   -0.237000    0.309000    0.4505000
+43221.00  -0.232628   0.304642   0.4453000   -0.237000    0.313000    0.4474000
+43222.00  -0.232363   0.308747   0.4421034   -0.238000    0.316000    0.4444000
+43223.00  -0.232043   0.312871   0.4389447   -0.238000    0.320000    0.4413000
+43224.00  -0.231654   0.317004   0.4358503   -0.238000    0.324000    0.4382000
+43225.00  -0.231184   0.321135   0.4328343   -0.238000    0.328000    0.4350000
+43226.00  -0.230620   0.325247   0.4298967   -0.238000    0.332000    0.4319000
+43227.00  -0.229953   0.329327   0.4270241   -0.237000    0.335000    0.4287000
+43228.00  -0.229171   0.333361   0.4241914   -0.237000    0.339000    0.4256000
+43229.00  -0.228262   0.337335   0.4213638   -0.237000    0.343000    0.4224000
+43230.00  -0.227216   0.341235   0.4184999   -0.237000    0.347000    0.4192000
+43231.00  -0.226025   0.345047   0.4155560   -0.236000    0.351000    0.4160000
+43232.00  -0.224684   0.348760   0.4124907   -0.236000    0.354000    0.4129000
+43233.00  -0.223197   0.352373   0.4092696   -0.235000    0.358000    0.4097000
+43234.00  -0.221567   0.355890   0.4058751   -0.235000    0.362000    0.4065000
+43235.00  -0.219801   0.359314   0.4023164   -0.234000    0.365000    0.4033000
+43236.00  -0.217905   0.362650   0.3986364   -0.234000    0.369000    0.4000000
+43237.00  -0.215888   0.365903   0.3949074   -0.233000    0.372000    0.3968000
+43238.00  -0.213757   0.369077   0.3912141   -0.233000    0.376000    0.3935000
+43239.00  -0.211521   0.372177   0.3876271   -0.232000    0.379000    0.3903000
+43240.00  -0.209204   0.375211   0.3841797   -0.231000    0.382000    0.3870000
+43241.00  -0.206833   0.378192   0.3808609   -0.230000    0.385000    0.3838000
+43242.00  -0.204436   0.381129   0.3776260   -0.228000    0.389000    0.3805000
+43243.00  -0.202037   0.384034   0.3744170   -0.227000    0.392000    0.3773000
+43244.00  -0.199634   0.386910   0.3711831   -0.226000    0.395000    0.3740000
+43245.00  -0.197223   0.389761   0.3678933   -0.224000    0.398000    0.3707000
+43246.00  -0.194800   0.392590   0.3645398   -0.222000    0.401000    0.3674000
+43247.00  -0.192364   0.395402   0.3611356   -0.221000    0.405000    0.3641000
+43248.00  -0.189909   0.398198   0.3577092   -0.219000    0.408000    0.3608000
+43249.00  -0.187432   0.400979   0.3542975   -0.217000    0.411000    0.3575000
+43250.00  -0.184928   0.403743   0.3509383   -0.215000    0.414000    0.3542000
+43251.00  -0.182393   0.406489   0.3476626   -0.213000    0.417000    0.3509000
+43252.00  -0.179824   0.409217   0.3444889   -0.210000    0.420000    0.3476000
+43253.00  -0.177220   0.411927   0.3414210   -0.208000    0.423000    0.3443000
+43254.00  -0.174581   0.414621   0.3384487   -0.206000    0.426000    0.3410000
+43255.00  -0.171908   0.417300   0.3355486   -0.204000    0.429000    0.3377000
+43256.00  -0.169202   0.419965   0.3326873   -0.201000    0.432000    0.3345000
+43257.00  -0.166465   0.422621   0.3298248   -0.199000    0.435000    0.3312000
+43258.00  -0.163700   0.425272   0.3269178   -0.196000    0.438000    0.3280000
+43259.00  -0.160921   0.427918   0.3239247   -0.194000    0.441000    0.3247000
+43260.00  -0.158151   0.430559   0.3208092   -0.191000    0.444000    0.3215000
+43261.00  -0.155409   0.433193   0.3175457   -0.189000    0.446000    0.3183000
+43262.00  -0.152699   0.435805   0.3141276   -0.186000    0.449000    0.3150000
+43263.00  -0.150019   0.438383   0.3105747   -0.184000    0.451000    0.3118000
+43264.00  -0.147367   0.440911   0.3069376   -0.181000    0.454000    0.3086000
+43265.00  -0.144737   0.443377   0.3032920   -0.178000    0.457000    0.3054000
+43266.00  -0.142125   0.445774   0.2997180   -0.175000    0.459000    0.3022000
+43267.00  -0.139531   0.448096   0.2962739   -0.173000    0.462000    0.2991000
+43268.00  -0.136957   0.450348   0.2929751   -0.170000    0.464000    0.2959000
+43269.00  -0.134402   0.452536   0.2897933   -0.167000    0.467000    0.2927000
+43270.00  -0.131868   0.454667   0.2866728   -0.164000    0.469000    0.2896000
+43271.00  -0.129341   0.456743   0.2835566   -0.161000    0.471000    0.2865000
+43272.00  -0.126805   0.458766   0.2804070   -0.158000    0.474000    0.2833000
+43273.00  -0.124248   0.460739   0.2772138   -0.155000    0.476000    0.2802000
+43274.00  -0.121648   0.462667   0.2739915   -0.152000    0.478000    0.2771000
+43275.00  -0.118986   0.464559   0.2707687   -0.149000    0.480000    0.2741000
+43276.00  -0.116243   0.466423   0.2675802   -0.145000    0.482000    0.2710000
+43277.00  -0.113404   0.468267   0.2644600   -0.142000    0.483000    0.2680000
+43278.00  -0.110466   0.470091   0.2614370   -0.138000    0.485000    0.2649000
+43279.00  -0.107435   0.471893   0.2585308   -0.135000    0.487000    0.2619000
+43280.00  -0.104316   0.473673   0.2557486   -0.131000    0.488000    0.2590000
+43281.00  -0.101114   0.475428   0.2530837   -0.128000    0.490000    0.2560000
+43282.00  -0.097835   0.477158   0.2505171   -0.124000    0.491000    0.2531000
+43283.00  -0.094479   0.478858   0.2480201   -0.121000    0.493000    0.2501000
+43284.00  -0.091047   0.480525   0.2455580   -0.117000    0.494000    0.2472000
+43285.00  -0.087541   0.482158   0.2430929   -0.113000    0.495000    0.2444000
+43286.00  -0.083960   0.483754   0.2405877   -0.109000    0.496000    0.2416000
+43287.00  -0.080306   0.485308   0.2380093   -0.105000    0.497000    0.2388000
+43288.00  -0.076579   0.486820   0.2353333   -0.101000    0.498000    0.2360000
+43289.00  -0.072781   0.488284   0.2325485   -0.097000    0.499000    0.2332000
+43290.00  -0.068911   0.489700   0.2296621   -0.093000    0.500000    0.2305000
+43291.00  -0.064971   0.491064   0.2267043   -0.089000    0.500000    0.2278000
+43292.00  -0.060961   0.492372   0.2237282   -0.084000    0.501000    0.2251000
+43293.00  -0.056883   0.493623   0.2208005   -0.080000    0.501000    0.2224000
+43294.00  -0.052743   0.494815   0.2179811   -0.076000    0.502000    0.2197000
+43295.00  -0.048549   0.495946   0.2153028   -0.072000    0.503000    0.2172000
+43296.00  -0.044308   0.497014   0.2127578   -0.067000    0.503000    0.2146000
+43297.00  -0.040027   0.498019   0.2103014   -0.063000    0.504000    0.2121000
+43298.00  -0.035714   0.498957   0.2078726   -0.058000    0.504000    0.2095000
+43299.00  -0.031377   0.499829   0.2054199   -0.054000    0.505000    0.2070000
+43300.00  -0.027023   0.500632   0.2029206   -0.050000    0.505000    0.2046000
+43301.00  -0.022658   0.501365   0.2003829   -0.045000    0.506000    0.2021000
+43302.00  -0.018292   0.502026   0.1978364   -0.041000    0.506000    0.1997000
+43303.00  -0.013929   0.502613   0.1953189   -0.036000    0.507000    0.1972000
+43304.00  -0.009571   0.503126   0.1928660   -0.032000    0.507000    0.1948000
+43305.00  -0.005223   0.503562   0.1905050   -0.027000    0.507000    0.1925000
+43306.00  -0.000887   0.503920   0.1882536   -0.023000    0.507000    0.1902000
+43307.00   0.003430   0.504204   0.1861192   -0.018000    0.508000    0.1878000
+43308.00   0.007721   0.504419   0.1840966   -0.014000    0.508000    0.1855000
+43309.00   0.011979   0.504571   0.1821681   -0.009000    0.508000    0.1832000
+43310.00   0.016202   0.504661   0.1803069   -0.004000    0.508000    0.1810000
+43311.00   0.020388   0.504691   0.1784795    0.000000    0.508000    0.1788000
+43312.00   0.024538   0.504661   0.1766499    0.005000    0.508000    0.1765000
+43313.00   0.028650   0.504573   0.1747829    0.009000    0.508000    0.1743000
+43314.00   0.032724   0.504425   0.1728472    0.014000    0.508000    0.1721000
+43315.00   0.036766   0.504209   0.1708190    0.018000    0.507000    0.1700000
+43316.00   0.040780   0.503916   0.1686870    0.023000    0.507000    0.1679000
+43317.00   0.044771   0.503537   0.1664562    0.027000    0.506000    0.1658000
+43318.00   0.048745   0.503064   0.1641497    0.032000    0.506000    0.1637000
+43319.00   0.052704   0.502488   0.1618082    0.036000    0.505000    0.1616000
+43320.00   0.056653   0.501798   0.1594844    0.040000    0.504000    0.1596000
+43321.00   0.060594   0.500987   0.1572312    0.045000    0.503000    0.1576000
+43322.00   0.064541   0.500045   0.1550854    0.049000    0.503000    0.1556000
+43323.00   0.068508   0.498965   0.1530532    0.054000    0.502000    0.1536000
+43324.00   0.072512   0.497736   0.1511055    0.058000    0.501000    0.1516000
+43325.00   0.076569   0.496352   0.1491874    0.062000    0.500000    0.1497000
+43326.00   0.080691   0.494806   0.1472403    0.067000    0.498000    0.1478000
+43327.00   0.084878   0.493103   0.1452269    0.071000    0.497000    0.1458000
+43328.00   0.089128   0.491249   0.1431446    0.076000    0.495000    0.1439000
+43329.00   0.093441   0.489249   0.1410233    0.080000    0.494000    0.1420000
+43330.00   0.097811   0.487111   0.1389103    0.084000    0.493000    0.1401000
+43331.00   0.102226   0.484849   0.1368509    0.089000    0.491000    0.1382000
+43332.00   0.106665   0.482482   0.1348787    0.093000    0.490000    0.1364000
+43333.00   0.111111   0.480027   0.1330132    0.098000    0.488000    0.1345000
+43334.00   0.115549   0.477496   0.1312618    0.102000    0.487000    0.1326000
+43335.00   0.119963   0.474902   0.1296198    0.106000    0.485000    0.1307000
+43336.00   0.124338   0.472259   0.1280705    0.111000    0.483000    0.1289000
+43337.00   0.128654   0.469581   0.1265848    0.115000    0.482000    0.1270000
+43338.00   0.132889   0.466887   0.1251247    0.120000    0.480000    0.1252000
+43339.00   0.137022   0.464193   0.1236482    0.124000    0.478000    0.1233000
+43340.00   0.141046   0.461511   0.1221143    0.128000    0.476000    0.1215000
+43341.00   0.144961   0.458851   0.1204865    0.132000    0.474000    0.1196000
+43342.00   0.148769   0.456223   0.1187374    0.137000    0.472000    0.1178000
+43343.00   0.152475   0.453630   0.1168530    0.141000    0.470000    0.1159000
+43344.00   0.156086   0.451069   0.1148374    0.145000    0.468000    0.1141000
+43345.00   0.159611   0.448538   0.1127143    0.149000    0.466000    0.1123000
+43346.00   0.163059   0.446031   0.1105254    0.153000    0.464000    0.1105000
+43347.00   0.166440   0.443541   0.1083208    0.157000    0.461000    0.1086000
+43348.00   0.169759   0.441062   0.1061490    0.161000    0.459000    0.1068000
+43349.00   0.173020   0.438590   0.1040455    0.165000    0.457000    0.1050000
+43350.00   0.176223   0.436122   0.1020235    0.169000    0.454000    0.1031000
+43351.00   0.179368   0.433652   0.1000677    0.172000    0.452000    0.1012000
+43352.00   0.182457   0.431178   0.0981372    0.176000    0.449000    0.0994000
+43353.00   0.185491   0.428696   0.0961782    0.179000    0.447000    0.0975000
+43354.00   0.188469   0.426202   0.0941448    0.183000    0.444000    0.0956000
+43355.00   0.191393   0.423697   0.0920173    0.186000    0.441000    0.0937000
+43356.00   0.194260   0.421184   0.0898110    0.190000    0.439000    0.0918000
+43357.00   0.197068   0.418666   0.0875704    0.193000    0.436000    0.0900000
+43358.00   0.199812   0.416148   0.0853505    0.197000    0.434000    0.0881000
+43359.00   0.202487   0.413635   0.0831987    0.200000    0.431000    0.0862000
+43360.00   0.205086   0.411134   0.0811463    0.203000    0.428000    0.0843000
+43361.00   0.207607   0.408645   0.0792069    0.207000    0.425000    0.0824000
+43362.00   0.210050   0.406167   0.0773793    0.210000    0.422000    0.0804000
+43363.00   0.212415   0.403695   0.0756495    0.214000    0.419000    0.0785000
+43364.00   0.214700   0.401224   0.0739912    0.217000    0.416000    0.0766000
+43365.00   0.216906   0.398749   0.0723674    0.220000    0.413000    0.0746000
+43366.00   0.219033   0.396262   0.0707352    0.223000    0.410000    0.0726000
+43367.00   0.221096   0.393754   0.0690521    0.227000    0.406000    0.0705000
+43368.00   0.223113   0.391214   0.0672798    0.230000    0.403000    0.0685000
+43369.00   0.225102   0.388632   0.0653877    0.233000    0.400000    0.0665000
+43370.00   0.227082   0.385999   0.0633572    0.236000    0.397000    0.0644000
+43371.00   0.229062   0.383309   0.0611873    0.239000    0.393000    0.0622000
+43372.00   0.231045   0.380563   0.0588980    0.241000    0.390000    0.0601000
+43373.00   0.233032   0.377760   0.0565309    0.244000    0.386000    0.0579000
+43374.00   0.235027   0.374900   0.0541403    0.247000    0.383000    0.0558000
+43375.00   0.237031   0.371983   0.0517795    0.250000    0.380000    0.0536000
+43376.00   0.239047   0.369011   0.0494863    0.252000    0.377000    0.0514000
+43377.00   0.241077   0.365981   0.0472730    0.255000    0.373000    0.0491000
+43378.00   0.243123   0.362896   0.0451234    0.257000    0.370000    0.0469000
+43379.00   0.245184   0.359754   0.0429967    0.260000    0.367000    0.0447000
+43380.00   0.247249   0.356560   0.0408378    0.262000    0.364000    0.0423000
+43381.00   0.249300   0.353319   0.0385923    0.265000    0.360000    0.0400000
+43382.00   0.251321   0.350034   0.0362236    0.267000    0.357000    0.0376000
+43383.00   0.253295   0.346710   0.0337251    0.270000    0.353000    0.0353000
+43384.00   0.255206   0.343352   0.0311225    0.272000    0.350000    0.0329000
+43385.00   0.257037   0.339965   0.0284644    0.274000    0.346000    0.0304000
+43386.00   0.258773   0.336553   0.0258056    0.276000    0.343000    0.0279000
+43387.00   0.260403   0.333124   0.0231927    0.278000    0.339000    0.0254000
+43388.00   0.261918   0.329686   0.0206533    0.280000    0.336000    0.0229000
+43389.00   0.263308   0.326247   0.0181958    0.282000    0.332000    0.0204000
+43390.00   0.264561   0.322814   0.0158123    0.283000    0.329000    0.0177000
+43391.00   0.265669   0.319396   0.0134830    0.285000    0.325000    0.0150000
+43392.00   0.266628   0.315994   0.0111780    0.286000    0.322000    0.0123000
+43393.00   0.267445   0.312596   0.0088600    0.288000    0.318000    0.0096000
+43394.00   0.268133   0.309189   0.0064876    0.289000    0.315000    0.0069000
+43395.00   0.268701   0.305760   0.0040210    0.290000    0.311000    0.0041000
+43396.00   0.269160   0.302295   0.0014261    0.291000    0.308000    0.0013000
+43397.00   0.269522   0.298782  -0.0013213    0.292000    0.304000   -0.0015000
+43398.00   0.269796   0.295208  -0.0042306    0.293000    0.301000   -0.0043000
+43399.00   0.269986   0.291573  -0.0072892    0.294000    0.297000   -0.0071000
+43400.00   0.270092   0.287884  -0.0104594    0.295000    0.293000   -0.0100000
+43401.00   0.270114   0.284148  -0.0136818    0.296000    0.289000   -0.0129000
+43402.00   0.270054   0.280373  -0.0168882    0.296000    0.286000   -0.0157000
+43403.00   0.269909   0.276564  -0.0200210    0.297000    0.282000   -0.0186000
+43404.00   0.269682   0.272729  -0.0230495    0.298000    0.278000   -0.0215000
+43405.00   0.269372   0.268874  -0.0259776    0.298000    0.274000   -0.0244000
+43406.00   0.268979   0.265007  -0.0288387    0.298000    0.271000   -0.0274000
+43407.00   0.268504   0.261134  -0.0316835    0.299000    0.267000   -0.0303000
+43408.00   0.267952   0.257254  -0.0345648    0.299000    0.264000   -0.0333000
+43409.00   0.267328   0.253366  -0.0375231    0.299000    0.260000   -0.0362000
+43410.00   0.266637   0.249467  -0.0405750    0.299000    0.256000   -0.0392000
+43411.00   0.265886   0.245557  -0.0437097    0.299000    0.252000   -0.0422000
+43412.00   0.265079   0.241635  -0.0468915    0.299000    0.249000   -0.0451000
+43413.00   0.264214   0.237704  -0.0500708    0.299000    0.245000   -0.0481000
+43414.00   0.263284   0.233771  -0.0531981    0.299000    0.241000   -0.0511000
+43415.00   0.262283   0.229846  -0.0562362    0.298000    0.237000   -0.0541000
+43416.00   0.261204   0.225936  -0.0591666    0.298000    0.233000   -0.0571000
+43417.00   0.260040   0.222048  -0.0619897    0.297000    0.230000   -0.0601000
+43418.00   0.258791   0.218186  -0.0647210    0.297000    0.226000   -0.0631000
+43419.00   0.257454   0.214353  -0.0673890    0.296000    0.222000   -0.0661000
+43420.00   0.256033   0.210549  -0.0700321    0.295000    0.218000   -0.0692000
+43421.00   0.254546   0.206766  -0.0726932    0.294000    0.214000   -0.0723000
+43422.00   0.253012   0.202993  -0.0754167    0.293000    0.211000   -0.0755000
+43423.00   0.251451   0.199220  -0.0782432    0.292000    0.207000   -0.0786000
+43424.00   0.249883   0.195435  -0.0812060    0.291000    0.203000   -0.0817000
+43425.00   0.248334   0.191630  -0.0843280    0.289000    0.199000   -0.0849000
+43426.00   0.246824   0.187794  -0.0876157    0.288000    0.195000   -0.0881000
+43427.00   0.245357   0.183928  -0.0910496    0.286000    0.192000   -0.0912000
+43428.00   0.243924   0.180034  -0.0945828    0.285000    0.188000   -0.0944000
+43429.00   0.242517   0.176116  -0.0981487    0.283000    0.184000   -0.0976000
+43430.00   0.241125   0.172177  -0.1016771    0.281000    0.180000   -0.1009000
+43431.00   0.239742   0.168223  -0.1051158    0.279000    0.177000   -0.1041000
+43432.00   0.238365   0.164261  -0.1084474    0.277000    0.173000   -0.1074000
+43433.00   0.236992   0.160299  -0.1116924    0.275000    0.170000   -0.1106000
+43434.00   0.235622   0.156343  -0.1148973    0.273000    0.166000   -0.1139000
+43435.00   0.234244   0.152405  -0.1181151    0.271000    0.163000   -0.1172000
+43436.00   0.232834   0.148495  -0.1213882    0.268000    0.159000   -0.1204000
+43437.00   0.231360   0.144629  -0.1247362    0.266000    0.156000   -0.1237000
+43438.00   0.229791   0.140821  -0.1281541    0.263000    0.152000   -0.1269000
+43439.00   0.228100   0.137082  -0.1316161    0.261000    0.149000   -0.1302000
+43440.00   0.226268   0.133418  -0.1350826    0.258000    0.146000   -0.1334000
+43441.00   0.224273   0.129837  -0.1385092    0.256000    0.142000   -0.1367000
+43442.00   0.222098   0.126344  -0.1418558    0.253000    0.139000   -0.1399000
+43443.00   0.219726   0.122945  -0.1450950    0.251000    0.135000   -0.1432000
+43444.00   0.217146   0.119647  -0.1482166    0.248000    0.132000   -0.1464000
+43445.00   0.214360   0.116453  -0.1512272    0.245000    0.129000   -0.1496000
+43446.00   0.211380   0.113368  -0.1541470    0.242000    0.126000   -0.1528000
+43447.00   0.208216   0.110397  -0.1570059    0.240000    0.122000   -0.1559000
+43448.00   0.204878   0.107544  -0.1598398    0.237000    0.119000   -0.1591000
+43449.00   0.201393   0.104809  -0.1626876    0.234000    0.116000   -0.1623000
+43450.00   0.197804   0.102181  -0.1655874    0.231000    0.113000   -0.1654000
+43451.00   0.194157   0.099653  -0.1685717    0.228000    0.110000   -0.1685000
+43452.00   0.190500   0.097216  -0.1716640    0.226000    0.108000   -0.1716000
+43453.00   0.186872   0.094859  -0.1748758    0.223000    0.105000   -0.1747000
+43454.00   0.183296   0.092575  -0.1782032    0.220000    0.102000   -0.1778000
+43455.00   0.179789   0.090356  -0.1816217    0.217000    0.099000   -0.1809000
+43456.00   0.176367   0.088191  -0.1850845    0.214000    0.097000   -0.1840000
+43457.00   0.173044   0.086073  -0.1885299    0.212000    0.094000   -0.1870000
+43458.00   0.169819   0.083993  -0.1918985    0.209000    0.092000   -0.1901000
+43459.00   0.166678   0.081943  -0.1951549    0.206000    0.089000   -0.1932000
+43460.00   0.163610   0.079914  -0.1983014    0.203000    0.087000   -0.1963000
+43461.00   0.160601   0.077898  -0.2013761    0.200000    0.084000   -0.1994000
+43462.00   0.157639   0.075885  -0.2044350    0.198000    0.082000   -0.2024000
+43463.00   0.154712   0.073867  -0.2075283    0.195000    0.079000   -0.2055000
+43464.00   0.151806   0.071837  -0.2106822    0.192000    0.077000   -0.2086000
+43465.00   0.148908   0.069796  -0.2138939    0.189000    0.075000   -0.2117000
+43466.00   0.146001   0.067748  -0.2171378    0.186000    0.073000   -0.2148000
+43467.00   0.143069   0.065696  -0.2203774    0.184000    0.070000   -0.2178000
+43468.00   0.140096   0.063645  -0.2235741    0.181000    0.068000   -0.2209000
+43469.00   0.137068   0.061601  -0.2266946    0.178000    0.066000   -0.2240000
+43470.00   0.133967   0.059568  -0.2297155    0.175000    0.064000   -0.2271000
+43471.00   0.130783   0.057551  -0.2326260    0.172000    0.062000   -0.2302000
+43472.00   0.127525   0.055548  -0.2354300    0.169000    0.060000   -0.2332000
+43473.00   0.124210   0.053556  -0.2381449    0.166000    0.058000   -0.2363000
+43474.00   0.120857   0.051573  -0.2407984    0.163000    0.056000   -0.2394000
+43475.00   0.117476   0.049596  -0.2434254    0.160000    0.054000   -0.2424000
+43476.00   0.114076   0.047626  -0.2460646    0.157000    0.052000   -0.2455000
+43477.00   0.110660   0.045664  -0.2487541    0.153000    0.051000   -0.2485000
+43478.00   0.107236   0.043711  -0.2515275    0.150000    0.049000   -0.2516000
+43479.00   0.103810   0.041766  -0.2544109    0.147000    0.047000   -0.2546000
+43480.00   0.100399   0.039832  -0.2574196    0.143000    0.045000   -0.2577000
+43481.00   0.097018   0.037907  -0.2605561    0.140000    0.043000   -0.2608000
+43482.00   0.093685   0.035992  -0.2638080    0.136000    0.042000   -0.2640000
+43483.00   0.090413   0.034089  -0.2671458    0.133000    0.040000   -0.2671000
+43484.00   0.087214   0.032206  -0.2705236    0.129000    0.038000   -0.2702000
+43485.00   0.084094   0.030357  -0.2738882    0.125000    0.036000   -0.2734000
+43486.00   0.081061   0.028552  -0.2771957    0.121000    0.035000   -0.2766000
+43487.00   0.078120   0.026806  -0.2804311    0.117000    0.033000   -0.2797000
+43488.00   0.075273   0.025137  -0.2836161    0.113000    0.032000   -0.2829000
+43489.00   0.072525   0.023559  -0.2867999    0.109000    0.030000   -0.2861000
+43490.00   0.069874   0.022086  -0.2900354    0.105000    0.029000   -0.2893000
+43491.00   0.067317   0.020731  -0.2933573    0.101000    0.028000   -0.2925000
+43492.00   0.064853   0.019504  -0.2967677    0.096000    0.026000   -0.2957000
+43493.00   0.062474   0.018405  -0.3002377    0.092000    0.025000   -0.2989000
+43494.00   0.060173   0.017429  -0.3037211    0.088000    0.024000   -0.3021000
+43495.00   0.057942   0.016575  -0.3071703    0.084000    0.023000   -0.3053000
+43496.00   0.055757   0.015844  -0.3105474    0.080000    0.022000   -0.3086000
+43497.00   0.053585   0.015243  -0.3138279    0.075000    0.021000   -0.3118000
+43498.00   0.051390   0.014776  -0.3170006    0.071000    0.020000   -0.3151000
+43499.00   0.049138   0.014450  -0.3200671    0.067000    0.019000   -0.3183000
+43500.00   0.046792   0.014270  -0.3230414    0.063000    0.018000   -0.3215000
+43501.00   0.044321   0.014241  -0.3259482    0.058000    0.018000   -0.3248000
+43502.00   0.041712   0.014360  -0.3288195    0.054000    0.017000   -0.3280000
+43503.00   0.038970   0.014618  -0.3316910    0.049000    0.017000   -0.3313000
+43504.00   0.036101   0.015005  -0.3345972    0.045000    0.016000   -0.3345000
+43505.00   0.033110   0.015512  -0.3375686    0.041000    0.016000   -0.3377000
+43506.00   0.030003   0.016128  -0.3406283    0.036000    0.016000   -0.3410000
+43507.00   0.026786   0.016844  -0.3437893    0.032000    0.015000   -0.3442000
+43508.00   0.023463   0.017651  -0.3470520    0.027000    0.015000   -0.3475000
+43509.00   0.020040   0.018540   0.6495968    0.023000    0.015000    0.6493000
+43510.00   0.016522   0.019499   0.6461826    0.019000    0.015000    0.6461000
+43511.00   0.012916   0.020520   0.6427420    0.015000    0.015000    0.6429000
+43512.00   0.009227   0.021594   0.6393177    0.011000    0.015000    0.6398000
+43513.00   0.005460   0.022710   0.6359491    0.007000    0.015000    0.6366000
+43514.00   0.001620   0.023858   0.6326596    0.003000    0.015000    0.6334000
+43515.00  -0.002286   0.025031   0.6294446   -0.001000    0.016000    0.6303000
+43516.00  -0.006254   0.026216   0.6262694   -0.005000    0.016000    0.6271000
+43517.00  -0.010278   0.027406   0.6230813   -0.008000    0.017000    0.6240000
+43518.00  -0.014352   0.028591   0.6198327   -0.012000    0.017000    0.6208000
+43519.00  -0.018472   0.029769   0.6165018   -0.016000    0.018000    0.6177000
+43520.00  -0.022634   0.030938   0.6131041   -0.020000    0.019000    0.6146000
+43521.00  -0.026834   0.032100   0.6096852   -0.024000    0.020000    0.6115000
+43522.00  -0.031067   0.033253   0.6063026   -0.027000    0.021000    0.6083000
+43523.00  -0.035329   0.034397   0.6030057   -0.031000    0.022000    0.6052000
+43524.00  -0.039617   0.035533   0.5998260   -0.035000    0.023000    0.6021000
+43525.00  -0.043925   0.036659   0.5967754   -0.039000    0.024000    0.5990000
+43526.00  -0.048250   0.037777   0.5938494   -0.042000    0.026000    0.5960000
+43527.00  -0.052587   0.038885   0.5910312   -0.046000    0.027000    0.5929000
+43528.00  -0.056933   0.039983   0.5882930   -0.049000    0.029000    0.5899000
+43529.00  -0.061283   0.041071   0.5855990   -0.053000    0.030000    0.5868000
+43530.00  -0.065632   0.042149   0.5829089   -0.057000    0.032000    0.5838000
+43531.00  -0.069978   0.043217   0.5801827   -0.060000    0.033000    0.5807000
+43532.00  -0.074314   0.044276   0.5773848   -0.064000    0.035000    0.5777000
+43533.00  -0.078629   0.045329   0.5744866   -0.067000    0.036000    0.5746000
+43534.00  -0.082914   0.046382   0.5714703   -0.071000    0.038000    0.5716000
+43535.00  -0.087156   0.047440   0.5683313   -0.075000    0.040000    0.5685000
+43536.00  -0.091345   0.048508   0.5650802   -0.079000    0.042000    0.5654000
+43537.00  -0.095469   0.049592   0.5617421   -0.082000    0.045000    0.5623000
+43538.00  -0.099518   0.050695   0.5583520   -0.086000    0.047000    0.5592000
+43539.00  -0.103479   0.051823   0.5549479   -0.090000    0.049000    0.5561000
+43540.00  -0.107343   0.052982   0.5515624   -0.094000    0.051000    0.5529000
+43541.00  -0.111101   0.054176   0.5482159   -0.097000    0.054000    0.5497000
+43542.00  -0.114749   0.055410   0.5449082   -0.101000    0.056000    0.5465000
+43543.00  -0.118282   0.056689   0.5416135   -0.104000    0.059000    0.5433000
+43544.00  -0.121695   0.058017   0.5382847   -0.108000    0.061000    0.5401000
+43545.00  -0.124985   0.059400   0.5348688   -0.111000    0.063000    0.5367000
+43546.00  -0.128145   0.060842   0.5313286   -0.114000    0.066000    0.5333000
+43547.00  -0.131173   0.062348   0.5276610   -0.118000    0.068000    0.5300000
+43548.00  -0.134063   0.063923   0.5239011   -0.121000    0.071000    0.5266000
+43549.00  -0.136811   0.065571   0.5201109   -0.124000    0.073000    0.5232000
+43550.00  -0.139412   0.067299   0.5163583   -0.127000    0.076000    0.5197000
+43551.00  -0.141862   0.069109   0.5126973   -0.130000    0.078000    0.5162000
+43552.00  -0.144157   0.071007   0.5091584   -0.133000    0.081000    0.5128000
+43553.00  -0.146306   0.072987   0.5057484   -0.136000    0.083000    0.5093000
+43554.00  -0.148325   0.075040   0.5024562   -0.139000    0.086000    0.5058000
+43555.00  -0.150229   0.077157   0.4992585   -0.142000    0.089000    0.5023000
+43556.00  -0.152032   0.079328   0.4961223   -0.145000    0.091000    0.4988000
+43557.00  -0.153750   0.081544   0.4930084   -0.148000    0.094000    0.4952000
+43558.00  -0.155398   0.083795   0.4898759   -0.151000    0.096000    0.4917000
+43559.00  -0.156991   0.086073   0.4866864   -0.154000    0.099000    0.4882000
+43560.00  -0.158545   0.088367   0.4834090   -0.157000    0.102000    0.4847000
+43561.00  -0.160073   0.090673   0.4800234   -0.160000    0.104000    0.4812000
+43562.00  -0.161591   0.092993   0.4765236   -0.162000    0.107000    0.4776000
+43563.00  -0.163112   0.095331   0.4729200   -0.165000    0.109000    0.4741000
+43564.00  -0.164649   0.097691   0.4692407   -0.168000    0.112000    0.4706000
+43565.00  -0.166217   0.100078   0.4655291   -0.171000    0.115000    0.4671000
+43566.00  -0.167826   0.102497   0.4618338   -0.174000    0.118000    0.4636000
+43567.00  -0.169483   0.104958   0.4581963   -0.176000    0.120000    0.4602000
+43568.00  -0.171196   0.107469   0.4546412   -0.179000    0.123000    0.4567000
+43569.00  -0.172969   0.110041   0.4511691   -0.182000    0.126000    0.4532000
+43570.00  -0.174810   0.112681   0.4477555   -0.185000    0.129000    0.4498000
+43571.00  -0.176725   0.115401   0.4443550   -0.187000    0.132000    0.4464000
+43572.00  -0.178721   0.118208   0.4409123   -0.190000    0.136000    0.4429000
+43573.00  -0.180803   0.121112   0.4373785   -0.192000    0.139000    0.4395000
+43574.00  -0.182979   0.124123   0.4337276   -0.195000    0.142000    0.4361000
+43575.00  -0.185247   0.127245   0.4299692   -0.197000    0.146000    0.4328000
+43576.00  -0.187587   0.130475   0.4261477   -0.199000    0.149000    0.4294000
+43577.00  -0.189978   0.133808   0.4223286   -0.202000    0.153000    0.4261000
+43578.00  -0.192398   0.137237   0.4185771   -0.204000    0.156000    0.4227000
+43579.00  -0.194826   0.140760   0.4149412   -0.206000    0.160000    0.4194000
+43580.00  -0.197240   0.144369   0.4114441   -0.208000    0.164000    0.4161000
+43581.00  -0.199617   0.148061   0.4080856   -0.210000    0.168000    0.4128000
+43582.00  -0.201929   0.151830   0.4048488   -0.212000    0.172000    0.4096000
+43583.00  -0.204152   0.155670   0.4017049   -0.214000    0.176000    0.4063000
+43584.00  -0.206260   0.159578   0.3986177   -0.216000    0.180000    0.4030000
+43585.00  -0.208229   0.163547   0.3955460   -0.218000    0.184000    0.3997000
+43586.00  -0.210050   0.167578   0.3924463   -0.220000    0.188000    0.3964000
+43587.00  -0.211723   0.171670   0.3892796   -0.222000    0.192000    0.3930000
+43588.00  -0.213243   0.175824   0.3860160   -0.224000    0.196000    0.3897000
+43589.00  -0.214610   0.180039   0.3826376   -0.226000    0.200000    0.3864000
+43590.00  -0.215820   0.184315   0.3791411   -0.228000    0.204000    0.3830000
+43591.00  -0.216879   0.188649   0.3755424   -0.229000    0.208000    0.3796000
+43592.00  -0.217801   0.193031   0.3718781   -0.231000    0.212000    0.3762000
+43593.00  -0.218600   0.197451   0.3681999   -0.232000    0.216000    0.3728000
+43594.00  -0.219292   0.201898   0.3645615   -0.234000    0.220000    0.3694000
+43595.00  -0.219892   0.206364   0.3610038   -0.235000    0.224000    0.3660000
+43596.00  -0.220414   0.210837   0.3575414   -0.236000    0.228000    0.3625000
+43597.00  -0.220871   0.215307   0.3541595   -0.238000    0.232000    0.3591000
+43598.00  -0.221278   0.219764   0.3508197   -0.239000    0.236000    0.3556000
+43599.00  -0.221646   0.224195   0.3474716   -0.240000    0.240000    0.3522000
+43600.00  -0.221987   0.228597   0.3440670   -0.241000    0.244000    0.3487000
+43601.00  -0.222313   0.232972   0.3405726   -0.242000    0.248000    0.3452000
+43602.00  -0.222636   0.237325   0.3369818   -0.242000    0.253000    0.3416000
+43603.00  -0.222965   0.241659   0.3333181   -0.243000    0.257000    0.3381000
+43604.00  -0.223290   0.245977   0.3296311   -0.244000    0.261000    0.3346000
+43605.00  -0.223596   0.250283   0.3259824   -0.245000    0.265000    0.3311000
+43606.00  -0.223867   0.254578   0.3224283   -0.245000    0.269000    0.3276000
+43607.00  -0.224091   0.258862   0.3190052   -0.246000    0.274000    0.3240000
+43608.00  -0.224257   0.263134   0.3157250   -0.246000    0.278000    0.3205000
+43609.00  -0.224355   0.267391   0.3125785   -0.247000    0.282000    0.3170000
+43610.00  -0.224376   0.271631   0.3095413   -0.247000    0.286000    0.3135000
+43611.00  -0.224308   0.275854   0.3065796   -0.247000    0.290000    0.3101000
+43612.00  -0.224149   0.280058   0.3036531   -0.248000    0.293000    0.3066000
+43613.00  -0.223913   0.284249   0.3007180   -0.248000    0.297000    0.3032000
+43614.00  -0.223616   0.288433   0.2977329   -0.248000    0.301000    0.2997000
+43615.00  -0.223270   0.292613   0.2946647   -0.248000    0.305000    0.2964000
+43616.00  -0.222878   0.296791   0.2914932   -0.248000    0.309000    0.2930000
+43617.00  -0.222440   0.300969   0.2882108   -0.247000    0.313000    0.2897000
+43618.00  -0.221956   0.305144   0.2848250   -0.247000    0.317000    0.2863000
+43619.00  -0.221413   0.309310   0.2813607   -0.247000    0.321000    0.2830000
+43620.00  -0.220797   0.313452   0.2778616   -0.246000    0.325000    0.2798000
+43621.00  -0.220093   0.317558   0.2743831   -0.246000    0.328000    0.2765000
+43622.00  -0.219285   0.321617   0.2709789   -0.245000    0.332000    0.2733000
+43623.00  -0.218367   0.325616   0.2676812   -0.245000    0.335000    0.2700000
+43624.00  -0.217331   0.329543   0.2644895   -0.244000    0.339000    0.2668000
+43625.00  -0.216175   0.333387   0.2613712   -0.243000    0.342000    0.2636000
+43626.00  -0.214891   0.337134   0.2582750   -0.242000    0.346000    0.2605000
+43627.00  -0.213476   0.340775   0.2551491   -0.241000    0.349000    0.2573000
+43628.00  -0.211925   0.344296   0.2519580   -0.240000    0.353000    0.2542000
+43629.00  -0.210242   0.347698   0.2486897   -0.239000    0.356000    0.2510000
+43630.00  -0.208441   0.350988   0.2453578   -0.238000    0.359000    0.2478000
+43631.00  -0.206533   0.354173   0.2419965   -0.237000    0.363000    0.2446000
+43632.00  -0.204531   0.357260   0.2386522   -0.235000    0.366000    0.2415000
+43633.00  -0.202448   0.360257   0.2353720   -0.234000    0.370000    0.2383000
+43634.00  -0.200298   0.363172   0.2321928   -0.233000    0.373000    0.2351000
+43635.00  -0.198095   0.366014   0.2291324   -0.232000    0.376000    0.2319000
+43636.00  -0.195857   0.368795   0.2261885   -0.230000    0.380000    0.2287000
+43637.00  -0.193599   0.371523   0.2233411   -0.229000    0.383000    0.2256000
+43638.00  -0.191337   0.374210   0.2205588   -0.227000    0.387000    0.2224000
+43639.00  -0.189077   0.376868   0.2178060   -0.226000    0.390000    0.2192000
+43640.00  -0.186824   0.379510   0.2150448   -0.224000    0.393000    0.2160000
+43641.00  -0.184585   0.382150   0.2122382   -0.222000    0.396000    0.2128000
+43642.00  -0.182362   0.384800   0.2093543   -0.220000    0.399000    0.2095000
+43643.00  -0.180143   0.387471   0.2063708   -0.218000    0.402000    0.2063000
+43644.00  -0.177914   0.390173   0.2032768   -0.216000    0.405000    0.2031000
+43645.00  -0.175657   0.392915   0.2000735   -0.214000    0.408000    0.2000000
+43646.00  -0.173361   0.395700   0.1967754   -0.212000    0.410000    0.1969000
+43647.00  -0.171014   0.398527   0.1934123   -0.210000    0.413000    0.1937000
+43648.00  -0.168606   0.401396   0.1900291   -0.208000    0.415000    0.1906000
+43649.00  -0.166125   0.404308   0.1866781   -0.206000    0.418000    0.1875000
+43650.00  -0.163564   0.407259   0.1834043   -0.203000    0.420000    0.1845000
+43651.00  -0.160919   0.410238   0.1802282   -0.201000    0.422000    0.1815000
+43652.00  -0.158192   0.413233   0.1771364   -0.198000    0.425000    0.1786000
+43653.00  -0.155381   0.416231   0.1740874   -0.196000    0.427000    0.1756000
+43654.00  -0.152485   0.419219   0.1710302   -0.193000    0.429000    0.1726000
+43655.00  -0.149505   0.422184   0.1679265   -0.190000    0.431000    0.1697000
+43656.00  -0.146444   0.425115   0.1647655   -0.187000    0.433000    0.1669000
+43657.00  -0.143321   0.428001   0.1615647   -0.185000    0.435000    0.1640000
+43658.00  -0.140155   0.430832   0.1583622   -0.182000    0.437000    0.1612000
+43659.00  -0.136967   0.433598   0.1552040   -0.179000    0.439000    0.1583000
+43660.00  -0.133776   0.436289   0.1521349   -0.176000    0.440000    0.1556000
+43661.00  -0.130589   0.438906   0.1491926   -0.173000    0.442000    0.1530000
+43662.00  -0.127415   0.441447   0.1464011   -0.170000    0.443000    0.1503000
+43663.00  -0.124258   0.443914   0.1437676   -0.167000    0.445000    0.1477000
+43664.00  -0.121127   0.446307   0.1412826   -0.164000    0.446000    0.1450000
+43665.00  -0.118027   0.448625   0.1389238   -0.161000    0.448000    0.1424000
+43666.00  -0.114966   0.450871   0.1366611   -0.158000    0.450000    0.1399000
+43667.00  -0.111946   0.453043   0.1344602   -0.154000    0.451000    0.1373000
+43668.00  -0.108962   0.455143   0.1322833   -0.151000    0.453000    0.1348000
+43669.00  -0.106002   0.457171   0.1300956   -0.148000    0.455000    0.1322000
+43670.00  -0.103056   0.459126   0.1278696   -0.144000    0.457000    0.1298000
+43671.00  -0.100114   0.461011   0.1255884   -0.141000    0.458000    0.1274000
+43672.00  -0.097167   0.462824   0.1232466   -0.137000    0.460000    0.1250000
+43673.00  -0.094212   0.464567   0.1208500   -0.134000    0.461000    0.1226000
+43674.00  -0.091255   0.466241   0.1184157   -0.130000    0.463000    0.1202000
+43675.00  -0.088300   0.467846   0.1159719   -0.126000    0.464000    0.1179000
+43676.00  -0.085345   0.469387   0.1135555   -0.122000    0.466000    0.1156000
+43677.00  -0.082389   0.470867   0.1112044   -0.118000    0.467000    0.1134000
+43678.00  -0.079428   0.472291   0.1089430   -0.114000    0.469000    0.1111000
+43679.00  -0.076459   0.473663   0.1067689   -0.110000    0.470000    0.1088000
+43680.00  -0.073480   0.474988   0.1046477   -0.106000    0.471000    0.1066000
+43681.00  -0.070485   0.476267   0.1025246   -0.102000    0.473000    0.1044000
+43682.00  -0.067473   0.477504   0.1003467   -0.098000    0.474000    0.1023000
+43683.00  -0.064438   0.478702   0.0980864   -0.094000    0.476000    0.1001000
+43684.00  -0.061377   0.479862   0.0957528   -0.090000    0.477000    0.0979000
+43685.00  -0.058285   0.480987   0.0933861   -0.086000    0.478000    0.0958000
+43686.00  -0.055159   0.482079   0.0910414   -0.082000    0.479000    0.0937000
+43687.00  -0.051996   0.483137   0.0887710   -0.077000    0.480000    0.0916000
+43688.00  -0.048792   0.484159   0.0866126   -0.073000    0.481000    0.0895000
+43689.00  -0.045544   0.485142   0.0845858   -0.069000    0.482000    0.0874000
+43690.00  -0.042250   0.486085   0.0826924   -0.065000    0.483000    0.0854000
+43691.00  -0.038905   0.486984   0.0809192   -0.061000    0.484000    0.0834000
+43692.00  -0.035507   0.487837   0.0792409   -0.056000    0.484000    0.0814000
+43693.00  -0.032052   0.488642   0.0776234   -0.052000    0.485000    0.0794000
+43694.00  -0.028535   0.489396   0.0760289   -0.048000    0.486000    0.0774000
+43695.00  -0.024953   0.490096   0.0744199   -0.044000    0.486000    0.0755000
+43696.00  -0.021301   0.490739   0.0727630   -0.039000    0.486000    0.0736000
+43697.00  -0.017575   0.491319   0.0710331   -0.035000    0.487000    0.0718000
+43698.00  -0.013772   0.491827   0.0692151   -0.030000    0.487000    0.0699000
+43699.00  -0.009888   0.492256   0.0673059   -0.026000    0.487000    0.0680000
+43700.00  -0.005928   0.492599   0.0653151   -0.022000    0.487000    0.0662000
+43701.00  -0.001900   0.492850   0.0632630   -0.017000    0.487000    0.0644000
+43702.00   0.002188   0.493007   0.0611786   -0.013000    0.487000    0.0626000
+43703.00   0.006322   0.493071   0.0590949   -0.008000    0.487000    0.0608000
+43704.00   0.010492   0.493048   0.0570456   -0.004000    0.487000    0.0590000
+43705.00   0.014681   0.492942   0.0550569    0.000000    0.487000    0.0572000
+43706.00   0.018873   0.492757   0.0531365    0.005000    0.487000    0.0554000
+43707.00   0.023046   0.492495   0.0512652    0.009000    0.486000    0.0537000
+43708.00   0.027181   0.492160   0.0493981    0.014000    0.486000    0.0519000
+43709.00   0.031258   0.491754   0.0474797    0.018000    0.486000    0.0501000
+43710.00   0.035262   0.491279   0.0454676    0.022000    0.485000    0.0483000
+43711.00   0.039190   0.490733   0.0433524    0.027000    0.485000    0.0465000
+43712.00   0.043039   0.490114   0.0411655    0.031000    0.484000    0.0447000
+43713.00   0.046808   0.489419   0.0389671    0.036000    0.484000    0.0429000
+43714.00   0.050496   0.488646   0.0368240    0.040000    0.483000    0.0411000
+43715.00   0.054113   0.487792   0.0347876    0.044000    0.482000    0.0392000
+43716.00   0.057670   0.486857   0.0328847    0.049000    0.482000    0.0374000
+43717.00   0.061179   0.485838   0.0311179    0.053000    0.481000    0.0355000
+43718.00   0.064651   0.484736   0.0294711    0.058000    0.481000    0.0337000
+43719.00   0.068101   0.483559   0.0279158    0.062000    0.480000    0.0318000
+43720.00   0.071536   0.482311   0.0264152    0.066000    0.479000    0.0298000
+43721.00   0.074958   0.481001   0.0249285    0.071000    0.478000    0.0278000
+43722.00   0.078364   0.479640   0.0234147    0.075000    0.477000    0.0259000
+43723.00   0.081753   0.478236   0.0218367    0.080000    0.476000    0.0239000
+43724.00   0.085123   0.476798   0.0201654    0.084000    0.475000    0.0219000
+43725.00   0.088473   0.475336   0.0183812    0.088000    0.474000    0.0197000
+43726.00   0.091802   0.473857   0.0164758    0.092000    0.472000    0.0175000
+43727.00   0.095104   0.472363   0.0144542    0.097000    0.471000    0.0154000
+43728.00   0.098376   0.470854   0.0123337    0.101000    0.469000    0.0132000
+43729.00   0.101614   0.469331   0.0101418    0.105000    0.468000    0.0110000
+43730.00   0.104814   0.467794   0.0079106    0.109000    0.466000    0.0087000
+43731.00   0.107971   0.466245   0.0056707    0.113000    0.465000    0.0063000
+43732.00   0.111084   0.464682   0.0034456    0.116000    0.463000    0.0040000
+43733.00   0.114151   0.463104   0.0012434    0.120000    0.462000    0.0016000
+43734.00   0.117171   0.461510  -0.0009488    0.124000    0.460000   -0.0007000
+43735.00   0.120145   0.459899  -0.0031673    0.127000    0.458000   -0.0031000
+43736.00   0.123077   0.458278  -0.0054643    0.131000    0.457000   -0.0055000
+43737.00   0.125976   0.456650  -0.0078913    0.134000    0.455000   -0.0080000
+43738.00   0.128849   0.455022  -0.0104762    0.138000    0.454000   -0.0104000
+43739.00   0.131701   0.453397  -0.0132074    0.141000    0.452000   -0.0128000
+43740.00   0.134538   0.451778  -0.0160330    0.144000    0.450000   -0.0153000
+43741.00   0.137366   0.450165  -0.0188762    0.147000    0.448000   -0.0178000
+43742.00   0.140189   0.448559  -0.0216605    0.151000    0.447000   -0.0202000
+43743.00   0.143012   0.446960  -0.0243301    0.154000    0.445000   -0.0227000
+43744.00   0.145841   0.445366  -0.0268586    0.157000    0.443000   -0.0252000
+43745.00   0.148680   0.443773  -0.0292468    0.160000    0.441000   -0.0277000
+43746.00   0.151531   0.442177  -0.0315147    0.163000    0.439000   -0.0302000
+43747.00   0.154396   0.440571  -0.0336935    0.166000    0.437000   -0.0326000
+43748.00   0.157271   0.438948  -0.0358210    0.169000    0.435000   -0.0351000
+43749.00   0.160151   0.437299  -0.0379363    0.172000    0.433000   -0.0376000
+43750.00   0.163031   0.435617  -0.0400759    0.175000    0.431000   -0.0401000
+43751.00   0.165899   0.433894  -0.0422703    0.177000    0.428000   -0.0426000
+43752.00   0.168737   0.432123  -0.0445405    0.180000    0.426000   -0.0452000
+43753.00   0.171528   0.430297  -0.0468958    0.182000    0.423000   -0.0477000
+43754.00   0.174256   0.428408  -0.0493332    0.185000    0.421000   -0.0502000
+43755.00   0.176908   0.426457  -0.0518383    0.188000    0.419000   -0.0527000
+43756.00   0.179473   0.424449  -0.0543860    0.191000    0.417000   -0.0552000
+43757.00   0.181941   0.422390  -0.0569425    0.193000    0.414000   -0.0577000
+43758.00   0.184304   0.420286  -0.0594734    0.196000    0.412000   -0.0602000
+43759.00   0.186550   0.418143  -0.0619536    0.199000    0.410000   -0.0627000
+43760.00   0.188684   0.415963  -0.0643739    0.202000    0.408000   -0.0652000
+43761.00   0.190718   0.413744  -0.0667459    0.205000    0.406000   -0.0677000
+43762.00   0.192669   0.411482  -0.0691035    0.207000    0.403000   -0.0703000
+43763.00   0.194550   0.409169  -0.0714977    0.210000    0.401000   -0.0728000
+43764.00   0.196378   0.406797  -0.0739843    0.213000    0.399000   -0.0753000
+43765.00   0.198163   0.404359  -0.0766076    0.216000    0.397000   -0.0779000
+43766.00   0.199903   0.401853  -0.0793833    0.218000    0.394000   -0.0805000
+43767.00   0.201596   0.399274  -0.0822877    0.221000    0.392000   -0.0831000
+43768.00   0.203239   0.396620  -0.0852623    0.223000    0.389000   -0.0857000
+43769.00   0.204826   0.393892  -0.0882315    0.226000    0.387000   -0.0883000
+43770.00   0.206351   0.391093  -0.0911272    0.228000    0.384000   -0.0910000
+43771.00   0.207809   0.388224  -0.0939069    0.230000    0.382000   -0.0937000
+43772.00   0.209195   0.385287  -0.0965604    0.233000    0.379000   -0.0964000
+43773.00   0.210502   0.382285  -0.0991042    0.235000    0.377000   -0.0991000
+43774.00   0.211732   0.379220  -0.1015708    0.237000    0.374000   -0.1018000
+43775.00   0.212889   0.376092  -0.1040003    0.239000    0.371000   -0.1046000
+43776.00   0.213980   0.372904  -0.1064361    0.241000    0.369000   -0.1074000
+43777.00   0.215010   0.369658  -0.1089203    0.243000    0.366000   -0.1101000
+43778.00   0.215989   0.366356  -0.1114893    0.245000    0.364000   -0.1129000
+43779.00   0.216932   0.363006  -0.1141692    0.247000    0.361000   -0.1157000
+43780.00   0.217853   0.359612  -0.1169746    0.249000    0.358000   -0.1186000
+43781.00   0.218767   0.356179  -0.1199073    0.250000    0.356000   -0.1214000
+43782.00   0.219684   0.352717  -0.1229554    0.252000    0.353000   -0.1243000
+43783.00   0.220609   0.349234  -0.1260928    0.253000    0.351000   -0.1271000
+43784.00   0.221550   0.345743  -0.1292806    0.255000    0.348000   -0.1300000
+43785.00   0.222509   0.342253  -0.1324730    0.256000    0.345000   -0.1329000
+43786.00   0.223491   0.338774  -0.1356278    0.257000    0.342000   -0.1359000
+43787.00   0.224485   0.335309  -0.1387186    0.259000    0.340000   -0.1388000
+43788.00   0.225478   0.331858  -0.1417440    0.260000    0.337000   -0.1418000
+43789.00   0.226457   0.328422  -0.1447283    0.261000    0.334000   -0.1447000
+43790.00   0.227409   0.325001  -0.1477144    0.262000    0.331000   -0.1477000
+43791.00   0.228323   0.321596  -0.1507530    0.263000    0.328000   -0.1507000
+43792.00   0.229191   0.318206  -0.1538881    0.263000    0.324000   -0.1536000
+43793.00   0.230008   0.314828  -0.1571436    0.264000    0.321000   -0.1566000
+43794.00   0.230768   0.311463  -0.1605123    0.265000    0.318000   -0.1596000
+43795.00   0.231468   0.308107  -0.1639545    0.265000    0.315000   -0.1626000
+43796.00   0.232115   0.304757  -0.1674075    0.266000    0.311000   -0.1656000
+43797.00   0.232718   0.301409  -0.1708012    0.266000    0.308000   -0.1687000
+43798.00   0.233283   0.298057  -0.1740780    0.267000    0.304000   -0.1717000
+43799.00   0.233814   0.294691  -0.1772045    0.267000    0.301000   -0.1747000
+43800.00   0.234310   0.291299  -0.1801762    0.267000    0.297000   -0.1778000
+43801.00   0.234773   0.287874  -0.1830117    0.267000    0.294000   -0.1809000
+43802.00   0.235200   0.284417  -0.1857462    0.268000    0.290000   -0.1839000
+43803.00   0.235586   0.280934  -0.1884211    0.268000    0.287000   -0.1870000
+43804.00   0.235928   0.277428  -0.1910785    0.268000    0.283000   -0.1901000
+43805.00   0.236222   0.273904  -0.1937577    0.268000    0.279000   -0.1932000
+43806.00   0.236463   0.270369  -0.1964924    0.268000    0.276000   -0.1963000
+43807.00   0.236650   0.266835  -0.1993084    0.267000    0.272000   -0.1994000
+43808.00   0.236778   0.263312  -0.2022192    0.267000    0.269000   -0.2025000
+43809.00   0.236845   0.259814  -0.2052260    0.267000    0.265000   -0.2056000
+43810.00   0.236847   0.256349  -0.2083172    0.267000    0.261000   -0.2087000
+43811.00   0.236778   0.252925  -0.2114663    0.266000    0.258000   -0.2118000
+43812.00   0.236632   0.249544  -0.2146351    0.266000    0.254000   -0.2149000
+43813.00   0.236404   0.246213  -0.2177810    0.265000    0.251000   -0.2180000
+43814.00   0.236086   0.242932  -0.2208692    0.265000    0.247000   -0.2211000
+43815.00   0.235680   0.239702  -0.2238856    0.264000    0.243000   -0.2243000
+43816.00   0.235182   0.236519  -0.2268452    0.264000    0.240000   -0.2274000
+43817.00   0.234594   0.233379  -0.2297885    0.263000    0.236000   -0.2306000
+43818.00   0.233914   0.230281  -0.2327680    0.263000    0.233000   -0.2337000
+43819.00   0.233142   0.227222  -0.2358311    0.262000    0.229000   -0.2369000
+43820.00   0.232276   0.224198  -0.2390052    0.261000    0.225000   -0.2401000
+43821.00   0.231317   0.221208  -0.2422912    0.260000    0.222000   -0.2432000
+43822.00   0.230263   0.218247  -0.2456624    0.259000    0.218000   -0.2464000
+43823.00   0.229114   0.215313  -0.2490712    0.258000    0.215000   -0.2495000
+43824.00   0.227870   0.212404  -0.2524596    0.257000    0.211000   -0.2527000
+43825.00   0.226529   0.209516  -0.2557732    0.256000    0.207000   -0.2559000
+43826.00   0.225095   0.206646  -0.2589735    0.255000    0.204000   -0.2590000
+43827.00   0.223575   0.203788  -0.2620454    0.253000    0.200000   -0.2622000
+43828.00   0.221976   0.200937  -0.2649981    0.252000    0.197000   -0.2653000
+43829.00   0.220303   0.198088  -0.2678583    0.251000    0.193000   -0.2685000
+43830.00   0.218564   0.195236  -0.2706623    0.249000    0.190000   -0.2716000
+43831.00   0.216764   0.192375  -0.2734491    0.248000    0.186000   -0.2748000
+43832.00   0.214912   0.189501  -0.2762562    0.246000    0.183000   -0.2779000
+43833.00   0.213012   0.186609  -0.2791156    0.245000    0.179000   -0.2811000
+43834.00   0.211071   0.183701  -0.2820508    0.243000    0.176000   -0.2842000
+43835.00   0.209093   0.180776  -0.2850740    0.241000    0.173000   -0.2873000
+43836.00   0.207082   0.177839  -0.2881851    0.239000    0.169000   -0.2904000
+43837.00   0.205044   0.174892  -0.2913729    0.238000    0.166000   -0.2936000
+43838.00   0.202984   0.171935  -0.2946162    0.236000    0.162000   -0.2967000
+43839.00   0.200905   0.168972  -0.2978839    0.234000    0.159000   -0.2998000
+43840.00   0.198815   0.166005  -0.3011377    0.232000    0.156000   -0.3029000
+43841.00   0.196725   0.163037  -0.3043399    0.230000    0.153000   -0.3060000
+43842.00   0.194646   0.160071  -0.3074654    0.228000    0.149000   -0.3091000
+43843.00   0.192590   0.157112  -0.3105135    0.226000    0.146000   -0.3122000
+43844.00   0.190570   0.154161  -0.3135135    0.224000    0.143000   -0.3153000
+43845.00   0.188596   0.151222  -0.3165158    0.222000    0.140000   -0.3184000
+43846.00   0.186681   0.148300  -0.3195721    0.220000    0.137000   -0.3214000
+43847.00   0.184837   0.145396  -0.3227160    0.217000    0.134000   -0.3245000
+43848.00   0.183075   0.142515  -0.3259501    0.215000    0.131000   -0.3275000
+43849.00   0.181394   0.139662  -0.3292471    0.213000    0.128000   -0.3306000
+43850.00   0.179786   0.136842  -0.3325594    0.211000    0.125000   -0.3336000
+43851.00   0.178245   0.134062  -0.3358331    0.209000    0.122000   -0.3366000
+43852.00   0.176763   0.131326  -0.3390196    0.206000    0.120000   -0.3397000
+43853.00   0.175333   0.128642  -0.3420848    0.204000    0.117000   -0.3427000
+43854.00   0.173948   0.126014  -0.3450138    0.202000    0.114000   -0.3457000
+43855.00   0.172598   0.123448  -0.3478125    0.200000    0.112000   -0.3487000
+43856.00   0.171269   0.120945  -0.3505033    0.198000    0.109000   -0.3517000
+43857.00   0.169949   0.118509  -0.3531192    0.195000    0.107000   -0.3547000
+43858.00   0.168623   0.116141  -0.3556976    0.193000    0.104000   -0.3577000
+43859.00   0.167280   0.113846  -0.3582751    0.191000    0.102000   -0.3607000
+43860.00   0.165906   0.111626  -0.3608843    0.189000    0.100000   -0.3637000
+43861.00   0.164489   0.109482  -0.3635510    0.187000    0.098000   -0.3667000
+43862.00   0.163023   0.107410  -0.3662935    0.184000    0.096000   -0.3696000
+43863.00   0.161506   0.105405  -0.3691193    0.182000    0.094000   -0.3726000
+43864.00   0.159933   0.103460  -0.3720247    0.180000    0.092000   -0.3756000
+43865.00   0.158303   0.101569  -0.3749969    0.178000    0.090000   -0.3786000
+43866.00   0.156610   0.099725  -0.3780160    0.176000    0.088000   -0.3815000
+43867.00   0.154854   0.097924  -0.3810565    0.173000    0.087000   -0.3845000
+43868.00   0.153029   0.096158  -0.3840898    0.171000    0.085000   -0.3874000
+43869.00   0.151133   0.094422  -0.3870918    0.169000    0.083000   -0.3904000
+43870.00   0.149163   0.092709  -0.3900537    0.167000    0.082000   -0.3933000
+43871.00   0.147115   0.091014  -0.3929930    0.164000    0.080000   -0.3962000
+43872.00   0.144987   0.089332  -0.3959529    0.162000    0.079000   -0.3992000
+43873.00   0.142776   0.087666  -0.3989868    0.159000    0.077000   -0.4021000
+43874.00   0.140480   0.086025   0.5978633    0.157000    0.076000    0.5950000
+43875.00   0.138099   0.084414   0.5945863    0.155000    0.075000    0.5921000
+43876.00   0.135629   0.082840   0.5912098    0.152000    0.074000    0.5892000
+43877.00   0.133069   0.081309   0.5877910    0.150000    0.072000    0.5863000
+43878.00   0.130417   0.079829   0.5843976    0.147000    0.071000    0.5834000
+43879.00   0.127672   0.078406   0.5810892    0.145000    0.070000    0.5805000
+43880.00   0.124831   0.077047   0.5779048    0.143000    0.069000    0.5776000
+43881.00   0.121894   0.075758   0.5748607    0.140000    0.068000    0.5747000
+43882.00   0.118858   0.074545   0.5719519    0.138000    0.067000    0.5719000
+43883.00   0.115732   0.073408   0.5691563    0.135000    0.066000    0.5690000
+43884.00   0.112525   0.072343   0.5664409    0.133000    0.065000    0.5661000
+43885.00   0.109250   0.071345   0.5637676    0.131000    0.064000    0.5632000
+43886.00   0.105917   0.070411   0.5610988    0.128000    0.063000    0.5604000
+43887.00   0.102538   0.069537   0.5584014    0.126000    0.063000    0.5575000
+43888.00   0.099123   0.068719   0.5556491    0.123000    0.062000    0.5547000
+43889.00   0.095684   0.067954   0.5528245    0.121000    0.061000    0.5518000
+43890.00   0.092231   0.067237   0.5499209    0.118000    0.060000    0.5489000
+43891.00   0.088776   0.066565   0.5469430    0.116000    0.060000    0.5461000
+43892.00   0.085330   0.065933   0.5439059    0.113000    0.059000    0.5432000
+43893.00   0.081903   0.065338   0.5408321    0.111000    0.059000    0.5404000
+43894.00   0.078507   0.064776   0.5377474    0.108000    0.058000    0.5375000
+43895.00   0.075153   0.064243   0.5346777    0.105000    0.057000    0.5346000
+43896.00   0.071853   0.063735   0.5316455    0.102000    0.057000    0.5318000
+43897.00   0.068616   0.063248   0.5286629    0.100000    0.056000    0.5289000
+43898.00   0.065446   0.062784   0.5257234    0.097000    0.056000    0.5261000
+43899.00   0.062342   0.062344   0.5227957    0.094000    0.055000    0.5232000
+43900.00   0.059301   0.061935   0.5198283    0.091000    0.055000    0.5203000
+43901.00   0.056322   0.061559   0.5167664    0.088000    0.054000    0.5175000
+43902.00   0.053402   0.061220   0.5135772    0.085000    0.054000    0.5146000
+43903.00   0.050539   0.060923   0.5102684    0.082000    0.053000    0.5118000
+43904.00   0.047732   0.060670   0.5068909    0.079000    0.053000    0.5089000
+43905.00   0.044977   0.060467   0.5035221    0.076000    0.053000    0.5060000
+43906.00   0.042272   0.060316   0.5002411    0.073000    0.053000    0.5031000
+43907.00   0.039616   0.060220   0.4971054    0.070000    0.052000    0.5003000
+43908.00   0.037003   0.060177   0.4941406    0.067000    0.052000    0.4974000
+43909.00   0.034431   0.060183   0.4913431    0.064000    0.052000    0.4945000
+43910.00   0.031894   0.060235   0.4886884    0.061000    0.052000    0.4916000
+43911.00   0.029391   0.060324   0.4861395    0.058000    0.053000    0.4887000
+43912.00   0.026917   0.060443   0.4836543    0.054000    0.053000    0.4859000
+43913.00   0.024464   0.060590   0.4811906    0.051000    0.054000    0.4830000
+43914.00   0.022021   0.060766   0.4787103    0.048000    0.054000    0.4801000
+43915.00   0.019579   0.060969   0.4761830    0.044000    0.055000    0.4772000
+43916.00   0.017129   0.061199   0.4735876    0.041000    0.055000    0.4743000
+43917.00   0.014664   0.061456   0.4709136    0.037000    0.056000    0.4714000
+43918.00   0.012186   0.061739   0.4681619    0.034000    0.056000    0.4685000
+43919.00   0.009695   0.062047   0.4653447    0.030000    0.057000    0.4656000
+43920.00   0.007190   0.062380   0.4624842    0.026000    0.058000    0.4627000
+43921.00   0.004672   0.062736   0.4596078    0.023000    0.058000    0.4598000
+43922.00   0.002142   0.063115   0.4567426    0.019000    0.059000    0.4569000
+43923.00  -0.000401   0.063517   0.4539100    0.016000    0.059000    0.4540000
+43924.00  -0.002956   0.063940   0.4511200    0.012000    0.060000    0.4511000
+43925.00  -0.005524   0.064384   0.4483673    0.008000    0.061000    0.4482000
+43926.00  -0.008108   0.064848   0.4456269    0.005000    0.062000    0.4453000
+43927.00  -0.010711   0.065332   0.4428528    0.001000    0.062000    0.4423000
+43928.00  -0.013336   0.065834   0.4399875   -0.002000    0.063000    0.4394000
+43929.00  -0.015987   0.066356   0.4369807   -0.006000    0.064000    0.4365000
+43930.00  -0.018666   0.066895   0.4338127   -0.010000    0.065000    0.4336000
+43931.00  -0.021378   0.067451   0.4305092   -0.014000    0.066000    0.4306000
+43932.00  -0.024122   0.068028   0.4271378   -0.017000    0.068000    0.4277000
+43933.00  -0.026899   0.068637   0.4237864   -0.021000    0.069000    0.4247000
+43934.00  -0.029706   0.069293   0.4205356   -0.025000    0.070000    0.4218000
+43935.00  -0.032543   0.070009   0.4174369   -0.029000    0.072000    0.4189000
+43936.00  -0.035406   0.070797   0.4145046   -0.032000    0.073000    0.4159000
+43937.00  -0.038296   0.071672   0.4117220   -0.036000    0.075000    0.4130000
+43938.00  -0.041208   0.072647   0.4090524   -0.039000    0.076000    0.4100000
+43939.00  -0.044139   0.073730   0.4064513   -0.043000    0.078000    0.4071000
+43940.00  -0.047081   0.074925   0.4038728   -0.046000    0.080000    0.4041000
+43941.00  -0.050025   0.076239   0.4012742   -0.050000    0.082000    0.4012000
+43942.00  -0.052969   0.077670   0.3986191   -0.053000    0.083000    0.3982000
+43943.00  -0.055911   0.079210   0.3958803   -0.057000    0.085000    0.3953000
+43944.00  -0.058854   0.080847   0.3930421   -0.060000    0.087000    0.3923000
+43945.00  -0.061800   0.082570   0.3901010   -0.063000    0.089000    0.3893000
+43946.00  -0.064750   0.084368   0.3870650   -0.066000    0.091000    0.3863000
+43947.00  -0.067704   0.086231   0.3839535   -0.070000    0.093000    0.3834000
+43948.00  -0.070664   0.088147   0.3807956   -0.073000    0.095000    0.3804000
+43949.00  -0.073618   0.090106   0.3776271   -0.076000    0.097000    0.3774000
+43950.00  -0.076549   0.092098   0.3744811   -0.079000    0.099000    0.3744000
+43951.00  -0.079444   0.094114   0.3713788   -0.082000    0.101000    0.3714000
+43952.00  -0.082286   0.096143   0.3683230   -0.084000    0.104000    0.3683000
+43953.00  -0.085059   0.098177   0.3652961   -0.087000    0.106000    0.3653000
+43954.00  -0.087749   0.100204   0.3622606   -0.090000    0.108000    0.3623000
+43955.00  -0.090350   0.102221   0.3591650   -0.093000    0.110000    0.3592000
+43956.00  -0.092865   0.104229   0.3559556   -0.096000    0.113000    0.3562000
+43957.00  -0.095296   0.106231   0.3525963   -0.098000    0.115000    0.3531000
+43958.00  -0.097642   0.108233   0.3490860   -0.101000    0.118000    0.3501000
+43959.00  -0.099898   0.110239   0.3454675   -0.104000    0.120000    0.3470000
+43960.00  -0.102064   0.112255   0.3418196   -0.106000    0.123000    0.3439000
+43961.00  -0.104136   0.114286   0.3382344   -0.109000    0.125000    0.3409000
+43962.00  -0.106120   0.116336   0.3347871   -0.111000    0.128000    0.3378000
+43963.00  -0.108024   0.118409   0.3315186   -0.114000    0.130000    0.3348000
+43964.00  -0.109856   0.120506   0.3284323   -0.116000    0.133000    0.3317000
+43965.00  -0.111624   0.122632   0.3255016   -0.118000    0.136000    0.3286000
+43966.00  -0.113335   0.124790   0.3226839   -0.120000    0.139000    0.3255000
+43967.00  -0.114998   0.126982   0.3199310   -0.123000    0.141000    0.3223000
+43968.00  -0.116619   0.129213   0.3171962   -0.125000    0.144000    0.3192000
+43969.00  -0.118202   0.131482   0.3144374   -0.127000    0.147000    0.3161000
+43970.00  -0.119746   0.133788   0.3116208   -0.129000    0.150000    0.3130000
+43971.00  -0.121251   0.136129   0.3087243   -0.131000    0.153000    0.3099000
+43972.00  -0.122714   0.138504   0.3057383   -0.133000    0.155000    0.3068000
+43973.00  -0.124135   0.140910   0.3026656   -0.135000    0.158000    0.3037000
+43974.00  -0.125512   0.143345   0.2995202   -0.137000    0.161000    0.3006000
+43975.00  -0.126849   0.145809   0.2963268   -0.139000    0.164000    0.2975000
+43976.00  -0.128150   0.148298   0.2931183   -0.141000    0.166000    0.2943000
+43977.00  -0.129419   0.150811   0.2899290   -0.142000    0.169000    0.2912000
+43978.00  -0.130661   0.153347   0.2867850   -0.144000    0.171000    0.2880000
+43979.00  -0.131882   0.155903   0.2836958   -0.146000    0.174000    0.2849000
+43980.00  -0.133085   0.158477   0.2806490   -0.148000    0.177000    0.2818000
+43981.00  -0.134274   0.161067   0.2776110   -0.149000    0.179000    0.2787000
+43982.00  -0.135449   0.163669   0.2745349   -0.151000    0.182000    0.2755000
+43983.00  -0.136606   0.166279   0.2713707   -0.152000    0.184000    0.2724000
+43984.00  -0.137741   0.168891   0.2680793   -0.154000    0.187000    0.2693000
+43985.00  -0.138853   0.171502   0.2646455   -0.155000    0.190000    0.2662000
+43986.00  -0.139941   0.174110   0.2610886   -0.157000    0.192000    0.2631000
+43987.00  -0.141004   0.176715   0.2574631   -0.158000    0.195000    0.2601000
+43988.00  -0.142046   0.179318   0.2538475   -0.160000    0.197000    0.2570000
+43989.00  -0.143074   0.181927   0.2503237   -0.161000    0.200000    0.2539000
+43990.00  -0.144092   0.184548   0.2469541   -0.162000    0.203000    0.2506000
+43991.00  -0.145100   0.187182   0.2437655   -0.163000    0.205000    0.2473000
+43992.00  -0.146089   0.189825   0.2407493   -0.164000    0.208000    0.2440000
+43993.00  -0.147054   0.192477   0.2378725   -0.165000    0.210000    0.2407000
+43994.00  -0.147988   0.195134   0.2350903   -0.142000    0.213000    0.2374000
+43995.00  -0.148886   0.197796   0.2323562   -0.143000    0.215000    0.2344000
+43996.00  -0.149745   0.200461   0.2296276   -0.143000    0.218000    0.2314000
+43997.00  -0.150564   0.203128   0.2268695   -0.144000    0.220000    0.2283000
+43998.00  -0.151339   0.205796   0.2240570   -0.144000    0.223000    0.2253000
+43999.00  -0.152070   0.208464   0.2211782   -0.145000    0.225000    0.2223000
+44000.00  -0.152753   0.211131   0.2182336   -0.145000    0.227000    0.2194000
+44001.00  -0.153387   0.213797   0.2152346   -0.146000    0.230000    0.2164000
+44002.00  -0.153973   0.216460   0.2122023   -0.146000    0.232000    0.2135000
+44003.00  -0.154510   0.219120   0.2091655   -0.147000    0.235000    0.2105000
+44004.00  -0.154999   0.221774   0.2061571   -0.147000    0.237000    0.2076000
+44005.00  -0.155442   0.224422   0.2032085   -0.147000    0.239000    0.2047000
+44006.00  -0.155839   0.227063   0.2003390   -0.148000    0.242000    0.2019000
+44007.00  -0.156190   0.229696   0.1975461   -0.148000    0.244000    0.1990000
+44008.00  -0.156498   0.232317   0.1948025   -0.149000    0.247000    0.1962000
+44009.00  -0.156760   0.234922   0.1920627   -0.149000    0.249000    0.1933000
+44010.00  -0.156979   0.237507   0.1892751   -0.149000    0.251000    0.1905000
+44011.00  -0.157154   0.240066   0.1863978   -0.149000    0.253000    0.1878000
+44012.00  -0.157284   0.242593   0.1834122   -0.149000    0.256000    0.1850000
+44013.00  -0.157372   0.245080   0.1803290   -0.149000    0.258000    0.1823000
+44014.00  -0.157415   0.247523   0.1771873   -0.149000    0.260000    0.1795000
+44015.00  -0.157410   0.249924   0.1740488   -0.149000    0.262000    0.1768000
+44016.00  -0.157353   0.252291   0.1709837   -0.149000    0.264000    0.1742000
+44017.00  -0.157240   0.254629   0.1680538   -0.149000    0.267000    0.1715000
+44018.00  -0.157071   0.256943   0.1652975   -0.149000    0.269000    0.1689000
+44019.00  -0.156842   0.259234   0.1627230   -0.149000    0.271000    0.1662000
+44020.00  -0.156555   0.261507   0.1603115   -0.149000    0.273000    0.1637000
+44021.00  -0.156206   0.263765   0.1580270   -0.149000    0.275000    0.1611000
+44022.00  -0.155797   0.266011   0.1558269   -0.148000    0.278000    0.1586000
+44023.00  -0.155330   0.268245   0.1536684   -0.148000    0.280000    0.1560000
+44024.00  -0.154810   0.270470   0.1515140   -0.148000    0.282000    0.1535000
+44025.00  -0.154242   0.272685   0.1493351   -0.147000    0.284000    0.1511000
+44026.00  -0.153628   0.274892   0.1471131   -0.147000    0.286000    0.1486000
+44027.00  -0.152969   0.277094   0.1448408   -0.146000    0.289000    0.1462000
+44028.00  -0.152263   0.279291   0.1425226   -0.146000    0.291000    0.1437000
+44029.00  -0.151511   0.281486   0.1401719   -0.145000    0.293000    0.1413000
+44030.00  -0.150712   0.283680   0.1378086   -0.144000    0.295000    0.1389000
+44031.00  -0.149866   0.285876   0.1354564   -0.143000    0.298000    0.1365000
+44032.00  -0.148972   0.288076   0.1331408   -0.142000    0.300000    0.1341000
+44033.00  -0.148031   0.290280   0.1308829   -0.141000    0.303000    0.1317000
+44034.00  -0.147047   0.292494   0.1286901   -0.140000    0.305000    0.1293000
+44035.00  -0.146027   0.294720   0.1265461   -0.139000    0.307000    0.1270000
+44036.00  -0.144979   0.296961   0.1244090   -0.138000    0.310000    0.1246000
+44037.00  -0.143907   0.299222   0.1222235   -0.137000    0.312000    0.1223000
+44038.00  -0.142819   0.301505   0.1199394   -0.136000    0.315000    0.1199000
+44039.00  -0.141721   0.303814   0.1175309   -0.135000    0.317000    0.1176000
+44040.00  -0.140619   0.306150   0.1150055   -0.134000    0.319000    0.1154000
+44041.00  -0.139515   0.308510   0.1124028   -0.133000    0.321000    0.1131000
+44042.00  -0.138413   0.310891   0.1097815   -0.132000    0.324000    0.1109000
+44043.00  -0.137315   0.313290   0.1072050   -0.131000    0.326000    0.1086000
+44044.00  -0.136226   0.315703   0.1047273   -0.130000    0.328000    0.1064000
+44045.00  -0.135149   0.318127   0.1023839   -0.129000    0.330000    0.1042000
+44046.00  -0.134082   0.320558   0.1001851   -0.128000    0.332000    0.1020000
+44047.00  -0.133023   0.322987   0.0981170   -0.126000    0.335000    0.0997000
+44048.00  -0.131967   0.325406   0.0961475   -0.125000    0.337000    0.0975000
+44049.00  -0.130909   0.327807   0.0942354   -0.124000    0.339000    0.0953000
+44050.00  -0.129846   0.330183   0.0923394   -0.123000    0.341000    0.0932000
+44051.00  -0.128778   0.332535   0.0904232   -0.122000    0.343000    0.0910000
+44052.00  -0.127706   0.334862   0.0884588   -0.120000    0.345000    0.0889000
+44053.00  -0.126628   0.337163   0.0864284   -0.119000    0.347000    0.0867000
+44054.00  -0.125542   0.339439   0.0843264   -0.118000    0.349000    0.0846000
+44055.00  -0.124442   0.341688   0.0821596   -0.117000    0.351000    0.0825000
+44056.00  -0.123326   0.343909   0.0799453   -0.116000    0.353000    0.0805000
+44057.00  -0.122188   0.346100   0.0777069   -0.114000    0.355000    0.0784000
+44058.00  -0.121023   0.348261   0.0754707   -0.113000    0.357000    0.0764000
+44059.00  -0.119823   0.350392   0.0732628   -0.112000    0.359000    0.0743000
+44060.00  -0.118580   0.352492   0.0711070   -0.111000    0.361000    0.0723000
+44061.00  -0.117286   0.354562   0.0690175   -0.109000    0.363000    0.0703000
+44062.00  -0.115933   0.356601   0.0669908   -0.108000    0.364000    0.0684000
+44063.00  -0.114514   0.358609   0.0649984   -0.106000    0.366000    0.0664000
+44064.00  -0.113023   0.360584   0.0629905   -0.105000    0.368000    0.0644000
+44065.00  -0.111458   0.362525   0.0609115   -0.103000    0.370000    0.0624000
+44066.00  -0.109817   0.364427   0.0587243   -0.101000    0.371000    0.0605000
+44067.00  -0.108098   0.366288   0.0564281   -0.100000    0.373000    0.0585000
+44068.00  -0.106300   0.368105   0.0540628   -0.098000    0.374000    0.0566000
+44069.00  -0.104419   0.369877   0.0516943   -0.096000    0.376000    0.0546000
+44070.00  -0.102454   0.371605   0.0493954   -0.094000    0.377000    0.0527000
+44071.00  -0.100404   0.373287   0.0472259   -0.092000    0.379000    0.0507000
+44072.00  -0.098267   0.374923   0.0452206   -0.090000    0.380000    0.0488000
+44073.00  -0.096040   0.376514   0.0433866   -0.088000    0.382000    0.0468000
+44074.00  -0.093725   0.378060   0.0417069   -0.086000    0.383000    0.0449000
+44075.00  -0.091327   0.379565   0.0401465   -0.084000    0.384000    0.0430000
+44076.00  -0.088856   0.381037   0.0386609   -0.081000    0.386000    0.0410000
+44077.00  -0.086321   0.382482   0.0372045   -0.079000    0.387000    0.0391000
+44078.00  -0.083733   0.383905   0.0357367   -0.075990    0.389000    0.0371000
+44079.00  -0.081101   0.385315   0.0342253   -0.074000    0.390000    0.0352000
+44080.00  -0.078433   0.386717   0.0326479   -0.071000    0.391000    0.0333000
+44081.00  -0.075739   0.388117   0.0309929   -0.069000    0.392000    0.0313990
+44082.00  -0.073026   0.389522   0.0292595   -0.066000    0.394000    0.0294000
+44083.00  -0.070302   0.390939   0.0274579   -0.064000    0.395000    0.0275000
+44084.00  -0.067573   0.392373   0.0256065   -0.061000    0.396000    0.0256000
+44085.00  -0.064847   0.393830   0.0237269   -0.058000    0.397000    0.0236000
+44086.00  -0.062126   0.395308   0.0218404   -0.055000    0.398000    0.0216000
+44087.00  -0.059411   0.396802   0.0199639   -0.053000    0.400000    0.0197000
+44088.00  -0.056703   0.398306   0.0181066   -0.050000    0.401000    0.0177000
+44089.00  -0.054003   0.399814   0.0162656   -0.047000    0.402000    0.0156990
+44090.00  -0.051312   0.401319   0.0144201   -0.044000    0.403000    0.0137000
+44091.00  -0.048629   0.402812   0.0125271   -0.041000    0.404000    0.0117000
+44092.00  -0.045956   0.404284   0.0105286   -0.039000    0.405000    0.0096000
+44093.00  -0.043291   0.405727   0.0083720   -0.035990    0.406000    0.0076000
+44094.00  -0.040636   0.407132   0.0060328   -0.033000    0.407000    0.0056000
+44095.00  -0.037989   0.408492   0.0035321   -0.030000    0.408000    0.0035000
+44096.00  -0.035345   0.409799   0.0009371   -0.027000    0.409000    0.0015000
+44097.00  -0.032689   0.411047  -0.0016589   -0.025000    0.409000   -0.0006000
+44098.00  -0.030008   0.412228  -0.0041672   -0.022000    0.410000   -0.0026000
+44099.00  -0.027287   0.413337  -0.0065267   -0.018990    0.411000   -0.0047000
+44100.00  -0.024514   0.414367  -0.0087132   -0.016000    0.412000   -0.0068000
+44101.00  -0.021675   0.415312  -0.0107356   -0.013000    0.413000   -0.0089000
+44102.00  -0.018775   0.416172  -0.0126275   -0.011000    0.413000   -0.0110000
+44103.00  -0.015826   0.416953  -0.0144346   -0.008000    0.414000   -0.0131000
+44104.00  -0.012839   0.417659  -0.0162036   -0.005000    0.415000   -0.0152000
+44105.00  -0.009828   0.418293  -0.0179767   -0.002000    0.416000   -0.0173000
+44106.00  -0.006804   0.418860  -0.0197865    0.000000    0.416000   -0.0194000
+44107.00  -0.003779   0.419365  -0.0216552    0.003000    0.417000   -0.0215000
+44108.00  -0.000767   0.419812  -0.0235931    0.005000    0.417000   -0.0236000
+44109.00   0.002222   0.420205  -0.0255994    0.008000    0.418000   -0.0257000
+44110.00   0.005175   0.420548  -0.0276627    0.011000    0.418000   -0.0278000
+44111.00   0.008079   0.420847  -0.0297624    0.013000    0.419000   -0.0300000
+44112.00   0.010923   0.421105  -0.0318720    0.016000    0.419000   -0.0321000
+44113.00   0.013703   0.421323  -0.0339667    0.017990    0.420000   -0.0343000
+44114.00   0.016420   0.421501  -0.0360279    0.021000    0.420000   -0.0364000
+44115.00   0.019077   0.421639  -0.0380466    0.024000    0.420000   -0.0386000
+44116.00   0.021674   0.421735  -0.0400268    0.026000    0.420000   -0.0407000
+44117.00   0.024215   0.421790  -0.0419887    0.029000    0.420000   -0.0429000
+44118.00   0.026700   0.421802  -0.0439715    0.031000    0.420000   -0.0450000
+44119.00   0.029132   0.421771  -0.0460322    0.034000    0.420000   -0.0472000
+44120.00   0.031511   0.421697  -0.0482331    0.037000    0.420000   -0.0494000
+44121.00   0.033842   0.421579  -0.0506223    0.039000    0.420000   -0.0516000
+44122.00   0.036124   0.421415  -0.0532098    0.042000    0.419000   -0.0539000
+44123.00   0.038362   0.421208  -0.0559546    0.044000    0.419000   -0.0561000
+44124.00   0.040559   0.420956  -0.0587727    0.047000    0.419000   -0.0583000
+44125.00   0.042720   0.420659  -0.0615625    0.049000    0.419000   -0.0606000
+44126.00   0.044847   0.420319  -0.0642366    0.052000    0.419000   -0.0627990
+44127.00   0.046946   0.419936  -0.0667455    0.054000    0.418000   -0.0651000
+44128.00   0.049019   0.419508  -0.0690829    0.057000    0.418000   -0.0673000
+44129.00   0.051074   0.419032  -0.0712768    0.059000    0.418000   -0.0696000
+44130.00   0.053117   0.418502  -0.0733744    0.061000    0.418000   -0.0720000
+44131.00   0.055153   0.417912  -0.0754270    0.063000    0.418000   -0.0743000
+44132.00   0.057180   0.417258  -0.0774808    0.066000    0.417000   -0.0767000
+44133.00   0.059199   0.416536  -0.0795726    0.068000    0.417000   -0.0790000
+44134.00   0.061206   0.415743  -0.0817274    0.070000    0.417000   -0.0814000
+44135.00   0.063200   0.414878  -0.0839581    0.071990    0.416000   -0.0838000
+44136.00   0.065176   0.413944  -0.0862669    0.074000    0.416000   -0.0863000
+44137.00   0.067126   0.412949  -0.0886452    0.075990    0.415000   -0.0887000
+44138.00   0.069047   0.411900  -0.0910757    0.078000    0.415000   -0.0912000
+44139.00   0.070931   0.410806  -0.0935341    0.080000    0.414000   -0.0936000
+44140.00   0.072773   0.409675  -0.0959929    0.082000    0.413000   -0.0961000
+44141.00   0.074567   0.408515  -0.0984269    0.083000    0.412000   -0.0987000
+44142.00   0.076307   0.407335  -0.1008215    0.085000    0.412000   -0.1012000
+44143.00   0.077989   0.406142  -0.1031763    0.086000    0.411000   -0.1038000
+44144.00   0.079608   0.404944  -0.1055084    0.088000    0.410000   -0.1063000
+44145.00   0.081169   0.403754  -0.1078519    0.089000    0.409000   -0.1089000
+44146.00   0.082675   0.402580  -0.1102558    0.091000    0.408000   -0.1116000
+44147.00   0.084132   0.401428  -0.1127779    0.092000    0.408000   -0.1142000
+44148.00   0.085546   0.400303  -0.1154722    0.094000    0.407000   -0.1169000
+44149.00   0.086922   0.399210  -0.1183698    0.095000    0.406000   -0.1195000
+44150.00   0.088261   0.398147  -0.1214611    0.096000    0.405000   -0.1222000
+44151.00   0.089562   0.397110  -0.1246893    0.097000    0.404000   -0.1249000
+44152.00   0.090826   0.396094  -0.1279624    0.098000    0.404000   -0.1275000
+44153.00   0.092056   0.395093  -0.1311808    0.099000    0.403000   -0.1302000
+44154.00   0.093254   0.394100  -0.1342675    0.100000    0.402000   -0.1329000
+44155.00   0.094421   0.393111  -0.1371874    0.101000    0.401000   -0.1356000
+44156.00   0.095564   0.392119  -0.1399479    0.102000    0.400000   -0.1383000
+44157.00   0.096691   0.391120  -0.1425862    0.103000    0.400000   -0.1409000
+44158.00   0.097813   0.390111  -0.1451529    0.104000    0.399000   -0.1436000
+44159.00   0.098939   0.389088  -0.1476981    0.105000    0.398000   -0.1463000
+44160.00   0.100079   0.388046  -0.1502641    0.106000    0.397000   -0.1490000
+44161.00   0.101241   0.386981  -0.1528811    0.107000    0.396000   -0.1517000
+44162.00   0.102432   0.385888  -0.1555665    0.108000    0.395000   -0.1545000
+44163.00   0.103656   0.384759  -0.1583234    0.109000    0.394000   -0.1572000
+44164.00   0.104919   0.383589  -0.1611426    0.110000    0.393000   -0.1599000
+44165.00   0.106230   0.382375  -0.1640036    0.111000    0.392000   -0.1626000
+44166.00   0.107594   0.381114  -0.1668783    0.112000    0.391000   -0.1653000
+44167.00   0.109010   0.379805  -0.1697354    0.113000    0.389000   -0.1681000
+44168.00   0.110472   0.378448  -0.1725428    0.114000    0.388000   -0.1708000
+44169.00   0.111973   0.377043  -0.1752734    0.115000    0.387000   -0.1735000
+44170.00   0.113508   0.375592  -0.1779123    0.116000    0.386000   -0.1762000
+44171.00   0.115071   0.374096  -0.1804638    0.117000    0.385000   -0.1790000
+44172.00   0.116658   0.372558  -0.1829532    0.117000    0.383000   -0.1817000
+44173.00   0.118264   0.370981  -0.1854237    0.118000    0.382000   -0.1845000
+44174.00   0.119881   0.369370  -0.1879304    0.119000    0.381000   -0.1872000
+44175.00   0.121499   0.367730  -0.1905278    0.120000    0.380000   -0.1899000
+44176.00   0.123110   0.366067  -0.1932557    0.121000    0.378000   -0.1927000
+44177.00   0.124704   0.364386  -0.1961259    0.121000    0.377000   -0.1954000
+44178.00   0.126267   0.362696  -0.1991127    0.122000    0.375000   -0.1982000
+44179.00   0.127786   0.361002  -0.2021533    0.123000    0.374000   -0.2009000
+44180.00   0.129247   0.359310  -0.2051622    0.124000    0.372000   -0.2036000
+44181.00   0.130640   0.357627  -0.2080566    0.125000    0.370000   -0.2063000
+44182.00   0.131952   0.355957  -0.2107808    0.126000    0.369000   -0.2090000
+44183.00   0.133174   0.354305  -0.2133200    0.127000    0.367000   -0.2117000
+44184.00   0.134298   0.352671  -0.2156970    0.128000    0.365000   -0.2144000
+44185.00   0.135323   0.351057  -0.2179589    0.128990    0.363000   -0.2170000
+44186.00   0.136248   0.349458  -0.2201601    0.130000    0.361000   -0.2197000
+44187.00   0.137075   0.347869  -0.2223508    0.131000    0.358000   -0.2223000
+44188.00   0.137808   0.346286  -0.2245710    0.132000    0.356000   -0.2250000
+44189.00   0.138446   0.344704  -0.2268483    0.133000    0.354000   -0.2276000
+44190.00   0.138994   0.343119  -0.2291972    0.134000    0.352000   -0.2302000
+44191.00   0.139451   0.341524  -0.2316187    0.135000    0.350000   -0.2328000
+44192.00   0.139821   0.339914  -0.2341023    0.135000    0.347000   -0.2355000
+44193.00   0.140103   0.338282  -0.2366291    0.136000    0.345000   -0.2381000
+44194.00   0.140302   0.336622  -0.2391737    0.136990    0.343000   -0.2407000
+44195.00   0.140426   0.334928  -0.2417079    0.138000    0.341000   -0.2433000
+44196.00   0.140482   0.333196  -0.2442046    0.138000    0.339000   -0.2459000
+44197.00   0.140477   0.331420  -0.2466437    0.139000    0.336000   -0.2485000
+44198.00   0.140419   0.329595  -0.2490207    0.139000    0.334000   -0.2511000
+44199.00   0.140318   0.327721  -0.2513531    0.140000    0.332000   -0.2536990
+44200.00   0.140180   0.325795  -0.2536803    0.140000    0.330000   -0.2563000
+44201.00   0.140012   0.323818  -0.2560557    0.140000    0.328000   -0.2588990
+44202.00   0.139821   0.321787  -0.2585334    0.141000    0.324990   -0.2615000
+44203.00   0.139611   0.319701  -0.2611536    0.141000    0.323000   -0.2641000
+44204.00   0.139390   0.317562  -0.2639311    0.141000    0.321000   -0.2667000
+44205.00   0.139167   0.315386  -0.2668483    0.141000    0.319000   -0.2693000
+44206.00   0.138953   0.313192  -0.2698572    0.141000    0.316000   -0.2718000
+44207.00   0.138753   0.310995  -0.2728880    0.141000    0.314000   -0.2744000
+44208.00   0.138573   0.308809  -0.2758646    0.141000    0.310990   -0.2769000
+44209.00   0.138418   0.306639  -0.2787243    0.141000    0.308990   -0.2795000
+44210.00   0.138289   0.304489  -0.2814329    0.141000    0.307000   -0.2821000
+44211.00   0.138190   0.302360  -0.2839919    0.141000    0.305000   -0.2846000
+44212.00   0.138124   0.300255  -0.2864315    0.140000    0.303000   -0.2872000
+44213.00   0.138091   0.298174  -0.2887981    0.140000    0.301000   -0.2897000
+44214.00   0.138089   0.296114  -0.2911396    0.140000    0.299000   -0.2923000
+44215.00   0.138117   0.294075  -0.2934961    0.140000    0.297000   -0.2949000
+44216.00   0.138168   0.292058  -0.2958970    0.140000    0.294990   -0.2974000
+44217.00   0.138238   0.290066  -0.2983595    0.139000    0.293000   -0.3000000
+44218.00   0.138320   0.288099  -0.3008881    0.139000    0.291000   -0.3025000
+44219.00   0.138410   0.286159  -0.3034748    0.139000    0.289000   -0.3051000
+44220.00   0.138505   0.284248  -0.3061010    0.139000    0.287000   -0.3076000
+44221.00   0.138603   0.282368  -0.3087421    0.138000    0.285000   -0.3102000
+44222.00   0.138703   0.280516  -0.3113708    0.138000    0.283000   -0.3127000
+44223.00   0.138805   0.278690  -0.3139607    0.136990    0.280990   -0.3153000
+44224.00   0.138909   0.276887  -0.3164895    0.136990    0.279000   -0.3178000
+44225.00   0.139014   0.275106  -0.3189447    0.136990    0.277000   -0.3203000
+44226.00   0.139120   0.273342  -0.3213305    0.136000    0.275000   -0.3229000
+44227.00   0.139225   0.271595  -0.3236743    0.136000    0.273990   -0.3254000
+44228.00   0.139326   0.269861  -0.3260234    0.135000    0.272000   -0.3280000
+44229.00   0.139418   0.268135  -0.3284335    0.135000    0.270000   -0.3305000
+44230.00   0.139498   0.266416  -0.3309502    0.134000    0.268000   -0.3330000
+44231.00   0.139560   0.264700  -0.3335935    0.134000    0.266000   -0.3355000
+44232.00   0.139603   0.262986  -0.3363494    0.133000    0.264000   -0.3380000
+44233.00   0.139621   0.261273  -0.3391744    0.133000    0.262000   -0.3405000
+44234.00   0.139611   0.259557  -0.3420076    0.132000    0.260000   -0.3430000
+44235.00   0.139569   0.257838  -0.3447863    0.131000    0.257990   -0.3455000
+44236.00   0.139492   0.256114  -0.3474594    0.131000    0.256000   -0.3480000
+44237.00   0.139373   0.254387  -0.3499978    0.130000    0.255000   -0.3506000
+44238.00   0.139205   0.252659  -0.3524007    0.130000    0.253000   -0.3531000
+44239.00   0.138980   0.250935   0.6453058    0.128990    0.250990    0.6444000
+44240.00   0.138693   0.249216   0.6430784    0.128000    0.249000    0.6419000
+44241.00   0.138335   0.247506   0.6408691    0.127000    0.247000    0.6394000
+44242.00   0.137902   0.245807   0.6386352    0.127000    0.246000    0.6368000
+44243.00   0.137395   0.244120   0.6363457    0.126000    0.244000    0.6343000
+44244.00   0.136811   0.242446   0.6339818    0.125000    0.242000    0.6318000
+44245.00   0.136151   0.240786   0.6315374    0.124000    0.240000    0.6293000
+44246.00   0.135413   0.239142   0.6290184    0.123000    0.239000    0.6267000
+44247.00   0.134598   0.237513   0.6264412    0.122000    0.237000    0.6242000
+44248.00   0.133709   0.235902   0.6238294    0.121000    0.236000    0.6216000
+44249.00   0.132757   0.234310   0.6212086    0.120000    0.234000    0.6191000
+44250.00   0.131748   0.232740   0.6186020    0.119000    0.232000    0.6166000
+44251.00   0.130692   0.231191   0.6160289    0.118000    0.231000    0.6141000
+44252.00   0.129593   0.229665   0.6135018    0.116000    0.229000    0.6115000
+44253.00   0.128457   0.228159   0.6110210    0.115000    0.228000    0.6090000
+44254.00   0.127290   0.226672   0.6085683    0.114000    0.226000    0.6065000
+44255.00   0.126096   0.225205   0.6061039    0.113000    0.224000    0.6040000
+44256.00   0.124881   0.223755   0.6035730    0.111000    0.223000    0.6015000
+44257.00   0.123646   0.222321   0.6009239    0.110000    0.221000    0.5989000
+44258.00   0.122392   0.220902   0.5981297    0.108000    0.220000    0.5964000
+44259.00   0.121118   0.219496   0.5952043    0.107000    0.218000    0.5939000
+44260.00   0.119821   0.218103   0.5922023    0.106000    0.216000    0.5914000
+44261.00   0.118501   0.216720   0.5892033    0.104000    0.215000    0.5889000
+44262.00   0.117150   0.215348   0.5862886    0.103000    0.213000    0.5863000
+44263.00   0.115758   0.213987   0.5835214    0.101000    0.212000    0.5838000
+44264.00   0.114323   0.212638   0.5809352    0.100000    0.210000    0.5813000
+44265.00   0.112841   0.211304   0.5785319    0.099000    0.209000    0.5788000
+44266.00   0.111310   0.209984   0.5762861    0.097000    0.207000    0.5764000
+44267.00   0.109728   0.208682   0.5741545    0.096000    0.206000    0.5739000
+44268.00   0.108092   0.207398   0.5720865    0.094000    0.204000    0.5715000
+44269.00   0.106402   0.206132   0.5700354    0.093000    0.203000    0.5690000
+44270.00   0.104655   0.204884   0.5679641    0.092000    0.202000    0.5665000
+44271.00   0.102851   0.203651   0.5658492    0.090000    0.201000    0.5641000
+44272.00   0.100993   0.202433   0.5636796    0.089000    0.199000    0.5616000
+44273.00   0.099091   0.201231   0.5614565    0.087000    0.198000    0.5592000
+44274.00   0.097155   0.200045   0.5591909    0.086000    0.197000    0.5567000
+44275.00   0.095195   0.198875   0.5569018    0.085000    0.196000    0.5543000
+44276.00   0.093226   0.197723   0.5546116    0.084000    0.195000    0.5519000
+44277.00   0.091266   0.196591   0.5523411    0.083000    0.193000    0.5494000
+44278.00   0.089333   0.195479   0.5501051    0.082000    0.192000    0.5470000
+44279.00   0.087446   0.194388   0.5479092    0.081000    0.191000    0.5446000
+44280.00   0.085620   0.193319   0.5457478    0.080000    0.190000    0.5422000
+44281.00   0.083873   0.192271   0.5436007    0.079000    0.189000    0.5397000
+44282.00   0.082218   0.191244   0.5414283    0.079000    0.188000    0.5373000
+44283.00   0.080651   0.190240   0.5391704    0.078000    0.187000    0.5348000
+44284.00   0.079166   0.189260   0.5367591    0.077000    0.186000    0.5324000
+44285.00   0.077756   0.188305   0.5341446    0.075990    0.185000    0.5300000
+44286.00   0.076414   0.187376   0.5313165    0.075000    0.184000    0.5276000
+44287.00   0.075131   0.186474   0.5283146    0.075000    0.184000    0.5251000
+44288.00   0.073900   0.185601   0.5252221    0.074000    0.183000    0.5227000
+44289.00   0.072715   0.184756   0.5221418    0.073000    0.182000    0.5202990
+44290.00   0.071567   0.183944   0.5191657    0.071990    0.181000    0.5179000
+44291.00   0.070449   0.183165   0.5163523    0.071000    0.180000    0.5155000
+44292.00   0.069354   0.182425   0.5137186    0.071000    0.180000    0.5130000
+44293.00   0.068273   0.181726   0.5112459    0.070000    0.179000    0.5105990
+44294.00   0.067199   0.181072   0.5088943    0.069000    0.178000    0.5082000
+44295.00   0.066122   0.180469   0.5066145    0.068000    0.177000    0.5058000
+44296.00   0.065033   0.179919   0.5043588    0.067000    0.177000    0.5034000
+44297.00   0.063923   0.179427   0.5020888    0.067000    0.176000    0.5010000
+44298.00   0.062782   0.178993   0.4997791    0.066000    0.176000    0.4986000
+44299.00   0.061603   0.178619   0.4974185    0.065000    0.175000    0.4962000
+44300.00   0.060380   0.178307   0.4950075    0.064000    0.175000    0.4938000
+44301.00   0.059110   0.178054   0.4925570    0.063000    0.175000    0.4913000
+44302.00   0.057789   0.177863   0.4900871    0.062000    0.175000    0.4889000
+44303.00   0.056416   0.177730   0.4876240    0.061000    0.175000    0.4864000
+44304.00   0.054990   0.177653   0.4851957    0.060000    0.175000    0.4840000
+44305.00   0.053512   0.177625   0.4828251    0.059000    0.175000    0.4815000
+44306.00   0.051987   0.177643   0.4805241    0.057000    0.175000    0.4790000
+44307.00   0.050421   0.177702   0.4782909    0.056000    0.176000    0.4766000
+44308.00   0.048818   0.177795   0.4761094    0.054000    0.176000    0.4741000
+44309.00   0.047184   0.177919   0.4739461    0.053000    0.176000    0.4716000
+44310.00   0.045521   0.178073   0.4717485    0.052000    0.176000    0.4691000
+44311.00   0.043833   0.178255   0.4694499    0.050000    0.177000    0.4666000
+44312.00   0.042121   0.178468   0.4669834    0.049000    0.177000    0.4641000
+44313.00   0.040391   0.178710   0.4643042    0.047000    0.178000    0.4616000
+44314.00   0.038646   0.178983   0.4614115    0.046000    0.178000    0.4591000
+44315.00   0.036890   0.179286   0.4583594    0.044000    0.179000    0.4566000
+44316.00   0.035126   0.179618   0.4552437    0.042000    0.179000    0.4541000
+44317.00   0.033358   0.179979   0.4521716    0.041000    0.180000    0.4515000
+44318.00   0.031588   0.180365   0.4492286    0.039000    0.180000    0.4490000
+44319.00   0.029816   0.180776   0.4464563    0.037000    0.181000    0.4465000
+44320.00   0.028044   0.181209   0.4438491    0.035000    0.182000    0.4439000
+44321.00   0.026277   0.181661   0.4413673    0.033000    0.182000    0.4413000
+44322.00   0.024517   0.182131   0.4389552    0.032000    0.183000    0.4388000
+44323.00   0.022769   0.182616   0.4365571    0.030000    0.183000    0.4362000
+44324.00   0.021032   0.183114   0.4341277    0.028000    0.184000    0.4336000
+44325.00   0.019308   0.183625   0.4316361    0.026000    0.185000    0.4310000
+44326.00   0.017596   0.184149   0.4290667    0.024000    0.186000    0.4284000
+44327.00   0.015900   0.184683   0.4264192    0.021000    0.186000    0.4258000
+44328.00   0.014225   0.185227   0.4237058    0.018990    0.187000    0.4232000
+44329.00   0.012573   0.185779   0.4209487    0.017000    0.188000    0.4206000
+44330.00   0.010942   0.186341   0.4181764    0.014000    0.189000    0.4180000
+44331.00   0.009330   0.186913   0.4154210    0.012000    0.190000    0.4154000
+44332.00   0.007732   0.187499   0.4127132    0.008990    0.191000    0.4127000
+44333.00   0.006145   0.188098   0.4100757    0.007000    0.192000    0.4101000
+44334.00   0.004564   0.188713   0.4075181    0.004000    0.193000    0.4075000
+44335.00   0.002985   0.189344   0.4050339    0.002000    0.194000    0.4049000
+44336.00   0.001404   0.189993   0.4025986    0.000000    0.195000    0.4022000
+44337.00  -0.000184   0.190657   0.4001715   -0.003000    0.196000    0.3996000
+44338.00  -0.001784   0.191338   0.3976986   -0.005000    0.197000    0.3969000
+44339.00  -0.003399   0.192036   0.3951184   -0.007000    0.198000    0.3943000
+44340.00  -0.005035   0.192749   0.3923766   -0.008990    0.199000    0.3916000
+44341.00  -0.006693   0.193480   0.3894468   -0.011000    0.200000    0.3889000
+44342.00  -0.008371   0.194228   0.3863481   -0.012000    0.202000    0.3863000
+44343.00  -0.010064   0.194996   0.3831491   -0.014000    0.203000    0.3836000
+44344.00  -0.011767   0.195783   0.3799519   -0.016000    0.204000    0.3809000
+44345.00  -0.013473   0.196591   0.3768602   -0.017990    0.205000    0.3782000
+44346.00  -0.015173   0.197424   0.3739470   -0.018990    0.206000    0.3756000
+44347.00  -0.016861   0.198283   0.3712357   -0.021000    0.208000    0.3729000
+44348.00  -0.018531   0.199172   0.3687020   -0.022000    0.209000    0.3703000
+44349.00  -0.020180   0.200093   0.3662917   -0.024000    0.210000    0.3676000
+44350.00  -0.021803   0.201050   0.3639420   -0.025000    0.211000    0.3650000
+44351.00  -0.023391   0.202038   0.3615979   -0.027000    0.213000    0.3623000
+44352.00  -0.024933   0.203054   0.3592189   -0.028000    0.214000    0.3597000
+44353.00  -0.026421   0.204098   0.3567803   -0.030000    0.216000    0.3570000
+44354.00  -0.027852   0.205166   0.3542727   -0.031000    0.217000    0.3544000
+44355.00  -0.029222   0.206260   0.3517004   -0.032000    0.218000    0.3518000
+44356.00  -0.030530   0.207376   0.3490794   -0.033000    0.220000    0.3492000
+44357.00  -0.031780   0.208512   0.3464344   -0.035000    0.221000    0.3465000
+44358.00  -0.032974   0.209662   0.3437946   -0.035990    0.223000    0.3439000
+44359.00  -0.034117   0.210823   0.3411890   -0.037000    0.224000    0.3413000
+44360.00  -0.035211   0.211990   0.3386427   -0.037990    0.225000    0.3387000
+44361.00  -0.036260   0.213160   0.3361710   -0.039000    0.226000    0.3362000
+44362.00  -0.037268   0.214328   0.3337745   -0.040000    0.228000    0.3336000
+44363.00  -0.038237   0.215493   0.3314354   -0.041000    0.229000    0.3311000
+44364.00  -0.039170   0.216651   0.3291184   -0.042000    0.230000    0.3285000
+44365.00  -0.040070   0.217799   0.3267747   -0.043000    0.231000    0.3260000
+44366.00  -0.040940   0.218938   0.3243499   -0.044000    0.232000    0.3235000
+44367.00  -0.041786   0.220064   0.3217946   -0.044000    0.234000    0.3210000
+44368.00  -0.042610   0.221179   0.3190770   -0.045000    0.235000    0.3185000
+44369.00  -0.043412   0.222281   0.3161979   -0.046000    0.236000    0.3160000
+44370.00  -0.044191   0.223371   0.3131995   -0.047000    0.237000    0.3135000
+44371.00  -0.044944   0.224447   0.3101614   -0.047000    0.238000    0.3111000
+44372.00  -0.045667   0.225511   0.3071817   -0.048000    0.240000    0.3086000
+44373.00  -0.046356   0.226563   0.3043487   -0.048000    0.241000    0.3062000
+44374.00  -0.047008   0.227603   0.3017146   -0.049000    0.242000    0.3037000
+44375.00  -0.047627   0.228634   0.2992833   -0.050000    0.243000    0.3014000
+44376.00  -0.048213   0.229657   0.2970177   -0.050000    0.244000    0.2990000
+44377.00  -0.048768   0.230674   0.2948597   -0.051000    0.245000    0.2967000
+44378.00  -0.049291   0.231685   0.2927505   -0.051000    0.246000    0.2943000
+44379.00  -0.049783   0.232689   0.2906432   -0.052000    0.247000    0.2920000
+44380.00  -0.050242   0.233687   0.2885074   -0.052000    0.248000    0.2897000
+44381.00  -0.050670   0.234679   0.2863292   -0.052000    0.249000    0.2874000
+44382.00  -0.051071   0.235667   0.2841089   -0.053000    0.250000    0.2852000
+44383.00  -0.051448   0.236653   0.2818591   -0.053000    0.250990    0.2829000
+44384.00  -0.051806   0.237638   0.2796014   -0.053000    0.252000    0.2806000
+44385.00  -0.052152   0.238627   0.2773624   -0.053000    0.253000    0.2784000
+44386.00  -0.052492   0.239621   0.2751687   -0.053000    0.254000    0.2762000
+44387.00  -0.052829   0.240622   0.2730435   -0.054000    0.255000    0.2741000
+44388.00  -0.053166   0.241634   0.2710025   -0.054000    0.256000    0.2719000
+44389.00  -0.053503   0.242657   0.2690501   -0.054000    0.257000    0.2697000
+44390.00  -0.053835   0.243694   0.2671735   -0.054000    0.257990    0.2676000
+44391.00  -0.054155   0.244745   0.2653405   -0.054000    0.259000    0.2655000
+44392.00  -0.054454   0.245810   0.2635027   -0.054000    0.260000    0.2634000
+44393.00  -0.054729   0.246888   0.2616043   -0.054000    0.261000    0.2613000
+44394.00  -0.054978   0.247978   0.2595953   -0.054000    0.262000    0.2592000
+44395.00  -0.055198   0.249076   0.2574431   -0.054000    0.263000    0.2572000
+44396.00  -0.055390   0.250181   0.2551422   -0.054000    0.264000    0.2551000
+44397.00  -0.055555   0.251291   0.2527187   -0.054000    0.264000    0.2531000
+44398.00  -0.055697   0.252403   0.2502291   -0.054000    0.264990    0.2509990
+44399.00  -0.055819   0.253518   0.2477506   -0.054000    0.266000    0.2490000
+44400.00  -0.055929   0.254635   0.2453624   -0.054000    0.266990    0.2470000
+44401.00  -0.056030   0.255754   0.2431236   -0.054000    0.268000    0.2451000
+44402.00  -0.056121   0.256876   0.2410578   -0.053000    0.268000    0.2431000
+44403.00  -0.056197   0.258000   0.2391486   -0.053000    0.269000    0.2412000
+44404.00  -0.056252   0.259130   0.2373506   -0.053000    0.270000    0.2392000
+44405.00  -0.056283   0.260267   0.2356071   -0.053000    0.271000    0.2373000
+44406.00  -0.056288   0.261414   0.2338680   -0.053000    0.271000    0.2355000
+44407.00  -0.056263   0.262575   0.2320990   -0.052000    0.272000    0.2336000
+44408.00  -0.056206   0.263751   0.2302829   -0.052000    0.272000    0.2318000
+44409.00  -0.056120   0.264944   0.2284177   -0.052000    0.273000    0.2299000
+44410.00  -0.056007   0.266154   0.2265144   -0.051000    0.273990    0.2281000
+44411.00  -0.055869   0.267379   0.2245938   -0.051000    0.273990    0.2263000
+44412.00  -0.055710   0.268620   0.2226833   -0.050000    0.275000    0.2245000
+44413.00  -0.055531   0.269874   0.2208110   -0.050000    0.275000    0.2227000
+44414.00  -0.055334   0.271140   0.2190006   -0.049000    0.276000    0.2209000
+44415.00  -0.055118   0.272416   0.2172690   -0.048000    0.277000    0.2192000
+44416.00  -0.054881   0.273700   0.2156238   -0.048000    0.277000    0.2175000
+44417.00  -0.054612   0.274989   0.2140596   -0.047000    0.278000    0.2157000
+44418.00  -0.054299   0.276282   0.2125530   -0.047000    0.278000    0.2140000
+44419.00  -0.053930   0.277574   0.2110616   -0.046000    0.279000    0.2123000
+44420.00  -0.053497   0.278865   0.2095287   -0.045000    0.280000    0.2106000
+44421.00  -0.052996   0.280150   0.2078977   -0.044000    0.280000    0.2089000
+44422.00  -0.052422   0.281429   0.2061299   -0.044000    0.280990    0.2073000
+44423.00  -0.051780   0.282702   0.2042170   -0.043000    0.280990    0.2056000
+44424.00  -0.051095   0.283971   0.2021850   -0.042000    0.282000    0.2039000
+44425.00  -0.050384   0.285238   0.2000888   -0.041000    0.283000    0.2022000
+44426.00  -0.049652   0.286501   0.1980003   -0.041000    0.283000    0.2006000
+44427.00  -0.048902   0.287755   0.1959907   -0.040000    0.284000    0.1989000
+44428.00  -0.048137   0.288998   0.1941150   -0.040000    0.284000    0.1973000
+44429.00  -0.047357   0.290226   0.1924001   -0.039000    0.285000    0.1956000
+44430.00  -0.046564   0.291435   0.1908393   -0.037990    0.286000    0.1939000
+44431.00  -0.045762   0.292623   0.1893957   -0.037990    0.287000    0.1923000
+44432.00  -0.044962   0.293793   0.1880149   -0.037000    0.287000    0.1906000
+44433.00  -0.044175   0.294946   0.1866427   -0.037000    0.287990    0.1890000
+44434.00  -0.043412   0.296085   0.1852377   -0.035990    0.289000    0.1873000
+44435.00  -0.042680   0.297212   0.1837753   -0.035000    0.290000    0.1857000
+44436.00  -0.041982   0.298330   0.1822469   -0.035000    0.291000    0.1841000
+44437.00  -0.041317   0.299440   0.1806576   -0.034000    0.291000    0.1824000
+44438.00  -0.040682   0.300543   0.1790231   -0.034000    0.292000    0.1808000
+44439.00  -0.040070   0.301639   0.1773670   -0.033000    0.293000    0.1792000
+44440.00  -0.039481   0.302728   0.1757154   -0.032000    0.294000    0.1776000
+44441.00  -0.038915   0.303805   0.1740930   -0.032000    0.294000    0.1760000
+44442.00  -0.038378   0.304868   0.1725194   -0.031000    0.294990    0.1743000
+44443.00  -0.037876   0.305911   0.1710052   -0.031000    0.294990    0.1727000
+44444.00  -0.037416   0.306930   0.1695499   -0.030000    0.296000    0.1711000
+44445.00  -0.037005   0.307918   0.1681384   -0.030000    0.297000    0.1695000
+44446.00  -0.036652   0.308869   0.1667384   -0.029000    0.297000    0.1679000
+44447.00  -0.036373   0.309774   0.1653007   -0.029000    0.298000    0.1662000
+44448.00  -0.036176   0.310620   0.1637689   -0.028000    0.298000    0.1646000
+44449.00  -0.036043   0.311387   0.1620941   -0.028000    0.299000    0.1630000
+44450.00  -0.035959   0.312081   0.1602557   -0.028000    0.299000    0.1614000
+44451.00  -0.035911   0.312715   0.1582745   -0.027000    0.300000    0.1598000
+44452.00  -0.035885   0.313299   0.1562108   -0.027000    0.300000    0.1581000
+44453.00  -0.035873   0.313843   0.1541461   -0.026000    0.301000    0.1565000
+44454.00  -0.035866   0.314353   0.1521605   -0.026000    0.301000    0.1549000
+44455.00  -0.035856   0.314838   0.1503126   -0.026000    0.301990    0.1533000
+44456.00  -0.035834   0.315303   0.1486280   -0.025000    0.301990    0.1517000
+44457.00  -0.035796   0.315753   0.1470977   -0.025000    0.303000    0.1500000
+44458.00  -0.035736   0.316194   0.1456845   -0.024000    0.303000    0.1484000
+44459.00  -0.035652   0.316631   0.1443340   -0.024000    0.303990    0.1468000
+44460.00  -0.035543   0.317071   0.1429894   -0.024000    0.305000    0.1451000
+44461.00  -0.035410   0.317518   0.1416036   -0.023000    0.305000    0.1435000
+44462.00  -0.035258   0.317974   0.1401463   -0.023000    0.306000    0.1418000
+44463.00  -0.035091   0.318443   0.1386056   -0.022000    0.306000    0.1402000
+44464.00  -0.034914   0.318927   0.1369849   -0.022000    0.307000    0.1385000
+44465.00  -0.034730   0.319428   0.1352991   -0.022000    0.308000    0.1368000
+44466.00  -0.034545   0.319948   0.1335716   -0.021000    0.308000    0.1351000
+44467.00  -0.034361   0.320488   0.1318295   -0.021000    0.308990    0.1333000
+44468.00  -0.034185   0.321048   0.1300996   -0.020000    0.308990    0.1316000
+44469.00  -0.034021   0.321629   0.1284021   -0.020000    0.310000    0.1298990
+44470.00  -0.033876   0.322231   0.1267463   -0.020000    0.310990    0.1280990
+44471.00  -0.033753   0.322852   0.1251295   -0.020000    0.310990    0.1263000
+44472.00  -0.033655   0.323491   0.1235369   -0.018990    0.312000    0.1244000
+44473.00  -0.033585   0.324146   0.1219387   -0.018990    0.312000    0.1226000
+44474.00  -0.033544   0.324811   0.1202889   -0.018990    0.313000    0.1208000
+44475.00  -0.033532   0.325482   0.1185273   -0.018990    0.313000    0.1189000
+44476.00  -0.033545   0.326154   0.1165928   -0.018990    0.314000    0.1170000
+44477.00  -0.033575   0.326823   0.1144442   -0.017990    0.314000    0.1151000
+44478.00  -0.033616   0.327484   0.1120823   -0.017990    0.315000    0.1132000
+44479.00  -0.033658   0.328135   0.1095587   -0.017990    0.315000    0.1113000
+44480.00  -0.033692   0.328774   0.1069650   -0.017990    0.315000    0.1093000
+44481.00  -0.033706   0.329403   0.1044053   -0.017990    0.316000    0.1073000
+44482.00  -0.033692   0.330022   0.1019650   -0.017000    0.316000    0.1052000
+44483.00  -0.033648   0.330632   0.0996895   -0.017000    0.317000    0.1032000
+44484.00  -0.033568   0.331235   0.0975787   -0.017000    0.317000    0.1012000
+44485.00  -0.033449   0.331832   0.0955965   -0.017000    0.317990    0.0991000
+44486.00  -0.033291   0.332424   0.0936871   -0.016000    0.317990    0.0969000
+44487.00  -0.033093   0.333013   0.0917909   -0.016000    0.319000    0.0948000
+44488.00  -0.032856   0.333598   0.0898578   -0.015000    0.319000    0.0926000
+44489.00  -0.032585   0.334183   0.0878542   -0.015000    0.320000    0.0905000
+44490.00  -0.032281   0.334766   0.0857654   -0.015000    0.321000    0.0883000
+44491.00  -0.031950   0.335350   0.0835940   -0.014000    0.321000    0.0861000
+44492.00  -0.031593   0.335936   0.0813562   -0.014000    0.322000    0.0838000
+44493.00  -0.031215   0.336526   0.0790771   -0.013000    0.322000    0.0816000
+44494.00  -0.030818   0.337119   0.0767870   -0.013000    0.323000    0.0794000
+44495.00  -0.030404   0.337719   0.0745165   -0.013000    0.324000    0.0771000
+44496.00  -0.029974   0.338327   0.0722922   -0.012000    0.324000    0.0748000
+44497.00  -0.029527   0.338943   0.0701300   -0.012000    0.324990    0.0724000
+44498.00  -0.029065   0.339567   0.0680318   -0.011000    0.324990    0.0701000
+44499.00  -0.028584   0.340199   0.0659849   -0.011000    0.326000    0.0678000
+44500.00  -0.028086   0.340837   0.0639626   -0.011000    0.327000    0.0653990
+44501.00  -0.027570   0.341480   0.0619229   -0.010000    0.327000    0.0630000
+44502.00  -0.027034   0.342124   0.0598094   -0.010000    0.328000    0.0607000
+44503.00  -0.026481   0.342766   0.0575568   -0.008990    0.328000    0.0583000
+44504.00  -0.025918   0.343399   0.0551051   -0.008990    0.329000    0.0559000
+44505.00  -0.025350   0.344019   0.0524227   -0.008990    0.330000    0.0534000
+44506.00  -0.024784   0.344617   0.0495267   -0.008000    0.331000    0.0510000
+44507.00  -0.024228   0.345183   0.0464885   -0.008000    0.332000    0.0485000
+44508.00  -0.023692   0.345704   0.0434152   -0.007000    0.333000    0.0461000
+44509.00  -0.023185   0.346167   0.0404165   -0.007000    0.334000    0.0436000
+44510.00  -0.022722   0.346558   0.0375738   -0.007000    0.335000    0.0411000
+44511.00  -0.022320   0.346877   0.0349343   -0.007000    0.336000    0.0385000
+44512.00  -0.021988   0.347173   0.0324995   -0.006000    0.337000    0.0360000
+44513.00  -0.021717   0.347498   0.0302196   -0.006000    0.338000    0.0334000
+44514.00  -0.021472   0.347867   0.0280224   -0.006000    0.339000    0.0309000
+44515.00  -0.021217   0.348281   0.0258395   -0.006000    0.340000    0.0283000
+44516.00  -0.020922   0.348738   0.0236201   -0.006000    0.341000    0.0258000
+44517.00  -0.020573   0.349238   0.0213366   -0.005000    0.341000    0.0232000
+44518.00  -0.020162   0.349784   0.0189812   -0.005000    0.342000    0.0207000
+44519.00  -0.019683   0.350378   0.0165613   -0.005000    0.343000    0.0181000
+44520.00  -0.019138   0.351018   0.0140960   -0.005000    0.344000    0.0155000
+44521.00  -0.018535   0.351705   0.0116109   -0.005000    0.345000    0.0129000
+44522.00  -0.017881   0.352436   0.0091350   -0.004000    0.345000    0.0103000
+44523.00  -0.017190   0.353206   0.0066954   -0.004000    0.346000    0.0077000
+44524.00  -0.016473   0.354013   0.0043126   -0.004000    0.347000    0.0051000
+44525.00  -0.015748   0.354848   0.0019960   -0.004000    0.348000    0.0025000
+44526.00  -0.015032   0.355706  -0.0002604   -0.004000    0.349000   -0.0002000
+44527.00  -0.014340   0.356578  -0.0024781   -0.004000    0.349000   -0.0028000
+44528.00  -0.013690   0.357455  -0.0046917   -0.004000    0.350000   -0.0055000
+44529.00  -0.013099   0.358326  -0.0069477   -0.004000    0.351000   -0.0081000
+44530.00  -0.012591   0.359173  -0.0093009   -0.004000    0.352000   -0.0108000
+44531.00  -0.012196   0.359957  -0.0118060   -0.004000    0.353000   -0.0135000
+44532.00  -0.011860   0.360634  -0.0145143   -0.004000    0.353000   -0.0161000
+44533.00  -0.011534   0.361203  -0.0174444   -0.004000    0.354000   -0.0188000
+44534.00  -0.011167   0.361676  -0.0205670   -0.004000    0.355000   -0.0215000
+44535.00  -0.010731   0.362085  -0.0237945   -0.004000    0.356000   -0.0242000
+44536.00  -0.010254   0.362467  -0.0270050   -0.004000    0.357000   -0.0268000
+44537.00  -0.009746   0.362830  -0.0300888   -0.005000    0.358000   -0.0295000
+44538.00  -0.009207   0.363175  -0.0329788   -0.005000    0.359000   -0.0321000
+44539.00  -0.008637   0.363503  -0.0356645   -0.005000    0.360000   -0.0348000
+44540.00  -0.008038   0.363816  -0.0381850   -0.005000    0.361000   -0.0375000
+44541.00  -0.007413   0.364115  -0.0406058   -0.006000    0.362000   -0.0402000
+44542.00  -0.006765   0.364402  -0.0429935   -0.006000    0.363000   -0.0428000
+44543.00  -0.006092   0.364677  -0.0453991   -0.007000    0.364000   -0.0455000
+44544.00  -0.005390   0.364943  -0.0478520   -0.007000    0.365000   -0.0482000
+44545.00  -0.004658   0.365200  -0.0503616   -0.007000    0.366000   -0.0508000
+44546.00  -0.003896   0.365451  -0.0529205   -0.007000    0.366000   -0.0534000
+44547.00  -0.003106   0.365698  -0.0555091   -0.006000    0.367000   -0.0560000
+44548.00  -0.002287   0.365945  -0.0581001   -0.006000    0.367000   -0.0586000
+44549.00  -0.001437   0.366193  -0.0606628   -0.006000    0.368000   -0.0612000
+44550.00  -0.000553   0.366443  -0.0631675   -0.006000    0.369000   -0.0638000
+44551.00   0.000371   0.366696  -0.0655907   -0.005000    0.369000   -0.0664000
+44552.00   0.001340   0.366953  -0.0679193   -0.005000    0.370000   -0.0689000
+44553.00   0.002361   0.367214  -0.0701540   -0.004000    0.370000   -0.0715000
+44554.00   0.003438   0.367480  -0.0723119   -0.004000    0.371000   -0.0741000
+44555.00   0.004576   0.367750  -0.0744269   -0.003000    0.371000   -0.0767000
+44556.00   0.005773   0.368025  -0.0765465   -0.002000    0.372000   -0.0792000
+44557.00   0.007027   0.368305  -0.0787259   -0.002000    0.372000   -0.0818000
+44558.00   0.008338   0.368590  -0.0810214   -0.001000    0.373000   -0.0843000
+44559.00   0.009704   0.368879  -0.0834809    0.000000    0.373000   -0.0869000
+44560.00   0.011124   0.369171  -0.0861313    0.001000    0.373000   -0.0894000
+44561.00   0.012595   0.369466  -0.0889644    0.002000    0.374000   -0.0919000
+44562.00   0.014116   0.369758  -0.0919282    0.004000    0.374000   -0.0945000
+44563.00   0.015682   0.370042  -0.0949346    0.005000    0.375000   -0.0970000
+44564.00   0.017292   0.370316  -0.0978809    0.006000    0.375000   -0.0995000
+44565.00   0.018944   0.370574  -0.1006828    0.007000    0.375000   -0.1020000
+44566.00   0.020631   0.370816  -0.1033006    0.008000    0.376000   -0.1045000
+44567.00   0.022349   0.371039  -0.1057468    0.010000    0.376000   -0.1069000
+44568.00   0.024091   0.371242  -0.1080740    0.011000    0.377000   -0.1094000
+44569.00   0.025851   0.371423  -0.1103500    0.012000    0.377000   -0.1119000
+44570.00   0.027614   0.371580  -0.1126341    0.013000    0.377000   -0.1143000
+44571.00   0.029368   0.371712  -0.1149645    0.014000    0.377000   -0.1168000
+44572.00   0.031095   0.371818  -0.1173563    0.016000    0.378000   -0.1192000
+44573.00   0.032776   0.371896  -0.1198056    0.017000    0.378000   -0.1217000
+44574.00   0.034394   0.371947  -0.1222956    0.018000    0.378000   -0.1241000
+44575.00   0.035930   0.371968  -0.1248005    0.019000    0.378000   -0.1265000
+44576.00   0.037371   0.371959  -0.1272907    0.021000    0.378000   -0.1289000
+44577.00   0.038728   0.371917  -0.1297379    0.022000    0.377000   -0.1314000
+44578.00   0.040017   0.371841  -0.1321190    0.024000    0.377000   -0.1338000
+44579.00   0.041254   0.371728  -0.1344185    0.025000    0.377000   -0.1362000
+44580.00   0.042454   0.371577  -0.1366321    0.026000    0.377000   -0.1386000
+44581.00   0.043634   0.371385  -0.1387701    0.028000    0.376000   -0.1410000
+44582.00   0.044806   0.371153  -0.1408587    0.029000    0.376000   -0.1434000
+44583.00   0.045978   0.370882  -0.1429387    0.031000    0.375000   -0.1458000
+44584.00   0.047160   0.370575  -0.1450613    0.032000    0.375000   -0.1482000
+44585.00   0.048360   0.370233  -0.1472792    0.033000    0.374000   -0.1506000
+44586.00   0.049583   0.369860  -0.1496355    0.034000    0.374000   -0.1530000
+44587.00   0.050829   0.369458  -0.1521542    0.036000    0.373000   -0.1553000
+44588.00   0.052094   0.369033  -0.1548320    0.037000    0.373000   -0.1577000
+44589.00   0.053373   0.368587  -0.1576339    0.038000    0.372000   -0.1601000
+44590.00   0.054663   0.368127  -0.1604947    0.039000    0.371000   -0.1625000
+44591.00   0.055958   0.367656  -0.1633315    0.040000    0.371000   -0.1649000
+44592.00   0.057255   0.367181  -0.1660643    0.042000    0.370000   -0.1672000
+44593.00   0.058546   0.366708  -0.1686409    0.043000    0.370000   -0.1696000
+44594.00   0.059819   0.366243  -0.1710521    0.044000    0.369000   -0.1720000
+44595.00   0.061068   0.365777  -0.1733318    0.045000    0.368000   -0.1744000
+44596.00   0.062292   0.365303  -0.1755406    0.046000    0.368000   -0.1768000
+44597.00   0.063487   0.364811  -0.1777423    0.048000    0.367000   -0.1791000
+44598.00   0.064652   0.364297  -0.1799847    0.049000    0.367000   -0.1815000
+44599.00   0.065782   0.363758  -0.1822914    0.050000    0.366000   -0.1839000
+44600.00   0.066876   0.363194  -0.1846637    0.051000    0.365000   -0.1863000
+44601.00   0.067929   0.362608  -0.1870873    0.052000    0.364000   -0.1886000
+44602.00   0.068940   0.362000  -0.1895385    0.053000    0.364000   -0.1910000
+44603.00   0.069905   0.361371  -0.1919885    0.054000    0.363000   -0.1933000
+44604.00   0.070820   0.360722  -0.1944082    0.055000    0.362000   -0.1957000
+44605.00   0.071683   0.360054  -0.1967724    0.056000    0.361000   -0.1981000
+44606.00   0.072490   0.359365  -0.1990643    0.057000    0.360000   -0.2004000
+44607.00   0.073235   0.358658  -0.2012778    0.057000    0.360000   -0.2028000
+44608.00   0.073915   0.357930  -0.2034193    0.058000    0.359000   -0.2051000
+44609.00   0.074525   0.357183  -0.2055098    0.059000    0.358000   -0.2075000
+44610.00   0.075064   0.356414  -0.2075860    0.060000    0.357000   -0.2099000
+44611.00   0.075536   0.355623  -0.2096986    0.060000    0.356000   -0.2122000
+44612.00   0.075945   0.354805  -0.2119034    0.061000    0.355000   -0.2146000
+44613.00   0.076296   0.353958  -0.2142488    0.061000    0.354000   -0.2169000
+44614.00   0.076594   0.353079  -0.2167605    0.062000    0.353000   -0.2193000
+44615.00   0.076842   0.352166  -0.2194331    0.062000    0.352000   -0.2216000
+44616.00   0.077049   0.351219  -0.2222276    0.063000    0.351000   -0.2239000
+44617.00   0.077220   0.350241  -0.2250792    0.063000    0.350000   -0.2263000
+44618.00   0.077364   0.349233  -0.2279115    0.064000    0.349000   -0.2286000
+44619.00   0.077487   0.348200  -0.2306522    0.064000    0.348000   -0.2309000
+44620.00   0.077597   0.347144  -0.2332501    0.064000    0.347000   -0.2332000
+44621.00   0.077698   0.346074  -0.2356873    0.065000    0.346000   -0.2355000
+44622.00   0.077796   0.345000  -0.2379834    0.065000    0.346000   -0.2379000
+44623.00   0.077897   0.343929  -0.2401875    0.066000    0.345000   -0.2402000
+44624.00   0.078006   0.342872  -0.2423598    0.066000    0.344000   -0.2425000
+44625.00   0.078128   0.341837  -0.2445519    0.066000    0.343000   -0.2448000
+44626.00   0.078270   0.340832  -0.2467935    0.067000    0.342000   -0.2471000
+44627.00   0.078438   0.339867  -0.2490897    0.067000    0.342000   -0.2493000
+44628.00   0.078639   0.338947  -0.2514260    0.068000    0.341000   -0.2516000
+44629.00   0.078874   0.338070  -0.2537780    0.068000    0.340000   -0.2539000
+44630.00   0.079143   0.337230  -0.2561161    0.068000    0.339000   -0.2562000
+44631.00   0.079447   0.336422  -0.2584095    0.069000    0.339000   -0.2584000
+44632.00   0.079787   0.335641  -0.2606311    0.069000    0.338000   -0.2607000
+44633.00   0.080160   0.334884  -0.2627623    0.070000    0.338000   -0.2629000
+44634.00   0.080567   0.334149  -0.2647967    0.070000    0.337000   -0.2652000
+44635.00   0.081002   0.333433  -0.2667414    0.070000    0.336000   -0.2674000
+44636.00   0.081459   0.332735  -0.2686169    0.071000    0.335000   -0.2697000
+44637.00   0.081926   0.332049  -0.2704577    0.071000    0.335000   -0.2719000
+44638.00   0.082394   0.331372  -0.2723128    0.072000    0.334000   -0.2742000
+44639.00   0.082853   0.330701  -0.2742421    0.072000    0.333000   -0.2764000
+44640.00   0.083296   0.330031  -0.2763049    0.073000    0.332000   -0.2787000
+44641.00   0.083721   0.329357  -0.2785419    0.073000    0.331000   -0.2810000
+44642.00   0.084131   0.328675  -0.2809579    0.074000    0.330000   -0.2833000
+44643.00   0.084534   0.327978  -0.2835154    0.074000    0.329000   -0.2856000
+44644.00   0.084935   0.327264  -0.2861427    0.075000    0.328000   -0.2879000
+44645.00   0.085339   0.326526  -0.2887526    0.076000    0.327000   -0.2902000
+44646.00   0.085753   0.325761  -0.2912661    0.076000    0.326000   -0.2925000
+44647.00   0.086171   0.324966  -0.2936321    0.077000    0.325000   -0.2949000
+44648.00   0.086582   0.324142  -0.2958376    0.077000    0.324000   -0.2972000
+44649.00   0.086975   0.323288  -0.2979063    0.078000    0.323000   -0.2995000
+44650.00   0.087342   0.322406  -0.2998904    0.079000    0.322000   -0.3019000
+44651.00   0.087683   0.321500  -0.3018556    0.079000    0.321000   -0.3043000
+44652.00   0.087995   0.320574  -0.3038641    0.080000    0.320000   -0.3066000
+44653.00   0.088275   0.319631  -0.3059601    0.080000    0.319000   -0.3090000
+44654.00   0.088523   0.318674  -0.3081632    0.081000    0.318000   -0.3114000
+44655.00   0.088735   0.317707  -0.3104694    0.082000    0.317000   -0.3138000
+44656.00   0.088911   0.316734  -0.3128585    0.082000    0.316000   -0.3162000
+44657.00   0.089055   0.315759  -0.3153014    0.083000    0.315000   -0.3187000
+44658.00   0.089169   0.314787  -0.3177663    0.083000    0.314000   -0.3211000
+44659.00   0.089258   0.313822  -0.3202234    0.084000    0.313000   -0.3235000
+44660.00   0.089324   0.312869  -0.3226500    0.084000    0.312000   -0.3260000
+44661.00   0.089374   0.311931  -0.3250343    0.085000    0.311000   -0.3285000
+44662.00   0.089414   0.311013  -0.3273788    0.085000    0.311000   -0.3309000
+44663.00   0.089444   0.310118  -0.3297000    0.086000    0.310000   -0.3334000
+44664.00   0.089465   0.309250  -0.3320271    0.086000    0.309000   -0.3359000
+44665.00   0.089485   0.308410  -0.3343997    0.086000    0.308000   -0.3384000
+44666.00   0.089511   0.307600  -0.3368684    0.087000    0.307000   -0.3410000
+44667.00   0.089556   0.306819  -0.3394912    0.087000    0.307000   -0.3435000
+44668.00   0.089628   0.306068  -0.3423190    0.088000    0.306000   -0.3461000
+44669.00   0.089735   0.305347  -0.3453741    0.088000    0.305000   -0.3486000
+44670.00   0.089884   0.304656  -0.3486333    0.088000    0.304000   -0.3512000
+44671.00   0.090081   0.303995  -0.3520246    0.088000    0.303000   -0.3537000
+44672.00   0.090330   0.303357  -0.3554424    0.089000    0.303000   -0.3563000
+44673.00   0.090635   0.302736  -0.3587773    0.089000    0.302000   -0.3588000
+44674.00   0.090999   0.302126  -0.3619463    0.089000    0.301000   -0.3614000
+44675.00   0.091420   0.301521  -0.3649125    0.089000    0.300000   -0.3640000
+44676.00   0.091892   0.300917  -0.3676865    0.089000    0.299000   -0.3666000
+44677.00   0.092409   0.300309  -0.3703147    0.089000    0.299000   -0.3691000
+44678.00   0.092963   0.299696  -0.3728610    0.089000    0.298000   -0.3717000
+44679.00   0.093548   0.299077  -0.3753878    0.089000    0.297000   -0.3743000
+44680.00   0.094158   0.298450  -0.3779422    0.089000    0.296000   -0.3769000
+44681.00   0.094786   0.297814  -0.3805484    0.089000    0.296000   -0.3795000
+44682.00   0.095427   0.297169  -0.3832082    0.089000    0.295000   -0.3821000
+44683.00   0.096077   0.296513  -0.3859045    0.089000    0.295000   -0.3847000
+44684.00   0.096730   0.295846  -0.3886086    0.089000    0.294000   -0.3873000
+44685.00   0.097382   0.295168  -0.3912862    0.089000    0.293000   -0.3899000
+44686.00   0.098029   0.294476  -0.3939040    0.089000    0.292000   -0.3926000
+44687.00   0.098666   0.293764  -0.3964337    0.089000    0.292000   -0.3952000
+44688.00   0.099288   0.293029  -0.3988580    0.089000    0.291000   -0.3979000
+44689.00   0.099892   0.292266  -0.4011740    0.089000    0.290000   -0.4005000
+44690.00   0.100472   0.291472  -0.4033956    0.089000    0.289000   -0.4031000
+44691.00   0.101022   0.290643  -0.4055526    0.089000    0.288000   -0.4058000
+44692.00   0.101536   0.289778  -0.4076876    0.088000    0.288000   -0.4084000
+44693.00   0.102008   0.288877  -0.4098543    0.088000    0.287000   -0.4111000
+44694.00   0.102433   0.287939  -0.4121152    0.088000    0.286000   -0.4137000
+44695.00   0.102804   0.286963  -0.4145355    0.088000    0.285000   -0.4164000
+44696.00   0.103118   0.285951  -0.4171670    0.088000    0.284000   -0.4191000
+44697.00   0.103374   0.284904  -0.4200277    0.087000    0.283000   -0.4217000
+44698.00   0.103570   0.283826  -0.4230844    0.087000    0.282000   -0.4244000
+44699.00   0.103706   0.282720  -0.4262527    0.087000    0.281000   -0.4271000
+44700.00   0.103787   0.281591  -0.4294194    0.087000    0.280000   -0.4298000
+44701.00   0.103815   0.280444  -0.4324794    0.087000    0.279000   -0.4326000
+44702.00   0.103796   0.279284  -0.4353684    0.087000    0.279000   -0.4353000
+44703.00   0.103731   0.278118  -0.4380784    0.087000    0.278000   -0.4381000
+44704.00   0.103622   0.276950  -0.4406514    0.087000    0.277000   -0.4408000
+44705.00   0.103472   0.275789  -0.4431571    0.087000    0.276000   -0.4435000
+44706.00   0.103281   0.274641  -0.4456681    0.087000    0.276000   -0.4462000
+44707.00   0.103048   0.273513  -0.4482414    0.087000    0.275000   -0.4490000
+44708.00   0.102772   0.272412  -0.4509090    0.087000    0.275000   -0.4517000
+44709.00   0.102451   0.271342  -0.4536782    0.087000    0.274000   -0.4544000
+44710.00   0.102084   0.270310  -0.4565359    0.087000    0.273000   -0.4571000
+44711.00   0.101671   0.269319  -0.4594538    0.087000    0.273000   -0.4598000
+44712.00   0.101215   0.268373  -0.4623949    0.087000    0.272000   -0.4626000
+44713.00   0.100723   0.267472  -0.4653187    0.087000    0.272000   -0.4653000
+44714.00   0.100204   0.266611  -0.4681860    0.087000    0.271000   -0.4680000
+44715.00   0.099670   0.265784  -0.4709654    0.087000    0.270000   -0.4707000
+44716.00   0.099132   0.264986  -0.4736384    0.087000    0.270000   -0.4734000
+44717.00   0.098598   0.264212  -0.4762027    0.087000    0.269000   -0.4761000
+44718.00   0.098078   0.263458  -0.4786745    0.087000    0.269000   -0.4788000
+44719.00   0.097577   0.262721  -0.4810851    0.087000    0.268000   -0.4815000
+44720.00   0.097098   0.262000  -0.4834781    0.087000    0.267000   -0.4841000
+44721.00   0.096643   0.261292  -0.4859053    0.087000    0.266000   -0.4868000
+44722.00   0.096213   0.260597  -0.4884219    0.087000    0.266000   -0.4894000
+44723.00   0.095810   0.259912  -0.4910793    0.087000    0.265000   -0.4921000
+44724.00   0.095437   0.259235  -0.4939106    0.087000    0.264000   -0.4947000
+44725.00   0.095099   0.258566  -0.4969117    0.087000    0.263000   -0.4973000
+44726.00   0.094796   0.257901  -0.5000299    0.087000    0.263000   -0.4999000
+44727.00   0.094526   0.257240  -0.5031687    0.086000    0.262000   -0.5025000
+44728.00   0.094287   0.256579  -0.5062135    0.086000    0.262000   -0.5051000
+44729.00   0.094077   0.255917  -0.5090697    0.086000    0.261000   -0.5077000
+44730.00   0.093897   0.255250  -0.5116939    0.086000    0.260000   -0.5102000
+44731.00   0.093746   0.254576  -0.5141026    0.086000    0.259000   -0.5127000
+44732.00   0.093621   0.253891  -0.5163592    0.086000    0.259000   -0.5153000
+44733.00   0.093517   0.253193  -0.5185444    0.086000    0.258000   -0.5178000
+44734.00   0.093428   0.252480  -0.5207305    0.086000    0.257000   -0.5203000
+44735.00   0.093349   0.251752  -0.5229648    0.086000    0.256000   -0.5227000
+44736.00   0.093274   0.251007  -0.5252670    0.086000    0.255000   -0.5251000
+44737.00   0.093197   0.250245  -0.5276341    0.086000    0.255000   -0.5276000
+44738.00   0.093113   0.249466  -0.5300478    0.086000    0.254000   -0.5300000
+44739.00   0.093014   0.248669  -0.5324811    0.086000    0.253000   -0.5324000
+44740.00   0.092896   0.247853  -0.5349032    0.086000    0.252000   -0.5348000
+44741.00   0.092753   0.247018  -0.5372836    0.086000    0.251000   -0.5371000
+44742.00   0.092580   0.246162  -0.5395967    0.086000    0.251000   -0.5395000
+44743.00   0.092374   0.245286  -0.5418269    0.086000    0.250000   -0.5418000
+44744.00   0.092128   0.244389  -0.5439719    0.086000    0.249000   -0.5442000
+44745.00   0.091841   0.243473  -0.5460448    0.086000    0.248000   -0.5465000
+44746.00   0.091507   0.242539  -0.5480747    0.086000    0.247000   -0.5488000
+44747.00   0.091128   0.241590  -0.5501027    0.086000    0.246000   -0.5511000
+44748.00   0.090703   0.240630  -0.5521769    0.086000    0.245000   -0.5534000
+44749.00   0.090233   0.239660  -0.5543473    0.086000    0.244000   -0.5557000
+44750.00   0.089719   0.238686  -0.5566596    0.086000    0.243000   -0.5580000
+44751.00   0.089165   0.237709  -0.5591475    0.086000    0.242000   -0.5602000
+44752.00   0.088574   0.236737  -0.5618204    0.085000    0.241000   -0.5625000
+44753.00   0.087953   0.235775  -0.5646512    0.085000    0.240000   -0.5647000
+44754.00   0.087307   0.234829  -0.5675712    0.085000    0.239000   -0.5670000
+44755.00   0.086642   0.233906  -0.5704801    0.085000    0.238000   -0.5692000
+44756.00   0.085963   0.233012  -0.5732731    0.085000    0.237000   -0.5713000
+44757.00   0.085277   0.232152  -0.5758756    0.085000    0.236000   -0.5735000
+44758.00   0.084589   0.231328  -0.5782669    0.085000    0.235000   -0.5756000
+44759.00   0.083908   0.230536  -0.5804817    0.085000    0.234000   -0.5778000
+44760.00   0.083242   0.229771  -0.5825897    0.085000    0.233000   -0.5799000
+44761.00   0.082595   0.229030  -0.5846656    0.085000    0.232000   -0.5819000
+44762.00   0.081969   0.228306  -0.5867638    0.084000    0.232000   -0.5840000
+44763.00   0.081359   0.227593  -0.5889077    0.084000    0.231000   -0.5860000
+44764.00   0.080762   0.226886  -0.5910926    0.084000    0.230000   -0.5881000
+44765.00   0.080177   0.226178  -0.5932950    0.084000    0.229000   -0.5900000
+44766.00   0.079601   0.225466  -0.5954823    0.083000    0.228000   -0.5920000
+44767.00   0.079033   0.224744  -0.5976188    0.083000    0.227000   -0.5939000
+44768.00   0.078473   0.224009  -0.5996709    0.082000    0.226000   -0.5959000
+44769.00   0.077923   0.223258  -0.6016105    0.082000    0.225000   -0.5978000
+44770.00   0.077383   0.222489  -0.6034192    0.082000    0.224000   -0.5996000
+44771.00   0.076854   0.221699  -0.6050920    0.081000    0.223000   -0.6014000
+44772.00   0.076331   0.220890  -0.6066392    0.081000    0.223000   -0.6033000
+44773.00   0.075810   0.220065  -0.6080868    0.080000    0.222000   -0.6051000
+44774.00   0.075287   0.219227  -0.6094747    0.080000    0.221000   -0.6069000
+44775.00   0.074760   0.218381  -0.6108528    0.080000    0.220000   -0.6086000
+44776.00   0.074228   0.217529  -0.6122734    0.079000    0.219000   -0.6103000
+44777.00   0.073692   0.216671  -0.6137833    0.079000    0.218000   -0.6120000
+44778.00   0.073151   0.215806  -0.6154147    0.078000    0.217000   -0.6137000
+44779.00   0.072607   0.214934  -0.6171786    0.078000    0.216000   -0.6154000
+44780.00   0.072064   0.214053  -0.6190588    0.078000    0.215000   -0.6170000
+44781.00   0.071529   0.213163  -0.6210088    0.077000    0.214000   -0.6186000
+44782.00   0.071005   0.212264  -0.6229551    0.077000    0.213000   -0.6201000
+44783.00   0.070500   0.211356  -0.6248128    0.076000    0.212000   -0.6217000
+44784.00   0.070017   0.210444  -0.6265089    0.076000    0.211000   -0.6233000
+44785.00   0.069562   0.209527  -0.6280070    0.076000    0.210000   -0.6248000
+44786.00   0.069135   0.208610   0.3706787    0.075000    0.209000    0.3738000
+44787.00   0.068727   0.207692   0.3694899    0.075000    0.208000    0.3723000
+44788.00   0.068327   0.206774   0.3683480    0.074000    0.207000    0.3709000
+44789.00   0.067935   0.205857   0.3671843    0.074000    0.206000    0.3694000
+44790.00   0.067552   0.204942   0.3659587    0.073000    0.205000    0.3680000
+44791.00   0.067176   0.204031   0.3646653    0.073000    0.204000    0.3666000
+44792.00   0.066808   0.203123   0.3633237    0.072000    0.204000    0.3651000
+44793.00   0.066442   0.202222   0.3619672    0.072000    0.203000    0.3637000
+44794.00   0.066073   0.201328   0.3606327    0.071000    0.202000    0.3623000
+44795.00   0.065696   0.200444   0.3593538    0.070000    0.201000    0.3609000
+44796.00   0.065302   0.199571   0.3581575    0.069000    0.200000    0.3596000
+44797.00   0.064885   0.198710   0.3570599    0.069000    0.200000    0.3582000
+44798.00   0.064440   0.197866   0.3560642    0.068000    0.199000    0.3569000
+44799.00   0.063959   0.197041   0.3551590    0.067000    0.198000    0.3555000
+44800.00   0.063435   0.196238   0.3543187    0.066000    0.197000    0.3542000
+44801.00   0.062865   0.195458   0.3535035    0.065000    0.196000    0.3529000
+44802.00   0.062243   0.194704   0.3526621    0.064000    0.196000    0.3515000
+44803.00   0.061567   0.193973   0.3517366    0.063000    0.195000    0.3502000
+44804.00   0.060837   0.193262   0.3506726    0.062000    0.194000    0.3489000
+44805.00   0.060054   0.192568   0.3494308    0.061000    0.193000    0.3476000
+44806.00   0.059219   0.191888   0.3479970    0.060000    0.193000    0.3462000
+44807.00   0.058334   0.191218   0.3463884    0.059000    0.192000    0.3449000
+44808.00   0.057398   0.190553   0.3446521    0.058000    0.192000    0.3435000
+44809.00   0.056412   0.189891   0.3428561    0.057000    0.191000    0.3422000
+44810.00   0.055375   0.189230   0.3410767    0.056000    0.190000    0.3408000
+44811.00   0.054287   0.188569   0.3393814    0.055000    0.190000    0.3394000
+44812.00   0.053147   0.187908   0.3378102    0.054000    0.189000    0.3380000
+44813.00   0.051952   0.187248   0.3363637    0.053000    0.189000    0.3366000
+44814.00   0.050703   0.186592   0.3350022    0.052000    0.188000    0.3352000
+44815.00   0.049399   0.185946   0.3336592    0.051000    0.187000    0.3337000
+44816.00   0.048043   0.185316   0.3322658    0.050000    0.187000    0.3323000
+44817.00   0.046639   0.184711   0.3307738    0.049000    0.186000    0.3308000
+44818.00   0.045190   0.184134   0.3291684    0.048000    0.186000    0.3294000
+44819.00   0.043700   0.183590   0.3274667    0.047000    0.185000    0.3279000
+44820.00   0.042171   0.183082   0.3257067    0.046000    0.185000    0.3264000
+44821.00   0.040604   0.182613   0.3239344    0.045000    0.184000    0.3250000
+44822.00   0.039001   0.182187   0.3221940    0.043000    0.184000    0.3235000
+44823.00   0.037364   0.181810   0.3205223    0.042000    0.183000    0.3221000
+44824.00   0.035693   0.181488   0.3189449    0.041000    0.183000    0.3206000
+44825.00   0.033992   0.181224   0.3174735    0.040000    0.183000    0.3191000
+44826.00   0.032265   0.181021   0.3161045    0.038000    0.183000    0.3176000
+44827.00   0.030516   0.180881   0.3148191    0.037000    0.182000    0.3161000
+44828.00   0.028750   0.180802   0.3135857    0.035000    0.182000    0.3146000
+44829.00   0.026974   0.180777   0.3123613    0.034000    0.182000    0.3131000
+44830.00   0.025195   0.180800   0.3110942    0.033000    0.182000    0.3116000
+44831.00   0.023416   0.180862   0.3097288    0.031000    0.182000    0.3100000
+44832.00   0.021640   0.180959   0.3082181    0.030000    0.181000    0.3085000
+44833.00   0.019869   0.181085   0.3065392    0.028000    0.181000    0.3069000
+44834.00   0.018106   0.181235   0.3047050    0.027000    0.181000    0.3054000
+44835.00   0.016361   0.181406   0.3027657    0.025000    0.181000    0.3038000
+44836.00   0.014640   0.181598   0.3007986    0.024000    0.181000    0.3022000
+44837.00   0.012948   0.181808   0.2988884    0.022000    0.181000    0.3006000
+44838.00   0.011288   0.182037   0.2971055    0.021000    0.181000    0.2990000
+44839.00   0.009661   0.182284   0.2954891    0.019000    0.181000    0.2974000
+44840.00   0.008067   0.182551   0.2940387    0.017000    0.181000    0.2958000
+44841.00   0.006502   0.182837   0.2927164    0.016000    0.181000    0.2941000
+44842.00   0.004967   0.183144   0.2914576    0.014000    0.182000    0.2925000
+44843.00   0.003459   0.183472   0.2901894    0.013000    0.182000    0.2908000
+44844.00   0.001971   0.183822   0.2888507    0.011000    0.182000    0.2892000
+44845.00   0.000498   0.184192   0.2874083    0.009000    0.182000    0.2875000
+44846.00  -0.000964   0.184580   0.2858615    0.008000    0.182000    0.2858000
+44847.00  -0.002421   0.184983   0.2842370    0.006000    0.183000    0.2841000
+44848.00  -0.003878   0.185399   0.2825765    0.005000    0.183000    0.2824000
+44849.00  -0.005341   0.185825   0.2809244    0.003000    0.183000    0.2807000
+44850.00  -0.006820   0.186262   0.2793189    0.001000    0.183000    0.2789000
+44851.00  -0.008321   0.186711   0.2777869    0.000000    0.184000    0.2771000
+44852.00  -0.009847   0.187177   0.2763411   -0.002000    0.184000    0.2754000
+44853.00  -0.011401   0.187664   0.2749774   -0.003000    0.185000    0.2736000
+44854.00  -0.012986   0.188175   0.2736749   -0.005000    0.185000    0.2718000
+44855.00  -0.014601   0.188708   0.2723988   -0.007000    0.186000    0.2699000
+44856.00  -0.016248   0.189263   0.2711036   -0.008000    0.186000    0.2681000
+44857.00  -0.017925   0.189838   0.2697354   -0.010000    0.187000    0.2662000
+44858.00  -0.019633   0.190433   0.2682354   -0.011000    0.187000    0.2644000
+44859.00  -0.021374   0.191049   0.2665468   -0.013000    0.188000    0.2625000
+44860.00  -0.023145   0.191686   0.2646284   -0.015000    0.189000    0.2606000
+44861.00  -0.024947   0.192346   0.2624718   -0.016000    0.190000    0.2586000
+44862.00  -0.026777   0.193028   0.2601132   -0.018000    0.191000    0.2567000
+44863.00  -0.028635   0.193735   0.2576309   -0.019000    0.192000    0.2547000
+44864.00  -0.030519   0.194466   0.2551270   -0.021000    0.193000    0.2528000
+44865.00  -0.032426   0.195223   0.2526969   -0.023000    0.194000    0.2508000
+44866.00  -0.034352   0.196006   0.2504021   -0.024000    0.195000    0.2488000
+44867.00  -0.036297   0.196814   0.2482567   -0.026000    0.197000    0.2467000
+44868.00  -0.038258   0.197645   0.2462289   -0.027000    0.198000    0.2447000
+44869.00  -0.040233   0.198500   0.2442564   -0.029000    0.199000    0.2427000
+44870.00  -0.042219   0.199377   0.2422674   -0.031000    0.200000    0.2406000
+44871.00  -0.044215   0.200276   0.2402008   -0.033000    0.201000    0.2385000
+44872.00  -0.046217   0.201196   0.2380198   -0.034000    0.203000    0.2365000
+44873.00  -0.048222   0.202137   0.2357184   -0.036000    0.204000    0.2344000
+44874.00  -0.050225   0.203100   0.2333195   -0.038000    0.205000    0.2323000
+44875.00  -0.052222   0.204084   0.2308657   -0.040000    0.206000    0.2301000
+44876.00  -0.054203   0.205092   0.2284081   -0.042000    0.207000    0.2279000
+44877.00  -0.056160   0.206123   0.2259958   -0.043000    0.209000    0.2258000
+44878.00  -0.058084   0.207180   0.2236689   -0.045000    0.210000    0.2236000
+44879.00  -0.059970   0.208266   0.2214526   -0.047000    0.211000    0.2214000
+44880.00  -0.061812   0.209382   0.2193548   -0.049000    0.212000    0.2192000
+44881.00  -0.063603   0.210532   0.2173644   -0.050000    0.213000    0.2169000
+44882.00  -0.065341   0.211718   0.2154516   -0.052000    0.215000    0.2147000
+44883.00  -0.067020   0.212941   0.2135733   -0.053000    0.216000    0.2124000
+44884.00  -0.068636   0.214202   0.2116786   -0.055000    0.217000    0.2102000
+44885.00  -0.070186   0.215504   0.2097112   -0.056000    0.218000    0.2079000
+44886.00  -0.071670   0.216846   0.2076132   -0.058000    0.219000    0.2056000
+44887.00  -0.073087   0.218228   0.2053325   -0.059000    0.221000    0.2032000
+44888.00  -0.074442   0.219649   0.2028375   -0.061000    0.222000    0.2009000
+44889.00  -0.075740   0.221105   0.2001341   -0.062000    0.223000    0.1986000
+44890.00  -0.076987   0.222593   0.1972769   -0.063000    0.224000    0.1962000
+44891.00  -0.078189   0.224110   0.1943629   -0.064000    0.226000    0.1939000
+44892.00  -0.079350   0.225653   0.1915051   -0.066000    0.227000    0.1915000
+44893.00  -0.080477   0.227218   0.1887956   -0.067000    0.229000    0.1892000
+44894.00  -0.081573   0.228801   0.1862774   -0.068000    0.230000    0.1868000
+44895.00  -0.082642   0.230398   0.1839350   -0.069000    0.231000    0.1844000
+44896.00  -0.083687   0.232006   0.1817077   -0.070000    0.233000    0.1820000
+44897.00  -0.084709   0.233618   0.1795157   -0.072000    0.234000    0.1797000
+44898.00  -0.085710   0.235233   0.1772865   -0.073000    0.236000    0.1773000
+44899.00  -0.086693   0.236845   0.1749720   -0.074000    0.237000    0.1749000
+44900.00  -0.087659   0.238452   0.1725556   -0.075000    0.239000    0.1725000
+44901.00  -0.088611   0.240050   0.1700496   -0.076000    0.240000    0.1700000
+44902.00  -0.089553   0.241637   0.1674868   -0.078000    0.242000    0.1676000
+44903.00  -0.090490   0.243214   0.1649119   -0.079000    0.243000    0.1651000
+44904.00  -0.091427   0.244778   0.1623716   -0.080000    0.245000    0.1627000
+44905.00  -0.092366   0.246330   0.1599077   -0.081000    0.247000    0.1603000
+44906.00  -0.093305   0.247871   0.1575515   -0.082000    0.248000    0.1579000
+44907.00  -0.094240   0.249406   0.1553197   -0.083000    0.250000    0.1554000
+44908.00  -0.095166   0.250935   0.1532109   -0.084000    0.251000    0.1530000
+44909.00  -0.096079   0.252462   0.1512050   -0.085000    0.253000    0.1506000
+44910.00  -0.096977   0.253991   0.1492653   -0.086000    0.255000    0.1482000
+44911.00  -0.097855   0.255525   0.1473445   -0.087000    0.257000    0.1459000
+44912.00  -0.098712   0.257066   0.1453902   -0.087000    0.258000    0.1435000
+44913.00  -0.099541   0.258616   0.1433488   -0.088000    0.260000    0.1412000
+44914.00  -0.100337   0.260174   0.1411696   -0.089000    0.262000    0.1388000
+44915.00  -0.101093   0.261743   0.1388120   -0.090000    0.264000    0.1365000
+44916.00  -0.101804   0.263326   0.1362590   -0.090000    0.266000    0.1342000
+44917.00  -0.102468   0.264926   0.1335327   -0.091000    0.268000    0.1319000
+44918.00  -0.103083   0.266547   0.1307020   -0.091000    0.270000    0.1296000
+44919.00  -0.103648   0.268194   0.1278706   -0.092000    0.272000    0.1273000
+44920.00  -0.104163   0.269872   0.1251477   -0.093000    0.274000    0.1250000
+44921.00  -0.104631   0.271582   0.1226108   -0.093000    0.276000    0.1228000
+44922.00  -0.105057   0.273331   0.1202790   -0.094000    0.278000    0.1205000
+44923.00  -0.105442   0.275117   0.1181126   -0.094000    0.280000    0.1183000
+44924.00  -0.105788   0.276940   0.1160345   -0.095000    0.282000    0.1160000
+44925.00  -0.106093   0.278798   0.1139627   -0.096000    0.284000    0.1138000
+44926.00  -0.106359   0.280687   0.1118375   -0.096000    0.286000    0.1115000
+44927.00  -0.106590   0.282607   0.1096341   -0.097000    0.288000    0.1093000
+44928.00  -0.106792   0.284556   0.1073603   -0.097000    0.290000    0.1070000
+44929.00  -0.106971   0.286533   0.1050456   -0.098000    0.292000    0.1048000
+44930.00  -0.107133   0.288535   0.1027286   -0.099000    0.294000    0.1026000
+44931.00  -0.107280   0.290560   0.1004497   -0.099000    0.296000    0.1004000
+44932.00  -0.107416   0.292608   0.0982459   -0.100000    0.299000    0.0982000
+44933.00  -0.107540   0.294677   0.0961454   -0.100000    0.301000    0.0960000
+44934.00  -0.107648   0.296764   0.0941645   -0.101000    0.303000    0.0938000
+44935.00  -0.107733   0.298869   0.0923040   -0.101000    0.305000    0.0916000
+44936.00  -0.107791   0.300991   0.0905479   -0.102000    0.307000    0.0894000
+44937.00  -0.107815   0.303127   0.0888633   -0.102000    0.310000    0.0872000
+44938.00  -0.107800   0.305278   0.0872040   -0.103000    0.312000    0.0850000
+44939.00  -0.107740   0.307440   0.0855165   -0.103000    0.314000    0.0828000
+44940.00  -0.107632   0.309614   0.0837467   -0.103000    0.314000    0.0806000
+44941.00  -0.107476   0.311800   0.0818456   -0.103000    0.314000    0.0785000
+44942.00  -0.107274   0.314001   0.0797734   -0.104000    0.314000    0.0763000
+44943.00  -0.107030   0.316218   0.0775061   -0.104000    0.314000    0.0742000
+44944.00  -0.106757   0.318450   0.0750458   -0.104000    0.314000    0.0720000
+44945.00  -0.106473   0.320697   0.0724298   -0.104000    0.318000    0.0699000
+44946.00  -0.106199   0.322958   0.0697333   -0.104000    0.323000    0.0677000
+44947.00  -0.105953   0.325231   0.0670535   -0.104000    0.327000    0.0656000
+44948.00  -0.105744   0.327514   0.0644823   -0.104000    0.332000    0.0634000
+44949.00  -0.105574   0.329807   0.0620725   -0.104000    0.336000    0.0613000
+44950.00  -0.105442   0.332106   0.0598174   -0.104000    0.338000    0.0592000
+44951.00  -0.105335   0.334407   0.0576587   -0.104000    0.340000    0.0570000
+44952.00  -0.105240   0.336708   0.0555139   -0.103000    0.342000    0.0549000
+44953.00  -0.105141   0.339004   0.0533108   -0.103000    0.344000    0.0527000
+44954.00  -0.105022   0.341291   0.0510120   -0.103000    0.346000    0.0506000
+44955.00  -0.104869   0.343568   0.0486201   -0.102000    0.348000    0.0485000
+44956.00  -0.104667   0.345832   0.0461677   -0.102000    0.350000    0.0463000
+44957.00  -0.104404   0.348081   0.0437014   -0.101000    0.352000    0.0442000
+44958.00  -0.104069   0.350313   0.0412683   -0.101000    0.354000    0.0420000
+44959.00  -0.103653   0.352526   0.0389087   -0.100000    0.356000    0.0399000
+44960.00  -0.103155   0.354720   0.0366522   -0.099000    0.358000    0.0378000
+44961.00  -0.102571   0.356893   0.0345166   -0.099000    0.360000    0.0357000
+44962.00  -0.101899   0.359045   0.0325064   -0.098000    0.362000    0.0335000
+44963.00  -0.101139   0.361177   0.0306103   -0.098000    0.364000    0.0314000
+44964.00  -0.100290   0.363287   0.0288017   -0.097000    0.366000    0.0293000
+44965.00  -0.099351   0.365377   0.0270397   -0.096000    0.368000    0.0271000
+44966.00  -0.098323   0.367444   0.0252743   -0.095000    0.370000    0.0249000
+44967.00  -0.097204   0.369491   0.0234518   -0.094000    0.372000    0.0228000
+44968.00  -0.095993   0.371516   0.0215243   -0.093000    0.374000    0.0206000
+44969.00  -0.094684   0.373520   0.0194563   -0.092000    0.376000    0.0184000
+44970.00  -0.093270   0.375504   0.0172296   -0.091000    0.378000    0.0163000
+44971.00  -0.091756   0.377467   0.0148474   -0.090000    0.380000    0.0141000
+44972.00  -0.090151   0.379406   0.0123377   -0.089000    0.382000    0.0120000
+44973.00  -0.088467   0.381320   0.0097557   -0.088000    0.384000    0.0098000
+44974.00  -0.086713   0.383207   0.0071781   -0.087000    0.386000    0.0077000
+44975.00  -0.084900   0.385064   0.0046864   -0.086000    0.388000    0.0052000
+44976.00  -0.083037   0.386892   0.0023421   -0.085000    0.390000    0.0028000
+44977.00  -0.081135   0.388691   0.0001620   -0.084000    0.391000    0.0003000
+44978.00  -0.079203   0.390460  -0.0018896   -0.083000    0.393000   -0.0021000
+44979.00  -0.077248   0.392198  -0.0038869   -0.082000    0.395000   -0.0046000
+44980.00  -0.075276   0.393905  -0.0059126   -0.081000    0.396000   -0.0071000
+44981.00  -0.073292   0.395579  -0.0080250   -0.080000    0.398000   -0.0096000
+44982.00  -0.071301   0.397219  -0.0102397   -0.078000    0.399000   -0.0121000
+44983.00  -0.069308   0.398823  -0.0125318   -0.077000    0.401000   -0.0146000
+44984.00  -0.067313   0.400393  -0.0148514   -0.076000    0.402000   -0.0171000
+44985.00  -0.065316   0.401928  -0.0171431   -0.074000    0.403000   -0.0192000
+44986.00  -0.063321   0.403428  -0.0193587   -0.073000    0.404000   -0.0213000
+44987.00  -0.061331   0.404895  -0.0214634   -0.071000    0.406000   -0.0235000
+44988.00  -0.059352   0.406328  -0.0234369   -0.070000    0.407000   -0.0256000
+44989.00  -0.057388   0.407730  -0.0252742   -0.068000    0.408000   -0.0277000
+44990.00  -0.055443   0.409100  -0.0269851   -0.066000    0.409000   -0.0289000
+44991.00  -0.053521   0.410441  -0.0285942   -0.064000    0.410000   -0.0301000
+44992.00  -0.051625   0.411754  -0.0301393   -0.062000    0.412000   -0.0313000
+44993.00  -0.049757   0.413040  -0.0316687   -0.060000    0.413000   -0.0325000
+44994.00  -0.047923   0.414302  -0.0332374   -0.058000    0.414000   -0.0337000
+44995.00  -0.046124   0.415539  -0.0348993   -0.056000    0.415000   -0.0357000
+44996.00  -0.044360   0.416754  -0.0366974   -0.054000    0.416000   -0.0378000
+44997.00  -0.042622   0.417944  -0.0386546   -0.051000    0.417000   -0.0398000
+44998.00  -0.040900   0.419110  -0.0407674   -0.049000    0.418000   -0.0419000
+44999.00  -0.039185   0.420248  -0.0430055   -0.047000    0.419000   -0.0439000
+45000.00  -0.037469   0.421359  -0.0453154   -0.045000    0.420000   -0.0462000
+45001.00  -0.035743   0.422440  -0.0476297   -0.043000    0.421000   -0.0485000
+45002.00  -0.033998   0.423491  -0.0498798   -0.040000    0.422000   -0.0507000
+45003.00  -0.032228   0.424510  -0.0520130   -0.038000    0.423000   -0.0530000
+45004.00  -0.030426   0.425498  -0.0540096   -0.036000    0.424000   -0.0553000
+45005.00  -0.028587   0.426453  -0.0558938   -0.034000    0.425000   -0.0574000
+45006.00  -0.026708   0.427377  -0.0577298   -0.032000    0.426000   -0.0596000
+45007.00  -0.024782   0.428269  -0.0596014   -0.029000    0.426000   -0.0617000
+45008.00  -0.022809   0.429130  -0.0615825   -0.027000    0.427000   -0.0639000
+45009.00  -0.020783   0.429960  -0.0637115   -0.025000    0.428000   -0.0660000
+45010.00  -0.018701   0.430758  -0.0659818   -0.023000    0.429000   -0.0686000
+45011.00  -0.016564   0.431527  -0.0683497   -0.021000    0.430000   -0.0711000
+45012.00  -0.014371   0.432269  -0.0707537   -0.018000    0.430000   -0.0737000
+45013.00  -0.012124   0.432987  -0.0731338   -0.016000    0.431000   -0.0762000
+45014.00  -0.009825   0.433684  -0.0754433   -0.014000    0.432000   -0.0788000
+45015.00  -0.007476   0.434361  -0.0776534   -0.012000    0.433000   -0.0809000
+45016.00  -0.005079   0.435019  -0.0797529   -0.010000    0.433000   -0.0830000
+45017.00  -0.002636   0.435659  -0.0817487   -0.007000    0.434000   -0.0852000
+45018.00  -0.000144   0.436282  -0.0836642   -0.005000    0.434000   -0.0873000
+45019.00   0.002399   0.436885  -0.0855371   -0.003000    0.435000   -0.0894000
+45020.00   0.004994   0.437468  -0.0874157   -0.001000    0.435000   -0.0915000
+45021.00   0.007642   0.438026  -0.0893546    0.001000    0.436000   -0.0935000
+45022.00   0.010346   0.438557  -0.0914086    0.004000    0.436000   -0.0956000
+45023.00   0.013106   0.439060  -0.0936249    0.006000    0.437000   -0.0976000
+45024.00   0.015915   0.439529  -0.0960319    0.008000    0.437000   -0.0997000
+45025.00   0.018763   0.439959  -0.0986291    0.010000    0.437000   -0.1023000
+45026.00   0.021638   0.440346  -0.1013812    0.012000    0.438000   -0.1049000
+45027.00   0.024531   0.440682  -0.1042217    0.015000    0.438000   -0.1075000
+45028.00   0.027431   0.440966  -0.1070648    0.017000    0.439000   -0.1101000
+45029.00   0.030329   0.441192  -0.1098255    0.019000    0.439000   -0.1127000
+45030.00   0.033215   0.441358  -0.1124409    0.021000    0.439000   -0.1149000
+45031.00   0.036080   0.441461  -0.1148853    0.023000    0.439000   -0.1170000
+45032.00   0.038915   0.441502  -0.1171751    0.026000    0.440000   -0.1192000
+45033.00   0.041713   0.441483  -0.1193640    0.028000    0.440000   -0.1213000
+45034.00   0.044468   0.441406  -0.1215290    0.030000    0.440000   -0.1235000
+45035.00   0.047179   0.441276  -0.1237490    0.024000    0.446000   -0.1259000
+45036.00   0.049845   0.441095  -0.1260787    0.026000    0.445000   -0.1282000
+45037.00   0.052468   0.440866  -0.1285333    0.028000    0.445000   -0.1306000
+45038.00   0.055050   0.440593  -0.1310860    0.030000    0.445000   -0.1329000
+45039.00   0.057596   0.440277  -0.1336814    0.042000    0.440000   -0.1353000
+45040.00   0.060110   0.439922  -0.1362541    0.045000    0.440000   -0.1376000
+45041.00   0.062597   0.439531  -0.1387466    0.047000    0.440000   -0.1399000
+45042.00   0.065059   0.439108  -0.1411186    0.050000    0.439000   -0.1423000
+45043.00   0.067495   0.438656  -0.1433509    0.052000    0.439000   -0.1446000
+45044.00   0.069900   0.438180  -0.1454461    0.055000    0.439000   -0.1469000
+45045.00   0.072266   0.437682  -0.1474266    0.057000    0.439000   -0.1489000
+45046.00   0.074588   0.437168  -0.1493315    0.060000    0.438000   -0.1509000
+45047.00   0.076865   0.436637  -0.1512116    0.062000    0.438000   -0.1529000
+45048.00   0.079099   0.436091  -0.1531232    0.065000    0.437000   -0.1549000
+45049.00   0.081299   0.435531  -0.1551240    0.067000    0.437000   -0.1569000
+45050.00   0.083474   0.434956  -0.1572687    0.069000    0.437000   -0.1595000
+45051.00   0.085634   0.434365  -0.1596019    0.072000    0.436000   -0.1621000
+45052.00   0.087787   0.433758  -0.1621463    0.074000    0.436000   -0.1646000
+45053.00   0.089939   0.433136  -0.1648898    0.077000    0.435000   -0.1672000
+45054.00   0.092088   0.432499  -0.1677794    0.079000    0.435000   -0.1698000
+45055.00   0.094233   0.431849  -0.1707293    0.081000    0.434000   -0.1725000
+45056.00   0.096372   0.431186  -0.1736428    0.084000    0.434000   -0.1752000
+45057.00   0.098502   0.430514  -0.1764405    0.086000    0.433000   -0.1778000
+45058.00   0.100624   0.429830  -0.1790828    0.089000    0.433000   -0.1805000
+45059.00   0.102737   0.429134  -0.1815781    0.091000    0.432000   -0.1832000
+45060.00   0.104842   0.428425  -0.1839775    0.093000    0.431000   -0.1857000
+45061.00   0.106941   0.427702  -0.1863567    0.096000    0.430000   -0.1881000
+45062.00   0.109031   0.426961  -0.1887936    0.098000    0.430000   -0.1906000
+45063.00   0.111112   0.426199  -0.1913466    0.101000    0.429000   -0.1930000
+45064.00   0.113181   0.425415  -0.1940409    0.103000    0.428000   -0.1955000
+45065.00   0.115237   0.424605  -0.1968630    0.105000    0.427000   -0.1981000
+45066.00   0.117279   0.423766  -0.1997669    0.107000    0.426000   -0.2009000
+45067.00   0.119306   0.422896  -0.2026883    0.110000    0.426000   -0.2038000
+45068.00   0.121322   0.421994  -0.2055617    0.112000    0.425000   -0.2066000
+45069.00   0.123331   0.421059  -0.2083333    0.114000    0.424000   -0.2093000
+45070.00   0.125337   0.420090  -0.2109687    0.116000    0.423000   -0.2118000
+45071.00   0.127343   0.419085  -0.2134548    0.118000    0.422000   -0.2143000
+45072.00   0.129354   0.418044  -0.2158001    0.121000    0.422000   -0.2165000
+45073.00   0.131372   0.416966  -0.2180335    0.123000    0.421000   -0.2187000
+45074.00   0.133400   0.415850  -0.2202005    0.125000    0.420000   -0.2209000
+45075.00   0.135437   0.414696  -0.2223560    0.127000    0.419000   -0.2230000
+45076.00   0.137486   0.413503  -0.2245576    0.130000    0.418000   -0.2252000
+45077.00   0.139544   0.412274  -0.2268617    0.132000    0.416000   -0.2274000
+45078.00   0.141609   0.411007  -0.2293184    0.135000    0.415000   -0.2299000
+45079.00   0.143679   0.409704  -0.2319647    0.137000    0.414000   -0.2325000
+45080.00   0.145750   0.408365  -0.2348120    0.139000    0.413000   -0.2353000
+45081.00   0.147821   0.406989  -0.2378327    0.141000    0.411000   -0.2383000
+45082.00   0.149889   0.405577  -0.2409566    0.143000    0.410000   -0.2415000
+45083.00   0.151952   0.404127  -0.2440824    0.145000    0.408000   -0.2446000
+45084.00   0.154008   0.402641  -0.2471068    0.147000    0.407000   -0.2476000
+45085.00   0.156054   0.401118  -0.2499578    0.149000    0.406000   -0.2504000
+45086.00   0.158087   0.399556  -0.2526180    0.151000    0.404000   -0.2531000
+45087.00   0.160102   0.397954  -0.2551261    0.153000    0.403000   -0.2556000
+45088.00   0.162096   0.396311  -0.2575582    0.155000    0.401000   -0.2580000
+45089.00   0.164065   0.394625  -0.2599978    0.157000    0.400000   -0.2604000
+45090.00   0.166004   0.392896  -0.2625100    0.159000    0.398000   -0.2628000
+45091.00   0.167910   0.391120  -0.2651262    0.161000    0.397000   -0.2653000
+45092.00   0.169780   0.389297  -0.2678408    0.163000    0.395000   -0.2680000
+45093.00   0.171613   0.387425  -0.2706173    0.165000    0.394000   -0.2706000
+45094.00   0.173407   0.385501  -0.2733995    0.167000    0.392000   -0.2733000
+45095.00   0.175159   0.383526  -0.2761253    0.169000    0.390000   -0.2759000
+45096.00   0.176870   0.381499  -0.2787385    0.171000    0.388000   -0.2784000
+45097.00   0.178538   0.379421  -0.2811970    0.173000    0.387000   -0.2807000
+45098.00   0.180164   0.377297  -0.2834795    0.175000    0.385000   -0.2829000
+45099.00   0.181745   0.375131  -0.2855857    0.177000    0.383000   -0.2849000
+45100.00   0.183283   0.372928  -0.2875363    0.179000    0.381000   -0.2868000
+45101.00   0.184779   0.370692  -0.2893706    0.181000    0.379000   -0.2885000
+45102.00   0.186242   0.368423  -0.2911426    0.183000    0.377000   -0.2901000
+45103.00   0.187678   0.366121  -0.2929113    0.185000    0.375000   -0.2918000
+45104.00   0.189092   0.363786  -0.2947347    0.187000    0.373000   -0.2936000
+45105.00   0.190487   0.361416  -0.2966654    0.189000    0.371000   -0.2954000
+45106.00   0.191868   0.359012  -0.2987462    0.191000    0.369000   -0.2975000
+45107.00   0.193238   0.356574  -0.3010038    0.192000    0.366000   -0.2997000
+45108.00   0.194605   0.354105  -0.3034383    0.194000    0.364000   -0.3021000
+45109.00   0.195973   0.351604  -0.3060123    0.196000    0.362000   -0.3046000
+45110.00   0.197343   0.349077  -0.3086493    0.198000    0.360000   -0.3072000
+45111.00   0.198718   0.346526  -0.3112500    0.200000    0.357000   -0.3098000
+45112.00   0.200099   0.343956  -0.3137251    0.201000    0.355000   -0.3121000
+45113.00   0.201486   0.341371  -0.3160281    0.203000    0.352000   -0.3144000
+45114.00   0.202880   0.338771  -0.3181723    0.205000    0.350000   -0.3164000
+45115.00   0.204280   0.336160  -0.3202241    0.206000    0.348000   -0.3184000
+45116.00   0.205687   0.333541  -0.3222740    0.208000    0.345000   -0.3204000
+45117.00   0.207099   0.330914  -0.3244012    0.209000    0.343000   -0.3225000
+45118.00   0.208516   0.328282  -0.3266486    0.211000    0.340000   -0.3247000
+45119.00   0.209939   0.325647  -0.3290153    0.212000    0.338000   -0.3270000
+45120.00   0.211368   0.323011  -0.3314653    0.213000    0.335000   -0.3294000
+45121.00   0.212803   0.320375  -0.3339430    0.214000    0.332000   -0.3318000
+45122.00   0.214243   0.317740  -0.3363887    0.216000    0.330000   -0.3342000
+45123.00   0.215683   0.315108  -0.3387481    0.217000    0.327000   -0.3365000
+45124.00   0.217119   0.312481  -0.3409797    0.218000    0.324000   -0.3387000
+45125.00   0.218546   0.309859  -0.3430580    0.219000    0.321000   -0.3408000
+45126.00   0.219954   0.307245  -0.3449760    0.220000    0.318000   -0.3427000
+45127.00   0.221334   0.304638  -0.3467460    0.221000    0.315000   -0.3445000
+45128.00   0.222672   0.302037  -0.3483989    0.222000    0.312000   -0.3461000
+45129.00   0.223952   0.299440  -0.3499803    0.223000    0.309000   -0.3477000
+45130.00   0.225160   0.296844  -0.3515439    0.224000    0.306000   -0.3492000
+45131.00   0.226281   0.294244  -0.3531437    0.225000    0.303000   -0.3507000
+45132.00   0.227300   0.291637  -0.3548271    0.225000    0.300000   -0.3523000
+45133.00   0.228203   0.289017  -0.3566297    0.226000    0.297000   -0.3541000
+45134.00   0.228977   0.286377  -0.3585730    0.227000    0.294000   -0.3559000
+45135.00   0.229609   0.283712  -0.3606600    0.227000    0.291000   -0.3579000
+45136.00   0.230087   0.281016  -0.3628686    0.228000    0.288000   -0.3600000
+45137.00   0.230396   0.278281  -0.3651449    0.228000    0.284000   -0.3620000
+45138.00   0.230535   0.275502  -0.3674097    0.229000    0.281000   -0.3642000
+45139.00   0.230552   0.272663  -0.3695846    0.229000    0.278000   -0.3663000
+45140.00   0.230505   0.269756  -0.3716098    0.229000    0.275000   -0.3682000
+45141.00   0.230433   0.266803  -0.3734581    0.230000    0.272000   -0.3699000
+45142.00   0.230327   0.263830  -0.3751547    0.230000    0.268000   -0.3716000
+45143.00   0.230183   0.260847  -0.3767764    0.231000    0.265000   -0.3732000
+45144.00   0.230002   0.257860  -0.3784170    0.231000    0.262000   -0.3748000
+45145.00   0.229786   0.254873  -0.3801438    0.231000    0.259000   -0.3766000
+45146.00   0.229539   0.251890  -0.3819751    0.231000    0.255000   -0.3784000
+45147.00   0.229263   0.248915  -0.3838827    0.231000    0.252000   -0.3804000
+45148.00   0.228962   0.245950  -0.3858102    0.231000    0.248000   -0.3824000
+45149.00   0.228635   0.242994  -0.3876947    0.231000    0.245000   -0.3843000
+45150.00   0.228282   0.240048  -0.3894825    0.231000    0.242000   -0.3860000
+45151.00   0.227903   0.237109   0.6088635    0.231000    0.239000    0.6123000
+45152.00   0.227500   0.234178   0.6073635    0.230000    0.235000    0.6110000
+45153.00   0.227071   0.231255   0.6060205    0.230000    0.232000    0.6097000
+45154.00   0.226617   0.228339   0.6048210    0.230000    0.229000    0.6085000
+45155.00   0.226138   0.225430   0.6037356    0.230000    0.226000    0.6074000
+45156.00   0.225636   0.222527   0.6027209    0.229000    0.223000    0.6063000
+45157.00   0.225110   0.219631   0.6017239    0.229000    0.220000    0.6053000
+45158.00   0.224558   0.216742   0.6006889    0.228000    0.217000    0.6042000
+45159.00   0.223980   0.213861   0.5995655    0.228000    0.214000    0.6031000
+45160.00   0.223370   0.210992   0.5983163    0.227000    0.211000    0.6018000
+45161.00   0.222726   0.208136   0.5969212    0.227000    0.208000    0.6003000
+45162.00   0.222040   0.205296   0.5953796    0.226000    0.206000    0.5988000
+45163.00   0.221308   0.202475   0.5937112    0.226000    0.203000    0.5971000
+45164.00   0.220525   0.199677   0.5919581    0.225000    0.200000    0.5953000
+45165.00   0.219686   0.196902   0.5901832    0.224000    0.197000    0.5934000
+45166.00   0.218788   0.194154   0.5884624    0.223000    0.194000    0.5917000
+45167.00   0.217832   0.191432   0.5868650    0.223000    0.192000    0.5900000
+45168.00   0.216819   0.188738   0.5854280    0.222000    0.189000    0.5885000
+45169.00   0.215754   0.186071   0.5841359    0.221000    0.186000    0.5872000
+45170.00   0.214638   0.183431   0.5829209    0.220000    0.183000    0.5859000
+45171.00   0.213479   0.180819   0.5816866    0.219000    0.181000    0.5846000
+45172.00   0.212281   0.178235   0.5803469    0.219000    0.178000    0.5833000
+45173.00   0.211046   0.175677   0.5788593    0.218000    0.176000    0.5817000
+45174.00   0.209779   0.173145   0.5772372    0.217000    0.173000    0.5801000
+45175.00   0.208482   0.170639   0.5755388    0.216000    0.170000    0.5783000
+45176.00   0.207157   0.168158   0.5738419    0.215000    0.168000    0.5765000
+45177.00   0.205807   0.165701   0.5722188    0.213000    0.165000    0.5749000
+45178.00   0.204434   0.163268   0.5707209    0.212000    0.163000    0.5733000
+45179.00   0.203041   0.160858   0.5693752    0.211000    0.160000    0.5719000
+45180.00   0.201628   0.158467   0.5681868    0.210000    0.157000    0.5706000
+45181.00   0.200198   0.156095   0.5671419    0.209000    0.155000    0.5695000
+45182.00   0.198750   0.153738   0.5662108    0.207000    0.152000    0.5685000
+45183.00   0.197283   0.151396   0.5653507    0.206000    0.150000    0.5675000
+45184.00   0.195796   0.149065   0.5645097    0.205000    0.147000    0.5666000
+45185.00   0.194287   0.146746   0.5636315    0.203000    0.145000    0.5656000
+45186.00   0.192753   0.144436   0.5626608    0.202000    0.142000    0.5646000
+45187.00   0.191188   0.142138   0.5615525    0.200000    0.140000    0.5634000
+45188.00   0.189587   0.139850   0.5602790    0.199000    0.137000    0.5620000
+45189.00   0.187942   0.137574   0.5588353    0.197000    0.135000    0.5605000
+45190.00   0.186248   0.135311   0.5572413    0.195000    0.133000    0.5589000
+45191.00   0.184499   0.133061   0.5555391    0.193000    0.130000    0.5572000
+45192.00   0.182691   0.130826   0.5537867    0.191000    0.128000    0.5556000
+45193.00   0.180820   0.128606   0.5520488    0.189000    0.125000    0.5538000
+45194.00   0.178885   0.126404   0.5503848    0.187000    0.123000    0.5522000
+45195.00   0.176885   0.124221   0.5488306    0.185000    0.121000    0.5506000
+45196.00   0.174821   0.122061   0.5473828    0.182000    0.119000    0.5492000
+45197.00   0.172698   0.119925   0.5459912    0.180000    0.116000    0.5479000
+45198.00   0.170519   0.117816   0.5445700    0.177000    0.114000    0.5464000
+45199.00   0.168290   0.115737   0.5430252    0.175000    0.112000    0.5448000
+45200.00   0.166012   0.113689   0.5412908    0.172000    0.110000    0.5430000
+45201.00   0.163690   0.111677   0.5393529    0.169000    0.108000    0.5409000
+45202.00   0.161328   0.109701   0.5372528    0.167000    0.106000    0.5386000
+45203.00   0.158931   0.107765   0.5350698    0.164000    0.104000    0.5363000
+45204.00   0.156504   0.105868   0.5328919    0.161000    0.102000    0.5340000
+45205.00   0.154050   0.104013   0.5307914    0.158000    0.100000    0.5318000
+45206.00   0.151574   0.102200   0.5288123    0.155000    0.098000    0.5297000
+45207.00   0.149075   0.100430   0.5269696    0.153000    0.097000    0.5277000
+45208.00   0.146556   0.098701   0.5252541    0.150000    0.095000    0.5259000
+45209.00   0.144019   0.097016   0.5236373    0.147000    0.093000    0.5243000
+45210.00   0.141467   0.095372   0.5220765    0.144000    0.091000    0.5228000
+45211.00   0.138905   0.093769   0.5205198    0.141000    0.090000    0.5213000
+45212.00   0.136334   0.092209   0.5189112    0.139000    0.088000    0.5198000
+45213.00   0.133756   0.090691   0.5171961    0.136000    0.087000    0.5182000
+45214.00   0.131172   0.089216   0.5153275    0.133000    0.085000    0.5165000
+45215.00   0.128584   0.087785   0.5132734    0.130000    0.083000    0.5145000
+45216.00   0.125988   0.086396   0.5110245    0.127000    0.082000    0.5124000
+45217.00   0.123382   0.085050   0.5086007    0.124000    0.080000    0.5101000
+45218.00   0.120758   0.083746   0.5060521    0.121000    0.079000    0.5077000
+45219.00   0.118109   0.082484   0.5034506    0.118000    0.077000    0.5052000
+45220.00   0.115426   0.081262   0.5008748    0.115000    0.076000    0.5027000
+45221.00   0.112698   0.080080   0.4983934    0.112000    0.074000    0.5003000
+45222.00   0.109909   0.078941   0.4960501    0.108000    0.073000    0.4980000
+45223.00   0.107038   0.077844   0.4938502    0.105000    0.071000    0.4959000
+45224.00   0.104060   0.076792   0.4917594    0.102000    0.070000    0.4939000
+45225.00   0.100941   0.075789   0.4897097    0.099000    0.069000    0.4919000
+45226.00   0.097653   0.074836   0.4876187    0.095000    0.068000    0.4899000
+45227.00   0.094171   0.073937   0.4854146    0.092000    0.067000    0.4877000
+45228.00   0.090519   0.073093   0.4830529    0.088000    0.066000    0.4854000
+45229.00   0.086735   0.072309   0.4805389    0.085000    0.065000    0.4828000
+45230.00   0.082848   0.071586   0.4779277    0.082000    0.064000    0.4802000
+45231.00   0.078886   0.070926   0.4753023    0.078000    0.064000    0.4775000
+45232.00   0.074873   0.070329   0.4727448    0.075000    0.063000    0.4750000
+45233.00   0.070826   0.069797   0.4703178    0.071000    0.063000    0.4725000
+45234.00   0.066765   0.069330   0.4680526    0.068000    0.062000    0.4702000
+45235.00   0.062704   0.068926   0.4659500    0.064000    0.062000    0.4680000
+45236.00   0.058656   0.068587   0.4639867    0.061000    0.062000    0.4660000
+45237.00   0.054630   0.068310   0.4621210    0.057000    0.061000    0.4640000
+45238.00   0.050631   0.068095   0.4603001    0.054000    0.061000    0.4622000
+45239.00   0.046665   0.067940   0.4584668    0.050000    0.061000    0.4602000
+45240.00   0.042735   0.067845   0.4565644    0.046000    0.061000    0.4583000
+45241.00   0.038844   0.067806   0.4545431    0.043000    0.061000    0.4562000
+45242.00   0.034995   0.067825   0.4523646    0.039000    0.060000    0.4539000
+45243.00   0.031186   0.067899   0.4500084    0.036000    0.060000    0.4514000
+45244.00   0.027414   0.068028   0.4474796    0.032000    0.060000    0.4488000
+45245.00   0.023680   0.068212   0.4448154    0.028000    0.060000    0.4461000
+45246.00   0.019981   0.068449   0.4420846    0.025000    0.060000    0.4433000
+45247.00   0.016317   0.068737   0.4393739    0.021000    0.061000    0.4406000
+45248.00   0.012688   0.069075   0.4367670    0.018000    0.061000    0.4379000
+45249.00   0.009093   0.069459   0.4343200    0.014000    0.061000    0.4354000
+45250.00   0.005535   0.069887   0.4320443    0.010000    0.062000    0.4331000
+45251.00   0.002014   0.070356   0.4299041    0.007000    0.062000    0.4310000
+45252.00  -0.001469   0.070862   0.4278270    0.003000    0.063000    0.4288000
+45253.00  -0.004914   0.071405   0.4257247    0.000000    0.063000    0.4267000
+45254.00  -0.008321   0.071980   0.4235175   -0.004000    0.064000    0.4245000
+45255.00  -0.011694   0.072585   0.4211563   -0.008000    0.065000    0.4221000
+45256.00  -0.015035   0.073219   0.4186360   -0.011000    0.066000    0.4196000
+45257.00  -0.018348   0.073880   0.4159972   -0.015000    0.067000    0.4169000
+45258.00  -0.021639   0.074564   0.4133142   -0.018000    0.068000    0.4142000
+45259.00  -0.024914   0.075271   0.4106733   -0.022000    0.069000    0.4115000
+45260.00  -0.028185   0.076001   0.4081512   -0.025000    0.070000    0.4089000
+45261.00  -0.031465   0.076753   0.4057997   -0.029000    0.072000    0.4065000
+45262.00  -0.034762   0.077527   0.4036387   -0.032000    0.073000    0.4042000
+45263.00  -0.038074   0.078327   0.4016561   -0.036000    0.075000    0.4022000
+45264.00  -0.041396   0.079157   0.3998154   -0.039000    0.076000    0.4002000
+45265.00  -0.044721   0.080019   0.3980639   -0.042000    0.078000    0.3984000
+45266.00  -0.048047   0.080921   0.3963395   -0.046000    0.079000    0.3965000
+45267.00  -0.051372   0.081867   0.3945793   -0.049000    0.081000    0.3947000
+45268.00  -0.054691   0.082863   0.3927267   -0.053000    0.082000    0.3927000
+45269.00  -0.058002   0.083912   0.3907363   -0.056000    0.084000    0.3906000
+45270.00  -0.061300   0.085016   0.3885770   -0.059000    0.086000    0.3884000
+45271.00  -0.064579   0.086176   0.3862366   -0.063000    0.088000    0.3860000
+45272.00  -0.067838   0.087394   0.3837292   -0.066000    0.090000    0.3834000
+45273.00  -0.071075   0.088669   0.3811019   -0.070000    0.092000    0.3807000
+45274.00  -0.074288   0.090002   0.3784328   -0.073000    0.094000    0.3780000
+45275.00  -0.077476   0.091392   0.3758140   -0.076000    0.096000    0.3754000
+45276.00  -0.080641   0.092837   0.3733233   -0.080000    0.097000    0.3729000
+45277.00  -0.083781   0.094337   0.3709964   -0.083000    0.099000    0.3706000
+45278.00  -0.086897   0.095891   0.3688131   -0.087000    0.100000    0.3685000
+45279.00  -0.089988   0.097496   0.3667045   -0.090000    0.102000    0.3663000
+45280.00  -0.093053   0.099153   0.3645773   -0.093000    0.104000    0.3642000
+45281.00  -0.096090   0.100860   0.3623449   -0.097000    0.105000    0.3619000
+45282.00  -0.099097   0.102619   0.3599507   -0.100000    0.107000    0.3594000
+45283.00  -0.102073   0.104429   0.3573813   -0.104000    0.108000    0.3568000
+45284.00  -0.105017   0.106290   0.3546666   -0.107000    0.110000    0.3541000
+45285.00  -0.107930   0.108203   0.3518680   -0.110000    0.112000    0.3513000
+45286.00  -0.110814   0.110166   0.3490618   -0.114000    0.114000    0.3485000
+45287.00  -0.113671   0.112177   0.3463220   -0.117000    0.116000    0.3457000
+45288.00  -0.116507   0.114233   0.3437057   -0.121000    0.118000    0.3431000
+45289.00  -0.119327   0.116329   0.3412446   -0.124000    0.120000    0.3407000
+45290.00  -0.122137   0.118463   0.3389427   -0.127000    0.122000    0.3384000
+45291.00  -0.124937   0.120632   0.3367799   -0.130000    0.124000    0.3362000
+45292.00  -0.127729   0.122834   0.3347163   -0.134000    0.126000    0.3342000
+45293.00  -0.130510   0.125067   0.3326984   -0.137000    0.128000    0.3322000
+45294.00  -0.133273   0.127330   0.3306678   -0.140000    0.130000    0.3302000
+45295.00  -0.136016   0.129623   0.3285691   -0.143000    0.132000    0.3281000
+45296.00  -0.138730   0.131948   0.3263564   -0.146000    0.135000    0.3259000
+45297.00  -0.141410   0.134311   0.3239977   -0.150000    0.137000    0.3236000
+45298.00  -0.144048   0.136713   0.3214765   -0.153000    0.140000    0.3211000
+45299.00  -0.146643   0.139159   0.3187951   -0.156000    0.142000    0.3184000
+45300.00  -0.149194   0.141652   0.3159804   -0.159000    0.144000    0.3156000
+45301.00  -0.151702   0.144196   0.3130895   -0.162000    0.147000    0.3128000
+45302.00  -0.154167   0.146794   0.3102055   -0.165000    0.149000    0.3099000
+45303.00  -0.156589   0.149450   0.3074182   -0.168000    0.152000    0.3071000
+45304.00  -0.158966   0.152166   0.3047930   -0.171000    0.154000    0.3045000
+45305.00  -0.161296   0.154943   0.3023427   -0.173000    0.157000    0.3021000
+45306.00  -0.163575   0.157781   0.3000190   -0.175000    0.159000    0.2997000
+45307.00  -0.165800   0.160681   0.2977304   -0.177000    0.162000    0.2974000
+45308.00  -0.167968   0.163642   0.2953770   -0.179000    0.164000    0.2950000
+45309.00  -0.170080   0.166663   0.2928865   -0.181000    0.167000    0.2925000
+45310.00  -0.172133   0.169742   0.2902369   -0.183000    0.170000    0.2899000
+45311.00  -0.174126   0.172880   0.2874545   -0.185000    0.173000    0.2872000
+45312.00  -0.176059   0.176075   0.2845995   -0.186000    0.175000    0.2844000
+45313.00  -0.177931   0.179324   0.2817447   -0.188000    0.178000    0.2816000
+45314.00  -0.179743   0.182626   0.2789577   -0.190000    0.181000    0.2789000
+45315.00  -0.181493   0.185976   0.2762903   -0.191000    0.184000    0.2764000
+45316.00  -0.183181   0.189370   0.2737720   -0.193000    0.187000    0.2740000
+45317.00  -0.184801   0.192806   0.2714093   -0.194000    0.190000    0.2719000
+45318.00  -0.186349   0.196277   0.2691863   -0.196000    0.193000    0.2698000
+45319.00  -0.187819   0.199780   0.2670684   -0.197000    0.196000    0.2678000
+45320.00  -0.189206   0.203308   0.2650073   -0.198000    0.199000    0.2658000
+45321.00  -0.190511   0.206851   0.2629497   -0.199000    0.202000    0.2638000
+45322.00  -0.191733   0.210394   0.2608427   -0.201000    0.205000    0.2617000
+45323.00  -0.192882   0.213927   0.2586404   -0.202000    0.208000    0.2596000
+45324.00  -0.193966   0.217450   0.2563087   -0.203000    0.211000    0.2572000
+45325.00  -0.194995   0.220960   0.2538298   -0.204000    0.214000    0.2547000
+45326.00  -0.195974   0.224458   0.2512028   -0.205000    0.218000    0.2521000
+45327.00  -0.196909   0.227945   0.2484449   -0.206000    0.221000    0.2493000
+45328.00  -0.197805   0.231421   0.2455952   -0.207000    0.225000    0.2464000
+45329.00  -0.198664   0.234887   0.2427162   -0.208000    0.228000    0.2434000
+45330.00  -0.199490   0.238344   0.2398866   -0.209000    0.231000    0.2404000
+45331.00  -0.200286   0.241793   0.2371797   -0.209000    0.235000    0.2376000
+45332.00  -0.201053   0.245236   0.2346342   -0.210000    0.238000    0.2347000
+45333.00  -0.201797   0.248673   0.2322319   -0.210000    0.242000    0.2321000
+45334.00  -0.202517   0.252106   0.2298981   -0.211000    0.245000    0.2295000
+45335.00  -0.203212   0.255534   0.2275282   -0.211000    0.249000    0.2269000
+45336.00  -0.203881   0.258959   0.2250294   -0.212000    0.252000    0.2242000
+45337.00  -0.204519   0.262381   0.2223567   -0.212000    0.256000    0.2222000
+45338.00  -0.205125   0.265800   0.2195252   -0.213000    0.259000    0.2194000
+45339.00  -0.205695   0.269217   0.2165984   -0.213000    0.263000    0.2150000
+45340.00  -0.206227   0.272632   0.2136601   -0.213000    0.267000    0.2120000
+45341.00  -0.206714   0.276045   0.2107876   -0.213000    0.271000    0.2092000
+45342.00  -0.207150   0.279453   0.2080355   -0.212000    0.275000    0.2068000
+45343.00  -0.207528   0.282860   0.2054302   -0.212000    0.279000    0.2044000
+45344.00  -0.207840   0.286269   0.2029718   -0.212000    0.283000    0.2021000
+45345.00  -0.208081   0.289686   0.2006395   -0.212000    0.287000    0.2000000
+45346.00  -0.208244   0.293116   0.1983961   -0.212000    0.291000    0.1980000
+45347.00  -0.208322   0.296563   0.1961929   -0.211000    0.295000    0.1962000
+45348.00  -0.208311   0.300030   0.1939732   -0.211000    0.299000    0.1943000
+45349.00  -0.208208   0.303518   0.1916788   -0.211000    0.303000    0.1923000
+45350.00  -0.208010   0.307029   0.1892581   -0.210000    0.307000    0.1901000
+45351.00  -0.207718   0.310562   0.1866722   -0.210000    0.311000    0.1879000
+45352.00  -0.207328   0.314118   0.1839011   -0.209000    0.315000    0.1855000
+45353.00  -0.206842   0.317697   0.1809453   -0.209000    0.319000    0.1829000
+45354.00  -0.206262   0.321296   0.1778254   -0.208000    0.323000    0.1800000
+45355.00  -0.205593   0.324915   0.1745799   -0.207000    0.327000    0.1771000
+45356.00  -0.204842   0.328552   0.1712638   -0.207000    0.331000    0.1740000
+45357.00  -0.204017   0.332205   0.1679448   -0.206000    0.335000    0.1710000
+45358.00  -0.203126   0.335874   0.1646928   -0.206000    0.339000    0.1679000
+45359.00  -0.202180   0.339560   0.1615602   -0.205000    0.343000    0.1649000
+45360.00  -0.201190   0.343261   0.1585570   -0.204000    0.347000    0.1619000
+45361.00  -0.200166   0.346978   0.1556373   -0.204000    0.351000    0.1589000
+45362.00  -0.199118   0.350711   0.1527105   -0.203000    0.354000    0.1558000
+45363.00  -0.198059   0.354459   0.1496744   -0.203000    0.358000    0.1526000
+45364.00  -0.196995   0.358220   0.1464584   -0.202000    0.362000    0.1491000
+45365.00  -0.195926   0.361986   0.1430533   -0.201000    0.366000    0.1455000
+45366.00  -0.194851   0.365751   0.1395149   -0.200000    0.369000    0.1416000
+45367.00  -0.193771   0.369508   0.1359409   -0.199000    0.373000    0.1374000
+45368.00  -0.192684   0.373250   0.1324362   -0.198000    0.376000    0.1335000
+45369.00  -0.191588   0.376969   0.1290835   -0.197000    0.380000    0.1298000
+45370.00  -0.190484   0.380659   0.1259296   -0.196000    0.383000    0.1264000
+45371.00  -0.189367   0.384312   0.1229869   -0.195000    0.386000    0.1233000
+45372.00  -0.188234   0.387921   0.1202404   -0.193000    0.390000    0.1205000
+45373.00  -0.187082   0.391479   0.1176558   -0.192000    0.393000    0.1178000
+45374.00  -0.185908   0.394981   0.1151858   -0.191000    0.396000    0.1152000
+45375.00  -0.184713   0.398433   0.1127737   -0.190000    0.399000    0.1127000
+45376.00  -0.183498   0.401844   0.1103595   -0.188000    0.402000    0.1103000
+45377.00  -0.182269   0.405222   0.1078857   -0.187000    0.406000    0.1079000
+45378.00  -0.181029   0.408577   0.1053057   -0.185000    0.409000    0.1053000
+45379.00  -0.179780   0.411914   0.1025905   -0.184000    0.412000    0.1025000
+45380.00  -0.178518   0.415234   0.0997325   -0.182000    0.415000    0.0996000
+45381.00  -0.177241   0.418537   0.0967476   -0.181000    0.418000    0.0965000
+45382.00  -0.175944   0.421823   0.0936712   -0.179000    0.421000    0.0934000
+45383.00  -0.174625   0.425092   0.0905532   -0.178000    0.424000    0.0902000
+45384.00  -0.173282   0.428344   0.0874499   -0.176000    0.427000    0.0870000
+45385.00  -0.171913   0.431577   0.0844157   -0.174000    0.430000    0.0840000
+45386.00  -0.170518   0.434794   0.0814906   -0.172000    0.433000    0.0810000
+45387.00  -0.169096   0.437994   0.0786836   -0.171000    0.436000    0.0782000
+45388.00  -0.167646   0.441179   0.0759597   -0.169000    0.439000    0.0755000
+45389.00  -0.166167   0.444350   0.0732408   -0.167000    0.442000    0.0728000
+45390.00  -0.164660   0.447507   0.0704274   -0.165000    0.445000    0.0700000
+45391.00  -0.163125   0.450652   0.0674296   -0.163000    0.448000    0.0670000
+45392.00  -0.161563   0.453782   0.0642052   -0.162000    0.451000    0.0637000
+45393.00  -0.159973   0.456896   0.0607805   -0.160000    0.454000    0.0603000
+45394.00  -0.158356   0.459991   0.0572429   -0.158000    0.457000    0.0567000
+45395.00  -0.156711   0.463065   0.0537102   -0.156000    0.460000    0.0531000
+45396.00  -0.155027   0.466114   0.0502915   -0.154000    0.463000    0.0496000
+45397.00  -0.153292   0.469131   0.0470614   -0.152000    0.466000    0.0462000
+45398.00  -0.151495   0.472111   0.0440498   -0.150000    0.469000    0.0431000
+45399.00  -0.149620   0.475047   0.0412480   -0.148000    0.472000    0.0403000
+45400.00  -0.147661   0.477937   0.0386193   -0.146000    0.475000    0.0376000
+45401.00  -0.145613   0.480781   0.0361107   -0.144000    0.478000    0.0351000
+45402.00  -0.143471   0.483580   0.0336612   -0.141000    0.481000    0.0326000
+45403.00  -0.141231   0.486336   0.0312083   -0.139000    0.484000    0.0302000
+45404.00  -0.138889   0.489050   0.0286930   -0.137000    0.487000    0.0277000
+45405.00  -0.136442   0.491725   0.0260663   -0.134000    0.490000    0.0251000
+45406.00  -0.133885   0.494364   0.0232944   -0.132000    0.493000    0.0224000
+45407.00  -0.131210   0.496969   0.0203649   -0.129000    0.496000    0.0197000
+45408.00  -0.128410   0.499543   0.0172904   -0.127000    0.499000    0.0167000
+45409.00  -0.125478   0.502086   0.0141085   -0.124000    0.502000    0.0136000
+45410.00  -0.122418   0.504602   0.0108778   -0.121000    0.505000    0.0104000
+45411.00  -0.119235   0.507091   0.0076674   -0.118000    0.507000    0.0073000
+45412.00  -0.115937   0.509555   0.0045425   -0.115000    0.510000    0.0043000
+45413.00  -0.112530   0.511993   0.0015490   -0.112000    0.512000    0.0014000
+45414.00  -0.109022   0.514404  -0.0012990   -0.109000    0.515000   -0.0014000
+45415.00  -0.105420   0.516789  -0.0040274   -0.106000    0.517000   -0.0041000
+45416.00  -0.101731   0.519147  -0.0067025   -0.102000    0.519000   -0.0068000
+45417.00  -0.097962   0.521477  -0.0094192   -0.099000    0.522000   -0.0095000
+45418.00  -0.094119   0.523776  -0.0122760   -0.095000    0.524000   -0.0124000
+45419.00  -0.090209   0.526043  -0.0153433   -0.092000    0.526000   -0.0154000
+45420.00  -0.086237   0.528276  -0.0186359   -0.088000    0.528000   -0.0187000
+45421.00  -0.082209   0.530471  -0.0221034   -0.084000    0.530000   -0.0222000
+45422.00  -0.078130   0.532621  -0.0256461   -0.081000    0.532000   -0.0257000
+45423.00  -0.074009   0.534721  -0.0291468   -0.077000    0.534000   -0.0292000
+45424.00  -0.069850   0.536763  -0.0325059   -0.073000    0.536000   -0.0326000
+45425.00  -0.065659   0.538743  -0.0356630   -0.069000    0.538000   -0.0358000
+45426.00  -0.061440   0.540657  -0.0386029   -0.065000    0.539000   -0.0387000
+45427.00  -0.057195   0.542501  -0.0413476   -0.060000    0.541000   -0.0415000
+45428.00  -0.052928   0.544268  -0.0439429   -0.056000    0.542000   -0.0441000
+45429.00  -0.048642   0.545956  -0.0464476   -0.052000    0.544000   -0.0466000
+45430.00  -0.044341   0.547559  -0.0489244   -0.048000    0.545000   -0.0491000
+45431.00  -0.040027   0.549075  -0.0514330   -0.044000    0.547000   -0.0516000
+45432.00  -0.035702   0.550499  -0.0540247   -0.040000    0.548000   -0.0542000
+45433.00  -0.031369   0.551829  -0.0567364   -0.036000    0.550000   -0.0568000
+45434.00  -0.027031   0.553065  -0.0595887   -0.032000    0.551000   -0.0596000
+45435.00  -0.022693   0.554205  -0.0625809   -0.028000    0.552000   -0.0625000
+45436.00  -0.018358   0.555249  -0.0656872   -0.024000    0.553000   -0.0656000
+45437.00  -0.014034   0.556197  -0.0688558   -0.020000    0.555000   -0.0686000
+45438.00  -0.009727   0.557050  -0.0720153   -0.016000    0.556000   -0.0716000
+45439.00  -0.005446   0.557808  -0.0750885   -0.012000    0.557000   -0.0746000
+45440.00  -0.001195   0.558473  -0.0780126   -0.008000    0.558000   -0.0774000
+45441.00   0.003020   0.559045  -0.0807588   -0.004000    0.559000   -0.0800000
+45442.00   0.007198   0.559527  -0.0833430    0.001000    0.559000   -0.0825000
+45443.00   0.011338   0.559922  -0.0858234    0.005000    0.560000   -0.0849000
+45444.00   0.015440   0.560231  -0.0882865    0.009000    0.561000   -0.0873000
+45445.00   0.019503   0.560459  -0.0908251    0.013000    0.561000   -0.0897000
+45446.00   0.023529   0.560610  -0.0935121    0.017000    0.562000   -0.0924000
+45447.00   0.027520   0.560689  -0.0963788    0.022000    0.562000   -0.0952000
+45448.00   0.031477   0.560698  -0.0994022    0.026000    0.563000   -0.0982000
+45449.00   0.035403   0.560642  -0.1025100    0.030000    0.563000   -0.1013000
+45450.00   0.039300   0.560525  -0.1056022    0.034000    0.563000   -0.1044000
+45451.00   0.043172   0.560348  -0.1085773    0.038000    0.563000   -0.1074000
+45452.00   0.047021   0.560116  -0.1113600    0.043000    0.564000   -0.1102000
+45453.00   0.050854   0.559830  -0.1139153    0.047000    0.564000   -0.1128000
+45454.00   0.054675   0.559493  -0.1162496    0.051000    0.564000   -0.1153000
+45455.00   0.058491   0.559106  -0.1184020    0.055000    0.564000   -0.1175000
+45456.00   0.062309   0.558672  -0.1204319    0.059000    0.564000   -0.1197000
+45457.00   0.066136   0.558194  -0.1224077    0.064000    0.564000   -0.1219000
+45458.00   0.069979   0.557674  -0.1243973    0.068000    0.564000   -0.1240000
+45459.00   0.073844   0.557115  -0.1264606    0.072000    0.564000   -0.1262000
+45460.00   0.077738   0.556519  -0.1286443    0.076000    0.563000   -0.1285000
+45461.00   0.081669   0.555888  -0.1309783    0.080000    0.563000   -0.1310000
+45462.00   0.085642   0.555220  -0.1334714    0.085000    0.562000   -0.1336000
+45463.00   0.089658   0.554509  -0.1361100    0.089000    0.562000   -0.1363000
+45464.00   0.093715   0.553751  -0.1388562    0.093000    0.561000   -0.1392000
+45465.00   0.097812   0.552942  -0.1416479    0.097000    0.560000   -0.1421000
+45466.00   0.101946   0.552078  -0.1444055    0.101000    0.559000   -0.1450000
+45467.00   0.106112   0.551155  -0.1470507    0.105000    0.559000   -0.1478000
+45468.00   0.110302   0.550170  -0.1495316    0.109000    0.558000   -0.1504000
+45469.00   0.114509   0.549120  -0.1518442    0.113000    0.557000   -0.1528000
+45470.00   0.118726   0.547998  -0.1540358    0.117000    0.556000   -0.1551000
+45471.00   0.122945   0.546802  -0.1561922    0.121000    0.555000   -0.1573000
+45472.00   0.127159   0.545527  -0.1584097    0.125000    0.554000   -0.1597000
+45473.00   0.131360   0.544168  -0.1607653    0.129000    0.553000   -0.1621000
+45474.00   0.135539   0.542719  -0.1632953    0.133000    0.552000   -0.1647000
+45475.00   0.139689   0.541175  -0.1659860    0.137000    0.551000   -0.1675000
+45476.00   0.143801   0.539534  -0.1687803    0.141000    0.549000   -0.1704000
+45477.00   0.147867   0.537793  -0.1715926    0.144000    0.548000   -0.1733000
+45478.00   0.151878   0.535950  -0.1743313    0.148000    0.546000   -0.1760000
+45479.00   0.155826   0.534004  -0.1769185    0.152000    0.545000   -0.1787000
+45480.00   0.159706   0.531958  -0.1793060    0.156000    0.543000   -0.1811000
+45481.00   0.163513   0.529815  -0.1814826    0.159000    0.541000   -0.1833000
+45482.00   0.167243   0.527577  -0.1834712    0.163000    0.540000   -0.1852000
+45483.00   0.170895   0.525249  -0.1853193    0.166000    0.538000   -0.1871000
+45484.00   0.174469   0.522836  -0.1870888    0.170000    0.536000   -0.1889000
+45485.00   0.177966   0.520342  -0.1888461    0.174000    0.534000   -0.1906000
+45486.00   0.181388   0.517771  -0.1906526    0.177000    0.532000   -0.1924000
+45487.00   0.184737   0.515127  -0.1925570    0.181000    0.529000   -0.1942000
+45488.00   0.188016   0.512417  -0.1945898    0.184000    0.527000   -0.1962000
+45489.00   0.191228   0.509648  -0.1967628    0.188000    0.525000   -0.1984000
+45490.00   0.194379   0.506829  -0.1990697    0.191000    0.522000   -0.2007000
+45491.00   0.197476   0.503969  -0.2014855    0.195000    0.520000   -0.2032000
+45492.00   0.200525   0.501077  -0.2039651    0.198000    0.517000   -0.2058000
+45493.00   0.203534   0.498163  -0.2064431    0.202000    0.515000   -0.2083000
+45494.00   0.206511   0.495238  -0.2088431    0.205000    0.512000   -0.2107000
+45495.00   0.209463   0.492312  -0.2110986    0.208000    0.509000   -0.2129000
+45496.00   0.212398   0.489397  -0.2131805    0.212000    0.506000   -0.2150000
+45497.00   0.215316   0.486502  -0.2151148    0.215000    0.503000   -0.2169000
+45498.00   0.218216   0.483639  -0.2169799    0.219000    0.500000   -0.2188000
+45499.00   0.221098   0.480815  -0.2188808    0.222000    0.497000   -0.2206000
+45500.00   0.223959   0.478037  -0.2209105    0.225000    0.494000   -0.2226000
+45501.00   0.226807   0.475297  -0.2231172    0.228000    0.490000   -0.2247000
+45502.00   0.229649   0.472583  -0.2254904    0.232000    0.487000   -0.2270000
+45503.00   0.232469   0.469865  -0.2279672    0.235000    0.483000   -0.2294000
+45504.00   0.235245   0.467113  -0.2304555    0.238000    0.480000   -0.2318000
+45505.00   0.237965   0.464296  -0.2328591    0.241000    0.476000   -0.2340000
+45506.00   0.240656   0.461383  -0.2350984    0.244000    0.473000   -0.2361000
+45507.00   0.243352   0.458346  -0.2371218    0.246000    0.469000   -0.2380000
+45508.00   0.246079   0.455162  -0.2389102    0.249000    0.466000   -0.2397000
+45509.00   0.248790   0.451851  -0.2404772    0.252000    0.462000   -0.2411000
+45510.00   0.251403   0.448455  -0.2418619    0.255000    0.458000   -0.2424000
+45511.00   0.253904   0.444994  -0.2431196    0.257000    0.455000   -0.2435000
+45512.00   0.256323   0.441473  -0.2443137    0.260000    0.451000   -0.2446000
+45513.00   0.258689   0.437896  -0.2455067    0.262000    0.448000   -0.2458000
+45514.00   0.261034   0.434269  -0.2467514    0.265000    0.444000   -0.2470000
+45515.00   0.263368   0.430601  -0.2480856    0.267000    0.440000   -0.2483000
+45516.00   0.265685   0.426907   0.7504719    0.269000    0.436000    0.7503000
+45517.00   0.267977   0.423195   0.7489233    0.272000    0.433000    0.7487000
+45518.00   0.270239   0.419450   0.7472900    0.274000    0.429000    0.7470000
+45519.00   0.272465   0.415657   0.7456089    0.276000    0.425000    0.7453000
+45520.00   0.274650   0.411797   0.7439316    0.278000    0.421000    0.7436000
+45521.00   0.276789   0.407855   0.7423215    0.280000    0.417000    0.7419000
+45522.00   0.278877   0.403817   0.7408431    0.283000    0.413000    0.7403000
+45523.00   0.280924   0.399698   0.7395374    0.285000    0.409000    0.7389000
+45524.00   0.282943   0.395528   0.7383994    0.287000    0.405000    0.7376000
+45525.00   0.284950   0.391334   0.7373669    0.289000    0.401000    0.7365000
+45526.00   0.286959   0.387145   0.7363329    0.291000    0.397000    0.7353000
+45527.00   0.288985   0.382991   0.7351809    0.294000    0.392000    0.7340000
+45528.00   0.291042   0.378900   0.7338288    0.296000    0.388000    0.7325000
+45529.00   0.293139   0.374884   0.7322570    0.298000    0.384000    0.7309000
+45530.00   0.295275   0.370936   0.7305113    0.300000    0.380000    0.7290000
+45531.00   0.297449   0.367044   0.7286812    0.302000    0.376000    0.7271000
+45532.00   0.299642   0.363202   0.7268689    0.305000    0.371000    0.7253000
+45533.00   0.301814   0.359408   0.7251607    0.307000    0.367000    0.7236000
+45534.00   0.303922   0.355660   0.7236072    0.309000    0.363000    0.7221000
+45535.00   0.305929   0.351954   0.7222224    0.311000    0.359000    0.7208000
+45536.00   0.307817   0.348280   0.7209944    0.313000    0.354000    0.7197000
+45537.00   0.309571   0.344626   0.7198906    0.315000    0.350000    0.7188000
+45538.00   0.311184   0.340978   0.7188644    0.317000    0.345000    0.7179000
+45539.00   0.312662   0.337319   0.7178588    0.319000    0.341000    0.7171000
+45540.00   0.314018   0.333629   0.7168157    0.320000    0.337000    0.7163000
+45541.00   0.315265   0.329890   0.7156832    0.322000    0.332000    0.7153000
+45542.00   0.316426   0.326094   0.7144231    0.323000    0.328000    0.7143000
+45543.00   0.317524   0.322234   0.7130171    0.325000    0.323000    0.7131000
+45544.00   0.318573   0.318298   0.7114668    0.326000    0.319000    0.7117000
+45545.00   0.319575   0.314267   0.7097910    0.327000    0.315000    0.7101000
+45546.00   0.320531   0.310123   0.7080234    0.328000    0.311000    0.7085000
+45547.00   0.321434   0.305872   0.7062103    0.328000    0.306000    0.7067000
+45548.00   0.322273   0.301533   0.7044070    0.329000    0.302000    0.7050000
+45549.00   0.323036   0.297129   0.7026705    0.330000    0.298000    0.7033000
+45550.00   0.323703   0.292676   0.7010479    0.331000    0.294000    0.7017000
+45551.00   0.324245   0.288185   0.6995577    0.331000    0.290000    0.7003000
+45552.00   0.324630   0.283662   0.6981705    0.332000    0.285000    0.6989000
+45553.00   0.324867   0.279123   0.6968051    0.332000    0.281000    0.6975000
+45554.00   0.325008   0.274583   0.6953489    0.333000    0.277000    0.6960000
+45555.00   0.325109   0.270061   0.6936998    0.333000    0.273000    0.6943000
+45556.00   0.325220   0.265574   0.6918090    0.333000    0.269000    0.6923000
+45557.00   0.325345   0.261129   0.6897026    0.333000    0.264000    0.6901000
+45558.00   0.325442   0.256722   0.6874726    0.333000    0.260000    0.6878000
+45559.00   0.325463   0.252350   0.6852440    0.333000    0.256000    0.6854000
+45560.00   0.325347   0.248012   0.6831328    0.333000    0.252000    0.6832000
+45561.00   0.325027   0.243710   0.6812146    0.332000    0.248000    0.6812000
+45562.00   0.324439   0.239444   0.6795146    0.332000    0.243000    0.6794000
+45563.00   0.323570   0.235203   0.6780156    0.331000    0.239000    0.6778000
+45564.00   0.322552   0.230949   0.6766742    0.331000    0.235000    0.6764000
+45565.00   0.321508   0.226648   0.6754331    0.330000    0.231000    0.6751000
+45566.00   0.320461   0.222306   0.6742298    0.330000    0.227000    0.6739000
+45567.00   0.319418   0.217934   0.6730031    0.329000    0.222000    0.6726000
+45568.00   0.318373   0.213545   0.6716981    0.329000    0.218000    0.6713000
+45569.00   0.317312   0.209152   0.6702728    0.328000    0.214000    0.6698000
+45570.00   0.316217   0.204768   0.6687035    0.327000    0.210000    0.6682000
+45571.00   0.315057   0.200403   0.6669878    0.326000    0.206000    0.6665000
+45572.00   0.313769   0.196072   0.6651441    0.324000    0.201000    0.6646000
+45573.00   0.312292   0.191785   0.6632099    0.323000    0.197000    0.6627000
+45574.00   0.310611   0.187543   0.6612365    0.322000    0.193000    0.6608000
+45575.00   0.308761   0.183334   0.6592822    0.320000    0.189000    0.6590000
+45576.00   0.306780   0.179145   0.6574024    0.319000    0.185000    0.6572000
+45577.00   0.304707   0.174963   0.6556410    0.317000    0.180000    0.6556000
+45578.00   0.302568   0.170795   0.6540182    0.316000    0.176000    0.6541000
+45579.00   0.300382   0.166667   0.6525168    0.314000    0.172000    0.6528000
+45580.00   0.298158   0.162606   0.6510751    0.312000    0.168000    0.6514000
+45581.00   0.295874   0.158623   0.6495919    0.310000    0.164000    0.6501000
+45582.00   0.293495   0.154721   0.6479549    0.308000    0.159000    0.6486000
+45583.00   0.290991   0.150908   0.6460815    0.306000    0.155000    0.6468000
+45584.00   0.288340   0.147183   0.6439545    0.304000    0.151000    0.6448000
+45585.00   0.285620   0.143517   0.6416305    0.302000    0.147000    0.6425000
+45586.00   0.282932   0.139868   0.6392241    0.300000    0.143000    0.6402000
+45587.00   0.280329   0.136199   0.6368703    0.297000    0.139000    0.6380000
+45588.00   0.277835   0.132485   0.6346791    0.295000    0.135000    0.6359000
+45589.00   0.275415   0.128738   0.6327116    0.293000    0.131000    0.6340000
+45590.00   0.273024   0.124983   0.6309726    0.290000    0.127000    0.6323000
+45591.00   0.270614   0.121243   0.6294242    0.288000    0.123000    0.6308000
+45592.00   0.268140   0.117541   0.6280058    0.285000    0.120000    0.6295000
+45593.00   0.265569   0.113896   0.6266502    0.283000    0.116000    0.6282000
+45594.00   0.262872   0.110324   0.6252925    0.280000    0.112000    0.6268000
+45595.00   0.260033   0.106829   0.6238741    0.277000    0.109000    0.6254000
+45596.00   0.257047   0.103408   0.6223466    0.274000    0.105000    0.6238000
+45597.00   0.253908   0.100056   0.6206790    0.271000    0.102000    0.6221000
+45598.00   0.250619   0.096769   0.6188601    0.268000    0.098000    0.6202000
+45599.00   0.247218   0.093546   0.6169004    0.265000    0.095000    0.6181000
+45600.00   0.243752   0.090388   0.6148318    0.262000    0.092000    0.6159000
+45601.00   0.240255   0.087290   0.6127044    0.259000    0.089000    0.6136000
+45602.00   0.236747   0.084238   0.6105772    0.255000    0.086000    0.6112000
+45603.00   0.233246   0.081217   0.6085069    0.252000    0.083000    0.6090000
+45604.00   0.229770   0.078212   0.6065357    0.249000    0.080000    0.6069000
+45605.00   0.226324   0.075212   0.6046808    0.245000    0.077000    0.6049000
+45606.00   0.222887   0.072205   0.6029261    0.242000    0.074000    0.6031000
+45607.00   0.219443   0.069196   0.6012183    0.238000    0.072000    0.6012000
+45608.00   0.215995   0.066215   0.5994726    0.235000    0.069000    0.5995000
+45609.00   0.212552   0.063299   0.5975868    0.231000    0.066000    0.5976000
+45610.00   0.209116   0.060476   0.5954696    0.227000    0.064000    0.5955000
+45611.00   0.205677   0.057756   0.5930760    0.223000    0.061000    0.5931000
+45612.00   0.202226   0.055143   0.5904304    0.219000    0.059000    0.5905000
+45613.00   0.198751   0.052641   0.5876271    0.215000    0.056000    0.5878000
+45614.00   0.195241   0.050236   0.5848052    0.211000    0.054000    0.5851000
+45615.00   0.191681   0.047915   0.5821044    0.207000    0.052000    0.5825000
+45616.00   0.188069   0.045677   0.5796249    0.202000    0.050000    0.5800000
+45617.00   0.184406   0.043530   0.5774064    0.198000    0.047000    0.5779000
+45618.00   0.180694   0.041484   0.5754327    0.193000    0.045000    0.5760000
+45619.00   0.176934   0.039547   0.5736494    0.189000    0.043000    0.5742000
+45620.00   0.173127   0.037715   0.5719840    0.185000    0.041000    0.5726000
+45621.00   0.169272   0.035973   0.5703624    0.180000    0.039000    0.5710000
+45622.00   0.165367   0.034308   0.5687185    0.176000    0.038000    0.5693000
+45623.00   0.161397   0.032717   0.5669967    0.171000    0.036000    0.5675000
+45624.00   0.157343   0.031201   0.5651578    0.167000    0.034000    0.5656000
+45625.00   0.153187   0.029760   0.5631825    0.163000    0.033000    0.5635000
+45626.00   0.148934   0.028382   0.5610699    0.158000    0.031000    0.5612000
+45627.00   0.144606   0.027048   0.5588385    0.154000    0.030000    0.5588000
+45628.00   0.140224   0.025746   0.5565266    0.149000    0.028000    0.5563000
+45629.00   0.135800   0.024495   0.5541889    0.145000    0.027000    0.5537000
+45630.00   0.131340   0.023320   0.5518854    0.141000    0.026000    0.5513000
+45631.00   0.126852   0.022244   0.5496675    0.136000    0.025000    0.5489000
+45632.00   0.122332   0.021273   0.5475627    0.132000    0.024000    0.5467000
+45633.00   0.117777   0.020411   0.5455610    0.127000    0.023000    0.5447000
+45634.00   0.113183   0.019661   0.5436110    0.123000    0.022000    0.5427000
+45635.00   0.108558   0.019010   0.5416323    0.118000    0.021000    0.5407000
+45636.00   0.103917   0.018434   0.5395291    0.114000    0.020000    0.5386000
+45637.00   0.099285   0.017915   0.5372094    0.109000    0.020000    0.5364000
+45638.00   0.094682   0.017444   0.5346166    0.105000    0.019000    0.5339000
+45639.00   0.090127   0.017010   0.5317491    0.100000    0.018000    0.5311000
+45640.00   0.085635   0.016605   0.5286668    0.095000    0.018000    0.5282000
+45641.00   0.081180   0.016226   0.5254822    0.091000    0.018000    0.5252000
+45642.00   0.076707   0.015875   0.5223304    0.086000    0.017000    0.5222000
+45643.00   0.072188   0.015555   0.5193312    0.082000    0.017000    0.5194000
+45644.00   0.067650   0.015274   0.5165611    0.077000    0.017000    0.5167000
+45645.00   0.063133   0.015041   0.5140408    0.073000    0.017000    0.5143000
+45646.00   0.058673   0.014863   0.5117429    0.068000    0.017000    0.5121000
+45647.00   0.054298   0.014750   0.5096069    0.064000    0.018000    0.5099000
+45648.00   0.050016   0.014711   0.5075559    0.059000    0.018000    0.5079000
+45649.00   0.045825   0.014760   0.5055162    0.055000    0.018000    0.5058000
+45650.00   0.041690   0.014919   0.5034252    0.051000    0.018000    0.5037000
+45651.00   0.037570   0.015212   0.5012379    0.046000    0.018000    0.5015000
+45652.00   0.033436   0.015651   0.4989301    0.042000    0.019000    0.4991000
+45653.00   0.029287   0.016216   0.4964974    0.037000    0.019000    0.4966000
+45654.00   0.025129   0.016880   0.4939555    0.033000    0.019000    0.4940000
+45655.00   0.020959   0.017618   0.4913389    0.029000    0.020000    0.4913000
+45656.00   0.016746   0.018418   0.4887009    0.025000    0.020000    0.4887000
+45657.00   0.012470   0.019266   0.4861057    0.020000    0.021000    0.4861000
+45658.00   0.008132   0.020152   0.4836202    0.016000    0.021000    0.4836000
+45659.00   0.003743   0.021065   0.4812962    0.012000    0.022000    0.4812000
+45660.00  -0.000682   0.021994   0.4791497    0.008000    0.023000    0.4790000
+45661.00  -0.005128   0.022932   0.4771491    0.004000    0.024000    0.4770000
+45662.00  -0.009563   0.023887   0.4752188   -0.001000    0.024000    0.4749000
+45663.00  -0.013953   0.024877   0.4732586   -0.005000    0.025000    0.4729000
+45664.00  -0.018277   0.025911   0.4711735   -0.009000    0.026000    0.4707000
+45665.00  -0.022529   0.026991   0.4689010   -0.013000    0.027000    0.4684000
+45666.00  -0.026704   0.028118   0.4664286   -0.017000    0.028000    0.4659000
+45667.00  -0.030799   0.029291   0.4637977   -0.021000    0.030000    0.4633000
+45668.00  -0.034830   0.030504   0.4610947   -0.025000    0.031000    0.4606000
+45669.00  -0.038822   0.031742   0.4584273   -0.029000    0.032000    0.4579000
+45670.00  -0.042803   0.032994   0.4558981   -0.033000    0.033000    0.4554000
+45671.00  -0.046774   0.034259   0.4535817   -0.036000    0.035000    0.4531000
+45672.00  -0.050719   0.035547   0.4515093   -0.040000    0.036000    0.4511000
+45673.00  -0.054619   0.036867   0.4496656   -0.043000    0.038000    0.4493000
+45674.00  -0.058443   0.038228   0.4479977   -0.047000    0.039000    0.4476000
+45675.00  -0.062153   0.039638   0.4464330   -0.050000    0.041000    0.4461000
+45676.00  -0.065717   0.041105   0.4448954   -0.054000    0.042000    0.4446000
+45677.00  -0.069128   0.042651   0.4433152   -0.057000    0.044000    0.4431000
+45678.00  -0.072386   0.044297   0.4416385   -0.061000    0.045000    0.4414000
+45679.00  -0.075499   0.046056   0.4398322   -0.064000    0.047000    0.4395000
+45680.00  -0.078500   0.047907   0.4378858   -0.067000    0.049000    0.4375000
+45681.00  -0.081426   0.049824   0.4358100   -0.070000    0.051000    0.4354000
+45682.00  -0.084310   0.051784   0.4336338   -0.074000    0.052000    0.4331000
+45683.00  -0.087163   0.053773   0.4313994   -0.077000    0.054000    0.4307000
+45684.00  -0.089984   0.055779   0.4291600   -0.080000    0.056000    0.4284000
+45685.00  -0.092784   0.057796   0.4269767   -0.083000    0.058000    0.4261000
+45686.00  -0.095588   0.059831   0.4249091   -0.086000    0.060000    0.4240000
+45687.00  -0.098428   0.061893   0.4229931   -0.089000    0.062000    0.4219000
+45688.00  -0.101331   0.063991   0.4212216   -0.092000    0.064000    0.4201000
+45689.00  -0.104285   0.066120   0.4195351   -0.095000    0.066000    0.4183000
+45690.00  -0.107234   0.068258   0.4178346   -0.098000    0.068000    0.4165000
+45691.00  -0.110135   0.070392   0.4160138   -0.101000    0.070000    0.4147000
+45692.00  -0.112977   0.072525   0.4139933   -0.103000    0.072000    0.4125000
+45693.00  -0.115751   0.074659   0.4117490   -0.106000    0.074000    0.4102000
+45694.00  -0.118457   0.076816   0.4093198   -0.109000    0.076000    0.4078000
+45695.00  -0.121102   0.079021   0.4067917   -0.112000    0.078000    0.4053000
+45696.00  -0.123689   0.081302   0.4042707   -0.114000    0.080000    0.4029000
+45697.00  -0.126226   0.083687   0.4018558   -0.117000    0.083000    0.4005000
+45698.00  -0.128720   0.086194   0.3996181   -0.119000    0.085000    0.3984000
+45699.00  -0.131198   0.088822   0.3975912   -0.122000    0.087000    0.3966000
+45700.00  -0.133693   0.091556   0.3957700   -0.125000    0.089000    0.3950000
+45701.00  -0.136248   0.094364   0.3941189   -0.127000    0.092000    0.3937000
+45702.00  -0.138908   0.097214   0.3925820   -0.130000    0.091000    0.3928000
+45703.00  -0.141713   0.100076   0.3910960   -0.132000    0.094000    0.3908000
+45704.00  -0.144634   0.102933   0.3896011   -0.135000    0.099000    0.3900000
+45705.00  -0.147567   0.105787   0.3880487   -0.137000    0.102000    0.3886000
+45706.00  -0.150445   0.108641   0.3864062   -0.140000    0.104000    0.3871000
+45707.00  -0.153237   0.111497   0.3846611   -0.142000    0.107000    0.3854000
+45708.00  -0.155921   0.114358   0.3828226   -0.145000    0.110000    0.3836000
+45709.00  -0.158475   0.117229   0.3809185   -0.147000    0.113000    0.3816000
+45710.00  -0.160879   0.120116   0.3789884   -0.150000    0.116000    0.3797000
+45711.00  -0.163135   0.123034   0.3770820   -0.152000    0.119000    0.3777000
+45712.00  -0.165270   0.125996   0.3752552   -0.154000    0.122000    0.3757000
+45713.00  -0.167340   0.128998   0.3735584   -0.157000    0.125000    0.3739000
+45714.00  -0.169412   0.132027   0.3720280   -0.159000    0.128000    0.3722000
+45715.00  -0.171529   0.135077   0.3706685   -0.161000    0.131000    0.3707000
+45716.00  -0.173696   0.138154   0.3694355   -0.164000    0.134000    0.3693000
+45717.00  -0.175913   0.141261   0.3682349   -0.166000    0.138000    0.3679000
+45718.00  -0.178181   0.144398   0.3669465   -0.168000    0.141000    0.3664000
+45719.00  -0.180499   0.147546   0.3654672   -0.171000    0.144000    0.3648000
+45720.00  -0.182868   0.150678   0.3637489   -0.173000    0.147000    0.3629000
+45721.00  -0.185285   0.153776   0.3618180   -0.175000    0.150000    0.3609000
+45722.00  -0.187747   0.156841   0.3597665   -0.177000    0.153000    0.3589000
+45723.00  -0.190250   0.159874   0.3577188   -0.180000    0.157000    0.3567000
+45724.00  -0.192791   0.162876   0.3557903   -0.182000    0.160000    0.3548000
+45725.00  -0.195341   0.165856   0.3540603   -0.184000    0.163000    0.3530000
+45726.00  -0.197857   0.168829   0.3525635   -0.186000    0.166000    0.3515000
+45727.00  -0.200310   0.171811   0.3512901   -0.189000    0.169000    0.3501000
+45728.00  -0.202689   0.174810   0.3501969   -0.191000    0.173000    0.3490000
+45729.00  -0.204988   0.177835   0.3492225   -0.193000    0.176000    0.3481000
+45730.00  -0.207201   0.180895   0.3482974   -0.195000    0.179000    0.3472000
+45731.00  -0.209302   0.184010   0.3473493   -0.197000    0.183000    0.3464000
+45732.00  -0.211263   0.187209   0.3463147   -0.200000    0.186000    0.3457000
+45733.00  -0.213058   0.190510   0.3451465   -0.202000    0.189000    0.3446000
+45734.00  -0.214671   0.193897   0.3438190   -0.204000    0.193000    0.3434000
+45735.00  -0.216102   0.197345   0.3423294   -0.206000    0.196000    0.3420000
+45736.00  -0.217398   0.200842   0.3407013   -0.208000    0.200000    0.3404000
+45737.00  -0.218614   0.204396   0.3389802   -0.210000    0.203000    0.3387000
+45738.00  -0.219801   0.208015   0.3372199   -0.212000    0.207000    0.3370000
+45739.00  -0.221010   0.211703   0.3354758   -0.214000    0.210000    0.3354000
+45740.00  -0.222283   0.215418   0.3338026   -0.216000    0.214000    0.3337000
+45741.00  -0.223623   0.219088   0.3322386   -0.217000    0.217000    0.3322000
+45742.00  -0.225009   0.222662   0.3307985   -0.219000    0.221000    0.3307000
+45743.00  -0.226395   0.226137   0.3294628   -0.221000    0.225000    0.3294000
+45744.00  -0.227735   0.229512   0.3281652   -0.223000    0.228000    0.3281000
+45745.00  -0.228996   0.232798   0.3267982   -0.224000    0.232000    0.3268000
+45746.00  -0.230184   0.236043   0.3252405   -0.226000    0.236000    0.3252000
+45747.00  -0.231313   0.239301   0.3234042   -0.227000    0.239000    0.3234000
+45748.00  -0.232399   0.242625   0.3212741   -0.229000    0.243000    0.3213000
+45749.00  -0.233470   0.246041   0.3189226   -0.230000    0.247000    0.3190000
+45750.00  -0.234563   0.249561   0.3164862   -0.231000    0.250000    0.3167000
+45751.00  -0.235703   0.253172   0.3141195   -0.233000    0.254000    0.3144000
+45752.00  -0.236884   0.256861   0.3119521   -0.234000    0.258000    0.3123000
+45753.00  -0.238091   0.260612   0.3100573   -0.235000    0.261000    0.3105000
+45754.00  -0.239304   0.264417   0.3084469   -0.236000    0.265000    0.3089000
+45755.00  -0.240498   0.268293   0.3070853   -0.237000    0.269000    0.3075000
+45756.00  -0.241649   0.272247   0.3058959   -0.238000    0.273000    0.3063000
+45757.00  -0.242735   0.276271   0.3047850   -0.238000    0.277000    0.3051000
+45758.00  -0.243735   0.280348   0.3036583   -0.239000    0.280000    0.3039000
+45759.00  -0.244627   0.284464   0.3024328   -0.240000    0.284000    0.3026000
+45760.00  -0.245397   0.288604   0.3010452   -0.241000    0.288000    0.3011000
+45761.00  -0.246066   0.292756   0.2994704   -0.241000    0.292000    0.2996000
+45762.00  -0.246663   0.296911   0.2977127   -0.241000    0.296000    0.2978000
+45763.00  -0.247186   0.301065   0.2957984   -0.242000    0.300000    0.2959000
+45764.00  -0.247564   0.305215   0.2937702   -0.242000    0.304000    0.2939000
+45765.00  -0.247740   0.309367   0.2916832   -0.242000    0.308000    0.2919000
+45766.00  -0.247738   0.313525   0.2895946   -0.242000    0.312000    0.2899000
+45767.00  -0.247545   0.317689   0.2875612   -0.242000    0.316000    0.2880000
+45768.00  -0.247117   0.321851   0.2856316   -0.241000    0.320000    0.2861000
+45769.00  -0.246412   0.326007   0.2838347   -0.241000    0.324000    0.2844000
+45770.00  -0.245453   0.330153   0.2821681   -0.240000    0.328000    0.2827000
+45771.00  -0.244301   0.334287   0.2805828   -0.240000    0.332000    0.2811000
+45772.00  -0.243017   0.338411   0.2789864   -0.239000    0.336000    0.2795000
+45773.00  -0.241646   0.342540   0.2772601   -0.239000    0.340000    0.2777000
+45774.00  -0.240224   0.346696   0.2752929   -0.238000    0.344000    0.2757000
+45775.00  -0.238804   0.350899   0.2730242   -0.237000    0.347000    0.2733000
+45776.00  -0.237426   0.355140   0.2704742   -0.236000    0.351000    0.2706000
+45777.00  -0.236099   0.359395   0.2677449   -0.235000    0.355000    0.2677000
+45778.00  -0.234833   0.363640   0.2649881   -0.234000    0.359000    0.2649000
+45779.00  -0.233638   0.367852   0.2623533   -0.233000    0.363000    0.2621000
+45780.00  -0.232516   0.372008   0.2599421   -0.231000    0.367000    0.2596000
+45781.00  -0.231434   0.376092   0.2577934   -0.230000    0.371000    0.2574000
+45782.00  -0.230332   0.380098   0.2558878   -0.228000    0.375000    0.2554000
+45783.00  -0.229142   0.384027   0.2541670   -0.227000    0.379000    0.2536000
+45784.00  -0.227828   0.387876   0.2525587   -0.225000    0.383000    0.2520000
+45785.00  -0.226424   0.391632   0.2509899   -0.223000    0.387000    0.2503000
+45786.00  -0.224943   0.395303   0.2493888   -0.221000    0.390000    0.2487000
+45787.00  -0.223387   0.398901   0.2476965   -0.219000    0.394000    0.2469000
+45788.00  -0.221753   0.402445   0.2458792   -0.218000    0.398000    0.2450000
+45789.00  -0.220027   0.405961   0.2439299   -0.216000    0.402000    0.2430000
+45790.00  -0.218203   0.409467   0.2418646   -0.213000    0.406000    0.2409000
+45791.00  -0.216277   0.412970   0.2397213   -0.211000    0.410000    0.2386000
+45792.00  -0.214248   0.416473   0.2375544   -0.209000    0.414000    0.2363000
+45793.00  -0.212116   0.419980   0.2354255   -0.206000    0.417000    0.2341000
+45794.00  -0.209885   0.423499   0.2333936   -0.204000    0.421000    0.2320000
+45795.00  -0.207558   0.427047   0.2315025   -0.202000    0.425000    0.2301000
+45796.00  -0.205116   0.430657   0.2297717   -0.199000    0.428000    0.2283000
+45797.00  -0.202564   0.434329   0.2281875   -0.197000    0.432000    0.2268000
+45798.00  -0.199927   0.438042   0.2267002   -0.195000    0.436000    0.2252000
+45799.00  -0.197234   0.441776   0.2252257   -0.192000    0.439000    0.2238000
+45800.00  -0.194512   0.445521   0.2236558   -0.190000    0.443000    0.2222000
+45801.00  -0.191747   0.449274   0.2218794   -0.187000    0.446000    0.2204000
+45802.00  -0.188910   0.453024   0.2198122   -0.185000    0.450000    0.2184000
+45803.00  -0.186018   0.456733   0.2174328   -0.182000    0.453000    0.2161000
+45804.00  -0.183103   0.460348   0.2148017   -0.179000    0.457000    0.2136000
+45805.00  -0.180195   0.463824   0.2120503   -0.177000    0.460000    0.2109000
+45806.00  -0.177307   0.467156   0.2093423   -0.174000    0.463000    0.2084000
+45807.00  -0.174447   0.470354   0.2068199   -0.171000    0.466000    0.2061000
+45808.00  -0.171620   0.473431   0.2045618   -0.168000    0.469000    0.2039000
+45809.00  -0.168823   0.476398   0.2025697   -0.165000    0.473000    0.2020000
+45810.00  -0.166061   0.479278   0.2007854   -0.162000    0.475000    0.2003000
+45811.00  -0.163372   0.482112   0.1991292   -0.159000    0.479000    0.1987000
+45812.00  -0.160723   0.484922   0.1975172   -0.156000    0.481000    0.1970000
+45813.00  -0.158061   0.487723   0.1958781   -0.153000    0.484000    0.1953000
+45814.00  -0.155331   0.490533   0.1941616   -0.150000    0.488000    0.1936000
+45815.00  -0.152483   0.493366   0.1923383   -0.146000    0.490000    0.1917000
+45816.00  -0.149499   0.496226   0.1903968   -0.143000    0.493000    0.1898000
+45817.00  -0.146378   0.499108   0.1883442   -0.140000    0.496000    0.1876000
+45818.00  -0.143112   0.502004   0.1862065   -0.136000    0.499000    0.1854000
+45819.00  -0.139694   0.504907   0.1840258   -0.133000    0.501000    0.1832000
+45820.00  -0.136117   0.507802   0.1818567   -0.129000    0.504000    0.1810000
+45821.00  -0.132404   0.510670   0.1797638   -0.126000    0.506000    0.1788000
+45822.00  -0.128584   0.513487   0.1778052   -0.122000    0.509000    0.1767000
+45823.00  -0.124685   0.516235   0.1760190   -0.119000    0.511000    0.1749000
+45824.00  -0.120721   0.518909   0.1744128   -0.115000    0.514000    0.1732000
+45825.00  -0.116696   0.521516   0.1729556   -0.112000    0.516000    0.1717000
+45826.00  -0.112618   0.524062   0.1715775   -0.108000    0.518000    0.1703000
+45827.00  -0.108541   0.526540   0.1701798   -0.104000    0.521000    0.1689000
+45828.00  -0.104534   0.528942   0.1686564   -0.101000    0.523000    0.1673000
+45829.00  -0.100665   0.531257   0.1669190   -0.097000    0.525000    0.1655000
+45830.00  -0.096980   0.533468   0.1649239   -0.094000    0.527000    0.1635000
+45831.00  -0.093484   0.535562   0.1626958   -0.090000    0.529000    0.1612000
+45832.00  -0.090126   0.537536   0.1603279   -0.086000    0.531000    0.1587000
+45833.00  -0.086832   0.539392   0.1579581   -0.083000    0.533000    0.1563000
+45834.00  -0.083528   0.541130   0.1557314   -0.079000    0.535000    0.1541000
+45835.00  -0.080144   0.542753   0.1537564   -0.075000    0.537000    0.1521000
+45836.00  -0.076659   0.544280   0.1520742   -0.071000    0.538000    0.1504000
+45837.00  -0.073068   0.545737   0.1506565   -0.067000    0.540000    0.1490000
+45838.00  -0.069372   0.547148   0.1494269   -0.064000    0.542000    0.1477000
+45839.00  -0.065593   0.548539   0.1482916   -0.060000    0.544000    0.1466000
+45840.00  -0.061753   0.549930   0.1471636   -0.056000    0.545000    0.1454000
+45841.00  -0.057842   0.551303   0.1459754   -0.052000    0.546000    0.1442000
+45842.00  -0.053862   0.552630   0.1446855   -0.048000    0.548000    0.1429000
+45843.00  -0.049830   0.553883   0.1432785   -0.044000    0.549000    0.1416000
+45844.00  -0.045764   0.555039   0.1417625   -0.040000    0.550000    0.1401000
+45845.00  -0.041679   0.556087   0.1401662   -0.036000    0.552000    0.1385000
+45846.00  -0.037550   0.557031   0.1385293   -0.032000    0.553000    0.1368000
+45847.00  -0.033346   0.557886   0.1368996   -0.028000    0.554000    0.1352000
+45848.00  -0.029063   0.558661   0.1353285   -0.024000    0.555000    0.1337000
+45849.00  -0.024702   0.559368   0.1338643   -0.020000    0.556000    0.1322000
+45850.00  -0.020261   0.560019   0.1325442   -0.016000    0.557000    0.1310000
+45851.00  -0.015736   0.560616   0.1313877   -0.012000    0.557000    0.1299000
+45852.00  -0.011164   0.561137   0.1303775   -0.007000    0.558000    0.1289000
+45853.00  -0.006584   0.561557   0.1294549   -0.003000    0.559000    0.1280000
+45854.00  -0.002004   0.561867   0.1285322    0.001000    0.559000    0.1272000
+45855.00   0.002574   0.562062   0.1275139    0.005000    0.560000    0.1262000
+45856.00   0.007150   0.562137   0.1263199    0.009000    0.560000    0.1250000
+45857.00   0.011713   0.562095   0.1249107    0.014000    0.560000    0.1236000
+45858.00   0.016250   0.561940   0.1233020    0.018000    0.560000    0.1220000
+45859.00   0.020744   0.561678   0.1215661    0.022000    0.560000    0.1203000
+45860.00   0.025167   0.561321   0.1198165    0.026000    0.560000    0.1185000
+45861.00   0.029479   0.560886   0.1181750    0.030000    0.560000    0.1169000
+45862.00   0.033651   0.560392   0.1167397    0.035000    0.560000    0.1154000
+45863.00   0.037698   0.559870   0.1155606    0.039000    0.560000    0.1142000
+45864.00   0.041648   0.559352   0.1146282    0.043000    0.560000    0.1132000
+45865.00   0.045528   0.558866   0.1138831    0.047000    0.559000    0.1125000
+45866.00   0.049374   0.558416   0.1132413    0.052000    0.559000    0.1118000
+45867.00   0.053228   0.557996   0.1126155    0.056000    0.558000    0.1112000
+45868.00   0.057118   0.557597   0.1119315    0.060000    0.558000    0.1105000
+45869.00   0.061049   0.557210   0.1111373    0.064000    0.557000    0.1097000
+45870.00   0.065011   0.556822   0.1102061    0.069000    0.556000    0.1088000
+45871.00   0.068961   0.556410   0.1091371    0.073000    0.555000    0.1077000
+45872.00   0.072888   0.555944   0.1079499    0.077000    0.555000    0.1066000
+45873.00   0.076789   0.555397   0.1066807    0.081000    0.554000    0.1053000
+45874.00   0.080672   0.554742   0.1053753    0.086000    0.553000    0.1041000
+45875.00   0.084588   0.553970   0.1040846    0.090000    0.552000    0.1029000
+45876.00   0.088574   0.553073   0.1028675    0.094000    0.551000    0.1017000
+45877.00   0.092657   0.552046   0.1017801    0.098000    0.549000    0.1006000
+45878.00   0.096871   0.550896   0.1008610    0.102000    0.548000    0.0998000
+45879.00   0.101258   0.549641   0.1001166    0.107000    0.547000    0.0991000
+45880.00   0.105838   0.548297   0.0995091    0.111000    0.546000    0.0986000
+45881.00   0.110541   0.546878   0.0989598    0.115000    0.544000    0.0981000
+45882.00   0.115262   0.545401   0.0983676    0.119000    0.543000    0.0975000
+45883.00   0.119923   0.543875   0.0976423    0.123000    0.541000    0.0967000
+45884.00   0.124503   0.542301   0.0967379    0.128000    0.540000    0.0957000
+45885.00   0.128981   0.540676   0.0956692    0.132000    0.538000    0.0946000
+45886.00   0.133340   0.538990   0.0944962    0.136000    0.536000    0.0932000
+45887.00   0.137574   0.537225   0.0933116    0.140000    0.535000    0.0919000
+45888.00   0.141680   0.535361   0.0922166    0.145000    0.533000    0.0907000
+45889.00   0.145664   0.533389   0.0912929    0.149000    0.531000    0.0897000
+45890.00   0.149545   0.531308   0.0905856    0.153000    0.529000    0.0889000
+45891.00   0.153348   0.529131   0.0900997    0.157000    0.527000    0.0884000
+45892.00   0.157110   0.526879   0.0897970    0.161000    0.525000    0.0881000
+45893.00   0.160862   0.524579   0.0896094    0.165000    0.523000    0.0880000
+45894.00   0.164632   0.522263   0.0894579    0.169000    0.521000    0.0878000
+45895.00   0.168441   0.519958   0.0892686    0.173000    0.518000    0.0877000
+45896.00   0.172283   0.517656   0.0889796    0.177000    0.516000    0.0874000
+45897.00   0.176152   0.515337   0.0885517    0.181000    0.514000    0.0870000
+45898.00   0.180041   0.512981   0.0879727    0.185000    0.511000    0.0865000
+45899.00   0.183948   0.510566   0.0872532    0.189000    0.509000    0.0858000
+45900.00   0.187878   0.508072   0.0864211    0.193000    0.507000    0.0850000
+45901.00   0.191806   0.505497   0.0855229    0.197000    0.504000    0.0842000
+45902.00   0.195733   0.502847   0.0846132    0.201000    0.502000    0.0834000
+45903.00   0.199670   0.500128   0.0837447    0.204000    0.499000    0.0826000
+45904.00   0.203624   0.497351   0.0829616    0.208000    0.496000    0.0819000
+45905.00   0.207573   0.494543   0.0822899    0.212000    0.494000    0.0813000
+45906.00   0.211481   0.491731   0.0817349    0.215000    0.491000    0.0808000
+45907.00   0.215311   0.488936   0.0812672    0.219000    0.488000    0.0804000
+45908.00   0.219051   0.486154   0.0808141    0.223000    0.485000    0.0801000
+45909.00   0.222688   0.483378   0.0802680    0.226000    0.483000    0.0796000
+45910.00   0.226221   0.480603   0.0795154    0.230000    0.479000    0.0789000
+45911.00   0.229681   0.477813   0.0784771    0.233000    0.476000    0.0780000
+45912.00   0.233082   0.474988   0.0771460    0.236000    0.473000    0.0768000
+45913.00   0.236413   0.472105   0.0755994    0.240000    0.470000    0.0753000
+45914.00   0.239692   0.469146   0.0739710    0.243000    0.467000    0.0737000
+45915.00   0.242944   0.466103   0.0724111    0.246000    0.464000    0.0722000
+45916.00   0.246165   0.462984   0.0710446    0.249000    0.461000    0.0708000
+45917.00   0.249335   0.459789   0.0699438    0.252000    0.458000    0.0695000
+45918.00   0.252427   0.456515   0.0691185    0.255000    0.454000    0.0686000
+45919.00   0.255424   0.453164   0.0685157    0.258000    0.451000    0.0679000
+45920.00   0.258353   0.449761   0.0680443    0.261000    0.448000    0.0674000
+45921.00   0.261229   0.446324   0.0676057    0.264000    0.444000    0.0669000
+45922.00   0.264070   0.442875   0.0671077    0.266000    0.441000    0.0666000
+45923.00   0.266901   0.439437   0.0664755    0.269000    0.438000    0.0660000
+45924.00   0.269751   0.436036   0.0656580    0.272000    0.434000    0.0652000
+45925.00   0.272629   0.432674   0.0646337    0.274000    0.431000    0.0642000
+45926.00   0.275486   0.429332   0.0634174    0.276000    0.427000    0.0631000
+45927.00   0.278259   0.425990   0.0620488    0.279000    0.424000    0.0619000
+45928.00   0.280889   0.422626   0.0605835    0.281000    0.420000    0.0605000
+45929.00   0.283325   0.419206   0.0590876    0.283000    0.417000    0.0591000
+45930.00   0.285528   0.415699   0.0576264    0.285000    0.413000    0.0578000
+45931.00   0.287484   0.412093   0.0562564    0.287000    0.410000    0.0565000
+45932.00   0.289188   0.408397   0.0550201    0.289000    0.406000    0.0552000
+45933.00   0.290636   0.404617   0.0539412    0.291000    0.403000    0.0541000
+45934.00   0.291826   0.400762   0.0530166    0.293000    0.399000    0.0531000
+45935.00   0.292763   0.396846   0.0522015    0.295000    0.395000    0.0521000
+45936.00   0.293494   0.392898   0.0513881    0.297000    0.392000    0.0511000
+45937.00   0.294091   0.388946   0.0504355    0.298000    0.388000    0.0499000
+45938.00   0.294653   0.385010   0.0492161    0.300000    0.384000    0.0485000
+45939.00   0.295269   0.381094   0.0476622    0.301000    0.380000    0.0468000
+45940.00   0.295999   0.377203   0.0457925    0.303000    0.377000    0.0448000
+45941.00   0.296808   0.373365   0.0437138    0.304000    0.373000    0.0426000
+45942.00   0.297631   0.369631   0.0415824    0.305000    0.369000    0.0404000
+45943.00   0.298357   0.366043   0.0395489    0.307000    0.365000    0.0384000
+45944.00   0.298912   0.362592   0.0377174    0.308000    0.362000    0.0367000
+45945.00   0.299411   0.359180   0.0361407    0.309000    0.358000    0.0353000
+45946.00   0.299981   0.355697   0.0348172    0.310000    0.354000    0.0341000
+45947.00   0.300693   0.352060   0.0337007    0.311000    0.350000    0.0330000
+45948.00   0.301537   0.348292   0.0327062    0.312000    0.346000    0.0321000
+45949.00   0.302483   0.344438   0.0317496    0.313000    0.342000    0.0312000
+45950.00   0.303491   0.340541   0.0307616    0.313000    0.338000    0.0302000
+45951.00   0.304482   0.336627   0.0296857    0.314000    0.334000    0.0291000
+45952.00   0.305408   0.332703   0.0284850    0.314000    0.331000    0.0279000
+45953.00   0.306254   0.328765   0.0271466    0.315000    0.327000    0.0266000
+45954.00   0.307012   0.324806   0.0256802    0.315000    0.323000    0.0251000
+45955.00   0.307671   0.320822   0.0241161    0.316000    0.319000    0.0236000
+45956.00   0.308251   0.316811   0.0225070    0.316000    0.315000    0.0220000
+45957.00   0.308790   0.312776   0.0209113    0.316000    0.311000    0.0206000
+45958.00   0.309318   0.308721   0.0193787    0.317000    0.307000    0.0192000
+45959.00   0.309831   0.304651   0.0179451    0.317000    0.303000    0.0179000
+45960.00   0.310317   0.300575   0.0166281    0.317000    0.299000    0.0168000
+45961.00   0.310782   0.296516   0.0154253    0.317000    0.295000    0.0157000
+45962.00   0.311226   0.292500   0.0143055    0.317000    0.291000    0.0147000
+45963.00   0.311631   0.288546   0.0132022    0.317000    0.288000    0.0137000
+45964.00   0.311986   0.284672   0.0120182    0.317000    0.284000    0.0126000
+45965.00   0.312306   0.280886   0.0106403    0.317000    0.280000    0.0112000
+45966.00   0.312596   0.277175   0.0089703    0.317000    0.276000    0.0095000
+45967.00   0.312847   0.273513   0.0069740    0.317000    0.272000    0.0075000
+45968.00   0.313046   0.269870   0.0047065    0.316000    0.268000    0.0052000
+45969.00   0.313180   0.266215   0.0022988    0.316000    0.265000    0.0028000
+45970.00   0.313234   0.262524  -0.0000854    0.315000    0.261000    0.0004000
+45971.00   0.313173   0.258798  -0.0022935    0.315000    0.257000   -0.0017000
+45972.00   0.312953   0.255048  -0.0042383    0.314000    0.253000   -0.0037000
+45973.00   0.312541   0.251287  -0.0059163    0.314000    0.249000   -0.0052000
+45974.00   0.311931   0.247524  -0.0073872    0.313000    0.246000   -0.0066000
+45975.00   0.311122   0.243769  -0.0087419    0.312000    0.242000   -0.0079000
+45976.00   0.310139   0.240020  -0.0100752    0.311000    0.238000   -0.0091000
+45977.00   0.309014   0.236272  -0.0114658    0.311000    0.234000   -0.0105000
+45978.00   0.307765   0.232514  -0.0129651    0.310000    0.231000   -0.0119000
+45979.00   0.306411   0.228737  -0.0145974    0.309000    0.227000   -0.0135000
+45980.00   0.304973   0.224926  -0.0163618    0.308000    0.223000   -0.0152000
+45981.00   0.303439   0.221082  -0.0182367    0.307000    0.219000   -0.0170000
+45982.00   0.301786   0.217214  -0.0201825    0.305000    0.216000   -0.0189000
+45983.00   0.300011   0.213339  -0.0221498    0.304000    0.212000   -0.0208000
+45984.00   0.298118   0.209481  -0.0240852    0.303000    0.209000   -0.0227000
+45985.00   0.296138   0.205665  -0.0259385    0.302000    0.205000   -0.0245000
+45986.00   0.294132   0.201920  -0.0276638    0.300000    0.201000   -0.0261000
+45987.00   0.292159   0.198270  -0.0292338    0.299000    0.198000   -0.0276000
+45988.00   0.290281   0.194738  -0.0306484    0.298000    0.194000   -0.0290000
+45989.00   0.288571   0.191344  -0.0319410    0.296000    0.191000   -0.0303000
+45990.00   0.287079   0.188101  -0.0331759    0.294000    0.187000   -0.0316000
+45991.00   0.285717   0.184988  -0.0344489    0.293000    0.184000   -0.0329000
+45992.00   0.284400   0.181961  -0.0358756    0.291000    0.180000   -0.0343000
+45993.00   0.283074   0.178971  -0.0375672    0.290000    0.177000   -0.0361000
+45994.00   0.281715   0.175971  -0.0395921    0.288000    0.174000   -0.0381000
+45995.00   0.280336   0.172924  -0.0419415    0.286000    0.170000   -0.0405000
+45996.00   0.278889   0.169829  -0.0445100    0.284000    0.167000   -0.0431000
+45997.00   0.277303   0.166697  -0.0471278    0.282000    0.164000   -0.0457000
+45998.00   0.275547   0.163527  -0.0496179    0.280000    0.160000   -0.0482000
+45999.00   0.273605   0.160317  -0.0518502    0.279000    0.157000   -0.0504000
+46000.00   0.271474   0.157067  -0.0537756    0.277000    0.154000   -0.0524000
+46001.00   0.269197   0.153794  -0.0554279    0.274000    0.151000   -0.0541000
+46002.00   0.266819   0.150514  -0.0568970    0.272000    0.148000   -0.0557000
+46003.00   0.264389   0.147245  -0.0582891    0.270000    0.145000   -0.0571000
+46004.00   0.261986   0.144010  -0.0596984    0.268000    0.142000   -0.0586000
+46005.00   0.259695   0.140827  -0.0611907    0.266000    0.139000   -0.0602000
+46006.00   0.257508   0.137697  -0.0628018    0.264000    0.136000   -0.0619000
+46007.00   0.255389   0.134606  -0.0645409    0.261000    0.133000   -0.0638000
+46008.00   0.253304   0.131541  -0.0663943    0.259000    0.130000   -0.0657000
+46009.00   0.251214   0.128501  -0.0683312    0.257000    0.127000   -0.0678000
+46010.00   0.249079   0.125494  -0.0703066    0.254000    0.124000   -0.0698000
+46011.00   0.246833   0.122520  -0.0722578    0.252000    0.121000   -0.0719000
+46012.00   0.244423   0.119576  -0.0741189    0.249000    0.119000   -0.0738000
+46013.00   0.241881   0.116679  -0.0758335    0.246000    0.116000   -0.0756000
+46014.00   0.239258   0.113852  -0.0773639    0.244000    0.113000   -0.0771000
+46015.00   0.236613   0.111116  -0.0787022    0.241000    0.110000   -0.0786000
+46016.00   0.233977   0.108480  -0.0798857    0.238000    0.108000   -0.0798000
+46017.00   0.231360   0.105945  -0.0809883    0.235000    0.105000   -0.0810000
+46018.00   0.228770   0.103517  -0.0821065    0.233000    0.103000   -0.0822000
+46019.00   0.226210   0.101208  -0.0833444    0.230000    0.100000   -0.0835000
+46020.00   0.223677   0.099029  -0.0847991    0.227000    0.098000   -0.0850000
+46021.00   0.221139   0.096966  -0.0865409    0.224000    0.095000   -0.0868000
+46022.00   0.218568   0.094989  -0.0885838    0.221000    0.092000   -0.0888000
+46023.00   0.215938   0.093066  -0.0908741    0.218000    0.090000   -0.0911000
+46024.00   0.213225   0.091166  -0.0932968    0.215000    0.088000   -0.0934000
+46025.00   0.210406   0.089264  -0.0957003    0.211000    0.085000   -0.0957000
+46026.00   0.207423   0.087336  -0.0979370    0.208000    0.083000   -0.0978000
+46027.00   0.204236   0.085360  -0.0999119    0.205000    0.081000   -0.0996000
+46028.00   0.200873   0.083317  -0.1016052    0.202000    0.078000   -0.1012000
+46029.00   0.197372   0.081189  -0.1030670    0.198000    0.076000   -0.1026000
+46030.00   0.193765   0.078961  -0.1043881    0.195000    0.074000   -0.1039000
+46031.00   0.190075   0.076633  -0.1056759    0.192000    0.072000   -0.1052000
+46032.00   0.186314   0.074211  -0.1070185    0.188000    0.069000   -0.1066000
+46033.00   0.182505   0.071705  -0.1084686    0.185000    0.067000   -0.1081000
+46034.00   0.178719   0.069134  -0.1100477    0.181000    0.065000   -0.1098000
+46035.00   0.175026   0.066518  -0.1117507    0.178000    0.063000   -0.1117000
+46036.00   0.171408   0.063884  -0.1135490    0.174000    0.061000   -0.1136000
+46037.00   0.167834   0.061277  -0.1153998    0.170000    0.059000   -0.1156000
+46038.00   0.164269   0.058741  -0.1172522    0.166000    0.057000   -0.1176000
+46039.00   0.160686   0.056322  -0.1190543    0.163000    0.055000   -0.1194000
+46040.00   0.157068   0.054065  -0.1207603    0.159000    0.053000   -0.1211000
+46041.00   0.153378   0.051970  -0.1223307    0.155000    0.051000   -0.1227000
+46042.00   0.149571   0.050022  -0.1237441    0.151000    0.050000   -0.1241000
+46043.00   0.145616   0.048212  -0.1250117    0.147000    0.048000   -0.1253000
+46044.00   0.141486   0.046529  -0.1261785    0.143000    0.046000   -0.1265000
+46045.00   0.137154   0.044964  -0.1273183    0.139000    0.045000   -0.1276000
+46046.00   0.132626   0.043500  -0.1285200    0.135000    0.043000   -0.1287000
+46047.00   0.127941   0.042103  -0.1298686    0.131000    0.042000   -0.1300000
+46048.00   0.123137   0.040739  -0.1314199    0.126000    0.040000   -0.1315000
+46049.00   0.118217   0.039369  -0.1331843    0.122000    0.039000   -0.1333000
+46050.00   0.113171   0.037956  -0.1351183    0.118000    0.037000   -0.1352000
+46051.00   0.108005   0.036506  -0.1371294    0.114000    0.036000   -0.1372000
+46052.00   0.102735   0.035042  -0.1390966    0.110000    0.035000   -0.1392000
+46053.00   0.097379   0.033587  -0.1408996    0.106000    0.034000   -0.1410000
+46054.00   0.091986   0.032161  -0.1424543    0.102000    0.033000   -0.1426000
+46055.00   0.086657   0.030775  -0.1437381    0.097000    0.032000   -0.1439000
+46056.00   0.081481   0.029451  -0.1447949    0.093000    0.031000   -0.1449000
+46057.00   0.076553   0.028198  -0.1457150    0.089000    0.030000   -0.1458000
+46058.00   0.071971   0.027005  -0.1466072    0.085000    0.029000   -0.1468000
+46059.00   0.067706   0.025917  -0.1475790    0.081000    0.028000   -0.1478000
+46060.00   0.063659   0.024995  -0.1487065    0.076000    0.028000   -0.1489000
+46061.00   0.059795   0.024242  -0.1500226    0.072000    0.027000   -0.1503000
+46062.00   0.056087   0.023657  -0.1515275    0.068000    0.026000   -0.1517000
+46063.00   0.052510   0.023235  -0.1531963    0.064000    0.026000   -0.1533000
+46064.00   0.049040   0.022967  -0.1549845    0.060000    0.025000   -0.1550000
+46065.00   0.045647   0.022839  -0.1568370    0.055000    0.025000   -0.1568000
+46066.00   0.042277   0.022841  -0.1586909    0.051000    0.025000   -0.1585000
+46067.00   0.038867   0.022960  -0.1604870    0.047000    0.025000   -0.1601000
+46068.00   0.035355   0.023183  -0.1621783    0.043000    0.025000   -0.1617000
+46069.00   0.031690   0.023492  -0.1637360    0.039000    0.025000   -0.1631000
+46070.00   0.027848   0.023864  -0.1651577    0.035000    0.025000   -0.1645000
+46071.00   0.023839   0.024297  -0.1664779    0.030000    0.025000   -0.1657000
+46072.00   0.019696   0.024802  -0.1677671    0.026000    0.025000   -0.1670000
+46073.00   0.015499   0.025390  -0.1691195    0.022000    0.025000   -0.1683000
+46074.00   0.011336   0.026070  -0.1706301    0.018000    0.025000   -0.1699000
+46075.00   0.007290   0.026845  -0.1723661    0.014000    0.026000   -0.1716000
+46076.00   0.003381   0.027703  -0.1743467    0.010000    0.027000   -0.1736000
+46077.00  -0.000400   0.028635  -0.1765293    0.006000    0.027000   -0.1758000
+46078.00  -0.004039   0.029631  -0.1788167    0.003000    0.028000   -0.1781000
+46079.00  -0.007510   0.030685  -0.1810844   -0.001000    0.029000   -0.1804000
+46080.00  -0.010793   0.031794  -0.1832128   -0.005000    0.029000   -0.1825000
+46081.00  -0.013922   0.032971  -0.1851106   -0.009000    0.030000   -0.1844000
+46082.00  -0.016965   0.034224  -0.1867379   -0.013000    0.031000   -0.1861000
+46083.00  -0.019994   0.035558  -0.1881139   -0.016000    0.032000   -0.1875000
+46084.00  -0.023076   0.036972  -0.1893057   -0.020000    0.033000   -0.1888000
+46085.00  -0.026261   0.038429  -0.1904083   -0.024000    0.034000   -0.1900000
+46086.00  -0.029573   0.039922  -0.1915209   -0.027000    0.035000   -0.1912000
+46087.00  -0.033034   0.041461  -0.1927203   -0.031000    0.036000   -0.1925000
+46088.00  -0.036654   0.043045  -0.1940474   -0.034000    0.037000   -0.1939000
+46089.00  -0.040417   0.044650  -0.1955065   -0.037000    0.039000   -0.1955000
+46090.00  -0.044273   0.046244  -0.1970727   -0.041000    0.040000   -0.1971000
+46091.00  -0.048135   0.047808  -0.1987025   -0.044000    0.041000   -0.1987000
+46092.00  -0.051928   0.049331  -0.2003427   -0.047000    0.043000   -0.2003000
+46093.00  -0.055578   0.050805  -0.2019384   -0.050000    0.044000   -0.2018000
+46094.00  -0.059013   0.052219  -0.2034409   -0.053000    0.046000   -0.2032000
+46095.00  -0.062163   0.053572  -0.2048137   -0.056000    0.048000   -0.2044000
+46096.00  -0.065008   0.054912  -0.2060277   -0.060000    0.049000   -0.2055000
+46097.00  -0.067595   0.056280  -0.2070748   -0.063000    0.051000   -0.2064000
+46098.00  -0.069992   0.057704  -0.2079760   -0.066000    0.053000   -0.2072000
+46099.00  -0.072265   0.059200  -0.2087876   -0.068000    0.055000   -0.2080000
+46100.00  -0.074472   0.060763  -0.2096027   -0.072000    0.057000   -0.2089000
+46101.00  -0.076633   0.062402  -0.2105400   -0.075000    0.059000   -0.2101000
+46102.00  -0.078755   0.064139  -0.2117058   -0.078000    0.061000   -0.2115000
+46103.00  -0.080874   0.066000  -0.2131574   -0.082000    0.063000   -0.2132000
+46104.00  -0.083041   0.068013  -0.2148787   -0.085000    0.065000   -0.2151000
+46105.00  -0.085300   0.070194  -0.2167819   -0.088000    0.067000   -0.2172000
+46106.00  -0.087660   0.072510  -0.2187311   -0.091000    0.069000   -0.2193000
+46107.00  -0.090126   0.074917  -0.2205888   -0.094000    0.071000   -0.2212000
+46108.00  -0.092701   0.077370  -0.2222578   -0.097000    0.073000   -0.2230000
+46109.00  -0.095382   0.079814  -0.2237016   -0.100000    0.075000   -0.2244000
+46110.00  -0.098160   0.082196  -0.2249468   -0.103000    0.077000   -0.2258000
+46111.00  -0.101037   0.084501  -0.2260661   -0.106000    0.080000   -0.2269000
+46112.00  -0.104051   0.086729  -0.2271542   -0.109000    0.082000   -0.2280000
+46113.00  -0.107245   0.088881  -0.2283057   -0.112000    0.084000   -0.2291000
+46114.00  -0.110647   0.090957  -0.2295951   -0.114000    0.086000   -0.2304000
+46115.00  -0.114250   0.092963  -0.2310654   -0.117000    0.088000   -0.2316000
+46116.00  -0.118004   0.094931  -0.2327217   -0.119000    0.091000   -0.2331000
+46117.00  -0.121853   0.096905  -0.2345343   -0.122000    0.093000   -0.2347000
+46118.00  -0.125726   0.098912  -0.2364516   -0.125000    0.096000   -0.2364000
+46119.00  -0.129547   0.100973  -0.2384076   -0.127000    0.098000   -0.2382000
+46120.00  -0.133243   0.103110  -0.2403331   -0.130000    0.101000   -0.2400000
+46121.00  -0.136777   0.105344  -0.2421679   -0.132000    0.103000   -0.2418000
+46122.00  -0.140144   0.107701  -0.2438655   -0.135000    0.106000   -0.2435000
+46123.00  -0.143341   0.110196  -0.2453988   -0.138000    0.109000   -0.2450000
+46124.00  -0.146379   0.112815  -0.2467622   -0.140000    0.111000   -0.2464000
+46125.00  -0.149266   0.115542  -0.2479762   -0.143000    0.114000   -0.2477000
+46126.00  -0.152035   0.118359  -0.2490984   -0.145000    0.117000   -0.2489000
+46127.00  -0.154732   0.121245  -0.2502217   -0.148000    0.119000   -0.2501000
+46128.00  -0.157401   0.124178  -0.2514646   -0.150000    0.122000   -0.2514000
+46129.00  -0.160087   0.127139  -0.2529506   -0.153000    0.125000   -0.2530000
+46130.00  -0.162838   0.130105  -0.2547729   -0.155000    0.128000   -0.2549000
+46131.00  -0.165701   0.133042  -0.2569614   -0.158000    0.131000   -0.2572000
+46132.00  -0.168632   0.135964  -0.2594436   -0.160000    0.134000   -0.2597000
+46133.00  -0.171568   0.138910  -0.2620707   -0.162000    0.137000   -0.2623000
+46134.00  -0.174442   0.141921  -0.2646679   -0.164000    0.141000   -0.2649000
+46135.00  -0.177192   0.145035  -0.2670902   -0.166000    0.144000   -0.2673000
+46136.00  -0.179774   0.148284  -0.2692583   -0.168000    0.147000   -0.2693000
+46137.00  -0.182174   0.151687  -0.2711697   -0.170000    0.150000   -0.2712000
+46138.00  -0.184390   0.155262  -0.2728870   -0.172000    0.154000   -0.2728000
+46139.00  -0.186475   0.159014  -0.2745066   -0.174000    0.157000   -0.2743000
+46140.00  -0.188489   0.162944  -0.2761266   -0.176000    0.161000   -0.2759000
+46141.00  -0.190425   0.167016  -0.2778277   -0.178000    0.164000   -0.2775000
+46142.00  -0.192241   0.171174  -0.2796601   -0.179000    0.168000   -0.2793000
+46143.00  -0.193893   0.175364  -0.2816404   -0.181000    0.171000   -0.2813000
+46144.00  -0.195330   0.179529  -0.2837540   -0.182000    0.175000   -0.2834000
+46145.00  -0.196459   0.183619  -0.2859606   -0.184000    0.178000   -0.2856000
+46146.00  -0.197257   0.187641  -0.2882065   -0.185000    0.182000   -0.2879000
+46147.00  -0.197770   0.191620  -0.2904336   -0.187000    0.186000   -0.2901000
+46148.00  -0.198078   0.195575  -0.2925881   -0.188000    0.189000   -0.2923000
+46149.00  -0.198263   0.199521  -0.2946278   -0.189000    0.193000   -0.2944000
+46150.00  -0.198402   0.203478  -0.2965273   -0.190000    0.197000   -0.2963000
+46151.00  -0.198546   0.207460  -0.2982722   -0.192000    0.200000   -0.2980000
+46152.00  -0.198758   0.211475  -0.2998687   -0.192000    0.204000   -0.2996000
+46153.00  -0.199088   0.215521  -0.3013471   -0.193000    0.208000   -0.3011000
+46154.00  -0.199546   0.219579  -0.3027625   -0.194000    0.212000   -0.3026000
+46155.00  -0.200125   0.223631  -0.3041967   -0.195000    0.216000   -0.3041000
+46156.00  -0.200799   0.227676  -0.3057583   -0.196000    0.220000   -0.3057000
+46157.00  -0.201506   0.231711  -0.3075487   -0.197000    0.223000   -0.3077000
+46158.00  -0.202157   0.235725  -0.3096280   -0.197000    0.227000   -0.3099000
+46159.00  -0.202656   0.239703  -0.3119810   -0.198000    0.231000   -0.3124000
+46160.00  -0.202894   0.243632  -0.3145091   -0.198000    0.235000   -0.3151000
+46161.00  -0.202887   0.247510  -0.3170467   -0.198000    0.239000   -0.3177000
+46162.00  -0.202694   0.251352  -0.3194193   -0.198000    0.243000   -0.3202000
+46163.00  -0.202363   0.255174  -0.3215064   -0.198000    0.247000   -0.3224000
+46164.00  -0.201945   0.258993  -0.3232768   -0.198000    0.251000   -0.3242000
+46165.00  -0.201479   0.262823  -0.3247880   -0.198000    0.255000   -0.3258000
+46166.00  -0.200979   0.266659  -0.3261551   -0.198000    0.259000   -0.3272000
+46167.00  -0.200452   0.270482  -0.3275061   -0.198000    0.263000   -0.3286000
+46168.00  -0.199907   0.274270  -0.3289482   -0.198000    0.267000   -0.3300000
+46169.00  -0.199368   0.277987  -0.3305500   -0.198000    0.271000   -0.3316000
+46170.00  -0.198855   0.281599  -0.3323403   -0.198000    0.275000   -0.3333000
+46171.00  -0.198376   0.285115  -0.3343101   -0.198000    0.279000   -0.3352000
+46172.00  -0.197935   0.288574  -0.3364221   -0.197000    0.283000   -0.3372000
+46173.00  -0.197529   0.292023  -0.3386206   -0.197000    0.286000   -0.3393000
+46174.00  -0.197147   0.295507  -0.3408390   -0.197000    0.290000   -0.3414000
+46175.00  -0.196748   0.299057  -0.3430114   -0.196000    0.294000   -0.3434000
+46176.00  -0.196341   0.302665  -0.3450778   -0.196000    0.298000   -0.3454000
+46177.00  -0.195970   0.306310  -0.3469911   -0.195000    0.302000   -0.3472000
+46178.00  -0.195653   0.309964  -0.3487283   -0.194000    0.305000   -0.3489000
+46179.00  -0.195403   0.313600  -0.3502938   -0.193000    0.309000   -0.3504000
+46180.00  -0.195218   0.317194  -0.3517206   -0.193000    0.313000   -0.3518000
+46181.00  -0.195070   0.320749  -0.3530704   -0.192000    0.316000   -0.3532000
+46182.00  -0.194934   0.324277  -0.3544233   -0.191000    0.320000   -0.3546000
+46183.00  -0.194754   0.327794  -0.3558732   -0.190000    0.324000   -0.3561000
+46184.00  -0.194430   0.331318  -0.3575151   -0.189000    0.327000   -0.3578000
+46185.00  -0.193875   0.334866  -0.3594226   -0.188000    0.331000   -0.3599000
+46186.00  -0.193064   0.338446  -0.3616246   -0.187000    0.335000   -0.3622000
+46187.00  -0.192022   0.342059  -0.3640725   -0.185000    0.338000   -0.3648000
+46188.00  -0.190796   0.345705  -0.3666421   -0.184000    0.342000   -0.3675000
+46189.00  -0.189423   0.349371  -0.3691694   -0.183000    0.345000   -0.3702000
+46190.00  -0.187921   0.353018  -0.3715021   -0.181000    0.349000   -0.3725000
+46191.00  -0.186343   0.356639  -0.3735395   -0.180000    0.352000   -0.3745000
+46192.00  -0.184758   0.360244  -0.3752701   -0.178000    0.355000   -0.3761000
+46193.00  -0.183180   0.363836  -0.3767639   -0.177000    0.359000   -0.3775000
+46194.00  -0.181540   0.367403  -0.3781335   -0.175000    0.362000   -0.3788000
+46195.00  -0.179759   0.370913  -0.3794931   -0.174000    0.365000   -0.3801000
+46196.00  -0.177855   0.374344  -0.3809154   -0.172000    0.369000   -0.3815000
+46197.00  -0.175860   0.377706  -0.3824452   -0.170000    0.372000   -0.3830000
+46198.00  -0.173806   0.381008  -0.3840934   -0.169000    0.375000   -0.3847000
+46199.00  -0.171735   0.384244  -0.3858435   -0.167000    0.378000   -0.3864000
+46200.00  -0.169684   0.387395  -0.3876593   -0.165000    0.382000   -0.3883000
+46201.00  -0.167665   0.390431  -0.3894825   -0.163000    0.385000   -0.3901000
+46202.00  -0.165657   0.393334  -0.3912474   -0.161000    0.388000   -0.3918000
+46203.00  -0.163573   0.396137  -0.3928999   -0.159000    0.391000   -0.3935000
+46204.00  -0.161262   0.398882  -0.3943990   -0.157000    0.394000   -0.3950000
+46205.00  -0.158584   0.401605  -0.3957248   -0.155000    0.397000   -0.3964000
+46206.00  -0.155578   0.404325  -0.3968812   -0.153000    0.400000   -0.3975000
+46207.00  -0.152367   0.407057  -0.3978971   -0.151000    0.403000   -0.3986000
+46208.00  -0.149070   0.409800  -0.3988270   -0.149000    0.406000   -0.3995000
+46209.00  -0.145811   0.412542  -0.3997466   -0.147000    0.409000   -0.4005000
+46210.00  -0.142687   0.415266  -0.4007451   -0.144000    0.412000   -0.4015000
+46211.00  -0.139715   0.417959  -0.4019197   -0.142000    0.415000   -0.4027000
+46212.00  -0.136898   0.420617  -0.4033516   -0.140000    0.418000   -0.4042000
+46213.00  -0.134239   0.423234  -0.4050843   -0.137000    0.420000   -0.4059000
+46214.00  -0.131710   0.425794  -0.4071018   -0.135000    0.423000   -0.4079000
+46215.00  -0.129268   0.428277  -0.4093187   -0.132000    0.426000   -0.4100000
+46216.00  -0.126862   0.430697  -0.4115847   -0.130000    0.429000   -0.4122000
+46217.00  -0.124466   0.433064  -0.4137295   -0.127000    0.431000   -0.4142000
+46218.00  -0.122062   0.435383  -0.4156205   -0.125000    0.434000   -0.4159000
+46219.00  -0.119627   0.437662  -0.4172008   -0.122000    0.436000   -0.4174000
+46220.00  -0.117138   0.439906  -0.4185018   -0.119000    0.439000   -0.4186000
+46221.00  -0.114629   0.442113  -0.4196298   -0.117000    0.441000   -0.4198000
+46222.00  -0.112156   0.444281  -0.4207131   -0.114000    0.443000   -0.4209000
+46223.00  -0.109765   0.446419  -0.4218551   -0.112000    0.445000   -0.4221000
+46224.00  -0.107492   0.448543  -0.4231118   -0.109000    0.447000   -0.4234000
+46225.00  -0.105357   0.450662  -0.4244918   -0.106000    0.449000   -0.4248000
+46226.00  -0.103325   0.452758  -0.4259725   -0.104000    0.451000   -0.4263000
+46227.00  -0.101352   0.454805  -0.4275097   -0.101000    0.453000   -0.4279000
+46228.00  -0.099393   0.456780  -0.4290493   -0.098000    0.455000   -0.4295000
+46229.00  -0.097404   0.458668  -0.4305347   -0.095000    0.456000   -0.4310000
+46230.00  -0.095335   0.460461  -0.4319136   -0.093000    0.458000   -0.4324000
+46231.00  -0.093149   0.462163  -0.4331459   -0.090000    0.460000   -0.4337000
+46232.00  -0.090825   0.463779  -0.4342128   -0.087000    0.462000   -0.4348000
+46233.00  -0.088349   0.465312  -0.4351203   -0.084000    0.464000   -0.4358000
+46234.00  -0.085711   0.466761  -0.4359015   -0.082000    0.465000   -0.4365000
+46235.00  -0.082936   0.468118  -0.4366149   -0.079000    0.467000   -0.4372000
+46236.00  -0.080122   0.469408  -0.4373028   -0.076000    0.468000   -0.4378000
+46237.00  -0.077361   0.470667  -0.4380153   -0.073000    0.470000   -0.4385000
+46238.00  -0.074610   0.471903  -0.4388551   -0.070000    0.471000   -0.4393000
+46239.00  -0.071793   0.473116  -0.4399191   -0.067000    0.473000   -0.4403000
+46240.00  -0.068839   0.474303  -0.4412630   -0.064000    0.474000   -0.4416000
+46241.00  -0.065753   0.475472  -0.4428618   -0.062000    0.475000   -0.4431000
+46242.00  -0.062576   0.476631  -0.4446257   -0.058000    0.477000   -0.4447000
+46243.00  -0.059354   0.477788  -0.4464248   -0.056000    0.478000   -0.4463000
+46244.00  -0.056153   0.478938  -0.4481124   -0.053000    0.479000   -0.4478000
+46245.00  -0.053034   0.480065  -0.4495582   -0.050000    0.480000   -0.4491000
+46246.00  -0.050009   0.481156  -0.4506836   -0.047000    0.481000   -0.4501000
+46247.00  -0.047069   0.482203   0.5485152   -0.044000    0.483000    0.5492000
+46248.00  -0.044201   0.483202   0.5479681   -0.041000    0.484000    0.5486000
+46249.00  -0.041392   0.484143   0.5475631   -0.038000    0.484000    0.5482000
+46250.00  -0.038612   0.485025   0.5471829   -0.034000    0.485000    0.5477000
+46251.00  -0.035784   0.485851   0.5467352   -0.031000    0.486000    0.5472000
+46252.00  -0.032827   0.486625   0.5461737   -0.028000    0.487000    0.5465000
+46253.00  -0.029687   0.487346   0.5455003   -0.025000    0.488000    0.5458000
+46254.00  -0.026333   0.488016   0.5447512   -0.022000    0.489000    0.5449000
+46255.00  -0.022800   0.488669   0.5439789   -0.019000    0.489000    0.5440000
+46256.00  -0.019130   0.489314   0.5432315   -0.016000    0.490000    0.5431000
+46257.00  -0.015371   0.489951   0.5425526   -0.013000    0.490000    0.5423000
+46258.00  -0.011572   0.490575   0.5419778   -0.010000    0.491000    0.5416000
+46259.00  -0.007792   0.491171   0.5415318   -0.006000    0.492000    0.5411000
+46260.00  -0.004082   0.491721   0.5412161   -0.003000    0.492000    0.5408000
+46261.00  -0.000464   0.492239   0.5410125    0.000000    0.493000    0.5405000
+46262.00   0.003063   0.492745   0.5408855    0.003000    0.493000    0.5404000
+46263.00   0.006507   0.493256   0.5407766    0.006000    0.493000    0.5403000
+46264.00   0.009872   0.493788   0.5406063    0.010000    0.494000    0.5401000
+46265.00   0.013176   0.494340   0.5402860    0.013000    0.494000    0.5398000
+46266.00   0.016429   0.494873   0.5397323    0.016000    0.494000    0.5393000
+46267.00   0.019647   0.495332   0.5389012    0.019000    0.495000    0.5386000
+46268.00   0.022886   0.495665   0.5378134    0.023000    0.495000    0.5375000
+46269.00   0.026158   0.495886   0.5365587    0.026000    0.495000    0.5363000
+46270.00   0.029459   0.496025   0.5352637    0.029000    0.495000    0.5351000
+46271.00   0.032763   0.496114   0.5340619    0.033000    0.495000    0.5339000
+46272.00   0.036041   0.496176   0.5330642    0.036000    0.495000    0.5329000
+46273.00   0.039272   0.496233   0.5323336    0.039000    0.495000    0.5321000
+46274.00   0.042463   0.496285   0.5318666    0.043000    0.495000    0.5316000
+46275.00   0.045629   0.496323   0.5315935    0.046000    0.495000    0.5313000
+46276.00   0.048765   0.496335   0.5313965    0.049000    0.495000    0.5310000
+46277.00   0.051872   0.496310   0.5311553    0.053000    0.495000    0.5307000
+46278.00   0.054974   0.496231   0.5307762    0.056000    0.495000    0.5303000
+46279.00   0.058100   0.496081   0.5302084    0.059000    0.495000    0.5297000
+46280.00   0.061323   0.495838   0.5294499    0.063000    0.494000    0.5289000
+46281.00   0.064658   0.495523   0.5285432    0.066000    0.494000    0.5280000
+46282.00   0.068084   0.495159   0.5275565    0.069000    0.494000    0.5271000
+46283.00   0.071580   0.494755   0.5265648    0.073000    0.493000    0.5261000
+46284.00   0.075122   0.494320   0.5256392    0.076000    0.493000    0.5252000
+46285.00   0.078697   0.493855   0.5248374    0.080000    0.493000    0.5244000
+46286.00   0.082279   0.493336   0.5241943    0.083000    0.492000    0.5237000
+46287.00   0.085829   0.492728   0.5237231    0.086000    0.492000    0.5232000
+46288.00   0.089317   0.491994   0.5234146    0.090000    0.491000    0.5229000
+46289.00   0.092722   0.491108   0.5232339    0.093000    0.491000    0.5226000
+46290.00   0.096012   0.490110   0.5231072    0.096000    0.490000    0.5224000
+46291.00   0.099202   0.489066   0.5229433    0.099000    0.489000    0.5222000
+46292.00   0.102330   0.488030   0.5226398    0.103000    0.488000    0.5219000
+46293.00   0.105445   0.487036   0.5220976    0.106000    0.488000    0.5213000
+46294.00   0.108604   0.486110   0.5212499    0.109000    0.487000    0.5205000
+46295.00   0.111863   0.485251   0.5200893    0.112000    0.486000    0.5194000
+46296.00   0.115192   0.484438   0.5186855    0.115000    0.485000    0.5180000
+46297.00   0.118524   0.483639   0.5171687    0.118000    0.484000    0.5166000
+46298.00   0.121791   0.482814   0.5156920    0.121000    0.483000    0.5152000
+46299.00   0.124926   0.481908   0.5143879    0.124000    0.482000    0.5140000
+46300.00   0.127884   0.480877   0.5133339    0.127000    0.481000    0.5130000
+46301.00   0.130691   0.479741   0.5125465    0.130000    0.479000    0.5123000
+46302.00   0.133380   0.478546   0.5119814    0.133000    0.478000    0.5117000
+46303.00   0.136005   0.477310   0.5115435    0.136000    0.477000    0.5113000
+46304.00   0.138660   0.475975   0.5111038    0.139000    0.476000    0.5108000
+46305.00   0.141430   0.474480   0.5105460    0.142000    0.474000    0.5102000
+46306.00   0.144270   0.472834   0.5098009    0.145000    0.473000    0.5094000
+46307.00   0.147121   0.471079   0.5088480    0.148000    0.471000    0.5084000
+46308.00   0.149942   0.469255   0.5077043    0.150000    0.470000    0.5072000
+46309.00   0.152721   0.467398   0.5064174    0.153000    0.468000    0.5060000
+46310.00   0.155498   0.465536   0.5050530    0.156000    0.467000    0.5047000
+46311.00   0.158276   0.463694   0.5036912    0.159000    0.465000    0.5035000
+46312.00   0.161038   0.461899   0.5024070    0.161000    0.464000    0.5023000
+46313.00   0.163789   0.460177   0.5012528    0.164000    0.462000    0.5013000
+46314.00   0.166536   0.458544   0.5002573    0.166000    0.460000    0.5005000
+46315.00   0.169227   0.456971   0.4994287    0.169000    0.458000    0.4997000
+46316.00   0.171822   0.455409   0.4987417    0.171000    0.456000    0.4991000
+46317.00   0.174282   0.453812   0.4981437    0.174000    0.455000    0.4985000
+46318.00   0.176573   0.452131   0.4975589    0.176000    0.453000    0.4979000
+46319.00   0.178677   0.450315   0.4968903    0.178000    0.451000    0.4971000
+46320.00   0.180611   0.448316   0.4960317    0.181000    0.449000    0.4962000
+46321.00   0.182398   0.446159   0.4948855    0.183000    0.447000    0.4949000
+46322.00   0.184053   0.443888   0.4934018    0.185000    0.445000    0.4934000
+46323.00   0.185592   0.441539   0.4916065    0.187000    0.442000    0.4915000
+46324.00   0.187053   0.439148   0.4896077    0.189000    0.440000    0.4894000
+46325.00   0.188508   0.436747   0.4875697    0.191000    0.438000    0.4873000
+46326.00   0.189998   0.434355   0.4856603    0.193000    0.436000    0.4854000
+46327.00   0.191546   0.431996   0.4839986    0.195000    0.434000    0.4837000
+46328.00   0.193176   0.429680   0.4826222    0.197000    0.431000    0.4823000
+46329.00   0.194912   0.427417   0.4814891    0.199000    0.429000    0.4812000
+46330.00   0.196776   0.425207   0.4805008    0.200000    0.427000    0.4802000
+46331.00   0.198726   0.423023   0.4795289    0.202000    0.424000    0.4791000
+46332.00   0.200685   0.420838   0.4784586    0.204000    0.422000    0.4780000
+46333.00   0.202577   0.418621   0.4772123    0.205000    0.419000    0.4767000
+46334.00   0.204362   0.416327   0.4757559    0.207000    0.417000    0.4752000
+46335.00   0.206018   0.413909   0.4740976    0.208000    0.414000    0.4736000
+46336.00   0.207522   0.411358   0.4722834    0.210000    0.412000    0.4718000
+46337.00   0.208873   0.408682   0.4703842    0.211000    0.409000    0.4699000
+46338.00   0.210079   0.405896   0.4684802    0.212000    0.406000    0.4681000
+46339.00   0.211149   0.403012   0.4666470    0.214000    0.404000    0.4663000
+46340.00   0.212122   0.400053   0.4649418    0.215000    0.401000    0.4646000
+46341.00   0.213067   0.397048   0.4633893    0.216000    0.398000    0.4631000
+46342.00   0.214036   0.394029   0.4619896    0.217000    0.395000    0.4617000
+46343.00   0.215075   0.391019   0.4607163    0.219000    0.392000    0.4604000
+46344.00   0.216228   0.388042   0.4595225    0.220000    0.390000    0.4592000
+46345.00   0.217527   0.385111   0.4583458    0.220000    0.387000    0.4580000
+46346.00   0.218911   0.382221   0.4571011    0.221000    0.384000    0.4568000
+46347.00   0.220280   0.379362   0.4556925    0.222000    0.381000    0.4554000
+46348.00   0.221542   0.376513   0.4540317    0.223000    0.378000    0.4538000
+46349.00   0.222619   0.373620   0.4520607    0.224000    0.375000    0.4518000
+46350.00   0.223455   0.370637   0.4497784    0.225000    0.372000    0.4496000
+46351.00   0.224080   0.367587   0.4472576    0.226000    0.369000    0.4471000
+46352.00   0.224562   0.364512   0.4446451    0.226000    0.366000    0.4444000
+46353.00   0.224966   0.361454   0.4421210    0.227000    0.363000    0.4419000
+46354.00   0.225359   0.358452   0.4398379    0.228000    0.360000    0.4396000
+46355.00   0.225798   0.355520   0.4378696    0.228000    0.358000    0.4376000
+46356.00   0.226285   0.352648   0.4361921    0.229000    0.355000    0.4359000
+46357.00   0.226795   0.349824   0.4347040    0.229000    0.352000    0.4344000
+46358.00   0.227307   0.347014   0.4332664    0.230000    0.349000    0.4329000
+46359.00   0.227800   0.344182   0.4317540    0.230000    0.346000    0.4314000
+46360.00   0.228257   0.341300   0.4300833    0.231000    0.343000    0.4297000
+46361.00   0.228671   0.338380   0.4282195    0.231000    0.340000    0.4279000
+46362.00   0.229030   0.335455   0.4261722    0.231000    0.337000    0.4258000
+46363.00   0.229326   0.332557   0.4239808    0.232000    0.334000    0.4237000
+46364.00   0.229560   0.329701   0.4217036    0.232000    0.331000    0.4215000
+46365.00   0.229736   0.326901   0.4194097    0.232000    0.328000    0.4192000
+46366.00   0.229840   0.324156   0.4171761    0.232000    0.326000    0.4171000
+46367.00   0.229859   0.321447   0.4150728    0.232000    0.323000    0.4150000
+46368.00   0.229785   0.318755   0.4131510    0.233000    0.320000    0.4132000
+46369.00   0.229620   0.316060   0.4114356    0.233000    0.317000    0.4115000
+46370.00   0.229408   0.313351   0.4099157    0.233000    0.314000    0.4100000
+46371.00   0.229171   0.310637   0.4085510    0.233000    0.312000    0.4086000
+46372.00   0.228924   0.307937   0.4072826    0.233000    0.309000    0.4073000
+46373.00   0.228693   0.305271   0.4060380    0.233000    0.306000    0.4060000
+46374.00   0.228504   0.302658   0.4047364    0.232000    0.303000    0.4046000
+46375.00   0.228386   0.300114   0.4032939    0.232000    0.301000    0.4030000
+46376.00   0.228334   0.297638   0.4016286    0.232000    0.298000    0.4012000
+46377.00   0.228320   0.295220   0.3996907    0.232000    0.295000    0.3990000
+46378.00   0.228317   0.292843   0.3974904    0.232000    0.293000    0.3967000
+46379.00   0.228298   0.290458   0.3951109    0.232000    0.290000    0.3942000
+46380.00   0.228247   0.288015   0.3926938    0.231000    0.288000    0.3917000
+46381.00   0.228135   0.285484   0.3904061    0.231000    0.285000    0.3893000
+46382.00   0.227954   0.282861   0.3883798    0.231000    0.282000    0.3873000
+46383.00   0.227722   0.280152   0.3866578    0.230000    0.280000    0.3855000
+46384.00   0.227464   0.277366   0.3851853    0.230000    0.277000    0.3841000
+46385.00   0.227247   0.274515   0.3838348    0.230000    0.275000    0.3827000
+46386.00   0.227107   0.271641   0.3824592    0.229000    0.272000    0.3814000
+46387.00   0.227067   0.268788   0.3809433    0.229000    0.270000    0.3799000
+46388.00   0.227161   0.265981   0.3792292    0.229000    0.267000    0.3783000
+46389.00   0.227422   0.263244   0.3773162    0.228000    0.265000    0.3765000
+46390.00   0.227857   0.260599   0.3752469    0.228000    0.262000    0.3746000
+46391.00   0.228289   0.258093   0.3731068    0.227000    0.260000    0.3726000
+46392.00   0.228513   0.255777   0.3709919    0.227000    0.257000    0.3705000
+46393.00   0.228522   0.253611   0.3689618    0.226000    0.255000    0.3686000
+46394.00   0.228351   0.251535   0.3670608    0.226000    0.252000    0.3667000
+46395.00   0.228034   0.249492   0.3653185    0.225000    0.250000    0.3650000
+46396.00   0.227578   0.247438   0.3637506    0.225000    0.247000    0.3635000
+46397.00   0.226974   0.245348   0.3623496    0.224000    0.245000    0.3622000
+46398.00   0.226214   0.243204   0.3610822    0.224000    0.243000    0.3609000
+46399.00   0.225289   0.240987   0.3598928    0.223000    0.240000    0.3598000
+46400.00   0.224203   0.238690   0.3587090    0.222000    0.238000    0.3586000
+46401.00   0.222985   0.236330   0.3574534    0.222000    0.235000    0.3574000
+46402.00   0.221664   0.233938   0.3560531    0.221000    0.233000    0.3560000
+46403.00   0.220295   0.231542   0.3544448    0.220000    0.231000    0.3544000
+46404.00   0.218965   0.229164   0.3525858    0.219000    0.228000    0.3526000
+46405.00   0.217750   0.226823   0.3504737    0.219000    0.226000    0.3505000
+46406.00   0.216636   0.224519   0.3481682    0.218000    0.224000    0.3482000
+46407.00   0.215563   0.222242   0.3457903    0.217000    0.221000    0.3459000
+46408.00   0.214475   0.219989   0.3434927    0.216000    0.219000    0.3436000
+46409.00   0.213331   0.217755   0.3414142    0.215000    0.217000    0.3416000
+46410.00   0.212100   0.215538   0.3396322    0.215000    0.215000    0.3398000
+46411.00   0.210811   0.213345   0.3381405    0.214000    0.213000    0.3383000
+46412.00   0.209523   0.211181   0.3368527    0.212000    0.211000    0.3371000
+46413.00   0.208303   0.209052   0.3356368    0.211000    0.208000    0.3358000
+46414.00   0.207217   0.206963   0.3343657    0.210000    0.206000    0.3345000
+46415.00   0.206324   0.204913   0.3329571    0.209000    0.204000    0.3331000
+46416.00   0.205587   0.202891   0.3313867    0.208000    0.202000    0.3315000
+46417.00   0.204918   0.200878   0.3296829    0.207000    0.200000    0.3297000
+46418.00   0.204245   0.198834   0.3279056    0.206000    0.198000    0.3279000
+46419.00   0.203505   0.196704   0.3261229    0.205000    0.196000    0.3261000
+46420.00   0.202651   0.194441   0.3243953    0.203000    0.194000    0.3244000
+46421.00   0.201682   0.192067   0.3227800    0.202000    0.192000    0.3228000
+46422.00   0.200599   0.189640   0.3213244    0.201000    0.190000    0.3213000
+46423.00   0.199401   0.187217   0.3200557    0.199000    0.188000    0.3201000
+46424.00   0.198074   0.184830   0.3189776    0.198000    0.186000    0.3191000
+46425.00   0.196602   0.182503   0.3180691    0.196000    0.184000    0.3182000
+46426.00   0.194969   0.180259   0.3172844    0.195000    0.182000    0.3174000
+46427.00   0.193183   0.178098   0.3165573    0.193000    0.180000    0.3167000
+46428.00   0.191268   0.176008   0.3158105    0.192000    0.178000    0.3159000
+46429.00   0.189274   0.173979   0.3149682    0.190000    0.176000    0.3150000
+46430.00   0.187278   0.172001   0.3139669    0.188000    0.174000    0.3139000
+46431.00   0.185312   0.170079   0.3127601    0.187000    0.172000    0.3126000
+46432.00   0.183379   0.168223   0.3113387    0.185000    0.170000    0.3111000
+46433.00   0.181454   0.166418   0.3097389    0.183000    0.169000    0.3094000
+46434.00   0.179503   0.164642   0.3080397    0.181000    0.167000    0.3076000
+46435.00   0.177500   0.162872   0.3063498    0.179000    0.165000    0.3058000
+46436.00   0.175449   0.161098   0.3047858    0.177000    0.163000    0.3042000
+46437.00   0.173353   0.159317   0.3034362    0.175000    0.161000    0.3028000
+46438.00   0.171218   0.157523   0.3023221    0.173000    0.159000    0.3017000
+46439.00   0.169057   0.155692   0.3013826    0.171000    0.157000    0.3008000
+46440.00   0.166891   0.153788   0.3004973    0.169000    0.155000    0.2999000
+46441.00   0.164758   0.151817   0.2995308    0.168000    0.153000    0.2990000
+46442.00   0.162690   0.149796   0.2983744    0.165000    0.151000    0.2979000
+46443.00   0.160722   0.147740   0.2969782    0.164000    0.149000    0.2966000
+46444.00   0.158878   0.145663   0.2953575    0.161000    0.147000    0.2950000
+46445.00   0.157141   0.143587   0.2935775    0.159000    0.145000    0.2933000
+46446.00   0.155372   0.141583   0.2917394    0.157000    0.144000    0.2916000
+46447.00   0.153408   0.139731   0.2899452    0.155000    0.142000    0.2898000
+46448.00   0.151200   0.138054   0.2882630    0.153000    0.141000    0.2882000
+46449.00   0.148727   0.136557   0.2867365    0.150000    0.139000    0.2867000
+46450.00   0.145986   0.135246   0.2853861    0.148000    0.137000    0.2853000
+46451.00   0.143091   0.134110   0.2842001    0.145000    0.136000    0.2842000
+46452.00   0.140167   0.133107   0.2831507    0.142000    0.135000    0.2831000
+46453.00   0.137314   0.132188   0.2821915    0.140000    0.133000    0.2822000
+46454.00   0.134567   0.131298   0.2812568    0.137000    0.132000    0.2813000
+46455.00   0.131939   0.130388   0.2802680    0.134000    0.131000    0.2803000
+46456.00   0.129401   0.129446   0.2791405    0.131000    0.130000    0.2793000
+46457.00   0.126889   0.128481   0.2777997    0.128000    0.128000    0.2780000
+46458.00   0.124344   0.127502   0.2761995    0.125000    0.127000    0.2764000
+46459.00   0.121701   0.126519   0.2743377    0.122000    0.126000    0.2747000
+46460.00   0.118895   0.125549   0.2722597    0.119000    0.125000    0.2727000
+46461.00   0.115908   0.124658   0.2700604    0.116000    0.124000    0.2707000
+46462.00   0.112821   0.123850   0.2678653    0.113000    0.124000    0.2686000
+46463.00   0.109714   0.123099   0.2657978    0.111000    0.123000    0.2668000
+46464.00   0.106661   0.122378   0.2639515    0.108000    0.122000    0.2651000
+46465.00   0.103734   0.121667   0.2623630    0.105000    0.122000    0.2636000
+46466.00   0.100944   0.121002   0.2609992    0.103000    0.121000    0.2624000
+46467.00   0.098291   0.120373   0.2597681    0.100000    0.121000    0.2612000
+46468.00   0.095777   0.119754   0.2585474    0.098000    0.120000    0.2600000
+46469.00   0.093387   0.119115   0.2572194    0.095000    0.120000    0.2587000
+46470.00   0.091103   0.118430   0.2557082    0.093000    0.120000    0.2571000
+46471.00   0.088893   0.117719   0.2539971    0.091000    0.119000    0.2554000
+46472.00   0.086712   0.117033   0.2521272    0.088000    0.119000    0.2535000
+46473.00   0.084518   0.116424   0.2501744    0.086000    0.119000    0.2515000
+46474.00   0.082279   0.115931   0.2482237    0.084000    0.119000    0.2495000
+46475.00   0.080014   0.115531   0.2463535    0.081000    0.119000    0.2477000
+46476.00   0.077728   0.115215   0.2446300    0.079000    0.118000    0.2459000
+46477.00   0.075396   0.114980   0.2431000    0.076000    0.118000    0.2443000
+46478.00   0.072972   0.114814   0.2417861    0.074000    0.118000    0.2430000
+46479.00   0.070407   0.114705   0.2406830    0.072000    0.118000    0.2417000
+46480.00   0.067671   0.114639   0.2397571    0.069000    0.118000    0.2406000
+46481.00   0.064784   0.114612   0.2389393    0.066000    0.118000    0.2395000
+46482.00   0.061769   0.114632   0.2381421    0.063000    0.118000    0.2385000
+46483.00   0.058642   0.114705   0.2372709    0.060000    0.118000    0.2373000
+46484.00   0.055401   0.114826   0.2362378    0.057000    0.118000    0.2360000
+46485.00   0.052054   0.114988   0.2349740    0.054000    0.118000    0.2346000
+46486.00   0.048662   0.115180   0.2334475    0.051000    0.118000    0.2330000
+46487.00   0.045283   0.115387   0.2316829    0.048000    0.118000    0.2312000
+46488.00   0.041965   0.115589   0.2297646    0.045000    0.118000    0.2292000
+46489.00   0.038757   0.115766   0.2278141    0.042000    0.118000    0.2273000
+46490.00   0.035706   0.115906   0.2259578    0.039000    0.118000    0.2255000
+46491.00   0.032791   0.116044   0.2243030    0.036000    0.118000    0.2239000
+46492.00   0.029953   0.116217   0.2229021    0.032000    0.118000    0.2226000
+46493.00   0.027128   0.116443   0.2217315    0.029000    0.118000    0.2216000
+46494.00   0.024253   0.116739   0.2207104    0.026000    0.118000    0.2206000
+46495.00   0.021283   0.117115   0.2197275    0.023000    0.119000    0.2197000
+46496.00   0.018245   0.117561   0.2186638    0.020000    0.119000    0.2187000
+46497.00   0.015184   0.118063   0.2174311    0.017000    0.119000    0.2175000
+46498.00   0.012129   0.118590   0.2159936    0.015000    0.119000    0.2161000
+46499.00   0.009097   0.119104   0.2143741    0.012000    0.120000    0.2145000
+46500.00   0.006107   0.119568   0.2126434    0.009000    0.120000    0.2128000
+46501.00   0.003162   0.119982   0.2109038    0.006000    0.121000    0.2111000
+46502.00   0.000266   0.120366   0.2092616    0.004000    0.121000    0.2095000
+46503.00  -0.002553   0.120732   0.2078046    0.001000    0.121000    0.2081000
+46504.00  -0.005233   0.121082   0.2065820   -0.002000    0.122000    0.2068000
+46505.00  -0.007745   0.121426   0.2055882   -0.004000    0.123000    0.2057000
+46506.00  -0.010088   0.121808   0.2047961   -0.007000    0.123000    0.2048000
+46507.00  -0.012282   0.122274   0.2041610   -0.009000    0.124000    0.2041000
+46508.00  -0.014357   0.122843   0.2036164   -0.011000    0.124000    0.2034000
+46509.00  -0.016346   0.123521   0.2030807   -0.014000    0.125000    0.2028000
+46510.00  -0.018278   0.124312   0.2024589   -0.016000    0.126000    0.2021000
+46511.00  -0.020183   0.125208   0.2016446   -0.018000    0.127000    0.2012000
+46512.00  -0.022093   0.126208   0.2005405   -0.021000    0.128000    0.2001000
+46513.00  -0.024062   0.127293   0.1990832   -0.023000    0.129000    0.1987000
+46514.00  -0.026177   0.128425   0.1972693   -0.025000    0.130000    0.1970000
+46515.00  -0.028501   0.129563   0.1951654   -0.027000    0.131000    0.1950000
+46516.00  -0.030986   0.130692   0.1929143   -0.030000    0.132000    0.1930000
+46517.00  -0.033545   0.131826   0.1906985   -0.032000    0.134000    0.1910000
+46518.00  -0.036090   0.132981   0.1886785   -0.034000    0.135000    0.1892000
+46519.00  -0.038555   0.134187   0.1869491   -0.036000    0.136000    0.1878000
+46520.00  -0.040914   0.135472   0.1855148   -0.039000    0.137000    0.1865000
+46521.00  -0.043172   0.136853   0.1842927   -0.041000    0.139000    0.1854000
+46522.00  -0.045336   0.138344   0.1831580   -0.043000    0.140000    0.1844000
+46523.00  -0.047454   0.139934   0.1819794   -0.045000    0.141000    0.1832000
+46524.00  -0.049568   0.141566   0.1806466   -0.047000    0.143000    0.1819000
+46525.00  -0.051700   0.143185   0.1790974   -0.049000    0.145000    0.1803000
+46526.00  -0.053825   0.144820   0.1773314   -0.051000    0.146000    0.1785000
+46527.00  -0.055874   0.146521   0.1754022   -0.053000    0.148000    0.1766000
+46528.00  -0.057773   0.148339   0.1733924   -0.055000    0.150000    0.1745000
+46529.00  -0.059477   0.150309   0.1713959   -0.057000    0.152000    0.1725000
+46530.00  -0.060969   0.152445   0.1694985   -0.059000    0.154000    0.1705000
+46531.00  -0.062290   0.154704   0.1677555   -0.060000    0.155000    0.1687000
+46532.00  -0.063503   0.157021   0.1661917   -0.062000    0.157000    0.1671000
+46533.00  -0.064673   0.159335   0.1648015   -0.064000    0.159000    0.1656000
+46534.00  -0.065863   0.161602   0.1635526   -0.065000    0.161000    0.1643000
+46535.00  -0.067116   0.163798   0.1623914   -0.067000    0.163000    0.1631000
+46536.00  -0.068423   0.165915   0.1612565   -0.068000    0.165000    0.1619000
+46537.00  -0.069777   0.167954   0.1600837   -0.070000    0.167000    0.1607000
+46538.00  -0.071187   0.169929   0.1588076   -0.071000    0.169000    0.1594000
+46539.00  -0.072663   0.171851   0.1573670   -0.072000    0.171000    0.1579000
+46540.00  -0.074202   0.173729   0.1557132   -0.074000    0.173000    0.1562000
+46541.00  -0.075759   0.175573   0.1538128   -0.075000    0.175000    0.1541000
+46542.00  -0.077279   0.177399   0.1516836   -0.077000    0.177000    0.1519000
+46543.00  -0.078707   0.179220   0.1494123   -0.078000    0.179000    0.1495000
+46544.00  -0.080012   0.181058   0.1471433   -0.079000    0.181000    0.1471000
+46545.00  -0.081171   0.182934   0.1450321   -0.081000    0.183000    0.1449000
+46546.00  -0.082168   0.184865   0.1431903   -0.082000    0.185000    0.1430000
+46547.00  -0.083015   0.186866   0.1416451   -0.083000    0.187000    0.1414000
+46548.00  -0.083728   0.188951   0.1403312   -0.084000    0.189000    0.1400000
+46549.00  -0.084328   0.191137   0.1391180   -0.085000    0.191000    0.1388000
+46550.00  -0.084841   0.193434   0.1378587   -0.086000    0.193000    0.1376000
+46551.00  -0.085302   0.195807   0.1364402   -0.087000    0.195000    0.1362000
+46552.00  -0.085766   0.198188   0.1348073   -0.088000    0.197000    0.1346000
+46553.00  -0.086310   0.200499   0.1329628   -0.089000    0.199000    0.1328000
+46554.00  -0.087006   0.202681   0.1309539   -0.090000    0.201000    0.1309000
+46555.00  -0.087865   0.204743   0.1288565   -0.090000    0.203000    0.1289000
+46556.00  -0.088838   0.206708   0.1267570   -0.091000    0.205000    0.1268000
+46557.00  -0.089873   0.208598   0.1247376   -0.092000    0.207000    0.1248000
+46558.00  -0.090917   0.210433   0.1228653   -0.092000    0.210000    0.1230000
+46559.00  -0.091923   0.212232   0.1211815   -0.093000    0.212000    0.1214000
+46560.00  -0.092849   0.214013   0.1196996   -0.094000    0.214000    0.1199000
+46561.00  -0.093688   0.215808   0.1184135   -0.094000    0.216000    0.1187000
+46562.00  -0.094451   0.217648   0.1172924   -0.095000    0.218000    0.1176000
+46563.00  -0.095149   0.219567   0.1162853   -0.095000    0.221000    0.1167000
+46564.00  -0.095796   0.221597   0.1153295   -0.096000    0.223000    0.1157000
+46565.00  -0.096430   0.223764   0.1143641   -0.096000    0.225000    0.1147000
+46566.00  -0.097129   0.226072   0.1133334   -0.097000    0.227000    0.1135000
+46567.00  -0.097864   0.228443   0.1121536   -0.097000    0.229000    0.1122000
+46568.00  -0.098581   0.230775   0.1107561   -0.098000    0.232000    0.1107000
+46569.00  -0.099267   0.233012   0.1091256   -0.098000    0.234000    0.1089000
+46570.00  -0.099946   0.235158   0.1073146   -0.099000    0.236000    0.1070000
+46571.00  -0.100619   0.237225   0.1054365   -0.099000    0.238000    0.1051000
+46572.00  -0.101277   0.239225   0.1036423   -0.100000    0.240000    0.1033000
+46573.00  -0.101901   0.241178   0.1020719   -0.100000    0.242000    0.1018000
+46574.00  -0.102451   0.243121   0.1008047   -0.100000    0.244000    0.1006000
+46575.00  -0.102876   0.245094   0.0998261   -0.101000    0.246000    0.0997000
+46576.00  -0.103160   0.247103   0.0990329   -0.102000    0.248000    0.0990000
+46577.00  -0.103325   0.249134   0.0982836   -0.102000    0.250000    0.0983000
+46578.00  -0.103399   0.251173   0.0974527   -0.103000    0.252000    0.0976000
+46579.00  -0.103404   0.253206   0.0964718   -0.103000    0.253000    0.0967000
+46580.00  -0.103346   0.255218   0.0953424   -0.103000    0.255000    0.0956000
+46581.00  -0.103243   0.257218   0.0941066   -0.104000    0.257000    0.0944000
+46582.00  -0.103123   0.259234   0.0928334   -0.104000    0.259000    0.0932000
+46583.00  -0.103011   0.261304   0.0915998   -0.104000    0.261000    0.0919000
+46584.00  -0.102934   0.263462   0.0904755   -0.104000    0.263000    0.0908000
+46585.00  -0.102910   0.265735   0.0895140   -0.104000    0.265000    0.0899000
+46586.00  -0.102914   0.268094   0.0887517   -0.104000    0.267000    0.0891000
+46587.00  -0.102903   0.270487   0.0882035   -0.103000    0.269000    0.0885000
+46588.00  -0.102833   0.272860   0.0878589   -0.103000    0.271000    0.0881000
+46589.00  -0.102660   0.275152   0.0876797   -0.102000    0.274000    0.0879000
+46590.00  -0.102342   0.277314   0.0876058   -0.101000    0.276000    0.0878000
+46591.00  -0.101845   0.279349   0.0875678   -0.100000    0.278000    0.0877000
+46592.00  -0.101155   0.281292   0.0874923   -0.100000    0.280000    0.0875000
+46593.00  -0.100277   0.283176   0.0873049   -0.099000    0.282000    0.0872000
+46594.00  -0.099225   0.285031   0.0869416   -0.098000    0.284000    0.0867000
+46595.00  -0.098024   0.286885   0.0863563   -0.096000    0.287000    0.0859000
+46596.00  -0.096693   0.288773   0.0855339   -0.095000    0.289000    0.0850000
+46597.00  -0.095276   0.290712   0.0844919   -0.094000    0.291000    0.0838000
+46598.00  -0.093812   0.292705   0.0832929   -0.093000    0.293000    0.0826000
+46599.00  -0.092335   0.294749   0.0820471   -0.092000    0.296000    0.0814000
+46600.00  -0.090889   0.296847   0.0808817   -0.090000    0.298000    0.0803000
+46601.00  -0.089542   0.299006   0.0799154   -0.089000    0.300000    0.0794000
+46602.00  -0.088276   0.301210   0.0791929   -0.088000    0.303000    0.0788000
+46603.00  -0.087034   0.303439   0.0786670   -0.087000    0.305000    0.0784000
+46604.00  -0.085751   0.305684   0.0782238   -0.085000    0.307000    0.0781000
+46605.00  -0.084370   0.307936   0.0777276   -0.084000    0.309000    0.0777000
+46606.00  -0.082897   0.310196   0.0770662   -0.083000    0.311000    0.0771000
+46607.00  -0.081352   0.312466   0.0761962   -0.081000    0.313000    0.0762000
+46608.00  -0.079745   0.314743   0.0751471   -0.080000    0.316000    0.0752000
+46609.00  -0.078095   0.317015   0.0739938   -0.079000    0.318000    0.0740000
+46610.00  -0.076437   0.319251   0.0728272   -0.077000    0.320000    0.0728000
+46611.00  -0.074819   0.321453   0.0717335   -0.076000    0.322000    0.0717000
+46612.00  -0.073288   0.323644   0.0707726   -0.075000    0.324000    0.0707000
+46613.00  -0.071864   0.325834   0.0699821   -0.073000    0.326000    0.0699000
+46614.00  -0.070558   0.328031   0.0693782   -0.072000    0.328000    0.0693000
+46615.00  -0.069363   0.330236   0.0689524   -0.070000    0.330000    0.0689000
+46616.00  -0.068207   0.332442   0.0686737   -0.069000    0.332000    0.0686000
+46617.00  -0.067004   0.334648   0.0684897   -0.067000    0.334000    0.0684000
+46618.00  -0.065692   0.336846   0.0683299   -0.066000    0.336000    0.0683000
+46619.00  -0.064318   0.339016   0.0681120   -0.064000    0.338000    0.0681000
+46620.00  -0.062954   0.341131   0.0677608   -0.063000    0.340000    0.0677000
+46621.00  -0.061644   0.343144   0.0672256   -0.061000    0.342000    0.0672000
+46622.00  -0.060394   0.344996   0.0664822   -0.060000    0.344000    0.0665000
+46623.00  -0.059201   0.346626   0.0655386   -0.058000    0.346000    0.0656000
+46624.00  -0.057992   0.348012   0.0644446   -0.056000    0.348000    0.0645000
+46625.00  -0.056638   0.349195   0.0632836   -0.054000    0.350000    0.0633000
+46626.00  -0.055124   0.350248   0.0621501   -0.053000    0.351000    0.0622000
+46627.00  -0.053477   0.351247   0.0611486   -0.051000    0.353000    0.0611000
+46628.00  -0.051711   0.352262   0.0603652   -0.049000    0.354000    0.0602000
+46629.00  -0.049825   0.353364   0.0598363   -0.047000    0.356000    0.0597000
+46630.00  -0.047823   0.354603   0.0595248   -0.045000    0.358000    0.0593000
+46631.00  -0.045742   0.355959   0.0593177   -0.043000    0.359000    0.0591000
+46632.00  -0.043634   0.357389   0.0590660   -0.042000    0.360000    0.0588000
+46633.00  -0.041552   0.358849   0.0586337   -0.040000    0.362000    0.0584000
+46634.00  -0.039560   0.360304   0.0579461   -0.038000    0.363000    0.0577000
+46635.00  -0.037728   0.361725   0.0570104   -0.036000    0.364000    0.0568000
+46636.00  -0.036067   0.363091   0.0559092   -0.034000    0.365000    0.0558000
+46637.00  -0.034531   0.364382   0.0547645   -0.033000    0.367000    0.0547000
+46638.00  -0.033067   0.365577   0.0536947   -0.031000    0.368000    0.0536000
+46639.00  -0.031637   0.366669   0.0527888   -0.029000    0.368000    0.0527000
+46640.00  -0.030274   0.367690   0.0520890   -0.028000    0.369000    0.0520000
+46641.00  -0.028996   0.368666   0.0516086   -0.026000    0.370000    0.0515000
+46642.00  -0.027795   0.369619   0.0513373   -0.025000    0.371000    0.0511000
+46643.00  -0.026634   0.370570   0.0512372   -0.024000    0.372000    0.0509000
+46644.00  -0.025468   0.371543   0.0512451   -0.022000    0.372000    0.0509000
+46645.00  -0.024242   0.372545   0.0512849   -0.021000    0.373000    0.0509000
+46646.00  -0.022891   0.373498   0.0512841   -0.020000    0.374000    0.0508000
+46647.00  -0.021434   0.374402   0.0511452   -0.019000    0.375000    0.0506000
+46648.00  -0.019918   0.375285   0.0507817   -0.018000    0.376000    0.0502000
+46649.00  -0.018396   0.376154   0.0501389   -0.017000    0.377000    0.0496000
+46650.00  -0.016918   0.377001   0.0492042   -0.016000    0.378000    0.0487000
+46651.00  -0.015510   0.377825   0.0480271   -0.014000    0.379000    0.0476000
+46652.00  -0.014168   0.378641   0.0467073   -0.013000    0.380000    0.0464000
+46653.00  -0.012882   0.379462   0.0453699   -0.012000    0.381000    0.0452000
+46654.00  -0.011640   0.380306   0.0441420   -0.011000    0.382000    0.0440000
+46655.00  -0.010403   0.381186   0.0431236   -0.009000    0.383000    0.0430000
+46656.00  -0.009141   0.382083   0.0423668   -0.008000    0.384000    0.0423000
+46657.00  -0.007839   0.382970   0.0418592   -0.007000    0.385000    0.0419000
+46658.00  -0.006482   0.383837   0.0415167   -0.005000    0.386000    0.0415000
+46659.00  -0.005055   0.384681   0.0412078   -0.004000    0.387000    0.0412000
+46660.00  -0.003544   0.385493   0.0407924   -0.002000    0.388000    0.0407000
+46661.00  -0.001936   0.386285   0.0401629    0.000000    0.389000    0.0401000
+46662.00  -0.000222   0.387076   0.0392789    0.001000    0.390000    0.0391000
+46663.00   0.001602   0.387884   0.0381772    0.003000    0.391000    0.0380000
+46664.00   0.003514   0.388702   0.0369529    0.005000    0.391000    0.0368000
+46665.00   0.005484   0.389514   0.0357240    0.006000    0.392000    0.0355000
+46666.00   0.007473   0.390301   0.0345931    0.008000    0.393000    0.0344000
+46667.00   0.009457   0.391054   0.0336291    0.010000    0.394000    0.0334000
+46668.00   0.011415   0.391764   0.0328618    0.011000    0.394000    0.0327000
+46669.00   0.013324   0.392421   0.0322875    0.013000    0.395000    0.0321000
+46670.00   0.015140   0.393021   0.0318770    0.015000    0.395000    0.0317000
+46671.00   0.016870   0.393579   0.0315836    0.016000    0.396000    0.0314000
+46672.00   0.018538   0.394122   0.0313464    0.018000    0.396000    0.0312000
+46673.00   0.020155   0.394677   0.0310971    0.020000    0.397000    0.0310000
+46674.00   0.021728   0.395271   0.0307690    0.021000    0.397000    0.0306000
+46675.00   0.023265   0.395918   0.0302994    0.023000    0.397000    0.0301000
+46676.00   0.024777   0.396582   0.0296206    0.025000    0.398000    0.0293000
+46677.00   0.026280   0.397211   0.0286889    0.026000    0.398000    0.0283000
+46678.00   0.027776   0.397750   0.0275048    0.028000    0.398000    0.0270000
+46679.00   0.029242   0.398137   0.0261182    0.030000    0.398000    0.0256000
+46680.00   0.030660   0.398317   0.0246201    0.031000    0.399000    0.0240000
+46681.00   0.032040   0.398304   0.0231308    0.033000    0.399000    0.0226000
+46682.00   0.033398   0.398165   0.0217642    0.034000    0.399000    0.0213000
+46683.00   0.034748   0.397969   0.0205972    0.036000    0.399000    0.0202000
+46684.00   0.036109   0.397793   0.0196517    0.037000    0.399000    0.0194000
+46685.00   0.037511   0.397718   0.0188843    0.038000    0.399000    0.0186000
+46686.00   0.038937   0.397755   0.0181818    0.040000    0.399000    0.0180000
+46687.00   0.040369   0.397869   0.0174055    0.041000    0.399000    0.0172000
+46688.00   0.041792   0.398011   0.0164323    0.043000    0.399000    0.0161000
+46689.00   0.043195   0.398133   0.0151853    0.044000    0.399000    0.0149000
+46690.00   0.044565   0.398189   0.0136551    0.045000    0.399000    0.0133000
+46691.00   0.045892   0.398157   0.0119068    0.046000    0.399000    0.0115000
+46692.00   0.047175   0.398031   0.0100508    0.048000    0.399000    0.0097000
+46693.00   0.048412   0.397812   0.0082069    0.049000    0.399000    0.0079000
+46694.00   0.049596   0.397519   0.0064771    0.050000    0.399000    0.0062000
+46695.00   0.050721   0.397166   0.0049286    0.051000    0.399000    0.0047000
+46696.00   0.051787   0.396760   0.0035841    0.052000    0.398000    0.0034000
+46697.00   0.052821   0.396311   0.0024267    0.054000    0.398000    0.0022000
+46698.00   0.053854   0.395831   0.0014112    0.055000    0.398000    0.0012000
+46699.00   0.054917   0.395336   0.0004703    0.056000    0.398000    0.0003000
+46700.00   0.056028   0.394854  -0.0004763    0.057000    0.397000   -0.0007000
+46701.00   0.057188   0.394417  -0.0015092    0.058000    0.397000   -0.0017000
+46702.00   0.058390   0.394051  -0.0027028    0.060000    0.396000   -0.0029000
+46703.00   0.059614   0.393782  -0.0041178    0.061000    0.396000   -0.0043000
+46704.00   0.060833   0.393642  -0.0057892    0.062000    0.395000   -0.0060000
+46705.00   0.062023   0.393644  -0.0077152    0.063000    0.395000   -0.0080000
+46706.00   0.063183   0.393743  -0.0098497    0.064000    0.395000   -0.0101000
+46707.00   0.064319   0.393870  -0.0120944    0.066000    0.394000   -0.0124000
+46708.00   0.065439   0.393967  -0.0143128    0.067000    0.394000   -0.0146000
+46709.00   0.066561   0.393983  -0.0163691    0.068000    0.393000   -0.0167000
+46710.00   0.067700   0.393877  -0.0181709    0.069000    0.393000   -0.0184000
+46711.00   0.068857   0.393644  -0.0196953    0.071000    0.392000   -0.0200000
+46712.00   0.070031   0.393309  -0.0209933    0.072000    0.392000   -0.0212000
+46713.00   0.071220   0.392901  -0.0221744    0.073000    0.392000   -0.0224000
+46714.00   0.072432   0.392455  -0.0233677    0.074000    0.391000   -0.0237000
+46715.00   0.073692   0.392017  -0.0246816    0.076000    0.391000   -0.0251000
+46716.00   0.075028   0.391609  -0.0261924    0.077000    0.391000   -0.0267000
+46717.00   0.076466   0.391242  -0.0279202    0.078000    0.391000   -0.0285000
+46718.00   0.078018   0.390927  -0.0298285    0.079000    0.390000   -0.0305000
+46719.00   0.079677   0.390666  -0.0318410    0.081000    0.390000   -0.0326000
+46720.00   0.081418   0.390457  -0.0338647    0.082000    0.390000   -0.0347000
+46721.00   0.083132   0.390277  -0.0358018    0.083000    0.389000   -0.0367000
+46722.00   0.084741   0.390107  -0.0375854    0.085000    0.389000   -0.0384000
+46723.00   0.086193   0.389931  -0.0391847    0.086000    0.389000   -0.0400000
+46724.00   0.087450   0.389740  -0.0406038    0.087000    0.388000   -0.0413000
+46725.00   0.088503   0.389529  -0.0418764    0.088000    0.388000   -0.0425000
+46726.00   0.089430   0.389295  -0.0430563    0.090000    0.388000   -0.0435000
+46727.00   0.090340   0.389034  -0.0442099    0.091000    0.387000   -0.0445000
+46728.00   0.091325   0.388742  -0.0454034    0.092000    0.387000   -0.0455000
+46729.00   0.092387   0.388414  -0.0466797    0.094000    0.387000   -0.0467000
+46730.00   0.093479   0.388037  -0.0480733    0.095000    0.387000   -0.0480000
+46731.00   0.094579   0.387595  -0.0496329    0.096000    0.386000   -0.0495000
+46732.00   0.095684   0.387071  -0.0513921    0.097000    0.386000   -0.0512000
+46733.00   0.096822   0.386457  -0.0533440    0.099000    0.385000   -0.0532000
+46734.00   0.098035   0.385746  -0.0554304    0.100000    0.385000   -0.0553000
+46735.00   0.099348   0.384936  -0.0575419    0.101000    0.385000   -0.0574000
+46736.00   0.100727   0.384042  -0.0595353    0.103000    0.384000   -0.0594000
+46737.00   0.102130   0.383079  -0.0612828    0.104000    0.383000   -0.0612000
+46738.00   0.103520   0.382064  -0.0627206    0.105000    0.383000   -0.0627000
+46739.00   0.104920   0.381025  -0.0638732    0.106000    0.382000   -0.0638000
+46740.00   0.106443   0.380021  -0.0648435    0.108000    0.382000   -0.0648000
+46741.00   0.108160   0.379146  -0.0657776    0.109000    0.381000   -0.0659000
+46742.00   0.110029   0.378363  -0.0668165    0.111000    0.380000   -0.0670000
+46743.00   0.111974   0.377602  -0.0680527    0.113000    0.379000   -0.0683000
+46744.00   0.113883   0.376827  -0.0695212    0.114000    0.378000   -0.0698000
+46745.00   0.115670   0.376009  -0.0711957    0.116000    0.377000   -0.0716000
+46746.00   0.117284   0.375126  -0.0730039    0.117000    0.376000   -0.0734000
+46747.00   0.118681   0.374161  -0.0748528    0.118000    0.374000   -0.0753000
+46748.00   0.119822   0.373111  -0.0766462    0.119000    0.373000   -0.0770000
+46749.00   0.120675   0.371975  -0.0783024    0.120000    0.372000   -0.0786000
+46750.00   0.121222   0.370755  -0.0797678    0.121000    0.371000   -0.0800000
+46751.00   0.121525   0.369473  -0.0810249    0.121000    0.370000   -0.0813000
+46752.00   0.121669   0.368161  -0.0820903    0.122000    0.368000   -0.0823000
+46753.00   0.121731   0.366845  -0.0830098    0.122000    0.367000   -0.0832000
+46754.00   0.121735   0.365532  -0.0838496    0.123000    0.366000   -0.0840000
+46755.00   0.121695   0.364223  -0.0846851    0.123000    0.365000   -0.0848000
+46756.00   0.121645   0.362924  -0.0855930    0.123000    0.364000   -0.0857000
+46757.00   0.121615   0.361638  -0.0866396    0.124000    0.362000   -0.0868000
+46758.00   0.121636   0.360361  -0.0878742    0.124000    0.361000   -0.0881000
+46759.00   0.121737   0.359088  -0.0893248    0.124000    0.360000   -0.0895000
+46760.00   0.121951   0.357817  -0.0909912    0.125000    0.359000   -0.0912000
+46761.00   0.122300   0.356546  -0.0928325    0.125000    0.358000   -0.0931000
+46762.00   0.122802   0.355271  -0.0947608    0.126000    0.356000   -0.0950000
+46763.00   0.123482   0.353996  -0.0966481    0.126000    0.355000   -0.0969000
+46764.00   0.124368   0.352720  -0.0983574    0.127000    0.354000   -0.0986000
+46765.00   0.125479   0.351444  -0.0997919    0.127000    0.352000   -0.1001000
+46766.00   0.126780   0.350165  -0.1009402    0.128000    0.351000   -0.1012000
+46767.00   0.128205   0.348882  -0.1018824    0.129000    0.350000   -0.1022000
+46768.00   0.129688   0.347592  -0.1027627    0.130000    0.348000   -0.1031000
+46769.00   0.131165   0.346274  -0.1037358    0.131000    0.347000   -0.1041000
+46770.00   0.132580   0.344903  -0.1049099    0.132000    0.346000   -0.1052000
+46771.00   0.133924   0.343466  -0.1063124    0.133000    0.344000   -0.1066000
+46772.00   0.135176   0.341972  -0.1079059    0.134000    0.343000   -0.1082000
+46773.00   0.136306   0.340435  -0.1096072    0.135000    0.341000   -0.1099000
+46774.00   0.137286   0.338871  -0.1113158    0.136000    0.340000   -0.1115000
+46775.00   0.138103   0.337292  -0.1129402    0.137000    0.339000   -0.1131000
+46776.00   0.138777   0.335713  -0.1144085    0.138000    0.337000   -0.1146000
+46777.00   0.139334   0.334158  -0.1156739    0.138000    0.336000   -0.1158000
+46778.00   0.139803   0.332651  -0.1167165    0.139000    0.335000   -0.1168000
+46779.00   0.140216   0.331220  -0.1175448    0.140000    0.334000   -0.1177000
+46780.00   0.140605   0.329888  -0.1182030    0.140000    0.332000   -0.1184000
+46781.00   0.140997   0.328664  -0.1187732    0.141000    0.331000   -0.1191000
+46782.00   0.141411   0.327549  -0.1193512    0.141000    0.330000   -0.1197000
+46783.00   0.141868   0.326542  -0.1200310    0.142000    0.329000   -0.1204000
+46784.00   0.142385   0.325615  -0.1208896    0.142000    0.328000   -0.1213000
+46785.00   0.142978   0.324735  -0.1219795    0.142000    0.327000   -0.1223000
+46786.00   0.143609   0.323868  -0.1233149    0.143000    0.326000   -0.1236000
+46787.00   0.144204   0.322977  -0.1248792    0.143000    0.325000   -0.1251000
+46788.00   0.144684   0.322025  -0.1266263    0.144000    0.325000   -0.1267000
+46789.00   0.145013   0.321001  -0.1284839    0.144000    0.323000   -0.1285000
+46790.00   0.145222   0.319917  -0.1303569    0.144000    0.322000   -0.1303000
+46791.00   0.145351   0.318788  -0.1321329    0.145000    0.321000   -0.1321000
+46792.00   0.145443   0.317630  -0.1337127    0.145000    0.320000   -0.1336000
+46793.00   0.145537   0.316458  -0.1350465    0.145000    0.319000   -0.1349000
+46794.00   0.145675   0.315287  -0.1361608    0.145000    0.317000   -0.1360000
+46795.00   0.145891   0.314129  -0.1371607    0.146000    0.316000   -0.1369000
+46796.00   0.146164   0.312978  -0.1381993    0.146000    0.315000   -0.1379000
+46797.00   0.146446   0.311819  -0.1394157    0.146000    0.314000   -0.1391000
+46798.00   0.146692   0.310639  -0.1408817    0.146000    0.312000   -0.1406000
+46799.00   0.146854   0.309420  -0.1425808    0.146000    0.311000   -0.1423000
+46800.00   0.146890   0.308156  -0.1444251    0.146000    0.309000   -0.1441000
+46801.00   0.146762   0.306858  -0.1462932    0.146000    0.308000   -0.1460000
+46802.00   0.146447   0.305532  -0.1480683    0.145000    0.307000   -0.1478000
+46803.00   0.145930   0.304171  -0.1496648    0.145000    0.305000   -0.1493000
+46804.00   0.145216   0.302764  -0.1510340    0.145000    0.304000   -0.1507000
+46805.00   0.144363   0.301309  -0.1521611    0.144000    0.303000   -0.1518000
+46806.00   0.143461   0.299808  -0.1530660    0.144000    0.302000   -0.1528000
+46807.00   0.142592   0.298270  -0.1537942    0.143000    0.300000   -0.1535000
+46808.00   0.141826   0.296709  -0.1544114    0.143000    0.299000   -0.1542000
+46809.00   0.141231   0.295141  -0.1549984    0.142000    0.297000   -0.1548000
+46810.00   0.140855   0.293585  -0.1556391    0.141000    0.296000   -0.1555000
+46811.00   0.140624   0.292056  -0.1564038    0.141000    0.295000   -0.1563000
+46812.00   0.140450   0.290534  -0.1573502    0.140000    0.293000   -0.1573000
+46813.00   0.140262   0.289004  -0.1585097    0.140000    0.292000   -0.1585000
+46814.00   0.139989   0.287471  -0.1598827    0.139000    0.290000   -0.1598000
+46815.00   0.139575   0.285944  -0.1614379    0.138000    0.289000   -0.1614000
+46816.00   0.139026   0.284463  -0.1631089    0.138000    0.288000   -0.1631000
+46817.00   0.138418   0.283070  -0.1648016    0.137000    0.286000   -0.1647000
+46818.00   0.137857   0.281786  -0.1664139    0.137000    0.285000   -0.1664000
+46819.00   0.137431   0.280616  -0.1678506    0.136000    0.284000   -0.1678000
+46820.00   0.137166   0.279539  -0.1690519    0.136000    0.282000   -0.1690000
+46821.00   0.137038   0.278561  -0.1700244    0.135000    0.281000   -0.1700000
+46822.00   0.137011   0.277704  -0.1708510    0.135000    0.280000   -0.1708000
+46823.00   0.137001   0.276949  -0.1716741    0.135000    0.279000   -0.1717000
+46824.00   0.136835   0.276208  -0.1726509    0.134000    0.277000   -0.1727000
+46825.00   0.136335   0.275387  -0.1738984    0.134000    0.276000   -0.1740000
+46826.00   0.135381   0.274410  -0.1754526    0.133000    0.275000   -0.1756000
+46827.00   0.134106   0.273266  -0.1772565    0.133000    0.274000   -0.1774000
+46828.00   0.132707   0.271968  -0.1791915    0.132000    0.273000   -0.1793000
+46829.00   0.131373   0.270544  -0.1811235    0.132000    0.271000   -0.1812000
+46830.00   0.130288   0.269034  -0.1829432    0.132000    0.270000   -0.1830000
+46831.00   0.129572   0.267504  -0.1845796    0.131000    0.269000   -0.1846000
+46832.00   0.129204   0.266010  -0.1860049    0.131000    0.268000   -0.1860000
+46833.00   0.129075   0.264586  -0.1872302    0.130000    0.267000   -0.1872000
+46834.00   0.129034   0.263271  -0.1882929    0.130000    0.266000   -0.1882000
+46835.00   0.128903   0.262120  -0.1892463    0.129000    0.265000   -0.1891000
+46836.00   0.128587   0.261167  -0.1901571    0.129000    0.264000   -0.1900000
+46837.00   0.128150   0.260373  -0.1911065    0.128000    0.263000   -0.1909000
+46838.00   0.127667   0.259684  -0.1921723    0.128000    0.262000   -0.1920000
+46839.00   0.127184   0.259047  -0.1934137    0.128000    0.262000   -0.1933000
+46840.00   0.126747   0.258408  -0.1948653    0.127000    0.261000   -0.1948000
+46841.00   0.126370   0.257736  -0.1965376    0.127000    0.260000   -0.1965000
+46842.00   0.126024   0.257013  -0.1984046    0.127000    0.259000   -0.1984000
+46843.00   0.125673   0.256225  -0.2004034    0.126000    0.258000   -0.2004000
+46844.00   0.125290   0.255354  -0.2024439    0.126000    0.257000   -0.2025000
+46845.00   0.124857   0.254391  -0.2044244    0.125000    0.256000   -0.2045000
+46846.00   0.124399   0.253371  -0.2062584    0.125000    0.255000   -0.2064000
+46847.00   0.123945   0.252313  -0.2078958    0.124000    0.254000   -0.2081000
+46848.00   0.123508   0.251228  -0.2093397    0.124000    0.253000   -0.2096000
+46849.00   0.123087   0.250117  -0.2106572    0.123000    0.252000   -0.2109000
+46850.00   0.122661   0.248976  -0.2119707    0.123000    0.251000   -0.2122000
+46851.00   0.122246   0.247844  -0.2134289    0.122000    0.250000   -0.2137000
+46852.00   0.121872   0.246727  -0.2151634    0.122000    0.249000   -0.2153000
+46853.00   0.121495   0.245613  -0.2172400    0.121000    0.247000   -0.2173000
+46854.00   0.121053   0.244482  -0.2196369    0.121000    0.246000   -0.2196000
+46855.00   0.120474   0.243307  -0.2222510    0.120000    0.245000   -0.2220000
+46856.00   0.119739   0.242064  -0.2249249    0.119000    0.243000   -0.2245000
+46857.00   0.118859   0.240737  -0.2274987    0.118000    0.242000   -0.2268000
+46858.00   0.117846   0.239311  -0.2298512    0.118000    0.240000   -0.2290000
+46859.00   0.116699   0.237782  -0.2319140    0.117000    0.239000   -0.2309000
+46860.00   0.115417   0.236156  -0.2336699    0.116000    0.237000   -0.2326000
+46861.00   0.114084   0.234471  -0.2351542    0.115000    0.236000   -0.2341000
+46862.00   0.112799   0.232769  -0.2364341    0.115000    0.235000   -0.2355000
+46863.00   0.111657   0.231094  -0.2375923    0.114000    0.234000   -0.2368000
+46864.00   0.110751   0.229489  -0.2387201    0.113000    0.233000   -0.2382000
+46865.00   0.110161   0.227994  -0.2399098    0.113000    0.231000   -0.2395000
+46866.00   0.109870   0.226654  -0.2412443    0.112000    0.230000   -0.2411000
+46867.00   0.109779   0.225503  -0.2427787    0.111000    0.229000   -0.2428000
+46868.00   0.109810   0.224548  -0.2445374    0.111000    0.228000   -0.2447000
+46869.00   0.109899   0.223794  -0.2465079    0.110000    0.227000   -0.2468000
+46870.00   0.109988   0.223232  -0.2486381    0.109000    0.226000   -0.2490000
+46871.00   0.110020   0.222818  -0.2508431    0.109000    0.225000   -0.2512000
+46872.00   0.109928   0.222495  -0.2530193    0.108000    0.224000   -0.2534000
+46873.00   0.109637   0.222199  -0.2550668    0.107000    0.223000   -0.2553000
+46874.00   0.109055   0.221849  -0.2569179    0.107000    0.223000   -0.2571000
+46875.00   0.108093   0.221367  -0.2585563    0.106000    0.222000   -0.2586000
+46876.00   0.106728   0.220718  -0.2600186    0.105000    0.221000   -0.2599000
+46877.00   0.105080   0.219945  -0.2613939    0.104000    0.220000   -0.2612000
+46878.00   0.103299   0.219106  -0.2628015    0.104000    0.219000   -0.2624000
+46879.00   0.101537   0.218262  -0.2643567    0.103000    0.218000   -0.2639000
+46880.00   0.099933   0.217460  -0.2661370    0.102000    0.217000   -0.2656000
+46881.00   0.098585   0.216712  -0.2681613    0.101000    0.216000   -0.2676000
+46882.00   0.097540   0.216016  -0.2703756    0.100000    0.215000   -0.2698000
+46883.00   0.096745   0.215368  -0.2726712    0.099000    0.215000   -0.2722000
+46884.00   0.096120   0.214758  -0.2749188    0.098000    0.214000   -0.2745000
+46885.00   0.095597   0.214178  -0.2770051    0.097000    0.213000   -0.2766000
+46886.00   0.095139   0.213641  -0.2788608    0.096000    0.212000   -0.2785000
+46887.00   0.094713   0.213178  -0.2804693    0.095000    0.211000   -0.2801000
+46888.00   0.094270   0.212808  -0.2818591    0.094000    0.211000   -0.2815000
+46889.00   0.093722   0.212509  -0.2830897    0.093000    0.210000   -0.2828000
+46890.00   0.092988   0.212263  -0.2842360    0.092000    0.210000   -0.2839000
+46891.00   0.092064   0.212060  -0.2853731    0.091000    0.209000   -0.2850000
+46892.00   0.090975   0.211868  -0.2865731    0.090000    0.209000   -0.2862000
+46893.00   0.089743   0.211650  -0.2878977    0.089000    0.209000   -0.2875000
+46894.00   0.088394   0.211366  -0.2893924    0.088000    0.208000   -0.2890000
+46895.00   0.086971   0.210974  -0.2910801    0.087000    0.208000   -0.2907000
+46896.00   0.085500   0.210491  -0.2929587    0.086000    0.208000   -0.2926000
+46897.00   0.083991   0.209938  -0.2949955    0.085000    0.207000   -0.2946000
+46898.00   0.082464   0.209321  -0.2971198    0.084000    0.207000   -0.2968000
+46899.00   0.080935   0.208641  -0.2992297    0.083000    0.206000   -0.2989000
+46900.00   0.079424   0.207916  -0.3012155    0.082000    0.206000   -0.3009000
+46901.00   0.077952   0.207220  -0.3029897    0.081000    0.206000   -0.3027000
+46902.00   0.076542   0.206595  -0.3045084    0.080000    0.206000   -0.3043000
+46903.00   0.075219   0.206060  -0.3057977    0.078000    0.205000   -0.3055000
+46904.00   0.074010   0.205614  -0.3069486    0.077000    0.205000   -0.3067000
+46905.00   0.072932   0.205255  -0.3080894    0.076000    0.205000   -0.3079000
+46906.00   0.071990   0.204990  -0.3093493    0.075000    0.204000   -0.3092000
+46907.00   0.071179   0.204811  -0.3108178    0.074000    0.204000   -0.3107000
+46908.00   0.070473   0.204690  -0.3125279    0.073000    0.204000   -0.3125000
+46909.00   0.069810   0.204601  -0.3144718    0.072000    0.203000   -0.3145000
+46910.00   0.069116   0.204498  -0.3165887    0.071000    0.203000   -0.3166000
+46911.00   0.068329   0.204384  -0.3187598    0.070000    0.203000   -0.3187000
+46912.00   0.067396   0.204269  -0.3208546    0.069000    0.202000   -0.3207000
+46913.00   0.066290   0.204134  -0.3227685    0.068000    0.202000   -0.3225000
+46914.00   0.064990   0.203955  -0.3244426    0.067000    0.202000   -0.3241000
+46915.00   0.063489   0.203714  -0.3258673    0.066000    0.202000   -0.3254000
+46916.00   0.061855   0.203438  -0.3270799    0.064000    0.201000   -0.3265000
+46917.00   0.060205   0.203149  -0.3281429    0.063000    0.201000   -0.3275000
+46918.00   0.058669   0.202858  -0.3291293    0.062000    0.201000   -0.3284000
+46919.00   0.057350   0.202549  -0.3301078    0.060000    0.201000   -0.3293000
+46920.00   0.056318   0.202180  -0.3311396    0.059000    0.201000   -0.3304000
+46921.00   0.055534   0.201759  -0.3322852    0.058000    0.200000   -0.3316000
+46922.00   0.054880   0.201348  -0.3335849    0.056000    0.200000   -0.3330000
+46923.00   0.054226   0.201005  -0.3350588    0.055000    0.200000   -0.3346000
+46924.00   0.053433   0.200748  -0.3367160    0.054000    0.200000   -0.3363000
+46925.00   0.052428   0.200544  -0.3385294    0.052000    0.199000   -0.3382000
+46926.00   0.051219   0.200369  -0.3404133    0.051000    0.199000   -0.3401000
+46927.00   0.049840   0.200207  -0.3422469    0.049000    0.199000   -0.3420000
+46928.00   0.048329   0.200022  -0.3439092    0.048000    0.198000   -0.3437000
+46929.00   0.046729   0.199778  -0.3453195    0.047000    0.198000   -0.3451000
+46930.00   0.045091   0.199449  -0.3464683    0.045000    0.198000   -0.3462000
+46931.00   0.043468   0.199072  -0.3474324    0.043000    0.197000   -0.3472000
+46932.00   0.041909   0.198704  -0.3483486    0.042000    0.197000   -0.3482000
+46933.00   0.040463   0.198405  -0.3493633    0.041000    0.197000   -0.3492000
+46934.00   0.039181   0.198239  -0.3505857    0.039000    0.197000   -0.3505000
+46935.00   0.038100   0.198265  -0.3520593    0.038000    0.196000   -0.3520000
+46936.00   0.037168   0.198481  -0.3537602    0.037000    0.196000   -0.3538000
+46937.00   0.036280   0.198815  -0.3556124    0.036000    0.196000   -0.3557000
+46938.00   0.035329   0.199181  -0.3575135    0.035000    0.196000   -0.3576000
+46939.00   0.034223   0.199476  -0.3593644    0.034000    0.195000   -0.3594000
+46940.00   0.032936   0.199579  -0.3610880    0.034000    0.195000   -0.3611000
+46941.00   0.031586   0.199492  -0.3626238    0.033000    0.195000   -0.3625000
+46942.00   0.030268   0.199271  -0.3639418    0.032000    0.195000   -0.3637000
+46943.00   0.029042   0.198973  -0.3650490    0.031000    0.195000   -0.3647000
+46944.00   0.027961   0.198658  -0.3659816    0.030000    0.195000   -0.3655000
+46945.00   0.027069   0.198384  -0.3667955    0.030000    0.195000   -0.3663000
+46946.00   0.026356   0.198192  -0.3675670    0.029000    0.195000   -0.3670000
+46947.00   0.025775   0.198083  -0.3683667    0.029000    0.195000   -0.3678000
+46948.00   0.025279   0.198053  -0.3692478    0.028000    0.195000   -0.3687000
+46949.00   0.024820   0.198093  -0.3702416    0.027000    0.195000   -0.3698000
+46950.00   0.024353   0.198199  -0.3713598    0.027000    0.195000   -0.3711000
+46951.00   0.023855   0.198360  -0.3726176    0.026000    0.195000   -0.3726000
+46952.00   0.023324   0.198545  -0.3740154    0.025000    0.196000   -0.3742000
+46953.00   0.022759   0.198715  -0.3755227    0.025000    0.196000   -0.3759000
+46954.00   0.022130   0.198843  -0.3770620    0.024000    0.196000   -0.3775000
+46955.00   0.021384   0.198929  -0.3785134    0.023000    0.197000   -0.3790000
+46956.00   0.020463   0.198976  -0.3797706    0.022000    0.197000   -0.3802000
+46957.00   0.019334   0.198993  -0.3807825    0.021000    0.198000   -0.3812000
+46958.00   0.018054   0.199008  -0.3815793    0.020000    0.198000   -0.3819000
+46959.00   0.016708   0.199060  -0.3822681    0.019000    0.199000   -0.3826000
+46960.00   0.015382   0.199185  -0.3829938    0.018000    0.199000   -0.3833000
+46961.00   0.014115   0.199396  -0.3838894    0.017000    0.200000   -0.3842000
+46962.00   0.012925   0.199696  -0.3850249    0.016000    0.200000   -0.3853000
+46963.00   0.011828   0.200086  -0.3863871    0.014000    0.201000   -0.3867000
+46964.00   0.010807   0.200548  -0.3878939    0.013000    0.201000   -0.3883000
+46965.00   0.009827   0.201057  -0.3894316    0.012000    0.202000   -0.3898000
+46966.00   0.008835   0.201586  -0.3908886    0.011000    0.202000   -0.3912000
+46967.00   0.007817   0.202110  -0.3921712    0.010000    0.203000   -0.3925000
+46968.00   0.006773   0.202608  -0.3932229    0.008000    0.203000   -0.3935000
+46969.00   0.005702   0.203054  -0.3940250    0.007000    0.204000   -0.3942000
+46970.00   0.004581   0.203435  -0.3945922    0.006000    0.205000   -0.3948000
+46971.00   0.003328   0.203807  -0.3949640    0.005000    0.205000   -0.3951000
+46972.00   0.001879   0.204234  -0.3952013    0.003000    0.206000   -0.3954000
+46973.00   0.000318   0.204697  -0.3953915    0.002000    0.206000   -0.3956000
+46974.00  -0.001239   0.205162  -0.3956235    0.001000    0.207000   -0.3959000
+46975.00  -0.002680   0.205607  -0.3959716    0.000000    0.207000   -0.3963000
+46976.00  -0.003968   0.206069  -0.3964826   -0.002000    0.208000   -0.3969000
+46977.00  -0.005103   0.206556  -0.3971767   -0.003000    0.209000   -0.3976000
+46978.00  -0.006110   0.207053  -0.3980465   -0.004000    0.209000   -0.3985000
+46979.00  -0.007044   0.207548  -0.3990551   -0.005000    0.210000   -0.3996000
+46980.00  -0.007966   0.208033  -0.4001420   -0.007000    0.210000   -0.4006000
+46981.00  -0.008911   0.208509  -0.4012269   -0.008000    0.211000   -0.4017000
+46982.00  -0.009909   0.208988  -0.4022166   -0.009000    0.211000   -0.4026000
+46983.00  -0.010986   0.209478  -0.4030268   -0.010000    0.212000   -0.4034000
+46984.00  -0.012169   0.209989  -0.4036106   -0.012000    0.213000   -0.4039000
+46985.00  -0.013472   0.210517  -0.4039816   -0.013000    0.213000   -0.4042000
+46986.00  -0.014873   0.211044  -0.4042189   -0.014000    0.214000   -0.4044000
+46987.00  -0.016356   0.211553  -0.4044596   -0.016000    0.215000   -0.4046000
+46988.00  -0.017869   0.212064  -0.4048559   -0.017000    0.216000   -0.4050000
+46989.00  -0.019344   0.212606  -0.4055222   -0.018000    0.216000   -0.4057000
+46990.00  -0.020705   0.213196  -0.4064820   -0.019000    0.217000   -0.4066000
+46991.00  -0.021913   0.213841  -0.4076496   -0.020000    0.218000   -0.4078000
+46992.00  -0.022946   0.214544  -0.4088811   -0.021000    0.219000   -0.4090000
+46993.00  -0.023784   0.215306  -0.4100321   -0.022000    0.219000   -0.4101000
+46994.00  -0.024434   0.216111  -0.4109970   -0.023000    0.220000   -0.4111000
+46995.00  -0.024929   0.216938  -0.4117266   -0.023000    0.221000   -0.4118000
+46996.00  -0.025340   0.217783  -0.4122057   -0.024000    0.222000   -0.4123000
+46997.00  -0.025680   0.218647  -0.4124557   -0.024000    0.222000   -0.4126000
+46998.00  -0.025937   0.219537  -0.4125270   -0.025000    0.223000   -0.4126000
+46999.00  -0.026102   0.220452  -0.4124929   -0.025000    0.224000   -0.4126000
+47000.00  -0.026180   0.221376  -0.4124338   -0.025000    0.225000   -0.4125000
+47001.00  -0.026210   0.222271  -0.4124291   -0.025000    0.226000   -0.4125000
+47002.00  -0.026216   0.223132  -0.4125424   -0.026000    0.226000   -0.4126000
+47003.00  -0.026225   0.223964  -0.4128161   -0.026000    0.227000   -0.4129000
+47004.00  -0.026262   0.224772  -0.4132713   -0.026000    0.228000   -0.4133000
+47005.00  -0.026357   0.225559  -0.4139051   -0.026000    0.229000   -0.4140000
+47006.00  -0.026538   0.226339  -0.4146832   -0.026000    0.230000   -0.4148000
+47007.00  -0.026828   0.227130  -0.4155480   -0.027000    0.230000   -0.4156000
+47008.00  -0.027249   0.227951  -0.4164262   -0.027000    0.231000   -0.4165000
+47009.00  -0.027824   0.228830  -0.4172396   -0.027000    0.232000   -0.4174000
+47010.00  -0.028553   0.229784  -0.4179193   -0.028000    0.233000   -0.4181000
+47011.00  -0.029366   0.230769  -0.4184132   -0.028000    0.234000   -0.4186000
+47012.00  -0.030185   0.231733  -0.4187114   -0.028000    0.234000   -0.4189000
+47013.00  -0.030950   0.232651  -0.4188688   -0.029000    0.235000   -0.4191000
+47014.00  -0.031605   0.233510  -0.4190079   -0.030000    0.236000   -0.4193000
+47015.00  -0.032125   0.234318  -0.4192911   -0.030000    0.237000   -0.4195000
+47016.00  -0.032557   0.235103  -0.4198449   -0.030000    0.238000   -0.4200000
+47017.00  -0.032972   0.235882  -0.4207195   -0.031000    0.239000   -0.4209000
+47018.00  -0.033433   0.236668  -0.4218732   -0.032000    0.239000   -0.4221000
+47019.00  -0.033981   0.237483  -0.4231936   -0.032000    0.240000   -0.4234000
+47020.00  -0.034635   0.238350  -0.4245314   -0.032000    0.241000   -0.4248000
+47021.00  -0.035353   0.239274  -0.4257428   -0.033000    0.242000   -0.4261000
+47022.00  -0.036075   0.240246  -0.4267350   -0.034000    0.243000   -0.4271000
+47023.00  -0.036740   0.241261  -0.4274817   -0.034000    0.244000   -0.4279000
+47024.00  -0.037298   0.242313  -0.4280123   -0.034000    0.245000   -0.4284000
+47025.00  -0.037712   0.243403  -0.4283918   -0.035000    0.246000   -0.4288000
+47026.00  -0.038015   0.244553  -0.4286951   -0.035000    0.247000   -0.4291000
+47027.00  -0.038232   0.245777  -0.4289996   -0.035000    0.248000   -0.4293000
+47028.00  -0.038367   0.247084  -0.4293826   -0.036000    0.249000   -0.4297000
+47029.00  -0.038417   0.248479  -0.4299181   -0.036000    0.250000   -0.4301000
+47030.00  -0.038349   0.249945  -0.4306659   -0.036000    0.251000   -0.4308000
+47031.00  -0.038177   0.251444  -0.4316518   -0.036000    0.252000   -0.4317000
+47032.00  -0.037996   0.252925  -0.4328582   -0.036000    0.253000   -0.4327000
+47033.00  -0.037882   0.254343  -0.4342427   -0.036000    0.254000   -0.4340000
+47034.00  -0.037847   0.255669  -0.4357424   -0.036000    0.256000   -0.4354000
+47035.00  -0.037895   0.256887  -0.4372807   -0.037000    0.257000   -0.4369000
+47036.00  -0.038023   0.258032  -0.4387741   -0.037000    0.258000   -0.4383000
+47037.00  -0.038225   0.259111  -0.4401286   -0.037000    0.259000   -0.4395000
+47038.00  -0.038498   0.260125  -0.4412756   -0.037000    0.260000   -0.4406000
+47039.00  -0.038840   0.261097  -0.4421910   -0.037000    0.261000   -0.4415000
+47040.00  -0.039245   0.262047  -0.4429115   -0.037000    0.262000   -0.4423000
+47041.00  -0.039685   0.262965  -0.4435399   -0.038000    0.263000   -0.4430000
+47042.00  -0.040124   0.263832  -0.4442230   -0.038000    0.264000   -0.4437000
+47043.00  -0.040523   0.264630  -0.4451130   -0.038000    0.265000   -0.4447000
+47044.00  -0.040855   0.265354  -0.4463229   -0.039000    0.266000   -0.4460000
+47045.00  -0.041118   0.266020  -0.4478847   -0.039000    0.267000   -0.4476000
+47046.00  -0.041363   0.266679  -0.4497182   -0.040000    0.268000   -0.4495000
+47047.00  -0.041653   0.267387  -0.4516703   -0.040000    0.269000   -0.4514000
+47048.00  -0.042021   0.268187  -0.4535705   -0.040000    0.270000   -0.4534000
+47049.00  -0.042457   0.269114  -0.4552813   -0.041000    0.271000   -0.4551000
+47050.00  -0.042953   0.270192  -0.4567302   -0.041000    0.272000   -0.4565000
+47051.00  -0.043493   0.271416  -0.4579047   -0.042000    0.273000   -0.4577000
+47052.00  -0.044063   0.272731  -0.4588415   -0.042000    0.274000   -0.4587000
+47053.00  -0.044648   0.274073  -0.4596119   -0.043000    0.275000   -0.4595000
+47054.00  -0.045227   0.275365  -0.4603080   -0.043000    0.276000   -0.4603000
+47055.00  -0.045787   0.276532  -0.4610335   -0.044000    0.277000   -0.4611000
+47056.00  -0.046373   0.277570  -0.4618809   -0.045000    0.278000   -0.4620000
+47057.00  -0.046997   0.278534  -0.4629139   -0.045000    0.279000   -0.4631000
+47058.00  -0.047655   0.279488  -0.4641732   -0.046000    0.280000   -0.4644000
+47059.00  -0.048324   0.280493  -0.4656749   -0.046000    0.281000   -0.4659000
+47060.00  -0.048973   0.281599  -0.4674083   -0.047000    0.282000   -0.4676000
+47061.00  -0.049614   0.282841  -0.4693152   -0.048000    0.284000   -0.4694000
+47062.00  -0.050272   0.284240  -0.4713061   -0.048000    0.285000   -0.4714000
+47063.00  -0.050944   0.285787  -0.4732788   -0.049000    0.286000   -0.4732000
+47064.00  -0.051614   0.287435  -0.4751360   -0.050000    0.288000   -0.4750000
+47065.00  -0.052266   0.289135  -0.4768015   -0.050000    0.289000   -0.4765000
+47066.00  -0.052910   0.290855  -0.4782346   -0.051000    0.291000   -0.4779000
+47067.00  -0.053555   0.292544  -0.4794534   -0.052000    0.292000   -0.4791000
+47068.00  -0.054200   0.294149  -0.4805379   -0.052000    0.294000   -0.4802000
+47069.00  -0.054800   0.295654  -0.4816162   -0.053000    0.295000   -0.4813000
+47070.00  -0.055299   0.297063  -0.4828365   -0.054000    0.296000   -0.4826000
+47071.00  -0.055709   0.298407  -0.4843190   -0.055000    0.298000   -0.4842000
+47072.00  -0.056069   0.299702  -0.4861217   -0.055000    0.299000   -0.4861000
+47073.00  -0.056419   0.300959  -0.4882227   -0.056000    0.300000   -0.4883000
+47074.00  -0.056798   0.302186  -0.4905233   -0.056000    0.302000   -0.4906000
+47075.00  -0.057215   0.303393  -0.4928758   -0.057000    0.303000   -0.4930000
+47076.00  -0.057662   0.304588  -0.4951190   -0.058000    0.304000   -0.4952000
+47077.00  -0.058140   0.305779  -0.4971304   -0.058000    0.305000   -0.4971000
+47078.00  -0.058658   0.306976  -0.4988550   -0.059000    0.307000   -0.4988000
+47079.00  -0.059234   0.308184  -0.5003025   -0.059000    0.308000   -0.5002000
+47080.00  -0.059879   0.309408  -0.5015304   -0.060000    0.309000   -0.5014000
+47081.00  -0.060578   0.310627  -0.5026280   -0.060000    0.310000   -0.5025000
+47082.00  -0.061318   0.311812  -0.5036915   -0.060000    0.311000   -0.5035000
+47083.00  -0.062070   0.312940  -0.5048089   -0.061000    0.313000   -0.5046000
+47084.00  -0.062740   0.314034  -0.5060519   -0.061000    0.314000   -0.5059000
+47085.00  -0.063223   0.315126  -0.5074719   -0.062000    0.315000   -0.5073000
+47086.00  -0.063481   0.316244  -0.5090887   -0.062000    0.316000   -0.5089000
+47087.00  -0.063542   0.317388  -0.5108898   -0.062000    0.317000   -0.5107000
+47088.00  -0.063443   0.318552  -0.5128381   -0.062000    0.318000   -0.5127000
+47089.00  -0.063229   0.319720  -0.5148723   -0.062000    0.319000   -0.5147000
+47090.00  -0.062982   0.320871  -0.5169156   -0.063000    0.320000   -0.5168000
+47091.00  -0.062783   0.322005  -0.5188895   -0.063000    0.321000   -0.5187000
+47092.00  -0.062657   0.323114  -0.5207186   -0.063000    0.322000   -0.5205000
+47093.00  -0.062589   0.324213  -0.5223417   -0.063000    0.323000   -0.5220000
+47094.00  -0.062553   0.325320  -0.5237487   -0.063000    0.325000   -0.5234000
+47095.00  -0.062531   0.326452  -0.5249939   -0.063000    0.326000   -0.5246000
+47096.00  -0.062507   0.327621  -0.5261949   -0.063000    0.327000   -0.5257000
+47097.00  -0.062459   0.328833  -0.5274888   -0.063000    0.328000   -0.5271000
+47098.00  -0.062370   0.330089  -0.5289897   -0.063000    0.329000   -0.5286000
+47099.00  -0.062261   0.331385  -0.5307583   -0.063000    0.331000   -0.5304000
+47100.00  -0.062173   0.332711  -0.5327890   -0.062000    0.332000   -0.5325000
+47101.00  -0.062171   0.334055  -0.5350140   -0.062000    0.333000   -0.5348000
+47102.00  -0.062204   0.335394  -0.5373157   -0.062000    0.335000   -0.5372000
+47103.00  -0.062184   0.336699  -0.5395601   -0.062000    0.336000   -0.5394000
+47104.00  -0.062019   0.337962  -0.5416347   -0.061000    0.337000   -0.5415000
+47105.00  -0.061613   0.339246  -0.5434731   -0.061000    0.339000   -0.5434000
+47106.00  -0.060956   0.340659  -0.5450545   -0.060000    0.340000   -0.5450000
+47107.00  -0.060130   0.342210  -0.5464189   -0.060000    0.342000   -0.5463000
+47108.00  -0.059246   0.343883  -0.5476474   -0.060000    0.343000   -0.5476000
+47109.00  -0.058418   0.345665  -0.5488370   -0.059000    0.345000   -0.5487000
+47110.00  -0.057743   0.347539  -0.5500785   -0.059000    0.346000   -0.5500000
+47111.00  -0.057237   0.349460  -0.5514459   -0.059000    0.348000   -0.5513000
+47112.00  -0.056859   0.351377  -0.5529858   -0.058000    0.350000   -0.5528000
+47113.00  -0.056557   0.353241  -0.5547122   -0.058000    0.351000   -0.5545000
+47114.00  -0.056266   0.355008  -0.5566074   -0.058000    0.353000   -0.5564000
+47115.00  -0.055923   0.356643  -0.5586307   -0.057000    0.355000   -0.5584000
+47116.00  -0.055519   0.358163  -0.5607359   -0.057000    0.356000   -0.5605000
+47117.00  -0.055096   0.359613  -0.5628610   -0.056000    0.358000   -0.5627000
+47118.00  -0.054714   0.361041  -0.5649276   -0.056000    0.360000   -0.5648000
+47119.00  -0.054428   0.362491  -0.5668549   -0.056000    0.362000   -0.5667000
+47120.00  -0.054270   0.363988  -0.5685829   -0.056000    0.363000   -0.5685000
+47121.00  -0.054283   0.365542  -0.5700928   -0.056000    0.365000   -0.5700000
+47122.00  -0.054500   0.367150  -0.5714266   -0.056000    0.366000   -0.5713000
+47123.00  -0.054941   0.368800  -0.5726827   -0.056000    0.368000   -0.5726000
+47124.00  -0.055621   0.370474  -0.5739870   -0.057000    0.369000   -0.5740000
+47125.00  -0.056525   0.372135  -0.5754571   -0.057000    0.371000   -0.5756000
+47126.00  -0.057559   0.373744  -0.5771728   -0.057000    0.373000   -0.5774000
+47127.00  -0.058610   0.375255  -0.5791443   -0.058000    0.374000   -0.5795000
+47128.00  -0.059560   0.376646  -0.5813132   -0.058000    0.375000   -0.5817000
+47129.00  -0.060283   0.377940  -0.5835738   -0.058000    0.377000   -0.5840000
+47130.00  -0.060669   0.379173  -0.5857988   -0.058000    0.378000   -0.5863000
+47131.00  -0.060705   0.380381  -0.5878707   -0.058000    0.379000   -0.5884000
+47132.00  -0.060455   0.381576  -0.5897125   -0.058000    0.380000   -0.5903000
+47133.00  -0.059990   0.382762  -0.5912961   -0.057000    0.382000   -0.5918000
+47134.00  -0.059370   0.383937  -0.5926399   -0.057000    0.383000   -0.5931000
+47135.00  -0.058644   0.385103  -0.5937993   -0.056000    0.385000   -0.5942000
+47136.00  -0.057828   0.386266  -0.5948576   -0.056000    0.386000   -0.5952000
+47137.00  -0.056931   0.387435  -0.5959009   -0.055000    0.387000   -0.5962000
+47138.00  -0.055941   0.388625  -0.5970021   -0.054000    0.389000   -0.5972000
+47139.00  -0.054829   0.389872  -0.5982189   -0.053000    0.390000   -0.5985000
+47140.00  -0.053586   0.391201  -0.5995910   -0.052000    0.391000   -0.5998000
+47141.00  -0.052243   0.392604  -0.6011298   -0.051000    0.393000   -0.6013000
+47142.00  -0.050835   0.394060  -0.6028227   -0.050000    0.394000   -0.6030000
+47143.00  -0.049410   0.395542  -0.6046343   -0.049000    0.395000   -0.6048000
+47144.00  -0.048052   0.396989  -0.6065087   -0.048000    0.396000   -0.6067000
+47145.00  -0.046844   0.398339  -0.6083731   -0.047000    0.397000   -0.6086000
+47146.00  -0.045784   0.399552  -0.6101502   -0.045000    0.399000   -0.6104000
+47147.00  -0.044816   0.400625  -0.6117713   -0.044000    0.400000   -0.6120000
+47148.00  -0.043878   0.401565  -0.6132009   -0.043000    0.401000   -0.6135000
+47149.00  -0.042903   0.402385  -0.6144610   -0.042000    0.402000   -0.6147000
+47150.00  -0.041828   0.403123  -0.6156405   -0.040000    0.403000   -0.6159000
+47151.00  -0.040607   0.403827  -0.6168739   -0.039000    0.404000   -0.6170000
+47152.00  -0.039281   0.404537  -0.6182946   -0.038000    0.405000   -0.6184000
+47153.00  -0.037899   0.405269  -0.6199903   -0.036000    0.406000   -0.6201000
+47154.00  -0.036458   0.406039  -0.6219738   -0.035000    0.407000   -0.6221000
+47155.00  -0.034936   0.406865  -0.6241775   -0.033000    0.408000   -0.6243000
+47156.00  -0.033335   0.407754  -0.6264776   -0.032000    0.409000   -0.6266000
+47157.00  -0.031657   0.408711  -0.6287351   -0.030000    0.410000   -0.6288000
+47158.00  -0.029907   0.409744  -0.6308325   -0.028000    0.411000   -0.6309000
+47159.00  -0.028088   0.410856  -0.6326933   -0.027000    0.412000   -0.6327000
+47160.00  -0.026206   0.412048  -0.6342906   -0.025000    0.413000   -0.6343000
+47161.00  -0.024282   0.413287   0.3643500   -0.023000    0.414000    0.3643000
+47162.00  -0.022333   0.414528   0.3631681   -0.021000    0.415000    0.3631000
+47163.00  -0.020379   0.415724   0.3620846   -0.019000    0.416000    0.3621000
+47164.00  -0.018437   0.416831   0.3610156   -0.017000    0.417000    0.3610000
+47165.00  -0.016529   0.417809   0.3598894   -0.015000    0.418000    0.3598000
+47166.00  -0.014674   0.418643   0.3586506   -0.013000    0.419000    0.3586000
+47167.00  -0.012861   0.419338   0.3572612   -0.011000    0.420000    0.3571000
+47168.00  -0.011080   0.419907   0.3557089   -0.010000    0.421000    0.3555000
+47169.00  -0.009315   0.420360   0.3540054   -0.008000    0.422000    0.3537000
+47170.00  -0.007555   0.420728   0.3521806   -0.006000    0.423000    0.3519000
+47171.00  -0.005836   0.421099   0.3502919   -0.004000    0.423000    0.3500000
+47172.00  -0.004197   0.421557   0.3484143   -0.003000    0.424000    0.3481000
+47173.00  -0.002659   0.422118   0.3466188   -0.001000    0.425000    0.3464000
+47174.00  -0.001282   0.422756   0.3449689    0.000000    0.425000    0.3447000
+47175.00  -0.000119   0.423392   0.3435070    0.002000    0.426000    0.3434000
+47176.00   0.000915   0.423966   0.3422443    0.003000    0.426000    0.3422000
+47177.00   0.001971   0.424454   0.3411366    0.005000    0.427000    0.3412000
+47178.00   0.003149   0.424857   0.3400813    0.006000    0.427000    0.3403000
+47179.00   0.004431   0.425192   0.3389456    0.008000    0.428000    0.3392000
+47180.00   0.005795   0.425472   0.3376186    0.009000    0.428000    0.3379000
+47181.00   0.007263   0.425747   0.3360472    0.011000    0.429000    0.3363000
+47182.00   0.008856   0.426060   0.3342621    0.012000    0.429000    0.3344000
+47183.00   0.010591   0.426454   0.3323757    0.014000    0.430000    0.3324000
+47184.00   0.012479   0.426949   0.3305363    0.016000    0.430000    0.3305000
+47185.00   0.014531   0.427485   0.3288699    0.018000    0.431000    0.3287000
+47186.00   0.016729   0.428019   0.3274564    0.020000    0.431000    0.3272000
+47187.00   0.019043   0.428523   0.3263147    0.022000    0.431000    0.3260000
+47188.00   0.021473   0.428978   0.3254055    0.024000    0.432000    0.3250000
+47189.00   0.024050   0.429387   0.3246553    0.027000    0.432000    0.3242000
+47190.00   0.026750   0.429743   0.3239802    0.029000    0.433000    0.3235000
+47191.00   0.029541   0.430052   0.3232923    0.031000    0.433000    0.3227000
+47192.00   0.032380   0.430341   0.3225081    0.033000    0.433000    0.3220000
+47193.00   0.035225   0.430638   0.3215594    0.036000    0.434000    0.3210000
+47194.00   0.038029   0.430972   0.3204005    0.038000    0.434000    0.3199000
+47195.00   0.040717   0.431369   0.3190133    0.041000    0.435000    0.3185000
+47196.00   0.043197   0.431846   0.3174101    0.044000    0.435000    0.3169000
+47197.00   0.045436   0.432383   0.3156276    0.046000    0.435000    0.3152000
+47198.00   0.047417   0.432934   0.3137210    0.049000    0.435000    0.3133000
+47199.00   0.049127   0.433448   0.3117564    0.051000    0.435000    0.3114000
+47200.00   0.050562   0.433883   0.3098046    0.053000    0.435000    0.3095000
+47201.00   0.051772   0.434212   0.3079399    0.054000    0.435000    0.3077000
+47202.00   0.052828   0.434415   0.3062239    0.056000    0.435000    0.3061000
+47203.00   0.053789   0.434472   0.3046859    0.057000    0.435000    0.3046000
+47204.00   0.054660   0.434371   0.3033032    0.058000    0.434000    0.3033000
+47205.00   0.055477   0.434080   0.3019846    0.059000    0.434000    0.3019000
+47206.00   0.056309   0.433626   0.3005816    0.060000    0.434000    0.3005000
+47207.00   0.057220   0.433069   0.2989343    0.061000    0.434000    0.2988000
+47208.00   0.058272   0.432465   0.2969319    0.062000    0.433000    0.2967000
+47209.00   0.059504   0.431860   0.2945576    0.063000    0.433000    0.2943000
+47210.00   0.060894   0.431273   0.2919006    0.064000    0.433000    0.2916000
+47211.00   0.062344   0.430743   0.2891320    0.065000    0.433000    0.2889000
+47212.00   0.063722   0.430316   0.2864434    0.066000    0.432000    0.2862000
+47213.00   0.064928   0.430027   0.2839871    0.067000    0.432000    0.2838000
+47214.00   0.065945   0.429861   0.2818466    0.068000    0.432000    0.2817000
+47215.00   0.066799   0.429796   0.2800276    0.069000    0.432000    0.2798000
+47216.00   0.067596   0.429844   0.2784642    0.071000    0.432000    0.2782000
+47217.00   0.068476   0.429993   0.2770556    0.072000    0.432000    0.2768000
+47218.00   0.069557   0.430227   0.2756960    0.073000    0.431000    0.2754000
+47219.00   0.070903   0.430544   0.2742860    0.075000    0.431000    0.2739000
+47220.00   0.072553   0.430941   0.2727442    0.076000    0.432000    0.2724000
+47221.00   0.074468   0.431406   0.2710225    0.078000    0.432000    0.2707000
+47222.00   0.076565   0.431907   0.2691009    0.080000    0.432000    0.2688000
+47223.00   0.078739   0.432401   0.2669896    0.082000    0.432000    0.2667000
+47224.00   0.080892   0.432834   0.2647288    0.084000    0.432000    0.2646000
+47225.00   0.082956   0.433137   0.2623834    0.086000    0.432000    0.2624000
+47226.00   0.084978   0.433274   0.2600346    0.088000    0.432000    0.2602000
+47227.00   0.087034   0.433225   0.2577686    0.090000    0.432000    0.2581000
+47228.00   0.089182   0.432984   0.2556665    0.092000    0.432000    0.2562000
+47229.00   0.091479   0.432548   0.2537920    0.095000    0.432000    0.2544000
+47230.00   0.093968   0.431925   0.2521810    0.097000    0.432000    0.2530000
+47231.00   0.096616   0.431178   0.2508327    0.100000    0.431000    0.2516000
+47232.00   0.099373   0.430378   0.2496800    0.102000    0.431000    0.2505000
+47233.00   0.102178   0.429573   0.2485988    0.104000    0.430000    0.2493000
+47234.00   0.104954   0.428784   0.2474383    0.107000    0.429000    0.2480000
+47235.00   0.107628   0.428032   0.2460608    0.109000    0.428000    0.2465000
+47236.00   0.110146   0.427325   0.2443810    0.112000    0.428000    0.2446000
+47237.00   0.112463   0.426598   0.2424056    0.114000    0.427000    0.2424000
+47238.00   0.114535   0.425771   0.2402340    0.116000    0.425000    0.2401000
+47239.00   0.116348   0.424764   0.2380222    0.118000    0.424000    0.2378000
+47240.00   0.117957   0.423510   0.2359328    0.120000    0.423000    0.2356000
+47241.00   0.119427   0.422058   0.2340876    0.122000    0.422000    0.2338000
+47242.00   0.120827   0.420480   0.2325317    0.123000    0.420000    0.2322000
+47243.00   0.122229   0.418839   0.2312297    0.125000    0.419000    0.2309000
+47244.00   0.123705   0.417199   0.2300952    0.126000    0.418000    0.2298000
+47245.00   0.125305   0.415613   0.2290276    0.128000    0.417000    0.2287000
+47246.00   0.126960   0.414115   0.2279249    0.129000    0.415000    0.2276000
+47247.00   0.128550   0.412734   0.2267026    0.130000    0.414000    0.2264000
+47248.00   0.129982   0.411461   0.2252995    0.131000    0.413000    0.2250000
+47249.00   0.131179   0.410263   0.2236844    0.132000    0.412000    0.2233000
+47250.00   0.132077   0.409106   0.2218552    0.133000    0.410000    0.2215000
+47251.00   0.132689   0.407969   0.2198328    0.134000    0.409000    0.2195000
+47252.00   0.133060   0.406819   0.2176635    0.134000    0.407000    0.2173000
+47253.00   0.133242   0.405621   0.2154137    0.135000    0.406000    0.2151000
+47254.00   0.133291   0.404331   0.2131608    0.136000    0.405000    0.2128000
+47255.00   0.133276   0.402927   0.2109864    0.136000    0.403000    0.2107000
+47256.00   0.133296   0.401435   0.2089597    0.137000    0.402000    0.2087000
+47257.00   0.133451   0.399885   0.2071178    0.137000    0.400000    0.2069000
+47258.00   0.133796   0.398302   0.2054537    0.138000    0.399000    0.2052000
+47259.00   0.134366   0.396706   0.2039126    0.138000    0.397000    0.2037000
+47260.00   0.135164   0.395107   0.2023987    0.139000    0.396000    0.2021000
+47261.00   0.136105   0.393495   0.2007934    0.140000    0.394000    0.2005000
+47262.00   0.137096   0.391851   0.1989811    0.141000    0.392000    0.1987000
+47263.00   0.138122   0.390169   0.1968751    0.142000    0.391000    0.1965000
+47264.00   0.139252   0.388462   0.1944597    0.143000    0.389000    0.1941000
+47265.00   0.140573   0.386753   0.1918060    0.144000    0.388000    0.1914000
+47266.00   0.142124   0.385078   0.1890631    0.146000    0.386000    0.1887000
+47267.00   0.143839   0.383464   0.1864171    0.147000    0.385000    0.1862000
+47268.00   0.145638   0.381931   0.1840284    0.148000    0.383000    0.1838000
+47269.00   0.147446   0.380503   0.1819888    0.150000    0.382000    0.1818000
+47270.00   0.149227   0.379205   0.1803083    0.151000    0.381000    0.1802000
+47271.00   0.150923   0.378018   0.1789243    0.153000    0.379000    0.1788000
+47272.00   0.152480   0.376907   0.1777309    0.154000    0.378000    0.1776000
+47273.00   0.153883   0.375832   0.1766178    0.156000    0.377000    0.1764000
+47274.00   0.155182   0.374728   0.1754892    0.157000    0.374000    0.1751000
+47275.00   0.156463   0.373523   0.1742706    0.159000    0.373000    0.1738000
+47276.00   0.157752   0.372218   0.1729094    0.160000    0.372000    0.1724000
+47277.00   0.159044   0.370851   0.1713784    0.162000    0.370000    0.1708000
+47278.00   0.160340   0.369461   0.1696762    0.163000    0.369000    0.1690000
+47279.00   0.161671   0.368081   0.1678264    0.165000    0.368000    0.1671000
+47280.00   0.163064   0.366725   0.1658776    0.166000    0.367000    0.1652000
+47281.00   0.164477   0.365389   0.1639065    0.167000    0.366000    0.1633000
+47282.00   0.165855   0.364063   0.1620006    0.169000    0.364000    0.1614000
+47283.00   0.167147   0.362735   0.1602385    0.170000    0.363000    0.1597000
+47284.00   0.168285   0.361394   0.1586712    0.171000    0.363000    0.1582000
+47285.00   0.169162   0.360021   0.1573034    0.172000    0.362000    0.1569000
+47286.00   0.169788   0.358587   0.1560843    0.173000    0.360000    0.1557000
+47287.00   0.170224   0.357071   0.1549187    0.173000    0.359000    0.1545000
+47288.00   0.170572   0.355490   0.1536860    0.174000    0.357000    0.1533000
+47289.00   0.170939   0.353873   0.1522705    0.174000    0.355000    0.1519000
+47290.00   0.171422   0.352246   0.1505918    0.175000    0.354000    0.1502000
+47291.00   0.172031   0.350624   0.1486276    0.176000    0.352000    0.1482000
+47292.00   0.172740   0.349018   0.1464257    0.176000    0.351000    0.1460000
+47293.00   0.173522   0.347441   0.1440946    0.177000    0.349000    0.1436000
+47294.00   0.174315   0.345886   0.1417745    0.177000    0.347000    0.1413000
+47295.00   0.175061   0.344342   0.1396002    0.178000    0.346000    0.1392000
+47296.00   0.175731   0.342790   0.1376748    0.178000    0.344000    0.1373000
+47297.00   0.176269   0.341207   0.1360396    0.178000    0.342000    0.1357000
+47298.00   0.176616   0.339565   0.1346682    0.179000    0.340000    0.1345000
+47299.00   0.176737   0.337839   0.1334901    0.179000    0.339000    0.1333000
+47300.00   0.176689   0.336003   0.1324249    0.179000    0.337000    0.1323000
+47301.00   0.176571   0.334072   0.1313853    0.179000    0.335000    0.1313000
+47302.00   0.176472   0.332078   0.1302903    0.179000    0.333000    0.1301000
+47303.00   0.176453   0.330055   0.1290823    0.179000    0.331000    0.1288000
+47304.00   0.176504   0.328048   0.1277310    0.178000    0.330000    0.1274000
+47305.00   0.176508   0.326097   0.1262273    0.178000    0.328000    0.1258000
+47306.00   0.176371   0.324224   0.1245896    0.177000    0.326000    0.1241000
+47307.00   0.176036   0.322405   0.1228631    0.177000    0.324000    0.1224000
+47308.00   0.175458   0.320609   0.1211064    0.176000    0.323000    0.1206000
+47309.00   0.174650   0.318806   0.1193840    0.176000    0.321000    0.1189000
+47310.00   0.173647   0.316974   0.1177611    0.175000    0.319000    0.1174000
+47311.00   0.172508   0.315109   0.1163064    0.174000    0.317000    0.1161000
+47312.00   0.171313   0.313217   0.1150594    0.174000    0.315000    0.1149000
+47313.00   0.170143   0.311304   0.1140094    0.173000    0.313000    0.1140000
+47314.00   0.169074   0.309379   0.1130943    0.172000    0.311000    0.1132000
+47315.00   0.168160   0.307456   0.1122126    0.171000    0.309000    0.1122000
+47316.00   0.167385   0.305554   0.1112338    0.170000    0.307000    0.1112000
+47317.00   0.166735   0.303687   0.1100538    0.170000    0.304000    0.1099000
+47318.00   0.166213   0.301859   0.1086331    0.169000    0.302000    0.1083000
+47319.00   0.165809   0.300064   0.1070061    0.168000    0.300000    0.1066000
+47320.00   0.165500   0.298286   0.1052785    0.168000    0.298000    0.1048000
+47321.00   0.165260   0.296515   0.1035866    0.168000    0.296000    0.1030000
+47322.00   0.165075   0.294741   0.1020572    0.168000    0.294000    0.1015000
+47323.00   0.164939   0.292954   0.1007806    0.167000    0.292000    0.1002000
+47324.00   0.164891   0.291145   0.0997917    0.167000    0.290000    0.0992000
+47325.00   0.164964   0.289308   0.0990674    0.167000    0.289000    0.0985000
+47326.00   0.165103   0.287445   0.0985414    0.167000    0.287000    0.0980000
+47327.00   0.165220   0.285551   0.0981234    0.167000    0.285000    0.0976000
+47328.00   0.165235   0.283612   0.0977212    0.167000    0.283000    0.0973000
+47329.00   0.165078   0.281610   0.0972599    0.168000    0.281000    0.0969000
+47330.00   0.164740   0.279509   0.0966944    0.168000    0.279000    0.0964000
+47331.00   0.164408   0.277292   0.0960175    0.168000    0.277000    0.0957000
+47332.00   0.164308   0.274976   0.0952407    0.168000    0.275000    0.0950000
+47333.00   0.164459   0.272627   0.0943817    0.169000    0.273000    0.0941000
+47334.00   0.164844   0.270336   0.0934727    0.169000    0.271000    0.0932000
+47335.00   0.165440   0.268181   0.0925620    0.169000    0.269000    0.0922000
+47336.00   0.166198   0.266171   0.0917134    0.170000    0.267000    0.0914000
+47337.00   0.167033   0.264291   0.0909916    0.170000    0.265000    0.0907000
+47338.00   0.167857   0.262495   0.0904523    0.170000    0.263000    0.0901000
+47339.00   0.168577   0.260716   0.0901316    0.171000    0.261000    0.0898000
+47340.00   0.169078   0.258899   0.0900288    0.171000    0.259000    0.0897000
+47341.00   0.169346   0.257053   0.0900763    0.171000    0.257000    0.0897000
+47342.00   0.169407   0.255191   0.0901508    0.171000    0.255000    0.0897000
+47343.00   0.169293   0.253312   0.0901073    0.171000    0.253000    0.0897000
+47344.00   0.169043   0.251413   0.0898241    0.171000    0.251000    0.0893000
+47345.00   0.168718   0.249492   0.0892455    0.171000    0.249000    0.0887000
+47346.00   0.168392   0.247551   0.0883989    0.171000    0.247000    0.0878000
+47347.00   0.168128   0.245587   0.0873872    0.171000    0.246000    0.0868000
+47348.00   0.167947   0.243581   0.0863579    0.171000    0.244000    0.0858000
+47349.00   0.167849   0.241539   0.0854474    0.170000    0.242000    0.0849000
+47350.00   0.167747   0.239484   0.0847557    0.170000    0.240000    0.0842000
+47351.00   0.167560   0.237432   0.0843280    0.169000    0.239000    0.0838000
+47352.00   0.167240   0.235399   0.0841458    0.169000    0.237000    0.0836000
+47353.00   0.166830   0.233390   0.0841389    0.169000    0.235000    0.0837000
+47354.00   0.166361   0.231408   0.0842164    0.168000    0.233000    0.0838000
+47355.00   0.165858   0.229461   0.0842809    0.168000    0.231000    0.0838000
+47356.00   0.165381   0.227575   0.0842424    0.167000    0.229000    0.0838000
+47357.00   0.164937   0.225745   0.0840428    0.167000    0.228000    0.0837000
+47358.00   0.164436   0.223927   0.0836617    0.166000    0.226000    0.0833000
+47359.00   0.163829   0.222063   0.0831075    0.165000    0.223000    0.0828000
+47360.00   0.163209   0.220095   0.0824115    0.165000    0.221000    0.0821000
+47361.00   0.162581   0.218034   0.0816152    0.164000    0.219000    0.0813000
+47362.00   0.161910   0.215909   0.0807731    0.163000    0.217000    0.0804000
+47363.00   0.161156   0.213768   0.0799456    0.162000    0.215000    0.0796000
+47364.00   0.160277   0.211659   0.0791869    0.161000    0.213000    0.0788000
+47365.00   0.159233   0.209618   0.0785392    0.160000    0.211000    0.0782000
+47366.00   0.158046   0.207629   0.0780408    0.159000    0.209000    0.0777000
+47367.00   0.156759   0.205662   0.0777049    0.158000    0.207000    0.0775000
+47368.00   0.155411   0.203688   0.0775034    0.156000    0.205000    0.0773000
+47369.00   0.154067   0.201711   0.0773500    0.155000    0.203000    0.0772000
+47370.00   0.152797   0.199746   0.0771104    0.154000    0.201000    0.0770000
+47371.00   0.151642   0.197803   0.0766462    0.153000    0.199000    0.0765000
+47372.00   0.150613   0.195889   0.0758730    0.152000    0.198000    0.0758000
+47373.00   0.149680   0.193991   0.0748029    0.150000    0.196000    0.0747000
+47374.00   0.148737   0.192093   0.0735477    0.149000    0.194000    0.0734000
+47375.00   0.147679   0.190193   0.0722613    0.148000    0.192000    0.0720000
+47376.00   0.146460   0.188283   0.0710986    0.147000    0.190000    0.0708000
+47377.00   0.145064   0.186359   0.0701735    0.145000    0.188000    0.0698000
+47378.00   0.143549   0.184427   0.0695288    0.144000    0.185000    0.0691000
+47379.00   0.141975   0.182503   0.0691416    0.142000    0.184000    0.0687000
+47380.00   0.140372   0.180588   0.0689507    0.141000    0.182000    0.0686000
+47381.00   0.138711   0.178690   0.0688957    0.140000    0.180000    0.0685000
+47382.00   0.137032   0.176831   0.0688826    0.138000    0.178000    0.0685000
+47383.00   0.135399   0.175036   0.0688161    0.137000    0.177000    0.0684000
+47384.00   0.133883   0.173345   0.0686211    0.135000    0.175000    0.0682000
+47385.00   0.132534   0.171798   0.0682485    0.134000    0.174000    0.0678000
+47386.00   0.131275   0.170397   0.0676839    0.132000    0.172000    0.0672000
+47387.00   0.130008   0.169109   0.0669456    0.131000    0.171000    0.0665000
+47388.00   0.128643   0.167903   0.0660771    0.129000    0.169000    0.0656000
+47389.00   0.127093   0.166742   0.0651379    0.127000    0.168000    0.0647000
+47390.00   0.125331   0.165595   0.0641926    0.125000    0.167000    0.0637000
+47391.00   0.123406   0.164434   0.0633086    0.124000    0.165000    0.0629000
+47392.00   0.121374   0.163241   0.0625467    0.122000    0.164000    0.0622000
+47393.00   0.119274   0.162008   0.0619491    0.120000    0.163000    0.0616000
+47394.00   0.117135   0.160741   0.0615310    0.118000    0.161000    0.0612000
+47395.00   0.114975   0.159443   0.0612741    0.116000    0.160000    0.0610000
+47396.00   0.112786   0.158136   0.0611109    0.114000    0.159000    0.0608000
+47397.00   0.110555   0.156842   0.0609245    0.112000    0.158000    0.0606000
+47398.00   0.108275   0.155581   0.0605739    0.110000    0.156000    0.0602000
+47399.00   0.105966   0.154361   0.0599372    0.108000    0.155000    0.0595000
+47400.00   0.103668   0.153183   0.0589596    0.105000    0.154000    0.0584000
+47401.00   0.101413   0.152048   0.0576822    0.103000    0.153000    0.0570000
+47402.00   0.099179   0.150947   0.0562406    0.101000    0.152000    0.0555000
+47403.00   0.096931   0.149870   0.0548202    0.099000    0.151000    0.0540000
+47404.00   0.094639   0.148808   0.0535890    0.097000    0.150000    0.0527000
+47405.00   0.092289   0.147762   0.0526372    0.094000    0.149000    0.0517000
+47406.00   0.089921   0.146743   0.0519722    0.092000    0.148000    0.0511000
+47407.00   0.087577   0.145762   0.0515357    0.090000    0.147000    0.0507000
+47408.00   0.085267   0.144806   0.0512297    0.088000    0.146000    0.0504000
+47409.00   0.082984   0.143851   0.0509546    0.086000    0.145000    0.0503000
+47410.00   0.080727   0.142877   0.0506274    0.083000    0.144000    0.0501000
+47411.00   0.078490   0.141894   0.0501843    0.081000    0.143000    0.0497000
+47412.00   0.076267   0.140924   0.0495867    0.079000    0.142000    0.0492000
+47413.00   0.074057   0.139990   0.0488225    0.076000    0.141000    0.0485000
+47414.00   0.071886   0.139112   0.0479034    0.074000    0.141000    0.0476000
+47415.00   0.069777   0.138302   0.0468630    0.071000    0.140000    0.0466000
+47416.00   0.067686   0.137557   0.0457511    0.069000    0.139000    0.0454000
+47417.00   0.065546   0.136864   0.0446227    0.067000    0.138000    0.0442000
+47418.00   0.063293   0.136209   0.0435312    0.064000    0.138000    0.0431000
+47419.00   0.060869   0.135577   0.0425233    0.062000    0.137000    0.0420000
+47420.00   0.058265   0.134961   0.0416315    0.059000    0.136000    0.0411000
+47421.00   0.055502   0.134348   0.0408676    0.056000    0.136000    0.0404000
+47422.00   0.052603   0.133723   0.0402159    0.053000    0.135000    0.0397000
+47423.00   0.049581   0.133086   0.0396256    0.050000    0.135000    0.0392000
+47424.00   0.046436   0.132471   0.0390073    0.047000    0.134000    0.0386000
+47425.00   0.043170   0.131913   0.0382443    0.044000    0.134000    0.0379000
+47426.00   0.039824   0.131419   0.0372149    0.041000    0.133000    0.0369000
+47427.00   0.036435   0.130977   0.0358406    0.037000    0.133000    0.0355000
+47428.00   0.033043   0.130578   0.0341172    0.034000    0.133000    0.0338000
+47429.00   0.029697   0.130234   0.0321301    0.031000    0.132000    0.0318000
+47430.00   0.026438   0.129962   0.0300408    0.027000    0.132000    0.0297000
+47431.00   0.023217   0.129768   0.0280465    0.024000    0.132000    0.0277000
+47432.00   0.019958   0.129647   0.0263097    0.021000    0.132000    0.0260000
+47433.00   0.016604   0.129578   0.0249050    0.018000    0.132000    0.0245000
+47434.00   0.013117   0.129525   0.0238086    0.014000    0.132000    0.0234000
+47435.00   0.009537   0.129435   0.0229268    0.011000    0.132000    0.0225000
+47436.00   0.005981   0.129326   0.0221395    0.008000    0.132000    0.0216000
+47437.00   0.002559   0.129250   0.0213314    0.005000    0.132000    0.0207000
+47438.00  -0.000668   0.129269   0.0204069    0.002000    0.132000    0.0198000
+47439.00  -0.003644   0.129446   0.0193018   -0.001600    0.132100    0.0186300
+47440.00  -0.006348   0.129831   0.0179812   -0.004800    0.132500    0.0172900
+47441.00  -0.008941   0.130419   0.0164502   -0.008000    0.133000    0.0157700
+47442.00  -0.011635   0.131184   0.0147438   -0.011200    0.133500    0.0141000
+47443.00  -0.014534   0.132069   0.0129147   -0.014400    0.134100    0.0123300
+47444.00  -0.017668   0.132983   0.0110303   -0.017600    0.134700    0.0105200
+47445.00  -0.021041   0.133834   0.0091639   -0.020800    0.135300    0.0087500
+47446.00  -0.024606   0.134593   0.0073898   -0.024000    0.136000    0.0070800
+47447.00  -0.028288   0.135265   0.0057691   -0.027200    0.136700    0.0055600
+47448.00  -0.031998   0.135860   0.0043350   -0.030400    0.137400    0.0042200
+47449.00  -0.035649   0.136389   0.0030862   -0.033600    0.138100    0.0030300
+47450.00  -0.039166   0.136885   0.0019811   -0.036800    0.138800    0.0019600
+47451.00  -0.042495   0.137399   0.0009354   -0.039900    0.139500    0.0009200
+47452.00  -0.045595   0.137986  -0.0001641   -0.043000    0.140300   -0.0002100
+47453.00  -0.048446   0.138691  -0.0014361   -0.046100    0.141100   -0.0015300
+47454.00  -0.051072   0.139535  -0.0029797   -0.049200    0.141900   -0.0031300
+47455.00  -0.053561   0.140503  -0.0048462   -0.052300    0.142800   -0.0050600
+47456.00  -0.056022   0.141580  -0.0069989   -0.055300    0.143700   -0.0072600
+47457.00  -0.058573   0.142753  -0.0093158   -0.058300    0.144700   -0.0096200
+47458.00  -0.061320   0.144005  -0.0116231   -0.061300    0.145700   -0.0119800
+47459.00  -0.064320   0.145306  -0.0137447   -0.064300    0.146800   -0.0141400
+47460.00  -0.067585   0.146623  -0.0155610   -0.067300    0.148000   -0.0160000
+47461.00  -0.071008   0.147942  -0.0170483   -0.070300    0.149200   -0.0175400
+47462.00  -0.074448   0.149283  -0.0182697   -0.073200    0.150600   -0.0188100
+47463.00  -0.077762   0.150669  -0.0193425   -0.076200    0.151900   -0.0199300
+47464.00  -0.080813   0.152136  -0.0203951   -0.079100    0.153400   -0.0210200
+47465.00  -0.083525   0.153718  -0.0215348   -0.082000    0.154900   -0.0222000
+47466.00  -0.086039   0.155397  -0.0228325   -0.084900    0.156500   -0.0235300
+47467.00  -0.088557   0.157148  -0.0243200   -0.087700    0.158200   -0.0250200
+47468.00  -0.091194   0.158948  -0.0259991   -0.090500    0.159900   -0.0266900
+47469.00  -0.093909   0.160779  -0.0278487   -0.093200    0.161700   -0.0285100
+47470.00  -0.096631   0.162627  -0.0298275   -0.095900    0.163500   -0.0304200
+47471.00  -0.099338   0.164502  -0.0318693   -0.098600    0.165400   -0.0323700
+47472.00  -0.102060   0.166418  -0.0338902   -0.101300    0.167300   -0.0343100
+47473.00  -0.104826   0.168374  -0.0358126   -0.103900    0.169200   -0.0361700
+47474.00  -0.107616   0.170322  -0.0375893   -0.106400    0.171200   -0.0378900
+47475.00  -0.110370   0.172220  -0.0391988   -0.108800    0.173200   -0.0394700
+47476.00  -0.112960   0.174102  -0.0406350   -0.111200    0.175300   -0.0408900
+47477.00  -0.115258   0.176073  -0.0419216   -0.113400    0.177400   -0.0421800
+47478.00  -0.117269   0.178146  -0.0431352   -0.115700    0.179600   -0.0434200
+47479.00  -0.119034   0.180309  -0.0443845   -0.117800    0.181800   -0.0447200
+47480.00  -0.120585   0.182550  -0.0457852   -0.119900    0.184100   -0.0461700
+47481.00  -0.121991   0.184885  -0.0474319   -0.121900    0.186400   -0.0478600
+47482.00  -0.123340   0.187332  -0.0493727   -0.123900    0.188800   -0.0498400
+47483.00  -0.124708   0.189888  -0.0515897   -0.125800    0.191300   -0.0520900
+47484.00  -0.126172   0.192541  -0.0539944   -0.127700    0.193800   -0.0545100
+47485.00  -0.127798   0.195270  -0.0564475   -0.129600    0.196400   -0.0569700
+47486.00  -0.129602   0.198043  -0.0587926   -0.131400    0.199000   -0.0593200
+47487.00  -0.131586   0.200833  -0.0609012   -0.133300    0.201700   -0.0614200
+47488.00  -0.133739   0.203614  -0.0627064   -0.135100    0.204400   -0.0632200
+47489.00  -0.135980   0.206392  -0.0642198   -0.136900    0.207200   -0.0647300
+47490.00  -0.138213   0.209184  -0.0655199   -0.138700    0.210000   -0.0660300
+47491.00  -0.140403   0.212013  -0.0667258   -0.140500    0.212800   -0.0672500
+47492.00  -0.142552   0.214861  -0.0679543   -0.142300    0.215700   -0.0684900
+47493.00  -0.144665   0.217698  -0.0692912   -0.144000    0.218500   -0.0698400
+47494.00  -0.146749   0.220493  -0.0707816   -0.145700    0.221400   -0.0713400
+47495.00  -0.148794   0.223228  -0.0724359   -0.147400    0.224300   -0.0729900
+47496.00  -0.150745   0.225929  -0.0742397   -0.149000    0.227100   -0.0747800
+47497.00  -0.152550   0.228623  -0.0761494   -0.150600    0.230000   -0.0766700
+47498.00  -0.154162   0.231337  -0.0781025   -0.152100    0.232900   -0.0785900
+47499.00  -0.155533   0.234098  -0.0800263   -0.153600    0.235800   -0.0804900
+47500.00  -0.156686   0.236928  -0.0818496   -0.155100    0.238700   -0.0822900
+47501.00  -0.157701   0.239829  -0.0835181   -0.156400    0.241500   -0.0839300
+47502.00  -0.158659   0.242801  -0.0849965   -0.157700    0.244400   -0.0853800
+47503.00  -0.159628   0.245841  -0.0862774   -0.158900    0.247200   -0.0866400
+47504.00  -0.160621   0.248931  -0.0873914   -0.160000    0.250100   -0.0877300
+47505.00  -0.161634   0.252042  -0.0884065   -0.160900    0.253000   -0.0887000
+47506.00  -0.162667   0.255108  -0.0894034   -0.161700    0.255900   -0.0896700
+47507.00  -0.163707   0.258085  -0.0904717   -0.162400    0.258800   -0.0907500
+47508.00  -0.164702   0.260986  -0.0917109   -0.163000    0.261700   -0.0920300
+47509.00  -0.165583   0.263835  -0.0931807   -0.163500    0.264600   -0.0935700
+47510.00  -0.166239   0.266649  -0.0948778   -0.164000    0.267500   -0.0953200
+47511.00  -0.166572   0.269463  -0.0967332   -0.164400    0.270400   -0.0972300
+47512.00  -0.166652   0.272299  -0.0986221   -0.164700    0.273300   -0.0991700
+47513.00  -0.166579   0.275166  -0.1004058   -0.164900    0.276200   -0.1009900
+47514.00  -0.166451   0.278072  -0.1019672   -0.165000    0.279100   -0.1025800
+47515.00  -0.166364   0.281028  -0.1032387   -0.165000    0.282000   -0.1038800
+47516.00  -0.166345   0.284038  -0.1042255   -0.165000    0.284900   -0.1049000
+47517.00  -0.166322   0.287090  -0.1049930   -0.164800    0.287700   -0.1057000
+47518.00  -0.166208   0.290178  -0.1056444   -0.164600    0.290600   -0.1063800
+47519.00  -0.165936   0.293325  -0.1062911   -0.164300    0.293500   -0.1070400
+47520.00  -0.165480   0.296533  -0.1070274   -0.163900    0.296400   -0.1078000
+47521.00  -0.164879   0.299756  -0.1079153   -0.163400    0.299300   -0.1087200
+47522.00  -0.164185   0.302943  -0.1089782   -0.162800    0.302200   -0.1098000
+47523.00  -0.163448   0.306042  -0.1102095   -0.162200    0.305100   -0.1110300
+47524.00  -0.162718   0.309000  -0.1115825   -0.161500    0.308000   -0.1123800
+47525.00  -0.162014   0.311771  -0.1130535   -0.160700    0.310800   -0.1138000
+47526.00  -0.161329   0.314379  -0.1145574   -0.159900    0.313700   -0.1152300
+47527.00  -0.160623   0.316868  -0.1160242   -0.159100    0.316500   -0.1166000
+47528.00  -0.159834   0.319279  -0.1173940   -0.158200    0.319200   -0.1178600
+47529.00  -0.158899   0.321651  -0.1186247   -0.157300    0.322000   -0.1189700
+47530.00  -0.157777   0.324021  -0.1196954   -0.156100    0.324500   -0.1200400
+47531.00  -0.156514   0.326406  -0.1206076   -0.154900    0.327000   -0.1209400
+47532.00  -0.155183   0.328817  -0.1213994   -0.153700    0.329500   -0.1217400
+47533.00  -0.153835   0.331261  -0.1221507   -0.152500    0.332000   -0.1225100
+47534.00  -0.152507   0.333745  -0.1229689   -0.151300    0.334600   -0.1233600
+47535.00  -0.151241   0.336273  -0.1239605   -0.150200    0.337300   -0.1243300
+47536.00  -0.150107   0.338829  -0.1252112   -0.149300    0.340000   -0.1255800
+47537.00  -0.149139   0.341389  -0.1267405   -0.148400    0.342700   -0.1271300
+47538.00  -0.148319   0.343925  -0.1284866   -0.147600    0.345300   -0.1288800
+47539.00  -0.147620   0.346411  -0.1303312   -0.146800    0.347900   -0.1307300
+47540.00  -0.146987   0.348832  -0.1321353   -0.146000    0.350300   -0.1325300
+47541.00  -0.146334   0.351191  -0.1337636   -0.145100    0.352700   -0.1341600
+47542.00  -0.145597   0.353501  -0.1351367   -0.144200    0.355100   -0.1355200
+47543.00  -0.144734   0.355784  -0.1362438   -0.143300    0.357400   -0.1366300
+47544.00  -0.143704   0.358063  -0.1371317   -0.142300    0.359600   -0.1375100
+47545.00  -0.142476   0.360360  -0.1378875   -0.141100    0.361800   -0.1382600
+47546.00  -0.141091   0.362676  -0.1386235   -0.140000    0.364000   -0.1389800
+47547.00  -0.139636   0.364995  -0.1394478   -0.138700    0.366200   -0.1397900
+47548.00  -0.138188   0.367291  -0.1404436   -0.137500    0.368300   -0.1407900
+47549.00  -0.136813   0.369529  -0.1416598   -0.136300    0.370400   -0.1419900
+47550.00  -0.135556   0.371683  -0.1431067   -0.135100    0.372400   -0.1435100
+47551.00  -0.134376   0.373769  -0.1447478   -0.134000    0.374400   -0.1452100
+47552.00  -0.133268   0.375796  -0.1465163   -0.132900    0.376400   -0.1470300
+47553.00  -0.132239   0.377769  -0.1483335   -0.131800    0.378400   -0.1488800
+47554.00  -0.131292   0.379698  -0.1501200   -0.130800    0.380300   -0.1507100
+47555.00  -0.130378   0.381620  -0.1518051   -0.129800    0.382400   -0.1523400
+47556.00  -0.129424   0.383583  -0.1533415   -0.128800    0.384500   -0.1538200
+47557.00  -0.128349   0.385633  -0.1547032   -0.127700    0.386700   -0.1551400
+47558.00  -0.127087   0.387818  -0.1558913   -0.126500    0.388900   -0.1562700
+47559.00  -0.125585   0.390188  -0.1569400   -0.125100    0.391200   -0.1572800
+47560.00  -0.123832   0.392773  -0.1579212   -0.123500    0.393600   -0.1583500
+47561.00  -0.121911   0.395525  -0.1589440   -0.121600    0.396200   -0.1593900
+47562.00  -0.119903   0.398354  -0.1601397   -0.119600    0.398900   -0.1606200
+47563.00  -0.117871   0.401156  -0.1616185   -0.117500    0.401600   -0.1621400
+47564.00  -0.115849   0.403803  -0.1634295   -0.115300    0.404100   -0.1639800
+47565.00  -0.113860   0.406177  -0.1655334   -0.113200    0.406500   -0.1661000
+47566.00  -0.111876   0.408262  -0.1678033   -0.111000    0.408700   -0.1683600
+47567.00  -0.109852   0.410131  -0.1700525   -0.108800    0.410800   -0.1705800
+47568.00  -0.107744   0.411869  -0.1721051   -0.106600    0.412700   -0.1726000
+47569.00  -0.105503   0.413566  -0.1738461   -0.104300    0.414600   -0.1743200
+47570.00  -0.103089   0.415308  -0.1752415   -0.101800    0.416200   -0.1757100
+47571.00  -0.100531   0.417146  -0.1763413   -0.099200    0.417700   -0.1768200
+47572.00  -0.097904   0.419098  -0.1772436   -0.096700    0.419300   -0.1777600
+47573.00  -0.095312   0.421131  -0.1780595   -0.094100    0.420900   -0.1786100
+47574.00  -0.092857   0.423210  -0.1788889   -0.091600    0.422600   -0.1794900
+47575.00  -0.090612   0.425283  -0.1798109   -0.089600    0.425000   -0.1803500
+47576.00  -0.088536   0.427269  -0.1808903   -0.087600    0.427400   -0.1813500
+47577.00  -0.086538   0.429119  -0.1821532   -0.085500    0.429900   -0.1825200
+47578.00  -0.084432   0.430932  -0.1835922   -0.083400    0.432500   -0.1838500
+47579.00  -0.082059   0.432797  -0.1851673   -0.081100    0.435000   -0.1852900
+47580.00  -0.079410   0.434757  -0.1868075   -0.078000    0.436900   -0.1870700
+47581.00  -0.076519   0.436825  -0.1884434   -0.074800    0.438700   -0.1888600
+47582.00  -0.073472   0.438984  -0.1900135   -0.071500    0.440400   -0.1905900
+47583.00  -0.070384   0.441200  -0.1914703   -0.068200    0.442200   -0.1922000
+47584.00  -0.067355   0.443424  -0.1927852   -0.065000    0.444000   -0.1936400
+47585.00  -0.064462   0.445590  -0.1939514   -0.062400    0.446200   -0.1946700
+47586.00  -0.061777   0.447654  -0.1949889   -0.060000    0.448300   -0.1955700
+47587.00  -0.059322   0.449590  -0.1959522   -0.057700    0.450300   -0.1963900
+47588.00  -0.057032   0.451392  -0.1969334   -0.055600    0.452100   -0.1972600
+47589.00  -0.054827   0.453056  -0.1980522   -0.053500    0.453900   -0.1983000
+47590.00  -0.052640   0.454584  -0.1994315   -0.051500    0.455300   -0.1997600
+47591.00  -0.050464   0.455997  -0.2011648   -0.049300    0.456600   -0.2015700
+47592.00  -0.048237   0.457318  -0.2032656   -0.047100    0.457700   -0.2037500
+47593.00  -0.045879   0.458566  -0.2056514   -0.044800    0.458800   -0.2061700
+47594.00  -0.043333   0.459762  -0.2081660   -0.042400    0.459900   -0.2087000
+47595.00  -0.040559   0.460928  -0.2106262   -0.039700    0.461200   -0.2111500
+47596.00  -0.037601   0.462108  -0.2128614   -0.037000    0.462600   -0.2133400
+47597.00  -0.034537   0.463328  -0.2147751   -0.034100    0.464000   -0.2151900
+47598.00  -0.031444   0.464609  -0.2163651   -0.031100    0.465500   -0.2167300
+47599.00  -0.028399   0.465972  -0.2176977   -0.028100    0.467000   -0.2180500
+47600.00  -0.025444   0.467418  -0.2188749   -0.025000    0.468400   -0.2192200
+47601.00  -0.022537   0.468923  -0.2200257   -0.021800    0.469800   -0.2204100
+47602.00  -0.019637   0.470447  -0.2212566   -0.018700    0.471100   -0.2217300
+47603.00  -0.016728   0.471924  -0.2226366   -0.015600    0.472300   -0.2232000
+47604.00  -0.013793   0.473291  -0.2242018   -0.012500    0.473500   -0.2248600
+47605.00  -0.010811   0.474489  -0.2259581   -0.009500    0.474800   -0.2265900
+47606.00  -0.007772   0.475523  -0.2278645   -0.006400    0.475900   -0.2284400
+47607.00  -0.004680   0.476423  -0.2298524   -0.003400    0.476900   -0.2303600
+47608.00  -0.001536   0.477221  -0.2318442   -0.000400    0.477800   -0.2322500
+47609.00   0.001662   0.477931  -0.2337629    0.002600    0.478600   -0.2340800
+47610.00   0.004910   0.478562  -0.2355462    0.005700    0.479300   -0.2359200
+47611.00   0.008163   0.479117  -0.2371585    0.008700    0.479800   -0.2375800
+47612.00   0.011358   0.479605  -0.2385892    0.011700    0.480300   -0.2390200
+47613.00   0.014456   0.480025  -0.2398493    0.014800    0.480700   -0.2402700
+47614.00   0.017512   0.480344  -0.2409700    0.017900    0.481100   -0.2413800
+47615.00   0.020572   0.480580  -0.2420101    0.021100    0.481400   -0.2424700
+47616.00   0.023676   0.480793  -0.2430699    0.024400    0.481700   -0.2436200
+47617.00   0.026828   0.481018  -0.2442733    0.027700    0.481900   -0.2449400
+47618.00   0.030002   0.481275  -0.2457298    0.031100    0.482200   -0.2465400
+47619.00   0.033252   0.481540  -0.2475001    0.034500    0.482400   -0.2484400
+47620.00   0.036645   0.481775  -0.2495596    0.037900    0.482600   -0.2503600
+47621.00   0.040156   0.481968  -0.2517880    0.041300    0.482700   -0.2524200
+47622.00   0.043695   0.482123  -0.2540059    0.044800    0.482900   -0.2544400
+47623.00   0.047168   0.482245  -0.2560372    0.048200    0.483000   -0.2563000
+47624.00   0.050497   0.482334  -0.2577692    0.051600    0.483100   -0.2578500
+47625.00   0.053617   0.482389  -0.2591858    0.054700    0.483200   -0.2594100
+47626.00   0.056542   0.482418  -0.2603588    0.057800    0.483200   -0.2607300
+47627.00   0.059357   0.482421  -0.2614125    0.060900    0.483200   -0.2619200
+47628.00   0.062146   0.482398  -0.2624770    0.063900    0.483200   -0.2630900
+47629.00   0.064996   0.482348  -0.2636536    0.066900    0.483200   -0.2643500
+47630.00   0.067974   0.482275  -0.2650022    0.069900    0.483100   -0.2656000
+47631.00   0.071056   0.482182  -0.2665444    0.072900    0.483000   -0.2670200
+47632.00   0.074175   0.482084  -0.2682722    0.075800    0.482900   -0.2686400
+47633.00   0.077261   0.481993  -0.2701558    0.078700    0.482800   -0.2704300
+47634.00   0.080242   0.481921  -0.2721509    0.081400    0.482800   -0.2723800
+47635.00   0.083051   0.481881  -0.2742048    0.084000    0.482900   -0.2745500
+47636.00   0.085654   0.481862  -0.2762618    0.086500    0.483000   -0.2767500
+47637.00   0.088070   0.481819  -0.2782747    0.088800    0.483100   -0.2789200
+47638.00   0.090333   0.481716  -0.2802104    0.091000    0.483200   -0.2809800
+47639.00   0.092486   0.481533  -0.2820442    0.093100    0.483100   -0.2829000
+47640.00   0.094494   0.481236  -0.2837585    0.095200    0.482700   -0.2844400
+47641.00   0.096371   0.480834  -0.2853637    0.097200    0.482100   -0.2858300
+47642.00   0.098163   0.480347  -0.2869023    0.099200    0.481400   -0.2871600
+47643.00   0.099938   0.479781  -0.2884530    0.101100    0.480700   -0.2885100
+47644.00   0.101770   0.479145  -0.2901074    0.103100    0.479800   -0.2899900
+47645.00   0.103681   0.478475  -0.2919493    0.104900    0.479400   -0.2919500
+47646.00   0.105679   0.477783  -0.2940401    0.106900    0.478800   -0.2942000
+47647.00   0.107778   0.477063  -0.2963841    0.109000    0.478300   -0.2966900
+47648.00   0.110015   0.476300  -0.2989080    0.111300    0.477700   -0.2993400
+47649.00   0.112428   0.475470  -0.3014717    0.113600    0.477000   -0.3020200
+47650.00   0.115060   0.474545  -0.3039073    0.116100    0.476000   -0.3044400
+47651.00   0.117874   0.473559  -0.3060707    0.118800    0.474900   -0.3065900
+47652.00   0.120803   0.472567  -0.3078923    0.121600    0.473800   -0.3084100
+47653.00   0.123774   0.471615  -0.3093978    0.124400    0.472800   -0.3099400
+47654.00   0.126711   0.470731  -0.3106890    0.127300    0.471800   -0.3112700
+47655.00   0.129543   0.469925  -0.3119010    0.130200    0.470900   -0.3124200
+47656.00   0.132250   0.469156  -0.3131560    0.133100    0.470000   -0.3136000
+47657.00   0.134873   0.468363  -0.3145339    0.135900    0.469100   -0.3148900
+47658.00   0.137457   0.467478  -0.3160676    0.138700    0.468100   -0.3163400
+47659.00   0.140051   0.466439  -0.3177515    0.141500    0.467000   -0.3179100
+47660.00   0.142692   0.465202  -0.3195541    0.144100    0.465800   -0.3197900
+47661.00   0.145352   0.463771  -0.3214257    0.146600    0.464600   -0.3217400
+47662.00   0.147974   0.462165  -0.3233090    0.149000    0.463200   -0.3236900
+47663.00   0.150536   0.460428  -0.3251465    0.151400    0.461700   -0.3255900
+47664.00   0.153032   0.458624  -0.3268908    0.153700    0.460100   -0.3274000
+47665.00   0.155458   0.456813  -0.3285086    0.156000    0.458200   -0.3290600
+47666.00   0.157792   0.455046  -0.3299836    0.158100    0.456200   -0.3305900
+47667.00   0.159992   0.453368  -0.3313262    0.160200    0.454200   -0.3320200
+47668.00   0.161991   0.451794  -0.3325726    0.162200    0.452200   -0.3333900
+47669.00   0.163797   0.450258  -0.3337839    0.164100    0.450300   -0.3347300
+47670.00   0.165449   0.448686  -0.3350409    0.166000    0.449200   -0.3357500
+47671.00   0.167014   0.447059  -0.3364270    0.167900    0.448100   -0.3368900
+47672.00   0.168630   0.445410  -0.3380170    0.169700    0.447100   -0.3382400
+47673.00   0.170353   0.443761  -0.3398651    0.171500    0.446000   -0.3398400
+47674.00   0.172213   0.442138  -0.3419779    0.173300    0.444800   -0.3417200
+47675.00   0.174223   0.440579  -0.3443007    0.175100    0.442700   -0.3443400
+47676.00   0.176326   0.439081  -0.3467165    0.176900    0.440500   -0.3470700
+47677.00   0.178441   0.437618  -0.3490693    0.178800    0.438300   -0.3496900
+47678.00   0.180503   0.436141  -0.3512058    0.180600    0.436100   -0.3520600
+47679.00   0.182458   0.434592  -0.3530186    0.182600    0.434100   -0.3540600
+47680.00   0.184277   0.432933  -0.3544779    0.184700    0.432400   -0.3555200
+47681.00   0.185968   0.431184  -0.3556373    0.186700    0.431000   -0.3567100
+47682.00   0.187552   0.429384  -0.3566092    0.188800    0.429700   -0.3577100
+47683.00   0.189112   0.427581  -0.3575300    0.190900    0.428500   -0.3586700
+47684.00   0.190740   0.425782  -0.3585215    0.192900    0.427200   -0.3597100
+47685.00   0.192506   0.423983  -0.3596579    0.195000    0.425500   -0.3608800
+47686.00   0.194400   0.422163  -0.3609551    0.197000    0.423800   -0.3621600
+47687.00   0.196393   0.420297  -0.3623900    0.199000    0.421900   -0.3635400
+47688.00   0.198458   0.418355  -0.3639148    0.201000    0.420000   -0.3649900
+47689.00   0.200570   0.416317  -0.3654695    0.202900    0.417900   -0.3664500
+47690.00   0.202666   0.414182  -0.3669805    0.204700    0.415500   -0.3678700
+47691.00   0.204677   0.411958  -0.3683846    0.206400    0.413100   -0.3691700
+47692.00   0.206570   0.409656  -0.3696329    0.208100    0.410600   -0.3703100
+47693.00   0.208381   0.407288  -0.3706947    0.209700    0.408000   -0.3712700
+47694.00   0.210155   0.404868  -0.3715637    0.211400    0.405400   -0.3720900
+47695.00   0.211929   0.402408  -0.3722632    0.213100    0.402800   -0.3727600
+47696.00   0.213726   0.399916  -0.3728549    0.214900    0.400300   -0.3733200
+47697.00   0.215569   0.397397  -0.3734306    0.216700    0.397700   -0.3738500
+47698.00   0.217478   0.394856  -0.3740968    0.218500    0.395100   -0.3744500
+47699.00   0.219460   0.392288  -0.3749524    0.220400    0.392400   -0.3752200
+47700.00   0.221490   0.389688  -0.3760604    0.222400    0.389800   -0.3764300
+47701.00   0.223484   0.387050  -0.3774185    0.224500    0.387200   -0.3778900
+47702.00   0.225443   0.384378  -0.3789637    0.226600    0.384600   -0.3795300
+47703.00   0.227389   0.381680  -0.3805834    0.228800    0.382000   -0.3812500
+47704.00   0.229349   0.378961  -0.3821356    0.230900    0.379300   -0.3829100
+47705.00   0.231358   0.376225  -0.3834821    0.232900    0.376700   -0.3841500
+47706.00   0.233388   0.373459  -0.3845358    0.234800    0.374000   -0.3851100
+47707.00   0.235346   0.370661  -0.3852727    0.236600    0.371300   -0.3857600
+47708.00   0.237174   0.367822  -0.3857380    0.238400    0.368600   -0.3861700
+47709.00   0.238840   0.364929  -0.3860344    0.240100    0.365800   -0.3864300
+47710.00   0.240365   0.361986  -0.3862903    0.241800    0.363000   -0.3867200
+47711.00   0.241816   0.359017  -0.3866311    0.243400    0.359800   -0.3870900
+47712.00   0.243232   0.356043  -0.3871441    0.244900    0.356500   -0.3876300
+47713.00   0.244637   0.353083  -0.3878642    0.246400    0.353200   -0.3883600
+47714.00   0.246029   0.350152  -0.3887809    0.247800    0.350000   -0.3892600
+47715.00   0.247390   0.347247  -0.3898499    0.249100    0.347000   -0.3903000
+47716.00   0.248711   0.344346  -0.3910046    0.250400    0.344100   -0.3914000
+47717.00   0.249981   0.341423  -0.3921652    0.251500    0.341300   -0.3925200
+47718.00   0.251214   0.338475  -0.3932617    0.252600    0.338500   -0.3935800
+47719.00   0.252378   0.335505  -0.3942564    0.253600    0.335700   -0.3945600
+47720.00   0.253441   0.332538  -0.3951311    0.254500    0.333000   -0.3954500
+47721.00   0.254448   0.329617  -0.3958903    0.255400    0.330200   -0.3962100
+47722.00   0.255471   0.326783  -0.3965658    0.256100    0.327500   -0.3968500
+47723.00   0.256523   0.324049  -0.3972030    0.256900    0.324700   -0.3975300
+47724.00   0.257510   0.321393  -0.3978523    0.257500    0.322000   -0.3980000
+47725.00   0.258280   0.318751  -0.3985792    0.258000    0.319100   -0.3986800
+47726.00   0.258834   0.316059  -0.3994760    0.258500    0.316200   -0.3995800
+47727.00   0.259242   0.313273  -0.4006205    0.258900    0.313200   -0.4007400
+47728.00   0.259520   0.310377  -0.4020440    0.259300    0.310100   -0.4022100
+47729.00   0.259677   0.307373  -0.4037092    0.259700    0.307100   -0.4039200
+47730.00   0.259721   0.304260  -0.4055103    0.260100    0.304000   -0.4057500
+47731.00   0.259656   0.301045  -0.4072883    0.260500    0.300900   -0.4075500
+47732.00   0.259516   0.297755  -0.4088916    0.260800    0.297800   -0.4092000
+47733.00   0.259411   0.294397  -0.4102297    0.261100    0.294700   -0.4105700
+47734.00   0.259400   0.290980  -0.4112703    0.261200    0.291500   -0.4116300
+47735.00   0.259475   0.287550  -0.4120367    0.261100    0.288400   -0.4122900
+47736.00   0.259578   0.284153  -0.4126159    0.260900    0.285300   -0.4127600
+47737.00   0.259598   0.280826  -0.4131170    0.260600    0.282100   -0.4131300
+47738.00   0.259486   0.277618  -0.4136479    0.260300    0.279000   -0.4135600
+47739.00   0.259283   0.274517  -0.4142997    0.259900    0.275800   -0.4141200
+47740.00   0.259062   0.271488  -0.4151187    0.259600    0.272500   -0.4149900
+47741.00   0.258932   0.268482  -0.4161049    0.259400    0.269200   -0.4160500
+47742.00   0.258981   0.265471  -0.4172229    0.259300    0.266000   -0.4172400
+47743.00   0.259177   0.262452  -0.4184194    0.259200    0.262800   -0.4185100
+47744.00   0.259382   0.259419  -0.4196321    0.259200    0.259600   -0.4197900
+47745.00   0.259502   0.256372  -0.4208062    0.259300    0.256400   -0.4210000
+47746.00   0.259528   0.253308  -0.4218918    0.259400    0.253200   -0.4220600
+47747.00   0.259514   0.250247  -0.4228527    0.259400    0.250000   -0.4229700
+47748.00   0.259562   0.247205  -0.4236824    0.259500    0.246800   -0.4237400
+47749.00   0.259708   0.244204  -0.4243916    0.259500    0.243700   -0.4243800
+47750.00   0.259841   0.241279  -0.4250039    0.259400    0.240800   -0.4250300
+47751.00   0.259861   0.238419  -0.4255850    0.259300    0.237900   -0.4256300
+47752.00   0.259701   0.235585  -0.4262314    0.259000    0.235100   -0.4263200
+47753.00   0.259331   0.232721  -0.4270534    0.258700    0.232200   -0.4271700
+47754.00   0.258722   0.229769  -0.4281495    0.258400    0.229400   -0.4283300
+47755.00   0.257874   0.226673  -0.4295740    0.257800    0.226300   -0.4297400
+47756.00   0.256866   0.223437  -0.4313153    0.257200    0.223000   -0.4314600
+47757.00   0.255786   0.220100  -0.4332828    0.256500    0.219700   -0.4333800
+47758.00   0.254697   0.216697  -0.4353113    0.255700    0.216300   -0.4353500
+47759.00   0.253687   0.213255  -0.4372182    0.255000    0.213000   -0.4372000
+47760.00   0.252831   0.209803  -0.4388607    0.254100    0.209600   -0.4389300
+47761.00   0.252154   0.206380  -0.4401749    0.253200    0.206300   -0.4403400
+47762.00   0.251620   0.203035  -0.4411822    0.252400    0.203000   -0.4414600
+47763.00   0.251154   0.199820  -0.4419658    0.251500    0.199900   -0.4423900
+47764.00   0.250680   0.196789  -0.4426360    0.250600    0.196800   -0.4431800
+47765.00   0.250131   0.193995  -0.4433011    0.250000    0.193900   -0.4436700
+47766.00   0.249428   0.191364  -0.4440612    0.249300    0.191100   -0.4442400
+47767.00   0.248493   0.188770  -0.4449861    0.248500    0.188300   -0.4449600
+47768.00   0.247289   0.186106  -0.4461029    0.247600    0.185500   -0.4458700
+47769.00   0.245810   0.183287  -0.4473984    0.246500    0.182700   -0.4469500
+47770.00   0.244066   0.180279  -0.4488220    0.245000    0.179700   -0.4486000
+47771.00   0.242130   0.177130  -0.4502998    0.243300    0.176800   -0.4503200
+47772.00   0.240098   0.173910  -0.4517486    0.241400    0.173700   -0.4520100
+47773.00   0.238027   0.170686  -0.4530928    0.239400    0.170700   -0.4536000
+47774.00   0.235922   0.167512  -0.4542755    0.237200    0.167600   -0.4550100
+47775.00   0.233773   0.164445  -0.4552687    0.234900    0.164200   -0.4556800
+47776.00   0.231549   0.161528  -0.4560882    0.232600    0.161100   -0.4563700
+47777.00   0.229223   0.158754  -0.4567806    0.230100    0.158100   -0.4569300
+47778.00   0.226787   0.156103  -0.4574131    0.227600    0.155200   -0.4574300
+47779.00   0.224295   0.153554  -0.4580653    0.225000    0.152400   -0.4579600
+47780.00   0.221831   0.151091  -0.4588307    0.222500    0.149900   -0.4587600
+47781.00   0.219415   0.148691  -0.4598036    0.220000    0.147500   -0.4597900
+47782.00   0.217025   0.146348  -0.4610755    0.217400    0.145200   -0.4611000
+47783.00   0.214648   0.144057  -0.4626902    0.214900    0.142900   -0.4627700
+47784.00   0.212275   0.141805  -0.4646152    0.212400    0.140700   -0.4647300
+47785.00   0.209901   0.139576  -0.4667389    0.210100    0.138600   -0.4669400
+47786.00   0.207490   0.137349  -0.4688831    0.207900    0.136500   -0.4691500
+47787.00   0.205003   0.135107  -0.4708694    0.205500    0.134400   -0.4711900
+47788.00   0.202444   0.132834  -0.4725772    0.203100    0.132300   -0.4729500
+47789.00   0.199818   0.130546  -0.4739759    0.200600    0.130100   -0.4744000
+47790.00   0.197123   0.128280  -0.4751244    0.197800    0.127600   -0.4754200
+47791.00   0.194319   0.126036  -0.4761386    0.194800    0.125000   -0.4763200
+47792.00   0.191399   0.123797  -0.4771475    0.191800    0.122400   -0.4772200
+47793.00   0.188364   0.121549  -0.4782554    0.188700    0.119800   -0.4782300
+47794.00   0.185238   0.119268  -0.4795257    0.185500    0.117200   -0.4794200
+47795.00   0.182078   0.116925  -0.4809871    0.182400    0.115100   -0.4809700
+47796.00   0.178927   0.114535  -0.4826303    0.179300    0.113000   -0.4827000
+47797.00   0.175801   0.112143  -0.4844125    0.176100    0.111000   -0.4845500
+47798.00   0.172692   0.109843  -0.4862662    0.172900    0.109100   -0.4864500
+47799.00   0.169578   0.107752  -0.4881118    0.169700    0.107400   -0.4883400
+47800.00   0.166400   0.105964  -0.4898709    0.166600    0.105800   -0.4901000
+47801.00   0.163140   0.104437  -0.4914920    0.163500    0.104400   -0.4917400
+47802.00   0.159817   0.103076  -0.4929552    0.160300    0.103000   -0.4932500
+47803.00   0.156445   0.101784  -0.4942671    0.157100    0.101700   -0.4946200
+47804.00   0.153030   0.100460  -0.4954603    0.153800    0.100400   -0.4958600
+47805.00   0.149568   0.099020  -0.4965907    0.150400    0.099000   -0.4968500
+47806.00   0.146082   0.097493  -0.4977285    0.146900    0.097700   -0.4978400
+47807.00   0.142608   0.095962  -0.4989574    0.143300    0.096400   -0.4988900
+47808.00   0.139183   0.094510  -0.5003659    0.139600    0.095100   -0.5001200
+47809.00   0.135807   0.093205  -0.5020325    0.135800    0.093800   -0.5016400
+47810.00   0.132325   0.092049  -0.5040085    0.131900    0.092500   -0.5037300
+47811.00   0.128573   0.090993  -0.5062987    0.127900    0.091200   -0.5062100
+47812.00   0.124506   0.089968  -0.5088255    0.123800    0.090000   -0.5089400
+47813.00   0.120219   0.088938  -0.5114430    0.119600    0.088700   -0.5117600
+47814.00   0.115844   0.087877  -0.5139777    0.115400    0.087500   -0.5145000
+47815.00   0.111554   0.086777  -0.5162918    0.111300    0.086400   -0.5167800
+47816.00   0.107593   0.085697  -0.5183447    0.107300    0.085300   -0.5187500
+47817.00   0.103907   0.084674  -0.5201702    0.103300    0.084300   -0.5204900
+47818.00   0.100289   0.083678  -0.5218716    0.099300    0.083200   -0.5220800
+47819.00   0.096530   0.082687  -0.5235770    0.095500    0.082100   -0.5236700
+47820.00   0.092643   0.081623  -0.5253861    0.091800    0.081100   -0.5255000
+47821.00   0.088758   0.080515  -0.5273585    0.088200    0.080000   -0.5275000
+47822.00   0.084999   0.079469  -0.5295096    0.084600    0.079100   -0.5296700
+47823.00   0.081461   0.078569  -0.5318132    0.081100    0.078200   -0.5319700
+47824.00   0.078112   0.077817  -0.5342076    0.077600    0.077400   -0.5343600
+47825.00   0.074806   0.077207  -0.5366070    0.074300    0.076900   -0.5368100
+47826.00   0.071438   0.076761  -0.5389626    0.071000    0.076500   -0.5392300
+47827.00   0.068006   0.076490  -0.5412601    0.067700    0.076300   -0.5415600
+47828.00   0.064380   0.076301  -0.5434609    0.064400    0.076100   -0.5437800
+47829.00   0.060604   0.076052  -0.5455241    0.061100    0.076000   -0.5458500
+47830.00   0.056867   0.075662  -0.5474344    0.057500    0.075700   -0.5476900
+47831.00   0.053306   0.075251  -0.5492010    0.053800    0.075500   -0.5493800
+47832.00   0.049948   0.074957  -0.5508516    0.050100    0.075200   -0.5510000
+47833.00   0.046609   0.074778  -0.5524322    0.046300    0.074900   -0.5525700
+47834.00   0.043062   0.074689  -0.5540111    0.042500    0.074600   -0.5541900
+47835.00   0.039172   0.074657  -0.5556779    0.039800    0.074600   -0.5558600
+47836.00   0.034976   0.074651  -0.5575302    0.035900    0.074300   -0.5577500
+47837.00   0.030594   0.074651  -0.5596418    0.031900    0.074100   -0.5599000
+47838.00   0.026170   0.074658  -0.5620377    0.027900    0.074000   -0.5623200
+47839.00   0.021873   0.074685  -0.5646849    0.023800    0.074000   -0.5649700
+47840.00   0.017774   0.074750  -0.5674802    0.019800    0.074100   -0.5676900
+47841.00   0.013797   0.074867  -0.5702598    0.015700    0.074400   -0.5703800
+47842.00   0.009853   0.075052  -0.5728625    0.011800    0.074600   -0.5728800
+47843.00   0.005935   0.075329  -0.5751842    0.007900    0.075000   -0.5751000
+47844.00   0.002118   0.075723  -0.5772119    0.004000    0.075500   -0.5770200
+47845.00  -0.001536   0.076257  -0.5790178    0.000400    0.076000   -0.5789500
+47846.00  -0.005052   0.076920  -0.5807332   -0.003100    0.076600   -0.5807900
+47847.00  -0.008515   0.077690  -0.5824945   -0.006600    0.077300   -0.5826900
+47848.00  -0.011982   0.078552  -0.5844024   -0.010200    0.078100   -0.5847500
+47849.00  -0.015443   0.079520  -0.5865052   -0.013700    0.079000   -0.5870000
+47850.00  -0.018894   0.080613  -0.5887973   -0.017400    0.080300   -0.5891500
+47851.00  -0.022304   0.081830  -0.5912393   -0.021200    0.081600   -0.5914300
+47852.00  -0.025738   0.083181  -0.5937721   -0.025100    0.083100   -0.5938300
+47853.00  -0.029284   0.084672  -0.5963324   -0.028900    0.084500   -0.5962600
+47854.00  -0.032975   0.086275  -0.5988612   -0.032900    0.086100   -0.5986900
+47855.00  -0.036766   0.087923  -0.6013089   -0.036700    0.087500   -0.6012100
+47856.00  -0.040644   0.089531  -0.6036224   -0.040400    0.088800   -0.6036100
+47857.00  -0.044621   0.091021  -0.6057624   -0.044100    0.090200   -0.6058300
+47858.00  -0.048622   0.092360  -0.6077381   -0.047700    0.091400   -0.6078900
+47859.00  -0.052504   0.093533  -0.6095892   -0.051200    0.092700   -0.6098000
+47860.00  -0.056142   0.094538  -0.6113724   -0.054600    0.093900   -0.6115700
+47861.00  -0.059499   0.095445  -0.6131492   -0.057800    0.095100   -0.6133100
+47862.00  -0.062559   0.096349  -0.6149856   -0.060800    0.096300   -0.6150900
+47863.00  -0.065327   0.097344  -0.6169418   -0.063700    0.097700   -0.6169800
+47864.00  -0.067830   0.098522  -0.6190600   -0.066400    0.099100   -0.6190500
+47865.00  -0.070117   0.099965  -0.6213521   -0.069000    0.100400   -0.6214400
+47866.00  -0.072305   0.101669  -0.6238043   -0.071500    0.102000   -0.6238700
+47867.00  -0.074486   0.103589  -0.6263590   -0.074000    0.103700   -0.6264000
+47868.00  -0.076739   0.105674  -0.6289126   -0.076500    0.105600   -0.6289500
+47869.00  -0.079122   0.107881  -0.6313409   -0.078900    0.107700   -0.6313800
+47870.00  -0.081630   0.110183  -0.6335356   -0.081300    0.109900   -0.6335700
+47871.00  -0.084241   0.112555  -0.6354462   -0.083700    0.112300   -0.6354900
+47872.00  -0.086932   0.114974  -0.6371037   -0.086200    0.114800   -0.6371500
+47873.00  -0.089614   0.117435  -0.6386064   -0.088800    0.117400   -0.6386400
+47874.00  -0.092151   0.119956  -0.6400823   -0.091100    0.120000   -0.6401300
+47875.00  -0.094464   0.122557  -0.6416399   -0.093500    0.122400   -0.6416800
+47876.00  -0.096596   0.125204  -0.6433545   -0.095800    0.125200   -0.6434200
+47877.00  -0.098647   0.127845  -0.6452473   -0.098100    0.127800   -0.6453100
+47878.00  -0.100700   0.130424  -0.6472936   -0.100200    0.130300   -0.6473600
+47879.00  -0.102791   0.132890  -0.6494431   -0.102300    0.132800   -0.6495200
+47880.00  -0.104932   0.135228  -0.6516350   -0.104300    0.135100   -0.6517000
+47881.00  -0.107038   0.137530  -0.6537996   -0.106300    0.137500   -0.6538500
+47882.00  -0.109166   0.139802  -0.6558744   -0.108600    0.139700   -0.6559100
+47883.00  -0.111425   0.142033  -0.6578097   -0.110900    0.141800   -0.6578200
+47884.00  -0.113908   0.144250  -0.6595674   -0.113200    0.144100   -0.6595900
+47885.00  -0.116617   0.146490  -0.6611365   -0.115800    0.146300   -0.6611500
+47886.00  -0.119446   0.148785  -0.6625434   -0.118400    0.148800   -0.6625900
+47887.00  -0.122271   0.151162  -0.6638408   -0.120900    0.151100   -0.6639200
+47888.00  -0.124968   0.153651  -0.6650994   -0.123500    0.153800   -0.6651700
+47889.00  -0.127413   0.156281  -0.6663989   -0.126200    0.156700   -0.6664800
+47890.00  -0.129524   0.159074  -0.6678172   -0.128600    0.159700   -0.6679000
+47891.00  -0.131349   0.161999  -0.6694241   -0.130600    0.162500   -0.6695100
+47892.00  -0.132974   0.164998   0.3287404   -0.132500    0.165300    0.3286400
+47893.00  -0.134485   0.168017   0.3266860   -0.134100    0.168000    0.3265900
+47894.00  -0.135970   0.170997   0.3244717   -0.135500    0.170600    0.3243500
+47895.00  -0.137516   0.173884   0.3221932   -0.136800    0.173400    0.3221100
+47896.00  -0.139181   0.176642   0.3199771   -0.138200    0.176000    0.3199100
+47897.00  -0.140984   0.179271   0.3179432   -0.139900    0.178800    0.3178800
+47898.00  -0.142938   0.181783   0.3161659   -0.141800    0.181300    0.3160700
+47899.00  -0.145050   0.184190   0.3146584   -0.143800    0.183400    0.3146100
+47900.00  -0.147283   0.186517   0.3133695   -0.146100    0.185800    0.3133200
+47901.00  -0.149527   0.188891   0.3121848   -0.148400    0.188200    0.3121100
+47902.00  -0.151664   0.191448   0.3109700   -0.150600    0.191000    0.3108600
+47903.00  -0.153666   0.194203   0.3096123   -0.152500    0.193700    0.3095300
+47904.00  -0.155469   0.197091   0.3080493   -0.154400    0.196600    0.3079800
+47905.00  -0.157038   0.200038   0.3062812   -0.156200    0.199900    0.3062400
+47906.00  -0.158489   0.203022   0.3043732   -0.157600    0.203200    0.3043100
+47907.00  -0.159972   0.206036   0.3024133   -0.159500    0.206100    0.3023500
+47908.00  -0.161637   0.209075   0.3004836   -0.161100    0.208900    0.3004300
+47909.00  -0.163648   0.212119   0.2986485   -0.162600    0.211200    0.2985800
+47910.00  -0.166171   0.215135   0.2969497   -0.165400    0.214500    0.2968900
+47911.00  -0.169172   0.218109   0.2953954   -0.168400    0.217500    0.2952300
+47912.00  -0.172428   0.221071   0.2939803   -0.172300    0.220900    0.2936200
+47913.00  -0.175694   0.224056   0.2926783   -0.174900    0.223800    0.2925700
+47914.00  -0.178725   0.227102   0.2914388   -0.177600    0.227000    0.2915700
+47915.00  -0.181309   0.230256   0.2901924   -0.180000    0.230000    0.2903300
+47916.00  -0.183495   0.233556   0.2888525   -0.182500    0.233400    0.2889000
+47917.00  -0.185427   0.236998   0.2873251   -0.185100    0.236700    0.2873100
+47918.00  -0.187215   0.240537   0.2855331   -0.187400    0.239900    0.2855600
+47919.00  -0.188973   0.244118   0.2834373   -0.189600    0.243100    0.2835100
+47920.00  -0.190812   0.247664   0.2810477   -0.191700    0.246300    0.2811600
+47921.00  -0.192611   0.251151   0.2784173   -0.193400    0.249600    0.2785100
+47922.00  -0.194191   0.254600   0.2756436   -0.195100    0.253200    0.2757500
+47923.00  -0.195576   0.258070   0.2728727   -0.196100    0.256800    0.2729000
+47924.00  -0.196900   0.261580   0.2702385   -0.196400    0.260600    0.2701800
+47925.00  -0.198266   0.265115   0.2678273   -0.197300    0.264100    0.2678600
+47926.00  -0.199681   0.268675   0.2656487   -0.198600    0.268000    0.2657200
+47927.00  -0.201065   0.272274   0.2636380   -0.200000    0.271900    0.2637700
+47928.00  -0.202333   0.275932   0.2616887   -0.201400    0.275700    0.2617500
+47929.00  -0.203403   0.279667   0.2596837   -0.202800    0.279500    0.2597000
+47930.00  -0.204211   0.283483   0.2575309   -0.203900    0.283200    0.2574600
+47931.00  -0.204694   0.287359   0.2551758   -0.204500    0.287000    0.2550700
+47932.00  -0.204841   0.291287   0.2526216   -0.204300    0.290600    0.2525600
+47933.00  -0.204676   0.295263   0.2499181   -0.203800    0.294500    0.2499100
+47934.00  -0.204224   0.299284   0.2471377   -0.202800    0.298600    0.2471500
+47935.00  -0.203533   0.303354   0.2443607   -0.202200    0.302700    0.2443800
+47936.00  -0.202713   0.307486   0.2416612   -0.201500    0.306700    0.2416800
+47937.00  -0.201903   0.311642   0.2390961   -0.200900    0.310800    0.2390900
+47938.00  -0.201203   0.315725   0.2366996   -0.200500    0.314800    0.2367000
+47939.00  -0.200751   0.319639   0.2344775   -0.200500    0.318800    0.2344600
+47940.00  -0.200722   0.323310   0.2324294   -0.200300    0.322600    0.2324500
+47941.00  -0.201068   0.326771   0.2305399   -0.200400    0.326300    0.2306200
+47942.00  -0.201659   0.330102   0.2287872   -0.200600    0.329800    0.2289000
+47943.00  -0.202289   0.333378   0.2271173   -0.200900    0.333100    0.2272200
+47944.00  -0.202612   0.336678   0.2254160   -0.201300    0.336400    0.2254900
+47945.00  -0.202312   0.340074   0.2235642   -0.200400    0.339400    0.2236000
+47946.00  -0.201390   0.343597   0.2214781   -0.199300    0.342800    0.2214800
+47947.00  -0.199994   0.347250   0.2191223   -0.198100    0.346400    0.2191200
+47948.00  -0.198336   0.351023   0.2165307   -0.197000    0.350000    0.2165100
+47949.00  -0.196637   0.354907   0.2138054   -0.195800    0.353800    0.2138000
+47950.00  -0.195118   0.358888   0.2110894   -0.194700    0.357900    0.2111100
+47951.00  -0.193973   0.362910   0.2085379   -0.193800    0.361900    0.2085800
+47952.00  -0.193306   0.366897   0.2062554   -0.193100    0.365800    0.2063000
+47953.00  -0.192965   0.370776   0.2042577   -0.192500    0.369800    0.2043200
+47954.00  -0.192737   0.374532   0.2024849   -0.192000    0.373700    0.2025600
+47955.00  -0.192439   0.378169   0.2008309   -0.191800    0.377700    0.2009100
+47956.00  -0.192013   0.381747   0.1991734   -0.191000    0.381600    0.1992000
+47957.00  -0.191447   0.385333   0.1973997   -0.190400    0.385100    0.1974000
+47958.00  -0.190752   0.388961   0.1954329   -0.189800    0.388800    0.1954000
+47959.00  -0.189934   0.392658   0.1932407   -0.188800    0.392400    0.1932300
+47960.00  -0.188990   0.396433   0.1908357   -0.187800    0.395700    0.1908200
+47961.00  -0.187975   0.400222   0.1882909   -0.186900    0.399300    0.1882700
+47962.00  -0.186968   0.403938   0.1857013   -0.186100    0.402900    0.1856700
+47963.00  -0.186071   0.407506   0.1831593   -0.185500    0.406600    0.1831000
+47964.00  -0.185394   0.410852   0.1807412   -0.184900    0.410200    0.1806400
+47965.00  -0.184985   0.413929   0.1784922   -0.184800    0.413200    0.1783300
+47966.00  -0.184660   0.416814   0.1763986   -0.184200    0.416100    0.1762300
+47967.00  -0.184220   0.419631   0.1744241   -0.183700    0.419200    0.1743500
+47968.00  -0.183515   0.422474   0.1725242   -0.182800    0.422100    0.1724700
+47969.00  -0.182424   0.425419   0.1706383   -0.181800    0.424900    0.1706200
+47970.00  -0.180871   0.428535   0.1686948   -0.180100    0.428200    0.1687600
+47971.00  -0.178945   0.431863   0.1666247   -0.178400    0.431500    0.1667000
+47972.00  -0.176800   0.435421   0.1643610   -0.176300    0.434900    0.1644100
+47973.00  -0.174577   0.439208   0.1618486   -0.174200    0.438600    0.1618200
+47974.00  -0.172343   0.443146   0.1590562   -0.171900    0.442500    0.1590600
+47975.00  -0.170198   0.447045   0.1559934   -0.169500    0.446100    0.1559800
+47976.00  -0.168148   0.450794   0.1527522   -0.167100    0.449700    0.1527600
+47977.00  -0.166137   0.454332   0.1494856   -0.164900    0.453400    0.1494900
+47978.00  -0.164136   0.457615   0.1463661   -0.162600    0.456800    0.1463300
+47979.00  -0.162161   0.460647   0.1435361   -0.160400    0.460000    0.1435100
+47980.00  -0.160228   0.463506   0.1410660   -0.158300    0.463100    0.1410500
+47981.00  -0.158241   0.466349   0.1389047   -0.156200    0.466100    0.1388900
+47982.00  -0.156117   0.469229   0.1369332   -0.154000    0.469000    0.1369100
+47983.00  -0.153796   0.472157   0.1350106   -0.151900    0.471900    0.1350100
+47984.00  -0.151225   0.475109   0.1330082   -0.149800    0.474800    0.1329900
+47985.00  -0.148374   0.478055   0.1308397   -0.147600    0.477600    0.1308000
+47986.00  -0.145312   0.480999   0.1284925   -0.145300    0.480200    0.1284300
+47987.00  -0.142123   0.483957   0.1260024   -0.142500    0.483200    0.1259600
+47988.00  -0.138849   0.486959   0.1234139   -0.139400    0.486300    0.1233900
+47989.00  -0.135526   0.490037   0.1207829   -0.136300    0.489800    0.1207400
+47990.00  -0.132195   0.493212   0.1181738   -0.132700    0.493100    0.1181400
+47991.00  -0.128937   0.496456   0.1156518   -0.129200    0.496400    0.1156300
+47992.00  -0.125835   0.499714   0.1132670   -0.125500    0.499800    0.1132800
+47993.00  -0.122922   0.502925   0.1110453   -0.122400    0.502900    0.1110100
+47994.00  -0.120184   0.506020   0.1089861   -0.119500    0.506000    0.1089800
+47995.00  -0.117489   0.508907   0.1070738   -0.116800    0.508700    0.1070600
+47996.00  -0.114801   0.511621   0.1052702   -0.114200    0.511600    0.1052700
+47997.00  -0.112127   0.514204   0.1035097   -0.111600    0.514200    0.1035000
+47998.00  -0.109471   0.516685   0.1017159   -0.109200    0.516200    0.1016800
+47999.00  -0.106858   0.519103   0.0998076   -0.106600    0.518400    0.0998100
+48000.00  -0.104324   0.521523   0.0976962   -0.104100    0.520900    0.0977300
+48001.00  -0.101893   0.523999   0.0953261   -0.101700    0.523200    0.0953800
+48002.00  -0.099610   0.526519   0.0926861   -0.099500    0.525300    0.0927600
+48003.00  -0.097463   0.528988   0.0898268   -0.097200    0.527700    0.0899300
+48004.00  -0.095416   0.531306   0.0868628   -0.094900    0.530100    0.0868500
+48005.00  -0.093418   0.533400   0.0839541   -0.092700    0.532300    0.0838600
+48006.00  -0.091348   0.535243   0.0812546   -0.090600    0.534500    0.0811100
+48007.00  -0.089161   0.536945   0.0788778   -0.088400    0.536700    0.0787500
+48008.00  -0.086878   0.538638   0.0768512   -0.086000    0.538500    0.0767500
+48009.00  -0.084572   0.540379   0.0751104   -0.083800    0.540300    0.0750700
+48010.00  -0.082361   0.542122   0.0735487   -0.081600    0.541900    0.0736400
+48011.00  -0.080182   0.543839   0.0720199   -0.079200    0.543400    0.0720700
+48012.00  -0.077877   0.545523   0.0703975   -0.076700    0.545100    0.0704400
+48013.00  -0.075287   0.547166   0.0686133   -0.073900    0.546600    0.0686400
+48014.00  -0.072276   0.548758   0.0666583   -0.070800    0.548100    0.0666600
+48015.00  -0.068759   0.550281   0.0645693   -0.067100    0.549600    0.0645800
+48016.00  -0.064784   0.551772   0.0624088   -0.063000    0.551100    0.0623900
+48017.00  -0.060468   0.553258   0.0602525   -0.058800    0.552700    0.0602400
+48018.00  -0.055942   0.554752   0.0581712   -0.054600    0.554200    0.0581700
+48019.00  -0.051343   0.556267   0.0562209   -0.050100    0.555700    0.0562200
+48020.00  -0.046853   0.557798   0.0544325   -0.045800    0.557300    0.0544100
+48021.00  -0.042609   0.559319   0.0527930   -0.041600    0.558800    0.0527700
+48022.00  -0.038544   0.560777   0.0512772   -0.037200    0.560300    0.0512600
+48023.00  -0.034545   0.562139   0.0498442   -0.032900    0.561800    0.0498300
+48024.00  -0.030505   0.563381   0.0484348   -0.028600    0.563100    0.0484100
+48025.00  -0.026329   0.564505   0.0469792   -0.024200    0.564300    0.0469400
+48026.00  -0.022010   0.565557   0.0453965   -0.019800    0.565300    0.0453300
+48027.00  -0.017588   0.566569   0.0436153   -0.015600    0.566300    0.0435700
+48028.00  -0.013173   0.567518   0.0416069   -0.011300    0.567200    0.0415600
+48029.00  -0.008793   0.568420   0.0393602   -0.007000    0.568000    0.0393400
+48030.00  -0.004462   0.569293   0.0369007   -0.002800    0.568800    0.0368700
+48031.00  -0.000214   0.570103   0.0342940    0.001200    0.569500    0.0342700
+48032.00   0.003913   0.570813   0.0316432    0.005100    0.570100    0.0316000
+48033.00   0.007879   0.571392   0.0290757    0.009200    0.570600    0.0290200
+48034.00   0.011670   0.571830   0.0267069    0.013000    0.571000    0.0266500
+48035.00   0.015380   0.572205   0.0246173    0.016600    0.571100    0.0246100
+48036.00   0.019079   0.572518   0.0228185    0.020300    0.571200    0.0228400
+48037.00   0.022725   0.572767   0.0212270    0.023700    0.571200    0.0212300
+48038.00   0.026334   0.572981   0.0196908    0.027200    0.571300    0.0196800
+48039.00   0.029941   0.573192   0.0180715    0.030800    0.571300    0.0180200
+48040.00   0.033584   0.573433   0.0162917    0.034500    0.571700    0.0162200
+48041.00   0.037283   0.573697   0.0143478    0.038100    0.572000    0.0142600
+48042.00   0.041030   0.573946   0.0122870    0.041900    0.572400    0.0122100
+48043.00   0.044816   0.574165   0.0101794    0.045600    0.572700    0.0101000
+48044.00   0.048642   0.574355   0.0080994    0.049400    0.573000    0.0080500
+48045.00   0.052531   0.574522   0.0061118    0.053300    0.573300    0.0060900
+48046.00   0.056477   0.574617   0.0042663    0.057200    0.573600    0.0042500
+48047.00   0.060427   0.574560   0.0025991    0.061100    0.573700    0.0025500
+48048.00   0.064325   0.574288   0.0011235    0.065000    0.573700    0.0010700
+48049.00   0.068136   0.573790  -0.0001825    0.068900    0.573700   -0.0002400
+48050.00   0.071863   0.573089  -0.0013675    0.072800    0.572500   -0.0014200
+48051.00   0.075621   0.572246  -0.0025001    0.076700    0.571500   -0.0025700
+48052.00   0.079542   0.571331  -0.0036596    0.080600    0.570500   -0.0037300
+48053.00   0.083643   0.570420  -0.0049200    0.084400    0.569500   -0.0049900
+48054.00   0.087869   0.569582  -0.0063350    0.088400    0.568400   -0.0063900
+48055.00   0.092108   0.568860  -0.0079295    0.092200    0.567900   -0.0080100
+48056.00   0.096265   0.568224  -0.0097134    0.096200    0.567300   -0.0098000
+48057.00   0.100309   0.567608  -0.0116770    0.100100    0.566600   -0.0117500
+48058.00   0.104234   0.566950  -0.0137740    0.104100    0.565300   -0.0138400
+48059.00   0.108071   0.566198  -0.0159224    0.108100    0.564300   -0.0159800
+48060.00   0.111870   0.565323  -0.0180150    0.112100    0.563000   -0.0180900
+48061.00   0.115674   0.564316  -0.0199498    0.116100    0.561900   -0.0200200
+48062.00   0.119509   0.563148  -0.0216658    0.119900    0.560700   -0.0217500
+48063.00   0.123347   0.561823  -0.0231539    0.123700    0.559800   -0.0232500
+48064.00   0.127096   0.560361  -0.0244627    0.127300    0.558400   -0.0245600
+48065.00   0.130620   0.558791  -0.0256832    0.130600    0.556900   -0.0257500
+48066.00   0.134018   0.557053  -0.0269483    0.133800    0.555400   -0.0269900
+48067.00   0.137425   0.555162  -0.0283598    0.137100    0.553600   -0.0283900
+48068.00   0.140913   0.553151  -0.0299500    0.140600    0.551900   -0.0299700
+48069.00   0.144519   0.551067  -0.0316821    0.144000    0.549800   -0.0317100
+48070.00   0.148236   0.549015  -0.0334770    0.148100    0.547900   -0.0335000
+48071.00   0.152136   0.547071  -0.0352622    0.152300    0.546000   -0.0352800
+48072.00   0.156303   0.545287  -0.0369802    0.156400    0.544100   -0.0370400
+48073.00   0.160777   0.543635  -0.0385964    0.161100    0.542400   -0.0386700
+48074.00   0.165414   0.542060  -0.0400777    0.165400    0.540500   -0.0401500
+48075.00   0.169947   0.540503  -0.0413918    0.170000    0.539600   -0.0414200
+48076.00   0.174278   0.538913  -0.0425512    0.174600    0.538700   -0.0425100
+48077.00   0.178387   0.537236  -0.0435793    0.179100    0.537200   -0.0435200
+48078.00   0.182250   0.535415  -0.0445130    0.182800    0.535400   -0.0445600
+48079.00   0.185852   0.533390  -0.0454012    0.186200    0.533000   -0.0454800
+48080.00   0.189216   0.531114  -0.0462908    0.189300    0.530300   -0.0463400
+48081.00   0.192463   0.528646  -0.0472579    0.192800    0.527400   -0.0473200
+48082.00   0.195765   0.526086  -0.0483778    0.196300    0.525100   -0.0484700
+48083.00   0.199218   0.523520  -0.0496942    0.199700    0.522400   -0.0497600
+48084.00   0.202743   0.521000  -0.0512015    0.202900    0.520000   -0.0512800
+48085.00   0.206192   0.518522  -0.0528385    0.206000    0.517200   -0.0529500
+48086.00   0.209567   0.516057  -0.0545233    0.209200    0.514500   -0.0546100
+48087.00   0.212890   0.513585  -0.0561489    0.212500    0.511900   -0.0562100
+48088.00   0.216181   0.511092  -0.0576099    0.216100    0.510000   -0.0576800
+48089.00   0.219452   0.508596  -0.0588309    0.219900    0.507900   -0.0588900
+48090.00   0.222704   0.506113  -0.0597892    0.223700    0.505900   -0.0598600
+48091.00   0.225900   0.503614  -0.0605297    0.227400    0.503400   -0.0605700
+48092.00   0.229029   0.501038  -0.0611658    0.230500    0.500700   -0.0612600
+48093.00   0.232094   0.498320  -0.0618410    0.233600    0.497600   -0.0619500
+48094.00   0.235100   0.495415  -0.0626823    0.236200    0.494500   -0.0627700
+48095.00   0.238069   0.492348  -0.0637592    0.238800    0.491200   -0.0638100
+48096.00   0.241052   0.489190  -0.0650623    0.241800    0.488000   -0.0651100
+48097.00   0.244089   0.486004  -0.0665222    0.244900    0.484600   -0.0665400
+48098.00   0.247129   0.482782  -0.0680495    0.248000    0.481400   -0.0680900
+48099.00   0.250068   0.479505  -0.0695657    0.251000    0.478200   -0.0695900
+48100.00   0.252813   0.476174  -0.0710254    0.253800    0.474900   -0.0710300
+48101.00   0.255333   0.472800  -0.0723894    0.256300    0.471800   -0.0724200
+48102.00   0.257658   0.469387  -0.0736311    0.258500    0.468500   -0.0736800
+48103.00   0.259827   0.465943  -0.0747407    0.260500    0.465200   -0.0748300
+48104.00   0.261882   0.462474  -0.0757207    0.262500    0.462100   -0.0757800
+48105.00   0.263860   0.458987  -0.0765851    0.264200    0.457800   -0.0766300
+48106.00   0.265854   0.455485  -0.0773983    0.266000    0.454300   -0.0774500
+48107.00   0.267940   0.451994  -0.0782502    0.268700    0.451400   -0.0782800
+48108.00   0.270121   0.448524  -0.0792279    0.271900    0.448500   -0.0792400
+48109.00   0.272360   0.445084  -0.0804064    0.274300    0.445500   -0.0804500
+48110.00   0.274589   0.441685  -0.0818318    0.276500    0.442500   -0.0818700
+48111.00   0.276741   0.438343  -0.0835090    0.278200    0.439000   -0.0835500
+48112.00   0.278754   0.435072  -0.0853967    0.279800    0.435300   -0.0854300
+48113.00   0.280596   0.431901  -0.0874084    0.281200    0.431200   -0.0874800
+48114.00   0.282279   0.428841  -0.0894342    0.282600    0.427500   -0.0894900
+48115.00   0.283818   0.425877  -0.0913622    0.284000    0.424500   -0.0914600
+48116.00   0.285247   0.422970  -0.0931091    0.285700    0.421600   -0.0933000
+48117.00   0.286585   0.420014  -0.0946352    0.286900    0.418600   -0.0948000
+48118.00   0.287859   0.416916  -0.0959629    0.288200    0.416000   -0.0960600
+48119.00   0.289172   0.413704  -0.0971804    0.290000    0.413000   -0.0972400
+48120.00   0.290686   0.410418  -0.0984078    0.291700    0.409300   -0.0984100
+48121.00   0.292427   0.407051  -0.0997677    0.293400    0.405500   -0.0998400
+48122.00   0.294320   0.403618  -0.1013457    0.295400    0.401900   -0.1014400
+48123.00   0.296284   0.400149  -0.1031677    0.297500    0.398400   -0.1032600
+48124.00   0.298236   0.396673  -0.1051968    0.299600    0.395700   -0.1053400
+48125.00   0.300054   0.393217  -0.1073506    0.301500    0.392900   -0.1074500
+48126.00   0.301644   0.389767  -0.1095274    0.303000    0.389800   -0.1096500
+48127.00   0.302975   0.386306  -0.1116384    0.304100    0.386400   -0.1117600
+48128.00   0.304034   0.382824  -0.1136104    0.305000    0.382300   -0.1137100
+48129.00   0.304812   0.379313  -0.1153995    0.305800    0.378400   -0.1154700
+48130.00   0.305343   0.375769  -0.1169930    0.305800    0.375100   -0.1170600
+48131.00   0.305706   0.372194  -0.1184076    0.306000    0.371900   -0.1184500
+48132.00   0.305957   0.368585  -0.1196803    0.306200    0.368500   -0.1197400
+48133.00   0.306169   0.364943  -0.1208648    0.306400    0.364700   -0.1209200
+48134.00   0.306493   0.361281  -0.1220331    0.307000    0.360600   -0.1220800
+48135.00   0.307079   0.357616  -0.1232639    0.307700    0.356600   -0.1233000
+48136.00   0.307923   0.353948  -0.1246436    0.308300    0.352500   -0.1246200
+48137.00   0.308928   0.350259  -0.1262465    0.309300    0.348600   -0.1261700
+48138.00   0.309992   0.346527  -0.1281148    0.310300    0.344800   -0.1280700
+48139.00   0.311003   0.342728  -0.1302441    0.311700    0.341400   -0.1303100
+48140.00   0.311833   0.338838  -0.1325698    0.312100    0.337500   -0.1327600
+48141.00   0.312470   0.334859  -0.1349571    0.312800    0.333600   -0.1351200
+48142.00   0.312954   0.330804  -0.1372534    0.313500    0.329700   -0.1374100
+48143.00   0.313259   0.326689  -0.1393367    0.313600    0.325700   -0.1394800
+48144.00   0.313337   0.322545  -0.1411452    0.313700    0.321600   -0.1412500
+48145.00   0.313177   0.318438  -0.1426925    0.313700    0.317500   -0.1427300
+48146.00   0.312883   0.314378  -0.1440792    0.313400    0.313500   -0.1441000
+48147.00   0.312523   0.310356  -0.1454500    0.312900    0.309400   -0.1454700
+48148.00   0.312119   0.306392  -0.1469374    0.312600    0.305600   -0.1469700
+48149.00   0.311661   0.302501  -0.1486353    0.311800    0.301500   -0.1486700
+48150.00   0.311131   0.298699  -0.1505805    0.310800    0.297400   -0.1505900
+48151.00   0.310531   0.294983  -0.1527464    0.310000    0.293700   -0.1527900
+48152.00   0.309871   0.291347  -0.1550642    0.309500    0.290500   -0.1551600
+48153.00   0.309160   0.287783  -0.1574408    0.309400    0.287000   -0.1575100
+48154.00   0.308393   0.284282  -0.1597815    0.309100    0.283700   -0.1598600
+48155.00   0.307506   0.280822  -0.1620083    0.308700    0.280400   -0.1620200
+48156.00   0.306397   0.277373  -0.1640705    0.307900    0.277000   -0.1641000
+48157.00   0.305056   0.273887  -0.1659581    0.306500    0.273600   -0.1660200
+48158.00   0.303574   0.270303  -0.1676926    0.304900    0.270000   -0.1677500
+48159.00   0.302063   0.266557  -0.1693171    0.303100    0.266300   -0.1694200
+48160.00   0.300634   0.262617  -0.1708895    0.301000    0.262200   -0.1710400
+48161.00   0.299388   0.258587  -0.1724761    0.299600    0.257900   -0.1726200
+48162.00   0.298364   0.254585  -0.1741494    0.298000    0.253500   -0.1742800
+48163.00   0.297561   0.250709  -0.1759802    0.297700    0.249800   -0.1760800
+48164.00   0.296905   0.247030  -0.1780282    0.297700    0.246300   -0.1781200
+48165.00   0.296302   0.243594  -0.1803265    0.297400    0.243000   -0.1804100
+48166.00   0.295654   0.240360  -0.1828631    0.297000    0.240100   -0.1829400
+48167.00   0.294851   0.237241  -0.1855792    0.295900    0.236900   -0.1856200
+48168.00   0.293782   0.234151  -0.1883677    0.294300    0.233700   -0.1884200
+48169.00   0.292376   0.230984  -0.1910975    0.292300    0.230000   -0.1911500
+48170.00   0.290636   0.227640  -0.1936458    0.290100    0.226000   -0.1937100
+48171.00   0.288711   0.224110  -0.1959332    0.287700    0.221900   -0.1960100
+48172.00   0.286692   0.220434  -0.1979555    0.285800    0.218400   -0.1980200
+48173.00   0.284630   0.216660  -0.1997832    0.283800    0.214900   -0.1998800
+48174.00   0.282572   0.212839  -0.2015301    0.282000    0.211800   -0.2016300
+48175.00   0.280541   0.209022  -0.2033154    0.280300    0.208800   -0.2033300
+48176.00   0.278421   0.205271  -0.2052338    0.278700    0.205400   -0.2052200
+48177.00   0.276142   0.201622  -0.2073263    0.276800    0.202000   -0.2073700
+48178.00   0.273732   0.198072  -0.2095793    0.274400    0.198200   -0.2096700
+48179.00   0.271236   0.194613  -0.2119396    0.271700    0.194200   -0.2120200
+48180.00   0.268687   0.191236  -0.2143357    0.269000    0.190700   -0.2143600
+48181.00   0.266124   0.187938  -0.2166969    0.266300    0.187300   -0.2167500
+48182.00   0.263546   0.184709  -0.2189627    0.263500    0.184100   -0.2190000
+48183.00   0.260909   0.181498  -0.2210914    0.260700    0.180900   -0.2211300
+48184.00   0.258171   0.178283  -0.2230666    0.258000    0.178100   -0.2231500
+48185.00   0.255334   0.175151  -0.2248874    0.255400    0.175300   -0.2249700
+48186.00   0.252529   0.172099  -0.2265925    0.252600    0.172300   -0.2266200
+48187.00   0.249944   0.169070  -0.2282489    0.250000    0.169100   -0.2282900
+48188.00   0.247695   0.166030  -0.2299310    0.247600    0.165700   -0.2300200
+48189.00   0.245667   0.163016  -0.2316994    0.245400    0.162000   -0.2317700
+48190.00   0.243703   0.160079  -0.2336061    0.244100    0.158900   -0.2337400
+48191.00   0.241754   0.157249  -0.2356992    0.242200    0.155800   -0.2357700
+48192.00   0.239851   0.154550  -0.2380273    0.240300    0.153400   -0.2381500
+48193.00   0.238015   0.152003  -0.2406142    0.238600    0.151400   -0.2406500
+48194.00   0.236208   0.149592  -0.2434364    0.236800    0.149200   -0.2435300
+48195.00   0.234410   0.147255  -0.2464136    0.235100    0.147300   -0.2465500
+48196.00   0.232557   0.144959  -0.2494178    0.233000    0.144000   -0.2494400
+48197.00   0.230548   0.142681  -0.2523024    0.231200    0.141800   -0.2522500
+48198.00   0.228263   0.140412  -0.2549381    0.228500    0.139400   -0.2549800
+48199.00   0.225562   0.138140  -0.2572554    0.226000    0.137100   -0.2573500
+48200.00   0.222305   0.135834  -0.2592738    0.222400    0.134500   -0.2594700
+48201.00   0.218641   0.133500  -0.2611209    0.218900    0.132000   -0.2611900
+48202.00   0.214857   0.131159  -0.2629680    0.214800    0.129500   -0.2631600
+48203.00   0.211202   0.128824  -0.2649608    0.211000    0.127600   -0.2650800
+48204.00   0.207788   0.126509  -0.2671750    0.207300    0.125300   -0.2672500
+48205.00   0.204666   0.124230  -0.2696089    0.203900    0.123000   -0.2697100
+48206.00   0.201775   0.121988  -0.2721936    0.201100    0.120800   -0.2724300
+48207.00   0.199017   0.119764  -0.2748376    0.198000    0.118600   -0.2750100
+48208.00   0.196297   0.117541  -0.2774490    0.195400    0.116400   -0.2775800
+48209.00   0.193519   0.115309  -0.2799492    0.192800    0.113800   -0.2800400
+48210.00   0.190608   0.113086  -0.2822820    0.189900    0.111800   -0.2823400
+48211.00   0.187568   0.110916  -0.2844222    0.186900    0.109500   -0.2844000
+48212.00   0.184445   0.108853  -0.2863721    0.183900    0.107900   -0.2863600
+48213.00   0.181286   0.106953  -0.2881592    0.180700    0.106100   -0.2881300
+48214.00   0.178131   0.105240  -0.2898352    0.177700    0.104400   -0.2898400
+48215.00   0.175047   0.103600  -0.2914843    0.174700    0.102800   -0.2915300
+48216.00   0.172026   0.102002  -0.2931847    0.171500    0.101200   -0.2932900
+48217.00   0.169017   0.100466  -0.2949979    0.168400    0.099600   -0.2951400
+48218.00   0.165974   0.098992  -0.2969738    0.165400    0.097800   -0.2970900
+48219.00   0.162854   0.097582  -0.2991467    0.162300    0.096300   -0.2992100
+48220.00   0.159599   0.096223  -0.3015319    0.159200    0.094800   -0.3015500
+48221.00   0.156136   0.094901  -0.3041180    0.155800    0.093300   -0.3041400
+48222.00   0.152413   0.093609  -0.3068584    0.152200    0.092400   -0.3068700
+48223.00   0.148427   0.092345  -0.3096706    0.147900    0.091200   -0.3097300
+48224.00   0.144337   0.091103  -0.3124545    0.143600    0.090000   -0.3125400
+48225.00   0.140317   0.089885  -0.3151098    0.139300    0.088800   -0.3152100
+48226.00   0.136535   0.088739  -0.3175645    0.135500    0.087700   -0.3176400
+48227.00   0.132960   0.087668  -0.3198018    0.131700    0.086600   -0.3198800
+48228.00   0.129504   0.086654  -0.3218807    0.128400    0.085500   -0.3219400
+48229.00   0.126079   0.085679  -0.3239147    0.125100    0.084500   -0.3240300
+48230.00   0.122590   0.084722  -0.3260221    0.121500    0.083500   -0.3261200
+48231.00   0.119050   0.083780  -0.3282816    0.117900    0.082600   -0.3284000
+48232.00   0.115520   0.082857  -0.3307054    0.114300    0.081700   -0.3308400
+48233.00   0.112032   0.081940  -0.3332465    0.110800    0.080800   -0.3333800
+48234.00   0.108594   0.081004  -0.3358221    0.107300    0.079900   -0.3359400
+48235.00   0.105159   0.080000  -0.3383410    0.103800    0.078900   -0.3384200
+48236.00   0.101696   0.078953  -0.3407106    0.100400    0.077900   -0.3407500
+48237.00   0.098177   0.077921  -0.3428633    0.097100    0.076900   -0.3429000
+48238.00   0.094582   0.076908  -0.3447823    0.093700    0.075800   -0.3448300
+48239.00   0.090989   0.075839  -0.3464901    0.090400    0.074700   -0.3465700
+48240.00   0.087493   0.074656  -0.3480344    0.087200    0.073500   -0.3481300
+48241.00   0.084124   0.073460  -0.3494722    0.083900    0.072500   -0.3495700
+48242.00   0.080833   0.072344  -0.3508638    0.080600    0.071500   -0.3509700
+48243.00   0.077561   0.071380  -0.3522759    0.077200    0.070600   -0.3523800
+48244.00   0.074250   0.070638  -0.3537777    0.073700    0.069800   -0.3538900
+48245.00   0.070866   0.070146  -0.3554434    0.070200    0.069200   -0.3555600
+48246.00   0.067377   0.069860  -0.3573110    0.066500    0.068600   -0.3574000
+48247.00   0.063775   0.069730  -0.3593874    0.062800    0.068200   -0.3594600
+48248.00   0.060069   0.069705  -0.3616542    0.058900    0.068000   -0.3617100
+48249.00   0.056270   0.069732  -0.3640664    0.055000    0.067800   -0.3641200
+48250.00   0.052379   0.069786  -0.3665589    0.050900    0.067700   -0.3666200
+48251.00   0.048355   0.069869  -0.3690509    0.046900    0.067700   -0.3691300
+48252.00   0.044145   0.069989  -0.3714556    0.042800    0.067800   -0.3715300
+48253.00   0.039730   0.070144  -0.3737009    0.038700    0.067900   -0.3737700
+48254.00   0.035225   0.070309  -0.3757491    0.034600    0.068100   -0.3757900
+48255.00   0.030776   0.070470  -0.3776271    0.030700    0.068400   -0.3776600
+48256.00   0.026474   0.070678  -0.3794330    0.026900    0.068800   -0.3794600
+48257.00   0.022389   0.070986   0.6186975    0.023100    0.069300    0.6186700
+48258.00   0.018591   0.071447   0.6166447    0.019500    0.070000    0.6166000
+48259.00   0.015151   0.072113   0.6143495    0.016100    0.070700    0.6142800
+48260.00   0.012118   0.073018   0.6118302    0.013200    0.072200    0.6117600
+48261.00   0.009382   0.074132   0.6091699    0.010000    0.073200    0.6091200
+48262.00   0.006782   0.075384   0.6064782    0.006800    0.074300    0.6064400
+48263.00   0.004177   0.076691   0.6038514    0.003600    0.075500    0.6038400
+48264.00   0.001418   0.077970   0.6013588    0.000400    0.076600    0.6013600
+48265.00  -0.001661   0.079172   0.5990451   -0.002800    0.077800    0.5990600
+48266.00  -0.005050   0.080296   0.5969117   -0.006100    0.078800    0.5969200
+48267.00  -0.008653   0.081354   0.5949329   -0.009500    0.079800    0.5949300
+48268.00  -0.012377   0.082340   0.5930695   -0.013000    0.080700    0.5930700
+48269.00  -0.016122   0.083186   0.5912743   -0.016600    0.081500    0.5912800
+48270.00  -0.019806   0.083830   0.5894949   -0.020300    0.082100    0.5895000
+48271.00  -0.023464   0.084317   0.5876705   -0.024000    0.082700    0.5876600
+48272.00  -0.027175   0.084736   0.5857450   -0.027700    0.083300    0.5857400
+48273.00  -0.031011   0.085171   0.5836807   -0.031400    0.083700    0.5836800
+48274.00  -0.034995   0.085646   0.5814692   -0.035100    0.084200    0.5814600
+48275.00  -0.039044   0.086098   0.5791302   -0.038700    0.084800    0.5791000
+48276.00  -0.043017   0.086553   0.5766972   -0.042200    0.085400    0.5766400
+48277.00  -0.046776   0.087092   0.5742211   -0.045600    0.086100    0.5741300
+48278.00  -0.050215   0.087788   0.5717685   -0.049000    0.086900    0.5716400
+48279.00  -0.053236   0.088712   0.5694044   -0.052200    0.087800    0.5692800
+48280.00  -0.055812   0.089927   0.5671813   -0.055300    0.088900    0.5670700
+48281.00  -0.058173   0.091415   0.5651398   -0.058500    0.090200    0.5650600
+48282.00  -0.060588   0.093097   0.5632680   -0.061800    0.091500    0.5632300
+48283.00  -0.063289   0.094881   0.5614935   -0.065100    0.092900    0.5614900
+48284.00  -0.066451   0.096671   0.5597015   -0.068600    0.094400    0.5597000
+48285.00  -0.070208   0.098382   0.5577729   -0.072100    0.096000    0.5577700
+48286.00  -0.074442   0.100022   0.5556263   -0.075800    0.097500    0.5556000
+48287.00  -0.078910   0.101594   0.5532423   -0.079600    0.099100    0.5531700
+48288.00  -0.083389   0.103081   0.5506707   -0.083500    0.100600    0.5505600
+48289.00  -0.087740   0.104469   0.5480040   -0.087400    0.102200    0.5478600
+48290.00  -0.091885   0.105752   0.5453487   -0.090900    0.103500    0.5452100
+48291.00  -0.095784   0.106978   0.5428091   -0.094400    0.104800    0.5426800
+48292.00  -0.099377   0.108224   0.5404512   -0.097900    0.106200    0.5403600
+48293.00  -0.102658   0.109555   0.5382971   -0.101500    0.107800    0.5382100
+48294.00  -0.105703   0.110999   0.5363232   -0.105000    0.109400    0.5362300
+48295.00  -0.108620   0.112573   0.5344856   -0.108400    0.111100    0.5343900
+48296.00  -0.111491   0.114302   0.5327295   -0.111700    0.112900    0.5326300
+48297.00  -0.114367   0.116185   0.5309856   -0.114900    0.114800    0.5308900
+48298.00  -0.117315   0.118198   0.5291822   -0.118000    0.116900    0.5290900
+48299.00  -0.120404   0.120310   0.5272551   -0.121100    0.119100    0.5271700
+48300.00  -0.123664   0.122513   0.5251586   -0.124100    0.121400    0.5251000
+48301.00  -0.126908   0.124910   0.5228707   -0.127100    0.123900    0.5228300
+48302.00  -0.130112   0.127563   0.5203904   -0.130100    0.126500    0.5203900
+48303.00  -0.133270   0.130449   0.5177638   -0.133100    0.129200    0.5177900
+48304.00  -0.136368   0.133521   0.5150737   -0.136100    0.132000    0.5151300
+48305.00  -0.139405   0.136727   0.5124253   -0.139100    0.134900    0.5124700
+48306.00  -0.142389   0.140004   0.5099051   -0.142200    0.137800    0.5099100
+48307.00  -0.145325   0.143291   0.5075665   -0.145300    0.140800    0.5075200
+48308.00  -0.148250   0.146514   0.5054215   -0.148300    0.143800    0.5053200
+48309.00  -0.151190   0.149542   0.5034249   -0.151300    0.146800    0.5033000
+48310.00  -0.154175   0.152268   0.5014858   -0.154300    0.149800    0.5013600
+48311.00  -0.157177   0.154778   0.4995116   -0.157100    0.152800    0.4994000
+48312.00  -0.160098   0.157240   0.4974054   -0.159900    0.155700    0.4973000
+48313.00  -0.162850   0.159794   0.4950844   -0.162600    0.158600    0.4949800
+48314.00  -0.165376   0.162467   0.4925069   -0.165200    0.161500    0.4923900
+48315.00  -0.167647   0.165250   0.4896908   -0.167700    0.164300    0.4895600
+48316.00  -0.169720   0.168114   0.4867258   -0.170100    0.167200    0.4866100
+48317.00  -0.171679   0.171052   0.4837438   -0.172400    0.170100    0.4836600
+48318.00  -0.173596   0.174060   0.4808788   -0.174500    0.173000    0.4808300
+48319.00  -0.175545   0.177130   0.4782346   -0.176500    0.175900    0.4782300
+48320.00  -0.177585   0.180250   0.4758656   -0.178400    0.179300    0.4758600
+48321.00  -0.179691   0.183430   0.4737648   -0.180200    0.182700    0.4737500
+48322.00  -0.181814   0.186689   0.4718903   -0.181900    0.186200    0.4718600
+48323.00  -0.183873   0.190040   0.4701765   -0.183500    0.189700    0.4701000
+48324.00  -0.185715   0.193494   0.4685319   -0.185200    0.193200    0.4684200
+48325.00  -0.187247   0.197079   0.4668623   -0.186800    0.196700    0.4667300
+48326.00  -0.188552   0.200801   0.4650913   -0.188400    0.200300    0.4649500
+48327.00  -0.189749   0.204643   0.4631528   -0.189900    0.203900    0.4630100
+48328.00  -0.190961   0.208577   0.4610003   -0.191400    0.207600    0.4608400
+48329.00  -0.192341   0.212539   0.4586217   -0.192800    0.211300    0.4584300
+48330.00  -0.193994   0.216441   0.4560330   -0.194200    0.215000    0.4557900
+48331.00  -0.195667   0.220202   0.4532397   -0.195600    0.218700    0.4529900
+48332.00  -0.197292   0.223842   0.4503304   -0.197000    0.222400    0.4501100
+48333.00  -0.198813   0.227397   0.4474393   -0.198400    0.226100    0.4472800
+48334.00  -0.200148   0.230902   0.4446940   -0.199700    0.229900    0.4445900
+48335.00  -0.201247   0.234395   0.4421798   -0.201100    0.233600    0.4421300
+48336.00  -0.202195   0.237934   0.4399037   -0.202600    0.237300    0.4398900
+48337.00  -0.203113   0.241573   0.4378071   -0.204000    0.241100    0.4378100
+48338.00  -0.204155   0.245328   0.4357970   -0.205400    0.245000    0.4358000
+48339.00  -0.205483   0.249211   0.4337687   -0.206900    0.248800    0.4337400
+48340.00  -0.207237   0.253232   0.4316295   -0.208400    0.252800    0.4315400
+48341.00  -0.209344   0.257362   0.4292805   -0.209800    0.256700    0.4291300
+48342.00  -0.211542   0.261519   0.4266726   -0.211300    0.260700    0.4265000
+48343.00  -0.213596   0.265659   0.4238689   -0.212600    0.264700    0.4237100
+48344.00  -0.215298   0.269752   0.4209839   -0.214000    0.268800    0.4208500
+48345.00  -0.216509   0.273776   0.4181445   -0.215100    0.272900    0.4180100
+48346.00  -0.217320   0.277756   0.4154460   -0.216200    0.277000    0.4152800
+48347.00  -0.217857   0.281739   0.4129329   -0.217200    0.281000    0.4127500
+48348.00  -0.218249   0.285770   0.4106103   -0.218100    0.285200    0.4104000
+48349.00  -0.218610   0.289880   0.4084511   -0.218800    0.289300    0.4082400
+48350.00  -0.219006   0.294038   0.4064165   -0.219600    0.293400    0.4062000
+48351.00  -0.219449   0.298196   0.4044592   -0.220200    0.297300    0.4042500
+48352.00  -0.219906   0.302322   0.4025264   -0.220600    0.301400    0.4023300
+48353.00  -0.220303   0.306406   0.4005663   -0.220700    0.305400    0.4004000
+48354.00  -0.220558   0.310445   0.3985331   -0.220700    0.309400    0.3983700
+48355.00  -0.220598   0.314438   0.3963880   -0.220400    0.313400    0.3962200
+48356.00  -0.220373   0.318404   0.3940960   -0.219900    0.317500    0.3939200
+48357.00  -0.219857   0.322358   0.3916419   -0.219300    0.321500    0.3914500
+48358.00  -0.219056   0.326309   0.3890362   -0.218600    0.325500    0.3888200
+48359.00  -0.218014   0.330253   0.3863229   -0.217800    0.329500    0.3861200
+48360.00  -0.216806   0.334184   0.3835812   -0.216900    0.333500    0.3834300
+48361.00  -0.215546   0.338103   0.3809266   -0.216000    0.337400    0.3808300
+48362.00  -0.214356   0.342027   0.3784616   -0.215200    0.341400    0.3784200
+48363.00  -0.213352   0.345978   0.3762352   -0.214500    0.345300    0.3762400
+48364.00  -0.212598   0.349971   0.3742255   -0.213800    0.349200    0.3742100
+48365.00  -0.212116   0.353997   0.3723417   -0.213200    0.353200    0.3722700
+48366.00  -0.211712   0.357991   0.3704127   -0.212800    0.357100    0.3702800
+48367.00  -0.211353   0.361955   0.3683187   -0.212400    0.361000    0.3681400
+48368.00  -0.211093   0.365896   0.3660009   -0.212200    0.365000    0.3657900
+48369.00  -0.210987   0.369820   0.3634567   -0.212000    0.368900    0.3632200
+48370.00  -0.211028   0.373708   0.3607315   -0.211900    0.372700    0.3605000
+48371.00  -0.211077   0.377499   0.3579095   -0.211800    0.376600    0.3576800
+48372.00  -0.211001   0.381195   0.3550906   -0.211700    0.380400    0.3548800
+48373.00  -0.210716   0.384817   0.3523675   -0.211500    0.384200    0.3521800
+48374.00  -0.210235   0.388395   0.3498073   -0.211200    0.388000    0.3496500
+48375.00  -0.209581   0.391966   0.3474433   -0.210700    0.391700    0.3473000
+48376.00  -0.208780   0.395564   0.3452739   -0.210000    0.395400    0.3451300
+48377.00  -0.207855   0.399210   0.3432674   -0.209200    0.399100    0.3431200
+48378.00  -0.206791   0.402890   0.3413683   -0.208200    0.402800    0.3412100
+48379.00  -0.205572   0.406589   0.3395114   -0.207000    0.406500    0.3393200
+48380.00  -0.204205   0.410310   0.3376298   -0.205400    0.409500    0.3374200
+48381.00  -0.202702   0.414042   0.3356575   -0.203900    0.413200    0.3354200
+48382.00  -0.201075   0.417773   0.3335389   -0.202400    0.417000    0.3332900
+48383.00  -0.199327   0.421520   0.3312343   -0.200700    0.420700    0.3309700
+48384.00  -0.197475   0.425296   0.3287302   -0.198900    0.424400    0.3284700
+48385.00  -0.195569   0.429075   0.3260494   -0.197000    0.428200    0.3257900
+48386.00  -0.193643   0.432845   0.3232425   -0.195100    0.431900    0.3229800
+48387.00  -0.191707   0.436597   0.3203892   -0.193000    0.435600    0.3201200
+48388.00  -0.189725   0.440306   0.3175869   -0.190700    0.439200    0.3173100
+48389.00  -0.187655   0.443949   0.3149288   -0.188400    0.442800    0.3146500
+48390.00  -0.185453   0.447502   0.3124747   -0.185900    0.446400    0.3122100
+48391.00  -0.183061   0.450942   0.3102256   -0.183200    0.449800    0.3099900
+48392.00  -0.180442   0.454273   0.3081325   -0.180500    0.453200    0.3079300
+48393.00  -0.177606   0.457530   0.3061030   -0.177600    0.456500    0.3059200
+48394.00  -0.174586   0.460741   0.3040388   -0.174600    0.459700    0.3038700
+48395.00  -0.171472   0.463903   0.3018647   -0.171700    0.462800    0.3017100
+48396.00  -0.168371   0.467004   0.2995630   -0.168700    0.465900    0.2994200
+48397.00  -0.165388   0.470030   0.2971748   -0.165700    0.468900    0.2970500
+48398.00  -0.162556   0.472974   0.2947821   -0.162800    0.471900    0.2946900
+48399.00  -0.159759   0.475856   0.2924858   -0.159800    0.474800    0.2924200
+48400.00  -0.156924   0.478719   0.2903538   -0.156900    0.477700    0.2902800
+48401.00  -0.154015   0.481596   0.2884229   -0.154000    0.480600    0.2883200
+48402.00  -0.151055   0.484485   0.2866991   -0.151100    0.483400    0.2865400
+48403.00  -0.148081   0.487372   0.2851582   -0.148200    0.486200    0.2849100
+48404.00  -0.145115   0.490241   0.2837485   -0.145200    0.489000    0.2834100
+48405.00  -0.142177   0.493080   0.2824062   -0.142300    0.491800    0.2819600
+48406.00  -0.139273   0.495891   0.2810759   -0.139400    0.494600    0.2805200
+48407.00  -0.136397   0.498678   0.2797012   -0.136400    0.497400    0.2790300
+48408.00  -0.133530   0.501446   0.2782301   -0.133300    0.500200    0.2774600
+48409.00  -0.130632   0.504185   0.2766170   -0.130200    0.502900    0.2758000
+48410.00  -0.127650   0.506887   0.2748345   -0.127300    0.505700    0.2751600
+48411.00  -0.124536   0.509546   0.2728759   -0.124100    0.508500    0.2730300
+48412.00  -0.121236   0.512145   0.2707460   -0.120800    0.511200    0.2707400
+48413.00  -0.117799   0.514711   0.2684654   -0.117500    0.513900    0.2683000
+48414.00  -0.114309   0.517299   0.2660808   -0.114000    0.516500    0.2657700
+48415.00  -0.110827   0.519947   0.2636634   -0.110500    0.519100    0.2633500
+48416.00  -0.107321   0.522631   0.2613040   -0.106900    0.521700    0.2610100
+48417.00  -0.103738   0.525310   0.2590792   -0.103200    0.524200    0.2588000
+48418.00  -0.100023   0.527945   0.2570190   -0.099400    0.526700    0.2567700
+48419.00  -0.096172   0.530474   0.2551011   -0.095600    0.529000    0.2549000
+48420.00  -0.092407   0.532808   0.2532746   -0.091800    0.531300    0.2530800
+48421.00  -0.088697   0.534938   0.2514330   -0.088100    0.533500    0.2512000
+48422.00  -0.084948   0.536894   0.2494815   -0.084400    0.535600    0.2492100
+48423.00  -0.081217   0.538697   0.2473810   -0.080700    0.537500    0.2470600
+48424.00  -0.077580   0.540381   0.2451650   -0.077200    0.539300    0.2448100
+48425.00  -0.074071   0.542020   0.2429220   -0.073700    0.541100    0.2425200
+48426.00  -0.070701   0.543663   0.2407553   -0.070300    0.542700    0.2403400
+48427.00  -0.067473   0.545292   0.2387508   -0.067100    0.544300    0.2383200
+48428.00  -0.064420   0.546876   0.2369620   -0.063900    0.545900    0.2365500
+48429.00  -0.061563   0.548392   0.2354087   -0.060900    0.547300    0.2350300
+48430.00  -0.058826   0.549874   0.2340892   -0.057800    0.548800    0.2337300
+48431.00  -0.056103   0.551379   0.2329812   -0.054800    0.550300    0.2326600
+48432.00  -0.053290   0.552965   0.2320435   -0.051700    0.551800    0.2317300
+48433.00  -0.050301   0.554667   0.2312179   -0.048700    0.553200    0.2309100
+48434.00  -0.047131   0.556431   0.2304329   -0.045600    0.554700    0.2301000
+48435.00  -0.043799   0.558138   0.2296136   -0.042500    0.556200    0.2292700
+48436.00  -0.040312   0.559642   0.2286955   -0.039300    0.557600    0.2283500
+48437.00  -0.036690   0.560847   0.2276412   -0.036000    0.559000    0.2272900
+48438.00  -0.033013   0.561856   0.2264439   -0.032600    0.560300    0.2260700
+48439.00  -0.029366   0.562824   0.2251163   -0.029100    0.561600    0.2247200
+48440.00  -0.025727   0.563843   0.2236695   -0.025000    0.563100    0.2234700
+48441.00  -0.021941   0.564872   0.2221343   -0.021200    0.564100    0.2219200
+48442.00  -0.017994   0.565876   0.2205824   -0.017400    0.565000    0.2203700
+48443.00  -0.013950   0.566824   0.2190993   -0.013500    0.565900    0.2189100
+48444.00  -0.009878   0.567683   0.2177630   -0.009600    0.566700    0.2176000
+48445.00  -0.005846   0.568421   0.2166216   -0.005700    0.567400    0.2164800
+48446.00  -0.001914   0.569021   0.2156705   -0.001700    0.568100    0.2155300
+48447.00   0.001903   0.569477   0.2148371    0.002300    0.568700    0.2146700
+48448.00   0.005725   0.569812   0.2139795    0.006300    0.569200    0.2137800
+48449.00   0.009670   0.570109   0.2129437    0.010300    0.569700    0.2127100
+48450.00   0.013729   0.570442   0.2116343    0.014400    0.570200    0.2113800
+48451.00   0.017946   0.570858   0.2100398    0.018700    0.570600    0.2097900
+48452.00   0.022375   0.571352   0.2082363    0.023000    0.570900    0.2080100
+48453.00   0.026968   0.571904   0.2063631    0.027400    0.571200    0.2061600
+48454.00   0.031649   0.572418   0.2045564    0.031800    0.571500    0.2043600
+48455.00   0.036358   0.572701   0.2029001    0.036400    0.571700    0.2027300
+48456.00   0.041044   0.572782   0.2014419    0.041000    0.571900    0.2012900
+48457.00   0.045589   0.572739   0.2002059    0.045600    0.572000    0.2000600
+48458.00   0.050029   0.572650   0.1991700    0.050300    0.572000    0.1990200
+48459.00   0.054497   0.572540   0.1982781    0.054900    0.572000    0.1981300
+48460.00   0.059109   0.572417   0.1974633    0.059500    0.571900    0.1973100
+48461.00   0.063817   0.572247   0.1966611    0.064000    0.571700    0.1965100
+48462.00   0.068502   0.572009   0.1958101    0.068500    0.571500    0.1956600
+48463.00   0.073105   0.571718   0.1948501    0.072800    0.571200    0.1947100
+48464.00   0.077590   0.571383   0.1937368    0.076900    0.570800    0.1936000
+48465.00   0.081901   0.571007   0.1924553    0.081000    0.570400    0.1923400
+48466.00   0.085971   0.570583   0.1910240    0.084800    0.569800    0.1909400
+48467.00   0.089710   0.570085   0.1895032    0.088500    0.569200    0.1894300
+48468.00   0.093032   0.569494   0.1879815    0.092000    0.568400    0.1878800
+48469.00   0.096042   0.568777   0.1865172    0.095400    0.567600    0.1863600
+48470.00   0.098933   0.567909   0.1851435    0.098700    0.566700    0.1849200
+48471.00   0.101871   0.566886   0.1838886    0.101900    0.565600    0.1836600
+48472.00   0.104958   0.565705   0.1827794    0.105100    0.564500    0.1825800
+48473.00   0.108217   0.564427   0.1818143    0.108400    0.563400    0.1816700
+48474.00   0.111662   0.563106   0.1809531    0.112300    0.562700    0.1807900
+48475.00   0.115248   0.561778   0.1801100    0.115600    0.561500    0.1799900
+48476.00   0.118816   0.560444   0.1791690    0.118900    0.560200    0.1790600
+48477.00   0.122395   0.559084   0.1780101    0.122300    0.558900    0.1778800
+48478.00   0.126042   0.557680   0.1765680    0.125800    0.557500    0.1764200
+48479.00   0.129721   0.556237   0.1748620    0.129300    0.556000    0.1746800
+48480.00   0.133366   0.554770   0.1729817    0.132700    0.554500    0.1727600
+48481.00   0.136914   0.553290   0.1710455    0.136200    0.552900    0.1708200
+48482.00   0.140316   0.551783   0.1691646    0.139500    0.551200    0.1689400
+48483.00   0.143560   0.550210   0.1674311    0.142800    0.549400    0.1672400
+48484.00   0.146642   0.548540   0.1658892    0.146000    0.547600    0.1657200
+48485.00   0.149543   0.546773   0.1645384    0.149000    0.545700    0.1644000
+48486.00   0.152226   0.544914   0.1633450    0.151800    0.543800    0.1632200
+48487.00   0.154714   0.542974   0.1622537    0.154600    0.541800    0.1621400
+48488.00   0.157052   0.540992   0.1612062    0.157200    0.539800    0.1611000
+48489.00   0.159312   0.538993   0.1601450    0.159700    0.537800    0.1600400
+48490.00   0.161609   0.536939   0.1590155    0.162200    0.535800    0.1588800
+48491.00   0.164054   0.534799   0.1577638    0.164500    0.533700    0.1575700
+48492.00   0.166659   0.532583   0.1563302    0.166900    0.531700    0.1561000
+48493.00   0.169344   0.530338   0.1546978    0.169300    0.529600    0.1544400
+48494.00   0.171997   0.528109   0.1528838    0.171700    0.527600    0.1525900
+48495.00   0.174560   0.525956   0.1509325    0.174100    0.525600    0.1506300
+48496.00   0.177137   0.523935   0.1489024    0.176600    0.523500    0.1486100
+48497.00   0.179726   0.521986   0.1468790    0.179200    0.521400    0.1466000
+48498.00   0.182299   0.520033   0.1449414    0.181800    0.519400    0.1446900
+48499.00   0.184849   0.518012   0.1431336    0.184500    0.517300    0.1429000
+48500.00   0.187398   0.515918   0.1414584    0.187300    0.515200    0.1412500
+48501.00   0.189978   0.513764   0.1398743    0.190100    0.513100    0.1396800
+48502.00   0.192616   0.511566   0.1383010    0.192900    0.510900    0.1381200
+48503.00   0.195337   0.509352   0.1366345    0.195700    0.508800    0.1364500
+48504.00   0.198209   0.507148   0.1347696    0.198400    0.506600    0.1345800
+48505.00   0.201270   0.504976   0.1326407    0.201400    0.504300    0.1324500
+48506.00   0.204388   0.502847   0.1302495    0.204100    0.502100    0.1300500
+48507.00   0.207393   0.500743   0.1276691    0.206700    0.499800    0.1274400
+48508.00   0.210161   0.498606   0.1250076    0.209100    0.497500    0.1247500
+48509.00   0.212566   0.496400   0.1223822    0.211300    0.495200    0.1221100
+48510.00   0.214480   0.494106   0.1198897    0.213300    0.492800    0.1196300
+48511.00   0.215997   0.491708   0.1175991    0.215100    0.490300    0.1173700
+48512.00   0.217312   0.489190   0.1155326    0.216900    0.487800    0.1153500
+48513.00   0.218598   0.486539   0.1136722    0.218600    0.485200    0.1135500
+48514.00   0.219935   0.483763   0.1119721    0.220200    0.482500    0.1119000
+48515.00   0.221414   0.480892   0.1103748    0.221900    0.479800    0.1103500
+48516.00   0.223135   0.477963   0.1088260    0.223700    0.477000    0.1088200
+48517.00   0.225137   0.475008   0.1072693    0.225500    0.474200    0.1072500
+48518.00   0.227290   0.472047   0.1056320    0.227400    0.471300    0.1055800
+48519.00   0.229438   0.469103   0.1038529    0.229400    0.468400    0.1037400
+48520.00   0.231466   0.466196   0.1018945    0.231400    0.465600    0.1017200
+48521.00   0.233424   0.463342   0.0997466    0.233500    0.462700    0.0995100
+48522.00   0.235406   0.460554   0.0974257    0.235600    0.459800    0.0971300
+48523.00   0.237502   0.457846   0.0949725    0.237800    0.456900    0.0946600
+48524.00   0.239748   0.455199   0.0924587    0.239800    0.454000    0.0921500
+48525.00   0.242034   0.452492   0.0899884    0.241800    0.451000    0.0897100
+48526.00   0.244219   0.449613   0.0876444    0.243600    0.448100    0.0874100
+48527.00   0.246208   0.446617   0.0854745    0.245300    0.445000    0.0852900
+48528.00   0.247973   0.443544   0.0834657    0.246900    0.442000    0.0833200
+48529.00   0.249478   0.440410   0.0815602    0.248300    0.438900    0.0814500
+48530.00   0.250664   0.437218   0.0796781    0.249500    0.435700    0.0795600
+48531.00   0.251533   0.433958   0.0777307    0.250700    0.432500    0.0775900
+48532.00   0.252261   0.430611   0.0756290    0.251700    0.429200    0.0754400
+48533.00   0.253026   0.427190   0.0733329    0.252700    0.425900    0.0731100
+48534.00   0.253919   0.423732   0.0708653    0.253500    0.422600    0.0706100
+48535.00   0.254908   0.420246   0.0683008    0.254600    0.419100    0.0680400
+48536.00   0.255934   0.416738   0.0657398    0.255600    0.415600    0.0655000
+48537.00   0.256938   0.413216   0.0632768    0.256600    0.412100    0.0630400
+48538.00   0.257857   0.409694   0.0609769    0.257500    0.408700    0.0607800
+48539.00   0.258653   0.406205   0.0588820    0.258400    0.405300    0.0587400
+48540.00   0.259307   0.402771   0.0569980    0.259000    0.401800    0.0569100
+48541.00   0.259816   0.399401   0.0552983    0.259600    0.398400    0.0552600
+48542.00   0.260215   0.396078   0.0537336    0.260100    0.395000    0.0537300
+48543.00   0.260541   0.392784   0.0522502    0.260600    0.391600    0.0522600
+48544.00   0.260834   0.389497   0.0508005    0.260900    0.388200    0.0507900
+48545.00   0.261131   0.386204   0.0493325    0.261200    0.384800    0.0492500
+48546.00   0.261471   0.382917   0.0477694    0.261400    0.381500    0.0476200
+48547.00   0.261748   0.379673   0.0460491    0.261500    0.378100    0.0458400
+48548.00   0.261878   0.376475   0.0441486    0.261500    0.374800    0.0439100
+48549.00   0.261867   0.373298   0.0420742    0.261400    0.371400    0.0418300
+48550.00   0.261762   0.370065   0.0398639    0.261200    0.368100    0.0396300
+48551.00   0.261585   0.366705   0.0375816    0.261000    0.364800    0.0373800
+48552.00   0.261298   0.363280   0.0353079    0.260700    0.361500    0.0351300
+48553.00   0.260952   0.359926   0.0331125    0.260400    0.358200    0.0329600
+48554.00   0.260565   0.356662   0.0310547    0.260100    0.355000    0.0309200
+48555.00   0.260157   0.353463   0.0291278    0.259800    0.351800    0.0289900
+48556.00   0.259771   0.350310   0.0272670    0.259700    0.348700    0.0271200
+48557.00   0.259457   0.347183   0.0253742    0.259700    0.345600    0.0252200
+48558.00   0.259269   0.344065   0.0233532    0.259900    0.342600    0.0231900
+48559.00   0.259278   0.340934   0.0211389    0.260100    0.339600    0.0209600
+48560.00   0.259575   0.337805   0.0187158    0.260400    0.336700    0.0185000
+48561.00   0.260184   0.334747   0.0160973    0.260800    0.333800    0.0158700
+48562.00   0.261067   0.331831   0.0133398    0.261100    0.330900    0.0131100
+48563.00   0.262021   0.329086   0.0105434    0.261500    0.328100    0.0103500
+48564.00   0.262838   0.326446   0.0078236    0.261800    0.325200    0.0076700
+48565.00   0.263316   0.323824   0.0052780    0.262300    0.322400    0.0051600
+48566.00   0.263351   0.321155   0.0029617    0.262400    0.319700    0.0028700
+48567.00   0.263058   0.318417   0.0008641    0.262400    0.317000    0.0007600
+48568.00   0.262580   0.315600  -0.0010654    0.262100    0.314300   -0.0011900
+48569.00   0.262019   0.312716  -0.0028975    0.261800    0.311400   -0.0030400
+48570.00   0.261375   0.309796  -0.0047002    0.261200    0.308500   -0.0048500
+48571.00   0.260618   0.306885  -0.0065338    0.260500    0.305600   -0.0066900
+48572.00   0.259733   0.303992  -0.0084483    0.259700    0.302600   -0.0086100
+48573.00   0.258711   0.301113  -0.0104806    0.258800    0.299700   -0.0106500
+48574.00   0.257557   0.298217  -0.0126521    0.257800    0.296700   -0.0128400
+48575.00   0.256324   0.295259  -0.0149750    0.256800    0.293800   -0.0151700
+48576.00   0.255081   0.292194  -0.0174429    0.255700    0.290800   -0.0176400
+48577.00   0.253909   0.289017  -0.0200270    0.254700    0.287800   -0.0202100
+48578.00   0.252920   0.285821  -0.0226646    0.253700    0.284700   -0.0228200
+48579.00   0.252234   0.282720  -0.0252746    0.252700    0.281700   -0.0253900
+48580.00   0.251872   0.279772  -0.0277784    0.251800    0.278600   -0.0278700
+48581.00   0.251571   0.276910  -0.0301254    0.250900    0.275500   -0.0302000
+48582.00   0.251059   0.274132  -0.0322901    0.250100    0.272400   -0.0323700
+48583.00   0.250193   0.271398  -0.0343196    0.249200    0.269300   -0.0344300
+48584.00   0.249052   0.268621  -0.0363251    0.248400    0.266200   -0.0364800
+48585.00   0.247766   0.265706  -0.0384389    0.247500    0.263100   -0.0386300
+48586.00   0.246464   0.262558  -0.0407691    0.246700    0.260000   -0.0409800
+48587.00   0.245276   0.259134  -0.0433617    0.246000    0.256900   -0.0435600
+48588.00   0.244270   0.255563  -0.0461708    0.245200    0.253800   -0.0463600
+48589.00   0.243445   0.251970  -0.0491002    0.244400    0.250800   -0.0492700
+48590.00   0.242748   0.248441  -0.0520428    0.243600    0.247800   -0.0522000
+48591.00   0.242116   0.245061  -0.0548992    0.242700    0.244800   -0.0550500
+48592.00   0.241483   0.241909  -0.0575943    0.241600    0.241900   -0.0577600
+48593.00   0.240748   0.239028  -0.0600857    0.240500    0.239100   -0.0602600
+48594.00   0.239785   0.236379  -0.0623717    0.239100    0.236300   -0.0625700
+48595.00   0.238404   0.233887  -0.0644791    0.237500    0.233600   -0.0646900
+48596.00   0.236477   0.231434  -0.0664603    0.235800    0.230800   -0.0666800
+48597.00   0.234014   0.228899  -0.0683727    0.233800    0.228000   -0.0685900
+48598.00   0.231202   0.226236  -0.0702803    0.231700    0.225200   -0.0704900
+48599.00   0.228292   0.223448  -0.0722415    0.229500    0.222400   -0.0724600
+48600.00   0.225542   0.220545  -0.0743029    0.227200    0.219600   -0.0745200
+48601.00   0.223142   0.217573  -0.0764947    0.224800    0.216800   -0.0767200
+48602.00   0.221062   0.214672  -0.0788245    0.222400    0.214100   -0.0790600
+48603.00   0.219187   0.211939  -0.0812771    0.220000    0.211400   -0.0815300
+48604.00   0.217366   0.209355  -0.0838232    0.217900    0.208800   -0.0841000
+48605.00   0.215521   0.206838  -0.0864216    0.215700    0.206200   -0.0867100
+48606.00   0.213630   0.204303  -0.0890221    0.213700    0.203800   -0.0893100
+48607.00   0.211809   0.201740  -0.0915598    0.211700    0.201400   -0.0918400
+48608.00   0.210191   0.199201  -0.0939742    0.209800    0.199100   -0.0942200
+48609.00   0.208711   0.196874  -0.0962138    0.207900    0.196800   -0.0964200
+48610.00   0.207235   0.194795  -0.0982561    0.206000    0.194600   -0.0984300
+48611.00   0.205507   0.192814  -0.1001693    0.204100    0.192400   -0.1003400
+48612.00   0.203369   0.190776  -0.1020840    0.202200    0.190200   -0.1022700
+48613.00   0.200948   0.188654  -0.1041185    0.200300    0.187900   -0.1043100
+48614.00   0.198454   0.186472  -0.1063444    0.198300    0.185700   -0.1065500
+48615.00   0.196072   0.184253  -0.1087701    0.196300    0.183400   -0.1089900
+48616.00   0.193885   0.182012  -0.1113399    0.194400    0.181200   -0.1115800
+48617.00   0.191929   0.179752  -0.1139584    0.192400    0.178900   -0.1142000
+48618.00   0.190212   0.177484  -0.1165251    0.190400    0.176600   -0.1167500
+48619.00   0.188613   0.175240  -0.1189540    0.188400    0.174400   -0.1191500
+48620.00   0.186972   0.173043  -0.1211998    0.186500    0.172200   -0.1213700
+48621.00   0.185126   0.170911  -0.1232581    0.184500    0.170000   -0.1234200
+48622.00   0.182965   0.168856  -0.1251559    0.182400    0.167900   -0.1253000
+48623.00   0.180592   0.166857  -0.1269451    0.180300    0.165900   -0.1270800
+48624.00   0.178161   0.164885  -0.1286896    0.178200    0.163900   -0.1288200
+48625.00   0.175799   0.162915  -0.1304568    0.175700    0.161600   -0.1305800
+48626.00   0.173506   0.160942  -0.1323066    0.173500    0.159600   -0.1324300
+48627.00   0.171249   0.158962  -0.1342857    0.171100    0.157800   -0.1344200
+48628.00   0.168993   0.156979  -0.1364224    0.168700    0.156000   -0.1365600
+48629.00   0.166663   0.155043  -0.1387244    0.166200    0.154200   -0.1388600
+48630.00   0.164123   0.153294  -0.1411761    0.163600    0.152600   -0.1413200
+48631.00   0.161402   0.151714  -0.1437490    0.160900    0.151000   -0.1439000
+48632.00   0.158585   0.150245  -0.1464011    0.158100    0.149500   -0.1465400
+48633.00   0.155692   0.148849  -0.1490736    0.155300    0.148100   -0.1491800
+48634.00   0.152741   0.147494  -0.1517024    0.152300    0.146700   -0.1518000
+48635.00   0.149763   0.146138  -0.1542271    0.149300    0.145400   -0.1543000
+48636.00   0.146774   0.144772  -0.1566078    0.146200    0.144200   -0.1566900
+48637.00   0.143765   0.143469  -0.1588545    0.143100    0.142900   -0.1589500
+48638.00   0.140654   0.142244  -0.1610204    0.139900    0.141700   -0.1611400
+48639.00   0.137345   0.141095  -0.1632032    0.136700    0.140500   -0.1633600
+48640.00   0.133813   0.139997  -0.1655276    0.133500    0.139300   -0.1657200
+48641.00   0.130220   0.138870  -0.1680973    0.130300    0.138000   -0.1683300
+48642.00   0.126773   0.137616  -0.1709548    0.127200    0.136700   -0.1712100
+48643.00   0.123633   0.136222  -0.1740492    0.124100    0.135400   -0.1742900
+48644.00   0.120838   0.134840  -0.1772390    0.121000    0.134000   -0.1774700
+48645.00   0.118239   0.133514  -0.1803757    0.118000    0.132700   -0.1805900
+48646.00   0.115660   0.132246  -0.1833389    0.115100    0.131300   -0.1835400
+48647.00   0.113031   0.131007  -0.1860675    0.112300    0.129900   -0.1862800
+48648.00   0.110308   0.129756  -0.1885557    0.109500    0.128600   -0.1887700
+48649.00   0.107447   0.128450  -0.1908372    0.106800    0.127300   -0.1910600
+48650.00   0.104483   0.127120  -0.1929756    0.104200    0.126100   -0.1932200
+48651.00   0.101609   0.125938  -0.1950580    0.101600    0.125000   -0.1953000
+48652.00   0.098935   0.124947  -0.1971464    0.099100    0.123900   -0.1973700
+48653.00   0.096518   0.124143  -0.1992921    0.096700    0.123000   -0.1994900
+48654.00   0.094302   0.123506  -0.2015317    0.094300    0.122200   -0.2017000
+48655.00   0.092183   0.123008  -0.2038871    0.091700    0.121400   -0.2040200
+48656.00   0.090027   0.122615  -0.2063606    0.089300    0.120700   -0.2065100
+48657.00   0.087698   0.122252  -0.2089429    0.086900    0.120100   -0.2091300
+48658.00   0.085120   0.121749  -0.2116347    0.084500    0.119600   -0.2118600
+48659.00   0.082425   0.121095  -0.2143996    0.082000    0.119100   -0.2146100
+48660.00   0.079747   0.120336  -0.2171790    0.079400    0.118600   -0.2173600
+48661.00   0.077048   0.119538  -0.2198943    0.076700    0.118200   -0.2200300
+48662.00   0.074254   0.118767  -0.2224826    0.073900    0.117700   -0.2225800
+48663.00   0.071296   0.118085  -0.2249019    0.071100    0.117300   -0.2249800
+48664.00   0.068126   0.117511  -0.2271426    0.068100    0.116900   -0.2272000
+48665.00   0.064769   0.117034  -0.2292410    0.065100    0.116600   -0.2293000
+48666.00   0.061346   0.116670  -0.2312755    0.062000    0.116300   -0.2313500
+48667.00   0.057994   0.116441  -0.2333549    0.058900    0.116100   -0.2334600
+48668.00   0.054803   0.116362  -0.2355963    0.055800    0.115900   -0.2357400
+48669.00   0.051751   0.116424  -0.2380810    0.052600    0.115700   -0.2382600
+48670.00   0.048799   0.116604  -0.2408220    0.049500    0.115700   -0.2410200
+48671.00   0.045888   0.116854  -0.2437544    0.046300    0.115600   -0.2439800
+48672.00   0.042907   0.117120  -0.2467553    0.043000    0.115500   -0.2469800
+48673.00   0.039772   0.117341  -0.2496841    0.039800    0.115500   -0.2499100
+48674.00   0.036468   0.117447  -0.2524364    0.036500    0.115400   -0.2526400
+48675.00   0.033121   0.117372  -0.2549754    0.033200    0.115300   -0.2551400
+48676.00   0.029796   0.117134  -0.2573113    0.029900    0.115300   -0.2574300
+48677.00   0.026512   0.116772  -0.2594898    0.026600    0.115200   -0.2595300
+48678.00   0.023303   0.116361  -0.2615661    0.023200    0.115100   -0.2615300
+48679.00   0.020120   0.116041  -0.2635649    0.019800    0.115000   -0.2634800
+48680.00   0.016870   0.115871  -0.2655401    0.016400    0.115000   -0.2654600
+48681.00   0.013480   0.115880  -0.2675507    0.013000    0.115100   -0.2675100
+48682.00   0.009978   0.116087  -0.2696648    0.009500    0.115200   -0.2697000
+48683.00   0.006393   0.116477  -0.2719343    0.006000    0.115400   -0.2720500
+48684.00   0.002747   0.117026  -0.2743831    0.002500    0.115700   -0.2745600
+48685.00  -0.000960   0.117699  -0.2770061   -0.000900    0.116200   -0.2772200
+48686.00  -0.004773   0.118416  -0.2797652   -0.004400    0.116700   -0.2799900
+48687.00  -0.008589   0.119105  -0.2825980   -0.008000    0.117300   -0.2828200
+48688.00  -0.012262   0.119710  -0.2854369   -0.011500    0.118000   -0.2856300
+48689.00  -0.015689   0.120230  -0.2882203   -0.015000    0.118800   -0.2883600
+48690.00  -0.018919   0.120750  -0.2908904   -0.018400    0.119700   -0.2910000
+48691.00  -0.022035   0.121373  -0.2934116   -0.021900    0.120700   -0.2935400
+48692.00  -0.025117   0.122197  -0.2958109   -0.025300    0.121800   -0.2959800
+48693.00  -0.028234   0.123272  -0.2982033   -0.028700    0.123000   -0.2984000
+48694.00  -0.031378   0.124589  -0.3006878   -0.032100    0.124300   -0.3008900
+48695.00  -0.034530   0.126117  -0.3033568   -0.035400    0.125700   -0.3035600
+48696.00  -0.037731   0.127806  -0.3062763   -0.038700    0.127300   -0.3064700
+48697.00  -0.040970   0.129629  -0.3094750   -0.042000    0.128900   -0.3096600
+48698.00  -0.044230   0.131570  -0.3129125   -0.045300    0.130600   -0.3130800
+48699.00  -0.047505   0.133592  -0.3164823   -0.048500    0.132400   -0.3166600
+48700.00  -0.050798   0.135598  -0.3200428   -0.051600    0.134300   -0.3202100
+48701.00  -0.054100   0.137506  -0.3234626   -0.054600    0.136300   -0.3236400
+48702.00  -0.057350   0.139342  -0.3266588   -0.057600    0.138300   -0.3268300
+48703.00  -0.060446   0.141156  -0.3296001   -0.060400    0.140300   -0.3297700
+48704.00  -0.063319   0.143002  -0.3323193   -0.063100    0.142400   -0.3324800
+48705.00  -0.065917   0.144942  -0.3348843   -0.065700    0.144600   -0.3350400
+48706.00  -0.068216   0.147021  -0.3373685   -0.068200    0.146800   -0.3375000
+48707.00  -0.070314   0.149236  -0.3398287   -0.070600    0.149000   -0.3399300
+48708.00  -0.072339   0.151570  -0.3423103   -0.072800    0.151200   -0.3424000
+48709.00  -0.074391   0.153996  -0.3448509   -0.074900    0.153400   -0.3449700
+48710.00  -0.076467   0.156449  -0.3474917   -0.077000    0.155600   -0.3476400
+48711.00  -0.078543   0.158854  -0.3502571   -0.079000    0.157800   -0.3504400
+48712.00  -0.080594   0.161135  -0.3531461   -0.080900    0.159900   -0.3533400
+48713.00  -0.082596   0.163229  -0.3561338   -0.082800    0.161900   -0.3563400
+48714.00  -0.084556   0.165149  -0.3591684   -0.084600    0.163900   -0.3593600
+48715.00  -0.086492   0.166920  -0.3621836   -0.086500    0.165900   -0.3623500
+48716.00  -0.088401   0.168583  -0.3651128   -0.088200    0.167700   -0.3652700
+48717.00  -0.090251   0.170205  -0.3679050   -0.090000    0.169600   -0.3680400
+48718.00  -0.092014   0.171858  -0.3705355   -0.091800    0.171500   -0.3706600
+48719.00  -0.093706   0.173611  -0.3730152   -0.093500    0.173400   -0.3731300
+48720.00  -0.095351   0.175516  -0.3753975   -0.095300    0.175300   -0.3755200
+48721.00  -0.096976   0.177569  -0.3777761   -0.097000    0.177300   -0.3779200
+48722.00  -0.098597   0.179745  -0.3802556   -0.098800    0.179300   -0.3804300
+48723.00  -0.100207   0.182020  -0.3829267   -0.100600    0.181500   -0.3831500
+48724.00  -0.101809   0.184383  -0.3858506   -0.102300    0.183800   -0.3861300
+48725.00  -0.103423   0.186819  -0.3890352   -0.104100    0.186100   -0.3893700
+48726.00  -0.105076   0.189311  -0.3924319   -0.105900    0.188500   -0.3927900
+48727.00  -0.106793   0.191842  -0.3959418   -0.107700    0.190800   -0.3962800
+48728.00  -0.108599   0.194378  -0.3994216   -0.109500    0.193200   -0.3997000
+48729.00  -0.110484   0.196883  -0.4027295   -0.111300    0.195700   -0.4029400
+48730.00  -0.112402   0.199323  -0.4057833   -0.113100    0.198100   -0.4059300
+48731.00  -0.114309   0.201692  -0.4085627   -0.114800    0.200500   -0.4086600
+48732.00  -0.116160   0.203983  -0.4110990   -0.116500    0.202800   -0.4111700
+48733.00  -0.117913   0.206196  -0.4134536   -0.118100    0.205200   -0.4135300
+48734.00  -0.119538   0.208379  -0.4157038   -0.119700    0.207600   -0.4158200
+48735.00  -0.121032   0.210673  -0.4179446   -0.121300    0.209900   -0.4181000
+48736.00  -0.122480   0.213058  -0.4202420   -0.122800    0.212300   -0.4204300
+48737.00  -0.123975   0.215479  -0.4226378   -0.124300    0.214700   -0.4228500
+48738.00  -0.125501   0.217910  -0.4251565   -0.125900    0.217100   -0.4253900
+48739.00  -0.127017   0.220342  -0.4277973   -0.127400    0.219400   -0.4280300
+48740.00  -0.128494   0.222791  -0.4305358   -0.129000    0.221800   -0.4307400
+48741.00  -0.129927   0.225263  -0.4333245   -0.130500    0.224100   -0.4335000
+48742.00  -0.131420   0.227741  -0.4360890   -0.132100    0.226500   -0.4362300
+48743.00  -0.133096   0.230149  -0.4387615   -0.133800    0.228800   -0.4388700
+48744.00  -0.135028   0.232420  -0.4412846   -0.135400    0.231100   -0.4413800
+48745.00  -0.137123   0.234572  -0.4436273   -0.137200    0.233300   -0.4437200
+48746.00  -0.139228   0.236665  -0.4457947   -0.138800    0.235700   -0.4458900
+48747.00  -0.141185   0.238758  -0.4478320   -0.140500    0.238100   -0.4479500
+48748.00  -0.142849   0.240919  -0.4498196   -0.142000    0.240500   -0.4499600
+48749.00  -0.144143   0.243238  -0.4518569   -0.143500    0.243000   -0.4520300
+48750.00  -0.145138   0.245766  -0.4540441   -0.144900    0.245500   -0.4542500
+48751.00  -0.145941   0.248520  -0.4564424   -0.146200    0.248000   -0.4566700
+48752.00  -0.146679   0.251420  -0.4590478   -0.147400    0.250600   -0.4592700
+48753.00  -0.147478   0.254362  -0.4618024   -0.148400    0.253100   -0.4620200
+48754.00  -0.148460   0.257247  -0.4646084   -0.149400    0.255700   -0.4648100
+48755.00  -0.149655   0.259981  -0.4673633   -0.150200    0.258200   -0.4675800
+48756.00  -0.150888   0.262487  -0.4700007   -0.151000    0.260700   -0.4702000
+48757.00  -0.151944   0.264796  -0.4724541   -0.151600    0.263100   -0.4726300
+48758.00  -0.152674   0.266971  -0.4746929   -0.152200    0.265600   -0.4748600
+48759.00  -0.153100   0.269090  -0.4767418   -0.152800    0.268000   -0.4769000
+48760.00  -0.153324   0.271238  -0.4786626   -0.153300    0.270400   -0.4788200
+48761.00  -0.153492   0.273491  -0.4805333   -0.153700    0.272800   -0.4806900
+48762.00  -0.153702   0.275896  -0.4824287   -0.154200    0.275200   -0.4825800
+48763.00  -0.153937   0.278419  -0.4843968   -0.154600    0.277600   -0.4845400
+48764.00  -0.154207   0.281007  -0.4864569   -0.155000    0.280000   -0.4865900
+48765.00  -0.154584   0.283623  -0.4886033   -0.155400    0.282400   -0.4887500
+48766.00  -0.155104   0.286252  -0.4908421   -0.155900    0.284700   -0.4910000
+48767.00  -0.155760   0.288822  -0.4931589   -0.156300    0.287100   -0.4933200
+48768.00  -0.156535   0.291241  -0.4955207   -0.156600    0.289500   -0.4956700
+48769.00  -0.157366   0.293461  -0.4978772   -0.157000    0.291800   -0.4980000
+48770.00  -0.158048   0.295568  -0.5001556   -0.157300    0.294200   -0.5002600
+48771.00  -0.158364   0.297671  -0.5022890   -0.157500    0.296500   -0.5023900
+48772.00  -0.158224   0.299872  -0.5042519   -0.157600    0.298900   -0.5043500
+48773.00  -0.157794   0.302214  -0.5060354   -0.157700    0.301200   -0.5061400
+48774.00  -0.157251   0.304723  -0.5076563   -0.157600    0.303500   -0.5077700
+48775.00  -0.156693   0.307325  -0.5091858   -0.157400    0.306000   -0.5093000
+48776.00  -0.156213   0.309905  -0.5107243   -0.157200    0.308800   -0.5108600
+48777.00  -0.155865   0.312371  -0.5123786   -0.156900    0.311500   -0.5125300
+48778.00  -0.155629   0.314694  -0.5142194   -0.156600    0.314100   -0.5143800
+48779.00  -0.155420   0.316894  -0.5162500   -0.156300    0.316300   -0.5164300
+48780.00  -0.155182   0.319021  -0.5184166   -0.156000    0.318300   -0.5186000
+48781.00  -0.154897   0.321135  -0.5206263   -0.155500    0.320200   -0.5208200
+48782.00  -0.154597   0.323300  -0.5227758   -0.154800    0.322200   -0.5229400
+48783.00  -0.154299   0.325574  -0.5247841   -0.154200    0.324500   -0.5249100
+48784.00  -0.153976   0.327985  -0.5266096   -0.153700    0.327100   -0.5266900
+48785.00  -0.153631   0.330557  -0.5282452   -0.153500    0.330000   -0.5282800
+48786.00  -0.153301   0.333315  -0.5297193   -0.153400    0.333000   -0.5297300
+48787.00  -0.153029   0.336272  -0.5310856   -0.153200    0.336000   -0.5311000
+48788.00  -0.152876   0.339376  -0.5324041   -0.153000    0.339100   -0.5324500
+48789.00  -0.152878   0.342509  -0.5337292   -0.152900    0.342000   -0.5338200
+48790.00  -0.153016   0.345557  -0.5351059   -0.153100    0.344800   -0.5352300
+48791.00  -0.153169   0.348440  -0.5365664   -0.153300    0.347400   -0.5367200
+48792.00  -0.153196   0.351097  -0.5381314   -0.153400    0.349900   -0.5383100
+48793.00  -0.152947   0.353491  -0.5398079   -0.153300    0.352200   -0.5400000
+48794.00  -0.152328   0.355688  -0.5415918   -0.153000    0.354400   -0.5417800
+48795.00  -0.151443   0.357814  -0.5434488   -0.152200    0.356600   -0.5436200
+48796.00  -0.150489   0.359987  -0.5453287   -0.151200    0.358800   -0.5454800
+48797.00  -0.149658   0.362268  -0.5471758   -0.150200    0.361000   -0.5473200
+48798.00  -0.148926   0.364635  -0.5489337   -0.149200    0.363400   -0.5490800
+48799.00  -0.148103   0.367069  -0.5505592   -0.148000    0.365800   -0.5507200
+48800.00  -0.147055   0.369542  -0.5520272   -0.146800    0.368400   -0.5521800
+48801.00  -0.145800   0.372031  -0.5533401   -0.145700    0.371000   -0.5535000
+48802.00  -0.144519   0.374531  -0.5545415   -0.144700    0.373500   -0.5547000
+48803.00  -0.143342   0.377023  -0.5557122   -0.143600    0.375900   -0.5558700
+48804.00  -0.142256   0.379477   0.4430472   -0.142500    0.378200    0.4429100
+48805.00  -0.141189   0.381860   0.4416753   -0.141300    0.380500    0.4415300
+48806.00  -0.140045   0.384172   0.4401262   -0.140200    0.382700    0.4399700
+48807.00  -0.138748   0.386425   0.4384135   -0.139200    0.384900    0.4382400
+48808.00  -0.137348   0.388614   0.4366213   -0.138000    0.387100    0.4364400
+48809.00  -0.135865   0.390743   0.4348628   -0.136700    0.389400    0.4346700
+48810.00  -0.134255   0.392840   0.4332363   -0.135000    0.391500    0.4330500
+48811.00  -0.132501   0.394965   0.4317956   -0.133200    0.393700    0.4316200
+48812.00  -0.130806   0.397220   0.4305555   -0.131400    0.396100    0.4304000
+48813.00  -0.129424   0.399651   0.4294993   -0.129900    0.398800    0.4293600
+48814.00  -0.128330   0.402171   0.4285694   -0.128700    0.401700    0.4284400
+48815.00  -0.127463   0.404642   0.4277093   -0.128000    0.404500    0.4275900
+48816.00  -0.126732   0.406920   0.4268671   -0.127500    0.406800    0.4267600
+48817.00  -0.126065   0.408912   0.4259970   -0.127000    0.408600    0.4258900
+48818.00  -0.125322   0.410633   0.4250476   -0.126200    0.410100    0.4249200
+48819.00  -0.124191   0.412277   0.4239735   -0.124900    0.411600    0.4238400
+48820.00  -0.122472   0.414066   0.4227595   -0.123100    0.413300    0.4226200
+48821.00  -0.120237   0.416097   0.4214188   -0.120700    0.415400    0.4212700
+48822.00  -0.117661   0.418425   0.4199735   -0.118100    0.417800    0.4198100
+48823.00  -0.114962   0.420939   0.4184560   -0.115400    0.420400    0.4182900
+48824.00  -0.112339   0.423477   0.4169068   -0.112800    0.422900    0.4167400
+48825.00  -0.110008   0.425890   0.4153668   -0.110400    0.425200    0.4152100
+48826.00  -0.108033   0.428109   0.4138765   -0.108200    0.427400    0.4137200
+48827.00  -0.106077   0.430216   0.4124657   -0.106000    0.429500    0.4122900
+48828.00  -0.103972   0.432230   0.4111448   -0.104000    0.431500    0.4109600
+48829.00  -0.101805   0.434165   0.4098930   -0.102100    0.433400    0.4097000
+48830.00  -0.099751   0.436034   0.4086562   -0.100000    0.435300    0.4084800
+48831.00  -0.097900   0.437831   0.4073690   -0.098000    0.437200    0.4072100
+48832.00  -0.096176   0.439589   0.4059513   -0.096100    0.439000    0.4058000
+48833.00  -0.094340   0.441446   0.4043197   -0.094300    0.440800    0.4041700
+48834.00  -0.092328   0.443549   0.4024494   -0.092500    0.442700    0.4023000
+48835.00  -0.090223   0.445839   0.4003910   -0.090400    0.444900    0.4002500
+48836.00  -0.088082   0.448188   0.3982501   -0.088300    0.447300    0.3981000
+48837.00  -0.085895   0.450460   0.3961543   -0.086200    0.449700    0.3959900
+48838.00  -0.083755   0.452563   0.3942075   -0.084100    0.451900    0.3940300
+48839.00  -0.081828   0.454576   0.3924681   -0.082200    0.454100    0.3922700
+48840.00  -0.080003   0.456571   0.3909099   -0.080400    0.456100    0.3907400
+48841.00  -0.078139   0.458634   0.3895165   -0.078500    0.457900    0.3893600
+48842.00  -0.076208   0.460875   0.3882434   -0.076500    0.459800    0.3881300
+48843.00  -0.074290   0.463332   0.3870412   -0.074400    0.462000    0.3869300
+48844.00  -0.072315   0.465924   0.3858530   -0.072500    0.464700    0.3857400
+48845.00  -0.070241   0.468541   0.3846145   -0.070700    0.467200    0.3844400
+48846.00  -0.068261   0.471062   0.3832183   -0.068900    0.469700    0.3830000
+48847.00  -0.066397   0.473325   0.3816262   -0.067100    0.471800    0.3814100
+48848.00  -0.064466   0.475307   0.3798709   -0.065100    0.473800    0.3796700
+48849.00  -0.062333   0.477088   0.3780113   -0.063000    0.475900    0.3778100
+48850.00  -0.060005   0.478776   0.3761010   -0.060700    0.477800    0.3759100
+48851.00  -0.057474   0.480454   0.3741898   -0.058100    0.479700    0.3740100
+48852.00  -0.054730   0.482120   0.3723218   -0.055400    0.481500    0.3721400
+48853.00  -0.051810   0.483699   0.3705234   -0.052500    0.483100    0.3703400
+48854.00  -0.048783   0.485162   0.3687999   -0.049300    0.484600    0.3686300
+48855.00  -0.045664   0.486491   0.3671538   -0.046100    0.485900    0.3669900
+48856.00  -0.042499   0.487683   0.3655657   -0.043100    0.487100    0.3654300
+48857.00  -0.039354   0.488789   0.3639866   -0.040100    0.488200    0.3638700
+48858.00  -0.036252   0.489888   0.3623445   -0.037200    0.489200    0.3622400
+48859.00  -0.033146   0.491085   0.3605595   -0.034200    0.490300    0.3604500
+48860.00  -0.030036   0.492464   0.3585519   -0.031000    0.491600    0.3584100
+48861.00  -0.026957   0.493979   0.3562597   -0.027700    0.493200    0.3560900
+48862.00  -0.023903   0.495593   0.3536961   -0.024400    0.494900    0.3535100
+48863.00  -0.020858   0.497302   0.3509702   -0.021300    0.496700    0.3507800
+48864.00  -0.017903   0.499041   0.3482488   -0.018300    0.498500    0.3480800
+48865.00  -0.015192   0.500593   0.3457035   -0.015600    0.499900    0.3455300
+48866.00  -0.012886   0.501710   0.3434557   -0.013200    0.500900    0.3432800
+48867.00  -0.010966   0.502220   0.3415326   -0.011000    0.501200    0.3413000
+48868.00  -0.008939   0.502278   0.3398721   -0.008900    0.501100    0.3396100
+48869.00  -0.006749   0.502235   0.3383956   -0.006700    0.500900    0.3381600
+48870.00  -0.004454   0.502390   0.3370185   -0.004100    0.501200    0.3368300
+48871.00  -0.002028   0.502831   0.3356478   -0.001500    0.501600    0.3355000
+48872.00   0.000513   0.503509   0.3342017    0.001000    0.502500    0.3340600
+48873.00   0.003059   0.504279   0.3326178    0.003300    0.503500    0.3324700
+48874.00   0.005476   0.504990   0.3308625    0.005600    0.504400    0.3307100
+48875.00   0.007814   0.505575   0.3289492    0.008000    0.505100    0.3287900
+48876.00   0.010098   0.506007   0.3269116    0.010500    0.505500    0.3267400
+48877.00   0.012363   0.506266   0.3247966    0.012800    0.505700    0.3246300
+48878.00   0.014709   0.506388   0.3226585    0.015100    0.505700    0.3225000
+48879.00   0.017122   0.506485   0.3205567    0.017300    0.505700    0.3204100
+48880.00   0.019557   0.506712   0.3185458    0.019400    0.505700    0.3184100
+48881.00   0.022043   0.507108   0.3166597    0.021800    0.506100    0.3165100
+48882.00   0.024591   0.507480   0.3148939    0.024400    0.506500    0.3147400
+48883.00   0.027139   0.507675   0.3132183    0.027200    0.506800    0.3130700
+48884.00   0.029623   0.507617   0.3115916    0.029900    0.506900    0.3114600
+48885.00   0.032003   0.507391   0.3099566    0.032400    0.506700    0.3098500
+48886.00   0.034238   0.507135   0.3082435    0.034600    0.506500    0.3081500
+48887.00   0.036273   0.507005   0.3063870    0.036500    0.506300    0.3062800
+48888.00   0.038147   0.507048   0.3043250    0.038300    0.506100    0.3041800
+48889.00   0.040098   0.507093   0.3020052    0.040200    0.506100    0.3018300
+48890.00   0.042174   0.507081   0.2994677    0.042200    0.506100    0.2992800
+48891.00   0.044339   0.506983   0.2968178    0.044500    0.506200    0.2966300
+48892.00   0.046531   0.506715   0.2941925    0.046600    0.505900    0.2940000
+48893.00   0.048707   0.506217   0.2917083    0.048700    0.505500    0.2915200
+48894.00   0.050938   0.505528   0.2894253    0.050800    0.504800    0.2892500
+48895.00   0.053292   0.504791   0.2873458    0.053000    0.504000    0.2872000
+48896.00   0.055692   0.504145   0.2854308    0.055400    0.503200    0.2853100
+48897.00   0.058118   0.503680   0.2836186    0.057800    0.502500    0.2835100
+48898.00   0.060608   0.503410   0.2818446    0.060400    0.502300    0.2817300
+48899.00   0.063234   0.503229   0.2800370    0.063100    0.502100    0.2799300
+48900.00   0.066052   0.503051   0.2781375    0.066200    0.501900    0.2780900
+48901.00   0.069055   0.502853   0.2761060    0.069200    0.501900    0.2760600
+48902.00   0.072135   0.502609   0.2739224    0.072300    0.501800    0.2738600
+48903.00   0.075164   0.502249   0.2715976    0.075300    0.501600    0.2715000
+48904.00   0.078011   0.501719   0.2691643    0.078100    0.501100    0.2690300
+48905.00   0.080592   0.500975   0.2666714    0.080600    0.500400    0.2665000
+48906.00   0.082938   0.500005   0.2641812    0.082800    0.499300    0.2640000
+48907.00   0.085182   0.498864   0.2617535    0.085100    0.498100    0.2615900
+48908.00   0.087534   0.497713   0.2594361    0.087500    0.496700    0.2592900
+48909.00   0.090137   0.496752   0.2572455    0.090200    0.495600    0.2571100
+48910.00   0.092869   0.496097   0.2551454    0.093100    0.495000    0.2550400
+48911.00   0.095524   0.495655   0.2531016    0.095800    0.494800    0.2530300
+48912.00   0.097959   0.495300   0.2510598    0.098300    0.494700    0.2509900
+48913.00   0.100177   0.494912   0.2489308    0.100400    0.494500    0.2488400
+48914.00   0.102242   0.494385   0.2466266    0.102200    0.493800    0.2465000
+48915.00   0.104231   0.493641   0.2440833    0.103900    0.492900    0.2439300
+48916.00   0.106221   0.492669   0.2412656    0.106000    0.491700    0.2410900
+48917.00   0.108349   0.491575   0.2381997    0.108200    0.490400    0.2380300
+48918.00   0.110632   0.490517   0.2350047    0.110700    0.489400    0.2348600
+48919.00   0.112984   0.489593   0.2318337    0.113300    0.488500    0.2317100
+48920.00   0.115243   0.488773   0.2288108    0.115600    0.487700    0.2286900
+48921.00   0.117297   0.487948   0.2260199    0.117500    0.487000    0.2259100
+48922.00   0.119065   0.486996   0.2234917    0.119000    0.486000    0.2233700
+48923.00   0.120544   0.485856   0.2211912    0.120200    0.484800    0.2210600
+48924.00   0.121936   0.484560   0.2190355    0.121400    0.483500    0.2188900
+48925.00   0.123457   0.483156   0.2169378    0.123000    0.482200    0.2167700
+48926.00   0.125267   0.481680   0.2148240    0.125000    0.480900    0.2146500
+48927.00   0.127408   0.480182   0.2126421    0.127400    0.479500    0.2124900
+48928.00   0.129736   0.478689   0.2103838    0.129900    0.478100    0.2102600
+48929.00   0.132018   0.477293   0.2080688    0.132200    0.476700    0.2079700
+48930.00   0.134034   0.476071   0.2057129    0.134000    0.475600    0.2056100
+48931.00   0.135730   0.474916   0.2032926    0.135500    0.474200    0.2031600
+48932.00   0.137164   0.473676   0.2007989    0.136900    0.473000    0.2006600
+48933.00   0.138539   0.472270   0.1982865    0.138100    0.471500    0.1981600
+48934.00   0.140013   0.470709   0.1958095    0.139500    0.470000    0.1956900
+48935.00   0.141637   0.469027   0.1934112    0.141100    0.468300    0.1932900
+48936.00   0.143375   0.467265   0.1911216    0.142900    0.466500    0.1909900
+48937.00   0.145215   0.465487   0.1889393    0.144900    0.464700    0.1887800
+48938.00   0.147211   0.463795   0.1868318    0.147000    0.463000    0.1866800
+48939.00   0.149308   0.462245   0.1847543    0.149200    0.461500    0.1846000
+48940.00   0.151459   0.460843   0.1826347    0.151300    0.460100    0.1824900
+48941.00   0.153688   0.459540   0.1803904    0.153500    0.458900    0.1802400
+48942.00   0.155993   0.458291   0.1779453    0.155800    0.457700    0.1778000
+48943.00   0.158277   0.457088   0.1752648    0.158200    0.456500    0.1751100
+48944.00   0.160480   0.455924   0.1723679    0.160600    0.455400    0.1722100
+48945.00   0.162663   0.454751   0.1693369    0.163000    0.454200    0.1691800
+48946.00   0.164781   0.453500   0.1662885    0.165200    0.452900    0.1661400
+48947.00   0.166706   0.452072   0.1633407    0.167200    0.451300    0.1632000
+48948.00   0.168352   0.450398   0.1605728    0.168700    0.449600    0.1604400
+48949.00   0.169592   0.448510   0.1580229    0.169600    0.447800    0.1578900
+48950.00   0.170409   0.446462   0.1556831    0.170200    0.445800    0.1555500
+48951.00   0.170909   0.444279   0.1535019    0.170600    0.443700    0.1533300
+48952.00   0.171329   0.441971   0.1514019    0.170900    0.441400    0.1512000
+48953.00   0.171871   0.439582   0.1493071    0.171400    0.439000    0.1490900
+48954.00   0.172586   0.437181   0.1471611    0.172000    0.436600    0.1469400
+48955.00   0.173419   0.434829   0.1449299    0.172700    0.434200    0.1447300
+48956.00   0.174288   0.432567   0.1425977    0.173500    0.432000    0.1424200
+48957.00   0.175124   0.430416   0.1401658    0.174400    0.429900    0.1400100
+48958.00   0.175909   0.428369   0.1376364    0.175600    0.427800    0.1374800
+48959.00   0.176811   0.426428   0.1350035    0.176900    0.425800    0.1348500
+48960.00   0.178006   0.424602   0.1323019    0.178200    0.424000    0.1321700
+48961.00   0.179261   0.422835   0.1296009    0.179500    0.422400    0.1294800
+48962.00   0.180442   0.421016   0.1269445    0.180600    0.420700    0.1268200
+48963.00   0.181507   0.419048   0.1243624    0.181600    0.418900    0.1242500
+48964.00   0.182424   0.416981   0.1218870    0.182400    0.416800    0.1217800
+48965.00   0.183234   0.414896   0.1195148    0.183300    0.414600    0.1194100
+48966.00   0.184075   0.412781   0.1171967    0.184100    0.412300    0.1170700
+48967.00   0.185114   0.410653   0.1148636    0.185100    0.410100    0.1147100
+48968.00   0.186391   0.408560   0.1124375    0.186200    0.407900    0.1122700
+48969.00   0.187772   0.406530   0.1098522    0.187400    0.405800    0.1096700
+48970.00   0.189068   0.404548   0.1070804    0.188600    0.403800    0.1069000
+48971.00   0.190156   0.402565   0.1041438    0.189800    0.401900    0.1039600
+48972.00   0.190995   0.400504   0.1011071    0.190800    0.400000    0.1009100
+48973.00   0.191741   0.398313   0.0980773    0.191700    0.397900    0.0978900
+48974.00   0.192464   0.395974   0.0951610    0.192700    0.395500    0.0949800
+48975.00   0.193139   0.393493   0.0924394    0.193500    0.393000    0.0922800
+48976.00   0.193720   0.390895   0.0899542    0.194200    0.390300    0.0898200
+48977.00   0.194234   0.388228   0.0877011    0.194800    0.387700    0.0875800
+48978.00   0.194827   0.385566   0.0856314    0.195500    0.385200    0.0855100
+48979.00   0.195638   0.382963   0.0836635    0.196100    0.382700    0.0835200
+48980.00   0.196651   0.380395   0.0817043    0.196700    0.380300    0.0815400
+48981.00   0.197901   0.377836   0.0796786    0.197700    0.377700    0.0794900
+48982.00   0.199439   0.375287   0.0775356    0.199200    0.375100    0.0773400
+48983.00   0.201252   0.372765   0.0752484    0.201000    0.372400    0.0750600
+48984.00   0.203210   0.370284   0.0728132    0.203100    0.369800    0.0726400
+48985.00   0.205074   0.367837   0.0702468    0.205100    0.367200    0.0701000
+48986.00   0.206599   0.365331   0.0675835    0.206700    0.364700    0.0674400
+48987.00   0.207631   0.362604   0.0648749    0.207800    0.362000    0.0647400
+48988.00   0.208194   0.359648   0.0621615    0.208500    0.359100    0.0620200
+48989.00   0.208431   0.356562   0.0594783    0.208700    0.355900    0.0593100
+48990.00   0.208526   0.353449   0.0568545    0.208800    0.352600    0.0566700
+48991.00   0.208647   0.350396   0.0543068    0.208900    0.349600    0.0541300
+48992.00   0.208921   0.347431   0.0518327    0.209000    0.346600    0.0516600
+48993.00   0.209421   0.344530   0.0494074    0.209300    0.343800    0.0492500
+48994.00   0.210159   0.341695   0.0469999    0.209700    0.341100    0.0468700
+48995.00   0.211067   0.338925   0.0445590    0.210600    0.338400    0.0444300
+48996.00   0.212049   0.336234   0.0420253    0.211600    0.335800    0.0418800
+48997.00   0.213045   0.333641   0.0393377    0.212800    0.333200    0.0392000
+48998.00   0.213955   0.331154   0.0364839    0.213900    0.330800    0.0363500
+48999.00   0.214684   0.328784   0.0335118    0.214800    0.328400    0.0333800
+49000.00   0.215132   0.326497   0.0305107    0.215300    0.326200    0.0303900
+49001.00   0.215226   0.324183   0.0275822    0.215500    0.324100    0.0274700
+49002.00   0.214978   0.321737   0.0248090    0.215300    0.321800    0.0247000
+49003.00   0.214461   0.319091   0.0222319    0.214500    0.319200    0.0221400
+49004.00   0.213815   0.316306   0.0198540    0.213700    0.316400    0.0197600
+49005.00   0.213184   0.313454   0.0176442    0.212800    0.313300    0.0175500
+49006.00   0.212639   0.310580   0.0155523    0.212300    0.310100    0.0154500
+49007.00   0.212180   0.307690   0.0135130    0.212000    0.306900    0.0133900
+49008.00   0.211925   0.304822   0.0114464    0.211900    0.303900    0.0113300
+49009.00   0.211898   0.302089   0.0093022    0.212000    0.301000    0.0091800
+49010.00   0.211999   0.299570   0.0070530    0.212100    0.298500    0.0069300
+49011.00   0.212110   0.297312   0.0046827    0.212300    0.296300    0.0045600
+49012.00   0.212144   0.295279   0.0021893    0.212400    0.294300    0.0020500
+49013.00   0.212037   0.293336  -0.0004160    0.212300    0.292300   -0.0005600
+49014.00   0.211751   0.291318  -0.0031141    0.211800    0.290300   -0.0032600
+49015.00   0.211304   0.289155  -0.0058559    0.211100    0.288300   -0.0059900
+49016.00   0.210612   0.286843  -0.0085770    0.210200    0.286100   -0.0087000
+49017.00   0.209635   0.284374  -0.0112341    0.209100    0.283700   -0.0113600
+49018.00   0.208394   0.281767  -0.0137992    0.208000    0.281100   -0.0139300
+49019.00   0.207041   0.279023  -0.0162659    0.206800    0.278400   -0.0164000
+49020.00   0.205890   0.276161  -0.0186504    0.205800    0.275500   -0.0187800
+49021.00   0.205272   0.273272  -0.0210032    0.205600    0.272600   -0.0211300
+49022.00   0.205181   0.270501  -0.0234080    0.205600    0.269600   -0.0235400
+49023.00   0.205410   0.267923  -0.0259570    0.205700    0.266800   -0.0261000
+49024.00   0.205674   0.265552  -0.0287298    0.205700    0.264600   -0.0288900
+49025.00   0.205728   0.263317  -0.0317594    0.205700    0.262400   -0.0319500
+49026.00   0.205400   0.261104  -0.0350095    0.205400    0.260200   -0.0352200
+49027.00   0.204693   0.258794  -0.0383773    0.204900    0.257800   -0.0386000
+49028.00   0.203779   0.256316  -0.0417285    0.204200    0.255300   -0.0419400
+49029.00   0.202761   0.253763  -0.0449247    0.203400    0.252700   -0.0451200
+49030.00   0.201616   0.251195  -0.0478655    0.202200    0.250100   -0.0480400
+49031.00   0.200385   0.248588  -0.0505214    0.200900    0.247500   -0.0506900
+49032.00   0.199315   0.245908  -0.0529393    0.199500    0.245000   -0.0531000
+49033.00   0.198417   0.243224  -0.0552035    0.198400    0.242500   -0.0553600
+49034.00   0.197692   0.240590  -0.0574059    0.197600    0.240100   -0.0575600
+49035.00   0.197110   0.238066  -0.0596344    0.197100    0.237400   -0.0597700
+49036.00   0.196581   0.235758  -0.0619457    0.196800    0.234900   -0.0620600
+49037.00   0.195886   0.233727  -0.0643816    0.196300    0.232700   -0.0644800
+49038.00   0.194803   0.232000  -0.0669627    0.195400    0.230900   -0.0670500
+49039.00   0.193264   0.230511  -0.0696740    0.193900    0.229400   -0.0697600
+49040.00   0.191321   0.229092  -0.0724767    0.191900    0.228000   -0.0725800
+49041.00   0.189105   0.227541  -0.0753148    0.189600    0.226700   -0.0754400
+49042.00   0.186790   0.225705  -0.0781292    0.187300    0.224800   -0.0782800
+49043.00   0.184497   0.223638  -0.0808761    0.184900    0.222900   -0.0810400
+49044.00   0.182313   0.221451  -0.0835222    0.182600    0.220800   -0.0836700
+49045.00   0.180270   0.219255  -0.0860470    0.180600    0.218600   -0.0861700
+49046.00   0.178323   0.217119  -0.0884464    0.178600    0.216500   -0.0885500
+49047.00   0.176433   0.215094  -0.0907343    0.176600    0.214400   -0.0908200
+49048.00   0.174589   0.213215  -0.0929467    0.174600    0.212500   -0.0930500
+49049.00   0.172837   0.211506  -0.0951493    0.172700    0.210700   -0.0952700
+49050.00   0.171272   0.209920  -0.0974343    0.171200    0.209000   -0.0975700
+49051.00   0.169878   0.208394  -0.0998884    0.169800    0.207600   -0.1000400
+49052.00   0.168575   0.206865  -0.1025715    0.168400    0.206100   -0.1027400
+49053.00   0.167251   0.205305  -0.1055072    0.167000    0.204500   -0.1056700
+49054.00   0.165878   0.203710  -0.1086556    0.165800    0.202900   -0.1088200
+49055.00   0.164621   0.202094  -0.1119204    0.164700    0.201400   -0.1120700
+49056.00   0.163704   0.200494  -0.1151810    0.163700    0.199800   -0.1153000
+49057.00   0.163151   0.198951  -0.1183182    0.163100    0.198300   -0.1184100
+49058.00   0.162596   0.197466  -0.1212618    0.162600    0.196900   -0.1213400
+49059.00   0.161644   0.196015  -0.1240068    0.161900    0.195500   -0.1240900
+49060.00   0.160152   0.194585  -0.1266037    0.160500    0.194100   -0.1267000
+49061.00   0.158171   0.193139  -0.1291206    0.158500    0.192700   -0.1292200
+49062.00   0.155860   0.191669  -0.1316158    0.156000    0.191200   -0.1317200
+49063.00   0.153408   0.190202  -0.1341389    0.153600    0.189700   -0.1342400
+49064.00   0.150961   0.188755  -0.1367249    0.151100    0.188200   -0.1368200
+49065.00   0.148646   0.187318  -0.1393946    0.148600    0.186700   -0.1394800
+49066.00   0.146522   0.185882  -0.1421530    0.146500    0.185300   -0.1422400
+49067.00   0.144578   0.184458  -0.1449878    0.144600    0.183900   -0.1450800
+49068.00   0.142711   0.183056  -0.1478704    0.142700    0.182600   -0.1479800
+49069.00   0.140784   0.181682  -0.1507614    0.140900    0.181100   -0.1508900
+49070.00   0.138650   0.180349  -0.1536223    0.138700    0.179700   -0.1537700
+49071.00   0.136190   0.179107  -0.1564082    0.136200    0.178400   -0.1565700
+49072.00   0.133300   0.177912  -0.1590735    0.133300    0.177200   -0.1592400
+49073.00   0.130164   0.176669  -0.1616137    0.130200    0.175900   -0.1617700
+49074.00   0.126954   0.175365  -0.1640387    0.127000    0.174400   -0.1641800
+49075.00   0.123795   0.174030  -0.1663745    0.123700    0.173000   -0.1665100
+49076.00   0.120808   0.172734  -0.1686651    0.120500    0.171600   -0.1687800
+49077.00   0.118065   0.171571  -0.1709796    0.117700    0.170700   -0.1711000
+49078.00   0.115461   0.170608  -0.1734106    0.115200    0.169800   -0.1735300
+49079.00   0.112819   0.169850  -0.1760396    0.112800    0.169100   -0.1761600
+49080.00   0.109985   0.169234  -0.1789125    0.110400    0.168500   -0.1790400
+49081.00   0.106929   0.168672  -0.1820214    0.107500    0.167900   -0.1821500
+49082.00   0.103742   0.168107  -0.1852993    0.104400    0.167300   -0.1854200
+49083.00   0.100497   0.167543  -0.1886393    0.101000    0.166800   -0.1887400
+49084.00   0.097190   0.166999  -0.1919310    0.097400    0.166300   -0.1920200
+49085.00   0.093870   0.166460  -0.1950761    0.093800    0.165900   -0.1951500
+49086.00   0.090617   0.165924  -0.1980306    0.090200    0.165400   -0.1981000
+49087.00   0.087503   0.165434  -0.2008148    0.087100    0.164900   -0.2008700
+49088.00   0.084599   0.165031  -0.2034890    0.084300    0.164400   -0.2035500
+49089.00   0.081980   0.164749  -0.2061209    0.081800    0.163900   -0.2061900
+49090.00   0.079691   0.164583  -0.2087601    0.079700    0.163600   -0.2088500
+49091.00   0.077753   0.164501  -0.2114531    0.077900    0.163600   -0.2115600
+49092.00   0.076037   0.164432  -0.2142464    0.076200    0.163600   -0.2143700
+49093.00   0.074297   0.164311  -0.2171315    0.074400    0.163600   -0.2172500
+49094.00   0.072364   0.164130  -0.2200758    0.072400    0.163500   -0.2201900
+49095.00   0.070167   0.163952  -0.2230453    0.070300    0.163500   -0.2231600
+49096.00   0.067678   0.163854  -0.2260020    0.068000    0.163400   -0.2261100
+49097.00   0.064991   0.163886  -0.2289063    0.065500    0.163400   -0.2290100
+49098.00   0.062269   0.164046  -0.2317394    0.062800    0.163400   -0.2318600
+49099.00   0.059560   0.164214  -0.2345184    0.060000    0.163300   -0.2346600
+49100.00   0.056815   0.164291  -0.2372510    0.057000    0.163100   -0.2373800
+49101.00   0.054017   0.164249  -0.2399097    0.054000    0.162900   -0.2400300
+49102.00   0.051235   0.164158  -0.2424968    0.051100    0.162800   -0.2426200
+49103.00   0.048568   0.164102  -0.2450409    0.048300    0.163000   -0.2451500
+49104.00   0.046083   0.164147  -0.2475844    0.045800    0.163400   -0.2477200
+49105.00   0.043828   0.164335  -0.2502105    0.043400    0.163800   -0.2503800
+49106.00   0.041787   0.164665  -0.2530275    0.041400    0.164200   -0.2532100
+49107.00   0.039911   0.165073  -0.2560917    0.039500    0.164600   -0.2562600
+49108.00   0.038162   0.165485  -0.2593800    0.038000    0.165000   -0.2595300
+49109.00   0.036530   0.165871  -0.2628226    0.036600    0.165300   -0.2629600
+49110.00   0.034937   0.166200  -0.2663070    0.035100    0.165600   -0.2664400
+49111.00   0.033243   0.166454  -0.2696980    0.033300    0.165800   -0.2698200
+49112.00   0.031379   0.166619  -0.2728791    0.031400    0.166100   -0.2730100
+49113.00   0.029478   0.166678  -0.2758004    0.029500    0.166200   -0.2759200
+49114.00   0.027545   0.166609  -0.2784700    0.027700    0.166400   -0.2785800
+49115.00   0.025583   0.166474  -0.2809558    0.025900    0.166400   -0.2810600
+49116.00   0.023567   0.166356  -0.2833422    0.024000    0.166400   -0.2834500
+49117.00   0.021584   0.166332  -0.2857034    0.022100    0.166300   -0.2858100
+49118.00   0.019770   0.166462  -0.2880885    0.020100    0.166200   -0.2882100
+49119.00   0.018154   0.166749  -0.2905120    0.018200    0.166100   -0.2906300
+49120.00   0.016659   0.167191  -0.2929677    0.016600    0.166400   -0.2931100
+49121.00   0.015280   0.167765  -0.2954686    0.015300    0.166800   -0.2956200
+49122.00   0.014000   0.168440  -0.2980239    0.014000    0.167400   -0.2981900
+49123.00   0.012715   0.169193  -0.3006133    0.012700    0.168200   -0.3007600
+49124.00   0.011395   0.170002  -0.3032040    0.011400    0.169000   -0.3033500
+49125.00   0.010122   0.170851  -0.3057593    0.010200    0.170000   -0.3059000
+49126.00   0.008962   0.171744  -0.3082467    0.009000    0.170900   -0.3083700
+49127.00   0.007802   0.172669  -0.3106349    0.007600    0.171900   -0.3107400
+49128.00   0.006488   0.173605  -0.3129097    0.006000    0.172900   -0.3130200
+49129.00   0.004962   0.174529  -0.3150881    0.004000    0.173900   -0.3152100
+49130.00   0.003180   0.175435  -0.3172081    0.001900    0.174900   -0.3173300
+49131.00   0.001001   0.176264  -0.3193324   -0.000400    0.175700   -0.3194600
+49132.00  -0.001543   0.176968  -0.3215329   -0.002700    0.176500   -0.3216700
+49133.00  -0.004186   0.177552  -0.3238725   -0.004900    0.177000   -0.3240100
+49134.00  -0.006612   0.178039  -0.3263804   -0.006900    0.177400   -0.3265300
+49135.00  -0.008664   0.178485  -0.3290562   -0.008700    0.177700   -0.3292300
+49136.00  -0.010426   0.178935  -0.3318619   -0.010400    0.178000   -0.3320500
+49137.00  -0.012046   0.179405  -0.3347233   -0.012000    0.178400   -0.3349100
+49138.00  -0.013693   0.179884  -0.3375466   -0.013700    0.178900   -0.3377400
+49139.00  -0.015457   0.180333  -0.3402389   -0.015400    0.179500   -0.3404000
+49140.00  -0.017306   0.180721  -0.3427283   -0.017100    0.180000   -0.3428600
+49141.00  -0.019162   0.181046  -0.3449825   -0.018900    0.180500   -0.3450900
+49142.00  -0.020994   0.181454  -0.3470279   -0.020900    0.180900   -0.3471100
+49143.00  -0.022821   0.182091  -0.3489347   -0.022800    0.181500   -0.3489900
+49144.00  -0.024651   0.182945  -0.3507899   -0.024600    0.182400   -0.3508500
+49145.00  -0.026472   0.183944  -0.3526721   -0.026500    0.183300   -0.3527500
+49146.00  -0.028260   0.184996  -0.3546329   -0.028300    0.184300   -0.3547500
+49147.00  -0.029986   0.186010  -0.3566907   -0.030200    0.185300   -0.3568300
+49148.00  -0.031655   0.186928  -0.3588295   -0.032000    0.186200   -0.3590000
+49149.00  -0.033229   0.187816  -0.3610373   -0.033500    0.187000   -0.3612300
+49150.00  -0.034528   0.188782  -0.3632832   -0.034700    0.188100   -0.3634800
+49151.00  -0.035578   0.189930  -0.3655243   -0.035600    0.189300   -0.3657100
+49152.00  -0.036543   0.191288  -0.3677157   -0.036600    0.190600   -0.3678700
+49153.00  -0.037651   0.192787  -0.3698172   -0.037800    0.192000   -0.3699400
+49154.00  -0.039083   0.194308  -0.3718005   -0.039200    0.193500   -0.3719100
+49155.00  -0.040805   0.195682  -0.3736641   -0.040900    0.194800   -0.3737700
+49156.00  -0.042804   0.196851  -0.3754291   -0.042900    0.195900   -0.3755400
+49157.00  -0.045006   0.197823  -0.3771254   -0.045100    0.196900   -0.3772600
+49158.00  -0.047278   0.198658  -0.3788066   -0.047200    0.197800   -0.3789600
+49159.00  -0.049500   0.199429  -0.3805425   -0.049300    0.198600   -0.3807100
+49160.00  -0.051507   0.200208  -0.3823984   -0.051300    0.199300   -0.3825600
+49161.00  -0.053139   0.201109  -0.3844143   -0.053000    0.200200   -0.3845700
+49162.00  -0.054384   0.202161  -0.3865801   -0.054400    0.201400   -0.3867400
+49163.00  -0.055378   0.203307  -0.3888537   -0.055600    0.202600   -0.3890300
+49164.00  -0.056273   0.204479  -0.3911756   -0.056600    0.203800   -0.3913600
+49165.00  -0.057333   0.205615  -0.3934676   -0.057700    0.204800   -0.3936400
+49166.00  -0.058551   0.206700  -0.3956403   -0.058800    0.205900   -0.3958000
+49167.00  -0.059828   0.207760  -0.3976239   -0.060000    0.206900   -0.3977900
+49168.00  -0.061112   0.208846  -0.3993921   -0.061200    0.208100   -0.3995700
+49169.00  -0.062437   0.210047   0.5990240   -0.062300    0.209300    0.5988300
+49170.00  -0.063739   0.211328   0.5975692   -0.063500    0.210600    0.5974000
+49171.00  -0.064926   0.212596   0.5961771   -0.064700    0.211800    0.5960200
+49172.00  -0.065948   0.213794   0.5947836   -0.065800    0.213000    0.5946400
+49173.00  -0.066764   0.214898   0.5933425   -0.066800    0.214100    0.5932100
+49174.00  -0.067351   0.215906   0.5918315   -0.067600    0.215200    0.5917300
+49175.00  -0.067751   0.216822   0.5902532   -0.068100    0.216100    0.5901800
+49176.00  -0.068022   0.217671   0.5886301   -0.068300    0.216800    0.5885900
+49177.00  -0.068127   0.218538   0.5869959   -0.068400    0.217600    0.5869700
+49178.00  -0.068091   0.219466   0.5853818   -0.068500    0.218600    0.5853600
+49179.00  -0.068029   0.220478   0.5838113   -0.068500    0.219600    0.5837500
+49180.00  -0.068167   0.221597   0.5822997   -0.068700    0.220900    0.5821800
+49181.00  -0.068638   0.222851   0.5808530   -0.069000    0.222300    0.5806900
+49182.00  -0.069396   0.224261   0.5794633   -0.069500    0.223800    0.5792900
+49183.00  -0.070293   0.225760   0.5781232   -0.070200    0.225100    0.5779600
+49184.00  -0.071136   0.227328   0.5768387   -0.071100    0.226600    0.5767200
+49185.00  -0.071804   0.228951   0.5755861   -0.071900    0.228000    0.5755000
+49186.00  -0.072163   0.230617   0.5743088   -0.072400    0.229700    0.5742500
+49187.00  -0.072376   0.232328   0.5729212   -0.072800    0.231500    0.5728500
+49188.00  -0.072659   0.234071   0.5713467   -0.073100    0.233300    0.5712500
+49189.00  -0.073162   0.235815   0.5695413   -0.073400    0.235100    0.5694000
+49190.00  -0.073761   0.237549   0.5674973   -0.073800    0.236700    0.5673200
+49191.00  -0.074331   0.239305   0.5652901   -0.074300    0.238300    0.5651100
+49192.00  -0.074925   0.241023   0.5630454   -0.074900    0.239900    0.5628700
+49193.00  -0.075668   0.242640   0.5608853   -0.075600    0.241500    0.5607100
+49194.00  -0.076631   0.244148   0.5588963   -0.076500    0.243100    0.5587400
+49195.00  -0.077778   0.245624   0.5571142   -0.077700    0.244800    0.5569700
+49196.00  -0.078981   0.247191   0.5555234   -0.078900    0.246600    0.5553900
+49197.00  -0.080118   0.248935   0.5540600   -0.080000    0.248400    0.5539300
+49198.00  -0.081227   0.250760   0.5526319   -0.081100    0.250200    0.5525200
+49199.00  -0.082366   0.252510   0.5511802   -0.082200    0.252100    0.5510800
+49200.00  -0.083461   0.254212   0.5496530   -0.083200    0.253900    0.5495600
+49201.00  -0.084451   0.255956   0.5480175   -0.084100    0.255600    0.5479300
+49202.00  -0.085351   0.257807   0.5462633   -0.085100    0.257300    0.5461500
+49203.00  -0.086260   0.259752   0.5443991   -0.086300    0.259100    0.5442500
+49204.00  -0.087499   0.261732   0.5424427   -0.087800    0.261100    0.5422800
+49205.00  -0.089023   0.263722   0.5404353   -0.089300    0.263000    0.5402800
+49206.00  -0.090640   0.265695   0.5384455   -0.090700    0.265000    0.5382800
+49207.00  -0.092153   0.267658   0.5365244   -0.092100    0.267000    0.5363500
+49208.00  -0.093437   0.269647   0.5347066   -0.093100    0.268900    0.5345300
+49209.00  -0.094417   0.271698   0.5330118   -0.094100    0.270900    0.5328700
+49210.00  -0.095113   0.273838   0.5314421   -0.094700    0.273000    0.5313200
+49211.00  -0.095676   0.276076   0.5299608   -0.095300    0.275200    0.5298800
+49212.00  -0.096200   0.278363   0.5285077   -0.095700    0.277400    0.5284700
+49213.00  -0.096752   0.280621   0.5270114   -0.096300    0.279500    0.5270100
+49214.00  -0.097412   0.282790   0.5253905   -0.097000    0.281600    0.5254000
+49215.00  -0.098178   0.284807   0.5235760   -0.097800    0.283500    0.5235700
+49216.00  -0.098964   0.286624   0.5215295   -0.098800    0.285400    0.5214800
+49217.00  -0.099720   0.288258   0.5192544   -0.099800    0.287100    0.5191600
+49218.00  -0.100408   0.289796   0.5167887   -0.100700    0.288800    0.5166500
+49219.00  -0.101032   0.291317   0.5142204   -0.101400    0.290500    0.5140600
+49220.00  -0.101549   0.292889   0.5116758   -0.101800    0.292100    0.5115200
+49221.00  -0.101885   0.294559   0.5092718   -0.102100    0.293700    0.5091100
+49222.00  -0.102012   0.296341   0.5070777   -0.102100    0.295500    0.5069000
+49223.00  -0.101942   0.298233   0.5051082   -0.102000    0.297300    0.5049300
+49224.00  -0.101719   0.300269   0.5033333   -0.101700    0.299400    0.5031500
+49225.00  -0.101402   0.302496   0.5016905   -0.101500    0.301700    0.5015000
+49226.00  -0.101072   0.304851   0.5001094   -0.101100    0.304100    0.4999300
+49227.00  -0.100733   0.307233   0.4985168   -0.100800    0.306500    0.4983600
+49228.00  -0.100362   0.309577   0.4968559   -0.100500    0.308900    0.4967200
+49229.00  -0.099972   0.311833   0.4950917   -0.100100    0.311100    0.4949600
+49230.00  -0.099671   0.313947   0.4932110   -0.099800    0.313100    0.4930800
+49231.00  -0.099584   0.315892   0.4912200   -0.099500    0.315100    0.4910800
+49232.00  -0.099550   0.317749   0.4891461   -0.099300    0.316900    0.4890000
+49233.00  -0.099476   0.319598   0.4870423   -0.099200    0.318800    0.4868900
+49234.00  -0.099417   0.321454   0.4849612   -0.099200    0.320600    0.4847900
+49235.00  -0.099508   0.323283   0.4829426   -0.099400    0.322300    0.4827600
+49236.00  -0.099829   0.325038   0.4810086   -0.099800    0.324000    0.4808200
+49237.00  -0.100319   0.326663   0.4791607   -0.100300    0.325600    0.4789900
+49238.00  -0.100783   0.328160   0.4773845   -0.100900    0.327100    0.4772400
+49239.00  -0.101141   0.329689   0.4756590   -0.101300    0.328700    0.4755400
+49240.00  -0.101269   0.331310   0.4739299   -0.101400    0.330300    0.4738300
+49241.00  -0.101089   0.333037   0.4721218   -0.101200    0.332100    0.4720200
+49242.00  -0.100565   0.334903   0.4701547   -0.100600    0.334000    0.4700600
+49243.00  -0.099728   0.336973   0.4679612   -0.099800    0.336100    0.4678600
+49244.00  -0.098778   0.339250   0.4655123   -0.099000    0.338400    0.4654000
+49245.00  -0.097920   0.341636   0.4628345   -0.098100    0.340900    0.4627000
+49246.00  -0.097307   0.344011   0.4600161   -0.097400    0.343300    0.4598700
+49247.00  -0.096914   0.346228   0.4571881   -0.096900    0.345600    0.4570400
+49248.00  -0.096709   0.348151   0.4544796   -0.096700    0.347600    0.4543300
+49249.00  -0.096650   0.349767   0.4519736   -0.096700    0.349400    0.4518200
+49250.00  -0.096757   0.351130   0.4496859   -0.096800    0.350800    0.4495400
+49251.00  -0.096961   0.352364   0.4475688   -0.097100    0.352000    0.4474300
+49252.00  -0.097137   0.353660   0.4455356   -0.097300    0.353200    0.4454100
+49253.00  -0.097226   0.355198   0.4435103   -0.097300    0.354400    0.4434000
+49254.00  -0.097154   0.356944   0.4414471   -0.097200    0.355900    0.4413300
+49255.00  -0.096846   0.358816   0.4393042   -0.096800    0.357700    0.4391800
+49256.00  -0.096384   0.360749   0.4370666   -0.096400    0.359600    0.4369400
+49257.00  -0.095887   0.362707   0.4347408   -0.095900    0.361700    0.4346100
+49258.00  -0.095487   0.364679   0.4323504   -0.095600    0.363900    0.4322300
+49259.00  -0.095344   0.366640   0.4299340   -0.095400    0.366000    0.4298200
+49260.00  -0.095565   0.368552   0.4275414   -0.095400    0.368000    0.4274400
+49261.00  -0.095760   0.370405   0.4251976   -0.095400    0.369900    0.4250900
+49262.00  -0.095500   0.372243   0.4229157   -0.095300    0.371700    0.4228100
+49263.00  -0.094925   0.374109   0.4207044   -0.094900    0.373400    0.4206100
+49264.00  -0.094225   0.376028   0.4185569   -0.094300    0.375200    0.4184500
+49265.00  -0.093568   0.377975   0.4164492   -0.093900    0.377100    0.4163300
+49266.00  -0.093027   0.379897   0.4143430   -0.093500    0.378900    0.4142100
+49267.00  -0.092536   0.381742   0.4121963   -0.092800    0.380700    0.4120700
+49268.00  -0.091935   0.383500   0.4099864   -0.092100    0.382500    0.4098600
+49269.00  -0.091076   0.385238   0.4076651   -0.091100    0.384200    0.4075300
+49270.00  -0.089912   0.387006   0.4051709   -0.089900    0.386000    0.4050400
+49271.00  -0.088541   0.388858   0.4024546   -0.088500    0.387900    0.4023200
+49272.00  -0.087093   0.390877   0.3995030   -0.087000    0.390000    0.3993700
+49273.00  -0.085687   0.393093   0.3963491   -0.085600    0.392300    0.3962100
+49274.00  -0.084433   0.395382   0.3930678   -0.084300    0.394500    0.3929500
+49275.00  -0.083389   0.397498   0.3898032   -0.083200    0.396600    0.3897000
+49276.00  -0.082597   0.399324   0.3866909   -0.082400    0.398400    0.3865900
+49277.00  -0.082066   0.400879   0.3838085   -0.081800    0.399900    0.3837000
+49278.00  -0.081697   0.402206   0.3811629   -0.081400    0.401200    0.3810500
+49279.00  -0.081249   0.403378   0.3787015   -0.081000    0.402400    0.3785800
+49280.00  -0.080501   0.404517   0.3763403   -0.080700    0.403700    0.3762200
+49281.00  -0.079780   0.405850   0.3739861   -0.080300    0.405100    0.3738500
+49282.00  -0.079255   0.407354   0.3715641   -0.079800    0.406700    0.3714300
+49283.00  -0.078848   0.408918   0.3690462   -0.079300    0.408300    0.3689200
+49284.00  -0.078395   0.410502   0.3664339   -0.078600    0.410000    0.3663200
+49285.00  -0.077753   0.412128   0.3637438   -0.077900    0.411700    0.3636400
+49286.00  -0.076914   0.413836   0.3610009   -0.077100    0.413400    0.3609000
+49287.00  -0.076031   0.415576   0.3582377   -0.076400    0.415100    0.3581400
+49288.00  -0.075370   0.417185   0.3554951   -0.075800    0.416700    0.3554000
+49289.00  -0.074954   0.418609   0.3528138   -0.075300    0.418000    0.3527000
+49290.00  -0.074635   0.419832   0.3502215   -0.074900    0.419100    0.3500800
+49291.00  -0.074213   0.420862   0.3477349   -0.074400    0.420000    0.3475800
+49292.00  -0.073492   0.421760   0.3453578   -0.073600    0.420800    0.3451900
+49293.00  -0.072270   0.422619   0.3430772   -0.072500    0.421500    0.3429200
+49294.00  -0.070455   0.423522   0.3408636   -0.071000    0.422300    0.3407300
+49295.00  -0.068458   0.424564   0.3386838   -0.069100    0.423400    0.3385700
+49296.00  -0.066409   0.425838   0.3364799   -0.066900    0.424700    0.3363600
+49297.00  -0.064303   0.427336   0.3341689   -0.064600    0.426300    0.3340500
+49298.00  -0.062188   0.428971   0.3316825   -0.062300    0.428100    0.3315700
+49299.00  -0.060083   0.430629   0.3289829   -0.060100    0.429800    0.3288600
+49300.00  -0.058051   0.432202   0.3260761   -0.058100    0.431400    0.3259500
+49301.00  -0.056232   0.433615   0.3230175   -0.056400    0.432900    0.3229000
+49302.00  -0.054719   0.434848   0.3199125   -0.054900    0.434200    0.3197900
+49303.00  -0.053383   0.435925   0.3168850   -0.053500    0.435300    0.3167600
+49304.00  -0.052073   0.436873   0.3140291   -0.052100    0.436100    0.3139000
+49305.00  -0.050757   0.437726   0.3113888   -0.050800    0.436900    0.3112500
+49306.00  -0.049472   0.438542   0.3089486   -0.049500    0.437600    0.3088100
+49307.00  -0.048279   0.439395   0.3066469   -0.048500    0.438500    0.3065200
+49308.00  -0.047288   0.440342   0.3044064   -0.047700    0.439400    0.3042900
+49309.00  -0.046610   0.441395   0.3021613   -0.047000    0.440400    0.3020400
+49310.00  -0.046115   0.442500   0.2998652   -0.046400    0.441500    0.2997400
+49311.00  -0.045686   0.443609   0.2975016   -0.045900    0.442600    0.2973700
+49312.00  -0.045176   0.444669   0.2950769   -0.045300    0.443700    0.2949600
+49313.00  -0.044412   0.445671   0.2926107   -0.044600    0.444800    0.2925000
+49314.00  -0.043359   0.446638   0.2901289   -0.043700    0.445900    0.2900300
+49315.00  -0.042190   0.447596   0.2876619   -0.042700    0.447000    0.2875600
+49316.00  -0.041254   0.448548   0.2852456   -0.041700    0.448000    0.2851600
+49317.00  -0.040560   0.449434   0.2829184   -0.040900    0.448900    0.2828300
+49318.00  -0.039980   0.450216   0.2806996   -0.040100    0.449600    0.2806200
+49319.00  -0.039440   0.450907   0.2785978   -0.039300    0.450300    0.2785100
+49320.00  -0.038813   0.451524   0.2766129   -0.038600    0.450800    0.2765200
+49321.00  -0.037886   0.452118   0.2747398   -0.037700    0.451300    0.2746400
+49322.00  -0.036491   0.452767   0.2729459   -0.036600    0.451900    0.2728200
+49323.00  -0.035037   0.453502   0.2711337   -0.035300    0.452600    0.2710000
+49324.00  -0.033629   0.454320   0.2692482   -0.033800    0.453400    0.2690900
+49325.00  -0.032184   0.455219   0.2672266   -0.032100    0.454600    0.2671000
+49326.00  -0.030695   0.456183   0.2650228   -0.030600    0.455600    0.2648700
+49327.00  -0.029197   0.457189   0.2626230   -0.029000    0.456600    0.2624600
+49328.00  -0.027689   0.458224   0.2600475   -0.027600    0.457600    0.2598800
+49329.00  -0.026169   0.459296   0.2573503   -0.026300    0.458600    0.2572000
+49330.00  -0.024876   0.460349   0.2546337   -0.025000    0.459500    0.2545100
+49331.00  -0.023654   0.461368   0.2519902   -0.023700    0.460400    0.2518700
+49332.00  -0.022373   0.462389   0.2494811   -0.022400    0.461400    0.2493700
+49333.00  -0.021116   0.463459   0.2471210   -0.021200    0.462400    0.2470100
+49334.00  -0.019978   0.464619   0.2448779   -0.020100    0.463600    0.2447600
+49335.00  -0.018938   0.465875   0.2426803   -0.019000    0.465000    0.2425400
+49336.00  -0.017978   0.467130   0.2404463   -0.017900    0.466300    0.2403000
+49337.00  -0.016999   0.468233   0.2381032   -0.017000    0.467500    0.2379400
+49338.00  -0.015881   0.469162   0.2356355   -0.016000    0.468500    0.2354700
+49339.00  -0.014635   0.469955   0.2330778   -0.014800    0.469300    0.2329100
+49340.00  -0.013258   0.470683   0.2304656   -0.013500    0.470000    0.2303000
+49341.00  -0.011691   0.471433   0.2278282   -0.011700    0.470700    0.2276700
+49342.00  -0.009752   0.472284   0.2251854   -0.009700    0.471500    0.2250500
+49343.00  -0.007342   0.473280   0.2225628   -0.007400    0.472500    0.2224700
+49344.00  -0.004758   0.474365   0.2200279   -0.005000    0.473600    0.2199400
+49345.00  -0.002260   0.475357   0.2176001   -0.002700    0.474600    0.2175100
+49346.00  -0.000079   0.476105   0.2152727   -0.000600    0.475300    0.2151800
+49347.00   0.001645   0.476538   0.2130411    0.001200    0.475800    0.2129400
+49348.00   0.002979   0.476647   0.2108865    0.002600    0.475900    0.2107800
+49349.00   0.004130   0.476509   0.2087733    0.003900    0.475800    0.2086600
+49350.00   0.005326   0.476268   0.2066509    0.005000    0.475600    0.2065100
+49351.00   0.006736   0.476099   0.2044548    0.006200    0.475500    0.2042600
+49352.00   0.008448   0.476147   0.2020948    0.007800    0.475500    0.2018500
+49353.00   0.010488   0.476399   0.1995489    0.009700    0.475600    0.1992800
+49354.00   0.012823   0.476816   0.1968449    0.012000    0.476000    0.1965900
+49355.00   0.015382   0.477358   0.1940457    0.014700    0.476700    0.1938000
+49356.00   0.018130   0.477976   0.1912390    0.017600    0.477300    0.1910200
+49357.00   0.021020   0.478639   0.1885171    0.020600    0.478000    0.1883300
+49358.00   0.023887   0.479288   0.1859460    0.023600    0.478600    0.1857800
+49359.00   0.026706   0.479806   0.1835517    0.026400    0.479000    0.1834100
+49360.00   0.029429   0.480131   0.1813388    0.029100    0.479200    0.1812100
+49361.00   0.031915   0.480235   0.1792719    0.031600    0.479300    0.1791600
+49362.00   0.034135   0.480135   0.1772787    0.033900    0.479200    0.1771700
+49363.00   0.036207   0.479908   0.1752855    0.036200    0.479000    0.1751800
+49364.00   0.038357   0.479664   0.1732344    0.038400    0.478900    0.1731100
+49365.00   0.040806   0.479395   0.1710573    0.040800    0.478700    0.1709500
+49366.00   0.043564   0.479149   0.1687462    0.043200    0.478700    0.1686700
+49367.00   0.046270   0.479033   0.1663712    0.045600    0.478600    0.1663300
+49368.00   0.048614   0.479092   0.1640261    0.047900    0.478700    0.1639700
+49369.00   0.050696   0.479199   0.1617348    0.050100    0.478700    0.1616500
+49370.00   0.052590   0.479158   0.1594831    0.052300    0.478500    0.1593800
+49371.00   0.054280   0.478859   0.1572956    0.054300    0.478200    0.1572000
+49372.00   0.055909   0.478392   0.1552205    0.056300    0.477600    0.1551100
+49373.00   0.057689   0.477780   0.1532311    0.058400    0.477000    0.1531100
+49374.00   0.059822   0.477049   0.1512759    0.060500    0.476400    0.1511700
+49375.00   0.062349   0.476481   0.1493481    0.062700    0.475900    0.1492600
+49376.00   0.065142   0.476263   0.1474173    0.065100    0.475500    0.1473300
+49377.00   0.067938   0.476205   0.1454482    0.067700    0.475200    0.1453500
+49378.00   0.070662   0.475982   0.1433702    0.070400    0.474900    0.1432400
+49379.00   0.073391   0.475402   0.1410971    0.073200    0.474500    0.1409600
+49380.00   0.076083   0.474665   0.1386113    0.076000    0.473900    0.1384800
+49381.00   0.078769   0.473851   0.1359433    0.078700    0.473200    0.1358100
+49382.00   0.081448   0.472951   0.1331513    0.081500    0.472300    0.1330100
+49383.00   0.084148   0.471983   0.1303264    0.084200    0.471400    0.1301900
+49384.00   0.086895   0.471012   0.1275677    0.086900    0.470400    0.1274300
+49385.00   0.089671   0.470130   0.1249677    0.089600    0.469600    0.1248200
+49386.00   0.092442   0.469412   0.1225727    0.092300    0.468800    0.1224200
+49387.00   0.095098   0.468762   0.1203722    0.095000    0.468000    0.1202300
+49388.00   0.097505   0.468057   0.1183323    0.097500    0.467300    0.1182000
+49389.00   0.099674   0.467277   0.1163942    0.099900    0.466600    0.1162700
+49390.00   0.101698   0.466450   0.1144788    0.102000    0.465800    0.1143600
+49391.00   0.103663   0.465580   0.1125030    0.104100    0.464900    0.1123800
+49392.00   0.105628   0.464636   0.1104216    0.106100    0.464000    0.1103000
+49393.00   0.107685   0.463574   0.1082301    0.108000    0.463000    0.1081000
+49394.00   0.109662   0.462451   0.1059464    0.109800    0.462000    0.1058200
+49395.00   0.111429   0.461331   0.1036106    0.111500    0.460800    0.1034800
+49396.00   0.113118   0.460239   0.1012573    0.113100    0.459700    0.1011200
+49397.00   0.114866   0.459172   0.0989138    0.114700    0.458500    0.0987800
+49398.00   0.116741   0.458065   0.0966117    0.116500    0.457300    0.0964700
+49399.00   0.118747   0.456839   0.0943824    0.118300    0.456100    0.0942400
+49400.00   0.120837   0.455491   0.0922419    0.120200    0.454800    0.0920800
+49401.00   0.122767   0.454064   0.0901704    0.122200    0.453400    0.0900000
+49402.00   0.124543   0.452625   0.0881463    0.124200    0.452000    0.0879700
+49403.00   0.126444   0.451326   0.0861344    0.126400    0.450800    0.0859700
+49404.00   0.128682   0.450311   0.0840749    0.128800    0.449800    0.0839300
+49405.00   0.131239   0.449616   0.0819021    0.131400    0.449100    0.0817700
+49406.00   0.133898   0.449154   0.0795498    0.134000    0.448600    0.0794300
+49407.00   0.136340   0.448793   0.0769470    0.136400    0.448300    0.0768400
+49408.00   0.138371   0.448401   0.0740647    0.138400    0.447900    0.0739700
+49409.00   0.139965   0.447866   0.0709302    0.140100    0.447400    0.0708400
+49410.00   0.141211   0.447137   0.0676205    0.141400    0.446600    0.0675100
+49411.00   0.142256   0.446175   0.0642490    0.142500    0.445600    0.0641200
+49412.00   0.143219   0.444899   0.0609375    0.143400    0.444200    0.0607900
+49413.00   0.144190   0.443314   0.0577774    0.144300    0.442700    0.0576300
+49414.00   0.145240   0.441509   0.0548062    0.145200    0.440900    0.0546700
+49415.00   0.146278   0.439625   0.0520263    0.146200    0.439000    0.0519500
+49416.00   0.147211   0.437777   0.0494116    0.147300    0.437200    0.0493600
+49417.00   0.148071   0.436001   0.0469051    0.148200    0.435400    0.0468600
+49418.00   0.148970   0.434307   0.0444290    0.149100    0.433800    0.0443700
+49419.00   0.149918   0.432679   0.0419256    0.150000    0.432200    0.0418500
+49420.00   0.150889   0.431067   0.0393615    0.150900    0.430500    0.0392600
+49421.00   0.151945   0.429415   0.0367221    0.151800    0.428800    0.0366200
+49422.00   0.153095   0.427708   0.0340371    0.153000    0.427100    0.0339500
+49423.00   0.154380   0.425975   0.0313733    0.154200    0.425200    0.0312900
+49424.00   0.155842   0.424297   0.0287840    0.155700    0.423500    0.0286800
+49425.00   0.157393   0.422761   0.0262872    0.157200    0.421800    0.0261500
+49426.00   0.158931   0.421377   0.0238883    0.158800    0.420400    0.0237300
+49427.00   0.160366   0.420007   0.0215796    0.160300    0.419000    0.0214200
+49428.00   0.161723   0.418500   0.0193435    0.161500    0.417700    0.0192100
+49429.00   0.162780   0.416933   0.0171856    0.162500    0.416200    0.0170800
+49430.00   0.163469   0.415334   0.0150740    0.163100    0.414700    0.0149800
+49431.00   0.163895   0.413673   0.0129631    0.163500    0.413100    0.0128700
+49432.00   0.164155   0.411919   0.0107951    0.163800    0.411400    0.0107000
+49433.00   0.164345   0.410066   0.0085070    0.164200    0.409600    0.0084200
+49434.00   0.164664   0.408178   0.0060379    0.164700    0.407700    0.0059400
+49435.00   0.165351   0.406360   0.0033379    0.165600    0.405800    0.0032300
+49436.00   0.166358   0.404603   0.0004004    0.166500    0.403900    0.0002800
+49437.00   0.167468   0.402813  -0.0027475    0.167500    0.402000   -0.0028700
+49438.00   0.168527   0.400966  -0.0060032    0.168400    0.400100   -0.0061200
+49439.00   0.169439   0.399053  -0.0092285    0.169300    0.398200   -0.0093300
+49440.00   0.170178   0.397074  -0.0122952    0.170300    0.396300   -0.0124100
+49441.00   0.170917   0.395070  -0.0151299    0.171400    0.394400   -0.0152600
+49442.00   0.172023   0.393179  -0.0177504    0.172600    0.392600   -0.0178900
+49443.00   0.173479   0.391451  -0.0202159    0.174000    0.390800   -0.0203400
+49444.00   0.175073   0.389822  -0.0225980    0.175300    0.389100   -0.0227200
+49445.00   0.176575   0.388195  -0.0249739    0.176500    0.387500   -0.0251000
+49446.00   0.177850   0.386499  -0.0273956    0.177700    0.385800   -0.0274900
+49447.00   0.178909   0.384736  -0.0298724    0.178800    0.384200   -0.0299700
+49448.00   0.179853   0.382938  -0.0323972    0.179800    0.382400   -0.0325000
+49449.00   0.180783   0.381137  -0.0349731    0.180800    0.380700   -0.0350900
+49450.00   0.181636   0.379343  -0.0375888    0.181700    0.379000   -0.0377100
+49451.00   0.182329   0.377572  -0.0402176    0.182400    0.377300   -0.0403500
+49452.00   0.182867   0.375874  -0.0428274    0.183000    0.375600   -0.0429600
+49453.00   0.183229   0.374257  -0.0453837    0.183300    0.373900   -0.0455200
+49454.00   0.183341   0.372623  -0.0478529    0.183400    0.372200   -0.0479800
+49455.00   0.183248   0.370836  -0.0502164    0.183300    0.370400   -0.0503400
+49456.00   0.183142   0.368777  -0.0524756    0.183100    0.368500   -0.0525900
+49457.00   0.183089   0.366542  -0.0546441    0.183100    0.366300   -0.0547500
+49458.00   0.183155   0.364319  -0.0567524    0.183200    0.364000   -0.0568600
+49459.00   0.183364   0.362224  -0.0588633    0.183400    0.361800   -0.0589700
+49460.00   0.183638   0.360311  -0.0610425    0.183600    0.359700   -0.0611500
+49461.00   0.183851   0.358589  -0.0633504    0.183800    0.357800   -0.0634500
+49462.00   0.183927   0.357041  -0.0658290    0.184000    0.356200   -0.0659100
+49463.00   0.183893   0.355593  -0.0684853    0.183900    0.354800   -0.0685800
+49464.00   0.183783   0.354125  -0.0713343    0.183800    0.353400   -0.0714500
+49465.00   0.183580   0.352535  -0.0743644    0.183600    0.351900   -0.0744800
+49466.00   0.183329   0.350751  -0.0775031    0.183300    0.350200   -0.0776000
+49467.00   0.183076   0.348730  -0.0806300    0.183000    0.348200   -0.0806900
+49468.00   0.182904   0.346445  -0.0836211    0.182800    0.345900   -0.0836600
+49469.00   0.182861   0.343925  -0.0864020    0.182700    0.343200   -0.0864600
+49470.00   0.182883   0.341266  -0.0889845    0.182700    0.340400   -0.0890600
+49471.00   0.183010   0.338645  -0.0914518    0.183000    0.337700   -0.0915600
+49472.00   0.183316   0.336208  -0.0938945    0.183300    0.335200   -0.0940100
+49473.00   0.183751   0.333988  -0.0963720    0.183800    0.332900   -0.0964900
+49474.00   0.184268   0.331918  -0.0989092    0.184300    0.330900   -0.0990100
+49475.00   0.184802   0.329910  -0.1014984    0.184400    0.329300   -0.1016100
+49476.00   0.185248   0.327875  -0.1041177    0.184700    0.327300   -0.1042400
+49477.00   0.185493   0.325771  -0.1067517    0.184900    0.325100   -0.1068800
+49478.00   0.185617   0.323560  -0.1093766    0.185200    0.322800   -0.1095100
+49479.00   0.185787   0.321227  -0.1119535    0.185500    0.320400   -0.1120800
+49480.00   0.186127   0.318824  -0.1144563    0.186000    0.318000   -0.1145800
+49481.00   0.186656   0.316455  -0.1168720    0.186700    0.315600   -0.1169900
+49482.00   0.187338   0.314204  -0.1191899    0.187400    0.313400   -0.1193000
+49483.00   0.188084   0.312099  -0.1214190    0.188200    0.311300   -0.1215400
+49484.00   0.188701   0.310098  -0.1236014    0.188700    0.309300   -0.1237200
+49485.00   0.189149   0.308105  -0.1257618    0.189000    0.307400   -0.1258600
+49486.00   0.189254   0.306043  -0.1279101    0.189000    0.305400   -0.1280100
+49487.00   0.188985   0.303929  -0.1300973    0.188800    0.303300   -0.1301900
+49488.00   0.188471   0.301845  -0.1323644    0.188400    0.301200   -0.1324700
+49489.00   0.187907   0.299837  -0.1347430    0.188000    0.299100   -0.1348500
+49490.00   0.187447   0.297844  -0.1372496    0.187600    0.297000   -0.1373600
+49491.00   0.187258   0.295802  -0.1398852    0.187300    0.295000   -0.1400100
+49492.00   0.187165   0.293799  -0.1426253    0.187000    0.293100   -0.1427500
+49493.00   0.186911   0.291936  -0.1454033    0.186600    0.291300   -0.1455300
+49494.00   0.186381   0.290234  -0.1481329    0.186000    0.289700   -0.1482500
+49495.00   0.185530   0.288644  -0.1507284    0.185200    0.288100   -0.1508300
+49496.00   0.184389   0.287063  -0.1531330    0.184200    0.286400   -0.1532300
+49497.00   0.183055   0.285359  -0.1553305    0.183100    0.284700   -0.1554300
+49498.00   0.181653   0.283438  -0.1573496    0.181900    0.282700   -0.1574600
+49499.00   0.180293   0.281331  -0.1592708    0.180600    0.280600   -0.1593900
+49500.00   0.179012   0.279090  -0.1611620    0.179200    0.278400   -0.1612800
+49501.00   0.177800   0.276742  -0.1630720    0.177900    0.276100   -0.1632000
+49502.00   0.176635   0.274317  -0.1650226    0.176700    0.273600   -0.1651600
+49503.00   0.175513   0.271880  -0.1670147    0.175500    0.271300   -0.1671600
+49504.00   0.174487   0.269528  -0.1690326    0.174500    0.268900   -0.1691800
+49505.00   0.173610   0.267345  -0.1710389    0.173500    0.266700   -0.1711600
+49506.00   0.172730   0.265339  -0.1729923    0.172400    0.264700   -0.1731100
+49507.00   0.171669   0.263474  -0.1748627    0.171400    0.262700   -0.1749800
+49508.00   0.170453   0.261702  -0.1766318    0.170200    0.260900   -0.1767500
+49509.00   0.169134   0.259973  -0.1782903    0.169100    0.259100   -0.1784000
+49510.00   0.167779   0.258226  -0.1798374    0.167900    0.257300   -0.1799400
+49511.00   0.166542   0.256394  -0.1812913    0.166800    0.255500   -0.1813900
+49512.00   0.165561   0.254458  -0.1826856    0.165700    0.253600   -0.1827900
+49513.00   0.164659   0.252447  -0.1840720    0.164600    0.251700   -0.1842000
+49514.00   0.163719   0.250366  -0.1855180    0.163600    0.249600   -0.1856600
+49515.00   0.162763   0.248207  -0.1870737    0.162600    0.247400   -0.1872300
+49516.00   0.161851   0.245977  -0.1887704    0.161800    0.245200   -0.1889200
+49517.00   0.161026   0.243751  -0.1906238    0.161000    0.243100   -0.1907700
+49518.00   0.160281   0.241641  -0.1926234    0.160200    0.241000   -0.1927500
+49519.00   0.159534   0.239746  -0.1947203    0.159500    0.239100   -0.1948300
+49520.00   0.158676   0.237991  -0.1968565    0.158700    0.237400   -0.1969500
+49521.00   0.157611   0.236226  -0.1989637    0.157700    0.235600   -0.1990500
+49522.00   0.156276   0.234349  -0.2009653    0.156500    0.233800   -0.2010400
+49523.00   0.154689   0.232316  -0.2027923    0.155100    0.231800   -0.2028700
+49524.00   0.152998   0.230146  -0.2044062    0.153500    0.229600   -0.2045000
+49525.00   0.151432   0.227937  -0.2058193    0.151900    0.227300   -0.2059300
+49526.00   0.149986   0.225843  -0.2071000    0.150100    0.225100   -0.2072300
+49527.00   0.148533   0.223884  -0.2083200    0.148300    0.223100   -0.2084500
+49528.00   0.146973   0.222011  -0.2095412    0.146500    0.221200   -0.2096800
+49529.00   0.145298   0.220177  -0.2108058    0.144800    0.219300   -0.2109400
+49530.00   0.143565   0.218360  -0.2121185    0.143000    0.217500   -0.2122400
+49531.00   0.141813   0.216582  -0.2134590    0.141400    0.215800   -0.2135700
+49532.00   0.140036   0.214909  -0.2147803    0.139800    0.214200   -0.2148800
+49533.00   0.138313   0.213438  -0.2160203    0.138300    0.212700   -0.2161400
+49534.00   0.136704   0.212123   0.7828134    0.136800    0.211500    0.7826700
+49535.00   0.135090   0.210874   0.7817000    0.135000    0.210300    0.7815500
+49536.00   0.133370   0.209627   0.7806540    0.133300    0.209000    0.7805200
+49537.00   0.131521   0.208338   0.7796814    0.131400    0.207600    0.7795600
+49538.00   0.129613   0.206974   0.7787730    0.129500    0.206200    0.7786700
+49539.00   0.127726   0.205520   0.7779060    0.127700    0.204600    0.7778100
+49540.00   0.125890   0.203972   0.7770472    0.125900    0.203000    0.7769300
+49541.00   0.124100   0.202429   0.7761321    0.124100    0.201400    0.7759900
+49542.00   0.122316   0.200998   0.7750765    0.122400    0.199900    0.7749200
+49543.00   0.120582   0.199702   0.7738352    0.120600    0.198700    0.7737000
+49544.00   0.118900   0.198504   0.7723924    0.118800    0.197600    0.7722700
+49545.00   0.117262   0.197367   0.7707577    0.117100    0.196600    0.7706400
+49546.00   0.115585   0.196254   0.7689693    0.115300    0.195500    0.7688400
+49547.00   0.113774   0.195114   0.7670815    0.113500    0.194400    0.7669300
+49548.00   0.111917   0.193886   0.7651629    0.111700    0.193100    0.7649800
+49549.00   0.110060   0.192527   0.7632581    0.109900    0.191700    0.7630900
+49550.00   0.108157   0.191009   0.7614317    0.108000    0.190200    0.7613000
+49551.00   0.106172   0.189314   0.7597348    0.106000    0.188400    0.7596400
+49552.00   0.104134   0.187488   0.7581907    0.104000    0.186600    0.7581200
+49553.00   0.102165   0.185697   0.7567906    0.102000    0.184800    0.7567200
+49554.00   0.100269   0.184109   0.7554917    0.100100    0.183100    0.7553900
+49555.00   0.098391   0.182709   0.7542115    0.098200    0.181700    0.7540600
+49556.00   0.096537   0.181392   0.7528672    0.096400    0.180400    0.7526900
+49557.00   0.094755   0.180140   0.7514222    0.094600    0.179200    0.7512600
+49558.00   0.092961   0.178983   0.7499005    0.092700    0.178100    0.7497800
+49559.00   0.091017   0.177952   0.7483472    0.090700    0.177100    0.7482500
+49560.00   0.088891   0.177080   0.7468139    0.088500    0.176200    0.7467100
+49561.00   0.086705   0.176385   0.7453424    0.086300    0.175400    0.7451900
+49562.00   0.084616   0.175803   0.7439290    0.084200    0.174700    0.7437300
+49563.00   0.082635   0.175254   0.7425566    0.082100    0.174200    0.7423500
+49564.00   0.080660   0.174710   0.7412436    0.080200    0.173800    0.7410500
+49565.00   0.078707   0.174217   0.7400066    0.078500    0.173400    0.7398500
+49566.00   0.076835   0.173815   0.7388476    0.076800    0.173100    0.7387200
+49567.00   0.075074   0.173500   0.7377391    0.075100    0.172800    0.7376000
+49568.00   0.073460   0.173232   0.7366003    0.073400    0.172500    0.7364400
+49569.00   0.071866   0.172970   0.7353776    0.071600    0.172200    0.7352000
+49570.00   0.070233   0.172666   0.7340370    0.069900    0.171700    0.7339000
+49571.00   0.068588   0.172309   0.7325559    0.068300    0.171400    0.7324300
+49572.00   0.067030   0.171954   0.7309394    0.066800    0.171300    0.7308300
+49573.00   0.065656   0.171684   0.7292203    0.065400    0.171200    0.7291200
+49574.00   0.064458   0.171547   0.7274593    0.064100    0.171200    0.7273400
+49575.00   0.063177   0.171524   0.7256921    0.062700    0.171200    0.7255600
+49576.00   0.061639   0.171505   0.7239814    0.061100    0.171000    0.7238400
+49577.00   0.059681   0.171353   0.7223951    0.059100    0.170700    0.7222500
+49578.00   0.057308   0.170981   0.7209662    0.056900    0.170200    0.7208100
+49579.00   0.054665   0.170386   0.7196757    0.054500    0.169600    0.7195100
+49580.00   0.052050   0.169688   0.7184603    0.052000    0.168900    0.7183000
+49581.00   0.049759   0.169084   0.7172468    0.049700    0.168300    0.7171100
+49582.00   0.047661   0.168749   0.7159904    0.047300    0.168000    0.7158700
+49583.00   0.045502   0.168670   0.7146430    0.045000    0.167800    0.7145300
+49584.00   0.043003   0.168739   0.7131745    0.042400    0.167900    0.7130500
+49585.00   0.040105   0.168840   0.7115798    0.039800    0.168000    0.7114500
+49586.00   0.036966   0.168869   0.7098798    0.037000    0.168000    0.7097400
+49587.00   0.033817   0.168787   0.7081159    0.034200    0.167900    0.7079800
+49588.00   0.030911   0.168608   0.7063369    0.031400    0.167600    0.7062200
+49589.00   0.028467   0.168424   0.7045966    0.029000    0.167400    0.7044700
+49590.00   0.026502   0.168362   0.7029378    0.026800    0.167300    0.7027900
+49591.00   0.024679   0.168471   0.7013413    0.024700    0.167400    0.7011900
+49592.00   0.022800   0.168698   0.6998036    0.022700    0.167700    0.6996800
+49593.00   0.020795   0.169007   0.6983214    0.020600    0.168100    0.6982400
+49594.00   0.018609   0.169415   0.6968824    0.018400    0.168600    0.6968200
+49595.00   0.016183   0.169907   0.6954573    0.016100    0.169000    0.6953800
+49596.00   0.013552   0.170355   0.6939768    0.013700    0.169400    0.6938700
+49597.00   0.010952   0.170717   0.6923683    0.011200    0.169800    0.6922400
+49598.00   0.008574   0.171044   0.6905787    0.008800    0.170200    0.6904400
+49599.00   0.006431   0.171426   0.6885696    0.006400    0.170700    0.6884300
+49600.00   0.004323   0.171935   0.6863372    0.004000    0.171000    0.6862300
+49601.00   0.002002   0.172597   0.6839238    0.001600    0.171800    0.6838300
+49602.00  -0.000616   0.173385   0.6814098   -0.000800    0.172700    0.6813000
+49603.00  -0.003456   0.174249   0.6788784   -0.003500    0.173600    0.6787400
+49604.00  -0.006148   0.175135   0.6763470   -0.006100    0.174600    0.6762100
+49605.00  -0.008596   0.176085   0.6738930   -0.008500    0.175500    0.6737700
+49606.00  -0.010833   0.177119   0.6715460   -0.010800    0.176500    0.6714500
+49607.00  -0.013072   0.178169   0.6692901   -0.013100    0.177400    0.6692100
+49608.00  -0.015556   0.179143   0.6670788   -0.015600    0.178200    0.6670100
+49609.00  -0.018422   0.179980   0.6648563   -0.018400    0.179100    0.6647800
+49610.00  -0.021599   0.180790   0.6625777   -0.021400    0.179800    0.6624700
+49611.00  -0.024813   0.181577   0.6601997   -0.024700    0.180600    0.6600700
+49612.00  -0.027798   0.182332   0.6576960   -0.027800    0.181300    0.6575600
+49613.00  -0.030505   0.183089   0.6551011   -0.030700    0.182200    0.6549800
+49614.00  -0.033007   0.183934   0.6524739   -0.033300    0.183100    0.6523600
+49615.00  -0.035461   0.184962   0.6498753   -0.035800    0.184200    0.6497700
+49616.00  -0.038015   0.186147   0.6473536   -0.038400    0.185400    0.6472300
+49617.00  -0.040832   0.187296   0.6449207   -0.041000    0.186600    0.6447800
+49618.00  -0.043818   0.188375   0.6426013   -0.043800    0.187700    0.6424600
+49619.00  -0.046775   0.189470   0.6404117   -0.046600    0.188700    0.6402600
+49620.00  -0.049585   0.190626   0.6383387   -0.049400    0.189700    0.6381900
+49621.00  -0.052280   0.191848   0.6363505   -0.052300    0.190900    0.6362000
+49622.00  -0.054909   0.193105   0.6344068   -0.055100    0.192100    0.6342700
+49623.00  -0.057521   0.194405   0.6324646   -0.057900    0.193600    0.6323400
+49624.00  -0.060233   0.195865   0.6304856   -0.060700    0.195100    0.6303600
+49625.00  -0.062857   0.197484   0.6284157   -0.063300    0.196800    0.6282800
+49626.00  -0.065164   0.199217   0.6261970   -0.065500    0.198500    0.6260500
+49627.00  -0.067149   0.201047   0.6237941   -0.067400    0.200300    0.6236400
+49628.00  -0.068989   0.202944   0.6212049   -0.069200    0.202200    0.6210500
+49629.00  -0.070976   0.204837   0.6184532   -0.071200    0.204000    0.6183100
+49630.00  -0.073424   0.206629   0.6155998   -0.073900    0.205500    0.6155100
+49631.00  -0.076446   0.208213   0.6127461   -0.076800    0.207100    0.6126800
+49632.00  -0.079838   0.209601   0.6100058   -0.080000    0.208600    0.6099500
+49633.00  -0.083262   0.210886   0.6074385   -0.083300    0.210000    0.6073700
+49634.00  -0.086479   0.212130   0.6050286   -0.086500    0.211300    0.6049500
+49635.00  -0.089290   0.213380   0.6027164   -0.089300    0.212600    0.6026200
+49636.00  -0.091505   0.214693   0.6004284   -0.091600    0.214000    0.6003300
+49637.00  -0.093166   0.216160   0.5981027   -0.093400    0.215500    0.5980000
+49638.00  -0.094538   0.217881   0.5957062   -0.094800    0.217200    0.5956100
+49639.00  -0.095750   0.219840   0.5932373   -0.095900    0.219100    0.5931400
+49640.00  -0.096889   0.221990   0.5907151   -0.096900    0.221100    0.5906300
+49641.00  -0.098078   0.224264   0.5881772   -0.098000    0.223400    0.5881000
+49642.00  -0.099501   0.226604   0.5856753   -0.099500    0.225700    0.5856200
+49643.00  -0.101377   0.228955   0.5832655   -0.101500    0.228000    0.5832200
+49644.00  -0.103841   0.231268   0.5809929   -0.104100    0.230300    0.5809500
+49645.00  -0.106752   0.233466   0.5788597   -0.107100    0.232600    0.5788000
+49646.00  -0.109750   0.235599   0.5768260   -0.110100    0.234900    0.5767600
+49647.00  -0.112530   0.237770   0.5748551   -0.112800    0.237100    0.5747800
+49648.00  -0.114973   0.239947   0.5729195   -0.115100    0.239300    0.5728500
+49649.00  -0.117092   0.242100   0.5709849   -0.117200    0.241300    0.5709100
+49650.00  -0.119084   0.244217   0.5690110   -0.119200    0.243300    0.5689300
+49651.00  -0.121195   0.246300   0.5669543   -0.121400    0.245300    0.5668700
+49652.00  -0.123525   0.248390   0.5647789   -0.123800    0.247300    0.5646900
+49653.00  -0.125949   0.250477   0.5624625   -0.126100    0.249400    0.5623800
+49654.00  -0.128152   0.252528   0.5599782   -0.128300    0.251500    0.5599200
+49655.00  -0.130001   0.254583   0.5573138   -0.130200    0.253700    0.5572700
+49656.00  -0.131520   0.256710   0.5544892   -0.131800    0.255900    0.5544400
+49657.00  -0.132859   0.258913   0.5515578   -0.133200    0.258200    0.5515000
+49658.00  -0.134179   0.261087   0.5485981   -0.134500    0.260400    0.5485300
+49659.00  -0.135582   0.263155   0.5457003   -0.135800    0.262600    0.5456100
+49660.00  -0.136997   0.265187   0.5429500   -0.137200    0.264900    0.5428600
+49661.00  -0.138425   0.267286   0.5403769   -0.138600    0.267000    0.5402800
+49662.00  -0.139887   0.269503   0.5379537   -0.140100    0.269100    0.5378600
+49663.00  -0.141344   0.271822   0.5356169   -0.141700    0.271300    0.5355300
+49664.00  -0.142740   0.274220   0.5333062   -0.143100    0.273600    0.5332300
+49665.00  -0.143972   0.276693   0.5309772   -0.144300    0.276000    0.5309100
+49666.00  -0.144804   0.279285   0.5285867   -0.145200    0.278700    0.5285300
+49667.00  -0.145287   0.281983   0.5261314   -0.145700    0.281400    0.5260800
+49668.00  -0.145653   0.284744   0.5236320   -0.146200    0.284200    0.5236000
+49669.00  -0.146085   0.287529   0.5211225   -0.146500    0.286900    0.5210900
+49670.00  -0.146623   0.290267   0.5186373   -0.146800    0.289600    0.5186000
+49671.00  -0.147213   0.292902   0.5162031   -0.147200    0.292300    0.5161600
+49672.00  -0.147808   0.295464   0.5138394   -0.147700    0.294900    0.5137800
+49673.00  -0.148488   0.298075   0.5115623   -0.148300    0.297500    0.5114900
+49674.00  -0.149331   0.300760   0.5093815   -0.149200    0.300200    0.5093100
+49675.00  -0.150308   0.303477   0.5072894   -0.150300    0.302900    0.5072100
+49676.00  -0.151335   0.306191   0.5052594   -0.151400    0.305600    0.5051700
+49677.00  -0.152336   0.308941   0.5032454   -0.152500    0.308400    0.5031400
+49678.00  -0.153136   0.311793   0.5011934   -0.153400    0.311100    0.5010800
+49679.00  -0.153511   0.314720   0.4990493   -0.153800    0.313900    0.4989500
+49680.00  -0.153354   0.317579   0.4967655   -0.153700    0.316700    0.4967000
+49681.00  -0.152777   0.320381   0.4943244   -0.153100    0.319500    0.4942900
+49682.00  -0.152113   0.323160   0.4917358   -0.152300    0.322300    0.4917300
+49683.00  -0.151650   0.325909   0.4890285   -0.151600    0.325100    0.4890400
+49684.00  -0.151460   0.328635   0.4862510   -0.151200    0.327900    0.4862600
+49685.00  -0.151383   0.331360   0.4834753   -0.151200    0.330600    0.4834500
+49686.00  -0.151275   0.334074   0.4807701   -0.151400    0.333400    0.4806900
+49687.00  -0.151498   0.336727   0.4781455   -0.151700    0.336100    0.4780300
+49688.00  -0.152003   0.339262   0.4756244   -0.152200    0.338700    0.4755000
+49689.00  -0.152556   0.341652   0.4732183   -0.152500    0.341100    0.4730900
+49690.00  -0.152865   0.343888   0.4708950   -0.152900    0.343200    0.4707300
+49691.00  -0.152737   0.346018   0.4685954   -0.152900    0.345300    0.4684300
+49692.00  -0.152319   0.348133   0.4662553   -0.152600    0.347400    0.4661100
+49693.00  -0.151855   0.350338   0.4638417   -0.152200    0.349600    0.4637200
+49694.00  -0.151502   0.352762   0.4613657   -0.151900    0.352100    0.4612800
+49695.00  -0.151377   0.355413   0.4588597   -0.151700    0.354700    0.4587800
+49696.00  -0.151519   0.358227   0.4563534   -0.151700    0.357600    0.4562800
+49697.00  -0.152018   0.361091   0.4538684   -0.152000    0.360500    0.4537800
+49698.00  -0.152813   0.363880   0.4514189   -0.152600    0.363300    0.4513200
+49699.00  -0.153503   0.366531   0.4490111   -0.153200    0.366000    0.4489200
+49700.00  -0.153781   0.369106   0.4466544   -0.153800    0.368500    0.4465900
+49701.00  -0.153973   0.371747   0.4443857   -0.154400    0.370900    0.4443300
+49702.00  -0.154240   0.374469   0.4421722   -0.154900    0.373300    0.4421100
+49703.00  -0.154662   0.377245   0.4399678   -0.155400    0.375900    0.4398900
+49704.00  -0.155272   0.380014   0.4377180   -0.156000    0.378700    0.4376300
+49705.00  -0.156060   0.382715   0.4353743   -0.156700    0.381500    0.4352700
+49706.00  -0.156932   0.385263   0.4328925   -0.157400    0.384300    0.4327900
+49707.00  -0.157709   0.387602   0.4302489   -0.158000    0.386800    0.4301800
+49708.00  -0.158271   0.389863   0.4274840   -0.158600    0.389100    0.4274400
+49709.00  -0.158674   0.392127   0.4246131   -0.159000    0.391400    0.4245700
+49710.00  -0.158946   0.394483   0.4216396   -0.159200    0.393700    0.4215900
+49711.00  -0.159032   0.397043   0.4185905   -0.159300    0.396200    0.4185400
+49712.00  -0.158912   0.399861   0.4155133   -0.159200    0.399100    0.4154600
+49713.00  -0.158656   0.402918   0.4124665   -0.159000    0.402200    0.4124000
+49714.00  -0.158336   0.406112   0.4095072   -0.158700    0.405500    0.4094300
+49715.00  -0.157891   0.409294   0.4066683   -0.158100    0.408700    0.4065700
+49716.00  -0.156905   0.412426   0.4039580   -0.157100    0.411700    0.4038300
+49717.00  -0.155298   0.415519   0.4013094   -0.155600    0.414800    0.4011600
+49718.00  -0.153189   0.418559   0.3986542   -0.153600    0.417700    0.3985000
+49719.00  -0.150825   0.421482   0.3959327   -0.151300    0.420500    0.3957900
+49720.00  -0.148493   0.424222   0.3931052   -0.148900    0.423300    0.3929800
+49721.00  -0.146361   0.426771   0.3901717   -0.146700    0.426000    0.3900700
+49722.00  -0.144479   0.429174   0.3871759   -0.144700    0.428500    0.3870900
+49723.00  -0.142828   0.431398   0.3841933   -0.143100    0.430800    0.3841100
+49724.00  -0.141374   0.433434   0.3812858   -0.141600    0.432800    0.3812000
+49725.00  -0.140045   0.435337   0.3785005   -0.140300    0.434700    0.3784200
+49726.00  -0.138689   0.437218   0.3758672   -0.138900    0.436700    0.3757900
+49727.00  -0.137139   0.439265   0.3734000   -0.137400    0.438800    0.3733300
+49728.00  -0.135392   0.441655   0.3710942   -0.135800    0.441100    0.3710100
+49729.00  -0.133709   0.444245   0.3689076   -0.134100    0.443500    0.3688100
+49730.00  -0.132167   0.446876   0.3667940   -0.132600    0.446100    0.3666900
+49731.00  -0.130820   0.449483   0.3647144   -0.131200    0.448700    0.3646100
+49732.00  -0.129771   0.452053   0.3626227   -0.130200    0.451300    0.3625200
+49733.00  -0.129050   0.454549   0.3604814   -0.129400    0.453800    0.3603800
+49734.00  -0.128552   0.456895   0.3582618   -0.128800    0.456200    0.3581600
+49735.00  -0.128075   0.459036   0.3559323   -0.128200    0.458300    0.3558300
+49736.00  -0.127274   0.460979   0.3534489   -0.127500    0.460200    0.3533500
+49737.00  -0.126173   0.462741   0.3508263   -0.126600    0.461900    0.3507400
+49738.00  -0.124927   0.464397   0.3480913   -0.125400    0.463400    0.3480000
+49739.00  -0.123597   0.466094   0.3452808   -0.124000    0.465200    0.3452000
+49740.00  -0.122176   0.468000   0.3424368   -0.122500    0.467100    0.3423600
+49741.00  -0.120697   0.470190   0.3396079   -0.120900    0.469400    0.3395300
+49742.00  -0.119243   0.472586   0.3368351   -0.119300    0.471900    0.3367500
+49743.00  -0.117856   0.475101   0.3341227   -0.117900    0.474400    0.3340300
+49744.00  -0.116658   0.477515   0.3314510   -0.116700    0.476800    0.3313600
+49745.00  -0.115778   0.479680   0.3287997   -0.115800    0.479000    0.3287100
+49746.00  -0.115216   0.481614   0.3261178   -0.115300    0.480900    0.3260400
+49747.00  -0.114986   0.483338   0.3233651   -0.115200    0.482600    0.3233000
+49748.00  -0.115087   0.484913   0.3205334   -0.115400    0.484200    0.3204800
+49749.00  -0.115431   0.486423   0.3176520   -0.115900    0.485700    0.3175900
+49750.00  -0.115770   0.487929   0.3147682   -0.116200    0.487200    0.3146800
+49751.00  -0.115750   0.489384   0.3119068   -0.116300    0.488600    0.3118000
+49752.00  -0.115037   0.490757   0.3090844   -0.115800    0.489900    0.3089800
+49753.00  -0.113739   0.492107   0.3063359   -0.114600    0.491300    0.3062500
+49754.00  -0.111844   0.493562   0.3036856   -0.112700    0.492800    0.3036200
+49755.00  -0.109299   0.495255   0.3011561   -0.110100    0.494600    0.3011100
+49756.00  -0.106276   0.497280   0.2987650   -0.107000    0.496600    0.2987300
+49757.00  -0.103092   0.499653   0.2965034   -0.103800    0.498900    0.2964500
+49758.00  -0.100159   0.502148   0.2943105   -0.100900    0.501300    0.2942500
+49759.00  -0.097808   0.504465   0.2921455   -0.098400    0.503500    0.2920700
+49760.00  -0.096094   0.506447   0.2899714   -0.096600    0.505500    0.2899000
+49761.00  -0.094893   0.508087   0.2877376   -0.095200    0.507300    0.2876600
+49762.00  -0.093920   0.509472   0.2853989   -0.094100    0.508700    0.2853100
+49763.00  -0.092755   0.510757   0.2829181   -0.092800    0.510100    0.2828300
+49764.00  -0.091000   0.512165   0.2802679   -0.091300    0.511400    0.2801800
+49765.00  -0.088903   0.513700   0.2774850   -0.089500    0.512800    0.2774100
+49766.00  -0.086820   0.515241   0.2746413   -0.087700    0.514200    0.2745700
+49767.00  -0.084959   0.516735   0.2718026   -0.085900    0.515600    0.2717300
+49768.00  -0.083343   0.518078   0.2690284   -0.084200    0.517000    0.2689500
+49769.00  -0.081869   0.519239   0.2663575   -0.082600    0.518400    0.2662800
+49770.00  -0.080396   0.520331   0.2637974   -0.081000    0.519800    0.2637300
+49771.00  -0.078734   0.521570   0.2613248   -0.079200    0.521300    0.2612700
+49772.00  -0.076638   0.523062   0.2588847   -0.077000    0.522800    0.2588400
+49773.00  -0.073891   0.524836   0.2564009   -0.074300    0.524500    0.2563800
+49774.00  -0.070608   0.526809   0.2538179   -0.071300    0.526200    0.2538100
+49775.00  -0.067041   0.528787   0.2511060   -0.067900    0.528000    0.2511100
+49776.00  -0.063404   0.530614   0.2482696   -0.064300    0.529700    0.2482600
+49777.00  -0.059834   0.532244   0.2453351   -0.060400    0.531300    0.2452600
+49778.00  -0.056421   0.533667   0.2423301   -0.057000    0.532700    0.2422100
+49779.00  -0.053150   0.534943   0.2392855   -0.053700    0.534200    0.2391700
+49780.00  -0.049859   0.536159   0.2362316   -0.050300    0.535400    0.2361300
+49781.00  -0.046533   0.537369   0.2332219   -0.047000    0.536600    0.2331600
+49782.00  -0.043210   0.538620   0.2303048   -0.043600    0.537800    0.2302700
+49783.00  -0.039960   0.539942   0.2275100   -0.040400    0.539000    0.2274800
+49784.00  -0.036857   0.541301   0.2248388   -0.037300    0.540300    0.2247700
+49785.00  -0.034054   0.542584   0.2222167   -0.034500    0.541600    0.2221100
+49786.00  -0.031586   0.543697   0.2195729   -0.032000    0.542800    0.2194600
+49787.00  -0.029449   0.544584   0.2168735   -0.029900    0.543800    0.2167900
+49788.00  -0.027596   0.545229   0.2141008   -0.028000    0.544400    0.2140400
+49789.00  -0.025967   0.545574   0.2112397   -0.026300    0.544800    0.2112100
+49790.00  -0.024318   0.545700   0.2082900   -0.024500    0.545000    0.2082700
+49791.00  -0.022230   0.545858   0.2052493   -0.022400    0.545200    0.2052000
+49792.00  -0.019570   0.546291   0.2020894   -0.019900    0.545500    0.2020200
+49793.00  -0.016638   0.547022   0.1988327   -0.017100    0.546100    0.1987600
+49794.00  -0.013712   0.547927   0.1955371   -0.014100    0.547000    0.1954600
+49795.00  -0.010970   0.548911   0.1922824   -0.011300    0.548000    0.1922100
+49796.00  -0.008609   0.549912   0.1891428   -0.009000    0.549000    0.1890800
+49797.00  -0.006664   0.550852   0.1861914   -0.007200    0.550000    0.1861400
+49798.00  -0.004915   0.551625   0.1834651   -0.005500    0.550700    0.1834100
+49799.00  -0.003080   0.552167   0.1809253   -0.003700    0.551200    0.1808400
+49800.00  -0.000718   0.552558   0.1784517   -0.001100    0.551600    0.1783400
+49801.00   0.002665   0.552945   0.1759392    0.002200    0.552000    0.1758500
+49802.00   0.007068   0.553526   0.1733344    0.006500    0.552600    0.1732600
+49803.00   0.012125   0.554424   0.1706200    0.011300    0.553600    0.1705600
+49804.00   0.017209   0.555609   0.1678072    0.016300    0.554800    0.1677500
+49805.00   0.021783   0.556881   0.1649300    0.021000    0.556000    0.1648600
+49806.00   0.025737   0.557853   0.1620269    0.025100    0.557000    0.1619500
+49807.00   0.029145   0.558411   0.1591455    0.028700    0.557600    0.1590500
+49808.00   0.032223   0.558606   0.1563266    0.031800    0.557800    0.1562300
+49809.00   0.035253   0.558563   0.1535919    0.034800    0.557700    0.1535000
+49810.00   0.038395   0.558415   0.1509506    0.037700    0.557500    0.1508600
+49811.00   0.041610   0.558191   0.1484005    0.041000    0.557300    0.1483300
+49812.00   0.044799   0.557877   0.1459346    0.044300    0.557000    0.1458700
+49813.00   0.048055   0.557657   0.1435147    0.047800    0.556900    0.1434500
+49814.00   0.051330   0.557646   0.1410720    0.051100    0.556800    0.1410100
+49815.00   0.054471   0.557796   0.1385650    0.054200    0.557000    0.1385200
+49816.00   0.057417   0.557972   0.1359808    0.057100    0.557200    0.1359400
+49817.00   0.060287   0.558056   0.1333095    0.059800    0.557300    0.1332500
+49818.00   0.063186   0.557998   0.1305124    0.062500    0.557300    0.1304400
+49819.00   0.066106   0.557795   0.1275648    0.065300    0.557100    0.1274900
+49820.00   0.069162   0.557477   0.1244732    0.068300    0.556600    0.1244100
+49821.00   0.072319   0.557059   0.1213199    0.071600    0.556200    0.1212700
+49822.00   0.075646   0.556623   0.1181739    0.075100    0.555700    0.1181200
+49823.00   0.079115   0.556274   0.1150863    0.078700    0.555400    0.1150300
+49824.00   0.082588   0.556036   0.1120940    0.082200    0.555100    0.1120400
+49825.00   0.085904   0.555871   0.1092209    0.085600    0.555000    0.1091800
+49826.00   0.088995   0.555759   0.1064576    0.088700    0.555000    0.1064300
+49827.00   0.091859   0.555687   0.1037831    0.091600    0.554900    0.1037200
+49828.00   0.094840   0.555488   0.1011338    0.094700    0.554700    0.1010300
+49829.00   0.098282   0.555027   0.0984390    0.097900    0.554300    0.0983000
+49830.00   0.101923   0.554313   0.0956750    0.101300    0.553600    0.0955200
+49831.00   0.105677   0.553421   0.0928361    0.105100    0.552800    0.0926800
+49832.00   0.109648   0.552409   0.0899362    0.109200    0.551800    0.0897900
+49833.00   0.113828   0.551295   0.0870060    0.113400    0.550600    0.0869000
+49834.00   0.118052   0.550083   0.0841042    0.117600    0.549400    0.0840200
+49835.00   0.122201   0.548781   0.0812932    0.121800    0.548000    0.0812200
+49836.00   0.126200   0.547400   0.0786103    0.125900    0.546600    0.0785400
+49837.00   0.130004   0.545963   0.0760682    0.129800    0.545100    0.0759800
+49838.00   0.133625   0.544472   0.0736575    0.133600    0.543700    0.0735500
+49839.00   0.137151   0.542978   0.0713226    0.137100    0.542200    0.0712000
+49840.00   0.140652   0.541540   0.0689971    0.140500    0.540700    0.0688500
+49841.00   0.144150   0.540101   0.0666340    0.144000    0.539100    0.0664900
+49842.00   0.147689   0.538598   0.0642119    0.147600    0.537600    0.0640600
+49843.00   0.151386   0.537040   0.0617241    0.151400    0.536000    0.0615800
+49844.00   0.155353   0.535497   0.0591702    0.155400    0.534400    0.0590300
+49845.00   0.159560   0.533999   0.0565425    0.159500    0.533000    0.0564200
+49846.00   0.163824   0.532527   0.0538021    0.163600    0.531600    0.0536900
+49847.00   0.167886   0.531033   0.0509297    0.167600    0.530200    0.0508200
+49848.00   0.171598   0.529473   0.0479442    0.171400    0.528700    0.0478400
+49849.00   0.174931   0.527820   0.0449067    0.175000    0.527100    0.0448200
+49850.00   0.178042   0.526120   0.0419048    0.178400    0.525500    0.0418100
+49851.00   0.181104   0.524458   0.0389846    0.181500    0.523800    0.0388900
+49852.00   0.184123   0.522872   0.0361612    0.184300    0.522100    0.0360800
+49853.00   0.187027   0.521341   0.0334480    0.186900    0.520400    0.0333900
+49854.00   0.189688   0.519782   0.0308279    0.189300    0.518700    0.0307800
+49855.00   0.191958   0.518055   0.0282646    0.191600    0.516900    0.0282100
+49856.00   0.193890   0.516107   0.0257150    0.193700    0.514900    0.0256500
+49857.00   0.195623   0.513903   0.0231537    0.195700    0.512800    0.0230700
+49858.00   0.197336   0.511469   0.0205831    0.197600    0.510400    0.0204900
+49859.00   0.199225   0.508960   0.0180327    0.199500    0.508100    0.0179500
+49860.00   0.201339   0.506500   0.0155409    0.201300    0.505700    0.0154600
+49861.00   0.203530   0.504116   0.0131463    0.203100    0.503400    0.0130500
+49862.00   0.205615   0.501788   0.0108704    0.205000    0.501000    0.0107500
+49863.00   0.207613   0.499451   0.0086859    0.207000    0.498600    0.0085600
+49864.00   0.209581   0.497026   0.0065837    0.209000    0.496100    0.0064700
+49865.00   0.211530   0.494489   0.0045724    0.211100    0.493600    0.0044800
+49866.00   0.213501   0.491824   0.0026326    0.213200    0.491000    0.0025600
+49867.00   0.215531   0.489023   0.0007373    0.215300    0.488400    0.0006700
+49868.00   0.217624   0.486128  -0.0011437    0.217400    0.485500   -0.0012200
+49869.00   0.219839   0.483227  -0.0030435    0.219700    0.482600   -0.0031400
+49870.00   0.222301   0.480281  -0.0050071    0.222200    0.479500   -0.0051200
+49871.00   0.225108   0.477193  -0.0070698    0.224900    0.476300   -0.0071900
+49872.00   0.228205   0.473977  -0.0092459    0.227900    0.473100   -0.0093500
+49873.00   0.231450   0.470731  -0.0115205    0.231000    0.469800   -0.0116100
+49874.00   0.234633   0.467602  -0.0138775    0.234200    0.466700   -0.0139500
+49875.00   0.237641   0.464714  -0.0162951    0.237400    0.463800   -0.0163700
+49876.00   0.240594   0.462046  -0.0187550    0.240300    0.461000   -0.0188200
+49877.00   0.243439   0.459261  -0.0212012    0.243100    0.458200   -0.0212500
+49878.00   0.246051   0.456323  -0.0235719    0.245600    0.455300   -0.0236100
+49879.00   0.248386   0.453343  -0.0258309    0.247900    0.452300   -0.0258700
+49880.00   0.250452   0.450340  -0.0279733    0.250100    0.449300   -0.0280200
+49881.00   0.252350   0.447248  -0.0300289    0.252100    0.446300   -0.0300900
+49882.00   0.254267   0.444027  -0.0320660    0.254200    0.443200   -0.0321300
+49883.00   0.256310   0.440739  -0.0341466    0.256200    0.440000   -0.0342100
+49884.00   0.258509   0.437481  -0.0362715    0.258400    0.436800   -0.0363500
+49885.00   0.260839   0.434277  -0.0384633    0.260600    0.433600   -0.0385600
+49886.00   0.263215   0.431106  -0.0407064    0.262900    0.430300   -0.0408200
+49887.00   0.265545   0.427896  -0.0429534    0.265100    0.427000   -0.0430700
+49888.00   0.267697   0.424553  -0.0451547    0.267300    0.423600   -0.0452600
+49889.00   0.269577   0.421018  -0.0472665    0.269200    0.420100   -0.0473600
+49890.00   0.271154   0.417398  -0.0492487    0.270800    0.416500   -0.0493200
+49891.00   0.272392   0.413796  -0.0510626    0.272100    0.412900   -0.0511200
+49892.00   0.273290   0.410267  -0.0526763    0.273000    0.409400   -0.0527200
+49893.00   0.273966   0.406811  -0.0540931    0.273700    0.405800   -0.0541400
+49894.00   0.274637   0.403337  -0.0553614    0.274400    0.402300   -0.0554100
+49895.00   0.275453   0.399721  -0.0565304    0.275100    0.398700   -0.0565800
+49896.00   0.276446   0.395963  -0.0576556    0.276100    0.395100   -0.0577100
+49897.00   0.277574   0.392210  -0.0588041    0.277300    0.391400   -0.0588700
+49898.00   0.278839   0.388463  -0.0600344    0.278600    0.387600   -0.0601100
+49899.00   0.280135   0.384623  -0.0613867    0.279900    0.383700   -0.0614700
+49900.00   0.281307   0.380660  -0.0628822    0.281100    0.379700   -0.0629700
+49901.00   0.282279   0.376569  -0.0645201    0.282100    0.375600   -0.0646000
+49902.00   0.283155   0.372366  -0.0662537    0.283000    0.371400   -0.0663200
+49903.00   0.284080   0.368160  -0.0680185    0.283900    0.367200   -0.0680800
+49904.00   0.284998   0.364071  -0.0697585    0.284700    0.363000   -0.0698300
+49905.00   0.285752   0.360096  -0.0714445    0.285500    0.358900   -0.0715200
+49906.00   0.286264   0.356144  -0.0730519    0.286100    0.354900   -0.0731300
+49907.00   0.286592   0.352185  -0.0745701    0.286500    0.351000   -0.0746500
+49908.00   0.286847   0.348271  -0.0760160    0.286800    0.347200   -0.0760900
+49909.00   0.287167   0.344456  -0.0774314    0.287100    0.343500   -0.0774900
+49910.00   0.287522   0.340770  -0.0788750    0.287200    0.339900   -0.0789200
+49911.00   0.287691   0.337184  -0.0803920    0.287400    0.336400   -0.0804300
+49912.00   0.287724   0.333635  -0.0819965    0.287400    0.332800   -0.0820500
+49913.00   0.287795   0.330040  -0.0836972    0.287500    0.329200   -0.0837700
+49914.00   0.287949   0.326344  -0.0854542    0.287600    0.325500   -0.0855200
+49915.00   0.288082   0.322556  -0.0872007    0.287700    0.321700   -0.0872600
+49916.00   0.288062   0.318743  -0.0888665    0.287800    0.317800   -0.0889200
+49917.00   0.287868   0.314950  -0.0904009    0.287700    0.313900   -0.0904600
+49918.00   0.287689   0.311145  -0.0917787    0.287600    0.309900   -0.0918600
+49919.00   0.287565   0.307292  -0.0929977    0.287400    0.306000   -0.0931000
+49920.00   0.287387   0.303396  -0.0940641    0.287100    0.302100   -0.0941900
+49921.00   0.287031   0.299471  -0.0950056    0.286700    0.298300   -0.0951400
+49922.00   0.286566   0.295521  -0.0958652    0.286300    0.294500   -0.0959900
+49923.00   0.286095   0.291575  -0.0966853    0.285800    0.290600   -0.0968000
+49924.00   0.285505   0.287660  -0.0975064    0.285100    0.286800   -0.0975900
+49925.00   0.284645   0.283792  -0.0983658    0.284300    0.282900   -0.0984300
+49926.00   0.283658   0.280001  -0.0992918    0.283300    0.279100   -0.0993500
+49927.00   0.282595   0.276263  -0.1003121    0.282200    0.275300   -0.1003800
+49928.00   0.281448   0.272477  -0.1014508    0.281100    0.271500   -0.1015400
+49929.00   0.280368   0.268586  -0.1027208    0.280100    0.267700   -0.1028100
+49930.00   0.279541   0.264644  -0.1041002    0.279200    0.263800   -0.1041800
+49931.00   0.278952   0.260708  -0.1055396    0.278500    0.259900   -0.1056100
+49932.00   0.278360   0.256799  -0.1069717    0.277900    0.255900   -0.1070400
+49933.00   0.277631   0.252861  -0.1083570    0.277200    0.251900   -0.1084400
+49934.00   0.276791   0.248912  -0.1096881    0.276500    0.248000   -0.1097900
+49935.00   0.275892   0.245043  -0.1109876    0.275900    0.244100   -0.1110900
+49936.00   0.274915   0.241303  -0.1122929    0.274800    0.240400   -0.1123800
+49937.00   0.273771   0.237673  -0.1136461    0.273500    0.236700   -0.1137100
+49938.00   0.272359   0.234099  -0.1150900    0.272100    0.233100   -0.1151400
+49939.00   0.270663   0.230520  -0.1166522    0.270600    0.229600   -0.1167000
+49940.00   0.269001   0.226934  -0.1183333    0.269100    0.226000   -0.1183800
+49941.00   0.267541   0.223342  -0.1200937    0.267600    0.222500   -0.1201600
+49942.00   0.266166   0.219764  -0.1218769    0.266100    0.218900   -0.1219600
+49943.00   0.264644   0.216284  -0.1236236    0.264400    0.215300   -0.1237100
+49944.00   0.262691   0.212933  -0.1252897    0.262400    0.211900   -0.1253800
+49945.00   0.260241   0.209642  -0.1268567    0.260100    0.208500   -0.1269500
+49946.00   0.257691   0.206449  -0.1283241    0.257700    0.205300   -0.1284100
+49947.00   0.255336   0.203412  -0.1296985    0.255300    0.202300   -0.1297800
+49948.00   0.253179   0.200506  -0.1310131    0.253000    0.199500   -0.1311000
+49949.00   0.251097   0.197685  -0.1323117    0.250800    0.196800   -0.1324000
+49950.00   0.249107   0.194904  -0.1336314    0.248900    0.194000   -0.1337000
+49951.00   0.247201   0.192091  -0.1350051    0.246900    0.191200   -0.1350600
+49952.00   0.245183   0.189208  -0.1364567    0.244800    0.188400   -0.1364900
+49953.00   0.242878   0.186348  -0.1379952    0.242600    0.185400   -0.1380200
+49954.00   0.240438   0.183450  -0.1396425    0.240300    0.182400   -0.1396900
+49955.00   0.238156   0.180477  -0.1414378    0.238000    0.179500   -0.1414900
+49956.00   0.236101   0.177546  -0.1433880    0.235800    0.176700   -0.1434400
+49957.00   0.234106   0.174753  -0.1454513    0.233600    0.173900   -0.1454900
+49958.00   0.232124   0.172120  -0.1475858    0.231500    0.171200   -0.1476000
+49959.00   0.229971   0.169548  -0.1496976    0.229300    0.168500   -0.1497300
+49960.00   0.227437   0.166958  -0.1517621    0.227000    0.165700   -0.1518200
+49961.00   0.224672   0.164364  -0.1537776    0.224600    0.163100   -0.1538500
+49962.00   0.221943   0.161813  -0.1557561    0.222000    0.160600   -0.1558300
+49963.00   0.219408   0.159389  -0.1577167    0.219600    0.158400   -0.1577800
+49964.00   0.217128   0.157140  -0.1596912    0.217200    0.156300   -0.1597400
+49965.00   0.215002   0.155058  -0.1617171    0.214800    0.154300   -0.1617900
+49966.00   0.212740   0.153069  -0.1638234    0.212500    0.152300   -0.1638800
+49967.00   0.210029   0.151075  -0.1660164    0.209800    0.150200   -0.1660900
+49968.00   0.206969   0.148852  -0.1682977    0.206900    0.147800   -0.1684000
+49969.00   0.203795   0.146375  -0.1706431    0.203600    0.145200   -0.1707500
+49970.00   0.200570   0.143742  -0.1729846    0.200300    0.142400   -0.1730900
+49971.00   0.197305   0.140984  -0.1752536    0.197000    0.139500   -0.1753400
+49972.00   0.194074   0.138110  -0.1774091    0.193700    0.136700   -0.1774800
+49973.00   0.191035   0.135293  -0.1794394    0.190800    0.134100   -0.1794900
+49974.00   0.188269   0.132860  -0.1813611    0.188000    0.131900   -0.1814000
+49975.00   0.185662   0.130861  -0.1832127    0.185400    0.129800   -0.1832300
+49976.00   0.182997   0.129117  -0.1850363    0.182800    0.127900   -0.1850200
+49977.00   0.180140   0.127395  -0.1868638    0.180200    0.126100   -0.1868200
+49978.00   0.177142   0.125569  -0.1887169    0.177500    0.124300   -0.1886600
+49979.00   0.174227   0.123629  -0.1906144    0.174600    0.122600   -0.1905900
+49980.00   0.171426   0.121661  -0.1925842    0.171500    0.120800   -0.1926200
+49981.00   0.168433   0.119799  -0.1946974    0.168300    0.119100   -0.1947900
+49982.00   0.165227   0.118066  -0.1969832    0.165000    0.117300   -0.1971000
+49983.00   0.161888   0.116469  -0.1994317    0.161700    0.115600   -0.1995500
+49984.00   0.158666   0.114917  -0.2020139    0.158400    0.113900   -0.2021200
+49985.00   0.155648   0.113415  -0.2046817    0.155400    0.112400   -0.2047700
+49986.00   0.152758   0.112022  -0.2073726    0.152500    0.111000   -0.2074300
+49987.00   0.149886   0.110801  -0.2100182    0.149700    0.109800   -0.2100500
+49988.00   0.147073   0.109840  -0.2125541    0.146900    0.108800   -0.2125800
+49989.00   0.144280   0.108970  -0.2149738    0.144100    0.107800   -0.2150100
+49990.00   0.141336   0.108038  -0.2173092    0.141100    0.106900   -0.2173700
+49991.00   0.138111   0.107025  -0.2196143    0.137900    0.105900   -0.2196900
+49992.00   0.134567   0.105987  -0.2219501    0.134300    0.105000   -0.2220200
+49993.00   0.130695   0.105001  -0.2243638    0.130600    0.104100   -0.2244200
+49994.00   0.126575   0.104045  -0.2268727    0.126600    0.103100   -0.2269100
+49995.00   0.122609   0.103062  -0.2294525    0.122500    0.102000   -0.2295000
+49996.00   0.118773   0.101939  -0.2320905    0.118500    0.100900   -0.2321600
+49997.00   0.114860   0.100643  -0.2347659    0.114600    0.099600   -0.2348500
+49998.00   0.110837   0.099201  -0.2374358    0.110600    0.098100   -0.2375100
+49999.00   0.106690   0.097625  -0.2400381    0.106400    0.096500   -0.2401000
+50000.00   0.102471   0.095946  -0.2425086    0.102200    0.094900   -0.2425500
+50001.00   0.098323   0.094278  -0.2448171    0.098200    0.093200   -0.2448700
+50002.00   0.094472   0.092742  -0.2469890    0.094400    0.091700   -0.2470300
+50003.00   0.090997   0.091469  -0.2490365    0.090900    0.090200   -0.2490800
+50004.00   0.087895   0.090594  -0.2509869    0.087800    0.089400   -0.2510300
+50005.00   0.084727   0.090020  -0.2528981    0.084800    0.088800   -0.2529600
+50006.00   0.081140   0.089428  -0.2548170    0.081100    0.088200   -0.2548900
+50007.00   0.077223   0.088633  -0.2567910    0.077100    0.087500   -0.2568600
+50008.00   0.073319   0.087696  -0.2588592    0.072900    0.086700   -0.2589200
+50009.00   0.069832   0.086810  -0.2610189    0.069400    0.085700   -0.2610700
+50010.00   0.066975   0.086104  -0.2632595    0.066600    0.084900   -0.2633200
+50011.00   0.064561   0.085696  -0.2656028    0.064500    0.084600   -0.2656600
+50012.00   0.062087   0.085544  -0.2680337    0.062100    0.084500   -0.2680800
+50013.00   0.059125   0.085440  -0.2705006    0.059000    0.084300   -0.2705500
+50014.00   0.055535   0.085180  -0.2729630    0.055100    0.084100   -0.2730200
+50015.00   0.051426   0.084676  -0.2753887    0.051100    0.083700   -0.2754500
+50016.00   0.047101   0.084034  -0.2777537    0.046900    0.083200   -0.2778100
+50017.00   0.042802   0.083316  -0.2800474    0.042600    0.082400   -0.2800900
+50018.00   0.038761   0.082671  -0.2823000    0.038600    0.081800   -0.2823500
+50019.00   0.035078   0.082295  -0.2845748    0.034900    0.081400   -0.2846300
+50020.00   0.031665   0.082253  -0.2869319    0.031500    0.081300   -0.2870000
+50021.00   0.028365   0.082439  -0.2894134    0.028200    0.081400   -0.2894800
+50022.00   0.025113   0.082725  -0.2920343    0.025000    0.081700   -0.2921000
+50023.00   0.021946   0.083061  -0.2947758    0.021800    0.082100   -0.2948200
+50024.00   0.018673   0.083419  -0.2975496    0.018300    0.082400   -0.2975700
+50025.00   0.015135   0.083780  -0.3002725    0.014900    0.082700   -0.3003000
+50026.00   0.011387   0.084118  -0.3028973    0.011200    0.083000   -0.3029300
+50027.00   0.007541   0.084428  -0.3053956    0.007600    0.083300   -0.3054500
+50028.00   0.003739   0.084751  -0.3077684    0.003700    0.083600   -0.3078300
+50029.00   0.000001   0.085124  -0.3100311   -0.000200    0.083900   -0.3100700
+50030.00  -0.003744   0.085488  -0.3121888   -0.004100    0.084300   -0.3122000
+50031.00  -0.007475   0.085790  -0.3142379   -0.007800    0.084600   -0.3142500
+50032.00  -0.011231   0.086077  -0.3162171   -0.011500    0.084900   -0.3162400
+50033.00  -0.014987   0.086408  -0.3181895   -0.015200    0.085300   -0.3182300
+50034.00  -0.018569   0.086804  -0.3202225   -0.018800    0.085700   -0.3202700
+50035.00  -0.021862   0.087327  -0.3223659   -0.022300    0.086300   -0.3224300
+50036.00  -0.025065   0.088127  -0.3246626   -0.025800    0.087100   -0.3247200
+50037.00  -0.028782   0.089292  -0.3271535   -0.029500    0.088100   -0.3271800
+50038.00  -0.033035   0.090654  -0.3298032   -0.033600    0.089400   -0.3298000
+50039.00  -0.037476   0.092091  -0.3325547   -0.037800    0.090800   -0.3325600
+50040.00  -0.041719   0.093516  -0.3353883   -0.042100    0.092200   -0.3354100
+50041.00  -0.045724   0.094850  -0.3382631   -0.046300    0.093600   -0.3383100
+50042.00  -0.049751   0.096014  -0.3411295   -0.050400    0.094800   -0.3411800
+50043.00  -0.053914   0.096986  -0.3439446   -0.054400    0.096000   -0.3440000
+50044.00  -0.057976   0.097979  -0.3466769   -0.058300    0.097200   -0.3467200
+50045.00  -0.061727   0.099130  -0.3493369   -0.061900    0.098300   -0.3493700
+50046.00  -0.065201   0.100487  -0.3519652   -0.065400    0.099600   -0.3519800
+50047.00  -0.068566   0.102026  -0.3546099   -0.068900    0.101000   -0.3546300
+50048.00  -0.071992   0.103675  -0.3573086   -0.072400    0.102600   -0.3573300
+50049.00  -0.075682   0.105386  -0.3600689   -0.076300    0.104400   -0.3601100
+50050.00  -0.079882   0.107134  -0.3628755   -0.080600    0.106100   -0.3629200
+50051.00  -0.084668   0.108887  -0.3656937   -0.085300    0.107800   -0.3657400
+50052.00  -0.089793   0.110581  -0.3684577   -0.090300    0.109500   -0.3685100
+50053.00  -0.094910   0.112190  -0.3711199   -0.095300    0.111200   -0.3711900
+50054.00  -0.099728   0.113810  -0.3736715   -0.100100    0.112800   -0.3737600
+50055.00  -0.104255   0.115578  -0.3761191   -0.104800    0.114500   -0.3762000
+50056.00  -0.108571   0.117528  -0.3784535   -0.109200    0.116400   -0.3785200
+50057.00  -0.112580   0.119567  -0.3806794   -0.113200    0.118500   -0.3807300
+50058.00  -0.116132   0.121605  -0.3828140   -0.116600    0.120700   -0.3828500
+50059.00  -0.119291   0.123582  -0.3849046   -0.119700    0.122700   -0.3849300
+50060.00  -0.122360   0.125522  -0.3870040   -0.122600    0.124600   -0.3870300
+50061.00  -0.125428   0.127453  -0.3891584   -0.125800    0.126400   -0.3891800
+50062.00  -0.128714   0.129439  -0.3913900   -0.129100    0.128400   -0.3914100
+50063.00  -0.132294   0.131545  -0.3937117   -0.132600    0.130400   -0.3937500
+50064.00  -0.135762   0.133872  -0.3961394   -0.135900    0.132800   -0.3961900
+50065.00  -0.138554   0.136542  -0.3986872   -0.138900    0.135500   -0.3987300
+50066.00  -0.140929   0.139485  -0.4013079   -0.141300    0.138400   -0.4013500
+50067.00  -0.143210   0.142573  -0.4039535   -0.143500    0.141400   -0.4040000
+50068.00  -0.145513   0.145731  -0.4066048   -0.145700    0.144600   -0.4066600
+50069.00  -0.147835   0.148873  -0.4092583   -0.148000    0.147700   -0.4093200
+50070.00  -0.150201   0.151908  -0.4118837   -0.150400    0.150800   -0.4119500
+50071.00  -0.152541   0.154799  -0.4144558   -0.152900    0.153700   -0.4145200
+50072.00  -0.154776   0.157666  -0.4169546   -0.155200    0.156600   -0.4170000
+50073.00  -0.156826   0.160665  -0.4193989   -0.157100    0.159500   -0.4194400
+50074.00  -0.158669   0.163912  -0.4218509   -0.158900    0.162800   -0.4218800
+50075.00  -0.160459   0.167387  -0.4243901   -0.160700    0.166200   -0.4244100
+50076.00  -0.162335   0.170951  -0.4270547   -0.162700    0.169900   -0.4270700
+50077.00  -0.164330   0.174443  -0.4298117   -0.164800    0.173400   -0.4298400
+50078.00  -0.166407   0.177742  -0.4325949   -0.167000    0.176600   -0.4326300
+50079.00  -0.168495   0.180798  -0.4353285   -0.169000    0.179500   -0.4353800
+50080.00  -0.170527   0.183633  -0.4379429   -0.170900    0.182300   -0.4379900
+50081.00  -0.172465   0.186350  -0.4403853   -0.172700    0.185100   -0.4404300
+50082.00  -0.174304   0.189077  -0.4426252   -0.174500    0.188000   -0.4426700
+50083.00  -0.175934   0.191874   0.5553429   -0.176200    0.190800    0.5552900
+50084.00  -0.177203   0.194733   0.5534992   -0.177700    0.193700    0.5534400
+50085.00  -0.178219   0.197661   0.5518040   -0.178700    0.196600    0.5517600
+50086.00  -0.179271   0.200769   0.5502108   -0.179700    0.199600    0.5501600
+50087.00  -0.180413   0.204061   0.5486679   -0.180700    0.202900    0.5486300
+50088.00  -0.181716   0.207436   0.5471137   -0.182100    0.206200    0.5471000
+50089.00  -0.183392   0.210670   0.5454827   -0.183900    0.209600    0.5454800
+50090.00  -0.185233   0.213575   0.5437364   -0.185700    0.212600    0.5437200
+50091.00  -0.186902   0.216171   0.5418803   -0.187100    0.215200    0.5418400
+50092.00  -0.188610   0.218633   0.5399478   -0.189000    0.217500    0.5399000
+50093.00  -0.190544   0.221323   0.5379721   -0.191000    0.220100    0.5379200
+50094.00  -0.192621   0.224329   0.5359773   -0.192700    0.223000    0.5359300
+50095.00  -0.194690   0.227472   0.5339774   -0.194800    0.226200    0.5339400
+50096.00  -0.196600   0.230482   0.5319852   -0.197000    0.229500    0.5319200
+50097.00  -0.198256   0.233199   0.5300178   -0.198600    0.232300    0.5299700
+50098.00  -0.199573   0.235746   0.5280928   -0.200000    0.234700    0.5280700
+50099.00  -0.200722   0.238434   0.5262338   -0.201300    0.237100    0.5262100
+50100.00  -0.202042   0.241575   0.5244563   -0.202500    0.240200    0.5244100
+50101.00  -0.203464   0.245190   0.5227037   -0.203700    0.244000    0.5227000
+50102.00  -0.204972   0.249073   0.5208840   -0.205300    0.248100    0.5209000
+50103.00  -0.206515   0.252890   0.5189387   -0.207100    0.252000    0.5189200
+50104.00  -0.208097   0.256437   0.5168645   -0.208500    0.255500    0.5168300
+50105.00  -0.209673   0.259782   0.5147185   -0.209900    0.258800    0.5146800
+50106.00  -0.210988   0.263176   0.5125896   -0.211400    0.262100    0.5125500
+50107.00  -0.211914   0.266751   0.5105347   -0.212400    0.265600    0.5104800
+50108.00  -0.212703   0.270456   0.5085976   -0.213000    0.269400    0.5085400
+50109.00  -0.213735   0.274297   0.5067973   -0.213900    0.273300    0.5067400
+50110.00  -0.215159   0.278183   0.5051407   -0.215500    0.277300    0.5051000
+50111.00  -0.216851   0.281901   0.5036132   -0.217200    0.281000    0.5035800
+50112.00  -0.218493   0.285363   0.5021832   -0.218800    0.284400    0.5021500
+50113.00  -0.219824   0.288726   0.5008057   -0.220200    0.287700    0.5007700
+50114.00  -0.220800   0.292186   0.4994203   -0.221300    0.290900    0.4993900
+50115.00  -0.221440   0.295824   0.4979729   -0.221800    0.294600    0.4979800
+50116.00  -0.221815   0.299586   0.4964256   -0.222200    0.298700    0.4964100
+50117.00  -0.222065   0.303384   0.4947668   -0.222300    0.302300    0.4947300
+50118.00  -0.222381   0.307187   0.4930004   -0.222400    0.305900    0.4929300
+50119.00  -0.222896   0.310942   0.4911259   -0.223200    0.309700    0.4910300
+50120.00  -0.223554   0.314574   0.4891368   -0.223900    0.313300    0.4890800
+50121.00  -0.224342   0.318023   0.4870476   -0.224600    0.316900    0.4870400
+50122.00  -0.225044   0.321171   0.4849035   -0.225300    0.320300    0.4848600
+50123.00  -0.225181   0.323983   0.4827588   -0.225300    0.322900    0.4827400
+50124.00  -0.224580   0.326681   0.4806567   -0.224700    0.325500    0.4806600
+50125.00  -0.223400   0.329470   0.4786206   -0.223900    0.328400    0.4786100
+50126.00  -0.221975   0.332497   0.4766505   -0.222400    0.331400    0.4766200
+50127.00  -0.220650   0.335894   0.4747223   -0.221000    0.334800    0.4746900
+50128.00  -0.219519   0.339747   0.4727933   -0.219800    0.338500    0.4727600
+50129.00  -0.218710   0.344051   0.4707968   -0.219100    0.342700    0.4707600
+50130.00  -0.218381   0.348607   0.4686632   -0.218900    0.347600    0.4686900
+50131.00  -0.218571   0.353126   0.4663566   -0.219000    0.352200    0.4664100
+50132.00  -0.219284   0.357369   0.4638910   -0.219800    0.356500    0.4639200
+50133.00  -0.220255   0.361221   0.4613243   -0.220800    0.360200    0.4613000
+50134.00  -0.221031   0.364694   0.4587397   -0.221300    0.363600    0.4586900
+50135.00  -0.221356   0.367982   0.4562191   -0.221700    0.366900    0.4562000
+50136.00  -0.221356   0.371356   0.4538542   -0.221500    0.369900    0.4538500
+50137.00  -0.221173   0.375065   0.4517135   -0.221400    0.373500    0.4516400
+50138.00  -0.220760   0.379108   0.4497550   -0.221200    0.378000    0.4496300
+50139.00  -0.220267   0.383241   0.4479084   -0.220700    0.382200    0.4477600
+50140.00  -0.219849   0.387230   0.4461162   -0.220100    0.386100    0.4459900
+50141.00  -0.219416   0.391075   0.4443430   -0.219800    0.390000    0.4442800
+50142.00  -0.218720   0.394877   0.4425576   -0.219200    0.393500    0.4425900
+50143.00  -0.217592   0.398724   0.4406961   -0.218100    0.397500    0.4407400
+50144.00  -0.216046   0.402709   0.4387105   -0.216400    0.401800    0.4386800
+50145.00  -0.214256   0.406820   0.4365826   -0.214400    0.405800    0.4365700
+50146.00  -0.212377   0.410989   0.4343027   -0.212700    0.409800    0.4343200
+50147.00  -0.210462   0.415165   0.4318862   -0.210800    0.413800    0.4318900
+50148.00  -0.208540   0.419342   0.4293659   -0.209000    0.418200    0.4293400
+50149.00  -0.206795   0.423380   0.4267812   -0.207100    0.422300    0.4267300
+50150.00  -0.205259   0.427213   0.4241680   -0.205900    0.425900    0.4241600
+50151.00  -0.203908   0.430867   0.4215619   -0.204300    0.429800    0.4216000
+50152.00  -0.202582   0.434377   0.4189899   -0.203000    0.433300    0.4190100
+50153.00  -0.201172   0.437787   0.4164748   -0.201800    0.436600    0.4164900
+50154.00  -0.199681   0.441159   0.4140508   -0.199800    0.440100    0.4140600
+50155.00  -0.198181   0.444555   0.4117438   -0.198600    0.443400    0.4117000
+50156.00  -0.196547   0.448062   0.4095361   -0.197100    0.447000    0.4094800
+50157.00  -0.194698   0.451546   0.4073526   -0.195100    0.450500    0.4073300
+50158.00  -0.192737   0.454954   0.4051230   -0.193100    0.453800    0.4051100
+50159.00  -0.190621   0.458387   0.4028131   -0.191100    0.457400    0.4028100
+50160.00  -0.188103   0.461977   0.4004263   -0.188700    0.460700    0.4004400
+50161.00  -0.185015   0.465952   0.3979922   -0.185500    0.464600    0.3979900
+50162.00  -0.181736   0.470274   0.3955522   -0.182000    0.469400    0.3955200
+50163.00  -0.178926   0.474524   0.3931553   -0.179100    0.473800    0.3931500
+50164.00  -0.176775   0.478401   0.3908599   -0.177000    0.477600    0.3908500
+50165.00  -0.175051   0.481739   0.3886987   -0.175600    0.480800    0.3886500
+50166.00  -0.173268   0.484606   0.3866632   -0.173900    0.483500    0.3866500
+50167.00  -0.171139   0.487249   0.3847174   -0.171600    0.486200    0.3847200
+50168.00  -0.168588   0.489916   0.3828287   -0.169100    0.488800    0.3828000
+50169.00  -0.165644   0.492745   0.3809636   -0.166000    0.491600    0.3809300
+50170.00  -0.162586   0.495671   0.3790850   -0.162900    0.494600    0.3790600
+50171.00  -0.159679   0.498650   0.3771505   -0.160200    0.497700    0.3771400
+50172.00  -0.156908   0.501603   0.3751298   -0.157200    0.500500    0.3751100
+50173.00  -0.154212   0.504539   0.3730040   -0.154600    0.503400    0.3729600
+50174.00  -0.151663   0.507519   0.3707596   -0.152300    0.506400    0.3708000
+50175.00  -0.149362   0.510501   0.3683888   -0.149700    0.509400    0.3685000
+50176.00  -0.147360   0.513289   0.3659173   -0.147900    0.512200    0.3659300
+50177.00  -0.145638   0.515645   0.3634127   -0.146000    0.514500    0.3633400
+50178.00  -0.143920   0.517579   0.3609107   -0.144200    0.516300    0.3609000
+50179.00  -0.142117   0.519318   0.3584370   -0.142300    0.518100    0.3584400
+50180.00  -0.140191   0.521063   0.3560213   -0.140400    0.519900    0.3560200
+50181.00  -0.137977   0.522959   0.3536761   -0.138500    0.521600    0.3536600
+50182.00  -0.135234   0.525188   0.3513918   -0.135700    0.523700    0.3513800
+50183.00  -0.132187   0.527816   0.3491286   -0.132700    0.526700    0.3491000
+50184.00  -0.129341   0.530702   0.3468257   -0.129400    0.529800    0.3468200
+50185.00  -0.126573   0.533654   0.3444367   -0.126700    0.532700    0.3444800
+50186.00  -0.123540   0.536510   0.3419479   -0.123800    0.535500    0.3419800
+50187.00  -0.120077   0.539229   0.3393835   -0.120300    0.538200    0.3393300
+50188.00  -0.116351   0.541835   0.3367947   -0.116700    0.540600    0.3367600
+50189.00  -0.112714   0.544371   0.3342362   -0.112900    0.543100    0.3342400
+50190.00  -0.109479   0.546890   0.3317556   -0.109600    0.545800    0.3317400
+50191.00  -0.106534   0.549353   0.3293821   -0.107000    0.548400    0.3293200
+50192.00  -0.103718   0.551690   0.3271622   -0.104200    0.550600    0.3271400
+50193.00  -0.100881   0.553900   0.3250953   -0.100900    0.552900    0.3250900
+50194.00  -0.097922   0.556020   0.3231337   -0.098000    0.554900    0.3231400
+50195.00  -0.094702   0.558058   0.3212225   -0.095100    0.556600    0.3212000
+50196.00  -0.091203   0.559985   0.3193323   -0.091300    0.558900    0.3192800
+50197.00  -0.087651   0.561722   0.3174315   -0.088000    0.560600    0.3174000
+50198.00  -0.084232   0.563198   0.3154816   -0.084800    0.562300    0.3154700
+50199.00  -0.080698   0.564540   0.3134595   -0.081100    0.563800    0.3134500
+50200.00  -0.076772   0.565959   0.3113307   -0.077100    0.564900    0.3113000
+50201.00  -0.072472   0.567693   0.3090793   -0.072700    0.566400    0.3090500
+50202.00  -0.068033   0.569841   0.3067111   -0.068300    0.568500    0.3066900
+50203.00  -0.063732   0.572237   0.3042392   -0.064000    0.571100    0.3042100
+50204.00  -0.059829   0.574547   0.3016817   -0.059900    0.573600    0.3016500
+50205.00  -0.056414   0.576487   0.2990626   -0.056600    0.575600    0.2990300
+50206.00  -0.053179   0.578097   0.2964375   -0.053500    0.577000    0.2964200
+50207.00  -0.049736   0.579512   0.2938736   -0.049900    0.578500    0.2938600
+50208.00  -0.045973   0.580829   0.2914053   -0.046000    0.579900    0.2913700
+50209.00  -0.042012   0.582102   0.2890282   -0.042100    0.581000    0.2890300
+50210.00  -0.037918   0.583347   0.2866998   -0.038000    0.582200    0.2867100
+50211.00  -0.033632   0.584594   0.2843591   -0.033800    0.583500    0.2843400
+50212.00  -0.029180   0.585848   0.2819499   -0.029300    0.584800    0.2819500
+50213.00  -0.024728   0.587025   0.2794478   -0.024900    0.586000    0.2794900
+50214.00  -0.020330   0.588093   0.2768482   -0.020800    0.587000    0.2768600
+50215.00  -0.015922   0.589125   0.2741811   -0.016300    0.587800    0.2741700
+50216.00  -0.011521   0.590154   0.2715088   -0.011700    0.589000    0.2714900
+50217.00  -0.007268   0.591099   0.2688973   -0.007800    0.589800    0.2688800
+50218.00  -0.003399   0.591911   0.2663992   -0.004000    0.590700    0.2663800
+50219.00  -0.000024   0.592626   0.2640413   -0.000300    0.591600    0.2640100
+50220.00   0.003144   0.593209   0.2618504    0.003000    0.592300    0.2618600
+50221.00   0.006435   0.593685   0.2598275    0.006000    0.592600    0.2598300
+50222.00   0.009919   0.594127   0.2579410    0.009700    0.592900    0.2579500
+50223.00   0.013491   0.594555   0.2561562    0.013400    0.593600    0.2561500
+50224.00   0.017124   0.594917   0.2544473    0.017100    0.593900    0.2544200
+50225.00   0.020937   0.595164   0.2527826    0.020800    0.594100    0.2527500
+50226.00   0.025152   0.595261   0.2511090    0.024800    0.594000    0.2511000
+50227.00   0.029779   0.595317   0.2493911    0.029600    0.594000    0.2493600
+50228.00   0.034665   0.595469   0.2476132    0.034400    0.594300    0.2476200
+50229.00   0.039753   0.595702   0.2457632    0.039400    0.594600    0.2457400
+50230.00   0.044841   0.595957   0.2438479    0.044700    0.594700    0.2438100
+50231.00   0.049615   0.596238   0.2418917    0.049500    0.594900    0.2418500
+50232.00   0.053882   0.596541   0.2399269    0.053800    0.595400    0.2398900
+50233.00   0.057700   0.596714   0.2379830    0.057500    0.595600    0.2379800
+50234.00   0.061444   0.596642   0.2361074    0.061200    0.595600    0.2361100
+50235.00   0.065503   0.596277   0.2343259    0.065300    0.595400    0.2343000
+50236.00   0.069860   0.595604   0.2326331    0.069800    0.594500    0.2326100
+50237.00   0.074230   0.594696   0.2309860    0.074300    0.593500    0.2309700
+50238.00   0.078467   0.593622   0.2293212    0.078400    0.592900    0.2292900
+50239.00   0.082604   0.592449   0.2275791    0.082300    0.591600    0.2275400
+50240.00   0.086784   0.591323   0.2257463    0.086400    0.590000    0.2257100
+50241.00   0.090816   0.590282   0.2238242    0.090500    0.589000    0.2237900
+50242.00   0.094546   0.589194   0.2218261    0.094500    0.588000    0.2218100
+50243.00   0.098025   0.587957   0.2197756    0.097900    0.586900    0.2198100
+50244.00   0.101457   0.586564   0.2177168    0.101200    0.585400    0.2177500
+50245.00   0.105120   0.585084   0.2157148    0.104900    0.583900    0.2156800
+50246.00   0.109158   0.583575   0.2138240    0.108900    0.582500    0.2137800
+50247.00   0.113468   0.581998   0.2120760    0.113200    0.580900    0.2121100
+50248.00   0.117895   0.580426   0.2104819    0.117800    0.579200    0.2104800
+50249.00   0.122258   0.578845   0.2090393    0.122200    0.577800    0.2089500
+50250.00   0.126467   0.577104   0.2077336    0.126100    0.576400    0.2077400
+50251.00   0.130498   0.575237   0.2065038    0.130300    0.574100    0.2064900
+50252.00   0.134268   0.573331   0.2053055    0.134300    0.572000    0.2052500
+50253.00   0.137862   0.571399   0.2040955    0.137800    0.570400    0.2040700
+50254.00   0.141523   0.569469   0.2028277    0.141400    0.568400    0.2028700
+50255.00   0.145303   0.567674   0.2014758    0.145100    0.566600    0.2015300
+50256.00   0.149172   0.566173   0.2000356    0.148900    0.565100    0.2000600
+50257.00   0.153039   0.564948   0.1985142    0.152900    0.564000    0.1984900
+50258.00   0.156765   0.563751   0.1969418    0.156600    0.562700    0.1969100
+50259.00   0.160185   0.562310   0.1953602    0.159800    0.561300    0.1953400
+50260.00   0.163281   0.560423   0.1938095    0.162900    0.559500    0.1937800
+50261.00   0.166362   0.558025   0.1923025    0.166200    0.556800    0.1923000
+50262.00   0.169493   0.555334   0.1908709    0.169200    0.554100    0.1908900
+50263.00   0.172608   0.552597   0.1895423    0.172200    0.551400    0.1895400
+50264.00   0.175817   0.549915   0.1883068    0.175600    0.548600    0.1882900
+50265.00   0.179070   0.547255   0.1871278    0.179000    0.545900    0.1871200
+50266.00   0.182205   0.544555   0.1859475    0.182300    0.543100    0.1859300
+50267.00   0.185266   0.541804   0.1846984    0.185200    0.540600    0.1846700
+50268.00   0.188487   0.539052   0.1833305    0.188100    0.537900    0.1833800
+50269.00   0.191979   0.536334   0.1818802    0.191700    0.534600    0.1820100
+50270.00   0.195778   0.533729   0.1804193    0.195600    0.531800    0.1805700
+50271.00   0.199792   0.531295   0.1790079    0.199700    0.529500    0.1791100
+50272.00   0.203823   0.528995   0.1776848    0.203900    0.527500    0.1777200
+50273.00   0.207687   0.526712   0.1764712    0.207800    0.525300    0.1764400
+50274.00   0.211388   0.524315   0.1753711    0.211100    0.523000    0.1753400
+50275.00   0.215158   0.521696   0.1743692    0.215000    0.520300    0.1743700
+50276.00   0.218752   0.518866   0.1734575    0.218500    0.517300    0.1734500
+50277.00   0.221889   0.515895   0.1726196    0.221500    0.514400    0.1725800
+50278.00   0.224827   0.512845   0.1718092    0.224800    0.511400    0.1718100
+50279.00   0.227709   0.509712   0.1709819    0.227600    0.508500    0.1710000
+50280.00   0.230519   0.506559   0.1701089    0.230200    0.505200    0.1701000
+50281.00   0.233085   0.503531   0.1691727    0.232800    0.502000    0.1691400
+50282.00   0.235301   0.500701   0.1681581    0.235000    0.499200    0.1681300
+50283.00   0.237275   0.497932   0.1670524    0.237200    0.497000    0.1670400
+50284.00   0.238918   0.495123   0.1658696    0.239100    0.493900    0.1658600
+50285.00   0.240184   0.492260   0.1646247    0.240000    0.490700    0.1645800
+50286.00   0.241314   0.489368   0.1633338    0.241100    0.487900    0.1632900
+50287.00   0.242591   0.486495   0.1620133    0.242800    0.484800    0.1620200
+50288.00   0.244009   0.483674   0.1606780    0.244000    0.482100    0.1606700
+50289.00   0.245469   0.480888   0.1593401    0.245000    0.479800    0.1593400
+50290.00   0.247079   0.478037   0.1580361    0.246800    0.476700    0.1580200
+50291.00   0.248835   0.475059   0.1567921    0.248700    0.473500    0.1567500
+50292.00   0.250734   0.472110   0.1555719    0.250700    0.470400    0.1555600
+50293.00   0.252661   0.469292   0.1543073    0.252700    0.467600    0.1542900
+50294.00   0.254453   0.466543   0.1529305    0.254300    0.465200    0.1529300
+50295.00   0.256087   0.463725   0.1513964    0.255900    0.462600    0.1513900
+50296.00   0.257649   0.460694   0.1497082    0.257700    0.459300    0.1496800
+50297.00   0.259242   0.457382   0.1479439    0.259000    0.456300    0.1479100
+50298.00   0.260754   0.453772   0.1462136    0.260500    0.452800    0.1461800
+50299.00   0.262152   0.449899   0.1446201    0.262200    0.448300    0.1445800
+50300.00   0.263615   0.445865   0.1432182    0.263600    0.444600    0.1432100
+50301.00   0.265323   0.441767   0.1420121    0.265100    0.440500    0.1420100
+50302.00   0.267376   0.437702   0.1409883    0.267100    0.436500    0.1410100
+50303.00   0.269796   0.433842   0.1401274    0.269400    0.432600    0.1401400
+50304.00   0.272298   0.430200   0.1393817    0.272200    0.429000    0.1394400
+50305.00   0.274464   0.426635   0.1386936    0.274400    0.425200    0.1386800
+50306.00   0.276292   0.423082   0.1380287    0.276000    0.421400    0.1379900
+50307.00   0.278048   0.419545   0.1373368    0.277800    0.418000    0.1372800
+50308.00   0.279980   0.416016   0.1365864    0.279900    0.414800    0.1365800
+50309.00   0.282112   0.412506   0.1357628    0.282000    0.411200    0.1357600
+50310.00   0.284194   0.409021   0.1348547    0.283900    0.407800    0.1348200
+50311.00   0.285973   0.405504   0.1338546    0.286100    0.404200    0.1338600
+50312.00   0.287390   0.401913   0.1327661    0.287200    0.400700    0.1327800
+50313.00   0.288667   0.398273   0.1316146    0.288100    0.396900    0.1315700
+50314.00   0.290024   0.394658   0.1304407    0.289900    0.393200    0.1304000
+50315.00   0.291404   0.391108   0.1292722    0.291000    0.389900    0.1292800
+50316.00   0.292692   0.387592   0.1281259    0.292200    0.386400    0.1281100
+50317.00   0.293933   0.384110   0.1270006    0.293900    0.382600    0.1269900
+50318.00   0.295088   0.380662   0.1258849    0.294800    0.379300    0.1258700
+50319.00   0.296087   0.377192   0.1247746    0.295600    0.376000    0.1247200
+50320.00   0.296841   0.373579   0.1236414    0.296700    0.372300    0.1235900
+50321.00   0.297290   0.369723   0.1224278    0.297100    0.368300    0.1224000
+50322.00   0.297493   0.365659   0.1210837    0.297000    0.364100    0.1210700
+50323.00   0.297670   0.361446   0.1195813    0.297300    0.360100    0.1195700
+50324.00   0.298033   0.357036   0.1178957    0.297900    0.355700    0.1179000
+50325.00   0.298180   0.352481   0.1160999    0.297700    0.351500    0.1161200
+50326.00   0.297996   0.347920   0.1142992    0.297500    0.346700    0.1143200
+50327.00   0.297595   0.343439   0.1125912    0.297200    0.342200    0.1126200
+50328.00   0.297111   0.339045   0.1110459    0.296800    0.337900    0.1111500
+50329.00   0.296552   0.334745   0.1096902    0.296500    0.333200    0.1098300
+50330.00   0.295960   0.330523   0.1085160    0.295700    0.329200    0.1085500
+50331.00   0.295473   0.326376   0.1074717    0.295100    0.325300    0.1073800
+50332.00   0.294897   0.322302   0.1065122    0.294600    0.321100    0.1064600
+50333.00   0.294281   0.318273   0.1055830    0.294000    0.316900    0.1055800
+50334.00   0.293581   0.314370   0.1046463    0.293300    0.313300    0.1046500
+50335.00   0.292918   0.310562   0.1036476    0.292600    0.309500    0.1036700
+50336.00   0.292468   0.306814   0.1025295    0.292200    0.305500    0.1025400
+50337.00   0.292078   0.303180   0.1012360    0.291700    0.302000    0.1012300
+50338.00   0.291441   0.299621   0.0997288    0.291100    0.298400    0.0997500
+50339.00   0.290440   0.295973   0.0980515    0.290200    0.294600    0.0980600
+50340.00   0.289166   0.292145   0.0962769    0.288800    0.291300    0.0962500
+50341.00   0.287719   0.288083   0.0944635    0.287500    0.287200    0.0944700
+50342.00   0.286237   0.283819   0.0926574    0.286000    0.282600    0.0927000
+50343.00   0.284921   0.279497   0.0909065    0.284000    0.278100    0.0908900
+50344.00   0.283911   0.275292   0.0892429    0.283300    0.274100    0.0892200
+50345.00   0.283203   0.271200   0.0876477    0.282900    0.270000    0.0876400
+50346.00   0.282657   0.267301   0.0860787    0.282500    0.266000    0.0860000
+50347.00   0.282139   0.263790   0.0844801    0.281900    0.262400    0.0844400
+50348.00   0.281419   0.260549   0.0828080    0.281100    0.259300    0.0828300
+50349.00   0.280372   0.257324   0.0809917    0.280100    0.256600    0.0810400
+50350.00   0.278936   0.253900   0.0789841    0.278500    0.253000    0.0790100
+50351.00   0.277230   0.250239   0.0767836    0.276800    0.249000    0.0767800
+50352.00   0.275516   0.246473   0.0744490    0.275400    0.245200    0.0744500
+50353.00   0.273808   0.242655   0.0720875    0.273500    0.241500    0.0720500
+50354.00   0.272150   0.238863   0.0698002    0.271400    0.237300    0.0697300
+50355.00   0.270516   0.235209   0.0676504    0.269700    0.233500    0.0676500
+50356.00   0.268914   0.231685   0.0656491    0.268400    0.230500    0.0656900
+50357.00   0.267380   0.228141   0.0637705    0.266800    0.227100    0.0638000
+50358.00   0.265754   0.224491   0.0619873    0.265200    0.223300    0.0619900
+50359.00   0.263829   0.220650   0.0603299    0.263400    0.219300    0.0603100
+50360.00   0.261799   0.216746   0.0587553    0.261400    0.215700    0.0587300
+50361.00   0.259851   0.213089   0.0572015    0.259500    0.212000    0.0571500
+50362.00   0.257961   0.209664   0.0556113    0.257600    0.208500    0.0555600
+50363.00   0.256087   0.206436   0.0539591    0.255700    0.205200    0.0539600
+50364.00   0.254144   0.203440   0.0522227    0.253800    0.202300    0.0522300
+50365.00   0.252026   0.200631   0.0503866    0.251600    0.199400    0.0503900
+50366.00   0.249757   0.197861   0.0484432    0.249400    0.196800    0.0484400
+50367.00   0.247358   0.194971   0.0464105    0.246900    0.193900    0.0463800
+50368.00   0.244908   0.191925   0.0443206    0.244400    0.190700    0.0443200
+50369.00   0.242479   0.188831   0.0422045    0.242000    0.187700    0.0422100
+50370.00   0.239981   0.185792   0.0400921    0.239500    0.184700    0.0401100
+50371.00   0.237451   0.182854   0.0380178    0.236900    0.181500    0.0380400
+50372.00   0.235135   0.180027   0.0360083    0.234600    0.178800    0.0360100
+50373.00   0.232875   0.177326   0.0340670    0.232300    0.176200    0.0340600
+50374.00   0.230420   0.174678   0.0321772    0.230000    0.173400    0.0321500
+50375.00   0.227845   0.172031   0.0302706    0.227500    0.170800    0.0302700
+50376.00   0.225250   0.169455   0.0282566    0.224900    0.168300    0.0283100
+50377.00   0.222714   0.166943   0.0261129    0.222200    0.165600    0.0261100
+50378.00   0.220149   0.164399   0.0238554    0.219700    0.163000    0.0238100
+50379.00   0.217347   0.161682   0.0215014    0.217100    0.160500    0.0215000
+50380.00   0.214308   0.158809   0.0190585    0.213900    0.157900    0.0190400
+50381.00   0.211321   0.155815   0.0166062    0.210900    0.154600    0.0165600
+50382.00   0.208747   0.152758   0.0142398    0.208300    0.151400    0.0142000
+50383.00   0.206648   0.149723   0.0120193    0.206100    0.148400    0.0119700
+50384.00   0.204727   0.146782   0.0099692    0.204300    0.145500    0.0099500
+50385.00   0.202616   0.143945   0.0080787    0.202100    0.142900    0.0080900
+50386.00   0.200095   0.141147   0.0063248    0.199500    0.139900    0.0063400
+50387.00   0.197246   0.138301   0.0046700    0.197100    0.137000    0.0046300
+50388.00   0.194360   0.135259   0.0030512    0.194300    0.134100    0.0030100
+50389.00   0.191481   0.132248   0.0014209    0.191000    0.130900    0.0014500
+50390.00   0.188491   0.129471  -0.0002731    0.188200    0.128200   -0.0002500
+50391.00   0.185317   0.126988  -0.0020740    0.185100    0.125800   -0.0021100
+50392.00   0.181928   0.124826  -0.0040039    0.181500    0.123400   -0.0040700
+50393.00   0.178262   0.122939  -0.0060643    0.177700    0.121700   -0.0060700
+50394.00   0.174164   0.121176  -0.0082014    0.173600    0.120000   -0.0081500
+50395.00   0.170037   0.119377  -0.0103803    0.169500    0.118000   -0.0104100
+50396.00   0.166197   0.117456  -0.0125639    0.165800    0.116000   -0.0126300
+50397.00   0.162466   0.115589  -0.0146986    0.162100    0.114300   -0.0147400
+50398.00   0.158663   0.113955  -0.0167781    0.158200    0.112600   -0.0168200
+50399.00   0.154633   0.112544  -0.0188187    0.154300    0.111200   -0.0188400
+50400.00   0.150422   0.111200  -0.0208427    0.150200    0.109900   -0.0208200
+50401.00   0.146079   0.109862  -0.0228148    0.145700    0.108800   -0.0228400
+50402.00   0.141568   0.108374  -0.0246957    0.141500    0.107300   -0.0247200
+50403.00   0.137457   0.106632  -0.0265370    0.137400    0.105400   -0.0264900
+50404.00   0.134081   0.104805  -0.0284527    0.133700    0.103500   -0.0284100
+50405.00   0.131054   0.103105  -0.0304736    0.130800    0.101900   -0.0304600
+50406.00   0.128026   0.101643  -0.0325821    0.127800    0.100400   -0.0325900
+50407.00   0.125019   0.100437  -0.0347399    0.124600    0.099200   -0.0347400
+50408.00   0.122162   0.099507  -0.0369011    0.121700    0.098300   -0.0369000
+50409.00   0.119269   0.098733  -0.0390000    0.118800    0.097400   -0.0390000
+50410.00   0.116042   0.097932  -0.0409795    0.115600    0.096900   -0.0409600
+50411.00   0.112575   0.097007  -0.0428249    0.112100    0.096000   -0.0427700
+50412.00   0.108895   0.096055  -0.0445572    0.108500    0.094800   -0.0444900
+50413.00   0.105076   0.095273  -0.0462060    0.104700    0.094000   -0.0461600
+50414.00   0.101293   0.094701  -0.0478085    0.100900    0.093400   -0.0478000
+50415.00   0.097465   0.094238  -0.0493838    0.097000    0.093200   -0.0493900
+50416.00   0.093540   0.093769  -0.0509576    0.093000    0.092800   -0.0509700
+50417.00   0.089593   0.093199  -0.0525574    0.089100    0.092000   -0.0525600
+50418.00   0.085827   0.092517  -0.0542025    0.085800    0.091500   -0.0541800
+50419.00   0.082281   0.091772  -0.0558923    0.082300    0.090600   -0.0558700
+50420.00   0.078743   0.091073  -0.0576114    0.078300    0.089700   -0.0576100
+50421.00   0.074984   0.090512  -0.0593535    0.074600    0.089400   -0.0593300
+50422.00   0.070877   0.089989  -0.0611357    0.070700    0.089000   -0.0611100
+50423.00   0.066593   0.089367  -0.0629445    0.066600    0.088100   -0.0629300
+50424.00   0.062411   0.088604  -0.0647376    0.062400    0.087300   -0.0647300
+50425.00   0.058416   0.087729  -0.0664852    0.058100    0.086500   -0.0664800
+50426.00   0.054504   0.086825  -0.0681764    0.054200    0.085500   -0.0681700
+50427.00   0.050574   0.085999  -0.0698227    0.050200    0.084500   -0.0697800
+50428.00   0.046689   0.085384  -0.0714549    0.046200    0.084100   -0.0714300
+50429.00   0.043010   0.085082  -0.0731165    0.042500    0.084000   -0.0731400
+50430.00   0.039688   0.085081  -0.0748463    0.039400    0.083000   -0.0748400
+50431.00   0.036684   0.085362  -0.0766865    0.036500    0.083600   -0.0766400
+50432.00   0.033789   0.085870  -0.0786548    0.033500    0.084600   -0.0786500
+50433.00   0.030822   0.086510  -0.0807293    0.030700    0.085300   -0.0807400
+50434.00   0.027731   0.087197  -0.0828705    0.027600    0.085900   -0.0828800
+50435.00   0.024552   0.087895  -0.0850383    0.024300    0.086600   -0.0850300
+50436.00   0.021384   0.088618  -0.0871961    0.020900    0.087200   -0.0871800
+50437.00   0.018204   0.089314  -0.0892816    0.017800    0.088000   -0.0892900
+50438.00   0.015098   0.089938  -0.0912487    0.014900    0.088500   -0.0912500
+50439.00   0.012264   0.090507  -0.0931030    0.012000    0.089100   -0.0930700
+50440.00   0.009778   0.091040  -0.0948805    0.009400    0.089700   -0.0948400
+50441.00   0.007387   0.091534  -0.0966031    0.007100    0.090300   -0.0965900
+50442.00   0.004683   0.091983  -0.0982859    0.004500    0.090800   -0.0982600
+50443.00   0.001482   0.092365  -0.0999606    0.001200    0.091300   -0.0999300
+50444.00  -0.002138   0.092697  -0.1016613   -0.002300    0.091500   -0.1016500
+50445.00  -0.006054   0.093050  -0.1034141   -0.006000    0.091700   -0.1034200
+50446.00  -0.010233   0.093503  -0.1052325   -0.010500    0.092100   -0.1052300
+50447.00  -0.014631   0.094090  -0.1071181   -0.014800    0.092900   -0.1071000
+50448.00  -0.019065   0.094720  -0.1090616   -0.019400    0.093700   -0.1090300
+50449.00  -0.023125   0.095292  -0.1110467   -0.023120    0.095270   -0.1110260
+50450.00  -0.026846   0.095921  -0.1130512   -0.026650    0.095730   -0.1129910
+50451.00  -0.030356   0.096804  -0.1150536   -0.030320    0.096580   -0.1150660
+50452.00  -0.033659   0.098009  -0.1170274   -0.033580    0.097740   -0.1170900
+50453.00  -0.036894   0.099459  -0.1189536   -0.036610    0.099330   -0.1189530
+50454.00  -0.040341   0.100969  -0.1208302   -0.040400    0.100800   -0.1208340
+50455.00  -0.043963   0.102347  -0.1226704   -0.044180    0.102110   -0.1226990
+50456.00  -0.047384   0.103560  -0.1245101   -0.047410    0.103440   -0.1245240
+50457.00  -0.050363   0.104719  -0.1263955   -0.050390    0.104490   -0.1264190
+50458.00  -0.053191   0.105897  -0.1283614   -0.053080    0.105700   -0.1283790
+50459.00  -0.056069   0.107078  -0.1304323   -0.055890    0.106970   -0.1304310
+50460.00  -0.058933   0.108271  -0.1325930   -0.058900    0.108160   -0.1326170
+50461.00  -0.061619   0.109534  -0.1348093   -0.061840    0.109360   -0.1347970
+50462.00  -0.063963   0.110918  -0.1370449   -0.063990    0.110710   -0.1370150
+50463.00  -0.066046   0.112506  -0.1392469   -0.065980    0.112380   -0.1392600
+50464.00  -0.068034   0.114400  -0.1413615   -0.068080    0.114440   -0.1413620
+50465.00  -0.069960   0.116470  -0.1433426   -0.069680    0.115950   -0.1433430
+50466.00  -0.071941   0.118545  -0.1451623   -0.071350    0.117780   -0.1451910
+50467.00  -0.074152   0.120610  -0.1468202   -0.074030    0.120500   -0.1468210
+50468.00  -0.076773   0.122686  -0.1483487   -0.076670    0.122510   -0.1483560
+50469.00  -0.079660   0.124753  -0.1497922   -0.079820    0.124740   -0.1498140
+50470.00  -0.082335   0.126769  -0.1512048   -0.082480    0.126830   -0.1512110
+50471.00  -0.084929   0.128732  -0.1526485   -0.084950    0.128440   -0.1526570
+50472.00  -0.087687   0.130552  -0.1541269   -0.087820    0.130140   -0.1541470
+50473.00  -0.090542   0.132279  -0.1556175   -0.090550    0.132210   -0.1556260
+50474.00  -0.093642   0.133992  -0.1571430   -0.093280    0.134020   -0.1571430
+50475.00  -0.096798   0.135756  -0.1587242   -0.096530    0.135560   -0.1586830
+50476.00  -0.099828   0.137597  -0.1603627   -0.099770    0.137440   -0.1603380
+50477.00  -0.102756   0.139487  -0.1620217   -0.102820    0.139450   -0.1620400
+50478.00  -0.105545   0.141428  -0.1636253   -0.105760    0.141240   -0.1636150
+50479.00  -0.108317   0.143490  -0.1651635   -0.108290    0.143260   -0.1651560
+50480.00  -0.111349   0.145610  -0.1666459   -0.111150    0.145580   -0.1666790
+50481.00  -0.114391   0.147746  -0.1680324   -0.114590    0.147850   -0.1680380
+50482.00  -0.117215   0.149884  -0.1693291   -0.117340    0.149870   -0.1693300
+50483.00  -0.119869   0.152077  -0.1705907   -0.119820    0.151850   -0.1705910
+50484.00  -0.122471   0.154427  -0.1718955   -0.122580    0.154440   -0.1718900
+50485.00  -0.124786   0.156838  -0.1733379   -0.124840    0.156950   -0.1733440
+50486.00  -0.126743   0.159259  -0.1749635   -0.126880    0.159370   -0.1749570
+50487.00  -0.128511   0.161777  -0.1767825   -0.128590    0.161690   -0.1767640
+50488.00  -0.130350   0.164433  -0.1787477   -0.130260    0.164120   -0.1787810
+50489.00  -0.132250   0.167200  -0.1807865   -0.132220    0.167330   -0.1807960
+50490.00  -0.134165   0.170000  -0.1828595   -0.134060    0.170390   -0.1828460
+50491.00  -0.136191   0.172752  -0.1849226   -0.136190    0.172770   -0.1849320
+50492.00  -0.138295   0.175444  -0.1868678   -0.138400    0.175240   -0.1868910
+50493.00  -0.140379   0.178160  -0.1886653   -0.140490    0.178200   -0.1886420
+50494.00  -0.142530   0.180941  -0.1903477   -0.142460    0.181110   -0.1903170
+50495.00  -0.144884   0.183697  -0.1919402   -0.144880    0.183840   -0.1919650
+50496.00  -0.147478   0.186326  -0.1934698   -0.147590    0.186540   -0.1934630
+50497.00  -0.150122   0.188791  -0.1949925   -0.150110    0.188740   -0.1949620
+50498.00  -0.152594   0.191120  -0.1965482   -0.152560    0.191020   -0.1965500
+50499.00  -0.154965   0.193390  -0.1981573   -0.155120    0.193450   -0.1981660
+50500.00  -0.157097   0.195697  -0.1998452   -0.157190    0.195650   -0.1998380
+50501.00  -0.158915   0.198140  -0.2016223   -0.158970    0.197900   -0.2016180
+50502.00  -0.160453   0.200800  -0.2034968   -0.160360    0.200690   -0.2034930
+50503.00  -0.161818   0.203733  -0.2054597   -0.161690    0.203580   -0.2054440
+50504.00  -0.163299   0.206941  -0.2074795   -0.163220    0.206860   -0.2074620
+50505.00  -0.165215   0.210308  -0.2095110   -0.165150    0.210200   -0.2095060
+50506.00  -0.167652   0.213624  -0.2115238   -0.167740    0.213480   -0.2115180
+50507.00  -0.170138   0.216857  -0.2135309   -0.170330    0.216900   -0.2135090
+50508.00  -0.172256   0.220043  -0.2155422   -0.172380    0.219970   -0.2155140
+50509.00  -0.173879   0.223231  -0.2175595   -0.173970    0.223050   -0.2175420
+50510.00  -0.174995   0.226453  -0.2195849   -0.175070    0.226410   -0.2195720
+50511.00  -0.175740   0.229709  -0.2216355   -0.175650    0.229730   -0.2216320
+50512.00  -0.176415   0.232987  -0.2237518   -0.176420    0.232950   -0.2237540
+50513.00  -0.177300   0.236295  -0.2259897   -0.177190    0.236190   -0.2259670
+50514.00  -0.178558   0.239568  -0.2284127   -0.178250    0.239750   -0.2283680
+50515.00  -0.180253   0.242757  -0.2310411   -0.180110    0.242900   -0.2310240
+50516.00  -0.182213   0.245858  -0.2338346   -0.182130    0.245820   -0.2338510
+50517.00  -0.184152   0.248905  -0.2367006   -0.184210    0.248920   -0.2366890
+50518.00  -0.185660   0.251986  -0.2395438   -0.185880    0.251900   -0.2395060
+50519.00  -0.186433   0.255167  -0.2422867   -0.186520    0.255110   -0.2422750
+50520.00  -0.186654   0.258479  -0.2448840   -0.186640    0.258650   -0.2449000
+50521.00  -0.186547   0.262016  -0.2473473   -0.186430    0.261920   -0.2473450
+50522.00  -0.186532   0.265890  -0.2497212   -0.186230    0.265770   -0.2496900
+50523.00  -0.186927   0.270001  -0.2520565   -0.186860    0.270210   -0.2520300
+50524.00  -0.187669   0.274026  -0.2543915   -0.187830    0.274290   -0.2543900
+50525.00  -0.188374   0.277782  -0.2567473   -0.188480    0.277770   -0.2567630
+50526.00  -0.188751   0.281365  -0.2591447   -0.188700    0.281390   -0.2591520
+50527.00  -0.188948   0.284964  -0.2615966   -0.188980    0.285030   -0.2615730
+50528.00  -0.189277   0.288636  -0.2640664   -0.189010    0.288660   -0.2640260
+50529.00  -0.189584   0.292340  -0.2665641   -0.189180    0.292380   -0.2665350
+50530.00  -0.189715   0.296049  -0.2691041   -0.189770    0.296020   -0.2691000
+50531.00  -0.189650   0.299799  -0.2716708   -0.189710    0.299790   -0.2716640
+50532.00  -0.189501   0.303650  -0.2742439   -0.189510    0.303670   -0.2742150
+50533.00  -0.189576   0.307593  -0.2767977   -0.189610    0.307610   -0.2767820
+50534.00  -0.190011   0.311542  -0.2793072   -0.190060    0.311790   -0.2793000
+50535.00  -0.190465   0.315430  -0.2817635   -0.190390    0.315590   -0.2817270
+50536.00  -0.190817   0.319244  -0.2841799   -0.190700    0.319190   -0.2841550
+50537.00  -0.191111   0.322923  -0.2865724   -0.191090    0.323120   -0.2865830
+50538.00  -0.191277   0.326401  -0.2889662   -0.191330    0.326200   -0.2889710
+50539.00  -0.191095   0.329761  -0.2913990   -0.191030    0.329370   -0.2913530
+50540.00  -0.190396   0.333126  -0.2939066   -0.190470    0.333060   -0.2938890
+50541.00  -0.189369   0.336798  -0.2965213   -0.189510    0.336850   -0.2965170
+50542.00  -0.188326   0.340644  -0.2992707   -0.188320    0.340750   -0.2992520
+50543.00  -0.187324   0.344421  -0.3021543   -0.186950    0.344770   -0.3021410
+50544.00  -0.186426   0.348044  -0.3051186   -0.186080    0.348490   -0.3050560
+50545.00  -0.185573   0.351535  -0.3080759   -0.185710    0.351620   -0.3080380
+50546.00  -0.184596   0.354973  -0.3109377   -0.184670    0.355100   -0.3109070
+50547.00  -0.183491   0.358473  -0.3136437   -0.183910    0.358510   -0.3136330
+50548.00  -0.182447   0.362076  -0.3161928   -0.182710    0.362170   -0.3162020
+50549.00  -0.181423   0.365823  -0.3185995   -0.181650    0.365730   -0.3186020
+50550.00  -0.180458   0.369697  -0.3208959   -0.180750    0.369460   -0.3209070
+50551.00  -0.179741   0.373586  -0.3231184   -0.179540    0.373610   -0.3231080
+50552.00  -0.179301   0.377335  -0.3253059   -0.179130    0.377460   -0.3253080
+50553.00  -0.178935   0.380843  -0.3274896   -0.179170    0.381090   -0.3275180
+50554.00  -0.178449   0.384082  -0.3296974   -0.178510    0.384090   -0.3297100
+50555.00  -0.177770   0.387200  -0.3319711   -0.177840    0.387160   -0.3319570
+50556.00  -0.176894   0.390339  -0.3342906   -0.177080    0.390390   -0.3342830
+50557.00  -0.175898   0.393568  -0.3366294   -0.175800    0.393490   -0.3366160
+50558.00  -0.174918   0.396920  -0.3389891   -0.174820    0.396880   -0.3389690
+50559.00  -0.174074   0.400408  -0.3413517   -0.173790    0.400430   -0.3413440
+50560.00  -0.173393   0.404014  -0.3436733   -0.173130    0.404030   -0.3436830
+50561.00  -0.172759   0.407606  -0.3459204   -0.172750    0.407590   -0.3459270
+50562.00  -0.171839   0.410966  -0.3480859   -0.172230    0.411360   -0.3480680
+50563.00  -0.170499   0.414152  -0.3501739   -0.170760    0.414200   -0.3501490
+50564.00  -0.168831   0.417314  -0.3521945   -0.168870    0.417240   -0.3521830
+50565.00  -0.167106   0.420468  -0.3541611   -0.167060    0.420570   -0.3541560
+50566.00  -0.165634   0.423541  -0.3561064   -0.165470    0.423620   -0.3560980
+50567.00  -0.164540   0.426430  -0.3580823   -0.164390    0.426550   -0.3580740
+50568.00  -0.163574   0.429110  -0.3601397   -0.163620    0.429130   -0.3601370
+50569.00  -0.162447   0.431668  -0.3623133   -0.162550    0.431430   -0.3623130
+50570.00  -0.160959   0.434208  -0.3645947   -0.161010    0.434320   -0.3645950
+50571.00  -0.158952   0.436863  -0.3669830   -0.159020    0.437110   -0.3669560
+50572.00  -0.156442   0.439744  -0.3694605   -0.156530    0.439490   -0.3694140
+50573.00  -0.153614   0.442881  -0.3719702   -0.153640    0.442850   -0.3719270
+50574.00  -0.150798   0.446180  -0.3744440   -0.150610    0.446380   -0.3744430
+50575.00  -0.148127   0.449423  -0.3768402   -0.148120    0.449590   -0.3768500
+50576.00  -0.145405   0.452426  -0.3790965   -0.145420    0.452510   -0.3790580
+50577.00  -0.142482   0.455307  -0.3812199   -0.142440    0.455210   -0.3811930
+50578.00  -0.139426   0.458227  -0.3832642   -0.139410    0.458130   -0.3832900
+50579.00  -0.136461   0.461179  -0.3852533   -0.136280    0.461250   -0.3852540
+50580.00  -0.133867   0.464047  -0.3872115   -0.133860    0.464070   -0.3872040
+50581.00  -0.131687   0.466720  -0.3891616   -0.131640    0.466690   -0.3891660
+50582.00  -0.129668   0.469178  -0.3911154   -0.129560    0.469170   -0.3911180
+50583.00  -0.127578   0.471457  -0.3930758   -0.127760    0.471830   -0.3930820
+50584.00  -0.125217   0.473621  -0.3950526   -0.125190    0.473650   -0.3950390
+50585.00  -0.122492   0.475779  -0.3970639   -0.122280    0.475780   -0.3970140
+50586.00  -0.119535   0.478059  -0.3991144   -0.119350    0.478040   -0.3990670
+50587.00  -0.116559   0.480544  -0.4011911   -0.116500    0.480480   -0.4011750
+50588.00  -0.113641   0.483188  -0.4032818   -0.113540    0.483140   -0.4032990
+50589.00  -0.110776   0.485865  -0.4053710   -0.110730    0.485760   -0.4053930
+50590.00  -0.108256   0.488391  -0.4074257   -0.108270    0.488390   -0.4074370
+50591.00  -0.105963   0.490720  -0.4094314   -0.105880    0.490770   -0.4094310
+50592.00  -0.103700   0.492983  -0.4114046   -0.103560    0.492970   -0.4114080
+50593.00  -0.101447   0.495252  -0.4133742   -0.101400    0.495370   -0.4133980
+50594.00  -0.099172   0.497478  -0.4153798   -0.099110    0.497570   -0.4154060
+50595.00  -0.096785   0.499650  -0.4174722   -0.096900    0.499300   -0.4174680
+50596.00  -0.094267   0.501741  -0.4196953   -0.094180    0.501500   -0.4197000
+50597.00  -0.091850   0.503791  -0.4220672   -0.091740    0.504070   -0.4220830
+50598.00  -0.089584   0.505799  -0.4245581   -0.089680    0.506070   -0.4245640
+50599.00  -0.087350   0.507760  -0.4270965   -0.087270    0.507660   -0.4271100
+50600.00  -0.085071   0.509688  -0.4296072   -0.084900    0.509870   -0.4295920
+50601.00  -0.082698   0.511540  -0.4320282   -0.082550    0.511740   -0.4320310
+50602.00  -0.080141   0.513310  -0.4342951   -0.080290    0.513170   -0.4342640
+50603.00  -0.077450   0.515008  -0.4363661   -0.077560    0.514830   -0.4363590
+50604.00  -0.074955   0.516512  -0.4382224   -0.074960    0.516480   -0.4382470
+50605.00  -0.072662   0.517746  -0.4398921   -0.072650    0.517730   -0.4398790
+50606.00  -0.070423   0.518722  -0.4414443   -0.070340    0.518680   -0.4414520
+50607.00  -0.068053   0.519422  -0.4429291   -0.068210    0.519640   -0.4429350
+50608.00  -0.065268   0.519887  -0.4443891   -0.065500    0.520040   -0.4443870
+50609.00  -0.061898   0.520349  -0.4458636   -0.061880    0.520140   -0.4458620
+50610.00  -0.058127   0.521097  -0.4473822   -0.058090    0.520920   -0.4473900
+50611.00  -0.054367   0.522302  -0.4489535   -0.054330    0.522280   -0.4489650
+50612.00  -0.050642   0.523897  -0.4505582   -0.050440    0.523870   -0.4505490
+50613.00  -0.046970   0.525560  -0.4521676   -0.046980    0.525830   -0.4521290
+50614.00  -0.043365   0.526958  -0.4537530   -0.043510    0.527200   -0.4537240
+50615.00  -0.039765   0.528003  -0.4552828   -0.039660    0.528020   -0.4552880
+50616.00  -0.036136   0.528829  -0.4567309   -0.036070    0.528570   -0.4567500
+50617.00  -0.032476   0.529592  -0.4580759   -0.032510    0.529440   -0.4580800
+50618.00  -0.028867   0.530411  -0.4592748   -0.028920    0.530430   -0.4592590
+50619.00  -0.025152   0.531246  -0.4603396   -0.025010    0.531400   -0.4603310
+50620.00  -0.021281   0.532003  -0.4613282   -0.021080    0.532140   -0.4613360
+50621.00  -0.017250   0.532620  -0.4623065   -0.017230    0.532690   -0.4623090
+50622.00  -0.013076   0.533105  -0.4633442   -0.012820    0.533010   -0.4633470
+50623.00  -0.008959   0.533538  -0.4644849   -0.008700    0.533480   -0.4644800
+50624.00  -0.005037   0.533969  -0.4657361   -0.004870    0.533880   -0.4657440
+50625.00  -0.001305   0.534422  -0.4670736   -0.001160    0.534400   -0.4670770
+50626.00   0.002419   0.534850  -0.4684479    0.002430    0.534990   -0.4684540
+50627.00   0.006322   0.535197  -0.4697869    0.006390    0.535310   -0.4698040
+50628.00   0.010476   0.535489  -0.4710285    0.010520    0.535600   -0.4710160
+50629.00   0.014809   0.535883  -0.4721332    0.014850    0.535860   -0.4721370
+50630.00   0.019205   0.536419   0.5269220    0.019150    0.536250    0.5268940
+50631.00   0.023493   0.536912   0.5261374    0.023390    0.536680    0.5261270
+50632.00   0.027452   0.537141   0.5254967    0.027540    0.537280    0.5255080
+50633.00   0.031223   0.537076   0.5249515    0.031140    0.536960    0.5249520
+50634.00   0.034962   0.536773   0.5244340    0.034850    0.536560    0.5244230
+50635.00   0.038689   0.536291   0.5238814    0.038740    0.536120    0.5238950
+50636.00   0.042534   0.535696   0.5232476    0.042370    0.535810    0.5232340
+50637.00   0.046663   0.535063   0.5225157    0.046380    0.534930    0.5224760
+50638.00   0.050873   0.534487   0.5216906    0.050830    0.534450    0.5216790
+50639.00   0.054750   0.534055   0.5207857    0.054960    0.534310    0.5207760
+50640.00   0.058275   0.533596   0.5198074    0.058500    0.533840    0.5198110
+50641.00   0.061703   0.532970   0.5187691    0.061880    0.533060    0.5187780
+50642.00   0.065250   0.532212   0.5176801    0.065180    0.532240    0.5176940
+50643.00   0.068880   0.531426   0.5165642    0.068750    0.531420    0.5166260
+50644.00   0.072463   0.530698   0.5154546    0.072390    0.530670    0.5154970
+50645.00   0.075783   0.530020   0.5143902    0.075690    0.529910    0.5143660
+50646.00   0.078739   0.529321   0.5133592    0.078780    0.529110    0.5133850
+50647.00   0.081527   0.528578   0.5122953    0.081560    0.528620    0.5123300
+50648.00   0.084482   0.527746   0.5111334    0.084430    0.527980    0.5111090
+50649.00   0.087863   0.526792   0.5098259    0.087470    0.526420    0.5098570
+50650.00   0.091630   0.525731   0.5083296    0.091370    0.525420    0.5083920
+50651.00   0.095462   0.524636   0.5066364    0.095540    0.524670    0.5066460
+50652.00   0.099070   0.523593   0.5047789    0.099300    0.523600    0.5047620
+50653.00   0.102369   0.522632   0.5028128    0.102360    0.522590    0.5028580
+50654.00   0.105406   0.521769   0.5007884    0.105250    0.521650    0.5008370
+50655.00   0.108028   0.520860   0.4988149    0.108100    0.520900    0.4987890
+50656.00   0.110314   0.519815   0.4969636    0.110350    0.519920    0.4969520
+50657.00   0.112515   0.518611   0.4952704    0.112490    0.518700    0.4952740
+50658.00   0.114866   0.517276   0.4937342    0.114770    0.517170    0.4937570
+50659.00   0.117460   0.515918   0.4923373    0.117530    0.515740    0.4923430
+50660.00   0.120245   0.514597   0.4910524    0.120280    0.514540    0.4910370
+50661.00   0.123226   0.513301   0.4898217    0.123130    0.513310    0.4898480
+50662.00   0.126325   0.512004   0.4885897    0.126390    0.512030    0.4885980
+50663.00   0.129373   0.510695   0.4873339    0.129470    0.510800    0.4872900
+50664.00   0.132300   0.509356   0.4860454    0.132500    0.509470    0.4860610
+50665.00   0.135125   0.507996   0.4847079    0.135210    0.508000    0.4847340
+50666.00   0.137790   0.506654   0.4833214    0.137730    0.506730    0.4833070
+50667.00   0.140168   0.505311   0.4818941    0.140190    0.505620    0.4819060
+50668.00   0.142406   0.503863   0.4804318    0.142330    0.503990    0.4804660
+50669.00   0.144656   0.502196   0.4789539    0.144720    0.502370    0.4789680
+50670.00   0.146894   0.500298   0.4774990    0.146900    0.500350    0.4775170
+50671.00   0.149084   0.498268   0.4761027    0.149040    0.498160    0.4761580
+50672.00   0.151262   0.496249   0.4747824    0.151260    0.496060    0.4748590
+50673.00   0.153451   0.494316   0.4735354    0.153290    0.494020    0.4735610
+50674.00   0.155554   0.492263   0.4723490    0.155510    0.492230    0.4723190
+50675.00   0.157518   0.490077   0.4711881    0.157600    0.489950    0.4711990
+50676.00   0.159400   0.487885   0.4699843    0.159390    0.487510    0.4700320
+50677.00   0.161219   0.485756   0.4686609    0.161220    0.485780    0.4686570
+50678.00   0.163022   0.483664   0.4671734    0.162940    0.483860    0.4671290
+50679.00   0.164917   0.481520   0.4655203    0.165070    0.481530    0.4655210
+50680.00   0.166942   0.479299   0.4637208    0.167120    0.479130    0.4637370
+50681.00   0.169141   0.476973   0.4618231    0.169090    0.476780    0.4617800
+50682.00   0.171583   0.474577   0.4599375    0.171650    0.474590    0.4599200
+50683.00   0.174167   0.472261   0.4581597    0.174220    0.472300    0.4581840
+50684.00   0.176778   0.470154   0.4565369    0.176740    0.470020    0.4564810
+50685.00   0.179283   0.468296   0.4550755    0.179480    0.468220    0.4549860
+50686.00   0.181563   0.466641   0.4537300    0.181730    0.466700    0.4537580
+50687.00   0.183704   0.465033   0.4524304    0.183670    0.465180    0.4524560
+50688.00   0.185879   0.463308   0.4511285    0.186040    0.463220    0.4510470
+50689.00   0.188047   0.461515   0.4498180    0.188200    0.461540    0.4497610
+50690.00   0.190182   0.459681   0.4484854    0.190040    0.459870    0.4484800
+50691.00   0.192397   0.457851   0.4471108    0.192810    0.457580    0.4470950
+50692.00   0.194835   0.456075   0.4456687    0.195190    0.455900    0.4456580
+50693.00   0.197512   0.454304   0.4441490    0.197410    0.454390    0.4441620
+50694.00   0.200273   0.452464   0.4425676    0.199960    0.452670    0.4425770
+50695.00   0.202819   0.450531   0.4409528    0.202790    0.450800    0.4409470
+50696.00   0.204834   0.448415   0.4393440    0.204910    0.448760    0.4393220
+50697.00   0.206425   0.446053   0.4377641    0.206370    0.446300    0.4377530
+50698.00   0.207950   0.443508   0.4362179    0.207950    0.443560    0.4362150
+50699.00   0.209613   0.440941   0.4347090    0.209570    0.440850    0.4347020
+50700.00   0.211333   0.438467   0.4332717    0.211080    0.438240    0.4332700
+50701.00   0.212799   0.435949   0.4319120    0.212530    0.435910    0.4319150
+50702.00   0.214129   0.433249   0.4305354    0.213980    0.433250    0.4305350
+50703.00   0.215443   0.430595   0.4290757    0.215450    0.430440    0.4290670
+50704.00   0.216636   0.428209   0.4274979    0.216880    0.428020    0.4275020
+50705.00   0.217447   0.426056   0.4257653    0.217650    0.426110    0.4257870
+50706.00   0.217825   0.423914   0.4238243    0.218030    0.424210    0.4238360
+50707.00   0.217939   0.421578   0.4216578    0.218030    0.421720    0.4216490
+50708.00   0.218042   0.419053   0.4193071    0.217980    0.419020    0.4192990
+50709.00   0.218405   0.416554   0.4168661    0.218320    0.416550    0.4168570
+50710.00   0.219060   0.414120   0.4144571    0.219070    0.414120    0.4144540
+50711.00   0.219824   0.411705   0.4121859    0.219840    0.411700    0.4122050
+50712.00   0.220552   0.409271   0.4100988    0.220580    0.409270    0.4101310
+50713.00   0.221159   0.406759   0.4081784    0.221230    0.406690    0.4081940
+50714.00   0.221544   0.404116   0.4063782    0.221670    0.403970    0.4063660
+50715.00   0.221639   0.401267   0.4046411    0.221680    0.400980    0.4046340
+50716.00   0.221427   0.398075   0.4029151    0.221290    0.397770    0.4029280
+50717.00   0.221148   0.394691   0.4011444    0.221180    0.394520    0.4011560
+50718.00   0.220967   0.391340   0.3992871    0.220850    0.391110    0.3992790
+50719.00   0.220896   0.388141   0.3973575    0.220760    0.387900    0.3973340
+50720.00   0.220936   0.385125   0.3953779    0.220990    0.385200    0.3953510
+50721.00   0.221029   0.382236   0.3933771    0.221110    0.382080    0.3933600
+50722.00   0.221183   0.379339   0.3913652    0.221130    0.379260    0.3913550
+50723.00   0.221438   0.376317   0.3893063    0.221440    0.376660    0.3893070
+50724.00   0.221678   0.373215   0.3872347    0.221760    0.373380    0.3872330
+50725.00   0.221852   0.370134   0.3852065    0.221910    0.370090    0.3852180
+50726.00   0.222066   0.367211   0.3832492    0.222050    0.367100    0.3832650
+50727.00   0.222328   0.364524   0.3813624    0.222270    0.364270    0.3813700
+50728.00   0.222531   0.362045   0.3795218    0.222490    0.361950    0.3795040
+50729.00   0.222583   0.359663   0.3776878    0.222450    0.359630    0.3776800
+50730.00   0.222463   0.357334   0.3758081    0.222590    0.357400    0.3758620
+50731.00   0.222364   0.354973   0.3738415    0.222250    0.355160    0.3739320
+50732.00   0.222546   0.352483   0.3717573    0.222270    0.352650    0.3718190
+50733.00   0.223066   0.349874   0.3695248    0.223130    0.349980    0.3695390
+50734.00   0.223742   0.347247   0.3671319    0.224030    0.347190    0.3671270
+50735.00   0.224350   0.344654   0.3646101    0.224420    0.344320    0.3646070
+50736.00   0.224829   0.342047   0.3620202    0.224770    0.341830    0.3620180
+50737.00   0.225233   0.339341   0.3594312    0.225390    0.339510    0.3594170
+50738.00   0.225513   0.336514   0.3569238    0.225690    0.336490    0.3568930
+50739.00   0.225723   0.333587   0.3545707    0.225510    0.333780    0.3545530
+50740.00   0.226071   0.330617   0.3524006    0.225840    0.330710    0.3524200
+50741.00   0.226734   0.327713   0.3503925    0.226660    0.327500    0.3504260
+50742.00   0.227583   0.324971   0.3485072    0.227620    0.324710    0.3485250
+50743.00   0.228135   0.322377   0.3467023    0.228130    0.322290    0.3467090
+50744.00   0.228073   0.319805   0.3449452    0.227930    0.319980    0.3449410
+50745.00   0.227639   0.317177   0.3431894    0.227590    0.317140    0.3431750
+50746.00   0.227015   0.314451   0.3413928    0.227000    0.314360    0.3413770
+50747.00   0.226260   0.311590   0.3395366    0.226170    0.311650    0.3395010
+50748.00   0.225349   0.308591   0.3376163    0.225500    0.308760    0.3375650
+50749.00   0.224117   0.305464   0.3356391    0.224230    0.305610    0.3356010
+50750.00   0.222553   0.302189   0.3336090    0.222490    0.302310    0.3336030
+50751.00   0.220887   0.298780   0.3315353    0.221120    0.298860    0.3315420
+50752.00   0.219360   0.295439   0.3294279    0.219130    0.295250    0.3294120
+50753.00   0.218197   0.292331   0.3272972    0.217830    0.292330    0.3272540
+50754.00   0.217440   0.289537   0.3251797    0.217660    0.290260    0.3251270
+50755.00   0.216911   0.287060   0.3231026    0.217200    0.287420    0.3230750
+50756.00   0.216413   0.284799   0.3210639    0.216380    0.285030    0.3210520
+50757.00   0.215856   0.282591   0.3190230    0.215990    0.282700    0.3190130
+50758.00   0.215208   0.280313   0.3168940    0.215420    0.280340    0.3168880
+50759.00   0.214575   0.278014   0.3146522    0.214490    0.278000    0.3146430
+50760.00   0.213973   0.275719   0.3123008    0.213790    0.275840    0.3122920
+50761.00   0.213355   0.273388   0.3098278    0.213340    0.273380    0.3098240
+50762.00   0.212621   0.271003   0.3072218    0.212890    0.271220    0.3072180
+50763.00   0.211614   0.268548   0.3045421    0.211850    0.268880    0.3045330
+50764.00   0.210284   0.266043   0.3018805    0.210220    0.266070    0.3018760
+50765.00   0.208730   0.263564   0.2993329    0.208840    0.263480    0.2993360
+50766.00   0.207088   0.261177   0.2969961    0.207230    0.261170    0.2969980
+50767.00   0.205438   0.258919   0.2949190    0.205490    0.258980    0.2949020
+50768.00   0.203918   0.256761   0.2930689    0.203880    0.256780    0.2930260
+50769.00   0.202655   0.254669   0.2913779    0.202760    0.254780    0.2913250
+50770.00   0.201619   0.252564   0.2897821    0.201850    0.252560    0.2897440
+50771.00   0.200584   0.250318   0.2882163    0.200620    0.250230    0.2882030
+50772.00   0.199174   0.247864   0.2866280    0.199210    0.248060    0.2866450
+50773.00   0.197451   0.245209   0.2850227    0.197520    0.245450    0.2850530
+50774.00   0.195727   0.242394   0.2834162    0.195750    0.242480    0.2834300
+50775.00   0.194034   0.239527   0.2817959    0.194200    0.239640    0.2817920
+50776.00   0.192232   0.236767   0.2801566    0.192380    0.236780    0.2801520
+50777.00   0.190303   0.234238   0.2785077    0.190440    0.234200    0.2785100
+50778.00   0.188296   0.231988   0.2768699    0.188400    0.232120    0.2768720
+50779.00   0.186082   0.230043   0.2752722    0.186160    0.230400    0.2752670
+50780.00   0.183599   0.228284   0.2737303    0.183680    0.228470    0.2737230
+50781.00   0.180925   0.226584   0.2722504    0.181050    0.226750    0.2722450
+50782.00   0.178200   0.224851   0.2708259    0.178370    0.225150    0.2708160
+50783.00   0.175672   0.223009   0.2694351    0.175500    0.223390    0.2694290
+50784.00   0.173588   0.221047   0.2680505    0.173170    0.221480    0.2680540
+50785.00   0.171942   0.219053   0.2666417    0.171810    0.219080    0.2666530
+50786.00   0.170344   0.217157   0.2651830    0.170440    0.217090    0.2651940
+50787.00   0.168527   0.215369   0.2636186    0.168750    0.215580    0.2636170
+50788.00   0.166342   0.213626   0.2618905    0.166520    0.213890    0.2618910
+50789.00   0.163903   0.211872   0.2599495    0.164000    0.212020    0.2599740
+50790.00   0.161422   0.210090   0.2577994    0.161480    0.210280    0.2578130
+50791.00   0.159046   0.208306   0.2555327    0.159250    0.208410    0.2555200
+50792.00   0.157016   0.206578   0.2532611    0.157260    0.206590    0.2532500
+50793.00   0.155360   0.204964   0.2510794    0.155610    0.204990    0.2510800
+50794.00   0.153813   0.203422   0.2490314    0.154140    0.203490    0.2490330
+50795.00   0.151960   0.201840   0.2471484    0.152290    0.202230    0.2471530
+50796.00   0.149526   0.200084   0.2454378    0.149870    0.200740    0.2454510
+50797.00   0.146669   0.198079   0.2438609    0.146680    0.198370    0.2438760
+50798.00   0.143768   0.195933   0.2423544    0.143740    0.196060    0.2423610
+50799.00   0.141075   0.193902   0.2408754    0.141160    0.194160    0.2408750
+50800.00   0.138876   0.192194   0.2393864    0.139040    0.192400    0.2393930
+50801.00   0.137047   0.190980   0.2378658    0.137340    0.191070    0.2378620
+50802.00   0.134949   0.190057   0.2362873    0.135090    0.190480    0.2362660
+50803.00   0.132348   0.189130   0.2346470    0.132320    0.189640    0.2346450
+50804.00   0.129498   0.188011   0.2329639    0.129690    0.188190    0.2329880
+50805.00   0.126543   0.186609   0.2312498    0.126820    0.186830    0.2312730
+50806.00   0.123555   0.184907   0.2295451    0.123590    0.185080    0.2295430
+50807.00   0.120723   0.183142   0.2278943    0.120750    0.183220    0.2278720
+50808.00   0.118041   0.181462   0.2263238    0.118120    0.181490    0.2263070
+50809.00   0.115429   0.179926   0.2248520    0.115510    0.179870    0.2248460
+50810.00   0.112782   0.178571   0.2234795    0.112950    0.178530    0.2234750
+50811.00   0.110042   0.177409   0.2221795    0.110410    0.177530    0.2221700
+50812.00   0.107282   0.176420   0.2208913    0.107600    0.176500    0.2208800
+50813.00   0.104669   0.175520   0.2195567    0.104840    0.175500    0.2195510
+50814.00   0.102330   0.174610   0.2181268    0.102620    0.174590    0.2181430
+50815.00   0.100067   0.173756   0.2165474    0.100400    0.173810    0.2165920
+50816.00   0.097717   0.173046   0.2147880    0.097860    0.173110    0.2148370
+50817.00   0.095308   0.172486   0.2128524    0.095580    0.172660    0.2128680
+50818.00   0.092881   0.172032   0.2107892    0.093160    0.172300    0.2107640
+50819.00   0.090425   0.171596   0.2087156    0.090530    0.171710    0.2086890
+50820.00   0.087912   0.171078   0.2067344    0.088120    0.171270    0.2067320
+50821.00   0.085272   0.170438   0.2048885    0.085520    0.170630    0.2048730
+50822.00   0.082407   0.169723   0.2032072    0.082510    0.169920    0.2031800
+50823.00   0.079324   0.169046   0.2016899    0.079420    0.168960    0.2016870
+50824.00   0.076072   0.168515   0.2002746    0.076390    0.168470    0.2002920
+50825.00   0.072675   0.168147   0.1988820    0.072880    0.168310    0.1988680
+50826.00   0.069398   0.167910   0.1974576    0.069500    0.167940    0.1974300
+50827.00   0.066566   0.167768   0.1959508    0.066770    0.167790    0.1959580
+50828.00   0.063657   0.167608   0.1943544    0.063900    0.167680    0.1943530
+50829.00   0.060477   0.167464   0.1927001    0.060510    0.167550    0.1926850
+50830.00   0.057260   0.167422   0.1910127    0.057290    0.167510    0.1910200
+50831.00   0.054151   0.167516   0.1892952    0.054110    0.167560    0.1893100
+50832.00   0.051100   0.167743   0.1875474    0.051120    0.167890    0.1875500
+50833.00   0.047984   0.167986   0.1857901    0.048350    0.168320    0.1857890
+50834.00   0.044715   0.168028   0.1840492    0.044980    0.168210    0.1840490
+50835.00   0.041334   0.167746   0.1823291    0.041680    0.167720    0.1823160
+50836.00   0.037958   0.167314   0.1806273    0.038150    0.167320    0.1806180
+50837.00   0.034688   0.166906   0.1789316    0.034730    0.167260    0.1789340
+50838.00   0.031556   0.166612   0.1772322    0.031770    0.167010    0.1772150
+50839.00   0.028551   0.166534   0.1755271    0.028770    0.166600    0.1754970
+50840.00   0.025795   0.166733   0.1737632    0.025910    0.166860    0.1737480
+50841.00   0.023234   0.167166   0.1718530    0.023200    0.167300    0.1718700
+50842.00   0.020394   0.167717   0.1697221    0.020430    0.167790    0.1697400
+50843.00   0.017343   0.168286   0.1673762    0.017520    0.168400    0.1673530
+50844.00   0.014234   0.168811   0.1648804    0.014380    0.168980    0.1648510
+50845.00   0.011108   0.169359   0.1623325    0.011350    0.169560    0.1623420
+50846.00   0.007955   0.170019   0.1597821    0.007820    0.170180    0.1597890
+50847.00   0.004686   0.170837   0.1572725    0.004450    0.170980    0.1572640
+50848.00   0.001321   0.171691   0.1548339    0.001740    0.171850    0.1548260
+50849.00  -0.001925   0.172410   0.1524679   -0.001740    0.172650    0.1524600
+50850.00  -0.004907   0.173114   0.1502065   -0.005010    0.173440    0.1501780
+50851.00  -0.007577   0.173869   0.1480662   -0.007560    0.174140    0.1480560
+50852.00  -0.010035   0.174566   0.1459967   -0.009850    0.174770    0.1460250
+50853.00  -0.012460   0.175079   0.1439594   -0.012370    0.175200    0.1439480
+50854.00  -0.015089   0.175334   0.1419780   -0.014930    0.175270    0.1419500
+50855.00  -0.017954   0.175401   0.1400520   -0.017950    0.175420    0.1400530
+50856.00  -0.020654   0.175617   0.1381159   -0.020750    0.175750    0.1381030
+50857.00  -0.023038   0.176036   0.1361486   -0.022900    0.176290    0.1361190
+50858.00  -0.025089   0.176594   0.1341574   -0.025110    0.176620    0.1341540
+50859.00  -0.026885   0.177371   0.1321610   -0.026860    0.177210    0.1321700
+50860.00  -0.028529   0.178429   0.1301747   -0.028500    0.178670    0.1302110
+50861.00  -0.030138   0.179743   0.1282129   -0.030170    0.179970    0.1282310
+50862.00  -0.032005   0.181204   0.1263079   -0.031880    0.181340    0.1262970
+50863.00  -0.034594   0.182632   0.1244965   -0.034580    0.182820    0.1244970
+50864.00  -0.037751   0.183914   0.1227247   -0.037650    0.184250    0.1227710
+50865.00  -0.041186   0.185022   0.1209348   -0.040900    0.185400    0.1210210
+50866.00  -0.044703   0.185960   0.1191234   -0.044630    0.186210    0.1191930
+50867.00  -0.047998   0.186807   0.1172605   -0.048200    0.186940    0.1172860
+50868.00  -0.050931   0.187634   0.1152941   -0.051200    0.187930    0.1152930
+50869.00  -0.053569   0.188438   0.1131771   -0.053470    0.188800    0.1131760
+50870.00  -0.055810   0.189277   0.1108978   -0.055720    0.189430    0.1109010
+50871.00  -0.057486   0.190283   0.1084770   -0.057560    0.190280    0.1084740
+50872.00  -0.058657   0.191598   0.1059310   -0.058660    0.191770    0.1059310
+50873.00  -0.059602   0.193205   0.1032905   -0.059380    0.193340    0.1033060
+50874.00  -0.060660   0.194960   0.1006586   -0.060390    0.194970    0.1006710
+50875.00  -0.062008   0.196729   0.0981386   -0.061790    0.196830    0.0981390
+50876.00  -0.063592   0.198476   0.0957946   -0.063460    0.198530    0.0957850
+50877.00  -0.065267   0.200360   0.0936210   -0.065140    0.200500    0.0935970
+50878.00  -0.066905   0.202319   0.0915841   -0.066730    0.202460    0.0915420
+50879.00  -0.068490   0.204312   0.0896482   -0.068240    0.204350    0.0895940
+50880.00  -0.070085   0.206386   0.0877786   -0.070080    0.206330    0.0877340
+50881.00  -0.071696   0.208509   0.0859377   -0.071740    0.208600    0.0859110
+50882.00  -0.073337   0.210604   0.0840788   -0.073230    0.210690    0.0840630
+50883.00  -0.074912   0.212602   0.0821452   -0.074840    0.212610    0.0821340
+50884.00  -0.076183   0.214536   0.0800910   -0.076150    0.214770    0.0801100
+50885.00  -0.077239   0.216649   0.0779831   -0.077210    0.216810    0.0780040
+50886.00  -0.078422   0.218857   0.0758586   -0.078270    0.219010    0.0758610
+50887.00  -0.080159   0.220952   0.0737412   -0.079990    0.221170    0.0737280
+50888.00  -0.082383   0.222816   0.0716611   -0.082370    0.222800    0.0716340
+50889.00  -0.084734   0.224483   0.0696389   -0.084700    0.224290    0.0696090
+50890.00  -0.086987   0.226085   0.0676945   -0.086850    0.226100    0.0676700
+50891.00  -0.089008   0.227692   0.0658367   -0.088830    0.227490    0.0658220
+50892.00  -0.090879   0.229385   0.0640498   -0.090580    0.229270    0.0640470
+50893.00  -0.092870   0.231116   0.0623018   -0.092640    0.231340    0.0623090
+50894.00  -0.094860   0.232676   0.0605486   -0.094790    0.232860    0.0605620
+50895.00  -0.096558   0.234047   0.0587419   -0.096640    0.234260    0.0587590
+50896.00  -0.097957   0.235445   0.0568313   -0.097950    0.235530    0.0568400
+50897.00  -0.099289   0.237069   0.0547597   -0.099210    0.237070    0.0547480
+50898.00  -0.100687   0.238870   0.0524515   -0.100620    0.239000    0.0524510
+50899.00  -0.102256   0.240823   0.0499414   -0.102090    0.240920    0.0499410
+50900.00  -0.104010   0.242915   0.0473499   -0.103850    0.242890    0.0473420
+50901.00  -0.105925   0.245145   0.0447784   -0.105760    0.245180    0.0447980
+50902.00  -0.107733   0.247444   0.0422919   -0.107670    0.247570    0.0423000
+50903.00  -0.109077   0.249741   0.0399650   -0.109050    0.249770    0.0399470
+50904.00  -0.109935   0.252103   0.0378239   -0.109830    0.252060    0.0378020
+50905.00  -0.110527   0.254548   0.0358353   -0.110280    0.254420    0.0358140
+50906.00  -0.111154   0.256961   0.0339580   -0.110870    0.256870    0.0339240
+50907.00  -0.112007   0.259254   0.0321401   -0.111810    0.259280    0.0321020
+50908.00  -0.112987   0.261411   0.0303301   -0.112870    0.261520    0.0303230
+50909.00  -0.113778   0.263513   0.0284947   -0.113770    0.263420    0.0285300
+50910.00  -0.114131   0.265742   0.0266181   -0.114090    0.265490    0.0266580
+50911.00  -0.114147   0.268209   0.0246935   -0.114030    0.268270    0.0247080
+50912.00  -0.113999   0.270830   0.0227298   -0.113910    0.270980    0.0227410
+50913.00  -0.113829   0.273649   0.0207804   -0.113910    0.273640    0.0207910
+50914.00  -0.113728   0.276606   0.0188422   -0.113670    0.276770    0.0188450
+50915.00  -0.113850   0.279529   0.0168902   -0.113640    0.279590    0.0168970
+50916.00  -0.114218   0.282316   0.0149437   -0.114090    0.282260    0.0149670
+50917.00  -0.114694   0.284973   0.0130558   -0.114540    0.285160    0.0130720
+50918.00  -0.115071   0.287594   0.0112541   -0.114800    0.287640    0.0112440
+50919.00  -0.114962   0.290211   0.0095180   -0.114880    0.290140    0.0095240
+50920.00  -0.114593   0.292907   0.0078497   -0.114520    0.292890    0.0078650
+50921.00  -0.114123   0.295738   0.0061926   -0.113800    0.295860    0.0061990
+50922.00  -0.113765   0.298599   0.0044955   -0.113500    0.298690    0.0044940
+50923.00  -0.113651   0.301376   0.0027115   -0.113460    0.301360    0.0027230
+50924.00  -0.113687   0.304101   0.0007922   -0.113590    0.304190    0.0008070
+50925.00  -0.113745   0.306874  -0.0013031   -0.113720    0.307070   -0.0012960
+50926.00  -0.113785   0.309763  -0.0035829   -0.113540    0.309850   -0.0035750
+50927.00  -0.113731   0.312527  -0.0060392   -0.113670    0.312610   -0.0060240
+50928.00  -0.113542   0.314955  -0.0086030   -0.113670    0.315030   -0.0085900
+50929.00  -0.113192   0.317088  -0.0111380   -0.113000    0.317070   -0.0111280
+50930.00  -0.112849   0.319179  -0.0135214   -0.112520    0.319060   -0.0135040
+50931.00  -0.112779   0.321423  -0.0156958   -0.112730    0.321280   -0.0156750
+50932.00  -0.113003   0.323784  -0.0176699   -0.113080    0.323640   -0.0176610
+50933.00  -0.113272   0.326044  -0.0195105   -0.113090    0.326010   -0.0195140
+50934.00  -0.113419   0.328142  -0.0212989   -0.113480    0.328220   -0.0213010
+50935.00  -0.113532   0.330140  -0.0230801   -0.113460    0.330150   -0.0230680
+50936.00  -0.113724   0.332146  -0.0248627   -0.113500    0.332250   -0.0248360
+50937.00  -0.113999   0.334250  -0.0266435   -0.113900    0.334350   -0.0266060
+50938.00  -0.114283   0.336511  -0.0284218   -0.114060    0.336460   -0.0283880
+50939.00  -0.114503   0.338910  -0.0302130   -0.114270    0.338950   -0.0301970
+50940.00  -0.114596   0.341219  -0.0320567   -0.114420    0.341170   -0.0320350
+50941.00  -0.114527   0.343367  -0.0339048   -0.114350    0.343360   -0.0338790
+50942.00  -0.114308   0.345406  -0.0357187   -0.114230    0.345460   -0.0357040
+50943.00  -0.113923   0.347417  -0.0374953   -0.113610    0.347380   -0.0374910
+50944.00  -0.113420   0.349476  -0.0392108   -0.113120    0.349430   -0.0392160
+50945.00  -0.112913   0.351594  -0.0408403   -0.112850    0.351680   -0.0408540
+50946.00  -0.112428   0.353738  -0.0423730   -0.112340    0.353770   -0.0423860
+50947.00  -0.111946   0.355832  -0.0438200   -0.111790    0.355760   -0.0438250
+50948.00  -0.111435   0.357812  -0.0452267   -0.111280    0.357820   -0.0452220
+50949.00  -0.110968   0.359665  -0.0466667   -0.110670    0.359770   -0.0466610
+50950.00  -0.110639   0.361433  -0.0482169   -0.110410    0.361540   -0.0482240
+50951.00  -0.110376   0.363189  -0.0499317   -0.110290    0.363160   -0.0499590
+50952.00  -0.110102   0.365018  -0.0518421   -0.110140    0.365110   -0.0518730
+50953.00  -0.109967   0.367001  -0.0539483   -0.109960    0.367090   -0.0539550
+50954.00  -0.109945   0.369171  -0.0562109   -0.109810    0.369190   -0.0561900
+50955.00  -0.109673   0.371698  -0.0585571   -0.109650    0.371850   -0.0585430
+50956.00  -0.109234   0.374455  -0.0609492   -0.109220    0.374630   -0.0609400
+50957.00  -0.108731   0.377218  -0.0632756   -0.108590    0.377330   -0.0632740
+50958.00  -0.108142   0.379851  -0.0654284   -0.107930    0.379900   -0.0654430
+50959.00  -0.107668   0.382295  -0.0673610   -0.107430    0.382270   -0.0673860
+50960.00  -0.107472   0.384492  -0.0690754   -0.107250    0.384510   -0.0690950
+50961.00  -0.107423   0.386358  -0.0705961   -0.107410    0.386640   -0.0706060
+50962.00  -0.107151   0.387984  -0.0719801   -0.107110    0.388050   -0.0719760
+50963.00  -0.106549   0.389639  -0.0733016   -0.106410    0.389480   -0.0732880
+50964.00  -0.105883   0.391519  -0.0746174   -0.105720    0.391520   -0.0746120
+50965.00  -0.105316   0.393568  -0.0759485   -0.105150    0.393650   -0.0759530
+50966.00  -0.104823   0.395623  -0.0772754   -0.104610    0.395670   -0.0772840
+50967.00  -0.104525   0.397545  -0.0785861   -0.104580    0.397600   -0.0785930
+50968.00  -0.104589   0.399252  -0.0798820   -0.104650    0.399280   -0.0798900
+50969.00  -0.104719   0.400837  -0.0811497   -0.104610    0.400860   -0.0811560
+50970.00  -0.104591   0.402402  -0.0823602   -0.104480    0.402480   -0.0823650
+50971.00  -0.104185   0.403981  -0.0834622   -0.104060    0.404030   -0.0834740
+50972.00  -0.103655   0.405577  -0.0844369   -0.103460    0.405670   -0.0844450
+50973.00  -0.103042   0.407182  -0.0852883   -0.102880    0.407220   -0.0852860
+50974.00  -0.102217   0.408801  -0.0860368   -0.102140    0.408820   -0.0860440
+50975.00  -0.101272   0.410408  -0.0867308   -0.101130    0.410530   -0.0867380
+50976.00  -0.100513   0.411926  -0.0874101   -0.100270    0.412130   -0.0873920
+50977.00  -0.100010   0.413302  -0.0881062   -0.099840    0.413470   -0.0880890
+50978.00  -0.099407   0.414567  -0.0888650   -0.099300    0.414620   -0.0888680
+50979.00  -0.098389   0.415830  -0.0897238   -0.098200    0.415820   -0.0897280
+50980.00  -0.097022   0.417175  -0.0906571   -0.096830    0.417220   -0.0906520
+50981.00  -0.095357   0.418640  -0.0916396   -0.095290    0.418670   -0.0916240
+50982.00  -0.093646   0.420329  -0.0926459   -0.093450    0.420380   -0.0926230
+50983.00  -0.091783   0.421990  -0.0936208   -0.091580    0.422120   -0.0936100
+50984.00  -0.089921   0.423490  -0.0945457   -0.089720    0.423670   -0.0945390
+50985.00  -0.088168   0.424852  -0.0953771   -0.087960    0.425010   -0.0953740
+50986.00  -0.086454   0.426143  -0.0960672   -0.086300    0.426230   -0.0960770
+50987.00  -0.084694   0.427473  -0.0966007   -0.084560    0.427490   -0.0966180
+50988.00  -0.082934   0.428940  -0.0970187   -0.082690    0.428930   -0.0970360
+50989.00  -0.081371   0.430507  -0.0974038   -0.081120    0.430630   -0.0974240
+50990.00  -0.079860   0.431951  -0.0977963   -0.079670    0.432110   -0.0978250
+50991.00  -0.078218   0.433227  -0.0982229   -0.078070    0.433380   -0.0982420
+50992.00  -0.076251   0.434408  -0.0986901   -0.076120    0.434520   -0.0986920
+50993.00  -0.073849   0.435647  -0.0992107   -0.073650    0.435640   -0.0992050
+50994.00  -0.071100   0.437082  -0.0997910   -0.070940    0.437030   -0.0997880
+50995.00  -0.068239   0.438694  -0.1004131   -0.068160    0.438840   -0.1004110
+50996.00  -0.065567   0.440347  -0.1010373   -0.065400    0.440690   -0.1010300
+50997.00  -0.063009   0.441996  -0.1016024   -0.062830    0.442190   -0.1015890
+50998.00  -0.060511   0.443674  -0.1020711   -0.060370    0.443770   -0.1020530
+50999.00  -0.057993   0.445381  -0.1024447   -0.057850    0.445490   -0.1024230
+51000.00  -0.055404   0.447048  -0.1027229   -0.055190    0.447270   -0.1027270
+51001.00  -0.052822   0.448568  -0.1029010   -0.052640    0.448880   -0.1029200
+51002.00  -0.050272   0.449909  -0.1029867   -0.050110    0.450080   -0.1030040
+51003.00  -0.047789   0.451171  -0.1030078   -0.047580    0.451160   -0.1030060
+51004.00  -0.045284   0.452559  -0.1030458   -0.045060    0.452530   -0.1030150
+51005.00  -0.042770   0.454154  -0.1031990   -0.042630    0.454360   -0.1031510
+51006.00  -0.040237   0.455835  -0.1035270   -0.040080    0.456150   -0.1034910
+51007.00  -0.037743   0.457466  -0.1040342   -0.037610    0.457660   -0.1040260
+51008.00  -0.035317   0.458998  -0.1046856   -0.035190    0.459100   -0.1046950
+51009.00  -0.032985   0.460388  -0.1054234   -0.032820    0.460470   -0.1054320
+51010.00  -0.030768   0.461506  -0.1061780   -0.030630    0.461620   -0.1061790
+51011.00  -0.028607   0.462383  -0.1068703   -0.028420    0.462510   -0.1068730
+51012.00  -0.026513   0.463176  -0.1074437   -0.026280    0.463320   -0.1074530
+51013.00  -0.024528   0.463998  -0.1078768   -0.024350    0.464190   -0.1078910
+51014.00  -0.022578   0.464858  -0.1081687   -0.022400    0.465070   -0.1081780
+51015.00  -0.020654   0.465732  -0.1083355   -0.020390    0.465930   -0.1083390
+51016.00  -0.018742   0.466609  -0.1084270   -0.018520    0.466820   -0.1084240
+51017.00  -0.016591   0.467461  -0.1085268   -0.016390    0.467730   -0.1085030
+51018.00  -0.014288   0.468234  -0.1086888   -0.014030    0.468560   -0.1086470
+51019.00  -0.011898   0.468963  -0.1089490   -0.011710    0.469190   -0.1089050
+51020.00  -0.009354   0.469790  -0.1093187   -0.009170    0.469900   -0.1092880
+51021.00  -0.006753   0.470803  -0.1097667   -0.006510    0.470970   -0.1097590
+51022.00  -0.004257   0.471952  -0.1102513   -0.004050    0.472140   -0.1102550
+51023.00  -0.001928   0.473168  -0.1107348   -0.001800    0.473270   -0.1107300
+51024.00   0.000141   0.474366  -0.1111979    0.000400    0.474440   -0.1111900
+51025.00   0.002045   0.475412  -0.1116748    0.002240    0.475590   -0.1116620
+51026.00   0.003902   0.476287  -0.1121413    0.004030    0.476470   -0.1121250
+51027.00   0.005810   0.476934  -0.1125566    0.006030    0.477150   -0.1125390
+51028.00   0.007707   0.477354  -0.1129220    0.007910    0.477580   -0.1129010
+51029.00   0.009695   0.477689  -0.1132513    0.009780    0.477780   -0.1132420
+51030.00   0.011888   0.478048  -0.1135649    0.011970    0.478280   -0.1135780
+51031.00   0.014036   0.478378  -0.1138882    0.014310    0.478670   -0.1138850
+51032.00   0.016013   0.478564  -0.1142787    0.016310    0.478810   -0.1142650
+51033.00   0.017857   0.478532  -0.1147998    0.018000    0.478750   -0.1147840
+51034.00   0.019846   0.478394  -0.1154884    0.020000    0.478530   -0.1154780
+51035.00   0.022136   0.478349  -0.1163435    0.022360    0.478550   -0.1163610
+51036.00   0.024683   0.478428  -0.1173135    0.024920    0.478620   -0.1172870
+51037.00   0.027507   0.478537  -0.1183214    0.027750    0.478650   -0.1183140
+51038.00   0.030469   0.478490  -0.1192916    0.030740    0.478710   -0.1193330
+51039.00   0.033161   0.478373  -0.1201373    0.033470    0.478530   -0.1201380
+51040.00   0.035385   0.478230  -0.1208043    0.035640    0.478380   -0.1208020
+51041.00   0.037338   0.477954  -0.1212799    0.037440    0.478200   -0.1212930
+51042.00   0.039383   0.477571  -0.1215970    0.039480    0.477780   -0.1215910
+51043.00   0.041616   0.477282  -0.1218310    0.041850    0.477460   -0.1217910
+51044.00   0.043877   0.477170  -0.1220399    0.044010    0.477310   -0.1220280
+51045.00   0.046118   0.477172  -0.1222411    0.046250    0.477350   -0.1222380
+51046.00   0.048308   0.477138  -0.1224999    0.048530    0.477440   -0.1224530
+51047.00   0.050364   0.476926  -0.1228631    0.050550    0.477220   -0.1228260
+51048.00   0.052280   0.476613  -0.1233193    0.052440    0.476810   -0.1233090
+51049.00   0.054145   0.476412  -0.1238454    0.054320    0.476560   -0.1238350
+51050.00   0.056077   0.476410  -0.1244283    0.056240    0.476540   -0.1244130
+51051.00   0.058154   0.476479  -0.1250667    0.058240    0.476620   -0.1250660
+51052.00   0.060476   0.476406  -0.1257468    0.060590    0.476610   -0.1257590
+51053.00   0.063022   0.476301  -0.1263679    0.063220    0.476500   -0.1263670
+51054.00   0.065457   0.476258  -0.1269255    0.065630    0.476440   -0.1268960
+51055.00   0.067554   0.476216  -0.1274719    0.067680    0.476380   -0.1274620
+51056.00   0.069401   0.476025  -0.1280318    0.069550    0.476230   -0.1280370
+51057.00   0.071120   0.475561  -0.1286135    0.071240    0.475620   -0.1285890
+51058.00   0.072844   0.474730  -0.1292386    0.073020    0.474780   -0.1292390
+51059.00   0.074764   0.473551  -0.1299286    0.074860    0.473590   -0.1299930
+51060.00   0.077100   0.472242  -0.1307263    0.077130    0.472220   -0.1307450
+51061.00   0.079771   0.471042  -0.1316873    0.079940    0.471050   -0.1316510
+51062.00   0.082436   0.470009  -0.1328413    0.082560    0.470090   -0.1328540
+51063.00   0.084904   0.469113  -0.1341544    0.084950    0.469130   -0.1341840
+51064.00   0.087182   0.468379  -0.1355772    0.087280    0.468310   -0.1355640
+51065.00   0.089300   0.467798  -0.1370359    0.089440    0.467860   -0.1370290
+51066.00   0.091338   0.467196  -0.1384475    0.091430    0.467320   -0.1384850
+51067.00   0.093191   0.466586  -0.1397068    0.093290    0.466610   -0.1397250
+51068.00   0.094945   0.466045  -0.1407607    0.095020    0.466090   -0.1407430
+51069.00   0.096838   0.465596  -0.1416295    0.096750    0.465690   -0.1416630
+51070.00   0.099104   0.465229  -0.1423579    0.099100    0.465260   -0.1423780
+51071.00   0.101649   0.464944  -0.1430406    0.101750    0.464930   -0.1430220
+51072.00   0.104134   0.464679  -0.1437528    0.104290    0.464720   -0.1437630
+51073.00   0.106262   0.464282  -0.1445295    0.106540    0.464300   -0.1445660
+51074.00   0.107886   0.463595  -0.1453877    0.108060    0.463730   -0.1453830
+51075.00   0.109157   0.462458  -0.1463485    0.109190    0.462570   -0.1463160
+51076.00   0.110451   0.461014  -0.1474139    0.110510    0.460960   -0.1474310
+51077.00   0.112012   0.459494  -0.1485386    0.112080    0.459540   -0.1485550
+51078.00   0.113860   0.457995  -0.1496770    0.113860    0.458050   -0.1496660
+51079.00   0.115813   0.456545  -0.1507847    0.115840    0.456520   -0.1507800
+51080.00   0.117554   0.455099  -0.1518337    0.117640    0.455060   -0.1518410
+51081.00   0.119055   0.453760  -0.1528303    0.119170    0.453760   -0.1528260
+51082.00   0.120278   0.452516  -0.1537788    0.120390    0.452570   -0.1537680
+51083.00   0.121282   0.451171  -0.1546743    0.121340    0.451320   -0.1546930
+51084.00   0.122230   0.449599  -0.1555100    0.122300    0.449720   -0.1555430
+51085.00   0.123196   0.447930  -0.1563226    0.123320    0.447910   -0.1563210
+51086.00   0.124155   0.446382  -0.1571765    0.124320    0.446400   -0.1571750
+51087.00   0.125228   0.445043  -0.1581667    0.125350    0.445040   -0.1581770
+51088.00   0.126461   0.443833  -0.1593503    0.126530    0.443810   -0.1593500
+51089.00   0.128001   0.442525  -0.1607548    0.128040    0.442560   -0.1607750
+51090.00   0.129722   0.441128  -0.1623864    0.129790    0.441260   -0.1623690
+51091.00   0.131406   0.439608  -0.1642040    0.131470    0.439610   -0.1641980
+51092.00   0.132907   0.437892  -0.1661050    0.132950    0.438040   -0.1661190
+51093.00   0.134223   0.435948  -0.1679813    0.134300    0.436030   -0.1679800
+51094.00   0.135605   0.433878  -0.1697762    0.135650    0.433830   -0.1697670
+51095.00   0.137157   0.431974  -0.1714343    0.137210    0.431860   -0.1714380
+51096.00   0.138748   0.430520  -0.1729566    0.138840    0.430600   -0.1729550
+51097.00   0.140117   0.429469  -0.1743488    0.140170    0.429510   -0.1743740
+51098.00   0.141200   0.428517  -0.1756317    0.141300    0.428710   -0.1756750
+51099.00   0.142006   0.427407  -0.1768635    0.142080    0.427600   -0.1768890
+51100.00   0.142798   0.426003  -0.1781129    0.142800    0.426040   -0.1781180
+51101.00   0.143900   0.424493  -0.1794441    0.144040    0.424490   -0.1794610
+51102.00   0.145221   0.423152  -0.1808316    0.145380    0.423230   -0.1808420
+51103.00   0.146618   0.422003  -0.1822482    0.146710    0.422100   -0.1822120
+51104.00   0.147994   0.420910  -0.1836933    0.148110    0.420990   -0.1836610
+51105.00   0.149208   0.419738  -0.1851581    0.149290    0.419730   -0.1851800
+51106.00   0.150207   0.418399  -0.1866206    0.150230    0.418400   -0.1866130
+51107.00   0.151112   0.416821  -0.1880692    0.151150    0.416880   -0.1880530
+51108.00   0.151751   0.414917  -0.1894838    0.151950    0.414940   -0.1895090
+51109.00   0.152151   0.412904  -0.1908182    0.152320    0.412880   -0.1908230
+51110.00   0.152450   0.411039  -0.1920780    0.152530    0.411040   -0.1920590
+51111.00   0.152867   0.409395  -0.1933079    0.152960    0.409450   -0.1933040
+51112.00   0.153419   0.407837  -0.1945276    0.153510    0.407950   -0.1945460
+51113.00   0.154100   0.406206  -0.1957206    0.154190    0.406270   -0.1957380
+51114.00   0.154951   0.404482  -0.1969058    0.155000    0.404560   -0.1969190
+51115.00   0.155988   0.402752  -0.1981645    0.156040    0.402790   -0.1981300
+51116.00   0.157099   0.401147  -0.1995944    0.157170    0.401180   -0.1995050
+51117.00   0.158205   0.399656  -0.2012498    0.158280    0.399690   -0.2011920
+51118.00   0.159372   0.398227  -0.2031347    0.159470    0.398270   -0.2031340
+51119.00   0.160608   0.396796  -0.2051970    0.160720    0.396800   -0.2052100
+51120.00   0.161800   0.395350  -0.2073295    0.161840    0.395320   -0.2073330
+51121.00   0.162759   0.393866  -0.2094137    0.162790    0.393910   -0.2094130
+51122.00   0.163186   0.392206  -0.2113694    0.163190    0.392310   -0.2113550
+51123.00   0.163127   0.390338  -0.2131451    0.163130    0.390500   -0.2131330
+51124.00   0.163177   0.388193  -0.2147595    0.163230    0.388320   -0.2147600
+51125.00   0.163540   0.385840  -0.2162531    0.163570    0.385860   -0.2162550
+51126.00   0.164135   0.383444  -0.2176863    0.164150    0.383480   -0.2176810
+51127.00   0.164793   0.381095  -0.2191246    0.164860    0.381130   -0.2191100
+51128.00   0.165431   0.378844  -0.2205953    0.165440    0.378810   -0.2205850
+51129.00   0.166094   0.376752  -0.2220940    0.166110    0.376800   -0.2220960
+51130.00   0.166560   0.374724  -0.2236125    0.166620    0.374820   -0.2236190
+51131.00   0.166577   0.372674  -0.2251363    0.166590    0.372680   -0.2251430
+51132.00   0.166129   0.370621  -0.2266458    0.166180    0.370690   -0.2266690
+51133.00   0.165389   0.368567  -0.2281187    0.165440    0.368620   -0.2281310
+51134.00   0.164616   0.366565  -0.2295496    0.164590    0.366530   -0.2295470
+51135.00   0.164092   0.364644  -0.2309382    0.164060    0.364670   -0.2309380
+51136.00   0.163768   0.362711  -0.2322849    0.163740    0.362710   -0.2322770
+51137.00   0.163194   0.360797  -0.2335826    0.163200    0.360760   -0.2335820
+51138.00   0.161963   0.358907  -0.2348381    0.162040    0.359040   -0.2348520
+51139.00   0.160085   0.356946  -0.2360666    0.160030    0.357130   -0.2360610
+51140.00   0.158115   0.354896  -0.2372925    0.158090    0.354910   -0.2372750
+51141.00   0.156354   0.352913  -0.2385373    0.156480    0.352940   -0.2385630
+51142.00   0.154814   0.351026  -0.2398232    0.154930    0.351080   -0.2398440
+51143.00   0.153691   0.349091  -0.2412084    0.153770    0.349160   -0.2411550
+51144.00   0.152791   0.347042  -0.2427449    0.152870    0.347140   -0.2427040
+51145.00   0.151834   0.344872  -0.2444448    0.151860    0.344890   -0.2444480
+51146.00   0.150915   0.342720  -0.2462737    0.150870    0.342630   -0.2462790
+51147.00   0.150141   0.340779  -0.2481367    0.150250    0.340730   -0.2481490
+51148.00   0.149184   0.339098  -0.2499346    0.149440    0.339290   -0.2499460
+51149.00   0.147951   0.337562  -0.2515983    0.148060    0.337590   -0.2516030
+51150.00   0.147004   0.336146  -0.2531202    0.146980    0.336120   -0.2531290
+51151.00   0.146455   0.335017  -0.2544842    0.146520    0.335080   -0.2544910
+51152.00   0.145863   0.334151  -0.2557114    0.145960    0.334300   -0.2557150
+51153.00   0.144933   0.333320  -0.2568539    0.144940    0.333360   -0.2568680
+51154.00   0.143695   0.332385  -0.2579548    0.143730    0.332270   -0.2579490
+51155.00   0.142269   0.331265  -0.2590365    0.142260    0.331140   -0.2590450
+51156.00   0.140845   0.329776  -0.2601098    0.140850    0.329720   -0.2601250
+51157.00   0.139654   0.327889  -0.2612229    0.139700    0.327880   -0.2611980
+51158.00   0.138670   0.325829  -0.2623954    0.138680    0.325810   -0.2623830
+51159.00   0.137880   0.323810  -0.2635872    0.137890    0.323810   -0.2636020
+51160.00   0.137344   0.321899  -0.2647402    0.137390    0.321940   -0.2647510
+51161.00   0.137118   0.320090  -0.2658320    0.137150    0.320130   -0.2658350
+51162.00   0.137191   0.318439  -0.2668510    0.137240    0.318410   -0.2668620
+51163.00   0.137482   0.317021  -0.2677836    0.137450    0.316970   -0.2677810
+51164.00   0.137993   0.315738  -0.2686203    0.137950    0.315730   -0.2686130
+51165.00   0.138867   0.314494  -0.2693765    0.138870    0.314470   -0.2693900
+51166.00   0.140070   0.313420  -0.2700640    0.140080    0.313340   -0.2700750
+51167.00   0.141362   0.312536  -0.2707054    0.141430    0.312550   -0.2706980
+51168.00   0.142330   0.311541  -0.2713404    0.142400    0.311680   -0.2713370
+51169.00   0.142795   0.310097  -0.2720051    0.142860    0.310170   -0.2720570
+51170.00   0.142988   0.308356  -0.2727487    0.143050    0.308250   -0.2727810
+51171.00   0.143090   0.306710  -0.2736544    0.143130    0.306690   -0.2735780
+51172.00   0.143080   0.305222  -0.2747547    0.143110    0.305330   -0.2746410
+51173.00   0.142815   0.303796  -0.2760051    0.142820    0.303820   -0.2759310
+51174.00   0.142282   0.302444  -0.2773357    0.142260    0.302430   -0.2773300
+51175.00   0.141721   0.301186  -0.2786798    0.141690    0.301160   -0.2787160
+51176.00   0.141207   0.299985  -0.2799819    0.141210    0.299910   -0.2800170
+51177.00   0.140598   0.298726  -0.2812143    0.140690    0.298710   -0.2812110
+51178.00   0.139675   0.297292  -0.2823385    0.139760    0.297390   -0.2823330
+51179.00   0.138577   0.295556   0.7166587    0.138510    0.295650    0.7166370
+51180.00   0.137785   0.293577   0.7157494    0.137710    0.293550    0.7157410
+51181.00   0.137421   0.291696   0.7148885    0.137480    0.291660    0.7148720
+51182.00   0.137132   0.290124   0.7140379    0.137190    0.290230    0.7139820
+51183.00   0.136733   0.288814   0.7131775    0.136690    0.288940    0.7131390
+51184.00   0.136397   0.287754   0.7122751    0.136360    0.287820    0.7122810
+51185.00   0.136247   0.287027   0.7112608    0.136280    0.287070    0.7112410
+51186.00   0.136118   0.286554   0.7101793    0.136140    0.286610    0.7101380
+51187.00   0.135752   0.286182   0.7091003    0.135710    0.286270    0.7091040
+51188.00   0.134909   0.285701   0.7080310    0.134860    0.285720    0.7080490
+51189.00   0.133471   0.284873   0.7069893    0.133490    0.284910    0.7069560
+51190.00   0.131464   0.283567   0.7060440    0.131450    0.283640    0.7060060
+51191.00   0.129180   0.281889   0.7052302    0.129100    0.281870    0.7052330
+51192.00   0.126962   0.279990   0.7044958    0.126900    0.280070    0.7044610
+51193.00   0.124851   0.277989   0.7038139    0.124920    0.278020    0.7037620
+51194.00   0.122714   0.276071   0.7032033    0.122820    0.276030    0.7031910
+51195.00   0.120630   0.274356   0.7026480    0.120620    0.274400    0.7026250
+51196.00   0.118887   0.272823   0.7021026    0.118870    0.272900    0.7020740
+51197.00   0.117525   0.271405   0.7014865    0.117530    0.271450    0.7014750
+51198.00   0.116539   0.270022   0.7007330    0.116510    0.270050    0.7007330
+51199.00   0.116083   0.268687   0.6997928    0.116100    0.268630    0.6997900
+51200.00   0.116118   0.267493   0.6986675    0.116140    0.267460    0.6986490
+51201.00   0.116223   0.266420   0.6973983    0.116330    0.266480    0.6973720
+51202.00   0.115937   0.265363   0.6960300    0.116040    0.265470    0.6960030
+51203.00   0.115117   0.264256   0.6946208    0.115100    0.264350    0.6945500
+51204.00   0.113991   0.263174   0.6932696    0.113920    0.263120    0.6931850
+51205.00   0.112826   0.262237   0.6920205    0.112830    0.262250    0.6919960
+51206.00   0.111620   0.261398   0.6908379    0.111640    0.261450    0.6908520
+51207.00   0.110525   0.260526   0.6896941    0.110480    0.260550    0.6896900
+51208.00   0.109379   0.259573   0.6885405    0.109400    0.259590    0.6885240
+51209.00   0.108080   0.258531   0.6873571    0.108160    0.258590    0.6873410
+51210.00   0.106598   0.257341   0.6861313    0.106640    0.257490    0.6861150
+51211.00   0.105004   0.256005   0.6848593    0.105030    0.256080    0.6848460
+51212.00   0.103396   0.254659   0.6835503    0.103380    0.254690    0.6835470
+51213.00   0.101922   0.253439   0.6822214    0.101750    0.253390    0.6822120
+51214.00   0.100916   0.252566   0.6808880    0.100770    0.252400    0.6808730
+51215.00   0.100335   0.252224   0.6795667    0.100380    0.252260    0.6795660
+51216.00   0.099624   0.252238   0.6782837    0.099720    0.252380    0.6782890
+51217.00   0.098384   0.252265   0.6770716    0.098340    0.252260    0.6770550
+51218.00   0.096675   0.252190   0.6759507    0.096530    0.252090    0.6759100
+51219.00   0.094706   0.251925   0.6749074    0.094590    0.252050    0.6748970
+51220.00   0.092622   0.251225   0.6739383    0.092500    0.251370    0.6739270
+51221.00   0.090736   0.250235   0.6730319    0.090610    0.250190    0.6730310
+51222.00   0.089163   0.249217   0.6721348    0.089120    0.249330    0.6721840
+51223.00   0.087881   0.248238   0.6711759    0.087860    0.248370    0.6711830
+51224.00   0.086783   0.247431   0.6701095    0.086770    0.247460    0.6701030
+51225.00   0.085708   0.246965   0.6689138    0.085630    0.246990    0.6689060
+51226.00   0.084445   0.246805   0.6675690    0.084480    0.246820    0.6675580
+51227.00   0.082689   0.246707   0.6660476    0.082810    0.246830    0.6660510
+51228.00   0.080806   0.246425   0.6643896    0.080790    0.246630    0.6643680
+51229.00   0.079313   0.245935   0.6626922    0.079250    0.246020    0.6626600
+51230.00   0.078445   0.245457   0.6610367    0.078400    0.245460    0.6610490
+51231.00   0.078153   0.245134   0.6594679    0.078140    0.245200    0.6594700
+51232.00   0.078078   0.244906   0.6580462    0.078080    0.244950    0.6580360
+51233.00   0.077632   0.244669   0.6568065    0.077660    0.244710    0.6568110
+51234.00   0.076472   0.244314   0.6557266    0.076510    0.244390    0.6557030
+51235.00   0.074875   0.243781   0.6547633    0.074890    0.243870    0.6547300
+51236.00   0.073082   0.243098   0.6538706    0.073090    0.243170    0.6538810
+51237.00   0.071378   0.242384   0.6529831    0.071330    0.242420    0.6529950
+51238.00   0.069983   0.241806   0.6520627    0.069960    0.241870    0.6520380
+51239.00   0.068909   0.241487   0.6511207    0.068880    0.241600    0.6510780
+51240.00   0.068114   0.241469   0.6501709    0.068060    0.241560    0.6501610
+51241.00   0.067407   0.241688   0.6491932    0.067400    0.241730    0.6492320
+51242.00   0.066561   0.242023   0.6481793    0.066570    0.242140    0.6481930
+51243.00   0.065483   0.242354   0.6471690    0.065460    0.242450    0.6471360
+51244.00   0.064348   0.242547   0.6462538    0.064360    0.242600    0.6462500
+51245.00   0.063278   0.242556   0.6454247    0.063280    0.242620    0.6454270
+51246.00   0.062192   0.242377   0.6446631    0.062040    0.242430    0.6446490
+51247.00   0.061032   0.242009   0.6439529    0.060890    0.242050    0.6439460
+51248.00   0.059969   0.241610   0.6432600    0.059840    0.241620    0.6432690
+51249.00   0.058807   0.241239   0.6425445    0.058710    0.241350    0.6425460
+51250.00   0.057514   0.240861   0.6417607    0.057520    0.241090    0.6417470
+51251.00   0.056092   0.240433   0.6408596    0.056150    0.240550    0.6408520
+51252.00   0.054562   0.239984   0.6397960    0.054620    0.239970    0.6397940
+51253.00   0.052907   0.239553   0.6385563    0.052900    0.239580    0.6385570
+51254.00   0.051136   0.239142   0.6371559    0.051100    0.239140    0.6371530
+51255.00   0.049793   0.238932   0.6356238    0.049790    0.238830    0.6356030
+51256.00   0.048941   0.239050   0.6340022    0.049020    0.238990    0.6339940
+51257.00   0.048186   0.239430   0.6323403    0.048260    0.239470    0.6323600
+51258.00   0.047327   0.239931   0.6306782    0.047260    0.239980    0.6306790
+51259.00   0.046514   0.240468   0.6290778    0.046430    0.240490    0.6290540
+51260.00   0.045565   0.240966   0.6276165    0.045540    0.241010    0.6275870
+51261.00   0.044085   0.241305   0.6263131    0.044080    0.241400    0.6263030
+51262.00   0.042017   0.241415   0.6251109    0.041950    0.241450    0.6251350
+51263.00   0.039295   0.241416   0.6239616    0.039180    0.241430    0.6239720
+51264.00   0.036347   0.241357   0.6227854    0.036230    0.241480    0.6227770
+51265.00   0.033747   0.241145   0.6215430    0.033680    0.241250    0.6215430
+51266.00   0.031676   0.240915   0.6202226    0.031690    0.240850    0.6202090
+51267.00   0.029843   0.240902   0.6188504    0.029860    0.240820    0.6188340
+51268.00   0.027952   0.241025   0.6174612    0.028000    0.241080    0.6174570
+51269.00   0.025972   0.240966   0.6160514    0.026030    0.241020    0.6160780
+51270.00   0.023889   0.240663   0.6146313    0.023870    0.240740    0.6146500
+51271.00   0.021820   0.240215   0.6132443    0.021760    0.240220    0.6132240
+51272.00   0.020052   0.239794   0.6119391    0.019960    0.239760    0.6119380
+51273.00   0.018652   0.239541   0.6107150    0.018570    0.239620    0.6107220
+51274.00   0.017384   0.239548   0.6095457    0.017320    0.239570    0.6095590
+51275.00   0.015829   0.239833   0.6084018    0.015740    0.239860    0.6084080
+51276.00   0.013791   0.240165   0.6072974    0.013730    0.240310    0.6072760
+51277.00   0.012055   0.240386   0.6062115    0.011990    0.240520    0.6061980
+51278.00   0.010998   0.240635   0.6050749    0.010940    0.240690    0.6050670
+51279.00   0.010475   0.241143   0.6038335    0.010500    0.241200    0.6038210
+51280.00   0.010119   0.241969   0.6024322    0.010210    0.242090    0.6024510
+51281.00   0.009565   0.242944   0.6008268    0.009610    0.243030    0.6008480
+51282.00   0.008800   0.243956   0.5990280    0.008830    0.243840    0.5990220
+51283.00   0.008074   0.245070   0.5971222    0.008020    0.244910    0.5971310
+51284.00   0.007323   0.246324   0.5951849    0.007300    0.246280    0.5951890
+51285.00   0.006257   0.247512   0.5932963    0.006360    0.247560    0.5932790
+51286.00   0.004782   0.248353   0.5915380    0.004820    0.248450    0.5915350
+51287.00   0.003071   0.248755   0.5899323    0.002980    0.248890    0.5899240
+51288.00   0.001508   0.248848   0.5884751    0.001420    0.248930    0.5884800
+51289.00   0.000190   0.248839   0.5871312    0.000130    0.248850    0.5871390
+51290.00  -0.001057   0.248911   0.5858655   -0.001090    0.248950    0.5858710
+51291.00  -0.002271   0.249270   0.5846479   -0.002300    0.249360    0.5846520
+51292.00  -0.003651   0.249966   0.5834051   -0.003630    0.250120    0.5833930
+51293.00  -0.005339   0.250849   0.5820993   -0.005340    0.251010    0.5820920
+51294.00  -0.006985   0.251766   0.5807227   -0.007100    0.251870    0.5807220
+51295.00  -0.008027   0.252694   0.5792838   -0.008140    0.252780    0.5792780
+51296.00  -0.008362   0.253742   0.5778108   -0.008440    0.253770    0.5777930
+51297.00  -0.008472   0.255001   0.5763380   -0.008530    0.255000    0.5763270
+51298.00  -0.008673   0.256408   0.5749065   -0.008740    0.256480    0.5749090
+51299.00  -0.009270   0.257734   0.5735522   -0.009340    0.257860    0.5735560
+51300.00  -0.010432   0.258759   0.5722963   -0.010510    0.258900    0.5723070
+51301.00  -0.011958   0.259424   0.5711562   -0.011990    0.259570    0.5711640
+51302.00  -0.013543   0.259790   0.5701485   -0.013550    0.260020    0.5701520
+51303.00  -0.014882   0.260006   0.5692803   -0.014970    0.260010    0.5692870
+51304.00  -0.015809   0.260357   0.5685207   -0.015880    0.260350    0.5685460
+51305.00  -0.016511   0.260980   0.5677978   -0.016510    0.261130    0.5678080
+51306.00  -0.017204   0.261793   0.5670197   -0.017230    0.261940    0.5670120
+51307.00  -0.017955   0.262620   0.5661215   -0.018020    0.262790    0.5661330
+51308.00  -0.018562   0.263437   0.5650617   -0.018650    0.263510    0.5650480
+51309.00  -0.018908   0.264417   0.5638338   -0.019000    0.264410    0.5638010
+51310.00  -0.019144   0.265626   0.5624372   -0.019220    0.265740    0.5624390
+51311.00  -0.019272   0.267017   0.5608976   -0.019370    0.267170    0.5609190
+51312.00  -0.019234   0.268489   0.5593032   -0.019350    0.268670    0.5593160
+51313.00  -0.019058   0.270015   0.5577666   -0.019090    0.270160    0.5577750
+51314.00  -0.019061   0.271612   0.5563793   -0.019060    0.271710    0.5563890
+51315.00  -0.019409   0.273248   0.5551731   -0.019510    0.273340    0.5551900
+51316.00  -0.019872   0.274767   0.5541002   -0.019990    0.274890    0.5541190
+51317.00  -0.020213   0.275981   0.5530726   -0.020290    0.276130    0.5530890
+51318.00  -0.020417   0.276839   0.5520267   -0.020500    0.276930    0.5520360
+51319.00  -0.020523   0.277505   0.5509281   -0.020570    0.277570    0.5509490
+51320.00  -0.020535   0.278185   0.5497378   -0.020560    0.278270    0.5497680
+51321.00  -0.020552   0.278928   0.5484707   -0.020600    0.279020    0.5484720
+51322.00  -0.020768   0.279657   0.5471797   -0.020760    0.279750    0.5471860
+51323.00  -0.021347   0.280333   0.5458784   -0.021300    0.280390    0.5459010
+51324.00  -0.022250   0.280942   0.5446124   -0.022280    0.280980    0.5446230
+51325.00  -0.023138   0.281436   0.5434745   -0.023170    0.281490    0.5434710
+51326.00  -0.023752   0.281994   0.5424832   -0.023750    0.282100    0.5424970
+51327.00  -0.024118   0.282646   0.5416162   -0.024110    0.282830    0.5416450
+51328.00  -0.024418   0.283263   0.5408527   -0.024450    0.283410    0.5408500
+51329.00  -0.024737   0.283829   0.5401746   -0.024820    0.283930    0.5401390
+51330.00  -0.025164   0.284461   0.5395663   -0.025220    0.284540    0.5395750
+51331.00  -0.025839   0.285250   0.5389945   -0.025920    0.285380    0.5390250
+51332.00  -0.026663   0.286131   0.5384164   -0.026790    0.286290    0.5383850
+51333.00  -0.027370   0.287157   0.5377925   -0.027450    0.287220    0.5377750
+51334.00  -0.027956   0.288463   0.5370730   -0.027980    0.288510    0.5371050
+51335.00  -0.028608   0.290007   0.5362194   -0.028550    0.290050    0.5362130
+51336.00  -0.029271   0.291489   0.5352299   -0.029220    0.291540    0.5352240
+51337.00  -0.029839   0.292673   0.5340830   -0.029720    0.292750    0.5341110
+51338.00  -0.030385   0.293489   0.5327898   -0.030240    0.293620    0.5327920
+51339.00  -0.030823   0.293963   0.5314166   -0.030730    0.294060    0.5314210
+51340.00  -0.031123   0.294357   0.5300536   -0.031020    0.294390    0.5300850
+51341.00  -0.031394   0.294908   0.5287990   -0.031350    0.295020    0.5287960
+51342.00  -0.031599   0.295656   0.5277405   -0.031630    0.295780    0.5277610
+51343.00  -0.031741   0.296616   0.5268796   -0.031650    0.296690    0.5269420
+51344.00  -0.032060   0.297675   0.5261601   -0.031880    0.297780    0.5261980
+51345.00  -0.032633   0.298575   0.5255218   -0.032610    0.298690    0.5255170
+51346.00  -0.033005   0.299226   0.5249077   -0.033010    0.299280    0.5249170
+51347.00  -0.033064   0.299759   0.5242518   -0.032930    0.299810    0.5242620
+51348.00  -0.032912   0.300307   0.5235385   -0.032790    0.300340    0.5235260
+51349.00  -0.032633   0.301003   0.5227939   -0.032530    0.301030    0.5228120
+51350.00  -0.032416   0.301872   0.5220497   -0.032260    0.301940    0.5220930
+51351.00  -0.032382   0.302784   0.5213298   -0.032230    0.302870    0.5213500
+51352.00  -0.032414   0.303630   0.5206856   -0.032430    0.303730    0.5206850
+51353.00  -0.032237   0.304449   0.5201823   -0.032270    0.304480    0.5202060
+51354.00  -0.032009   0.305351   0.5198199   -0.031920    0.305360    0.5198390
+51355.00  -0.032072   0.306343   0.5195874   -0.031990    0.306370    0.5195890
+51356.00  -0.032424   0.307330   0.5194846   -0.032390    0.307400    0.5195020
+51357.00  -0.032681   0.308172   0.5194888   -0.032680    0.308260    0.5194870
+51358.00  -0.032643   0.308831   0.5195759   -0.032600    0.308870    0.5196000
+51359.00  -0.032439   0.309415   0.5196994   -0.032410    0.309380    0.5197040
+51360.00  -0.032311   0.310020   0.5198139   -0.032200    0.309950    0.5198250
+51361.00  -0.032153   0.310745   0.5198820   -0.032030    0.310750    0.5199040
+51362.00  -0.031784   0.311739   0.5198545   -0.031690    0.311740    0.5198140
+51363.00  -0.031170   0.313045   0.5196951   -0.031120    0.313080    0.5197340
+51364.00  -0.030442   0.314529   0.5193487   -0.030430    0.314630    0.5194090
+51365.00  -0.029704   0.315956   0.5188292   -0.029680    0.316030    0.5188420
+51366.00  -0.028910   0.317201   0.5181918   -0.028900    0.317270    0.5181920
+51367.00  -0.027983   0.318324   0.5175273   -0.028030    0.318330    0.5175160
+51368.00  -0.027037   0.319481   0.5169244   -0.027030    0.319480    0.5169230
+51369.00  -0.026224   0.320703   0.5164345   -0.026250    0.320820    0.5164500
+51370.00  -0.025557   0.321908   0.5160590   -0.025590    0.321960    0.5160600
+51371.00  -0.025019   0.323101   0.5157613   -0.024980    0.323000    0.5157550
+51372.00  -0.024667   0.324344   0.5154946   -0.024670    0.324210    0.5154940
+51373.00  -0.024535   0.325578   0.5152068   -0.024560    0.325580    0.5152140
+51374.00  -0.024744   0.326635   0.5148456   -0.024670    0.326630    0.5148690
+51375.00  -0.025238   0.327339   0.5144052   -0.025120    0.327330    0.5144310
+51376.00  -0.025708   0.327855   0.5138917   -0.025670    0.327860    0.5138980
+51377.00  -0.025892   0.328401   0.5133447   -0.025820    0.328410    0.5133180
+51378.00  -0.025770   0.329054   0.5128171   -0.025650    0.329040    0.5127800
+51379.00  -0.025384   0.329837   0.5123367   -0.025280    0.329830    0.5123250
+51380.00  -0.024803   0.330652   0.5119213   -0.024770    0.330670    0.5119280
+51381.00  -0.024166   0.331389   0.5115879   -0.024170    0.331470    0.5115740
+51382.00  -0.023612   0.332029   0.5113555   -0.023650    0.332070    0.5113690
+51383.00  -0.023436   0.332554   0.5112222   -0.023290    0.332570    0.5112600
+51384.00  -0.023690   0.332988   0.5111749   -0.023610    0.333050    0.5111350
+51385.00  -0.023985   0.333417   0.5111988   -0.023970    0.333480    0.5111610
+51386.00  -0.024026   0.333921   0.5112401   -0.023980    0.333980    0.5112580
+51387.00  -0.023857   0.334593   0.5112419   -0.023810    0.334630    0.5112540
+51388.00  -0.023510   0.335462   0.5111595   -0.023400    0.335520    0.5111360
+51389.00  -0.023000   0.336427   0.5109628   -0.022860    0.336460    0.5109910
+51390.00  -0.022362   0.337453   0.5105973   -0.022280    0.337490    0.5106410
+51391.00  -0.021640   0.338517   0.5100551   -0.021570    0.338540    0.5100190
+51392.00  -0.020859   0.339539   0.5093789   -0.020830    0.339690    0.5093670
+51393.00  -0.019878   0.340497   0.5085788   -0.019910    0.340720    0.5086130
+51394.00  -0.018583   0.341387   0.5077202   -0.018720    0.341550    0.5077120
+51395.00  -0.017112   0.342266   0.5069471   -0.017190    0.342340    0.5069180
+51396.00  -0.015757   0.343305   0.5063317   -0.015620    0.343440    0.5063360
+51397.00  -0.014781   0.344488   0.5058809   -0.014670    0.344680    0.5058930
+51398.00  -0.014169   0.345595   0.5055680   -0.014300    0.345840    0.5055380
+51399.00  -0.013827   0.346439   0.5053520   -0.013890    0.346680    0.5053180
+51400.00  -0.013570   0.347008   0.5051508   -0.013640    0.347170    0.5051770
+51401.00  -0.013229   0.347413   0.5048772   -0.013310    0.347500    0.5048890
+51402.00  -0.012743   0.347767   0.5044897   -0.012860    0.347800    0.5044840
+51403.00  -0.012239   0.348141   0.5040260   -0.012470    0.348220    0.5040580
+51404.00  -0.011835   0.348680   0.5034709   -0.011940    0.348780    0.5034900
+51405.00  -0.011719   0.349523   0.5028289   -0.011760    0.349630    0.5028440
+51406.00  -0.011590   0.350631   0.5021460   -0.011790    0.350780    0.5021740
+51407.00  -0.011034   0.351884   0.5014710   -0.011220    0.352100    0.5014320
+51408.00  -0.010319   0.353153   0.5008619   -0.010320    0.353250    0.5008390
+51409.00  -0.009797   0.354359   0.5003492   -0.009880    0.354600    0.5003400
+51410.00  -0.009297   0.355503   0.4999451   -0.009530    0.355730    0.4999570
+51411.00  -0.008671   0.356678   0.4996400   -0.008740    0.356860    0.4997250
+51412.00  -0.008248   0.357883   0.4993711   -0.008330    0.358130    0.4993600
+51413.00  -0.008243   0.358996   0.4991033   -0.008270    0.359220    0.4990810
+51414.00  -0.008483   0.359983   0.4988082   -0.008450    0.360160    0.4988010
+51415.00  -0.008694   0.360823   0.4984339   -0.008880    0.360930    0.4984360
+51416.00  -0.008598   0.361514   0.4978839   -0.008690    0.361790    0.4979120
+51417.00  -0.008393   0.362115   0.4971095   -0.008440    0.362310    0.4971170
+51418.00  -0.008182   0.362641   0.4961321   -0.008280    0.362810    0.4961240
+51419.00  -0.007823   0.363098   0.4950086   -0.007930    0.363190    0.4950170
+51420.00  -0.007289   0.363548   0.4937904   -0.007340    0.363690    0.4937990
+51421.00  -0.006771   0.364020   0.4925551   -0.006770    0.364210    0.4925550
+51422.00  -0.006488   0.364447   0.4913924   -0.006490    0.364650    0.4913850
+51423.00  -0.006386   0.364778   0.4903690   -0.006430    0.364930    0.4903750
+51424.00  -0.006220   0.365077   0.4894913   -0.006220    0.365200    0.4895020
+51425.00  -0.005751   0.365411   0.4887284   -0.005850    0.365580    0.4887250
+51426.00  -0.004880   0.365869   0.4880370   -0.004960    0.366010    0.4880420
+51427.00  -0.003703   0.366428   0.4873721   -0.003810    0.366600    0.4873840
+51428.00  -0.002464   0.367003   0.4866972   -0.002570    0.367190    0.4867060
+51429.00  -0.001309   0.367620   0.4859923   -0.001440    0.367610    0.4860000
+51430.00  -0.000411   0.368267   0.4852548   -0.000550    0.368300    0.4852780
+51431.00   0.000061   0.368863   0.4844846    0.000030    0.369070    0.4845120
+51432.00   0.000383   0.369397   0.4836996    0.000460    0.369580    0.4837120
+51433.00   0.000863   0.369937   0.4829502    0.000800    0.370170    0.4829480
+51434.00   0.001600   0.370608   0.4822679    0.001620    0.370730    0.4822670
+51435.00   0.002414   0.371490   0.4816712    0.002450    0.371490    0.4816770
+51436.00   0.003040   0.372432   0.4811759    0.003040    0.372620    0.4811780
+51437.00   0.003503   0.373142   0.4807967    0.003510    0.373430    0.4807760
+51438.00   0.003889   0.373599   0.4805028    0.003850    0.373750    0.4804930
+51439.00   0.004184   0.373979   0.4802355    0.004070    0.374060    0.4802480
+51440.00   0.004345   0.374430   0.4799329    0.004280    0.374520    0.4799360
+51441.00   0.004513   0.374913   0.4795506    0.004470    0.375080    0.4795610
+51442.00   0.004887   0.375305   0.4790321    0.004820    0.375420    0.4790480
+51443.00   0.005344   0.375576   0.4783296    0.005330    0.375590    0.4783330
+51444.00   0.005458   0.375778   0.4774428    0.005560    0.375850    0.4774500
+51445.00   0.005210   0.375829   0.4763994    0.005150    0.376030    0.4764010
+51446.00   0.004983   0.375759   0.4752638    0.004960    0.375910    0.4752540
+51447.00   0.005018   0.375787   0.4740970    0.005000    0.375900    0.4741130
+51448.00   0.005262   0.376086   0.4729339    0.005160    0.376130    0.4729480
+51449.00   0.005564   0.376625   0.4718474    0.005430    0.376760    0.4718160
+51450.00   0.005826   0.377307   0.4708876    0.005810    0.377530    0.4708750
+51451.00   0.006099   0.378097   0.4700343    0.006070    0.378340    0.4700530
+51452.00   0.006366   0.378984   0.4692888    0.006260    0.379200    0.4692770
+51453.00   0.006616   0.379890   0.4686406    0.006580    0.380060    0.4686480
+51454.00   0.007091   0.380748   0.4679841    0.007070    0.380920    0.4680100
+51455.00   0.007760   0.381484   0.4671962    0.007850    0.381800    0.4672270
+51456.00   0.008441   0.382034   0.4662445    0.008400    0.382310    0.4662570
+51457.00   0.009156   0.382437   0.4651338    0.009120    0.382600    0.4651320
+51458.00   0.009792   0.382579   0.4638922    0.010140    0.382930    0.4639000
+51459.00   0.010453   0.382483   0.4625759    0.010710    0.382670    0.4625900
+51460.00   0.011302   0.382442   0.4612270    0.011250    0.382540    0.4612430
+51461.00   0.012327   0.382576   0.4598931    0.012270    0.382700    0.4599020
+51462.00   0.013291   0.382712   0.4586308    0.013270    0.382940    0.4586320
+51463.00   0.013933   0.382739   0.4574905    0.013950    0.382940    0.4574970
+51464.00   0.014160   0.382696   0.4564699    0.014140    0.382830    0.4564730
+51465.00   0.014011   0.382620   0.4555245    0.013960    0.382790    0.4555120
+51466.00   0.013764   0.382574   0.4546679    0.013670    0.382780    0.4546620
+51467.00   0.013723   0.382518   0.4539027    0.013670    0.382760    0.4539230
+51468.00   0.013998   0.382379   0.4531777    0.014010    0.382650    0.4531920
+51469.00   0.014481   0.382265   0.4524251    0.014510    0.382440    0.4524180
+51470.00   0.015066   0.382334   0.4515871    0.015050    0.382360    0.4515800
+51471.00   0.015704   0.382570   0.4505951    0.015740    0.382680    0.4505970
+51472.00   0.016335   0.382806   0.4493644    0.016400    0.382990    0.4493660
+51473.00   0.016759   0.382957   0.4478747    0.016850    0.383150    0.4478740
+51474.00   0.017150   0.383056   0.4461607    0.017130    0.383230    0.4461670
+51475.00   0.017702   0.383229   0.4442931    0.017730    0.383390    0.4442970
+51476.00   0.018291   0.383481   0.4423756    0.018330    0.383650    0.4423640
+51477.00   0.018863   0.383621   0.4405168    0.018810    0.383830    0.4405100
+51478.00   0.019452   0.383532   0.4388010    0.019430    0.383710    0.4387960
+51479.00   0.019978   0.383305   0.4372960    0.019970    0.383500    0.4372900
+51480.00   0.020327   0.383128   0.4359633    0.020230    0.383360    0.4359710
+51481.00   0.020502   0.383004   0.4347173    0.020480    0.383150    0.4347180
+51482.00   0.020765   0.382805   0.4334775    0.020760    0.382990    0.4334870
+51483.00   0.021271   0.382449   0.4321898    0.021280    0.382720    0.4321920
+51484.00   0.021918   0.381946   0.4308357    0.021890    0.382230    0.4308260
+51485.00   0.022609   0.381443   0.4294020    0.022700    0.381380    0.4293980
+51486.00   0.023219   0.380905   0.4278975    0.023300    0.380950    0.4279030
+51487.00   0.023757   0.380406   0.4263817    0.023730    0.380400    0.4263920
+51488.00   0.024151   0.380035   0.4248891    0.024130    0.380020    0.4249070
+51489.00   0.024358   0.379606   0.4234446    0.024420    0.379640    0.4234540
+51490.00   0.024501   0.378912   0.4220772    0.024490    0.378880    0.4220890
+51491.00   0.024892   0.378025   0.4207713    0.024840    0.377880    0.4207840
+51492.00   0.025657   0.377145   0.4195125    0.025610    0.377150    0.4195120
+51493.00   0.026741   0.376460   0.4183198    0.026750    0.376460    0.4183170
+51494.00   0.027764   0.376262   0.4171923    0.027750    0.376140    0.4172030
+51495.00   0.028474   0.376609   0.4160811    0.028440    0.376550    0.4160980
+51496.00   0.028809   0.377310   0.4149095    0.028950    0.377350    0.4149150
+51497.00   0.028761   0.377949   0.4136514    0.028830    0.378060    0.4136300
+51498.00   0.028712   0.378224   0.4123109    0.028600    0.378260    0.4122870
+51499.00   0.029229   0.378250   0.4108591    0.029220    0.378260    0.4108540
+51500.00   0.030311   0.378225   0.4092440    0.030320    0.378220    0.4092360
+51501.00   0.031599   0.378298   0.4075068    0.031550    0.378290    0.4074990
+51502.00   0.032630   0.378479   0.4057008    0.032670    0.378520    0.4057080
+51503.00   0.033130   0.378618   0.4038769    0.033180    0.378650    0.4038770
+51504.00   0.033152   0.378618   0.4021328    0.033160    0.378630    0.4021340
+51505.00   0.032879   0.378417   0.4005584    0.032830    0.378490    0.4005630
+51506.00   0.032516   0.378016   0.3991948    0.032520    0.378050    0.3991930
+51507.00   0.032158   0.377578   0.3980249    0.032220    0.377580    0.3980150
+51508.00   0.031840   0.377205   0.3969956    0.031880    0.377270    0.3969800
+51509.00   0.031731   0.376900   0.3960227    0.031730    0.376960    0.3960080
+51510.00   0.032013   0.376716   0.3950159    0.031980    0.376690    0.3950130
+51511.00   0.032624   0.376739   0.3939021    0.032620    0.376770    0.3939120
+51512.00   0.033305   0.377007   0.3926455    0.033360    0.377030    0.3926480
+51513.00   0.033866   0.377511   0.3912784    0.033910    0.377480    0.3912670
+51514.00   0.034243   0.378168   0.3898971    0.034260    0.378180    0.3898730
+51515.00   0.034482   0.378767   0.3885654    0.034480    0.378850    0.3885010
+51516.00   0.034712   0.379145   0.3873141    0.034700    0.379180    0.3872730
+51517.00   0.034806   0.379274   0.3861571    0.034850    0.379330    0.3861610
+51518.00   0.034636   0.379187   0.3851032    0.034710    0.379240    0.3851200
+51519.00   0.034509   0.378991   0.3841467    0.034490    0.378970    0.3841480
+51520.00   0.034839   0.378900   0.3832822    0.034790    0.378900    0.3832790
+51521.00   0.035705   0.379108   0.3824822    0.035700    0.379150    0.3824670
+51522.00   0.036626   0.379542   0.3816877    0.036690    0.379600    0.3816790
+51523.00   0.037157   0.379986   0.3808536    0.037230    0.380060    0.3808610
+51524.00   0.037313   0.380340   0.3799565    0.037340    0.380370    0.3799460
+51525.00   0.037388   0.380650   0.3789967    0.037370    0.380650    0.3790130
+51526.00   0.037709   0.381013   0.3779240    0.037680    0.380980    0.3779550
+51527.00   0.038233   0.381469   0.3767143    0.038290    0.381490    0.3767130
+51528.00   0.038411   0.381831   0.3753863    0.038470    0.381890    0.3753720
+51529.00   0.038307   0.381983   0.3739443    0.038300    0.381920    0.3739430
+51530.00   0.038151   0.382020   0.3724233    0.038120    0.381980    0.3724420
+51531.00   0.037966   0.382019   0.3708867    0.037990    0.382080    0.3709520
+51532.00   0.037613   0.381863   0.3694136    0.037700    0.381940    0.3694790
+51533.00   0.037076   0.381420   0.3680734    0.037210    0.381360    0.3680520
+51534.00   0.036608   0.380682   0.3668939    0.036610    0.380730    0.3668750
+51535.00   0.036570   0.379666   0.3658297    0.036540    0.379730    0.3658610
+51536.00   0.037112   0.378618   0.3648087    0.037160    0.378560    0.3647670
+51537.00   0.038004   0.377845   0.3637629    0.038060    0.377680    0.3636580
+51538.00   0.039000   0.377510   0.3626369    0.039020    0.377410    0.3625410
+51539.00   0.040049   0.377548   0.3614129    0.040050    0.377580    0.3613690
+51540.00   0.041137   0.377788   0.3601152    0.041120    0.377740    0.3601150
+51541.00   0.042052   0.378106   0.3588103    0.041930    0.378160    0.3588120
+51542.00   0.042547   0.378200   0.3575773    0.042450    0.378270    0.3575540
+51543.00   0.042890   0.377991   0.3564633    0.042860    0.377980    0.3564530
+51544.00   0.043215   0.377697   0.3554732    0.043190    0.377700    0.3554990
+51545.00   0.043455   0.377468   0.3545966    0.043480    0.377510    0.3546330
+51546.00   0.043512   0.377238   0.3538464    0.043590    0.377270    0.3538640
+51547.00   0.043363   0.376945   0.3532360    0.043410    0.376940    0.3532310
+51548.00   0.043134   0.376623   0.3527537    0.043160    0.376600    0.3527960
+51549.00   0.043149   0.376297   0.3523431    0.043140    0.376270    0.3524010
+51550.00   0.043422   0.376104   0.3519484    0.043440    0.376070    0.3519450
+51551.00   0.043659   0.375969   0.3515404    0.043730    0.376020    0.3515040
+51552.00   0.043675   0.375686   0.3510966    0.043690    0.375760    0.3510900
+51553.00   0.043604   0.375254   0.3505534    0.043590    0.375250    0.3505590
+51554.00   0.043745   0.374881   0.3498790    0.043740    0.374760    0.3498330
+51555.00   0.044251   0.374731   0.3490532    0.044220    0.374640    0.3489670
+51556.00   0.045178   0.374726   0.3480742    0.045170    0.374690    0.3479980
+51557.00   0.046378   0.374757   0.3469483    0.046490    0.374730    0.3469090
+51558.00   0.047518   0.374783   0.3457480    0.047640    0.374810    0.3457280
+51559.00   0.048487   0.374789   0.3445662    0.048510    0.374820    0.3445610
+51560.00   0.049484   0.374848   0.3434739    0.049450    0.374840    0.3435070
+51561.00   0.050671   0.375099   0.3424895    0.050690    0.375130    0.3425460
+51562.00   0.051944   0.375468   0.3415968    0.051980    0.375540    0.3416250
+51563.00   0.053115   0.375682   0.3407635    0.053160    0.375720    0.3407420
+51564.00   0.054190   0.375869   0.3399193    0.054210    0.375830    0.3398860
+51565.00   0.055094   0.376229   0.3390041    0.055110    0.376210    0.3389880
+51566.00   0.055780   0.376695   0.3379742    0.055820    0.376690    0.3379660
+51567.00   0.056228   0.377100   0.3368184    0.056210    0.377100    0.3368040
+51568.00   0.056440   0.377321   0.3355604    0.056420    0.377350    0.3355310
+51569.00   0.056569   0.377302   0.3342584    0.056570    0.377290    0.3342110
+51570.00   0.056930   0.377024   0.3329659    0.056980    0.376990    0.3329220
+51571.00   0.057279   0.376691   0.3317549    0.057300    0.376720    0.3317330
+51572.00   0.057260   0.376406   0.3306897    0.057180    0.376380    0.3306810
+51573.00   0.057053   0.376159   0.3297764    0.056980    0.376130    0.3297760
+51574.00   0.056920   0.375899   0.3289928    0.056930    0.375950    0.3290060
+51575.00   0.056955   0.375515   0.3283083    0.056890    0.375520    0.3283220
+51576.00   0.057214   0.374985   0.3276983    0.057200    0.375010    0.3276990
+51577.00   0.057620   0.374341   0.3271605    0.057660    0.374400    0.3271500
+51578.00   0.058222   0.373714   0.3266641    0.058190    0.373750    0.3266670
+51579.00   0.058993   0.373135   0.3261312    0.059040    0.373130    0.3261480
+51580.00   0.059938   0.372624   0.3254829    0.059970    0.372620    0.3254630
+51581.00   0.061017   0.372235   0.3246859    0.061040    0.372220    0.3245860
+51582.00   0.062195   0.371971   0.3237181    0.062240    0.371940    0.3236430
+51583.00   0.063502   0.371823   0.3225902    0.063470    0.371980    0.3225890
+51584.00   0.064771   0.371580   0.3213657    0.064720    0.371710    0.3213750
+51585.00   0.065761   0.371153   0.3201477    0.065790    0.371160    0.3201260
+51586.00   0.066202   0.370649   0.3190448    0.066240    0.370670    0.3190220
+51587.00   0.066157   0.370171   0.3181117    0.066190    0.370140    0.3181430
+51588.00   0.066000   0.369722   0.3173178    0.065970    0.369710    0.3173170
+51589.00   0.066136   0.369254   0.3166298    0.066020    0.369250    0.3166280
+51590.00   0.066699   0.368822   0.3159886    0.066650    0.368810    0.3159960
+51591.00   0.067329   0.368509   0.3153378    0.067380    0.368490    0.3153000
+51592.00   0.067939   0.368369   0.3146275    0.067960    0.368340    0.3145900
+51593.00   0.068516   0.368291   0.3137951    0.068520    0.368310    0.3138080
+51594.00   0.069027   0.368146   0.3127835    0.069000    0.368190    0.3128270
+51595.00   0.069214   0.367844   0.3116045    0.069170    0.367900    0.3116160
+51596.00   0.069035   0.367356   0.3103486    0.068980    0.367350    0.3103220
+51597.00   0.068730   0.366746   0.3091149    0.068720    0.366770    0.3091040
+51598.00   0.068361   0.366024   0.3079714    0.068400    0.366050    0.3079750
+51599.00   0.067890   0.365197   0.3069563    0.067930    0.365220    0.3069360
+51600.00   0.067390   0.364288   0.3060864    0.067440    0.364350    0.3060680
+51601.00   0.066951   0.363382   0.3053466    0.066970    0.363430    0.3053490
+51602.00   0.066617   0.362566   0.3047064    0.066620    0.362640    0.3046720
+51603.00   0.066422   0.361905   0.3041427    0.066450    0.361910    0.3041150
+51604.00   0.066401   0.361388   0.3036098    0.066400    0.361470    0.3036080
+51605.00   0.066648   0.360890   0.3030436    0.066580    0.360960    0.3030390
+51606.00   0.067130   0.360477   0.3024079    0.067140    0.360330    0.3024040
+51607.00   0.067620   0.360236   0.3016646    0.067760    0.360170    0.3016670
+51608.00   0.067948   0.360099   0.3007691    0.067950    0.360180    0.3007680
+51609.00   0.068216   0.359961   0.2996940    0.068160    0.360020    0.2996990
+51610.00   0.068525   0.359801   0.2984518    0.068600    0.359770    0.2984570
+51611.00   0.068828   0.359586   0.2970918    0.068820    0.359720    0.2970870
+51612.00   0.069387   0.359217   0.2956919    0.069280    0.359300    0.2956890
+51613.00   0.070402   0.358866   0.2943263    0.070270    0.358810    0.2943390
+51614.00   0.071773   0.358686   0.2930544    0.071690    0.358680    0.2930720
+51615.00   0.073083   0.358599   0.2919128    0.073060    0.358650    0.2919190
+51616.00   0.073990   0.358364   0.2909174    0.073940    0.358480    0.2909220
+51617.00   0.074480   0.357854   0.2900542    0.074370    0.357960    0.2900640
+51618.00   0.074671   0.357111   0.2892761    0.074620    0.357200    0.2892770
+51619.00   0.074758   0.356241   0.2884848    0.074740    0.356290    0.2884850
+51620.00   0.074596   0.355401   0.2876281    0.074570    0.355470    0.2876310
+51621.00   0.074245   0.354616   0.2867043    0.074260    0.354760    0.2867060
+51622.00   0.073913   0.353859   0.2857300    0.073980    0.353950    0.2857400
+51623.00   0.073747   0.353172   0.2847254    0.073730    0.353110    0.2847510
+51624.00   0.073826   0.352606   0.2836875    0.073800    0.352620    0.2837170
+51625.00   0.073986   0.352100   0.2826302    0.074010    0.352130    0.2826370
+51626.00   0.073990   0.351575   0.2816146    0.073980    0.351510    0.2815880
+51627.00   0.073949   0.351028   0.2807146    0.073900    0.351030    0.2807060
+51628.00   0.073762   0.350394   0.2799580    0.073770    0.350450    0.2799650
+51629.00   0.073420   0.349596   0.2793488    0.073380    0.349600    0.2793210
+51630.00   0.073080   0.348740   0.2788852    0.073100    0.348650    0.2789080
+51631.00   0.072772   0.347935   0.2784878    0.072860    0.347950    0.2785310
+51632.00   0.072757   0.347135   0.2780890    0.072710    0.347190    0.2780890
+51633.00   0.073380   0.346415   0.2776678    0.073380    0.346410    0.2776610
+51634.00   0.074374   0.345977   0.2771617    0.074460    0.346060    0.2771740
+51635.00   0.075198   0.345904   0.2764928    0.075220    0.345980    0.2765170
+51636.00   0.075808   0.346131   0.2756134    0.075680    0.346120    0.2756560
+51637.00   0.076481   0.346510   0.2745277    0.076340    0.346510    0.2745730
+51638.00   0.076985   0.346828   0.2732774    0.077030    0.346880    0.2732930
+51639.00   0.077059   0.346914   0.2719400    0.077060    0.346950    0.2719320
+51640.00   0.077093   0.346707   0.2706251    0.077060    0.346750    0.2706330
+51641.00   0.077296   0.346262   0.2694147    0.077310    0.346310    0.2694350
+51642.00   0.077573   0.345685   0.2683416    0.077600    0.345730    0.2683450
+51643.00   0.077806   0.345015   0.2674068    0.077800    0.345120    0.2674040
+51644.00   0.078238   0.344262   0.2665760    0.078180    0.344290    0.2665780
+51645.00   0.079142   0.343638   0.2657834    0.079090    0.343600    0.2657810
+51646.00   0.080330   0.343365   0.2649542    0.080370    0.343360    0.2649460
+51647.00   0.081333   0.343380   0.2640252    0.081460    0.343390    0.2640320
+51648.00   0.081906   0.343576   0.2629513    0.081990    0.343570    0.2629550
+51649.00   0.082086   0.343849   0.2617145    0.082090    0.343870    0.2617140
+51650.00   0.082136   0.344069   0.2603384    0.082110    0.344100    0.2603470
+51651.00   0.082226   0.344150   0.2588795    0.082280    0.344150    0.2588640
+51652.00   0.082276   0.344044   0.2574198    0.082430    0.344110    0.2574370
+51653.00   0.082219   0.343715   0.2560309    0.082280    0.343740    0.2560440
+51654.00   0.082294   0.343161   0.2547592    0.082350    0.343120    0.2547520
+51655.00   0.082424   0.342461   0.2536047    0.082580    0.342510    0.2536740
+51656.00   0.082541   0.341696   0.2525701    0.082560    0.341730    0.2526650
+51657.00   0.082838   0.340923   0.2516714    0.082870    0.340860    0.2517210
+51658.00   0.083396   0.340229   0.2509021    0.083440    0.340130    0.2509180
+51659.00   0.084129   0.339694   0.2502312    0.084160    0.339640    0.2502580
+51660.00   0.084942   0.339305   0.2496080    0.084990    0.339270    0.2496080
+51661.00   0.085776   0.338979   0.2489548    0.085840    0.338870    0.2489720
+51662.00   0.086596   0.338677   0.2481768    0.086640    0.338570    0.2481970
+51663.00   0.087438   0.338335   0.2472104    0.087450    0.338270    0.2471880
+51664.00   0.088310   0.337912   0.2460469    0.088320    0.337850    0.2460670
+51665.00   0.089074   0.337501   0.2447165    0.089060    0.337400    0.2447320
+51666.00   0.089646   0.337119   0.2432550    0.089650    0.337110    0.2432530
+51667.00   0.089943   0.336653   0.2417404    0.089980    0.336700    0.2417460
+51668.00   0.089876   0.335967   0.2402498    0.089850    0.336090    0.2402460
+51669.00   0.089512   0.335028   0.2388797    0.089490    0.334990    0.2388380
+51670.00   0.089040   0.333967   0.2376950    0.089050    0.333810    0.2376060
+51671.00   0.088496   0.332907   0.2366841    0.088530    0.332910    0.2365990
+51672.00   0.087926   0.331857   0.2357910    0.087960    0.331860    0.2357550
+51673.00   0.087469   0.330886   0.2349382    0.087530    0.330830    0.2349480
+51674.00   0.087221   0.330148   0.2340443    0.087260    0.330140    0.2340480
+51675.00   0.087388   0.329677   0.2330322    0.087380    0.329760    0.2330110
+51676.00   0.088025   0.329575   0.2319108    0.088070    0.329580    0.2318930
+51677.00   0.088812   0.329617   0.2307012    0.088870    0.329580    0.2307180
+51678.00   0.089475   0.329668   0.2294361    0.089470    0.329690    0.2294520
+51679.00   0.090028   0.329588   0.2281687    0.090050    0.329610    0.2281330
+51680.00   0.090488   0.329187   0.2269698    0.090540    0.329170    0.2269170
+51681.00   0.090846   0.328477   0.2258826    0.090870    0.328450    0.2258710
+51682.00   0.091393   0.327755   0.2248928    0.091390    0.327710    0.2248910
+51683.00   0.092217   0.327154   0.2240194    0.092280    0.327100    0.2239890
+51684.00   0.093187   0.326636   0.2232798    0.093220    0.326590    0.2232440
+51685.00   0.093994   0.326187   0.2226631    0.094040    0.326130    0.2226540
+51686.00   0.094457   0.325675   0.2221314    0.094520    0.325650    0.2221350
+51687.00   0.094541   0.324805   0.2216301    0.094640    0.324730    0.2216290
+51688.00   0.094765   0.323507   0.2211125    0.094850    0.323410    0.2210940
+51689.00   0.095304   0.322391   0.2205648    0.095340    0.322360    0.2205220
+51690.00   0.096112   0.321571   0.2199668    0.096140    0.321540    0.2199430
+51691.00   0.097312   0.320835   0.2192941    0.097340    0.320780    0.2193000
+51692.00   0.098918   0.320093   0.2185232    0.098910    0.320120    0.2185070
+51693.00   0.100678   0.319359   0.2176552    0.100690    0.319400    0.2176300
+51694.00   0.102331   0.318691   0.2167245    0.102300    0.318690    0.2167470
+51695.00   0.103800   0.318055   0.2157888    0.103790    0.318060    0.2157980
+51696.00   0.105087   0.317341   0.2149209    0.105050    0.317360    0.2149210
+51697.00   0.106400   0.316492   0.2141761    0.106310    0.316510    0.2141970
+51698.00   0.107684   0.315507   0.2135716    0.107760    0.315500    0.2135470
+51699.00   0.108770   0.314486   0.2130846    0.108820    0.314500    0.2130840
+51700.00   0.109818   0.313510   0.2126473    0.109820    0.313540    0.2126560
+51701.00   0.110928   0.312639   0.2121691    0.110970    0.312620    0.2121930
+51702.00   0.111937   0.311860   0.2115752    0.111870    0.311870    0.2115840
+51703.00   0.112461   0.311070   0.2108262    0.112450    0.311120    0.2108200
+51704.00   0.112535   0.310143   0.2099522    0.112510    0.310180    0.2099400
+51705.00   0.112490   0.308914   0.2090299    0.112470    0.308970    0.2090050
+51706.00   0.112729   0.307475   0.2081422    0.112680    0.307450    0.2081520
+51707.00   0.113194   0.306099   0.2073506    0.113130    0.306060    0.2073440
+51708.00   0.113500   0.304864   0.2067048    0.113470    0.304900    0.2067100
+51709.00   0.113577   0.303639   0.2062475    0.113620    0.303680    0.2062480
+51710.00   0.113645   0.302472   0.2060009    0.113600    0.302480    0.2059850
+51711.00   0.113161   0.301332   0.2059520    0.113100    0.301300    0.2059900
+51712.00   0.112189   0.300267   0.2060449    0.112160    0.300250    0.2060860
+51713.00   0.110999   0.299135   0.2062115    0.111010    0.299210    0.2061740
+51714.00   0.109831   0.297643   0.2064062    0.109870    0.297710    0.2063480
+51715.00   0.108880   0.295971   0.2065859    0.108870    0.296020    0.2065770
+51716.00   0.108124   0.294316   0.2066983    0.108140    0.294340    0.2067080
+51717.00   0.107741   0.292556   0.2067001    0.107770    0.292570    0.2066590
+51718.00   0.107635   0.290771   0.2065948    0.107700    0.290810    0.2065550
+51719.00   0.107822   0.289158   0.2063949    0.107890    0.289170    0.2064170
+51720.00   0.108185   0.287884   0.2061000    0.108190    0.287890    0.2061060
+51721.00   0.108554   0.286664   0.2057138    0.108550    0.286670    0.2056990
+51722.00   0.108995   0.285324   0.2052783    0.108990    0.285330    0.2053220
+51723.00   0.109531   0.283989   0.2048415    0.109520    0.283980    0.2048650
+51724.00   0.110044   0.282821   0.2044716    0.110020    0.282840    0.2044680
+51725.00   0.110221   0.281535   0.2042304    0.110210    0.281560    0.2042410
+51726.00   0.110169   0.280039   0.2041293    0.110140    0.280000    0.2040530
+51727.00   0.109967   0.278617   0.2041166    0.109940    0.278620    0.2039720
+51728.00   0.109546   0.277387   0.2040917    0.109520    0.277410    0.2039550
+51729.00   0.108963   0.276322   0.2039568    0.108970    0.276340    0.2038820
+51730.00   0.108195   0.275448   0.2036533    0.108210    0.275460    0.2036680
+51731.00   0.107386   0.274682   0.2031755    0.107390    0.274680    0.2031930
+51732.00   0.107012   0.273910   0.2025619    0.106990    0.273890    0.2025540
+51733.00   0.106830   0.273235   0.2018964    0.106810    0.273230    0.2018930
+51734.00   0.106345   0.272676   0.2012574    0.106320    0.272700    0.2012680
+51735.00   0.105472   0.272081   0.2006965    0.105450    0.272070    0.2007080
+51736.00   0.104471   0.271366   0.2002445    0.104450    0.271300    0.2002440
+51737.00   0.103658   0.270383   0.1999051    0.103620    0.270380    0.1999000
+51738.00   0.102881   0.269207   0.1996952    0.102910    0.269240    0.1996870
+51739.00   0.102127   0.268078   0.1996037    0.102150    0.268100    0.1996080
+51740.00   0.101213   0.266972   0.1996296    0.101200    0.267020    0.1996010
+51741.00   0.100163   0.265843   0.1997601    0.100110    0.265850    0.1997040
+51742.00   0.099235   0.264837   0.1999437    0.099230    0.264830    0.1999110
+51743.00   0.098285   0.263948   0.2001382    0.098270    0.263900    0.2001430
+51744.00   0.097161   0.262947   0.2003128    0.097120    0.262910    0.2003170
+51745.00   0.096019   0.261634   0.2004413    0.096020    0.261600    0.2004180
+51746.00   0.094818   0.260257   0.2005212    0.094860    0.260160    0.2005040
+51747.00   0.093439   0.259074   0.2005555    0.093450    0.259050    0.2005770
+51748.00   0.092112   0.258017   0.2005297    0.092080    0.258090    0.2005480
+51749.00   0.091249   0.256975   0.2004521    0.091250    0.257010    0.2004300
+51750.00   0.090774   0.256140   0.2003567    0.090800    0.256130    0.2003190
+51751.00   0.090467   0.255547   0.2002801    0.090420    0.255640    0.2002770
+51752.00   0.090291   0.255049   0.2002659    0.090210    0.255130    0.2002820
+51753.00   0.089877   0.254516   0.2003391    0.089820    0.254560    0.2003350
+51754.00   0.089117   0.253907   0.2004866    0.089090    0.253970    0.2004870
+51755.00   0.088353   0.253164   0.2006483    0.088320    0.253210    0.2006530
+51756.00   0.087624   0.252369   0.2007368    0.087600    0.252400    0.2007420
+51757.00   0.086922   0.251692   0.2006819    0.086920    0.251760    0.2006950
+51758.00   0.086416   0.251127   0.2004629    0.086350    0.251190    0.2004710
+51759.00   0.085879   0.250675   0.2001140    0.085880    0.250750    0.2000900
+51760.00   0.085277   0.250228   0.1997081    0.085310    0.250310    0.1997160
+51761.00   0.084583   0.249655   0.1993192    0.084600    0.249680    0.1993260
+51762.00   0.083909   0.249042   0.1990109    0.083890    0.249060    0.1990050
+51763.00   0.083208   0.248525   0.1988276    0.083200    0.248580    0.1988220
+51764.00   0.082305   0.248115   0.1987890    0.082270    0.248180    0.1987940
+51765.00   0.081267   0.247773   0.1988874    0.081170    0.247790    0.1988930
+51766.00   0.079976   0.247487   0.1990782    0.079980    0.247550    0.1990860
+51767.00   0.078346   0.247089   0.1993185    0.078360    0.247210    0.1993320
+51768.00   0.076607   0.246398   0.1995682    0.076600    0.246450    0.1995820
+51769.00   0.075050   0.245638   0.1997864    0.075080    0.245660    0.1997900
+51770.00   0.073685   0.245040   0.1999357    0.073680    0.245120    0.1999360
+51771.00   0.072650   0.244535   0.1999812    0.072610    0.244590    0.1999960
+51772.00   0.071939   0.244191   0.1998923    0.071990    0.244180    0.1999100
+51773.00   0.071315   0.244107   0.1996358    0.071320    0.244120    0.1996310
+51774.00   0.070374   0.244145   0.1992137    0.070350    0.244190    0.1992030
+51775.00   0.069083   0.244139   0.1986598    0.069080    0.244150    0.1986760
+51776.00   0.067529   0.244006   0.1980179    0.067510    0.244020    0.1980460
+51777.00   0.065843   0.243813   0.1973375    0.065840    0.243870    0.1973380
+51778.00   0.063909   0.243619   0.1966686    0.063950    0.243660    0.1966380
+51779.00   0.062076   0.243350   0.1960529    0.062070    0.243450    0.1960370
+51780.00   0.060596   0.243028   0.1955095    0.060540    0.243110    0.1955580
+51781.00   0.059118   0.242771   0.1950637    0.059070    0.242770    0.1951390
+51782.00   0.057453   0.242702   0.1946679    0.057380    0.242750    0.1946870
+51783.00   0.055584   0.242638   0.1942311    0.055540    0.242700    0.1942120
+51784.00   0.053762   0.242461   0.1936784    0.053770    0.242480    0.1936680
+51785.00   0.052242   0.242168   0.1929644    0.052240    0.242200    0.1929680
+51786.00   0.051091   0.241829   0.1920968    0.051050    0.241840    0.1920900
+51787.00   0.050026   0.241689   0.1911429    0.050050    0.241700    0.1911300
+51788.00   0.049004   0.241787   0.1901885    0.049000    0.241830    0.1902050
+51789.00   0.047868   0.241878   0.1893152    0.047870    0.241900    0.1893390
+51790.00   0.046627   0.241895   0.1885815    0.046610    0.241930    0.1885410
+51791.00   0.045163   0.241966   0.1880169    0.045120    0.242050    0.1879720
+51792.00   0.043394   0.242041   0.1876245    0.043370    0.242120    0.1875600
+51793.00   0.041253   0.242044   0.1873924    0.041300    0.242150    0.1873680
+51794.00   0.038987   0.241874   0.1872994    0.038960    0.241980    0.1873270
+51795.00   0.036646   0.241519   0.1873119    0.036650    0.241590    0.1873080
+51796.00   0.034228   0.241045   0.1873757    0.034220    0.241120    0.1874040
+51797.00   0.031863   0.240490   0.1874162    0.031860    0.240520    0.1874630
+51798.00   0.029800   0.240042   0.1873909    0.029850    0.240070    0.1874080
+51799.00   0.027756   0.239711   0.1872765    0.027770    0.239730    0.1872670
+51800.00   0.025131   0.239553   0.1870392    0.025180    0.239550    0.1870400
+51801.00   0.022100   0.239613   0.1866261    0.022170    0.239660    0.1866420
+51802.00   0.019235   0.239702   0.1860446    0.019230    0.239770    0.1860490
+51803.00   0.016892   0.239768   0.1853451    0.016920    0.239800    0.1853450
+51804.00   0.015146   0.239926   0.1846132    0.015190    0.239960    0.1845970
+51805.00   0.013673   0.240390   0.1839280    0.013700    0.240450    0.1839060
+51806.00   0.012234   0.241177   0.1833480    0.012240    0.241170    0.1833410
+51807.00   0.010639   0.242107   0.1828855    0.010580    0.242100    0.1828890
+51808.00   0.008784   0.242995   0.1824901    0.008730    0.243030    0.1824890
+51809.00   0.006877   0.243489   0.1821322    0.006910    0.243550    0.1821650
+51810.00   0.005374   0.243521   0.1817526    0.005410    0.243510    0.1817180
+51811.00   0.004180   0.243406   0.1812846    0.004180    0.243400    0.1812110
+51812.00   0.002961   0.243379   0.1806641    0.002940    0.243410    0.1806320
+51813.00   0.001685   0.243522   0.1798529    0.001720    0.243530    0.1798730
+51814.00   0.000637   0.243908   0.1788651    0.000670    0.243960    0.1788750
+51815.00  -0.000618   0.244567   0.1777557   -0.000600    0.244620    0.1777340
+51816.00  -0.002327   0.245461   0.1766239   -0.002350    0.245470    0.1766000
+51817.00  -0.004224   0.246291   0.1755666   -0.004230    0.246310    0.1756310
+51818.00  -0.005825   0.246948   0.1746510   -0.005830    0.246970    0.1747260
+51819.00  -0.007317   0.247621   0.1739216   -0.007300    0.247640    0.1739250
+51820.00  -0.009224   0.248297   0.1733949   -0.009170    0.248320    0.1733650
+51821.00  -0.011143   0.248950   0.1730499   -0.011180    0.248870    0.1730460
+51822.00  -0.013069   0.249438   0.1728087   -0.013050    0.249390    0.1728150
+51823.00  -0.014665   0.249912   0.1725813   -0.014670    0.249930    0.1725740
+51824.00  -0.016139   0.250291   0.1722903   -0.016150    0.250310    0.1723010
+51825.00  -0.017636   0.250548   0.1718754   -0.017650    0.250520    0.1718640
+51826.00  -0.019065   0.250992   0.1712972   -0.019100    0.250930    0.1712420
+51827.00  -0.020494   0.251829   0.1705340   -0.020520    0.251850    0.1704830
+51828.00  -0.021762   0.253062   0.1695809   -0.021790    0.253120    0.1695750
+51829.00  -0.022955   0.254629   0.1684621   -0.022930    0.254700    0.1684640
+51830.00  -0.024334   0.256374   0.1672188   -0.024370    0.256430    0.1672620
+51831.00  -0.026199   0.257929   0.1659060   -0.026230    0.257910    0.1658840
+51832.00  -0.028276   0.259197   0.1645897   -0.028270    0.259150    0.1644540
+51833.00  -0.030364   0.260226   0.1633355   -0.030370    0.260220    0.1631760
+51834.00  -0.032237   0.261068   0.1621903   -0.032240    0.261070    0.1621250
+51835.00  -0.034006   0.261825   0.1611638   -0.033940    0.261820    0.1611760
+51836.00  -0.035697   0.262610   0.1602119   -0.035650    0.262640    0.1601930
+51837.00  -0.037440   0.263490   0.1592914   -0.037410    0.263500    0.1593100
+51838.00  -0.039119   0.264415   0.1583268   -0.039160    0.264420    0.1584290
+51839.00  -0.040594   0.265452   0.1572384   -0.040620    0.265410    0.1573430
+51840.00  -0.041999   0.266789   0.1559864   -0.042050    0.266770    0.1560110
+51841.00  -0.043325   0.268351   0.1545737   -0.043340    0.268370    0.1545450
+51842.00  -0.044307   0.269937   0.1530409   -0.044330    0.269950    0.1530330
+51843.00  -0.045559   0.271645   0.1514582   -0.045520    0.271650    0.1514710
+51844.00  -0.046947   0.273406   0.1499040   -0.046990    0.273440    0.1499100
+51845.00  -0.048312   0.274938   0.1484473   -0.048330    0.274930    0.1484300
+51846.00  -0.049258   0.276441   0.1471312   -0.049290    0.276360    0.1471610
+51847.00  -0.050029   0.278336   0.1459646   -0.050100    0.278260    0.1460480
+51848.00  -0.051245   0.280534   0.1449376   -0.051190    0.280530    0.1449780
+51849.00  -0.052365   0.282579   0.1440273   -0.052430    0.282600    0.1440070
+51850.00  -0.053768   0.284278   0.1432139   -0.053760    0.284260    0.1432280
+51851.00  -0.055168   0.285920   0.1424100   -0.055120    0.285960    0.1424240
+51852.00  -0.056466   0.287605   0.1415673   -0.056420    0.287660    0.1415700
+51853.00  -0.057701   0.289318   0.1406684   -0.057720    0.289250    0.1406590
+51854.00  -0.059197   0.291235   0.1396914   -0.059220    0.291140    0.1396830
+51855.00  -0.060735   0.293436   0.1386201   -0.060760    0.293410    0.1386150
+51856.00  -0.062207   0.295705   0.1374452   -0.062220    0.295680    0.1374380
+51857.00  -0.063703   0.297752   0.1361688   -0.063730    0.297700    0.1361610
+51858.00  -0.065070   0.299403   0.1348301   -0.065100    0.299370    0.1348130
+51859.00  -0.065971   0.300644   0.1335009   -0.066020    0.300590    0.1334650
+51860.00  -0.066267   0.301799   0.1322709   -0.066280    0.301690    0.1322310
+51861.00  -0.066492   0.303333   0.1312182   -0.066450    0.303290    0.1312040
+51862.00  -0.067493   0.305215   0.1303689   -0.067400    0.305250    0.1303790
+51863.00  -0.069107   0.306966   0.1296774   -0.069160    0.307050    0.1296860
+51864.00  -0.071252   0.308424   0.1290646   -0.071220    0.308460    0.1290580
+51865.00  -0.073154   0.309840   0.1284567   -0.073120    0.309810    0.1284520
+51866.00  -0.074727   0.311485   0.1277697   -0.074710    0.311500    0.1277780
+51867.00  -0.075653   0.313425   0.1269444   -0.075660    0.313440    0.1269430
+51868.00  -0.075902   0.315632   0.1259690   -0.075870    0.315600    0.1259530
+51869.00  -0.075832   0.317969   0.1248704   -0.075810    0.317980    0.1248720
+51870.00  -0.075847   0.320194   0.1236988   -0.075840    0.320240    0.1237180
+51871.00  -0.076126   0.322251   0.1225155   -0.076120    0.322250    0.1224790
+51872.00  -0.076569   0.324131   0.1214167   -0.076580    0.324110    0.1213050
+51873.00  -0.077090   0.325916   0.1204631   -0.077060    0.325860    0.1203310
+51874.00  -0.077734   0.327749   0.1196786   -0.077740    0.327710    0.1195830
+51875.00  -0.078464   0.329650   0.1190476   -0.078510    0.329660    0.1190050
+51876.00  -0.079156   0.331507   0.1185260   -0.079060    0.331530    0.1185190
+51877.00  -0.079387   0.333385   0.1180405   -0.079330    0.333330    0.1180470
+51878.00  -0.079572   0.335195   0.1175256   -0.079610    0.335150    0.1175220
+51879.00  -0.080077   0.336962   0.1169465   -0.080070    0.336980    0.1169220
+51880.00  -0.080633   0.338725   0.1162730   -0.080610    0.338690    0.1162800
+51881.00  -0.080874   0.340453   0.1154659   -0.080890    0.340360    0.1155090
+51882.00  -0.080959   0.342134   0.1145151   -0.081000    0.342100    0.1145360
+51883.00  -0.081277   0.343792   0.1134289   -0.081290    0.343710    0.1134120
+51884.00  -0.081778   0.345435   0.1122263   -0.081830    0.345320    0.1122210
+51885.00  -0.082321   0.347178   0.1109317   -0.082340    0.347130    0.1109530
+51886.00  -0.082764   0.349073   0.1095995   -0.082810    0.349020    0.1095820
+51887.00  -0.083349   0.350969   0.1083132   -0.083330    0.350940    0.1083340
+51888.00  -0.083714   0.352612   0.1071406   -0.083680    0.352570    0.1071950
+51889.00  -0.083829   0.353999   0.1061295   -0.083840    0.354000    0.1061520
+51890.00  -0.083649   0.355350   0.1052914   -0.083610    0.355370    0.1052740
+51891.00  -0.082749   0.357031   0.1045855   -0.082670    0.356990    0.1045790
+51892.00  -0.081454   0.359066   0.1039318   -0.081380    0.359130    0.1039370
+51893.00  -0.080187   0.361234   0.1032411   -0.080170    0.361250    0.1032250
+51894.00  -0.079088   0.363461   0.1024684   -0.079090    0.363400    0.1024390
+51895.00  -0.078140   0.365712   0.1016101   -0.078160    0.365680    0.1014840
+51896.00  -0.077838   0.367832   0.1006813   -0.077860    0.367890    0.1004880
+51897.00  -0.078183   0.369725   0.0997235   -0.078140    0.369680    0.0996230
+51898.00  -0.078693   0.371616   0.0987847   -0.078760    0.371540    0.0987910
+51899.00  -0.078656   0.373822   0.0979190   -0.078790    0.373900    0.0978530
+51900.00  -0.078397   0.376311   0.0971744   -0.078300    0.376360    0.0971970
+51901.00  -0.078015   0.378911   0.0965589   -0.077960    0.378920    0.0966350
+51902.00  -0.077568   0.381524   0.0960664   -0.077570    0.381510    0.0961140
+51903.00  -0.077052   0.384097   0.0956855   -0.077090    0.384100    0.0956820
+51904.00  -0.076627   0.386538   0.0953894   -0.076580    0.386520    0.0953610
+51905.00  -0.076313   0.388743   0.0951401   -0.076400    0.388660    0.0951180
+51906.00  -0.076058   0.390710   0.0948989   -0.076070    0.390660    0.0948940
+51907.00  -0.075340   0.392637   0.0946252   -0.075390    0.392710    0.0946300
+51908.00  -0.074867   0.394535   0.0942721   -0.074860    0.394520    0.0942860
+51909.00  -0.074232   0.396311   0.0938013   -0.074200    0.396280    0.0938240
+51910.00  -0.073475   0.398000   0.0932012   -0.073450    0.397930    0.0932210
+51911.00  -0.072616   0.399776   0.0924807   -0.072570    0.399630    0.0924880
+51912.00  -0.071574   0.401696   0.0916585   -0.071560    0.401750    0.0916580
+51913.00  -0.071043   0.403552   0.0907643   -0.071020    0.403630    0.0907650
+51914.00  -0.070727   0.405169   0.0898571   -0.070740    0.405100    0.0898580
+51915.00  -0.070375   0.406607   0.0890085   -0.070400    0.406550    0.0890200
+51916.00  -0.070175   0.407955   0.0882806   -0.070180    0.407910    0.0883060
+51917.00  -0.070202   0.409286   0.0876993   -0.070120    0.409250    0.0877260
+51918.00  -0.070110   0.410656   0.0872305   -0.070060    0.410700    0.0872420
+51919.00  -0.069867   0.412121   0.0868107   -0.069810    0.412100    0.0868070
+51920.00  -0.069253   0.413913   0.0863871   -0.069330    0.413860    0.0863720
+51921.00  -0.068491   0.415946   0.0858811   -0.068490    0.415910    0.0858980
+51922.00  -0.067476   0.418035   0.0852171   -0.067480    0.418010    0.0852290
+51923.00  -0.066439   0.420036   0.0843862   -0.066440    0.420030    0.0843870
+51924.00  -0.065330   0.421837   0.0834544   -0.065340    0.421820    0.0834490
+51925.00  -0.063996   0.423433   0.0825093   -0.064050    0.423420    0.0825050
+51926.00  -0.062707   0.424929   0.0816517   -0.062730    0.424880    0.0816480
+51927.00  -0.061468   0.426305   0.0809688   -0.061460    0.426190    0.0809690
+51928.00  -0.060311   0.427747   0.0804814   -0.060340    0.427710    0.0804850
+51929.00  -0.059229   0.429112   0.0801680   -0.059270    0.429150    0.0801190
+51930.00  -0.058108   0.430179   0.0800094   -0.058160    0.430160    0.0799550
+51931.00  -0.056802   0.431143   0.0799699   -0.056820    0.431050    0.0799820
+51932.00  -0.055433   0.432399   0.0800133   -0.055400    0.432420    0.0800690
+51933.00  -0.054105   0.434088   0.0800967   -0.054080    0.434170    0.0801060
+51934.00  -0.052244   0.435907   0.0801067   -0.052310    0.435870    0.0800960
+51935.00  -0.050480   0.437795   0.0799898   -0.050530    0.437790    0.0799840
+51936.00  -0.049071   0.439653   0.0797378   -0.049080    0.439600    0.0797570
+51937.00  -0.047516   0.441457   0.0793490   -0.047500    0.441380    0.0794010
+51938.00  -0.045591   0.443202   0.0788464   -0.045590    0.443170    0.0789010
+51939.00  -0.043693   0.444798   0.0782754   -0.043680    0.444700    0.0782950
+51940.00  -0.042033   0.446206   0.0776959   -0.042080    0.446190    0.0776930
+51941.00  -0.040636   0.447190   0.0771815   -0.040620    0.447160    0.0771880
+51942.00  -0.039126   0.447931   0.0767659   -0.039100    0.447830    0.0767630
+51943.00  -0.037703   0.448626   0.0764797   -0.037700    0.448620    0.0764750
+51944.00  -0.036061   0.449368   0.0763355   -0.036070    0.449360    0.0763320
+51945.00  -0.034020   0.450341   0.0762962   -0.034080    0.450270    0.0763010
+51946.00  -0.032180   0.451589   0.0762796   -0.032090    0.451620    0.0762880
+51947.00  -0.030365   0.453000   0.0761678   -0.030410    0.453010    0.0761720
+51948.00  -0.028369   0.454523   0.0758262   -0.028520    0.454470    0.0758360
+51949.00  -0.026513   0.456074   0.0752100   -0.026520    0.456090    0.0751990
+51950.00  -0.024700   0.457538   0.0743443   -0.024720    0.457560    0.0743150
+51951.00  -0.023433   0.458809   0.0733026   -0.023390    0.458870    0.0732830
+51952.00  -0.022363   0.459792   0.0721740   -0.022340    0.459760    0.0721790
+51953.00  -0.020906   0.460725   0.0710272   -0.020910    0.460680    0.0710400
+51954.00  -0.019204   0.461937   0.0699186   -0.019230    0.461970    0.0699210
+51955.00  -0.017628   0.463232   0.0689386   -0.017580    0.463260    0.0689260
+51956.00  -0.015910   0.464556   0.0681089   -0.015900    0.464530    0.0681100
+51957.00  -0.014070   0.465865   0.0674058   -0.014070    0.465820    0.0673560
+51958.00  -0.012282   0.467076   0.0667945   -0.012260    0.467050    0.0667340
+51959.00  -0.010501   0.468197   0.0662114   -0.010490    0.468140    0.0661950
+51960.00  -0.008318   0.469373   0.0656072   -0.008260    0.469310    0.0656190
+51961.00  -0.005520   0.470698   0.0649585   -0.005540    0.470720    0.0649590
+51962.00  -0.002296   0.472165   0.0642607   -0.002380    0.472160    0.0642450
+51963.00   0.000948   0.473902   0.0634634    0.001010    0.473850    0.0634730
+51964.00   0.004069   0.475723   0.0625081    0.004070    0.475750    0.0625000
+51965.00   0.006803   0.477346   0.0613757    0.006820    0.477360    0.0613410
+51966.00   0.009390   0.478757   0.0600802    0.009410    0.478700    0.0600530
+51967.00   0.011992   0.479986   0.0586678    0.011990    0.479910    0.0586680
+51968.00   0.014200   0.481045   0.0572087    0.014300    0.481000    0.0572140
+51969.00   0.016153   0.481998   0.0557767    0.016090    0.481990    0.0557520
+51970.00   0.017276   0.482700   0.0544494    0.017280    0.482630    0.0544200
+51971.00   0.018496   0.483232   0.0532711    0.018470    0.483100    0.0532710
+51972.00   0.020341   0.483840   0.0522268    0.020380    0.483750    0.0522360
+51973.00   0.022588   0.484558   0.0512724    0.022620    0.484550    0.0512640
+51974.00   0.024503   0.485112   0.0503386    0.024500    0.485160    0.0503260
+51975.00   0.026284   0.485380   0.0493333    0.026380    0.485330    0.0493410
+51976.00   0.028795   0.485434   0.0481232    0.028660    0.485440    0.0481510
+51977.00   0.031018   0.485399   0.0466901    0.031050    0.485380    0.0466690
+51978.00   0.033359   0.485373   0.0451120    0.033350    0.485300    0.0451310
+51979.00   0.035564   0.485413   0.0434664    0.035550    0.485400    0.0435500
+51980.00   0.038022   0.485524   0.0418693    0.037980    0.485480    0.0419400
+51981.00   0.040915   0.485734   0.0404265    0.040870    0.485670    0.0404350
+51982.00   0.043874   0.486103   0.0392186    0.043920    0.486220    0.0392130
+51983.00   0.046406   0.486377   0.0382843    0.046450    0.486420    0.0383080
+51984.00   0.048681   0.486655   0.0375617    0.048650    0.486610    0.0375840
+51985.00   0.050978   0.487043   0.0369778    0.050980    0.487060    0.0369820
+51986.00   0.053555   0.487408   0.0364941    0.053540    0.487400    0.0364890
+51987.00   0.056329   0.487821   0.0360676    0.056310    0.487770    0.0360630
+51988.00   0.059135   0.488394   0.0356430    0.059060    0.488390    0.0356370
+51989.00   0.061688   0.488864   0.0351568    0.061640    0.488880    0.0351550
+51990.00   0.064613   0.489050   0.0345635    0.064420    0.489130    0.0345770
+51991.00   0.067318   0.489461   0.0338698    0.067340    0.489510    0.0338570
+51992.00   0.070277   0.490025   0.0330839    0.070240    0.490030    0.0330900
+51993.00   0.073332   0.490497   0.0321623    0.073320    0.490500    0.0321940
+51994.00   0.076391   0.490842   0.0311235    0.076380    0.490860    0.0311380
+51995.00   0.079270   0.491052   0.0300255    0.079230    0.491060    0.0300160
+51996.00   0.081797   0.491117   0.0289424    0.081730    0.491080    0.0289390
+51997.00   0.084181   0.491078   0.0279322    0.084070    0.491050    0.0279420
+51998.00   0.086363   0.490780   0.0270475    0.086360    0.490830    0.0270520
+51999.00   0.088580   0.490263   0.0262681    0.088550    0.490340    0.0262730
+52000.00   0.090657   0.489693   0.0255680    0.090660    0.489730    0.0255590
+52001.00   0.092713   0.489218   0.0248977    0.092710    0.489170    0.0248780
+52002.00   0.094742   0.488819   0.0241865    0.094770    0.488820    0.0241770
+52003.00   0.096970   0.488212   0.0233415    0.097060    0.488230    0.0233470
+52004.00   0.099146   0.487316   0.0222831    0.099230    0.487290    0.0222820
+52005.00   0.101639   0.486689   0.0210200    0.101620    0.486670    0.0210190
+52006.00   0.104372   0.486451   0.0196018    0.104330    0.486450    0.0196170
+52007.00   0.106684   0.486230   0.0181065    0.106650    0.486230    0.0181290
+52008.00   0.108446   0.485677   0.0167021    0.108450    0.485660    0.0167110
+52009.00   0.110408   0.484787   0.0155036    0.110420    0.484780    0.0155010
+52010.00   0.112966   0.483921   0.0145483    0.113020    0.483870    0.0145520
+52011.00   0.115877   0.483273   0.0138282    0.115940    0.483200    0.0138300
+52012.00   0.118552   0.482764   0.0133060    0.118580    0.482740    0.0132910
+52013.00   0.120993   0.482036   0.0129373    0.121010    0.481980    0.0129400
+52014.00   0.123648   0.481138   0.0126343    0.123680    0.481070    0.0126510
+52015.00   0.126521   0.480220   0.0122983    0.126590    0.480190    0.0123230
+52016.00   0.129576   0.479260   0.0118375    0.129610    0.479320    0.0118590
+52017.00   0.132758   0.478579   0.0112160    0.132870    0.478610    0.0112190
+52018.00   0.136404   0.478052   0.0104241    0.136310    0.478060    0.0104330
+52019.00   0.139400   0.477581   0.0095180    0.139470    0.477590    0.0095310
+52020.00   0.142322   0.476918   0.0085454    0.142300    0.476880    0.0085640
+52021.00   0.144945   0.476117   0.0075411    0.144920    0.476040    0.0075520
+52022.00   0.147340   0.475404   0.0065665    0.147380    0.475340    0.0065630
+52023.00   0.149333   0.474680   0.0056600    0.149330    0.474630    0.0056590
+52024.00   0.151014   0.473687   0.0048331    0.151040    0.473690    0.0048440
+52025.00   0.153471   0.472377   0.0040681    0.153320    0.472380    0.0040670
+52026.00   0.155789   0.471026   0.0033913    0.155790    0.470940    0.0033730
+52027.00   0.157806   0.469570   0.0028082    0.157800    0.469490    0.0028290
+52028.00   0.159603   0.467985   0.0022319    0.159600    0.467950    0.0022510
+52029.00   0.161444   0.466243   0.0015624    0.161460    0.466200    0.0015540
+52030.00   0.163356   0.464304   0.0007169    0.163170    0.464260    0.0006960
+52031.00   0.164950   0.462522  -0.0003369    0.164950    0.462500   -0.0003420
+52032.00   0.167277   0.461283  -0.0016044    0.167170    0.461160   -0.0015850
+52033.00   0.169451   0.460079  -0.0030462    0.169470    0.460060   -0.0030340
+52034.00   0.171731   0.458504  -0.0046043    0.171710    0.458490   -0.0046170
+52035.00   0.173634   0.456540  -0.0061441    0.173660    0.456560   -0.0061660
+52036.00   0.175770   0.454417  -0.0075479    0.175750    0.454430   -0.0075520
+52037.00   0.178361   0.452275  -0.0087514    0.178400    0.452300   -0.0087500
+52038.00   0.181152   0.450055  -0.0097479    0.181190    0.450110   -0.0097520
+52039.00   0.183899   0.447804  -0.0105429    0.183850    0.447870   -0.0105440
+52040.00   0.186558   0.445713  -0.0111874    0.186520    0.445780   -0.0111840
+52041.00   0.189162   0.444034  -0.0117606    0.189130    0.444060   -0.0117580
+52042.00   0.191399   0.442523  -0.0123357    0.191380    0.442540   -0.0123210
+52043.00   0.193310   0.440749  -0.0129715    0.193240    0.440740   -0.0129560
+52044.00   0.194797   0.438595  -0.0137153    0.194970    0.438620   -0.0137180
+52045.00   0.196885   0.436321  -0.0145805    0.196880    0.436330   -0.0145870
+52046.00   0.198612   0.433526  -0.0155485    0.198680    0.433650   -0.0155280
+52047.00   0.200793   0.430724  -0.0166117    0.200760    0.430710   -0.0166130
+52048.00   0.203161   0.427891  -0.0177380    0.203180    0.427890   -0.0177410
+52049.00   0.205174   0.425164  -0.0188736    0.205110    0.425180   -0.0188620
+52050.00   0.206955   0.422557  -0.0199526    0.206910    0.422590   -0.0199380
+52051.00   0.208746   0.420125  -0.0209135    0.208850    0.420250   -0.0209080
+52052.00   0.210426   0.417989  -0.0216892    0.210410    0.418040   -0.0216940
+52053.00   0.212039   0.415612  -0.0222479    0.211890    0.415630   -0.0222490
+52054.00   0.213256   0.413088  -0.0226142    0.213350    0.413120   -0.0226100
+52055.00   0.214679   0.410347  -0.0228347    0.214620    0.410370   -0.0228330
+52056.00   0.215942   0.407372  -0.0230140    0.215880    0.407350   -0.0230120
+52057.00   0.217326   0.404456  -0.0232574    0.217320    0.404480   -0.0232580
+52058.00   0.219019   0.401751  -0.0236287    0.219030    0.401800   -0.0236350
+52059.00   0.220923   0.399153  -0.0241428    0.220930    0.399220   -0.0241480
+52060.00   0.222844   0.396603  -0.0247593    0.222860    0.396660   -0.0247580
+52061.00   0.224462   0.394024  -0.0254085    0.224420    0.394050   -0.0253810
+52062.00   0.225856   0.391301  -0.0260285    0.225790    0.391340   -0.0260010
+52063.00   0.227208   0.388448  -0.0265294    0.227200    0.388480   -0.0265140
+52064.00   0.228755   0.385506  -0.0268396    0.228700    0.385520   -0.0268270
+52065.00   0.230305   0.382461  -0.0269233    0.230520    0.382550   -0.0268950
+52066.00   0.232394   0.379951  -0.0267701    0.232380    0.380060   -0.0267660
+52067.00   0.234494   0.377269  -0.0264129    0.234400    0.377400   -0.0264180
+52068.00   0.236427   0.374759  -0.0259398    0.236450    0.374730   -0.0259510
+52069.00   0.238000   0.372435  -0.0254444    0.237980    0.372450   -0.0254460
+52070.00   0.239149   0.370013  -0.0250118    0.239120    0.370100   -0.0250090
+52071.00   0.240122   0.367384  -0.0247004    0.240140    0.367450   -0.0246970
+52072.00   0.241075   0.364662  -0.0245354    0.241010    0.364630   -0.0245310
+52073.00   0.241862   0.361668  -0.0245107    0.241850    0.361720   -0.0245030
+52074.00   0.243205   0.358359  -0.0245966    0.243170    0.358550   -0.0245960
+52075.00   0.244809   0.355242  -0.0247260    0.244820    0.355300   -0.0247330
+52076.00   0.246266   0.352304  -0.0248955    0.246230    0.352330   -0.0248900
+52077.00   0.247366   0.349444  -0.0250968    0.247330    0.349550   -0.0250730
+52078.00   0.248189   0.346490  -0.0252880    0.248170    0.346560   -0.0252650
+52079.00   0.248439   0.343539  -0.0254205    0.248480    0.343620   -0.0254190
+52080.00   0.248322   0.340726  -0.0254431    0.248350    0.340790   -0.0254530
+52081.00   0.248209   0.337798  -0.0253430    0.248270    0.337830   -0.0253190
+52082.00   0.248422   0.334848  -0.0251893    0.248430    0.334900   -0.0252160
+52083.00   0.248662   0.331828  -0.0250212    0.248720    0.331890   -0.0249870
+52084.00   0.248931   0.328775  -0.0249081    0.248970    0.328800   -0.0248170
+52085.00   0.249514   0.325692  -0.0249313    0.249480    0.325730   -0.0248560
+52086.00   0.250305   0.322581  -0.0251681    0.250410    0.322600   -0.0251460
+52087.00   0.251263   0.319736  -0.0256329    0.251270    0.319770   -0.0256330
+52088.00   0.252011   0.317012  -0.0262455    0.251940    0.316990   -0.0262050
+52089.00   0.252360   0.313934  -0.0268484    0.252380    0.313920   -0.0268050
+52090.00   0.252992   0.310715  -0.0273415    0.252990    0.310720   -0.0273470
+52091.00   0.253731   0.307575  -0.0276738    0.253780    0.307610   -0.0277010
+52092.00   0.253964   0.304394  -0.0278178    0.253940    0.304430   -0.0278370
+52093.00   0.253753   0.301145  -0.0277834    0.253750    0.301190   -0.0277890
+52094.00   0.254215   0.298188  -0.0276027    0.254090    0.298230   -0.0276160
+52095.00   0.254232   0.295468  -0.0273324    0.254290    0.295550   -0.0273580
+52096.00   0.253911   0.292761  -0.0270278    0.253890    0.292770   -0.0270370
+52097.00   0.253524   0.290040  -0.0267282    0.253530    0.290050   -0.0267140
+52098.00   0.252708   0.287256  -0.0264986    0.252700    0.287310   -0.0264760
+52099.00   0.251813   0.284150  -0.0263669    0.251720    0.284140   -0.0263590
+52100.00   0.251686   0.280793  -0.0263067    0.251690    0.280760   -0.0263210
+52101.00   0.251944   0.277620  -0.0263047    0.251980    0.277670   -0.0263080
+52102.00   0.251507   0.274653  -0.0263530    0.251470    0.274620   -0.0263440
+52103.00   0.250977   0.271726  -0.0264534    0.250880    0.271650   -0.0264360
+52104.00   0.250420   0.268800  -0.0265857    0.250360    0.268790   -0.0265840
+52105.00   0.249489   0.265680  -0.0266888    0.249470    0.265690   -0.0266870
+52106.00   0.248487   0.262337  -0.0267108    0.248500    0.262310   -0.0266970
+52107.00   0.248045   0.259044  -0.0266166    0.248020    0.259030   -0.0265990
+52108.00   0.247899   0.255898  -0.0263879    0.247930    0.255930   -0.0263840
+52109.00   0.247886   0.252693  -0.0260054    0.247880    0.252780   -0.0260360
+52110.00   0.247644   0.249621  -0.0255194    0.247620    0.249630   -0.0255320
+52111.00   0.247124   0.246549  -0.0250172    0.247110    0.246510   -0.0250070
+52112.00   0.246308   0.243497  -0.0246244    0.246310    0.243480   -0.0245960
+52113.00   0.245303   0.240604  -0.0244283    0.245290    0.240600   -0.0243980
+52114.00   0.244181   0.237844  -0.0244490    0.244230    0.237850   -0.0244370
+52115.00   0.243624   0.235369  -0.0246366    0.243550    0.235400   -0.0246340
+52116.00   0.242989   0.232644  -0.0248666    0.242970    0.232700   -0.0248580
+52117.00   0.242022   0.229827  -0.0250474    0.242000    0.229780   -0.0250340
+52118.00   0.240668   0.226879  -0.0250822    0.240630    0.226830   -0.0250700
+52119.00   0.239018   0.223779  -0.0249085    0.239010    0.223820   -0.0249070
+52120.00   0.237308   0.220720  -0.0245315    0.237300    0.220680   -0.0245330
+52121.00   0.235872   0.217961  -0.0239862    0.235850    0.217860   -0.0239860
+52122.00   0.234708   0.215461  -0.0233285    0.234740    0.215420   -0.0233300
+52123.00   0.233413   0.213496  -0.0226143    0.233290    0.213440   -0.0226280
+52124.00   0.230934   0.211330  -0.0219419    0.230970    0.211380   -0.0219510
+52125.00   0.228252   0.208668  -0.0213853    0.228190    0.208600   -0.0213740
+52126.00   0.225848   0.205783  -0.0209760    0.225820    0.205680   -0.0209610
+52127.00   0.223854   0.203137  -0.0207247    0.223840    0.203130   -0.0207250
+52128.00   0.221846   0.200695  -0.0206206    0.221850    0.200830   -0.0206260
+52129.00   0.219641   0.198602  -0.0206566    0.219630    0.198630   -0.0206580
+52130.00   0.217156   0.196301  -0.0208393    0.217110    0.196290   -0.0208430
+52131.00   0.214270   0.194048  -0.0211293    0.214300    0.194010   -0.0211170
+52132.00   0.211292   0.191558  -0.0214996    0.211260    0.191530   -0.0215140
+52133.00   0.208443   0.188809  -0.0218695    0.208400    0.188780   -0.0219090
+52134.00   0.205986   0.186155  -0.0221796    0.206010    0.186150   -0.0222080
+52135.00   0.203661   0.183652  -0.0223872    0.203770    0.183640   -0.0223950
+52136.00   0.201030   0.181204  -0.0224832    0.201050    0.181170   -0.0224850
+52137.00   0.198726   0.178978  -0.0225064    0.198400    0.178940   -0.0225100
+52138.00   0.195935   0.176831  -0.0225537    0.196010    0.176810   -0.0225520
+52139.00   0.193417   0.174597  -0.0227462    0.193410    0.174570   -0.0227320
+52140.00   0.190985   0.172340  -0.0231581    0.190970    0.172310   -0.0231440
+52141.00   0.188799   0.170234  -0.0238223    0.188770    0.170270   -0.0238140
+52142.00   0.186205   0.168172  -0.0246828    0.186480    0.168150   -0.0246870
+52143.00   0.183852   0.165911  -0.0256448    0.183820    0.165890   -0.0256510
+52144.00   0.181210   0.163907  -0.0265834    0.181090    0.163910   -0.0265740
+52145.00   0.178677   0.162045  -0.0273417    0.178690    0.162090   -0.0273220
+52146.00   0.176475   0.160131  -0.0278971    0.176480    0.160180   -0.0279020
+52147.00   0.174361   0.158092  -0.0282024    0.174360    0.158120   -0.0282170
+52148.00   0.172421   0.156000  -0.0282814    0.172380    0.156010   -0.0282800
+52149.00   0.170433   0.153890  -0.0281922    0.170520    0.154080   -0.0281840
+52150.00   0.168322   0.152401  -0.0279987    0.168310    0.152450   -0.0280010
+52151.00   0.166083   0.150982  -0.0277811    0.165940    0.151010   -0.0277760
+52152.00   0.163282   0.149983  -0.0276358    0.163320    0.150020   -0.0276410
+52153.00   0.160290   0.149077  -0.0275677    0.160240    0.149120   -0.0275220
+52154.00   0.157071   0.147756  -0.0276349    0.157030    0.147790   -0.0275400
+52155.00   0.153925   0.146230  -0.0278569    0.153930    0.146230   -0.0278290
+52156.00   0.150804   0.144646  -0.0282213    0.150800    0.144700   -0.0282350
+52157.00   0.147452   0.142777  -0.0286907    0.147470    0.142820   -0.0286930
+52158.00   0.144611   0.140770  -0.0292332    0.144570    0.140680   -0.0292230
+52159.00   0.141812   0.139100  -0.0297340    0.141940    0.139040   -0.0296950
+52160.00   0.139469   0.137762  -0.0301363    0.139480    0.137780   -0.0301450
+52161.00   0.137173   0.136680  -0.0304103    0.137220    0.136710   -0.0304570
+52162.00   0.134793   0.135598  -0.0305519    0.134820    0.135600   -0.0305810
+52163.00   0.131937   0.134534  -0.0305697    0.132050    0.134440   -0.0305690
+52164.00   0.128799   0.133191  -0.0305005    0.128800    0.133200   -0.0304980
+52165.00   0.125569   0.132151  -0.0304014    0.125660    0.132030   -0.0304240
+52166.00   0.122462   0.130991  -0.0303880    0.122490    0.130970   -0.0304260
+52167.00   0.118823   0.129921  -0.0305704    0.118860    0.129900   -0.0305830
+52168.00   0.115134   0.129008  -0.0310339    0.115160    0.129020   -0.0310310
+52169.00   0.111702   0.128161  -0.0317828    0.111710    0.128160   -0.0317820
+52170.00   0.108312   0.127305  -0.0327193    0.108320    0.127300   -0.0327230
+52171.00   0.104560   0.126560  -0.0336945    0.104560    0.126580   -0.0336970
+52172.00   0.101202   0.125935  -0.0345894    0.101130    0.125960   -0.0345850
+52173.00   0.097832   0.125409  -0.0353300    0.097880    0.125390   -0.0353410
+52174.00   0.094515   0.124864  -0.0358762    0.094530    0.124880   -0.0358710
+52175.00   0.091429   0.124165  -0.0362401    0.091460    0.124210   -0.0362300
+52176.00   0.088536   0.123182  -0.0364427    0.088580    0.123200   -0.0364470
+52177.00   0.085411   0.122051  -0.0365260    0.085470    0.122040   -0.0365380
+52178.00   0.082376   0.120940  -0.0365832    0.082290    0.120950   -0.0365820
+52179.00   0.078852   0.119978  -0.0366762    0.078960    0.119830   -0.0366910
+52180.00   0.075375   0.119070  -0.0368739    0.075340    0.118980   -0.0368680
+52181.00   0.071799   0.118503  -0.0372183    0.071810    0.118500   -0.0372090
+52182.00   0.068626   0.118099  -0.0376932    0.068640    0.118080   -0.0376640
+52183.00   0.065428   0.117780  -0.0382971    0.065420    0.117780   -0.0382510
+52184.00   0.061701   0.117636  -0.0390380    0.061680    0.117630   -0.0390180
+52185.00   0.057927   0.117397  -0.0398955    0.057890    0.117430   -0.0399000
+52186.00   0.054765   0.117299  -0.0407777    0.054770    0.117230   -0.0407620
+52187.00   0.052007   0.117381  -0.0416711    0.052010    0.117390   -0.0416520
+52188.00   0.048970   0.117600  -0.0425184    0.049030    0.117650   -0.0425430
+52189.00   0.045981   0.117726  -0.0432326    0.045990    0.117750   -0.0432790
+52190.00   0.043041   0.117743  -0.0438207    0.043020    0.117720   -0.0438420
+52191.00   0.039838   0.117721  -0.0443233    0.039870    0.117720   -0.0443200
+52192.00   0.036029   0.117573  -0.0448060    0.036070    0.117610   -0.0447990
+52193.00   0.031916   0.117281  -0.0453585    0.031890    0.117280   -0.0453630
+52194.00   0.027453   0.117238  -0.0460502    0.027530    0.117290   -0.0460900
+52195.00   0.023095   0.117487  -0.0469466    0.023160    0.117520   -0.0469310
+52196.00   0.019346   0.117756  -0.0481163    0.019360    0.117720   -0.0480670
+52197.00   0.015946   0.118233  -0.0495361    0.015960    0.118200   -0.0495160
+52198.00   0.012831   0.118897  -0.0511037    0.012800    0.118850   -0.0511100
+52199.00   0.009771   0.119369  -0.0526870    0.009780    0.119370   -0.0526840
+52200.00   0.006624   0.119915  -0.0541499    0.006640    0.119920   -0.0541550
+52201.00   0.003117   0.120419  -0.0553755    0.003130    0.120460   -0.0553510
+52202.00  -0.000633   0.121004  -0.0563214   -0.000630    0.121030   -0.0563460
+52203.00  -0.004548   0.121787  -0.0570287   -0.004550    0.121810   -0.0571110
+52204.00  -0.008592   0.122717  -0.0575979   -0.008610    0.122720   -0.0576820
+52205.00  -0.012764   0.123691  -0.0581052   -0.012680    0.123650   -0.0581450
+52206.00  -0.016471   0.124646  -0.0586147   -0.016540    0.124620   -0.0586110
+52207.00  -0.020057   0.125720  -0.0591595   -0.020140    0.125640   -0.0591720
+52208.00  -0.023592   0.126873  -0.0598171   -0.023560    0.126840   -0.0598170
+52209.00  -0.027016   0.128062  -0.0606384   -0.027010    0.128060   -0.0606310
+52210.00  -0.030363   0.129127  -0.0616118   -0.030400    0.129080   -0.0616080
+52211.00  -0.033489   0.130207  -0.0626844   -0.033430    0.130180   -0.0626800
+52212.00  -0.036312   0.131409  -0.0638054   -0.036440    0.131310   -0.0637910
+52213.00  -0.039723   0.132495  -0.0649117   -0.039670    0.132430   -0.0649050
+52214.00  -0.042998   0.133687  -0.0659738   -0.043040    0.133660   -0.0659630
+52215.00  -0.046638   0.134773  -0.0669227   -0.046640    0.134770   -0.0669370
+52216.00  -0.050417   0.135809  -0.0676901   -0.050340    0.135800   -0.0676910
+52217.00  -0.053930   0.136813  -0.0683200   -0.053880    0.136820   -0.0683050
+52218.00  -0.056874   0.137877  -0.0688598   -0.056890    0.137870   -0.0688520
+52219.00  -0.059567   0.139242  -0.0693496   -0.059350    0.139300   -0.0693540
+52220.00  -0.061636   0.140902  -0.0698298   -0.061630    0.140970   -0.0698350
+52221.00  -0.063465   0.142802  -0.0704038   -0.063600    0.142770   -0.0703970
+52222.00  -0.065483   0.144986  -0.0712220   -0.065480    0.145000   -0.0712120
+52223.00  -0.067793   0.147468  -0.0723560   -0.067800    0.147470   -0.0723670
+52224.00  -0.069810   0.149958  -0.0737262   -0.069820    0.149910   -0.0737430
+52225.00  -0.071468   0.152519  -0.0752515   -0.071500    0.152520   -0.0752510
+52226.00  -0.073350   0.155089  -0.0768355   -0.073310    0.155070   -0.0768330
+52227.00  -0.075332   0.157245  -0.0783711   -0.075350    0.157260   -0.0783700
+52228.00  -0.077750   0.159099  -0.0796914   -0.077860    0.159060   -0.0796860
+52229.00  -0.080457   0.160740  -0.0807265   -0.080460    0.160730   -0.0807140
+52230.00  -0.082630   0.162556  -0.0815412   -0.082630    0.162600   -0.0815400
+52231.00  -0.084537   0.164432  -0.0821981   -0.084550    0.164490   -0.0822050
+52232.00  -0.086599   0.166367  -0.0827684   -0.086610    0.166400   -0.0827700
+52233.00  -0.088961   0.168447  -0.0833152   -0.088970    0.168490   -0.0833120
+52234.00  -0.091666   0.170548  -0.0838756   -0.091650    0.170580   -0.0838760
+52235.00  -0.094439   0.172912  -0.0844874   -0.094470    0.172930   -0.0844770
+52236.00  -0.097417   0.175528  -0.0852001   -0.097430    0.175550   -0.0851540
+52237.00  -0.100610   0.178082  -0.0860027   -0.100640    0.178070   -0.0859420
+52238.00  -0.103621   0.180536  -0.0868873   -0.103680    0.180540   -0.0868450
+52239.00  -0.106097   0.183056  -0.0878437   -0.106080    0.183040   -0.0878340
+52240.00  -0.108161   0.185529  -0.0888340   -0.108120    0.185520   -0.0888440
+52241.00  -0.110184   0.187897  -0.0898046   -0.110230    0.187940   -0.0898050
+52242.00  -0.112626   0.190242  -0.0906958   -0.112570    0.190300   -0.0906720
+52243.00  -0.115319   0.192771  -0.0914748   -0.115330    0.192810   -0.0914570
+52244.00  -0.118472   0.195444  -0.0921087   -0.118480    0.195510   -0.0921210
+52245.00  -0.121686   0.197956  -0.0925504   -0.121680    0.198010   -0.0925740
+52246.00  -0.124538   0.200158  -0.0928592   -0.124610    0.200230   -0.0928610
+52247.00  -0.127367   0.202499  -0.0931227   -0.127150    0.202470   -0.0931050
+52248.00  -0.129424   0.205211  -0.0934180   -0.129470    0.205230   -0.0934140
+52249.00  -0.131746   0.208532  -0.0938524   -0.131820    0.208530   -0.0938520
+52250.00  -0.134559   0.211661  -0.0944922   -0.134560    0.211710   -0.0944960
+52251.00  -0.137796   0.214373  -0.0953455   -0.137790    0.214400   -0.0953380
+52252.00  -0.140993   0.216887  -0.0963972   -0.140970    0.216840   -0.0963730
+52253.00  -0.143845   0.219614  -0.0975759   -0.143840    0.219610   -0.0975250
+52254.00  -0.146663   0.222530  -0.0987595   -0.146720    0.222550   -0.0986800
+52255.00  -0.149955   0.225288  -0.0998250   -0.149810    0.225270   -0.0997520
+52256.00  -0.152619   0.227892  -0.1007198   -0.152690    0.227810   -0.1006790
+52257.00  -0.155312   0.230578  -0.1014175   -0.155270    0.230440   -0.1014000
+52258.00  -0.157721   0.233138  -0.1018974   -0.157710    0.233150   -0.1018990
+52259.00  -0.159985   0.235805  -0.1022319   -0.159980    0.235780   -0.1022370
+52260.00  -0.161987   0.238560  -0.1025397   -0.161990    0.238560   -0.1025390
+52261.00  -0.163912   0.241535  -0.1029096   -0.163910    0.241570   -0.1029110
+52262.00  -0.165861   0.244801  -0.1033804   -0.165910    0.244800   -0.1033780
+52263.00  -0.167850   0.248402  -0.1039633   -0.167810    0.248290   -0.1039500
+52264.00  -0.169230   0.251819  -0.1047528   -0.169310    0.251770   -0.1047380
+52265.00  -0.170330   0.255332  -0.1057456   -0.170420    0.255330   -0.1057660
+52266.00  -0.171339   0.258892  -0.1068441   -0.171340    0.258950   -0.1068720
+52267.00  -0.172082   0.262522  -0.1080049   -0.172130    0.262500   -0.1080060
+52268.00  -0.172610   0.266399  -0.1091917   -0.172630    0.266380   -0.1091660
+52269.00  -0.173029   0.270501  -0.1103666   -0.172980    0.270520   -0.1103540
+52270.00  -0.173564   0.274604  -0.1114947   -0.173600    0.274890   -0.1114930
+52271.00  -0.174569   0.278560  -0.1125366   -0.174540    0.278470   -0.1125380
+52272.00  -0.175316   0.282350  -0.1134552   -0.175190    0.282350   -0.1134560
+52273.00  -0.175984   0.286373  -0.1142677   -0.175920    0.286410   -0.1142540
+52274.00  -0.176653   0.290272  -0.1150324   -0.176950    0.290250   -0.1149770
+52275.00  -0.177016   0.293921  -0.1158152   -0.177000    0.293620   -0.1158230
+52276.00  -0.177495   0.297612  -0.1166808   -0.177470    0.297200   -0.1166920
+52277.00  -0.178336   0.300810  -0.1177100   -0.178340    0.300940   -0.1177150
+52278.00  -0.179101   0.303585  -0.1189101   -0.179090    0.303680   -0.1189030
+52279.00  -0.179532   0.306461  -0.1202228   -0.179550    0.306440   -0.1202550
+52280.00  -0.180020   0.309511  -0.1215588   -0.179970    0.309560   -0.1216410
+52281.00  -0.180675   0.312707  -0.1228480   -0.180630    0.312730   -0.1229070
+52282.00  -0.181819   0.315313  -0.1239725   -0.181690    0.315560   -0.1239680
+52283.00  -0.182661   0.318169  -0.1248182   -0.182690    0.318280   -0.1248070
+52284.00  -0.183034   0.321152  -0.1254127   -0.183060    0.321190   -0.1254210
+52285.00  -0.183124   0.324202  -0.1258003   -0.183040    0.324220   -0.1257940
+52286.00  -0.182606   0.327370  -0.1260104   -0.182770    0.327380   -0.1260400
+52287.00  -0.182290   0.330466  -0.1260705   -0.182260    0.330390   -0.1261170
+52288.00  -0.181735   0.333568  -0.1260606   -0.181660    0.333550   -0.1260720
+52289.00  -0.181305   0.336868  -0.1260645   -0.181260    0.336820   -0.1260550
+52290.00  -0.180666   0.339816  -0.1261542   -0.180660    0.339780   -0.1261430
+52291.00  -0.179799   0.342945  -0.1263182   -0.179880    0.342870   -0.1263090
+52292.00  -0.179235   0.346080  -0.1265902   -0.179240    0.346120   -0.1265880
+52293.00  -0.178756   0.349176  -0.1270231   -0.178740    0.349230   -0.1270260
+52294.00  -0.178420   0.352206  -0.1275466   -0.178410    0.352280   -0.1275570
+52295.00  -0.178175   0.355254  -0.1281161   -0.178170    0.355300   -0.1281260
+52296.00  -0.177717   0.358445  -0.1287129   -0.177750    0.358510   -0.1287150
+52297.00  -0.177120   0.361868  -0.1293129   -0.177120    0.361930   -0.1293080
+52298.00  -0.176579   0.365288  -0.1298667   -0.176510    0.365290   -0.1298590
+52299.00  -0.175468   0.368698  -0.1303104   -0.175730    0.368690   -0.1303060
+52300.00  -0.174998   0.372115  -0.1306448   -0.175000    0.372170   -0.1306280
+52301.00  -0.174316   0.375411  -0.1309512   -0.174370    0.375470   -0.1309230
+52302.00  -0.173598   0.378730  -0.1313384   -0.173550    0.378710   -0.1313120
+52303.00  -0.172607   0.382056  -0.1319137   -0.172820    0.382030   -0.1319070
+52304.00  -0.172389   0.385066  -0.1327493   -0.172060    0.385140   -0.1327840
+52305.00  -0.170719   0.388310  -0.1338791   -0.170750    0.388250   -0.1339120
+52306.00  -0.168875   0.391546  -0.1351973   -0.168940    0.391530   -0.1352080
+52307.00  -0.167477   0.394739  -0.1366556   -0.167400    0.394750   -0.1365590
+52308.00  -0.166425   0.397897  -0.1381554   -0.166390    0.397840   -0.1380160
+52309.00  -0.165568   0.400893  -0.1395328   -0.165460    0.400780   -0.1394740
+52310.00  -0.164218   0.403149  -0.1407285   -0.164370    0.403580   -0.1407190
+52311.00  -0.163276   0.406446  -0.1417207   -0.163320    0.406310   -0.1417360
+52312.00  -0.162284   0.409364  -0.1424592   -0.162230    0.409320   -0.1424540
+52313.00  -0.161022   0.412491  -0.1430272   -0.161130    0.412430   -0.1430230
+52314.00  -0.159842   0.415394  -0.1434514   -0.159750    0.415430   -0.1434440
+52315.00  -0.157943   0.418781  -0.1438265   -0.157950    0.418820   -0.1438150
+52316.00  -0.156070   0.422412  -0.1442607   -0.156050    0.422450   -0.1442500
+52317.00  -0.154331   0.425633  -0.1448045   -0.154400    0.425740   -0.1447930
+52318.00  -0.153348   0.428706  -0.1454279   -0.153250    0.428820   -0.1454300
+52319.00  -0.152471   0.431713  -0.1461593   -0.152390    0.431700   -0.1461620
+52320.00  -0.151431   0.434433  -0.1469919   -0.151390    0.434420   -0.1469910
+52321.00  -0.150089   0.437078  -0.1479225   -0.150100    0.437100   -0.1479090
+52322.00  -0.148485   0.439730  -0.1489536   -0.148490    0.439730   -0.1489370
+52323.00  -0.146818   0.442441  -0.1500204   -0.146780    0.442390   -0.1500170
+52324.00  -0.145248   0.445256  -0.1510529   -0.145260    0.445150   -0.1510560
+52325.00  -0.143908   0.447804  -0.1520116   -0.143730    0.447920   -0.1520210
+52326.00  -0.142017   0.450844  -0.1528875   -0.142030    0.450810   -0.1528980
+52327.00  -0.140236   0.453903  -0.1536583   -0.140240    0.453930   -0.1536530
+52328.00  -0.138470   0.457214  -0.1543530   -0.138440    0.457220   -0.1543870
+52329.00  -0.136564   0.460595  -0.1550116   -0.136510    0.460600   -0.1551080
+52330.00  -0.134094   0.463762  -0.1557661   -0.134050    0.463790   -0.1558460
+52331.00  -0.131075   0.466899  -0.1567270   -0.131080    0.466900   -0.1567280
+52332.00  -0.128411   0.470196  -0.1579544   -0.128360    0.470160   -0.1579220
+52333.00  -0.125976   0.473454  -0.1594256   -0.125970    0.473440   -0.1594490
+52334.00  -0.123473   0.476420  -0.1610247   -0.123360    0.476540   -0.1610230
+52335.00  -0.120773   0.479750  -0.1626879   -0.120890    0.479830   -0.1626750
+52336.00  -0.118537   0.482904  -0.1642927   -0.118380    0.482830   -0.1642830
+52337.00  -0.116662   0.485901  -0.1656830   -0.116640    0.485860   -0.1656820
+52338.00  -0.114402   0.488895  -0.1668039   -0.114530    0.488880   -0.1668110
+52339.00  -0.111601   0.491674  -0.1677040   -0.111870    0.491710   -0.1677050
+52340.00  -0.108721   0.494389  -0.1684320   -0.108770    0.494280   -0.1684280
+52341.00  -0.105539   0.497043  -0.1690387   -0.105510    0.497000   -0.1690330
+52342.00  -0.102297   0.499702  -0.1695899   -0.102260    0.499710   -0.1696080
+52343.00  -0.099240   0.502427  -0.1701263   -0.099160    0.502440   -0.1701600
+52344.00  -0.096907   0.505134  -0.1707381   -0.096810    0.505150   -0.1707500
+52345.00  -0.095010   0.507537  -0.1714858   -0.095040    0.507560   -0.1714790
+52346.00  -0.092703   0.509844  -0.1723819   -0.092840    0.509890   -0.1723820
+52347.00  -0.090280   0.512133  -0.1733703   -0.090220    0.512070   -0.1733690
+52348.00  -0.087647   0.514321  -0.1743650   -0.087660    0.514370   -0.1743580
+52349.00  -0.084887   0.516458  -0.1753014   -0.084860    0.516470   -0.1753050
+52350.00  -0.081872   0.518429  -0.1761905   -0.081840    0.518430   -0.1762050
+52351.00  -0.078750   0.520307  -0.1770307   -0.078700    0.520250   -0.1770390
+52352.00  -0.075580   0.522229  -0.1777808   -0.075630    0.522210   -0.1777670
+52353.00  -0.072564   0.523982  -0.1783793   -0.072570    0.523990   -0.1783600
+52354.00  -0.069255   0.525681  -0.1788451   -0.069250    0.525600   -0.1788340
+52355.00  -0.065799   0.527523  -0.1792335   -0.065770    0.527460   -0.1792290
+52356.00  -0.062329   0.529144  -0.1796148   -0.062270    0.529100   -0.1796230
+52357.00  -0.058837   0.530650  -0.1800603   -0.058810    0.530710   -0.1801030
+52358.00  -0.055246   0.532291  -0.1806694   -0.055180    0.532160   -0.1807120
+52359.00  -0.051312   0.534075  -0.1815255   -0.051350    0.533960   -0.1815250
+52360.00  -0.048010   0.535792  -0.1826741   -0.047900    0.535800   -0.1826590
+52361.00  -0.045031   0.537281  -0.1841051   -0.044850    0.537270   -0.1841020
+52362.00  -0.041718   0.538545  -0.1856662   -0.041730    0.538490   -0.1856720
+52363.00  -0.038236   0.539546  -0.1871407   -0.038250    0.539420   -0.1871400
+52364.00  -0.034512   0.540256  -0.1884214   -0.034510    0.540310   -0.1884220
+52365.00  -0.030784   0.540741  -0.1894701   -0.030760    0.540660   -0.1894800
+52366.00  -0.027006   0.541124  -0.1902749   -0.026850    0.541150   -0.1902860
+52367.00  -0.022763   0.541626  -0.1908693   -0.022980    0.541720   -0.1908570
+52368.00  -0.018616   0.542855  -0.1913055   -0.018830    0.542820   -0.1912760
+52369.00  -0.014936   0.544230  -0.1916850   -0.014960    0.544200   -0.1916810
+52370.00  -0.011584   0.545683  -0.1920849   -0.011580    0.545710   -0.1920770
+52371.00  -0.008818   0.546912  -0.1925759   -0.008710    0.546960   -0.1925570
+52372.00  -0.006177   0.547837  -0.1931609   -0.006160    0.547850   -0.1931530
+52373.00  -0.003471   0.548651  -0.1938494   -0.003780    0.548650   -0.1938450
+52374.00  -0.001639   0.549319  -0.1946504   -0.001790    0.549280   -0.1946360
+52375.00   0.000751   0.549735  -0.1955552    0.000630    0.549660   -0.1955480
+52376.00   0.003786   0.550390  -0.1965192    0.003750    0.550380   -0.1965140
+52377.00   0.006859   0.551221  -0.1974561    0.006850    0.551270   -0.1974570
+52378.00   0.009914   0.551919  -0.1983019    0.009940    0.551890   -0.1983280
+52379.00   0.012891   0.552556  -0.1990194    0.012910    0.552530   -0.1990500
+52380.00   0.015666   0.553134  -0.1995747    0.015700    0.553150   -0.1995690
+52381.00   0.018798   0.553633  -0.1999273    0.018850    0.553680   -0.1999170
+52382.00   0.022135   0.554122  -0.2002060    0.022150    0.554150   -0.2001990
+52383.00   0.025465   0.554554  -0.2004662    0.025500    0.554540   -0.2004610
+52384.00   0.028893   0.554819  -0.2006861    0.028950    0.554890   -0.2006920
+52385.00   0.032006   0.554948  -0.2009777    0.032050    0.555000   -0.2009790
+52386.00   0.034837   0.554923  -0.2014742    0.034890    0.554920   -0.2014710
+52387.00   0.037858   0.554783  -0.2022657    0.037860    0.554810   -0.2022590
+52388.00   0.041216   0.554620  -0.2033246    0.041250    0.554620   -0.2033180
+52389.00   0.044875   0.554471  -0.2045984    0.044890    0.554490   -0.2045770
+52390.00   0.048626   0.554293  -0.2060056    0.048700    0.554360   -0.2059990
+52391.00   0.052514   0.553903  -0.2073772    0.052510    0.553960   -0.2073770
+52392.00   0.056207   0.553344  -0.2085449    0.056270    0.553350   -0.2085470
+52393.00   0.059895   0.552842  -0.2094192    0.059990    0.552900   -0.2094170
+52394.00   0.063457   0.552433  -0.2100075    0.063540    0.552680   -0.2099850
+52395.00   0.066763   0.552386  -0.2103409    0.066810    0.552550   -0.2103280
+52396.00   0.069968   0.552234  -0.2105649    0.070040    0.552260   -0.2105750
+52397.00   0.073288   0.551921  -0.2107657    0.073400    0.551980   -0.2107650
+52398.00   0.076875   0.551796  -0.2110038    0.076890    0.551810   -0.2110110
+52399.00   0.080648   0.551735  -0.2113748    0.080730    0.551700   -0.2114160
+52400.00   0.084621   0.551579  -0.2119226    0.084750    0.551580   -0.2119720
+52401.00   0.088539   0.551194  -0.2126279    0.088540    0.551260   -0.2126300
+52402.00   0.091562   0.550682  -0.2134579    0.091560    0.550730   -0.2134090
+52403.00   0.094243   0.549881  -0.2143723    0.094320    0.549950   -0.2143480
+52404.00   0.097639   0.549119  -0.2153390    0.097670    0.549210   -0.2153350
+52405.00   0.101069   0.548815  -0.2163055    0.101130    0.548850   -0.2163140
+52406.00   0.103917   0.548350  -0.2172079    0.104010    0.548380   -0.2172330
+52407.00   0.106766   0.547632  -0.2180235    0.106820    0.547560   -0.2180410
+52408.00   0.109822   0.546781  -0.2187407    0.109690    0.546720   -0.2187190
+52409.00   0.112006   0.545573  -0.2193284    0.112130    0.545580   -0.2193030
+52410.00   0.114375   0.544014  -0.2198677    0.114200    0.544020   -0.2198500
+52411.00   0.116538   0.542416  -0.2203632    0.116510    0.542440   -0.2203590
+52412.00   0.119081   0.541230  -0.2208752    0.119110    0.541240   -0.2208620
+52413.00   0.121980   0.540478  -0.2215514    0.121990    0.540490   -0.2215070
+52414.00   0.125203   0.539959  -0.2224608    0.125270    0.539970   -0.2224240
+52415.00   0.128372   0.539462  -0.2236311    0.128480    0.539480   -0.2236270
+52416.00   0.131149   0.538798  -0.2249998    0.131230    0.538810   -0.2250060
+52417.00   0.133610   0.537948  -0.2264286    0.133700    0.537930   -0.2264180
+52418.00   0.135820   0.537054  -0.2278096    0.135990    0.537030   -0.2278030
+52419.00   0.138173   0.536021  -0.2290156    0.138220    0.536070   -0.2290250
+52420.00   0.140457   0.534941  -0.2299334    0.140550    0.534950   -0.2299640
+52421.00   0.143105   0.533782  -0.2305356    0.143240    0.533780   -0.2305660
+52422.00   0.146195   0.532652  -0.2308616    0.146250    0.532660   -0.2308730
+52423.00   0.148920   0.531341  -0.2309940    0.149140    0.531430   -0.2309950
+52424.00   0.151861   0.529999  -0.2310346    0.151940    0.530030   -0.2310410
+52425.00   0.154660   0.528524  -0.2310732    0.154720    0.528570   -0.2310720
+52426.00   0.157254   0.526992  -0.2311274    0.157340    0.527040   -0.2311240
+52427.00   0.159754   0.525267  -0.2312328    0.159880    0.525310   -0.2312140
+52428.00   0.162414   0.523380  -0.2313968    0.162500    0.523430   -0.2313740
+52429.00   0.165131   0.521795  -0.2316129    0.165300    0.521670   -0.2316120
+52430.00   0.168230   0.520114  -0.2318286    0.168340    0.520030   -0.2318370
+52431.00   0.171414   0.518441  -0.2319476    0.171450    0.518430   -0.2319380
+52432.00   0.174114   0.516725  -0.2319645    0.174530    0.516740   -0.2319570
+52433.00   0.177634   0.515071  -0.2318775    0.177650    0.515060   -0.2318840
+52434.00   0.180641   0.513456  -0.2316492    0.180710    0.513450   -0.2316690
+52435.00   0.183530   0.511787  -0.2312677    0.183570    0.511780   -0.2312790
+52436.00   0.186077   0.509890  -0.2307375    0.186120    0.509890   -0.2307300
+52437.00   0.188424   0.507805  -0.2300812    0.188420    0.507800   -0.2300660
+52438.00   0.190960   0.505694  -0.2294015    0.190770    0.505750   -0.2293550
+52439.00   0.193075   0.503992  -0.2287126    0.193220    0.503920   -0.2287030
+52440.00   0.195887   0.502180  -0.2281968    0.195910    0.502180   -0.2282220
+52441.00   0.199050   0.500101  -0.2279198    0.199100    0.500090   -0.2279630
+52442.00   0.202216   0.497766  -0.2279049    0.202290    0.497760   -0.2279310
+52443.00   0.204825   0.495346  -0.2280920    0.204940    0.495220   -0.2280790
+52444.00   0.207395   0.492822  -0.2283708    0.207500    0.492730   -0.2283380
+52445.00   0.210129   0.490564  -0.2286949    0.210240    0.490560   -0.2286600
+52446.00   0.212475   0.488780  -0.2289991    0.212630    0.488670   -0.2289890
+52447.00   0.214565   0.486718  -0.2292301    0.214630    0.486680   -0.2292480
+52448.00   0.216538   0.484377  -0.2293139    0.216550    0.484340   -0.2293260
+52449.00   0.218215   0.481849  -0.2292565    0.218230    0.481840   -0.2292470
+52450.00   0.219488   0.479106  -0.2291217    0.219580    0.479140   -0.2291160
+52451.00   0.220855   0.476274  -0.2289744    0.220900    0.476260   -0.2289770
+52452.00   0.222495   0.473233  -0.2288397    0.222530    0.473240   -0.2288370
+52453.00   0.224475   0.470176  -0.2287886    0.224420    0.470060   -0.2287840
+52454.00   0.225974   0.467245  -0.2288471    0.226010    0.467180   -0.2288750
+52455.00   0.227145   0.464496  -0.2289808    0.227260    0.464490   -0.2290160
+52456.00   0.228085   0.461571  -0.2292183    0.228170    0.461610   -0.2292270
+52457.00   0.228670   0.458420  -0.2295755    0.228800    0.458450   -0.2295720
+52458.00   0.229477   0.455657  -0.2300230    0.229470    0.455480   -0.2300200
+52459.00   0.229929   0.452705  -0.2304413    0.230020    0.452640   -0.2304250
+52460.00   0.230325   0.449629  -0.2307979    0.230440    0.449620   -0.2307870
+52461.00   0.230813   0.446335  -0.2310385    0.230840    0.446330   -0.2310450
+52462.00   0.231538   0.442868  -0.2311346    0.231560    0.442800   -0.2311160
+52463.00   0.232357   0.439566  -0.2310913    0.232370    0.439500   -0.2310570
+52464.00   0.233101   0.436299  -0.2309258    0.233070    0.436310   -0.2309210
+52465.00   0.234036   0.433127  -0.2306797    0.234030    0.433250   -0.2306780
+52466.00   0.235477   0.430263  -0.2304023    0.235400    0.430340   -0.2303350
+52467.00   0.236735   0.427399  -0.2301805    0.236820    0.427380   -0.2301690
+52468.00   0.238121   0.424430  -0.2301960    0.238100    0.424380   -0.2302190
+52469.00   0.239167   0.421365  -0.2304459    0.239200    0.421320   -0.2304620
+52470.00   0.240194   0.418308  -0.2309062    0.240220    0.418270   -0.2308980
+52471.00   0.241186   0.415505  -0.2314847    0.241160    0.415450   -0.2314830
+52472.00   0.241880   0.412784  -0.2320585    0.241970    0.412750   -0.2320660
+52473.00   0.242880   0.410344  -0.2325272    0.242950    0.410280   -0.2324960
+52474.00   0.244016   0.408159  -0.2328183    0.243920    0.408040   -0.2328090
+52475.00   0.244296   0.405615  -0.2328375    0.244380    0.405560   -0.2328360
+52476.00   0.244742   0.402725  -0.2325613    0.244780    0.402680   -0.2325410
+52477.00   0.245495   0.399749  -0.2320551    0.245540    0.399670   -0.2320260
+52478.00   0.246117   0.396855  -0.2314115    0.246260    0.396850   -0.2314050
+52479.00   0.246666   0.394095  -0.2307041    0.246670    0.394040   -0.2307260
+52480.00   0.246900   0.391189  -0.2299998    0.246940    0.391050   -0.2300230
+52481.00   0.246744   0.387877  -0.2294179    0.246920    0.387830   -0.2294130
+52482.00   0.246820   0.384346  -0.2289761    0.246880    0.384320   -0.2289960
+52483.00   0.247096   0.380592  -0.2286673    0.247190    0.380540   -0.2287050
+52484.00   0.247619   0.376810  -0.2284689    0.247690    0.376780   -0.2284900
+52485.00   0.248459   0.373119  -0.2283114    0.248530    0.373170   -0.2283280
+52486.00   0.249767   0.369842  -0.2281178    0.249840    0.369800   -0.2281490
+52487.00   0.251195   0.366848  -0.2278583    0.251250    0.366800   -0.2278600
+52488.00   0.252209   0.364110  -0.2275238    0.252240    0.364080   -0.2275160
+52489.00   0.252951   0.361548  -0.2271092    0.253020    0.361480   -0.2271160
+52490.00   0.253921   0.358980  -0.2265683    0.253980    0.358920   -0.2265870
+52491.00   0.254925   0.356325  -0.2259038    0.254980    0.356280   -0.2259130
+52492.00   0.255633   0.353419  -0.2251723    0.255770    0.353310   -0.2251650
+52493.00   0.256726   0.350230  -0.2244566    0.256720    0.350180   -0.2244500
+52494.00   0.257858   0.347410  -0.2238681    0.257930    0.347350   -0.2238700
+52495.00   0.259013   0.344903  -0.2235232    0.259060    0.344730   -0.2235210
+52496.00   0.259881   0.342253  -0.2234895    0.259930    0.342210   -0.2235000
+52497.00   0.260387   0.339692  -0.2237494    0.260430    0.339680   -0.2237760
+52498.00   0.260493   0.336844  -0.2242302    0.260510    0.336830   -0.2242470
+52499.00   0.260687   0.333646  -0.2248094    0.260660    0.333730   -0.2248020
+52500.00   0.260828   0.330859  -0.2253333    0.260880    0.330800   -0.2253290
+52501.00   0.260774   0.328167  -0.2257140    0.260800    0.328160   -0.2257190
+52502.00   0.260540   0.325597  -0.2258714    0.260590    0.325560   -0.2258670
+52503.00   0.260229   0.322975  -0.2257922    0.260270    0.322960   -0.2258040
+52504.00   0.259728   0.320286  -0.2255360    0.259760    0.320270   -0.2255640
+52505.00   0.259106   0.317546  -0.2251760    0.259100    0.317490   -0.2251930
+52506.00   0.258503   0.314720  -0.2247865    0.258500    0.314620   -0.2247810
+52507.00   0.257794   0.311720  -0.2244519    0.257860    0.311680   -0.2244460
+52508.00   0.257201   0.308634  -0.2242797    0.257220    0.308630   -0.2242690
+52509.00   0.256792   0.305386  -0.2242788    0.256760    0.305340   -0.2242730
+52510.00   0.256222   0.302075  -0.2243658    0.256260    0.302050   -0.2243560
+52511.00   0.255523   0.298675  -0.2245688    0.255550    0.298650   -0.2245560
+52512.00   0.254858   0.295170  -0.2248545    0.254880    0.295130   -0.2248520
+52513.00   0.254463   0.291714  -0.2251742    0.254570    0.291610   -0.2251720
+52514.00   0.254529   0.288453  -0.2254807    0.254600    0.288320   -0.2254850
+52515.00   0.254519   0.285396  -0.2257157    0.254560    0.285420   -0.2257490
+52516.00   0.254091   0.282309  -0.2258262    0.254150    0.282230   -0.2258220
+52517.00   0.253545   0.279192  -0.2258030    0.253570    0.279080   -0.2258010
+52518.00   0.252756   0.276056  -0.2256657    0.252800    0.276040   -0.2256610
+52519.00   0.251578   0.272855  -0.2254402    0.251620    0.272820   -0.2254090
+52520.00   0.250129   0.269689  -0.2251797    0.250140    0.269660   -0.2251190
+52521.00   0.248953   0.266676  -0.2249860    0.248990    0.266590   -0.2249760
+52522.00   0.248303   0.263660  -0.2249649    0.248290    0.263550   -0.2249850
+52523.00   0.247358   0.260693  -0.2251708    0.247480    0.260610   -0.2251690
+52524.00   0.246202   0.258028  -0.2257018    0.246250    0.257990   -0.2256930
+52525.00   0.244623   0.255658  -0.2265511    0.244650    0.255640   -0.2265270
+52526.00   0.242763   0.253201  -0.2275770    0.242770    0.253220   -0.2275570
+52527.00   0.241036   0.250576  -0.2285921    0.241050    0.250530   -0.2286070
+52528.00   0.239272   0.248027  -0.2294206    0.239380    0.247960   -0.2294510
+52529.00   0.237442   0.245443  -0.2299704    0.237550    0.245520   -0.2299690
+52530.00   0.235727   0.242781  -0.2303005    0.235820    0.242720   -0.2302950
+52531.00   0.234195   0.239697  -0.2304384    0.234230    0.239620   -0.2304410
+52532.00   0.232872   0.236809  -0.2304447    0.232900    0.236760   -0.2304400
+52533.00   0.231666   0.234534  -0.2304053    0.231690    0.234540   -0.2303880
+52534.00   0.230134   0.232605  -0.2303821    0.230150    0.232650   -0.2303780
+52535.00   0.227992   0.230846  -0.2304091    0.228000    0.230880   -0.2304330
+52536.00   0.225292   0.229151  -0.2305297    0.225360    0.229150   -0.2305380
+52537.00   0.222316   0.227178  -0.2307597    0.222380    0.227190   -0.2307560
+52538.00   0.219435   0.224964  -0.2310915    0.219440    0.224910   -0.2310880
+52539.00   0.216745   0.222702  -0.2315018    0.216770    0.222670   -0.2314890
+52540.00   0.214368   0.220362  -0.2319528    0.214380    0.220350   -0.2319350
+52541.00   0.212100   0.217747  -0.2323967    0.212310    0.217770   -0.2323930
+52542.00   0.210452   0.215446  -0.2327672    0.210510    0.215420   -0.2327630
+52543.00   0.208767   0.213100  -0.2330159    0.208750    0.213110   -0.2329580
+52544.00   0.206775   0.210842  -0.2331152    0.206800    0.210720   -0.2330990
+52545.00   0.204745   0.208471  -0.2331567    0.204730    0.208400   -0.2332060
+52546.00   0.202677   0.205888  -0.2330781    0.202690    0.205880   -0.2331410
+52547.00   0.200592   0.203187  -0.2329247    0.200580    0.203140   -0.2329430
+52548.00   0.198430   0.200445  -0.2327953    0.198520    0.200480   -0.2328000
+52549.00   0.196564   0.198099  -0.2328142    0.196610    0.198090   -0.2328330
+52550.00   0.194659   0.195695  -0.2330134    0.194670    0.195650   -0.2330330
+52551.00   0.192452   0.193467  -0.2334663    0.192520    0.193410   -0.2334550
+52552.00   0.189854   0.191374  -0.2342676    0.189930    0.191370   -0.2343080
+52553.00   0.187313   0.189304  -0.2353188    0.187340    0.189270   -0.2353840
+52554.00   0.184951   0.187197  -0.2364692    0.184980    0.187140   -0.2364880
+52555.00   0.182522   0.185011  -0.2375451    0.182620    0.184970   -0.2375410
+52556.00   0.180036   0.182676  -0.2384155    0.180170    0.182730   -0.2384380
+52557.00   0.177936   0.180677  -0.2390205    0.177910    0.180620   -0.2390100
+52558.00   0.175524   0.178743  -0.2393753    0.175550    0.178830   -0.2393640
+52559.00   0.173126   0.176892  -0.2395524    0.173160    0.176910   -0.2395600
+52560.00   0.170913   0.175068  -0.2396689    0.170950    0.175020   -0.2396870
+52561.00   0.168481   0.173411  -0.2398339    0.168490    0.173340   -0.2398360
+52562.00   0.165852   0.171533  -0.2401034    0.165930    0.171640   -0.2400990
+52563.00   0.163384   0.170132  -0.2404951    0.163530    0.170080   -0.2405390
+52564.00   0.161183   0.168869  -0.2410285    0.161140    0.168790   -0.2410910
+52565.00   0.158396   0.167813  -0.2417084    0.158390    0.167740   -0.2417050
+52566.00   0.154524   0.166697  -0.2424420    0.154650    0.166660   -0.2424460
+52567.00   0.150245   0.165404  -0.2432013    0.150440    0.165330   -0.2432310
+52568.00   0.146611   0.163868  -0.2439578    0.146600    0.163910   -0.2439770
+52569.00   0.143148   0.162588  -0.2446889    0.143220    0.162700   -0.2446860
+52570.00   0.139888   0.161306  -0.2453597    0.139950    0.161420   -0.2453520
+52571.00   0.136555   0.160043  -0.2459212    0.136600    0.159880   -0.2459250
+52572.00   0.132959   0.158365  -0.2463482    0.133050    0.158470   -0.2463450
+52573.00   0.129479   0.156684  -0.2466410    0.129500    0.156810   -0.2466280
+52574.00   0.125826   0.155193  -0.2468584    0.125880    0.155240   -0.2468390
+52575.00   0.122277   0.154017  -0.2470415    0.122280    0.153930   -0.2470170
+52576.00   0.118729   0.152877  -0.2472156    0.118900    0.152880   -0.2472120
+52577.00   0.115378   0.152147  -0.2475286    0.115420    0.152150   -0.2475260
+52578.00   0.111554   0.151452  -0.2480936    0.111560    0.151430   -0.2480760
+52579.00   0.107456   0.150724  -0.2489322    0.107540    0.150700   -0.2489260
+52580.00   0.103551   0.149991  -0.2500339    0.103570    0.149990   -0.2500350
+52581.00   0.099863   0.149384  -0.2512752    0.099910    0.149380   -0.2512750
+52582.00   0.096363   0.148926  -0.2525032    0.096410    0.148870   -0.2524980
+52583.00   0.092454   0.148083  -0.2535988    0.092750    0.148250   -0.2535730
+52584.00   0.088989   0.147567  -0.2544123    0.088800    0.147510   -0.2544100
+52585.00   0.084733   0.146842  -0.2549895    0.084780    0.146840   -0.2549740
+52586.00   0.080605   0.146169  -0.2553384    0.080700    0.146110   -0.2553240
+52587.00   0.076653   0.145400  -0.2555385    0.076660    0.145430   -0.2555430
+52588.00   0.072792   0.144872  -0.2556375    0.072840    0.144860   -0.2556750
+52589.00   0.069204   0.144512  -0.2557455    0.069250    0.144500   -0.2557700
+52590.00   0.065865   0.144240  -0.2559352    0.065950    0.144170   -0.2559250
+52591.00   0.062931   0.143850  -0.2562653    0.063020    0.143850   -0.2562560
+52592.00   0.059962   0.143707  -0.2567568    0.059990    0.143740   -0.2567520
+52593.00   0.056305   0.143674  -0.2573501    0.056470    0.143620   -0.2573380
+52594.00   0.052983   0.143654  -0.2579905    0.053070    0.143640   -0.2579920
+52595.00   0.049731   0.143605  -0.2586403    0.049760    0.143600   -0.2586500
+52596.00   0.046266   0.143164  -0.2592524    0.046290    0.143150   -0.2592520
+52597.00   0.042668   0.142504  -0.2597790    0.042760    0.142590   -0.2597580
+52598.00   0.039023   0.142080  -0.2601516    0.039060    0.142050   -0.2601350
+52599.00   0.035643   0.141645  -0.2603927    0.035610    0.141620   -0.2603750
+52600.00   0.032382   0.141439  -0.2605844    0.032530    0.141440   -0.2605540
+52601.00   0.029455   0.141705  -0.2607451    0.029500    0.141590   -0.2607550
+52602.00   0.026278   0.141919  -0.2609050    0.026290    0.141920   -0.2609170
+52603.00   0.023146   0.142072  -0.2611561    0.023140    0.142090   -0.2611320
+52604.00   0.020419   0.142111  -0.2615777    0.020440    0.142180   -0.2615560
+52605.00   0.017702   0.141972  -0.2622236    0.017780    0.141910   -0.2622010
+52606.00   0.014598   0.141668  -0.2631419    0.014610    0.141600   -0.2631340
+52607.00   0.011103   0.141506  -0.2642958    0.011120    0.141530   -0.2642190
+52608.00   0.007579   0.141384  -0.2656414    0.007650    0.141380   -0.2655300
+52609.00   0.004190   0.141181  -0.2670650    0.004230    0.141160   -0.2669760
+52610.00   0.000976   0.141175  -0.2684199    0.000980    0.141150   -0.2683720
+52611.00  -0.002188   0.141366  -0.2695781   -0.002100    0.141540   -0.2695620
+52612.00  -0.005190   0.142182  -0.2704599   -0.005140    0.142250   -0.2704590
+52613.00  -0.008410   0.143068  -0.2710687   -0.008540    0.143130   -0.2710520
+52614.00  -0.012360   0.144207  -0.2714357   -0.012430    0.144160   -0.2714150
+52615.00  -0.016720   0.145538  -0.2717517   -0.016710    0.145520   -0.2717620
+52616.00  -0.020888   0.146709  -0.2721330   -0.020860    0.146750   -0.2721450
+52617.00  -0.024269   0.147737  -0.2726535   -0.024190    0.147790   -0.2726310
+52618.00  -0.027099   0.149028  -0.2733480   -0.027050    0.149110   -0.2733250
+52619.00  -0.030097   0.150624  -0.2742030   -0.029990    0.150660   -0.2742050
+52620.00  -0.032910   0.152253  -0.2751980   -0.032910    0.152320   -0.2751850
+52621.00  -0.036521   0.153643  -0.2762097   -0.036380    0.153690   -0.2762010
+52622.00  -0.040135   0.154901  -0.2772696   -0.040130    0.154810   -0.2772850
+52623.00  -0.044170   0.155905  -0.2782719   -0.044120    0.155800   -0.2783150
+52624.00  -0.048085   0.156669  -0.2791553   -0.048100    0.156550   -0.2791780
+52625.00  -0.051623   0.157274  -0.2798786   -0.051560    0.157350   -0.2798630
+52626.00  -0.054557   0.158497  -0.2804448   -0.054500    0.158360   -0.2804270
+52627.00  -0.056950   0.159883  -0.2808192   -0.056950    0.159770   -0.2808220
+52628.00  -0.059147   0.161774  -0.2810419   -0.059110    0.161760   -0.2810310
+52629.00  -0.061584   0.164084  -0.2812224   -0.061540    0.164060   -0.2812280
+52630.00  -0.064259   0.166441  -0.2813994   -0.064250    0.166410   -0.2814080
+52631.00  -0.066758   0.168646  -0.2816508   -0.066770    0.168630   -0.2816380
+52632.00  -0.069104   0.170874  -0.2820935   -0.069100    0.170830   -0.2820620
+52633.00  -0.071648   0.173153  -0.2828125   -0.071640    0.173150   -0.2827880
+52634.00  -0.074203   0.175133  -0.2837772   -0.074230    0.175140   -0.2837740
+52635.00  -0.076492   0.176860  -0.2849084   -0.076500    0.176820   -0.2849120
+52636.00  -0.078749   0.178859  -0.2860947   -0.078730    0.178830   -0.2860920
+52637.00  -0.081140   0.181116  -0.2872199   -0.081160    0.181130   -0.2872110
+52638.00  -0.083425   0.183254  -0.2881773   -0.083470    0.183210   -0.2881680
+52639.00  -0.085587   0.185612  -0.2889136   -0.085710    0.185460   -0.2889120
+52640.00  -0.088506   0.187936  -0.2894400   -0.088460    0.187930   -0.2894480
+52641.00  -0.091525   0.190250  -0.2897971   -0.091550    0.190200   -0.2897920
+52642.00  -0.094535   0.192681  -0.2901029   -0.094510    0.192640   -0.2900920
+52643.00  -0.097547   0.195185  -0.2904422   -0.097560    0.195210   -0.2904570
+52644.00  -0.100674   0.197402  -0.2908236   -0.100680    0.197410   -0.2908460
+52645.00  -0.103497   0.199315  -0.2913364   -0.103540    0.199310   -0.2913490
+52646.00  -0.105918   0.201164  -0.2920312   -0.105960    0.201290   -0.2920180
+52647.00  -0.108609   0.203326  -0.2928525   -0.108520    0.203380   -0.2928610
+52648.00  -0.111066   0.205508  -0.2937266   -0.111060    0.205470   -0.2937380
+52649.00  -0.113231   0.207881  -0.2946374   -0.113170    0.207880   -0.2946290
+52650.00  -0.115263   0.210338  -0.2955678   -0.115240    0.210360   -0.2955520
+52651.00  -0.117288   0.212631  -0.2964717   -0.117260    0.212610   -0.2964480
+52652.00  -0.118995   0.214760  -0.2972660   -0.119000    0.214730   -0.2972470
+52653.00  -0.120321   0.216851  -0.2979152   -0.120310    0.216890   -0.2979030
+52654.00  -0.121679   0.219146  -0.2984219   -0.121570    0.219120   -0.2984020
+52655.00  -0.122808   0.221634  -0.2987735   -0.122870    0.221530   -0.2987500
+52656.00  -0.124297   0.224033  -0.2990067   -0.124280    0.224060   -0.2989930
+52657.00  -0.126047   0.226509  -0.2992149   -0.125980    0.226530   -0.2992100
+52658.00  -0.127673   0.228811  -0.2994968   -0.127580    0.228790   -0.2994690
+52659.00  -0.128873   0.231030  -0.2999075   -0.128840    0.231010   -0.2998440
+52660.00  -0.129940   0.233478  -0.3004639   -0.129910    0.233420   -0.3004010
+52661.00  -0.131006   0.236356  -0.3011699   -0.130970    0.236350   -0.3011540
+52662.00  -0.132220   0.239582  -0.3019920   -0.132140    0.239610   -0.3020130
+52663.00  -0.133567   0.242395  -0.3028736   -0.133540    0.242440   -0.3028670
+52664.00  -0.135144   0.244806  -0.3037659   -0.135120    0.244810   -0.3037500
+52665.00  -0.136792   0.247042  -0.3045278   -0.136780    0.247020   -0.3045260
+52666.00  -0.138126   0.249381  -0.3050546   -0.138090    0.249450   -0.3050560
+52667.00  -0.138992   0.252107  -0.3053340   -0.138880    0.252210   -0.3053250
+52668.00  -0.139269   0.255088  -0.3054123   -0.139280    0.255170   -0.3054140
+52669.00  -0.139478   0.258219  -0.3054011   -0.139380    0.258240   -0.3053970
+52670.00  -0.139794   0.261292  -0.3053653   -0.139870    0.261290   -0.3053620
+52671.00  -0.141155   0.264059  -0.3053500   -0.141130    0.264050   -0.3053730
+52672.00  -0.142642   0.266788  -0.3054336   -0.142570    0.266750   -0.3054490
+52673.00  -0.144127   0.269650  -0.3056955   -0.144090    0.269670   -0.3056800
+52674.00  -0.145231   0.272399  -0.3061850   -0.145260    0.272440   -0.3061760
+52675.00  -0.146060   0.275320  -0.3068810   -0.146160    0.275280   -0.3068960
+52676.00  -0.147316   0.277939  -0.3076581   -0.147270    0.278000   -0.3076810
+52677.00  -0.148426   0.280393  -0.3084369   -0.148390    0.280430   -0.3084350
+52678.00  -0.149667   0.283189  -0.3091443   -0.149610    0.283160   -0.3091510
+52679.00  -0.151304   0.285945  -0.3097464   -0.151270    0.285920   -0.3097880
+52680.00  -0.152949   0.288612  -0.3102376   -0.152910    0.288570   -0.3102810
+52681.00  -0.154297   0.291302  -0.3106009   -0.154280    0.291370   -0.3106030
+52682.00  -0.155365   0.294094  -0.3108547   -0.155300    0.294100   -0.3108090
+52683.00  -0.155875   0.296851  -0.3110916   -0.155910    0.296780   -0.3110340
+52684.00  -0.156091   0.299473  -0.3113852   -0.156160    0.299500   -0.3113730
+52685.00  -0.156343   0.302128  -0.3117429   -0.156320    0.302110   -0.3117780
+52686.00  -0.156302   0.304583  -0.3121947   -0.156290    0.304550   -0.3122450
+52687.00  -0.155862   0.307063  -0.3128650   -0.155780    0.307060   -0.3128800
+52688.00  -0.155323   0.310072  -0.3137985   -0.155280    0.310030   -0.3137900
+52689.00  -0.154911   0.313488  -0.3149478   -0.154890    0.313550   -0.3149440
+52690.00  -0.154784   0.317584  -0.3161986   -0.154700    0.317550   -0.3161840
+52691.00  -0.155311   0.321366  -0.3174385   -0.155090    0.321380   -0.3174310
+52692.00  -0.155475   0.324475  -0.3185303   -0.155490    0.324470   -0.3185360
+52693.00  -0.155454   0.327396  -0.3193753   -0.155400    0.327390   -0.3193890
+52694.00  -0.155123   0.330443  -0.3199700   -0.155070    0.330430   -0.3199750
+52695.00  -0.154614   0.333493  -0.3203602   -0.154650    0.333430   -0.3203540
+52696.00  -0.154637   0.336386  -0.3206182   -0.154530    0.336370   -0.3206160
+52697.00  -0.154656   0.339306  -0.3208399   -0.154610    0.339280   -0.3208330
+52698.00  -0.154375   0.342141  -0.3210741   -0.154360    0.342110   -0.3210690
+52699.00  -0.153841   0.344649  -0.3213467   -0.153800    0.344630   -0.3213410
+52700.00  -0.153198   0.347240  -0.3217410   -0.153150    0.347190   -0.3217350
+52701.00  -0.152880   0.350202  -0.3222894   -0.152810    0.350190   -0.3222940
+52702.00  -0.153092   0.353080  -0.3229464   -0.153040    0.353130   -0.3229420
+52703.00  -0.153422   0.355842  -0.3236106   -0.153370    0.355830   -0.3236090
+52704.00  -0.153763   0.358316  -0.3242961   -0.153700    0.358310   -0.3242970
+52705.00  -0.154354   0.360787  -0.3250005   -0.154090    0.360750   -0.3249950
+52706.00  -0.154534   0.363477  -0.3256463   -0.154490    0.363470   -0.3256600
+52707.00  -0.154768   0.366339  -0.3262172   -0.154690    0.366290   -0.3262470
+52708.00  -0.154911   0.369337  -0.3267117   -0.154860    0.369300   -0.3267360
+52709.00  -0.155587   0.372368  -0.3271255   -0.155520    0.372380   -0.3271240
+52710.00  -0.156437   0.375474  -0.3274535   -0.156400    0.375480   -0.3274420
+52711.00  -0.157026   0.378713  -0.3277792   -0.157030    0.378590   -0.3277560
+52712.00  -0.157691   0.381330  -0.3281342   -0.157290    0.381370   -0.3281250
+52713.00  -0.157076   0.384012  -0.3286353   -0.157110    0.384010   -0.3286480
+52714.00  -0.156943   0.386870  -0.3293660   -0.156910    0.386880   -0.3293890
+52715.00  -0.156743   0.389761  -0.3303950   -0.156650    0.389760   -0.3304080
+52716.00  -0.155936   0.392624  -0.3317189   -0.155950    0.392630   -0.3317120
+52717.00  -0.154945   0.395674  -0.3332271   -0.154890    0.395810   -0.3332170
+52718.00  -0.153875   0.399566  -0.3347675   -0.153840    0.399600   -0.3347600
+52719.00  -0.153268   0.403517  -0.3361673   -0.153080    0.403390   -0.3361610
+52720.00  -0.152322   0.406664  -0.3373037   -0.152280    0.406640   -0.3373110
+52721.00  -0.151525   0.409589  -0.3381325   -0.151440    0.409600   -0.3381530
+52722.00  -0.150576   0.412494  -0.3386755   -0.150480    0.412410   -0.3386950
+52723.00  -0.148982   0.415439  -0.3390145   -0.149060    0.415390   -0.3390110
+52724.00  -0.147578   0.418309  -0.3392556   -0.147490    0.418350   -0.3392450
+52725.00  -0.145779   0.421212  -0.3395425   -0.145670    0.421160   -0.3395480
+52726.00  -0.143452   0.424036  -0.3399480   -0.143350    0.424030   -0.3399430
+52727.00  -0.140613   0.426885  -0.3404906   -0.140570    0.426870   -0.3404850
+52728.00  -0.137880   0.429885  -0.3411865   -0.137770    0.429840   -0.3412060
+52729.00  -0.135448   0.432832  -0.3420147   -0.135380    0.432830   -0.3420440
+52730.00  -0.133253   0.435574  -0.3429313   -0.133250    0.435620   -0.3429290
+52731.00  -0.131337   0.438596  -0.3438876   -0.131170    0.438590   -0.3438740
+52732.00  -0.129096   0.441736  -0.3448712   -0.129050    0.441740   -0.3448740
+52733.00  -0.127130   0.444678  -0.3458013   -0.126950    0.444670   -0.3457940
+52734.00  -0.124629   0.447542  -0.3466152   -0.124580    0.447540   -0.3466170
+52735.00  -0.122177   0.450759  -0.3473352   -0.122080    0.450760   -0.3473310
+52736.00  -0.119826   0.454061  -0.3479283   -0.119780    0.454050   -0.3479260
+52737.00  -0.117581   0.456994  -0.3483759   -0.117590    0.456990   -0.3483700
+52738.00  -0.116238   0.459671  -0.3487087   -0.115960    0.459710   -0.3487010
+52739.00  -0.114690   0.462343  -0.3490285   -0.114680    0.462350   -0.3490260
+52740.00  -0.113539   0.464834  -0.3494229   -0.113230    0.464850   -0.3494180
+52741.00  -0.111408   0.467511  -0.3499690   -0.111410    0.467490   -0.3499570
+52742.00  -0.109399   0.470304  -0.3507694   -0.109290    0.470290   -0.3507470
+52743.00  -0.107268   0.472883  -0.3518708   -0.107170    0.472880   -0.3518560
+52744.00  -0.105224   0.475253  -0.3532336   -0.105260    0.475280   -0.3532260
+52745.00  -0.103976   0.477415  -0.3547178   -0.103790    0.477410   -0.3547140
+52746.00  -0.102241   0.479238  -0.3561391   -0.102200    0.479230   -0.3561370
+52747.00  -0.100347   0.481207  -0.3573084   -0.100290    0.481160   -0.3572750
+52748.00  -0.098110   0.483411  -0.3581655   -0.098020    0.483390   -0.3581420
+52749.00  -0.095579   0.485678  -0.3586492   -0.095490    0.485660   -0.3586890
+52750.00  -0.093129   0.487711  -0.3588349   -0.093040    0.487740   -0.3589020
+52751.00  -0.090755   0.489518  -0.3588929   -0.090700    0.489620   -0.3588960
+52752.00  -0.088601   0.491625  -0.3589248   -0.088490    0.491700   -0.3588860
+52753.00  -0.086073   0.493677  -0.3590416   -0.085900    0.493740   -0.3590440
+52754.00  -0.082835   0.495853  -0.3593016   -0.082770    0.495770   -0.3592960
+52755.00  -0.080055   0.497996  -0.3596948   -0.079990    0.497960   -0.3596890
+52756.00  -0.077683   0.500185  -0.3602410   -0.077590    0.500160   -0.3602360
+52757.00  -0.075264   0.502396  -0.3609113   -0.075190    0.502440   -0.3609080
+52758.00  -0.072945   0.504336  -0.3616574   -0.072840    0.504420   -0.3616510
+52759.00  -0.070452   0.506168  -0.3624152   -0.070270    0.506180   -0.3624080
+52760.00  -0.066884   0.508018  -0.3631068   -0.066850    0.508020   -0.3631080
+52761.00  -0.063134   0.509849  -0.3636406   -0.063020    0.509870   -0.3636450
+52762.00  -0.059460   0.511574  -0.3639858   -0.059380    0.511610   -0.3639710
+52763.00  -0.056188   0.513428  -0.3641403   -0.056110    0.513430   -0.3640950
+52764.00  -0.053251   0.515445  -0.3641271   -0.053140    0.515420   -0.3640860
+52765.00  -0.050102   0.517481  -0.3640312   -0.050100    0.517520   -0.3640240
+52766.00  -0.047177   0.519684  -0.3639292   -0.047150    0.519710   -0.3639140
+52767.00  -0.044759   0.521707  -0.3638449   -0.044670    0.521610   -0.3638350
+52768.00  -0.042316   0.523206  -0.3639183   -0.042140    0.523330   -0.3639110
+52769.00  -0.039744   0.525015  -0.3642129   -0.039680    0.525050   -0.3642000
+52770.00  -0.037767   0.526629  -0.3647435   -0.037690    0.526600   -0.3647260
+52771.00  -0.036128   0.527945  -0.3655317   -0.036010    0.527970   -0.3655100
+52772.00  -0.034478   0.529264  -0.3665666   -0.034400    0.529290   -0.3665540
+52773.00  -0.032655   0.530583  -0.3677702   -0.032560    0.530530   -0.3677770
+52774.00  -0.030523   0.531887  -0.3689547   -0.030430    0.531850   -0.3689700
+52775.00  -0.028293   0.533225  -0.3699223   -0.028250    0.533180   -0.3699180
+52776.00  -0.025981   0.534353  -0.3705531   -0.025890    0.534300   -0.3705330
+52777.00  -0.023693   0.535468  -0.3709000   -0.023560    0.535470   -0.3708860
+52778.00  -0.021358   0.536500  -0.3710701   -0.021250    0.536550   -0.3710520
+52779.00  -0.018620   0.537522  -0.3712217   -0.018500    0.537530   -0.3712130
+52780.00  -0.015410   0.538602  -0.3714380   -0.015430    0.538610   -0.3714480
+52781.00  -0.012603   0.539688  -0.3717551   -0.012490    0.539640   -0.3717310
+52782.00  -0.009866   0.540575  -0.3722105   -0.009800    0.540440   -0.3722000
+52783.00  -0.007004   0.540972  -0.3727809   -0.006960    0.540960   -0.3727690
+52784.00  -0.004106   0.541211  -0.3734260   -0.004030    0.541340   -0.3733950
+52785.00  -0.001056   0.541528  -0.3740728   -0.000940    0.541610   -0.3740370
+52786.00   0.002298   0.541977  -0.3746654    0.002370    0.541990   -0.3746410
+52787.00   0.005837   0.542598  -0.3751705    0.005880    0.542640   -0.3751620
+52788.00   0.009260   0.543591  -0.3755531    0.009390    0.543690   -0.3755520
+52789.00   0.012429   0.544507  -0.3757844    0.012550    0.544580   -0.3757760
+52790.00   0.015576   0.545546  -0.3758359    0.015640    0.545500   -0.3758550
+52791.00   0.018416   0.546437  -0.3756855    0.018520    0.546410   -0.3757030
+52792.00   0.021231   0.547100  -0.3753793    0.021340    0.546990   -0.3753690
+52793.00   0.024311   0.547652  -0.3749825    0.024380    0.547540   -0.3749710
+52794.00   0.027719   0.547855  -0.3745271    0.027810    0.547850   -0.3745520
+52795.00   0.031430   0.547882  -0.3740730    0.031540    0.547890   -0.3740740
+52796.00   0.034917   0.547818  -0.3737576    0.035020    0.547910   -0.3737490
+52797.00   0.038130   0.547817  -0.3736737    0.038190    0.547810   -0.3736780
+52798.00   0.041333   0.547855  -0.3738447    0.041410    0.547810   -0.3738780
+52799.00   0.044736   0.547922  -0.3742503    0.044810    0.547930   -0.3742770
+52800.00   0.048281   0.548027  -0.3747833    0.048330    0.548050   -0.3747790
+52801.00   0.051673   0.548061  -0.3752832    0.051790    0.548060   -0.3752700
+52802.00   0.055139   0.547756  -0.3756082    0.055260    0.547750   -0.3756120
+52803.00   0.058556   0.547201  -0.3756412    0.058730    0.547230   -0.3756430
+52804.00   0.062094   0.546689  -0.3753452    0.062170    0.546630   -0.3753230
+52805.00   0.065802   0.546171  -0.3747957    0.065870    0.546100   -0.3747700
+52806.00   0.069733   0.545588  -0.3741172    0.069830    0.545570   -0.3741000
+52807.00   0.074052   0.545079  -0.3734464    0.074140    0.545040   -0.3734360
+52808.00   0.078547   0.544862  -0.3728845    0.078630    0.544840   -0.3728820
+52809.00   0.082892   0.544756  -0.3724844    0.083140    0.544680   -0.3724930
+52810.00   0.087700   0.544532  -0.3722530    0.087770    0.544520   -0.3722550
+52811.00   0.091956   0.544384  -0.3721348    0.092070    0.544420   -0.3721070
+52812.00   0.095888   0.544085  -0.3720627    0.095980    0.544080   -0.3720050
+52813.00   0.099772   0.543616  -0.3719772    0.099880    0.543620   -0.3719230
+52814.00   0.103554   0.542996  -0.3718238    0.103640    0.543060   -0.3718090
+52815.00   0.107221   0.542491  -0.3715540    0.107340    0.542480   -0.3715590
+52816.00   0.110928   0.542076  -0.3711389    0.111030    0.542050   -0.3711060
+52817.00   0.114722   0.541683  -0.3705786    0.114840    0.541520   -0.3705660
+52818.00   0.118619   0.541064  -0.3698746    0.118730    0.540990   -0.3698540
+52819.00   0.122623   0.540447  -0.3690496    0.122740    0.540420   -0.3690180
+52820.00   0.126689   0.539883  -0.3681479    0.126790    0.539860   -0.3681090
+52821.00   0.130468   0.539313  -0.3672420    0.130560    0.539290   -0.3672270
+52822.00   0.133821   0.538582  -0.3664106    0.133980    0.538570   -0.3664210
+52823.00   0.136813   0.537563  -0.3657010    0.137040    0.537500   -0.3656920
+52824.00   0.139880   0.535923  -0.3651475    0.139980    0.535890   -0.3651290
+52825.00   0.143073   0.534157  -0.3647861    0.143200    0.534100   -0.3647340
+52826.00   0.146403   0.532623  -0.3645739    0.146510    0.532570   -0.3644890
+52827.00   0.149616   0.531187  -0.3644331    0.149680    0.531190   -0.3643570
+52828.00   0.153014   0.529717  -0.3642754    0.153090    0.529730   -0.3642560
+52829.00   0.156652   0.528214  -0.3640177    0.156730    0.528180   -0.3640300
+52830.00   0.160400   0.526692  -0.3635461    0.160450    0.526650   -0.3635320
+52831.00   0.163893   0.525208  -0.3628283    0.164060    0.525270   -0.3628150
+52832.00   0.167447   0.523922  -0.3619299    0.167540    0.523940   -0.3619300
+52833.00   0.170540   0.522624  -0.3609656    0.170640    0.522650   -0.3609700
+52834.00   0.173119   0.521200  -0.3600377    0.173240    0.521190   -0.3600350
+52835.00   0.175615   0.519726  -0.3592355    0.175690    0.519660   -0.3592290
+52836.00   0.178216   0.518207  -0.3586320    0.178280    0.518100   -0.3586240
+52837.00   0.180961   0.516617  -0.3582506    0.180970    0.516510   -0.3582320
+52838.00   0.183442   0.514713  -0.3580347    0.183560    0.514700   -0.3580200
+52839.00   0.186304   0.512678  -0.3579064    0.186360    0.512610   -0.3579100
+52840.00   0.189291   0.510645  -0.3577985    0.189360    0.510580   -0.3577950
+52841.00   0.191970   0.508531  -0.3576469    0.192020    0.508560   -0.3576290
+52842.00   0.194308   0.506211  -0.3574124    0.194430    0.506240   -0.3573990
+52843.00   0.197124   0.503868  -0.3570731    0.197180    0.503790   -0.3570760
+52844.00   0.199921   0.501622  -0.3566195    0.200020    0.501600   -0.3566200
+52845.00   0.202476   0.499419  -0.3560571    0.202520    0.499500   -0.3560510
+52846.00   0.204685   0.497220  -0.3554030    0.204770    0.497210   -0.3554020
+52847.00   0.206809   0.494817  -0.3547428    0.206880    0.494770   -0.3547510
+52848.00   0.208980   0.492100  -0.3541361    0.209050    0.492120   -0.3541420
+52849.00   0.211303   0.489142  -0.3536200    0.211370    0.489240   -0.3536150
+52850.00   0.213815   0.486413  -0.3532271    0.213900    0.486430   -0.3532260
+52851.00   0.216500   0.483887  -0.3530459    0.216580    0.483900   -0.3530440
+52852.00   0.219112   0.481538  -0.3531033    0.219220    0.481570   -0.3530920
+52853.00   0.221386   0.479513  -0.3533306    0.221430    0.479470   -0.3533400
+52854.00   0.223071   0.477318  -0.3536689    0.223150    0.477200   -0.3537200
+52855.00   0.224748   0.474861  -0.3540496    0.224830    0.474730   -0.3540840
+52856.00   0.226443   0.472367  -0.3543334    0.226500    0.472310   -0.3543240
+52857.00   0.228150   0.469614  -0.3543726    0.228230    0.469600   -0.3543660
+52858.00   0.230141   0.466612  -0.3541745    0.230230    0.466580   -0.3541780
+52859.00   0.232124   0.463704  -0.3537567    0.232240    0.463660   -0.3537490
+52860.00   0.234060   0.461100  -0.3531909    0.234120    0.461030   -0.3531860
+52861.00   0.235882   0.458865  -0.3526009    0.235980    0.458830   -0.3525970
+52862.00   0.237707   0.456702  -0.3521001    0.237790    0.456760   -0.3520940
+52863.00   0.239487   0.454309  -0.3517812    0.239600    0.454460   -0.3517730
+52864.00   0.241314   0.452088  -0.3516894    0.241370    0.452080   -0.3516780
+52865.00   0.243093   0.449784  -0.3517784    0.243160    0.449760   -0.3517770
+52866.00   0.244570   0.447324  -0.3520012    0.244710    0.447420   -0.3519870
+52867.00   0.246011   0.444752  -0.3523064    0.246050    0.444730   -0.3523300
+52868.00   0.247532   0.441804  -0.3526161    0.247620    0.441710   -0.3526850
+52869.00   0.249124   0.438677  -0.3528512    0.249190    0.438650   -0.3529040
+52870.00   0.250680   0.435417  -0.3529173    0.250720    0.435430   -0.3529140
+52871.00   0.252254   0.432462  -0.3527417    0.252330    0.432380   -0.3527250
+52872.00   0.253774   0.429753  -0.3523732    0.253840    0.429680   -0.3523710
+52873.00   0.254928   0.427262  -0.3518548    0.255050    0.427260   -0.3518440
+52874.00   0.255947   0.424872  -0.3512339    0.255980    0.424890   -0.3512340
+52875.00   0.256958   0.422061  -0.3505935    0.257020    0.422020   -0.3506380
+52876.00   0.258249   0.418880  -0.3499998    0.258310    0.418840   -0.3500360
+52877.00   0.259210   0.415542  -0.3495442    0.259350    0.415660   -0.3495410
+52878.00   0.260146   0.412296  -0.3493055    0.260160    0.412320   -0.3493060
+52879.00   0.261170   0.409038  -0.3494278    0.261270    0.409070   -0.3493980
+52880.00   0.262134   0.406067  -0.3498745    0.262410    0.406130   -0.3498590
+52881.00   0.263337   0.403308  -0.3505380    0.263420    0.403290   -0.3505460
+52882.00   0.264430   0.400496  -0.3512890    0.264490    0.400450   -0.3513040
+52883.00   0.265244   0.397526  -0.3519601    0.265340    0.397440   -0.3519670
+52884.00   0.265514   0.394414  -0.3524292    0.265610    0.394360   -0.3524220
+52885.00   0.265529   0.391195  -0.3526473    0.265540    0.391190   -0.3526340
+52886.00   0.265386   0.387938  -0.3525985    0.265490    0.387910   -0.3525990
+52887.00   0.265113   0.384441  -0.3523713    0.265320    0.384430   -0.3523630
+52888.00   0.265182   0.380910  -0.3520822    0.265190    0.380840   -0.3520800
+52889.00   0.265053   0.377505  -0.3517877    0.265120    0.377410   -0.3518070
+52890.00   0.264911   0.374278  -0.3515887    0.264960    0.374190   -0.3515900
+52891.00   0.264699   0.371156  -0.3515693    0.264780    0.371110   -0.3515600
+52892.00   0.264511   0.367894  -0.3517499    0.264640    0.367820   -0.3517590
+52893.00   0.264605   0.364586  -0.3520732    0.264740    0.364530   -0.3520710
+52894.00   0.264519   0.361280  -0.3524733    0.264560    0.361260   -0.3524620
+52895.00   0.264233   0.357987  -0.3528459    0.264300    0.357910   -0.3528470
+52896.00   0.264223   0.354809  -0.3531448    0.264280    0.354740   -0.3531440
+52897.00   0.264230   0.351772  -0.3533557    0.264320    0.351740   -0.3533550
+52898.00   0.264141   0.348778  -0.3534664    0.264230    0.348800   -0.3534580
+52899.00   0.264033   0.345932  -0.3534705    0.264090    0.345900   -0.3534420
+52900.00   0.264004   0.342995  -0.3533594    0.264010    0.342920   -0.3533250
+52901.00   0.263698   0.339808  -0.3531016    0.263760    0.339770   -0.3530900
+52902.00   0.263057   0.336422  -0.3528097    0.263100    0.336400   -0.3528150
+52903.00   0.262535   0.332948  -0.3525464    0.262570    0.332800   -0.3525610
+52904.00   0.262588   0.329728  -0.3524014    0.262610    0.329590   -0.3523970
+52905.00   0.262482   0.326856  -0.3524815    0.262550    0.326880   -0.3524690
+52906.00   0.261882   0.324086  -0.3528272    0.261930    0.324050   -0.3528410
+52907.00   0.260917   0.320821  -0.3534321    0.260980    0.320860   -0.3534380
+52908.00   0.260061   0.317273  -0.3542544    0.260120    0.317310   -0.3542520
+52909.00   0.259523   0.314020  -0.3551847    0.259650    0.313970   -0.3551960
+52910.00   0.259357   0.311099  -0.3560733    0.259430    0.311050   -0.3560970
+52911.00   0.259185   0.308483  -0.3567894    0.259270    0.308390   -0.3568040
+52912.00   0.259205   0.306213  -0.3572758    0.259180    0.306160   -0.3572700
+52913.00   0.258441   0.304032  -0.3575077    0.258530    0.304000   -0.3575060
+52914.00   0.257371   0.301431  -0.3575564    0.257380    0.301390   -0.3575580
+52915.00   0.256285   0.298632  -0.3575382    0.256250    0.298690   -0.3575300
+52916.00   0.254920   0.296130  -0.3575692    0.254990    0.296150   -0.3575800
+52917.00   0.253564   0.293447  -0.3577245    0.253630    0.293450   -0.3577210
+52918.00   0.252216   0.290458  -0.3580633    0.252280    0.290420   -0.3580400
+52919.00   0.250917   0.287406  -0.3586134    0.250960    0.287370   -0.3586000
+52920.00   0.249549   0.284675  -0.3593206    0.249600    0.284630   -0.3593330
+52921.00   0.248047   0.282076  -0.3600671    0.248110    0.282020   -0.3600770
+52922.00   0.246394   0.279262  -0.3607698    0.246430    0.279220   -0.3607580
+52923.00   0.244461   0.276479  -0.3614181    0.244420    0.276420   -0.3614210
+52924.00   0.242041   0.273675  -0.3619580    0.242100    0.273640   -0.3619840
+52925.00   0.239767   0.270716  -0.3623574    0.239820    0.270690   -0.3623750
+52926.00   0.237786   0.267467  -0.3626070    0.237790    0.267490   -0.3625980
+52927.00   0.235754   0.264252  -0.3627139    0.235850    0.264180   -0.3626930
+52928.00   0.233833   0.261075  -0.3626985    0.233880    0.261050   -0.3626940
+52929.00   0.231713   0.257917  -0.3626126    0.231840    0.257960   -0.3626070
+52930.00   0.229791   0.255061  -0.3625398    0.229830    0.254970   -0.3625330
+52931.00   0.227912   0.252528  -0.3625865    0.227960    0.252450   -0.3626020
+52932.00   0.225985   0.250041  -0.3628482    0.226020    0.250040   -0.3628760
+52933.00   0.224045   0.247363  -0.3633946    0.224070    0.247350   -0.3633930
+52934.00   0.221840   0.244727  -0.3642337    0.221970    0.244690   -0.3641960
+52935.00   0.220252   0.242207  -0.3653312    0.220270    0.242220   -0.3652860
+52936.00   0.219375   0.239955  -0.3666165    0.219290    0.239920   -0.3665910
+52937.00   0.218588   0.237830  -0.3679282    0.218640    0.237720   -0.3679320
+52938.00   0.218074   0.235800  -0.3690879    0.218130    0.235680   -0.3690970
+52939.00   0.217044   0.233764  -0.3699470    0.217130    0.233710   -0.3699450
+52940.00   0.215526   0.231571  -0.3704387    0.215490    0.231520   -0.3704240
+52941.00   0.213727   0.229245  -0.3705972    0.213800    0.229150   -0.3705920
+52942.00   0.212410   0.226990  -0.3705550    0.212460    0.226900   -0.3705570
+52943.00   0.210964   0.225015  -0.3704466    0.211060    0.225010   -0.3704340
+52944.00   0.209202   0.223347  -0.3703764    0.209260    0.223310   -0.3703860
+52945.00   0.207195   0.221630  -0.3704385    0.207240    0.221550   -0.3704470
+52946.00   0.205026   0.219554  -0.3706830    0.205080    0.219480   -0.3706680
+52947.00   0.202683   0.217024  -0.3711079    0.202740    0.216980   -0.3710900
+52948.00   0.200310   0.214164  -0.3716585    0.200360    0.214070   -0.3716580
+52949.00   0.198074   0.211221  -0.3722295    0.198250    0.211100   -0.3722220
+52950.00   0.196251   0.208259  -0.3727509    0.196320    0.208230   -0.3727430
+52951.00   0.194565   0.205715  -0.3731800    0.194580    0.205620   -0.3731760
+52952.00   0.192663   0.203465  -0.3735121    0.192750    0.203370   -0.3735510
+52953.00   0.190187   0.201115  -0.3737231    0.190250    0.201140   -0.3737820
+52954.00   0.187195   0.198509  -0.3738188    0.187240    0.198490   -0.3738110
+52955.00   0.184474   0.196121  -0.3737553    0.184520    0.195980   -0.3737240
+52956.00   0.182107   0.193777  -0.3736979    0.182190    0.193700   -0.3736830
+52957.00   0.179905   0.191388  -0.3737004    0.179870    0.191370   -0.3736890
+52958.00   0.177366   0.189111  -0.3737575    0.177470    0.189040   -0.3737660
+52959.00   0.175135   0.187130  -0.3739175    0.175190    0.187030   -0.3739370
+52960.00   0.172807   0.185468  -0.3742697    0.172860    0.185420   -0.3742740
+52961.00   0.170303   0.183784  -0.3748706    0.170180    0.183740   -0.3748600
+52962.00   0.167240   0.181979  -0.3756862    0.167340    0.181890   -0.3756920
+52963.00   0.164407   0.180379  -0.3766273    0.164430    0.180360   -0.3766400
+52964.00   0.161879   0.179080  -0.3776699    0.161850    0.179120   -0.3776600
+52965.00   0.159310   0.178390  -0.3787721    0.159410    0.178310   -0.3787760
+52966.00   0.156601   0.177787  -0.3797053    0.156660    0.177700   -0.3797530
+52967.00   0.153407   0.176857  -0.3803525    0.153490    0.176770   -0.3803940
+52968.00   0.149829   0.175610  -0.3807045    0.149710    0.175550   -0.3806940
+52969.00   0.145164   0.174214  -0.3807790    0.145320    0.174190   -0.3807670
+52970.00   0.141260   0.172979  -0.3807209    0.141260    0.172870   -0.3807190
+52971.00   0.137855   0.171862  -0.3806866    0.137930    0.171760   -0.3807010
+52972.00   0.134328   0.170603  -0.3807901    0.134400    0.170550   -0.3808080
+52973.00   0.130325   0.169151  -0.3810508    0.130400    0.169040   -0.3810580
+52974.00   0.126420   0.167832  -0.3814428    0.126440    0.167710   -0.3814390
+52975.00   0.123266   0.166673  -0.3819390    0.123160    0.166610   -0.3819270
+52976.00   0.120076   0.165475  -0.3824741    0.120170    0.165390   -0.3824720
+52977.00   0.116995   0.164471  -0.3829730    0.117050    0.164370   -0.3829730
+52978.00   0.113973   0.163778  -0.3833573    0.114130    0.163830   -0.3833480
+52979.00   0.111334   0.163491  -0.3836394    0.111400    0.163450   -0.3836440
+52980.00   0.108467   0.163096  -0.3838023    0.108520    0.162960   -0.3838250
+52981.00   0.105420   0.162534  -0.3837965    0.105460    0.162450   -0.3837940
+52982.00   0.102488   0.162010  -0.3835799    0.102520    0.162010   -0.3835510
+52983.00   0.099885   0.161527  -0.3832095    0.099840    0.161520   -0.3832000
+52984.00   0.096725   0.161011  -0.3828210    0.096870    0.160960   -0.3828340
+52985.00   0.093248   0.160277  -0.3824442    0.093360    0.160300   -0.3824450
+52986.00   0.089735   0.159516  -0.3821568    0.089740    0.159450   -0.3821680
+52987.00   0.086412   0.158859  -0.3819860    0.086450    0.158710   -0.3819980
+52988.00   0.083456   0.158562  -0.3820095    0.083500    0.158510   -0.3820050
+52989.00   0.080536   0.158347  -0.3822637    0.080610    0.158360   -0.3822560
+52990.00   0.077519   0.158153  -0.3827665    0.077620    0.158050   -0.3827510
+52991.00   0.074448   0.157708  -0.3834666    0.074480    0.157650   -0.3834390
+52992.00   0.071389   0.157209  -0.3842406    0.071480    0.157210   -0.3842270
+52993.00   0.068871   0.156887  -0.3850126    0.068890    0.156860   -0.3850240
+52994.00   0.066300   0.156452  -0.3856916    0.066350    0.156400   -0.3857350
+52995.00   0.063454   0.155925  -0.3861420    0.063520    0.155850   -0.3861900
+52996.00   0.060465   0.155499  -0.3863182    0.060390    0.155410   -0.3863100
+52997.00   0.056583   0.155114  -0.3863249    0.056690    0.155070   -0.3862360
+52998.00   0.052600   0.154776  -0.3863931    0.052620    0.154710   -0.3862160
+52999.00   0.048915   0.154521  -0.3865721    0.048950    0.154440   -0.3863760
+53000.00   0.045817   0.154535  -0.3868940    0.045870    0.154430   -0.3867280
+53001.00   0.042887   0.154743  -0.3873570    0.042940    0.154660   -0.3872450
+53002.00   0.039838   0.154765  -0.3879178    0.039910    0.154710   -0.3878690
+53003.00   0.037041   0.154460  -0.3885253    0.037030    0.154430   -0.3885140
+53004.00   0.034030   0.154152  -0.3891069    0.034120    0.154100   -0.3890920
+53005.00   0.031244   0.153837  -0.3896242    0.031250    0.153770   -0.3895920
+53006.00   0.028844   0.153666  -0.3900898    0.028870    0.153550   -0.3900740
+53007.00   0.026642   0.153845  -0.3904388    0.026700    0.153740   -0.3904550
+53008.00   0.024196   0.154296  -0.3906064    0.024260    0.154230   -0.3906300
+53009.00   0.021488   0.154846  -0.3906172    0.021590    0.154800   -0.3906200
+53010.00   0.018791   0.155474  -0.3905345    0.018790    0.155440   -0.3905240
+53011.00   0.015852   0.156213  -0.3904555    0.015960    0.156150   -0.3904520
+53012.00   0.012904   0.156919  -0.3904750    0.012990    0.156850   -0.3904660
+53013.00   0.010078   0.157347  -0.3906236    0.010150    0.157360   -0.3906230
+53014.00   0.007583   0.157668  -0.3909863    0.007710    0.157630   -0.3909980
+53015.00   0.005354   0.157925  -0.3916007    0.005380    0.157840   -0.3916020
+53016.00   0.003033   0.158350  -0.3924755    0.003030    0.158320   -0.3924550
+53017.00   0.001086   0.159029  -0.3935831    0.001150    0.159010   -0.3935690
+53018.00  -0.001169   0.160079  -0.3948550   -0.000940    0.159990   -0.3948590
+53019.00  -0.003297   0.161392  -0.3961229   -0.003280    0.161360   -0.3961320
+53020.00  -0.005379   0.162631  -0.3972759   -0.005390    0.162610   -0.3972730
+53021.00  -0.007618   0.163769  -0.3982373   -0.007540    0.163700   -0.3982610
+53022.00  -0.010220   0.165080  -0.3989327   -0.010170    0.165030   -0.3989770
+53023.00  -0.013234   0.166344  -0.3993909   -0.013220    0.166280   -0.3994140
+53024.00  -0.016200   0.167254  -0.3996555   -0.016180    0.167220   -0.3996580
+53025.00  -0.018669   0.168256  -0.3998536   -0.018640    0.168310   -0.3998410
+53026.00  -0.021135   0.170026  -0.4000824   -0.021090    0.169990   -0.4000790
+53027.00  -0.024176   0.171779  -0.4004461   -0.023830    0.171790   -0.4004350
+53028.00  -0.026293   0.173195  -0.4009626   -0.026220    0.173120   -0.4009750
+53029.00  -0.027930   0.174506  -0.4016320   -0.027880    0.174430   -0.4016610
+53030.00  -0.029166   0.175922  -0.4024375   -0.029110    0.175870   -0.4024540
+53031.00  -0.030225   0.177463  -0.4033134   -0.030220    0.177420   -0.4033020
+53032.00  -0.031671   0.179219  -0.4040843   -0.031610    0.179160   -0.4040940
+53033.00  -0.033515   0.181087  -0.4046773   -0.033490    0.181000   -0.4046910
+53034.00  -0.036000   0.182968  -0.4050564   -0.035950    0.182930   -0.4050420
+53035.00  -0.038965   0.184635  -0.4051777   -0.038950    0.184640   -0.4051900
+53036.00  -0.042053   0.185947  -0.4051274   -0.042020    0.185890   -0.4051460
+53037.00  -0.045042   0.187066  -0.4049571   -0.044930    0.186970   -0.4049600
+53038.00  -0.047761   0.188154  -0.4047153   -0.047970    0.188030   -0.4047120
+53039.00  -0.051257   0.189038  -0.4044378   -0.051200    0.188970   -0.4044510
+53040.00  -0.054189   0.189870  -0.4041928   -0.054160    0.189780   -0.4042020
+53041.00  -0.056733   0.190686  -0.4041270   -0.056640    0.190710   -0.4041170
+53042.00  -0.058825   0.192037  -0.4042345   -0.058780    0.191970   -0.4042390
+53043.00  -0.060688   0.193626  -0.4046462   -0.060610    0.193540   -0.4046350
+53044.00  -0.062592   0.195360  -0.4053054   -0.062540    0.195330   -0.4052940
+53045.00  -0.064874   0.197078  -0.4061337   -0.064880    0.197140   -0.4061210
+53046.00  -0.067757   0.199111  -0.4070526   -0.067650    0.199070   -0.4070350
+53047.00  -0.070767   0.200933  -0.4079696   -0.070720    0.200890   -0.4079620
+53048.00  -0.073855   0.202602  -0.4087687   -0.073760    0.202610   -0.4087570
+53049.00  -0.076809   0.204484  -0.4093587   -0.076740    0.204440   -0.4093720
+53050.00  -0.079498   0.206270  -0.4097087   -0.079460    0.206200   -0.4097500
+53051.00  -0.081567   0.208068  -0.4098775   -0.081510    0.208070   -0.4099010
+53052.00  -0.082877   0.210042  -0.4099673   -0.082870    0.210050   -0.4099550
+53053.00  -0.084374   0.212379  -0.4100716   -0.084210    0.212270   -0.4100770
+53054.00  -0.086622   0.214835  -0.4103309   -0.086570    0.214860   -0.4103380
+53055.00  -0.090064   0.217070  -0.4108241   -0.089840    0.217220   -0.4108150
+53056.00  -0.093068   0.219557  -0.4115674   -0.093010    0.219500   -0.4115850
+53057.00  -0.095704   0.222040  -0.4124818   -0.095640    0.221970   -0.4125160
+53058.00  -0.097959   0.224295  -0.4135315   -0.097910    0.224270   -0.4135320
+53059.00  -0.099798   0.226393  -0.4146100   -0.099810    0.226380   -0.4145990
+53060.00  -0.101355   0.228660  -0.4155830   -0.101260    0.228610   -0.4155980
+53061.00  -0.102509   0.231015  -0.4163774   -0.102480    0.230940   -0.4163790
+53062.00  -0.104300   0.233444  -0.4169791   -0.103900    0.233390   -0.4169680
+53063.00  -0.105932   0.235858  -0.4173906   -0.105970    0.235800   -0.4173890
+53064.00  -0.108401   0.237914  -0.4175409   -0.108330    0.237830   -0.4175520
+53065.00  -0.110501   0.239906  -0.4174892   -0.110480    0.239820   -0.4174860
+53066.00  -0.112161   0.242124  -0.4173477   -0.112280    0.242030   -0.4173340
+53067.00  -0.114044   0.244227  -0.4172256   -0.113870    0.244170   -0.4172170
+53068.00  -0.115346   0.246409  -0.4171978   -0.115450    0.246340   -0.4171940
+53069.00  -0.117613   0.248880  -0.4173544   -0.117240    0.248810   -0.4173440
+53070.00  -0.118972   0.251544  -0.4177290   -0.118920    0.251420   -0.4177250
+53071.00  -0.120333   0.254079  -0.4183482   -0.120260    0.254030   -0.4183150
+53072.00  -0.121611   0.256235  -0.4191872   -0.121520    0.256150   -0.4191520
+53073.00  -0.122119   0.258099  -0.4201285   -0.122270    0.258000   -0.4201170
+53074.00  -0.123051   0.260080  -0.4210229   -0.122910    0.260000   -0.4210240
+53075.00  -0.123777   0.262118  -0.4217468   -0.123690    0.262090   -0.4217560
+53076.00  -0.124455   0.264057  -0.4222153   -0.124390    0.264080   -0.4222000
+53077.00  -0.125205   0.266248  -0.4224569   -0.125150    0.266190   -0.4224630
+53078.00  -0.126024   0.268487  -0.4225151   -0.125960    0.268470   -0.4225910
+53079.00  -0.126916   0.270666  -0.4224659   -0.126830    0.270610   -0.4225390
+53080.00  -0.128146   0.272835  -0.4224683   -0.128020    0.272740   -0.4224570
+53081.00  -0.129176   0.274877  -0.4225792   -0.129130    0.274780   -0.4225720
+53082.00  -0.130041   0.277121  -0.4229180   -0.130030    0.277030   -0.4229130
+53083.00  -0.131319   0.279582  -0.4235105   -0.131010    0.279470   -0.4234930
+53084.00  -0.131686   0.281985  -0.4243745   -0.131650    0.281880   -0.4243930
+53085.00  -0.131870   0.284727  -0.4254624   -0.131780    0.284630   -0.4255200
+53086.00  -0.131970   0.287889  -0.4266041   -0.131850    0.287800   -0.4266280
+53087.00  -0.131763   0.291266  -0.4277337   -0.131810    0.291260   -0.4277180
+53088.00  -0.132120   0.294892  -0.4288225   -0.132020    0.294870   -0.4288330
+53089.00  -0.132879   0.298392  -0.4297907   -0.132830    0.298330   -0.4298080
+53090.00  -0.134157   0.301760  -0.4306100   -0.134010    0.301770   -0.4306060
+53091.00  -0.135580   0.305204  -0.4312566   -0.135510    0.305200   -0.4312590
+53092.00  -0.136937   0.308387  -0.4317381   -0.136880    0.308330   -0.4317360
+53093.00  -0.137964   0.311519  -0.4321208   -0.137840    0.311480   -0.4321140
+53094.00  -0.138655   0.314671  -0.4325104   -0.138680    0.314710   -0.4325020
+53095.00  -0.139535   0.317771  -0.4329854   -0.139440    0.317740   -0.4329880
+53096.00  -0.140236   0.320643  -0.4336280   -0.140150    0.320590   -0.4336250
+53097.00  -0.140804   0.323381  -0.4344862   -0.140650    0.323290   -0.4344790
+53098.00  -0.140861   0.326053  -0.4355582   -0.140730    0.325970   -0.4355490
+53099.00  -0.140991   0.328659  -0.4368895   -0.140850    0.328620   -0.4368240
+53100.00  -0.141196   0.331012  -0.4384145   -0.141110    0.330940   -0.4383520
+53101.00  -0.140720   0.333333  -0.4399631   -0.140720    0.333270   -0.4399620
+53102.00  -0.140158   0.336193  -0.4414204   -0.140070    0.336140   -0.4414210
+53103.00  -0.139965   0.339311  -0.4426320   -0.139870    0.339300   -0.4426250
+53104.00  -0.139741   0.342543  -0.4435244   -0.139720    0.342460   -0.4435230
+53105.00  -0.139228   0.345814  -0.4441924   -0.139160    0.345730   -0.4441910
+53106.00  -0.138782   0.349171  -0.4446457   -0.138700    0.349120   -0.4446890
+53107.00  -0.138908   0.352344  -0.4449674   -0.138820    0.352320   -0.4450120
+53108.00  -0.139092   0.355216  -0.4453570   -0.139050    0.355180   -0.4453520
+53109.00  -0.139133   0.358031  -0.4459426   -0.139020    0.357970   -0.4459340
+53110.00  -0.138562   0.360795  -0.4467443   -0.138500    0.360750   -0.4467570
+53111.00  -0.137666   0.363707  -0.4476965   -0.137280    0.363660   -0.4476920
+53112.00  -0.135942   0.367067  -0.4487434   -0.135880    0.367000   -0.4487340
+53113.00  -0.134730   0.370449  -0.4497836   -0.134630    0.370400   -0.4498170
+53114.00  -0.133542   0.373800  -0.4507463   -0.133420    0.373690   -0.4507790
+53115.00  -0.132548   0.377244  -0.4515631   -0.132720    0.377200   -0.4515530
+53116.00  -0.133007   0.380339  -0.4521528   -0.132860    0.380270   -0.4521440
+53117.00  -0.133116   0.382851  -0.4525157   -0.133030    0.382740   -0.4525130
+53118.00  -0.132946   0.385170  -0.4526433   -0.132900    0.385140   -0.4526290
+53119.00  -0.132752   0.387312  -0.4525541   -0.132680    0.387240   -0.4525500
+53120.00  -0.132086   0.389109  -0.4523613   -0.132000    0.389000   -0.4523700
+53121.00  -0.130584   0.390902  -0.4521309   -0.130460    0.390820   -0.4521400
+53122.00  -0.128369   0.392900  -0.4519811   -0.128460    0.392810   -0.4519700
+53123.00  -0.126762   0.395229  -0.4519888   -0.126640    0.395120   -0.4519830
+53124.00  -0.125075   0.398008  -0.4522232   -0.125000    0.397910   -0.4522110
+53125.00  -0.123708   0.401091  -0.4526894   -0.123340    0.401040   -0.4526730
+53126.00  -0.121495   0.404263  -0.4534530   -0.121380    0.404210   -0.4534520
+53127.00  -0.119760   0.407435  -0.4544589   -0.119660    0.407360   -0.4544520
+53128.00  -0.118501   0.410525  -0.4555827   -0.118390    0.410450   -0.4555700
+53129.00  -0.117058   0.413529  -0.4567177   -0.117060    0.413470   -0.4567090
+53130.00  -0.115921   0.416451  -0.4577698   -0.115790    0.416380   -0.4577810
+53131.00  -0.115001   0.419270  -0.4586211   -0.114930    0.419100   -0.4586060
+53132.00  -0.113993   0.421565  -0.4592557   -0.113920    0.421550   -0.4592340
+53133.00  -0.112544   0.423904  -0.4596957   -0.112480    0.423880   -0.4597010
+53134.00  -0.111161   0.426258  -0.4600231   -0.111080    0.426160   -0.4600310
+53135.00  -0.109683   0.428816  -0.4603919   -0.109620    0.428730   -0.4603710
+53136.00  -0.107972   0.431679  -0.4608945   -0.107920    0.431630   -0.4608740
+53137.00  -0.106068   0.434503  -0.4615534   -0.105990    0.434460   -0.4615630
+53138.00  -0.104092   0.437149  -0.4623758   -0.103920    0.437210   -0.4623610
+53139.00  -0.102428   0.440023  -0.4633390   -0.102230    0.439990   -0.4633250
+53140.00  -0.101278   0.442385  -0.4643792   -0.101230    0.442280   -0.4643800
+53141.00  -0.100665   0.444173  -0.4654198   -0.100590    0.444050   -0.4654470
+53142.00  -0.100219   0.445640  -0.4663589   -0.100120    0.445550   -0.4663810
+53143.00  -0.099649   0.446857  -0.4671628   -0.099630    0.446880   -0.4671500
+53144.00  -0.098890   0.448219  -0.4677645   -0.098880    0.448150   -0.4677680
+53145.00  -0.097898   0.449511  -0.4681790   -0.097620    0.449480   -0.4681660
+53146.00  -0.096338   0.451049  -0.4683682   -0.096230    0.450990   -0.4683520
+53147.00  -0.095219   0.452370  -0.4684372   -0.095120    0.452300   -0.4684340
+53148.00  -0.094280   0.453624  -0.4684003   -0.094210    0.453520   -0.4684300
+53149.00  -0.093254   0.455066  -0.4682929   -0.093170    0.454970   -0.4683400
+53150.00  -0.091741   0.456647  -0.4682759   -0.091820    0.456610   -0.4682760
+53151.00  -0.090950   0.458370  -0.4683178   -0.090800    0.458290   -0.4683110
+53152.00  -0.090152   0.459921  -0.4684932   -0.090100    0.459820   -0.4684840
+53153.00  -0.089523   0.461519  -0.4688633   -0.089290    0.461430   -0.4688530
+53154.00  -0.088312   0.463088  -0.4693417   -0.088260    0.463050   -0.4693440
+53155.00  -0.087161   0.464435  -0.4698880   -0.087050    0.464430   -0.4699190
+53156.00  -0.085650   0.465701  -0.4704246   -0.085550    0.465620   -0.4704730
+53157.00  -0.083868   0.467218  -0.4708810   -0.083740    0.467120   -0.4709090
+53158.00  -0.081799   0.468949  -0.4711648   -0.081900    0.468990   -0.4711550
+53159.00  -0.079985   0.470735  -0.4711849   -0.079860    0.470660   -0.4711760
+53160.00  -0.077621   0.472213  -0.4710285   -0.077750    0.472270   -0.4710280
+53161.00  -0.075857   0.473775  -0.4707305   -0.075560    0.473570   -0.4707200
+53162.00  -0.074535   0.475438  -0.4704523   -0.074410    0.475460   -0.4705960
+53163.00  -0.073383   0.476969  -0.4703119   -0.073240    0.476850   -0.4704280
+53164.00  -0.071795   0.478307  -0.4703860   -0.071780    0.478280   -0.4703790
+53165.00  -0.070038   0.479938  -0.4706078   -0.069820    0.479820   -0.4706000
+53166.00  -0.068404   0.481561  -0.4709961   -0.068360    0.481590   -0.4710010
+53167.00  -0.066910   0.483048  -0.4714383   -0.066580    0.482870   -0.4714330
+53168.00  -0.064939   0.484485  -0.4719111   -0.064920    0.484510   -0.4719020
+53169.00  -0.062810   0.485931  -0.4723223   -0.062670    0.485760   -0.4723950
+53170.00  -0.060442   0.487285  -0.4725840   -0.060290    0.487340   -0.4726390
+53171.00  -0.057892   0.488548  -0.4726368   -0.057860    0.488480   -0.4726280
+53172.00  -0.054948   0.489985  -0.4724290   -0.054740    0.489970   -0.4724330
+53173.00  -0.051504   0.491577  -0.4720163   -0.051580    0.491350   -0.4720090
+53174.00  -0.048345   0.493373  -0.4714789   -0.048070    0.493340   -0.4714770
+53175.00  -0.045157   0.495352  -0.4708713   -0.045250    0.495410   -0.4708420
+53176.00  -0.041914   0.497014  -0.4703102   -0.041700    0.496900   -0.4702260
+53177.00  -0.038593   0.498387  -0.4697864   -0.038620    0.498330   -0.4697110
+53178.00  -0.035032   0.499688  -0.4693800   -0.035040    0.499680   -0.4693630
+53179.00  -0.031929   0.500978  -0.4691390   -0.031760    0.500860   -0.4691500
+53180.00  -0.028606   0.502081  -0.4690429   -0.028540    0.502170   -0.4690560
+53181.00  -0.025186   0.503216  -0.4691310   -0.024900    0.503120   -0.4691160
+53182.00  -0.022010   0.504758  -0.4693568   -0.022050    0.504870   -0.4693900
+53183.00  -0.019196   0.506084  -0.4695599   -0.019040    0.505910   -0.4695710
+53184.00  -0.016274   0.507102  -0.4696971   -0.016150    0.507110   -0.4696610
+53185.00  -0.013099   0.507971  -0.4696824   -0.013090    0.507900   -0.4696690
+53186.00  -0.010373   0.509063  -0.4694842   -0.010180    0.508950   -0.4694860
+53187.00  -0.007578   0.510144  -0.4690131   -0.007570    0.510130   -0.4689890
+53188.00  -0.004596   0.510923  -0.4683883   -0.004540    0.510830   -0.4683790
+53189.00  -0.001923   0.511665  -0.4676178   -0.001750    0.511710   -0.4676050
+53190.00   0.000402   0.512395  -0.4668923    0.000410    0.512320   -0.4671130
+53191.00   0.002674   0.513104  -0.4663701    0.002740    0.512910   -0.4665540
+53192.00   0.005046   0.513765  -0.4660865    0.005140    0.513650   -0.4661270
+53193.00   0.007429   0.514409  -0.4659617    0.007380    0.514200   -0.4659470
+53194.00   0.009614   0.514767  -0.4658791    0.009680    0.514750   -0.4659020
+53195.00   0.012046   0.515222  -0.4657610    0.012350    0.515160   -0.4657470
+53196.00   0.015285   0.516147  -0.4655392    0.015460    0.516110   -0.4655440
+53197.00   0.018237   0.517026  -0.4651595    0.018290    0.517010   -0.4651960
+53198.00   0.021368   0.517832  -0.4646157    0.021370    0.517650   -0.4646290
+53199.00   0.024098   0.518753  -0.4638864    0.024340    0.518770   -0.4638740
+53200.00   0.026259   0.519371  -0.4629584    0.026250    0.519040   -0.4629580
+53201.00   0.028136   0.519908  -0.4619342    0.028290    0.519920   -0.4619040
+53202.00   0.030201   0.520349  -0.4608848    0.030460    0.520300   -0.4608800
+53203.00   0.032695   0.520696  -0.4598724    0.032540    0.520650   -0.4598740
+53204.00   0.035675   0.520838  -0.4589813    0.035970    0.520750   -0.4591510
+53205.00   0.038656   0.520772  -0.4582448    0.038570    0.520680   -0.4583560
+53206.00   0.041511   0.520561  -0.4576830    0.041520    0.520480   -0.4576720
+53207.00   0.043937   0.520226  -0.4573298    0.044090    0.520190   -0.4573140
+53208.00   0.046947   0.519842  -0.4572036    0.046900    0.519710   -0.4572010
+53209.00   0.049938   0.519578  -0.4572236    0.050240    0.519410   -0.4572220
+53210.00   0.053027   0.519249  -0.4573103    0.053030    0.519170   -0.4573080
+53211.00   0.056044   0.519068  -0.4573685    0.056170    0.518960   -0.4575160
+53212.00   0.059036   0.519100  -0.4572955    0.059160    0.518990   -0.4573630
+53213.00   0.061402   0.519126  -0.4570073    0.061620    0.519010   -0.4569860
+53214.00   0.063961   0.518761  -0.4564881    0.064010    0.518560   -0.4565000
+53215.00   0.066425   0.518316  -0.4557866    0.066520    0.518320   -0.4557640
+53216.00   0.068292   0.517610  -0.4550391    0.068510    0.517600   -0.4550260
+53217.00   0.070361   0.516953  -0.4543881    0.070360    0.516990   -0.4544200
+53218.00   0.072781   0.516279  -0.4539370    0.073010    0.516150   -0.4539160
+53219.00   0.075509   0.515709  -0.4537905    0.075510    0.515600   -0.4537780
+53220.00   0.078330   0.515356  -0.4539134    0.078360    0.515260   -0.4539070
+53221.00   0.081069   0.515218  -0.4541631    0.081140    0.515250   -0.4541560
+53222.00   0.083899   0.515111  -0.4544162    0.083910    0.514940   -0.4544350
+53223.00   0.086284   0.514958  -0.4545697    0.086610    0.514910   -0.4545740
+53224.00   0.088898   0.514767  -0.4546248    0.088870    0.514780   -0.4546190
+53225.00   0.091700   0.514255  -0.4545303    0.091820    0.514020   -0.4545550
+53226.00   0.094712   0.513742  -0.4542631    0.094820    0.513720   -0.4543150
+53227.00   0.097298   0.513313  -0.4538315    0.097430    0.513170   -0.4538400
+53228.00   0.099778   0.512915  -0.4532408    0.099730    0.512940   -0.4532180
+53229.00   0.102675   0.512908  -0.4526319    0.102930    0.512670   -0.4526570
+53230.00   0.105710   0.513250  -0.4519755    0.105680    0.513270   -0.4519510
+53231.00   0.108724   0.513582  -0.4514079    0.109030    0.513470   -0.4514100
+53232.00   0.111598   0.513717  -0.4509577    0.111430    0.513730   -0.4509560
+53233.00   0.114518   0.513528  -0.4507020    0.114790    0.513410   -0.4506920
+53234.00   0.117334   0.513095  -0.4506897    0.117320    0.513010   -0.4506820
+53235.00   0.119676   0.512409  -0.4509190    0.119780    0.512270   -0.4509170
+53236.00   0.121944   0.511753  -0.4513356    0.122060    0.511690   -0.4513250
+53237.00   0.124050   0.511179  -0.4518695    0.124400    0.511110   -0.4518640
+53238.00   0.126390   0.510409  -0.4524144    0.126480    0.510290   -0.4523980
+53239.00   0.128311   0.509358  -0.4528330    0.128420    0.509320   -0.4527810
+53240.00   0.130122   0.507982  -0.4530276    0.130190    0.507920   -0.4529650
+53241.00   0.132321   0.506426  -0.4529950    0.132450    0.506280   -0.4529790
+53242.00   0.134332   0.505013  -0.4528267    0.134230    0.504940   -0.4528310
+53243.00   0.136126   0.503770  -0.4524803    0.136480    0.503780   -0.4525190
+53244.00   0.138481   0.502491  -0.4521246    0.138370    0.502450   -0.4521300
+53245.00   0.141011   0.501310  -0.4518504    0.141370    0.501310   -0.4518260
+53246.00   0.143747   0.500062  -0.4518326    0.143630    0.500000   -0.4519000
+53247.00   0.146762   0.498775  -0.4520936    0.146990    0.498580   -0.4519870
+53248.00   0.149575   0.497404  -0.4525890    0.149620    0.497350   -0.4525720
+53249.00   0.152186   0.495783  -0.4531724    0.152260    0.495710   -0.4531700
+53250.00   0.154805   0.494025  -0.4537231    0.154950    0.493790   -0.4537380
+53251.00   0.156751   0.492063  -0.4540991    0.157100    0.492160   -0.4540690
+53252.00   0.159306   0.489843  -0.4542873    0.159290    0.489530   -0.4543050
+53253.00   0.161596   0.487414  -0.4542082    0.161670    0.487550   -0.4542020
+53254.00   0.163658   0.484943  -0.4538241    0.163920    0.484750   -0.4538370
+53255.00   0.165355   0.482747  -0.4532046    0.165290    0.482670   -0.4531710
+53256.00   0.167626   0.480880  -0.4524372    0.167670    0.480770   -0.4524300
+53257.00   0.169427   0.478979  -0.4516332    0.169340    0.478900   -0.4516360
+53258.00   0.171745   0.477178  -0.4509009    0.171940    0.476990   -0.4508980
+53259.00   0.173940   0.475321  -0.4503629    0.173870    0.475360   -0.4503450
+53260.00   0.176082   0.473181  -0.4500928    0.176360    0.473000   -0.4501280
+53261.00   0.178039   0.470868  -0.4501143    0.177940    0.470780   -0.4500520
+53262.00   0.179683   0.468549  -0.4504353    0.179830    0.468440   -0.4504170
+53263.00   0.180952   0.466219  -0.4509843    0.181080    0.466080   -0.4509920
+53264.00   0.182269   0.463884  -0.4516538    0.182210    0.463820   -0.4516470
+53265.00   0.183470   0.461490  -0.4523111    0.183810    0.461380   -0.4522950
+53266.00   0.185044   0.459163  -0.4528708    0.184930    0.459040   -0.4528940
+53267.00   0.186416   0.456911  -0.4531693    0.186690    0.456860   -0.4531750
+53268.00   0.187895   0.454563  -0.4532092    0.187940    0.454470   -0.4531980
+53269.00   0.189810   0.452079  -0.4530455    0.189840    0.452090   -0.4530270
+53270.00   0.191609   0.449970  -0.4527551    0.191680    0.449870   -0.4527690
+53271.00   0.193437   0.448135  -0.4524592    0.193570    0.448000   -0.4524880
+53272.00   0.194889   0.446565  -0.4522814    0.194980    0.446500   -0.4522670
+53273.00   0.195547   0.444853  -0.4523419    0.195610    0.444780   -0.4523490
+53274.00   0.195827   0.442870  -0.4526597    0.196000    0.442720   -0.4526700
+53275.00   0.196285   0.440805  -0.4531943    0.196280    0.440770   -0.4531930
+53276.00   0.197100   0.438643  -0.4539437    0.197200    0.438530   -0.4539310
+53277.00   0.197791   0.436437  -0.4547530    0.197950    0.436360   -0.4547580
+53278.00   0.198126   0.434025  -0.4554428    0.198130    0.433980   -0.4554360
+53279.00   0.198194   0.431641  -0.4559734    0.198510    0.431510   -0.4559600
+53280.00   0.198981   0.429193  -0.4563607    0.199100    0.429100   -0.4563610
+53281.00   0.199852   0.426681  -0.4565717    0.199940    0.426610   -0.4565630
+53282.00   0.200636   0.424209  -0.4566457    0.200730    0.424020   -0.4566420
+53283.00   0.201335   0.421828  -0.4566609    0.201340    0.421850   -0.4566540
+53284.00   0.201842   0.419341  -0.4566663    0.202020    0.419160   -0.4566540
+53285.00   0.202607   0.416861  -0.4567542    0.202660    0.416750   -0.4567840
+53286.00   0.203214   0.414366  -0.4569443    0.203200    0.414300   -0.4569290
+53287.00   0.203452   0.412140  -0.4571469    0.203540    0.411920   -0.4571240
+53288.00   0.203421   0.409992  -0.4575931    0.203630    0.410140   -0.4575590
+53289.00   0.203230   0.407653  -0.4582463    0.203200    0.407350   -0.4582150
+53290.00   0.203306   0.405183  -0.4591080    0.203500    0.405210   -0.4590980
+53291.00   0.203570   0.402676  -0.4601640    0.203520    0.402510   -0.4601650
+53292.00   0.203797   0.399898  -0.4612971    0.203970    0.399850   -0.4612710
+53293.00   0.204353   0.397057  -0.4623647    0.204570    0.396910   -0.4623530
+53294.00   0.205297   0.394763  -0.4633118    0.205350    0.394800   -0.4633100
+53295.00   0.205760   0.392592  -0.4639139    0.205870    0.392450   -0.4639360
+53296.00   0.205980   0.390523  -0.4641872    0.206030    0.390350   -0.4641390
+53297.00   0.205961   0.388589  -0.4642472    0.206130    0.388570   -0.4642380
+53298.00   0.205903   0.386513  -0.4642229    0.205820    0.386210   -0.4642030
+53299.00   0.205991   0.384516  -0.4642770    0.206220    0.384560   -0.4642620
+53300.00   0.205965   0.382563  -0.4645168    0.205990    0.382410   -0.4645050
+53301.00   0.205914   0.380751  -0.4649778    0.206050    0.380650   -0.4649800
+53302.00   0.205468   0.378916  -0.4656335    0.205590    0.378870   -0.4656320
+53303.00   0.205089   0.376842  -0.4664432    0.205090    0.376680   -0.4664280
+53304.00   0.205365   0.374567  -0.4673313    0.205460    0.374570   -0.4673210
+53305.00   0.205862   0.372441  -0.4682317    0.205960    0.372360   -0.4682280
+53306.00   0.206688   0.370131  -0.4690505    0.206610    0.370000   -0.4690430
+53307.00   0.207148   0.367884  -0.4697064    0.207220    0.367830   -0.4696950
+53308.00   0.207181   0.365758  -0.4701190    0.207370    0.365660   -0.4701200
+53309.00   0.207011   0.363608  -0.4702980    0.207040    0.363600   -0.4702780
+53310.00   0.207338   0.361428  -0.4703278    0.207210    0.361180   -0.4703530
+53311.00   0.208128   0.359183  -0.4703004    0.208320    0.359280   -0.4702890
+53312.00   0.208200   0.357165  -0.4702755    0.208080    0.356940   -0.4702790
+53313.00   0.208048   0.354846  -0.4703089    0.208280    0.354810   -0.4702980
+53314.00   0.207819   0.352108  -0.4704739    0.207730    0.352090   -0.4704670
+53315.00   0.207806   0.349429  -0.4708618    0.207990    0.349330   -0.4708600
+53316.00   0.207932   0.346855  -0.4715148    0.207990    0.346740   -0.4715210
+53317.00   0.208042   0.344640  -0.4724291    0.208130    0.344430   -0.4724170
+53318.00   0.207968   0.342708  -0.4735707    0.208170    0.342720   -0.4735620
+53319.00   0.208062   0.341039  -0.4748663    0.208020    0.340850   -0.4748570
+53320.00   0.207963   0.339038  -0.4761744    0.208180    0.339000   -0.4761690
+53321.00   0.207689   0.336617  -0.4773539    0.207760    0.336450   -0.4773500
+53322.00   0.208011   0.334322  -0.4783658    0.208150    0.334360   -0.4783540
+53323.00   0.207894   0.331940  -0.4790716    0.207950    0.331750   -0.4791100
+53324.00   0.207640   0.329327  -0.4795120    0.207730    0.329340   -0.4794100
+53325.00   0.207538   0.326628  -0.4798472    0.207490    0.326570   -0.4798260
+53326.00   0.207120   0.324354  -0.4801878    0.207330    0.324420   -0.4801980
+53327.00   0.206905   0.322071  -0.4806762    0.206980    0.321840   -0.4806780
+53328.00   0.206703   0.319823  -0.4813958    0.206820    0.319770   -0.4813830
+53329.00   0.206022   0.317745  -0.4823155    0.205970    0.317760   -0.4823050
+53330.00   0.205577   0.315592  -0.4834040    0.205910    0.315230   -0.4834030
+53331.00   0.205037   0.313371  -0.4845338    0.204990    0.313220   -0.4845320
+53332.00   0.204298   0.311115  -0.4856362    0.204340    0.311110   -0.4856390
+53333.00   0.203036   0.308969  -0.4866192    0.203200    0.308830   -0.4866210
+53334.00   0.201791   0.306736  -0.4873872    0.201810    0.306630   -0.4873620
+53335.00   0.200589   0.304321  -0.4879570    0.200720    0.304190   -0.4878990
+53336.00   0.199842   0.301959  -0.4882769    0.199830    0.301850   -0.4882490
+53337.00   0.199477   0.299851  -0.4883918    0.199660    0.299680   -0.4884350
+53338.00   0.199141   0.297878  -0.4884007    0.199190    0.297800   -0.4884790
+53339.00   0.198457   0.295825  -0.4883758    0.198590    0.295660   -0.4883780
+53340.00   0.197400   0.293534  -0.4883166    0.197420    0.293400   -0.4882780
+53341.00   0.195839   0.291194  -0.4884185    0.196040    0.291060   -0.4883320
+53342.00   0.194117   0.288731  -0.4887420    0.193950    0.288690   -0.4887380
+53343.00   0.192188   0.286454  -0.4891950    0.192260    0.286290   -0.4891760
+53344.00   0.190470   0.284460  -0.4898540    0.190570    0.284260   -0.4897990
+53345.00   0.188810   0.282492  -0.4907220    0.188940    0.282480   -0.4907450
+53346.00   0.187431   0.280376  -0.4917378    0.187320    0.280240   -0.4917210
+53347.00   0.186133   0.278540  -0.4928255    0.186440    0.278200   -0.4928240
+53348.00   0.184330   0.276772  -0.4938695    0.184120    0.276920   -0.4938780
+53349.00   0.181822   0.274584  -0.4946942    0.182340    0.274360   -0.4946950
+53350.00   0.180265   0.272589  -0.4953247    0.180270    0.272530   -0.4953200
+53351.00   0.178425   0.270639  -0.4957109    0.178570    0.270460   -0.4957160
+53352.00   0.176542   0.268620  -0.4959183    0.176580    0.268500   -0.4958990
+53353.00   0.174874   0.266464  -0.4961188    0.174870    0.266360   -0.4960900
+53354.00   0.173183   0.264323  -0.4964932    0.173230    0.264190   -0.4964970
+53355.00   0.171738   0.262289  -0.4970982    0.171920    0.262080   -0.4970940
+53356.00   0.170288   0.260433  -0.4979187    0.170300    0.260390   -0.4979080
+53357.00   0.169309   0.258834  -0.4989160    0.169390    0.258700   -0.4989080
+53358.00   0.168587   0.257388  -0.4999777    0.168680    0.257210   -0.4999790
+53359.00   0.167436   0.255847  -0.5010346    0.167570    0.255800   -0.5010160
+53360.00   0.165938   0.254031  -0.5019557    0.165950    0.253860   -0.5019450
+53361.00   0.164494   0.252104  -0.5025952    0.164650    0.252080   -0.5025630
+53362.00   0.162908   0.250099  -0.5030885    0.163020    0.250020   -0.5030760
+53363.00   0.161464   0.248564  -0.5033585    0.161650    0.248540   -0.5033550
+53364.00   0.159803   0.247327  -0.5034028    0.159800    0.247200   -0.5033760
+53365.00   0.158183   0.246477  -0.5032959    0.158420    0.246260   -0.5032740
+53366.00   0.156267   0.245476  -0.5031410    0.156380    0.245520   -0.5031320
+53367.00   0.154683   0.243879  -0.5030152    0.154540    0.243630   -0.5030010
+53368.00   0.152721   0.242334  -0.5029581    0.153040    0.242230   -0.5029620
+53369.00   0.151003   0.240748  -0.5029873    0.151160    0.240790   -0.5029790
+53370.00   0.149998   0.239256  -0.5031989    0.150140    0.239040   -0.5031920
+53371.00   0.149104   0.237996  -0.5036402    0.149240    0.237870   -0.5036520
+53372.00   0.148678   0.236862  -0.5042759    0.148770    0.236710   -0.5042860
+53373.00   0.148656   0.235806  -0.5050240    0.148790    0.235650   -0.5050180
+53374.00   0.148204   0.234687  -0.5058603    0.148460    0.234660   -0.5058480
+53375.00   0.147981   0.233358  -0.5066944    0.148050    0.233130   -0.5067430
+53376.00   0.146797   0.231734  -0.5074074    0.146950    0.231630   -0.5074040
+53377.00   0.145070   0.229723  -0.5078917    0.145140    0.229740   -0.5078740
+53378.00   0.143416   0.228078  -0.5081696    0.143520    0.227800   -0.5081770
+53379.00   0.141821   0.226807  -0.5083413    0.141930    0.226690   -0.5083080
+53380.00   0.139769   0.225653  -0.5085214    0.139960    0.225440   -0.5085820
+53381.00   0.137387   0.224471  -0.5087974    0.137390    0.224310   -0.5087860
+53382.00   0.135161   0.223188  -0.5093487    0.135370    0.223010   -0.5093340
+53383.00   0.133594   0.221990  -0.5101914    0.133610    0.221820   -0.5101990
+53384.00   0.131829   0.220955  -0.5112823    0.131980    0.220840   -0.5112860
+53385.00   0.130028   0.219792  -0.5124667    0.130070    0.219630   -0.5124470
+53386.00   0.128580   0.218550  -0.5136689    0.128720    0.218340   -0.5136980
+53387.00   0.127274   0.217360  -0.5147717    0.127440    0.217250   -0.5147280
+53388.00   0.125770   0.216373  -0.5157120    0.125840    0.216160   -0.5156800
+53389.00   0.124131   0.215757  -0.5164304    0.124160    0.215520   -0.5164150
+53390.00   0.121542   0.215118  -0.5169046    0.121650    0.215080   -0.5169020
+53391.00   0.119235   0.214095  -0.5172315    0.119310    0.213850   -0.5172320
+53392.00   0.117016   0.213241  -0.5174137    0.117180    0.213190   -0.5174180
+53393.00   0.114655   0.212420  -0.5174974    0.114640    0.212230   -0.5175070
+53394.00   0.112303   0.211497  -0.5175387    0.112480    0.211350   -0.5175350
+53395.00   0.110008   0.210563  -0.5175949    0.110060    0.210450   -0.5175790
+53396.00   0.107599   0.209727  -0.5177116    0.107690    0.209510   -0.5177280
+53397.00   0.105423   0.209179  -0.5179821    0.105520    0.209030   -0.5179890
+53398.00   0.102987   0.208872  -0.5184503    0.103190    0.208950   -0.5184380
+53399.00   0.100584   0.208896  -0.5190574    0.100770    0.208770   -0.5190740
+53400.00   0.097538   0.208712  -0.5197447    0.097550    0.208720   -0.5196790
+53401.00   0.094624   0.208515  -0.5204753    0.094690    0.208020   -0.5203660
+53402.00   0.091954   0.208506  -0.5212031    0.092070    0.208360   -0.5211880
+53403.00   0.089197   0.207774  -0.5218860    0.089220    0.207360   -0.5219040
+53404.00   0.086552   0.206870  -0.5224172    0.086670    0.206850   -0.5224260
+53405.00   0.083584   0.206141  -0.5227682    0.083920    0.206040   -0.5227550
+53406.00   0.081544   0.205868  -0.5229579    0.081570    0.205850   -0.5229780
+53407.00   0.078824   0.205838  -0.5231935    0.078920    0.205580   -0.5231160
+53408.00   0.076009   0.205788  -0.5235610    0.076100    0.205770   -0.5237140
+53409.00   0.073076   0.205521  -0.5240825    0.073150    0.205280   -0.5240730
+53410.00   0.069879   0.205014  -0.5249003    0.070030    0.205020   -0.5248950
+53411.00   0.066804   0.204321  -0.5260474    0.066860    0.204090   -0.5260380
+53412.00   0.064032   0.203564  -0.5274687    0.064120    0.203610   -0.5274600
+53413.00   0.061534   0.203227  -0.5290350    0.061670    0.203150   -0.5290210
+53414.00   0.059260   0.203103  -0.5305665    0.059180    0.202910   -0.5305800
+53415.00   0.057168   0.203188  -0.5319091    0.057480    0.203020   -0.5318810
+53416.00   0.054637   0.203354  -0.5329896    0.054560    0.203270   -0.5329820
+53417.00   0.051720   0.203158  -0.5337633    0.052100    0.202990   -0.5337520
+53418.00   0.048512   0.202982  -0.5342942    0.048250    0.202830   -0.5342930
+53419.00   0.045319   0.203117  -0.5347072    0.045910    0.203060   -0.5347030
+53420.00   0.042670   0.203752  -0.5351355    0.042380    0.203710   -0.5351220
+53421.00   0.040103   0.204527  -0.5356627    0.040510    0.204360   -0.5356850
+53422.00   0.037712   0.205249  -0.5363048    0.037570    0.205130   -0.5362980
+53423.00   0.035488   0.205829  -0.5371187    0.035710    0.205590   -0.5371080
+53424.00   0.033293   0.206106  -0.5381066    0.033200    0.206030   -0.5380940
+53425.00   0.031548   0.206231  -0.5393036    0.031760    0.205990   -0.5392940
+53426.00   0.030087   0.206590  -0.5407214    0.030330    0.206570   -0.5407130
+53427.00   0.029198   0.207544  -0.5423145    0.029430    0.207400   -0.5422950
+53428.00   0.027670   0.208625  -0.5440521    0.027720    0.208530   -0.5440730
+53429.00   0.025816   0.209840  -0.5457451    0.025890    0.209650   -0.5457110
+53430.00   0.024128   0.211329  -0.5473574    0.024110    0.211090   -0.5473360
+53431.00   0.021776   0.212859  -0.5488361    0.021880    0.212750   -0.5488330
+53432.00   0.019379   0.214130  -0.5501179    0.019440    0.213970   -0.5500970
+53433.00   0.016952   0.214886  -0.5512260    0.017160    0.214930   -0.5512200
+53434.00   0.015134   0.215849  -0.5522279    0.015180    0.215700   -0.5522110
+53435.00   0.012738   0.216531  -0.5531713    0.000000    0.000000    0.0000000
+53436.00   0.010557   0.217070  -0.5541355    0.000000    0.000000    0.0000000
+53437.00   0.008805   0.217764  -0.5552418    0.000000    0.000000    0.0000000
+53438.00   0.006758   0.218630  -0.5565094    0.000000    0.000000    0.0000000
+53439.00   0.004648   0.219470  -0.5578988    0.000000    0.000000    0.0000000
+53440.00   0.002114   0.220380  -0.5593918    0.000000    0.000000    0.0000000
+53441.00   0.000388   0.221223  -0.5609127    0.000000    0.000000    0.0000000
+53442.00  -0.001412   0.221965  -0.5622917    0.000000    0.000000    0.0000000
+53443.00  -0.002677   0.222527  -0.5634065    0.000000    0.000000    0.0000000
+53444.00  -0.003696   0.223202  -0.5641684    0.000000    0.000000    0.0000000
+53445.00  -0.005588   0.223371  -0.5645880    0.000000    0.000000    0.0000000
+53446.00  -0.006897   0.223602  -0.5647672    0.000000    0.000000    0.0000000
+53447.00  -0.008130   0.223964  -0.5648197    0.000000    0.000000    0.0000000
+53448.00  -0.009000   0.224769  -0.5648552    0.000000    0.000000    0.0000000
+53449.00  -0.009854   0.225788  -0.5649488    0.000000    0.000000    0.0000000
+53450.00  -0.010752   0.226865  -0.5651214    0.000000    0.000000    0.0000000
+53451.00  -0.011840   0.228178  -0.5654544    0.000000    0.000000    0.0000000
+53452.00  -0.013358   0.230112  -0.5660329    0.000000    0.000000    0.0000000
+53453.00  -0.015359   0.231733  -0.5668881    0.000000    0.000000    0.0000000
+53454.00  -0.016629   0.233036  -0.5679461    0.000000    0.000000    0.0000000
+53455.00  -0.017847   0.234493  -0.5691350    0.000000    0.000000    0.0000000
+53456.00  -0.019259   0.236179  -0.5703272    0.000000    0.000000    0.0000000
+53457.00  -0.020918   0.237826  -0.5714205    0.000000    0.000000    0.0000000
+53458.00  -0.022444   0.239266  -0.5723283    0.000000    0.000000    0.0000000
+53459.00  -0.024824   0.240180  -0.5729861    0.000000    0.000000    0.0000000
+53460.00  -0.026795   0.241255  -0.5733634    0.000000    0.000000    0.0000000
+53461.00  -0.028997   0.242470  -0.5735448    0.000000    0.000000    0.0000000
+53462.00  -0.030748   0.243908  -0.5736220    0.000000    0.000000    0.0000000
+53463.00  -0.032355   0.244908  -0.5738480    0.000000    0.000000    0.0000000
+53464.00  -0.033549   0.245573  -0.5743133    0.000000    0.000000    0.0000000
+53465.00  -0.034314   0.246009  -0.5750870    0.000000    0.000000    0.0000000
+53466.00  -0.035093   0.246522  -0.5760682    0.000000    0.000000    0.0000000
+53467.00  -0.035603   0.247955  -0.5772363    0.000000    0.000000    0.0000000
+53468.00  -0.036718   0.250078  -0.5785306    0.000000    0.000000    0.0000000
+53469.00  -0.038502   0.252218  -0.5797697    0.000000    0.000000    0.0000000
+53470.00  -0.040638   0.253880  -0.5808114    0.000000    0.000000    0.0000000
+53471.00  -0.042585   0.255276  -0.5815761    0.000000    0.000000    0.0000000
+53472.00  -0.044042   0.256696  -0.5820791    0.000000    0.000000    0.0000000
+53473.00  -0.045470   0.258542  -0.5823466    0.000000    0.000000    0.0000000
+53474.00  -0.046115   0.260362  -0.5825920    0.000000    0.000000    0.0000000
+53475.00  -0.046839   0.262307  -0.5828570    0.000000    0.000000    0.0000000
+53476.00  -0.047868   0.264720  -0.5831796    0.000000    0.000000    0.0000000
+53477.00  -0.049363   0.267063  -0.5836566    0.000000    0.000000    0.0000000
+53478.00  -0.050998   0.269275  -0.5842974    0.000000    0.000000    0.0000000
+53479.00  -0.052488   0.271589  -0.5851281    0.000000    0.000000    0.0000000
+53480.00  -0.053852   0.273906  -0.5861710    0.000000    0.000000    0.0000000
+53481.00  -0.054932   0.276138  -0.5873988    0.000000    0.000000    0.0000000
+53482.00  -0.055784   0.278356  -0.5887576    0.000000    0.000000    0.0000000
+53483.00  -0.056771   0.280694  -0.5901941    0.000000    0.000000    0.0000000
+53484.00  -0.057675   0.283026  -0.5916167    0.000000    0.000000    0.0000000
+53485.00  -0.058387   0.284998  -0.5929170    0.000000    0.000000    0.0000000
+53486.00  -0.058808   0.286671  -0.5940167    0.000000    0.000000    0.0000000
+53487.00  -0.058820   0.288330  -0.5948574    0.000000    0.000000    0.0000000
+53488.00  -0.058559   0.290118  -0.5954984    0.000000    0.000000    0.0000000
+53489.00  -0.058393   0.291917  -0.5960518    0.000000    0.000000    0.0000000
+53490.00  -0.058316   0.293666  -0.5966125    0.000000    0.000000    0.0000000
+53491.00  -0.058318   0.295380  -0.5973345    0.000000    0.000000    0.0000000
+53492.00  -0.058390   0.297070  -0.5983193    0.000000    0.000000    0.0000000
+53493.00  -0.058522   0.298743  -0.5995699    0.000000    0.000000    0.0000000
+53494.00  -0.058706   0.300405  -0.6009985    0.000000    0.000000    0.0000000
+53495.00  -0.058935   0.302062  -0.6024845    0.000000    0.000000    0.0000000
+53496.00  -0.059203   0.303716  -0.6039283    0.000000    0.000000    0.0000000
+53497.00  -0.059502   0.305370  -0.6052122    0.000000    0.000000    0.0000000
+53498.00  -0.059826   0.307027  -0.6062461    0.000000    0.000000    0.0000000
+53499.00  -0.060171   0.308686  -0.6069829    0.000000    0.000000    0.0000000
+53500.00  -0.060532   0.310349  -0.6074436    0.000000    0.000000    0.0000000
+53501.00  -0.060904   0.312017  -0.6076888    0.000000    0.000000    0.0000000
+53502.00  -0.061282   0.313689  -0.6077942    0.000000    0.000000    0.0000000
+53503.00  -0.061663   0.315365  -0.6078289    0.000000    0.000000    0.0000000
+53504.00  -0.062043   0.317046  -0.6078505    0.000000    0.000000    0.0000000
+53505.00  -0.062420   0.318731  -0.6079327    0.000000    0.000000    0.0000000
+53506.00  -0.062790   0.320420  -0.6081303    0.000000    0.000000    0.0000000
+53507.00  -0.063150   0.322112  -0.6084627    0.000000    0.000000    0.0000000
+53508.00  -0.063499   0.323806  -0.6089358    0.000000    0.000000    0.0000000
+53509.00  -0.063834   0.325504  -0.6095162    0.000000    0.000000    0.0000000
+53510.00  -0.064152   0.327203  -0.6101472    0.000000    0.000000    0.0000000
+53511.00  -0.064453   0.328905  -0.6107558    0.000000    0.000000    0.0000000
+53512.00  -0.064734   0.330607  -0.6112464    0.000000    0.000000    0.0000000
+53513.00  -0.064995   0.332310  -0.6115299    0.000000    0.000000    0.0000000
+53514.00  -0.065232   0.334014  -0.6115529    0.000000    0.000000    0.0000000
+53515.00  -0.065447   0.335717  -0.6113279    0.000000    0.000000    0.0000000
+53516.00  -0.065637   0.337420  -0.6109488    0.000000    0.000000    0.0000000
+53517.00  -0.065801   0.339122  -0.6105519    0.000000    0.000000    0.0000000
+53518.00  -0.065939   0.340822  -0.6102797    0.000000    0.000000    0.0000000
+53519.00  -0.066050   0.342522  -0.6102362    0.000000    0.000000    0.0000000
+53520.00  -0.066134   0.344219  -0.6104445    0.000000    0.000000    0.0000000
+53521.00  -0.066189   0.345913  -0.6108579    0.000000    0.000000    0.0000000
+53522.00  -0.066215   0.347605  -0.6113927    0.000000    0.000000    0.0000000
+53523.00  -0.066212   0.349281  -0.6119409    0.000000    0.000000    0.0000000
+53524.00  -0.066180   0.350968  -0.6123911    0.000000    0.000000    0.0000000
+53525.00  -0.066119   0.352651  -0.6126575    0.000000    0.000000    0.0000000
+53526.00  -0.066027   0.354330  -0.6126980    0.000000    0.000000    0.0000000
+53527.00  -0.065906   0.356004  -0.6125152    0.000000    0.000000    0.0000000
+53528.00  -0.065754   0.357673  -0.6121483    0.000000    0.000000    0.0000000
+53529.00  -0.065572   0.359337  -0.6116652    0.000000    0.000000    0.0000000
+53530.00  -0.065360   0.360996  -0.6111423    0.000000    0.000000    0.0000000
+53531.00  -0.065118   0.362649  -0.6106517    0.000000    0.000000    0.0000000
+53532.00  -0.064846   0.364296  -0.6102616    0.000000    0.000000    0.0000000
+53533.00  -0.064544   0.365936  -0.6100239    0.000000    0.000000    0.0000000
+53534.00  -0.064213   0.367570  -0.6099593    0.000000    0.000000    0.0000000
+53535.00  -0.063851   0.369197  -0.6100599    0.000000    0.000000    0.0000000
+53536.00  -0.063461   0.370817  -0.6102988    0.000000    0.000000    0.0000000
+53537.00  -0.063041   0.372430  -0.6106255    0.000000    0.000000    0.0000000
+53538.00  -0.062592   0.374034  -0.6109647    0.000000    0.000000    0.0000000
+53539.00  -0.062114   0.375631  -0.6112275    0.000000    0.000000    0.0000000
+53540.00  -0.061608   0.377220  -0.6113218    0.000000    0.000000    0.0000000
+53541.00  -0.061074   0.378800  -0.6111834    0.000000    0.000000    0.0000000
+53542.00  -0.060512   0.380371  -0.6108019    0.000000    0.000000    0.0000000
+53543.00  -0.059922   0.381934  -0.6102428    0.000000    0.000000    0.0000000
+53544.00  -0.059305   0.383487  -0.6096371    0.000000    0.000000    0.0000000
+53545.00  -0.058662   0.385030  -0.6091370    0.000000    0.000000    0.0000000
+53546.00  -0.057992   0.386564  -0.6088632    0.000000    0.000000    0.0000000
+53547.00  -0.057296   0.388087  -0.6088621    0.000000    0.000000    0.0000000
+53548.00  -0.056574   0.389600  -0.6090923    0.000000    0.000000    0.0000000
+53549.00  -0.055826   0.391103  -0.6094469    0.000000    0.000000    0.0000000
+53550.00  -0.055054   0.392595  -0.6097895    0.000000    0.000000    0.0000000
+53551.00  -0.054258   0.394076  -0.6099955    0.000000    0.000000    0.0000000
+53552.00  -0.053437   0.395545  -0.6099794    0.000000    0.000000    0.0000000
+53553.00  -0.052592   0.397002  -0.6097058    0.000000    0.000000    0.0000000
+53554.00  -0.051725   0.398448  -0.6091810    0.000000    0.000000    0.0000000
+53555.00  -0.050834   0.399882  -0.6084457    0.000000    0.000000    0.0000000
+53556.00  -0.049921   0.401303  -0.6075639    0.000000    0.000000    0.0000000
+53557.00  -0.048986   0.402712  -0.6066152    0.000000    0.000000    0.0000000
+53558.00  -0.048029   0.404108  -0.6056803    0.000000    0.000000    0.0000000
+53559.00  -0.047052   0.405491  -0.6048334    0.000000    0.000000    0.0000000
+53560.00  -0.046053   0.406860  -0.6041286    0.000000    0.000000    0.0000000
+53561.00  -0.045035   0.408216  -0.6035963    0.000000    0.000000    0.0000000
+53562.00  -0.043996   0.409558  -0.6032371    0.000000    0.000000    0.0000000
+53563.00  -0.042938   0.410886  -0.6030275    0.000000    0.000000    0.0000000
+53564.00  -0.041862   0.412200  -0.6029216    0.000000    0.000000    0.0000000
+53565.00  -0.040767   0.413499  -0.6028588    0.000000    0.000000    0.0000000
+53566.00  -0.039654   0.414784  -0.6027649    0.000000    0.000000    0.0000000
+53567.00  -0.038523   0.416054  -0.6025605    0.000000    0.000000    0.0000000
+53568.00  -0.037375   0.417308  -0.6021755    0.000000    0.000000    0.0000000
+53569.00  -0.036211   0.418547  -0.6015762    0.000000    0.000000    0.0000000
+53570.00  -0.035030   0.419770  -0.6007902    0.000000    0.000000    0.0000000
+53571.00  -0.033834   0.420977  -0.5999156    0.000000    0.000000    0.0000000
+53572.00  -0.032622   0.422169  -0.5991005    0.000000    0.000000    0.0000000
+53573.00  -0.031396   0.423344  -0.5984935    0.000000    0.000000    0.0000000
+53574.00  -0.030155   0.424502  -0.5981855    0.000000    0.000000    0.0000000
+53575.00  -0.028900   0.425644  -0.5981730    0.000000    0.000000    0.0000000
+53576.00  -0.027631   0.426768  -0.5983602    0.000000    0.000000    0.0000000
+53577.00  -0.026350   0.427876  -0.5985965    0.000000    0.000000    0.0000000
+53578.00  -0.025056   0.428966  -0.5987300    0.000000    0.000000    0.0000000
+53579.00  -0.023749   0.430039  -0.5986511    0.000000    0.000000    0.0000000
+53580.00  -0.022431   0.431093  -0.5983098    0.000000    0.000000    0.0000000
+53581.00  -0.021102   0.432130  -0.5977117    0.000000    0.000000    0.0000000
+53582.00  -0.019762   0.433149  -0.5969010    0.000000    0.000000    0.0000000
+53583.00  -0.018411   0.434149  -0.5959441    0.000000    0.000000    0.0000000
+53584.00  -0.017050   0.435131  -0.5949184    0.000000    0.000000    0.0000000
+53585.00  -0.015680   0.436094  -0.5939036    0.000000    0.000000    0.0000000
+53586.00  -0.014300   0.437038  -0.5929728    0.000000    0.000000    0.0000000
+53587.00  -0.012912   0.437962  -0.5921853    0.000000    0.000000    0.0000000
+53588.00  -0.011515   0.438868  -0.5915752    0.000000    0.000000    0.0000000
+53589.00  -0.010110   0.439754  -0.5911486    0.000000    0.000000    0.0000000
+53590.00  -0.008698   0.440620  -0.5908851    0.000000    0.000000    0.0000000
+53591.00  -0.007279   0.441467  -0.5907400    0.000000    0.000000    0.0000000
+53592.00  -0.005853   0.442293  -0.5906517    0.000000    0.000000    0.0000000
+53593.00  -0.004420   0.443100  -0.5905516    0.000000    0.000000    0.0000000
+53594.00  -0.003209   0.443886  -0.5904012    0.000000    0.000000    0.0000000
+53595.00  -0.001754   0.444651  -0.5901045    0.000000    0.000000    0.0000000
+53596.00  -0.000295   0.445396  -0.5896244    0.000000    0.000000    0.0000000
+53597.00   0.001169   0.446120  -0.5889693    0.000000    0.000000    0.0000000
+53598.00   0.002636   0.446824  -0.5882077    0.000000    0.000000    0.0000000
+53599.00   0.004108   0.447506  -0.5874650    0.000000    0.000000    0.0000000
+53600.00   0.005582   0.448167  -0.5868920    0.000000    0.000000    0.0000000
+53601.00   0.007059   0.448806  -0.5866144    0.000000    0.000000    0.0000000
+53602.00   0.008538   0.449425  -0.5866804    0.000000    0.000000    0.0000000
+53603.00   0.010020   0.450021  -0.5870364    0.000000    0.000000    0.0000000
+53604.00   0.011504   0.450596  -0.5875436    0.000000    0.000000    0.0000000
+53605.00   0.012989   0.451149  -0.5880275    0.000000    0.000000    0.0000000
+53606.00   0.014475   0.451680  -0.5883354    0.000000    0.000000    0.0000000
+53607.00   0.015961   0.452189  -0.5883779    0.000000    0.000000    0.0000000
+53608.00   0.017449   0.452676  -0.5881384    0.000000    0.000000    0.0000000
+53609.00   0.018936   0.453141  -0.5876592    0.000000    0.000000    0.0000000
+53610.00   0.020423   0.453583  -0.5870155    0.000000    0.000000    0.0000000
+53611.00   0.021910   0.454003  -0.5862936    0.000000    0.000000    0.0000000
+53612.00   0.023396   0.454401  -0.5855783    0.000000    0.000000    0.0000000
+53613.00   0.024881   0.454776  -0.5849460    0.000000    0.000000    0.0000000
+53614.00   0.026364   0.455128  -0.5844593    0.000000    0.000000    0.0000000
+53615.00   0.027845   0.455458  -0.5841604    0.000000    0.000000    0.0000000
+53616.00   0.029325   0.455765  -0.5840650    0.000000    0.000000    0.0000000
+53617.00   0.030802   0.456049  -0.5841590    0.000000    0.000000    0.0000000
+53618.00   0.032276   0.456310  -0.5844004    0.000000    0.000000    0.0000000
+53619.00   0.033748   0.456548  -0.5847248    0.000000    0.000000    0.0000000
+53620.00   0.035216   0.456764  -0.5850557    0.000000    0.000000    0.0000000
+53621.00   0.036681   0.456956  -0.5853163    0.000000    0.000000    0.0000000
+53622.00   0.038142   0.457126  -0.5854416    0.000000    0.000000    0.0000000
+53623.00   0.039599   0.457272  -0.5853929    0.000000    0.000000    0.0000000
+53624.00   0.041052   0.457396  -0.5851720    0.000000    0.000000    0.0000000
+53625.00   0.042500   0.457496  -0.5848305    0.000000    0.000000    0.0000000
+53626.00   0.043944   0.457573  -0.5844710    0.000000    0.000000    0.0000000
+53627.00   0.045382   0.457628  -0.5842291    0.000000    0.000000    0.0000000
+53628.00   0.046815   0.457659  -0.5842381    0.000000    0.000000    0.0000000
+53629.00   0.048243   0.457668  -0.5845824    0.000000    0.000000    0.0000000
+53630.00   0.049664   0.457653  -0.5852609    0.000000    0.000000    0.0000000
+53631.00   0.051080   0.457616  -0.5861783    0.000000    0.000000    0.0000000
+53632.00   0.052489   0.457555  -0.5871727    0.000000    0.000000    0.0000000
+53633.00   0.053892   0.457472  -0.5880692    0.000000    0.000000    0.0000000
+53634.00   0.055288   0.457366  -0.5887343    0.000000    0.000000    0.0000000
+53635.00   0.056677   0.457237  -0.5891083    0.000000    0.000000    0.0000000
+53636.00   0.058059   0.457086  -0.5892062    0.000000    0.000000    0.0000000
+53637.00   0.059434   0.456912  -0.5890965    0.000000    0.000000    0.0000000
+53638.00   0.060801   0.456715  -0.5888716    0.000000    0.000000    0.0000000
+53639.00   0.062160   0.456496  -0.5886259    0.000000    0.000000    0.0000000
+53640.00   0.063511   0.456255  -0.5884432    0.000000    0.000000    0.0000000
+53641.00   0.064854   0.455992  -0.5883910    0.000000    0.000000    0.0000000
+53642.00   0.066188   0.455706  -0.5885166    0.000000    0.000000    0.0000000
+53643.00   0.067514   0.455398  -0.5888431    0.000000    0.000000    0.0000000
+53644.00   0.068832   0.455069  -0.5893658    0.000000    0.000000    0.0000000
+53645.00   0.070140   0.454717  -0.5900518    0.000000    0.000000    0.0000000
+53646.00   0.071439   0.454344  -0.5908420    0.000000    0.000000    0.0000000
+53647.00   0.072729   0.453949  -0.5916589    0.000000    0.000000    0.0000000
+53648.00   0.074009   0.453533  -0.5924178    0.000000    0.000000    0.0000000
+53649.00   0.075280   0.453096  -0.5930421    0.000000    0.000000    0.0000000
+53650.00   0.076540   0.452638  -0.5934813    0.000000    0.000000    0.0000000
+53651.00   0.077791   0.452159  -0.5937269    0.000000    0.000000    0.0000000
+53652.00   0.079032   0.451659  -0.5938215    0.000000    0.000000    0.0000000
+53653.00   0.080262   0.451138  -0.5938556    0.000000    0.000000    0.0000000
+53654.00   0.081482   0.450597  -0.5939521    0.000000    0.000000    0.0000000
+53655.00   0.082692   0.450036  -0.5942384    0.000000    0.000000    0.0000000
+53656.00   0.083890   0.449455  -0.5948111    0.000000    0.000000    0.0000000
+53657.00   0.085078   0.448853  -0.5957022    0.000000    0.000000    0.0000000
+53658.00   0.086255   0.448233  -0.5968621    0.000000    0.000000    0.0000000
+53659.00   0.087420   0.447593  -0.5981670    0.000000    0.000000    0.0000000
+53660.00   0.088575   0.446933  -0.5994545    0.000000    0.000000    0.0000000
+53661.00   0.089717   0.446255  -0.6005727    0.000000    0.000000    0.0000000
+53662.00   0.090849   0.445558  -0.6014225    0.000000    0.000000    0.0000000
+53663.00   0.091968   0.444843  -0.6019788    0.000000    0.000000    0.0000000
+53664.00   0.093076   0.444109  -0.6022833    0.000000    0.000000    0.0000000
+53665.00   0.094172   0.443357  -0.6024214    0.000000    0.000000    0.0000000
+53666.00   0.095255   0.442587  -0.6024933    0.000000    0.000000    0.0000000
+53667.00   0.096327   0.441800  -0.6025936    0.000000    0.000000    0.0000000
+53668.00   0.097386   0.440996  -0.6027999    0.000000    0.000000    0.0000000
+53669.00   0.098433   0.440175  -0.6031678    0.000000    0.000000    0.0000000
+53670.00   0.099468   0.439336  -0.6037281    0.000000    0.000000    0.0000000
+53671.00   0.100489   0.438482  -0.6044850    0.000000    0.000000    0.0000000
+53672.00   0.101499   0.437611  -0.6054158    0.000000    0.000000    0.0000000
+53673.00   0.102495   0.436724  -0.6064715    0.000000    0.000000    0.0000000
+53674.00   0.103478   0.435822  -0.6075801    0.000000    0.000000    0.0000000
+53675.00   0.104449   0.434904  -0.6086532    0.000000    0.000000    0.0000000
+53676.00   0.105406   0.433972  -0.6095989    0.000000    0.000000    0.0000000
+53677.00   0.106350   0.433024  -0.6103430    0.000000    0.000000    0.0000000
+53678.00   0.107281   0.432062  -0.6108514    0.000000    0.000000    0.0000000
+53679.00   0.108223   0.431124  -0.6111462    0.000000    0.000000    0.0000000
+53680.00   0.109151   0.430171  -0.6113060    0.000000    0.000000    0.0000000
+53681.00   0.110066   0.429205  -0.6114479    0.000000    0.000000    0.0000000
+53682.00   0.110967   0.428226  -0.6116958    0.000000    0.000000    0.0000000
+53683.00   0.111855   0.427233  -0.6121457    0.000000    0.000000    0.0000000
+53684.00   0.112729   0.426229  -0.6128371    0.000000    0.000000    0.0000000
+53685.00   0.113589   0.425212  -0.6137401    0.000000    0.000000    0.0000000
+53686.00   0.114435   0.424183  -0.6147618    0.000000    0.000000    0.0000000
+53687.00   0.115267   0.423142  -0.6157710    0.000000    0.000000    0.0000000
+53688.00   0.116085   0.422091  -0.6166338    0.000000    0.000000    0.0000000
+53689.00   0.116888   0.421028  -0.6172502    0.000000    0.000000    0.0000000
+53690.00   0.117678   0.419955  -0.6175782    0.000000    0.000000    0.0000000
+53691.00   0.118453   0.418871  -0.6176403    0.000000    0.000000    0.0000000
+53692.00   0.119213   0.417778  -0.6175113    0.000000    0.000000    0.0000000
+53693.00   0.119960   0.416675  -0.6172933    0.000000    0.000000    0.0000000
+53694.00   0.120692   0.415563  -0.6170891    0.000000    0.000000    0.0000000
+53695.00   0.121409   0.414442  -0.6169821    0.000000    0.000000    0.0000000
+53696.00   0.122111   0.413312  -0.6170263    0.000000    0.000000    0.0000000
+53697.00   0.122799   0.412174  -0.6172457    0.000000    0.000000    0.0000000
+53698.00   0.123472   0.411029  -0.6176422    0.000000    0.000000    0.0000000
+53699.00   0.124131   0.409875  -0.6182042    0.000000    0.000000    0.0000000
+53700.00   0.124774   0.408715  -0.6189069    0.000000    0.000000    0.0000000
+53701.00   0.125403   0.407548  -0.6197065    0.000000    0.000000    0.0000000
+53702.00   0.126016   0.406374  -0.6205275    0.000000    0.000000    0.0000000
+53703.00   0.126615   0.405194  -0.6212552    0.000000    0.000000    0.0000000
+53704.00   0.127199   0.404009  -0.6217797    0.000000    0.000000    0.0000000
+53705.00   0.127767   0.402818  -0.6220429    0.000000    0.000000    0.0000000
+53706.00   0.128320   0.401621  -0.6220504    0.000000    0.000000    0.0000000
+53707.00   0.128858   0.400420  -0.6218700    0.000000    0.000000    0.0000000
+53708.00   0.129381   0.399215  -0.6216063    0.000000    0.000000    0.0000000
+53709.00   0.129889   0.398005  -0.6214324    0.000000    0.000000    0.0000000
+53710.00   0.130381   0.396792  -0.6215055    0.000000    0.000000    0.0000000
+53711.00   0.130858   0.395575  -0.6218252    0.000000    0.000000    0.0000000
+53712.00   0.131319   0.394355  -0.6223291    0.000000    0.000000    0.0000000
+53713.00   0.131765   0.393132  -0.6230128    0.000000    0.000000    0.0000000
+53714.00   0.132196   0.391907  -0.6237385    0.000000    0.000000    0.0000000
+53715.00   0.132611   0.390680  -0.6243539    0.000000    0.000000    0.0000000
+53716.00   0.133011   0.389450  -0.6247772    0.000000    0.000000    0.0000000
+53717.00   0.133395   0.388220  -0.6249471    0.000000    0.000000    0.0000000
+53718.00   0.133763   0.386988  -0.6248605    0.000000    0.000000    0.0000000
+53719.00   0.134116   0.385755  -0.6245562    0.000000    0.000000    0.0000000
+53720.00   0.134453   0.384522  -0.6241433    0.000000    0.000000    0.0000000
+53721.00   0.134775   0.383288  -0.6237553    0.000000    0.000000    0.0000000
+53722.00   0.135081   0.382055  -0.6234058    0.000000    0.000000    0.0000000
+53723.00   0.135371   0.380822  -0.6231935    0.000000    0.000000    0.0000000
+53724.00   0.135646   0.379589  -0.6231388    0.000000    0.000000    0.0000000
+53725.00   0.135905   0.378358  -0.6232290    0.000000    0.000000    0.0000000
+53726.00   0.136148   0.377127  -0.6234184    0.000000    0.000000    0.0000000
+53727.00   0.136375   0.375898  -0.6236764    0.000000    0.000000    0.0000000
+53728.00   0.136587   0.374671  -0.6239384    0.000000    0.000000    0.0000000
+53729.00   0.136783   0.373447  -0.6241555    0.000000    0.000000    0.0000000
+53730.00   0.136964   0.372224  -0.6243078    0.000000    0.000000    0.0000000
+53731.00   0.137128   0.371004  -0.6243570    0.000000    0.000000    0.0000000
+53732.00   0.137277   0.369787  -0.6242861    0.000000    0.000000    0.0000000
+53733.00   0.137410   0.368573  -0.6241018    0.000000    0.000000    0.0000000
+53734.00   0.137528   0.367362  -0.6238675    0.000000    0.000000    0.0000000
+53735.00   0.137630   0.366156  -0.6236275    0.000000    0.000000    0.0000000
+53736.00   0.137716   0.364952  -0.6234604    0.000000    0.000000    0.0000000
+53737.00   0.137786   0.363754  -0.6235498    0.000000    0.000000    0.0000000
+53738.00   0.137841   0.362559  -0.6239378    0.000000    0.000000    0.0000000
+53739.00   0.137880   0.361369  -0.6246240    0.000000    0.000000    0.0000000
+53740.00   0.137904   0.360184  -0.6254857    0.000000    0.000000    0.0000000
+53741.00   0.137912   0.359004  -0.6263990    0.000000    0.000000    0.0000000
+53742.00   0.137904   0.357829  -0.6272249    0.000000    0.000000    0.0000000
+53743.00   0.137881   0.356660  -0.6279379    0.000000    0.000000    0.0000000
+53744.00   0.137842   0.355496  -0.6284401    0.000000    0.000000    0.0000000
+53745.00   0.137788   0.354338  -0.6287682    0.000000    0.000000    0.0000000
+53746.00   0.137719   0.353186  -0.6288322    0.000000    0.000000    0.0000000
+53747.00   0.137634   0.352041  -0.6287654    0.000000    0.000000    0.0000000
+53748.00   0.137534   0.350901  -0.6286803    0.000000    0.000000    0.0000000
+53749.00   0.137419   0.349769  -0.6286400    0.000000    0.000000    0.0000000
+53750.00   0.137288   0.348643  -0.6286794    0.000000    0.000000    0.0000000
+53751.00   0.137143   0.347524  -0.6288431    0.000000    0.000000    0.0000000
+53752.00   0.136982   0.346413  -0.6291656    0.000000    0.000000    0.0000000
+53753.00   0.136806   0.345308  -0.6296356    0.000000    0.000000    0.0000000
+53754.00   0.136615   0.344211  -0.6302305    0.000000    0.000000    0.0000000
+53755.00   0.136410   0.343122  -0.6309239    0.000000    0.000000    0.0000000
+53756.00   0.136189   0.342040  -0.6316796    0.000000    0.000000    0.0000000
+53757.00   0.135954   0.340966  -0.6323916    0.000000    0.000000    0.0000000
+53758.00   0.135704   0.339900  -0.6330571    0.000000    0.000000    0.0000000
+53759.00   0.135439   0.338842  -0.6335213    0.000000    0.000000    0.0000000
+53760.00   0.135160   0.337792  -0.6338344    0.000000    0.000000    0.0000000
+53761.00   0.134867   0.336751  -0.6339713    0.000000    0.000000    0.0000000
+53762.00   0.134559   0.335718  -0.6339889    0.000000    0.000000    0.0000000
+53763.00   0.134237   0.334693  -0.6339930    0.000000    0.000000    0.0000000
+53764.00   0.133901   0.333677  -0.6341178    0.000000    0.000000    0.0000000
+53765.00   0.133550   0.332670  -0.6345595    0.000000    0.000000    0.0000000
+53766.00   0.133186   0.331672  -0.6353636    0.000000    0.000000    0.0000000
+53767.00   0.132808   0.330682  -0.6364640    0.000000    0.000000    0.0000000
+53768.00   0.132416   0.329702  -0.6376642    0.000000    0.000000    0.0000000
+53769.00   0.132011   0.328730  -0.6387772    0.000000    0.000000    0.0000000
+53770.00   0.131592   0.327768  -0.6396991    0.000000    0.000000    0.0000000
+53771.00   0.131160   0.326814  -0.6403238    0.000000    0.000000    0.0000000
+53772.00   0.130714   0.325870  -0.6406376    0.000000    0.000000    0.0000000
+53773.00   0.130256   0.324935  -0.6407102    0.000000    0.000000    0.0000000
+53774.00   0.129784   0.324010  -0.6406236    0.000000    0.000000    0.0000000
+53775.00   0.129300   0.323094  -0.6404781    0.000000    0.000000    0.0000000
+53776.00   0.128802   0.322187  -0.6403549    0.000000    0.000000    0.0000000
+53777.00   0.128293   0.321290  -0.6403270    0.000000    0.000000    0.0000000
+53778.00   0.127770   0.320402  -0.6404516    0.000000    0.000000    0.0000000
+53779.00   0.127236   0.319524  -0.6407561    0.000000    0.000000    0.0000000
+53780.00   0.126689   0.318655  -0.6412093    0.000000    0.000000    0.0000000
+53781.00   0.126130   0.317795  -0.6418189    0.000000    0.000000    0.0000000
+53782.00   0.125560   0.316946  -0.6425537    0.000000    0.000000    0.0000000
+53783.00   0.124978   0.316106  -0.6433265    0.000000    0.000000    0.0000000
+53784.00   0.124384   0.315275  -0.6440384    0.000000    0.000000    0.0000000
+53785.00   0.123779   0.314454  -0.6446354    0.000000    0.000000    0.0000000
+53786.00   0.123163   0.313643  -0.6450543    0.000000    0.000000    0.0000000
+53787.00   0.122535   0.312841  -0.6452726    0.000000    0.000000    0.0000000
+53788.00   0.121897   0.312049  -0.6453503    0.000000    0.000000    0.0000000
+53789.00   0.121248   0.311267  -0.6453001    0.000000    0.000000    0.0000000
+53790.00   0.120589   0.310494  -0.6452021    0.000000    0.000000    0.0000000
+53791.00   0.119920   0.309730  -0.6452154    0.000000    0.000000    0.0000000
+53792.00   0.119240   0.308976  -0.6454803    0.000000    0.000000    0.0000000
+53793.00   0.118550   0.308232  -0.6461007    0.000000    0.000000    0.0000000
+53794.00   0.117851   0.307497  -0.6470960    0.000000    0.000000    0.0000000
+53795.00   0.117142   0.306772  -0.6483371    0.000000    0.000000    0.0000000
+53796.00   0.116423   0.306056  -0.6496191    0.000000    0.000000    0.0000000
+53797.00   0.115695   0.305350  -0.6507704    0.000000    0.000000    0.0000000
+53798.00   0.114959   0.304653  -0.6517162    0.000000    0.000000    0.0000000
+53799.00   0.114213   0.303966  -0.6524506    0.000000    0.000000    0.0000000
+53800.00   0.113459   0.303288  -0.6528723    0.000000    0.000000    0.0000000
+53801.00   0.112697   0.302619  -0.6531031    0.000000    0.000000    0.0000000
+53802.00   0.111926   0.301960  -0.6531597    0.000000    0.000000    0.0000000
+53803.00   0.111148   0.301310  -0.6531858    0.000000    0.000000    0.0000000
+53804.00   0.110362   0.300669  -0.6532919    0.000000    0.000000    0.0000000
+53805.00   0.109568   0.300038  -0.6536087    0.000000    0.000000    0.0000000
+53806.00   0.108766   0.299416  -0.6540974    0.000000    0.000000    0.0000000
+53807.00   0.107958   0.298803  -0.6547697    0.000000    0.000000    0.0000000
+53808.00   0.107143   0.298199  -0.6556600    0.000000    0.000000    0.0000000
+53809.00   0.106320   0.297604  -0.6566890    0.000000    0.000000    0.0000000
+53810.00   0.105492   0.297018  -0.6578024    0.000000    0.000000    0.0000000
+53811.00   0.104657   0.296441  -0.6589494    0.000000    0.000000    0.0000000
+53812.00   0.103816   0.295873  -0.6599791    0.000000    0.000000    0.0000000
+53813.00   0.102969   0.295314  -0.6608763    0.000000    0.000000    0.0000000
+53814.00   0.102117   0.294764  -0.6615944    0.000000    0.000000    0.0000000
+53815.00   0.101259   0.294223  -0.6621621    0.000000    0.000000    0.0000000
+53816.00   0.100396   0.293691  -0.6626469    0.000000    0.000000    0.0000000
+53817.00   0.099528   0.293168  -0.6630793    0.000000    0.000000    0.0000000
+53818.00   0.098655   0.292653  -0.6635451    0.000000    0.000000    0.0000000
+53819.00   0.097777   0.292147  -0.6641407    0.000000    0.000000    0.0000000
+53820.00   0.096895   0.291650  -0.6649557    0.000000    0.000000    0.0000000
+53821.00   0.096010   0.291162  -0.6660964    0.000000    0.000000    0.0000000
+53822.00   0.095120   0.290682  -0.6674847    0.000000    0.000000    0.0000000
+53823.00   0.094226   0.290211  -0.6690594    0.000000    0.000000    0.0000000
+53824.00   0.093329   0.289748  -0.6705733    0.000000    0.000000    0.0000000
+53825.00   0.092429   0.289294  -0.6718669    0.000000    0.000000    0.0000000
+53826.00   0.091526   0.288849  -0.6728283    0.000000    0.000000    0.0000000
+53827.00   0.090620   0.288412  -0.6734571    0.000000    0.000000    0.0000000
+53828.00   0.089711   0.287983  -0.6737357    0.000000    0.000000    0.0000000
+53829.00   0.088800   0.287563  -0.6738183    0.000000    0.000000    0.0000000
+53830.00   0.087887   0.287152  -0.6737775    0.000000    0.000000    0.0000000
+53831.00   0.086972   0.286749  -0.6737327    0.000000    0.000000    0.0000000
+53832.00   0.086055   0.286354  -0.6738200    0.000000    0.000000    0.0000000
+53833.00   0.085136   0.285968  -0.6741178    0.000000    0.000000    0.0000000
+53834.00   0.084216   0.285590  -0.6746694    0.000000    0.000000    0.0000000
+53835.00   0.083295   0.285220  -0.6753979    0.000000    0.000000    0.0000000
+53836.00   0.082373   0.284859  -0.6763123    0.000000    0.000000    0.0000000
+53837.00   0.081450   0.284507  -0.6773890    0.000000    0.000000    0.0000000
+53838.00   0.080527   0.284162  -0.6784682    0.000000    0.000000    0.0000000
+53839.00   0.079604   0.283826  -0.6795676    0.000000    0.000000    0.0000000
+53840.00   0.078680   0.283498  -0.6805828    0.000000    0.000000    0.0000000
+53841.00   0.077756   0.283179  -0.6814142    0.000000    0.000000    0.0000000
+53842.00   0.076833   0.282868  -0.6821005    0.000000    0.000000    0.0000000
+53843.00   0.075910   0.282565  -0.6826011    0.000000    0.000000    0.0000000
+53844.00   0.074987   0.282270  -0.6829987    0.000000    0.000000    0.0000000
+53845.00   0.074066   0.281984  -0.6833576    0.000000    0.000000    0.0000000
+53846.00   0.073145   0.281707  -0.6838171    0.000000    0.000000    0.0000000
+53847.00   0.072226   0.281437  -0.6844564    0.000000    0.000000    0.0000000
+53848.00   0.071307   0.281176  -0.6853447    0.000000    0.000000    0.0000000
+53849.00   0.070391   0.280924  -0.6864878    0.000000    0.000000    0.0000000
+53850.00   0.069476   0.280680  -0.6878293    0.000000    0.000000    0.0000000
+53851.00   0.068563   0.280444  -0.6892386    0.000000    0.000000    0.0000000
+53852.00   0.067651   0.280217  -0.6905256    0.000000    0.000000    0.0000000
+53853.00   0.066742   0.279998  -0.6915469    0.000000    0.000000    0.0000000
+53854.00   0.065836   0.279787  -0.6922592    0.000000    0.000000    0.0000000
+53855.00   0.064931   0.279586  -0.6926365    0.000000    0.000000    0.0000000
+53856.00   0.064030   0.279392  -0.6927103    0.000000    0.000000    0.0000000
+53857.00   0.063131   0.279208  -0.6925860    0.000000    0.000000    0.0000000
+53858.00   0.062235   0.279032  -0.6923453    0.000000    0.000000    0.0000000
+53859.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53860.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53861.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53862.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53863.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53864.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53865.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53866.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53867.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53868.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53869.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53870.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53871.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53872.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53873.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53874.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53875.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53876.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53877.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53878.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53879.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53880.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53881.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53882.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53883.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53884.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53885.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53886.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53887.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53888.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53889.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53890.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53891.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53892.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53893.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53894.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53895.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53896.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53897.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53898.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53899.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53900.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53901.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53902.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53903.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53904.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53905.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53906.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53907.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
+53908.00   0.000000   0.000000   0.0000000    0.000000    0.000000    0.0000000
Index: /tags/sj_tags/sj_root_20080929/psLib/share/finals_all.raw
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/finals_all.raw	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/finals_all.raw	(revision 22322)
@@ -0,0 +1,11994 @@
+73 1 2 41684.00 I   .120724  .009786   .137066  .015902  I  .8084319  .0002710  0.0000 0.1916  P    44.969     .500     2.839     .300   .143000   .137000   .8075000      .000      .000  
+73 1 3 41685.00 I   .118971  .011039   .135756  .013616  I  .8056304  .0002710  3.5563 0.1916  P    45.005     .500     2.762     .300   .141000   .134000   .8044000      .000      .000  
+73 1 4 41686.00 I   .117218  .011039   .134448  .013616  I  .8028036  .0002710  2.6599 0.1916  P    45.122     .500     2.851     .300   .139000   .131000   .8012000      .000      .000  
+73 1 5 41687.00 I   .115464  .009743   .133144  .013089  I  .7998870  .0002710  3.0344 0.1916  P    45.344     .500     3.020     .300   .137000   .128000   .7981000      .000      .000  
+73 1 6 41688.00 I   .113708  .011236   .131846  .009898  I  .7968285  .0002710  3.1276 0.1916  P    45.623     .500     3.115     .300   .136000   .126000   .7949000      .000      .000  
+73 1 7 41689.00 I   .111948  .012506   .130558  .009144  I  .7935963  .0002710  3.3271 0.1916  P    45.812     .500     3.086     .300   .134000   .123000   .7918000      .000      .000  
+73 1 8 41690.00 I   .110180  .012042   .129282  .008563  I  .7901851  .0002710  3.4940 0.1916  P    45.804     .500     3.014     .300   .132000   .122000   .7887000      .000      .000  
+73 1 9 41691.00 I   .108401  .011753   .128020  .007670  I  .7866194  .0002710  3.6275 0.1916  P    45.647     .500     2.993     .300   .130000   .120000   .7855000      .000      .000  
+73 110 41692.00 I   .106607  .011233   .126775  .007850  I  .7829530  .0002710  3.6924 0.2189  P    45.466     .500     3.049     .300   .128000   .119000   .7824000      .000      .000  
+73 111 41693.00 I   .104795  .010595   .125551  .006952  I  .7792628  .0003439  3.6729 0.2373  P    45.326     .500     3.164     .300   .126000   .117000   .7792000      .000      .000  
+73 112 41694.00 I   .102960  .012040   .124351  .004628  I  .7756359  .0003895  3.5674 0.2632  P    45.177     .500     3.328     .300   .124000   .116000   .7761000      .000      .000  
+73 113 41695.00 I   .101103  .017283   .123180  .014244  I  .7721500  .0003986  3.3960 0.2787  P    44.931     .500     3.504     .300   .122000   .114000   .7730000      .000      .000  
+73 114 41696.00 I   .099219  .017776   .122040  .016032  I  .7688519  .0003986  3.2007 0.2819  P    44.607     .500     3.585     .300   .120000   .113000   .7698000      .000      .000  
+73 115 41697.00 I   .097304  .020404   .120936  .014510  I  .7657376  .0003986  3.0383 0.2819  P    44.381     .500     3.456     .300   .118000   .111000   .7667000      .000      .000  
+73 116 41698.00 I   .095355  .022714   .119870  .016602  I  .7627466  .0003986  2.9620 0.2917  P    44.444     .500     3.159     .300   .116000   .110000   .7635000      .000      .000  
+73 117 41699.00 I   .093368  .024425   .118846  .015222  I  .7597749  .0004261  3.0019 0.3035  P    44.789     .500     2.911     .300   .114000   .108000   .7604000      .000      .000  
+73 118 41700.00 I   .091341  .027783   .117865  .017497  I  .7567072  .0004579  3.1486 0.3214  P    45.213     .500     2.902     .300   .112000   .106000   .7573000      .000      .000  
+73 119 41701.00 I   .089273  .027006   .116926  .020543  I  .7534582  .0004814  3.3540 0.3322  P    45.571     .500     3.082     .300   .110000   .105000   .7541000      .000      .000  
+73 120 41702.00 I   .087167  .026817   .116031  .017708  I  .7500021  .0004814  3.5509 0.3404  P    45.880     .500     3.232     .300   .107000   .103000   .7510000      .000      .000  
+73 121 41703.00 I   .085025  .023253   .115178  .016235  I  .7463792  .0004814  3.6794 0.3404  P    46.144     .500     3.212     .300   .105000   .102000   .7478000      .000      .000  
+73 122 41704.00 I   .082853  .021951   .114370  .018104  I  .7426762  .0004814  3.7090 0.2762  P    46.215     .500     3.080     .300   .103000   .100000   .7447000      .000      .000  
+73 123 41705.00 I   .080657  .020859   .113605  .016592  I  .7389931  .0002710  3.6428 0.2762  P    45.986     .500     2.953     .300   .101000   .098000   .7415000      .000      .000  
+73 124 41706.00 I   .078440  .017339   .112884  .018269  I  .7354139  .0002710  3.5067 0.1916  P    45.602     .500     2.872     .300   .099000   .097000   .7383000      .000      .000  
+73 125 41707.00 I   .076205  .015190   .112202  .018414  I  .7319919  .0002710  3.3338 0.1916  P    45.321     .500     2.830     .300   .096000   .095000   .7352000      .000      .000  
+73 126 41708.00 I   .073954  .010350   .111559  .013984  I  .7287489  .0002710  3.1530 0.1916  P    45.204     .500     2.845     .300   .094000   .094000   .7320000      .000      .000  
+73 127 41709.00 I   .071691  .011387   .110952  .013008  I  .7256811  .0002710  2.9868 0.1916  P    45.127     .500     2.911     .300   .092000   .092000   .7288000      .000      .000  
+73 128 41710.00 I   .069420  .013080   .110378  .013702  I  .7227643  .0002710  2.8538 0.1916  P    45.037     .500     2.932     .300   .090000   .091000   .7256000      .000      .000  
+73 129 41711.00 I   .067147  .011392   .109836  .011893  I  .7199568  .0002710  2.7710 0.1916  P    45.034     .500     2.807     .300   .088000   .089000   .7224000      .000      .000  
+73 130 41712.00 I   .064874  .008644   .109325  .012216  I  .7172012  .0002710  2.7515 0.1916  P    45.159     .500     2.575     .300   .086000   .088000   .7193000      .000      .000  
+73 131 41713.00 I   .062607  .007486   .108846  .010740  I  .7144307  .0002710  2.8012 0.1916  P    45.302     .500     2.396     .300   .084000   .087000   .7161000      .000      .000  
+73 2 1 41714.00 I   .060349  .008228   .108400  .005952  I  .7115769  .0002710  2.9167 0.1916  P    45.368     .500     2.374     .300   .082000   .086000   .7129000      .000      .000  
+73 2 2 41715.00 I   .058100  .008273   .107987  .006132  I  .7085797  .0002710  3.0849 0.1916  P    45.424     .500     2.457     .300   .079000   .084000   .7096000      .000      .000  
+73 2 3 41716.00 I   .055865  .005051   .107608  .004481  I  .7053972  .0002710  3.2828 0.1916  P    45.573     .500     2.528     .300   .077000   .083000   .7063000      .000      .000  
+73 2 4 41717.00 I   .053644  .005684   .107263  .004652  I  .7020146  .0002710  3.4793 0.1916  P    45.777     .500     2.546     .300   .075000   .082000   .7031000      .000      .000  
+73 2 5 41718.00 I   .051441  .006413   .106954  .005294  I  .6984511  .0002710  3.6385 0.1916  P    45.873     .500     2.551     .300   .073000   .080000   .6998000      .000      .000  
+73 2 6 41719.00 I   .049257  .009371   .106681  .005496  I  .6947614  .0002710  3.7265 0.1916  P    45.754     .500     2.591     .300   .071000   .079000   .6965000      .000      .000  
+73 2 7 41720.00 I   .047093  .010820   .106442  .005977  I  .6910290  .0002710  3.7218 0.1916  P    45.463     .500     2.668     .300   .069000   .080000   .6932000      .000      .000  
+73 2 8 41721.00 I   .044951  .009430   .106239  .005536  I  .6873489  .0002710  3.6237 0.1916  P    45.133     .500     2.756     .300   .067000   .081000   .6899000      .000      .000  
+73 2 9 41722.00 I   .042831  .009747   .106070  .005115  I  .6838052  .0002710  3.4547 0.1916  P    44.868     .500     2.829     .300   .066000   .082000   .6866000      .000      .000  
+73 210 41723.00 I   .040732  .009517   .105935  .009579  I  .6804490  .0002710  3.2572 0.1916  P    44.688     .500     2.858     .300   .064000   .083000   .6833000      .000      .000  
+73 211 41724.00 I   .038653  .010158   .105832  .010657  I  .6772827  .0002710  3.0839 0.1916  P    44.561     .500     2.796     .300   .062000   .085000   .6800000      .000      .000  
+73 212 41725.00 I   .036593  .008858   .105760  .010542  I  .6742568  .0002710  2.9836 0.2290  P    44.488     .500     2.592     .300   .060000   .086000   .6767000      .000      .000  
+73 213 41726.00 I   .034550  .005354   .105717  .011659  I  .6712808  .0003693  2.9872 0.2290  P    44.523     .500     2.256     .300   .058000   .087000   .6734000      .000      .000  
+73 214 41727.00 I   .032521  .014261   .105702  .013368  I  .6682469  .0003693  3.0973 0.2611  P    44.706     .500     1.910     .300   .057000   .088000   .6701000      .000      .000  
+73 215 41728.00 I   .030505  .016421   .105715  .015269  I  .6650607  .0003693  3.2841 0.2611  P    45.014     .500     1.740     .300   .055000   .089000   .6668000      .000      .000  
+73 216 41729.00 I   .028503  .014294   .105755  .013263  I  .6616712  .0003693  3.4936 0.2611  P    45.369     .500     1.834     .300   .053000   .090000   .6635000      .000      .000  
+73 217 41730.00 I   .026512  .015706   .105824  .011762  I  .6580860  .0003693  3.6657 0.2611  P    45.690     .500     2.090     .300   .051000   .091000   .6601000      .000      .000  
+73 218 41731.00 I   .024531  .020115   .105922  .013465  I  .6543676  .0003693  3.7543 0.2290  P    45.896     .500     2.292     .300   .049000   .092000   .6568000      .000      .000  
+73 219 41732.00 I   .022556  .023196   .106048  .014393  I  .6506111  .0002710  3.7418 0.2290  P    45.915     .500     2.292     .300   .047000   .093000   .6534000      .000      .000  
+73 220 41733.00 I   .020582  .020204   .106203  .013455  I  .6469136  .0002710  3.6403 0.1916  P    45.743     .500     2.108     .300   .045000   .094000   .6501000      .000      .000  
+73 221 41734.00 I   .018606  .017371   .106384  .011791  I  .6433499  .0002710  3.4804 0.1916  P    45.478     .500     1.871     .300   .044000   .095000   .6467000      .000      .000  
+73 222 41735.00 I   .016623  .015060   .106592  .011458  I  .6399606  .0002710  3.2971 0.1916  P    45.236     .500     1.717     .300   .042000   .096000   .6433000      .000      .000  
+73 223 41736.00 I   .014632  .017310   .106828  .013177  I  .6367535  .0002710  3.1203 0.1916  P    45.062     .500     1.699     .300   .040000   .097000   .6400000      .000      .000  
+73 224 41737.00 I   .012634  .015302   .107093  .012172  I  .6337102  .0002710  2.9728 0.1916  P    44.960     .500     1.744     .300   .038000   .098000   .6366000      .000      .000  
+73 225 41738.00 I   .010628  .004408   .107386  .009703  I  .6307923  .0002710  2.8720 0.1916  P    44.978     .500     1.702     .300   .036000   .099000   .6333000      .000      .000  
+73 226 41739.00 I   .008615  .006024   .107709  .009450  I  .6279462  .0002710  2.8311 0.1916  P    45.179     .500     1.485     .300   .034000   .100000   .6299000      .000      .000  
+73 227 41740.00 I   .006593  .006496   .108060  .009211  I  .6251074  .0002710  2.8582 0.1916  P    45.518     .500     1.180     .300   .032000   .101000   .6265000      .000      .000  
+73 228 41741.00 I   .004564  .012724   .108438  .011717  I  .6222069  .0002710  2.9540 0.1916  P    45.826     .500      .966     .300   .030000   .102000   .6232000      .000      .000  
+73 3 1 41742.00 I   .002526  .014671   .108842  .012126  I  .6191790  .0002710  3.1108 0.1916  P    45.957     .500      .925     .300   .028000   .102000   .6198000      .000      .000  
+73 3 2 41743.00 I   .000479  .013161   .109270  .011853  I  .6159706  .0002710  3.3115 0.1916  P    45.948     .500      .981     .300   .026000   .103000   .6165000      .000      .000  
+73 3 3 41744.00 I  -.001578  .014776   .109721  .012783  I  .6125505  .0002710  3.5288 0.1916  P    45.972     .500     1.036     .300   .024000   .104000   .6131000      .000      .000  
+73 3 4 41745.00 I  -.003643  .012867   .110195  .012062  I  .6089196  .0002710  3.7263 0.1916  P    46.134     .500     1.098     .300   .022000   .105000   .6097000      .000      .000  
+73 3 5 41746.00 I  -.005713  .013849   .110692  .013003  I  .6051180  .0002710  3.8634 0.1916  P    46.333     .500     1.230     .300   .020000   .106000   .6063000      .000      .000  
+73 3 6 41747.00 I  -.007786  .018598   .111214  .011377  I  .6012241  .0002710  3.9064 0.1916  P    46.349     .500     1.411     .300   .018000   .106000   .6030000      .000      .000  
+73 3 7 41748.00 I  -.009857  .016956   .111761  .008623  I  .5973410  .0002710  3.8419 0.1916  P    46.073     .500     1.530     .300   .016000   .107000   .5996000      .000      .000  
+73 3 8 41749.00 I  -.011922  .014730   .112336  .010019  I  .5935706  .0002710  3.6865 0.1916  P    45.630     .500     1.491     .300   .014000   .108000   .5962000      .000      .000  
+73 3 9 41750.00 I  -.013977  .016541   .112940  .009673  I  .5899833  .0002710  3.4851 0.1916  P    45.282     .500     1.305     .300   .012000   .109000   .5928000      .000      .000  
+73 310 41751.00 I  -.016019  .027845   .113573  .009673  I  .5865962  .0002710  3.2964 0.1916  P    45.200     .500     1.053     .300   .009000   .109000   .5893000      .000      .000  
+73 311 41752.00 I  -.018040  .032115   .114237  .009720  I  .5833682  .0002710  3.1747 0.4760  P    45.348     .500      .809     .300   .007000   .110000   .5859000      .000      .000  
+73 312 41753.00 I  -.020032  .035235   .114933  .012152  I  .5802133  .0009126  3.1535 0.4760  P    45.567     .500      .583     .300   .004000   .111000   .5824000      .000      .000  
+73 313 41754.00 I  -.021990  .037228   .115661  .014705  I  .5770261  .0009126  3.2375 0.6453  P    45.749     .500      .342     .300   .002000   .112000   .5790000      .000      .000  
+73 314 41755.00 I  -.023907  .032343   .116421  .014705  I  .5737111  .0009126  3.4032 0.6453  P    45.908     .500      .085     .300   .000000   .112000   .5755000      .000      .000  
+73 315 41756.00 I  -.025780  .037323   .117212  .015938  I  .5702072  .0009126  3.6066 0.6453  P    46.111     .500     -.101     .300  -.003000   .113000   .5720000      .000      .000  
+73 316 41757.00 I  -.027610  .033991   .118036  .018578  I  .5665027  .0009126  3.7956 0.6453  P    46.359     .500     -.097     .300  -.005000   .114000   .5685000      .000      .000  
+73 317 41758.00 I  -.029400  .027934   .118890  .018578  I  .5626361  .0009126  3.9242 0.4760  P    46.565     .500      .113     .300  -.008000   .114000   .5650000      .000      .000  
+73 318 41759.00 I  -.031155  .024244   .119773  .016207  I  .5586838  .0002710  3.9642 0.4760  P    46.646     .500      .377     .300  -.010000   .115000   .5615000      .000      .000  
+73 319 41760.00 I  -.032878  .012640   .120683  .016340  I  .5547386  .0002710  3.9115 0.1916  P    46.625     .500      .491     .300  -.012000   .116000   .5579000      .000      .000  
+73 320 41761.00 I  -.034573  .013809   .121619  .013403  I  .5508857  .0002710  3.7840 0.1916  P    46.599     .500      .371     .300  -.015000   .117000   .5544000      .000      .000  
+73 321 41762.00 I  -.036242  .015665   .122582  .013403  I  .5471853  .0002710  3.6125 0.1916  P    46.602     .500      .123     .300  -.017000   .118000   .5508000      .000      .000  
+73 322 41763.00 I  -.037885  .014168   .123571  .012131  I  .5436641  .0002710  3.4307 0.1916  P    46.573     .500     -.074     .300  -.020000   .119000   .5473000      .000      .000  
+73 323 41764.00 I  -.039504  .010960   .124588  .007128  I  .5403179  .0002710  3.2668 0.1916  P    46.479     .500     -.128     .300  -.022000   .120000   .5437000      .000      .000  
+73 324 41765.00 I  -.041099  .009572   .125635  .010385  I  .5371173  .0002710  3.1424 0.1916  P    46.407     .500     -.113     .300  -.024000   .120000   .5401000      .000      .000  
+73 325 41766.00 I  -.042673  .010897   .126712  .010546  I  .5340153  .0002710  3.0715 0.4939  P    46.482     .500     -.191     .300  -.027000   .121000   .5365000      .000      .000  
+73 326 41767.00 I  -.044229  .010997   .127821  .009188  I  .5309537  .0009499  3.0626 0.4939  P    46.738     .500     -.435     .300  -.029000   .122000   .5330000      .000      .000  
+73 327 41768.00 I  -.045771  .008170   .128964  .010531  I  .5278687  .0009499  3.1181 0.6717  P    47.096     .500     -.719     .300  -.032000   .123000   .5294000      .000      .000  
+73 328 41769.00 I  -.047302  .008769   .130142  .009502  I  .5246976  .0009499  3.2334 0.6717  P    47.439     .500     -.841     .300  -.034000   .124000   .5258000      .000      .000  
+73 329 41770.00 I  -.048828  .008961   .131356  .010189  I  .5213856  .0009499  3.3975 0.6717  P    47.660     .500     -.755     .300  -.036000   .125000   .5222000      .000      .000  
+73 330 41771.00 I  -.050351  .008871   .132605  .008830  I  .5178924  .0009499  3.5920 0.6717  P    47.708     .500     -.619     .300  -.039000   .127000   .5186000      .000      .000  
+73 331 41772.00 I  -.051875  .010144   .133891  .003313  I  .5142009  .0009499  3.7884 0.4939  P    47.656     .500     -.586     .300  -.041000   .128000   .5149000      .000      .000  
+73 4 1 41773.00 I  -.053404  .027169   .135214  .005442  I  .5103278  .0002710  3.9482 0.4939  P    47.657     .500     -.594     .300  -.044000   .129000   .5113000      .000      .000  
+73 4 2 41774.00 I  -.054940  .030688   .136573  .006176  I  .5063304  .0002710  4.0304 0.1916  P    47.755     .500     -.472     .300  -.046000   .131000   .5077000      .000      .000  
+73 4 3 41775.00 I  -.056488  .027434   .137969  .013677  I  .5023026  .0002710  4.0058 0.1916  P    47.777     .500     -.208     .300  -.048000   .132000   .5041000      .000      .000  
+73 4 4 41776.00 I  -.058053  .031109   .139402  .015490  I  .4983553  .0002710  3.8718 0.1916  P    47.525     .500     -.014     .300  -.051000   .133000   .5006000      .000      .000  
+73 4 5 41777.00 I  -.059639  .027250   .140872  .029276  I  .4945843  .0002710  3.6619 0.1916  P    47.067     .500     -.098     .300  -.053000   .134000   .4970000      .000      .000  
+73 4 6 41778.00 I  -.061247  .031072   .142377  .033803  I  .4910366  .0002710  3.4367 0.1916  P    46.721     .500     -.445     .300  -.056000   .136000   .4935000      .000      .000  
+73 4 7 41779.00 I  -.062874  .029203   .143917  .029521  I  .4876939  .0002710  3.2625 0.1355  P    46.735     .500     -.857     .300  -.058000   .137000   .4899000      .000      .000  
+73 4 8 41780.00 I  -.064517  .015992   .145494  .033667  I  .4844787  .0000063  3.1868 0.1368  P    47.048     .500    -1.157     .300  -.061000   .139000   .4864000      .000      .000  
+73 4 9 41781.00 I  -.066174  .014330   .147108  .030260  I  .4812836  .0000370  3.2211 0.0188  P    47.407     .500    -1.310     .300  -.063000   .141000   .4829000      .000      .000  
+73 410 41782.00 I  -.067840  .014560   .148757  .031775  I  .4780073  .0000370  3.3431 0.0262  P    47.621     .500    -1.381     .300  -.066000   .143000   .4794000      .000      .000  
+73 411 41783.00 I  -.069512  .016351   .150437  .027523  I  .4745829  .0000370  3.5087 0.0262  P    47.697     .500    -1.438     .300  -.068000   .145000   .4759000      .000      .000  
+73 412 41784.00 I  -.071188  .018280   .152143  .010351  I  .4709918  .0000370  3.6687 0.0262  P    47.760     .500    -1.487     .300  -.071000   .147000   .4724000      .000      .000  
+73 413 41785.00 I  -.072868  .016606   .153873  .013467  I  .4672608  .0000370  3.7828 0.0319  P    47.893     .500    -1.478     .300  -.073000   .148000   .4689000      .000      .000  
+73 414 41786.00 I  -.074547  .014003   .155619  .014917  I  .4634499  .0000519  3.8256 0.1368  P    48.042     .500    -1.369     .300  -.076000   .150000   .4655000      .000      .000  
+73 415 41787.00 I  -.076226  .012772   .157377  .015513  I  .4596361  .0002710  3.7888 0.1380  P    48.110     .500    -1.192     .300  -.078000   .152000   .4620000      .000      .000  
+73 416 41788.00 I  -.077898  .014123   .159141  .015277  I  .4558961  .0002710  3.6806 0.1916  P    48.103     .500    -1.063     .300  -.081000   .154000   .4586000      .000      .000  
+73 417 41789.00 I  -.079559  .013893   .160904  .013978  I  .4522916  .0002710  3.5222 0.2293  P    48.130     .500    -1.090     .300  -.083000   .156000   .4551000      .000      .000  
+73 418 41790.00 I  -.081206  .010623   .162664  .016129  I  .4488589  .0003700  3.3421 0.2293  P    48.230     .500    -1.274     .300  -.085000   .158000   .4517000      .000      .000  
+73 419 41791.00 I  -.082836  .009813   .164416  .022876  I  .4456047  .0003700  3.1700 0.2320  P    48.307     .500    -1.497     .300  -.086000   .160000   .4483000      .000      .000  
+73 420 41792.00 I  -.084445  .009740   .166159  .023729  I  .4425079  .0002801  3.0308 0.2893  P    48.285     .500    -1.633     .300  -.088000   .162000   .4448000      .000      .000  
+73 421 41793.00 I  -.086031  .008672   .167888  .020834  I  .4395262  .0004449  2.9421 0.2779  P    48.252     .500    -1.673     .300  -.090000   .164000   .4414000      .000      .000  
+73 422 41794.00 I  -.087594  .008879   .169602  .021918  I  .4366037  .0004801  2.9133 0.3093  P    48.328     .500    -1.729     .300  -.092000   .166000   .4380000      .000      .000  
+73 423 41795.00 I  -.089132  .007702   .171299  .022632  I  .4336788  .0004298  2.9468 0.3268  P    48.496     .500    -1.890     .300  -.093000   .168000   .4346000      .000      .000  
+73 424 41796.00 I  -.090646  .004604   .172977  .025609  I  .4306908  .0004434  3.0381 0.3088  P    48.672     .500    -2.069     .300  -.095000   .170000   .4312000      .000      .000  
+73 425 41797.00 I  -.092135  .013391   .174637  .022861  I  .4275866  .0004434  3.1773 0.3362  P    48.855     .500    -2.076     .300  -.097000   .172000   .4277000      .000      .000  
+73 426 41798.00 I  -.093600  .014952   .176280  .016101  I  .4243250  .0005055  3.3502 0.3006  P    49.072     .500    -1.865     .300  -.098000   .174000   .4243000      .000      .000  
+73 427 41799.00 I  -.095039  .017996   .177908  .016389  I  .4208818  .0004060  3.5365 0.2535  P    49.227     .500    -1.639     .300  -.100000   .176000   .4209000      .000      .000  
+73 428 41800.00 I  -.096452  .020007   .179524  .018068  I  .4172571  .0000402  3.7076 0.2441  P    49.192     .500    -1.624     .300  -.101000   .178000   .4175000      .000      .000  
+73 429 41801.00 I  -.097839  .022840   .181129  .016125  I  .4134837  .0002710  3.8275 0.1370  P    49.002     .500    -1.766     .300  -.102000   .180000   .4141000      .000      .000  
+73 430 41802.00 I  -.099202  .024995   .182728  .013615  I  .4096311  .0002710  3.8605 0.1916  P    48.804     .500    -1.785     .300  -.103000   .181000   .4108000      .000      .000  
+73 5 1 41803.00 I  -.100543  .024464   .184322  .016261  I  .4057984  .0002710  3.7865 0.1916  P    48.609     .500    -1.553     .300  -.104000   .183000   .4074000      .000      .000  
+73 5 2 41804.00 I  -.101860  .024278   .185910  .017034  I  .4020902  .0002710  3.6163 0.1916  P    48.303     .500    -1.283     .300  -.106000   .185000   .4040000      .000      .000  
+73 5 3 41805.00 I  -.103149  .023618   .187492  .015012  I  .3985825  .0002710  3.3961 0.1916  P    47.912     .500    -1.272     .300  -.107000   .187000   .4006000      .000      .000  
+73 5 4 41806.00 I  -.104401  .024214   .189067  .014347  I  .3952917  .0002710  3.1948 0.1916  P    47.676     .500    -1.556     .300  -.108000   .189000   .3972000      .000      .000  
+73 5 5 41807.00 I  -.105607  .021898   .190634  .012515  I  .3921651  .0002710  3.0767 0.1916  P    47.783     .500    -1.901     .300  -.109000   .190000   .3939000      .000      .000  
+73 5 6 41808.00 I  -.106760  .019623   .192195  .012981  I  .3891000  .0002710  3.0734 0.1916  P    48.144     .500    -2.105     .300  -.110000   .192000   .3905000      .000      .000  
+73 5 7 41809.00 I  -.107853  .019807   .193751  .012218  I  .3859841  .0002710  3.1727 0.1916  P    48.506     .500    -2.159     .300  -.111000   .194000   .3871000      .000      .000  
+73 5 8 41810.00 I  -.108882  .018586   .195303  .006619  I  .3827358  .0002710  3.3283 0.1423  P    48.682     .500    -2.135     .300  -.112000   .196000   .3838000      .000      .000  
+73 5 9 41811.00 I  -.109843  .017897   .196854  .007580  I  .3793280  .0000870  3.4823 0.1423  P    48.646     .500    -2.059     .300  -.112000   .198000   .3805000      .000      .000  
+73 510 41812.00 I  -.110735  .016515   .198402  .008142  I  .3757875  .0000870  3.5875 0.0615  P    48.495     .500    -1.934     .300  -.113000   .200000   .3771000      .000      .000  
+73 511 41813.00 I  -.111557  .014604   .199950  .020486  I  .3721774  .0000870  3.6195 0.0615  P    48.380     .500    -1.805     .300  -.113000   .202000   .3738000      .000      .000  
+73 512 41814.00 I  -.112313  .015209   .201499  .023592  I  .3685739  .0000870  3.5752 0.0615  P    48.391     .500    -1.724     .300  -.114000   .204000   .3705000      .000      .000  
+73 513 41815.00 I  -.113006  .013690   .203052  .025493  I  .3650488  .0000870  3.4653 0.0615  P    48.470     .500    -1.681     .300  -.114000   .205000   .3672000      .000      .000  
+73 514 41816.00 I  -.113643  .010576   .204611  .028913  I  .3616590  .0000870  3.3082 0.1423  P    48.493     .500    -1.642     .300  -.115000   .207000   .3640000      .000      .000  
+73 515 41817.00 I  -.114229  .015352   .206179  .026139  I  .3584406  .0002710  3.1264 0.1274  P    48.429     .500    -1.642     .300  -.115000   .209000   .3607000      .000      .000  
+73 516 41818.00 I  -.114769  .015251   .207756  .029634  I  .3554062  .0002395  2.9444 0.1808  P    48.338     .500    -1.764     .300  -.116000   .211000   .3575000      .000      .000  
+73 517 41819.00 I  -.115267  .018306   .209342  .025695  I  .3525437  .0002395  2.7866 0.1694  P    48.257     .500    -2.005     .300  -.116000   .213000   .3542000      .000      .000  
+73 518 41820.00 I  -.115729  .020861   .210937  .019673  I  .3498182  .0002395  2.6733 0.1694  P    48.190     .500    -2.232     .300  -.116000   .214000   .3510000      .000      .000  
+73 519 41821.00 I  -.116160  .018243   .212543  .019925  I  .3471778  .0002395  2.6178 0.1694  P    48.184     .500    -2.313     .300  -.116000   .216000   .3479000      .000      .000  
+73 520 41822.00 I  -.116560  .020619   .214161  .014814  I  .3445621  .0002395  2.6239 0.1694  P    48.271     .500    -2.270     .300  -.116000   .217000   .3447000      .000      .000  
+73 521 41823.00 I  -.116928  .025576   .215794  .012854  I  .3419107  .0002395  2.6878 0.1808  P    48.355     .500    -2.237     .300  -.116000   .219000   .3416000      .000      .000  
+73 522 41824.00 I  -.117260  .025880   .217446  .012054  I  .3391710  .0002710  2.7983 0.1808  P    48.321     .500    -2.263     .300  -.117000   .220000   .3384000      .000      .000  
+73 523 41825.00 I  -.117551  .022418   .219118  .011225  I  .3363033  .0002710  2.9411 0.1916  P    48.237     .500    -2.235     .300  -.117000   .221000   .3353000      .000      .000  
+73 524 41826.00 I  -.117796  .021351   .220811  .012878  I  .3332839  .0002710  3.0985 0.1916  P    48.279     .500    -2.059     .300  -.117000   .223000   .3321000      .000      .000  
+73 525 41827.00 I  -.117988  .024367   .222526  .012677  I  .3301084  .0002710  3.2495 0.1916  P    48.438     .500    -1.855     .300  -.117000   .224000   .3290000      .000      .000  
+73 526 41828.00 I  -.118122  .027985   .224260  .008483  I  .3267957  .0002710  3.3681 0.1916  P    48.477     .500    -1.837     .300  -.117000   .226000   .3258000      .000      .000  
+73 527 41829.00 I  -.118191  .025031   .226015  .010133  I  .3233929  .0002710  3.4247 0.1916  P    48.233     .500    -2.004     .300  -.117000   .227000   .3227000      .000      .000  
+73 528 41830.00 I  -.118187  .019707   .227791  .011665  I  .3199751  .0002710  3.3949 0.1916  P    47.789     .500    -2.097     .300  -.117000   .228000   .3196000      .000      .000  
+73 529 41831.00 I  -.118104  .017895   .229587  .015466  I  .3166341  .0002710  3.2722 0.1916  P    47.316     .500    -1.928     .300  -.117000   .230000   .3166000      .000      .000  
+73 530 41832.00 I  -.117943  .020656   .231402  .017212  I  .3134538  .0002710  3.0804 0.1916  P    46.906     .500    -1.643     .300  -.117000   .231000   .3135000      .000      .000  
+73 531 41833.00 I  -.117703  .017892   .233238  .016251  I  .3104780  .0002710  2.8745 0.1916  P    46.634     .500    -1.536     .300  -.117000   .233000   .3105000      .000      .000  
+73 6 1 41834.00 I  -.117388  .009540   .235091  .017428  I  .3076868  .0002710  2.7224 0.1916  P    46.601     .500    -1.673     .300  -.117000   .234000   .3074000      .000      .000  
+73 6 2 41835.00 I  -.117002  .008392   .236962  .015311  I  .3049978  .0002710  2.6763 0.1916  P    46.812     .500    -1.851     .300  -.117000   .235000   .3044000      .000      .000  
+73 6 3 41836.00 I  -.116549  .006454   .238846  .015736  I  .3022950  .0002710  2.7478 0.1916  P    47.124     .500    -1.909     .300  -.117000   .237000   .3014000      .000      .000  
+73 6 4 41837.00 I  -.116036  .013487   .240738  .014724  I  .2994746  .0002710  2.9021 0.1916  P    47.384     .500    -1.899     .300  -.117000   .238000   .2984000      .000      .000  
+73 6 5 41838.00 I  -.115466  .014280   .242636  .010305  I  .2964846  .0002710  3.0751 0.1916  P    47.513     .500    -1.906     .300  -.117000   .240000   .2954000      .000      .000  
+73 6 6 41839.00 I  -.114846  .019850   .244536  .009134  I  .2933393  .0002710  3.2035 0.2630  P    47.430     .500    -1.863     .300  -.117000   .241000   .2924000      .000      .000  
+73 6 7 41840.00 I  -.114180  .022917   .246436  .007441  I  .2901054  .0004509  3.2485 0.2630  P    47.075     .500    -1.675     .300  -.117000   .243000   .2895000      .000      .000  
+73 6 8 41841.00 I  -.113474  .022825   .248333  .011421  I  .2868723  .0004509  3.2035 0.3188  P    46.583     .500    -1.423     .300  -.116000   .245000   .2865000      .000      .000  
+73 6 9 41842.00 I  -.112728  .026302   .250227  .012847  I  .2837227  .0004509  3.0857 0.3188  P    46.250     .500    -1.296     .300  -.116000   .246000   .2836000      .000      .000  
+73 610 41843.00 I  -.111943  .023501   .252117  .012077  I  .2807165  .0004509  2.9212 0.3188  P    46.209     .500    -1.348     .300  -.116000   .248000   .2806000      .000      .000  
+73 611 41844.00 I  -.111124  .023142   .254000  .012371  I  .2778874  .0004509  2.7353 0.3188  P    46.255     .500    -1.447     .300  -.116000   .250000   .2777000      .000      .000  
+73 612 41845.00 I  -.110279  .021487   .255878  .017632  I  .2752458  .0004509  2.5498 0.2630  P    46.116     .500    -1.494     .300  -.115000   .252000   .2748000      .000      .000  
+73 613 41846.00 I  -.109416  .017151   .257745  .020236  I  .2727809  .0002710  2.3851 0.2630  P    45.778     .500    -1.564     .300  -.115000   .254000   .2720000      .000      .000  
+73 614 41847.00 I  -.108543  .016601   .259600  .018843  I  .2704624  .0002710  2.2600 0.3157  P    45.432     .500    -1.753     .300  -.115000   .255000   .2691000      .000      .000  
+73 615 41848.00 I  -.107669  .014071   .261438  .018838  I  .2682428  .0005702  2.1893 0.3157  P    45.234     .500    -1.978     .300  -.114000   .257000   .2663000      .000      .000  
+73 616 41849.00 I  -.106798  .025101   .263258  .017549  I  .2660632  .0005702  2.1806 0.4032  P    45.208     .500    -2.043     .300  -.114000   .259000   .2634000      .000      .000  
+73 617 41850.00 I  -.105935  .028203   .265060  .019524  I  .2638618  .0005702  2.2315 0.4032  P    45.288     .500    -1.886     .300  -.113000   .261000   .2607000      .000      .000  
+73 618 41851.00 I  -.105082  .027314   .266846  .017249  I  .2615839  .0005702  2.3313 0.4032  P    45.332     .500    -1.668     .300  -.112000   .263000   .2580000      .000      .000  
+73 619 41852.00 I  -.104241  .030244   .268620  .011627  I  .2591881  .0005702  2.4643 0.4032  P    45.194     .500    -1.573     .300  -.111000   .264000   .2552000      .000      .000  
+73 620 41853.00 I  -.103414  .026380   .270385  .014594  I  .2566509  .0005702  2.6107 0.3157  P    44.903     .500    -1.600     .300  -.110000   .266000   .2525000      .000      .000  
+73 621 41854.00 I  -.102602  .029233   .272147  .014833  I  .2539691  .0002710  2.7501 0.3157  P    44.673     .500    -1.617     .300  -.110000   .268000   .2498000      .000      .000  
+73 622 41855.00 I  -.101802  .026309   .273910  .014971  I  .2511601  .0002710  2.8613 0.1784  P    44.646     .500    -1.579     .300  -.109000   .270000   .2472000      .000      .000  
+73 623 41856.00 I  -.101014  .016756   .275678  .015592  I  .2482631  .0002321  2.9228 0.2197  P    44.685     .500    -1.573     .300  -.108000   .272000   .2446000      .000      .000  
+73 624 41857.00 I  -.100233  .014869   .277455  .016456  I  .2453372  .0003459  2.9161 0.2083  P    44.530     .500    -1.640     .300  -.107000   .273000   .2421000      .000      .000  
+73 625 41858.00 I  -.099456  .009771   .279243  .018589  I  .2424569  .0003459  2.8314 0.2446  P    44.084     .500    -1.662     .300  -.106000   .275000   .2395000      .000      .000  
+73 626 41859.00 I  -.098677  .014683   .281043  .020595  I  .2396968  .0003459  2.6795 0.2446  P    43.488     .500    -1.528     .300  -.105000   .277000   .2369000      .000      .000  
+73 627 41860.00 I  -.097889  .016561   .282855  .020415  I  .2371084  .0003459  2.4962 0.2446  P    42.990     .500    -1.336     .300  -.103000   .279000   .2344000      .000      .000  
+73 628 41861.00 I  -.097085  .019212   .284679  .018761  I  .2346963  .0003459  2.3374 0.2762  P    42.793     .500    -1.289     .300  -.102000   .281000   .2320000      .000      .000  
+73 629 41862.00 I  -.096256  .020588   .286516  .019760  I  .2324062  .0004307  2.2610 0.2197  P    42.935     .500    -1.412     .300  -.100000   .282000   .2295000      .000      .000  
+73 630 41863.00 I  -.095395  .021457   .288366  .017237  I  .2301356  .0002710  2.3006 0.2544  P    43.238     .500    -1.515     .300  -.099000   .284000   .2271000      .000      .000  
+73 7 1 41864.00 I  -.094499  .024493   .290226  .016679  I  .2277697  .0002710  2.4460 0.1916  P    43.467     .500    -1.477     .300  -.097000   .286000   .2246000      .000      .000  
+73 7 2 41865.00 I  -.093566  .021929   .292091  .016416  I  .2252259  .0002710  2.6446 0.1916  P    43.568     .500    -1.406     .300  -.095000   .288000   .2223000      .000      .000  
+73 7 3 41866.00 I  -.092596  .021194   .293957  .011802  I  .2224869  .0002710  2.8242 0.1916  P    43.619     .500    -1.435     .300  -.094000   .290000   .2200000      .000      .000  
+73 7 4 41867.00 I  -.091588  .019350   .295818  .011256  I  .2196042  .0002710  2.9239 0.1916  P    43.555     .500    -1.481     .300  -.092000   .291000   .2177000      .000      .000  
+73 7 5 41868.00 I  -.090547  .016774   .297669  .010791  I  .2166747  .0002710  2.9169 0.1916  P    43.148     .500    -1.365     .300  -.091000   .293000   .2154000      .000      .000  
+73 7 6 41869.00 I  -.089478  .016849   .299504  .009358  I  .2138025  .0002710  2.8135 0.1916  P    42.410     .500    -1.110     .300  -.089000   .295000   .2131000      .000      .000  
+73 7 7 41870.00 I  -.088387  .013730   .301319  .010540  I  .2110689  .0002710  2.6464 0.1916  P    41.750     .500     -.951     .300  -.087000   .296000   .2108000      .000      .000  
+73 7 8 41871.00 I  -.087280  .019979   .303108  .012764  I  .2085187  .0002710  2.4524 0.1916  P    41.540     .500    -1.025     .300  -.085000   .298000   .2085000      .000      .000  
+73 7 9 41872.00 I  -.086167  .022157   .304868  .011666  I  .2061633  .0002710  2.2609 0.1916  P    41.654     .500    -1.203     .300  -.083000   .299000   .2062000      .000      .000  
+73 710 41873.00 I  -.085056  .020893   .306592  .015599  I  .2039890  .0002710  2.0931 0.1916  P    41.661     .500    -1.301     .300  -.081000   .300000   .2039000      .000      .000  
+73 711 41874.00 I  -.083952  .023481   .308278  .017434  I  .2019641  .0002710  1.9644 0.1916  P    41.377     .500    -1.323     .300  -.080000   .302000   .2016000      .000      .000  
+73 712 41875.00 I  -.082861  .021541   .309919  .015936  I  .2000425  .0002710  1.8887 0.1916  P    41.003     .500    -1.397     .300  -.078000   .303000   .1993000      .000      .000  
+73 713 41876.00 I  -.081780  .023470   .311513  .019360  I  .1981661  .0002710  1.8749 0.1916  P    40.774     .500    -1.523     .300  -.076000   .304000   .1970000      .000      .000  
+73 714 41877.00 I  -.080706  .021516   .313057  .017777  I  .1962715  .0002710  1.9246 0.1916  P    40.719     .500    -1.543     .300  -.074000   .305000   .1947000      .000      .000  
+73 715 41878.00 I  -.079639  .008902   .314550  .017736  I  .1942984  .0002710  2.0297 0.1916  P    40.750     .500    -1.368     .300  -.072000   .307000   .1924000      .000      .000  
+73 716 41879.00 I  -.078576  .013383   .315992  .015590  I  .1921991  .0002710  2.1738 0.1916  P    40.772     .500    -1.121     .300  -.070000   .308000   .1901000      .000      .000  
+73 717 41880.00 I  -.077516  .014265   .317382  .006845  I  .1899451  .0002710  2.3351 0.1916  P    40.683     .500    -1.011     .300  -.068000   .309000   .1877000      .000      .000  
+73 718 41881.00 I  -.076456  .012458   .318721  .007667  I  .1875310  .0002710  2.4900 0.1916  P    40.421     .500    -1.103     .300  -.066000   .310000   .1854000      .000      .000  
+73 719 41882.00 I  -.075393  .014120   .320007  .008445  I  .1849750  .0002710  2.6151 0.1916  P    40.073     .500    -1.280     .300  -.064000   .311000   .1830000      .000      .000  
+73 720 41883.00 I  -.074324  .012666   .321240  .007383  I  .1823177  .0002710  2.6896 0.1916  P    39.825     .500    -1.400     .300  -.062000   .312000   .1807000      .000      .000  
+73 721 41884.00 I  -.073249  .013325   .322420  .006510  I  .1796175  .0002710  2.6990 0.1916  P    39.743     .500    -1.425     .300  -.061000   .314000   .1783000      .000      .000  
+73 722 41885.00 I  -.072165  .011720   .323546  .005658  I  .1769433  .0002710  2.6378 0.1916  P    39.684     .500    -1.388     .300  -.059000   .315000   .1759000      .000      .000  
+73 723 41886.00 I  -.071074  .004857   .324618  .005761  I  .1743623  .0002710  2.5156 0.1916  P    39.458     .500    -1.310     .300  -.057000   .316000   .1735000      .000      .000  
+73 724 41887.00 I  -.069974  .007184   .325637  .005914  I  .1719236  .0002710  2.3591 0.1916  P    39.044     .500    -1.212     .300  -.055000   .317000   .1712000      .000      .000  
+73 725 41888.00 I  -.068865  .008084   .326605  .003889  I  .1696410  .0002710  2.2120 0.1916  P    38.635     .500    -1.178     .300  -.053000   .318000   .1688000      .000      .000  
+73 726 41889.00 I  -.067746  .007905   .327521  .003722  I  .1674796  .0002710  2.1251 0.1916  P    38.475     .500    -1.300     .300  -.051000   .319000   .1664000      .000      .000  
+73 727 41890.00 I  -.066615  .008293   .328385  .004136  I  .1653574  .0002710  2.1386 0.1916  P    38.632     .500    -1.529     .300  -.049000   .320000   .1639000      .000      .000  
+73 728 41891.00 I  -.065470  .007735   .329200  .014973  I  .1631654  .0002710  2.2632 0.1916  P    38.911     .500    -1.670     .300  -.047000   .321000   .1615000      .000      .000  
+73 729 41892.00 I  -.064308  .008613   .329966  .017281  I  .1608038  .0002710  2.4692 0.1916  P    39.057     .500    -1.601     .300  -.046000   .322000   .1590000      .000      .000  
+73 730 41893.00 I  -.063127  .009939   .330684  .016150  I  .1582205  .0002710  2.6945 0.1916  P    39.048     .500    -1.422     .300  -.044000   .323000   .1566000      .000      .000  
+73 731 41894.00 I  -.061922  .009300   .331356  .018284  I  .1554320  .0002710  2.8682 0.1916  P    39.042     .500    -1.320     .300  -.042000   .325000   .1541000      .000      .000  
+73 8 1 41895.00 I  -.060690  .008717   .331984  .020554  I  .1525183  .0002710  2.9394 0.1916  P    39.030     .500    -1.312     .300  -.040000   .326000   .1516000      .000      .000  
+73 8 2 41896.00 I  -.059426  .009129   .332571  .023664  I  .1495917  .0002710  2.8952 0.1916  P    38.750     .500    -1.259     .300  -.038000   .327000   .1491000      .000      .000  
+73 8 3 41897.00 I  -.058129  .011588   .333123  .021583  I  .1467587  .0002710  2.7584 0.1916  P    38.079     .500    -1.104     .300  -.037000   .328000   .1467000      .000      .000  
+73 8 4 41898.00 I  -.056796  .012963   .333641  .018419  I  .1440916  .0002710  2.5713 0.1916  P    37.356     .500     -.980     .300  -.035000   .329000   .1442000      .000      .000  
+73 8 5 41899.00 I  -.055426  .012045   .334129  .016004  I  .1416190  .0002710  2.3756 0.1916  P    37.053     .500    -1.016     .300  -.033000   .330000   .1417000      .000      .000  
+73 8 6 41900.00 I  -.054018  .011657   .334587  .017099  I  .1393332  .0002710  2.2017 0.1916  P    37.223     .500    -1.147     .300  -.031000   .331000   .1392000      .000      .000  
+73 8 7 41901.00 I  -.052571  .013442   .335018  .014829  I  .1372021  .0002710  2.0684 0.1916  P    37.493     .500    -1.221     .300  -.030000   .331000   .1368000      .000      .000  
+73 8 8 41902.00 I  -.051087  .015037   .335422  .008011  I  .1351788  .0002710  1.9877 0.1916  P    37.566     .500    -1.207     .300  -.028000   .332000   .1343000      .000      .000  
+73 8 9 41903.00 I  -.049566  .014660   .335801  .007393  I  .1332063  .0002710  1.9680 0.1916  P    37.487     .500    -1.200     .300  -.027000   .333000   .1319000      .000      .000  
+73 810 41904.00 I  -.048009  .013814   .336158  .003430  I  .1312213  .0002710  2.0127 0.1432  P    37.412     .500    -1.241     .300  -.025000   .334000   .1294000      .000      .000  
+73 811 41905.00 I  -.046418  .020502   .336495  .003133  I  .1291610  .0000927  2.1172 0.1747  P    37.363     .500    -1.248     .300  -.023000   .334000   .1269000      .000      .000  
+73 812 41906.00 I  -.044793  .023132   .336814  .003291  I  .1269711  .0002206  2.2690 0.1196  P    37.300     .500    -1.156     .300  -.022000   .335000   .1244000      .000      .000  
+73 813 41907.00 I  -.043138  .020037   .337117  .003063  I  .1246142  .0002206  2.4472 0.1660  P    37.253     .500    -1.025     .300  -.020000   .336000   .1219000      .000      .000  
+73 814 41908.00 I  -.041457  .020744   .337408  .003419  I  .1220762  .0002482  2.6269 0.1660  P    37.235     .500     -.981     .300  -.019000   .336000   .1194000      .000      .000  
+73 815 41909.00 I  -.039753  .019112   .337689  .006789  I  .1193688  .0002482  2.7814 0.1653  P    37.148     .500    -1.064     .300  -.017000   .337000   .1169000      .000      .000  
+73 816 41910.00 I  -.038027  .020655   .337962  .007263  I  .1165303  .0002183  2.8853 0.1749  P    36.897     .500    -1.202     .300  -.016000   .337000   .1144000      .000      .000  
+73 817 41911.00 I  -.036279  .018781   .338231  .008496  I  .1136211  .0002464  2.9204 0.1536  P    36.550     .500    -1.292     .300  -.014000   .337000   .1118000      .000      .000  
+73 818 41912.00 I  -.034509  .010034   .338497  .009743  I  .1107146  .0002161  2.8803 0.1639  P    36.278     .500    -1.285     .300  -.013000   .338000   .1093000      .000      .000  
+73 819 41913.00 I  -.032716  .010358   .338764  .015201  I  .1078820  .0002161  2.7757 0.5275  P    36.167     .500    -1.206     .300  -.011000   .338000   .1067000      .000      .000  
+73 820 41914.00 I  -.030904  .011949   .339034  .017505  I  .1051753  .0010326  2.6347 0.4683  P    36.149     .500    -1.113     .300  -.010000   .338000   .1042000      .000      .000  
+73 821 41915.00 I  -.029072  .010447   .339312  .024151  I  .1026111  .0009113  2.4982 0.7598  P    36.119     .500    -1.077     .300  -.009000   .338000   .1016000      .000      .000  
+73 822 41916.00 I  -.027225  .009422   .339599  .026980  I  .1001622  .0011148  2.4117 0.7199  P    36.066     .500    -1.158     .300  -.007000   .338000   .0990000      .000      .000  
+73 823 41917.00 I  -.025364  .008885   .339898  .024179  I  .0977587  .0011148  2.4123 0.7883  P    36.072     .500    -1.377     .300  -.006000   .339000   .0965000      .000      .000  
+73 824 41918.00 I  -.023491  .007846   .340210  .027130  I  .0953036  .0011148  2.5151 0.7883  P    36.178     .500    -1.657     .300  -.004000   .339000   .0939000      .000      .000  
+73 825 41919.00 I  -.021609  .006916   .340534  .023659  I  .0926991  .0011148  2.7061 0.6328  P    36.302     .500    -1.839     .300  -.003000   .339000   .0913000      .000      .000  
+73 826 41920.00 I  -.019719  .004628   .340866  .023091  I  .0898766  .0005990  2.9415 0.5736  P    36.331     .500    -1.793     .300  -.002000   .339000   .0887000      .000      .000  
+73 827 41921.00 I  -.017823  .009660   .341204  .020004  I  .0868220  .0002710  3.1591 0.3287  P    36.274     .500    -1.543     .300  -.001000   .339000   .0860000      .000      .000  
+73 828 41922.00 I  -.015923  .011031   .341546  .007889  I  .0835842  .0002710  3.2991 0.1916  P    36.251     .500    -1.253     .300   .000000   .339000   .0834000      .000      .000  
+73 829 41923.00 I  -.014024  .013022   .341889  .007631  I  .0802614  .0002710  3.3262 0.1916  P    36.267     .500    -1.065     .300   .001000   .339000   .0807000      .000      .000  
+73 830 41924.00 I  -.012126  .014478   .342230  .005104  I  .0769694  .0002710  3.2406 0.1916  P    36.134     .500     -.987     .300   .003000   .340000   .0781000      .000      .000  
+73 831 41925.00 I  -.010233  .012716   .342570  .006431  I  .0738075  .0002710  3.0733 0.1916  P    35.708     .500     -.958     .300   .004000   .340000   .0754000      .000      .000  
+73 9 1 41926.00 I  -.008345  .014608   .342904  .006697  I  .0708351  .0002710  2.8693 0.3603  P    35.162     .500     -.955     .300   .005000   .340000   .0727000      .000      .000  
+73 9 2 41927.00 I  -.006462  .019669   .343233  .006730  I  .0680673  .0006676  2.6701 0.3603  P    34.859     .500     -.990     .300   .006000   .340000   .0699000      .000      .000  
+73 9 3 41928.00 I  -.004582  .020318   .343554  .007748  I  .0654838  .0006676  2.5044 0.4181  P    34.981     .500    -1.038     .300   .007000   .340000   .0672000      .000      .000  
+73 9 4 41929.00 I  -.002703  .017699   .343867  .007243  I  .0630419  .0005035  2.3887 0.4181  P    35.383     .500    -1.053     .300   .008000   .340000   .0645000      .000      .000  
+73 9 5 41930.00 I  -.000823  .017699   .344170  .007386  I  .0606870  .0005035  2.3314 0.4917  P    35.812     .500    -1.041     .300   .010000   .340000   .0617000      .000      .000  
+73 9 6 41931.00 I   .001061  .015526   .344464  .014265  I  .0583584  .0008448  2.3363 0.4621  P    36.129     .500    -1.055     .300   .011000   .340000   .0589000      .000      .000  
+73 9 7 41932.00 I   .002946  .017760   .344750  .015563  I  .0559943  .0007750  2.4016 0.5844  P    36.294     .500    -1.103     .300   .013000   .341000   .0561000      .000      .000  
+73 9 8 41933.00 I   .004833  .015493   .345026  .013573  I  .0535381  .0008076  2.5182 0.5597  P    36.287     .500    -1.135     .300   .015000   .341000   .0533000      .000      .000  
+73 9 9 41934.00 I   .006719  .004197   .345293  .015169  I  .0509461  .0008076  2.6702 0.6324  P    36.140     .500    -1.123     .300   .017000   .341000   .0505000      .000      .000  
+73 910 41935.00 I   .008606  .003692   .345551  .013815  I  .0481927  .0009735  2.8370 0.6324  P    35.984     .500    -1.115     .300   .018000   .341000   .0476000      .000      .000  
+73 911 41936.00 I   .010491  .003652   .345797  .015639  I  .0452758  .0009735  2.9925 0.5498  P    35.929     .500    -1.153     .300   .020000   .341000   .0447000      .000      .000  
+73 912 41937.00 I   .012375  .007163   .346029  .016450  I  .0422207  .0005113  3.1088 0.5053  P    35.930     .500    -1.203     .300   .022000   .342000   .0418000      .000      .000  
+73 913 41938.00 I   .014258  .007764   .346244  .012001  I  .0390798  .0002710  3.1602 0.3320  P    35.836     .500    -1.194     .300   .023000   .342000   .0389000      .000      .000  
+73 914 41939.00 I   .016138  .006751   .346440  .011191  I  .0359271  .0004237  3.1313 0.3320  P    35.575     .500    -1.110     .300   .025000   .342000   .0360000      .000      .000  
+73 915 41940.00 I   .018015  .007492   .346613  .012790  I  .0328428  .0006062  3.0258 0.3698  P    35.255     .500     -.996     .300   .027000   .342000   .0331000      .000      .000  
+73 916 41941.00 I   .019888  .014443   .346762  .012607  I  .0298925  .0006062  2.8694 0.4286  P    35.063     .500     -.901     .300   .028000   .343000   .0301000      .000      .000  
+73 917 41942.00 I   .021754  .016660   .346884  .013693  I  .0271064  .0006062  2.7058 0.4286  P    35.106     .500     -.851     .300   .030000   .343000   .0272000      .000      .000  
+73 918 41943.00 I   .023611  .015938   .346978  .012071  I  .0244669  .0006062  2.5841 0.4286  P    35.349     .500     -.862     .300   .031000   .344000   .0242000      .000      .000  
+73 919 41944.00 I   .025456  .016842   .347042  .008835  I  .0219114  .0006062  2.5428 0.4803  P    35.671     .500     -.950     .300   .033000   .344000   .0213000      .000      .000  
+73 920 41945.00 I   .027289  .015226   .347074  .009369  I  .0193484  .0007452  2.6000 0.3320  P    35.952     .500    -1.113     .300   .035000   .344000   .0183000      .000      .000  
+73 921 41946.00 I   .029108  .017568   .347072  .009699  I  .0166814  .0002710  2.7470 0.3965  P    36.134     .500    -1.315     .300   .036000   .345000   .0153000      .000      .000  
+73 922 41947.00 I   .030916  .015327   .347036  .010335  I  .0138355  .0002710  2.9505 0.1916  P    36.213     .500    -1.471     .300   .038000   .345000   .0123000      .000      .000  
+73 923 41948.00 I   .032711  .009551   .346966  .009700  I  .0107781  .0002710  3.1609 0.1916  P    36.226     .500    -1.476     .300   .039000   .346000   .0093000      .000      .000  
+73 924 41949.00 I   .034492  .008391   .346860  .011018  I  .0075291  .0002710  3.3253 0.1916  P    36.225     .500    -1.273     .300   .041000   .346000   .0063000      .000      .000  
+73 925 41950.00 I   .036259  .005722   .346719  .012453  I  .0041565  .0002710  3.4029 0.1916  P    36.241     .500     -.933     .300   .042000   .346000   .0033000      .000      .000  
+73 926 41951.00 I   .038008  .005853   .346543  .013390  I  .0007577  .0002710  3.3773 0.1916  P    36.243     .500     -.625     .300   .044000   .346000   .0002000      .000      .000  
+73 927 41952.00 I   .039739  .004493   .346333  .014145  I -.0025677  .0002710  3.2604 0.1916  P    36.159     .500     -.491     .300   .045000   .347000  -.0028000      .000      .000  
+73 928 41953.00 I   .041450  .011754   .346086  .014431  I -.0057439  .0002710  3.0858 0.1916  P    35.950     .500     -.542     .300   .047000   .347000  -.0059000      .000      .000  
+73 929 41954.00 I   .043140  .013403   .345801  .015144  I -.0087342  .0002710  2.8958 0.1916  P    35.683     .500     -.681     .300   .048000   .347000  -.0089000      .000      .000  
+73 930 41955.00 I   .044808  .012719   .345476  .014070  I -.0115428  .0002710  2.7276 0.1916  P    35.517     .500     -.796     .300   .049000   .347000  -.0120000      .000      .000  
+7310 1 41956.00 I   .046453  .014596   .345111  .014007  I -.0142048  .0002710  2.6057 0.1916  P    35.609     .500     -.833     .300   .051000   .347000  -.0151000      .000      .000  
+7310 2 41957.00 I   .048076  .018419   .344705  .012204  I -.0167739  .0002710  2.5431 0.1916  P    36.017     .500     -.814     .300   .052000   .348000  -.0181000      .000      .000  
+7310 3 41958.00 I   .049673  .020963   .344259  .010706  I -.0193120  .0002710  2.5437 0.1916  P    36.629     .500     -.805     .300   .054000   .348000  -.0212000      .000      .000  
+7310 4 41959.00 I   .051242  .019970   .343773  .016288  I -.0218818  .0002710  2.6058 0.1916  P    37.213     .500     -.849     .300   .055000   .348000  -.0243000      .000      .000  
+7310 5 41960.00 I   .052777  .024885   .343248  .021259  I -.0245414  .0002710  2.7215 0.1916  P    37.556     .500     -.911     .300   .057000   .348000  -.0274000      .000      .000  
+7310 6 41961.00 I   .054276  .023456   .342684  .023115  I -.0273383  .0002710  2.8779 0.1916  P    37.600     .500     -.920     .300   .058000   .348000  -.0305000      .000      .000  
+7310 7 41962.00 I   .055736  .021473   .342080  .022267  I -.0303046  .0002710  3.0567 0.1916  P    37.446     .500     -.881     .300   .060000   .347000  -.0336000      .000      .000  
+7310 8 41963.00 I   .057156  .020400   .341436  .021453  I -.0334515  .0002710  3.2351 0.1916  P    37.245     .500     -.885     .300   .061000   .347000  -.0367000      .000      .000  
+7310 9 41964.00 I   .058539  .017609   .340754  .020681  I -.0367661  .0002710  3.3876 0.1916  P    37.102     .500     -.977     .300   .063000   .347000  -.0398000      .000      .000  
+731010 41965.00 I   .059888  .017199   .340035  .024914  I -.0402090  .0002710  3.4871 0.3006  P    37.042     .500    -1.063     .300   .065000   .347000  -.0429000      .000      .000  
+731011 41966.00 I   .061203  .018154   .339282  .022836  I -.0437149  .0005367  3.5103 0.3006  P    37.031     .500    -1.003     .300   .066000   .347000  -.0460000      .000      .000  
+731012 41967.00 I   .062488  .013776   .338497  .020474  I -.0472011  .0005367  3.4479 0.3293  P    37.007     .500     -.791     .300   .068000   .346000  -.0492000      .000      .000  
+731013 41968.00 I   .063742  .014686   .337684  .025127  I -.0505867  .0003817  3.3136 0.3293  P    36.954     .500     -.561     .300   .069000   .346000  -.0523000      .000      .000  
+731014 41969.00 I   .064969  .015228   .336845  .025892  I -.0538171  .0003817  3.1459 0.3118  P    36.937     .500     -.429     .300   .071000   .346000  -.0554000      .000      .000  
+731015 41970.00 I   .066169  .018441   .335980  .025066  I -.0568853  .0004931  2.9987 0.3239  P    37.043     .500     -.391     .300   .072000   .346000  -.0586000      .000      .000  
+731016 41971.00 I   .067343  .019432   .335085  .026019  I -.0598382  .0005235  2.9225 0.3539  P    37.283     .500     -.383     .300   .074000   .345000  -.0618000      .000      .000  
+731017 41972.00 I   .068487  .019507   .334159  .024326  I -.0627630  .0005078  2.9444 0.3950  P    37.574     .500     -.385     .300   .075000   .345000  -.0650000      .000      .000  
+731018 41973.00 I   .069599  .018037   .333202  .025205  I -.0657581  .0005916  3.0595 0.4113  P    37.828     .500     -.427     .300   .077000   .344000  -.0682000      .000      .000  
+731019 41974.00 I   .070676  .018253   .332213  .026705  I -.0689022  .0006471  3.2354 0.4384  P    38.023     .500     -.522     .300   .078000   .344000  -.0714000      .000      .000  
+731020 41975.00 I   .071714  .017726   .331195  .018646  I -.0722334  .0006471  3.4250 0.4562  P    38.185     .500     -.620     .300   .079000   .343000  -.0746000      .000      .000  
+731021 41976.00 I   .072709  .018911   .330147  .019479  I -.0757409  .0006433  3.5806 0.4605  P    38.342     .500     -.634     .300   .081000   .343000  -.0779000      .000      .000  
+731022 41977.00 I   .073660  .017522   .329075  .019560  I -.0793711  .0006554  3.6657 0.4865  P    38.486     .500     -.494     .300   .082000   .342000  -.0811000      .000      .000  
+731023 41978.00 I   .074564  .018102   .327981  .021186  I -.0830434  .0007299  3.6640 0.4405  P    38.559     .500     -.216     .300   .084000   .342000  -.0844000      .000      .000  
+731024 41979.00 I   .075421  .018676   .326871  .008742  I -.0866712  .0005887  3.5787 0.3893  P    38.511     .500      .080     .300   .085000   .341000  -.0876000      .000      .000  
+731025 41980.00 I   .076229  .021565   .325751  .006749  I -.0901797  .0002710  3.4303 0.3240  P    38.369     .500      .243     .300   .086000   .340000  -.0909000      .000      .000  
+731026 41981.00 I   .076990  .019107   .324625  .017092  I -.0935217  .0002710  3.2517 0.1916  P    38.235     .500      .206     .300   .088000   .339000  -.0941000      .000      .000  
+731027 41982.00 I   .077706  .020657   .323499  .018931  I -.0966847  .0002710  3.0779 0.1916  P    38.179     .500      .031     .300   .089000   .339000  -.0974000      .000      .000  
+731028 41983.00 I   .078381  .018058   .322377  .018836  I -.0996889  .0002710  2.9383 0.1916  P    38.212     .500     -.141     .300   .091000   .338000  -.1006000      .000      .000  
+731029 41984.00 I   .079016  .011901   .321265  .021443  I -.1025792  .0002710  2.8522 0.1916  P    38.373     .500     -.207     .300   .092000   .337000  -.1039000      .000      .000  
+731030 41985.00 I   .079614  .010319   .320168  .018574  I -.1054139  .0002710  2.8278 0.1916  P    38.756     .500     -.167     .300   .093000   .336000  -.1072000      .000      .000  
+731031 41986.00 I   .080175  .005485   .319086  .021421  I -.1082551  .0002710  2.8645 0.1916  P    39.353     .500     -.103     .300   .095000   .335000  -.1105000      .000      .000  
+7311 1 41987.00 I   .080703  .006708   .318022  .018887  I -.1111607  .0002710  2.9548 0.1916  P    39.940     .500     -.083     .300   .096000   .335000  -.1139000      .000      .000  
+7311 2 41988.00 I   .081201  .006187   .316975  .011474  I -.1141787  .0002710  3.0870 0.1916  P    40.252     .500     -.079     .300   .098000   .334000  -.1172000      .000      .000  
+7311 3 41989.00 I   .081671  .010329   .315947  .010024  I -.1173433  .0002710  3.2450 0.2907  P    40.248     .500     -.008     .300   .099000   .333000  -.1205000      .000      .000  
+7311 4 41990.00 I   .082118  .011584   .314936  .004394  I -.1206712  .0005143  3.4103 0.2538  P    40.092     .500      .127     .300   .100000   .332000  -.1238000      .000      .000  
+7311 5 41991.00 I   .082543  .010808   .313944  .007728  I -.1241596  .0004293  3.5625 0.3350  P    39.917     .500      .188     .300   .101000   .331000  -.1271000      .000      .000  
+7311 6 41992.00 I   .082951  .012467   .312970  .008912  I -.1277842  .0004293  3.6787 0.3854  P    39.731     .500      .072     .300   .101000   .330000  -.1305000      .000      .000  
+7311 7 41993.00 I   .083347  .014222   .312016  .009978  I -.1314973  .0006403  3.7357 0.3854  P    39.552     .500     -.116     .300   .102000   .329000  -.1338000      .000      .000  
+7311 8 41994.00 I   .083732  .015484   .311082  .010767  I -.1352296  .0006403  3.7148 0.4528  P    39.482     .500     -.147     .300   .103000   .328000  -.1371000      .000      .000  
+7311 9 41995.00 I   .084109  .013649   .310169  .009652  I -.1389005  .0006403  3.6146 0.4348  P    39.596     .500      .060     .300   .104000   .326000  -.1404000      .000      .000  
+731110 41996.00 I   .084479  .012018   .309275  .011041  I -.1424400  .0005883  3.4579 0.4872  P    39.861     .500      .336     .300   .105000   .325000  -.1437000      .000      .000  
+731111 41997.00 I   .084843  .016474   .308399  .010225  I -.1458132  .0007344  3.2918 0.4705  P    40.194     .500      .487     .300   .105000   .324000  -.1470000      .000      .000  
+731112 41998.00 I   .085205  .018447   .307541  .008893  I -.1490397  .0007344  3.1738 0.4760  P    40.509     .500      .512     .300   .106000   .323000  -.1503000      .000      .000  
+731113 41999.00 I   .085567  .016377   .306698  .017613  I -.1521912  .0006057  3.1469 0.4760  P    40.699     .500      .533     .300   .107000   .322000  -.1536000      .000      .000  
+731114 42000.00 I   .085934  .015599   .305868  .018983  I -.1553673  .0006057  3.2212 0.4283  P    40.689     .500      .589     .300   .108000   .321000  -.1569000      .000      .000  
+731115 42001.00 I   .086310  .014034   .305050  .020813  I -.1586579  .0006057  3.3683 0.4692  P    40.555     .500      .593     .300   .108000   .320000  -.1603000      .000      .000  
+731116 42002.00 I   .086696  .015936   .304242  .023860  I -.1621106  .0007168  3.5353 0.4215  P    40.495     .500      .499     .300   .109000   .318000  -.1636000      .000      .000  
+731117 42003.00 I   .087092  .013880   .303442  .021493  I -.1657170  .0005864  3.6676 0.4631  P    40.625     .500      .398     .300   .109000   .317000  -.1670000      .000      .000  
+731118 42004.00 I   .087500  .006283   .302650  .024463  I -.1694219  .0005864  3.7280 0.3230  P    40.870     .500      .418     .300   .110000   .316000  -.1703000      .000      .000  
+731119 42005.00 I   .087923  .005441   .301867  .023066  I -.1731446  .0002710  3.7030 0.3230  P    41.069     .500      .588     .300   .110000   .315000  -.1736000      .000      .000  
+731120 42006.00 I   .088360  .004710   .301092  .019360  I -.1768022  .0002710  3.6008 0.1916  P    41.114     .500      .828     .300   .111000   .314000  -.1768000      .000      .000  
+731121 42007.00 I   .088813  .014519   .300322  .016766  I -.1803275  .0002710  3.4424 0.1916  P    40.995     .500     1.040     .300   .111000   .312000  -.1801000      .000      .000  
+731122 42008.00 I   .089282  .016180   .299559  .012552  I -.1836772  .0002710  3.2544 0.1916  P    40.787     .500     1.161     .300   .112000   .311000  -.1833000      .000      .000  
+731123 42009.00 I   .089767  .014146   .298800  .011093  I -.1868358  .0002710  3.0648 0.1916  P    40.620     .500     1.162     .300   .112000   .310000  -.1866000      .000      .000  
+731124 42010.00 I   .090270  .016245   .298044  .010837  I -.1898150  .0002710  2.8998 0.1916  P    40.598     .500     1.053     .300   .113000   .309000  -.1898000      .000      .000  
+731125 42011.00 I   .090789  .014532   .297288  .009837  I -.1926504  .0002710  2.7800 0.1916  P    40.715     .500      .905     .300   .113000   .308000  -.1930000      .000      .000  
+731126 42012.00 I   .091322  .016780   .296531  .004253  I -.1953941  .0002710  2.7177 0.1916  P    40.934     .500      .829     .300   .114000   .307000  -.1963000      .000      .000  
+731127 42013.00 I   .091866  .014929   .295769  .005106  I -.1981060  .0002710  2.7160 0.1916  P    41.279     .500      .889     .300   .114000   .306000  -.1995000      .000      .000  
+731128 42014.00 I   .092419  .006188   .295002  .005895  I -.2008443  .0002710  2.7689 0.1916  P    41.744     .500     1.032     .300   .115000   .305000  -.2027000      .000      .000  
+731129 42015.00 I   .092978  .006391   .294225  .005109  I -.2036580  .0002710  2.8644 0.1916  P    42.157     .500     1.153     .300   .116000   .303000  -.2059000      .000      .000  
+731130 42016.00 I   .093541  .007031   .293436  .005320  I -.2065818  .0002710  2.9861 0.1916  P    42.298     .500     1.218     .300   .116000   .302000  -.2090000      .000      .000  
+7312 1 42017.00 I   .094106  .009172   .292631  .005126  I -.2096332  .0002710  3.1166 0.1916  P    42.167     .500     1.301     .300   .117000   .301000  -.2122000      .000      .000  
+7312 2 42018.00 I   .094671  .009720   .291809  .004844  I -.2128122  .0002710  3.2385 0.3071  P    41.979     .500     1.442     .300   .117000   .300000  -.2153000      .000      .000  
+7312 3 42019.00 I   .095233  .009484   .290966  .005468  I -.2161014  .0005511  3.3340 0.3071  P    41.860     .500     1.532     .300   .118000   .299000  -.2185000      .000      .000  
+7312 4 42020.00 I   .095789  .010215   .290100  .004816  I -.2194657  .0005511  3.3858 0.3656  P    41.731     .500     1.430     .300   .119000   .298000  -.2217000      .000      .000  
+7312 5 42021.00 I   .096336  .008982   .289208  .010398  I -.2228531  .0004805  3.3777 0.3634  P    41.515     .500     1.201     .300   .120000   .296000  -.2248000      .000      .000  
+7312 6 42022.00 I   .096871  .009560   .288288  .012004  I -.2261984  .0004739  3.3013 0.3374  P    41.311     .500     1.094     .300   .120000   .295000  -.2280000      .000      .000  
+7312 7 42023.00 I   .097393  .011093   .287336  .010844  I -.2294359  .0004739  3.1654 0.4072  P    41.281     .500     1.245     .300   .121000   .293000  -.2311000      .000      .000  
+7312 8 42024.00 I   .097902  .010067   .286351  .012250  I -.2325201  .0006623  3.0022 0.4372  P    41.489     .500     1.491     .300   .122000   .292000  -.2343000      .000      .000  
+7312 9 42025.00 I   .098396  .008771   .285330  .012807  I -.2354485  .0007348  2.8637 0.4946  P    41.900     .500     1.585     .300   .123000   .291000  -.2374000      .000      .000  
+731210 42026.00 I   .098876  .008782   .284272  .014222  I -.2382734  .0007348  2.8029 0.5603  P    42.391     .500     1.508     .300   .124000   .289000  -.2405000      .000      .000  
+731211 42027.00 I   .099341  .012400   .283177  .020406  I -.2410897  .0008460  2.8481 0.7522  P    42.740     .500     1.444     .300   .124000   .288000  -.2436000      .000      .000  
+731212 42028.00 I   .099789  .014205   .282043  .020838  I -.2440007  .0013128  2.9865 0.7558  P    42.751     .500     1.474     .300   .125000   .286000  -.2467000      .000      .000  
+731213 42029.00 I   .100218  .012509   .280870  .021160  I -.2470769  .0012528  3.1677 0.7808  P    42.499     .500     1.469     .300   .126000   .285000  -.2498000      .000      .000  
+731214 42030.00 I   .100626  .011661   .279656  .024172  I -.2503280  .0008456  3.3248 0.7557  P    42.301     .500     1.330     .300   .127000   .283000  -.2528000      .000      .000  
+731215 42031.00 I   .101013  .015848   .278397  .021122  I -.2537002  .0008456  3.4030 0.5979  P    42.358     .500     1.184     .300   .127000   .282000  -.2558000      .000      .000  
+731216 42032.00 I   .101377  .018266   .277095  .022939  I -.2570999  .0008456  3.3791 0.5979  P    42.537     .500     1.228     .300   .128000   .280000  -.2589000      .000      .000  
+731217 42033.00 I   .101720  .016975   .275751  .021608  I -.2604279  .0008456  3.2639 0.5979  P    42.603     .500     1.462     .300   .128000   .279000  -.2619000      .000      .000  
+731218 42034.00 I   .102043  .016009   .274366  .016421  I -.2636073  .0008456  3.0877 0.4271  P    42.509     .500     1.696     .300   .129000   .277000  -.2649000      .000      .000  
+731219 42035.00 I   .102347  .014896   .272944  .016593  I -.2665943  .0001206  2.8847 0.4440  P    42.350     .500     1.793     .300   .129000   .275000  -.2678000      .000      .000  
+731220 42036.00 I   .102630  .017000   .271487  .014296  I -.2693777  .0002710  2.6847 0.1483  P    42.178     .500     1.784     .300   .130000   .274000  -.2708000      .000      .000  
+731221 42037.00 I   .102890  .015245   .270000  .017143  I -.2719721  .0002710  2.5099 0.1916  P    42.000     .500     1.749     .300   .130000   .272000  -.2737000      .000      .000  
+731222 42038.00 I   .103124  .010535   .268487  .019527  I -.2744117  .0002710  2.3775 0.1916  P    41.879     .500     1.690     .300   .131000   .271000  -.2767000      .000      .000  
+731223 42039.00 I   .103328  .009394   .266952  .017178  I -.2767452  .0002710  2.2993 0.1916  P    41.897     .500     1.591     .300   .131000   .269000  -.2796000      .000      .000  
+731224 42040.00 I   .103502  .008193   .265400  .017236  I -.2790302  .0002710  2.2810 0.1916  P    42.071     .500     1.522     .300   .131000   .267000  -.2823000      .000      .000  
+731225 42041.00 I   .103641  .007382   .263832  .015412  I -.2813262  .0002710  2.3199 0.1916  P    42.365     .500     1.587     .300   .131000   .265000  -.2850000      .000      .000  
+731226 42042.00 I   .103746  .005753   .262251  .014806  I -.2836854  .0002710  2.4051 0.1916  P    42.717     .500     1.779     .300   .131000   .263000  -.2876000      .000      .000  
+731227 42043.00 I   .103816  .007123   .260657  .019758  I -.2861463  .0002710  2.5200 0.1916  P    42.993     .500     1.955     .300   .131000   .261000  -.2903000      .000      .000  
+731228 42044.00 I   .103849  .006838   .259051  .018249  I -.2887288  .0002710  2.6451 0.1916  P    43.040     .500     2.006     .300   .131000   .260000  -.2930000      .000      .000  
+731229 42045.00 I   .103845  .006838   .257437  .018249  I -.2914337  .0002710  2.7618 0.1916  P    42.865     .500     1.983     .300   .131000   .258000  -.2952000      .000      .000  
+731230 42046.00 I   .103805  .007755   .255813  .021940  I -.2942442  .0002710  2.8538 0.1916  P    42.663     .500     2.001     .300   .131000   .256000  -.2974000      .000      .000  
+731231 42047.00 I   .103728  .011371   .254183  .021829  I -.2971285  .0002710  2.9073 0.1916  P    42.577     .500     2.053     .300   .131000   .254000  -.2997000      .000      .000  
+74 1 1 42048.00 I   .103613  .013626   .252548  .026179  I  .6999577  .0002710  2.9112 0.1916  P    42.536     .500     2.028     .300   .131000   .252000   .6981000      .000      .000  
+74 1 2 42049.00 I   .103459  .012568   .250912  .023301  I  .6970678  .0002710  2.8589 0.1916  P    42.386     .500     1.917     .300   .131000   .250000   .6959000      .000      .000  
+74 1 3 42050.00 I   .103264  .013605   .249280  .019039  I  .6942575  .0002710  2.7537 0.1916  P    42.104     .500     1.876     .300   .130000   .248000   .6934000      .000      .000  
+74 1 4 42051.00 I   .103028  .012300   .247655  .015546  I  .6915715  .0002710  2.6152 0.1916  P    41.796     .500     2.011     .300   .129000   .246000   .6909000      .000      .000  
+74 1 5 42052.00 I   .102751  .012300   .246038  .015546  I  .6890254  .0002710  2.4818 0.1916  P    41.601     .500     2.176     .300   .129000   .243000   .6885000      .000      .000  
+74 1 6 42053.00 I   .102434  .016881   .244432  .013563  I  .6865897  .0002710  2.4028 0.1916  P    41.651     .500     2.135     .300   .128000   .241000   .6860000      .000      .000  
+74 1 7 42054.00 I   .102077  .017052   .242835  .009469  I  .6841878  .0002710  2.4196 0.1916  P    41.997     .500     1.890     .300   .127000   .239000   .6835000      .000      .000  
+74 1 8 42055.00 I   .101678  .014768   .241249  .009766  I  .6817150  .0002710  2.5430 0.1916  P    42.478     .500     1.691     .300   .126000   .237000   .6807000      .000      .000  
+74 1 9 42056.00 I   .101236  .016019   .239674  .006413  I  .6790766  .0002710  2.7420 0.1916  P    42.808     .500     1.697     .300   .125000   .235000   .6778000      .000      .000  
+74 110 42057.00 I   .100750  .014258   .238110  .011625  I  .6762273  .0002710  2.9521 0.1916  P    42.893     .500     1.772     .300   .125000   .232000   .6750000      .000      .000  
+74 111 42058.00 I   .100222  .015593   .236557  .013423  I  .6731927  .0002710  3.1015 0.1916  P    42.921     .500     1.726     .300   .124000   .230000   .6721000      .000      .000  
+74 112 42059.00 I   .099653  .014280   .235016  .011750  I  .6700606  .0002710  3.1421 0.1916  P    43.036     .500     1.610     .300   .123000   .228000   .6693000      .000      .000  
+74 113 42060.00 I   .099045  .006574   .233489  .013433  I  .6669475  .0002710  3.0656 0.1916  P    43.106     .500     1.629     .300   .122000   .226000   .6666000      .000      .000  
+74 114 42061.00 I   .098399  .006909   .231975  .012772  I  .6639589  .0002710  2.9004 0.1916  P    42.960     .500     1.814     .300   .120000   .224000   .6639000      .000      .000  
+74 115 42062.00 I   .097719  .007977   .230476  .013416  I  .6611611  .0002710  2.6920 0.1916  P    42.702     .500     1.961     .300   .119000   .222000   .6611000      .000      .000  
+74 116 42063.00 I   .097006  .014655   .228990  .012698  I  .6585751  .0002710  2.4830 0.1916  P    42.551     .500     1.913     .300   .118000   .220000   .6584000      .000      .000  
+74 117 42064.00 I   .096261  .016490   .227517  .008714  I  .6561854  .0002710  2.3030 0.1916  P    42.501     .500     1.745     .300   .117000   .218000   .6557000      .000      .000  
+74 118 42065.00 I   .095487  .016116   .226057  .011481  I  .6539538  .0002710  2.1687 0.1916  P    42.372     .500     1.610     .300   .115000   .216000   .6534000      .000      .000  
+74 119 42066.00 I   .094684  .017820   .224609  .013110  I  .6518298  .0002710  2.0890 0.1916  P    42.140     .500     1.530     .300   .114000   .214000   .6511000      .000      .000  
+74 120 42067.00 I   .093852  .019242   .223173  .011397  I  .6497560  .0002710  2.0688 0.1916  P    42.000     .500     1.434     .300   .113000   .212000   .6489000      .000      .000  
+74 121 42068.00 I   .092995  .021755   .221752  .011668  I  .6476724  .0002710  2.1080 0.1916  P    42.095     .500     1.347     .300   .111000   .210000   .6466000      .000      .000  
+74 122 42069.00 I   .092117  .020908   .220345  .012296  I  .6455229  .0002710  2.1986 0.1916  P    42.370     .500     1.385     .300   .110000   .208000   .6443000      .000      .000  
+74 123 42070.00 I   .091222  .018977   .218956  .012906  I  .6432632  .0002710  2.3253 0.1916  P    42.684     .500     1.572     .300   .109000   .207000   .6420000      .000      .000  
+74 124 42071.00 I   .090317  .016800   .217587  .013798  I  .6408669  .0002710  2.4681 0.1916  P    42.926     .500     1.757     .300   .108000   .205000   .6397000      .000      .000  
+74 125 42072.00 I   .089404  .017376   .216242  .012412  I  .6383288  .0002710  2.6054 0.1916  P    43.013     .500     1.775     .300   .106000   .204000   .6375000      .000      .000  
+74 126 42073.00 I   .088485  .015051   .214922  .011651  I  .6356643  .0002710  2.7180 0.1916  P    42.924     .500     1.642     .300   .105000   .202000   .6352000      .000      .000  
+74 127 42074.00 I   .087567  .011222   .213632  .013404  I  .6329062  .0002710  2.7905 0.1916  P    42.756     .500     1.513     .300   .104000   .201000   .6329000      .000      .000  
+74 128 42075.00 I   .086653  .018446   .212376  .011610  I  .6301000  .0002710  2.8129 0.1916  P    42.646     .500     1.491     .300   .103000   .200000   .6302000      .000      .000  
+74 129 42076.00 I   .085750  .018549   .211155  .010691  I  .6272985  .0002710  2.7812 0.1916  P    42.608     .500     1.536     .300   .102000   .198000   .6276000      .000      .000  
+74 130 42077.00 I   .084863  .026563   .209973  .014861  I  .6245540  .0002710  2.7005 0.1715  P    42.526     .500     1.583     .300   .100000   .197000   .6249000      .000      .000  
+74 131 42078.00 I   .083998  .030407   .208833  .014393  I  .6219084  .0002104  2.5873 0.1715  P    42.286     .500     1.647     .300   .099000   .195000   .6223000      .000      .000  
+74 2 1 42079.00 I   .083160  .026385   .207738  .012579  I  .6193803  .0002104  2.4714 0.1488  P    41.889     .500     1.741     .300   .098000   .194000   .6196000      .000      .000  
+74 2 2 42080.00 I   .082351  .030465   .206691  .013566  I  .6169531  .0002104  2.3930 0.1488  P    41.451     .500     1.752     .300   .097000   .193000   .6167000      .000      .000  
+74 2 3 42081.00 I   .081573  .027073   .205695  .015200  I  .6145682  .0002104  2.3930 0.1488  P    41.179     .500     1.531     .300   .096000   .193000   .6139000      .000      .000  
+74 2 4 42082.00 I   .080822  .025486   .204752  .017550  I  .6121330  .0002104  2.4954 0.1488  P    41.248     .500     1.134     .300   .095000   .192000   .6110000      .000      .000  
+74 2 5 42083.00 I   .080096  .022442   .203862  .015936  I  .6095460  .0002104  2.6918 0.1715  P    41.620     .500      .845     .300   .094000   .192000   .6082000      .000      .000  
+74 2 6 42084.00 I   .079390  .008650   .203027  .012589  I  .6067332  .0002710  2.9366 0.1280  P    42.053     .500      .877     .300   .093000   .191000   .6053000      .000      .000  
+74 2 7 42085.00 I   .078702  .009398   .202247  .012669  I  .6036800  .0001457  3.1598 0.1538  P    42.366     .500     1.122     .300   .092000   .190000   .6024000      .000      .000  
+74 2 8 42086.00 I   .078027  .010682   .201524  .014497  I  .6004433  .0001457  3.2939 0.1030  P    42.585     .500     1.301     .300   .091000   .190000   .5996000      .000      .000  
+74 2 9 42087.00 I   .077360  .016646   .200856  .012674  I  .5971340  .0001457  3.3026 0.1030  P    42.752     .500     1.300     .300   .090000   .189000   .5967000      .000      .000  
+74 210 42088.00 I   .076698  .017896   .200241  .009496  I  .5938779  .0001457  3.1920 0.0910  P    42.759     .500     1.237     .300   .089000   .189000   .5939000      .000      .000  
+74 211 42089.00 I   .076036  .015542   .199679  .011080  I  .5907764  .0001091  3.0024 0.0910  P    42.532     .500     1.215     .300   .088000   .188000   .5910000      .000      .000  
+74 212 42090.00 I   .075371  .017324   .199166  .011534  I  .5878817  .0001091  2.7871 0.0601  P    42.258     .500     1.163     .300   .087000   .188000   .5883000      .000      .000  
+74 213 42091.00 I   .074696  .015656   .198701  .010084  I  .5851957  .0000506  2.5912 0.0601  P    42.183     .500      .995     .300   .086000   .187000   .5856000      .000      .000  
+74 214 42092.00 I   .074011  .016848   .198281  .008948  I  .5826839  .0000506  2.4415 0.0358  P    42.254     .500      .768     .300   .086000   .187000   .5830000      .000      .000  
+74 215 42093.00 I   .073312  .017627   .197903  .008805  I  .5802936  .0000506  2.3492 0.0358  P    42.200     .500      .601     .300   .085000   .186000   .5803000      .000      .000  
+74 216 42094.00 I   .072601  .012607   .197565  .009969  I  .5779652  .0000506  2.3177 0.1378  P    41.966     .500      .495     .300   .084000   .186000   .5776000      .000      .000  
+74 217 42095.00 I   .071874  .011069   .197261  .008685  I  .5756383  .0002710  2.3459 0.1378  P    41.819     .500      .369     .300   .083000   .186000   .5750000      .000      .000  
+74 218 42096.00 I   .071131  .012710   .196990  .005202  I  .5732552  .0002710  2.4287 0.1916  P    41.977     .500      .235     .300   .082000   .185000   .5724000      .000      .000  
+74 219 42097.00 I   .070373  .011307   .196746  .005272  I  .5707666  .0002710  2.5544 0.1916  P    42.344     .500      .216     .300   .082000   .185000   .5697000      .000      .000  
+74 220 42098.00 I   .069596  .011991   .196527  .005875  I  .5681383  .0002710  2.7045 0.1916  P    42.693     .500      .359     .300   .081000   .184000   .5671000      .000      .000  
+74 221 42099.00 I   .068798  .010419   .196328  .014712  I  .5653568  .0002710  2.8570 0.1916  P    42.928     .500      .533     .300   .080000   .184000   .5645000      .000      .000  
+74 222 42100.00 I   .067976  .003786   .196145  .016288  I  .5624308  .0002710  2.9898 0.1916  P    43.065     .500      .565     .300   .079000   .184000   .5617000      .000      .000  
+74 223 42101.00 I   .067127  .013875   .195974  .014403  I  .5593901  .0002710  3.0837 0.1916  P    43.098     .500      .431     .300   .078000   .183000   .5589000      .000      .000  
+74 224 42102.00 I   .066248  .015883   .195811  .016595  I  .5562812  .0002710  3.1243 0.1916  P    43.020     .500      .263     .300   .077000   .183000   .5562000      .000      .000  
+74 225 42103.00 I   .065336  .013868   .195653  .014379  I  .5531613  .0002710  3.1056 0.1916  P    42.899     .500      .188     .300   .076000   .182000   .5534000      .000      .000  
+74 226 42104.00 I   .064391  .015732   .195500  .016299  I  .5500884  .0002710  3.0318 0.1916  P    42.814     .500      .218     .300   .076000   .182000   .5506000      .000      .000  
+74 227 42105.00 I   .063408  .015506   .195349  .015008  I  .5471104  .0002710  2.9198 0.1916  P    42.760     .500      .287     .300   .075000   .182000   .5476000      .000      .000  
+74 228 42106.00 I   .062390  .017878   .195202  .006799  I  .5442519  .0002710  2.7986 0.1916  P    42.654     .500      .333     .300   .074000   .181000   .5446000      .000      .000  
+74 3 1 42107.00 I   .061338  .016163   .195056  .007013  I  .5415040  .0002710  2.7050 0.1916  P    42.447     .500      .309     .300   .073000   .181000   .5416000      .000      .000  
+74 3 2 42108.00 I   .060251  .010292   .194913  .007368  I  .5388205  .0002710  2.6756 0.1916  P    42.193     .500      .150     .300   .072000   .180000   .5386000      .000      .000  
+74 3 3 42109.00 I   .059132  .027041   .194771  .018143  I  .5361227  .0002710  2.7367 0.1916  P    42.030     .500     -.189     .300   .071000   .180000   .5356000      .000      .000  
+74 3 4 42110.00 I   .057982  .031158   .194630  .020943  I  .5333157  .0002710  2.8922 0.1916  P    42.079     .500     -.620     .300   .070000   .180000   .5325000      .000      .000  
+74 3 5 42111.00 I   .056803  .038096   .194488  .020943  I  .5303155  .0002710  3.1162 0.1916  P    42.323     .500     -.906     .300   .068000   .180000   .5293000      .000      .000  
+74 3 6 42112.00 I   .055601  .043151   .194343  .024615  I  .5270781  .0002710  3.3558 0.1916  P    42.607     .500     -.845     .300   .067000   .180000   .5262000      .000      .000  
+74 3 7 42113.00 I   .054379  .037704   .194194  .024615  I  .5236205  .0002710  3.5456 0.1916  P    42.798     .500     -.495     .300   .066000   .180000   .5230000      .000      .000  
+74 3 8 42114.00 I   .053147  .043206   .194042  .033967  I  .5200213  .0002710  3.6321 0.1916  P    42.895     .500     -.139     .300   .065000   .180000   .5199000      .000      .000  
+74 3 9 42115.00 I   .051913  .037538   .193888  .024077  I  .5163977  .0002710  3.5942 0.1916  P    42.943     .500     -.023     .300   .063000   .179000   .5166000      .000      .000  
+74 310 42116.00 I   .050682  .031778   .193733  .002379  I  .5128685  .0002710  3.4494 0.1916  P    42.915     .500     -.159     .300   .062000   .179000   .5134000      .000      .000  
+74 311 42117.00 I   .049458  .027521   .193578  .005314  I  .5095191  .0002710  3.2440 0.1916  P    42.797     .500     -.399     .300   .061000   .179000   .5101000      .000      .000  
+74 312 42118.00 I   .048242  .006752   .193426  .005314  I  .5063829  .0002710  3.0313 0.1916  P    42.704     .500     -.623     .300   .059000   .179000   .5069000      .000      .000  
+74 313 42119.00 I   .047038  .013642   .193276  .004602  I  .5034451  .0002710  2.8527 0.1916  P    42.750     .500     -.791     .300   .058000   .179000   .5036000      .000      .000  
+74 314 42120.00 I   .045847  .014652   .193131  .004602  I  .5006586  .0002710  2.7308 0.1916  P    42.845     .500     -.893     .300   .056000   .179000   .5004000      .000      .000  
+74 315 42121.00 I   .044672  .012816   .192991  .004964  I  .4979625  .0002710  2.6719 0.1916  P    42.795     .500     -.943     .300   .055000   .179000   .4972000      .000      .000  
+74 316 42122.00 I   .043517  .014384   .192859  .005565  I  .4952943  .0002710  2.6744 0.1916  P    42.620     .500    -1.011     .300   .053000   .180000   .4940000      .000      .000  
+74 317 42123.00 I   .042385  .015412   .192733  .020190  I  .4925950  .0002710  2.7329 0.1916  P    42.601     .500    -1.158     .300   .052000   .180000   .4908000      .000      .000  
+74 318 42124.00 I   .041274  .017795   .192617  .022947  I  .4898130  .0002710  2.8379 0.1916  P    42.912     .500    -1.331     .300   .050000   .180000   .4876000      .000      .000  
+74 319 42125.00 I   .040185  .017050   .192508  .024768  I  .4869085  .0002710  2.9749 0.1916  P    43.394     .500    -1.393     .300   .048000   .180000   .4846000      .000      .000  
+74 320 42126.00 I   .039119  .013603   .192409  .028558  I  .4838591  .0002710  3.1242 0.1916  P    43.753     .500    -1.298     .300   .047000   .180000   .4815000      .000      .000  
+74 321 42127.00 I   .038076  .011800   .192317  .027239  I  .4806634  .0002710  3.2638 0.1916  P    43.896     .500    -1.153     .300   .045000   .181000   .4785000      .000      .000  
+74 322 42128.00 I   .037057  .013467   .192231  .031267  I  .4773418  .0002710  3.3726 0.1916  P    43.931     .500    -1.099     .300   .044000   .181000   .4754000      .000      .000  
+74 323 42129.00 I   .036067  .013705   .192146  .027163  I  .4739344  .0002710  3.4327 0.1916  P    43.944     .500    -1.155     .300   .042000   .181000   .4724000      .000      .000  
+74 324 42130.00 I   .035104  .011859   .192061  .021708  I  .4704966  .0002710  3.4322 0.1916  P    43.909     .500    -1.236     .300   .040000   .182000   .4695000      .000      .000  
+74 325 42131.00 I   .034171  .010731   .191977  .018931  I  .4670908  .0002710  3.3694 0.1916  P    43.802     .500    -1.277     .300   .039000   .182000   .4666000      .000      .000  
+74 326 42132.00 I   .033267  .009089   .191893  .013656  I  .4637744  .0002710  3.2569 0.1916  P    43.678     .500    -1.290     .300   .037000   .183000   .4636000      .000      .000  
+74 327 42133.00 I   .032395  .008238   .191811  .012197  I  .4605841  .0002710  3.1231 0.1916  P    43.611     .500    -1.327     .300   .036000   .183000   .4607000      .000      .000  
+74 328 42134.00 I   .031552  .009470   .191732  .004136  I  .4575226  .0002710  3.0061 0.1916  P    43.617     .500    -1.415     .300   .034000   .184000   .4578000      .000      .000  
+74 329 42135.00 I   .030738  .009314   .191656  .007292  I  .4545535  .0002710  2.9443 0.1916  P    43.662     .500    -1.559     .300   .032000   .184000   .4549000      .000      .000  
+74 330 42136.00 I   .029953  .006876   .191584  .008136  I  .4516067  .0002710  2.9645 0.1916  P    43.720     .500    -1.767     .300   .031000   .185000   .4520000      .000      .000  
+74 331 42137.00 I   .029197  .005985   .191517  .008342  I  .4485951  .0002710  3.0730 0.1916  P    43.804     .500    -2.037     .300   .029000   .185000   .4491000      .000      .000  
+74 4 1 42138.00 I   .028470  .005907   .191456  .009467  I  .4454372  .0002710  3.2520 0.1916  P    43.947     .500    -2.323     .300   .028000   .186000   .4462000      .000      .000  
+74 4 2 42139.00 I   .027772  .005426   .191399  .008635  I  .4420806  .0002710  3.4624 0.1916  P    44.140     .500    -2.508     .300   .026000   .186000   .4433000      .000      .000  
+74 4 3 42140.00 I   .027104  .005567   .191349  .009764  I  .4385193  .0002710  3.6522 0.1916  P    44.313     .500    -2.462     .300   .024000   .187000   .4404000      .000      .000  
+74 4 4 42141.00 I   .026463  .015459   .191303  .009925  I  .4347997  .0002710  3.7714 0.1916  P    44.379     .500    -2.172     .300   .023000   .187000   .4375000      .000      .000  
+74 4 5 42142.00 I   .025851  .017106   .191261  .008521  I  .4310112  .0002710  3.7866 0.1916  P    44.321     .500    -1.813     .300   .021000   .188000   .4347000      .000      .000  
+74 4 6 42143.00 I   .025266  .014854   .191223  .007498  I  .4272623  .0002710  3.6944 0.1916  P    44.201     .500    -1.636     .300   .020000   .188000   .4318000      .000      .000  
+74 4 7 42144.00 I   .024707  .017135   .191187  .006940  I  .4236496  .0002710  3.5208 0.1916  P    44.112     .500    -1.758     .300   .018000   .189000   .4289000      .000      .000  
+74 4 8 42145.00 I   .024174  .015766   .191153  .013682  I  .4202328  .0002710  3.3111 0.1916  P    44.124     .500    -2.066     .300   .016000   .190000   .4259000      .000      .000  
+74 4 9 42146.00 I   .023660  .018085   .191121  .015486  I  .4170238  .0002710  3.1123 0.1916  P    44.244     .500    -2.336     .300   .015000   .190000   .4229000      .000      .000  
+74 410 42147.00 I   .023163  .018826   .191091  .017114  I  .4139931  .0002710  2.9587 0.1916  P    44.388     .500    -2.424     .300   .013000   .191000   .4199000      .000      .000  
+74 411 42148.00 I   .022678  .013599   .191064  .018829  I  .4110859  .0002710  2.8666 0.1916  P    44.422     .500    -2.349     .300   .012000   .191000   .4169000      .000      .000  
+74 412 42149.00 I   .022199  .014932   .191040  .017408  I  .4082387  .0002710  2.8380 0.1916  P    44.295     .500    -2.249     .300   .010000   .192000   .4139000      .000      .000  
+74 413 42150.00 I   .021724  .017196   .191017  .020043  I  .4053908  .0002710  2.8667 0.1916  P    44.145     .500    -2.268     .300   .009000   .193000   .4106000      .000      .000  
+74 414 42151.00 I   .021250  .018893   .190994  .019873  I  .4024893  .0002710  2.9434 0.1916  P    44.197     .500    -2.435     .300   .008000   .193000   .4074000      .000      .000  
+74 415 42152.00 I   .020770  .020931   .190971  .018032  I  .3994923  .0002710  3.0552 0.1916  P    44.526     .500    -2.632     .300   .006000   .194000   .4041000      .000      .000  
+74 416 42153.00 I   .020280  .018912   .190949  .017368  I  .3963728  .0002710  3.1853 0.1916  P    44.958     .500    -2.704     .300   .005000   .194000   .4009000      .000      .000  
+74 417 42154.00 I   .019776  .018203   .190925  .015858  I  .3931219  .0002710  3.3148 0.1916  P    45.229     .500    -2.623     .300   .004000   .195000   .3976000      .000      .000  
+74 418 42155.00 I   .019254  .023277   .190901  .014414  I  .3897500  .0002710  3.4239 0.1916  P    45.224     .500    -2.516     .300   .003000   .196000   .3941000      .000      .000  
+74 419 42156.00 I   .018715  .024700   .190875  .015083  I  .3862870  .0002710  3.4942 0.1916  P    45.042     .500    -2.495     .300   .002000   .196000   .3906000      .000      .000  
+74 420 42157.00 I   .018156  .021552   .190850  .017211  I  .3827794  .0002710  3.5110 0.1916  P    44.843     .500    -2.528     .300   .000000   .197000   .3872000      .000      .000  
+74 421 42158.00 I   .017577  .020955   .190827  .016434  I  .3792856  .0002710  3.4661 0.1916  P    44.693     .500    -2.507     .300  -.001000   .197000   .3837000      .000      .000  
+74 422 42159.00 I   .016976  .022133   .190808  .014292  I  .3758664  .0002710  3.3637 0.1916  P    44.557     .500    -2.427     .300  -.002000   .198000   .3802000      .000      .000  
+74 423 42160.00 I   .016356  .024787   .190795  .013974  I  .3725704  .0002710  3.2247 0.1916  P    44.413     .500    -2.400     .300  -.003000   .198000   .3767000      .000      .000  
+74 424 42161.00 I   .015719  .021688   .190790  .016509  I  .3694168  .0002710  3.0862 0.1916  P    44.330     .500    -2.516     .300  -.004000   .199000   .3732000      .000      .000  
+74 425 42162.00 I   .015068  .015365   .190796  .018381  I  .3663835  .0002710  2.9912 0.1916  P    44.397     .500    -2.735     .300  -.005000   .199000   .3696000      .000      .000  
+74 426 42163.00 I   .014407  .013327   .190812  .016118  I  .3634087  .0002710  2.9734 0.1916  P    44.612     .500    -2.942     .300  -.006000   .200000   .3661000      .000      .000  
+74 427 42164.00 I   .013735  .015084   .190839  .013376  I  .3604073  .0002710  3.0437 0.1916  P    44.856     .500    -3.060     .300  -.007000   .200000   .3626000      .000      .000  
+74 428 42165.00 I   .013053  .014065   .190877  .012786  I  .3572975  .0002710  3.1853 0.1916  P    45.006     .500    -3.102     .300  -.007000   .200000   .3592000      .000      .000  
+74 429 42166.00 I   .012362  .007053   .190927  .014687  I  .3540256  .0002710  3.3603 0.1916  P    45.051     .500    -3.122     .300  -.008000   .201000   .3558000      .000      .000  
+74 430 42167.00 I   .011662  .011509   .190986  .013042  I  .3505813  .0002710  3.5220 0.1425  P    45.085     .500    -3.140     .300  -.009000   .201000   .3524000      .000      .000  
+74 5 1 42168.00 I   .010954  .012799   .191053  .007661  I  .3470003  .0000881  3.6277 0.1425  P    45.178     .500    -3.110     .300  -.010000   .202000   .3490000      .000      .000  
+74 5 2 42169.00 I   .010240  .018678   .191129  .007753  I  .3433539  .0000881  3.6495 0.0623  P    45.285     .500    -2.968     .300  -.011000   .202000   .3456000      .000      .000  
+74 5 3 42170.00 I   .009519  .021551   .191213  .008464  I  .3397321  .0000881  3.5789 0.0623  P    45.283     .500    -2.742     .300  -.012000   .202000   .3424000      .000      .000  
+74 5 4 42171.00 I   .008791  .021913   .191305  .008412  I  .3362223  .0000881  3.4296 0.0623  P    45.127     .500    -2.576     .300  -.013000   .203000   .3392000      .000      .000  
+74 5 5 42172.00 I   .008058  .024577   .191406  .007435  I  .3328887  .0000881  3.2328 0.0623  P    44.938     .500    -2.611     .300  -.014000   .203000   .3359000      .000      .000  
+74 5 6 42173.00 I   .007323  .021470   .191517  .007172  I  .3297594  .0000881  3.0277 0.1425  P    44.900     .500    -2.829     .300  -.015000   .203000   .3327000      .000      .000  
+74 5 7 42174.00 I   .006592  .022085   .191636  .007583  I  .3268237  .0002710  2.8512 0.1425  P    45.050     .500    -3.057     .300  -.016000   .204000   .3295000      .000      .000  
+74 5 8 42175.00 I   .005869  .019262   .191765  .010305  I  .3240393  .0002710  2.7278 0.1916  P    45.225     .500    -3.122     .300  -.016000   .204000   .3264000      .000      .000  
+74 5 9 42176.00 I   .005159  .013904   .191901  .010960  I  .3213473  .0002710  2.6669 0.1916  P    45.247     .500    -3.008     .300  -.017000   .204000   .3233000      .000      .000  
+74 510 42177.00 I   .004465  .014818   .192046  .010584  I  .3186855  .0002710  2.6660 0.1916  P    45.125     .500    -2.860     .300  -.018000   .204000   .3201000      .000      .000  
+74 511 42178.00 I   .003788  .010815   .192197  .011254  I  .3159986  .0002710  2.7151 0.1916  P    45.019     .500    -2.846     .300  -.019000   .205000   .3170000      .000      .000  
+74 512 42179.00 I   .003132  .011523   .192354  .010429  I  .3132432  .0002710  2.8005 0.1916  P    45.046     .500    -2.998     .300  -.020000   .205000   .3139000      .000      .000  
+74 513 42180.00 I   .002499  .012901   .192516  .011476  I  .3103906  .0002710  2.9068 0.1916  P    45.195     .500    -3.182     .300  -.021000   .205000   .3109000      .000      .000  
+74 514 42181.00 I   .001889  .011268   .192683  .011647  I  .3074284  .0002710  3.0167 0.1916  P    45.385     .500    -3.228     .300  -.021000   .206000   .3079000      .000      .000  
+74 515 42182.00 I   .001303  .012742   .192856  .009837  I  .3043618  .0002710  3.1127 0.1916  P    45.511     .500    -3.119     .300  -.022000   .206000   .3050000      .000      .000  
+74 516 42183.00 I   .000740  .016443   .193037  .009532  I  .3012128  .0002710  3.1791 0.1916  P    45.452     .500    -3.010     .300  -.022000   .206000   .3020000      .000      .000  
+74 517 42184.00 I   .000202  .016157   .193225  .009587  I  .2980181  .0002710  3.2019 0.1916  P    45.147     .500    -3.032     .300  -.023000   .207000   .2990000      .000      .000  
+74 518 42185.00 I  -.000310  .014963   .193423  .008862  I  .2948268  .0002710  3.1711 0.1916  P    44.695     .500    -3.108     .300  -.023000   .207000   .2960000      .000      .000  
+74 519 42186.00 I  -.000794  .015442   .193631  .009292  I  .2916945  .0002710  3.0844 0.1916  P    44.289     .500    -3.055     .300  -.024000   .207000   .2930000      .000      .000  
+74 520 42187.00 I  -.001250  .013655   .193850  .008115  I  .2886731  .0002710  2.9525 0.1916  P    44.030     .500    -2.848     .300  -.024000   .207000   .2900000      .000      .000  
+74 521 42188.00 I  -.001679  .015677   .194079  .006217  I  .2857950  .0002710  2.8040 0.1916  P    43.889     .500    -2.686     .300  -.025000   .208000   .2870000      .000      .000  
+74 522 42189.00 I  -.002080  .014529   .194321  .015539  I  .2830565  .0002710  2.6813 0.1916  P    43.849     .500    -2.760     .300  -.025000   .208000   .2840000      .000      .000  
+74 523 42190.00 I  -.002456  .009128   .194577  .017250  I  .2804098  .0002710  2.6265 0.1916  P    43.978     .500    -3.022     .300  -.025000   .208000   .2810000      .000      .000  
+74 524 42191.00 I  -.002805  .016048   .194845  .017075  I  .2777733  .0002710  2.6625 0.1916  P    44.281     .500    -3.240     .300  -.026000   .209000   .2779000      .000      .000  
+74 525 42192.00 I  -.003130  .017490   .195128  .019389  I  .2750574  .0002710  2.7807 0.1916  P    44.586     .500    -3.251     .300  -.026000   .209000   .2749000      .000      .000  
+74 526 42193.00 I  -.003430  .017449   .195427  .017028  I  .2721975  .0002710  2.9419 0.1916  P    44.673     .500    -3.086     .300  -.026000   .209000   .2718000      .000      .000  
+74 527 42194.00 I  -.003708  .019894   .195744  .019625  I  .2691772  .0002710  3.0922 0.1916  P    44.498     .500    -2.882     .300  -.027000   .210000   .2688000      .000      .000  
+74 528 42195.00 I  -.003966  .017240   .196082  .019346  I  .2660328  .0002710  3.1835 0.1916  P    44.226     .500    -2.737     .300  -.027000   .210000   .2659000      .000      .000  
+74 529 42196.00 I  -.004202  .018990   .196445  .014688  I  .2628389  .0002710  3.1888 0.1916  P    44.069     .500    -2.659     .300  -.027000   .210000   .2630000      .000      .000  
+74 530 42197.00 I  -.004414  .016925   .196839  .019995  I  .2596849  .0002710  3.1052 0.1916  P    44.081     .500    -2.607     .300  -.027000   .210000   .2600000      .000      .000  
+74 531 42198.00 I  -.004600  .011040   .197266  .021022  I  .2566525  .0002710  2.9497 0.1916  P    44.123     .500    -2.544     .300  -.028000   .211000   .2571000      .000      .000  
+74 6 1 42199.00 I  -.004756  .010111   .197730  .018305  I  .2538000  .0002710  2.7507 0.1916  P    44.013     .500    -2.486     .300  -.028000   .211000   .2542000      .000      .000  
+74 6 2 42200.00 I  -.004877  .006021   .198235  .020882  I  .2511548  .0002710  2.5405 0.1916  P    43.737     .500    -2.491     .300  -.028000   .211000   .2515000      .000      .000  
+74 6 3 42201.00 I  -.004960  .010249   .198781  .018236  I  .2487125  .0002710  2.3498 0.1916  P    43.476     .500    -2.592     .300  -.027000   .211000   .2488000      .000      .000  
+74 6 4 42202.00 I  -.005000  .011813   .199370  .018152  I  .2464406  .0002710  2.2030 0.1916  P    43.390     .500    -2.748     .300  -.027000   .212000   .2462000      .000      .000  
+74 6 5 42203.00 I  -.004992  .011720   .200001  .018491  I  .2442871  .0002710  2.1143 0.1916  P    43.438     .500    -2.852     .300  -.027000   .212000   .2435000      .000      .000  
+74 6 6 42204.00 I  -.004932  .012721   .200676  .011772  I  .2421917  .0002710  2.0864 0.1916  P    43.475     .500    -2.829     .300  -.027000   .212000   .2408000      .000      .000  
+74 6 7 42205.00 I  -.004816  .014625   .201393  .017165  I  .2400963  .0002710  2.1123 0.1755  P    43.459     .500    -2.712     .300  -.026000   .212000   .2384000      .000      .000  
+74 6 8 42206.00 I  -.004643  .016455   .202153  .019698  I  .2379537  .0002230  2.1782 0.2303  P    43.439     .500    -2.624     .300  -.026000   .212000   .2359000      .000      .000  
+74 6 9 42207.00 I  -.004410  .022004   .202956  .018278  I  .2357321  .0003724  2.2674 0.2236  P    43.400     .500    -2.659     .300  -.026000   .213000   .2335000      .000      .000  
+74 610 42208.00 I  -.004117  .023275   .203801  .020931  I  .2334170  .0003877  2.3623 0.2688  P    43.285     .500    -2.770     .300  -.025000   .213000   .2310000      .000      .000  
+74 611 42209.00 I  -.003764  .020291   .204685  .026681  I  .2310113  .0003877  2.4459 0.2741  P    43.153     .500    -2.811     .300  -.025000   .213000   .2286000      .000      .000  
+74 612 42210.00 I  -.003348  .021673   .205605  .025030  I  .2285338  .0003877  2.5038 0.2741  P    43.119     .500    -2.716     .300  -.024000   .213000   .2264000      .000      .000  
+74 613 42211.00 I  -.002866  .019413   .206557  .022434  I  .2260158  .0003877  2.5254 0.2962  P    43.109     .500    -2.602     .300  -.024000   .213000   .2242000      .000      .000  
+74 614 42212.00 I  -.002321  .021726   .207536  .023739  I  .2234977  .0004478  2.5030 0.2845  P    42.864     .500    -2.623     .300  -.023000   .214000   .2220000      .000      .000  
+74 615 42213.00 I  -.001713  .025217   .208538  .021821  I  .2210256  .0004166  2.4333 0.2617  P    42.283     .500    -2.735     .300  -.023000   .214000   .2198000      .000      .000  
+74 616 42214.00 I  -.001045  .021277   .209555  .023255  I  .2186454  .0002710  2.3207 0.2485  P    41.601     .500    -2.723     .300  -.022000   .214000   .2176000      .000      .000  
+74 617 42215.00 I  -.000315  .019995   .210579  .026815  I  .2163925  .0002710  2.1832 0.1916  P    41.133     .500    -2.496     .300  -.021000   .214000   .2155000      .000      .000  
+74 618 42216.00 I   .000476  .022788   .211604  .023660  I  .2142759  .0002710  2.0550 0.1916  P    40.981     .500    -2.251     .300  -.021000   .214000   .2135000      .000      .000  
+74 619 42217.00 I   .001331  .021696   .212624  .024142  I  .2122651  .0002710  1.9791 0.1916  P    41.067     .500    -2.249     .300  -.020000   .215000   .2114000      .000      .000  
+74 620 42218.00 I   .002251  .021628   .213636  .026501  I  .2102885  .0002710  1.9910 0.1916  P    41.313     .500    -2.484     .300  -.020000   .215000   .2094000      .000      .000  
+74 621 42219.00 I   .003234  .018701   .214635  .023710  I  .2082511  .0002710  2.0991 0.1916  P    41.637     .500    -2.689     .300  -.019000   .215000   .2073000      .000      .000  
+74 622 42220.00 I   .004277  .013963   .215616  .024857  I  .2060678  .0002710  2.2747 0.1916  P    41.876     .500    -2.659     .300  -.018000   .215000   .2053000      .000      .000  
+74 623 42221.00 I   .005373  .013889   .216573  .029033  I  .2036984  .0002710  2.4600 0.1916  P    41.859     .500    -2.437     .300  -.017000   .215000   .2032000      .000      .000  
+74 624 42222.00 I   .006518  .015027   .217503  .027734  I  .2011655  .0002710  2.5921 0.1916  P    41.552     .500    -2.187     .300  -.016000   .215000   .2012000      .000      .000  
+74 625 42223.00 I   .007703  .010841   .218405  .026756  I  .1985464  .0002710  2.6277 0.1416  P    41.071     .500    -1.986     .300  -.015000   .215000   .1991000      .000      .000  
+74 626 42224.00 I   .008924  .015788   .219276  .027568  I  .1959452  .0000821  2.5579 0.1788  P    40.592     .500    -1.838     .300  -.014000   .216000   .1971000      .000      .000  
+74 627 42225.00 I   .010171  .016579   .220115  .025758  I  .1934584  .0002334  2.4047 0.1237  P    40.266     .500    -1.767     .300  -.013000   .216000   .1951000      .000      .000  
+74 628 42226.00 I   .011437  .016493   .220919  .027148  I  .1911510  .0002334  2.2059 0.1817  P    40.148     .500    -1.819     .300  -.012000   .216000   .1930000      .000      .000  
+74 629 42227.00 I   .012711  .015665   .221686  .026593  I  .1890489  .0002785  2.0003 0.1817  P    40.115     .500    -1.959     .300  -.011000   .216000   .1910000      .000      .000  
+74 630 42228.00 I   .013984  .019691   .222416  .020071  I  .1871428  .0002785  1.8176 0.1969  P    39.970     .500    -2.094     .300  -.010000   .216000   .1889000      .000      .000  
+74 7 1 42229.00 I   .015250  .022024   .223106  .014871  I  .1853992  .0002785  1.6782 0.2182  P    39.657     .500    -2.176     .300  -.009000   .216000   .1869000      .000      .000  
+74 7 2 42230.00 I   .016503  .021654   .223758  .015564  I  .1837679  .0003361  1.5941 0.2243  P    39.329     .500    -2.247     .300  -.008000   .216000   .1848000      .000      .000  
+74 7 3 42231.00 I   .017736  .018392   .224372  .016211  I  .1821913  .0003518  1.5691 0.2433  P    39.149     .500    -2.332     .300  -.007000   .216000   .1827000      .000      .000  
+74 7 4 42232.00 I   .018940  .022670   .224947  .020112  I  .1806110  .0003518  1.6001 0.2220  P    39.134     .500    -2.365     .300  -.006000   .216000   .1805000      .000      .000  
+74 7 5 42233.00 I   .020113  .023557   .225484  .019281  I  .1789761  .0002710  1.6760 0.2220  P    39.202     .500    -2.263     .300  -.005000   .216000   .1784000      .000      .000  
+74 7 6 42234.00 I   .021252  .023881   .225982  .020073  I  .1772496  .0002710  1.7801 0.1916  P    39.257     .500    -2.069     .300  -.004000   .216000   .1763000      .000      .000  
+74 7 7 42235.00 I   .022358  .020722   .226444  .020776  I  .1754130  .0002710  1.8931 0.2576  P    39.185     .500    -1.942     .300  -.002000   .216000   .1742000      .000      .000  
+74 7 8 42236.00 I   .023430  .019542   .226867  .021383  I  .1734666  .0004381  1.9967 0.2576  P    38.916     .500    -1.978     .300  -.001000   .216000   .1721000      .000      .000  
+74 7 9 42237.00 I   .024465  .019545   .227255  .021367  I  .1714277  .0004381  2.0760 0.2723  P    38.579     .500    -2.095     .300   .000000   .216000   .1699000      .000      .000  
+74 710 42238.00 I   .025459  .023284   .227607  .022373  I  .1693264  .0003235  2.1200 0.2723  P    38.425     .500    -2.147     .300   .001000   .216000   .1678000      .000      .000  
+74 711 42239.00 I   .026407  .018951   .227925  .019316  I  .1672016  .0003235  2.1224 0.2287  P    38.479     .500    -2.122     .300   .002000   .216000   .1657000      .000      .000  
+74 712 42240.00 I   .027305  .019040   .228211  .018536  I  .1650959  .0003235  2.0819 0.2287  P    38.418     .500    -2.139     .300   .003000   .216000   .1636000      .000      .000  
+74 713 42241.00 I   .028151  .020244   .228464  .016780  I  .1630508  .0003235  2.0025 0.1747  P    37.945     .500    -2.237     .300   .004000   .216000   .1616000      .000      .000  
+74 714 42242.00 I   .028942  .020973   .228683  .016039  I  .1610997  .0001319  1.8967 0.1747  P    37.181     .500    -2.280     .300   .005000   .216000   .1595000      .000      .000  
+74 715 42243.00 I   .029674  .018768   .228869  .016737  I  .1592580  .0001319  1.7890 0.1507  P    36.551     .500    -2.161     .300   .006000   .216000   .1575000      .000      .000  
+74 716 42244.00 I   .030340  .019179   .229023  .022235  I  .1575102  .0002710  1.7157 0.1507  P    36.355     .500    -2.000     .300   .007000   .217000   .1554000      .000      .000  
+74 717 42245.00 I   .030935  .014328   .229145  .022155  I  .1558022  .0002710  1.7156 0.1916  P    36.571     .500    -2.015     .300   .007000   .217000   .1534000      .000      .000  
+74 718 42246.00 I   .031459  .014630   .229236  .021440  I  .1540466  .0002710  1.8127 0.1916  P    36.975     .500    -2.212     .300   .008000   .217000   .1515000      .000      .000  
+74 719 42247.00 I   .031910  .013291   .229297  .020905  I  .1521472  .0002710  1.9987 0.1916  P    37.310     .500    -2.356     .300   .009000   .217000   .1495000      .000      .000  
+74 720 42248.00 I   .032289  .014634   .229326  .021094  I  .1500348  .0002710  2.2280 0.1916  P    37.402     .500    -2.274     .300   .010000   .217000   .1476000      .000      .000  
+74 721 42249.00 I   .032598  .014228   .229326  .021864  I  .1476996  .0002710  2.4321 0.1916  P    37.243     .500    -2.058     .300   .011000   .217000   .1456000      .000      .000  
+74 722 42250.00 I   .032838  .014040   .229296  .021349  I  .1452006  .0002710  2.5464 0.1916  P    36.943     .500    -1.879     .300   .012000   .217000   .1437000      .000      .000  
+74 723 42251.00 I   .033012  .016313   .229237  .014736  I  .1426478  .0002710  2.5378 0.1916  P    36.550     .500    -1.748     .300   .012000   .217000   .1418000      .000      .000  
+74 724 42252.00 I   .033124  .019029   .229150  .009664  I  .1401629  .0002710  2.4154 0.1916  P    36.020     .500    -1.579     .300   .013000   .218000   .1399000      .000      .000  
+74 725 42253.00 I   .033179  .021126   .229032  .009283  I  .1378411  .0002710  2.2206 0.1916  P    35.445     .500    -1.427     .300   .013000   .218000   .1380000      .000      .000  
+74 726 42254.00 I   .033183  .021243   .228884  .011548  I  .1357282  .0002710  2.0061 0.1916  P    35.112     .500    -1.465     .300   .014000   .218000   .1361000      .000      .000  
+74 727 42255.00 I   .033140  .018248   .228705  .013064  I  .1338212  .0002710  1.8146 0.1916  P    35.180     .500    -1.731     .300   .015000   .218000   .1342000      .000      .000  
+74 728 42256.00 I   .033052  .017860   .228495  .013045  I  .1320826  .0002710  1.6722 0.1916  P    35.413     .500    -2.030     .300   .015000   .218000   .1323000      .000      .000  
+74 729 42257.00 I   .032919  .017940   .228251  .012110  I  .1304569  .0002710  1.5895 0.1916  P    35.435     .500    -2.161     .300   .016000   .219000   .1305000      .000      .000  
+74 730 42258.00 I   .032742  .016762   .227975  .016195  I  .1288833  .0002710  1.5678 0.1916  P    35.179     .500    -2.138     .300   .016000   .219000   .1286000      .000      .000  
+74 731 42259.00 I   .032525  .018798   .227666  .016409  I  .1273020  .0002710  1.6040 0.3212  P    34.905     .500    -2.096     .300   .017000   .219000   .1267000      .000      .000  
+74 8 1 42260.00 I   .032268  .018396   .227323  .020372  I  .1256589  .0005824  1.6895 0.3212  P    34.827     .500    -2.062     .300   .017000   .219000   .1247000      .000      .000  
+74 8 2 42261.00 I   .031975  .018801   .226948  .020518  I  .1239113  .0005824  1.8101 0.4118  P    34.913     .500    -1.938     .300   .017000   .219000   .1227000      .000      .000  
+74 8 3 42262.00 I   .031645  .019145   .226540  .020465  I  .1220332  .0005824  1.9470 0.4118  P    34.999     .500    -1.700     .300   .017000   .220000   .1206000      .000      .000  
+74 8 4 42263.00 I   .031279  .019682   .226103  .019788  I  .1200186  .0005824  2.0798 0.4118  P    34.949     .500    -1.504     .300   .017000   .220000   .1186000      .000      .000  
+74 8 5 42264.00 I   .030879  .019654   .225640  .020278  I  .1178811  .0005824  2.1901 0.3899  P    34.709     .500    -1.521     .300   .017000   .220000   .1166000      .000      .000  
+74 8 6 42265.00 I   .030451  .019101   .225155  .018481  I  .1156503  .0005186  2.2649 0.3667  P    34.374     .500    -1.735     .300   .017000   .220000   .1143000      .000      .000  
+74 8 7 42266.00 I   .029999  .013944   .224651  .018267  I  .1133655  .0004457  2.2972 0.3419  P    34.161     .500    -1.957     .300   .017000   .220000   .1121000      .000      .000  
+74 8 8 42267.00 I   .029528  .010255   .224133  .014559  I  .1110705  .0004457  2.2856 0.3152  P    34.188     .500    -2.055     .300   .017000   .221000   .1098000      .000      .000  
+74 8 9 42268.00 I   .029038  .009143   .223605  .014356  I  .1088073  .0004457  2.2348 0.3152  P    34.267     .500    -2.070     .300   .017000   .221000   .1076000      .000      .000  
+74 810 42269.00 I   .028531  .007967   .223069  .013214  I  .1066102  .0004457  2.1559 0.3152  P    34.080     .500    -2.100     .300   .017000   .221000   .1053000      .000      .000  
+74 811 42270.00 I   .028009  .009880   .222528  .020923  I  .1044981  .0004457  2.0690 0.2608  P    33.561     .500    -2.146     .300   .017000   .221000   .1029000      .000      .000  
+74 812 42271.00 I   .027475  .010686   .221983  .021551  I  .1024651  .0002710  2.0032 0.2608  P    33.005     .500    -2.159     .300   .018000   .220000   .1005000      .000      .000  
+74 813 42272.00 I   .026931  .009509   .221433  .022808  I  .1004733  .0002710  1.9925 0.1916  P    32.777     .500    -2.181     .300   .018000   .220000   .0980000      .000      .000  
+74 814 42273.00 I   .026380  .010620   .220880  .023672  I  .0984520  .0002710  2.0660 0.1916  P    32.986     .500    -2.301     .300   .019000   .220000   .0956000      .000      .000  
+74 815 42274.00 I   .025824  .010394   .220324  .023604  I  .0963103  .0002710  2.2324 0.1916  P    33.422     .500    -2.475     .300   .019000   .220000   .0932000      .000      .000  
+74 816 42275.00 I   .025264  .011064   .219770  .023051  I  .0939641  .0002710  2.4680 0.1916  P    33.721     .500    -2.510     .300   .019000   .219000   .0908000      .000      .000  
+74 817 42276.00 I   .024699  .011121   .219221  .024604  I  .0913700  .0002710  2.7166 0.1916  P    33.663     .500    -2.309     .300   .020000   .219000   .0884000      .000      .000  
+74 818 42277.00 I   .024131  .010702   .218679  .019065  I  .0885501  .0002710  2.9077 0.1916  P    33.364     .500    -2.032     .300   .020000   .219000   .0860000      .000      .000  
+74 819 42278.00 I   .023558  .011811   .218150  .018781  I  .0855929  .0002710  2.9839 0.1916  P    33.119     .500    -1.890     .300   .021000   .218000   .0836000      .000      .000  
+74 820 42279.00 I   .022981  .012083   .217635  .019144  I  .0826272  .0002710  2.9253 0.1916  P    32.997     .500    -1.863     .300   .021000   .218000   .0812000      .000      .000  
+74 821 42280.00 I   .022400  .011322   .217136  .019905  I  .0797792  .0002710  2.7559 0.1916  P    32.754     .500    -1.769     .300   .021000   .218000   .0789000      .000      .000  
+74 822 42281.00 I   .021818  .012378   .216657  .019415  I  .0771343  .0002710  2.5296 0.1916  P    32.259     .500    -1.581     .300   .021000   .218000   .0766000      .000      .000  
+74 823 42282.00 I   .021237  .024803   .216199  .019773  I  .0747195  .0002710  2.3047 0.1916  P    31.830     .500    -1.506     .300   .020000   .218000   .0743000      .000      .000  
+74 824 42283.00 I   .020664  .025425   .215762  .018400  I  .0725103  .0002710  2.1236 0.1916  P    31.882     .500    -1.688     .300   .020000   .218000   .0720000      .000      .000  
+74 825 42284.00 I   .020105  .024355   .215347  .021438  I  .0704511  .0002710  2.0060 0.1916  P    32.354     .500    -1.987     .300   .020000   .218000   .0697000      .000      .000  
+74 826 42285.00 I   .019567  .023519   .214953  .023762  I  .0684765  .0002710  1.9538 0.1916  P    32.762     .500    -2.140     .300   .020000   .218000   .0675000      .000      .000  
+74 827 42286.00 I   .019055  .023334   .214582  .021494  I  .0665239  .0002710  1.9606 0.7695  P    32.811     .500    -2.079     .300   .020000   .218000   .0653000      .000      .000  
+74 828 42287.00 I   .018573  .023669   .214234  .019573  I  .0645387  .0015149  2.0173 0.7695  P    32.675     .500    -1.945     .300   .019000   .218000   .0632000      .000      .000  
+74 829 42288.00 I   .018120  .023046   .213910  .021051  I  .0624767  .0015149  2.1118 1.0712  P    32.641     .500    -1.847     .300   .019000   .218000   .0610000      .000      .000  
+74 830 42289.00 I   .017700  .008170   .213614  .020349  I  .0603081  .0015149  2.2273 0.9339  P    32.732     .500    -1.744     .300   .019000   .218000   .0588000      .000      .000  
+74 831 42290.00 I   .017314  .007840   .213344  .019573  I  .0580213  .0010926  2.3453 0.9339  P    32.797     .500    -1.590     .300   .019000   .218000   .0566000      .000      .000  
+74 9 1 42291.00 I   .016963  .008178   .213104  .021503  I  .0556227  .0010926  2.4477 0.7066  P    32.753     .500    -1.470     .300   .019000   .218000   .0543000      .000      .000  
+74 9 2 42292.00 I   .016648  .008503   .212894  .017844  I  .0531363  .0008964  2.5188 0.5617  P    32.606     .500    -1.516     .300   .018000   .218000   .0521000      .000      .000  
+74 9 3 42293.00 I   .016370  .011932   .212717  .017893  I  .0505988  .0002616  2.5487 0.4691  P    32.381     .500    -1.725     .300   .018000   .218000   .0498000      .000      .000  
+74 9 4 42294.00 I   .016129  .013144   .212577  .018114  I  .0480536  .0002772  2.5344 0.1906  P    32.152     .500    -1.944     .300   .018000   .218000   .0476000      .000      .000  
+74 9 5 42295.00 I   .015927  .015742   .212477  .017267  I  .0455436  .0002772  2.4794 0.2929  P    32.043     .500    -2.035     .300   .018000   .218000   .0452000      .000      .000  
+74 9 6 42296.00 I   .015762  .015177   .212418  .019806  I  .0431045  .0005161  2.3953 0.2692  P    32.090     .500    -2.002     .300   .018000   .218000   .0428000      .000      .000  
+74 9 7 42297.00 I   .015632  .017581   .212401  .021649  I  .0407563  .0004616  2.3013 0.3629  P    32.160     .500    -1.949     .300   .018000   .218000   .0403000      .000      .000  
+74 9 8 42298.00 I   .015532  .018825   .212427  .015538  I  .0384965  .0005104  2.2236 0.3643  P    32.098     .500    -1.957     .300   .018000   .218000   .0379000      .000      .000  
+74 9 9 42299.00 I   .015456  .019919   .212495  .017464  I  .0362936  .0005636  2.1923 0.4141  P    31.934     .500    -2.042     .300   .018000   .218000   .0355000      .000      .000  
+74 910 42300.00 I   .015400  .020041   .212606  .018153  I  .0340879  .0006522  2.2330 0.4310  P    31.865     .500    -2.204     .300   .018000   .218000   .0330000      .000      .000  
+74 911 42301.00 I   .015358  .020773   .212761  .019557  I  .0317988  .0006522  2.3600 0.3261  P    32.036     .500    -2.418     .300   .018000   .218000   .0304000      .000      .000  
+74 912 42302.00 I   .015324  .018865   .212960  .019760  I  .0293420  .0000072  2.5648 0.3531  P    32.361     .500    -2.583     .300   .019000   .218000   .0279000      .000      .000  
+74 913 42303.00 I   .015293  .021151   .213203  .020303  I  .0266552  .0002710  2.8116 0.1355  P    32.561     .500    -2.543     .300   .019000   .218000   .0253000      .000      .000  
+74 914 42304.00 I   .015256  .018523   .213488  .019536  I  .0237243  .0002710  3.0420 0.1916  P    32.438     .500    -2.256     .300   .019000   .218000   .0228000      .000      .000  
+74 915 42305.00 I   .015206  .019698   .213815  .022084  I  .0205976  .0002710  3.1934 0.1916  P    32.118     .500    -1.903     .300   .019000   .218000   .0201000      .000      .000  
+74 916 42306.00 I   .015133  .019916   .214184  .020900  I  .0173782  .0002710  3.2229 0.1916  P    31.946     .500    -1.726     .300   .019000   .218000   .0175000      .000      .000  
+74 917 42307.00 I   .015032  .020853   .214591  .023551  I  .0141941  .0002710  3.1256 0.1916  P    32.054     .500    -1.762     .300   .020000   .219000   .0148000      .000      .000  
+74 918 42308.00 I   .014897  .020123   .215036  .025793  I  .0111579  .0002710  2.9358 0.1916  P    32.171     .500    -1.814     .300   .020000   .219000   .0122000      .000      .000  
+74 919 42309.00 I   .014725  .020191   .215514  .025145  I  .0083342  .0002710  2.7111 0.1916  P    32.000     .500    -1.735     .300   .020000   .219000   .0095000      .000      .000  
+74 920 42310.00 I   .014510  .021988   .216019  .026337  I  .0057286  .0002710  2.5078 0.1916  P    31.682     .500    -1.606     .300   .020000   .219000   .0068000      .000      .000  
+74 921 42311.00 I   .014246  .022170   .216544  .036599  I  .0032992  .0002710  2.3628 0.1916  P    31.644     .500    -1.598     .300   .020000   .220000   .0041000      .000      .000  
+74 922 42312.00 I   .013932  .019840   .217082  .040434  I  .0009794  .0002710  2.2886 0.1916  P    32.035     .500    -1.718     .300   .019000   .220000   .0013000      .000      .000  
+74 923 42313.00 I   .013565  .024273   .217628  .040316  I -.0013001  .0002710  2.2806 0.1916  P    32.555     .500    -1.817     .300   .019000   .221000  -.0014000      .000      .000  
+74 924 42314.00 I   .013142  .022758   .218176  .039421  I -.0036001  .0002710  2.3274 0.3591  P    32.888     .500    -1.805     .300   .019000   .221000  -.0041000      .000      .000  
+74 925 42315.00 I   .012664  .022647   .218720  .037207  I -.0059688  .0006651  2.4157 0.3591  P    33.047     .500    -1.740     .300   .019000   .222000  -.0068000      .000      .000  
+74 926 42316.00 I   .012129  .024434   .219255  .038349  I -.0084405  .0006651  2.5308 0.4703  P    33.191     .500    -1.701     .300   .019000   .222000  -.0095000      .000      .000  
+74 927 42317.00 I   .011540  .021385   .219774  .036125  I -.0110335  .0006651  2.6552 0.4703  P    33.322     .500    -1.682     .300   .018000   .223000  -.0122000      .000      .000  
+74 928 42318.00 I   .010901  .021482   .220274  .027141  I -.0137479  .0006651  2.7706 0.4703  P    33.336     .500    -1.655     .300   .018000   .223000  -.0149000      .000      .000  
+74 929 42319.00 I   .010218  .023056   .220751  .018389  I -.0165659  .0006651  2.8596 0.4101  P    33.242     .500    -1.647     .300   .018000   .224000  -.0176000      .000      .000  
+74 930 42320.00 I   .009497  .016849   .221202  .019571  I -.0194541  .0004800  2.9096 0.3394  P    33.135     .500    -1.698     .300   .018000   .225000  -.0203000      .000      .000  
+7410 1 42321.00 I   .008739  .016810   .221628  .019070  I -.0223698  .0001360  2.9138 0.2494  P    33.024     .500    -1.776     .300   .018000   .225000  -.0230000      .000      .000  
+7410 2 42322.00 I   .007946  .021855   .222027  .020845  I -.0252664  .0001360  2.8722 0.2058  P    32.856     .500    -1.794     .300   .017000   .226000  -.0258000      .000      .000  
+7410 3 42323.00 I   .007119  .024480   .222401  .021298  I -.0281018  .0003884  2.7934 0.2058  P    32.676     .500    -1.713     .300   .017000   .226000  -.0285000      .000      .000  
+7410 4 42324.00 I   .006262  .026627   .222753  .026640  I -.0308468  .0003884  2.6952 0.2746  P    32.634     .500    -1.580     .300   .017000   .227000  -.0312000      .000      .000  
+7410 5 42325.00 I   .005380  .026705   .223082  .026249  I -.0334946  .0003884  2.6041 0.3201  P    32.805     .500    -1.479     .300   .016000   .228000  -.0341000      .000      .000  
+7410 6 42326.00 I   .004477  .027347   .223392  .028180  I -.0360675  .0005089  2.5504 0.3201  P    33.102     .500    -1.466     .300   .016000   .229000  -.0370000      .000      .000  
+7410 7 42327.00 I   .003560  .027406   .223685  .028061  I -.0386168  .0005089  2.5607 0.3599  P    33.393     .500    -1.554     .300   .015000   .230000  -.0398000      .000      .000  
+7410 8 42328.00 I   .002633  .028959   .223963  .029114  I -.0412151  .0005091  2.6495 0.3557  P    33.640     .500    -1.722     .300   .015000   .231000  -.0427000      .000      .000  
+7410 9 42329.00 I   .001704  .031157   .224230  .028425  I -.0439406  .0004972  2.8127 0.3558  P    33.881     .500    -1.911     .300   .014000   .232000  -.0456000      .000      .000  
+741010 42330.00 I   .000778  .029067   .224491  .028453  I -.0468567  .0004972  3.0247 0.3516  P    34.116     .500    -2.027     .300   .013000   .233000  -.0487000      .000      .000  
+741011 42331.00 I  -.000138  .024667   .224747  .019351  I -.0499919  .0004972  3.2426 0.3560  P    34.245     .500    -1.963     .300   .012000   .234000  -.0518000      .000      .000  
+741012 42332.00 I  -.001038  .028001   .225001  .015849  I -.0533268  .0005096  3.4152 0.3560  P    34.178     .500    -1.693     .300   .012000   .236000  -.0548000      .000      .000  
+741013 42333.00 I  -.001917  .024583   .225253  .014307  I -.0567922  .0005096  3.4976 0.2886  P    33.988     .500    -1.340     .300   .011000   .237000  -.0579000      .000      .000  
+741014 42334.00 I  -.002775  .028298   .225504  .013369  I -.0602843  .0002710  3.4671 0.2886  P    33.896     .500    -1.110     .300   .010000   .238000  -.0610000      .000      .000  
+741015 42335.00 I  -.003611  .028052   .225755  .014799  I -.0636919  .0002710  3.3332 0.1916  P    34.031     .500    -1.114     .300   .009000   .239000  -.0641000      .000      .000  
+741016 42336.00 I  -.004427  .016520   .226006  .013857  I -.0669295  .0002710  3.1357 0.1916  P    34.266     .500    -1.259     .300   .008000   .240000  -.0673000      .000      .000  
+741017 42337.00 I  -.005222  .014475   .226259  .014188  I -.0699599  .0002710  2.9279 0.1916  P    34.370     .500    -1.351     .300   .007000   .242000  -.0704000      .000      .000  
+741018 42338.00 I  -.005998  .016631   .226515  .014539  I -.0727978  .0002710  2.7575 0.1916  P    34.319     .500    -1.298     .300   .006000   .243000  -.0736000      .000      .000  
+741019 42339.00 I  -.006755  .016868   .226777  .012659  I -.0754965  .0002710  2.6520 0.1916  P    34.331     .500    -1.169     .300   .005000   .244000  -.0767000      .000      .000  
+741020 42340.00 I  -.007498  .018912   .227044  .013854  I -.0781253  .0002710  2.6170 0.1916  P    34.583     .500    -1.071     .300   .004000   .245000  -.0798000      .000      .000  
+741021 42341.00 I  -.008231  .016527   .227318  .012244  I -.0807508  .0002710  2.6428 0.1916  P    35.022     .500    -1.042     .300   .003000   .246000  -.0830000      .000      .000  
+741022 42342.00 I  -.008959  .010758   .227600  .009307  I -.0834263  .0002710  2.7146 0.1916  P    35.482     .500    -1.066     .300   .002000   .247000  -.0861000      .000      .000  
+741023 42343.00 I  -.009685  .011828   .227891  .009580  I -.0861902  .0002710  2.8169 0.1916  P    35.866     .500    -1.121     .300   .001000   .248000  -.0893000      .000      .000  
+741024 42344.00 I  -.010412  .013420   .228192  .006781  I -.0890648  .0002710  2.9334 0.1916  P    36.134     .500    -1.181     .300   .000000   .249000  -.0924000      .000      .000  
+741025 42345.00 I  -.011141  .012912   .228506  .006374  I -.0920563  .0002710  3.0476 0.1916  P    36.232     .500    -1.215     .300  -.001000   .250000  -.0955000      .000      .000  
+741026 42346.00 I  -.011873  .010931   .228834  .007203  I -.0951537  .0002710  3.1427 0.1916  P    36.132     .500    -1.223     .300  -.002000   .251000  -.0986000      .000      .000  
+741027 42347.00 I  -.012609  .016719   .229178  .006323  I -.0983305  .0002710  3.2043 0.1916  P    35.930     .500    -1.238     .300  -.003000   .252000  -.1017000      .000      .000  
+741028 42348.00 I  -.013350  .019137   .229538  .006734  I -.1015481  .0002710  3.2233 0.1916  P    35.772     .500    -1.259     .300  -.004000   .253000  -.1048000      .000      .000  
+741029 42349.00 I  -.014093  .016633   .229916  .009104  I -.1047621  .0002710  3.1972 0.1916  P    35.697     .500    -1.210     .300  -.005000   .254000  -.1079000      .000      .000  
+741030 42350.00 I  -.014838  .017265   .230312  .008647  I -.1079286  .0002710  3.1295 0.1916  P    35.635     .500    -1.031     .300  -.006000   .255000  -.1110000      .000      .000  
+741031 42351.00 I  -.015582  .017431   .230727  .007820  I -.1110115  .0002710  3.0331 0.2168  P    35.555     .500     -.777     .300  -.007000   .256000  -.1140000      .000      .000  
+7411 1 42352.00 I  -.016326  .019051   .231161  .008565  I -.1139930  .0003384  2.9314 0.2168  P    35.548     .500     -.580     .300  -.008000   .256000  -.1171000      .000      .000  
+7411 2 42353.00 I  -.017073  .016536   .231616  .007419  I -.1168828  .0003384  2.8552 0.2393  P    35.724     .500     -.519     .300  -.009000   .257000  -.1201000      .000      .000  
+7411 3 42354.00 I  -.017822  .010554   .232090  .008483  I -.1197222  .0003384  2.8354 0.2393  P    36.079     .500     -.567     .300  -.010000   .258000  -.1232000      .000      .000  
+7411 4 42355.00 I  -.018574  .010175   .232584  .008942  I -.1225786  .0003384  2.8906 0.2393  P    36.484     .500     -.651     .300  -.011000   .259000  -.1262000      .000      .000  
+7411 5 42356.00 I  -.019330  .011635   .233097  .006439  I -.1255277  .0003384  3.0187 0.2393  P    36.804     .500     -.726     .300  -.012000   .259000  -.1293000      .000      .000  
+7411 6 42357.00 I  -.020086  .010298   .233628  .006083  I -.1286323  .0003384  3.1958 0.2168  P    36.995     .500     -.776     .300  -.012000   .260000  -.1323000      .000      .000  
+7411 7 42358.00 I  -.020843  .005859   .234176  .006523  I -.1319226  .0002710  3.3827 0.2168  P    37.092     .500     -.780     .300  -.013000   .260000  -.1354000      .000      .000  
+7411 8 42359.00 I  -.021598  .019559   .234741  .012920  I -.1353865  .0002710  3.5358 0.1916  P    37.136     .500     -.700     .300  -.014000   .261000  -.1384000      .000      .000  
+7411 9 42360.00 I  -.022348  .022548   .235320  .014917  I -.1389706  .0002710  3.6179 0.1916  P    37.135     .500     -.509     .300  -.015000   .261000  -.1415000      .000      .000  
+741110 42361.00 I  -.023091  .019937   .235914  .012975  I -.1425916  .0002710  3.6078 0.1916  P    37.085     .500     -.240     .300  -.015000   .262000  -.1445000      .000      .000  
+741111 42362.00 I  -.023823  .022435   .236519  .013778  I -.1461556  .0002710  3.5061 0.1916  P    37.016     .500     -.005     .300  -.016000   .262000  -.1476000      .000      .000  
+741112 42363.00 I  -.024543  .024254   .237136  .022801  I -.1495809  .0002710  3.3359 0.1916  P    36.982     .500      .067     .300  -.016000   .263000  -.1506000      .000      .000  
+741113 42364.00 I  -.025251  .027899   .237761  .026178  I -.1528176  .0002710  3.1363 0.1916  P    37.025     .500     -.047     .300  -.017000   .263000  -.1537000      .000      .000  
+741114 42365.00 I  -.025948  .024211   .238390  .022738  I -.1558584  .0002710  2.9512 0.1916  P    37.146     .500     -.221     .300  -.018000   .263000  -.1568000      .000      .000  
+741115 42366.00 I  -.026638  .017488   .239023  .022569  I -.1587363  .0002710  2.8149 0.1916  P    37.319     .500     -.280     .300  -.018000   .264000  -.1599000      .000      .000  
+741116 42367.00 I  -.027322  .015223   .239655  .021575  I -.1615100  .0002710  2.7439 0.1916  P    37.523     .500     -.160     .300  -.019000   .264000  -.1629000      .000      .000  
+741117 42368.00 I  -.027999  .016952   .240288  .024873  I -.1642452  .0002710  2.7364 0.1916  P    37.778     .500      .047     .300  -.019000   .265000  -.1660000      .000      .000  
+741118 42369.00 I  -.028670  .015219   .240920  .021700  I -.1669996  .0002710  2.7795 0.1916  P    38.116     .500      .197     .300  -.020000   .265000  -.1691000      .000      .000  
+741119 42370.00 I  -.029332  .005271   .241553  .011159  I -.1698154  .0002710  2.8561 0.1916  P    38.519     .500      .215     .300  -.021000   .265000  -.1722000      .000      .000  
+741120 42371.00 I  -.029984  .007877   .242187  .009697  I -.1727172  .0002710  2.9489 0.1916  P    38.874     .500      .129     .300  -.021000   .266000  -.1753000      .000      .000  
+741121 42372.00 I  -.030624  .008918   .242822  .011014  I -.1757133  .0002710  3.0422 0.1916  P    39.038     .500      .037     .300  -.022000   .266000  -.1783000      .000      .000  
+741122 42373.00 I  -.031252  .007784   .243455  .011306  I -.1787966  .0002710  3.1207 0.1916  P    38.959     .500      .013     .300  -.022000   .267000  -.1814000      .000      .000  
+741123 42374.00 I  -.031866  .008811   .244087  .007691  I -.1819454  .0002710  3.1714 0.1916  P    38.721     .500      .042     .300  -.023000   .267000  -.1845000      .000      .000  
+741124 42375.00 I  -.032463  .008922   .244716  .016418  I -.1851271  .0002710  3.1852 0.2967  P    38.457     .500      .046     .300  -.024000   .267000  -.1876000      .000      .000  
+741125 42376.00 I  -.033043  .009202   .245339  .018715  I -.1883019  .0005279  3.1574 0.2967  P    38.251     .500      .007     .300  -.025000   .268000  -.1906000      .000      .000  
+741126 42377.00 I  -.033605  .011661   .245957  .018537  I -.1914286  .0005279  3.0896 0.3733  P    38.129     .500      .026     .300  -.025000   .268000  -.1937000      .000      .000  
+741127 42378.00 I  -.034149  .011242   .246568  .021384  I -.1944705  .0005279  2.9898 0.3733  P    38.095     .500      .205     .300  -.026000   .269000  -.1967000      .000      .000  
+741128 42379.00 I  -.034674  .010017   .247172  .018721  I -.1974034  .0005279  2.8754 0.3733  P    38.147     .500      .477     .300  -.027000   .269000  -.1998000      .000      .000  
+741129 42380.00 I  -.035178  .011512   .247771  .020449  I -.2002257  .0005279  2.7742 0.3733  P    38.287     .500      .641     .300  -.028000   .269000  -.2029000      .000      .000  
+741130 42381.00 I  -.035660  .011259   .248366  .019127  I -.2029672  .0005279  2.7191 0.2967  P    38.522     .500      .578     .300  -.029000   .270000  -.2059000      .000      .000  
+7412 1 42382.00 I  -.036119  .011855   .248959  .013694  I -.2056880  .0002710  2.7362 0.2967  P    38.831     .500      .381     .300  -.029000   .270000  -.2090000      .000      .000  
+7412 2 42383.00 I  -.036555  .014117   .249549  .011865  I -.2084660  .0002710  2.8324 0.1916  P    39.120     .500      .242     .300  -.030000   .271000  -.2120000      .000      .000  
+7412 3 42384.00 I  -.036969  .013003   .250136  .008934  I -.2113722  .0002710  2.9868 0.1916  P    39.263     .500      .251     .300  -.031000   .271000  -.2151000      .000      .000  
+7412 4 42385.00 I  -.037360  .011264   .250720  .008184  I -.2144449  .0002710  3.1571 0.1916  P    39.218     .500      .355     .300  -.031000   .271000  -.2180000      .000      .000  
+7412 5 42386.00 I  -.037727  .012718   .251301  .008905  I -.2176753  .0002710  3.2941 0.1916  P    39.079     .500      .466     .300  -.032000   .272000  -.2210000      .000      .000  
+7412 6 42387.00 I  -.038067  .013288   .251878  .018719  I -.2210090  .0002710  3.3588 0.1916  P    38.979     .500      .560     .300  -.032000   .272000  -.2239000      .000      .000  
+7412 7 42388.00 I  -.038382  .014103   .252448  .019939  I -.2243632  .0002710  3.3343 0.1916  P    38.961     .500      .669     .300  -.033000   .273000  -.2269000      .000      .000  
+7412 8 42389.00 I  -.038673  .015565   .253012  .017587  I -.2276498  .0002710  3.2262 0.1916  P    38.957     .500      .815     .300  -.033000   .273000  -.2298000      .000      .000  
+7412 9 42390.00 I  -.038940  .014067   .253568  .020304  I -.2307956  .0002710  3.0579 0.1916  P    38.867     .500      .961     .300  -.033000   .273000  -.2326000      .000      .000  
+741210 42391.00 I  -.039184  .014547   .254115  .018221  I -.2337566  .0002710  2.8625 0.1916  P    38.676     .500     1.027     .300  -.034000   .274000  -.2354000      .000      .000  
+741211 42392.00 I  -.039408  .016796   .254656  .020813  I -.2365234  .0002710  2.6754 0.1916  P    38.495     .500      .963     .300  -.034000   .274000  -.2381000      .000      .000  
+741212 42393.00 I  -.039614  .016993   .255193  .025832  I -.2391202  .0002710  2.5268 0.1916  P    38.486     .500      .819     .300  -.035000   .275000  -.2409000      .000      .000  
+741213 42394.00 I  -.039809  .017645   .255726  .022401  I -.2415963  .0002710  2.4361 0.1916  P    38.704     .500      .724     .300  -.035000   .275000  -.2437000      .000      .000  
+741214 42395.00 I  -.039996  .015983   .256257  .019487  I -.2440136  .0002710  2.4090 0.1916  P    39.036     .500      .776     .300  -.035000   .275000  -.2463000      .000      .000  
+741215 42396.00 I  -.040178  .014713   .256789  .022169  I -.2464328  .0002710  2.4375 0.1916  P    39.343     .500      .954     .300  -.035000   .276000  -.2490000      .000      .000  
+741216 42397.00 I  -.040360  .014467   .257323  .021215  I -.2489018  .0002710  2.5056 0.1916  P    39.606     .500     1.142     .300  -.036000   .276000  -.2516000      .000      .000  
+741217 42398.00 I  -.040542  .013955   .257858  .023867  I -.2514511  .0002710  2.5949 0.1916  P    39.857     .500     1.228     .300  -.036000   .277000  -.2543000      .000      .000  
+741218 42399.00 I  -.040724  .012341   .258394  .021131  I -.2540928  .0002710  2.6877 0.1916  P    40.025     .500     1.194     .300  -.036000   .277000  -.2569000      .000      .000  
+741219 42400.00 I  -.040910  .010006   .258930  .011782  I -.2568225  .0002710  2.7686 0.1916  P    39.984     .500     1.121     .300  -.036000   .277000  -.2595000      .000      .000  
+741220 42401.00 I  -.041101  .011267   .259466  .012572  I -.2596221  .0002710  2.8257 0.1916  P    39.751     .500     1.100     .300  -.036000   .278000  -.2621000      .000      .000  
+741221 42402.00 I  -.041297  .011834   .260000  .014361  I -.2624630  .0002710  2.8502 0.1916  P    39.503     .500     1.135     .300  -.037000   .278000  -.2648000      .000      .000  
+741222 42403.00 I  -.041498  .012767   .260534  .013773  I -.2653103  .0002710  2.8382 0.1916  P    39.346     .500     1.128     .300  -.037000   .279000  -.2674000      .000      .000  
+741223 42404.00 I  -.041705  .012439   .261069  .012013  I -.2681275  .0002710  2.7907 0.1916  P    39.210     .500     1.013     .300  -.037000   .279000  -.2700000      .000      .000  
+741224 42405.00 I  -.041917  .016573   .261606  .010522  I -.2708818  .0002710  2.7137 0.1916  P    39.019     .500      .889     .300  -.037000   .279000  -.2726000      .000      .000  
+741225 42406.00 I  -.042136  .018919   .262144  .011042  I -.2735492  .0002710  2.6195 0.1916  P    38.851     .500      .924     .300  -.037000   .279000  -.2752000      .000      .000  
+741226 42407.00 I  -.042362  .021855   .262684  .009594  I -.2761222  .0002710  2.5293 0.1916  P    38.830     .500     1.117     .300  -.038000   .280000  -.2777000      .000      .000  
+741227 42408.00 I  -.042596  .023827   .263227  .007128  I -.2786190  .0002710  2.4727 0.1916  P    38.977     .500     1.240     .300  -.038000   .280000  -.2803000      .000      .000  
+741228 42409.00 I  -.042837  .023827   .263773  .007128  I -.2810892  .0002710  2.4807 0.1916  P    39.237     .500     1.096     .300  -.038000   .280000  -.2829000      .000      .000  
+741229 42410.00 I  -.043081  .027122   .264322  .002483  I -.2836080  .0002710  2.5713 0.1916  P    39.556     .500      .777     .300  -.038000   .280000  -.2855000      .000      .000  
+741230 42411.00 I  -.043325  .028310   .264876  .023533  I -.2862566  .0002710  2.7363 0.1916  P    39.851     .500      .568     .300  -.038000   .280000  -.2882000      .000      .000  
+741231 42412.00 I  -.043569  .029749   .265436  .028736  I -.2890927  .0002710  2.9375 0.1916  P    39.981     .500      .627     .300  -.039000   .281000  -.2908000      .000      .000  
+75 1 1 42413.00 I  -.043813  .024593   .266001  .023470  I  .7078756  .0002710  3.1172 0.1916  P    39.881     .500      .847     .300  -.039000   .281000   .7065000      .000      .000  
+75 1 2 42414.00 I  -.044056  .022109   .266573  .028724  I  .7046980  .0002710  3.2216 0.1916  P    39.658     .500     1.037     .300  -.039000   .281000   .7039000      .000      .000  
+75 1 3 42415.00 I  -.044299  .018390   .267153  .024374  I  .7014674  .0002710  3.2212 0.1916  P    39.483     .500     1.129     .300  -.039000   .281000   .7012000      .000      .000  
+75 1 4 42416.00 I  -.044538  .018390   .267744  .024374  I  .6982891  .0002710  3.1205 0.1916  P    39.405     .500     1.174     .300  -.039000   .282000   .6986000      .000      .000  
+75 1 5 42417.00 I  -.044768  .020644   .268350  .029016  I  .6952497  .0002710  2.9500 0.1916  P    39.348     .500     1.204     .300  -.040000   .282000   .6959000      .000      .000  
+75 1 6 42418.00 I  -.044988  .016038   .268975  .023935  I  .6923984  .0002710  2.7517 0.1916  P    39.226     .500     1.192     .300  -.040000   .283000   .6933000      .000      .000  
+75 1 7 42419.00 I  -.045193  .015936   .269624  .020759  I  .6897425  .0002710  2.5648 0.1916  P    39.023     .500     1.112     .300  -.040000   .283000   .6906000      .000      .000  
+75 1 8 42420.00 I  -.045381  .017994   .270298  .023963  I  .6872556  .0002710  2.4175 0.1916  P    38.799     .500      .992     .300  -.040000   .283000   .6879000      .000      .000  
+75 1 9 42421.00 I  -.045545  .016918   .271002  .020839  I  .6848888  .0002710  2.3263 0.1916  P    38.672     .500      .884     .300  -.040000   .284000   .6853000      .000      .000  
+75 110 42422.00 I  -.045681  .017869   .271735  .020617  I  .6825825  .0002710  2.2967 0.1916  P    38.736     .500      .825     .300  -.039000   .284000   .6826000      .000      .000  
+75 111 42423.00 I  -.045780  .016065   .272497  .018874  I  .6802763  .0002710  2.3246 0.1916  P    38.961     .500      .838     .300  -.039000   .285000   .6800000      .000      .000  
+75 112 42424.00 I  -.045838  .011104   .273288  .007385  I  .6779183  .0002710  2.3977 0.1916  P    39.219     .500      .932     .300  -.039000   .285000   .6773000      .000      .000  
+75 113 42425.00 I  -.045849  .017543   .274108  .008335  I  .6754717  .0002710  2.4984 0.1916  P    39.431     .500     1.082     .300  -.039000   .286000   .6746000      .000      .000  
+75 114 42426.00 I  -.045810  .018481   .274956  .009561  I  .6729187  .0002710  2.6074 0.1916  P    39.588     .500     1.210     .300  -.038000   .286000   .6719000      .000      .000  
+75 115 42427.00 I  -.045718  .017283   .275833  .009310  I  .6702599  .0002710  2.7072 0.1916  P    39.642     .500     1.239     .300  -.038000   .287000   .6691000      .000      .000  
+75 116 42428.00 I  -.045572  .017033   .276739  .010330  I  .6675120  .0002710  2.7838 0.1916  P    39.522     .500     1.167     .300  -.037000   .287000   .6664000      .000      .000  
+75 117 42429.00 I  -.045372  .018276   .277675  .012408  I  .6647030  .0002710  2.8284 0.1916  P    39.297     .500     1.079     .300  -.037000   .288000   .6637000      .000      .000  
+75 118 42430.00 I  -.045120  .019795   .278643  .014775  I  .6618672  .0002710  2.8371 0.3211  P    39.166     .500     1.035     .300  -.036000   .289000   .6610000      .000      .000  
+75 119 42431.00 I  -.044818  .022021   .279643  .014884  I  .6590403  .0005823  2.8112 0.3211  P    39.193     .500      .979     .300  -.036000   .289000   .6582000      .000      .000  
+75 120 42432.00 I  -.044467  .019029   .280677  .014510  I  .6562544  .0005823  2.7565 0.4117  P    39.185     .500      .819     .300  -.035000   .290000   .6555000      .000      .000  
+75 121 42433.00 I  -.044070  .018518   .281746  .013774  I  .6535334  .0005823  2.6836 0.4117  P    38.957     .500      .601     .300  -.035000   .290000   .6527000      .000      .000  
+75 122 42434.00 I  -.043628  .019424   .282851  .014309  I  .6508876  .0005823  2.6093 0.4117  P    38.595     .500      .511     .300  -.034000   .291000   .6500000      .000      .000  
+75 123 42435.00 I  -.043138  .019011   .283988  .015448  I  .6483073  .0005823  2.5573 0.4117  P    38.326     .500      .626     .300  -.033000   .292000   .6472000      .000      .000  
+75 124 42436.00 I  -.042603  .023491   .285155  .013692  I  .6457559  .0005823  2.5567 0.3211  P    38.256     .500      .752     .300  -.032000   .292000   .6443000      .000      .000  
+75 125 42437.00 I  -.042021  .022965   .286350  .010985  I  .6431682  .0002710  2.6332 0.3211  P    38.354     .500      .637     .300  -.032000   .293000   .6415000      .000      .000  
+75 126 42438.00 I  -.041394  .027165   .287570  .009970  I  .6404612  .0002710  2.7945 0.1916  P    38.593     .500      .314     .300  -.031000   .293000   .6386000      .000      .000  
+75 127 42439.00 I  -.040726  .026099   .288813  .009830  I  .6375585  .0002710  3.0179 0.1916  P    38.925     .500      .093     .300  -.030000   .294000   .6358000      .000      .000  
+75 128 42440.00 I  -.040018  .025657   .290078  .012892  I  .6344222  .0002710  3.2507 0.1916  P    39.206     .500      .188     .300  -.029000   .295000   .6328000      .000      .000  
+75 129 42441.00 I  -.039271  .024739   .291366  .011992  I  .6310758  .0002710  3.4272 0.1916  P    39.306     .500      .475     .300  -.028000   .295000   .6299000      .000      .000  
+75 130 42442.00 I  -.038487  .027779   .292676  .010691  I  .6276040  .0002710  3.4949 0.1916  P    39.260     .500      .689     .300  -.026000   .296000   .6269000      .000      .000  
+75 131 42443.00 I  -.037672  .023350   .294007  .018348  I  .6241280  .0002710  3.4366 0.1916  P    39.181     .500      .734     .300  -.025000   .296000   .6240000      .000      .000  
+75 2 1 42444.00 I  -.036831  .022707   .295362  .018633  I  .6207651  .0002710  3.2755 0.1916  P    39.094     .500      .697     .300  -.024000   .297000   .6210000      .000      .000  
+75 2 2 42445.00 I  -.035972  .014775   .296738  .019207  I  .6175948  .0002710  3.0610 0.1916  P    38.964     .500      .628     .300  -.023000   .298000   .6181000      .000      .000  
+75 2 3 42446.00 I  -.035098  .017407   .298134  .019805  I  .6146428  .0002710  2.8472 0.1916  P    38.826     .500      .468     .300  -.022000   .299000   .6151000      .000      .000  
+75 2 4 42447.00 I  -.034215  .016949   .299551  .018489  I  .6118869  .0002710  2.6737 0.1916  P    38.736     .500      .203     .300  -.022000   .300000   .6122000      .000      .000  
+75 2 5 42448.00 I  -.033323  .020700   .300984  .018641  I  .6092753  .0002710  2.5603 0.1916  P    38.642     .500     -.053     .300  -.021000   .301000   .6092000      .000      .000  
+75 2 6 42449.00 I  -.032428  .016985   .302432  .017931  I  .6067448  .0002710  2.5112 0.1916  P    38.471     .500     -.185     .300  -.020000   .302000   .6063000      .000      .000  
+75 2 7 42450.00 I  -.031533  .016188   .303890  .013287  I  .6042334  .0002710  2.5205 0.1916  P    38.296     .500     -.206     .300  -.019000   .303000   .6034000      .000      .000  
+75 2 8 42451.00 I  -.030641  .016046   .305355  .012596  I  .6016879  .0002710  2.5775 0.1916  P    38.280     .500     -.197     .300  -.018000   .304000   .6005000      .000      .000  
+75 2 9 42452.00 I  -.029752  .021826   .306822  .014655  I  .5990674  .0002710  2.6675 0.1916  P    38.470     .500     -.171     .300  -.018000   .306000   .5975000      .000      .000  
+75 210 42453.00 I  -.028867  .019896   .308289  .014059  I  .5963479  .0002710  2.7723 0.1916  P    38.756     .500     -.075     .300  -.017000   .307000   .5946000      .000      .000  
+75 211 42454.00 I  -.027985  .020009   .309752  .013988  I  .5935241  .0002710  2.8730 0.1916  P    39.002     .500      .075     .300  -.016000   .308000   .5917000      .000      .000  
+75 212 42455.00 I  -.027107  .017072   .311208  .013989  I  .5906088  .0002710  2.9528 0.1916  P    39.124     .500      .152     .300  -.015000   .309000   .5888000      .000      .000  
+75 213 42456.00 I  -.026233  .017678   .312651  .016142  I  .5876293  .0002710  3.0001 0.1916  P    39.092     .500      .059     .300  -.014000   .310000   .5859000      .000      .000  
+75 214 42457.00 I  -.025359  .018935   .314078  .014714  I  .5846213  .0002710  3.0094 0.1916  P    38.986     .500     -.146     .300  -.014000   .312000   .5830000      .000      .000  
+75 215 42458.00 I  -.024481  .020649   .315489  .017452  I  .5816232  .0002710  2.9808 0.1916  P    38.969     .500     -.328     .300  -.013000   .313000   .5801000      .000      .000  
+75 216 42459.00 I  -.023595  .014177   .316880  .015465  I  .5786704  .0002710  2.9202 0.1916  P    39.099     .500     -.448     .300  -.012000   .314000   .5772000      .000      .000  
+75 217 42460.00 I  -.022698  .014375   .318250  .015363  I  .5757901  .0002710  2.8377 0.1916  P    39.194     .500     -.588     .300  -.011000   .315000   .5743000      .000      .000  
+75 218 42461.00 I  -.021785  .015949   .319596  .014755  I  .5729971  .0002710  2.7488 0.1916  P    39.041     .500     -.782     .300  -.011000   .316000   .5714000      .000      .000  
+75 219 42462.00 I  -.020854  .017702   .320913  .014759  I  .5702878  .0002710  2.6741 0.1916  P    38.674     .500     -.915     .300  -.010000   .318000   .5686000      .000      .000  
+75 220 42463.00 I  -.019901  .018204   .322199  .013429  I  .5676357  .0002710  2.6390 0.1916  P    38.323     .500     -.883     .300  -.010000   .319000   .5657000      .000      .000  
+75 221 42464.00 I  -.018922  .017853   .323451  .014297  I  .5649878  .0002710  2.6697 0.1916  P    38.140     .500     -.796     .300  -.009000   .320000   .5628000      .000      .000  
+75 222 42465.00 I  -.017916  .015959   .324668  .011230  I  .5622687  .0002710  2.7831 0.4960  P    38.127     .500     -.867     .300  -.008000   .321000   .5600000      .000      .000  
+75 223 42466.00 I  -.016879  .016534   .325847  .010394  I  .5593957  .0009542  2.9744 0.4960  P    38.252     .500    -1.110     .300  -.008000   .322000   .5572000      .000      .000  
+75 224 42467.00 I  -.015812  .016512   .326987  .012503  I  .5563054  .0009542  3.2094 0.6747  P    38.485     .500    -1.267     .300  -.007000   .323000   .5543000      .000      .000  
+75 225 42468.00 I  -.014712  .027310   .328087  .013680  I  .5529823  .0009542  3.4284 0.6747  P    38.732     .500    -1.130     .300  -.007000   .324000   .5515000      .000      .000  
+75 226 42469.00 I  -.013581  .026073   .329149  .015755  I  .5494757  .0009542  3.5663 0.6747  P    38.893     .500     -.821     .300  -.006000   .325000   .5487000      .000      .000  
+75 227 42470.00 I  -.012420  .025357   .330173  .014861  I  .5458914  .0009542  3.5793 0.5872  P    38.975     .500     -.635     .300  -.006000   .326000   .5459000      .000      .000  
+75 228 42471.00 I  -.011231  .024959   .331158  .012962  I  .5423600  .0006845  3.4636 0.4840  P    39.018     .500     -.681     .300  -.005000   .327000   .5432000      .000      .000  
+75 3 1 42472.00 I  -.010016  .025611   .332105  .014933  I  .5389947  .0001625  3.2562 0.3518  P    38.985     .500     -.828     .300  -.005000   .327000   .5404000      .000      .000  
+75 3 2 42473.00 I  -.008773  .026568   .333012  .014809  I  .5358580  .0001625  3.0174 0.1472  P    38.853     .500     -.969     .300  -.004000   .328000   .5377000      .000      .000  
+75 3 3 42474.00 I  -.007500  .026451   .333880  .013388  I  .5329514  .0002456  2.8042 0.1472  P    38.758     .500    -1.159     .300  -.004000   .329000   .5349000      .000      .000  
+75 3 4 42475.00 I  -.006197  .015960   .334705  .012026  I  .5302293  .0002456  2.6521 0.1737  P    38.824     .500    -1.454     .300  -.004000   .330000   .5321000      .000      .000  
+75 3 5 42476.00 I  -.004863  .017065   .335489  .009368  I  .5276233  .0002456  2.5719 0.1965  P    38.914     .500    -1.745     .300  -.003000   .331000   .5292000      .000      .000  
+75 3 6 42477.00 I  -.003500  .023307   .336232  .009321  I  .5250636  .0003069  2.5574 0.1965  P    38.778     .500    -1.889     .300  -.003000   .331000   .5264000      .000      .000  
+75 3 7 42478.00 I  -.002108  .029753   .336934  .009360  I  .5224911  .0003069  2.5950 0.2170  P    38.452     .500    -1.896     .300  -.002000   .332000   .5235000      .000      .000  
+75 3 8 42479.00 I  -.000692  .029555   .337597  .005197  I  .5198611  .0003069  2.6700 0.2047  P    38.286     .500    -1.890     .300  -.002000   .333000   .5207000      .000      .000  
+75 3 9 42480.00 I   .000744  .028481   .338223  .005257  I  .5171438  .0002710  2.7668 0.2047  P    38.513     .500    -1.901     .300  -.002000   .334000   .5178000      .000      .000  
+75 310 42481.00 I   .002194  .028602   .338812  .010660  I  .5143260  .0002710  2.8681 0.1916  P    38.986     .500    -1.835     .300  -.001000   .334000   .5149000      .000      .000  
+75 311 42482.00 I   .003653  .028150   .339365  .010763  I  .5114119  .0002710  2.9564 0.1916  P    39.396     .500    -1.665     .300  -.001000   .335000   .5121000      .000      .000  
+75 312 42483.00 I   .005126  .026852   .339885  .010375  I  .5084225  .0002710  3.0167 0.1916  P    39.591     .500    -1.533     .300   .000000   .335000   .5092000      .000      .000  
+75 313 42484.00 I   .006613  .021501   .340372  .011366  I  .5053905  .0002710  3.0408 0.1916  P    39.605     .500    -1.605     .300   .000000   .336000   .5063000      .000      .000  
+75 314 42485.00 I   .008116  .009967   .340829  .013656  I  .5023538  .0002710  3.0262 0.1916  P    39.548     .500    -1.867     .300   .001000   .337000   .5033000      .000      .000  
+75 315 42486.00 I   .009635  .010404   .341255  .014170  I  .4993497  .0002710  2.9769 0.1916  P    39.541     .500    -2.147     .300   .001000   .337000   .5003000      .000      .000  
+75 316 42487.00 I   .011171  .014487   .341651  .014443  I  .4964084  .0002710  2.9024 0.1916  P    39.650     .500    -2.319     .300   .002000   .338000   .4974000      .000      .000  
+75 317 42488.00 I   .012726  .015678   .342017  .010889  I  .4935487  .0002710  2.8166 0.1916  P    39.795     .500    -2.415     .300   .002000   .338000   .4944000      .000      .000  
+75 318 42489.00 I   .014304  .014246   .342352  .011073  I  .4907725  .0002710  2.7389 0.1916  P    39.814     .500    -2.520     .300   .003000   .339000   .4914000      .000      .000  
+75 319 42490.00 I   .015905  .014675   .342660  .011739  I  .4880607  .0002710  2.6919 0.1916  P    39.657     .500    -2.629     .300   .004000   .340000   .4883000      .000      .000  
+75 320 42491.00 I   .017531  .014824   .342942  .013082  I  .4853706  .0002710  2.6993 0.1916  P    39.464     .500    -2.682     .300   .005000   .340000   .4852000      .000      .000  
+75 321 42492.00 I   .019180  .018094   .343198  .011215  I  .4826378  .0002710  2.7795 0.1916  P    39.396     .500    -2.701     .300   .005000   .341000   .4821000      .000      .000  
+75 322 42493.00 I   .020849  .018025   .343430  .011563  I  .4797859  .0002710  2.9369 0.2593  P    39.488     .500    -2.786     .300   .006000   .341000   .4790000      .000      .000  
+75 323 42494.00 I   .022537  .015014   .343638  .011325  I  .4767436  .0004421  3.1554 0.2593  P    39.668     .500    -2.936     .300   .007000   .342000   .4759000      .000      .000  
+75 324 42495.00 I   .024242  .013963   .343820  .013510  I  .4734686  .0004421  3.3932 0.3126  P    39.827     .500    -2.979     .300   .008000   .342000   .4727000      .000      .000  
+75 325 42496.00 I   .025962  .015343   .343976  .014857  I  .4699702  .0004421  3.5919 0.3126  P    39.877     .500    -2.780     .300   .009000   .343000   .4695000      .000      .000  
+75 326 42497.00 I   .027694  .014774   .344104  .017618  I  .4663166  .0004421  3.6952 0.3126  P    39.812     .500    -2.461     .300   .010000   .343000   .4664000      .000      .000  
+75 327 42498.00 I   .029437  .015044   .344201  .016367  I  .4626221  .0004421  3.6715 0.3126  P    39.733     .500    -2.302     .300   .011000   .344000   .4632000      .000      .000  
+75 328 42499.00 I   .031187  .016712   .344266  .015578  I  .4590129  .0004421  3.5298 0.2593  P    39.715     .500    -2.422     .300   .012000   .344000   .4600000      .000      .000  
+75 329 42500.00 I   .032941  .015697   .344294  .015187  I  .4555869  .0002710  3.3150 0.2932  P    39.702     .500    -2.665     .300   .013000   .344000   .4569000      .000      .000  
+75 330 42501.00 I   .034696  .015756   .344283  .015494  I  .4523866  .0003853  3.0890 0.2355  P    39.636     .500    -2.841     .300   .015000   .344000   .4538000      .000      .000  
+75 331 42502.00 I   .036448  .019185   .344232  .013966  I  .4493950  .0003853  2.9046 0.2724  P    39.626     .500    -2.938     .300   .016000   .344000   .4507000      .000      .000  
+75 4 1 42503.00 I   .038197  .018087   .344141  .012700  I  .4465544  .0003853  2.7893 0.2724  P    39.785     .500    -3.052     .300   .018000   .344000   .4476000      .000      .000  
+75 4 2 42504.00 I   .039936  .018807   .344009  .008052  I  .4437925  .0003853  2.7456 0.2724  P    39.958     .500    -3.190     .300   .019000   .344000   .4445000      .000      .000  
+75 4 3 42505.00 I   .041661  .018590   .343836  .007529  I  .4410437  .0003853  2.7603 0.2724  P    39.852     .500    -3.280     .300   .020000   .344000   .4415000      .000      .000  
+75 4 4 42506.00 I   .043367  .015648   .343622  .008367  I  .4382584  .0003853  2.8156 0.2355  P    39.493     .500    -3.320     .300   .021000   .344000   .4386000      .000      .000  
+75 4 5 42507.00 I   .045053  .015515   .343367  .008001  I  .4354043  .0002710  2.8954 0.2355  P    39.294     .500    -3.379     .300   .023000   .345000   .4356000      .000      .000  
+75 4 6 42508.00 I   .046719  .017748   .343070  .008399  I  .4324646  .0002710  2.9843 0.1916  P    39.556     .500    -3.449     .300   .024000   .345000   .4327000      .000      .000  
+75 4 7 42509.00 I   .048364  .013572   .342730  .011161  I  .4294378  .0002710  3.0668 0.1916  P    40.091     .500    -3.423     .300   .025000   .345000   .4297000      .000      .000  
+75 4 8 42510.00 I   .049985  .017553   .342348  .011085  I  .4263381  .0002710  3.1278 0.1916  P    40.474     .500    -3.259     .300   .026000   .345000   .4268000      .000      .000  
+75 4 9 42511.00 I   .051578  .018329   .341921  .010870  I  .4231931  .0002710  3.1560 0.1916  P    40.524     .500    -3.090     .300   .027000   .345000   .4239000      .000      .000  
+75 410 42512.00 I   .053137  .022640   .341449  .010892  I  .4200387  .0002710  3.1464 0.1916  P    40.372     .500    -3.094     .300   .029000   .345000   .4211000      .000      .000  
+75 411 42513.00 I   .054661  .023079   .340931  .010201  I  .4169125  .0002710  3.1004 0.1916  P    40.203     .500    -3.293     .300   .030000   .345000   .4182000      .000      .000  
+75 412 42514.00 I   .056146  .024106   .340369  .012818  I  .4138476  .0002710  3.0255 0.1916  P    40.109     .500    -3.548     .300   .031000   .345000   .4153000      .000      .000  
+75 413 42515.00 I   .057593  .022407   .339764  .011848  I  .4108669  .0002710  2.9344 0.1916  P    40.136     .500    -3.724     .300   .032000   .345000   .4124000      .000      .000  
+75 414 42516.00 I   .058999  .022761   .339119  .009958  I  .4079785  .0002710  2.8443 0.1916  P    40.285     .500    -3.809     .300   .033000   .345000   .4095000      .000      .000  
+75 415 42517.00 I   .060367  .019979   .338435  .010111  I  .4051706  .0002710  2.7773 0.1916  P    40.460     .500    -3.869     .300   .035000   .344000   .4065000      .000      .000  
+75 416 42518.00 I   .061701  .019270   .337716  .010759  I  .4024088  .0002710  2.7557 0.1916  P    40.538     .500    -3.941     .300   .036000   .344000   .4036000      .000      .000  
+75 417 42519.00 I   .063009  .019183   .336966  .020934  I  .3996382  .0002710  2.7970 0.1916  P    40.515     .500    -4.017     .300   .037000   .344000   .4007000      .000      .000  
+75 418 42520.00 I   .064296  .016990   .336186  .021301  I  .3967917  .0002710  2.9075 0.1356  P    40.515     .500    -4.089     .300   .038000   .343000   .3977000      .000      .000  
+75 419 42521.00 I   .065567  .015814   .335382  .019725  I  .3938035  .0000076  3.0772 0.1356  P    40.648     .500    -4.168     .300   .039000   .343000   .3947000      .000      .000  
+75 420 42522.00 I   .066826  .019725   .334554  .019875  I  .3906270  .0000076  3.2780 0.0054  P    40.892     .500    -4.224     .300   .041000   .342000   .3917000      .000      .000  
+75 421 42523.00 I   .068077  .020744   .333704  .019108  I  .3872515  .0000076  3.4669 0.1804  P    41.099     .500    -4.160     .300   .042000   .342000   .3887000      .000      .000  
+75 422 42524.00 I   .069321  .023399   .332833  .021009  I  .3837133  .0003608  3.5956 0.1804  P    41.112     .500    -3.916     .300   .043000   .341000   .3857000      .000      .000  
+75 423 42525.00 I   .070557  .023884   .331940  .020543  I  .3800930  .0003608  3.6261 0.2551  P    40.908     .500    -3.593     .300   .044000   .340000   .3826000      .000      .000  
+75 424 42526.00 I   .071788  .020385   .331024  .011059  I  .3764981  .0003608  3.5455 0.3124  P    40.627     .500    -3.417     .300   .045000   .339000   .3796000      .000      .000  
+75 425 42527.00 I   .073014  .021086   .330086  .021849  I  .3730323  .0005102  3.3741 0.3124  P    40.429     .500    -3.518     .300   .046000   .338000   .3765000      .000      .000  
+75 426 42528.00 I   .074238  .022092   .329124  .022757  I  .3697646  .0005102  3.1587 0.3157  P    40.357     .500    -3.783     .300   .047000   .337000   .3735000      .000      .000  
+75 427 42529.00 I   .075463  .023969   .328141  .022638  I  .3667112  .0003719  2.9545 0.2630  P    40.374     .500    -3.977     .300   .048000   .336000   .3704000      .000      .000  
+75 428 42530.00 I   .076690  .024582   .327137  .023180  I  .3638376  .0001276  2.8041 0.1935  P    40.490     .500    -3.987     .300   .049000   .335000   .3674000      .000      .000  
+75 429 42531.00 I   .077921  .021790   .326113  .021730  I  .3610792  .0001069  2.7249 0.0832  P    40.706     .500    -3.887     .300   .050000   .334000   .3643000      .000      .000  
+75 430 42532.00 I   .079157  .020791   .325068  .023010  I  .3583660  .0001069  2.7111 0.0771  P    40.870     .500    -3.810     .300   .051000   .332000   .3613000      .000      .000  
+75 5 1 42533.00 I   .080398  .020809   .324001  .022596  I  .3556415  .0001112  2.7439 0.0771  P    40.772     .500    -3.829     .300   .052000   .331000   .3582000      .000      .000  
+75 5 2 42534.00 I   .081641  .025199   .322911  .014921  I  .3528694  .0001112  2.8032 0.2490  P    40.455     .500    -3.952     .300   .053000   .330000   .3552000      .000      .000  
+75 5 3 42535.00 I   .082882  .025309   .321798  .013709  I  .3500319  .0004854  2.8721 0.2490  P    40.256     .500    -4.145     .300   .054000   .329000   .3522000      .000      .000  
+75 5 4 42536.00 I   .084121  .023017   .320663  .017797  I  .3471267  .0004854  2.9364 0.3827  P    40.414     .500    -4.312     .300   .055000   .327000   .3492000      .000      .000  
+75 5 5 42537.00 I   .085356  .020825   .319504  .017220  I  .3441649  .0005918  2.9832 0.3827  P    40.754     .500    -4.337     .300   .055000   .326000   .3462000      .000      .000  
+75 5 6 42538.00 I   .086586  .021746   .318325  .016862  I  .3411700  .0005918  3.0010 0.5090  P    40.897     .500    -4.200     .300   .056000   .324000   .3432000      .000      .000  
+75 5 7 42539.00 I   .087811  .021790   .317127  .021524  I  .3381746  .0008283  2.9837 0.5090  P    40.691     .500    -4.027     .300   .057000   .323000   .3402000      .000      .000  
+75 5 8 42540.00 I   .089031  .024637   .315913  .021903  I  .3352143  .0008283  2.9312 0.4358  P    40.305     .500    -3.963     .300   .058000   .322000   .3373000      .000      .000  
+75 5 9 42541.00 I   .090241  .019422   .314689  .020433  I  .3323221  .0002710  2.8489 0.4358  P    39.960     .500    -4.026     .300   .059000   .321000   .3344000      .000      .000  
+75 510 42542.00 I   .091439  .019385   .313459  .021471  I  .3295229  .0002710  2.7474 0.1916  P    39.749     .500    -4.136     .300   .059000   .319000   .3314000      .000      .000  
+75 511 42543.00 I   .092621  .016215   .312226  .020481  I  .3268285  .0002710  2.6424 0.1916  P    39.691     .500    -4.231     .300   .060000   .318000   .3285000      .000      .000  
+75 512 42544.00 I   .093786  .025007   .310993  .021438  I  .3242325  .0002710  2.5543 0.1916  P    39.806     .500    -4.320     .300   .061000   .317000   .3256000      .000      .000  
+75 513 42545.00 I   .094932  .024534   .309761  .021070  I  .3217068  .0002710  2.5056 0.1916  P    40.054     .500    -4.417     .300   .062000   .316000   .3228000      .000      .000  
+75 514 42546.00 I   .096058  .025432   .308531  .015751  I  .3192016  .0002710  2.5159 0.1916  P    40.291     .500    -4.493     .300   .063000   .314000   .3200000      .000      .000  
+75 515 42547.00 I   .097163  .022993   .307303  .016795  I  .3166529  .0002710  2.5924 0.1916  P    40.377     .500    -4.511     .300   .064000   .313000   .3172000      .000      .000  
+75 516 42548.00 I   .098247  .023162   .306076  .018229  I  .3139979  .0002710  2.7256 0.1916  P    40.308     .500    -4.470     .300   .065000   .311000   .3144000      .000      .000  
+75 517 42549.00 I   .099311  .022905   .304848  .018864  I  .3111915  .0002710  2.8895 0.1916  P    40.209     .500    -4.401     .300   .066000   .310000   .3116000      .000      .000  
+75 518 42550.00 I   .100359  .023467   .303619  .016544  I  .3082210  .0002710  3.0471 0.1485  P    40.208     .500    -4.319     .300   .067000   .309000   .3089000      .000      .000  
+75 519 42551.00 I   .101397  .013929   .302388  .015767  I  .3051124  .0001217  3.1593 0.1485  P    40.312     .500    -4.196     .300   .068000   .308000   .3061000      .000      .000  
+75 520 42552.00 I   .102426  .013407   .301158  .018793  I  .3019278  .0001217  3.1951 0.0861  P    40.399     .500    -3.995     .300   .069000   .306000   .3034000      .000      .000  
+75 521 42553.00 I   .103450  .015856   .299929  .017598  I  .2987524  .0001217  3.1401 0.0861  P    40.331     .500    -3.742     .300   .070000   .305000   .3006000      .000      .000  
+75 522 42554.00 I   .104473  .016796   .298699  .016208  I  .2956753  .0001217  3.0020 0.0861  P    40.074     .500    -3.556     .300   .071000   .304000   .2979000      .000      .000  
+75 523 42555.00 I   .105500  .016462   .297467  .016761  I  .2927661  .0001217  2.8107 0.0810  P    39.733     .500    -3.562     .300   .072000   .303000   .2952000      .000      .000  
+75 524 42556.00 I   .106539  .015430   .296233  .017860  I  .2900570  .0001068  2.6099 0.0755  P    39.479     .500    -3.748     .300   .073000   .302000   .2926000      .000      .000  
+75 525 42557.00 I   .107595  .016900   .294997  .017778  I  .2875349  .0000895  2.4433 0.0697  P    39.434     .500    -3.941     .300   .074000   .300000   .2899000      .000      .000  
+75 526 42558.00 I   .108671  .017884   .293758  .017764  I  .2851496  .0000895  2.3392 0.0633  P    39.610     .500    -3.962     .300   .075000   .299000   .2873000      .000      .000  
+75 527 42559.00 I   .109773  .017986   .292518  .014607  I  .2828336  .0000895  2.3039 0.0633  P    39.883     .500    -3.800     .300   .076000   .298000   .2846000      .000      .000  
+75 528 42560.00 I   .110906  .014728   .291275  .015041  I  .2805229  .0000895  2.3253 0.1133  P    40.054     .500    -3.609     .300   .077000   .297000   .2820000      .000      .000  
+75 529 42561.00 I   .112075  .013825   .290031  .014718  I  .2781713  .0002081  2.3819 0.1937  P    39.990     .500    -3.564     .300   .079000   .295000   .2795000      .000      .000  
+75 530 42562.00 I   .113283  .016841   .288785  .014134  I  .2757543  .0003770  2.4529 0.3109  P    39.751     .500    -3.721     .300   .080000   .294000   .2769000      .000      .000  
+75 531 42563.00 I   .114533  .016471   .287537  .012143  I  .2732663  .0005859  2.5216 0.3490  P    39.531     .500    -3.991     .300   .082000   .292000   .2744000      .000      .000  
+75 6 1 42564.00 I   .115825  .018141   .286288  .011891  I  .2707160  .0005875  2.5757 0.4149  P    39.455     .500    -4.207     .300   .083000   .291000   .2718000      .000      .000  
+75 6 2 42565.00 I   .117160  .021320   .285035  .012244  I  .2681232  .0005875  2.6050 0.4154  P    39.452     .500    -4.244     .300   .084000   .290000   .2693000      .000      .000  
+75 6 3 42566.00 I   .118536  .022033   .283780  .019172  I  .2655170  .0005875  2.6015 0.4414  P    39.340     .500    -4.116     .300   .086000   .289000   .2668000      .000      .000  
+75 6 4 42567.00 I   .119950  .022356   .282521  .020437  I  .2629321  .0006588  2.5624 0.4726  P    39.017     .500    -3.955     .300   .087000   .287000   .2643000      .000      .000  
+75 6 5 42568.00 I   .121398  .024367   .281258  .020572  I  .2604028  .0007404  2.4916 0.4429  P    38.532     .500    -3.872     .300   .089000   .286000   .2618000      .000      .000  
+75 6 6 42569.00 I   .122879  .022303   .279990  .019558  I  .2579562  .0005922  2.3988 0.3942  P    38.028     .500    -3.851     .300   .090000   .285000   .2593000      .000      .000  
+75 6 7 42570.00 I   .124388  .025349   .278717  .018663  I  .2556079  .0002710  2.2978 0.3256  P    37.638     .500    -3.820     .300   .092000   .284000   .2568000      .000      .000  
+75 6 8 42571.00 I   .125921  .027449   .277438  .019318  I  .2533572  .0002710  2.2069 0.1916  P    37.447     .500    -3.788     .300   .093000   .282000   .2544000      .000      .000  
+75 6 9 42572.00 I   .127474  .024856   .276154  .026624  I  .2511834  .0002710  2.1481 0.1916  P    37.489     .500    -3.842     .300   .095000   .281000   .2519000      .000      .000  
+75 610 42573.00 I   .129037  .027006   .274864  .021990  I  .2490428  .0002710  2.1437 0.1916  P    37.726     .500    -3.999     .300   .096000   .279000   .2495000      .000      .000  
+75 611 42574.00 I   .130603  .029281   .273567  .021064  I  .2468736  .0002710  2.2063 0.1916  P    38.032     .500    -4.133     .300   .098000   .278000   .2470000      .000      .000  
+75 612 42575.00 I   .132165  .026834   .272260  .021442  I  .2446096  .0002710  2.3307 0.1916  P    38.207     .500    -4.097     .300   .100000   .277000   .2446000      .000      .000  
+75 613 42576.00 I   .133713  .029198   .270945  .023301  I  .2422005  .0002710  2.4903 0.1916  P    38.093     .500    -3.870     .300   .101000   .275000   .2422000      .000      .000  
+75 614 42577.00 I   .135237  .026775   .269625  .026334  I  .2396310  .0002710  2.6440 0.5153  P    37.703     .500    -3.571     .300   .103000   .274000   .2398000      .000      .000  
+75 615 42578.00 I   .136725  .026548   .268302  .026009  I  .2369286  .0009943  2.7494 0.4036  P    37.236     .500    -3.338     .300   .104000   .272000   .2374000      .000      .000  
+75 616 42579.00 I   .138167  .025969   .266980  .023202  I  .2341584  .0007603  2.7764 0.6258  P    36.936     .500    -3.225     .300   .106000   .271000   .2350000      .000      .000  
+75 617 42580.00 I   .139554  .025914   .265660  .026829  I  .2314051  .0007603  2.7156 0.5376  P    36.893     .500    -3.183     .300   .108000   .270000   .2326000      .000      .000  
+75 618 42581.00 I   .140882  .023994   .264345  .028703  I  .2287524  .0007603  2.5790 0.5376  P    36.960     .500    -3.137     .300   .109000   .268000   .2303000      .000      .000  
+75 619 42582.00 I   .142146  .030650   .263038  .031243  I  .2262630  .0007603  2.3945 0.5350  P    36.889     .500    -3.073     .300   .111000   .267000   .2279000      .000      .000  
+75 620 42583.00 I   .143336  .029711   .261738  .029269  I  .2239675  .0007530  2.1975 0.4833  P    36.559     .500    -3.057     .300   .112000   .265000   .2256000      .000      .000  
+75 621 42584.00 I   .144446  .029705   .260444  .026524  I  .2218601  .0005969  2.0240 0.5272  P    36.107     .500    -3.149     .300   .114000   .264000   .2232000      .000      .000  
+75 622 42585.00 I   .145469  .025687   .259152  .026820  I  .2199022  .0007382  1.9022 0.4747  P    35.815     .500    -3.303     .300   .115000   .263000   .2209000      .000      .000  
+75 623 42586.00 I   .146405  .027583   .257856  .024668  I  .2180341  .0007382  1.8450 0.5220  P    35.839     .500    -3.385     .300   .117000   .261000   .2187000      .000      .000  
+75 624 42587.00 I   .147255  .026242   .256553  .021921  I  .2161921  .0007382  1.8480 0.5220  P    36.067     .500    -3.310     .300   .118000   .260000   .2164000      .000      .000  
+75 625 42588.00 I   .148021  .030116   .255237  .019152  I  .2143237  .0007382  1.8943 0.5840  P    36.253     .500    -3.142     .300   .120000   .258000   .2142000      .000      .000  
+75 626 42589.00 I   .148702  .023205   .253903  .014634  I  .2123966  .0009052  1.9615 0.5840  P    36.257     .500    -3.036     .300   .121000   .257000   .2119000      .000      .000  
+75 627 42590.00 I   .149300  .024080   .252549  .018273  I  .2104005  .0009052  2.0293 0.6401  P    36.105     .500    -3.092     .300   .122000   .255000   .2098000      .000      .000  
+75 628 42591.00 I   .149817  .024227   .251173  .019908  I  .2083430  .0009052  2.0820 0.6401  P    35.867     .500    -3.276     .300   .123000   .254000   .2077000      .000      .000  
+75 629 42592.00 I   .150256  .026242   .249773  .027163  I  .2062449  .0009052  2.1096 0.6401  P    35.587     .500    -3.452     .300   .125000   .252000   .2055000      .000      .000  
+75 630 42593.00 I   .150620  .024327   .248350  .025556  I  .2041341  .0009052  2.1064 0.6401  P    35.326     .500    -3.499     .300   .126000   .251000   .2034000      .000      .000  
+75 7 1 42594.00 I   .150907  .022889   .246906  .024865  I  .2020430  .0009052  2.0704 0.4724  P    35.137     .500    -3.419     .300   .127000   .249000   .2013000      .000      .000  
+75 7 2 42595.00 I   .151121  .023680   .245438  .026638  I  .2000033  .0002710  2.0044 0.4724  P    34.943     .500    -3.326     .300   .128000   .247000   .1993000      .000      .000  
+75 7 3 42596.00 I   .151264  .026477   .243945  .026940  I  .1980417  .0002710  1.9160 0.1916  P    34.572     .500    -3.321     .300   .129000   .246000   .1973000      .000      .000  
+75 7 4 42597.00 I   .151341  .024358   .242428  .024600  I  .1961746  .0002710  1.8178 0.1916  P    33.978     .500    -3.360     .300   .129000   .244000   .1953000      .000      .000  
+75 7 5 42598.00 I   .151357  .025867   .240886  .024994  I  .1944039  .0002710  1.7261 0.1916  P    33.370     .500    -3.330     .300   .130000   .243000   .1933000      .000      .000  
+75 7 6 42599.00 I   .151316  .026980   .239321  .021832  I  .1927134  .0002710  1.6612 0.1916  P    33.030     .500    -3.230     .300   .131000   .241000   .1913000      .000      .000  
+75 7 7 42600.00 I   .151218  .027831   .237734  .022291  I  .1910650  .0002710  1.6456 0.1916  P    33.057     .500    -3.206     .300   .131000   .239000   .1893000      .000      .000  
+75 7 8 42601.00 I   .151067  .028588   .236130  .022331  I  .1893995  .0002710  1.6979 0.1916  P    33.337     .500    -3.350     .300   .132000   .238000   .1874000      .000      .000  
+75 7 9 42602.00 I   .150864  .023536   .234510  .022270  I  .1876449  .0002710  1.8229 0.1916  P    33.678     .500    -3.532     .300   .132000   .236000   .1854000      .000      .000  
+75 710 42603.00 I   .150608  .020407   .232880  .025206  I  .1857359  .0002710  2.0013 0.1916  P    33.892     .500    -3.509     .300   .133000   .235000   .1835000      .000      .000  
+75 711 42604.00 I   .150303  .020456   .231240  .025791  I  .1836383  .0002710  2.1915 0.1916  P    33.802     .500    -3.201     .300   .133000   .233000   .1815000      .000      .000  
+75 712 42605.00 I   .149951  .019155   .229592  .024384  I  .1813664  .0002710  2.3410 0.1916  P    33.336     .500    -2.778     .300   .133000   .231000   .1796000      .000      .000  
+75 713 42606.00 I   .149556  .021203   .227935  .019476  I  .1789845  .0002710  2.4060 0.1916  P    32.638     .500    -2.481     .300   .133000   .230000   .1777000      .000      .000  
+75 714 42607.00 I   .149123  .020441   .226270  .018441  I  .1765882  .0002710  2.3696 0.1916  P    32.010     .500    -2.412     .300   .134000   .228000   .1758000      .000      .000  
+75 715 42608.00 I   .148659  .023203   .224596  .018318  I  .1742749  .0002710  2.2444 0.1916  P    31.697     .500    -2.514     .300   .134000   .227000   .1739000      .000      .000  
+75 716 42609.00 I   .148167  .022357   .222915  .016658  I  .1721174  .0002710  2.0650 0.1916  P    31.705     .500    -2.672     .300   .134000   .225000   .1720000      .000      .000  
+75 717 42610.00 I   .147651  .022529   .221225  .011073  I  .1701488  .0002710  1.8737 0.1916  P    31.813     .500    -2.809     .300   .134000   .223000   .1702000      .000      .000  
+75 718 42611.00 I   .147119  .022850   .219529  .011508  I  .1683616  .0002710  1.7076 0.1916  P    31.758     .500    -2.908     .300   .134000   .221000   .1683000      .000      .000  
+75 719 42612.00 I   .146579  .022336   .217826  .010793  I  .1667168  .0002710  1.5920 0.1916  P    31.465     .500    -2.988     .300   .135000   .220000   .1665000      .000      .000  
+75 720 42613.00 I   .146036  .015432   .216118  .012428  I  .1651568  .0002710  1.5390 0.1916  P    31.118     .500    -3.063     .300   .135000   .218000   .1646000      .000      .000  
+75 721 42614.00 I   .145495  .017941   .214404  .014705  I  .1636182  .0002710  1.5479 0.1916  P    30.958     .500    -3.114     .300   .135000   .216000   .1628000      .000      .000  
+75 722 42615.00 I   .144958  .015196   .212684  .014862  I  .1620442  .0002710  1.6071 0.1916  P    31.029     .500    -3.095     .300   .135000   .214000   .1610000      .000      .000  
+75 723 42616.00 I   .144430  .015453   .210958  .013235  I  .1603935  .0002710  1.6977 0.1916  P    31.170     .500    -2.985     .300   .135000   .213000   .1592000      .000      .000  
+75 724 42617.00 I   .143917  .015271   .209226  .014678  I  .1586456  .0002710  1.7979 0.1916  P    31.223     .500    -2.825     .300   .135000   .211000   .1573000      .000      .000  
+75 725 42618.00 I   .143422  .017232   .207487  .013351  I  .1568008  .0002710  1.8887 0.4314  P    31.149     .500    -2.704     .300   .135000   .210000   .1555000      .000      .000  
+75 726 42619.00 I   .142948  .017121   .205744  .013281  I  .1548759  .0008191  1.9565 0.4314  P    30.942     .500    -2.698     .300   .135000   .208000   .1537000      .000      .000  
+75 727 42620.00 I   .142495  .020242   .203995  .011738  I  .1528981  .0008191  1.9936 0.5792  P    30.617     .500    -2.799     .300   .135000   .206000   .1518000      .000      .000  
+75 728 42621.00 I   .142061  .020845   .202241  .008943  I  .1508998  .0008191  1.9975 0.5792  P    30.316     .500    -2.919     .300   .135000   .204000   .1499000      .000      .000  
+75 729 42622.00 I   .141647  .019441   .200481  .010228  I  .1489135  .0008191  1.9702 0.5792  P    30.229     .500    -2.988     .300   .136000   .203000   .1480000      .000      .000  
+75 730 42623.00 I   .141250  .019943   .198715  .009985  I  .1469678  .0008191  1.9178 0.5792  P    30.303     .500    -3.042     .300   .136000   .201000   .1461000      .000      .000  
+75 731 42624.00 I   .140866  .020137   .196941  .009761  I  .1450827  .0008191  1.8512 0.4314  P    30.190     .500    -3.161     .300   .136000   .199000   .1442000      .000      .000  
+75 8 1 42625.00 I   .140495  .018143   .195159  .009683  I  .1432652  .0002710  1.7852 0.4314  P    29.655     .500    -3.324     .300   .136000   .197000   .1422000      .000      .000  
+75 8 2 42626.00 I   .140131  .021024   .193370  .010410  I  .1415062  .0002710  1.7375 0.1916  P    28.937     .500    -3.394     .300   .136000   .195000   .1402000      .000      .000  
+75 8 3 42627.00 I   .139771  .018077   .191574  .012753  I  .1397771  .0002710  1.7291 0.1916  P    28.525     .500    -3.304     .300   .136000   .194000   .1383000      .000      .000  
+75 8 4 42628.00 I   .139409  .017944   .189773  .012977  I  .1380281  .0002710  1.7806 0.1916  P    28.634     .500    -3.183     .300   .136000   .192000   .1363000      .000      .000  
+75 8 5 42629.00 I   .139043  .017124   .187967  .013652  I  .1361916  .0002710  1.9052 0.1916  P    29.054     .500    -3.205     .300   .136000   .190000   .1343000      .000      .000  
+75 8 6 42630.00 I   .138669  .022640   .186160  .013717  I  .1341949  .0002710  2.0981 0.1916  P    29.439     .500    -3.327     .300   .136000   .188000   .1322000      .000      .000  
+75 8 7 42631.00 I   .138283  .022450   .184355  .014082  I  .1319829  .0002710  2.3283 0.1916  P    29.583     .500    -3.320     .300   .136000   .186000   .1301000      .000      .000  
+75 8 8 42632.00 I   .137886  .022251   .182552  .014325  I  .1295436  .0002710  2.5426 0.1916  P    29.436     .500    -3.059     .300   .137000   .185000   .1279000      .000      .000  
+75 8 9 42633.00 I   .137474  .022840   .180753  .013750  I  .1269223  .0002710  2.6831 0.1916  P    29.046     .500    -2.692     .300   .137000   .183000   .1258000      .000      .000  
+75 810 42634.00 I   .137044  .022963   .178960  .011623  I  .1242150  .0002710  2.7107 0.1916  P    28.527     .500    -2.455     .300   .137000   .181000   .1237000      .000      .000  
+75 811 42635.00 I   .136594  .020171   .177173  .011802  I  .1215397  .0002710  2.6219 0.1916  P    28.016     .500    -2.414     .300   .137000   .179000   .1215000      .000      .000  
+75 812 42636.00 I   .136127  .025956   .175389  .012163  I  .1189993  .0002710  2.4488 0.1916  P    27.617     .500    -2.481     .300   .137000   .178000   .1193000      .000      .000  
+75 813 42637.00 I   .135643  .020715   .173609  .012551  I  .1166529  .0002710  2.2434 0.1916  P    27.419     .500    -2.591     .300   .137000   .176000   .1172000      .000      .000  
+75 814 42638.00 I   .135138  .020951   .171833  .010654  I  .1145063  .0002710  2.0566 0.1916  P    27.478     .500    -2.759     .300   .137000   .175000   .1150000      .000      .000  
+75 815 42639.00 I   .134611  .020805   .170059  .010889  I  .1125222  .0002710  1.9222 0.1916  P    27.724     .500    -2.968     .300   .137000   .173000   .1128000      .000      .000  
+75 816 42640.00 I   .134057  .017200   .168287  .010837  I  .1106400  .0002710  1.8535 0.1916  P    27.938     .500    -3.119     .300   .137000   .172000   .1106000      .000      .000  
+75 817 42641.00 I   .133477  .017380   .166517  .010796  I  .1087940  .0002710  1.8483 0.1916  P    27.962     .500    -3.138     .300   .137000   .170000   .1084000      .000      .000  
+75 818 42642.00 I   .132872  .017505   .164748  .010647  I  .1069262  .0002710  1.8947 0.1916  P    27.861     .500    -3.062     .300   .136000   .169000   .1063000      .000      .000  
+75 819 42643.00 I   .132241  .006851   .162980  .007233  I  .1049929  .0002710  1.9761 0.1916  P    27.810     .500    -2.969     .300   .136000   .167000   .1041000      .000      .000  
+75 820 42644.00 I   .131582  .008878   .161210  .006966  I  .1029686  .0002710  2.0736 0.1916  P    27.857     .500    -2.863     .300   .136000   .166000   .1019000      .000      .000  
+75 821 42645.00 I   .130895  .016443   .159439  .006546  I  .1008470  .0002710  2.1674 0.1916  P    27.903     .500    -2.693     .300   .136000   .165000   .0998000      .000      .000  
+75 822 42646.00 I   .130180  .019536   .157663  .013390  I  .0986407  .0002710  2.2407 0.1916  P    27.854     .500    -2.477     .300   .136000   .163000   .0976000      .000      .000  
+75 823 42647.00 I   .129438  .019720   .155884  .013430  I  .0963759  .0002710  2.2830 0.3416  P    27.677     .500    -2.344     .300   .135000   .162000   .0955000      .000      .000  
+75 824 42648.00 I   .128669  .019531   .154099  .020073  I  .0940867  .0006271  2.2893 0.3416  P    27.403     .500    -2.405     .300   .135000   .160000   .0933000      .000      .000  
+75 825 42649.00 I   .127872  .019371   .152307  .020009  I  .0918087  .0006271  2.2614 0.4434  P    27.172     .500    -2.623     .300   .135000   .159000   .0912000      .000      .000  
+75 826 42650.00 I   .127042  .020107   .150509  .020119  I  .0895730  .0006271  2.2060 0.3942  P    27.183     .500    -2.857     .300   .134000   .158000   .0890000      .000      .000  
+75 827 42651.00 I   .126175  .021462   .148704  .020071  I  .0874023  .0004778  2.1336 0.3766  P    27.423     .500    -3.033     .300   .134000   .156000   .0869000      .000      .000  
+75 828 42652.00 I   .125270  .020517   .146895  .020025  I  .0853067  .0004173  2.0585 0.3172  P    27.550     .500    -3.201     .300   .133000   .155000   .0847000      .000      .000  
+75 829 42653.00 I   .124326  .021597   .145082  .015990  I  .0832809  .0004173  1.9967 0.2443  P    27.248     .500    -3.396     .300   .133000   .153000   .0826000      .000      .000  
+75 830 42654.00 I   .123343  .023402   .143270  .016835  I  .0813027  .0002541  1.9667 0.2443  P    26.664     .500    -3.527     .300   .132000   .152000   .0804000      .000      .000  
+75 831 42655.00 I   .122316  .024394   .141461  .009961  I  .0793306  .0002541  1.9876 0.1797  P    26.303     .500    -3.484     .300   .131000   .151000   .0781000      .000      .000  
+75 9 1 42656.00 I   .121242  .025299   .139659  .010617  I  .0773052  .0002541  2.0756 0.1806  P    26.468     .500    -3.325     .300   .130000   .149000   .0758000      .000      .000  
+75 9 2 42657.00 I   .120119  .026575   .137868  .010905  I  .0751549  .0002566  2.2370 0.1857  P    26.965     .500    -3.221     .300   .128000   .148000   .0735000      .000      .000  
+75 9 3 42658.00 I   .118946  .024728   .136090  .014194  I  .0728116  .0002710  2.4572 0.1866  P    27.361     .500    -3.219     .300   .127000   .146000   .0712000      .000      .000  
+75 9 4 42659.00 I   .117724  .021603   .134331  .014060  I  .0702342  .0002710  2.6963 0.1916  P    27.395     .500    -3.177     .300   .126000   .145000   .0689000      .000      .000  
+75 9 5 42660.00 I   .116450  .018392   .132594  .019354  I  .0674318  .0002710  2.8969 0.1916  P    27.125     .500    -2.992     .300   .125000   .144000   .0665000      .000      .000  
+75 9 6 42661.00 I   .115122  .015947   .130881  .018430  I  .0644718  .0002710  3.0031 0.1916  P    26.806     .500    -2.774     .300   .123000   .143000   .0641000      .000      .000  
+75 9 7 42662.00 I   .113735  .021923   .129198  .019283  I  .0614672  .0002710  2.9843 0.1916  P    26.652     .500    -2.685     .300   .122000   .141000   .0618000      .000      .000  
+75 9 8 42663.00 I   .112285  .021763   .127549  .018556  I  .0585415  .0002710  2.8505 0.1916  P    26.621     .500    -2.694     .300   .120000   .140000   .0594000      .000      .000  
+75 9 9 42664.00 I   .110770  .019606   .125938  .018569  I  .0557892  .0002710  2.6476 0.1916  P    26.478     .500    -2.635     .300   .119000   .139000   .0570000      .000      .000  
+75 910 42665.00 I   .109188  .020552   .124368  .016534  I  .0532491  .0002710  2.4364 0.1916  P    26.144     .500    -2.494     .300   .117000   .138000   .0546000      .000      .000  
+75 911 42666.00 I   .107540  .020747   .122843  .016919  I  .0509018  .0002710  2.2689 0.1916  P    25.913     .500    -2.458     .300   .116000   .137000   .0521000      .000      .000  
+75 912 42667.00 I   .105831  .021163   .121362  .023017  I  .0486878  .0002710  2.1719 0.1916  P    26.119     .500    -2.640     .300   .114000   .135000   .0497000      .000      .000  
+75 913 42668.00 I   .104064  .022424   .119928  .023663  I  .0465338  .0002710  2.1474 0.1916  P    26.679     .500    -2.891     .300   .113000   .134000   .0472000      .000      .000  
+75 914 42669.00 I   .102244  .021471   .118541  .022066  I  .0443734  .0002710  2.1816 0.2164  P    27.169     .500    -2.977     .300   .111000   .133000   .0448000      .000      .000  
+75 915 42670.00 I   .100377  .023134   .117203  .022328  I  .0421573  .0003375  2.2557 0.2164  P    27.340     .500    -2.860     .300   .109000   .132000   .0423000      .000      .000  
+75 916 42671.00 I   .098468  .022834   .115914  .022023  I  .0398549  .0003375  2.3512 0.2386  P    27.333     .500    -2.689     .300   .107000   .131000   .0398000      .000      .000  
+75 917 42672.00 I   .096524  .022272   .114671  .021669  I  .0374535  .0003375  2.4507 0.2386  P    27.363     .500    -2.566     .300   .106000   .130000   .0374000      .000      .000  
+75 918 42673.00 I   .094556  .021722   .113472  .021296  I  .0349574  .0003375  2.5383 0.2386  P    27.426     .500    -2.442     .300   .104000   .129000   .0349000      .000      .000  
+75 919 42674.00 I   .092568  .020850   .112315  .009853  I  .0323854  .0003375  2.6005 0.2386  P    27.398     .500    -2.269     .300   .102000   .128000   .0324000      .000      .000  
+75 920 42675.00 I   .090562  .020559   .111197  .008617  I  .0297676  .0003375  2.6293 0.1691  P    27.245     .500    -2.138     .300   .100000   .127000   .0298000      .000      .000  
+75 921 42676.00 I   .088541  .015420   .110116  .009317  I  .0271382  .0000206  2.6241 0.1691  P    27.039     .500    -2.181     .300   .098000   .126000   .0272000      .000      .000  
+75 922 42677.00 I   .086507  .011918   .109068  .009059  I  .0245291  .0000206  2.5899 0.0146  P    26.888     .500    -2.395     .300   .097000   .124000   .0247000      .000      .000  
+75 923 42678.00 I   .084463  .016149   .108051  .010960  I  .0219647  .0000206  2.5367 0.2542  P    26.908     .500    -2.632     .300   .095000   .123000   .0221000      .000      .000  
+75 924 42679.00 I   .082411  .016596   .107063  .010773  I  .0194578  .0005080  2.4773 0.2398  P    27.127     .500    -2.776     .300   .093000   .122000   .0195000      .000      .000  
+75 925 42680.00 I   .080351  .023567   .106103  .012355  I  .0170073  .0004791  2.4265 0.3491  P    27.376     .500    -2.859     .300   .091000   .121000   .0168000      .000      .000  
+75 926 42681.00 I   .078281  .023322   .105169  .009325  I  .0145964  .0004791  2.4010 0.3787  P    27.412     .500    -2.962     .300   .089000   .120000   .0141000      .000      .000  
+75 927 42682.00 I   .076200  .023726   .104262  .010817  I  .0121912  .0005866  2.4178 0.3787  P    27.224     .500    -3.069     .300   .087000   .119000   .0114000      .000      .000  
+75 928 42683.00 I   .074109  .022409   .103379  .012421  I  .0097417  .0005866  2.4917 0.4148  P    27.096     .500    -3.086     .300   .085000   .118000   .0087000      .000      .000  
+75 929 42684.00 I   .072008  .022385   .102523  .012949  I  .0071859  .0005866  2.6311 0.3594  P    27.287     .500    -2.990     .300   .083000   .117000   .0060000      .000      .000  
+75 930 42685.00 I   .069891  .019461   .101693  .014099  I  .0044591  .0004154  2.8318 0.3231  P    27.730     .500    -2.870     .300   .081000   .116000   .0032000      .000      .000  
+7510 1 42686.00 I   .067753  .019003   .100891  .016291  I  .0015094  .0002710  3.0713 0.2480  P    28.100     .500    -2.778     .300   .079000   .115000   .0004000      .000      .000  
+7510 2 42687.00 I   .065591  .009604   .100115  .015106  I -.0016819  .0002710  3.3061 0.1916  P    28.134     .500    -2.657     .300   .077000   .114000  -.0023000      .000      .000  
+7510 3 42688.00 I   .063400  .014906   .099364  .015200  I -.0050831  .0002710  3.4819 0.1916  P    27.870     .500    -2.469     .300   .075000   .113000  -.0051000      .000      .000  
+7510 4 42689.00 I   .061177  .013008   .098637  .014220  I -.0086107  .0002710  3.5527 0.1916  P    27.631     .500    -2.328     .300   .073000   .112000  -.0079000      .000      .000  
+7510 5 42690.00 I   .058918  .013185   .097935  .013350  I -.0121475  .0002710  3.5005 0.1916  P    27.737     .500    -2.357     .300   .071000   .111000  -.0108000      .000      .000  
+7510 6 42691.00 I   .056619  .014942   .097260  .012933  I -.0155777  .0002710  3.3464 0.1916  P    28.141     .500    -2.465     .300   .069000   .110000  -.0137000      .000      .000  
+7510 7 42692.00 I   .054278  .015263   .096614  .011629  I -.0188238  .0002710  3.1428 0.1916  P    28.408     .500    -2.407     .300   .066000   .110000  -.0167000      .000      .000  
+7510 8 42693.00 I   .051893  .016599   .095998  .012287  I -.0218672  .0002710  2.9508 0.1916  P    28.222     .500    -2.123     .300   .064000   .109000  -.0196000      .000      .000  
+7510 9 42694.00 I   .049466  .017675   .095416  .012491  I -.0247445  .0002710  2.8161 0.1916  P    27.839     .500    -1.865     .300   .062000   .108000  -.0225000      .000      .000  
+751010 42695.00 I   .046997  .013812   .094868  .013567  I -.0275243  .0002710  2.7562 0.3300  P    27.822     .500    -1.894     .300   .060000   .107000  -.0255000      .000      .000  
+751011 42696.00 I   .044489  .018819   .094358  .015391  I -.0302791  .0006019  2.7629 0.2532  P    28.345     .500    -2.158     .300   .057000   .107000  -.0285000      .000      .000  
+751012 42697.00 I   .041945  .019113   .093886  .015401  I -.0330653  .0004277  2.8150 0.3692  P    29.005     .500    -2.373     .300   .055000   .106000  -.0315000      .000      .000  
+751013 42698.00 I   .039363  .021381   .093452  .015620  I -.0359169  .0004277  2.8904 0.3024  P    29.382     .500    -2.375     .300   .052000   .106000  -.0345000      .000      .000  
+751014 42699.00 I   .036746  .021577   .093054  .014291  I -.0388482  .0004277  2.9720 0.3024  P    29.494     .500    -2.249     .300   .050000   .105000  -.0375000      .000      .000  
+751015 42700.00 I   .034093  .020685   .092693  .012976  I -.0418584  .0004277  3.0460 0.3024  P    29.578     .500    -2.132     .300   .047000   .105000  -.0406000      .000      .000  
+751016 42701.00 I   .031402  .020663   .092367  .015041  I -.0449336  .0004277  3.1000 0.2160  P    29.679     .500    -2.038     .300   .045000   .104000  -.0437000      .000      .000  
+751017 42702.00 I   .028671  .022301   .092077  .014046  I -.0480485  .0000606  3.1241 0.2532  P    29.671     .500    -1.920     .300   .042000   .104000  -.0469000      .000      .000  
+751018 42703.00 I   .025899  .018015   .091825  .012042  I -.0511704  .0002710  3.1140 0.1388  P    29.538     .500    -1.810     .300   .040000   .103000  -.0500000      .000      .000  
+751019 42704.00 I   .023088  .025107   .091609  .011344  I -.0542659  .0002710  3.0722 0.1916  P    29.396     .500    -1.786     .300   .037000   .103000  -.0531000      .000      .000  
+751020 42705.00 I   .020236  .022015   .091433  .011044  I -.0573070  .0002710  3.0072 0.1916  P    29.319     .500    -1.848     .300   .034000   .103000  -.0563000      .000      .000  
+751021 42706.00 I   .017343  .024763   .091295  .020572  I -.0602766  .0002710  2.9313 0.1916  P    29.308     .500    -1.903     .300   .032000   .103000  -.0594000      .000      .000  
+751022 42707.00 I   .014408  .024511   .091196  .020956  I -.0631707  .0002710  2.8586 0.1916  P    29.393     .500    -1.888     .300   .029000   .102000  -.0626000      .000      .000  
+751023 42708.00 I   .011430  .024041   .091136  .022092  I -.0660001  .0002710  2.8046 0.1457  P    29.600     .500    -1.846     .300   .027000   .102000  -.0657000      .000      .000  
+751024 42709.00 I   .008415  .022281   .091113  .023521  I -.0687916  .0001072  2.7857 0.1457  P    29.863     .500    -1.857     .300   .024000   .102000  -.0689000      .000      .000  
+751025 42710.00 I   .005366  .025589   .091128  .023675  I -.0715879  .0001072  2.8162 0.1656  P    30.073     .500    -1.929     .300   .021000   .102000  -.0721000      .000      .000  
+751026 42711.00 I   .002284  .018221   .091180  .024643  I -.0744434  .0003134  2.9050 0.1656  P    30.229     .500    -1.990     .300   .018000   .102000  -.0753000      .000      .000  
+751027 42712.00 I  -.000828  .020257   .091271  .024852  I -.0774171  .0003134  3.0513 0.2216  P    30.428     .500    -1.982     .300   .016000   .102000  -.0785000      .000      .000  
+751028 42713.00 I  -.003970  .016502   .091400  .017716  I -.0805602  .0003134  3.2403 0.2216  P    30.714     .500    -1.903     .300   .013000   .102000  -.0817000      .000      .000  
+751029 42714.00 I  -.007140  .020348   .091566  .016086  I -.0839023  .0003134  3.4430 0.2449  P    30.982     .500    -1.764     .300   .010000   .102000  -.0849000      .000      .000  
+751030 42715.00 I  -.010330  .020296   .091769  .012218  I -.0874367  .0003763  3.6172 0.2449  P    31.066     .500    -1.546     .300   .007000   .102000  -.0881000      .000      .000  
+751031 42716.00 I  -.013536  .020907   .092013  .015480  I -.0911128  .0003763  3.7198 0.2319  P    30.926     .500    -1.273     .300   .004000   .102000  -.0913000      .000      .000  
+7511 1 42717.00 I  -.016752  .016677   .092297  .015355  I -.0948426  .0002710  3.7213 0.2319  P    30.755     .500    -1.077     .300   .001000   .103000  -.0945000      .000      .000  
+7511 2 42718.00 I  -.019973  .016852   .092625  .013346  I -.0985208  .0002710  3.6191 0.1916  P    30.838     .500    -1.097     .300  -.002000   .103000  -.0977000      .000      .000  
+7511 3 42719.00 I  -.023191  .014464   .092996  .013846  I -.1020561  .0002710  3.4428 0.1916  P    31.237     .500    -1.281     .300  -.005000   .103000  -.1009000      .000      .000  
+7511 4 42720.00 I  -.026399  .019220   .093412  .013924  I -.1053988  .0002710  3.2437 0.1916  P    31.649     .500    -1.381     .300  -.008000   .104000  -.1041000      .000      .000  
+7511 5 42721.00 I  -.029590  .016092   .093876  .017300  I -.1085538  .0002710  3.0754 0.1916  P    31.733     .500    -1.225     .300  -.011000   .105000  -.1073000      .000      .000  
+7511 6 42722.00 I  -.032760  .019046   .094391  .017656  I -.1115712  .0002710  2.9720 0.1916  P    31.560     .500     -.937     .300  -.013000   .105000  -.1106000      .000      .000  
+7511 7 42723.00 I  -.035902  .022159   .094961  .012633  I -.1145216  .0002710  2.9400 0.1916  P    31.549     .500     -.794     .300  -.016000   .106000  -.1138000      .000      .000  
+7511 8 42724.00 I  -.039010  .022266   .095587  .014269  I -.1174698  .0002710  2.9637 0.1916  P    31.932     .500     -.909     .300  -.019000   .107000  -.1170000      .000      .000  
+7511 9 42725.00 I  -.042077  .022402   .096271  .016171  I -.1204590  .0002710  3.0175 0.1916  P    32.475     .500    -1.133     .300  -.022000   .108000  -.1202000      .000      .000  
+751110 42726.00 I  -.045090  .022756   .097017  .015950  I -.1235075  .0002710  3.0793 0.1916  P    32.824     .500    -1.274     .300  -.025000   .110000  -.1234000      .000      .000  
+751111 42727.00 I  -.048044  .018617   .097829  .016001  I -.1266150  .0002710  3.1334 0.1584  P    32.930     .500    -1.283     .300  -.027000   .111000  -.1266000      .000      .000  
+751112 42728.00 I  -.050932  .019412   .098710  .012043  I -.1297683  .0001642  3.1694 0.1584  P    32.958     .500    -1.220     .300  -.030000   .113000  -.1298000      .000      .000  
+751113 42729.00 I  -.053751  .020194   .099660  .013092  I -.1329450  .0001642  3.1790 0.1035  P    32.967     .500    -1.125     .300  -.033000   .114000  -.1330000      .000      .000  
+751114 42730.00 I  -.056499  .016215   .100680  .016603  I -.1361156  .0001262  3.1566 0.1036  P    32.886     .500    -1.006     .300  -.036000   .116000  -.1362000      .000      .000  
+751115 42731.00 I  -.059173  .016433   .101771  .016948  I -.1392475  .0001265  3.1021 0.0893  P    32.735     .500     -.890     .300  -.039000   .117000  -.1393000      .000      .000  
+751116 42732.00 I  -.061773  .017450   .102933  .015413  I -.1423112  .0001265  3.0217 0.0894  P    32.641     .500     -.802     .300  -.041000   .119000  -.1425000      .000      .000  
+751117 42733.00 I  -.064300  .017370   .104164  .017321  I -.1452861  .0001265  2.9268 0.0815  P    32.635     .500     -.723     .300  -.044000   .120000  -.1456000      .000      .000  
+751118 42734.00 I  -.066758  .024230   .105465  .017692  I -.1481645  .0001027  2.8311 0.0769  P    32.626     .500     -.618     .300  -.047000   .122000  -.1488000      .000      .000  
+751119 42735.00 I  -.069154  .023483   .106830  .018050  I -.1509530  .0000876  2.7496 0.0700  P    32.596     .500     -.512     .300  -.050000   .124000  -.1519000      .000      .000  
+751120 42736.00 I  -.071496  .026012   .108256  .017179  I -.1536738  .0000951  2.6984 0.2431  P    32.670     .500     -.476     .300  -.053000   .126000  -.1550000      .000      .000  
+751121 42737.00 I  -.073792  .027358   .109737  .013581  I -.1563645  .0004782  2.6915 0.2438  P    32.934     .500     -.540     .300  -.055000   .127000  -.1582000      .000      .000  
+751122 42738.00 I  -.076051  .027336   .111268  .011999  I -.1590747  .0004782  2.7385 0.3381  P    33.292     .500     -.650     .300  -.058000   .129000  -.1613000      .000      .000  
+751123 42739.00 I  -.078276  .030164   .112848  .011354  I -.1618592  .0004782  2.8387 0.3381  P    33.569     .500     -.722     .300  -.061000   .131000  -.1644000      .000      .000  
+751124 42740.00 I  -.080474  .030179   .114472  .011018  I -.1647655  .0004782  2.9789 0.4136  P    33.697     .500     -.709     .300  -.064000   .133000  -.1674000      .000      .000  
+751125 42741.00 I  -.082652  .025827   .116140  .015525  I -.1678220  .0006749  3.1339 0.4136  P    33.739     .500     -.613     .300  -.066000   .135000  -.1705000      .000      .000  
+751126 42742.00 I  -.084820  .025313   .117848  .015519  I -.1710272  .0006749  3.2706 0.3636  P    33.773     .500     -.438     .300  -.069000   .136000  -.1735000      .000      .000  
+751127 42743.00 I  -.086983  .019199   .119596  .015388  I -.1743461  .0002710  3.3559 0.3636  P    33.791     .500     -.186     .300  -.071000   .138000  -.1766000      .000      .000  
+751128 42744.00 I  -.089145  .017040   .121384  .019244  I -.1777136  .0002710  3.3649 0.1916  P    33.735     .500      .112     .300  -.074000   .140000  -.1796000      .000      .000  
+751129 42745.00 I  -.091308  .017353   .123210  .020907  I -.1810477  .0002710  3.2894 0.1916  P    33.609     .500      .348     .300  -.077000   .142000  -.1826000      .000      .000  
+751130 42746.00 I  -.093472  .010537   .125073  .021761  I -.1842687  .0002710  3.1430 0.1916  P    33.523     .500      .389     .300  -.079000   .144000  -.1855000      .000      .000  
+7512 1 42747.00 I  -.095638  .013659   .126970  .020126  I -.1873216  .0002710  2.9604 0.1916  P    33.583     .500      .209     .300  -.082000   .145000  -.1885000      .000      .000  
+7512 2 42748.00 I  -.097804  .011946   .128899  .016733  I -.1901925  .0002710  2.7867 0.1916  P    33.763     .500     -.037     .300  -.084000   .147000  -.1914000      .000      .000  
+7512 3 42749.00 I  -.099969  .011717   .130854  .017512  I -.1929110  .0002710  2.6611 0.1916  P    33.946     .500     -.121     .300  -.087000   .149000  -.1944000      .000      .000  
+7512 4 42750.00 I  -.102128  .011789   .132833  .020827  I -.1955375  .0002710  2.6039 0.1916  P    34.104     .500      .028     .300  -.089000   .151000  -.1972000      .000      .000  
+7512 5 42751.00 I  -.104281  .011812   .134833  .017899  I -.1981401  .0002710  2.6107 0.1916  P    34.334     .500      .264     .300  -.091000   .152000  -.2000000      .000      .000  
+7512 6 42752.00 I  -.106423  .012843   .136852  .016085  I -.2007731  .0002710  2.6601 0.1916  P    34.688     .500      .380     .300  -.093000   .154000  -.2029000      .000      .000  
+7512 7 42753.00 I  -.108550  .011600   .138887  .015526  I -.2034657  .0002710  2.7256 0.1916  P    35.044     .500      .309     .300  -.095000   .155000  -.2057000      .000      .000  
+7512 8 42754.00 I  -.110656  .014017   .140936  .017409  I -.2062227  .0002710  2.7862 0.1916  P    35.233     .500      .144     .300  -.097000   .157000  -.2085000      .000      .000  
+7512 9 42755.00 I  -.112735  .019088   .142996  .017600  I -.2090323  .0002710  2.8293 0.1916  P    35.220     .500      .012     .300  -.099000   .159000  -.2113000      .000      .000  
+751210 42756.00 I  -.114783  .019234   .145066  .016374  I -.2118730  .0002710  2.8477 0.2693  P    35.090     .500     -.023     .300  -.100000   .160000  -.2141000      .000      .000  
+751211 42757.00 I  -.116794  .019189   .147142  .012163  I -.2147182  .0004655  2.8378 0.2693  P    34.915     .500      .034     .300  -.102000   .162000  -.2168000      .000      .000  
+751212 42758.00 I  -.118762  .019864   .149221  .011741  I -.2175390  .0004655  2.7991 0.3292  P    34.726     .500      .128     .300  -.103000   .163000  -.2196000      .000      .000  
+751213 42759.00 I  -.120682  .019543   .151303  .010811  I -.2203080  .0004655  2.7353 0.3292  P    34.587     .500      .196     .300  -.105000   .165000  -.2224000      .000      .000  
+751214 42760.00 I  -.122549  .020240   .153388  .014069  I -.2230042  .0004655  2.6554 0.3292  P    34.567     .500      .232     .300  -.107000   .167000  -.2252000      .000      .000  
+751215 42761.00 I  -.124360  .018935   .155474  .011612  I -.2256178  .0004655  2.5725 0.3292  P    34.630     .500      .288     .300  -.108000   .169000  -.2279000      .000      .000  
+751216 42762.00 I  -.126111  .017967   .157562  .014048  I -.2281532  .0004655  2.5014 0.2693  P    34.658     .500      .392     .300  -.110000   .170000  -.2307000      .000      .000  
+751217 42763.00 I  -.127800  .017891   .159653  .014440  I -.2306297  .0002710  2.4574 0.2693  P    34.618     .500      .466     .300  -.111000   .172000  -.2334000      .000      .000  
+751218 42764.00 I  -.129423  .019098   .161749  .014423  I -.2330821  .0002710  2.4556 0.1916  P    34.630     .500      .399     .300  -.113000   .174000  -.2362000      .000      .000  
+751219 42765.00 I  -.130977  .021330   .163852  .013762  I -.2355587  .0002710  2.5073 0.1916  P    34.823     .500      .196     .300  -.114000   .176000  -.2389000      .000      .000  
+751220 42766.00 I  -.132461  .020970   .165965  .015005  I -.2381153  .0002710  2.6150 0.1916  P    35.160     .500      .004     .300  -.116000   .178000  -.2416000      .000      .000  
+751221 42767.00 I  -.133879  .020276   .168089  .019798  I -.2408033  .0002710  2.7667 0.1916  P    35.443     .500     -.031     .300  -.117000   .180000  -.2444000      .000      .000  
+751222 42768.00 I  -.135231  .017999   .170225  .020846  I -.2436542  .0002710  2.9350 0.1916  P    35.502     .500      .108     .300  -.119000   .182000  -.2471000      .000      .000  
+751223 42769.00 I  -.136520  .015591   .172373  .021188  I -.2466668  .0002710  3.0837 0.1916  P    35.340     .500      .328     .300  -.120000   .184000  -.2498000      .000      .000  
+751224 42770.00 I  -.137749  .017060   .174535  .021074  I -.2498037  .0002710  3.1786 0.1916  P    35.106     .500      .535     .300  -.121000   .186000  -.2525000      .000      .000  
+751225 42771.00 I  -.138922  .016117   .176708  .023143  I -.2529990  .0002710  3.1985 0.1916  P    34.943     .500      .706     .300  -.122000   .188000  -.2551000      .000      .000  
+751226 42772.00 I  -.140046  .011635   .178891  .023424  I -.2561747  .0002710  3.1404 0.1916  P    34.869     .500      .856     .300  -.123000   .191000  -.2578000      .000      .000  
+751227 42773.00 I  -.141125  .013889   .181081  .023376  I -.2592587  .0002710  3.0189 0.1916  P    34.796     .500      .972     .300  -.124000   .193000  -.2604000      .000      .000  
+751228 42774.00 I  -.142162  .017653   .183277  .017474  I -.2622010  .0002710  2.8628 0.1916  P    34.646     .500      .995     .300  -.125000   .195000  -.2631000      .000      .000  
+751229 42775.00 I  -.143157  .024671   .185479  .022726  I -.2649847  .0002710  2.7081 0.1916  P    34.452     .500      .864     .300  -.126000   .197000  -.2657000      .000      .000  
+751230 42776.00 I  -.144114  .024174   .187689  .020654  I -.2676290  .0002710  2.5890 0.1916  P    34.340     .500      .618     .300  -.127000   .199000  -.2683000      .000      .000  
+751231 42777.00 I  -.145035  .026013   .189909  .024262  I -.2701822  .0002710  2.5284 0.1916  P    34.425     .500      .421     .300  -.128000   .202000  -.2710000      .000      .000  
+76 1 1 42778.00 I  -.145922  .027744   .192143  .023998  I  .7272928  .0002710  2.5319 0.1916  P    34.708     .500      .435     .300  -.129000   .204000   .7264000      .000      .000  
+76 1 2 42779.00 I  -.146779  .031985   .194393  .023882  I  .7247370  .0002710  2.5864 0.1916  P    35.073     .500      .657     .300  -.130000   .206000   .7238000      .000      .000  
+76 1 3 42780.00 I  -.147599  .035403   .196662  .022891  I  .7221112  .0002710  2.6674 0.1916  P    35.373     .500      .909     .300  -.131000   .208000   .7211000      .000      .000  
+76 1 4 42781.00 I  -.148380  .033483   .198951  .024308  I  .7194019  .0002710  2.7494 0.1916  P    35.531     .500     1.005     .300  -.131000   .210000   .7185000      .000      .000  
+76 1 5 42782.00 I  -.149117  .028982   .201257  .017352  I  .7166183  .0002710  2.8136 0.1916  P    35.547     .500      .908     .300  -.132000   .213000   .7158000      .000      .000  
+76 1 6 42783.00 I  -.149810  .029323   .203580  .021269  I  .7137839  .0002710  2.8500 0.1916  P    35.439     .500      .728     .300  -.132000   .215000   .7132000      .000      .000  
+76 1 7 42784.00 I  -.150451  .031620   .205917  .016665  I  .7109287  .0002710  2.8551 0.1916  P    35.221     .500      .594     .300  -.133000   .217000   .7105000      .000      .000  
+76 1 8 42785.00 I  -.151041  .029530   .208267  .015592  I  .7080839  .0002710  2.8296 0.1916  P    34.957     .500      .551     .300  -.133000   .219000   .7078000      .000      .000  
+76 1 9 42786.00 I  -.151577  .025456   .210631  .016068  I  .7052786  .0002710  2.7770 0.1916  P    34.765     .500      .540     .300  -.134000   .221000   .7051000      .000      .000  
+76 110 42787.00 I  -.152057  .019713   .213008  .017090  I  .7025362  .0002710  2.7056 0.3409  P    34.716     .500      .476     .300  -.134000   .224000   .7024000      .000      .000  
+76 111 42788.00 I  -.152481  .019619   .215400  .014602  I  .6998696  .0006256  2.6275 0.3409  P    34.767     .500      .353     .300  -.135000   .226000   .6997000      .000      .000  
+76 112 42789.00 I  -.152852  .018850   .217808  .018479  I  .6972785  .0006256  2.5574 0.4424  P    34.811     .500      .267     .300  -.135000   .228000   .6970000      .000      .000  
+76 113 42790.00 I  -.153173  .019967   .220234  .014358  I  .6947468  .0006256  2.5111 0.4424  P    34.794     .500      .300     .300  -.135000   .230000   .6942000      .000      .000  
+76 114 42791.00 I  -.153446  .013855   .222679  .014233  I  .6922431  .0006256  2.5043 0.4211  P    34.754     .500      .377     .300  -.135000   .232000   .6915000      .000      .000  
+76 115 42792.00 I  -.153675  .013816   .225144  .014771  I  .6897202  .0005639  2.5514 0.4641  P    34.769     .500      .325     .300  -.136000   .235000   .6887000      .000      .000  
+76 116 42793.00 I  -.153862  .013765   .227632  .014258  I  .6871198  .0006856  2.6599 0.4548  P    34.906     .500      .089     .300  -.136000   .237000   .6860000      .000      .000  
+76 117 42794.00 I  -.154011  .013716   .230140  .015580  I  .6843817  .0007137  2.8244 0.4948  P    35.170     .500     -.155     .300  -.136000   .239000   .6832000      .000      .000  
+76 118 42795.00 I  -.154124  .015049   .232668  .020301  I  .6814605  .0007137  3.0204 0.5047  P    35.454     .500     -.171     .300  -.136000   .241000   .6803000      .000      .000  
+76 119 42796.00 I  -.154204  .018636   .235217  .016831  I  .6783439  .0007137  3.2073 0.5047  P    35.579     .500      .086     .300  -.136000   .243000   .6775000      .000      .000  
+76 120 42797.00 I  -.154253  .016248   .237786  .017702  I  .6750641  .0007137  3.3396 0.5664  P    35.440     .500      .431     .300  -.137000   .246000   .6746000      .000      .000  
+76 121 42798.00 I  -.154273  .014295   .240376  .017099  I  .6716935  .0008798  3.3855 0.3817  P    35.124     .500      .646     .300  -.137000   .248000   .6718000      .000      .000  
+76 122 42799.00 I  -.154264  .014284   .242988  .016999  I  .6683242  .0002710  3.3379 0.4603  P    34.836     .500      .665     .300  -.137000   .250000   .6689000      .000      .000  
+76 123 42800.00 I  -.154226  .016091   .245617  .016843  I  .6650428  .0002710  3.2149 0.1916  P    34.694     .500      .568     .300  -.137000   .252000   .6660000      .000      .000  
+76 124 42801.00 I  -.154162  .022566   .248261  .015025  I  .6619076  .0002710  3.0523 0.2230  P    34.641     .500      .454     .300  -.137000   .255000   .6630000      .000      .000  
+76 125 42802.00 I  -.154076  .022431   .250916  .010501  I  .6589383  .0003543  2.8895 0.2230  P    34.536     .500      .352     .300  -.138000   .257000   .6601000      .000      .000  
+76 126 42803.00 I  -.153970  .019010   .253580  .013465  I  .6561175  .0003543  2.7601 0.2505  P    34.323     .500      .228     .300  -.138000   .260000   .6571000      .000      .000  
+76 127 42804.00 I  -.153848  .018940   .256250  .014309  I  .6533998  .0003543  2.6856 0.2505  P    34.108     .500      .060     .300  -.138000   .262000   .6542000      .000      .000  
+76 128 42805.00 I  -.153715  .019674   .258922  .015134  I  .6507259  .0003543  2.6724 0.2505  P    34.068     .500     -.096     .300  -.138000   .264000   .6512000      .000      .000  
+76 129 42806.00 I  -.153574  .020069   .261594  .016652  I  .6480368  .0003543  2.7138 0.2505  P    34.263     .500     -.133     .300  -.138000   .267000   .6482000      .000      .000  
+76 130 42807.00 I  -.153428  .027449   .264265  .017661  I  .6452861  .0003543  2.7916 0.2230  P    34.553     .500     -.010     .300  -.138000   .269000   .6453000      .000      .000  
+76 131 42808.00 I  -.153279  .022599   .266934  .017278  I  .6424496  .0002710  2.8812 0.2230  P    34.735     .500      .192     .300  -.138000   .272000   .6423000      .000      .000  
+76 2 1 42809.00 I  -.153131  .024773   .269604  .018088  I  .6395274  .0002710  2.9598 0.1916  P    34.750     .500      .323     .300  -.138000   .274000   .6393000      .000      .000  
+76 2 2 42810.00 I  -.152986  .024613   .272274  .016586  I  .6365390  .0002710  3.0115 0.1916  P    34.685     .500      .294     .300  -.138000   .277000   .6363000      .000      .000  
+76 2 3 42811.00 I  -.152842  .027576   .274945  .015890  I  .6335159  .0002710  3.0288 0.1916  P    34.593     .500      .135     .300  -.138000   .279000   .6333000      .000      .000  
+76 2 4 42812.00 I  -.152695  .027962   .277617  .015073  I  .6304930  .0002710  3.0114 0.1475  P    34.446     .500     -.061     .300  -.138000   .282000   .6302000      .000      .000  
+76 2 5 42813.00 I  -.152540  .029022   .280290  .022401  I  .6275034  .0001165  2.9633 0.1475  P    34.274     .500     -.229     .300  -.138000   .284000   .6272000      .000      .000  
+76 2 6 42814.00 I  -.152375  .020871   .282961  .021750  I  .6245740  .0001165  2.8924 0.0824  P    34.208     .500     -.383     .300  -.138000   .287000   .6242000      .000      .000  
+76 2 7 42815.00 I  -.152197  .022280   .285628  .021834  I  .6217225  .0001165  2.8096 0.0824  P    34.293     .500     -.590     .300  -.138000   .290000   .6213000      .000      .000  
+76 2 8 42816.00 I  -.152000  .020205   .288288  .019899  I  .6189541  .0001165  2.7288 0.0824  P    34.380     .500     -.858     .300  -.138000   .292000   .6184000      .000      .000  
+76 2 9 42817.00 I  -.151781  .024814   .290936  .019500  I  .6162592  .0001165  2.6652 0.3020  P    34.325     .500    -1.072     .300  -.137000   .295000   .6155000      .000      .000  
+76 210 42818.00 I  -.151533  .021592   .293573  .019727  I  .6136127  .0005927  2.6346 0.4191  P    34.190     .500    -1.078     .300  -.137000   .297000   .6126000      .000      .000  
+76 211 42819.00 I  -.151251  .022318   .296199  .019903  I  .6109741  .0008300  2.6517 0.5099  P    34.139     .500     -.896     .300  -.137000   .300000   .6097000      .000      .000  
+76 212 42820.00 I  -.150933  .023046   .298814  .008227  I  .6082897  .0008300  2.7278 0.5515  P    34.206     .500     -.757     .300  -.137000   .302000   .6069000      .000      .000  
+76 213 42821.00 I  -.150575  .023444   .301418  .009201  I  .6054976  .0007263  2.8667 0.5515  P    34.312     .500     -.860     .300  -.136000   .305000   .6040000      .000      .000  
+76 214 42822.00 I  -.150174  .022883   .304014  .010289  I  .6025395  .0007263  3.0558 0.5136  P    34.448     .500    -1.114     .300  -.136000   .307000   .6012000      .000      .000  
+76 215 42823.00 I  -.149726  .022415   .306599  .013488  I  .5993805  .0007263  3.2612 0.4399  P    34.660     .500    -1.217     .300  -.135000   .310000   .5983000      .000      .000  
+76 216 42824.00 I  -.149229  .019353   .309176  .013318  I  .5960279  .0004964  3.4343 0.4593  P    34.884     .500    -1.014     .300  -.135000   .312000   .5955000      .000      .000  
+76 217 42825.00 I  -.148684  .020516   .311742  .012157  I  .5925383  .0005623  3.5283 0.3750  P    34.957     .500     -.678     .300  -.134000   .314000   .5927000      .000      .000  
+76 218 42826.00 I  -.148094  .018395   .314296  .012154  I  .5890064  .0005623  3.5170 0.3897  P    34.822     .500     -.500     .300  -.134000   .317000   .5899000      .000      .000  
+76 219 42827.00 I  -.147460  .027753   .316836  .011996  I  .5855373  .0005398  3.4066 0.3897  P    34.595     .500     -.593     .300  -.133000   .319000   .5871000      .000      .000  
+76 220 42828.00 I  -.146784  .027597   .319360  .012263  I  .5822144  .0005398  3.2327 0.3817  P    34.424     .500     -.844     .300  -.133000   .322000   .5843000      .000      .000  
+76 221 42829.00 I  -.146073  .027579   .321867  .011442  I  .5790765  .0005398  3.0449 0.4322  P    34.339     .500    -1.102     .300  -.132000   .324000   .5815000      .000      .000  
+76 222 42830.00 I  -.145331  .028228   .324357  .008838  I  .5761146  .0006751  2.8872 0.3020  P    34.283     .500    -1.312     .300  -.131000   .326000   .5787000      .000      .000  
+76 223 42831.00 I  -.144566  .027037   .326827  .010557  I  .5732827  .0002710  2.7875 0.3637  P    34.198     .500    -1.485     .300  -.131000   .328000   .5759000      .000      .000  
+76 224 42832.00 I  -.143778  .026292   .329279  .014138  I  .5705177  .0002710  2.7533 0.1916  P    34.084     .500    -1.619     .300  -.130000   .331000   .5731000      .000      .000  
+76 225 42833.00 I  -.142971  .028828   .331710  .016565  I  .5677568  .0002710  2.7771 0.1916  P    34.017     .500    -1.698     .300  -.130000   .333000   .5703000      .000      .000  
+76 226 42834.00 I  -.142145  .019992   .334119  .016758  I  .5649494  .0002710  2.8431 0.1916  P    34.082     .500    -1.716     .300  -.129000   .335000   .5675000      .000      .000  
+76 227 42835.00 I  -.141302  .021732   .336505  .015875  I  .5620635  .0002710  2.9305 0.1916  P    34.266     .500    -1.682     .300  -.128000   .337000   .5646000      .000      .000  
+76 228 42836.00 I  -.140442  .021616   .338863  .015840  I  .5590883  .0002710  3.0182 0.1916  P    34.445     .500    -1.606     .300  -.128000   .339000   .5617000      .000      .000  
+76 229 42837.00 I  -.139563  .023839   .341194  .015307  I  .5560332  .0002710  3.0876 0.1916  P    34.527     .500    -1.516     .300  -.127000   .341000   .5589000      .000      .000  
+76 3 1 42838.00 I  -.138660  .025223   .343496  .016603  I  .5529235  .0002710  3.1259 0.1916  P    34.541     .500    -1.471     .300  -.127000   .343000   .5560000      .000      .000  
+76 3 2 42839.00 I  -.137733  .026716   .345769  .017604  I  .5497932  .0002710  3.1287 0.1916  P    34.536     .500    -1.539     .300  -.126000   .345000   .5531000      .000      .000  
+76 3 3 42840.00 I  -.136781  .025231   .348013  .017350  I  .5466772  .0002710  3.0982 0.1916  P    34.508     .500    -1.735     .300  -.125000   .347000   .5501000      .000      .000  
+76 3 4 42841.00 I  -.135807  .022954   .350227  .017060  I  .5436056  .0002710  3.0415 0.1916  P    34.481     .500    -2.002     .300  -.124000   .349000   .5471000      .000      .000  
+76 3 5 42842.00 I  -.134811  .021359   .352411  .017042  I  .5405995  .0002710  2.9690 0.1916  P    34.561     .500    -2.272     .300  -.124000   .350000   .5441000      .000      .000  
+76 3 6 42843.00 I  -.133796  .020792   .354565  .017960  I  .5376686  .0002710  2.8934 0.1916  P    34.759     .500    -2.538     .300  -.123000   .352000   .5411000      .000      .000  
+76 3 7 42844.00 I  -.132760  .016235   .356692  .017907  I  .5348089  .0002710  2.8292 0.1916  P    34.880     .500    -2.824     .300  -.122000   .354000   .5381000      .000      .000  
+76 3 8 42845.00 I  -.131707  .013653   .358793  .015608  I  .5320016  .0002710  2.7909 0.1916  P    34.772     .500    -3.058     .300  -.121000   .356000   .5350000      .000      .000  
+76 3 9 42846.00 I  -.130636  .011786   .360869  .015417  I  .5292134  .0002710  2.7936 0.1916  P    34.585     .500    -3.076     .300  -.120000   .358000   .5319000      .000      .000  
+76 310 42847.00 I  -.129551  .018724   .362923  .013113  I  .5263966  .0002710  2.8498 0.1916  P    34.595     .500    -2.822     .300  -.120000   .359000   .5288000      .000      .000  
+76 311 42848.00 I  -.128452  .018696   .364956  .013617  I  .5234931  .0002710  2.9680 0.1916  P    34.818     .500    -2.505     .300  -.119000   .361000   .5257000      .000      .000  
+76 312 42849.00 I  -.127341  .020624   .366971  .013536  I  .5204408  .0002710  3.1456 0.1916  P    35.016     .500    -2.426     .300  -.118000   .363000   .5226000      .000      .000  
+76 313 42850.00 I  -.126221  .020315   .368970  .013093  I  .5171889  .0002710  3.3623 0.1916  P    35.059     .500    -2.626     .300  -.117000   .365000   .5194000      .000      .000  
+76 314 42851.00 I  -.125094  .024892   .370954  .013170  I  .5137165  .0002710  3.5783 0.1916  P    35.068     .500    -2.830     .300  -.116000   .367000   .5163000      .000      .000  
+76 315 42852.00 I  -.123959  .025303   .372922  .013194  I  .5100498  .0002710  3.7419 0.1916  P    35.167     .500    -2.783     .300  -.116000   .368000   .5131000      .000      .000  
+76 316 42853.00 I  -.122815  .026578   .374874  .015764  I  .5062651  .0002710  3.8081 0.1916  P    35.289     .500    -2.563     .300  -.115000   .370000   .5100000      .000      .000  
+76 317 42854.00 I  -.121658  .021103   .376808  .016142  I  .5024721  .0002710  3.7586 0.1916  P    35.306     .500    -2.443     .300  -.114000   .372000   .5068000      .000      .000  
+76 318 42855.00 I  -.120484  .022236   .378726  .016388  I  .4987803  .0002710  3.6120 0.1916  P    35.190     .500    -2.552     .300  -.113000   .374000   .5036000      .000      .000  
+76 319 42856.00 I  -.119284  .019972   .380628  .016285  I  .4952644  .0002710  3.4166 0.1916  P    35.006     .500    -2.776     .300  -.112000   .376000   .5003000      .000      .000  
+76 320 42857.00 I  -.118052  .021091   .382514  .015835  I  .4919446  .0002710  3.2289 0.1916  P    34.848     .500    -2.980     .300  -.112000   .377000   .4971000      .000      .000  
+76 321 42858.00 I  -.116778  .016806   .384388  .015902  I  .4887897  .0002710  3.0921 0.1916  P    34.805     .500    -3.161     .300  -.111000   .379000   .4938000      .000      .000  
+76 322 42859.00 I  -.115450  .019674   .386253  .015930  I  .4857373  .0002710  3.0246 0.1916  P    34.892     .500    -3.358     .300  -.110000   .381000   .4906000      .000      .000  
+76 323 42860.00 I  -.114059  .017426   .388112  .021129  I  .4827192  .0002710  3.0210 0.1916  P    34.984     .500    -3.521     .300  -.109000   .383000   .4873000      .000      .000  
+76 324 42861.00 I  -.112595  .018859   .389968  .022575  I  .4796800  .0002710  3.0635 0.1916  P    34.953     .500    -3.577     .300  -.108000   .385000   .4839000      .000      .000  
+76 325 42862.00 I  -.111052  .017496   .391823  .023131  I  .4765838  .0002710  3.1314 0.1916  P    34.861     .500    -3.551     .300  -.107000   .386000   .4806000      .000      .000  
+76 326 42863.00 I  -.109424  .017660   .393681  .024201  I  .4734152  .0002710  3.2053 0.1916  P    34.903     .500    -3.524     .300  -.106000   .388000   .4772000      .000      .000  
+76 327 42864.00 I  -.107709  .016876   .395542  .023938  I  .4701765  .0002710  3.2692 0.1916  P    35.128     .500    -3.502     .300  -.105000   .390000   .4739000      .000      .000  
+76 328 42865.00 I  -.105906  .019459   .397406  .024321  I  .4668847  .0002710  3.3096 0.1916  P    35.376     .500    -3.418     .300  -.104000   .392000   .4705000      .000      .000  
+76 329 42866.00 I  -.104018  .020254   .399271  .024936  I  .4635681  .0002710  3.3178 0.1916  P    35.485     .500    -3.279     .300  -.103000   .394000   .4671000      .000      .000  
+76 330 42867.00 I  -.102047  .020075   .401134  .015593  I  .4602603  .0002710  3.2924 0.1916  P    35.452     .500    -3.219     .300  -.102000   .395000   .4638000      .000      .000  
+76 331 42868.00 I  -.099995  .019682   .402994  .013104  I  .4569929  .0002710  3.2381 0.1916  P    35.370     .500    -3.366     .300  -.101000   .397000   .4604000      .000      .000  
+76 4 1 42869.00 I  -.097866  .022869   .404852  .010940  I  .4537904  .0002710  3.1647 0.1916  P    35.343     .500    -3.687     .300  -.100000   .399000   .4570000      .000      .000  
+76 4 2 42870.00 I  -.095669  .023134   .406706  .009676  I  .4506658  .0002710  3.0843 0.1916  P    35.454     .500    -4.023     .300  -.099000   .401000   .4536000      .000      .000  
+76 4 3 42871.00 I  -.093413  .029528   .408553  .010259  I  .4476198  .0002710  3.0098 0.1916  P    35.683     .500    -4.271     .300  -.097000   .403000   .4502000      .000      .000  
+76 4 4 42872.00 I  -.091109  .027848   .410392  .013521  I  .4446395  .0002710  2.9552 0.1916  P    35.842     .500    -4.453     .300  -.096000   .405000   .4467000      .000      .000  
+76 4 5 42873.00 I  -.088767  .027254   .412218  .015908  I  .4416984  .0002710  2.9334 0.1916  P    35.769     .500    -4.601     .300  -.094000   .407000   .4433000      .000      .000  
+76 4 6 42874.00 I  -.086392  .027282   .414029  .016508  I  .4387576  .0002710  2.9567 0.1916  P    35.592     .500    -4.633     .300  -.093000   .409000   .4399000      .000      .000  
+76 4 7 42875.00 I  -.083990  .026590   .415821  .016570  I  .4357669  .0002710  3.0343 0.1916  P    35.623     .500    -4.456     .300  -.091000   .411000   .4366000      .000      .000  
+76 4 8 42876.00 I  -.081562  .025638   .417591  .016427  I  .4326701  .0002710  3.1686 0.1916  P    35.957     .500    -4.164     .300  -.090000   .413000   .4332000      .000      .000  
+76 4 9 42877.00 I  -.079110  .025479   .419335  .018981  I  .4294138  .0002710  3.3508 0.1916  P    36.333     .500    -3.998     .300  -.088000   .415000   .4299000      .000      .000  
+76 410 42878.00 I  -.076635  .017470   .421048  .018549  I  .4259609  .0002710  3.5560 0.1364  P    36.470     .500    -4.066     .300  -.087000   .417000   .4265000      .000      .000  
+76 411 42879.00 I  -.074138  .017454   .422728  .016428  I  .4223079  .0000313  3.7430 0.1364  P    36.362     .500    -4.200     .300  -.085000   .419000   .4232000      .000      .000  
+76 412 42880.00 I  -.071621  .013801   .424368  .013859  I  .4184967  .0000313  3.8647 0.0221  P    36.184     .500    -4.178     .300  -.083000   .421000   .4199000      .000      .000  
+76 413 42881.00 I  -.069088  .019246   .425965  .012853  I  .4146124  .0000313  3.8848 0.0221  P    36.049     .500    -4.022     .300  -.081000   .423000   .4166000      .000      .000  
+76 414 42882.00 I  -.066544  .021378   .427516  .012722  I  .4107633  .0000313  3.7961 0.0221  P    35.947     .500    -3.945     .300  -.080000   .424000   .4132000      .000      .000  
+76 415 42883.00 I  -.063992  .021303   .429019  .019956  I  .4070470  .0000313  3.6268 0.0221  P    35.821     .500    -4.045     .300  -.078000   .426000   .4099000      .000      .000  
+76 416 42884.00 I  -.061439  .021056   .430471  .017183  I  .4035190  .0000313  3.4296 0.0909  P    35.634     .500    -4.194     .300  -.076000   .428000   .4066000      .000      .000  
+76 417 42885.00 I  -.058886  .024179   .431870  .017536  I  .4001787  .0001791  3.2597 0.0702  P    35.448     .500    -4.264     .300  -.074000   .430000   .4033000      .000      .000  
+76 418 42886.00 I  -.056337  .023067   .433213  .017025  I  .3969785  .0001368  3.1529 0.1078  P    35.437     .500    -4.314     .300  -.072000   .431000   .4000000      .000      .000  
+76 419 42887.00 I  -.053792  .023358   .434496  .017256  I  .3938495  .0001202  3.1162 0.0911  P    35.686     .500    -4.457     .300  -.071000   .433000   .3968000      .000      .000  
+76 420 42888.00 I  -.051253  .018740   .435720  .017254  I  .3907280  .0001202  3.1339 0.0850  P    35.980     .500    -4.652     .300  -.069000   .434000   .3935000      .000      .000  
+76 421 42889.00 I  -.048720  .017063   .436883  .017983  I  .3875718  .0001202  3.1813 0.0850  P    35.988     .500    -4.762     .300  -.067000   .436000   .3902000      .000      .000  
+76 422 42890.00 I  -.046197  .015392   .437983  .009674  I  .3843629  .0001202  3.2364 0.0709  P    35.700     .500    -4.754     .300  -.065000   .437000   .3870000      .000      .000  
+76 423 42891.00 I  -.043688  .019258   .439018  .009643  I  .3811017  .0000752  3.2836 0.0714  P    35.482     .500    -4.722     .300  -.063000   .439000   .3837000      .000      .000  
+76 424 42892.00 I  -.041199  .019207   .439986  .009190  I  .3778019  .0000770  3.3122 0.1406  P    35.596     .500    -4.700     .300  -.061000   .440000   .3805000      .000      .000  
+76 425 42893.00 I  -.038736  .022299   .440883  .012492  I  .3744861  .0002710  3.3145 0.1409  P    35.865     .500    -4.608     .300  -.059000   .442000   .3772000      .000      .000  
+76 426 42894.00 I  -.036303  .021863   .441706  .010986  I  .3711830  .0002710  3.2866 0.1916  P    35.938     .500    -4.418     .300  -.057000   .443000   .3740000      .000      .000  
+76 427 42895.00 I  -.033903  .023053   .442451  .010729  I  .3679226  .0002710  3.2298 0.1916  P    35.729     .500    -4.275     .300  -.055000   .444000   .3707000      .000      .000  
+76 428 42896.00 I  -.031534  .022153   .443115  .010928  I  .3647305  .0002710  3.1517 0.1916  P    35.441     .500    -4.356     .300  -.053000   .445000   .3675000      .000      .000  
+76 429 42897.00 I  -.029191  .023138   .443696  .011384  I  .3616222  .0002710  3.0645 0.1916  P    35.289     .500    -4.659     .300  -.050000   .446000   .3642000      .000      .000  
+76 430 42898.00 I  -.026867  .024223   .444191  .012930  I  .3586003  .0002710  2.9810 0.1916  P    35.342     .500    -4.994     .300  -.048000   .447000   .3610000      .000      .000  
+76 5 1 42899.00 I  -.024555  .021533   .444600  .014059  I  .3556546  .0002710  2.9143 0.1916  P    35.544     .500    -5.191     .300  -.046000   .448000   .3577000      .000      .000  
+76 5 2 42900.00 I  -.022248  .019638   .444920  .010883  I  .3527621  .0002710  2.8764 0.1916  P    35.744     .500    -5.254     .300  -.044000   .449000   .3545000      .000      .000  
+76 5 3 42901.00 I  -.019943  .019551   .445150  .010874  I  .3498887  .0002710  2.8779 0.1916  P    35.771     .500    -5.278     .300  -.042000   .450000   .3512000      .000      .000  
+76 5 4 42902.00 I  -.017638  .019515   .445288  .010778  I  .3469905  .0002710  2.9269 0.1916  P    35.618     .500    -5.294     .300  -.039000   .450000   .3480000      .000      .000  
+76 5 5 42903.00 I  -.015335  .029887   .445331  .014184  I  .3440182  .0002710  3.0261 0.1916  P    35.509     .500    -5.242     .300  -.037000   .451000   .3447000      .000      .000  
+76 5 6 42904.00 I  -.013036  .032615   .445276  .013640  I  .3409233  .0002710  3.1702 0.1916  P    35.660     .500    -5.106     .300  -.035000   .452000   .3415000      .000      .000  
+76 5 7 42905.00 I  -.010745  .031145   .445122  .015107  I  .3376682  .0002710  3.3430 0.1916  P    36.018     .500    -4.963     .300  -.032000   .452000   .3381000      .000      .000  
+76 5 8 42906.00 I  -.008465  .032311   .444867  .014822  I  .3342371  .0002710  3.5165 0.1361  P    36.316     .500    -4.873     .300  -.030000   .453000   .3347000      .000      .000  
+76 5 9 42907.00 I  -.006202  .031974   .444511  .015973  I  .3306472  .0000251  3.6541 0.1361  P    36.340     .500    -4.773     .300  -.027000   .453000   .3314000      .000      .000  
+76 510 42908.00 I  -.003954  .031749   .444053  .015863  I  .3269529  .0000251  3.7198 0.0177  P    36.081     .500    -4.577     .300  -.025000   .454000   .3280000      .000      .000  
+76 511 42909.00 I  -.001718  .034863   .443494  .018669  I  .3232386  .0000251  3.6924 0.0177  P    35.686     .500    -4.353     .300  -.022000   .454000   .3246000      .000      .000  
+76 512 42910.00 I   .000512  .027128   .442837  .015306  I  .3195973  .0000251  3.5774 0.0177  P    35.326     .500    -4.278     .300  -.019000   .454000   .3212000      .000      .000  
+76 513 42911.00 I   .002734  .023574   .442084  .016928  I  .3161017  .0000251  3.4087 0.0177  P    35.081     .500    -4.407     .300  -.017000   .454000   .3177000      .000      .000  
+76 514 42912.00 I   .004949  .022560   .441237  .018179  I  .3127807  .0000251  3.2374 0.1361  P    34.920     .500    -4.573     .300  -.014000   .454000   .3143000      .000      .000  
+76 515 42913.00 I   .007154  .025115   .440300  .017685  I  .3096121  .0002710  3.1103 0.1361  P    34.829     .500    -4.601     .300  -.012000   .454000   .3108000      .000      .000  
+76 516 42914.00 I   .009354  .025886   .439276  .017374  I  .3065373  .0002710  3.0514 0.5838  P    34.921     .500    -4.531     .300  -.009000   .454000   .3074000      .000      .000  
+76 517 42915.00 I   .011554  .015228   .438172  .024085  I  .3034880  .0011357  3.0564 0.5838  P    35.272     .500    -4.538     .300  -.006000   .454000   .3041000      .000      .000  
+76 518 42916.00 I   .013761  .013212   .436993  .021861  I  .3004110  .0011357  3.1018 0.8031  P    35.653     .500    -4.680     .300  -.003000   .454000   .3007000      .000      .000  
+76 519 42917.00 I   .015983  .012736   .435748  .023128  I  .2972803  .0011357  3.1595 0.7826  P    35.662     .500    -4.847     .300   .002000   .453000   .2940000      .000      .000  
+76 520 42918.00 I   .018227  .011988   .434445  .021843  I  .2940956  .0010770  3.2064 0.7826  P    35.218     .500    -4.940     .300   .002000   .453000   .2940000      .000      .000  
+76 521 42919.00 I   .020497  .013350   .433093  .019769  I  .2908756  .0010770  3.2288 0.7616  P    34.722     .500    -4.973     .300   .005000   .453000   .2907000      .000      .000  
+76 522 42920.00 I   .022791  .010961   .431707  .020316  I  .2876480  .0010770  3.2211 0.7112  P    34.569     .500    -4.973     .300   .008000   .452000   .2876000      .000      .000  
+76 523 42921.00 I   .025112  .017028   .430297  .025592  I  .2844442  .0009290  3.1811 0.7112  P    34.662     .500    -4.898     .300   .011000   .452000   .2845000      .000      .000  
+76 524 42922.00 I   .027462  .018765   .428878  .021431  I  .2812960  .0009290  3.1104 0.6569  P    34.604     .500    -4.727     .300   .014000   .451000   .2813000      .000      .000  
+76 525 42923.00 I   .029845  .021464   .427465  .023084  I  .2782318  .0009290  3.0144 0.4839  P    34.243     .500    -4.571     .300   .017000   .451000   .2782000      .000      .000  
+76 526 42924.00 I   .032266  .021326   .426071  .026544  I  .2752722  .0002710  2.9034 0.5321  P    33.798     .500    -4.578     .300   .020000   .450000   .2751000      .000      .000  
+76 527 42925.00 I   .034735  .019010   .424714  .031487  I  .2724254  .0005192  2.7911 0.2928  P    33.522     .500    -4.764     .300   .023000   .449000   .2721000      .000      .000  
+76 528 42926.00 I   .037259  .017697   .423411  .032194  I  .2696860  .0005192  2.6909 0.3671  P    33.468     .500    -4.989     .300   .026000   .448000   .2692000      .000      .000  
+76 529 42927.00 I   .039850  .017832   .422181  .032774  I  .2670353  .0005192  2.6155 0.3671  P    33.572     .500    -5.113     .300   .029000   .447000   .2662000      .000      .000  
+76 530 42928.00 I   .042519  .018091   .421041  .032189  I  .2644432  .0005192  2.5754 0.3671  P    33.756     .500    -5.119     .300   .032000   .446000   .2633000      .000      .000  
+76 531 42929.00 I   .045268  .017790   .419990  .032522  I  .2618698  .0005192  2.5793 0.3671  P    33.886     .500    -5.076     .300   .035000   .445000   .2603000      .000      .000  
+76 6 1 42930.00 I   .048095  .016824   .419011  .032820  I  .2592691  .0005192  2.6297 0.2928  P    33.816     .500    -5.034     .300   .038000   .444000   .2575000      .000      .000  
+76 6 2 42931.00 I   .050998  .029671   .418092  .038316  I  .2565967  .0002710  2.7211 0.2928  P    33.554     .500    -4.990     .300   .041000   .443000   .2547000      .000      .000  
+76 6 3 42932.00 I   .053973  .014562   .417222  .032233  I  .2538185  .0002710  2.8377 0.1916  P    33.306     .500    -4.919     .300   .044000   .441000   .2518000      .000      .000  
+76 6 4 42933.00 I   .057023  .016750   .416390  .030196  I  .2509208  .0002710  2.9557 0.2772  P    33.274     .500    -4.811     .300   .047000   .440000   .2490000      .000      .000  
+76 6 5 42934.00 I   .060145  .010769   .415586  .026172  I  .2479160  .0004837  3.0468 0.2772  P    33.439     .500    -4.650     .300   .050000   .439000   .2462000      .000      .000  
+76 6 6 42935.00 I   .063339  .010144   .414802  .024184  I  .2448453  .0004837  3.0834 0.3156  P    33.580     .500    -4.407     .300   .053000   .438000   .2436000      .000      .000  
+76 6 7 42936.00 I   .066601  .010178   .414032  .026245  I  .2417737  .0004054  3.0464 0.3173  P    33.474     .500    -4.092     .300   .056000   .436000   .2411000      .000      .000  
+76 6 8 42937.00 I   .069929  .009029   .413270  .024375  I  .2387777  .0004107  2.9338 0.2885  P    33.081     .500    -3.817     .300   .060000   .435000   .2385000      .000      .000  
+76 6 9 42938.00 I   .073319  .008816   .412510  .021824  I  .2359248  .0004107  2.7650 0.2904  P    32.557     .500    -3.744     .300   .063000   .433000   .2360000      .000      .000  
+76 610 42939.00 I   .076764  .009057   .411747  .019391  I  .2332542  .0004107  2.5767 0.2562  P    32.112     .500    -3.916     .300   .066000   .432000   .2334000      .000      .000  
+76 611 42940.00 I   .080261  .026762   .410977  .017018  I  .2307632  .0003065  2.4129 0.2562  P    31.856     .500    -4.169     .300   .069000   .430000   .2310000      .000      .000  
+76 612 42941.00 I   .083804  .023827   .410197  .019613  I  .2284091  .0003065  2.3070 0.2631  P    31.804     .500    -4.279     .300   .072000   .428000   .2286000      .000      .000  
+76 613 42942.00 I   .087388  .020718   .409404  .020443  I  .2261262  .0004277  2.2700 0.2046  P    31.968     .500    -4.188     .300   .076000   .427000   .2262000      .000      .000  
+76 614 42943.00 I   .091007  .023104   .408596  .014955  I  .2238509  .0002710  2.2876 0.2532  P    32.315     .500    -4.048     .300   .079000   .425000   .2238000      .000      .000  
+76 615 42944.00 I   .094657  .020057   .407772  .016181  I  .2215422  .0002710  2.3315 0.5257  P    32.631     .500    -4.035     .300   .082000   .423000   .2214000      .000      .000  
+76 616 42945.00 I   .098336  .010221   .406929  .008280  I  .2191885  .0010159  2.3734 0.5257  P    32.594     .500    -4.168     .300   .085000   .421000   .2192000      .000      .000  
+76 617 42946.00 I   .102046  .010927   .406065  .008886  I  .2168023  .0010159  2.3945 0.7183  P    32.118     .500    -4.349     .300   .089000   .419000   .2170000      .000      .000  
+76 618 42947.00 I   .105785  .006070   .405180  .009023  I  .2144087  .0010159  2.3878 0.7183  P    31.502     .500    -4.488     .300   .092000   .417000   .2147000      .000      .000  
+76 619 42948.00 I   .109553  .006764   .404273  .010024  I  .2120361  .0010159  2.3528 0.7183  P    31.108     .500    -4.546     .300   .096000   .415000   .2125000      .000      .000  
+76 620 42949.00 I   .113344  .006896   .403338  .008907  I  .2097117  .0010159  2.2918 0.7183  P    30.959     .500    -4.508     .300   .099000   .413000   .2103000      .000      .000  
+76 621 42950.00 I   .117150  .006919   .402367  .008774  I  .2074596  .0010159  2.2093 0.5257  P    30.791     .500    -4.394     .300   .102000   .411000   .2082000      .000      .000  
+76 622 42951.00 I   .120965  .014599   .401350  .016242  I  .2052974  .0002710  2.1139 0.5257  P    30.440     .500    -4.282     .300   .106000   .409000   .2060000      .000      .000  
+76 623 42952.00 I   .124783  .014148   .400278  .018873  I  .2032318  .0002710  2.0184 0.1916  P    30.009     .500    -4.253     .300   .109000   .407000   .2039000      .000      .000  
+76 624 42953.00 I   .128599  .011801   .399142  .019498  I  .2012559  .0002710  1.9371 0.2842  P    29.662     .500    -4.305     .300   .113000   .405000   .2017000      .000      .000  
+76 625 42954.00 I   .132409  .009464   .397934  .013822  I  .1993482  .0004996  1.8840 0.2842  P    29.449     .500    -4.373     .300   .116000   .403000   .1996000      .000      .000  
+76 626 42955.00 I   .136210  .009118   .396646  .013289  I  .1974747  .0004996  1.8704 0.3533  P    29.376     .500    -4.417     .300   .119000   .401000   .1974000      .000      .000  
+76 627 42956.00 I   .139998  .010044   .395270  .013855  I  .1955915  .0004996  1.9044 0.3533  P    29.465     .500    -4.443     .300   .123000   .399000   .1952000      .000      .000  
+76 628 42957.00 I   .143772  .010177   .393797  .014147  I  .1936489  .0004996  1.9894 0.3533  P    29.664     .500    -4.447     .300   .126000   .396000   .1931000      .000      .000  
+76 629 42958.00 I   .147529  .009946   .392219  .014527  I  .1915975  .0004996  2.1200 0.3533  P    29.768     .500    -4.392     .300   .130000   .394000   .1909000      .000      .000  
+76 630 42959.00 I   .151267  .009767   .390543  .013771  I  .1893993  .0004996  2.2795 0.2842  P    29.579     .500    -4.262     .300   .133000   .392000   .1887000      .000      .000  
+76 7 1 42960.00 I   .154984  .019830   .388776  .017706  I  .1870376  .0002710  2.4419 0.2842  P    29.122     .500    -4.100     .300   .136000   .390000   .1865000      .000      .000  
+76 7 2 42961.00 I   .158679  .012035   .386923  .018126  I  .1845248  .0002710  2.5765 0.1916  P    28.647     .500    -3.961     .300   .140000   .387000   .1843000      .000      .000  
+76 7 3 42962.00 I   .162352  .025314   .384992  .017509  I  .1819028  .0002710  2.6567 0.1916  P    28.398     .500    -3.850     .300   .143000   .385000   .1821000      .000      .000  
+76 7 4 42963.00 I   .166004  .026766   .382986  .015606  I  .1792345  .0002710  2.6678 0.1916  P    28.401     .500    -3.727     .300   .147000   .382000   .1799000      .000      .000  
+76 7 5 42964.00 I   .169636  .023622   .380910  .013515  I  .1765898  .0002710  2.6111 0.1916  P    28.460     .500    -3.574     .300   .150000   .380000   .1777000      .000      .000  
+76 7 6 42965.00 I   .173247  .027171   .378770  .010076  I  .1740294  .0002710  2.5031 0.4874  P    28.341     .500    -3.441     .300   .153000   .378000   .1753000      .000      .000  
+76 7 7 42966.00 I   .176837  .023531   .376569  .010530  I  .1715909  .0009363  2.3731 0.4874  P    27.962     .500    -3.432     .300   .156000   .376000   .1730000      .000      .000  
+76 7 8 42967.00 I   .180401  .027149   .374315  .011249  I  .1692790  .0009363  2.2558 0.6621  P    27.439     .500    -3.606     .300   .159000   .373000   .1706000      .000      .000  
+76 7 9 42968.00 I   .183934  .023688   .372014  .012146  I  .1670645  .0009363  2.1828 0.6621  P    26.996     .500    -3.885     .300   .162000   .371000   .1683000      .000      .000  
+76 710 42969.00 I   .187428  .006215   .369671  .010793  I  .1648930  .0009363  2.1709 0.6621  P    26.810     .500    -4.080     .300   .165000   .369000   .1659000      .000      .000  
+76 711 42970.00 I   .190877  .013048   .367294  .021545  I  .1627037  .0009363  2.2161 0.6621  P    26.920     .500    -4.053     .300   .168000   .367000   .1635000      .000      .000  
+76 712 42971.00 I   .194274  .014123   .364887  .024878  I  .1604486  .0009363  2.2978 0.4874  P    27.202     .500    -3.857     .300   .171000   .365000   .1611000      .000      .000  
+76 713 42972.00 I   .197611  .012369   .362456  .021578  I  .1581059  .0002710  2.3862 0.4874  P    27.424     .500    -3.682     .300   .173000   .362000   .1586000      .000      .000  
+76 714 42973.00 I   .200882  .014282   .360005  .023968  I  .1556828  .0002710  2.4549 0.1916  P    27.379     .500    -3.672     .300   .176000   .360000   .1562000      .000      .000  
+76 715 42974.00 I   .204078  .012459   .357538  .021185  I  .1532076  .0002710  2.4891 0.1916  P    27.028     .500    -3.813     .300   .179000   .358000   .1538000      .000      .000  
+76 716 42975.00 I   .207189  .013996   .355057  .022984  I  .1507172  .0002710  2.4854 0.1916  P    26.525     .500    -3.987     .300   .182000   .356000   .1514000      .000      .000  
+76 717 42976.00 I   .210205  .013428   .352562  .022192  I  .1482482  .0002710  2.4474 0.1916  P    26.082     .500    -4.096     .300   .184000   .354000   .1489000      .000      .000  
+76 718 42977.00 I   .213113  .007213   .350055  .012419  I  .1458315  .0002710  2.3820 0.2024  P    25.800     .500    -4.114     .300   .187000   .351000   .1465000      .000      .000  
+76 719 42978.00 I   .215903  .011449   .347537  .011023  I  .1434907  .0003008  2.2972 0.2024  P    25.637     .500    -4.084     .300   .189000   .349000   .1440000      .000      .000  
+76 720 42979.00 I   .218565  .011737   .345008  .012654  I  .1412396  .0003008  2.2047 0.2127  P    25.480     .500    -4.067     .300   .192000   .347000   .1416000      .000      .000  
+76 721 42980.00 I   .221086  .013109   .342468  .011130  I  .1390792  .0003008  2.1183 0.2127  P    25.243     .500    -4.085     .300   .194000   .345000   .1392000      .000      .000  
+76 722 42981.00 I   .223456  .013457   .339916  .011884  I  .1369961  .0003008  2.0527 0.2127  P    24.912     .500    -4.097     .300   .197000   .343000   .1368000      .000      .000  
+76 723 42982.00 I   .225666  .014000   .337350  .010613  I  .1349622  .0003008  2.0218 0.2127  P    24.574     .500    -4.063     .300   .199000   .340000   .1343000      .000      .000  
+76 724 42983.00 I   .227725  .014125   .334770  .004666  I  .1329375  .0003008  2.0358 0.2024  P    24.377     .500    -4.020     .300   .202000   .338000   .1319000      .000      .000  
+76 725 42984.00 I   .229645  .018683   .332174  .004836  I  .1308736  .0002710  2.1007 0.2024  P    24.427     .500    -4.043     .300   .204000   .336000   .1295000      .000      .000  
+76 726 42985.00 I   .231435  .021550   .329559  .004837  I  .1287194  .0002710  2.2157 0.1916  P    24.679     .500    -4.122     .300   .206000   .334000   .1271000      .000      .000  
+76 727 42986.00 I   .233101  .018726   .326925  .006796  I  .1264293  .0002710  2.3694 0.1916  P    24.928     .500    -4.128     .300   .209000   .332000   .1247000      .000      .000  
+76 728 42987.00 I   .234651  .014478   .324267  .007520  I  .1239761  .0002710  2.5366 0.1916  P    24.933     .500    -3.959     .300   .211000   .329000   .1223000      .000      .000  
+76 729 42988.00 I   .236088  .014191   .321585  .008238  I  .1213634  .0002710  2.6819 0.1916  P    24.587     .500    -3.672     .300   .214000   .327000   .1199000      .000      .000  
+76 730 42989.00 I   .237420  .010745   .318877  .009031  I  .1186313  .0002710  2.7702 0.1916  P    24.008     .500    -3.435     .300   .216000   .325000   .1175000      .000      .000  
+76 731 42990.00 I   .238655  .009757   .316140  .017333  I  .1158493  .0002710  2.7796 0.1916  P    23.474     .500    -3.362     .300   .218000   .323000   .1151000      .000      .000  
+76 8 1 42991.00 I   .239798  .008575   .313373  .019777  I  .1130985  .0002710  2.7097 0.1916  P    23.222     .500    -3.427     .300   .220000   .320000   .1128000      .000      .000  
+76 8 2 42992.00 I   .240858  .015887   .310575  .021115  I  .1104494  .0002710  2.5814 0.5479  P    23.278     .500    -3.528     .300   .223000   .318000   .1104000      .000      .000  
+76 8 3 42993.00 I   .241840  .018258   .307745  .023586  I  .1079439  .0010618  2.4286 0.4141  P    23.444     .500    -3.601     .300   .225000   .315000   .1081000      .000      .000  
+76 8 4 42994.00 I   .242751  .017501   .304881  .020428  I  .1055884  .0007826  2.2872 0.6595  P    23.466     .500    -3.664     .300   .227000   .313000   .1057000      .000      .000  
+76 8 5 42995.00 I   .243595  .018695   .301983  .022857  I  .1033558  .0007826  2.1868 0.5638  P    23.237     .500    -3.774     .300   .229000   .310000   .1034000      .000      .000  
+76 8 6 42996.00 I   .244380  .016191   .299052  .020758  I  .1011959  .0008117  2.1431 0.5368  P    22.881     .500    -3.946     .300   .231000   .308000   .1011000      .000      .000  
+76 8 7 42997.00 I   .245108  .018386   .296091  .015984  I  .0990505  .0007349  2.1567 0.5328  P    22.645     .500    -4.097     .300   .233000   .305000   .0989000      .000      .000  
+76 8 8 42998.00 I   .245781  .018222   .293101  .014653  I  .0968681  .0006904  2.2136 0.4648  P    22.668     .500    -4.107     .300   .235000   .303000   .0966000      .000      .000  
+76 8 9 42999.00 I   .246402  .013405   .290085  .009107  I  .0946168  .0005694  2.2900 0.4437  P    22.859     .500    -3.942     .300   .237000   .300000   .0943000      .000      .000  
+76 810 43000.00 I   .246970  .013248   .287047  .008296  I  .0922901  .0005576  2.3605 0.3884  P    23.000     .500    -3.703     .300   .239000   .297000   .0921000      .000      .000  
+76 811 43001.00 I   .247487  .012610   .283988  .009573  I  .0899044  .0005284  2.4052 0.3624  P    22.955     .500    -3.540     .300   .241000   .294000   .0898000      .000      .000  
+76 812 43002.00 I   .247953  .010924   .280913  .012004  I  .0874919  .0004629  2.4132 0.3405  P    22.739     .500    -3.523     .300   .242000   .292000   .0876000      .000      .000  
+76 813 43003.00 I   .248371  .012612   .277825  .011836  I  .0850903  .0004295  2.3843 0.3379  P    22.425     .500    -3.612     .300   .244000   .289000   .0853000      .000      .000  
+76 814 43004.00 I   .248745  .013997   .274725  .010724  I  .0827333  .0004923  2.3255 0.3486  P    22.079     .500    -3.732     .300   .246000   .286000   .0831000      .000      .000  
+76 815 43005.00 I   .249075  .012514   .271617  .011071  I  .0804458  .0005491  2.2472 0.3934  P    21.816     .500    -3.839     .300   .248000   .283000   .0809000      .000      .000  
+76 816 43006.00 I   .249363  .013555   .268503  .010565  I  .0782417  .0006137  2.1605 0.4251  P    21.756     .500    -3.934     .300   .249000   .280000   .0786000      .000      .000  
+76 817 43007.00 I   .249611  .013808   .265386  .011833  I  .0761236  .0006492  2.0775 0.4414  P    21.858     .500    -4.040     .300   .251000   .278000   .0764000      .000      .000  
+76 818 43008.00 I   .249819  .012405   .262269  .010582  I  .0740805  .0006345  2.0131 0.4272  P    21.880     .500    -4.150     .300   .252000   .275000   .0741000      .000      .000  
+76 819 43009.00 I   .249988  .014321   .259157  .006985  I  .0720864  .0005554  1.9818 0.4157  P    21.641     .500    -4.205     .300   .254000   .272000   .0719000      .000      .000  
+76 820 43010.00 I   .250115  .013783   .256052  .007315  I  .0701021  .0005373  1.9951 0.3826  P    21.274     .500    -4.142     .300   .255000   .269000   .0696000      .000      .000  
+76 821 43011.00 I   .250199  .012293   .252957  .007622  I  .0680790  .0005263  2.0602 0.3761  P    21.091     .500    -4.001     .300   .256000   .266000   .0673000      .000      .000  
+76 822 43012.00 I   .250237  .011947   .249874  .007182  I  .0659637  .0005263  2.1795 0.3886  P    21.229     .500    -3.918     .300   .258000   .263000   .0649000      .000      .000  
+76 823 43013.00 I   .250226  .010096   .246806  .006520  I  .0637037  .0005719  2.3479 0.2960  P    21.531     .500    -3.972     .300   .259000   .260000   .0626000      .000      .000  
+76 824 43014.00 I   .250164  .009780   .243753  .007338  I  .0612575  .0002710  2.5476 0.3164  P    21.749     .500    -4.050     .300   .260000   .257000   .0603000      .000      .000  
+76 825 43015.00 I   .250047  .010630   .240718  .007907  I  .0586089  .0002710  2.7460 0.1916  P    21.756     .500    -3.960     .300   .261000   .254000   .0579000      .000      .000  
+76 826 43016.00 I   .249873  .014868   .237703  .007438  I  .0557798  .0002710  2.9015 0.1916  P    21.547     .500    -3.674     .300   .262000   .251000   .0555000      .000      .000  
+76 827 43017.00 I   .249635  .015702   .234708  .007157  I  .0528321  .0002710  2.9783 0.1916  P    21.178     .500    -3.375     .300   .263000   .248000   .0530000      .000      .000  
+76 828 43018.00 I   .249330  .014287   .231736  .009845  I  .0498545  .0002710  2.9610 0.1916  P    20.757     .500    -3.256     .300   .264000   .245000   .0506000      .000      .000  
+76 829 43019.00 I   .248952  .015263   .228790  .010887  I  .0469369  .0002710  2.8632 0.1916  P    20.435     .500    -3.331     .300   .265000   .242000   .0482000      .000      .000  
+76 830 43020.00 I   .248499  .013659   .225872  .013501  I  .0441424  .0002710  2.7223 0.1916  P    20.321     .500    -3.468     .300   .266000   .239000   .0456000      .000      .000  
+76 831 43021.00 I   .247962  .014938   .222984  .014621  I  .0414915  .0002710  2.5836 0.1916  P    20.414     .500    -3.565     .300   .266000   .236000   .0431000      .000      .000  
+76 9 1 43022.00 I   .247335  .014419   .220128  .012947  I  .0389619  .0002710  2.4848 0.1916  P    20.621     .500    -3.626     .300   .267000   .233000   .0405000      .000      .000  
+76 9 2 43023.00 I   .246611  .009771   .217308  .014569  I  .0365018  .0002710  2.4465 0.1916  P    20.817     .500    -3.692     .300   .267000   .230000   .0380000      .000      .000  
+76 9 3 43024.00 I   .245781  .009661   .214527  .012626  I  .0340482  .0002710  2.4705 0.1916  P    20.930     .500    -3.767     .300   .268000   .227000   .0354000      .000      .000  
+76 9 4 43025.00 I   .244837  .009943   .211786  .011600  I  .0315440  .0002710  2.5449 0.1865  P    20.991     .500    -3.811     .300   .268000   .224000   .0326000      .000      .000  
+76 9 5 43026.00 I   .243773  .012131   .209088  .010047  I  .0289484  .0002564  2.6494 0.1802  P    21.076     .500    -3.777     .300   .268000   .222000   .0299000      .000      .000  
+76 9 6 43027.00 I   .242580  .012361   .206434  .003174  I  .0262430  .0002377  2.7604 0.1748  P    21.182     .500    -3.653     .300   .267000   .219000   .0271000      .000      .000  
+76 9 7 43028.00 I   .241249  .013597   .203825  .003342  I  .0234329  .0002377  2.8554 0.1681  P    21.213     .500    -3.465     .300   .267000   .217000   .0244000      .000      .000  
+76 9 8 43029.00 I   .239773  .013644   .201263  .002271  I  .0205429  .0002377  2.9183 0.1681  P    21.109     .500    -3.267     .300   .267000   .214000   .0216000      .000      .000  
+76 9 9 43030.00 I   .238153  .013768   .198749  .002677  I  .0176095  .0002377  2.9418 0.1829  P    20.922     .500    -3.125     .300   .266000   .212000   .0186000      .000      .000  
+76 910 43031.00 I   .236405  .013977   .196285  .003042  I  .0146718  .0002781  2.9279 0.2098  P    20.716     .500    -3.095     .300   .266000   .209000   .0157000      .000      .000  
+76 911 43032.00 I   .234546  .015939   .193873  .014380  I  .0117630  .0003457  2.8860 0.2444  P    20.512     .500    -3.195     .300   .265000   .207000   .0127000      .000      .000  
+76 912 43033.00 I   .232589  .018403   .191512  .016603  I  .0089050  .0004019  2.8285 0.2651  P    20.385     .500    -3.388     .300   .265000   .204000   .0098000      .000      .000  
+76 913 43034.00 I   .230549  .017623   .189206  .016110  I  .0061070  .0004019  2.7681 0.2842  P    20.482     .500    -3.603     .300   .264000   .202000   .0068000      .000      .000  
+76 914 43035.00 I   .228436  .012796   .186953  .018473  I  .0033653  .0004019  2.7183 0.2842  P    20.795     .500    -3.791     .300   .263000   .200000   .0037000      .000      .000  
+76 915 43036.00 I   .226262  .011136   .184756  .018093  I  .0006625  .0004019  2.6923 0.2911  P    21.035     .500    -3.944     .300   .262000   .197000   .0006000      .000      .000  
+76 916 43037.00 I   .224040  .009070   .182613  .020786  I -.0020313  .0004212  2.7022 0.2424  P    20.940     .500    -4.032     .300   .262000   .195000  -.0024000      .000      .000  
+76 917 43038.00 I   .221780  .009682   .180523  .018237  I -.0047568  .0002710  2.7569 0.2504  P    20.654     .500    -3.981     .300   .261000   .192000  -.0055000      .000      .000  
+76 918 43039.00 I   .219493  .010943   .178483  .013304  I -.0075618  .0002710  2.8618 0.1916  P    20.582     .500    -3.778     .300   .260000   .190000  -.0086000      .000      .000  
+76 919 43040.00 I   .217189  .011320   .176491  .017030  I -.0104971  .0002710  3.0170 0.1916  P    20.877     .500    -3.559     .300   .259000   .187000  -.0118000      .000      .000  
+76 920 43041.00 I   .214879  .009769   .174542  .017785  I -.0136096  .0002710  3.2136 0.1916  P    21.259     .500    -3.495     .300   .258000   .185000  -.0150000      .000      .000  
+76 921 43042.00 I   .212572  .010696   .172634  .017341  I -.0169307  .0002710  3.4292 0.1916  P    21.396     .500    -3.579     .300   .257000   .182000  -.0183000      .000      .000  
+76 922 43043.00 I   .210280  .012285   .170763  .017485  I -.0204622  .0002710  3.6272 0.1916  P    21.270     .500    -3.611     .300   .256000   .180000  -.0215000      .000      .000  
+76 923 43044.00 I   .208015  .029307   .168926  .028284  I -.0241649  .0002710  3.7647 0.1916  P    21.096     .500    -3.456     .300   .255000   .177000  -.0247000      .000      .000  
+76 924 43045.00 I   .205788  .033204   .167116  .032485  I -.0279605  .0002710  3.8090 0.1916  P    21.034     .500    -3.213     .300   .254000   .175000  -.0280000      .000      .000  
+76 925 43046.00 I   .203607  .035532   .165330  .028962  I -.0317496  .0002710  3.7533 0.2476  P    21.062     .500    -3.065     .300   .252000   .172000  -.0313000      .000      .000  
+76 926 43047.00 I   .201482  .018900   .163560  .012018  I -.0354420  .0004145  3.6223 0.2476  P    21.047     .500    -3.041     .300   .251000   .170000  -.0345000      .000      .000  
+76 927 43048.00 I   .199423  .018532   .161799  .012591  I -.0389851  .0004145  3.4638 0.2931  P    20.891     .500    -3.023     .300   .249000   .167000  -.0378000      .000      .000  
+76 928 43049.00 I   .197443  .018794   .160038  .012397  I -.0423772  .0004145  3.3279 0.2931  P    20.665     .500    -2.964     .300   .248000   .165000  -.0411000      .000      .000  
+76 929 43050.00 I   .195556  .019277   .158267  .012136  I -.0456595  .0004145  3.2478 0.2541  P    20.606     .500    -2.958     .300   .246000   .163000  -.0444000      .000      .000  
+76 930 43051.00 I   .193761  .012188   .156476  .007973  I -.0488942  .0002940  3.2319 0.2541  P    20.896     .500    -3.072     .300   .245000   .161000  -.0477000      .000      .000  
+7610 1 43052.00 I   .192049  .012048   .154657  .009238  I -.0521409  .0002940  3.2687 0.1479  P    21.436     .500    -3.219     .300   .243000   .158000  -.0509000      .000      .000  
+7610 2 43053.00 I   .190412  .009765   .152802  .012088  I -.0554420  .0000331  3.3368 0.1479  P    21.930     .500    -3.253     .300   .242000   .156000  -.0542000      .000      .000  
+7610 3 43054.00 I   .188844  .009539   .150901  .013958  I -.0588172  .0000331  3.4131 0.4891  P    22.190     .500    -3.150     .300   .240000   .154000  -.0575000      .000      .000  
+7610 4 43055.00 I   .187338  .009510   .148947  .013512  I -.0622645  .0009776  3.4782 0.4891  P    22.245     .500    -2.998     .300   .238000   .152000  -.0609000      .000      .000  
+7610 5 43056.00 I   .185878  .013209   .146941  .013651  I -.0657649  .0009776  3.5171 0.7151  P    22.189     .500    -2.862     .300   .236000   .150000  -.0642000      .000      .000  
+7610 6 43057.00 I   .184444  .014620   .144886  .011245  I -.0692867  .0010440  3.5200 0.6729  P    22.062     .500    -2.722     .300   .233000   .147000  -.0676000      .000      .000  
+7610 7 43058.00 I   .183018  .014982   .142781  .011933  I -.0727925  .0009248  3.4858 0.6845  P    21.900     .500    -2.563     .300   .231000   .145000  -.0709000      .000      .000  
+7610 8 43059.00 I   .181581  .015335   .140626  .010248  I -.0762483  .0008855  3.4215 0.5864  P    21.769     .500    -2.455     .300   .229000   .143000  -.0743000      .000      .000  
+7610 9 43060.00 I   .180116  .012133   .138420  .011026  I -.0796288  .0007212  3.3370 0.4895  P    21.712     .500    -2.490     .300   .227000   .141000  -.0777000      .000      .000  
+761010 43061.00 I   .178626  .012302   .136163  .009655  I -.0829199  .0004176  3.2451 0.4167  P    21.765     .500    -2.662     .300   .224000   .139000  -.0811000      .000      .000  
+761011 43062.00 I   .177119  .012169   .133857  .009566  I -.0861213  .0004176  3.1599 0.2917  P    21.997     .500    -2.862     .300   .222000   .137000  -.0846000      .000      .000  
+761012 43063.00 I   .175602  .008273   .131502  .012266  I -.0892462  .0004073  3.0942 0.2998  P    22.391     .500    -2.999     .300   .219000   .135000  -.0880000      .000      .000  
+761013 43064.00 I   .174075  .008049   .129107  .013162  I -.0923197  .0004302  3.0585 0.2558  P    22.727     .500    -3.081     .300   .217000   .133000  -.0914000      .000      .000  
+761014 43065.00 I   .172531  .006712   .126686  .012604  I -.0953760  .0003097  3.0613 0.2580  P    22.777     .500    -3.141     .300   .214000   .131000  -.0948000      .000      .000  
+761015 43066.00 I   .170964  .003370   .124257  .008581  I -.0984571  .0002851  3.1086 0.2105  P    22.640     .500    -3.124     .300   .212000   .129000  -.0982000      .000      .000  
+761016 43067.00 I   .169366  .003350   .121837  .008654  I -.1016087  .0002851  3.2025 0.2016  P    22.677     .500    -2.955     .300   .209000   .128000  -.1017000      .000      .000  
+761017 43068.00 I   .167730  .005942   .119440  .008450  I -.1048764  .0002851  3.3393 0.2016  P    23.050     .500    -2.691     .300   .207000   .126000  -.1051000      .000      .000  
+761018 43069.00 I   .166052  .005904   .117081  .008576  I -.1082973  .0002851  3.5062 0.2016  P    23.493     .500    -2.506     .300   .204000   .124000  -.1085000      .000      .000  
+761019 43070.00 I   .164323  .007678   .114771  .008591  I -.1118907  .0002851  3.6790 0.2016  P    23.650     .500    -2.486     .300   .201000   .122000  -.1119000      .000      .000  
+761020 43071.00 I   .162540  .007891   .112517  .006782  I -.1156453  .0002851  3.8219 0.1967  P    23.501     .500    -2.509     .300   .198000   .121000  -.1153000      .000      .000  
+761021 43072.00 I   .160699  .020995   .110324  .007254  I -.1195118  .0002710  3.8967 0.2557  P    23.339     .500    -2.429     .300   .196000   .119000  -.1188000      .000      .000  
+761022 43073.00 I   .158798  .009683   .108199  .012443  I -.1234077  .0004246  3.8784 0.2519  P    23.443     .500    -2.279     .300   .193000   .118000  -.1222000      .000      .000  
+761023 43074.00 I   .156836  .009738   .106147  .013048  I -.1272383  .0004246  3.7691 0.3002  P    23.824     .500    -2.196     .300   .190000   .116000  -.1256000      .000      .000  
+761024 43075.00 I   .154807  .008609   .104176  .013369  I -.1309265  .0004246  3.6016 0.3002  P    24.216     .500    -2.181     .300   .187000   .114000  -.1290000      .000      .000  
+761025 43076.00 I   .152708  .008552   .102291  .013094  I -.1344389  .0004246  3.4264 0.3002  P    24.282     .500    -2.095     .300   .184000   .113000  -.1324000      .000      .000  
+761026 43077.00 I   .150532  .007060   .100493  .013272  I -.1377922  .0004246  3.2899 0.3822  P    23.954     .500    -1.914     .300   .181000   .111000  -.1358000      .000      .000  
+761027 43078.00 I   .148275  .006551   .098774  .010333  I -.1410399  .0006357  3.2169 0.4495  P    23.600     .500    -1.829     .300   .178000   .110000  -.1392000      .000      .000  
+761028 43079.00 I   .145933  .006382   .097124  .007214  I -.1442462  .0007924  3.2046 0.4807  P    23.695     .500    -1.992     .300   .175000   .108000  -.1426000      .000      .000  
+761029 43080.00 I   .143504  .005654   .095532  .007883  I -.1474621  .0007213  3.2313 0.5358  P    24.277     .500    -2.275     .300   .172000   .106000  -.1459000      .000      .000  
+761030 43081.00 I   .140986  .005425   .093990  .007130  I -.1507129  .0007213  3.2704 0.5100  P    24.901     .500    -2.404     .300   .169000   .104000  -.1492000      .000      .000  
+761031 43082.00 I   .138378  .005348   .092491  .007074  I -.1540004  .0007213  3.3022 0.5100  P    25.164     .500    -2.280     .300   .165000   .103000  -.1526000      .000      .000  
+7611 1 43083.00 I   .135685  .005270   .091033  .007118  I -.1573108  .0007213  3.3147 0.4368  P    25.097     .500    -2.055     .300   .162000   .101000  -.1559000      .000      .000  
+7611 2 43084.00 I   .132909  .004707   .089609  .007308  I -.1606205  .0004930  3.2995 0.4354  P    24.967     .500    -1.893     .300   .159000   .099000  -.1592000      .000      .000  
+7611 3 43085.00 I   .130046  .003837   .088210  .007363  I -.1638992  .0004879  3.2527 0.2975  P    24.889     .500    -1.793     .300   .156000   .097000  -.1624000      .000      .000  
+7611 4 43086.00 I   .127091  .002721   .086820  .008642  I -.1671159  .0003330  3.1761 0.3158  P    24.821     .500    -1.676     .300   .152000   .096000  -.1656000      .000      .000  
+7611 5 43087.00 I   .124040  .008496   .085427  .009488  I -.1702435  .0004012  3.0760 0.2607  P    24.763     .500    -1.551     .300   .149000   .094000  -.1687000      .000      .000  
+7611 6 43088.00 I   .120887  .008986   .084023  .010297  I -.1732639  .0004012  2.9639 0.2837  P    24.785     .500    -1.504     .300   .145000   .093000  -.1719000      .000      .000  
+7611 7 43089.00 I   .117629  .009121   .082602  .010409  I -.1761719  .0004012  2.8536 0.2728  P    24.913     .500    -1.554     .300   .142000   .091000  -.1751000      .000      .000  
+7611 8 43090.00 I   .114259  .010208   .081160  .010291  I -.1789766  .0003698  2.7597 0.2663  P    25.125     .500    -1.616     .300   .139000   .090000  -.1782000      .000      .000  
+7611 9 43091.00 I   .110789  .010174   .079701  .010405  I -.1817006  .0003503  2.6938 0.2579  P    25.391     .500    -1.620     .300   .135000   .088000  -.1813000      .000      .000  
+761110 43092.00 I   .107237  .010159   .078236  .010538  I -.1843763  .0003596  2.6642 0.2061  P    25.640     .500    -1.606     .300   .132000   .087000  -.1843000      .000      .000  
+761111 43093.00 I   .103622  .008311   .076775  .008726  I -.1870431  .0002171  2.6768 0.2100  P    25.763     .500    -1.642     .300   .128000   .085000  -.1874000      .000      .000  
+761112 43094.00 I   .099976  .008401   .075327  .009751  I -.1897447  .0002171  2.7336 0.1535  P    25.770     .500    -1.696     .300   .125000   .084000  -.1905000      .000      .000  
+761113 43095.00 I   .096332  .007855   .073900  .008875  I -.1925243  .0002171  2.8322 0.4672  P    25.843     .500    -1.655     .300   .122000   .083000  -.1935000      .000      .000  
+761114 43096.00 I   .092723  .014996   .072502  .010644  I -.1954200  .0009088  2.9635 0.4672  P    26.113     .500    -1.472     .300   .118000   .081000  -.1965000      .000      .000  
+761115 43097.00 I   .089176  .015215   .071137  .010295  I -.1984565  .0009088  3.1102 0.6426  P    26.458     .500    -1.241     .300   .115000   .080000  -.1995000      .000      .000  
+761116 43098.00 I   .085713  .015082   .069805  .010457  I -.2016370  .0009088  3.2465 0.6716  P    26.637     .500    -1.063     .300   .111000   .078000  -.2025000      .000      .000  
+761117 43099.00 I   .082352  .014765   .068506  .008835  I -.2049362  .0009891  3.3429 0.6716  P    26.571     .500     -.922     .300   .108000   .077000  -.2055000      .000      .000  
+761118 43100.00 I   .079093  .015244   .067245  .010547  I -.2083012  .0009891  3.3745 0.6994  P    26.419     .500     -.764     .300   .105000   .076000  -.2085000      .000      .000  
+761119 43101.00 I   .075922  .015359   .066036  .009630  I -.2116598  .0009891  3.3297 0.5968  P    26.418     .500     -.646     .300   .101000   .075000  -.2115000      .000      .000  
+761120 43102.00 I   .072820  .007022   .064892  .011208  I -.2149386  .0006682  3.2187 0.5968  P    26.677     .500     -.685     .300   .098000   .073000  -.2145000      .000      .000  
+761121 43103.00 I   .069783  .006899   .063821  .011310  I -.2180867  .0006682  3.0762 0.4313  P    27.070     .500     -.844     .300   .094000   .072000  -.2175000      .000      .000  
+761122 43104.00 I   .066805  .005955   .062834  .009785  I -.2210963  .0005456  2.9497 0.4169  P    27.292     .500     -.914     .300   .091000   .071000  -.2205000      .000      .000  
+761123 43105.00 I   .063885  .005695   .061943  .011439  I -.2240040  .0004988  2.8768 0.3696  P    27.163     .500     -.789     .300   .088000   .070000  -.2235000      .000      .000  
+761124 43106.00 I   .061019  .005783   .061152  .012820  I -.2268716  .0004988  2.8689 0.3527  P    26.907     .500     -.636     .300   .084000   .070000  -.2265000      .000      .000  
+761125 43107.00 I   .058197  .003590   .060458  .011516  I -.2297588  .0004988  2.9118 0.2713  P    26.960     .500     -.697     .300   .081000   .069000  -.2294000      .000      .000  
+761126 43108.00 I   .055414  .006921   .059859  .008629  I -.2327027  .0002136  2.9770 0.2713  P    27.420     .500     -.954     .300   .077000   .069000  -.2324000      .000      .000  
+761127 43109.00 I   .052661  .006979   .059348  .007629  I -.2357106  .0002136  3.0356 0.1849  P    27.886     .500    -1.144     .300   .074000   .068000  -.2354000      .000      .000  
+761128 43110.00 I   .049933  .010931   .058916  .010545  I -.2387657  .0003018  3.0698 0.1511  P    27.954     .500    -1.089     .300   .071000   .068000  -.2384000      .000      .000  
+761129 43111.00 I   .047221  .008194   .058557  .008893  I -.2418399  .0002137  3.0733 0.1849  P    27.696     .500     -.881     .300   .068000   .067000  -.2414000      .000      .000  
+761130 43112.00 I   .044512  .008211   .058263  .010860  I -.2449021  .0002137  3.0462 0.1560  P    27.472     .500     -.710     .300   .064000   .067000  -.2444000      .000      .000  
+7612 1 43113.00 I   .041779  .009528   .058026  .007925  I -.2479232  .0002273  2.9918 0.1393  P    27.432     .500     -.632     .300   .061000   .066000  -.2474000      .000      .000  
+7612 2 43114.00 I   .038999  .009757   .057837  .009928  I -.2508785  .0001788  2.9156 0.1446  P    27.453     .500     -.578     .300   .058000   .066000  -.2504000      .000      .000  
+7612 3 43115.00 I   .036147  .009663   .057695  .009980  I -.2537501  .0001788  2.8264 0.1264  P    27.450     .500     -.507     .300   .054000   .066000  -.2534000      .000      .000  
+7612 4 43116.00 I   .033197  .009538   .057603  .010269  I -.2565310  .0001788  2.7365 0.1775  P    27.491     .500     -.453     .300   .051000   .066000  -.2564000      .000      .000  
+7612 5 43117.00 I   .030129  .012103   .057561  .009428  I -.2592275  .0003067  2.6600 0.1775  P    27.602     .500     -.428     .300   .047000   .066000  -.2595000      .000      .000  
+7612 6 43118.00 I   .026940  .012277   .057570  .010037  I -.2618595  .0003067  2.6090 0.2337  P    27.705     .500     -.394     .300   .044000   .066000  -.2625000      .000      .000  
+7612 7 43119.00 I   .023629  .012729   .057629  .009892  I -.2644568  .0003528  2.5918 0.2178  P    27.768     .500     -.342     .300   .040000   .066000  -.2655000      .000      .000  
+7612 8 43120.00 I   .020197  .010318   .057738  .007337  I -.2670559  .0003092  2.6129 0.2346  P    27.862     .500     -.330     .300   .036000   .066000  -.2685000      .000      .000  
+7612 9 43121.00 I   .016660  .010332   .057894  .006445  I -.2696957  .0003092  2.6731 0.2186  P    28.020     .500     -.410     .300   .033000   .066000  -.2715000      .000      .000  
+761210 43122.00 I   .013042  .010437   .058091  .006756  I -.2724142  .0003092  2.7694 0.1936  P    28.177     .500     -.537     .300   .029000   .066000  -.2745000      .000      .000  
+761211 43123.00 I   .009367  .009044   .058325  .006742  I -.2752438  .0002330  2.8934 0.1936  P    28.281     .500     -.597     .300   .026000   .066000  -.2775000      .000      .000  
+761212 43124.00 I   .005650  .008907   .058591  .007972  I -.2782053  .0002330  3.0302 0.1648  P    28.363     .500     -.511     .300   .022000   .066000  -.2805000      .000      .000  
+761213 43125.00 I   .001900  .008537   .058889  .007119  I -.2813018  .0002330  3.1596 0.1549  P    28.457     .500     -.303     .300   .018000   .066000  -.2834000      .000      .000  
+761214 43126.00 I  -.001873  .011472   .059217  .009787  I -.2845144  .0002043  3.2585 0.1402  P    28.527     .500     -.040     .300   .015000   .066000  -.2864000      .000      .000  
+761215 43127.00 I  -.005671  .008470   .059580  .008588  I -.2878015  .0001561  3.3053 0.1286  P    28.505     .500      .238     .300   .011000   .067000  -.2893000      .000      .000  
+761216 43128.00 I  -.009501  .008367   .059986  .008573  I -.2911032  .0001561  3.2867 0.0886  P    28.376     .500      .488     .300   .008000   .067000  -.2923000      .000      .000  
+761217 43129.00 I  -.013371  .006129   .060446  .009323  I -.2943538  .0000838  3.2049 0.0886  P    28.200     .500      .605     .300   .004000   .067000  -.2952000      .000      .000  
+761218 43130.00 I  -.017289  .006035   .060970  .009173  I -.2974989  .0000838  3.0808 0.0593  P    28.088     .500      .475     .300   .000000   .067000  -.2981000      .000      .000  
+761219 43131.00 I  -.021259  .006203   .061568  .007193  I -.3005132  .0000838  2.9502 0.2498  P    28.099     .500      .143     .300  -.003000   .068000  -.3010000      .000      .000  
+761220 43132.00 I  -.025285  .004932   .062241  .005312  I -.3034101  .0004926  2.8521 0.3483  P    28.163     .500     -.144     .300  -.007000   .068000  -.3038000      .000      .000  
+761221 43133.00 I  -.029368  .003025   .062994  .002563  I -.3062365  .0006916  2.8114 0.4245  P    28.178     .500     -.162     .300  -.010000   .069000  -.3067000      .000      .000  
+761222 43134.00 I  -.033512  .005234   .063830  .004679  I -.3090520  .0006916  2.8281 0.4890  P    28.196     .500      .045     .300  -.014000   .069000  -.3096000      .000      .000  
+761223 43135.00 I  -.037719  .005306   .064751  .004444  I -.3119048  .0006916  2.8811 0.4573  P    28.399     .500      .220     .300  -.018000   .070000  -.3124000      .000      .000  
+761224 43136.00 I  -.041989  .006582   .065763  .009015  I -.3148158  .0005986  2.9390 0.4573  P    28.792     .500      .197     .300  -.021000   .070000  -.3153000      .000      .000  
+761225 43137.00 I  -.046322  .006645   .066870  .009093  I -.3177753  .0005986  2.9745 0.3862  P    29.086     .500      .057     .300  -.025000   .071000  -.3181000      .000      .000  
+761226 43138.00 I  -.050717  .008696   .068076  .012141  I -.3207530  .0004882  2.9745 0.3862  P    29.013     .500     -.023     .300  -.028000   .071000  -.3210000      .000      .000  
+761227 43139.00 I  -.055176  .008729   .069388  .012427  I -.3237125  .0004882  2.9390 0.3452  P    28.672     .500      .007     .300  -.032000   .072000  -.3238000      .000      .000  
+761228 43140.00 I  -.059698  .011773   .070808  .012281  I -.3266212  .0004882  2.8741 0.3452  P    28.384     .500      .065     .300  -.035000   .073000  -.3265000      .000      .000  
+761229 43141.00 I  -.064273  .011254   .072331  .011917  I -.3294534  .0004882  2.7873 0.2792  P    28.294     .500      .083     .300  -.039000   .074000  -.3293000      .000      .000  
+761230 43142.00 I  -.068888  .020907   .073951  .015322  I -.3321916  .0002710  2.6878 0.2792  P    28.296     .500      .066     .300  -.042000   .074000  -.3320000      .000      .000  
+761231 43143.00 I  -.073529  .020756   .075660  .007038  I -.3348284  .0002710  2.5865 0.1916  P    28.308     .500      .040     .300  -.046000   .075000  -.3348000      .000      .000  
+77 1 1 43144.00 I  -.078182  .020756   .077451  .007038  I  .6626318  .0002710  2.4961 0.1916  P    28.381     .500      .022     .300  -.049000   .076000   .6625000      .000      .000  
+77 1 2 43145.00 I  -.082831  .024669   .079316  .005364  I  .6601714  .0002710  2.4297 0.1916  P    28.520     .500      .028     .300  -.052000   .077000   .6597000      .000      .000  
+77 1 3 43146.00 I  -.087456  .026664   .081248  .004964  I  .6577609  .0002710  2.3977 0.1916  P    28.608     .500      .065     .300  -.056000   .079000   .6569000      .000      .000  
+77 1 4 43147.00 I  -.092040  .023122   .083241  .002516  I  .6553626  .0002710  2.4058 0.1916  P    28.591     .500      .088     .300  -.059000   .080000   .6542000      .000      .000  
+77 1 5 43148.00 I  -.096564  .021201   .085288  .007954  I  .6529353  .0002710  2.4559 0.1916  P    28.606     .500      .025     .300  -.063000   .082000   .6514000      .000      .000  
+77 1 6 43149.00 I  -.101010  .021201   .087382  .007954  I  .6504376  .0002710  2.5457 0.1916  P    28.788     .500     -.122     .300  -.066000   .083000   .6486000      .000      .000  
+77 1 7 43150.00 I  -.105357  .021200   .089515  .017413  I  .6478336  .0002710  2.6665 0.1916  P    29.069     .500     -.260     .300  -.069000   .085000   .6458000      .000      .000  
+77 1 8 43151.00 I  -.109588  .021200   .091680  .017413  I  .6450997  .0002710  2.8020 0.1916  P    29.249     .500     -.289     .300  -.073000   .087000   .6430000      .000      .000  
+77 1 9 43152.00 I  -.113685  .022765   .093870  .021309  I  .6422322  .0002710  2.9297 0.1358  P    29.230     .500     -.188     .300  -.076000   .088000   .6403000      .000      .000  
+77 110 43153.00 I  -.117633  .019540   .096076  .012970  I  .6392508  .0000175  3.0258 0.1358  P    29.088     .500     -.007     .300  -.080000   .090000   .6375000      .000      .000  
+77 111 43154.00 I  -.121416  .021251   .098289  .012654  I  .6361975  .0000175  3.0710 0.0124  P    28.954     .500      .201     .300  -.083000   .092000   .6347000      .000      .000  
+77 112 43155.00 I  -.125019  .021592   .100498  .012505  I  .6331285  .0000175  3.0571 0.0124  P    28.867     .500      .410     .300  -.086000   .094000   .6319000      .000      .000  
+77 113 43156.00 I  -.128428  .022678   .102693  .012348  I  .6301016  .0000175  2.9885 0.0124  P    28.767     .500      .582     .300  -.090000   .096000   .6291000      .000      .000  
+77 114 43157.00 I  -.131651  .023308   .104872  .010561  I  .6271638  .0000175  2.8831 0.0124  P    28.577     .500      .633     .300  -.093000   .098000   .6264000      .000      .000  
+77 115 43158.00 I  -.134713  .022823   .107036  .013000  I  .6243384  .0000175  2.7688 0.1358  P    28.301     .500      .473     .300  -.097000   .100000   .6236000      .000      .000  
+77 116 43159.00 I  -.137636  .032519   .109191  .021442  I  .6216184  .0002710  2.6778 0.1358  P    28.032     .500      .121     .300  -.100000   .102000   .6208000      .000      .000  
+77 117 43160.00 I  -.140438  .029047   .111338  .028518  I  .6189663  .0002710  2.6363 0.1916  P    27.867     .500     -.236     .300  -.103000   .104000   .6180000      .000      .000  
+77 118 43161.00 I  -.143137  .023743   .113481  .032929  I  .6163259  .0002710  2.6544 0.1866  P    27.845     .500     -.354     .300  -.106000   .107000   .6152000      .000      .000  
+77 119 43162.00 I  -.145749  .011772   .115623  .014238  I  .6136413  .0002567  2.7210 0.1866  P    27.959     .500     -.157     .300  -.110000   .109000   .6125000      .000      .000  
+77 120 43163.00 I  -.148288  .008320   .117769  .014462  I  .6108770  .0002567  2.8081 0.1815  P    28.187     .500      .173     .300  -.113000   .112000   .6097000      .000      .000  
+77 121 43164.00 I  -.150769  .009529   .119923  .014441  I  .6080291  .0002567  2.8830 0.1815  P    28.445     .500      .361     .300  -.116000   .114000   .6069000      .000      .000  
+77 122 43165.00 I  -.153209  .009424   .122089  .012373  I  .6051233  .0002567  2.9210 0.1815  P    28.582     .500      .293     .300  -.119000   .117000   .6042000      .000      .000  
+77 123 43166.00 I  -.155619  .013496   .124269  .013654  I  .6022025  .0002567  2.9129 0.1815  P    28.498     .500      .069     .300  -.122000   .120000   .6015000      .000      .000  
+77 124 43167.00 I  -.158001  .013453   .126466  .010047  I  .5993115  .0002567  2.8629 0.1866  P    28.252     .500     -.145     .300  -.126000   .122000   .5987000      .000      .000  
+77 125 43168.00 I  -.160358  .024519   .128681  .025391  I  .5964868  .0002710  2.7825 0.1866  P    27.995     .500     -.276     .300  -.129000   .125000   .5960000      .000      .000  
+77 126 43169.00 I  -.162693  .027971   .130919  .023779  I  .5937527  .0002710  2.6836 0.1916  P    27.810     .500     -.353     .300  -.132000   .128000   .5933000      .000      .000  
+77 127 43170.00 I  -.165008  .024230   .133184  .028017  I  .5911218  .0002710  2.5782 0.3047  P    27.702     .500     -.435     .300  -.135000   .131000   .5906000      .000      .000  
+77 128 43171.00 I  -.167302  .011592   .135480  .012220  I  .5885942  .0005459  2.4791 0.3047  P    27.693     .500     -.542     .300  -.138000   .134000   .5879000      .000      .000  
+77 129 43172.00 I  -.169575  .011391   .137814  .012965  I  .5861572  .0005459  2.3992 0.3623  P    27.829     .500     -.641     .300  -.142000   .137000   .5853000      .000      .000  
+77 130 43173.00 I  -.171822  .005783   .140189  .011568  I  .5837852  .0004765  2.3510 0.3623  P    28.064     .500     -.672     .300  -.145000   .140000   .5826000      .000      .000  
+77 131 43174.00 I  -.174043  .008497   .142610  .012645  I  .5814415  .0004765  2.3440 0.3369  P    28.252     .500     -.625     .300  -.148000   .143000   .5799000      .000      .000  
+77 2 1 43175.00 I  -.176244  .008584   .145082  .011611  I  .5790819  .0004765  2.3831 0.3369  P    28.313     .500     -.591     .300  -.151000   .146000   .5773000      .000      .000  
+77 2 2 43176.00 I  -.178434  .009857   .147612  .013004  I  .5766600  .0004765  2.4681 0.2700  P    28.338     .500     -.679     .300  -.154000   .149000   .5746000      .000      .000  
+77 2 3 43177.00 I  -.180620  .020941   .150203  .026200  I  .5741326  .0002541  2.5925 0.2700  P    28.479     .500     -.874     .300  -.157000   .153000   .5720000      .000      .000  
+77 2 4 43178.00 I  -.182809  .018695   .152861  .023468  I  .5714666  .0002541  2.7421 0.1601  P    28.750     .500    -1.022     .300  -.160000   .156000   .5693000      .000      .000  
+77 2 5 43179.00 I  -.185006  .010647   .155592  .013575  I  .5686478  .0001950  2.8934 0.1601  P    28.993     .500     -.992     .300  -.163000   .159000   .5667000      .000      .000  
+77 2 6 43180.00 I  -.187216  .010921   .158401  .014592  I  .5656889  .0001950  3.0173 0.1307  P    29.049     .500     -.811     .300  -.166000   .162000   .5641000      .000      .000  
+77 2 7 43181.00 I  -.189444  .009120   .161298  .013766  I  .5626312  .0001741  3.0872 0.1307  P    28.902     .500     -.629     .300  -.169000   .166000   .5614000      .000      .000  
+77 2 8 43182.00 I  -.191697  .008975   .164290  .013470  I  .5595370  .0001741  3.0894 0.1231  P    28.679     .500     -.551     .300  -.172000   .169000   .5588000      .000      .000  
+77 2 9 43183.00 I  -.193973  .007492   .167373  .012156  I  .5564734  .0001741  3.0285 0.1231  P    28.516     .500     -.575     .300  -.175000   .173000   .5561000      .000      .000  
+77 210 43184.00 I  -.196266  .007294   .170544  .011866  I  .5534939  .0001741  2.9260 0.1036  P    28.433     .500     -.641     .300  -.178000   .176000   .5535000      .000      .000  
+77 211 43185.00 I  -.198571  .009297   .173796  .016363  I  .5506249  .0001125  2.8130 0.2395  P    28.350     .500     -.730     .300  -.181000   .180000   .5509000      .000      .000  
+77 212 43186.00 I  -.200881  .007912   .177122  .006975  I  .5478609  .0004462  2.7210 0.2708  P    28.185     .500     -.876     .300  -.184000   .183000   .5482000      .000      .000  
+77 213 43187.00 I  -.203191  .007313   .180515  .003420  I  .5451674  .0005297  2.6754 0.3463  P    27.964     .500    -1.107     .300  -.186000   .187000   .5456000      .000      .000  
+77 214 43188.00 I  -.205494  .010447   .183966  .003672  I  .5424907  .0005297  2.6880 0.3746  P    27.807     .500    -1.365     .300  -.189000   .190000   .5429000      .000      .000  
+77 215 43189.00 I  -.207785  .010649   .187470  .003611  I  .5397734  .0005297  2.7545 0.3746  P    27.820     .500    -1.502     .300  -.192000   .194000   .5403000      .000      .000  
+77 216 43190.00 I  -.210053  .012242   .191017  .003705  I  .5369705  .0005297  2.8545 0.3746  P    27.986     .500    -1.407     .300  -.194000   .198000   .5376000      .000      .000  
+77 217 43191.00 I  -.212281  .012580   .194602  .003778  I  .5340632  .0005297  2.9582 0.2975  P    28.176     .500    -1.146     .300  -.197000   .201000   .5349000      .000      .000  
+77 218 43192.00 I  -.214452  .025281   .198216  .009410  I  .5310624  .0002710  3.0370 0.2975  P    28.270     .500     -.943     .300  -.199000   .205000   .5322000      .000      .000  
+77 219 43193.00 I  -.216553  .025418   .201853  .007189  I  .5280040  .0002710  3.0714 0.1916  P    28.258     .500     -.986     .300  -.202000   .208000   .5295000      .000      .000  
+77 220 43194.00 I  -.218572  .024741   .205506  .006873  I  .5249359  .0002710  3.0568 0.1916  P    28.206     .500    -1.260     .300  -.204000   .212000   .5268000      .000      .000  
+77 221 43195.00 I  -.220498  .021266   .209170  .006837  I  .5219041  .0002710  3.0009 0.1916  P    28.145     .500    -1.593     .300  -.206000   .216000   .5240000      .000      .000  
+77 222 43196.00 I  -.222323  .019259   .212838  .009589  I  .5189433  .0002710  2.9172 0.1916  P    28.031     .500    -1.837     .300  -.208000   .219000   .5212000      .000      .000  
+77 223 43197.00 I  -.224035  .015166   .216503  .010653  I  .5160743  .0002710  2.8197 0.1916  P    27.853     .500    -1.978     .300  -.210000   .223000   .5185000      .000      .000  
+77 224 43198.00 I  -.225626  .013190   .220158  .027018  I  .5133038  .0002710  2.7224 0.2912  P    27.705     .500    -2.092     .300  -.212000   .226000   .5157000      .000      .000  
+77 225 43199.00 I  -.227088  .009991   .223796  .013388  I  .5106251  .0005156  2.6382 0.2912  P    27.713     .500    -2.234     .300  -.214000   .230000   .5129000      .000      .000  
+77 226 43200.00 I  -.228413  .010191   .227410  .013740  I  .5080187  .0005156  2.5801 0.3401  P    27.903     .500    -2.371     .300  -.216000   .234000   .5100000      .000      .000  
+77 227 43201.00 I  -.229592  .008990   .230994  .014023  I  .5054527  .0004438  2.5586 0.3401  P    28.188     .500    -2.407     .300  -.217000   .237000   .5071000      .000      .000  
+77 228 43202.00 I  -.230618  .008921   .234542  .015210  I  .5028868  .0004438  2.5812 0.3138  P    28.465     .500    -2.296     .300  -.219000   .241000   .5041000      .000      .000  
+77 3 1 43203.00 I  -.231493  .008770   .238056  .015216  I  .5002744  .0004438  2.6514 0.3138  P    28.675     .500    -2.144     .300  -.220000   .244000   .5012000      .000      .000  
+77 3 2 43204.00 I  -.232225  .009383   .241545  .014921  I  .4975693  .0004438  2.7658 0.2430  P    28.796     .500    -2.137     .300  -.222000   .248000   .4983000      .000      .000  
+77 3 3 43205.00 I  -.232824  .013131   .245014  .021137  I  .4947312  .0001982  2.9150 0.2430  P    28.857     .500    -2.334     .300  -.223000   .252000   .4954000      .000      .000  
+77 3 4 43206.00 I  -.233299  .014034   .248473  .018665  I  .4917340  .0001982  3.0799 0.1679  P    28.937     .500    -2.567     .300  -.224000   .256000   .4925000      .000      .000  
+77 3 5 43207.00 I  -.233660  .014292   .251931  .018405  I  .4885759  .0002710  3.2312 0.1679  P    29.093     .500    -2.604     .300  -.226000   .259000   .4895000      .000      .000  
+77 3 6 43208.00 I  -.233915  .012379   .255398  .016966  I  .4852872  .0002710  3.3356 0.1916  P    29.261     .500    -2.412     .300  -.227000   .263000   .4866000      .000      .000  
+77 3 7 43209.00 I  -.234077  .013604   .258884  .008857  I  .4819292  .0002710  3.3666 0.1916  P    29.307     .500    -2.195     .300  -.228000   .267000   .4837000      .000      .000  
+77 3 8 43210.00 I  -.234156  .016745   .262400  .007705  I  .4785808  .0002710  3.3173 0.2889  P    29.187     .500    -2.172     .300  -.229000   .271000   .4807000      .000      .000  
+77 3 9 43211.00 I  -.234164  .007237   .265954  .012964  I  .4753155  .0005102  3.2052 0.2889  P    29.005     .500    -2.365     .300  -.230000   .275000   .4777000      .000      .000  
+77 310 43212.00 I  -.234116  .007071   .269558  .013246  I  .4721797  .0005102  3.0652 0.3608  P    28.887     .500    -2.630     .300  -.231000   .278000   .4748000      .000      .000  
+77 311 43213.00 I  -.234025  .006124   .273222  .013477  I  .4691810  .0005102  2.9373 0.3608  P    28.848     .500    -2.826     .300  -.232000   .282000   .4718000      .000      .000  
+77 312 43214.00 I  -.233903  .009348   .276956  .013689  I  .4662904  .0005102  2.8533 0.3125  P    28.811     .500    -2.931     .300  -.233000   .286000   .4688000      .000      .000  
+77 313 43215.00 I  -.233757  .010982   .280757  .010289  I  .4634546  .0003610  2.8287 0.3125  P    28.743     .500    -3.005     .300  -.234000   .290000   .4658000      .000      .000  
+77 314 43216.00 I  -.233593  .010816   .284619  .010199  I  .4606137  .0003610  2.8620 0.1808  P    28.716     .500    -3.095     .300  -.235000   .294000   .4627000      .000      .000  
+77 315 43217.00 I  -.233417  .013554   .288535  .006243  I  .4577165  .0000194  2.9377 0.1808  P    28.835     .500    -3.172     .300  -.235000   .297000   .4597000      .000      .000  
+77 316 43218.00 I  -.233234  .013247   .292501  .006631  I  .4547318  .0000194  3.0329 0.0137  P    29.092     .500    -3.168     .300  -.236000   .301000   .4566000      .000      .000  
+77 317 43219.00 I  -.233050  .013600   .296512  .005470  I  .4516527  .0000194  3.1221 0.3554  P    29.335     .500    -3.069     .300  -.237000   .305000   .4536000      .000      .000  
+77 318 43220.00 I  -.232852  .013934   .300562  .009396  I  .4484970  .0007106  3.1831 0.5025  P    29.406     .500    -2.961     .300  -.237000   .309000   .4505000      .000      .000  
+77 319 43221.00 I  -.232628  .011358   .304642  .012115  I  .4453000  .0010048  3.2037 0.6153  P    29.323     .500    -2.962     .300  -.237000   .313000   .4474000      .000      .000  
+77 320 43222.00 I  -.232363  .010448   .308747  .012000  I  .4421034  .0010048  3.1831 0.7105  P    29.246     .500    -3.111     .300  -.238000   .316000   .4444000      .000      .000  
+77 321 43223.00 I  -.232043  .012063   .312871  .012224  I  .4389447  .0010048  3.1300 0.7105  P    29.252     .500    -3.341     .300  -.238000   .320000   .4413000      .000      .000  
+77 322 43224.00 I  -.231654  .010611   .317004  .012224  I  .4358503  .0010048  3.0564 0.7105  P    29.254     .500    -3.559     .300  -.238000   .324000   .4382000      .000      .000  
+77 323 43225.00 I  -.231184  .011834   .321135  .012239  I  .4328343  .0010048  2.9756 0.5204  P    29.177     .500    -3.724     .300  -.238000   .328000   .4350000      .000      .000  
+77 324 43226.00 I  -.230620  .020829   .325247  .029484  I  .4298967  .0002710  2.9018 0.5204  P    29.111     .500    -3.862     .300  -.238000   .332000   .4319000      .000      .000  
+77 325 43227.00 I  -.229953  .021438   .329327  .007713  I  .4270241  .0002710  2.8476 0.3225  P    29.180     .500    -4.007     .300  -.237000   .335000   .4287000      .000      .000  
+77 326 43228.00 I  -.229171  .010363   .333361  .013201  I  .4241914  .0005852  2.8237 0.3073  P    29.347     .500    -4.144     .300  -.237000   .339000   .4256000      .000      .000  
+77 327 43229.00 I  -.228262  .010520   .337335  .013372  I  .4213638  .0005517  2.8385 0.4021  P    29.515     .500    -4.188     .300  -.237000   .343000   .4224000      .000      .000  
+77 328 43230.00 I  -.227216  .011487   .341235  .014594  I  .4184999  .0005517  2.8967 0.3901  P    29.708     .500    -4.053     .300  -.237000   .347000   .4192000      .000      .000  
+77 329 43231.00 I  -.226025  .011775   .345047  .014594  I  .4155560  .0005517  2.9981 0.3901  P    30.002     .500    -3.795     .300  -.236000   .351000   .4160000      .000      .000  
+77 330 43232.00 I  -.224684  .011524   .348760  .014261  I  .4124907  .0005517  3.1386 0.3901  P    30.295     .500    -3.633     .300  -.236000   .354000   .4129000      .000      .000  
+77 331 43233.00 I  -.223197  .008112   .352373  .014566  I  .4092696  .0005517  3.3068 0.3611  P    30.373     .500    -3.746     .300  -.235000   .358000   .4097000      .000      .000  
+77 4 1 43234.00 I  -.221567  .015056   .355890  .025045  I  .4058751  .0004660  3.4809 0.3073  P    30.223     .500    -4.042     .300  -.235000   .362000   .4065000      .000      .000  
+77 4 2 43235.00 I  -.219801  .013625   .359314  .017534  I  .4023164  .0002710  3.6292 0.2472  P    30.088     .500    -4.219     .300  -.234000   .365000   .4033000      .000      .000  
+77 4 3 43236.00 I  -.217905  .010485   .362650  .009604  I  .3986364  .0001649  3.7182 0.1522  P    30.143     .500    -4.103     .300  -.234000   .369000   .4000000      .000      .000  
+77 4 4 43237.00 I  -.215888  .009349   .365903  .007328  I  .3949074  .0001386  3.7249 0.2105  P    30.266     .500    -3.853     .300  -.233000   .372000   .3968000      .000      .000  
+77 4 5 43238.00 I  -.213757  .009523   .369077  .007233  I  .3912141  .0003873  3.6493 0.2057  P    30.238     .500    -3.769     .300  -.233000   .376000   .3935000      .000      .000  
+77 4 6 43239.00 I  -.211521  .009703   .372177  .007365  I  .3876271  .0003873  3.5190 0.2611  P    30.031     .500    -3.961     .300  -.232000   .379000   .3903000      .000      .000  
+77 4 7 43240.00 I  -.209204  .016548   .375211  .009990  I  .3841797  .0003502  3.3780 0.2611  P    29.805     .500    -4.268     .300  -.231000   .382000   .3870000      .000      .000  
+77 4 8 43241.00 I  -.206833  .016784   .378192  .010051  I  .3808609  .0003502  3.2677 0.2820  P    29.696     .500    -4.487     .300  -.230000   .385000   .3838000      .000      .000  
+77 4 9 43242.00 I  -.204436  .020317   .381129  .013166  I  .3776260  .0004421  3.2124 0.3223  P    29.703     .500    -4.562     .300  -.228000   .389000   .3805000      .000      .000  
+77 410 43243.00 I  -.202037  .020762   .384034  .012049  I  .3744170  .0005411  3.2144 0.2242  P    29.750     .500    -4.563     .300  -.227000   .392000   .3773000      .000      .000  
+77 411 43244.00 I  -.199634  .020225   .386910  .011755  I  .3711831  .0000743  3.2586 0.2759  P    29.790     .500    -4.552     .300  -.226000   .395000   .3740000      .000      .000  
+77 412 43245.00 I  -.197223  .015223   .389761  .009177  I  .3678933  .0001081  3.3222 0.0705  P    29.848     .500    -4.544     .300  -.224000   .398000   .3707000      .000      .000  
+77 413 43246.00 I  -.194800  .009539   .392590  .009871  I  .3645398  .0001199  3.3825 0.0807  P    29.978     .500    -4.535     .300  -.222000   .401000   .3674000      .000      .000  
+77 414 43247.00 I  -.192364  .002535   .395402  .004852  I  .3611356  .0001199  3.4210 0.0848  P    30.160     .500    -4.521     .300  -.221000   .405000   .3641000      .000      .000  
+77 415 43248.00 I  -.189909  .006708   .398198  .008110  I  .3577092  .0001199  3.4255 0.0848  P    30.269     .500    -4.491     .300  -.219000   .408000   .3608000      .000      .000  
+77 416 43249.00 I  -.187432  .006883   .400979  .007774  I  .3542975  .0001199  3.3914 0.0848  P    30.213     .500    -4.440     .300  -.217000   .411000   .3575000      .000      .000  
+77 417 43250.00 I  -.184928  .006859   .403743  .008093  I  .3509383  .0001199  3.3217 0.2928  P    30.054     .500    -4.400     .300  -.215000   .414000   .3542000      .000      .000  
+77 418 43251.00 I  -.182393  .006948   .406489  .007601  I  .3476626  .0005731  3.2266 0.2928  P    29.923     .500    -4.434     .300  -.213000   .417000   .3509000      .000      .000  
+77 419 43252.00 I  -.179824  .006793   .409217  .011127  I  .3444889  .0005731  3.1202 0.4052  P    29.860     .500    -4.576     .300  -.210000   .420000   .3476000      .000      .000  
+77 420 43253.00 I  -.177220  .006631   .411927  .011266  I  .3414210  .0005731  3.0172 0.4052  P    29.846     .500    -4.787     .300  -.208000   .423000   .3443000      .000      .000  
+77 421 43254.00 I  -.174581  .007115   .414621  .012381  I  .3384487  .0005731  2.9314 0.3698  P    29.908     .500    -4.982     .300  -.206000   .426000   .3410000      .000      .000  
+77 422 43255.00 I  -.171908  .006734   .417300  .008576  I  .3355486  .0004675  2.8745 0.3698  P    30.055     .500    -5.120     .300  -.204000   .429000   .3377000      .000      .000  
+77 423 43256.00 I  -.169202  .010042   .419965  .008512  I  .3326873  .0004675  2.8548 0.2788  P    30.160     .500    -5.222     .300  -.201000   .432000   .3345000      .000      .000  
+77 424 43257.00 I  -.166465  .013233   .422621  .010330  I  .3298248  .0003038  2.8776 0.2788  P    30.111     .500    -5.286     .300  -.199000   .435000   .3312000      .000      .000  
+77 425 43258.00 I  -.163700  .014531   .425272  .012124  I  .3269178  .0003038  2.9435 0.2148  P    30.061     .500    -5.235     .300  -.196000   .438000   .3280000      .000      .000  
+77 426 43259.00 I  -.160921  .014745   .427918  .010578  I  .3239247  .0003038  3.0489 0.2148  P    30.276     .500    -5.026     .300  -.194000   .441000   .3247000      .000      .000  
+77 427 43260.00 I  -.158151  .014698   .430559  .010746  I  .3208092  .0003038  3.1866 0.2208  P    30.714     .500    -4.790     .300  -.191000   .444000   .3215000      .000      .000  
+77 428 43261.00 I  -.155409  .013458   .433193  .009807  I  .3175457  .0003205  3.3417 0.2208  P    30.982     .500    -4.741     .300  -.189000   .446000   .3183000      .000      .000  
+77 429 43262.00 I  -.152699  .015055   .435805  .009703  I  .3141276  .0003205  3.4913 0.3197  P    30.823     .500    -4.916     .300  -.186000   .449000   .3150000      .000      .000  
+77 430 43263.00 I  -.150019  .013703   .438383  .010901  I  .3105747  .0005533  3.6059 0.3197  P    30.438     .500    -5.080     .300  -.184000   .451000   .3118000      .000      .000  
+77 5 1 43264.00 I  -.147367  .013764   .440911  .010825  I  .3069376  .0005533  3.6550 0.3912  P    30.183     .500    -5.000     .300  -.181000   .454000   .3086000      .000      .000  
+77 5 2 43265.00 I  -.144737  .011059   .443377  .008722  I  .3032920  .0005533  3.6221 0.3912  P    30.125     .500    -4.748     .300  -.178000   .457000   .3054000      .000      .000  
+77 5 3 43266.00 I  -.142125  .020285   .445774  .008722  I  .2997180  .0005533  3.5155 0.3757  P    30.058     .500    -4.613     .300  -.175000   .459000   .3022000      .000      .000  
+77 5 4 43267.00 I  -.139531  .020968   .448096  .008363  I  .2962739  .0005084  3.3701 0.3757  P    29.841     .500    -4.743     .300  -.173000   .462000   .2991000      .000      .000  
+77 5 5 43268.00 I  -.136957  .021823   .450348  .008539  I  .2929751  .0005084  3.2327 0.3003  P    29.561     .500    -4.998     .300  -.170000   .464000   .2959000      .000      .000  
+77 5 6 43269.00 I  -.134402  .020176   .452536  .004878  I  .2897933  .0003199  3.1410 0.3104  P    29.403     .500    -5.171     .300  -.167000   .467000   .2927000      .000      .000  
+77 5 7 43270.00 I  -.131868  .015461   .454667  .005910  I  .2866728  .0003562  3.1101 0.2394  P    29.463     .500    -5.225     .300  -.164000   .469000   .2896000      .000      .000  
+77 5 8 43271.00 I  -.129341  .015481   .456743  .005856  I  .2835566  .0003562  3.1288 0.2519  P    29.663     .500    -5.242     .300  -.161000   .471000   .2865000      .000      .000  
+77 5 9 43272.00 I  -.126805  .016083   .458766  .006168  I  .2804070  .0003562  3.1720 0.2688  P    29.784     .500    -5.249     .300  -.158000   .474000   .2833000      .000      .000  
+77 510 43273.00 I  -.124248  .012527   .460739  .007725  I  .2772138  .0004026  3.2116 0.2634  P    29.666     .500    -5.211     .300  -.155000   .476000   .2802000      .000      .000  
+77 511 43274.00 I  -.121648  .012635   .462667  .008167  I  .2739915  .0003881  3.2280 0.2796  P    29.409     .500    -5.147     .300  -.152000   .478000   .2771000      .000      .000  
+77 512 43275.00 I  -.118986  .011396   .464559  .007977  I  .2707687  .0003881  3.2115 0.2691  P    29.264     .500    -5.124     .300  -.149000   .480000   .2741000      .000      .000  
+77 513 43276.00 I  -.116243  .014237   .466423  .010539  I  .2675802  .0003730  3.1597 0.2691  P    29.312     .500    -5.141     .300  -.145000   .482000   .2710000      .000      .000  
+77 514 43277.00 I  -.113404  .013935   .468267  .009643  I  .2644600  .0003730  3.0757 0.2638  P    29.349     .500    -5.110     .300  -.142000   .483000   .2680000      .000      .000  
+77 515 43278.00 I  -.110466  .015815   .470091  .009597  I  .2614370  .0003730  2.9669 0.2638  P    29.175     .500    -4.999     .300  -.138000   .485000   .2649000      .000      .000  
+77 516 43279.00 I  -.107435  .014657   .471893  .009386  I  .2585308  .0003730  2.8442 0.2305  P    28.849     .500    -4.918     .300  -.135000   .487000   .2619000      .000      .000  
+77 517 43280.00 I  -.104316  .029701   .473673  .016075  I  .2557486  .0002710  2.7213 0.2305  P    28.587     .500    -5.003     .300  -.131000   .488000   .2590000      .000      .000  
+77 518 43281.00 I  -.101114  .024953   .475428  .014447  I  .2530837  .0002710  2.6117 0.1916  P    28.527     .500    -5.236     .300  -.128000   .490000   .2560000      .000      .000  
+77 519 43282.00 I  -.097835  .024953   .477158  .014447  I  .2505171  .0002710  2.5264 0.1916  P    28.662     .500    -5.456     .300  -.124000   .491000   .2531000      .000      .000  
+77 520 43283.00 I  -.094479  .035178   .478858  .008647  I  .2480201  .0002710  2.4734 0.1916  P    28.875     .500    -5.542     .300  -.121000   .493000   .2501000      .000      .000  
+77 521 43284.00 I  -.091047  .035178   .480525  .008647  I  .2455580  .0002710  2.4573 0.1916  P    28.964     .500    -5.539     .300  -.117000   .494000   .2472000      .000      .000  
+77 522 43285.00 I  -.087541  .003310   .482158  .002690  I  .2430929  .0002710  2.4792 0.1916  P    28.791     .500    -5.558     .300  -.113000   .495000   .2444000      .000      .000  
+77 523 43286.00 I  -.083960  .003310   .483754  .002690  I  .2405877  .0002710  2.5369 0.1916  P    28.503     .500    -5.599     .300  -.109000   .496000   .2416000      .000      .000  
+77 524 43287.00 I  -.080306  .003310   .485308  .002690  I  .2380093  .0002710  2.6241 0.1916  P    28.458     .500    -5.552     .300  -.105000   .497000   .2388000      .000      .000  
+77 525 43288.00 I  -.076579  .003310   .486820  .002690  I  .2353333  .0002710  2.7299 0.1916  P    28.791     .500    -5.382     .300  -.101000   .498000   .2360000      .000      .000  
+77 526 43289.00 I  -.072781  .003310   .488284  .002690  I  .2325485  .0002710  2.8386 0.3725  P    29.185     .500    -5.204     .300  -.097000   .499000   .2332000      .000      .000  
+77 527 43290.00 I  -.068911  .003525   .489700  .000995  I  .2296621  .0006939  2.9291 0.3725  P    29.219     .500    -5.118     .300  -.093000   .500000   .2305000      .000      .000  
+77 528 43291.00 I  -.064971  .003525   .491064  .000995  I  .2267043  .0006939  2.9774 0.4907  P    28.839     .500    -5.056     .300  -.089000   .500000   .2278000      .000      .000  
+77 529 43292.00 I  -.060961  .003525   .492372  .000995  I  .2237282  .0006939  2.9631 0.4907  P    28.339     .500    -4.892     .300  -.084000   .501000   .2251000      .000      .000  
+77 530 43293.00 I  -.056883  .003525   .493623  .000995  I  .2208005  .0006939  2.8818 0.4907  P    27.967     .500    -4.673     .300  -.080000   .501000   .2224000      .000      .000  
+77 531 43294.00 I  -.052743  .003525   .494815  .000995  I  .2179811  .0006939  2.7511 0.4907  P    27.728     .500    -4.602     .300  -.076000   .502000   .2197000      .000      .000  
+77 6 1 43295.00 I  -.048549  .003525   .495946  .000995  I  .2153028  .0006939  2.6069 0.3725  P    27.514     .500    -4.759     .300  -.072000   .503000   .2172000      .000      .000  
+77 6 2 43296.00 I  -.044308  .003310   .497014  .002690  I  .2127578  .0002710  2.4912 0.3725  P    27.293     .500    -4.979     .300  -.067000   .503000   .2146000      .000      .000  
+77 6 3 43297.00 I  -.040027  .003310   .498019  .002690  I  .2103014  .0002710  2.4325 0.1916  P    27.156     .500    -5.066     .300  -.063000   .504000   .2121000      .000      .000  
+77 6 4 43298.00 I  -.035714  .003310   .498957  .002690  I  .2078726  .0002710  2.4343 0.4973  P    27.237     .500    -5.035     .300  -.058000   .504000   .2095000      .000      .000  
+77 6 5 43299.00 I  -.031377  .002283   .499829  .000094  I  .2054199  .0009569  2.4749 0.4651  P    27.520     .500    -5.035     .300  -.054000   .505000   .2070000      .000      .000  
+77 6 6 43300.00 I  -.027023  .002283   .500632  .000094  I  .2029206  .0008898  2.5220 0.6533  P    27.719     .500    -5.097     .300  -.050000   .505000   .2046000      .000      .000  
+77 6 7 43301.00 I  -.022658  .002283   .501365  .000094  I  .2003829  .0008898  2.5481 0.6292  P    27.497     .500    -5.117     .300  -.045000   .506000   .2021000      .000      .000  
+77 6 8 43302.00 I  -.018292  .002283   .502026  .000094  I  .1978364  .0008898  2.5384 0.5611  P    26.888     .500    -5.058     .300  -.041000   .506000   .1997000      .000      .000  
+77 6 9 43303.00 I  -.013929  .002274   .502613  .004000  I  .1953189  .0006838  2.4905 0.5611  P    26.346     .500    -5.015     .300  -.036000   .507000   .1972000      .000      .000  
+77 610 43304.00 I  -.009571  .002274   .503126  .004000  I  .1928660  .0006838  2.4107 0.3948  P    26.217     .500    -5.058     .300  -.032000   .507000   .1948000      .000      .000  
+77 611 43305.00 I  -.005223  .002264   .503562  .005656  I  .1905050  .0003948  2.3083 0.3955  P    26.322     .500    -5.108     .300  -.027000   .507000   .1925000      .000      .000  
+77 612 43306.00 I  -.000887  .002913   .503920  .004782  I  .1882536  .0003978  2.1931 0.2802  P    26.229     .500    -5.068     .300  -.023000   .507000   .1902000      .000      .000  
+77 613 43307.00 I   .003430  .002913   .504204  .004782  I  .1861192  .0003978  2.0765 0.2813  P    25.824     .500    -4.992     .300  -.018000   .508000   .1878000      .000      .000  
+77 614 43308.00 I   .007721  .002913   .504419  .004782  I  .1840966  .0003978  1.9718 0.3438  P    25.389     .500    -5.022     .300  -.014000   .508000   .1855000      .000      .000  
+77 615 43309.00 I   .011979  .003442   .504571  .003709  I  .1821681  .0005608  1.8897 0.3496  P    25.207     .500    -5.179     .300  -.009000   .508000   .1832000      .000      .000  
+77 616 43310.00 I   .016202  .003297   .504661  .005862  I  .1803069  .0005751  1.8384 0.4016  P    25.294     .500    -5.320     .300  -.004000   .508000   .1810000      .000      .000  
+77 617 43311.00 I   .020388  .003297   .504691  .005862  I  .1784795  .0005751  1.8225 0.4116  P    25.485     .500    -5.314     .300   .000000   .508000   .1788000      .000      .000  
+77 618 43312.00 I   .024538  .003145   .504661  .007414  I  .1766499  .0005890  1.8427 0.4116  P    25.593     .500    -5.203     .300   .005000   .508000   .1765000      .000      .000  
+77 619 43313.00 I   .028650  .003145   .504573  .007414  I  .1747829  .0005890  1.8966 0.4165  P    25.475     .500    -5.137     .300   .009000   .508000   .1743000      .000      .000  
+77 620 43314.00 I   .032724  .003145   .504425  .007414  I  .1728472  .0005890  1.9789 0.3835  P    25.145     .500    -5.184     .300   .014000   .508000   .1721000      .000      .000  
+77 621 43315.00 I   .036766  .002336   .504209  .005245  I  .1708190  .0004913  2.0795 0.3474  P    24.834     .500    -5.257     .300   .018000   .507000   .1700000      .000      .000  
+77 622 43316.00 I   .040780  .001011   .503916  .000238  I  .1686870  .0003686  2.1837 0.3095  P    24.795     .500    -5.233     .300   .023000   .507000   .1679000      .000      .000  
+77 623 43317.00 I   .044771  .001011   .503537  .000238  I  .1664562  .0003765  2.2740 0.3442  P    25.004     .500    -5.082     .300   .027000   .506000   .1658000      .000      .000  
+77 624 43318.00 I   .048745  .004864   .503064  .000406  I  .1641497  .0005813  2.3320 0.3463  P    25.155     .500    -4.860     .300   .032000   .506000   .1637000      .000      .000  
+77 625 43319.00 I   .052704  .004864   .502488  .000406  I  .1618082  .0005813  2.3419 0.4110  P    24.986     .500    -4.612     .300   .036000   .505000   .1616000      .000      .000  
+77 626 43320.00 I   .056653  .004864   .501798  .000406  I  .1594844  .0005813  2.2964 0.4518  P    24.508     .500    -4.373     .300   .040000   .504000   .1596000      .000      .000  
+77 627 43321.00 I   .060594  .006804   .500987  .000522  I  .1572312  .0006917  2.2034 0.4186  P    23.933     .500    -4.239     .300   .045000   .503000   .1576000      .000      .000  
+77 628 43322.00 I   .064541  .008208   .500045  .007675  I  .1550854  .0006025  2.0870 0.4709  P    23.461     .500    -4.329     .300   .049000   .503000   .1556000      .000      .000  
+77 629 43323.00 I   .068508  .008208   .498965  .007675  I  .1530532  .0006391  1.9825 0.3742  P    23.157     .500    -4.613     .300   .054000   .502000   .1536000      .000      .000  
+77 630 43324.00 I   .072512  .009404   .497736  .010841  I  .1511055  .0004439  1.9227 0.4782  P    22.968     .500    -4.869     .300   .058000   .501000   .1516000      .000      .000  
+77 7 1 43325.00 I   .076569  .009404   .496352  .010841  I  .1491874  .0007116  1.9240 0.4194  P    22.854     .500    -4.890     .300   .062000   .500000   .1497000      .000      .000  
+77 7 2 43326.00 I   .080691  .009404   .494806  .010841  I  .1472403  .0007116  1.9768 0.4898  P    22.878     .500    -4.726     .300   .067000   .498000   .1478000      .000      .000  
+77 7 3 43327.00 I   .084878  .009155   .493103  .010095  I  .1452269  .0006731  2.0504 0.4981  P    23.087     .500    -4.606     .300   .071000   .497000   .1458000      .000      .000  
+77 7 4 43328.00 I   .089128  .008899   .491249  .009289  I  .1431446  .0006971  2.1088 0.4388  P    23.282     .500    -4.652     .300   .076000   .495000   .1439000      .000      .000  
+77 7 5 43329.00 I   .093441  .006458   .489249  .007229  I  .1410233  .0005631  2.1253 0.4404  P    23.101     .500    -4.759     .300   .080000   .494000   .1420000      .000      .000  
+77 7 6 43330.00 I   .097811  .006458   .487111  .007229  I  .1389103  .0005385  2.0929 0.3415  P    22.459     .500    -4.794     .300   .084000   .493000   .1401000      .000      .000  
+77 7 7 43331.00 I   .102226  .006458   .484849  .007229  I  .1368509  .0003865  2.0202 0.3314  P    21.758     .500    -4.779     .300   .089000   .491000   .1382000      .000      .000  
+77 7 8 43332.00 I   .106665  .006458   .482482  .007229  I  .1348787  .0003865  1.9211 0.2169  P    21.455     .500    -4.809     .300   .093000   .490000   .1364000      .000      .000  
+77 7 9 43333.00 I   .111111  .002892   .480027  .003875  I  .1330132  .0001971  1.8087 0.2200  P    21.511     .500    -4.884     .300   .098000   .488000   .1345000      .000      .000  
+77 710 43334.00 I   .115549  .002892   .477496  .003875  I  .1312618  .0002104  1.6949 0.1684  P    21.501     .500    -4.925     .300   .102000   .487000   .1326000      .000      .000  
+77 711 43335.00 I   .119963  .003538   .474902  .003434  I  .1296198  .0002732  1.5918 0.1501  P    21.196     .500    -4.906     .300   .106000   .485000   .1307000      .000      .000  
+77 712 43336.00 I   .124338  .008811   .472259  .004510  I  .1280705  .0002142  1.5118 0.1736  P    20.794     .500    -4.894     .300   .111000   .483000   .1289000      .000      .000  
+77 713 43337.00 I   .128654  .008811   .469581  .004510  I  .1265848  .0002142  1.4661 0.1515  P    20.574     .500    -4.922     .300   .115000   .482000   .1270000      .000      .000  
+77 714 43338.00 I   .132889  .008811   .466887  .004510  I  .1251247  .0002142  1.4613 0.1256  P    20.569     .500    -4.938     .300   .120000   .480000   .1252000      .000      .000  
+77 715 43339.00 I   .137022  .008773   .464193  .007298  I  .1236482  .0001313  1.4986 0.1256  P    20.660     .500    -4.885     .300   .124000   .478000   .1233000      .000      .000  
+77 716 43340.00 I   .141046  .008773   .461511  .007298  I  .1221143  .0001313  1.5754 0.0928  P    20.764     .500    -4.786     .300   .128000   .476000   .1215000      .000      .000  
+77 717 43341.00 I   .144961  .008773   .458851  .007298  I  .1204865  .0001313  1.6849 0.2357  P    20.810     .500    -4.717     .300   .132000   .474000   .1196000      .000      .000  
+77 718 43342.00 I   .148769  .002495   .456223  .006823  I  .1187374  .0004528  1.8158 0.2357  P    20.686     .500    -4.720     .300   .137000   .472000   .1178000      .000      .000  
+77 719 43343.00 I   .152475  .002495   .453630  .006823  I  .1168530  .0004528  1.9523 0.2925  P    20.370     .500    -4.765     .300   .141000   .470000   .1159000      .000      .000  
+77 720 43344.00 I   .156086  .003354   .451069  .005733  I  .1148374  .0003704  2.0750 0.3202  P    20.040     .500    -4.791     .300   .145000   .468000   .1141000      .000      .000  
+77 721 43345.00 I   .159611  .003359   .448538  .003239  I  .1127143  .0004529  2.1639 0.2913  P    19.913     .500    -4.744     .300   .149000   .466000   .1123000      .000      .000  
+77 722 43346.00 I   .163059  .003359   .446031  .003239  I  .1105254  .0004496  2.2053 0.3086  P    19.993     .500    -4.607     .300   .153000   .464000   .1105000      .000      .000  
+77 723 43347.00 I   .166440  .003359   .443541  .003239  I  .1083208  .0004192  2.1955 0.2518  P    20.061     .500    -4.408     .300   .157000   .461000   .1086000      .000      .000  
+77 724 43348.00 I   .169759  .004615   .441062  .002345  I  .1061490  .0002267  2.1419 0.2383  P    19.897     .500    -4.232     .300   .161000   .459000   .1068000      .000      .000  
+77 725 43349.00 I   .173020  .004615   .438590  .002345  I  .1040455  .0002267  2.0626 0.1999  P    19.477     .500    -4.202     .300   .165000   .457000   .1050000      .000      .000  
+77 726 43350.00 I   .176223  .003310   .436122  .002690  I  .1020235  .0003293  1.9840 0.2457  P    18.967     .500    -4.397     .300   .169000   .454000   .1031000      .000      .000  
+77 727 43351.00 I   .179368  .001299   .433652  .005142  I  .1000677  .0004360  1.9348 0.2734  P    18.552     .500    -4.749     .300   .172000   .452000   .1012000      .000      .000  
+77 728 43352.00 I   .182457  .001299   .431178  .005142  I  .0981372  .0004365  1.9359 0.3851  P    18.297     .500    -5.033     .300   .176000   .449000   .0994000      .000      .000  
+77 729 43353.00 I   .185491  .001299   .428696  .005142  I  .0961782  .0006348  1.9903 0.3584  P    18.170     .500    -5.041     .300   .179000   .447000   .0975000      .000      .000  
+77 730 43354.00 I   .188469  .001873   .426202  .003945  I  .0941448  .0005685  2.0800 0.4211  P    18.158     .500    -4.792     .300   .183000   .444000   .0956000      .000      .000  
+77 731 43355.00 I   .191393  .001873   .423697  .003945  I  .0920173  .0005536  2.1723 0.3968  P    18.280     .500    -4.525     .300   .186000   .441000   .0937000      .000      .000  
+77 8 1 43356.00 I   .194260  .001873   .421184  .003945  I  .0898110  .0005536  2.2323 0.3686  P    18.439     .500    -4.453     .300   .190000   .439000   .0918000      .000      .000  
+77 8 2 43357.00 I   .197068  .002767   .418666  .004987  I  .0875704  .0004869  2.2392 0.3686  P    18.387     .500    -4.562     .300   .193000   .436000   .0900000      .000      .000  
+77 8 3 43358.00 I   .199812  .002767   .416148  .004987  I  .0853505  .0004869  2.1925 0.3161  P    17.984     .500    -4.682     .300   .197000   .434000   .0881000      .000      .000  
+77 8 4 43359.00 I   .202487  .002767   .413635  .004987  I  .0831987  .0004033  2.1058 0.2811  P    17.416     .500    -4.714     .300   .200000   .431000   .0862000      .000      .000  
+77 8 5 43360.00 I   .205086  .002292   .411134  .005120  I  .0811463  .0002812  1.9968 0.2324  P    17.021     .500    -4.698     .300   .203000   .428000   .0843000      .000      .000  
+77 8 6 43361.00 I   .207607  .002292   .408645  .005120  I  .0792069  .0002312  1.8822 0.1820  P    16.907     .500    -4.707     .300   .207000   .425000   .0824000      .000      .000  
+77 8 7 43362.00 I   .210050  .002292   .406167  .005120  I  .0773793  .0002312  1.7753 0.2785  P    16.893     .500    -4.744     .300   .210000   .422000   .0804000      .000      .000  
+77 8 8 43363.00 I   .212415  .007256   .403695  .002685  I  .0756495  .0005068  1.6887 0.2785  P    16.806     .500    -4.770     .300   .214000   .419000   .0785000      .000      .000  
+77 8 9 43364.00 I   .214700  .007256   .401224  .002685  I  .0739912  .0005068  1.6342 0.3584  P    16.673     .500    -4.762     .300   .217000   .416000   .0766000      .000      .000  
+77 810 43365.00 I   .216906  .007256   .398749  .002685  I  .0723674  .0005068  1.6207 0.4170  P    16.589     .500    -4.724     .300   .220000   .413000   .0746000      .000      .000  
+77 811 43366.00 I   .219033  .010236   .396262  .002654  I  .0707352  .0006624  1.6508 0.3794  P    16.550     .500    -4.672     .300   .223000   .410000   .0726000      .000      .000  
+77 812 43367.00 I   .221096  .009367   .393754  .005785  I  .0690521  .0005648  1.7219 0.4353  P    16.522     .500    -4.641     .300   .227000   .406000   .0705000      .000      .000  
+77 813 43368.00 I   .223113  .009367   .391214  .005785  I  .0672798  .0005648  1.8280 0.3599  P    16.551     .500    -4.654     .300   .230000   .403000   .0685000      .000      .000  
+77 814 43369.00 I   .225102  .008408   .388632  .007738  I  .0653877  .0004463  1.9595 0.3599  P    16.664     .500    -4.681     .300   .233000   .400000   .0665000      .000      .000  
+77 815 43370.00 I   .227082  .008408   .385999  .007738  I  .0633572  .0004463  2.1017 0.3156  P    16.737     .500    -4.656     .300   .236000   .397000   .0644000      .000      .000  
+77 816 43371.00 I   .229062  .008408   .383309  .007738  I  .0611873  .0004463  2.2348 0.3156  P    16.599     .500    -4.555     .300   .239000   .393000   .0622000      .000      .000  
+77 817 43372.00 I   .231045  .008408   .380563  .007738  I  .0588980  .0004463  2.3365 0.2611  P    16.261     .500    -4.439     .300   .241000   .390000   .0601000      .000      .000  
+77 818 43373.00 I   .233032  .003310   .377760  .002690  I  .0565309  .0002710  2.3882 0.2611  P    15.942     .500    -4.372     .300   .244000   .386000   .0579000      .000      .000  
+77 819 43374.00 I   .235027  .003310   .374900  .002690  I  .0541403  .0002710  2.3837 0.1916  P    15.845     .500    -4.351     .300   .247000   .383000   .0558000      .000      .000  
+77 820 43375.00 I   .237031  .003310   .371983  .002690  I  .0517795  .0002710  2.3314 0.1750  P    15.961     .500    -4.328     .300   .250000   .380000   .0536000      .000      .000  
+77 821 43376.00 I   .239047  .009904   .369011  .003391  I  .0494863  .0002215  2.2529 0.1750  P    16.094     .500    -4.298     .300   .252000   .377000   .0514000      .000      .000  
+77 822 43377.00 I   .241077  .009904   .365981  .003391  I  .0472730  .0002215  2.1765 0.1566  P    16.061     .500    -4.324     .300   .255000   .373000   .0491000      .000      .000  
+77 823 43378.00 I   .243123  .009904   .362896  .003391  I  .0451234  .0002215  2.1298 0.1566  P    15.837     .500    -4.470     .300   .257000   .370000   .0469000      .000      .000  
+77 824 43379.00 I   .245184  .009904   .359754  .003391  I  .0429967  .0002215  2.1332 0.1566  P    15.536     .500    -4.713     .300   .260000   .367000   .0447000      .000      .000  
+77 825 43380.00 I   .247249  .009904   .356560  .003391  I  .0408378  .0002215  2.1942 0.1566  P    15.302     .500    -4.922     .300   .262000   .364000   .0423000      .000      .000  
+77 826 43381.00 I   .249300  .009904   .353319  .003391  I  .0385923  .0002215  2.3033 0.2772  P    15.206     .500    -4.939     .300   .265000   .360000   .0400000      .000      .000  
+77 827 43382.00 I   .251321  .003466   .350034  .002468  I  .0362236  .0005082  2.4353 0.2772  P    15.240     .500    -4.726     .300   .267000   .357000   .0376000      .000      .000  
+77 828 43383.00 I   .253295  .003466   .346710  .002468  I  .0337251  .0005082  2.5571 0.3594  P    15.354     .500    -4.423     .300   .270000   .353000   .0353000      .000      .000  
+77 829 43384.00 I   .255206  .003466   .343352  .002468  I  .0311225  .0005082  2.6394 0.3594  P    15.465     .500    -4.240     .300   .272000   .350000   .0329000      .000      .000  
+77 830 43385.00 I   .257037  .003466   .339965  .002468  I  .0284644  .0005082  2.6673 0.3594  P    15.470     .500    -4.258     .300   .274000   .346000   .0304000      .000      .000  
+77 831 43386.00 I   .258773  .003466   .336553  .002468  I  .0258056  .0005082  2.6422 0.4092  P    15.298     .500    -4.371     .300   .276000   .343000   .0279000      .000      .000  
+77 9 1 43387.00 I   .260403  .003466   .333124  .002468  I  .0231927  .0006414  2.5791 0.3341  P    14.974     .500    -4.433     .300   .278000   .339000   .0254000      .000      .000  
+77 9 2 43388.00 I   .261918  .010931   .329686  .016021  I  .0206533  .0004339  2.4984 0.3872  P    14.625     .500    -4.409     .300   .280000   .336000   .0229000      .000      .000  
+77 9 3 43389.00 I   .263308  .010931   .326247  .016021  I  .0181958  .0004339  2.4181 0.3068  P    14.398     .500    -4.377     .300   .282000   .332000   .0204000      .000      .000  
+77 9 4 43390.00 I   .264561  .010931   .322814  .016021  I  .0158123  .0004339  2.3522 0.3068  P    14.379     .500    -4.401     .300   .283000   .329000   .0177000      .000      .000  
+77 9 5 43391.00 I   .265669  .010931   .319396  .016021  I  .0134830  .0004339  2.3115 0.2987  P    14.547     .500    -4.468     .300   .285000   .325000   .0150000      .000      .000  
+77 9 6 43392.00 I   .266628  .010931   .315994  .016021  I  .0111780  .0004106  2.3048 0.2338  P    14.777     .500    -4.517     .300   .286000   .322000   .0123000      .000      .000  
+77 9 7 43393.00 I   .267445  .010931   .312596  .016021  I  .0088600  .0001741  2.3383 0.2515  P    14.924     .500    -4.507     .300   .288000   .318000   .0096000      .000      .000  
+77 9 8 43394.00 I   .268133  .003310   .309189  .002690  I  .0064876  .0002905  2.4132 0.1401  P    14.926     .500    -4.459     .300   .289000   .315000   .0069000      .000      .000  
+77 9 9 43395.00 I   .268701  .005556   .305760  .015372  I  .0040210  .0002195  2.5258 0.1821  P    14.854     .500    -4.448     .300   .290000   .311000   .0041000      .000      .000  
+77 910 43396.00 I   .269160  .005556   .302295  .015372  I  .0014261  .0002195  2.6682 0.1552  P    14.824     .500    -4.529     .300   .291000   .308000   .0013000      .000      .000  
+77 911 43397.00 I   .269522  .005556   .298782  .015372  I -.0013213  .0002195  2.8284 0.1419  P    14.877     .500    -4.641     .300   .292000   .304000  -.0015000      .000      .000  
+77 912 43398.00 I   .269796  .005556   .295208  .015372  I -.0042306  .0001799  2.9881 0.1419  P    14.937     .500    -4.634     .300   .293000   .301000  -.0043000      .000      .000  
+77 913 43399.00 I   .269986  .005556   .291573  .015372  I -.0072892  .0001799  3.1227 0.1272  P    14.900     .500    -4.427     .300   .294000   .297000  -.0071000      .000      .000  
+77 914 43400.00 I   .270092  .005556   .287884  .015372  I -.0104594  .0001799  3.2074 0.1626  P    14.738     .500    -4.120     .300   .295000   .293000  -.0100000      .000      .000  
+77 915 43401.00 I   .270114  .003310   .284148  .002690  I -.0136818  .0002710  3.2254 0.1626  P    14.527     .500    -3.911     .300   .296000   .289000  -.0129000      .000      .000  
+77 916 43402.00 I   .270054  .003310   .280373  .002690  I -.0168882  .0002710  3.1773 0.1916  P    14.387     .500    -3.895     .300   .296000   .286000  -.0157000      .000      .000  
+77 917 43403.00 I   .269909  .003310   .276564  .002690  I -.0200210  .0002710  3.0829 0.3706  P    14.390     .500    -3.997     .300   .297000   .282000  -.0186000      .000      .000  
+77 918 43404.00 I   .269682  .002806   .272729  .006509  I -.0230495  .0006899  2.9749 0.3706  P    14.505     .500    -4.089     .300   .298000   .278000  -.0215000      .000      .000  
+77 919 43405.00 I   .269372  .002806   .268874  .006509  I -.0259776  .0006899  2.8872 0.4878  P    14.640     .500    -4.125     .300   .298000   .274000  -.0244000      .000      .000  
+77 920 43406.00 I   .268979  .002806   .265007  .006509  I -.0288387  .0006899  2.8439 0.4878  P    14.717     .500    -4.148     .300   .298000   .271000  -.0274000      .000      .000  
+77 921 43407.00 I   .268504  .002806   .261134  .006509  I -.0316835  .0006899  2.8549 0.4878  P    14.737     .500    -4.204     .300   .299000   .267000  -.0303000      .000      .000  
+77 922 43408.00 I   .267952  .002806   .257254  .006509  I -.0345648  .0006899  2.9148 0.4309  P    14.764     .500    -4.273     .300   .299000   .264000  -.0333000      .000      .000  
+77 923 43409.00 I   .267328  .004994   .253366  .006890  I -.0375231  .0005166  3.0046 0.3653  P    14.858     .500    -4.277     .300   .299000   .260000  -.0362000      .000      .000  
+77 924 43410.00 I   .266637  .006481   .249467  .007251  I -.0405750  .0002405  3.0974 0.2849  P    15.015     .500    -4.160     .300   .299000   .256000  -.0392000      .000      .000  
+77 925 43411.00 I   .265886  .006481   .245557  .007251  I -.0437097  .0002405  3.1657 0.1701  P    15.153     .500    -3.956     .300   .299000   .252000  -.0422000      .000      .000  
+77 926 43412.00 I   .265079  .006481   .241635  .007251  I -.0468915  .0002405  3.1892 0.1701  P    15.196     .500    -3.779     .300   .299000   .249000  -.0451000      .000      .000  
+77 927 43413.00 I   .264214  .006481   .237704  .007251  I -.0500708  .0002405  3.1608 0.2556  P    15.146     .500    -3.714     .300   .299000   .245000  -.0481000      .000      .000  
+77 928 43414.00 I   .263284  .005080   .233771  .005733  I -.0531981  .0004510  3.0874 0.3731  P    15.070     .500    -3.741     .300   .299000   .241000  -.0511000      .000      .000  
+77 929 43415.00 I   .262283  .003100   .229846  .003625  I -.0562362  .0007063  2.9857 0.3685  P    14.988     .500    -3.771     .300   .298000   .237000  -.0541000      .000      .000  
+77 930 43416.00 I   .261204  .008030   .225936  .007837  I -.0591666  .0005828  2.8753 0.4579  P    14.877     .500    -3.768     .300   .298000   .233000  -.0571000      .000      .000  
+7710 1 43417.00 I   .260040  .008030   .222048  .007837  I -.0619897  .0005828  2.7735 0.4121  P    14.800     .500    -3.768     .300   .297000   .230000  -.0601000      .000      .000  
+7710 2 43418.00 I   .258791  .008030   .218186  .007837  I -.0647210  .0005828  2.6939 0.4121  P    14.924     .500    -3.819     .300   .297000   .226000  -.0631000      .000      .000  
+7710 3 43419.00 I   .257454  .008030   .214353  .007837  I -.0673890  .0005828  2.6487 0.3989  P    15.321     .500    -3.911     .300   .296000   .222000  -.0661000      .000      .000  
+7710 4 43420.00 I   .256033  .007860   .210549  .007426  I -.0700321  .0005447  2.6447 0.3364  P    15.803     .500    -3.993     .300   .295000   .218000  -.0692000      .000      .000  
+7710 5 43421.00 I   .254546  .007860   .206766  .007426  I -.0726932  .0003362  2.6851 0.3469  P    16.099     .500    -4.018     .300   .294000   .214000  -.0723000      .000      .000  
+7710 6 43422.00 I   .253012  .002056   .202993  .000778  I -.0754167  .0004298  2.7688 0.5658  P    16.159     .500    -3.975     .300   .293000   .211000  -.0755000      .000      .000  
+7710 7 43423.00 I   .251451  .012307   .199220  .007154  I -.0782432  .0010804  2.8899 0.5814  P    16.166     .500    -3.913     .300   .292000   .207000  -.0786000      .000      .000  
+7710 8 43424.00 I   .249883  .012307   .195435  .007154  I -.0812060  .0010804  3.0397 0.7640  P    16.242     .500    -3.911     .300   .291000   .203000  -.0817000      .000      .000  
+7710 9 43425.00 I   .248334  .012307   .191630  .007154  I -.0843280  .0010804  3.2057 0.9106  P    16.310     .500    -3.978     .300   .289000   .199000  -.0849000      .000      .000  
+771010 43426.00 I   .246824  .017283   .187794  .010087  I -.0876157  .0014662  3.3664 0.7496  P    16.275     .500    -3.990     .300   .288000   .195000  -.0881000      .000      .000  
+771011 43427.00 I   .245357  .012486   .183928  .007458  I -.0910496  .0010395  3.4933 0.8987  P    16.203     .500    -3.803     .300   .286000   .192000  -.0912000      .000      .000  
+771012 43428.00 I   .243924  .012486   .180034  .007458  I -.0945828  .0010395  3.5615 0.5225  P    16.224     .500    -3.445     .300   .285000   .188000  -.0944000      .000      .000  
+771013 43429.00 I   .242517  .003619   .176116  .003080  I -.0981487  .0001064  3.5579 0.5225  P    16.342     .500    -3.139     .300   .283000   .184000  -.0976000      .000      .000  
+771014 43430.00 I   .241125  .003619   .172177  .003080  I -.1016771  .0001064  3.4897 0.1951  P    16.450     .500    -3.076     .300   .281000   .180000  -.1009000      .000      .000  
+771015 43431.00 I   .239742  .010825   .168223  .004041  I -.1051158  .0003754  3.3848 0.1951  P    16.462     .500    -3.218     .300   .279000   .177000  -.1041000      .000      .000  
+771016 43432.00 I   .238365  .010825   .164261  .004041  I -.1084474  .0003754  3.2822 0.3207  P    16.392     .500    -3.368     .300   .277000   .173000  -.1074000      .000      .000  
+771017 43433.00 I   .236992  .014874   .160299  .004814  I -.1116924  .0005201  3.2161 0.3207  P    16.318     .500    -3.396     .300   .275000   .170000  -.1106000      .000      .000  
+771018 43434.00 I   .235622  .014874   .156343  .004814  I -.1148973  .0005201  3.2030 0.3201  P    16.332     .500    -3.337     .300   .273000   .166000  -.1139000      .000      .000  
+771019 43435.00 I   .234244  .011193   .152405  .005267  I -.1181151  .0003732  3.2401 0.3201  P    16.487     .500    -3.282     .300   .271000   .163000  -.1172000      .000      .000  
+771020 43436.00 I   .232834  .011193   .148495  .005267  I -.1213882  .0003732  3.3094 0.1920  P    16.752     .500    -3.254     .300   .268000   .159000  -.1204000      .000      .000  
+771021 43437.00 I   .231360  .005414   .144629  .005683  I -.1247362  .0000904  3.3857 0.2110  P    17.032     .500    -3.211     .300   .266000   .156000  -.1237000      .000      .000  
+771022 43438.00 I   .229791  .004175   .140821  .004043  I -.1281541  .0001971  3.4456 0.1084  P    17.238     .500    -3.125     .300   .263000   .152000  -.1269000      .000      .000  
+771023 43439.00 I   .228100  .004175   .137082  .004043  I -.1316161  .0001971  3.4715 0.1330  P    17.321     .500    -3.018     .300   .261000   .149000  -.1302000      .000      .000  
+771024 43440.00 I   .226268  .004175   .133418  .004043  I -.1350826  .0001786  3.4538 0.1331  P    17.276     .500    -2.930     .300   .258000   .146000  -.1334000      .000      .000  
+771025 43441.00 I   .224273  .009785   .129837  .000726  I -.1385092  .0001789  3.3924 0.1264  P    17.170     .500    -2.864     .300   .256000   .142000  -.1367000      .000      .000  
+771026 43442.00 I   .222098  .009785   .126344  .000726  I -.1418558  .0001789  3.2961 0.1265  P    17.123     .500    -2.800     .300   .253000   .139000  -.1399000      .000      .000  
+771027 43443.00 I   .219726  .009785   .122945  .000726  I -.1450950  .0001789  3.1806 0.0974  P    17.199     .500    -2.729     .300   .251000   .135000  -.1432000      .000      .000  
+771028 43444.00 I   .217146  .013637   .119647  .000809  I -.1482166  .0000769  3.0638 0.2663  P    17.342     .500    -2.681     .300   .248000   .132000  -.1464000      .000      .000  
+771029 43445.00 I   .214360  .016366   .116453  .007386  I -.1512272  .0005016  2.9609 0.2751  P    17.510     .500    -2.686     .300   .245000   .129000  -.1496000      .000      .000  
+771030 43446.00 I   .211380  .016366   .113368  .007386  I -.1541470  .0005447  2.8838 0.4327  P    17.780     .500    -2.737     .300   .242000   .126000  -.1528000      .000      .000  
+771031 43447.00 I   .208216  .018702   .110397  .010414  I -.1570059  .0007052  2.8401 0.4455  P    18.214     .500    -2.798     .300   .240000   .122000  -.1559000      .000      .000  
+7711 1 43448.00 I   .204878  .018702   .107544  .010414  I -.1598398  .0007052  2.8343 0.4987  P    18.658     .500    -2.850     .300   .237000   .119000  -.1591000      .000      .000  
+7711 2 43449.00 I   .201393  .018702   .104809  .010414  I -.1626876  .0007052  2.8678 0.4598  P    18.882     .500    -2.875     .300   .234000   .116000  -.1623000      .000      .000  
+7711 3 43450.00 I   .197804  .017105   .102181  .007368  I -.1655874  .0005903  2.9373 0.3975  P    18.912     .500    -2.837     .300   .231000   .113000  -.1654000      .000      .000  
+7711 4 43451.00 I   .194157  .015343   .099653  .000364  I -.1685717  .0003669  3.0353 0.3475  P    19.006     .500    -2.712     .300   .228000   .110000  -.1685000      .000      .000  
+7711 5 43452.00 I   .190500  .015343   .097216  .000364  I -.1716640  .0003669  3.1513 0.2594  P    19.255     .500    -2.559     .300   .226000   .108000  -.1716000      .000      .000  
+7711 6 43453.00 I   .186872  .015343   .094859  .000364  I -.1748758  .0003669  3.2718 0.2249  P    19.438     .500    -2.478     .300   .223000   .105000  -.1747000      .000      .000  
+7711 7 43454.00 I   .183296  .015307   .092575  .000364  I -.1782032  .0002603  3.3790 0.2249  P    19.362     .500    -2.456     .300   .220000   .102000  -.1778000      .000      .000  
+7711 8 43455.00 I   .179789  .015307   .090356  .000364  I -.1816217  .0002603  3.4500 0.1310  P    19.183     .500    -2.354     .300   .217000   .099000  -.1809000      .000      .000  
+7711 9 43456.00 I   .176367  .015270   .088191  .002690  I -.1850845  .0000303  3.4648 0.1310  P    19.208     .500    -2.099     .300   .214000   .097000  -.1840000      .000      .000  
+771110 43457.00 I   .173044  .015270   .086073  .002690  I -.1885299  .0000303  3.4154 0.0214  P    19.508     .500    -1.837     .300   .212000   .094000  -.1870000      .000      .000  
+771111 43458.00 I   .169819  .015270   .083993  .002690  I -.1918985  .0000303  3.3154 0.0214  P    19.863     .500    -1.785     .300   .209000   .092000  -.1901000      .000      .000  
+771112 43459.00 I   .166678  .015270   .081943  .002690  I -.1951549  .0000303  3.1978 0.1363  P    20.019     .500    -1.953     .300   .206000   .089000  -.1932000      .000      .000  
+771113 43460.00 I   .163610  .003310   .079914  .002690  I -.1983014  .0002710  3.1021 0.1394  P    19.895     .500    -2.131     .300   .203000   .087000  -.1963000      .000      .000  
+771114 43461.00 I   .160601  .001793   .077898  .009681  I -.2013761  .0002771  3.0573 0.1938  P    19.608     .500    -2.148     .300   .200000   .084000  -.1994000      .000      .000  
+771115 43462.00 I   .157639  .001793   .075885  .009681  I -.2044350  .0002771  3.0694 0.1959  P    19.395     .500    -2.056     .300   .198000   .082000  -.2024000      .000      .000  
+771116 43463.00 I   .154712  .001793   .073867  .009681  I -.2075283  .0002771  3.1217 0.1744  P    19.442     .500    -1.988     .300   .195000   .079000  -.2055000      .000      .000  
+771117 43464.00 I   .151806  .001272   .071837  .006865  I -.2106822  .0002117  3.1855 0.1744  P    19.708     .500    -1.958     .300   .192000   .077000  -.2086000      .000      .000  
+771118 43465.00 I   .148908  .001272   .069796  .006865  I -.2138939  .0002117  3.2333 0.1436  P    19.954     .500    -1.884     .300   .189000   .075000  -.2117000      .000      .000  
+771119 43466.00 I   .146001  .001272   .067748  .006865  I -.2171378  .0001942  3.2482 0.1176  P    20.005     .500    -1.753     .300   .186000   .073000  -.2148000      .000      .000  
+771120 43467.00 I   .143069  .000144   .065696  .000735  I -.2203774  .0001026  3.2243 0.1361  P    19.906     .500    -1.659     .300   .184000   .070000  -.2178000      .000      .000  
+771121 43468.00 I   .140096  .012232   .063645  .004452  I -.2235741  .0001907  3.1634 0.1083  P    19.796     .500    -1.657     .300   .181000   .068000  -.2209000      .000      .000  
+771122 43469.00 I   .137068  .012232   .061601  .004452  I -.2266946  .0001907  3.0736 0.1460  P    19.743     .500    -1.679     .300   .178000   .066000  -.2240000      .000      .000  
+771123 43470.00 I   .133967  .017298   .059568  .006253  I -.2297155  .0002211  2.9663 0.1613  P    19.756     .500    -1.626     .300   .175000   .064000  -.2271000      .000      .000  
+771124 43471.00 I   .130783  .012959   .057551  .004867  I -.2326260  .0002603  2.8554 0.1776  P    19.850     .500    -1.493     .300   .172000   .062000  -.2302000      .000      .000  
+771125 43472.00 I   .127525  .012959   .055548  .004867  I -.2354300  .0002780  2.7556 0.1904  P    20.022     .500    -1.374     .300   .169000   .060000  -.2332000      .000      .000  
+771126 43473.00 I   .124210  .012959   .053556  .004867  I -.2381449  .0002780  2.6789 0.2060  P    20.229     .500    -1.331     .300   .166000   .058000  -.2363000      .000      .000  
+771127 43474.00 I   .120857  .006052   .051573  .002877  I -.2407984  .0003040  2.6340 0.1849  P    20.458     .500    -1.338     .300   .163000   .056000  -.2394000      .000      .000  
+771128 43475.00 I   .117476  .005817   .049596  .002041  I -.2434254  .0002440  2.6266 0.1949  P    20.722     .500    -1.348     .300   .160000   .054000  -.2424000      .000      .000  
+771129 43476.00 I   .114076  .005817   .047626  .002041  I -.2460646  .0002440  2.6583 0.1340  P    20.952     .500    -1.369     .300   .157000   .052000  -.2455000      .000      .000  
+771130 43477.00 I   .110660  .005571   .045664  .000242  I -.2487541  .0001108  2.7264 0.1340  P    21.035     .500    -1.426     .300   .153000   .051000  -.2485000      .000      .000  
+7712 1 43478.00 I   .107236  .005571   .043711  .000242  I -.2515275  .0001108  2.8249 0.0783  P    21.023     .500    -1.466     .300   .150000   .049000  -.2516000      .000      .000  
+7712 2 43479.00 I   .103810  .005571   .041766  .000242  I -.2544109  .0001108  2.9445 0.1077  P    21.134     .500    -1.387     .300   .147000   .047000  -.2546000      .000      .000  
+7712 3 43480.00 I   .100399  .005206   .039832  .006273  I -.2574196  .0001848  3.0734 0.1306  P    21.426     .500    -1.184     .300   .143000   .045000  -.2577000      .000      .000  
+7712 4 43481.00 I   .097018  .004815   .037907  .008868  I -.2605561  .0002366  3.1976 0.1501  P    21.652     .500     -.985     .300   .140000   .043000  -.2608000      .000      .000  
+7712 5 43482.00 I   .093685  .004815   .035992  .008868  I -.2638080  .0002366  3.3013 0.2804  P    21.575     .500     -.889     .300   .136000   .042000  -.2640000      .000      .000  
+7712 6 43483.00 I   .090413  .003622   .034089  .006659  I -.2671458  .0005084  3.3664 0.2804  P    21.310     .500     -.833     .300   .133000   .040000  -.2671000      .000      .000  
+7712 7 43484.00 I   .087214  .003622   .032206  .006659  I -.2705236  .0005084  3.3798 0.4687  P    21.178     .500     -.707     .300   .129000   .038000  -.2702000      .000      .000  
+7712 8 43485.00 I   .084094  .003411   .030357  .005560  I -.2738882  .0007876  3.3414 0.5387  P    21.308     .500     -.569     .300   .125000   .036000  -.2734000      .000      .000  
+7712 9 43486.00 I   .081061  .002421   .028552  .002656  I -.2771957  .0009500  3.2709 0.6170  P    21.537     .500     -.595     .300   .121000   .035000  -.2766000      .000      .000  
+771210 43487.00 I   .078120  .002421   .026806  .002656  I -.2804311  .0009500  3.2038 0.6135  P    21.638     .500     -.809     .300   .117000   .033000  -.2797000      .000      .000  
+771211 43488.00 I   .075273  .002604   .025137  .007517  I -.2836161  .0007764  3.1752 0.6277  P    21.523     .500    -1.002     .300   .113000   .032000  -.2829000      .000      .000  
+771212 43489.00 I   .072525  .002940   .023559  .008929  I -.2867999  .0008208  3.2018 0.5649  P    21.276     .500     -.988     .300   .109000   .030000  -.2861000      .000      .000  
+771213 43490.00 I   .069874  .002940   .022086  .008929  I -.2900354  .0008208  3.2754 0.4114  P    21.109     .500     -.838     .300   .105000   .029000  -.2893000      .000      .000  
+771214 43491.00 I   .067317  .002937   .020731  .012466  I -.2933573  .0000587  3.3686 0.4110  P    21.205     .500     -.746     .300   .101000   .028000  -.2925000      .000      .000  
+771215 43492.00 I   .064853  .010476   .019504  .009938  I -.2967677  .0000428  3.4469 0.0363  P    21.494     .500     -.746     .300   .096000   .026000  -.2957000      .000      .000  
+771216 43493.00 I   .062474  .010476   .018405  .009938  I -.3002377  .0000428  3.4849 0.0226  P    21.663     .500     -.703     .300   .092000   .025000  -.2989000      .000      .000  
+771217 43494.00 I   .060173  .014522   .017429  .006491  I -.3037211  .0000143  3.4736 0.0226  P    21.518     .500     -.559     .300   .088000   .024000  -.3021000      .000      .000  
+771218 43495.00 I   .057942  .014522   .016575  .006491  I -.3071703  .0000143  3.4184 0.0101  P    21.229     .500     -.443     .300   .084000   .023000  -.3053000      .000      .000  
+771219 43496.00 I   .055757  .014522   .015844  .006491  I -.3105474  .0000143  3.3318 0.0101  P    21.094     .500     -.480     .300   .080000   .022000  -.3086000      .000      .000  
+771220 43497.00 I   .053585  .014522   .015243  .006491  I -.3138279  .0000143  3.2274 0.0839  P    21.163     .500     -.610     .300   .075000   .021000  -.3118000      .000      .000  
+771221 43498.00 I   .051390  .021222   .014776  .011623  I -.3170006  .0001672  3.1183 0.0839  P    21.256     .500     -.672     .300   .071000   .020000  -.3151000      .000      .000  
+771222 43499.00 I   .049138  .021222   .014450  .011623  I -.3200671  .0001672  3.0171 0.1182  P    21.280     .500     -.607     .300   .067000   .019000  -.3183000      .000      .000  
+771223 43500.00 I   .046792  .021222   .014270  .011623  I -.3230414  .0001672  2.9357 0.1182  P    21.309     .500     -.502     .300   .063000   .018000  -.3215000      .000      .000  
+771224 43501.00 I   .044321  .021222   .014241  .011623  I -.3259482  .0001672  2.8833 0.1182  P    21.402     .500     -.441     .300   .058000   .018000  -.3248000      .000      .000  
+771225 43502.00 I   .041712  .021222   .014360  .011623  I -.3288195  .0001672  2.8654 0.1182  P    21.527     .500     -.417     .300   .054000   .017000  -.3280000      .000      .000  
+771226 43503.00 I   .038970  .021222   .014618  .011623  I -.3316910  .0001672  2.8834 0.1592  P    21.649     .500     -.399     .300   .049000   .017000  -.3313000      .000      .000  
+771227 43504.00 I   .036101  .003310   .015005  .002690  I -.3345972  .0002710  2.9342 0.1592  P    21.769     .500     -.424     .300   .045000   .016000  -.3345000      .000      .000  
+771228 43505.00 I   .033110  .003310   .015512  .002690  I -.3375686  .0002710  3.0125 0.1916  P    21.867     .500     -.535     .300   .041000   .016000  -.3377000      .000      .000  
+771229 43506.00 I   .030003  .003310   .016128  .002690  I -.3406283  .0002710  3.1092 0.1916  P    21.936     .500     -.679     .300   .036000   .016000  -.3410000      .000      .000  
+771230 43507.00 I   .026786  .003310   .016844  .002690  I -.3437893  .0002710  3.2129 0.1916  P    22.033     .500     -.718     .300   .032000   .015000  -.3442000      .000      .000  
+771231 43508.00 I   .023463  .003310   .017651  .002690  I -.3470520  .0002710  3.3102 0.1916  P    22.183     .500     -.586     .300   .027000   .015000  -.3475000      .000      .000  
+78 1 1 43509.00 I   .020040  .003310   .018540  .002690  I  .6495968  .0002710  3.3879 0.1916  P    22.271     .500     -.376     .300   .023000   .015000   .6493000      .000      .000  
+78 1 2 43510.00 I   .016522  .003310   .019499  .002690  I  .6461826  .0002710  3.4342 0.1916  P    22.160     .500     -.223     .300   .019000   .015000   .6461000      .000      .000  
+78 1 3 43511.00 I   .012916  .003310   .020520  .002690  I  .6427420  .0002710  3.4396 0.1916  P    21.900     .500     -.148     .300   .015000   .015000   .6429000      .000      .000  
+78 1 4 43512.00 I   .009227  .003310   .021594  .002690  I  .6393177  .0002710  3.4021 0.1916  P    21.668     .500     -.089     .300   .011000   .015000   .6398000      .000      .000  
+78 1 5 43513.00 I   .005460  .003310   .022710  .002690  I  .6359491  .0002710  3.3308 0.1916  P    21.535     .500     -.063     .300   .007000   .015000   .6366000      .000      .000  
+78 1 6 43514.00 I   .001620  .003310   .023858  .002690  I  .6326596  .0002710  3.2488 0.3493  P    21.412     .500     -.176     .300   .003000   .015000   .6334000      .000      .000  
+78 1 7 43515.00 I  -.002286  .000685   .025031  .007140  I  .6294446  .0006439  3.1873 0.3493  P    21.213     .500     -.431     .300  -.001000   .016000   .6303000      .000      .000  
+78 1 8 43516.00 I  -.006254  .000685   .026216  .007140  I  .6262694  .0006439  3.1726 0.4553  P    20.960     .500     -.624     .300  -.005000   .016000   .6271000      .000      .000  
+78 1 9 43517.00 I  -.010278  .000685   .027406  .007140  I  .6230813  .0006439  3.2121 0.4553  P    20.763     .500     -.565     .300  -.008000   .017000   .6240000      .000      .000  
+78 110 43518.00 I  -.014352  .000685   .028591  .007140  I  .6198327  .0006439  3.2892 0.4553  P    20.769     .500     -.324     .300  -.012000   .017000   .6208000      .000      .000  
+78 111 43519.00 I  -.018472  .000685   .029769  .007140  I  .6165018  .0006439  3.3698 0.4553  P    21.062     .500     -.155     .300  -.016000   .018000   .6177000      .000      .000  
+78 112 43520.00 I  -.022634  .000685   .030938  .007140  I  .6131041  .0006439  3.4175 0.3493  P    21.495     .500     -.169     .300  -.020000   .019000   .6146000      .000      .000  
+78 113 43521.00 I  -.026834  .003310   .032100  .002690  I  .6096852  .0002710  3.4101 0.3493  P    21.719     .500     -.232     .300  -.024000   .020000   .6115000      .000      .000  
+78 114 43522.00 I  -.031067  .003310   .033253  .002690  I  .6063026  .0002710  3.3466 0.1916  P    21.535     .500     -.203     .300  -.027000   .021000   .6083000      .000      .000  
+78 115 43523.00 I  -.035329  .003310   .034397  .002690  I  .6030057  .0002710  3.2419 0.1916  P    21.165     .500     -.136     .300  -.031000   .022000   .6052000      .000      .000  
+78 116 43524.00 I  -.039617  .003310   .035533  .002690  I  .5998260  .0002710  3.1156 0.1916  P    20.984     .500     -.177     .300  -.035000   .023000   .6021000      .000      .000  
+78 117 43525.00 I  -.043925  .003310   .036659  .002690  I  .5967754  .0002710  2.9865 0.1916  P    21.054     .500     -.342     .300  -.039000   .024000   .5990000      .000      .000  
+78 118 43526.00 I  -.048250  .003310   .037777  .002690  I  .5938494  .0002710  2.8683 0.1916  P    21.131     .500     -.503     .300  -.042000   .026000   .5960000      .000      .000  
+78 119 43527.00 I  -.052587  .003310   .038885  .002690  I  .5910312  .0002710  2.7728 0.1373  P    21.080     .500     -.569     .300  -.046000   .027000   .5929000      .000      .000  
+78 120 43528.00 I  -.056933  .003644   .039983  .004005  I  .5882930  .0000447  2.7096 0.1373  P    21.041     .500     -.567     .300  -.049000   .029000   .5899000      .000      .000  
+78 121 43529.00 I  -.061283  .003644   .041071  .004005  I  .5855990  .0000447  2.6852 0.0316  P    21.149     .500     -.552     .300  -.053000   .030000   .5868000      .000      .000  
+78 122 43530.00 I  -.065632  .003644   .042149  .004005  I  .5829089  .0000447  2.7018 0.0316  P    21.333     .500     -.537     .300  -.057000   .032000   .5838000      .000      .000  
+78 123 43531.00 I  -.069978  .003644   .043217  .004005  I  .5801827  .0000447  2.7566 0.0316  P    21.483     .500     -.528     .300  -.060000   .033000   .5807000      .000      .000  
+78 124 43532.00 I  -.074314  .003644   .044276  .004005  I  .5773848  .0000447  2.8441 0.0316  P    21.623     .500     -.570     .300  -.064000   .035000   .5777000      .000      .000  
+78 125 43533.00 I  -.078629  .003644   .045329  .004005  I  .5744866  .0000447  2.9553 0.1373  P    21.814     .500     -.698     .300  -.067000   .036000   .5746000      .000      .000  
+78 126 43534.00 I  -.082914  .003310   .046382  .002690  I  .5714703  .0002710  3.0781 0.1373  P    22.007     .500     -.860     .300  -.071000   .038000   .5716000      .000      .000  
+78 127 43535.00 I  -.087156  .003310   .047440  .002690  I  .5683313  .0002710  3.1981 0.1916  P    22.102     .500     -.947     .300  -.075000   .040000   .5685000      .000      .000  
+78 128 43536.00 I  -.091345  .003310   .048508  .002690  I  .5650802  .0002710  3.2998 0.4703  P    22.073     .500     -.902     .300  -.079000   .042000   .5654000      .000      .000  
+78 129 43537.00 I  -.095469  .003457   .049592  .000177  I  .5617421  .0009008  3.3704 0.4703  P    21.961     .500     -.776     .300  -.082000   .045000   .5623000      .000      .000  
+78 130 43538.00 I  -.099518  .003457   .050695  .000177  I  .5583520  .0009008  3.4032 0.6370  P    21.805     .500     -.660     .300  -.086000   .047000   .5592000      .000      .000  
+78 131 43539.00 I  -.103479  .003457   .051823  .000177  I  .5549479  .0009008  3.3994 0.6370  P    21.635     .500     -.596     .300  -.090000   .049000   .5561000      .000      .000  
+78 2 1 43540.00 I  -.107343  .003457   .052982  .000177  I  .5515624  .0009008  3.3679 0.6370  P    21.479     .500     -.580     .300  -.094000   .051000   .5529000      .000      .000  
+78 2 2 43541.00 I  -.111101  .003457   .054176  .000177  I  .5482159  .0009008  3.3250 0.6370  P    21.316     .500     -.635     .300  -.097000   .054000   .5497000      .000      .000  
+78 2 3 43542.00 I  -.114749  .003457   .055410  .000177  I  .5449082  .0009008  3.2948 0.4703  P    21.088     .500     -.813     .300  -.101000   .056000   .5465000      .000      .000  
+78 2 4 43543.00 I  -.118282  .003310   .056689  .002690  I  .5416135  .0002710  3.3029 0.4703  P    20.784     .500    -1.087     .300  -.104000   .059000   .5433000      .000      .000  
+78 2 5 43544.00 I  -.121695  .003310   .058017  .002690  I  .5382847  .0002710  3.3642 0.1916  P    20.482     .500    -1.282     .300  -.108000   .061000   .5401000      .000      .000  
+78 2 6 43545.00 I  -.124985  .003310   .059400  .002690  I  .5348688  .0002710  3.4743 0.1916  P    20.298     .500    -1.222     .300  -.111000   .063000   .5367000      .000      .000  
+78 2 7 43546.00 I  -.128145  .003310   .060842  .002690  I  .5313286  .0002710  3.6067 0.1916  P    20.338     .500     -.955     .300  -.114000   .066000   .5333000      .000      .000  
+78 2 8 43547.00 I  -.131173  .003310   .062348  .002690  I  .5276610  .0002710  3.7223 0.1916  P    20.638     .500     -.739     .300  -.118000   .068000   .5300000      .000      .000  
+78 2 9 43548.00 I  -.134063  .003310   .063923  .002690  I  .5239011  .0002710  3.7865 0.2103  P    21.060     .500     -.771     .300  -.121000   .071000   .5266000      .000      .000  
+78 210 43549.00 I  -.136811  .010342   .065571  .010800  I  .5201109  .0003216  3.7820 0.2103  P    21.321     .500     -.984     .300  -.124000   .073000   .5232000      .000      .000  
+78 211 43550.00 I  -.139412  .010342   .067299  .010800  I  .5163583  .0003216  3.7140 0.2274  P    21.242     .500    -1.172     .300  -.127000   .076000   .5197000      .000      .000  
+78 212 43551.00 I  -.141862  .010342   .069109  .010800  I  .5126973  .0003216  3.6030 0.2274  P    20.955     .500    -1.244     .300  -.130000   .078000   .5162000      .000      .000  
+78 213 43552.00 I  -.144157  .010342   .071007  .010800  I  .5091584  .0003216  3.4739 0.2274  P    20.740     .500    -1.279     .300  -.133000   .081000   .5128000      .000      .000  
+78 214 43553.00 I  -.146306  .010342   .072987  .010800  I  .5057484  .0003216  3.3481 0.2274  P    20.680     .500    -1.371     .300  -.136000   .083000   .5093000      .000      .000  
+78 215 43554.00 I  -.148325  .010342   .075040  .010800  I  .5024562  .0003216  3.2402 0.2103  P    20.642     .500    -1.512     .300  -.139000   .086000   .5058000      .000      .000  
+78 216 43555.00 I  -.150229  .003310   .077157  .002690  I  .4992585  .0002710  3.1608 0.2103  P    20.588     .500    -1.643     .300  -.142000   .089000   .5023000      .000      .000  
+78 217 43556.00 I  -.152032  .003310   .079328  .002690  I  .4961223  .0002710  3.1183 0.2583  P    20.682     .500    -1.728     .300  -.145000   .091000   .4988000      .000      .000  
+78 218 43557.00 I  -.153750  .000711   .081544  .010230  I  .4930084  .0004399  3.1165 0.2583  P    21.016     .500    -1.762     .300  -.148000   .094000   .4952000      .000      .000  
+78 219 43558.00 I  -.155398  .000711   .083795  .010230  I  .4898759  .0004399  3.1551 0.3111  P    21.422     .500    -1.760     .300  -.151000   .096000   .4917000      .000      .000  
+78 220 43559.00 I  -.156991  .000711   .086073  .010230  I  .4866864  .0004399  3.2291 0.3111  P    21.685     .500    -1.766     .300  -.154000   .099000   .4882000      .000      .000  
+78 221 43560.00 I  -.158545  .000711   .088367  .010230  I  .4834090  .0004399  3.3292 0.3111  P    21.815     .500    -1.830     .300  -.157000   .102000   .4847000      .000      .000  
+78 222 43561.00 I  -.160073  .000711   .090673  .010230  I  .4800234  .0004399  3.4430 0.3924  P    21.962     .500    -1.949     .300  -.160000   .104000   .4812000      .000      .000  
+78 223 43562.00 I  -.161591  .002779   .092993  .007757  I  .4765236  .0006499  3.5549 0.4596  P    22.154     .500    -2.048     .300  -.162000   .107000   .4776000      .000      .000  
+78 224 43563.00 I  -.163112  .003866   .095331  .003961  I  .4729200  .0008070  3.6476 0.5181  P    22.267     .500    -2.064     .300  -.165000   .109000   .4741000      .000      .000  
+78 225 43564.00 I  -.164649  .003866   .097691  .003961  I  .4692407  .0008070  3.7035 0.5706  P    22.224     .500    -2.018     .300  -.168000   .112000   .4706000      .000      .000  
+78 226 43565.00 I  -.166217  .003866   .100078  .003961  I  .4655291  .0008070  3.7113 0.5706  P    22.088     .500    -1.983     .300  -.171000   .115000   .4671000      .000      .000  
+78 227 43566.00 I  -.167826  .003866   .102497  .003961  I  .4618338  .0008070  3.6722 0.5706  P    21.968     .500    -1.998     .300  -.174000   .118000   .4636000      .000      .000  
+78 228 43567.00 I  -.169483  .003866   .104958  .003961  I  .4581963  .0008070  3.5985 0.4256  P    21.902     .500    -2.045     .300  -.176000   .120000   .4602000      .000      .000  
+78 3 1 43568.00 I  -.171196  .003310   .107469  .002690  I  .4546412  .0002710  3.5116 0.4256  P    21.862     .500    -2.101     .300  -.179000   .123000   .4567000      .000      .000  
+78 3 2 43569.00 I  -.172969  .003310   .110041  .002690  I  .4511691  .0002710  3.4368 0.1916  P    21.804     .500    -2.186     .300  -.182000   .126000   .4532000      .000      .000  
+78 3 3 43570.00 I  -.174810  .003310   .112681  .002690  I  .4477555  .0002710  3.3983 0.2620  P    21.717     .500    -2.341     .300  -.185000   .129000   .4498000      .000      .000  
+78 3 4 43571.00 I  -.176725  .014722   .115401  .010929  I  .4443550  .0004485  3.4124 0.2620  P    21.632     .500    -2.554     .300  -.187000   .132000   .4464000      .000      .000  
+78 3 5 43572.00 I  -.178721  .014722   .118208  .010929  I  .4409123  .0004485  3.4816 0.3171  P    21.597     .500    -2.714     .300  -.190000   .136000   .4429000      .000      .000  
+78 3 6 43573.00 I  -.180803  .014722   .121112  .010929  I  .4373785  .0004485  3.5908 0.3171  P    21.640     .500    -2.692     .300  -.192000   .139000   .4395000      .000      .000  
+78 3 7 43574.00 I  -.182979  .014722   .124123  .010929  I  .4337276  .0004485  3.7094 0.3171  P    21.757     .500    -2.489     .300  -.195000   .142000   .4361000      .000      .000  
+78 3 8 43575.00 I  -.185247  .014722   .127245  .010929  I  .4299692  .0004485  3.7996 0.3171  P    21.925     .500    -2.292     .300  -.197000   .146000   .4328000      .000      .000  
+78 3 9 43576.00 I  -.187587  .014722   .130475  .010929  I  .4261477  .0004485  3.8318 0.2268  P    22.100     .500    -2.316     .300  -.199000   .149000   .4294000      .000      .000  
+78 310 43577.00 I  -.189978  .002182   .133808  .002690  I  .4223286  .0000680  3.7951 0.2268  P    22.209     .500    -2.585     .300  -.202000   .153000   .4261000      .000      .000  
+78 311 43578.00 I  -.192398  .002182   .137237  .002690  I  .4185771  .0000680  3.6997 0.0481  P    22.202     .500    -2.908     .300  -.204000   .156000   .4227000      .000      .000  
+78 312 43579.00 I  -.194826  .002182   .140760  .002690  I  .4149412  .0000680  3.5683 0.0481  P    22.099     .500    -3.088     .300  -.206000   .160000   .4194000      .000      .000  
+78 313 43580.00 I  -.197240  .002182   .144369  .002690  I  .4114441  .0000680  3.4262 0.0481  P    21.959     .500    -3.101     .300  -.208000   .164000   .4161000      .000      .000  
+78 314 43581.00 I  -.199617  .002182   .148061  .002690  I  .4080856  .0000680  3.2937 0.3286  P    21.809     .500    -3.069     .300  -.210000   .168000   .4128000      .000      .000  
+78 315 43582.00 I  -.201929  .008840   .151830  .004015  I  .4048488  .0006536  3.1848 0.4622  P    21.670     .500    -3.102     .300  -.212000   .172000   .4096000      .000      .000  
+78 316 43583.00 I  -.204152  .012310   .155670  .004015  I  .4017049  .0009218  3.1091 0.5650  P    21.649     .500    -3.207     .300  -.214000   .176000   .4063000      .000      .000  
+78 317 43584.00 I  -.206260  .012310   .159578  .004015  I  .3986177  .0009218  3.0722 0.6518  P    21.885     .500    -3.318     .300  -.216000   .180000   .4030000      .000      .000  
+78 318 43585.00 I  -.208229  .012310   .163547  .004015  I  .3955460  .0009218  3.0786 0.6518  P    22.368     .500    -3.368     .300  -.218000   .184000   .3997000      .000      .000  
+78 319 43586.00 I  -.210050  .012310   .167578  .004015  I  .3924463  .0009218  3.1274 0.6208  P    22.875     .500    -3.353     .300  -.220000   .188000   .3964000      .000      .000  
+78 320 43587.00 I  -.211723  .010773   .171670  .006169  I  .3892796  .0008318  3.2111 0.5882  P    23.167     .500    -3.344     .300  -.222000   .192000   .3930000      .000      .000  
+78 321 43588.00 I  -.213243  .008977   .175824  .007745  I  .3860160  .0007308  3.3191 0.5536  P    23.209     .500    -3.415     .300  -.224000   .196000   .3897000      .000      .000  
+78 322 43589.00 I  -.214610  .008977   .180039  .007745  I  .3826376  .0007308  3.4384 0.5168  P    23.155     .500    -3.539     .300  -.226000   .200000   .3864000      .000      .000  
+78 323 43590.00 I  -.215820  .008977   .184315  .007745  I  .3791411  .0007308  3.5520 0.5168  P    23.148     .500    -3.599     .300  -.228000   .204000   .3830000      .000      .000  
+78 324 43591.00 I  -.216879  .008977   .188649  .007745  I  .3755424  .0007308  3.6391 0.5168  P    23.197     .500    -3.520     .300  -.229000   .208000   .3796000      .000      .000  
+78 325 43592.00 I  -.217801  .008977   .193031  .007745  I  .3718781  .0007308  3.6804 0.4053  P    23.236     .500    -3.384     .300  -.231000   .212000   .3762000      .000      .000  
+78 326 43593.00 I  -.218600  .001138   .197451  .000868  I  .3681999  .0003505  3.6666 0.4053  P    23.223     .500    -3.352     .300  -.232000   .216000   .3728000      .000      .000  
+78 327 43594.00 I  -.219292  .001138   .201898  .000868  I  .3645615  .0003505  3.6029 0.2478  P    23.173     .500    -3.486     .300  -.234000   .220000   .3694000      .000      .000  
+78 328 43595.00 I  -.219892  .001138   .206364  .000868  I  .3610038  .0003505  3.5100 0.2975  P    23.110     .500    -3.699     .300  -.235000   .224000   .3660000      .000      .000  
+78 329 43596.00 I  -.220414  .000902   .210837  .008212  I  .3575414  .0004807  3.4175 0.2975  P    23.037     .500    -3.862     .300  -.236000   .228000   .3625000      .000      .000  
+78 330 43597.00 I  -.220871  .000902   .215307  .008212  I  .3541595  .0004807  3.3531 0.3399  P    22.950     .500    -3.926     .300  -.238000   .232000   .3591000      .000      .000  
+78 331 43598.00 I  -.221278  .000902   .219764  .008212  I  .3508197  .0004807  3.3354 0.3776  P    22.876     .500    -3.942     .300  -.239000   .236000   .3556000      .000      .000  
+78 4 1 43599.00 I  -.221646  .000576   .224195  .011581  I  .3474716  .0005825  3.3692 0.3629  P    22.887     .500    -3.972     .300  -.240000   .240000   .3522000      .000      .000  
+78 4 2 43600.00 I  -.221987  .010198   .228597  .008229  I  .3440670  .0005437  3.4459 0.3984  P    23.052     .500    -4.020     .300  -.241000   .244000   .3487000      .000      .000  
+78 4 3 43601.00 I  -.222313  .010198   .232972  .008229  I  .3405726  .0005437  3.5440 0.3700  P    23.356     .500    -4.021     .300  -.242000   .248000   .3452000      .000      .000  
+78 4 4 43602.00 I  -.222636  .014410   .237325  .001150  I  .3369818  .0005020  3.6336 0.3664  P    23.669     .500    -3.927     .300  -.242000   .253000   .3416000      .000      .000  
+78 4 5 43603.00 I  -.222965  .010405   .241659  .002529  I  .3333181  .0004912  3.6851 0.3512  P    23.827     .500    -3.797     .300  -.243000   .257000   .3381000      .000      .000  
+78 4 6 43604.00 I  -.223290  .010405   .245977  .002529  I  .3296311  .0004912  3.6782 0.3473  P    23.771     .500    -3.770     .300  -.244000   .261000   .3346000      .000      .000  
+78 4 7 43605.00 I  -.223596  .010405   .250283  .002529  I  .3259824  .0004912  3.6094 0.3435  P    23.608     .500    -3.924     .300  -.245000   .265000   .3311000      .000      .000  
+78 4 8 43606.00 I  -.223867  .002983   .254578  .003386  I  .3224283  .0004802  3.4926 0.3435  P    23.502     .500    -4.168     .300  -.245000   .269000   .3276000      .000      .000  
+78 4 9 43607.00 I  -.224091  .002983   .258862  .003386  I  .3190052  .0004802  3.3517 0.3564  P    23.503     .500    -4.332     .300  -.246000   .274000   .3240000      .000      .000  
+78 410 43608.00 I  -.224257  .010007   .263134  .004621  I  .3157250  .0005268  3.2104 0.3725  P    23.508     .500    -4.338     .300  -.246000   .278000   .3205000      .000      .000  
+78 411 43609.00 I  -.224355  .013835   .267391  .005589  I  .3125785  .0005695  3.0869 0.3879  P    23.417     .500    -4.271     .300  -.247000   .282000   .3170000      .000      .000  
+78 412 43610.00 I  -.224376  .013835   .271631  .005589  I  .3095413  .0005695  2.9932 0.4819  P    23.290     .500    -4.260     .300  -.247000   .286000   .3135000      .000      .000  
+78 413 43611.00 I  -.224308  .011313   .275854  .004613  I  .3065796  .0007776  2.9370 0.4819  P    23.284     .500    -4.351     .300  -.247000   .290000   .3101000      .000      .000  
+78 414 43612.00 I  -.224149  .011313   .280058  .004613  I  .3036531  .0007776  2.9235 0.5498  P    23.476     .500    -4.483     .300  -.248000   .293000   .3066000      .000      .000  
+78 415 43613.00 I  -.223913  .011313   .284249  .004613  I  .3007180  .0007776  2.9537 0.6102  P    23.800     .500    -4.564     .300  -.248000   .297000   .3032000      .000      .000  
+78 416 43614.00 I  -.223616  .008036   .288433  .003364  I  .2977329  .0009407  3.0222 0.5117  P    24.131     .500    -4.553     .300  -.248000   .301000   .2997000      .000      .000  
+78 417 43615.00 I  -.223270  .008171   .292613  .007570  I  .2946647  .0006652  3.1175 0.5761  P    24.361     .500    -4.511     .300  -.248000   .305000   .2964000      .000      .000  
+78 418 43616.00 I  -.222878  .008171   .296791  .007570  I  .2914932  .0006652  3.2269 0.3340  P    24.408     .500    -4.537     .300  -.248000   .309000   .2930000      .000      .000  
+78 419 43617.00 I  -.222440  .008304   .300969  .010164  I  .2882108  .0000621  3.3367 0.3340  P    24.247     .500    -4.649     .300  -.247000   .313000   .2897000      .000      .000  
+78 420 43618.00 I  -.221956  .008304   .305144  .010164  I  .2848250  .0000621  3.4308 0.1241  P    23.983     .500    -4.723     .300  -.247000   .317000   .2863000      .000      .000  
+78 421 43619.00 I  -.221413  .006931   .309310  .007190  I  .2813607  .0002404  3.4902 0.1436  P    23.805     .500    -4.626     .300  -.247000   .321000   .2830000      .000      .000  
+78 422 43620.00 I  -.220797  .006931   .313452  .007190  I  .2778616  .0002805  3.4984 0.2065  P    23.798     .500    -4.406     .300  -.246000   .325000   .2798000      .000      .000  
+78 423 43621.00 I  -.220093  .005209   .317558  .000296  I  .2743831  .0003357  3.4490 0.2187  P    23.859     .500    -4.285     .300  -.246000   .328000   .2765000      .000      .000  
+78 424 43622.00 I  -.219285  .005209   .321617  .000296  I  .2709789  .0003357  3.3537 0.2494  P    23.832     .500    -4.429     .300  -.245000   .332000   .2733000      .000      .000  
+78 425 43623.00 I  -.218367  .005209   .325616  .000296  I  .2676812  .0003689  3.2417 0.2494  P    23.689     .500    -4.757     .300  -.245000   .335000   .2700000      .000      .000  
+78 426 43624.00 I  -.217331  .005209   .329543  .000296  I  .2644895  .0003689  3.1475 0.2382  P    23.510     .500    -5.028     .300  -.244000   .339000   .2668000      .000      .000  
+78 427 43625.00 I  -.216175  .009203   .333387  .014647  I  .2613712  .0003013  3.0981 0.2074  P    23.351     .500    -5.074     .300  -.243000   .342000   .2636000      .000      .000  
+78 428 43626.00 I  -.214891  .009203   .337134  .014647  I  .2582750  .0001898  3.1034 0.1780  P    23.208     .500    -4.933     .300  -.242000   .346000   .2605000      .000      .000  
+78 429 43627.00 I  -.213476  .009203   .340775  .014647  I  .2551491  .0001898  3.1544 0.1342  P    23.102     .500    -4.758     .300  -.241000   .349000   .2573000      .000      .000  
+78 430 43628.00 I  -.211925  .009203   .344296  .014647  I  .2519580  .0001898  3.2299 0.1342  P    23.124     .500    -4.668     .300  -.240000   .353000   .2542000      .000      .000  
+78 5 1 43629.00 I  -.210242  .009203   .347698  .014647  I  .2486897  .0001898  3.3043 0.1222  P    23.344     .500    -4.669     .300  -.239000   .356000   .2510000      .000      .000  
+78 5 2 43630.00 I  -.208441  .006650   .350988  .010457  I  .2453578  .0001540  3.3536 0.1089  P    23.682     .500    -4.691     .300  -.238000   .359000   .2478000      .000      .000  
+78 5 3 43631.00 I  -.206533  .001935   .354173  .002038  I  .2419965  .0001067  3.3609 0.0937  P    23.915     .500    -4.678     .300  -.237000   .363000   .2446000      .000      .000  
+78 5 4 43632.00 I  -.204531  .001935   .357260  .002038  I  .2386522  .0001067  3.3195 0.0754  P    23.857     .500    -4.645     .300  -.235000   .366000   .2415000      .000      .000  
+78 5 5 43633.00 I  -.202448  .001935   .360257  .002038  I  .2353720  .0001067  3.2344 0.0754  P    23.555     .500    -4.642     .300  -.234000   .370000   .2383000      .000      .000  
+78 5 6 43634.00 I  -.200298  .001935   .363172  .002038  I  .2321928  .0001067  3.1211 0.3062  P    23.260     .500    -4.683     .300  -.233000   .373000   .2351000      .000      .000  
+78 5 7 43635.00 I  -.198095  .004273   .366014  .002267  I  .2291324  .0006030  3.0002 0.4264  P    23.160     .500    -4.736     .300  -.232000   .376000   .2319000      .000      .000  
+78 5 8 43636.00 I  -.195857  .005724   .368795  .002474  I  .2261885  .0008460  2.8911 0.5195  P    23.206     .500    -4.770     .300  -.230000   .380000   .2287000      .000      .000  
+78 5 9 43637.00 I  -.193599  .005724   .371523  .002474  I  .2233411  .0008460  2.8091 0.5982  P    23.250     .500    -4.800     .300  -.229000   .383000   .2256000      .000      .000  
+78 510 43638.00 I  -.191337  .005724   .374210  .002474  I  .2205588  .0008460  2.7614 0.5250  P    23.266     .500    -4.858     .300  -.227000   .387000   .2224000      .000      .000  
+78 511 43639.00 I  -.189077  .008974   .376868  .002554  I  .2178060  .0006220  2.7506 0.5250  P    23.319     .500    -4.958     .300  -.226000   .390000   .2192000      .000      .000  
+78 512 43640.00 I  -.186824  .008974   .379510  .002554  I  .2150448  .0006220  2.7780 0.3335  P    23.388     .500    -5.083     .300  -.224000   .393000   .2160000      .000      .000  
+78 513 43641.00 I  -.184585  .011327   .382150  .002630  I  .2122382  .0002408  2.8406 0.3252  P    23.402     .500    -5.189     .300  -.222000   .396000   .2128000      .000      .000  
+78 514 43642.00 I  -.182362  .008262   .384800  .005647  I  .2093543  .0001901  2.9309 0.1534  P    23.423     .500    -5.217     .300  -.220000   .399000   .2095000      .000      .000  
+78 515 43643.00 I  -.180143  .008262   .387471  .005647  I  .2063708  .0001901  3.0379 0.1330  P    23.583     .500    -5.156     .300  -.218000   .402000   .2063000      .000      .000  
+78 516 43644.00 I  -.177914  .008262   .390173  .005647  I  .2032768  .0001862  3.1499 0.1168  P    23.796     .500    -5.083     .300  -.216000   .405000   .2031000      .000      .000  
+78 517 43645.00 I  -.175657  .002870   .392915  .007541  I  .2000735  .0001357  3.2543 0.1137  P    23.764     .500    -5.088     .300  -.214000   .408000   .2000000      .000      .000  
+78 518 43646.00 I  -.173361  .004343   .395700  .008610  I  .1967754  .0001305  3.3369 0.0941  P    23.364     .500    -5.132     .300  -.212000   .410000   .1969000      .000      .000  
+78 519 43647.00 I  -.171014  .004343   .398527  .008610  I  .1934123  .0001305  3.3816 0.0948  P    22.868     .500    -5.063     .300  -.210000   .413000   .1937000      .000      .000  
+78 520 43648.00 I  -.168606  .005431   .401396  .009560  I  .1900291  .0001376  3.3757 0.0948  P    22.612     .500    -4.827     .300  -.208000   .415000   .1906000      .000      .000  
+78 521 43649.00 I  -.166125  .005431   .404308  .009560  I  .1866781  .0001376  3.3183 0.0922  P    22.600     .500    -4.609     .300  -.206000   .418000   .1875000      .000      .000  
+78 522 43650.00 I  -.163564  .005431   .407259  .009560  I  .1834043  .0001228  3.2256 0.0922  P    22.571     .500    -4.650     .300  -.203000   .420000   .1845000      .000      .000  
+78 523 43651.00 I  -.160919  .005431   .410238  .009560  I  .1802282  .0001228  3.1290 0.1416  P    22.389     .500    -4.954     .300  -.201000   .422000   .1815000      .000      .000  
+78 524 43652.00 I  -.158192  .010862   .413233  .001924  I  .1771364  .0002552  3.0621 0.1416  P    22.164     .500    -5.251     .300  -.198000   .425000   .1786000      .000      .000  
+78 525 43653.00 I  -.155381  .010862   .416231  .001924  I  .1740874  .0002552  3.0451 0.2477  P    22.028     .500    -5.291     .300  -.196000   .427000   .1756000      .000      .000  
+78 526 43654.00 I  -.152485  .010862   .419219  .001924  I  .1710302  .0004246  3.0761 0.2477  P    21.956     .500    -5.079     .300  -.193000   .429000   .1726000      .000      .000  
+78 527 43655.00 I  -.149505  .010862   .422184  .001924  I  .1679265  .0004246  3.1330 0.3341  P    21.840     .500    -4.815     .300  -.190000   .431000   .1697000      .000      .000  
+78 528 43656.00 I  -.146444  .010862   .425115  .001924  I  .1647655  .0005159  3.1858 0.4782  P    21.660     .500    -4.666     .300  -.187000   .433000   .1669000      .000      .000  
+78 529 43657.00 I  -.143321  .009179   .428001  .005834  I  .1615647  .0008569  3.2090 0.5826  P    21.518     .500    -4.663     .300  -.185000   .435000   .1640000      .000      .000  
+78 530 43658.00 I  -.140155  .007109   .430832  .008023  I  .1583622  .0010447  3.1881 0.6756  P    21.532     .500    -4.750     .300  -.182000   .437000   .1612000      .000      .000  
+78 531 43659.00 I  -.136967  .007109   .433598  .008023  I  .1552040  .0010447  3.1207 0.7713  P    21.675     .500    -4.858     .300  -.179000   .439000   .1583000      .000      .000  
+78 6 1 43660.00 I  -.133776  .007109   .436289  .008023  I  .1521349  .0011351  3.0110 0.7713  P    21.748     .500    -4.924     .300  -.176000   .440000   .1556000      .000      .000  
+78 6 2 43661.00 I  -.130589  .007109   .438906  .008023  I  .1491926  .0011351  2.8695 0.8515  P    21.568     .500    -4.905     .300  -.173000   .442000   .1530000      .000      .000  
+78 6 3 43662.00 I  -.127415  .007109   .441447  .008023  I  .1464011  .0012695  2.7122 0.5835  P    21.184     .500    -4.824     .300  -.170000   .443000   .1503000      .000      .000  
+78 6 4 43663.00 I  -.124258  .003310   .443914  .002690  I  .1437676  .0002710  2.5565 0.9577  P    20.832     .500    -4.767     .300  -.167000   .445000   .1477000      .000      .000  
+78 6 5 43664.00 I  -.121127  .008912   .446307  .002690  I  .1412826  .0014344  2.4174 0.7299  P    20.687     .500    -4.810     .300  -.164000   .446000   .1450000      .000      .000  
+78 6 6 43665.00 I  -.118027  .008912   .448625  .002690  I  .1389238  .0014344  2.3054 1.0143  P    20.735     .500    -4.938     .300  -.161000   .448000   .1424000      .000      .000  
+78 6 7 43666.00 I  -.114966  .008912   .450871  .002690  I  .1366611  .0014344  2.2257 1.0143  P    20.881     .500    -5.055     .300  -.158000   .450000   .1399000      .000      .000  
+78 6 8 43667.00 I  -.111946  .008912   .453043  .002690  I  .1344602  .0014344  2.1827 1.0143  P    21.029     .500    -5.100     .300  -.154000   .451000   .1373000      .000      .000  
+78 6 9 43668.00 I  -.108962  .008912   .455143  .002690  I  .1322833  .0014344  2.1770 0.8794  P    21.041     .500    -5.111     .300  -.151000   .453000   .1348000      .000      .000  
+78 610 43669.00 I  -.106002  .008541   .457171  .000301  I  .1300956  .0010179  2.2031 0.7198  P    20.838     .500    -5.161     .300  -.148000   .455000   .1322000      .000      .000  
+78 611 43670.00 I  -.103056  .008154   .459126  .000301  I  .1278696  .0001211  2.2517 0.5125  P    20.593     .500    -5.230     .300  -.144000   .457000   .1298000      .000      .000  
+78 612 43671.00 I  -.100114  .008154   .461011  .000301  I  .1255884  .0001211  2.3115 0.0753  P    20.612     .500    -5.220     .300  -.141000   .458000   .1274000      .000      .000  
+78 613 43672.00 I  -.097167  .006486   .462824  .001917  I  .1232466  .0000895  2.3711 0.0753  P    20.898     .500    -5.104     .300  -.137000   .460000   .1250000      .000      .000  
+78 614 43673.00 I  -.094212  .006486   .464567  .001917  I  .1208500  .0000895  2.4192 0.0633  P    21.022     .500    -4.984     .300  -.134000   .461000   .1226000      .000      .000  
+78 615 43674.00 I  -.091255  .006486   .466241  .001917  I  .1184157  .0000895  2.4446 0.0484  P    20.623     .500    -4.936     .300  -.130000   .463000   .1202000      .000      .000  
+78 616 43675.00 I  -.088300  .004203   .467846  .002695  I  .1159719  .0000370  2.4367 0.0487  P    19.883     .500    -4.882     .300  -.126000   .464000   .1179000      .000      .000  
+78 617 43676.00 I  -.085345  .003000   .469387  .002130  I  .1135555  .0000382  2.3894 0.0266  P    19.302     .500    -4.713     .300  -.122000   .466000   .1156000      .000      .000  
+78 618 43677.00 I  -.082389  .003000   .470867  .002130  I  .1112044  .0000382  2.3084 0.0274  P    19.093     .500    -4.503     .300  -.118000   .467000   .1134000      .000      .000  
+78 619 43678.00 I  -.079428  .000578   .472291  .001346  I  .1089430  .0000393  2.2146 0.0274  P    19.043     .500    -4.466     .300  -.114000   .469000   .1111000      .000      .000  
+78 620 43679.00 I  -.076459  .000578   .473663  .001346  I  .1067689  .0000393  2.1397 0.1773  P    18.901     .500    -4.663     .300  -.110000   .470000   .1088000      .000      .000  
+78 621 43680.00 I  -.073480  .000578   .474988  .001346  I  .1046477  .0003525  2.1125 0.1773  P    18.682     .500    -4.883     .300  -.106000   .471000   .1066000      .000      .000  
+78 622 43681.00 I  -.070485  .000578   .476267  .001346  I  .1025246  .0003525  2.1433 0.3638  P    18.545     .500    -4.892     .300  -.102000   .473000   .1044000      .000      .000  
+78 623 43682.00 I  -.067473  .003310   .477504  .002690  I  .1003467  .0006365  2.2175 0.2808  P    18.544     .500    -4.698     .300  -.098000   .474000   .1023000      .000      .000  
+78 624 43683.00 I  -.064438  .000423   .478702  .003672  I  .0980864  .0004371  2.3014 0.3861  P    18.553     .500    -4.502     .300  -.094000   .476000   .1001000      .000      .000  
+78 625 43684.00 I  -.061377  .000423   .479862  .003672  I  .0957528  .0004371  2.3586 0.3091  P    18.388     .500    -4.428     .300  -.090000   .477000   .0979000      .000      .000  
+78 626 43685.00 I  -.058285  .000423   .480987  .003672  I  .0933861  .0004371  2.3651 0.2686  P    17.998     .500    -4.445     .300  -.086000   .478000   .0958000      .000      .000  
+78 627 43686.00 I  -.055159  .000423   .482079  .003672  I  .0910414  .0003122  2.3153 0.2686  P    17.580     .500    -4.503     .300  -.082000   .479000   .0937000      .000      .000  
+78 628 43687.00 I  -.051996  .000423   .483137  .003672  I  .0887710  .0003122  2.2192 0.1916  P    17.420     .500    -4.616     .300  -.077000   .480000   .0916000      .000      .000  
+78 629 43688.00 I  -.048792  .000459   .484159  .002611  I  .0866126  .0002221  2.0943 0.1571  P    17.548     .500    -4.775     .300  -.073000   .481000   .0895000      .000      .000  
+78 630 43689.00 I  -.045544  .000493   .485142  .000392  I  .0845858  .0000354  1.9591 0.1125  P    17.649     .500    -4.884     .300  -.069000   .482000   .0874000      .000      .000  
+78 7 1 43690.00 I  -.042250  .000493   .486085  .000392  I  .0826924  .0000354  1.8300 0.0250  P    17.420     .500    -4.868     .300  -.065000   .483000   .0854000      .000      .000  
+78 7 2 43691.00 I  -.038905  .000493   .486984  .000392  I  .0809192  .0000354  1.7207 0.0250  P    16.936     .500    -4.798     .300  -.061000   .484000   .0834000      .000      .000  
+78 7 3 43692.00 I  -.035507  .000493   .487837  .000392  I  .0792409  .0000354  1.6418 0.1836  P    16.544     .500    -4.810     .300  -.056000   .484000   .0814000      .000      .000  
+78 7 4 43693.00 I  -.032052  .000356   .488642  .002848  I  .0776234  .0003654  1.5996 0.2584  P    16.467     .500    -4.924     .300  -.052000   .485000   .0794000      .000      .000  
+78 7 5 43694.00 I  -.028535  .000102   .489396  .004008  I  .0760289  .0005156  1.5957 0.3160  P    16.642     .500    -5.004     .300  -.048000   .486000   .0774000      .000      .000  
+78 7 6 43695.00 I  -.024953  .000102   .490096  .004008  I  .0744199  .0005156  1.6280 0.3236  P    16.858     .500    -4.940     .300  -.044000   .486000   .0755000      .000      .000  
+78 7 7 43696.00 I  -.021301  .004578   .490739  .003196  I  .0727630  .0003913  1.6900 0.3236  P    16.909     .500    -4.799     .300  -.039000   .486000   .0736000      .000      .000  
+78 7 8 43697.00 I  -.017575  .004578   .491319  .003196  I  .0710331  .0003913  1.7724 0.2767  P    16.701     .500    -4.747     .300  -.035000   .487000   .0718000      .000      .000  
+78 7 9 43698.00 I  -.013772  .004578   .491827  .003196  I  .0692151  .0003913  1.8642 0.2448  P    16.367     .500    -4.838     .300  -.030000   .487000   .0699000      .000      .000  
+78 710 43699.00 I  -.009888  .004872   .492256  .005841  I  .0673059  .0002942  1.9525 0.2448  P    16.204     .500    -4.943     .300  -.026000   .487000   .0680000      .000      .000  
+78 711 43700.00 I  -.005928  .004872   .492599  .005841  I  .0653151  .0002942  2.0257 0.1989  P    16.336     .500    -4.926     .300  -.022000   .487000   .0662000      .000      .000  
+78 712 43701.00 I  -.001900  .004483   .492850  .004874  I  .0632630  .0002677  2.0735 0.2085  P    16.474     .500    -4.807     .300  -.017000   .487000   .0644000      .000      .000  
+78 713 43702.00 I   .002188  .003031   .493007  .005784  I  .0611786  .0002955  2.0897 0.1994  P    16.214     .500    -4.703     .300  -.013000   .487000   .0626000      .000      .000  
+78 714 43703.00 I   .006322  .003031   .493071  .005784  I  .0590949  .0002955  2.0718 0.2090  P    15.538     .500    -4.648     .300  -.008000   .487000   .0608000      .000      .000  
+78 715 43704.00 I   .010492  .003031   .493048  .005784  I  .0570456  .0002955  2.0221 0.1798  P    14.839     .500    -4.581     .300  -.004000   .487000   .0590000      .000      .000  
+78 716 43705.00 I   .014681  .003580   .492942  .001737  I  .0550569  .0002049  1.9537 0.2609  P    14.448     .500    -4.503     .300   .000000   .487000   .0572000      .000      .000  
+78 717 43706.00 I   .018873  .007094   .492757  .003159  I  .0531365  .0004300  1.8903 0.3041  P    14.327     .500    -4.521     .300   .005000   .487000   .0554000      .000      .000  
+78 718 43707.00 I   .023046  .009372   .492495  .004115  I  .0512652  .0005726  1.8602 0.3580  P    14.242     .500    -4.665     .300   .009000   .486000   .0537000      .000      .000  
+78 719 43708.00 I   .027181  .009372   .492160  .004115  I  .0493981  .0005726  1.8839 0.4049  P    14.073     .500    -4.783     .300   .014000   .486000   .0519000      .000      .000  
+78 720 43709.00 I   .031258  .009372   .491754  .004115  I  .0474797  .0005726  1.9605 0.4049  P    13.895     .500    -4.718     .300   .018000   .486000   .0501000      .000      .000  
+78 721 43710.00 I   .035262  .009372   .491279  .004115  I  .0454676  .0005726  2.0655 0.3879  P    13.840     .500    -4.534     .300   .022000   .485000   .0483000      .000      .000  
+78 722 43711.00 I   .039190  .008265   .490733  .002933  I  .0433524  .0005233  2.1592 0.3715  P    13.919     .500    -4.436     .300   .027000   .485000   .0465000      .000      .000  
+78 723 43712.00 I   .043039  .006986   .490114  .000523  I  .0411655  .0004734  2.2039 0.3528  P    13.925     .500    -4.489     .300   .031000   .484000   .0447000      .000      .000  
+78 724 43713.00 I   .046808  .006986   .489419  .000523  I  .0389671  .0004734  2.1811 0.3047  P    13.607     .500    -4.548     .300   .036000   .484000   .0429000      .000      .000  
+78 725 43714.00 I   .050496  .004993   .488646  .004001  I  .0368240  .0003836  2.0964 0.3047  P    13.009     .500    -4.505     .300   .040000   .483000   .0411000      .000      .000  
+78 726 43715.00 I   .054113  .004993   .487792  .004001  I  .0347876  .0003836  1.9720 0.2564  P    12.544     .500    -4.453     .300   .044000   .482000   .0392000      .000      .000  
+78 727 43716.00 I   .057670  .006109   .486857  .003612  I  .0328847  .0003404  1.8335 0.2347  P    12.545     .500    -4.534     .300   .049000   .482000   .0374000      .000      .000  
+78 728 43717.00 I   .061179  .005619   .485838  .004408  I  .0311179  .0002706  1.7029 0.1962  P    12.822     .500    -4.719     .300   .053000   .481000   .0355000      .000      .000  
+78 729 43718.00 I   .064651  .005619   .484736  .004408  I  .0294711  .0001952  1.5955 0.1653  P    12.890     .500    -4.855     .300   .058000   .481000   .0337000      .000      .000  
+78 730 43719.00 I   .068101  .004589   .483559  .003602  I  .0279158  .0001898  1.5214 0.1359  P    12.577     .500    -4.881     .300   .062000   .480000   .0318000      .000      .000  
+78 731 43720.00 I   .071536  .005573   .482311  .001896  I  .0264152  .0001890  1.4867 0.1339  P    12.193     .500    -4.878     .300   .066000   .479000   .0298000      .000      .000  
+78 8 1 43721.00 I   .074958  .005573   .481001  .001896  I  .0249285  .0001890  1.4936 0.1143  P    12.081     .500    -4.908     .300   .071000   .478000   .0278000      .000      .000  
+78 8 2 43722.00 I   .078364  .001100   .479640  .004737  I  .0234147  .0001285  1.5403 0.1143  P    12.240     .500    -4.905     .300   .075000   .477000   .0259000      .000      .000  
+78 8 3 43723.00 I   .081753  .001100   .478236  .004737  I  .0218367  .0001285  1.6206 0.0909  P    12.449     .500    -4.785     .300   .080000   .476000   .0239000      .000      .000  
+78 8 4 43724.00 I   .085123  .001100   .476798  .004737  I  .0201654  .0001285  1.7254 0.0666  P    12.539     .500    -4.596     .300   .084000   .475000   .0219000      .000      .000  
+78 8 5 43725.00 I   .088473  .001544   .475336  .006694  I  .0183812  .0000351  1.8445 0.0666  P    12.458     .500    -4.486     .300   .088000   .474000   .0197000      .000      .000  
+78 8 6 43726.00 I   .091802  .001544   .473857  .006694  I  .0164758  .0000351  1.9654 0.1333  P    12.237     .500    -4.536     .300   .092000   .472000   .0175000      .000      .000  
+78 8 7 43727.00 I   .095104  .001520   .472363  .004827  I  .0144542  .0002642  2.0749 0.1868  P    12.007     .500    -4.668     .300   .097000   .471000   .0154000      .000      .000  
+78 8 8 43728.00 I   .098376  .001496   .470854  .001335  I  .0123337  .0003719  2.1613 0.2281  P    11.923     .500    -4.743     .300   .101000   .469000   .0132000      .000      .000  
+78 8 9 43729.00 I   .101614  .001496   .469331  .001335  I  .0101418  .0003719  2.2169 0.2630  P    11.968     .500    -4.709     .300   .105000   .468000   .0110000      .000      .000  
+78 810 43730.00 I   .104814  .001496   .467794  .001335  I  .0079106  .0003719  2.2403 0.2630  P    11.924     .500    -4.624     .300   .109000   .466000   .0087000      .000      .000  
+78 811 43731.00 I   .107971  .001496   .466245  .001335  I  .0056707  .0003719  2.2353 0.2323  P    11.626     .500    -4.563     .300   .113000   .465000   .0063000      .000      .000  
+78 812 43732.00 I   .111084  .003483   .464682  .003795  I  .0034456  .0002783  2.2134 0.1968  P    11.162     .500    -4.550     .300   .116000   .463000   .0040000      .000      .000  
+78 813 43733.00 I   .114151  .004693   .463104  .005198  I  .0012434  .0001287  2.1930 0.1533  P    10.759     .500    -4.599     .300   .120000   .462000   .0016000      .000      .000  
+78 814 43734.00 I   .117171  .004693   .461510  .005198  I -.0009488  .0001287  2.1976 0.0795  P    10.541     .500    -4.729     .300   .124000   .460000  -.0007000      .000      .000  
+78 815 43735.00 I   .120145  .003439   .459899  .004155  I -.0031673  .0000932  2.2485 0.0795  P    10.451     .500    -4.894     .300   .127000   .458000  -.0031000      .000      .000  
+78 816 43736.00 I   .123077  .003439   .458278  .004155  I -.0054643  .0000932  2.3548 0.0659  P    10.363     .500    -4.949     .300   .131000   .457000  -.0055000      .000      .000  
+78 817 43737.00 I   .125976  .003439   .456650  .004155  I -.0078913  .0000932  2.5043 0.1075  P    10.226     .500    -4.799     .300   .134000   .455000  -.0080000      .000      .000  
+78 818 43738.00 I   .128849  .000906   .455022  .002419  I -.0104762  .0001938  2.6636 0.1863  P    10.132     .500    -4.556     .300   .138000   .454000  -.0104000      .000      .000  
+78 819 43739.00 I   .131701  .000906   .453397  .002419  I -.0132074  .0003607  2.7897 0.2505  P    10.195     .500    -4.450     .300   .141000   .452000  -.0128000      .000      .000  
+78 820 43740.00 I   .134538  .000906   .451778  .002419  I -.0160330  .0004620  2.8479 0.2960  P    10.330     .500    -4.546     .300   .144000   .450000  -.0153000      .000      .000  
+78 821 43741.00 I   .137366  .000655   .450165  .002675  I -.0188762  .0004694  2.8252 0.3293  P    10.244     .500    -4.648     .300   .147000   .448000  -.0178000      .000      .000  
+78 822 43742.00 I   .140189  .000655   .448559  .002675  I -.0216605  .0004694  2.7339 0.3215  P     9.784     .500    -4.557     .300   .151000   .447000  -.0202000      .000      .000  
+78 823 43743.00 I   .143012  .002346   .446960  .002612  I -.0243301  .0004393  2.6010 0.3371  P     9.223     .500    -4.334     .300   .154000   .445000  -.0227000      .000      .000  
+78 824 43744.00 I   .145841  .002873   .445366  .002853  I -.0268586  .0004840  2.4564 0.2943  P     9.000     .500    -4.218     .300   .157000   .443000  -.0252000      .000      .000  
+78 825 43745.00 I   .148680  .002873   .443773  .002853  I -.0292468  .0003917  2.3236 0.2709  P     9.183     .500    -4.329     .300   .160000   .441000  -.0277000      .000      .000  
+78 826 43746.00 I   .151531  .002873   .442177  .002853  I -.0315147  .0002437  2.2175 0.2582  P     9.420     .500    -4.554     .300   .163000   .439000  -.0302000      .000      .000  
+78 827 43747.00 I   .154396  .005558   .440571  .001798  I -.0336935  .0003364  2.1466 0.2077  P     9.440     .500    -4.726     .300   .166000   .437000  -.0326000      .000      .000  
+78 828 43748.00 I   .157271  .005558   .438948  .001798  I -.0358210  .0003364  2.1150 0.2374  P     9.353     .500    -4.793     .300   .169000   .435000  -.0351000      .000      .000  
+78 829 43749.00 I   .160151  .006792   .437299  .000551  I -.0379363  .0003352  2.1218 0.2374  P     9.387     .500    -4.798     .300   .172000   .433000  -.0376000      .000      .000  
+78 830 43750.00 I   .163031  .006792   .435617  .000551  I -.0400759  .0003352  2.1626 0.4673  P     9.535     .500    -4.767     .300   .175000   .431000  -.0401000      .000      .000  
+78 831 43751.00 I   .165899  .005469   .433894  .007063  I -.0422703  .0008724  2.2297 0.4673  P     9.641     .500    -4.692     .300   .177000   .428000  -.0426000      .000      .000  
+78 9 1 43752.00 I   .168737  .005469   .432123  .007063  I -.0445405  .0008724  2.3122 0.7300  P     9.664     .500    -4.584     .300   .180000   .426000  -.0452000      .000      .000  
+78 9 2 43753.00 I   .171528  .003701   .430297  .009973  I -.0468958  .0011706  2.3978 0.7300  P     9.660     .500    -4.480     .300   .182000   .423000  -.0477000      .000      .000  
+78 9 3 43754.00 I   .174256  .003701   .428408  .009973  I -.0493332  .0011706  2.4746 0.8277  P     9.614     .500    -4.409     .300   .185000   .421000  -.0502000      .000      .000  
+78 9 4 43755.00 I   .176908  .003701   .426457  .009973  I -.0518383  .0011706  2.5314 0.7554  P     9.467     .500    -4.375     .300   .188000   .419000  -.0527000      .000      .000  
+78 9 5 43756.00 I   .179473  .009571   .424449  .008102  I -.0543860  .0009551  2.5581 0.6754  P     9.284     .500    -4.366     .300   .191000   .417000  -.0552000      .000      .000  
+78 9 6 43757.00 I   .181941  .013020   .422390  .005642  I -.0569425  .0006740  2.5489 0.5413  P     9.212     .500    -4.367     .300   .193000   .414000  -.0577000      .000      .000  
+78 9 7 43758.00 I   .184304  .009207   .420286  .005266  I -.0594734  .0005095  2.5086 0.4225  P     9.280     .500    -4.357     .300   .196000   .412000  -.0602000      .000      .000  
+78 9 8 43759.00 I   .186550  .009207   .418143  .005266  I -.0619536  .0005095  2.4502 0.3603  P     9.343     .500    -4.336     .300   .199000   .410000  -.0627000      .000      .000  
+78 9 9 43760.00 I   .188684  .009207   .415963  .005266  I -.0643739  .0005095  2.3923 0.3364  P     9.258     .500    -4.336     .300   .202000   .408000  -.0652000      .000      .000  
+78 910 43761.00 I   .190718  .009270   .413744  .004782  I -.0667459  .0004394  2.3575 0.2838  P     9.047     .500    -4.406     .300   .205000   .406000  -.0677000      .000      .000  
+78 911 43762.00 I   .192669  .006644   .411482  .004287  I -.0691035  .0002500  2.3666 0.2528  P     8.850     .500    -4.556     .300   .207000   .403000  -.0703000      .000      .000  
+78 912 43763.00 I   .194550  .006644   .409169  .004287  I -.0714977  .0002500  2.4316 0.1751  P     8.777     .500    -4.714     .300   .210000   .401000  -.0728000      .000      .000  
+78 913 43764.00 I   .196378  .009395   .406797  .003623  I -.0739843  .0002453  2.5495 0.1528  P     8.809     .500    -4.740     .300   .213000   .399000  -.0753000      .000      .000  
+78 914 43765.00 I   .198163  .006744   .404359  .003863  I -.0766076  .0001757  2.7001 0.1509  P     8.848     .500    -4.556     .300   .216000   .397000  -.0779000      .000      .000  
+78 915 43766.00 I   .199903  .006744   .401853  .003863  I -.0793833  .0001757  2.8472 0.0901  P     8.867     .500    -4.262     .300   .218000   .394000  -.0805000      .000      .000  
+78 916 43767.00 I   .201596  .001640   .399274  .004088  I -.0822877  .0000398  2.9513 0.0901  P     8.934     .500    -4.077     .300   .221000   .392000  -.0831000      .000      .000  
+78 917 43768.00 I   .203239  .001640   .396620  .004088  I -.0852623  .0000398  2.9845 0.0281  P     9.074     .500    -4.113     .300   .223000   .389000  -.0857000      .000      .000  
+78 918 43769.00 I   .204826  .001640   .393892  .004088  I -.0882315  .0000398  2.9420 0.2303  P     9.144     .500    -4.239     .300   .226000   .387000  -.0883000      .000      .000  
+78 919 43770.00 I   .206351  .004235   .391093  .002912  I -.0911272  .0004589  2.8421 0.3245  P     8.975     .500    -4.224     .300   .228000   .384000  -.0910000      .000      .000  
+78 920 43771.00 I   .207809  .005760   .388224  .000489  I -.0939069  .0006478  2.7158 0.3969  P     8.630     .500    -4.015     .300   .230000   .382000  -.0937000      .000      .000  
+78 921 43772.00 I   .209195  .005760   .385287  .000489  I -.0965604  .0006478  2.5943 0.4581  P     8.387     .500    -3.803     .300   .233000   .379000  -.0964000      .000      .000  
+78 922 43773.00 I   .210502  .005760   .382285  .000489  I -.0991042  .0006478  2.4990 0.4069  P     8.436     .500    -3.796     .300   .235000   .377000  -.0991000      .000      .000  
+78 923 43774.00 I   .211732  .005337   .379220  .002204  I -.1015708  .0004925  2.4410 0.4069  P     8.708     .500    -4.002     .300   .237000   .374000  -.1018000      .000      .000  
+78 924 43775.00 I   .212889  .005337   .376092  .002204  I -.1040003  .0004925  2.4254 0.2776  P     9.031     .500    -4.261     .300   .239000   .371000  -.1046000      .000      .000  
+78 925 43776.00 I   .213980  .004878   .372904  .003079  I -.1064361  .0002561  2.4533 0.2776  P     9.336     .500    -4.428     .300   .241000   .369000  -.1074000      .000      .000  
+78 926 43777.00 I   .215010  .004878   .369658  .003079  I -.1089203  .0002561  2.5213 0.2022  P     9.609     .500    -4.477     .300   .243000   .366000  -.1101000      .000      .000  
+78 927 43778.00 I   .215989  .004846   .366356  .003526  I -.1114893  .0003130  2.6210 0.2022  P     9.780     .500    -4.465     .300   .245000   .364000  -.1129000      .000      .000  
+78 928 43779.00 I   .216932  .004846   .363006  .003526  I -.1141692  .0003130  2.7412 0.2389  P     9.795     .500    -4.447     .300   .247000   .361000  -.1157000      .000      .000  
+78 929 43780.00 I   .217853  .004814   .359612  .003922  I -.1169746  .0003610  2.8699 0.2389  P     9.736     .500    -4.416     .300   .249000   .358000  -.1186000      .000      .000  
+78 930 43781.00 I   .218767  .004814   .356179  .003922  I -.1199073  .0003610  2.9936 0.2553  P     9.732     .500    -4.315     .300   .250000   .356000  -.1214000      .000      .000  
+7810 1 43782.00 I   .219684  .004814   .352717  .003922  I -.1229554  .0003610  3.0983 0.2239  P     9.780     .500    -4.097     .300   .252000   .353000  -.1243000      .000      .000  
+7810 2 43783.00 I   .220609  .007654   .349234  .006082  I -.1260928  .0002649  3.1699 0.1873  P     9.760     .500    -3.813     .300   .253000   .351000  -.1271000      .000      .000  
+7810 3 43784.00 I   .221550  .009695   .345743  .007655  I -.1292806  .0000999  3.1978 0.1416  P     9.639     .500    -3.603     .300   .255000   .348000  -.1300000      .000      .000  
+7810 4 43785.00 I   .222509  .009695   .342253  .007655  I -.1324730  .0000999  3.1796 0.0706  P     9.543     .500    -3.570     .300   .256000   .345000  -.1329000      .000      .000  
+7810 5 43786.00 I   .223491  .009695   .338774  .007655  I -.1356278  .0000999  3.1253 0.2062  P     9.593     .500    -3.671     .300   .257000   .342000  -.1359000      .000      .000  
+7810 6 43787.00 I   .224485  .007366   .335309  .005525  I -.1387186  .0004002  3.0561 0.2062  P     9.743     .500    -3.779     .300   .259000   .340000  -.1388000      .000      .000  
+7810 7 43788.00 I   .225478  .007366   .331858  .005525  I -.1417440  .0004002  2.9990 0.3429  P     9.843     .500    -3.814     .300   .260000   .337000  -.1418000      .000      .000  
+7810 8 43789.00 I   .226457  .003813   .328422  .001570  I -.1447283  .0005570  2.9770 0.3429  P     9.815     .500    -3.807     .300   .261000   .334000  -.1447000      .000      .000  
+7810 9 43790.00 I   .227409  .003813   .325001  .001570  I -.1477144  .0005570  3.0041 0.3423  P     9.738     .500    -3.821     .300   .262000   .331000  -.1477000      .000      .000  
+781010 43791.00 I   .228323  .006141   .321596  .001802  I -.1507530  .0003980  3.0808 0.3714  P     9.750     .500    -3.852     .300   .263000   .328000  -.1507000      .000      .000  
+781011 43792.00 I   .229191  .006141   .318206  .001802  I -.1538881  .0004914  3.1937 0.2966  P     9.906     .500    -3.825     .300   .263000   .324000  -.1536000      .000      .000  
+781012 43793.00 I   .230008  .007802   .314828  .002008  I -.1571436  .0004399  3.3161 0.3525  P    10.132     .500    -3.675     .300   .264000   .321000  -.1566000      .000      .000  
+781013 43794.00 I   .230768  .006157   .311463  .004014  I -.1605123  .0005055  3.4143 0.3351  P    10.314     .500    -3.432     .300   .265000   .318000  -.1596000      .000      .000  
+781014 43795.00 I   .231468  .006157   .308107  .004014  I -.1639545  .0005055  3.4592 0.3918  P    10.403     .500    -3.231     .300   .265000   .315000  -.1626000      .000      .000  
+781015 43796.00 I   .232115  .006157   .304757  .004014  I -.1674075  .0005988  3.4345 0.4855  P    10.440     .500    -3.193     .300   .266000   .311000  -.1656000      .000      .000  
+781016 43797.00 I   .232718  .003407   .301409  .009129  I -.1708012  .0008291  3.3431 0.5074  P    10.477     .500    -3.288     .300   .266000   .308000  -.1687000      .000      .000  
+781017 43798.00 I   .233283  .003407   .298057  .009129  I -.1740780  .0008193  3.2048 0.5828  P    10.514     .500    -3.351     .300   .267000   .304000  -.1717000      .000      .000  
+781018 43799.00 I   .233814  .003407   .294691  .009129  I -.1772045  .0008193  3.0477 0.6091  P    10.529     .500    -3.260     .300   .267000   .301000  -.1747000      .000      .000  
+781019 43800.00 I   .234310  .002876   .291299  .011768  I -.1801762  .0009014  2.8989 0.6091  P    10.545     .500    -3.085     .300   .267000   .297000  -.1778000      .000      .000  
+781020 43801.00 I   .234773  .002876   .287874  .011768  I -.1830117  .0009014  2.7784 0.6842  P    10.648     .500    -3.007     .300   .267000   .294000  -.1809000      .000      .000  
+781021 43802.00 I   .235200  .002056   .284417  .009204  I -.1857462  .0010296  2.6976 0.7179  P    10.924     .500    -3.116     .300   .268000   .290000  -.1839000      .000      .000  
+781022 43803.00 I   .235586  .000423   .280934  .005562  I -.1884211  .0011175  2.6593 0.8119  P    11.378     .500    -3.327     .300   .268000   .287000  -.1870000      .000      .000  
+781023 43804.00 I   .235928  .000423   .277428  .005562  I -.1910785  .0012556  2.6622 0.8404  P    11.889     .500    -3.494     .300   .268000   .283000  -.1901000      .000      .000  
+781024 43805.00 I   .236222  .000423   .273904  .005562  I -.1937577  .0012556  2.7019 0.8878  P    12.283     .500    -3.548     .300   .268000   .279000  -.1932000      .000      .000  
+781025 43806.00 I   .236463  .000423   .270369  .005562  I -.1964924  .0012556  2.7720 0.7598  P    12.465     .500    -3.521     .300   .268000   .276000  -.1963000      .000      .000  
+781026 43807.00 I   .236650  .001163   .266835  .005621  I -.1993084  .0008558  2.8622 0.6766  P    12.479     .500    -3.475     .300   .267000   .272000  -.1994000      .000      .000  
+781027 43808.00 I   .236778  .001590   .263312  .005679  I -.2022192  .0005045  2.9596 0.4967  P    12.444     .500    -3.422     .300   .267000   .269000  -.2025000      .000      .000  
+781028 43809.00 I   .236845  .001590   .259814  .005679  I -.2052260  .0005045  3.0522 0.3326  P    12.426     .500    -3.296     .300   .267000   .265000  -.2056000      .000      .000  
+781029 43810.00 I   .236847  .002330   .256349  .005552  I -.2083172  .0004335  3.1257 0.3326  P    12.414     .500    -3.017     .300   .267000   .261000  -.2087000      .000      .000  
+781030 43811.00 I   .236778  .002330   .252925  .005552  I -.2114663  .0004335  3.1660 0.3065  P    12.383     .500    -2.619     .300   .266000   .258000  -.2118000      .000      .000  
+781031 43812.00 I   .236632  .002330   .249544  .005552  I -.2146351  .0004335  3.1642 0.2541  P    12.351     .500    -2.287     .300   .266000   .254000  -.2149000      .000      .000  
+7811 1 43813.00 I   .236404  .002886   .246213  .005422  I -.2177810  .0002653  3.1215 0.2418  P    12.358     .500    -2.216     .300   .265000   .251000  -.2180000      .000      .000  
+7811 2 43814.00 I   .236086  .002886   .242932  .005422  I -.2208692  .0002145  3.0523 0.1706  P    12.415     .500    -2.416     .300   .265000   .247000  -.2211000      .000      .000  
+7811 3 43815.00 I   .235680  .002886   .239702  .005422  I -.2238856  .0002145  2.9831 0.1227  P    12.482     .500    -2.683     .300   .264000   .243000  -.2243000      .000      .000  
+7811 4 43816.00 I   .235182  .003310   .236519  .002690  I -.2268452  .0001193  2.9432 0.1227  P    12.503     .500    -2.792     .300   .264000   .240000  -.2274000      .000      .000  
+7811 5 43817.00 I   .234594  .003310   .233379  .002690  I -.2297885  .0001193  2.9526 0.0844  P    12.451     .500    -2.693     .300   .263000   .236000  -.2306000      .000      .000  
+7811 6 43818.00 I   .233914  .003310   .230281  .002690  I -.2327680  .0001193  3.0148 0.0852  P    12.373     .500    -2.499     .300   .263000   .233000  -.2337000      .000      .000  
+7811 7 43819.00 I   .233142  .003310   .227222  .002690  I -.2358311  .0001218  3.1161 0.0817  P    12.365     .500    -2.337     .300   .262000   .229000  -.2369000      .000      .000  
+7811 8 43820.00 I   .232276  .003310   .224198  .002690  I -.2390052  .0001117  3.2323 0.0826  P    12.490     .500    -2.240     .300   .261000   .225000  -.2401000      .000      .000  
+7811 9 43821.00 I   .231317  .003310   .221208  .002690  I -.2422912  .0001117  3.3350 0.1462  P    12.709     .500    -2.166     .300   .260000   .222000  -.2432000      .000      .000  
+781110 43822.00 I   .230263  .003809   .218247  .001759  I -.2456624  .0002702  3.3992 0.1462  P    12.898     .500    -2.078     .300   .259000   .218000  -.2464000      .000      .000  
+781111 43823.00 I   .229114  .003809   .215313  .001759  I -.2490712  .0002702  3.4084 0.1911  P    12.945     .500    -1.998     .300   .258000   .215000  -.2495000      .000      .000  
+781112 43824.00 I   .227870  .003809   .212404  .001759  I -.2524596  .0002702  3.3590 0.2021  P    12.853     .500    -1.978     .300   .257000   .211000  -.2527000      .000      .000  
+781113 43825.00 I   .226529  .003809   .209516  .001759  I -.2557732  .0003006  3.2615 0.2230  P    12.753     .500    -2.025     .300   .256000   .207000  -.2559000      .000      .000  
+781114 43826.00 I   .225095  .003809   .206646  .001759  I -.2589735  .0003548  3.1365 0.2325  P    12.796     .500    -2.061     .300   .255000   .204000  -.2590000      .000      .000  
+781115 43827.00 I   .223575  .003809   .203788  .001759  I -.2620454  .0003548  3.0091 0.2134  P    12.999     .500    -2.005     .300   .253000   .200000  -.2622000      .000      .000  
+781116 43828.00 I   .221976  .003310   .200937  .002690  I -.2649981  .0002374  2.9010 0.2695  P    13.240     .500    -1.876     .300   .252000   .197000  -.2653000      .000      .000  
+781117 43829.00 I   .220303  .003310   .198088  .002690  I -.2678583  .0004058  2.8257 0.1941  P    13.430     .500    -1.785     .300   .251000   .193000  -.2685000      .000      .000  
+781118 43830.00 I   .218564  .000942   .195236  .005539  I -.2706623  .0003072  2.7889 0.2545  P    13.644     .500    -1.811     .300   .249000   .190000  -.2716000      .000      .000  
+781119 43831.00 I   .216764  .000942   .192375  .005539  I -.2734491  .0003072  2.7911 0.2172  P    13.988     .500    -1.918     .300   .248000   .186000  -.2748000      .000      .000  
+781120 43832.00 I   .214912  .000942   .189501  .005539  I -.2762562  .0003072  2.8285 0.2172  P    14.399     .500    -2.012     .300   .246000   .183000  -.2779000      .000      .000  
+781121 43833.00 I   .213012  .000942   .186609  .005539  I -.2791156  .0003072  2.8943 0.2238  P    14.697     .500    -2.033     .300   .245000   .179000  -.2811000      .000      .000  
+781122 43834.00 I   .211071  .000942   .183701  .005539  I -.2820508  .0003255  2.9782 0.1784  P    14.825     .500    -1.983     .300   .243000   .176000  -.2842000      .000      .000  
+781123 43835.00 I   .209093  .000942   .180776  .005539  I -.2850740  .0001816  3.0681 0.3488  P    14.898     .500    -1.893     .300   .241000   .173000  -.2873000      .000      .000  
+781124 43836.00 I   .207082  .003725   .177839  .000902  I -.2881851  .0006169  3.1522 0.3215  P    14.978     .500    -1.794     .300   .239000   .169000  -.2904000      .000      .000  
+781125 43837.00 I   .205044  .003725   .174892  .000902  I -.2913729  .0006169  3.2199 0.4362  P    14.963     .500    -1.683     .300   .238000   .166000  -.2936000      .000      .000  
+781126 43838.00 I   .202984  .003725   .171935  .000902  I -.2946162  .0006169  3.2614 0.4362  P    14.780     .500    -1.509     .300   .236000   .162000  -.2967000      .000      .000  
+781127 43839.00 I   .200905  .003725   .168972  .000902  I -.2978839  .0006169  3.2674 0.4362  P    14.562     .500    -1.233     .300   .234000   .159000  -.2998000      .000      .000  
+781128 43840.00 I   .198815  .003725   .166005  .000902  I -.3011377  .0006169  3.2336 0.4362  P    14.505     .500     -.942     .300   .232000   .156000  -.3029000      .000      .000  
+781129 43841.00 I   .196725  .003725   .163037  .000902  I -.3043399  .0006169  3.1663 0.3369  P    14.620     .500     -.836     .300   .230000   .153000  -.3060000      .000      .000  
+781130 43842.00 I   .194646  .003310   .160071  .002690  I -.3074654  .0002710  3.0844 0.3369  P    14.744     .500    -1.024     .300   .228000   .149000  -.3091000      .000      .000  
+7812 1 43843.00 I   .192590  .003310   .157112  .002690  I -.3105135  .0002710  3.0170 0.1916  P    14.760     .500    -1.356     .300   .226000   .146000  -.3122000      .000      .000  
+7812 2 43844.00 I   .190570  .003310   .154161  .002690  I -.3135135  .0002710  2.9921 0.1728  P    14.688     .500    -1.534     .300   .224000   .143000  -.3153000      .000      .000  
+7812 3 43845.00 I   .188596  .010013   .151222  .001921  I -.3165158  .0002144  3.0217 0.1728  P    14.593     .500    -1.404     .300   .222000   .140000  -.3184000      .000      .000  
+7812 4 43846.00 I   .186681  .010013   .148300  .001921  I -.3195721  .0002144  3.0969 0.1516  P    14.513     .500    -1.082     .300   .220000   .137000  -.3214000      .000      .000  
+7812 5 43847.00 I   .184837  .010013   .145396  .001921  I -.3227160  .0002144  3.1912 0.1516  P    14.472     .500     -.795     .300   .217000   .134000  -.3245000      .000      .000  
+7812 6 43848.00 I   .183075  .010013   .142515  .001921  I -.3259501  .0002144  3.2721 0.1516  P    14.488     .500     -.663     .300   .215000   .131000  -.3275000      .000      .000  
+7812 7 43849.00 I   .181394  .010013   .139662  .001921  I -.3292471  .0002144  3.3135 0.1516  P    14.541     .500     -.656     .300   .213000   .128000  -.3306000      .000      .000  
+7812 8 43850.00 I   .179786  .010013   .136842  .001921  I -.3325594  .0002144  3.3018 0.2269  P    14.574     .500     -.707     .300   .211000   .125000  -.3336000      .000      .000  
+7812 9 43851.00 I   .178245  .003110   .134062  .002384  I -.3358331  .0004000  3.2372 0.2533  P    14.537     .500     -.785     .300   .209000   .122000  -.3366000      .000      .000  
+781210 43852.00 I   .176763  .003110   .131326  .002384  I -.3390196  .0004589  3.1300 0.3044  P    14.428     .500     -.878     .300   .206000   .120000  -.3397000      .000      .000  
+781211 43853.00 I   .175333  .003110   .128642  .002384  I -.3420848  .0004589  2.9978 0.3245  P    14.314     .500     -.945     .300   .204000   .117000  -.3427000      .000      .000  
+781212 43854.00 I   .173948  .003110   .126014  .002384  I -.3450138  .0004589  2.8613 0.3245  P    14.308     .500     -.933     .300   .202000   .114000  -.3457000      .000      .000  
+781213 43855.00 I   .172598  .003110   .123448  .002384  I -.3478125  .0004589  2.7400 0.3245  P    14.459     .500     -.835     .300   .200000   .112000  -.3487000      .000      .000  
+781214 43856.00 I   .171269  .003110   .120945  .002384  I -.3505033  .0004589  2.6473 0.3832  P    14.676     .500     -.714     .300   .198000   .109000  -.3517000      .000      .000  
+781215 43857.00 I   .169949  .003310   .118509  .002690  I -.3531192  .0006139  2.5909 0.3497  P    14.832     .500     -.651     .300   .195000   .107000  -.3547000      .000      .000  
+781216 43858.00 I   .168623  .005190   .116141  .006718  I -.3556976  .0005278  2.5721 0.4048  P    14.937     .500     -.670     .300   .193000   .104000  -.3577000      .000      .000  
+781217 43859.00 I   .167280  .005190   .113846  .006718  I -.3582751  .0005278  2.5884 0.3732  P    15.096     .500     -.727     .300   .191000   .102000  -.3607000      .000      .000  
+781218 43860.00 I   .165906  .005190   .111626  .006718  I -.3608843  .0005278  2.6342 0.3732  P    15.313     .500     -.777     .300   .189000   .100000  -.3637000      .000      .000  
+781219 43861.00 I   .164489  .005190   .109482  .006718  I -.3635510  .0005278  2.7024 0.3732  P    15.469     .500     -.805     .300   .187000   .098000  -.3667000      .000      .000  
+781220 43862.00 I   .163023  .005190   .107410  .006718  I -.3662935  .0005278  2.7839 0.3732  P    15.545     .500     -.799     .300   .184000   .096000  -.3696000      .000      .000  
+781221 43863.00 I   .161506  .005190   .105405  .006718  I -.3691193  .0005278  2.8670 0.2967  P    15.659     .500     -.740     .300   .182000   .094000  -.3726000      .000      .000  
+781222 43864.00 I   .159933  .003310   .103460  .002690  I -.3720247  .0002710  2.9416 0.2967  P    15.812     .500     -.654     .300   .180000   .092000  -.3756000      .000      .000  
+781223 43865.00 I   .158303  .003310   .101569  .002690  I -.3749969  .0002710  2.9995 0.1916  P    15.782     .500     -.608     .300   .178000   .090000  -.3786000      .000      .000  
+781224 43866.00 I   .156610  .003310   .099725  .002690  I -.3780160  .0002710  3.0344 0.1916  P    15.448     .500     -.609     .300   .176000   .088000  -.3815000      .000      .000  
+781225 43867.00 I   .154854  .003310   .097924  .002690  I -.3810565  .0002710  3.0416 0.1916  P    15.042     .500     -.554     .300   .173000   .087000  -.3845000      .000      .000  
+781226 43868.00 I   .153029  .003310   .096158  .002690  I -.3840898  .0002710  3.0207 0.5804  P    14.892     .500     -.375     .300   .171000   .085000  -.3874000      .000      .000  
+781227 43869.00 I   .151133  .000746   .094422  .009705  I -.3870918  .0011288  2.9814 0.5804  P    14.995     .500     -.196     .300   .169000   .083000  -.3904000      .000      .000  
+781228 43870.00 I   .149163  .000746   .092709  .009705  I -.3900537  .0011288  2.9453 0.7063  P    15.069     .500     -.222     .300   .167000   .082000  -.3933000      .000      .000  
+781229 43871.00 I   .147115  .000746   .091014  .009705  I -.3929930  .0008494  2.9410 0.7063  P    14.958     .500     -.461     .300   .164000   .080000  -.3962000      .000      .000  
+781230 43872.00 I   .144987  .000746   .089332  .009705  I -.3959529  .0008494  2.9883 0.6006  P    14.798     .500     -.658     .300   .162000   .079000  -.3992000      .000      .000  
+781231 43873.00 I   .142776  .000746   .087666  .009705  I -.3989868  .0008494  3.0871 0.6006  P    14.781     .500     -.583     .300   .159000   .077000  -.4021000      .000      .000  
+79 1 1 43874.00 I   .140480  .000746   .086025  .009705  I  .5978633  .0008494  3.2148 0.4248  P    14.925     .500     -.286     .300   .157000   .076000   .5950000      .000      .000  
+79 1 2 43875.00 I   .138099  .003310   .084414  .002690  I  .5945863  .0000180  3.3343 0.4248  P    15.115     .500     -.007     .300   .155000   .075000   .5921000      .000      .000  
+79 1 3 43876.00 I   .135629  .003310   .082840  .002690  I  .5912098  .0000180  3.4086 0.1358  P    15.221     .500      .113     .300   .152000   .074000   .5892000      .000      .000  
+79 1 4 43877.00 I   .133069  .003310   .081309  .002690  I  .5877910  .0002710  3.4172 0.1358  P    15.172     .500      .114     .300   .150000   .072000   .5863000      .000      .000  
+79 1 5 43878.00 I   .130417  .003310   .079829  .002690  I  .5843976  .0002710  3.3593 0.2373  P    15.003     .500      .067     .300   .147000   .071000   .5834000      .000      .000  
+79 1 6 43879.00 I   .127672  .006757   .078406  .008005  I  .5810892  .0003897  3.2510 0.2373  P    14.845     .500     -.040     .300   .145000   .070000   .5805000      .000      .000  
+79 1 7 43880.00 I   .124831  .006757   .077047  .008005  I  .5779048  .0003897  3.1151 0.2756  P    14.795     .500     -.219     .300   .143000   .069000   .5776000      .000      .000  
+79 1 8 43881.00 I   .121894  .006757   .075758  .008005  I  .5748607  .0003897  2.9740 0.2756  P    14.818     .500     -.390     .300   .140000   .068000   .5747000      .000      .000  
+79 1 9 43882.00 I   .118858  .006757   .074545  .008005  I  .5719519  .0003897  2.8474 0.2756  P    14.832     .500     -.443     .300   .138000   .067000   .5719000      .000      .000  
+79 110 43883.00 I   .115732  .006757   .073408  .008005  I  .5691563  .0003897  2.7494 0.2756  P    14.836     .500     -.374     .300   .135000   .066000   .5690000      .000      .000  
+79 111 43884.00 I   .112525  .006757   .072343  .008005  I  .5664409  .0003897  2.6879 0.2373  P    14.878     .500     -.289     .300   .133000   .065000   .5661000      .000      .000  
+79 112 43885.00 I   .109250  .003310   .071345  .002690  I  .5637676  .0002710  2.6651 0.2373  P    14.957     .500     -.277     .300   .131000   .064000   .5632000      .000      .000  
+79 113 43886.00 I   .105917  .003310   .070411  .002690  I  .5610988  .0002710  2.6781 0.1916  P    15.047     .500     -.322     .300   .128000   .063000   .5604000      .000      .000  
+79 114 43887.00 I   .102538  .003310   .069537  .002690  I  .5584014  .0002710  2.7212 0.1916  P    15.168     .500     -.362     .300   .126000   .063000   .5575000      .000      .000  
+79 115 43888.00 I   .099123  .003310   .068719  .002690  I  .5556491  .0002710  2.7864 0.1916  P    15.321     .500     -.392     .300   .123000   .062000   .5547000      .000      .000  
+79 116 43889.00 I   .095684  .003310   .067954  .002690  I  .5528245  .0002710  2.8639 0.1916  P    15.441     .500     -.450     .300   .121000   .061000   .5518000      .000      .000  
+79 117 43890.00 I   .092231  .003310   .067237  .002690  I  .5499209  .0002710  2.9425 0.1916  P    15.501     .500     -.529     .300   .118000   .060000   .5489000      .000      .000  
+79 118 43891.00 I   .088776  .003310   .066565  .002690  I  .5469430  .0002710  3.0107 0.1916  P    15.561     .500     -.563     .300   .116000   .060000   .5461000      .000      .000  
+79 119 43892.00 I   .085330  .003310   .065933  .002690  I  .5439059  .0002710  3.0596 0.1916  P    15.613     .500     -.537     .300   .113000   .059000   .5432000      .000      .000  
+79 120 43893.00 I   .081903  .003310   .065338  .002690  I  .5408321  .0002710  3.0836 0.1400  P    15.489     .500     -.541     .300   .111000   .059000   .5404000      .000      .000  
+79 121 43894.00 I   .078507  .006923   .064776  .005908  I  .5377474  .0000706  3.0814 0.1400  P    15.107     .500     -.639     .300   .108000   .058000   .5375000      .000      .000  
+79 122 43895.00 I   .075153  .006923   .064243  .005908  I  .5346777  .0000706  3.0541 0.0499  P    14.700     .500     -.733     .300   .105000   .057000   .5346000      .000      .000  
+79 123 43896.00 I   .071853  .006923   .063735  .005908  I  .5316455  .0000706  3.0081 0.0499  P    14.567     .500     -.660     .300   .102000   .057000   .5318000      .000      .000  
+79 124 43897.00 I   .068616  .006923   .063248  .005908  I  .5286629  .0000706  2.9580 0.0499  P    14.653     .500     -.448     .300   .100000   .056000   .5289000      .000      .000  
+79 125 43898.00 I   .065446  .006923   .062784  .005908  I  .5257234  .0000706  2.9264 0.0499  P    14.607     .500     -.324     .300   .097000   .056000   .5261000      .000      .000  
+79 126 43899.00 I   .062342  .006923   .062344  .005908  I  .5227957  .0000706  2.9381 0.1400  P    14.276     .500     -.433     .300   .094000   .055000   .5232000      .000      .000  
+79 127 43900.00 I   .059301  .003310   .061935  .002690  I  .5198283  .0002710  3.0067 0.1400  P    13.919     .500     -.628     .300   .091000   .055000   .5203000      .000      .000  
+79 128 43901.00 I   .056322  .003310   .061559  .002690  I  .5167664  .0002710  3.1231 0.1916  P    13.885     .500     -.664     .300   .088000   .054000   .5175000      .000      .000  
+79 129 43902.00 I   .053402  .003310   .061220  .002690  I  .5135772  .0002710  3.2541 0.2257  P    14.245     .500     -.510     .300   .085000   .054000   .5146000      .000      .000  
+79 130 43903.00 I   .050539  .001000   .060923  .004615  I  .5102684  .0003611  3.3545 0.2257  P    14.767     .500     -.354     .300   .082000   .053000   .5118000      .000      .000  
+79 131 43904.00 I   .047732  .001000   .060670  .004615  I  .5068909  .0003611  3.3869 0.2553  P    15.125     .500     -.312     .300   .079000   .053000   .5089000      .000      .000  
+79 2 1 43905.00 I   .044977  .001000   .060467  .004615  I  .5035221  .0003611  3.3368 0.2211  P    15.115     .500     -.313     .300   .076000   .053000   .5060000      .000      .000  
+79 2 2 43906.00 I   .042272  .000786   .060316  .004555  I  .5002411  .0002553  3.2154 0.2211  P    14.803     .500     -.278     .300   .073000   .053000   .5031000      .000      .000  
+79 2 3 43907.00 I   .039616  .000786   .060220  .004555  I  .4971054  .0002553  3.0519 0.1805  P    14.502     .500     -.286     .300   .070000   .052000   .5003000      .000      .000  
+79 2 4 43908.00 I   .037003  .000786   .060177  .004555  I  .4941406  .0002553  2.8786 0.1277  P    14.473     .500     -.449     .300   .067000   .052000   .4974000      .000      .000  
+79 2 5 43909.00 I   .034431  .002510   .060183  .005218  I  .4913431  .0000077  2.7208 0.1277  P    14.640     .500     -.721     .300   .064000   .052000   .4945000      .000      .000  
+79 2 6 43910.00 I   .031894  .002510   .060235  .005218  I  .4886884  .0000077  2.5950 0.0054  P    14.731     .500     -.937     .300   .061000   .052000   .4916000      .000      .000  
+79 2 7 43911.00 I   .029391  .002510   .060324  .005218  I  .4861395  .0000077  2.5099 0.0058  P    14.659     .500    -1.017     .300   .058000   .053000   .4887000      .000      .000  
+79 2 8 43912.00 I   .026917  .003516   .060443  .005852  I  .4836543  .0000088  2.4676 0.0464  P    14.607     .500    -1.030     .300   .054000   .053000   .4859000      .000      .000  
+79 2 9 43913.00 I   .024464  .004495   .060590  .004142  I  .4811906  .0000924  2.4662 0.0464  P    14.735     .500    -1.060     .300   .051000   .054000   .4830000      .000      .000  
+79 210 43914.00 I   .022021  .004495   .060766  .004142  I  .4787103  .0000924  2.4995 0.0799  P    14.988     .500    -1.092     .300   .048000   .054000   .4801000      .000      .000  
+79 211 43915.00 I   .019579  .005296   .060969  .000260  I  .4761830  .0001304  2.5587 0.0799  P    15.238     .500    -1.077     .300   .044000   .055000   .4772000      .000      .000  
+79 212 43916.00 I   .017129  .005296   .061199  .000260  I  .4735876  .0001304  2.6339 0.0922  P    15.439     .500    -1.046     .300   .041000   .055000   .4743000      .000      .000  
+79 213 43917.00 I   .014664  .005296   .061456  .000260  I  .4709136  .0001304  2.7140 0.0922  P    15.584     .500    -1.081     .300   .037000   .056000   .4714000      .000      .000  
+79 214 43918.00 I   .012186  .005296   .061739  .000260  I  .4681619  .0001304  2.7874 0.1504  P    15.645     .500    -1.193     .300   .034000   .056000   .4685000      .000      .000  
+79 215 43919.00 I   .009695  .003310   .062047  .002690  I  .4653447  .0002710  2.8431 0.1504  P    15.611     .500    -1.300     .300   .030000   .057000   .4656000      .000      .000  
+79 216 43920.00 I   .007190  .003310   .062380  .002690  I  .4624842  .0002710  2.8731 0.2580  P    15.492     .500    -1.344     .300   .026000   .058000   .4627000      .000      .000  
+79 217 43921.00 I   .004672  .001796   .062736  .000100  I  .4596078  .0004390  2.8750 0.2580  P    15.275     .500    -1.376     .300   .023000   .058000   .4598000      .000      .000  
+79 218 43922.00 I   .002142  .001796   .063115  .000100  I  .4567426  .0004390  2.8516 0.3104  P    14.975     .500    -1.477     .300   .019000   .059000   .4569000      .000      .000  
+79 219 43923.00 I  -.000401  .001796   .063517  .000100  I  .4539100  .0004390  2.8118 0.3104  P    14.755     .500    -1.605     .300   .016000   .059000   .4540000      .000      .000  
+79 220 43924.00 I  -.002956  .001796   .063940  .000100  I  .4511200  .0004390  2.7689 0.3104  P    14.791     .500    -1.625     .300   .012000   .060000   .4511000      .000      .000  
+79 221 43925.00 I  -.005524  .001796   .064384  .000100  I  .4483673  .0004390  2.7405 0.3104  P    14.998     .500    -1.509     .300   .008000   .061000   .4482000      .000      .000  
+79 222 43926.00 I  -.008108  .001796   .064848  .000100  I  .4456269  .0004390  2.7482 0.2580  P    15.060     .500    -1.412     .300   .005000   .062000   .4453000      .000      .000  
+79 223 43927.00 I  -.010711  .003310   .065332  .002690  I  .4428528  .0002710  2.8101 0.3388  P    14.807     .500    -1.483     .300   .001000   .062000   .4423000      .000      .000  
+79 224 43928.00 I  -.013336  .002521   .065834  .010697  I  .4399875  .0005162  2.9296 0.2915  P    14.447     .500    -1.657     .300  -.002000   .063000   .4394000      .000      .000  
+79 225 43929.00 I  -.015987  .002521   .066356  .010697  I  .4369807  .0005162  3.0877 0.3650  P    14.336     .500    -1.743     .300  -.006000   .064000   .4365000      .000      .000  
+79 226 43930.00 I  -.018666  .002521   .066895  .010697  I  .4338127  .0005162  3.2439 0.3650  P    14.622     .500    -1.695     .300  -.010000   .065000   .4336000      .000      .000  
+79 227 43931.00 I  -.021378  .002521   .067451  .010697  I  .4305092  .0005162  3.3512 0.3650  P    15.137     .500    -1.657     .300  -.014000   .066000   .4306000      .000      .000  
+79 228 43932.00 I  -.024122  .002521   .068028  .010697  I  .4271378  .0005162  3.3762 0.3650  P    15.544     .500    -1.728     .300  -.017000   .068000   .4277000      .000      .000  
+79 3 1 43933.00 I  -.026899  .002521   .068637  .010697  I  .4237864  .0005162  3.3125 0.2915  P    15.574     .500    -1.808     .300  -.021000   .069000   .4247000      .000      .000  
+79 3 2 43934.00 I  -.029706  .003310   .069293  .002690  I  .4205356  .0002710  3.1802 0.2772  P    15.252     .500    -1.774     .300  -.025000   .070000   .4218000      .000      .000  
+79 3 3 43935.00 I  -.032543  .003063   .070009  .006290  I  .4174369  .0002023  3.0150 0.1691  P    14.917     .500    -1.692     .300  -.029000   .072000   .4189000      .000      .000  
+79 3 4 43936.00 I  -.035406  .003063   .070797  .006290  I  .4145046  .0002023  2.8527 0.1430  P    14.896     .500    -1.750     .300  -.032000   .073000   .4159000      .000      .000  
+79 3 5 43937.00 I  -.038296  .003063   .071672  .006290  I  .4117220  .0002023  2.7191 0.1551  P    15.130     .500    -2.004     .300  -.036000   .075000   .4130000      .000      .000  
+79 3 6 43938.00 I  -.041208  .003977   .072647  .009776  I  .4090524  .0002352  2.6277 0.1551  P    15.287     .500    -2.314     .300  -.039000   .076000   .4100000      .000      .000  
+79 3 7 43939.00 I  -.044139  .003977   .073730  .009776  I  .4064513  .0002352  2.5823 0.1663  P    15.239     .500    -2.529     .300  -.043000   .078000   .4071000      .000      .000  
+79 3 8 43940.00 I  -.047081  .003977   .074925  .009776  I  .4038728  .0002352  2.5819 0.1737  P    15.231     .500    -2.627     .300  -.046000   .080000   .4041000      .000      .000  
+79 3 9 43941.00 I  -.050025  .004716   .076239  .012312  I  .4012742  .0002556  2.6215 0.1737  P    15.501     .500    -2.654     .300  -.050000   .082000   .4012000      .000      .000  
+79 310 43942.00 I  -.052969  .004716   .077670  .012312  I  .3986191  .0002556  2.6933 0.1807  P    15.948     .500    -2.622     .300  -.053000   .083000   .3982000      .000      .000  
+79 311 43943.00 I  -.055911  .004716   .079210  .012312  I  .3958803  .0002556  2.7869 0.1863  P    16.305     .500    -2.525     .300  -.057000   .085000   .3953000      .000      .000  
+79 312 43944.00 I  -.058854  .003310   .080847  .002690  I  .3930421  .0002710  2.8900 0.6688  P    16.484     .500    -2.410     .300  -.060000   .087000   .3923000      .000      .000  
+79 313 43945.00 I  -.061800  .008930   .082570  .000884  I  .3901010  .0013130  2.9909 0.6703  P    16.567     .500    -2.360     .300  -.063000   .089000   .3893000      .000      .000  
+79 314 43946.00 I  -.064750  .008930   .084368  .000884  I  .3870650  .0013130  3.0778 0.9284  P    16.592     .500    -2.402     .300  -.066000   .091000   .3863000      .000      .000  
+79 315 43947.00 I  -.067704  .008930   .086231  .000884  I  .3839535  .0013130  3.1403 0.9284  P    16.514     .500    -2.489     .300  -.070000   .093000   .3834000      .000      .000  
+79 316 43948.00 I  -.070664  .008930   .088147  .000884  I  .3807956  .0013130  3.1692 0.9284  P    16.332     .500    -2.572     .300  -.073000   .095000   .3804000      .000      .000  
+79 317 43949.00 I  -.073618  .008930   .090106  .000884  I  .3776271  .0013130  3.1620 0.9284  P    16.126     .500    -2.654     .300  -.076000   .097000   .3774000      .000      .000  
+79 318 43950.00 I  -.076549  .008930   .092098  .000884  I  .3744811  .0013130  3.1263 0.6697  P    15.965     .500    -2.761     .300  -.079000   .099000   .3744000      .000      .000  
+79 319 43951.00 I  -.079444  .010706   .094114  .008790  I  .3713788  .0002644  3.0778 0.6697  P    15.904     .500    -2.874     .300  -.082000   .101000   .3714000      .000      .000  
+79 320 43952.00 I  -.082286  .010706   .096143  .008790  I  .3683230  .0002644  3.0367 0.1708  P    15.990     .500    -2.939     .300  -.084000   .104000   .3683000      .000      .000  
+79 321 43953.00 I  -.085059  .008098   .098177  .006523  I  .3652961  .0002163  3.0235 0.1708  P    16.209     .500    -2.944     .300  -.087000   .106000   .3653000      .000      .000  
+79 322 43954.00 I  -.087749  .008098   .100204  .006523  I  .3622606  .0002163  3.0563 0.1529  P    16.442     .500    -2.956     .300  -.090000   .108000   .3623000      .000      .000  
+79 323 43955.00 I  -.090350  .008098   .102221  .006523  I  .3591650  .0002163  3.1445 0.1529  P    16.574     .500    -3.034     .300  -.093000   .110000   .3592000      .000      .000  
+79 324 43956.00 I  -.092865  .008098   .104229  .006523  I  .3559556  .0002163  3.2808 0.1327  P    16.610     .500    -3.124     .300  -.096000   .113000   .3562000      .000      .000  
+79 325 43957.00 I  -.095296  .004064   .106231  .002797  I  .3525963  .0001538  3.4382 0.4103  P    16.648     .500    -3.117     .300  -.098000   .115000   .3531000      .000      .000  
+79 326 43958.00 I  -.097642  .004889   .108233  .002308  I  .3490860  .0007915  3.5750 0.5597  P    16.753     .500    -3.014     .300  -.101000   .118000   .3501000      .000      .000  
+79 327 43959.00 I  -.099898  .005593   .110239  .001682  I  .3454675  .0011088  3.6483 0.6812  P    16.895     .500    -2.964     .300  -.104000   .120000   .3470000      .000      .000  
+79 328 43960.00 I  -.102064  .005593   .112255  .001682  I  .3418196  .0011088  3.6312 0.7840  P    16.969     .500    -3.080     .300  -.106000   .123000   .3439000      .000      .000  
+79 329 43961.00 I  -.104136  .005593   .114286  .001682  I  .3382344  .0011088  3.5262 0.7840  P    16.872     .500    -3.273     .300  -.109000   .125000   .3409000      .000      .000  
+79 330 43962.00 I  -.106120  .005593   .116336  .001682  I  .3347871  .0011088  3.3615 0.7840  P    16.630     .500    -3.355     .300  -.111000   .128000   .3378000      .000      .000  
+79 331 43963.00 I  -.108024  .005593   .118409  .001682  I  .3315186  .0011088  3.1751 0.5707  P    16.440     .500    -3.290     .300  -.114000   .130000   .3348000      .000      .000  
+79 4 1 43964.00 I  -.109856  .003310   .120506  .002690  I  .3284323  .0002710  3.0024 0.5853  P    16.487     .500    -3.238     .300  -.116000   .133000   .3317000      .000      .000  
+79 4 2 43965.00 I  -.111624  .004700   .122632  .003826  I  .3255016  .0003754  2.8664 0.2315  P    16.698     .500    -3.352     .300  -.118000   .136000   .3286000      .000      .000  
+79 4 3 43966.00 I  -.113335  .004700   .124790  .003826  I  .3226839  .0003754  2.7773 0.2654  P    16.815     .500    -3.605     .300  -.120000   .139000   .3255000      .000      .000  
+79 4 4 43967.00 I  -.114998  .004700   .126982  .003826  I  .3199310  .0003754  2.7364 0.2654  P    16.762     .500    -3.853     .300  -.123000   .141000   .3223000      .000      .000  
+79 4 5 43968.00 I  -.116619  .004700   .129213  .003826  I  .3171962  .0003754  2.7404 0.2654  P    16.775     .500    -3.993     .300  -.125000   .144000   .3192000      .000      .000  
+79 4 6 43969.00 I  -.118202  .004700   .131482  .003826  I  .3144374  .0003754  2.7830 0.2447  P    17.069     .500    -4.014     .300  -.127000   .147000   .3161000      .000      .000  
+79 4 7 43970.00 I  -.119746  .004700   .133788  .003826  I  .3116208  .0003141  2.8539 0.2407  P    17.518     .500    -3.947     .300  -.129000   .150000   .3130000      .000      .000  
+79 4 8 43971.00 I  -.121251  .003106   .136129  .000016  I  .3087243  .0003015  2.9407 0.2177  P    17.817     .500    -3.826     .300  -.131000   .153000   .3099000      .000      .000  
+79 4 9 43972.00 I  -.122714  .003106   .138504  .000016  I  .3057383  .0003015  3.0308 0.2132  P    17.856     .500    -3.697     .300  -.133000   .155000   .3068000      .000      .000  
+79 410 43973.00 I  -.124135  .003106   .140910  .000016  I  .3026656  .0003015  3.1123 0.2132  P    17.769     .500    -3.594     .300  -.135000   .158000   .3037000      .000      .000  
+79 411 43974.00 I  -.125512  .003106   .143345  .000016  I  .2995202  .0003015  3.1743 0.2728  P    17.678     .500    -3.526     .300  -.137000   .161000   .3006000      .000      .000  
+79 412 43975.00 I  -.126849  .003106   .145809  .000016  I  .2963268  .0004548  3.2068 0.3663  P    17.569     .500    -3.504     .300  -.139000   .164000   .2975000      .000      .000  
+79 413 43976.00 I  -.128150  .003106   .148298  .000016  I  .2931183  .0006677  3.2042 0.5035  P    17.426     .500    -3.558     .300  -.141000   .166000   .2943000      .000      .000  
+79 414 43977.00 I  -.129419  .003310   .150811  .002690  I  .2899290  .0008985  3.1698 0.4876  P    17.309     .500    -3.702     .300  -.142000   .169000   .2912000      .000      .000  
+79 415 43978.00 I  -.130661  .004198   .153347  .002319  I  .2867850  .0007109  3.1164 0.5729  P    17.260     .500    -3.888     .300  -.144000   .171000   .2880000      .000      .000  
+79 416 43979.00 I  -.131882  .004198   .155903  .002319  I  .2836958  .0007109  3.0640 0.5027  P    17.229     .500    -4.029     .300  -.146000   .174000   .2849000      .000      .000  
+79 417 43980.00 I  -.133085  .004198   .158477  .002319  I  .2806490  .0007109  3.0354 0.4443  P    17.165     .500    -4.083     .300  -.148000   .177000   .2818000      .000      .000  
+79 418 43981.00 I  -.134274  .003059   .161067  .002158  I  .2776110  .0005331  3.0486 0.3960  P    17.119     .500    -4.081     .300  -.149000   .179000   .2787000      .000      .000  
+79 419 43982.00 I  -.135449  .003059   .163669  .002158  I  .2745349  .0003493  3.1123 0.3187  P    17.205     .500    -4.086     .300  -.151000   .182000   .2755000      .000      .000  
+79 420 43983.00 I  -.136606  .003059   .166279  .002158  I  .2713707  .0003493  3.2229 0.1779  P    17.483     .500    -4.111     .300  -.152000   .184000   .2724000      .000      .000  
+79 421 43984.00 I  -.137741  .002933   .168891  .004908  I  .2680793  .0000678  3.3628 0.1779  P    17.885     .500    -4.101     .300  -.154000   .187000   .2693000      .000      .000  
+79 422 43985.00 I  -.138853  .002933   .171502  .004908  I  .2646455  .0000678  3.5016 0.0479  P    18.239     .500    -3.988     .300  -.155000   .190000   .2662000      .000      .000  
+79 423 43986.00 I  -.139941  .002933   .174110  .004908  I  .2610886  .0000678  3.6027 0.3528  P    18.370     .500    -3.795     .300  -.157000   .192000   .2631000      .000      .000  
+79 424 43987.00 I  -.141004  .005579   .176715  .006599  I  .2574631  .0007023  3.6346 0.3528  P    18.217     .500    -3.659     .300  -.158000   .195000   .2601000      .000      .000  
+79 425 43988.00 I  -.142046  .005579   .179318  .006599  I  .2538475  .0007023  3.5823 0.4966  P    17.874     .500    -3.714     .300  -.160000   .197000   .2570000      .000      .000  
+79 426 43989.00 I  -.143074  .005579   .181927  .006599  I  .2503237  .0007023  3.4544 0.6068  P    17.521     .500    -3.929     .300  -.161000   .200000   .2539000      .000      .000  
+79 427 43990.00 I  -.144092  .006793   .184548  .006546  I  .2469541  .0009898  3.2804 0.4967  P    17.320     .500    -4.122     .300  -.162000   .203000   .2506000      .000      .000  
+79 428 43991.00 I  -.145100  .005079   .187182  .004675  I  .2437655  .0007025  3.0986 0.6069  P    17.344     .500    -4.153     .300  -.163000   .205000   .2473000      .000      .000  
+79 429 43992.00 I  -.146089  .005079   .189825  .004675  I  .2407493  .0007025  2.9397 0.3539  P    17.534     .500    -4.077     .300  -.164000   .208000   .2440000      .000      .000  
+79 430 43993.00 I  -.147054  .002334   .192477  .000929  I  .2378725  .0000862  2.8217 0.3539  P    17.721     .500    -4.064     .300  -.165000   .210000   .2407000      .000      .000  
+79 5 1 43994.00 I  -.147988  .002334   .195134  .000929  I  .2350903  .0000862  2.7506 0.0610  P    17.755     .500    -4.198     .300  -.142000   .213000   .2374000      .000      .000  
+79 5 2 43995.00 I  -.148886  .002334   .197796  .000929  I  .2323562  .0000862  2.7248 0.0610  P    17.658     .500    -4.409     .300  -.143000   .215000   .2344000      .000      .000  
+79 5 3 43996.00 I  -.149745  .002334   .200461  .000929  I  .2296276  .0000862  2.7383 0.2460  P    17.615     .500    -4.573     .300  -.143000   .218000   .2314000      .000      .000  
+79 5 4 43997.00 I  -.150564  .003310   .203128  .002690  I  .2268695  .0004843  2.7822 0.1781  P    17.765     .500    -4.628     .300  -.144000   .220000   .2283000      .000      .000  
+79 5 5 43998.00 I  -.151339  .001531   .205796  .000143  I  .2240570  .0003456  2.8447 0.2915  P    18.027     .500    -4.594     .300  -.144000   .223000   .2253000      .000      .000  
+79 5 6 43999.00 I  -.152070  .001531   .208464  .000143  I  .2211782  .0003245  2.9127 0.2489  P    18.196     .500    -4.521     .300  -.145000   .225000   .2223000      .000      .000  
+79 5 7 44000.00 I  -.152753  .001531   .211131  .000143  I  .2182336  .0003584  2.9745 0.2417  P    18.152     .500    -4.440     .300  -.145000   .227000   .2194000      .000      .000  
+79 5 8 44001.00 I  -.153387  .001531   .213797  .000143  I  .2152346  .0003584  3.0199 0.2534  P    17.938     .500    -4.343     .300  -.146000   .230000   .2164000      .000      .000  
+79 5 9 44002.00 I  -.153973  .001531   .216460  .000143  I  .2122023  .0003584  3.0398 0.2433  P    17.668     .500    -4.200     .300  -.146000   .232000   .2135000      .000      .000  
+79 510 44003.00 I  -.154510  .001531   .219120  .000143  I  .2091655  .0003292  3.0282 0.3234  P    17.430     .500    -4.039     .300  -.147000   .235000   .2105000      .000      .000  
+79 511 44004.00 I  -.154999  .000312   .221774  .002626  I  .2061571  .0005385  2.9830 0.3439  P    17.280     .500    -3.969     .300  -.147000   .237000   .2076000      .000      .000  
+79 512 44005.00 I  -.155442  .000312   .224422  .002626  I  .2032085  .0006039  2.9106 0.4196  P    17.239     .500    -4.081     .300  -.147000   .239000   .2047000      .000      .000  
+79 513 44006.00 I  -.155839  .000312   .227063  .002626  I  .2003390  .0006437  2.8287 0.4413  P    17.267     .500    -4.329     .300  -.148000   .242000   .2019000      .000      .000  
+79 514 44007.00 I  -.156190  .000312   .229696  .002626  I  .1975461  .0006437  2.7619 0.3991  P    17.257     .500    -4.539     .300  -.148000   .244000   .1990000      .000      .000  
+79 515 44008.00 I  -.156498  .000222   .232317  .002459  I  .1948025  .0004720  2.7332 0.3991  P    17.119     .500    -4.575     .300  -.149000   .247000   .1962000      .000      .000  
+79 516 44009.00 I  -.156760  .000222   .234922  .002459  I  .1920627  .0004720  2.7555 0.2673  P    16.876     .500    -4.455     .300  -.149000   .249000   .1933000      .000      .000  
+79 517 44010.00 I  -.156979  .001591   .237507  .007610  I  .1892751  .0002512  2.8271 0.2673  P    16.676     .500    -4.300     .300  -.149000   .251000   .1905000      .000      .000  
+79 518 44011.00 I  -.157154  .001591   .240066  .007610  I  .1863978  .0002512  2.9307 0.1776  P    16.693     .500    -4.203     .300  -.149000   .253000   .1878000      .000      .000  
+79 519 44012.00 I  -.157284  .001591   .242593  .007610  I  .1834122  .0002512  3.0386 0.1776  P    16.984     .500    -4.158     .300  -.149000   .256000   .1850000      .000      .000  
+79 520 44013.00 I  -.157372  .001591   .245080  .007610  I  .1803290  .0002512  3.1211 0.1970  P    17.407     .500    -4.098     .300  -.149000   .258000   .1823000      .000      .000  
+79 521 44014.00 I  -.157415  .001962   .247523  .007616  I  .1771873  .0003035  3.1516 0.1970  P    17.678     .500    -3.985     .300  -.149000   .260000   .1795000      .000      .000  
+79 522 44015.00 I  -.157410  .001962   .249924  .007616  I  .1740488  .0003035  3.1133 0.2128  P    17.565     .500    -3.866     .300  -.149000   .262000   .1768000      .000      .000  
+79 523 44016.00 I  -.157353  .001625   .252291  .002321  I  .1709837  .0002984  3.0062 0.2128  P    17.079     .500    -3.841     .300  -.149000   .264000   .1742000      .000      .000  
+79 524 44017.00 I  -.157240  .001625   .254629  .002321  I  .1680538  .0002984  2.8470 0.2110  P    16.499     .500    -3.953     .300  -.149000   .267000   .1715000      .000      .000  
+79 525 44018.00 I  -.157071  .001625   .256943  .002321  I  .1652975  .0002984  2.6643 0.3381  P    16.161     .500    -4.121     .300  -.149000   .269000   .1689000      .000      .000  
+79 526 44019.00 I  -.156842  .002407   .259234  .002062  I  .1627230  .0006069  2.4881 0.4291  P    16.197     .500    -4.217     .300  -.149000   .271000   .1662000      .000      .000  
+79 527 44020.00 I  -.156555  .002991   .261507  .001764  I  .1603115  .0008047  2.3412 0.5040  P    16.450     .500    -4.209     .300  -.149000   .273000   .1637000      .000      .000  
+79 528 44021.00 I  -.156206  .002991   .263765  .001764  I  .1580270  .0008047  2.2350 0.5575  P    16.656     .500    -4.185     .300  -.149000   .275000   .1611000      .000      .000  
+79 529 44022.00 I  -.155797  .002821   .266011  .001362  I  .1558269  .0007719  2.1725 0.5575  P    16.690     .500    -4.236     .300  -.148000   .278000   .1586000      .000      .000  
+79 530 44023.00 I  -.155330  .002821   .268245  .001362  I  .1536684  .0007719  2.1509 0.5458  P    16.608     .500    -4.356     .300  -.148000   .280000   .1560000      .000      .000  
+79 531 44024.00 I  -.154810  .002821   .270470  .001362  I  .1515140  .0007719  2.1627 0.5339  P    16.509     .500    -4.476     .300  -.148000   .282000   .1535000      .000      .000  
+79 6 1 44025.00 I  -.154242  .002639   .272685  .000771  I  .1493351  .0007377  2.1983 0.5339  P    16.445     .500    -4.546     .300  -.147000   .284000   .1511000      .000      .000  
+79 6 2 44026.00 I  -.153628  .002639   .274892  .000771  I  .1471131  .0007377  2.2470 0.5216  P    16.437     .500    -4.562     .300  -.147000   .286000   .1486000      .000      .000  
+79 6 3 44027.00 I  -.152969  .002639   .277094  .000771  I  .1448408  .0007377  2.2968 0.3930  P    16.474     .500    -4.542     .300  -.146000   .289000   .1462000      .000      .000  
+79 6 4 44028.00 I  -.152263  .003310   .279291  .002690  I  .1425226  .0002710  2.3373 0.4250  P    16.448     .500    -4.499     .300  -.146000   .291000   .1437000      .000      .000  
+79 6 5 44029.00 I  -.151511  .003310   .281486  .002690  I  .1401719  .0004222  2.3607 0.3096  P    16.196     .500    -4.419     .300  -.145000   .293000   .1413000      .000      .000  
+79 6 6 44030.00 I  -.150712  .003618   .283680  .001226  I  .1378086  .0005568  2.3620 0.3494  P    15.694     .500    -4.266     .300  -.144000   .295000   .1389000      .000      .000  
+79 6 7 44031.00 I  -.149866  .003618   .285876  .001226  I  .1354564  .0005568  2.3380 0.3937  P    15.157     .500    -4.032     .300  -.143000   .298000   .1365000      .000      .000  
+79 6 8 44032.00 I  -.148972  .003618   .288076  .001226  I  .1331408  .0005568  2.2894 0.3937  P    14.844     .500    -3.813     .300  -.142000   .300000   .1341000      .000      .000  
+79 6 9 44033.00 I  -.148031  .003618   .290280  .001226  I  .1308829  .0005568  2.2248 0.3937  P    14.802     .500    -3.765     .300  -.141000   .303000   .1317000      .000      .000  
+79 610 44034.00 I  -.147047  .003618   .292494  .001226  I  .1286901  .0005568  2.1634 0.4116  P    14.870     .500    -3.941     .300  -.140000   .305000   .1293000      .000      .000  
+79 611 44035.00 I  -.146027  .003618   .294720  .001226  I  .1265461  .0006064  2.1320 0.3350  P    14.886     .500    -4.183     .300  -.139000   .307000   .1270000      .000      .000  
+79 612 44036.00 I  -.144979  .001747   .296961  .005060  I  .1244090  .0003725  2.1519 0.3643  P    14.804     .500    -4.255     .300  -.138000   .310000   .1246000      .000      .000  
+79 613 44037.00 I  -.143907  .001747   .299222  .005060  I  .1222235  .0004038  2.2280 0.2747  P    14.634     .500    -4.078     .300  -.137000   .312000   .1223000      .000      .000  
+79 614 44038.00 I  -.142819  .001747   .301505  .005060  I  .1199394  .0004038  2.3447 0.2855  P    14.393     .500    -3.792     .300  -.136000   .315000   .1199000      .000      .000  
+79 615 44039.00 I  -.141721  .001747   .303814  .005060  I  .1175309  .0004038  2.4711 0.2855  P    14.146     .500    -3.603     .300  -.135000   .317000   .1176000      .000      .000  
+79 616 44040.00 I  -.140619  .001747   .306150  .005060  I  .1150055  .0004038  2.5727 0.2855  P    14.028     .500    -3.593     .300  -.134000   .319000   .1154000      .000      .000  
+79 617 44041.00 I  -.139515  .001747   .308510  .005060  I  .1124028  .0004038  2.6226 0.2438  P    14.118     .500    -3.693     .300  -.133000   .321000   .1131000      .000      .000  
+79 618 44042.00 I  -.138413  .003956   .310891  .003754  I  .1097815  .0002732  2.6090 0.2090  P    14.316     .500    -3.789     .300  -.132000   .324000   .1109000      .000      .000  
+79 619 44043.00 I  -.137315  .003956   .313290  .003754  I  .1072050  .0001084  2.5348 0.1470  P    14.369     .500    -3.826     .300  -.131000   .326000   .1086000      .000      .000  
+79 620 44044.00 I  -.136226  .003956   .315703  .003754  I  .1047273  .0001084  2.4145 0.0767  P    14.079     .500    -3.828     .300  -.130000   .328000   .1064000      .000      .000  
+79 621 44045.00 I  -.135149  .003956   .318127  .003754  I  .1023839  .0001084  2.2707 0.0666  P    13.513     .500    -3.851     .300  -.129000   .330000   .1042000      .000      .000  
+79 622 44046.00 I  -.134082  .003280   .320558  .005181  I  .1001851  .0000773  2.1294 0.0666  P    12.983     .500    -3.917     .300  -.128000   .332000   .1020000      .000      .000  
+79 623 44047.00 I  -.133023  .003280   .322987  .005181  I  .0981170  .0000773  2.0124 0.0393  P    12.767     .500    -4.005     .300  -.126000   .335000   .0997000      .000      .000  
+79 624 44048.00 I  -.131967  .002422   .325406  .006292  I  .0961475  .0000140  1.9337 0.0393  P    12.865     .500    -4.084     .300  -.125000   .337000   .0975000      .000      .000  
+79 625 44049.00 I  -.130909  .002422   .327807  .006292  I  .0942354  .0000140  1.8975 0.0899  P    13.061     .500    -4.139     .300  -.124000   .339000   .0953000      .000      .000  
+79 626 44050.00 I  -.129846  .002407   .330183  .004512  I  .0923394  .0001792  1.9007 0.0899  P    13.187     .500    -4.159     .300  -.123000   .341000   .0932000      .000      .000  
+79 627 44051.00 I  -.128778  .002407   .332535  .004512  I  .0904232  .0001792  1.9364 0.1551  P    13.209     .500    -4.140     .300  -.122000   .343000   .0910000      .000      .000  
+79 628 44052.00 I  -.127706  .002392   .334862  .001059  I  .0884588  .0002531  1.9954 0.1551  P    13.114     .500    -4.110     .300  -.120000   .345000   .0889000      .000      .000  
+79 629 44053.00 I  -.126628  .002392   .337163  .001059  I  .0864284  .0002531  2.0663 0.1559  P    12.886     .500    -4.119     .300  -.119000   .347000   .0867000      .000      .000  
+79 630 44054.00 I  -.125542  .002155   .339439  .000950  I  .0843264  .0001821  2.1365 0.1559  P    12.635     .500    -4.176     .300  -.118000   .349000   .0846000      .000      .000  
+79 7 1 44055.00 I  -.124442  .002155   .341688  .000950  I  .0821596  .0001821  2.1941 0.0941  P    12.542     .500    -4.232     .300  -.117000   .351000   .0825000      .000      .000  
+79 7 2 44056.00 I  -.123326  .001888   .343909  .000826  I  .0799453  .0000477  2.2306 0.0941  P    12.554     .500    -4.237     .300  -.116000   .353000   .0805000      .000      .000  
+79 7 3 44057.00 I  -.122188  .001888   .346100  .000826  I  .0777069  .0000477  2.2417 0.0337  P    12.335     .500    -4.194     .300  -.114000   .355000   .0784000      .000      .000  
+79 7 4 44058.00 I  -.121023  .001888   .348261  .000826  I  .0754707  .0000477  2.2263 0.0337  P    11.678     .500    -4.108     .300  -.113000   .357000   .0764000      .000      .000  
+79 7 5 44059.00 I  -.119823  .001888   .350392  .000826  I  .0732628  .0000477  2.1852 0.0244  P    10.853     .500    -3.942     .300  -.112000   .359000   .0743000      .000      .000  
+79 7 6 44060.00 I  -.118580  .003037   .352492  .002178  I  .0711070  .0000099  2.1238 0.0244  P    10.345     .500    -3.697     .300  -.111000   .361000   .0723000      .000      .000  
+79 7 7 44061.00 I  -.117286  .003037   .354562  .002178  I  .0690175  .0000099  2.0555 0.0070  P    10.295     .500    -3.506     .300  -.109000   .363000   .0703000      .000      .000  
+79 7 8 44062.00 I  -.115933  .003037   .356601  .002178  I  .0669908  .0000099  2.0028 0.0070  P    10.425     .500    -3.526     .300  -.108000   .364000   .0684000      .000      .000  
+79 7 9 44063.00 I  -.114514  .003037   .358609  .002178  I  .0649984  .0000099  1.9908 0.0070  P    10.456     .500    -3.727     .300  -.106000   .366000   .0664000      .000      .000  
+79 710 44064.00 I  -.113023  .003037   .360584  .002178  I  .0629905  .0000099  2.0351 0.2899  P    10.384     .500    -3.876     .300  -.105000   .368000   .0644000      .000      .000  
+79 711 44065.00 I  -.111458  .002152   .362525  .002131  I  .0609115  .0005797  2.1296 0.4099  P    10.318     .500    -3.790     .300  -.103000   .370000   .0624000      .000      .000  
+79 712 44066.00 I  -.109817  .000193   .364427  .002082  I  .0587243  .0008197  2.2451 0.5020  P    10.231     .500    -3.546     .300  -.101000   .371000   .0605000      .000      .000  
+79 713 44067.00 I  -.108098  .000193   .366288  .002082  I  .0564281  .0008197  2.3401 0.5796  P    10.016     .500    -3.371     .300  -.100000   .373000   .0585000      .000      .000  
+79 714 44068.00 I  -.106300  .000193   .368105  .002082  I  .0540628  .0008197  2.3790 0.5796  P     9.674     .500    -3.389     .300  -.098000   .374000   .0566000      .000      .000  
+79 715 44069.00 I  -.104419  .000193   .369877  .002082  I  .0516943  .0008197  2.3452 0.5796  P     9.359     .500    -3.533     .300  -.096000   .376000   .0546000      .000      .000  
+79 716 44070.00 I  -.102454  .000193   .371605  .002082  I  .0493954  .0008197  2.2423 0.4196  P     9.234     .500    -3.688     .300  -.094000   .377000   .0527000      .000      .000  
+79 717 44071.00 I  -.100404  .005905   .373287  .005117  I  .0472259  .0001794  2.0909 0.4196  P     9.299     .500    -3.811     .300  -.092000   .379000   .0507000      .000      .000  
+79 718 44072.00 I  -.098267  .005905   .374923  .005117  I  .0452206  .0001794  1.9187 0.1269  P     9.356     .500    -3.913     .300  -.090000   .380000   .0488000      .000      .000  
+79 719 44073.00 I  -.096040  .005905   .376514  .005117  I  .0433866  .0001794  1.7523 0.1269  P     9.183     .500    -3.994     .300  -.088000   .382000   .0468000      .000      .000  
+79 720 44074.00 I  -.093725  .005905   .378060  .005117  I  .0417069  .0001794  1.6132 0.1269  P     8.783     .500    -4.049     .300  -.086000   .383000   .0449000      .000      .000  
+79 721 44075.00 I  -.091327  .005905   .379565  .005117  I  .0401465  .0001794  1.5153 0.1269  P     8.405     .500    -4.110     .300  -.084000   .384000   .0430000      .000      .000  
+79 722 44076.00 I  -.088856  .005905   .381037  .005117  I  .0386609  .0001794  1.4637 0.1190  P     8.286     .500    -4.208     .300  -.081000   .386000   .0410000      .000      .000  
+79 723 44077.00 I  -.086321  .001209   .382482  .000154  I  .0372045  .0001564  1.4560 0.1190  P     8.427     .500    -4.304     .300  -.079000   .387000   .0391000      .000      .000  
+79 724 44078.00 I  -.083733  .001209   .383905  .000154  I  .0357367  .0001564  1.4850 0.1106  P     8.660     .500    -4.293     .300  -.075990   .389000   .0371000      .000      .000  
+79 725 44079.00 I  -.081101  .001209   .385315  .000154  I  .0342253  .0001564  1.5416 0.1106  P     8.822     .500    -4.127     .300  -.074000   .390000   .0352000      .000      .000  
+79 726 44080.00 I  -.078433  .001209   .386717  .000154  I  .0326479  .0001564  1.6152 0.1106  P     8.799     .500    -3.905     .300  -.071000   .391000   .0333000      .000      .000  
+79 727 44081.00 I  -.075739  .001209   .388117  .000154  I  .0309929  .0001564  1.6950 0.1320  P     8.553     .500    -3.801     .300  -.069000   .392000   .0313990      .000      .000  
+79 728 44082.00 I  -.073026  .002451   .389522  .006269  I  .0292595  .0002128  1.7700 0.1505  P     8.225     .500    -3.882     .300  -.066000   .394000   .0294000      .000      .000  
+79 729 44083.00 I  -.070302  .003248   .390939  .008864  I  .0274579  .0002571  1.8300 0.1669  P     8.073     .500    -4.039     .300  -.064000   .395000   .0275000      .000      .000  
+79 730 44084.00 I  -.067573  .003248   .392373  .008864  I  .0256065  .0002571  1.8692 0.2934  P     8.128     .500    -4.130     .300  -.061000   .396000   .0256000      .000      .000  
+79 731 44085.00 I  -.064847  .002315   .393830  .006471  I  .0237269  .0005275  1.8863 0.2934  P     8.041     .500    -4.142     .300  -.058000   .397000   .0236000      .000      .000  
+79 8 1 44086.00 I  -.062126  .002315   .395308  .006471  I  .0218404  .0005275  1.8838 0.3730  P     7.489     .500    -4.138     .300  -.055000   .398000   .0216000      .000      .000  
+79 8 2 44087.00 I  -.059411  .002315   .396802  .006471  I  .0199639  .0005275  1.8676 0.4383  P     6.665     .500    -4.105     .300  -.053000   .400000   .0197000      .000      .000  
+79 8 3 44088.00 I  -.056703  .000406   .398306  .002277  I  .0181066  .0007002  1.8473 0.4475  P     6.111     .500    -3.972     .300  -.050000   .401000   .0177000      .000      .000  
+79 8 4 44089.00 I  -.054003  .000406   .399814  .002277  I  .0162656  .0007231  1.8379 0.5617  P     6.076     .500    -3.777     .300  -.047000   .402000   .0156990      .000      .000  
+79 8 5 44090.00 I  -.051312  .000406   .401319  .002277  I  .0144201  .0008785  1.8604 0.6357  P     6.280     .500    -3.693     .300  -.044000   .403000   .0137000      .000      .000  
+79 8 6 44091.00 I  -.048629  .003310   .402812  .002690  I  .0125271  .0010458  1.9359 0.6903  P     6.349     .500    -3.803     .300  -.041000   .404000   .0117000      .000      .000  
+79 8 7 44092.00 I  -.045956  .007342   .404284  .001414  I  .0105286  .0010651  2.0705 0.7463  P     6.239     .500    -3.963     .300  -.039000   .405000   .0096000      .000      .000  
+79 8 8 44093.00 I  -.043291  .007342   .405727  .001414  I  .0083720  .0010651  2.2473 0.7531  P     6.139     .500    -3.973     .300  -.035990   .406000   .0076000      .000      .000  
+79 8 9 44094.00 I  -.040636  .007342   .407132  .001414  I  .0060328  .0010651  2.4276 0.7798  P     6.130     .500    -3.837     .300  -.033000   .407000   .0056000      .000      .000  
+79 810 44095.00 I  -.037989  .007342   .408492  .001414  I  .0035321  .0011392  2.5620 0.7590  P     6.091     .500    -3.730     .300  -.030000   .408000   .0035000      .000      .000  
+79 811 44096.00 I  -.035345  .007342   .409799  .001414  I  .0009371  .0010817  2.6115 0.7855  P     5.853     .500    -3.731     .300  -.027000   .409000   .0015000      .000      .000  
+79 812 44097.00 I  -.032689  .007342   .411047  .001414  I -.0016589  .0010817  2.5651 0.7414  P     5.401     .500    -3.746     .300  -.025000   .409000  -.0006000      .000      .000  
+79 813 44098.00 I  -.030008  .013625   .412228  .008250  I -.0041672  .0010141  2.4411 0.7414  P     4.945     .500    -3.700     .300  -.022000   .410000  -.0026000      .000      .000  
+79 814 44099.00 I  -.027287  .013625   .413337  .008250  I -.0065267  .0010141  2.2741 0.7171  P     4.781     .500    -3.683     .300  -.018990   .411000  -.0047000      .000      .000  
+79 815 44100.00 I  -.024514  .013625   .414367  .008250  I -.0087132  .0010141  2.1006 0.7171  P     4.988     .500    -3.813     .300  -.016000   .412000  -.0068000      .000      .000  
+79 816 44101.00 I  -.021675  .013625   .415312  .008250  I -.0107356  .0010141  1.9502 0.7171  P     5.287     .500    -4.058     .300  -.013000   .413000  -.0089000      .000      .000  
+79 817 44102.00 I  -.018775  .013625   .416172  .008250  I -.0126275  .0010141  1.8416 0.7171  P     5.326     .500    -4.268     .300  -.011000   .413000  -.0110000      .000      .000  
+79 818 44103.00 I  -.015826  .013625   .416953  .008250  I -.0144346  .0010141  1.7805 0.5248  P     5.090     .500    -4.374     .300  -.008000   .414000  -.0131000      .000      .000  
+79 819 44104.00 I  -.012839  .003310   .417659  .002690  I -.0162036  .0002710  1.7647 0.5248  P     4.893     .500    -4.427     .300  -.005000   .415000  -.0152000      .000      .000  
+79 820 44105.00 I  -.009828  .003310   .418293  .002690  I -.0179767  .0002710  1.7869 0.1916  P     4.971     .500    -4.455     .300  -.002000   .416000  -.0173000      .000      .000  
+79 821 44106.00 I  -.006804  .003310   .418860  .002690  I -.0197865  .0002710  1.8365 0.1916  P     5.245     .500    -4.378     .300   .000000   .416000  -.0194000      .000      .000  
+79 822 44107.00 I  -.003779  .003310   .419365  .002690  I -.0216552  .0002710  1.9025 0.1916  P     5.486     .500    -4.127     .300   .003000   .417000  -.0215000      .000      .000  
+79 823 44108.00 I  -.000767  .003310   .419812  .002690  I -.0235931  .0002710  1.9731 0.4315  P     5.543     .500    -3.798     .300   .005000   .417000  -.0236000      .000      .000  
+79 824 44109.00 I   .002222  .007366   .420205  .003760  I -.0255994  .0008193  2.0375 0.4315  P     5.404     .500    -3.604     .300   .008000   .418000  -.0257000      .000      .000  
+79 825 44110.00 I   .005175  .007366   .420548  .003760  I -.0276627  .0008193  2.0856 0.5793  P     5.182     .500    -3.653     .300   .011000   .418000  -.0278000      .000      .000  
+79 826 44111.00 I   .008079  .007366   .420847  .003760  I -.0297624  .0008193  2.1091 0.5793  P     5.069     .500    -3.841     .300   .013000   .419000  -.0300000      .000      .000  
+79 827 44112.00 I   .010923  .007366   .421105  .003760  I -.0318720  .0008193  2.1059 0.5793  P     5.142     .500    -3.984     .300   .016000   .419000  -.0321000      .000      .000  
+79 828 44113.00 I   .013703  .007366   .421323  .003760  I -.0339667  .0008193  2.0803 0.5793  P     5.199     .500    -4.027     .300   .017990   .420000  -.0343000      .000      .000  
+79 829 44114.00 I   .016420  .007366   .421501  .003760  I -.0360279  .0008193  2.0405 0.4315  P     4.952     .500    -4.047     .300   .021000   .420000  -.0364000      .000      .000  
+79 830 44115.00 I   .019077  .003310   .421639  .002690  I -.0380466  .0002710  1.9975 0.5166  P     4.426     .500    -4.087     .300   .024000   .420000  -.0386000      .000      .000  
+79 831 44116.00 I   .021674  .003310   .421735  .002690  I -.0400268  .0006294  1.9662 0.3426  P     3.990     .500    -4.086     .300   .026000   .420000  -.0407000      .000      .000  
+79 9 1 44117.00 I   .024215  .003310   .421790  .002690  I -.0419887  .0006294  1.9641 0.4451  P     3.920     .500    -4.009     .300   .029000   .420000  -.0429000      .000      .000  
+79 9 2 44118.00 I   .026700  .003310   .421802  .002690  I -.0439715  .0006294  2.0113 0.3967  P     4.098     .500    -3.943     .300   .031000   .420000  -.0450000      .000      .000  
+79 9 3 44119.00 I   .029132  .001039   .421771  .000637  I -.0460322  .0004830  2.1210 0.3828  P     4.224     .500    -3.970     .300   .034000   .420000  -.0472000      .000      .000  
+79 9 4 44120.00 I   .031511  .001039   .421697  .000637  I -.0482331  .0004360  2.2896 0.3535  P     4.180     .500    -4.024     .300   .037000   .420000  -.0494000      .000      .000  
+79 9 5 44121.00 I   .033842  .001039   .421579  .000637  I -.0506223  .0005163  2.4909 0.3147  P     4.083     .500    -3.977     .300   .039000   .420000  -.0516000      .000      .000  
+79 9 6 44122.00 I   .036124  .001039   .421415  .000637  I -.0532098  .0004540  2.6771 0.3438  P     4.068     .500    -3.844     .300   .042000   .419000  -.0539000      .000      .000  
+79 9 7 44123.00 I   .038362  .001039   .421208  .000637  I -.0559546  .0004540  2.7978 0.3137  P     4.134     .500    -3.770     .300   .044000   .419000  -.0561000      .000      .000  
+79 9 8 44124.00 I   .040559  .001039   .420956  .000637  I -.0587727  .0004329  2.8205 0.2920  P     4.123     .500    -3.780     .300   .047000   .419000  -.0583000      .000      .000  
+79 9 9 44125.00 I   .042720  .001895   .420659  .004941  I -.0615625  .0003672  2.7438 0.2940  P     3.855     .500    -3.719     .300   .049000   .419000  -.0606000      .000      .000  
+79 910 44126.00 I   .044847  .001895   .420319  .004941  I -.0642366  .0003979  2.5961 0.2403  P     3.364     .500    -3.491     .300   .052000   .419000  -.0627990      .000      .000  
+79 911 44127.00 I   .046946  .003521   .419936  .003727  I -.0667455  .0003102  2.4210 0.2582  P     2.996     .500    -3.273     .300   .054000   .418000  -.0651000      .000      .000  
+79 912 44128.00 I   .049019  .003521   .419508  .003727  I -.0690829  .0003293  2.2589 0.2262  P     3.090     .500    -3.333     .300   .057000   .418000  -.0673000      .000      .000  
+79 913 44129.00 I   .051074  .003521   .419032  .003727  I -.0712768  .0003293  2.1372 0.2366  P     3.552     .500    -3.698     .300   .059000   .418000  -.0696000      .000      .000  
+79 914 44130.00 I   .053117  .003521   .418502  .003727  I -.0733744  .0003397  2.0668 0.3102  P     3.930     .500    -4.120     .300   .061000   .418000  -.0720000      .000      .000  
+79 915 44131.00 I   .055153  .003632   .417912  .005270  I -.0754270  .0005259  2.0462 0.3237  P     3.963     .500    -4.359     .300   .063000   .418000  -.0743000      .000      .000  
+79 916 44132.00 I   .057180  .003632   .417258  .005270  I -.0774808  .0005511  2.0676 0.4205  P     3.858     .500    -4.392     .300   .066000   .417000  -.0767000      .000      .000  
+79 917 44133.00 I   .059199  .002276   .416536  .007223  I -.0795726  .0006562  2.1202 0.3791  P     3.938     .500    -4.314     .300   .068000   .417000  -.0790000      .000      .000  
+79 918 44134.00 I   .061206  .001611   .415743  .005343  I -.0817274  .0005207  2.1915 0.4188  P     4.217     .500    -4.163     .300   .070000   .417000  -.0814000      .000      .000  
+79 919 44135.00 I   .063200  .001611   .414878  .005343  I -.0839581  .0005207  2.2703 0.3682  P     4.478     .500    -3.911     .300   .071990   .416000  -.0838000      .000      .000  
+79 920 44136.00 I   .065176  .001611   .413944  .005343  I -.0862669  .0005207  2.3458 0.3309  P     4.594     .500    -3.602     .300   .074000   .416000  -.0863000      .000      .000  
+79 921 44137.00 I   .067126  .000173   .412949  .001668  I -.0886452  .0004086  2.4079 0.3309  P     4.603     .500    -3.367     .300   .075990   .415000  -.0887000      .000      .000  
+79 922 44138.00 I   .069047  .000173   .411900  .001668  I -.0910757  .0004086  2.4489 0.2889  P     4.566     .500    -3.305     .300   .078000   .415000  -.0912000      .000      .000  
+79 923 44139.00 I   .070931  .000173   .410806  .001668  I -.0935341  .0004086  2.4632 0.2895  P     4.529     .500    -3.379     .300   .080000   .414000  -.0936000      .000      .000  
+79 924 44140.00 I   .072773  .003214   .409675  .002695  I -.0959929  .0004103  2.4499 0.2895  P     4.554     .500    -3.472     .300   .082000   .413000  -.0961000      .000      .000  
+79 925 44141.00 I   .074567  .003214   .408515  .002695  I -.0984269  .0004103  2.4156 0.3003  P     4.637     .500    -3.519     .300   .083000   .412000  -.0987000      .000      .000  
+79 926 44142.00 I   .076307  .002742   .407335  .003737  I -.1008215  .0004387  2.3734 0.2952  P     4.656     .500    -3.547     .300   .085000   .412000  -.1012000      .000      .000  
+79 927 44143.00 I   .077989  .003355   .406142  .004542  I -.1031763  .0004245  2.3391 0.3052  P     4.516     .500    -3.594     .300   .086000   .411000  -.1038000      .000      .000  
+79 928 44144.00 I   .079608  .003355   .404944  .004542  I -.1055084  .0004245  2.3308 0.3002  P     4.301     .500    -3.649     .300   .088000   .410000  -.1063000      .000      .000  
+79 929 44145.00 I   .081169  .003355   .403754  .004542  I -.1078519  .0004245  2.3645 0.2806  P     4.197     .500    -3.676     .300   .089000   .409000  -.1089000      .000      .000  
+79 930 44146.00 I   .082675  .002602   .402580  .006364  I -.1102558  .0003670  2.4532 0.2806  P     4.278     .500    -3.677     .300   .091000   .408000  -.1116000      .000      .000  
+7910 1 44147.00 I   .084132  .002602   .401428  .006364  I -.1127779  .0003670  2.6005 0.1858  P     4.464     .500    -3.653     .300   .092000   .408000  -.1142000      .000      .000  
+7910 2 44148.00 I   .085546  .002654   .400303  .005275  I -.1154722  .0000578  2.7938 0.1858  P     4.628     .500    -3.551     .300   .094000   .407000  -.1169000      .000      .000  
+7910 3 44149.00 I   .086922  .002654   .399210  .005275  I -.1183698  .0000578  3.0000 0.0409  P     4.709     .500    -3.330     .300   .095000   .406000  -.1195000      .000      .000  
+7910 4 44150.00 I   .088261  .002654   .398147  .005275  I -.1214611  .0000578  3.1727 0.0409  P     4.736     .500    -3.081     .300   .096000   .405000  -.1222000      .000      .000  
+7910 5 44151.00 I   .089562  .002654   .397110  .005275  I -.1246893  .0000578  3.2675 0.1876  P     4.787     .500    -2.976     .300   .097000   .404000  -.1249000      .000      .000  
+7910 6 44152.00 I   .090826  .002995   .396094  .001458  I -.1279624  .0003707  3.2612 0.1876  P     4.870     .500    -3.047     .300   .098000   .404000  -.1275000      .000      .000  
+7910 7 44153.00 I   .092056  .002995   .395093  .001458  I -.1311808  .0003707  3.1622 0.3034  P     4.861     .500    -3.100     .300   .099000   .403000  -.1302000      .000      .000  
+7910 8 44154.00 I   .093254  .003937   .394100  .001496  I -.1342675  .0004805  3.0054 0.2817  P     4.659     .500    -2.948     .300   .100000   .402000  -.1329000      .000      .000  
+7910 9 44155.00 I   .094421  .003937   .393111  .001496  I -.1371874  .0004242  2.8360 0.3205  P     4.418     .500    -2.693     .300   .101000   .401000  -.1356000      .000      .000  
+791010 44156.00 I   .095564  .003937   .392119  .001496  I -.1399479  .0004242  2.6916 0.2743  P     4.450     .500    -2.643     .300   .102000   .400000  -.1383000      .000      .000  
+791011 44157.00 I   .096691  .003162   .391120  .001447  I -.1425862  .0003480  2.5938 0.2187  P     4.829     .500    -2.943     .300   .103000   .400000  -.1409000      .000      .000  
+791012 44158.00 I   .097813  .002121   .390111  .001397  I -.1451529  .0001063  2.5481 0.1819  P     5.266     .500    -3.407     .300   .104000   .399000  -.1436000      .000      .000  
+791013 44159.00 I   .098939  .002121   .389088  .001397  I -.1476981  .0001063  2.5495 0.0776  P     5.486     .500    -3.723     .300   .105000   .398000  -.1463000      .000      .000  
+791014 44160.00 I   .100079  .002121   .388046  .001397  I -.1502641  .0001131  2.5875 0.0840  P     5.558     .500    -3.764     .300   .106000   .397000  -.1490000      .000      .000  
+791015 44161.00 I   .101241  .001834   .386981  .001811  I -.1528811  .0001301  2.6495 0.0862  P     5.720     .500    -3.616     .300   .107000   .396000  -.1517000      .000      .000  
+791016 44162.00 I   .102432  .001834   .385888  .001811  I -.1555665  .0001301  2.7217 0.2196  P     6.001     .500    -3.406     .300   .108000   .395000  -.1545000      .000      .000  
+791017 44163.00 I   .103656  .005483   .384759  .002324  I -.1583234  .0004194  2.7906 0.2196  P     6.238     .500    -3.179     .300   .109000   .394000  -.1572000      .000      .000  
+791018 44164.00 I   .104919  .005483   .383589  .002324  I -.1611426  .0004194  2.8443 0.2966  P     6.353     .500    -2.930     .300   .110000   .393000  -.1599000      .000      .000  
+791019 44165.00 I   .106230  .005483   .382375  .002324  I -.1640036  .0004194  2.8729 0.3108  P     6.416     .500    -2.673     .300   .111000   .392000  -.1626000      .000      .000  
+791020 44166.00 I   .107594  .005483   .381114  .002324  I -.1668783  .0004587  2.8713 0.3119  P     6.458     .500    -2.455     .300   .112000   .391000  -.1653000      .000      .000  
+791021 44167.00 I   .109010  .005398   .379805  .002210  I -.1697354  .0004618  2.8374 0.3254  P     6.423     .500    -2.328     .300   .113000   .389000  -.1681000      .000      .000  
+791022 44168.00 I   .110472  .005398   .378448  .002210  I -.1725428  .0004618  2.7727 0.3337  P     6.334     .500    -2.314     .300   .114000   .388000  -.1708000      .000      .000  
+791023 44169.00 I   .111973  .001530   .377043  .001906  I -.1752734  .0004817  2.6858 0.3337  P     6.317     .500    -2.392     .300   .115000   .387000  -.1735000      .000      .000  
+791024 44170.00 I   .113508  .001530   .375592  .001906  I -.1779123  .0004817  2.5927 0.3406  P     6.422     .500    -2.497     .300   .116000   .386000  -.1762000      .000      .000  
+791025 44171.00 I   .115071  .001530   .374096  .001906  I -.1804638  .0004817  2.5146 0.3406  P     6.533     .500    -2.569     .300   .117000   .385000  -.1790000      .000      .000  
+791026 44172.00 I   .116658  .001530   .372558  .001906  I -.1829532  .0004817  2.4715 0.3485  P     6.520     .500    -2.592     .300   .117000   .383000  -.1817000      .000      .000  
+791027 44173.00 I   .118264  .002580   .370981  .001917  I -.1854237  .0005037  2.4791 0.3485  P     6.423     .500    -2.591     .300   .118000   .382000  -.1845000      .000      .000  
+791028 44174.00 I   .119881  .002580   .369370  .001917  I -.1879304  .0005037  2.5438 0.2691  P     6.402     .500    -2.578     .300   .119000   .381000  -.1872000      .000      .000  
+791029 44175.00 I   .121499  .002312   .367730  .001860  I -.1905278  .0001894  2.6580 0.2691  P     6.568     .500    -2.513     .300   .120000   .380000  -.1899000      .000      .000  
+791030 44176.00 I   .123110  .002312   .366067  .001860  I -.1932557  .0001894  2.8001 0.1339  P     6.860     .500    -2.329     .300   .121000   .378000  -.1927000      .000      .000  
+791031 44177.00 I   .124704  .002312   .364386  .001860  I -.1961259  .0001894  2.9361 0.1339  P     7.101     .500    -2.019     .300   .121000   .377000  -.1954000      .000      .000  
+7911 1 44178.00 I   .126267  .002312   .362696  .001860  I -.1991127  .0001894  3.0267 0.1310  P     7.157     .500    -1.706     .300   .122000   .375000  -.1982000      .000      .000  
+7911 2 44179.00 I   .127786  .002905   .361002  .002850  I -.2021533  .0001811  3.0395 0.1310  P     7.065     .500    -1.576     .300   .123000   .374000  -.2009000      .000      .000  
+7911 3 44180.00 I   .129247  .002905   .359310  .002850  I -.2051622  .0001811  2.9638 0.1568  P     6.971     .500    -1.696     .300   .124000   .372000  -.2036000      .000      .000  
+7911 4 44181.00 I   .130640  .003898   .357627  .003603  I -.2080566  .0002561  2.8153 0.1285  P     6.958     .500    -1.906     .300   .125000   .370000  -.2063000      .000      .000  
+7911 5 44182.00 I   .131952  .003677   .355957  .003851  I -.2107808  .0001823  2.6308 0.1572  P     6.990     .500    -1.963     .300   .126000   .369000  -.2090000      .000      .000  
+7911 6 44183.00 I   .133174  .003677   .354305  .003851  I -.2133200  .0001823  2.4518 0.1289  P     7.040     .500    -1.814     .300   .127000   .367000  -.2117000      .000      .000  
+7911 7 44184.00 I   .134298  .003677   .352671  .003851  I -.2156970  .0001823  2.3106 0.0924  P     7.178     .500    -1.669     .300   .128000   .365000  -.2144000      .000      .000  
+7911 8 44185.00 I   .135323  .003442   .351057  .004083  I -.2179589  .0000301  2.2226 0.0924  P     7.454     .500    -1.745     .300   .128990   .363000  -.2170000      .000      .000  
+7911 9 44186.00 I   .136248  .003442   .349458  .004083  I -.2201601  .0000301  2.1883 0.0450  P     7.786     .500    -2.017     .300   .130000   .361000  -.2197000      .000      .000  
+791110 44187.00 I   .137075  .002449   .347869  .003046  I -.2223508  .0000849  2.1998 0.0600  P     8.056     .500    -2.260     .300   .131000   .358000  -.2223000      .000      .000  
+791111 44188.00 I   .137808  .000384   .346286  .001372  I -.2245710  .0001162  2.2452 0.0720  P     8.269     .500    -2.296     .300   .132000   .356000  -.2250000      .000      .000  
+791112 44189.00 I   .138446  .000384   .344704  .001372  I -.2268483  .0001162  2.3118 0.1840  P     8.495     .500    -2.148     .300   .133000   .354000  -.2276000      .000      .000  
+791113 44190.00 I   .138994  .002275   .343119  .001146  I -.2291972  .0003491  2.3861 0.1840  P     8.715     .500    -1.941     .300   .134000   .352000  -.2302000      .000      .000  
+791114 44191.00 I   .139451  .002275   .341524  .001146  I -.2316187  .0003491  2.4551 0.2469  P     8.840     .500    -1.758     .300   .135000   .350000  -.2328000      .000      .000  
+791115 44192.00 I   .139821  .002275   .339914  .001146  I -.2341023  .0003491  2.5090 0.2967  P     8.850     .500    -1.578     .300   .135000   .347000  -.2355000      .000      .000  
+791116 44193.00 I   .140103  .003195   .338282  .000863  I -.2366291  .0004799  2.5403 0.2564  P     8.811     .500    -1.341     .300   .136000   .345000  -.2381000      .000      .000  
+791117 44194.00 I   .140302  .002377   .336622  .002374  I -.2391737  .0003756  2.5441 0.3047  P     8.744     .500    -1.044     .300   .136990   .343000  -.2407000      .000      .000  
+791118 44195.00 I   .140426  .002377   .334928  .002374  I -.2417079  .0003756  2.5196 0.2103  P     8.607     .500     -.783     .300   .138000   .341000  -.2433000      .000      .000  
+791119 44196.00 I   .140482  .001435   .333196  .002297  I -.2442046  .0001892  2.4702 0.2103  P     8.429     .500     -.697     .300   .138000   .339000  -.2459000      .000      .000  
+791120 44197.00 I   .140477  .001435   .331420  .002297  I -.2466437  .0001892  2.4071 0.1261  P     8.351     .500     -.835     .300   .139000   .336000  -.2485000      .000      .000  
+791121 44198.00 I   .140419  .001435   .329595  .002297  I -.2490207  .0001669  2.3498 0.1261  P     8.464     .500    -1.080     .300   .139000   .334000  -.2511000      .000      .000  
+791122 44199.00 I   .140318  .001435   .327721  .002297  I -.2513531  .0001669  2.3218 0.0850  P     8.669     .500    -1.238     .300   .140000   .332000  -.2536990      .000      .000  
+791123 44200.00 I   .140180  .000510   .325795  .001795  I -.2536803  .0000325  2.3419 0.0873  P     8.780     .500    -1.219     .300   .140000   .330000  -.2563000      .000      .000  
+791124 44201.00 I   .140012  .000510   .323818  .001795  I -.2560557  .0000510  2.4183 0.0278  P     8.730     .500    -1.087     .300   .140000   .328000  -.2588990      .000      .000  
+791125 44202.00 I   .139821  .000184   .321787  .001899  I -.2585334  .0000451  2.5441 0.0345  P     8.640     .500     -.952     .300   .141000   .324990  -.2615000      .000      .000  
+791126 44203.00 I   .139611  .000704   .319701  .002263  I -.2611536  .0000466  2.6990 0.0319  P     8.674     .500     -.845     .300   .141000   .323000  -.2641000      .000      .000  
+791127 44204.00 I   .139390  .000704   .317562  .002263  I -.2639311  .0000452  2.8530 0.0393  P     8.863     .500     -.719     .300   .141000   .321000  -.2667000      .000      .000  
+791128 44205.00 I   .139167  .000772   .315386  .003455  I -.2668483  .0000632  2.9732 0.0388  P     9.060     .500     -.540     .300   .141000   .319000  -.2693000      .000      .000  
+791129 44206.00 I   .138953  .000772   .313192  .003455  I -.2698572  .0000632  3.0325 0.0981  P     9.075     .500     -.363     .300   .141000   .316000  -.2718000      .000      .000  
+791130 44207.00 I   .138753  .002006   .310995  .006822  I -.2728880  .0001857  3.0158 0.0981  P     8.850     .500     -.306     .300   .141000   .314000  -.2744000      .000      .000  
+7912 1 44208.00 I   .138573  .002006   .308809  .006822  I -.2758646  .0001857  2.9266 0.1813  P     8.526     .500     -.436     .300   .141000   .310990  -.2769000      .000      .000  
+7912 2 44209.00 I   .138418  .001687   .306639  .006543  I -.2787243  .0003114  2.7869 0.1813  P     8.316     .500     -.670     .300   .141000   .308990  -.2795000      .000      .000  
+7912 3 44210.00 I   .138289  .001687   .304489  .006543  I -.2814329  .0003114  2.6309 0.2203  P     8.335     .500     -.819     .300   .141000   .307000  -.2821000      .000      .000  
+7912 4 44211.00 I   .138190  .002264   .302360  .002854  I -.2839919  .0003116  2.4925 0.2203  P     8.530     .500     -.763     .300   .141000   .305000  -.2846000      .000      .000  
+7912 5 44212.00 I   .138124  .002264   .300255  .002854  I -.2864315  .0003116  2.3949 0.2119  P     8.772     .500     -.586     .300   .140000   .303000  -.2872000      .000      .000  
+7912 6 44213.00 I   .138091  .002264   .298174  .002854  I -.2887981  .0002872  2.3466 0.2051  P     8.983     .500     -.484     .300   .140000   .301000  -.2897000      .000      .000  
+7912 7 44214.00 I   .138089  .002264   .296114  .002854  I -.2911396  .0002669  2.3432 0.1763  P     9.180     .500     -.545     .300   .140000   .299000  -.2923000      .000      .000  
+7912 8 44215.00 I   .138117  .002089   .294075  .001784  I -.2934961  .0002045  2.3748 0.1681  P     9.411     .500     -.670     .300   .140000   .297000  -.2949000      .000      .000  
+7912 9 44216.00 I   .138168  .002089   .292058  .001784  I -.2958970  .0002045  2.4299 0.1655  P     9.671     .500     -.711     .300   .140000   .294990  -.2974000      .000      .000  
+791210 44217.00 I   .138238  .001369   .290066  .002530  I -.2983595  .0002603  2.4959 0.1655  P     9.884     .500     -.636     .300   .139000   .293000  -.3000000      .000      .000  
+791211 44218.00 I   .138320  .001369   .288099  .002530  I -.3008881  .0002603  2.5600 0.1894  P     9.982     .500     -.520     .300   .139000   .291000  -.3025000      .000      .000  
+791212 44219.00 I   .138410  .001369   .286159  .002530  I -.3034748  .0002751  2.6102 0.1972  P     9.963     .500     -.433     .300   .139000   .289000  -.3051000      .000      .000  
+791213 44220.00 I   .138505  .001369   .284248  .002530  I -.3061010  .0002962  2.6381 0.2368  P     9.862     .500     -.359     .300   .139000   .287000  -.3076000      .000      .000  
+791214 44221.00 I   .138603  .001227   .282368  .003533  I -.3087421  .0003856  2.6395 0.2431  P     9.698     .500     -.227     .300   .138000   .285000  -.3102000      .000      .000  
+791215 44222.00 I   .138703  .001227   .280516  .003533  I -.3113708  .0003856  2.6135 0.2269  P     9.476     .500      .004     .300   .138000   .283000  -.3127000      .000      .000  
+791216 44223.00 I   .138805  .002109   .278690  .000876  I -.3139607  .0002392  2.5624 0.2269  P     9.245     .500      .271     .300   .136990   .280990  -.3153000      .000      .000  
+791217 44224.00 I   .138909  .002109   .276887  .000876  I -.3164895  .0002392  2.4929 0.2904  P     9.098     .500      .417     .300   .136990   .279000  -.3178000      .000      .000  
+791218 44225.00 I   .139014  .002109   .275106  .000876  I -.3189447  .0005293  2.4179 0.3238  P     9.097     .500      .316     .300   .136990   .277000  -.3203000      .000      .000  
+791219 44226.00 I   .139120  .001512   .273342  .001091  I -.3213305  .0006018  2.3584 0.4007  P     9.230     .500      .024     .300   .136000   .275000  -.3229000      .000      .000  
+791220 44227.00 I   .139225  .001512   .271595  .001091  I -.3236743  .0006018  2.3374 0.4255  P     9.429     .500     -.230     .300   .136000   .273990  -.3254000      .000      .000  
+791221 44228.00 I   .139326  .001512   .269861  .001091  I -.3260234  .0006018  2.3707 0.4735  P     9.610     .500     -.242     .300   .135000   .272000  -.3280000      .000      .000  
+791222 44229.00 I   .139418  .000357   .268135  .001271  I -.3284335  .0007313  2.4576 0.4735  P     9.711     .500     -.017     .300   .135000   .270000  -.3305000      .000      .000  
+791223 44230.00 I   .139498  .000357   .266416  .001271  I -.3309502  .0007313  2.5794 0.5104  P     9.722     .500      .264     .300   .134000   .268000  -.3330000      .000      .000  
+791224 44231.00 I   .139560  .000357   .264700  .001271  I -.3335935  .0007121  2.7046 0.3989  P     9.694     .500      .434     .300   .134000   .266000  -.3355000      .000      .000  
+791225 44232.00 I   .139603  .003310   .262986  .002690  I -.3363494  .0003188  2.7996 0.3794  P     9.686     .500      .468     .300   .133000   .264000  -.3380000      .000      .000  
+791226 44233.00 I   .139621  .001759   .261273  .003757  I -.3391744  .0002620  2.8398 0.1984  P     9.689     .500      .434     .300   .133000   .262000  -.3405000      .000      .000  
+791227 44234.00 I   .139611  .001759   .259557  .003757  I -.3420076  .0002363  2.8158 0.1764  P     9.614     .500      .385     .300   .132000   .260000  -.3430000      .000      .000  
+791228 44235.00 I   .139569  .001759   .257838  .003757  I -.3447863  .0002363  2.7328 0.1666  P     9.389     .500      .313     .300   .131000   .257990  -.3455000      .000      .000  
+791229 44236.00 I   .139492  .001759   .256114  .003757  I -.3474594  .0002349  2.6083 0.1666  P     9.058     .500      .190     .300   .131000   .256000  -.3480000      .000      .000  
+791230 44237.00 I   .139373  .001759   .254387  .003757  I -.3499978  .0002349  2.4684 0.1398  P     8.791     .500      .041     .300   .130000   .255000  -.3506000      .000      .000  
+791231 44238.00 I   .139205  .002692   .252659  .002880  I -.3524007  .0001516  2.3421 0.1471  P     8.751     .500     -.051     .300   .130000   .253000  -.3531000      .000      .000  
+80 1 1 44239.00 I   .138980  .003377   .250935  .001572  I  .6453058  .0001770  2.2525 0.1299  P     8.940     .500     -.017     .300   .128990   .250990   .6444000      .000      .000  
+80 1 2 44240.00 I   .138693  .003377   .249216  .001572  I  .6430784  .0002110  2.2106 0.1797  P     9.190     .500      .106     .300   .128000   .249000   .6419000      .000      .000  
+80 1 3 44241.00 I   .138335  .003377   .247506  .001572  I  .6408691  .0003129  2.2154 0.1982  P     9.345     .500      .200     .300   .127000   .247000   .6394000      .000      .000  
+80 1 4 44242.00 I   .137902  .003377   .245807  .001572  I  .6386352  .0003355  2.2576 0.2294  P     9.427     .500      .185     .300   .127000   .246000   .6368000      .000      .000  
+80 1 5 44243.00 I   .137395  .003377   .244120  .001572  I  .6363457  .0003355  2.3246 0.2104  P     9.562     .500      .103     .300   .126000   .244000   .6343000      .000      .000  
+80 1 6 44244.00 I   .136811  .005031   .242446  .000749  I  .6339818  .0002540  2.4042 0.2104  P     9.761     .500      .041     .300   .125000   .242000   .6318000      .000      .000  
+80 1 7 44245.00 I   .136151  .005031   .240786  .000749  I  .6315374  .0002540  2.4836 0.1796  P     9.887     .500      .030     .300   .124000   .240000   .6293000      .000      .000  
+80 1 8 44246.00 I   .135413  .005031   .239142  .000749  I  .6290184  .0002540  2.5516 0.1793  P     9.853     .500      .029     .300   .123000   .239000   .6267000      .000      .000  
+80 1 9 44247.00 I   .134598  .003779   .237513  .001582  I  .6264412  .0002531  2.5988 0.1691  P     9.728     .500      .005     .300   .122000   .237000   .6242000      .000      .000  
+80 110 44248.00 I   .133709  .003779   .235902  .001582  I  .6238294  .0002232  2.6204 0.1687  P     9.586     .500     -.031     .300   .121000   .236000   .6216000      .000      .000  
+80 111 44249.00 I   .132757  .003779   .234310  .001582  I  .6212086  .0002232  2.6173 0.1867  P     9.381     .500     -.038     .300   .120000   .234000   .6191000      .000      .000  
+80 112 44250.00 I   .131748  .001806   .232740  .002108  I  .6186020  .0002993  2.5926 0.1714  P     9.088     .500      .037     .300   .119000   .232000   .6166000      .000      .000  
+80 113 44251.00 I   .130692  .001323   .231191  .001528  I  .6160289  .0002601  2.5513 0.1983  P     8.847     .500      .223     .300   .118000   .231000   .6141000      .000      .000  
+80 114 44252.00 I   .129593  .001323   .229665  .001528  I  .6135018  .0002601  2.5026 0.1474  P     8.811     .500      .449     .300   .116000   .229000   .6115000      .000      .000  
+80 115 44253.00 I   .128457  .002049   .228159  .000338  I  .6110210  .0001389  2.4619 0.1521  P     8.917     .500      .539     .300   .115000   .228000   .6090000      .000      .000  
+80 116 44254.00 I   .127290  .002049   .226672  .000338  I  .6085683  .0001579  2.4503 0.1051  P     8.977     .500      .376     .300   .114000   .226000   .6065000      .000      .000  
+80 117 44255.00 I   .126096  .002049   .225205  .000338  I  .6061039  .0001579  2.4882 0.1117  P     8.964     .500      .074     .300   .113000   .224000   .6040000      .000      .000  
+80 118 44256.00 I   .124881  .002049   .223755  .000338  I  .6035730  .0001579  2.5829 0.2100  P     9.054     .500     -.093     .300   .111000   .223000   .6015000      .000      .000  
+80 119 44257.00 I   .123646  .003572   .222321  .000859  I  .6009239  .0003892  2.7203 0.2100  P     9.340     .500      .037     .300   .110000   .221000   .5989000      .000      .000  
+80 120 44258.00 I   .122392  .003572   .220902  .000859  I  .5981297  .0003892  2.8658 0.3292  P     9.674     .500      .347     .300   .108000   .220000   .5964000      .000      .000  
+80 121 44259.00 I   .121118  .004167   .219496  .001213  I  .5952043  .0005311  2.9755 0.2738  P     9.832     .500      .585     .300   .107000   .218000   .5939000      .000      .000  
+80 122 44260.00 I   .119821  .003819   .218103  .001099  I  .5922023  .0003852  3.0145 0.3027  P     9.750     .500      .620     .300   .106000   .216000   .5914000      .000      .000  
+80 123 44261.00 I   .118501  .003819   .216720  .001099  I  .5892033  .0002905  2.9693 0.2374  P     9.532     .500      .504     .300   .104000   .215000   .5889000      .000      .000  
+80 124 44262.00 I   .117150  .003819   .215348  .001099  I  .5862886  .0002777  2.8492 0.1871  P     9.301     .500      .348     .300   .103000   .213000   .5863000      .000      .000  
+80 125 44263.00 I   .115758  .003435   .213987  .000970  I  .5835214  .0002360  2.6796 0.1786  P     9.107     .500      .196     .300   .101000   .212000   .5838000      .000      .000  
+80 126 44264.00 I   .114323  .003435   .212638  .000970  I  .5809352  .0002247  2.4926 0.1571  P     8.949     .500      .046     .300   .100000   .210000   .5813000      .000      .000  
+80 127 44265.00 I   .112841  .002468   .211304  .001808  I  .5785319  .0002074  2.3185 0.1574  P     8.845     .500     -.083     .300   .099000   .209000   .5788000      .000      .000  
+80 128 44266.00 I   .111310  .000614   .209984  .002365  I  .5762861  .0002206  2.1806 0.1663  P     8.849     .500     -.149     .300   .097000   .207000   .5764000      .000      .000  
+80 129 44267.00 I   .109728  .000614   .208682  .002365  I  .5741545  .0002601  2.0915 0.1617  P     8.993     .500     -.145     .300   .096000   .206000   .5739000      .000      .000  
+80 130 44268.00 I   .108092  .004099   .207398  .002045  I  .5720865  .0002364  2.0524 0.1392  P     9.205     .500     -.124     .300   .094000   .204000   .5715000      .000      .000  
+80 131 44269.00 I   .106402  .004099   .206132  .002045  I  .5700354  .0000992  2.0561 0.1366  P     9.365     .500     -.147     .300   .093000   .203000   .5690000      .000      .000  
+80 2 1 44270.00 I   .104655  .004099   .204884  .002045  I  .5679641  .0001369  2.0903 0.1554  P     9.447     .500     -.215     .300   .092000   .202000   .5665000      .000      .000  
+80 2 2 44271.00 I   .102851  .005765   .203651  .001665  I  .5658492  .0002945  2.1415 0.1471  P     9.533     .500     -.280     .300   .090000   .201000   .5641000      .000      .000  
+80 2 3 44272.00 I   .100993  .005130   .202433  .001274  I  .5636796  .0002604  2.1974 0.1912  P     9.645     .500     -.312     .300   .089000   .199000   .5616000      .000      .000  
+80 2 4 44273.00 I   .099091  .005130   .201231  .001274  I  .5614565  .0002440  2.2469 0.1956  P     9.674     .500     -.344     .300   .087000   .198000   .5592000      .000      .000  
+80 2 5 44274.00 I   .097155  .004404   .200045  .000689  I  .5591909  .0002919  2.2809 0.1751  P     9.553     .500     -.413     .300   .086000   .197000   .5567000      .000      .000  
+80 2 6 44275.00 I   .095195  .003150   .198875  .000981  I  .5569018  .0002511  2.2934 0.1929  P     9.380     .500     -.508     .300   .085000   .196000   .5543000      .000      .000  
+80 2 7 44276.00 I   .093226  .003150   .197723  .000981  I  .5546116  .0002522  2.2834 0.1433  P     9.239     .500     -.605     .300   .084000   .195000   .5519000      .000      .000  
+80 2 8 44277.00 I   .091266  .003150   .196591  .000981  I  .5523411  .0001381  2.2550 0.2027  P     9.059     .500     -.706     .300   .083000   .193000   .5494000      .000      .000  
+80 2 9 44278.00 I   .089333  .008667   .195479  .001230  I  .5501051  .0003175  2.2160 0.1876  P     8.803     .500     -.787     .300   .082000   .192000   .5470000      .000      .000  
+80 210 44279.00 I   .087446  .008667   .194388  .001230  I  .5479092  .0003489  2.1766 0.2488  P     8.657     .500     -.748     .300   .081000   .191000   .5446000      .000      .000  
+80 211 44280.00 I   .085620  .007085   .193319  .001032  I  .5457478  .0003831  2.1494 0.3863  P     8.806     .500     -.528     .300   .080000   .190000   .5422000      .000      .000  
+80 212 44281.00 I   .083873  .008664   .192271  .000934  I  .5436007  .0006894  2.1513 0.3943  P     9.070     .500     -.271     .300   .079000   .189000   .5397000      .000      .000  
+80 213 44282.00 I   .082218  .008664   .191244  .000934  I  .5414283  .0006894  2.2039 0.4875  P     9.070     .500     -.245     .300   .079000   .188000   .5373000      .000      .000  
+80 214 44283.00 I   .080651  .008664   .190240  .000934  I  .5391704  .0006894  2.3241 0.5040  P     8.773     .500     -.538     .300   .078000   .187000   .5348000      .000      .000  
+80 215 44284.00 I   .079166  .000589   .189260  .000412  I  .5367591  .0007355  2.5074 0.5040  P     8.600     .500     -.902     .300   .077000   .186000   .5324000      .000      .000  
+80 216 44285.00 I   .077756  .000589   .188305  .000412  I  .5341446  .0007355  2.7237 0.5100  P     8.907     .500    -1.017     .300   .075990   .185000   .5300000      .000      .000  
+80 217 44286.00 I   .076414  .000053   .187376  .001721  I  .5313165  .0007068  2.9257 0.3822  P     9.531     .500     -.834     .300   .075000   .184000   .5276000      .000      .000  
+80 218 44287.00 I   .075131  .000053   .186474  .001721  I  .5283146  .0002082  3.0635 0.3684  P     9.988     .500     -.589     .300   .075000   .184000   .5251000      .000      .000  
+80 219 44288.00 I   .073900  .000053   .185601  .001721  I  .5252221  .0002082  3.1034 0.1693  P     9.984     .500     -.494     .300   .074000   .183000   .5227000      .000      .000  
+80 220 44289.00 I   .072715  .000611   .184756  .001318  I  .5221418  .0002671  3.0412 0.1627  P     9.620     .500     -.546     .300   .073000   .182000   .5202990      .000      .000  
+80 221 44290.00 I   .071567  .000611   .183944  .001318  I  .5191657  .0002501  2.9011 0.1843  P     9.213     .500     -.653     .300   .071990   .181000   .5179000      .000      .000  
+80 222 44291.00 I   .070449  .000611   .183165  .001318  I  .5163523  .0002541  2.7231 0.1788  P     9.023     .500     -.799     .300   .071000   .180000   .5155000      .000      .000  
+80 223 44292.00 I   .069354  .000863   .182425  .000714  I  .5137186  .0002557  2.5479 0.1843  P     9.088     .500    -1.012     .300   .071000   .180000   .5130000      .000      .000  
+80 224 44293.00 I   .068273  .000614   .181726  .001319  I  .5112459  .0002669  2.4044 0.1848  P     9.253     .500    -1.247     .300   .070000   .179000   .5105990      .000      .000  
+80 225 44294.00 I   .067199  .000614   .181072  .001319  I  .5088943  .0002669  2.3073 0.1808  P     9.363     .500    -1.406     .300   .069000   .178000   .5082000      .000      .000  
+80 226 44295.00 I   .066122  .000099   .180469  .001723  I  .5066145  .0002439  2.2604 0.2011  P     9.428     .500    -1.462     .300   .068000   .177000   .5058000      .000      .000  
+80 227 44296.00 I   .065033  .002118   .179919  .001673  I  .5043588  .0003008  2.2575 0.2012  P     9.567     .500    -1.485     .300   .067000   .177000   .5034000      .000      .000  
+80 228 44297.00 I   .063923  .002118   .179427  .001673  I  .5020888  .0003201  2.2868 0.2254  P     9.807     .500    -1.531     .300   .067000   .176000   .5010000      .000      .000  
+80 229 44298.00 I   .062782  .002118   .178993  .001673  I  .4997791  .0003359  2.3343 0.2170  P    10.059     .500    -1.561     .300   .066000   .176000   .4986000      .000      .000  
+80 3 1 44299.00 I   .061603  .002628   .178619  .002590  I  .4974185  .0002932  2.3868 0.2229  P    10.245     .500    -1.512     .300   .065000   .175000   .4962000      .000      .000  
+80 3 2 44300.00 I   .060380  .002628   .178307  .002590  I  .4950075  .0002932  2.4334 0.2101  P    10.341     .500    -1.411     .300   .064000   .175000   .4938000      .000      .000  
+80 3 3 44301.00 I   .059110  .002628   .178054  .002590  I  .4925570  .0003011  2.4642 0.2117  P    10.322     .500    -1.368     .300   .063000   .175000   .4913000      .000      .000  
+80 3 4 44302.00 I   .057789  .002398   .177863  .002360  I  .4900871  .0003054  2.4712 0.2144  P    10.183     .500    -1.438     .300   .062000   .175000   .4889000      .000      .000  
+80 3 5 44303.00 I   .056416  .002398   .177730  .002360  I  .4876240  .0003054  2.4501 0.2160  P    10.003     .500    -1.566     .300   .061000   .175000   .4864000      .000      .000  
+80 3 6 44304.00 I   .054990  .002398   .177653  .002360  I  .4851957  .0003054  2.4025 0.2769  P     9.859     .500    -1.683     .300   .060000   .175000   .4840000      .000      .000  
+80 3 7 44305.00 I   .053512  .002132   .177625  .002369  I  .4828251  .0004620  2.3367 0.2769  P     9.709     .500    -1.805     .300   .059000   .175000   .4815000      .000      .000  
+80 3 8 44306.00 I   .051987  .002132   .177643  .002369  I  .4805241  .0004620  2.2657 0.3389  P     9.529     .500    -1.967     .300   .057000   .175000   .4790000      .000      .000  
+80 3 9 44307.00 I   .050421  .002132   .177702  .002369  I  .4782909  .0004960  2.2033 0.3425  P     9.501     .500    -2.083     .300   .056000   .176000   .4766000      .000      .000  
+80 310 44308.00 I   .048818  .001193   .177795  .002528  I  .4761094  .0005057  2.1652 0.3542  P     9.821     .500    -2.009     .300   .054000   .176000   .4741000      .000      .000  
+80 311 44309.00 I   .047184  .001193   .177919  .002528  I  .4739461  .0005057  2.1703 0.3576  P    10.311     .500    -1.778     .300   .053000   .176000   .4716000      .000      .000  
+80 312 44310.00 I   .045521  .001193   .178073  .002528  I  .4717485  .0005057  2.2364 0.3351  P    10.511     .500    -1.652     .300   .052000   .176000   .4691000      .000      .000  
+80 313 44311.00 I   .043833  .000463   .178255  .001422  I  .4694499  .0004397  2.3726 0.3351  P    10.260     .500    -1.839     .300   .050000   .177000   .4666000      .000      .000  
+80 314 44312.00 I   .042121  .000463   .178468  .001422  I  .4669834  .0004397  2.5685 0.3109  P     9.959     .500    -2.210     .300   .049000   .177000   .4641000      .000      .000  
+80 315 44313.00 I   .040391  .000463   .178710  .001422  I  .4643042  .0004397  2.7904 0.3098  P    10.086     .500    -2.436     .300   .047000   .178000   .4616000      .000      .000  
+80 316 44314.00 I   .038646  .001144   .178983  .001158  I  .4614115  .0004365  2.9856 0.3098  P    10.612     .500    -2.372     .300   .046000   .178000   .4591000      .000      .000  
+80 317 44315.00 I   .036890  .001144   .179286  .001158  I  .4583594  .0004365  3.1018 0.3199  P    11.062     .500    -2.187     .300   .044000   .179000   .4566000      .000      .000  
+80 318 44316.00 I   .035126  .001327   .179618  .000964  I  .4552437  .0004678  3.1108 0.3196  P    11.045     .500    -2.093     .300   .042000   .179000   .4541000      .000      .000  
+80 319 44317.00 I   .033358  .001622   .179979  .000571  I  .4521716  .0004669  3.0185 0.3305  P    10.598     .500    -2.105     .300   .041000   .180000   .4515000      .000      .000  
+80 320 44318.00 I   .031588  .001622   .180365  .000571  I  .4492286  .0004669  2.8605 0.3301  P    10.098     .500    -2.141     .300   .039000   .180000   .4490000      .000      .000  
+80 321 44319.00 I   .029816  .001622   .180776  .000571  I  .4464563  .0004669  2.6855 0.3097  P     9.936     .500    -2.226     .300   .037000   .181000   .4465000      .000      .000  
+80 322 44320.00 I   .028044  .001737   .181209  .000946  I  .4438491  .0004069  2.5361 0.3097  P    10.203     .500    -2.450     .300   .035000   .182000   .4439000      .000      .000  
+80 323 44321.00 I   .026277  .001737   .181661  .000946  I  .4413673  .0004069  2.4373 0.2352  P    10.612     .500    -2.780     .300   .033000   .182000   .4413000      .000      .000  
+80 324 44322.00 I   .024517  .001836   .182131  .001299  I  .4389552  .0002361  2.3965 0.2421  P    10.790     .500    -3.045     .300   .032000   .183000   .4388000      .000      .000  
+80 325 44323.00 I   .022769  .002040   .182616  .001004  I  .4365571  .0002624  2.4074 0.1765  P    10.696     .500    -3.135     .300   .030000   .183000   .4362000      .000      .000  
+80 326 44324.00 I   .021032  .002040   .183114  .001004  I  .4341277  .0002624  2.4566 0.1829  P    10.646     .500    -3.102     .300   .028000   .184000   .4336000      .000      .000  
+80 327 44325.00 I   .019308  .002851   .183625  .001550  I  .4316361  .0002549  2.5292 0.1860  P    10.888     .500    -3.033     .300   .026000   .185000   .4310000      .000      .000  
+80 328 44326.00 I   .017596  .003241   .184149  .001661  I  .4290667  .0002638  2.6095 0.1834  P    11.307     .500    -2.922     .300   .024000   .186000   .4284000      .000      .000  
+80 329 44327.00 I   .015900  .003241   .184683  .001661  I  .4264192  .0002638  2.6834 0.1865  P    11.621     .500    -2.720     .300   .021000   .186000   .4258000      .000      .000  
+80 330 44328.00 I   .014225  .003241   .185227  .001661  I  .4237058  .0002638  2.7396 0.2025  P    11.707     .500    -2.479     .300   .018990   .187000   .4232000      .000      .000  
+80 331 44329.00 I   .012573  .002856   .185779  .001611  I  .4209487  .0003074  2.7698 0.2025  P    11.627     .500    -2.341     .300   .017000   .188000   .4206000      .000      .000  
+80 4 1 44330.00 I   .010942  .002856   .186341  .001611  I  .4181764  .0003074  2.7693 0.2349  P    11.467     .500    -2.383     .300   .014000   .189000   .4180000      .000      .000  
+80 4 2 44331.00 I   .009330  .000378   .186913  .000975  I  .4154210  .0003552  2.7362 0.2349  P    11.284     .500    -2.532     .300   .012000   .190000   .4154000      .000      .000  
+80 4 3 44332.00 I   .007732  .000378   .187499  .000975  I  .4127132  .0003552  2.6754 0.2468  P    11.125     .500    -2.677     .300   .008990   .191000   .4127000      .000      .000  
+80 4 4 44333.00 I   .006145  .000378   .188098  .000975  I  .4100757  .0003428  2.5979 0.2468  P    10.989     .500    -2.801     .300   .007000   .192000   .4101000      .000      .000  
+80 4 5 44334.00 I   .004564  .000378   .188713  .000975  I  .4075181  .0003428  2.5183 0.2629  P    10.859     .500    -2.962     .300   .004000   .193000   .4075000      .000      .000  
+80 4 6 44335.00 I   .002985  .000116   .189344  .001015  I  .4050339  .0003987  2.4542 0.2629  P    10.829     .500    -3.143     .300   .002000   .194000   .4049000      .000      .000  
+80 4 7 44336.00 I   .001404  .000116   .189993  .001015  I  .4025986  .0003987  2.4232 0.2630  P    11.067     .500    -3.227     .300   .000000   .195000   .4022000      .000      .000  
+80 4 8 44337.00 I  -.000184  .003244   .190657  .000441  I  .4001715  .0003431  2.4401 0.2630  P    11.551     .500    -3.157     .300  -.003000   .196000   .3996000      .000      .000  
+80 4 9 44338.00 I  -.001784  .003244   .191338  .000441  I  .3976986  .0003431  2.5164 0.2470  P    11.991     .500    -3.051     .300  -.005000   .197000   .3969000      .000      .000  
+80 410 44339.00 I  -.003399  .003244   .192036  .000441  I  .3951184  .0003555  2.6537 0.2470  P    12.138     .500    -3.067     .300  -.007000   .198000   .3943000      .000      .000  
+80 411 44340.00 I  -.005035  .003244   .192749  .000441  I  .3923766  .0003555  2.8348 0.1860  P    12.078     .500    -3.185     .300  -.008990   .199000   .3916000      .000      .000  
+80 412 44341.00 I  -.006693  .003553   .193480  .000840  I  .3894468  .0001094  3.0217 0.1860  P    12.069     .500    -3.228     .300  -.011000   .200000   .3889000      .000      .000  
+80 413 44342.00 I  -.008371  .003553   .194228  .000840  I  .3863481  .0001094  3.1638 0.0630  P    12.169     .500    -3.123     .300  -.012000   .202000   .3863000      .000      .000  
+80 414 44343.00 I  -.010064  .002050   .194996  .001089  I  .3831491  .0000624  3.2162 0.0627  P    12.176     .500    -3.007     .300  -.014000   .203000   .3836000      .000      .000  
+80 415 44344.00 I  -.011767  .002405   .195783  .000950  I  .3799519  .0000613  3.1599 0.0437  P    11.904     .500    -3.031     .300  -.016000   .204000   .3809000      .000      .000  
+80 416 44345.00 I  -.013473  .002405   .196591  .000950  I  .3768602  .0000613  3.0107 0.1356  P    11.408     .500    -3.148     .300  -.017990   .205000   .3782000      .000      .000  
+80 417 44346.00 I  -.015173  .002923   .197424  .002939  I  .3739470  .0002642  2.8119 0.1632  P    10.976     .500    -3.216     .300  -.018990   .206000   .3756000      .000      .000  
+80 418 44347.00 I  -.016861  .003274   .198283  .003516  I  .3712357  .0003205  2.6154 0.2077  P    10.925     .500    -3.240     .300  -.021000   .208000   .3729000      .000      .000  
+80 419 44348.00 I  -.018531  .003274   .199172  .003516  I  .3687020  .0003205  2.4617 0.2070  P    11.322     .500    -3.367     .300  -.022000   .209000   .3703000      .000      .000  
+80 420 44349.00 I  -.020180  .003395   .200093  .003038  I  .3662917  .0002621  2.3699 0.2258  P    11.839     .500    -3.647     .300  -.024000   .210000   .3676000      .000      .000  
+80 421 44350.00 I  -.021803  .003688   .201050  .003679  I  .3639420  .0003181  2.3389 0.2061  P    12.018     .500    -3.925     .300  -.025000   .211000   .3650000      .000      .000  
+80 422 44351.00 I  -.023391  .003688   .202038  .003679  I  .3615979  .0003181  2.3561 0.2047  P    11.772     .500    -4.027     .300  -.027000   .213000   .3623000      .000      .000  
+80 423 44352.00 I  -.024933  .003606   .203054  .002158  I  .3592189  .0002576  2.4060 0.2047  P    11.503     .500    -3.946     .300  -.028000   .214000   .3597000      .000      .000  
+80 424 44353.00 I  -.026421  .003606   .204098  .002158  I  .3567803  .0002576  2.4727 0.1822  P    11.610     .500    -3.787     .300  -.030000   .216000   .3570000      .000      .000  
+80 425 44354.00 I  -.027852  .003606   .205166  .002158  I  .3542727  .0002576  2.5417 0.2268  P    12.018     .500    -3.604     .300  -.031000   .217000   .3544000      .000      .000  
+80 426 44355.00 I  -.029222  .003588   .206260  .002519  I  .3517004  .0003733  2.6001 0.2268  P    12.332     .500    -3.379     .300  -.032000   .218000   .3518000      .000      .000  
+80 427 44356.00 I  -.030530  .003588   .207376  .002519  I  .3490794  .0003733  2.6376 0.2640  P    12.341     .500    -3.135     .300  -.033000   .220000   .3492000      .000      .000  
+80 428 44357.00 I  -.031780  .003588   .208512  .002519  I  .3464344  .0003733  2.6474 0.2186  P    12.156     .500    -2.967     .300  -.035000   .221000   .3465000      .000      .000  
+80 429 44358.00 I  -.032974  .000383   .209662  .001127  I  .3437946  .0002276  2.6273 0.2186  P    11.947     .500    -2.950     .300  -.035990   .223000   .3439000      .000      .000  
+80 430 44359.00 I  -.034117  .000383   .210823  .001127  I  .3411890  .0002276  2.5795 0.1609  P    11.769     .500    -3.060     .300  -.037000   .224000   .3413000      .000      .000  
+80 5 1 44360.00 I  -.035211  .000383   .211990  .001127  I  .3386427  .0002276  2.5105 0.1207  P    11.635     .500    -3.213     .300  -.037990   .225000   .3387000      .000      .000  
+80 5 2 44361.00 I  -.036260  .000383   .213160  .001127  I  .3361710  .0000802  2.4327 0.1896  P    11.567     .500    -3.356     .300  -.039000   .226000   .3362000      .000      .000  
+80 5 3 44362.00 I  -.037268  .000710   .214328  .001256  I  .3337745  .0003033  2.3633 0.1569  P    11.534     .500    -3.489     .300  -.040000   .228000   .3336000      .000      .000  
+80 5 4 44363.00 I  -.038237  .000710   .215493  .001256  I  .3314354  .0003033  2.3209 0.2468  P    11.476     .500    -3.617     .300  -.041000   .229000   .3311000      .000      .000  
+80 5 5 44364.00 I  -.039170  .000929   .216651  .001372  I  .3291184  .0003893  2.3215 0.2247  P    11.438     .500    -3.710     .300  -.042000   .230000   .3285000      .000      .000  
+80 5 6 44365.00 I  -.040070  .002420   .217799  .001039  I  .3267747  .0003316  2.3753 0.2557  P    11.552     .500    -3.734     .300  -.043000   .231000   .3260000      .000      .000  
+80 5 7 44366.00 I  -.040940  .002420   .218938  .001039  I  .3243499  .0003316  2.4829 0.2675  P    11.871     .500    -3.689     .300  -.044000   .232000   .3235000      .000      .000  
+80 5 8 44367.00 I  -.041786  .002115   .220064  .000854  I  .3217946  .0004199  2.6335 0.2757  P    12.282     .500    -3.599     .300  -.044000   .234000   .3210000      .000      .000  
+80 5 9 44368.00 I  -.042610  .002505   .221179  .000392  I  .3190770  .0004406  2.8018 0.3043  P    12.601     .500    -3.453     .300  -.045000   .235000   .3185000      .000      .000  
+80 510 44369.00 I  -.043412  .002505   .222281  .000392  I  .3161979  .0004406  2.9492 0.2980  P    12.684     .500    -3.232     .300  -.046000   .236000   .3160000      .000      .000  
+80 511 44370.00 I  -.044191  .002781   .223371  .000980  I  .3131995  .0004013  3.0338 0.3204  P    12.464     .500    -3.001     .300  -.047000   .237000   .3135000      .000      .000  
+80 512 44371.00 I  -.044944  .002485   .224447  .001141  I  .3101614  .0004652  3.0253 0.3072  P    11.970     .500    -2.918     .300  -.047000   .238000   .3111000      .000      .000  
+80 513 44372.00 I  -.045667  .002485   .225511  .001141  I  .3071817  .0004652  2.9186 0.2686  P    11.347     .500    -3.075     .300  -.048000   .240000   .3086000      .000      .000  
+80 514 44373.00 I  -.046356  .002451   .226563  .001650  I  .3043487  .0002685  2.7383 0.2686  P    10.806     .500    -3.353     .300  -.048000   .241000   .3062000      .000      .000  
+80 515 44374.00 I  -.047008  .002451   .227603  .001650  I  .3017146  .0002685  2.5295 0.1899  P    10.542     .500    -3.528     .300  -.049000   .242000   .3037000      .000      .000  
+80 516 44375.00 I  -.047627  .002451   .228634  .001650  I  .2992833  .0002685  2.3399 0.1743  P    10.660     .500    -3.527     .300  -.050000   .243000   .3014000      .000      .000  
+80 517 44376.00 I  -.048213  .001173   .229657  .001694  I  .2970177  .0002223  2.2016 0.1556  P    11.099     .500    -3.498     .300  -.050000   .244000   .2990000      .000      .000  
+80 518 44377.00 I  -.048768  .001357   .230674  .001407  I  .2948597  .0001572  2.1246 0.1361  P    11.564     .500    -3.596     .300  -.051000   .245000   .2967000      .000      .000  
+80 519 44378.00 I  -.049291  .001357   .231685  .001407  I  .2927505  .0001572  2.1018 0.0803  P    11.695     .500    -3.784     .300  -.051000   .246000   .2943000      .000      .000  
+80 520 44379.00 I  -.049783  .001641   .232689  .001000  I  .2906432  .0000329  2.1179 0.0803  P    11.414     .500    -3.897     .300  -.052000   .247000   .2920000      .000      .000  
+80 521 44380.00 I  -.050242  .001641   .233687  .001000  I  .2885074  .0000329  2.1559 0.0233  P    11.044     .500    -3.859     .300  -.052000   .248000   .2897000      .000      .000  
+80 522 44381.00 I  -.050670  .001641   .234679  .001000  I  .2863292  .0000329  2.2004 0.0447  P    10.958     .500    -3.738     .300  -.052000   .249000   .2874000      .000      .000  
+80 523 44382.00 I  -.051071  .001641   .235667  .001000  I  .2841089  .0000832  2.2380 0.0860  P    11.144     .500    -3.616     .300  -.053000   .250000   .2852000      .000      .000  
+80 524 44383.00 I  -.051448  .001657   .236653  .000683  I  .2818591  .0001688  2.2579 0.0941  P    11.281     .500    -3.499     .300  -.053000   .250990   .2829000      .000      .000  
+80 525 44384.00 I  -.051806  .001657   .237638  .000683  I  .2796014  .0001688  2.2529 0.1186  P    11.158     .500    -3.354     .300  -.053000   .252000   .2806000      .000      .000  
+80 526 44385.00 I  -.052152  .003978   .238627  .000900  I  .2773624  .0001667  2.2206 0.1186  P    10.860     .500    -3.186     .300  -.053000   .253000   .2784000      .000      .000  
+80 527 44386.00 I  -.052492  .003978   .239621  .000900  I  .2751687  .0001667  2.1628 0.1179  P    10.559     .500    -3.045     .300  -.053000   .254000   .2762000      .000      .000  
+80 528 44387.00 I  -.052829  .003978   .240622  .000900  I  .2730435  .0001667  2.0849 0.1167  P    10.331     .500    -2.990     .300  -.054000   .255000   .2741000      .000      .000  
+80 529 44388.00 I  -.053166  .003978   .241634  .000900  I  .2710025  .0001635  1.9961 0.1250  P    10.201     .500    -3.048     .300  -.054000   .256000   .2719000      .000      .000  
+80 530 44389.00 I  -.053503  .004237   .242657  .001801  I  .2690501  .0001863  1.9107 0.1239  P    10.206     .500    -3.188     .300  -.054000   .257000   .2697000      .000      .000  
+80 531 44390.00 I  -.053835  .004237   .243694  .001801  I  .2671735  .0001863  1.8479 0.1851  P    10.305     .500    -3.328     .300  -.054000   .257990   .2676000      .000      .000  
+80 6 1 44391.00 I  -.054155  .002580   .244745  .002210  I  .2653405  .0003200  1.8264 0.1609  P    10.330     .500    -3.394     .300  -.054000   .259000   .2655000      .000      .000  
+80 6 2 44392.00 I  -.054454  .002290   .245810  .001568  I  .2635027  .0002624  1.8589 0.2069  P    10.163     .500    -3.384     .300  -.054000   .260000   .2634000      .000      .000  
+80 6 3 44393.00 I  -.054729  .002290   .246888  .001568  I  .2616043  .0002624  1.9465 0.1695  P     9.916     .500    -3.348     .300  -.054000   .261000   .2613000      .000      .000  
+80 6 4 44394.00 I  -.054978  .002256   .247978  .001657  I  .2595953  .0002145  2.0772 0.1634  P     9.844     .500    -3.314     .300  -.054000   .262000   .2592000      .000      .000  
+80 6 5 44395.00 I  -.055198  .002074   .249076  .001294  I  .2574431  .0001947  2.2282 0.1448  P    10.078     .500    -3.245     .300  -.054000   .263000   .2572000      .000      .000  
+80 6 6 44396.00 I  -.055390  .002074   .250181  .001294  I  .2551422  .0001947  2.3692 0.1125  P    10.469     .500    -3.083     .300  -.054000   .264000   .2551000      .000      .000  
+80 6 7 44397.00 I  -.055555  .002074   .251291  .001294  I  .2527187  .0001129  2.4681 0.1774  P    10.684     .500    -2.835     .300  -.054000   .264000   .2531000      .000      .000  
+80 6 8 44398.00 I  -.055697  .004688   .252403  .001294  I  .2502291  .0002966  2.4977 0.1587  P    10.457     .500    -2.607     .300  -.054000   .264990   .2509990      .000      .000  
+80 6 9 44399.00 I  -.055819  .004688   .253518  .001294  I  .2477506  .0002966  2.4454 0.2197  P     9.787     .500    -2.552     .300  -.054000   .266000   .2490000      .000      .000  
+80 610 44400.00 I  -.055929  .004586   .254635  .001408  I  .2453624  .0003241  2.3207 0.2197  P     8.946     .500    -2.741     .300  -.054000   .266990   .2470000      .000      .000  
+80 611 44401.00 I  -.056030  .004586   .255754  .001408  I  .2431236  .0003241  2.1528 0.2292  P     8.299     .500    -3.070     .300  -.054000   .268000   .2451000      .000      .000  
+80 612 44402.00 I  -.056121  .004586   .256876  .001408  I  .2410578  .0003241  1.9819 0.2292  P     8.073     .500    -3.317     .300  -.053000   .268000   .2431000      .000      .000  
+80 613 44403.00 I  -.056197  .004586   .258000  .001408  I  .2391486  .0003241  1.8446 0.1662  P     8.267     .500    -3.350     .300  -.053000   .269000   .2412000      .000      .000  
+80 614 44404.00 I  -.056252  .002177   .259130  .001585  I  .2373506  .0000740  1.7614 0.1662  P     8.683     .500    -3.247     .300  -.053000   .270000   .2392000      .000      .000  
+80 615 44405.00 I  -.056283  .002177   .260267  .001585  I  .2356071  .0000740  1.7341 0.0445  P     9.045     .500    -3.188     .300  -.053000   .271000   .2373000      .000      .000  
+80 616 44406.00 I  -.056288  .001876   .261414  .001590  I  .2338680  .0000494  1.7498 0.0445  P     9.135     .500    -3.248     .300  -.053000   .271000   .2355000      .000      .000  
+80 617 44407.00 I  -.056263  .001876   .262575  .001590  I  .2320990  .0000494  1.7910 0.0349  P     8.920     .500    -3.340     .300  -.052000   .272000   .2336000      .000      .000  
+80 618 44408.00 I  -.056206  .001876   .263751  .001590  I  .2302829  .0000494  1.8415 0.1129  P     8.566     .500    -3.371     .300  -.052000   .272000   .2318000      .000      .000  
+80 619 44409.00 I  -.056120  .001783   .264944  .001318  I  .2284177  .0002204  1.8870 0.1350  P     8.280     .500    -3.351     .300  -.052000   .273000   .2299000      .000      .000  
+80 620 44410.00 I  -.056007  .001211   .266154  .001435  I  .2265144  .0002654  1.9161 0.1725  P     8.121     .500    -3.343     .300  -.051000   .273990   .2281000      .000      .000  
+80 621 44411.00 I  -.055869  .001211   .267379  .001435  I  .2245938  .0002654  1.9203 0.1906  P     7.988     .500    -3.349     .300  -.051000   .273990   .2263000      .000      .000  
+80 622 44412.00 I  -.055710  .004611   .268620  .000645  I  .2226833  .0002737  1.8958 0.1906  P     7.765     .500    -3.300     .300  -.050000   .275000   .2245000      .000      .000  
+80 623 44413.00 I  -.055531  .004611   .269874  .000645  I  .2208110  .0002737  1.8448 0.1935  P     7.422     .500    -3.137     .300  -.050000   .275000   .2227000      .000      .000  
+80 624 44414.00 I  -.055334  .004611   .271140  .000645  I  .2190006  .0002737  1.7731 0.1448  P     7.010     .500    -2.883     .300  -.049000   .276000   .2209000      .000      .000  
+80 625 44415.00 I  -.055118  .006326   .272416  .000823  I  .2172690  .0000947  1.6887 0.2162  P     6.629     .500    -2.646     .300  -.048000   .277000   .2192000      .000      .000  
+80 626 44416.00 I  -.054881  .005269   .273700  .000678  I  .2156238  .0003348  1.6024 0.1740  P     6.395     .500    -2.556     .300  -.048000   .277000   .2175000      .000      .000  
+80 627 44417.00 I  -.054612  .005269   .274989  .000678  I  .2140596  .0003348  1.5299 0.2861  P     6.382     .500    -2.662     .300  -.047000   .278000   .2157000      .000      .000  
+80 628 44418.00 I  -.054299  .003938   .276282  .000493  I  .2125530  .0004640  1.4905 0.2413  P     6.533     .500    -2.864     .300  -.047000   .278000   .2140000      .000      .000  
+80 629 44419.00 I  -.053930  .011309   .277574  .003053  I  .2110616  .0003477  1.5022 0.2899  P     6.666     .500    -2.992     .300  -.046000   .279000   .2123000      .000      .000  
+80 630 44420.00 I  -.053497  .011309   .278865  .003053  I  .2095287  .0003477  1.5736 0.2904  P     6.614     .500    -2.956     .300  -.045000   .280000   .2106000      .000      .000  
+80 7 1 44421.00 I  -.052996  .011321   .280150  .003820  I  .2078977  .0004653  1.6952 0.2907  P     6.375     .500    -2.827     .300  -.044000   .280000   .2089000      .000      .000  
+80 7 2 44422.00 I  -.052422  .013582   .281429  .004665  I  .2061299  .0004659  1.8419 0.3292  P     6.114     .500    -2.732     .300  -.044000   .280990   .2073000      .000      .000  
+80 7 3 44423.00 I  -.051780  .013582   .282702  .004665  I  .2042170  .0004659  1.9794 0.3294  P     6.016     .500    -2.716     .300  -.043000   .280990   .2056000      .000      .000  
+80 7 4 44424.00 I  -.051095  .013582   .283971  .004665  I  .2021850  .0004659  2.0751 0.3591  P     6.120     .500    -2.723     .300  -.042000   .282000   .2039000      .000      .000  
+80 7 5 44425.00 I  -.050384  .008098   .285238  .003602  I  .2000888  .0005465  2.1048 0.3591  P     6.263     .500    -2.691     .300  -.041000   .283000   .2022000      .000      .000  
+80 7 6 44426.00 I  -.049652  .008098   .286501  .003602  I  .1980003  .0005465  2.0600 0.3730  P     6.193     .500    -2.637     .300  -.041000   .283000   .2006000      .000      .000  
+80 7 7 44427.00 I  -.048902  .004104   .287755  .003600  I  .1959907  .0005077  1.9497 0.3730  P     5.761     .500    -2.644     .300  -.040000   .284000   .1989000      .000      .000  
+80 7 8 44428.00 I  -.048137  .004104   .288998  .003600  I  .1941150  .0005077  1.7970 0.3590  P     5.068     .500    -2.776     .300  -.040000   .284000   .1973000      .000      .000  
+80 7 9 44429.00 I  -.047357  .004104   .290226  .003600  I  .1924001  .0005077  1.6340 0.3590  P     4.420     .500    -3.011     .300  -.039000   .285000   .1956000      .000      .000  
+80 710 44430.00 I  -.046564  .004104   .291435  .003600  I  .1908393  .0005077  1.4941 0.3638  P     4.120     .500    -3.234     .300  -.037990   .286000   .1939000      .000      .000  
+80 711 44431.00 I  -.045762  .005388   .292623  .003541  I  .1893957  .0005212  1.4027 0.3638  P     4.256     .500    -3.328     .300  -.037990   .287000   .1923000      .000      .000  
+80 712 44432.00 I  -.044962  .005388   .293793  .003541  I  .1880149  .0005212  1.3683 0.3127  P     4.644     .500    -3.288     .300  -.037000   .287000   .1906000      .000      .000  
+80 713 44433.00 I  -.044175  .004264   .294946  .001270  I  .1866427  .0003457  1.3831 0.3127  P     5.002     .500    -3.203     .300  -.037000   .287990   .1890000      .000      .000  
+80 714 44434.00 I  -.043412  .004264   .296085  .001270  I  .1852377  .0003457  1.4310 0.2444  P     5.152     .500    -3.154     .300  -.035990   .289000   .1873000      .000      .000  
+80 715 44435.00 I  -.042680  .004264   .297212  .001270  I  .1837753  .0003457  1.4952 0.2444  P     5.073     .500    -3.137     .300  -.035000   .290000   .1857000      .000      .000  
+80 716 44436.00 I  -.041982  .004264   .298330  .001270  I  .1822469  .0003457  1.5607 0.2423  P     4.808     .500    -3.125     .300  -.035000   .291000   .1841000      .000      .000  
+80 717 44437.00 I  -.041317  .004120   .299440  .002369  I  .1806576  .0003396  1.6153 0.2423  P     4.418     .500    -3.142     .300  -.034000   .291000   .1824000      .000      .000  
+80 718 44438.00 I  -.040682  .004120   .300543  .002369  I  .1790231  .0003396  1.6496 0.2386  P     4.024     .500    -3.220     .300  -.034000   .292000   .1808000      .000      .000  
+80 719 44439.00 I  -.040070  .003623   .301639  .002290  I  .1773670  .0003352  1.6582 0.2386  P     3.763     .500    -3.320     .300  -.033000   .293000   .1792000      .000      .000  
+80 720 44440.00 I  -.039481  .003623   .302728  .002290  I  .1757154  .0003352  1.6408 0.2370  P     3.629     .500    -3.341     .300  -.032000   .294000   .1776000      .000      .000  
+80 721 44441.00 I  -.038915  .003623   .303805  .002290  I  .1740930  .0003352  1.6006 0.2274  P     3.428     .500    -3.218     .300  -.032000   .294000   .1760000      .000      .000  
+80 722 44442.00 I  -.038378  .004011   .304868  .002431  I  .1725194  .0003074  1.5448 0.3275  P     3.007     .500    -2.976     .300  -.031000   .294990   .1743000      .000      .000  
+80 723 44443.00 I  -.037876  .003453   .305911  .002205  I  .1710052  .0005628  1.4835 0.1701  P     2.484     .500    -2.713     .300  -.031000   .294990   .1727000      .000      .000  
+80 724 44444.00 I  -.037416  .001017   .306930  .000822  I  .1695499  .0001457  1.4295 0.2870  P     2.138     .500    -2.558     .300  -.030000   .296000   .1711000      .000      .000  
+80 725 44445.00 I  -.037005  .000753   .307918  .001028  I  .1681384  .0001127  1.3988 0.0923  P     2.101     .500    -2.609     .300  -.030000   .297000   .1695000      .000      .000  
+80 726 44446.00 I  -.036652  .000749   .308869  .001035  I  .1667384  .0001133  1.4097 0.0798  P     2.251     .500    -2.840     .300  -.029000   .297000   .1679000      .000      .000  
+80 727 44447.00 I  -.036373  .000749   .309774  .001035  I  .1653007  .0001129  1.4755 0.0795  P     2.396     .500    -3.078     .300  -.029000   .298000   .1662000      .000      .000  
+80 728 44448.00 I  -.036176  .000571   .310620  .001099  I  .1637689  .0001116  1.5970 0.0587  P     2.457     .500    -3.132     .300  -.028000   .298000   .1646000      .000      .000  
+80 729 44449.00 I  -.036043  .000571   .311387  .001099  I  .1620941  .0000322  1.7564 0.0603  P     2.433     .500    -2.977     .300  -.028000   .299000   .1630000      .000      .000  
+80 730 44450.00 I  -.035959  .000571   .312081  .001099  I  .1602557  .0000457  1.9169 0.0441  P     2.307     .500    -2.780     .300  -.028000   .299000   .1614000      .000      .000  
+80 731 44451.00 I  -.035911  .000476   .312715  .001427  I  .1582745  .0000822  2.0349 0.1115  P     2.085     .500    -2.715     .300  -.027000   .300000   .1598000      .000      .000  
+80 8 1 44452.00 I  -.035885  .002027   .313299  .003060  I  .1562108  .0002183  2.0783 0.1242  P     1.853     .500    -2.803     .300  -.027000   .300000   .1581000      .000      .000  
+80 8 2 44453.00 I  -.035873  .002027   .313843  .003060  I  .1541461  .0002343  2.0372 0.1779  P     1.712     .500    -2.935     .300  -.026000   .301000   .1565000      .000      .000  
+80 8 3 44454.00 I  -.035866  .001859   .314353  .001591  I  .1521605  .0002809  1.9239 0.1829  P     1.663     .500    -3.022     .300  -.026000   .301000   .1549000      .000      .000  
+80 8 4 44455.00 I  -.035856  .001859   .314838  .001591  I  .1503126  .0002809  1.7676 0.1897  P     1.582     .500    -3.072     .300  -.026000   .301990   .1533000      .000      .000  
+80 8 5 44456.00 I  -.035834  .001859   .315303  .001591  I  .1486280  .0002549  1.6034 0.1619  P     1.357     .500    -3.143     .300  -.025000   .301990   .1517000      .000      .000  
+80 8 6 44457.00 I  -.035796  .002414   .315753  .001929  I  .1470977  .0001612  1.4637 0.1603  P     1.028     .500    -3.262     .300  -.025000   .303000   .1500000      .000      .000  
+80 8 7 44458.00 I  -.035736  .002722   .316194  .001863  I  .1456845  .0001945  1.3722 0.1263  P      .797     .500    -3.398     .300  -.024000   .303000   .1484000      .000      .000  
+80 8 8 44459.00 I  -.035652  .002722   .316631  .001863  I  .1443340  .0001945  1.3386 0.0999  P      .852     .500    -3.500     .300  -.024000   .303990   .1468000      .000      .000  
+80 8 9 44460.00 I  -.035543  .003253   .317071  .002470  I  .1429894  .0000461  1.3586 0.1023  P     1.178     .500    -3.533     .300  -.024000   .305000   .1451000      .000      .000  
+80 810 44461.00 I  -.035410  .002307   .317518  .001896  I  .1416036  .0000636  1.4181 0.0393  P     1.569     .500    -3.487     .300  -.023000   .305000   .1435000      .000      .000  
+80 811 44462.00 I  -.035258  .002307   .317974  .001896  I  .1401463  .0000636  1.4984 0.0533  P     1.827     .500    -3.358     .300  -.023000   .306000   .1418000      .000      .000  
+80 812 44463.00 I  -.035091  .000542   .318443  .001027  I  .1386056  .0000855  1.5823 0.0533  P     1.881     .500    -3.163     .300  -.022000   .306000   .1402000      .000      .000  
+80 813 44464.00 I  -.034914  .000542   .318927  .001027  I  .1369849  .0000855  1.6566 0.0605  P     1.728     .500    -2.979     .300  -.022000   .307000   .1385000      .000      .000  
+80 814 44465.00 I  -.034730  .000542   .319428  .001027  I  .1352991  .0000855  1.7110 0.0605  P     1.393     .500    -2.921     .300  -.022000   .308000   .1368000      .000      .000  
+80 815 44466.00 I  -.034545  .000542   .319948  .001027  I  .1335716  .0000855  1.7394 0.0978  P     1.033     .500    -3.029     .300  -.021000   .308000   .1351000      .000      .000  
+80 816 44467.00 I  -.034361  .000944   .320488  .001281  I  .1318295  .0001759  1.7401 0.0978  P      .895     .500    -3.205     .300  -.021000   .308990   .1333000      .000      .000  
+80 817 44468.00 I  -.034185  .000944   .321048  .001281  I  .1300996  .0001759  1.7162 0.1451  P     1.017     .500    -3.297     .300  -.020000   .308990   .1316000      .000      .000  
+80 818 44469.00 I  -.034021  .001122   .321629  .001503  I  .1284021  .0002307  1.6772 0.1201  P     1.082     .500    -3.255     .300  -.020000   .310000   .1298990      .000      .000  
+80 819 44470.00 I  -.033876  .001473   .322231  .002151  I  .1267463  .0001635  1.6349 0.1414  P      .771     .500    -3.136     .300  -.020000   .310990   .1280990      .000      .000  
+80 820 44471.00 I  -.033753  .001473   .322852  .002151  I  .1251295  .0001635  1.6011 0.1082  P      .200     .500    -3.009     .300  -.020000   .310990   .1263000      .000      .000  
+80 821 44472.00 I  -.033655  .002643   .323491  .001843  I  .1235369  .0001418  1.5891 0.0870  P     -.191     .500    -2.910     .300  -.018990   .312000   .1244000      .000      .000  
+80 822 44473.00 I  -.033585  .003139   .324146  .001991  I  .1219387  .0000597  1.6150 0.0769  P     -.189     .500    -2.895     .300  -.018990   .312000   .1226000      .000      .000  
+80 823 44474.00 I  -.033544  .003139   .324811  .001991  I  .1202889  .0000597  1.6951 0.0422  P      .000     .500    -3.025     .300  -.018990   .313000   .1208000      .000      .000  
+80 824 44475.00 I  -.033532  .003139   .325482  .001991  I  .1185273  .0000597  1.8389 0.0595  P      .109     .500    -3.242     .300  -.018990   .313000   .1189000      .000      .000  
+80 825 44476.00 I  -.033545  .003095   .326154  .002490  I  .1165928  .0001030  2.0377 0.0595  P      .140     .500    -3.348     .300  -.018990   .314000   .1170000      .000      .000  
+80 826 44477.00 I  -.033575  .003095   .326823  .002490  I  .1144442  .0001030  2.2596 0.0760  P      .207     .500    -3.213     .300  -.017990   .314000   .1151000      .000      .000  
+80 827 44478.00 I  -.033616  .002868   .327484  .002436  I  .1120823  .0001117  2.4553 0.0760  P      .265     .500    -2.945     .300  -.017990   .315000   .1132000      .000      .000  
+80 828 44479.00 I  -.033658  .002868   .328135  .002436  I  .1095587  .0001117  2.5758 0.0790  P      .145     .500    -2.779     .300  -.017990   .315000   .1113000      .000      .000  
+80 829 44480.00 I  -.033692  .002868   .328774  .002436  I  .1069650  .0001117  2.5933 0.0790  P     -.195     .500    -2.809     .300  -.017990   .315000   .1093000      .000      .000  
+80 830 44481.00 I  -.033706  .002868   .329403  .002436  I  .1044053  .0001117  2.5113 0.0703  P     -.591     .500    -2.925     .300  -.017990   .316000   .1073000      .000      .000  
+80 831 44482.00 I  -.033692  .002928   .330022  .000447  I  .1019650  .0000854  2.3616 0.0703  P     -.829     .500    -2.997     .300  -.017000   .316000   .1052000      .000      .000  
+80 9 1 44483.00 I  -.033648  .002928   .330632  .000447  I  .0996895  .0000854  2.1897 0.0456  P     -.830     .500    -3.032     .300  -.017000   .317000   .1032000      .000      .000  
+80 9 2 44484.00 I  -.033568  .001883   .331235  .000068  I  .0975787  .0000320  2.0384 0.0456  P     -.689     .500    -3.114     .300  -.017000   .317000   .1012000      .000      .000  
+80 9 3 44485.00 I  -.033449  .001883   .331832  .000068  I  .0955965  .0000320  1.9358 0.0226  P     -.583     .500    -3.254     .300  -.017000   .317990   .0991000      .000      .000  
+80 9 4 44486.00 I  -.033291  .001883   .332424  .000068  I  .0936871  .0000320  1.8934 0.0226  P     -.580     .500    -3.380     .300  -.016000   .317990   .0969000      .000      .000  
+80 9 5 44487.00 I  -.033093  .001883   .333013  .000068  I  .0917909  .0000320  1.9076 0.0192  P     -.562     .500    -3.445     .300  -.016000   .319000   .0948000      .000      .000  
+80 9 6 44488.00 I  -.032856  .001388   .333598  .000202  I  .0898578  .0000212  1.9643 0.0192  P     -.375     .500    -3.457     .300  -.015000   .319000   .0926000      .000      .000  
+80 9 7 44489.00 I  -.032585  .001388   .334183  .000202  I  .0878542  .0000212  2.0453 0.0157  P     -.035     .500    -3.409     .300  -.015000   .320000   .0905000      .000      .000  
+80 9 8 44490.00 I  -.032281  .000025   .334766  .000279  I  .0857654  .0000231  2.1318 0.0464  P      .288     .500    -3.246     .300  -.015000   .321000   .0883000      .000      .000  
+80 9 9 44491.00 I  -.031950  .000932   .335350  .000197  I  .0835940  .0000903  2.2082 0.0466  P      .450     .500    -2.942     .300  -.014000   .321000   .0861000      .000      .000  
+80 910 44492.00 I  -.031593  .000932   .335936  .000197  I  .0813562  .0000903  2.2632 0.0998  P      .421     .500    -2.612     .300  -.014000   .322000   .0838000      .000      .000  
+80 911 44493.00 I  -.031215  .000901   .336526  .000813  I  .0790771  .0001779  2.2898 0.1177  P      .264     .500    -2.449     .300  -.013000   .322000   .0816000      .000      .000  
+80 912 44494.00 I  -.030818  .001104   .337119  .000976  I  .0767870  .0002173  2.2852 0.1404  P      .133     .500    -2.537     .300  -.013000   .323000   .0794000      .000      .000  
+80 913 44495.00 I  -.030404  .001104   .337719  .000976  I  .0745165  .0002173  2.2511 0.1751  P      .227     .500    -2.753     .300  -.013000   .324000   .0771000      .000      .000  
+80 914 44496.00 I  -.029974  .000902   .338327  .001336  I  .0722922  .0002747  2.1948 0.1953  P      .562     .500    -2.904     .300  -.012000   .324000   .0748000      .000      .000  
+80 915 44497.00 I  -.029527  .000594   .338943  .001637  I  .0701300  .0003245  2.1293 0.2219  P      .823     .500    -2.937     .300  -.012000   .324990   .0724000      .000      .000  
+80 916 44498.00 I  -.029065  .000594   .339567  .001637  I  .0680318  .0003485  2.0693 0.2334  P      .659     .500    -2.944     .300  -.011000   .324990   .0701000      .000      .000  
+80 917 44499.00 I  -.028584  .002438   .340199  .002022  I  .0659849  .0003355  2.0289 0.2381  P      .159     .500    -2.978     .300  -.011000   .326000   .0678000      .000      .000  
+80 918 44500.00 I  -.028086  .002438   .340837  .002022  I  .0639626  .0003246  2.0227 0.2277  P     -.207     .500    -2.983     .300  -.011000   .327000   .0653990      .000      .000  
+80 919 44501.00 I  -.027570  .002438   .341480  .002022  I  .0619229  .0003079  2.0662 0.2126  P     -.154     .500    -2.921     .300  -.010000   .327000   .0630000      .000      .000  
+80 920 44502.00 I  -.027034  .002693   .342124  .004047  I  .0598094  .0002745  2.1721 0.1997  P      .119     .500    -2.873     .300  -.010000   .328000   .0607000      .000      .000  
+80 921 44503.00 I  -.026481  .002693   .342766  .004047  I  .0575568  .0002544  2.3439 0.1775  P      .289     .500    -2.903     .300  -.008990   .328000   .0583000      .000      .000  
+80 922 44504.00 I  -.025918  .002693   .343399  .004047  I  .0551051  .0002252  2.5654 0.1603  P      .329     .500    -2.918     .300  -.008990   .329000   .0559000      .000      .000  
+80 923 44505.00 I  -.025350  .001619   .344019  .005294  I  .0524227  .0001951  2.7968 0.1311  P      .411     .500    -2.770     .300  -.008990   .330000   .0534000      .000      .000  
+80 924 44506.00 I  -.024784  .000801   .344617  .001255  I  .0495267  .0001342  2.9828 0.1101  P      .563     .500    -2.503     .300  -.008000   .331000   .0510000      .000      .000  
+80 925 44507.00 I  -.024228  .000543   .345183  .001540  I  .0464885  .0001023  3.0748 0.0822  P      .623     .500    -2.327     .300  -.008000   .332000   .0485000      .000      .000  
+80 926 44508.00 I  -.023692  .000467   .345704  .001118  I  .0434152  .0000950  3.0524 0.0700  P      .467     .500    -2.344     .300  -.007000   .333000   .0461000      .000      .000  
+80 927 44509.00 I  -.023185  .000570   .346167  .001768  I  .0404165  .0000956  2.9316 0.0660  I     -.660    1.471    -2.476     .528  -.007000   .334000   .0436000      .000      .000  
+80 928 44510.00 I  -.022722  .000515   .346558  .001644  I  .0375738  .0000917  2.7456 0.0680  I     -.993    1.386    -2.515     .626  -.007000   .335000   .0411000      .000      .000  
+80 929 44511.00 I  -.022320  .000514   .346877  .001673  I  .0349343  .0000966  2.5328 0.0661  I    -1.152    1.658    -2.512     .660  -.007000   .336000   .0385000      .000      .000  
+80 930 44512.00 I  -.021988  .000499   .347173  .001613  I  .0324995  .0000951  2.3463 0.0637  I    -1.074    1.927    -2.591     .647  -.006000   .337000   .0360000      .000      .000  
+8010 1 44513.00 I  -.021717  .000547   .347498  .001781  I  .0302196  .0000831  2.2263 0.0661  I     -.882    1.882    -2.771     .648  -.006000   .338000   .0334000      .000      .000  
+8010 2 44514.00 I  -.021472  .000633   .347867  .001801  I  .0280224  .0000919  2.1799 0.0644  I     -.806    1.681    -2.929     .620  -.006000   .339000   .0309000      .000      .000  
+8010 3 44515.00 I  -.021217  .001128   .348281  .002333  I  .0258395  .0000985  2.1946 0.0666  I     -.930    2.409    -2.945     .977  -.006000   .340000   .0283000      .000      .000  
+8010 4 44516.00 I  -.020922  .001231   .348738  .000922  I  .0236201  .0000965  2.2486 0.0740  I    -1.047    2.764    -2.867     .787  -.006000   .341000   .0258000      .000      .000  
+8010 5 44517.00 I  -.020573  .002357   .349238  .001086  I  .0213366  .0001105  2.3195 0.0734  I     -.949    2.622    -2.792     .618  -.005000   .341000   .0232000      .000      .000  
+8010 6 44518.00 I  -.020162  .002357   .349784  .001086  I  .0189812  .0001105  2.3900 0.1465  I     -.721    1.266    -2.706     .792  -.005000   .342000   .0207000      .000      .000  
+8010 7 44519.00 I  -.019683  .005552   .350378  .002087  I  .0165613  .0002714  2.4464 0.1954  I     -.580    1.982    -2.539     .868  -.005000   .343000   .0181000      .000      .000  
+8010 8 44520.00 I  -.019138  .004820   .351018  .002289  I  .0140960  .0003749  2.4799 0.2553  I     -.600    1.982    -2.332     .868  -.005000   .344000   .0155000      .000      .000  
+8010 9 44521.00 I  -.018535  .003477   .351705  .002418  I  .0116109  .0004325  2.4853 0.2862  I     -.682    1.982    -2.248     .868  -.005000   .345000   .0129000      .000      .000  
+801010 44522.00 I  -.017881  .003477   .352436  .002418  I  .0091350  .0004325  2.4618 0.3161  I     -.679    1.982    -2.389     .868  -.004000   .345000   .0103000      .000      .000  
+801011 44523.00 I  -.017190  .002022   .353206  .003017  I  .0066954  .0004610  2.4138 0.3161  I     -.488    1.982    -2.663     .868  -.004000   .346000   .0077000      .000      .000  
+801012 44524.00 I  -.016473  .002022   .354013  .003017  I  .0043126  .0004610  2.3501 0.3260  I     -.112    1.982    -2.889     .868  -.004000   .347000   .0051000      .000      .000  
+801013 44525.00 I  -.015748  .002022   .354848  .003017  I  .0019960  .0004610  2.2842 0.2345  I      .270    1.982    -2.996     .868  -.004000   .348000   .0025000      .000      .000  
+801014 44526.00 I  -.015032  .000542   .355706  .001021  I -.0002604  .0000860  2.2323 0.2344  I      .426    1.982    -3.053     .868  -.004000   .349000  -.0002000      .000      .000  
+801015 44527.00 I  -.014340  .001289   .356578  .000752  I -.0024781  .0000855  2.2089 0.0589  I      .377    1.428    -3.098     .685  -.004000   .349000  -.0028000      .000      .000  
+801016 44528.00 I  -.013690  .001154   .357455  .001460  I -.0046917  .0000804  2.2261 0.0557  I      .448    1.401    -3.038     .727  -.004000   .350000  -.0055000      .000      .000  
+801017 44529.00 I  -.013099  .001067   .358326  .001278  I -.0069477  .0000715  2.2954 0.0538  I      .914    1.613    -2.765     .714  -.004000   .351000  -.0081000      .000      .000  
+801018 44530.00 I  -.012591  .000945   .359173  .001190  I -.0093009  .0000716  2.4198 0.0501  I     1.707    1.554    -2.315     .717  -.004000   .352000  -.0108000      .000      .000  
+801019 44531.00 I  -.012196  .001057   .359957  .001123  I -.0118060  .0000702  2.6002 0.0460  I     2.454    1.415    -1.924     .606  -.004000   .353000  -.0135000      .000      .000  
+801020 44532.00 I  -.011860  .000983   .360634  .001100  I -.0145143  .0000577  2.8197 0.0470  I     2.932    1.617    -1.681     .569  -.004000   .353000  -.0161000      .000      .000  
+801021 44533.00 I  -.011534  .001043   .361203  .001153  I -.0174444  .0000626  3.0362 0.0400  I     3.217    1.901    -1.473     .482  -.004000   .354000  -.0188000      .000      .000  
+801022 44534.00 I  -.011167  .000798   .361676  .001287  I -.0205670  .0000553  3.1937 0.0420  I     3.424    2.029    -1.304     .346  -.004000   .355000  -.0215000      .000      .000  
+801023 44535.00 I  -.010731  .000829   .362085  .000778  I -.0237945  .0000559  3.2395 0.0421  I     3.623    1.954    -1.282     .349  -.004000   .356000  -.0242000      .000      .000  
+801024 44536.00 I  -.010254  .000889   .362467  .000840  I -.0270050  .0000635  3.1625 0.0405  I     3.776    2.014    -1.448     .327  -.004000   .357000  -.0268000      .000      .000  
+801025 44537.00 I  -.009746  .001057   .362830  .000886  I -.0300888  .0000587  2.9935 0.0428  I     3.846    2.362    -1.637     .369  -.005000   .358000  -.0295000      .000      .000  
+801026 44538.00 I  -.009207  .000638   .363175  .001019  I -.0329788  .0000575  2.7850 0.1426  I     3.880    2.538    -1.654     .296  -.005000   .359000  -.0321000      .000      .000  
+801027 44539.00 I  -.008637  .002485   .363503  .000490  I -.0356645  .0002791  2.5936 0.1425  I     3.991    1.982    -1.576     .868  -.005000   .360000  -.0348000      .000      .000  
+801028 44540.00 I  -.008038  .002485   .363816  .000490  I -.0381850  .0002791  2.4590 0.1804  I     4.303    1.982    -1.656     .868  -.005000   .361000  -.0375000      .000      .000  
+801029 44541.00 I  -.007413  .002371   .364115  .000823  I -.0406058  .0002287  2.3941 0.1435  I     4.754    1.982    -1.974     .868  -.006000   .362000  -.0402000      .000      .000  
+801030 44542.00 I  -.006765  .002722   .364402  .000935  I -.0429935  .0000669  2.3899 0.1191  I     5.083    1.982    -2.304     .868  -.006000   .363000  -.0428000      .000      .000  
+801031 44543.00 I  -.006092  .002722   .364677  .000935  I -.0453991  .0000669  2.4261 0.0473  I     5.150    1.982    -2.395     .868  -.007000   .364000  -.0455000      .000      .000  
+8011 1 44544.00 I  -.005390  .002722   .364943  .000935  I -.0478520  .0000669  2.4812 0.0724  I     5.151    1.982    -2.247     .868  -.007000   .365000  -.0482000      .000      .000  
+8011 2 44545.00 I  -.004658  .002255   .365200  .001130  I -.0503616  .0001285  2.5366 0.0724  I     5.359    1.982    -2.037     .868  -.007000   .366000  -.0508000      .000      .000  
+8011 3 44546.00 I  -.003896  .002255   .365451  .001130  I -.0529205  .0001285  2.5778 0.1866  I     5.749    1.982    -1.870     .868  -.007000   .366000  -.0534000      .000      .000  
+8011 4 44547.00 I  -.003106  .001864   .365698  .000858  I -.0555091  .0003503  2.5948 0.1866  I     6.058    1.982    -1.705     .868  -.006000   .367000  -.0560000      .000      .000  
+8011 5 44548.00 I  -.002287  .001864   .365945  .000858  I -.0581001  .0003503  2.5820 0.2477  I     6.152    1.982    -1.504     .868  -.006000   .367000  -.0586000      .000      .000  
+8011 6 44549.00 I  -.001437  .001864   .366193  .000858  I -.0606628  .0003503  2.5383 0.2477  I     6.138    1.982    -1.333     .868  -.006000   .368000  -.0612000      .000      .000  
+8011 7 44550.00 I  -.000553  .001864   .366443  .000858  I -.0631675  .0003503  2.4671 0.2490  I     6.149    1.982    -1.285     .868  -.006000   .369000  -.0638000      .000      .000  
+8011 8 44551.00 I   .000371  .002807   .366696  .000524  I -.0655907  .0003539  2.3770 0.2490  I     6.215    1.982    -1.363     .868  -.005000   .369000  -.0664000      .000      .000  
+8011 9 44552.00 I   .001340  .002807   .366953  .000524  I -.0679193  .0003539  2.2802 0.2014  I     6.325    1.982    -1.489     .868  -.005000   .370000  -.0689000      .000      .000  
+801110 44553.00 I   .002361  .003802   .367214  .000272  I -.0701540  .0001923  2.1920 0.1896  I     6.457    1.982    -1.602     .868  -.004000   .370000  -.0715000      .000      .000  
+801111 44554.00 I   .003438  .002688   .367480  .000333  I -.0723119  .0001360  2.1295 0.1178  I     6.532    1.982    -1.710     .868  -.004000   .371000  -.0741000      .000      .000  
+801112 44555.00 I   .004576  .002688   .367750  .000333  I -.0744269  .0001360  2.1086 0.0962  I     6.468    1.982    -1.825     .868  -.003000   .371000  -.0767000      .000      .000  
+801113 44556.00 I   .005773  .002688   .368025  .000333  I -.0765465  .0001360  2.1400 0.1146  I     6.313    1.982    -1.899     .868  -.002000   .372000  -.0792000      .000      .000  
+801114 44557.00 I   .007027  .000517   .368305  .002281  I -.0787259  .0001846  2.2285 0.1146  I     6.222    1.982    -1.867     .868  -.002000   .372000  -.0818000      .000      .000  
+801115 44558.00 I   .008338  .000517   .368590  .002281  I -.0810214  .0001846  2.3709 0.1305  I     6.284    1.982    -1.724     .868  -.001000   .373000  -.0843000      .000      .000  
+801116 44559.00 I   .009704  .000517   .368879  .002281  I -.0834809  .0001846  2.5530 0.1482  I     6.454    1.982    -1.520     .868   .000000   .373000  -.0869000      .000      .000  
+801117 44560.00 I   .011124  .000573   .369171  .002338  I -.0861313  .0002319  2.7467 0.1482  I     6.627    1.982    -1.287     .868   .001000   .373000  -.0894000      .000      .000  
+801118 44561.00 I   .012595  .000573   .369466  .002338  I -.0889644  .0002319  2.9105 0.1531  I     6.698    1.982    -1.036     .868   .002000   .374000  -.0919000      .000      .000  
+801119 44562.00 I   .014116  .001322   .369758  .002084  I -.0919282  .0002000  3.0018 0.1543  I     6.591    1.982     -.847     .868   .004000   .374000  -.0945000      .000      .000  
+801120 44563.00 I   .015682  .001534   .370042  .001178  I -.0949346  .0002036  2.9928 0.1427  I     6.321    1.982     -.861     .868   .005000   .375000  -.0970000      .000      .000  
+801121 44564.00 I   .017292  .001534   .370316  .001178  I -.0978809  .0002036  2.8849 0.1440  I     6.026    1.982    -1.095     .868   .006000   .375000  -.0995000      .000      .000  
+801122 44565.00 I   .018944  .001534   .370574  .001178  I -.1006828  .0002036  2.7120 0.1311  I     5.843    1.982    -1.340     .868   .007000   .375000  -.1020000      .000      .000  
+801123 44566.00 I   .020631  .003481   .370816  .001025  I -.1033006  .0001652  2.5260 0.1311  I     5.802    1.982    -1.351     .868   .008000   .376000  -.1045000      .000      .000  
+801124 44567.00 I   .022349  .003481   .371039  .001025  I -.1057468  .0001652  2.3759 0.1161  I     5.887    1.982    -1.149     .868   .010000   .376000  -.1069000      .000      .000  
+801125 44568.00 I   .024091  .003403   .371242  .000366  I -.1080740  .0001633  2.2905 0.0905  I     6.122    1.982    -1.010     .868   .011000   .377000  -.1094000      .000      .000  
+801126 44569.00 I   .025851  .003403   .371423  .000366  I -.1103500  .0000740  2.2717 0.0896  I     6.463    1.982    -1.120     .868   .012000   .377000  -.1119000      .000      .000  
+801127 44570.00 I   .027614  .003403   .371580  .000366  I -.1126341  .0000740  2.3029 0.0523  I     6.723    1.982    -1.343     .868   .013000   .377000  -.1143000      .000      .000  
+801128 44571.00 I   .029368  .003403   .371712  .000366  I -.1149645  .0000740  2.3603 0.0427  I     6.755    1.982    -1.404     .868   .014000   .377000  -.1168000      .000      .000  
+801129 44572.00 I   .031095  .002329   .371818  .000982  I -.1173563  .0000428  2.4224 0.0427  I     6.668    1.982    -1.216     .868   .016000   .378000  -.1192000      .000      .000  
+801130 44573.00 I   .032776  .002329   .371896  .000982  I -.1198056  .0000428  2.4733 0.0286  I     6.682    1.982     -.931     .868   .017000   .378000  -.1217000      .000      .000  
+8012 1 44574.00 I   .034394  .002365   .371947  .001015  I -.1222956  .0000378  2.5022 0.0288  I     6.810    1.982     -.720     .868   .018000   .378000  -.1241000      .000      .000  
+8012 2 44575.00 I   .035930  .002270   .371968  .000974  I -.1248005  .0000384  2.5025 0.0269  I     6.859    1.982     -.593     .868   .019000   .378000  -.1265000      .000      .000  
+8012 3 44576.00 I   .037371  .002270   .371959  .000974  I -.1272907  .0000384  2.4731 0.0272  I     6.738    1.982     -.464     .868   .021000   .378000  -.1289000      .000      .000  
+8012 4 44577.00 I   .038728  .002270   .371917  .000974  I -.1297379  .0000384  2.4174 0.0472  I     6.567    1.982     -.297     .868   .022000   .377000  -.1314000      .000      .000  
+8012 5 44578.00 I   .040017  .002221   .371841  .000850  I -.1321190  .0000863  2.3421 0.1378  I     6.467    1.982     -.144     .868   .024000   .377000  -.1338000      .000      .000  
+8012 6 44579.00 I   .041254  .002669   .371728  .001991  I -.1344185  .0002729  2.2561 0.1431  I     6.424    1.982     -.084     .868   .025000   .377000  -.1362000      .000      .000  
+8012 7 44580.00 I   .042454  .002669   .371577  .001991  I -.1366321  .0002729  2.1728 0.1968  I     6.414    1.982     -.147     .868   .026000   .377000  -.1386000      .000      .000  
+8012 8 44581.00 I   .043634  .002673   .371385  .002008  I -.1387701  .0002835  2.1076 0.1968  I     6.513    1.982     -.292     .868   .028000   .376000  -.1410000      .000      .000  
+8012 9 44582.00 I   .044806  .002673   .371153  .002008  I -.1408587  .0002835  2.0765 0.1881  I     6.753    1.982     -.430     .868   .029000   .376000  -.1434000      .000      .000  
+801210 44583.00 I   .045978  .004254   .370882  .001797  I -.1429387  .0002474  2.0923 0.1573  I     7.011    1.982     -.487     .868   .031000   .375000  -.1458000      .000      .000  
+801211 44584.00 I   .047160  .004480   .370575  .000960  I -.1450613  .0001363  2.1620 0.1412  I     7.125    1.982     -.447     .868   .032000   .375000  -.1482000      .000      .000  
+801212 44585.00 I   .048360  .004480   .370233  .000960  I -.1472792  .0001363  2.2813 0.0964  I     7.097    1.982     -.348     .868   .033000   .374000  -.1506000      .000      .000  
+801213 44586.00 I   .049583  .004480   .369860  .000960  I -.1496355  .0001363  2.4356 0.0944  I     7.073    1.982     -.226     .868   .034000   .374000  -.1530000      .000      .000  
+801214 44587.00 I   .050829  .004472   .369458  .001345  I -.1521542  .0001305  2.6014 0.1027  I     7.173    1.982     -.082     .868   .036000   .373000  -.1553000      .000      .000  
+801215 44588.00 I   .052094  .004472   .369033  .001345  I -.1548320  .0001536  2.7484 0.0906  I     7.355    1.982      .104     .868   .037000   .373000  -.1577000      .000      .000  
+801216 44589.00 I   .053373  .003725   .368587  .001228  I -.1576339  .0001256  2.8442 0.0801  I     7.447    1.982      .314     .868   .038000   .372000  -.1601000      .000      .000  
+801217 44590.00 I   .054663  .001492   .368127  .001099  I -.1604947  .0000456  2.8631 0.0668  I     7.287    1.982      .459     .868   .039000   .371000  -.1625000      .000      .000  
+801218 44591.00 I   .055958  .001492   .367656  .001099  I -.1633315  .0000456  2.7964 0.0322  I     6.876    1.982      .426     .868   .040000   .371000  -.1649000      .000      .000  
+801219 44592.00 I   .057255  .001492   .367181  .001099  I -.1660643  .0000456  2.6600 0.0339  I     6.414    1.982      .208     .868   .042000   .370000  -.1672000      .000      .000  
+801220 44593.00 I   .058546  .001492   .366708  .001261  I -.1686409  .0000502  2.4916 0.0291  I     6.137    1.982     -.028     .868   .043000   .370000  -.1696000      .000      .000  
+801221 44594.00 I   .059819  .001492   .366243  .001261  I -.1710521  .0000363  2.3370 0.0302  I     6.146    1.982     -.061     .868   .044000   .369000  -.1720000      .000      .000  
+801222 44595.00 I   .061068  .000472   .365777  .001847  I -.1733318  .0000336  2.2332 0.0247  I     6.389    1.982      .151     .868   .045000   .368000  -.1744000      .000      .000  
+801223 44596.00 I   .062292  .000472   .365303  .001847  I -.1755406  .0000336  2.1956 0.0526  I     6.780    1.982      .399     .868   .046000   .368000  -.1768000      .000      .000  
+801224 44597.00 I   .063487  .000121   .364811  .004080  I -.1777423  .0000997  2.2160 0.0526  I     7.231    1.982      .452     .868   .048000   .367000  -.1791000      .000      .000  
+801225 44598.00 I   .064652  .000121   .364297  .004080  I -.1799847  .0000997  2.2726 0.0505  I     7.625    1.982      .317     .868   .049000   .367000  -.1815000      .000      .000  
+801226 44599.00 I   .065782  .000172   .363758  .005104  I -.1822914  .0000163  2.3408 0.0505  I     7.853    1.982      .204     .868   .050000   .366000  -.1839000      .000      .000  
+801227 44600.00 I   .066876  .000172   .363194  .005104  I -.1846637  .0000163  2.4013 0.0261  I     7.910    1.982      .267     .868   .051000   .365000  -.1863000      .000      .000  
+801228 44601.00 I   .067929  .000629   .362608  .000838  I -.1870873  .0000496  2.4419 0.0261  I     7.867    1.982      .445     .868   .052000   .364000  -.1886000      .000      .000  
+801229 44602.00 I   .068940  .000629   .362000  .000838  I -.1895385  .0000496  2.4556 0.0351  I     7.749    1.982      .587     .868   .053000   .364000  -.1910000      .000      .000  
+801230 44603.00 I   .069905  .000629   .361371  .000838  I -.1919885  .0000496  2.4395 0.0351  I     7.536    1.982      .639     .868   .054000   .363000  -.1933000      .000      .000  
+801231 44604.00 I   .070820  .000629   .360722  .000838  I -.1944082  .0000496  2.3955 0.0351  I     7.275    1.982      .677     .868   .055000   .362000  -.1957000      .000      .000  
+81 1 1 44605.00 I   .071683  .000629   .360054  .000838  I -.1967724  .0000496  2.3300 0.0306  I     7.091    1.982      .780     .868   .056000   .361000  -.1981000      .000      .000  
+81 1 2 44606.00 I   .072490  .003327   .359365  .001604  I -.1990643  .0000357  2.2527 0.0252  I     7.042    1.982      .932     .868   .057000   .360000  -.2004000      .000      .000  
+81 1 3 44607.00 I   .073235  .004662   .358658  .002109  I -.2012778  .0000091  2.1753 0.0184  I     7.058    1.982     1.024     .868   .057000   .360000  -.2028000      .000      .000  
+81 1 4 44608.00 I   .073915  .004662   .357930  .002109  I -.2034193  .0000091  2.1112 0.0064  I     7.082    1.982      .949     .868   .058000   .359000  -.2051000      .000      .000  
+81 1 5 44609.00 I   .074525  .004662   .357183  .002109  I -.2055098  .0000091  2.0759 0.0064  I     7.173    1.982      .707     .868   .059000   .358000  -.2075000      .000      .000  
+81 1 6 44610.00 I   .075064  .004662   .356414  .002109  I -.2075860  .0000091  2.0852 0.0064  I     7.410    1.982      .436     .868   .060000   .357000  -.2099000      .000      .000  
+81 1 7 44611.00 I   .075536  .004662   .355623  .002109  I -.2096986  .0000091  2.1496 0.2433  I     7.738    1.982      .309     .868   .060000   .356000  -.2122000      .000      .000  
+81 1 8 44612.00 I   .075945  .001337   .354805  .004055  I -.2119034  .0004866  2.2686 0.2433  I     7.998    1.982      .377     .868   .061000   .355000  -.2146000      .000      .000  
+81 1 9 44613.00 I   .076296  .001337   .353958  .004055  I -.2142488  .0004866  2.4267 0.3441  I     8.095    1.982      .534     .868   .061000   .354000  -.2169000      .000      .000  
+81 110 44614.00 I   .076594  .001337   .353079  .004055  I -.2167605  .0004866  2.5959 0.3441  I     8.075    1.982      .642     .868   .062000   .353000  -.2193000      .000      .000  
+81 111 44615.00 I   .076842  .001337   .352166  .004055  I -.2194331  .0004866  2.7425 0.3119  I     8.039    1.982      .663     .868   .062000   .352000  -.2216000      .000      .000  
+81 112 44616.00 I   .077049  .001157   .351219  .004521  I -.2222276  .0003903  2.8352 0.3119  I     8.014    1.982      .650     .868   .063000   .351000  -.2239000      .000      .000  
+81 113 44617.00 I   .077220  .001157   .350241  .004521  I -.2250792  .0003903  2.8548 0.2347  I     7.922    1.982      .651     .868   .063000   .350000  -.2263000      .000      .000  
+81 114 44618.00 I   .077364  .000943   .349233  .004943  I -.2279115  .0002606  2.7972 0.2347  I     7.666    1.982      .642     .868   .064000   .349000  -.2286000      .000      .000  
+81 115 44619.00 I   .077487  .000943   .348200  .004943  I -.2306522  .0002606  2.6753 0.1843  I     7.246    1.982      .563     .868   .064000   .348000  -.2309000      .000      .000  
+81 116 44620.00 I   .077597  .000943   .347144  .004943  I -.2332501  .0002606  2.5174 0.1843  I     6.809    1.982      .392     .868   .064000   .347000  -.2332000      .000      .000  
+81 117 44621.00 I   .077698  .000943   .346074  .004943  I -.2356873  .0002606  2.3605 0.1507  I     6.555    1.982      .207     .868   .065000   .346000  -.2355000      .000      .000  
+81 118 44622.00 I   .077796  .001051   .345000  .000397  I -.2379834  .0001514  2.2404 0.1507  I     6.596    1.982      .130     .868   .065000   .346000  -.2379000      .000      .000  
+81 119 44623.00 I   .077897  .001051   .343929  .000397  I -.2401875  .0001514  2.1784 0.0818  I     6.874    1.982      .209     .868   .066000   .345000  -.2402000      .000      .000  
+81 120 44624.00 I   .078006  .000865   .342872  .001453  I -.2423598  .0000620  2.1752 0.0949  I     7.240    1.982      .336     .868   .066000   .344000  -.2425000      .000      .000  
+81 121 44625.00 I   .078128  .000973   .341837  .001453  I -.2445519  .0001145  2.2140 0.0651  I     7.575    1.982      .340     .868   .066000   .343000  -.2448000      .000      .000  
+81 122 44626.00 I   .078270  .000973   .340832  .001453  I -.2467935  .0001145  2.2699 0.0810  I     7.843    1.982      .174     .868   .067000   .342000  -.2471000      .000      .000  
+81 123 44627.00 I   .078438  .000973   .339867  .001453  I -.2490897  .0001145  2.3197 0.0798  I     8.038    1.982     -.033     .868   .067000   .342000  -.2493000      .000      .000  
+81 124 44628.00 I   .078639  .000961   .338947  .001529  I -.2514260  .0001111  2.3487 0.0784  I     8.108    1.982     -.131     .868   .068000   .341000  -.2516000      .000      .000  
+81 125 44629.00 I   .078874  .000996   .338070  .001649  I -.2537780  .0001070  2.3502 0.0791  I     7.982    1.982     -.106     .868   .068000   .340000  -.2539000      .000      .000  
+81 126 44630.00 I   .079143  .000996   .337230  .001649  I -.2561161  .0001127  2.3207 0.0722  I     7.664    1.982     -.062     .868   .068000   .339000  -.2562000      .000      .000  
+81 127 44631.00 I   .079447  .003128   .336422  .001942  I -.2584095  .0000969  2.2614 0.0743  I     7.283    1.982     -.065     .868   .069000   .339000  -.2584000      .000      .000  
+81 128 44632.00 I   .079787  .003128   .335641  .001942  I -.2606311  .0000969  2.1786 0.0685  I     7.014    1.982     -.078     .868   .069000   .338000  -.2607000      .000      .000  
+81 129 44633.00 I   .080160  .003128   .334884  .001942  I -.2627623  .0000969  2.0827 0.0685  I     6.942    1.982     -.036     .868   .070000   .338000  -.2629000      .000      .000  
+81 130 44634.00 I   .080567  .003128   .334149  .001942  I -.2647967  .0000969  1.9873 0.0707  I     7.028    1.982      .070     .868   .070000   .337000  -.2652000      .000      .000  
+81 131 44635.00 I   .081002  .003175   .333433  .001215  I -.2667414  .0001030  1.9055 0.0523  I     7.181    1.982      .164     .868   .070000   .336000  -.2674000      .000      .000  
+81 2 1 44636.00 I   .081459  .003175   .332735  .001215  I -.2686169  .0000394  1.8512 0.1544  I     7.331    1.982      .130     .868   .071000   .335000  -.2697000      .000      .000  
+81 2 2 44637.00 I   .081926  .004944   .332049  .000859  I -.2704577  .0002912  1.8387 0.1469  I     7.448    1.982     -.091     .868   .071000   .335000  -.2719000      .000      .000  
+81 2 3 44638.00 I   .082394  .004944   .331372  .000859  I -.2723128  .0002912  1.8818 0.2059  I     7.552    1.982     -.421     .868   .072000   .334000  -.2742000      .000      .000  
+81 2 4 44639.00 I   .082853  .004944   .330701  .000859  I -.2742421  .0002912  1.9872 0.2059  I     7.718    1.982     -.665     .868   .072000   .333000  -.2764000      .000      .000  
+81 2 5 44640.00 I   .083296  .004944   .330031  .000859  I -.2763049  .0002912  2.1457 0.2323  I     8.005    1.982     -.668     .868   .073000   .332000  -.2787000      .000      .000  
+81 2 6 44641.00 I   .083721  .006842   .329357  .000263  I -.2785419  .0003620  2.3293 0.2323  I     8.358    1.982     -.470     .868   .073000   .331000  -.2810000      .000      .000  
+81 2 7 44642.00 I   .084131  .006842   .328675  .000263  I -.2809579  .0003620  2.4962 0.3602  I     8.617    1.982     -.271     .868   .074000   .330000  -.2833000      .000      .000  
+81 2 8 44643.00 I   .084534  .009490   .327978  .003544  I -.2835154  .0006228  2.6063 0.3602  I     8.647    1.982     -.236     .868   .074000   .329000  -.2856000      .000      .000  
+81 2 9 44644.00 I   .084935  .009490   .327264  .003544  I -.2861427  .0006228  2.6331 0.4404  I     8.459    1.982     -.366     .868   .075000   .328000  -.2879000      .000      .000  
+81 210 44645.00 I   .085339  .009490   .326526  .003544  I -.2887526  .0006228  2.5730 0.3814  I     8.165    1.982     -.551     .868   .076000   .327000  -.2902000      .000      .000  
+81 211 44646.00 I   .085753  .007697   .325761  .003145  I -.2912661  .0004404  2.4452 0.3814  I     7.869    1.982     -.700     .868   .076000   .326000  -.2925000      .000      .000  
+81 212 44647.00 I   .086171  .007697   .324966  .003145  I -.2936321  .0004404  2.2847 0.3114  I     7.620    1.982     -.801     .868   .077000   .325000  -.2949000      .000      .000  
+81 213 44648.00 I   .086582  .007697   .324142  .003145  I -.2958376  .0004404  2.1304 0.2202  I     7.469    1.982     -.885     .868   .077000   .324000  -.2972000      .000      .000  
+81 214 44649.00 I   .086975  .005331   .323288  .002687  I -.2979063  .0000014  2.0161 0.2202  I     7.494    1.982     -.968     .868   .078000   .323000  -.2995000      .000      .000  
+81 215 44650.00 I   .087342  .005331   .322406  .002687  I -.2998904  .0000014  1.9635 0.0010  I     7.751    1.982    -1.034     .868   .079000   .322000  -.3019000      .000      .000  
+81 216 44651.00 I   .087683  .005331   .321500  .002687  I -.3018556  .0000014  1.9777 0.1176  I     8.178    1.982    -1.070     .868   .079000   .321000  -.3043000      .000      .000  
+81 217 44652.00 I   .087995  .002863   .320574  .000780  I -.3038641  .0002351  2.0469 0.1176  I     8.604    1.982    -1.112     .868   .080000   .320000  -.3066000      .000      .000  
+81 218 44653.00 I   .088275  .002863   .319631  .000780  I -.3059601  .0002351  2.1484 0.1662  I     8.878    1.982    -1.220     .868   .080000   .319000  -.3090000      .000      .000  
+81 219 44654.00 I   .088523  .002863   .318674  .000780  I -.3081632  .0002351  2.2569 0.1662  I     9.003    1.982    -1.401     .868   .081000   .318000  -.3114000      .000      .000  
+81 220 44655.00 I   .088735  .002863   .317707  .000780  I -.3104694  .0002351  2.3519 0.2364  I     9.078    1.982    -1.577     .868   .082000   .317000  -.3138000      .000      .000  
+81 221 44656.00 I   .088911  .002312   .316734  .000681  I -.3128585  .0004103  2.4212 0.2364  I     9.115    1.982    -1.662     .868   .082000   .316000  -.3162000      .000      .000  
+81 222 44657.00 I   .089055  .002312   .315759  .000681  I -.3153014  .0004103  2.4592 0.3354  I     9.008    1.982    -1.663     .868   .083000   .315000  -.3187000      .000      .000  
+81 223 44658.00 I   .089169  .001579   .314787  .000566  I -.3177663  .0005306  2.4655 0.3067  I     8.725    1.982    -1.653     .868   .083000   .314000  -.3211000      .000      .000  
+81 224 44659.00 I   .089258  .003146   .313822  .000451  I -.3202234  .0004560  2.4448 0.3498  I     8.427    1.982    -1.678     .868   .084000   .313000  -.3235000      .000      .000  
+81 225 44660.00 I   .089324  .003146   .312869  .000451  I -.3226500  .0004560  2.4063 0.4189  I     8.301    1.982    -1.722     .868   .084000   .312000  -.3260000      .000      .000  
+81 226 44661.00 I   .089374  .004341   .311931  .001623  I -.3250343  .0007029  2.3628 0.4496  I     8.363    1.982    -1.757     .868   .085000   .311000  -.3285000      .000      .000  
+81 227 44662.00 I   .089414  .005198   .311013  .001947  I -.3273788  .0007749  2.3290 0.5231  I     8.520    1.982    -1.768     .868   .085000   .311000  -.3309000      .000      .000  
+81 228 44663.00 I   .089444  .005198   .310118  .001947  I -.3297000  .0007749  2.3184 0.5132  I     8.753    1.982    -1.751     .868   .086000   .310000  -.3334000      .000      .000  
+81 3 1 44664.00 I   .089465  .004331   .309250  .001603  I -.3320271  .0006730  2.3423 0.5506  I     9.072    1.982    -1.734     .868   .086000   .309000  -.3359000      .000      .000  
+81 3 2 44665.00 I   .089485  .004414   .308410  .001952  I -.3343997  .0007824  2.4114 0.5160  I     9.347    1.982    -1.809     .868   .086000   .308000  -.3384000      .000      .000  
+81 3 3 44666.00 I   .089511  .004414   .307600  .001952  I -.3368684  .0007824  2.5361 0.4387  I     9.410    1.982    -2.049     .868   .087000   .307000  -.3410000      .000      .000  
+81 3 4 44667.00 I   .089556  .001491   .306819  .000351  I -.3394912  .0003972  2.7186 0.4156  I     9.351    1.982    -2.368     .868   .087000   .307000  -.3435000      .000      .000  
+81 3 5 44668.00 I   .089628  .001760   .306068  .003608  I -.3423190  .0002809  2.9412 0.2432  I     9.494    1.982    -2.537     .868   .088000   .306000  -.3461000      .000      .000  
+81 3 6 44669.00 I   .089735  .001760   .305347  .003608  I -.3453741  .0002809  3.1652 0.1406  I     9.979    1.982    -2.422     .868   .088000   .305000  -.3486000      .000      .000  
+81 3 7 44670.00 I   .089884  .001992   .304656  .005090  I -.3486333  .0000108  3.3407 0.1889  I    10.513    1.982    -2.158     .868   .088000   .304000  -.3512000      .000      .000  
+81 3 8 44671.00 I   .090081  .003957   .303995  .003771  I -.3520246  .0002526  3.4234 0.1264  I    10.673    1.982    -2.024     .868   .088000   .303000  -.3537000      .000      .000  
+81 3 9 44672.00 I   .090330  .003957   .303357  .003771  I -.3554424  .0002526  3.3931 0.2131  I    10.361    1.982    -2.153     .868   .089000   .303000  -.3563000      .000      .000  
+81 310 44673.00 I   .090635  .003231   .302736  .003399  I -.3587773  .0003434  3.2622 0.2453  I     9.865    1.982    -2.435     .868   .089000   .302000  -.3588000      .000      .000  
+81 311 44674.00 I   .090999  .003698   .302126  .002091  I -.3619463  .0004205  3.0696 0.2715  I     9.521    1.982    -2.679     .868   .089000   .301000  -.3614000      .000      .000  
+81 312 44675.00 I   .091420  .003698   .301521  .002091  I -.3649125  .0004205  2.8649 0.2973  I     9.448    1.982    -2.802     .868   .089000   .300000  -.3640000      .000      .000  
+81 313 44676.00 I   .091892  .003698   .300917  .002091  I -.3676865  .0004205  2.6914 0.3380  I     9.568    1.982    -2.843     .868   .089000   .299000  -.3666000      .000      .000  
+81 314 44677.00 I   .092409  .000511   .300309  .001763  I -.3703147  .0005293  2.5763 0.3380  I     9.786    1.982    -2.862     .868   .089000   .299000  -.3691000      .000      .000  
+81 315 44678.00 I   .092963  .000511   .299696  .001763  I -.3728610  .0005293  2.5271 0.3759  I    10.086    1.982    -2.887     .868   .089000   .298000  -.3717000      .000      .000  
+81 316 44679.00 I   .093548  .000549   .299077  .000556  I -.3753878  .0005339  2.5346 0.3759  I    10.482    1.982    -2.933     .868   .089000   .297000  -.3743000      .000      .000  
+81 317 44680.00 I   .094158  .000549   .298450  .000556  I -.3779422  .0005339  2.5782 0.3775  I    10.910    1.982    -3.018     .868   .089000   .296000  -.3769000      .000      .000  
+81 318 44681.00 I   .094786  .000549   .297814  .000556  I -.3805484  .0005339  2.6344 0.3582  I    11.232    1.982    -3.130     .868   .089000   .296000  -.3795000      .000      .000  
+81 319 44682.00 I   .095427  .000455   .297169  .002017  I -.3832082  .0004776  2.6821 0.3392  I    11.369    1.982    -3.209     .868   .089000   .295000  -.3821000      .000      .000  
+81 320 44683.00 I   .096077  .000226   .296513  .002470  I -.3859045  .0004184  2.7056 0.3175  I    11.377    1.982    -3.191     .868   .089000   .295000  -.3847000      .000      .000  
+81 321 44684.00 I   .096730  .000226   .295846  .002470  I -.3886086  .0004184  2.6966 0.2689  I    11.335    1.982    -3.088     .868   .089000   .294000  -.3873000      .000      .000  
+81 322 44685.00 I   .097382  .000129   .295168  .003403  I -.3912862  .0003380  2.6530 0.2446  I    11.230    1.982    -2.995     .868   .089000   .293000  -.3899000      .000      .000  
+81 323 44686.00 I   .098029  .000731   .294476  .002657  I -.3939040  .0002536  2.5777 0.2113  I    11.045    1.982    -2.988     .868   .089000   .292000  -.3926000      .000      .000  
+81 324 44687.00 I   .098666  .000731   .293764  .002657  I -.3964337  .0002536  2.4788 0.1337  I    10.884    1.982    -3.060     .868   .089000   .292000  -.3952000      .000      .000  
+81 325 44688.00 I   .099288  .000762   .293029  .001538  I -.3988580  .0000847  2.3693 0.1337  I    10.855    1.982    -3.161     .868   .089000   .291000  -.3979000      .000      .000  
+81 326 44689.00 I   .099892  .000762   .292266  .001538  I -.4011740  .0000847  2.2651 0.0599  I    10.907    1.982    -3.285     .868   .089000   .290000  -.4005000      .000      .000  
+81 327 44690.00 I   .100472  .000762   .291472  .001538  I -.4033956  .0000847  2.1832 0.0599  I    10.952    1.982    -3.444     .868   .089000   .289000  -.4031000      .000      .000  
+81 328 44691.00 I   .101022  .000762   .290643  .001538  I -.4055526  .0000847  2.1379 0.0467  I    11.097    1.982    -3.573     .868   .089000   .288000  -.4058000      .000      .000  
+81 329 44692.00 I   .101536  .001438   .289778  .001910  I -.4076876  .0000394  2.1411 0.0467  I    11.502    1.982    -3.573     .868   .088000   .288000  -.4084000      .000      .000  
+81 330 44693.00 I   .102008  .001438   .288877  .001910  I -.4098543  .0000394  2.2029 0.0341  I    12.021    1.982    -3.482     .868   .088000   .287000  -.4111000      .000      .000  
+81 331 44694.00 I   .102433  .002007   .287939  .002259  I -.4121152  .0000556  2.3303 0.1498  I    12.267    1.982    -3.493     .868   .088000   .286000  -.4137000      .000      .000  
+81 4 1 44695.00 I   .102804  .002506   .286963  .002112  I -.4145355  .0002970  2.5195 0.1511  I    12.143    1.982    -3.708     .868   .088000   .285000  -.4164000      .000      .000  
+81 4 2 44696.00 I   .103118  .002506   .285951  .002112  I -.4171670  .0002970  2.7471 0.2100  I    12.038    1.982    -3.955     .868   .088000   .284000  -.4191000      .000      .000  
+81 4 3 44697.00 I   .103374  .002506   .284904  .002112  I -.4200277  .0002970  2.9688 0.2232  I    12.305    1.982    -3.967     .868   .087000   .283000  -.4217000      .000      .000  
+81 4 4 44698.00 I   .103570  .002105   .283826  .001456  I -.4230844  .0003333  3.1299 0.2232  I    12.740    1.982    -3.735     .868   .087000   .282000  -.4244000      .000      .000  
+81 4 5 44699.00 I   .103706  .002105   .282720  .001456  I -.4262527  .0003333  3.1867 0.2357  I    12.817    1.982    -3.539     .868   .087000   .281000  -.4271000      .000      .000  
+81 4 6 44700.00 I   .103787  .002105   .281591  .001456  I -.4294194  .0003333  3.1282 0.2039  I    12.349    1.982    -3.610     .868   .087000   .280000  -.4298000      .000      .000  
+81 4 7 44701.00 I   .103815  .000740   .280444  .001019  I -.4324794  .0002351  2.9807 0.2039  I    11.686    1.982    -3.883     .868   .087000   .279000  -.4326000      .000      .000  
+81 4 8 44702.00 I   .103796  .000740   .279284  .001019  I -.4353684  .0002351  2.7962 0.2191  I    11.319    1.982    -4.131     .868   .087000   .279000  -.4353000      .000      .000  
+81 4 9 44703.00 I   .103731  .001025   .278118  .001173  I -.4380784  .0003698  2.6315 0.2429  I    11.417    1.982    -4.246     .868   .087000   .278000  -.4381000      .000      .000  
+81 410 44704.00 I   .103622  .001187   .276950  .001361  I -.4406514  .0004251  2.5269 0.2817  I    11.779    1.982    -4.286     .868   .087000   .277000  -.4408000      .000      .000  
+81 411 44705.00 I   .103472  .001187   .275789  .001361  I -.4431571  .0004251  2.4972 0.3006  I    12.088    1.982    -4.313     .868   .087000   .276000  -.4435000      .000      .000  
+81 412 44706.00 I   .103281  .001187   .274641  .001361  I -.4456681  .0004251  2.5346 0.3033  I    12.198    1.982    -4.317     .868   .087000   .276000  -.4462000      .000      .000  
+81 413 44707.00 I   .103048  .001773   .273513  .001374  I -.4482414  .0004328  2.6173 0.3033  I    12.231    1.982    -4.291     .868   .087000   .275000  -.4490000      .000      .000  
+81 414 44708.00 I   .102772  .001773   .272412  .001374  I -.4509090  .0004328  2.7190 0.3431  I    12.391    1.982    -4.274     .868   .087000   .275000  -.4517000      .000      .000  
+81 415 44709.00 I   .102451  .003506   .271342  .005022  I -.4536782  .0005325  2.8171 0.3431  I    12.692    1.982    -4.275     .868   .087000   .274000  -.4544000      .000      .000  
+81 416 44710.00 I   .102084  .003506   .270310  .005022  I -.4565359  .0005325  2.8934 0.3765  I    12.941    1.982    -4.214     .868   .087000   .273000  -.4571000      .000      .000  
+81 417 44711.00 I   .101671  .003506   .269319  .005022  I -.4594538  .0005325  2.9361 0.3765  I    12.972    1.982    -4.018     .868   .087000   .273000  -.4598000      .000      .000  
+81 418 44712.00 I   .101215  .003506   .268373  .005022  I -.4623949  .0005325  2.9393 0.3930  I    12.813    1.982    -3.749     .868   .087000   .272000  -.4626000      .000      .000  
+81 419 44713.00 I   .100723  .003367   .267472  .004999  I -.4653187  .0005781  2.9016 0.3930  I    12.589    1.982    -3.577     .868   .087000   .272000  -.4653000      .000      .000  
+81 420 44714.00 I   .100204  .003367   .266611  .004999  I -.4681860  .0005781  2.8276 0.3869  I    12.391    1.982    -3.604     .868   .087000   .271000  -.4680000      .000      .000  
+81 421 44715.00 I   .099670  .002566   .265784  .001453  I -.4709654  .0005144  2.7280 0.3869  I    12.277    1.982    -3.762     .868   .087000   .270000  -.4707000      .000      .000  
+81 422 44716.00 I   .099132  .002566   .264986  .001453  I -.4736384  .0005144  2.6175 0.3637  I    12.253    1.982    -3.930     .868   .087000   .270000  -.4734000      .000      .000  
+81 423 44717.00 I   .098598  .002566   .264212  .001453  I -.4762027  .0005144  2.5140 0.3637  I    12.221    1.982    -4.089     .868   .087000   .269000  -.4761000      .000      .000  
+81 424 44718.00 I   .098078  .002566   .263458  .001453  I -.4786745  .0005144  2.4349 0.3930  I    12.100    1.982    -4.295     .868   .087000   .269000  -.4788000      .000      .000  
+81 425 44719.00 I   .097577  .003294   .262721  .001723  I -.4810851  .0005942  2.3937 0.3459  I    12.031    1.982    -4.510     .868   .087000   .268000  -.4815000      .000      .000  
+81 426 44720.00 I   .097098  .002585   .262000  .001247  I -.4834781  .0004626  2.4010 0.3271  I    12.278    1.982    -4.572     .868   .087000   .267000  -.4841000      .000      .000  
+81 427 44721.00 I   .096643  .001585   .261292  .000374  I -.4859053  .0002739  2.4627 0.3155  I    12.818    1.982    -4.416     .868   .087000   .266000  -.4868000      .000      .000  
+81 428 44722.00 I   .096213  .002760   .260597  .000294  I -.4884219  .0004291  2.5795 0.2545  I    13.242    1.982    -4.213     .868   .087000   .266000  -.4894000      .000      .000  
+81 429 44723.00 I   .095810  .002760   .259912  .000294  I -.4910793  .0004291  2.7415 0.3034  I    13.240    1.982    -4.179     .868   .087000   .265000  -.4921000      .000      .000  
+81 430 44724.00 I   .095437  .002760   .259235  .000294  I -.4939106  .0004291  2.9208 0.2888  I    12.987    1.982    -4.292     .868   .087000   .264000  -.4947000      .000      .000  
+81 5 1 44725.00 I   .095099  .002331   .258566  .000634  I -.4969117  .0003865  3.0725 0.3044  I    12.840    1.982    -4.323     .868   .087000   .263000  -.4973000      .000      .000  
+81 5 2 44726.00 I   .094796  .002625   .257901  .000731  I -.5000299  .0004319  3.1470 0.2898  I    12.801    1.982    -4.175     .868   .087000   .263000  -.4999000      .000      .000  
+81 5 3 44727.00 I   .094526  .002625   .257240  .000731  I -.5031687  .0004319  3.1103 0.2500  I    12.536    1.982    -4.030     .868   .086000   .262000  -.5025000      .000      .000  
+81 5 4 44728.00 I   .094287  .001886   .256579  .000760  I -.5062135  .0002520  2.9626 0.2500  I    11.898    1.982    -4.094     .868   .086000   .262000  -.5051000      .000      .000  
+81 5 5 44729.00 I   .094077  .001886   .255917  .000760  I -.5090697  .0002520  2.7424 0.1738  I    11.189    1.982    -4.313     .868   .086000   .261000  -.5077000      .000      .000  
+81 5 6 44730.00 I   .093897  .001559   .255250  .001048  I -.5116939  .0002393  2.5091 0.1655  I    10.860    1.982    -4.477     .868   .086000   .260000  -.5102000      .000      .000  
+81 5 7 44731.00 I   .093746  .001765   .254576  .001064  I -.5141026  .0002146  2.3198 0.1607  I    11.088    1.982    -4.511     .868   .086000   .259000  -.5127000      .000      .000  
+81 5 8 44732.00 I   .093621  .001765   .253891  .001064  I -.5163592  .0002146  2.2075 0.1517  I    11.638    1.982    -4.530     .868   .086000   .259000  -.5153000      .000      .000  
+81 5 9 44733.00 I   .093517  .001765   .253193  .001064  I -.5185444  .0002146  2.1754 0.1337  I    12.052    1.982    -4.604     .868   .086000   .258000  -.5178000      .000      .000  
+81 510 44734.00 I   .093428  .000332   .252480  .001047  I -.5207305  .0001594  2.2047 0.1337  I    12.015    1.982    -4.650     .868   .086000   .257000  -.5203000      .000      .000  
+81 511 44735.00 I   .093349  .000332   .251752  .001047  I -.5229648  .0001594  2.2670 0.0886  I    11.639    1.982    -4.579     .868   .086000   .256000  -.5227000      .000      .000  
+81 512 44736.00 I   .093274  .000202   .251007  .000219  I -.5252670  .0000774  2.3366 0.2351  I    11.356    1.982    -4.441     .868   .086000   .255000  -.5251000      .000      .000  
+81 513 44737.00 I   .093197  .000563   .250245  .000270  I -.5276341  .0004424  2.3943 0.2037  I    11.432    1.982    -4.330     .868   .086000   .255000  -.5276000      .000      .000  
+81 514 44738.00 I   .093113  .000563   .249466  .000270  I -.5300478  .0003999  2.4284 0.2982  I    11.674    1.982    -4.222     .868   .086000   .254000  -.5300000      .000      .000  
+81 515 44739.00 I   .093014  .000563   .248669  .000270  I -.5324811  .0003999  2.4329 0.3179  I    11.700    1.982    -4.027     .868   .086000   .253000  -.5324000      .000      .000  
+81 516 44740.00 I   .092896  .000652   .247853  .001354  I -.5349032  .0004944  2.4061 0.3179  I    11.398    1.982    -3.762     .868   .086000   .252000  -.5348000      .000      .000  
+81 517 44741.00 I   .092753  .000652   .247018  .001354  I -.5372836  .0004944  2.3503 0.3496  I    10.979    1.982    -3.586     .868   .086000   .251000  -.5371000      .000      .000  
+81 518 44742.00 I   .092580  .000652   .246162  .001354  I -.5395967  .0004944  2.2732 0.3149  I    10.670    1.982    -3.616     .868   .086000   .251000  -.5395000      .000      .000  
+81 519 44743.00 I   .092374  .001741   .245286  .001547  I -.5418269  .0003903  2.1868 0.3277  I    10.523    1.982    -3.792     .868   .086000   .250000  -.5418000      .000      .000  
+81 520 44744.00 I   .092128  .001741   .244389  .001547  I -.5439719  .0004304  2.1054 0.2905  I    10.471    1.982    -3.961     .868   .086000   .249000  -.5442000      .000      .000  
+81 521 44745.00 I   .091841  .001741   .243473  .001547  I -.5460448  .0004304  2.0454 0.2810  I    10.403    1.982    -4.067     .868   .086000   .248000  -.5465000      .000      .000  
+81 522 44746.00 I   .091507  .002409   .242539  .001103  I -.5480747  .0003615  2.0214 0.2510  I    10.221    1.982    -4.171     .868   .086000   .247000  -.5488000      .000      .000  
+81 523 44747.00 I   .091128  .002447   .241590  .001791  I -.5501027  .0002582  2.0428 0.2221  I     9.963    1.982    -4.305     .868   .086000   .246000  -.5511000      .000      .000  
+81 524 44748.00 I   .090703  .002447   .240630  .001791  I -.5521769  .0002582  2.1141 0.1316  I     9.834    1.982    -4.371     .868   .086000   .245000  -.5534000      .000      .000  
+81 525 44749.00 I   .090233  .002485   .239660  .002280  I -.5543473  .0000510  2.2345 0.1316  I     9.977    1.982    -4.259     .868   .086000   .244000  -.5557000      .000      .000  
+81 526 44750.00 I   .089719  .002485   .238686  .002280  I -.5566596  .0000510  2.3961 0.0361  I    10.240    1.982    -4.015     .868   .086000   .243000  -.5580000      .000      .000  
+81 527 44751.00 I   .089165  .002485   .237709  .002280  I -.5591475  .0000510  2.5816 0.0361  I    10.320    1.982    -3.798     .868   .086000   .242000  -.5602000      .000      .000  
+81 528 44752.00 I   .088574  .002485   .236737  .002280  I -.5618204  .0000510  2.7599 0.1674  I    10.094    1.982    -3.684     .868   .085000   .241000  -.5625000      .000      .000  
+81 529 44753.00 I   .087953  .003310   .235775  .002690  I -.5646512  .0003308  2.8900 0.1119  I     9.659    1.982    -3.604     .868   .085000   .240000  -.5647000      .000      .000  
+81 530 44754.00 I   .087307  .001168   .234829  .007887  I -.5675712  .0002180  2.9325 0.1981  I     9.099    1.982    -3.510     .868   .085000   .239000  -.5670000      .000      .000  
+81 531 44755.00 I   .086642  .001168   .233906  .007887  I -.5704801  .0002180  2.8668 0.1541  I     8.398    1.982    -3.492     .868   .085000   .238000  -.5692000      .000      .000  
+81 6 1 44756.00 I   .085963  .001168   .233012  .007887  I -.5732731  .0002180  2.7061 0.1541  I     7.611    1.982    -3.636     .868   .085000   .237000  -.5713000      .000      .000  
+81 6 2 44757.00 I   .085277  .001168   .232152  .007887  I -.5758756  .0002180  2.4954 0.1575  I     6.967    1.982    -3.838     .868   .085000   .236000  -.5735000      .000      .000  
+81 6 3 44758.00 I   .084589  .004077   .231328  .005654  I -.5782669  .0002274  2.2936 0.1388  I     6.715    1.982    -3.902     .868   .085000   .235000  -.5756000      .000      .000  
+81 6 4 44759.00 I   .083908  .004077   .230536  .005654  I -.5804817  .0001720  2.1485 0.1664  I     6.930    1.982    -3.808     .868   .085000   .234000  -.5778000      .000      .000  
+81 6 5 44760.00 I   .083242  .005646   .229771  .001316  I -.5825897  .0002430  2.0807 0.1219  I     7.427    1.982    -3.743     .868   .085000   .233000  -.5799000      .000      .000  
+81 6 6 44761.00 I   .082595  .004014   .229030  .001189  I -.5846656  .0001729  2.0805 0.1491  I     7.812    1.982    -3.829     .868   .085000   .232000  -.5819000      .000      .000  
+81 6 7 44762.00 I   .081969  .004014   .228306  .001189  I -.5867638  .0001729  2.1197 0.1122  I     7.712    1.982    -3.947     .868   .084000   .232000  -.5840000      .000      .000  
+81 6 8 44763.00 I   .081359  .003385   .227593  .001213  I -.5889077  .0001430  2.1670 0.0881  I     7.121    1.982    -3.918     .868   .084000   .231000  -.5860000      .000      .000  
+81 6 9 44764.00 I   .080762  .001117   .226886  .001159  I -.5910926  .0000341  2.1985 0.0735  I     6.457    1.982    -3.744     .868   .084000   .230000  -.5881000      .000      .000  
+81 610 44765.00 I   .080177  .001117   .226178  .001159  I -.5932950  .0000341  2.2007 0.0241  I     6.132    1.982    -3.579     .868   .084000   .229000  -.5900000      .000      .000  
+81 611 44766.00 I   .079601  .001117   .225466  .001159  I -.5954823  .0000341  2.1678 0.0861  I     6.094    1.982    -3.497     .868   .083000   .228000  -.5920000      .000      .000  
+81 612 44767.00 I   .079033  .002493   .224744  .003089  I -.5976188  .0001687  2.0995 0.0861  I     5.952    1.982    -3.419     .868   .083000   .227000  -.5939000      .000      .000  
+81 613 44768.00 I   .078473  .002493   .224009  .003089  I -.5996709  .0001687  1.9998 0.1343  I     5.503    1.982    -3.267     .868   .082000   .226000  -.5959000      .000      .000  
+81 614 44769.00 I   .077923  .003206   .223258  .004183  I -.6016105  .0002090  1.8762 0.1310  I     4.929    1.982    -3.098     .868   .082000   .225000  -.5978000      .000      .000  
+81 615 44770.00 I   .077383  .002495   .222489  .003090  I -.6034192  .0002004  1.7403 0.1448  I     4.485    1.982    -3.030     .868   .082000   .224000  -.5996000      .000      .000  
+81 616 44771.00 I   .076854  .002495   .221699  .003090  I -.6050920  .0002004  1.6070 0.1327  I     4.218    1.982    -3.088     .868   .081000   .223000  -.6014000      .000      .000  
+81 617 44772.00 I   .076331  .002196   .220890  .003278  I -.6066392  .0001739  1.4918 0.1210  I     4.044    1.982    -3.193     .868   .081000   .223000  -.6033000      .000      .000  
+81 618 44773.00 I   .075810  .001446   .220065  .002716  I -.6080868  .0001356  1.4101 0.1103  I     3.901    1.982    -3.259     .868   .080000   .222000  -.6051000      .000      .000  
+81 619 44774.00 I   .075287  .001446   .219227  .002716  I -.6094747  .0001356  1.3743 0.0949  I     3.722    1.982    -3.272     .868   .080000   .221000  -.6069000      .000      .000  
+81 620 44775.00 I   .074760  .001901   .218381  .002219  I -.6108528  .0001328  1.3908 0.0856  I     3.418    1.982    -3.268     .868   .080000   .220000  -.6086000      .000      .000  
+81 621 44776.00 I   .074228  .002082   .217529  .002566  I -.6122734  .0001045  1.4584 0.0845  I     3.002    1.982    -3.254     .868   .079000   .219000  -.6103000      .000      .000  
+81 622 44777.00 I   .073692  .002082   .216671  .002566  I -.6137833  .0001045  1.5669 0.0829  I     2.643    1.982    -3.198     .868   .079000   .218000  -.6120000      .000      .000  
+81 623 44778.00 I   .073151  .002581   .215806  .000137  I -.6154147  .0001287  1.6979 0.1435  I     2.482    1.982    -3.070     .868   .078000   .217000  -.6137000      .000      .000  
+81 624 44779.00 I   .072607  .001826   .214934  .001702  I -.6171786  .0002672  1.8272 0.1483  I     2.444    1.982    -2.886     .868   .078000   .216000  -.6154000      .000      .000  
+81 625 44780.00 I   .072064  .001826   .214053  .001702  I -.6190588  .0002672  1.9254 0.2353  I     2.289    1.982    -2.686     .868   .078000   .215000  -.6170000      .000      .000  
+81 626 44781.00 I   .071529  .000076   .213163  .002404  I -.6210088  .0003875  1.9619 0.2172  I     1.816    1.982    -2.515     .868   .077000   .214000  -.6186000      .000      .000  
+81 627 44782.00 I   .071005  .005605   .212264  .001880  I -.6229551  .0003425  1.9159 0.2586  I     1.002    1.982    -2.437     .868   .077000   .213000  -.6201000      .000      .000  
+81 628 44783.00 I   .070500  .005605   .211356  .001880  I -.6248128  .0003425  1.7866 0.2422  I      .012    1.982    -2.522     .868   .076000   .212000  -.6217000      .000      .000  
+81 629 44784.00 I   .070017  .005605   .210444  .001880  I -.6265089  .0003425  1.5991 0.2157  I     -.884    1.982    -2.761     .868   .076000   .211000  -.6233000      .000      .000  
+81 630 44785.00 I   .069562  .006753   .209527  .000803  I -.6280070  .0002624  1.3997 0.2157  I    -1.453    1.982    -2.995     .868   .076000   .210000  -.6248000      .000      .000  
+81 7 1 44786.00 I   .069135  .006753   .208610  .000803  I  .3706787  .0002624  1.2393 0.1855  I    -1.627    1.982    -3.033     .868   .075000   .209000   .3738000      .000      .000  
+81 7 2 44787.00 I   .068727  .006753   .207692  .000803  I  .3694899  .0002624  1.1523 0.1598  I    -1.491    1.982    -2.871     .868   .075000   .208000   .3723000      .000      .000  
+81 7 3 44788.00 I   .068327  .004254   .206774  .000564  I  .3683480  .0001826  1.1434 0.1598  I    -1.212    1.982    -2.716     .868   .074000   .207000   .3709000      .000      .000  
+81 7 4 44789.00 I   .067935  .004254   .205857  .000564  I  .3671843  .0001826  1.1909 0.1291  I    -1.024    1.982    -2.743     .868   .074000   .206000   .3694000      .000      .000  
+81 7 5 44790.00 I   .067552  .004254   .204942  .000564  I  .3659587  .0001826  1.2610 0.1121  I    -1.184    1.982    -2.885     .868   .073000   .205000   .3680000      .000      .000  
+81 7 6 44791.00 I   .067176  .002161   .204031  .000663  I  .3646653  .0001301  1.3222 0.1121  I    -1.778    1.982    -2.932     .868   .073000   .204000   .3666000      .000      .000  
+81 7 7 44792.00 I   .066808  .002161   .203123  .000663  I  .3633237  .0001301  1.3551 0.1057  I    -2.583    1.982    -2.810     .868   .072000   .204000   .3651000      .000      .000  
+81 7 8 44793.00 I   .066442  .001782   .202222  .000960  I  .3619672  .0001666  1.3515 0.1138  I    -3.251    1.982    -2.653     .868   .072000   .203000   .3637000      .000      .000  
+81 7 9 44794.00 I   .066073  .000926   .201328  .001032  I  .3606327  .0001868  1.3119 0.1251  I    -3.671    1.982    -2.597     .868   .071000   .202000   .3623000      .000      .000  
+81 710 44795.00 I   .065696  .000926   .200444  .001032  I  .3593538  .0001868  1.2413 0.1321  I    -4.022    1.982    -2.603     .868   .070000   .201000   .3609000      .000      .000  
+81 711 44796.00 I   .065302  .000926   .199571  .001032  I  .3581575  .0001868  1.1486 0.1223  I    -4.472    1.982    -2.541     .868   .069000   .200000   .3596000      .000      .000  
+81 712 44797.00 I   .064885  .000683   .198710  .001549  I  .3570599  .0001580  1.0460 0.1223  I    -4.978    1.982    -2.366     .868   .069000   .200000   .3582000      .000      .000  
+81 713 44798.00 I   .064440  .000683   .197866  .001549  I  .3560642  .0001580  0.9474 0.1063  I    -5.431    1.982    -2.175     .868   .068000   .199000   .3569000      .000      .000  
+81 714 44799.00 I   .063959  .002177   .197041  .002598  I  .3551590  .0001421  0.8673 0.1063  I    -5.825    1.982    -2.097     .868   .067000   .198000   .3555000      .000      .000  
+81 715 44800.00 I   .063435  .002177   .196238  .002598  I  .3543187  .0001421  0.8201 0.1005  I    -6.181    1.982    -2.168     .868   .066000   .197000   .3542000      .000      .000  
+81 716 44801.00 I   .062865  .002177   .195458  .002598  I  .3535035  .0001421  0.8190 0.1015  I    -6.448    1.982    -2.309     .868   .065000   .196000   .3529000      .000      .000  
+81 717 44802.00 I   .062243  .002177   .194704  .002598  I  .3526621  .0001451  0.8738 0.1851  I    -6.597    1.982    -2.393     .868   .064000   .196000   .3515000      .000      .000  
+81 718 44803.00 I   .061567  .002092   .193973  .002319  I  .3517366  .0003418  0.9866 0.1857  I    -6.749    1.982    -2.353     .868   .063000   .195000   .3502000      .000      .000  
+81 719 44804.00 I   .060837  .002092   .193262  .002319  I  .3506726  .0003418  1.1482 0.2633  I    -7.057    1.982    -2.232     .868   .062000   .194000   .3489000      .000      .000  
+81 720 44805.00 I   .060054  .000154   .192568  .000416  I  .3494308  .0004005  1.3380 0.2766  I    -7.495    1.982    -2.129     .868   .061000   .193000   .3476000      .000      .000  
+81 721 44806.00 I   .059219  .000603   .191888  .001887  I  .3479970  .0004350  1.5267 0.2956  I    -7.868    1.982    -2.086     .868   .060000   .193000   .3462000      .000      .000  
+81 722 44807.00 I   .058334  .000603   .191218  .001887  I  .3463884  .0004350  1.6823 0.3324  I    -8.062    1.982    -2.051     .868   .059000   .192000   .3449000      .000      .000  
+81 723 44808.00 I   .057398  .000603   .190553  .001887  I  .3446521  .0005028  1.7786 0.2763  I    -8.198    1.982    -1.963     .868   .058000   .192000   .3435000      .000      .000  
+81 724 44809.00 I   .056412  .000604   .189891  .003506  I  .3428561  .0003407  1.8002 0.3037  I    -8.523    1.982    -1.839     .868   .057000   .191000   .3422000      .000      .000  
+81 725 44810.00 I   .055375  .000604   .189230  .003506  I  .3410767  .0003407  1.7468 0.2409  I    -9.169    1.982    -1.774     .868   .056000   .190000   .3408000      .000      .000  
+81 726 44811.00 I   .054287  .000604   .188569  .003506  I  .3393814  .0003407  1.6368 0.2073  I   -10.015    1.982    -1.854     .868   .055000   .190000   .3394000      .000      .000  
+81 727 44812.00 I   .053147  .002078   .187908  .003162  I  .3378102  .0002363  1.5054 0.2073  I   -10.769    1.982    -2.063     .868   .054000   .189000   .3380000      .000      .000  
+81 728 44813.00 I   .051952  .002078   .187248  .003162  I  .3363637  .0002363  1.3946 0.1603  I   -11.185    1.982    -2.267     .868   .053000   .189000   .3366000      .000      .000  
+81 729 44814.00 I   .050703  .001805   .186592  .003082  I  .3350022  .0002166  1.3403 0.1926  I   -11.222    1.982    -2.316     .868   .052000   .188000   .3352000      .000      .000  
+81 730 44815.00 I   .049399  .002208   .185946  .002330  I  .3336592  .0003043  1.3579 0.1619  I   -11.029    1.982    -2.180     .868   .051000   .187000   .3337000      .000      .000  
+81 731 44816.00 I   .048043  .002208   .185316  .002330  I  .3322658  .0002407  1.4373 0.1940  I   -10.802    1.982    -1.997     .868   .050000   .187000   .3323000      .000      .000  
+81 8 1 44817.00 I   .046639  .002208   .184711  .002330  I  .3307738  .0002407  1.5491 0.1586  I   -10.691    1.982    -1.930     .868   .049000   .186000   .3308000      .000      .000  
+81 8 2 44818.00 I   .045190  .000952   .184134  .002538  I  .3291684  .0002066  1.6585 0.1586  I   -10.809    1.982    -1.983     .868   .048000   .186000   .3294000      .000      .000  
+81 8 3 44819.00 I   .043700  .000952   .183590  .002538  I  .3274667  .0002066  1.7382 0.1508  I   -11.207    1.982    -2.018     .868   .047000   .185000   .3279000      .000      .000  
+81 8 4 44820.00 I   .042171  .000819   .183082  .002095  I  .3257067  .0002196  1.7739 0.1089  I   -11.829    1.982    -1.944     .868   .046000   .185000   .3264000      .000      .000  
+81 8 5 44821.00 I   .040604  .001400   .182613  .002322  I  .3239344  .0000688  1.7632 0.1108  I   -12.498    1.982    -1.835     .868   .045000   .184000   .3250000      .000      .000  
+81 8 6 44822.00 I   .039001  .001400   .182187  .002322  I  .3221940  .0000302  1.7113 0.0376  I   -13.030    1.982    -1.807     .868   .043000   .184000   .3235000      .000      .000  
+81 8 7 44823.00 I   .037364  .001400   .181810  .002322  I  .3205223  .0000302  1.6277 0.0158  I   -13.351    1.982    -1.847     .868   .042000   .183000   .3221000      .000      .000  
+81 8 8 44824.00 I   .035693  .001624   .181488  .005717  I  .3189449  .0000091  1.5251 0.0158  I   -13.520    1.982    -1.826     .868   .041000   .183000   .3206000      .000      .000  
+81 8 9 44825.00 I   .033992  .001624   .181224  .005717  I  .3174735  .0000091  1.4183 0.0064  I   -13.674    1.982    -1.670     .868   .040000   .183000   .3191000      .000      .000  
+81 810 44826.00 I   .032265  .001624   .181021  .005717  I  .3161045  .0000091  1.3229 0.1604  I   -13.941    1.982    -1.459     .868   .038000   .183000   .3176000      .000      .000  
+81 811 44827.00 I   .030516  .002006   .180881  .005495  I  .3148191  .0003207  1.2531 0.1604  I   -14.349    1.982    -1.356     .868   .037000   .182000   .3161000      .000      .000  
+81 812 44828.00 I   .028750  .002006   .180802  .005495  I  .3135857  .0003207  1.2209 0.2268  I   -14.780    1.982    -1.455     .868   .035000   .182000   .3146000      .000      .000  
+81 813 44829.00 I   .026974  .002006   .180777  .005495  I  .3123613  .0003207  1.2366 0.2561  I   -15.058    1.982    -1.692     .868   .034000   .182000   .3131000      .000      .000  
+81 814 44830.00 I   .025195  .002714   .180800  .002200  I  .3110942  .0003995  1.3073 0.2561  I   -15.112    1.982    -1.888     .868   .033000   .182000   .3116000      .000      .000  
+81 815 44831.00 I   .023416  .002714   .180862  .002200  I  .3097288  .0003995  1.4318 0.2825  I   -15.047    1.982    -1.880     .868   .031000   .182000   .3100000      .000      .000  
+81 816 44832.00 I   .021640  .002714   .180959  .002200  I  .3082181  .0003995  1.5938 0.2329  I   -15.044    1.982    -1.668     .868   .030000   .181000   .3085000      .000      .000  
+81 817 44833.00 I   .019869  .002573   .181085  .002192  I  .3065392  .0002396  1.7619 0.2329  I   -15.192    1.982    -1.420     .868   .028000   .181000   .3069000      .000      .000  
+81 818 44834.00 I   .018106  .002573   .181235  .002192  I  .3047050  .0002396  1.8978 0.1555  I   -15.445    1.982    -1.312     .868   .027000   .181000   .3054000      .000      .000  
+81 819 44835.00 I   .016361  .002595   .181406  .001884  I  .3027657  .0001984  1.9673 0.1222  I   -15.702    1.982    -1.367     .868   .025000   .181000   .3038000      .000      .000  
+81 820 44836.00 I   .014640  .002398   .181598  .001087  I  .3007986  .0000480  1.9521 0.1021  I   -15.925    1.982    -1.466     .868   .024000   .181000   .3022000      .000      .000  
+81 821 44837.00 I   .012948  .002398   .181808  .001087  I  .2988884  .0000480  1.8561 0.0339  I   -16.160    1.982    -1.501     .868   .022000   .181000   .3006000      .000      .000  
+81 822 44838.00 I   .011288  .002398   .182037  .001087  I  .2971055  .0000480  1.7030 0.0961  I   -16.474    1.982    -1.473     .868   .021000   .181000   .2990000      .000      .000  
+81 823 44839.00 I   .009661  .002387   .182284  .000767  I  .2954891  .0001861  1.5300 0.0961  I   -16.855    1.982    -1.456     .868   .019000   .181000   .2974000      .000      .000  
+81 824 44840.00 I   .008067  .002387   .182551  .000767  I  .2940387  .0001861  1.3774 0.1577  I   -17.191    1.982    -1.495     .868   .017000   .181000   .2958000      .000      .000  
+81 825 44841.00 I   .006502  .001489   .182837  .001621  I  .2927164  .0002546  1.2785 0.1577  I   -17.331    1.982    -1.561     .868   .016000   .181000   .2941000      .000      .000  
+81 826 44842.00 I   .004967  .001489   .183144  .001621  I  .2914576  .0002546  1.2518 0.1800  I   -17.205    1.982    -1.584     .868   .014000   .182000   .2925000      .000      .000  
+81 827 44843.00 I   .003459  .001489   .183472  .001621  I  .2901894  .0002546  1.2953 0.1800  I   -16.882    1.982    -1.519     .868   .013000   .182000   .2908000      .000      .000  
+81 828 44844.00 I   .001971  .001489   .183822  .001621  I  .2888507  .0002546  1.3877 0.1700  I   -16.534    1.982    -1.399     .868   .011000   .182000   .2892000      .000      .000  
+81 829 44845.00 I   .000498  .001452   .184192  .002333  I  .2874083  .0002253  1.4970 0.1700  I   -16.316    1.982    -1.290     .868   .009000   .182000   .2875000      .000      .000  
+81 830 44846.00 I  -.000964  .001452   .184580  .002333  I  .2858615  .0002253  1.5917 0.1378  I   -16.283    1.982    -1.215     .868   .008000   .182000   .2858000      .000      .000  
+81 831 44847.00 I  -.002421  .002707   .184983  .002725  I  .2842370  .0001589  1.6500 0.1378  I   -16.412    1.982    -1.135     .868   .006000   .183000   .2841000      .000      .000  
+81 9 1 44848.00 I  -.003878  .002707   .185399  .002725  I  .2825765  .0001589  1.6634 0.1124  I   -16.670    1.982    -1.035     .868   .005000   .183000   .2824000      .000      .000  
+81 9 2 44849.00 I  -.005341  .002707   .185825  .002725  I  .2809244  .0001589  1.6343 0.1205  I   -17.002    1.982     -.980     .868   .003000   .183000   .2807000      .000      .000  
+81 9 3 44850.00 I  -.006820  .002371   .186262  .003254  I  .2793189  .0001813  1.5721 0.1179  I   -17.260    1.982    -1.033     .868   .001000   .183000   .2789000      .000      .000  
+81 9 4 44851.00 I  -.008321  .002515   .186711  .003605  I  .2777869  .0001742  1.4897 0.1257  I   -17.259    1.982    -1.147     .868   .000000   .184000   .2771000      .000      .000  
+81 9 5 44852.00 I  -.009847  .002515   .187177  .003605  I  .2763411  .0001742  1.4026 0.1400  I   -16.985    1.982    -1.195     .868  -.002000   .184000   .2754000      .000      .000  
+81 9 6 44853.00 I  -.011401  .001488   .187664  .004112  I  .2749774  .0002192  1.3284 0.1241  I   -16.700    1.982    -1.121     .868  -.003000   .185000   .2736000      .000      .000  
+81 9 7 44854.00 I  -.012986  .001062   .188175  .003021  I  .2736749  .0001768  1.2825 0.1408  I   -16.698    1.982    -1.009     .868  -.005000   .185000   .2718000      .000      .000  
+81 9 8 44855.00 I  -.014601  .001062   .188708  .003021  I  .2723988  .0001768  1.2773 0.1513  I   -16.980    1.982     -.988     .868  -.007000   .186000   .2699000      .000      .000  
+81 9 9 44856.00 I  -.016248  .000659   .189263  .000854  I  .2711036  .0002456  1.3222 0.1513  I   -17.252    1.982    -1.106     .868  -.008000   .186000   .2681000      .000      .000  
+81 910 44857.00 I  -.017925  .000659   .189838  .000854  I  .2697354  .0002456  1.4242 0.1737  I   -17.272    1.982    -1.306     .868  -.010000   .187000   .2662000      .000      .000  
+81 911 44858.00 I  -.019633  .000659   .190433  .000854  I  .2682354  .0002456  1.5857 0.1737  I   -17.063    1.982    -1.463     .868  -.011000   .187000   .2644000      .000      .000  
+81 912 44859.00 I  -.021374  .000659   .191049  .000854  I  .2665468  .0002456  1.7989 0.2203  I   -16.790    1.982    -1.446     .868  -.013000   .188000   .2625000      .000      .000  
+81 913 44860.00 I  -.023145  .000878   .191686  .000257  I  .2646284  .0003657  2.0397 0.2203  I   -16.550    1.982    -1.211     .868  -.015000   .189000   .2606000      .000      .000  
+81 914 44861.00 I  -.024947  .000878   .192346  .000257  I  .2624718  .0003657  2.2674 0.2742  I   -16.371    1.982     -.880     .868  -.016000   .190000   .2586000      .000      .000  
+81 915 44862.00 I  -.026777  .000666   .193028  .000960  I  .2601132  .0004086  2.4362 0.2742  I   -16.300    1.982     -.683     .868  -.018000   .191000   .2567000      .000      .000  
+81 916 44863.00 I  -.028635  .000666   .193735  .000960  I  .2576309  .0004086  2.5104 0.2889  I   -16.378    1.982     -.758     .868  -.019000   .192000   .2547000      .000      .000  
+81 917 44864.00 I  -.030519  .000666   .194466  .000960  I  .2551270  .0004086  2.4807 0.2889  I   -16.544    1.982    -1.015     .868  -.021000   .193000   .2528000      .000      .000  
+81 918 44865.00 I  -.032426  .000666   .195223  .000960  I  .2526969  .0004086  2.3689 0.2687  I   -16.666    1.982    -1.233     .868  -.023000   .194000   .2508000      .000      .000  
+81 919 44866.00 I  -.034352  .000382   .196006  .001011  I  .2504021  .0003491  2.2184 0.2687  I   -16.666    1.982    -1.274     .868  -.024000   .195000   .2488000      .000      .000  
+81 920 44867.00 I  -.036297  .000382   .196814  .001011  I  .2482567  .0003491  2.0782 0.2196  I   -16.562    1.982    -1.181     .868  -.026000   .197000   .2467000      .000      .000  
+81 921 44868.00 I  -.038258  .000344   .197645  .000477  I  .2462289  .0002666  1.9884 0.1986  I   -16.403    1.982    -1.081     .868  -.027000   .198000   .2447000      .000      .000  
+81 922 44869.00 I  -.040233  .000507   .198500  .000453  I  .2442564  .0001896  1.9692 0.1636  I   -16.184    1.982    -1.045     .868  -.029000   .199000   .2427000      .000      .000  
+81 923 44870.00 I  -.042219  .000507   .199377  .000453  I  .2422674  .0001896  2.0194 0.1417  I   -15.851    1.982    -1.061     .868  -.031000   .200000   .2406000      .000      .000  
+81 924 44871.00 I  -.044215  .001548   .200276  .000552  I  .2402008  .0002107  2.1202 0.1294  I   -15.377    1.982    -1.086     .868  -.033000   .201000   .2385000      .000      .000  
+81 925 44872.00 I  -.046217  .001880   .201196  .000586  I  .2380198  .0001761  2.2428 0.1373  I   -14.834    1.982    -1.092     .868  -.034000   .203000   .2365000      .000      .000  
+81 926 44873.00 I  -.048222  .001880   .202137  .000586  I  .2357184  .0001761  2.3559 0.1874  I   -14.361    1.982    -1.057     .868  -.036000   .204000   .2344000      .000      .000  
+81 927 44874.00 I  -.050225  .001755   .203100  .000936  I  .2333195  .0003308  2.4345 0.2206  I   -14.040    1.982     -.956     .868  -.038000   .205000   .2323000      .000      .000  
+81 928 44875.00 I  -.052222  .002103   .204084  .001106  I  .2308657  .0004046  2.4643 0.2613  I   -13.835    1.982     -.787     .868  -.040000   .206000   .2301000      .000      .000  
+81 929 44876.00 I  -.054203  .002103   .205092  .001106  I  .2284081  .0004046  2.4426 0.3443  I   -13.666    1.982     -.622     .868  -.042000   .207000   .2279000      .000      .000  
+81 930 44877.00 I  -.056160  .001213   .206123  .001181  I  .2259958  .0005573  2.3752 0.3443  I   -13.482    1.982     -.587     .868  -.043000   .209000   .2258000      .000      .000  
+8110 1 44878.00 I  -.058084  .001213   .207180  .001181  I  .2236689  .0005573  2.2744 0.3941  I   -13.205    1.982     -.736     .868  -.045000   .210000   .2236000      .000      .000  
+8110 2 44879.00 I  -.059970  .001213   .208266  .001181  I  .2214526  .0005573  2.1568 0.4079  I   -12.702    1.982     -.969     .868  -.047000   .211000   .2214000      .000      .000  
+8110 3 44880.00 I  -.061812  .000881   .209382  .000922  I  .2193548  .0005958  2.0406 0.3575  I   -11.976    1.982    -1.126     .868  -.049000   .212000   .2192000      .000      .000  
+8110 4 44881.00 I  -.063603  .001432   .210532  .000926  I  .2173644  .0004478  1.9453 0.3727  I   -11.306    1.982    -1.161     .868  -.050000   .213000   .2169000      .000      .000  
+8110 5 44882.00 I  -.065341  .001432   .211718  .000926  I  .2154516  .0004478  1.8876 0.2483  I   -11.003    1.982    -1.168     .868  -.052000   .215000   .2147000      .000      .000  
+8110 6 44883.00 I  -.067020  .001824   .212941  .000929  I  .2135733  .0002146  1.8775 0.2397  I   -10.996    1.982    -1.230     .868  -.053000   .216000   .2124000      .000      .000  
+8110 7 44884.00 I  -.068636  .002727   .214202  .001927  I  .2116786  .0001712  1.9214 0.1373  I   -10.879    1.982    -1.314     .868  -.055000   .217000   .2102000      .000      .000  
+8110 8 44885.00 I  -.070186  .002727   .215504  .001927  I  .2097112  .0001712  2.0232 0.1211  I   -10.405    1.982    -1.349     .868  -.056000   .218000   .2079000      .000      .000  
+8110 9 44886.00 I  -.071670  .002727   .216846  .001927  I  .2076132  .0001712  2.1819 0.1727  I    -9.746    1.982    -1.327     .868  -.058000   .219000   .2056000      .000      .000  
+811010 44887.00 I  -.073087  .002406   .218228  .001834  I  .2053325  .0002999  2.3853 0.1727  I    -9.179    1.982    -1.255     .868  -.059000   .221000   .2032000      .000      .000  
+811011 44888.00 I  -.074442  .002406   .219649  .001834  I  .2028375  .0002999  2.6041 0.2121  I    -8.722    1.982    -1.096     .868  -.061000   .222000   .2009000      .000      .000  
+811012 44889.00 I  -.075740  .002406   .221105  .001834  I  .2001341  .0002999  2.7934 0.2243  I    -8.238    1.982     -.850     .868  -.062000   .223000   .1986000      .000      .000  
+811013 44890.00 I  -.076987  .000538   .222593  .000475  I  .1972769  .0003337  2.9041 0.2243  I    -7.731    1.982     -.667     .868  -.063000   .224000   .1962000      .000      .000  
+811014 44891.00 I  -.078189  .000538   .224110  .000475  I  .1943629  .0003337  2.9040 0.2203  I    -7.332    1.982     -.740     .868  -.064000   .226000   .1939000      .000      .000  
+811015 44892.00 I  -.079350  .000836   .225653  .000388  I  .1915051  .0002878  2.7955 0.1949  I    -7.055    1.982    -1.057     .868  -.066000   .227000   .1915000      .000      .000  
+811016 44893.00 I  -.080477  .001017   .227218  .000378  I  .1887956  .0002015  2.6161 0.1757  I    -6.750    1.982    -1.370     .868  -.067000   .229000   .1892000      .000      .000  
+811017 44894.00 I  -.081573  .001017   .228801  .000378  I  .1862774  .0002015  2.4233 0.1335  I    -6.302    1.982    -1.455     .868  -.068000   .230000   .1868000      .000      .000  
+811018 44895.00 I  -.082642  .000922   .230398  .000536  I  .1839350  .0001752  2.2724 0.1214  I    -5.734    1.982    -1.343     .868  -.069000   .231000   .1844000      .000      .000  
+811019 44896.00 I  -.083687  .001000   .232006  .000536  I  .1817077  .0001355  2.1963 0.1107  I    -5.134    1.982    -1.234     .868  -.070000   .233000   .1820000      .000      .000  
+811020 44897.00 I  -.084709  .001000   .233618  .000536  I  .1795157  .0001355  2.2002 0.0843  I    -4.548     .110    -1.249     .868  -.072000   .234000   .1797000      .000      .000  
+811021 44898.00 I  -.085710  .000797   .235233  .001396  I  .1772865  .0001003  2.2664 0.0843  I    -3.957     .110    -1.347     .868  -.073000   .236000   .1773000      .000      .000  
+811022 44899.00 I  -.086693  .000797   .236845  .001396  I  .1749720  .0001003  2.3653 0.0709  I    -3.296     .110    -1.445     .868  -.074000   .237000   .1749000      .000      .000  
+811023 44900.00 I  -.087659  .000797   .238452  .001396  I  .1725556  .0001003  2.4652 0.0694  I    -2.525     .110    -1.525     .868  -.075000   .239000   .1725000      .000      .000  
+811024 44901.00 I  -.088611  .000888   .240050  .001823  I  .1700496  .0000960  2.5411 0.0608  I    -1.709     .110    -1.585     .868  -.076000   .240000   .1700000      .000      .000  
+811025 44902.00 I  -.089553  .003430   .241637  .002271  I  .1674868  .0000687  2.5767 0.0590  I     -.980     .110    -1.570     .868  -.078000   .242000   .1676000      .000      .000  
+811026 44903.00 I  -.090490  .003430   .243214  .002271  I  .1649119  .0000687  2.5651 0.0352  I     -.394    1.982    -1.436     .868  -.079000   .243000   .1651000      .000      .000  
+811027 44904.00 I  -.091427  .004769   .244778  .002643  I  .1623716  .0000150  2.5083 0.1127  I      .130    1.982    -1.258     .868  -.080000   .245000   .1627000      .000      .000  
+811028 44905.00 I  -.092366  .003412   .246330  .001888  I  .1599077  .0002146  2.4142 0.1076  I      .702    1.982    -1.208     .868  -.081000   .247000   .1603000      .000      .000  
+811029 44906.00 I  -.093305  .003412   .247871  .001888  I  .1575515  .0002146  2.2953 0.1517  I     1.390    1.982    -1.377     .868  -.082000   .248000   .1579000      .000      .000  
+811030 44907.00 I  -.094240  .003412   .249406  .001888  I  .1553197  .0002146  2.1685 0.1857  I     2.224    1.982    -1.659     .868  -.083000   .250000   .1554000      .000      .000  
+811031 44908.00 I  -.095166  .000738   .250935  .000375  I  .1532109  .0003031  2.0525 0.1887  I     3.143    1.982    -1.868     .868  -.084000   .251000   .1530000      .000      .000  
+8111 1 44909.00 I  -.096079  .001179   .252462  .001699  I  .1512050  .0003104  1.9656 0.2169  I     3.925    1.982    -1.946     .868  -.085000   .253000   .1506000      .000      .000  
+8111 2 44910.00 I  -.096977  .001179   .253991  .001699  I  .1492653  .0003104  1.9218 0.1993  I     4.339    1.982    -1.982     .868  -.086000   .255000   .1482000      .000      .000  
+8111 3 44911.00 I  -.097855  .001057   .255525  .002302  I  .1473445  .0002502  1.9286 0.1993  I     4.467    1.982    -2.035     .868  -.087000   .257000   .1459000      .000      .000  
+8111 4 44912.00 I  -.098712  .001057   .257066  .002302  I  .1453902  .0002502  1.9890 0.1630  I     4.675    1.982    -2.028     .868  -.087000   .258000   .1435000      .000      .000  
+8111 5 44913.00 I  -.099541  .001668   .258616  .001938  I  .1433488  .0002090  2.1024 0.1630  I     5.169    1.982    -1.878     .868  -.088000   .260000   .1412000      .000      .000  
+8111 6 44914.00 I  -.100337  .001668   .260174  .001938  I  .1411696  .0002090  2.2632 0.1212  I     5.739    1.982    -1.652     .868  -.089000   .262000   .1388000      .000      .000  
+8111 7 44915.00 I  -.101093  .001748   .261743  .001679  I  .1388120  .0001228  2.4553 0.1477  I     6.083    1.982    -1.478     .868  -.090000   .264000   .1365000      .000      .000  
+8111 8 44916.00 I  -.101804  .002179   .263326  .001374  I  .1362590  .0002089  2.6473 0.1307  I     6.200    1.982    -1.350     .868  -.090000   .266000   .1342000      .000      .000  
+8111 9 44917.00 I  -.102468  .002669   .264926  .000590  I  .1335327  .0002308  2.7935 0.1557  I     6.278    1.982    -1.160     .868  -.091000   .268000   .1319000      .000      .000  
+811110 44918.00 I  -.103083  .002669   .266547  .000590  I  .1307020  .0002308  2.8498 0.2266  I     6.344    1.982     -.924     .868  -.091000   .270000   .1296000      .000      .000  
+811111 44919.00 I  -.103648  .002515   .268194  .003197  I  .1278706  .0003900  2.7937 0.2266  I     6.262    1.982     -.814     .868  -.092000   .272000   .1273000      .000      .000  
+811112 44920.00 I  -.104163  .002515   .269872  .003197  I  .1251477  .0003900  2.6384 0.2758  I     6.013    1.982     -.901     .868  -.093000   .274000   .1250000      .000      .000  
+811113 44921.00 I  -.104631  .002515   .271582  .003197  I  .1226108  .0003900  2.4323 0.2982  I     5.748    1.982     -.992     .868  -.093000   .276000   .1228000      .000      .000  
+811114 44922.00 I  -.105057  .002127   .273331  .004518  I  .1202790  .0004512  2.2386 0.2637  I     5.557    1.982     -.852     .868  -.094000   .278000   .1205000      .000      .000  
+811115 44923.00 I  -.105442  .002864   .275117  .003202  I  .1181126  .0003550  2.1081 0.2871  I     5.364    1.982     -.511     .868  -.094000   .280000   .1183000      .000      .000  
+811116 44924.00 I  -.105788  .002864   .276940  .003202  I  .1160345  .0003550  2.0626 0.1789  I     5.046    1.982     -.211     .868  -.095000   .282000   .1160000      .000      .000  
+811117 44925.00 I  -.106093  .000562   .278798  .000100  I  .1139627  .0000454  2.0913 0.1789  I     4.531     .905     -.080     .113  -.096000   .284000   .1138000      .000      .000  
+811118 44926.00 I  -.106359  .000562   .280687  .000100  I  .1118375  .0000454  2.1631 0.0321  I     3.789     .921      .007     .102  -.096000   .286000   .1115000      .000      .000  
+811119 44927.00 I  -.106590  .000562   .282607  .000100  I  .1096341  .0000454  2.2421 0.0321  I     2.864     .921      .220     .102  -.097000   .288000   .1093000      .000      .000  
+811120 44928.00 I  -.106792  .000562   .284556  .000100  I  .1073603  .0000454  2.3002 0.0262  I     1.890     .921      .546     .102  -.097000   .290000   .1070000      .000      .000  
+811121 44929.00 I  -.106971  .000678   .286533  .000095  I  .1050456  .0000261  2.3226 0.0262  I     1.018     .921      .857     .102  -.098000   .292000   .1048000      .000      .000  
+811122 44930.00 I  -.107133  .000678   .288535  .000095  I  .1027286  .0000261  2.3046 0.0161  I      .179     .921     1.130     .102  -.099000   .294000   .1026000      .000      .000  
+811123 44931.00 I  -.107280  .000579   .290560  .000056  I  .1004497  .0000189  2.2469 0.0590  I     -.792     .936     1.452     .868  -.099000   .296000   .1004000      .000      .000  
+811124 44932.00 I  -.107416  .004625   .292608  .000273  I  .0982459  .0001151  2.1560 0.0583  I    -1.873    1.982     1.837     .868  -.100000   .299000   .0982000      .000      .000  
+811125 44933.00 I  -.107540  .004625   .294677  .000273  I  .0961454  .0001151  2.0422 0.0814  I    -2.879    1.982     2.155     .868  -.100000   .301000   .0960000      .000      .000  
+811126 44934.00 I  -.107648  .004625   .296764  .000273  I  .0941645  .0001151  1.9195 0.1472  I    -3.679    1.982     2.291     .868  -.101000   .303000   .0938000      .000      .000  
+811127 44935.00 I  -.107733  .003310   .298869  .002690  I  .0923040  .0002710  1.8042 0.0628  I    -4.285    1.982     2.297     .868  -.101000   .305000   .0916000      .000      .000  
+811128 44936.00 I  -.107791  .004135   .300991  .002312  I  .0905479  .0000501  1.7136 0.1378  I    -4.795    1.982     2.328     .868  -.102000   .307000   .0894000      .000      .000  
+811129 44937.00 I  -.107815  .004135   .303127  .002312  I  .0888633  .0000501  1.6635 0.0354  I    -5.338    1.982     2.460     .868  -.102000   .310000   .0872000      .000      .000  
+811130 44938.00 I  -.107800  .004135   .305278  .002312  I  .0872040  .0000501  1.6643 0.0354  I    -6.041    1.982     2.629     .868  -.103000   .312000   .0850000      .000      .000  
+8112 1 44939.00 I  -.107740  .004135   .307440  .002312  I  .0855165  .0000501  1.7199 0.0385  I    -6.891    1.982     2.779     .868  -.103000   .314000   .0828000      .000      .000  
+8112 2 44940.00 I  -.107632  .007051   .309614  .002234  I  .0837467  .0000586  1.8279 0.0385  I    -7.700    1.982     2.968     .868  -.103000   .314000   .0806000      .000      .000  
+8112 3 44941.00 I  -.107476  .007051   .311800  .002234  I  .0818456  .0000586  1.9811 0.0441  I    -8.322    1.982     3.266     .868  -.103000   .314000   .0785000      .000      .000  
+8112 4 44942.00 I  -.107274  .009073   .314001  .002153  I  .0797734  .0000660  2.1676 0.0441  I    -8.835    1.982     3.608     .868  -.104000   .314000   .0763000      .000      .000  
+8112 5 44943.00 I  -.107030  .009073   .316218  .002153  I  .0775061  .0000660  2.3669 0.3207  I    -9.399    1.982     3.858     .868  -.104000   .314000   .0742000      .000      .000  
+8112 6 44944.00 I  -.106757  .009266   .318450  .001575  I  .0750458  .0006380  2.5477 0.3207  I   -10.018    1.982     4.003     .868  -.104000   .314000   .0720000      .000      .000  
+8112 7 44945.00 I  -.106473  .009266   .320697  .001575  I  .0724298  .0006380  2.6712 0.5516  I   -10.612    1.982     4.174     .868  -.104000   .318000   .0699000      .000      .000  
+8112 8 44946.00 I  -.106199  .009455   .322958  .000569  I  .0697333  .0008999  2.7048 0.4686  I   -11.231    1.982     4.430     .868  -.104000   .323000   .0677000      .000      .000  
+8112 9 44947.00 I  -.105953  .009107   .325231  .001187  I  .0670535  .0006864  2.6384 0.5659  I   -11.999    1.982     4.641     .868  -.104000   .327000   .0656000      .000      .000  
+811210 44948.00 I  -.105744  .009107   .327514  .001187  I  .0644823  .0006864  2.4945 0.4854  I   -12.839    1.982     4.690     .868  -.104000   .332000   .0634000      .000      .000  
+811211 44949.00 I  -.105574  .009107   .329807  .001187  I  .0620725  .0006864  2.3265 0.3667  I   -13.484    1.982     4.686     .868  -.104000   .336000   .0613000      .000      .000  
+811212 44950.00 I  -.105442  .006184   .332106  .001657  I  .0598174  .0002584  2.1942 0.3667  I   -13.767    1.982     4.833     .868  -.104000   .338000   .0592000      .000      .000  
+811213 44951.00 I  -.105335  .006184   .334407  .001657  I  .0576587  .0002584  2.1380 0.1827  I   -13.762    1.982     5.121     .868  -.104000   .340000   .0570000      .000      .000  
+811214 44952.00 I  -.105240  .006184   .336708  .001657  I  .0555139  .0002584  2.1644 0.1349  I   -13.655    1.982     5.314     .868  -.103000   .342000   .0549000      .000      .000  
+811215 44953.00 I  -.105141  .001281   .339004  .001285  I  .0533108  .0000774  2.2482 0.1672  I   -13.612    1.982     5.283     .868  -.103000   .344000   .0527000      .000      .000  
+811216 44954.00 I  -.105022  .001281   .341291  .001285  I  .0510120  .0002121  2.3487 0.1577  I   -13.767    1.982     5.201     .868  -.103000   .346000   .0506000      .000      .000  
+811217 44955.00 I  -.104869  .002934   .343568  .001323  I  .0486201  .0003057  2.4292 0.2072  I   -14.153    1.982     5.309     .868  -.102000   .348000   .0485000      .000      .000  
+811218 44956.00 I  -.104667  .003592   .345832  .001062  I  .0461677  .0003560  2.4674 0.2346  I   -14.622    1.982     5.604     .868  -.102000   .350000   .0463000      .000      .000  
+811219 44957.00 I  -.104404  .003592   .348081  .001062  I  .0437014  .0003560  2.4571 0.2517  I   -14.989    1.982     5.872     .868  -.101000   .352000   .0442000      .000      .000  
+811220 44958.00 I  -.104069  .003592   .350313  .001062  I  .0412683  .0003560  2.4022 0.2540  I   -15.283    1.982     5.981     .868  -.101000   .354000   .0420000      .000      .000  
+811221 44959.00 I  -.103653  .003435   .352526  .001021  I  .0389087  .0003623  2.3120 0.2510  I   -15.681    1.982     6.003     .868  -.100000   .356000   .0399000      .000      .000  
+811222 44960.00 I  -.103155  .003435   .354720  .001021  I  .0366522  .0003540  2.1980 0.1965  I   -16.206    1.982     6.050     .868  -.099000   .358000   .0378000      .000      .000  
+811223 44961.00 I  -.102571  .001027   .356893  .000364  I  .0345166  .0001521  2.0724 0.1926  I   -16.649    1.982     6.108     .868  -.099000   .360000   .0357000      .000      .000  
+811224 44962.00 I  -.101899  .001027   .359045  .000364  I  .0325064  .0001521  1.9500 0.1076  I   -16.839    1.982     6.083     .868  -.098000   .362000   .0335000      .000      .000  
+811225 44963.00 I  -.101139  .001027   .361177  .000364  I  .0306103  .0001521  1.8466 0.1648  I   -16.827    1.982     5.958     .868  -.098000   .364000   .0314000      .000      .000  
+811226 44964.00 I  -.100290  .002534   .363287  .000530  I  .0288017  .0002923  1.7776 0.2067  I   -16.746    1.982     5.823     .868  -.097000   .366000   .0293000      .000      .000  
+811227 44965.00 I  -.099351  .003433   .365377  .000655  I  .0270397  .0003844  1.7548 0.2415  I   -16.654    1.982     5.756     .868  -.096000   .368000   .0271000      .000      .000  
+811228 44966.00 I  -.098323  .003433   .367444  .000655  I  .0252743  .0003844  1.7852 0.2461  I   -16.581    1.982     5.752     .868  -.095000   .370000   .0249000      .000      .000  
+811229 44967.00 I  -.097204  .006823   .369491  .001586  I  .0234518  .0003073  1.8679 0.2461  I   -16.589    1.982     5.769     .868  -.094000   .372000   .0228000      .000      .000  
+811230 44968.00 I  -.095993  .006823   .371516  .001586  I  .0215243  .0003073  1.9931 0.2173  I   -16.706    1.982     5.798     .868  -.093000   .374000   .0206000      .000      .000  
+811231 44969.00 I  -.094684  .006823   .373520  .001586  I  .0194563  .0003073  2.1460 0.1841  I   -16.873    1.982     5.852     .868  -.092000   .376000   .0184000      .000      .000  
+82 1 1 44970.00 I  -.093270  .009017   .375504  .002146  I  .0172296  .0002029  2.3070 0.2160  I   -17.013    1.982     5.904     .868  -.091000   .378000   .0163000      .000      .000  
+82 1 2 44971.00 I  -.091756  .006377   .377467  .001876  I  .0148474  .0003035  2.4529 0.1825  I   -17.095    1.982     5.903     .868  -.090000   .380000   .0141000      .000      .000  
+82 1 3 44972.00 I  -.090151  .006377   .379406  .001876  I  .0123377  .0003035  2.5572 0.2425  I   -17.120    1.982     5.863     .868  -.089000   .382000   .0120000      .000      .000  
+82 1 4 44973.00 I  -.088467  .000096   .381320  .001559  I  .0097557  .0003782  2.5936 0.2120  I   -17.145    1.982     5.867     .868  -.088000   .384000   .0098000      .000      .000  
+82 1 5 44974.00 I  -.086713  .000784   .383207  .001120  I  .0071781  .0002961  2.5473 0.2424  I   -17.319    1.982     5.947     .868  -.087000   .386000   .0077000      .000      .000  
+82 1 6 44975.00 I  -.084900  .001117   .385064  .000992  I  .0046864  .0003034  2.4250 0.2120  I   -17.752    1.982     6.005     .868  -.086000   .388000   .0052000      .000      .000  
+82 1 7 44976.00 I  -.083037  .001117   .386892  .000992  I  .0023421  .0003034  2.2605 0.2069  I   -18.316    1.982     5.936     .868  -.085000   .390000   .0028000      .000      .000  
+82 1 8 44977.00 I  -.081135  .001366   .388691  .000510  I  .0001620  .0002814  2.1060 0.2069  I   -18.684    1.982     5.800     .868  -.084000   .391000   .0003000      .000      .000  
+82 1 9 44978.00 I  -.079203  .001366   .390460  .000510  I -.0018896  .0002814  2.0104 0.1886  I   -18.614    1.982     5.759     .868  -.083000   .393000  -.0021000      .000      .000  
+82 110 44979.00 I  -.077248  .001907   .392198  .000759  I -.0038869  .0002513  1.9990 0.1937  I   -18.119    1.982     5.834     .868  -.082000   .395000  -.0046000      .000      .000  
+82 111 44980.00 I  -.075276  .002201   .393905  .000908  I -.0059126  .0002664  2.0627 0.1257  I   -17.380    1.982     5.843     .868  -.081000   .396000  -.0071000      .000      .000  
+82 112 44981.00 I  -.073292  .002678   .395579  .001099  I -.0080250  .0000029  2.1646 0.1500  I   -16.626    1.982     5.660     .868  -.080000   .398000  -.0096000      .000      .000  
+82 113 44982.00 I  -.071301  .002587   .397219  .000823  I -.0102397  .0001380  2.2602 0.0690  I   -16.081    1.982     5.423     .868  -.078000   .399000  -.0121000      .000      .000  
+82 114 44983.00 I  -.069308  .002587   .398823  .000823  I -.0125318  .0001380  2.3151 0.0976  I   -15.882    1.982     5.383     .868  -.077000   .401000  -.0146000      .000      .000  
+82 115 44984.00 I  -.067313  .002587   .400393  .000823  I -.0148514  .0001380  2.3145 0.0993  I   -15.953    1.982     5.587     .868  -.076000   .402000  -.0171000      .000      .000  
+82 116 44985.00 I  -.065316  .001850   .401928  .000494  I -.0171431  .0001428  2.2607 0.0993  I   -16.090    1.982     5.816     .868  -.074000   .403000  -.0192000      .000      .000  
+82 117 44986.00 I  -.063321  .001850   .403428  .000494  I -.0193587  .0001428  2.1648 0.1010  I   -16.205    1.982     5.859     .868  -.073000   .404000  -.0213000      .000      .000  
+82 118 44987.00 I  -.061331  .001850   .404895  .000494  I -.0214634  .0001428  2.0412 0.0760  I   -16.350    1.982     5.725     .868  -.071000   .406000  -.0235000      .000      .000  
+82 119 44988.00 I  -.059352  .000794   .406328  .000583  I -.0234369  .0000519  1.9050 0.0760  I   -16.501    1.982     5.560     .868  -.070000   .407000  -.0256000      .000      .000  
+82 120 44989.00 I  -.057388  .000794   .407730  .000583  I -.0252742  .0000519  1.7712 0.2480  I   -16.468    1.982     5.440     .868  -.068000   .408000  -.0277000      .000      .000  
+82 121 44990.00 I  -.055443  .000794   .409100  .000583  I -.0269851  .0004932  1.6547 0.3741  I   -16.135    1.982     5.317     .868  -.066000   .409000  -.0289000      .000      .000  
+82 122 44991.00 I  -.053521  .003310   .410441  .002690  I -.0285942  .0007463  1.5698 0.3496  I   -15.627    1.982     5.127     .868  -.064000   .410000  -.0301000      .000      .000  
+82 123 44992.00 I  -.051625  .007194   .411754  .002014  I -.0301393  .0004955  1.5285 0.4479  I   -15.135    1.982     4.888     .868  -.062000   .412000  -.0313000      .000      .000  
+82 124 44993.00 I  -.049757  .007194   .413040  .002014  I -.0316687  .0004955  1.5397 0.3504  I   -14.689    1.982     4.679     .868  -.060000   .413000  -.0325000      .000      .000  
+82 125 44994.00 I  -.047923  .007194   .414302  .002014  I -.0332374  .0004955  1.6070 0.3174  I   -14.220    1.982     4.568     .868  -.058000   .414000  -.0337000      .000      .000  
+82 126 44995.00 I  -.046124  .005135   .415539  .001426  I -.0348993  .0003967  1.7243 0.2499  I   -13.760    1.982     4.555     .868  -.056000   .415000  -.0357000      .000      .000  
+82 127 44996.00 I  -.044360  .005135   .416754  .001426  I -.0366974  .0000647  1.8759 0.2010  I   -13.430    1.982     4.572     .868  -.054000   .416000  -.0378000      .000      .000  
+82 128 44997.00 I  -.042622  .005135   .417944  .001426  I -.0386546  .0000647  2.0379 0.0381  I   -13.265    1.982     4.534     .868  -.051000   .417000  -.0398000      .000      .000  
+82 129 44998.00 I  -.040900  .000989   .419110  .000107  I -.0407674  .0000403  2.1826 0.0625  I   -13.161    1.982     4.403     .868  -.049000   .418000  -.0419000      .000      .000  
+82 130 44999.00 I  -.039185  .001420   .420248  .000744  I -.0430055  .0001070  2.2844 0.0572  I   -13.006    1.982     4.219     .868  -.047000   .419000  -.0439000      .000      .000  
+82 131 45000.00 I  -.037469  .001420   .421359  .000744  I -.0453154  .0001070  2.3239 0.0905  I   -12.789    1.982     4.062     .868  -.045000   .420000  -.0462000      .000      .000  
+82 2 1 45001.00 I  -.035743  .001747   .422440  .001046  I -.0476297  .0001459  2.2928 0.0905  I   -12.590    1.982     3.982     .868  -.043000   .421000  -.0485000      .000      .000  
+82 2 2 45002.00 I  -.033998  .001747   .423491  .001046  I -.0498798  .0001459  2.1981 0.1033  I   -12.514    1.982     3.955     .868  -.040000   .422000  -.0507000      .000      .000  
+82 2 3 45003.00 I  -.032228  .001382   .424510  .000741  I -.0520130  .0001462  2.0647 0.1033  I   -12.588    1.982     3.892     .868  -.038000   .423000  -.0530000      .000      .000  
+82 2 4 45004.00 I  -.030426  .001382   .425498  .000741  I -.0540096  .0001462  1.9327 0.1035  I   -12.694    1.982     3.728     .868  -.036000   .424000  -.0553000      .000      .000  
+82 2 5 45005.00 I  -.028587  .000876   .426453  .000053  I -.0558938  .0001466  1.8470 0.0954  I   -12.625    1.982     3.501     .868  -.034000   .425000  -.0574000      .000      .000  
+82 2 6 45006.00 I  -.026708  .000876   .427377  .000053  I -.0577298  .0001226  1.8398 0.0893  I   -12.250    1.982     3.322     .868  -.032000   .426000  -.0596000      .000      .000  
+82 2 7 45007.00 I  -.024782  .001713   .428269  .001486  I -.0596014  .0001019  1.9165 0.0797  I   -11.600    1.982     3.225     .868  -.029000   .426000  -.0617000      .000      .000  
+82 2 8 45008.00 I  -.022809  .001713   .429130  .001486  I -.0615825  .0001019  2.0524 0.0602  I   -10.801    1.982     3.104     .868  -.027000   .427000  -.0639000      .000      .000  
+82 2 9 45009.00 I  -.020783  .002259   .429960  .002101  I -.0637115  .0000640  2.2043 0.0617  I    -9.992    1.982     2.850     .868  -.025000   .428000  -.0660000      .000      .000  
+82 210 45010.00 I  -.018701  .001677   .430758  .001618  I -.0659818  .0000697  2.3285 0.0715  I    -9.307    1.982     2.530     .868  -.023000   .429000  -.0686000      .000      .000  
+82 211 45011.00 I  -.016564  .001677   .431527  .001618  I -.0683497  .0001279  2.3965 0.0760  I    -8.860    1.982     2.346     .868  -.021000   .430000  -.0711000      .000      .000  
+82 212 45012.00 I  -.014371  .001677   .432269  .001618  I -.0707537  .0001351  2.4012 0.0928  I    -8.680    1.982     2.391     .868  -.018000   .430000  -.0737000      .000      .000  
+82 213 45013.00 I  -.012124  .001763   .432987  .001385  I -.0731338  .0001344  2.3511 0.0953  I    -8.688    1.982     2.530     .868  -.016000   .431000  -.0762000      .000      .000  
+82 214 45014.00 I  -.009825  .001763   .433684  .001385  I -.0754433  .0001344  2.2631 0.0975  I    -8.782    1.982     2.559     .868  -.014000   .432000  -.0788000      .000      .000  
+82 215 45015.00 I  -.007476  .001459   .434361  .001395  I -.0776534  .0001414  2.1552 0.1019  I    -8.864    1.982     2.423     .868  -.012000   .433000  -.0809000      .000      .000  
+82 216 45016.00 I  -.005079  .001712   .435019  .001584  I -.0797529  .0001532  2.0451 0.0929  I    -8.799    1.982     2.228     .868  -.010000   .433000  -.0830000      .000      .000  
+82 217 45017.00 I  -.002636  .001712   .435659  .001584  I -.0817487  .0001204  1.9505 0.1114  I    -8.435    1.982     2.072     .868  -.007000   .434000  -.0852000      .000      .000  
+82 218 45018.00 I  -.000144  .001712   .436282  .001584  I -.0836642  .0001617  1.8869 0.2000  I    -7.754    1.982     1.943     .868  -.005000   .434000  -.0873000      .000      .000  
+82 219 45019.00 I   .002399  .000413   .436885  .001416  I -.0855371  .0003814  1.8670 0.1932  I    -6.945    1.982     1.769     .868  -.003000   .435000  -.0894000      .000      .000  
+82 220 45020.00 I   .004994  .004493   .437468  .001830  I -.0874157  .0003509  1.8994 0.2713  I    -6.239    1.982     1.526     .868  -.001000   .435000  -.0915000      .000      .000  
+82 221 45021.00 I   .007642  .006341   .438026  .002166  I -.0893546  .0003858  1.9877 0.2608  I    -5.696    1.982     1.275     .868   .001000   .436000  -.0935000      .000      .000  
+82 222 45022.00 I   .010346  .006341   .438557  .002166  I -.0914086  .0003858  2.1285 0.2728  I    -5.220    1.982     1.121     .868   .004000   .436000  -.0956000      .000      .000  
+82 223 45023.00 I   .013106  .006341   .439060  .002166  I -.0936249  .0003858  2.3091 0.3136  I    -4.739    1.982     1.122     .868   .006000   .437000  -.0976000      .000      .000  
+82 224 45024.00 I   .015915  .004499   .439529  .001870  I -.0960319  .0004946  2.5050 0.2589  I    -4.281    1.982     1.212     .868   .008000   .437000  -.0997000      .000      .000  
+82 225 45025.00 I   .018763  .004499   .439959  .001870  I -.0986291  .0003454  2.6834 0.4592  I    -3.909    1.982     1.240     .868   .010000   .437000  -.1023000      .000      .000  
+82 226 45026.00 I   .021638  .000528   .440346  .001519  I -.1013812  .0007739  2.8095 0.4237  I    -3.639    1.982     1.098     .868   .012000   .438000  -.1049000      .000      .000  
+82 227 45027.00 I   .024531  .000528   .440682  .001519  I -.1042217  .0007739  2.8566 0.4982  I    -3.454    1.982      .826     .868   .015000   .438000  -.1075000      .000      .000  
+82 228 45028.00 I   .027431  .000722   .440966  .003324  I -.1070648  .0006275  2.8148 0.4982  I    -3.341    1.982      .558     .868   .017000   .439000  -.1101000      .000      .000  
+82 3 1 45029.00 I   .030329  .000722   .441192  .003324  I -.1098255  .0006275  2.6957 0.4394  I    -3.284    1.982      .397     .868   .019000   .439000  -.1127000      .000      .000  
+82 3 2 45030.00 I   .033215  .003015   .441358  .003167  I -.1124409  .0006153  2.5307 0.4498  I    -3.235    1.982      .332     .868   .021000   .439000  -.1149000      .000      .000  
+82 3 3 45031.00 I   .036080  .003015   .441461  .003167  I -.1148853  .0006447  2.3610 0.3782  I    -3.108    1.982      .282     .868   .023000   .439000  -.1170000      .000      .000  
+82 3 4 45032.00 I   .038915  .003015   .441502  .003167  I -.1171751  .0004401  2.2280 0.3903  I    -2.811    1.982      .183     .868   .026000   .440000  -.1192000      .000      .000  
+82 3 5 45033.00 I   .041713  .003015   .441483  .003167  I -.1193640  .0004401  2.1632 0.3100  I    -2.293    1.982      .031     .868   .028000   .440000  -.1213000      .000      .000  
+82 3 6 45034.00 I   .044468  .003593   .441406  .000566  I -.1215290  .0004368  2.1807 0.3100  I    -1.605    1.982     -.129     .868   .030000   .440000  -.1235000      .000      .000  
+82 3 7 45035.00 I   .047179  .003593   .441276  .000566  I -.1237490  .0004368  2.2688 0.2990  I     -.894    1.982     -.266     .868   .024000   .446000  -.1259000      .000      .000  
+82 3 8 45036.00 I   .049845  .002240   .441095  .000834  I -.1260787  .0004083  2.3933 0.2509  I     -.304    1.982     -.412     .868   .026000   .445000  -.1282000      .000      .000  
+82 3 9 45037.00 I   .052468  .002240   .440866  .000834  I -.1285333  .0002470  2.5110 0.2386  I      .118    1.982     -.618     .868   .028000   .445000  -.1306000      .000      .000  
+82 310 45038.00 I   .055050  .002240   .440593  .000834  I -.1310860  .0002470  2.5847 0.1761  I      .433    1.982     -.858     .868   .030000   .445000  -.1329000      .000      .000  
+82 311 45039.00 I   .057596  .003482   .440277  .000681  I -.1336814  .0002511  2.5947 0.1894  I      .698    1.982    -1.018     .868   .042000   .440000  -.1353000      .000      .000  
+82 312 45040.00 I   .060110  .003740   .439922  .000713  I -.1362541  .0002873  2.5409 0.1908  I      .888    1.982    -1.015     .868   .045000   .440000  -.1376000      .000      .000  
+82 313 45041.00 I   .062597  .003740   .439531  .000713  I -.1387466  .0002873  2.4373 0.1935  I      .922    1.982     -.910     .868   .047000   .440000  -.1399000      .000      .000  
+82 314 45042.00 I   .065059  .005133   .439108  .000033  I -.1411186  .0002593  2.3035 0.1479  I      .794    1.982     -.848     .868   .050000   .439000  -.1423000      .000      .000  
+82 315 45043.00 I   .067495  .001797   .438656  .000634  I -.1433509  .0000701  2.1616 0.1343  I      .644    1.982     -.899     .868   .052000   .439000  -.1446000      .000      .000  
+82 316 45044.00 I   .069900  .001797   .438180  .000634  I -.1454461  .0000701  2.0326 0.0432  I      .680    1.982    -1.011     .868   .055000   .439000  -.1469000      .000      .000  
+82 317 45045.00 I   .072266  .001105   .437682  .000749  I -.1474266  .0000505  1.9350 0.0432  I     1.020    1.982    -1.114     .868   .057000   .439000  -.1489000      .000      .000  
+82 318 45046.00 I   .074588  .001105   .437168  .000749  I -.1493315  .0000505  1.8834 0.0357  I     1.624    1.982    -1.198     .868   .060000   .438000  -.1509000      .000      .000  
+82 319 45047.00 I   .076865  .001105   .436637  .000749  I -.1512116  .0000505  1.8862 0.0357  I     2.346    1.982    -1.297     .868   .062000   .438000  -.1529000      .000      .000  
+82 320 45048.00 I   .079099  .001105   .436091  .000749  I -.1531232  .0000505  1.9467 0.0349  I     3.017    1.982    -1.425     .868   .065000   .437000  -.1549000      .000      .000  
+82 321 45049.00 I   .081299  .001979   .435531  .000561  I -.1551240  .0000482  2.0642 0.0664  I     3.496    1.982    -1.558     .868   .067000   .437000  -.1569000      .000      .000  
+82 322 45050.00 I   .083474  .004444   .434956  .001274  I -.1572687  .0001229  2.2330 0.0393  I     3.743    1.982    -1.648     .868   .069000   .437000  -.1595000      .000      .000  
+82 323 45051.00 I   .085634  .005848   .434365  .001265  I -.1596019  .0000622  2.4376 0.0706  I     3.857    1.982    -1.636     .868   .072000   .436000  -.1621000      .000      .000  
+82 324 45052.00 I   .087787  .005848   .433758  .001265  I -.1621463  .0000696  2.6494 0.0467  I     4.009    1.982    -1.511     .868   .074000   .436000  -.1646000      .000      .000  
+82 325 45053.00 I   .089939  .005848   .433136  .001265  I -.1648898  .0000696  2.8286 0.0676  I     4.250    1.982    -1.368     .868   .077000   .435000  -.1672000      .000      .000  
+82 326 45054.00 I   .092088  .004256   .432499  .001672  I -.1677794  .0001159  2.9357 0.1516  I     4.443    1.982    -1.361     .868   .079000   .435000  -.1698000      .000      .000  
+82 327 45055.00 I   .094233  .001421   .431849  .001998  I -.1707293  .0002952  2.9471 0.1586  I     4.410    1.982    -1.558     .868   .081000   .434000  -.1725000      .000      .000  
+82 328 45056.00 I   .096372  .001421   .431186  .001998  I -.1736428  .0002952  2.8660 0.2087  I     4.147    1.982    -1.856     .868   .084000   .434000  -.1752000      .000      .000  
+82 329 45057.00 I   .098502  .001421   .430514  .001998  I -.1764405  .0002952  2.7226 0.2320  I     3.827    1.982    -2.077     .868   .086000   .433000  -.1778000      .000      .000  
+82 330 45058.00 I   .100624  .001344   .429830  .001622  I -.1790828  .0003580  2.5635 0.2320  I     3.644    1.982    -2.124     .868   .089000   .433000  -.1805000      .000      .000  
+82 331 45059.00 I   .102737  .001344   .429134  .001622  I -.1815781  .0003580  2.4362 0.2770  I     3.680    1.982    -2.037     .868   .091000   .432000  -.1832000      .000      .000  
+82 4 1 45060.00 I   .104842  .001262   .428425  .001125  I -.1839775  .0004228  2.3759 0.2059  I     3.930    1.982    -1.926     .868   .093000   .431000  -.1857000      .000      .000  
+82 4 2 45061.00 I   .106941  .001262   .427702  .001125  I -.1863567  .0002037  2.3962 0.2347  I     4.362    1.982    -1.876     .868   .096000   .430000  -.1881000      .000      .000  
+82 4 3 45062.00 I   .109031  .001262   .426961  .001125  I -.1887936  .0002037  2.4877 0.1629  I     4.912    1.982    -1.902     .868   .098000   .430000  -.1906000      .000      .000  
+82 4 4 45063.00 I   .111112  .002211   .426199  .001229  I -.1913466  .0002542  2.6227 0.1798  I     5.442    1.982    -1.976     .868   .101000   .429000  -.1930000      .000      .000  
+82 4 5 45064.00 I   .113181  .002860   .425415  .001326  I -.1940409  .0002963  2.7635 0.2270  I     5.772    1.982    -2.060     .868   .103000   .428000  -.1955000      .000      .000  
+82 4 6 45065.00 I   .115237  .002860   .424605  .001326  I -.1968630  .0003762  2.8727 0.2394  I     5.807    1.982    -2.127     .868   .105000   .427000  -.1981000      .000      .000  
+82 4 7 45066.00 I   .117279  .002860   .423766  .001326  I -.1997669  .0003762  2.9239 0.2660  I     5.636    1.982    -2.146     .868   .107000   .426000  -.2009000      .000      .000  
+82 4 8 45067.00 I   .119306  .002860   .422896  .001326  I -.2026883  .0003762  2.9076 0.2660  I     5.453    1.982    -2.084     .868   .110000   .426000  -.2038000      .000      .000  
+82 4 9 45068.00 I   .121322  .002860   .421994  .001326  I -.2055617  .0003762  2.8300 0.2249  I     5.351    1.982    -1.949     .868   .112000   .425000  -.2066000      .000      .000  
+82 410 45069.00 I   .123331  .003310   .421059  .002690  I -.2083333  .0002466  2.7075 0.2435  I     5.237    1.982    -1.814     .868   .114000   .424000  -.2093000      .000      .000  
+82 411 45070.00 I   .125337  .001441   .420090  .000690  I -.2109687  .0003094  2.5611 0.1777  I     5.011    1.982    -1.762     .868   .116000   .423000  -.2118000      .000      .000  
+82 412 45071.00 I   .127343  .001441   .419085  .000690  I -.2134548  .0002560  2.4125 0.2008  I     4.740    1.982    -1.807     .868   .118000   .422000  -.2143000      .000      .000  
+82 413 45072.00 I   .129354  .001441   .418044  .000690  I -.2158001  .0002560  2.2830 0.1821  I     4.590    1.982    -1.895     .868   .121000   .422000  -.2165000      .000      .000  
+82 414 45073.00 I   .131372  .001624   .416966  .000642  I -.2180335  .0002590  2.1916 0.1821  I     4.623    1.982    -1.987     .868   .123000   .421000  -.2187000      .000      .000  
+82 415 45074.00 I   .133400  .001624   .415850  .000642  I -.2202005  .0002590  2.1517 0.2226  I     4.795    1.982    -2.084     .868   .125000   .420000  -.2209000      .000      .000  
+82 416 45075.00 I   .135437  .001624   .414696  .000642  I -.2223560  .0003620  2.1689 0.2027  I     5.101    1.982    -2.166     .868   .127000   .419000  -.2230000      .000      .000  
+82 417 45076.00 I   .137486  .001267   .413503  .000742  I -.2245576  .0003120  2.2438 0.2389  I     5.541    1.982    -2.188     .868   .130000   .418000  -.2252000      .000      .000  
+82 418 45077.00 I   .139544  .001267   .412274  .000742  I -.2268617  .0003120  2.3729 0.2206  I     5.936    1.982    -2.151     .868   .132000   .416000  -.2274000      .000      .000  
+82 419 45078.00 I   .141609  .001267   .411007  .000742  I -.2293184  .0003120  2.5471 0.2740  I     5.998    1.982    -2.129     .868   .135000   .415000  -.2299000      .000      .000  
+82 420 45079.00 I   .143679  .000337   .409704  .000824  I -.2319647  .0004505  2.7479 0.2740  I     5.685    1.982    -2.155     .868   .137000   .414000  -.2325000      .000      .000  
+82 421 45080.00 I   .145750  .000337   .408365  .000824  I -.2348120  .0004505  2.9422 0.3166  I     5.308    1.982    -2.147     .868   .139000   .413000  -.2353000      .000      .000  
+82 422 45081.00 I   .147821  .000656   .406989  .001036  I -.2378327  .0004450  3.0872 0.3311  I     5.144    1.982    -2.031     .868   .141000   .411000  -.2383000      .000      .000  
+82 423 45082.00 I   .149889  .000799   .405577  .001111  I -.2409566  .0004853  3.1428 0.3292  I     5.065    1.982    -1.898     .868   .143000   .410000  -.2415000      .000      .000  
+82 424 45083.00 I   .151952  .000799   .404127  .001111  I -.2440824  .0004853  3.0906 0.3432  I     4.745    1.982    -1.932     .868   .145000   .408000  -.2446000      .000      .000  
+82 425 45084.00 I   .154008  .000799   .402641  .001111  I -.2471068  .0004853  2.9455 0.3016  I     4.135    1.982    -2.176     .868   .147000   .407000  -.2476000      .000      .000  
+82 426 45085.00 I   .156054  .000794   .401118  .000971  I -.2499578  .0003581  2.7536 0.3016  I     3.551    1.982    -2.449     .868   .149000   .406000  -.2504000      .000      .000  
+82 427 45086.00 I   .158087  .000794   .399556  .000971  I -.2526180  .0003581  2.5738 0.2015  I     3.295    1.982    -2.541     .868   .151000   .404000  -.2531000      .000      .000  
+82 428 45087.00 I   .160102  .001001   .397954  .000177  I -.2551261  .0001847  2.4560 0.2015  I     3.354    1.982    -2.423     .868   .153000   .403000  -.2556000      .000      .000  
+82 429 45088.00 I   .162096  .001001   .396311  .000177  I -.2575582  .0001847  2.4229 0.1306  I     3.508    1.982    -2.239     .868   .155000   .401000  -.2580000      .000      .000  
+82 430 45089.00 I   .164065  .001001   .394625  .000177  I -.2599978  .0001847  2.4676 0.1306  I     3.610    1.982    -2.129     .868   .157000   .400000  -.2604000      .000      .000  
+82 5 1 45090.00 I   .166004  .001001   .392896  .000177  I -.2625100  .0001847  2.5621 0.1125  I     3.683    1.982    -2.123     .868   .159000   .398000  -.2628000      .000      .000  
+82 5 2 45091.00 I   .167910  .001034   .391120  .001044  I -.2651262  .0001284  2.6692 0.1125  I     3.795    1.982    -2.180     .868   .161000   .397000  -.2653000      .000      .000  
+82 5 3 45092.00 I   .169780  .001034   .389297  .001044  I -.2678408  .0001284  2.7536 0.1763  I     3.901    1.982    -2.240     .868   .163000   .395000  -.2680000      .000      .000  
+82 5 4 45093.00 I   .171613  .000431   .387425  .002773  I -.2706173  .0003284  2.7896 0.1463  I     3.845    1.982    -2.244     .868   .165000   .394000  -.2706000      .000      .000  
+82 5 5 45094.00 I   .173407  .000431   .385501  .002773  I -.2733995  .0002630  2.7641 0.2104  I     3.538    1.982    -2.138     .868   .167000   .392000  -.2733000      .000      .000  
+82 5 6 45095.00 I   .175159  .000431   .383526  .002773  I -.2761253  .0002630  2.6779 0.1860  I     3.079    1.982    -1.920     .868   .169000   .390000  -.2759000      .000      .000  
+82 5 7 45096.00 I   .176870  .000431   .381499  .002773  I -.2787385  .0002630  2.5412 0.2724  I     2.657    1.982    -1.688     .868   .171000   .388000  -.2784000      .000      .000  
+82 5 8 45097.00 I   .178538  .003207   .379421  .003546  I -.2811970  .0004772  2.3723 0.2724  I     2.348    1.982    -1.583     .868   .173000   .387000  -.2807000      .000      .000  
+82 5 9 45098.00 I   .180164  .003207   .377297  .003546  I -.2834795  .0004772  2.1926 0.3236  I     2.102    1.982    -1.664     .868   .175000   .385000  -.2829000      .000      .000  
+82 510 45099.00 I   .181745  .003659   .375131  .002548  I -.2855857  .0004371  2.0233 0.3914  I     1.894    1.982    -1.848     .868   .177000   .383000  -.2849000      .000      .000  
+82 511 45100.00 I   .183283  .003659   .372928  .002548  I -.2875363  .0006205  1.8845 0.3795  I     1.747    1.982    -2.008     .868   .179000   .381000  -.2868000      .000      .000  
+82 512 45101.00 I   .184779  .003659   .370692  .002548  I -.2893706  .0006205  1.7935 0.4108  I     1.624    1.982    -2.115     .868   .181000   .379000  -.2885000      .000      .000  
+82 513 45102.00 I   .186242  .003044   .368423  .002281  I -.2911426  .0005386  1.7605 0.3681  I     1.471    1.982    -2.224     .868   .183000   .377000  -.2901000      .000      .000  
+82 514 45103.00 I   .187678  .001907   .366121  .001359  I -.2929113  .0003962  1.7867 0.3343  I     1.397    1.982    -2.327     .868   .185000   .375000  -.2918000      .000      .000  
+82 515 45104.00 I   .189092  .001907   .363786  .001359  I -.2947347  .0003962  1.8690 0.3087  I     1.589    1.982    -2.320     .868   .187000   .373000  -.2936000      .000      .000  
+82 516 45105.00 I   .190487  .001007   .361416  .001618  I -.2966654  .0004735  1.9997 0.2556  I     1.948    1.982    -2.170     .868   .189000   .371000  -.2954000      .000      .000  
+82 517 45106.00 I   .191868  .001637   .359012  .002112  I -.2987462  .0003231  2.1666 0.2866  I     2.051    1.982    -2.026     .868   .191000   .369000  -.2975000      .000      .000  
+82 518 45107.00 I   .193238  .001637   .356574  .002112  I -.3010038  .0003231  2.3489 0.2458  I     1.647    1.982    -2.047     .868   .192000   .366000  -.2997000      .000      .000  
+82 519 45108.00 I   .194605  .001510   .354105  .002059  I -.3034383  .0003704  2.5139 0.2458  I     1.008    1.982    -2.166     .868   .194000   .364000  -.3021000      .000      .000  
+82 520 45109.00 I   .195973  .001510   .351604  .002059  I -.3060123  .0003704  2.6209 0.2760  I      .566    1.982    -2.165     .868   .196000   .362000  -.3046000      .000      .000  
+82 521 45110.00 I   .197343  .001510   .349077  .002059  I -.3086493  .0004092  2.6356 0.2691  I      .336    1.982    -1.992     .868   .198000   .360000  -.3072000      .000      .000  
+82 522 45111.00 I   .198718  .001510   .346526  .002059  I -.3112500  .0003905  2.5500 0.2762  I     -.016    1.982    -1.857     .868   .200000   .357000  -.3098000      .000      .000  
+82 523 45112.00 I   .200099  .000681   .343956  .001076  I -.3137251  .0003712  2.3920 0.2694  I     -.591    1.982    -1.947     .868   .201000   .355000  -.3121000      .000      .000  
+82 524 45113.00 I   .201486  .000681   .341371  .001076  I -.3160281  .0003712  2.2164 0.2682  I    -1.049    1.982    -2.173     .868   .203000   .352000  -.3144000      .000      .000  
+82 525 45114.00 I   .202880  .000845   .338771  .000374  I -.3181723  .0003871  2.0839 0.2613  I    -1.033    1.982    -2.294     .868   .205000   .350000  -.3164000      .000      .000  
+82 526 45115.00 I   .204280  .001049   .336160  .000299  I -.3202241  .0003679  2.0358 0.2453  I     -.595    1.982    -2.234     .868   .206000   .348000  -.3184000      .000      .000  
+82 527 45116.00 I   .205687  .001049   .333541  .000299  I -.3222740  .0003015  2.0779 0.2269  I     -.118    1.982    -2.129     .868   .208000   .345000  -.3204000      .000      .000  
+82 528 45117.00 I   .207099  .001679   .330914  .000328  I -.3244012  .0002656  2.1838 0.1875  I      .080    1.982    -2.104     .868   .209000   .343000  -.3225000      .000      .000  
+82 529 45118.00 I   .208516  .001968   .328282  .000302  I -.3266486  .0002231  2.3105 0.1734  I     -.009    1.982    -2.130     .868   .211000   .340000  -.3247000      .000      .000  
+82 530 45119.00 I   .209939  .001968   .325647  .000302  I -.3290153  .0002231  2.4164 0.1578  I     -.151    1.982    -2.127     .868   .212000   .338000  -.3270000      .000      .000  
+82 531 45120.00 I   .211368  .001968   .323011  .000302  I -.3314653  .0002231  2.4738 0.1644  I     -.128    1.982    -2.086     .868   .213000   .335000  -.3294000      .000      .000  
+82 6 1 45121.00 I   .212803  .002515   .320375  .000366  I -.3339430  .0002416  2.4714 0.1644  I      .030    1.982    -2.033     .868   .214000   .332000  -.3318000      .000      .000  
+82 6 2 45122.00 I   .214243  .002515   .317740  .000366  I -.3363887  .0002416  2.4107 0.2165  I      .080    1.982    -1.932     .868   .216000   .330000  -.3342000      .000      .000  
+82 6 3 45123.00 I   .215683  .002974   .315108  .002232  I -.3387481  .0003594  2.3012 0.2165  I     -.132    1.982    -1.739     .868   .217000   .327000  -.3365000      .000      .000  
+82 6 4 45124.00 I   .217119  .002974   .312481  .002232  I -.3409797  .0003594  2.1577 0.2541  I     -.484    1.982    -1.527     .868   .218000   .324000  -.3387000      .000      .000  
+82 6 5 45125.00 I   .218546  .002974   .309859  .002232  I -.3430580  .0003594  1.9977 0.2541  I     -.716    1.982    -1.464     .868   .219000   .321000  -.3408000      .000      .000  
+82 6 6 45126.00 I   .219954  .002974   .307245  .002232  I -.3449760  .0003594  1.8403 0.2442  I     -.705    1.982    -1.624     .868   .220000   .318000  -.3427000      .000      .000  
+82 6 7 45127.00 I   .221334  .002377   .304638  .002653  I -.3467460  .0003308  1.7049 0.2442  I     -.509    1.982    -1.877     .868   .221000   .315000  -.3445000      .000      .000  
+82 6 8 45128.00 I   .222672  .002377   .302037  .002653  I -.3483989  .0003308  1.6087 0.1872  I     -.251    1.982    -2.030     .868   .222000   .312000  -.3461000      .000      .000  
+82 6 9 45129.00 I   .223952  .000704   .299440  .001543  I -.3499803  .0001753  1.5633 0.1872  I     -.066    1.982    -2.039     .868   .223000   .309000  -.3477000      .000      .000  
+82 610 45130.00 I   .225160  .000704   .296844  .001543  I -.3515439  .0001753  1.5731 0.1240  I     -.030    1.982    -2.022     .868   .224000   .306000  -.3492000      .000      .000  
+82 611 45131.00 I   .226281  .000704   .294244  .001543  I -.3531437  .0001753  1.6346 0.1240  I     -.037    1.982    -2.051     .868   .225000   .303000  -.3507000      .000      .000  
+82 612 45132.00 I   .227300  .000704   .291637  .001543  I -.3548271  .0001753  1.7382 0.0903  I      .165    1.982    -2.031     .868   .225000   .300000  -.3523000      .000      .000  
+82 613 45133.00 I   .228203  .000898   .289017  .000653  I -.3566297  .0000434  1.8708 0.0993  I      .637    1.982    -1.869     .868   .226000   .297000  -.3541000      .000      .000  
+82 614 45134.00 I   .228977  .001650   .286377  .000446  I -.3585730  .0000934  2.0165 0.0520  I     1.066    1.982    -1.662     .868   .227000   .294000  -.3559000      .000      .000  
+82 615 45135.00 I   .229609  .001409   .283712  .000369  I -.3606600  .0000946  2.1541 0.0617  I     1.111     .329    -1.606     .211   .227000   .291000  -.3579000      .000      .000  
+82 616 45136.00 I   .230087  .000999   .281016  .000910  I -.3628686  .0000806  2.2541 0.0619  I      .844     .329    -1.717     .211   .228000   .288000  -.3600000      .000      .000  
+82 617 45137.00 I   .230396  .001374   .278281  .000807  I -.3651449  .0000799  2.2844 0.0530  I      .631     .217    -1.788     .868   .228000   .284000  -.3620000      .000      .000  
+82 618 45138.00 I   .230535  .001074   .275502  .000751  I -.3674097  .0000688  2.2317 0.0531  I      .622     .217    -1.683     .868   .229000   .281000  -.3642000      .000      .000  
+82 619 45139.00 I   .230552  .001067   .272663  .000766  I -.3695846  .0000699  2.1078 0.0490  I      .644     .548    -1.526     .235   .229000   .278000  -.3663000      .000      .000  
+82 620 45140.00 I   .230505  .001067   .269756  .000766  I -.3716098  .0000699  1.9373 0.0476  I      .590     .862    -1.512     .239   .229000   .275000  -.3682000      .000      .000  
+82 621 45141.00 I   .230433  .000930   .266803  .000796  I -.3734581  .0000646  1.7634 0.0473  I      .653     .892    -1.599     .240   .230000   .272000  -.3699000      .000      .000  
+82 622 45142.00 I   .230327  .000956   .263830  .000753  I -.3751547  .0000636  1.6437 0.0439  I     1.058     .892    -1.570     .240   .230000   .268000  -.3716000      .000      .000  
+82 623 45143.00 I   .230183  .001111   .260847  .000596  I -.3767764  .0000594  1.6167 0.0417  I     1.797    1.064    -1.400     .288   .231000   .265000  -.3732000      .000      .000  
+82 624 45144.00 I   .230002  .000615   .257860  .000830  I -.3784170  .0000541  1.6763 0.0454  I     2.571    1.064    -1.255     .288   .231000   .262000  -.3748000      .000      .000  
+82 625 45145.00 I   .229786  .000436   .254873  .000890  I -.3801438  .0000686  1.7802 0.0580  I     3.028    1.239    -1.219     .248   .231000   .259000  -.3766000      .000      .000  
+82 626 45146.00 I   .229539  .001290   .251890  .002389  I -.3819751  .0001025  1.8771 0.0617  I     3.035    1.982    -1.165     .868   .231000   .255000  -.3784000      .000      .000  
+82 627 45147.00 I   .229263  .001290   .248915  .002389  I -.3838827  .0001025  1.9280 0.0797  I     2.772    1.982     -.945     .868   .231000   .252000  -.3804000      .000      .000  
+82 628 45148.00 I   .228962  .001214   .245950  .002294  I -.3858102  .0001222  1.9160 0.0797  I     2.601    1.982     -.617     .868   .231000   .248000  -.3824000      .000      .000  
+82 629 45149.00 I   .228635  .001214   .242994  .002294  I -.3876947  .0001222  1.8438 0.0892  I     2.729    1.982     -.363     .868   .231000   .245000  -.3843000      .000      .000  
+82 630 45150.00 I   .228282  .000158   .240048  .000278  I -.3894825  .0001300  1.7257 0.0892  I     2.979    1.982     -.237     .868   .231000   .242000  -.3860000      .000      .000  
+82 7 1 45151.00 I   .227903  .000158   .237109  .000278  I  .6088635  .0001300  1.5789 0.0919  I     3.013    1.982     -.130     .868   .231000   .239000   .6123000      .000      .000  
+82 7 2 45152.00 I   .227500  .000158   .234178  .000278  I  .6073635  .0001300  1.4206 0.0919  I     2.769    1.982      .029     .868   .230000   .235000   .6110000      .000      .000  
+82 7 3 45153.00 I   .227071  .000158   .231255  .000278  I  .6060205  .0001300  1.2676 0.0845  I     2.524    1.982      .152     .868   .230000   .232000   .6097000      .000      .000  
+82 7 4 45154.00 I   .226617  .001194   .228339  .001166  I  .6048210  .0001081  1.1363 0.0845  I     2.531    1.982      .134     .868   .230000   .229000   .6085000      .000      .000  
+82 7 5 45155.00 I   .226138  .001194   .225430  .001166  I  .6037356  .0001081  1.0419 0.0795  I     2.763    1.982      .036     .868   .230000   .226000   .6074000      .000      .000  
+82 7 6 45156.00 I   .225636  .001673   .222527  .001641  I  .6027209  .0001165  0.9966 0.0763  I     3.038    1.982      .032     .868   .229000   .223000   .6063000      .000      .000  
+82 7 7 45157.00 I   .225110  .001580   .219631  .001335  I  .6017239  .0001077  1.0069 0.0793  I     3.216    1.982      .196     .868   .229000   .220000   .6053000      .000      .000  
+82 7 8 45158.00 I   .224558  .001580   .216742  .001335  I  .6006889  .0001077  1.0717 0.0762  I     3.242    1.982      .421     .868   .228000   .217000   .6042000      .000      .000  
+82 7 9 45159.00 I   .223980  .001580   .213861  .001335  I  .5995655  .0001077  1.1814 0.0772  I     3.147    1.982      .577     .868   .228000   .214000   .6031000      .000      .000  
+82 710 45160.00 I   .223370  .001116   .210992  .000785  I  .5983163  .0001105  1.3204 0.0772  I     3.069    1.982      .674     .868   .227000   .211000   .6018000      .000      .000  
+82 711 45161.00 I   .222726  .001116   .208136  .000785  I  .5969212  .0001105  1.4699 0.0781  I     3.146    1.982      .810     .868   .227000   .208000   .6003000      .000      .000  
+82 712 45162.00 I   .222040  .001116   .205296  .000785  I  .5953796  .0001105  1.6101 0.0764  I     3.308    1.982     1.000     .868   .226000   .206000   .5988000      .000      .000  
+82 713 45163.00 I   .221308  .002457   .202475  .001012  I  .5937112  .0001055  1.7196 0.0764  I     3.322    1.982     1.143     .868   .226000   .203000   .5971000      .000      .000  
+82 714 45164.00 I   .220525  .002457   .199677  .001012  I  .5919581  .0001055  1.7759 0.1431  I     3.066    1.982     1.174     .868   .225000   .200000   .5953000      .000      .000  
+82 715 45165.00 I   .219686  .002178   .196902  .000868  I  .5901832  .0002660  1.7606 0.2385  I     2.652    1.982     1.165     .868   .224000   .197000   .5934000      .000      .000  
+82 716 45166.00 I   .218788  .002640   .194154  .000974  I  .5884624  .0004651  1.6687 0.2679  I     2.252    1.982     1.201     .868   .223000   .194000   .5917000      .000      .000  
+82 717 45167.00 I   .217832  .002640   .191432  .000974  I  .5868650  .0004651  1.5193 0.3289  I     1.939    1.982     1.243     .868   .223000   .192000   .5900000      .000      .000  
+82 718 45168.00 I   .216819  .002640   .188738  .000974  I  .5854280  .0004651  1.3573 0.3300  I     1.762    1.982     1.202     .868   .222000   .189000   .5885000      .000      .000  
+82 719 45169.00 I   .215754  .001276   .186071  .000411  I  .5841359  .0004682  1.2389 0.3300  I     1.809    1.982     1.124     .868   .221000   .186000   .5872000      .000      .000  
+82 720 45170.00 I   .214638  .001276   .183431  .000411  I  .5829209  .0004682  1.2083 0.3101  I     2.115    1.982     1.140     .868   .220000   .183000   .5859000      .000      .000  
+82 721 45171.00 I   .213479  .000775   .180819  .000252  I  .5816866  .0004068  1.2756 0.2523  I     2.576    1.982     1.251     .868   .219000   .181000   .5846000      .000      .000  
+82 722 45172.00 I   .212281  .000775   .178235  .000252  I  .5803469  .0001881  1.4112 0.2241  I     3.015    1.982     1.289     .868   .219000   .178000   .5833000      .000      .000  
+82 723 45173.00 I   .211046  .000775   .175677  .000252  I  .5788593  .0001881  1.5614 0.1330  I     3.258    1.982     1.167     .868   .218000   .176000   .5817000      .000      .000  
+82 724 45174.00 I   .209779  .000775   .173145  .000252  I  .5772372  .0001881  1.6723 0.1353  I     3.166    1.982     1.055     .868   .217000   .173000   .5801000      .000      .000  
+82 725 45175.00 I   .208482  .000244   .170639  .000799  I  .5755388  .0001944  1.7107 0.1353  I     2.752    1.982     1.168     .868   .216000   .170000   .5783000      .000      .000  
+82 726 45176.00 I   .207157  .000244   .168158  .000799  I  .5738419  .0001944  1.6706 0.1711  I     2.278    1.982     1.463     .868   .215000   .168000   .5765000      .000      .000  
+82 727 45177.00 I   .205807  .001130   .165701  .000804  I  .5722188  .0002815  1.5670 0.1711  I     2.060    1.982     1.665     .868   .213000   .165000   .5749000      .000      .000  
+82 728 45178.00 I   .204434  .001130   .163268  .000804  I  .5707209  .0002815  1.4244 0.1991  I     2.120    1.982     1.610     .868   .212000   .163000   .5733000      .000      .000  
+82 729 45179.00 I   .203041  .001130   .160858  .000804  I  .5693752  .0002815  1.2662 0.1991  I     2.177    1.982     1.421     .868   .211000   .160000   .5719000      .000      .000  
+82 730 45180.00 I   .201628  .001130   .158467  .000804  I  .5681868  .0002815  1.1129 0.2433  I     2.038    1.982     1.305     .868   .210000   .157000   .5706000      .000      .000  
+82 731 45181.00 I   .200198  .001716   .156095  .001069  I  .5671419  .0003969  0.9819 0.2433  I     1.831    1.982     1.304     .868   .209000   .155000   .5695000      .000      .000  
+82 8 1 45182.00 I   .198750  .001716   .153738  .001069  I  .5662108  .0003969  0.8876 0.2885  I     1.765    1.982     1.300     .868   .207000   .152000   .5685000      .000      .000  
+82 8 2 45183.00 I   .197283  .001830   .151396  .001507  I  .5653507  .0004187  0.8414 0.2538  I     1.839    1.982     1.224     .868   .206000   .150000   .5675000      .000      .000  
+82 8 3 45184.00 I   .195796  .001531   .149065  .001119  I  .5645097  .0003165  0.8501 0.2624  I     1.923    1.982     1.147     .868   .205000   .147000   .5666000      .000      .000  
+82 8 4 45185.00 I   .194287  .001531   .146746  .001119  I  .5636315  .0003165  0.9158 0.2238  I     1.976    1.982     1.174     .868   .203000   .145000   .5656000      .000      .000  
+82 8 5 45186.00 I   .192753  .001531   .144436  .001119  I  .5626608  .0003165  1.0333 0.1965  I     2.028    1.982     1.306     .868   .202000   .142000   .5646000      .000      .000  
+82 8 6 45187.00 I   .191188  .001198   .142138  .000404  I  .5615525  .0002331  1.1881 0.1965  I     2.047    1.982     1.448     .868   .200000   .140000   .5634000      .000      .000  
+82 8 7 45188.00 I   .189587  .001198   .139850  .000404  I  .5602790  .0002331  1.3598 0.2013  I     1.981    1.982     1.514     .868   .199000   .137000   .5620000      .000      .000  
+82 8 8 45189.00 I   .187942  .001332   .137574  .000538  I  .5588353  .0003283  1.5241 0.2255  I     1.883    1.982     1.505     .868   .197000   .135000   .5605000      .000      .000  
+82 8 9 45190.00 I   .186248  .001411   .135311  .000563  I  .5572413  .0003862  1.6567 0.2534  I     1.849    1.982     1.474     .868   .195000   .133000   .5589000      .000      .000  
+82 810 45191.00 I   .184499  .001411   .133061  .000563  I  .5555391  .0003862  1.7378 0.4669  I     1.848    1.982     1.461     .868   .193000   .130000   .5572000      .000      .000  
+82 811 45192.00 I   .182691  .001985   .130826  .000497  I  .5537867  .0008502  1.7559 0.5414  I     1.724    1.982     1.462     .868   .191000   .128000   .5556000      .000      .000  
+82 812 45193.00 I   .180820  .002268   .128606  .000570  I  .5520488  .0010115  1.7094 0.6574  I     1.381    1.982     1.448     .868   .189000   .125000   .5538000      .000      .000  
+82 813 45194.00 I   .178885  .002268   .126404  .000570  I  .5503848  .0010029  1.6122 0.7031  I      .904    1.982     1.384     .868   .187000   .123000   .5522000      .000      .000  
+82 814 45195.00 I   .176885  .001987   .124221  .000267  I  .5488306  .0009770  1.4965 0.7001  I      .507    1.982     1.249     .868   .185000   .121000   .5506000      .000      .000  
+82 815 45196.00 I   .174821  .001987   .122061  .000267  I  .5473828  .0009770  1.4078 0.6908  I      .384    1.982     1.071     .868   .182000   .119000   .5492000      .000      .000  
+82 816 45197.00 I   .172698  .001987   .119925  .000267  I  .5459912  .0009770  1.3905 0.5194  I      .570    1.982      .959     .868   .180000   .116000   .5479000      .000      .000  
+82 817 45198.00 I   .170519  .000566   .117816  .000723  I  .5445700  .0003531  1.4687 0.5194  I      .904    1.982     1.019     .868   .177000   .114000   .5464000      .000      .000  
+82 818 45199.00 I   .168290  .000566   .115737  .000723  I  .5430252  .0003531  1.6326 0.2296  I     1.170    1.982     1.205     .868   .175000   .112000   .5448000      .000      .000  
+82 819 45200.00 I   .166012  .000566   .113689  .000723  I  .5412908  .0002937  1.8386 0.2226  I     1.277    1.982     1.320     .868   .172000   .110000   .5430000      .000      .000  
+82 820 45201.00 I   .163690  .000762   .111677  .001006  I  .5393529  .0002710  2.0298 0.1754  I     1.264    1.982     1.234     .868   .169000   .108000   .5409000      .000      .000  
+82 821 45202.00 I   .161328  .001474   .109701  .000929  I  .5372528  .0001917  2.1563 0.1660  I     1.135    1.982     1.063     .868   .167000   .106000   .5386000      .000      .000  
+82 822 45203.00 I   .158931  .001474   .107765  .000929  I  .5350698  .0001917  2.1943 0.0959  I      .830    1.982     1.033     .868   .164000   .104000   .5363000      .000      .000  
+82 823 45204.00 I   .156504  .001940   .105868  .000846  I  .5328919  .0000039  2.1491 0.1284  I      .396    1.982     1.178     .868   .161000   .102000   .5340000      .000      .000  
+82 824 45205.00 I   .154050  .001642   .104013  .000741  I  .5307914  .0001708  2.0446 0.0854  I      .036    1.982     1.281     .868   .158000   .100000   .5318000      .000      .000  
+82 825 45206.00 I   .151574  .001642   .102200  .000741  I  .5288123  .0001708  1.9112 0.1152  I     -.097    1.982     1.151     .868   .155000   .098000   .5297000      .000      .000  
+82 826 45207.00 I   .149075  .001458   .100430  .000762  I  .5269696  .0001545  1.7758 0.1274  I     -.065    1.982      .861     .868   .153000   .097000   .5277000      .000      .000  
+82 827 45208.00 I   .146556  .001144   .098701  .000715  I  .5252541  .0001892  1.6601 0.1221  I     -.017    1.982      .643     .868   .150000   .095000   .5259000      .000      .000  
+82 828 45209.00 I   .144019  .001144   .097016  .000715  I  .5236373  .0001892  1.5808 0.1338  I      .000    1.982      .609     .868   .147000   .093000   .5243000      .000      .000  
+82 829 45210.00 I   .141467  .001144   .095372  .000715  I  .5220765  .0001892  1.5496 0.1378  I      .012    1.982      .649     .868   .144000   .091000   .5228000      .000      .000  
+82 830 45211.00 I   .138905  .001461   .093769  .000676  I  .5205198  .0002005  1.5733 0.1378  I     -.004    1.982      .607     .868   .141000   .090000   .5213000      .000      .000  
+82 831 45212.00 I   .136334  .001461   .092209  .000676  I  .5189112  .0002005  1.6532 0.2503  I     -.075    1.982      .462     .868   .139000   .088000   .5198000      .000      .000  
+82 9 1 45213.00 I   .133756  .002642   .090691  .000457  I  .5171961  .0004587  1.7851 0.2503  I     -.127    1.982      .337     .868   .136000   .087000   .5182000      .000      .000  
+82 9 2 45214.00 I   .131172  .002642   .089216  .000457  I  .5153275  .0004587  1.9577 0.3243  I     -.065    1.982      .355     .868   .133000   .085000   .5165000      .000      .000  
+82 9 3 45215.00 I   .128584  .002642   .087785  .000457  I  .5132734  .0004587  2.1523 0.3243  I      .056    1.982      .510     .868   .130000   .083000   .5145000      .000      .000  
+82 9 4 45216.00 I   .125988  .002642   .086396  .000457  I  .5110245  .0004587  2.3423 0.3803  I      .077    1.982      .668     .868   .127000   .082000   .5124000      .000      .000  
+82 9 5 45217.00 I   .123382  .004241   .085050  .000935  I  .5086007  .0006068  2.4967 0.3803  I     -.056    1.982      .683     .868   .124000   .080000   .5101000      .000      .000  
+82 9 6 45218.00 I   .120758  .004241   .083746  .000935  I  .5060521  .0006068  2.5881 0.4333  I     -.235    1.982      .538     .868   .121000   .079000   .5077000      .000      .000  
+82 9 7 45219.00 I   .118109  .005028   .082484  .001266  I  .5034506  .0006186  2.6014 0.4044  I     -.366    1.982      .350     .868   .118000   .077000   .5052000      .000      .000  
+82 9 8 45220.00 I   .115426  .006145   .081262  .001327  I  .5008748  .0005347  2.5384 0.4088  I     -.506    1.982      .246     .868   .115000   .076000   .5027000      .000      .000  
+82 9 9 45221.00 I   .112698  .006145   .080080  .001327  I  .4983934  .0005347  2.4166 0.3781  I     -.771    1.982      .237     .868   .112000   .074000   .5003000      .000      .000  
+82 910 45222.00 I   .109909  .006145   .078941  .001327  I  .4960501  .0005347  2.2691 0.3644  I    -1.150    1.982      .232     .868   .108000   .073000   .4980000      .000      .000  
+82 911 45223.00 I   .107038  .008720   .077844  .000983  I  .4938502  .0004952  2.1365 0.3644  I    -1.461    1.982      .159     .868   .105000   .071000   .4959000      .000      .000  
+82 912 45224.00 I   .104060  .008720   .076792  .000983  I  .4917594  .0004952  2.0570 0.3502  I    -1.508    1.982      .046     .868   .102000   .070000   .4939000      .000      .000  
+82 913 45225.00 I   .100941  .008720   .075789  .000983  I  .4897097  .0004952  2.0570 0.3808  I    -1.266    1.982     -.001     .868   .099000   .069000   .4919000      .000      .000  
+82 914 45226.00 I   .097653  .008554   .074836  .000519  I  .4876187  .0005787  2.1373 0.3808  I     -.933    1.982      .100     .868   .095000   .068000   .4899000      .000      .000  
+82 915 45227.00 I   .094171  .008554   .073937  .000519  I  .4854146  .0005787  2.2792 0.4202  I     -.758    1.982      .311     .868   .092000   .067000   .4877000      .000      .000  
+82 916 45228.00 I   .090519  .008190   .073093  .000848  I  .4830529  .0006094  2.4432 0.4135  I     -.827    1.982      .484     .868   .088000   .066000   .4854000      .000      .000  
+82 917 45229.00 I   .086735  .008106   .072309  .001035  I  .4805389  .0005909  2.5749 0.6037  I    -1.028    1.982      .493     .868   .085000   .065000   .4828000      .000      .000  
+82 918 45230.00 I   .082848  .005251   .071586  .001035  I  .4779277  .0010424  2.6327 0.5991  I    -1.222    1.982      .368     .868   .082000   .064000   .4802000      .000      .000  
+82 919 45231.00 I   .078886  .005251   .070926  .001035  I  .4753023  .0010424  2.6039 0.6181  I    -1.398    1.982      .256     .868   .078000   .064000   .4775000      .000      .000  
+82 920 45232.00 I   .074873  .004731   .070329  .001050  I  .4727448  .0006647  2.5002 0.6181  I    -1.624    1.982      .231     .868   .075000   .063000   .4750000      .000      .000  
+82 921 45233.00 I   .070826  .004731   .069797  .001050  I  .4703178  .0006647  2.3486 0.3553  I    -1.892    1.982      .198     .868   .071000   .063000   .4725000      .000      .000  
+82 922 45234.00 I   .066765  .003617   .069330  .000592  I  .4680526  .0002512  2.1819 0.3553  I    -2.087    1.982      .037     .868   .068000   .062000   .4702000      .000      .000  
+82 923 45235.00 I   .062704  .003617   .068926  .000592  I  .4659500  .0002512  2.0274 0.1737  I    -2.092    1.982     -.209     .868   .064000   .062000   .4680000      .000      .000  
+82 924 45236.00 I   .058656  .003617   .068587  .000592  I  .4639867  .0002401  1.9064 0.1737  I    -1.916    1.982     -.369     .868   .061000   .062000   .4660000      .000      .000  
+82 925 45237.00 I   .054630  .003617   .068310  .000592  I  .4621210  .0002401  1.8340 0.1633  I    -1.687    1.982     -.357     .868   .057000   .061000   .4640000      .000      .000  
+82 926 45238.00 I   .050631  .002748   .068095  .000548  I  .4603001  .0002213  1.8174 0.1633  I    -1.556    1.982     -.277     .868   .054000   .061000   .4622000      .000      .000  
+82 927 45239.00 I   .046665  .002748   .067940  .000548  I  .4584668  .0002213  1.8588 0.1423  I    -1.582    1.982     -.292     .868   .050000   .061000   .4602000      .000      .000  
+82 928 45240.00 I   .042735  .002986   .067845  .000701  I  .4565644  .0001789  1.9544 0.1335  I    -1.693    1.982     -.439     .868   .046000   .061000   .4583000      .000      .000  
+82 929 45241.00 I   .038844  .002146   .067806  .000721  I  .4545431  .0001493  2.0948 0.1047  I    -1.764    1.982     -.591     .868   .043000   .061000   .4562000      .000      .000  
+82 930 45242.00 I   .034995  .002146   .067825  .000721  I  .4523646  .0001088  2.2659 0.1299  I    -1.750    1.982     -.588     .868   .039000   .060000   .4539000      .000      .000  
+8210 1 45243.00 I   .031186  .002146   .067899  .000721  I  .4500084  .0002126  2.4459 0.1095  I    -1.725    1.982     -.385     .868   .036000   .060000   .4514000      .000      .000  
+8210 2 45244.00 I   .027414  .000673   .068028  .000997  I  .4474796  .0001901  2.6055 0.1426  I    -1.793    1.982     -.107     .868   .032000   .060000   .4488000      .000      .000  
+8210 3 45245.00 I   .023680  .000673   .068212  .000997  I  .4448154  .0001901  2.7110 0.1344  I    -1.973    1.982      .036     .868   .028000   .060000   .4461000      .000      .000  
+8210 4 45246.00 I   .019981  .000673   .068449  .000997  I  .4420846  .0001901  2.7356 0.1335  I    -2.195    1.982     -.075     .868   .025000   .060000   .4433000      .000      .000  
+8210 5 45247.00 I   .016317  .000606   .068737  .000859  I  .4393739  .0001875  2.6711 0.1335  I    -2.395    1.982     -.355     .868   .021000   .061000   .4406000      .000      .000  
+8210 6 45248.00 I   .012688  .000606   .069075  .000859  I  .4367670  .0001875  2.5329 0.1024  I    -2.569    1.982     -.591     .868   .018000   .061000   .4379000      .000      .000  
+8210 7 45249.00 I   .009093  .001205   .069459  .000726  I  .4343200  .0000822  2.3592 0.0939  I    -2.756    1.982     -.641     .868   .014000   .061000   .4354000      .000      .000  
+8210 8 45250.00 I   .005535  .001369   .069887  .000268  I  .4320443  .0000090  2.1985 0.0413  I    -2.958    1.982     -.535     .868   .010000   .062000   .4331000      .000      .000  
+8210 9 45251.00 I   .002014  .001369   .070356  .000268  I  .4299041  .0000090  2.0946 0.0064  I    -3.107    1.982     -.404     .868   .007000   .062000   .4310000      .000      .000  
+821010 45252.00 I  -.001469  .001369   .070862  .000268  I  .4278270  .0000090  2.0750 0.0510  I    -3.109    1.982     -.327     .868   .003000   .063000   .4288000      .000      .000  
+821011 45253.00 I  -.004914  .001869   .071405  .000349  I  .4257247  .0001017  2.1435 0.0510  I    -2.946    1.982     -.287     .868   .000000   .063000   .4267000      .000      .000  
+821012 45254.00 I  -.008321  .001869   .071980  .000349  I  .4235175  .0001017  2.2795 0.0878  I    -2.724    1.982     -.228     .868  -.004000   .064000   .4245000      .000      .000  
+821013 45255.00 I  -.011694  .002787   .072585  .000965  I  .4211563  .0001431  2.4439 0.0878  I    -2.612    1.982     -.132     .868  -.008000   .065000   .4221000      .000      .000  
+821014 45256.00 I  -.015035  .002787   .073219  .000965  I  .4186360  .0001431  2.5896 0.1012  I    -2.717    1.982     -.039     .868  -.011000   .066000   .4196000      .000      .000  
+821015 45257.00 I  -.018348  .002787   .073880  .000965  I  .4159972  .0001431  2.6749 0.0759  I    -2.995    1.982      .004     .868  -.015000   .067000   .4169000      .000      .000  
+821016 45258.00 I  -.021639  .000799   .074564  .000314  I  .4133142  .0000504  2.6761 0.0751  I    -3.294    1.982     -.010     .868  -.018000   .068000   .4142000      .000      .000  
+821017 45259.00 I  -.024914  .000893   .075271  .000788  I  .4106733  .0000454  2.5925 0.0339  I    -3.496     .841     -.038     .224  -.022000   .069000   .4115000      .000      .000  
+821018 45260.00 I  -.028185  .000893   .076001  .000788  I  .4081512  .0000454  2.4429 0.0299  I    -3.609     .841     -.063     .224  -.025000   .070000   .4089000      .000      .000  
+821019 45261.00 I  -.031465  .000657   .076753  .000749  I  .4057997  .0000390  2.2568 0.0299  I    -3.720     .841     -.132     .224  -.029000   .072000   .4065000      .000      .000  
+821020 45262.00 I  -.034762  .000697   .077527  .000798  I  .4036387  .0000388  2.0675 0.0275  I    -3.832     .841     -.279     .224  -.032000   .073000   .4042000      .000      .000  
+821021 45263.00 I  -.038074  .000697   .078327  .000798  I  .4016561  .0000388  1.9040 0.0274  I    -3.830     .841     -.435     .224  -.036000   .075000   .4022000      .000      .000  
+821022 45264.00 I  -.041396  .000697   .079157  .000798  I  .3998154  .0000388  1.7863 0.0709  I    -3.641     .841     -.470     .224  -.039000   .076000   .4002000      .000      .000  
+821023 45265.00 I  -.044721  .001303   .080019  .002444  I  .3980639  .0001364  1.7273 0.0709  I    -3.387    1.982     -.352     .868  -.042000   .078000   .3984000      .000      .000  
+821024 45266.00 I  -.048047  .001303   .080921  .002444  I  .3963395  .0001364  1.7322 0.0964  I    -3.281    1.982     -.199     .868  -.046000   .079000   .3965000      .000      .000  
+821025 45267.00 I  -.051372  .001303   .081867  .002444  I  .3945793  .0001364  1.7978 0.1180  I    -3.367    1.982     -.142     .868  -.049000   .081000   .3947000      .000      .000  
+821026 45268.00 I  -.054691  .001285   .082863  .002297  I  .3927267  .0001927  1.9150 0.1180  I    -3.488    1.982     -.180     .868  -.053000   .082000   .3927000      .000      .000  
+821027 45269.00 I  -.058002  .001285   .083912  .002297  I  .3907363  .0001927  2.0711 0.1277  I    -3.532    1.982     -.197     .868  -.056000   .084000   .3906000      .000      .000  
+821028 45270.00 I  -.061300  .001192   .085016  .002031  I  .3885770  .0001676  2.2498 0.1236  I    -3.589    1.982     -.089     .868  -.059000   .086000   .3884000      .000      .000  
+821029 45271.00 I  -.064579  .001231   .086176  .001103  I  .3862366  .0001548  2.4289 0.1141  I    -3.792    1.982      .167     .868  -.063000   .088000   .3860000      .000      .000  
+821030 45272.00 I  -.067838  .001231   .087394  .001103  I  .3837292  .0001548  2.5780 0.1095  I    -4.107    1.982      .510     .868  -.066000   .090000   .3834000      .000      .000  
+821031 45273.00 I  -.071075  .001231   .088669  .001103  I  .3811019  .0001548  2.6631 0.1458  I    -4.388    1.982      .810     .868  -.070000   .092000   .3807000      .000      .000  
+8211 1 45274.00 I  -.074288  .000701   .090002  .000960  I  .3784328  .0002472  2.6590 0.1458  I    -4.575    1.982      .915     .868  -.073000   .094000   .3780000      .000      .000  
+8211 2 45275.00 I  -.077476  .000701   .091392  .000960  I  .3758140  .0002472  2.5648 0.2268  I    -4.717    1.982      .790     .868  -.076000   .096000   .3754000      .000      .000  
+8211 3 45276.00 I  -.080641  .000628   .092837  .000722  I  .3733233  .0003804  2.4102 0.1895  I    -4.839    1.982      .589     .868  -.080000   .097000   .3729000      .000      .000  
+8211 4 45277.00 I  -.083781  .000628   .094337  .000722  I  .3709964  .0002872  2.2472 0.2383  I    -4.908    1.982      .543     .868  -.083000   .099000   .3706000      .000      .000  
+8211 5 45278.00 I  -.086897  .000628   .095891  .000722  I  .3688131  .0002872  2.1316 0.2206  I    -4.922    1.982      .748     .868  -.087000   .100000   .3685000      .000      .000  
+8211 6 45279.00 I  -.089988  .000917   .097496  .000589  I  .3667045  .0003350  2.1021 0.2207  I    -4.938    1.982     1.082     .868  -.090000   .102000   .3663000      .000      .000  
+8211 7 45280.00 I  -.093053  .001118   .099153  .000717  I  .3645773  .0003351  2.1674 0.2249  I    -4.983    1.982     1.351     .868  -.093000   .104000   .3642000      .000      .000  
+8211 8 45281.00 I  -.096090  .001118   .100860  .000717  I  .3623449  .0003003  2.3073 0.2110  I    -5.018    1.982     1.471     .868  -.097000   .105000   .3619000      .000      .000  
+8211 9 45282.00 I  -.099097  .001318   .102619  .000016  I  .3599507  .0002564  2.4834 0.2041  I    -5.008    1.982     1.494     .868  -.100000   .107000   .3594000      .000      .000  
+821110 45283.00 I  -.102073  .001950   .104429  .001924  I  .3573813  .0002764  2.6501 0.1885  I    -4.993    1.982     1.506     .868  -.104000   .108000   .3568000      .000      .000  
+821111 45284.00 I  -.105017  .001950   .106290  .001924  I  .3546666  .0002764  2.7686 0.1605  I    -5.075    1.982     1.546     .868  -.107000   .110000   .3541000      .000      .000  
+821112 45285.00 I  -.107930  .002422   .108203  .002721  I  .3518680  .0001631  2.8154 0.1605  I    -5.314    1.982     1.621     .868  -.110000   .112000   .3513000      .000      .000  
+821113 45286.00 I  -.110814  .002422   .110166  .002721  I  .3490618  .0001631  2.7843 0.1205  I    -5.654    1.982     1.740     .868  -.114000   .114000   .3485000      .000      .000  
+821114 45287.00 I  -.113671  .002190   .112177  .002052  I  .3463220  .0001774  2.6856 0.1205  I    -5.943    1.982     1.898     .868  -.117000   .116000   .3457000      .000      .000  
+821115 45288.00 I  -.116507  .002190   .114233  .002052  I  .3437057  .0001774  2.5416 0.1276  I    -6.068    1.982     2.039     .868  -.121000   .118000   .3431000      .000      .000  
+821116 45289.00 I  -.119327  .002593   .116329  .000879  I  .3412446  .0001834  2.3800 0.1276  I    -6.049    1.982     2.077     .868  -.124000   .120000   .3407000      .000      .000  
+821117 45290.00 I  -.122137  .002593   .118463  .000879  I  .3389427  .0001834  2.2272 0.1485  I    -5.965    1.982     1.994     .868  -.127000   .122000   .3384000      .000      .000  
+821118 45291.00 I  -.124937  .002223   .120632  .002259  I  .3367799  .0002336  2.1052 0.1485  I    -5.818    1.982     1.901     .868  -.130000   .124000   .3362000      .000      .000  
+821119 45292.00 I  -.127729  .002223   .122834  .002259  I  .3347163  .0002336  2.0312 0.1791  I    -5.576    1.982     1.941     .868  -.134000   .126000   .3342000      .000      .000  
+821120 45293.00 I  -.130510  .002356   .125067  .002673  I  .3326984  .0002716  2.0146 0.1791  I    -5.332    1.982     2.130     .868  -.137000   .128000   .3322000      .000      .000  
+821121 45294.00 I  -.133273  .002356   .127330  .002673  I  .3306678  .0002716  2.0560 0.1963  I    -5.247    1.982     2.332     .868  -.140000   .130000   .3302000      .000      .000  
+821122 45295.00 I  -.136016  .002729   .129623  .002623  I  .3285691  .0002835  2.1491 0.1781  I    -5.301    1.982     2.436     .868  -.143000   .132000   .3281000      .000      .000  
+821123 45296.00 I  -.138730  .002729   .131948  .002623  I  .3263564  .0002306  2.2816 0.1609  I    -5.281    1.982     2.475     .868  -.146000   .135000   .3259000      .000      .000  
+821124 45297.00 I  -.141410  .003676   .134311  .000025  I  .3239977  .0001524  2.4386 0.1382  I    -5.094    1.982     2.547     .868  -.150000   .137000   .3236000      .000      .000  
+821125 45298.00 I  -.144048  .003676   .136713  .000025  I  .3214765  .0001524  2.6035 0.1123  I    -4.936    1.982     2.672     .868  -.153000   .140000   .3211000      .000      .000  
+821126 45299.00 I  -.146643  .003676   .139159  .000025  I  .3187951  .0001650  2.7550 0.1092  I    -5.003    1.982     2.816     .868  -.156000   .142000   .3184000      .000      .000  
+821127 45300.00 I  -.149194  .002929   .141652  .001571  I  .3159804  .0001564  2.8648 0.1199  I    -5.185    1.982     3.002     .868  -.159000   .144000   .3156000      .000      .000  
+821128 45301.00 I  -.151702  .001908   .144196  .002222  I  .3130895  .0001739  2.9026 0.1197  I    -5.231    1.982     3.272     .868  -.162000   .147000   .3128000      .000      .000  
+821129 45302.00 I  -.154167  .001908   .146794  .002222  I  .3102055  .0001813  2.8495 0.1310  I    -5.108    1.982     3.537     .868  -.165000   .149000   .3099000      .000      .000  
+821130 45303.00 I  -.156589  .001536   .149450  .001790  I  .3074182  .0001960  2.7133 0.1335  I    -4.961    1.982     3.616     .868  -.168000   .152000   .3071000      .000      .000  
+8212 1 45304.00 I  -.158966  .001536   .152166  .001790  I  .3047930  .0001960  2.5347 0.1577  I    -4.801    1.982     3.471     .868  -.171000   .154000   .3045000      .000      .000  
+8212 2 45305.00 I  -.161296  .001536   .154943  .001790  I  .3023427  .0002472  2.3745 0.1681  I    -4.454    1.982     3.306     .868  -.173000   .157000   .3021000      .000      .000  
+8212 3 45306.00 I  -.163575  .001039   .157781  .001214  I  .3000190  .0002732  2.2891 0.1867  I    -3.859    1.982     3.343     .868  -.175000   .159000   .2997000      .000      .000  
+8212 4 45307.00 I  -.165800  .001039   .160681  .001214  I  .2977304  .0002799  2.3058 0.1827  I    -3.200    1.982     3.557     .868  -.177000   .162000   .2974000      .000      .000  
+8212 5 45308.00 I  -.167968  .000889   .163642  .000878  I  .2953770  .0002426  2.4138 0.1816  I    -2.684    1.982     3.729     .868  -.179000   .164000   .2950000      .000      .000  
+8212 6 45309.00 I  -.170080  .000513   .166663  .001080  I  .2928865  .0002316  2.5707 0.1677  I    -2.326    1.982     3.721     .868  -.181000   .167000   .2925000      .000      .000  
+8212 7 45310.00 I  -.172133  .000513   .169742  .001080  I  .2902369  .0002316  2.7237 0.1357  I    -2.005    1.982     3.607     .868  -.183000   .170000   .2899000      .000      .000  
+8212 8 45311.00 I  -.174126  .000513   .172880  .001080  I  .2874545  .0001413  2.8304 0.1342  I    -1.641    1.982     3.517     .868  -.185000   .173000   .2872000      .000      .000  
+8212 9 45312.00 I  -.176059  .001548   .176075  .001061  I  .2845995  .0001355  2.8670 0.1003  I    -1.257    1.982     3.475     .868  -.186000   .175000   .2844000      .000      .000  
+821210 45313.00 I  -.177931  .001548   .179324  .001061  I  .2817447  .0001425  2.8311 0.1042  I     -.929    1.982     3.433     .868  -.188000   .178000   .2816000      .000      .000  
+821211 45314.00 I  -.179743  .001828   .182626  .001286  I  .2789577  .0001583  2.7341 0.1010  I     -.697    1.982     3.388     .868  -.190000   .181000   .2789000      .000      .000  
+821212 45315.00 I  -.181493  .002581   .185976  .001021  I  .2762903  .0001433  2.5958 0.0833  I     -.486    1.982     3.378     .868  -.191000   .184000   .2764000      .000      .000  
+821213 45316.00 I  -.183181  .000447   .189370  .000527  I  .2737720  .0000521  2.4397 0.0743  I     -.134    1.982     3.370     .868  -.193000   .187000   .2740000      .000      .000  
+821214 45317.00 I  -.184801  .000323   .192806  .000496  I  .2714093  .0000395  2.2885 0.0320  I      .468     .515     3.255     .367  -.194000   .190000   .2719000      .000      .000  
+821215 45318.00 I  -.186349  .000171   .196277  .000497  I  .2691863  .0000370  2.1633 0.0261  I     1.276     .509     2.983     .264  -.196000   .193000   .2698000      .000      .000  
+821216 45319.00 I  -.187819  .000150   .199780  .000514  I  .2670684  .0000340  2.0808 0.0251  I     2.180     .509     2.663     .264  -.197000   .196000   .2678000      .000      .000  
+821217 45320.00 I  -.189206  .000150   .203308  .000514  I  .2650073  .0000338  2.0503 0.0240  I     3.089     .509     2.463     .264  -.198000   .199000   .2658000      .000      .000  
+821218 45321.00 I  -.190511  .000150   .206851  .000514  I  .2629497  .0000338  2.0739 0.0238  I     3.871     .509     2.424     .264  -.199000   .202000   .2638000      .000      .000  
+821219 45322.00 I  -.191733  .000235   .210394  .000528  I  .2608427  .0000334  2.1478 0.0235  I     4.429     .509     2.414     .264  -.201000   .205000   .2617000      .000      .000  
+821220 45323.00 I  -.192882  .000275   .213927  .000542  I  .2586404  .0000326  2.2626 0.0261  I     4.819     .502     2.320     .868  -.202000   .208000   .2596000      .000      .000  
+821221 45324.00 I  -.193966  .000387   .217450  .000551  I  .2563087  .0000400  2.4037 0.0286  I     5.242    1.982     2.183     .868  -.203000   .211000   .2572000      .000      .000  
+821222 45325.00 I  -.194995  .000423   .220960  .000574  I  .2538298  .0000471  2.5542 0.0255  I     5.787    1.982     2.088     .868  -.204000   .214000   .2547000      .000      .000  
+821223 45326.00 I  -.195974  .001580   .224458  .000675  I  .2512028  .0000317  2.6970 0.0284  I     6.278    1.982     2.012     .868  -.205000   .218000   .2521000      .000      .000  
+821224 45327.00 I  -.196909  .001580   .227945  .000675  I  .2484449  .0000317  2.8123 0.0239  I     6.528    1.982     1.880     .868  -.206000   .221000   .2493000      .000      .000  
+821225 45328.00 I  -.197805  .001166   .231421  .000526  I  .2455952  .0000357  2.8765 0.0239  I     6.624    1.982     1.758     .868  -.207000   .225000   .2464000      .000      .000  
+821226 45329.00 I  -.198664  .001166   .234887  .000526  I  .2427162  .0000357  2.8677 0.0877  I     6.753    1.982     1.819     .868  -.208000   .228000   .2434000      .000      .000  
+821227 45330.00 I  -.199490  .002915   .238344  .000515  I  .2398866  .0001717  2.7784 0.0877  I     6.848    1.982     2.070     .868  -.209000   .231000   .2404000      .000      .000  
+821228 45331.00 I  -.200286  .002915   .241793  .000515  I  .2371797  .0001717  2.6283 0.1214  I     6.705    1.982     2.253     .868  -.209000   .235000   .2376000      .000      .000  
+821229 45332.00 I  -.201053  .002915   .245236  .000515  I  .2346342  .0001717  2.4658 0.1214  I     6.417    1.982     2.141     .868  -.210000   .238000   .2347000      .000      .000  
+821230 45333.00 I  -.201797  .002915   .248673  .000515  I  .2322319  .0001717  2.3521 0.1012  I     6.398    1.982     1.833     .868  -.210000   .242000   .2321000      .000      .000  
+821231 45334.00 I  -.202517  .002915   .252106  .000515  I  .2298981  .0001072  2.3342 0.1012  I     6.899    1.982     1.624     .868  -.211000   .245000   .2295000      .000      .000  
+83 1 1 45335.00 I  -.203212  .002915   .255534  .000515  I  .2275282  .0001072  2.4222 0.1100  I     7.695    1.982     1.630     .868  -.211000   .249000   .2269000      .000      .000  
+83 1 2 45336.00 I  -.203881  .002158   .258959  .001033  I  .2250294  .0001922  2.5833 0.1100  I     8.358    1.982     1.688     .868  -.212000   .252000   .2242000      .000      .000  
+83 1 3 45337.00 I  -.204519  .002158   .262381  .001033  I  .2223567  .0001922  2.7593 0.1359  I     8.666    1.982     1.656     .868  -.212000   .256000   .2222000      .000      .000  
+83 1 4 45338.00 I  -.205125  .002158   .265800  .001033  I  .2195252  .0001922  2.8923 0.1526  I     8.662    1.982     1.614     .868  -.213000   .259000   .2194000      .000      .000  
+83 1 5 45339.00 I  -.205695  .001526   .269217  .003380  I  .2165984  .0002370  2.9466 0.1595  I     8.483    1.982     1.702     .868  -.213000   .263000   .2150000      .000      .000  
+83 1 6 45340.00 I  -.206227  .001526   .272632  .003380  I  .2136601  .0002547  2.9167 0.1740  I     8.244    1.982     1.872     .868  -.213000   .267000   .2120000      .000      .000  
+83 1 7 45341.00 I  -.206714  .001526   .276045  .003380  I  .2107876  .0002547  2.8191 0.1705  I     7.987    1.982     1.950     .868  -.213000   .271000   .2092000      .000      .000  
+83 1 8 45342.00 I  -.207150  .003310   .279453  .004667  I  .2080355  .0002267  2.6808 0.1961  I     7.667    1.982     1.869     .868  -.212000   .275000   .2068000      .000      .000  
+83 1 9 45343.00 I  -.207528  .000663   .282860  .003542  I  .2054302  .0002982  2.5300 0.1873  I     7.247    1.982     1.739     .868  -.212000   .279000   .2044000      .000      .000  
+83 110 45344.00 I  -.207840  .000663   .286269  .003542  I  .2029718  .0002982  2.3904 0.2236  I     6.840    1.982     1.654     .868  -.212000   .283000   .2021000      .000      .000  
+83 111 45345.00 I  -.208081  .001547   .289686  .001815  I  .2006395  .0003333  2.2806 0.2570  I     6.649    1.982     1.567     .868  -.212000   .287000   .2000000      .000      .000  
+83 112 45346.00 I  -.208244  .001547   .293116  .001815  I  .1983961  .0004186  2.2144 0.2675  I     6.733    1.982     1.401     .868  -.212000   .291000   .1980000      .000      .000  
+83 113 45347.00 I  -.208322  .001547   .296563  .001815  I  .1961929  .0004186  2.2017 0.2960  I     6.936    1.982     1.211     .868  -.211000   .295000   .1962000      .000      .000  
+83 114 45348.00 I  -.208311  .001547   .300030  .001815  I  .1939732  .0004186  2.2477 0.3748  I     7.065    1.982     1.140     .868  -.211000   .299000   .1943000      .000      .000  
+83 115 45349.00 I  -.208208  .002241   .303518  .001940  I  .1916788  .0006219  2.3498 0.3748  I     7.029    1.982     1.229     .868  -.211000   .303000   .1923000      .000      .000  
+83 116 45350.00 I  -.208010  .002241   .307029  .001940  I  .1892581  .0006219  2.4983 0.4537  I     6.829    1.982     1.357     .868  -.210000   .307000   .1901000      .000      .000  
+83 117 45351.00 I  -.207718  .002337   .310562  .001588  I  .1866722  .0006608  2.6769 0.4537  I     6.521    1.982     1.397     .868  -.210000   .311000   .1879000      .000      .000  
+83 118 45352.00 I  -.207328  .002337   .314118  .001588  I  .1839011  .0006608  2.8653 0.4673  I     6.219    1.982     1.362     .868  -.209000   .315000   .1855000      .000      .000  
+83 119 45353.00 I  -.206842  .002337   .317697  .001588  I  .1809453  .0006608  3.0428 0.4294  I     6.003    1.982     1.332     .868  -.209000   .319000   .1829000      .000      .000  
+83 120 45354.00 I  -.206262  .002068   .321296  .001369  I  .1778254  .0005486  3.1905 0.3802  I     5.817    1.982     1.307     .868  -.208000   .323000   .1800000      .000      .000  
+83 121 45355.00 I  -.205593  .001829   .324915  .000826  I  .1745799  .0003761  3.2912 0.3482  I     5.568    1.982     1.223     .868  -.207000   .327000   .1771000      .000      .000  
+83 122 45356.00 I  -.204842  .001829   .328552  .000826  I  .1712638  .0004289  3.3294 0.2325  I     5.290    1.982     1.128     .868  -.207000   .331000   .1740000      .000      .000  
+83 123 45357.00 I  -.204017  .001379   .332205  .000756  I  .1679448  .0002736  3.2964 0.2522  I     5.029    1.982     1.193     .868  -.206000   .335000   .1710000      .000      .000  
+83 124 45358.00 I  -.203126  .001079   .335874  .000535  I  .1646928  .0002654  3.1981 0.1906  I     4.633    1.982     1.467     .868  -.206000   .339000   .1679000      .000      .000  
+83 125 45359.00 I  -.202180  .001079   .339560  .000535  I  .1615602  .0002654  3.0650 0.1864  I     3.886    1.982     1.732     .868  -.205000   .343000   .1649000      .000      .000  
+83 126 45360.00 I  -.201190  .004400   .343261  .003693  I  .1585570  .0002618  2.9494 0.1785  I     2.925    1.982     1.715     .868  -.204000   .347000   .1619000      .000      .000  
+83 127 45361.00 I  -.200166  .004400   .346978  .003693  I  .1556373  .0002389  2.9060 0.1527  I     2.273    1.982     1.438     .868  -.204000   .351000   .1589000      .000      .000  
+83 128 45362.00 I  -.199118  .004400   .350711  .003693  I  .1527105  .0001573  2.9659 0.1513  I     2.332    1.982     1.183     .868  -.203000   .354000   .1558000      .000      .000  
+83 129 45363.00 I  -.198059  .004400   .354459  .003693  I  .1496744  .0001857  3.1189 0.1111  I     2.972    1.982     1.139     .868  -.203000   .358000   .1526000      .000      .000  
+83 130 45364.00 I  -.196995  .004394   .358220  .003694  I  .1464584  .0001570  3.3147 0.1216  I     3.714    1.982     1.214     .868  -.202000   .362000   .1491000      .000      .000  
+83 131 45365.00 I  -.195926  .004394   .361986  .003694  I  .1430533  .0001570  3.4854 0.1127  I     4.169    1.982     1.269     .868  -.201000   .366000   .1455000      .000      .000  
+83 2 1 45366.00 I  -.194851  .000566   .365751  .000131  I  .1395149  .0001618  3.5740 0.0963  I     4.222    1.982     1.363     .868  -.200000   .369000   .1416000      .000      .000  
+83 2 2 45367.00 I  -.193771  .000890   .369508  .000155  I  .1359409  .0001114  3.5556 0.1255  I     3.959    1.982     1.628     .868  -.199000   .373000   .1374000      .000      .000  
+83 2 3 45368.00 I  -.192684  .000890   .373250  .000155  I  .1324362  .0001918  3.4396 0.1135  I     3.572    1.982     1.993     .868  -.198000   .376000   .1335000      .000      .000  
+83 2 4 45369.00 I  -.191588  .000890   .376969  .000155  I  .1290835  .0001979  3.2581 0.1238  I     3.240    1.982     2.200     .868  -.197000   .380000   .1298000      .000      .000  
+83 2 5 45370.00 I  -.190484  .000629   .380659  .002277  I  .1259296  .0001566  3.0479 0.1262  I     2.983    1.982     2.117     .868  -.196000   .383000   .1264000      .000      .000  
+83 2 6 45371.00 I  -.189367  .000629   .384312  .002277  I  .1229869  .0001566  2.8403 0.1190  I     2.712     .115     1.881     .868  -.195000   .386000   .1233000      .000      .000  
+83 2 7 45372.00 I  -.188234  .000795   .387921  .002104  I  .1202404  .0001792  2.6586 0.1199  I     2.474     .115     1.704     .868  -.193000   .390000   .1205000      .000      .000  
+83 2 8 45373.00 I  -.187082  .000765   .391479  .002278  I  .1176558  .0001815  2.5185 0.1236  I     2.501     .115     1.638     .868  -.192000   .393000   .1178000      .000      .000  
+83 2 9 45374.00 I  -.185908  .000765   .394981  .002278  I  .1151858  .0001704  2.4312 0.1204  I     2.928     .115     1.601     .868  -.191000   .396000   .1152000      .000      .000  
+83 210 45375.00 I  -.184713  .001330   .398433  .003276  I  .1127737  .0001582  2.4032 0.1047  I     3.581     .115     1.571     .868  -.190000   .399000   .1127000      .000      .000  
+83 211 45376.00 I  -.183498  .001263   .401844  .003426  I  .1103595  .0001217  2.4351 0.1560  I     4.159     .115     1.624     .868  -.188000   .402000   .1103000      .000      .000  
+83 212 45377.00 I  -.182269  .003306   .405222  .004628  I  .1078857  .0002689  2.5205 0.0718  I     4.536    1.982     1.808     .868  -.187000   .406000   .1079000      .000      .000  
+83 213 45378.00 I  -.181029  .004242   .408577  .006541  I  .1053057  .0000762  2.6445 0.1387  I     4.766    1.982     2.038     .868  -.185000   .409000   .1053000      .000      .000  
+83 214 45379.00 I  -.179780  .003088   .411914  .004643  I  .1025905  .0000680  2.7873 0.0511  I     4.919    1.982     2.186     .868  -.184000   .412000   .1025000      .000      .000  
+83 215 45380.00 I  -.178518  .003088   .415234  .004643  I  .0997325  .0000680  2.9259 0.1113  I     5.026    1.982     2.203     .868  -.182000   .415000   .0996000      .000      .000  
+83 216 45381.00 I  -.177241  .000821   .418537  .000936  I  .0967476  .0002120  3.0379 0.1113  I     5.132    1.982     2.140     .868  -.181000   .418000   .0965000      .000      .000  
+83 217 45382.00 I  -.175944  .000821   .421823  .000936  I  .0936712  .0002120  3.1063 0.1499  I     5.281    1.982     2.053     .868  -.179000   .421000   .0934000      .000      .000  
+83 218 45383.00 I  -.174625  .000821   .425092  .000936  I  .0905532  .0002120  3.1201 0.1545  I     5.469    1.982     1.959     .868  -.178000   .424000   .0902000      .000      .000  
+83 219 45384.00 I  -.173282  .000821   .428344  .000936  I  .0874499  .0002249  3.0770 0.2929  I     5.676    1.982     1.888     .868  -.176000   .427000   .0870000      .000      .000  
+83 220 45385.00 I  -.171913  .000524   .431577  .000847  I  .0844157  .0005460  2.9842 0.2953  I     5.865    1.982     1.908     .868  -.174000   .430000   .0840000      .000      .000  
+83 221 45386.00 I  -.170518  .000524   .434794  .000847  I  .0814906  .0005460  2.8641 0.4075  I     5.933    1.982     2.024     .868  -.172000   .433000   .0810000      .000      .000  
+83 222 45387.00 I  -.169096  .000524   .437994  .000092  I  .0786836  .0006050  2.7557 0.3876  I     5.770    1.982     2.106     .868  -.171000   .436000   .0782000      .000      .000  
+83 223 45388.00 I  -.167646  .000383   .441179  .001618  I  .0759597  .0005503  2.7058 0.4089  I     5.459    1.982     1.996     .868  -.169000   .439000   .0755000      .000      .000  
+83 224 45389.00 I  -.166167  .000383   .444350  .001618  I  .0732408  .0005503  2.7493 0.3891  I     5.311    1.982     1.711     .868  -.167000   .442000   .0728000      .000      .000  
+83 225 45390.00 I  -.164660  .000383   .447507  .001618  I  .0704274  .0005503  2.8937 0.2792  I     5.580    1.982     1.453     .868  -.165000   .445000   .0700000      .000      .000  
+83 226 45391.00 I  -.163125  .000542   .450652  .000761  I  .0674296  .0000950  3.1096 0.2792  I     6.197    1.982     1.363     .868  -.163000   .448000   .0670000      .000      .000  
+83 227 45392.00 I  -.161563  .000542   .453782  .000761  I  .0642052  .0000950  3.3346 0.0672  I     6.862     .187     1.369     .868  -.162000   .451000   .0637000      .000      .000  
+83 228 45393.00 I  -.159973  .000542   .456896  .000761  I  .0607805  .0000950  3.4993 0.0656  I     7.307     .187     1.338     .868  -.160000   .454000   .0603000      .000      .000  
+83 3 1 45394.00 I  -.158356  .000625   .459991  .000572  I  .0572429  .0000905  3.5550 0.0656  I     7.414     .187     1.296     .868  -.158000   .457000   .0567000      .000      .000  
+83 3 2 45395.00 I  -.156711  .000625   .463065  .000572  I  .0537102  .0000905  3.4915 0.0727  I     7.194     .187     1.378     .868  -.156000   .460000   .0531000      .000      .000  
+83 3 3 45396.00 I  -.155027  .001403   .466114  .001870  I  .0502915  .0001139  3.3331 0.0736  I     6.767     .187     1.568     .868  -.154000   .463000   .0496000      .000      .000  
+83 3 4 45397.00 I  -.153292  .001410   .469131  .001955  I  .0470614  .0001162  3.1223 0.1340  I     6.298     .187     1.650     .868  -.152000   .466000   .0462000      .000      .000  
+83 3 5 45398.00 I  -.151495  .003480   .472111  .004355  I  .0440498  .0002426  2.9026 0.1345  I     5.851    1.982     1.462     .868  -.150000   .469000   .0431000      .000      .000  
+83 3 6 45399.00 I  -.149620  .003480   .475047  .004355  I  .0412480  .0002426  2.7075 0.1533  I     5.385    1.982     1.098     .868  -.148000   .472000   .0403000      .000      .000  
+83 3 7 45400.00 I  -.147661  .003431   .477937  .004401  I  .0386193  .0001876  2.5590 0.1533  I     4.949    1.982      .789     .868  -.146000   .475000   .0376000      .000      .000  
+83 3 8 45401.00 I  -.145613  .003431   .480781  .004401  I  .0361107  .0001876  2.4686 0.1434  I     4.763    1.982      .637     .868  -.144000   .478000   .0351000      .000      .000  
+83 3 9 45402.00 I  -.143471  .002039   .483580  .001492  I  .0336612  .0002170  2.4409 0.1434  I     4.963    1.982      .572     .868  -.141000   .481000   .0326000      .000      .000  
+83 310 45403.00 I  -.141231  .002039   .486336  .001492  I  .0312083  .0002170  2.4749 0.1534  I     5.363    1.982      .510     .868  -.139000   .484000   .0302000      .000      .000  
+83 311 45404.00 I  -.138889  .002039   .489050  .001492  I  .0286930  .0002170  2.5639 0.1144  I     5.604    1.982      .472     .868  -.137000   .487000   .0277000      .000      .000  
+83 312 45405.00 I  -.136442  .001122   .491725  .000702  I  .0260663  .0000726  2.6953 0.1160  I     5.520    1.982      .516     .868  -.134000   .490000   .0251000      .000      .000  
+83 313 45406.00 I  -.133885  .001107   .494364  .000579  I  .0232944  .0000819  2.8507 0.0547  I     5.211     .120      .628     .868  -.132000   .493000   .0224000      .000      .000  
+83 314 45407.00 I  -.131210  .001107   .496969  .000579  I  .0203649  .0000819  3.0062 0.0468  I     4.824     .120      .709     .868  -.129000   .496000   .0197000      .000      .000  
+83 315 45408.00 I  -.128410  .000742   .499543  .000606  I  .0172904  .0000455  3.1364 0.0468  I     4.419     .120      .656     .868  -.127000   .499000   .0167000      .000      .000  
+83 316 45409.00 I  -.125478  .000742   .502086  .000606  I  .0141085  .0000455  3.2173 0.0322  I     4.034     .120      .448     .868  -.124000   .502000   .0136000      .000      .000  
+83 317 45410.00 I  -.122418  .000742   .504602  .000606  I  .0108778  .0000455  3.2322 0.0322  I     3.722     .120      .156     .868  -.121000   .505000   .0104000      .000      .000  
+83 318 45411.00 I  -.119235  .000742   .507091  .000606  I  .0076674  .0000455  3.1773 0.0574  I     3.482     .120     -.105     .868  -.118000   .507000   .0073000      .000      .000  
+83 319 45412.00 I  -.115937  .000322   .509555  .000960  I  .0045425  .0001053  3.0645 0.0574  I     3.245    1.982     -.262     .868  -.115000   .510000   .0043000      .000      .000  
+83 320 45413.00 I  -.112530  .000322   .511993  .000960  I  .0015490  .0001053  2.9199 0.0862  I     2.965    1.982     -.321     .868  -.112000   .512000   .0014000      .000      .000  
+83 321 45414.00 I  -.109022  .000050   .514404  .000777  I -.0012990  .0001364  2.7804 0.0915  I     2.690    1.982     -.350     .868  -.109000   .515000  -.0014000      .000      .000  
+83 322 45415.00 I  -.105420  .000646   .516789  .000796  I -.0040274  .0001497  2.6878 0.1319  I     2.519    1.982     -.435     .868  -.106000   .517000  -.0041000      .000      .000  
+83 323 45416.00 I  -.101731  .000646   .519147  .000796  I -.0067025  .0002259  2.6790 0.1260  I     2.531    1.982     -.609     .868  -.102000   .519000  -.0068000      .000      .000  
+83 324 45417.00 I  -.097962  .000656   .521477  .001889  I -.0094192  .0002028  2.7718 0.1564  I     2.738    1.982     -.816     .868  -.099000   .522000  -.0095000      .000      .000  
+83 325 45418.00 I  -.094119  .000802   .523776  .002247  I -.0122760  .0002164  2.9544 0.1483  I     3.049    1.982     -.948     .868  -.095000   .524000  -.0124000      .000      .000  
+83 326 45419.00 I  -.090209  .000802   .526043  .002247  I -.0153433  .0002164  3.1830 0.1530  I     3.301    1.982     -.955     .868  -.092000   .526000  -.0154000      .000      .000  
+83 327 45420.00 I  -.086237  .000802   .528276  .002247  I -.0186359  .0002164  3.3933 0.1479  I     3.380    1.982     -.916     .868  -.088000   .528000  -.0187000      .000      .000  
+83 328 45421.00 I  -.082209  .001142   .530471  .002405  I -.0221034  .0002018  3.5242 0.1105  I     3.304    1.982     -.942     .868  -.084000   .530000  -.0222000      .000      .000  
+83 329 45422.00 I  -.078130  .001142   .532621  .002405  I -.0256461  .0000450  3.5406 0.1034  I     3.171    1.982    -1.028     .868  -.081000   .532000  -.0257000      .000      .000  
+83 330 45423.00 I  -.074009  .001062   .534721  .001033  I -.0291468  .0000448  3.4436 0.0317  I     3.035    1.982    -1.055     .868  -.077000   .534000  -.0292000      .000      .000  
+83 331 45424.00 I  -.069850  .001062   .536763  .001033  I -.0325059  .0000448  3.2644 0.0317  I     2.891    1.982     -.968     .868  -.073000   .536000  -.0326000      .000      .000  
+83 4 1 45425.00 I  -.065659  .001062   .538743  .001033  I -.0356630  .0000448  3.0476 0.0317  I     2.723    1.982     -.881     .868  -.069000   .538000  -.0358000      .000      .000  
+83 4 2 45426.00 I  -.061440  .001062   .540657  .001033  I -.0386029  .0000448  2.8363 0.0940  I     2.532    1.982     -.931     .868  -.065000   .539000  -.0387000      .000      .000  
+83 4 3 45427.00 I  -.057195  .000454   .542501  .000976  I -.0413476  .0001825  2.6610 0.0940  I     2.344    1.982    -1.100     .868  -.060000   .541000  -.0415000      .000      .000  
+83 4 4 45428.00 I  -.052928  .000454   .544268  .000976  I -.0439429  .0001825  2.5396 0.1565  I     2.274    1.982    -1.238     .868  -.056000   .542000  -.0441000      .000      .000  
+83 4 5 45429.00 I  -.048642  .000555   .545956  .001380  I -.0464476  .0002542  2.4804 0.1404  I     2.501    1.982    -1.256     .868  -.052000   .544000  -.0466000      .000      .000  
+83 4 6 45430.00 I  -.044341  .000911   .547559  .001490  I -.0489244  .0002133  2.4833 0.1659  I     3.093    1.982    -1.208     .868  -.048000   .545000  -.0491000      .000      .000  
+83 4 7 45431.00 I  -.040027  .000911   .549075  .001490  I -.0514330  .0002133  2.5427 0.1508  I     3.864    1.982    -1.184     .868  -.044000   .547000  -.0516000      .000      .000  
+83 4 8 45432.00 I  -.035702  .000911   .550499  .001490  I -.0540247  .0002133  2.6468 0.1398  I     4.481    1.982    -1.184     .868  -.040000   .548000  -.0542000      .000      .000  
+83 4 9 45433.00 I  -.031369  .001542   .551829  .001346  I -.0567364  .0001807  2.7802 0.1398  I     4.733    1.982    -1.138     .868  -.036000   .550000  -.0568000      .000      .000  
+83 410 45434.00 I  -.027031  .001542   .553065  .001346  I -.0595887  .0001807  2.9244 0.1287  I     4.664    1.982    -1.005     .868  -.032000   .551000  -.0596000      .000      .000  
+83 411 45435.00 I  -.022693  .001542   .554205  .001346  I -.0625809  .0001833  3.0558 0.1308  I     4.461    1.982     -.834     .868  -.028000   .552000  -.0625000      .000      .000  
+83 412 45436.00 I  -.018358  .001890   .555249  .000766  I -.0656872  .0001891  3.1480 0.1317  I     4.290    1.982     -.733     .868  -.024000   .553000  -.0656000      .000      .000  
+83 413 45437.00 I  -.014034  .001890   .556197  .000766  I -.0688558  .0001891  3.1769 0.1457  I     4.235    1.982     -.790     .868  -.020000   .555000  -.0686000      .000      .000  
+83 414 45438.00 I  -.009727  .001920   .557050  .000941  I -.0720153  .0002217  3.1286 0.1532  I     4.287    1.982     -.994     .868  -.016000   .556000  -.0716000      .000      .000  
+83 415 45439.00 I  -.005446  .001957   .557808  .000886  I -.0750885  .0002410  3.0067 0.1637  I     4.358    1.982    -1.231     .868  -.012000   .557000  -.0746000      .000      .000  
+83 416 45440.00 I  -.001195  .001957   .558473  .000886  I -.0780126  .0002410  2.8363 0.1670  I     4.341    1.982    -1.372     .868  -.008000   .558000  -.0774000      .000      .000  
+83 417 45441.00 I   .003020  .001691   .559045  .001048  I -.0807588  .0002313  2.6588 0.1652  I     4.226    1.982    -1.383     .868  -.004000   .559000  -.0800000      .000      .000  
+83 418 45442.00 I   .007198  .001556   .559527  .001266  I -.0833430  .0002261  2.5197 0.1617  I     4.150    1.982    -1.340     .868   .001000   .559000  -.0825000      .000      .000  
+83 419 45443.00 I   .011338  .001556   .559922  .001266  I -.0858234  .0002261  2.4561 0.1139  I     4.301    1.982    -1.327     .868   .005000   .560000  -.0849000      .000      .000  
+83 420 45444.00 I   .015440  .000805   .560231  .001116  I -.0882865  .0000283  2.4863 0.2799  I     4.772    1.982    -1.361     .868   .009000   .561000  -.0873000      .000      .000  
+83 421 45445.00 I   .019503  .000805   .560459  .001116  I -.0908251  .0005120  2.6036 0.2564  I     5.469    1.982    -1.391     .868   .013000   .561000  -.0897000      .000      .000  
+83 422 45446.00 I   .023529  .000805   .560610  .001116  I -.0935121  .0005120  2.7760 0.3891  I     6.141    1.982    -1.364     .868   .017000   .562000  -.0924000      .000      .000  
+83 423 45447.00 I   .027520  .000608   .560689  .000875  I -.0963788  .0005860  2.9534 0.3632  I     6.524    1.982    -1.273     .868   .022000   .562000  -.0952000      .000      .000  
+83 424 45448.00 I   .031477  .001150   .560698  .000944  I -.0994022  .0005154  3.0807 0.3902  I     6.510    1.982    -1.164     .868   .026000   .563000  -.0982000      .000      .000  
+83 425 45449.00 I   .035403  .001150   .560642  .000944  I -.1025100  .0005154  3.1176 0.3916  I     6.219    1.982    -1.098     .868   .030000   .563000  -.1013000      .000      .000  
+83 426 45450.00 I   .039300  .001509   .560525  .001009  I -.1056022  .0005898  3.0490 0.2662  I     5.910    1.982    -1.088     .868   .034000   .563000  -.1044000      .000      .000  
+83 427 45451.00 I   .043172  .001177   .560348  .000745  I -.1085773  .0001333  2.8883 0.3023  I     5.781    1.982    -1.080     .868   .038000   .563000  -.1074000      .000      .000  
+83 428 45452.00 I   .047021  .001177   .560116  .000745  I -.1113600  .0001333  2.6713 0.0943  I     5.828    1.982    -1.028     .868   .043000   .564000  -.1102000      .000      .000  
+83 429 45453.00 I   .050854  .001177   .559830  .000745  I -.1139153  .0001333  2.4407 0.0973  I     5.897    1.982     -.971     .868   .047000   .564000  -.1128000      .000      .000  
+83 430 45454.00 I   .054675  .000704   .559493  .000304  I -.1162496  .0001417  2.2348 0.0973  I     5.863    1.982     -.981     .868   .051000   .564000  -.1153000      .000      .000  
+83 5 1 45455.00 I   .058491  .000704   .559106  .000304  I -.1184020  .0001417  2.0802 0.1276  I     5.755    1.982    -1.059     .868   .055000   .564000  -.1175000      .000      .000  
+83 5 2 45456.00 I   .062309  .000649   .558672  .000671  I -.1204319  .0002123  1.9913 0.0757  I     5.725    1.982    -1.129     .868   .059000   .564000  -.1197000      .000      .000  
+83 5 3 45457.00 I   .066136  .000165   .558194  .000379  I -.1224077  .0000536  1.9719 0.1095  I     5.902    1.982    -1.151     .868   .064000   .564000  -.1219000      .000      .000  
+83 5 4 45458.00 I   .069979  .000165   .557674  .000379  I -.1243973  .0000536  2.0175 0.0405  I     6.293    1.982    -1.169     .868   .068000   .564000  -.1240000      .000      .000  
+83 5 5 45459.00 I   .073844  .000700   .557115  .000397  I -.1264606  .0000608  2.1170 0.0405  I     6.795    1.982    -1.234     .868   .072000   .564000  -.1262000      .000      .000  
+83 5 6 45460.00 I   .077738  .000700   .556519  .000397  I -.1286443  .0000608  2.2555 0.0430  I     7.231    1.982    -1.316     .868   .076000   .563000  -.1285000      .000      .000  
+83 5 7 45461.00 I   .081669  .000700   .555888  .000397  I -.1309783  .0000608  2.4139 0.0347  I     7.403    1.982    -1.342     .868   .080000   .563000  -.1310000      .000      .000  
+83 5 8 45462.00 I   .085642  .000705   .555220  .000356  I -.1334714  .0000336  2.5701 0.0449  I     7.199    1.982    -1.282     .868   .085000   .562000  -.1336000      .000      .000  
+83 5 9 45463.00 I   .089658  .000942   .554509  .000366  I -.1361100  .0000662  2.7008 0.1815  I     6.714    1.982    -1.164     .868   .089000   .562000  -.1363000      .000      .000  
+83 510 45464.00 I   .093715  .003000   .553751  .000610  I -.1388562  .0003615  2.7811 0.1858  I     6.220    1.982    -1.041     .868   .093000   .561000  -.1392000      .000      .000  
+83 511 45465.00 I   .097812  .002066   .552942  .000645  I -.1416479  .0003656  2.7885 0.2571  I     5.938    1.982     -.976     .868   .097000   .560000  -.1421000      .000      .000  
+83 512 45466.00 I   .101946  .002066   .552078  .000645  I -.1444055  .0003656  2.7130 0.2585  I     5.837    1.982    -1.031     .868   .101000   .559000  -.1450000      .000      .000  
+83 513 45467.00 I   .106112  .002066   .551155  .000645  I -.1470507  .0003656  2.5681 0.2585  I     5.722    1.982    -1.214     .868   .105000   .559000  -.1478000      .000      .000  
+83 514 45468.00 I   .110302  .002066   .550170  .000645  I -.1495316  .0003656  2.3930 0.2062  I     5.489    1.982    -1.438     .868   .109000   .558000  -.1504000      .000      .000  
+83 515 45469.00 I   .114509  .000312   .549120  .000770  I -.1518442  .0001910  2.2404 0.2062  I     5.232    1.982    -1.581     .868   .113000   .557000  -.1528000      .000      .000  
+83 516 45470.00 I   .118726  .000312   .547998  .000770  I -.1540358  .0001910  2.1580 0.1081  I     5.105    1.982    -1.601     .868   .117000   .556000  -.1551000      .000      .000  
+83 517 45471.00 I   .122945  .000361   .546802  .000799  I -.1561922  .0001015  2.1718 0.1425  I     5.172    1.982    -1.554     .868   .121000   .555000  -.1573000      .000      .000  
+83 518 45472.00 I   .127159  .000272   .545527  .002039  I -.1584097  .0002116  2.2767 0.1173  I     5.391    1.982    -1.518     .868   .125000   .554000  -.1597000      .000      .000  
+83 519 45473.00 I   .131360  .000272   .544168  .002039  I -.1607653  .0002116  2.4409 0.1564  I     5.686    1.982    -1.512     .868   .129000   .553000  -.1621000      .000      .000  
+83 520 45474.00 I   .135539  .000272   .542719  .002039  I -.1632953  .0002304  2.6166 0.2434  I     5.960    1.982    -1.509     .868   .133000   .552000  -.1647000      .000      .000  
+83 521 45475.00 I   .139689  .001183   .541175  .003105  I -.1659860  .0004384  2.7549 0.2226  I     6.076    1.982    -1.476     .868   .137000   .551000  -.1675000      .000      .000  
+83 522 45476.00 I   .143801  .001183   .539534  .003105  I -.1687803  .0003810  2.8187 0.2904  I     5.901    1.982    -1.406     .868   .141000   .549000  -.1704000      .000      .000  
+83 523 45477.00 I   .147867  .001183   .537793  .003105  I -.1715926  .0003810  2.7901 0.2700  I     5.417    1.982    -1.308     .868   .144000   .548000  -.1733000      .000      .000  
+83 524 45478.00 I   .151878  .001630   .535950  .002590  I -.1743313  .0003828  2.6738 0.2700  I     4.793    1.982    -1.199     .868   .148000   .546000  -.1760000      .000      .000  
+83 525 45479.00 I   .155826  .001630   .534004  .002590  I -.1769185  .0003828  2.4924 0.2717  I     4.289    1.982    -1.104     .868   .152000   .545000  -.1787000      .000      .000  
+83 526 45480.00 I   .159706  .001776   .531958  .002117  I -.1793060  .0003856  2.2809 0.1973  I     4.048    1.982    -1.058     .868   .156000   .543000  -.1811000      .000      .000  
+83 527 45481.00 I   .163513  .001827   .529815  .000960  I -.1814826  .0000959  2.0764 0.1964  I     3.977    1.982    -1.099     .868   .159000   .541000  -.1833000      .000      .000  
+83 528 45482.00 I   .167243  .001827   .527577  .000960  I -.1834712  .0000752  1.9090 0.0609  I     3.888    1.982    -1.223     .868   .163000   .540000  -.1852000      .000      .000  
+83 529 45483.00 I   .170895  .001827   .525249  .000960  I -.1853193  .0000752  1.7978 0.0457  I     3.710    1.982    -1.360     .868   .166000   .538000  -.1871000      .000      .000  
+83 530 45484.00 I   .174469  .001582   .522836  .001735  I -.1870888  .0000520  1.7525 0.1181  I     3.515    1.982    -1.429     .868   .170000   .536000  -.1889000      .000      .000  
+83 531 45485.00 I   .177966  .001582   .520342  .001735  I -.1888461  .0002240  1.7725 0.1155  I     3.361    1.982    -1.439     .868   .174000   .534000  -.1906000      .000      .000  
+83 6 1 45486.00 I   .181388  .001512   .517771  .002038  I -.1906526  .0002251  1.8488 0.1588  I     3.238    1.982    -1.472     .868   .177000   .532000  -.1924000      .000      .000  
+83 6 2 45487.00 I   .184737  .001512   .515127  .002038  I -.1925570  .0002251  1.9651 0.1592  I     3.174    1.982    -1.561     .868   .181000   .529000  -.1942000      .000      .000  
+83 6 3 45488.00 I   .188016  .001512   .512417  .002038  I -.1945898  .0002251  2.1024 0.1592  I     3.220    1.982    -1.625     .868   .184000   .527000  -.1962000      .000      .000  
+83 6 4 45489.00 I   .191228  .001512   .509648  .002038  I -.1967628  .0002251  2.2425 0.1966  I     3.258    1.982    -1.583     .868   .188000   .525000  -.1984000      .000      .000  
+83 6 5 45490.00 I   .194379  .001406   .506829  .001455  I -.1990697  .0003223  2.3671 0.1787  I     2.994    1.982    -1.463     .868   .191000   .522000  -.2007000      .000      .000  
+83 6 6 45491.00 I   .197476  .001406   .503969  .001455  I -.2014855  .0002775  2.4570 0.2532  I     2.308    1.982    -1.346     .868   .195000   .520000  -.2032000      .000      .000  
+83 6 7 45492.00 I   .200525  .000482   .501077  .001388  I -.2039651  .0003907  2.4910 0.2022  I     1.481    1.982    -1.231     .868   .198000   .517000  -.2058000      .000      .000  
+83 6 8 45493.00 I   .203534  .003437   .498163  .001262  I -.2064431  .0002943  2.4516 0.2446  I      .902    1.982    -1.065     .868   .202000   .515000  -.2083000      .000      .000  
+83 6 9 45494.00 I   .206511  .003437   .495238  .001262  I -.2088431  .0002943  2.3364 0.2081  I      .616    1.982     -.892     .868   .205000   .512000  -.2107000      .000      .000  
+83 610 45495.00 I   .209463  .003437   .492312  .001262  I -.2110986  .0002943  2.1692 0.1495  I      .368    1.982     -.857     .868   .208000   .509000  -.2129000      .000      .000  
+83 611 45496.00 I   .212398  .002297   .489397  .002117  I -.2131805  .0000532  1.9990 0.1495  I      .041    1.982    -1.014     .868   .212000   .506000  -.2150000      .000      .000  
+83 612 45497.00 I   .215316  .002297   .486502  .002117  I -.2151148  .0000532  1.8834 0.0376  I     -.175    1.982    -1.218     .868   .215000   .503000  -.2169000      .000      .000  
+83 613 45498.00 I   .218216  .002297   .483639  .002117  I -.2169799  .0000532  1.8654 0.0399  I     -.095    1.982    -1.275     .868   .219000   .500000  -.2188000      .000      .000  
+83 614 45499.00 I   .221098  .000931   .480815  .001173  I -.2188808  .0000596  1.9529 0.0399  I      .171    1.982    -1.159     .868   .222000   .497000  -.2206000      .000      .000  
+83 615 45500.00 I   .223959  .000931   .478037  .001173  I -.2209105  .0000596  2.1150 0.0387  I      .351    1.982     -.995     .868   .225000   .494000  -.2226000      .000      .000  
+83 616 45501.00 I   .226807  .000896   .475297  .000881  I -.2231172  .0000494  2.2965 0.0389  I      .322    1.982     -.882     .868   .228000   .490000  -.2247000      .000      .000  
+83 617 45502.00 I   .229649  .000766   .472583  .000824  I -.2254904  .0000500  2.4386 0.0372  I      .179    1.982     -.795     .868   .232000   .487000  -.2270000      .000      .000  
+83 618 45503.00 I   .232469  .000690   .469865  .000451  I -.2279672  .0000557  2.4989 0.0374  I      .052    1.982     -.673     .868   .235000   .483000  -.2294000      .000      .000  
+83 619 45504.00 I   .235245  .000690   .467113  .000451  I -.2304555  .0000557  2.4611 0.0510  I     -.072    1.982     -.500     .868   .238000   .480000  -.2318000      .000      .000  
+83 620 45505.00 I   .237965  .001422   .464296  .001535  I -.2328591  .0000855  2.3326 0.0510  I     -.322    1.982     -.303     .868   .241000   .476000  -.2340000      .000      .000  
+83 621 45506.00 I   .240656  .001422   .461383  .001535  I -.2350984  .0000855  2.1373 0.0611  I     -.773    1.982     -.093     .868   .244000   .473000  -.2361000      .000      .000  
+83 622 45507.00 I   .243352  .001795   .458346  .001746  I -.2371218  .0000873  1.9063 0.0611  I    -1.306    1.982      .107     .868   .246000   .469000  -.2380000      .000      .000  
+83 623 45508.00 I   .246079  .001795   .455162  .001746  I -.2389102  .0000873  1.6730 0.0617  I    -1.693    1.982      .232     .868   .249000   .466000  -.2397000      .000      .000  
+83 624 45509.00 I   .248790  .001795   .451851  .001746  I -.2404772  .0000873  1.4677 0.0617  I    -1.824    1.982      .204     .868   .252000   .462000  -.2411000      .000      .000  
+83 625 45510.00 I   .251403  .001795   .448455  .001746  I -.2418619  .0000873  1.3111 0.0539  I    -1.806    1.982      .051     .868   .255000   .458000  -.2424000      .000      .000  
+83 626 45511.00 I   .253904  .001314   .444994  .000863  I -.2431196  .0000633  1.2151 0.0539  I    -1.807    1.982     -.077     .868   .257000   .455000  -.2435000      .000      .000  
+83 627 45512.00 I   .256323  .001314   .441473  .000863  I -.2443137  .0000633  1.1837 0.0554  I    -1.913    1.982     -.039     .868   .260000   .451000  -.2446000      .000      .000  
+83 628 45513.00 I   .258689  .000409   .437896  .000709  I -.2455067  .0000910  1.2112 0.0554  I    -2.152    1.982      .137     .868   .262000   .448000  -.2458000      .000      .000  
+83 629 45514.00 I   .261034  .000409   .434269  .000709  I -.2467514  .0000910  1.2846 0.0643  I    -2.517    1.982      .286     .868   .265000   .444000  -.2470000      .000      .000  
+83 630 45515.00 I   .263368  .000409   .430601  .000709  I -.2480856  .0000910  1.3870 0.0643  I    -2.883    1.982      .325     .868   .267000   .440000  -.2483000      .000      .000  
+83 7 1 45516.00 I   .265685  .000409   .426907  .000709  I  .7504719  .0000910  1.4976 0.0662  I    -3.047    1.982      .353     .868   .269000   .436000   .7503000      .000      .000  
+83 7 2 45517.00 I   .267977  .000035   .423195  .000953  I  .7489233  .0000961  1.5959 0.0662  I    -3.031    1.982      .491     .868   .272000   .433000   .7487000      .000      .000  
+83 7 3 45518.00 I   .270239  .000035   .419450  .000953  I  .7472900  .0000961  1.6646 0.0788  I    -3.200    1.982      .682     .868   .274000   .429000   .7470000      .000      .000  
+83 7 4 45519.00 I   .272465  .000302   .415657  .001360  I  .7456089  .0001250  1.6889 0.0788  I    -3.864    1.982      .772     .868   .276000   .425000   .7453000      .000      .000  
+83 7 5 45520.00 I   .274650  .000302   .411797  .001360  I  .7439316  .0001250  1.6549 0.0911  I    -4.842    1.982      .749     .868   .278000   .421000   .7436000      .000      .000  
+83 7 6 45521.00 I   .276789  .000302   .407855  .001360  I  .7423215  .0001325  1.5538 0.0911  I    -5.649    1.982      .771     .868   .280000   .417000   .7419000      .000      .000  
+83 7 7 45522.00 I   .278877  .000302   .403817  .001360  I  .7408431  .0001325  1.3956 0.0937  I    -6.088    1.982      .904     .868   .283000   .413000   .7403000      .000      .000  
+83 7 8 45523.00 I   .280924  .000302   .399698  .001360  I  .7395374  .0001325  1.2163 0.0937  I    -6.368    1.982      .986     .868   .285000   .409000   .7389000      .000      .000  
+83 7 9 45524.00 I   .282943  .000302   .395528  .001360  I  .7383994  .0001325  1.0705 0.0814  I    -6.657    1.982      .846     .868   .287000   .405000   .7376000      .000      .000  
+83 710 45525.00 I   .284950  .000235   .391334  .001169  I  .7373669  .0000947  1.0134 0.0814  I    -6.806    1.982      .570     .868   .289000   .401000   .7365000      .000      .000  
+83 711 45526.00 I   .286959  .000235   .387145  .001169  I  .7363329  .0000947  1.0754 0.0630  I    -6.648    1.982      .403     .868   .291000   .397000   .7353000      .000      .000  
+83 712 45527.00 I   .288985  .000235   .382991  .001169  I  .7351809  .0000832  1.2431 0.0883  I    -6.338    1.982      .433     .868   .294000   .392000   .7340000      .000      .000  
+83 713 45528.00 I   .291042  .000467   .378900  .000847  I  .7338288  .0001491  1.4644 0.1302  I    -6.223    1.982      .515     .868   .296000   .388000   .7325000      .000      .000  
+83 714 45529.00 I   .293139  .000467   .374884  .000847  I  .7322570  .0002468  1.6708 0.1455  I    -6.469    1.982      .521     .868   .298000   .384000   .7309000      .000      .000  
+83 715 45530.00 I   .295275  .000467   .370936  .000847  I  .7305113  .0002498  1.8047 0.2106  I    -6.938    1.982      .507     .868   .300000   .380000   .7290000      .000      .000  
+83 716 45531.00 I   .297449  .000618   .367044  .000258  I  .7286812  .0003414  1.8376 0.1846  I    -7.368    1.982      .577     .868   .302000   .376000   .7271000      .000      .000  
+83 717 45532.00 I   .299642  .000535   .363202  .000284  I  .7268689  .0002718  1.7720 0.2182  I    -7.596    1.982      .692     .868   .305000   .371000   .7253000      .000      .000  
+83 718 45533.00 I   .301814  .000535   .359408  .000284  I  .7251607  .0002718  1.6360 0.2140  I    -7.668    1.982      .726     .868   .307000   .367000   .7236000      .000      .000  
+83 719 45534.00 I   .303922  .000437   .355660  .000307  I  .7236072  .0003305  1.4691 0.1573  I    -7.777    1.982      .651     .868   .309000   .363000   .7221000      .000      .000  
+83 720 45535.00 I   .305929  .000410   .351954  .000268  I  .7222224  .0001583  1.3025 0.1820  I    -8.041    1.982      .552     .868   .311000   .359000   .7208000      .000      .000  
+83 721 45536.00 I   .307817  .000410   .348280  .000268  I  .7209944  .0001527  1.1592 0.1014  I    -8.338    1.982      .465     .868   .313000   .354000   .7197000      .000      .000  
+83 722 45537.00 I   .309571  .000347   .344626  .000305  I  .7198906  .0001267  1.0562 0.1278  I    -8.428    1.982      .317     .868   .315000   .350000   .7188000      .000      .000  
+83 723 45538.00 I   .311184  .000291   .340978  .000304  I  .7188644  .0002050  1.0061 0.1456  I    -8.230    1.982      .073     .868   .317000   .345000   .7179000      .000      .000  
+83 724 45539.00 I   .312662  .000291   .337319  .000304  I  .7178588  .0002621  1.0150 0.1515  I    -7.898     .184     -.142     .868   .319000   .341000   .7171000      .000      .000  
+83 725 45540.00 I   .314018  .000305   .333629  .000322  I  .7168157  .0002231  1.0801 0.1836  I    -7.626     .184     -.156     .868   .320000   .337000   .7163000      .000      .000  
+83 726 45541.00 I   .315265  .000260   .329890  .000362  I  .7156832  .0002572  1.1915 0.1702  I    -7.490     .184      .029     .868   .322000   .332000   .7153000      .000      .000  
+83 727 45542.00 I   .316426  .000260   .326094  .000362  I  .7144231  .0002572  1.3317 0.1872  I    -7.454     .184      .215     .868   .323000   .328000   .7143000      .000      .000  
+83 728 45543.00 I   .317524  .000270   .322234  .000897  I  .7130171  .0002720  1.4799 0.1640  I    -7.386     .184      .246     .868   .325000   .323000   .7131000      .000      .000  
+83 729 45544.00 I   .318573  .000270   .318298  .000897  I  .7114668  .0002035  1.6175 0.1486  I    -7.111     .184      .186     .868   .326000   .319000   .7117000      .000      .000  
+83 730 45545.00 I   .319575  .000270   .314267  .000897  I  .7097910  .0001200  1.7284 0.1125  I    -6.616    1.982      .188     .868   .327000   .315000   .7101000      .000      .000  
+83 731 45546.00 I   .320531  .000247   .310123  .000903  I  .7080234  .0000962  1.7990 0.0883  I    -6.190    1.982      .250     .868   .328000   .311000   .7085000      .000      .000  
+83 8 1 45547.00 I   .321434  .000247   .305872  .000903  I  .7062103  .0001297  1.8178 0.0807  I    -6.177    1.982      .209     .868   .328000   .306000   .7067000      .000      .000  
+83 8 2 45548.00 I   .322273  .000247   .301533  .000903  I  .7044070  .0001297  1.7790 0.0800  I    -6.542    1.982     -.001     .868   .329000   .302000   .7050000      .000      .000  
+83 8 3 45549.00 I   .323036  .000945   .297129  .000334  I  .7026705  .0000937  1.6856 0.0800  I    -6.882    1.982     -.229     .868   .330000   .298000   .7033000      .000      .000  
+83 8 4 45550.00 I   .323703  .000945   .292676  .000334  I  .7010479  .0000937  1.5559 0.0663  I    -6.892    1.982     -.322     .868   .331000   .294000   .7017000      .000      .000  
+83 8 5 45551.00 I   .324245  .000945   .288185  .000334  I  .6995577  .0000937  1.4293 0.0792  I    -6.628    1.982     -.342     .868   .331000   .290000   .7003000      .000      .000  
+83 8 6 45552.00 I   .324630  .001304   .283662  .000274  I  .6981705  .0001278  1.3592 0.0572  I    -6.264    1.982     -.443     .868   .332000   .285000   .6989000      .000      .000  
+83 8 7 45553.00 I   .324867  .001395   .279123  .000493  I  .6968051  .0000656  1.3916 0.0718  I    -5.827     .210     -.602     .868   .332000   .281000   .6975000      .000      .000  
+83 8 8 45554.00 I   .325008  .001395   .274583  .000493  I  .6953489  .0000656  1.5391 0.0913  I    -5.313     .210     -.642     .868   .333000   .277000   .6960000      .000      .000  
+83 8 9 45555.00 I   .325109  .001481   .270061  .000642  I  .6936998  .0001704  1.7679 0.0707  I    -4.876     .210     -.522     .868   .333000   .273000   .6943000      .000      .000  
+83 810 45556.00 I   .325220  .001076   .265574  .000463  I  .6918090  .0001252  2.0091 0.1057  I    -4.724     .210     -.418     .868   .333000   .269000   .6923000      .000      .000  
+83 811 45557.00 I   .325345  .001076   .261129  .000463  I  .6897026  .0001252  2.1874 0.0885  I    -4.903     .210     -.461     .868   .333000   .264000   .6901000      .000      .000  
+83 812 45558.00 I   .325442  .001076   .256722  .000463  I  .6874726  .0001252  2.2505 0.0846  I    -5.306     .210     -.527     .868   .333000   .260000   .6878000      .000      .000  
+83 813 45559.00 I   .325463  .002327   .252350  .000924  I  .6852440  .0001139  2.1865 0.0846  I    -5.779    1.982     -.436     .868   .333000   .256000   .6854000      .000      .000  
+83 814 45560.00 I   .325347  .002327   .248012  .000924  I  .6831328  .0001139  2.0231 0.0610  I    -6.162    1.982     -.247     .868   .333000   .252000   .6832000      .000      .000  
+83 815 45561.00 I   .325027  .002287   .243710  .001216  I  .6812146  .0000435  1.8094 0.0607  I    -6.358    1.982     -.203     .868   .332000   .248000   .6812000      .000      .000  
+83 816 45562.00 I   .324439  .002790   .239444  .001487  I  .6795146  .0000421  1.5940 0.0303  I    -6.459    1.982     -.398     .868   .332000   .243000   .6794000      .000      .000  
+83 817 45563.00 I   .323570  .002790   .235203  .001487  I  .6780156  .0000421  1.4115 0.0512  I    -6.683    1.982     -.653     .868   .331000   .239000   .6778000      .000      .000  
+83 818 45564.00 I   .322552  .002285   .230949  .001487  I  .6766742  .0000933  1.2811 0.1399  I    -7.100    1.982     -.762     .868   .331000   .235000   .6764000      .000      .000  
+83 819 45565.00 I   .321508  .001576   .226648  .001653  I  .6754331  .0002767  1.2117 0.1460  I    -7.513    1.982     -.734     .868   .330000   .231000   .6751000      .000      .000  
+83 820 45566.00 I   .320461  .001576   .222306  .001653  I  .6742298  .0002767  1.2052 0.1973  I    -7.732    1.982     -.703     .868   .330000   .227000   .6739000      .000      .000  
+83 821 45567.00 I   .319418  .000555   .217934  .002690  I  .6730031  .0002814  1.2576 0.1973  I    -7.819    1.982     -.687     .868   .329000   .222000   .6726000      .000      .000  
+83 822 45568.00 I   .318373  .000555   .213545  .002690  I  .6716981  .0002814  1.3596 0.1990  I    -7.973    1.982     -.566     .868   .329000   .218000   .6713000      .000      .000  
+83 823 45569.00 I   .317312  .000555   .209152  .002690  I  .6702728  .0002814  1.4951 0.1725  I    -8.275    1.982     -.282     .868   .328000   .214000   .6698000      .000      .000  
+83 824 45570.00 I   .316217  .001200   .204768  .000753  I  .6687035  .0001995  1.6439 0.1481  I    -8.654    1.982      .039     .868   .327000   .210000   .6682000      .000      .000  
+83 825 45571.00 I   .315057  .001200   .200403  .000753  I  .6669878  .0000927  1.7845 0.1188  I    -9.026    1.982      .229     .868   .326000   .206000   .6665000      .000      .000  
+83 826 45572.00 I   .313769  .001200   .196072  .000753  I  .6651441  .0001292  1.8965 0.1000  I    -9.346    1.982      .270     .868   .324000   .201000   .6646000      .000      .000  
+83 827 45573.00 I   .312292  .001538   .191785  .000753  I  .6632099  .0001772  1.9632 0.0924  I    -9.614    1.982      .285     .868   .323000   .197000   .6627000      .000      .000  
+83 828 45574.00 I   .310611  .001111   .187543  .001035  I  .6612365  .0001320  1.9736 0.1105  I    -9.938    1.982      .341     .868   .322000   .193000   .6608000      .000      .000  
+83 829 45575.00 I   .308761  .001111   .183334  .001035  I  .6592822  .0001320  1.9256 0.0990  I   -10.500    1.982      .368     .868   .320000   .189000   .6590000      .000      .000  
+83 830 45576.00 I   .306780  .000322   .179145  .001254  I  .6574024  .0001477  1.8263 0.0857  I   -11.346    1.982      .286     .868   .319000   .185000   .6572000      .000      .000  
+83 831 45577.00 I   .304707  .000572   .174963  .001020  I  .6556410  .0001094  1.6927 0.0802  I   -12.265    1.982      .144     .868   .317000   .180000   .6556000      .000      .000  
+83 9 1 45578.00 I   .302568  .000572   .170795  .001020  I  .6540182  .0000624  1.5556 0.0601  I   -12.966    1.982      .058     .868   .316000   .176000   .6541000      .000      .000  
+83 9 2 45579.00 I   .300382  .000572   .166667  .001020  I  .6525168  .0000499  1.4575 0.0381  I   -13.323    1.982      .065     .868   .314000   .172000   .6528000      .000      .000  
+83 9 3 45580.00 I   .298158  .000742   .162606  .000711  I  .6510751  .0000436  1.4436 0.0778  I   -13.395    1.982      .129     .868   .312000   .168000   .6514000      .000      .000  
+83 9 4 45581.00 I   .295874  .001655   .158623  .001131  I  .6495919  .0001473  1.5427 0.0778  I   -13.328    1.982      .271     .868   .310000   .164000   .6501000      .000      .000  
+83 9 5 45582.00 I   .293495  .001655   .154721  .001131  I  .6479549  .0001494  1.7462 0.1130  I   -13.320    1.982      .558     .868   .308000   .159000   .6486000      .000      .000  
+83 9 6 45583.00 I   .290991  .001770   .150908  .001023  I  .6460815  .0001713  2.0036 0.1148  I   -13.546    1.982      .923     .868   .306000   .155000   .6468000      .000      .000  
+83 9 7 45584.00 I   .288340  .001622   .147183  .001320  I  .6439545  .0001744  2.2405 0.1222  I   -14.009    1.982     1.136     .868   .304000   .151000   .6448000      .000      .000  
+83 9 8 45585.00 I   .285620  .001622   .143517  .001320  I  .6416305  .0001744  2.3873 0.1233  I   -14.524    1.982     1.062     .868   .302000   .147000   .6425000      .000      .000  
+83 9 9 45586.00 I   .282932  .001622   .139868  .001320  I  .6392241  .0001744  2.4015 0.1177  I   -14.948    1.982      .868     .868   .300000   .143000   .6402000      .000      .000  
+83 910 45587.00 I   .280329  .001218   .136199  .001260  I  .6368703  .0001580  2.2872 0.1170  I   -15.305    1.982      .822     .868   .297000   .139000   .6380000      .000      .000  
+83 911 45588.00 I   .277835  .001218   .132485  .001260  I  .6346791  .0001561  2.0847 0.1016  I   -15.634    1.982      .936     .868   .295000   .135000   .6359000      .000      .000  
+83 912 45589.00 I   .275415  .000920   .128738  .001259  I  .6327116  .0001277  1.8499 0.0781  I   -15.836    1.982      .950     .868   .293000   .131000   .6340000      .000      .000  
+83 913 45590.00 I   .273024  .000249   .124983  .000181  I  .6309726  .0000052  1.6350 0.0795  I   -15.828    1.982      .695     .868   .290000   .127000   .6323000      .000      .000  
+83 914 45591.00 I   .270614  .000321   .121243  .000478  I  .6294242  .0000946  1.4724 0.0474  I   -15.706    1.982      .335     .868   .288000   .123000   .6308000      .000      .000  
+83 915 45592.00 I   .268140  .000321   .117541  .000478  I  .6280058  .0000946  1.3758 0.0669  I   -15.598    1.982      .157     .868   .285000   .120000   .6295000      .000      .000  
+83 916 45593.00 I   .265569  .000321   .113896  .000478  I  .6266502  .0000946  1.3463 0.0714  I   -15.453    1.982      .226     .868   .283000   .116000   .6282000      .000      .000  
+83 917 45594.00 I   .262872  .000321   .110324  .000478  I  .6252925  .0001069  1.3790 0.0724  I   -15.133    1.982      .358     .868   .280000   .112000   .6268000      .000      .000  
+83 918 45595.00 I   .260033  .000612   .106829  .000472  I  .6238741  .0001097  1.4662 0.0766  I   -14.655    1.982      .394     .868   .277000   .109000   .6254000      .000      .000  
+83 919 45596.00 I   .257047  .000612   .103408  .000472  I  .6223466  .0001097  1.5940 0.0637  I   -14.168    1.982      .371     .868   .274000   .105000   .6238000      .000      .000  
+83 920 45597.00 I   .253908  .000630   .100056  .000330  I  .6206790  .0000647  1.7432 0.1082  I   -13.710    1.982      .412     .868   .271000   .102000   .6221000      .000      .000  
+83 921 45598.00 I   .250619  .000630   .096769  .000330  I  .6188601  .0001866  1.8929 0.0987  I   -13.188     .720      .531     .234   .268000   .098000   .6202000      .000      .000  
+83 922 45599.00 I   .247218  .000630   .093546  .000330  I  .6169004  .0001866  2.0212 0.1301  I   -12.558     .720      .618     .234   .265000   .095000   .6181000      .000      .000  
+83 923 45600.00 I   .243752  .000630   .090388  .000330  I  .6148318  .0001814  2.1074 0.1305  I   -11.888     .720      .566     .234   .262000   .092000   .6159000      .000      .000  
+83 924 45601.00 I   .240255  .000546   .087290  .000334  I  .6127044  .0001825  2.1373 0.1287  I   -11.219     .720      .383     .234   .259000   .089000   .6136000      .000      .000  
+83 925 45602.00 I   .236747  .000546   .084238  .000334  I  .6105772  .0001825  2.1073 0.1346  I   -10.540     .720      .159     .234   .255000   .086000   .6112000      .000      .000  
+83 926 45603.00 I   .233246  .000564   .081217  .000958  I  .6085069  .0001978  2.0260 0.1025  I    -9.876     .722     -.035     .270   .252000   .083000   .6090000      .000      .000  
+83 927 45604.00 I   .229770  .000564   .078212  .000958  I  .6065357  .0000932  1.9133 0.1093  I    -9.313     .724     -.193     .301   .249000   .080000   .6069000      .000      .000  
+83 928 45605.00 I   .226324  .000564   .075212  .000958  I  .6046808  .0000930  1.7990 0.0610  I    -8.870     .724     -.335     .301   .245000   .077000   .6049000      .000      .000  
+83 929 45606.00 I   .222887  .000486   .072205  .000942  I  .6029261  .0000787  1.7195 0.0638  I    -8.422     .724     -.457     .301   .242000   .074000   .6031000      .000      .000  
+83 930 45607.00 I   .219443  .000387   .069196  .001148  I  .6012183  .0000874  1.7106 0.0588  I    -7.793     .724     -.523     .301   .238000   .072000   .6012000      .000      .000  
+8310 1 45608.00 I   .215995  .000387   .066215  .001148  I  .5994726  .0000874  1.7987 0.0493  I    -6.930     .724     -.475     .301   .235000   .069000   .5995000      .000      .000  
+8310 2 45609.00 I   .212552  .000191   .063299  .000780  I  .5975868  .0000456  1.9893 0.0488  I    -5.999    1.982     -.251     .868   .231000   .066000   .5976000      .000      .000  
+8310 3 45610.00 I   .209116  .000191   .060476  .000780  I  .5954696  .0000433  2.2533 0.0336  I    -5.303    1.982      .157     .868   .227000   .064000   .5955000      .000      .000  
+8310 4 45611.00 I   .205677  .000191   .057756  .000780  I  .5930760  .0000494  2.5298 0.0579  I    -5.058    1.982      .620     .868   .223000   .061000   .5931000      .000      .000  
+8310 5 45612.00 I   .202226  .000161   .055143  .000678  I  .5904304  .0001075  2.7449 0.0592  I    -5.177    1.982      .900     .868   .219000   .059000   .5905000      .000      .000  
+8310 6 45613.00 I   .198751  .000161   .052641  .000678  I  .5876271  .0001075  2.8371 0.0760  I    -5.331    1.982      .866     .868   .215000   .056000   .5878000      .000      .000  
+8310 7 45614.00 I   .195241  .000161   .050236  .000678  I  .5848052  .0001075  2.7822 0.0892  I    -5.286    1.982      .651     .868   .211000   .054000   .5851000      .000      .000  
+8310 8 45615.00 I   .191681  .000225   .047915  .000728  I  .5821044  .0001423  2.6020 0.0755  I    -5.122    1.982      .527     .868   .207000   .052000   .5825000      .000      .000  
+8310 9 45616.00 I   .188069  .000162   .045677  .000709  I  .5796249  .0001060  2.3506 0.0880  I    -5.047    1.982      .595     .868   .202000   .050000   .5800000      .000      .000  
+831010 45617.00 I   .184406  .000162   .043530  .000709  I  .5774064  .0001036  2.0897 0.0723  I    -5.099    1.982      .687     .868   .198000   .047000   .5779000      .000      .000  
+831011 45618.00 I   .180694  .000043   .041484  .000689  I  .5754327  .0000983  1.8674 0.0858  I    -5.137    1.982      .623     .124   .193000   .045000   .5760000      .000      .000  
+831012 45619.00 I   .176934  .000201   .039547  .000579  I  .5736494  .0001369  1.7118 0.0843  I    -5.069    1.982      .474     .124   .189000   .043000   .5742000      .000      .000  
+831013 45620.00 I   .173127  .000201   .037715  .000579  I  .5719840  .0001369  1.6317 0.0968  I    -4.933    1.982      .464     .124   .185000   .041000   .5726000      .000      .000  
+831014 45621.00 I   .169272  .000201   .035973  .000579  I  .5703624  .0001369  1.6225 0.1072  I    -4.785    1.982      .667     .124   .180000   .039000   .5710000      .000      .000  
+831015 45622.00 I   .165367  .000498   .034308  .000537  I  .5687185  .0001650  1.6748 0.1072  I    -4.643    1.982      .916     .124   .176000   .038000   .5693000      .000      .000  
+831016 45623.00 I   .161397  .000498   .032717  .000537  I  .5669967  .0001650  1.7755 0.1126  I    -4.559    1.982     1.020     .124   .171000   .036000   .5675000      .000      .000  
+831017 45624.00 I   .157343  .000498   .031201  .000537  I  .5651578  .0001534  1.9054 0.1116  I    -4.605    1.982      .984     .868   .167000   .034000   .5656000      .000      .000  
+831018 45625.00 I   .153187  .000483   .029760  .001069  I  .5631825  .0001502  2.0454 0.1093  I    -4.766    1.982      .977     .868   .163000   .033000   .5635000      .000      .000  
+831019 45626.00 I   .148934  .000483   .028382  .001069  I  .5610699  .0001556  2.1768 0.1004  I    -4.944    1.982     1.123     .868   .158000   .031000   .5612000      .000      .000  
+831020 45627.00 I   .144606  .000409   .027048  .000953  I  .5588385  .0001332  2.2796 0.1016  I    -5.109    1.982     1.374     .868   .154000   .030000   .5588000      .000      .000  
+831021 45628.00 I   .140224  .000206   .025746  .001083  I  .5565266  .0001306  2.3347 0.0933  I    -5.325    1.982     1.562     .868   .149000   .028000   .5563000      .000      .000  
+831022 45629.00 I   .135800  .000206   .024495  .001083  I  .5541889  .0001306  2.3303 0.0923  I    -5.613    1.982     1.561     .868   .145000   .027000   .5537000      .000      .000  
+831023 45630.00 I   .131340  .000206   .023320  .001083  I  .5518854  .0001306  2.2677 0.1070  I    -5.883    1.982     1.395     .868   .141000   .026000   .5513000      .000      .000  
+831024 45631.00 I   .126852  .000228   .022244  .000758  I  .5496675  .0001696  2.1631 0.0890  I    -6.047    1.982     1.211     .868   .136000   .025000   .5489000      .000      .000  
+831025 45632.00 I   .122332  .000249   .021273  .000737  I  .5475627  .0001208  2.0480 0.1110  I    -6.141    1.982     1.135     .868   .132000   .024000   .5467000      .000      .000  
+831026 45633.00 I   .117777  .000242   .020411  .000617  I  .5455610  .0001434  1.9645 0.0937  I    -6.246    1.982     1.169     .868   .127000   .023000   .5447000      .000      .000  
+831027 45634.00 I   .113183  .000242   .019661  .000617  I  .5436110  .0001434  1.9493 0.1014  I    -6.334    1.982     1.230     .868   .123000   .022000   .5427000      .000      .000  
+831028 45635.00 I   .108558  .000242   .019010  .000617  I  .5416323  .0001434  2.0245 0.1014  I    -6.275    1.982     1.268     .868   .118000   .021000   .5407000      .000      .000  
+831029 45636.00 I   .103917  .000242   .018434  .000617  I  .5395291  .0001434  2.1983 0.0897  I    -5.994    1.982     1.331     .868   .114000   .020000   .5386000      .000      .000  
+831030 45637.00 I   .099285  .000685   .017915  .000351  I  .5372094  .0001079  2.4511 0.0893  I    -5.605    1.982     1.503     .868   .109000   .020000   .5364000      .000      .000  
+831031 45638.00 I   .094682  .000685   .017444  .000351  I  .5346166  .0001064  2.7350 0.0681  I    -5.371    1.982     1.804     .868   .105000   .019000   .5339000      .000      .000  
+8311 1 45639.00 I   .090127  .001256   .017010  .000275  I  .5317491  .0000832  2.9898 0.0913  I    -5.491    1.982     2.140     .868   .100000   .018000   .5311000      .000      .000  
+8311 2 45640.00 I   .085635  .001256   .016605  .000275  I  .5286668  .0001485  3.1551 0.0910  I    -5.903    1.982     2.359     .868   .095000   .018000   .5282000      .000      .000  
+8311 3 45641.00 I   .081180  .001256   .016226  .000275  I  .5254822  .0001618  3.1904 0.1098  I    -6.312    1.982     2.371     .868   .091000   .018000   .5252000      .000      .000  
+8311 4 45642.00 I   .076707  .001256   .015875  .000275  I  .5223304  .0001618  3.0924 0.1374  I    -6.449    1.982     2.236     .868   .086000   .017000   .5222000      .000      .000  
+8311 5 45643.00 I   .072188  .001035   .015555  .000140  I  .5193312  .0002222  2.8928 0.1374  I    -6.303    1.982     2.120     .868   .082000   .017000   .5194000      .000      .000  
+8311 6 45644.00 I   .067650  .001035   .015274  .000140  I  .5165611  .0002222  2.6443 0.1574  I    -6.073    1.982     2.122     .868   .077000   .017000   .5167000      .000      .000  
+8311 7 45645.00 I   .063133  .000605   .015041  .000302  I  .5140408  .0002230  2.4013 0.1451  I    -5.911    1.982     2.184     .868   .073000   .017000   .5143000      .000      .000  
+8311 8 45646.00 I   .058673  .000605   .014863  .000302  I  .5117429  .0001868  2.2049 0.1419  I    -5.773    1.982     2.201     .868   .068000   .017000   .5121000      .000      .000  
+8311 9 45647.00 I   .054298  .000605   .014750  .000302  I  .5096069  .0001755  2.0806 0.1222  I    -5.517    1.982     2.193     .868   .064000   .018000   .5099000      .000      .000  
+831110 45648.00 I   .050016  .000631   .014711  .000904  I  .5075559  .0001576  2.0338 0.1000  I    -5.078    1.982     2.275     .868   .059000   .018000   .5079000      .000      .000  
+831111 45649.00 I   .045825  .000703   .014760  .001107  I  .5055162  .0000959  2.0562 0.0922  I    -4.537    1.982     2.479     .868   .055000   .018000   .5058000      .000      .000  
+831112 45650.00 I   .041690  .000703   .014919  .001107  I  .5034252  .0000959  2.1334 0.0751  I    -4.027    1.982     2.667     .868   .051000   .018000   .5037000      .000      .000  
+831113 45651.00 I   .037570  .000679   .015212  .001507  I  .5012379  .0001155  2.2452 0.0740  I    -3.631    1.982     2.698     .868   .046000   .018000   .5015000      .000      .000  
+831114 45652.00 I   .033436  .000724   .015651  .001011  I  .4989301  .0001128  2.3712 0.0808  I    -3.335    1.982     2.603     .868   .042000   .019000   .4991000      .000      .000  
+831115 45653.00 I   .029287  .000724   .016216  .001011  I  .4964974  .0001129  2.4915 0.0782  I    -3.084     .510     2.560     .284   .037000   .019000   .4966000      .000      .000  
+831116 45654.00 I   .025129  .000652   .016880  .000328  I  .4939555  .0001082  2.5867 0.0779  I    -2.850     .510     2.708     .284   .033000   .019000   .4940000      .000      .000  
+831117 45655.00 I   .020959  .000652   .017618  .000328  I  .4913389  .0001075  2.6373 0.0783  I    -2.642     .510     3.014     .284   .029000   .020000   .4913000      .000      .000  
+831118 45656.00 I   .016746  .000652   .018418  .000328  I  .4887009  .0001133  2.6279 0.0782  I    -2.452     .510     3.303     .284   .025000   .020000   .4887000      .000      .000  
+831119 45657.00 I   .012470  .000652   .019266  .000328  I  .4861057  .0001136  2.5508 0.0654  I    -2.233     .510     3.392     .284   .020000   .021000   .4861000      .000      .000  
+831120 45658.00 I   .008132  .000478   .020152  .000519  I  .4836202  .0000652  2.4109 0.0717  I    -1.918     .670     3.227     .305   .016000   .021000   .4836000      .000      .000  
+831121 45659.00 I   .003743  .000380   .021065  .000573  I  .4812962  .0000876  2.2341 0.0444  I    -1.491     .798     2.924     .325   .012000   .022000   .4812000      .000      .000  
+831122 45660.00 I  -.000682  .000397   .021994  .000661  I  .4791497  .0000604  2.0643 0.0531  I    -1.004     .798     2.688     .325   .008000   .023000   .4790000      .000      .000  
+831123 45661.00 I  -.005128  .000397   .022932  .000661  I  .4771491  .0000599  1.9501 0.0331  I     -.538     .798     2.643     .325   .004000   .024000   .4770000      .000      .000  
+831124 45662.00 I  -.009563  .000397   .023887  .000661  I  .4752188  .0000270  1.9281 0.0367  I     -.136     .798     2.751     .325  -.001000   .024000   .4749000      .000      .000  
+831125 45663.00 I  -.013953  .000397   .024877  .000661  I  .4732586  .0000425  2.0089 0.0258  I      .206     .798     2.886     .325  -.005000   .025000   .4729000      .000      .000  
+831126 45664.00 I  -.018277  .000472   .025911  .000445  I  .4711735  .0000439  2.1723 0.0901  I      .500    1.982     2.984     .868  -.009000   .026000   .4707000      .000      .000  
+831127 45665.00 I  -.022529  .000472   .026991  .000445  I  .4689010  .0001751  2.3749 0.1223  I      .717    1.982     3.070     .868  -.013000   .027000   .4684000      .000      .000  
+831128 45666.00 I  -.026704  .000480   .028118  .000421  I  .4664286  .0002407  2.5628 0.1265  I      .771    1.982     3.192     .868  -.017000   .028000   .4659000      .000      .000  
+831129 45667.00 I  -.030799  .000587   .029291  .000575  I  .4637977  .0001827  2.6839 0.1511  I      .573    1.982     3.349     .868  -.021000   .030000   .4633000      .000      .000  
+831130 45668.00 I  -.034830  .000587   .030504  .000575  I  .4610947  .0001827  2.7034 0.1286  I      .138     .371     3.504     .868  -.025000   .031000   .4606000      .000      .000  
+8312 1 45669.00 I  -.038822  .000587   .031742  .000575  I  .4584273  .0001809  2.6136 0.1806  I     -.374     .371     3.619     .868  -.029000   .032000   .4579000      .000      .000  
+8312 2 45670.00 I  -.042803  .000524   .032994  .000495  I  .4558981  .0003115  2.4319 0.1610  I     -.740     .371     3.696     .868  -.033000   .033000   .4554000      .000      .000  
+8312 3 45671.00 I  -.046774  .000524   .034259  .000495  I  .4535817  .0002663  2.1956 0.2049  I     -.826     .371     3.762     .868  -.036000   .035000   .4531000      .000      .000  
+8312 4 45672.00 I  -.050719  .000524   .035547  .000495  I  .4515093  .0002663  1.9520 0.1881  I     -.682     .371     3.829     .868  -.040000   .036000   .4511000      .000      .000  
+8312 5 45673.00 I  -.054619  .000447   .036867  .000332  I  .4496656  .0002657  1.7448 0.1881  I     -.472     .371     3.872     .868  -.043000   .038000   .4493000      .000      .000  
+8312 6 45674.00 I  -.058443  .000447   .038228  .000332  I  .4479977  .0002657  1.6036 0.1716  I     -.302    1.982     3.875     .868  -.047000   .039000   .4476000      .000      .000  
+8312 7 45675.00 I  -.062153  .000487   .039638  .000873  I  .4464330  .0002172  1.5387 0.1354  I     -.132    1.982     3.890     .868  -.050000   .041000   .4461000      .000      .000  
+8312 8 45676.00 I  -.065717  .000558   .041105  .001068  I  .4448954  .0000522  1.5484 0.1302  I      .122    1.982     3.994     .868  -.054000   .042000   .4446000      .000      .000  
+8312 9 45677.00 I  -.069128  .000558   .042651  .001068  I  .4433152  .0001438  1.6211 0.0765  I      .432    1.982     4.171     .868  -.057000   .044000   .4431000      .000      .000  
+831210 45678.00 I  -.072386  .000558   .044297  .001068  I  .4416385  .0001438  1.7379 0.0987  I      .678    1.982     4.300     .868  -.061000   .045000   .4414000      .000      .000  
+831211 45679.00 I  -.075499  .000546   .046056  .001059  I  .4398322  .0001353  1.8764 0.0987  I      .821    1.982     4.289     .868  -.064000   .047000   .4395000      .000      .000  
+831212 45680.00 I  -.078500  .000546   .047907  .001059  I  .4378858  .0001353  2.0145 0.1043  I      .948    1.982     4.206     .868  -.067000   .049000   .4375000      .000      .000  
+831213 45681.00 I  -.081426  .000484   .049824  .000522  I  .4358100  .0001589  2.1320 0.1045  I     1.109    1.982     4.207     .868  -.070000   .051000   .4354000      .000      .000  
+831214 45682.00 I  -.084310  .000484   .051784  .000522  I  .4336338  .0001592  2.2133 0.0909  I     1.200    1.982     4.376     .868  -.074000   .052000   .4331000      .000      .000  
+831215 45683.00 I  -.087163  .000484   .053773  .000522  I  .4313994  .0000885  2.2466 0.0922  I     1.110    1.982     4.663     .868  -.077000   .054000   .4307000      .000      .000  
+831216 45684.00 I  -.089984  .000484   .055779  .000522  I  .4291600  .0000929  2.2219 0.0668  I      .904    1.982     4.956     .868  -.080000   .056000   .4284000      .000      .000  
+831217 45685.00 I  -.092784  .000988   .057796  .000774  I  .4269767  .0001000  2.1340 0.0682  I      .748    1.982     5.139     .868  -.083000   .058000   .4261000      .000      .000  
+831218 45686.00 I  -.095588  .000988   .059831  .000774  I  .4249091  .0001000  1.9946 0.0667  I      .708    1.982     5.107     .868  -.086000   .060000   .4240000      .000      .000  
+831219 45687.00 I  -.098428  .000964   .061893  .000745  I  .4229931  .0000882  1.8382 0.0664  I      .744    1.982     4.827     .868  -.089000   .062000   .4219000      .000      .000  
+831220 45688.00 I  -.101331  .000964   .063991  .000745  I  .4212216  .0000875  1.7151 0.0621  I      .883     .207     4.420     .234  -.092000   .064000   .4201000      .000      .000  
+831221 45689.00 I  -.104285  .000964   .066120  .000745  I  .4195351  .0000874  1.6754 0.0591  I     1.220     .207     4.106     .234  -.095000   .066000   .4183000      .000      .000  
+831222 45690.00 I  -.107234  .000817   .068258  .000743  I  .4178346  .0000796  1.7443 0.0607  I     1.725     .207     4.012     .234  -.098000   .068000   .4165000      .000      .000  
+831223 45691.00 I  -.110135  .000447   .070392  .000658  I  .4160138  .0000843  1.9115 0.0580  I     2.184     .207     4.053     .234  -.101000   .070000   .4147000      .000      .000  
+831224 45692.00 I  -.112977  .000447   .072525  .000658  I  .4139933  .0000843  2.1338 0.0665  I     2.392     .207     4.045     .234  -.103000   .072000   .4125000      .000      .000  
+831225 45693.00 I  -.115751  .000249   .074659  .000635  I  .4117490  .0001028  2.3478 0.0683  I     2.331     .207     3.903     .234  -.106000   .074000   .4102000      .000      .000  
+831226 45694.00 I  -.118457  .000287   .076816  .000885  I  .4093198  .0001076  2.4954 0.0744  I     2.113    1.982     3.682     .868  -.109000   .076000   .4078000      .000      .000  
+831227 45695.00 I  -.121102  .000287   .079021  .000885  I  .4067917  .0001077  2.5423 0.0835  I     1.810    1.982     3.480     .868  -.112000   .078000   .4053000      .000      .000  
+831228 45696.00 I  -.123689  .000298   .081302  .000891  I  .4042707  .0001278  2.4825 0.0836  I     1.414    1.982     3.336     .868  -.114000   .080000   .4029000      .000      .000  
+831229 45697.00 I  -.126226  .000298   .083687  .000891  I  .4018558  .0001278  2.3352 0.0904  I      .937    1.982     3.242     .868  -.117000   .083000   .4005000      .000      .000  
+831230 45698.00 I  -.128720  .000298   .086194  .000891  I  .3996181  .0001278  2.1346 0.0820  I      .478    1.982     3.184     .868  -.119000   .085000   .3984000      .000      .000  
+831231 45699.00 I  -.131198  .000298   .088822  .000891  I  .3975912  .0001028  1.9204 0.0774  I      .198    1.982     3.150     .868  -.122000   .087000   .3966000      .000      .000  
+84 1 1 45700.00 I  -.133693  .001671   .091556  .000915  I  .3957700  .0000875  1.7282 0.0674  I      .182    1.982     3.101     .868  -.125000   .089000   .3950000      .000      .000  
+84 1 2 45701.00 I  -.136248  .001671   .094364  .000915  I  .3941189  .0000873  1.5837 0.0540  I      .347     .791     2.996     .298  -.127000   .092000   .3937000      .000      .000  
+84 1 3 45702.00 I  -.138908  .001885   .097214  .001002  I  .3925820  .0000633  1.5009 0.0521  I      .515     .791     2.862     .298  -.130000   .091000   .3928000      .000      .000  
+84 1 4 45703.00 I  -.141713  .001538   .100076  .000736  I  .3910960  .0000568  1.4813 0.0425  I      .586     .791     2.816     .298  -.132000   .094000   .3908000      .000      .000  
+84 1 5 45704.00 I  -.144634  .001538   .102933  .000736  I  .3896011  .0000568  1.5168 0.0402  I      .598     .791     2.975     .298  -.135000   .099000   .3900000      .000      .000  
+84 1 6 45705.00 I  -.147567  .001538   .105787  .000736  I  .3880487  .0000568  1.5936 0.0380  I      .606     .791     3.323     .298  -.137000   .102000   .3886000      .000      .000  
+84 1 7 45706.00 I  -.150445  .000712   .108641  .000327  I  .3864062  .0000506  1.6935 0.0362  I      .619     .791     3.691     .298  -.140000   .104000   .3871000      .000      .000  
+84 1 8 45707.00 I  -.153237  .000804   .111497  .000278  I  .3846611  .0000450  1.7951 0.0324  I      .701     .791     3.943     .298  -.142000   .107000   .3854000      .000      .000  
+84 1 9 45708.00 I  -.155921  .000698   .114358  .000261  I  .3828226  .0000406  1.8770 0.0398  I      .998     .111     4.102     .298  -.145000   .110000   .3836000      .000      .000  
+84 110 45709.00 I  -.158475  .000772   .117229  .000521  I  .3809185  .0000657  1.9246 0.0389  I     1.545     .111     4.239     .298  -.147000   .113000   .3816000      .000      .000  
+84 111 45710.00 I  -.160879  .000772   .120116  .000521  I  .3789884  .0000663  1.9273 0.0467  I     2.145     .111     4.321     .298  -.150000   .116000   .3797000      .000      .000  
+84 112 45711.00 I  -.163135  .000834   .123034  .000517  I  .3770820  .0000663  1.8757 0.0459  I     2.564     .111     4.283     .298  -.152000   .119000   .3777000      .000      .000  
+84 113 45712.00 I  -.165270  .000920   .125996  .000535  I  .3752552  .0000636  1.7694 0.0805  I     2.823     .119     4.147     .298  -.154000   .122000   .3757000      .000      .000  
+84 114 45713.00 I  -.167340  .000871   .128998  .000709  I  .3735584  .0001467  1.6176 0.0799  I     3.075     .126     4.000     .298  -.157000   .125000   .3739000      .000      .000  
+84 115 45714.00 I  -.169412  .000871   .132027  .000709  I  .3720280  .0001467  1.4419 0.0947  I     3.274     .126     3.828     .298  -.159000   .128000   .3722000      .000      .000  
+84 116 45715.00 I  -.171529  .000709   .135077  .000742  I  .3706685  .0001198  1.2844 0.0940  I     3.245     .126     3.487     .298  -.161000   .131000   .3707000      .000      .000  
+84 117 45716.00 I  -.173696  .000709   .138154  .000742  I  .3694355  .0001177  1.1979 0.0809  I     3.103     .126     2.931     .298  -.164000   .134000   .3693000      .000      .000  
+84 118 45717.00 I  -.175913  .000035   .141261  .000872  I  .3682349  .0001088  1.2247 0.0801  I     3.236     .126     2.387     .298  -.166000   .138000   .3679000      .000      .000  
+84 119 45718.00 I  -.178181  .000035   .144398  .000872  I  .3669465  .0001088  1.3704 0.0780  I     3.792     .791     2.171     .298  -.168000   .141000   .3664000      .000      .000  
+84 120 45719.00 I  -.180499  .000035   .147546  .000872  I  .3654672  .0001119  1.5968 0.1337  I     4.429     .791     2.340     .298  -.171000   .144000   .3648000      .000      .000  
+84 121 45720.00 I  -.182868  .000035   .150678  .000872  I  .3637489  .0002443  1.8351 0.0879  I     4.676     .791     2.656     .298  -.173000   .147000   .3629000      .000      .000  
+84 122 45721.00 I  -.185285  .000652   .153776  .000605  I  .3618180  .0001355  2.0104 0.1298  I     4.420     .791     2.896     .298  -.175000   .150000   .3609000      .000      .000  
+84 123 45722.00 I  -.187747  .000576   .156841  .000501  I  .3597665  .0000880  2.0706 0.0808  I     3.921     .791     3.067     .298  -.177000   .153000   .3589000      .000      .000  
+84 124 45723.00 I  -.190250  .000624   .159874  .000315  I  .3577188  .0000879  2.0048 0.0622  I     3.467     .791     3.301     .298  -.180000   .157000   .3567000      .000      .000  
+84 125 45724.00 I  -.192791  .000624   .162876  .000315  I  .3557903  .0000879  1.8388 0.0619  I     3.155     .791     3.642     .298  -.182000   .160000   .3548000      .000      .000  
+84 126 45725.00 I  -.195341  .000624   .165856  .000315  I  .3540603  .0000873  1.6153 0.0467  I     2.967     .791     4.032     .298  -.184000   .163000   .3530000      .000      .000  
+84 127 45726.00 I  -.197857  .000603   .168829  .000302  I  .3525635  .0000316  1.3803 0.0476  I     2.892     .791     4.395     .298  -.186000   .166000   .3515000      .000      .000  
+84 128 45727.00 I  -.200310  .000434   .171811  .000261  I  .3512901  .0000382  1.1742 0.0294  I     3.013     .121     4.698     .298  -.189000   .169000   .3501000      .000      .000  
+84 129 45728.00 I  -.202689  .000468   .174810  .000301  I  .3501969  .0000497  1.0227 0.0321  I     3.459     .165     4.876     .298  -.191000   .173000   .3490000      .000      .000  
+84 130 45729.00 I  -.204988  .000474   .177835  .000320  I  .3492225  .0000517  0.9377 0.0352  I     4.264     .165     4.798     .298  -.193000   .176000   .3481000      .000      .000  
+84 131 45730.00 I  -.207201  .000425   .180895  .000889  I  .3482974  .0000499  0.9250 0.0347  I     5.271     .165     4.396     .298  -.195000   .179000   .3472000      .000      .000  
+84 2 1 45731.00 I  -.209302  .000487   .184010  .000856  I  .3473493  .0000464  0.9820 0.0407  I     6.169     .165     3.835     .298  -.197000   .183000   .3464000      .000      .000  
+84 2 2 45732.00 I  -.211263  .000694   .187209  .000841  I  .3463147  .0000642  1.0953 0.0496  I     6.729     .380     3.352     .298  -.200000   .186000   .3457000      .000      .000  
+84 2 3 45733.00 I  -.213058  .000813   .190510  .001115  I  .3451465  .0000876  1.2454 0.0543  I     6.862     .511     3.040     .298  -.202000   .189000   .3446000      .000      .000  
+84 2 4 45734.00 I  -.214671  .000813   .193897  .001115  I  .3438190  .0000876  1.4102 0.0619  I     6.524     .511     2.835     .298  -.204000   .193000   .3434000      .000      .000  
+84 2 5 45735.00 I  -.216102  .000813   .197345  .001115  I  .3423294  .0000876  1.5649 0.0657  I     5.700     .511     2.706     .298  -.206000   .196000   .3420000      .000      .000  
+84 2 6 45736.00 I  -.217398  .000838   .200842  .001302  I  .3407013  .0000980  1.6832 0.0644  I     4.621     .511     2.704     .298  -.208000   .200000   .3404000      .000      .000  
+84 2 7 45737.00 I  -.218614  .000911   .204396  .001220  I  .3389802  .0000943  1.7498 0.0691  I     3.543     .571     2.888     .298  -.210000   .203000   .3387000      .000      .000  
+84 2 8 45738.00 I  -.219801  .000740   .208015  .001808  I  .3372199  .0000975  1.7617 0.0663  I     2.534     .626     3.189     .298  -.212000   .207000   .3370000      .000      .000  
+84 2 9 45739.00 I  -.221010  .000740   .211703  .001808  I  .3354758  .0000933  1.7168 0.0685  I     1.682     .626     3.454     .298  -.214000   .210000   .3354000      .000      .000  
+84 210 45740.00 I  -.222283  .000740   .215418  .001808  I  .3338026  .0000962  1.6232 0.0661  I     1.311     .626     3.656     .298  -.216000   .214000   .3337000      .000      .000  
+84 211 45741.00 I  -.223623  .000791   .219088  .001758  I  .3322386  .0000937  1.5020 0.0625  I     1.545     .626     3.921     .298  -.217000   .217000   .3322000      .000      .000  
+84 212 45742.00 I  -.225009  .000927   .222662  .001651  I  .3307985  .0000798  1.3809 0.0674  I     2.123     .534     4.256     .298  -.219000   .221000   .3307000      .000      .000  
+84 213 45743.00 I  -.226395  .000748   .226137  .001946  I  .3294628  .0000968  1.3017 0.0679  I     2.619     .423     4.401     .298  -.221000   .225000   .3294000      .000      .000  
+84 214 45744.00 I  -.227735  .000913   .229512  .001604  I  .3281652  .0001100  1.3121 0.0773  I     2.911     .423     4.101     .298  -.223000   .228000   .3281000      .000      .000  
+84 215 45745.00 I  -.228996  .000691   .232798  .001413  I  .3267982  .0001206  1.4436 0.0687  I     3.276     .423     3.457     .298  -.224000   .232000   .3268000      .000      .000  
+84 216 45746.00 I  -.230184  .000651   .236043  .001426  I  .3252405  .0000823  1.6876 0.0699  I     3.991     .423     2.879     .298  -.226000   .236000   .3252000      .000      .000  
+84 217 45747.00 I  -.231313  .000291   .239301  .001397  I  .3234042  .0000708  1.9881 0.0677  I     4.812     .315     2.613     .298  -.227000   .239000   .3234000      .000      .000  
+84 218 45748.00 I  -.232399  .000440   .242625  .001108  I  .3212741  .0001076  2.2593 0.0644  I     5.195     .140     2.518     .298  -.229000   .243000   .3213000      .000      .000  
+84 219 45749.00 I  -.233470  .000440   .246041  .001108  I  .3189226  .0001076  2.4195 0.0761  I     4.947     .140     2.357     .298  -.230000   .247000   .3190000      .000      .000  
+84 220 45750.00 I  -.234563  .000440   .249561  .001108  I  .3164862  .0001076  2.4263 0.0656  I     4.377     .140     2.144     .298  -.231000   .250000   .3167000      .000      .000  
+84 221 45751.00 I  -.235703  .000396   .253172  .001015  I  .3141195  .0000752  2.2845 0.0654  I     3.871     .140     2.033     .298  -.233000   .254000   .3144000      .000      .000  
+84 222 45752.00 I  -.236884  .000361   .256861  .001018  I  .3119521  .0000744  2.0379 0.0644  I     3.602     .615     2.053     .298  -.234000   .258000   .3123000      .000      .000  
+84 223 45753.00 I  -.238091  .000393   .260612  .000772  I  .3100573  .0001045  1.7504 0.0593  I     3.549    1.090     2.085     .100  -.235000   .261000   .3105000      .000      .000  
+84 224 45754.00 I  -.239304  .000214   .264417  .000864  I  .3084469  .0000924  1.4762 0.0697  I     3.615    1.090     2.083     .100  -.236000   .265000   .3089000      .000      .000  
+84 225 45755.00 I  -.240498  .000214   .268293  .000864  I  .3070853  .0000924  1.2607 0.0694  I     3.700    1.090     2.145     .100  -.237000   .269000   .3075000      .000      .000  
+84 226 45756.00 I  -.241649  .000346   .272247  .000772  I  .3058959  .0001036  1.1341 0.0705  I     3.473    1.090     2.222     .100  -.238000   .273000   .3063000      .000      .000  
+84 227 45757.00 I  -.242735  .000404   .276271  .000588  I  .3047850  .0001065  1.1038 0.0936  I     3.065     .924     2.233     .298  -.238000   .277000   .3051000      .000      .000  
+84 228 45758.00 I  -.243735  .000505   .280348  .000358  I  .3036583  .0001558  1.1634 0.0821  I     2.748     .956     2.146     .298  -.239000   .280000   .3039000      .000      .000  
+84 229 45759.00 I  -.244627  .001082   .284464  .000348  I  .3024328  .0001251  1.2990 0.0992  I     2.557     .460     2.054     .298  -.240000   .284000   .3026000      .000      .000  
+84 3 1 45760.00 I  -.245397  .001082   .288604  .000348  I  .3010452  .0001228  1.4798 0.0796  I     2.482     .460     2.132     .298  -.241000   .288000   .3011000      .000      .000  
+84 3 2 45761.00 I  -.246066  .001240   .292756  .000428  I  .2994704  .0000983  1.6690 0.0737  I     2.507     .460     2.395     .298  -.241000   .292000   .2996000      .000      .000  
+84 3 3 45762.00 I  -.246663  .001315   .296911  .000448  I  .2977127  .0000816  1.8419 0.0685  I     2.658     .347     2.659     .298  -.241000   .296000   .2978000      .000      .000  
+84 3 4 45763.00 I  -.247186  .001483   .301065  .000415  I  .2957984  .0000953  1.9796 0.0627  I     2.971     .170     2.740     .298  -.242000   .300000   .2959000      .000      .000  
+84 3 5 45764.00 I  -.247564  .001483   .305215  .000415  I  .2937702  .0000953  2.0671 0.0675  I     3.409     .170     2.639     .298  -.242000   .304000   .2939000      .000      .000  
+84 3 6 45765.00 I  -.247740  .001431   .309367  .000454  I  .2916832  .0000956  2.0974 0.0648  I     3.855     .170     2.468     .298  -.242000   .308000   .2919000      .000      .000  
+84 3 7 45766.00 I  -.247738  .001409   .313525  .000369  I  .2895946  .0000879  2.0701 0.0632  I     4.170     .170     2.284     .298  -.242000   .312000   .2899000      .000      .000  
+84 3 8 45767.00 I  -.247545  .001359   .317689  .000353  I  .2875612  .0000828  1.9883 0.0771  I     4.258     .121     2.043     .298  -.242000   .316000   .2880000      .000      .000  
+84 3 9 45768.00 I  -.247117  .001390   .321851  .000256  I  .2856316  .0001267  1.8659 0.0511  I     4.195     .791     1.769     .298  -.241000   .320000   .2861000      .000      .000  
+84 310 45769.00 I  -.246412  .001014   .326007  .000431  I  .2838347  .0000600  1.7277 0.0701  I     4.164     .791     1.629     .298  -.241000   .324000   .2844000      .000      .000  
+84 311 45770.00 I  -.245453  .001014   .330153  .000431  I  .2821681  .0000600  1.6139 0.0385  I     4.182     .791     1.733     .298  -.240000   .328000   .2827000      .000      .000  
+84 312 45771.00 I  -.244301  .001366   .334287  .000376  I  .2805828  .0000483  1.5725 0.0348  I     4.088     .791     1.910     .298  -.240000   .332000   .2811000      .000      .000  
+84 313 45772.00 I  -.243017  .001325   .338411  .000602  I  .2789864  .0000351  1.6411 0.0280  I     3.894     .143     1.892     .159  -.239000   .336000   .2795000      .000      .000  
+84 314 45773.00 I  -.241646  .001446   .342540  .000659  I  .2772601  .0000283  1.8313 0.0198  I     3.940     .176     1.660     .199  -.239000   .340000   .2777000      .000      .000  
+84 315 45774.00 I  -.240224  .001446   .346696  .000659  I  .2752929  .0000183  2.1141 0.0276  I     4.510     .176     1.460     .199  -.238000   .344000   .2757000      .000      .000  
+84 316 45775.00 I  -.238804  .001672   .350899  .000642  I  .2730242  .0000475  2.4200 0.0240  I     5.415     .176     1.448     .199  -.237000   .347000   .2733000      .000      .000  
+84 317 45776.00 I  -.237426  .001540   .355140  .000594  I  .2704742  .0000444  2.6621 0.0321  I     6.114     .176     1.493     .199  -.236000   .351000   .2706000      .000      .000  
+84 318 45777.00 I  -.236099  .001073   .359395  .000609  I  .2677449  .0000433  2.7697 0.0412  I     6.267     .963     1.367     .162  -.235000   .355000   .2677000      .000      .000  
+84 319 45778.00 I  -.234833  .000504   .363640  .000346  I  .2649881  .0000694  2.7176 0.0464  I     5.933    1.350     1.060     .113  -.234000   .359000   .2649000      .000      .000  
+84 320 45779.00 I  -.233638  .000504   .367852  .000346  I  .2623533  .0000820  2.5348 0.0567  I     5.325    1.350      .756     .113  -.233000   .363000   .2621000      .000      .000  
+84 321 45780.00 I  -.232516  .001037   .372008  .000403  I  .2599421  .0000898  2.2812 0.0523  I     4.562    1.350      .530     .113  -.231000   .367000   .2596000      .000      .000  
+84 322 45781.00 I  -.231434  .000996   .376092  .000507  I  .2577934  .0000649  2.0200 0.0545  I     3.727    1.350      .259     .113  -.230000   .371000   .2574000      .000      .000  
+84 323 45782.00 I  -.230332  .000908   .380098  .000475  I  .2558878  .0000617  1.8017 0.0495  I     2.824     .972     -.121     .220  -.228000   .375000   .2554000      .000      .000  
+84 324 45783.00 I  -.229142  .001034   .384027  .000547  I  .2541670  .0000747  1.6523 0.0574  I     1.819     .257     -.434     .290  -.227000   .379000   .2536000      .000      .000  
+84 325 45784.00 I  -.227828  .001019   .387876  .000520  I  .2525587  .0000968  1.5763 0.0580  I      .843     .257     -.457     .290  -.225000   .383000   .2520000      .000      .000  
+84 326 45785.00 I  -.226424  .001019   .391632  .000520  I  .2509899  .0000888  1.5736 0.0629  I      .230     .257     -.167     .290  -.223000   .387000   .2503000      .000      .000  
+84 327 45786.00 I  -.224943  .000382   .395303  .000553  I  .2493888  .0000803  1.6388 0.0597  I      .162     .257      .266     .290  -.221000   .390000   .2487000      .000      .000  
+84 328 45787.00 I  -.223387  .000382   .398901  .000553  I  .2476965  .0000799  1.7515 0.0991  I      .455     .257      .734     .290  -.219000   .394000   .2469000      .000      .000  
+84 329 45788.00 I  -.221753  .000192   .402445  .000510  I  .2458792  .0001811  1.8841 0.1014  I      .741     .791     1.270     .298  -.218000   .398000   .2450000      .000      .000  
+84 330 45789.00 I  -.220027  .000192   .405961  .000510  I  .2439299  .0001863  2.0119 0.0953  I      .859     .791     1.874     .298  -.216000   .402000   .2430000      .000      .000  
+84 331 45790.00 I  -.218203  .000167   .409467  .000497  I  .2418646  .0000596  2.1122 0.0960  I      .942     .791     2.386     .298  -.213000   .406000   .2409000      .000      .000  
+84 4 1 45791.00 I  -.216277  .000369   .412970  .000787  I  .2397213  .0000461  2.1650 0.0353  I     1.186     .791     2.587     .298  -.211000   .410000   .2386000      .000      .000  
+84 4 2 45792.00 I  -.214248  .000673   .416473  .000865  I  .2375544  .0000377  2.1583 0.0412  I     1.668     .116     2.395     .298  -.209000   .414000   .2363000      .000      .000  
+84 4 3 45793.00 I  -.212116  .000791   .419980  .001333  I  .2354255  .0000682  2.0893 0.0383  I     2.373     .116     1.886     .298  -.206000   .417000   .2341000      .000      .000  
+84 4 4 45794.00 I  -.209885  .000791   .423499  .001333  I  .2333936  .0000667  1.9670 0.0517  I     3.241     .116     1.144     .298  -.204000   .421000   .2320000      .000      .000  
+84 4 5 45795.00 I  -.207558  .000791   .427047  .001333  I  .2315025  .0000776  1.8116 0.0557  I     4.171     .116      .165     .298  -.202000   .425000   .2301000      .000      .000  
+84 4 6 45796.00 I  -.205116  .000828   .430657  .001285  I  .2297717  .0000893  1.6522 0.0563  I     5.044     .116     -.964     .298  -.199000   .428000   .2283000      .000      .000  
+84 4 7 45797.00 I  -.202564  .000936   .434329  .001211  I  .2281875  .0000817  1.5245 0.0689  I     5.821     .247    -2.041     .298  -.197000   .432000   .2268000      .000      .000  
+84 4 8 45798.00 I  -.199927  .000914   .438042  .001183  I  .2267002  .0001049  1.4644 0.0729  I     6.491     .330    -2.846     .114  -.195000   .436000   .2252000      .000      .000  
+84 4 9 45799.00 I  -.197234  .000940   .441776  .000921  I  .2252257  .0001207  1.5032 0.0805  I     6.949     .330    -3.227     .114  -.192000   .439000   .2238000      .000      .000  
+84 410 45800.00 I  -.194512  .000940   .445521  .000921  I  .2236558  .0001221  1.6559 0.0833  I     7.063     .330    -3.109     .114  -.190000   .443000   .2222000      .000      .000  
+84 411 45801.00 I  -.191747  .000878   .449274  .000963  I  .2218794  .0001148  1.9121 0.0768  I     6.909     .330    -2.583     .114  -.187000   .446000   .2204000      .000      .000  
+84 412 45802.00 I  -.188910  .000824   .453024  .000984  I  .2198122  .0000933  2.2264 0.0836  I     6.623     .349    -1.726     .114  -.185000   .450000   .2184000      .000      .000  
+84 413 45803.00 I  -.186018  .000629   .456733  .001073  I  .2174328  .0001216  2.5220 0.0770  I     6.224     .367     -.678     .113  -.182000   .453000   .2161000      .000      .000  
+84 414 45804.00 I  -.183103  .000629   .460348  .001073  I  .2148017  .0001224  2.7172 0.0803  I     5.713     .367      .222     .113  -.179000   .457000   .2136000      .000      .000  
+84 415 45805.00 I  -.180195  .000231   .463824  .000725  I  .2120503  .0001050  2.7566 0.0736  I     5.247     .367      .592     .113  -.177000   .460000   .2109000      .000      .000  
+84 416 45806.00 I  -.177307  .000309   .467156  .000706  I  .2093423  .0000816  2.6344 0.0580  I     4.967     .367      .474     .113  -.174000   .463000   .2084000      .000      .000  
+84 417 45807.00 I  -.174447  .000774   .470354  .000769  I  .2068199  .0000493  2.3969 0.0574  I     4.911     .809      .194     .219  -.171000   .466000   .2061000      .000      .000  
+84 418 45808.00 I  -.171620  .000899   .473431  .000726  I  .2045618  .0000808  2.1195 0.0430  I     5.012    1.044     -.006     .271  -.168000   .469000   .2039000      .000      .000  
+84 419 45809.00 I  -.168823  .000899   .476398  .000726  I  .2025697  .0000705  1.8759 0.0536  I     5.125    1.044     -.070     .271  -.165000   .473000   .2020000      .000      .000  
+84 420 45810.00 I  -.166061  .000899   .479278  .000726  I  .2007854  .0000704  1.7062 0.0484  I     5.088    1.044      .016     .271  -.162000   .475000   .2003000      .000      .000  
+84 421 45811.00 I  -.163372  .000905   .482112  .000731  I  .1991292  .0000662  1.6208 0.0475  I     5.144    1.044      .005     .271  -.159000   .479000   .1987000      .000      .000  
+84 422 45812.00 I  -.160723  .000936   .484922  .000740  I  .1975172  .0000638  1.6153 0.0549  I     5.455     .957     -.128     .231  -.156000   .481000   .1970000      .000      .000  
+84 423 45813.00 I  -.158061  .001071   .487723  .000472  I  .1958781  .0000875  1.6712 0.0402  I     5.953     .951     -.199     .194  -.153000   .484000   .1953000      .000      .000  
+84 424 45814.00 I  -.155331  .000641   .490533  .000480  I  .1941616  .0000489  1.7665 0.0499  I     6.565     .650     -.198     .298  -.150000   .488000   .1936000      .000      .000  
+84 425 45815.00 I  -.152483  .000641   .493366  .000480  I  .1923383  .0000482  1.8820 0.0326  I     6.994     .761     -.115     .298  -.146000   .490000   .1917000      .000      .000  
+84 426 45816.00 I  -.149499  .000549   .496226  .000477  I  .1903968  .0000432  1.9998 0.0306  I     7.055     .761      .066     .298  -.143000   .493000   .1898000      .000      .000  
+84 427 45817.00 I  -.146378  .000598   .499108  .000487  I  .1883442  .0000378  2.1009 0.0307  I     6.831     .688      .348     .298  -.140000   .496000   .1876000      .000      .000  
+84 428 45818.00 I  -.143112  .000309   .502004  .000338  I  .1862065  .0000435  2.1673 0.0273  I     6.676     .707      .616     .298  -.136000   .499000   .1854000      .000      .000  
+84 429 45819.00 I  -.139694  .000309   .504907  .000338  I  .1840258  .0000393  2.1851 0.0359  I     6.677     .707      .723     .298  -.133000   .501000   .1832000      .000      .000  
+84 430 45820.00 I  -.136117  .000374   .507802  .000566  I  .1818567  .0000571  2.1417 0.0366  I     6.634     .707      .603     .298  -.129000   .504000   .1810000      .000      .000  
+84 5 1 45821.00 I  -.132404  .000374   .510670  .000566  I  .1797638  .0000617  2.0341 0.0444  I     6.501     .521      .310     .298  -.126000   .506000   .1788000      .000      .000  
+84 5 2 45822.00 I  -.128584  .000563   .513487  .000626  I  .1778052  .0000681  1.8765 0.0609  I     6.311     .521     -.013     .298  -.122000   .509000   .1767000      .000      .000  
+84 5 3 45823.00 I  -.124685  .000857   .516235  .000741  I  .1760190  .0001050  1.6944 0.0620  I     6.039     .791     -.240     .298  -.119000   .511000   .1749000      .000      .000  
+84 5 4 45824.00 I  -.120721  .000857   .518909  .000741  I  .1744128  .0001036  1.5232 0.0736  I     5.620     .791     -.352     .298  -.115000   .514000   .1732000      .000      .000  
+84 5 5 45825.00 I  -.116696  .000857   .521516  .000741  I  .1729556  .0001031  1.4031 0.0628  I     5.087     .791     -.411     .298  -.112000   .516000   .1717000      .000      .000  
+84 5 6 45826.00 I  -.112618  .000835   .524062  .000475  I  .1715775  .0000711  1.3702 0.0594  I     4.643     .791     -.481     .298  -.108000   .518000   .1703000      .000      .000  
+84 5 7 45827.00 I  -.108541  .000895   .526540  .000422  I  .1701798  .0000589  1.4436 0.0440  I     4.535     .250     -.569     .171  -.104000   .521000   .1689000      .000      .000  
+84 5 8 45828.00 I  -.104534  .000873   .528942  .000373  I  .1686564  .0000519  1.6186 0.0460  I     4.848     .250     -.625     .171  -.101000   .523000   .1673000      .000      .000  
+84 5 9 45829.00 I  -.100665  .000806   .531257  .000295  I  .1669190  .0000706  1.8643 0.0592  I     5.448     .250     -.578     .171  -.097000   .525000   .1655000      .000      .000  
+84 510 45830.00 I  -.096980  .000806   .533468  .000295  I  .1649239  .0001065  2.1218 0.0601  I     6.096     .250     -.386     .171  -.094000   .527000   .1635000      .000      .000  
+84 511 45831.00 I  -.093484  .000857   .535562  .000330  I  .1626958  .0000973  2.3181 0.0692  I     6.529     .250     -.088     .171  -.090000   .529000   .1612000      .000      .000  
+84 512 45832.00 I  -.090126  .000861   .537536  .000351  I  .1603279  .0000883  2.3936 0.0734  I     6.635     .325      .192     .178  -.086000   .531000   .1587000      .000      .000  
+84 513 45833.00 I  -.086832  .000810   .539392  .000396  I  .1579581  .0001100  2.3205 0.0733  I     6.526     .386      .329     .186  -.083000   .533000   .1563000      .000      .000  
+84 514 45834.00 I  -.083528  .000737   .541130  .000408  I  .1557314  .0001171  2.1138 0.0744  I     6.352     .386      .310     .186  -.079000   .535000   .1541000      .000      .000  
+84 515 45835.00 I  -.080144  .000670   .542753  .000339  I  .1537564  .0001002  1.8293 0.0637  I     6.125     .386      .229     .186  -.075000   .537000   .1521000      .000      .000  
+84 516 45836.00 I  -.076659  .000688   .544280  .000343  I  .1520742  .0000503  1.5404 0.0557  I     5.837     .386      .153     .186  -.071000   .538000   .1504000      .000      .000  
+84 517 45837.00 I  -.073068  .000773   .545737  .000735  I  .1506565  .0000488  1.3086 0.0382  I     5.497     .522      .028     .134  -.067000   .540000   .1490000      .000      .000  
+84 518 45838.00 I  -.069372  .000812   .547148  .000804  I  .1494269  .0000575  1.1669 0.0375  I     5.139     .629     -.219     .298  -.064000   .542000   .1477000      .000      .000  
+84 519 45839.00 I  -.065593  .000812   .548539  .000804  I  .1482916  .0000569  1.1185 0.0406  I     4.810     .629     -.520     .298  -.060000   .544000   .1466000      .000      .000  
+84 520 45840.00 I  -.061753  .000812   .549930  .000804  I  .1471636  .0000572  1.1489 0.0374  I     4.588     .629     -.705     .298  -.056000   .545000   .1454000      .000      .000  
+84 521 45841.00 I  -.057842  .000767   .551303  .000815  I  .1459754  .0000487  1.2344 0.0364  I     4.521     .629     -.714     .298  -.052000   .546000   .1442000      .000      .000  
+84 522 45842.00 I  -.053862  .000893   .552630  .000787  I  .1446855  .0000449  1.3479 0.0349  I     4.632     .705     -.650     .298  -.048000   .548000   .1429000      .000      .000  
+84 523 45843.00 I  -.049830  .000803   .553883  .000425  I  .1432785  .0000499  1.4648 0.0305  I     4.820     .774     -.620     .298  -.044000   .549000   .1416000      .000      .000  
+84 524 45844.00 I  -.045764  .000793   .555039  .000445  I  .1417625  .0000413  1.5620 0.0340  I     4.832     .774     -.590     .298  -.040000   .550000   .1401000      .000      .000  
+84 525 45845.00 I  -.041679  .000793   .556087  .000445  I  .1401662  .0000463  1.6240 0.0333  I     4.443     .774     -.459     .298  -.036000   .552000   .1385000      .000      .000  
+84 526 45846.00 I  -.037550  .000745   .557031  .000428  I  .1385293  .0000522  1.6417 0.0336  I     3.700     .774     -.213     .298  -.032000   .553000   .1368000      .000      .000  
+84 527 45847.00 I  -.033346  .000963   .557886  .000765  I  .1368996  .0000488  1.6089 0.0409  I     2.843     .591      .023     .298  -.028000   .554000   .1352000      .000      .000  
+84 528 45848.00 I  -.029063  .000865   .558661  .000818  I  .1353285  .0000631  1.5249 0.0395  I     2.126     .315      .099     .298  -.024000   .555000   .1337000      .000      .000  
+84 529 45849.00 I  -.024702  .000946   .559368  .000991  I  .1338643  .0000622  1.3973 0.0451  I     1.736     .315     -.034     .298  -.020000   .556000   .1322000      .000      .000  
+84 530 45850.00 I  -.020261  .000861   .560019  .000901  I  .1325442  .0000644  1.2389 0.0414  I     1.760     .315     -.289     .298  -.016000   .557000   .1310000      .000      .000  
+84 531 45851.00 I  -.015736  .000784   .560616  .000824  I  .1313877  .0000546  1.0769 0.0393  I     2.069     .315     -.516     .298  -.012000   .557000   .1299000      .000      .000  
+84 6 1 45852.00 I  -.011164  .000661   .561137  .000700  I  .1303775  .0000450  0.9537 0.0403  I     2.429     .333     -.606     .298  -.007000   .558000   .1289000      .000      .000  
+84 6 2 45853.00 I  -.006584  .000375   .561557  .000428  I  .1294549  .0000594  0.9068 0.0360  I     2.681     .350     -.562     .298  -.003000   .559000   .1280000      .000      .000  
+84 6 3 45854.00 I  -.002004  .000375   .561867  .000428  I  .1285322  .0000562  0.9552 0.0434  I     2.810     .350     -.462     .298   .001000   .559000   .1272000      .000      .000  
+84 6 4 45855.00 I   .002574  .000375   .562062  .000428  I  .1275139  .0000633  1.0956 0.0390  I     2.870     .350     -.354     .298   .005000   .560000   .1262000      .000      .000  
+84 6 5 45856.00 I   .007150  .000282   .562137  .000244  I  .1263199  .0000540  1.2994 0.0407  I     2.931     .350     -.229     .298   .009000   .560000   .1250000      .000      .000  
+84 6 6 45857.00 I   .011713  .000288   .562095  .000238  I  .1249107  .0000512  1.5165 0.0486  I     2.935     .293     -.057     .551   .014000   .560000   .1236000      .000      .000  
+84 6 7 45858.00 I   .016250  .000380   .561940  .000202  I  .1233020  .0000808  1.6885 0.0589  I     2.720     .223      .139     .778   .018000   .560000   .1220000      .000      .000  
+84 6 8 45859.00 I   .020744  .000652   .561678  .000281  I  .1215661  .0001060  1.7633 0.0665  I     2.185     .223      .264     .778   .022000   .560000   .1203000      .000      .000  
+84 6 9 45860.00 I   .025167  .000652   .561321  .000281  I  .1198165  .0001056  1.7147 0.0678  I     1.416     .223      .209     .778   .026000   .560000   .1185000      .000      .000  
+84 610 45861.00 I   .029479  .000526   .560886  .000441  I  .1181750  .0000846  1.5513 0.0649  I      .642     .223     -.027     .778   .030000   .560000   .1169000      .000      .000  
+84 611 45862.00 I   .033651  .000595   .560392  .000571  I  .1167397  .0000755  1.3106 0.0636  I      .129     .506     -.356     .690   .035000   .560000   .1154000      .000      .000  
+84 612 45863.00 I   .037698  .000634   .559870  .000680  I  .1155606  .0000949  1.0494 0.0581  I      .024     .679     -.653     .588   .039000   .560000   .1142000      .000      .000  
+84 613 45864.00 I   .041648  .000630   .559352  .000595  I  .1146282  .0000884  0.8261 0.0550  I      .278     .679     -.799     .588   .043000   .560000   .1132000      .000      .000  
+84 614 45865.00 I   .045528  .000467   .558866  .000641  I  .1138831  .0000558  0.6787 0.0501  I      .686     .679     -.800     .588   .047000   .559000   .1125000      .000      .000  
+84 615 45866.00 I   .049374  .000533   .558416  .000569  I  .1132413  .0000471  0.6200 0.0344  I     1.028     .679     -.827     .588   .052000   .559000   .1118000      .000      .000  
+84 616 45867.00 I   .053228  .000711   .557996  .000563  I  .1126155  .0000401  0.6442 0.0334  I     1.229     .575     -.861     .421   .056000   .558000   .1112000      .000      .000  
+84 617 45868.00 I   .057118  .000775   .557597  .000272  I  .1119315  .0000473  0.7325 0.0278  I     1.293     .448     -.847     .298   .060000   .558000   .1105000      .000      .000  
+84 618 45869.00 I   .061049  .000775   .557210  .000272  I  .1111373  .0000384  0.8605 0.0274  I     1.249     .448     -.799     .298   .064000   .557000   .1097000      .000      .000  
+84 619 45870.00 I   .065011  .000824   .556822  .000312  I  .1102061  .0000275  1.0018 0.0243  I     1.124     .448     -.817     .298   .069000   .556000   .1088000      .000      .000  
+84 620 45871.00 I   .068961  .000944   .556410  .000475  I  .1091371  .0000299  1.1329 0.0270  I      .980     .448     -.935     .298   .073000   .555000   .1077000      .000      .000  
+84 621 45872.00 I   .072888  .000886   .555944  .000440  I  .1079499  .0000464  1.2352 0.0405  I      .849     .958    -1.046     .167   .077000   .555000   .1066000      .000      .000  
+84 622 45873.00 I   .076789  .000955   .555397  .000557  I  .1066807  .0000752  1.2953 0.0437  I      .654    1.279    -1.009     .219   .081000   .554000   .1053000      .000      .000  
+84 623 45874.00 I   .080672  .000808   .554742  .000625  I  .1053753  .0000740  1.3073 0.0532  I      .256    1.279     -.800     .219   .086000   .553000   .1041000      .000      .000  
+84 624 45875.00 I   .084588  .000808   .553970  .000625  I  .1040846  .0000754  1.2639 0.0495  I     -.363    1.279     -.521     .219   .090000   .552000   .1029000      .000      .000  
+84 625 45876.00 I   .088574  .000919   .553073  .000580  I  .1028675  .0000658  1.1605 0.0492  I     -.993    1.279     -.319     .219   .094000   .551000   .1017000      .000      .000  
+84 626 45877.00 I   .092657  .000791   .552046  .000377  I  .1017801  .0000633  1.0074 0.0503  I    -1.337    1.223     -.268     .164   .098000   .549000   .1006000      .000      .000  
+84 627 45878.00 I   .096871  .000826   .550896  .000421  I  .1008610  .0000760  0.8293 0.0461  I    -1.273    1.165     -.359     .298   .102000   .548000   .0998000      .000      .000  
+84 628 45879.00 I   .101258  .000957   .549641  .000433  I  .1001166  .0000671  0.6659 0.0512  I     -.893    1.165     -.549     .298   .107000   .547000   .0991000      .000      .000  
+84 629 45880.00 I   .105838  .001070   .548297  .000287  I  .0995091  .0000685  0.5627 0.0470  I     -.328    1.165     -.761     .298   .111000   .546000   .0986000      .000      .000  
+84 630 45881.00 I   .110541  .000967   .546878  .000287  I  .0989598  .0000657  0.5538 0.0457  I      .317    1.165     -.897     .298   .115000   .544000   .0981000      .000      .000  
+84 7 1 45882.00 I   .115262  .000814   .545401  .000256  I  .0983676  .0000604  0.6466 0.0484  I      .940    1.001     -.925     .298   .119000   .543000   .0975000      .000      .000  
+84 7 2 45883.00 I   .119923  .000701   .543875  .000300  I  .0976423  .0000710  0.8123 0.0392  I     1.377     .804     -.895     .298   .123000   .541000   .0967000      .000      .000  
+84 7 3 45884.00 I   .124503  .000701   .542301  .000300  I  .0967379  .0000501  0.9932 0.0471  I     1.422     .804     -.855     .298   .128000   .540000   .0957000      .000      .000  
+84 7 4 45885.00 I   .128981  .000284   .540676  .000360  I  .0956692  .0000618  1.1340 0.0375  I      .940     .804     -.777     .298   .132000   .538000   .0946000      .000      .000  
+84 7 5 45886.00 I   .133340  .000404   .538990  .000311  I  .0944962  .0000557  1.1958 0.0438  I      .045     .804     -.625     .298   .136000   .536000   .0932000      .000      .000  
+84 7 6 45887.00 I   .137574  .000526   .537225  .000437  I  .0933116  .0000621  1.1557 0.0451  I    -1.071    1.211     -.404     .189   .140000   .535000   .0919000      .000      .000  
+84 7 7 45888.00 I   .141680  .000673   .535361  .000562  I  .0922166  .0000709  1.0201 0.0482  I    -2.249    1.423     -.166     .245   .145000   .533000   .0907000      .000      .000  
+84 7 8 45889.00 I   .145664  .000601   .533389  .000529  I  .0912929  .0000737  0.8198 0.0511  I    -3.299    1.423      .021     .245   .149000   .531000   .0897000      .000      .000  
+84 7 9 45890.00 I   .149545  .000601   .531308  .000529  I  .0905856  .0000737  0.5937 0.0490  I    -3.991    1.423      .091     .245   .153000   .529000   .0889000      .000      .000  
+84 710 45891.00 I   .153348  .000587   .529131  .000651  I  .0900997  .0000646  0.3849 0.0468  I    -4.274    1.423      .014     .245   .157000   .527000   .0884000      .000      .000  
+84 711 45892.00 I   .157110  .000551   .526879  .000668  I  .0897970  .0000577  0.2324 0.0439  I    -4.148    1.105     -.212     .223   .161000   .525000   .0881000      .000      .000  
+84 712 45893.00 I   .160862  .000330   .524579  .000742  I  .0896094  .0000594  0.1565 0.0414  I    -3.775     .182     -.546     .187   .165000   .523000   .0880000      .000      .000  
+84 713 45894.00 I   .164632  .000330   .522263  .000742  I  .0894579  .0000594  0.1589 0.0449  I    -3.430     .182     -.882     .187   .169000   .521000   .0878000      .000      .000  
+84 714 45895.00 I   .168441  .000362   .519958  .000714  I  .0892686  .0000674  0.2305 0.0420  I    -3.330     .182    -1.071     .187   .173000   .518000   .0877000      .000      .000  
+84 715 45896.00 I   .172283  .000505   .517656  .000661  I  .0889796  .0000593  0.3542 0.0422  I    -3.470     .182    -1.037     .187   .177000   .516000   .0874000      .000      .000  
+84 716 45897.00 I   .176152  .000446   .515337  .000729  I  .0885517  .0000507  0.5035 0.0415  I    -3.744     .172     -.851     .132   .181000   .514000   .0870000      .000      .000  
+84 717 45898.00 I   .180041  .000577   .512981  .000366  I  .0879727  .0000580  0.6525 0.0365  I    -4.050     .161     -.669     .298   .185000   .511000   .0865000      .000      .000  
+84 718 45899.00 I   .183948  .000577   .510566  .000366  I  .0872532  .0000526  0.7821 0.0464  I    -4.238     .161     -.566     .298   .189000   .509000   .0858000      .000      .000  
+84 719 45900.00 I   .187878  .000536   .508072  .000708  I  .0864211  .0000725  0.8739 0.0442  I    -4.150     .149     -.465     .298   .193000   .507000   .0850000      .000      .000  
+84 720 45901.00 I   .191806  .000536   .505497  .000707  I  .0855229  .0000711  0.9131 0.0508  I    -3.888     .130     -.259     .298   .197000   .504000   .0842000      .000      .000  
+84 721 45902.00 I   .195733  .000536   .502847  .000707  I  .0846132  .0000711  0.8974 0.0609  I    -3.829     .130      .007     .298   .201000   .502000   .0834000      .000      .000  
+84 722 45903.00 I   .199670  .000477   .500128  .000832  I  .0837447  .0000990  0.8318 0.0620  I    -4.258     .791      .174     .298   .204000   .499000   .0826000      .000      .000  
+84 723 45904.00 I   .203624  .000413   .497351  .000935  I  .0829616  .0001016  0.7302 0.0709  I    -5.006     .791      .163     .298   .208000   .496000   .0819000      .000      .000  
+84 724 45905.00 I   .207573  .000413   .494543  .000935  I  .0822899  .0001016  0.6118 0.0735  I    -5.582     .791      .027     .298   .212000   .494000   .0813000      .000      .000  
+84 725 45906.00 I   .211481  .000481   .491731  .001050  I  .0817349  .0001061  0.5029 0.0534  I    -5.734     .791     -.150     .298   .215000   .491000   .0808000      .000      .000  
+84 726 45907.00 I   .215311  .000821   .488936  .000509  I  .0812672  .0000332  0.4448 0.0561  I    -5.627     .506     -.379     .489   .219000   .488000   .0804000      .000      .000  
+84 727 45908.00 I   .219051  .000743   .486154  .000467  I  .0808141  .0000363  0.4802 0.0252  I    -5.503     .506     -.684     .489   .223000   .485000   .0801000      .000      .000  
+84 728 45909.00 I   .222688  .000809   .483378  .000147  I  .0802680  .0000379  0.6321 0.0290  I    -5.402     .417     -.991     .439   .226000   .483000   .0796000      .000      .000  
+84 729 45910.00 I   .226221  .000787   .480603  .000226  I  .0795154  .0000453  0.8874 0.0275  I    -5.267     .417    -1.153     .439   .230000   .479000   .0789000      .000      .000  
+84 730 45911.00 I   .229681  .000767   .477813  .000285  I  .0784771  .0000398  1.1908 0.0286  I    -5.078     .417     -.887     .439   .233000   .476000   .0780000      .000      .000  
+84 731 45912.00 I   .233082  .000724   .474988  .000321  I  .0771460  .0000349  1.4575 0.0267  I    -5.046     .542     -.715     .342   .236000   .473000   .0768000      .000      .000  
+84 8 1 45913.00 I   .236413  .000584   .472105  .000381  I  .0755994  .0000357  1.6124 0.0267  I    -5.231     .565     -.787     .176   .240000   .470000   .0753000      .000      .000  
+84 8 2 45914.00 I   .239692  .000370   .469146  .000344  I  .0739710  .0000405  1.6181 0.0262  I    -5.508     .515     -.933     .161   .243000   .467000   .0737000      .000      .000  
+84 8 3 45915.00 I   .242944  .000551   .466103  .000484  I  .0724111  .0000383  1.4801 0.0273  I    -5.788     .592     -.954     .298   .246000   .464000   .0722000      .000      .000  
+84 8 4 45916.00 I   .246165  .000556   .462984  .000571  I  .0710446  .0000365  1.2407 0.0256  I    -6.044     .840     -.811     .185   .249000   .461000   .0708000      .000      .000  
+84 8 5 45917.00 I   .249335  .000543   .459789  .000560  I  .0699438  .0000341  0.9591 0.0268  I    -6.298     .996     -.586     .220   .252000   .458000   .0695000      .000      .000  
+84 8 6 45918.00 I   .252427  .000553   .456515  .000564  I  .0691185  .0000392  0.7011 0.0258  I    -6.455    1.122     -.274     .263   .255000   .454000   .0686000      .000      .000  
+84 8 7 45919.00 I   .255424  .000556   .453164  .000533  I  .0685157  .0000388  0.5207 0.0272  I    -6.273    1.122      .211     .263   .258000   .451000   .0679000      .000      .000  
+84 8 8 45920.00 I   .258353  .000556   .449761  .000533  I  .0680443  .0000378  0.4388 0.0316  I    -6.014    1.278      .447     .294   .261000   .448000   .0674000      .000      .000  
+84 8 9 45921.00 I   .261229  .000753   .446324  .000711  I  .0676057  .0000498  0.4541 0.0358  I    -5.757    1.278      .300     .294   .264000   .444000   .0669000      .000      .000  
+84 810 45922.00 I   .264070  .000677   .442875  .000615  I  .0671077  .0000607  0.5545 0.0479  I    -5.544     .921     -.069     .236   .266000   .441000   .0666000      .000      .000  
+84 811 45923.00 I   .266901  .000813   .439437  .000718  I  .0664755  .0000819  0.7186 0.0510  I    -5.493     .385     -.405     .194   .269000   .438000   .0660000      .000      .000  
+84 812 45924.00 I   .269751  .000813   .436036  .000718  I  .0656580  .0000819  0.9204 0.0663  I    -5.615     .385     -.498     .194   .272000   .434000   .0652000      .000      .000  
+84 813 45925.00 I   .272629  .000818   .432674  .000749  I  .0646337  .0001043  1.1252 0.0560  I    -5.804     .385     -.337     .194   .274000   .431000   .0642000      .000      .000  
+84 814 45926.00 I   .275486  .000727   .429332  .000693  I  .0634174  .0000764  1.3005 0.0601  I    -5.989     .385     -.125     .194   .276000   .427000   .0631000      .000      .000  
+84 815 45927.00 I   .278259  .000678   .425990  .000514  I  .0620488  .0000596  1.4273 0.0493  I    -6.126     .908     -.019     .212   .279000   .424000   .0619000      .000      .000  
+84 816 45928.00 I   .280889  .000312   .422626  .000500  I  .0605835  .0000622  1.4919 0.0431  I    -6.173    1.225      .040     .229   .281000   .420000   .0605000      .000      .000  
+84 817 45929.00 I   .283325  .000312   .419206  .000500  I  .0590876  .0000622  1.4889 0.0440  I    -6.253    1.225      .169     .229   .283000   .417000   .0591000      .000      .000  
+84 818 45930.00 I   .285528  .000312   .415699  .000500  I  .0576264  .0000622  1.4240 0.0447  I    -6.727    1.225      .311     .229   .285000   .413000   .0578000      .000      .000  
+84 819 45931.00 I   .287484  .000327   .412093  .000452  I  .0562564  .0000641  1.3088 0.0459  I    -7.793    1.225      .277     .229   .287000   .410000   .0565000      .000      .000  
+84 820 45932.00 I   .289188  .000370   .408397  .000433  I  .0550201  .0000674  1.1596 0.0588  I    -9.128    1.042      .015     .289   .289000   .406000   .0552000      .000      .000  
+84 821 45933.00 I   .290636  .000409   .404617  .000298  I  .0539412  .0000986  0.9985 0.0496  I   -10.055     .819     -.290     .339   .291000   .403000   .0541000      .000      .000  
+84 822 45934.00 I   .291826  .000646   .400762  .000402  I  .0530166  .0000729  0.8571 0.0618  I   -10.135     .819     -.452     .339   .293000   .399000   .0531000      .000      .000  
+84 823 45935.00 I   .292763  .000646   .396846  .000402  I  .0522015  .0000746  0.7923 0.0491  I    -9.488     .749     -.505     .286   .295000   .395000   .0521000      .000      .000  
+84 824 45936.00 I   .293494  .000855   .392898  .000372  I  .0513881  .0000658  0.8593 0.0474  I    -8.615     .749     -.559     .286   .297000   .392000   .0511000      .000      .000  
+84 825 45937.00 I   .294091  .000860   .388946  .000500  I  .0504355  .0000585  1.0687 0.0359  I    -7.887     .894     -.571     .283   .298000   .388000   .0499000      .000      .000  
+84 826 45938.00 I   .294653  .000954   .385010  .000524  I  .0492161  .0000289  1.3821 0.0326  I    -7.502     .942     -.390     .235   .300000   .384000   .0485000      .000      .000  
+84 827 45939.00 I   .295269  .000954   .381094  .000524  I  .0476622  .0000289  1.7228 0.0157  I    -7.558     .824     -.050     .212   .301000   .380000   .0468000      .000      .000  
+84 828 45940.00 I   .295999  .000598   .377203  .000502  I  .0457925  .0000122  1.9976 0.0151  I    -7.946     .827      .174     .199   .303000   .377000   .0448000      .000      .000  
+84 829 45941.00 I   .296808  .000574   .373365  .000490  I  .0437138  .0000091  2.1321 0.0085  I    -8.531     .877      .097     .219   .304000   .373000   .0426000      .000      .000  
+84 830 45942.00 I   .297631  .000579   .369631  .000475  I  .0415824  .0000119  2.1042 0.0064  I    -9.160    1.224     -.179     .205   .305000   .369000   .0404000      .000      .000  
+84 831 45943.00 I   .298357  .000367   .366043  .000423  I  .0395489  .0000089  1.9456 0.0074  I    -9.733    1.262     -.343     .139   .307000   .365000   .0384000      .000      .000  
+84 9 1 45944.00 I   .298912  .000367   .362592  .000423  I  .0377174  .0000089  1.7082 0.0063  I   -10.120    1.262     -.286     .139   .308000   .362000   .0367000      .000      .000  
+84 9 2 45945.00 I   .299411  .000325   .359180  .000427  I  .0361407  .0000090  1.4460 0.0074  I    -9.912    1.405     -.294     .146   .309000   .358000   .0353000      .000      .000  
+84 9 3 45946.00 I   .299981  .000327   .355697  .000346  I  .0348172  .0000118  1.2084 0.0094  I    -9.420    1.543     -.451     .152   .310000   .354000   .0341000      .000      .000  
+84 9 4 45947.00 I   .300693  .000302   .352060  .000375  I  .0337007  .0000165  1.0405 0.0097  I    -9.152    1.250     -.597     .153   .311000   .350000   .0330000      .000      .000  
+84 9 5 45948.00 I   .301537  .000380   .348292  .000425  I  .0327062  .0000153  0.9628 0.0113  I    -9.011     .801     -.637     .133   .312000   .346000   .0321000      .000      .000  
+84 9 6 45949.00 I   .302483  .000380   .344438  .000425  I  .0317496  .0000153  0.9618 0.0252  I    -8.773     .594     -.620     .154   .313000   .342000   .0312000      .000      .000  
+84 9 7 45950.00 I   .303491  .000732   .340541  .000565  I  .0307616  .0000481  1.0240 0.0242  I    -8.330     .594     -.622     .154   .313000   .338000   .0302000      .000      .000  
+84 9 8 45951.00 I   .304482  .000639   .336627  .000503  I  .0296857  .0000459  1.1341 0.0305  I    -7.829     .594     -.574     .154   .314000   .334000   .0291000      .000      .000  
+84 9 9 45952.00 I   .305408  .000598   .332703  .000457  I  .0284850  .0000374  1.2694 0.0284  I    -7.469     .479     -.330     .160   .314000   .331000   .0279000      .000      .000  
+84 910 45953.00 I   .306254  .000631   .328765  .000411  I  .0271466  .0000336  1.4057 0.0296  I    -7.296     .327      .104     .166   .315000   .327000   .0266000      .000      .000  
+84 911 45954.00 I   .307012  .000467   .324806  .000190  I  .0256802  .0000458  1.5224 0.0284  I    -7.257     .327      .526     .166   .315000   .323000   .0251000      .000      .000  
+84 912 45955.00 I   .307671  .000467   .320822  .000190  I  .0241161  .0000458  1.5964 0.0326  I    -7.313     .327      .762     .166   .316000   .319000   .0236000      .000      .000  
+84 913 45956.00 I   .308251  .000495   .316811  .000433  I  .0225070  .0000463  1.6116 0.0326  I    -7.402     .327      .844     .166   .316000   .315000   .0220000      .000      .000  
+84 914 45957.00 I   .308790  .000495   .312776  .000433  I  .0209113  .0000463  1.5714 0.0462  I    -7.512     .300      .922     .269   .316000   .311000   .0206000      .000      .000  
+84 915 45958.00 I   .309318  .000660   .308721  .000581  I  .0193787  .0000800  1.4876 0.0462  I    -7.751     .269     1.025     .343   .317000   .307000   .0192000      .000      .000  
+84 916 45959.00 I   .309831  .000660   .304651  .000581  I  .0179451  .0000800  1.3767 0.0483  I    -8.202     .269     1.015     .343   .317000   .303000   .0179000      .000      .000  
+84 917 45960.00 I   .310317  .000533   .300575  .000744  I  .0166281  .0000540  1.2574 0.0826  I    -8.658     .269      .826     .343   .317000   .299000   .0168000      .000      .000  
+84 918 45961.00 I   .310782  .000630   .296516  .000644  I  .0154253  .0001445  1.1532 0.0661  I    -8.740     .269      .627     .343   .317000   .295000   .0157000      .000      .000  
+84 919 45962.00 I   .311226  .000623   .292500  .000651  I  .0143055  .0001206  1.0976 0.0968  I    -8.267     .266      .637     .272   .317000   .291000   .0147000      .000      .000  
+84 920 45963.00 I   .311631  .000529   .288546  .000455  I  .0132022  .0001288  1.1255 0.0901  I    -7.486     .260      .851     .298   .317000   .288000   .0137000      .000      .000  
+84 921 45964.00 I   .311986  .000562   .284672  .000503  I  .0120182  .0001340  1.2621 0.0929  I    -6.863     .260     1.096     .298   .317000   .284000   .0126000      .000      .000  
+84 922 45965.00 I   .312306  .000562   .280886  .000503  I  .0106403  .0001340  1.5117 0.0848  I    -6.709     .260     1.288     .298   .317000   .280000   .0112000      .000      .000  
+84 923 45966.00 I   .312596  .000475   .277175  .000506  I  .0089703  .0001038  1.8347 0.0722  I    -7.026     .260     1.483     .298   .317000   .276000   .0095000      .000      .000  
+84 924 45967.00 I   .312847  .000368   .273513  .000497  I  .0069740  .0000537  2.1483 0.0573  I    -7.703     .236     1.659     .287   .317000   .272000   .0075000      .000      .000  
+84 925 45968.00 I   .313046  .000305   .269870  .000409  I  .0047065  .0000487  2.3634 0.0373  I    -8.540     .223     1.623     .361   .316000   .268000   .0052000      .000      .000  
+84 926 45969.00 I   .313180  .000420   .266215  .000388  I  .0022988  .0000519  2.4238 0.0356  I    -9.191     .223     1.280     .361   .316000   .265000   .0028000      .000      .000  
+84 927 45970.00 I   .313234  .000420   .262524  .000388  I -.0000854  .0000519  2.3173 0.0349  I    -9.381     .223      .844     .361   .315000   .261000   .0004000      .000      .000  
+84 928 45971.00 I   .313173  .000387   .258798  .000357  I -.0022935  .0000468  2.0839 0.0341  I    -9.212     .223      .631     .361   .315000   .257000  -.0017000      .000      .000  
+84 929 45972.00 I   .312953  .000435   .255048  .000355  I -.0042383  .0000441  1.8059 0.0302  I    -8.964    1.208      .665     .256   .314000   .253000  -.0037000      .000      .000  
+84 930 45973.00 I   .312541  .000490   .251287  .000203  I -.0059163  .0000382  1.5610 0.0292  I    -8.751    1.694      .678     .298   .314000   .249000  -.0052000      .000      .000  
+8410 1 45974.00 I   .311931  .000490   .247524  .000203  I -.0073872  .0000382  1.3967 0.0254  I    -8.525    1.694      .506     .298   .313000   .246000  -.0066000      .000      .000  
+8410 2 45975.00 I   .311122  .000435   .243769  .000231  I -.0087419  .0000336  1.3291 0.0239  I    -8.289    1.694      .320     .298   .312000   .242000  -.0079000      .000      .000  
+8410 3 45976.00 I   .310139  .000355   .240020  .000347  I -.0100752  .0000286  1.3510 0.0209  I    -8.079    1.694      .362     .298   .311000   .238000  -.0091000      .000      .000  
+8410 4 45977.00 I   .309014  .000401   .236272  .000342  I -.0114658  .0000249  1.4387 0.0217  I    -7.847    1.259      .627     .298   .311000   .234000  -.0105000      .000      .000  
+8410 5 45978.00 I   .307765  .000364   .232514  .000373  I -.0129651  .0000326  1.5639 0.0241  I    -7.518     .549      .914     .298   .310000   .231000  -.0119000      .000      .000  
+8410 6 45979.00 I   .306411  .000392   .228737  .000430  I -.0145974  .0000412  1.7003 0.0263  I    -7.137     .549     1.104     .298   .309000   .227000  -.0135000      .000      .000  
+8410 7 45980.00 I   .304973  .000392   .224926  .000430  I -.0163618  .0000412  1.8249 0.0304  I    -6.826     .549     1.264     .298   .308000   .223000  -.0152000      .000      .000  
+8410 8 45981.00 I   .303439  .000612   .221082  .000434  I -.0182367  .0000446  1.9180 0.0304  I    -6.608     .549     1.481     .298   .307000   .219000  -.0170000      .000      .000  
+8410 9 45982.00 I   .301786  .000657   .217214  .000452  I -.0201825  .0000447  1.9653 0.0394  I    -6.383     .511     1.674     .298   .305000   .216000  -.0189000      .000      .000  
+841010 45983.00 I   .300011  .000761   .213339  .000447  I -.0221498  .0000650  1.9601 0.0396  I    -6.093     .470     1.690     .298   .304000   .212000  -.0208000      .000      .000  
+841011 45984.00 I   .298118  .000674   .209481  .000398  I -.0240852  .0000655  1.9026 0.0557  I    -5.789     .470     1.517     .298   .303000   .209000  -.0227000      .000      .000  
+841012 45985.00 I   .296138  .000726   .205665  .000388  I -.0259385  .0000904  1.7958 0.0557  I    -5.533     .470     1.310     .298   .302000   .205000  -.0245000      .000      .000  
+841013 45986.00 I   .294132  .000826   .201920  .000444  I -.0276638  .0000900  1.6501 0.0627  I    -5.362     .470     1.184     .298   .300000   .201000  -.0261000      .000      .000  
+841014 45987.00 I   .292159  .000940   .198270  .000484  I -.0292338  .0000868  1.4896 0.0824  I    -5.302     .362     1.110     .298   .299000   .198000  -.0276000      .000      .000  
+841015 45988.00 I   .290281  .001187   .194738  .000490  I -.0306484  .0001380  1.3454 0.0895  I    -5.324     .201     1.029     .107   .298000   .194000  -.0290000      .000      .000  
+841016 45989.00 I   .288571  .001187   .191344  .000490  I -.0319410  .0001565  1.2503 0.1038  I    -5.304     .201      .982     .107   .296000   .191000  -.0303000      .000      .000  
+841017 45990.00 I   .287079  .001315   .188101  .000490  I -.0331759  .0001552  1.2358 0.1016  I    -5.132     .201     1.071     .107   .294000   .187000  -.0316000      .000      .000  
+841018 45991.00 I   .285717  .001189   .184988  .000599  I -.0344489  .0001296  1.3300 0.0937  I    -4.843     .201     1.312     .107   .293000   .184000  -.0329000      .000      .000  
+841019 45992.00 I   .284400  .001115   .181961  .000576  I -.0358756  .0001050  1.5431 0.0915  I    -4.601     .397     1.610     .113   .291000   .180000  -.0343000      .000      .000  
+841020 45993.00 I   .283074  .001041   .178971  .000558  I -.0375672  .0001292  1.8524 0.0681  I    -4.567     .524     1.903     .119   .290000   .177000  -.0361000      .000      .000  
+841021 45994.00 I   .281715  .000863   .175971  .000577  I -.0395921  .0000869  2.1968 0.0683  I    -4.839     .524     2.207     .119   .288000   .174000  -.0381000      .000      .000  
+841022 45995.00 I   .280336  .000863   .172924  .000577  I -.0419415  .0000446  2.4833 0.0483  I    -5.427     .524     2.483     .119   .286000   .170000  -.0405000      .000      .000  
+841023 45996.00 I   .278889  .001057   .169829  .000586  I -.0445100  .0000420  2.6237 0.0286  I    -6.147     .524     2.569     .119   .284000   .167000  -.0431000      .000      .000  
+841024 45997.00 I   .277303  .000961   .166697  .000529  I -.0471278  .0000358  2.5808 0.0264  I    -6.646     .536     2.373     .195   .282000   .164000  -.0457000      .000      .000  
+841025 45998.00 I   .275547  .001119   .163527  .000391  I -.0496179  .0000321  2.3767 0.0240  I    -6.695     .548     2.076     .249   .280000   .160000  -.0482000      .000      .000  
+841026 45999.00 I   .273605  .001119   .160317  .000391  I -.0518502  .0000321  2.0797 0.0273  I    -6.431     .548     1.974     .249   .279000   .157000  -.0504000      .000      .000  
+841027 46000.00 I   .271474  .001101   .157067  .000338  I -.0537756  .0000442  1.7775 0.0260  I    -6.145     .548     2.113     .249   .277000   .154000  -.0524000      .000      .000  
+841028 46001.00 I   .269197  .001105   .153794  .000347  I -.0554279  .0000410  1.5434 0.0273  I    -5.933     .548     2.219     .249   .274000   .151000  -.0541000      .000      .000  
+841029 46002.00 I   .266819  .000730   .150514  .000346  I -.0568970  .0000322  1.4133 0.0292  I    -5.653     .409     2.076     .238   .272000   .148000  -.0557000      .000      .000  
+841030 46003.00 I   .264389  .000850   .147245  .000367  I -.0582891  .0000417  1.3871 0.0263  I    -5.232     .183     1.817     .226   .270000   .145000  -.0571000      .000      .000  
+841031 46004.00 I   .261986  .000850   .144010  .000367  I -.0596984  .0000417  1.4424 0.0338  I    -4.804     .183     1.741     .226   .268000   .142000  -.0586000      .000      .000  
+8411 1 46005.00 I   .259695  .000762   .140827  .000456  I -.0611907  .0000533  1.5480 0.0316  I    -4.514     .183     1.923     .226   .266000   .139000  -.0602000      .000      .000  
+8411 2 46006.00 I   .257508  .000720   .137697  .000470  I -.0628018  .0000475  1.6756 0.0347  I    -4.335     .183     2.161     .226   .264000   .136000  -.0619000      .000      .000  
+8411 3 46007.00 I   .255389  .000726   .134606  .000511  I -.0645409  .0000443  1.8001 0.0387  I    -4.220     .498     2.271     .161   .261000   .133000  -.0638000      .000      .000  
+8411 4 46008.00 I   .253304  .001183   .131541  .000705  I -.0663943  .0000610  1.9014 0.0377  I    -4.192     .680     2.299     .298   .259000   .130000  -.0657000      .000      .000  
+8411 5 46009.00 I   .251214  .001183   .128501  .000705  I -.0683312  .0000610  1.9653 0.0431  I    -4.222     .680     2.398     .298   .257000   .127000  -.0678000      .000      .000  
+8411 6 46010.00 I   .249079  .001183   .125494  .000705  I -.0703066  .0000610  1.9745 0.0390  I    -4.185     .680     2.588     .298   .254000   .124000  -.0698000      .000      .000  
+8411 7 46011.00 I   .246833  .001205   .122520  .000562  I -.0722578  .0000487  1.9166 0.0397  I    -4.051     .680     2.727     .298   .252000   .121000  -.0719000      .000      .000  
+8411 8 46012.00 I   .244423  .001180   .119576  .000530  I -.0741189  .0000509  1.7959 0.0354  I    -3.927     .502     2.691     .214   .249000   .119000  -.0738000      .000      .000  
+8411 9 46013.00 I   .241881  .001272   .116679  .000525  I -.0758335  .0000514  1.6268 0.0368  I    -3.887     .206     2.512     .302   .246000   .116000  -.0756000      .000      .000  
+841110 46014.00 I   .239258  .000963   .113852  .000300  I -.0773639  .0000532  1.4320 0.0427  I    -3.862     .206     2.322     .302   .244000   .113000  -.0771000      .000      .000  
+841111 46015.00 I   .236613  .000839   .111116  .000319  I -.0787022  .0000683  1.2512 0.0405  I    -3.758     .206     2.226     .302   .241000   .110000  -.0786000      .000      .000  
+841112 46016.00 I   .233977  .000784   .108480  .000308  I -.0798857  .0000610  1.1284 0.0472  I    -3.560     .206     2.234     .302   .238000   .108000  -.0798000      .000      .000  
+841113 46017.00 I   .231360  .000684   .105945  .000566  I -.0809883  .0000653  1.0933 0.0473  I    -3.311     .269     2.304     .344   .235000   .105000  -.0810000      .000      .000  
+841114 46018.00 I   .228770  .000527   .103517  .000641  I -.0821065  .0000722  1.1609 0.0487  I    -3.016     .319     2.405     .382   .233000   .103000  -.0822000      .000      .000  
+841115 46019.00 I   .226210  .000527   .101208  .000641  I -.0833444  .0000722  1.3316 0.0511  I    -2.634     .319     2.536     .382   .230000   .100000  -.0835000      .000      .000  
+841116 46020.00 I   .223677  .000527   .099029  .000641  I -.0847991  .0000722  1.5906 0.0509  I    -2.166     .319     2.706     .382   .227000   .098000  -.0850000      .000      .000  
+841117 46021.00 I   .221139  .000527   .096966  .000573  I -.0865409  .0000718  1.8956 0.0468  I    -1.726     .319     2.936     .382   .224000   .095000  -.0868000      .000      .000  
+841118 46022.00 I   .218568  .000528   .094989  .000508  I -.0885838  .0000595  2.1811 0.0505  I    -1.495     .588     3.230     .276   .221000   .092000  -.0888000      .000      .000  
+841119 46023.00 I   .215938  .000908   .093066  .000193  I -.0908741  .0000709  2.3796 0.0463  I    -1.585     .768     3.521     .298   .218000   .090000  -.0911000      .000      .000  
+841120 46024.00 I   .213225  .000908   .091166  .000193  I -.0932968  .0000709  2.4394 0.0501  I    -1.905     .768     3.660     .298   .215000   .088000  -.0934000      .000      .000  
+841121 46025.00 I   .210406  .000908   .089264  .000193  I -.0957003  .0000709  2.3414 0.0476  I    -2.181     .768     3.544     .298   .211000   .085000  -.0957000      .000      .000  
+841122 46026.00 I   .207423  .000845   .087336  .000204  I -.0979370  .0000636  2.1156 0.0444  I    -2.179     .768     3.270     .298   .208000   .083000  -.0978000      .000      .000  
+841123 46027.00 I   .204236  .001016   .085360  .000274  I -.0999119  .0000536  1.8310 0.0473  I    -1.952     .556     3.062     .184   .205000   .081000  -.0996000      .000      .000  
+841124 46028.00 I   .200873  .001212   .083317  .000313  I -.1016052  .0000699  1.5651 0.0399  I    -1.733     .165     3.023     .246   .202000   .078000  -.1012000      .000      .000  
+841125 46029.00 I   .197372  .001027   .081189  .000345  I -.1030670  .0000590  1.3741 0.0465  I    -1.593     .165     3.036     .246   .198000   .076000  -.1026000      .000      .000  
+841126 46030.00 I   .193765  .001048   .078961  .000378  I -.1043881  .0000613  1.2873 0.0424  I    -1.371     .165     2.976     .246   .195000   .074000  -.1039000      .000      .000  
+841127 46031.00 I   .190075  .001002   .076633  .000340  I -.1056759  .0000608  1.3034 0.0396  I     -.975     .165     2.906     .246   .192000   .072000  -.1052000      .000      .000  
+841128 46032.00 I   .186314  .000933   .074211  .000457  I -.1070185  .0000500  1.3904 0.0367  I     -.532     .230     3.015     .232   .188000   .069000  -.1066000      .000      .000  
+841129 46033.00 I   .182505  .000851   .071705  .000523  I -.1084686  .0000411  1.5133 0.0330  I     -.244     .280     3.343     .217   .185000   .067000  -.1081000      .000      .000  
+841130 46034.00 I   .178719  .000851   .069134  .000523  I -.1100477  .0000431  1.6440 0.0298  I     -.202     .280     3.690     .217   .181000   .065000  -.1098000      .000      .000  
+8412 1 46035.00 I   .175026  .000851   .066518  .000523  I -.1117507  .0000431  1.7568 0.0278  I     -.402     .280     3.842     .217   .178000   .063000  -.1117000      .000      .000  
+8412 2 46036.00 I   .171408  .000625   .063884  .000573  I -.1135490  .0000351  1.8325 0.0277  I     -.783     .280     3.831     .217   .174000   .061000  -.1136000      .000      .000  
+8412 3 46037.00 I   .167834  .000603   .061277  .000614  I -.1153998  .0000349  1.8604 0.0309  I    -1.250     .215     3.845     .157   .170000   .059000  -.1156000      .000      .000  
+8412 4 46038.00 I   .164269  .000368   .058741  .000617  I -.1172522  .0000509  1.8355 0.0309  I    -1.696     .118     3.975     .298   .166000   .057000  -.1176000      .000      .000  
+8412 5 46039.00 I   .160686  .000368   .056322  .000617  I -.1190543  .0000509  1.7613 0.0354  I    -2.072     .118     4.128     .298   .163000   .055000  -.1194000      .000      .000  
+8412 6 46040.00 I   .157068  .000368   .054065  .000617  I -.1207603  .0000491  1.6437 0.0335  I    -2.405     .118     4.148     .298   .159000   .053000  -.1211000      .000      .000  
+8412 7 46041.00 I   .153378  .000333   .051970  .000571  I -.1223307  .0000436  1.4930 0.0367  I    -2.710     .118     3.973     .298   .155000   .051000  -.1227000      .000      .000  
+8412 8 46042.00 I   .149571  .000630   .050022  .000637  I -.1237441  .0000545  1.3357 0.0380  I    -2.885     .408     3.696     .211   .151000   .050000  -.1241000      .000      .000  
+8412 9 46043.00 I   .145616  .000737   .048212  .000521  I -.1250117  .0000623  1.2070 0.0411  I    -2.802     .565     3.480     .295   .147000   .048000  -.1253000      .000      .000  
+841210 46044.00 I   .141486  .000811   .046529  .000582  I -.1261785  .0000615  1.1393 0.0455  I    -2.482     .565     3.418     .295   .143000   .046000  -.1265000      .000      .000  
+841211 46045.00 I   .137154  .000815   .044964  .000523  I -.1273183  .0000662  1.1556 0.0433  I    -2.088     .565     3.464     .295   .139000   .045000  -.1276000      .000      .000  
+841212 46046.00 I   .132626  .000739   .043500  .000605  I -.1285200  .0000610  1.2628 0.0433  I    -1.747     .565     3.503     .295   .135000   .043000  -.1287000      .000      .000  
+841213 46047.00 I   .127941  .000629   .042103  .000657  I -.1298686  .0000559  1.4441 0.0395  I    -1.474     .418     3.474     .214   .131000   .042000  -.1300000      .000      .000  
+841214 46048.00 I   .123137  .000426   .040739  .000717  I -.1314199  .0000502  1.6605 0.0415  I    -1.242     .173     3.420     .298   .126000   .040000  -.1315000      .000      .000  
+841215 46049.00 I   .118217  .000426   .039369  .000717  I -.1331843  .0000614  1.8609 0.0382  I    -1.064     .173     3.434     .298   .122000   .039000  -.1333000      .000      .000  
+841216 46050.00 I   .113171  .000691   .037956  .000670  I -.1351183  .0000577  1.9910 0.0384  I     -.995     .173     3.574     .298   .118000   .037000  -.1352000      .000      .000  
+841217 46051.00 I   .108005  .000605   .036506  .000689  I -.1371294  .0000461  2.0102 0.0357  I    -1.081     .173     3.798     .298   .114000   .036000  -.1372000      .000      .000  
+841218 46052.00 I   .102735  .000618   .035042  .000658  I -.1390966  .0000419  1.9030 0.0353  I    -1.250     .329     4.011     .108   .110000   .035000  -.1392000      .000      .000  
+841219 46053.00 I   .097379  .000828   .033587  .000777  I -.1408996  .0000536  1.6882 0.0327  I    -1.317     .431     4.124     .137   .106000   .034000  -.1410000      .000      .000  
+841220 46054.00 I   .091986  .000703   .032161  .000668  I -.1424543  .0000501  1.4172 0.0307  I    -1.119     .431     4.119     .137   .102000   .033000  -.1426000      .000      .000  
+841221 46055.00 I   .086657  .000877   .030775  .000768  I -.1437381  .0000299  1.1583 0.0358  I     -.658     .360     4.044     .227   .097000   .032000  -.1439000      .000      .000  
+841222 46056.00 I   .081481  .000953   .029451  .001247  I -.1447949  .0000511  0.9712 0.0296  I     -.104     .360     3.958     .227   .093000   .031000  -.1449000      .000      .000  
+841223 46057.00 I   .076553  .000953   .028198  .001247  I -.1457150  .0000511  0.8874 0.0381  I      .385     .360     3.861     .227   .089000   .030000  -.1458000      .000      .000  
+841224 46058.00 I   .071971  .001038   .027005  .001380  I -.1466072  .0000566  0.9160 0.0405  I      .772     .270     3.742     .290   .085000   .029000  -.1468000      .000      .000  
+841225 46059.00 I   .067706  .001017   .025917  .001285  I -.1475790  .0000629  1.0406 0.0423  I     1.096     .270     3.661     .290   .081000   .028000  -.1478000      .000      .000  
+841226 46060.00 I   .063659  .001017   .024995  .001285  I -.1487065  .0000629  1.2194 0.0816  I     1.344     .270     3.718     .290   .076000   .028000  -.1489000      .000      .000  
+841227 46061.00 I   .059795  .001196   .024242  .001751  I -.1500226  .0001505  1.4127 0.0693  I     1.428     .791     3.920     .298   .072000   .027000  -.1503000      .000      .000  
+841228 46062.00 I   .056087  .000649   .023657  .000210  I -.1515275  .0001235  1.5929 0.0973  I     1.300     .791     4.137     .286   .068000   .026000  -.1517000      .000      .000  
+841229 46063.00 I   .052510  .000649   .023235  .000210  I -.1531963  .0001235  1.7369 0.0873  I     1.033     .791     4.246     .286   .064000   .026000  -.1533000      .000      .000  
+841230 46064.00 I   .049040  .000649   .022967  .000210  I -.1549845  .0001235  1.8304 0.0786  I      .766     .791     4.277     .286   .060000   .025000  -.1550000      .000      .000  
+841231 46065.00 I   .045647  .000773   .022839  .000394  I -.1568370  .0000972  1.8637 0.0705  I      .603     .791     4.351     .286   .055000   .025000  -.1568000      .000      .000  
+85 1 1 46066.00 I   .042277  .000661   .022841  .000334  I -.1586909  .0000681  1.8340 0.0537  I      .541     .791     4.509     .286   .051000   .025000  -.1585000      .000      .000  
+85 1 2 46067.00 I   .038867  .000583   .022960  .000652  I -.1604870  .0000457  1.7502 0.0353  I      .515     .868     4.639     .202   .047000   .025000  -.1601000      .000      .000  
+85 1 3 46068.00 I   .035355  .000649   .023183  .000627  I -.1621783  .0000189  1.6274 0.0247  I      .487    1.227     4.588     .298   .043000   .025000  -.1617000      .000      .000  
+85 1 4 46069.00 I   .031690  .000649   .023492  .000627  I -.1637360  .0000189  1.4872 0.0134  I      .490    1.227     4.301     .298   .039000   .025000  -.1631000      .000      .000  
+85 1 5 46070.00 I   .027848  .000649   .023864  .000627  I -.1651577  .0000189  1.3620 0.0152  I      .604    1.227     3.862     .298   .035000   .025000  -.1645000      .000      .000  
+85 1 6 46071.00 I   .023839  .000506   .024297  .000743  I -.1664779  .0000238  1.2905 0.0134  I      .873    1.227     3.451     .298   .030000   .025000  -.1657000      .000      .000  
+85 1 7 46072.00 I   .019696  .000554   .024802  .000650  I -.1677671  .0000191  1.3043 0.0150  I     1.278     .897     3.244     .298   .026000   .025000  -.1670000      .000      .000  
+85 1 8 46073.00 I   .015499  .000605   .025390  .000580  I -.1691195  .0000184  1.4173 0.0150  I     1.721     .323     3.317     .298   .022000   .025000  -.1683000      .000      .000  
+85 1 9 46074.00 I   .011336  .000467   .026070  .000580  I -.1706301  .0000231  1.6156 0.0148  I     2.021     .323     3.607     .298   .018000   .025000  -.1699000      .000      .000  
+85 110 46075.00 I   .007290  .000467   .026845  .000580  I -.1723661  .0000231  1.8601 0.0225  I     1.970     .323     3.983     .298   .014000   .026000  -.1716000      .000      .000  
+85 111 46076.00 I   .003381  .000603   .027703  .000682  I -.1743467  .0000386  2.0938 0.0231  I     1.522     .323     4.333     .298   .010000   .027000  -.1736000      .000      .000  
+85 112 46077.00 I  -.000400  .000734   .028635  .000648  I -.1765293  .0000399  2.2544 0.0401  I      .800     .297     4.626     .298   .006000   .027000  -.1758000      .000      .000  
+85 113 46078.00 I  -.004039  .000966   .029631  .000929  I -.1788167  .0000702  2.2987 0.0404  I      .003     .242     4.884     .298   .003000   .028000  -.1781000      .000      .000  
+85 114 46079.00 I  -.007510  .000966   .030685  .000929  I -.1810844  .0000702  2.2163 0.0504  I     -.671     .242     5.090     .298  -.001000   .029000  -.1804000      .000      .000  
+85 115 46080.00 I  -.010793  .001151   .031794  .001180  I -.1832128  .0000723  2.0245 0.0473  I    -1.063     .242     5.179     .298  -.005000   .029000  -.1825000      .000      .000  
+85 116 46081.00 I  -.013922  .000977   .032971  .001407  I -.1851106  .0000634  1.7642 0.0417  I    -1.137     .242     5.122     .298  -.009000   .030000  -.1844000      .000      .000  
+85 117 46082.00 I  -.016965  .000806   .034224  .001224  I -.1867379  .0000415  1.4940 0.0348  I     -.903     .840     4.914     .298  -.013000   .031000  -.1861000      .000      .000  
+85 118 46083.00 I  -.019994  .000510   .035558  .001247  I -.1881139  .0000287  1.2698 0.0258  I     -.402    1.022     4.564     .115  -.016000   .032000  -.1875000      .000      .000  
+85 119 46084.00 I  -.023076  .000534   .036972  .001211  I -.1893057  .0000307  1.1303 0.0210  I      .239    1.022     4.119     .115  -.020000   .033000  -.1888000      .000      .000  
+85 120 46085.00 I  -.026261  .000534   .038429  .001211  I -.1904083  .0000307  1.0923 0.0215  I      .831    1.022     3.671     .115  -.024000   .034000  -.1900000      .000      .000  
+85 121 46086.00 I  -.029573  .000969   .039922  .001113  I -.1915209  .0000300  1.1460 0.0178  I     1.249    1.022     3.320     .115  -.027000   .035000  -.1912000      .000      .000  
+85 122 46087.00 I  -.033034  .001011   .041461  .000798  I -.1927203  .0000180  1.2596 0.0168  I     1.475     .991     3.169     .235  -.031000   .036000  -.1925000      .000      .000  
+85 123 46088.00 I  -.036654  .001179   .043045  .000730  I -.1940474  .0000153  1.3951 0.0361  I     1.548     .838     3.278     .283  -.034000   .037000  -.1939000      .000      .000  
+85 124 46089.00 I  -.040417  .001179   .044650  .000730  I -.1955065  .0000699  1.5186 0.0378  I     1.472     .838     3.607     .283  -.037000   .039000  -.1955000      .000      .000  
+85 125 46090.00 I  -.044273  .001357   .046244  .000554  I -.1970727  .0000741  1.6063 0.0486  I     1.226     .838     4.012     .283  -.041000   .040000  -.1971000      .000      .000  
+85 126 46091.00 I  -.048135  .001206   .047808  .000669  I -.1987025  .0000676  1.6442 0.0465  I      .992     .838     4.190     .283  -.044000   .041000  -.1987000      .000      .000  
+85 127 46092.00 I  -.051928  .000513   .049331  .000692  I -.2003427  .0000561  1.6268 0.0515  I      .912     .658     4.128     .266  -.047000   .043000  -.2003000      .000      .000  
+85 128 46093.00 I  -.055578  .000601   .050805  .000943  I -.2019384  .0000776  1.5561 0.0479  I      .963     .322     4.026     .228  -.050000   .044000  -.2018000      .000      .000  
+85 129 46094.00 I  -.059013  .000601   .052219  .000943  I -.2034409  .0000776  1.4432 0.0500  I      .991     .190     3.978     .240  -.053000   .046000  -.2032000      .000      .000  
+85 130 46095.00 I  -.062163  .000541   .053572  .000990  I -.2048137  .0000631  1.2969 0.0507  I      .832     .190     3.961     .240  -.056000   .048000  -.2044000      .000      .000  
+85 131 46096.00 I  -.065008  .000600   .054912  .000960  I -.2060277  .0000652  1.1296 0.0414  I      .549     .190     3.906     .240  -.060000   .049000  -.2055000      .000      .000  
+85 2 1 46097.00 I  -.067595  .000540   .056280  .000887  I -.2070748  .0000537  0.9678 0.0470  I      .323     .281     3.793     .190  -.063000   .051000  -.2064000      .000      .000  
+85 2 2 46098.00 I  -.069992  .000651   .057704  .000975  I -.2079760  .0000677  0.8439 0.0381  I      .225     .348     3.626     .120  -.066000   .053000  -.2072000      .000      .000  
+85 2 3 46099.00 I  -.072265  .000521   .059200  .000715  I -.2087876  .0000541  0.7950 0.0433  I      .241     .348     3.383     .120  -.068000   .055000  -.2080000      .000      .000  
+85 2 4 46100.00 I  -.074472  .000521   .060763  .000715  I -.2096027  .0000541  0.8563 0.0335  I      .467     .348     3.090     .120  -.072000   .057000  -.2089000      .000      .000  
+85 2 5 46101.00 I  -.076633  .000541   .062402  .000671  I -.2105400  .0000395  1.0370 0.0317  I     1.034     .348     2.895     .120  -.075000   .059000  -.2101000      .000      .000  
+85 2 6 46102.00 I  -.078755  .000458   .064139  .000570  I -.2117058  .0000331  1.3049 0.0242  I     1.873     .268     2.916     .313  -.078000   .061000  -.2115000      .000      .000  
+85 2 7 46103.00 I  -.080874  .000425   .066000  .000564  I -.2131574  .0000278  1.5955 0.0256  I     2.619     .150     3.081     .426  -.082000   .063000  -.2132000      .000      .000  
+85 2 8 46104.00 I  -.083041  .000425   .068013  .000564  I -.2148787  .0000390  1.8319 0.0291  I     2.885     .150     3.215     .426  -.085000   .065000  -.2151000      .000      .000  
+85 2 9 46105.00 I  -.085300  .000318   .070194  .000588  I -.2167819  .0000511  1.9503 0.0300  I     2.599     .150     3.245     .426  -.088000   .067000  -.2172000      .000      .000  
+85 210 46106.00 I  -.087660  .000368   .072510  .000552  I -.2187311  .0000455  1.9239 0.0316  I     2.041     .150     3.244     .426  -.091000   .069000  -.2193000      .000      .000  
+85 211 46107.00 I  -.090126  .000423   .074917  .000406  I -.2205888  .0000373  1.7748 0.0338  I     1.506     .252     3.328     .394  -.094000   .071000  -.2212000      .000      .000  
+85 212 46108.00 I  -.092701  .000453   .077370  .000500  I -.2222578  .0000500  1.5570 0.0312  I     1.084     .323     3.526     .360  -.097000   .073000  -.2230000      .000      .000  
+85 213 46109.00 I  -.095382  .000453   .079814  .000500  I -.2237016  .0000500  1.3356 0.0326  I      .759     .323     3.752     .360  -.100000   .075000  -.2244000      .000      .000  
+85 214 46110.00 I  -.098160  .000487   .082196  .000461  I -.2249468  .0000417  1.1676 0.0278  I      .557     .323     3.889     .360  -.103000   .077000  -.2258000      .000      .000  
+85 215 46111.00 I  -.101037  .000553   .084501  .000414  I -.2260661  .0000244  1.0873 0.0243  I      .559     .323     3.866     .360  -.106000   .080000  -.2269000      .000      .000  
+85 216 46112.00 I  -.104051  .000488   .086729  .000436  I -.2271542  .0000248  1.1053 0.0201  I      .807     .234     3.671     .308  -.109000   .082000  -.2280000      .000      .000  
+85 217 46113.00 I  -.107245  .000471   .088881  .000469  I -.2283057  .0000319  1.2104 0.0202  I     1.223     .791     3.349     .246  -.112000   .084000  -.2291000      .000      .000  
+85 218 46114.00 I  -.110647  .000471   .090957  .000469  I -.2295951  .0000319  1.3758 0.0226  I     1.638     .791     3.020     .246  -.114000   .086000  -.2304000      .000      .000  
+85 219 46115.00 I  -.114250  .000471   .092963  .000469  I -.2310654  .0000319  1.5656 0.0225  I     1.941     .791     2.836     .246  -.117000   .088000  -.2316000      .000      .000  
+85 220 46116.00 I  -.118004  .000441   .094931  .000468  I -.2327217  .0000318  1.7415 0.0211  I     2.152     .791     2.870     .246  -.119000   .091000  -.2331000      .000      .000  
+85 221 46117.00 I  -.121853  .000475   .096905  .000480  I -.2345343  .0000275  1.8750 0.0208  I     2.339     .132     3.038     .214  -.122000   .093000  -.2347000      .000      .000  
+85 222 46118.00 I  -.125726  .000498   .098912  .000533  I -.2364516  .0000267  1.9483 0.0176  I     2.508     .172     3.169     .176  -.125000   .096000  -.2364000      .000      .000  
+85 223 46119.00 I  -.129547  .000486   .100973  .000651  I -.2384076  .0000221  1.9518 0.0173  I     2.621     .172     3.173     .176  -.127000   .098000  -.2382000      .000      .000  
+85 224 46120.00 I  -.133243  .000486   .103110  .000651  I -.2403331  .0000221  1.8892 0.0161  I     2.680     .172     3.124     .176  -.130000   .101000  -.2400000      .000      .000  
+85 225 46121.00 I  -.136777  .000479   .105344  .000595  I -.2421679  .0000235  1.7725 0.0148  I     2.716     .172     3.115     .176  -.132000   .103000  -.2418000      .000      .000  
+85 226 46122.00 I  -.140144  .000505   .107701  .000510  I -.2438655  .0000196  1.6182 0.0146  I     2.673     .404     3.108     .219  -.135000   .106000  -.2435000      .000      .000  
+85 227 46123.00 I  -.143341  .000496   .110196  .000553  I -.2453988  .0000173  1.4474 0.0132  I     2.520     .544     2.986     .255  -.138000   .109000  -.2450000      .000      .000  
+85 228 46124.00 I  -.146379  .000496   .112815  .000553  I -.2467622  .0000177  1.2824 0.0124  I     2.441     .544     2.741     .255  -.140000   .111000  -.2464000      .000      .000  
+85 3 1 46125.00 I  -.149266  .000524   .115542  .000292  I -.2479762  .0000177  1.1554 0.0170  I     2.681     .544     2.498     .255  -.143000   .114000  -.2477000      .000      .000  
+85 3 2 46126.00 I  -.152035  .000502   .118359  .000316  I -.2490984  .0000291  1.1048 0.0163  I     3.134     .544     2.321     .255  -.145000   .117000  -.2489000      .000      .000  
+85 3 3 46127.00 I  -.154732  .000553   .121245  .000525  I -.2502217  .0000274  1.1621 0.0224  I     3.441    1.297     2.091     .324  -.148000   .119000  -.2501000      .000      .000  
+85 3 4 46128.00 I  -.157401  .000540   .124178  .000659  I -.2514646  .0000341  1.3455 0.0219  I     3.475    1.464     1.706     .320  -.150000   .122000  -.2514000      .000      .000  
+85 3 5 46129.00 I  -.160087  .000540   .127139  .000659  I -.2529506  .0000341  1.6427 0.0240  I     3.458    1.464     1.324     .320  -.153000   .125000  -.2530000      .000      .000  
+85 3 6 46130.00 I  -.162838  .000540   .130105  .000659  I -.2547729  .0000339  2.0087 0.0240  I     3.513    1.464     1.232     .320  -.155000   .128000  -.2549000      .000      .000  
+85 3 7 46131.00 I  -.165701  .000540   .133042  .000659  I -.2569614  .0000339  2.3551 0.0293  I     3.527    1.464     1.484     .320  -.158000   .131000  -.2572000      .000      .000  
+85 3 8 46132.00 I  -.168632  .000605   .135964  .000693  I -.2594436  .0000478  2.5832 0.0306  I     3.198    1.464     1.803     .320  -.160000   .134000  -.2597000      .000      .000  
+85 3 9 46133.00 I  -.171568  .000606   .138910  .000651  I -.2620707  .0000509  2.6402 0.1376  I     2.450     .454     1.910     .298  -.162000   .137000  -.2623000      .000      .000  
+85 310 46134.00 I  -.174442  .000781   .141921  .000640  I -.2646679  .0002710  2.5291 0.1002  I     1.659     .791     1.807     .298  -.164000   .141000  -.2649000      .000      .000  
+85 311 46135.00 I  -.177192  .001034   .145035  .000541  I -.2670902  .0001939  2.3020 0.1438  I     1.208     .791     1.694     .298  -.166000   .144000  -.2673000      .000      .000  
+85 312 46136.00 I  -.179774  .000885   .148284  .000459  I -.2692583  .0000962  2.0341 0.1015  I     1.145     .791     1.676     .298  -.168000   .147000  -.2693000      .000      .000  
+85 313 46137.00 I  -.182174  .000916   .151687  .000516  I -.2711697  .0000602  1.8001 0.0516  I     1.296    1.168     1.687     .482  -.170000   .150000  -.2712000      .000      .000  
+85 314 46138.00 I  -.184390  .000946   .155262  .000486  I -.2728870  .0000372  1.6515 0.0354  I     1.542    1.168     1.644     .482  -.172000   .154000  -.2728000      .000      .000  
+85 315 46139.00 I  -.186475  .000946   .159014  .000486  I -.2745066  .0000372  1.6045 0.0200  I     1.916    1.168     1.546     .482  -.174000   .157000  -.2743000      .000      .000  
+85 316 46140.00 I  -.188489  .000946   .162944  .000486  I -.2761266  .0000147  1.6494 0.0202  I     2.525    1.168     1.418     .482  -.176000   .161000  -.2759000      .000      .000  
+85 317 46141.00 I  -.190425  .000893   .167016  .000559  I -.2778277  .0000159  1.7612 0.0102  I     3.359    1.168     1.263     .482  -.178000   .164000  -.2775000      .000      .000  
+85 318 46142.00 I  -.192241  .000921   .171174  .000579  I -.2796601  .0000141  1.9063 0.0186  I     4.232    1.031     1.117     .342  -.179000   .168000  -.2793000      .000      .000  
+85 319 46143.00 I  -.193893  .000979   .175364  .000601  I -.2816404  .0000336  2.0518 0.0182  I     4.892     .872     1.094     .298  -.181000   .171000  -.2813000      .000      .000  
+85 320 46144.00 I  -.195330  .000979   .179529  .000601  I -.2837540  .0000336  2.1683 0.0238  I     5.209     .872     1.287     .298  -.182000   .175000  -.2834000      .000      .000  
+85 321 46145.00 I  -.196459  .000979   .183619  .000601  I -.2859606  .0000336  2.2358 0.0364  I     5.231     .872     1.619     .298  -.184000   .178000  -.2856000      .000      .000  
+85 322 46146.00 I  -.197257  .000809   .187641  .000525  I -.2882065  .0000645  2.2460 0.0361  I     5.113     .872     1.860     .298  -.185000   .182000  -.2879000      .000      .000  
+85 323 46147.00 I  -.197770  .000660   .191620  .000547  I -.2904336  .0000640  2.1990 0.0608  I     4.972     .761     1.862     .162  -.187000   .186000  -.2901000      .000      .000  
+85 324 46148.00 I  -.198078  .000525   .195575  .000323  I -.2925881  .0001032  2.1027 0.0572  I     4.868     .630     1.700     .227  -.188000   .189000  -.2923000      .000      .000  
+85 325 46149.00 I  -.198263  .000527   .199521  .000490  I -.2946278  .0000948  1.9730 0.0732  I     4.822     .630     1.537     .227  -.189000   .193000  -.2944000      .000      .000  
+85 326 46150.00 I  -.198402  .000527   .203478  .000490  I -.2965273  .0001039  1.8231 0.0624  I     4.771     .630     1.408     .227  -.190000   .197000  -.2963000      .000      .000  
+85 327 46151.00 I  -.198546  .000463   .207460  .000447  I -.2982722  .0000813  1.6677 0.0585  I     4.651     .630     1.217     .227  -.192000   .200000  -.2980000      .000      .000  
+85 328 46152.00 I  -.198758  .000420   .211475  .000404  I -.2998687  .0000538  1.5303 0.0501  I     4.587     .499      .966     .276  -.192000   .204000  -.2996000      .000      .000  
+85 329 46153.00 I  -.199088  .000586   .215521  .000492  I -.3013471  .0000585  1.4357 0.0500  I     4.795     .317      .815     .318  -.193000   .208000  -.3011000      .000      .000  
+85 330 46154.00 I  -.199546  .000586   .219579  .000492  I -.3027625  .0000843  1.4084 0.0525  I     5.236     .317      .840     .318  -.194000   .212000  -.3026000      .000      .000  
+85 331 46155.00 I  -.200125  .000475   .223631  .000373  I -.3041967  .0000871  1.4794 0.0541  I     5.587     .317      .850     .318  -.195000   .216000  -.3041000      .000      .000  
+85 4 1 46156.00 I  -.200799  .000464   .227676  .000321  I -.3057583  .0000679  1.6615 0.0522  I     5.687     .317      .609     .318  -.196000   .220000  -.3057000      .000      .000  
+85 4 2 46157.00 I  -.201506  .000725   .231711  .000349  I -.3075487  .0000574  1.9305 0.0463  I     5.794     .575      .202     .228  -.197000   .223000  -.3077000      .000      .000  
+85 4 3 46158.00 I  -.202157  .000859   .235725  .000364  I -.3096280  .0000629  2.2257 0.0429  I     6.171     .748     -.010     .298  -.197000   .227000  -.3099000      .000      .000  
+85 4 4 46159.00 I  -.202656  .000848   .239703  .000316  I -.3119810  .0000639  2.4636 0.0367  I     6.598     .748      .176     .298  -.198000   .231000  -.3124000      .000      .000  
+85 4 5 46160.00 I  -.202894  .000848   .243632  .000316  I -.3145091  .0000378  2.5631 0.0359  I     6.582     .748      .542     .298  -.198000   .235000  -.3151000      .000      .000  
+85 4 6 46161.00 I  -.202887  .000768   .247510  .000284  I -.3170467  .0000326  2.4811 0.0226  I     6.007     .748      .734     .298  -.198000   .239000  -.3177000      .000      .000  
+85 4 7 46162.00 I  -.202694  .000661   .251352  .000454  I -.3194193  .0000249  2.2430 0.0211  I     5.270     .583      .674     .298  -.198000   .243000  -.3202000      .000      .000  
+85 4 8 46163.00 I  -.202363  .000391   .255174  .000517  I -.3215064  .0000268  1.9261 0.0191  I     4.817     .346      .549     .113  -.198000   .247000  -.3224000      .000      .000  
+85 4 9 46164.00 I  -.201945  .000312   .258993  .000556  I -.3232768  .0000291  1.6252 0.0190  I     4.747     .346      .484     .113  -.198000   .251000  -.3242000      .000      .000  
+85 410 46165.00 I  -.201479  .000312   .262823  .000556  I -.3247880  .0000268  1.4177 0.0187  I     4.879     .346      .410     .113  -.198000   .255000  -.3258000      .000      .000  
+85 411 46166.00 I  -.200979  .000347   .266659  .000499  I -.3261551  .0000235  1.3387 0.0179  I     5.000     .346      .264     .113  -.198000   .259000  -.3272000      .000      .000  
+85 412 46167.00 I  -.200452  .000335   .270482  .000554  I -.3275061  .0000237  1.3817 0.0176  I     5.018     .552      .147     .106  -.198000   .263000  -.3286000      .000      .000  
+85 413 46168.00 I  -.199907  .000346   .274270  .000588  I -.3289482  .0000261  1.5139 0.0175  I     5.002     .700      .178     .298  -.198000   .267000  -.3300000      .000      .000  
+85 414 46169.00 I  -.199368  .000346   .277987  .000588  I -.3305500  .0000258  1.6946 0.0194  I     5.118     .700      .305     .298  -.198000   .271000  -.3316000      .000      .000  
+85 415 46170.00 I  -.198855  .000438   .281599  .000559  I -.3323403  .0000288  1.8842 0.0184  I     5.435     .700      .385     .298  -.198000   .275000  -.3333000      .000      .000  
+85 416 46171.00 I  -.198376  .000412   .285115  .000517  I -.3343101  .0000263  2.0489 0.0197  I     5.792     .700      .427     .298  -.198000   .279000  -.3352000      .000      .000  
+85 417 46172.00 I  -.197935  .000555   .288574  .000471  I -.3364221  .0000268  2.1657 0.0209  I     5.963     .504      .559     .125  -.197000   .283000  -.3372000      .000      .000  
+85 418 46173.00 I  -.197529  .000627   .292023  .000397  I -.3386206  .0000324  2.2197 0.0212  I     5.914     .138      .798     .147  -.197000   .286000  -.3393000      .000      .000  
+85 419 46174.00 I  -.197147  .000661   .295507  .000319  I -.3408390  .0000329  2.2060 0.0237  I     5.798     .138      .988     .147  -.197000   .290000  -.3414000      .000      .000  
+85 420 46175.00 I  -.196748  .000661   .299057  .000319  I -.3430114  .0000346  2.1286 0.0223  I     5.751     .138      .996     .147  -.196000   .294000  -.3434000      .000      .000  
+85 421 46176.00 I  -.196341  .000625   .302665  .000300  I -.3450778  .0000301  1.9959 0.0224  I     5.808     .138      .823     .147  -.196000   .298000  -.3454000      .000      .000  
+85 422 46177.00 I  -.195970  .000611   .306310  .000325  I -.3469911  .0000284  1.8268 0.0213  I     5.945     .405      .577     .279  -.195000   .302000  -.3472000      .000      .000  
+85 423 46178.00 I  -.195653  .000572   .309964  .000314  I -.3487283  .0000302  1.6483 0.0269  I     6.100     .606      .270     .424  -.194000   .305000  -.3489000      .000      .000  
+85 424 46179.00 I  -.195403  .000682   .313600  .000313  I -.3502938  .0000457  1.4881 0.0327  I     6.220     .606     -.280     .424  -.193000   .309000  -.3504000      .000      .000  
+85 425 46180.00 I  -.195218  .000682   .317194  .000313  I -.3517206  .0000579  1.3763 0.0354  I     6.345     .606    -1.178     .424  -.193000   .313000  -.3518000      .000      .000  
+85 426 46181.00 I  -.195070  .000684   .320749  .000304  I -.3530704  .0000542  1.3367 0.0397  I     6.419     .606    -1.710     .424  -.192000   .316000  -.3532000      .000      .000  
+85 427 46182.00 I  -.194934  .000684   .324277  .000304  I -.3544233  .0000542  1.3851 0.0592  I     6.466     .537    -1.571     .332  -.191000   .320000  -.3546000      .000      .000  
+85 428 46183.00 I  -.194754  .000908   .327794  .000206  I -.3558732  .0001053  1.5313 0.0602  I     6.454     .524    -1.093     .306  -.190000   .324000  -.3561000      .000      .000  
+85 429 46184.00 I  -.194430  .000908   .331318  .000206  I -.3575151  .0001074  1.7653 0.0692  I     6.334     .415     -.701     .111  -.189000   .327000  -.3578000      .000      .000  
+85 430 46185.00 I  -.193875  .000747   .334866  .000814  I -.3594226  .0000898  2.0558 0.0617  I     6.266     .415     -.602     .111  -.188000   .331000  -.3599000      .000      .000  
+85 5 1 46186.00 I  -.193064  .000678   .338446  .000829  I -.3616246  .0000609  2.3399 0.0533  I     6.366     .415     -.563     .111  -.187000   .335000  -.3622000      .000      .000  
+85 5 2 46187.00 I  -.192022  .000675   .342059  .000767  I -.3640725  .0000575  2.5341 0.0458  I     6.471     .304     -.335     .162  -.185000   .338000  -.3648000      .000      .000  
+85 5 3 46188.00 I  -.190796  .000665   .345705  .000873  I -.3666421  .0000684  2.5761 0.0413  I     6.279     .115     -.010     .200  -.184000   .342000  -.3675000      .000      .000  
+85 5 4 46189.00 I  -.189423  .001150   .349371  .000892  I -.3691694  .0000594  2.4522 0.0462  I     5.732     .115      .155     .200  -.183000   .345000  -.3702000      .000      .000  
+85 5 5 46190.00 I  -.187921  .001056   .353018  .001040  I -.3715021  .0000621  2.1949 0.0403  I     5.101     .115      .103     .200  -.181000   .349000  -.3725000      .000      .000  
+85 5 6 46191.00 I  -.186343  .001073   .356639  .000803  I -.3735395  .0000546  1.8783 0.0383  I     4.715     .116     -.028     .188  -.180000   .352000  -.3745000      .000      .000  
+85 5 7 46192.00 I  -.184758  .000881   .360244  .000583  I -.3752701  .0000448  1.5959 0.0344  I     4.668     .697     -.177     .291  -.178000   .355000  -.3761000      .000      .000  
+85 5 8 46193.00 I  -.183180  .000955   .363836  .000547  I -.3767639  .0000419  1.4114 0.0300  I     4.843     .855     -.456     .314  -.177000   .359000  -.3775000      .000      .000  
+85 5 9 46194.00 I  -.181540  .000955   .367403  .000547  I -.3781335  .0000399  1.3487 0.0290  I     5.042     .855     -.895     .314  -.175000   .362000  -.3788000      .000      .000  
+85 510 46195.00 I  -.179759  .000595   .370913  .000608  I -.3794931  .0000400  1.3814 0.0273  I     5.124     .855    -1.102     .314  -.174000   .365000  -.3801000      .000      .000  
+85 511 46196.00 I  -.177855  .000610   .374344  .000604  I -.3809154  .0000372  1.4713 0.0263  I     5.190     .855     -.987     .314  -.172000   .369000  -.3815000      .000      .000  
+85 512 46197.00 I  -.175860  .000566   .377706  .000403  I -.3824452  .0000342  1.5898 0.0242  I     5.403     .891     -.726     .327  -.170000   .372000  -.3830000      .000      .000  
+85 513 46198.00 I  -.173806  .000324   .381008  .000453  I -.3840934  .0000309  1.7035 0.0217  I     5.748     .728     -.464     .270  -.169000   .375000  -.3847000      .000      .000  
+85 514 46199.00 I  -.171735  .000345   .384244  .000490  I -.3858435  .0000268  1.7909 0.0205  I     5.977     .662     -.260     .251  -.167000   .378000  -.3864000      .000      .000  
+85 515 46200.00 I  -.169684  .000495   .387395  .000472  I -.3876593  .0000270  1.8304 0.0191  I     5.669     .662      .011     .251  -.165000   .382000  -.3883000      .000      .000  
+85 516 46201.00 I  -.167665  .000623   .390431  .000483  I -.3894825  .0000271  1.8043 0.0191  I     4.864     .662      .368     .251  -.163000   .385000  -.3901000      .000      .000  
+85 517 46202.00 I  -.165657  .000812   .393334  .000462  I -.3912474  .0000270  1.7167 0.0191  I     4.003     .530      .615     .198  -.161000   .388000  -.3918000      .000      .000  
+85 518 46203.00 I  -.163573  .000909   .396137  .000483  I -.3928999  .0000268  1.5811 0.0196  I     3.394     .319      .551     .298  -.159000   .391000  -.3935000      .000      .000  
+85 519 46204.00 I  -.161262  .000865   .398882  .000537  I -.3943990  .0000284  1.4138 0.0231  I     3.070     .319      .192     .298  -.157000   .394000  -.3950000      .000      .000  
+85 520 46205.00 I  -.158584  .001205   .401605  .000631  I -.3957248  .0000377  1.2384 0.0271  I     2.955     .219     -.236     .298  -.155000   .397000  -.3964000      .000      .000  
+85 521 46206.00 I  -.155578  .001130   .404325  .000641  I -.3968812  .0000461  1.0792 0.0269  I     2.968     .219     -.514     .298  -.153000   .400000  -.3975000      .000      .000  
+85 522 46207.00 I  -.152367  .001011   .407057  .000637  I -.3978971  .0000385  0.9618 0.0326  I     3.020     .160     -.613     .298  -.151000   .403000  -.3986000      .000      .000  
+85 523 46208.00 I  -.149070  .000702   .409800  .000652  I -.3988270  .0000460  0.9109 0.0350  I     3.030     .791     -.644     .298  -.149000   .406000  -.3995000      .000      .000  
+85 524 46209.00 I  -.145811  .000702   .412542  .000652  I -.3997466  .0000584  0.9431 0.0402  I     3.009     .791     -.692     .298  -.147000   .409000  -.4005000      .000      .000  
+85 525 46210.00 I  -.142687  .000757   .415266  .000582  I -.4007451  .0000660  1.0710 0.0428  I     3.070     .791     -.751     .298  -.144000   .412000  -.4015000      .000      .000  
+85 526 46211.00 I  -.139715  .000679   .417959  .000555  I -.4019197  .0000625  1.2922 0.0427  I     3.289     .791     -.800     .298  -.142000   .415000  -.4027000      .000      .000  
+85 527 46212.00 I  -.136898  .000376   .420617  .000533  I -.4033516  .0000542  1.5795 0.0463  I     3.602     .124     -.845     .298  -.140000   .418000  -.4042000      .000      .000  
+85 528 46213.00 I  -.134239  .000421   .423234  .000555  I -.4050843  .0000684  1.8834 0.0440  I     3.868     .165     -.851     .298  -.137000   .420000  -.4059000      .000      .000  
+85 529 46214.00 I  -.131710  .000421   .425794  .000555  I -.4071018  .0000693  2.1375 0.0454  I     3.957     .165     -.718     .298  -.135000   .423000  -.4079000      .000      .000  
+85 530 46215.00 I  -.129268  .000393   .428277  .000511  I -.4093187  .0000598  2.2698 0.0454  I     3.789     .165     -.395     .298  -.132000   .426000  -.4100000      .000      .000  
+85 531 46216.00 I  -.126862  .000306   .430697  .000518  I -.4115847  .0000588  2.2321 0.0375  I     3.370     .165     -.014     .298  -.130000   .429000  -.4122000      .000      .000  
+85 6 1 46217.00 I  -.124466  .000373   .433064  .000470  I -.4137295  .0000453  2.0344 0.0409  I     2.877     .194      .217     .179  -.127000   .431000  -.4142000      .000      .000  
+85 6 2 46218.00 I  -.122062  .000396   .435383  .000374  I -.4156205  .0000568  1.7378 0.0370  I     2.564     .219      .234     .239  -.125000   .434000  -.4159000      .000      .000  
+85 6 3 46219.00 I  -.119627  .000383   .437662  .000411  I -.4172008  .0000586  1.4282 0.0405  I     2.540     .219      .149     .239  -.122000   .436000  -.4174000      .000      .000  
+85 6 4 46220.00 I  -.117138  .000383   .439906  .000411  I -.4185018  .0000578  1.1934 0.0409  I     2.724     .219      .039     .239  -.119000   .439000  -.4186000      .000      .000  
+85 6 5 46221.00 I  -.114629  .000501   .442113  .000485  I -.4196298  .0000570  1.0854 0.0361  I     3.011     .219     -.165     .239  -.117000   .441000  -.4198000      .000      .000  
+85 6 6 46222.00 I  -.112156  .000453   .444281  .000425  I -.4207131  .0000434  1.0991 0.0375  I     3.301     .172     -.511     .180  -.114000   .443000  -.4209000      .000      .000  
+85 6 7 46223.00 I  -.109765  .000456   .446419  .000424  I -.4218551  .0000487  1.1943 0.0331  I     3.412     .106     -.837     .298  -.112000   .445000  -.4221000      .000      .000  
+85 6 8 46224.00 I  -.107492  .000456   .448543  .000424  I -.4231118  .0000500  1.3199 0.0339  I     3.171     .106     -.907     .298  -.109000   .447000  -.4234000      .000      .000  
+85 6 9 46225.00 I  -.105357  .000437   .450662  .000414  I -.4244918  .0000473  1.4362 0.0322  I     2.626     .106     -.699     .298  -.106000   .449000  -.4248000      .000      .000  
+85 610 46226.00 I  -.103325  .000397   .452758  .000381  I -.4259725  .0000406  1.5174 0.0263  I     2.037     .106     -.430     .298  -.104000   .451000  -.4263000      .000      .000  
+85 611 46227.00 I  -.101352  .000405   .454805  .000470  I -.4275097  .0000230  1.5478 0.0276  I     1.575     .379     -.279     .175  -.101000   .453000  -.4279000      .000      .000  
+85 612 46228.00 I  -.099393  .000221   .456780  .000478  I -.4290493  .0000374  1.5218 0.0217  I     1.164     .525     -.203     .233  -.098000   .455000  -.4295000      .000      .000  
+85 613 46229.00 I  -.097404  .000221   .458668  .000478  I -.4305347  .0000367  1.4401 0.0254  I      .668     .525     -.062     .233  -.095000   .456000  -.4310000      .000      .000  
+85 614 46230.00 I  -.095335  .000221   .460461  .000478  I -.4319136  .0000344  1.3107 0.0279  I      .128     .525      .145     .233  -.093000   .458000  -.4324000      .000      .000  
+85 615 46231.00 I  -.093149  .000540   .462163  .000512  I -.4331459  .0000421  1.1507 0.0351  I     -.309     .525      .237     .233  -.090000   .460000  -.4337000      .000      .000  
+85 616 46232.00 I  -.090825  .000672   .463779  .000625  I -.4342128  .0000612  0.9842 0.0265  I     -.576     .742      .084     .327  -.087000   .462000  -.4348000      .000      .000  
+85 617 46233.00 I  -.088349  .000444   .465312  .000331  I -.4351203  .0000321  0.8359 0.0342  I     -.683    1.046     -.247     .396  -.084000   .464000  -.4358000      .000      .000  
+85 618 46234.00 I  -.085711  .000446   .466761  .000331  I -.4359015  .0000305  0.7383 0.0228  I     -.629     .986     -.591     .363  -.082000   .465000  -.4365000      .000      .000  
+85 619 46235.00 I  -.082936  .000446   .468118  .000331  I -.4366149  .0000325  0.6946 0.0228  I     -.406     .986     -.840     .363  -.079000   .467000  -.4372000      .000      .000  
+85 620 46236.00 I  -.080122  .000425   .469408  .000334  I -.4373028  .0000340  0.6872 0.0243  I      .100     .986     -.766     .363  -.076000   .468000  -.4378000      .000      .000  
+85 621 46237.00 I  -.077361  .000467   .470667  .000289  I -.4380153  .0000362  0.7579 0.0223  I      .599     .809     -.619     .297  -.073000   .470000  -.4385000      .000      .000  
+85 622 46238.00 I  -.074610  .000377   .471903  .000153  I -.4388551  .0000290  0.9381 0.0267  I      .909     .753     -.661     .221  -.070000   .471000  -.4393000      .000      .000  
+85 623 46239.00 I  -.071793  .000377   .473116  .000153  I -.4399191  .0000392  1.2010 0.0565  I     1.188     .465     -.790     .137  -.067000   .473000  -.4403000      .000      .000  
+85 624 46240.00 I  -.068839  .000698   .474303  .000403  I -.4412630  .0001093  1.4817 0.0499  I     1.488     .190     -.818     .298  -.064000   .474000  -.4416000      .000      .000  
+85 625 46241.00 I  -.065753  .000784   .475472  .000466  I -.4428618  .0000918  1.7003 0.0682  I     1.671     .190     -.649     .298  -.062000   .475000  -.4431000      .000      .000  
+85 626 46242.00 I  -.062576  .000731   .476631  .000452  I -.4446257  .0000815  1.8053 0.0647  I     1.531     .398     -.300     .169  -.058000   .477000  -.4447000      .000      .000  
+85 627 46243.00 I  -.059354  .000657   .477788  .000537  I -.4464248  .0000913  1.7675 0.0607  I     1.000     .530      .123     .220  -.056000   .478000  -.4463000      .000      .000  
+85 628 46244.00 I  -.056153  .000657   .478938  .000537  I -.4481124  .0000899  1.5850 0.0573  I      .250     .530      .479     .220  -.053000   .479000  -.4478000      .000      .000  
+85 629 46245.00 I  -.053034  .000657   .480065  .000537  I -.4495582  .0000692  1.2928 0.0554  I     -.387     .530      .652     .220  -.050000   .480000  -.4491000      .000      .000  
+85 630 46246.00 I  -.050009  .000610   .481156  .000405  I -.4506836  .0000646  0.9573 0.0475  I     -.679     .530      .642     .220  -.047000   .481000  -.4501000      .000      .000  
+85 7 1 46247.00 I  -.047069  .000610   .482203  .000405  I  .5485152  .0000650  0.6579 0.0516  I     -.633     .581      .540     .193  -.044000   .483000   .5492000      .000      .000  
+85 7 2 46248.00 I  -.044201  .000451   .483202  .000152  I  .5479681  .0000806  0.4560 0.0494  I     -.425     .628      .413     .161  -.041000   .484000   .5486000      .000      .000  
+85 7 3 46249.00 I  -.041392  .000502   .484143  .000204  I  .5475631  .0000743  0.3744 0.0568  I     -.210     .628      .237     .161  -.038000   .484000   .5482000      .000      .000  
+85 7 4 46250.00 I  -.038612  .000621   .485025  .000419  I  .5471829  .0000801  0.4020 0.0495  I     -.067     .628     -.021     .161  -.034000   .485000   .5477000      .000      .000  
+85 7 5 46251.00 I  -.035784  .000585   .485851  .000489  I  .5467352  .0000655  0.5011 0.0503  I     -.070     .518     -.285     .143  -.031000   .486000   .5472000      .000      .000  
+85 7 6 46252.00 I  -.032827  .000539   .486625  .000464  I  .5461737  .0000607  0.6211 0.0450  I     -.338     .578     -.419     .261  -.028000   .487000   .5465000      .000      .000  
+85 7 7 46253.00 I  -.029687  .000542   .487346  .000500  I  .5455003  .0000616  0.7193 0.0466  I     -.908     .542     -.411     .313  -.025000   .488000   .5458000      .000      .000  
+85 7 8 46254.00 I  -.026333  .000542   .488016  .000500  I  .5447512  .0000708  0.7694 0.0465  I    -1.633     .542     -.397     .313  -.022000   .489000   .5449000      .000      .000  
+85 7 9 46255.00 I  -.022800  .000492   .488669  .000542  I  .5439789  .0000697  0.7675 0.0433  I    -2.329     .542     -.374     .313  -.019000   .489000   .5440000      .000      .000  
+85 710 46256.00 I  -.019130  .000519   .489314  .000511  I  .5432315  .0000499  0.7198 0.0432  I    -2.910     .542     -.288     .313  -.016000   .490000   .5431000      .000      .000  
+85 711 46257.00 I  -.015371  .000463   .489951  .000569  I  .5425526  .0000512  0.6321 0.0379  I    -3.406     .682     -.087     .281  -.013000   .490000   .5423000      .000      .000  
+85 712 46258.00 I  -.011572  .000538   .490575  .000628  I  .5419778  .0000571  0.5127 0.0383  I    -3.850     .705      .168     .153  -.010000   .491000   .5416000      .000      .000  
+85 713 46259.00 I  -.007792  .000538   .491171  .000628  I  .5415318  .0000571  0.3793 0.0369  I    -4.185     .705      .273     .153  -.006000   .492000   .5411000      .000      .000  
+85 714 46260.00 I  -.004082  .000538   .491721  .000628  I  .5412161  .0000466  0.2553 0.0366  I    -4.296     .705      .059     .153  -.003000   .492000   .5408000      .000      .000  
+85 715 46261.00 I  -.000464  .000526   .492239  .000659  I  .5410125  .0000457  0.1576 0.0302  I    -4.144     .705     -.401     .153   .000000   .493000   .5405000      .000      .000  
+85 716 46262.00 I   .003063  .000533   .492745  .000573  I  .5408855  .0000384  0.1062 0.0297  I    -3.766     .636     -.865     .112   .003000   .493000   .5404000      .000      .000  
+85 717 46263.00 I   .006507  .000674   .493256  .000546  I  .5407766  .0000381  0.1253 0.0268  I    -3.285     .557    -1.122     .298   .006000   .493000   .5403000      .000      .000  
+85 718 46264.00 I   .009872  .000603   .493788  .000595  I  .5406063  .0000375  0.2303 0.0297  I    -2.914     .557    -1.097     .298   .010000   .494000   .5401000      .000      .000  
+85 719 46265.00 I   .013176  .000603   .494340  .000595  I  .5402860  .0000455  0.4254 0.0285  I    -2.846     .509     -.871     .298   .013000   .494000   .5398000      .000      .000  
+85 720 46266.00 I   .016429  .000573   .494873  .000572  I  .5397323  .0000430  0.6900 0.0301  I    -3.036     .509     -.635     .298   .016000   .494000   .5393000      .000      .000  
+85 721 46267.00 I   .019647  .000631   .495332  .000533  I  .5389012  .0000394  0.9691 0.0307  I    -3.290     .732     -.527     .519   .019000   .495000   .5386000      .000      .000  
+85 722 46268.00 I   .022886  .000672   .495665  .000535  I  .5378134  .0000437  1.1903 0.0301  I    -3.438     .829     -.549     .677   .023000   .495000   .5375000      .000      .000  
+85 723 46269.00 I   .026158  .000546   .495886  .000518  I  .5365587  .0000456  1.2973 0.0315  I    -3.518     .829     -.583     .677   .026000   .495000   .5363000      .000      .000  
+85 724 46270.00 I   .029459  .000557   .496025  .000563  I  .5352637  .0000453  1.2695 0.0354  I    -3.755     .829     -.485     .677   .029000   .495000   .5351000      .000      .000  
+85 725 46271.00 I   .032763  .000501   .496114  .000441  I  .5340619  .0000542  1.1149 0.0428  I    -4.208     .971     -.295     .804   .033000   .495000   .5339000      .000      .000  
+85 726 46272.00 I   .036041  .000556   .496176  .000490  I  .5330642  .0000726  0.8694 0.0533  I    -4.758     .662     -.117     .521   .036000   .495000   .5329000      .000      .000  
+85 727 46273.00 I   .039272  .000573   .496233  .000556  I  .5323336  .0000918  0.5925 0.0586  I    -5.211     .312     -.007     .298   .039000   .495000   .5321000      .000      .000  
+85 728 46274.00 I   .042463  .000573   .496285  .000556  I  .5318666  .0000920  0.3534 0.0640  I    -5.413     .312      .030     .298   .043000   .495000   .5316000      .000      .000  
+85 729 46275.00 I   .045629  .000573   .496323  .000556  I  .5315935  .0000892  0.2140 0.0608  I    -5.330     .312      .010     .298   .046000   .495000   .5313000      .000      .000  
+85 730 46276.00 I   .048765  .000639   .496335  .000462  I  .5313965  .0000794  0.2007 0.0557  I    -5.056     .312     -.077     .298   .049000   .495000   .5310000      .000      .000  
+85 731 46277.00 I   .051872  .000698   .496310  .000632  I  .5311553  .0000668  0.2978 0.0445  I    -4.763     .312     -.188     .298   .053000   .495000   .5307000      .000      .000  
+85 8 1 46278.00 I   .054974  .000788   .496231  .000833  I  .5307762  .0000400  0.4690 0.0369  I    -4.578     .313     -.274     .298   .056000   .495000   .5303000      .000      .000  
+85 8 2 46279.00 I   .058100  .000776   .496081  .000894  I  .5302084  .0000316  0.6669 0.0262  I    -4.597     .313     -.270     .298   .059000   .495000   .5297000      .000      .000  
+85 8 3 46280.00 I   .061323  .000776   .495838  .000894  I  .5294499  .0000339  0.8424 0.0257  I    -4.918     .313     -.138     .298   .063000   .494000   .5289000      .000      .000  
+85 8 4 46281.00 I   .064658  .000677   .495523  .000821  I  .5285432  .0000405  0.9590 0.0268  I    -5.541     .313      .067     .298   .066000   .494000   .5280000      .000      .000  
+85 8 5 46282.00 I   .068084  .000630   .495159  .000784  I  .5275565  .0000414  1.0016 0.0322  I    -6.339     .660      .220     .298   .069000   .494000   .5271000      .000      .000  
+85 8 6 46283.00 I   .071580  .000565   .494755  .000706  I  .5265648  .0000501  0.9697 0.0388  I    -7.081     .879      .252     .298   .073000   .493000   .5261000      .000      .000  
+85 8 7 46284.00 I   .075122  .000598   .494320  .000422  I  .5256392  .0000657  0.8715 0.0410  I    -7.519     .879      .238     .298   .076000   .493000   .5252000      .000      .000  
+85 8 8 46285.00 I   .078697  .000454   .493855  .000467  I  .5248374  .0000648  0.7264 0.0450  I    -7.551     .879      .285     .298   .080000   .493000   .5244000      .000      .000  
+85 8 9 46286.00 I   .082279  .000602   .493336  .000447  I  .5241943  .0000615  0.5575 0.0442  I    -7.398     .915      .369     .298   .083000   .492000   .5237000      .000      .000  
+85 810 46287.00 I   .085829  .000586   .492728  .000427  I  .5237231  .0000600  0.3866 0.0442  I    -7.340     .905      .341     .213   .086000   .492000   .5232000      .000      .000  
+85 811 46288.00 I   .089317  .000520   .491994  .000602  I  .5234146  .0000636  0.2354 0.0440  I    -7.451     .921      .110     .269   .090000   .491000   .5229000      .000      .000  
+85 812 46289.00 I   .092722  .000520   .491108  .000602  I  .5232339  .0000645  0.1394 0.0424  I    -7.600     .921     -.239     .269   .093000   .491000   .5226000      .000      .000  
+85 813 46290.00 I   .096012  .000518   .490110  .000686  I  .5231072  .0000562  0.1290 0.0377  I    -7.355     .921     -.509     .269   .096000   .490000   .5224000      .000      .000  
+85 814 46291.00 I   .099202  .000548   .489066  .000680  I  .5229433  .0000390  0.2162 0.0339  I    -6.762     .921     -.587     .269   .099000   .489000   .5222000      .000      .000  
+85 815 46292.00 I   .102330  .000824   .488030  .000752  I  .5226398  .0000378  0.4082 0.0247  I    -6.136     .772     -.485     .243   .103000   .488000   .5219000      .000      .000  
+85 816 46293.00 I   .105445  .000782   .487036  .000859  I  .5220976  .0000303  0.6881 0.0255  I    -5.663     .633     -.281     .126   .106000   .488000   .5213000      .000      .000  
+85 817 46294.00 I   .108604  .000758   .486110  .000698  I  .5212499  .0000342  1.0093 0.0228  I    -5.411     .633     -.089     .126   .109000   .487000   .5205000      .000      .000  
+85 818 46295.00 I   .111863  .000758   .485251  .000698  I  .5200893  .0000340  1.2997 0.0226  I    -5.438     .633     -.017     .126   .112000   .486000   .5194000      .000      .000  
+85 819 46296.00 I   .115192  .000864   .484438  .000665  I  .5186855  .0000295  1.4851 0.0234  I    -5.741     .633     -.086     .126   .115000   .485000   .5180000      .000      .000  
+85 820 46297.00 I   .118524  .000802   .483639  .000877  I  .5171687  .0000321  1.5217 0.0239  I    -6.235     .525     -.200     .298   .118000   .484000   .5166000      .000      .000  
+85 821 46298.00 I   .121791  .000696   .482814  .001017  I  .5156920  .0000375  1.4084 0.0286  I    -6.791     .390     -.226     .298   .121000   .483000   .5152000      .000      .000  
+85 822 46299.00 I   .124926  .000585   .481908  .001298  I  .5143879  .0000473  1.1869 0.0299  I    -7.304     .390     -.115     .298   .124000   .482000   .5140000      .000      .000  
+85 823 46300.00 I   .127884  .000622   .480877  .001369  I  .5133339  .0000467  0.9182 0.0329  I    -7.716     .340      .077     .298   .127000   .481000   .5130000      .000      .000  
+85 824 46301.00 I   .130691  .000614   .479741  .001284  I  .5125465  .0000456  0.6643 0.0370  I    -7.995     .340      .235     .298   .130000   .479000   .5123000      .000      .000  
+85 825 46302.00 I   .133380  .000789   .478546  .001402  I  .5119814  .0000574  0.4819 0.0426  I    -8.094     .900      .269     .242   .133000   .478000   .5117000      .000      .000  
+85 826 46303.00 I   .136005  .000809   .477310  .001369  I  .5115435  .0000719  0.4170 0.0453  I    -7.964    1.120      .140     .316   .136000   .477000   .5113000      .000      .000  
+85 827 46304.00 I   .138660  .000809   .475975  .001369  I  .5111038  .0000701  0.4828 0.0498  I    -7.674    1.120     -.116     .316   .139000   .476000   .5108000      .000      .000  
+85 828 46305.00 I   .141430  .000957   .474480  .001334  I  .5105460  .0000690  0.6442 0.0489  I    -7.395    1.120     -.406     .316   .142000   .474000   .5102000      .000      .000  
+85 829 46306.00 I   .144270  .001073   .472834  .000972  I  .5098009  .0000683  0.8489 0.0500  I    -7.260    1.354     -.616     .375   .145000   .473000   .5094000      .000      .000  
+85 830 46307.00 I   .147121  .001039   .471079  .000924  I  .5088480  .0000724  1.0540 0.0520  I    -7.334    1.098     -.651     .298   .148000   .471000   .5084000      .000      .000  
+85 831 46308.00 I   .149942  .001041   .469255  .000421  I  .5077043  .0000783  1.2249 0.0521  I    -7.600     .791     -.504     .298   .150000   .470000   .5072000      .000      .000  
+85 9 1 46309.00 I   .152721  .000950   .467398  .000411  I  .5064174  .0000749  1.3381 0.0525  I    -8.019     .791     -.290     .298   .153000   .468000   .5060000      .000      .000  
+85 9 2 46310.00 I   .155498  .001175   .465536  .001400  I  .5050530  .0000700  1.3767 0.0529  I    -8.558     .791     -.157     .298   .156000   .467000   .5047000      .000      .000  
+85 9 3 46311.00 I   .158276  .000975   .463694  .001430  I  .5036912  .0000746  1.3339 0.0479  I    -9.102     .541     -.153     .101   .159000   .465000   .5035000      .000      .000  
+85 9 4 46312.00 I   .161038  .000957   .461899  .001131  I  .5024070  .0000655  1.2258 0.0538  I    -9.450     .749     -.155     .144   .161000   .464000   .5023000      .000      .000  
+85 9 5 46313.00 I   .163789  .001020   .460177  .001227  I  .5012528  .0000776  1.0781 0.0507  I    -9.462     .870     -.023     .172   .164000   .462000   .5013000      .000      .000  
+85 9 6 46314.00 I   .166536  .001020   .458544  .001227  I  .5002573  .0000775  0.9108 0.0527  I    -9.194     .870      .214     .172   .166000   .460000   .5005000      .000      .000  
+85 9 7 46315.00 I   .169227  .001043   .456971  .001237  I  .4994287  .0000714  0.7511 0.0517  I    -8.915     .870      .276     .172   .169000   .458000   .4997000      .000      .000  
+85 9 8 46316.00 I   .171822  .000959   .455409  .001169  I  .4987417  .0000686  0.6318 0.0469  I    -8.668     .870      .043     .172   .171000   .456000   .4991000      .000      .000  
+85 9 9 46317.00 I   .174282  .000672   .453812  .000865  I  .4981437  .0000607  0.5768 0.0475  I    -8.179     .873     -.281     .176   .174000   .455000   .4985000      .000      .000  
+85 910 46318.00 I   .176573  .000450   .452131  .001043  I  .4975589  .0000657  0.6094 0.0373  I    -7.382     .833     -.477     .173   .176000   .453000   .4979000      .000      .000  
+85 911 46319.00 I   .178677  .000450   .450315  .001043  I  .4968903  .0000432  0.7458 0.0423  I    -6.597     .833     -.497     .173   .178000   .451000   .4971000      .000      .000  
+85 912 46320.00 I   .180611  .000543   .448316  .000954  I  .4960317  .0000532  0.9890 0.0341  I    -6.201     .833     -.409     .173   .181000   .449000   .4962000      .000      .000  
+85 913 46321.00 I   .182398  .000449   .446159  .000957  I  .4948855  .0000527  1.3124 0.0350  I    -6.236     .833     -.270     .173   .183000   .447000   .4949000      .000      .000  
+85 914 46322.00 I   .184053  .000505   .443888  .000839  I  .4934018  .0000454  1.6509 0.0379  I    -6.504     .798     -.091     .308   .185000   .445000   .4934000      .000      .000  
+85 915 46323.00 I   .185592  .000571   .441539  .000448  I  .4916065  .0000546  1.9209 0.0370  I    -6.869     .762      .049     .400   .187000   .442000   .4915000      .000      .000  
+85 916 46324.00 I   .187053  .000571   .439148  .000359  I  .4896077  .0000584  2.0476 0.0393  I    -7.295     .762      .022     .400   .189000   .440000   .4894000      .000      .000  
+85 917 46325.00 I   .188508  .000571   .436747  .000359  I  .4875697  .0000566  1.9989 0.0383  I    -7.705     .762     -.180     .400   .191000   .438000   .4873000      .000      .000  
+85 918 46326.00 I   .189998  .000433   .434355  .000428  I  .4856603  .0000496  1.7991 0.0359  I    -7.985     .762     -.373     .400   .193000   .436000   .4854000      .000      .000  
+85 919 46327.00 I   .191546  .000654   .431996  .000385  I  .4839986  .0000443  1.5182 0.0347  I    -8.120     .555     -.384     .347   .195000   .434000   .4837000      .000      .000  
+85 920 46328.00 I   .193176  .000719   .429680  .000381  I  .4826222  .0000484  1.2424 0.0332  I    -8.189     .191     -.243     .285   .197000   .431000   .4823000      .000      .000  
+85 921 46329.00 I   .194912  .000719   .427417  .000381  I  .4814891  .0000495  1.0408 0.0339  I    -8.223     .191     -.117     .285   .199000   .429000   .4812000      .000      .000  
+85 922 46330.00 I   .196776  .000818   .425207  .000457  I  .4805008  .0000475  0.9588 0.0335  I    -8.153     .191     -.099     .285   .200000   .427000   .4802000      .000      .000  
+85 923 46331.00 I   .198726  .000723   .423023  .000448  I  .4795289  .0000453  1.0047 0.0310  I    -7.918     .191     -.143     .285   .202000   .424000   .4791000      .000      .000  
+85 924 46332.00 I   .200685  .000669   .420838  .000464  I  .4784586  .0000400  1.1490 0.0317  I    -7.543     .268     -.167     .244   .204000   .422000   .4780000      .000      .000  
+85 925 46333.00 I   .202577  .000512   .418621  .000542  I  .4772123  .0000444  1.3493 0.0302  I    -7.144     .327     -.134     .195   .205000   .419000   .4767000      .000      .000  
+85 926 46334.00 I   .204362  .000512   .416327  .000542  I  .4757559  .0000452  1.5621 0.0311  I    -6.895     .327     -.024     .195   .207000   .417000   .4752000      .000      .000  
+85 927 46335.00 I   .206018  .000512   .413909  .000542  I  .4740976  .0000436  1.7464 0.0298  I    -6.938     .327      .214     .195   .208000   .414000   .4736000      .000      .000  
+85 928 46336.00 I   .207522  .000376   .411358  .000372  I  .4722834  .0000390  1.8697 0.0290  I    -7.223     .387      .595     .170   .210000   .412000   .4718000      .000      .000  
+85 929 46337.00 I   .208873  .000507   .408682  .000359  I  .4703842  .0000384  1.9149 0.0302  I    -7.566     .362     1.000     .145   .211000   .409000   .4699000      .000      .000  
+85 930 46338.00 I   .210079  .000543   .405896  .000284  I  .4684802  .0000461  1.8801 0.0307  I    -7.810     .384     1.225     .298   .212000   .406000   .4681000      .000      .000  
+8510 1 46339.00 I   .211149  .000527   .403012  .000278  I  .4666470  .0000480  1.7761 0.0343  I    -7.853     .384     1.188     .298   .214000   .404000   .4663000      .000      .000  
+8510 2 46340.00 I   .212122  .000527   .400053  .000278  I  .4649418  .0000508  1.6308 0.0356  I    -7.726     .384      .958     .298   .215000   .401000   .4646000      .000      .000  
+8510 3 46341.00 I   .213067  .000538   .397048  .000327  I  .4633893  .0000526  1.4739 0.0350  I    -7.482     .384      .712     .298   .216000   .398000   .4631000      .000      .000  
+8510 4 46342.00 I   .214036  .000602   .394029  .000251  I  .4619896  .0000482  1.3301 0.0420  I    -7.214     .372      .520     .298   .217000   .395000   .4617000      .000      .000  
+8510 5 46343.00 I   .215075  .000683   .391019  .000365  I  .4607163  .0000656  1.2246 0.0403  I    -7.045     .416      .283     .298   .219000   .392000   .4604000      .000      .000  
+8510 6 46344.00 I   .216228  .000701   .388042  .000774  I  .4595225  .0000647  1.1730 0.0449  I    -6.908     .416     -.018     .298   .220000   .390000   .4592000      .000      .000  
+8510 7 46345.00 I   .217527  .000701   .385111  .000774  I  .4583458  .0000613  1.1951 0.0436  I    -6.553     .416     -.203     .298   .220000   .387000   .4580000      .000      .000  
+8510 8 46346.00 I   .218911  .000738   .382221  .000793  I  .4571011  .0000584  1.3107 0.0406  I    -5.916     .416     -.111     .298   .221000   .384000   .4568000      .000      .000  
+8510 9 46347.00 I   .220280  .000686   .379362  .001027  I  .4556925  .0000534  1.5218 0.0425  I    -5.310     .312      .181     .298   .222000   .381000   .4554000      .000      .000  
+851010 46348.00 I   .221542  .000549   .376513  .001256  I  .4540317  .0000618  1.8103 0.0413  I    -5.105     .148      .459     .110   .223000   .378000   .4538000      .000      .000  
+851011 46349.00 I   .222619  .000549   .373620  .001256  I  .4520607  .0000631  2.1323 0.0435  I    -5.336     .148      .655     .110   .224000   .375000   .4518000      .000      .000  
+851012 46350.00 I   .223455  .000584   .370637  .001192  I  .4497784  .0000613  2.4204 0.0441  I    -5.772     .148      .876     .110   .225000   .372000   .4496000      .000      .000  
+851013 46351.00 I   .224080  .000469   .367587  .001083  I  .4472576  .0000617  2.5955 0.0400  I    -6.224     .148     1.148     .110   .226000   .369000   .4471000      .000      .000  
+851014 46352.00 I   .224562  .000588   .364512  .000947  I  .4446451  .0000513  2.5977 0.0416  I    -6.600     .249     1.303     .164   .226000   .366000   .4444000      .000      .000  
+851015 46353.00 I   .224966  .000535   .361454  .000691  I  .4421210  .0000559  2.4235 0.0392  I    -6.774     .320     1.227     .204   .227000   .363000   .4419000      .000      .000  
+851016 46354.00 I   .225359  .000535   .358452  .000691  I  .4398379  .0000593  2.1298 0.0548  I    -6.636     .320     1.060     .204   .228000   .360000   .4396000      .000      .000  
+851017 46355.00 I   .225798  .000535   .355520  .000691  I  .4378696  .0000942  1.8116 0.0510  I    -6.277     .320     1.023     .204   .228000   .358000   .4376000      .000      .000  
+851018 46356.00 I   .226285  .000494   .352648  .000620  I  .4361921  .0000830  1.5614 0.0585  I    -5.943     .320     1.111     .204   .229000   .355000   .4359000      .000      .000  
+851019 46357.00 I   .226795  .000572   .349824  .000554  I  .4347040  .0000694  1.4396 0.0660  I    -5.711     .346     1.102     .314   .229000   .352000   .4344000      .000      .000  
+851020 46358.00 I   .227307  .000499   .347014  .000612  I  .4332664  .0001027  1.4572 0.0636  I    -5.420     .370      .892     .394   .230000   .349000   .4329000      .000      .000  
+851021 46359.00 I   .227800  .000551   .344182  .000558  I  .4317540  .0001066  1.5817 0.0727  I    -4.949     .370      .659     .394   .230000   .346000   .4314000      .000      .000  
+851022 46360.00 I   .228257  .000508   .341300  .000568  I  .4300833  .0001030  1.7655 0.0615  I    -4.379     .370      .642     .394   .231000   .343000   .4297000      .000      .000  
+851023 46361.00 I   .228671  .000466   .338380  .000503  I  .4282195  .0000612  1.9600 0.0581  I    -3.874     .370      .848     .394   .231000   .340000   .4279000      .000      .000  
+851024 46362.00 I   .229030  .000475   .335455  .000454  I  .4261722  .0000538  2.1278 0.0461  I    -3.555     .501     1.112     .309   .231000   .337000   .4258000      .000      .000  
+851025 46363.00 I   .229326  .000410   .332557  .000354  I  .4239808  .0000690  2.2450 0.0307  I    -3.492     .604     1.337     .190   .232000   .334000   .4237000      .000      .000  
+851026 46364.00 I   .229560  .000410   .329701  .000354  I  .4217036  .0000296  2.2980 0.0367  I    -3.692     .604     1.533     .190   .232000   .331000   .4215000      .000      .000  
+851027 46365.00 I   .229736  .000400   .326901  .000293  I  .4194097  .0000252  2.2764 0.0210  I    -4.077     .543     1.653     .199   .232000   .328000   .4192000      .000      .000  
+851028 46366.00 I   .229840  .000410   .324156  .000239  I  .4171761  .0000299  2.1788 0.0225  I    -4.493     .543     1.613     .199   .232000   .326000   .4171000      .000      .000  
+851029 46367.00 I   .229859  .000512   .321447  .000386  I  .4150728  .0000373  2.0192 0.0262  I    -4.842     .480     1.419     .335   .232000   .323000   .4150000      .000      .000  
+851030 46368.00 I   .229785  .000547   .318755  .000429  I  .4131510  .0000430  1.8198 0.0293  I    -5.076     .373     1.301     .407   .233000   .320000   .4132000      .000      .000  
+851031 46369.00 I   .229620  .000547   .316060  .000429  I  .4114356  .0000451  1.6131 0.0310  I    -5.191     .373     1.444     .407   .233000   .317000   .4115000      .000      .000  
+8511 1 46370.00 I   .229408  .000547   .313351  .000429  I  .4099157  .0000447  1.4338 0.0351  I    -5.313     .373     1.612     .407   .233000   .314000   .4100000      .000      .000  
+8511 2 46371.00 I   .229171  .000616   .310637  .000511  I  .4085510  .0000539  1.3055 0.0383  I    -5.461     .368     1.635     .463   .233000   .312000   .4086000      .000      .000  
+8511 3 46372.00 I   .228924  .000717   .307937  .000551  I  .4072826  .0000623  1.2435 0.0413  I    -5.536     .467     1.503     .407   .233000   .309000   .4073000      .000      .000  
+8511 4 46373.00 I   .228693  .000657   .305271  .000445  I  .4060380  .0000627  1.2593 0.0460  I    -5.374     .548     1.373     .341   .233000   .306000   .4060000      .000      .000  
+8511 5 46374.00 I   .228504  .000633   .302658  .000783  I  .4047364  .0000677  1.3577 0.0482  I    -4.916     .548     1.442     .341   .232000   .303000   .4046000      .000      .000  
+8511 6 46375.00 I   .228386  .000633   .300114  .000783  I  .4032939  .0000731  1.5420 0.0476  I    -4.313     .548     1.672     .341   .232000   .301000   .4030000      .000      .000  
+8511 7 46376.00 I   .228334  .000637   .297638  .000693  I  .4016286  .0000670  1.7977 0.0490  I    -3.818     .548     1.820     .341   .232000   .298000   .4012000      .000      .000  
+8511 8 46377.00 I   .228320  .000638   .295220  .000661  I  .3996907  .0000653  2.0769 0.0469  I    -3.533     .728     1.855     .287   .232000   .295000   .3990000      .000      .000  
+8511 9 46378.00 I   .228317  .000677   .292843  .000737  I  .3974904  .0000657  2.3093 0.0463  I    -3.405     .872     1.947     .220   .232000   .293000   .3967000      .000      .000  
+851110 46379.00 I   .228298  .000677   .290458  .000737  I  .3951109  .0000657  2.4253 0.0462  I    -3.451     .872     2.194     .220   .232000   .290000   .3942000      .000      .000  
+851111 46380.00 I   .228247  .000736   .288015  .000560  I  .3926938  .0000650  2.3791 0.0457  I    -3.703     .872     2.441     .220   .231000   .288000   .3917000      .000      .000  
+851112 46381.00 I   .228135  .000741   .285484  .000485  I  .3904061  .0000635  2.1725 0.0440  I    -3.959     .872     2.484     .220   .231000   .285000   .3893000      .000      .000  
+851113 46382.00 I   .227954  .000777   .282861  .000633  I  .3883798  .0000594  1.8729 0.0430  I    -3.977     .638     2.380     .156   .231000   .282000   .3873000      .000      .000  
+851114 46383.00 I   .227722  .000821   .280152  .000772  I  .3866578  .0000580  1.5809 0.0426  I    -3.803     .234     2.359     .298   .230000   .280000   .3855000      .000      .000  
+851115 46384.00 I   .227464  .000756   .277366  .000781  I  .3851853  .0000611  1.3870 0.0449  I    -3.671     .234     2.445     .298   .230000   .277000   .3841000      .000      .000  
+851116 46385.00 I   .227247  .000756   .274515  .000781  I  .3838348  .0000685  1.3401 0.0411  I    -3.605     .234     2.394     .298   .230000   .275000   .3827000      .000      .000  
+851117 46386.00 I   .227107  .000780   .271641  .000971  I  .3824592  .0000551  1.4310 0.0441  I    -3.421     .234     2.080     .298   .229000   .272000   .3814000      .000      .000  
+851118 46387.00 I   .227067  .000985   .268788  .001078  I  .3809433  .0000557  1.6105 0.0296  I    -3.035     .681     1.731     .131   .229000   .270000   .3799000      .000      .000  
+851119 46388.00 I   .227161  .000543   .265981  .000614  I  .3792292  .0000219  1.8172 0.0300  I    -2.636     .958     1.676     .212   .229000   .267000   .3783000      .000      .000  
+851120 46389.00 I   .227422  .000540   .263244  .000583  I  .3773162  .0000224  2.0021 0.0189  I    -2.489     .885     1.955     .198   .228000   .265000   .3765000      .000      .000  
+851121 46390.00 I   .227857  .000540   .260599  .000583  I  .3752469  .0000307  2.1213 0.0225  I    -2.672     .885     2.339     .198   .228000   .262000   .3746000      .000      .000  
+851122 46391.00 I   .228289  .000539   .258093  .000578  I  .3731068  .0000390  2.1408 0.0246  I    -2.886     .885     2.792     .198   .227000   .260000   .3726000      .000      .000  
+851123 46392.00 I   .228513  .000559   .255777  .000457  I  .3709919  .0000385  2.0806 0.0268  I    -3.109     .761     3.227     .270   .227000   .257000   .3705000      .000      .000  
+851124 46393.00 I   .228522  .000309   .253611  .000305  I  .3689618  .0000367  1.9718 0.0266  I    -3.346     .654     3.575     .307   .226000   .255000   .3686000      .000      .000  
+851125 46394.00 I   .228351  .000309   .251535  .000305  I  .3670608  .0000368  1.8255 0.0574  I    -3.396     .491     3.770     .317   .226000   .252000   .3667000      .000      .000  
+851126 46395.00 I   .228034  .000506   .249492  .000456  I  .3653185  .0001087  1.6560 0.0453  I    -3.139     .426     3.674     .367   .225000   .250000   .3650000      .000      .000  
+851127 46396.00 I   .227578  .000642   .247438  .000422  I  .3637506  .0000827  1.4810 0.0581  I    -2.705     .426     3.317     .367   .225000   .247000   .3635000      .000      .000  
+851128 46397.00 I   .226974  .000593   .245348  .000553  I  .3623496  .0000413  1.3267 0.0487  I    -2.239     .364     2.914     .291   .224000   .245000   .3622000      .000      .000  
+851129 46398.00 I   .226214  .000534   .243204  .000602  I  .3610822  .0000514  1.2175 0.0386  I    -1.746     .195     2.650     .298   .224000   .243000   .3609000      .000      .000  
+851130 46399.00 I   .225289  .000686   .240987  .000554  I  .3598928  .0000653  1.1738 0.0429  I    -1.217     .195     2.540     .298   .223000   .240000   .3598000      .000      .000  
+8512 1 46400.00 I   .224203  .000686   .238690  .000554  I  .3587090  .0000687  1.2070 0.0454  I     -.735     .195     2.506     .298   .222000   .238000   .3586000      .000      .000  
+8512 2 46401.00 I   .222985  .000686   .236330  .000572  I  .3574534  .0000630  1.3164 0.0463  I     -.370     .195     2.514     .298   .222000   .235000   .3574000      .000      .000  
+8512 3 46402.00 I   .221664  .000963   .233938  .000528  I  .3560531  .0000620  1.4950 0.0466  I     -.163     .461     2.592     .114   .221000   .233000   .3560000      .000      .000  
+8512 4 46403.00 I   .220295  .001076   .231542  .000402  I  .3544448  .0000686  1.7292 0.0485  I     -.158     .551     2.724     .143   .220000   .231000   .3544000      .000      .000  
+8512 5 46404.00 I   .218965  .000978   .229164  .000369  I  .3525858  .0000746  1.9899 0.0481  I     -.361     .551     2.828     .143   .219000   .228000   .3526000      .000      .000  
+8512 6 46405.00 I   .217750  .000994   .226823  .000388  I  .3504737  .0000675  2.2243 0.0477  I     -.703     .551     2.892     .143   .219000   .226000   .3505000      .000      .000  
+8512 7 46406.00 I   .216636  .000992   .224519  .000391  I  .3481682  .0000595  2.3656 0.0438  I    -1.141     .551     3.029     .143   .218000   .224000   .3482000      .000      .000  
+8512 8 46407.00 I   .215563  .000910   .222242  .000435  I  .3457903  .0000557  2.3634 0.0417  I    -1.720     .611     3.297     .305   .217000   .221000   .3459000      .000      .000  
+8512 9 46408.00 I   .214475  .000550   .219989  .000409  I  .3434927  .0000583  2.2071 0.0413  I    -2.431     .665     3.548     .407   .216000   .219000   .3436000      .000      .000  
+851210 46409.00 I   .213331  .000550   .217755  .000409  I  .3414142  .0000609  1.9365 0.0407  I    -3.044     .665     3.586     .407   .215000   .217000   .3416000      .000      .000  
+851211 46410.00 I   .212100  .000532   .215538  .000403  I  .3396322  .0000567  1.6286 0.0420  I    -3.266     .665     3.442     .407   .215000   .215000   .3398000      .000      .000  
+851212 46411.00 I   .210811  .000515   .213345  .000434  I  .3381405  .0000578  1.3703 0.0387  I    -3.079     .665     3.334     .407   .214000   .213000   .3383000      .000      .000  
+851213 46412.00 I   .209523  .001023   .211181  .000403  I  .3368527  .0000527  1.2289 0.0414  I    -2.738     .479     3.366     .296   .212000   .211000   .3371000      .000      .000  
+851214 46413.00 I   .208303  .001161   .209052  .000319  I  .3356368  .0000593  1.2253 0.0406  I    -2.413     .132     3.390     .298   .211000   .208000   .3358000      .000      .000  
+851215 46414.00 I   .207217  .001156   .206963  .000635  I  .3343657  .0000617  1.3309 0.0426  I    -2.045     .132     3.257     .298   .210000   .206000   .3345000      .000      .000  
+851216 46415.00 I   .206324  .001156   .204913  .000635  I  .3329571  .0000613  1.4902 0.0431  I    -1.605     .132     3.081     .298   .209000   .204000   .3331000      .000      .000  
+851217 46416.00 I   .205587  .001105   .202891  .000565  I  .3313867  .0000603  1.6451 0.0409  I    -1.270     .132     3.127     .298   .208000   .202000   .3315000      .000      .000  
+851218 46417.00 I   .204918  .000955   .200878  .000926  I  .3296829  .0000541  1.7518 0.0423  I    -1.220     .791     3.430     .261   .207000   .200000   .3297000      .000      .000  
+851219 46418.00 I   .204245  .000571   .198834  .001048  I  .3279056  .0000592  1.7908 0.0410  I    -1.390     .791     3.731     .356   .206000   .198000   .3279000      .000      .000  
+851220 46419.00 I   .203505  .000571   .196704  .001048  I  .3261229  .0000617  1.7649 0.0423  I    -1.570     .791     3.809     .356   .205000   .196000   .3261000      .000      .000  
+851221 46420.00 I   .202651  .000561   .194441  .001030  I  .3243953  .0000603  1.6805 0.0426  I    -1.641     .791     3.722     .356   .203000   .194000   .3244000      .000      .000  
+851222 46421.00 I   .201682  .000561   .192067  .001030  I  .3227800  .0000587  1.5419 0.0388  I    -1.586     .791     3.656     .356   .202000   .192000   .3228000      .000      .000  
+851223 46422.00 I   .200599  .000491   .189640  .001141  I  .3213244  .0000490  1.3648 0.0547  I    -1.409     .791     3.653     .356   .201000   .190000   .3213000      .000      .000  
+851224 46423.00 I   .199401  .000472   .187217  .001061  I  .3200557  .0000924  1.1720 0.0475  I    -1.159     .791     3.596     .298   .199000   .188000   .3201000      .000      .000  
+851225 46424.00 I   .198074  .000472   .184830  .001061  I  .3189776  .0000813  0.9877 0.0535  I     -.970     .791     3.410     .298   .198000   .186000   .3191000      .000      .000  
+851226 46425.00 I   .196602  .000635   .182503  .000870  I  .3180691  .0000540  0.8371 0.0565  I     -.918     .791     3.190     .298   .196000   .184000   .3182000      .000      .000  
+851227 46426.00 I   .194969  .000648   .180259  .000787  I  .3172844  .0000786  0.7436 0.0373  I     -.892     .791     3.094     .298   .195000   .182000   .3174000      .000      .000  
+851228 46427.00 I   .193183  .000748   .178098  .000734  I  .3165573  .0000516  0.7238 0.0470  I     -.713     .329     3.174     .102   .193000   .180000   .3167000      .000      .000  
+851229 46428.00 I   .191268  .000748   .176008  .000734  I  .3158105  .0000516  0.7828 0.0356  I     -.363     .329     3.344     .102   .192000   .178000   .3159000      .000      .000  
+851230 46429.00 I   .189274  .000752   .173979  .000740  I  .3149682  .0000491  0.9123 0.0356  I     -.003     .329     3.484     .102   .190000   .176000   .3150000      .000      .000  
+851231 46430.00 I   .187278  .000752   .172001  .000740  I  .3139669  .0000492  1.0989 0.0348  I      .207     .329     3.534     .102   .188000   .174000   .3139000      .000      .000  
+86 1 1 46431.00 I   .185312  .000642   .170079  .000696  I  .3127601  .0000493  1.3163 0.0346  I      .236     .329     3.517     .102   .187000   .172000   .3126000      .000      .000  
+86 1 2 46432.00 I   .183379  .000595   .168223  .000660  I  .3113387  .0000486  1.5204 0.0433  I      .156     .414     3.491     .298   .185000   .170000   .3111000      .000      .000  
+86 1 3 46433.00 I   .181454  .000475   .166418  .000631  I  .3097389  .0000712  1.6656 0.0453  I      .041     .484     3.521     .298   .183000   .169000   .3094000      .000      .000  
+86 1 4 46434.00 I   .179503  .000475   .164642  .000631  I  .3080397  .0000765  1.7141 0.0523  I     -.089     .484     3.653     .298   .181000   .167000   .3076000      .000      .000  
+86 1 5 46435.00 I   .177500  .000370   .162872  .000611  I  .3063498  .0000767  1.6452 0.0530  I     -.269     .484     3.862     .298   .179000   .165000   .3058000      .000      .000  
+86 1 6 46436.00 I   .175449  .000429   .161098  .000572  I  .3047858  .0000735  1.4668 0.0505  I     -.522     .484     4.033     .298   .177000   .163000   .3042000      .000      .000  
+86 1 7 46437.00 I   .173353  .000327   .159317  .000634  I  .3034362  .0000656  1.2284 0.0574  I     -.722     .705     4.047     .191   .175000   .161000   .3028000      .000      .000  
+86 1 8 46438.00 I   .171218  .000280   .157523  .000652  I  .3023221  .0000882  1.0106 0.0432  I     -.672     .847     3.930     .260   .173000   .159000   .3017000      .000      .000  
+86 1 9 46439.00 I   .169057  .000280   .155692  .000652  I  .3013826  .0000563  0.8898 0.0485  I     -.314     .847     3.872     .260   .171000   .157000   .3008000      .000      .000  
+86 110 46440.00 I   .166891  .000280   .153788  .000652  I  .3004973  .0000405  0.9045 0.0361  I      .206     .847     4.034     .260   .169000   .155000   .2999000      .000      .000  
+86 111 46441.00 I   .164758  .000342   .151817  .000543  I  .2995308  .0000452  1.0476 0.0311  I      .543     .847     4.165     .260   .168000   .153000   .2990000      .000      .000  
+86 112 46442.00 I   .162690  .000556   .149796  .000624  I  .2983744  .0000473  1.2737 0.0436  I      .604     .739     4.065     .209   .165000   .151000   .2979000      .000      .000  
+86 113 46443.00 I   .160722  .000631   .147740  .000382  I  .2969782  .0000746  1.5156 0.0453  I      .507     .635     3.896     .154   .164000   .149000   .2966000      .000      .000  
+86 114 46444.00 I   .158878  .000714   .145663  .000459  I  .2953575  .0000773  1.7148 0.0568  I      .243     .468     3.916     .102   .161000   .147000   .2950000      .000      .000  
+86 115 46445.00 I   .157141  .000714   .143587  .000459  I  .2935775  .0000856  1.8272 0.0564  I     -.189     .468     4.236     .102   .159000   .145000   .2933000      .000      .000  
+86 116 46446.00 I   .155372  .000837   .141583  .000474  I  .2917394  .0000821  1.8306 0.0583  I     -.755     .468     4.589     .102   .157000   .144000   .2916000      .000      .000  
+86 117 46447.00 I   .153408  .000752   .139731  .000382  I  .2899452  .0000791  1.7471 0.0667  I    -1.339     .710     4.650     .298   .155000   .142000   .2898000      .000      .000  
+86 118 46448.00 I   .151200  .000744   .138054  .000278  I  .2882630  .0001051  1.6100 0.0653  I    -1.765     .699     4.474     .298   .153000   .141000   .2882000      .000      .000  
+86 119 46449.00 I   .148727  .000744   .136557  .000278  I  .2867365  .0001040  1.4390 0.0699  I    -1.930     .699     4.317     .298   .150000   .139000   .2867000      .000      .000  
+86 120 46450.00 I   .145986  .000897   .135246  .000254  I  .2853861  .0000922  1.2646 0.0627  I    -1.808     .792     4.296     .298   .148000   .137000   .2853000      .000      .000  
+86 121 46451.00 I   .143091  .000835   .134110  .000250  I  .2842001  .0000702  1.1117 0.0545  I    -1.489     .792     4.257     .298   .145000   .136000   .2842000      .000      .000  
+86 122 46452.00 I   .140167  .000798   .133107  .000350  I  .2831507  .0000581  0.9948 0.0466  I    -1.131     .619     4.052     .103   .142000   .135000   .2831000      .000      .000  
+86 123 46453.00 I   .137314  .000566   .132188  .000310  I  .2821915  .0000612  0.9348 0.0338  I     -.855     .129     3.712     .127   .140000   .133000   .2822000      .000      .000  
+86 124 46454.00 I   .134567  .000745   .131298  .000505  I  .2812568  .0000345  0.9478 0.0355  I     -.630     .149     3.382     .149   .137000   .132000   .2813000      .000      .000  
+86 125 46455.00 I   .131939  .000745   .130388  .000505  I  .2802680  .0000358  1.0444 0.0247  I     -.308     .149     3.167     .149   .134000   .131000   .2803000      .000      .000  
+86 126 46456.00 I   .129401  .000490   .129446  .000538  I  .2791405  .0000354  1.2236 0.0262  I      .160     .149     3.094     .149   .131000   .130000   .2793000      .000      .000  
+86 127 46457.00 I   .126889  .000501   .128481  .000637  I  .2777997  .0000383  1.4662 0.0295  I      .659     .426     3.108     .188   .128000   .128000   .2780000      .000      .000  
+86 128 46458.00 I   .124344  .000561   .127502  .000644  I  .2761995  .0000473  1.7345 0.0302  I     1.007     .542     3.158     .242   .125000   .127000   .2764000      .000      .000  
+86 129 46459.00 I   .121701  .000561   .126519  .000644  I  .2743377  .0000467  1.9818 0.0401  I     1.077     .542     3.280     .242   .122000   .126000   .2747000      .000      .000  
+86 130 46460.00 I   .118895  .000561   .125549  .000644  I  .2722597  .0000648  2.1578 0.0367  I      .840     .542     3.564     .242   .119000   .125000   .2727000      .000      .000  
+86 131 46461.00 I   .115908  .000521   .124658  .000619  I  .2700604  .0000566  2.2188 0.0399  I      .484     .542     3.764     .242   .116000   .124000   .2707000      .000      .000  
+86 2 1 46462.00 I   .112821  .000486   .123850  .000563  I  .2678653  .0000466  2.1502 0.0366  I      .107     .634     3.821     .249   .113000   .124000   .2686000      .000      .000  
+86 2 2 46463.00 I   .109714  .000475   .123099  .000433  I  .2657978  .0000465  1.9684 0.0338  I     -.289     .612     3.821     .239   .111000   .123000   .2668000      .000      .000  
+86 2 3 46464.00 I   .106661  .000394   .122378  .000463  I  .2639515  .0000490  1.7177 0.0360  I     -.711     .658     3.812     .222   .108000   .122000   .2651000      .000      .000  
+86 2 4 46465.00 I   .103734  .000398   .121667  .000452  I  .2623630  .0000550  1.4651 0.0326  I    -1.141     .658     3.826     .222   .105000   .122000   .2636000      .000      .000  
+86 2 5 46466.00 I   .100944  .000411   .121002  .000439  I  .2609992  .0000429  1.2787 0.0341  I    -1.376     .658     3.744     .222   .103000   .121000   .2624000      .000      .000  
+86 2 6 46467.00 I   .098291  .000439   .120373  .000502  I  .2597681  .0000403  1.2048 0.0325  I    -1.344     .531     3.558     .227   .100000   .121000   .2612000      .000      .000  
+86 2 7 46468.00 I   .095777  .000398   .119754  .000407  I  .2585474  .0000489  1.2574 0.0334  I    -1.119     .307     3.376     .203   .098000   .120000   .2600000      .000      .000  
+86 2 8 46469.00 I   .093387  .000515   .119115  .000614  I  .2572194  .0000532  1.4117 0.0364  I     -.767     .246     3.265     .234   .095000   .120000   .2587000      .000      .000  
+86 2 9 46470.00 I   .091103  .000565   .118430  .000633  I  .2557082  .0000538  1.6136 0.0344  I     -.320     .246     3.233     .234   .093000   .120000   .2571000      .000      .000  
+86 210 46471.00 I   .088893  .000544   .117719  .000601  I  .2539971  .0000436  1.8010 0.0334  I      .158     .246     3.270     .234   .091000   .119000   .2554000      .000      .000  
+86 211 46472.00 I   .086712  .000525   .117033  .000610  I  .2521272  .0000395  1.9254 0.0313  I      .523     .554     3.368     .221   .088000   .119000   .2535000      .000      .000  
+86 212 46473.00 I   .084518  .000536   .116424  .000566  I  .2501744  .0000448  1.9656 0.0329  I      .624     .744     3.479     .208   .086000   .119000   .2515000      .000      .000  
+86 213 46474.00 I   .082279  .000536   .115931  .000566  I  .2482237  .0000527  1.9227 0.0346  I      .422     .744     3.518     .208   .084000   .119000   .2495000      .000      .000  
+86 214 46475.00 I   .080014  .000536   .115531  .000566  I  .2463535  .0000527  1.8064 0.0381  I      .027     .744     3.457     .208   .081000   .119000   .2477000      .000      .000  
+86 215 46476.00 I   .077728  .000570   .115215  .000558  I  .2446300  .0000551  1.6327 0.0346  I     -.355     .744     3.380     .208   .079000   .118000   .2459000      .000      .000  
+86 216 46477.00 I   .075396  .000468   .114980  .000587  I  .2431000  .0000448  1.4234 0.0381  I     -.552     .867     3.389     .211   .076000   .118000   .2443000      .000      .000  
+86 217 46478.00 I   .072972  .000541   .114814  .000613  I  .2417861  .0000526  1.2054 0.0359  I     -.499     .976     3.459     .215   .074000   .118000   .2430000      .000      .000  
+86 218 46479.00 I   .070407  .000508   .114705  .000594  I  .2406830  .0000562  1.0058 0.0365  I     -.228     .976     3.437     .215   .072000   .118000   .2417000      .000      .000  
+86 219 46480.00 I   .067671  .000508   .114639  .000594  I  .2397571  .0000506  0.8583 0.0409  I      .166     .976     3.206     .215   .069000   .118000   .2406000      .000      .000  
+86 220 46481.00 I   .064784  .000612   .114612  .000574  I  .2389393  .0000594  0.7920 0.0356  I      .536     .976     2.825     .215   .066000   .118000   .2395000      .000      .000  
+86 221 46482.00 I   .061769  .000568   .114632  .000532  I  .2381421  .0000500  0.8186 0.0416  I      .759     .844     2.450     .170   .063000   .118000   .2385000      .000      .000  
+86 222 46483.00 I   .058642  .000530   .114705  .000354  I  .2372709  .0000583  0.9387 0.0382  I      .824     .687     2.201     .109   .060000   .118000   .2373000      .000      .000  
+86 223 46484.00 I   .055401  .000530   .114826  .000354  I  .2362378  .0000578  1.1395 0.0419  I      .838     .687     2.122     .109   .057000   .118000   .2360000      .000      .000  
+86 224 46485.00 I   .052054  .000498   .114988  .000325  I  .2349740  .0000601  1.3941 0.0402  I      .924     .687     2.206     .109   .054000   .118000   .2346000      .000      .000  
+86 225 46486.00 I   .048662  .000454   .115180  .000304  I  .2334475  .0000558  1.6549 0.0388  I     1.083     .687     2.375     .109   .051000   .118000   .2330000      .000      .000  
+86 226 46487.00 I   .045283  .000569   .115387  .000537  I  .2316829  .0000492  1.8595 0.0487  I     1.159     .643     2.525     .119   .048000   .118000   .2312000      .000      .000  
+86 227 46488.00 I   .041965  .000518   .115589  .000746  I  .2297646  .0000798  1.9558 0.0462  I      .987     .595     2.592     .128   .045000   .118000   .2292000      .000      .000  
+86 228 46489.00 I   .038757  .000544   .115766  .000792  I  .2278141  .0000781  1.9237 0.0571  I      .571     .595     2.581     .128   .042000   .118000   .2273000      .000      .000  
+86 3 1 46490.00 I   .035706  .000544   .115906  .000792  I  .2259578  .0000817  1.7698 0.0542  I      .096     .595     2.537     .128   .039000   .118000   .2255000      .000      .000  
+86 3 2 46491.00 I   .032791  .000758   .116044  .000781  I  .2243030  .0000751  1.5303 0.0517  I     -.230     .595     2.493     .128   .036000   .118000   .2239000      .000      .000  
+86 3 3 46492.00 I   .029953  .000634   .116217  .000748  I  .2229021  .0000634  1.2763 0.0536  I     -.313     .435     2.454     .298   .032000   .118000   .2226000      .000      .000  
+86 3 4 46493.00 I   .027128  .000616   .116443  .000749  I  .2217315  .0000766  1.0792 0.0397  I     -.187     .156     2.396     .298   .029000   .118000   .2216000      .000      .000  
+86 3 5 46494.00 I   .024253  .000639   .116739  .000623  I  .2207104  .0000478  0.9819 0.0548  I      .069     .156     2.293     .298   .026000   .118000   .2206000      .000      .000  
+86 3 6 46495.00 I   .021283  .000639   .117115  .000623  I  .2197275  .0000783  1.0051 0.0422  I      .386     .156     2.137     .298   .023000   .119000   .2197000      .000      .000  
+86 3 7 46496.00 I   .018245  .000581   .117561  .000557  I  .2186638  .0000696  1.1375 0.0508  I      .708     .156     1.968     .298   .020000   .119000   .2187000      .000      .000  
+86 3 8 46497.00 I   .015184  .000586   .118063  .000673  I  .2174311  .0000648  1.3341 0.0530  I      .981     .356     1.875     .168   .017000   .119000   .2175000      .000      .000  
+86 3 9 46498.00 I   .012129  .000489   .118590  .000678  I  .2159936  .0000799  1.5367 0.0500  I     1.169     .478     1.943     .237   .015000   .119000   .2161000      .000      .000  
+86 310 46499.00 I   .009097  .000632   .119104  .000677  I  .2143741  .0000762  1.6902 0.0598  I     1.282     .478     2.192     .237   .012000   .120000   .2145000      .000      .000  
+86 311 46500.00 I   .006107  .000708   .119568  .000741  I  .2126434  .0000890  1.7533 0.0515  I     1.367     .476     2.551     .222   .009000   .120000   .2128000      .000      .000  
+86 312 46501.00 I   .003162  .000685   .119982  .000721  I  .2109038  .0000694  1.7076 0.0552  I     1.453     .576     2.825     .209   .006000   .121000   .2111000      .000      .000  
+86 313 46502.00 I   .000266  .000709   .120366  .000701  I  .2092616  .0000653  1.5617 0.0528  I     1.529     .555     2.810     .278   .004000   .121000   .2095000      .000      .000  
+86 314 46503.00 I  -.002553  .000816   .120732  .000679  I  .2078046  .0000797  1.3431 0.0530  I     1.595     .590     2.444     .298   .001000   .121000   .2081000      .000      .000  
+86 315 46504.00 I  -.005233  .000792   .121082  .000853  I  .2065820  .0000834  1.1047 0.0575  I     1.577     .590     2.029     .298  -.002000   .122000   .2068000      .000      .000  
+86 316 46505.00 I  -.007745  .000792   .121426  .000853  I  .2055882  .0000828  0.8872 0.0588  I     1.638     .590     1.785     .298  -.004000   .123000   .2057000      .000      .000  
+86 317 46506.00 I  -.010088  .000697   .121808  .000841  I  .2047961  .0000828  0.7043 0.0509  I     1.906     .623     1.663     .325  -.007000   .123000   .2048000      .000      .000  
+86 318 46507.00 I  -.012282  .000481   .122274  .000853  I  .2041610  .0000592  0.5773 0.0517  I     2.331     .610     1.526     .311  -.009000   .124000   .2041000      .000      .000  
+86 319 46508.00 I  -.014357  .000340   .122843  .000967  I  .2036164  .0000618  0.5255 0.0453  I     2.858     .694     1.326     .232  -.011000   .124000   .2034000      .000      .000  
+86 320 46509.00 I  -.016346  .000455   .123521  .001003  I  .2030807  .0000687  0.5617 0.0467  I     3.406     .694     1.150     .232  -.014000   .125000   .2028000      .000      .000  
+86 321 46510.00 I  -.018278  .000485   .124312  .000913  I  .2024589  .0000699  0.7004 0.0451  I     3.784     .694     1.080     .232  -.016000   .126000   .2021000      .000      .000  
+86 322 46511.00 I  -.020183  .000664   .125208  .000806  I  .2016446  .0000584  0.9450 0.0471  I     3.803     .694     1.080     .232  -.018000   .127000   .2012000      .000      .000  
+86 323 46512.00 I  -.022093  .000562   .126208  .000746  I  .2005405  .0000632  1.2746 0.0446  I     3.525     .568     1.087     .168  -.021000   .128000   .2001000      .000      .000  
+86 324 46513.00 I  -.024062  .000663   .127293  .000681  I  .1990832  .0000674  1.6406 0.0461  I     3.267     .404     1.146     .298  -.023000   .129000   .1987000      .000      .000  
+86 325 46514.00 I  -.026177  .000663   .128425  .000681  I  .1972693  .0000671  1.9767 0.0458  I     3.229     .404     1.327     .298  -.025000   .130000   .1970000      .000      .000  
+86 326 46515.00 I  -.028501  .000612   .129563  .000559  I  .1951654  .0000619  2.2062 0.0463  I     3.234     .404     1.550     .298  -.027000   .131000   .1950000      .000      .000  
+86 327 46516.00 I  -.030986  .000551   .130692  .000460  I  .1929143  .0000637  2.2636 0.0443  I     2.965    1.057     1.619     .361  -.030000   .132000   .1930000      .000      .000  
+86 328 46517.00 I  -.033545  .000551   .131826  .000460  I  .1906985  .0000635  2.1401 0.0481  I     2.404    1.057     1.416     .361  -.032000   .134000   .1910000      .000      .000  
+86 329 46518.00 I  -.036090  .000432   .132981  .000340  I  .1886785  .0000722  1.8834 0.0508  I     1.896    1.439     1.028     .507  -.034000   .135000   .1892000      .000      .000  
+86 330 46519.00 I  -.038555  .000707   .134187  .000967  I  .1869491  .0000794  1.5745 0.0537  I     1.779    1.439      .660     .507  -.036000   .136000   .1878000      .000      .000  
+86 331 46520.00 I  -.040914  .000707   .135472  .000967  I  .1855148  .0000795  1.3096 0.0519  I     2.070    1.184      .432     .427  -.039000   .137000   .1865000      .000      .000  
+86 4 1 46521.00 I  -.043172  .000851   .136853  .000919  I  .1842927  .0000669  1.1563 0.0457  I     2.538    1.184      .328     .427  -.041000   .139000   .1854000      .000      .000  
+86 4 2 46522.00 I  -.045336  .000693   .138344  .000839  I  .1831580  .0000452  1.1357 0.0392  I     2.972     .799      .287     .307  -.043000   .140000   .1844000      .000      .000  
+86 4 3 46523.00 I  -.047454  .000739   .139934  .000889  I  .1819794  .0000407  1.2407 0.0303  I     3.279     .835      .268     .271  -.045000   .141000   .1832000      .000      .000  
+86 4 4 46524.00 I  -.049568  .000739   .141566  .000889  I  .1806466  .0000403  1.4357 0.0261  I     3.407     .835      .246     .271  -.047000   .143000   .1819000      .000      .000  
+86 4 5 46525.00 I  -.051700  .000666   .143185  .000878  I  .1790974  .0000328  1.6626 0.0241  I     3.293     .835      .228     .271  -.049000   .145000   .1803000      .000      .000  
+86 4 6 46526.00 I  -.053825  .000585   .144820  .000591  I  .1773314  .0000265  1.8596 0.0217  I     3.190     .960      .304     .308  -.051000   .146000   .1785000      .000      .000  
+86 4 7 46527.00 I  -.055874  .000509   .146521  .000656  I  .1754022  .0000283  1.9847 0.0189  I     3.276    1.193      .545     .235  -.053000   .148000   .1766000      .000      .000  
+86 4 8 46528.00 I  -.057773  .000441   .148339  .000558  I  .1733924  .0000270  2.0188 0.0217  I     3.503    1.292      .891     .112  -.055000   .150000   .1745000      .000      .000  
+86 4 9 46529.00 I  -.059477  .000481   .150309  .000655  I  .1713959  .0000329  1.9590 0.0251  I     3.852    1.388     1.175     .127  -.057000   .152000   .1725000      .000      .000  
+86 410 46530.00 I  -.060969  .000456   .152445  .000737  I  .1694985  .0000423  1.8268 0.0242  I     4.223    1.388     1.239     .127  -.059000   .154000   .1705000      .000      .000  
+86 411 46531.00 I  -.062290  .000459   .154704  .000682  I  .1677555  .0000355  1.6550 0.0274  I     4.511    1.388     1.075     .127  -.060000   .155000   .1687000      .000      .000  
+86 412 46532.00 I  -.063503  .000407   .157021  .000599  I  .1661917  .0000348  1.4737 0.0297  I     4.699    1.073      .844     .125  -.062000   .157000   .1671000      .000      .000  
+86 413 46533.00 I  -.064673  .000372   .159335  .000491  I  .1648015  .0000476  1.3121 0.0296  I     4.841     .106      .702     .123  -.064000   .159000   .1656000      .000      .000  
+86 414 46534.00 I  -.065863  .000384   .161602  .000515  I  .1635526  .0000480  1.1952 0.0338  I     4.954     .106      .624     .123  -.065000   .161000   .1643000      .000      .000  
+86 415 46535.00 I  -.067116  .000384   .163798  .000515  I  .1623914  .0000479  1.1375 0.0327  I     5.009     .106      .471     .123  -.067000   .163000   .1631000      .000      .000  
+86 416 46536.00 I  -.068423  .000363   .165915  .000232  I  .1612565  .0000443  1.1430 0.0342  I     5.081     .106      .213     .123  -.068000   .165000   .1619000      .000      .000  
+86 417 46537.00 I  -.069777  .000392   .167954  .000305  I  .1600837  .0000487  1.2136 0.0417  I     5.290     .147     -.016     .104  -.070000   .167000   .1607000      .000      .000  
+86 418 46538.00 I  -.071187  .000382   .169929  .000403  I  .1588076  .0000707  1.3492 0.0406  I     5.558     .179     -.121     .298  -.071000   .169000   .1594000      .000      .000  
+86 419 46539.00 I  -.072663  .000355   .171851  .000390  I  .1573670  .0000649  1.5398 0.0569  I     5.616     .179     -.211     .298  -.072000   .171000   .1579000      .000      .000  
+86 420 46540.00 I  -.074202  .000402   .173729  .000365  I  .1557132  .0000893  1.7749 0.0496  I     5.386     .179     -.414     .298  -.074000   .173000   .1562000      .000      .000  
+86 421 46541.00 I  -.075759  .000378   .175573  .000358  I  .1538128  .0000749  2.0234 0.0544  I     5.129     .179     -.609     .298  -.075000   .175000   .1541000      .000      .000  
+86 422 46542.00 I  -.077279  .000376   .177399  .000293  I  .1516836  .0000620  2.2204 0.0483  I     5.095     .338     -.507     .298  -.077000   .177000   .1519000      .000      .000  
+86 423 46543.00 I  -.078707  .000373   .179220  .000203  I  .1494123  .0000609  2.2967 0.0425  I     5.106     .443     -.038     .298  -.078000   .179000   .1495000      .000      .000  
+86 424 46544.00 I  -.080012  .000373   .181058  .000203  I  .1471433  .0000582  2.2138 0.0431  I     4.743     .443      .507     .298  -.079000   .181000   .1471000      .000      .000  
+86 425 46545.00 I  -.081171  .000373   .182934  .000203  I  .1450321  .0000611  1.9888 0.0322  I     3.941     .443      .786     .298  -.081000   .183000   .1449000      .000      .000  
+86 426 46546.00 I  -.082168  .000382   .184865  .000186  I  .1431903  .0000274  1.6900 0.0330  I     3.160     .443      .733     .298  -.082000   .185000   .1430000      .000      .000  
+86 427 46547.00 I  -.083015  .000386   .186866  .000483  I  .1416451  .0000251  1.4121 0.0210  I     2.866     .692      .546     .298  -.083000   .187000   .1414000      .000      .000  
+86 428 46548.00 I  -.083728  .000445   .188951  .000576  I  .1403312  .0000319  1.2389 0.0203  I     3.089     .872      .386     .298  -.084000   .189000   .1400000      .000      .000  
+86 429 46549.00 I  -.084328  .000445   .191137  .000576  I  .1391180  .0000319  1.2135 0.0212  I     3.553     .872      .224     .298  -.085000   .191000   .1388000      .000      .000  
+86 430 46550.00 I  -.084841  .000455   .193434  .000834  I  .1378587  .0000278  1.3246 0.0203  I     4.032     .872     -.009     .298  -.086000   .193000   .1376000      .000      .000  
+86 5 1 46551.00 I  -.085302  .000636   .195807  .000796  I  .1364402  .0000250  1.5216 0.0172  I     4.423     .784     -.239     .298  -.087000   .195000   .1362000      .000      .000  
+86 5 2 46552.00 I  -.085766  .000660   .198188  .000836  I  .1348073  .0000203  1.7433 0.0194  I     4.716     .709     -.359     .298  -.088000   .197000   .1346000      .000      .000  
+86 5 3 46553.00 I  -.086310  .000888   .200499  .000978  I  .1329628  .0000297  1.9374 0.0206  I     4.895     .496     -.350     .298  -.089000   .199000   .1328000      .000      .000  
+86 5 4 46554.00 I  -.087006  .001011   .202681  .001077  I  .1309539  .0000359  2.0672 0.0268  I     4.888     .496     -.240     .298  -.090000   .201000   .1309000      .000      .000  
+86 5 5 46555.00 I  -.087865  .000839   .204743  .000956  I  .1288565  .0000445  2.1128 0.0259  I     4.777     .496     -.085     .298  -.090000   .203000   .1289000      .000      .000  
+86 5 6 46556.00 I  -.088838  .000672   .206708  .000870  I  .1267570  .0000374  2.0722 0.0295  I     4.610     .496      .108     .298  -.091000   .205000   .1268000      .000      .000  
+86 5 7 46557.00 I  -.089873  .000225   .208598  .000224  I  .1247376  .0000388  1.9551 0.0275  I     4.468     .257      .263     .109  -.092000   .207000   .1248000      .000      .000  
+86 5 8 46558.00 I  -.090917  .000225   .210433  .000224  I  .1228653  .0000404  1.7824 0.0311  I     4.441     .791      .233     .134  -.092000   .210000   .1230000      .000      .000  
+86 5 9 46559.00 I  -.091923  .000225   .212232  .000224  I  .1211815  .0000486  1.5834 0.0306  I     4.507     .791     -.005     .134  -.093000   .212000   .1214000      .000      .000  
+86 510 46560.00 I  -.092849  .000225   .214013  .000224  I  .1196996  .0000461  1.3810 0.0305  I     4.574     .791     -.246     .134  -.094000   .214000   .1199000      .000      .000  
+86 511 46561.00 I  -.093688  .000275   .215808  .000204  I  .1184135  .0000370  1.1966 0.0366  I     4.600     .791     -.292     .134  -.094000   .216000   .1187000      .000      .000  
+86 512 46562.00 I  -.094451  .000623   .217648  .000550  I  .1172924  .0000569  1.0543 0.0384  I     4.567     .342     -.178     .262  -.095000   .218000   .1176000      .000      .000  
+86 513 46563.00 I  -.095149  .000771   .219567  .000658  I  .1162853  .0000673  0.9707 0.0443  I     4.458     .434     -.148     .375  -.095000   .221000   .1167000      .000      .000  
+86 514 46564.00 I  -.095796  .000726   .221597  .000626  I  .1153295  .0000679  0.9516 0.0443  I     4.353     .434     -.395     .375  -.096000   .223000   .1157000      .000      .000  
+86 515 46565.00 I  -.096430  .000617   .223764  .000699  I  .1143641  .0000575  0.9864 0.0446  I     4.420     .577     -.856     .313  -.096000   .225000   .1147000      .000      .000  
+86 516 46566.00 I  -.097129  .000617   .226072  .000699  I  .1133334  .0000578  1.0910 0.0421  I     4.631     .577     -.962     .313  -.097000   .227000   .1135000      .000      .000  
+86 517 46567.00 I  -.097864  .000627   .228443  .000700  I  .1121536  .0000616  1.2810 0.0477  I     4.761     .577     -.606     .313  -.097000   .229000   .1122000      .000      .000  
+86 518 46568.00 I  -.098581  .000702   .230775  .000824  I  .1107561  .0000760  1.5170 0.0463  I     4.713     .634     -.336     .287  -.098000   .232000   .1107000      .000      .000  
+86 519 46569.00 I  -.099267  .000362   .233012  .000701  I  .1091256  .0000692  1.7350 0.0547  I     4.612     .739     -.291     .193  -.098000   .234000   .1089000      .000      .000  
+86 520 46570.00 I  -.099946  .000418   .235158  .000780  I  .1073146  .0000787  1.8676 0.0537  I     4.623     .739     -.162     .193  -.099000   .236000   .1070000      .000      .000  
+86 521 46571.00 I  -.100619  .000345   .237225  .000474  I  .1054365  .0000821  1.8619 0.0557  I     4.658     .791      .222     .298  -.099000   .238000   .1051000      .000      .000  
+86 522 46572.00 I  -.101277  .000516   .239225  .000479  I  .1036423  .0000789  1.7018 0.0579  I     4.430     .292      .623     .411  -.100000   .240000   .1033000      .000      .000  
+86 523 46573.00 I  -.101901  .000508   .241178  .000515  I  .1020719  .0000816  1.4247 0.0564  I     3.913     .292      .668     .411  -.100000   .242000   .1018000      .000      .000  
+86 524 46574.00 I  -.102451  .000508   .243121  .000515  I  .1008047  .0000806  1.1122 0.0545  I     3.446     .292      .302     .411  -.100000   .244000   .1006000      .000      .000  
+86 525 46575.00 I  -.102876  .000508   .245094  .000515  I  .0998261  .0000723  0.8639 0.0492  I     3.318     .292     -.189     .411  -.101000   .246000   .0997000      .000      .000  
+86 526 46576.00 I  -.103160  .000430   .247103  .000351  I  .0990329  .0000565  0.7476 0.0438  I     3.503     .292     -.570     .411  -.102000   .248000   .0990000      .000      .000  
+86 527 46577.00 I  -.103325  .000412   .249134  .000348  I  .0982836  .0000493  0.7731 0.0427  I     3.748     .445     -.844     .373  -.102000   .250000   .0983000      .000      .000  
+86 528 46578.00 I  -.103399  .000294   .251173  .000337  I  .0974527  .0000640  0.9007 0.0359  I     3.846     .558    -1.049     .329  -.103000   .252000   .0976000      .000      .000  
+86 529 46579.00 I  -.103404  .000294   .253206  .000337  I  .0964718  .0000523  1.0595 0.0420  I     3.766     .558    -1.078     .329  -.103000   .253000   .0967000      .000      .000  
+86 530 46580.00 I  -.103346  .000294   .255218  .000337  I  .0953424  .0000545  1.1922 0.0370  I     3.622     .558     -.820     .329  -.103000   .255000   .0956000      .000      .000  
+86 531 46581.00 I  -.103243  .000340   .257218  .000427  I  .0941066  .0000523  1.2672 0.0335  I     3.537     .558     -.380     .329  -.104000   .257000   .0944000      .000      .000  
+86 6 1 46582.00 I  -.103123  .000329   .259234  .000478  I  .0928334  .0000390  1.2660 0.0381  I     3.595     .666      .001     .297  -.104000   .259000   .0932000      .000      .000  
+86 6 2 46583.00 I  -.103011  .000363   .261304  .000555  I  .0915998  .0000554  1.1893 0.0294  I     3.723     .760      .209     .260  -.104000   .261000   .0919000      .000      .000  
+86 6 3 46584.00 I  -.102934  .000361   .263462  .000531  I  .0904755  .0000439  1.0504 0.0354  I     3.703     .760      .326     .260  -.104000   .263000   .0908000      .000      .000  
+86 6 4 46585.00 I  -.102910  .000361   .265735  .000531  I  .0895140  .0000442  0.8663 0.0291  I     3.396     .760      .412     .260  -.104000   .265000   .0899000      .000      .000  
+86 6 5 46586.00 I  -.102914  .000325   .268094  .000538  I  .0887517  .0000383  0.6557 0.0257  I     2.922     .760      .368     .260  -.104000   .267000   .0891000      .000      .000  
+86 6 6 46587.00 I  -.102903  .000303   .270487  .000637  I  .0882035  .0000261  0.4423 0.0380  I     2.444     .541      .103     .354  -.103000   .269000   .0885000      .000      .000  
+86 6 7 46588.00 I  -.102833  .000277   .272860  .000635  I  .0878589  .0000657  0.2534 0.0409  I     1.995     .791     -.247     .427  -.103000   .271000   .0881000      .000      .000  
+86 6 8 46589.00 I  -.102660  .000277   .275152  .000635  I  .0876797  .0000776  0.1155 0.0515  I     1.551     .791     -.400     .427  -.102000   .274000   .0879000      .000      .000  
+86 6 9 46590.00 I  -.102342  .000308   .277314  .000691  I  .0876058  .0000793  0.0440 0.0558  I     1.124     .791     -.231     .427  -.101000   .276000   .0878000      .000      .000  
+86 610 46591.00 I  -.101845  .000295   .279349  .000590  I  .0875678  .0000801  0.0442 0.0536  I      .738     .791      .072     .427  -.100000   .278000   .0877000      .000      .000  
+86 611 46592.00 I  -.101155  .000745   .281292  .000608  I  .0874923  .0000721  0.1197 0.0682  I      .452     .352      .230     .389  -.100000   .280000   .0875000      .000      .000  
+86 612 46593.00 I  -.100277  .000891   .283176  .000502  I  .0873049  .0001104  0.2659 0.0766  I      .407     .412      .137     .318  -.099000   .282000   .0872000      .000      .000  
+86 613 46594.00 I  -.099225  .000948   .285031  .000543  I  .0869416  .0001352  0.4690 0.0864  I      .725     .412     -.181     .318  -.098000   .284000   .0867000      .000      .000  
+86 614 46595.00 I  -.098024  .000948   .286885  .000543  I  .0863563  .0001329  0.7039 0.0865  I     1.320     .412     -.729     .318  -.096000   .287000   .0859000      .000      .000  
+86 615 46596.00 I  -.096693  .000804   .288773  .000483  I  .0855339  .0001080  0.9385 0.0698  I     1.819     .537    -1.207     .412  -.095000   .289000   .0850000      .000      .000  
+86 616 46597.00 I  -.095276  .000468   .290712  .000299  I  .0844919  .0000425  1.1354 0.0582  I     1.940     .615    -1.369     .526  -.094000   .291000   .0838000      .000      .000  
+86 617 46598.00 I  -.093812  .000488   .292705  .000264  I  .0832929  .0000433  1.2429 0.0290  I     1.603     .601     -.949     .562  -.093000   .293000   .0826000      .000      .000  
+86 618 46599.00 I  -.092335  .000253   .294749  .000213  I  .0820471  .0000394  1.2274 0.0249  I      .806     .667      .112     .613  -.092000   .296000   .0814000      .000      .000  
+86 619 46600.00 I  -.090889  .000253   .296847  .000213  I  .0808817  .0000246  1.0809 0.0240  I     -.121     .667      .732     .613  -.090000   .298000   .0803000      .000      .000  
+86 620 46601.00 I  -.089542  .000276   .299006  .000219  I  .0799154  .0000275  0.8439 0.0183  I     -.865     .667      .731     .613  -.089000   .300000   .0794000      .000      .000  
+86 621 46602.00 I  -.088276  .000304   .301210  .000214  I  .0791929  .0000271  0.6097 0.0182  I    -1.169     .484      .461     .464  -.088000   .303000   .0788000      .000      .000  
+86 622 46603.00 I  -.087034  .000261   .303439  .000158  I  .0786670  .0000237  0.4625 0.0451  I     -.954     .129      .146     .187  -.087000   .305000   .0784000      .000      .000  
+86 623 46604.00 I  -.085751  .000473   .305684  .000297  I  .0782238  .0000861  0.4477 0.0466  I     -.436     .157     -.058     .298  -.085000   .307000   .0781000      .000      .000  
+86 624 46605.00 I  -.084370  .000482   .307936  .000555  I  .0777276  .0000901  0.5650 0.0586  I      .083     .157     -.227     .298  -.084000   .309000   .0777000      .000      .000  
+86 625 46606.00 I  -.082897  .000437   .310196  .000500  I  .0770662  .0000795  0.7650 0.0619  I      .416     .157     -.434     .298  -.083000   .311000   .0771000      .000      .000  
+86 626 46607.00 I  -.081352  .000387   .312466  .000695  I  .0761962  .0000850  0.9692 0.0674  I      .433     .340     -.528     .239  -.081000   .313000   .0762000      .000      .000  
+86 627 46608.00 I  -.079745  .000223   .314743  .000834  I  .0751471  .0001089  1.1157 0.0723  I      .037     .454     -.300     .336  -.080000   .316000   .0752000      .000      .000  
+86 628 46609.00 I  -.078095  .000337   .317015  .000862  I  .0739938  .0001171  1.1754 0.0766  I     -.667     .454      .201     .336  -.079000   .318000   .0740000      .000      .000  
+86 629 46610.00 I  -.076437  .000337   .319251  .000862  I  .0728272  .0001078  1.1426 0.0760  I    -1.368     .454      .677     .336  -.077000   .320000   .0728000      .000      .000  
+86 630 46611.00 I  -.074819  .000515   .321453  .000722  I  .0717335  .0000968  1.0352 0.0692  I    -1.836     .454      .896     .336  -.076000   .322000   .0717000      .000      .000  
+86 7 1 46612.00 I  -.073288  .000485   .323644  .000715  I  .0707726  .0000867  0.8803 0.0602  I    -2.137     .406      .909     .272  -.075000   .324000   .0707000      .000      .000  
+86 7 2 46613.00 I  -.071864  .000618   .325834  .000591  I  .0699821  .0000717  0.6979 0.0504  I    -2.431     .352      .853     .189  -.073000   .326000   .0699000      .000      .000  
+86 7 3 46614.00 I  -.070558  .000571   .328031  .000431  I  .0693782  .0000516  0.5115 0.0401  I    -2.703     .352      .686     .189  -.072000   .328000   .0693000      .000      .000  
+86 7 4 46615.00 I  -.069363  .000568   .330236  .000420  I  .0689524  .0000358  0.3452 0.0312  I    -2.815     .294      .256     .170  -.070000   .330000   .0689000      .000      .000  
+86 7 5 46616.00 I  -.068207  .000531   .332442  .000447  I  .0686737  .0000351  0.2210 0.0260  I    -2.748     .294     -.343     .170  -.069000   .332000   .0686000      .000      .000  
+86 7 6 46617.00 I  -.067004  .000402   .334648  .000425  I  .0684897  .0000376  0.1588 0.0268  I    -2.588     .305     -.765     .276  -.067000   .334000   .0684000      .000      .000  
+86 7 7 46618.00 I  -.065692  .000478   .336846  .000284  I  .0683299  .0000404  0.1751 0.0290  I    -2.382     .267     -.724     .324  -.066000   .336000   .0683000      .000      .000  
+86 7 8 46619.00 I  -.064318  .000478   .339016  .000284  I  .0681120  .0000442  0.2738 0.0300  I    -2.168     .267     -.355     .324  -.064000   .338000   .0681000      .000      .000  
+86 7 9 46620.00 I  -.062954  .000899   .341131  .000989  I  .0677608  .0000444  0.4369 0.0400  I    -2.023     .267     -.012     .324  -.063000   .340000   .0677000      .000      .000  
+86 710 46621.00 I  -.061644  .000820   .343144  .000973  I  .0672256  .0000666  0.6377 0.0394  I    -1.974     .320      .132     .378  -.061000   .342000   .0672000      .000      .000  
+86 711 46622.00 I  -.060394  .000826   .344996  .000934  I  .0664822  .0000652  0.8482 0.0482  I    -1.955     .498      .138     .262  -.060000   .344000   .0665000      .000      .000  
+86 712 46623.00 I  -.059201  .000921   .346626  .001008  I  .0655386  .0000698  1.0305 0.0475  I    -1.915     .589      .140     .127  -.058000   .346000   .0656000      .000      .000  
+86 713 46624.00 I  -.057992  .000905   .348012  .001004  I  .0644446  .0000690  1.1426 0.0489  I    -1.927     .589      .210     .127  -.056000   .348000   .0645000      .000      .000  
+86 714 46625.00 I  -.056638  .000905   .349195  .001004  I  .0632836  .0000685  1.1641 0.0470  I    -2.155     .589      .343     .127  -.054000   .350000   .0633000      .000      .000  
+86 715 46626.00 I  -.055124  .000455   .350248  .000640  I  .0621501  .0000639  1.0844 0.0462  I    -2.699     .519      .484     .135  -.053000   .351000   .0622000      .000      .000  
+86 716 46627.00 I  -.053477  .000449   .351247  .000655  I  .0611486  .0000619  0.9034 0.0393  I    -3.492     .486      .574     .161  -.051000   .353000   .0611000      .000      .000  
+86 717 46628.00 I  -.051711  .000343   .352262  .000696  I  .0603652  .0000458  0.6566 0.0417  I    -4.316     .353      .505     .189  -.049000   .354000   .0602000      .000      .000  
+86 718 46629.00 I  -.049825  .000343   .353364  .000696  I  .0598363  .0000560  0.4071 0.0364  I    -4.909     .353      .203     .189  -.047000   .356000   .0597000      .000      .000  
+86 719 46630.00 I  -.047823  .000337   .354603  .000725  I  .0595248  .0000566  0.2361 0.0375  I    -5.199     .353     -.040     .189  -.045000   .358000   .0593000      .000      .000  
+86 720 46631.00 I  -.045742  .000314   .355959  .000674  I  .0593177  .0000499  0.2042 0.0368  I    -5.252     .353     -.015     .189  -.043000   .359000   .0591000      .000      .000  
+86 721 46632.00 I  -.043634  .000447   .357389  .000576  I  .0590660  .0000471  0.3234 0.0363  I    -5.208     .824      .239     .228  -.042000   .360000   .0588000      .000      .000  
+86 722 46633.00 I  -.041552  .000504   .358849  .000394  I  .0586337  .0000527  0.5541 0.0352  I    -5.075    1.091      .470     .253  -.040000   .362000   .0584000      .000      .000  
+86 723 46634.00 I  -.039560  .000504   .360304  .000394  I  .0579461  .0000522  0.8198 0.0323  I    -4.673    1.330      .409     .241  -.038000   .363000   .0577000      .000      .000  
+86 724 46635.00 I  -.037728  .000341   .361725  .000391  I  .0570104  .0000374  1.0368 0.0309  I    -3.925    1.330      .028     .241  -.036000   .364000   .0568000      .000      .000  
+86 725 46636.00 I  -.036067  .000542   .363091  .000362  I  .0559092  .0000329  1.1439 0.0268  I    -3.423    1.164     -.284     .216  -.034000   .365000   .0558000      .000      .000  
+86 726 46637.00 I  -.034531  .000524   .364382  .000338  I  .0547645  .0000385  1.1252 0.0262  I    -3.883    1.015     -.189     .176  -.033000   .367000   .0547000      .000      .000  
+86 727 46638.00 I  -.033067  .000538   .365577  .000314  I  .0536947  .0000407  0.9987 0.0276  I    -5.065     .975      .139     .112  -.031000   .368000   .0536000      .000      .000  
+86 728 46639.00 I  -.031637  .000538   .366669  .000314  I  .0527888  .0000396  0.8070 0.0286  I    -6.121     .975      .350     .112  -.029000   .368000   .0527000      .000      .000  
+86 729 46640.00 I  -.030274  .000538   .367690  .000314  I  .0520890  .0000403  0.5904 0.0283  I    -6.605     .542      .482     .298  -.028000   .369000   .0520000      .000      .000  
+86 730 46641.00 I  -.028996  .000495   .368666  .000545  I  .0516086  .0000405  0.3719 0.0321  I    -6.661     .476      .679     .298  -.026000   .370000   .0515000      .000      .000  
+86 731 46642.00 I  -.027795  .000367   .369619  .000914  I  .0513373  .0000501  0.1771 0.0348  I    -6.537     .581      .890     .146  -.025000   .371000   .0511000      .000      .000  
+86 8 1 46643.00 I  -.026634  .000390   .370570  .001029  I  .0512372  .0000565  0.0338 0.0385  I    -6.350     .507      .876     .203  -.024000   .372000   .0509000      .000      .000  
+86 8 2 46644.00 I  -.025468  .000348   .371543  .001071  I  .0512451  .0000585 -0.0358 0.0406  I    -6.215     .507      .458     .203  -.022000   .372000   .0509000      .000      .000  
+86 8 3 46645.00 I  -.024242  .000334   .372545  .001074  I  .0512849  .0000583 -0.0336 0.0396  I    -6.247     .507     -.223     .203  -.021000   .373000   .0509000      .000      .000  
+86 8 4 46646.00 I  -.022891  .000315   .373498  .001009  I  .0512841  .0000533  0.0534 0.0384  I    -6.523     .507     -.616     .203  -.020000   .374000   .0508000      .000      .000  
+86 8 5 46647.00 I  -.021434  .000340   .374402  .000948  I  .0511452  .0000501  0.2392 0.0354  I    -6.937     .504     -.530     .178  -.019000   .375000   .0506000      .000      .000  
+86 8 6 46648.00 I  -.019918  .000366   .375285  .000653  I  .0507817  .0000467  0.4970 0.0325  I    -7.341     .369     -.184     .141  -.018000   .376000   .0502000      .000      .000  
+86 8 7 46649.00 I  -.018396  .000306   .376154  .000546  I  .0501389  .0000415  0.7916 0.0299  I    -7.655     .370      .158     .298  -.017000   .377000   .0496000      .000      .000  
+86 8 8 46650.00 I  -.016918  .000306   .377001  .000546  I  .0492042  .0000375  1.0690 0.0376  I    -7.769     .370      .353     .298  -.016000   .378000   .0487000      .000      .000  
+86 8 9 46651.00 I  -.015510  .000422   .377825  .000475  I  .0480271  .0000626  1.2678 0.0339  I    -7.657     .370      .451     .298  -.014000   .379000   .0476000      .000      .000  
+86 810 46652.00 I  -.014168  .000409   .378641  .000444  I  .0467073  .0000564  1.3504 0.0493  I    -7.410     .317      .569     .298  -.013000   .380000   .0464000      .000      .000  
+86 811 46653.00 I  -.012882  .000525   .379462  .000548  I  .0453699  .0000762  1.3023 0.0473  I    -7.225     .252      .745     .298  -.012000   .381000   .0452000      .000      .000  
+86 812 46654.00 I  -.011640  .000455   .380306  .000591  I  .0441420  .0000760  1.1363 0.0523  I    -7.301     .252      .902     .298  -.011000   .382000   .0440000      .000      .000  
+86 813 46655.00 I  -.010403  .000455   .381186  .000591  I  .0431236  .0000717  0.8913 0.0503  I    -7.673     .252      .923     .298  -.009000   .383000   .0430000      .000      .000  
+86 814 46656.00 I  -.009141  .000380   .382083  .000560  I  .0423668  .0000658  0.6239 0.0384  I    -8.099     .252      .758     .298  -.008000   .384000   .0423000      .000      .000  
+86 815 46657.00 I  -.007839  .000200   .382970  .000597  I  .0418592  .0000277  0.4061 0.0369  I    -8.275     .434      .462     .140  -.007000   .385000   .0419000      .000      .000  
+86 816 46658.00 I  -.006482  .000084   .383837  .000578  I  .0415167  .0000336  0.3019 0.0229  I    -8.092     .560      .162     .198  -.005000   .386000   .0415000      .000      .000  
+86 817 46659.00 I  -.005055  .000259   .384681  .000673  I  .0412078  .0000364  0.3404 0.0254  I    -7.676     .604     -.014     .174  -.004000   .387000   .0412000      .000      .000  
+86 818 46660.00 I  -.003544  .000287   .385493  .000660  I  .0407924  .0000381  0.5094 0.0255  I    -7.233     .604     -.032     .174  -.002000   .388000   .0407000      .000      .000  
+86 819 46661.00 I  -.001936  .000312   .386285  .000680  I  .0401629  .0000356  0.7566 0.0238  I    -7.009     .604      .056     .174   .000000   .389000   .0401000      .000      .000  
+86 820 46662.00 I  -.000222  .000346   .387076  .000622  I  .0392789  .0000284  1.0047 0.0258  I    -7.149     .908      .191     .147   .001000   .390000   .0391000      .000      .000  
+86 821 46663.00 I   .001602  .000431   .387884  .000692  I  .0381772  .0000374  1.1819 0.0257  I    -7.574    1.082      .310     .298   .003000   .391000   .0380000      .000      .000  
+86 822 46664.00 I   .003514  .000431   .388702  .000692  I  .0369529  .0000429  1.2458 0.0259  I    -8.197    1.082      .361     .298   .005000   .391000   .0368000      .000      .000  
+86 823 46665.00 I   .005484  .000369   .389514  .000555  I  .0357240  .0000359  1.1945 0.0261  I    -8.983    1.226      .266     .298   .006000   .392000   .0355000      .000      .000  
+86 824 46666.00 I   .007473  .000712   .390301  .000519  I  .0345931  .0000299  1.0557 0.0233  I    -9.754    1.336     -.003     .119   .008000   .393000   .0344000      .000      .000  
+86 825 46667.00 I   .009457  .000800   .391054  .000540  I  .0336291  .0000296  0.8676 0.0241  I   -10.164    1.378     -.383     .108   .010000   .394000   .0334000      .000      .000  
+86 826 46668.00 I   .011415  .001113   .391764  .000520  I  .0328618  .0000378  0.6678 0.0253  I    -9.916    1.515     -.667     .116   .011000   .394000   .0327000      .000      .000  
+86 827 46669.00 I   .013324  .000934   .392421  .000443  I  .0322875  .0000410  0.4859 0.0332  I    -9.015    1.515     -.619     .116   .013000   .395000   .0321000      .000      .000  
+86 828 46670.00 I   .015140  .000934   .393021  .000443  I  .0318770  .0000546  0.3429 0.0344  I    -8.206    1.515     -.369     .116   .015000   .395000   .0317000      .000      .000  
+86 829 46671.00 I   .016870  .000765   .393579  .000647  I  .0315836  .0000553  0.2543 0.0372  I    -7.786    1.515     -.154     .116   .016000   .396000   .0314000      .000      .000  
+86 830 46672.00 I   .018538  .000695   .394122  .000655  I  .0313464  .0000506  0.2318 0.0396  I    -7.673     .903     -.097     .197   .018000   .396000   .0312000      .000      .000  
+86 831 46673.00 I   .020155  .000287   .394677  .000653  I  .0310971  .0000567  0.2782 0.0374  I    -7.751     .284     -.137     .241   .020000   .397000   .0310000      .000      .000  
+86 9 1 46674.00 I   .021728  .000374   .395271  .000610  I  .0307690  .0000550  0.3877 0.0389  I    -7.880     .284     -.104     .241   .021000   .397000   .0306000      .000      .000  
+86 9 2 46675.00 I   .023265  .000385   .395918  .000671  I  .0302994  .0000533  0.5640 0.0325  I    -7.919     .284      .089     .241   .023000   .397000   .0301000      .000      .000  
+86 9 3 46676.00 I   .024777  .000347   .396582  .000553  I  .0296206  .0000348  0.8013 0.0331  I    -7.869     .284      .367     .241   .025000   .398000   .0293000      .000      .000  
+86 9 4 46677.00 I   .026280  .000345   .397211  .000792  I  .0286889  .0000394  1.0622 0.0298  I    -7.808     .777      .565     .253   .026000   .398000   .0283000      .000      .000  
+86 9 5 46678.00 I   .027776  .000373   .397750  .000911  I  .0275048  .0000485  1.2973 0.0375  I    -7.794     .979      .561     .261   .028000   .398000   .0270000      .000      .000  
+86 9 6 46679.00 I   .029242  .000373   .398137  .000911  I  .0261182  .0000639  1.4606 0.0425  I    -7.873     .979      .347     .261   .030000   .398000   .0256000      .000      .000  
+86 9 7 46680.00 I   .030660  .000315   .398317  .000952  I  .0246201  .0000699  1.5143 0.0459  I    -8.114     .979      .048     .261   .031000   .399000   .0240000      .000      .000  
+86 9 8 46681.00 I   .032040  .000365   .398304  .001017  I  .0231308  .0000659  1.4445 0.0471  I    -8.540     .979     -.151     .261   .033000   .399000   .0226000      .000      .000  
+86 9 9 46682.00 I   .033398  .000368   .398165  .000983  I  .0217642  .0000630  1.2754 0.0509  I    -9.071    1.091     -.214     .212   .034000   .399000   .0213000      .000      .000  
+86 910 46683.00 I   .034748  .000417   .397969  .001018  I  .0205972  .0000776  1.0548 0.0487  I    -9.543    1.238     -.197     .109   .036000   .399000   .0202000      .000      .000  
+86 911 46684.00 I   .036109  .000417   .397793  .001018  I  .0196517  .0000743  0.8428 0.0494  I    -9.743    1.238     -.137     .109   .037000   .399000   .0194000      .000      .000  
+86 912 46685.00 I   .037511  .000417   .397718  .001018  I  .0188843  .0000612  0.7126 0.0439  I    -9.535    1.238      .004     .109   .038000   .399000   .0186000      .000      .000  
+86 913 46686.00 I   .038937  .000375   .397755  .000925  I  .0181818  .0000466  0.7166 0.0380  I    -9.018    1.238      .219     .109   .040000   .399000   .0180000      .000      .000  
+86 914 46687.00 I   .040369  .000331   .397869  .000803  I  .0174055  .0000449  0.8574 0.0328  I    -8.459    1.118      .428     .406   .041000   .399000   .0172000      .000      .000  
+86 915 46688.00 I   .041792  .000231   .398011  .000610  I  .0164323  .0000461  1.1024 0.0317  I    -8.112    1.259      .498     .852   .043000   .399000   .0161000      .000      .000  
+86 916 46689.00 I   .043195  .000173   .398133  .000476  I  .0151853  .0000448  1.3936 0.0295  I    -8.084    1.164      .253     .803   .044000   .399000   .0149000      .000      .000  
+86 917 46690.00 I   .044565  .000173   .398189  .000476  I  .0136551  .0000368  1.6549 0.0286  I    -8.296    1.164     -.352     .803   .045000   .399000   .0133000      .000      .000  
+86 918 46691.00 I   .045892  .000157   .398157  .000500  I  .0119068  .0000357  1.8222 0.0248  I    -8.272    1.164     -.317     .803   .046000   .399000   .0115000      .000      .000  
+86 919 46692.00 I   .047175  .000265   .398031  .000431  I  .0100508  .0000333  1.8690 0.0259  I    -8.133    1.044      .085     .646   .048000   .399000   .0097000      .000      .000  
+86 920 46693.00 I   .048412  .000309   .397812  .000473  I  .0082069  .0000375  1.8013 0.0242  I    -8.186    1.075      .299     .686   .049000   .399000   .0079000      .000      .000  
+86 921 46694.00 I   .049596  .000309   .397519  .000473  I  .0064771  .0000350  1.6467 0.0279  I    -8.448     .772      .356     .329   .050000   .399000   .0062000      .000      .000  
+86 922 46695.00 I   .050721  .000351   .397166  .000368  I  .0049286  .0000414  1.4467 0.0246  I    -8.762     .757      .350     .298   .051000   .399000   .0047000      .000      .000  
+86 923 46696.00 I   .051787  .000311   .396760  .000442  I  .0035841  .0000347  1.2455 0.0253  I    -8.891     .757      .407     .298   .052000   .398000   .0034000      .000      .000  
+86 924 46697.00 I   .052821  .000288   .396311  .000430  I  .0024267  .0000291  1.0769 0.0234  I    -8.700     .642      .600     .334   .054000   .398000   .0022000      .000      .000  
+86 925 46698.00 I   .053854  .000196   .395831  .000490  I  .0014112  .0000313  0.9656 0.0285  I    -8.267     .502      .852     .471   .055000   .398000   .0012000      .000      .000  
+86 926 46699.00 I   .054917  .000236   .395336  .000437  I  .0004703  .0000491  0.9301 0.0284  I    -7.799     .502      .972     .471   .056000   .398000   .0003000      .000      .000  
+86 927 46700.00 I   .056028  .000236   .394854  .000437  I -.0004763  .0000475  0.9767 0.0309  I    -7.441     .502      .860     .471   .057000   .397000  -.0007000      .000      .000  
+86 928 46701.00 I   .057188  .000210   .394417  .000640  I -.0015092  .0000375  1.1017 0.0302  I    -7.190     .502      .634     .471   .058000   .397000  -.0017000      .000      .000  
+86 929 46702.00 I   .058390  .000277   .394051  .000640  I -.0027028  .0000373  1.2960 0.0337  I    -7.034     .717      .492     .335   .060000   .396000  -.0029000      .000      .000  
+86 930 46703.00 I   .059614  .000306   .393782  .000758  I -.0041178  .0000561  1.5402 0.0339  I    -7.031     .882      .521     .298   .061000   .396000  -.0043000      .000      .000  
+8610 1 46704.00 I   .060833  .000288   .393642  .000729  I -.0057892  .0000565  1.8024 0.0337  I    -7.188     .882      .674     .298   .062000   .395000  -.0060000      .000      .000  
+8610 2 46705.00 I   .062023  .000279   .393644  .000804  I -.0077152  .0000373  2.0423 0.0355  I    -7.361     .882      .871     .298   .063000   .395000  -.0080000      .000      .000  
+8610 3 46706.00 I   .063183  .000252   .393743  .000711  I -.0098497  .0000429  2.2100 0.0289  I    -7.407     .882     1.035     .298   .064000   .395000  -.0101000      .000      .000  
+8610 4 46707.00 I   .064319  .000229   .393870  .000765  I -.0120944  .0000442  2.2555 0.0329  I    -7.329     .637     1.095     .270   .066000   .394000  -.0124000      .000      .000  
+8610 5 46708.00 I   .065439  .000122   .393967  .000637  I -.0143128  .0000500  2.1574 0.0336  I    -7.253     .185     1.017     .377   .067000   .394000  -.0146000      .000      .000  
+8610 6 46709.00 I   .066561  .000122   .393983  .000637  I -.0163691  .0000506  1.9391 0.0372  I    -7.298     .185      .842     .377   .068000   .393000  -.0167000      .000      .000  
+8610 7 46710.00 I   .067700  .000188   .393877  .000662  I -.0181709  .0000551  1.6606 0.0352  I    -7.481     .185      .649     .377   .069000   .393000  -.0184000      .000      .000  
+8610 8 46711.00 I   .068857  .000272   .393644  .000590  I -.0196953  .0000489  1.3971 0.0348  I    -7.687     .185      .498     .377   .071000   .392000  -.0200000      .000      .000  
+8610 9 46712.00 I   .070031  .000283   .393309  .000507  I -.0209933  .0000425  1.2184 0.0332  I    -7.781     .204      .383     .276   .072000   .392000  -.0212000      .000      .000  
+861010 46713.00 I   .071220  .000339   .392901  .000377  I -.0221744  .0000450  1.1668 0.0307  I    -7.663     .222      .286     .298   .073000   .392000  -.0224000      .000      .000  
+861011 46714.00 I   .072432  .000401   .392455  .000361  I -.0233677  .0000443  1.2377 0.0301  I    -7.283     .222      .249     .298   .074000   .391000  -.0237000      .000      .000  
+861012 46715.00 I   .073692  .000401   .392017  .000361  I -.0246816  .0000400  1.4038 0.0271  I    -6.668     .222      .339     .298   .076000   .391000  -.0251000      .000      .000  
+861013 46716.00 I   .075028  .000596   .391609  .000323  I -.0261924  .0000311  1.6210 0.0248  I    -5.993     .222      .547     .298   .077000   .391000  -.0267000      .000      .000  
+861014 46717.00 I   .076466  .000597   .391242  .000254  I -.0279202  .0000292  1.8281 0.0200  I    -5.429     .357      .813     .152   .078000   .391000  -.0285000      .000      .000  
+861015 46718.00 I   .078018  .000669   .390927  .000259  I -.0298285  .0000250  1.9749 0.0200  I    -5.071     .391     1.070     .185   .079000   .390000  -.0305000      .000      .000  
+861016 46719.00 I   .079677  .000669   .390666  .000259  I -.0318410  .0000274  2.0347 0.0187  I    -4.953     .391     1.232     .185   .081000   .390000  -.0326000      .000      .000  
+861017 46720.00 I   .081418  .000662   .390457  .000266  I -.0338647  .0000278  1.9948 0.0211  I    -5.071     .391     1.201     .185   .082000   .390000  -.0347000      .000      .000  
+861018 46721.00 I   .083132  .000760   .390277  .000255  I -.0358018  .0000322  1.8684 0.0206  I    -5.309     .391     1.085     .185   .083000   .389000  -.0367000      .000      .000  
+861019 46722.00 I   .084741  .000716   .390107  .000230  I -.0375854  .0000305  1.6936 0.0226  I    -5.595     .346      .969     .244   .085000   .389000  -.0384000      .000      .000  
+861020 46723.00 I   .086193  .000638   .389931  .000134  I -.0391847  .0000318  1.5058 0.0245  I    -5.920     .249      .829     .276   .086000   .389000  -.0400000      .000      .000  
+861021 46724.00 I   .087450  .000578   .389740  .000126  I -.0406038  .0000384  1.3383 0.0239  I    -6.152     .266      .737     .309   .087000   .388000  -.0413000      .000      .000  
+861022 46725.00 I   .088503  .000654   .389529  .000199  I -.0418764  .0000357  1.2160 0.0281  I    -6.104     .219      .742     .263   .088000   .388000  -.0425000      .000      .000  
+861023 46726.00 I   .089430  .000649   .389295  .000195  I -.0430563  .0000410  1.1551 0.0282  I    -5.762     .219      .811     .263   .090000   .388000  -.0435000      .000      .000  
+861024 46727.00 I   .090340  .000620   .389034  .000205  I -.0442099  .0000437  1.1642 0.0320  I    -5.297     .496      .850     .271   .091000   .387000  -.0445000      .000      .000  
+861025 46728.00 I   .091325  .000501   .388742  .000245  I -.0454034  .0000491  1.2293 0.0326  I    -4.856     .605      .860     .240   .092000   .387000  -.0455000      .000      .000  
+861026 46729.00 I   .092387  .000539   .388414  .000259  I -.0466797  .0000483  1.3279 0.0347  I    -4.473     .605      .920     .240   .094000   .387000  -.0467000      .000      .000  
+861027 46730.00 I   .093479  .000539   .388037  .000259  I -.0480733  .0000490  1.4687 0.0357  I    -4.226     .605     1.082     .240   .095000   .387000  -.0480000      .000      .000  
+861028 46731.00 I   .094579  .000535   .387595  .000350  I -.0496329  .0000527  1.6567 0.0335  I    -4.287     .735     1.307     .281   .096000   .386000  -.0495000      .000      .000  
+861029 46732.00 I   .095684  .000443   .387071  .000302  I -.0513921  .0000457  1.8608 0.0300  I    -4.725     .548     1.480     .397   .097000   .386000  -.0512000      .000      .000  
+861030 46733.00 I   .096822  .000479   .386457  .000309  I -.0533440  .0000285  2.0333 0.0270  I    -5.310     .213     1.572     .432   .099000   .385000  -.0532000      .000      .000  
+861031 46734.00 I   .098035  .000479   .385746  .000309  I -.0554304  .0000286  2.1210 0.0202  I    -5.665     .213     1.661     .432   .100000   .385000  -.0553000      .000      .000  
+8611 1 46735.00 I   .099348  .001097   .384936  .000490  I -.0575419  .0000286  2.0766 0.0185  I    -5.603     .213     1.789     .432   .101000   .385000  -.0574000      .000      .000  
+8611 2 46736.00 I   .100727  .000967   .384042  .000541  I -.0595353  .0000236  1.8875 0.0155  I    -5.247     .359     2.027     .435   .103000   .384000  -.0594000      .000      .000  
+8611 3 46737.00 I   .102130  .000565   .383079  .000564  I -.0612828  .0000120  1.5961 0.0126  I    -4.795     .608     2.200     .380   .104000   .383000  -.0612000      .000      .000  
+8611 4 46738.00 I   .103520  .000555   .382064  .000553  I -.0627206  .0000090  1.2839 0.0096  I    -4.383     .763     2.101     .279   .105000   .383000  -.0627000      .000      .000  
+8611 5 46739.00 I   .104920  .000610   .381025  .000589  I -.0638732  .0000149  1.0395 0.0109  I    -4.163     .849     1.671     .282   .106000   .382000  -.0638000      .000      .000  
+8611 6 46740.00 I   .106443  .000619   .380021  .000580  I -.0648435  .0000199  0.9268 0.0126  I    -4.070     .872     1.343     .268   .108000   .382000  -.0648000      .000      .000  
+8611 7 46741.00 I   .108160  .000313   .379146  .000550  I -.0657776  .0000204  0.9663 0.0155  I    -3.884     .872     1.189     .268   .109000   .381000  -.0659000      .000      .000  
+8611 8 46742.00 I   .110029  .000313   .378363  .000550  I -.0668165  .0000238  1.1268 0.0173  I    -3.586     .852     1.186     .466   .111000   .380000  -.0670000      .000      .000  
+8611 9 46743.00 I   .111974  .000337   .377602  .000501  I -.0680527  .0000279  1.3517 0.0481  I    -3.452     .817     1.514     .550   .113000   .379000  -.0683000      .000      .000  
+861110 46744.00 I   .113883  .000607   .376827  .000529  I -.0695212  .0000932  1.5804 0.0496  I    -3.482     .788     2.068     .624   .114000   .378000  -.0698000      .000      .000  
+861111 46745.00 I   .115670  .000607   .376009  .000529  I -.0711957  .0000951  1.7557 0.0639  I    -3.573     .788     2.524     .624   .116000   .377000  -.0716000      .000      .000  
+861112 46746.00 I   .117284  .000555   .375126  .000341  I -.0730039  .0000875  1.8448 0.0549  I    -3.730     .693     2.747     .732   .117000   .376000  -.0734000      .000      .000  
+861113 46747.00 I   .118681  .000491   .374161  .000311  I -.0748528  .0000550  1.8364 0.0513  I    -3.938     .538     2.787     .546   .118000   .374000  -.0753000      .000      .000  
+861114 46748.00 I   .119822  .000521   .373111  .000301  I -.0766462  .0000534  1.7364 0.0370  I    -4.109     .313     2.724     .244   .119000   .373000  -.0770000      .000      .000  
+861115 46749.00 I   .120675  .000521   .371975  .000301  I -.0783024  .0000494  1.5668 0.0300  I    -4.187     .313     2.591     .244   .120000   .372000  -.0786000      .000      .000  
+861116 46750.00 I   .121222  .000703   .370755  .000419  I -.0797678  .0000274  1.3612 0.0276  I    -4.231     .313     2.376     .244   .121000   .371000  -.0800000      .000      .000  
+861117 46751.00 I   .121525  .000689   .369473  .000388  I -.0810249  .0000247  1.1559 0.0175  I    -4.336     .313     2.087     .244   .121000   .370000  -.0813000      .000      .000  
+861118 46752.00 I   .121669  .000649   .368161  .000345  I -.0820903  .0000217  0.9828 0.0197  I    -4.500     .322     1.850     .290   .122000   .368000  -.0823000      .000      .000  
+861119 46753.00 I   .121731  .000672   .366845  .000439  I -.0830098  .0000308  0.8676 0.0182  I    -4.578     .332     1.810     .330   .122000   .367000  -.0832000      .000      .000  
+861120 46754.00 I   .121735  .000672   .365532  .000439  I -.0838496  .0000293  0.8247 0.0214  I    -4.401     .332     1.935     .330   .123000   .366000  -.0840000      .000      .000  
+861121 46755.00 I   .121695  .000672   .364223  .000439  I -.0846851  .0000296  0.8595 0.0180  I    -3.925     .332     2.033     .330   .123000   .365000  -.0848000      .000      .000  
+861122 46756.00 I   .121645  .000393   .362924  .000177  I -.0855930  .0000208  0.9675 0.0184  I    -3.228     .332     2.015     .330   .123000   .364000  -.0857000      .000      .000  
+861123 46757.00 I   .121615  .000459   .361638  .000166  I -.0866396  .0000218  1.1340 0.0193  I    -2.399     .259     2.005     .609   .124000   .362000  -.0868000      .000      .000  
+861124 46758.00 I   .121636  .000352   .360361  .000190  I -.0878742  .0000326  1.3400 0.0211  I    -1.596     .157     2.141     .796   .124000   .361000  -.0881000      .000      .000  
+861125 46759.00 I   .121737  .000397   .359088  .000060  I -.0893248  .0000362  1.5616 0.0245  I    -1.127     .157     2.365     .796   .124000   .360000  -.0895000      .000      .000  
+861126 46760.00 I   .121951  .000397   .357817  .000060  I -.0909912  .0000367  1.7645 0.0276  I    -1.218     .157     2.495     .796   .125000   .359000  -.0912000      .000      .000  
+861127 46761.00 I   .122300  .000318   .356546  .000126  I -.0928325  .0000418  1.9033 0.0244  I    -1.718     .157     2.511     .796   .125000   .358000  -.0931000      .000      .000  
+861128 46762.00 I   .122802  .000360   .355271  .000152  I -.0947608  .0000322  1.9311 0.0274  I    -2.288     .288     2.584     .576   .126000   .356000  -.0950000      .000      .000  
+861129 46763.00 I   .123482  .000291   .353996  .000173  I -.0966481  .0000354  1.8193 0.0234  I    -2.768     .376     2.799     .175   .126000   .355000  -.0969000      .000      .000  
+861130 46764.00 I   .124368  .000304   .352720  .000180  I -.0983574  .0000339  1.5817 0.0248  I    -3.167     .376     2.989     .175   .127000   .354000  -.0986000      .000      .000  
+8612 1 46765.00 I   .125479  .000281   .351444  .000335  I -.0997919  .0000348  1.2853 0.0228  I    -3.401     .376     2.977     .175   .127000   .352000  -.1001000      .000      .000  
+8612 2 46766.00 I   .126780  .000314   .350165  .000330  I -.1009402  .0000305  1.0254 0.0237  I    -3.328     .376     2.828     .175   .128000   .351000  -.1012000      .000      .000  
+8612 3 46767.00 I   .128205  .000423   .348882  .000436  I -.1018824  .0000321  0.8847 0.0265  I    -3.030     .571     2.765     .449   .129000   .350000  -.1022000      .000      .000  
+8612 4 46768.00 I   .129688  .000523   .347592  .000582  I -.1027627  .0000434  0.9033 0.0278  I    -2.817     .699     2.813     .542   .130000   .348000  -.1031000      .000      .000  
+8612 5 46769.00 I   .131165  .000523   .346274  .000582  I -.1037358  .0000454  1.0621 0.0314  I    -2.876     .699     2.716     .542   .131000   .347000  -.1041000      .000      .000  
+8612 6 46770.00 I   .132580  .000523   .344903  .000582  I -.1049099  .0000454  1.2898 0.0299  I    -3.056     .699     2.302     .542   .132000   .346000  -.1052000      .000      .000  
+8612 7 46771.00 I   .133924  .000504   .343466  .000466  I -.1063124  .0000390  1.5087 0.0288  I    -2.962     .648     2.037     .476   .133000   .344000  -.1066000      .000      .000  
+8612 8 46772.00 I   .135176  .000458   .341972  .000433  I -.1079059  .0000355  1.6636 0.0296  I    -2.605     .670     2.200     .392   .134000   .343000  -.1082000      .000      .000  
+8612 9 46773.00 I   .136306  .000468   .340435  .000420  I -.1096072  .0000445  1.7215 0.0239  I    -2.239     .645     2.655     .184   .135000   .341000  -.1099000      .000      .000  
+861210 46774.00 I   .137286  .000222   .338871  .000225  I -.1113158  .0000321  1.6803 0.0264  I    -2.014     .639     3.083     .118   .136000   .340000  -.1115000      .000      .000  
+861211 46775.00 I   .138103  .000222   .337292  .000225  I -.1129402  .0000285  1.5563 0.0467  I    -2.097     .639     3.282     .118   .137000   .339000  -.1131000      .000      .000  
+861212 46776.00 I   .138777  .000231   .335713  .000235  I -.1144085  .0000877  1.3724 0.0394  I    -2.396     .639     3.306     .118   .138000   .337000  -.1146000      .000      .000  
+861213 46777.00 I   .139334  .000265   .334158  .000202  I -.1156739  .0000735  1.1551 0.0639  I    -2.713     .553     3.256     .145   .138000   .336000  -.1158000      .000      .000  
+861214 46778.00 I   .139803  .000280   .332651  .000167  I -.1167165  .0000930  0.9313 0.0598  I    -2.944     .332     3.107     .150   .139000   .335000  -.1168000      .000      .000  
+861215 46779.00 I   .140216  .000305   .331220  .000166  I -.1175448  .0000943  0.7324 0.0674  I    -3.081     .332     2.826     .150   .140000   .334000  -.1177000      .000      .000  
+861216 46780.00 I   .140605  .000279   .329888  .000468  I -.1182030  .0000976  0.5987 0.0631  I    -3.127     .332     2.533     .150   .140000   .332000  -.1184000      .000      .000  
+861217 46781.00 I   .140997  .000241   .328664  .000424  I -.1187732  .0000840  0.5579 0.0575  I    -3.062     .332     2.424     .150   .141000   .331000  -.1191000      .000      .000  
+861218 46782.00 I   .141411  .000302   .327549  .000347  I -.1193512  .0000607  0.6144 0.0562  I    -2.802     .279     2.556     .183   .141000   .330000  -.1197000      .000      .000  
+861219 46783.00 I   .141868  .000301   .326542  .000441  I -.1200310  .0000748  0.7581 0.0480  I    -2.301     .214     2.787     .211   .142000   .329000  -.1204000      .000      .000  
+861220 46784.00 I   .142385  .000301   .325615  .000441  I -.1208896  .0000743  0.9686 0.0479  I    -1.635     .214     2.969     .211   .142000   .328000  -.1213000      .000      .000  
+861221 46785.00 I   .142978  .000369   .324735  .000440  I -.1219795  .0000597  1.2131 0.0466  I     -.951     .555     3.119     .163   .142000   .327000  -.1223000      .000      .000  
+861222 46786.00 I   .143609  .000387   .323868  .000309  I -.1233149  .0000561  1.4551 0.0410  I     -.381     .555     3.318     .163   .143000   .326000  -.1236000      .000      .000  
+861223 46787.00 I   .144204  .000387   .322977  .000309  I -.1248792  .0000562  1.6657 0.0319  I     -.066     .555     3.501     .163   .143000   .325000  -.1251000      .000      .000  
+861224 46788.00 I   .144684  .000450   .322025  .000439  I -.1266263  .0000303  1.8163 0.0409  I     -.063     .756     3.487     .298   .144000   .325000  -.1267000      .000      .000  
+861225 46789.00 I   .145013  .000450   .321001  .000439  I -.1284839  .0000594  1.8831 0.0348  I     -.257     .756     3.258     .298   .144000   .323000  -.1285000      .000      .000  
+861226 46790.00 I   .145222  .000450   .319917  .000439  I -.1303569  .0000626  1.8432 0.0490  I     -.498     .756     3.023     .298   .144000   .322000  -.1303000      .000      .000  
+861227 46791.00 I   .145351  .000366   .318788  .000232  I -.1321329  .0000779  1.6913 0.0444  I     -.810     .791     2.936     .298   .145000   .321000  -.1321000      .000      .000  
+861228 46792.00 I   .145443  .000417   .317630  .000216  I -.1337127  .0000629  1.4592 0.0499  I    -1.293     .350     2.867     .298   .145000   .320000  -.1336000      .000      .000  
+861229 46793.00 I   .145537  .000417   .316458  .000216  I -.1350465  .0000623  1.2127 0.0441  I    -1.816     .350     2.612     .298   .145000   .319000  -.1349000      .000      .000  
+861230 46794.00 I   .145675  .000417   .315287  .000216  I -.1361608  .0000617  1.0342 0.0343  I    -2.078     .350     2.249     .298   .145000   .317000  -.1360000      .000      .000  
+861231 46795.00 I   .145891  .000417   .314129  .000216  I -.1371607  .0000287  0.9931 0.0321  I    -2.023     .350     2.113     .298   .146000   .316000  -.1369000      .000      .000  
+87 1 1 46796.00 I   .146164  .000350   .312978  .000374  I -.1381993  .0000175  1.1088 0.0192  I    -1.921     .350     2.344     .298   .146000   .315000  -.1379000      .000      .000  
+87 1 2 46797.00 I   .146446  .000350   .311819  .000374  I -.1394157  .0000256  1.3366 0.0210  I    -2.019     .455     2.725     .276   .146000   .314000  -.1391000      .000      .000  
+87 1 3 46798.00 I   .146692  .000160   .310639  .000557  I -.1408817  .0000381  1.5921 0.0246  I    -2.244     .541     2.996     .387   .146000   .312000  -.1406000      .000      .000  
+87 1 4 46799.00 I   .146854  .000160   .309420  .000557  I -.1425808  .0000419  1.7903 0.0303  I    -2.388     .541     3.176     .387   .146000   .311000  -.1423000      .000      .000  
+87 1 5 46800.00 I   .146890  .000443   .308156  .000455  I -.1444251  .0000471  1.8770 0.0277  I    -2.406     .541     3.440     .387   .146000   .309000  -.1441000      .000      .000  
+87 1 6 46801.00 I   .146762  .000393   .306858  .000406  I -.1462932  .0000361  1.8389 0.0280  I    -2.419     .541     3.783     .387   .146000   .308000  -.1460000      .000      .000  
+87 1 7 46802.00 I   .146447  .000343   .305532  .000346  I -.1480683  .0000303  1.6969 0.0224  I    -2.418     .437     3.968     .280   .145000   .307000  -.1478000      .000      .000  
+87 1 8 46803.00 I   .145930  .000362   .304171  .000242  I -.1496648  .0000265  1.4881 0.0218  I    -2.267     .299     3.844     .298   .145000   .305000  -.1493000      .000      .000  
+87 1 9 46804.00 I   .145216  .000388   .302764  .000135  I -.1510340  .0000314  1.2476 0.0196  I    -1.948     .299     3.542     .298   .145000   .304000  -.1507000      .000      .000  
+87 110 46805.00 I   .144363  .000388   .301309  .000135  I -.1521611  .0000290  1.0103 0.0197  I    -1.598     .299     3.273     .298   .144000   .303000  -.1518000      .000      .000  
+87 111 46806.00 I   .143461  .000389   .299808  .000229  I -.1530660  .0000238  0.8072 0.0200  I    -1.314     .299     3.066     .298   .144000   .302000  -.1528000      .000      .000  
+87 112 46807.00 I   .142592  .000621   .298270  .000481  I -.1537942  .0000276  0.6603 0.0234  I    -1.168     .680     2.818     .298   .143000   .300000  -.1535000      .000      .000  
+87 113 46808.00 I   .141826  .000772   .296709  .000587  I -.1544114  .0000402  0.5879 0.0247  I    -1.258     .864     2.538     .298   .143000   .299000  -.1542000      .000      .000  
+87 114 46809.00 I   .141231  .000762   .295141  .000645  I -.1549984  .0000410  0.6008 0.0276  I    -1.612     .864     2.391     .298   .142000   .297000  -.1548000      .000      .000  
+87 115 46810.00 I   .140855  .000762   .293585  .000645  I -.1556391  .0000378  0.6919 0.0271  I    -2.059     .864     2.498     .298   .141000   .296000  -.1555000      .000      .000  
+87 116 46811.00 I   .140624  .000821   .292056  .000630  I -.1564038  .0000354  0.8478 0.0269  I    -2.160     .864     2.779     .298   .141000   .295000  -.1563000      .000      .000  
+87 117 46812.00 I   .140450  .000806   .290534  .000630  I -.1573502  .0000384  1.0503 0.0273  I    -1.797     .868     3.034     .141   .140000   .293000  -.1573000      .000      .000  
+87 118 46813.00 I   .140262  .000719   .289004  .000583  I -.1585097  .0000415  1.2687 0.0285  I    -1.227     .727     3.150     .247   .140000   .292000  -.1585000      .000      .000  
+87 119 46814.00 I   .139989  .000795   .287471  .000449  I -.1598827  .0000422  1.4722 0.0303  I     -.725     .647     3.136     .257   .139000   .290000  -.1598000      .000      .000  
+87 120 46815.00 I   .139575  .000887   .285944  .000507  I -.1614379  .0000441  1.6269 0.0285  I     -.460     .647     2.994     .257   .138000   .289000  -.1614000      .000      .000  
+87 121 46816.00 I   .139026  .000779   .284463  .000483  I -.1631089  .0000383  1.6987 0.0282  I     -.434     .647     2.959     .257   .138000   .288000  -.1631000      .000      .000  
+87 122 46817.00 I   .138418  .000724   .283070  .000512  I -.1648016  .0000352  1.6695 0.0252  I     -.627     .590     3.006     .211   .137000   .286000  -.1647000      .000      .000  
+87 123 46818.00 I   .137857  .000663   .281786  .000493  I -.1664139  .0000329  1.5384 0.0251  I     -.939     .369     3.089     .210   .137000   .285000  -.1664000      .000      .000  
+87 124 46819.00 I   .137431  .000761   .280616  .000974  I -.1678506  .0000358  1.3241 0.0229  I    -1.338     .421     3.264     .102   .136000   .284000  -.1678000      .000      .000  
+87 125 46820.00 I   .137166  .000807   .279539  .001125  I -.1690519  .0000319  1.0794 0.0227  I    -1.833     .458     3.406     .298   .136000   .282000  -.1690000      .000      .000  
+87 126 46821.00 I   .137038  .000710   .278561  .001084  I -.1700244  .0000278  0.8799 0.0206  I    -2.308     .458     3.324     .298   .135000   .281000  -.1700000      .000      .000  
+87 127 46822.00 I   .137011  .000732   .277704  .000597  I -.1708510  .0000262  0.7984 0.0210  I    -2.492     .422     3.018     .117   .135000   .280000  -.1708000      .000      .000  
+87 128 46823.00 I   .137001  .000813   .276949  .000608  I -.1716741  .0000315  0.8757 0.0204  I    -2.264     .329     2.722     .157   .135000   .279000  -.1717000      .000      .000  
+87 129 46824.00 I   .136835  .000769   .276208  .000599  I -.1726509  .0000313  1.0986 0.0209  I    -1.856     .329     2.595     .157   .134000   .277000  -.1727000      .000      .000  
+87 130 46825.00 I   .136335  .000648   .275387  .000272  I -.1738984  .0000274  1.4029 0.0207  I    -1.619     .329     2.529     .157   .134000   .276000  -.1740000      .000      .000  
+87 131 46826.00 I   .135381  .000663   .274410  .000333  I -.1754526  .0000270  1.6949 0.0198  I    -1.688     .329     2.507     .157   .133000   .275000  -.1756000      .000      .000  
+87 2 1 46827.00 I   .134106  .000779   .273266  .000425  I -.1772565  .0000285  1.8918 0.0196  I    -2.003     .647     2.552     .132   .133000   .274000  -.1774000      .000      .000  
+87 2 2 46828.00 I   .132707  .000783   .271968  .000325  I -.1791915  .0000284  1.9545 0.0207  I    -2.522     .869     2.706     .106   .132000   .273000  -.1793000      .000      .000  
+87 2 3 46829.00 I   .131373  .000713   .270544  .000400  I -.1811235  .0000301  1.8911 0.0205  I    -3.146    1.121     2.899     .116   .132000   .271000  -.1812000      .000      .000  
+87 2 4 46830.00 I   .130288  .000755   .269034  .000391  I -.1829432  .0000297  1.7363 0.0205  I    -3.543    1.121     2.877     .116   .132000   .270000  -.1830000      .000      .000  
+87 2 5 46831.00 I   .129572  .000756   .267504  .000383  I -.1845796  .0000279  1.5320 0.0379  I    -3.415    1.121     2.624     .116   .131000   .269000  -.1846000      .000      .000  
+87 2 6 46832.00 I   .129204  .000678   .266010  .000329  I -.1860049  .0000697  1.3209 0.0405  I    -3.125    1.099     2.476     .192   .131000   .268000  -.1860000      .000      .000  
+87 2 7 46833.00 I   .129075  .000661   .264586  .000256  I -.1872302  .0000761  1.1362 0.0547  I    -2.914    1.077     2.582     .240   .130000   .267000  -.1872000      .000      .000  
+87 2 8 46834.00 I   .129034  .000464   .263271  .000194  I -.1882929  .0000843  0.9981 0.0633  I    -2.667    1.068     2.719     .266   .130000   .266000  -.1882000      .000      .000  
+87 2 9 46835.00 I   .128903  .000572   .262120  .000247  I -.1892463  .0001011  0.9195 0.0609  I    -2.261     .916     2.539     .282   .129000   .265000  -.1891000      .000      .000  
+87 210 46836.00 I   .128587  .000512   .261167  .000283  I -.1901571  .0000879  0.9163 0.0647  I    -1.922     .916     2.259     .282   .129000   .264000  -.1900000      .000      .000  
+87 211 46837.00 I   .128150  .000504   .260373  .000272  I -.1911065  .0000807  0.9959 0.0548  I    -1.844     .839     2.163     .325   .128000   .263000  -.1909000      .000      .000  
+87 212 46838.00 I   .127667  .000465   .259684  .000266  I -.1921723  .0000655  1.1458 0.0526  I    -1.933     .593     2.319     .310   .128000   .262000  -.1920000      .000      .000  
+87 213 46839.00 I   .127184  .000435   .259047  .000316  I -.1934137  .0000675  1.3426 0.0480  I    -1.942     .616     2.635     .319   .128000   .262000  -.1933000      .000      .000  
+87 214 46840.00 I   .126747  .000356   .258408  .000315  I -.1948653  .0000703  1.5630 0.0409  I    -1.689     .616     2.886     .319   .127000   .261000  -.1948000      .000      .000  
+87 215 46841.00 I   .126370  .000531   .257736  .000338  I -.1965376  .0000461  1.7772 0.0404  I    -1.180     .616     2.971     .319   .127000   .260000  -.1965000      .000      .000  
+87 216 46842.00 I   .126024  .000444   .257013  .000290  I -.1984046  .0000398  1.9460 0.0328  I     -.623     .619     2.910     .245   .127000   .259000  -.1984000      .000      .000  
+87 217 46843.00 I   .125673  .000443   .256225  .000260  I -.2004034  .0000466  2.0363 0.0252  I     -.217     .513     2.767     .108   .126000   .258000  -.2004000      .000      .000  
+87 218 46844.00 I   .125290  .000447   .255354  .000286  I -.2024439  .0000309  2.0269 0.0281  I     -.066     .548     2.590     .119   .126000   .257000  -.2025000      .000      .000  
+87 219 46845.00 I   .124857  .000442   .254391  .000291  I -.2044244  .0000313  1.9191 0.0191  I     -.227     .548     2.391     .119   .125000   .256000  -.2045000      .000      .000  
+87 220 46846.00 I   .124399  .000436   .253371  .000304  I -.2062584  .0000223  1.7400 0.0181  I     -.559     .548     2.235     .119   .125000   .255000  -.2064000      .000      .000  
+87 221 46847.00 I   .123945  .000358   .252313  .000329  I -.2078958  .0000180  1.5349 0.0141  I     -.942     .507     2.147     .245   .124000   .254000  -.2081000      .000      .000  
+87 222 46848.00 I   .123508  .000276   .251228  .000304  I -.2093397  .0000173  1.3644 0.0122  I    -1.330     .478     2.078     .320   .124000   .253000  -.2096000      .000      .000  
+87 223 46849.00 I   .123087  .000318   .250117  .000382  I -.2106572  .0000166  1.2919 0.0118  I    -1.615     .570     1.967     .331   .123000   .252000  -.2109000      .000      .000  
+87 224 46850.00 I   .122661  .000318   .248976  .000382  I -.2119707  .0000160  1.3611 0.0114  I    -1.620     .570     1.819     .331   .123000   .251000  -.2122000      .000      .000  
+87 225 46851.00 I   .122246  .000355   .247844  .000390  I -.2134289  .0000157  1.5788 0.0118  I    -1.372     .570     1.562     .331   .122000   .250000  -.2137000      .000      .000  
+87 226 46852.00 I   .121872  .000386   .246727  .000395  I -.2151634  .0000173  1.9017 0.0129  I     -.824     .500     1.458     .267   .122000   .249000  -.2153000      .000      .000  
+87 227 46853.00 I   .121495  .000396   .245613  .000288  I -.2172400  .0000205  2.2476 0.0148  I     -.069     .537     1.607     .177   .121000   .247000  -.2173000      .000      .000  
+87 228 46854.00 I   .121053  .000572   .244482  .000489  I -.2196369  .0000239  2.5284 0.0169  I      .667     .455     1.802     .298   .121000   .246000  -.2196000      .000      .000  
+87 3 1 46855.00 I   .120474  .000502   .243307  .000416  I -.2222510  .0000269  2.6719 0.0172  I     1.163     .324     1.907     .298   .120000   .245000  -.2220000      .000      .000  
+87 3 2 46856.00 I   .119739  .000494   .242064  .000392  I -.2249249  .0000248  2.6481 0.0196  I     1.253     .324     1.940     .298   .119000   .243000  -.2245000      .000      .000  
+87 3 3 46857.00 I   .118859  .000602   .240737  .000344  I -.2274987  .0000285  2.4790 0.0228  I      .895     .362     1.931     .133   .118000   .242000  -.2268000      .000      .000  
+87 3 4 46858.00 I   .117846  .000632   .239311  .000335  I -.2298512  .0000383  2.2148 0.0280  I      .264     .396     1.831     .188   .118000   .240000  -.2290000      .000      .000  
+87 3 5 46859.00 I   .116699  .000632   .237782  .000335  I -.2319140  .0000481  1.9076 0.0318  I     -.342     .396     1.610     .188   .117000   .239000  -.2309000      .000      .000  
+87 3 6 46860.00 I   .115417  .000632   .236156  .000335  I -.2336699  .0000507  1.6109 0.0315  I     -.742     .396     1.381     .188   .116000   .237000  -.2326000      .000      .000  
+87 3 7 46861.00 I   .114084  .000539   .234471  .000347  I -.2351542  .0000408  1.3693 0.0308  I     -.914     .396     1.287     .188   .115000   .236000  -.2341000      .000      .000  
+87 3 8 46862.00 I   .112799  .000648   .232769  .000373  I -.2364341  .0000349  1.2044 0.0275  I     -.872     .547     1.312     .143   .115000   .235000  -.2355000      .000      .000  
+87 3 9 46863.00 I   .111657  .000608   .231094  .000414  I -.2375923  .0000368  1.1275 0.0253  I     -.623     .548     1.288     .298   .114000   .234000  -.2368000      .000      .000  
+87 310 46864.00 I   .110751  .000608   .229489  .000414  I -.2387201  .0000365  1.1437 0.0237  I     -.259     .548     1.122     .298   .113000   .233000  -.2382000      .000      .000  
+87 311 46865.00 I   .110161  .000608   .227994  .000414  I -.2399098  .0000298  1.2503 0.0210  I      .046     .548      .910     .298   .113000   .231000  -.2395000      .000      .000  
+87 312 46866.00 I   .109870  .000516   .226654  .000366  I -.2412443  .0000209  1.4278 0.0171  I      .167     .548      .815     .298   .112000   .230000  -.2411000      .000      .000  
+87 313 46867.00 I   .109779  .000476   .225503  .000307  I -.2427787  .0000167  1.6454 0.0146  I      .146     .437      .884     .298   .111000   .229000  -.2428000      .000      .000  
+87 314 46868.00 I   .109810  .000494   .224548  .000323  I -.2445374  .0000205  1.8700 0.0122  I      .100     .147     1.018     .298   .111000   .228000  -.2447000      .000      .000  
+87 315 46869.00 I   .109899  .000220   .223794  .000247  I -.2465079  .0000178  2.0621 0.0132  I      .054     .175     1.078     .298   .110000   .227000  -.2468000      .000      .000  
+87 316 46870.00 I   .109988  .000220   .223232  .000247  I -.2486381  .0000165  2.1838 0.0146  I     -.092     .175     1.006     .298   .109000   .226000  -.2490000      .000      .000  
+87 317 46871.00 I   .110020  .000225   .222818  .000342  I -.2508431  .0000231  2.2083 0.0135  I     -.389     .175      .864     .298   .109000   .225000  -.2512000      .000      .000  
+87 318 46872.00 I   .109928  .000465   .222495  .000380  I -.2530193  .0000213  2.1266 0.0209  I     -.803     .382      .771     .166   .108000   .224000  -.2534000      .000      .000  
+87 319 46873.00 I   .109637  .000595   .222199  .000472  I -.2550668  .0000349  1.9565 0.0205  I    -1.226     .422      .825     .261   .107000   .223000  -.2553000      .000      .000  
+87 320 46874.00 I   .109055  .000595   .221849  .000472  I -.2569179  .0000350  1.7431 0.0254  I    -1.483     .422     1.081     .261   .107000   .223000  -.2571000      .000      .000  
+87 321 46875.00 I   .108093  .000642   .221367  .000476  I -.2585563  .0000370  1.5396 0.0240  I    -1.407     .422     1.511     .261   .106000   .222000  -.2586000      .000      .000  
+87 322 46876.00 I   .106728  .000628   .220718  .000631  I -.2600186  .0000329  1.4006 0.0222  I     -.987     .422     1.678     .261   .105000   .221000  -.2599000      .000      .000  
+87 323 46877.00 I   .105080  .000703   .219945  .000615  I -.2613939  .0000247  1.3709 0.0210  I     -.374     .641     1.436     .279   .104000   .220000  -.2612000      .000      .000  
+87 324 46878.00 I   .103299  .000849   .219106  .000650  I -.2628015  .0000262  1.4646 0.0186  I      .211     .666      .920     .385   .104000   .219000  -.2624000      .000      .000  
+87 325 46879.00 I   .101537  .000737   .218262  .000651  I -.2643567  .0000279  1.6592 0.0192  I      .563     .669      .279     .390   .103000   .218000  -.2639000      .000      .000  
+87 326 46880.00 I   .099933  .000737   .217460  .000651  I -.2661370  .0000281  1.9052 0.0177  I      .565     .669     -.283     .390   .102000   .217000  -.2656000      .000      .000  
+87 327 46881.00 I   .098585  .000740   .216712  .000613  I -.2681613  .0000218  2.1337 0.0172  I      .453     .669     -.256     .390   .101000   .216000  -.2676000      .000      .000  
+87 328 46882.00 I   .097540  .000701   .216016  .000580  I -.2703756  .0000197  2.2758 0.0149  I      .446     .569      .102     .325   .100000   .215000  -.2698000      .000      .000  
+87 329 46883.00 I   .096745  .000568   .215368  .000214  I -.2726712  .0000202  2.2928 0.0165  I      .536     .286      .362     .345   .099000   .215000  -.2722000      .000      .000  
+87 330 46884.00 I   .096120  .000574   .214758  .000436  I -.2749188  .0000265  2.1827 0.0175  I      .600     .153      .500     .190   .098000   .214000  -.2745000      .000      .000  
+87 331 46885.00 I   .095597  .000478   .214178  .000519  I -.2770051  .0000285  1.9781 0.0189  I      .514     .148      .543     .298   .097000   .213000  -.2766000      .000      .000  
+87 4 1 46886.00 I   .095139  .000479   .213641  .000606  I -.2788608  .0000269  1.7308 0.0238  I      .296     .148      .506     .298   .096000   .212000  -.2785000      .000      .000  
+87 4 2 46887.00 I   .094713  .000526   .213178  .000636  I -.2804693  .0000381  1.4914 0.0329  I      .081     .148      .431     .142   .095000   .211000  -.2801000      .000      .000  
+87 4 3 46888.00 I   .094270  .000586   .212808  .000855  I -.2818591  .0000600  1.2985 0.0356  I      .009     .148      .408     .199   .094000   .211000  -.2815000      .000      .000  
+87 4 4 46889.00 I   .093722  .000586   .212509  .000855  I -.2830897  .0000601  1.1757 0.0405  I      .160     .148      .481     .199   .093000   .210000  -.2828000      .000      .000  
+87 4 5 46890.00 I   .092988  .000581   .212263  .000799  I -.2842360  .0000545  1.1293 0.0354  I      .550     .148      .552     .199   .092000   .210000  -.2839000      .000      .000  
+87 4 6 46891.00 I   .092064  .000439   .212060  .000776  I -.2853731  .0000375  1.1572 0.0303  I     1.122     .148      .464     .199   .091000   .209000  -.2850000      .000      .000  
+87 4 7 46892.00 I   .090975  .000389   .211868  .000591  I -.2865731  .0000263  1.2532 0.0196  I     1.729     .282      .166     .212   .090000   .209000  -.2862000      .000      .000  
+87 4 8 46893.00 I   .089743  .000262   .211650  .000501  I -.2878977  .0000113  1.4038 0.0148  I     2.151     .415     -.215     .199   .089000   .209000  -.2875000      .000      .000  
+87 4 9 46894.00 I   .088394  .000262   .211366  .000501  I -.2893924  .0000136  1.5894 0.0094  I     2.182     .415     -.513     .199   .088000   .208000  -.2890000      .000      .000  
+87 410 46895.00 I   .086971  .000262   .210974  .000501  I -.2910801  .0000149  1.7858 0.0102  I     1.782     .415     -.670     .199   .087000   .208000  -.2907000      .000      .000  
+87 411 46896.00 I   .085500  .000245   .210491  .000572  I -.2929587  .0000151  1.9663 0.0114  I     1.289     .415     -.610     .199   .086000   .208000  -.2926000      .000      .000  
+87 412 46897.00 I   .083991  .000195   .209938  .000697  I -.2949955  .0000173  2.0954 0.0143  I     1.040     .460     -.328     .300   .085000   .207000  -.2946000      .000      .000  
+87 413 46898.00 I   .082464  .000079   .209321  .000755  I -.2971198  .0000242  2.1356 0.0167  I     1.042     .472      .059     .310   .084000   .207000  -.2968000      .000      .000  
+87 414 46899.00 I   .080935  .000075   .208641  .000866  I -.2992297  .0000285  2.0650 0.0179  I     1.075     .464      .427     .346   .083000   .206000  -.2989000      .000      .000  
+87 415 46900.00 I   .079424  .000075   .207916  .000866  I -.3012155  .0000263  1.8917 0.0185  I      .888     .464      .634     .346   .082000   .206000  -.3009000      .000      .000  
+87 416 46901.00 I   .077952  .000107   .207220  .000774  I -.3029897  .0000235  1.6483 0.0187  I      .584     .603      .506     .366   .081000   .206000  -.3027000      .000      .000  
+87 417 46902.00 I   .076542  .000107   .206595  .000774  I -.3045084  .0000266  1.3937 0.0290  I      .436     .603      .085     .366   .080000   .206000  -.3043000      .000      .000  
+87 418 46903.00 I   .075219  .000140   .206060  .000777  I -.3057977  .0000530  1.2009 0.0453  I      .618     .654     -.416     .337   .078000   .205000  -.3055000      .000      .000  
+87 419 46904.00 I   .074010  .000333   .205614  .000467  I -.3069486  .0000866  1.1233 0.0506  I     1.113     .763     -.792     .391   .077000   .205000  -.3067000      .000      .000  
+87 420 46905.00 I   .072932  .000381   .205255  .000453  I -.3080894  .0000861  1.1812 0.0564  I     1.760     .668     -.887     .335   .076000   .205000  -.3079000      .000      .000  
+87 421 46906.00 I   .071990  .000437   .204990  .000679  I -.3093493  .0000724  1.3542 0.0543  I     2.315     .668     -.814     .335   .075000   .204000  -.3092000      .000      .000  
+87 422 46907.00 I   .071179  .000431   .204811  .000663  I -.3108178  .0000661  1.5874 0.0531  I     2.617     .615     -.744     .104   .074000   .204000  -.3107000      .000      .000  
+87 423 46908.00 I   .070473  .000461   .204690  .000707  I -.3125279  .0000777  1.8322 0.0485  I     2.629     .615     -.798     .104   .073000   .204000  -.3125000      .000      .000  
+87 424 46909.00 I   .069810  .000492   .204601  .000701  I -.3144718  .0000709  2.0460 0.0526  I     2.530     .615     -.853     .104   .072000   .203000  -.3145000      .000      .000  
+87 425 46910.00 I   .069116  .000492   .204498  .000701  I -.3165887  .0000710  2.1662 0.0436  I     2.565     .615     -.712     .104   .071000   .203000  -.3166000      .000      .000  
+87 426 46911.00 I   .068329  .000484   .204384  .000743  I -.3187598  .0000508  2.1533 0.0423  I     2.743     .696     -.408     .298   .070000   .203000  -.3187000      .000      .000  
+87 427 46912.00 I   .067396  .000538   .204269  .000780  I -.3208546  .0000461  2.0183 0.0412  I     2.986    1.105     -.124     .537   .069000   .202000  -.3207000      .000      .000  
+87 428 46913.00 I   .066290  .000560   .204134  .000855  I -.3227685  .0000650  1.7998 0.0286  I     3.201    1.248     -.041     .634   .068000   .202000  -.3225000      .000      .000  
+87 429 46914.00 I   .064990  .000469   .203955  .000781  I -.3244426  .0000337  1.5467 0.0363  I     3.229    1.248     -.241     .634   .067000   .202000  -.3241000      .000      .000  
+87 430 46915.00 I   .063489  .000422   .203714  .000795  I -.3258673  .0000325  1.3098 0.0233  I     2.930    1.090     -.662     .558   .066000   .202000  -.3254000      .000      .000  
+87 5 1 46916.00 I   .061855  .000400   .203438  .000720  I -.3270799  .0000323  1.1261 0.0206  I     2.627    1.006    -1.030     .513   .064000   .201000  -.3265000      .000      .000  
+87 5 2 46917.00 I   .060205  .000490   .203149  .000754  I -.3281429  .0000253  1.0126 0.0211  I     2.551    1.025    -1.156     .463   .063000   .201000  -.3275000      .000      .000  
+87 5 3 46918.00 I   .058669  .000431   .202858  .000682  I -.3291293  .0000273  0.9718 0.0240  I     2.695     .756    -1.062     .226   .062000   .201000  -.3284000      .000      .000  
+87 5 4 46919.00 I   .057350  .000398   .202549  .000674  I -.3301078  .0000407  0.9948 0.0254  I     2.917     .736     -.954     .245   .060000   .201000  -.3293000      .000      .000  
+87 5 5 46920.00 I   .056318  .000420   .202180  .000716  I -.3311396  .0000428  1.0799 0.0291  I     3.246     .674    -1.220     .229   .059000   .201000  -.3304000      .000      .000  
+87 5 6 46921.00 I   .055534  .000517   .201759  .000754  I -.3322852  .0000416  1.2180 0.0303  I     3.674     .731    -1.582     .248   .058000   .200000  -.3316000      .000      .000  
+87 5 7 46922.00 I   .054880  .000536   .201348  .000707  I -.3335849  .0000430  1.3842 0.0358  I     3.998     .706    -1.634     .256   .056000   .200000  -.3330000      .000      .000  
+87 5 8 46923.00 I   .054226  .000469   .201005  .000804  I -.3350588  .0000583  1.5661 0.0428  I     3.928     .409    -1.353     .211   .055000   .200000  -.3346000      .000      .000  
+87 5 9 46924.00 I   .053433  .000540   .200748  .000747  I -.3367160  .0000741  1.7447 0.0473  I     3.409     .463     -.962     .227   .054000   .200000  -.3363000      .000      .000  
+87 510 46925.00 I   .052428  .000540   .200544  .000747  I -.3385294  .0000746  1.8668 0.0444  I     2.699     .463     -.637     .227   .052000   .199000  -.3382000      .000      .000  
+87 511 46926.00 I   .051219  .000467   .200369  .000698  I -.3404133  .0000488  1.8800 0.0428  I     2.101     .558     -.335     .263   .051000   .199000  -.3401000      .000      .000  
+87 512 46927.00 I   .049840  .000374   .200207  .000485  I -.3422469  .0000418  1.7658 0.0325  I     1.637     .653     -.061     .204   .049000   .199000  -.3420000      .000      .000  
+87 513 46928.00 I   .048329  .000279   .200022  .000507  I -.3439092  .0000428  1.5445 0.0250  I     1.152     .735      .027     .118   .048000   .198000  -.3437000      .000      .000  
+87 514 46929.00 I   .046729  .000254   .199778  .000464  I -.3453195  .0000275  1.2738 0.0371  I      .704     .735     -.178     .118   .047000   .198000  -.3451000      .000      .000  
+87 515 46930.00 I   .045091  .000274   .199449  .000479  I -.3464683  .0000607  1.0374 0.0295  I      .633     .735     -.561     .118   .045000   .198000  -.3462000      .000      .000  
+87 516 46931.00 I   .043468  .000318   .199072  .000469  I -.3474324  .0000522  0.9151 0.0383  I     1.138     .735     -.884     .118   .043000   .197000  -.3472000      .000      .000  
+87 517 46932.00 I   .041909  .000513   .198704  .000638  I -.3483486  .0000466  0.9430 0.0424  I     2.025     .568    -1.007     .304   .042000   .197000  -.3482000      .000      .000  
+87 518 46933.00 I   .040463  .000551   .198405  .000837  I -.3493633  .0000669  1.1054 0.0406  I     2.882     .323     -.959     .413   .041000   .197000  -.3492000      .000      .000  
+87 519 46934.00 I   .039181  .000551   .198239  .000837  I -.3505857  .0000665  1.3466 0.0568  I     3.403     .323     -.849     .413   .039000   .197000  -.3505000      .000      .000  
+87 520 46935.00 I   .038100  .000622   .198265  .000994  I -.3520593  .0000917  1.5962 0.0447  I     3.536     .323     -.750     .413   .038000   .196000  -.3520000      .000      .000  
+87 521 46936.00 I   .037168  .000813   .198481  .001149  I -.3537602  .0000597  1.7922 0.0514  I     3.418     .323     -.675     .413   .037000   .196000  -.3538000      .000      .000  
+87 522 46937.00 I   .036280  .000865   .198815  .001210  I -.3556124  .0000465  1.8942 0.0415  I     3.161     .350     -.597     .345   .036000   .196000  -.3557000      .000      .000  
+87 523 46938.00 I   .035329  .000895   .199181  .001320  I -.3575135  .0000576  1.8907 0.0371  I     2.770     .401     -.481     .224   .035000   .196000  -.3576000      .000      .000  
+87 524 46939.00 I   .034223  .000897   .199476  .001206  I -.3593644  .0000578  1.7988 0.0413  I     2.225     .401     -.308     .224   .034000   .195000  -.3594000      .000      .000  
+87 525 46940.00 I   .032936  .000897   .199579  .001206  I -.3610880  .0000593  1.6377 0.0331  I     1.611     .401     -.140     .224   .034000   .195000  -.3611000      .000      .000  
+87 526 46941.00 I   .031586  .000867   .199492  .001159  I -.3626238  .0000324  1.4288 0.0341  I     1.208     .401     -.193     .224   .033000   .195000  -.3625000      .000      .000  
+87 527 46942.00 I   .030268  .000693   .199271  .001069  I -.3639418  .0000337  1.2087 0.0240  I     1.113     .690     -.549     .216   .032000   .195000  -.3637000      .000      .000  
+87 528 46943.00 I   .029042  .000405   .198973  .000674  I -.3650490  .0000353  1.0122 0.0265  I     1.161     .730    -1.032     .168   .031000   .195000  -.3647000      .000      .000  
+87 529 46944.00 I   .027961  .000237   .198658  .000747  I -.3659816  .0000410  0.8619 0.0270  I     1.179     .800    -1.340     .184   .030000   .195000  -.3655000      .000      .000  
+87 530 46945.00 I   .027069  .000233   .198384  .000842  I -.3667955  .0000408  0.7798 0.0279  I     1.103     .800    -1.262     .184   .030000   .195000  -.3663000      .000      .000  
+87 531 46946.00 I   .026356  .000210   .198192  .000781  I -.3675670  .0000378  0.7751 0.0306  I      .974     .800    -1.017     .184   .029000   .195000  -.3670000      .000      .000  
+87 6 1 46947.00 I   .025775  .000224   .198083  .000727  I -.3683667  .0000457  0.8332 0.0331  I      .852     .665     -.875     .201   .029000   .195000  -.3678000      .000      .000  
+87 6 2 46948.00 I   .025279  .000173   .198053  .000671  I -.3692478  .0000543  0.9344 0.0377  I      .828     .323     -.884     .199   .028000   .195000  -.3687000      .000      .000  
+87 6 3 46949.00 I   .024820  .000191   .198093  .000419  I -.3702416  .0000599  1.0539 0.0408  I     1.019     .388     -.894     .224   .027000   .195000  -.3698000      .000      .000  
+87 6 4 46950.00 I   .024353  .000268   .198199  .000477  I -.3713598  .0000608  1.1861 0.0431  I     1.395     .522     -.770     .191   .027000   .195000  -.3711000      .000      .000  
+87 6 5 46951.00 I   .023855  .000268   .198360  .000477  I -.3726176  .0000621  1.3299 0.0434  I     1.621     .522     -.588     .191   .026000   .195000  -.3726000      .000      .000  
+87 6 6 46952.00 I   .023324  .000268   .198545  .000477  I -.3740154  .0000620  1.4612 0.0427  I     1.389     .522     -.519     .191   .025000   .196000  -.3742000      .000      .000  
+87 6 7 46953.00 I   .022759  .000811   .198715  .000462  I -.3755227  .0000586  1.5407 0.0443  I      .772     .629     -.539     .151   .025000   .196000  -.3759000      .000      .000  
+87 6 8 46954.00 I   .022130  .000848   .198843  .000468  I -.3770620  .0000632  1.5158 0.0434  I      .124     .532     -.444     .128   .024000   .196000  -.3775000      .000      .000  
+87 6 9 46955.00 I   .021384  .000848   .198929  .000468  I -.3785134  .0000640  1.3683 0.0422  I     -.348     .532     -.171     .128   .023000   .197000  -.3790000      .000      .000  
+87 610 46956.00 I   .020463  .000920   .198976  .000403  I -.3797706  .0000559  1.1367 0.0369  I     -.710     .218      .050     .298   .022000   .197000  -.3802000      .000      .000  
+87 611 46957.00 I   .019334  .000928   .198993  .000378  I -.3807825  .0000368  0.8922 0.0349  I     -.928     .449     -.054     .149   .021000   .198000  -.3812000      .000      .000  
+87 612 46958.00 I   .018054  .000928   .199008  .000378  I -.3815793  .0000418  0.7205 0.0321  I     -.807     .449     -.419     .149   .020000   .198000  -.3819000      .000      .000  
+87 613 46959.00 I   .016708  .000928   .199060  .000378  I -.3822681  .0000525  0.6826 0.0334  I     -.282     .449     -.717     .149   .019000   .199000  -.3826000      .000      .000  
+87 614 46960.00 I   .015382  .000514   .199185  .000416  I -.3829938  .0000520  0.7926 0.0420  I      .350     .524     -.739     .176   .018000   .199000  -.3833000      .000      .000  
+87 615 46961.00 I   .014115  .000474   .199396  .000386  I -.3838894  .0000657  1.0108 0.0368  I      .694     .524     -.581     .176   .017000   .200000  -.3842000      .000      .000  
+87 616 46962.00 I   .012925  .000470   .199696  .000453  I -.3850249  .0000521  1.2576 0.0482  I      .593     .422     -.420     .125   .016000   .200000  -.3853000      .000      .000  
+87 617 46963.00 I   .011828  .000445   .200086  .000452  I -.3863871  .0000706  1.4519 0.0448  I      .179     .415     -.295     .298   .014000   .201000  -.3867000      .000      .000  
+87 618 46964.00 I   .010807  .000445   .200548  .000452  I -.3878939  .0000728  1.5417 0.0486  I     -.311     .415     -.177     .298   .013000   .201000  -.3883000      .000      .000  
+87 619 46965.00 I   .009827  .000445   .201057  .000452  I -.3894316  .0000669  1.5150 0.0459  I     -.683     .415     -.125     .298   .012000   .202000  -.3898000      .000      .000  
+87 620 46966.00 I   .008835  .000389   .201586  .000672  I -.3908886  .0000560  1.3826 0.0380  I     -.751     .415     -.236     .298   .011000   .202000  -.3912000      .000      .000  
+87 621 46967.00 I   .007817  .000405   .202110  .000738  I -.3921712  .0000359  1.1733 0.0295  I     -.609     .831     -.463     .275   .010000   .203000  -.3925000      .000      .000  
+87 622 46968.00 I   .006773  .000227   .202608  .000483  I -.3932229  .0000183  0.9271 0.0192  I     -.539    1.065     -.641     .359   .008000   .203000  -.3935000      .000      .000  
+87 623 46969.00 I   .005702  .000236   .203054  .000491  I -.3940250  .0000136  0.6799 0.0102  I     -.718    1.051     -.657     .361   .007000   .204000  -.3942000      .000      .000  
+87 624 46970.00 I   .004581  .000236   .203435  .000491  I -.3945922  .0000088  0.4613 0.0106  I    -1.184    1.051     -.532     .361   .006000   .205000  -.3948000      .000      .000  
+87 625 46971.00 I   .003328  .000253   .203807  .000561  I -.3949640  .0000162  0.2919 0.0108  I    -1.594    1.051     -.649     .361   .005000   .205000  -.3951000      .000      .000  
+87 626 46972.00 I   .001879  .000364   .204234  .000645  I -.3952013  .0000197  0.1984 0.0131  I    -1.945     .875     -.891     .300   .003000   .206000  -.3954000      .000      .000  
+87 627 46973.00 I   .000318  .000364   .204697  .000554  I -.3953915  .0000205  0.1971 0.0145  I    -2.344     .709     -.879     .216   .002000   .206000  -.3956000      .000      .000  
+87 628 46974.00 I  -.001239  .000373   .205162  .000580  I -.3956235  .0000214  0.2798 0.0293  I    -2.655     .516     -.559     .152   .001000   .207000  -.3959000      .000      .000  
+87 629 46975.00 I  -.002680  .000603   .205607  .000943  I -.3959716  .0000549  0.4240 0.0279  I    -2.707     .568     -.154     .171   .000000   .207000  -.3963000      .000      .000  
+87 630 46976.00 I  -.003968  .000629   .206069  .000923  I -.3964826  .0000515  0.6014 0.0330  I    -2.274     .568     -.082     .171  -.002000   .208000  -.3969000      .000      .000  
+87 7 1 46977.00 I  -.005103  .000608   .206556  .000784  I -.3971767  .0000366  0.7857 0.0325  I    -1.457     .553     -.241     .299  -.003000   .209000  -.3976000      .000      .000  
+87 7 2 46978.00 I  -.006110  .000382   .207053  .000896  I -.3980465  .0000396  0.9475 0.0328  I     -.698     .623     -.195     .350  -.004000   .209000  -.3985000      .000      .000  
+87 7 3 46979.00 I  -.007044  .000444   .207548  .000632  I -.3990551  .0000545  1.0596 0.0343  I     -.394     .518      .046     .363  -.005000   .210000  -.3996000      .000      .000  
+87 7 4 46980.00 I  -.007966  .000426   .208033  .000457  I -.4001420  .0000559  1.1007 0.0353  I     -.789     .591      .147     .413  -.007000   .210000  -.4006000      .000      .000  
+87 7 5 46981.00 I  -.008911  .000359   .208509  .000314  I -.4012269  .0000448  1.0528 0.0365  I    -1.793     .513      .018     .474  -.008000   .211000  -.4017000      .000      .000  
+87 7 6 46982.00 I  -.009909  .000300   .208988  .000275  I -.4022166  .0000470  0.9117 0.0337  I    -2.994     .635     -.114     .343  -.009000   .211000  -.4026000      .000      .000  
+87 7 7 46983.00 I  -.010986  .000223   .209478  .000207  I -.4030268  .0000504  0.7002 0.0388  I    -4.034     .686     -.045     .298  -.010000   .212000  -.4034000      .000      .000  
+87 7 8 46984.00 I  -.012169  .000320   .209989  .000400  I -.4036106  .0000617  0.4697 0.0392  I    -4.800     .686      .136     .298  -.012000   .213000  -.4039000      .000      .000  
+87 7 9 46985.00 I  -.013472  .000349   .210517  .000487  I -.4039816  .0000600  0.2854 0.0434  I    -5.220     .686      .197     .298  -.013000   .213000  -.4042000      .000      .000  
+87 710 46986.00 I  -.014873  .000429   .211044  .000464  I -.4042189  .0000610  0.2135 0.0432  I    -5.067     .686      .109     .298  -.014000   .214000  -.4044000      .000      .000  
+87 711 46987.00 I  -.016356  .000589   .211553  .000454  I -.4044596  .0000622  0.2947 0.0485  I    -4.402     .739      .074     .298  -.016000   .215000  -.4046000      .000      .000  
+87 712 46988.00 I  -.017869  .000681   .212064  .000521  I -.4048559  .0000753  0.5186 0.0552  I    -3.672     .740      .191     .298  -.017000   .216000  -.4050000      .000      .000  
+87 713 46989.00 I  -.019344  .000783   .212606  .000599  I -.4055222  .0000913  0.8188 0.0609  I    -3.319     .810      .291     .110  -.018000   .216000  -.4057000      .000      .000  
+87 714 46990.00 I  -.020705  .000756   .213196  .000479  I -.4064820  .0000958  1.0846 0.0658  I    -3.523     .810      .203     .110  -.019000   .217000  -.4066000      .000      .000  
+87 715 46991.00 I  -.021913  .000742   .213841  .000532  I -.4076496  .0000949  1.2249 0.0600  I    -4.130    1.046      .029     .224  -.020000   .218000  -.4078000      .000      .000  
+87 716 46992.00 I  -.022946  .000600   .214544  .000297  I -.4088811  .0000721  1.2130 0.0610  I    -4.927    1.293     -.021     .290  -.021000   .219000  -.4090000      .000      .000  
+87 717 46993.00 I  -.023784  .000453   .215306  .000302  I -.4100321  .0000768  1.0706 0.0520  I    -5.712    1.336      .047     .323  -.022000   .219000  -.4101000      .000      .000  
+87 718 46994.00 I  -.024434  .000453   .216111  .000302  I -.4109970  .0000750  0.8523 0.0538  I    -6.226    1.336      .012     .323  -.023000   .220000  -.4111000      .000      .000  
+87 719 46995.00 I  -.024929  .000413   .216938  .000327  I -.4117266  .0000753  0.6039 0.0473  I    -6.433    1.336     -.129     .323  -.023000   .221000  -.4118000      .000      .000  
+87 720 46996.00 I  -.025340  .000429   .217783  .000392  I -.4122057  .0000576  0.3584 0.0442  I    -6.574    1.214     -.172     .355  -.024000   .222000  -.4123000      .000      .000  
+87 721 46997.00 I  -.025680  .000366   .218647  .000366  I -.4124557  .0000463  0.1500 0.0354  I    -6.743     .954     -.130     .484  -.024000   .222000  -.4126000      .000      .000  
+87 722 46998.00 I  -.025937  .000302   .219537  .000334  I -.4125270  .0000412  0.0054 0.0329  I    -6.845     .261     -.246     .502  -.025000   .223000  -.4126000      .000      .000  
+87 723 46999.00 I  -.026102  .000270   .220452  .000394  I -.4124929  .0000468 -0.0602 0.0313  I    -6.713     .290     -.728     .556  -.025000   .224000  -.4126000      .000      .000  
+87 724 47000.00 I  -.026180  .000270   .221376  .000394  I -.4124338  .0000471 -0.0442 0.0303  I    -6.470     .290     -.981     .556  -.025000   .225000  -.4125000      .000      .000  
+87 725 47001.00 I  -.026210  .000268   .222271  .000388  I -.4124291  .0000386  0.0454 0.0302  I    -6.292     .290     -.888     .556  -.025000   .226000  -.4125000      .000      .000  
+87 726 47002.00 I  -.026216  .000266   .223132  .000432  I -.4125424  .0000378  0.1882 0.0266  I    -6.218     .457     -.512     .494  -.026000   .226000  -.4126000      .000      .000  
+87 727 47003.00 I  -.026225  .000136   .223964  .000322  I -.4128161  .0000365  0.3627 0.0335  I    -6.222     .552     -.040     .326  -.026000   .227000  -.4129000      .000      .000  
+87 728 47004.00 I  -.026262  .000176   .224772  .000483  I -.4132713  .0000553  0.5476 0.0393  I    -6.281     .668      .264     .342  -.026000   .228000  -.4133000      .000      .000  
+87 729 47005.00 I  -.026357  .000139   .225559  .000398  I -.4139051  .0000696  0.7139 0.0332  I    -6.363     .668      .338     .342  -.026000   .229000  -.4140000      .000      .000  
+87 730 47006.00 I  -.026538  .000353   .226339  .000367  I -.4146832  .0000367  0.8326 0.0396  I    -6.430     .668      .314     .342  -.026000   .230000  -.4148000      .000      .000  
+87 731 47007.00 I  -.026828  .000392   .227130  .000548  I -.4155480  .0000378  0.8845 0.0274  I    -6.528     .788      .273     .232  -.027000   .230000  -.4156000      .000      .000  
+87 8 1 47008.00 I  -.027249  .000413   .227951  .000608  I -.4164262  .0000407  0.8583 0.0280  I    -6.793     .859      .148     .298  -.027000   .231000  -.4165000      .000      .000  
+87 8 2 47009.00 I  -.027824  .000413   .228830  .000608  I -.4172396  .0000413  0.7571 0.0315  I    -7.282     .859     -.111     .298  -.027000   .232000  -.4174000      .000      .000  
+87 8 3 47010.00 I  -.028553  .000413   .229784  .000608  I -.4179193  .0000482  0.5928 0.0336  I    -7.851     .859     -.391     .298  -.028000   .233000  -.4181000      .000      .000  
+87 8 4 47011.00 I  -.029366  .000478   .230769  .000672  I -.4184132  .0000531  0.3927 0.0356  I    -8.282     .859     -.534     .298  -.028000   .234000  -.4186000      .000      .000  
+87 8 5 47012.00 I  -.030185  .000552   .231733  .000647  I -.4187114  .0000525  0.2128 0.0530  I    -8.423     .747     -.533     .166  -.028000   .234000  -.4189000      .000      .000  
+87 8 6 47013.00 I  -.030950  .000568   .232651  .000627  I -.4188688  .0000917  0.1229 0.0437  I    -8.236     .540     -.506     .232  -.029000   .235000  -.4191000      .000      .000  
+87 8 7 47014.00 I  -.031605  .000482   .233510  .000421  I -.4190079  .0000699  0.1852 0.0578  I    -7.793     .540     -.467     .232  -.030000   .236000  -.4193000      .000      .000  
+87 8 8 47015.00 I  -.032125  .000482   .234318  .000421  I -.4192911  .0000705  0.4030 0.0481  I    -7.319     .564     -.311     .203  -.030000   .237000  -.4195000      .000      .000  
+87 8 9 47016.00 I  -.032557  .000471   .235103  .000414  I -.4198449  .0000661  0.7136 0.0464  I    -7.053     .564     -.078     .203  -.030000   .238000  -.4200000      .000      .000  
+87 810 47017.00 I  -.032972  .000595   .235882  .000593  I -.4207195  .0000604  1.0279 0.0425  I    -6.993    1.691     -.052     .762  -.031000   .239000  -.4209000      .000      .000  
+87 811 47018.00 I  -.033433  .000435   .236668  .000552  I -.4218732  .0000535  1.2599 0.0371  I    -6.859    2.145     -.466     .976  -.032000   .239000  -.4221000      .000      .000  
+87 812 47019.00 I  -.033981  .000487   .237483  .000592  I -.4231936  .0000430  1.3549 0.0355  I    -6.603    2.145    -1.100     .976  -.032000   .240000  -.4234000      .000      .000  
+87 813 47020.00 I  -.034635  .000438   .238350  .000530  I -.4245314  .0000467  1.2953 0.0430  I    -6.641    2.145    -1.438     .976  -.032000   .241000  -.4248000      .000      .000  
+87 814 47021.00 I  -.035353  .000827   .239274  .000758  I -.4257428  .0000745  1.1117 0.0428  I    -7.136    2.572    -1.366    1.159  -.033000   .242000  -.4261000      .000      .000  
+87 815 47022.00 I  -.036075  .000856   .240246  .000732  I -.4267350  .0000718  0.8687 0.0520  I    -7.828    1.994    -1.180    1.018  -.034000   .243000  -.4271000      .000      .000  
+87 816 47023.00 I  -.036740  .000785   .241261  .000407  I -.4274817  .0000726  0.6301 0.0520  I    -8.349    1.158    -1.141     .855  -.034000   .244000  -.4279000      .000      .000  
+87 817 47024.00 I  -.037298  .000785   .242313  .000407  I -.4280123  .0000752  0.4429 0.0515  I    -8.597    1.158    -1.197     .855  -.034000   .245000  -.4284000      .000      .000  
+87 818 47025.00 I  -.037712  .000785   .243403  .000407  I -.4283918  .0000731  0.3286 0.0512  I    -8.723    1.158    -1.160     .855  -.035000   .246000  -.4288000      .000      .000  
+87 819 47026.00 I  -.038015  .000830   .244553  .000469  I -.4286951  .0000696  0.2909 0.0431  I    -8.859    1.158    -1.005     .855  -.035000   .247000  -.4291000      .000      .000  
+87 820 47027.00 I  -.038232  .000815   .245777  .000529  I -.4289996  .0000455  0.3310 0.0411  I    -8.969     .925     -.880     .691  -.035000   .248000  -.4293000      .000      .000  
+87 821 47028.00 I  -.038367  .000666   .247084  .000493  I -.4293826  .0000438  0.4476 0.0415  I    -9.025     .519     -.842     .414  -.036000   .249000  -.4297000      .000      .000  
+87 822 47029.00 I  -.038417  .000800   .248479  .000512  I -.4299181  .0000694  0.6340 0.0426  I    -9.120     .519     -.749     .414  -.036000   .250000  -.4301000      .000      .000  
+87 823 47030.00 I  -.038349  .000800   .249945  .000512  I -.4306659  .0000731  0.8664 0.0471  I    -9.337     .519     -.483     .414  -.036000   .251000  -.4308000      .000      .000  
+87 824 47031.00 I  -.038177  .000727   .251444  .000659  I -.4316518  .0000636  1.1016 0.0493  I    -9.611     .870     -.287     .409  -.036000   .252000  -.4317000      .000      .000  
+87 825 47032.00 I  -.037996  .000643   .252925  .000899  I -.4328582  .0000662  1.3043 0.0498  I    -9.766     .819     -.312     .388  -.036000   .253000  -.4327000      .000      .000  
+87 826 47033.00 I  -.037882  .000586   .254343  .000889  I -.4342427  .0000767  1.4540 0.0588  I    -9.610     .876     -.402     .301  -.036000   .254000  -.4340000      .000      .000  
+87 827 47034.00 I  -.037847  .000519   .255669  .000950  I -.4357424  .0000973  1.5322 0.0591  I    -9.027     .969     -.281     .316  -.036000   .256000  -.4354000      .000      .000  
+87 828 47035.00 I  -.037895  .000313   .256887  .001041  I -.4372807  .0000899  1.5311 0.0626  I    -8.509     .969     -.174     .316  -.037000   .257000  -.4369000      .000      .000  
+87 829 47036.00 I  -.038023  .000355   .258032  .000954  I -.4387741  .0000787  1.4384 0.0582  I    -8.480     .969     -.151     .316  -.037000   .258000  -.4383000      .000      .000  
+87 830 47037.00 I  -.038225  .000375   .259111  .001024  I -.4401286  .0000738  1.2589 0.0602  I    -8.932     .697     -.194     .241  -.037000   .259000  -.4395000      .000      .000  
+87 831 47038.00 I  -.038498  .000303   .260125  .000969  I -.4412756  .0000911  1.0306 0.0532  I    -9.637     .688     -.308     .122  -.037000   .260000  -.4406000      .000      .000  
+87 9 1 47039.00 I  -.038840  .000351   .261097  .000696  I -.4421910  .0000767  0.8061 0.0544  I   -10.255     .670     -.450     .143  -.037000   .261000  -.4415000      .000      .000  
+87 9 2 47040.00 I  -.039245  .000378   .262047  .000682  I -.4429115  .0000595  0.6527 0.0495  I   -10.417     .670     -.573     .143  -.037000   .262000  -.4423000      .000      .000  
+87 9 3 47041.00 I  -.039685  .000408   .262965  .000782  I -.4435399  .0000626  0.6296 0.0406  I   -10.029     .670     -.651     .143  -.038000   .263000  -.4430000      .000      .000  
+87 9 4 47042.00 I  -.040124  .000411   .263832  .000746  I -.4442230  .0000554  0.7633 0.0673  I    -9.290     .797     -.624     .358  -.038000   .264000  -.4437000      .000      .000  
+87 9 5 47043.00 I  -.040523  .000354   .264630  .000626  I -.4451130  .0001191  1.0366 0.0657  I    -8.587     .906     -.416     .485  -.038000   .265000  -.4447000      .000      .000  
+87 9 6 47044.00 I  -.040855  .000526   .265354  .000657  I -.4463229  .0001191  1.3900 0.0842  I    -8.290     .906     -.075     .485  -.039000   .266000  -.4460000      .000      .000  
+87 9 7 47045.00 I  -.041118  .000526   .266020  .000657  I -.4478847  .0001191  1.7187 0.0789  I    -8.450     .906      .183     .485  -.039000   .267000  -.4476000      .000      .000  
+87 9 8 47046.00 I  -.041363  .000552   .266679  .000644  I -.4497182  .0001035  1.9213 0.0728  I    -8.761     .906      .165     .485  -.040000   .268000  -.4495000      .000      .000  
+87 9 9 47047.00 I  -.041653  .000547   .267387  .000655  I -.4516703  .0000836  1.9531 0.0694  I    -8.990     .662     -.054     .475  -.040000   .269000  -.4514000      .000      .000  
+87 910 47048.00 I  -.042021  .000569   .268187  .000561  I -.4535705  .0000926  1.8233 0.0512  I    -9.275     .216     -.206     .390  -.040000   .270000  -.4534000      .000      .000  
+87 911 47049.00 I  -.042457  .000569   .269114  .000561  I -.4552813  .0000591  1.5866 0.0630  I    -9.842     .216     -.165     .390  -.041000   .271000  -.4551000      .000      .000  
+87 912 47050.00 I  -.042953  .000446   .270192  .000563  I -.4567302  .0000854  1.3093 0.0492  I   -10.563     .216     -.089     .390  -.041000   .272000  -.4565000      .000      .000  
+87 913 47051.00 I  -.043493  .000504   .271416  .000723  I -.4579047  .0000786  1.0462 0.0568  I   -11.005     .216     -.149     .390  -.042000   .273000  -.4577000      .000      .000  
+87 914 47052.00 I  -.044063  .000506   .272731  .001045  I -.4588415  .0000750  0.8397 0.0572  I   -10.949     .281     -.292     .309  -.042000   .274000  -.4587000      .000      .000  
+87 915 47053.00 I  -.044648  .000502   .274073  .001134  I -.4596119  .0000830  0.7165 0.0611  I   -10.581     .309     -.333     .106  -.043000   .275000  -.4595000      .000      .000  
+87 916 47054.00 I  -.045227  .000583   .275365  .001194  I -.4603080  .0000965  0.6937 0.0674  I   -10.176     .355     -.225     .126  -.043000   .276000  -.4603000      .000      .000  
+87 917 47055.00 I  -.045787  .000540   .276532  .001062  I -.4610335  .0001062  0.7733 0.0635  I    -9.859     .355     -.114     .126  -.044000   .277000  -.4611000      .000      .000  
+87 918 47056.00 I  -.046373  .000617   .277570  .001111  I -.4618809  .0000827  0.9316 0.0632  I    -9.614     .355     -.112     .126  -.045000   .278000  -.4620000      .000      .000  
+87 919 47057.00 I  -.046997  .000643   .278534  .001047  I -.4629139  .0000685  1.1414 0.0616  I    -9.438     .341     -.129     .130  -.045000   .279000  -.4631000      .000      .000  
+87 920 47058.00 I  -.047655  .000632   .279488  .001034  I -.4641732  .0000912  1.3796 0.0502  I    -9.309     .327     -.007     .134  -.046000   .280000  -.4644000      .000      .000  
+87 921 47059.00 I  -.048324  .000481   .280493  .000494  I -.4656749  .0000734  1.6231 0.0583  I    -9.117     .327      .263     .134  -.046000   .281000  -.4659000      .000      .000  
+87 922 47060.00 I  -.048973  .000481   .281599  .000494  I -.4674083  .0000725  1.8331 0.0448  I    -8.764     .348      .515     .113  -.047000   .282000  -.4676000      .000      .000  
+87 923 47061.00 I  -.049614  .000491   .282841  .000577  I -.4693152  .0000513  1.9653 0.0426  I    -8.383     .348      .580     .113  -.048000   .284000  -.4694000      .000      .000  
+87 924 47062.00 I  -.050272  .000492   .284240  .000634  I -.4713061  .0000449  1.9989 0.0392  I    -8.196     .365      .436     .136  -.048000   .285000  -.4714000      .000      .000  
+87 925 47063.00 I  -.050944  .000420   .285787  .000679  I -.4732788  .0000593  1.9298 0.0382  I    -8.331     .501      .203     .121  -.049000   .286000  -.4732000      .000      .000  
+87 926 47064.00 I  -.051614  .000420   .287435  .000679  I -.4751360  .0000618  1.7717 0.0488  I    -8.724     .501      .020     .121  -.050000   .288000  -.4750000      .000      .000  
+87 927 47065.00 I  -.052266  .000494   .289135  .000754  I -.4768015  .0000776  1.5516 0.0507  I    -9.242     .501     -.074     .121  -.050000   .289000  -.4765000      .000      .000  
+87 928 47066.00 I  -.052910  .000618   .290855  .000780  I -.4782346  .0000804  1.3176 0.0527  I    -9.577     .532     -.147     .137  -.051000   .291000  -.4779000      .000      .000  
+87 929 47067.00 I  -.053555  .000726   .292544  .000861  I -.4794534  .0000713  1.1338 0.0563  I    -9.584     .676     -.248     .385  -.052000   .292000  -.4791000      .000      .000  
+87 930 47068.00 I  -.054200  .000669   .294149  .000798  I -.4805379  .0000787  1.0572 0.0489  I    -9.251     .816     -.374     .484  -.052000   .294000  -.4802000      .000      .000  
+8710 1 47069.00 I  -.054800  .000720   .295654  .000744  I -.4816162  .0000668  1.1257 0.0513  I    -8.618     .846     -.444     .576  -.053000   .295000  -.4813000      .000      .000  
+8710 2 47070.00 I  -.055299  .000720   .297063  .000744  I -.4828365  .0000659  1.3358 0.0396  I    -7.876     .846     -.359     .576  -.054000   .296000  -.4826000      .000      .000  
+8710 3 47071.00 I  -.055709  .000743   .298407  .000497  I -.4843190  .0000427  1.6393 0.0426  I    -7.311     .846     -.089     .576  -.055000   .298000  -.4842000      .000      .000  
+8710 4 47072.00 I  -.056069  .000717   .299702  .000478  I -.4861217  .0000541  1.9624 0.0440  I    -7.129     .688      .293     .457  -.055000   .299000  -.4861000      .000      .000  
+8710 5 47073.00 I  -.056419  .000557   .300959  .000379  I -.4882227  .0000769  2.2222 0.0470  I    -7.287     .100      .627     .298  -.056000   .300000  -.4883000      .000      .000  
+8710 6 47074.00 I  -.056798  .000570   .302186  .000027  I -.4905233  .0000769  2.3536 0.0563  I    -7.541     .100      .756     .298  -.056000   .302000  -.4906000      .000      .000  
+8710 7 47075.00 I  -.057215  .000570   .303393  .000027  I -.4928758  .0000822  2.3228 0.0538  I    -7.731     .100      .651     .298  -.057000   .303000  -.4930000      .000      .000  
+8710 8 47076.00 I  -.057662  .000494   .304588  .000144  I -.4951190  .0000754  2.1424 0.0519  I    -7.946     .100      .444     .298  -.058000   .304000  -.4952000      .000      .000  
+8710 9 47077.00 I  -.058140  .000359   .305779  .000249  I -.4971304  .0000633  1.8714 0.0467  I    -8.366     .822      .274     .298  -.058000   .305000  -.4971000      .000      .000  
+871010 47078.00 I  -.058658  .000276   .306976  .000268  I -.4988550  .0000552  1.5800 0.0552  I    -8.949    1.011      .160     .375  -.059000   .307000  -.4988000      .000      .000  
+871011 47079.00 I  -.059234  .000276   .308184  .000268  I -.5003025  .0000904  1.3249 0.0632  I    -9.362    1.011      .082     .375  -.059000   .308000  -.5002000      .000      .000  
+871012 47080.00 I  -.059879  .000621   .309408  .000643  I -.5015304  .0001136  1.1467 0.0617  I    -9.281    1.011      .097     .375  -.060000   .309000  -.5014000      .000      .000  
+871013 47081.00 I  -.060578  .000534   .310627  .000544  I -.5026280  .0000839  1.0649 0.0642  I    -8.711    1.011      .264     .375  -.060000   .310000  -.5025000      .000      .000  
+871014 47082.00 I  -.061318  .000527   .311812  .000542  I -.5036915  .0000599  1.0770 0.0531  I    -7.906     .811      .515     .284  -.060000   .311000  -.5035000      .000      .000  
+871015 47083.00 I  -.062070  .000570   .312940  .000562  I -.5048089  .0000651  1.1697 0.0456  I    -7.139     .590      .670     .124  -.061000   .313000  -.5046000      .000      .000  
+871016 47084.00 I  -.062740  .000570   .314034  .000562  I -.5060519  .0000687  1.3253 0.0410  I    -6.603     .590      .608     .124  -.061000   .314000  -.5059000      .000      .000  
+871017 47085.00 I  -.063223  .000511   .315126  .000550  I -.5074719  .0000500  1.5180 0.0371  I    -6.414     .590      .389     .124  -.062000   .315000  -.5073000      .000      .000  
+871018 47086.00 I  -.063481  .000354   .316244  .000459  I -.5090887  .0000278  1.7132 0.0289  I    -6.384     .513      .270     .115  -.062000   .316000  -.5089000      .000      .000  
+871019 47087.00 I  -.063542  .000364   .317388  .000478  I -.5108898  .0000288  1.8830 0.0249  I    -6.344     .640      .390     .124  -.062000   .317000  -.5107000      .000      .000  
+871020 47088.00 I  -.063443  .000286   .318552  .000537  I -.5128381  .0000412  2.0031 0.0282  I    -6.341     .618      .658     .121  -.062000   .318000  -.5127000      .000      .000  
+871021 47089.00 I  -.063229  .000268   .319720  .000567  I -.5148723  .0000484  2.0520 0.0286  I    -6.444     .542      .948     .139  -.062000   .319000  -.5147000      .000      .000  
+871022 47090.00 I  -.062982  .000268   .320871  .000567  I -.5169156  .0000398  2.0215 0.0327  I    -6.725     .542     1.123     .139  -.063000   .320000  -.5168000      .000      .000  
+871023 47091.00 I  -.062783  .000281   .322005  .000537  I -.5188895  .0000439  1.9137 0.0311  I    -7.055     .542     1.224     .139  -.063000   .321000  -.5187000      .000      .000  
+871024 47092.00 I  -.062657  .000325   .323114  .000574  I -.5207186  .0000479  1.7328 0.0352  I    -7.282     .549     1.206     .127  -.063000   .322000  -.5205000      .000      .000  
+871025 47093.00 I  -.062589  .000351   .324213  .000599  I -.5223417  .0000551  1.5116 0.0352  I    -7.361     .318     1.096     .120  -.063000   .323000  -.5220000      .000      .000  
+871026 47094.00 I  -.062553  .000342   .325320  .000373  I -.5237487  .0000516  1.3111 0.0397  I    -7.273     .355     1.006     .109  -.063000   .325000  -.5234000      .000      .000  
+871027 47095.00 I  -.062531  .000434   .326452  .000453  I -.5249939  .0000571  1.2005 0.0365  I    -6.979     .420      .973     .298  -.063000   .326000  -.5246000      .000      .000  
+871028 47096.00 I  -.062507  .000436   .327621  .000406  I -.5261949  .0000515  1.2255 0.0349  I    -6.469     .420      .959     .298  -.063000   .327000  -.5257000      .000      .000  
+871029 47097.00 I  -.062459  .000626   .328833  .000365  I -.5274888  .0000400  1.3822 0.0307  I    -5.804     .713      .947     .115  -.063000   .328000  -.5271000      .000      .000  
+871030 47098.00 I  -.062370  .000697   .330089  .000266  I -.5289897  .0000335  1.6301 0.0266  I    -5.128     .776      .961     .132  -.063000   .329000  -.5286000      .000      .000  
+871031 47099.00 I  -.062261  .000697   .331385  .000266  I -.5307583  .0000351  1.9060 0.0248  I    -4.629     .776     1.007     .132  -.063000   .331000  -.5304000      .000      .000  
+8711 1 47100.00 I  -.062173  .000673   .332711  .000724  I -.5327890  .0000365  2.1439 0.0239  I    -4.440     .776     1.060     .132  -.062000   .332000  -.5325000      .000      .000  
+8711 2 47101.00 I  -.062171  .000618   .334055  .000734  I -.5350140  .0000324  2.2854 0.0200  I    -4.597     .685     1.185     .116  -.062000   .333000  -.5348000      .000      .000  
+8711 3 47102.00 I  -.062204  .000450   .335394  .000654  I -.5373157  .0000163  2.2946 0.0179  I    -4.908     .674     1.365     .106  -.062000   .335000  -.5372000      .000      .000  
+8711 4 47103.00 I  -.062184  .000453   .336699  .000690  I -.5395601  .0000152  2.1746 0.0159  I    -5.121     .533     1.497     .298  -.062000   .336000  -.5394000      .000      .000  
+8711 5 47104.00 I  -.062019  .000252   .337962  .000725  I -.5416347  .0000272  1.9640 0.0162  I    -5.123     .572     1.489     .298  -.061000   .337000  -.5415000      .000      .000  
+8711 6 47105.00 I  -.061613  .000252   .339246  .000725  I -.5434731  .0000286  1.7083 0.0193  I    -5.033     .572     1.279     .298  -.061000   .339000  -.5434000      .000      .000  
+8711 7 47106.00 I  -.060956  .000322   .340659  .000564  I -.5450545  .0000274  1.4620 0.0197  I    -5.125     .572     1.015     .298  -.060000   .340000  -.5450000      .000      .000  
+8711 8 47107.00 I  -.060130  .000338   .342210  .000560  I -.5464189  .0000270  1.2809 0.0236  I    -5.393     .755      .856     .233  -.060000   .342000  -.5463000      .000      .000  
+8711 9 47108.00 I  -.059246  .000323   .343883  .000539  I -.5476474  .0000385  1.1930 0.0536  I    -5.526     .787      .869     .262  -.060000   .343000  -.5476000      .000      .000  
+871110 47109.00 I  -.058418  .000471   .345665  .000435  I -.5488370  .0001038  1.2015 0.0489  I    -5.240     .832     1.060     .293  -.059000   .345000  -.5487000      .000      .000  
+871111 47110.00 I  -.057743  .000471   .347539  .000435  I -.5500785  .0000898  1.2942 0.0646  I    -4.455     .832     1.315     .293  -.059000   .346000  -.5500000      .000      .000  
+871112 47111.00 I  -.057237  .000400   .349460  .000473  I -.5514459  .0000768  1.4486 0.0566  I    -3.587     .832     1.430     .293  -.059000   .348000  -.5513000      .000      .000  
+871113 47112.00 I  -.056859  .000444   .351377  .000526  I -.5529858  .0000689  1.6335 0.0557  I    -2.974     .643     1.360     .239  -.058000   .350000  -.5528000      .000      .000  
+871114 47113.00 I  -.056557  .000410   .353241  .000442  I -.5547122  .0000806  1.8162 0.0490  I    -2.673     .389     1.272     .110  -.058000   .351000  -.5545000      .000      .000  
+871115 47114.00 I  -.056266  .000386   .355008  .000494  I -.5566074  .0000697  1.9666 0.0463  I    -2.695     .791     1.326     .125  -.058000   .353000  -.5564000      .000      .000  
+871116 47115.00 I  -.055923  .000422   .356643  .000569  I -.5586307  .0000457  2.0730 0.0407  I    -3.032     .791     1.518     .125  -.057000   .355000  -.5584000      .000      .000  
+871117 47116.00 I  -.055519  .000376   .358163  .000520  I -.5607359  .0000420  2.1271 0.0325  I    -3.601     .791     1.699     .125  -.057000   .356000  -.5605000      .000      .000  
+871118 47117.00 I  -.055096  .000448   .359613  .000501  I -.5628610  .0000461  2.1097 0.0384  I    -4.190     .166     1.766     .196  -.056000   .358000  -.5627000      .000      .000  
+871119 47118.00 I  -.054714  .000455   .361041  .000320  I -.5649276  .0000643  2.0093 0.0404  I    -4.579     .233     1.733     .248  -.056000   .360000  -.5648000      .000      .000  
+871120 47119.00 I  -.054428  .000445   .362491  .000355  I -.5668549  .0000664  1.8348 0.0426  I    -4.704     .233     1.656     .248  -.056000   .362000  -.5667000      .000      .000  
+871121 47120.00 I  -.054270  .000416   .363988  .000365  I -.5685829  .0000558  1.6173 0.0423  I    -4.653     .233     1.566     .248  -.056000   .363000  -.5685000      .000      .000  
+871122 47121.00 I  -.054283  .000374   .365542  .000334  I -.5700928  .0000523  1.4096 0.0402  I    -4.532     .242     1.498     .209  -.056000   .365000  -.5700000      .000      .000  
+871123 47122.00 I  -.054500  .000545   .367150  .000462  I -.5714266  .0000579  1.2753 0.0408  I    -4.375     .253     1.518     .261  -.056000   .366000  -.5713000      .000      .000  
+871124 47123.00 I  -.054941  .000534   .368800  .000491  I -.5726827  .0000626  1.2590 0.0407  I    -4.212     .266     1.647     .270  -.056000   .368000  -.5726000      .000      .000  
+871125 47124.00 I  -.055621  .000512   .370474  .000759  I -.5739870  .0000571  1.3698 0.0585  I    -4.126     .266     1.807     .270  -.057000   .369000  -.5740000      .000      .000  
+871126 47125.00 I  -.056525  .000512   .372135  .000759  I -.5754571  .0000989  1.5849 0.0487  I    -4.098     .266     1.910     .270  -.057000   .371000  -.5756000      .000      .000  
+871127 47126.00 I  -.057559  .000466   .373744  .000688  I -.5771728  .0000788  1.8480 0.0627  I    -4.076     .266     1.935     .270  -.057000   .373000  -.5774000      .000      .000  
+871128 47127.00 I  -.058610  .000710   .375255  .000722  I -.5791443  .0000772  2.0846 0.0574  I    -4.039     .389     1.941     .229  -.058000   .374000  -.5795000      .000      .000  
+871129 47128.00 I  -.059560  .000662   .376646  .000700  I -.5813132  .0000836  2.2349 0.0568  I    -3.996     .418     2.006     .298  -.058000   .375000  -.5817000      .000      .000  
+871130 47129.00 I  -.060283  .000662   .377940  .000700  I -.5835738  .0000834  2.2642 0.0589  I    -3.955     .418     2.164     .298  -.058000   .377000  -.5840000      .000      .000  
+8712 1 47130.00 I  -.060669  .000663   .379173  .000354  I -.5857988  .0000831  2.1652 0.0442  I    -3.876     .418     2.389     .298  -.058000   .378000  -.5863000      .000      .000  
+8712 2 47131.00 I  -.060705  .000598   .380381  .000326  I -.5878707  .0000291  1.9656 0.0515  I    -3.763     .418     2.548     .298  -.058000   .379000  -.5884000      .000      .000  
+8712 3 47132.00 I  -.060455  .000599   .381576  .000330  I -.5897125  .0000607  1.7133 0.0478  I    -3.563     .454     2.569     .141  -.058000   .380000  -.5903000      .000      .000  
+8712 4 47133.00 I  -.059990  .000409   .382762  .000332  I -.5912961  .0000910  1.4573 0.0582  I    -3.235     .435     2.470     .180  -.057000   .382000  -.5918000      .000      .000  
+8712 5 47134.00 I  -.059370  .000378   .383937  .000367  I -.5926399  .0000993  1.2396 0.0637  I    -2.848     .503     2.352     .207  -.057000   .383000  -.5931000      .000      .000  
+8712 6 47135.00 I  -.058644  .000376   .385103  .000434  I -.5937993  .0000893  1.0941 0.0609  I    -2.527     .627     2.391     .428  -.056000   .385000  -.5942000      .000      .000  
+8712 7 47136.00 I  -.057828  .000563   .386266  .000417  I -.5948576  .0000706  1.0373 0.0553  I    -2.336     .684     2.569     .491  -.056000   .386000  -.5952000      .000      .000  
+8712 8 47137.00 I  -.056931  .000515   .387435  .000439  I -.5959009  .0000653  1.0614 0.0443  I    -2.222     .783     2.660     .414  -.055000   .387000  -.5962000      .000      .000  
+8712 9 47138.00 I  -.055941  .000572   .388625  .000474  I -.5970021  .0000536  1.1506 0.0396  I    -2.123     .893     2.326     .492  -.054000   .389000  -.5972000      .000      .000  
+871210 47139.00 I  -.054829  .000572   .389872  .000474  I -.5982189  .0000447  1.2901 0.0348  I    -1.873     .893     1.954     .492  -.053000   .390000  -.5985000      .000      .000  
+871211 47140.00 I  -.053586  .000584   .391201  .000584  I -.5995910  .0000444  1.4557 0.0290  I    -1.522     .893     1.794     .492  -.052000   .391000  -.5998000      .000      .000  
+871212 47141.00 I  -.052243  .000571   .392604  .000492  I -.6011298  .0000369  1.6199 0.0253  I    -1.224     .913     1.859     .400  -.051000   .393000  -.6013000      .000      .000  
+871213 47142.00 I  -.050835  .000559   .394060  .000426  I -.6028227  .0000242  1.7599 0.0237  I    -1.099     .947     2.151     .384  -.050000   .394000  -.6030000      .000      .000  
+871214 47143.00 I  -.049410  .000501   .395542  .000574  I -.6046343  .0000297  1.8540 0.0215  I    -1.287     .803     2.483     .421  -.049000   .395000  -.6048000      .000      .000  
+871215 47144.00 I  -.048052  .000501   .396989  .000574  I -.6065087  .0000355  1.8823 0.0223  I    -1.826     .803     2.586     .421  -.048000   .396000  -.6067000      .000      .000  
+871216 47145.00 I  -.046844  .000422   .398339  .000476  I -.6083731  .0000332  1.8334 0.0248  I    -2.532     .803     2.373     .421  -.047000   .397000  -.6086000      .000      .000  
+871217 47146.00 I  -.045784  .000380   .399552  .000423  I -.6101502  .0000346  1.7085 0.0238  I    -3.189     .699     2.099     .402  -.045000   .399000  -.6104000      .000      .000  
+871218 47147.00 I  -.044816  .000620   .400625  .000408  I -.6117713  .0000340  1.5270 0.0362  I    -3.696     .848     2.006     .403  -.044000   .400000  -.6120000      .000      .000  
+871219 47148.00 I  -.043878  .000621   .401565  .000398  I -.6132009  .0000637  1.3355 0.0327  I    -4.066     .756     2.118     .306  -.043000   .401000  -.6135000      .000      .000  
+871220 47149.00 I  -.042903  .000599   .402385  .000261  I -.6144610  .0000559  1.2001 0.0366  I    -4.232     .742     2.356     .307  -.042000   .402000  -.6147000      .000      .000  
+871221 47150.00 I  -.041828  .000567   .403123  .000252  I -.6156405  .0000362  1.1827 0.0329  I    -3.987     .648     2.350     .262  -.040000   .403000  -.6159000      .000      .000  
+871222 47151.00 I  -.040607  .000584   .403827  .000252  I -.6168739  .0000348  1.3076 0.0235  I    -3.544     .648     2.226     .262  -.039000   .404000  -.6170000      .000      .000  
+871223 47152.00 I  -.039281  .000626   .404537  .000213  I -.6182946  .0000300  1.5493 0.0275  I    -3.221     .698     2.218     .250  -.038000   .405000  -.6184000      .000      .000  
+871224 47153.00 I  -.037899  .000626   .405269  .000213  I -.6199903  .0000425  1.8444 0.0248  I    -3.113     .371     2.251     .298  -.036000   .406000  -.6201000      .000      .000  
+871225 47154.00 I  -.036458  .000367   .406039  .000222  I -.6219738  .0000396  2.1105 0.0393  I    -3.119     .268     2.212     .298  -.035000   .407000  -.6221000      .000      .000  
+871226 47155.00 I  -.034936  .000407   .406865  .000201  I -.6241775  .0000661  2.2750 0.0541  I    -3.094     .316     2.148     .106  -.033000   .408000  -.6243000      .000      .000  
+871227 47156.00 I  -.033335  .000267   .407754  .000421  I -.6264776  .0001007  2.3010 0.0518  I    -3.027     .791     2.192     .298  -.032000   .409000  -.6266000      .000      .000  
+871228 47157.00 I  -.031657  .000102   .408711  .000430  I -.6287351  .0000797  2.1940 0.0644  I    -3.027     .994     2.368     .169  -.030000   .410000  -.6288000      .000      .000  
+871229 47158.00 I  -.029907  .000102   .409744  .000430  I -.6308325  .0000802  1.9879 0.0540  I    -3.147     .994     2.575     .169  -.028000   .411000  -.6309000      .000      .000  
+871230 47159.00 I  -.028088  .000102   .410856  .000430  I -.6326933  .0000730  1.7289 0.0605  I    -3.321     .994     2.709     .169  -.027000   .412000  -.6327000      .000      .000  
+871231 47160.00 I  -.026206  .000102   .412048  .000430  I -.6342906  .0000907  1.4706 0.0456  I    -3.441     .994     2.728     .169  -.025000   .413000  -.6343000      .000      .000  
+88 1 1 47161.00 I  -.024282  .000105   .413287  .000466  I  .3643500  .0000548  1.2587 0.0530  I    -3.420     .994     2.636     .169  -.023000   .414000   .3643000      .000      .000  
+88 1 2 47162.00 I  -.022333  .000380   .414528  .000515  I  .3631681  .0000549  1.1186 0.0443  I    -3.284     .866     2.451     .221  -.021000   .415000   .3631000      .000      .000  
+88 1 3 47163.00 I  -.020379  .000487   .415724  .000509  I  .3620846  .0000696  1.0630 0.0437  I    -3.151     .590     2.267     .292  -.019000   .416000   .3621000      .000      .000  
+88 1 4 47164.00 I  -.018437  .000487   .416831  .000509  I  .3610156  .0000680  1.0870 0.0473  I    -3.083     .590     2.276     .292  -.017000   .417000   .3610000      .000      .000  
+88 1 5 47165.00 I  -.016529  .000345   .417809  .000284  I  .3598894  .0000641  1.1746 0.0408  I    -2.964     .590     2.600     .292  -.015000   .418000   .3598000      .000      .000  
+88 1 6 47166.00 I  -.014674  .000379   .418643  .000650  I  .3586506  .0000450  1.3098 0.0386  I    -2.683     .582     2.845     .311  -.013000   .419000   .3586000      .000      .000  
+88 1 7 47167.00 I  -.012861  .000371   .419338  .000643  I  .3572612  .0000430  1.4710 0.0514  I    -2.267     .646     2.817     .285  -.011000   .420000   .3571000      .000      .000  
+88 1 8 47168.00 I  -.011080  .000428   .419907  .000749  I  .3557089  .0000925  1.6313 0.0513  I    -1.882     .606     2.728     .297  -.010000   .421000   .3555000      .000      .000  
+88 1 9 47169.00 I  -.009315  .000360   .420360  .000778  I  .3540054  .0000931  1.7713 0.0658  I    -1.681     .631     2.872     .246  -.008000   .422000   .3537000      .000      .000  
+88 110 47170.00 I  -.007555  .000401   .420728  .000743  I  .3521806  .0000936  1.8684 0.0616  I    -1.559     .631     3.005     .246  -.006000   .423000   .3519000      .000      .000  
+88 111 47171.00 I  -.005836  .000538   .421099  .000731  I  .3502919  .0000808  1.8956 0.0607  I    -1.647     .631     2.958     .247  -.004000   .423000   .3500000      .000      .000  
+88 112 47172.00 I  -.004197  .000663   .421557  .000736  I  .3484143  .0000772  1.8480 0.0591  I    -2.132     .761     2.731     .215  -.003000   .424000   .3481000      .000      .000  
+88 113 47173.00 I  -.002659  .000777   .422118  .000736  I  .3466188  .0000862  1.7318 0.0473  I    -2.832     .764     2.446     .206  -.001000   .425000   .3464000      .000      .000  
+88 114 47174.00 I  -.001282  .000729   .422756  .000635  I  .3449689  .0000547  1.5609 0.0533  I    -3.436     .691     2.380     .187   .000000   .425000   .3447000      .000      .000  
+88 115 47175.00 I  -.000119  .000650   .423392  .000395  I  .3435070  .0000628  1.3601 0.0392  I    -3.626     .679     2.377     .236   .002000   .426000   .3434000      .000      .000  
+88 116 47176.00 I   .000915  .000608   .423966  .000399  I  .3422443  .0000562  1.1726 0.0404  I    -3.527     .759     2.294     .224   .003000   .426000   .3422000      .000      .000  
+88 117 47177.00 I   .001971  .000562   .424454  .000396  I  .3411366  .0000509  1.0606 0.0392  I    -3.273     .746     2.121     .262   .005000   .427000   .3412000      .000      .000  
+88 118 47178.00 I   .003149  .000430   .424857  .000370  I  .3400813  .0000548  1.0739 0.0372  I    -2.855     .608     2.083     .265   .006000   .427000   .3403000      .000      .000  
+88 119 47179.00 I   .004431  .000410   .425192  .000354  I  .3389456  .0000542  1.2168 0.0390  I    -2.454     .608     2.135     .265   .008000   .428000   .3392000      .000      .000  
+88 120 47180.00 I   .005795  .000311   .425472  .000311  I  .3376186  .0000556  1.4469 0.0323  I    -2.190     .658     2.134     .289   .009000   .428000   .3379000      .000      .000  
+88 121 47181.00 I   .007263  .000220   .425747  .000327  I  .3360472  .0000352  1.6909 0.0314  I    -2.200     .677     2.117     .254   .011000   .429000   .3363000      .000      .000  
+88 122 47182.00 I   .008856  .000229   .426060  .000356  I  .3342621  .0000293  1.8592 0.0247  I    -2.504     .567     2.021     .232   .012000   .429000   .3344000      .000      .000  
+88 123 47183.00 I   .010591  .000216   .426454  .000303  I  .3323757  .0000347  1.8869 0.0232  I    -2.961     .516     1.918     .140   .014000   .430000   .3324000      .000      .000  
+88 124 47184.00 I   .012479  .000323   .426949  .000298  I  .3305363  .0000359  1.7705 0.0241  I    -3.497     .562     2.019     .123   .016000   .430000   .3305000      .000      .000  
+88 125 47185.00 I   .014531  .000369   .427485  .000296  I  .3288699  .0000335  1.5484 0.0243  I    -3.997     .562     2.221     .123   .018000   .431000   .3287000      .000      .000  
+88 126 47186.00 I   .016729  .000360   .428019  .000302  I  .3274564  .0000328  1.2755 0.0217  I    -4.368     .491     2.345     .149   .020000   .431000   .3272000      .000      .000  
+88 127 47187.00 I   .019043  .000384   .428523  .000384  I  .3263147  .0000276  1.0153 0.0269  I    -4.502     .417     2.359     .158   .022000   .431000   .3260000      .000      .000  
+88 128 47188.00 I   .021473  .000470   .428978  .000451  I  .3254055  .0000427  0.8162 0.0228  I    -4.355     .177     2.416     .177   .024000   .432000   .3250000      .000      .000  
+88 129 47189.00 I   .024050  .000470   .429387  .000451  I  .3246553  .0000363  0.6982 0.0258  I    -4.003     .177     2.642     .177   .027000   .432000   .3242000      .000      .000  
+88 130 47190.00 I   .026750  .000357   .429743  .000412  I  .3239802  .0000291  0.6669 0.0208  I    -3.540     .177     2.728     .177   .029000   .433000   .3235000      .000      .000  
+88 131 47191.00 I   .029541  .000348   .430052  .000355  I  .3232923  .0000205  0.7231 0.0168  I    -3.070     .177     2.534     .177   .031000   .433000   .3227000      .000      .000  
+88 2 1 47192.00 I   .032380  .000336   .430341  .000451  I  .3225081  .0000167  0.8568 0.0133  I    -2.683     .243     2.208     .184   .033000   .433000   .3220000      .000      .000  
+88 2 2 47193.00 I   .035225  .000359   .430638  .000408  I  .3215594  .0000168  1.0483 0.0111  I    -2.379     .263     1.975     .181   .036000   .434000   .3210000      .000      .000  
+88 2 3 47194.00 I   .038029  .000359   .430972  .000408  I  .3204005  .0000145  1.2726 0.0112  I    -2.042     .263     1.928     .181   .038000   .434000   .3199000      .000      .000  
+88 2 4 47195.00 I   .040717  .000359   .431369  .000408  I  .3190133  .0000149  1.4995 0.0108  I    -1.592     .263     1.964     .181   .041000   .435000   .3185000      .000      .000  
+88 2 5 47196.00 I   .043197  .000366   .431846  .000498  I  .3174101  .0000161  1.7007 0.0111  I    -1.177     .263     2.000     .181   .044000   .435000   .3169000      .000      .000  
+88 2 6 47197.00 I   .045436  .000358   .432383  .000547  I  .3156276  .0000164  1.8549 0.0126  I     -.958     .264     2.033     .151   .046000   .435000   .3152000      .000      .000  
+88 2 7 47198.00 I   .047417  .000409   .432934  .000612  I  .3137210  .0000193  1.9471 0.0138  I     -.985     .256     2.086     .298   .049000   .435000   .3133000      .000      .000  
+88 2 8 47199.00 I   .049127  .000382   .433448  .000685  I  .3117564  .0000222  1.9705 0.0148  I    -1.242     .265     2.077     .298   .051000   .435000   .3114000      .000      .000  
+88 2 9 47200.00 I   .050562  .000518   .433883  .000614  I  .3098046  .0000225  1.9201 0.0160  I    -1.663     .265     1.831     .298   .053000   .435000   .3095000      .000      .000  
+88 210 47201.00 I   .051772  .000506   .434212  .000668  I  .3079399  .0000231  1.7985 0.0167  I    -2.093     .222     1.400     .126   .054000   .435000   .3077000      .000      .000  
+88 211 47202.00 I   .052828  .000452   .434415  .000635  I  .3062239  .0000248  1.6281 0.0178  I    -2.409     .210     1.094     .127   .056000   .435000   .3061000      .000      .000  
+88 212 47203.00 I   .053789  .000480   .434472  .000610  I  .3046859  .0000271  1.4510 0.0227  I    -2.685     .163     1.172     .146   .057000   .435000   .3046000      .000      .000  
+88 213 47204.00 I   .054660  .000480   .434371  .000610  I  .3033032  .0000380  1.3298 0.0238  I    -3.087     .163     1.546     .146   .058000   .434000   .3033000      .000      .000  
+88 214 47205.00 I   .055477  .000531   .434080  .000638  I  .3019846  .0000392  1.3335 0.0256  I    -3.499     .163     1.716     .146   .059000   .434000   .3019000      .000      .000  
+88 215 47206.00 I   .056309  .000411   .433626  .000640  I  .3005816  .0000343  1.5010 0.0249  I    -3.561     .163     1.551     .146   .060000   .434000   .3005000      .000      .000  
+88 216 47207.00 I   .057220  .000458   .433069  .000575  I  .2989343  .0000307  1.8134 0.0252  I    -3.088     .723     1.375     .212   .061000   .434000   .2988000      .000      .000  
+88 217 47208.00 I   .058272  .000554   .432465  .000478  I  .2969319  .0000368  2.1945 0.0289  I    -2.366    1.004     1.432     .271   .062000   .433000   .2967000      .000      .000  
+88 218 47209.00 I   .059504  .000501   .431860  .000506  I  .2945576  .0000489  2.5386 0.0278  I    -1.903     .833     1.610     .228   .063000   .433000   .2943000      .000      .000  
+88 219 47210.00 I   .060894  .000501   .431273  .000506  I  .2919006  .0000418  2.7449 0.0308  I    -1.942     .833     1.665     .228   .064000   .433000   .2916000      .000      .000  
+88 220 47211.00 I   .062344  .000497   .430743  .000468  I  .2891320  .0000373  2.7585 0.0266  I    -2.322     .833     1.560     .228   .065000   .433000   .2889000      .000      .000  
+88 221 47212.00 I   .063722  .000415   .430316  .000555  I  .2864434  .0000328  2.5926 0.0271  I    -2.798     .775     1.458     .186   .066000   .432000   .2862000      .000      .000  
+88 222 47213.00 I   .064928  .000405   .430027  .000620  I  .2839871  .0000394  2.3059 0.0261  I    -3.213     .576     1.399     .298   .067000   .432000   .2838000      .000      .000  
+88 223 47214.00 I   .065945  .000465   .429861  .000593  I  .2818466  .0000405  1.9741 0.0231  I    -3.384     .576     1.199     .298   .068000   .432000   .2817000      .000      .000  
+88 224 47215.00 I   .066799  .000489   .429796  .000634  I  .2800276  .0000243  1.6762 0.0393  I    -3.133     .653      .738     .298   .069000   .432000   .2798000      .000      .000  
+88 225 47216.00 I   .067596  .000514   .429844  .000575  I  .2784642  .0000674  1.4683 0.0358  I    -2.691     .653      .267     .298   .071000   .432000   .2782000      .000      .000  
+88 226 47217.00 I   .068476  .000511   .429993  .000573  I  .2770556  .0000673  1.3668 0.0608  I    -2.334     .622      .043     .298   .072000   .432000   .2768000      .000      .000  
+88 227 47218.00 I   .069557  .000579   .430227  .000677  I  .2756960  .0001011  1.3693 0.0668  I    -2.133     .580      .049     .298   .073000   .431000   .2754000      .000      .000  
+88 228 47219.00 I   .070903  .000651   .430544  .000372  I  .2742860  .0001154  1.4650 0.0761  I    -2.008     .572      .107     .298   .075000   .431000   .2739000      .000      .000  
+88 229 47220.00 I   .072553  .000496   .430941  .000434  I  .2727442  .0001137  1.6262 0.0711  I    -1.886     .572      .152     .298   .076000   .432000   .2724000      .000      .000  
+88 3 1 47221.00 I   .074468  .000541   .431406  .000383  I  .2710225  .0000830  1.8207 0.0573  I    -1.806     .572      .305     .298   .078000   .432000   .2707000      .000      .000  
+88 3 2 47222.00 I   .076565  .000505   .431907  .000399  I  .2691009  .0000139  2.0207 0.0469  I    -1.780     .429      .644     .129   .080000   .432000   .2688000      .000      .000  
+88 3 3 47223.00 I   .078739  .000435   .432401  .000413  I  .2669896  .0000439  2.1950 0.0227  I    -1.746     .202     1.027     .181   .082000   .432000   .2667000      .000      .000  
+88 3 4 47224.00 I   .080892  .000427   .432834  .000389  I  .2647288  .0000432  2.3156 0.0306  I    -1.693     .202     1.236     .181   .084000   .432000   .2646000      .000      .000  
+88 3 5 47225.00 I   .082956  .000427   .433137  .000389  I  .2623834  .0000425  2.3613 0.0276  I    -1.690     .202     1.215     .181   .086000   .432000   .2624000      .000      .000  
+88 3 6 47226.00 I   .084978  .000381   .433274  .000360  I  .2600346  .0000343  2.3216 0.0291  I    -1.765     .202     1.079     .181   .088000   .432000   .2602000      .000      .000  
+88 3 7 47227.00 I   .087034  .000445   .433225  .000380  I  .2577686  .0000397  2.1965 0.0311  I    -1.843     .273      .932     .136   .090000   .432000   .2581000      .000      .000  
+88 3 8 47228.00 I   .089182  .000396   .432984  .000393  I  .2556665  .0000519  1.9967 0.0305  I    -1.821     .332      .760     .298   .092000   .432000   .2562000      .000      .000  
+88 3 9 47229.00 I   .091479  .000403   .432548  .000530  I  .2537920  .0000463  1.7463 0.0348  I    -1.631     .332      .521     .298   .095000   .432000   .2544000      .000      .000  
+88 310 47230.00 I   .093968  .000403   .431925  .000530  I  .2521810  .0000465  1.4744 0.0339  I    -1.299     .332      .274     .298   .097000   .432000   .2530000      .000      .000  
+88 311 47231.00 I   .096616  .000392   .431178  .000515  I  .2508327  .0000494  1.2339 0.0324  I    -1.074     .332      .205     .298   .100000   .431000   .2516000      .000      .000  
+88 312 47232.00 I   .099373  .000355   .430378  .000777  I  .2496800  .0000451  1.0930 0.0399  I    -1.146     .493      .257     .126   .102000   .431000   .2505000      .000      .000  
+88 313 47233.00 I   .102178  .000341   .429573  .000879  I  .2485988  .0000626  1.0957 0.0363  I    -1.442     .567      .181     .189   .104000   .430000   .2493000      .000      .000  
+88 314 47234.00 I   .104954  .000323   .428784  .000936  I  .2474383  .0000570  1.2492 0.0350  I    -1.670     .623     -.115     .206   .107000   .429000   .2480000      .000      .000  
+88 315 47235.00 I   .107628  .000353   .428032  .000676  I  .2460608  .0000315  1.5214 0.0340  I    -1.614     .623     -.447     .206   .109000   .428000   .2465000      .000      .000  
+88 316 47236.00 I   .110146  .000327   .427325  .000716  I  .2443810  .0000371  1.8370 0.0206  I    -1.494     .624     -.338     .183   .112000   .428000   .2446000      .000      .000  
+88 317 47237.00 I   .112463  .000294   .426598  .000938  I  .2424056  .0000266  2.0961 0.0245  I    -1.542     .532      .149     .256   .114000   .427000   .2424000      .000      .000  
+88 318 47238.00 I   .114535  .000312   .425771  .000979  I  .2402340  .0000321  2.2196 0.0259  I    -1.726     .404      .531     .292   .116000   .425000   .2401000      .000      .000  
+88 319 47239.00 I   .116348  .000272   .424764  .000925  I  .2380222  .0000444  2.1755 0.0280  I    -1.862     .366      .477     .296   .118000   .424000   .2378000      .000      .000  
+88 320 47240.00 I   .117957  .000272   .423510  .000925  I  .2359328  .0000459  1.9818 0.0383  I    -1.998     .366      .030     .296   .120000   .423000   .2356000      .000      .000  
+88 321 47241.00 I   .119427  .000182   .422058  .000900  I  .2340876  .0000625  1.7009 0.0393  I    -2.134     .366     -.358     .296   .122000   .422000   .2338000      .000      .000  
+88 322 47242.00 I   .120827  .000354   .420480  .000894  I  .2325317  .0000639  1.4177 0.0525  I    -2.077     .373     -.610     .260   .123000   .420000   .2322000      .000      .000  
+88 323 47243.00 I   .122229  .000460   .418839  .000703  I  .2312297  .0000843  1.2020 0.0545  I    -1.661     .427     -.857     .168   .125000   .419000   .2309000      .000      .000  
+88 324 47244.00 I   .123705  .000468   .417199  .000628  I  .2300952  .0000884  1.0838 0.0585  I    -1.017     .519    -1.026     .298   .126000   .418000   .2298000      .000      .000  
+88 325 47245.00 I   .125305  .000468   .415613  .000628  I  .2290276  .0000810  1.0692 0.0554  I     -.501     .519     -.986     .298   .128000   .417000   .2287000      .000      .000  
+88 326 47246.00 I   .126960  .000422   .414115  .000561  I  .2279249  .0000669  1.1502 0.0458  I     -.273     .519     -.762     .298   .129000   .415000   .2276000      .000      .000  
+88 327 47247.00 I   .128550  .000439   .412734  .000472  I  .2267026  .0000426  1.3049 0.0395  I     -.204     .403     -.548     .113   .130000   .414000   .2264000      .000      .000  
+88 328 47248.00 I   .129982  .000359   .411461  .000594  I  .2252995  .0000422  1.5065 0.0300  I     -.151     .237     -.456     .144   .131000   .413000   .2250000      .000      .000  
+88 329 47249.00 I   .131179  .000359   .410263  .000594  I  .2236844  .0000422  1.7238 0.0249  I     -.140     .237     -.392     .144   .132000   .412000   .2233000      .000      .000  
+88 330 47250.00 I   .132077  .000302   .409106  .000235  I  .2218552  .0000264  1.9314 0.0282  I     -.229     .237     -.239     .144   .133000   .410000   .2215000      .000      .000  
+88 331 47251.00 I   .132689  .000256   .407969  .000376  I  .2198328  .0000374  2.1055 0.0224  I     -.383     .256     -.046     .198   .134000   .409000   .2195000      .000      .000  
+88 4 1 47252.00 I   .133060  .000256   .406819  .000376  I  .2176635  .0000363  2.2217 0.0335  I     -.525     .256      .020     .198   .134000   .407000   .2173000      .000      .000  
+88 4 2 47253.00 I   .133242  .000153   .405621  .000461  I  .2154137  .0000557  2.2649 0.0337  I     -.657     .274     -.109     .240   .135000   .406000   .2151000      .000      .000  
+88 4 3 47254.00 I   .133291  .000329   .404331  .000421  I  .2131608  .0000569  2.2268 0.0397  I     -.822     .274     -.300     .240   .136000   .405000   .2128000      .000      .000  
+88 4 4 47255.00 I   .133276  .000329   .402927  .000421  I  .2109864  .0000567  2.1099 0.0375  I     -.987     .274     -.393     .240   .136000   .403000   .2107000      .000      .000  
+88 4 5 47256.00 I   .133296  .000464   .401435  .000411  I  .2089597  .0000488  1.9370 0.0309  I    -1.071     .274     -.373     .240   .137000   .402000   .2087000      .000      .000  
+88 4 6 47257.00 I   .133451  .000520   .399885  .000383  I  .2071178  .0000246  1.7478 0.0321  I    -1.048     .932     -.340     .153   .137000   .400000   .2069000      .000      .000  
+88 4 7 47258.00 I   .133796  .000630   .398302  .000215  I  .2054537  .0000416  1.5897 0.0219  I     -.947     .932     -.433     .153   .138000   .399000   .2052000      .000      .000  
+88 4 8 47259.00 I   .134366  .000630   .396706  .000215  I  .2039126  .0000363  1.5090 0.0281  I     -.807     .773     -.769     .207   .138000   .397000   .2037000      .000      .000  
+88 4 9 47260.00 I   .135164  .000719   .395107  .000284  I  .2023987  .0000379  1.5394 0.0240  I     -.636     .773    -1.421     .207   .139000   .396000   .2021000      .000      .000  
+88 410 47261.00 I   .136105  .000647   .393495  .000262  I  .2007934  .0000314  1.6909 0.0224  I     -.403     .773    -2.123     .207   .140000   .394000   .2005000      .000      .000  
+88 411 47262.00 I   .137096  .000495   .391851  .000211  I  .1989811  .0000237  1.9497 0.0216  I     -.055     .692    -2.436     .220   .141000   .392000   .1987000      .000      .000  
+88 412 47263.00 I   .138122  .000527   .390169  .000221  I  .1968751  .0000298  2.2652 0.0180  I      .346     .525    -2.302     .249   .142000   .391000   .1965000      .000      .000  
+88 413 47264.00 I   .139252  .000527   .388462  .000221  I  .1944597  .0000271  2.5536 0.0186  I      .616     .525    -1.924     .249   .143000   .389000   .1941000      .000      .000  
+88 414 47265.00 I   .140573  .000492   .386753  .000236  I  .1918060  .0000222  2.7278 0.0167  I      .594     .596    -1.606     .231   .144000   .388000   .1914000      .000      .000  
+88 415 47266.00 I   .142124  .000372   .385078  .000291  I  .1890631  .0000196  2.7249 0.0160  I      .456     .596    -1.322     .231   .146000   .386000   .1887000      .000      .000  
+88 416 47267.00 I   .143839  .000365   .383464  .000312  I  .1864171  .0000231  2.5391 0.0170  I      .301     .650    -1.090     .276   .147000   .385000   .1862000      .000      .000  
+88 417 47268.00 I   .145638  .000393   .381931  .000377  I  .1840284  .0000278  2.2227 0.0195  I      .038     .633     -.979     .265   .148000   .383000   .1838000      .000      .000  
+88 418 47269.00 I   .147446  .000417   .380503  .000512  I  .1819888  .0000313  1.8551 0.0280  I     -.402     .618     -.982     .277   .150000   .382000   .1818000      .000      .000  
+88 419 47270.00 I   .149227  .000475   .379205  .000571  I  .1803083  .0000485  1.5171 0.0265  I     -.975     .968    -1.118     .245   .151000   .381000   .1802000      .000      .000  
+88 420 47271.00 I   .150923  .000545   .378018  .000570  I  .1789243  .0000428  1.2698 0.0306  I    -1.409     .914    -1.409     .227   .153000   .379000   .1788000      .000      .000  
+88 421 47272.00 I   .152480  .000547   .376907  .000636  I  .1777309  .0000372  1.1358 0.0303  I    -1.415     .961    -1.748     .251   .154000   .378000   .1776000      .000      .000  
+88 422 47273.00 I   .153883  .000609   .375832  .000638  I  .1766178  .0000430  1.1066 0.0275  I     -.920    1.031    -1.979     .215   .156000   .377000   .1764000      .000      .000  
+88 423 47274.00 I   .155182  .000616   .374728  .000619  I  .1754892  .0000404  1.1630 0.0299  I     -.472    1.119    -2.042     .233   .157000   .374000   .1751000      .000      .000  
+88 424 47275.00 I   .156463  .000638   .373523  .000823  I  .1742706  .0000415  1.2832 0.0240  I     -.354    1.119    -2.148     .233   .159000   .373000   .1738000      .000      .000  
+88 425 47276.00 I   .157752  .000679   .372218  .000785  I  .1729094  .0000258  1.4437 0.0279  I     -.410     .961    -2.343     .278   .160000   .372000   .1724000      .000      .000  
+88 426 47277.00 I   .159044  .000677   .370851  .000735  I  .1713784  .0000374  1.6185 0.0341  I     -.513     .956    -2.398     .279   .162000   .370000   .1708000      .000      .000  
+88 427 47278.00 I   .160340  .000560   .369461  .000704  I  .1696762  .0000631  1.7819 0.0366  I     -.625     .893    -2.050     .275   .163000   .369000   .1690000      .000      .000  
+88 428 47279.00 I   .161671  .000560   .368081  .000704  I  .1678264  .0000630  1.9099 0.0441  I     -.626     .893    -1.265     .275   .165000   .368000   .1671000      .000      .000  
+88 429 47280.00 I   .163064  .000970   .366725  .000795  I  .1658776  .0000616  1.9742 0.0379  I     -.652     .893     -.638     .275   .166000   .367000   .1652000      .000      .000  
+88 430 47281.00 I   .164477  .000896   .365389  .000684  I  .1639065  .0000423  1.9529 0.0347  I     -.733     .803     -.478     .251   .167000   .366000   .1633000      .000      .000  
+88 5 1 47282.00 I   .165855  .000763   .364063  .000248  I  .1620006  .0000319  1.8453 0.0238  I     -.802     .638     -.687     .185   .169000   .364000   .1614000      .000      .000  
+88 5 2 47283.00 I   .167147  .000720   .362735  .000456  I  .1602385  .0000217  1.6698 0.0190  I     -.839     .277    -1.071     .298   .170000   .363000   .1597000      .000      .000  
+88 5 3 47284.00 I   .168285  .000780   .361394  .000499  I  .1586712  .0000208  1.4637 0.0156  I     -.812     .277    -1.447     .298   .171000   .363000   .1582000      .000      .000  
+88 5 4 47285.00 I   .169162  .000780   .360021  .000499  I  .1573034  .0000225  1.2808 0.0139  I     -.632     .277    -1.624     .298   .172000   .362000   .1569000      .000      .000  
+88 5 5 47286.00 I   .169788  .000744   .358587  .000447  I  .1560843  .0000184  1.1737 0.0150  I     -.298     .277    -1.616     .298   .173000   .360000   .1557000      .000      .000  
+88 5 6 47287.00 I   .170224  .000606   .357071  .000419  I  .1549187  .0000198  1.1786 0.0163  I      .129     .287    -1.546     .152   .173000   .359000   .1545000      .000      .000  
+88 5 7 47288.00 I   .170572  .000664   .355490  .000540  I  .1536860  .0000270  1.3070 0.0163  I      .572     .247    -1.493     .193   .174000   .357000   .1533000      .000      .000  
+88 5 8 47289.00 I   .170939  .000693   .353873  .000207  I  .1522705  .0000260  1.5380 0.0198  I      .911     .247    -1.503     .193   .174000   .355000   .1519000      .000      .000  
+88 5 9 47290.00 I   .171422  .000657   .352246  .000264  I  .1505918  .0000290  1.8235 0.0177  I     1.015     .247    -1.596     .193   .175000   .354000   .1502000      .000      .000  
+88 510 47291.00 I   .172031  .000687   .350624  .000235  I  .1486276  .0000240  2.0966 0.0195  I      .865     .247    -1.710     .193   .176000   .352000   .1482000      .000      .000  
+88 511 47292.00 I   .172740  .000669   .349018  .000241  I  .1464257  .0000261  2.2882 0.0204  I      .517     .436    -1.748     .395   .176000   .351000   .1460000      .000      .000  
+88 512 47293.00 I   .173522  .000650   .347441  .000264  I  .1440946  .0000331  2.3494 0.0214  I      .070     .565    -1.642     .524   .177000   .349000   .1436000      .000      .000  
+88 513 47294.00 I   .174315  .000650   .345886  .000264  I  .1417745  .0000340  2.2678 0.0231  I     -.326     .565    -1.402     .524   .177000   .347000   .1413000      .000      .000  
+88 514 47295.00 I   .175061  .000752   .344342  .000698  I  .1396002  .0000323  2.0625 0.0223  I     -.508     .565    -1.109     .524   .178000   .346000   .1392000      .000      .000  
+88 515 47296.00 I   .175731  .000747   .342790  .000758  I  .1376748  .0000288  1.7813 0.0256  I     -.454     .519     -.913     .457   .178000   .344000   .1373000      .000      .000  
+88 516 47297.00 I   .176269  .000713   .341207  .000751  I  .1360396  .0000397  1.4942 0.0332  I     -.221     .840     -.918     .494   .178000   .342000   .1357000      .000      .000  
+88 517 47298.00 I   .176616  .000673   .339565  .000879  I  .1346682  .0000599  1.2617 0.0355  I      .118     .916    -1.132     .419   .179000   .340000   .1345000      .000      .000  
+88 518 47299.00 I   .176737  .000673   .337839  .000879  I  .1334901  .0000588  1.1076 0.0383  I      .454     .916    -1.465     .419   .179000   .339000   .1333000      .000      .000  
+88 519 47300.00 I   .176689  .000655   .336003  .000699  I  .1324249  .0000479  1.0378 0.0381  I      .772     .912    -1.567     .354   .179000   .337000   .1323000      .000      .000  
+88 520 47301.00 I   .176571  .000516   .334072  .000661  I  .1313853  .0000486  1.0554 0.0369  I      .863     .866    -1.530     .333   .179000   .335000   .1313000      .000      .000  
+88 521 47302.00 I   .176472  .000520   .332078  .000387  I  .1302903  .0000561  1.1443 0.0418  I      .630     .918    -1.513     .346   .179000   .333000   .1301000      .000      .000  
+88 522 47303.00 I   .176453  .000528   .330055  .000270  I  .1290823  .0000680  1.2764 0.0426  I      .237     .764    -1.614     .158   .179000   .331000   .1288000      .000      .000  
+88 523 47304.00 I   .176504  .000574   .328048  .000290  I  .1277310  .0000640  1.4281 0.0479  I     -.041     .794    -1.736     .166   .178000   .330000   .1274000      .000      .000  
+88 524 47305.00 I   .176508  .000623   .326097  .000277  I  .1262273  .0000674  1.5763 0.0485  I     -.139     .794    -1.718     .166   .178000   .328000   .1258000      .000      .000  
+88 525 47306.00 I   .176371  .000534   .324224  .000256  I  .1245896  .0000730  1.6911 0.0537  I     -.219     .491    -1.497     .126   .177000   .326000   .1241000      .000      .000  
+88 526 47307.00 I   .176036  .000463   .322405  .000337  I  .1228631  .0000837  1.7521 0.0533  I     -.421     .910    -1.172     .334   .177000   .324000   .1224000      .000      .000  
+88 527 47308.00 I   .175458  .000428   .320609  .000291  I  .1211064  .0000776  1.7503 0.0554  I     -.728     .910     -.940     .334   .176000   .323000   .1206000      .000      .000  
+88 528 47309.00 I   .174650  .000460   .318806  .000395  I  .1193840  .0000726  1.6842 0.0528  I     -.988     .910     -.921     .334   .176000   .321000   .1189000      .000      .000  
+88 529 47310.00 I   .173647  .000460   .316974  .000395  I  .1177611  .0000715  1.5488 0.0458  I    -1.079     .910    -1.093     .334   .175000   .319000   .1174000      .000      .000  
+88 530 47311.00 I   .172508  .000183   .315109  .000348  I  .1163064  .0000560  1.3535 0.0425  I    -1.045     .910    -1.351     .334   .174000   .317000   .1161000      .000      .000  
+88 531 47312.00 I   .171313  .000234   .313217  .000290  I  .1150594  .0000458  1.1422 0.0365  I    -1.024     .693    -1.623     .240   .174000   .315000   .1149000      .000      .000  
+88 6 1 47313.00 I   .170143  .000299   .311304  .000159  I  .1140094  .0000467  0.9687 0.0321  I    -1.092     .363    -1.871     .298   .173000   .313000   .1140000      .000      .000  
+88 6 2 47314.00 I   .169074  .000282   .309379  .000156  I  .1130943  .0000450  0.8781 0.0357  I    -1.225     .363    -2.034     .298   .172000   .311000   .1132000      .000      .000  
+88 6 3 47315.00 I   .168160  .000354   .307456  .000407  I  .1122126  .0000541  0.9091 0.0316  I    -1.407     .562    -2.049     .298   .171000   .309000   .1122000      .000      .000  
+88 6 4 47316.00 I   .167385  .000327   .305554  .000430  I  .1112338  .0000445  1.0669 0.0340  I    -1.579     .562    -1.964     .298   .170000   .307000   .1112000      .000      .000  
+88 6 5 47317.00 I   .166735  .000302   .303687  .000433  I  .1100538  .0000413  1.2998 0.0336  I    -1.653     .569    -1.895     .253   .170000   .304000   .1099000      .000      .000  
+88 6 6 47318.00 I   .166213  .000328   .301859  .000543  I  .1086331  .0000504  1.5361 0.0328  I    -1.499     .674    -1.945     .327   .169000   .302000   .1083000      .000      .000  
+88 6 7 47319.00 I   .165809  .000367   .300064  .000534  I  .1070061  .0000509  1.6988 0.0354  I    -1.232     .674    -2.061     .327   .168000   .300000   .1066000      .000      .000  
+88 6 8 47320.00 I   .165500  .000367   .298286  .000534  I  .1052785  .0000496  1.7326 0.0361  I    -1.091     .674    -2.097     .327   .168000   .298000   .1048000      .000      .000  
+88 6 9 47321.00 I   .165260  .000349   .296515  .000518  I  .1035866  .0000513  1.6294 0.0296  I    -1.117     .580    -1.979     .385   .168000   .296000   .1030000      .000      .000  
+88 610 47322.00 I   .165075  .000321   .294741  .000302  I  .1020572  .0000324  1.4138 0.0306  I    -1.291     .445    -1.717     .317   .168000   .294000   .1015000      .000      .000  
+88 611 47323.00 I   .164939  .000373   .292954  .000149  I  .1007806  .0000335  1.1335 0.0237  I    -1.580     .244    -1.402     .229   .167000   .292000   .1002000      .000      .000  
+88 612 47324.00 I   .164891  .000373   .291145  .000149  I  .0997917  .0000346  0.8486 0.0238  I    -1.849     .244    -1.181     .229   .167000   .290000   .0992000      .000      .000  
+88 613 47325.00 I   .164964  .000333   .289308  .000248  I  .0990674  .0000338  0.6115 0.0249  I    -1.906     .244    -1.144     .229   .167000   .289000   .0985000      .000      .000  
+88 614 47326.00 I   .165103  .000555   .287445  .000282  I  .0985414  .0000359  0.4562 0.0201  I    -1.707     .244    -1.243     .229   .167000   .287000   .0980000      .000      .000  
+88 615 47327.00 I   .165220  .000729   .285551  .000340  I  .0981234  .0000216  0.3958 0.0212  I    -1.391     .361    -1.326     .270   .167000   .285000   .0976000      .000      .000  
+88 616 47328.00 I   .165235  .000926   .283612  .000331  I  .0977212  .0000226  0.4214 0.0182  I    -1.161     .420    -1.263     .257   .167000   .283000   .0973000      .000      .000  
+88 617 47329.00 I   .165078  .000930   .281610  .000350  I  .0972599  .0000292  0.5092 0.0200  I    -1.154     .365    -1.080     .229   .168000   .281000   .0969000      .000      .000  
+88 618 47330.00 I   .164740  .000922   .279509  .000347  I  .0966944  .0000331  0.6222 0.0228  I    -1.407     .365     -.964     .229   .168000   .279000   .0964000      .000      .000  
+88 619 47331.00 I   .164408  .000907   .277292  .000332  I  .0960175  .0000349  0.7293 0.0233  I    -1.763     .358    -1.049     .239   .168000   .277000   .0957000      .000      .000  
+88 620 47332.00 I   .164308  .000884   .274976  .000482  I  .0952407  .0000327  0.8219 0.0256  I    -2.111     .339    -1.318     .230   .168000   .275000   .0950000      .000      .000  
+88 621 47333.00 I   .164459  .000734   .272627  .000514  I  .0943817  .0000374  0.8907 0.0250  I    -2.451     .298    -1.542     .172   .169000   .273000   .0941000      .000      .000  
+88 622 47334.00 I   .164844  .000688   .270336  .000546  I  .0934727  .0000378  0.9194 0.0370  I    -2.872     .286    -1.467     .187   .169000   .271000   .0932000      .000      .000  
+88 623 47335.00 I   .165440  .000701   .268181  .000789  I  .0925620  .0000639  0.8907 0.0319  I    -3.436     .319    -1.377     .205   .169000   .269000   .0922000      .000      .000  
+88 624 47336.00 I   .166198  .000737   .266171  .000731  I  .0917134  .0000514  0.7955 0.0389  I    -4.059     .287    -1.476     .204   .170000   .267000   .0914000      .000      .000  
+88 625 47337.00 I   .167033  .000712   .264291  .000713  I  .0909916  .0000444  0.6385 0.0345  I    -4.558     .557    -1.672     .383   .170000   .265000   .0907000      .000      .000  
+88 626 47338.00 I   .167857  .000651   .262495  .000607  I  .0904523  .0000460  0.4338 0.0324  I    -4.718     .653    -1.735     .451   .170000   .263000   .0901000      .000      .000  
+88 627 47339.00 I   .168577  .000678   .260716  .000646  I  .0901316  .0000472  0.2064 0.0334  I    -4.521     .721    -1.491     .513   .171000   .261000   .0898000      .000      .000  
+88 628 47340.00 I   .169078  .000689   .258899  .000503  I  .0900288  .0000484  0.0110 0.0297  I    -4.186     .721    -1.094     .513   .171000   .259000   .0897000      .000      .000  
+88 629 47341.00 I   .169346  .000636   .257053  .000469  I  .0900763  .0000362 -0.0845 0.0266  I    -3.914     .721     -.826     .513   .171000   .257000   .0897000      .000      .000  
+88 630 47342.00 I   .169407  .000506   .255191  .000473  I  .0901508  .0000220 -0.0390 0.0449  I    -3.730    1.137     -.750     .449   .171000   .255000   .0897000      .000      .000  
+88 7 1 47343.00 I   .169293  .000985   .253312  .000510  I  .0901073  .0000822  0.1477 0.0421  I    -3.662    1.285     -.827     .305   .171000   .253000   .0897000      .000      .000  
+88 7 2 47344.00 I   .169043  .001107   .251413  .000604  I  .0898241  .0000812  0.4284 0.0603  I    -3.921    1.120    -1.103     .346   .171000   .251000   .0893000      .000      .000  
+88 7 3 47345.00 I   .168718  .001428   .249492  .000600  I  .0892455  .0000882  0.7239 0.0564  I    -4.717    1.067    -1.679     .332   .171000   .249000   .0887000      .000      .000  
+88 7 4 47346.00 I   .168392  .001353   .247551  .000649  I  .0883989  .0000784  0.9514 0.0590  I    -5.741    1.011    -2.148     .343   .171000   .247000   .0878000      .000      .000  
+88 7 5 47347.00 I   .168128  .001305   .245587  .000619  I  .0873872  .0000785  1.0454 0.0565  I    -6.628     .946    -2.040     .362   .171000   .246000   .0868000      .000      .000  
+88 7 6 47348.00 I   .167947  .001442   .243581  .000613  I  .0863579  .0000815  0.9901 0.0558  I    -7.024     .717    -1.525     .363   .171000   .244000   .0858000      .000      .000  
+88 7 7 47349.00 I   .167849  .001346   .241539  .000564  I  .0854474  .0000793  0.8136 0.0522  I    -7.052     .717     -.787     .363   .170000   .242000   .0849000      .000      .000  
+88 7 8 47350.00 I   .167747  .001246   .239484  .000563  I  .0847557  .0000653  0.5619 0.0497  I    -6.886     .768     -.459     .332   .170000   .240000   .0842000      .000      .000  
+88 7 9 47351.00 I   .167560  .001227   .237432  .000427  I  .0843280  .0000598  0.2968 0.0437  I    -6.928     .758     -.619     .343   .169000   .239000   .0838000      .000      .000  
+88 710 47352.00 I   .167240  .001103   .235399  .000458  I  .0841458  .0000581  0.0804 0.0441  I    -7.301    1.191     -.988     .331   .169000   .237000   .0836000      .000      .000  
+88 711 47353.00 I   .166830  .001311   .233390  .000572  I  .0841389  .0000649 -0.0513 0.0442  I    -7.643    1.252    -1.347     .259   .169000   .235000   .0837000      .000      .000  
+88 712 47354.00 I   .166361  .001311   .231408  .000572  I  .0842164  .0000667 -0.0872 0.0476  I    -7.765    1.201    -1.655     .291   .168000   .233000   .0838000      .000      .000  
+88 713 47355.00 I   .165858  .001700   .229461  .000641  I  .0842809  .0000696 -0.0259 0.0453  I    -7.874    1.201    -2.100     .291   .168000   .231000   .0838000      .000      .000  
+88 714 47356.00 I   .165381  .001451   .227575  .000443  I  .0842424  .0000612  0.1127 0.0477  I    -7.869    1.295    -2.410     .296   .167000   .229000   .0838000      .000      .000  
+88 715 47357.00 I   .164937  .001472   .225745  .000434  I  .0840428  .0000652  0.2895 0.0510  I    -7.635     .995    -2.093     .264   .167000   .228000   .0837000      .000      .000  
+88 716 47358.00 I   .164436  .001581   .223927  .000403  I  .0836617  .0000815  0.4713 0.0503  I    -7.327     .406    -1.325     .224   .166000   .226000   .0833000      .000      .000  
+88 717 47359.00 I   .163829  .001510   .222063  .000382  I  .0831075  .0000767  0.6313 0.0574  I    -7.175     .361     -.574     .240   .165000   .223000   .0828000      .000      .000  
+88 718 47360.00 I   .163209  .001322   .220095  .000282  I  .0824115  .0000808  0.7541 0.0549  I    -7.375     .287     -.135     .215   .165000   .221000   .0821000      .000      .000  
+88 719 47361.00 I   .162581  .000835   .218034  .000323  I  .0816152  .0000785  0.8291 0.0543  I    -7.818     .287      .132     .215   .164000   .219000   .0813000      .000      .000  
+88 720 47362.00 I   .161910  .000803   .215909  .000364  I  .0807731  .0000727  0.8447 0.0565  I    -8.289     .535      .326     .198   .163000   .217000   .0804000      .000      .000  
+88 721 47363.00 I   .161156  .000470   .213768  .000522  I  .0799456  .0000813  0.8010 0.0445  I    -8.751     .648      .346     .156   .162000   .215000   .0796000      .000      .000  
+88 722 47364.00 I   .160277  .000407   .211659  .000525  I  .0791869  .0000512  0.7101 0.0482  I    -9.220     .756     -.010     .158   .161000   .213000   .0788000      .000      .000  
+88 723 47365.00 I   .159233  .000391   .209618  .000558  I  .0785392  .0000518  0.5780 0.0320  I    -9.503     .756     -.741     .158   .160000   .211000   .0782000      .000      .000  
+88 724 47366.00 I   .158046  .000376   .207629  .000550  I  .0780408  .0000384  0.4164 0.0311  I    -9.535     .737    -1.369     .150   .159000   .209000   .0777000      .000      .000  
+88 725 47367.00 I   .156759  .000574   .205662  .000508  I  .0777049  .0000345  0.2594 0.0260  I    -9.456     .664    -1.491     .181   .158000   .207000   .0775000      .000      .000  
+88 726 47368.00 I   .155411  .000676   .203688  .000440  I  .0775034  .0000350  0.1583 0.0259  I    -9.497     .573    -1.227     .180   .156000   .205000   .0773000      .000      .000  
+88 727 47369.00 I   .154067  .000863   .201711  .000446  I  .0773500  .0000386  0.1722 0.0249  I    -9.521     .573    -1.108     .183   .155000   .203000   .0772000      .000      .000  
+88 728 47370.00 I   .152797  .000800   .199746  .000353  I  .0771104  .0000353  0.3318 0.0249  I    -9.421     .434    -1.333     .186   .154000   .201000   .0770000      .000      .000  
+88 729 47371.00 I   .151642  .000799   .197803  .000302  I  .0766462  .0000316  0.6119 0.0235  I    -9.136     .426    -1.485     .171   .153000   .199000   .0765000      .000      .000  
+88 730 47372.00 I   .150613  .000796   .195889  .000297  I  .0758730  .0000311  0.9329 0.0226  I    -8.803     .403    -1.305     .237   .152000   .198000   .0758000      .000      .000  
+88 731 47373.00 I   .149680  .000827   .193991  .000295  I  .0748029  .0000323  1.1863 0.0230  I    -8.733     .389    -1.059     .237   .150000   .196000   .0747000      .000      .000  
+88 8 1 47374.00 I   .148737  .000793   .192093  .000331  I  .0735477  .0000340  1.2976 0.0234  I    -9.075     .412    -1.083     .251   .149000   .194000   .0734000      .000      .000  
+88 8 2 47375.00 I   .147679  .000728   .190193  .000371  I  .0722613  .0000340  1.2481 0.0231  I    -9.672     .435    -1.442     .280   .148000   .192000   .0720000      .000      .000  
+88 8 3 47376.00 I   .146460  .000547   .188283  .000349  I  .0710986  .0000312  1.0573 0.0234  I   -10.262     .435    -1.842     .280   .147000   .190000   .0708000      .000      .000  
+88 8 4 47377.00 I   .145064  .000496   .186359  .000300  I  .0701735  .0000323  0.7861 0.0261  I   -10.662     .408    -1.939     .289   .145000   .188000   .0698000      .000      .000  
+88 8 5 47378.00 I   .143549  .000560   .184427  .000314  I  .0695288  .0000419  0.5076 0.0272  I   -10.747     .307    -1.632     .250   .144000   .185000   .0691000      .000      .000  
+88 8 6 47379.00 I   .141975  .000560   .182503  .000314  I  .0691416  .0000437  0.2791 0.0300  I   -10.588     .307    -1.303     .250   .142000   .184000   .0687000      .000      .000  
+88 8 7 47380.00 I   .140372  .000436   .180588  .000328  I  .0689507  .0000429  0.1104 0.0296  I   -10.368     .307    -1.330     .250   .141000   .182000   .0686000      .000      .000  
+88 8 8 47381.00 I   .138711  .000431   .178690  .000273  I  .0688957  .0000398  0.0171 0.0282  I   -10.192     .266    -1.527     .224   .140000   .180000   .0685000      .000      .000  
+88 8 9 47382.00 I   .137032  .000510   .176831  .000240  I  .0688826  .0000367  0.0254 0.0281  I   -10.022     .207    -1.683     .181   .138000   .178000   .0685000      .000      .000  
+88 810 47383.00 I   .135399  .000487   .175036  .000267  I  .0688161  .0000397  0.1202 0.0225  I    -9.794     .791    -1.786     .114   .137000   .177000   .0684000      .000      .000  
+88 811 47384.00 I   .133883  .000583   .173345  .000378  I  .0686211  .0000260  0.2784 0.0219  I    -9.552     .791    -1.731     .298   .135000   .175000   .0682000      .000      .000  
+88 812 47385.00 I   .132534  .000583   .171798  .000378  I  .0682485  .0000186  0.4691 0.0159  I    -9.397     .791    -1.403     .298   .134000   .174000   .0678000      .000      .000  
+88 813 47386.00 I   .131275  .000616   .170397  .000308  I  .0676839  .0000182  0.6568 0.0143  I    -9.376     .791     -.883     .298   .132000   .172000   .0672000      .000      .000  
+88 814 47387.00 I   .130008  .000563   .169109  .000296  I  .0669456  .0000218  0.8123 0.0178  I    -9.491     .258     -.448     .298   .131000   .171000   .0665000      .000      .000  
+88 815 47388.00 I   .128643  .000470   .167903  .000138  I  .0660771  .0000306  0.9144 0.0198  I    -9.720     .362     -.316     .298   .129000   .169000   .0656000      .000      .000  
+88 816 47389.00 I   .127093  .000445   .166742  .000234  I  .0651379  .0000331  0.9534 0.0211  I    -9.971     .362     -.437     .298   .127000   .168000   .0647000      .000      .000  
+88 817 47390.00 I   .125331  .000445   .165595  .000234  I  .0641926  .0000291  0.9257 0.0231  I   -10.123     .362     -.604     .298   .125000   .167000   .0637000      .000      .000  
+88 818 47391.00 I   .123406  .000449   .164434  .000206  I  .0633086  .0000322  0.8318 0.0199  I   -10.205     .362     -.747     .298   .124000   .165000   .0629000      .000      .000  
+88 819 47392.00 I   .121374  .000301   .163241  .000187  I  .0625467  .0000272  0.6847 0.0214  I   -10.397     .370     -.966     .298   .122000   .164000   .0622000      .000      .000  
+88 820 47393.00 I   .119274  .000339   .162008  .000195  I  .0619491  .0000282  0.5079 0.0194  I   -10.766     .378    -1.291     .298   .120000   .163000   .0616000      .000      .000  
+88 821 47394.00 I   .117135  .000339   .160741  .000195  I  .0615310  .0000276  0.3307 0.0192  I   -11.131     .378    -1.542     .298   .118000   .161000   .0612000      .000      .000  
+88 822 47395.00 I   .114975  .000367   .159443  .000170  I  .0612741  .0000261  0.1943 0.0183  I   -11.277     .378    -1.537     .298   .116000   .160000   .0610000      .000      .000  
+88 823 47396.00 I   .112786  .000331   .158136  .000160  I  .0611109  .0000241  0.1523 0.0148  I   -11.185     .378    -1.335     .298   .114000   .159000   .0608000      .000      .000  
+88 824 47397.00 I   .110555  .000400   .156842  .000173  I  .0609245  .0000141  0.2452 0.0170  I   -10.990     .447    -1.151     .298   .112000   .158000   .0606000      .000      .000  
+88 825 47398.00 I   .108275  .000463   .155581  .000218  I  .0605739  .0000241  0.4778 0.0138  I   -10.810     .506    -1.050     .298   .110000   .156000   .0602000      .000      .000  
+88 826 47399.00 I   .105966  .000463   .154361  .000218  I  .0599372  .0000238  0.8056 0.0206  I   -10.733     .506     -.886     .298   .108000   .155000   .0595000      .000      .000  
+88 827 47400.00 I   .103668  .000451   .153183  .000226  I  .0589596  .0000335  1.1429 0.0241  I   -10.846     .506     -.589     .298   .105000   .154000   .0584000      .000      .000  
+88 828 47401.00 I   .101413  .000389   .152048  .000191  I  .0576822  .0000418  1.3879 0.0275  I   -11.141     .506     -.362     .298   .103000   .153000   .0570000      .000      .000  
+88 829 47402.00 I   .099179  .000357   .150947  .000223  I  .0562406  .0000435  1.4625 0.0392  I   -11.436     .358     -.409     .190   .101000   .152000   .0555000      .000      .000  
+88 830 47403.00 I   .096931  .000236   .149870  .000259  I  .0548202  .0000664  1.3483 0.0409  I   -11.607     .148     -.636     .252   .099000   .151000   .0540000      .000      .000  
+88 831 47404.00 I   .094639  .000313   .148808  .000364  I  .0535890  .0000692  1.0993 0.0478  I   -11.795     .148     -.832     .252   .097000   .150000   .0527000      .000      .000  
+88 9 1 47405.00 I   .092289  .000313   .147762  .000364  I  .0526372  .0000687  0.8036 0.0467  I   -12.200     .148    -1.020     .252   .094000   .149000   .0517000      .000      .000  
+88 9 2 47406.00 I   .089921  .000285   .146743  .000373  I  .0519722  .0000626  0.5368 0.0435  I   -12.762     .148    -1.204     .252   .092000   .148000   .0511000      .000      .000  
+88 9 3 47407.00 I   .087577  .000295   .145762  .000537  I  .0515357  .0000535  0.3539 0.0453  I   -13.165     .270    -1.448     .202   .090000   .147000   .0507000      .000      .000  
+88 9 4 47408.00 I   .085267  .000307   .144806  .000638  I  .0512297  .0000654  0.2751 0.0377  I   -13.239     .349    -1.700     .147   .088000   .146000   .0504000      .000      .000  
+88 9 5 47409.00 I   .082984  .000318   .143851  .000673  I  .0509546  .0000532  0.2888 0.0396  I   -13.053     .384    -1.797     .298   .086000   .145000   .0503000      .000      .000  
+88 9 6 47410.00 I   .080727  .000257   .142877  .000594  I  .0506274  .0000448  0.3765 0.0321  I   -12.622     .384    -1.765     .298   .083000   .144000   .0501000      .000      .000  
+88 9 7 47411.00 I   .078490  .000226   .141894  .000541  I  .0501843  .0000359  0.5162 0.0403  I   -11.938     .350    -1.708     .126   .081000   .143000   .0497000      .000      .000  
+88 9 8 47412.00 I   .076267  .000314   .140924  .000476  I  .0495867  .0000669  0.6809 0.0425  I   -11.080     .279    -1.588     .124   .079000   .142000   .0492000      .000      .000  
+88 9 9 47413.00 I   .074057  .000347   .139990  .000165  I  .0488225  .0000770  0.8455 0.0510  I   -10.257     .175    -1.228     .141   .076000   .141000   .0485000      .000      .000  
+88 910 47414.00 I   .071886  .000347   .139112  .000165  I  .0479034  .0000769  0.9870 0.0555  I    -9.698     .175     -.574     .141   .074000   .141000   .0476000      .000      .000  
+88 911 47415.00 I   .069777  .000347   .138302  .000165  I  .0468630  .0000800  1.0852 0.0515  I    -9.448     .175     -.053     .141   .071000   .140000   .0466000      .000      .000  
+88 912 47416.00 I   .067686  .000406   .137557  .000256  I  .0457511  .0000686  1.1293 0.0509  I    -9.473     .175      .009     .141   .069000   .139000   .0454000      .000      .000  
+88 913 47417.00 I   .065546  .000372   .136864  .000241  I  .0446227  .0000631  1.1185 0.0538  I    -9.709     .115     -.336     .298   .067000   .138000   .0442000      .000      .000  
+88 914 47418.00 I   .063293  .000339   .136209  .000241  I  .0435312  .0000830  1.0565 0.0354  I   -10.030     .121     -.813     .298   .064000   .138000   .0431000      .000      .000  
+88 915 47419.00 I   .060869  .000350   .135577  .000369  I  .0425233  .0000321  0.9537 0.0447  I   -10.321     .121    -1.176     .298   .062000   .137000   .0420000      .000      .000  
+88 916 47420.00 I   .058265  .000350   .134961  .000369  I  .0416315  .0000331  0.8277 0.0219  I   -10.584     .180    -1.381     .298   .059000   .136000   .0411000      .000      .000  
+88 917 47421.00 I   .055502  .000347   .134348  .000426  I  .0408676  .0000298  0.7025 0.0216  I   -10.851     .180    -1.522     .298   .056000   .136000   .0404000      .000      .000  
+88 918 47422.00 I   .052603  .000157   .133723  .000395  I  .0402159  .0000277  0.6092 0.0225  I   -11.036     .516    -1.624     .298   .053000   .135000   .0397000      .000      .000  
+88 919 47423.00 I   .049581  .000191   .133086  .000425  I  .0396256  .0000337  0.5867 0.0235  I   -10.958     .663    -1.642     .109   .050000   .135000   .0392000      .000      .000  
+88 920 47424.00 I   .046436  .000206   .132471  .000460  I  .0390073  .0000379  0.6697 0.0259  I   -10.562     .663    -1.580     .109   .047000   .134000   .0386000      .000      .000  
+88 921 47425.00 I   .043170  .000204   .131913  .000466  I  .0382443  .0000394  0.8783 0.0339  I    -9.966     .663    -1.506     .109   .044000   .134000   .0379000      .000      .000  
+88 922 47426.00 I   .039824  .000210   .131419  .000386  I  .0372149  .0000563  1.1943 0.0326  I    -9.532     .747    -1.382     .122   .041000   .133000   .0369000      .000      .000  
+88 923 47427.00 I   .036435  .000336   .130977  .000351  I  .0358406  .0000519  1.5556 0.0417  I    -9.523     .717    -1.096     .118   .037000   .133000   .0355000      .000      .000  
+88 924 47428.00 I   .033043  .000377   .130578  .000317  I  .0341172  .0000616  1.8766 0.0412  I    -9.996     .668     -.668     .298   .034000   .133000   .0338000      .000      .000  
+88 925 47429.00 I   .029697  .000425   .130234  .000333  I  .0321301  .0000641  2.0697 0.0433  I   -10.757     .669     -.330     .112   .031000   .132000   .0318000      .000      .000  
+88 926 47430.00 I   .026438  .000519   .129962  .000468  I  .0300408  .0000608  2.0737 0.0407  I   -11.373     .669     -.294     .112   .027000   .132000   .0297000      .000      .000  
+88 927 47431.00 I   .023217  .000537   .129768  .000556  I  .0280465  .0000503  1.8862 0.0314  I   -11.611     .669     -.456     .112   .024000   .132000   .0277000      .000      .000  
+88 928 47432.00 I   .019958  .000480   .129647  .000563  I  .0263097  .0000161  1.5747 0.0373  I   -11.680     .769     -.521     .179   .021000   .132000   .0260000      .000      .000  
+88 929 47433.00 I   .016604  .000523   .129578  .000643  I  .0249050  .0000552  1.2396 0.0306  I   -11.885     .858     -.427     .227   .018000   .132000   .0245000      .000      .000  
+88 930 47434.00 I   .013117  .000549   .129525  .000669  I  .0238086  .0000590  0.9702 0.0402  I   -12.136     .858     -.450     .227   .014000   .132000   .0234000      .000      .000  
+8810 1 47435.00 I   .009537  .000549   .129435  .000669  I  .0229268  .0000584  0.8144 0.0391  I   -12.027     .858     -.829     .227   .011000   .132000   .0225000      .000      .000  
+8810 2 47436.00 I   .005981  .000476   .129326  .000550  I  .0221395  .0000512  0.7797 0.0380  I   -11.425     .858    -1.362     .227   .008000   .132000   .0216000      .000      .000  
+8810 3 47437.00 I   .002559  .000523   .129250  .000548  I  .0213314  .0000486  0.8529 0.0417  I   -10.659     .843    -1.612     .271   .005000   .132000   .0207000      .000      .000  
+8810 4 47438.00 I  -.000668  .000851   .129269  .000668  I  .0204069  .0000659  1.0064 0.0308  I   -10.096     .684    -1.346     .396   .002000   .132000   .0198000      .000      .000  
+8810 5 47439.00 I  -.003644  .000847   .129446  .000731  I  .0193018  .0000377  1.2103 0.0365  I    -9.802     .684     -.615     .396  -.001600   .132100   .0186300      .000      .000  
+8810 6 47440.00 I  -.006348  .000833   .129831  .000796  I  .0179812  .0000315  1.4296 0.0233  I    -9.680     .684      .472     .396  -.004800   .132500   .0172900      .000      .000  
+8810 7 47441.00 I  -.008941  .000775   .130419  .000736  I  .0164502  .0000273  1.6262 0.0207  I    -9.673     .684     1.342     .396  -.008000   .133000   .0157700      .000      .000  
+8810 8 47442.00 I  -.011635  .000721   .131184  .000732  I  .0147438  .0000269  1.7779 0.0199  I    -9.803     .533     1.470     .322  -.011200   .133500   .0141000      .000      .000  
+8810 9 47443.00 I  -.014534  .000788   .132069  .000819  I  .0129147  .0000290  1.8687 0.0196  I   -10.097     .112     1.079     .331  -.014400   .134100   .0123300      .000      .000  
+881010 47444.00 I  -.017668  .000788   .132983  .000819  I  .0110303  .0000285  1.8879 0.0197  I   -10.541     .116      .424     .166  -.017600   .134700   .0105200      .000      .000  
+881011 47445.00 I  -.021041  .000312   .133834  .000641  I  .0091639  .0000266  1.8320 0.0208  I   -11.122     .116     -.145     .166  -.020800   .135300   .0087500      .000      .000  
+881012 47446.00 I  -.024606  .000361   .134593  .000628  I  .0073898  .0000304  1.7055 0.0215  I   -11.735     .116     -.439     .166  -.024000   .136000   .0070800      .000      .000  
+881013 47447.00 I  -.028288  .000335   .135265  .000533  I  .0057691  .0000339  1.5302 0.0319  I   -12.164     .835     -.406     .729  -.027200   .136700   .0055600      .000      .000  
+881014 47448.00 I  -.031998  .000399   .135860  .000433  I  .0043350  .0000561  1.3380 0.0338  I   -12.223    1.176     -.145    1.017  -.030400   .137400   .0042200      .000      .000  
+881015 47449.00 I  -.035649  .000462   .136389  .000483  I  .0030862  .0000584  1.1663 0.0326  I   -11.874    1.176      .093    1.017  -.033600   .138100   .0030300      .000      .000  
+881016 47450.00 I  -.039166  .000550   .136885  .000671  I  .0019811  .0000331  1.0584 0.0332  I   -11.175    1.176      .064    1.017  -.036800   .138800   .0019600      .000      .000  
+881017 47451.00 I  -.042495  .000600   .137399  .000829  I  .0009354  .0000318  1.0526 0.0234  I   -10.311    1.215     -.235     .856  -.039900   .139500   .0009200      .000      .000  
+881018 47452.00 I  -.045595  .000713   .137986  .001057  I -.0001641  .0000332  1.1669 0.0213  I    -9.469    1.150     -.659     .720  -.043000   .140300  -.0002100      .000      .000  
+881019 47453.00 I  -.048446  .000713   .138691  .001104  I -.0014361  .0000282  1.3942 0.0233  I    -8.822    1.125    -1.050     .298  -.046100   .141100  -.0015300      .000      .000  
+881020 47454.00 I  -.051072  .000713   .139535  .001104  I -.0029797  .0000327  1.7032 0.0219  I    -8.561    1.125    -1.229     .298  -.049200   .141900  -.0031300      .000      .000  
+881021 47455.00 I  -.053561  .000771   .140503  .001105  I -.0048462  .0000335  2.0235 0.0231  I    -8.456    1.125    -1.100     .298  -.052300   .142800  -.0050600      .000      .000  
+881022 47456.00 I  -.056022  .000790   .141580  .000971  I -.0069989  .0000327  2.2606 0.0250  I    -8.480    1.125     -.713     .298  -.055300   .143700  -.0072600      .000      .000  
+881023 47457.00 I  -.058573  .000718   .142753  .000824  I -.0093158  .0000370  2.3428 0.0239  I    -8.623     .615     -.290     .298  -.058300   .144700  -.0096200      .000      .000  
+881024 47458.00 I  -.061320  .000661   .144005  .000413  I -.0116231  .0000348  2.2406 0.0254  I    -8.619     .334     -.065     .298  -.061300   .145700  -.0119800      .000      .000  
+881025 47459.00 I  -.064320  .000661   .145306  .000413  I -.0137447  .0000348  1.9813 0.0224  I    -8.291     .334     -.055     .298  -.064300   .146800  -.0141400      .000      .000  
+881026 47460.00 I  -.067585  .000603   .146623  .000432  I -.0155610  .0000283  1.6478 0.0250  I    -7.836     .334     -.072     .298  -.067300   .148000  -.0160000      .000      .000  
+881027 47461.00 I  -.071008  .000511   .147942  .000477  I -.0170483  .0000358  1.3382 0.0229  I    -7.589     .334      .022     .298  -.070300   .149200  -.0175400      .000      .000  
+881028 47462.00 I  -.074448  .000511   .149283  .000492  I -.0182697  .0000359  1.1255 0.0267  I    -7.577     .305      .130     .175  -.073200   .150600  -.0188100      .000      .000  
+881029 47463.00 I  -.077762  .000455   .150669  .000522  I -.0193425  .0000397  1.0424 0.0264  I    -7.490     .284      .088     .214  -.076200   .151900  -.0199300      .000      .000  
+881030 47464.00 I  -.080813  .000750   .152136  .000507  I -.0203951  .0000387  1.0811 0.0282  I    -7.131     .284     -.066     .214  -.079100   .153400  -.0210200      .000      .000  
+881031 47465.00 I  -.083525  .000750   .153718  .000507  I -.0215348  .0000401  1.2103 0.0265  I    -6.675     .316     -.142     .188  -.082000   .154900  -.0222000      .000      .000  
+8811 1 47466.00 I  -.086039  .000685   .155397  .000443  I -.0228325  .0000361  1.3901 0.0232  I    -6.359     .316     -.035     .188  -.084900   .156500  -.0235300      .000      .000  
+8811 2 47467.00 I  -.088557  .000640   .157148  .000405  I -.0243200  .0000233  1.5851 0.0218  I    -6.152     .283      .163     .183  -.087700   .158200  -.0250200      .000      .000  
+8811 3 47468.00 I  -.091194  .000758   .158948  .000239  I -.0259991  .0000245  1.7695 0.0235  I    -5.850     .348      .359     .214  -.090500   .159900  -.0266900      .000      .000  
+8811 4 47469.00 I  -.093909  .000714   .160779  .000576  I -.0278487  .0000408  1.9231 0.0248  I    -5.480     .348      .629     .214  -.093200   .161700  -.0285100      .000      .000  
+8811 5 47470.00 I  -.096631  .000657   .162627  .000584  I -.0298275  .0000432  2.0233 0.0280  I    -5.301     .365     1.072     .213  -.095900   .163500  -.0304200      .000      .000  
+8811 6 47471.00 I  -.099338  .000303   .164502  .000597  I -.0318693  .0000383  2.0456 0.0282  I    -5.472     .356     1.329     .236  -.098600   .165400  -.0323700      .000      .000  
+8811 7 47472.00 I  -.102060  .000318   .166418  .000750  I -.0338902  .0000363  1.9823 0.0230  I    -5.851     .398     1.258     .235  -.101300   .167300  -.0343100      .000      .000  
+8811 8 47473.00 I  -.104826  .000283   .168374  .000608  I -.0358126  .0000253  1.8549 0.0220  I    -6.198     .567     1.090     .241  -.103900   .169200  -.0361700      .000      .000  
+8811 9 47474.00 I  -.107616  .000265   .170322  .000621  I -.0375893  .0000249  1.6954 0.0166  I    -6.467     .574      .902     .195  -.106400   .171200  -.0378900      .000      .000  
+881110 47475.00 I  -.110370  .000265   .172220  .000621  I -.0391988  .0000215  1.5222 0.0150  I    -6.609     .574      .694     .195  -.108800   .173200  -.0394700      .000      .000  
+881111 47476.00 I  -.112960  .000250   .174102  .000506  I -.0406350  .0000168  1.3531 0.0137  I    -6.371     .605      .536     .192  -.111200   .175300  -.0408900      .000      .000  
+881112 47477.00 I  -.115258  .000247   .176073  .000492  I -.0419216  .0000169  1.2339 0.0117  I    -5.850     .496      .561     .162  -.113400   .177400  -.0421800      .000      .000  
+881113 47478.00 I  -.117269  .000220   .178146  .000455  I -.0431352  .0000162  1.2120 0.0121  I    -5.318     .509      .737     .105  -.115700   .179600  -.0434200      .000      .000  
+881114 47479.00 I  -.119034  .000221   .180309  .000290  I -.0443845  .0000174  1.3067 0.0204  I    -4.851     .321      .864     .108  -.117800   .181800  -.0447200      .000      .000  
+881115 47480.00 I  -.120585  .000244   .182550  .000357  I -.0457852  .0000374  1.5112 0.0146  I    -4.436     .108      .840     .298  -.119900   .184100  -.0461700      .000      .000  
+881116 47481.00 I  -.121991  .000239   .184885  .000336  I -.0474319  .0000234  1.7908 0.0219  I    -4.045     .108      .730     .298  -.121900   .186400  -.0478600      .000      .000  
+881117 47482.00 I  -.123340  .000242   .187332  .000331  I -.0493727  .0000229  2.0880 0.0425  I    -3.721     .791      .670     .298  -.123900   .188800  -.0498400      .000      .000  
+881118 47483.00 I  -.124708  .000235   .189888  .000359  I -.0515897  .0000817  2.3306 0.0422  I    -3.585     .791      .753     .298  -.125800   .191300  -.0520900      .000      .000  
+881119 47484.00 I  -.126172  .000235   .192541  .000359  I -.0539944  .0000813  2.4547 0.0568  I    -3.753     .791      .979     .298  -.127700   .193800  -.0545100      .000      .000  
+881120 47485.00 I  -.127798  .000564   .195270  .000415  I -.0564475  .0000789  2.4240 0.0524  I    -4.185     .791     1.249     .298  -.129600   .196400  -.0569700      .000      .000  
+881121 47486.00 I  -.129602  .000515   .198043  .000373  I -.0587926  .0000661  2.2439 0.0513  I    -4.658     .791     1.407     .298  -.131400   .199000  -.0593200      .000      .000  
+881122 47487.00 I  -.131586  .000565   .200833  .000368  I -.0609012  .0000656  1.9616 0.0576  I    -4.955     .136     1.378     .298  -.133300   .201700  -.0614200      .000      .000  
+881123 47488.00 I  -.133739  .000626   .203614  .000711  I -.0627064  .0000944  1.6512 0.0346  I    -5.071     .182     1.231     .298  -.135100   .204400  -.0632200      .000      .000  
+881124 47489.00 I  -.135980  .000626   .206392  .000711  I -.0642198  .0000222  1.3893 0.0485  I    -5.156     .182     1.098     .298  -.136900   .207200  -.0647300      .000      .000  
+881125 47490.00 I  -.138213  .000626   .209184  .000711  I -.0655199  .0000225  1.2322 0.0149  I    -5.282     .182     1.044     .298  -.138700   .210000  -.0660300      .000      .000  
+881126 47491.00 I  -.140403  .000331   .212013  .000569  I -.0667258  .0000199  1.1998 0.0203  I    -5.325     .182     1.064     .298  -.140500   .212800  -.0672500      .000      .000  
+881127 47492.00 I  -.142552  .000305   .214861  .000503  I -.0679543  .0000339  1.2718 0.0217  I    -5.158     .232     1.158     .153  -.142300   .215700  -.0684900      .000      .000  
+881128 47493.00 I  -.144665  .000241   .217698  .000512  I -.0692912  .0000386  1.4094 0.0258  I    -4.809     .231     1.342     .178  -.144000   .218500  -.0698400      .000      .000  
+881129 47494.00 I  -.146749  .000229   .220493  .000308  I -.0707816  .0000389  1.5727 0.0273  I    -4.386     .231     1.581     .178  -.145700   .221400  -.0713400      .000      .000  
+881130 47495.00 I  -.148794  .000229   .223228  .000308  I -.0724359  .0000386  1.7339 0.0273  I    -3.903     .231     1.784     .178  -.147400   .224300  -.0729900      .000      .000  
+8812 1 47496.00 I  -.150745  .000592   .225929  .000287  I -.0742397  .0000384  1.8659 0.0272  I    -3.338     .231     1.883     .178  -.149000   .227100  -.0747800      .000      .000  
+8812 2 47497.00 I  -.152550  .000616   .228623  .000235  I -.0761494  .0000382  1.9429 0.0250  I    -2.752     .475     1.935     .299  -.150600   .230000  -.0766700      .000      .000  
+8812 3 47498.00 I  -.154162  .000691   .231337  .000160  I -.0781025  .0000321  1.9509 0.0257  I    -2.286     .572     2.025     .348  -.152100   .232900  -.0785900      .000      .000  
+8812 4 47499.00 I  -.155533  .000965   .234098  .000232  I -.0800263  .0000344  1.8842 0.0231  I    -2.072     .693     2.101     .414  -.153600   .235800  -.0804900      .000      .000  
+8812 5 47500.00 I  -.156686  .000931   .236928  .000273  I -.0818496  .0000333  1.7535 0.0221  I    -2.170     .693     2.024     .414  -.155100   .238700  -.0822900      .000      .000  
+8812 6 47501.00 I  -.157701  .000787   .239829  .000384  I -.0835181  .0000276  1.5773 0.0230  I    -2.503     .693     1.759     .414  -.156400   .241500  -.0839300      .000      .000  
+8812 7 47502.00 I  -.158659  .000318   .242801  .000505  I -.0849965  .0000316  1.3779 0.0191  I    -2.969     .498     1.422     .302  -.157700   .244400  -.0853800      .000      .000  
+8812 8 47503.00 I  -.159628  .000298   .245841  .000350  I -.0862774  .0000263  1.1888 0.0234  I    -3.490     .131     1.180     .103  -.158900   .247200  -.0866400      .000      .000  
+8812 9 47504.00 I  -.160621  .000298   .248931  .000350  I -.0873914  .0000346  1.0516 0.0221  I    -3.964     .318     1.131     .298  -.160000   .250100  -.0877300      .000      .000  
+881210 47505.00 I  -.161634  .000298   .252042  .000350  I -.0884065  .0000355  0.9921 0.0241  I    -4.273     .318     1.279     .298  -.160900   .253000  -.0887000      .000      .000  
+881211 47506.00 I  -.162667  .000200   .255108  .000371  I -.0894034  .0000335  1.0161 0.0237  I    -4.278     .318     1.485     .298  -.161700   .255900  -.0896700      .000      .000  
+881212 47507.00 I  -.163707  .000493   .258085  .000365  I -.0904717  .0000315  1.1392 0.0231  I    -3.896     .397     1.577     .298  -.162400   .258800  -.0907500      .000      .000  
+881213 47508.00 I  -.164702  .000577   .260986  .000266  I -.0917109  .0000319  1.3496 0.0223  I    -3.323     .442     1.530     .298  -.163000   .261700  -.0920300      .000      .000  
+881214 47509.00 I  -.165583  .000577   .263835  .000266  I -.0931807  .0000317  1.5895 0.0220  I    -2.854     .442     1.438     .298  -.163500   .264600  -.0935700      .000      .000  
+881215 47510.00 I  -.166239  .000696   .266649  .000375  I -.0948778  .0000303  1.7932 0.0198  I    -2.721     .412     1.420     .298  -.164000   .267500  -.0953200      .000      .000  
+881216 47511.00 I  -.166572  .000586   .269463  .000337  I -.0967332  .0000238  1.8953 0.0181  I    -2.887     .412     1.446     .298  -.164400   .270400  -.0972300      .000      .000  
+881217 47512.00 I  -.166652  .000594   .272299  .000294  I -.0986221  .0000199  1.8584 0.0186  I    -3.226     .394     1.524     .182  -.164700   .273300  -.0991700      .000      .000  
+881218 47513.00 I  -.166579  .000652   .275166  .000209  I -.1004058  .0000285  1.6888 0.0157  I    -3.616     .294     1.717     .254  -.164900   .276200  -.1009900      .000      .000  
+881219 47514.00 I  -.166451  .000447   .278072  .000218  I -.1019672  .0000243  1.4218 0.0187  I    -3.893     .330     2.052     .282  -.165000   .279100  -.1025800      .000      .000  
+881220 47515.00 I  -.166364  .000446   .281028  .000300  I -.1032387  .0000242  1.1227 0.0149  I    -3.901     .330     2.485     .282  -.165000   .282000  -.1038800      .000      .000  
+881221 47516.00 I  -.166345  .000389   .284038  .000337  I -.1042255  .0000174  0.8624 0.0149  I    -3.670     .449     2.594     .222  -.165000   .284900  -.1049000      .000      .000  
+881222 47517.00 I  -.166322  .000389   .287090  .000337  I -.1049930  .0000175  0.6907 0.0136  I    -3.282     .449     2.315     .222  -.164800   .287700  -.1057000      .000      .000  
+881223 47518.00 I  -.166208  .000474   .290178  .000429  I -.1056444  .0000210  0.6314 0.0183  I    -2.825     .497     1.921     .186  -.164600   .290600  -.1063800      .000      .000  
+881224 47519.00 I  -.165936  .000322   .293325  .000429  I -.1062911  .0000321  0.6782 0.0192  I    -2.395     .582     1.664     .298  -.164300   .293500  -.1070400      .000      .000  
+881225 47520.00 I  -.165480  .000322   .296533  .000429  I -.1070274  .0000322  0.8050 0.0236  I    -2.047     .582     1.631     .298  -.163900   .296400  -.1078000      .000      .000  
+881226 47521.00 I  -.164879  .000247   .299756  .000537  I -.1079153  .0000346  0.9743 0.0258  I    -1.787     .582     1.731     .298  -.163400   .299300  -.1087200      .000      .000  
+881227 47522.00 I  -.164185  .000250   .302943  .000570  I -.1089782  .0000404  1.1501 0.0291  I    -1.611     .182     1.817     .217  -.162800   .302200  -.1098000      .000      .000  
+881228 47523.00 I  -.163448  .000364   .306042  .000601  I -.1102095  .0000467  1.3079 0.0353  I    -1.541     .182     1.788     .217  -.162200   .305100  -.1110300      .000      .000  
+881229 47524.00 I  -.162718  .000364   .309000  .000601  I -.1115825  .0000578  1.4312 0.0318  I    -1.610     .182     1.679     .217  -.161500   .308000  -.1123800      .000      .000  
+881230 47525.00 I  -.162014  .000364   .311771  .000601  I -.1130535  .0000432  1.4993 0.0339  I    -1.815     .182     1.629     .217  -.160700   .310800  -.1138000      .000      .000  
+881231 47526.00 I  -.161329  .000386   .314379  .000510  I -.1145574  .0000356  1.4965 0.0274  I    -2.118     .182     1.714     .217  -.159900   .313700  -.1152300      .000      .000  
+89 1 1 47527.00 I  -.160623  .000365   .316868  .000435  I -.1160242  .0000337  1.4270 0.0270  I    -2.471     .203     1.844     .154  -.159100   .316500  -.1166000      .000      .000  
+89 1 2 47528.00 I  -.159834  .000411   .319279  .000120  I -.1173940  .0000405  1.3055 0.0267  I    -2.806     .222     1.872     .298  -.158200   .319200  -.1178600      .000      .000  
+89 1 3 47529.00 I  -.158899  .000368   .321651  .000104  I -.1186247  .0000415  1.1525 0.0245  I    -3.020     .222     1.781     .298  -.157300   .322000  -.1189700    -1.200      .700  
+89 1 4 47530.00 I  -.157777  .000368   .324021  .000104  I -.1196954  .0000277  0.9885 0.0245  I    -3.061     .222     1.702     .298  -.156100   .324500  -.1200400    -1.200      .600  
+89 1 5 47531.00 I  -.156514  .000324   .326406  .000149  I -.1206076  .0000262  0.8421 0.0200  I    -3.009     .222     1.731     .298  -.154900   .327000  -.1209400    -1.300      .500  
+89 1 6 47532.00 I  -.155183  .000292   .328817  .000139  I -.1213994  .0000288  0.7552 0.0233  I    -2.991     .184     1.814     .298  -.153700   .329500  -.1217400    -1.500      .400  
+89 1 7 47533.00 I  -.153835  .000396   .331261  .000158  I -.1221507  .0000386  0.7663 0.0237  I    -3.001     .136     1.855     .298  -.152500   .332000  -.1225100    -1.600      .300  
+89 1 8 47534.00 I  -.152507  .000396   .333745  .000158  I -.1229689  .0000377  0.8879 0.0268  I    -2.909     .136     1.834     .298  -.151300   .334600  -.1233600    -1.700      .200  
+89 1 9 47535.00 I  -.151241  .000398   .336273  .000176  I -.1239605  .0000372  1.1113 0.0246  I    -2.668     .136     1.789     .298  -.150200   .337300  -.1243300    -1.800      .300  
+89 110 47536.00 I  -.150107  .000336   .338829  .000194  I -.1252112  .0000316  1.3937 0.0221  I    -2.437     .136     1.734     .298  -.149300   .340000  -.1255800    -1.900      .300  
+89 111 47537.00 I  -.149139  .000263   .341389  .000145  I -.1267405  .0000238  1.6537 0.0188  I    -2.425     .387     1.635     .130  -.148400   .342700  -.1271300    -2.100      .400  
+89 112 47538.00 I  -.148319  .000302   .343925  .000138  I -.1284866  .0000202  1.8175 0.0150  I    -2.674     .488     1.500     .155  -.147600   .345300  -.1288800    -2.100      .600  
+89 113 47539.00 I  -.147620  .000155   .346411  .000139  I -.1303312  .0000181  1.8484 0.0135  I    -3.060     .488     1.435     .155  -.146800   .347900  -.1307300    -2.200      .700  
+89 114 47540.00 I  -.146987  .000155   .348832  .000139  I -.1321353  .0000180  1.7352 0.0122  I    -3.433     .488     1.548     .155  -.146000   .350300  -.1325300    -2.100      .800  
+89 115 47541.00 I  -.146334  .000134   .351191  .000157  I -.1337636  .0000165  1.5081 0.0116  I    -3.619     .488     1.685     .155  -.145100   .352700  -.1341600    -2.000      .800  
+89 116 47542.00 I  -.145597  .000165   .353501  .000181  I -.1351367  .0000147  1.2366 0.0109  I    -3.626     .514     1.772     .144  -.144200   .355100  -.1355200    -1.900      .900  
+89 117 47543.00 I  -.144734  .000187   .355784  .000173  I -.1362438  .0000141  0.9859 0.0115  I    -3.488     .549     1.762     .127  -.143300   .357400  -.1366300    -1.800     1.000  
+89 118 47544.00 I  -.143704  .000202   .358063  .000271  I -.1371317  .0000178  0.8046 0.0114  I    -3.220     .549     1.622     .127  -.142300   .359600  -.1375100    -1.700     1.000  
+89 119 47545.00 I  -.142476  .000202   .360360  .000271  I -.1378875  .0000178  0.7267 0.0128  I    -2.864     .549     1.380     .127  -.141100   .361800  -.1382600    -1.700     1.000  
+89 120 47546.00 I  -.141091  .000279   .362676  .000407  I -.1386235  .0000183  0.7638 0.0124  I    -2.500     .549     1.145     .127  -.140000   .364000  -.1389800    -1.700     1.000  
+89 121 47547.00 I  -.139636  .000437   .364995  .000361  I -.1394478  .0000172  0.8989 0.0147  I    -2.255     .428     1.065     .109  -.138700   .366200  -.1397900    -1.700     1.000  
+89 122 47548.00 I  -.138188  .000567   .367291  .000392  I -.1404436  .0000230  1.1008 0.0132  I    -2.205     .262     1.229     .109  -.137500   .368300  -.1407900    -1.700      .900  
+89 123 47549.00 I  -.136813  .000567   .369529  .000392  I -.1416598  .0000201  1.3338 0.0150  I    -2.270     .262     1.593     .109  -.136300   .370400  -.1419900    -1.800      .800  
+89 124 47550.00 I  -.135556  .000602   .371683  .000398  I -.1431067  .0000193  1.5532 0.0136  I    -2.289     .262     1.987     .109  -.135100   .372400  -.1435100    -2.100      .700  
+89 125 47551.00 I  -.134376  .000558   .373769  .000422  I -.1447478  .0000183  1.7173 0.0135  I    -2.170     .262     2.095     .109  -.134000   .374400  -.1452100    -2.400      .600  
+89 126 47552.00 I  -.133268  .000519   .375796  .000397  I -.1465163  .0000190  1.8063 0.0213  I    -2.029     .640     1.896     .121  -.132900   .376400  -.1470300    -2.600      .500  
+89 127 47553.00 I  -.132239  .000635   .377769  .000385  I -.1483335  .0000385  1.8148 0.0205  I    -2.004     .802     1.627     .141  -.131800   .378400  -.1488800    -2.900      .400  
+89 128 47554.00 I  -.131292  .000326   .379698  .000354  I -.1501200  .0000364  1.7458 0.0265  I    -2.144     .955     1.468     .137  -.130800   .380300  -.1507100    -3.000      .300  
+89 129 47555.00 I  -.130378  .000326   .381620  .000354  I -.1518051  .0000365  1.6168 0.0245  I    -2.479     .955     1.417     .137  -.129800   .382400  -.1523400    -2.700      .300  
+89 130 47556.00 I  -.129424  .000460   .383583  .000421  I -.1533415  .0000327  1.4513 0.0246  I    -2.938     .955     1.312     .137  -.128800   .384500  -.1538200    -2.300      .400  
+89 131 47557.00 I  -.128349  .000378   .385633  .000381  I -.1547032  .0000331  1.2721 0.0279  I    -3.355     .841     1.124     .145  -.127700   .386700  -.1551400    -1.900      .500  
+89 2 1 47558.00 I  -.127087  .000460   .387818  .000425  I -.1558913  .0000452  1.1096 0.0245  I    -3.579     .594      .996     .179  -.126500   .388900  -.1562700    -1.400      .600  
+89 2 2 47559.00 I  -.125585  .000454   .390188  .000450  I -.1569400  .0000362  0.9997 0.0300  I    -3.623     .594      .965     .179  -.125100   .391200  -.1572800    -1.000      .700  
+89 2 3 47560.00 I  -.123832  .000478   .392773  .000479  I -.1579212  .0000396  0.9811 0.0236  I    -3.594     .594      .840     .179  -.123500   .393600  -.1583500     -.300     1.000  
+89 2 4 47561.00 I  -.121911  .000425   .395525  .000435  I -.1589440  .0000304  1.0879 0.0239  I    -3.554     .594      .681     .179  -.121600   .396200  -.1593900      .000     1.100  
+89 2 5 47562.00 I  -.119903  .000220   .398354  .000387  I -.1601397  .0000266  1.3230 0.0201  I    -3.323     .564      .617     .211  -.119600   .398900  -.1606200      .200     1.100  
+89 2 6 47563.00 I  -.117871  .000194   .401156  .000348  I -.1616185  .0000264  1.6437 0.0187  I    -2.796     .480      .756     .254  -.117500   .401600  -.1621400      .400     1.100  
+89 2 7 47564.00 I  -.115849  .000189   .403803  .000379  I -.1634295  .0000263  1.9716 0.0185  I    -2.277     .541     1.069     .260  -.115300   .404100  -.1639800      .300     1.100  
+89 2 8 47565.00 I  -.113860  .000143   .406177  .000366  I -.1655334  .0000258  2.2145 0.0179  I    -2.200     .541     1.304     .260  -.113200   .406500  -.1661000     -.200      .900  
+89 2 9 47566.00 I  -.111876  .000141   .408262  .000366  I -.1678033  .0000244  2.2917 0.0172  I    -2.483     .481     1.477     .284  -.111000   .408700  -.1683600     -.800      .700  
+89 210 47567.00 I  -.109852  .000261   .410131  .000349  I -.1700525  .0000228  2.1760 0.0205  I    -2.843     .486     1.548     .252  -.108800   .410800  -.1705800    -1.500      .500  
+89 211 47568.00 I  -.107744  .000334   .411869  .000450  I -.1721051  .0000329  1.9096 0.0179  I    -3.115     .469     1.371     .253  -.106600   .412700  -.1726000    -2.200      .300  
+89 212 47569.00 I  -.105503  .000409   .413566  .000610  I -.1738461  .0000277  1.5664 0.0217  I    -3.280     .424      .854     .243  -.104300   .414600  -.1743200    -2.800      .100  
+89 213 47570.00 I  -.103089  .000409   .415308  .000610  I -.1752415  .0000284  1.2341 0.0205  I    -3.317     .424      .336     .243  -.101800   .416200  -.1757100    -2.800     -.100  
+89 214 47571.00 I  -.100531  .000487   .417146  .000627  I -.1763413  .0000303  0.9828 0.0195  I    -3.205     .424     -.036     .243  -.099200   .417700  -.1768200    -2.800     -.200  
+89 215 47572.00 I  -.097904  .000451   .419098  .000474  I -.1772436  .0000266  0.8410 0.0216  I    -2.952     .938     -.259     .166  -.096700   .419300  -.1777600    -2.700     -.300  
+89 216 47573.00 I  -.095312  .000372   .421131  .000421  I -.1780595  .0000308  0.8079 0.0268  I    -2.609    1.064     -.320     .174  -.094100   .420900  -.1786100    -2.600     -.300  
+89 217 47574.00 I  -.092857  .000366   .423210  .000338  I -.1788889  .0000466  0.8634 0.0276  I    -2.269    1.044     -.242     .160  -.091600   .422600  -.1794900    -2.500     -.400  
+89 218 47575.00 I  -.090612  .000607   .425283  .000405  I -.1798109  .0000458  0.9927 0.0313  I    -2.044    1.044     -.055     .160  -.089600   .425000  -.1803500    -2.500     -.300  
+89 219 47576.00 I  -.088536  .000571   .427269  .000399  I -.1808903  .0000417  1.1699 0.0300  I    -2.020    1.039      .113     .145  -.087600   .427400  -.1813500    -2.600     -.200  
+89 220 47577.00 I  -.086538  .000559   .429119  .000360  I -.1821532  .0000387  1.3547 0.0291  I    -1.960    1.020      .328     .125  -.085500   .429900  -.1825200    -2.800     -.100  
+89 221 47578.00 I  -.084432  .000574   .430932  .000390  I -.1835922  .0000405  1.5169 0.0282  I    -1.794     .919      .592     .110  -.083400   .432500  -.1838500    -2.900     -.100  
+89 222 47579.00 I  -.082059  .000620   .432797  .000432  I -.1851673  .0000409  1.6200 0.0267  I    -1.752     .985      .733     .298  -.081100   .435000  -.1852900    -3.000      .000  
+89 223 47580.00 I  -.079410  .000759   .434757  .000603  I -.1868075  .0000349  1.6490 0.0269  I    -1.737     .988      .631     .298  -.078000   .436900  -.1870700    -3.000     -.200  
+89 224 47581.00 I  -.076519  .000777   .436825  .000420  I -.1884434  .0000350  1.6123 0.0223  I    -1.716     .988      .394     .298  -.074800   .438700  -.1888600    -3.100     -.400  
+89 225 47582.00 I  -.073472  .000378   .438984  .000437  I -.1900135  .0000277  1.5199 0.0262  I    -1.682     .791      .190     .298  -.071500   .440400  -.1905900    -3.100     -.600  
+89 226 47583.00 I  -.070384  .000611   .441200  .000513  I -.1914703  .0000389  1.3889 0.0243  I    -1.614     .791      .022     .112  -.068200   .442200  -.1922000    -3.000     -.800  
+89 227 47584.00 I  -.067355  .000590   .443424  .000469  I -.1927852  .0000399  1.2396 0.0277  I    -1.562     .791     -.203     .112  -.065000   .444000  -.1936400    -2.900    -1.000  
+89 228 47585.00 I  -.064462  .000590   .445590  .000469  I -.1939514  .0000394  1.0958 0.0256  I    -1.565     .791     -.471     .112  -.062400   .446200  -.1946700    -2.700    -1.100  
+89 3 1 47586.00 I  -.061777  .000496   .447654  .000341  I -.1949889  .0000320  0.9881 0.0231  I    -1.585     .791     -.744     .112  -.060000   .448300  -.1955700    -2.500    -1.100  
+89 3 2 47587.00 I  -.059322  .000503   .449590  .000237  I -.1959522  .0000240  0.9541 0.0207  I    -1.712     .503     -.899     .125  -.057700   .450300  -.1963900    -2.200    -1.200  
+89 3 3 47588.00 I  -.057032  .000527   .451392  .000183  I -.1969334  .0000264  1.0290 0.0171  I    -2.125     .542    -1.004     .136  -.055600   .452100  -.1972600    -2.100    -1.200  
+89 3 4 47589.00 I  -.054827  .000415   .453056  .000124  I -.1980522  .0000244  1.2297 0.0186  I    -2.779     .622    -1.219     .133  -.053500   .453900  -.1983000    -1.900    -1.100  
+89 3 5 47590.00 I  -.052640  .000405   .454584  .000118  I -.1994315  .0000261  1.5463 0.0170  I    -3.303     .622    -1.423     .133  -.051500   .455300  -.1997600    -2.200    -1.000  
+89 3 6 47591.00 I  -.050464  .000372   .455997  .000143  I -.2011648  .0000236  1.9229 0.0180  I    -3.291     .622    -1.420     .133  -.049300   .456600  -.2015700    -2.400     -.800  
+89 3 7 47592.00 I  -.048237  .000456   .457318  .000232  I -.2032656  .0000248  2.2643 0.0183  I    -2.920     .500    -1.089     .104  -.047100   .457700  -.2037500    -2.600     -.700  
+89 3 8 47593.00 I  -.045879  .000485   .458566  .000252  I -.2056514  .0000281  2.4796 0.0210  I    -2.722     .288     -.613     .298  -.044800   .458800  -.2061700    -2.800     -.500  
+89 3 9 47594.00 I  -.043333  .000470   .459762  .000327  I -.2081660  .0000340  2.5184 0.0214  I    -2.909     .220     -.340     .298  -.042400   .459900  -.2087000    -2.900     -.500  
+89 310 47595.00 I  -.040559  .000470   .460928  .000327  I -.2106262  .0000322  2.3713 0.0282  I    -3.250     .220     -.371     .298  -.039700   .461200  -.2111500    -2.600     -.700  
+89 311 47596.00 I  -.037601  .000500   .462108  .000353  I -.2128614  .0000451  2.0825 0.0277  I    -3.438     .220     -.559     .298  -.037000   .462600  -.2133400    -2.300    -1.000  
+89 312 47597.00 I  -.034537  .000500   .463328  .000353  I -.2147751  .0000451  1.7454 0.0434  I    -3.346     .254     -.792     .298  -.034100   .464000  -.2151900    -2.000    -1.400  
+89 313 47598.00 I  -.031444  .000485   .464609  .000445  I -.2163651  .0000741  1.4469 0.0431  I    -2.976     .284    -1.099     .298  -.031100   .465500  -.2167300    -1.600    -1.700  
+89 314 47599.00 I  -.028399  .000485   .465972  .000445  I -.2176977  .0000734  1.2348 0.0525  I    -2.402     .284    -1.481     .298  -.028100   .467000  -.2180500    -1.300    -2.100  
+89 315 47600.00 I  -.025444  .000485   .467418  .000445  I -.2188749  .0000743  1.1432 0.0467  I    -1.825     .284    -1.792     .298  -.025000   .468400  -.2192200    -1.200    -2.300  
+89 316 47601.00 I  -.022537  .000373   .468923  .000478  I -.2200257  .0000577  1.1762 0.0447  I    -1.467     .284    -1.894     .298  -.021800   .469800  -.2204100    -1.100    -2.500  
+89 317 47602.00 I  -.019637  .000359   .470447  .000491  I -.2212566  .0000497  1.2969 0.0349  I    -1.370     .300    -1.811     .298  -.018700   .471100  -.2217300    -1.100    -2.700  
+89 318 47603.00 I  -.016728  .000253   .471924  .000525  I -.2226366  .0000394  1.4688 0.0312  I    -1.411     .310    -1.674     .108  -.015600   .472300  -.2232000    -1.100    -2.800  
+89 319 47604.00 I  -.013793  .000195   .473291  .000504  I -.2242018  .0000377  1.6636 0.0276  I    -1.450     .310    -1.500     .108  -.012500   .473500  -.2248600    -1.100    -2.800  
+89 320 47605.00 I  -.010811  .000180   .474489  .000487  I -.2259581  .0000386  1.8412 0.0253  I    -1.426     .310    -1.205     .108  -.009500   .474800  -.2265900    -1.100    -2.700  
+89 321 47606.00 I  -.007772  .000178   .475523  .000436  I -.2278645  .0000336  1.9597 0.0228  I    -1.333     .310     -.837     .108  -.006400   .475900  -.2284400    -1.100    -2.500  
+89 322 47607.00 I  -.004680  .000169   .476423  .000394  I -.2298524  .0000244  2.0030 0.0212  I    -1.191     .429     -.622     .214  -.003400   .476900  -.2303600    -1.100    -2.400  
+89 323 47608.00 I  -.001536  .000165   .477221  .000210  I -.2318442  .0000258  1.9673 0.0256  I    -1.062     .560     -.755     .306  -.000400   .477800  -.2322500    -1.000    -2.200  
+89 324 47609.00 I   .001662  .000586   .477931  .000339  I -.2337629  .0000449  1.8593 0.0239  I     -.992     .461    -1.208     .276   .002600   .478600  -.2340800     -.800    -2.100  
+89 325 47610.00 I   .004910  .000567   .478562  .000360  I -.2355462  .0000402  1.7015 0.0294  I     -.932     .461    -1.799     .276   .005700   .479300  -.2359200     -.400    -2.200  
+89 326 47611.00 I   .008163  .000514   .479117  .000437  I -.2371585  .0000379  1.5214 0.0269  I     -.817     .402    -2.283     .244   .008700   .479800  -.2375800      .100    -2.300  
+89 327 47612.00 I   .011358  .000460   .479605  .000433  I -.2385892  .0000357  1.3419 0.0301  I     -.676     .723    -2.505     .230   .011700   .480300  -.2390200      .500    -2.500  
+89 328 47613.00 I   .014456  .000563   .480025  .000526  I -.2398493  .0000469  1.1833 0.0284  I     -.589     .715    -2.497     .176   .014800   .480700  -.2402700      .900    -2.700  
+89 329 47614.00 I   .017512  .000597   .480344  .000558  I -.2409700  .0000441  1.0672 0.0275  I     -.601     .715    -2.379     .176   .017900   .481100  -.2413800     1.300    -2.900  
+89 330 47615.00 I   .020572  .000597   .480580  .000558  I -.2420101  .0000289  1.0303 0.0289  I     -.747     .719    -2.296     .157   .021100   .481400  -.2424700     1.400    -2.900  
+89 331 47616.00 I   .023676  .000384   .480793  .000506  I -.2430699  .0000373  1.1111 0.0228  I    -1.083     .719    -2.205     .157   .024400   .481700  -.2436200     1.300    -2.900  
+89 4 1 47617.00 I   .026828  .000419   .481018  .000408  I -.2442733  .0000354  1.3147 0.0267  I    -1.483     .662    -2.227     .146   .027700   .481900  -.2449400     1.100    -2.800  
+89 4 2 47618.00 I   .030002  .000464   .481275  .000377  I -.2457298  .0000383  1.6096 0.0257  I    -1.605     .181    -2.406     .104   .031100   .482200  -.2465400      .900    -2.800  
+89 4 3 47619.00 I   .033252  .000505   .481540  .000405  I -.2475001  .0000374  1.9273 0.0268  I    -1.274     .190    -2.457     .298   .034500   .482400  -.2484400      .500    -2.700  
+89 4 4 47620.00 I   .036645  .000445   .481775  .000404  I -.2495596  .0000376  2.1704 0.0258  I     -.737     .190    -2.163     .298   .037900   .482600  -.2503600     -.400    -2.600  
+89 4 5 47621.00 I   .040156  .000333   .481968  .000204  I -.2517880  .0000356  2.2546 0.0203  I     -.446     .191    -1.693     .298   .041300   .482700  -.2524200    -1.100    -2.700  
+89 4 6 47622.00 I   .043695  .000482   .482123  .000244  I -.2540059  .0000153  2.1501 0.0210  I     -.585     .148    -1.429     .165   .044800   .482900  -.2544400    -1.700    -2.900  
+89 4 7 47623.00 I   .047168  .000609   .482245  .000303  I -.2560372  .0000224  1.8927 0.0138  I     -.953     .791    -1.531     .221   .048200   .483000  -.2563000    -2.200    -3.000  
+89 4 8 47624.00 I   .050497  .000609   .482334  .000303  I -.2577692  .0000230  1.5690 0.0162  I    -1.275     .791    -1.831     .221   .051600   .483100  -.2578500    -2.500    -3.200  
+89 4 9 47625.00 I   .053617  .000609   .482389  .000303  I -.2591858  .0000235  1.2772 0.0134  I    -1.416     .791    -2.156     .221   .054700   .483200  -.2594100    -2.200    -3.300  
+89 410 47626.00 I   .056542  .000461   .482418  .000289  I -.2603588  .0000136  1.0909 0.0127  I    -1.283     .791    -2.540     .221   .057800   .483200  -.2607300    -1.600    -3.400  
+89 411 47627.00 I   .059357  .000405   .482421  .000265  I -.2614125  .0000097  1.0392 0.0084  I     -.880     .190    -2.963     .255   .060900   .483200  -.2619200    -1.000    -3.400  
+89 412 47628.00 I   .062146  .000201   .482398  .000212  I -.2624770  .0000098  1.1070 0.0075  I     -.458     .255    -3.231     .285   .063900   .483200  -.2630900     -.400    -3.500  
+89 413 47629.00 I   .064996  .000201   .482348  .000212  I -.2636536  .0000114  1.2560 0.0116  I     -.354     .255    -3.152     .285   .066900   .483200  -.2643500      .200    -3.500  
+89 414 47630.00 I   .067974  .000201   .482275  .000212  I -.2650022  .0000211  1.4445 0.0108  I     -.609     .255    -2.809     .285   .069900   .483100  -.2656000      .400    -3.400  
+89 415 47631.00 I   .071056  .000176   .482182  .000209  I -.2665444  .0000184  1.6384 0.0134  I     -.926     .255    -2.470     .285   .072900   .483000  -.2670200      .600    -3.300  
+89 416 47632.00 I   .074175  .000259   .482084  .000278  I -.2682722  .0000165  1.8121 0.0152  I    -1.006     .190    -2.277     .211   .075800   .482900  -.2686400      .600    -3.100  
+89 417 47633.00 I   .077261  .000278   .481993  .000300  I -.2701558  .0000243  1.9476 0.0159  I     -.847     .791    -2.136     .134   .078700   .482800  -.2704300      .600    -3.000  
+89 418 47634.00 I   .080242  .000269   .481921  .000346  I -.2721509  .0000273  2.0338 0.0214  I     -.624     .791    -1.949     .134   .081400   .482800  -.2723800      .600    -2.900  
+89 419 47635.00 I   .083051  .000384   .481881  .000391  I -.2742048  .0000352  2.0643 0.0197  I     -.455     .791    -1.834     .120   .084000   .482900  -.2745500      .500    -2.900  
+89 420 47636.00 I   .085654  .000373   .481862  .000449  I -.2762618  .0000284  2.0416 0.0231  I     -.352     .298    -2.123     .166   .086500   .483000  -.2767500      .500    -2.900  
+89 421 47637.00 I   .088070  .000404   .481819  .000480  I -.2782747  .0000298  1.9789 0.0235  I     -.307     .380    -2.737     .251   .088800   .483100  -.2789200      .400    -2.900  
+89 422 47638.00 I   .090333  .000398   .481716  .000490  I -.2802104  .0000375  1.8886 0.0246  I     -.262     .437    -3.226     .289   .091000   .483200  -.2809800      .400    -2.900  
+89 423 47639.00 I   .092486  .000423   .481533  .000502  I -.2820442  .0000391  1.7750 0.0272  I     -.105     .445    -3.333     .297   .093100   .483100  -.2829000      .400    -2.900  
+89 424 47640.00 I   .094494  .000437   .481236  .000498  I -.2837585  .0000393  1.6555 0.0259  I     -.029     .445    -3.419     .297   .095200   .482700  -.2844400      .400    -2.900  
+89 425 47641.00 I   .096371  .000412   .480834  .000530  I -.2853637  .0000339  1.5616 0.0224  I     -.132     .479    -3.434     .324   .097200   .482100  -.2858300      .400    -2.900  
+89 426 47642.00 I   .098163  .000289   .480347  .000502  I -.2869023  .0000217  1.5297 0.0208  I     -.305     .437    -3.219     .297   .099200   .481400  -.2871600      .400    -2.800  
+89 427 47643.00 I   .099938  .000233   .479781  .000386  I -.2884530  .0000240  1.5876 0.0161  I     -.448     .397    -2.993     .253   .101100   .480700  -.2885100      .300    -2.800  
+89 428 47644.00 I   .101770  .000233   .479145  .000443  I -.2901074  .0000239  1.7353 0.0169  I     -.460     .397    -3.073     .253   .103100   .479800  -.2899900      .100    -2.800  
+89 429 47645.00 I   .103681  .000292   .478475  .000583  I -.2919493  .0000237  1.9599 0.0158  I     -.417     .441    -3.312     .249   .104900   .479400  -.2919500     -.500    -2.700  
+89 430 47646.00 I   .105679  .000266   .477783  .000577  I -.2940401  .0000207  2.2230 0.0150  I     -.329     .441    -3.531     .249   .106900   .478800  -.2942000    -1.200    -2.600  
+89 5 1 47647.00 I   .107778  .000283   .477063  .000565  I -.2963841  .0000183  2.4525 0.0161  I     -.221     .393    -3.544     .198   .109000   .478300  -.2966900    -1.900    -2.600  
+89 5 2 47648.00 I   .110015  .000376   .476300  .000687  I -.2989080  .0000247  2.5708 0.0136  I     -.259     .347    -3.226     .193   .111300   .477700  -.2993400    -2.500    -2.600  
+89 5 3 47649.00 I   .112428  .000345   .475470  .000721  I -.3014717  .0000202  2.5272 0.0160  I     -.592     .347    -2.738     .193   .113600   .477000  -.3020200    -3.000    -2.600  
+89 5 4 47650.00 I   .115060  .000345   .474545  .000721  I -.3039073  .0000202  2.3184 0.0127  I    -1.131     .347    -2.414     .193   .116100   .476000  -.3044400    -2.900    -3.000  
+89 5 5 47651.00 I   .117874  .000304   .473559  .000677  I -.3060707  .0000154  1.9962 0.0133  I    -1.632     .347    -2.428     .193   .118800   .474900  -.3065900    -2.500    -3.300  
+89 5 6 47652.00 I   .120803  .000311   .472567  .000522  I -.3078923  .0000173  1.6520 0.0138  I    -1.911     .451    -2.665     .157   .121600   .473800  -.3084100    -2.000    -3.600  
+89 5 7 47653.00 I   .123774  .000281   .471615  .000447  I -.3093978  .0000230  1.3773 0.0144  I    -1.927     .527    -2.937     .298   .124400   .472800  -.3099400    -1.500    -3.900  
+89 5 8 47654.00 I   .126711  .000281   .470731  .000447  I -.3106890  .0000229  1.2290 0.0162  I    -1.697     .527    -3.193     .298   .127300   .471800  -.3112700    -1.100    -4.100  
+89 5 9 47655.00 I   .129543  .000300   .469925  .000431  I -.3119010  .0000227  1.2162 0.0145  I    -1.315     .527    -3.433     .298   .130200   .470900  -.3124200    -1.000    -4.100  
+89 510 47656.00 I   .132250  .000245   .469156  .000410  I -.3131560  .0000177  1.3072 0.0162  I     -.908     .527    -3.552     .298   .133100   .470000  -.3136000    -1.000    -3.900  
+89 511 47657.00 I   .134873  .000264   .468363  .000358  I -.3145339  .0000232  1.4539 0.0163  I     -.807     .392    -3.416     .112   .135900   .469100  -.3148900    -1.000    -3.800  
+89 512 47658.00 I   .137457  .000232   .467478  .000276  I -.3160676  .0000273  1.6122 0.0209  I    -1.149     .293    -3.086     .113   .138700   .468100  -.3163400    -1.100    -3.600  
+89 513 47659.00 I   .140051  .000284   .466439  .000383  I -.3177515  .0000348  1.7503 0.0232  I    -1.591     .172    -2.817     .128   .141500   .467000  -.3179100    -1.300    -3.400  
+89 514 47660.00 I   .142692  .000284   .465202  .000383  I -.3195541  .0000374  1.8463 0.0250  I    -1.730     .172    -2.761     .128   .144100   .465800  -.3197900    -1.400    -3.400  
+89 515 47661.00 I   .145352  .000367   .463771  .000501  I -.3214257  .0000359  1.8873 0.0252  I    -1.542     .172    -2.817     .128   .146600   .464600  -.3217400    -1.600    -3.400  
+89 516 47662.00 I   .147974  .000432   .462165  .000483  I -.3233090  .0000337  1.8694 0.0288  I    -1.304     .138    -2.821     .103   .149000   .463200  -.3236900    -1.800    -3.400  
+89 517 47663.00 I   .150536  .000574   .460428  .000628  I -.3251465  .0000451  1.7977 0.0290  I    -1.217     .791    -2.794     .298   .151400   .461700  -.3255900    -1.900    -3.500  
+89 518 47664.00 I   .153032  .000574   .458624  .000580  I -.3268908  .0000471  1.6854 0.0304  I    -1.289     .791    -2.880     .298   .153700   .460100  -.3274000    -1.900    -3.500  
+89 519 47665.00 I   .155458  .000576   .456813  .000554  I -.3285086  .0000409  1.5470 0.0290  I    -1.474     .791    -3.109     .298   .156000   .458200  -.3290600    -1.800    -3.600  
+89 520 47666.00 I   .157792  .000511   .455046  .000534  I -.3299836  .0000337  1.4048 0.0250  I    -1.742     .791    -3.325     .298   .158100   .456200  -.3305900    -1.500    -3.800  
+89 521 47667.00 I   .159992  .000470   .453368  .000576  I -.3313262  .0000289  1.2864 0.0219  I    -2.029     .108    -3.351     .298   .160200   .454200  -.3320200    -1.300    -3.900  
+89 522 47668.00 I   .161991  .000454   .451794  .000549  I -.3325726  .0000281  1.2167 0.0202  I    -2.271     .161    -3.175     .298   .162200   .452200  -.3333900    -1.100    -3.900  
+89 523 47669.00 I   .163797  .000454   .450258  .000549  I -.3337839  .0000281  1.2200 0.0156  I    -2.461     .197    -2.953     .298   .164100   .450300  -.3347300    -1.000    -3.900  
+89 524 47670.00 I   .165449  .000383   .448686  .000565  I -.3350409  .0000134  1.3081 0.0158  I    -2.646     .197    -2.856     .298   .166000   .449200  -.3357500    -1.000    -3.600  
+89 525 47671.00 I   .167014  .000349   .447059  .000563  I -.3364270  .0000146  1.4767 0.0104  I    -2.844     .214    -2.924     .298   .167900   .448100  -.3368900    -1.200    -3.300  
+89 526 47672.00 I   .168630  .000359   .445410  .000531  I -.3380170  .0000158  1.7133 0.0154  I    -2.892     .378    -3.049     .298   .169700   .447100  -.3382400    -1.300    -3.000  
+89 527 47673.00 I   .170353  .000383   .443761  .000436  I -.3398651  .0000272  1.9842 0.0180  I    -2.671     .454    -3.159     .298   .171500   .446000  -.3398400    -1.600    -2.600  
+89 528 47674.00 I   .172213  .000408   .442138  .000471  I -.3419779  .0000323  2.2324 0.0233  I    -2.250     .505    -3.256     .298   .173300   .444800  -.3417200    -1.900    -2.300  
+89 529 47675.00 I   .174223  .000286   .440579  .000499  I -.3443007  .0000379  2.3930 0.0220  I    -1.815     .515    -3.331     .298   .175100   .442700  -.3443400    -2.000    -2.300  
+89 530 47676.00 I   .176326  .000321   .439081  .000507  I -.3467165  .0000299  2.4113 0.0233  I    -1.617     .515    -3.317     .298   .176900   .440500  -.3470700    -2.000    -2.400  
+89 531 47677.00 I   .178441  .000313   .437618  .000468  I -.3490693  .0000271  2.2675 0.0237  I    -1.819     .415    -3.206     .387   .178800   .438300  -.3496900    -2.000    -2.500  
+89 6 1 47678.00 I   .180503  .000351   .436141  .000435  I -.3512058  .0000369  1.9867 0.0197  I    -2.379     .258    -3.079     .458   .180600   .436100  -.3520600    -1.900    -2.700  
+89 6 2 47679.00 I   .182458  .000333   .434592  .000469  I -.3530186  .0000287  1.6338 0.0325  I    -3.020     .234    -3.004     .414   .182600   .434100  -.3540600    -1.700    -2.900  
+89 6 3 47680.00 I   .184277  .000333   .432933  .000469  I -.3544779  .0000535  1.2946 0.0275  I    -3.500     .349    -2.928     .387   .184700   .432400  -.3555200    -2.500    -4.000  
+89 6 4 47681.00 I   .185968  .000360   .431184  .000428  I -.3556373  .0000469  1.0439 0.0355  I    -3.764     .349    -2.961     .421   .186700   .431000  -.3567100    -1.800    -4.200  
+89 6 5 47682.00 I   .187552  .000324   .429384  .000373  I -.3566092  .0000467  0.9238 0.0430  I    -3.751     .312    -3.207     .357   .188800   .429700  -.3577100    -1.100    -4.200  
+89 6 6 47683.00 I   .189112  .000373   .427581  .000393  I -.3575300  .0000722  0.9391 0.0424  I    -3.297     .331    -3.377     .150   .190900   .428500  -.3586700     -.400    -4.300  
+89 6 7 47684.00 I   .190740  .000373   .425782  .000393  I -.3585215  .0000709  1.0567 0.0487  I    -2.656     .341    -3.340     .117   .192900   .427200  -.3597100      .100    -4.300  
+89 6 8 47685.00 I   .192506  .000445   .423983  .000278  I -.3596579  .0000655  1.2179 0.0423  I    -2.366     .378    -3.131     .128   .195000   .425500  -.3608800     -.100    -4.100  
+89 6 9 47686.00 I   .194400  .000407   .422163  .000179  I -.3609551  .0000462  1.3724 0.0388  I    -2.653     .175    -2.815     .105   .197000   .423800  -.3621600     -.400    -3.900  
+89 610 47687.00 I   .196393  .000386   .420297  .000164  I -.3623900  .0000417  1.4889 0.0333  I    -3.246     .258    -2.562     .105   .199000   .421900  -.3635400     -.900    -3.600  
+89 611 47688.00 I   .198458  .000498   .418355  .000136  I -.3639148  .0000479  1.5512 0.0265  I    -3.702     .285    -2.517     .298   .201000   .420000  -.3649900    -1.500    -3.500  
+89 612 47689.00 I   .200570  .000498   .416317  .000136  I -.3654695  .0000326  1.5447 0.0295  I    -3.868     .307    -2.623     .298   .202900   .417900  -.3664500    -2.000    -3.300  
+89 613 47690.00 I   .202666  .000498   .414182  .000136  I -.3669805  .0000345  1.4669 0.0226  I    -3.926     .307    -2.667     .298   .204700   .415500  -.3678700    -2.500    -3.400  
+89 614 47691.00 I   .204677  .000403   .411958  .000103  I -.3683846  .0000314  1.3330 0.0189  I    -4.068     .307    -2.620     .298   .206400   .413100  -.3691700    -2.800    -3.600  
+89 615 47692.00 I   .206570  .000362   .409656  .000106  I -.3696329  .0000155  1.1583 0.0213  I    -4.307     .273    -2.620     .298   .208100   .410600  -.3703100    -3.200    -3.800  
+89 616 47693.00 I   .208381  .000459   .407288  .000128  I -.3706947  .0000288  0.9642 0.0162  I    -4.571     .114    -2.736     .298   .209700   .408000  -.3712700    -3.500    -4.000  
+89 617 47694.00 I   .210155  .000118   .404868  .000141  I -.3715637  .0000284  0.7772 0.0200  I    -4.813     .114    -2.873     .298   .211400   .405400  -.3720900    -3.800    -4.200  
+89 618 47695.00 I   .211929  .000143   .402408  .000148  I -.3722632  .0000277  0.6324 0.0188  I    -5.008     .114    -2.898     .298   .213100   .402800  -.3727600    -3.700    -4.100  
+89 619 47696.00 I   .213726  .000456   .399916  .000171  I -.3728549  .0000247  0.5667 0.0179  I    -5.133     .114    -2.772     .298   .214900   .400300  -.3733200    -3.700    -3.900  
+89 620 47697.00 I   .215569  .000600   .397397  .000233  I -.3734306  .0000228  0.6031 0.0201  I    -5.205     .269    -2.578     .129   .216700   .397700  -.3738500    -3.600    -3.800  
+89 621 47698.00 I   .217478  .000753   .394856  .000281  I -.3740968  .0000318  0.7465 0.0168  I    -5.312     .356    -2.425     .143   .218500   .395100  -.3744500    -3.600    -3.600  
+89 622 47699.00 I   .219460  .000753   .392288  .000281  I -.3749524  .0000247  0.9764 0.0251  I    -5.569     .356    -2.370     .143   .220400   .392400  -.3752200    -3.600    -3.500  
+89 623 47700.00 I   .221490  .000753   .389688  .000281  I -.3760604  .0000388  1.2388 0.0222  I    -6.026     .356    -2.393     .143   .222400   .389800  -.3764300    -4.000    -3.400  
+89 624 47701.00 I   .223484  .000684   .387050  .000248  I -.3774185  .0000368  1.4667 0.0255  I    -6.518     .356    -2.412     .143   .224500   .387200  -.3778900    -4.500    -3.400  
+89 625 47702.00 I   .225443  .000744   .384378  .000359  I -.3789637  .0000332  1.6044 0.0270  I    -6.923     .334    -2.389     .122   .226600   .384600  -.3795300    -5.000    -3.400  
+89 626 47703.00 I   .227389  .000758   .381680  .000374  I -.3805834  .0000395  1.6106 0.0280  I    -7.211     .275    -2.343     .298   .228800   .382000  -.3812500    -5.300    -3.500  
+89 627 47704.00 I   .229349  .000672   .378961  .000378  I -.3821356  .0000451  1.4690 0.0299  I    -7.374     .250    -2.319     .298   .230900   .379300  -.3829100    -5.500    -3.600  
+89 628 47705.00 I   .231358  .000672   .376225  .000378  I -.3834821  .0000450  1.2095 0.0254  I    -7.405     .250    -2.351     .298   .232900   .376700  -.3841500    -5.100    -3.700  
+89 629 47706.00 I   .233388  .000685   .373459  .000363  I -.3845358  .0000232  0.8936 0.0262  I    -7.320     .250    -2.350     .298   .234800   .374000  -.3851100    -4.500    -3.900  
+89 630 47707.00 I   .235346  .000718   .370661  .000387  I -.3852727  .0000267  0.5881 0.0177  I    -7.167     .751    -2.340     .134   .236600   .371300  -.3857600    -3.800    -4.100  
+89 7 1 47708.00 I   .237174  .000830   .367822  .000449  I -.3857380  .0000268  0.3607 0.0193  I    -7.011     .852    -2.417     .150   .238400   .368600  -.3861700    -3.000    -4.300  
+89 7 2 47709.00 I   .238840  .000729   .364929  .000326  I -.3860344  .0000280  0.2541 0.0184  I    -6.922     .979    -2.596     .156   .240100   .365800  -.3864300    -2.300    -4.400  
+89 7 3 47710.00 I   .240365  .000633   .361986  .000367  I -.3862903  .0000253  0.2798 0.0185  I    -6.962     .979    -2.833     .156   .241800   .363000  -.3867200    -2.100    -3.700  
+89 7 4 47711.00 I   .241816  .000575   .359017  .000380  I -.3866311  .0000241  0.4167 0.0200  I    -6.994     .853    -2.920     .138   .243400   .359800  -.3870900    -1.800    -3.400  
+89 7 5 47712.00 I   .243232  .000679   .356043  .000464  I -.3871441  .0000309  0.6148 0.0208  I    -7.040     .743    -2.777     .343   .244900   .356500  -.3876300    -1.800    -3.100  
+89 7 6 47713.00 I   .244637  .000700   .353083  .000473  I -.3878642  .0000339  0.8233 0.0197  I    -7.290     .456    -2.500     .364   .246400   .353200  -.3883600    -1.900    -2.900  
+89 7 7 47714.00 I   .246029  .000508   .350152  .000338  I -.3887809  .0000244  1.0023 0.0208  I    -7.804     .433    -2.239     .368   .247800   .350000  -.3892600    -2.200    -2.700  
+89 7 8 47715.00 I   .247390  .000508   .347247  .000338  I -.3898499  .0000242  1.1244 0.0173  I    -8.394     .403    -2.153     .348   .249100   .347000  -.3903000    -3.200    -2.700  
+89 7 9 47716.00 I   .248711  .000480   .344346  .000349  I -.3910046  .0000244  1.1710 0.0180  I    -8.816     .403    -2.192     .348   .250400   .344100  -.3914000    -4.400    -2.800  
+89 710 47717.00 I   .249981  .000556   .341423  .000323  I -.3921652  .0000266  1.1376 0.0190  I    -8.905     .571    -2.159     .321   .251500   .341300  -.3925200    -5.600    -3.000  
+89 711 47718.00 I   .251214  .000534   .338475  .000257  I -.3932617  .0000291  1.0500 0.0200  I    -8.809     .573    -2.140     .116   .252600   .338500  -.3935800    -6.900    -3.100  
+89 712 47719.00 I   .252378  .000535   .335505  .000237  I -.3942564  .0000300  0.9361 0.0225  I    -8.886     .631    -2.266     .165   .253600   .335700  -.3945600    -8.000    -3.200  
+89 713 47720.00 I   .253441  .000629   .332538  .000350  I -.3951311  .0000342  0.8138 0.0340  I    -9.428     .620    -2.596     .169   .254500   .333000  -.3954500    -8.400    -3.000  
+89 714 47721.00 I   .254448  .000803   .329617  .000442  I -.3958903  .0000611  0.7105 0.0343  I   -10.190     .753    -2.930     .224   .255400   .330200  -.3962100    -8.600    -2.900  
+89 715 47722.00 I   .255471  .000864   .326783  .000453  I -.3965658  .0000594  0.6484 0.0396  I   -10.698     .737    -2.919     .317   .256100   .327500  -.3968500    -8.700    -2.700  
+89 716 47723.00 I   .256523  .000804   .324049  .000491  I -.3972030  .0000504  0.6342 0.0392  I   -10.795     .683    -2.614     .377   .256900   .324700  -.3975300    -8.800    -2.500  
+89 717 47724.00 I   .257510  .000798   .321393  .000499  I -.3978523  .0000511  0.6745 0.0356  I   -10.463     .625    -2.132     .364   .257500   .322000  -.3980000    -8.900    -2.300  
+89 718 47725.00 I   .258280  .000782   .318751  .000522  I -.3985792  .0000502  0.7964 0.0344  I   -10.059     .627    -2.055     .378   .258000   .319100  -.3986800    -9.200    -2.100  
+89 719 47726.00 I   .258834  .000604   .316059  .000495  I -.3994760  .0000460  1.0108 0.0293  I    -9.743     .691    -2.295     .409   .258500   .316200  -.3995800    -9.600    -2.000  
+89 720 47727.00 I   .259242  .000486   .313273  .000520  I -.4006205  .0000303  1.2842 0.0349  I    -9.567     .498    -2.371     .342   .258900   .313200  -.4007400   -10.000    -1.900  
+89 721 47728.00 I   .259520  .000908   .310377  .000880  I -.4020440  .0000525  1.5564 0.0353  I    -9.661     .293    -2.255     .219   .259300   .310100  -.4022100   -10.300    -1.900  
+89 722 47729.00 I   .259677  .000894   .307373  .000812  I -.4037092  .0000637  1.7563 0.0489  I   -10.131     .282    -2.180     .200   .259700   .307100  -.4039200   -10.600    -1.900  
+89 723 47730.00 I   .259721  .001211   .304260  .000887  I -.4055103  .0000825  1.8174 0.0466  I   -10.909     .393    -2.338     .217   .260100   .304000  -.4057500   -10.800    -2.000  
+89 724 47731.00 I   .259656  .000970   .301045  .000750  I -.4072883  .0000681  1.7115 0.0525  I   -11.646     .377    -2.566     .271   .260500   .300900  -.4075500   -10.900    -2.200  
+89 725 47732.00 I   .259516  .000946   .297755  .000746  I -.4088916  .0000651  1.4806 0.0491  I   -11.967     .366    -2.561     .236   .260800   .297800  -.4092000   -11.000    -2.400  
+89 726 47733.00 I   .259411  .001136   .294397  .000786  I -.4102297  .0000707  1.1904 0.0446  I   -11.822     .385    -2.305     .239   .261100   .294700  -.4105700   -10.900    -2.700  
+89 727 47734.00 I   .259400  .000986   .290980  .000721  I -.4112703  .0000611  0.8938 0.0445  I   -11.589     .385    -2.280     .239   .261200   .291500  -.4116300   -10.600    -2.900  
+89 728 47735.00 I   .259475  .000723   .287550  .000526  I -.4120367  .0000542  0.6553 0.0393  I   -11.568     .388    -2.484     .237   .261100   .288400  -.4122900    -9.900    -3.000  
+89 729 47736.00 I   .259578  .000669   .284153  .000535  I -.4126159  .0000494  0.5217 0.0352  I   -11.665     .354    -2.690     .219   .260900   .285300  -.4127600    -9.100    -3.100  
+89 730 47737.00 I   .259598  .000484   .280826  .000480  I -.4131170  .0000450  0.4987 0.0344  I   -11.771     .511    -2.791     .186   .260600   .282100  -.4131300    -8.200    -3.200  
+89 731 47738.00 I   .259486  .000634   .277618  .000540  I -.4136479  .0000480  0.5794 0.0323  I   -11.868     .698    -2.721     .190   .260300   .279000  -.4135600    -7.400    -3.200  
+89 8 1 47739.00 I   .259283  .000634   .274517  .000420  I -.4142997  .0000463  0.7317 0.0332  I   -11.954     .669    -2.494     .214   .259900   .275800  -.4141200    -6.700    -3.200  
+89 8 2 47740.00 I   .259062  .000597   .271488  .000399  I -.4151187  .0000459  0.9060 0.0260  I   -12.066     .676    -2.239     .205   .259600   .272500  -.4149900    -7.500    -3.000  
+89 8 3 47741.00 I   .258932  .000620   .268482  .000316  I -.4161049  .0000237  1.0598 0.0310  I   -12.031     .841    -2.043     .205   .259400   .269200  -.4160500    -7.300    -2.900  
+89 8 4 47742.00 I   .258981  .000616   .265471  .000322  I -.4172229  .0000416  1.1673 0.0267  I   -11.897     .748    -1.859     .207   .259300   .266000  -.4172400    -7.300    -2.800  
+89 8 5 47743.00 I   .259177  .000731   .262452  .000384  I -.4184194  .0000478  1.2146 0.0319  I   -11.788     .744    -1.900     .188   .259200   .262800  -.4185100    -7.500    -2.700  
+89 8 6 47744.00 I   .259382  .000731   .259419  .000385  I -.4196321  .0000485  1.2021 0.0362  I   -11.989     .629    -2.102     .241   .259200   .259600  -.4197900    -7.800    -2.600  
+89 8 7 47745.00 I   .259502  .000828   .256372  .000395  I -.4208062  .0000544  1.1375 0.0354  I   -12.279     .657    -2.116     .247   .259300   .256400  -.4210000    -8.900    -2.900  
+89 8 8 47746.00 I   .259528  .000722   .253308  .000518  I -.4218918  .0000515  1.0269 0.0373  I   -12.396     .670    -2.014     .240   .259400   .253200  -.4220600   -10.100    -3.100  
+89 8 9 47747.00 I   .259514  .000757   .250247  .000530  I -.4228527  .0000511  0.8944 0.0341  I   -12.372     .605    -2.120     .206   .259400   .250000  -.4229700   -11.300    -3.400  
+89 810 47748.00 I   .259562  .000817   .247205  .000590  I -.4236824  .0000446  0.7671 0.0338  I   -12.374     .669    -2.245     .203   .259500   .246800  -.4237400   -12.500    -3.600  
+89 811 47749.00 I   .259708  .000849   .244204  .000586  I -.4243916  .0000444  0.6537 0.0316  I   -12.301     .669    -2.371     .203   .259500   .243700  -.4243800   -13.400    -3.800  
+89 812 47750.00 I   .259841  .000805   .241279  .000562  I -.4250039  .0000449  0.5826 0.0295  I   -12.112     .730    -2.497     .135   .259400   .240800  -.4250300   -13.400    -3.500  
+89 813 47751.00 I   .259861  .000719   .238419  .000647  I -.4255850  .0000389  0.5960 0.0259  I   -11.765     .778    -2.541     .298   .259300   .237900  -.4256300   -13.100    -3.200  
+89 814 47752.00 I   .259701  .000415   .235585  .000673  I -.4262314  .0000259  0.7159 0.0272  I   -11.373     .641    -2.544     .227   .259000   .235100  -.4263200   -12.700    -2.800  
+89 815 47753.00 I   .259331  .000494   .232721  .000665  I -.4270534  .0000381  0.9455 0.0218  I   -11.104     .357    -2.612     .348   .258700   .232200  -.4271700   -12.200    -2.500  
+89 816 47754.00 I   .258722  .000486   .229769  .000628  I -.4281495  .0000351  1.2563 0.0259  I   -11.035     .295    -2.702     .294   .258400   .229400  -.4283300   -11.500    -2.200  
+89 817 47755.00 I   .257874  .000486   .226673  .000628  I -.4295740  .0000351  1.5911 0.0245  I   -11.202     .295    -2.649     .294   .257800   .226300  -.4297400   -11.900    -2.100  
+89 818 47756.00 I   .256866  .000490   .223437  .000609  I -.4313153  .0000343  1.8766 0.0252  I   -11.658     .295    -2.397     .294   .257200   .223000  -.4314600   -12.300    -2.100  
+89 819 47757.00 I   .255786  .000452   .220100  .000549  I -.4332828  .0000361  2.0288 0.0319  I   -12.438     .470    -2.064     .245   .256500   .219700  -.4333800   -12.900    -2.100  
+89 820 47758.00 I   .254697  .000638   .216697  .000322  I -.4353113  .0000539  1.9962 0.0327  I   -13.390     .534    -1.855     .130   .255700   .216300  -.4353500   -13.400    -2.200  
+89 821 47759.00 I   .253687  .000638   .213255  .000322  I -.4372182  .0000545  1.7926 0.0320  I   -14.183     .803    -1.884     .150   .255000   .213000  -.4372000   -13.900    -2.300  
+89 822 47760.00 I   .252831  .000521   .209803  .000354  I -.4388607  .0000345  1.4816 0.0301  I   -14.540     .923    -2.158     .168   .254100   .209600  -.4389300   -14.000    -2.400  
+89 823 47761.00 I   .252154  .000387   .206380  .000337  I -.4401749  .0000255  1.1511 0.0200  I   -14.651    1.210    -2.453     .148   .253200   .206300  -.4403400   -14.000    -2.600  
+89 824 47762.00 I   .251620  .000324   .203035  .000545  I -.4411822  .0000201  0.8783 0.0146  I   -14.908    1.186    -2.502     .145   .252400   .203000  -.4414600   -13.900    -2.700  
+89 825 47763.00 I   .251154  .000291   .199820  .000563  I -.4419658  .0000141  0.7083 0.0124  I   -15.105    1.383    -2.373     .141   .251500   .199900  -.4423900   -13.700    -2.800  
+89 826 47764.00 I   .250680  .000298   .196789  .000599  I -.4426360  .0000144  0.6499 0.0099  I   -14.867    1.383    -2.227     .141   .250600   .196800  -.4431800   -13.400    -2.800  
+89 827 47765.00 I   .250131  .000245   .193995  .000550  I -.4433011  .0000138  0.6978 0.0098  I   -14.528    1.401    -2.188     .116   .250000   .193900  -.4436700   -12.800    -2.600  
+89 828 47766.00 I   .249428  .000225   .191364  .000552  I -.4440612  .0000133  0.8344 0.0114  I   -14.207    1.221    -2.234     .103   .249300   .191100  -.4442400   -12.100    -2.300  
+89 829 47767.00 I   .248493  .000286   .188770  .000646  I -.4449861  .0000181  1.0198 0.0146  I   -13.819     .848    -2.244     .211   .248500   .188300  -.4449600   -11.500    -2.000  
+89 830 47768.00 I   .247289  .000392   .186106  .000636  I -.4461029  .0000259  1.2117 0.0165  I   -13.393     .548    -2.093     .234   .247600   .185500  -.4458700   -11.000    -1.600  
+89 831 47769.00 I   .245810  .000498   .183287  .000599  I -.4473984  .0000277  1.3703 0.0191  I   -12.986     .548    -1.746     .234   .246500   .182700  -.4469500   -10.600    -1.300  
+89 9 1 47770.00 I   .244066  .000452   .180279  .000594  I -.4488220  .0000281  1.4642 0.0214  I   -12.682     .548    -1.276     .234   .245000   .179700  -.4486000   -11.100    -1.300  
+89 9 2 47771.00 I   .242130  .000406   .177130  .000535  I -.4502998  .0000326  1.4770 0.0194  I   -12.674     .548    -1.056     .234   .243300   .176800  -.4503200   -11.700    -1.300  
+89 9 3 47772.00 I   .240098  .000386   .173910  .000496  I -.4517486  .0000268  1.4078 0.0217  I   -12.995     .844    -1.235     .264   .241400   .173700  -.4520100   -12.500    -1.300  
+89 9 4 47773.00 I   .238027  .000377   .170686  .000460  I -.4530928  .0000287  1.2710 0.0196  I   -13.447     .935    -1.600     .237   .239400   .170700  -.4536000   -13.200    -1.400  
+89 9 5 47774.00 I   .235922  .000394   .167512  .000460  I -.4542755  .0000286  1.0890 0.0204  I   -13.798    1.091    -1.912     .262   .237200   .167600  -.4550100   -14.000    -1.600  
+89 9 6 47775.00 I   .233773  .000351   .164445  .000454  I -.4552687  .0000289  0.9007 0.0193  I   -13.998     .905    -2.114     .221   .234900   .164200  -.4556800   -15.200    -2.200  
+89 9 7 47776.00 I   .231549  .000317   .161528  .000413  I -.4560882  .0000259  0.7462 0.0183  I   -14.180     .905    -2.328     .221   .232600   .161100  -.4563700   -15.200    -2.400  
+89 9 8 47777.00 I   .229223  .000308   .158754  .000324  I -.4567806  .0000223  0.6502 0.0185  I   -14.405     .796    -2.593     .269   .230100   .158100  -.4569300   -15.000    -2.600  
+89 9 9 47778.00 I   .226787  .000375   .156103  .000387  I -.4574131  .0000264  0.6277 0.0173  I   -14.475     .589    -2.758     .248   .227600   .155200  -.4574300   -14.700    -2.800  
+89 910 47779.00 I   .224295  .000366   .153554  .000153  I -.4580653  .0000264  0.6930 0.0192  I   -14.179     .589    -2.633     .248   .225000   .152400  -.4579600   -14.200    -2.900  
+89 911 47780.00 I   .221831  .000366   .151091  .000153  I -.4588307  .0000279  0.8530 0.0191  I   -13.567     .589    -2.263     .248   .222500   .149900  -.4587600   -13.600    -2.700  
+89 912 47781.00 I   .219415  .000387   .148691  .000149  I -.4598036  .0000277  1.1098 0.0200  I   -13.092     .668    -2.005     .282   .220000   .147500  -.4597900   -12.900    -2.500  
+89 913 47782.00 I   .217025  .000383   .146348  .000175  I -.4610755  .0000288  1.4420 0.0201  I   -12.949     .553    -1.963     .247   .217400   .145200  -.4611000   -12.200    -2.300  
+89 914 47783.00 I   .214648  .000358   .144057  .000175  I -.4626902  .0000291  1.7819 0.0242  I   -13.034     .519    -1.931     .171   .214900   .142900  -.4627700   -11.500    -2.100  
+89 915 47784.00 I   .212275  .000396   .141805  .000200  I -.4646152  .0000390  2.0496 0.0254  I   -13.265     .378    -1.787     .162   .212400   .140700  -.4647300   -11.000    -2.000  
+89 916 47785.00 I   .209901  .000396   .139576  .000356  I -.4667389  .0000416  2.1657 0.0260  I   -13.568     .393    -1.646     .142   .210100   .138600  -.4669400   -11.000    -2.200  
+89 917 47786.00 I   .207490  .000373   .137349  .000325  I -.4688831  .0000345  2.0914 0.0253  I   -13.888     .393    -1.689     .142   .207900   .136500  -.4691500   -11.200    -2.400  
+89 918 47787.00 I   .205003  .000316   .135107  .000362  I -.4708694  .0000287  1.8601 0.0216  I   -14.181     .355    -1.879     .136   .205500   .134400  -.4711900   -11.400    -2.700  
+89 919 47788.00 I   .202444  .000251   .132834  .000371  I -.4725772  .0000261  1.5504 0.0187  I   -14.344     .355    -1.963     .124   .203100   .132300  -.4729500   -11.600    -2.900  
+89 920 47789.00 I   .199818  .000251   .130546  .000371  I -.4739759  .0000239  1.2579 0.0191  I   -14.445     .355    -1.837     .124   .200600   .130100  -.4744000   -11.800    -3.200  
+89 921 47790.00 I   .197123  .000319   .128280  .000439  I -.4751244  .0000278  1.0597 0.0284  I   -14.505     .316    -1.616     .134   .197800   .127600  -.4754200   -11.800    -3.200  
+89 922 47791.00 I   .194319  .000297   .126036  .000344  I -.4761386  .0000516  0.9913 0.0251  I   -14.337     .265    -1.591     .152   .194800   .125000  -.4763200   -11.700    -3.200  
+89 923 47792.00 I   .191399  .000266   .123797  .000303  I -.4771475  .0000419  1.0444 0.0353  I   -13.938     .220    -1.739     .120   .191800   .122400  -.4772200   -11.500    -3.100  
+89 924 47793.00 I   .188364  .000246   .121549  .000236  I -.4782554  .0000482  1.1815 0.0320  I   -13.522     .182    -1.829     .117   .188700   .119800  -.4782300   -11.400    -3.000  
+89 925 47794.00 I   .185238  .000284   .119268  .000272  I -.4795257  .0000484  1.3642 0.0348  I   -13.256     .707    -1.784     .118   .185500   .117200  -.4794200   -11.300    -2.800  
+89 926 47795.00 I   .182078  .000284   .116925  .000272  I -.4809871  .0000502  1.5569 0.0329  I   -13.175    1.094    -1.648     .156   .182400   .115100  -.4809700   -11.400    -2.500  
+89 927 47796.00 I   .178927  .000390   .114535  .000361  I -.4826303  .0000446  1.7222 0.0270  I   -13.275    1.094    -1.498     .156   .179300   .113000  -.4827000   -11.500    -2.100  
+89 928 47797.00 I   .175801  .000368   .112143  .000429  I -.4844125  .0000201  1.8305 0.0259  I   -13.537     .945    -1.356     .185   .176100   .111000  -.4845500   -11.600    -1.800  
+89 929 47798.00 I   .172692  .000468   .109843  .000496  I -.4862662  .0000263  1.8634 0.0166  I   -13.477    1.159    -1.040     .228   .172900   .109100  -.4864500   -11.800    -1.500  
+89 930 47799.00 I   .169578  .000468   .107752  .000496  I -.4881118  .0000263  1.8137 0.0197  I   -13.140    1.159     -.722     .228   .169700   .107400  -.4883400   -11.900    -1.400  
+8910 1 47800.00 I   .166400  .000509   .105964  .000653  I -.4898709  .0000293  1.6959 0.0161  I   -12.924    1.135     -.700     .234   .166600   .105800  -.4901000   -12.000    -1.500  
+8910 2 47801.00 I   .163140  .000422   .104437  .000609  I -.4914920  .0000185  1.5432 0.0162  I   -12.882     .550     -.934     .228   .163500   .104400  -.4917400   -12.000    -1.700  
+8910 3 47802.00 I   .159817  .000270   .103076  .000633  I -.4929552  .0000137  1.3843 0.0128  I   -12.851     .708    -1.204     .213   .160300   .103000  -.4932500   -12.000    -2.000  
+8910 4 47803.00 I   .156445  .000252   .101784  .000556  I -.4942671  .0000178  1.2450 0.0139  I   -12.770     .899    -1.428     .236   .157100   .101700  -.4946200   -12.000    -2.300  
+8910 5 47804.00 I   .153030  .000252   .100460  .000556  I -.4954603  .0000241  1.1512 0.0132  I   -12.802     .899    -1.725     .236   .153800   .100400  -.4958600   -11.900    -2.600  
+8910 6 47805.00 I   .149568  .000402   .099020  .000503  I -.4965907  .0000195  1.1212 0.0160  I   -13.131     .899    -2.168     .236   .150400   .099000  -.4968500   -11.200    -2.500  
+8910 7 47806.00 I   .146082  .000354   .097493  .000528  I -.4977285  .0000210  1.1686 0.0135  I   -13.389     .792    -2.349     .220   .146900   .097700  -.4978400   -10.800    -2.500  
+8910 8 47807.00 I   .142608  .000362   .095962  .000444  I -.4989574  .0000186  1.3043 0.0151  I   -13.249     .652    -2.073     .181   .143300   .096400  -.4988900   -10.500    -2.400  
+8910 9 47808.00 I   .139183  .000446   .094510  .000395  I -.5003659  .0000216  1.5263 0.0136  I   -12.736     .686    -1.633     .163   .139600   .095100  -.5001200   -10.200    -2.400  
+891010 47809.00 I   .135807  .000444   .093205  .000369  I -.5020325  .0000198  1.8157 0.0136  I   -12.048     .518    -1.419     .221   .135800   .093800  -.5016400   -10.000    -2.300  
+891011 47810.00 I   .132325  .000444   .092049  .000369  I -.5040085  .0000164  2.1387 0.0129  I   -11.547     .488    -1.452     .214   .131900   .092500  -.5037300   -10.200    -2.200  
+891012 47811.00 I   .128573  .000497   .090993  .000385  I -.5062987  .0000164  2.4280 0.0149  I   -11.609     .488    -1.575     .214   .127900   .091200  -.5062100   -10.400    -2.200  
+891013 47812.00 I   .124506  .000547   .089968  .000309  I -.5088255  .0000250  2.6002 0.0158  I   -12.103     .532    -1.596     .195   .123800   .090000  -.5089400   -10.700    -2.100  
+891014 47813.00 I   .120219  .000588   .088938  .000320  I -.5114430  .0000271  2.6042 0.0170  I   -12.606     .566    -1.465     .211   .119600   .088700  -.5117600   -11.000    -2.100  
+891015 47814.00 I   .115844  .000507   .087877  .000279  I -.5139777  .0000231  2.4397 0.0174  I   -12.726     .382    -1.359     .208   .115400   .087500  -.5145000   -11.200    -2.000  
+891016 47815.00 I   .111554  .000468   .086777  .000314  I -.5162918  .0000217  2.1835 0.0164  I   -12.292     .384    -1.330     .298   .111300   .086400  -.5167800   -10.800    -2.000  
+891017 47816.00 I   .107593  .000476   .085697  .000346  I -.5183447  .0000232  1.9273 0.0158  I   -11.657     .390    -1.269     .298   .107300   .085300  -.5187500   -10.400    -2.000  
+891018 47817.00 I   .103907  .000457   .084674  .000484  I -.5201702  .0000229  1.7425 0.0149  I   -11.310     .359    -1.163     .105   .103300   .084300  -.5204900   -10.000    -2.000  
+891019 47818.00 I   .100289  .000445   .083678  .000499  I -.5218716  .0000188  1.6835 0.0143  I   -11.338     .244    -1.147     .110   .099300   .083200  -.5220800    -9.700    -2.000  
+891020 47819.00 I   .096530  .000279   .082687  .000513  I -.5235770  .0000170  1.7439 0.0153  I   -11.348     .268    -1.336     .115   .095500   .082100  -.5236700    -9.500    -1.900  
+891021 47820.00 I   .092643  .000369   .081623  .000515  I -.5253861  .0000241  1.8844 0.0145  I   -11.157     .284    -1.486     .116   .091800   .081100  -.5255000    -9.900    -1.700  
+891022 47821.00 I   .088758  .000374   .080515  .000566  I -.5273585  .0000235  2.0628 0.0168  I   -10.927     .238    -1.355     .125   .088200   .080000  -.5275000   -10.400    -1.500  
+891023 47822.00 I   .084999  .000329   .079469  .000494  I -.5295096  .0000234  2.2347 0.0170  I   -10.805     .206    -1.037     .181   .084600   .079100  -.5296700   -11.000    -1.300  
+891024 47823.00 I   .081461  .000323   .078569  .000341  I -.5318132  .0000245  2.3625 0.0158  I   -10.745     .225     -.752     .177   .081100   .078200  -.5319700   -11.600    -1.100  
+891025 47824.00 I   .078112  .000322   .077817  .000292  I -.5342076  .0000211  2.4094 0.0152  I   -10.569     .255     -.451     .166   .077600   .077400  -.5343600   -12.100     -.900  
+891026 47825.00 I   .074806  .000300   .077207  .000274  I -.5366070  .0000179  2.3813 0.0140  I   -10.252     .232     -.166     .164   .074300   .076900  -.5368100   -12.300     -.900  
+891027 47826.00 I   .071438  .000306   .076761  .000328  I -.5389626  .0000183  2.3303 0.0112  I   -10.005     .234      .038     .163   .071000   .076500  -.5392300   -12.400    -1.000  
+891028 47827.00 I   .068006  .000271   .076490  .000386  I -.5412601  .0000133  2.2569 0.0106  I    -9.895     .464      .103     .155   .067700   .076300  -.5415600   -12.300    -1.000  
+891029 47828.00 I   .064380  .000308   .076301  .000407  I -.5434609  .0000108  2.1370 0.0089  I    -9.975     .578     -.046     .273   .064400   .076100  -.5437800   -12.200    -1.100  
+891030 47829.00 I   .060604  .000303   .076052  .000429  I -.5455241  .0000118  1.9871 0.0088  I   -10.292     .563     -.433     .278   .061100   .076000  -.5458500   -12.000    -1.200  
+891031 47830.00 I   .056867  .000350   .075662  .000435  I -.5474344  .0000140  1.8353 0.0098  I   -10.684     .592     -.863     .288   .057500   .075700  -.5476900   -11.600    -1.200  
+8911 1 47831.00 I   .053306  .000363   .075251  .000472  I -.5492010  .0000157  1.7025 0.0110  I   -10.804     .632    -1.026     .305   .053800   .075500  -.5493800   -11.100    -1.200  
+8911 2 47832.00 I   .049948  .000386   .074957  .000480  I -.5508516  .0000171  1.6062 0.0128  I   -10.512     .743     -.958     .291   .050100   .075200  -.5510000   -10.600    -1.200  
+8911 3 47833.00 I   .046609  .000392   .074778  .000318  I -.5524322  .0000202  1.5664 0.0146  I    -9.907     .720     -.740     .337   .046300   .074900  -.5525700    -9.900    -1.200  
+8911 4 47834.00 I   .043062  .000444   .074689  .000334  I -.5540111  .0000237  1.6066 0.0194  I    -9.335     .658     -.504     .160   .042500   .074600  -.5541900    -9.300    -1.200  
+8911 5 47835.00 I   .039172  .000518   .074657  .000191  I -.5556779  .0000332  1.7444 0.0195  I    -9.139     .676     -.445     .164   .039800   .074600  -.5558600    -8.700    -1.300  
+8911 6 47836.00 I   .034976  .000518   .074651  .000167  I -.5575302  .0000310  1.9733 0.0213  I    -9.190     .625     -.484     .182   .035900   .074300  -.5577500    -8.100    -1.300  
+8911 7 47837.00 I   .030594  .000393   .074651  .000184  I -.5596418  .0000268  2.2541 0.0231  I    -9.193     .687     -.478     .185   .031900   .074100  -.5599000    -7.400    -1.200  
+8911 8 47838.00 I   .026170  .000454   .074658  .000210  I -.5620377  .0000342  2.5326 0.0217  I    -9.015     .559     -.393     .180   .027900   .074000  -.5623200    -7.000    -1.100  
+8911 9 47839.00 I   .021873  .000578   .074685  .000208  I -.5646849  .0000342  2.7446 0.0254  I    -8.806     .559     -.334     .180   .023800   .074000  -.5649700    -6.500    -2.000  
+891110 47840.00 I   .017774  .000609   .074750  .000182  I -.5674802  .0000376  2.8164 0.0235  I    -8.863     .592     -.142     .189   .019800   .074100  -.5676900    -6.100    -2.100  
+891111 47841.00 I   .013797  .000633   .074867  .000178  I -.5702598  .0000323  2.7145 0.0225  I    -8.961     .723      .032     .220   .015700   .074400  -.5703800    -5.600    -2.200  
+891112 47842.00 I   .009853  .000591   .075052  .000151  I -.5728625  .0000248  2.4723 0.0212  I    -8.674     .758     -.059     .190   .011800   .074600  -.5728800    -5.200    -2.300  
+891113 47843.00 I   .005935  .000679   .075329  .000172  I -.5751842  .0000275  2.1693 0.0181  I    -7.872     .736     -.305     .189   .007900   .075000  -.5751000    -4.700    -2.200  
+891114 47844.00 I   .002118  .000716   .075723  .000189  I -.5772119  .0000265  1.8987 0.0178  I    -7.039     .773     -.195     .205   .004000   .075500  -.5770200    -4.300    -2.100  
+891115 47845.00 I  -.001536  .000698   .076257  .000225  I -.5790178  .0000227  1.7369 0.0166  I    -6.495     .833      .191     .200   .000400   .076000  -.5789500    -4.300    -1.900  
+891116 47846.00 I  -.005052  .000581   .076920  .000233  I -.5807332  .0000201  1.7178 0.0135  I    -6.337     .845      .431     .226  -.003100   .076600  -.5807900    -4.300    -1.700  
+891117 47847.00 I  -.008515  .000551   .077690  .000217  I -.5824945  .0000148  1.8219 0.0124  I    -6.434     .799      .300     .194  -.006600   .077300  -.5826900    -4.300    -1.600  
+891118 47848.00 I  -.011982  .000491   .078552  .000268  I -.5844024  .0000145  2.0021 0.0103  I    -6.643     .824     -.055     .197  -.010200   .078100  -.5847500    -4.400    -1.400  
+891119 47849.00 I  -.015443  .000478   .079520  .000323  I -.5865052  .0000144  2.2017 0.0103  I    -6.754     .824     -.067     .197  -.013700   .079000  -.5870000    -4.400    -1.300  
+891120 47850.00 I  -.018894  .000546   .080613  .000361  I -.5887973  .0000145  2.3757 0.0105  I    -6.776     .755      .284     .194  -.017400   .080300  -.5891500    -4.600    -1.200  
+891121 47851.00 I  -.022304  .000489   .081830  .000332  I -.5912393  .0000153  2.4979 0.0104  I    -6.885     .669      .718     .198  -.021200   .081600  -.5914300    -4.900    -1.100  
+891122 47852.00 I  -.025738  .000438   .083181  .000273  I -.5937721  .0000148  2.5570 0.0123  I    -6.957     .560      .939     .169  -.025100   .083100  -.5938300    -5.100    -1.000  
+891123 47853.00 I  -.029284  .000501   .084672  .000215  I -.5963324  .0000192  2.5534 0.0120  I    -6.907     .357      .842     .163  -.028900   .084500  -.5962600    -5.300    -1.000  
+891124 47854.00 I  -.032975  .000393   .086275  .000230  I -.5988612  .0000189  2.4967 0.0136  I    -6.854     .117      .687     .164  -.032900   .086100  -.5986900    -5.400    -1.100  
+891125 47855.00 I  -.036766  .000393   .087923  .000230  I -.6013089  .0000192  2.3894 0.0140  I    -6.879     .117      .709     .164  -.036700   .087500  -.6012100    -5.400    -1.200  
+891126 47856.00 I  -.040644  .000344   .089531  .000183  I -.6036224  .0000206  2.2295 0.0129  I    -6.941     .125      .758     .161  -.040400   .088800  -.6036100    -5.400    -1.300  
+891127 47857.00 I  -.044621  .000327   .091021  .000211  I -.6057624  .0000171  2.0531 0.0149  I    -6.964     .135      .658     .127  -.044100   .090200  -.6058300    -5.400    -1.400  
+891128 47858.00 I  -.048622  .000347   .092360  .000242  I -.6077381  .0000215  1.9050 0.0139  I    -6.926     .152      .529     .298  -.047700   .091400  -.6078900    -5.200    -1.600  
+891129 47859.00 I  -.052504  .000357   .093533  .000237  I -.6095892  .0000219  1.8071 0.0171  I    -6.771     .152      .530     .298  -.051200   .092700  -.6098000    -5.000    -1.600  
+891130 47860.00 I  -.056142  .000428   .094538  .000440  I -.6113724  .0000266  1.7693 0.0173  I    -6.457     .149      .638     .298  -.054600   .093900  -.6115700    -4.600    -1.600  
+8912 1 47861.00 I  -.059499  .000363   .095445  .000369  I -.6131492  .0000269  1.7957 0.0174  I    -6.052     .149      .723     .298  -.057800   .095100  -.6133100    -4.100    -1.500  
+8912 2 47862.00 I  -.062559  .000340   .096349  .000367  I -.6149856  .0000224  1.8873 0.0178  I    -5.744     .233      .715     .298  -.060800   .096300  -.6150900    -3.700    -1.300  
+8912 3 47863.00 I  -.065327  .000277   .097344  .000301  I -.6169418  .0000233  2.0327 0.0204  I    -5.673     .284      .678     .298  -.063700   .097700  -.6169800    -3.300    -1.200  
+8912 4 47864.00 I  -.067830  .000277   .098522  .000301  I -.6190600  .0000340  2.2052 0.0242  I    -5.830     .284      .730     .298  -.066400   .099100  -.6190500    -3.100    -1.100  
+8912 5 47865.00 I  -.070117  .000385   .099965  .000330  I -.6213521  .0000424  2.3776 0.0308  I    -6.112     .284      .942     .298  -.069000   .100400  -.6214400    -2.800     -.500  
+8912 6 47866.00 I  -.072305  .000385   .101669  .000330  I -.6238043  .0000513  2.5173 0.0341  I    -6.361     .284     1.201     .298  -.071500   .102000  -.6238700    -3.700     -.200  
+8912 7 47867.00 I  -.074486  .000385   .103589  .000330  I -.6263590  .0000533  2.5740 0.0810  I    -6.593     .284     1.458     .298  -.074000   .103700  -.6264000    -4.700      .100  
+8912 8 47868.00 I  -.076739  .000748   .105674  .000318  I -.6289126  .0001537  2.5115 0.1026  I    -6.927     .259     1.700     .298  -.076500   .105600  -.6289500    -5.600      .000  
+8912 9 47869.00 I  -.079122  .001151   .107881  .000538  I -.6313409  .0001982  2.3258 0.0780  I    -7.324     .791     1.798     .298  -.078900   .107700  -.6313800    -6.100     -.100  
+891210 47870.00 I  -.081630  .000396   .110183  .000207  I -.6335356  .0000265  2.0544 0.0997  I    -7.520     .791     1.614     .298  -.081300   .109900  -.6335700    -6.400     -.300  
+891211 47871.00 I  -.084241  .000334   .112555  .000231  I -.6354462  .0000226  1.7725 0.0157  I    -7.271     .140     1.201     .298  -.083700   .112300  -.6354900    -6.200     -.500  
+891212 47872.00 I  -.086932  .000342   .114974  .000227  I -.6371037  .0000169  1.5598 0.0133  I    -6.656     .279      .818     .298  -.086200   .114800  -.6371500    -5.400     -.800  
+891213 47873.00 I  -.089614  .000342   .117435  .000227  I -.6386064  .0000140  1.4687 0.0090  I    -6.017     .279      .674     .298  -.088800   .117400  -.6386400    -4.300     -.900  
+891214 47874.00 I  -.092151  .000363   .119956  .000221  I -.6400823  .0000062  1.5010 0.0080  I    -5.577     .279      .722     .298  -.091100   .120000  -.6401300    -3.100     -.900  
+891215 47875.00 I  -.094464  .000259   .122557  .000174  I -.6416399  .0000077  1.6278 0.0054  I    -5.311     .279      .809     .298  -.093500   .122400  -.6416800    -1.900    -1.000  
+891216 47876.00 I  -.096596  .000223   .125204  .000155  I -.6433545  .0000088  1.8045 0.0069  I    -5.099     .310      .845     .298  -.095800   .125200  -.6434200    -1.100     -.900  
+891217 47877.00 I  -.098647  .000279   .127845  .000160  I -.6452473  .0000114  1.9764 0.0074  I    -4.999     .393      .886     .298  -.098100   .127800  -.6453100     -.600     -.800  
+891218 47878.00 I  -.100700  .000272   .130424  .000132  I -.6472936  .0000118  2.1073 0.0084  I    -5.115     .388      .980     .298  -.100200   .130300  -.6473600     -.700     -.600  
+891219 47879.00 I  -.102791  .000265   .132890  .000156  I -.6494431  .0000124  2.1818 0.0085  I    -5.335     .388     1.034     .298  -.102300   .132800  -.6495200    -1.100     -.500  
+891220 47880.00 I  -.104932  .000269   .135228  .000159  I -.6516350  .0000121  2.1897 0.0084  I    -5.551     .462      .928     .298  -.104300   .135100  -.6517000    -1.600     -.200  
+891221 47881.00 I  -.107038  .000272   .137530  .000155  I -.6537996  .0000112  2.1290 0.0083  I    -5.702     .473      .781     .132  -.106300   .137500  -.6538500    -2.200     -.100  
+891222 47882.00 I  -.109166  .000279   .139802  .000156  I -.6558744  .0000114  2.0126 0.0086  I    -5.778     .483      .770     .140  -.108600   .139700  -.6559100    -2.800     -.100  
+891223 47883.00 I  -.111425  .000358   .142033  .000182  I -.6578097  .0000130  1.8511 0.0103  I    -5.733     .518      .914     .169  -.110900   .141800  -.6578200    -3.200     -.300  
+891224 47884.00 I  -.113908  .000348   .144250  .000249  I -.6595674  .0000171  1.6621 0.0106  I    -5.662     .586      .990     .192  -.113200   .144100  -.6595900    -3.200     -.500  
+891225 47885.00 I  -.116617  .000342   .146490  .000244  I -.6611365  .0000168  1.4811 0.0106  I    -5.698     .586      .868     .192  -.115800   .146300  -.6611500    -3.300     -.800  
+891226 47886.00 I  -.119446  .000328   .148785  .000110  I -.6625434  .0000126  1.3417 0.0115  I    -5.835     .425      .701     .185  -.118400   .148800  -.6625900    -3.100     -.900  
+891227 47887.00 I  -.122271  .000358   .151162  .000239  I -.6638408  .0000157  1.2651 0.0092  I    -6.002     .159      .667     .170  -.120900   .151100  -.6639200    -2.800     -.800  
+891228 47888.00 I  -.124968  .000349   .153651  .000260  I -.6650994  .0000134  1.2657 0.0111  I    -6.036     .159      .809     .170  -.123500   .153800  -.6651700    -2.300     -.700  
+891229 47889.00 I  -.127413  .000349   .156281  .000260  I -.6663989  .0000158  1.3462 0.0104  I    -5.836     .159     1.041     .170  -.126200   .156700  -.6664800    -1.900     -.500  
+891230 47890.00 I  -.129524  .000349   .159074  .000260  I -.6678172  .0000158  1.5028 0.0101  I    -5.536     .159     1.174     .170  -.128600   .159700  -.6679000    -1.600     -.300  
+891231 47891.00 I  -.131349  .000268   .161999  .000202  I -.6694241  .0000127  1.7184 0.0109  I    -5.317     .159     1.154     .170  -.130600   .162500  -.6695100    -1.700     -.200  
+90 1 1 47892.00 I  -.132974  .000234   .164998  .000286  I  .3287404  .0000150  1.9510 0.0105  I    -5.258     .217     1.052     .144  -.132500   .165300   .3286400    -2.100     -.100  
+90 1 2 47893.00 I  -.134485  .000173   .168017  .000291  I  .3266860  .0000168  2.1474 0.0120  I    -5.325     .268      .955     .298  -.134100   .168000   .3265900    -2.700     -.100  
+90 1 3 47894.00 I  -.135970  .000173   .170997  .000291  I  .3244717  .0000188  2.2655 0.0135  I    -5.434     .268      .921     .298  -.135500   .170600   .3243500    -3.500     -.100  
+90 1 4 47895.00 I  -.137516  .000173   .173884  .000291  I  .3221932  .0000211  2.2691 0.0131  I    -5.532     .268      .960     .298  -.136800   .173400   .3221100    -4.500     1.000  
+90 1 5 47896.00 I  -.139181  .000210   .176642  .000295  I  .3199771  .0000182  2.1420 0.0139  I    -5.645     .268     1.037     .298  -.138200   .176000   .3199100    -6.100      .400  
+90 1 6 47897.00 I  -.140984  .000214   .179271  .000312  I  .3179432  .0000180  1.9131 0.0136  I    -5.798     .588     1.083     .127  -.139900   .178800   .3178800    -6.300      .000  
+90 1 7 47898.00 I  -.142938  .000344   .181783  .000529  I  .3161659  .0000203  1.6394 0.0135  I    -5.935     .787     1.027     .172  -.141800   .181300   .3160700    -5.300    -1.100  
+90 1 8 47899.00 I  -.145050  .000368   .184190  .000570  I  .3146584  .0000201  1.3838 0.0137  I    -5.982     .648      .875     .144  -.143800   .183400   .3146100    -3.200    -1.500  
+90 1 9 47900.00 I  -.147283  .000368   .186517  .000570  I  .3133695  .0000185  1.2147 0.0129  I    -5.952     .677      .752     .127  -.146100   .185800   .3133200    -2.100    -2.000  
+90 110 47901.00 I  -.149527  .000376   .188891  .000491  I  .3121848  .0000163  1.1782 0.0113  I    -5.846     .677      .787     .127  -.148400   .188200   .3121100     -.600    -2.100  
+90 111 47902.00 I  -.151664  .000297   .191448  .000431  I  .3109700  .0000131  1.2711 0.0109  I    -5.614     .726      .990     .129  -.150600   .191000   .3108600      .500    -1.600  
+90 112 47903.00 I  -.153666  .000338   .194203  .000459  I  .3096123  .0000146  1.4548 0.0098  I    -5.156     .692     1.202     .298  -.152500   .193700   .3095300      .700     -.500  
+90 113 47904.00 I  -.155469  .000338   .197091  .000459  I  .3080493  .0000147  1.6716 0.0107  I    -4.657     .692     1.186     .298  -.154400   .196600   .3079800      .300      .600  
+90 114 47905.00 I  -.157038  .000306   .200038  .000379  I  .3062812  .0000156  1.8520 0.0101  I    -4.558     .764      .791     .237  -.156200   .199900   .3062400      .300     1.300  
+90 115 47906.00 I  -.158489  .000244   .203022  .000149  I  .3043732  .0000138  1.9486 0.0120  I    -4.895     .765      .227     .269  -.157600   .203200   .3043100     -.500     1.700  
+90 116 47907.00 I  -.159972  .000314   .206036  .000165  I  .3024133  .0000183  1.9571 0.0139  I    -5.367     .602     -.097     .325  -.159500   .206100   .3023500    -1.900     1.600  
+90 117 47908.00 I  -.161637  .000356   .209075  .000196  I  .3004836  .0000241  1.8916 0.0147  I    -5.625     .612      .174     .358  -.161100   .208900   .3004300    -3.500     1.200  
+90 118 47909.00 I  -.163648  .000356   .212119  .000196  I  .2986485  .0000231  1.7709 0.0167  I    -5.688     .612      .676     .358  -.162600   .211200   .2985800    -4.900      .500  
+90 119 47910.00 I  -.166171  .000356   .215135  .000196  I  .2969497  .0000230  1.6265 0.0151  I    -5.794     .786      .749     .359  -.165400   .214500   .2968900    -6.500     -.200  
+90 120 47911.00 I  -.169172  .000439   .218109  .000309  I  .2953954  .0000195  1.4824 0.0142  I    -6.115     .741      .316     .326  -.168400   .217500   .2952300    -7.100     -.900  
+90 121 47912.00 I  -.172428  .000388   .221071  .000324  I  .2939803  .0000165  1.3521 0.0138  I    -6.348     .687     -.220     .368  -.172300   .220900   .2936200    -6.600    -1.300  
+90 122 47913.00 I  -.175694  .000417   .224056  .000493  I  .2926783  .0000195  1.2606 0.0103  I    -6.056     .798     -.296     .353  -.174900   .223800   .2925700    -5.100    -1.600  
+90 123 47914.00 I  -.178725  .000442   .227102  .000542  I  .2914388  .0000122  1.2299 0.0122  I    -5.523     .709      .153     .471  -.177600   .227000   .2915700    -3.400    -1.600  
+90 124 47915.00 I  -.181309  .000500   .230256  .000623  I  .2901924  .0000147  1.2776 0.0095  I    -5.199     .683      .610     .513  -.180000   .230000   .2903300    -2.100    -1.600  
+90 125 47916.00 I  -.183495  .000441   .233556  .000565  I  .2888525  .0000146  1.4187 0.0106  I    -5.239     .532      .833     .480  -.182500   .233400   .2889000    -1.300    -1.500  
+90 126 47917.00 I  -.185427  .000391   .236998  .000491  I  .2873251  .0000152  1.6495 0.0135  I    -5.306     .513      .528     .432  -.185100   .236700   .2873100     -.800    -1.200  
+90 127 47918.00 I  -.187215  .000426   .240537  .000473  I  .2855331  .0000226  1.9414 0.0148  I    -5.072     .492      .255     .431  -.187400   .239900   .2855600    -1.000     -.700  
+90 128 47919.00 I  -.188973  .000497   .244118  .000507  I  .2834373  .0000253  2.2482 0.0168  I    -4.695     .421      .241     .423  -.189600   .243100   .2835100    -1.500     -.300  
+90 129 47920.00 I  -.190812  .000565   .247664  .000613  I  .2810477  .0000249  2.5222 0.0183  I    -4.513     .435      .380     .321  -.191700   .246300   .2811600    -2.000      .000  
+90 130 47921.00 I  -.192611  .000563   .251151  .000585  I  .2784173  .0000264  2.7232 0.0218  I    -4.646     .443      .543     .156  -.193400   .249600   .2785100    -2.600      .100  
+90 131 47922.00 I  -.194191  .000553   .254600  .000497  I  .2756436  .0000357  2.7972 0.0288  I    -4.979     .342      .595     .174  -.195100   .253200   .2757500    -3.500      .200  
+90 2 1 47923.00 I  -.195576  .000729   .258070  .000590  I  .2728727  .0000512  2.7217 0.0314  I    -5.387     .338      .569     .172  -.196100   .256800   .2729000    -4.100     -.100  
+90 2 2 47924.00 I  -.196900  .000729   .261580  .000590  I  .2702385  .0000516  2.5312 0.0375  I    -5.757     .338      .544     .172  -.196400   .260600   .2701800    -4.400     -.600  
+90 2 3 47925.00 I  -.198266  .000702   .265115  .000732  I  .2678273  .0000547  2.2897 0.0356  I    -5.924     .343      .521     .184  -.197300   .264100   .2678600    -5.600    -1.100  
+90 2 4 47926.00 I  -.199681  .000600   .268675  .000605  I  .2656487  .0000490  2.0795 0.0351  I    -5.909     .314      .493     .171  -.198600   .268000   .2657200    -5.000    -1.900  
+90 2 5 47927.00 I  -.201065  .000480   .272274  .000274  I  .2636380  .0000440  1.9604 0.0305  I    -5.812     .480      .395     .246  -.200000   .271900   .2637700    -3.700    -2.300  
+90 2 6 47928.00 I  -.202333  .000409   .275932  .000393  I  .2616887  .0000363  1.9591 0.0296  I    -5.695     .546      .180     .249  -.201400   .275700   .2617500    -2.100    -2.300  
+90 2 7 47929.00 I  -.203403  .000412   .279667  .000396  I  .2596837  .0000395  2.0662 0.0266  I    -5.608     .600     -.101     .263  -.202800   .279500   .2597000     -.800    -2.100  
+90 2 8 47930.00 I  -.204211  .000412   .283483  .000396  I  .2575309  .0000389  2.2494 0.0269  I    -5.613     .625     -.236     .303  -.203900   .283200   .2574600      .200    -1.900  
+90 2 9 47931.00 I  -.204694  .000411   .287359  .000390  I  .2551758  .0000365  2.4597 0.0243  I    -5.538     .844     -.095     .340  -.204500   .287000   .2550700      .400    -1.500  
+90 210 47932.00 I  -.204841  .000347   .291287  .000274  I  .2526216  .0000291  2.6396 0.0225  I    -5.155     .862      .128     .325  -.204300   .290600   .2525600     -.300    -1.100  
+90 211 47933.00 I  -.204676  .000350   .295263  .000311  I  .2499181  .0000263  2.7550 0.0194  I    -4.645     .867      .420     .335  -.203800   .294500   .2499100    -1.600     -.800  
+90 212 47934.00 I  -.204224  .000272   .299284  .000324  I  .2471377  .0000258  2.7920 0.0176  I    -4.496     .890      .322     .369  -.202800   .298600   .2471500    -3.000     -.500  
+90 213 47935.00 I  -.203533  .000284   .303354  .000268  I  .2443607  .0000233  2.7493 0.0169  I    -4.550     .871      .047     .367  -.202200   .302700   .2443800    -3.700     -.300  
+90 214 47936.00 I  -.202713  .000269   .307486  .000269  I  .2416612  .0000218  2.6402 0.0155  I    -4.588     .824     -.052     .328  -.201500   .306700   .2416800    -4.100     -.500  
+90 215 47937.00 I  -.201903  .000324   .311642  .000284  I  .2390961  .0000205  2.4838 0.0150  I    -4.580     .513     -.123     .251  -.200900   .310800   .2390900    -3.600     -.800  
+90 216 47938.00 I  -.201203  .000307   .315725  .000271  I  .2366996  .0000205  2.3096 0.0178  I    -4.658     .391     -.063     .266  -.200500   .314800   .2367000    -2.100    -1.400  
+90 217 47939.00 I  -.200751  .000374   .319639  .000354  I  .2344775  .0000291  2.1338 0.0170  I    -4.771     .417     -.063     .243  -.200500   .318800   .2344600     -.200    -1.800  
+90 218 47940.00 I  -.200722  .000365   .323310  .000303  I  .2324294  .0000271  1.9657 0.0198  I    -4.891     .467     -.211     .174  -.200300   .322600   .2324500     1.400    -2.300  
+90 219 47941.00 I  -.201068  .000448   .326771  .000282  I  .2305399  .0000269  1.8160 0.0177  I    -5.017     .440     -.584     .148  -.200400   .326300   .2306200     2.700    -2.700  
+90 220 47942.00 I  -.201659  .000427   .330102  .000285  I  .2287872  .0000227  1.6968 0.0190  I    -5.124     .550     -.975     .140  -.200600   .329800   .2289000     3.900    -3.000  
+90 221 47943.00 I  -.202289  .000455   .333378  .000237  I  .2271173  .0000269  1.6644 0.0206  I    -5.061     .594    -1.128     .136  -.200900   .333100   .2272200     4.800    -3.000  
+90 222 47944.00 I  -.202612  .000505   .336678  .000228  I  .2254160  .0000344  1.7591 0.0239  I    -4.895     .649    -1.067     .123  -.201300   .336400   .2254900     5.000    -2.700  
+90 223 47945.00 I  -.202312  .000558   .340074  .000252  I  .2235642  .0000395  1.9586 0.0248  I    -4.818     .696    -1.115     .135  -.200400   .339400   .2236000     4.500    -2.400  
+90 224 47946.00 I  -.201390  .000514   .343597  .000252  I  .2214781  .0000357  2.2204 0.0260  I    -4.681     .669    -1.271     .153  -.199300   .342800   .2214800     3.100    -2.100  
+90 225 47947.00 I  -.199994  .000487   .347250  .000302  I  .2191223  .0000337  2.4856 0.0304  I    -4.374     .535    -1.311     .142  -.198100   .346400   .2191200     1.100    -1.700  
+90 226 47948.00 I  -.198336  .000751   .351023  .000359  I  .2165307  .0000492  2.6796 0.0298  I    -4.120     .198    -1.136     .142  -.197000   .350000   .2165100    -1.500    -1.600  
+90 227 47949.00 I  -.196637  .000720   .354907  .000364  I  .2138054  .0000491  2.7468 0.0295  I    -4.212     .182     -.833     .151  -.195800   .353800   .2138000    -3.200    -1.600  
+90 228 47950.00 I  -.195118  .000689   .358888  .000626  I  .2110894  .0000326  2.6571 0.0276  I    -4.643     .182     -.531     .151  -.194700   .357900   .2111100    -4.200    -1.800  
+90 3 1 47951.00 I  -.193973  .000641   .362910  .000538  I  .2085379  .0000252  2.4273 0.0209  I    -5.149     .182     -.441     .151  -.193800   .361900   .2085800    -4.700    -2.000  
+90 3 2 47952.00 I  -.193306  .000527   .366897  .000465  I  .2062554  .0000262  2.1357 0.0188  I    -5.455     .206     -.687     .147  -.193100   .365800   .2063000    -4.700    -2.400  
+90 3 3 47953.00 I  -.192965  .000613   .370776  .000539  I  .2042577  .0000280  1.8706 0.0194  I    -5.458     .212    -1.084     .150  -.192500   .369800   .2043200    -4.000    -2.800  
+90 3 4 47954.00 I  -.192737  .000613   .374532  .000539  I  .2024849  .0000287  1.6936 0.0197  I    -5.273     .212    -1.425     .150  -.192000   .373700   .2025600    -3.700    -3.100  
+90 3 5 47955.00 I  -.192439  .000268   .378169  .000536  I  .2008309  .0000278  1.6354 0.0194  I    -5.072     .238    -1.603     .140  -.191800   .377700   .2009100    -2.900    -4.400  
+90 3 6 47956.00 I  -.192013  .000269   .381747  .000316  I  .1991734  .0000262  1.6992 0.0198  I    -4.927     .205    -1.679     .134  -.191000   .381600   .1992000    -1.600    -4.500  
+90 3 7 47957.00 I  -.191447  .000229   .385333  .000330  I  .1973997  .0000283  1.8612 0.0197  I    -4.810     .271    -1.754     .109  -.190400   .385100   .1974000     -.500    -4.200  
+90 3 8 47958.00 I  -.190752  .000206   .388961  .000386  I  .1954329  .0000293  2.0774 0.0202  I    -4.685     .291    -1.871     .298  -.189800   .388800   .1954000     -.100    -3.900  
+90 3 9 47959.00 I  -.189934  .000188   .392658  .000293  I  .1932407  .0000289  2.3060 0.0203  I    -4.569     .291    -2.021     .298  -.188800   .392400   .1932300     -.800    -2.900  
+90 310 47960.00 I  -.188990  .000188   .396433  .000293  I  .1908357  .0000282  2.4900 0.0206  I    -4.455     .291    -2.075     .298  -.187800   .395700   .1908200     -.200    -2.200  
+90 311 47961.00 I  -.187975  .000423   .400222  .000516  I  .1882909  .0000293  2.5833 0.0218  I    -4.336     .291    -2.058     .298  -.186900   .399300   .1882700    -1.100    -1.800  
+90 312 47962.00 I  -.186968  .000434   .403938  .000509  I  .1857013  .0000333  2.5801 0.0279  I    -4.191     .540    -2.087     .289  -.186100   .402900   .1856700    -2.300    -2.000  
+90 313 47963.00 I  -.186071  .000637   .407506  .000690  I  .1831593  .0000476  2.4910 0.0322  I    -4.072     .760    -2.190     .361  -.185500   .406600   .1831000    -3.200    -2.800  
+90 314 47964.00 I  -.185394  .000639   .410852  .000680  I  .1807412  .0000552  2.3360 0.0367  I    -4.119     .722    -2.247     .318  -.184900   .410200   .1806400    -4.100    -3.500  
+90 315 47965.00 I  -.184985  .000639   .413929  .000680  I  .1784922  .0000558  2.1661 0.0372  I    -4.374     .722    -2.114     .318  -.184800   .413200   .1783300    -5.400    -4.000  
+90 316 47966.00 I  -.184660  .000772   .416814  .000652  I  .1763986  .0000498  2.0274 0.0374  I    -4.518     .722    -2.007     .318  -.184200   .416100   .1762300    -6.100    -4.400  
+90 317 47967.00 I  -.184220  .000772   .419631  .000652  I  .1744241  .0000498  1.9286 0.0375  I    -4.489     .793    -2.115     .353  -.183700   .419200   .1743500    -5.800    -5.100  
+90 318 47968.00 I  -.183515  .000712   .422474  .000493  I  .1725242  .0000561  1.8815 0.0353  I    -4.359     .845    -2.468     .321  -.182800   .422100   .1724700    -4.700    -5.400  
+90 319 47969.00 I  -.182424  .000773   .425419  .000587  I  .1706383  .0000500  1.9028 0.0299  I    -4.103     .825    -2.917     .344  -.181800   .424900   .1706200    -3.400    -5.600  
+90 320 47970.00 I  -.180871  .000469   .428535  .000234  I  .1686948  .0000206  1.9954 0.0269  I    -3.685     .779    -3.155     .344  -.180100   .428200   .1687600    -1.600    -5.500  
+90 321 47971.00 I  -.178945  .000419   .431863  .000231  I  .1666247  .0000200  2.1563 0.0134  I    -3.270     .714    -3.080     .311  -.178400   .431500   .1667000     -.400    -5.400  
+90 322 47972.00 I  -.176800  .000453   .435421  .000293  I  .1643610  .0000172  2.3806 0.0130  I    -3.127     .702    -2.907     .257  -.176300   .434900   .1644100     -.100    -4.700  
+90 323 47973.00 I  -.174577  .000324   .439208  .000285  I  .1618486  .0000165  2.6495 0.0117  I    -3.345     .561    -2.908     .102  -.174200   .438600   .1618200     -.500    -4.000  
+90 324 47974.00 I  -.172343  .000349   .443146  .000315  I  .1590562  .0000158  2.9356 0.0103  I    -3.641     .561    -3.098     .102  -.171900   .442500   .1590600    -1.200    -3.500  
+90 325 47975.00 I  -.170198  .000291   .447045  .000249  I  .1559934  .0000124  3.1735 0.0097  I    -3.630     .561    -3.103     .102  -.169500   .446100   .1559800    -1.700    -3.100  
+90 326 47976.00 I  -.168148  .000247   .450794  .000234  I  .1527522  .0000114  3.2824 0.0093  I    -3.450     .539    -2.746     .104  -.167100   .449700   .1527600    -2.600    -2.900  
+90 327 47977.00 I  -.166137  .000369   .454332  .000375  I  .1494856  .0000138  3.2205 0.0087  I    -3.530     .690    -2.289     .298  -.164900   .453400   .1494900    -3.400    -2.800  
+90 328 47978.00 I  -.164136  .000354   .457615  .000354  I  .1463661  .0000132  2.9941 0.0096  I    -3.969     .626    -2.087     .298  -.162600   .456800   .1463300    -3.900    -3.200  
+90 329 47979.00 I  -.162161  .000354   .460647  .000354  I  .1435361  .0000133  2.6516 0.0115  I    -4.431     .626    -2.231     .298  -.160400   .460000   .1435100    -4.100    -3.600  
+90 330 47980.00 I  -.160228  .000322   .463506  .000351  I  .1410660  .0000189  2.2997 0.0110  I    -4.584     .626    -2.575     .298  -.158300   .463100   .1410500    -4.000    -4.100  
+90 331 47981.00 I  -.158241  .000306   .466349  .000337  I  .1389047  .0000176  2.0437 0.0134  I    -4.402     .626    -2.928     .298  -.156200   .466100   .1388900    -3.400    -4.700  
+90 4 1 47982.00 I  -.156117  .000418   .469229  .000441  I  .1369332  .0000190  1.9238 0.0161  I    -4.078     .617    -3.196     .298  -.154000   .469000   .1369100    -2.400    -4.800  
+90 4 2 47983.00 I  -.153796  .000396   .472157  .000493  I  .1350106  .0000269  1.9436 0.0165  I    -3.828     .491    -3.361     .136  -.151900   .471900   .1350100    -1.100    -4.700  
+90 4 3 47984.00 I  -.151225  .000385   .475109  .000307  I  .1330082  .0000270  2.0770 0.0191  I    -3.771     .511    -3.404     .159  -.149800   .474800   .1329900     -.300    -4.700  
+90 4 4 47985.00 I  -.148374  .000374   .478055  .000296  I  .1308397  .0000270  2.2611 0.0171  I    -3.878     .511    -3.323     .159  -.147600   .477600   .1308000     1.800    -4.900  
+90 4 5 47986.00 I  -.145312  .000355   .480999  .000164  I  .1284925  .0000210  2.4258 0.0162  I    -3.883     .511    -3.167     .159  -.145300   .480200   .1284300     2.300    -4.600  
+90 4 6 47987.00 I  -.142123  .000317   .483957  .000242  I  .1260024  .0000180  2.5475 0.0165  I    -3.741     .450    -3.132     .220  -.142500   .483200   .1259600     1.700    -4.100  
+90 4 7 47988.00 I  -.138849  .000329   .486959  .000319  I  .1234139  .0000254  2.6199 0.0161  I    -3.512     .464    -3.186     .248  -.139400   .486300   .1233900      .200    -3.700  
+90 4 8 47989.00 I  -.135526  .000252   .490037  .000293  I  .1207829  .0000266  2.6312 0.0166  I    -3.248     .304    -3.205     .270  -.136300   .489800   .1207400    -2.000    -3.800  
+90 4 9 47990.00 I  -.132195  .000359   .493212  .000316  I  .1181738  .0000214  2.5755 0.0162  I    -2.983     .297    -3.162     .301  -.132700   .493100   .1181400    -3.500    -3.700  
+90 410 47991.00 I  -.128937  .000397   .496456  .000358  I  .1156518  .0000186  2.4599 0.0123  I    -2.750     .245    -3.182     .257  -.129200   .496400   .1156300    -4.500    -3.700  
+90 411 47992.00 I  -.125835  .000308   .499714  .000340  I  .1132670  .0000123  2.3051 0.0118  I    -2.623     .202    -3.343     .234  -.125500   .499800   .1132800    -4.700    -4.100  
+90 412 47993.00 I  -.122922  .000357   .502925  .000341  I  .1110453  .0000146  2.1390 0.0090  I    -2.629     .791    -3.572     .152  -.122400   .502900   .1110100    -3.400    -4.700  
+90 413 47994.00 I  -.120184  .000357   .506020  .000341  I  .1089861  .0000132  1.9816 0.0128  I    -2.656     .791    -3.761     .152  -.119500   .506000   .1089800    -2.500    -5.400  
+90 414 47995.00 I  -.117489  .000344   .508907  .000342  I  .1070738  .0000211  1.8491 0.0122  I    -2.600     .791    -3.839     .152  -.116800   .508700   .1070600    -1.600    -6.000  
+90 415 47996.00 I  -.114801  .000341   .511621  .000319  I  .1052702  .0000205  1.7697 0.0136  I    -2.500     .791    -3.965     .147  -.114200   .511600   .1052700     -.500    -6.500  
+90 416 47997.00 I  -.112127  .000276   .514204  .000299  I  .1035097  .0000173  1.7642 0.0146  I    -2.449     .402    -4.201     .186  -.111600   .514200   .1035000      .700    -6.700  
+90 417 47998.00 I  -.109471  .000325   .516685  .000294  I  .1017159  .0000209  1.8363 0.0137  I    -2.440     .578    -4.385     .235  -.109200   .516200   .1016800     1.900    -6.300  
+90 418 47999.00 I  -.106858  .000346   .519103  .000319  I  .0998076  .0000213  1.9970 0.0149  I    -2.420     .633    -4.363     .234  -.106600   .518400   .0998100     2.400    -5.700  
+90 419 48000.00 I  -.104324  .000339   .521523  .000348  I  .0976962  .0000213  2.2349 0.0143  I    -2.360     .633    -4.108     .234  -.104100   .520900   .0977300     3.000    -4.900  
+90 420 48001.00 I  -.101893  .000293   .523999  .000333  I  .0953261  .0000191  2.5080 0.0129  I    -2.168     .633    -4.005     .234  -.101700   .523200   .0953800     2.700    -4.400  
+90 421 48002.00 I  -.099610  .000283   .526519  .000305  I  .0926861  .0000146  2.7636 0.0124  I    -2.009     .595    -4.077     .224  -.099500   .525300   .0927600     1.800    -3.800  
+90 422 48003.00 I  -.097463  .000276   .528988  .000358  I  .0898268  .0000157  2.9356 0.0113  I    -1.889     .513    -4.052     .208  -.097200   .527700   .0899300      .300    -3.500  
+90 423 48004.00 I  -.095416  .000274   .531306  .000368  I  .0868628  .0000172  2.9637 0.0121  I    -1.920     .363    -3.759     .122  -.094900   .530100   .0868500    -1.300    -3.600  
+90 424 48005.00 I  -.093418  .000231   .533400  .000354  I  .0839541  .0000185  2.8275 0.0123  I    -2.345     .241    -3.414     .124  -.092700   .532300   .0838600    -2.700    -3.900  
+90 425 48006.00 I  -.091348  .000215   .535243  .000316  I  .0812546  .0000175  2.5510 0.0139  I    -3.129     .230    -3.306     .112  -.090600   .534500   .0811100    -3.600    -4.400  
+90 426 48007.00 I  -.089161  .000273   .536945  .000300  I  .0788778  .0000207  2.1976 0.0157  I    -3.837     .194    -3.545     .121  -.088400   .536700   .0787500    -3.700    -4.800  
+90 427 48008.00 I  -.086878  .000303   .538638  .000332  I  .0768512  .0000261  1.8688 0.0186  I    -4.046     .171    -3.976     .106  -.086000   .538500   .0767500    -3.000    -5.500  
+90 428 48009.00 I  -.084572  .000304   .540379  .000348  I  .0751104  .0000308  1.6295 0.0199  I    -3.799     .183    -4.347     .106  -.083800   .540300   .0750700    -2.000    -5.900  
+90 429 48010.00 I  -.082361  .000273   .542122  .000345  I  .0735487  .0000300  1.5208 0.0204  I    -3.343     .183    -4.605     .106  -.081600   .541900   .0736400    -1.000    -6.200  
+90 430 48011.00 I  -.080182  .000277   .543839  .000270  I  .0720199  .0000268  1.5589 0.0199  I    -2.922     .791    -4.770     .104  -.079200   .543400   .0720700     -.400    -6.300  
+90 5 1 48012.00 I  -.077877  .000256   .545523  .000262  I  .0703975  .0000263  1.6971 0.0195  I    -2.749     .192    -4.768     .146  -.076700   .545100   .0704400      .000    -5.900  
+90 5 2 48013.00 I  -.075287  .000184   .547166  .000300  I  .0686133  .0000282  1.8724 0.0198  I    -2.935     .300    -4.540     .141  -.073900   .546600   .0686400     -.400    -5.300  
+90 5 3 48014.00 I  -.072276  .000184   .548758  .000300  I  .0666583  .0000295  2.0307 0.0182  I    -3.341     .300    -4.205     .141  -.070800   .548100   .0666600     -.800    -5.100  
+90 5 4 48015.00 I  -.068759  .000183   .550281  .000242  I  .0645693  .0000230  2.1367 0.0181  I    -3.624     .300    -4.009     .141  -.067100   .549600   .0645800    -2.400    -5.100  
+90 5 5 48016.00 I  -.064784  .000209   .551772  .000244  I  .0624088  .0000210  2.1710 0.0162  I    -3.476     .305    -3.930     .143  -.063000   .551100   .0623900    -2.000    -5.300  
+90 5 6 48017.00 I  -.060468  .000371   .553258  .000220  I  .0602525  .0000229  2.1295 0.0176  I    -3.004     .346    -3.852     .157  -.058800   .552700   .0602400    -1.600    -5.500  
+90 5 7 48018.00 I  -.055942  .000445   .554752  .000241  I  .0581712  .0000282  2.0237 0.0171  I    -2.551     .694    -3.796     .149  -.054600   .554200   .0581700    -1.100    -5.700  
+90 5 8 48019.00 I  -.051343  .000453   .556267  .000227  I  .0562209  .0000255  1.8706 0.0179  I    -2.348     .735    -3.915     .156  -.050100   .555700   .0562200     -.600    -5.800  
+90 5 9 48020.00 I  -.046853  .000453   .557798  .000227  I  .0544325  .0000222  1.7102 0.0163  I    -2.366     .735    -4.162     .156  -.045800   .557300   .0544100     -.300    -5.900  
+90 510 48021.00 I  -.042609  .000435   .559319  .000288  I  .0527930  .0000204  1.5725 0.0150  I    -2.447     .735    -4.518     .156  -.041600   .558800   .0527700      .100    -5.800  
+90 511 48022.00 I  -.038544  .000425   .560777  .000336  I  .0512772  .0000202  1.4659 0.0151  I    -2.499     .713    -4.814     .194  -.037200   .560300   .0512600      .300    -5.800  
+90 512 48023.00 I  -.034545  .000456   .562139  .000391  I  .0498442  .0000222  1.4104 0.0155  I    -2.506     .839    -4.900     .212  -.032900   .561800   .0498300      .500    -5.600  
+90 513 48024.00 I  -.030505  .000373   .563381  .000472  I  .0484348  .0000236  1.4198 0.0168  I    -2.579     .460    -4.840     .216  -.028600   .563100   .0484100      .700    -5.500  
+90 514 48025.00 I  -.026329  .000361   .564505  .000478  I  .0469792  .0000253  1.5054 0.0165  I    -2.889     .439    -4.732     .192  -.024200   .564300   .0469400      .600    -5.100  
+90 515 48026.00 I  -.022010  .000350   .565557  .000391  I  .0453965  .0000230  1.6737 0.0182  I    -3.287     .439    -4.619     .192  -.019800   .565300   .0453300      .500    -4.800  
+90 516 48027.00 I  -.017588  .000330   .566569  .000387  I  .0436153  .0000263  1.8917 0.0191  I    -3.416     .432    -4.493     .186  -.015600   .566300   .0435700      .300    -4.400  
+90 517 48028.00 I  -.013173  .000353   .567518  .000398  I  .0416069  .0000304  2.1282 0.0204  I    -3.169     .399    -4.325     .142  -.011300   .567200   .0415600      .000    -4.000  
+90 518 48029.00 I  -.008793  .000353   .568420  .000398  I  .0393602  .0000313  2.3608 0.0203  I    -2.805     .399    -4.245     .142  -.007000   .568000   .0393400     -.400    -3.700  
+90 519 48030.00 I  -.004462  .000316   .569293  .000294  I  .0369007  .0000268  2.5473 0.0204  I    -2.654     .403    -4.302     .151  -.002800   .568800   .0368700     -.900    -3.600  
+90 520 48031.00 I  -.000214  .000217   .570103  .000291  I  .0342940  .0000262  2.6487 0.0165  I    -2.790     .345    -4.313     .147   .001200   .569500   .0342700    -1.400    -3.500  
+90 521 48032.00 I   .003913  .000183   .570813  .000254  I  .0316432  .0000193  2.6302 0.0152  I    -3.185     .429    -4.110     .155   .005100   .570100   .0316000    -1.900    -3.400  
+90 522 48033.00 I   .007879  .000146   .571392  .000190  I  .0290757  .0000155  2.4852 0.0124  I    -3.788     .429    -3.830     .129   .009200   .570600   .0290200    -2.400    -3.400  
+90 523 48034.00 I   .011670  .000160   .571830  .000189  I  .0267069  .0000157  2.2379 0.0108  I    -4.365     .394    -3.772     .120   .013000   .571000   .0266500    -2.700    -3.500  
+90 524 48035.00 I   .015380  .000160   .572205  .000189  I  .0246173  .0000149  1.9384 0.0106  I    -4.574     .394    -4.002     .120   .016600   .571100   .0246100    -2.400    -3.700  
+90 525 48036.00 I   .019079  .000165   .572518  .000213  I  .0228185  .0000143  1.6737 0.0096  I    -4.438     .394    -4.247     .120   .020300   .571200   .0228400    -2.100    -4.000  
+90 526 48037.00 I   .022725  .000165   .572767  .000271  I  .0212270  .0000120  1.5376 0.0091  I    -4.175     .387    -4.290     .177   .023700   .571200   .0212300    -1.700    -4.200  
+90 527 48038.00 I   .026334  .000192   .572981  .000260  I  .0196908  .0000114  1.5590 0.0164  I    -3.933     .271    -4.241     .188   .027200   .571300   .0196800    -1.300    -4.500  
+90 528 48039.00 I   .029941  .000198   .573192  .000318  I  .0180715  .0000305  1.6930 0.0262  I    -3.807     .353    -4.307     .196   .030800   .571300   .0180200    -1.100    -4.700  
+90 529 48040.00 I   .033584  .000204   .573433  .000511  I  .0162917  .0000512  1.8663 0.0272  I    -3.956     .404    -4.500     .222   .034500   .571700   .0162200    -1.600    -4.700  
+90 530 48041.00 I   .037283  .000228   .573697  .000450  I  .0143478  .0000451  2.0128 0.0320  I    -4.445     .404    -4.561     .222   .038100   .572000   .0142600    -2.300    -4.700  
+90 531 48042.00 I   .041030  .000231   .573946  .000393  I  .0122870  .0000384  2.0967 0.0340  I    -5.012     .434    -4.352     .174   .041900   .572400   .0122100    -2.900    -4.600  
+90 6 1 48043.00 I   .044816  .000244   .574165  .000259  I  .0101794  .0000510  2.1057 0.0315  I    -5.291     .523    -4.111     .298   .045600   .572700   .0101000    -3.500    -4.600  
+90 6 2 48044.00 I   .048642  .000236   .574355  .000457  I  .0080994  .0000499  2.0433 0.0314  I    -5.121     .523    -4.051     .298   .049400   .573000   .0080500    -4.000    -4.500  
+90 6 3 48045.00 I   .052531  .000248   .574522  .000441  I  .0061118  .0000366  1.9238 0.0261  I    -4.782     .516    -4.117     .298   .053300   .573300   .0060900    -4.200    -4.500  
+90 6 4 48046.00 I   .056477  .000246   .574617  .000406  I  .0042663  .0000155  1.7609 0.0196  I    -4.617     .516    -4.167     .298   .057200   .573600   .0042500    -3.800    -4.600  
+90 6 5 48047.00 I   .060427  .000241   .574560  .000365  I  .0025991  .0000143  1.5707 0.0105  I    -4.691     .614    -4.241     .298   .061100   .573700   .0025500    -3.500    -4.700  
+90 6 6 48048.00 I   .064325  .000400   .574288  .000491  I  .0011235  .0000141  1.3846 0.0108  I    -4.816     .694    -4.484     .298   .065000   .573700   .0010700    -3.200    -4.800  
+90 6 7 48049.00 I   .068136  .000433   .573790  .000485  I -.0001825  .0000162  1.2356 0.0116  I    -4.988     .613    -4.810     .298   .068900   .573700  -.0002400    -2.800    -4.900  
+90 6 8 48050.00 I   .071863  .000440   .573089  .000514  I -.0013675  .0000184  1.1461 0.0111  I    -5.352     .637    -4.919     .108   .072800   .572500  -.0014200    -3.100    -5.000  
+90 6 9 48051.00 I   .075621  .000412   .572246  .000287  I -.0025001  .0000153  1.1328 0.0119  I    -5.733     .600    -4.713     .123   .076700   .571500  -.0025700    -3.200    -5.100  
+90 610 48052.00 I   .079542  .000366   .571331  .000403  I -.0036596  .0000151  1.1989 0.0117  I    -5.847     .582    -4.407     .116   .080600   .570500  -.0037300    -3.500    -5.200  
+90 611 48053.00 I   .083643  .000411   .570420  .000473  I -.0049200  .0000177  1.3313 0.0117  I    -5.677     .492    -4.286     .127   .084400   .569500  -.0049900    -3.400    -5.200  
+90 612 48054.00 I   .087869  .000420   .569582  .000487  I -.0063350  .0000178  1.5021 0.0127  I    -5.566     .492    -4.224     .127   .088400   .568400  -.0063900    -3.600    -5.100  
+90 613 48055.00 I   .092108  .000342   .568860  .000464  I -.0079295  .0000183  1.6887 0.0121  I    -5.668     .529    -4.056     .138   .092200   .567900  -.0080100    -3.900    -5.000  
+90 614 48056.00 I   .096265  .000276   .568224  .000587  I -.0097134  .0000164  1.8782 0.0115  I    -5.743     .468    -3.892     .118   .096200   .567300  -.0098000    -4.200    -4.900  
+90 615 48057.00 I   .100309  .000228   .567608  .000586  I -.0116770  .0000138  2.0412 0.0125  I    -5.677     .417    -3.856     .120   .100100   .566600  -.0117500    -4.500    -5.000  
+90 616 48058.00 I   .104234  .000264   .566950  .000681  I -.0137740  .0000189  2.1390 0.0132  I    -5.610     .312    -3.958     .133   .104100   .565300  -.0138400    -4.600    -5.100  
+90 617 48059.00 I   .108071  .000251   .566198  .000682  I -.0159224  .0000226  2.1390 0.0158  I    -5.738     .385    -4.124     .267   .108100   .564300  -.0159800    -4.700    -5.100  
+90 618 48060.00 I   .111870  .000302   .565323  .000664  I -.0180150  .0000252  2.0279 0.0177  I    -6.171     .508    -4.257     .326   .112100   .563000  -.0180900    -4.700    -5.000  
+90 619 48061.00 I   .115674  .000279   .564316  .000629  I -.0199498  .0000273  1.8315 0.0175  I    -6.752     .517    -4.225     .309   .116100   .561900  -.0200200    -4.500    -5.300  
+90 620 48062.00 I   .119509  .000319   .563148  .000493  I -.0216658  .0000244  1.5985 0.0216  I    -7.158     .468    -3.999     .281   .119900   .560700  -.0217500    -4.500    -5.300  
+90 621 48063.00 I   .123347  .000391   .561823  .000368  I -.0231539  .0000335  1.3868 0.0208  I    -7.316     .499    -4.027     .305   .123700   .559800  -.0232500    -4.400    -5.500  
+90 622 48064.00 I   .127096  .000417   .560361  .000373  I -.0244627  .0000338  1.2450 0.0224  I    -7.168     .499    -4.136     .305   .127300   .558400  -.0245600    -4.500    -5.400  
+90 623 48065.00 I   .130620  .000492   .558791  .000411  I -.0256832  .0000298  1.2213 0.0217  I    -6.926     .482    -4.188     .271   .130600   .556900  -.0257500    -4.800    -5.200  
+90 624 48066.00 I   .134018  .000499   .557053  .000398  I -.0269483  .0000273  1.3267 0.0181  I    -6.675     .451    -4.120     .187   .133800   .555400  -.0269900    -5.400    -5.000  
+90 625 48067.00 I   .137425  .000430   .555162  .000371  I -.0283598  .0000204  1.5016 0.0202  I    -6.414     .443    -4.020     .163   .137100   .553600  -.0283900    -5.800    -4.800  
+90 626 48068.00 I   .140913  .000396   .553151  .000354  I -.0299500  .0000298  1.6721 0.0176  I    -6.327     .479    -3.953     .144   .140600   .551900  -.0299700    -6.400    -4.300  
+90 627 48069.00 I   .144519  .000487   .551067  .000391  I -.0316821  .0000288  1.7770 0.0211  I    -6.739     .448    -3.849     .132   .144000   .549800  -.0317100    -6.800    -4.300  
+90 628 48070.00 I   .148236  .000508   .549015  .000400  I -.0334770  .0000298  1.8011 0.0201  I    -7.601     .436    -3.766     .132   .148100   .547900  -.0335000    -7.100    -4.200  
+90 629 48071.00 I   .152136  .000472   .547071  .000406  I -.0352622  .0000281  1.7591 0.0212  I    -8.430     .451    -3.773     .134   .152300   .546000  -.0352800    -7.200    -4.500  
+90 630 48072.00 I   .156303  .000383   .545287  .000356  I -.0369802  .0000301  1.6721 0.0226  I    -8.728     .370    -3.828     .113   .156400   .544100  -.0370400    -7.300    -4.500  
+90 7 1 48073.00 I   .160777  .000413   .543635  .000384  I -.0385964  .0000355  1.5552 0.0210  I    -8.542     .290    -3.825     .111   .161100   .542400  -.0386700    -7.600    -4.700  
+90 7 2 48074.00 I   .165414  .000413   .542060  .000384  I -.0400777  .0000293  1.3995 0.0244  I    -8.459     .290    -3.787     .111   .165400   .540500  -.0401500    -7.500    -4.800  
+90 7 3 48075.00 I   .169947  .000490   .540503  .000416  I -.0413918  .0000336  1.2330 0.0209  I    -8.787     .314    -3.754     .117   .170000   .539600  -.0414200    -7.300    -4.900  
+90 7 4 48076.00 I   .174278  .000330   .538913  .000387  I -.0425512  .0000299  1.0891 0.0212  I    -9.397     .495    -3.703     .118   .174600   .538700  -.0425100    -7.900    -4.700  
+90 7 5 48077.00 I   .178387  .000231   .537236  .000331  I -.0435793  .0000260  0.9732 0.0234  I    -9.992     .586    -3.676     .122   .179100   .537200  -.0435200    -8.400    -4.300  
+90 7 6 48078.00 I   .182250  .000222   .535415  .000287  I -.0445130  .0000359  0.9034 0.0203  I   -10.323     .709    -3.672     .144   .182800   .535400  -.0445600    -8.800    -4.000  
+90 7 7 48079.00 I   .185852  .000431   .533390  .000291  I -.0454012  .0000312  0.8789 0.0240  I   -10.246     .813    -3.639     .140   .186200   .533000  -.0454800    -9.200    -4.100  
+90 7 8 48080.00 I   .189216  .000440   .531114  .000407  I -.0462908  .0000320  0.9146 0.0209  I   -10.004     .952    -3.487     .208   .189300   .530300  -.0463400    -9.700    -4.000  
+90 7 9 48081.00 I   .192463  .000407   .528646  .000394  I -.0472579  .0000278  1.0330 0.0219  I    -9.772     .919    -3.317     .192   .192800   .527400  -.0473200    -9.700    -4.100  
+90 710 48082.00 I   .195765  .000414   .526086  .000360  I -.0483778  .0000298  1.2145 0.0232  I    -9.655     .813    -3.273     .178   .196300   .525100  -.0484700    -9.800    -4.200  
+90 711 48083.00 I   .199218  .000546   .523520  .000426  I -.0496942  .0000371  1.4180 0.0221  I    -9.746    1.138    -3.366     .177   .199700   .522400  -.0497600    -9.400    -4.300  
+90 712 48084.00 I   .202743  .000534   .521000  .000465  I -.0512015  .0000327  1.5845 0.0246  I   -10.081    1.175    -3.539     .170   .202900   .520000  -.0512800    -9.100    -4.400  
+90 713 48085.00 I   .206192  .000503   .518522  .000463  I -.0528385  .0000324  1.6768 0.0229  I   -10.332    1.175    -3.763     .170   .206000   .517200  -.0529500    -9.100    -4.500  
+90 714 48086.00 I   .209567  .000392   .516057  .000444  I -.0545233  .0000320  1.6738 0.0216  I   -10.065    1.191    -3.973     .298   .209200   .514500  -.0546100    -9.300    -4.700  
+90 715 48087.00 I   .212890  .000335   .513585  .000415  I -.0561489  .0000287  1.5591 0.0215  I    -9.812    1.064    -4.033     .104   .212500   .511900  -.0562100    -9.600    -4.800  
+90 716 48088.00 I   .216181  .000356   .511092  .000591  I -.0576099  .0000288  1.3497 0.0250  I    -9.970    1.134    -3.942     .298   .216100   .510000  -.0576800    -9.800    -4.900  
+90 717 48089.00 I   .219452  .000200   .508596  .000458  I -.0588309  .0000409  1.0881 0.0238  I   -10.401     .738    -3.848     .298   .219900   .507900  -.0588900   -10.000    -4.700  
+90 718 48090.00 I   .222704  .000204   .506113  .000331  I -.0597892  .0000378  0.8357 0.0272  I   -10.858     .318    -3.894     .109   .223700   .505900  -.0598600   -10.000    -4.200  
+90 719 48091.00 I   .225900  .000194   .503614  .000323  I -.0605297  .0000358  0.6656 0.0249  I   -11.204     .429    -4.072     .101   .227400   .503400  -.0605700   -10.000    -4.100  
+90 720 48092.00 I   .229029  .000173   .501038  .000311  I -.0611658  .0000323  0.6319 0.0256  I   -11.449     .722    -4.181     .127   .230500   .500700  -.0612600    -9.400    -4.000  
+90 721 48093.00 I   .232094  .000233   .498320  .000411  I -.0618410  .0000367  0.7409 0.0240  I   -11.626     .821    -4.029     .134   .233600   .497600  -.0619500    -9.200    -3.900  
+90 722 48094.00 I   .235100  .000290   .495415  .000398  I -.0626823  .0000356  0.9540 0.0223  I   -11.579     .822    -3.786     .159   .236200   .494500  -.0627700    -9.000    -3.800  
+90 723 48095.00 I   .238069  .000286   .492348  .000415  I -.0637592  .0000252  1.1976 0.0218  I   -11.343     .754    -3.696     .181   .238800   .491200  -.0638100    -9.600    -4.100  
+90 724 48096.00 I   .241052  .000259   .489190  .000445  I -.0650623  .0000250  1.3957 0.0164  I   -11.146     .777    -3.725     .173   .241800   .488000  -.0651100   -10.100    -4.300  
+90 725 48097.00 I   .244089  .000265   .486004  .000452  I -.0665222  .0000211  1.5085 0.0175  I   -11.354     .751    -3.702     .247   .244900   .484600  -.0665400   -10.700    -4.500  
+90 726 48098.00 I   .247129  .000317   .482782  .000503  I -.0680495  .0000246  1.5319 0.0192  I   -12.138     .556    -3.742     .277   .248000   .481400  -.0680900   -10.800    -5.100  
+90 727 48099.00 I   .250068  .000317   .479505  .000503  I -.0695657  .0000320  1.4945 0.0211  I   -13.139     .556    -3.769     .277   .251000   .478200  -.0695900   -10.900    -5.400  
+90 728 48100.00 I   .252813  .000270   .476174  .000331  I -.0710254  .0000342  1.4177 0.0243  I   -13.962     .596    -3.601     .287   .253800   .474900  -.0710300   -11.200    -5.500  
+90 729 48101.00 I   .255333  .000245   .472800  .000284  I -.0723894  .0000365  1.3059 0.0234  I   -14.262     .578    -3.468     .292   .256300   .471800  -.0724200   -11.600    -5.200  
+90 730 48102.00 I   .257658  .000231   .469387  .000251  I -.0736311  .0000320  1.1760 0.0288  I   -14.137     .753    -3.410     .286   .258500   .468500  -.0736800   -12.000    -4.800  
+90 731 48103.00 I   .259827  .000415   .465943  .000403  I -.0747407  .0000446  1.0442 0.0271  I   -13.945     .730    -3.297     .233   .260500   .465200  -.0748300   -12.200    -4.000  
+90 8 1 48104.00 I   .261882  .000374   .462474  .000398  I -.0757207  .0000437  0.9160 0.0285  I   -13.870     .730    -3.145     .198   .262500   .462100  -.0757800   -12.900    -3.600  
+90 8 2 48105.00 I   .263860  .000371   .458987  .000379  I -.0765851  .0000356  0.8248 0.0288  I   -13.848     .715    -3.185     .184   .264200   .457800  -.0766300   -13.200    -3.300  
+90 8 3 48106.00 I   .265854  .000381   .455485  .000394  I -.0773983  .0000375  0.8174 0.0258  I   -13.945     .715    -3.272     .184   .266000   .454300  -.0774500   -13.400    -2.800  
+90 8 4 48107.00 I   .267940  .000393   .451994  .000406  I -.0782502  .0000373  0.9010 0.0243  I   -14.230     .758    -3.269     .182   .268700   .451400  -.0782800   -13.500    -2.500  
+90 8 5 48108.00 I   .270121  .000427   .448524  .000426  I -.0792279  .0000308  1.0675 0.0266  I   -14.425     .512    -3.215     .109   .271900   .448500  -.0792400   -14.000    -2.300  
+90 8 6 48109.00 I   .272360  .000524   .445084  .000560  I -.0804064  .0000378  1.2975 0.0270  I   -14.370     .536    -3.177     .298   .274300   .445500  -.0804500   -14.000    -2.400  
+90 8 7 48110.00 I   .274589  .000390   .441685  .000522  I -.0818318  .0000444  1.5542 0.0298  I   -14.208     .400    -3.200     .247   .276500   .442500  -.0818700   -14.200    -2.400  
+90 8 8 48111.00 I   .276741  .000360   .438343  .000506  I -.0835090  .0000461  1.7934 0.0278  I   -14.182     .250    -3.268     .318   .278200   .439000  -.0835500   -14.400    -2.600  
+90 8 9 48112.00 I   .278754  .000292   .435072  .000648  I -.0853967  .0000333  1.9667 0.0315  I   -14.459     .287    -3.311     .238   .279800   .435300  -.0854300   -14.700    -2.900  
+90 810 48113.00 I   .280596  .000292   .431901  .000648  I -.0874084  .0000430  2.0380 0.0264  I   -14.914     .289    -3.134     .220   .281200   .431200  -.0874800   -14.700    -3.500  
+90 811 48114.00 I   .282279  .000333   .428841  .000699  I -.0894342  .0000411  1.9938 0.0303  I   -15.264     .289    -3.016     .220   .282600   .427500  -.0894900   -14.800    -3.800  
+90 812 48115.00 I   .283818  .000399   .425877  .000724  I -.0913622  .0000428  1.8481 0.0285  I   -15.387     .260    -3.111     .222   .284000   .424500  -.0914600   -14.400    -4.000  
+90 813 48116.00 I   .285247  .000319   .422970  .000641  I -.0931091  .0000396  1.6384 0.0258  I   -15.368     .299    -3.360     .152   .285700   .421600  -.0933000   -13.900    -3.900  
+90 814 48117.00 I   .286585  .000372   .420014  .000600  I -.0946352  .0000288  1.4172 0.0261  I   -15.286     .530    -3.611     .107   .286900   .418600  -.0948000   -13.400    -3.900  
+90 815 48118.00 I   .287859  .000423   .416916  .000672  I -.0959629  .0000340  1.2543 0.0180  I   -15.186     .580    -3.664     .119   .288200   .416000  -.0960600   -12.600    -3.700  
+90 816 48119.00 I   .289172  .000436   .413704  .000561  I -.0971804  .0000217  1.2013 0.0226  I   -15.126     .621    -3.537     .126   .290000   .413000  -.0972400   -11.500    -4.100  
+90 817 48120.00 I   .290686  .000436   .410418  .000561  I -.0984078  .0000298  1.2753 0.0125  I   -15.245     .621    -3.377     .126   .291700   .409300  -.0984100   -11.200    -4.400  
+90 818 48121.00 I   .292427  .000296   .407051  .000291  I -.0997677  .0000123  1.4593 0.0164  I   -15.362     .692    -3.271     .123   .293400   .405500  -.0998400   -11.800    -4.500  
+90 819 48122.00 I   .294320  .000452   .403618  .000273  I -.1013457  .0000137  1.7011 0.0106  I   -15.271     .625    -3.181     .134   .295400   .401900  -.1014400   -12.800    -4.400  
+90 820 48123.00 I   .296284  .000458   .400149  .000219  I -.1031677  .0000172  1.9361 0.0110  I   -14.941     .455    -3.074     .118   .297500   .398400  -.1032600   -14.100    -4.200  
+90 821 48124.00 I   .298236  .000471   .396673  .000117  I -.1051968  .0000173  2.1077 0.0122  I   -14.468     .441    -2.992     .114   .299600   .395700  -.1053400   -15.100    -3.700  
+90 822 48125.00 I   .300054  .000471   .393217  .000117  I -.1073506  .0000173  2.1816 0.0112  I   -14.067     .441    -2.996     .114   .301500   .392900  -.1074500   -15.300    -3.600  
+90 823 48126.00 I   .301644  .000535   .389767  .000169  I -.1095274  .0000143  2.1576 0.0111  I   -14.020     .441    -3.104     .114   .303000   .389800  -.1096500   -15.100    -3.200  
+90 824 48127.00 I   .302975  .000571   .386306  .000172  I -.1116384  .0000140  2.0516 0.0178  I   -14.442     .379    -3.273     .103   .304100   .386400  -.1117600   -15.100    -3.600  
+90 825 48128.00 I   .304034  .000774   .382824  .000286  I -.1136104  .0000327  1.8852 0.0153  I   -15.057     .314    -3.432     .298   .305000   .382300  -.1137100   -14.900    -4.000  
+90 826 48129.00 I   .304812  .000807   .379313  .000279  I -.1153995  .0000271  1.6908 0.0211  I   -15.493     .314    -3.501     .298   .305800   .378400  -.1154700   -14.700    -4.300  
+90 827 48130.00 I   .305343  .000830   .375769  .000344  I -.1169930  .0000267  1.4993 0.0179  I   -15.571     .194    -3.458     .298   .305800   .375100  -.1170600   -15.000    -4.100  
+90 828 48131.00 I   .305706  .000725   .372194  .000306  I -.1184076  .0000235  1.3362 0.0156  I   -15.369     .194    -3.368     .298   .306000   .371900  -.1184500   -14.900    -4.000  
+90 829 48132.00 I   .305957  .000533   .368585  .000271  I -.1196803  .0000163  1.2179 0.0143  I   -15.088     .171    -3.322     .298   .306200   .368500  -.1197400   -14.500    -3.600  
+90 830 48133.00 I   .306169  .000395   .364943  .000212  I -.1208648  .0000162  1.1637 0.0126  I   -14.878     .140    -3.311     .298   .306400   .364700  -.1209200   -13.100    -3.600  
+90 831 48134.00 I   .306493  .000395   .361281  .000212  I -.1220331  .0000191  1.1856 0.0130  I   -14.829     .140    -3.260     .298   .307000   .360600  -.1220800   -12.500    -3.600  
+90 9 1 48135.00 I   .307079  .000395   .357616  .000212  I -.1232639  .0000204  1.2912 0.0167  I   -14.952     .140    -3.135     .298   .307700   .356600  -.1233000   -12.400    -3.500  
+90 9 2 48136.00 I   .307923  .000356   .353948  .000293  I -.1246436  .0000273  1.4811 0.0160  I   -15.171     .129    -2.994     .298   .308300   .352500  -.1246200   -12.600    -3.400  
+90 9 3 48137.00 I   .308928  .000368   .350259  .000284  I -.1262465  .0000247  1.7321 0.0217  I   -15.405     .416    -2.924     .206   .309300   .348600  -.1261700   -13.000    -3.300  
+90 9 4 48138.00 I   .309992  .000434   .346527  .000358  I -.1281148  .0000338  2.0040 0.0209  I   -15.643     .560    -2.938     .394   .310300   .344800  -.1280700   -13.200    -3.400  
+90 9 5 48139.00 I   .311003  .000431   .342728  .000340  I -.1302441  .0000338  2.2447 0.0224  I   -15.972     .504    -2.998     .389   .311700   .341400  -.1303100   -13.700    -3.300  
+90 9 6 48140.00 I   .311833  .000403   .338838  .000279  I -.1325698  .0000294  2.3821 0.0227  I   -16.510     .504    -3.067     .389   .312100   .337500  -.1327600   -14.300    -3.300  
+90 9 7 48141.00 I   .312470  .000430   .334859  .000304  I -.1349571  .0000302  2.3658 0.0196  I   -17.064     .460    -2.839     .352   .312800   .333600  -.1351200   -14.500    -3.500  
+90 9 8 48142.00 I   .312954  .000463   .330804  .000370  I -.1372534  .0000260  2.2057 0.0184  I   -17.382     .591    -2.650     .316   .313500   .329700  -.1374100   -14.200    -3.600  
+90 9 9 48143.00 I   .313259  .000442   .326689  .000364  I -.1393367  .0000209  1.9504 0.0178  I   -17.322     .712    -2.727     .317   .313600   .325700  -.1394800   -14.500    -3.600  
+90 910 48144.00 I   .313337  .000442   .322545  .000351  I -.1411452  .0000242  1.6680 0.0173  I   -16.910     .726    -2.871     .232   .313700   .321600  -.1412500   -14.600    -3.900  
+90 911 48145.00 I   .313177  .000443   .318438  .000365  I -.1426925  .0000275  1.4449 0.0195  I   -16.405     .776    -2.998     .203   .313700   .317500  -.1427300   -14.900    -3.500  
+90 912 48146.00 I   .312883  .000413   .314378  .000348  I -.1440792  .0000307  1.3545 0.0202  I   -15.884     .776    -3.069     .203   .313400   .313500  -.1441000   -15.100    -3.500  
+90 913 48147.00 I   .312523  .000418   .310356  .000386  I -.1454500  .0000295  1.4097 0.0219  I   -15.574     .732    -3.121     .182   .312900   .309400  -.1454700   -15.200    -3.400  
+90 914 48148.00 I   .312119  .000440   .306392  .000372  I -.1469374  .0000312  1.5813 0.0230  I   -15.571     .690    -3.195     .167   .312600   .305600  -.1469700   -15.200    -3.800  
+90 915 48149.00 I   .311661  .000307   .302501  .000262  I -.1486353  .0000352  1.8210 0.0229  I   -15.637     .456    -3.255     .298   .311800   .301500  -.1486700   -14.800    -3.800  
+90 916 48150.00 I   .311131  .000336   .298699  .000256  I -.1505805  .0000334  2.0641 0.0230  I   -15.505     .325    -3.212     .298   .310800   .297400  -.1505900   -14.600    -4.000  
+90 917 48151.00 I   .310531  .000404   .294983  .000247  I -.1527464  .0000296  2.2559 0.0225  I   -15.167     .325    -3.000     .298   .310000   .293700  -.1527900   -14.700    -4.000  
+90 918 48152.00 I   .309871  .000464   .291347  .000268  I -.1550642  .0000303  2.3635 0.0226  I   -14.817     .493    -2.653     .298   .309500   .290500  -.1551600   -14.800    -3.900  
+90 919 48153.00 I   .309160  .000847   .287783  .000283  I -.1574408  .0000342  2.3734 0.0234  I   -14.667     .622    -2.311     .298   .309400   .287000  -.1575100   -14.800    -3.600  
+90 920 48154.00 I   .308393  .000847   .284282  .000283  I -.1597815  .0000357  2.2949 0.0268  I   -14.846     .622    -2.125     .298   .309100   .283700  -.1598600   -14.800    -3.400  
+90 921 48155.00 I   .307506  .000847   .280822  .000283  I -.1620083  .0000412  2.1496 0.0231  I   -15.359     .622    -2.145     .298   .308700   .280400  -.1620200   -14.800    -3.300  
+90 922 48156.00 I   .306397  .000611   .277373  .000480  I -.1640705  .0000294  1.9736 0.0223  I   -15.866     .622    -2.293     .298   .307900   .277000  -.1641000   -14.700    -3.300  
+90 923 48157.00 I   .305056  .000409   .273887  .000460  I -.1659581  .0000169  1.8055 0.0177  I   -16.053     .523    -2.428     .298   .306500   .273600  -.1660200   -14.500    -3.200  
+90 924 48158.00 I   .303574  .000416   .270303  .000481  I -.1676926  .0000196  1.6709 0.0114  I   -15.806     .395    -2.481     .298   .304900   .270000  -.1677500   -14.200    -3.200  
+90 925 48159.00 I   .302063  .000205   .266557  .000403  I -.1693171  .0000154  1.5880 0.0128  I   -15.223     .279    -2.527     .298   .303100   .266300  -.1694200   -14.300    -3.200  
+90 926 48160.00 I   .300634  .000205   .262617  .000403  I -.1708895  .0000166  1.5678 0.0107  I   -14.637     .282    -2.687     .298   .301000   .262200  -.1710400   -14.300    -3.300  
+90 927 48161.00 I   .299388  .000186   .258587  .000372  I -.1724761  .0000149  1.6178 0.0128  I   -14.285     .282    -2.973     .298   .299600   .257900  -.1726200   -14.300    -3.500  
+90 928 48162.00 I   .298364  .000168   .254585  .000423  I -.1741494  .0000195  1.7409 0.0147  I   -14.164     .420    -3.200     .298   .298000   .253500  -.1742800   -14.100    -3.500  
+90 929 48163.00 I   .297561  .000209   .250709  .000353  I -.1759802  .0000254  1.9311 0.0164  I   -14.147     .460    -3.201     .298   .297700   .249800  -.1760800   -14.000    -3.800  
+90 930 48164.00 I   .296905  .000196   .247030  .000343  I -.1780282  .0000264  2.1711 0.0183  I   -14.274     .524    -3.043     .298   .297700   .246300  -.1781200   -13.900    -3.700  
+9010 1 48165.00 I   .296302  .000185   .243594  .000354  I -.1803265  .0000263  2.4233 0.0189  I   -14.698     .581    -2.874     .298   .297400   .243000  -.1804100   -14.100    -3.700  
+9010 2 48166.00 I   .295654  .000237   .240360  .000444  I -.1828631  .0000270  2.6404 0.0170  I   -15.259     .647    -2.748     .103   .297000   .240100  -.1829400   -14.400    -3.300  
+9010 3 48167.00 I   .294851  .000270   .237241  .000299  I -.1855792  .0000216  2.7730 0.0165  I   -15.616     .611    -2.614     .298   .295900   .236900  -.1856200   -14.500    -3.200  
+9010 4 48168.00 I   .293782  .000339   .234151  .000224  I -.1883677  .0000188  2.7812 0.0142  I   -15.537     .575    -2.492     .298   .294300   .233700  -.1884200   -14.300    -3.100  
+9010 5 48169.00 I   .292376  .000344   .230984  .000227  I -.1910975  .0000185  2.6569 0.0142  I   -15.169     .575    -2.451     .298   .292300   .230000  -.1911500   -13.900    -3.300  
+9010 6 48170.00 I   .290636  .000344   .227640  .000227  I -.1936458  .0000214  2.4253 0.0133  I   -14.752     .600    -2.508     .111   .290100   .226000  -.1937100   -14.400    -3.700  
+9010 7 48171.00 I   .288711  .000310   .224110  .000203  I -.1959332  .0000192  2.1488 0.0181  I   -14.406     .747    -2.580     .197   .287700   .221900  -.1960100   -13.700    -3.600  
+9010 8 48172.00 I   .286692  .000376   .220434  .000196  I -.1979555  .0000291  1.9086 0.0208  I   -14.125     .691    -2.585     .222   .285800   .218400  -.1980200   -13.000    -3.700  
+9010 9 48173.00 I   .284630  .000383   .216660  .000144  I -.1997832  .0000369  1.7670 0.0258  I   -13.940     .697    -2.523     .290   .283800   .214900  -.1998800   -12.600    -3.400  
+901010 48174.00 I   .282572  .000419   .212839  .000204  I -.2015301  .0000426  1.7474 0.0294  I   -14.111     .685    -2.253     .321   .282000   .211800  -.2016300   -12.800    -2.600  
+901011 48175.00 I   .280541  .000419   .209022  .000204  I -.2033154  .0000458  1.8401 0.0293  I   -14.374     .685    -2.129     .321   .280300   .208800  -.2033300   -12.800    -2.400  
+901012 48176.00 I   .278421  .000375   .205271  .000217  I -.2052338  .0000402  2.0034 0.0283  I   -14.419     .679    -2.386     .337   .278700   .205400  -.2052200   -13.200    -1.900  
+901013 48177.00 I   .276142  .000326   .201622  .000224  I -.2073263  .0000332  2.1789 0.0231  I   -14.192     .356    -2.584     .261   .276800   .202000  -.2073700   -13.100    -2.100  
+901014 48178.00 I   .273732  .000362   .198072  .000311  I -.2095793  .0000226  2.3177 0.0193  I   -13.791     .259    -2.460     .250   .274400   .198200  -.2096700   -13.000    -2.300  
+901015 48179.00 I   .271236  .000285   .194613  .000323  I -.2119396  .0000195  2.3904 0.0151  I   -13.375     .256    -2.186     .185   .271700   .194200  -.2120200   -12.900    -2.500  
+901016 48180.00 I   .268687  .000237   .191236  .000365  I -.2143357  .0000200  2.3898 0.0116  I   -13.114     .276    -1.953     .298   .269000   .190700  -.2143600   -12.900    -2.400  
+901017 48181.00 I   .266124  .000214   .187938  .000336  I -.2166969  .0000127  2.3223 0.0119  I   -13.025     .243    -1.816     .106   .266300   .187300  -.2167500   -12.700    -2.200  
+901018 48182.00 I   .263546  .000232   .184709  .000401  I -.2189627  .0000128  2.2019 0.0103  I   -12.995     .299    -1.782     .166   .263500   .184100  -.2190000   -12.600    -2.200  
+901019 48183.00 I   .260909  .000271   .181498  .000458  I -.2210914  .0000163  2.0537 0.0118  I   -12.987     .302    -1.859     .193   .260700   .180900  -.2211300   -12.100    -2.200  
+901020 48184.00 I   .258171  .000428   .178283  .000459  I -.2230666  .0000198  1.8952 0.0135  I   -13.034     .302    -1.984     .193   .258000   .178100  -.2231500   -11.900    -2.500  
+901021 48185.00 I   .255334  .000448   .175151  .000463  I -.2248874  .0000214  1.7536 0.0139  I   -13.137     .367    -2.195     .197   .255400   .175300  -.2249700   -12.000    -2.300  
+901022 48186.00 I   .252529  .000404   .172099  .000426  I -.2265925  .0000194  1.6683 0.0133  I   -13.082     .367    -2.385     .197   .252600   .172300  -.2266200   -12.400    -2.100  
+901023 48187.00 I   .249944  .000374   .169070  .000357  I -.2282489  .0000159  1.6578 0.0127  I   -12.685     .629    -2.458     .162   .250000   .169100  -.2282900   -12.000    -2.100  
+901024 48188.00 I   .247695  .000424   .166030  .000281  I -.2299310  .0000165  1.7161 0.0160  I   -12.047     .750    -2.444     .298   .247600   .165700  -.2300200   -10.800    -2.300  
+901025 48189.00 I   .245667  .000431   .163016  .000288  I -.2316994  .0000277  1.8294 0.0178  I   -11.556     .750    -2.427     .298   .245400   .162000  -.2317700    -9.700    -2.900  
+901026 48190.00 I   .243703  .000432   .160079  .000377  I -.2336061  .0000316  1.9916 0.0201  I   -11.444     .712    -2.398     .111   .244100   .158900  -.2337400    -9.300    -2.900  
+901027 48191.00 I   .241754  .000251   .157249  .000361  I -.2356992  .0000291  2.2038 0.0203  I   -11.368     .960    -2.274     .136   .242200   .155800  -.2357700    -9.400    -3.000  
+901028 48192.00 I   .239851  .000240   .154550  .000326  I -.2380273  .0000256  2.4570 0.0216  I   -11.158     .944    -2.056     .143   .240300   .153400  -.2381500    -9.900    -2.900  
+901029 48193.00 I   .238015  .000258   .152003  .000377  I -.2406142  .0000319  2.7132 0.0215  I   -11.030     .981    -1.851     .169   .238600   .151400  -.2406500   -10.600    -2.800  
+901030 48194.00 I   .236208  .000272   .149592  .000419  I -.2434364  .0000346  2.9176 0.0210  I   -11.317     .923    -1.900     .169   .236800   .149200  -.2435300   -10.900    -2.800  
+901031 48195.00 I   .234410  .000307   .147255  .000441  I -.2464136  .0000272  3.0145 0.0189  I   -11.651     .923    -2.035     .169   .235100   .147300  -.2465500   -10.800    -2.600  
+9011 1 48196.00 I   .232557  .000257   .144959  .000404  I -.2494178  .0000150  2.9686 0.0157  I   -11.755     .996    -2.037     .152   .233000   .144000  -.2494400   -10.600    -2.600  
+9011 2 48197.00 I   .230548  .000360   .142681  .000270  I -.2523024  .0000157  2.7777 0.0124  I   -11.620     .692    -1.949     .111   .231200   .141800  -.2522500   -10.100    -2.600  
+9011 3 48198.00 I   .228263  .000473   .140412  .000220  I -.2549381  .0000197  2.4816 0.0132  I   -11.270     .500    -1.920     .298   .228500   .139400  -.2549800    -9.600    -2.400  
+9011 4 48199.00 I   .225562  .000484   .138140  .000201  I -.2572554  .0000213  2.1550 0.0145  I   -10.804     .500    -1.935     .298   .226000   .137100  -.2573500    -9.500    -2.400  
+9011 5 48200.00 I   .222305  .000514   .135834  .000200  I -.2592738  .0000212  1.9056 0.0184  I   -10.492     .500    -1.842     .298   .222400   .134500  -.2594700   -10.000    -2.600  
+9011 6 48201.00 I   .218641  .000438   .133500  .000141  I -.2611209  .0000300  1.8190 0.0169  I   -10.567     .500    -1.633     .298   .218900   .132000  -.2611900   -10.900    -2.300  
+9011 7 48202.00 I   .214857  .000376   .131159  .000170  I -.2629680  .0000264  1.9008 0.0214  I   -10.943     .372    -1.501     .107   .214800   .129500  -.2631600   -11.200    -2.100  
+9011 8 48203.00 I   .211202  .000292   .128824  .000186  I -.2649608  .0000306  2.0973 0.0202  I   -11.255     .142    -1.575     .123   .211000   .127600  -.2650800   -11.100    -2.100  
+9011 9 48204.00 I   .207788  .000292   .126509  .000186  I -.2671750  .0000306  2.3308 0.0216  I   -11.248     .142    -1.724     .123   .207300   .125300  -.2672500   -10.900    -2.200  
+901110 48205.00 I   .204666  .000291   .124230  .000226  I -.2696089  .0000306  2.5237 0.0207  I   -10.997     .142    -1.723     .123   .203900   .123000  -.2697100   -10.300    -2.300  
+901111 48206.00 I   .201775  .000188   .121988  .000232  I -.2721936  .0000278  2.6300 0.0171  I   -10.717     .162    -1.513     .140   .201100   .120800  -.2724300    -9.800    -2.200  
+901112 48207.00 I   .199017  .000158   .119764  .000203  I -.2748376  .0000154  2.6422 0.0149  I   -10.492     .152    -1.218     .133   .198000   .118600  -.2750100    -9.400    -2.100  
+901113 48208.00 I   .196297  .000141   .117541  .000204  I -.2774490  .0000108  2.5673 0.0094  I   -10.299     .206     -.945     .136   .195400   .116400  -.2775800    -9.000    -2.100  
+901114 48209.00 I   .193519  .000141   .115309  .000204  I -.2799492  .0000107  2.4233 0.0083  I   -10.141     .206     -.687     .136   .192800   .113800  -.2800400    -9.100    -1.900  
+901115 48210.00 I   .190608  .000190   .113086  .000250  I -.2822820  .0000126  2.2383 0.0090  I   -10.061     .206     -.459     .136   .189900   .111800  -.2823400    -9.000    -1.800  
+901116 48211.00 I   .187568  .000177   .110916  .000220  I -.2844222  .0000145  2.0425 0.0102  I   -10.119     .185     -.484     .135   .186900   .109500  -.2844000    -8.900    -1.800  
+901117 48212.00 I   .184445  .000189   .108853  .000350  I -.2863721  .0000161  1.8622 0.0127  I   -10.250     .399     -.798     .116   .183900   .107900  -.2863600    -8.900    -1.900  
+901118 48213.00 I   .181286  .000218   .106953  .000483  I -.2881592  .0000209  1.7198 0.0138  I   -10.285     .481    -1.136     .112   .180700   .106100  -.2881300    -8.900    -1.800  
+901119 48214.00 I   .178131  .000225   .105240  .000506  I -.2898352  .0000224  1.6481 0.0152  I   -10.021     .580    -1.186     .298   .177700   .104400  -.2898400    -9.000    -1.900  
+901120 48215.00 I   .175047  .000225   .103600  .000506  I -.2914843  .0000221  1.6632 0.0149  I    -9.368     .580     -.974     .298   .174700   .102800  -.2915300    -9.100    -1.900  
+901121 48216.00 I   .172026  .000230   .102002  .000474  I -.2931847  .0000197  1.7475 0.0149  I    -8.648     .580     -.732     .298   .171500   .101200  -.2932900    -9.200    -1.600  
+901122 48217.00 I   .169017  .000223   .100466  .000494  I -.2949979  .0000200  1.8874 0.0142  I    -8.373     .575     -.651     .298   .168400   .099600  -.2951400    -9.300    -1.600  
+901123 48218.00 I   .165974  .000244   .098992  .000421  I -.2969738  .0000204  2.0701 0.0126  I    -8.595     .531     -.685     .298   .165400   .097800  -.2970900    -9.300    -1.500  
+901124 48219.00 I   .162854  .000230   .097582  .000272  I -.2991467  .0000154  2.2785 0.0134  I    -8.985     .531     -.695     .298   .162300   .096300  -.2992100    -9.400    -1.300  
+901125 48220.00 I   .159599  .000272   .096223  .000143  I -.3015319  .0000174  2.4903 0.0131  I    -9.224     .338     -.690     .126   .159200   .094800  -.3015500    -9.200    -1.300  
+901126 48221.00 I   .156136  .000258   .094901  .000139  I -.3041180  .0000211  2.6743 0.0128  I    -9.307     .305     -.762     .115   .155800   .093300  -.3041400    -9.000    -1.300  
+901127 48222.00 I   .152413  .000305   .093609  .000234  I -.3068584  .0000188  2.7920 0.0143  I    -9.369     .391     -.846     .177   .152200   .092400  -.3068700    -8.800    -1.300  
+901128 48223.00 I   .148427  .000326   .092345  .000242  I -.3096706  .0000193  2.8154 0.0137  I    -9.474     .401     -.709     .200   .147900   .091200  -.3097300    -8.300    -1.500  
+901129 48224.00 I   .144337  .000326   .091103  .000242  I -.3124545  .0000199  2.7348 0.0139  I    -9.602     .401     -.429     .200   .143600   .090000  -.3125400    -7.900    -1.500  
+901130 48225.00 I   .140317  .000326   .089885  .000242  I -.3151098  .0000199  2.5632 0.0153  I    -9.691     .401     -.318     .200   .139300   .088800  -.3152100    -7.500    -1.400  
+9012 1 48226.00 I   .136535  .000383   .088739  .000354  I -.3175645  .0000233  2.3426 0.0116  I    -9.439     .458     -.431     .206   .135500   .087700  -.3176400    -7.000    -1.400  
+9012 2 48227.00 I   .132960  .000365   .087668  .000326  I -.3198018  .0000119  2.1426 0.0145  I    -8.764     .507     -.601     .213   .131700   .086600  -.3198800    -6.600    -1.400  
+9012 3 48228.00 I   .129504  .000444   .086654  .000331  I -.3218807  .0000171  2.0357 0.0111  I    -8.039     .479     -.585     .164   .128400   .085500  -.3219400    -6.200    -1.200  
+9012 4 48229.00 I   .126079  .000380   .085679  .000266  I -.3239147  .0000188  2.0532 0.0119  I    -7.766     .422     -.311     .165   .125100   .084500  -.3240300    -6.000    -1.200  
+9012 5 48230.00 I   .122590  .000380   .084722  .000266  I -.3260221  .0000165  2.1756 0.0126  I    -8.074     .422     -.016     .165   .121500   .083500  -.3261200    -6.900    -1.500  
+9012 6 48231.00 I   .119050  .000390   .083780  .000334  I -.3282816  .0000169  2.3450 0.0108  I    -8.507     .422     -.030     .165   .117900   .082600  -.3284000    -7.000    -1.600  
+9012 7 48232.00 I   .115520  .000313   .082857  .000347  I -.3307054  .0000140  2.4941 0.0121  I    -8.601     .461     -.364     .136   .114300   .081700  -.3308400    -7.100    -1.600  
+9012 8 48233.00 I   .112032  .000233   .081940  .000312  I -.3332465  .0000173  2.5733 0.0106  I    -8.434     .448     -.652     .298   .110800   .080800  -.3333800    -7.100    -1.700  
+9012 9 48234.00 I   .108594  .000290   .081004  .000385  I -.3358221  .0000160  2.5629 0.0121  I    -8.327     .417     -.652     .298   .107300   .079900  -.3359400    -7.200    -1.700  
+901210 48235.00 I   .105159  .000275   .080000  .000365  I -.3383410  .0000168  2.4586 0.0111  I    -8.364     .417     -.441     .298   .103800   .078900  -.3384200    -7.200    -1.600  
+901211 48236.00 I   .101696  .000249   .078953  .000359  I -.3407106  .0000153  2.2684 0.0102  I    -8.415     .417     -.259     .298   .100400   .077900  -.3407500    -7.200    -1.600  
+901212 48237.00 I   .098177  .000162   .077921  .000443  I -.3428633  .0000116  2.0349 0.0099  I    -8.443     .421     -.200     .298   .097100   .076900  -.3429000    -7.200    -1.500  
+901213 48238.00 I   .094582  .000187   .076908  .000480  I -.3447823  .0000127  1.8071 0.0085  I    -8.454     .353     -.113     .114   .093700   .075800  -.3448300    -7.100    -1.400  
+901214 48239.00 I   .090989  .000187   .075839  .000480  I -.3464901  .0000125  1.6170 0.0108  I    -8.431     .353      .047     .114   .090400   .074700  -.3465700    -7.100    -1.300  
+901215 48240.00 I   .087493  .000169   .074656  .000501  I -.3480344  .0000174  1.4813 0.0097  I    -8.305     .364      .185     .116   .087200   .073500  -.3481300    -7.100    -1.200  
+901216 48241.00 I   .084124  .000160   .073460  .000484  I -.3494722  .0000149  1.4040 0.0108  I    -8.147     .427      .135     .113   .083900   .072500  -.3495700    -7.100    -1.100  
+901217 48242.00 I   .080833  .000146   .072344  .000434  I -.3508638  .0000128  1.3907 0.0110  I    -8.037     .423      .003     .129   .080600   .071500  -.3509700    -7.100     -.900  
+901218 48243.00 I   .077561  .000184   .071380  .000364  I -.3522759  .0000163  1.4443 0.0107  I    -7.999     .421     -.026     .139   .077200   .070600  -.3523800    -7.100     -.800  
+901219 48244.00 I   .074250  .000186   .070638  .000308  I -.3537777  .0000171  1.5737 0.0138  I    -8.092     .476      .036     .124   .073700   .069800  -.3538900    -7.200     -.700  
+901220 48245.00 I   .070866  .000186   .070146  .000308  I -.3554434  .0000222  1.7633 0.0142  I    -8.252     .476      .117     .124   .070200   .069200  -.3555600    -7.200     -.700  
+901221 48246.00 I   .067377  .000185   .069860  .000333  I -.3573110  .0000227  1.9729 0.0146  I    -8.381     .515      .132     .134   .066500   .068600  -.3574000    -7.100     -.800  
+901222 48247.00 I   .063775  .000185   .069730  .000375  I -.3593874  .0000191  2.1770 0.0253  I    -8.297     .514      .063     .144   .062800   .068200  -.3594600    -7.100     -.900  
+901223 48248.00 I   .060069  .000201   .069705  .000436  I -.3616542  .0000452  2.3489 0.0235  I    -7.960     .594     -.016     .140   .058900   .068000  -.3617100    -7.100     -.900  
+901224 48249.00 I   .056270  .000389   .069732  .000258  I -.3640664  .0000430  2.4646 0.0320  I    -7.504     .594     -.072     .140   .055000   .067800  -.3641200    -7.000    -1.000  
+901225 48250.00 I   .052379  .000366   .069786  .000243  I -.3665589  .0000454  2.5067 0.0269  I    -7.092     .571     -.118     .134   .050900   .067700  -.3666200    -7.000    -1.100  
+901226 48251.00 I   .048355  .000299   .069869  .000328  I -.3690509  .0000322  2.4621 0.0256  I    -6.893     .571     -.101     .134   .046900   .067700  -.3691300    -6.900    -1.100  
+901227 48252.00 I   .044145  .000321   .069989  .000425  I -.3714556  .0000237  2.3349 0.0211  I    -7.061     .528      .023     .211   .042800   .067800  -.3715300    -6.900    -1.200  
+901228 48253.00 I   .039730  .000325   .070144  .000434  I -.3737009  .0000272  2.1483 0.0185  I    -7.614     .528      .125     .211   .038700   .067900  -.3737700    -6.800    -1.100  
+901229 48254.00 I   .035225  .000325   .070309  .000434  I -.3757491  .0000285  1.9523 0.0197  I    -8.167     .528      .061     .211   .034600   .068100  -.3757900    -6.800    -1.100  
+901230 48255.00 I   .030776  .000325   .070470  .000434  I -.3776271  .0000286  1.8211 0.0184  I    -8.243     .528     -.090     .211   .030700   .068400  -.3776600    -6.800    -1.000  
+901231 48256.00 I   .026474  .000249   .070678  .000529  I -.3794330  .0000234  1.8149 0.0166  I    -7.885     .538     -.098     .250   .026900   .068800  -.3794600    -6.700     -.900  
+91 1 1 48257.00 I   .022389  .000235   .070986  .000487  I  .6186975  .0000170  1.9454 0.0151  I    -7.510     .598      .134     .204   .023100   .069300   .6186700    -6.700     -.800  
+91 1 2 48258.00 I   .018591  .000253   .071447  .000317  I  .6166447  .0000190  2.1705 0.0106  I    -7.378     .754      .394     .171   .019500   .070000   .6166000    -6.600     -.800  
+91 1 3 48259.00 I   .015151  .000253   .072113  .000317  I  .6143495  .0000125  2.4165 0.0174  I    -7.377     .754      .386     .171   .016100   .070700   .6142800    -6.600     -.700  
+91 1 4 48260.00 I   .012118  .000334   .073018  .000392  I  .6118302  .0000292  2.6071 0.0133  I    -7.330     .737      .106     .191   .013200   .072200   .6117600    -6.600     -.900  
+91 1 5 48261.00 I   .009382  .000291   .074132  .000359  I  .6091699  .0000235  2.6940 0.0174  I    -7.116     .737     -.012     .191   .010000   .073200   .6091200    -6.700     -.900  
+91 1 6 48262.00 I   .006782  .000345   .075384  .000278  I  .6064782  .0000191  2.6730 0.0195  I    -6.952     .657      .157     .181   .006800   .074300   .6064400    -6.700     -.900  
+91 1 7 48263.00 I   .004177  .000391   .076691  .000295  I  .6038514  .0000310  2.5695 0.0184  I    -7.033     .591      .248     .179   .003600   .075500   .6038400    -6.600    -1.000  
+91 1 8 48264.00 I   .001418  .000381   .077970  .000288  I  .6013588  .0000315  2.4071 0.0221  I    -7.208     .473      .045     .168   .000400   .076600   .6013600    -6.600    -1.000  
+91 1 9 48265.00 I  -.001661  .000381   .079172  .000288  I  .5990451  .0000315  2.2209 0.0230  I    -7.248     .428     -.143     .152  -.002800   .077800   .5990600    -6.500    -1.000  
+91 110 48266.00 I  -.005050  .000366   .080296  .000301  I  .5969117  .0000334  2.0505 0.0204  I    -7.294     .339     -.146     .121  -.006100   .078800   .5969200    -6.500    -1.000  
+91 111 48267.00 I  -.008653  .000300   .081354  .000296  I  .5949329  .0000258  1.9138 0.0255  I    -7.460     .521     -.042     .102  -.009500   .079800   .5949300    -6.400    -1.000  
+91 112 48268.00 I  -.012377  .000206   .082340  .000345  I  .5930695  .0000386  1.8211 0.0196  I    -7.638     .548      .008     .298  -.013000   .080700   .5930700    -6.400    -1.000  
+91 113 48269.00 I  -.016122  .000208   .083186  .000428  I  .5912743  .0000294  1.7778 0.0235  I    -7.753     .629     -.007     .298  -.016600   .081500   .5912800    -6.400    -1.000  
+91 114 48270.00 I  -.019806  .000292   .083830  .000431  I  .5894949  .0000267  1.7917 0.0187  I    -7.869     .559      .062     .122  -.020300   .082100   .5895000    -6.500     -.900  
+91 115 48271.00 I  -.023464  .000256   .084317  .000436  I  .5876705  .0000230  1.8668 0.0144  I    -7.962     .571      .261     .147  -.024000   .082700   .5876600    -6.600     -.900  
+91 116 48272.00 I  -.027175  .000208   .084736  .000445  I  .5857450  .0000107  1.9909 0.0134  I    -7.901     .499      .404     .136  -.027700   .083300   .5857400    -6.700     -.800  
+91 117 48273.00 I  -.031011  .000231   .085171  .000444  I  .5836807  .0000136  2.1392 0.0093  I    -7.606     .284      .232     .165  -.031400   .083700   .5836800    -6.800     -.800  
+91 118 48274.00 I  -.034995  .000236   .085646  .000456  I  .5814692  .0000151  2.2799 0.0132  I    -7.207     .284     -.097     .165  -.035100   .084200   .5814600    -6.900     -.700  
+91 119 48275.00 I  -.039044  .000236   .086098  .000456  I  .5791302  .0000227  2.3929 0.0134  I    -6.976     .284     -.338     .165  -.038700   .084800   .5791000    -7.000     -.700  
+91 120 48276.00 I  -.043017  .000347   .086553  .000364  I  .5766972  .0000221  2.4646 0.0149  I    -6.944     .299     -.403     .134  -.042200   .085400   .5766400    -7.100     -.700  
+91 121 48277.00 I  -.046776  .000253   .087092  .000319  I  .5742211  .0000193  2.4758 0.0163  I    -6.989     .275     -.314     .142  -.045600   .086100   .5741300    -7.200     -.700  
+91 122 48278.00 I  -.050215  .000318   .087788  .000285  I  .5717685  .0000240  2.4181 0.0159  I    -7.048     .218     -.151     .144  -.049000   .086900   .5716400    -7.300     -.700  
+91 123 48279.00 I  -.053236  .000351   .088712  .000283  I  .5694044  .0000253  2.3020 0.0172  I    -7.174     .218      .019     .144  -.052200   .087800   .5692800    -7.300     -.700  
+91 124 48280.00 I  -.055812  .000351   .089927  .000283  I  .5671813  .0000246  2.1356 0.0164  I    -7.469     .218      .128     .144  -.055300   .088900   .5670700    -7.400     -.800  
+91 125 48281.00 I  -.058173  .000330   .091415  .000261  I  .5651398  .0000210  1.9494 0.0156  I    -7.933     .218      .155     .144  -.058500   .090200   .5650600    -7.300     -.800  
+91 126 48282.00 I  -.060588  .000331   .093097  .000376  I  .5632680  .0000191  1.8067 0.0160  I    -8.348     .378      .015     .123  -.061800   .091500   .5632300    -7.200     -.900  
+91 127 48283.00 I  -.063289  .000353   .094881  .000497  I  .5614935  .0000241  1.7625 0.0134  I    -8.406     .449     -.300     .298  -.065100   .092900   .5614900    -7.100     -.900  
+91 128 48284.00 I  -.066451  .000364   .096671  .000449  I  .5597015  .0000189  1.8426 0.0146  I    -7.969     .515     -.610     .298  -.068600   .094400   .5597000    -6.800    -1.000  
+91 129 48285.00 I  -.070208  .000311   .098382  .000447  I  .5577729  .0000164  2.0287 0.0132  I    -7.170     .450     -.738     .298  -.072100   .096000   .5577700    -6.500    -1.100  
+91 130 48286.00 I  -.074442  .000361   .100022  .000411  I  .5556263  .0000184  2.2681 0.0104  I    -6.388     .450     -.746     .298  -.075800   .097500   .5556000    -6.200    -1.200  
+91 131 48287.00 I  -.078910  .000338   .101594  .000385  I  .5532423  .0000127  2.4906 0.0121  I    -5.811     .431     -.753     .298  -.079600   .099100   .5531700    -5.800    -1.400  
+91 2 1 48288.00 I  -.083389  .000392   .103081  .000366  I  .5506707  .0000157  2.6362 0.0098  I    -5.482     .335     -.738     .103  -.083500   .100600   .5505600    -5.500    -1.500  
+91 2 2 48289.00 I  -.087740  .000394   .104469  .000213  I  .5480040  .0000150  2.6794 0.0109  I    -5.521     .326     -.698     .104  -.087400   .102200   .5478600    -5.100    -1.600  
+91 2 3 48290.00 I  -.091885  .000394   .105752  .000213  I  .5453487  .0000152  2.6123 0.0127  I    -5.999     .288     -.667     .298  -.090900   .103500   .5452100    -5.600    -1.800  
+91 2 4 48291.00 I  -.095784  .000408   .106978  .000237  I  .5428091  .0000205  2.4562 0.0117  I    -6.696     .328     -.669     .298  -.094400   .104800   .5426800    -5.500    -1.900  
+91 2 5 48292.00 I  -.099377  .000380   .108224  .000248  I  .5404512  .0000177  2.2555 0.0143  I    -7.245     .293     -.728     .162  -.097900   .106200   .5403600    -5.400    -1.900  
+91 2 6 48293.00 I  -.102658  .000371   .109555  .000275  I  .5382971  .0000198  2.0577 0.0135  I    -7.480     .176     -.807     .201  -.101500   .107800   .5382100    -5.300    -2.000  
+91 2 7 48294.00 I  -.105703  .000371   .110999  .000275  I  .5363232  .0000205  1.8976 0.0146  I    -7.517     .176     -.780     .201  -.105000   .109400   .5362300    -5.200    -2.000  
+91 2 8 48295.00 I  -.108620  .000327   .112573  .000278  I  .5344856  .0000215  1.7864 0.0129  I    -7.461     .176     -.625     .201  -.108400   .111100   .5343900    -5.100    -2.100  
+91 2 9 48296.00 I  -.111491  .000290   .114302  .000256  I  .5327295  .0000155  1.7379 0.0121  I    -7.282     .193     -.686     .227  -.111700   .112900   .5326300    -5.000    -2.100  
+91 210 48297.00 I  -.114367  .000311   .116185  .000203  I  .5309856  .0000111  1.7621 0.0090  I    -6.970     .337    -1.042     .209  -.114900   .114800   .5308900    -5.000    -2.200  
+91 211 48298.00 I  -.117315  .000292   .118198  .000224  I  .5291822  .0000090  1.8558 0.0070  I    -6.671     .363    -1.419     .200  -.118000   .116900   .5290900    -4.900    -2.200  
+91 212 48299.00 I  -.120404  .000290   .120310  .000180  I  .5272551  .0000085  2.0063 0.0061  I    -6.553     .349    -1.570     .179  -.121100   .119100   .5271700    -4.900    -2.200  
+91 213 48300.00 I  -.123664  .000287   .122513  .000181  I  .5251586  .0000083  2.1898 0.0058  I    -6.611     .349    -1.453     .179  -.124100   .121400   .5251000    -5.000    -2.200  
+91 214 48301.00 I  -.126908  .000302   .124910  .000179  I  .5228707  .0000078  2.3875 0.0072  I    -6.635     .349    -1.368     .179  -.127100   .123900   .5228300    -5.100    -2.200  
+91 215 48302.00 I  -.130112  .000295   .127563  .000171  I  .5203904  .0000118  2.5646 0.0084  I    -6.576     .570    -1.369     .157  -.130100   .126500   .5203900    -5.200    -2.200  
+91 216 48303.00 I  -.133270  .000322   .130449  .000145  I  .5177638  .0000149  2.6748 0.0122  I    -6.571     .637    -1.365     .133  -.133100   .129200   .5177900    -5.300    -2.300  
+91 217 48304.00 I  -.136368  .000261   .133521  .000309  I  .5150737  .0000214  2.6861 0.0148  I    -6.721     .697    -1.356     .103  -.136100   .132000   .5151300    -5.500    -2.300  
+91 218 48305.00 I  -.139405  .000324   .136727  .000397  I  .5124253  .0000256  2.5961 0.0156  I    -6.999     .793    -1.374     .298  -.139100   .134900   .5124700    -5.600    -2.300  
+91 219 48306.00 I  -.142389  .000285   .140004  .000487  I  .5099051  .0000226  2.4353 0.0163  I    -7.288     .793    -1.409     .298  -.142200   .137800   .5099100    -5.800    -2.400  
+91 220 48307.00 I  -.145325  .000301   .143291  .000462  I  .5075665  .0000202  2.2393 0.0175  I    -7.482     .651    -1.429     .298  -.145300   .140800   .5075200    -5.900    -2.400  
+91 221 48308.00 I  -.148250  .000290   .146514  .000534  I  .5054215  .0000267  2.0584 0.0165  I    -7.553     .302    -1.416     .298  -.148300   .143800   .5053200    -6.100    -2.400  
+91 222 48309.00 I  -.151190  .000252   .149542  .000462  I  .5034249  .0000260  1.9520 0.0193  I    -7.587     .302    -1.468     .298  -.151300   .146800   .5033000    -6.200    -2.500  
+91 223 48310.00 I  -.154175  .000251   .152268  .000453  I  .5014858  .0000278  1.9409 0.0185  I    -7.667     .280    -1.725     .298  -.154300   .149800   .5013600    -6.300    -2.500  
+91 224 48311.00 I  -.157177  .000232   .154778  .000401  I  .4995116  .0000264  2.0244 0.0184  I    -7.708     .306    -2.149     .298  -.157100   .152800   .4994000    -6.300    -2.600  
+91 225 48312.00 I  -.160098  .000243   .157240  .000364  I  .4974054  .0000241  2.2026 0.0199  I    -7.582     .333    -2.536     .298  -.159900   .155700   .4973000    -6.400    -2.600  
+91 226 48313.00 I  -.162850  .000202   .159794  .000263  I  .4950844  .0000299  2.4467 0.0166  I    -7.253     .446    -2.630     .298  -.162600   .158600   .4949800    -6.400    -2.700  
+91 227 48314.00 I  -.165376  .000202   .162467  .000263  I  .4925069  .0000229  2.7060 0.0182  I    -6.828     .473    -2.375     .298  -.165200   .161500   .4923900    -6.400    -2.700  
+91 228 48315.00 I  -.167647  .000200   .165250  .000262  I  .4896908  .0000208  2.9102 0.0158  I    -6.463     .473    -1.961     .298  -.167700   .164300   .4895600    -6.400    -2.800  
+91 3 1 48316.00 I  -.169720  .000266   .168114  .000262  I  .4867258  .0000217  2.9967 0.0145  I    -6.350     .518    -1.712     .298  -.170100   .167200   .4866100    -6.400    -2.800  
+91 3 2 48317.00 I  -.171679  .000249   .171052  .000247  I  .4837438  .0000203  2.9440 0.0181  I    -6.423     .439    -1.710     .298  -.172400   .170100   .4836600    -6.400    -2.800  
+91 3 3 48318.00 I  -.173596  .000372   .174060  .000264  I  .4808788  .0000289  2.7683 0.0192  I    -6.584     .470    -1.861     .298  -.174500   .173000   .4808300    -6.400    -2.900  
+91 3 4 48319.00 I  -.175545  .000380   .177130  .000164  I  .4782346  .0000326  2.5103 0.0217  I    -6.804     .367    -2.056     .298  -.176500   .175900   .4782300    -6.500    -3.000  
+91 3 5 48320.00 I  -.177585  .000393   .180250  .000174  I  .4758656  .0000323  2.2301 0.0214  I    -7.014     .223    -2.173     .298  -.178400   .179300   .4758600    -6.300    -2.400  
+91 3 6 48321.00 I  -.179691  .000431   .183430  .000208  I  .4737648  .0000277  1.9787 0.0187  I    -7.138     .223    -2.170     .298  -.180200   .182700   .4737500    -6.300    -2.400  
+91 3 7 48322.00 I  -.181814  .000322   .186689  .000199  I  .4718903  .0000188  1.7808 0.0168  I    -7.128     .186    -2.125     .298  -.181900   .186200   .4718600    -6.300    -2.500  
+91 3 8 48323.00 I  -.183873  .000316   .190040  .000202  I  .4701765  .0000191  1.6629 0.0135  I    -6.910     .167    -2.205     .298  -.183500   .189700   .4701000    -6.400    -2.600  
+91 3 9 48324.00 I  -.185715  .000315   .193494  .000269  I  .4685319  .0000194  1.6428 0.0139  I    -6.464     .167    -2.473     .298  -.185200   .193200   .4684200    -6.500    -2.600  
+91 310 48325.00 I  -.187247  .000310   .197079  .000269  I  .4668623  .0000203  1.7085 0.0117  I    -5.902     .150    -2.802     .298  -.186800   .196700   .4667300    -6.600    -2.700  
+91 311 48326.00 I  -.188552  .000232   .200801  .000263  I  .4650913  .0000132  1.8449 0.0130  I    -5.448     .791    -3.012     .298  -.188400   .200300   .4649500    -6.700    -2.800  
+91 312 48327.00 I  -.189749  .000424   .204643  .000187  I  .4631528  .0000163  2.0402 0.0112  I    -5.317     .460    -3.024     .298  -.189900   .203900   .4630100    -6.900    -2.900  
+91 313 48328.00 I  -.190961  .000510   .208577  .000182  I  .4610003  .0000182  2.2672 0.0124  I    -5.544     .561    -2.927     .298  -.191400   .207600   .4608400    -7.000    -3.000  
+91 314 48329.00 I  -.192341  .000510   .212539  .000182  I  .4586217  .0000186  2.4843 0.0130  I    -5.948     .650    -2.852     .298  -.192800   .211300   .4584300    -7.100    -3.200  
+91 315 48330.00 I  -.193994  .000510   .216441  .000182  I  .4560330  .0000185  2.6975 0.0129  I    -6.350     .650    -2.822     .298  -.194200   .215000   .4557900    -7.100    -3.200  
+91 316 48331.00 I  -.195667  .000505   .220202  .000135  I  .4532397  .0000178  2.8718 0.0126  I    -6.707     .710    -2.766     .298  -.195600   .218700   .4529900    -7.100    -3.300  
+91 317 48332.00 I  -.197292  .000468   .223842  .000131  I  .4503304  .0000171  2.9231 0.0140  I    -6.887     .634    -2.692     .298  -.197000   .222400   .4501100    -7.000    -3.400  
+91 318 48333.00 I  -.198813  .000379   .227397  .000167  I  .4474393  .0000216  2.8371 0.0183  I    -6.928     .627    -2.682     .298  -.198400   .226100   .4472800    -6.900    -3.500  
+91 319 48334.00 I  -.200148  .000360   .230902  .000232  I  .4446940  .0000323  2.6379 0.0196  I    -6.963     .562    -2.791     .298  -.199700   .229900   .4445900    -6.800    -3.600  
+91 320 48335.00 I  -.201247  .000255   .234395  .000242  I  .4421798  .0000326  2.3900 0.0227  I    -6.974     .324    -3.001     .298  -.201100   .233600   .4421300    -6.600    -3.600  
+91 321 48336.00 I  -.202195  .000227   .237934  .000224  I  .4399037  .0000318  2.1730 0.0211  I    -6.848     .324    -3.244     .298  -.202600   .237300   .4398900    -6.400    -3.700  
+91 322 48337.00 I  -.203113  .000305   .241573  .000191  I  .4378071  .0000267  2.0360 0.0247  I    -6.577     .607    -3.461     .298  -.204000   .241100   .4378100    -6.200    -3.800  
+91 323 48338.00 I  -.204155  .000306   .245328  .000213  I  .4357970  .0000378  2.0029 0.0208  I    -6.261     .692    -3.657     .298  -.205400   .245000   .4358000    -6.000    -3.800  
+91 324 48339.00 I  -.205483  .000305   .249211  .000200  I  .4337687  .0000319  2.0676 0.0247  I    -5.961     .718    -3.888     .298  -.206900   .248800   .4337400    -5.800    -3.900  
+91 325 48340.00 I  -.207237  .000298   .253232  .000168  I  .4316295  .0000317  2.2293 0.0205  I    -5.617     .801    -4.176     .298  -.208400   .252800   .4315400    -5.600    -3.900  
+91 326 48341.00 I  -.209344  .000362   .257362  .000170  I  .4292805  .0000257  2.4797 0.0205  I    -5.165     .801    -4.393     .298  -.209800   .256700   .4291300    -5.400    -4.000  
+91 327 48342.00 I  -.211542  .000365   .261519  .000166  I  .4266726  .0000260  2.7224 0.0191  I    -4.907     .976    -4.402     .298  -.211300   .260700   .4265000    -5.200    -4.000  
+91 328 48343.00 I  -.213596  .000394   .265659  .000159  I  .4238689  .0000282  2.8655 0.0199  I    -4.920    1.093    -4.183     .112  -.212600   .264700   .4237100    -5.000    -4.100  
+91 329 48344.00 I  -.215298  .000386   .269752  .000176  I  .4209839  .0000301  2.8818 0.0181  I    -5.118    1.093    -3.877     .112  -.214000   .268800   .4208500    -4.900    -4.200  
+91 330 48345.00 I  -.216509  .000438   .273776  .000358  I  .4181445  .0000227  2.7806 0.0176  I    -5.334    1.168    -3.696     .127  -.215100   .272900   .4180100    -4.600    -4.200  
+91 331 48346.00 I  -.217320  .000427   .277756  .000350  I  .4154460  .0000183  2.6097 0.0144  I    -5.428    1.033    -3.744     .112  -.216200   .277000   .4152800    -4.400    -4.300  
+91 4 1 48347.00 I  -.217857  .000403   .281739  .000324  I  .4129329  .0000177  2.4156 0.0111  I    -5.355     .958    -3.941     .165  -.217200   .281000   .4127500    -4.200    -4.400  
+91 4 2 48348.00 I  -.218249  .000376   .285770  .000441  I  .4106103  .0000127  2.2350 0.0109  I    -5.172     .683    -4.111     .170  -.218100   .285200   .4104000    -4.100    -4.400  
+91 4 3 48349.00 I  -.218610  .000376   .289880  .000441  I  .4084511  .0000128  2.0897 0.0088  I    -4.967     .683    -4.150     .170  -.218800   .289300   .4082400    -4.000    -4.500  
+91 4 4 48350.00 I  -.219006  .000376   .294038  .000441  I  .4064165  .0000123  1.9875 0.0108  I    -4.883     .683    -4.132     .170  -.219600   .293400   .4062000    -4.000    -4.600  
+91 4 5 48351.00 I  -.219449  .000336   .298196  .000410  I  .4044592  .0000174  1.9362 0.0103  I    -4.834     .683    -4.203     .170  -.220200   .297300   .4042500    -4.100    -4.600  
+91 4 6 48352.00 I  -.219906  .000303   .302322  .000261  I  .4025264  .0000164  1.9382 0.0144  I    -4.711     .606    -4.438     .172  -.220600   .301400   .4023300    -4.100    -4.700  
+91 4 7 48353.00 I  -.220303  .000213   .306406  .000221  I  .4005663  .0000229  1.9897 0.0145  I    -4.535     .118    -4.731     .114  -.220700   .305400   .4004000    -4.100    -4.700  
+91 4 8 48354.00 I  -.220558  .000200   .310445  .000166  I  .3985331  .0000240  2.0828 0.0166  I    -4.408     .237    -4.865     .118  -.220700   .309400   .3983700    -4.100    -4.800  
+91 4 9 48355.00 I  -.220598  .000200   .314438  .000166  I  .3963880  .0000240  2.2140 0.0160  I    -4.435     .237    -4.746     .118  -.220400   .313400   .3962200    -4.100    -4.900  
+91 410 48356.00 I  -.220373  .000230   .318404  .000143  I  .3940960  .0000211  2.3725 0.0133  I    -4.593     .237    -4.552     .118  -.219900   .317500   .3939200    -4.100    -4.900  
+91 411 48357.00 I  -.219857  .000198   .322358  .000115  I  .3916419  .0000114  2.5343 0.0124  I    -4.697     .276    -4.512     .107  -.219300   .321500   .3914500    -4.200    -5.000  
+91 412 48358.00 I  -.219056  .000227   .326309  .000163  I  .3890362  .0000129  2.6696 0.0080  I    -4.612     .339    -4.584     .298  -.218600   .325500   .3888200    -4.200    -5.000  
+91 413 48359.00 I  -.218014  .000174   .330253  .000132  I  .3863229  .0000111  2.7444 0.0085  I    -4.379     .435    -4.544     .298  -.217800   .329500   .3861200    -4.300    -5.100  
+91 414 48360.00 I  -.216806  .000153   .334184  .000121  I  .3835812  .0000112  2.7178 0.0081  I    -4.256     .528    -4.288     .298  -.216900   .333500   .3834300    -4.400    -5.200  
+91 415 48361.00 I  -.215546  .000147   .338103  .000157  I  .3809266  .0000117  2.5733 0.0086  I    -4.453     .528    -4.023     .298  -.216000   .337400   .3808300    -4.500    -5.200  
+91 416 48362.00 I  -.214356  .000252   .342027  .000159  I  .3784616  .0000130  2.3479 0.0092  I    -4.898     .468    -4.030     .298  -.215200   .341400   .3784200    -4.600    -5.300  
+91 417 48363.00 I  -.213352  .000271   .345978  .000216  I  .3762352  .0000141  2.1094 0.0096  I    -5.316     .472    -4.368     .298  -.214500   .345300   .3762400    -4.800    -5.400  
+91 418 48364.00 I  -.212598  .000271   .349971  .000204  I  .3742255  .0000142  1.9229 0.0103  I    -5.479     .472    -4.811     .298  -.213800   .349200   .3742100    -5.100    -5.500  
+91 419 48365.00 I  -.212116  .000271   .353997  .000204  I  .3723417  .0000151  1.8796 0.0099  I    -5.340     .419    -5.119     .298  -.213200   .353200   .3722700    -5.200    -5.600  
+91 420 48366.00 I  -.211712  .000325   .357991  .000226  I  .3704127  .0000138  1.9971 0.0101  I    -5.100     .310    -5.305     .298  -.212800   .357100   .3702800    -5.400    -5.800  
+91 421 48367.00 I  -.211353  .000303   .361955  .000229  I  .3683187  .0000133  2.2009 0.0089  I    -4.950     .310    -5.450     .298  -.212400   .361000   .3681400    -5.500    -5.900  
+91 422 48368.00 I  -.211093  .000262   .365896  .000232  I  .3660009  .0000113  2.4348 0.0090  I    -4.888     .308    -5.578     .298  -.212200   .365000   .3657900    -5.600    -6.100  
+91 423 48369.00 I  -.210987  .000445   .369820  .000321  I  .3634567  .0000121  2.6459 0.0083  I    -4.781     .352    -5.646     .298  -.212000   .368900   .3632200    -5.600    -6.300  
+91 424 48370.00 I  -.211028  .000445   .373708  .000321  I  .3607315  .0000121  2.7896 0.0075  I    -4.551     .316    -5.561     .298  -.211900   .372700   .3605000    -5.500    -6.600  
+91 425 48371.00 I  -.211077  .000445   .377499  .000321  I  .3579095  .0000089  2.8371 0.0099  I    -4.358     .316    -5.267     .298  -.211800   .376600   .3576800    -5.400    -6.800  
+91 426 48372.00 I  -.211001  .000484   .381195  .000341  I  .3550906  .0000156  2.7847 0.0077  I    -4.312     .252    -4.897     .298  -.211700   .380400   .3548800    -5.200    -7.000  
+91 427 48373.00 I  -.210716  .000413   .384817  .000306  I  .3523675  .0000126  2.6501 0.0103  I    -4.342     .380    -4.727     .299  -.211500   .384200   .3521800    -5.000    -7.300  
+91 428 48374.00 I  -.210235  .000490   .388395  .000348  I  .3498073  .0000134  2.4647 0.0094  I    -4.325     .397    -4.873     .338  -.211200   .388000   .3496500    -4.800    -7.400  
+91 429 48375.00 I  -.209581  .000188   .391966  .000369  I  .3474433  .0000139  2.2638 0.0101  I    -4.234     .397    -5.223     .338  -.210700   .391700   .3473000    -4.600    -7.600  
+91 430 48376.00 I  -.208780  .000178   .395564  .000303  I  .3452739  .0000152  2.0804 0.0103  I    -4.196     .399    -5.528     .346  -.210000   .395400   .3451300    -4.500    -7.700  
+91 5 1 48377.00 I  -.207855  .000178   .399210  .000303  I  .3432674  .0000152  1.9424 0.0101  I    -4.323     .352    -5.593     .310  -.209200   .399100   .3431200    -4.300    -7.800  
+91 5 2 48378.00 I  -.206791  .000178   .402890  .000303  I  .3413683  .0000134  1.8669 0.0123  I    -4.506     .352    -5.526     .310  -.208200   .402800   .3412100    -4.200    -7.800  
+91 5 3 48379.00 I  -.205572  .000108   .406589  .000417  I  .3395114  .0000193  1.8580 0.0106  I    -4.559     .146    -5.577     .153  -.207000   .406500   .3393200    -4.100    -7.800  
+91 5 4 48380.00 I  -.204205  .000148   .410310  .000422  I  .3376298  .0000164  1.9166 0.0111  I    -4.478     .257    -5.739     .202  -.205400   .409500   .3374200    -3.800    -7.600  
+91 5 5 48381.00 I  -.202702  .000149   .414042  .000292  I  .3356575  .0000109  2.0373 0.0101  I    -4.345     .257    -5.881     .202  -.203900   .413200   .3354200    -3.900    -7.400  
+91 5 6 48382.00 I  -.201075  .000138   .417773  .000260  I  .3335389  .0000117  2.2068 0.0075  I    -4.170     .426    -5.861     .199  -.202400   .417000   .3332900    -4.000    -7.300  
+91 5 7 48383.00 I  -.199327  .000207   .421520  .000251  I  .3312343  .0000104  2.4052 0.0075  I    -3.938     .472    -5.643     .212  -.200700   .420700   .3309700    -4.200    -7.100  
+91 5 8 48384.00 I  -.197475  .000211   .425296  .000252  I  .3287302  .0000093  2.5987 0.0071  I    -3.766     .429    -5.402     .193  -.198900   .424400   .3284700    -4.300    -7.000  
+91 5 9 48385.00 I  -.195569  .000215   .429075  .000204  I  .3260494  .0000097  2.7548 0.0069  I    -3.882     .429    -5.315     .193  -.197000   .428200   .3257900    -4.400    -6.800  
+91 510 48386.00 I  -.193643  .000212   .432845  .000216  I  .3232425  .0000101  2.8454 0.0076  I    -4.231     .487    -5.414     .150  -.195100   .431900   .3229800    -4.500    -6.600  
+91 511 48387.00 I  -.191707  .000242   .436597  .000136  I  .3203892  .0000116  2.8444 0.0081  I    -4.546     .410    -5.495     .142  -.193000   .435600   .3201200    -4.700    -6.400  
+91 512 48388.00 I  -.189725  .000249   .440306  .000143  I  .3175869  .0000127  2.7439 0.0086  I    -4.762     .204    -5.377     .105  -.190700   .439200   .3173100    -4.900    -6.300  
+91 513 48389.00 I  -.187655  .000249   .443949  .000143  I  .3149288  .0000127  2.5613 0.0109  I    -5.019     .204    -5.165     .105  -.188400   .442800   .3146500    -5.100    -6.100  
+91 514 48390.00 I  -.185453  .000225   .447502  .000166  I  .3124747  .0000178  2.3474 0.0109  I    -5.347     .215    -5.087     .116  -.185900   .446400   .3122100    -5.400    -6.000  
+91 515 48391.00 I  -.183061  .000220   .450942  .000164  I  .3102256  .0000178  2.1587 0.0126  I    -5.545     .353    -5.200     .298  -.183200   .449800   .3099900    -5.800    -5.800  
+91 516 48392.00 I  -.180442  .000220   .454273  .000164  I  .3081325  .0000178  2.0444 0.0151  I    -5.432     .353    -5.414     .298  -.180500   .453200   .3079300    -6.200    -5.600  
+91 517 48393.00 I  -.177606  .000238   .457530  .000191  I  .3061030  .0000244  2.0315 0.0127  I    -5.108     .403    -5.555     .298  -.177600   .456500   .3059200    -6.700    -5.500  
+91 518 48394.00 I  -.174586  .000267   .460741  .000158  I  .3040388  .0000181  2.1108 0.0137  I    -4.869     .623    -5.615     .298  -.174600   .459700   .3038700    -7.100    -5.400  
+91 519 48395.00 I  -.171472  .000218   .463903  .000134  I  .3018647  .0000126  2.2402 0.0112  I    -4.892     .623    -5.662     .298  -.171700   .462800   .3017100    -7.400    -5.300  
+91 520 48396.00 I  -.168371  .000215   .467004  .000132  I  .2995630  .0000132  2.3556 0.0078  I    -5.140     .607    -5.695     .298  -.168700   .465900   .2994200    -7.700    -5.200  
+91 521 48397.00 I  -.165388  .000212   .470030  .000114  I  .2971748  .0000091  2.4069 0.0079  I    -5.432     .675    -5.668     .298  -.165700   .468900   .2970500    -7.900    -5.100  
+91 522 48398.00 I  -.162556  .000212   .472974  .000114  I  .2947821  .0000086  2.3594 0.0063  I    -5.630     .550    -5.571     .298  -.162800   .471900   .2946900    -8.000    -5.000  
+91 523 48399.00 I  -.159759  .000212   .475856  .000114  I  .2924858  .0000087  2.2226 0.0065  I    -5.810     .550    -5.435     .298  -.159800   .474800   .2924200    -8.000    -5.000  
+91 524 48400.00 I  -.156924  .000113   .478719  .000125  I  .2903538  .0000097  2.0351 0.0062  I    -5.978     .380    -5.284     .298  -.156900   .477700   .2902800    -7.900    -5.000  
+91 525 48401.00 I  -.154015  .000118   .481596  .000116  I  .2884229  .0000089  1.8258 0.0080  I    -6.086     .313    -5.144     .298  -.154000   .480600   .2883200    -7.700    -5.100  
+91 526 48402.00 I  -.151055  .000160   .484485  .000185  I  .2866991  .0000128  1.6258 0.0078  I    -6.133     .231    -5.105     .109  -.151100   .483400   .2865400    -7.500    -5.200  
+91 527 48403.00 I  -.148081  .000160   .487372  .000181  I  .2851582  .0000129  1.4650 0.0104  I    -6.087     .231    -5.301     .109  -.148200   .486200   .2849100    -7.300    -5.300  
+91 528 48404.00 I  -.145115  .000157   .490241  .000170  I  .2837485  .0000163  1.3660 0.0119  I    -5.953     .273    -5.782     .180  -.145200   .489000   .2834100    -7.000    -5.400  
+91 529 48405.00 I  -.142177  .000152   .493080  .000171  I  .2824062  .0000199  1.3271 0.0173  I    -5.781     .422    -6.379     .172  -.142300   .491800   .2819600    -6.800    -5.500  
+91 530 48406.00 I  -.139273  .000146   .495891  .000177  I  .2810759  .0000305  1.3433 0.0255  I    -5.569     .422    -6.709     .172  -.139400   .494600   .2805200    -6.500    -5.600  
+91 531 48407.00 I  -.136397  .000198   .498678  .000226  I  .2797012  .0000469  1.4146 0.0208  I    -5.318     .579    -6.566     .207  -.136400   .497400   .2790300    -6.300    -5.700  
+91 6 1 48408.00 I  -.133530  .000321   .501446  .000242  I  .2782301  .0000282  1.5357 0.0276  I    -5.291     .457    -6.261     .197  -.133300   .500200   .2774600    -6.100    -5.900  
+91 6 2 48409.00 I  -.130632  .000320   .504185  .000269  I  .2766170  .0000291  1.6951 0.0203  I    -5.610     .428    -6.039     .181  -.130200   .502900   .2758000    -5.900    -6.100  
+91 6 3 48410.00 I  -.127650  .000320   .506887  .000269  I  .2748345  .0000291  1.8705 0.0196  I    -6.055     .432    -5.823     .160  -.127300   .505700   .2751600    -6.500    -6.000  
+91 6 4 48411.00 I  -.124536  .000327   .509546  .000294  I  .2728759  .0000263  2.0461 0.0175  I    -6.284     .292    -5.518     .164  -.124100   .508500   .2730300    -6.700    -5.800  
+91 6 5 48412.00 I  -.121236  .000327   .512145  .000294  I  .2707460  .0000194  2.2107 0.0163  I    -6.168     .393    -5.250     .145  -.120800   .511200   .2707400    -6.800    -5.700  
+91 6 6 48413.00 I  -.117799  .000327   .514711  .000294  I  .2684654  .0000193  2.3425 0.0140  I    -6.030     .393    -5.246     .145  -.117500   .513900   .2683000    -7.000    -5.500  
+91 6 7 48414.00 I  -.114309  .000344   .517299  .000260  I  .2660808  .0000202  2.4150 0.0127  I    -6.211     .447    -5.496     .104  -.114000   .516500   .2657700    -7.200    -5.400  
+91 6 8 48415.00 I  -.110827  .000563   .519947  .000464  I  .2636634  .0000165  2.4034 0.0128  I    -6.785     .599    -5.717     .114  -.110500   .519100   .2633500    -7.400    -5.300  
+91 6 9 48416.00 I  -.107321  .000554   .522631  .000423  I  .2613040  .0000157  2.3017 0.0113  I    -7.525     .599    -5.661     .114  -.106900   .521700   .2610100    -7.700    -5.200  
+91 610 48417.00 I  -.103738  .000562   .525310  .000417  I  .2590792  .0000155  2.1425 0.0107  I    -8.189     .591    -5.415     .298  -.103200   .524200   .2588000    -8.000    -5.100  
+91 611 48418.00 I  -.100023  .000588   .527945  .000391  I  .2570190  .0000146  1.9833 0.0106  I    -8.576     .630    -5.255     .298  -.099400   .526700   .2567700    -8.300    -5.100  
+91 612 48419.00 I  -.096172  .000588   .530474  .000391  I  .2551011  .0000145  1.8587 0.0103  I    -8.545     .515    -5.269     .298  -.095600   .529000   .2549000    -8.600    -5.000  
+91 613 48420.00 I  -.092407  .000593   .532808  .000433  I  .2532746  .0000146  1.8152 0.0109  I    -8.255     .515    -5.288     .298  -.091800   .531300   .2530800    -9.000    -5.000  
+91 614 48421.00 I  -.088697  .000518   .534938  .000423  I  .2514330  .0000162  1.8846 0.0103  I    -7.917     .406    -5.209     .298  -.088100   .533500   .2512000    -9.200    -5.000  
+91 615 48422.00 I  -.084948  .000419   .536894  .000368  I  .2494815  .0000146  2.0255 0.0108  I    -7.706     .339    -5.161     .115  -.084400   .535600   .2492100    -9.400    -5.100  
+91 616 48423.00 I  -.081217  .000355   .538697  .000372  I  .2473810  .0000143  2.1693 0.0105  I    -7.679     .113    -5.232     .134  -.080700   .537500   .2470600    -9.600    -5.100  
+91 617 48424.00 I  -.077580  .000356   .540381  .000347  I  .2451650  .0000151  2.2466 0.0113  I    -7.830     .113    -5.299     .134  -.077200   .539300   .2448100    -9.600    -5.200  
+91 618 48425.00 I  -.074071  .000264   .542020  .000390  I  .2429220  .0000175  2.2213 0.0116  I    -8.136     .134    -5.176     .180  -.073700   .541100   .2425200    -9.500    -5.200  
+91 619 48426.00 I  -.070701  .000211   .543663  .000307  I  .2407553  .0000175  2.0975 0.0124  I    -8.462     .471    -4.875     .310  -.070300   .542700   .2403400    -9.300    -5.300  
+91 620 48427.00 I  -.067473  .000211   .545292  .000307  I  .2387508  .0000175  1.9025 0.0149  I    -8.565     .471    -4.645     .310  -.067100   .544300   .2383200    -9.000    -5.400  
+91 621 48428.00 I  -.064420  .000263   .546876  .000301  I  .2369620  .0000240  1.6723 0.0145  I    -8.332     .653    -4.638     .399  -.063900   .545900   .2365500    -8.700    -5.500  
+91 622 48429.00 I  -.061563  .000297   .548392  .000495  I  .2354087  .0000232  1.4344 0.0160  I    -7.965     .656    -4.800     .306  -.060900   .547300   .2350300    -8.300    -5.600  
+91 623 48430.00 I  -.058826  .000265   .549874  .000379  I  .2340892  .0000213  1.2084 0.0165  I    -7.811     .656    -4.982     .306  -.057800   .548800   .2337300    -7.900    -5.700  
+91 624 48431.00 I  -.056103  .000338   .551379  .000418  I  .2329812  .0000235  1.0145 0.0162  I    -8.017     .646    -5.152     .287  -.054800   .550300   .2326600    -7.400    -5.800  
+91 625 48432.00 I  -.053290  .000370   .552965  .000391  I  .2320435  .0000244  0.8707 0.0135  I    -8.521     .563    -5.326     .248  -.051700   .551800   .2317300    -7.000    -5.900  
+91 626 48433.00 I  -.050301  .000261   .554667  .000331  I  .2312179  .0000133  0.7928 0.0138  I    -9.139     .487    -5.398     .233  -.048700   .553200   .2309100    -6.700    -6.100  
+91 627 48434.00 I  -.047131  .000261   .556431  .000331  I  .2304329  .0000131  0.7901 0.0100  I    -9.525     .455    -5.267     .225  -.045600   .554700   .2301000    -6.400    -6.200  
+91 628 48435.00 I  -.043799  .000271   .558138  .000322  I  .2296136  .0000150  0.8599 0.0101  I    -9.661     .323    -5.170     .241  -.042500   .556200   .2292700    -6.200    -6.400  
+91 629 48436.00 I  -.040312  .000264   .559642  .000410  I  .2286955  .0000154  0.9826 0.0117  I    -9.769     .339    -5.053     .216  -.039300   .557600   .2283500    -6.100    -6.600  
+91 630 48437.00 I  -.036690  .000356   .560847  .000438  I  .2276412  .0000180  1.1269 0.0119  I   -10.016     .284    -4.882     .222  -.036000   .559000   .2272900    -6.100    -6.800  
+91 7 1 48438.00 I  -.033013  .000341   .561856  .000417  I  .2264439  .0000182  1.2645 0.0127  I   -10.402     .345    -4.829     .196  -.032600   .560300   .2260700    -6.200    -6.900  
+91 7 2 48439.00 I  -.029366  .000322   .562824  .000430  I  .2251163  .0000180  1.3898 0.0160  I   -10.710     .362    -4.824     .193  -.029100   .561600   .2247200    -6.300    -7.100  
+91 7 3 48440.00 I  -.025727  .000433   .563843  .000543  I  .2236695  .0000262  1.4997 0.0159  I   -10.699     .391    -4.686     .200  -.025000   .563100   .2234700    -6.800    -7.000  
+91 7 4 48441.00 I  -.021941  .000433   .564872  .000543  I  .2221343  .0000263  1.5575 0.0213  I   -10.451     .418    -4.601     .196  -.021200   .564100   .2219200    -7.200    -6.800  
+91 7 5 48442.00 I  -.017994  .000512   .565876  .000239  I  .2205824  .0000335  1.5317 0.0196  I   -10.264     .452    -4.665     .245  -.017400   .565000   .2203700    -7.900    -6.600  
+91 7 6 48443.00 I  -.013950  .000445   .566824  .000198  I  .2190993  .0000291  1.4209 0.0197  I   -10.327     .986    -4.746     .177  -.013500   .565900   .2189100    -8.400    -6.400  
+91 7 7 48444.00 I  -.009878  .000182   .567683  .000208  I  .2177630  .0000207  1.2430 0.0155  I   -10.606    1.319    -4.691     .298  -.009600   .566700   .2176000    -8.800    -6.200  
+91 7 8 48445.00 I  -.005846  .000158   .568421  .000135  I  .2166216  .0000109  1.0402 0.0117  I   -10.986    1.119    -4.555     .298  -.005700   .567400   .2164800    -9.600    -6.200  
+91 7 9 48446.00 I  -.001914  .000187   .569021  .000146  I  .2156705  .0000109  0.8736 0.0079  I   -11.405     .986    -4.539     .298  -.001700   .568100   .2155300   -10.200    -6.100  
+91 710 48447.00 I   .001903  .000183   .569477  .000280  I  .2148371  .0000115  0.8187 0.0102  I   -11.798     .892    -4.726     .183   .002300   .568700   .2146700   -10.900    -5.800  
+91 711 48448.00 I   .005725  .000317   .569812  .000273  I  .2139795  .0000172  0.9245 0.0108  I   -11.999     .780    -4.998     .193   .006300   .569200   .2137800   -11.400    -5.700  
+91 712 48449.00 I   .009670  .000328   .570109  .000284  I  .2129437  .0000182  1.1634 0.0123  I   -12.132     .841    -5.069     .211   .010300   .569700   .2127100   -11.800    -5.600  
+91 713 48450.00 I   .013729  .000305   .570442  .000518  I  .2116343  .0000175  1.4575 0.0122  I   -12.357     .919    -4.983     .196   .014400   .570200   .2113800   -11.500    -5.500  
+91 714 48451.00 I   .017946  .000282   .570858  .000548  I  .2100398  .0000163  1.7183 0.0152  I   -12.478     .959    -4.854     .201   .018700   .570600   .2097900   -11.600    -5.400  
+91 715 48452.00 I   .022375  .000425   .571352  .000699  I  .2082363  .0000249  1.8633 0.0140  I   -12.266     .920    -4.788     .207   .023000   .570900   .2080100   -11.700    -5.300  
+91 716 48453.00 I   .026968  .000321   .571904  .000611  I  .2063631  .0000227  1.8587 0.0165  I   -12.268     .929    -4.726     .222   .027400   .571200   .2061600   -11.800    -5.400  
+91 717 48454.00 I   .031649  .000327   .572418  .000593  I  .2045564  .0000217  1.7417 0.0160  I   -12.998    1.072    -4.642     .209   .031800   .571500   .2043600   -11.800    -5.500  
+91 718 48455.00 I   .036358  .000250   .572701  .000600  I  .2029001  .0000226  1.5636 0.0153  I   -13.903     .835    -4.660     .192   .036400   .571700   .2027300   -12.000    -5.500  
+91 719 48456.00 I   .041044  .000262   .572782  .000406  I  .2014419  .0000215  1.3476 0.0159  I   -14.393     .720    -4.676     .209   .041000   .571900   .2012900   -12.100    -5.600  
+91 720 48457.00 I   .045589  .000312   .572739  .000526  I  .2002059  .0000223  1.1288 0.0171  I   -14.254     .712    -4.778     .186   .045600   .572000   .2000600   -12.300    -5.600  
+91 721 48458.00 I   .050029  .000360   .572650  .000533  I  .1991700  .0000267  0.9531 0.0163  I   -14.009     .725    -4.872     .210   .050300   .572000   .1990200   -12.600    -5.600  
+91 722 48459.00 I   .054497  .000276   .572540  .000537  I  .1982781  .0000239  0.8422 0.0212  I   -14.205     .768    -4.856     .207   .054900   .572000   .1981300   -12.900    -5.500  
+91 723 48460.00 I   .059109  .000440   .572417  .000734  I  .1974633  .0000329  0.7981 0.0162  I   -14.833     .628    -4.836     .218   .059500   .571900   .1973100   -13.300    -5.500  
+91 724 48461.00 I   .063817  .000450   .572247  .000700  I  .1966611  .0000219  0.8163 0.0207  I   -15.457     .610    -4.746     .236   .064000   .571700   .1965100   -13.700    -5.400  
+91 725 48462.00 I   .068502  .000426   .572009  .000698  I  .1958101  .0000252  0.8963 0.0200  I   -15.667     .610    -4.748     .236   .068500   .571500   .1956600   -14.100    -5.400  
+91 726 48463.00 I   .073105  .000320   .571718  .000535  I  .1948501  .0000335  1.0315 0.0185  I   -15.531     .527    -4.763     .286   .072800   .571200   .1947100   -14.600    -5.300  
+91 727 48464.00 I   .077590  .000260   .571383  .000358  I  .1937368  .0000272  1.1975 0.0236  I   -15.331     .583    -4.566     .331   .076900   .570800   .1936000   -15.000    -5.300  
+91 728 48465.00 I   .081901  .000312   .571007  .000318  I  .1924553  .0000332  1.3628 0.0219  I   -15.227     .693    -4.295     .259   .081000   .570400   .1923400   -15.300    -5.300  
+91 729 48466.00 I   .085971  .000323   .570583  .000412  I  .1910240  .0000344  1.4898 0.0252  I   -15.260     .678    -4.151     .251   .084800   .569800   .1909400   -15.700    -5.300  
+91 730 48467.00 I   .089710  .000339   .570085  .000444  I  .1895032  .0000380  1.5344 0.0256  I   -15.242     .723    -4.454     .280   .088500   .569200   .1894300   -16.000    -5.300  
+91 731 48468.00 I   .093032  .000335   .569494  .000501  I  .1879815  .0000378  1.5000 0.0260  I   -15.100     .637    -4.834     .258   .092000   .568400   .1878800   -16.200    -5.300  
+91 8 1 48469.00 I   .096042  .000322   .568777  .000476  I  .1865172  .0000354  1.4237 0.0264  I   -14.997     .629    -4.921     .247   .095400   .567600   .1863600   -16.400    -5.400  
+91 8 2 48470.00 I   .098933  .000331   .567909  .000479  I  .1851435  .0000370  1.3193 0.0244  I   -15.069     .594    -4.824     .178   .098700   .566700   .1849200   -16.600    -5.500  
+91 8 3 48471.00 I   .101871  .000419   .566886  .000584  I  .1838886  .0000336  1.1848 0.0232  I   -15.354     .477    -4.773     .182   .101900   .565600   .1836600   -16.800    -5.600  
+91 8 4 48472.00 I   .104958  .000412   .565705  .000356  I  .1827794  .0000281  1.0339 0.0217  I   -15.846     .458    -4.704     .182   .105100   .564500   .1825800   -17.000    -5.600  
+91 8 5 48473.00 I   .108217  .000412   .564427  .000356  I  .1818143  .0000275  0.9025 0.0171  I   -16.345     .420    -4.719     .161   .108400   .563400   .1816700   -17.200    -5.700  
+91 8 6 48474.00 I   .111662  .000420   .563106  .000306  I  .1809531  .0000194  0.8348 0.0175  I   -16.595     .442    -4.836     .145   .112300   .562700   .1807900   -15.800    -5.400  
+91 8 7 48475.00 I   .115248  .000432   .561778  .000249  I  .1801100  .0000216  0.8711 0.0145  I   -16.529     .411    -4.860     .280   .115600   .561500   .1799900   -16.000    -5.400  
+91 8 8 48476.00 I   .118816  .000470   .560444  .000254  I  .1791690  .0000215  1.0330 0.0165  I   -16.317     .442    -4.781     .288   .118900   .560200   .1790600   -16.200    -5.300  
+91 8 9 48477.00 I   .122395  .000391   .559084  .000128  I  .1780101  .0000249  1.2965 0.0134  I   -16.217     .214    -4.613     .341   .122300   .558900   .1778800   -16.400    -5.200  
+91 810 48478.00 I   .126042  .000293   .557680  .000372  I  .1765680  .0000161  1.5840 0.0151  I   -16.291     .207    -4.488     .285   .125800   .557500   .1764200   -16.700    -5.000  
+91 811 48479.00 I   .129721  .000206   .556237  .000483  I  .1748620  .0000171  1.8117 0.0117  I   -16.385     .169    -4.495     .343   .129300   .556000   .1746800   -16.900    -4.900  
+91 812 48480.00 I   .133366  .000206   .554770  .000483  I  .1729817  .0000169  1.9279 0.0112  I   -16.460     .169    -4.625     .343   .132700   .554500   .1727600   -17.200    -4.700  
+91 813 48481.00 I   .136914  .000108   .553290  .000495  I  .1710455  .0000145  1.9261 0.0122  I   -16.781     .193    -4.687     .298   .136200   .552900   .1708200   -17.500    -4.600  
+91 814 48482.00 I   .140316  .000209   .551783  .000497  I  .1691646  .0000177  1.8190 0.0106  I   -17.533     .286    -4.580     .402   .139500   .551200   .1689400   -17.800    -4.400  
+91 815 48483.00 I   .143560  .000204   .550210  .000519  I  .1674311  .0000155  1.6410 0.0146  I   -18.378     .282    -4.443     .363   .142800   .549400   .1672400   -18.100    -4.300  
+91 816 48484.00 I   .146642  .000233   .548540  .000504  I  .1658892  .0000231  1.4432 0.0168  I   -18.744     .327    -4.439     .455   .146000   .547600   .1657200   -18.300    -4.300  
+91 817 48485.00 I   .149543  .000420   .546773  .000502  I  .1645384  .0000297  1.2642 0.0185  I   -18.442     .649    -4.516     .354   .149000   .545700   .1644000   -18.400    -4.200  
+91 818 48486.00 I   .152226  .000407   .544914  .000434  I  .1633450  .0000289  1.1325 0.0172  I   -17.924     .649    -4.500     .354   .151800   .543800   .1632200   -18.400    -4.300  
+91 819 48487.00 I   .154714  .000316   .542974  .000271  I  .1622537  .0000173  1.0597 0.0179  I   -17.630     .603    -4.334     .342   .154600   .541800   .1621400   -18.300    -4.300  
+91 820 48488.00 I   .157052  .000334   .540992  .000293  I  .1612062  .0000211  1.0449 0.0128  I   -17.572     .724    -4.122     .170   .157200   .539800   .1611000   -18.200    -4.400  
+91 821 48489.00 I   .159312  .000290   .538993  .000235  I  .1601450  .0000188  1.0867 0.0132  I   -17.533     .895    -4.009     .239   .159700   .537800   .1600400   -18.100    -4.500  
+91 822 48490.00 I   .161609  .000290   .536939  .000235  I  .1590155  .0000159  1.1803 0.0136  I   -17.306     .895    -4.081     .239   .162200   .535800   .1588800   -17.900    -4.600  
+91 823 48491.00 I   .164054  .000326   .534799  .000118  I  .1577638  .0000197  1.3355 0.0115  I   -16.927     .871    -4.043     .289   .164500   .533700   .1575700   -17.800    -4.700  
+91 824 48492.00 I   .166659  .000282   .532583  .000292  I  .1563302  .0000166  1.5335 0.0129  I   -16.758     .743    -3.944     .221   .166900   .531700   .1561000   -17.700    -4.800  
+91 825 48493.00 I   .169344  .000285   .530338  .000303  I  .1546978  .0000167  1.7287 0.0167  I   -16.866     .738    -3.992     .237   .169300   .529600   .1544400   -17.600    -4.900  
+91 826 48494.00 I   .171997  .000348   .528109  .000351  I  .1528838  .0000290  1.8911 0.0149  I   -17.094     .724    -4.229     .235   .171700   .527600   .1525900   -17.600    -5.000  
+91 827 48495.00 I   .174560  .000335   .525956  .000357  I  .1509325  .0000248  2.0029 0.0190  I   -17.324     .537    -4.543     .230   .174100   .525600   .1506300   -17.500    -5.100  
+91 828 48496.00 I   .177137  .000329   .523935  .000371  I  .1489024  .0000246  2.0416 0.0163  I   -17.565     .621    -4.617     .212   .176600   .523500   .1486100   -17.600    -5.200  
+91 829 48497.00 I   .179726  .000329   .521986  .000371  I  .1468790  .0000213  1.9912 0.0201  I   -17.813     .621    -4.468     .212   .179200   .521400   .1466000   -17.600    -5.300  
+91 830 48498.00 I   .182299  .000283   .520033  .000311  I  .1449414  .0000318  1.8767 0.0208  I   -18.113     .682    -4.510     .276   .181800   .519400   .1446900   -17.700    -5.300  
+91 831 48499.00 I   .184849  .000307   .518012  .000544  I  .1431336  .0000358  1.7383 0.0217  I   -18.328     .634    -4.642     .190   .184500   .517300   .1429000   -17.800    -5.300  
+91 9 1 48500.00 I   .187398  .000307   .515918  .000544  I  .1414584  .0000296  1.6192 0.0197  I   -18.443     .634    -4.739     .190   .187300   .515200   .1412500   -18.000    -5.300  
+91 9 2 48501.00 I   .189978  .000246   .513764  .000420  I  .1398743  .0000164  1.5629 0.0177  I   -18.529     .597    -4.849     .116   .190100   .513100   .1396800   -18.100    -5.300  
+91 9 3 48502.00 I   .192616  .000243   .511566  .000386  I  .1383010  .0000194  1.6015 0.0106  I   -18.582     .884    -4.977     .298   .192900   .510900   .1381200   -18.400    -5.300  
+91 9 4 48503.00 I   .195337  .000193   .509352  .000297  I  .1366345  .0000135  1.7504 0.0116  I   -18.582     .828    -5.070     .298   .195700   .508800   .1364500   -18.700    -5.200  
+91 9 5 48504.00 I   .198209  .000193   .507148  .000297  I  .1347696  .0000129  1.9910 0.0096  I   -18.493     .771    -5.058     .298   .198400   .506600   .1345800   -19.000    -5.100  
+91 9 6 48505.00 I   .201270  .000208   .504976  .000244  I  .1326407  .0000137  2.2669 0.0099  I   -18.352     .895    -4.901     .107   .201400   .504300   .1324500   -19.100    -5.100  
+91 9 7 48506.00 I   .204388  .000238   .502847  .000267  I  .1302495  .0000150  2.5019 0.0089  I   -18.377     .924    -4.586     .114   .204100   .502100   .1300500   -19.300    -4.900  
+91 9 8 48507.00 I   .207393  .000250   .500743  .000270  I  .1276691  .0000115  2.6405 0.0092  I   -18.509     .971    -4.302     .130   .206700   .499800   .1274400   -19.500    -4.800  
+91 9 9 48508.00 I   .210161  .000252   .498606  .000240  I  .1250076  .0000108  2.6618 0.0077  I   -18.510     .887    -4.146     .132   .209100   .497500   .1247500   -19.600    -4.700  
+91 910 48509.00 I   .212566  .000297   .496400  .000231  I  .1223822  .0000102  2.5731 0.0093  I   -18.391     .951    -3.983     .147   .211300   .495200   .1221100   -19.700    -4.500  
+91 911 48510.00 I   .214480  .000366   .494106  .000365  I  .1198897  .0000152  2.3994 0.0097  I   -18.644     .915    -3.835     .137   .213300   .492800   .1196300   -19.600    -4.500  
+91 912 48511.00 I   .215997  .000375   .491708  .000342  I  .1175991  .0000164  2.1785 0.0112  I   -19.249     .858    -3.777     .139   .215100   .490300   .1173700   -19.400    -4.400  
+91 913 48512.00 I   .217312  .000338   .489190  .000349  I  .1155326  .0000164  1.9578 0.0119  I   -19.719     .821    -3.871     .138   .216900   .487800   .1153500   -19.300    -4.400  
+91 914 48513.00 I   .218598  .000315   .486539  .000256  I  .1136722  .0000173  1.7711 0.0120  I   -19.703     .373    -4.107     .102   .218600   .485200   .1135500   -19.000    -4.400  
+91 915 48514.00 I   .219935  .000309   .483763  .000246  I  .1119721  .0000175  1.6392 0.0123  I   -19.204     .373    -4.236     .102   .220200   .482500   .1119000   -18.700    -4.400  
+91 916 48515.00 I   .221414  .000309   .480892  .000246  I  .1103748  .0000176  1.5643 0.0133  I   -18.511     .373    -4.178     .102   .221900   .479800   .1103500   -18.400    -4.500  
+91 917 48516.00 I   .223135  .000381   .477963  .000258  I  .1088260  .0000201  1.5418 0.0119  I   -17.877     .194    -4.070     .103   .223700   .477000   .1088200   -18.100    -4.500  
+91 918 48517.00 I   .225137  .000394   .475008  .000145  I  .1072693  .0000160  1.5851 0.0126  I   -17.416     .347    -4.011     .139   .225500   .474200   .1072500   -17.900    -4.600  
+91 919 48518.00 I   .227290  .000394   .472047  .000145  I  .1056320  .0000153  1.6998 0.0136  I   -17.214     .347    -3.972     .139   .227400   .471300   .1055800   -17.700    -4.700  
+91 920 48519.00 I   .229438  .000457   .469103  .000174  I  .1038529  .0000220  1.8647 0.0144  I   -17.332     .490    -3.912     .182   .229400   .468400   .1037400   -17.600    -4.700  
+91 921 48520.00 I   .231466  .000403   .466196  .000482  I  .1018945  .0000243  2.0538 0.0162  I   -17.725     .484    -3.871     .183   .231400   .465600   .1017200   -17.600    -4.700  
+91 922 48521.00 I   .233424  .000389   .463342  .000461  I  .0997466  .0000237  2.2392 0.0161  I   -18.181     .484    -3.907     .183   .233500   .462700   .0995100   -17.700    -4.700  
+91 923 48522.00 I   .235406  .000473   .460554  .000503  I  .0974257  .0000211  2.3960 0.0179  I   -18.536     .436    -4.007     .180   .235600   .459800   .0971300   -17.800    -4.700  
+91 924 48523.00 I   .237502  .000402   .457846  .000568  I  .0949725  .0000268  2.4991 0.0172  I   -18.737     .311    -4.092     .170   .237800   .456900   .0946600   -17.900    -4.600  
+91 925 48524.00 I   .239748  .000402   .455199  .000568  I  .0924587  .0000272  2.5087 0.0193  I   -18.768     .298    -4.082     .169   .239800   .454000   .0921500   -18.100    -4.600  
+91 926 48525.00 I   .242034  .000405   .452492  .000560  I  .0899884  .0000277  2.4185 0.0188  I   -18.671     .298    -3.937     .169   .241800   .451000   .0897100   -18.100    -4.600  
+91 927 48526.00 I   .244219  .000444   .449613  .000623  I  .0876444  .0000260  2.2601 0.0181  I   -18.468     .177    -3.834     .162   .243600   .448100   .0874100   -18.100    -4.600  
+91 928 48527.00 I   .246208  .000374   .446617  .000483  I  .0854745  .0000232  2.0827 0.0171  I   -18.199     .298    -3.786     .145   .245300   .445000   .0852900   -18.100    -4.600  
+91 929 48528.00 I   .247973  .000382   .443544  .000494  I  .0834657  .0000222  1.9451 0.0162  I   -17.883     .320    -3.782     .142   .246900   .442000   .0833200   -18.000    -4.700  
+91 930 48529.00 I   .249478  .000306   .440410  .000471  I  .0815602  .0000225  1.8797 0.0149  I   -17.537     .387    -3.835     .136   .248300   .438900   .0814500   -17.800    -4.700  
+9110 1 48530.00 I   .250664  .000364   .437218  .000392  I  .0796781  .0000200  1.8990 0.0151  I   -17.176     .425    -3.925     .158   .249500   .435700   .0795600   -17.700    -4.800  
+9110 2 48531.00 I   .251533  .000347   .433958  .000384  I  .0777307  .0000200  2.0127 0.0141  I   -16.831     .616    -4.028     .160   .250700   .432500   .0775900   -17.600    -4.800  
+9110 3 48532.00 I   .252261  .000347   .430611  .000384  I  .0756290  .0000200  2.1976 0.0155  I   -16.627     .616    -4.184     .160   .251700   .429200   .0754400   -17.500    -4.900  
+9110 4 48533.00 I   .253026  .000407   .427190  .000415  I  .0733329  .0000238  2.3905 0.0136  I   -16.658     .695    -4.398     .193   .252700   .425900   .0731100   -17.400    -5.000  
+9110 5 48534.00 I   .253919  .000279   .423732  .000271  I  .0708653  .0000185  2.5315 0.0153  I   -16.845     .545    -4.365     .149   .253500   .422600   .0706100   -17.400    -5.000  
+9110 6 48535.00 I   .254908  .000281   .420246  .000373  I  .0683008  .0000191  2.5799 0.0131  I   -17.038     .515    -4.030     .139   .254600   .419100   .0680400   -17.400    -4.400  
+9110 7 48536.00 I   .255934  .000246   .416738  .000368  I  .0657398  .0000185  2.5254 0.0129  I   -17.147     .541    -3.632     .101   .255600   .415600   .0655000   -17.400    -4.300  
+9110 8 48537.00 I   .256938  .000169   .413216  .000377  I  .0632768  .0000173  2.3904 0.0127  I   -17.156     .312    -3.380     .298   .256600   .412100   .0630400   -17.400    -4.300  
+9110 9 48538.00 I   .257857  .000190   .409694  .000378  I  .0609769  .0000173  2.2016 0.0111  I   -17.118     .366    -3.353     .298   .257500   .408700   .0607800   -17.300    -4.200  
+911010 48539.00 I   .258653  .000190   .406205  .000378  I  .0588820  .0000138  1.9875 0.0120  I   -17.198     .366    -3.475     .298   .258400   .405300   .0587400   -17.200    -4.200  
+911011 48540.00 I   .259307  .000215   .402771  .000454  I  .0569980  .0000165  1.7851 0.0092  I   -17.302     .436    -3.597     .298   .259000   .401800   .0569100   -17.100    -4.200  
+911012 48541.00 I   .259816  .000249   .399401  .000356  I  .0552983  .0000123  1.6231 0.0090  I   -17.215     .460    -3.614     .298   .259600   .398400   .0552600   -17.000    -4.200  
+911013 48542.00 I   .260215  .000207   .396078  .000143  I  .0537336  .0000070  1.5155 0.0068  I   -16.857     .405    -3.533     .298   .260100   .395000   .0537300   -16.800    -4.200  
+911014 48543.00 I   .260541  .000175   .392784  .000122  I  .0522502  .0000058  1.4590 0.0045  I   -16.315     .405    -3.440     .298   .260600   .391600   .0522600   -16.600    -4.100  
+911015 48544.00 I   .260834  .000189   .389497  .000059  I  .0508005  .0000056  1.4479 0.0039  I   -15.775     .428    -3.412     .298   .260900   .388200   .0507900   -16.300    -4.100  
+911016 48545.00 I   .261131  .000170   .386204  .000071  I  .0493325  .0000053  1.5027 0.0038  I   -15.401     .694    -3.440     .298   .261200   .384800   .0492500   -16.100    -4.100  
+911017 48546.00 I   .261471  .000170   .382917  .000071  I  .0477694  .0000052  1.6346 0.0034  I   -15.230     .644    -3.454     .298   .261400   .381500   .0476200   -15.900    -4.100  
+911018 48547.00 I   .261748  .000161   .379673  .000104  I  .0460491  .0000041  1.8092 0.0047  I   -15.191     .662    -3.447     .298   .261500   .378100   .0458400   -15.600    -4.000  
+911019 48548.00 I   .261878  .000214   .376475  .000157  I  .0441486  .0000078  1.9911 0.0048  I   -15.376     .618    -3.513     .298   .261500   .374800   .0439100   -15.400    -4.000  
+911020 48549.00 I   .261867  .000198   .373298  .000181  I  .0420742  .0000087  2.1510 0.0064  I   -15.685     .579    -3.635     .108   .261400   .371400   .0418300   -15.300    -3.900  
+911021 48550.00 I   .261762  .000206   .370065  .000195  I  .0398639  .0000101  2.2590 0.0067  I   -15.882     .579    -3.698     .114   .261200   .368100   .0396300   -15.200    -3.800  
+911022 48551.00 I   .261585  .000188   .366705  .000197  I  .0375816  .0000101  2.2907 0.0075  I   -15.900     .174    -3.618     .103   .261000   .364800   .0373800   -15.000    -3.800  
+911023 48552.00 I   .261298  .000215   .363280  .000223  I  .0353079  .0000110  2.2462 0.0074  I   -15.848     .190    -3.452     .108   .260700   .361500   .0351300   -14.900    -3.700  
+911024 48553.00 I   .260952  .000217   .359926  .000223  I  .0331125  .0000109  2.1319 0.0093  I   -15.783     .123    -3.372     .108   .260400   .358200   .0329600   -14.800    -3.600  
+911025 48554.00 I   .260565  .000098   .356662  .000266  I  .0310547  .0000150  1.9857 0.0083  I   -15.673     .791    -3.291     .135   .260100   .355000   .0309200   -14.800    -3.500  
+911026 48555.00 I   .260157  .000092   .353463  .000178  I  .0291278  .0000124  1.8794 0.0105  I   -15.454     .216    -3.183     .298   .259800   .351800   .0289900   -14.700    -3.500  
+911027 48556.00 I   .259771  .000149   .350310  .000168  I  .0272670  .0000147  1.8597 0.0099  I   -15.125     .216    -3.064     .298   .259700   .348700   .0271200   -14.700    -3.400  
+911028 48557.00 I   .259457  .000159   .347183  .000177  I  .0253742  .0000154  1.9426 0.0114  I   -14.759     .225    -2.967     .123   .259700   .345600   .0252200   -14.700    -3.400  
+911029 48558.00 I   .259269  .000285   .344065  .000169  I  .0233532  .0000175  2.1111 0.0107  I   -14.422     .225    -2.955     .123   .259900   .342600   .0231900   -14.700    -3.400  
+911030 48559.00 I   .259278  .000242   .340934  .000153  I  .0211389  .0000150  2.3187 0.0103  I   -14.129     .382    -3.040     .113   .260100   .339600   .0209600   -14.700    -3.400  
+911031 48560.00 I   .259575  .000242   .337805  .000153  I  .0187158  .0000110  2.5261 0.0097  I   -13.868     .392    -3.116     .106   .260400   .336700   .0185000   -14.800    -3.400  
+9111 1 48561.00 I   .260184  .000267   .334747  .000159  I  .0160973  .0000122  2.7015 0.0082  I   -13.691     .424    -3.089     .116   .260800   .333800   .0158700   -14.800    -3.400  
+9111 2 48562.00 I   .261067  .000325   .331831  .000191  I  .0133398  .0000122  2.7959 0.0094  I   -13.684     .595    -2.916     .140   .261100   .330900   .0131100   -14.700    -3.300  
+9111 3 48563.00 I   .262021  .000363   .329086  .000215  I  .0105434  .0000142  2.7766 0.0087  I   -13.719     .770    -2.618     .115   .261500   .328100   .0103500   -14.600    -3.300  
+9111 4 48564.00 I   .262838  .000359   .326446  .000227  I  .0078236  .0000123  2.6462 0.0089  I   -13.698     .770    -2.313     .115   .261800   .325200   .0076700   -14.400    -3.300  
+9111 5 48565.00 I   .263316  .000286   .323824  .000219  I  .0052780  .0000107  2.4346 0.0086  I   -13.595     .859    -2.146     .138   .262300   .322400   .0051600   -14.400    -3.300  
+9111 6 48566.00 I   .263351  .000337   .321155  .000272  I  .0029617  .0000119  2.2011 0.0080  I   -13.471     .836    -2.165     .139   .262400   .319700   .0028700   -14.400    -3.200  
+9111 7 48567.00 I   .263058  .000336   .318417  .000350  I  .0008641  .0000118  2.0029 0.0094  I   -13.386     .834    -2.313     .139   .262400   .317000   .0007600   -14.400    -3.200  
+9111 8 48568.00 I   .262580  .000304   .315600  .000394  I -.0010654  .0000146  1.8688 0.0074  I   -13.287     .809    -2.488     .108   .262100   .314300  -.0011900   -14.100    -3.100  
+9111 9 48569.00 I   .262019  .000290   .312716  .000437  I -.0028975  .0000089  1.8067 0.0080  I   -13.136     .990    -2.610     .103   .261800   .311400  -.0030400   -13.800    -3.100  
+911110 48570.00 I   .261375  .000200   .309796  .000277  I -.0047002  .0000066  1.8089 0.0056  I   -13.035     .990    -2.539     .103   .261200   .308500  -.0048500   -13.500    -3.000  
+911111 48571.00 I   .260618  .000200   .306885  .000277  I -.0065338  .0000068  1.8667 0.0042  I   -12.898     .856    -2.346     .298   .260500   .305600  -.0066900   -13.200    -3.000  
+911112 48572.00 I   .259733  .000138   .303992  .000279  I -.0084483  .0000053  1.9687 0.0045  I   -12.593     .936    -2.227     .298   .259700   .302600  -.0086100   -13.000    -2.900  
+911113 48573.00 I   .258711  .000145   .301113  .000274  I -.0104806  .0000058  2.0991 0.0052  I   -12.210     .876    -2.225     .298   .258800   .299700  -.0106500   -12.800    -2.900  
+911114 48574.00 I   .257557  .000145   .298217  .000274  I -.0126521  .0000090  2.2465 0.0054  I   -12.014     .814    -2.201     .298   .257800   .296700  -.0128400   -12.600    -2.800  
+911115 48575.00 I   .256324  .000132   .295259  .000276  I -.0149750  .0000091  2.3981 0.0064  I   -11.987     .476    -2.108     .298   .256800   .293800  -.0151700   -12.500    -2.800  
+911116 48576.00 I   .255081  .000275   .292194  .000336  I -.0174429  .0000090  2.5336 0.0083  I   -12.041     .406    -2.059     .298   .255700   .290800  -.0176400   -12.400    -2.900  
+911117 48577.00 I   .253909  .000412   .289017  .000356  I -.0200270  .0000139  2.6234 0.0081  I   -12.194     .442    -2.232     .105   .254700   .287800  -.0202100   -12.400    -2.900  
+911118 48578.00 I   .252920  .000399   .285821  .000373  I -.0226646  .0000135  2.6379 0.0097  I   -12.421     .429    -2.529     .104   .253700   .284700  -.0228200   -12.400    -2.900  
+911119 48579.00 I   .252234  .000378   .282720  .000381  I -.0252746  .0000135  2.5678 0.0096  I   -12.615     .479    -2.634     .105   .252700   .281700  -.0253900   -12.400    -2.900  
+911120 48580.00 I   .251872  .000399   .279772  .000361  I -.0277784  .0000136  2.4324 0.0096  I   -12.702     .475    -2.403     .116   .251800   .278600  -.0278700   -12.400    -2.800  
+911121 48581.00 I   .251571  .000399   .276910  .000361  I -.0301254  .0000136  2.2549 0.0111  I   -12.665     .475    -2.051     .116   .250900   .275500  -.0302000   -12.400    -2.700  
+911122 48582.00 I   .251059  .000350   .274132  .000274  I -.0322901  .0000176  2.0829 0.0131  I   -12.373     .546    -1.836     .109   .250100   .272400  -.0323700   -12.400    -2.600  
+911123 48583.00 I   .250193  .000409   .271398  .000419  I -.0343196  .0000223  1.9959 0.0146  I   -11.997     .643    -1.823     .298   .249200   .269300  -.0344300   -12.400    -2.500  
+911124 48584.00 I   .249052  .000340   .268621  .000541  I -.0363251  .0000232  2.0385 0.0179  I   -11.728     .705    -1.850     .298   .248400   .266200  -.0364800   -12.400    -2.400  
+911125 48585.00 I   .247766  .000355   .265706  .000554  I -.0384389  .0000280  2.2080 0.0207  I   -11.627     .831    -1.807     .150   .247500   .263100  -.0386300   -12.400    -2.300  
+911126 48586.00 I   .246464  .000455   .262558  .000630  I -.0407691  .0000344  2.4616 0.0224  I   -11.648     .980    -1.716     .200   .246700   .260000  -.0409800   -12.400    -2.300  
+911127 48587.00 I   .245276  .000455   .259134  .000630  I -.0433617  .0000349  2.7142 0.0240  I   -11.670     .980    -1.590     .200   .246000   .256900  -.0435600   -12.500    -2.200  
+911128 48588.00 I   .244270  .000455   .255563  .000630  I -.0461708  .0000336  2.8869 0.0244  I   -11.741     .980    -1.381     .200   .245200   .253800  -.0463600   -12.500    -2.200  
+911129 48589.00 I   .243445  .000411   .251970  .000853  I -.0491002  .0000340  2.9537 0.0202  I   -11.722     .913    -1.306     .232   .244400   .250800  -.0492700   -12.500    -2.100  
+911130 48590.00 I   .242748  .000348   .248441  .000682  I -.0520428  .0000223  2.9144 0.0217  I   -11.670     .767    -1.345     .236   .243600   .247800  -.0522000   -12.400    -2.100  
+9112 1 48591.00 I   .242116  .000386   .245061  .000668  I -.0548992  .0000270  2.7858 0.0131  I   -11.714     .395    -1.423     .242   .242700   .244800  -.0550500   -12.400    -2.100  
+9112 2 48592.00 I   .241483  .000254   .241909  .000387  I -.0575943  .0000136  2.5968 0.0151  I   -11.882     .428    -1.498     .297   .241600   .241900  -.0577600   -12.400    -2.100  
+9112 3 48593.00 I   .240748  .000365   .239028  .000423  I -.0600857  .0000137  2.3864 0.0097  I   -12.099     .406    -1.604     .282   .240500   .239100  -.0602600   -12.300    -2.100  
+9112 4 48594.00 I   .239785  .000367   .236379  .000438  I -.0623717  .0000137  2.1897 0.0097  I   -12.168     .363    -1.767     .290   .239100   .236300  -.0625700   -12.200    -2.100  
+9112 5 48595.00 I   .238404  .000355   .233887  .000431  I -.0644791  .0000138  2.0349 0.0094  I   -11.861     .390    -1.645     .306   .237500   .233600  -.0646900   -12.000    -2.000  
+9112 6 48596.00 I   .236477  .000381   .231434  .000310  I -.0664603  .0000130  1.9366 0.0089  I   -11.349     .418    -1.441     .336   .235800   .230800  -.0666800   -11.800    -1.900  
+9112 7 48597.00 I   .234014  .000394   .228899  .000367  I -.0683727  .0000114  1.8995 0.0075  I   -10.898     .356    -1.435     .366   .233800   .228000  -.0685900   -11.700    -1.800  
+9112 8 48598.00 I   .231202  .000346   .226236  .000379  I -.0702803  .0000073  1.9256 0.0074  I   -10.537     .226    -1.527     .342   .231700   .225200  -.0704900   -11.600    -1.800  
+9112 9 48599.00 I   .228292  .000381   .223448  .000385  I -.0722415  .0000093  2.0047 0.0056  I   -10.209     .207    -1.561     .360   .229500   .222400  -.0724600   -11.500    -1.800  
+911210 48600.00 I   .225542  .000270   .220545  .000313  I -.0743029  .0000084  2.1233 0.0070  I    -9.989     .312    -1.435     .351   .227200   .219600  -.0745200   -11.400    -1.700  
+911211 48601.00 I   .223142  .000267   .217573  .000316  I -.0764947  .0000105  2.2616 0.0074  I   -10.029     .337    -1.154     .305   .224800   .216800  -.0767200   -11.300    -1.700  
+911212 48602.00 I   .221062  .000279   .214672  .000328  I -.0788245  .0000122  2.3951 0.0093  I   -10.288     .337     -.990     .305   .222400   .214100  -.0790600   -11.300    -1.700  
+911213 48603.00 I   .219187  .000274   .211939  .000222  I -.0812771  .0000154  2.5053 0.0111  I   -10.438     .478     -.795     .226   .220000   .211400  -.0815300   -11.300    -1.700  
+911214 48604.00 I   .217366  .000265   .209355  .000271  I -.0838232  .0000185  2.5797 0.0110  I   -10.313     .424     -.721     .207   .217900   .208800  -.0841000   -11.200    -1.700  
+911215 48605.00 I   .215521  .000214   .206838  .000232  I -.0864216  .0000156  2.6092 0.0114  I   -10.178     .446     -.909     .184   .215700   .206200  -.0867100   -11.200    -1.700  
+911216 48606.00 I   .213630  .000186   .204303  .000223  I -.0890221  .0000133  2.5800 0.0104  I   -10.387     .412    -1.177     .135   .213700   .203800  -.0893100   -11.100    -1.700  
+911217 48607.00 I   .211809  .000191   .201740  .000213  I -.0915598  .0000138  2.4854 0.0091  I   -10.792     .416    -1.313     .131   .211700   .201400  -.0918400   -11.100    -1.700  
+911218 48608.00 I   .210191  .000239   .199201  .000240  I -.0939742  .0000125  2.3347 0.0091  I   -11.055     .389    -1.229     .135   .209800   .199100  -.0942200   -11.100    -1.600  
+911219 48609.00 I   .208711  .000232   .196874  .000243  I -.0962138  .0000119  2.1379 0.0085  I   -11.019     .255     -.986     .129   .207900   .196800  -.0964200   -11.100    -1.600  
+911220 48610.00 I   .207235  .000244   .194795  .000250  I -.0982561  .0000114  1.9592 0.0073  I   -10.627     .275     -.790     .121   .206000   .194600  -.0984300   -11.000    -1.600  
+911221 48611.00 I   .205507  .000194   .192814  .000202  I -.1001693  .0000086  1.8916 0.0078  I   -10.018     .428     -.874     .107   .204100   .192400  -.1003400   -11.000    -1.600  
+911222 48612.00 I   .203369  .000249   .190776  .000238  I -.1020840  .0000106  1.9582 0.0102  I    -9.412     .461    -1.084     .115   .202200   .190200  -.1022700   -11.000    -1.600  
+911223 48613.00 I   .200948  .000400   .188654  .000371  I -.1041185  .0000184  2.1232 0.0085  I    -9.100     .473    -1.190     .102   .200300   .187900  -.1043100   -11.000    -1.600  
+911224 48614.00 I   .198454  .000415   .186472  .000375  I -.1063444  .0000132  2.3301 0.0133  I    -9.251     .556    -1.222     .298   .198300   .185700  -.1065500   -10.900    -1.600  
+911225 48615.00 I   .196072  .000297   .184253  .000251  I -.1087701  .0000191  2.5113 0.0172  I    -9.653     .652    -1.291     .298   .196300   .183400  -.1089900   -10.800    -1.600  
+911226 48616.00 I   .193885  .000297   .182012  .000251  I -.1113399  .0000317  2.6112 0.0276  I    -9.981     .652    -1.355     .298   .194400   .181200  -.1115800   -10.600    -1.600  
+911227 48617.00 I   .191929  .000393   .179752  .000327  I -.1139584  .0000518  2.6088 0.0236  I   -10.162     .637    -1.266     .298   .192400   .178900  -.1142000   -10.500    -1.600  
+911228 48618.00 I   .190212  .000615   .177484  .000274  I -.1165251  .0000351  2.5092 0.0322  I   -10.284     .473    -1.032     .174   .190400   .176600  -.1167500   -10.300    -1.700  
+911229 48619.00 I   .188613  .000711   .175240  .000404  I -.1189540  .0000383  2.3412 0.0258  I   -10.401     .473     -.823     .174   .188400   .174400  -.1191500   -10.200    -1.700  
+911230 48620.00 I   .186972  .000711   .173043  .000404  I -.1211998  .0000378  2.1500 0.0258  I   -10.471     .473     -.767     .174   .186500   .172200  -.1213700   -10.100    -1.700  
+911231 48621.00 I   .185126  .000714   .170911  .000381  I -.1232581  .0000346  1.9712 0.0215  I   -10.473     .203     -.831     .231   .184500   .170000  -.1234200   -10.000    -1.700  
+92 1 1 48622.00 I   .182965  .000672   .168856  .000345  I -.1251559  .0000207  1.8335 0.0201  I   -10.437     .507     -.917     .165   .182400   .167900  -.1253000    -9.900    -1.700  
+92 1 2 48623.00 I   .180592  .000672   .166857  .000345  I -.1269451  .0000203  1.7556 0.0144  I   -10.354     .507     -.996     .165   .180300   .165900  -.1270800    -9.900    -1.700  
+92 1 3 48624.00 I   .178161  .000672   .164885  .000345  I -.1286896  .0000200  1.7450 0.0107  I   -10.201     .688    -1.068     .298   .178200   .163900  -.1288200    -9.900    -1.700  
+92 1 4 48625.00 I   .175799  .000264   .162915  .000341  I -.1304568  .0000070  1.7995 0.0105  I   -10.003     .529    -1.066     .298   .175700   .161600  -.1305800   -10.100    -1.500  
+92 1 5 48626.00 I   .173506  .000192   .160942  .000246  I -.1323066  .0000063  1.9081 0.0045  I    -9.781     .529     -.909     .298   .173500   .159600  -.1324300   -10.100    -1.400  
+92 1 6 48627.00 I   .171249  .000200   .158962  .000246  I -.1342857  .0000055  2.0548 0.0044  I    -9.535     .475     -.646     .298   .171100   .157800  -.1344200   -10.000    -1.400  
+92 1 7 48628.00 I   .168993  .000169   .156979  .000254  I -.1364224  .0000061  2.2201 0.0041  I    -9.315     .241     -.461     .298   .168700   .156000  -.1365600    -9.900    -1.400  
+92 1 8 48629.00 I   .166663  .000169   .155043  .000254  I -.1387244  .0000061  2.3807 0.0044  I    -9.222     .197     -.468     .107   .166200   .154200  -.1388600    -9.900    -1.400  
+92 1 9 48630.00 I   .164123  .000172   .153294  .000254  I -.1411761  .0000063  2.5180 0.0059  I    -9.265     .197     -.513     .107   .163600   .152600  -.1413200    -9.800    -1.400  
+92 110 48631.00 I   .161402  .000185   .151714  .000220  I -.1437490  .0000102  2.6210 0.0053  I    -9.283     .791     -.499     .134   .160900   .151000  -.1439000    -9.800    -1.400  
+92 111 48632.00 I   .158585  .000149   .150245  .000208  I -.1464011  .0000085  2.6729 0.0085  I    -9.144     .166     -.527     .121   .158100   .149500  -.1465400    -9.800    -1.400  
+92 112 48633.00 I   .155692  .000357   .148849  .000334  I -.1490736  .0000135  2.6613 0.0082  I    -8.960     .184     -.691     .123   .155300   .148100  -.1491800    -9.800    -1.400  
+92 113 48634.00 I   .152741  .000365   .147494  .000339  I -.1517024  .0000141  2.5859 0.0101  I    -8.956     .304     -.927     .115   .152300   .146700  -.1518000    -9.800    -1.400  
+92 114 48635.00 I   .149763  .000359   .146138  .000332  I -.1542271  .0000151  2.4555 0.0101  I    -9.257     .367     -.997     .298   .149300   .145400  -.1543000    -9.900    -1.300  
+92 115 48636.00 I   .146774  .000366   .144772  .000263  I -.1566078  .0000145  2.3083 0.0098  I    -9.832     .369     -.773     .133   .146200   .144200  -.1566900   -10.000    -1.300  
+92 116 48637.00 I   .143765  .000366   .143469  .000263  I -.1588545  .0000126  2.1937 0.0103  I   -10.496     .343     -.556     .125   .143100   .142900  -.1589500   -10.200    -1.300  
+92 117 48638.00 I   .140654  .000403   .142244  .000248  I -.1610204  .0000147  2.1548 0.0086  I   -10.968     .378     -.626     .134   .139900   .141700  -.1611400   -10.400    -1.300  
+92 118 48639.00 I   .137345  .000298   .141095  .000326  I -.1632032  .0000117  2.2332 0.0085  I   -10.999     .421     -.965     .123   .136700   .140500  -.1633600   -10.500    -1.300  
+92 119 48640.00 I   .133813  .000193   .139997  .000287  I -.1655276  .0000086  2.4339 0.0069  I   -10.616     .479    -1.297     .125   .133500   .139300  -.1657200   -10.500    -1.400  
+92 120 48641.00 I   .130220  .000191   .138870  .000286  I -.1680973  .0000075  2.7136 0.0057  I   -10.135     .465    -1.374     .135   .130300   .138000  -.1683300   -10.500    -1.400  
+92 121 48642.00 I   .126773  .000203   .137616  .000282  I -.1709548  .0000076  2.9935 0.0056  I    -9.818     .493    -1.239     .113   .127200   .136700  -.1712100   -10.500    -1.500  
+92 122 48643.00 I   .123633  .000164   .136222  .000333  I -.1740492  .0000084  3.1682 0.0057  I    -9.600     .460    -1.120     .122   .124100   .135400  -.1742900   -10.400    -1.600  
+92 123 48644.00 I   .120838  .000164   .134840  .000333  I -.1772390  .0000084  3.1864 0.0060  I    -9.442     .460    -1.125     .122   .121000   .134000  -.1774700   -10.300    -1.700  
+92 124 48645.00 I   .118239  .000176   .133514  .000294  I -.1803757  .0000086  3.0656 0.0070  I    -9.437     .437    -1.097     .132   .118000   .132700  -.1805900   -10.200    -1.800  
+92 125 48646.00 I   .115660  .000171   .132246  .000378  I -.1833389  .0000112  2.8511 0.0086  I    -9.644     .344     -.960     .191   .115100   .131300  -.1835400   -10.100    -1.800  
+92 126 48647.00 I   .113031  .000172   .131007  .000394  I -.1860675  .0000148  2.6056 0.0093  I    -9.925     .371     -.876     .326   .112300   .129900  -.1862800    -9.900    -1.900  
+92 127 48648.00 I   .110308  .000172   .129756  .000394  I -.1885557  .0000148  2.3768 0.0102  I   -10.025     .418     -.983     .303   .109500   .128600  -.1887700    -9.800    -2.000  
+92 128 48649.00 I   .107447  .000190   .128450  .000390  I -.1908372  .0000141  2.1964 0.0103  I    -9.890     .477    -1.190     .347   .106800   .127300  -.1910600    -9.700    -2.000  
+92 129 48650.00 I   .104483  .000190   .127120  .000390  I -.1929756  .0000143  2.0974 0.0100  I    -9.674     .466    -1.436     .301   .104200   .126100  -.1932200    -9.600    -2.100  
+92 130 48651.00 I   .101609  .000199   .125938  .000387  I -.1950580  .0000142  2.0764 0.0119  I    -9.421     .466    -1.643     .301   .101600   .125000  -.1953000    -9.500    -2.100  
+92 131 48652.00 I   .098935  .000227   .124947  .000371  I -.1971464  .0000189  2.1095 0.0115  I    -9.104     .487    -1.771     .316   .099100   .123900  -.1973700    -9.500    -2.200  
+92 2 1 48653.00 I   .096518  .000235   .124143  .000387  I -.1992921  .0000181  2.1877 0.0104  I    -8.786     .515    -1.815     .172   .096700   .123000  -.1994900    -9.500    -2.200  
+92 2 2 48654.00 I   .094302  .000203   .123506  .000278  I -.2015317  .0000089  2.2954 0.0102  I    -8.534     .503    -1.695     .158   .094300   .122200  -.2017000    -9.700    -2.300  
+92 2 3 48655.00 I   .092183  .000204   .123008  .000277  I -.2038871  .0000096  2.4158 0.0069  I    -8.416     .465    -1.427     .144   .091700   .121400  -.2040200    -9.600    -2.200  
+92 2 4 48656.00 I   .090027  .000214   .122615  .000274  I -.2063606  .0000106  2.5280 0.0075  I    -8.473     .484    -1.213     .168   .089300   .120700  -.2065100    -9.400    -2.200  
+92 2 5 48657.00 I   .087698  .000216   .122252  .000272  I -.2089429  .0000115  2.6394 0.0080  I    -8.697     .400    -1.266     .128   .086900   .120100  -.2091300    -9.200    -2.300  
+92 2 6 48658.00 I   .085120  .000216   .121749  .000272  I -.2116347  .0000119  2.7366 0.0082  I    -9.004     .380    -1.552     .119   .084500   .119600  -.2118600    -9.000    -2.300  
+92 2 7 48659.00 I   .082425  .000212   .121095  .000251  I -.2143996  .0000118  2.7841 0.0075  I    -9.303     .236    -1.864     .298   .082000   .119100  -.2146100    -9.100    -2.300  
+92 2 8 48660.00 I   .079747  .000145   .120336  .000202  I -.2171790  .0000092  2.7599 0.0077  I    -9.505     .258    -2.042     .298   .079400   .118600  -.2173600    -9.200    -2.400  
+92 2 9 48661.00 I   .077048  .000177   .119538  .000228  I -.2198943  .0000100  2.6606 0.0071  I    -9.550     .260    -2.086     .298   .076700   .118200  -.2200300    -9.400    -2.400  
+92 210 48662.00 I   .074254  .000177   .118767  .000228  I -.2224826  .0000108  2.5086 0.0072  I    -9.572     .306    -2.036     .298   .073900   .117700  -.2225800    -9.500    -2.400  
+92 211 48663.00 I   .071296  .000113   .118085  .000239  I -.2249019  .0000103  2.3279 0.0067  I    -9.777     .335    -1.844     .120   .071100   .117300  -.2249800    -9.700    -2.400  
+92 212 48664.00 I   .068126  .000124   .117511  .000189  I -.2271426  .0000080  2.1597 0.0065  I   -10.210     .329    -1.526     .116   .068100   .116900  -.2272000    -9.800    -2.400  
+92 213 48665.00 I   .064769  .000124   .117034  .000189  I -.2292410  .0000080  2.0505 0.0064  I   -10.677     .303    -1.413     .109   .065100   .116600  -.2293000   -10.000    -2.300  
+92 214 48666.00 I   .061346  .000159   .116670  .000140  I -.2312755  .0000100  2.0371 0.0062  I   -11.010     .307    -1.693     .114   .062000   .116300  -.2313500   -10.200    -2.300  
+92 215 48667.00 I   .057994  .000218   .116441  .000174  I -.2333549  .0000095  2.1427 0.0091  I   -11.097     .271    -2.234     .298   .058900   .116100  -.2334600   -10.300    -2.400  
+92 216 48668.00 I   .054803  .000246   .116362  .000155  I -.2355963  .0000153  2.3546 0.0093  I   -10.912     .178    -2.651     .298   .055800   .115900  -.2357400   -10.500    -2.400  
+92 217 48669.00 I   .051751  .000246   .116424  .000155  I -.2380810  .0000159  2.6173 0.0102  I   -10.611     .161    -2.670     .298   .052600   .115700  -.2382600   -10.600    -2.500  
+92 218 48670.00 I   .048799  .000290   .116604  .000149  I -.2408220  .0000134  2.8532 0.0099  I   -10.353     .137    -2.396     .298   .049500   .115700  -.2410200   -10.700    -2.500  
+92 219 48671.00 I   .045888  .000321   .116854  .000342  I -.2437544  .0000119  2.9900 0.0089  I   -10.123     .173    -2.147     .298   .046300   .115600  -.2439800   -10.700    -2.600  
+92 220 48672.00 I   .042907  .000325   .117120  .000331  I -.2467553  .0000117  2.9867 0.0085  I    -9.889     .190    -2.105     .298   .043000   .115500  -.2469800   -10.800    -2.600  
+92 221 48673.00 I   .039772  .000332   .117341  .000296  I -.2496841  .0000122  2.8523 0.0094  I    -9.710     .195    -2.142     .110   .039800   .115500  -.2499100   -10.800    -2.700  
+92 222 48674.00 I   .036468  .000333   .117447  .000203  I -.2524364  .0000146  2.6473 0.0101  I    -9.651     .260    -2.107     .151   .036500   .115400  -.2526400   -10.800    -2.700  
+92 223 48675.00 I   .033121  .000299   .117372  .000194  I -.2549754  .0000160  2.4325 0.0108  I    -9.670     .283    -2.212     .162   .033200   .115300  -.2551400   -10.800    -2.800  
+92 224 48676.00 I   .029796  .000295   .117134  .000205  I -.2573113  .0000159  2.2473 0.0125  I    -9.614     .423    -2.581     .175   .029900   .115300  -.2574300   -10.700    -2.800  
+92 225 48677.00 I   .026512  .000262   .116772  .000198  I -.2594898  .0000192  2.1213 0.0135  I    -9.548     .486    -3.032     .198   .026600   .115200  -.2595300   -10.700    -2.900  
+92 226 48678.00 I   .023303  .000264   .116361  .000149  I -.2615661  .0000219  2.0317 0.0149  I    -9.711     .442    -3.300     .183   .023200   .115100  -.2615300   -10.600    -3.000  
+92 227 48679.00 I   .020120  .000267   .116041  .000189  I -.2635649  .0000228  1.9770 0.0186  I   -10.007     .442    -3.272     .183   .019800   .115000  -.2634800   -10.500    -3.000  
+92 228 48680.00 I   .016870  .000283   .115871  .000204  I -.2655401  .0000301  1.9822 0.0179  I   -10.167     .482    -3.161     .169   .016400   .115000  -.2654600   -10.300    -3.100  
+92 229 48681.00 I   .013480  .000161   .115880  .000229  I -.2675507  .0000275  2.0516 0.0165  I   -10.006     .538    -3.175     .178   .013000   .115100  -.2675100   -10.100    -3.200  
+92 3 1 48682.00 I   .009978  .000153   .116087  .000159  I -.2696648  .0000136  2.1854 0.0145  I    -9.601     .468    -3.219     .165   .009500   .115200  -.2697000    -9.900    -3.200  
+92 3 2 48683.00 I   .006393  .000153   .116477  .000159  I -.2719343  .0000093  2.3575 0.0080  I    -9.196     .422    -3.154     .150   .006000   .115400  -.2720500    -9.700    -3.300  
+92 3 3 48684.00 I   .002747  .000135   .117026  .000169  I -.2743831  .0000084  2.5395 0.0075  I    -8.927     .523    -3.034     .163   .002500   .115700  -.2745600    -9.500    -3.400  
+92 3 4 48685.00 I  -.000960  .000172   .117699  .000122  I -.2770061  .0000118  2.6999 0.0072  I    -8.780     .444    -3.013     .136  -.000900   .116200  -.2772200    -8.800    -3.500  
+92 3 5 48686.00 I  -.004773  .000172   .118416  .000122  I -.2797652  .0000116  2.8073 0.0084  I    -8.712     .444    -3.165     .131  -.004400   .116700  -.2799900    -8.700    -3.600  
+92 3 6 48687.00 I  -.008589  .000172   .119105  .000122  I -.2825980  .0000120  2.8466 0.0095  I    -8.712     .304    -3.375     .298  -.008000   .117300  -.2828200    -8.600    -3.700  
+92 3 7 48688.00 I  -.012262  .000181   .119710  .000131  I -.2854369  .0000150  2.8212 0.0122  I    -8.720     .304    -3.487     .140  -.011500   .118000  -.2856300    -8.500    -3.800  
+92 3 8 48689.00 I  -.015689  .000206   .120230  .000146  I -.2882203  .0000212  2.7353 0.0131  I    -8.570     .388    -3.404     .226  -.015000   .118800  -.2883600    -8.500    -3.900  
+92 3 9 48690.00 I  -.018919  .000209   .120750  .000143  I -.2908904  .0000214  2.5979 0.0153  I    -8.316     .369    -3.238     .239  -.018400   .119700  -.2910000    -8.500    -4.000  
+92 310 48691.00 I  -.022035  .000261   .121373  .000134  I -.2934116  .0000220  2.4468 0.0153  I    -8.202     .414    -3.195     .256  -.021900   .120700  -.2935400    -8.600    -4.100  
+92 311 48692.00 I  -.025117  .000254   .122197  .000140  I -.2958109  .0000220  2.3764 0.0170  I    -8.423     .481    -3.444     .244  -.025300   .121800  -.2959800    -8.700    -4.200  
+92 312 48693.00 I  -.028234  .000254   .123272  .000140  I -.2982033  .0000258  2.4228 0.0165  I    -8.890     .463    -3.756     .232  -.028700   .123000  -.2984000    -8.700    -4.300  
+92 313 48694.00 I  -.031378  .000263   .124589  .000118  I -.3006878  .0000247  2.5630 0.0170  I    -9.379     .501    -4.126     .238  -.032100   .124300  -.3008900    -8.800    -4.400  
+92 314 48695.00 I  -.034530  .000228   .126117  .000114  I -.3033568  .0000221  2.7858 0.0179  I    -9.542     .533    -4.570     .158  -.035400   .125700  -.3035600    -8.900    -4.500  
+92 315 48696.00 I  -.037731  .000252   .127806  .000135  I -.3062763  .0000260  3.0595 0.0164  I    -9.272     .589    -4.835     .100  -.038700   .127300  -.3064700    -9.000    -4.600  
+92 316 48697.00 I  -.040970  .000268   .129629  .000200  I -.3094750  .0000242  3.3309 0.0169  I    -8.884     .582    -4.740     .101  -.042000   .128900  -.3096600    -9.000    -4.600  
+92 317 48698.00 I  -.044230  .000193   .131570  .000220  I -.3129125  .0000217  3.5254 0.0134  I    -8.774     .546    -4.405     .298  -.045300   .130600  -.3130800    -9.000    -4.700  
+92 318 48699.00 I  -.047505  .000155   .133592  .000206  I -.3164823  .0000113  3.5892 0.0122  I    -9.030     .590    -4.136     .298  -.048500   .132400  -.3166600    -9.000    -4.800  
+92 319 48700.00 I  -.050798  .000150   .135598  .000207  I -.3200428  .0000112  3.5085 0.0084  I    -9.299     .554    -4.072     .298  -.051600   .134300  -.3202100    -8.900    -4.800  
+92 320 48701.00 I  -.054100  .000189   .137506  .000215  I -.3234626  .0000125  3.3178 0.0078  I    -9.392     .490    -4.157     .298  -.054600   .136300  -.3236400    -8.900    -4.900  
+92 321 48702.00 I  -.057350  .000189   .139342  .000226  I -.3266588  .0000110  3.0685 0.0088  I    -9.326     .404    -4.250     .115  -.057600   .138300  -.3268300    -8.800    -5.000  
+92 322 48703.00 I  -.060446  .000211   .141156  .000229  I -.3296001  .0000123  2.8209 0.0077  I    -9.135     .388    -4.393     .123  -.060400   .140300  -.3297700    -8.600    -5.000  
+92 323 48704.00 I  -.063319  .000174   .143002  .000157  I -.3323193  .0000109  2.6295 0.0086  I    -8.781     .388    -4.595     .123  -.063100   .142400  -.3324800    -8.500    -5.100  
+92 324 48705.00 I  -.065917  .000265   .144942  .000164  I -.3348843  .0000119  2.5136 0.0096  I    -8.367     .198    -4.743     .141  -.065700   .144600  -.3350400    -8.400    -5.100  
+92 325 48706.00 I  -.068216  .000310   .147021  .000196  I -.3373685  .0000158  2.4638 0.0104  I    -8.153     .301    -4.766     .126  -.068200   .146800  -.3375000    -8.400    -5.100  
+92 326 48707.00 I  -.070314  .000310   .149236  .000196  I -.3398287  .0000171  2.4643 0.0146  I    -8.233     .301    -4.741     .126  -.070600   .149000  -.3399300    -8.300    -5.100  
+92 327 48708.00 I  -.072339  .000373   .151570  .000240  I -.3423103  .0000245  2.5044 0.0136  I    -8.380     .412    -4.791     .108  -.072800   .151200  -.3424000    -8.300    -5.100  
+92 328 48709.00 I  -.074391  .000401   .153996  .000305  I -.3448509  .0000212  2.5847 0.0169  I    -8.381     .345    -4.931     .298  -.074900   .153400  -.3449700    -8.400    -5.100  
+92 329 48710.00 I  -.076467  .000408   .156449  .000302  I -.3474917  .0000233  2.7012 0.0157  I    -8.272     .345    -5.027     .298  -.077000   .155600  -.3476400    -8.400    -5.100  
+92 330 48711.00 I  -.078543  .000408   .158854  .000302  I -.3502571  .0000232  2.8293 0.0157  I    -8.252     .469    -4.980     .298  -.079000   .157800  -.3504400    -8.500    -5.200  
+92 331 48712.00 I  -.080594  .000266   .161135  .000331  I -.3531461  .0000209  2.9448 0.0148  I    -8.359     .505    -4.865     .298  -.080900   .159900  -.3533400    -8.600    -5.200  
+92 4 1 48713.00 I  -.082596  .000266   .163229  .000331  I -.3561338  .0000184  3.0214 0.0137  I    -8.384     .656    -4.838     .148  -.082800   .161900  -.3563400    -8.700    -5.300  
+92 4 2 48714.00 I  -.084556  .000322   .165149  .000316  I -.3591684  .0000177  3.0364 0.0179  I    -8.108     .656    -4.966     .148  -.084600   .163900  -.3593600    -8.800    -5.400  
+92 4 3 48715.00 I  -.086492  .000417   .166920  .000264  I -.3621836  .0000307  2.9824 0.0154  I    -7.774     .822    -5.110     .187  -.086500   .165900  -.3623500    -8.600    -5.600  
+92 4 4 48716.00 I  -.088401  .000265   .168583  .000385  I -.3651128  .0000253  2.8673 0.0192  I    -7.780     .813    -5.096     .234  -.088200   .167700  -.3652700    -8.500    -5.700  
+92 4 5 48717.00 I  -.090251  .000264   .170205  .000376  I -.3679050  .0000230  2.7127 0.0172  I    -8.052     .816    -4.950     .256  -.090000   .169600  -.3680400    -8.300    -5.800  
+92 4 6 48718.00 I  -.092014  .000347   .171858  .000359  I -.3705355  .0000233  2.5499 0.0171  I    -8.319     .713    -4.813     .326  -.091800   .171500  -.3706600    -8.200    -5.900  
+92 4 7 48719.00 I  -.093706  .000336   .173611  .000324  I -.3730152  .0000252  2.4183 0.0179  I    -8.369     .644    -4.828     .371  -.093500   .173400  -.3731300    -8.200    -6.000  
+92 4 8 48720.00 I  -.095351  .000286   .175516  .000343  I -.3753975  .0000272  2.3630 0.0184  I    -8.188     .603    -5.034     .316  -.095300   .175300  -.3755200    -8.200    -6.000  
+92 4 9 48721.00 I  -.096976  .000279   .177569  .000327  I -.3777761  .0000268  2.4123 0.0203  I    -7.961     .603    -5.431     .316  -.097000   .177300  -.3779200    -8.300    -6.100  
+92 410 48722.00 I  -.098597  .000279   .179745  .000327  I -.3802556  .0000301  2.5621 0.0176  I    -7.720     .470    -5.849     .330  -.098800   .179300  -.3804300    -8.400    -6.100  
+92 411 48723.00 I  -.100207  .000260   .182020  .000220  I -.3829267  .0000228  2.7913 0.0212  I    -7.475     .433    -6.328     .305  -.100600   .181500  -.3831500    -8.500    -6.100  
+92 412 48724.00 I  -.101809  .000203   .184383  .000152  I -.3858506  .0000299  3.0578 0.0182  I    -7.264     .583    -6.729     .394  -.102300   .183800  -.3861300    -8.600    -6.000  
+92 413 48725.00 I  -.103423  .000230   .186819  .000191  I -.3890352  .0000284  3.3030 0.0207  I    -7.173     .559    -6.812     .368  -.104100   .186100  -.3893700    -8.600    -6.000  
+92 414 48726.00 I  -.105076  .000239   .189311  .000237  I -.3924319  .0000285  3.4741 0.0208  I    -7.289     .467    -6.448     .331  -.105900   .188500  -.3927900    -8.700    -5.900  
+92 415 48727.00 I  -.106793  .000240   .191842  .000240  I -.3959418  .0000303  3.5204 0.0211  I    -7.598     .467    -5.794     .331  -.107700   .190800  -.3962800    -8.700    -5.800  
+92 416 48728.00 I  -.108599  .000240   .194378  .000240  I -.3994216  .0000311  3.4134 0.0219  I    -7.914     .467    -5.348     .331  -.109500   .193200  -.3997000    -8.700    -5.800  
+92 417 48729.00 I  -.110484  .000241   .196883  .000233  I -.4027295  .0000316  3.1891 0.0185  I    -8.160     .495    -5.304     .349  -.111300   .195700  -.4029400    -8.700    -5.700  
+92 418 48730.00 I  -.112402  .000213   .199323  .000253  I -.4057833  .0000199  2.9153 0.0178  I    -8.288     .530    -5.535     .139  -.113100   .198100  -.4059300    -8.600    -5.700  
+92 419 48731.00 I  -.114309  .000196   .201692  .000292  I -.4085627  .0000166  2.6495 0.0128  I    -8.269     .578    -5.795     .156  -.114800   .200500  -.4086600    -8.500    -5.800  
+92 420 48732.00 I  -.116160  .000120   .203983  .000278  I -.4110990  .0000161  2.4340 0.0106  I    -8.171     .681    -5.904     .190  -.116500   .202800  -.4111700    -8.400    -5.800  
+92 421 48733.00 I  -.117913  .000163   .206196  .000264  I -.4134536  .0000133  2.2873 0.0106  I    -8.150     .681    -5.835     .190  -.118100   .205200  -.4135300    -8.300    -5.900  
+92 422 48734.00 I  -.119538  .000163   .208379  .000264  I -.4157038  .0000137  2.2313 0.0098  I    -8.251     .659    -5.760     .154  -.119700   .207600  -.4158200    -8.200    -6.000  
+92 423 48735.00 I  -.121032  .000164   .210673  .000278  I -.4179446  .0000143  2.2606 0.0107  I    -8.374     .659    -5.847     .154  -.121300   .209900  -.4181000    -8.100    -6.100  
+92 424 48736.00 I  -.122480  .000174   .213058  .000288  I -.4202420  .0000165  2.3410 0.0105  I    -8.323     .564    -6.084     .112  -.122800   .212300  -.4204300    -7.900    -6.300  
+92 425 48737.00 I  -.123975  .000206   .215479  .000249  I -.4226378  .0000154  2.4551 0.0118  I    -7.965     .505    -6.341     .298  -.124300   .214700  -.4228500    -7.800    -6.400  
+92 426 48738.00 I  -.125501  .000273   .217910  .000244  I -.4251565  .0000170  2.5820 0.0109  I    -7.453     .559    -6.459     .298  -.125900   .217100  -.4253900    -7.700    -6.500  
+92 427 48739.00 I  -.127017  .000275   .220342  .000249  I -.4277973  .0000153  2.6954 0.0106  I    -7.106     .681    -6.406     .102  -.127400   .219400  -.4280300    -7.600    -6.600  
+92 428 48740.00 I  -.128494  .000278   .222791  .000292  I -.4305358  .0000126  2.7743 0.0095  I    -7.126     .714    -6.319     .126  -.129000   .221800  -.4307400    -7.500    -6.700  
+92 429 48741.00 I  -.129927  .000284   .225263  .000283  I -.4333245  .0000112  2.7891 0.0084  I    -7.404     .635    -6.331     .127  -.130500   .224100  -.4335000    -7.500    -6.700  
+92 430 48742.00 I  -.131420  .000284   .227741  .000283  I -.4360890  .0000112  2.7291 0.0086  I    -7.558     .635    -6.360     .127  -.132100   .226500  -.4362300    -7.500    -6.800  
+92 5 1 48743.00 I  -.133096  .000324   .230149  .000304  I -.4387615  .0000131  2.6056 0.0083  I    -7.533     .695    -6.293     .155  -.133800   .228800  -.4388700    -7.500    -6.800  
+92 5 2 48744.00 I  -.135028  .000302   .232420  .000382  I -.4412846  .0000122  2.4352 0.0090  I    -7.501     .639    -6.120     .151  -.135400   .231100  -.4413800    -7.500    -6.900  
+92 5 3 48745.00 I  -.137123  .000289   .234572  .000262  I -.4436273  .0000122  2.2509 0.0080  I    -7.542     .408    -5.969     .131  -.137200   .233300  -.4437200    -7.700    -7.000  
+92 5 4 48746.00 I  -.139228  .000289   .236665  .000262  I -.4457947  .0000103  2.0915 0.0085  I    -7.620     .392    -5.988     .130  -.138800   .235700  -.4458900    -7.800    -7.000  
+92 5 5 48747.00 I  -.141185  .000285   .238758  .000302  I -.4478320  .0000119  1.9970 0.0078  I    -7.679     .353    -6.229     .130  -.140500   .238100  -.4479500    -7.800    -7.100  
+92 5 6 48748.00 I  -.142849  .000285   .240919  .000302  I -.4498196  .0000118  1.9950 0.0122  I    -7.682     .349    -6.579     .109  -.142000   .240500  -.4499600    -7.900    -7.100  
+92 5 7 48749.00 I  -.144143  .000285   .243238  .000302  I -.4518569  .0000214  2.0977 0.0142  I    -7.567     .349    -6.751     .109  -.143500   .243000  -.4520300    -7.900    -7.000  
+92 5 8 48750.00 I  -.145138  .000283   .245766  .000257  I -.4540441  .0000258  2.2879 0.0179  I    -7.420     .355    -6.728     .104  -.144900   .245500  -.4542500    -7.900    -7.000  
+92 5 9 48751.00 I  -.145941  .000387   .248520  .000284  I -.4564424  .0000287  2.5074 0.0176  I    -7.278     .616    -6.665     .298  -.146200   .248000  -.4566700    -7.900    -6.900  
+92 510 48752.00 I  -.146679  .000318   .251420  .000297  I -.4590478  .0000240  2.6935 0.0188  I    -7.084     .717    -6.613     .298  -.147400   .250600  -.4592700    -7.900    -6.900  
+92 511 48753.00 I  -.147478  .000283   .254362  .000232  I -.4618024  .0000244  2.7985 0.0173  I    -6.797     .681    -6.520     .298  -.148400   .253100  -.4620200    -7.900    -6.800  
+92 512 48754.00 I  -.148460  .000345   .257247  .000198  I -.4646084  .0000249  2.7943 0.0162  I    -6.485     .778    -6.319     .298  -.149400   .255700  -.4648100    -7.800    -6.700  
+92 513 48755.00 I  -.149655  .000371   .259981  .000224  I -.4673633  .0000212  2.7070 0.0156  I    -6.393     .658    -6.038     .112  -.150200   .258200  -.4675800    -7.700    -6.700  
+92 514 48756.00 I  -.150888  .000384   .262487  .000233  I -.4700007  .0000189  2.5547 0.0126  I    -6.689     .628    -5.780     .106  -.151000   .260700  -.4702000    -7.700    -6.700  
+92 515 48757.00 I  -.151944  .000332   .264796  .000219  I -.4724541  .0000135  2.3465 0.0112  I    -7.140     .482    -5.779     .135  -.151600   .263100  -.4726300    -7.600    -6.600  
+92 516 48758.00 I  -.152674  .000286   .266971  .000205  I -.4746929  .0000122  2.1360 0.0105  I    -7.495     .377    -6.172     .125  -.152200   .265600  -.4748600    -7.600    -6.600  
+92 517 48759.00 I  -.153100  .000363   .269090  .000249  I -.4767418  .0000162  1.9727 0.0109  I    -7.641     .284    -6.650     .138  -.152800   .268000  -.4769000    -7.700    -6.600  
+92 518 48760.00 I  -.153324  .000371   .271238  .000247  I -.4786626  .0000180  1.8823 0.0124  I    -7.555     .284    -6.759     .138  -.153300   .270400  -.4788200    -7.700    -6.600  
+92 519 48761.00 I  -.153492  .000278   .273491  .000426  I -.4805333  .0000189  1.8725 0.0129  I    -7.372     .664    -6.486     .109  -.153700   .272800  -.4806900    -7.900    -6.700  
+92 520 48762.00 I  -.153702  .000230   .275896  .000335  I -.4824287  .0000186  1.9261 0.0133  I    -7.283     .699    -6.167     .159  -.154200   .275200  -.4825800    -8.100    -6.700  
+92 521 48763.00 I  -.153937  .000211   .278419  .000325  I -.4843968  .0000186  2.0138 0.0142  I    -7.418     .683    -6.020     .137  -.154600   .277600  -.4845400    -8.400    -6.700  
+92 522 48764.00 I  -.154207  .000172   .281007  .000357  I -.4864569  .0000214  2.1033 0.0135  I    -7.812     .798    -6.119     .157  -.155000   .280000  -.4865900    -8.700    -6.700  
+92 523 48765.00 I  -.154584  .000204   .283623  .000449  I -.4886033  .0000196  2.1926 0.0144  I    -8.062     .723    -6.315     .144  -.155400   .282400  -.4887500    -8.900    -6.700  
+92 524 48766.00 I  -.155104  .000204   .286252  .000449  I -.4908421  .0000194  2.2821 0.0119  I    -8.052     .723    -6.411     .144  -.155900   .284700  -.4910000    -9.100    -6.700  
+92 525 48767.00 I  -.155760  .000183   .288822  .000442  I -.4931589  .0000135  2.3460 0.0124  I    -7.984     .568    -6.373     .154  -.156300   .287100  -.4933200    -9.400    -6.700  
+92 526 48768.00 I  -.156535  .000364   .291241  .000355  I -.4955207  .0000156  2.3696 0.0110  I    -8.160     .448    -6.321     .298  -.156600   .289500  -.4956700    -9.500    -6.700  
+92 527 48769.00 I  -.157366  .000389   .293461  .000460  I -.4978772  .0000174  2.3304 0.0107  I    -8.719     .388    -6.393     .101  -.157000   .291800  -.4980000    -9.700    -6.700  
+92 528 48770.00 I  -.158048  .000384   .295568  .000530  I -.5001556  .0000146  2.2136 0.0117  I    -9.404     .333    -6.533     .141  -.157300   .294200  -.5002600    -9.800    -6.700  
+92 529 48771.00 I  -.158364  .000387   .297671  .000451  I -.5022890  .0000155  2.0504 0.0102  I    -9.853     .308    -6.541     .145  -.157500   .296500  -.5023900    -9.900    -6.700  
+92 530 48772.00 I  -.158224  .000403   .299872  .000431  I -.5042519  .0000143  1.8735 0.0108  I   -10.001     .318    -6.260     .167  -.157600   .298900  -.5043500    -9.900    -6.600  
+92 531 48773.00 I  -.157794  .000380   .302214  .000367  I -.5060354  .0000151  1.6949 0.0108  I   -10.019     .302    -5.954     .168  -.157700   .301200  -.5061400    -9.900    -6.600  
+92 6 1 48774.00 I  -.157251  .000377   .304723  .000406  I -.5076563  .0000163  1.5601 0.0111  I   -10.041     .293    -5.989     .163  -.157600   .303500  -.5077700   -10.300    -6.900  
+92 6 2 48775.00 I  -.156693  .000352   .307325  .000450  I -.5091858  .0000164  1.5159 0.0117  I    -9.928     .316    -6.266     .175  -.157400   .306000  -.5093000    -9.600    -6.800  
+92 6 3 48776.00 I  -.156213  .000370   .309905  .000462  I -.5107243  .0000167  1.5803 0.0126  I    -9.536     .383    -6.524     .152  -.157200   .308800  -.5108600    -9.200    -6.700  
+92 6 4 48777.00 I  -.155865  .000419   .312371  .000384  I -.5123786  .0000192  1.7411 0.0138  I    -9.196     .354    -6.637     .158  -.156900   .311500  -.5125300    -8.900    -6.500  
+92 6 5 48778.00 I  -.155629  .000398   .314694  .000394  I -.5142194  .0000220  1.9404 0.0153  I    -9.147     .367    -6.518     .106  -.156600   .314100  -.5143800    -8.700    -6.400  
+92 6 6 48779.00 I  -.155420  .000354   .316894  .000381  I -.5162500  .0000238  2.1114 0.0246  I    -9.249     .358    -6.320     .298  -.156300   .316300  -.5164300    -8.700    -6.200  
+92 6 7 48780.00 I  -.155182  .000412   .319021  .000533  I -.5184166  .0000440  2.2054 0.0263  I    -9.325     .363    -6.156     .107  -.156000   .318300  -.5186000    -8.900    -6.000  
+92 6 8 48781.00 I  -.154897  .000421   .321135  .000431  I -.5206263  .0000470  2.1957 0.0319  I    -9.268     .363    -6.017     .107  -.155500   .320200  -.5208200    -9.300    -6.000  
+92 6 9 48782.00 I  -.154597  .000386   .323300  .000289  I -.5227758  .0000463  2.0892 0.0320  I    -9.130     .234    -5.843     .125  -.154800   .322200  -.5229400    -9.800    -6.000  
+92 610 48783.00 I  -.154299  .000257   .325574  .000366  I -.5247841  .0000435  1.9209 0.0285  I    -9.122     .234    -5.605     .125  -.154200   .324500  -.5249100   -10.200    -6.100  
+92 611 48784.00 I  -.153976  .000226   .327985  .000404  I -.5266096  .0000331  1.7287 0.0292  I    -9.404     .225    -5.387     .113  -.153700   .327100  -.5266900   -10.700    -6.300  
+92 612 48785.00 I  -.153631  .000235   .330557  .000464  I -.5282452  .0000389  1.5475 0.0178  I    -9.962     .473    -5.328     .289  -.153500   .330000  -.5282800   -11.000    -6.600  
+92 613 48786.00 I  -.153301  .000288   .333315  .000424  I -.5297193  .0000130  1.4102 0.0206  I   -10.577     .701    -5.546     .238  -.153400   .333000  -.5297300   -11.100    -6.900  
+92 614 48787.00 I  -.153029  .000219   .336272  .000432  I -.5310856  .0000134  1.3327 0.0098  I   -11.015     .701    -6.052     .238  -.153200   .336000  -.5311000   -10.900    -7.100  
+92 615 48788.00 I  -.152876  .000219   .339376  .000432  I -.5324041  .0000147  1.3134 0.0105  I   -11.238     .774    -6.525     .222  -.153000   .339100  -.5324500   -10.600    -7.300  
+92 616 48789.00 I  -.152878  .000246   .342509  .000504  I -.5337292  .0000162  1.3445 0.0124  I   -11.461     .774    -6.625     .222  -.152900   .342000  -.5338200   -10.300    -7.300  
+92 617 48790.00 I  -.153016  .000639   .345557  .000575  I -.5351059  .0000200  1.4141 0.0131  I   -11.671    1.055    -6.476     .250  -.153100   .344800  -.5352300   -10.100    -7.200  
+92 618 48791.00 I  -.153169  .000729   .348440  .000574  I -.5365664  .0000205  1.5105 0.0163  I   -11.576    1.079    -6.302     .312  -.153300   .347400  -.5367200   -10.000    -7.000  
+92 619 48792.00 I  -.153196  .000753   .351097  .000566  I -.5381314  .0000257  1.6202 0.0176  I   -11.402    1.167    -6.297     .423  -.153400   .349900  -.5383100   -10.200    -6.800  
+92 620 48793.00 I  -.152947  .000761   .353491  .000584  I -.5398079  .0000285  1.7333 0.0186  I   -11.581    1.023    -6.449     .356  -.153300   .352200  -.5400000   -10.500    -6.500  
+92 621 48794.00 I  -.152328  .000696   .355688  .000419  I -.5415918  .0000268  1.8280 0.0196  I   -11.896    1.006    -6.230     .382  -.153000   .354400  -.5417800   -11.100    -6.300  
+92 622 48795.00 I  -.151443  .000748   .357814  .000401  I -.5434488  .0000270  1.8774 0.0176  I   -11.841     .876    -5.916     .339  -.152200   .356600  -.5436200   -11.800    -6.100  
+92 623 48796.00 I  -.150489  .000616   .359987  .000336  I -.5453287  .0000229  1.8731 0.0167  I   -11.617     .639    -5.875     .372  -.151200   .358800  -.5454800   -12.600    -6.000  
+92 624 48797.00 I  -.149658  .000587   .362268  .000327  I -.5471758  .0000196  1.8111 0.0148  I   -11.860     .722    -6.016     .259  -.150200   .361000  -.5473200   -13.400    -6.000  
+92 625 48798.00 I  -.148926  .000548   .364635  .000317  I -.5489337  .0000187  1.6975 0.0145  I   -12.582     .552    -6.266     .170  -.149200   .363400  -.5490800   -14.200    -6.100  
+92 626 48799.00 I  -.148103  .000580   .367069  .000288  I -.5505592  .0000214  1.5491 0.0150  I   -13.435     .564    -6.367     .199  -.148000   .365800  -.5507200   -14.700    -6.300  
+92 627 48800.00 I  -.147055  .000584   .369542  .000262  I -.5520272  .0000235  1.3867 0.0149  I   -14.003     .645    -6.152     .157  -.146800   .368400  -.5521800   -14.900    -6.400  
+92 628 48801.00 I  -.145800  .000433   .372031  .000236  I -.5533401  .0000208  1.2467 0.0157  I   -14.186     .756    -5.812     .171  -.145700   .371000  -.5535000   -14.700    -6.600  
+92 629 48802.00 I  -.144519  .000421   .374531  .000214  I -.5545415  .0000208  1.1695 0.0152  I   -14.124     .692    -5.677     .163  -.144700   .373500  -.5547000   -14.500    -6.700  
+92 630 48803.00 I  -.143342  .000393   .377023  .000216  I -.5557122  .0000223  1.1918 0.0152  I   -13.968     .607    -5.833     .298  -.143600   .375900  -.5558700   -14.300    -6.800  
+92 7 1 48804.00 I  -.142256  .000389   .379477  .000214  I  .4430472  .0000222  1.2973 0.0162  I   -13.824     .607    -6.085     .298  -.142500   .378200   .4429100   -13.900    -6.900  
+92 7 2 48805.00 I  -.141189  .000376   .381860  .000187  I  .4416753  .0000236  1.4565 0.0147  I   -13.758     .607    -6.227     .298  -.141300   .380500   .4415300   -13.600    -7.000  
+92 7 3 48806.00 I  -.140045  .000301   .384172  .000200  I  .4401262  .0000194  1.6397 0.0159  I   -13.855     .436    -6.156     .102  -.140200   .382700   .4399700   -13.900    -7.000  
+92 7 4 48807.00 I  -.138748  .000262   .386425  .000242  I  .4384135  .0000214  1.7702 0.0195  I   -14.145     .432    -5.945     .159  -.139200   .384900   .4382400   -14.300    -6.800  
+92 7 5 48808.00 I  -.137348  .000411   .388614  .000349  I  .4366213  .0000339  1.7941 0.0225  I   -14.556     .433    -5.731     .154  -.138000   .387100   .4364400   -14.800    -6.500  
+92 7 6 48809.00 I  -.135865  .000387   .390743  .000267  I  .4348628  .0000396  1.7055 0.0251  I   -14.958     .434    -5.560     .166  -.136700   .389400   .4346700   -15.400    -6.400  
+92 7 7 48810.00 I  -.134255  .000355   .392840  .000256  I  .4332363  .0000371  1.5389 0.0262  I   -15.276     .399    -5.493     .174  -.135000   .391500   .4330500   -16.000    -6.300  
+92 7 8 48811.00 I  -.132501  .000343   .394965  .000255  I  .4317956  .0000343  1.3405 0.0254  I   -15.475     .356    -5.591     .152  -.133200   .393700   .4316200   -16.400    -6.300  
+92 7 9 48812.00 I  -.130806  .000342   .397220  .000241  I  .4305555  .0000346  1.1414 0.0250  I   -15.508     .356    -5.743     .152  -.131400   .396100   .4304000   -16.600    -6.200  
+92 710 48813.00 I  -.129424  .000359   .399651  .000284  I  .4294993  .0000363  0.9827 0.0208  I   -15.635     .329    -5.839     .120  -.129900   .398800   .4293600   -16.800    -6.200  
+92 711 48814.00 I  -.128330  .000418   .402171  .000254  I  .4285694  .0000232  0.8860 0.0216  I   -15.918     .318    -5.844     .166  -.128700   .401700   .4284400   -16.700    -6.200  
+92 712 48815.00 I  -.127463  .000428   .404642  .000263  I  .4277093  .0000236  0.8432 0.0147  I   -16.200     .237    -5.827     .175  -.128000   .404500   .4275900   -16.400    -6.200  
+92 713 48816.00 I  -.126732  .000409   .406920  .000243  I  .4268671  .0000182  0.8482 0.0145  I   -16.348     .189    -5.829     .168  -.127500   .406800   .4267600   -16.200    -6.200  
+92 714 48817.00 I  -.126065  .000423   .408912  .000245  I  .4259970  .0000169  0.9010 0.0122  I   -16.372     .169    -5.812     .202  -.127000   .408600   .4258900   -16.000    -6.200  
+92 715 48818.00 I  -.125322  .000427   .410633  .000287  I  .4250476  .0000163  1.0063 0.0117  I   -16.368     .289    -5.720     .239  -.126200   .410100   .4249200   -16.200    -6.200  
+92 716 48819.00 I  -.124191  .000421   .412277  .000272  I  .4239735  .0000161  1.1444 0.0123  I   -16.389     .268    -5.603     .221  -.124900   .411600   .4238400   -16.500    -6.300  
+92 717 48820.00 I  -.122472  .000330   .414066  .000295  I  .4227595  .0000185  1.2806 0.0102  I   -16.483     .300    -5.600     .215  -.123100   .413300   .4226200   -17.000    -6.300  
+92 718 48821.00 I  -.120237  .000226   .416097  .000278  I  .4214188  .0000124  1.3974 0.0106  I   -16.639     .263    -5.650     .206  -.120700   .415400   .4212700   -17.500    -6.200  
+92 719 48822.00 I  -.117661  .000206   .418425  .000237  I  .4199735  .0000104  1.4877 0.0077  I   -16.815     .261    -5.698     .202  -.118100   .417800   .4198100   -18.200    -6.200  
+92 720 48823.00 I  -.114962  .000173   .420939  .000237  I  .4184560  .0000092  1.5402 0.0068  I   -16.864     .258    -5.707     .202  -.115400   .420400   .4182900   -19.000    -6.200  
+92 721 48824.00 I  -.112339  .000197   .423477  .000249  I  .4169068  .0000087  1.5515 0.0063  I   -16.785     .178    -5.748     .142  -.112800   .422900   .4167400   -19.600    -6.200  
+92 722 48825.00 I  -.110008  .000206   .425890  .000194  I  .4153668  .0000086  1.5213 0.0057  I   -16.887     .473    -5.896     .186  -.110400   .425200   .4152100   -20.100    -6.200  
+92 723 48826.00 I  -.108033  .000211   .428109  .000198  I  .4138765  .0000073  1.4542 0.0056  I   -17.380     .464    -6.042     .175  -.108200   .427400   .4137200   -20.500    -6.200  
+92 724 48827.00 I  -.106077  .000218   .430216  .000178  I  .4124657  .0000073  1.3653 0.0053  I   -18.060     .527    -6.100     .158  -.106000   .429500   .4122900   -20.600    -6.300  
+92 725 48828.00 I  -.103972  .000217   .432230  .000185  I  .4111448  .0000076  1.2797 0.0066  I   -18.518     .502    -6.026     .135  -.104000   .431500   .4109600   -20.400    -6.300  
+92 726 48829.00 I  -.101805  .000263   .434165  .000173  I  .4098930  .0000111  1.2340 0.0062  I   -18.481     .487    -5.905     .127  -.102100   .433400   .4097000   -20.000    -6.400  
+92 727 48830.00 I  -.099751  .000271   .436034  .000263  I  .4086562  .0000097  1.2501 0.0074  I   -18.074     .509    -5.913     .133  -.100000   .435300   .4084800   -19.600    -6.400  
+92 728 48831.00 I  -.097900  .000263   .437831  .000254  I  .4073690  .0000097  1.3376 0.0065  I   -17.673     .305    -6.096     .298  -.098000   .437200   .4072100   -19.100    -6.400  
+92 729 48832.00 I  -.096176  .000241   .439589  .000222  I  .4059513  .0000087  1.5142 0.0064  I   -17.470     .281    -6.281     .298  -.096100   .439000   .4058000   -18.600    -6.400  
+92 730 48833.00 I  -.094340  .000238   .441446  .000225  I  .4043197  .0000083  1.7533 0.0057  I   -17.567     .224    -6.235     .298  -.094300   .440800   .4041700   -18.400    -6.200  
+92 731 48834.00 I  -.092328  .000233   .443549  .000221  I  .4024494  .0000075  1.9781 0.0078  I   -17.998     .280    -5.994     .108  -.092500   .442700   .4023000   -18.300    -6.100  
+92 8 1 48835.00 I  -.090223  .000324   .445839  .000240  I  .4003910  .0000131  2.1204 0.0073  I   -18.641     .288    -5.745     .137  -.090400   .444900   .4002500   -18.400    -5.900  
+92 8 2 48836.00 I  -.088082  .000285   .448188  .000237  I  .3982501  .0000125  2.1384 0.0091  I   -19.285     .265    -5.597     .135  -.088300   .447300   .3981000   -18.700    -5.700  
+92 8 3 48837.00 I  -.085895  .000284   .450460  .000166  I  .3961543  .0000127  2.0362 0.0089  I   -19.786     .283    -5.452     .149  -.086200   .449700   .3959900   -19.100    -5.600  
+92 8 4 48838.00 I  -.083755  .000283   .452563  .000180  I  .3942075  .0000127  1.8445 0.0089  I   -20.107     .260    -5.315     .128  -.084100   .451900   .3940300   -19.400    -5.500  
+92 8 5 48839.00 I  -.081828  .000278   .454576  .000184  I  .3924681  .0000125  1.6442 0.0093  I   -20.223     .257    -5.378     .123  -.082200   .454100   .3922700   -19.900    -5.500  
+92 8 6 48840.00 I  -.080003  .000270   .456571  .000182  I  .3909099  .0000135  1.4714 0.0079  I   -20.052     .173    -5.604     .124  -.080400   .456100   .3907400   -20.200    -5.700  
+92 8 7 48841.00 I  -.078139  .000178   .458634  .000144  I  .3895165  .0000097  1.3248 0.0089  I   -19.775     .204    -5.851     .298  -.078500   .457900   .3893600   -20.500    -5.800  
+92 8 8 48842.00 I  -.076208  .000201   .460875  .000172  I  .3882434  .0000115  1.2287 0.0082  I   -19.707     .269    -6.000     .163  -.076500   .459800   .3881300   -20.400    -5.800  
+92 8 9 48843.00 I  -.074290  .000207   .463332  .000186  I  .3870412  .0000131  1.1862 0.0097  I   -19.894     .304    -5.974     .157  -.074400   .462000   .3869300   -20.300    -5.900  
+92 810 48844.00 I  -.072315  .000219   .465924  .000210  I  .3858530  .0000157  1.1978 0.0104  I   -20.048     .360    -5.886     .159  -.072500   .464700   .3857400   -19.800    -6.000  
+92 811 48845.00 I  -.070241  .000222   .468541  .000207  I  .3846145  .0000161  1.3029 0.0119  I   -19.997     .414    -5.892     .171  -.070700   .467200   .3844400   -19.700    -5.900  
+92 812 48846.00 I  -.068261  .000235   .471062  .000240  I  .3832183  .0000178  1.4949 0.0121  I   -19.901     .409    -5.885     .188  -.068900   .469700   .3830000   -19.800    -5.700  
+92 813 48847.00 I  -.066397  .000236   .473325  .000232  I  .3816262  .0000181  1.6825 0.0134  I   -19.828     .401    -5.694     .199  -.067100   .471800   .3814100   -20.100    -5.700  
+92 814 48848.00 I  -.064466  .000200   .475307  .000290  I  .3798709  .0000201  1.8172 0.0120  I   -19.995     .411    -5.393     .158  -.065100   .473800   .3796700   -20.500    -5.700  
+92 815 48849.00 I  -.062333  .000208   .477088  .000358  I  .3780113  .0000157  1.8934 0.0129  I   -20.418     .627    -5.267     .159  -.063000   .475900   .3778100   -21.100    -5.600  
+92 816 48850.00 I  -.060005  .000234   .478776  .000329  I  .3761010  .0000162  1.9188 0.0121  I   -20.925     .646    -5.393     .172  -.060700   .477800   .3759100   -21.700    -5.700  
+92 817 48851.00 I  -.057474  .000241   .480454  .000313  I  .3741898  .0000185  1.8958 0.0115  I   -21.248     .609    -5.604     .167  -.058100   .479700   .3740100   -22.200    -5.900  
+92 818 48852.00 I  -.054730  .000224   .482120  .000313  I  .3723218  .0000163  1.8354 0.0118  I   -21.189     .757    -5.773     .123  -.055400   .481500   .3721400   -22.600    -6.100  
+92 819 48853.00 I  -.051810  .000200   .483699  .000302  I  .3705234  .0000148  1.7616 0.0134  I   -20.899     .859    -5.909     .173  -.052500   .483100   .3703400   -22.700    -6.200  
+92 820 48854.00 I  -.048783  .000190   .485162  .000241  I  .3687999  .0000213  1.6840 0.0162  I   -20.838    1.089    -5.998     .160  -.049300   .484600   .3686300   -22.400    -6.400  
+92 821 48855.00 I  -.045664  .000274   .486491  .000200  I  .3671538  .0000289  1.6114 0.0190  I   -21.209    1.131    -6.045     .157  -.046100   .485900   .3669900   -21.900    -6.600  
+92 822 48856.00 I  -.042499  .000354   .487683  .000395  I  .3655657  .0000314  1.5732 0.0214  I   -21.780     .952    -6.043     .173  -.043100   .487100   .3654300   -21.300    -6.700  
+92 823 48857.00 I  -.039354  .000484   .488789  .000510  I  .3639866  .0000316  1.5976 0.0204  I   -21.812     .951    -6.065     .175  -.040100   .488200   .3638700   -20.800    -6.800  
+92 824 48858.00 I  -.036252  .000463   .489888  .000498  I  .3623445  .0000262  1.7001 0.0207  I   -21.320     .951    -6.229     .175  -.037200   .489200   .3622400   -20.300    -6.800  
+92 825 48859.00 I  -.033146  .000462   .491085  .000495  I  .3605595  .0000268  1.8833 0.0165  I   -20.771     .856    -6.454     .149  -.034200   .490300   .3604500   -20.000    -6.700  
+92 826 48860.00 I  -.030036  .000388   .492464  .000551  I  .3585519  .0000199  2.1444 0.0161  I   -20.499     .446    -6.459     .176  -.031000   .491600   .3584100   -20.000    -6.600  
+92 827 48861.00 I  -.026957  .000302   .493979  .000507  I  .3562597  .0000179  2.4385 0.0121  I   -20.712     .298    -6.126     .208  -.027700   .493200   .3560900   -20.300    -6.500  
+92 828 48862.00 I  -.023903  .000203   .495593  .000344  I  .3536961  .0000136  2.6692 0.0109  I   -21.237     .416    -5.714     .197  -.024400   .494900   .3535100   -20.700    -6.200  
+92 829 48863.00 I  -.020858  .000309   .497302  .000425  I  .3509702  .0000124  2.7534 0.0093  I   -21.607     .488    -5.375     .185  -.021300   .496700   .3507800   -21.400    -6.100  
+92 830 48864.00 I  -.017903  .000323   .499041  .000391  I  .3482488  .0000127  2.6592 0.0091  I   -21.624     .488    -5.364     .185  -.018300   .498500   .3480800   -22.100    -6.000  
+92 831 48865.00 I  -.015192  .000345   .500593  .000399  I  .3457035  .0000134  2.4097 0.0093  I   -21.714     .488    -5.457     .185  -.015600   .499900   .3455300   -22.700    -6.000  
+92 9 1 48866.00 I  -.012886  .000353   .501710  .000449  I  .3434557  .0000137  2.0811 0.0098  I   -22.079     .499    -5.363     .177  -.013200   .500900   .3432800   -23.200    -6.100  
+92 9 2 48867.00 I  -.010966  .000387   .502220  .000438  I  .3415326  .0000142  1.7786 0.0105  I   -22.354     .454    -5.245     .137  -.011000   .501200   .3413000   -23.300    -6.200  
+92 9 3 48868.00 I  -.008939  .000416   .502278  .000473  I  .3398721  .0000160  1.5552 0.0119  I   -22.168     .386    -5.472     .144  -.008900   .501100   .3396100   -23.200    -6.400  
+92 9 4 48869.00 I  -.006749  .000354   .502235  .000546  I  .3383956  .0000191  1.4117 0.0108  I   -21.532     .119    -5.923     .167  -.006700   .500900   .3381600   -23.000    -6.400  
+92 9 5 48870.00 I  -.004454  .000335   .502390  .000385  I  .3370185  .0000146  1.3589 0.0127  I   -20.903     .650    -6.154     .167  -.004100   .501200   .3368300   -22.700    -6.500  
+92 9 6 48871.00 I  -.002028  .000303   .502831  .000447  I  .3356478  .0000168  1.3963 0.0111  I   -20.689     .721    -6.054     .168  -.001500   .501600   .3355000   -22.000    -6.300  
+92 9 7 48872.00 I   .000513  .000303   .503509  .000390  I  .3342017  .0000167  1.5064 0.0115  I   -20.853     .686    -5.840     .161   .001000   .502500   .3340600   -21.400    -6.100  
+92 9 8 48873.00 I   .003059  .000287   .504279  .000318  I  .3326178  .0000158  1.6681 0.0117  I   -21.168     .884    -5.726     .155   .003300   .503500   .3324700   -20.900    -5.800  
+92 9 9 48874.00 I   .005476  .000276   .504990  .000274  I  .3308625  .0000163  1.8389 0.0112  I   -21.529     .686    -5.696     .133   .005600   .504400   .3307100   -20.800    -5.600  
+92 910 48875.00 I   .007814  .000433   .505575  .000346  I  .3289492  .0000158  1.9822 0.0113  I   -21.797     .686    -5.542     .133   .008000   .505100   .3287900   -20.900    -5.600  
+92 911 48876.00 I   .010098  .000561   .506007  .000397  I  .3269116  .0000157  2.0848 0.0093  I   -21.955     .308    -5.319     .103   .010500   .505500   .3267400   -21.200    -5.500  
+92 912 48877.00 I   .012363  .000451   .506266  .000560  I  .3247966  .0000100  2.1362 0.0089  I   -22.048     .272    -5.271     .298   .012800   .505700   .3246300   -21.600    -5.600  
+92 913 48878.00 I   .014709  .000446   .506388  .000563  I  .3226585  .0000084  2.1298 0.0065  I   -22.091     .166    -5.423     .298   .015100   .505700   .3225000   -22.200    -5.700  
+92 914 48879.00 I   .017122  .000428   .506485  .000554  I  .3205567  .0000083  2.0643 0.0050  I   -22.054     .275    -5.592     .298   .017300   .505700   .3204100   -22.500    -5.800  
+92 915 48880.00 I   .019557  .000390   .506712  .000547  I  .3185458  .0000056  1.9510 0.0055  I   -21.857     .359    -5.597     .105   .019400   .505700   .3184100   -22.900    -6.000  
+92 916 48881.00 I   .022043  .000267   .507108  .000505  I  .3166597  .0000072  1.8225 0.0048  I   -21.544     .359    -5.438     .105   .021800   .506100   .3165100   -22.800    -6.300  
+92 917 48882.00 I   .024591  .000160   .507480  .000515  I  .3148939  .0000078  1.7147 0.0058  I   -21.401     .359    -5.292     .105   .024400   .506500   .3147400   -22.500    -6.300  
+92 918 48883.00 I   .027139  .000188   .507675  .000351  I  .3132183  .0000090  1.6431 0.0069  I   -21.524     .416    -5.297     .128   .027200   .506800   .3130700   -22.200    -6.400  
+92 919 48884.00 I   .029623  .000331   .507617  .000414  I  .3115916  .0000114  1.6199 0.0072  I   -21.760     .463    -5.428     .104   .029900   .506900   .3114600   -21.800    -6.500  
+92 920 48885.00 I   .032003  .000308   .507391  .000330  I  .3099566  .0000113  1.6625 0.0078  I   -21.891     .450    -5.629     .113   .032400   .506700   .3098500   -21.300    -6.500  
+92 921 48886.00 I   .034238  .000321   .507135  .000336  I  .3082435  .0000107  1.7746 0.0079  I   -21.759     .732    -5.859     .119   .034600   .506500   .3081500   -21.000    -6.500  
+92 922 48887.00 I   .036273  .000345   .507005  .000324  I  .3063870  .0000111  1.9480 0.0077  I   -21.433     .628    -6.023     .120   .036500   .506300   .3062800   -20.800    -6.300  
+92 923 48888.00 I   .038147  .000346   .507048  .000314  I  .3043250  .0000110  2.1888 0.0078  I   -21.182     .628    -5.954     .120   .038300   .506100   .3041800   -20.700    -6.200  
+92 924 48889.00 I   .040098  .000340   .507093  .000325  I  .3020052  .0000110  2.4420 0.0073  I   -21.242     .628    -5.550     .120   .040200   .506100   .3018300   -20.800    -6.000  
+92 925 48890.00 I   .042174  .000265   .507081  .000320  I  .2994677  .0000096  2.6150 0.0070  I   -21.562     .667    -5.104     .148   .042200   .506100   .2992800   -21.000    -5.800  
+92 926 48891.00 I   .044339  .000247   .506983  .000330  I  .2968178  .0000087  2.6602 0.0062  I   -21.819     .558    -4.999     .315   .044500   .506200   .2966300   -21.300    -5.700  
+92 927 48892.00 I   .046531  .000269   .506715  .000420  I  .2941925  .0000078  2.5700 0.0062  I   -21.780     .288    -5.210     .357   .046600   .505900   .2940000   -21.600    -5.600  
+92 928 48893.00 I   .048707  .000269   .506217  .000405  I  .2917083  .0000088  2.3885 0.0062  I   -21.641     .219    -5.436     .490   .048700   .505500   .2915200   -21.800    -5.700  
+92 929 48894.00 I   .050938  .000271   .505528  .000409  I  .2894253  .0000096  2.1777 0.0065  I   -21.721     .155    -5.411     .397   .050800   .504800   .2892500   -21.900    -5.700  
+92 930 48895.00 I   .053292  .000280   .504791  .000387  I  .2873458  .0000095  1.9882 0.0064  I   -21.956     .155    -5.178     .397   .053000   .504000   .2872000   -21.800    -5.700  
+9210 1 48896.00 I   .055692  .000242   .504145  .000345  I  .2854308  .0000086  1.8530 0.0070  I   -21.949     .155    -5.002     .397   .055400   .503200   .2853100   -21.500    -5.800  
+9210 2 48897.00 I   .058118  .000308   .503680  .000361  I  .2836186  .0000104  1.7815 0.0060  I   -21.509     .791    -4.989     .275   .057800   .502500   .2835100   -21.100    -5.800  
+9210 3 48898.00 I   .060608  .000308   .503410  .000368  I  .2818446  .0000085  1.7795 0.0063  I   -20.922     .165    -5.033     .210   .060400   .502300   .2817300   -20.600    -5.700  
+9210 4 48899.00 I   .063234  .000303   .503229  .000389  I  .2800370  .0000072  1.8454 0.0064  I   -20.563     .165    -5.014     .210   .063100   .502100   .2799300   -20.100    -5.700  
+9210 5 48900.00 I   .066052  .000305   .503051  .000382  I  .2781375  .0000096  1.9602 0.0058  I   -20.472     .233    -4.978     .113   .066200   .501900   .2780900   -20.000    -5.600  
+9210 6 48901.00 I   .069055  .000326   .502853  .000348  I  .2761060  .0000090  2.1067 0.0068  I   -20.475     .202    -5.009     .208   .069200   .501900   .2760600   -19.900    -5.500  
+9210 7 48902.00 I   .072135  .000340   .502609  .000291  I  .2739224  .0000096  2.2581 0.0067  I   -20.520     .202    -5.090     .208   .072300   .501800   .2738600   -20.200    -5.300  
+9210 8 48903.00 I   .075164  .000329   .502249  .000357  I  .2715976  .0000099  2.3859 0.0082  I   -20.652     .202    -5.160     .208   .075300   .501600   .2715000   -20.600    -5.200  
+9210 9 48904.00 I   .078011  .000336   .501719  .000414  I  .2691643  .0000133  2.4727 0.0065  I   -20.838     .165    -5.226     .272   .078100   .501100   .2690300   -21.200    -5.100  
+921010 48905.00 I   .080592  .000333   .500975  .000368  I  .2666714  .0000085  2.5021 0.0071  I   -20.948     .184    -5.288     .217   .080600   .500400   .2665000   -21.700    -5.100  
+921011 48906.00 I   .082938  .000227   .500005  .000273  I  .2641812  .0000051  2.4682 0.0050  I   -20.956     .184    -5.317     .217   .082800   .499300   .2640000   -21.900    -5.100  
+921012 48907.00 I   .085182  .000227   .498864  .000273  I  .2617535  .0000052  2.3789 0.0034  I   -20.915     .213    -5.241     .121   .085100   .498100   .2615900   -22.000    -5.200  
+921013 48908.00 I   .087534  .000215   .497713  .000255  I  .2594361  .0000045  2.2516 0.0035  I   -20.796     .205    -5.015     .298   .087500   .496700   .2592900   -21.900    -5.200  
+921014 48909.00 I   .090137  .000191   .496752  .000206  I  .2572455  .0000048  2.1387 0.0034  I   -20.515     .205    -4.702     .298   .090200   .495600   .2571100   -21.400    -5.400  
+921015 48910.00 I   .092869  .000181   .496097  .000161  I  .2551454  .0000052  2.0656 0.0040  I   -20.151     .205    -4.438     .298   .093100   .495000   .2550400   -20.700    -5.500  
+921016 48911.00 I   .095524  .000153   .495655  .000176  I  .2531016  .0000064  2.0304 0.0045  I   -19.851     .208    -4.337     .298   .095800   .494800   .2530300   -19.800    -5.500  
+921017 48912.00 I   .097959  .000186   .495300  .000184  I  .2510598  .0000074  2.0696 0.0062  I   -19.728     .293    -4.431     .153   .098300   .494700   .2509900   -19.200    -5.500  
+921018 48913.00 I   .100177  .000242   .494912  .000177  I  .2489308  .0000106  2.2037 0.0065  I   -19.704     .624    -4.645     .159   .100400   .494500   .2488400   -18.600    -5.500  
+921019 48914.00 I   .102242  .000251   .494385  .000198  I  .2466266  .0000108  2.4151 0.0075  I   -19.601     .663    -4.865     .231   .102200   .493800   .2465000   -18.200    -5.400  
+921020 48915.00 I   .104231  .000250   .493641  .000242  I  .2440833  .0000105  2.6784 0.0075  I   -19.281     .763    -4.958     .223   .103900   .492900   .2439300   -18.000    -5.100  
+921021 48916.00 I   .106221  .000257   .492669  .000269  I  .2412656  .0000104  2.9543 0.0075  I   -18.761     .763    -4.843     .223   .106000   .491700   .2410900   -18.000    -4.900  
+921022 48917.00 I   .108349  .000293   .491575  .000269  I  .2381997  .0000106  3.1553 0.0085  I   -18.351     .722    -4.507     .214   .108200   .490400   .2380300   -18.200    -4.600  
+921023 48918.00 I   .110632  .000297   .490517  .000304  I  .2350047  .0000134  3.2071 0.0079  I   -18.228     .795    -4.019     .208   .110700   .489400   .2348600   -18.600    -4.400  
+921024 48919.00 I   .112984  .000304   .489593  .000324  I  .2318337  .0000118  3.1144 0.0084  I   -18.293     .601    -3.578     .228   .113300   .488500   .2317100   -18.900    -4.300  
+921025 48920.00 I   .115243  .000322   .488773  .000379  I  .2288108  .0000101  2.9169 0.0079  I   -18.350     .659    -3.468     .199   .115600   .487700   .2286900   -19.100    -4.200  
+921026 48921.00 I   .117297  .000301   .487948  .000331  I  .2260199  .0000105  2.6592 0.0071  I   -18.391     .232    -3.642     .199   .117500   .487000   .2259100   -19.200    -4.300  
+921027 48922.00 I   .119065  .000291   .486996  .000282  I  .2234917  .0000099  2.4035 0.0077  I   -18.517     .254    -3.834     .262   .119000   .486000   .2233700   -19.100    -4.400  
+921028 48923.00 I   .120544  .000258   .485856  .000302  I  .2211912  .0000113  2.2130 0.0077  I   -18.692     .270    -3.901     .283   .120200   .484800   .2210600   -18.700    -4.500  
+921029 48924.00 I   .121936  .000290   .484560  .000303  I  .2190355  .0000119  2.1131 0.0088  I   -18.711     .264    -3.915     .265   .121400   .483500   .2188900   -18.200    -4.600  
+921030 48925.00 I   .123457  .000259   .483156  .000359  I  .2169378  .0000135  2.0947 0.0081  I   -18.381     .269    -3.992     .290   .123000   .482200   .2167700   -17.700    -4.700  
+921031 48926.00 I   .125267  .000347   .481680  .000326  I  .2148240  .0000110  2.1427 0.0089  I   -17.788     .277    -4.127     .239   .125000   .480900   .2146500   -17.100    -4.700  
+9211 1 48927.00 I   .127408  .000345   .480182  .000359  I  .2126421  .0000117  2.2218 0.0086  I   -17.256     .277    -4.159     .239   .127400   .479500   .2124900   -16.600    -4.800  
+9211 2 48928.00 I   .129736  .000330   .478689  .000348  I  .2103838  .0000131  2.2909 0.0081  I   -16.939     .286    -4.125     .129   .129900   .478100   .2102600   -16.300    -4.600  
+9211 3 48929.00 I   .132018  .000301   .477293  .000348  I  .2080688  .0000113  2.3347 0.0081  I   -16.823     .479    -4.195     .197   .132200   .476700   .2079700   -16.300    -4.600  
+9211 4 48930.00 I   .134034  .000269   .476071  .000330  I  .2057129  .0000096  2.3832 0.0074  I   -16.880     .451    -4.389     .177   .134000   .475600   .2056100   -15.800    -4.600  
+9211 5 48931.00 I   .135730  .000275   .474916  .000299  I  .2032926  .0000096  2.4613 0.0068  I   -17.026     .451    -4.474     .177   .135500   .474200   .2031600   -16.200    -4.600  
+9211 6 48932.00 I   .137164  .000181   .473676  .000284  I  .2007989  .0000095  2.5136 0.0064  I   -17.230     .502    -4.389     .192   .136900   .473000   .2006600   -16.800    -4.700  
+9211 7 48933.00 I   .138539  .000217   .472270  .000266  I  .1982865  .0000085  2.5026 0.0063  I   -17.402     .481    -4.300     .216   .138100   .471500   .1981600   -17.400    -4.700  
+9211 8 48934.00 I   .140013  .000204   .470709  .000214  I  .1958095  .0000084  2.4441 0.0070  I   -17.483     .502    -4.223     .229   .139500   .470000   .1956900   -18.000    -4.800  
+9211 9 48935.00 I   .141637  .000290   .469027  .000222  I  .1934112  .0000112  2.3467 0.0081  I   -17.500     .317    -4.032     .181   .141100   .468300   .1932900   -18.500    -4.800  
+921110 48936.00 I   .143375  .000321   .467265  .000250  I  .1911216  .0000139  2.2326 0.0087  I   -17.451     .361    -3.687     .225   .142900   .466500   .1909900   -18.400    -4.700  
+921111 48937.00 I   .145215  .000299   .465487  .000244  I  .1889393  .0000133  2.1385 0.0098  I   -17.249     .423    -3.316     .259   .144900   .464700   .1887800   -18.000    -4.600  
+921112 48938.00 I   .147211  .000334   .463795  .000244  I  .1868318  .0000137  2.0828 0.0121  I   -16.860     .477    -3.215     .324   .147000   .463000   .1866800   -17.300    -4.400  
+921113 48939.00 I   .149308  .000353   .462245  .000264  I  .1847543  .0000202  2.0851 0.0104  I   -16.307     .501    -3.488     .353   .149200   .461500   .1846000   -16.700    -4.100  
+921114 48940.00 I   .151459  .000315   .460843  .000272  I  .1826347  .0000156  2.1680 0.0125  I   -15.731     .434    -4.053     .305   .151300   .460100   .1824900   -16.100    -3.900  
+921115 48941.00 I   .153688  .000336   .459540  .000290  I  .1803904  .0000146  2.3348 0.0094  I   -15.464     .468    -4.279     .328   .153500   .458900   .1802400   -15.800    -3.700  
+921116 48942.00 I   .155993  .000267   .458291  .000235  I  .1779453  .0000106  2.5612 0.0102  I   -15.578     .430    -4.006     .303   .155800   .457700   .1778000   -15.600    -3.500  
+921117 48943.00 I   .158277  .000331   .457088  .000228  I  .1752648  .0000143  2.7974 0.0085  I   -15.855     .388    -3.657     .304   .158200   .456500   .1751100   -15.600    -3.300  
+921118 48944.00 I   .160480  .000250   .455924  .000207  I  .1723679  .0000134  2.9815 0.0096  I   -16.111     .167    -3.492     .123   .160600   .455400   .1722100   -16.000    -3.300  
+921119 48945.00 I   .162663  .000270   .454751  .000216  I  .1693369  .0000128  3.0604 0.0104  I   -16.326     .161    -3.368     .114   .163000   .454200   .1691800   -16.600    -3.300  
+921120 48946.00 I   .164781  .000328   .453500  .000210  I  .1662885  .0000160  3.0152 0.0089  I   -16.531     .791    -3.183     .298   .165200   .452900   .1661400   -17.200    -3.400  
+921121 48947.00 I   .166706  .000318   .452072  .000244  I  .1633407  .0000124  2.8676 0.0099  I   -16.739     .130    -2.949     .298   .167200   .451300   .1632000   -17.300    -3.500  
+921122 48948.00 I   .168352  .000314   .450398  .000256  I  .1605728  .0000118  2.6615 0.0086  I   -16.915     .138    -2.788     .298   .168700   .449600   .1604400   -17.300    -3.700  
+921123 48949.00 I   .169592  .000324   .448510  .000255  I  .1580229  .0000120  2.4396 0.0063  I   -16.980     .138    -2.771     .298   .169600   .447800   .1578900   -17.100    -3.800  
+921124 48950.00 I   .170409  .000294   .446462  .000247  I  .1556831  .0000044  2.2491 0.0066  I   -16.881     .176    -2.821     .298   .170200   .445800   .1555500   -16.600    -4.100  
+921125 48951.00 I   .170909  .000326   .444279  .000286  I  .1535019  .0000056  2.1271 0.0038  I   -16.620     .195    -2.799     .298   .170600   .443700   .1533300   -15.900    -4.100  
+921126 48952.00 I   .171329  .000285   .441971  .000279  I  .1514019  .0000061  2.0861 0.0056  I   -16.254     .195    -2.636     .298   .170900   .441400   .1512000   -15.200    -4.100  
+921127 48953.00 I   .171871  .000348   .439582  .000265  I  .1493071  .0000097  2.1130 0.0051  I   -15.821     .791    -2.416     .298   .171400   .439000   .1490900   -14.400    -4.000  
+921128 48954.00 I   .172586  .000311   .437181  .000229  I  .1471611  .0000081  2.1845 0.0089  I   -15.352     .845    -2.292     .298   .172000   .436600   .1469400   -13.600    -3.900  
+921129 48955.00 I   .173419  .000420   .434829  .000254  I  .1449299  .0000149  2.2807 0.0085  I   -14.899     .845    -2.352     .298   .172700   .434200   .1447300   -13.000    -3.600  
+921130 48956.00 I   .174288  .000399   .432567  .000291  I  .1425977  .0000149  2.3830 0.0109  I   -14.560     .896    -2.573     .298   .173500   .432000   .1424200   -12.700    -3.300  
+9212 1 48957.00 I   .175124  .000426   .430416  .000329  I  .1401658  .0000158  2.4796 0.0109  I   -14.452     .738    -2.842     .105   .174400   .429900   .1400100   -12.700    -3.100  
+9212 2 48958.00 I   .175909  .000437   .428369  .000331  I  .1376364  .0000158  2.5825 0.0104  I   -14.536     .655    -3.012     .102   .175600   .427800   .1374800   -13.100    -3.000  
+9212 3 48959.00 I   .176811  .000414   .426428  .000331  I  .1350035  .0000136  2.6773 0.0117  I   -14.543     .655    -2.958     .102   .176900   .425800   .1348500   -13.600    -2.800  
+9212 4 48960.00 I   .178006  .000457   .424602  .000360  I  .1323019  .0000172  2.7116 0.0095  I   -14.584     .513    -2.749     .298   .178200   .424000   .1321700   -14.400    -2.800  
+9212 5 48961.00 I   .179261  .000406   .422835  .000335  I  .1296009  .0000133  2.6839 0.0100  I   -14.752     .483    -2.609     .104   .179500   .422400   .1294800   -15.100    -2.800  
+9212 6 48962.00 I   .180442  .000356   .421016  .000320  I  .1269445  .0000101  2.6249 0.0082  I   -14.959     .358    -2.530     .109   .180600   .420700   .1268200   -15.500    -3.000  
+9212 7 48963.00 I   .181507  .000358   .419048  .000275  I  .1243624  .0000097  2.5322 0.0068  I   -15.060     .341    -2.371     .102   .181600   .418900   .1242500   -15.700    -3.200  
+9212 8 48964.00 I   .182424  .000332   .416981  .000277  I  .1218870  .0000090  2.4190 0.0066  I   -14.963     .318    -2.137     .106   .182400   .416800   .1217800   -15.700    -3.200  
+9212 9 48965.00 I   .183234  .000320   .414896  .000283  I  .1195148  .0000089  2.3347 0.0068  I   -14.659     .293    -2.023     .101   .183300   .414600   .1194100   -15.600    -3.300  
+921210 48966.00 I   .184075  .000322   .412781  .000254  I  .1171967  .0000101  2.3130 0.0089  I   -14.290     .320    -2.251     .112   .184100   .412300   .1170700   -15.300    -3.300  
+921211 48967.00 I   .185114  .000334   .410653  .000234  I  .1148636  .0000154  2.3668 0.0070  I   -13.928     .237    -2.668     .102   .185100   .410100   .1147100   -14.900    -3.200  
+921212 48968.00 I   .186391  .000267   .408560  .000296  I  .1124375  .0000098  2.4973 0.0093  I   -13.673     .557    -2.930     .261   .186200   .407900   .1122700   -14.500    -3.100  
+921213 48969.00 I   .187772  .000286   .406530  .000273  I  .1098522  .0000104  2.6782 0.0069  I   -13.636     .657    -2.892     .389   .187400   .405800   .1096700   -14.200    -3.100  
+921214 48970.00 I   .189068  .000243   .404548  .000264  I  .1070804  .0000097  2.8614 0.0071  I   -13.827     .607    -2.614     .421   .188600   .403800   .1069000   -13.900    -3.000  
+921215 48971.00 I   .190156  .000223   .402565  .000254  I  .1041438  .0000097  3.0016 0.0071  I   -14.091     .604    -2.203     .462   .189800   .401900   .1039600   -13.900    -3.000  
+921216 48972.00 I   .190995  .000233   .400504  .000243  I  .1011071  .0000104  3.0522 0.0077  I   -14.260     .604    -1.834     .462   .190800   .400000   .1009100   -13.900    -2.700  
+921217 48973.00 I   .191741  .000264   .398313  .000254  I  .0980773  .0000119  2.9892 0.0098  I   -14.219     .604    -1.402     .462   .191700   .397900   .0978900   -14.200    -2.700  
+921218 48974.00 I   .192464  .000295   .395974  .000268  I  .0951610  .0000167  2.8295 0.0093  I   -14.027     .438    -1.186     .470   .192700   .395500   .0949800   -14.400    -2.600  
+921219 48975.00 I   .193139  .000313   .393493  .000203  I  .0924394  .0000144  2.6066 0.0108  I   -13.866     .327    -1.286     .323   .193500   .393000   .0922800   -14.700    -2.600  
+921220 48976.00 I   .193720  .000258   .390895  .000276  I  .0899542  .0000137  2.3646 0.0101  I   -13.858     .326    -1.557     .249   .194200   .390300   .0898200   -14.800    -2.600  
+921221 48977.00 I   .194234  .000265   .388228  .000275  I  .0877011  .0000142  2.1501 0.0089  I   -13.984     .565    -1.858     .111   .194800   .387700   .0875800   -14.800    -2.500  
+921222 48978.00 I   .194827  .000266   .385566  .000272  I  .0856314  .0000114  2.0034 0.0087  I   -14.040     .565    -2.110     .111   .195500   .385200   .0855100   -14.700    -2.600  
+921223 48979.00 I   .195638  .000224   .382963  .000270  I  .0836635  .0000100  1.9490 0.0069  I   -13.887     .565    -2.222     .111   .196100   .382700   .0835200   -14.500    -2.600  
+921224 48980.00 I   .196651  .000235   .380395  .000261  I  .0817043  .0000078  1.9820 0.0073  I   -13.621     .615    -2.154     .114   .196700   .380300   .0815400   -14.200    -2.500  
+921225 48981.00 I   .197901  .000240   .377836  .000272  I  .0796786  .0000106  2.0779 0.0054  I   -13.390     .764    -1.948     .298   .197700   .377700   .0794900   -13.800    -2.400  
+921226 48982.00 I   .199439  .000187   .375287  .000213  I  .0775356  .0000075  2.2126 0.0070  I   -13.226     .551    -1.735     .298   .199200   .375100   .0773400   -13.500    -2.200  
+921227 48983.00 I   .201252  .000152   .372765  .000191  I  .0752484  .0000090  2.3624 0.0063  I   -13.070     .153    -1.652     .134   .201000   .372400   .0750600   -13.200    -2.100  
+921228 48984.00 I   .203210  .000172   .370284  .000214  I  .0728132  .0000100  2.5050 0.0069  I   -12.926     .376    -1.731     .114   .203100   .369800   .0726400   -13.100    -1.900  
+921229 48985.00 I   .205074  .000173   .367837  .000218  I  .0702468  .0000104  2.6225 0.0073  I   -12.865     .512    -1.879     .189   .205100   .367200   .0701000   -13.100    -1.800  
+921230 48986.00 I   .206599  .000146   .365331  .000225  I  .0675835  .0000106  2.6940 0.0073  I   -12.859     .512    -1.962     .189   .206700   .364700   .0674400   -13.000    -1.800  
+921231 48987.00 I   .207631  .000152   .362604  .000242  I  .0648749  .0000103  2.7170 0.0088  I   -12.761     .512    -1.923     .189   .207800   .362000   .0647400   -13.100    -1.800  
+93 1 1 48988.00 I   .208194  .000172   .359648  .000240  I  .0621615  .0000141  2.7038 0.0075  I   -12.718     .651    -1.862     .219   .208500   .359100   .0620200   -13.300    -1.900  
+93 1 2 48989.00 I   .208431  .000166   .356562  .000241  I  .0594783  .0000108  2.6575 0.0094  I   -12.881     .587    -1.893     .180   .208700   .355900   .0593100   -13.400    -2.000  
+93 1 3 48990.00 I   .208526  .000197   .353449  .000252  I  .0568545  .0000124  2.5872 0.0081  I   -13.238     .578    -1.969     .198   .208800   .352600   .0566700   -13.400    -2.200  
+93 1 4 48991.00 I   .208647  .000207   .350396  .000251  I  .0543068  .0000120  2.5083 0.0105  I   -13.660     .393    -1.947     .298   .208900   .349600   .0541300   -13.500    -2.500  
+93 1 5 48992.00 I   .208921  .000289   .347431  .000241  I  .0518327  .0000170  2.4451 0.0104  I   -13.951     .377    -1.822     .298   .209000   .346600   .0516600   -13.400    -2.600  
+93 1 6 48993.00 I   .209421  .000313   .344530  .000237  I  .0494074  .0000169  2.4096 0.0116  I   -14.002     .377    -1.780     .298   .209300   .343800   .0492500   -13.400    -2.700  
+93 1 7 48994.00 I   .210159  .000312   .341695  .000223  I  .0469999  .0000158  2.4148 0.0126  I   -13.838     .377    -1.928     .298   .209700   .341100   .0468700   -13.400    -2.700  
+93 1 8 48995.00 I   .211067  .000323   .338925  .000221  I  .0445590  .0000187  2.4764 0.0109  I   -13.503     .290    -2.188     .298   .210600   .338400   .0444300   -13.400    -2.700  
+93 1 9 48996.00 I   .212049  .000299   .336234  .000240  I  .0420253  .0000149  2.6034 0.0122  I   -13.108     .235    -2.304     .298   .211600   .335800   .0418800   -13.400    -2.500  
+93 110 48997.00 I   .213045  .000365   .333641  .000276  I  .0393377  .0000158  2.7739 0.0136  I   -12.856     .261    -2.140     .298   .212800   .333200   .0392000   -13.500    -2.300  
+93 111 48998.00 I   .213955  .000354   .331154  .000273  I  .0364839  .0000227  2.9251 0.0127  I   -12.921     .114    -1.841     .298   .213900   .330800   .0363500   -13.700    -2.100  
+93 112 48999.00 I   .214684  .000225   .328784  .000464  I  .0335118  .0000199  3.0034 0.0151  I   -13.266     .114    -1.665     .298   .214800   .328400   .0333800   -14.000    -2.000  
+93 113 49000.00 I   .215132  .000204   .326497  .000462  I  .0305107  .0000198  2.9810 0.0140  I   -13.680     .216    -1.722     .298   .215300   .326200   .0303900   -14.200    -2.000  
+93 114 49001.00 I   .215226  .000206   .324183  .000462  I  .0275822  .0000196  2.8614 0.0177  I   -13.987     .216    -1.890     .298   .215500   .324100   .0274700   -14.400    -2.100  
+93 115 49002.00 I   .214978  .000303   .321737  .000557  I  .0248090  .0000293  2.6787 0.0136  I   -14.172     .283    -2.023     .298   .215300   .321800   .0247000   -14.600    -2.200  
+93 116 49003.00 I   .214461  .000289   .319091  .000435  I  .0222319  .0000190  2.4749 0.0149  I   -14.290     .207    -2.050     .298   .214500   .319200   .0221400   -14.500    -2.400  
+93 117 49004.00 I   .213815  .000202   .316306  .000317  I  .0198540  .0000056  2.2867 0.0099  I   -14.398     .207    -2.028     .298   .213700   .316400   .0197600   -14.300    -2.600  
+93 118 49005.00 I   .213184  .000208   .313454  .000306  I  .0176442  .0000055  2.1414 0.0039  I   -14.485     .186    -2.037     .298   .212800   .313300   .0175500   -14.000    -2.700  
+93 119 49006.00 I   .212639  .000206   .310580  .000242  I  .0155523  .0000053  2.0527 0.0039  I   -14.469     .791    -2.128     .298   .212300   .310100   .0154500   -13.700    -2.800  
+93 120 49007.00 I   .212180  .000206   .307690  .000254  I  .0135130  .0000056  2.0414 0.0040  I   -14.282     .152    -2.283     .298   .212000   .306900   .0133900   -13.600    -2.800  
+93 121 49008.00 I   .211925  .000182   .304822  .000258  I  .0114464  .0000059  2.0995 0.0043  I   -13.979     .152    -2.328     .298   .211900   .303900   .0113300   -13.500    -2.700  
+93 122 49009.00 I   .211898  .000133   .302089  .000223  I  .0093022  .0000066  2.1932 0.0051  I   -13.722     .187    -2.199     .298   .212000   .301000   .0091800   -13.500    -2.500  
+93 123 49010.00 I   .211999  .000142   .299570  .000282  I  .0070530  .0000083  2.3081 0.0078  I   -13.625     .210    -1.974     .134   .212100   .298500   .0069300   -13.600    -2.300  
+93 124 49011.00 I   .212110  .000240   .297312  .000313  I  .0046827  .0000141  2.4329 0.0081  I   -13.642     .227    -1.772     .175   .212300   .296300   .0045600   -13.700    -2.200  
+93 125 49012.00 I   .212144  .000228   .295279  .000312  I  .0021893  .0000140  2.5515 0.0099  I   -13.705     .212    -1.700     .188   .212400   .294300   .0020500   -13.900    -2.000  
+93 126 49013.00 I   .212037  .000245   .293336  .000316  I -.0004160  .0000138  2.6570 0.0106  I   -13.793     .235    -1.778     .272   .212300   .292300  -.0005600   -14.200    -2.000  
+93 127 49014.00 I   .211751  .000254   .291318  .000305  I -.0031141  .0000158  2.7305 0.0103  I   -13.835     .253    -1.980     .234   .211800   .290300  -.0032600   -14.500    -2.100  
+93 128 49015.00 I   .211304  .000267   .289155  .000302  I -.0058559  .0000154  2.7408 0.0127  I   -13.777     .253    -2.246     .234   .211100   .288300  -.0059900   -14.600    -2.200  
+93 129 49016.00 I   .210612  .000316   .286843  .000326  I -.0085770  .0000200  2.6949 0.0116  I   -13.629     .258    -2.350     .248   .210200   .286100  -.0087000   -14.800    -2.400  
+93 130 49017.00 I   .209635  .000364   .284374  .000254  I -.0112341  .0000173  2.6141 0.0127  I   -13.471     .556    -2.481     .197   .209100   .283700  -.0113600   -15.000    -2.700  
+93 131 49018.00 I   .208394  .000343   .281767  .000275  I -.0137992  .0000158  2.5152 0.0124  I   -13.492     .605    -2.715     .187   .208000   .281100  -.0139300   -14.900    -2.900  
+93 2 1 49019.00 I   .207041  .000355   .279023  .000271  I -.0162659  .0000177  2.4203 0.0109  I   -13.725     .591    -2.840     .298   .206800   .278400  -.0164000   -14.900    -3.200  
+93 2 2 49020.00 I   .205890  .000341   .276161  .000271  I -.0186504  .0000151  2.3570 0.0116  I   -14.044     .846    -2.728     .298   .205800   .275500  -.0187800      .000      .000  
+93 2 3 49021.00 I   .205272  .000326   .273272  .000285  I -.0210032  .0000150  2.3635 0.0106  I   -14.312     .901    -2.545     .185   .205600   .272600  -.0211300   -14.700    -3.500  
+93 2 4 49022.00 I   .205181  .000303   .270501  .000277  I -.0234080  .0000150  2.4618 0.0119  I   -14.286     .901    -2.595     .185   .205600   .269600  -.0235400   -14.400    -3.600  
+93 2 5 49023.00 I   .205410  .000354   .267923  .000288  I -.0259570  .0000186  2.6506 0.0094  I   -13.832     .911    -2.859     .217   .205700   .266800  -.0261000   -14.100    -3.500  
+93 2 6 49024.00 I   .205674  .000251   .265552  .000266  I -.0287298  .0000112  2.9011 0.0113  I   -13.309     .785    -3.090     .181   .205700   .264600  -.0288900   -13.800    -3.400  
+93 2 7 49025.00 I   .205728  .000216   .263317  .000278  I -.0317594  .0000127  3.1521 0.0083  I   -12.956     .843    -2.999     .192   .205700   .262400  -.0319500   -13.600    -3.100  
+93 2 8 49026.00 I   .205400  .000200   .261104  .000271  I -.0350095  .0000122  3.3296 0.0092  I   -12.944     .662    -2.688     .178   .205400   .260200  -.0352200   -13.600    -3.000  
+93 2 9 49027.00 I   .204693  .000244   .258794  .000243  I -.0383773  .0000133  3.3832 0.0079  I   -13.242     .285    -2.450     .298   .204900   .257800  -.0386000   -13.500    -2.900  
+93 210 49028.00 I   .203779  .000228   .256316  .000213  I -.0417285  .0000100  3.2948 0.0083  I   -13.628     .321    -2.430     .298   .204200   .255300  -.0419400   -13.700    -3.000  
+93 211 49029.00 I   .202761  .000220   .253763  .000218  I -.0449247  .0000099  3.0800 0.0075  I   -13.898     .301    -2.563     .298   .203400   .252700  -.0451200   -14.000    -3.100  
+93 212 49030.00 I   .201616  .000226   .251195  .000215  I -.0478655  .0000112  2.7963 0.0072  I   -14.001     .299    -2.714     .298   .202200   .250100  -.0480400   -14.200    -3.300  
+93 213 49031.00 I   .200385  .000208   .248588  .000190  I -.0505214  .0000105  2.5248 0.0072  I   -14.073     .275    -2.963     .298   .200900   .247500  -.0506900   -14.500    -3.500  
+93 214 49032.00 I   .199315  .000256   .245908  .000186  I -.0529393  .0000092  2.3260 0.0070  I   -14.148     .314    -3.302     .298   .199500   .245000  -.0531000   -14.600    -3.800  
+93 215 49033.00 I   .198417  .000267   .243224  .000178  I -.0552035  .0000092  2.2176 0.0061  I   -14.258     .295    -3.616     .298   .198400   .242500  -.0553600   -14.600    -3.900  
+93 216 49034.00 I   .197692  .000252   .240590  .000178  I -.0574059  .0000081  2.2032 0.0074  I   -14.457     .225    -3.825     .298   .197600   .240100  -.0575600   -14.600    -4.100  
+93 217 49035.00 I   .197110  .000271   .238066  .000256  I -.0596344  .0000115  2.2621 0.0070  I   -14.627     .340    -3.899     .298   .197100   .237400  -.0597700   -14.400    -4.100  
+93 218 49036.00 I   .196581  .000298   .235758  .000255  I -.0619457  .0000114  2.3679 0.0095  I   -14.554     .340    -3.815     .298   .196800   .234900  -.0620600   -14.400    -4.000  
+93 219 49037.00 I   .195886  .000367   .233727  .000297  I -.0643816  .0000151  2.5080 0.0084  I   -14.280     .412    -3.669     .298   .196300   .232700  -.0644800   -14.300    -3.900  
+93 220 49038.00 I   .194803  .000374   .232000  .000347  I -.0669627  .0000124  2.6510 0.0091  I   -14.013     .382    -3.503     .298   .195400   .230900  -.0670500   -14.200    -3.700  
+93 221 49039.00 I   .193264  .000290   .230511  .000308  I -.0696740  .0000102  2.7651 0.0079  I   -13.856     .382    -3.342     .298   .193900   .229400  -.0697600   -14.100    -3.700  
+93 222 49040.00 I   .191321  .000291   .229092  .000313  I -.0724767  .0000097  2.8306 0.0068  I   -13.793     .382    -3.277     .298   .191900   .228000  -.0725800   -14.100    -3.700  
+93 223 49041.00 I   .189105  .000232   .227541  .000294  I -.0753148  .0000091  2.8350 0.0065  I   -13.752     .324    -3.382     .298   .189600   .226700  -.0754400   -14.200    -3.700  
+93 224 49042.00 I   .186790  .000206   .225705  .000282  I -.0781292  .0000087  2.7869 0.0054  I   -13.630     .364    -3.583     .298   .187300   .224800  -.0782800   -14.000    -3.900  
+93 225 49043.00 I   .184497  .000205   .223638  .000279  I -.0808761  .0000059  2.7011 0.0059  I   -13.369     .364    -3.741     .298   .184900   .222900  -.0810400   -13.900    -4.000  
+93 226 49044.00 I   .182313  .000195   .221451  .000305  I -.0835222  .0000081  2.5876 0.0041  I   -13.021     .400    -3.814     .298   .182600   .220800  -.0836700   -13.600    -4.300  
+93 227 49045.00 I   .180270  .000174   .219255  .000209  I -.0860470  .0000056  2.4613 0.0050  I   -12.853     .377    -3.892     .161   .180600   .218600  -.0861700   -13.600    -4.500  
+93 228 49046.00 I   .178323  .000206   .217119  .000197  I -.0884464  .0000059  2.3397 0.0051  I   -13.068     .377    -4.038     .161   .178600   .216500  -.0885500   -13.500    -4.600  
+93 3 1 49047.00 I   .176433  .000203   .215094  .000201  I -.0907343  .0000085  2.2419 0.0060  I   -13.574     .349    -4.168     .152   .176600   .214400  -.0908200   -13.500    -4.800  
+93 3 2 49048.00 I   .174589  .000224   .213215  .000182  I -.0929467  .0000104  2.1938 0.0067  I   -14.056     .311    -4.186     .198   .174600   .212500  -.0930500   -13.500    -4.900  
+93 3 3 49049.00 I   .172837  .000217   .211506  .000180  I -.0951493  .0000104  2.2280 0.0074  I   -14.283     .523    -4.184     .171   .172700   .210700  -.0952700   -13.600    -4.900  
+93 3 4 49050.00 I   .171272  .000225   .209920  .000175  I -.0974343  .0000104  2.3571 0.0084  I   -14.290     .523    -4.308     .171   .171200   .209000  -.0975700   -13.300    -5.100  
+93 3 5 49051.00 I   .169878  .000277   .208394  .000166  I -.0998884  .0000133  2.5611 0.0088  I   -14.149     .613    -4.555     .116   .169800   .207600  -.1000400   -13.500    -5.000  
+93 3 6 49052.00 I   .168575  .000261   .206865  .000176  I -.1025715  .0000142  2.8103 0.0086  I   -13.856     .476    -4.781     .298   .168400   .206100  -.1027400   -13.700    -5.000  
+93 3 7 49053.00 I   .167251  .000275   .205305  .000209  I -.1055072  .0000108  3.0541 0.0097  I   -13.524     .521    -4.761     .298   .167000   .204500  -.1056700   -14.000    -4.800  
+93 3 8 49054.00 I   .165878  .000279   .203710  .000207  I -.1086556  .0000131  3.2254 0.0074  I   -13.331     .480    -4.526     .298   .165800   .202900  -.1088200   -13.900    -4.800  
+93 3 9 49055.00 I   .164621  .000289   .202094  .000212  I -.1119204  .0000102  3.2838 0.0084  I   -13.303     .155    -4.325     .298   .164700   .201400  -.1120700   -14.000    -4.800  
+93 310 49056.00 I   .163704  .000289   .200494  .000211  I -.1151810  .0000106  3.2157 0.0082  I   -13.360     .492    -4.288     .298   .163700   .199800  -.1153000   -14.100    -4.800  
+93 311 49057.00 I   .163151  .000260   .198951  .000204  I -.1183182  .0000128  3.0467 0.0083  I   -13.491     .492    -4.377     .298   .163100   .198300  -.1184100   -14.200    -4.900  
+93 312 49058.00 I   .162596  .000258   .197466  .000197  I -.1212618  .0000128  2.8398 0.0110  I   -13.631     .633    -4.483     .298   .162600   .196900  -.1213400   -14.200    -5.000  
+93 313 49059.00 I   .161644  .000294   .196015  .000192  I -.1240068  .0000178  2.6600 0.0110  I   -13.616     .529    -4.644     .107   .161900   .195500  -.1240900   -14.100    -5.100  
+93 314 49060.00 I   .160152  .000258   .194585  .000306  I -.1266037  .0000179  2.5461 0.0126  I   -13.364     .577    -4.923     .116   .160500   .194100  -.1267000   -13.900    -5.400  
+93 315 49061.00 I   .158171  .000259   .193139  .000304  I -.1291206  .0000177  2.4971 0.0124  I   -13.045     .478    -5.234     .158   .158500   .192700  -.1292200   -13.500    -5.600  
+93 316 49062.00 I   .155860  .000272   .191669  .000310  I -.1316158  .0000171  2.5020 0.0119  I   -12.943     .248    -5.445     .170   .156000   .191200  -.1317200   -13.300    -5.600  
+93 317 49063.00 I   .153408  .000283   .190202  .000311  I -.1341389  .0000159  2.5499 0.0118  I   -13.092     .328    -5.523     .152   .153600   .189700  -.1342400   -13.000    -5.700  
+93 318 49064.00 I   .150961  .000286   .188755  .000311  I -.1367249  .0000163  2.6256 0.0107  I   -13.231     .328    -5.469     .152   .151100   .188200  -.1368200   -12.900    -5.700  
+93 319 49065.00 I   .148646  .000274   .187318  .000331  I -.1393946  .0000142  2.7147 0.0102  I   -13.129     .332    -5.414     .157   .148600   .186700  -.1394800   -12.800    -5.700  
+93 320 49066.00 I   .146522  .000303   .185882  .000328  I -.1421530  .0000121  2.8001 0.0101  I   -12.810     .347    -5.324     .149   .146500   .185300  -.1422400   -12.700    -5.500  
+93 321 49067.00 I   .144578  .000329   .184458  .000195  I -.1449878  .0000144  2.8647 0.0072  I   -12.526     .404    -5.138     .108   .144600   .183900  -.1450800   -12.700    -5.500  
+93 322 49068.00 I   .142711  .000254   .183056  .000178  I -.1478704  .0000077  2.8934 0.0082  I   -12.435     .391    -4.961     .160   .142700   .182600  -.1479800   -12.600    -5.500  
+93 323 49069.00 I   .140784  .000245   .181682  .000176  I -.1507614  .0000080  2.8826 0.0055  I   -12.483     .348    -4.949     .216   .140900   .181100  -.1508900   -12.700    -5.600  
+93 324 49070.00 I   .138650  .000249   .180349  .000178  I -.1536223  .0000078  2.8320 0.0057  I   -12.558     .340    -5.167     .184   .138700   .179700  -.1537700   -13.000    -5.700  
+93 325 49071.00 I   .136190  .000258   .179107  .000178  I -.1564082  .0000081  2.7300 0.0058  I   -12.610     .340    -5.614     .184   .136200   .178400  -.1565700   -13.000    -5.900  
+93 326 49072.00 I   .133300  .000213   .177912  .000189  I -.1590735  .0000086  2.6017 0.0054  I   -12.565     .321    -5.919     .204   .133300   .177200  -.1592400   -13.100    -6.100  
+93 327 49073.00 I   .130164  .000193   .176669  .000221  I -.1616137  .0000071  2.4798 0.0055  I   -12.393     .277    -6.054     .174   .130200   .175900  -.1617700   -13.100    -6.400  
+93 328 49074.00 I   .126954  .000190   .175365  .000224  I -.1640387  .0000070  2.3746 0.0076  I   -12.261     .291    -6.159     .149   .127000   .174400  -.1641800   -12.900    -6.600  
+93 329 49075.00 I   .123795  .000206   .174030  .000268  I -.1663745  .0000134  2.3040 0.0076  I   -12.291     .239    -6.262     .298   .123700   .173000  -.1665100   -12.700    -6.800  
+93 330 49076.00 I   .120808  .000168   .172734  .000260  I -.1686651  .0000135  2.2885 0.0093  I   -12.384     .112    -6.312     .298   .120500   .171600  -.1687800   -12.300    -6.800  
+93 331 49077.00 I   .118065  .000182   .171571  .000256  I -.1709796  .0000129  2.3573 0.0088  I   -12.374     .112    -6.328     .298   .117700   .170700  -.1711000   -12.000    -6.900  
+93 4 1 49078.00 I   .115461  .000161   .170608  .000249  I -.1734106  .0000113  2.5188 0.0121  I   -12.193     .112    -6.406     .298   .115200   .169800  -.1735300   -11.700    -6.800  
+93 4 2 49079.00 I   .112819  .000189   .169850  .000226  I -.1760396  .0000204  2.7474 0.0110  I   -11.913     .791    -6.562     .298   .112800   .169100  -.1761600   -11.600    -6.600  
+93 4 3 49080.00 I   .109985  .000251   .169234  .000161  I -.1789125  .0000189  2.9973 0.0128  I   -11.669     .310    -6.630     .298   .110400   .168500  -.1790400   -11.000    -6.500  
+93 4 4 49081.00 I   .106929  .000258   .168672  .000135  I -.1820214  .0000155  3.2089 0.0124  I   -11.588     .310    -6.450     .298   .107500   .167900  -.1821500   -11.400    -6.400  
+93 4 5 49082.00 I   .103742  .000253   .168107  .000178  I -.1852993  .0000160  3.3277 0.0111  I   -11.705     .310    -6.115     .298   .104400   .167300  -.1854200   -12.000    -6.300  
+93 4 6 49083.00 I   .100497  .000243   .167543  .000195  I -.1886393  .0000160  3.3340 0.0113  I   -11.894     .236    -5.905     .298   .101000   .166800  -.1887400   -12.300    -6.300  
+93 4 7 49084.00 I   .097190  .000266   .166999  .000196  I -.1919310  .0000159  3.2312 0.0107  I   -12.000     .236    -5.965     .298   .097400   .166300  -.1920200   -12.600    -6.300  
+93 4 8 49085.00 I   .093870  .000272   .166460  .000200  I -.1950761  .0000143  3.0515 0.0127  I   -12.022     .236    -6.176     .298   .093800   .165900  -.1951500   -12.700    -6.500  
+93 4 9 49086.00 I   .090617  .000223   .165924  .000225  I -.1980306  .0000198  2.8618 0.0107  I   -12.033     .125    -6.353     .298   .090200   .165400  -.1981000   -12.700    -6.700  
+93 410 49087.00 I   .087503  .000279   .165434  .000265  I -.2008148  .0000158  2.7176 0.0129  I   -11.973     .153    -6.465     .134   .087100   .164900  -.2008700   -12.700    -6.800  
+93 411 49088.00 I   .084599  .000288   .165031  .000277  I -.2034890  .0000165  2.6431 0.0131  I   -11.743     .187    -6.582     .122   .084300   .164400  -.2035500   -12.500    -6.900  
+93 412 49089.00 I   .081980  .000304   .164749  .000212  I -.2061209  .0000208  2.6282 0.0128  I   -11.481     .220    -6.689     .142   .081800   .163900  -.2061900   -12.300    -7.100  
+93 413 49090.00 I   .079691  .000297   .164583  .000244  I -.2087601  .0000195  2.6572 0.0142  I   -11.477     .306    -6.677     .145   .079700   .163600  -.2088500   -12.000    -7.200  
+93 414 49091.00 I   .077753  .000287   .164501  .000247  I -.2114531  .0000194  2.7395 0.0138  I   -11.738     .303    -6.562     .132   .077900   .163600  -.2115600   -11.900    -7.200  
+93 415 49092.00 I   .076037  .000287   .164432  .000236  I -.2142464  .0000195  2.8438 0.0158  I   -11.886     .303    -6.561     .132   .076200   .163600  -.2143700   -11.800    -7.200  
+93 416 49093.00 I   .074297  .000303   .164311  .000202  I -.2171315  .0000250  2.9204 0.0121  I   -11.677     .342    -6.775     .112   .074400   .163600  -.2172500   -11.800    -7.200  
+93 417 49094.00 I   .072364  .000233   .164130  .000208  I -.2200758  .0000142  2.9628 0.0134  I   -11.179     .413    -6.919     .121   .072400   .163500  -.2201900   -11.700    -7.100  
+93 418 49095.00 I   .070167  .000252   .163952  .000226  I -.2230453  .0000096  2.9699 0.0102  I   -10.809     .430    -6.854     .130   .070300   .163500  -.2231600   -11.600    -7.100  
+93 419 49096.00 I   .067678  .000203   .163854  .000248  I -.2260020  .0000146  2.9362 0.0091  I   -10.838     .559    -6.730     .159   .068000   .163400  -.2261100   -11.600    -7.100  
+93 420 49097.00 I   .064991  .000208   .163886  .000265  I -.2289063  .0000155  2.8684 0.0107  I   -11.190     .581    -6.775     .256   .065500   .163400  -.2290100   -11.700    -7.200  
+93 421 49098.00 I   .062269  .000234   .164046  .000266  I -.2317394  .0000156  2.8024 0.0111  I   -11.576     .570    -7.003     .222   .062800   .163400  -.2318600   -11.700    -7.400  
+93 422 49099.00 I   .059560  .000208   .164214  .000266  I -.2345184  .0000159  2.7584 0.0146  I   -11.622     .570    -7.146     .222   .060000   .163300  -.2346600   -11.700    -7.600  
+93 423 49100.00 I   .056815  .000247   .164291  .000279  I -.2372510  .0000248  2.6990 0.0132  I   -11.470     .579    -7.299     .251   .057000   .163100  -.2373800   -11.500    -7.700  
+93 424 49101.00 I   .054017  .000269   .164249  .000247  I -.2399097  .0000212  2.6196 0.0165  I   -11.390     .636    -7.341     .298   .054000   .162900  -.2400300   -11.300    -8.000  
+93 425 49102.00 I   .051235  .000294   .164158  .000262  I -.2424968  .0000218  2.5600 0.0131  I   -11.502     .551    -7.297     .300   .051100   .162800  -.2426200   -11.000    -8.200  
+93 426 49103.00 I   .048568  .000275   .164102  .000202  I -.2450409  .0000153  2.5339 0.0132  I   -11.837     .829    -7.303     .345   .048300   .163000  -.2451500   -10.800    -8.200  
+93 427 49104.00 I   .046083  .000262   .164147  .000183  I -.2475844  .0000149  2.5672 0.0106  I   -12.288     .845    -7.420     .379   .045800   .163400  -.2477200   -10.500    -8.300  
+93 428 49105.00 I   .043828  .000260   .164335  .000186  I -.2502105  .0000148  2.7060 0.0106  I   -12.520     .776    -7.610     .316   .043400   .163800  -.2503800   -10.400    -8.200  
+93 429 49106.00 I   .041787  .000279   .164665  .000190  I -.2530275  .0000151  2.9383 0.0106  I   -12.123     .776    -7.690     .316   .041400   .164200  -.2532100   -10.500    -8.200  
+93 430 49107.00 I   .039911  .000278   .165073  .000192  I -.2560917  .0000151  3.1846 0.0099  I   -11.325     .783    -7.647     .280   .039500   .164600  -.2562600   -10.700    -8.000  
+93 5 1 49108.00 I   .038162  .000254   .165485  .000180  I -.2593800  .0000129  3.3808 0.0089  I   -10.625     .783    -7.446     .280   .038000   .165000  -.2595300   -11.100    -7.900  
+93 5 2 49109.00 I   .036530  .000228   .165871  .000096  I -.2628226  .0000095  3.4850 0.0090  I   -10.323     .410    -7.069     .298   .036600   .165300  -.2629600   -11.600    -7.700  
+93 5 3 49110.00 I   .034937  .000282   .166200  .000124  I -.2663070  .0000126  3.4602 0.0091  I   -10.460     .410    -6.657     .298   .035100   .165600  -.2664400   -12.200    -7.700  
+93 5 4 49111.00 I   .033243  .000329   .166454  .000202  I -.2696980  .0000156  3.3004 0.0099  I   -10.874     .264    -6.478     .298   .033300   .165800  -.2698200   -12.800    -7.600  
+93 5 5 49112.00 I   .031379  .000322   .166619  .000197  I -.2728791  .0000152  3.0546 0.0109  I   -11.347     .334    -6.692     .298   .031400   .166100  -.2730100   -13.300    -7.600  
+93 5 6 49113.00 I   .029478  .000322   .166678  .000199  I -.2758004  .0000151  2.7886 0.0107  I   -11.792     .334    -7.115     .298   .029500   .166200  -.2759200   -13.500    -7.700  
+93 5 7 49114.00 I   .027545  .000315   .166609  .000212  I -.2784700  .0000152  2.5638 0.0113  I   -12.174     .334    -7.366     .298   .027700   .166400  -.2785800   -13.500    -7.800  
+93 5 8 49115.00 I   .025583  .000327   .166474  .000238  I -.2809558  .0000167  2.4224 0.0117  I   -12.284     .373    -7.404     .105   .025900   .166400  -.2810600   -13.200    -7.800  
+93 5 9 49116.00 I   .023567  .000292   .166356  .000262  I -.2833422  .0000178  2.3632 0.0110  I   -12.050     .439    -7.370     .141   .024000   .166400  -.2834500   -12.800    -7.900  
+93 510 49117.00 I   .021584  .000244   .166332  .000218  I -.2857034  .0000144  2.3678 0.0104  I   -11.693     .381    -7.354     .298   .022100   .166300  -.2858100   -12.300    -7.800  
+93 511 49118.00 I   .019770  .000179   .166462  .000185  I -.2880885  .0000106  2.4047 0.0089  I   -11.533     .125    -7.374     .298   .020100   .166200  -.2882100   -11.800    -7.800  
+93 512 49119.00 I   .018154  .000181   .166749  .000181  I -.2905120  .0000106  2.4394 0.0076  I   -11.656     .495    -7.402     .155   .018200   .166100  -.2906300   -11.400    -7.700  
+93 513 49120.00 I   .016659  .000192   .167191  .000169  I -.2929677  .0000108  2.4754 0.0070  I   -11.782     .495    -7.402     .155   .016600   .166400  -.2931100   -11.100    -7.500  
+93 514 49121.00 I   .015280  .000186   .167765  .000158  I -.2954686  .0000092  2.5290 0.0069  I   -11.599     .495    -7.368     .155   .015300   .166800  -.2956200   -10.900    -7.500  
+93 515 49122.00 I   .014000  .000185   .168440  .000164  I -.2980239  .0000087  2.5773 0.0063  I   -11.239     .495    -7.276     .155   .014000   .167400  -.2981900   -11.000    -7.400  
+93 516 49123.00 I   .012715  .000192   .169193  .000153  I -.3006133  .0000086  2.5961 0.0077  I   -11.088     .507    -7.121     .164   .012700   .168200  -.3007600   -11.300    -7.400  
+93 517 49124.00 I   .011395  .000270   .170002  .000179  I -.3032040  .0000127  2.5788 0.0064  I   -11.308     .508    -7.010     .180   .011400   .169000  -.3033500   -11.700    -7.500  
+93 518 49125.00 I   .010122  .000263   .170851  .000161  I -.3057593  .0000094  2.5268 0.0076  I   -11.718     .206    -7.106     .135   .010200   .170000  -.3059000   -12.300    -7.600  
+93 519 49126.00 I   .008962  .000260   .171744  .000138  I -.3082467  .0000083  2.4422 0.0060  I   -12.046     .206    -7.431     .135   .009000   .170900  -.3083700   -12.900    -7.800  
+93 520 49127.00 I   .007802  .000272   .172669  .000138  I -.3106349  .0000074  2.3312 0.0056  I   -12.255     .195    -7.694     .110   .007600   .171900  -.3107400   -13.300    -7.900  
+93 521 49128.00 I   .006488  .000306   .173605  .000145  I -.3129097  .0000074  2.2219 0.0056  I   -12.370     .195    -7.751     .110   .006000   .172900  -.3130200   -13.500    -8.100  
+93 522 49129.00 I   .004962  .000323   .174529  .000121  I -.3150881  .0000085  2.1407 0.0056  I   -12.385     .166    -7.664     .105   .004000   .173900  -.3152100   -13.500    -8.300  
+93 523 49130.00 I   .003180  .000307   .175435  .000147  I -.3172081  .0000083  2.1104 0.0050  I   -12.340     .374    -7.541     .298   .001900   .174900  -.3173300   -13.300    -8.300  
+93 524 49131.00 I   .001001  .000288   .176264  .000158  I -.3193324  .0000051  2.1505 0.0049  I   -12.299     .357    -7.459     .298  -.000400   .175700  -.3194600   -13.000    -8.300  
+93 525 49132.00 I  -.001543  .000293   .176968  .000147  I -.3215329  .0000052  2.2621 0.0039  I   -12.297     .309    -7.467     .298  -.002700   .176500  -.3216700   -12.700    -8.200  
+93 526 49133.00 I  -.004186  .000271   .177552  .000173  I -.3238725  .0000058  2.4214 0.0039  I   -12.264     .343    -7.597     .298  -.004900   .177000  -.3240100   -12.500    -8.000  
+93 527 49134.00 I  -.006612  .000255   .178039  .000168  I -.3263804  .0000058  2.5948 0.0056  I   -12.208     .343    -7.764     .298  -.006900   .177400  -.3265300   -12.300    -7.900  
+93 528 49135.00 I  -.008664  .000254   .178485  .000165  I -.3290562  .0000095  2.7504 0.0062  I   -12.087     .343    -7.860     .298  -.008700   .177700  -.3292300   -12.300    -7.600  
+93 529 49136.00 I  -.010426  .000279   .178935  .000144  I -.3318619  .0000110  2.8481 0.0100  I   -11.910     .228    -7.834     .298  -.010400   .178000  -.3320500   -12.500    -7.400  
+93 530 49137.00 I  -.012046  .000327   .179405  .000164  I -.3347233  .0000176  2.8585 0.0095  I   -11.812     .791    -7.665     .298  -.012000   .178400  -.3349100   -12.900    -7.300  
+93 531 49138.00 I  -.013693  .000317   .179884  .000236  I -.3375466  .0000156  2.7720 0.0114  I   -11.935     .122    -7.392     .298  -.013700   .178900  -.3377400   -13.300    -7.200  
+93 6 1 49139.00 I  -.015457  .000320   .180333  .000303  I -.3402389  .0000145  2.6002 0.0110  I   -12.301     .122    -7.129     .298  -.015400   .179500  -.3404000   -13.600    -7.300  
+93 6 2 49140.00 I  -.017306  .000327   .180721  .000305  I -.3427283  .0000154  2.3724 0.0098  I   -12.803     .791    -6.996     .298  -.017100   .180000  -.3428600   -13.800    -7.300  
+93 6 3 49141.00 I  -.019162  .000308   .181046  .000313  I -.3449825  .0000131  2.1410 0.0097  I   -13.288     .791    -6.985     .298  -.018900   .180500  -.3450900   -13.900    -7.300  
+93 6 4 49142.00 I  -.020994  .000281   .181454  .000317  I -.3470279  .0000117  1.9623 0.0091  I   -13.599     .791    -6.988     .298  -.020900   .180900  -.3471100   -14.000    -7.500  
+93 6 5 49143.00 I  -.022821  .000274   .182091  .000324  I -.3489347  .0000126  1.8665 0.0114  I   -13.654     .791    -6.902     .298  -.022800   .181500  -.3489900   -13.800    -7.600  
+93 6 6 49144.00 I  -.024651  .000361   .182945  .000336  I -.3507899  .0000196  1.8576 0.0088  I   -13.514     .283    -6.721     .298  -.024600   .182400  -.3508500   -13.600    -7.500  
+93 6 7 49145.00 I  -.026472  .000260   .183944  .000245  I -.3526721  .0000122  1.9154 0.0117  I   -13.344     .292    -6.582     .298  -.026500   .183300  -.3527500   -13.500    -7.500  
+93 6 8 49146.00 I  -.028260  .000292   .184996  .000179  I -.3546329  .0000129  2.0099 0.0089  I   -13.296     .298    -6.641     .298  -.028300   .184300  -.3547500   -13.300    -7.500  
+93 6 9 49147.00 I  -.029986  .000296   .186010  .000178  I -.3566907  .0000130  2.1009 0.0099  I   -13.369     .298    -6.942     .298  -.030200   .185300  -.3568300   -13.300    -7.400  
+93 610 49148.00 I  -.031655  .000290   .186928  .000197  I -.3588295  .0000149  2.1764 0.0098  I   -13.533     .298    -7.344     .298  -.032000   .186200  -.3590000   -13.500    -7.300  
+93 611 49149.00 I  -.033229  .000292   .187816  .000194  I -.3610373  .0000148  2.2334 0.0091  I   -13.633     .298    -7.653     .298  -.033500   .187000  -.3612300   -13.800    -7.300  
+93 612 49150.00 I  -.034528  .000240   .188782  .000156  I -.3632832  .0000106  2.2510 0.0109  I   -13.643     .258    -7.716     .298  -.034700   .188100  -.3634800   -14.300    -7.300  
+93 613 49151.00 I  -.035578  .000210   .189930  .000218  I -.3655243  .0000161  2.2235 0.0080  I   -13.730     .791    -7.560     .298  -.035600   .189300  -.3657100   -14.700    -7.500  
+93 614 49152.00 I  -.036543  .000172   .191288  .000197  I -.3677157  .0000119  2.1525 0.0101  I   -14.024     .791    -7.349     .102  -.036600   .190600  -.3678700   -15.200    -7.600  
+93 615 49153.00 I  -.037651  .000163   .192787  .000205  I -.3698172  .0000122  2.0451 0.0082  I   -14.497     .791    -7.256     .102  -.037800   .192000  -.3699400   -15.700    -7.800  
+93 616 49154.00 I  -.039083  .000163   .194308  .000190  I -.3718005  .0000114  1.9214 0.0082  I   -15.026     .791    -7.312     .102  -.039200   .193500  -.3719100   -16.000    -8.000  
+93 617 49155.00 I  -.040805  .000159   .195682  .000185  I -.3736641  .0000111  1.8101 0.0077  I   -15.473     .791    -7.313     .102  -.040900   .194800  -.3737700   -16.200    -8.200  
+93 618 49156.00 I  -.042804  .000146   .196851  .000181  I -.3754291  .0000104  1.7240 0.0075  I   -15.752     .791    -7.207     .102  -.042900   .195900  -.3755400   -16.200    -8.300  
+93 619 49157.00 I  -.045006  .000178   .197823  .000239  I -.3771254  .0000101  1.6779 0.0084  I   -15.816     .791    -7.109     .106  -.045100   .196900  -.3772600   -16.200    -8.400  
+93 620 49158.00 I  -.047278  .000179   .198658  .000185  I -.3788066  .0000132  1.6968 0.0083  I   -15.676     .298    -7.109     .182  -.047200   .197800  -.3789600   -16.000    -8.400  
+93 621 49159.00 I  -.049500  .000171   .199429  .000186  I -.3805425  .0000132  1.7863 0.0080  I   -15.454     .511    -7.179     .208  -.049300   .198600  -.3807100   -15.900    -8.400  
+93 622 49160.00 I  -.051507  .000202   .200208  .000178  I -.3823984  .0000090  1.9333 0.0076  I   -15.366     .512    -7.253     .173  -.051300   .199300  -.3825600   -15.800    -8.200  
+93 623 49161.00 I  -.053139  .000189   .201109  .000146  I -.3844143  .0000076  2.0959 0.0062  I   -15.564     .512    -7.291     .173  -.053000   .200200  -.3845700   -15.900    -7.900  
+93 624 49162.00 I  -.054384  .000192   .202161  .000157  I -.3865801  .0000084  2.2283 0.0057  I   -15.910     .399    -7.107     .145  -.054400   .201400  -.3867400   -16.000    -7.700  
+93 625 49163.00 I  -.055378  .000192   .203307  .000153  I -.3888537  .0000085  2.3092 0.0060  I   -16.328     .399    -6.821     .145  -.055600   .202600  -.3890300   -16.400    -7.400  
+93 626 49164.00 I  -.056273  .000190   .204479  .000159  I -.3911756  .0000087  2.3215 0.0057  I   -16.764     .420    -6.642     .134  -.056600   .203800  -.3913600   -16.800    -7.200  
+93 627 49165.00 I  -.057333  .000176   .205615  .000149  I -.3934676  .0000075  2.2464 0.0057  I   -17.057     .286    -6.586     .298  -.057700   .204800  -.3936400   -17.200    -7.100  
+93 628 49166.00 I  -.058551  .000209   .206700  .000193  I -.3956403  .0000073  2.0868 0.0054  I   -17.229     .138    -6.567     .298  -.058800   .205900  -.3958000   -17.700    -7.000  
+93 629 49167.00 I  -.059828  .000181   .207760  .000201  I -.3976239  .0000077  1.8751 0.0050  I   -17.427     .138    -6.535     .298  -.060000   .206900  -.3977900   -18.100    -7.100  
+93 630 49168.00 I  -.061112  .000200   .208846  .000269  I -.3993921  .0000069  1.6680 0.0055  I   -17.718     .182    -6.527     .298  -.061200   .208100  -.3995700   -18.300    -7.100  
+93 7 1 49169.00 I  -.062437  .000190   .210047  .000264  I  .5990240  .0000079  1.5090 0.0053  I   -18.047     .182    -6.655     .298  -.062300   .209300   .5988300   -18.400    -7.200  
+93 7 2 49170.00 I  -.063739  .000190   .211328  .000263  I  .5975692  .0000080  1.4121 0.0050  I   -18.203     .182    -6.887     .298  -.063500   .210600   .5974000   -18.400    -7.300  
+93 7 3 49171.00 I  -.064926  .000177   .212596  .000240  I  .5961771  .0000060  1.3833 0.0058  I   -18.031     .105    -7.056     .298  -.064700   .211800   .5960200   -18.200    -7.300  
+93 7 4 49172.00 I  -.065948  .000087   .213794  .000140  I  .5947836  .0000084  1.4115 0.0050  I   -17.620     .791    -7.038     .298  -.065800   .213000   .5946400   -17.900    -7.300  
+93 7 5 49173.00 I  -.066764  .000179   .214898  .000198  I  .5933425  .0000079  1.4745 0.0080  I   -17.220     .791    -6.855     .187  -.066800   .214100   .5932100   -17.800    -7.300  
+93 7 6 49174.00 I  -.067351  .000224   .215906  .000200  I  .5918315  .0000136  1.5469 0.0081  I   -17.022     .107    -6.637     .209  -.067600   .215200   .5917300   -17.700    -7.300  
+93 7 7 49175.00 I  -.067751  .000238   .216822  .000198  I  .5902532  .0000142  1.6057 0.0102  I   -17.051     .151    -6.473     .171  -.068100   .216100   .5901800   -17.700    -7.200  
+93 7 8 49176.00 I  -.068022  .000234   .217671  .000199  I  .5886301  .0000151  1.6344 0.0108  I   -17.247     .151    -6.350     .171  -.068300   .216800   .5885900   -17.900    -7.100  
+93 7 9 49177.00 I  -.068127  .000242   .218538  .000195  I  .5869959  .0000163  1.6288 0.0107  I   -17.535     .151    -6.400     .171  -.068400   .217600   .5869700   -18.400    -6.900  
+93 710 49178.00 I  -.068091  .000226   .219466  .000175  I  .5853818  .0000151  1.5955 0.0113  I   -17.884     .151    -6.558     .171  -.068500   .218600   .5853600   -18.800    -6.900  
+93 711 49179.00 I  -.068029  .000196   .220478  .000137  I  .5838113  .0000157  1.5430 0.0093  I   -18.225     .190    -6.700     .142  -.068500   .219600   .5837500   -19.300    -6.900  
+93 712 49180.00 I  -.068167  .000183   .221597  .000096  I  .5822997  .0000108  1.4789 0.0091  I   -18.552     .252    -6.842     .298  -.068700   .220900   .5821800   -19.800    -6.900  
+93 713 49181.00 I  -.068638  .000162   .222851  .000095  I  .5808530  .0000093  1.4162 0.0072  I   -18.970     .295    -7.001     .140  -.069000   .222300   .5806900   -20.100    -7.000  
+93 714 49182.00 I  -.069396  .000163   .224261  .000143  I  .5794633  .0000096  1.3654 0.0067  I   -19.529     .256    -7.099     .205  -.069500   .223800   .5792900   -20.300    -7.100  
+93 715 49183.00 I  -.070293  .000161   .225760  .000142  I  .5781232  .0000096  1.3117 0.0068  I   -20.135     .259    -7.114     .203  -.070200   .225100   .5779600   -20.400    -7.300  
+93 716 49184.00 I  -.071136  .000161   .227328  .000144  I  .5768387  .0000096  1.2618 0.0069  I   -20.478     .259    -7.131     .203  -.071100   .226600   .5767200   -20.300    -7.400  
+93 717 49185.00 I  -.071804  .000215   .228951  .000177  I  .5755861  .0000099  1.2525 0.0088  I   -20.439     .271    -7.089     .222  -.071900   .228000   .5755000   -20.200    -7.500  
+93 718 49186.00 I  -.072163  .000256   .230617  .000215  I  .5743088  .0000147  1.3178 0.0070  I   -20.201     .339    -7.122     .204  -.072400   .229700   .5742500   -20.100    -7.500  
+93 719 49187.00 I  -.072376  .000219   .232328  .000235  I  .5729212  .0000100  1.4709 0.0088  I   -19.960     .315    -7.267     .179  -.072800   .231500   .5728500   -20.100    -7.500  
+93 720 49188.00 I  -.072659  .000216   .234071  .000224  I  .5713467  .0000096  1.6847 0.0062  I   -19.857     .317    -7.238     .102  -.073100   .233300   .5712500   -20.100    -7.400  
+93 721 49189.00 I  -.073162  .000228   .235815  .000201  I  .5695413  .0000074  1.9299 0.0060  I   -20.031     .327    -6.900     .298  -.073400   .235100   .5694000   -20.300    -7.300  
+93 722 49190.00 I  -.073761  .000231   .237549  .000190  I  .5674973  .0000071  2.1440 0.0055  I   -20.523     .327    -6.462     .298  -.073800   .236700   .5673200   -20.700    -7.100  
+93 723 49191.00 I  -.074331  .000234   .239305  .000193  I  .5652901  .0000082  2.2478 0.0059  I   -21.227     .327    -6.173     .298  -.074300   .238300   .5651100   -21.100    -7.000  
+93 724 49192.00 I  -.074925  .000150   .241023  .000158  I  .5630454  .0000093  2.2204 0.0103  I   -21.856     .147    -6.120     .298  -.074900   .239900   .5628700   -21.500    -7.000  
+93 725 49193.00 I  -.075668  .000146   .242640  .000135  I  .5608853  .0000190  2.0849 0.0133  I   -22.149     .791    -6.240     .298  -.075600   .241500   .5607100   -22.000    -6.900  
+93 726 49194.00 I  -.076631  .000150   .244148  .000158  I  .5588963  .0000250  1.8872 0.0128  I   -22.081     .791    -6.417     .298  -.076500   .243100   .5587400   -22.400    -7.000  
+93 727 49195.00 I  -.077778  .000133   .245624  .000153  I  .5571142  .0000171  1.6798 0.0151  I   -21.870     .159    -6.574     .115  -.077700   .244800   .5569700   -22.500    -7.000  
+93 728 49196.00 I  -.078981  .000129   .247191  .000156  I  .5555234  .0000171  1.5125 0.0119  I   -21.791     .186    -6.700     .121  -.078900   .246600   .5553900   -22.600    -7.200  
+93 729 49197.00 I  -.080118  .000138   .248935  .000159  I  .5540600  .0000166  1.4327 0.0118  I   -21.953     .186    -6.824     .121  -.080000   .248400   .5539300   -22.500    -7.200  
+93 730 49198.00 I  -.081227  .000132   .250760  .000163  I  .5526319  .0000163  1.4312 0.0115  I   -22.231     .186    -7.024     .121  -.081100   .250200   .5525200   -22.400    -7.300  
+93 731 49199.00 I  -.082366  .000211   .252510  .000201  I  .5511802  .0000159  1.4819 0.0099  I   -22.339     .186    -7.165     .121  -.082200   .252100   .5510800   -22.100    -7.300  
+93 8 1 49200.00 I  -.083461  .000233   .254212  .000223  I  .5496530  .0000113  1.5778 0.0093  I   -22.196     .206    -7.121     .122  -.083200   .253900   .5495600   -22.000    -7.300  
+93 8 2 49201.00 I  -.084451  .000305   .255956  .000259  I  .5480175  .0000096  1.6950 0.0094  I   -21.996     .162    -6.932     .298  -.084100   .255600   .5479300   -21.900    -7.200  
+93 8 3 49202.00 I  -.085351  .000369   .257807  .000301  I  .5462633  .0000149  1.8114 0.0088  I   -21.952     .791    -6.722     .298  -.085100   .257300   .5461500   -22.000    -7.000  
+93 8 4 49203.00 I  -.086260  .000372   .259752  .000304  I  .5443991  .0000147  1.9146 0.0105  I   -22.146     .134    -6.650     .298  -.086300   .259100   .5442500   -22.300    -6.900  
+93 8 5 49204.00 I  -.087499  .000376   .261732  .000301  I  .5424427  .0000147  1.9919 0.0104  I   -22.525     .134    -6.750     .298  -.087800   .261100   .5422800   -22.700    -6.900  
+93 8 6 49205.00 I  -.089023  .000378   .263722  .000271  I  .5404353  .0000146  2.0093 0.0118  I   -22.986     .134    -6.888     .298  -.089300   .263000   .5402800   -22.600    -6.700  
+93 8 7 49206.00 I  -.090640  .000330   .265695  .000231  I  .5384455  .0000185  1.9624 0.0138  I   -23.322     .124    -6.985     .298  -.090700   .265000   .5382800   -23.000    -6.800  
+93 8 8 49207.00 I  -.092153  .000247   .267658  .000183  I  .5365244  .0000235  1.8739 0.0165  I   -23.344     .279    -7.016     .298  -.092100   .267000   .5363500   -23.500    -6.900  
+93 8 9 49208.00 I  -.093437  .000225   .269647  .000135  I  .5347066  .0000273  1.7586 0.0187  I   -23.125     .326    -7.016     .298  -.093100   .268900   .5345300   -23.800    -7.200  
+93 810 49209.00 I  -.094417  .000248   .271698  .000134  I  .5330118  .0000291  1.6295 0.0204  I   -23.045     .316    -7.069     .298  -.094100   .270900   .5328700   -24.100    -7.400  
+93 811 49210.00 I  -.095113  .000241   .273838  .000134  I  .5314421  .0000302  1.5168 0.0224  I   -23.423     .316    -7.159     .298  -.094700   .273000   .5313200   -24.200    -7.700  
+93 812 49211.00 I  -.095676  .000242   .276076  .000128  I  .5299608  .0000340  1.4562 0.0212  I   -24.027     .316    -7.143     .298  -.095300   .275200   .5298800   -24.200    -8.000  
+93 813 49212.00 I  -.096200  .000228   .278363  .000122  I  .5285077  .0000299  1.4617 0.0216  I   -24.426     .316    -7.005     .298  -.095700   .277400   .5284700   -24.100    -8.100  
+93 814 49213.00 I  -.096752  .000187   .280621  .000106  I  .5270114  .0000266  1.5454 0.0185  I   -24.397     .260    -6.890     .298  -.096300   .279500   .5270100   -23.900    -8.200  
+93 815 49214.00 I  -.097412  .000160   .282790  .000094  I  .5253905  .0000217  1.7083 0.0186  I   -24.072     .110    -6.957     .298  -.097000   .281600   .5254000   -23.800    -8.200  
+93 816 49215.00 I  -.098178  .000209   .284807  .000167  I  .5235760  .0000260  1.9276 0.0133  I   -23.742     .309    -7.175     .167  -.097800   .283500   .5235700   -23.700    -8.100  
+93 817 49216.00 I  -.098964  .000158   .286624  .000135  I  .5215295  .0000155  2.1641 0.0144  I   -23.598     .444    -7.312     .185  -.098800   .285400   .5214800   -23.700    -8.100  
+93 818 49217.00 I  -.099720  .000155   .288258  .000134  I  .5192544  .0000125  2.3806 0.0102  I   -23.687     .406    -7.198     .169  -.099800   .287100   .5191600   -23.900    -7.900  
+93 819 49218.00 I  -.100408  .000153   .289796  .000136  I  .5167887  .0000134  2.5358 0.0090  I   -23.953     .406    -6.829     .169  -.100700   .288800   .5166500   -24.200    -7.700  
+93 820 49219.00 I  -.101032  .000140   .291317  .000134  I  .5142204  .0000129  2.5781 0.0093  I   -24.412     .406    -6.574     .169  -.101400   .290500   .5140600   -24.600    -7.600  
+93 821 49220.00 I  -.101549  .000162   .292889  .000180  I  .5116758  .0000128  2.4904 0.0095  I   -24.882     .468    -6.507     .190  -.101800   .292100   .5115200   -25.000    -7.500  
+93 822 49221.00 I  -.101885  .000154   .294559  .000187  I  .5092718  .0000140  2.3059 0.0082  I   -25.156     .466    -6.484     .157  -.102100   .293700   .5091100   -25.300    -7.400  
+93 823 49222.00 I  -.102012  .000155   .296341  .000245  I  .5070777  .0000102  2.0803 0.0109  I   -25.153     .330    -6.494     .109  -.102100   .295500   .5069000   -25.500    -7.400  
+93 824 49223.00 I  -.101942  .000202   .298233  .000312  I  .5051082  .0000166  1.8639 0.0091  I   -24.816     .777    -6.634     .290  -.102000   .297300   .5049300   -25.500    -7.300  
+93 825 49224.00 I  -.101719  .000205   .300269  .000311  I  .5033333  .0000151  1.6974 0.0109  I   -24.203     .777    -6.953     .290  -.101700   .299400   .5031500   -25.400    -7.400  
+93 826 49225.00 I  -.101402  .000213   .302496  .000320  I  .5016905  .0000141  1.5997 0.0102  I   -23.690     .777    -7.229     .290  -.101500   .301700   .5015000   -25.100    -7.400  
+93 827 49226.00 I  -.101072  .000241   .304851  .000309  I  .5001094  .0000138  1.5756 0.0104  I   -23.590     .777    -7.299     .290  -.101100   .304100   .4999300   -24.700    -7.300  
+93 828 49227.00 I  -.100733  .000300   .307233  .000306  I  .4985168  .0000152  1.6191 0.0107  I   -23.844     .862    -7.191     .328  -.100800   .306500   .4983600   -24.400    -7.200  
+93 829 49228.00 I  -.100362  .000302   .309577  .000261  I  .4968559  .0000163  1.7086 0.0103  I   -24.192     .739    -7.003     .368  -.100500   .308900   .4967200   -24.100    -7.100  
+93 830 49229.00 I  -.099972  .000246   .311833  .000224  I  .4950917  .0000140  1.8219 0.0106  I   -24.464     .449    -6.777     .288  -.100100   .311100   .4949600   -24.000    -6.900  
+93 831 49230.00 I  -.099671  .000258   .313947  .000242  I  .4932110  .0000135  1.9384 0.0098  I   -24.611     .449    -6.508     .288  -.099800   .313100   .4930800   -24.000    -6.700  
+93 9 1 49231.00 I  -.099584  .000250   .315892  .000248  I  .4912200  .0000137  2.0395 0.0101  I   -24.610     .446    -6.203     .258  -.099500   .315100   .4910800   -24.200    -6.600  
+93 9 2 49232.00 I  -.099550  .000241   .317749  .000249  I  .4891461  .0000149  2.0983 0.0102  I   -24.508     .446    -6.013     .258  -.099300   .316900   .4890000   -24.500    -6.600  
+93 9 3 49233.00 I  -.099476  .000251   .319598  .000249  I  .4870423  .0000152  2.1003 0.0101  I   -24.446     .446    -6.060     .258  -.099200   .318800   .4868900   -24.800    -6.700  
+93 9 4 49234.00 I  -.099417  .000231   .321454  .000216  I  .4849612  .0000136  2.0551 0.0116  I   -24.520     .567    -6.269     .187  -.099200   .320600   .4847900   -25.200    -6.800  
+93 9 5 49235.00 I  -.099508  .000242   .323283  .000169  I  .4829426  .0000176  1.9783 0.0092  I   -24.583     .317    -6.497     .298  -.099400   .322300   .4827600   -25.600    -6.800  
+93 9 6 49236.00 I  -.099829  .000223   .325038  .000160  I  .4810086  .0000123  1.8895 0.0107  I   -24.514     .261    -6.687     .298  -.099800   .324000   .4808200   -25.600    -7.200  
+93 9 7 49237.00 I  -.100319  .000228   .326663  .000149  I  .4791607  .0000120  1.8095 0.0078  I   -24.510     .791    -6.907     .298  -.100300   .325600   .4789900   -25.600    -7.500  
+93 9 8 49238.00 I  -.100783  .000217   .328160  .000144  I  .4773845  .0000097  1.7449 0.0075  I   -24.824     .791    -7.199     .298  -.100900   .327100   .4772400   -25.400    -7.700  
+93 9 9 49239.00 I  -.101141  .000274   .329689  .000144  I  .4756590  .0000091  1.7159 0.0077  I   -25.359     .791    -7.424     .298  -.101300   .328700   .4755400   -25.100    -7.900  
+93 910 49240.00 I  -.101269  .000250   .331310  .000159  I  .4739299  .0000120  1.7553 0.0072  I   -25.685     .791    -7.440     .298  -.101400   .330300   .4738300   -24.800    -7.900  
+93 911 49241.00 I  -.101089  .000237   .333037  .000156  I  .4721218  .0000111  1.8747 0.0085  I   -25.513     .791    -7.296     .298  -.101200   .332100   .4720200   -24.600    -8.000  
+93 912 49242.00 I  -.100565  .000275   .334903  .000139  I  .4701547  .0000119  2.0716 0.0067  I   -25.009     .791    -7.185     .298  -.100600   .334000   .4700600   -24.300    -7.900  
+93 913 49243.00 I  -.099728  .000235   .336973  .000117  I  .4679612  .0000076  2.3206 0.0079  I   -24.590     .177    -7.190     .298  -.099800   .336100   .4678600   -24.300    -7.700  
+93 914 49244.00 I  -.098778  .000246   .339250  .000156  I  .4655123  .0000103  2.5730 0.0065  I   -24.535     .132    -7.185     .149  -.099000   .338400   .4654000   -24.400    -7.600  
+93 915 49245.00 I  -.097920  .000227   .341636  .000166  I  .4628345  .0000106  2.7673 0.0070  I   -24.830     .122    -7.050     .155  -.098100   .340900   .4627000   -24.800    -7.500  
+93 916 49246.00 I  -.097307  .000211   .344011  .000146  I  .4600161  .0000095  2.8462 0.0072  I   -25.253     .122    -6.860     .155  -.097400   .343300   .4598700   -25.200    -7.300  
+93 917 49247.00 I  -.096914  .000224   .346228  .000166  I  .4571881  .0000097  2.7869 0.0067  I   -25.517     .122    -6.685     .155  -.096900   .345600   .4570400   -25.700    -7.100  
+93 918 49248.00 I  -.096709  .000224   .348151  .000175  I  .4544796  .0000095  2.6157 0.0099  I   -25.554     .122    -6.637     .155  -.096700   .347600   .4543300   -26.200    -7.000  
+93 919 49249.00 I  -.096650  .000225   .349767  .000236  I  .4519736  .0000173  2.3938 0.0065  I   -25.586     .791    -6.634     .183  -.096700   .349400   .4518200   -26.500    -7.100  
+93 920 49250.00 I  -.096757  .000183   .351130  .000192  I  .4496859  .0000089  2.1901 0.0101  I   -25.778     .255    -6.554     .220  -.096800   .350800   .4495400   -26.700    -7.000  
+93 921 49251.00 I  -.096961  .000189   .352364  .000187  I  .4475688  .0000106  2.0603 0.0069  I   -25.920     .481    -6.489     .267  -.097100   .352000   .4474300   -26.700    -7.000  
+93 922 49252.00 I  -.097137  .000166   .353660  .000186  I  .4455356  .0000106  2.0195 0.0076  I   -25.653     .608    -6.490     .265  -.097300   .353200   .4454100   -26.500    -7.100  
+93 923 49253.00 I  -.097226  .000156   .355198  .000177  I  .4435103  .0000108  2.0370 0.0076  I   -24.982     .608    -6.457     .265  -.097300   .354400   .4434000   -26.200    -7.000  
+93 924 49254.00 I  -.097154  .000176   .356944  .000175  I  .4414471  .0000109  2.0979 0.0074  I   -24.493     .608    -6.516     .265  -.097200   .355900   .4413300   -25.800    -7.000  
+93 925 49255.00 I  -.096846  .000165   .358816  .000183  I  .4393042  .0000102  2.1898 0.0125  I   -24.450     .608    -6.519     .265  -.096800   .357700   .4391800   -25.400    -6.900  
+93 926 49256.00 I  -.096384  .000227   .360749  .000236  I  .4370666  .0000226  2.2843 0.0103  I   -24.583     .645    -6.379     .241  -.096400   .359600   .4369400   -25.000    -6.800  
+93 927 49257.00 I  -.095887  .000291   .362707  .000215  I  .4347408  .0000180  2.3632 0.0122  I   -24.665     .687    -6.258     .181  -.095900   .361700   .4346100   -24.800    -6.700  
+93 928 49258.00 I  -.095487  .000216   .364679  .000178  I  .4323504  .0000092  2.4115 0.0100  I   -24.652     .633    -6.169     .163  -.095600   .363900   .4322300   -24.800    -6.500  
+93 929 49259.00 I  -.095344  .000215   .366640  .000172  I  .4299340  .0000089  2.4112 0.0064  I   -24.637     .829    -6.102     .189  -.095400   .366000   .4298200   -24.900    -6.500  
+93 930 49260.00 I  -.095565  .000219   .368552  .000172  I  .4275414  .0000088  2.3708 0.0063  I   -24.633     .829    -6.150     .189  -.095400   .368000   .4274400   -25.100    -6.500  
+9310 1 49261.00 I  -.095760  .000218   .370405  .000176  I  .4251976  .0000088  2.3148 0.0056  I   -24.496     .829    -6.232     .189  -.095400   .369900   .4250900   -25.300    -6.600  
+9310 2 49262.00 I  -.095500  .000226   .372243  .000166  I  .4229157  .0000070  2.2470 0.0061  I   -24.490     .908    -6.368     .207  -.095300   .371700   .4228100   -25.400    -6.700  
+9310 3 49263.00 I  -.094925  .000203   .374109  .000172  I  .4207044  .0000084  2.1768 0.0046  I   -24.506     .986    -6.599     .250  -.094900   .373400   .4206100   -25.400    -6.900  
+9310 4 49264.00 I  -.094225  .000169   .376028  .000164  I  .4185569  .0000061  2.1223 0.0083  I   -24.422     .918    -6.768     .195  -.094300   .375200   .4184500   -25.300    -7.100  
+9310 5 49265.00 I  -.093568  .000206   .377975  .000149  I  .4164492  .0000144  2.0995 0.0088  I   -24.311     .492    -6.860     .298  -.093900   .377100   .4163300   -25.100    -7.300  
+9310 6 49266.00 I  -.093027  .000228   .379897  .000142  I  .4143430  .0000165  2.1215 0.0111  I   -24.354     .530    -6.912     .298  -.093500   .378900   .4142100   -24.600    -7.400  
+9310 7 49267.00 I  -.092536  .000212   .381742  .000134  I  .4121963  .0000169  2.1732 0.0118  I   -24.537     .530    -6.871     .298  -.092800   .380700   .4120700   -24.200    -7.400  
+9310 8 49268.00 I  -.091935  .000197   .383500  .000125  I  .4099864  .0000168  2.2556 0.0113  I   -24.531     .530    -6.708     .298  -.092100   .382500   .4098600   -23.900    -7.400  
+9310 9 49269.00 I  -.091076  .000194   .385238  .000194  I  .4076651  .0000151  2.3978 0.0115  I   -24.249     .530    -6.498     .298  -.091100   .384200   .4075300   -23.500    -7.300  
+931010 49270.00 I  -.089912  .000169   .387006  .000184  I  .4051709  .0000157  2.5995 0.0102  I   -23.848     .538    -6.358     .298  -.089900   .386000   .4050400   -23.400    -7.200  
+931011 49271.00 I  -.088541  .000172   .388858  .000201  I  .4024546  .0000137  2.8358 0.0094  I   -23.561     .363    -6.329     .116  -.088500   .387900   .4023200   -23.400    -6.900  
+931012 49272.00 I  -.087093  .000176   .390877  .000231  I  .3995030  .0000104  3.0611 0.0085  I   -23.573     .294    -6.317     .157  -.087000   .390000   .3993700   -23.600    -6.700  
+931013 49273.00 I  -.085687  .000145   .393093  .000231  I  .3963491  .0000102  3.2365 0.0074  I   -23.811     .318    -6.234     .139  -.085600   .392300   .3962100   -23.900    -6.600  
+931014 49274.00 I  -.084433  .000167   .395382  .000234  I  .3930678  .0000106  3.2986 0.0074  I   -24.012     .318    -6.229     .139  -.084300   .394500   .3929500   -24.200    -6.400  
+931015 49275.00 I  -.083389  .000175   .397498  .000232  I  .3898032  .0000106  3.2069 0.0063  I   -23.934     .318    -6.287     .139  -.083200   .396600   .3897000   -24.600    -6.300  
+931016 49276.00 I  -.082597  .000163   .399324  .000180  I  .3866909  .0000067  3.0044 0.0058  I   -23.489     .352    -6.331     .155  -.082400   .398400   .3865900   -24.800    -6.300  
+931017 49277.00 I  -.082066  .000161   .400879  .000175  I  .3838085  .0000048  2.7596 0.0040  I   -23.037     .383    -6.208     .134  -.081800   .399900   .3837000   -24.900    -6.300  
+931018 49278.00 I  -.081697  .000155   .402206  .000165  I  .3811629  .0000044  2.5414 0.0033  I   -23.032     .313    -5.863     .121  -.081400   .401200   .3810500   -24.800    -6.300  
+931019 49279.00 I  -.081249  .000152   .403378  .000172  I  .3787015  .0000044  2.3959 0.0030  I   -23.383     .248    -5.557     .125  -.081000   .402400   .3785800   -24.600    -6.300  
+931020 49280.00 I  -.080501  .000142   .404517  .000178  I  .3763403  .0000040  2.3427 0.0030  I   -23.567     .248    -5.575     .125  -.080700   .403700   .3762200   -24.200    -6.300  
+931021 49281.00 I  -.079780  .000138   .405850  .000185  I  .3739861  .0000042  2.3794 0.0029  I   -23.227     .248    -5.774     .125  -.080300   .405100   .3738500   -23.600    -6.300  
+931022 49282.00 I  -.079255  .000139   .407354  .000188  I  .3715641  .0000041  2.4684 0.0028  I   -22.639     .248    -5.927     .125  -.079800   .406700   .3714300   -23.000    -6.300  
+931023 49283.00 I  -.078848  .000146   .408918  .000222  I  .3690462  .0000038  2.5669 0.0033  I   -22.252     .116    -5.911     .125  -.079300   .408300   .3689200   -22.500    -6.300  
+931024 49284.00 I  -.078395  .000174   .410502  .000250  I  .3664339  .0000051  2.6548 0.0040  I   -22.117     .791    -5.793     .298  -.078600   .410000   .3663200   -22.100    -6.200  
+931025 49285.00 I  -.077753  .000194   .412128  .000213  I  .3637438  .0000071  2.7212 0.0045  I   -21.998     .158    -5.717     .298  -.077900   .411700   .3636400   -21.900    -6.200  
+931026 49286.00 I  -.076914  .000191   .413836  .000207  I  .3610009  .0000075  2.7592 0.0052  I   -21.814     .158    -5.684     .298  -.077100   .413400   .3609000   -21.900    -6.100  
+931027 49287.00 I  -.076031  .000218   .415576  .000201  I  .3582377  .0000077  2.7601 0.0054  I   -21.706     .158    -5.613     .298  -.076400   .415100   .3581400   -22.000    -6.100  
+931028 49288.00 I  -.075370  .000203   .417185  .000175  I  .3554951  .0000077  2.7178 0.0055  I   -21.801     .158    -5.524     .298  -.075800   .416700   .3554000   -22.300    -6.000  
+931029 49289.00 I  -.074954  .000197   .418609  .000151  I  .3528138  .0000079  2.6404 0.0054  I   -22.010     .158    -5.508     .298  -.075300   .418000   .3527000   -22.500    -5.900  
+931030 49290.00 I  -.074635  .000306   .419832  .000127  I  .3502215  .0000077  2.5413 0.0071  I   -22.205     .184    -5.578     .298  -.074900   .419100   .3500800   -22.700    -6.000  
+931031 49291.00 I  -.074213  .000306   .420862  .000122  I  .3477349  .0000119  2.4312 0.0066  I   -22.328     .136    -5.637     .141  -.074400   .420000   .3475800   -22.700    -6.000  
+9311 1 49292.00 I  -.073492  .000322   .421760  .000135  I  .3453578  .0000106  2.3250 0.0079  I   -22.310     .137    -5.610     .125  -.073600   .420800   .3451900   -22.600    -6.000  
+9311 2 49293.00 I  -.072270  .000336   .422619  .000171  I  .3430772  .0000103  2.2420 0.0075  I   -22.086     .131    -5.541     .113  -.072500   .421500   .3429200   -22.400    -6.100  
+9311 3 49294.00 I  -.070455  .000327   .423522  .000174  I  .3408636  .0000105  2.1897 0.0074  I   -21.692     .131    -5.534     .113  -.071000   .422300   .3407300   -22.000    -6.000  
+9311 4 49295.00 I  -.068458  .000329   .424564  .000177  I  .3386838  .0000105  2.1794 0.0077  I   -21.259     .131    -5.651     .113  -.069100   .423400   .3385700   -21.800    -6.200  
+9311 5 49296.00 I  -.066409  .000337   .425838  .000177  I  .3364799  .0000113  2.2439 0.0078  I   -20.892     .131    -5.748     .113  -.066900   .424700   .3363600   -21.500    -6.000  
+9311 6 49297.00 I  -.064303  .000265   .427336  .000206  I  .3341689  .0000115  2.3895 0.0079  I   -20.567     .125    -5.708     .298  -.064600   .426300   .3340500   -21.200    -5.800  
+9311 7 49298.00 I  -.062188  .000220   .428971  .000218  I  .3316825  .0000109  2.5901 0.0068  I   -20.264     .332    -5.554     .298  -.062300   .428100   .3315700   -21.100    -5.600  
+9311 8 49299.00 I  -.060083  .000174   .430629  .000147  I  .3289829  .0000071  2.8082 0.0064  I   -20.108     .451    -5.336     .298  -.060100   .429800   .3288600   -21.200    -5.400  
+9311 9 49300.00 I  -.058051  .000181   .432202  .000118  I  .3260761  .0000068  2.9964 0.0048  I   -20.288     .451    -5.072     .298  -.058100   .431400   .3259500   -21.400    -5.300  
+931110 49301.00 I  -.056232  .000183   .433615  .000131  I  .3230175  .0000065  3.1022 0.0048  I   -20.836     .451    -4.786     .298  -.056400   .432900   .3229000   -21.600    -5.300  
+931111 49302.00 I  -.054719  .000168   .434848  .000140  I  .3199125  .0000068  3.0854 0.0046  I   -21.361     .451    -4.585     .298  -.054900   .434200   .3197900   -21.800    -5.200  
+931112 49303.00 I  -.053383  .000156   .435925  .000138  I  .3168850  .0000066  2.9536 0.0052  I   -21.490     .451    -4.602     .298  -.053500   .435300   .3167600   -22.000    -5.100  
+931113 49304.00 I  -.052073  .000128   .436873  .000134  I  .3140291  .0000078  2.7505 0.0103  I   -21.142     .446    -4.799     .298  -.052100   .436100   .3139000   -22.000    -4.900  
+931114 49305.00 I  -.050757  .000171   .437726  .000152  I  .3113888  .0000196  2.5331 0.0062  I   -20.615     .791    -4.935     .298  -.050800   .436900   .3112500   -21.800    -4.900  
+931115 49306.00 I  -.049472  .000175   .438542  .000145  I  .3089486  .0000096  2.3585 0.0109  I   -20.322     .791    -4.825     .298  -.049500   .437600   .3088100   -21.500    -4.900  
+931116 49307.00 I  -.048279  .000162   .439395  .000150  I  .3066469  .0000096  2.2587 0.0064  I   -20.309     .791    -4.579     .298  -.048500   .438500   .3065200   -21.000    -4.900  
+931117 49308.00 I  -.047288  .000169   .440342  .000164  I  .3044064  .0000084  2.2332 0.0064  I   -20.213     .791    -4.482     .298  -.047700   .439400   .3042900   -20.400    -4.800  
+931118 49309.00 I  -.046610  .000169   .441395  .000156  I  .3021613  .0000085  2.2653 0.0060  I   -19.782     .791    -4.655     .298  -.047000   .440400   .3020400   -19.900    -4.800  
+931119 49310.00 I  -.046115  .000190   .442500  .000154  I  .2998652  .0000086  2.3293 0.0073  I   -19.221     .791    -4.918     .298  -.046400   .441500   .2997400   -19.400    -4.800  
+931120 49311.00 I  -.045686  .000223   .443609  .000166  I  .2975016  .0000118  2.3966 0.0103  I   -18.897     .791    -5.055     .298  -.045900   .442600   .2973700   -19.000    -4.800  
+931121 49312.00 I  -.045176  .000239   .444669  .000186  I  .2950769  .0000188  2.4494 0.0110  I   -18.869     .791    -5.032     .298  -.045300   .443700   .2949600   -18.800    -4.700  
+931122 49313.00 I  -.044412  .000252   .445671  .000164  I  .2926107  .0000185  2.4787 0.0133  I   -18.924     .791    -4.930     .298  -.044600   .444800   .2925000   -18.800    -4.800  
+931123 49314.00 I  -.043359  .000254   .446638  .000168  I  .2901289  .0000187  2.4798 0.0134  I   -18.947     .791    -4.794     .298  -.043700   .445900   .2900300   -19.000    -4.800  
+931124 49315.00 I  -.042190  .000257   .447596  .000157  I  .2876619  .0000195  2.4483 0.0134  I   -19.013     .791    -4.619     .298  -.042700   .447000   .2875600   -19.200    -4.900  
+931125 49316.00 I  -.041254  .000245   .448548  .000155  I  .2852456  .0000191  2.3768 0.0133  I   -19.151     .791    -4.431     .298  -.041700   .448000   .2851600   -19.500    -4.900  
+931126 49317.00 I  -.040560  .000237   .449434  .000162  I  .2829184  .0000182  2.2751 0.0109  I   -19.261     .791    -4.283     .298  -.040900   .448900   .2828300   -19.800    -4.900  
+931127 49318.00 I  -.039980  .000226   .450216  .000121  I  .2806996  .0000106  2.1610 0.0107  I   -19.290     .791    -4.178     .298  -.040100   .449600   .2806200   -19.800    -4.900  
+931128 49319.00 I  -.039440  .000209   .450907  .000127  I  .2785978  .0000113  2.0429 0.0074  I   -19.308     .143    -4.071     .298  -.039300   .450300   .2785100   -19.800    -4.800  
+931129 49320.00 I  -.038813  .000222   .451524  .000099  I  .2766129  .0000102  1.9275 0.0075  I   -19.336     .219    -3.958     .298  -.038600   .450800   .2765200   -19.500    -4.700  
+931130 49321.00 I  -.037886  .000229   .452118  .000088  I  .2747398  .0000098  1.8211 0.0070  I   -19.259     .191    -3.909     .298  -.037700   .451300   .2746400   -19.200    -4.600  
+9312 1 49322.00 I  -.036491  .000226   .452767  .000087  I  .2729459  .0000095  1.7893 0.0061  I   -18.993     .191    -3.986     .298  -.036600   .451900   .2728200   -18.800    -4.500  
+9312 2 49323.00 I  -.035037  .000221   .453502  .000081  I  .2711337  .0000074  1.8401 0.0060  I   -18.689     .191    -4.162     .298  -.035300   .452600   .2710000   -18.400    -4.400  
+9312 3 49324.00 I  -.033629  .000216   .454320  .000080  I  .2692482  .0000074  1.9434 0.0065  I   -18.501     .191    -4.304     .298  -.033800   .453400   .2690900   -18.300    -4.300  
+9312 4 49325.00 I  -.032184  .000207   .455219  .000093  I  .2672266  .0000108  2.1076 0.0078  I   -18.472     .211    -4.324     .298  -.032100   .454600   .2671000   -18.100    -4.000  
+9312 5 49326.00 I  -.030695  .000292   .456183  .000199  I  .2650228  .0000137  2.3025 0.0078  I   -18.535     .791    -4.221     .298  -.030600   .455600   .2648700   -18.200    -4.000  
+9312 6 49327.00 I  -.029197  .000284   .457189  .000184  I  .2626230  .0000113  2.4933 0.0088  I   -18.592     .791    -4.020     .298  -.029000   .456600   .2624600   -18.400    -4.000  
+9312 7 49328.00 I  -.027689  .000286   .458224  .000189  I  .2600475  .0000110  2.6501 0.0079  I   -18.624     .791    -3.748     .298  -.027600   .457600   .2598800   -18.700    -3.900  
+9312 8 49329.00 I  -.026169  .000294   .459296  .000202  I  .2573503  .0000110  2.7245 0.0080  I   -18.670     .791    -3.454     .298  -.026300   .458600   .2572000   -19.000    -4.000  
+9312 9 49330.00 I  -.024876  .000313   .460349  .000201  I  .2546337  .0000115  2.6932 0.0081  I   -18.735     .791    -3.262     .298  -.025000   .459500   .2545100   -19.300    -4.000  
+931210 49331.00 I  -.023654  .000316   .461368  .000221  I  .2519902  .0000120  2.5829 0.0089  I   -18.736     .791    -3.232     .298  -.023700   .460400   .2518700   -19.400    -4.200  
+931211 49332.00 I  -.022373  .000323   .462389  .000273  I  .2494811  .0000135  2.4331 0.0091  I   -18.590     .791    -3.287     .115  -.022400   .461400   .2493700   -19.300    -4.300  
+931212 49333.00 I  -.021116  .000199   .463459  .000226  I  .2471210  .0000136  2.2924 0.0084  I   -18.334     .791    -3.351     .111  -.021200   .462400   .2470100   -19.100    -4.200  
+931213 49334.00 I  -.019978  .000171   .464619  .000189  I  .2448779  .0000101  2.2070 0.0087  I   -18.112     .791    -3.402     .114  -.020100   .463600   .2447600   -18.700    -4.200  
+931214 49335.00 I  -.018938  .000200   .465875  .000240  I  .2426803  .0000109  2.2019 0.0077  I   -17.959     .791    -3.356     .104  -.019000   .465000   .2425400   -18.100    -4.100  
+931215 49336.00 I  -.017978  .000189   .467130  .000238  I  .2404463  .0000117  2.2803 0.0074  I   -17.747     .791    -3.247     .298  -.017900   .466300   .2403000   -17.600    -3.900  
+931216 49337.00 I  -.016999  .000162   .468233  .000226  I  .2381032  .0000101  2.4082 0.0079  I   -17.388     .791    -3.138     .298  -.017000   .467500   .2379400   -17.000    -3.700  
+931217 49338.00 I  -.015881  .000177   .469162  .000207  I  .2356355  .0000106  2.5194 0.0067  I   -17.021     .791    -3.137     .298  -.016000   .468500   .2354700   -16.800    -3.600  
+931218 49339.00 I  -.014635  .000223   .469955  .000259  I  .2330778  .0000088  2.5903 0.0087  I   -16.866     .791    -3.278     .298  -.014800   .469300   .2329100   -16.700    -3.600  
+931219 49340.00 I  -.013258  .000252   .470683  .000292  I  .2304656  .0000138  2.6290 0.0070  I   -16.964     .148    -3.465     .150  -.013500   .470000   .2303000   -16.800    -3.500  
+931220 49341.00 I  -.011691  .000209   .471433  .000245  I  .2278282  .0000109  2.6425 0.0087  I   -17.184     .133    -3.572     .131  -.011700   .470700   .2276700   -17.100    -3.500  
+931221 49342.00 I  -.009752  .000176   .472284  .000203  I  .2251854  .0000107  2.6415 0.0074  I   -17.423     .152    -3.527     .146  -.009700   .471500   .2250500   -17.500    -3.500  
+931222 49343.00 I  -.007342  .000180   .473280  .000202  I  .2225628  .0000101  2.5875 0.0072  I   -17.655     .152    -3.359     .146  -.007400   .472500   .2224700   -17.900    -3.600  
+931223 49344.00 I  -.004758  .000189   .474365  .000201  I  .2200279  .0000096  2.4810 0.0069  I   -17.813     .152    -3.224     .146  -.005000   .473600   .2199400   -18.300    -3.700  
+931224 49345.00 I  -.002260  .000188   .475357  .000198  I  .2176001  .0000094  2.3767 0.0062  I   -17.835     .152    -3.182     .146  -.002700   .474600   .2175100   -18.600    -3.800  
+931225 49346.00 I  -.000079  .000128   .476105  .000129  I  .2152727  .0000078  2.2779 0.0092  I   -17.752     .791    -3.188     .298  -.000600   .475300   .2151800   -18.700    -3.900  
+931226 49347.00 I   .001645  .000162   .476538  .000152  I  .2130411  .0000158  2.1885 0.0049  I   -17.692     .791    -3.178     .298   .001200   .475800   .2129400   -18.700    -4.000  
+931227 49348.00 I   .002979  .000134   .476647  .000114  I  .2108865  .0000059  2.1265 0.0084  I   -17.717     .149    -3.171     .298   .002600   .475900   .2107800   -18.400    -4.000  
+931228 49349.00 I   .004130  .000125   .476509  .000113  I  .2087733  .0000059  2.1089 0.0043  I   -17.717     .179    -3.245     .298   .003900   .475800   .2086600   -18.100    -3.900  
+931229 49350.00 I   .005326  .000128   .476268  .000110  I  .2066509  .0000063  2.1448 0.0043  I   -17.537     .179    -3.412     .298   .005000   .475600   .2065100   -17.900    -3.800  
+931230 49351.00 I   .006736  .000104   .476099  .000089  I  .2044548  .0000063  2.2673 0.0044  I   -17.187     .179    -3.556     .298   .006200   .475500   .2042600   -17.600    -3.700  
+931231 49352.00 I   .008448  .000106   .476147  .000089  I  .2020948  .0000062  2.4544 0.0046  I   -16.805     .179    -3.580     .298   .007800   .475500   .2018500   -17.300    -3.600  
+94 1 1 49353.00 I   .010488  .000152   .476399  .000131  I  .1995489  .0000067  2.6327 0.0049  I   -16.588     .179    -3.467     .298   .009700   .475600   .1992800   -17.200    -3.400  
+94 1 2 49354.00 I   .012823  .000198   .476816  .000148  I  .1968449  .0000076  2.7646 0.0055  I   -16.618     .204    -3.280     .298   .012000   .476000   .1965900   -17.300    -3.300  
+94 1 3 49355.00 I   .015382  .000219   .477358  .000131  I  .1940457  .0000087  2.8187 0.0056  I   -16.799     .166    -3.093     .104   .014700   .476700   .1938000   -17.500    -3.100  
+94 1 4 49356.00 I   .018130  .000221   .477976  .000131  I  .1912390  .0000082  2.7783 0.0062  I   -17.011     .186    -2.927     .181   .017600   .477300   .1910200   -17.800    -3.100  
+94 1 5 49357.00 I   .021020  .000223   .478639  .000131  I  .1885171  .0000088  2.6539 0.0061  I   -17.249     .252    -2.762     .174   .020600   .478000   .1883300   -18.100    -3.100  
+94 1 6 49358.00 I   .023887  .000233   .479288  .000129  I  .1859460  .0000091  2.4852 0.0064  I   -17.614     .252    -2.611     .174   .023600   .478600   .1857800   -18.500    -3.100  
+94 1 7 49359.00 I   .026706  .000226   .479806  .000138  I  .1835517  .0000092  2.3015 0.0067  I   -18.139     .252    -2.669     .174   .026400   .479000   .1834100   -18.700    -3.200  
+94 1 8 49360.00 I   .029429  .000197   .480131  .000128  I  .1813388  .0000098  2.1303 0.0079  I   -18.653     .252    -2.888     .174   .029100   .479200   .1812100   -18.900    -3.200  
+94 1 9 49361.00 I   .031915  .000185   .480235  .000174  I  .1792719  .0000129  2.0168 0.0065  I   -18.969     .233    -3.116     .165   .031600   .479300   .1791600   -18.900    -3.200  
+94 110 49362.00 I   .034135  .000162   .480135  .000219  I  .1772787  .0000085  1.9828 0.0071  I   -19.013     .210    -3.245     .298   .033900   .479200   .1771700   -18.800    -3.200  
+94 111 49363.00 I   .036207  .000130   .479908  .000191  I  .1752855  .0000061  2.0110 0.0050  I   -18.808     .232    -3.165     .298   .036200   .479000   .1751800   -18.600    -3.200  
+94 112 49364.00 I   .038357  .000117   .479664  .000179  I  .1732344  .0000052  2.1059 0.0039  I   -18.485     .227    -2.917     .298   .038400   .478900   .1731100   -18.400    -3.200  
+94 113 49365.00 I   .040806  .000110   .479395  .000162  I  .1710573  .0000048  2.2498 0.0037  I   -18.199     .270    -2.670     .298   .040800   .478700   .1709500   -18.300    -3.100  
+94 114 49366.00 I   .043564  .000103   .479149  .000151  I  .1687462  .0000052  2.3594 0.0035  I   -18.008     .247    -2.589     .298   .043200   .478700   .1686700   -18.300    -3.000  
+94 115 49367.00 I   .046270  .000103   .479033  .000136  I  .1663712  .0000052  2.3710 0.0037  I   -17.898     .246    -2.705     .298   .045600   .478600   .1663300   -18.400    -3.000  
+94 116 49368.00 I   .048614  .000087   .479092  .000142  I  .1640261  .0000052  2.3168 0.0036  I   -17.900     .257    -2.905     .298   .047900   .478700   .1639700   -18.500    -3.000  
+94 117 49369.00 I   .050696  .000099   .479199  .000136  I  .1617348  .0000049  2.2710 0.0039  I   -18.030     .234    -3.045     .298   .050100   .478700   .1616500   -18.700    -3.000  
+94 118 49370.00 I   .052590  .000105   .479158  .000141  I  .1594831  .0000057  2.2281 0.0037  I   -18.224     .235    -3.039     .108   .052300   .478500   .1593800   -18.900    -3.100  
+94 119 49371.00 I   .054280  .000109   .478859  .000134  I  .1572956  .0000055  2.1342 0.0039  I   -18.379     .208    -2.996     .125   .054300   .478200   .1572000   -19.100    -3.300  
+94 120 49372.00 I   .055909  .000109   .478392  .000172  I  .1552205  .0000054  2.0228 0.0039  I   -18.417     .197    -3.058     .144   .056300   .477600   .1551100   -19.300    -3.500  
+94 121 49373.00 I   .057689  .000106   .477780  .000197  I  .1532311  .0000054  1.9679 0.0036  I   -18.335     .191    -3.289     .146   .058400   .477000   .1531100   -19.400    -3.700  
+94 122 49374.00 I   .059822  .000103   .477049  .000209  I  .1512759  .0000048  1.9394 0.0036  I   -18.256     .163    -3.491     .148   .060500   .476400   .1511700   -19.300    -3.800  
+94 123 49375.00 I   .062349  .000105   .476481  .000219  I  .1493481  .0000047  1.9236 0.0034  I   -18.261     .175    -3.495     .142   .062700   .475900   .1492600   -19.200    -4.100  
+94 124 49376.00 I   .065142  .000101   .476263  .000210  I  .1474173  .0000049  1.9419 0.0034  I   -18.317     .156    -3.434     .132   .065100   .475500   .1473300   -18.800    -4.100  
+94 125 49377.00 I   .067938  .000101   .476205  .000223  I  .1454482  .0000049  2.0084 0.0037  I   -18.314     .133    -3.467     .119   .067700   .475200   .1453500   -18.600    -4.200  
+94 126 49378.00 I   .070662  .000095   .475982  .000238  I  .1433702  .0000055  2.1658 0.0040  I   -18.145     .145    -3.654     .298   .070400   .474900   .1432400   -18.300    -4.100  
+94 127 49379.00 I   .073391  .000096   .475402  .000214  I  .1410971  .0000063  2.3817 0.0045  I   -17.786     .154    -3.898     .298   .073200   .474500   .1409600   -18.100    -4.000  
+94 128 49380.00 I   .076083  .000112   .474665  .000188  I  .1386113  .0000070  2.5843 0.0057  I   -17.406     .163    -3.954     .298   .076000   .473900   .1384800   -18.100    -3.900  
+94 129 49381.00 I   .078769  .000143   .473851  .000179  I  .1359433  .0000094  2.7427 0.0094  I   -17.259     .108    -3.737     .298   .078700   .473200   .1358100   -18.200    -3.700  
+94 130 49382.00 I   .081448  .000162   .472951  .000128  I  .1331513  .0000175  2.8249 0.0075  I   -17.486     .112    -3.416     .298   .081500   .472300   .1330100   -18.500    -3.600  
+94 131 49383.00 I   .084148  .000161   .471983  .000128  I  .1303264  .0000118  2.8085 0.0102  I   -18.006     .244    -3.203     .298   .084200   .471400   .1301900   -18.800    -3.600  
+94 2 1 49384.00 I   .086895  .000155   .471012  .000109  I  .1275677  .0000105  2.6919 0.0078  I   -18.573     .258    -3.170     .298   .086900   .470400   .1274300   -19.200    -3.500  
+94 2 2 49385.00 I   .089671  .000170   .470130  .000132  I  .1249677  .0000101  2.4999 0.0073  I   -19.000     .258    -3.232     .298   .089600   .469600   .1248200   -19.800    -3.400  
+94 2 3 49386.00 I   .092442  .000180   .469412  .000139  I  .1225727  .0000101  2.2935 0.0073  I   -19.285     .258    -3.282     .298   .092300   .468800   .1224200   -20.000    -3.600  
+94 2 4 49387.00 I   .095098  .000180   .468762  .000143  I  .1203722  .0000105  2.1126 0.0074  I   -19.509     .258    -3.312     .298   .095000   .468000   .1202300   -20.100    -3.900  
+94 2 5 49388.00 I   .097505  .000183   .468057  .000150  I  .1183323  .0000108  1.9773 0.0088  I   -19.753     .258    -3.357     .298   .097500   .467300   .1182000   -19.900    -4.100  
+94 2 6 49389.00 I   .099674  .000189   .467277  .000175  I  .1163942  .0000141  1.9123 0.0079  I   -19.927     .195    -3.485     .298   .099900   .466600   .1162700   -19.800    -4.200  
+94 2 7 49390.00 I   .101698  .000179   .466450  .000133  I  .1144788  .0000116  1.9341 0.0092  I   -19.846     .245    -3.683     .298   .102000   .465800   .1143600   -19.400    -4.300  
+94 2 8 49391.00 I   .103663  .000198   .465580  .000108  I  .1125030  .0000118  2.0249 0.0082  I   -19.415     .245    -3.875     .298   .104100   .464900   .1123800   -19.100    -4.400  
+94 2 9 49392.00 I   .105628  .000200   .464636  .000123  I  .1104216  .0000115  2.1377 0.0081  I   -18.747     .309    -3.988     .298   .106100   .464000   .1103000   -18.800    -4.400  
+94 210 49393.00 I   .107685  .000189   .463574  .000119  I  .1082301  .0000111  2.2428 0.0082  I   -18.069     .309    -3.912     .298   .108000   .463000   .1081000   -18.500    -4.400  
+94 211 49394.00 I   .109662  .000184   .462451  .000122  I  .1059464  .0000117  2.3166 0.0069  I   -17.606     .309    -3.787     .298   .109800   .462000   .1058200   -18.400    -4.300  
+94 212 49395.00 I   .111429  .000183   .461331  .000114  I  .1036106  .0000083  2.3494 0.0085  I   -17.486     .380    -3.812     .103   .111500   .460800   .1034800   -18.500    -4.400  
+94 213 49396.00 I   .113118  .000190   .460239  .000142  I  .1012573  .0000124  2.3532 0.0066  I   -17.668     .387    -3.999     .298   .113100   .459700   .1011200   -18.700    -4.500  
+94 214 49397.00 I   .114866  .000193   .459172  .000137  I  .0989138  .0000102  2.3283 0.0079  I   -18.062     .335    -4.224     .298   .114700   .458500   .0987800   -19.100    -4.600  
+94 215 49398.00 I   .116741  .000209   .458065  .000153  I  .0966117  .0000097  2.2704 0.0074  I   -18.546     .267    -4.383     .298   .116500   .457300   .0964700   -19.400    -4.800  
+94 216 49399.00 I   .118747  .000202   .456839  .000140  I  .0943824  .0000107  2.1842 0.0066  I   -18.900     .267    -4.473     .298   .118300   .456100   .0942400   -19.700    -5.000  
+94 217 49400.00 I   .120837  .000212   .455491  .000149  I  .0922419  .0000089  2.1020 0.0075  I   -18.964     .267    -4.576     .298   .120200   .454800   .0920800   -19.900    -5.300  
+94 218 49401.00 I   .122767  .000221   .454064  .000161  I  .0901704  .0000106  2.0437 0.0083  I   -18.792     .267    -4.715     .298   .122200   .453400   .0900000   -20.100    -5.500  
+94 219 49402.00 I   .124543  .000228   .452625  .000170  I  .0881463  .0000139  2.0099 0.0112  I   -18.583     .140    -4.813     .298   .124200   .452000   .0879700   -20.200    -5.700  
+94 220 49403.00 I   .126444  .000223   .451326  .000199  I  .0861344  .0000197  2.0247 0.0107  I   -18.411     .242    -4.749     .298   .126400   .450800   .0859700   -20.000    -5.900  
+94 221 49404.00 I   .128682  .000208   .450311  .000207  I  .0840749  .0000164  2.1054 0.0125  I   -18.227     .226    -4.549     .298   .128800   .449800   .0839300   -19.900    -5.900  
+94 222 49405.00 I   .131239  .000220   .449616  .000208  I  .0819021  .0000155  2.2504 0.0112  I   -18.057     .226    -4.460     .298   .131400   .449100   .0817700   -19.700    -5.900  
+94 223 49406.00 I   .133898  .000213   .449154  .000213  I  .0795498  .0000154  2.4681 0.0103  I   -17.986     .226    -4.706     .298   .134000   .448600   .0794300   -19.700    -5.900  
+94 224 49407.00 I   .136340  .000246   .448793  .000270  I  .0769470  .0000135  2.7425 0.0095  I   -17.926     .226    -5.112     .298   .136400   .448300   .0768400   -19.500    -5.700  
+94 225 49408.00 I   .138371  .000251   .448401  .000257  I  .0740647  .0000111  3.0174 0.0073  I   -17.790     .226    -5.340     .298   .138400   .447900   .0739700   -19.400    -5.600  
+94 226 49409.00 I   .139965  .000254   .447866  .000290  I  .0709302  .0000055  3.2385 0.0084  I   -17.651     .155    -5.222     .298   .140100   .447400   .0708400   -19.300    -5.500  
+94 227 49410.00 I   .141211  .000249   .447137  .000267  I  .0676205  .0000126  3.3612 0.0049  I   -17.683     .791    -4.921     .298   .141400   .446600   .0675100   -19.400    -5.300  
+94 228 49411.00 I   .142256  .000207   .446175  .000230  I  .0642490  .0000081  3.3603 0.0074  I   -17.934     .791    -4.737     .298   .142500   .445600   .0641200   -19.500    -5.300  
+94 3 1 49412.00 I   .143219  .000197   .444899  .000224  I  .0609375  .0000078  3.2465 0.0062  I   -18.226     .791    -4.796     .298   .143400   .444200   .0607900   -19.600    -5.400  
+94 3 2 49413.00 I   .144190  .000180   .443314  .000229  I  .0577774  .0000095  3.0684 0.0069  I   -18.342     .791    -4.976     .298   .144300   .442700   .0576300   -19.500    -5.500  
+94 3 3 49414.00 I   .145240  .000149   .441509  .000185  I  .0548062  .0000113  2.8736 0.0075  I   -18.225     .791    -5.096     .298   .145200   .440900   .0546700   -19.600    -5.600  
+94 3 4 49415.00 I   .146278  .000148   .439625  .000149  I  .0520263  .0000117  2.6904 0.0080  I   -18.003     .791    -5.108     .298   .146200   .439000   .0519500   -19.400    -5.800  
+94 3 5 49416.00 I   .147211  .000154   .437777  .000188  I  .0494116  .0000113  2.5485 0.0092  I   -17.834     .791    -5.075     .298   .147300   .437200   .0493600   -19.300    -6.000  
+94 3 6 49417.00 I   .148071  .000135   .436001  .000188  I  .0469051  .0000143  2.4792 0.0080  I   -17.782     .274    -5.066     .159   .148200   .435400   .0468600   -19.100    -6.000  
+94 3 7 49418.00 I   .148970  .000145   .434307  .000171  I  .0444290  .0000112  2.4825 0.0087  I   -17.814     .196    -5.126     .158   .149100   .433800   .0443700   -18.900    -6.100  
+94 3 8 49419.00 I   .149918  .000151   .432679  .000160  I  .0419256  .0000098  2.5294 0.0072  I   -17.885     .196    -5.293     .158   .150000   .432200   .0418500   -18.700    -6.100  
+94 3 9 49420.00 I   .150889  .000156   .431067  .000164  I  .0393615  .0000092  2.6025 0.0066  I   -18.033     .277    -5.567     .129   .150900   .430500   .0392600   -18.600    -6.100  
+94 310 49421.00 I   .151945  .000155   .429415  .000167  I  .0367221  .0000089  2.6713 0.0064  I   -18.286     .277    -5.728     .129   .151800   .428800   .0366200   -18.400    -6.100  
+94 311 49422.00 I   .153095  .000168   .427708  .000171  I  .0340371  .0000090  2.6857 0.0056  I   -18.576     .277    -5.730     .129   .153000   .427100   .0339500   -18.200    -6.000  
+94 312 49423.00 I   .154380  .000191   .425975  .000140  I  .0313733  .0000069  2.6323 0.0063  I   -18.702     .279    -5.692     .111   .154200   .425200   .0312900   -18.100    -6.000  
+94 313 49424.00 I   .155842  .000215   .424297  .000208  I  .0287840  .0000089  2.5445 0.0070  I   -18.580     .284    -5.719     .298   .155700   .423500   .0286800   -18.100    -6.200  
+94 314 49425.00 I   .157393  .000201   .422761  .000197  I  .0262872  .0000121  2.4480 0.0074  I   -18.319     .347    -5.848     .298   .157200   .421800   .0261500   -18.100    -6.300  
+94 315 49426.00 I   .158931  .000204   .421377  .000203  I  .0238883  .0000117  2.3507 0.0085  I   -18.031     .284    -6.038     .298   .158800   .420400   .0237300   -18.100    -6.500  
+94 316 49427.00 I   .160366  .000209   .420007  .000205  I  .0215796  .0000119  2.2720 0.0084  I   -17.744     .392    -6.239     .298   .160300   .419000   .0214200   -18.000    -6.700  
+94 317 49428.00 I   .161723  .000212   .418500  .000199  I  .0193435  .0000120  2.1956 0.0085  I   -17.558     .392    -6.430     .298   .161500   .417700   .0192100   -17.900    -7.000  
+94 318 49429.00 I   .162780  .000198   .416933  .000193  I  .0171856  .0000120  2.1275 0.0091  I   -17.573     .392    -6.625     .298   .162500   .416200   .0170800   -17.700    -7.100  
+94 319 49430.00 I   .163469  .000193   .415334  .000235  I  .0150740  .0000137  2.1027 0.0106  I   -17.734     .436    -6.821     .298   .163100   .414700   .0149800   -17.600    -7.300  
+94 320 49431.00 I   .163895  .000179   .413673  .000201  I  .0129631  .0000174  2.1291 0.0075  I   -17.862     .433    -6.888     .298   .163500   .413100   .0128700   -17.200    -7.300  
+94 321 49432.00 I   .164155  .000146   .411919  .000205  I  .0107951  .0000063  2.2177 0.0092  I   -17.766     .482    -6.727     .298   .163800   .411400   .0107000   -16.900    -7.300  
+94 322 49433.00 I   .164345  .000159   .410066  .000233  I  .0085070  .0000061  2.3686 0.0043  I   -17.460     .384    -6.496     .298   .164200   .409600   .0084200   -16.800    -7.300  
+94 323 49434.00 I   .164664  .000150   .408178  .000255  I  .0060379  .0000058  2.5797 0.0042  I   -17.222     .314    -6.469     .298   .164700   .407700   .0059400   -16.600    -7.200  
+94 324 49435.00 I   .165351  .000149   .406360  .000248  I  .0033379  .0000058  2.8199 0.0041  I   -17.252     .314    -6.681     .298   .165600   .405800   .0032300   -16.600    -7.000  
+94 325 49436.00 I   .166358  .000135   .404603  .000240  I  .0004004  .0000059  3.0532 0.0041  I   -17.354     .314    -6.848     .298   .166500   .403900   .0002800   -16.800    -6.900  
+94 326 49437.00 I   .167468  .000155   .402813  .000235  I -.0027475  .0000059  3.2234 0.0052  I   -17.361     .314    -6.747     .298   .167500   .402000  -.0028700   -17.100    -6.800  
+94 327 49438.00 I   .168527  .000149   .400966  .000268  I -.0060032  .0000086  3.2638 0.0054  I   -17.347     .251    -6.479     .298   .168400   .400100  -.0061200   -17.500    -6.900  
+94 328 49439.00 I   .169439  .000181   .399053  .000224  I -.0092285  .0000090  3.1643 0.0060  I   -17.417     .791    -6.336     .298   .169300   .398200  -.0093300   -17.800    -6.900  
+94 329 49440.00 I   .170178  .000194   .397074  .000207  I -.0122952  .0000084  2.9551 0.0063  I   -17.544     .791    -6.472     .298   .170300   .396300  -.0124100   -18.100    -7.100  
+94 330 49441.00 I   .170917  .000197   .395070  .000188  I -.0151299  .0000089  2.7197 0.0062  I   -17.627     .791    -6.763     .298   .171400   .394400  -.0152600   -18.200    -7.400  
+94 331 49442.00 I   .172023  .000217   .393179  .000204  I -.0177504  .0000092  2.5319 0.0063  I   -17.603     .791    -7.030     .298   .172600   .392600  -.0178900   -18.300    -7.600  
+94 4 1 49443.00 I   .173479  .000204   .391451  .000220  I -.0202159  .0000088  2.4109 0.0059  I   -17.464     .791    -7.234     .298   .174000   .390800  -.0203400   -18.200    -7.800  
+94 4 2 49444.00 I   .175073  .000196   .389822  .000165  I -.0225980  .0000073  2.3673 0.0081  I   -17.254     .791    -7.413     .298   .175300   .389100  -.0227200   -17.900    -7.900  
+94 4 3 49445.00 I   .176575  .000191   .388195  .000209  I -.0249739  .0000137  2.3938 0.0056  I   -17.090     .791    -7.541     .298   .176500   .387500  -.0251000   -17.700    -8.000  
+94 4 4 49446.00 I   .177850  .000184   .386499  .000206  I -.0273956  .0000086  2.4502 0.0078  I   -17.076     .791    -7.561     .298   .177700   .385800  -.0274900   -17.500    -8.100  
+94 4 5 49447.00 I   .178909  .000184   .384736  .000239  I -.0298724  .0000075  2.5010 0.0054  I   -17.153     .791    -7.499     .298   .178800   .384200  -.0299700   -17.500    -8.000  
+94 4 6 49448.00 I   .179853  .000164   .382938  .000221  I -.0323972  .0000064  2.5505 0.0049  I   -17.148     .791    -7.436     .298   .179800   .382400  -.0325000   -17.500    -7.900  
+94 4 7 49449.00 I   .180783  .000174   .381137  .000230  I -.0349731  .0000064  2.5993 0.0038  I   -17.013     .791    -7.383     .298   .180800   .380700  -.0350900   -17.500    -7.900  
+94 4 8 49450.00 I   .181636  .000172   .379343  .000241  I -.0375888  .0000042  2.6273 0.0039  I   -16.894     .791    -7.280     .298   .181700   .379000  -.0377100   -17.400    -7.800  
+94 4 9 49451.00 I   .182329  .000218   .377572  .000239  I -.0402176  .0000044  2.6249 0.0031  I   -16.937     .791    -7.124     .298   .182400   .377300  -.0403500   -17.400    -7.800  
+94 410 49452.00 I   .182867  .000240   .375874  .000405  I -.0428274  .0000045  2.5890 0.0036  I   -17.124     .791    -7.045     .298   .183000   .375600  -.0429600   -17.500    -7.800  
+94 411 49453.00 I   .183229  .000219   .374257  .000344  I -.0453837  .0000056  2.5175 0.0049  I   -17.334     .791    -7.171     .298   .183300   .373900  -.0455200   -17.500    -7.900  
+94 412 49454.00 I   .183341  .000212   .372623  .000339  I -.0478529  .0000087  2.4177 0.0055  I   -17.448     .791    -7.473     .298   .183400   .372200  -.0479800   -17.400    -8.000  
+94 413 49455.00 I   .183248  .000230   .370836  .000334  I -.0502164  .0000094  2.3099 0.0064  I   -17.371     .791    -7.792     .298   .183300   .370400  -.0503400   -17.200    -8.200  
+94 414 49456.00 I   .183142  .000236   .368777  .000321  I -.0524756  .0000093  2.2107 0.0066  I   -17.103     .791    -8.018     .298   .183100   .368500  -.0525900   -17.000    -8.400  
+94 415 49457.00 I   .183089  .000200   .366542  .000316  I -.0546441  .0000092  2.1304 0.0072  I   -16.833     .791    -8.179     .298   .183100   .366300  -.0547500   -16.800    -8.500  
+94 416 49458.00 I   .183155  .000188   .364319  .000350  I -.0567524  .0000109  2.0982 0.0069  I   -16.769     .791    -8.318     .298   .183200   .364000  -.0568600   -16.500    -8.400  
+94 417 49459.00 I   .183364  .000191   .362224  .000243  I -.0588633  .0000104  2.1345 0.0064  I   -16.844     .791    -8.365     .298   .183400   .361800  -.0589700   -16.300    -8.400  
+94 418 49460.00 I   .183638  .000193   .360311  .000201  I -.0610425  .0000068  2.2343 0.0062  I   -16.757     .791    -8.215     .298   .183600   .359700  -.0611500   -16.000    -8.300  
+94 419 49461.00 I   .183851  .000186   .358589  .000193  I -.0633504  .0000066  2.3898 0.0050  I   -16.367     .791    -7.933     .298   .183800   .357800  -.0634500   -15.900    -8.200  
+94 420 49462.00 I   .183927  .000170   .357041  .000191  I -.0658290  .0000072  2.5660 0.0051  I   -15.908     .791    -7.744     .298   .184000   .356200  -.0659100   -16.000    -8.100  
+94 421 49463.00 I   .183893  .000169   .355593  .000196  I -.0684853  .0000079  2.7509 0.0050  I   -15.709     .791    -7.768     .298   .183900   .354800  -.0685800   -16.100    -7.900  
+94 422 49464.00 I   .183783  .000172   .354125  .000143  I -.0713343  .0000070  2.9463 0.0054  I   -15.837     .791    -7.849     .298   .183800   .353400  -.0714500   -16.400    -7.700  
+94 423 49465.00 I   .183580  .000185   .352535  .000137  I -.0743644  .0000073  3.1014 0.0068  I   -16.130     .791    -7.766     .298   .183600   .351900  -.0744800   -16.600    -7.700  
+94 424 49466.00 I   .183329  .000200   .350751  .000180  I -.0775031  .0000117  3.1546 0.0068  I   -16.435     .791    -7.537     .298   .183300   .350200  -.0776000   -17.000    -7.700  
+94 425 49467.00 I   .183076  .000199   .348730  .000149  I -.0806300  .0000114  3.0769 0.0077  I   -16.680     .119    -7.404     .298   .183000   .348200  -.0806900   -17.300    -7.800  
+94 426 49468.00 I   .182904  .000177   .346445  .000155  I -.0836211  .0000101  2.8917 0.0075  I   -16.834     .272    -7.499     .298   .182800   .345900  -.0836600   -17.500    -7.900  
+94 427 49469.00 I   .182861  .000178   .343925  .000163  I -.0864020  .0000098  2.6725 0.0071  I   -16.894     .259    -7.699     .205   .182700   .343200  -.0864600   -17.600    -8.200  
+94 428 49470.00 I   .182883  .000173   .341266  .000166  I -.0889845  .0000099  2.5087 0.0072  I   -16.887     .259    -7.817     .205   .182700   .340400  -.0890600   -17.500    -8.300  
+94 429 49471.00 I   .183010  .000168   .338645  .000153  I -.0914518  .0000106  2.4420 0.0073  I   -16.831     .259    -7.940     .205   .183000   .337700  -.0915600   -17.400    -8.500  
+94 430 49472.00 I   .183316  .000151   .336208  .000152  I -.0938945  .0000106  2.4531 0.0074  I   -16.631     .280    -8.134     .224   .183300   .335200  -.0940100   -17.200    -8.600  
+94 5 1 49473.00 I   .183751  .000175   .333988  .000186  I -.0963720  .0000102  2.5061 0.0069  I   -16.392     .305    -8.311     .247   .183800   .332900  -.0964900   -16.900    -8.600  
+94 5 2 49474.00 I   .184268  .000144   .331918  .000169  I -.0989092  .0000089  2.5664 0.0067  I   -16.413     .212    -8.351     .240   .184300   .330900  -.0990100   -16.900    -8.600  
+94 5 3 49475.00 I   .184802  .000148   .329910  .000163  I -.1014984  .0000088  2.6074 0.0062  I   -16.728     .791    -8.242     .298   .184400   .329300  -.1016100   -17.000    -8.700  
+94 5 4 49476.00 I   .185248  .000158   .327875  .000171  I -.1041177  .0000087  2.6293 0.0056  I   -16.996     .791    -8.111     .298   .184700   .327300  -.1042400   -17.200    -8.600  
+94 5 5 49477.00 I   .185493  .000164   .325771  .000175  I -.1067517  .0000070  2.6351 0.0056  I   -16.932     .791    -8.031     .298   .184900   .325100  -.1068800   -17.400    -8.500  
+94 5 6 49478.00 I   .185617  .000167   .323560  .000181  I -.1093766  .0000070  2.6069 0.0045  I   -16.682     .791    -7.929     .298   .185200   .322800  -.1095100   -17.600    -8.400  
+94 5 7 49479.00 I   .185787  .000150   .321227  .000153  I -.1119535  .0000056  2.5427 0.0044  I   -16.611     .791    -7.751     .298   .185500   .320400  -.1120800   -17.900    -8.400  
+94 5 8 49480.00 I   .186127  .000153   .318824  .000169  I -.1144563  .0000053  2.4613 0.0039  I   -16.842     .791    -7.619     .298   .186000   .318000  -.1145800   -18.000    -8.400  
+94 5 9 49481.00 I   .186656  .000118   .316455  .000178  I -.1168720  .0000053  2.3677 0.0044  I   -17.177     .282    -7.719     .298   .186700   .315600  -.1169900   -18.200    -8.600  
+94 510 49482.00 I   .187338  .000130   .314204  .000206  I -.1191899  .0000070  2.2686 0.0044  I   -17.383     .612    -8.067     .147   .187400   .313400  -.1193000   -18.200    -8.800  
+94 511 49483.00 I   .188084  .000125   .312099  .000195  I -.1214190  .0000070  2.1990 0.0050  I   -17.392     .548    -8.476     .137   .188200   .311300  -.1215400   -18.100    -8.900  
+94 512 49484.00 I   .188701  .000131   .310098  .000190  I -.1236014  .0000071  2.1700 0.0050  I   -17.311     .548    -8.763     .137   .188700   .309300  -.1237200   -17.800    -9.100  
+94 513 49485.00 I   .189149  .000139   .308105  .000196  I -.1257618  .0000072  2.1493 0.0054  I   -17.209     .548    -8.917     .137   .189000   .307400  -.1258600   -17.400    -9.200  
+94 514 49486.00 I   .189254  .000145   .306043  .000202  I -.1279101  .0000081  2.1590 0.0058  I   -17.242     .611    -8.959     .153   .189000   .305400  -.1280100   -17.100    -9.300  
+94 515 49487.00 I   .188985  .000130   .303929  .000199  I -.1300973  .0000092  2.2213 0.0091  I   -17.469     .669    -8.904     .177   .188800   .303300  -.1301900   -16.700    -9.300  
+94 516 49488.00 I   .188471  .000243   .301845  .000148  I -.1323644  .0000162  2.3187 0.0089  I   -17.675     .116    -8.750     .298   .188400   .301200  -.1324700   -16.600    -9.200  
+94 517 49489.00 I   .187907  .000233   .299837  .000120  I -.1347430  .0000152  2.4411 0.0112  I   -17.613     .168    -8.515     .298   .188000   .299100  -.1348500   -16.600    -9.100  
+94 518 49490.00 I   .187447  .000226   .297844  .000120  I -.1372496  .0000155  2.5725 0.0109  I   -17.309     .168    -8.325     .298   .187600   .297000  -.1373600   -16.800    -9.000  
+94 519 49491.00 I   .187258  .000219   .295802  .000108  I -.1398852  .0000155  2.6955 0.0110  I   -17.047     .168    -8.289     .298   .187300   .295000  -.1400100   -17.000    -8.800  
+94 520 49492.00 I   .187165  .000222   .293799  .000125  I -.1426253  .0000155  2.7725 0.0115  I   -17.028     .168    -8.248     .298   .187000   .293100  -.1427500   -17.300    -8.600  
+94 521 49493.00 I   .186911  .000221   .291936  .000139  I -.1454033  .0000170  2.7688 0.0121  I   -17.269     .161    -8.051     .298   .186600   .291300  -.1455300   -17.800    -8.500  
+94 522 49494.00 I   .186381  .000133   .290234  .000209  I -.1481329  .0000187  2.6751 0.0117  I   -17.634     .164    -7.775     .298   .186000   .289700  -.1482500   -18.100    -8.500  
+94 523 49495.00 I   .185530  .000136   .288644  .000188  I -.1507284  .0000162  2.5062 0.0122  I   -17.930     .140    -7.622     .298   .185200   .288100  -.1508300   -18.300    -8.400  
+94 524 49496.00 I   .184389  .000139   .287063  .000200  I -.1531330  .0000157  2.3005 0.0112  I   -18.058     .115    -7.683     .298   .184200   .286400  -.1532300   -18.500    -8.400  
+94 525 49497.00 I   .183055  .000153   .285359  .000203  I -.1553305  .0000155  2.0982 0.0115  I   -18.066     .115    -7.809     .298   .183100   .284700  -.1554300   -18.500    -8.500  
+94 526 49498.00 I   .181653  .000151   .283438  .000200  I -.1573496  .0000168  1.9565 0.0111  I   -17.983     .115    -7.803     .298   .181900   .282700  -.1574600   -18.400    -8.600  
+94 527 49499.00 I   .180293  .000163   .281331  .000202  I -.1592708  .0000160  1.8965 0.0117  I   -17.762     .791    -7.691     .298   .180600   .280600  -.1593900   -18.100    -8.600  
+94 528 49500.00 I   .179012  .000173   .279090  .000188  I -.1611620  .0000164  1.8946 0.0136  I   -17.346     .791    -7.651     .298   .179200   .278400  -.1612800   -17.900    -8.700  
+94 529 49501.00 I   .177800  .000187   .276742  .000174  I -.1630720  .0000220  1.9287 0.0113  I   -16.919     .791    -7.749     .298   .177900   .276100  -.1632000   -17.800    -8.600  
+94 530 49502.00 I   .176635  .000174   .274317  .000121  I -.1650226  .0000156  1.9725 0.0135  I   -16.872     .101    -7.874     .298   .176700   .273600  -.1651600   -17.700    -8.600  
+94 531 49503.00 I   .175513  .000231   .271880  .000103  I -.1670147  .0000158  2.0096 0.0105  I   -17.343     .791    -7.936     .298   .175500   .271300  -.1671600   -17.900    -8.500  
+94 6 1 49504.00 I   .174487  .000230   .269528  .000095  I -.1690326  .0000140  2.0193 0.0104  I   -17.975     .791    -7.981     .298   .174500   .268900  -.1691800   -18.200    -8.400  
+94 6 2 49505.00 I   .173610  .000218   .267345  .000087  I -.1710389  .0000134  1.9860 0.0093  I   -18.333     .791    -8.073     .298   .173500   .266700  -.1711600   -18.700    -8.500  
+94 6 3 49506.00 I   .172730  .000204   .265339  .000102  I -.1729923  .0000122  1.9159 0.0072  I   -18.405     .791    -8.130     .298   .172400   .264700  -.1731100   -19.200    -8.500  
+94 6 4 49507.00 I   .171669  .000195   .263474  .000095  I -.1748627  .0000051  1.8219 0.0080  I   -18.540     .791    -8.049     .298   .171400   .262700  -.1749800   -19.600    -8.500  
+94 6 5 49508.00 I   .170453  .000245   .261702  .000155  I -.1766318  .0000104  1.7148 0.0046  I   -18.938     .791    -7.915     .298   .170200   .260900  -.1767500   -20.000    -8.600  
+94 6 6 49509.00 I   .169134  .000261   .259973  .000143  I -.1782903  .0000076  1.6015 0.0066  I   -19.405     .791    -7.902     .298   .169100   .259100  -.1784000   -20.300    -8.600  
+94 6 7 49510.00 I   .167779  .000220   .258226  .000159  I -.1798374  .0000081  1.4961 0.0057  I   -19.659     .116    -8.062     .298   .167900   .257300  -.1799400   -20.400    -8.800  
+94 6 8 49511.00 I   .166542  .000220   .256394  .000161  I -.1812913  .0000085  1.4172 0.0058  I   -19.626     .116    -8.274     .298   .166800   .255500  -.1813900   -20.300    -8.900  
+94 6 9 49512.00 I   .165561  .000220   .254458  .000146  I -.1826856  .0000084  1.3798 0.0061  I   -19.394     .116    -8.404     .298   .165700   .253600  -.1827900   -20.200    -9.000  
+94 610 49513.00 I   .164659  .000222   .252447  .000149  I -.1840720  .0000088  1.4058 0.0056  I   -19.114     .116    -8.469     .298   .164600   .251700  -.1842000   -20.000    -9.100  
+94 611 49514.00 I   .163719  .000226   .250366  .000146  I -.1855180  .0000074  1.4943 0.0076  I   -18.937     .116    -8.546     .298   .163600   .249600  -.1856600   -19.800    -9.100  
+94 612 49515.00 I   .162763  .000227   .248207  .000185  I -.1870737  .0000125  1.6222 0.0054  I   -18.989     .132    -8.653     .298   .162600   .247400  -.1872300   -19.600    -9.100  
+94 613 49516.00 I   .161851  .000150   .245977  .000231  I -.1887704  .0000080  1.7741 0.0074  I   -19.243     .276    -8.717     .249   .161800   .245200  -.1889200   -19.600    -9.000  
+94 614 49517.00 I   .161026  .000149   .243751  .000232  I -.1906238  .0000078  1.9318 0.0062  I   -19.475     .271    -8.624     .254   .161000   .243100  -.1907700   -19.800    -8.800  
+94 615 49518.00 I   .160281  .000153   .241641  .000241  I -.1926234  .0000094  2.0577 0.0058  I   -19.454     .271    -8.279     .254   .160200   .241000  -.1927500   -20.100    -8.700  
+94 616 49519.00 I   .159534  .000141   .239746  .000235  I -.1947203  .0000085  2.1269 0.0062  I   -19.190     .271    -7.649     .254   .159500   .239100  -.1948300   -20.500    -8.400  
+94 617 49520.00 I   .158676  .000159   .237991  .000257  I -.1968565  .0000082  2.1340 0.0049  I   -19.016     .271    -7.061     .254   .158700   .237400  -.1969500   -21.000    -8.300  
+94 618 49521.00 I   .157611  .000147   .236226  .000220  I -.1989637  .0000049  2.0671 0.0052  I   -19.208     .271    -6.696     .254   .157700   .235600  -.1990500   -21.400    -8.100  
+94 619 49522.00 I   .156276  .000146   .234349  .000184  I -.2009653  .0000065  1.9239 0.0038  I   -19.769     .192    -6.573     .185   .156500   .233800  -.2010400   -21.800    -8.000  
+94 620 49523.00 I   .154689  .000127   .232316  .000179  I -.2027923  .0000057  1.7232 0.0041  I   -20.493     .148    -6.755     .298   .155100   .231800  -.2028700   -22.000    -8.000  
+94 621 49524.00 I   .152998  .000139   .230146  .000171  I -.2044062  .0000049  1.5059 0.0038  I   -21.146     .148    -7.190     .298   .153500   .229600  -.2045000   -22.100    -8.000  
+94 622 49525.00 I   .151432  .000135   .227937  .000170  I -.2058193  .0000049  1.3341 0.0039  I   -21.641     .148    -7.629     .298   .151900   .227300  -.2059300   -22.100    -8.000  
+94 623 49526.00 I   .149986  .000123   .225843  .000155  I -.2071000  .0000061  1.2392 0.0040  I   -22.029     .289    -7.815     .298   .150100   .225100  -.2072300   -21.900    -8.100  
+94 624 49527.00 I   .148533  .000122   .223884  .000147  I -.2083200  .0000062  1.2114 0.0059  I   -22.261     .289    -7.712     .298   .148300   .223100  -.2084500   -21.800    -8.100  
+94 625 49528.00 I   .146973  .000158   .222011  .000151  I -.2095412  .0000102  1.2389 0.0079  I   -22.210     .316    -7.516     .298   .146500   .221200  -.2096800   -21.600    -8.100  
+94 626 49529.00 I   .145298  .000261   .220177  .000205  I -.2108058  .0000145  1.2903 0.0088  I   -21.905     .359    -7.442     .298   .144800   .219300  -.2109400   -21.700    -8.000  
+94 627 49530.00 I   .143565  .000270   .218360  .000251  I -.2121185  .0000144  1.3319 0.0106  I   -21.637     .293    -7.526     .298   .143000   .217500  -.2122400   -21.800    -8.000  
+94 628 49531.00 I   .141813  .000248   .216582  .000252  I -.2134590  .0000156  1.3419 0.0105  I   -21.735     .488    -7.650     .298   .141400   .215800  -.2135700   -22.100    -8.100  
+94 629 49532.00 I   .140036  .000246   .214909  .000253  I -.2147803  .0000153  1.2859 0.0112  I   -22.113     .470    -7.727     .336   .139800   .214200  -.2148800   -22.600    -8.000  
+94 630 49533.00 I   .138313  .000256   .213438  .000261  I -.2160203  .0000160  1.1984 0.0107  I   -22.429     .470    -7.778     .336   .138300   .212700  -.2161400   -23.100    -8.100  
+94 7 1 49534.00 I   .136704  .000246   .212123  .000260  I  .7828134  .0000151  1.1396 0.0112  I   -22.586     .495    -7.896     .359   .136800   .211500   .7826700   -23.700    -8.200  
+94 7 2 49535.00 I   .135090  .000228   .210874  .000257  I  .7817000  .0000158  1.0824 0.0111  I   -22.711     .495    -7.977     .359   .135000   .210300   .7815500   -24.300    -7.700  
+94 7 3 49536.00 I   .133370  .000217   .209627  .000206  I  .7806540  .0000164  1.0089 0.0098  I   -22.984     .535    -7.891     .387   .133300   .209000   .7805200   -24.300    -7.800  
+94 7 4 49537.00 I   .131521  .000171   .208338  .000169  I  .7796814  .0000116  0.9377 0.0094  I   -23.345     .396    -7.780     .416   .131400   .207600   .7795600   -24.200    -7.800  
+94 7 5 49538.00 I   .129613  .000140   .206974  .000120  I  .7787730  .0000093  0.8833 0.0074  I   -23.627     .251    -7.733     .241   .129500   .206200   .7786700   -23.900    -7.900  
+94 7 6 49539.00 I   .127726  .000134   .205520  .000147  I  .7779060  .0000092  0.8552 0.0065  I   -23.785     .265    -7.754     .244   .127700   .204600   .7778100   -23.700    -7.900  
+94 7 7 49540.00 I   .125890  .000123   .203972  .000139  I  .7770472  .0000090  0.8732 0.0065  I   -23.846     .265    -7.837     .244   .125900   .203000   .7769300   -23.400    -7.900  
+94 7 8 49541.00 I   .124100  .000119   .202429  .000137  I  .7761321  .0000091  0.9738 0.0073  I   -23.815     .265    -7.887     .244   .124100   .201400   .7759900   -23.100    -7.800  
+94 7 9 49542.00 I   .122316  .000122   .200998  .000145  I  .7750765  .0000114  1.1439 0.0081  I   -23.717     .265    -7.840     .244   .122400   .199900   .7749200   -22.900    -7.700  
+94 710 49543.00 I   .120582  .000120   .199702  .000172  I  .7738352  .0000133  1.3414 0.0086  I   -23.683     .266    -7.763     .236   .120600   .198700   .7737000   -22.900    -7.600  
+94 711 49544.00 I   .118900  .000197   .198504  .000149  I  .7723924  .0000129  1.5430 0.0098  I   -23.895     .171    -7.649     .144   .118800   .197600   .7722700   -23.100    -7.600  
+94 712 49545.00 I   .117262  .000212   .197367  .000155  I  .7707577  .0000144  1.7193 0.0098  I   -24.337     .288    -7.524     .154   .117100   .196600   .7706400   -23.400    -7.400  
+94 713 49546.00 I   .115585  .000219   .196254  .000144  I  .7689693  .0000147  1.8492 0.0108  I   -24.771     .289    -7.427     .146   .115300   .195500   .7688400   -23.900    -7.400  
+94 714 49547.00 I   .113774  .000210   .195114  .000147  I  .7670815  .0000162  1.9124 0.0108  I   -24.962     .289    -7.384     .146   .113500   .194400   .7669300   -24.500    -7.400  
+94 715 49548.00 I   .111917  .000204   .193886  .000157  I  .7651629  .0000159  1.9204 0.0112  I   -24.879     .289    -7.363     .146   .111700   .193100   .7649800   -25.000    -7.400  
+94 716 49549.00 I   .110060  .000186   .192527  .000164  I  .7632581  .0000156  1.8763 0.0113  I   -24.790     .316    -7.261     .160   .109900   .191700   .7630900   -25.500    -7.400  
+94 717 49550.00 I   .108157  .000190   .191009  .000196  I  .7614317  .0000160  1.7680 0.0105  I   -24.951     .324    -7.108     .164   .108000   .190200   .7613000   -25.900    -7.500  
+94 718 49551.00 I   .106172  .000114   .189314  .000232  I  .7597348  .0000142  1.6217 0.0104  I   -25.392     .196    -7.070     .298   .106000   .188400   .7596400   -26.100    -7.700  
+94 719 49552.00 I   .104134  .000078   .187488  .000220  I  .7581907  .0000132  1.4681 0.0097  I   -25.979     .394    -7.267     .298   .104000   .186600   .7581200   -26.000    -7.800  
+94 720 49553.00 I   .102165  .000080   .185697  .000223  I  .7567906  .0000131  1.3386 0.0094  I   -26.503     .426    -7.666     .298   .102000   .184800   .7567200   -25.900    -7.900  
+94 721 49554.00 I   .100269  .000084   .184109  .000224  I  .7554917  .0000133  1.2743 0.0083  I   -26.720     .426    -8.016     .298   .100100   .183100   .7553900   -25.700    -7.900  
+94 722 49555.00 I   .098391  .000080   .182709  .000195  I  .7542115  .0000102  1.3013 0.0100  I   -26.485     .426    -8.144     .298   .098200   .181700   .7540600   -25.500    -8.000  
+94 723 49556.00 I   .096537  .000087   .181392  .000201  I  .7528672  .0000150  1.3941 0.0124  I   -26.031     .482    -8.003     .298   .096400   .180400   .7526900   -25.300    -8.000  
+94 724 49557.00 I   .094755  .000094   .180140  .000155  I  .7514222  .0000227  1.4901 0.0110  I   -25.675     .589    -7.741     .298   .094600   .179200   .7512600   -25.300    -7.900  
+94 725 49558.00 I   .092961  .000095   .178983  .000129  I  .7499005  .0000161  1.5457 0.0129  I   -25.608     .365    -7.572     .102   .092700   .178100   .7497800   -25.400    -7.800  
+94 726 49559.00 I   .091017  .000095   .177952  .000101  I  .7483472  .0000123  1.5521 0.0099  I   -25.894     .791    -7.555     .105   .090700   .177100   .7482500   -25.700    -7.700  
+94 727 49560.00 I   .088891  .000094   .177080  .000117  I  .7468139  .0000114  1.5058 0.0086  I   -26.385     .175    -7.649     .140   .088500   .176200   .7467100   -26.200    -7.700  
+94 728 49561.00 I   .086705  .000104   .176385  .000121  I  .7453424  .0000120  1.4390 0.0071  I   -26.828     .175    -7.806     .140   .086300   .175400   .7451900   -26.700    -7.600  
+94 729 49562.00 I   .084616  .000106   .175803  .000130  I  .7439290  .0000083  1.3929 0.0086  I   -27.103     .175    -7.879     .140   .084200   .174700   .7437300   -27.200    -7.600  
+94 730 49563.00 I   .082635  .000104   .175254  .000134  I  .7425566  .0000122  1.3467 0.0095  I   -27.271     .175    -7.739     .140   .082100   .174200   .7423500   -27.600    -7.600  
+94 731 49564.00 I   .080660  .000113   .174710  .000181  I  .7412436  .0000172  1.2766 0.0086  I   -27.440     .169    -7.519     .169   .080200   .173800   .7410500   -27.800    -7.700  
+94 8 1 49565.00 I   .078707  .000115   .174217  .000167  I  .7400066  .0000122  1.1969 0.0105  I   -27.676     .146    -7.396     .155   .078500   .173400   .7398500   -28.000    -7.700  
+94 8 2 49566.00 I   .076835  .000114   .173815  .000233  I  .7388476  .0000122  1.1236 0.0089  I   -27.985     .272    -7.481     .128   .076800   .173100   .7387200   -27.800    -7.700  
+94 8 3 49567.00 I   .075074  .000115   .173500  .000238  I  .7377391  .0000130  1.1110 0.0089  I   -28.297     .369    -7.815     .120   .075100   .172800   .7376000   -27.700    -7.700  
+94 8 4 49568.00 I   .073460  .000112   .173232  .000230  I  .7366003  .0000130  1.1742 0.0088  I   -28.465     .369    -8.208     .120   .073400   .172500   .7364400   -27.500    -7.700  
+94 8 5 49569.00 I   .071866  .000127   .172970  .000229  I  .7353776  .0000119  1.2766 0.0094  I   -28.347     .369    -8.552     .120   .071600   .172200   .7352000   -27.100    -7.700  
+94 8 6 49570.00 I   .070233  .000141   .172666  .000192  I  .7340370  .0000137  1.4092 0.0103  I   -28.089     .425    -8.744     .298   .069900   .171700   .7339000   -27.600    -7.400  
+94 8 7 49571.00 I   .068588  .000160   .172309  .000195  I  .7325559  .0000169  1.5517 0.0102  I   -27.916     .469    -8.671     .298   .068300   .171400   .7324300   -27.700    -7.400  
+94 8 8 49572.00 I   .067030  .000164   .171954  .000213  I  .7309394  .0000151  1.6768 0.0114  I   -27.955     .419    -8.318     .298   .066800   .171300   .7308300   -27.800    -7.400  
+94 8 9 49573.00 I   .065656  .000172   .171684  .000181  I  .7292203  .0000153  1.7480 0.0108  I   -28.228     .547    -7.808     .169   .065400   .171200   .7291200   -28.000    -7.200  
+94 810 49574.00 I   .064458  .000175   .171547  .000180  I  .7274593  .0000155  1.7713 0.0111  I   -28.560     .658    -7.362     .182   .064100   .171200   .7273400   -28.200    -7.300  
+94 811 49575.00 I   .063177  .000176   .171524  .000190  I  .7256921  .0000162  1.7512 0.0106  I   -28.757     .658    -7.088     .182   .062700   .171200   .7255600   -28.500    -7.400  
+94 812 49576.00 I   .061639  .000167   .171505  .000195  I  .7239814  .0000145  1.6577 0.0103  I   -28.588     .658    -6.841     .182   .061100   .171000   .7238400   -28.800    -7.500  
+94 813 49577.00 I   .059681  .000189   .171353  .000175  I  .7223951  .0000126  1.5089 0.0112  I   -28.281     .734    -6.720     .209   .059100   .170700   .7222500   -29.100    -7.600  
+94 814 49578.00 I   .057308  .000206   .170981  .000140  I  .7209662  .0000170  1.3523 0.0091  I   -28.145     .718    -6.823     .202   .056900   .170200   .7208100   -29.300    -7.700  
+94 815 49579.00 I   .054665  .000188   .170386  .000139  I  .7196757  .0000132  1.2402 0.0107  I   -28.281     .538    -7.082     .126   .054500   .169600   .7195100   -29.300    -7.900  
+94 816 49580.00 I   .052050  .000176   .169688  .000119  I  .7184603  .0000130  1.2047 0.0089  I   -28.667     .130    -7.455     .298   .052000   .168900   .7183000   -29.200    -7.900  
+94 817 49581.00 I   .049759  .000195   .169084  .000115  I  .7172468  .0000118  1.2276 0.0088  I   -29.099     .130    -7.862     .298   .049700   .168300   .7171100   -29.100    -7.900  
+94 818 49582.00 I   .047661  .000195   .168749  .000091  I  .7159904  .0000119  1.2947 0.0084  I   -29.330     .130    -8.157     .298   .047300   .168000   .7158700   -28.800    -7.900  
+94 819 49583.00 I   .045502  .000198   .168670  .000093  I  .7146430  .0000119  1.4050 0.0079  I   -29.250     .130    -8.214     .298   .045000   .167800   .7145300   -28.500    -7.800  
+94 820 49584.00 I   .043003  .000174   .168739  .000106  I  .7131745  .0000105  1.5329 0.0071  I   -28.961     .126    -8.018     .298   .042400   .167900   .7130500   -28.400    -7.700  
+94 821 49585.00 I   .040105  .000156   .168840  .000130  I  .7115798  .0000078  1.6529 0.0077  I   -28.685     .791    -7.697     .298   .039800   .168000   .7114500   -28.400    -7.600  
+94 822 49586.00 I   .036966  .000256   .168869  .000241  I  .7098798  .0000114  1.7397 0.0071  I   -28.600     .278    -7.439     .298   .037000   .168000   .7097400   -28.600    -7.400  
+94 823 49587.00 I   .033817  .000249   .168787  .000242  I  .7081159  .0000118  1.7799 0.0083  I   -28.752     .271    -7.346     .298   .034200   .167900   .7079800   -28.900    -7.300  
+94 824 49588.00 I   .030911  .000250   .168608  .000243  I  .7063369  .0000122  1.7693 0.0085  I   -29.122     .315    -7.395     .298   .031400   .167600   .7062200   -29.400    -7.300  
+94 825 49589.00 I   .028467  .000253   .168424  .000243  I  .7045966  .0000123  1.7009 0.0086  I   -29.669     .315    -7.508     .298   .029000   .167400   .7044700   -29.800    -7.300  
+94 826 49590.00 I   .026502  .000277   .168362  .000243  I  .7029378  .0000122  1.6244 0.0084  I   -30.183     .315    -7.619     .298   .026800   .167300   .7027900   -30.200    -7.400  
+94 827 49591.00 I   .024679  .000269   .168471  .000226  I  .7013413  .0000114  1.5674 0.0090  I   -30.349     .315    -7.719     .298   .024700   .167400   .7011900   -30.500    -7.500  
+94 828 49592.00 I   .022800  .000288   .168698  .000224  I  .6998036  .0000132  1.5086 0.0075  I   -30.126     .331    -7.837     .298   .022700   .167700   .6996800   -30.600    -7.700  
+94 829 49593.00 I   .020795  .000197   .169007  .000125  I  .6983214  .0000097  1.4578 0.0081  I   -29.812     .357    -7.971     .298   .020600   .168100   .6982400   -30.500    -7.800  
+94 830 49594.00 I   .018609  .000185   .169415  .000116  I  .6968824  .0000093  1.4237 0.0066  I   -29.713     .266    -8.053     .298   .018400   .168600   .6968200   -30.200    -7.900  
+94 831 49595.00 I   .016183  .000172   .169907  .000115  I  .6954573  .0000090  1.4398 0.0064  I   -29.858     .245    -8.001     .298   .016100   .169000   .6953800   -30.000    -7.900  
+94 9 1 49596.00 I   .013552  .000149   .170355  .000120  I  .6939768  .0000089  1.5340 0.0061  I   -29.913     .245    -7.856     .298   .013700   .169400   .6938700   -29.600    -7.900  
+94 9 2 49597.00 I   .010952  .000128   .170717  .000126  I  .6923683  .0000083  1.6913 0.0066  I   -29.700     .245    -7.829     .298   .011200   .169800   .6922400   -29.200    -7.800  
+94 9 3 49598.00 I   .008574  .000125   .171044  .000126  I  .6905787  .0000097  1.8952 0.0065  I   -29.379     .202    -7.949     .298   .008800   .170200   .6904400   -29.100    -7.700  
+94 9 4 49599.00 I   .006431  .000122   .171426  .000121  I  .6885696  .0000101  2.1240 0.0074  I   -29.182     .132    -8.049     .107   .006400   .170700   .6884300   -29.000    -7.500  
+94 9 5 49600.00 I   .004323  .000130   .171935  .000116  I  .6863372  .0000111  2.3334 0.0077  I   -29.213     .122    -7.953     .298   .004000   .171000   .6862300   -29.400    -7.400  
+94 9 6 49601.00 I   .002002  .000148   .172597  .000123  I  .6839238  .0000117  2.4798 0.0081  I   -29.478     .285    -7.625     .263   .001600   .171800   .6838300   -29.700    -7.300  
+94 9 7 49602.00 I  -.000616  .000165   .173385  .000129  I  .6814098  .0000119  2.5294 0.0085  I   -29.918     .269    -7.240     .237  -.000800   .172700   .6813000   -30.100    -7.200  
+94 9 8 49603.00 I  -.003456  .000162   .174249  .000122  I  .6788784  .0000122  2.5387 0.0085  I   -30.414     .269    -7.010     .237  -.003500   .173600   .6787400   -30.400    -7.200  
+94 9 9 49604.00 I  -.006148  .000167   .175135  .000119  I  .6763470  .0000122  2.5042 0.0086  I   -30.813     .269    -7.039     .237  -.006100   .174600   .6762100   -30.700    -7.200  
+94 910 49605.00 I  -.008596  .000172   .176085  .000124  I  .6738930  .0000122  2.4007 0.0097  I   -30.956     .269    -7.180     .237  -.008500   .175500   .6737700   -30.800    -7.400  
+94 911 49606.00 I  -.010833  .000180   .177119  .000121  I  .6715460  .0000150  2.2959 0.0079  I   -30.831     .270    -7.279     .236  -.010800   .176500   .6714500   -30.900    -7.400  
+94 912 49607.00 I  -.013072  .000198   .178169  .000123  I  .6692901  .0000101  2.2243 0.0092  I   -30.566     .133    -7.349     .298  -.013100   .177400   .6692100   -30.600    -7.500  
+94 913 49608.00 I  -.015556  .000172   .179143  .000122  I  .6670788  .0000106  2.2084 0.0069  I   -30.308     .791    -7.449     .298  -.015600   .178200   .6670100   -30.400    -7.600  
+94 914 49609.00 I  -.018422  .000167   .179980  .000116  I  .6648563  .0000094  2.2434 0.0071  I   -30.158     .151    -7.589     .298  -.018400   .179100   .6647800   -30.100    -7.500  
+94 915 49610.00 I  -.021599  .000162   .180790  .000122  I  .6625777  .0000095  2.3212 0.0065  I   -30.105     .151    -7.710     .298  -.021400   .179800   .6624700   -29.800    -7.500  
+94 916 49611.00 I  -.024813  .000164   .181577  .000124  I  .6601997  .0000091  2.4414 0.0066  I   -30.049     .151    -7.730     .298  -.024700   .180600   .6600700   -29.600    -7.300  
+94 917 49612.00 I  -.027798  .000153   .182332  .000142  I  .6576960  .0000092  2.5583 0.0067  I   -29.920     .178    -7.609     .298  -.027800   .181300   .6575600   -29.400    -7.200  
+94 918 49613.00 I  -.030505  .000136   .183089  .000153  I  .6551011  .0000098  2.6213 0.0054  I   -29.808     .235    -7.388     .298  -.030700   .182200   .6549800   -29.400    -7.100  
+94 919 49614.00 I  -.033007  .000153   .183934  .000145  I  .6524739  .0000057  2.6229 0.0055  I   -29.836     .195    -7.172     .298  -.033300   .183100   .6523600   -29.500    -6.900  
+94 920 49615.00 I  -.035461  .000150   .184962  .000153  I  .6498753  .0000050  2.5646 0.0039  I   -30.017     .177    -7.061     .298  -.035800   .184200   .6497700   -29.700    -7.000  
+94 921 49616.00 I  -.038015  .000154   .186147  .000148  I  .6473536  .0000053  2.4796 0.0036  I   -30.328     .236    -7.099     .298  -.038400   .185400   .6472300   -30.100    -7.000  
+94 922 49617.00 I  -.040832  .000163   .187296  .000151  I  .6449207  .0000053  2.3806 0.0040  I   -30.690     .236    -7.186     .298  -.041000   .186600   .6447800   -30.300    -7.100  
+94 923 49618.00 I  -.043818  .000160   .188375  .000146  I  .6426013  .0000060  2.2548 0.0040  I   -30.923     .236    -7.290     .298  -.043800   .187700   .6424600   -30.600    -7.200  
+94 924 49619.00 I  -.046775  .000165   .189470  .000129  I  .6404117  .0000059  2.1271 0.0049  I   -30.778     .232    -7.429     .298  -.046600   .188700   .6402600   -30.600    -7.300  
+94 925 49620.00 I  -.049585  .000145   .190626  .000157  I  .6383387  .0000078  2.0244 0.0041  I   -30.261     .309    -7.585     .298  -.049400   .189700   .6381900   -30.500    -7.600  
+94 926 49621.00 I  -.052280  .000127   .191848  .000143  I  .6363505  .0000056  1.9587 0.0047  I   -29.706     .264    -7.744     .298  -.052300   .190900   .6362000   -30.200    -7.700  
+94 927 49622.00 I  -.054909  .000132   .193105  .000143  I  .6344068  .0000054  1.9365 0.0041  I   -29.502     .229    -7.860     .298  -.055100   .192100   .6342700   -29.900    -7.700  
+94 928 49623.00 I  -.057521  .000134   .194405  .000155  I  .6324646  .0000061  1.9532 0.0052  I   -29.675     .229    -7.841     .298  -.057900   .193600   .6323400   -29.500    -7.700  
+94 929 49624.00 I  -.060233  .000131   .195865  .000160  I  .6304856  .0000088  2.0144 0.0053  I   -29.819     .229    -7.680     .298  -.060700   .195100   .6303600   -29.100    -7.600  
+94 930 49625.00 I  -.062857  .000132   .197484  .000168  I  .6284157  .0000086  2.1359 0.0065  I   -29.697     .229    -7.504     .298  -.063300   .196800   .6282800   -28.800    -7.500  
+9410 1 49626.00 I  -.065164  .000139   .199217  .000171  I  .6261970  .0000096  2.3078 0.0071  I   -29.482     .135    -7.481     .298  -.065500   .198500   .6260500   -28.700    -7.300  
+9410 2 49627.00 I  -.067149  .000138   .201047  .000165  I  .6237941  .0000114  2.4978 0.0070  I   -29.431     .221    -7.604     .298  -.067400   .200300   .6236400   -28.700    -7.100  
+9410 3 49628.00 I  -.068989  .000137   .202944  .000165  I  .6212049  .0000101  2.6771 0.0076  I   -29.544     .210    -7.670     .298  -.069200   .202200   .6210500   -28.800    -7.000  
+9410 4 49629.00 I  -.070976  .000189   .204837  .000163  I  .6184532  .0000100  2.8163 0.0064  I   -29.597     .617    -7.515     .298  -.071200   .204000   .6183100   -29.100    -6.900  
+9410 5 49630.00 I  -.073424  .000189   .206629  .000149  I  .6155998  .0000077  2.8730 0.0063  I   -29.430     .636    -7.233     .298  -.073900   .205500   .6155100   -29.300    -6.700  
+9410 6 49631.00 I  -.076446  .000186   .208213  .000149  I  .6127461  .0000077  2.8130 0.0052  I   -29.179     .636    -7.115     .298  -.076800   .207100   .6126800   -29.600    -6.900  
+9410 7 49632.00 I  -.079838  .000186   .209601  .000144  I  .6100058  .0000070  2.6571 0.0049  I   -29.178     .636    -7.176     .298  -.080000   .208600   .6099500   -29.800    -6.900  
+9410 8 49633.00 I  -.083262  .000194   .210886  .000134  I  .6074385  .0000060  2.4813 0.0045  I   -29.386     .723    -7.226     .298  -.083300   .210000   .6073700   -29.800    -7.100  
+9410 9 49634.00 I  -.086479  .000211   .212130  .000184  I  .6050286  .0000057  2.3492 0.0038  I   -29.605     .874    -7.178     .298  -.086500   .211300   .6049500   -29.600    -7.200  
+941010 49635.00 I  -.089290  .000169   .213380  .000144  I  .6027164  .0000046  2.2882 0.0037  I   -29.663     .487    -7.125     .298  -.089300   .212600   .6026200   -29.300    -7.200  
+941011 49636.00 I  -.091505  .000134   .214693  .000114  I  .6004284  .0000047  2.2986 0.0033  I   -29.374     .791    -7.157     .298  -.091600   .214000   .6003300   -29.000    -7.200  
+941012 49637.00 I  -.093166  .000133   .216160  .000114  I  .5981027  .0000047  2.3584 0.0050  I   -28.789     .791    -7.217     .105  -.093400   .215500   .5980000   -28.500    -7.100  
+941013 49638.00 I  -.094538  .000137   .217881  .000124  I  .5957062  .0000088  2.4344 0.0052  I   -28.195     .791    -7.171     .105  -.094800   .217200   .5956100   -28.100    -7.000  
+941014 49639.00 I  -.095750  .000120   .219840  .000120  I  .5932373  .0000092  2.5003 0.0063  I   -27.833     .791    -6.981     .105  -.095900   .219100   .5931400   -27.800    -6.700  
+941015 49640.00 I  -.096889  .000115   .221990  .000123  I  .5907151  .0000091  2.5377 0.0070  I   -27.732     .791    -6.730     .298  -.096900   .221100   .5906300   -27.500    -6.600  
+941016 49641.00 I  -.098078  .000120   .224264  .000151  I  .5881772  .0000106  2.5292 0.0068  I   -27.799     .791    -6.522     .113  -.098000   .223400   .5881000   -27.400    -6.400  
+941017 49642.00 I  -.099501  .000137   .226604  .000138  I  .5856753  .0000101  2.4650 0.0079  I   -27.949     .791    -6.388     .100  -.099500   .225700   .5856200   -27.400    -6.300  
+941018 49643.00 I  -.101377  .000139   .228955  .000166  I  .5832655  .0000117  2.3458 0.0066  I   -28.097     .145    -6.342     .298  -.101500   .228000   .5832200   -27.600    -6.200  
+941019 49644.00 I  -.103841  .000157   .231268  .000168  I  .5809929  .0000084  2.1989 0.0077  I   -28.159     .146    -6.428     .298  -.104100   .230300   .5809500   -27.600    -6.200  
+941020 49645.00 I  -.106752  .000150   .233466  .000159  I  .5788597  .0000099  2.0761 0.0065  I   -28.151     .146    -6.598     .298  -.107100   .232600   .5788000   -27.800    -6.300  
+941021 49646.00 I  -.109750  .000145   .235599  .000154  I  .5768260  .0000100  1.9973 0.0065  I   -28.099     .152    -6.777     .298  -.110100   .234900   .5767600   -27.900    -6.600  
+941022 49647.00 I  -.112530  .000130   .237770  .000135  I  .5748551  .0000085  1.9483 0.0080  I   -27.906     .152    -6.895     .298  -.112800   .237100   .5747800   -27.900    -6.700  
+941023 49648.00 I  -.114973  .000108   .239947  .000137  I  .5729195  .0000124  1.9288 0.0060  I   -27.529     .200    -6.919     .298  -.115100   .239300   .5728500   -27.700    -6.900  
+941024 49649.00 I  -.117092  .000113   .242100  .000147  I  .5709849  .0000085  1.9471 0.0077  I   -27.136     .189    -6.900     .298  -.117200   .241300   .5709100   -27.400    -7.000  
+941025 49650.00 I  -.119084  .000118   .244217  .000210  I  .5690110  .0000090  2.0083 0.0058  I   -26.957     .181    -6.902     .298  -.119200   .243300   .5689300   -27.100    -7.100  
+941026 49651.00 I  -.121195  .000117   .246300  .000210  I  .5669543  .0000078  2.1115 0.0060  I   -26.972     .347    -6.898     .298  -.121400   .245300   .5668700   -26.800    -7.100  
+941027 49652.00 I  -.123525  .000121   .248390  .000209  I  .5647789  .0000079  2.2419 0.0056  I   -26.961     .347    -6.781     .298  -.123800   .247300   .5646900   -26.600    -6.900  
+941028 49653.00 I  -.125949  .000123   .250477  .000214  I  .5624625  .0000079  2.3964 0.0054  I   -26.804     .347    -6.581     .298  -.126100   .249400   .5623800   -26.400    -6.700  
+941029 49654.00 I  -.128152  .000152   .252528  .000235  I  .5599782  .0000074  2.5748 0.0061  I   -26.673     .366    -6.388     .298  -.128300   .251500   .5599200   -26.500    -6.600  
+941030 49655.00 I  -.130001  .000159   .254583  .000231  I  .5573138  .0000094  2.7507 0.0051  I   -26.803     .447    -6.270     .104  -.130200   .253700   .5572700   -26.600    -6.300  
+941031 49656.00 I  -.131520  .000155   .256710  .000201  I  .5544892  .0000071  2.8894 0.0057  I   -27.115     .480    -6.190     .101  -.131800   .255900   .5544400   -26.900    -6.300  
+9411 1 49657.00 I  -.132859  .000160   .258913  .000127  I  .5515578  .0000065  2.9599 0.0051  I   -27.224     .424    -6.064     .298  -.133200   .258200   .5515000   -27.100    -6.200  
+9411 2 49658.00 I  -.134179  .000156   .261087  .000129  I  .5485981  .0000073  2.9445 0.0051  I   -26.818     .424    -5.963     .298  -.134500   .260400   .5485300   -27.300    -6.200  
+9411 3 49659.00 I  -.135582  .000161   .263155  .000113  I  .5457003  .0000079  2.8346 0.0053  I   -26.109     .424    -5.946     .298  -.135800   .262600   .5456100   -27.500    -6.200  
+9411 4 49660.00 I  -.136997  .000157   .265187  .000106  I  .5429500  .0000077  2.6615 0.0057  I   -25.467     .424    -5.987     .298  -.137200   .264900   .5428600   -28.000    -6.100  
+9411 5 49661.00 I  -.138425  .000121   .267286  .000088  I  .5403769  .0000081  2.4897 0.0079  I   -25.234     .297    -5.951     .298  -.138600   .267000   .5402800   -27.700    -6.200  
+9411 6 49662.00 I  -.139887  .000157   .269503  .000130  I  .5379537  .0000137  2.3687 0.0062  I   -25.520     .791    -5.795     .298  -.140100   .269100   .5378600   -27.200    -6.200  
+9411 7 49663.00 I  -.141344  .000139   .271822  .000107  I  .5356169  .0000094  2.3154 0.0082  I   -26.008     .157    -5.692     .298  -.141700   .271300   .5355300   -26.700    -6.100  
+9411 8 49664.00 I  -.142740  .000149   .274220  .000101  I  .5333062  .0000089  2.3121 0.0062  I   -26.181     .157    -5.819     .298  -.143100   .273600   .5332300   -26.100    -6.100  
+9411 9 49665.00 I  -.143972  .000163   .276693  .000095  I  .5309772  .0000081  2.3554 0.0062  I   -25.847     .141    -6.093     .298  -.144300   .276000   .5309100   -25.600    -5.900  
+941110 49666.00 I  -.144804  .000165   .279285  .000090  I  .5285867  .0000085  2.4250 0.0058  I   -25.301     .141    -6.270     .298  -.145200   .278700   .5285300   -25.300    -5.800  
+941111 49667.00 I  -.145287  .000167   .281983  .000089  I  .5261314  .0000084  2.4822 0.0060  I   -24.982     .141    -6.215     .298  -.145700   .281400   .5260800   -25.100    -5.500  
+941112 49668.00 I  -.145653  .000176   .284744  .000100  I  .5236320  .0000084  2.5105 0.0093  I   -24.995     .165    -6.033     .298  -.146200   .284200   .5236000   -24.900    -5.400  
+941113 49669.00 I  -.146085  .000137   .287529  .000106  I  .5211225  .0000166  2.5025 0.0067  I   -25.125     .100    -5.890     .298  -.146500   .286900   .5210900   -25.000    -5.300  
+941114 49670.00 I  -.146623  .000138   .290267  .000131  I  .5186373  .0000104  2.4635 0.0098  I   -25.220     .791    -5.802     .298  -.146800   .289600   .5186000   -25.200    -5.300  
+941115 49671.00 I  -.147213  .000135   .292902  .000140  I  .5162031  .0000105  2.4019 0.0078  I   -25.299     .791    -5.682     .298  -.147200   .292300   .5161600   -25.300    -5.400  
+941116 49672.00 I  -.147808  .000101   .295464  .000142  I  .5138394  .0000116  2.3228 0.0080  I   -25.366     .791    -5.524     .298  -.147700   .294900   .5137800   -25.400    -5.500  
+941117 49673.00 I  -.148488  .000101   .298075  .000147  I  .5115623  .0000122  2.2293 0.0084  I   -25.355     .791    -5.410     .298  -.148300   .297500   .5114900   -25.500    -5.500  
+941118 49674.00 I  -.149331  .000109   .300760  .000141  I  .5093815  .0000122  2.1336 0.0073  I   -25.239     .791    -5.379     .298  -.149200   .300200   .5093100   -25.600    -5.700  
+941119 49675.00 I  -.150308  .000100   .303477  .000122  I  .5072894  .0000079  2.0548 0.0103  I   -25.045     .791    -5.368     .298  -.150300   .302900   .5072100   -25.400    -5.800  
+941120 49676.00 I  -.151335  .000092   .306191  .000112  I  .5052594  .0000166  2.0133 0.0065  I   -24.808     .165    -5.328     .298  -.151400   .305600   .5051700   -25.200    -5.900  
+941121 49677.00 I  -.152336  .000133   .308941  .000110  I  .5032454  .0000102  2.0239 0.0093  I   -24.601     .175    -5.319     .298  -.152500   .308400   .5031400   -24.900    -5.800  
+941122 49678.00 I  -.153136  .000129   .311793  .000113  I  .5011934  .0000082  2.0891 0.0063  I   -24.547     .175    -5.435     .298  -.153400   .311100   .5010800   -24.500    -5.800  
+941123 49679.00 I  -.153511  .000141   .314720  .000109  I  .4990493  .0000075  2.2081 0.0057  I   -24.659     .175    -5.642     .298  -.153800   .313900   .4989500   -24.200    -5.600  
+941124 49680.00 I  -.153354  .000165   .317579  .000122  I  .4967655  .0000078  2.3621 0.0047  I   -24.704     .175    -5.716     .298  -.153700   .316700   .4967000   -24.000    -5.400  
+941125 49681.00 I  -.152777  .000179   .320381  .000118  I  .4943244  .0000057  2.5182 0.0069  I   -24.517     .175    -5.571     .298  -.153100   .319500   .4942900   -23.700    -5.200  
+941126 49682.00 I  -.152113  .000199   .323160  .000175  I  .4917358  .0000113  2.6543 0.0066  I   -24.210     .185    -5.329     .298  -.152300   .322300   .4917300   -23.600    -5.000  
+941127 49683.00 I  -.151650  .000230   .325909  .000179  I  .4890285  .0000120  2.7525 0.0077  I   -24.031     .130    -5.127     .298  -.151600   .325100   .4890400   -23.700    -4.800  
+941128 49684.00 I  -.151460  .000193   .328635  .000162  I  .4862510  .0000106  2.7901 0.0080  I   -24.087     .121    -4.977     .298  -.151200   .327900   .4862600   -23.800    -4.800  
+941129 49685.00 I  -.151383  .000184   .331360  .000163  I  .4834753  .0000107  2.7465 0.0076  I   -24.250     .121    -4.831     .298  -.151200   .330600   .4834500   -23.900    -4.700  
+941130 49686.00 I  -.151275  .000171   .334074  .000169  I  .4807701  .0000109  2.6664 0.0076  I   -24.308     .121    -4.744     .298  -.151400   .333400   .4806900   -23.900    -4.700  
+9412 1 49687.00 I  -.151498  .000148   .336727  .000158  I  .4781455  .0000109  2.5772 0.0074  I   -24.154     .110    -4.797     .298  -.151700   .336100   .4780300   -24.000    -4.900  
+9412 2 49688.00 I  -.152003  .000145   .339262  .000158  I  .4756244  .0000100  2.4620 0.0066  I   -23.860     .110    -4.966     .298  -.152200   .338700   .4755000   -24.000    -5.000  
+9412 3 49689.00 I  -.152556  .000125   .341652  .000123  I  .4732183  .0000076  2.3565 0.0075  I   -23.675     .791    -5.060     .298  -.152500   .341100   .4730900   -23.900    -5.100  
+9412 4 49690.00 I  -.152865  .000092   .343888  .000153  I  .4708950  .0000111  2.3003 0.0052  I   -23.792     .162    -4.944     .298  -.152900   .343200   .4707300   -24.000    -5.300  
+9412 5 49691.00 I  -.152737  .000097   .346018  .000149  I  .4685954  .0000072  2.3109 0.0068  I   -24.054     .252    -4.767     .298  -.152900   .345300   .4684300   -23.500    -5.300  
+9412 6 49692.00 I  -.152319  .000106   .348133  .000148  I  .4662553  .0000079  2.3752 0.0053  I   -24.048     .230    -4.791     .298  -.152600   .347400   .4661100   -23.300    -5.200  
+9412 7 49693.00 I  -.151855  .000104   .350338  .000142  I  .4638417  .0000079  2.4492 0.0054  I   -23.616     .265    -5.034     .298  -.152200   .349600   .4637200   -23.300    -5.000  
+9412 8 49694.00 I  -.151502  .000124   .352762  .000150  I  .4613657  .0000075  2.4966 0.0055  I   -23.189     .265    -5.183     .298  -.151900   .352100   .4612800   -23.000    -4.800  
+9412 9 49695.00 I  -.151377  .000150   .355413  .000160  I  .4588597  .0000077  2.5104 0.0046  I   -23.153     .265    -5.046     .298  -.151700   .354700   .4587800   -22.900    -4.700  
+941210 49696.00 I  -.151519  .000160   .358227  .000137  I  .4563534  .0000052  2.4986 0.0052  I   -23.433     .282    -4.750     .298  -.151700   .357600   .4562800   -22.800    -4.400  
+941211 49697.00 I  -.152018  .000164   .361091  .000124  I  .4538684  .0000069  2.4690 0.0037  I   -23.665     .182    -4.531     .298  -.152000   .360500   .4537800   -22.900    -4.400  
+941212 49698.00 I  -.152813  .000156   .363880  .000124  I  .4514189  .0000052  2.4288 0.0043  I   -23.655     .220    -4.470     .298  -.152600   .363300   .4513200   -22.900    -4.300  
+941213 49699.00 I  -.153503  .000156   .366531  .000131  I  .4490111  .0000052  2.3877 0.0040  I   -23.518     .220    -4.502     .298  -.153200   .366000   .4489200   -22.900    -4.400  
+941214 49700.00 I  -.153781  .000150   .369106  .000142  I  .4466544  .0000062  2.3139 0.0040  I   -23.379     .181    -4.584     .298  -.153800   .368500   .4465900   -23.000    -4.400  
+941215 49701.00 I  -.153973  .000146   .371747  .000149  I  .4443857  .0000062  2.2330 0.0043  I   -23.276     .181    -4.656     .298  -.154400   .370900   .4443300   -23.000    -4.500  
+941216 49702.00 I  -.154240  .000148   .374469  .000154  I  .4421722  .0000059  2.2006 0.0045  I   -23.179     .181    -4.710     .298  -.154900   .373300   .4421100   -23.000    -4.800  
+941217 49703.00 I  -.154662  .000116   .377245  .000159  I  .4399678  .0000064  2.2182 0.0049  I   -23.123     .164    -4.723     .298  -.155400   .375900   .4398900   -22.800    -4.900  
+941218 49704.00 I  -.155272  .000130   .380014  .000181  I  .4377180  .0000077  2.2892 0.0047  I   -23.120     .183    -4.675     .298  -.156000   .378700   .4376300   -22.700    -4.900  
+941219 49705.00 I  -.156060  .000119   .382715  .000166  I  .4353743  .0000070  2.4056 0.0047  I   -23.084     .194    -4.632     .112  -.156700   .381500   .4352700   -22.500    -5.000  
+941220 49706.00 I  -.156932  .000122   .385263  .000184  I  .4328925  .0000055  2.5650 0.0045  I   -22.946     .237    -4.670     .135  -.157400   .384300   .4327900   -22.400    -4.900  
+941221 49707.00 I  -.157709  .000119   .387602  .000171  I  .4302489  .0000056  2.7106 0.0039  I   -22.740     .237    -4.765     .135  -.158000   .386800   .4301800   -22.300    -4.700  
+941222 49708.00 I  -.158271  .000100   .389863  .000142  I  .4274840  .0000055  2.8181 0.0039  I   -22.605     .237    -4.798     .135  -.158600   .389100   .4274400   -22.300    -4.400  
+941223 49709.00 I  -.158674  .000098   .392127  .000145  I  .4246131  .0000055  2.9243 0.0043  I   -22.550     .237    -4.680     .135  -.159000   .391400   .4245700   -22.200    -4.100  
+941224 49710.00 I  -.158946  .000107   .394483  .000172  I  .4216396  .0000065  3.0178 0.0051  I   -22.525     .215    -4.434     .185  -.159200   .393700   .4215900   -22.300    -3.800  
+941225 49711.00 I  -.159032  .000143   .397043  .000170  I  .4185905  .0000087  3.0722 0.0048  I   -22.553     .791    -4.146     .298  -.159300   .396200   .4185400   -22.500    -3.500  
+941226 49712.00 I  -.158912  .000146   .399861  .000124  I  .4155133  .0000070  3.0723 0.0059  I   -22.682     .791    -3.874     .298  -.159200   .399100   .4154600   -22.600    -3.300  
+941227 49713.00 I  -.158656  .000148   .402918  .000211  I  .4124665  .0000080  3.0106 0.0054  I   -22.908     .791    -3.651     .298  -.159000   .402200   .4124000   -22.700    -3.200  
+941228 49714.00 I  -.158336  .000147   .406112  .000211  I  .4095072  .0000081  2.9037 0.0057  I   -23.150     .162    -3.573     .298  -.158700   .405500   .4094300   -22.700    -3.100  
+941229 49715.00 I  -.157891  .000153   .409294  .000210  I  .4066683  .0000082  2.7694 0.0057  I   -23.281     .162    -3.761     .298  -.158100   .408700   .4065700   -22.600    -3.200  
+941230 49716.00 I  -.156905  .000153   .412426  .000207  I  .4039580  .0000081  2.6665 0.0058  I   -23.240     .162    -4.218     .298  -.157100   .411700   .4038300   -22.300    -3.300  
+941231 49717.00 I  -.155298  .000148   .415519  .000209  I  .4013094  .0000081  2.6414 0.0059  I   -23.114     .162    -4.629     .298  -.155600   .414800   .4011600   -21.900    -3.300  
+95 1 1 49718.00 I  -.153189  .000141   .418559  .000222  I  .3986542  .0000085  2.6795 0.0055  I   -23.041     .180    -4.690     .298  -.153600   .417700   .3985000   -21.600    -3.400  
+95 1 2 49719.00 I  -.150825  .000118   .421482  .000225  I  .3959327  .0000075  2.7708 0.0064  I   -23.011     .179    -4.436     .298  -.151300   .420500   .3957900   -21.200    -3.400  
+95 1 3 49720.00 I  -.148493  .000091   .424222  .000141  I  .3931052  .0000095  2.8841 0.0059  I   -22.859     .791    -4.143     .298  -.148900   .423300   .3929800   -22.600    -3.900  
+95 1 4 49721.00 I  -.146361  .000074   .426771  .000140  I  .3901717  .0000092  2.9756 0.0069  I   -22.540     .791    -4.023     .298  -.146700   .426000   .3900700   -22.700    -3.800  
+95 1 5 49722.00 I  -.144479  .000075   .429174  .000140  I  .3871759  .0000099  3.0014 0.0069  I   -22.305     .791    -4.043     .298  -.144700   .428500   .3870900   -22.800    -3.900  
+95 1 6 49723.00 I  -.142828  .000071   .431398  .000139  I  .3841933  .0000104  2.9540 0.0075  I   -22.431     .791    -4.051     .298  -.143100   .430800   .3841100   -22.900    -3.900  
+95 1 7 49724.00 I  -.141374  .000109   .433434  .000199  I  .3812858  .0000113  2.8529 0.0096  I   -22.839     .791    -4.004     .298  -.141600   .432800   .3812000   -23.000    -3.900  
+95 1 8 49725.00 I  -.140045  .000146   .435337  .000266  I  .3785005  .0000162  2.7128 0.0069  I   -23.190     .791    -3.985     .298  -.140300   .434700   .3784200   -23.300    -4.000  
+95 1 9 49726.00 I  -.138689  .000123   .437218  .000223  I  .3758672  .0000080  2.5516 0.0091  I   -23.310     .791    -4.048     .298  -.138900   .436700   .3757900   -23.500    -4.100  
+95 110 49727.00 I  -.137139  .000123   .439265  .000231  I  .3734000  .0000081  2.3824 0.0056  I   -23.314     .791    -4.168     .298  -.137400   .438800   .3733300   -23.500    -4.300  
+95 111 49728.00 I  -.135392  .000122   .441655  .000235  I  .3710942  .0000078  2.2379 0.0054  I   -23.318     .791    -4.303     .298  -.135800   .441100   .3710100   -23.500    -4.600  
+95 112 49729.00 I  -.133709  .000124   .444245  .000232  I  .3689076  .0000072  2.1433 0.0051  I   -23.243     .791    -4.386     .298  -.134100   .443500   .3688100   -23.300    -4.800  
+95 113 49730.00 I  -.132167  .000114   .446876  .000228  I  .3667940  .0000067  2.0895 0.0055  I   -23.043     .791    -4.399     .298  -.132600   .446100   .3666900   -23.100    -5.000  
+95 114 49731.00 I  -.130820  .000091   .449483  .000178  I  .3647144  .0000083  2.0783 0.0083  I   -22.842     .791    -4.343     .298  -.131200   .448700   .3646100   -22.800    -5.100  
+95 115 49732.00 I  -.129771  .000120   .452053  .000158  I  .3626227  .0000152  2.1111 0.0073  I   -22.760     .791    -4.265     .298  -.130200   .451300   .3625200   -22.400    -5.200  
+95 116 49733.00 I  -.129050  .000211   .454549  .000164  I  .3604814  .0000120  2.1762 0.0096  I   -22.734     .483    -4.270     .144  -.129400   .453800   .3603800   -22.100    -5.200  
+95 117 49734.00 I  -.128552  .000213   .456895  .000160  I  .3582618  .0000117  2.2669 0.0085  I   -22.655     .513    -4.429     .151  -.128800   .456200   .3581600   -21.900    -5.000  
+95 118 49735.00 I  -.128075  .000216   .459036  .000143  I  .3559323  .0000119  2.4035 0.0084  I   -22.589     .513    -4.717     .151  -.128200   .458300   .3558300   -21.800    -4.800  
+95 119 49736.00 I  -.127274  .000216   .460979  .000134  I  .3534489  .0000120  2.5576 0.0077  I   -22.721     .513    -5.033     .151  -.127500   .460200   .3533500   -21.800    -4.600  
+95 120 49737.00 I  -.126173  .000222   .462741  .000144  I  .3508263  .0000097  2.6839 0.0070  I   -22.958     .513    -5.129     .151  -.126600   .461900   .3507400   -21.900    -4.300  
+95 121 49738.00 I  -.124927  .000169   .464397  .000124  I  .3480913  .0000073  2.7794 0.0060  I   -23.146     .513    -4.947     .151  -.125400   .463400   .3480000   -22.200    -4.100  
+95 122 49739.00 I  -.123597  .000096   .466094  .000096  I  .3452808  .0000070  2.8348 0.0050  I   -23.225     .276    -4.613     .114  -.124000   .465200   .3452000   -22.400    -4.000  
+95 123 49740.00 I  -.122176  .000098   .468000  .000105  I  .3424368  .0000069  2.8447 0.0053  I   -23.209     .101    -4.237     .298  -.122500   .467100   .3423600   -22.800    -3.900  
+95 124 49741.00 I  -.120697  .000099   .470190  .000110  I  .3396079  .0000079  2.8050 0.0052  I   -23.207     .101    -3.924     .298  -.120900   .469400   .3395300   -22.900    -3.900  
+95 125 49742.00 I  -.119243  .000101   .472586  .000109  I  .3368351  .0000078  2.7404 0.0055  I   -23.344     .791    -3.810     .298  -.119300   .471900   .3367500   -22.900    -3.900  
+95 126 49743.00 I  -.117856  .000104   .475101  .000110  I  .3341227  .0000077  2.6891 0.0053  I   -23.656     .791    -3.985     .298  -.117900   .474400   .3340300   -22.900    -4.100  
+95 127 49744.00 I  -.116658  .000100   .477515  .000108  I  .3314510  .0000071  2.6555 0.0060  I   -24.026     .791    -4.436     .298  -.116700   .476800   .3313600   -22.800    -4.200  
+95 128 49745.00 I  -.115778  .000118   .479680  .000113  I  .3287997  .0000093  2.6578 0.0071  I   -24.304     .128    -4.978     .298  -.115800   .479000   .3287100   -22.700    -4.200  
+95 129 49746.00 I  -.115216  .000159   .481614  .000120  I  .3261178  .0000123  2.7130 0.0058  I   -24.434     .160    -5.290     .138  -.115300   .480900   .3260400   -22.600    -4.300  
+95 130 49747.00 I  -.114986  .000179   .483338  .000179  I  .3233651  .0000069  2.7940 0.0084  I   -24.403     .180    -5.194     .143  -.115200   .482600   .3233000   -22.500    -4.200  
+95 131 49748.00 I  -.115087  .000228   .484913  .000183  I  .3205334  .0000114  2.8640 0.0066  I   -24.191     .223    -4.772     .276  -.115400   .484200   .3204800   -22.500    -4.200  
+95 2 1 49749.00 I  -.115431  .000254   .486423  .000184  I  .3176520  .0000113  2.8891 0.0082  I   -23.847     .193    -4.282     .232  -.115900   .485700   .3175900   -22.700    -4.100  
+95 2 2 49750.00 I  -.115770  .000254   .487929  .000181  I  .3147682  .0000117  2.8750 0.0081  I   -23.596     .193    -4.132     .232  -.116200   .487200   .3146800   -23.200    -4.200  
+95 2 3 49751.00 I  -.115750  .000254   .489384  .000183  I  .3119068  .0000116  2.8464 0.0085  I   -23.576     .193    -4.355     .232  -.116300   .488600   .3118000   -23.400    -4.200  
+95 2 4 49752.00 I  -.115037  .000257   .490757  .000184  I  .3090844  .0000123  2.7908 0.0092  I   -23.713     .190    -4.654     .237  -.115800   .489900   .3089800   -23.800    -4.300  
+95 2 5 49753.00 I  -.113739  .000256   .492107  .000188  I  .3063359  .0000144  2.7030 0.0088  I   -23.852     .186    -4.843     .246  -.114600   .491300   .3062500   -24.100    -4.300  
+95 2 6 49754.00 I  -.111844  .000240   .493562  .000164  I  .3036856  .0000125  2.5936 0.0097  I   -23.933     .115    -4.939     .298  -.112700   .492800   .3036200   -24.200    -4.500  
+95 2 7 49755.00 I  -.109299  .000204   .495255  .000188  I  .3011561  .0000131  2.4622 0.0091  I   -24.006     .132    -5.004     .298  -.110100   .494600   .3011100   -24.300    -4.700  
+95 2 8 49756.00 I  -.106276  .000199   .497280  .000189  I  .2987650  .0000133  2.3196 0.0093  I   -24.099     .132    -5.090     .298  -.107000   .496600   .2987300   -24.200    -5.000  
+95 2 9 49757.00 I  -.103092  .000192   .499653  .000191  I  .2965034  .0000133  2.2176 0.0094  I   -24.164     .132    -5.204     .298  -.103800   .498900   .2964500   -24.100    -5.200  
+95 210 49758.00 I  -.100159  .000195   .502148  .000184  I  .2943105  .0000133  2.1735 0.0092  I   -24.165     .132    -5.293     .298  -.100900   .501300   .2942500   -23.900    -5.500  
+95 211 49759.00 I  -.097808  .000179   .504465  .000170  I  .2921455  .0000127  2.1621 0.0104  I   -24.181     .132    -5.306     .298  -.098400   .503500   .2920700   -23.600    -5.600  
+95 212 49760.00 I  -.096094  .000176   .506447  .000116  I  .2899714  .0000160  2.1955 0.0081  I   -24.282     .370    -5.286     .298  -.096600   .505500   .2899000   -23.400    -5.800  
+95 213 49761.00 I  -.094893  .000205   .508087  .000126  I  .2877376  .0000100  2.2797 0.0095  I   -24.354     .264    -5.329     .130  -.095200   .507300   .2876600   -23.200    -5.800  
+95 214 49762.00 I  -.093920  .000195   .509472  .000118  I  .2853989  .0000102  2.4033 0.0071  I   -24.172     .262    -5.453     .126  -.094100   .508700   .2853100   -23.200    -5.700  
+95 215 49763.00 I  -.092755  .000186   .510757  .000109  I  .2829181  .0000102  2.5657 0.0070  I   -23.653     .262    -5.564     .126  -.092800   .510100   .2828300   -23.200    -5.700  
+95 216 49764.00 I  -.091000  .000187   .512165  .000108  I  .2802679  .0000095  2.7271 0.0066  I   -23.077     .262    -5.637     .126  -.091300   .511400   .2801800   -23.300    -5.600  
+95 217 49765.00 I  -.088903  .000188   .513700  .000117  I  .2774850  .0000085  2.8252 0.0071  I   -22.779     .262    -5.567     .126  -.089500   .512800   .2774100   -23.500    -5.400  
+95 218 49766.00 I  -.086820  .000187   .515241  .000112  I  .2746413  .0000105  2.8517 0.0077  I   -22.832     .187    -5.402     .146  -.087700   .514200   .2745700   -23.700    -5.400  
+95 219 49767.00 I  -.084959  .000152   .516735  .000116  I  .2718026  .0000128  2.8151 0.0059  I   -23.087     .259    -5.282     .117  -.085900   .515600   .2717300   -23.900    -5.300  
+95 220 49768.00 I  -.083343  .000119   .518078  .000151  I  .2690284  .0000055  2.7265 0.0072  I   -23.374     .791    -5.248     .298  -.084200   .517000   .2689500   -23.900    -5.300  
+95 221 49769.00 I  -.081869  .000138   .519239  .000157  I  .2663575  .0000066  2.6142 0.0043  I   -23.630     .791    -5.279     .298  -.082600   .518400   .2662800   -23.800    -5.400  
+95 222 49770.00 I  -.080396  .000138   .520331  .000168  I  .2637974  .0000066  2.5099 0.0048  I   -23.905     .137    -5.384     .298  -.081000   .519800   .2637300   -23.500    -5.400  
+95 223 49771.00 I  -.078734  .000126   .521570  .000165  I  .2613248  .0000071  2.4445 0.0045  I   -24.254     .137    -5.613     .298  -.079200   .521300   .2612700   -23.200    -5.500  
+95 224 49772.00 I  -.076638  .000133   .523062  .000185  I  .2588847  .0000062  2.4502 0.0051  I   -24.606     .137    -5.941     .298  -.077000   .522800   .2588400   -22.800    -5.600  
+95 225 49773.00 I  -.073891  .000140   .524836  .000192  I  .2564009  .0000074  2.5262 0.0079  I   -24.792     .137    -6.236     .298  -.074300   .524500   .2563800   -22.400    -5.600  
+95 226 49774.00 I  -.070608  .000164   .526809  .000182  I  .2538179  .0000145  2.6453 0.0072  I   -24.734     .170    -6.357     .298  -.071300   .526200   .2538100   -22.100    -5.600  
+95 227 49775.00 I  -.067041  .000180   .528787  .000183  I  .2511060  .0000123  2.7774 0.0127  I   -24.473     .124    -6.212     .298  -.067900   .528000   .2511100   -21.900    -5.600  
+95 228 49776.00 I  -.063404  .000248   .530614  .000173  I  .2482696  .0000208  2.8902 0.0119  I   -24.144     .791    -5.883     .298  -.064300   .529700   .2482600   -21.800    -5.500  
+95 3 1 49777.00 I  -.059834  .000244   .532244  .000179  I  .2453351  .0000204  2.9747 0.0138  I   -23.881     .791    -5.624     .298  -.060400   .531300   .2452600   -23.600    -5.400  
+95 3 2 49778.00 I  -.056421  .000241   .533667  .000166  I  .2423301  .0000182  3.0295 0.0134  I   -23.719     .791    -5.663     .298  -.057000   .532700   .2422100   -23.700    -5.400  
+95 3 3 49779.00 I  -.053150  .000235   .534943  .000164  I  .2392855  .0000173  3.0562 0.0099  I   -23.603     .791    -5.974     .298  -.053700   .534200   .2391700   -24.000    -5.500  
+95 3 4 49780.00 I  -.049859  .000199   .536159  .000119  I  .2362316  .0000078  3.0412 0.0093  I   -23.497     .791    -6.334     .298  -.050300   .535400   .2361300   -24.100    -5.600  
+95 3 5 49781.00 I  -.046533  .000180   .537369  .000118  I  .2332219  .0000070  2.9698 0.0050  I   -23.450     .791    -6.566     .298  -.047000   .536600   .2331600   -24.300    -5.800  
+95 3 6 49782.00 I  -.043210  .000146   .538620  .000118  I  .2303048  .0000063  2.8598 0.0041  I   -23.513     .791    -6.647     .298  -.043600   .537800   .2302700   -24.300    -6.100  
+95 3 7 49783.00 I  -.039960  .000117   .539942  .000115  I  .2275100  .0000044  2.7265 0.0041  I   -23.639     .101    -6.679     .298  -.040400   .539000   .2274800   -24.100    -6.300  
+95 3 8 49784.00 I  -.036857  .000115   .541301  .000111  I  .2248388  .0000051  2.6323 0.0036  I   -23.701     .101    -6.770     .298  -.037300   .540300   .2247700   -23.700    -6.600  
+95 3 9 49785.00 I  -.034054  .000114   .542584  .000113  I  .2222167  .0000058  2.6244 0.0038  I   -23.581     .101    -6.931     .298  -.034500   .541600   .2221100   -23.300    -6.900  
+95 310 49786.00 I  -.031586  .000115   .543697  .000122  I  .2195729  .0000057  2.6680 0.0053  I   -23.314     .101    -7.060     .298  -.032000   .542800   .2194600   -23.000    -7.000  
+95 311 49787.00 I  -.029449  .000176   .544584  .000129  I  .2168735  .0000089  2.7332 0.0071  I   -23.030     .791    -7.060     .298  -.029900   .543800   .2167900   -22.600    -7.200  
+95 312 49788.00 I  -.027596  .000253   .545229  .000117  I  .2141008  .0000131  2.8155 0.0071  I   -22.835     .268    -6.971     .149  -.028000   .544400   .2140400   -22.300    -7.200  
+95 313 49789.00 I  -.025967  .000243   .545574  .000124  I  .2112397  .0000110  2.9063 0.0085  I   -22.769     .247    -6.939     .135  -.026300   .544800   .2112100   -22.100    -7.300  
+95 314 49790.00 I  -.024318  .000251   .545700  .000124  I  .2082900  .0000107  2.9916 0.0074  I   -22.794     .247    -7.025     .135  -.024500   .545000   .2082700   -22.100    -7.200  
+95 315 49791.00 I  -.022230  .000250   .545858  .000126  I  .2052493  .0000099  3.0986 0.0086  I   -22.814     .247    -7.093     .135  -.022400   .545200   .2052000   -22.200    -7.200  
+95 316 49792.00 I  -.019570  .000241   .546291  .000101  I  .2020894  .0000134  3.2157 0.0083  I   -22.722     .247    -7.002     .135  -.019900   .545500   .2020200   -22.300    -7.100  
+95 317 49793.00 I  -.016638  .000245   .547022  .000103  I  .1988327  .0000133  3.2882 0.0098  I   -22.567     .247    -6.745     .135  -.017100   .546100   .1987600   -22.500    -7.100  
+95 318 49794.00 I  -.013712  .000197   .547927  .000102  I  .1955371  .0000144  3.2882 0.0112  I   -22.488     .134    -6.507     .298  -.014100   .547000   .1954600   -22.700    -7.000  
+95 319 49795.00 I  -.010970  .000160   .548911  .000093  I  .1922824  .0000181  3.2097 0.0097  I   -22.534     .791    -6.498     .298  -.011300   .548000   .1922100   -22.800    -7.100  
+95 320 49796.00 I  -.008609  .000144   .549912  .000087  I  .1891428  .0000129  3.0559 0.0111  I   -22.617     .166    -6.743     .298  -.009000   .549000   .1890800   -22.800    -7.100  
+95 321 49797.00 I  -.006664  .000206   .550852  .000085  I  .1861914  .0000129  2.8397 0.0080  I   -22.639     .166    -7.087     .298  -.007200   .550000   .1861400   -22.700    -7.300  
+95 322 49798.00 I  -.004915  .000209   .551625  .000086  I  .1834651  .0000095  2.6183 0.0080  I   -22.633     .175    -7.372     .298  -.005500   .550700   .1834100   -22.300    -7.300  
+95 323 49799.00 I  -.003080  .000214   .552167  .000111  I  .1809253  .0000093  2.4855 0.0066  I   -22.722     .175    -7.560     .298  -.003700   .551200   .1808400   -22.100    -7.400  
+95 324 49800.00 I  -.000718  .000208   .552558  .000117  I  .1784517  .0000093  2.4799 0.0060  I   -22.899     .175    -7.700     .298  -.001100   .551600   .1783400   -21.800    -7.400  
+95 325 49801.00 I   .002665  .000211   .552945  .000137  I  .1759392  .0000077  2.5532 0.0065  I   -23.001     .175    -7.829     .298   .002200   .552000   .1758500   -21.700    -7.500  
+95 326 49802.00 I   .007068  .000233   .553526  .000191  I  .1733344  .0000092  2.6591 0.0051  I   -22.894     .160    -7.922     .298   .006500   .552600   .1732600   -21.600    -7.400  
+95 327 49803.00 I   .012125  .000209   .554424  .000185  I  .1706200  .0000066  2.7678 0.0051  I   -22.605     .144    -7.910     .298   .011300   .553600   .1705600   -21.600    -7.300  
+95 328 49804.00 I   .017209  .000170   .555609  .000213  I  .1678072  .0000045  2.8512 0.0040  I   -22.317     .118    -7.772     .298   .016300   .554800   .1677500   -21.800    -7.300  
+95 329 49805.00 I   .021783  .000146   .556881  .000202  I  .1649300  .0000045  2.8974 0.0035  I   -22.216     .118    -7.598     .298   .021000   .556000   .1648600   -22.100    -7.300  
+95 330 49806.00 I   .025737  .000154   .557853  .000196  I  .1620269  .0000053  2.9002 0.0034  I   -22.259     .118    -7.539     .298   .025100   .557000   .1619500   -22.500    -7.300  
+95 331 49807.00 I   .029145  .000154   .558411  .000200  I  .1591455  .0000051  2.8554 0.0036  I   -22.270     .118    -7.660     .298   .028700   .557600   .1590500   -22.700    -7.400  
+95 4 1 49808.00 I   .032223  .000131   .558606  .000178  I  .1563266  .0000050  2.7793 0.0064  I   -22.159     .104    -7.887     .298   .031800   .557800   .1562300   -23.000    -7.500  
+95 4 2 49809.00 I   .035253  .000123   .558563  .000124  I  .1535919  .0000118  2.6884 0.0074  I   -22.027     .791    -8.095     .298   .034800   .557700   .1535000   -23.000    -7.700  
+95 4 3 49810.00 I   .038395  .000124   .558415  .000121  I  .1509506  .0000139  2.5952 0.0092  I   -22.004     .791    -8.223     .298   .037700   .557500   .1508600   -22.900    -8.000  
+95 4 4 49811.00 I   .041610  .000121   .558191  .000127  I  .1484005  .0000140  2.5050 0.0100  I   -22.061     .791    -8.307     .298   .041000   .557300   .1483300   -22.600    -8.200  
+95 4 5 49812.00 I   .044799  .000114   .557877  .000129  I  .1459346  .0000144  2.4328 0.0104  I   -22.084     .791    -8.415     .298   .044300   .557000   .1458700   -22.200    -8.400  
+95 4 6 49813.00 I   .048055  .000105   .557657  .000118  I  .1435147  .0000153  2.4210 0.0106  I   -22.077     .791    -8.566     .298   .047800   .556900   .1434500   -21.800    -8.600  
+95 4 7 49814.00 I   .051330  .000106   .557646  .000117  I  .1410720  .0000155  2.4708 0.0103  I   -22.107     .791    -8.676     .298   .051100   .556800   .1410100   -21.400    -8.700  
+95 4 8 49815.00 I   .054471  .000086   .557796  .000160  I  .1385650  .0000137  2.5448 0.0119  I   -22.142     .791    -8.634     .298   .054200   .557000   .1385200   -21.000    -8.700  
+95 4 9 49816.00 I   .057417  .000111   .557972  .000177  I  .1359808  .0000180  2.6234 0.0110  I   -22.082     .225    -8.449     .298   .057100   .557200   .1359400   -20.800    -8.600  
+95 410 49817.00 I   .060287  .000181   .558056  .000180  I  .1333095  .0000173  2.7281 0.0121  I   -21.956     .318    -8.302     .298   .059800   .557300   .1332500   -20.600    -8.600  
+95 411 49818.00 I   .063186  .000199   .557998  .000181  I  .1305124  .0000162  2.8696 0.0114  I   -21.911     .489    -8.361     .298   .062500   .557300   .1304400   -20.700    -8.500  
+95 412 49819.00 I   .066106  .000203   .557795  .000180  I  .1275648  .0000150  3.0278 0.0112  I   -21.966     .505    -8.550     .298   .065300   .557100   .1274900   -21.000    -8.400  
+95 413 49820.00 I   .069162  .000206   .557477  .000186  I  .1244732  .0000155  3.1369 0.0108  I   -21.985     .505    -8.591     .298   .068300   .556600   .1244100   -21.400    -8.400  
+95 414 49821.00 I   .072319  .000220   .557059  .000188  I  .1213199  .0000156  3.1592 0.0109  I   -21.738     .505    -8.385     .298   .071600   .556200   .1212700   -21.700    -8.400  
+95 415 49822.00 I   .075646  .000222   .556623  .000130  I  .1181739  .0000152  3.1240 0.0108  I   -21.434     .568    -8.108     .298   .075100   .555700   .1181200   -22.100    -8.400  
+95 416 49823.00 I   .079115  .000187   .556274  .000143  I  .1150863  .0000150  3.0455 0.0085  I   -21.370     .639    -8.040     .298   .078700   .555400   .1150300   -22.400    -8.500  
+95 417 49824.00 I   .082588  .000187   .556036  .000088  I  .1120940  .0000074  2.9337 0.0082  I   -21.526     .404    -8.275     .298   .082200   .555100   .1120400   -22.400    -8.500  
+95 418 49825.00 I   .085904  .000165   .555871  .000118  I  .1092209  .0000067  2.8158 0.0048  I   -21.732     .262    -8.624     .298   .085600   .555000   .1091800   -22.400    -8.700  
+95 419 49826.00 I   .088995  .000164   .555759  .000123  I  .1064576  .0000061  2.7122 0.0045  I   -21.871     .428    -8.839     .298   .088700   .555000   .1064300   -22.100    -8.800  
+95 420 49827.00 I   .091859  .000167   .555687  .000126  I  .1037831  .0000061  2.6488 0.0041  I   -21.966     .428    -8.843     .298   .091600   .554900   .1037200   -21.800    -8.800  
+95 421 49828.00 I   .094840  .000154   .555488  .000128  I  .1011338  .0000055  2.6640 0.0041  I   -22.131     .428    -8.791     .298   .094700   .554700   .1010300   -21.400    -8.800  
+95 422 49829.00 I   .098282  .000142   .555027  .000123  I  .0984390  .0000055  2.7276 0.0039  I   -22.304     .391    -8.765     .298   .097900   .554300   .0983000   -21.100    -8.800  
+95 423 49830.00 I   .101923  .000120   .554313  .000155  I  .0956750  .0000056  2.8020 0.0040  I   -22.350     .423    -8.753     .298   .101300   .553600   .0955200   -20.900    -8.700  
+95 424 49831.00 I   .105677  .000118   .553421  .000134  I  .0928361  .0000059  2.8729 0.0043  I   -22.220     .400    -8.741     .298   .105100   .552800   .0926800   -20.900    -8.600  
+95 425 49832.00 I   .109648  .000110   .552409  .000117  I  .0899362  .0000065  2.9226 0.0044  I   -21.969     .103    -8.709     .298   .109200   .551800   .0897900   -21.100    -8.600  
+95 426 49833.00 I   .113828  .000093   .551295  .000118  I  .0870060  .0000066  2.9271 0.0059  I   -21.778     .103    -8.637     .298   .113400   .550600   .0869000   -21.300    -8.500  
+95 427 49834.00 I   .118052  .000094   .550083  .000113  I  .0841042  .0000098  2.8651 0.0058  I   -21.811     .103    -8.532     .298   .117600   .549400   .0840200   -21.700    -8.600  
+95 428 49835.00 I   .122201  .000095   .548781  .000115  I  .0812932  .0000096  2.7508 0.0070  I   -22.006     .128    -8.444     .298   .121800   .548000   .0812200   -22.100    -8.600  
+95 429 49836.00 I   .126200  .000085   .547400  .000115  I  .0786103  .0000100  2.6133 0.0070  I   -22.183     .128    -8.443     .298   .125900   .546600   .0785400   -22.300    -8.700  
+95 430 49837.00 I   .130004  .000104   .545963  .000085  I  .0760682  .0000102  2.4709 0.0063  I   -22.260     .791    -8.560     .298   .129800   .545100   .0759800   -22.500    -8.900  
+95 5 1 49838.00 I   .133625  .000099   .544472  .000076  I  .0736575  .0000077  2.3614 0.0064  I   -22.260     .791    -8.763     .298   .133600   .543700   .0735500   -22.500    -9.100  
+95 5 2 49839.00 I   .137151  .000102   .542978  .000085  I  .0713226  .0000078  2.3203 0.0044  I   -22.173     .791    -8.993     .298   .137100   .542200   .0712000   -22.300    -9.200  
+95 5 3 49840.00 I   .140652  .000105   .541540  .000084  I  .0689971  .0000041  2.3387 0.0067  I   -21.978     .791    -9.223     .298   .140500   .540700   .0688500   -21.800    -9.200  
+95 5 4 49841.00 I   .144150  .000115   .540101  .000081  I  .0666340  .0000108  2.3906 0.0064  I   -21.787     .791    -9.398     .298   .144000   .539100   .0664900   -21.400    -9.300  
+95 5 5 49842.00 I   .147689  .000127   .538598  .000080  I  .0642119  .0000122  2.4545 0.0086  I   -21.736     .791    -9.496     .298   .147600   .537600   .0640600   -21.000    -9.300  
+95 5 6 49843.00 I   .151386  .000099   .537040  .000079  I  .0617241  .0000135  2.5210 0.0174  I   -21.732     .791    -9.460     .298   .151400   .536000   .0615800   -20.600    -9.200  
+95 5 7 49844.00 I   .155353  .000183   .535497  .000096  I  .0591702  .0000325  2.5865 0.0125  I   -21.524     .791    -9.247     .298   .155400   .534400   .0590300   -20.500    -9.100  
+95 5 8 49845.00 I   .159560  .000268   .533999  .000154  I  .0565425  .0000211  2.6779 0.0194  I   -21.105     .160    -8.972     .298   .159500   .533000   .0564200   -20.400    -9.000  
+95 5 9 49846.00 I   .163824  .000268   .532527  .000154  I  .0538021  .0000213  2.8062 0.0135  I   -20.845     .149    -8.852     .298   .163600   .531600   .0536900   -20.600    -8.900  
+95 510 49847.00 I   .167886  .000258   .531033  .000152  I  .0509297  .0000169  2.9355 0.0136  I   -21.061     .149    -8.949     .298   .167600   .530200   .0508200   -21.000    -8.900  
+95 511 49848.00 I   .171598  .000255   .529473  .000154  I  .0479442  .0000168  3.0254 0.0118  I   -21.632     .149    -9.068     .298   .171400   .528700   .0478400   -21.400    -8.800  
+95 512 49849.00 I   .174931  .000252   .527820  .000159  I  .0449067  .0000164  3.0320 0.0117  I   -22.134     .149    -8.967     .298   .175000   .527100   .0448200   -22.000    -8.700  
+95 513 49850.00 I   .178042  .000252   .526120  .000170  I  .0419048  .0000164  2.9650 0.0105  I   -22.377     .149    -8.702     .298   .178400   .525500   .0418100   -22.300    -8.700  
+95 514 49851.00 I   .181104  .000127   .524458  .000133  I  .0389846  .0000132  2.8743 0.0091  I   -22.490     .138    -8.566     .298   .181500   .523800   .0388900   -22.600    -8.900  
+95 515 49852.00 I   .184123  .000119   .522872  .000105  I  .0361612  .0000079  2.7684 0.0077  I   -22.604     .791    -8.710     .298   .184300   .522100   .0360800   -22.700    -8.900  
+95 516 49853.00 I   .187027  .000113   .521341  .000116  I  .0334480  .0000080  2.6617 0.0047  I   -22.693     .791    -8.980     .298   .186900   .520400   .0333900   -22.700    -8.900  
+95 517 49854.00 I   .189688  .000112   .519782  .000143  I  .0308279  .0000050  2.5846 0.0044  I   -22.662     .791    -9.132     .298   .189300   .518700   .0307800   -22.500    -8.900  
+95 518 49855.00 I   .191958  .000104   .518055  .000144  I  .0282646  .0000035  2.5502 0.0036  I   -22.480     .791    -9.105     .298   .191600   .516900   .0282100   -22.100    -8.900  
+95 519 49856.00 I   .193890  .000110   .516107  .000149  I  .0257150  .0000051  2.5534 0.0030  I   -22.234     .791    -9.000     .298   .193700   .514900   .0256500   -21.700    -8.800  
+95 520 49857.00 I   .195623  .000096   .513903  .000139  I  .0231537  .0000049  2.5689 0.0065  I   -22.089     .791    -8.885     .298   .195700   .512800   .0230700   -21.500    -8.800  
+95 521 49858.00 I   .197336  .000114   .511469  .000163  I  .0205831  .0000119  2.5666 0.0057  I   -22.162     .791    -8.757     .298   .197600   .510400   .0204900   -21.300    -8.700  
+95 522 49859.00 I   .199225  .000111   .508960  .000140  I  .0180327  .0000104  2.5277 0.0095  I   -22.379     .261    -8.644     .298   .199500   .508100   .0179500   -21.300    -8.600  
+95 523 49860.00 I   .201339  .000112   .506500  .000116  I  .0155409  .0000147  2.4494 0.0094  I   -22.542     .257    -8.621     .298   .201300   .505700   .0154600   -21.500    -8.500  
+95 524 49861.00 I   .203530  .000114   .504116  .000113  I  .0131463  .0000156  2.3341 0.0103  I   -22.610     .257    -8.696     .298   .203100   .503400   .0130500   -21.800    -8.600  
+95 525 49862.00 I   .205615  .000105   .501788  .000100  I  .0108704  .0000145  2.2256 0.0113  I   -22.787     .257    -8.781     .298   .205000   .501000   .0107500   -22.300    -8.600  
+95 526 49863.00 I   .207613  .000104   .499451  .000097  I  .0086859  .0000164  2.1446 0.0109  I   -23.171     .257    -8.783     .298   .207000   .498600   .0085600   -22.700    -8.700  
+95 527 49864.00 I   .209581  .000104   .497026  .000089  I  .0065837  .0000164  2.0562 0.0109  I   -23.641     .257    -8.767     .298   .209000   .496100   .0064700   -23.100    -8.700  
+95 528 49865.00 I   .211530  .000060   .494489  .000106  I  .0045724  .0000143  1.9711 0.0095  I   -23.974     .252    -8.865     .298   .211100   .493600   .0044800   -23.300    -8.800  
+95 529 49866.00 I   .213501  .000074   .491824  .000139  I  .0026326  .0000096  1.9128 0.0085  I   -24.035     .791    -9.068     .298   .213200   .491000   .0025600   -23.400    -9.000  
+95 530 49867.00 I   .215531  .000072   .489023  .000150  I  .0007373  .0000092  1.8831 0.0070  I   -23.855     .791    -9.227     .298   .215300   .488400   .0006700   -23.300    -9.100  
+95 531 49868.00 I   .217624  .000069   .486128  .000154  I -.0011437  .0000102  1.8837 0.0071  I   -23.581     .791    -9.231     .298   .217400   .485500  -.0012200   -23.100    -9.100  
+95 6 1 49869.00 I   .219839  .000068   .483227  .000154  I -.0030435  .0000107  1.9245 0.0074  I   -23.435     .791    -9.142     .298   .219700   .482600  -.0031400   -22.800    -9.100  
+95 6 2 49870.00 I   .222301  .000067   .480281  .000151  I -.0050071  .0000107  2.0086 0.0076  I   -23.587     .791    -9.132     .298   .222200   .479500  -.0051200   -22.600    -9.100  
+95 6 3 49871.00 I   .225108  .000070   .477193  .000153  I -.0070698  .0000108  2.1199 0.0093  I   -23.919     .791    -9.219     .298   .224900   .476300  -.0071900   -22.400    -9.100  
+95 6 4 49872.00 I   .228205  .000095   .473977  .000167  I -.0092459  .0000153  2.2283 0.0082  I   -24.056     .791    -9.259     .298   .227900   .473100  -.0093500   -22.400    -8.900  
+95 6 5 49873.00 I   .231450  .000116   .470731  .000189  I -.0115205  .0000124  2.3191 0.0097  I   -23.793     .147    -9.158     .298   .231000   .469800  -.0116100   -22.700    -8.800  
+95 6 6 49874.00 I   .234633  .000122   .467602  .000186  I -.0138775  .0000118  2.3900 0.0080  I   -23.391     .147    -8.974     .298   .234200   .466700  -.0139500   -22.900    -8.800  
+95 6 7 49875.00 I   .237641  .000132   .464714  .000187  I -.0162951  .0000101  2.4446 0.0080  I   -23.280     .142    -8.779     .298   .237400   .463800  -.0163700   -23.400    -8.800  
+95 6 8 49876.00 I   .240594  .000133   .462046  .000191  I -.0187550  .0000109  2.4642 0.0074  I   -23.560     .142    -8.498     .298   .240300   .461000  -.0188200   -23.900    -8.700  
+95 6 9 49877.00 I   .243439  .000131   .459261  .000186  I -.0212012  .0000107  2.4171 0.0074  I   -23.983     .142    -8.092     .298   .243100   .458200  -.0212500   -24.300    -8.700  
+95 610 49878.00 I   .246051  .000125   .456323  .000186  I -.0235719  .0000099  2.3183 0.0082  I   -24.310     .142    -7.727     .298   .245600   .455300  -.0236100   -24.700    -8.700  
+95 611 49879.00 I   .248386  .000079   .453343  .000162  I -.0258309  .0000123  2.1989 0.0084  I   -24.504     .136    -7.645     .298   .247900   .452300  -.0258700   -24.900    -8.800  
+95 612 49880.00 I   .250452  .000088   .450340  .000155  I -.0279733  .0000136  2.0905 0.0089  I   -24.670     .285    -7.906     .298   .250100   .449300  -.0280200   -24.900    -8.800  
+95 613 49881.00 I   .252350  .000068   .447248  .000142  I -.0300289  .0000130  2.0333 0.0092  I   -24.913     .427    -8.312     .298   .252100   .446300  -.0300900   -24.900    -8.800  
+95 614 49882.00 I   .254267  .000068   .444027  .000135  I -.0320660  .0000125  2.0546 0.0095  I   -25.237     .427    -8.609     .298   .254200   .443200  -.0321300   -24.700    -8.700  
+95 615 49883.00 I   .256310  .000064   .440739  .000132  I -.0341466  .0000138  2.1014 0.0093  I   -25.532     .427    -8.757     .298   .256200   .440000  -.0342100   -24.500    -8.600  
+95 616 49884.00 I   .258509  .000068   .437481  .000131  I -.0362715  .0000139  2.1562 0.0098  I   -25.586     .427    -8.817     .298   .258400   .436800  -.0363500   -24.300    -8.500  
+95 617 49885.00 I   .260839  .000064   .434277  .000136  I -.0384633  .0000139  2.2237 0.0137  I   -25.507     .386    -8.823     .298   .260600   .433600  -.0385600   -24.300    -8.300  
+95 618 49886.00 I   .263215  .000076   .431106  .000130  I -.0407064  .0000235  2.2537 0.0137  I   -25.630     .389    -8.728     .298   .262900   .430300  -.0408200   -24.300    -8.200  
+95 619 49887.00 I   .265545  .000112   .427896  .000148  I -.0429534  .0000237  2.2319 0.0159  I   -26.013     .324    -8.539     .298   .265100   .427000  -.0430700   -24.600    -8.100  
+95 620 49888.00 I   .267697  .000117   .424553  .000151  I -.0451547  .0000214  2.1635 0.0151  I   -26.320     .324    -8.406     .298   .267300   .423600  -.0452600   -25.100    -8.000  
+95 621 49889.00 I   .269577  .000126   .421018  .000161  I -.0472665  .0000186  2.0535 0.0155  I   -26.224     .324    -8.444     .298   .269200   .420100  -.0473600   -25.500    -8.000  
+95 622 49890.00 I   .271154  .000126   .417398  .000173  I -.0492487  .0000225  1.9044 0.0151  I   -25.768     .324    -8.550     .298   .270800   .416500  -.0493200   -26.100    -8.000  
+95 623 49891.00 I   .272392  .000107   .413796  .000169  I -.0510626  .0000238  1.7170 0.0162  I   -25.413     .359    -8.583     .298   .272100   .412900  -.0511200   -26.600    -8.200  
+95 624 49892.00 I   .273290  .000101   .410267  .000169  I -.0526763  .0000232  1.5102 0.0142  I   -25.425     .359    -8.560     .298   .273000   .409400  -.0527200   -26.900    -8.300  
+95 625 49893.00 I   .273966  .000136   .406811  .000169  I -.0540931  .0000154  1.3337 0.0133  I   -25.639    1.012    -8.617     .298   .273700   .405800  -.0541400   -27.100    -8.400  
+95 626 49894.00 I   .274637  .000145   .403337  .000153  I -.0553614  .0000129  1.2104 0.0099  I   -25.850     .866    -8.795     .404   .274400   .402300  -.0554100   -27.200    -8.500  
+95 627 49895.00 I   .275453  .000145   .399721  .000150  I -.0565304  .0000126  1.1366 0.0085  I   -26.137     .893    -8.961     .319   .275100   .398700  -.0565800   -27.100    -8.600  
+95 628 49896.00 I   .276446  .000144   .395963  .000144  I -.0576556  .0000111  1.1259 0.0087  I   -26.700     .893    -8.958     .319   .276100   .395100  -.0577100   -26.900    -8.600  
+95 629 49897.00 I   .277574  .000151   .392210  .000148  I -.0588041  .0000121  1.1811 0.0082  I   -27.309     .893    -8.713     .319   .277300   .391400  -.0588700   -26.700    -8.400  
+95 630 49898.00 I   .278839  .000162   .388463  .000144  I -.0600344  .0000121  1.2863 0.0085  I   -27.865     .893    -8.474     .319   .278600   .387600  -.0601100   -26.500    -8.300  
+95 7 1 49899.00 I   .280135  .000175   .384623  .000138  I -.0613867  .0000118  1.4216 0.0106  I   -28.378     .861    -8.417     .354   .279900   .383700  -.0614700   -26.400    -8.200  
+95 7 2 49900.00 I   .281307  .000150   .380660  .000089  I -.0628822  .0000174  1.5707 0.0123  I   -28.702     .932    -8.450     .298   .281100   .379700  -.0629700   -26.400    -8.000  
+95 7 3 49901.00 I   .282279  .000132   .376569  .000109  I -.0645201  .0000215  1.6960 0.0134  I   -28.656     .791    -8.429     .298   .282100   .375600  -.0646000   -26.600    -7.900  
+95 7 4 49902.00 I   .283155  .000160   .372366  .000099  I -.0662537  .0000205  1.7600 0.0130  I   -28.335     .288    -8.308     .298   .283000   .371400  -.0663200   -27.000    -7.900  
+95 7 5 49903.00 I   .284080  .000157   .368160  .000105  I -.0680185  .0000146  1.7593 0.0126  I   -28.129     .288    -8.148     .298   .283900   .367200  -.0680800   -27.500    -7.900  
+95 7 6 49904.00 I   .284998  .000146   .364071  .000101  I -.0697585  .0000148  1.7171 0.0117  I   -28.393     .288    -8.005     .298   .284700   .363000  -.0698300   -27.900    -8.000  
+95 7 7 49905.00 I   .285752  .000113   .360096  .000104  I -.0714445  .0000182  1.6502 0.0113  I   -29.156     .288    -7.910     .298   .285500   .358900  -.0715200   -28.500    -8.000  
+95 7 8 49906.00 I   .286264  .000126   .356144  .000107  I -.0730519  .0000172  1.5624 0.0128  I   -30.083     .288    -7.923     .298   .286100   .354900  -.0731300   -28.900    -8.100  
+95 7 9 49907.00 I   .286592  .000155   .352185  .000101  I -.0745701  .0000179  1.4770 0.0111  I   -30.805     .580    -8.127     .298   .286500   .351000  -.0746500   -29.300    -8.300  
+95 710 49908.00 I   .286847  .000152   .348271  .000101  I -.0760160  .0000140  1.4218 0.0117  I   -31.152     .672    -8.474     .147   .286800   .347200  -.0760900   -29.500    -8.400  
+95 711 49909.00 I   .287167  .000150   .344456  .000090  I -.0774314  .0000150  1.4198 0.0101  I   -31.126     .549    -8.724     .134   .287100   .343500  -.0774900   -29.600    -8.400  
+95 712 49910.00 I   .287522  .000146   .340770  .000095  I -.0788750  .0000147  1.4759 0.0104  I   -30.798     .549    -8.676     .134   .287200   .339900  -.0789200   -29.800    -8.400  
+95 713 49911.00 I   .287691  .000147   .337184  .000124  I -.0803920  .0000144  1.5585 0.0103  I   -30.419     .549    -8.516     .134   .287400   .336400  -.0804300   -29.700    -8.400  
+95 714 49912.00 I   .287724  .000131   .333635  .000125  I -.0819965  .0000143  1.6546 0.0091  I   -30.024     .549    -8.405     .134   .287400   .332800  -.0820500   -29.500    -8.300  
+95 715 49913.00 I   .287795  .000125   .330040  .000134  I -.0836972  .0000110  1.7387 0.0103  I   -29.694     .325    -8.404     .124   .287500   .329200  -.0837700   -29.500    -8.200  
+95 716 49914.00 I   .287949  .000102   .326344  .000170  I -.0854542  .0000149  1.7638 0.0079  I   -29.729     .791    -8.440     .298   .287600   .325500  -.0855200   -29.700    -8.200  
+95 717 49915.00 I   .288082  .000135   .322556  .000174  I -.0872007  .0000114  1.7167 0.0097  I   -30.260     .791    -8.406     .298   .287700   .321700  -.0872600   -30.000    -8.200  
+95 718 49916.00 I   .288062  .000140   .318743  .000174  I -.0888665  .0000124  1.6065 0.0080  I   -30.944     .791    -8.365     .298   .287800   .317800  -.0889200   -30.300    -8.300  
+95 719 49917.00 I   .287868  .000139   .314950  .000156  I -.0904009  .0000113  1.4580 0.0084  I   -31.292     .791    -8.442     .298   .287700   .313900  -.0904600   -30.700    -8.400  
+95 720 49918.00 I   .287689  .000148   .311145  .000156  I -.0917787  .0000114  1.2981 0.0075  I   -31.247     .791    -8.575     .298   .287600   .309900  -.0918600   -31.400    -8.500  
+95 721 49919.00 I   .287565  .000154   .307292  .000155  I -.0929977  .0000099  1.1401 0.0068  I   -31.216     .791    -8.638     .298   .287400   .306000  -.0931000   -31.900    -8.600  
+95 722 49920.00 I   .287387  .000132   .303396  .000155  I -.0940641  .0000075  0.9976 0.0059  I   -31.487     .791    -8.598     .298   .287100   .302100  -.0941900   -32.400    -8.800  
+95 723 49921.00 I   .287031  .000127   .299471  .000155  I -.0950056  .0000065  0.8932 0.0049  I   -31.891     .123    -8.540     .298   .286700   .298300  -.0951400   -32.700    -8.900  
+95 724 49922.00 I   .286566  .000138   .295521  .000170  I -.0958652  .0000063  0.8330 0.0049  I   -32.115     .168    -8.534     .117   .286300   .294500  -.0959900   -32.800    -8.900  
+95 725 49923.00 I   .286095  .000149   .291575  .000168  I -.0966853  .0000074  0.8139 0.0049  I   -32.100     .168    -8.554     .117   .285800   .290600  -.0968000   -32.800    -8.900  
+95 726 49924.00 I   .285505  .000144   .287660  .000163  I -.0975064  .0000075  0.8349 0.0057  I   -31.971     .168    -8.512     .117   .285100   .286800  -.0975900   -32.800    -8.900  
+95 727 49925.00 I   .284645  .000141   .283792  .000157  I -.0983658  .0000088  0.8882 0.0064  I   -31.774     .168    -8.272     .117   .284300   .282900  -.0984300   -32.500    -8.700  
+95 728 49926.00 I   .283658  .000138   .280001  .000145  I -.0992918  .0000104  0.9687 0.0084  I   -31.577     .168    -7.928     .117   .283300   .279100  -.0993500   -32.200    -8.500  
+95 729 49927.00 I   .282595  .000165   .276263  .000102  I -.1003121  .0000144  1.0759 0.0135  I   -31.515     .203    -7.639     .131   .282200   .275300  -.1003800   -31.900    -8.400  
+95 730 49928.00 I   .281448  .000113   .272477  .000062  I -.1014508  .0000249  1.2049 0.0105  I   -31.664     .565    -7.469     .273   .281100   .271500  -.1015400   -31.900    -8.300  
+95 731 49929.00 I   .280368  .000091   .268586  .000071  I -.1027208  .0000154  1.3308 0.0146  I   -31.932     .552    -7.431     .266   .280100   .267700  -.1028100   -32.000    -8.200  
+95 8 1 49930.00 I   .279541  .000088   .264644  .000075  I -.1041002  .0000153  1.4200 0.0104  I   -32.183     .552    -7.568     .266   .279200   .263800  -.1041800   -32.000    -8.200  
+95 8 2 49931.00 I   .278952  .000083   .260708  .000068  I -.1055396  .0000140  1.4455 0.0100  I   -32.433     .552    -7.912     .266   .278500   .259900  -.1056100   -32.300    -8.200  
+95 8 3 49932.00 I   .278360  .000084   .256799  .000081  I -.1069717  .0000130  1.4125 0.0089  I   -32.662     .552    -8.138     .266   .277900   .255900  -.1070400   -32.700    -8.400  
+95 8 4 49933.00 I   .277631  .000069   .252861  .000085  I -.1083570  .0000109  1.3567 0.0085  I   -32.963     .552    -8.181     .266   .277200   .251900  -.1084400   -33.200    -8.500  
+95 8 5 49934.00 I   .276791  .000065   .248912  .000099  I -.1096881  .0000110  1.3099 0.0079  I   -33.362     .380    -8.205     .189   .276500   .248000  -.1097900   -33.500    -8.600  
+95 8 6 49935.00 I   .275892  .000094   .245043  .000144  I -.1109876  .0000113  1.2957 0.0074  I   -33.726     .791    -8.336     .298   .275900   .244100  -.1110900   -33.800    -8.800  
+95 8 7 49936.00 I   .274915  .000117   .241303  .000159  I -.1122929  .0000100  1.3219 0.0076  I   -33.948     .272    -8.565     .298   .274800   .240400  -.1123800   -34.000    -8.800  
+95 8 8 49937.00 I   .273771  .000126   .237673  .000152  I -.1136461  .0000101  1.3923 0.0064  I   -34.077     .316    -8.742     .298   .273500   .236700  -.1137100   -34.000    -8.800  
+95 8 9 49938.00 I   .272359  .000128   .234099  .000141  I -.1150900  .0000079  1.5002 0.0065  I   -34.252     .316    -8.709     .298   .272100   .233100  -.1151400   -34.200    -8.700  
+95 810 49939.00 I   .270663  .000124   .230520  .000137  I -.1166522  .0000082  1.6251 0.0056  I   -34.508     .316    -8.427     .298   .270600   .229600  -.1167000   -34.100    -8.600  
+95 811 49940.00 I   .269001  .000119   .226934  .000142  I -.1183333  .0000078  1.7294 0.0057  I   -34.609     .387    -8.133     .298   .269100   .226000  -.1183800   -34.000    -8.500  
+95 812 49941.00 I   .267541  .000124   .223342  .000134  I -.1200937  .0000078  1.7818 0.0065  I   -34.488     .387    -8.056     .298   .267600   .222500  -.1201600   -34.000    -8.300  
+95 813 49942.00 I   .266166  .000126   .219764  .000117  I -.1218769  .0000103  1.7740 0.0046  I   -34.401     .389    -8.171     .298   .266100   .218900  -.1219600   -34.200    -8.200  
+95 814 49943.00 I   .264644  .000100   .216284  .000150  I -.1236236  .0000048  1.7117 0.0057  I   -34.593     .233    -8.298     .298   .264400   .215300  -.1237100   -34.400    -8.200  
+95 815 49944.00 I   .262691  .000095   .212933  .000155  I -.1252897  .0000050  1.6177 0.0043  I   -34.989     .217    -8.335     .298   .262400   .211900  -.1253800   -34.700    -8.200  
+95 816 49945.00 I   .260241  .000091   .209642  .000156  I -.1268567  .0000072  1.5169 0.0045  I   -35.290     .217    -8.333     .298   .260100   .208500  -.1269500   -34.900    -8.300  
+95 817 49946.00 I   .257691  .000095   .206449  .000149  I -.1283241  .0000075  1.4178 0.0057  I   -35.361     .217    -8.370     .298   .257700   .205300  -.1284100   -35.200    -8.400  
+95 818 49947.00 I   .255336  .000102   .203412  .000147  I -.1296985  .0000088  1.3374 0.0062  I   -35.305     .217    -8.443     .298   .255300   .202300  -.1297800   -35.400    -8.500  
+95 819 49948.00 I   .253179  .000098   .200506  .000137  I -.1310131  .0000098  1.2997 0.0065  I   -35.293     .217    -8.503     .298   .253000   .199500  -.1311000   -35.500    -8.700  
+95 820 49949.00 I   .251097  .000111   .197685  .000081  I -.1323117  .0000097  1.3034 0.0062  I   -35.336     .199    -8.530     .298   .250800   .196800  -.1324000   -35.400    -8.700  
+95 821 49950.00 I   .249107  .000162   .194904  .000115  I -.1336314  .0000076  1.3416 0.0059  I   -35.346     .428    -8.527     .298   .248900   .194000  -.1337000   -35.200    -8.700  
+95 822 49951.00 I   .247201  .000178   .192091  .000122  I -.1350051  .0000067  1.4103 0.0052  I   -35.347     .535    -8.499     .298   .246900   .191200  -.1350600   -35.000    -8.700  
+95 823 49952.00 I   .245183  .000187   .189208  .000122  I -.1364567  .0000072  1.4933 0.0046  I   -35.427     .438    -8.466     .298   .244800   .188400  -.1364900   -34.900    -8.500  
+95 824 49953.00 I   .242878  .000175   .186348  .000176  I -.1379952  .0000063  1.5870 0.0046  I   -35.563     .379    -8.460     .298   .242600   .185400  -.1380200   -34.700    -8.400  
+95 825 49954.00 I   .240438  .000168   .183450  .000174  I -.1396425  .0000058  1.7160 0.0051  I   -35.521     .374    -8.475     .298   .240300   .182400  -.1396900   -34.700    -8.200  
+95 826 49955.00 I   .238156  .000165   .180477  .000169  I -.1414378  .0000079  1.8769 0.0050  I   -35.362     .356    -8.394     .298   .238000   .179500  -.1414900   -34.800    -8.100  
+95 827 49956.00 I   .236101  .000158   .177546  .000167  I -.1433880  .0000082  2.0131 0.0057  I   -35.373     .311    -8.223     .298   .235800   .176700  -.1434400   -35.100    -8.000  
+95 828 49957.00 I   .234106  .000107   .174753  .000163  I -.1454513  .0000081  2.1114 0.0059  I   -35.688     .193    -8.026     .124   .233600   .173900  -.1454900   -35.300    -8.000  
+95 829 49958.00 I   .232124  .000099   .172120  .000177  I -.1475858  .0000086  2.1347 0.0062  I   -36.091     .360    -7.956     .176   .231500   .171200  -.1476000   -35.700    -8.100  
+95 830 49959.00 I   .229971  .000107   .169548  .000176  I -.1496976  .0000094  2.0887 0.0068  I   -36.370     .417    -8.076     .175   .229300   .168500  -.1497300   -36.000    -8.200  
+95 831 49960.00 I   .227437  .000105   .166958  .000120  I -.1517621  .0000106  2.0392 0.0063  I   -36.548     .427    -8.135     .185   .227000   .165700  -.1518200   -36.200    -8.400  
+95 9 1 49961.00 I   .224672  .000120   .164364  .000112  I -.1537776  .0000085  1.9944 0.0082  I   -36.726     .454    -8.080     .201   .224600   .163100  -.1538500   -36.500    -8.600  
+95 9 2 49962.00 I   .221943  .000131   .161813  .000119  I -.1557561  .0000125  1.9654 0.0105  I   -36.847     .507    -8.078     .210   .222000   .160600  -.1558300   -36.600    -8.800  
+95 9 3 49963.00 I   .219408  .000149   .159389  .000131  I -.1577167  .0000193  1.9615 0.0087  I   -36.861     .576    -8.229     .216   .219600   .158400  -.1577800   -36.500    -9.000  
+95 9 4 49964.00 I   .217128  .000142   .157140  .000126  I -.1596912  .0000121  1.9941 0.0115  I   -36.779     .455    -8.488     .298   .217200   .156300  -.1597400   -36.400    -9.100  
+95 9 5 49965.00 I   .215002  .000142   .155058  .000111  I -.1617171  .0000124  2.0633 0.0086  I   -36.615     .263    -8.714     .298   .214800   .154300  -.1617900   -36.300    -9.100  
+95 9 6 49966.00 I   .212740  .000142   .153069  .000109  I -.1638234  .0000121  2.1491 0.0091  I   -36.445     .263    -8.750     .298   .212500   .152300  -.1638800   -36.200    -9.100  
+95 9 7 49967.00 I   .210029  .000131   .151075  .000109  I -.1660164  .0000134  2.2381 0.0082  I   -36.352     .263    -8.556     .298   .209800   .150200  -.1660900   -36.000    -8.800  
+95 9 8 49968.00 I   .206969  .000128   .148852  .000109  I -.1682977  .0000110  2.3215 0.0086  I   -36.269     .263    -8.263     .298   .206900   .147800  -.1684000   -35.900    -8.700  
+95 9 9 49969.00 I   .203795  .000118   .146375  .000110  I -.1706431  .0000109  2.3561 0.0121  I   -36.151     .263    -8.079     .298   .203600   .145200  -.1707500   -36.000    -8.500  
+95 910 49970.00 I   .200570  .000147   .143742  .000112  I -.1729846  .0000215  2.3147 0.0080  I   -36.077     .185    -8.092     .298   .200300   .142400  -.1730900   -36.000    -8.300  
+95 911 49971.00 I   .197305  .000134   .140984  .000123  I -.1752536  .0000116  2.2166 0.0127  I   -36.148     .791    -8.197     .298   .197000   .139500  -.1753400   -36.100    -8.200  
+95 912 49972.00 I   .194074  .000119   .138110  .000129  I -.1774091  .0000134  2.0924 0.0082  I   -36.373     .791    -8.241     .298   .193700   .136700  -.1774800   -36.200    -8.100  
+95 913 49973.00 I   .191035  .000115   .135293  .000134  I -.1794394  .0000117  1.9710 0.0087  I   -36.631     .791    -8.195     .298   .190800   .134100  -.1794900   -36.200    -8.200  
+95 914 49974.00 I   .188269  .000110   .132860  .000142  I -.1813611  .0000111  1.8794 0.0081  I   -36.762     .791    -8.150     .298   .188000   .131900  -.1814000   -36.200    -8.300  
+95 915 49975.00 I   .185662  .000101   .130861  .000132  I -.1832127  .0000112  1.8312 0.0066  I   -36.676     .791    -8.190     .298   .185400   .129800  -.1832300   -36.100    -8.400  
+95 916 49976.00 I   .182997  .000104   .129117  .000146  I -.1850363  .0000072  1.8213 0.0067  I   -36.397     .791    -8.304     .298   .182800   .127900  -.1850200   -35.900    -8.600  
+95 917 49977.00 I   .180140  .000133   .127395  .000142  I -.1868638  .0000073  1.8370 0.0047  I   -36.035     .791    -8.419     .298   .180200   .126100  -.1868200   -35.600    -8.600  
+95 918 49978.00 I   .177142  .000144   .125569  .000181  I -.1887169  .0000059  1.8726 0.0044  I   -35.742     .791    -8.462     .134   .177500   .124300  -.1886600   -35.300    -8.600  
+95 919 49979.00 I   .174227  .000150   .123629  .000197  I -.1906144  .0000050  1.9245 0.0038  I   -35.624     .791    -8.408     .134   .174600   .122600  -.1905900   -35.000    -8.500  
+95 920 49980.00 I   .171426  .000156   .121661  .000205  I -.1925842  .0000048  2.0314 0.0035  I   -35.667     .791    -8.328     .121   .171500   .120800  -.1926200   -34.800    -8.400  
+95 921 49981.00 I   .168433  .000161   .119799  .000208  I -.1946974  .0000048  2.1987 0.0034  I   -35.733     .791    -8.430     .121   .168300   .119100  -.1947900   -34.700    -8.300  
+95 922 49982.00 I   .165227  .000150   .118066  .000205  I -.1969832  .0000047  2.3706 0.0036  I   -35.721     .791    -8.631     .121   .165000   .117300  -.1971000   -34.700    -8.000  
+95 923 49983.00 I   .161888  .000153   .116469  .000202  I -.1994317  .0000055  2.5218 0.0046  I   -35.649     .791    -8.651     .132   .161700   .115600  -.1995500   -34.900    -7.800  
+95 924 49984.00 I   .158666  .000103   .114917  .000179  I -.2020139  .0000080  2.6345 0.0043  I   -35.618     .791    -8.403     .298   .158400   .113900  -.2021200   -35.100    -7.700  
+95 925 49985.00 I   .155648  .000109   .113415  .000199  I -.2046817  .0000066  2.6903 0.0056  I   -35.683     .791    -8.016     .298   .155400   .112400  -.2047700   -35.500    -7.600  
+95 926 49986.00 I   .152758  .000088   .112022  .000191  I -.2073726  .0000078  2.6805 0.0052  I   -35.797     .791    -7.726     .298   .152500   .111000  -.2074300   -35.700    -7.600  
+95 927 49987.00 I   .149886  .000084   .110801  .000185  I -.2100182  .0000081  2.5973 0.0059  I   -35.857     .791    -7.672     .298   .149700   .109800  -.2100500   -35.900    -7.600  
+95 928 49988.00 I   .147073  .000089   .109840  .000186  I -.2125541  .0000089  2.4749 0.0057  I   -35.792     .791    -7.761     .298   .146900   .108800  -.2125800   -36.000    -7.700  
+95 929 49989.00 I   .144280  .000091   .108970  .000184  I -.2149738  .0000081  2.3701 0.0067  I   -35.636     .791    -7.849     .298   .144100   .107800  -.2150100   -36.100    -7.900  
+95 930 49990.00 I   .141336  .000102   .108038  .000171  I -.2173092  .0000100  2.3101 0.0066  I   -35.486     .791    -7.879     .298   .141100   .106900  -.2173700   -35.900    -8.000  
+9510 1 49991.00 I   .138111  .000091   .107025  .000131  I -.2196143  .0000104  2.3110 0.0064  I   -35.414     .791    -7.885     .298   .137900   .105900  -.2196900   -35.700    -8.000  
+9510 2 49992.00 I   .134567  .000131   .105987  .000110  I -.2219501  .0000079  2.3687 0.0063  I   -35.417     .791    -7.910     .298   .134300   .105000  -.2220200   -35.500    -8.100  
+9510 3 49993.00 I   .130695  .000129   .105001  .000111  I -.2243638  .0000070  2.4627 0.0055  I   -35.442     .791    -7.940     .298   .130600   .104100  -.2244200   -35.300    -8.000  
+9510 4 49994.00 I   .126575  .000127   .104045  .000111  I -.2268727  .0000076  2.5481 0.0061  I   -35.442     .791    -7.919     .298   .126600   .103100  -.2269100   -35.200    -7.900  
+9510 5 49995.00 I   .122609  .000128   .103062  .000114  I -.2294525  .0000100  2.6108 0.0062  I   -35.406     .791    -7.857     .298   .122500   .102000  -.2295000   -35.000    -7.700  
+9510 6 49996.00 I   .118773  .000127   .101939  .000117  I -.2320905  .0000098  2.6619 0.0070  I   -35.359     .791    -7.743     .298   .118500   .100900  -.2321600   -35.000    -7.600  
+9510 7 49997.00 I   .114860  .000126   .100643  .000105  I -.2347659  .0000097  2.6817 0.0091  I   -35.325     .791    -7.601     .298   .114600   .099600  -.2348500   -35.000    -7.400  
+9510 8 49998.00 I   .110837  .000098   .099201  .000116  I -.2374358  .0000154  2.6473 0.0091  I   -35.303     .121    -7.521     .298   .110600   .098100  -.2375100   -35.100    -7.400  
+9510 9 49999.00 I   .106690  .000169   .097625  .000155  I -.2400381  .0000155  2.5456 0.0107  I   -35.274     .101    -7.504     .298   .106400   .096500  -.2401000   -35.200    -7.400  
+951010 50000.00 I   .102471  .000212   .095946  .000161  I -.2425086  .0000148  2.3888 0.0109  I   -35.261     .133    -7.468     .126   .102200   .094900  -.2425500   -35.300    -7.400  
+951011 50001.00 I   .098323  .000219   .094278  .000180  I -.2448171  .0000154  2.2362 0.0108  I   -35.331     .116    -7.377     .138   .098200   .093200  -.2448700   -35.200    -7.600  
+951012 50002.00 I   .094472  .000228   .092742  .000174  I -.2469890  .0000158  2.1077 0.0110  I   -35.412     .116    -7.317     .138   .094400   .091700  -.2470300   -35.100    -7.700  
+951013 50003.00 I   .090997  .000231   .091469  .000182  I -.2490365  .0000158  1.9911 0.0121  I   -35.282     .116    -7.447     .138   .090900   .090200  -.2490800   -34.900    -7.800  
+951014 50004.00 I   .087895  .000229   .090594  .000182  I -.2509869  .0000183  1.9215 0.0115  I   -34.828     .106    -7.641     .160   .087800   .089400  -.2510300   -34.700    -7.900  
+951015 50005.00 I   .084727  .000197   .090020  .000153  I -.2528981  .0000166  1.9076 0.0115  I   -34.210     .127    -7.787     .194   .084800   .088800  -.2529600   -34.300    -7.900  
+951016 50006.00 I   .081140  .000165   .089428  .000142  I -.2548170  .0000140  1.9383 0.0107  I   -33.717     .791    -7.838     .121   .081100   .088200  -.2548900   -34.000    -7.900  
+951017 50007.00 I   .077223  .000152   .088633  .000115  I -.2567910  .0000136  2.0179 0.0099  I   -33.484     .791    -7.776     .298   .077100   .087500  -.2568600   -33.700    -7.800  
+951018 50008.00 I   .073319  .000124   .087696  .000122  I -.2588592  .0000140  2.1168 0.0097  I   -33.416     .791    -7.655     .298   .072900   .086700  -.2589200   -33.600    -7.600  
+951019 50009.00 I   .069832  .000125   .086810  .000106  I -.2610189  .0000137  2.1986 0.0089  I   -33.396     .791    -7.580     .298   .069400   .085700  -.2610700   -33.400    -7.400  
+951020 50010.00 I   .066975  .000143   .086104  .000108  I -.2632595  .0000110  2.2898 0.0088  I   -33.437     .791    -7.586     .298   .066600   .084900  -.2633200   -33.400    -7.200  
+951021 50011.00 I   .064561  .000151   .085696  .000106  I -.2656028  .0000109  2.3939 0.0092  I   -33.575     .118    -7.568     .298   .064500   .084600  -.2656600   -33.400    -6.900  
+951022 50012.00 I   .062087  .000193   .085544  .000129  I -.2680337  .0000147  2.4573 0.0079  I   -33.726     .158    -7.383     .298   .062100   .084500  -.2680800   -33.500    -6.800  
+951023 50013.00 I   .059125  .000151   .085440  .000138  I -.2705006  .0000114  2.4705 0.0086  I   -33.747     .157    -7.048     .298   .059000   .084300  -.2705500   -33.500    -6.600  
+951024 50014.00 I   .055535  .000146   .085180  .000131  I -.2729630  .0000091  2.4488 0.0075  I   -33.596     .157    -6.774     .298   .055100   .084100  -.2730200   -33.400    -6.700  
+951025 50015.00 I   .051426  .000130   .084676  .000130  I -.2753887  .0000096  2.3988 0.0060  I   -33.367     .157    -6.750     .298   .051100   .083700  -.2754500   -33.200    -6.700  
+951026 50016.00 I   .047101  .000122   .084034  .000150  I -.2777537  .0000079  2.3282 0.0059  I   -33.227     .157    -6.921     .298   .046900   .083200  -.2778100   -33.100    -6.700  
+951027 50017.00 I   .042802  .000110   .083316  .000146  I -.2800474  .0000068  2.2645 0.0052  I   -33.224     .155    -7.077     .298   .042600   .082400  -.2800900   -33.000    -6.900  
+951028 50018.00 I   .038761  .000099   .082671  .000148  I -.2823000  .0000069  2.2526 0.0048  I   -33.307     .155    -7.080     .298   .038600   .081800  -.2823500   -32.800    -6.900  
+951029 50019.00 I   .035078  .000106   .082295  .000131  I -.2845748  .0000067  2.3071 0.0046  I   -33.384     .791    -6.962     .298   .034900   .081400  -.2846300   -32.500    -6.900  
+951030 50020.00 I   .031665  .000092   .082253  .000105  I -.2869319  .0000060  2.4145 0.0048  I   -33.363     .791    -6.831     .298   .031500   .081300  -.2870000   -32.300    -6.800  
+951031 50021.00 I   .028365  .000097   .082439  .000106  I -.2894134  .0000069  2.5505 0.0046  I   -33.213     .791    -6.740     .298   .028200   .081400  -.2894800   -32.100    -6.700  
+9511 1 50022.00 I   .025113  .000097   .082725  .000098  I -.2920343  .0000071  2.6907 0.0050  I   -32.986     .791    -6.667     .298   .025000   .081700  -.2921000   -32.200    -6.600  
+9511 2 50023.00 I   .021946  .000089   .083061  .000097  I -.2947758  .0000073  2.7741 0.0051  I   -32.769     .791    -6.592     .298   .021800   .082100  -.2948200   -32.100    -6.400  
+9511 3 50024.00 I   .018673  .000089   .083419  .000100  I -.2975496  .0000073  2.7590 0.0050  I   -32.641     .791    -6.528     .298   .018300   .082400  -.2975700   -32.100    -6.200  
+9511 4 50025.00 I   .015135  .000085   .083780  .000104  I -.3002725  .0000069  2.6800 0.0073  I   -32.633     .791    -6.500     .298   .014900   .082700  -.3003000   -32.100    -6.100  
+9511 5 50026.00 I   .011387  .000095   .084118  .000185  I -.3028973  .0000126  2.5639 0.0086  I   -32.694     .791    -6.508     .298   .011200   .083000  -.3029300   -32.200    -6.000  
+9511 6 50027.00 I   .007541  .000086   .084428  .000184  I -.3053956  .0000158  2.4335 0.0100  I   -32.713     .357    -6.507     .298   .007600   .083300  -.3054500   -32.200    -6.100  
+9511 7 50028.00 I   .003739  .000098   .084751  .000188  I -.3077684  .0000156  2.3155 0.0109  I   -32.643     .357    -6.442     .298   .003700   .083600  -.3078300   -32.000    -6.100  
+9511 8 50029.00 I   .000001  .000104   .085124  .000181  I -.3100311  .0000151  2.2112 0.0108  I   -32.577     .384    -6.293     .298  -.000200   .083900  -.3100700   -31.700    -6.100  
+9511 9 50030.00 I  -.003744  .000106   .085488  .000179  I -.3121888  .0000149  2.1011 0.0108  I   -32.616     .384    -6.082     .298  -.004100   .084300  -.3122000   -31.500    -6.400  
+951110 50031.00 I  -.007475  .000111   .085790  .000182  I -.3142379  .0000154  2.0049 0.0103  I   -32.588     .384    -5.914     .298  -.007800   .084600  -.3142500   -31.200    -6.500  
+951111 50032.00 I  -.011231  .000132   .086077  .000175  I -.3162171  .0000143  1.9643 0.0087  I   -32.224     .384    -5.906     .298  -.011500   .084900  -.3162400   -30.700    -6.600  
+951112 50033.00 I  -.014987  .000180   .086408  .000142  I -.3181895  .0000081  1.9927 0.0078  I   -31.607     .362    -6.055     .298  -.015200   .085300  -.3182300   -30.400    -6.700  
+951113 50034.00 I  -.018569  .000198   .086804  .000161  I -.3202225  .0000062  2.0812 0.0050  I   -31.082     .295    -6.260     .298  -.018800   .085700  -.3202700   -30.100    -6.600  
+951114 50035.00 I  -.021862  .000177   .087327  .000169  I -.3223659  .0000058  2.2116 0.0044  I   -30.819     .192    -6.394     .298  -.022300   .086300  -.3224300   -29.900    -6.600  
+951115 50036.00 I  -.025065  .000182   .088127  .000170  I -.3246626  .0000063  2.3926 0.0040  I   -30.711     .168    -6.366     .298  -.025800   .087100  -.3247200   -29.900    -6.400  
+951116 50037.00 I  -.028782  .000189   .089292  .000170  I -.3271535  .0000054  2.5806 0.0041  I   -30.607     .168    -6.214     .298  -.029500   .088100  -.3271800   -30.000    -6.200  
+951117 50038.00 I  -.033035  .000171   .090654  .000162  I -.3298032  .0000054  2.7066 0.0037  I   -30.601     .168    -6.085     .298  -.033600   .089400  -.3298000   -30.100    -5.900  
+951118 50039.00 I  -.037476  .000174   .092091  .000160  I -.3325547  .0000050  2.7965 0.0048  I   -30.807     .791    -6.032     .298  -.037800   .090800  -.3325600   -30.300    -5.800  
+951119 50040.00 I  -.041719  .000156   .093516  .000157  I -.3353883  .0000079  2.8626 0.0055  I   -31.079     .791    -5.930     .298  -.042100   .092200  -.3354100   -30.500    -5.600  
+951120 50041.00 I  -.045724  .000152   .094850  .000153  I -.3382631  .0000097  2.8784 0.0074  I   -31.117     .791    -5.695     .298  -.046300   .093600  -.3383100   -30.700    -5.500  
+951121 50042.00 I  -.049751  .000177   .096014  .000189  I -.3411295  .0000125  2.8476 0.0078  I   -30.820     .791    -5.463     .298  -.050400   .094800  -.3411800   -30.600    -5.600  
+951122 50043.00 I  -.053914  .000163   .096986  .000212  I -.3439446  .0000122  2.7759 0.0087  I   -30.385     .791    -5.474     .298  -.054400   .096000  -.3440000   -30.500    -5.600  
+951123 50044.00 I  -.057976  .000171   .097979  .000217  I -.3466769  .0000122  2.6912 0.0090  I   -30.102     .791    -5.804     .298  -.058300   .097200  -.3467200   -30.400    -5.600  
+951124 50045.00 I  -.061727  .000179   .099130  .000223  I -.3493369  .0000133  2.6362 0.0090  I   -30.101     .791    -6.219     .298  -.061900   .098300  -.3493700   -30.200    -5.800  
+951125 50046.00 I  -.065201  .000197   .100487  .000251  I -.3519652  .0000131  2.6288 0.0084  I   -30.323     .791    -6.466     .298  -.065400   .099600  -.3519800   -29.900    -5.900  
+951126 50047.00 I  -.068566  .000211   .102026  .000157  I -.3546099  .0000101  2.6677 0.0083  I   -30.582     .791    -6.517     .109  -.068900   .101000  -.3546300   -29.800    -5.900  
+951127 50048.00 I  -.071992  .000199   .103675  .000136  I -.3573086  .0000102  2.7305 0.0074  I   -30.681     .791    -6.465     .101  -.072400   .102600  -.3573300   -29.600    -5.900  
+951128 50049.00 I  -.075682  .000271   .105386  .000112  I -.3600689  .0000109  2.7872 0.0075  I   -30.591     .791    -6.310     .101  -.076300   .104400  -.3601100   -29.600    -5.800  
+951129 50050.00 I  -.079882  .000284   .107134  .000105  I -.3628755  .0000110  2.8213 0.0079  I   -30.483     .791    -5.984     .101  -.080600   .106100  -.3629200   -29.800    -5.700  
+951130 50051.00 I  -.084668  .000292   .108887  .000088  I -.3656937  .0000115  2.8019 0.0078  I   -30.525     .791    -5.636     .101  -.085300   .107800  -.3657400   -30.000    -5.600  
+9512 1 50052.00 I  -.089793  .000278   .110581  .000107  I -.3684577  .0000112  2.7177 0.0080  I   -30.702     .791    -5.408     .101  -.090300   .109500  -.3685100   -30.200    -5.400  
+9512 2 50053.00 I  -.094910  .000269   .112190  .000102  I -.3711199  .0000110  2.6059 0.0122  I   -30.883     .791    -5.347     .298  -.095300   .111200  -.3711900   -30.400    -5.200  
+9512 3 50054.00 I  -.099728  .000269   .113810  .000111  I -.3736715  .0000216  2.4999 0.0094  I   -30.973     .791    -5.378     .298  -.100100   .112800  -.3737600   -30.600    -5.100  
+9512 4 50055.00 I  -.104255  .000249   .115578  .000145  I -.3761191  .0000153  2.3919 0.0130  I   -30.935     .299    -5.380     .298  -.104800   .114500  -.3762000   -30.600    -5.200  
+9512 5 50056.00 I  -.108571  .000228   .117528  .000152  I -.3784535  .0000146  2.2785 0.0107  I   -30.736     .314    -5.315     .298  -.109200   .116400  -.3785200   -30.600    -5.200  
+9512 6 50057.00 I  -.112580  .000228   .119567  .000159  I -.3806794  .0000151  2.1748 0.0105  I   -30.376     .314    -5.244     .298  -.113200   .118500  -.3807300   -30.300    -5.200  
+9512 7 50058.00 I  -.116132  .000230   .121605  .000164  I -.3828140  .0000151  2.1036 0.0107  I   -29.921     .314    -5.220     .298  -.116600   .120700  -.3828500   -30.100    -5.400  
+9512 8 50059.00 I  -.119291  .000224   .123582  .000165  I -.3849046  .0000151  2.0863 0.0092  I   -29.527     .314    -5.230     .298  -.119700   .122700  -.3849300   -29.600    -5.400  
+9512 9 50060.00 I  -.122360  .000237   .125522  .000166  I -.3870040  .0000105  2.1211 0.0092  I   -29.226     .314    -5.249     .298  -.122600   .124600  -.3870300   -29.300    -5.500  
+951210 50061.00 I  -.125428  .000269   .127453  .000175  I -.3891584  .0000106  2.1906 0.0062  I   -29.022     .329    -5.293     .298  -.125800   .126400  -.3891800   -29.000    -5.500  
+951211 50062.00 I  -.128714  .000217   .129439  .000150  I -.3913900  .0000065  2.2746 0.0062  I   -28.989     .122    -5.385     .298  -.129100   .128400  -.3914100   -28.600    -5.500  
+951212 50063.00 I  -.132294  .000191   .131545  .000153  I -.3937117  .0000063  2.3708 0.0047  I   -29.081     .102    -5.472     .298  -.132600   .130400  -.3937500   -28.600    -5.400  
+951213 50064.00 I  -.135762  .000195   .133872  .000147  I -.3961394  .0000068  2.4904 0.0052  I   -29.082     .102    -5.462     .298  -.135900   .132800  -.3961900   -28.600    -5.300  
+951214 50065.00 I  -.138554  .000179   .136542  .000145  I -.3986872  .0000084  2.5940 0.0051  I   -28.888     .102    -5.347     .298  -.138900   .135500  -.3987300   -28.800    -5.100  
+951215 50066.00 I  -.140929  .000162   .139485  .000140  I -.4013079  .0000077  2.6389 0.0057  I   -28.737     .102    -5.185     .298  -.141300   .138400  -.4013500   -28.900    -5.000  
+951216 50067.00 I  -.143210  .000193   .142573  .000191  I -.4039535  .0000077  2.6492 0.0083  I   -28.889     .102    -5.061     .298  -.143500   .141400  -.4040000   -29.100    -4.800  
+951217 50068.00 I  -.145513  .000210   .145731  .000237  I -.4066048  .0000146  2.6551 0.0087  I   -29.254     .791    -4.953     .298  -.145700   .144600  -.4066600   -29.200    -4.800  
+951218 50069.00 I  -.147835  .000228   .148873  .000240  I -.4092583  .0000155  2.6447 0.0105  I   -29.504     .791    -4.784     .298  -.148000   .147700  -.4093200   -29.200    -4.800  
+951219 50070.00 I  -.150201  .000257   .151908  .000242  I -.4118837  .0000150  2.6029 0.0095  I   -29.474     .791    -4.651     .298  -.150400   .150800  -.4119500   -29.100    -4.800  
+951220 50071.00 I  -.152541  .000252   .154799  .000250  I -.4144558  .0000110  2.5363 0.0099  I   -29.300     .791    -4.766     .298  -.152900   .153700  -.4145200   -28.900    -4.800  
+951221 50072.00 I  -.154776  .000258   .157666  .000255  I -.4169546  .0000128  2.4645 0.0086  I   -29.200     .791    -5.091     .298  -.155200   .156600  -.4170000   -28.700    -4.900  
+951222 50073.00 I  -.156826  .000233   .160665  .000254  I -.4193989  .0000133  2.4351 0.0088  I   -29.275     .791    -5.380     .298  -.157100   .159500  -.4194400   -28.400    -5.000  
+951223 50074.00 I  -.158669  .000175   .163912  .000148  I -.4218509  .0000120  2.4842 0.0080  I   -29.480     .791    -5.421     .298  -.158900   .162800  -.4218800   -28.300    -5.100  
+951224 50075.00 I  -.160459  .000145   .167387  .000129  I -.4243901  .0000090  2.6018 0.0065  I   -29.656     .791    -5.252     .298  -.160700   .166200  -.4244100   -28.200    -5.100  
+951225 50076.00 I  -.162335  .000113   .170951  .000119  I -.4270547  .0000049  2.7202 0.0052  I   -29.628     .791    -5.083     .298  -.162700   .169900  -.4270700   -28.200    -5.100  
+951226 50077.00 I  -.164330  .000069   .174443  .000101  I -.4298117  .0000053  2.7824 0.0027  I   -29.412     .791    -4.994     .298  -.164800   .173400  -.4298400   -28.400    -4.900  
+951227 50078.00 I  -.166407  .000055   .177742  .000094  I -.4325949  .0000024  2.7709 0.0033  I   -29.256     .791    -4.890     .298  -.167000   .176600  -.4326300   -28.700    -4.900  
+951228 50079.00 I  -.168495  .000074   .180798  .000093  I -.4353285  .0000039  2.6844 0.0024  I   -29.357     .791    -4.719     .298  -.169000   .179500  -.4353800   -29.000    -4.800  
+951229 50080.00 I  -.170527  .000064   .183633  .000091  I -.4379429  .0000042  2.5354 0.0029  I   -29.591     .791    -4.596     .298  -.170900   .182300  -.4379900   -29.100    -4.700  
+951230 50081.00 I  -.172465  .000108   .186350  .000088  I -.4403853  .0000043  2.3442 0.0056  I   -29.680     .791    -4.643     .298  -.172700   .185100  -.4404300   -29.400    -4.600  
+951231 50082.00 I  -.174304  .000169   .189077  .000146  I -.4426252  .0000103  2.1347 0.0044  I   -29.557     .791    -4.797     .298  -.174500   .188000  -.4426700   -29.500    -4.500  
+96 1 1 50083.00 I  -.175934  .000172   .191874  .000177  I  .5553429  .0000076  1.9325 0.0065  I   -29.394     .791    -4.888     .298  -.176200   .190800   .5552900   -29.400    -4.600  
+96 1 2 50084.00 I  -.177203  .000171   .194733  .000187  I  .5534992  .0000080  1.7620 0.0050  I   -29.306     .791    -4.849     .298  -.177700   .193700   .5534400   -29.300    -4.600  
+96 1 3 50085.00 I  -.178219  .000167   .197661  .000231  I  .5518040  .0000066  1.6361 0.0057  I   -29.226     .791    -4.768     .298  -.178700   .196600   .5517600   -28.800    -4.700  
+96 1 4 50086.00 I  -.179271  .000201   .200769  .000265  I  .5502108  .0000082  1.5587 0.0058  I   -29.079     .791    -4.739     .298  -.179700   .199600   .5501600   -28.400    -4.800  
+96 1 5 50087.00 I  -.180413  .000175   .204061  .000270  I  .5486679  .0000096  1.5374 0.0061  I   -28.892     .791    -4.756     .298  -.180700   .202900   .5486300   -28.000    -5.000  
+96 1 6 50088.00 I  -.181716  .000200   .207436  .000272  I  .5471137  .0000089  1.5831 0.0084  I   -28.710     .791    -4.783     .298  -.182100   .206200   .5471000   -27.600    -5.200  
+96 1 7 50089.00 I  -.183392  .000165   .210670  .000268  I  .5454827  .0000137  1.6859 0.0068  I   -28.546     .791    -4.849     .298  -.183900   .209600   .5454800   -27.400    -5.300  
+96 1 8 50090.00 I  -.185233  .000158   .213575  .000255  I  .5437364  .0000102  1.8052 0.0086  I   -28.421     .791    -5.007     .298  -.185700   .212600   .5437200   -27.400    -5.200  
+96 1 9 50091.00 I  -.186902  .000161   .216171  .000229  I  .5418803  .0000103  1.9004 0.0071  I   -28.349     .791    -5.208     .298  -.187100   .215200   .5418400   -27.600    -5.300  
+96 110 50092.00 I  -.188610  .000139   .218633  .000185  I  .5399478  .0000098  1.9590 0.0070  I   -28.276     .791    -5.310     .298  -.189000   .217500   .5399000   -27.800    -5.300  
+96 111 50093.00 I  -.190544  .000143   .221323  .000184  I  .5379721  .0000096  1.9883 0.0070  I   -28.168     .791    -5.232     .298  -.191000   .220100   .5379200   -28.100    -5.200  
+96 112 50094.00 I  -.192621  .000131   .224329  .000240  I  .5359773  .0000101  1.9993 0.0063  I   -28.132     .791    -5.062     .298  -.192700   .223000   .5359300   -28.600    -5.100  
+96 113 50095.00 I  -.194690  .000122   .227472  .000226  I  .5339774  .0000082  1.9986 0.0095  I   -28.313     .791    -4.945     .298  -.194800   .226200   .5339400   -28.900    -5.000  
+96 114 50096.00 I  -.196600  .000139   .230482  .000225  I  .5319852  .0000160  1.9827 0.0087  I   -28.673     .791    -4.904     .298  -.197000   .229500   .5319200   -29.100    -5.000  
+96 115 50097.00 I  -.198256  .000192   .233199  .000221  I  .5300178  .0000154  1.9494 0.0118  I   -29.010     .791    -4.864     .298  -.198600   .232300   .5299700   -29.300    -4.900  
+96 116 50098.00 I  -.199573  .000179   .235746  .000221  I  .5280928  .0000173  1.8968 0.0116  I   -29.212     .791    -4.833     .298  -.200000   .234700   .5280700   -29.200    -4.900  
+96 117 50099.00 I  -.200722  .000180   .238434  .000215  I  .5262338  .0000173  1.8155 0.0121  I   -29.356     .791    -4.937     .298  -.201300   .237100   .5262100   -29.100    -5.000  
+96 118 50100.00 I  -.202042  .000179   .241575  .000178  I  .5244563  .0000169  1.7506 0.0117  I   -29.552     .791    -5.217     .298  -.202500   .240200   .5244100   -28.800    -5.000  
+96 119 50101.00 I  -.203464  .000175   .245190  .000185  I  .5227037  .0000157  1.7723 0.0121  I   -29.802     .791    -5.482     .298  -.203700   .244000   .5227000   -28.700    -5.000  
+96 120 50102.00 I  -.204972  .000215   .249073  .000239  I  .5208840  .0000173  1.8771 0.0124  I   -30.012     .791    -5.505     .298  -.205300   .248100   .5209000   -28.500    -5.000  
+96 121 50103.00 I  -.206515  .000183   .252890  .000244  I  .5189387  .0000193  2.0143 0.0112  I   -30.053     .791    -5.306     .298  -.207100   .252000   .5189200   -28.300    -5.000  
+96 122 50104.00 I  -.208097  .000203   .256437  .000255  I  .5168645  .0000143  2.1240 0.0117  I   -29.834     .791    -5.112     .298  -.208500   .255500   .5168300   -28.400    -5.000  
+96 123 50105.00 I  -.209673  .000198   .259782  .000264  I  .5147185  .0000131  2.1502 0.0096  I   -29.454     .791    -5.049     .298  -.209900   .258800   .5146800   -28.600    -4.900  
+96 124 50106.00 I  -.210988  .000196   .263176  .000257  I  .5125896  .0000129  2.1000 0.0087  I   -29.222     .791    -5.015     .298  -.211400   .262100   .5125500   -29.000    -4.900  
+96 125 50107.00 I  -.211914  .000199   .266751  .000251  I  .5105347  .0000115  2.0013 0.0087  I   -29.365     .791    -4.949     .298  -.212400   .265600   .5104800   -29.300    -4.900  
+96 126 50108.00 I  -.212703  .000195   .270456  .000263  I  .5085976  .0000118  1.8707 0.0064  I   -29.717     .791    -4.913     .298  -.213000   .269400   .5085400   -29.700    -5.000  
+96 127 50109.00 I  -.213735  .000128   .274297  .000255  I  .5067973  .0000058  1.7280 0.0066  I   -29.911     .791    -5.020     .298  -.213900   .273300   .5067400   -29.900    -5.100  
+96 128 50110.00 I  -.215159  .000098   .278183  .000253  I  .5051407  .0000061  1.5880 0.0045  I   -29.841     .158    -5.262     .298  -.215500   .277300   .5051000   -30.000    -5.200  
+96 129 50111.00 I  -.216851  .000099   .281901  .000234  I  .5036132  .0000069  1.4725 0.0050  I   -29.729     .163    -5.495     .298  -.217200   .281000   .5035800   -29.900    -5.400  
+96 130 50112.00 I  -.218493  .000109   .285363  .000237  I  .5021832  .0000078  1.3947 0.0052  I   -29.714     .197    -5.618     .298  -.218800   .284400   .5021500   -29.600    -5.600  
+96 131 50113.00 I  -.219824  .000099   .288726  .000235  I  .5008057  .0000078  1.3714 0.0054  I   -29.645     .197    -5.663     .298  -.220200   .287700   .5007700   -29.200    -5.700  
+96 2 1 50114.00 I  -.220800  .000088   .292186  .000183  I  .4994203  .0000075  1.4085 0.0058  I   -29.423     .197    -5.681     .298  -.221300   .290900   .4993900   -28.700    -5.800  
+96 2 2 50115.00 I  -.221440  .000087   .295824  .000163  I  .4979729  .0000085  1.4932 0.0069  I   -29.133     .197    -5.670     .298  -.221800   .294600   .4979800   -28.500    -5.800  
+96 2 3 50116.00 I  -.221815  .000102   .299586  .000186  I  .4964256  .0000117  1.6030 0.0061  I   -28.932     .174    -5.620     .298  -.222200   .298700   .4964100   -28.200    -5.800  
+96 2 4 50117.00 I  -.222065  .000106   .303384  .000196  I  .4947668  .0000088  1.7133 0.0066  I   -28.852     .110    -5.589     .298  -.222300   .302300   .4947300   -28.100    -5.800  
+96 2 5 50118.00 I  -.222381  .000097   .307187  .000164  I  .4930004  .0000061  1.8194 0.0055  I   -28.845     .576    -5.645     .298  -.222400   .305900   .4929300   -28.000    -5.800  
+96 2 6 50119.00 I  -.222896  .000103   .310942  .000182  I  .4911259  .0000066  1.9318 0.0045  I   -28.935     .670    -5.767     .298  -.223200   .309700   .4910300   -28.200    -5.800  
+96 2 7 50120.00 I  -.223554  .000137   .314574  .000187  I  .4891368  .0000066  2.0444 0.0043  I   -29.209     .670    -5.857     .298  -.223900   .313300   .4890800   -28.300    -5.800  
+96 2 8 50121.00 I  -.224342  .000139   .318023  .000180  I  .4870476  .0000056  2.1257 0.0042  I   -29.711     .670    -5.902     .298  -.224600   .316900   .4870400   -28.200    -5.900  
+96 2 9 50122.00 I  -.225044  .000162   .321171  .000182  I  .4849035  .0000051  2.1529 0.0040  I   -30.118     .670    -5.896     .298  -.225300   .320300   .4848600   -28.200    -6.000  
+96 210 50123.00 I  -.225181  .000161   .323983  .000167  I  .4827588  .0000056  2.1291 0.0068  I   -30.281     .817    -5.882     .298  -.225300   .322900   .4827400   -28.200    -6.100  
+96 211 50124.00 I  -.224580  .000171   .326681  .000187  I  .4806567  .0000127  2.0711 0.0053  I   -30.265     .663    -5.893     .298  -.224700   .325500   .4806600   -28.300    -6.200  
+96 212 50125.00 I  -.223400  .000164   .329470  .000151  I  .4786206  .0000091  2.0010 0.0076  I   -30.117     .533    -5.894     .298  -.223900   .328400   .4786100   -28.600    -6.100  
+96 213 50126.00 I  -.221975  .000135   .332497  .000148  I  .4766505  .0000083  1.9436 0.0063  I   -29.920     .533    -5.875     .298  -.222400   .331400   .4766200   -29.100    -6.000  
+96 214 50127.00 I  -.220650  .000133   .335894  .000146  I  .4747223  .0000086  1.9194 0.0057  I   -29.779     .483    -5.885     .298  -.221000   .334800   .4746900   -29.600    -5.900  
+96 215 50128.00 I  -.219519  .000084   .339747  .000120  I  .4727933  .0000079  1.9504 0.0054  I   -29.609     .483    -5.960     .298  -.219800   .338500   .4727600   -29.800    -5.700  
+96 216 50129.00 I  -.218710  .000092   .344051  .000123  I  .4707968  .0000064  2.0554 0.0051  I   -29.461     .483    -6.013     .298  -.219100   .342700   .4707600   -29.800    -5.600  
+96 217 50130.00 I  -.218381  .000093   .348607  .000176  I  .4686632  .0000065  2.2182 0.0050  I   -29.437     .502    -5.924     .298  -.218900   .347600   .4686900   -29.700    -5.600  
+96 218 50131.00 I  -.218571  .000096   .353126  .000188  I  .4663566  .0000078  2.3924 0.0047  I   -29.438     .365    -5.710     .298  -.219000   .352200   .4664100   -29.300    -5.600  
+96 219 50132.00 I  -.219284  .000139   .357369  .000168  I  .4638910  .0000067  2.5287 0.0046  I   -29.353     .262    -5.582     .298  -.219800   .356500   .4639200   -29.000    -5.700  
+96 220 50133.00 I  -.220255  .000132   .361221  .000179  I  .4613243  .0000050  2.5896 0.0043  I   -29.218     .791    -5.715     .298  -.220800   .360200   .4613000   -28.800    -5.900  
+96 221 50134.00 I  -.221031  .000133   .364694  .000191  I  .4587397  .0000055  2.5668 0.0040  I   -29.233     .791    -6.025     .298  -.221300   .363600   .4586900   -28.800    -6.000  
+96 222 50135.00 I  -.221356  .000127   .367982  .000191  I  .4562191  .0000062  2.4586 0.0044  I   -29.540     .791    -6.278     .298  -.221700   .366900   .4562000   -29.100    -6.100  
+96 223 50136.00 I  -.221356  .000128   .371356  .000161  I  .4538542  .0000068  2.2551 0.0048  I   -30.009     .791    -6.389     .298  -.221500   .369900   .4538500   -29.500    -6.300  
+96 224 50137.00 I  -.221173  .000122   .375065  .000174  I  .4517135  .0000074  2.0378 0.0053  I   -30.356     .791    -6.464     .298  -.221400   .373500   .4516400   -30.000    -6.400  
+96 225 50138.00 I  -.220760  .000100   .379108  .000194  I  .4497550  .0000082  1.8915 0.0056  I   -30.495     .791    -6.594     .298  -.221200   .378000   .4496300   -30.300    -6.500  
+96 226 50139.00 I  -.220267  .000096   .383241  .000183  I  .4479084  .0000084  1.8116 0.0117  I   -30.591     .791    -6.764     .298  -.220700   .382200   .4477600   -30.500    -6.600  
+96 227 50140.00 I  -.219849  .000130   .387230  .000182  I  .4461162  .0000220  1.7783 0.0114  I   -30.729     .791    -6.920     .298  -.220100   .386100   .4459900   -30.500    -6.900  
+96 228 50141.00 I  -.219416  .000138   .391075  .000184  I  .4443430  .0000212  1.7712 0.0149  I   -30.727     .791    -7.034     .298  -.219800   .390000   .4442800   -30.100    -7.000  
+96 229 50142.00 I  -.218720  .000146   .394877  .000190  I  .4425576  .0000202  1.8128 0.0142  I   -30.420     .791    -7.097     .298  -.219200   .393500   .4425900   -29.700    -7.100  
+96 3 1 50143.00 I  -.217592  .000138   .398724  .000167  I  .4406961  .0000190  1.9185 0.0133  I   -29.944     .791    -7.096     .298  -.218100   .397500   .4407400   -29.200    -7.100  
+96 3 2 50144.00 I  -.216046  .000143   .402709  .000173  I  .4387105  .0000173  2.0545 0.0111  I   -29.568     .791    -7.053     .298  -.216400   .401800   .4386800   -28.700    -7.200  
+96 3 3 50145.00 I  -.214256  .000143   .406820  .000157  I  .4365826  .0000113  2.2041 0.0090  I   -29.366     .791    -7.034     .298  -.214400   .405800   .4365700   -28.400    -7.200  
+96 3 4 50146.00 I  -.212377  .000114   .410989  .000146  I  .4343027  .0000052  2.3526 0.0062  I   -29.210     .791    -7.087     .298  -.212700   .409800   .4343200   -28.200    -7.100  
+96 3 5 50147.00 I  -.210462  .000108   .415165  .000159  I  .4318862  .0000052  2.4747 0.0042  I   -29.035     .791    -7.179     .298  -.210800   .413800   .4318900   -28.200    -7.100  
+96 3 6 50148.00 I  -.208540  .000104   .419342  .000163  I  .4293659  .0000065  2.5588 0.0039  I   -28.924     .791    -7.249     .298  -.209000   .418200   .4293400   -28.400    -7.200  
+96 3 7 50149.00 I  -.206795  .000103   .423380  .000159  I  .4267812  .0000057  2.6050 0.0049  I   -28.931     .791    -7.285     .298  -.207100   .422300   .4267300   -28.700    -7.200  
+96 3 8 50150.00 I  -.205259  .000102   .427213  .000154  I  .4241680  .0000073  2.6150 0.0053  I   -29.022     .791    -7.314     .298  -.205900   .425900   .4241600   -29.100    -7.300  
+96 3 9 50151.00 I  -.203908  .000117   .430867  .000202  I  .4215619  .0000090  2.5927 0.0076  I   -29.113     .321    -7.363     .298  -.204300   .429800   .4216000   -29.400    -7.300  
+96 310 50152.00 I  -.202582  .000126   .434377  .000214  I  .4189899  .0000133  2.5483 0.0092  I   -29.170     .367    -7.426     .298  -.203000   .433300   .4190100   -29.500    -7.400  
+96 311 50153.00 I  -.201172  .000142   .437787  .000233  I  .4164748  .0000160  2.4755 0.0100  I   -29.216     .367    -7.480     .298  -.201800   .436600   .4164900   -29.500    -7.500  
+96 312 50154.00 I  -.199681  .000153   .441159  .000243  I  .4140508  .0000150  2.3672 0.0109  I   -29.273     .367    -7.534     .298  -.199800   .440100   .4140600   -29.400    -7.600  
+96 313 50155.00 I  -.198181  .000161   .444555  .000234  I  .4117438  .0000148  2.2488 0.0098  I   -29.332     .367    -7.631     .298  -.198600   .443400   .4117000   -29.100    -7.600  
+96 314 50156.00 I  -.196547  .000168   .448062  .000231  I  .4095361  .0000125  2.1818 0.0091  I   -29.352     .367    -7.780     .298  -.197100   .447000   .4094800   -28.700    -7.800  
+96 315 50157.00 I  -.194698  .000164   .451546  .000228  I  .4073526  .0000105  2.1975 0.0079  I   -29.321     .791    -7.891     .298  -.195100   .450500   .4073300   -28.400    -7.800  
+96 316 50158.00 I  -.192737  .000188   .454954  .000212  I  .4051230  .0000098  2.2674 0.0076  I   -29.256     .791    -7.841     .298  -.193100   .453800   .4051100   -28.100    -7.900  
+96 317 50159.00 I  -.190621  .000202   .458387  .000229  I  .4028131  .0000111  2.3515 0.0059  I   -29.180     .791    -7.622     .298  -.191100   .457400   .4028100   -28.000    -7.900  
+96 318 50160.00 I  -.188103  .000190   .461977  .000198  I  .4004263  .0000067  2.4167 0.0076  I   -29.102     .791    -7.415     .298  -.188700   .460700   .4004400   -28.100    -7.900  
+96 319 50161.00 I  -.185015  .000175   .465952  .000201  I  .3979922  .0000103  2.4444 0.0068  I   -29.037     .791    -7.436     .298  -.185500   .464600   .3979900   -28.200    -7.900  
+96 320 50162.00 I  -.181736  .000174   .470274  .000220  I  .3955522  .0000118  2.4279 0.0083  I   -29.044     .791    -7.720     .298  -.182000   .469400   .3955200   -28.400    -8.000  
+96 321 50163.00 I  -.178926  .000199   .474524  .000225  I  .3931553  .0000129  2.3547 0.0088  I   -29.179     .791    -8.078     .298  -.179100   .473800   .3931500   -28.500    -7.900  
+96 322 50164.00 I  -.176775  .000150   .478401  .000200  I  .3908599  .0000131  2.2303 0.0087  I   -29.403     .791    -8.294     .298  -.177000   .477600   .3908500   -28.700    -7.900  
+96 323 50165.00 I  -.175051  .000160   .481739  .000189  I  .3886987  .0000118  2.0938 0.0088  I   -29.629     .791    -8.320     .298  -.175600   .480800   .3886500   -28.800    -8.000  
+96 324 50166.00 I  -.173268  .000164   .484606  .000166  I  .3866632  .0000117  1.9845 0.0068  I   -29.829     .369    -8.268     .298  -.173900   .483500   .3866500   -28.800    -8.000  
+96 325 50167.00 I  -.171139  .000176   .487249  .000143  I  .3847174  .0000067  1.9120 0.0066  I   -30.011     .496    -8.254     .298  -.171600   .486200   .3847200   -28.700    -8.100  
+96 326 50168.00 I  -.168588  .000169   .489916  .000116  I  .3828287  .0000061  1.8711 0.0043  I   -30.084     .440    -8.305     .298  -.169100   .488800   .3828000   -28.600    -8.200  
+96 327 50169.00 I  -.165644  .000155   .492745  .000120  I  .3809636  .0000055  1.8650 0.0043  I   -29.876     .440    -8.364     .298  -.166000   .491600   .3809300   -28.300    -8.300  
+96 328 50170.00 I  -.162586  .000142   .495671  .000102  I  .3790850  .0000060  1.9002 0.0040  I   -29.464     .440    -8.392     .298  -.162900   .494600   .3790600   -27.900    -8.500  
+96 329 50171.00 I  -.159679  .000133   .498650  .000098  I  .3771505  .0000057  1.9737 0.0053  I   -28.989     .440    -8.361     .298  -.160200   .497700   .3771400   -27.700    -8.500  
+96 330 50172.00 I  -.156908  .000114   .501603  .000097  I  .3751298  .0000088  2.0707 0.0090  I   -28.666     .472    -8.305     .298  -.157200   .500500   .3751100   -27.400    -8.500  
+96 331 50173.00 I  -.154212  .000101   .504539  .000100  I  .3730040  .0000171  2.1829 0.0079  I   -28.606     .298    -8.301     .298  -.154600   .503400   .3729600   -27.400    -8.500  
+96 4 1 50174.00 I  -.151663  .000117   .507519  .000123  I  .3707596  .0000131  2.3081 0.0110  I   -28.693     .247    -8.367     .298  -.152300   .506400   .3708000   -27.500    -8.400  
+96 4 2 50175.00 I  -.149362  .000124   .510501  .000125  I  .3683888  .0000137  2.4303 0.0093  I   -28.784     .246    -8.423     .298  -.149700   .509400   .3685000   -28.000    -8.300  
+96 4 3 50176.00 I  -.147360  .000128   .513289  .000134  I  .3659173  .0000133  2.4977 0.0095  I   -28.867     .246    -8.396     .298  -.147900   .512200   .3659300   -28.400    -8.300  
+96 4 4 50177.00 I  -.145638  .000137   .515645  .000143  I  .3634127  .0000133  2.5073 0.0086  I   -28.994     .246    -8.338     .298  -.146000   .514500   .3633400   -28.700    -8.400  
+96 4 5 50178.00 I  -.143920  .000148   .517579  .000142  I  .3609107  .0000110  2.4928 0.0081  I   -29.092     .246    -8.369     .298  -.144200   .516300   .3609000   -29.100    -8.400  
+96 4 6 50179.00 I  -.142117  .000183   .519318  .000155  I  .3584370  .0000093  2.4487 0.0086  I   -29.093     .246    -8.541     .298  -.142300   .518100   .3584400   -29.400    -8.500  
+96 4 7 50180.00 I  -.140191  .000181   .521063  .000161  I  .3560213  .0000131  2.3808 0.0059  I   -29.012     .245    -8.775     .298  -.140400   .519900   .3560200   -29.500    -8.700  
+96 4 8 50181.00 I  -.137977  .000180   .522959  .000139  I  .3536761  .0000071  2.3108 0.0075  I   -28.909     .791    -8.941     .298  -.138500   .521600   .3536600   -29.400    -8.800  
+96 4 9 50182.00 I  -.135234  .000179   .525188  .000133  I  .3513918  .0000075  2.2646 0.0055  I   -28.840     .791    -8.991     .298  -.135700   .523700   .3513800   -29.200    -8.800  
+96 410 50183.00 I  -.132187  .000195   .527816  .000131  I  .3491286  .0000085  2.2733 0.0059  I   -28.802     .791    -8.973     .298  -.132700   .526700   .3491000   -28.900    -8.900  
+96 411 50184.00 I  -.129341  .000205   .530702  .000141  I  .3468257  .0000090  2.3407 0.0060  I   -28.733     .791    -8.959     .298  -.129400   .529800   .3468200   -28.400    -8.900  
+96 412 50185.00 I  -.126573  .000181   .533654  .000131  I  .3444367  .0000085  2.4398 0.0059  I   -28.558     .791    -8.964     .298  -.126700   .532700   .3444800   -28.000    -8.900  
+96 413 50186.00 I  -.123540  .000182   .536510  .000120  I  .3419479  .0000076  2.5335 0.0094  I   -28.286     .791    -8.932     .298  -.123800   .535500   .3419800   -27.700    -8.900  
+96 414 50187.00 I  -.120077  .000180   .539229  .000133  I  .3393835  .0000168  2.5859 0.0086  I   -28.040     .791    -8.816     .298  -.120300   .538200   .3393300   -27.500    -8.800  
+96 415 50188.00 I  -.116351  .000192   .541835  .000124  I  .3367947  .0000154  2.5827 0.0112  I   -27.977     .791    -8.672     .298  -.116700   .540600   .3367600   -27.500    -8.800  
+96 416 50189.00 I  -.112714  .000165   .544371  .000136  I  .3342362  .0000147  2.5252 0.0103  I   -28.144     .791    -8.650     .298  -.112900   .543100   .3342400   -27.600    -8.800  
+96 417 50190.00 I  -.109479  .000154   .546890  .000134  I  .3317556  .0000138  2.4338 0.0100  I   -28.426     .791    -8.844     .298  -.109600   .545800   .3317400   -27.800    -8.900  
+96 418 50191.00 I  -.106534  .000154   .549353  .000153  I  .3293821  .0000136  2.3020 0.0112  I   -28.662     .791    -9.175     .298  -.107000   .548400   .3293200   -28.100    -9.000  
+96 419 50192.00 I  -.103718  .000163   .551690  .000158  I  .3271622  .0000176  2.1385 0.0114  I   -28.763     .791    -9.444     .298  -.104200   .550600   .3271400   -28.400    -9.100  
+96 420 50193.00 I  -.100881  .000147   .553900  .000169  I  .3250953  .0000184  2.0044 0.0108  I   -28.796     .791    -9.531     .298  -.100900   .552900   .3250900   -28.500    -9.300  
+96 421 50194.00 I  -.097922  .000130   .556020  .000150  I  .3231337  .0000126  1.9294 0.0106  I   -28.874     .791    -9.490     .298  -.098000   .554900   .3231400   -28.500    -9.300  
+96 422 50195.00 I  -.094702  .000133   .558058  .000135  I  .3212225  .0000104  1.8964 0.0082  I   -28.979     .466    -9.454     .255  -.095100   .556600   .3212000   -28.500    -9.400  
+96 423 50196.00 I  -.091203  .000148   .559985  .000143  I  .3193323  .0000104  1.8890 0.0074  I   -28.959     .427    -9.480     .223  -.091300   .558900   .3192800   -28.200    -9.400  
+96 424 50197.00 I  -.087651  .000165   .561722  .000132  I  .3174315  .0000104  1.9204 0.0063  I   -28.674     .382    -9.499     .200  -.088000   .560600   .3174000   -27.800    -9.400  
+96 425 50198.00 I  -.084232  .000163   .563198  .000149  I  .3154816  .0000071  1.9813 0.0062  I   -28.166     .382    -9.450     .200  -.084800   .562300   .3154700   -27.500    -9.300  
+96 426 50199.00 I  -.080698  .000158   .564540  .000154  I  .3134595  .0000066  2.0704 0.0048  I   -27.711     .382    -9.384     .200  -.081100   .563800   .3134500   -27.200    -9.200  
+96 427 50200.00 I  -.076772  .000158   .565959  .000155  I  .3113307  .0000064  2.1896 0.0053  I   -27.474     .382    -9.296     .200  -.077100   .564900   .3113000   -27.000    -9.100  
+96 428 50201.00 I  -.072472  .000156   .567693  .000185  I  .3090793  .0000084  2.3116 0.0058  I   -27.509     .199    -9.247     .298  -.072700   .566400   .3090500   -27.000    -9.100  
+96 429 50202.00 I  -.068033  .000137   .569841  .000182  I  .3067111  .0000096  2.4227 0.0065  I   -27.810     .243    -9.282     .298  -.068300   .568500   .3066900   -27.100    -9.000  
+96 430 50203.00 I  -.063732  .000100   .572237  .000178  I  .3042392  .0000099  2.5177 0.0069  I   -28.257     .523    -9.318     .298  -.064000   .571100   .3042100   -27.500    -9.100  
+96 5 1 50204.00 I  -.059829  .000118   .574547  .000157  I  .3016817  .0000099  2.5946 0.0071  I   -28.644     .523    -9.243     .298  -.059900   .573600   .3016500   -27.900    -9.200  
+96 5 2 50205.00 I  -.056414  .000130   .576487  .000138  I  .2990626  .0000102  2.6336 0.0074  I   -28.806     .523    -9.089     .298  -.056600   .575600   .2990300   -28.400    -9.300  
+96 5 3 50206.00 I  -.053179  .000140   .578097  .000127  I  .2964375  .0000111  2.6037 0.0076  I   -28.598     .523    -8.975     .298  -.053500   .577000   .2964200   -28.900    -9.500  
+96 5 4 50207.00 I  -.049736  .000144   .579512  .000115  I  .2938736  .0000112  2.5186 0.0085  I   -28.151     .523    -9.038     .298  -.049900   .578500   .2938600   -29.300    -9.600  
+96 5 5 50208.00 I  -.045973  .000147   .580829  .000104  I  .2914053  .0000128  2.4185 0.0087  I   -27.721     .931    -9.255     .298  -.046000   .579900   .2913700   -29.400    -9.700  
+96 5 6 50209.00 I  -.042012  .000153   .582102  .000126  I  .2890282  .0000133  2.3435 0.0093  I   -27.502    1.043    -9.442     .298  -.042100   .581000   .2890300   -29.400    -9.800  
+96 5 7 50210.00 I  -.037918  .000135   .583347  .000133  I  .2866998  .0000135  2.3239 0.0095  I   -27.703    1.043    -9.490     .298  -.038000   .582200   .2867100   -29.300    -9.700  
+96 5 8 50211.00 I  -.033632  .000123   .584594  .000137  I  .2843591  .0000136  2.3681 0.0098  I   -28.404    1.043    -9.460     .298  -.033800   .583500   .2843400   -29.000    -9.700  
+96 5 9 50212.00 I  -.029180  .000115   .585848  .000152  I  .2819499  .0000141  2.4533 0.0099  I   -29.094    1.043    -9.383     .298  -.029300   .584800   .2819500   -28.700    -9.500  
+96 510 50213.00 I  -.024728  .000110   .587025  .000157  I  .2794478  .0000144  2.5525 0.0098  I   -29.457    1.043    -9.335     .298  -.024900   .586000   .2794900   -28.400    -9.300  
+96 511 50214.00 I  -.020330  .000086   .588093  .000151  I  .2768482  .0000135  2.6416 0.0093  I   -29.424     .934    -9.336     .298  -.020800   .587000   .2768600   -28.300    -9.200  
+96 512 50215.00 I  -.015922  .000091   .589125  .000121  I  .2741811  .0000117  2.6811 0.0084  I   -29.110     .791    -9.319     .298  -.016300   .587800   .2741700   -28.100    -9.100  
+96 513 50216.00 I  -.011521  .000088   .590154  .000137  I  .2715088  .0000101  2.6523 0.0078  I   -28.799     .791    -9.232     .298  -.011700   .589000   .2714900   -28.100    -9.000  
+96 514 50217.00 I  -.007268  .000104   .591099  .000148  I  .2688973  .0000103  2.5610 0.0069  I   -28.748     .791    -9.102     .298  -.007800   .589800   .2688800   -28.300    -8.900  
+96 515 50218.00 I  -.003399  .000112   .591911  .000133  I  .2663992  .0000095  2.4324 0.0070  I   -28.967     .791    -9.008     .298  -.004000   .590700   .2663800   -28.500    -9.000  
+96 516 50219.00 I  -.000024  .000114   .592626  .000134  I  .2640413  .0000095  2.2775 0.0065  I   -29.226     .791    -8.988     .298  -.000300   .591600   .2640100   -28.600    -9.000  
+96 517 50220.00 I   .003144  .000109   .593209  .000140  I  .2618504  .0000090  2.1039 0.0068  I   -29.296     .791    -9.005     .298   .003000   .592300   .2618600   -28.800    -9.000  
+96 518 50221.00 I   .006435  .000110   .593685  .000131  I  .2598275  .0000096  1.9484 0.0077  I   -29.186     .791    -9.005     .298   .006000   .592600   .2598300   -28.800    -9.200  
+96 519 50222.00 I   .009919  .000123   .594127  .000116  I  .2579410  .0000125  1.8307 0.0067  I   -29.065     .791    -8.998     .298   .009700   .592900   .2579500   -28.700    -9.300  
+96 520 50223.00 I   .013491  .000122   .594555  .000111  I  .2561562  .0000094  1.7428 0.0083  I   -29.007     .791    -9.042     .298   .013400   .593600   .2561500   -28.500    -9.300  
+96 521 50224.00 I   .017124  .000103   .594917  .000120  I  .2544473  .0000109  1.6793 0.0069  I   -28.956     .791    -9.186     .298   .017100   .593900   .2544200   -28.300    -9.300  
+96 522 50225.00 I   .020937  .000098   .595164  .000115  I  .2527826  .0000102  1.6609 0.0078  I   -28.895     .791    -9.420     .298   .020800   .594100   .2527500   -28.000    -9.300  
+96 523 50226.00 I   .025152  .000109   .595261  .000115  I  .2511090  .0000112  1.6920 0.0082  I   -28.900     .791    -9.618     .298   .024800   .594000   .2511000   -27.900    -9.200  
+96 524 50227.00 I   .029779  .000105   .595317  .000111  I  .2493911  .0000129  1.7456 0.0083  I   -28.934     .791    -9.690     .298   .029600   .594000   .2493600   -27.900    -9.200  
+96 525 50228.00 I   .034665  .000098   .595469  .000115  I  .2476132  .0000122  1.8132 0.0114  I   -28.871     .791    -9.625     .298   .034400   .594300   .2476200   -28.000    -9.000  
+96 526 50229.00 I   .039753  .000092   .595702  .000149  I  .2457632  .0000188  1.8854 0.0098  I   -28.756     .791    -9.503     .298   .039400   .594600   .2457400   -28.300    -9.000  
+96 527 50230.00 I   .044841  .000092   .595957  .000175  I  .2438479  .0000153  1.9409 0.0115  I   -28.864     .791    -9.426     .298   .044700   .594700   .2438100   -28.600    -9.000  
+96 528 50231.00 I   .049615  .000087   .596238  .000155  I  .2418917  .0000134  1.9653 0.0100  I   -29.358     .791    -9.384     .298   .049500   .594900   .2418500   -29.000    -9.000  
+96 529 50232.00 I   .053882  .000096   .596541  .000156  I  .2399269  .0000128  1.9610 0.0088  I   -30.023     .791    -9.256     .298   .053800   .595400   .2398900   -29.400    -9.100  
+96 530 50233.00 I   .057700  .000094   .596714  .000163  I  .2379830  .0000114  1.9168 0.0085  I   -30.487     .791    -9.008     .298   .057500   .595600   .2379800   -29.700    -9.100  
+96 531 50234.00 I   .061444  .000097   .596642  .000164  I  .2361074  .0000111  1.8304 0.0077  I   -30.610     .791    -8.791     .298   .061200   .595600   .2361100   -30.000    -9.200  
+96 6 1 50235.00 I   .065503  .000113   .596277  .000155  I  .2343259  .0000105  1.7330 0.0075  I   -30.561     .791    -8.810     .298   .065300   .595400   .2343000   -30.100    -9.200  
+96 6 2 50236.00 I   .069860  .000121   .595604  .000121  I  .2326331  .0000101  1.6606 0.0068  I   -30.532     .791    -9.069     .298   .069800   .594500   .2326100   -30.200    -9.200  
+96 6 3 50237.00 I   .074230  .000146   .594696  .000164  I  .2309860  .0000085  1.6445 0.0073  I   -30.547     .791    -9.326     .298   .074300   .593500   .2309700   -30.100    -9.100  
+96 6 4 50238.00 I   .078467  .000146   .593622  .000190  I  .2293212  .0000105  1.6973 0.0072  I   -30.581     .791    -9.359     .298   .078400   .592900   .2292900   -29.900    -9.100  
+96 6 5 50239.00 I   .082604  .000148   .592449  .000190  I  .2275791  .0000116  1.7872 0.0087  I   -30.692     .791    -9.192     .298   .082300   .591600   .2275400   -30.000    -9.000  
+96 6 6 50240.00 I   .086784  .000143   .591323  .000184  I  .2257463  .0000138  1.8786 0.0088  I   -30.930     .791    -8.975     .298   .086400   .590000   .2257100   -29.900    -8.900  
+96 6 7 50241.00 I   .090816  .000143   .590282  .000182  I  .2238242  .0000133  1.9631 0.0097  I   -31.233     .791    -8.864     .298   .090500   .589000   .2237900   -30.000    -8.800  
+96 6 8 50242.00 I   .094546  .000115   .589194  .000162  I  .2218261  .0000136  2.0296 0.0099  I   -31.444     .791    -8.892     .298   .094500   .588000   .2218100   -30.200    -8.700  
+96 6 9 50243.00 I   .098025  .000073   .587957  .000130  I  .2197756  .0000148  2.0643 0.0094  I   -31.444     .791    -8.991     .298   .097900   .586900   .2198100   -30.500    -8.700  
+96 610 50244.00 I   .101457  .000100   .586564  .000172  I  .2177168  .0000129  2.0411 0.0099  I   -31.287     .166    -9.077     .172   .101200   .585400   .2177500   -30.900    -8.800  
+96 611 50245.00 I   .105120  .000094   .585084  .000178  I  .2157148  .0000131  1.9536 0.0088  I   -31.193     .165    -9.087     .177   .104900   .583900   .2156800   -31.200    -8.800  
+96 612 50246.00 I   .109158  .000086   .583575  .000171  I  .2138240  .0000121  1.8228 0.0092  I   -31.343     .165    -8.964     .177   .108900   .582500   .2137800   -31.500    -9.000  
+96 613 50247.00 I   .113468  .000083   .581998  .000178  I  .2120760  .0000130  1.6714 0.0088  I   -31.651     .165    -8.626     .177   .113200   .580900   .2121100   -31.700    -9.200  
+96 614 50248.00 I   .117895  .000076   .580426  .000178  I  .2104819  .0000128  1.5179 0.0093  I   -31.912     .165    -8.354     .177   .117800   .579200   .2104800   -31.900    -9.300  
+96 615 50249.00 I   .122258  .000086   .578845  .000182  I  .2090393  .0000134  1.3673 0.0099  I   -31.982     .197    -8.356     .211   .122200   .577800   .2089500   -32.000    -9.400  
+96 616 50250.00 I   .126467  .000109   .577104  .000144  I  .2077336  .0000150  1.2579 0.0086  I   -31.881     .162    -8.552     .187   .126100   .576400   .2077400   -31.800    -9.500  
+96 617 50251.00 I   .130498  .000090   .575237  .000125  I  .2065038  .0000108  1.2075 0.0101  I   -31.732     .791    -8.801     .298   .130300   .574100   .2064900   -31.800    -9.400  
+96 618 50252.00 I   .134268  .000094   .573331  .000167  I  .2053055  .0000136  1.1962 0.0093  I   -31.645     .791    -8.996     .298   .134300   .572000   .2052500   -31.600    -9.400  
+96 619 50253.00 I   .137862  .000123   .571399  .000193  I  .2040955  .0000151  1.2324 0.0103  I   -31.733     .383    -9.125     .185   .137800   .570400   .2040700   -31.500    -9.200  
+96 620 50254.00 I   .141523  .000125   .569469  .000193  I  .2028277  .0000155  1.3076 0.0107  I   -32.068     .361    -9.204     .160   .141400   .568400   .2028700   -31.500    -9.000  
+96 621 50255.00 I   .145303  .000121   .567674  .000189  I  .2014758  .0000151  1.3962 0.0111  I   -32.526     .361    -9.185     .160   .145100   .566600   .2015300   -31.600    -8.900  
+96 622 50256.00 I   .149172  .000116   .566173  .000189  I  .2000356  .0000159  1.4838 0.0110  I   -32.804     .361    -9.013     .160   .148900   .565100   .2000600   -31.800    -8.900  
+96 623 50257.00 I   .153039  .000102   .564948  .000191  I  .1985142  .0000161  1.5532 0.0095  I   -32.842     .465    -8.803     .173   .152900   .564000   .1984900   -32.100    -8.800  
+96 624 50258.00 I   .156765  .000108   .563751  .000167  I  .1969418  .0000103  1.5847 0.0084  I   -32.954     .463    -8.676     .169   .156600   .562700   .1969100   -32.500    -8.800  
+96 625 50259.00 I   .160185  .000088   .562310  .000087  I  .1953602  .0000050  1.5701 0.0056  I   -33.423     .458    -8.732     .116   .159800   .561300   .1953400   -32.800    -8.900  
+96 626 50260.00 I   .163281  .000085   .560423  .000094  I  .1938095  .0000045  1.5319 0.0035  I   -34.065     .429    -8.894     .116   .162900   .559500   .1937800   -33.200    -8.900  
+96 627 50261.00 I   .166362  .000090   .558025  .000101  I  .1923025  .0000050  1.4755 0.0034  I   -34.561     .429    -8.855     .116   .166200   .556800   .1923000   -33.700    -8.900  
+96 628 50262.00 I   .169493  .000098   .555334  .000099  I  .1908709  .0000050  1.3818 0.0035  I   -34.691     .429    -8.649     .116   .169200   .554100   .1908900   -34.100    -8.900  
+96 629 50263.00 I   .172608  .000096   .552597  .000113  I  .1895423  .0000048  1.2778 0.0045  I   -34.607     .319    -8.556     .108   .172200   .551400   .1895400   -34.500    -8.900  
+96 630 50264.00 I   .175817  .000090   .549915  .000137  I  .1883068  .0000074  1.1992 0.0050  I   -34.604     .791    -8.675     .298   .175600   .548600   .1882900   -34.800    -8.700  
+96 7 1 50265.00 I   .179070  .000128   .547255  .000148  I  .1871278  .0000087  1.1689 0.0109  I   -34.769     .791    -8.814     .298   .179000   .545900   .1871200   -34.900    -8.600  
+96 7 2 50266.00 I   .182205  .000154   .544555  .000191  I  .1859475  .0000205  1.2031 0.0113  I   -34.999     .791    -8.744     .298   .182300   .543100   .1859300   -35.000    -8.500  
+96 7 3 50267.00 I   .185266  .000151   .541804  .000203  I  .1846984  .0000209  1.3070 0.0149  I   -35.206     .791    -8.482     .298   .185200   .540600   .1846700   -35.000    -8.400  
+96 7 4 50268.00 I   .188487  .000146   .539052  .000212  I  .1833305  .0000216  1.4201 0.0148  I   -35.411     .791    -8.248     .298   .188100   .537900   .1833800   -35.000    -8.400  
+96 7 5 50269.00 I   .191979  .000141   .536334  .000193  I  .1818802  .0000209  1.4673 0.0146  I   -35.707     .791    -8.181     .298   .191700   .534600   .1820100   -35.000    -8.400  
+96 7 6 50270.00 I   .195778  .000138   .533729  .000179  I  .1804193  .0000195  1.4444 0.0127  I   -36.123     .791    -8.244     .298   .195600   .531800   .1805700   -35.200    -8.300  
+96 7 7 50271.00 I   .199792  .000133   .531295  .000169  I  .1790079  .0000146  1.3721 0.0106  I   -36.514     .791    -8.360     .298   .199700   .529500   .1791100   -35.200    -8.500  
+96 7 8 50272.00 I   .203823  .000149   .528995  .000150  I  .1776848  .0000085  1.2706 0.0090  I   -36.655     .791    -8.526     .298   .203900   .527500   .1777200   -35.400    -8.600  
+96 7 9 50273.00 I   .207687  .000170   .526712  .000172  I  .1764712  .0000105  1.1554 0.0067  I   -36.524     .791    -8.726     .298   .207800   .525300   .1764400   -35.500    -8.800  
+96 710 50274.00 I   .211388  .000184   .524315  .000175  I  .1753711  .0000103  1.0487 0.0071  I   -36.416     .164    -8.858     .298   .211100   .523000   .1753400   -35.700    -8.800  
+96 711 50275.00 I   .215158  .000179   .521696  .000172  I  .1743692  .0000097  0.9557 0.0071  I   -36.639     .164    -8.827     .298   .215000   .520300   .1743700   -35.900    -9.000  
+96 712 50276.00 I   .218752  .000171   .518866  .000170  I  .1734575  .0000097  0.8694 0.0067  I   -37.114     .164    -8.713     .298   .218500   .517300   .1734500   -36.000    -9.000  
+96 713 50277.00 I   .221889  .000168   .515895  .000155  I  .1726196  .0000091  0.8157 0.0076  I   -37.464     .164    -8.688     .298   .221500   .514400   .1725800   -36.000    -8.900  
+96 714 50278.00 I   .224827  .000136   .512845  .000160  I  .1718092  .0000116  0.8127 0.0065  I   -37.448     .179    -8.781     .298   .224800   .511400   .1718100   -36.100    -8.800  
+96 715 50279.00 I   .227709  .000132   .509712  .000143  I  .1709819  .0000092  0.8465 0.0066  I   -37.134     .189    -8.855     .298   .227600   .508500   .1710000   -36.100    -8.700  
+96 716 50280.00 I   .230519  .000104   .506559  .000117  I  .1701089  .0000062  0.9022 0.0056  I   -36.745     .152    -8.790     .298   .230200   .505200   .1701000   -36.100    -8.600  
+96 717 50281.00 I   .233085  .000110   .503531  .000097  I  .1691727  .0000063  0.9725 0.0048  I   -36.504     .152    -8.636     .298   .232800   .502000   .1691400   -36.200    -8.600  
+96 718 50282.00 I   .235301  .000117   .500701  .000087  I  .1681581  .0000074  1.0603 0.0048  I   -36.580     .152    -8.534     .298   .235000   .499200   .1681300   -36.200    -8.400  
+96 719 50283.00 I   .237275  .000121   .497932  .000086  I  .1670524  .0000073  1.1472 0.0048  I   -36.866     .152    -8.572     .298   .237200   .497000   .1670400   -36.300    -8.400  
+96 720 50284.00 I   .238918  .000102   .495123  .000149  I  .1658696  .0000061  1.2163 0.0054  I   -37.048     .155    -8.664     .298   .239100   .493900   .1658600   -36.500    -8.400  
+96 721 50285.00 I   .240184  .000110   .492260  .000180  I  .1646247  .0000079  1.2707 0.0047  I   -36.970     .456    -8.684     .134   .240000   .490700   .1645800   -36.700    -8.500  
+96 722 50286.00 I   .241314  .000111   .489368  .000202  I  .1633338  .0000072  1.3085 0.0047  I   -36.881     .468    -8.648     .148   .241100   .487900   .1632900   -36.900    -8.500  
+96 723 50287.00 I   .242591  .000086   .486495  .000169  I  .1620133  .0000051  1.3296 0.0044  I   -37.183     .468    -8.681     .148   .242800   .484800   .1620200   -37.200    -8.700  
+96 724 50288.00 I   .244009  .000084   .483674  .000170  I  .1606780  .0000049  1.3405 0.0037  I   -37.914     .406    -8.810     .130   .244000   .482100   .1606700   -37.500    -8.900  
+96 725 50289.00 I   .245469  .000087   .480888  .000191  I  .1593401  .0000053  1.3279 0.0036  I   -38.712     .406    -8.841     .130   .245000   .479800   .1593400   -37.800    -9.000  
+96 726 50290.00 I   .247079  .000090   .478037  .000195  I  .1580361  .0000054  1.2735 0.0037  I   -39.162     .406    -8.761     .130   .246800   .476700   .1580200   -38.100    -9.200  
+96 727 50291.00 I   .248835  .000088   .475059  .000168  I  .1567921  .0000053  1.2222 0.0040  I   -39.230     .139    -8.752     .298   .248700   .473500   .1567500   -38.200    -9.300  
+96 728 50292.00 I   .250734  .000066   .472110  .000160  I  .1555719  .0000059  1.2304 0.0038  I   -39.206     .791    -8.875     .298   .250700   .470400   .1555600   -38.500    -9.300  
+96 729 50293.00 I   .252661  .000068   .469292  .000148  I  .1543073  .0000054  1.3107 0.0050  I   -39.316     .791    -8.994     .298   .252700   .467600   .1542900   -38.700    -9.200  
+96 730 50294.00 I   .254453  .000072   .466543  .000190  I  .1529305  .0000080  1.4510 0.0052  I   -39.543     .791    -8.940     .298   .254300   .465200   .1529300   -38.900    -9.000  
+96 731 50295.00 I   .256087  .000063   .463725  .000180  I  .1513964  .0000089  1.6182 0.0071  I   -39.717     .791    -8.754     .298   .255900   .462600   .1513900   -39.200    -8.800  
+96 8 1 50296.00 I   .257649  .000058   .460694  .000139  I  .1497082  .0000117  1.7433 0.0075  I   -39.727     .791    -8.625     .298   .257700   .459300   .1496800   -39.500    -8.700  
+96 8 2 50297.00 I   .259242  .000055   .457382  .000130  I  .1479439  .0000121  1.7662 0.0083  I   -39.698     .791    -8.628     .298   .259000   .456300   .1479100   -39.800    -8.400  
+96 8 3 50298.00 I   .260754  .000066   .453772  .000118  I  .1462136  .0000118  1.6758 0.0142  I   -39.895     .791    -8.631     .298   .260500   .452800   .1461800   -40.100    -8.400  
+96 8 4 50299.00 I   .262152  .000057   .449899  .000094  I  .1446201  .0000258  1.5020 0.0127  I   -40.375     .791    -8.529     .298   .262200   .448300   .1445800   -40.300    -8.300  
+96 8 5 50300.00 I   .263615  .000070   .445865  .000177  I  .1432182  .0000225  1.3024 0.0186  I   -40.820     .397    -8.427     .298   .263600   .444600   .1432100   -40.300    -8.300  
+96 8 6 50301.00 I   .265323  .000090   .441767  .000171  I  .1420121  .0000268  1.1126 0.0185  I   -40.895     .405    -8.496     .298   .265100   .440500   .1420100   -40.300    -8.400  
+96 8 7 50302.00 I   .267376  .000103   .437702  .000177  I  .1409883  .0000293  0.9371 0.0204  I   -40.729     .405    -8.727     .298   .267100   .436500   .1410100   -40.300    -8.400  
+96 8 8 50303.00 I   .269796  .000111   .433842  .000176  I  .1401274  .0000308  0.7933 0.0215  I   -40.798     .405    -8.954     .298   .269400   .432600   .1401400   -40.400    -8.600  
+96 8 9 50304.00 I   .272298  .000105   .430200  .000197  I  .1393817  .0000314  0.7096 0.0204  I   -41.176     .405    -9.030     .298   .272200   .429000   .1394400   -40.600    -8.800  
+96 810 50305.00 I   .274464  .000112   .426635  .000172  I  .1386936  .0000267  0.6697 0.0222  I   -41.526     .405    -9.004     .298   .274400   .425200   .1386800   -40.700    -8.800  
+96 811 50306.00 I   .276292  .000104   .423082  .000163  I  .1380287  .0000314  0.6707 0.0171  I   -41.524     .294    -8.971     .298   .276000   .421400   .1379900   -40.800    -8.900  
+96 812 50307.00 I   .278048  .000114   .419545  .000113  I  .1373368  .0000215  0.7178 0.0178  I   -41.175     .791    -8.898     .298   .277800   .418000   .1372800   -40.800    -8.800  
+96 813 50308.00 I   .279980  .000102   .416016  .000103  I  .1365864  .0000168  0.7850 0.0129  I   -40.735     .791    -8.723     .298   .279900   .414800   .1365800   -40.600    -8.800  
+96 814 50309.00 I   .282112  .000117   .412506  .000110  I  .1357628  .0000142  0.8642 0.0107  I   -40.421     .791    -8.510     .298   .282000   .411200   .1357600   -40.100    -8.700  
+96 815 50310.00 I   .284194  .000105   .409021  .000089  I  .1348547  .0000133  0.9535 0.0091  I   -40.320     .791    -8.397     .298   .283900   .407800   .1348200   -39.600    -8.500  
+96 816 50311.00 I   .285973  .000100   .405504  .000084  I  .1338546  .0000115  1.0465 0.0078  I   -40.398     .791    -8.468     .298   .286100   .404200   .1338600   -39.400    -8.400  
+96 817 50312.00 I   .287390  .000103   .401913  .000088  I  .1327661  .0000083  1.1262 0.0072  I   -40.518     .791    -8.647     .298   .287200   .400700   .1327800   -39.400    -8.400  
+96 818 50313.00 I   .288667  .000072   .398273  .000095  I  .1316146  .0000085  1.1688 0.0056  I   -40.537     .791    -8.782     .298   .288100   .396900   .1315700   -39.400    -8.400  
+96 819 50314.00 I   .290024  .000078   .394658  .000078  I  .1304407  .0000074  1.1749 0.0054  I   -40.479     .791    -8.808     .298   .289900   .393200   .1304000   -39.500    -8.400  
+96 820 50315.00 I   .291404  .000066   .391108  .000078  I  .1292722  .0000066  1.1588 0.0058  I   -40.559     .791    -8.782     .298   .291000   .389900   .1292800   -39.800    -8.500  
+96 821 50316.00 I   .292692  .000065   .387592  .000071  I  .1281259  .0000089  1.1341 0.0064  I   -40.933     .791    -8.773     .298   .292200   .386400   .1281100   -40.100    -8.700  
+96 822 50317.00 I   .293933  .000065   .384110  .000067  I  .1270006  .0000109  1.1194 0.0071  I   -41.481     .791    -8.794     .298   .293900   .382600   .1269900   -40.200    -8.800  
+96 823 50318.00 I   .295088  .000063   .380662  .000085  I  .1258849  .0000111  1.1111 0.0084  I   -41.919     .791    -8.842     .298   .294800   .379300   .1258700   -40.300    -9.000  
+96 824 50319.00 I   .296087  .000096   .377192  .000122  I  .1247746  .0000128  1.1140 0.0121  I   -42.099     .791    -8.937     .298   .295600   .376000   .1247200   -40.500    -9.100  
+96 825 50320.00 I   .296841  .000105   .373579  .000148  I  .1236414  .0000215  1.1633 0.0098  I   -42.109     .767    -9.062     .298   .296700   .372300   .1235900   -40.700    -9.200  
+96 826 50321.00 I   .297290  .000157   .369723  .000144  I  .1224278  .0000148  1.2731 0.0126  I   -42.127     .661    -9.102     .298   .297100   .368300   .1224000   -41.000    -9.100  
+96 827 50322.00 I   .297493  .000158   .365659  .000148  I  .1210837  .0000131  1.4173 0.0092  I   -42.204     .575    -8.971     .298   .297000   .364100   .1210700   -41.300    -9.100  
+96 828 50323.00 I   .297670  .000164   .361446  .000146  I  .1195813  .0000110  1.5972 0.0092  I   -42.215     .575    -8.755     .298   .297300   .360100   .1195700   -41.600    -8.900  
+96 829 50324.00 I   .298033  .000165   .357036  .000140  I  .1178957  .0000130  1.7578 0.0084  I   -42.082     .575    -8.621     .298   .297900   .355700   .1179000   -41.900    -8.700  
+96 830 50325.00 I   .298180  .000167   .352481  .000139  I  .1160999  .0000128  1.8157 0.0086  I   -41.825     .575    -8.648     .298   .297700   .351500   .1161200   -42.100    -8.500  
+96 831 50326.00 I   .297996  .000153   .347920  .000095  I  .1142992  .0000114  1.7689 0.0083  I   -41.710     .269    -8.670     .298   .297500   .346700   .1143200   -42.200    -8.300  
+96 9 1 50327.00 I   .297595  .000102   .343439  .000088  I  .1125912  .0000107  1.6347 0.0072  I   -41.977     .143    -8.489     .298   .297200   .342200   .1126200   -42.200    -8.100  
+96 9 2 50328.00 I   .297111  .000110   .339045  .000075  I  .1110459  .0000089  1.4523 0.0068  I   -42.430     .277    -8.184     .298   .296800   .337900   .1111500   -42.200    -8.000  
+96 9 3 50329.00 I   .296552  .000117   .334745  .000077  I  .1096902  .0000083  1.2592 0.0056  I   -42.608     .234    -8.023     .298   .296500   .333200   .1098300   -42.100    -8.100  
+96 9 4 50330.00 I   .295960  .000112   .330523  .000078  I  .1085160  .0000067  1.1007 0.0053  I   -42.366     .305    -8.154     .152   .295700   .329200   .1085500   -42.000    -8.100  
+96 9 5 50331.00 I   .295473  .000114   .326376  .000077  I  .1074717  .0000067  0.9935 0.0041  I   -42.104     .279    -8.470     .146   .295100   .325300   .1073800   -41.700    -8.300  
+96 9 6 50332.00 I   .294897  .000109   .322302  .000078  I  .1065122  .0000048  0.9367 0.0042  I   -42.107     .279    -8.807     .146   .294600   .321100   .1064600   -41.700    -8.500  
+96 9 7 50333.00 I   .294281  .000118   .318273  .000083  I  .1055830  .0000051  0.9258 0.0042  I   -42.159     .299    -8.926     .163   .294000   .316900   .1055800   -41.700    -8.600  
+96 9 8 50334.00 I   .293581  .000101   .314370  .000080  I  .1046463  .0000068  0.9578 0.0047  I   -42.023     .248    -8.902     .163   .293300   .313300   .1046500   -41.600    -8.800  
+96 9 9 50335.00 I   .292918  .000102   .310562  .000082  I  .1036476  .0000079  1.0491 0.0062  I   -41.746     .243    -8.853     .140   .292600   .309500   .1036700   -41.600    -8.700  
+96 910 50336.00 I   .292468  .000093   .306814  .000071  I  .1025295  .0000103  1.1963 0.0094  I   -41.537     .791    -8.756     .298   .292200   .305500   .1025400   -41.600    -8.700  
+96 911 50337.00 I   .292078  .000087   .303180  .000065  I  .1012360  .0000170  1.4005 0.0096  I   -41.497     .791    -8.620     .298   .291700   .302000   .1012300   -41.700    -8.600  
+96 912 50338.00 I   .291441  .000082   .299621  .000075  I  .0997288  .0000162  1.6039 0.0115  I   -41.534     .791    -8.506     .298   .291100   .298400   .0997500   -41.700    -8.400  
+96 913 50339.00 I   .290440  .000076   .295973  .000076  I  .0980515  .0000154  1.7374 0.0094  I   -41.571     .791    -8.430     .298   .290200   .294600   .0980600   -41.800    -8.300  
+96 914 50340.00 I   .289166  .000060   .292145  .000096  I  .0962769  .0000094  1.8023 0.0085  I   -41.639     .791    -8.378     .298   .288800   .291300   .0962500   -41.900    -8.200  
+96 915 50341.00 I   .287719  .000074   .288083  .000095  I  .0944635  .0000074  1.8175 0.0053  I   -41.718     .791    -8.343     .298   .287500   .287200   .0944700   -41.900    -8.200  
+96 916 50342.00 I   .286237  .000072   .283819  .000095  I  .0926574  .0000048  1.7863 0.0042  I   -41.709     .791    -8.328     .298   .286000   .282600   .0927000   -41.800    -8.200  
+96 917 50343.00 I   .284921  .000075   .279497  .000105  I  .0909065  .0000038  1.7084 0.0029  I   -41.606     .791    -8.354     .298   .284000   .278100   .0908900   -41.800    -8.200  
+96 918 50344.00 I   .283911  .000076   .275292  .000097  I  .0892429  .0000033  1.6237 0.0028  I   -41.559     .791    -8.432     .298   .283300   .274100   .0892200   -41.600    -8.400  
+96 919 50345.00 I   .283203  .000081   .271200  .000093  I  .0876477  .0000042  1.5732 0.0026  I   -41.688     .791    -8.488     .298   .282900   .270000   .0876400   -41.200    -8.500  
+96 920 50346.00 I   .282657  .000078   .267301  .000096  I  .0860787  .0000039  1.5759 0.0031  I   -41.923     .791    -8.491     .298   .282500   .266000   .0860000   -41.000    -8.500  
+96 921 50347.00 I   .282139  .000071   .263790  .000096  I  .0844801  .0000045  1.6258 0.0040  I   -42.120     .122    -8.456     .298   .281900   .262400   .0844400   -40.700    -8.600  
+96 922 50348.00 I   .281419  .000060   .260549  .000110  I  .0828080  .0000069  1.7330 0.0040  I   -42.210     .129    -8.452     .298   .281100   .259300   .0828300   -40.700    -8.500  
+96 923 50349.00 I   .280372  .000066   .257324  .000102  I  .0809917  .0000066  1.9075 0.0052  I   -42.221     .139    -8.439     .298   .280100   .256600   .0810400   -40.700    -8.500  
+96 924 50350.00 I   .278936  .000060   .253900  .000086  I  .0789841  .0000077  2.1086 0.0060  I   -42.204     .161    -8.344     .298   .278500   .253000   .0790100   -41.000    -8.400  
+96 925 50351.00 I   .277230  .000067   .250239  .000094  I  .0767836  .0000101  2.2825 0.0064  I   -42.146     .191    -8.206     .298   .276800   .249000   .0767800   -41.300    -8.200  
+96 926 50352.00 I   .275516  .000087   .246473  .000150  I  .0744490  .0000102  2.3668 0.0069  I   -42.005     .191    -8.075     .298   .275400   .245200   .0744500   -41.700    -8.100  
+96 927 50353.00 I   .273808  .000091   .242655  .000150  I  .0720875  .0000095  2.3387 0.0060  I   -41.738     .152    -8.036     .298   .273500   .241500   .0720500   -42.100    -7.900  
+96 928 50354.00 I   .272150  .000094   .238863  .000154  I  .0698002  .0000064  2.2247 0.0058  I   -41.488     .152    -8.011     .298   .271400   .237300   .0697300   -42.300    -7.700  
+96 929 50355.00 I   .270516  .000093   .235209  .000170  I  .0676504  .0000068  2.0738 0.0044  I   -41.480     .791    -7.819     .115   .269700   .233500   .0676500   -42.300    -7.600  
+96 930 50356.00 I   .268914  .000103   .231685  .000142  I  .0656491  .0000059  1.9333 0.0040  I   -41.709     .173    -7.466     .101   .268400   .230500   .0656900   -42.200    -7.400  
+9610 1 50357.00 I   .267380  .000095   .228141  .000148  I  .0637705  .0000043  1.8328 0.0036  I   -41.890     .285    -7.213     .298   .266800   .227100   .0638000   -41.900    -7.400  
+9610 2 50358.00 I   .265754  .000082   .224491  .000134  I  .0619873  .0000041  1.7209 0.0031  I   -41.789     .250    -7.323     .298   .265200   .223300   .0619900   -41.500    -7.500  
+9610 3 50359.00 I   .263829  .000079   .220650  .000110  I  .0603299  .0000044  1.6054 0.0030  I   -41.471     .247    -7.677     .298   .263400   .219300   .0603100   -41.000    -7.600  
+9610 4 50360.00 I   .261799  .000074   .216746  .000105  I  .0587553  .0000043  1.5537 0.0032  I   -41.054     .247    -8.029     .298   .261400   .215700   .0587300   -40.700    -7.800  
+9610 5 50361.00 I   .259851  .000093   .213089  .000101  I  .0572015  .0000047  1.5651 0.0032  I   -40.668     .276    -8.307     .298   .259500   .212000   .0571500   -40.300    -8.100  
+9610 6 50362.00 I   .257961  .000070   .209664  .000114  I  .0556113  .0000048  1.6178 0.0035  I   -40.416     .249    -8.470     .298   .257600   .208500   .0555600   -40.000    -8.300  
+9610 7 50363.00 I   .256087  .000067   .206436  .000116  I  .0539591  .0000053  1.6910 0.0038  I   -40.347     .131    -8.503     .298   .255700   .205200   .0539600   -39.900    -8.400  
+9610 8 50364.00 I   .254144  .000068   .203440  .000088  I  .0522227  .0000058  1.7840 0.0048  I   -40.475     .125    -8.431     .298   .253800   .202300   .0522300   -39.900    -8.300  
+9610 9 50365.00 I   .252026  .000068   .200631  .000093  I  .0503866  .0000080  1.8904 0.0050  I   -40.692     .791    -8.256     .298   .251600   .199400   .0503900   -40.100    -8.200  
+961010 50366.00 I   .249757  .000067   .197861  .000089  I  .0484432  .0000081  1.9928 0.0054  I   -40.814     .791    -7.997     .298   .249400   .196800   .0484400   -40.300    -8.000  
+961011 50367.00 I   .247358  .000077   .194971  .000084  I  .0464105  .0000074  2.0668 0.0047  I   -40.798     .791    -7.678     .298   .246900   .193900   .0463800   -40.500    -7.800  
+961012 50368.00 I   .244908  .000068   .191925  .000084  I  .0443206  .0000048  2.1079 0.0046  I   -40.751     .791    -7.368     .298   .244400   .190700   .0443200   -40.500    -7.500  
+961013 50369.00 I   .242479  .000074   .188831  .000085  I  .0422045  .0000056  2.1196 0.0034  I   -40.738     .791    -7.164     .298   .242000   .187700   .0422100   -40.500    -7.300  
+961014 50370.00 I   .239981  .000080   .185792  .000080  I  .0400921  .0000047  2.0990 0.0034  I   -40.669     .180    -7.141     .298   .239500   .184700   .0401100   -40.400    -7.200  
+961015 50371.00 I   .237451  .000084   .182854  .000086  I  .0380178  .0000039  2.0444 0.0030  I   -40.444     .188    -7.256     .298   .236900   .181500   .0380400   -40.200    -7.100  
+961016 50372.00 I   .235135  .000084   .180027  .000084  I  .0360083  .0000037  1.9747 0.0029  I   -40.135     .216    -7.379     .298   .234600   .178800   .0360100   -39.800    -7.100  
+961017 50373.00 I   .232875  .000088   .177326  .000086  I  .0340670  .0000044  1.9090 0.0030  I   -39.988     .193    -7.476     .298   .232300   .176200   .0340600   -39.400    -7.200  
+961018 50374.00 I   .230420  .000093   .174678  .000087  I  .0321772  .0000046  1.8826 0.0038  I   -40.099     .193    -7.516     .298   .230000   .173400   .0321500   -39.100    -7.300  
+961019 50375.00 I   .227845  .000088   .172031  .000109  I  .0302706  .0000062  1.9499 0.0043  I   -40.282     .207    -7.501     .298   .227500   .170800   .0302700   -38.900    -7.400  
+961020 50376.00 I   .225250  .000099   .169455  .000113  I  .0282566  .0000073  2.0797 0.0056  I   -40.347     .191    -7.413     .298   .224900   .168300   .0283100   -38.800    -7.300  
+961021 50377.00 I   .222714  .000104   .166943  .000105  I  .0261129  .0000094  2.2044 0.0070  I   -40.237     .180    -7.237     .298   .222200   .165600   .0261100   -38.700    -7.300  
+961022 50378.00 I   .220149  .000108   .164399  .000106  I  .0238554  .0000120  2.3063 0.0091  I   -40.009     .128    -7.016     .298   .219700   .163000   .0238100   -38.800    -7.200  
+961023 50379.00 I   .217347  .000119   .161682  .000118  I  .0215014  .0000155  2.4050 0.0106  I   -39.770     .155    -6.840     .298   .217100   .160500   .0215000   -39.000    -6.900  
+961024 50380.00 I   .214308  .000116   .158809  .000111  I  .0190585  .0000175  2.4645 0.0113  I   -39.617     .155    -6.724     .298   .213900   .157900   .0190400   -39.100    -6.700  
+961025 50381.00 I   .211321  .000120   .155815  .000110  I  .0166062  .0000164  2.4228 0.0117  I   -39.515     .155    -6.738     .298   .210900   .154600   .0165600   -39.300    -6.500  
+961026 50382.00 I   .208747  .000111   .152758  .000096  I  .0142398  .0000154  2.3005 0.0122  I   -39.402     .154    -6.863     .298   .208300   .151400   .0142000   -39.400    -6.400  
+961027 50383.00 I   .206648  .000096   .149723  .000113  I  .0120193  .0000181  2.1361 0.0091  I   -39.287     .791    -6.935     .298   .206100   .148400   .0119700   -39.500    -6.300  
+961028 50384.00 I   .204727  .000077   .146782  .000151  I  .0099692  .0000096  1.9670 0.0106  I   -39.198     .791    -6.828     .298   .204300   .145500   .0099500   -39.300    -6.300  
+961029 50385.00 I   .202616  .000079   .143945  .000153  I  .0080787  .0000112  1.8177 0.0066  I   -39.110     .791    -6.614     .298   .202100   .142900   .0080900   -39.000    -6.400  
+961030 50386.00 I   .200095  .000059   .141147  .000163  I  .0063248  .0000090  1.6953 0.0074  I   -38.940     .791    -6.509     .298   .199500   .139900   .0063400   -38.600    -6.500  
+961031 50387.00 I   .197246  .000058   .138301  .000162  I  .0046700  .0000096  1.6272 0.0066  I   -38.613     .791    -6.647     .298   .197100   .137000   .0046300   -38.200    -6.600  
+9611 1 50388.00 I   .194360  .000064   .135259  .000165  I  .0030512  .0000096  1.6166 0.0064  I   -38.142     .791    -6.945     .298   .194300   .134100   .0030100   -37.700    -6.700  
+9611 2 50389.00 I   .191481  .000054   .132248  .000148  I  .0014209  .0000086  1.6535 0.0065  I   -37.631     .791    -7.213     .298   .191000   .130900   .0014500   -37.300    -6.800  
+9611 3 50390.00 I   .188491  .000062   .129471  .000149  I -.0002731  .0000087  1.7422 0.0055  I   -37.232     .791    -7.315     .130   .188200   .128200  -.0002500   -37.000    -7.000  
+9611 4 50391.00 I   .185317  .000109   .126988  .000090  I -.0020740  .0000068  1.8625 0.0054  I   -37.058     .791    -7.226     .106   .185100   .125800  -.0021100   -36.800    -7.100  
+9611 5 50392.00 I   .181928  .000106   .124826  .000082  I -.0040039  .0000063  2.0003 0.0043  I   -37.116     .277    -7.019     .298   .181500   .123400  -.0040700   -36.700    -7.000  
+9611 6 50393.00 I   .178262  .000102   .122939  .000131  I -.0060643  .0000053  2.1074 0.0041  I   -37.305     .294    -6.899     .298   .177700   .121700  -.0060700   -36.800    -7.000  
+9611 7 50394.00 I   .174164  .000103   .121176  .000133  I -.0082014  .0000054  2.1626 0.0035  I   -37.469     .273    -6.823     .298   .173600   .120000  -.0081500   -36.900    -7.000  
+9611 8 50395.00 I   .170037  .000105   .119377  .000142  I -.0103803  .0000046  2.1900 0.0036  I   -37.495     .299    -6.638     .298   .169500   .118000  -.0104100   -37.000    -6.900  
+9611 9 50396.00 I   .166197  .000103   .117456  .000151  I -.0125639  .0000048  2.1648 0.0032  I   -37.514     .327    -6.265     .298   .165800   .116000  -.0126300   -37.100    -6.800  
+961110 50397.00 I   .162466  .000078   .115589  .000163  I -.0146986  .0000045  2.1056 0.0031  I   -37.607     .376    -5.870     .298   .162100   .114300  -.0147400   -37.200    -6.700  
+961111 50398.00 I   .158663  .000099   .113955  .000160  I -.0167781  .0000040  2.0555 0.0030  I   -37.670     .328    -5.722     .126   .158200   .112600  -.0168200   -37.100    -6.700  
+961112 50399.00 I   .154633  .000103   .112544  .000165  I -.0188187  .0000039  2.0328 0.0029  I   -37.586     .319    -5.928     .120   .154300   .111200  -.0188400   -37.000    -6.700  
+961113 50400.00 I   .150422  .000133   .111200  .000162  I -.0208427  .0000042  2.0071 0.0032  I   -37.337     .329    -6.343     .120   .150200   .109900  -.0208200   -36.800    -6.600  
+961114 50401.00 I   .146079  .000131   .109862  .000152  I -.0228148  .0000051  1.9270 0.0033  I   -37.116     .294    -6.538     .108   .145700   .108800  -.0228400   -36.600    -6.600  
+961115 50402.00 I   .141568  .000135   .108374  .000150  I -.0246957  .0000052  1.8437 0.0039  I   -37.000     .294    -6.398     .108   .141500   .107300  -.0247200   -36.300    -6.500  
+961116 50403.00 I   .137457  .000152   .106632  .000152  I -.0265370  .0000058  1.8646 0.0043  I   -37.006     .271    -6.100     .298   .137400   .105400  -.0264900   -36.200    -6.400  
+961117 50404.00 I   .134081  .000153   .104805  .000166  I -.0284527  .0000068  1.9688 0.0046  I   -37.069     .219    -5.847     .298   .133700   .103500  -.0284100   -36.200    -6.100  
+961118 50405.00 I   .131054  .000146   .103105  .000195  I -.0304736  .0000072  2.0698 0.0047  I   -37.076     .203    -5.711     .298   .130800   .101900  -.0304600   -36.300    -6.000  
+961119 50406.00 I   .128026  .000130   .101643  .000190  I -.0325821  .0000066  2.1400 0.0047  I   -37.013     .179    -5.659     .298   .127800   .100400  -.0325900   -36.500    -5.800  
+961120 50407.00 I   .125019  .000101   .100437  .000173  I -.0347399  .0000060  2.1689 0.0046  I   -36.968     .343    -5.653     .167   .124600   .099200  -.0347400   -36.700    -5.600  
+961121 50408.00 I   .122162  .000095   .099507  .000171  I -.0369011  .0000065  2.1414 0.0043  I   -37.042     .343    -5.707     .167   .121700   .098300  -.0369000   -36.900    -5.400  
+961122 50409.00 I   .119269  .000090   .098733  .000172  I -.0390000  .0000062  2.0456 0.0047  I   -37.352     .343    -5.838     .167   .118800   .097400  -.0390000   -37.000    -5.300  
+961123 50410.00 I   .116042  .000090   .097932  .000158  I -.0409795  .0000067  1.9112 0.0046  I   -37.890     .343    -6.207     .167   .115600   .096900  -.0409600   -37.000    -5.200  
+961124 50411.00 I   .112575  .000075   .097007  .000143  I -.0428249  .0000067  1.7844 0.0043  I   -38.389    1.034    -6.721     .328   .112100   .096000  -.0427700   -36.800    -5.300  
+961125 50412.00 I   .108895  .000065   .096055  .000124  I -.0445572  .0000055  1.6845 0.0045  I   -38.547    1.319    -7.006     .393   .108500   .094800  -.0444900   -36.400    -5.300  
+961126 50413.00 I   .105076  .000056   .095273  .000104  I -.0462060  .0000059  1.6209 0.0035  I   -38.213    1.575    -6.785     .452   .104700   .094000  -.0461600   -35.900    -5.300  
+961127 50414.00 I   .101293  .000054   .094701  .000094  I -.0478085  .0000044  1.5857 0.0038  I   -37.349    1.575    -6.024     .452   .100900   .093400  -.0478000   -35.400    -5.400  
+961128 50415.00 I   .097465  .000062   .094238  .000094  I -.0493838  .0000049  1.5698 0.0030  I   -36.506    1.575    -5.425     .452   .097000   .093200  -.0493900   -34.800    -5.500  
+961129 50416.00 I   .093540  .000069   .093769  .000087  I -.0509576  .0000042  1.5826 0.0033  I   -35.843    1.575    -5.250     .452   .093000   .092800  -.0509700   -34.300    -5.700  
+961130 50417.00 I   .089593  .000068   .093199  .000087  I -.0525574  .0000043  1.6207 0.0038  I   -35.312    1.423    -5.330     .411   .089100   .092000  -.0525600   -34.000    -5.800  
+9612 1 50418.00 I   .085827  .000073   .092517  .000111  I -.0542025  .0000064  1.6692 0.0041  I   -35.002     .404    -5.499     .298   .085800   .091500  -.0541800   -33.700    -5.900  
+9612 2 50419.00 I   .082281  .000112   .091772  .000131  I -.0558923  .0000069  1.7071 0.0053  I   -34.933     .499    -5.663     .133   .082300   .090600  -.0558700   -33.700    -6.000  
+9612 3 50420.00 I   .078743  .000120   .091073  .000145  I -.0576114  .0000085  1.7289 0.0053  I   -34.965     .499    -5.790     .133   .078300   .089700  -.0576100   -33.800    -6.000  
+9612 4 50421.00 I   .074984  .000110   .090512  .000155  I -.0593535  .0000080  1.7607 0.0058  I   -34.992     .459    -5.849     .123   .074600   .089400  -.0593300   -34.000    -6.000  
+9612 5 50422.00 I   .070877  .000107   .089989  .000148  I -.0611357  .0000079  1.8011 0.0054  I   -35.211     .459    -5.693     .123   .070700   .089000  -.0611100   -34.400    -6.000  
+9612 6 50423.00 I   .066593  .000114   .089367  .000150  I -.0629445  .0000073  1.8077 0.0063  I   -35.635     .459    -5.341     .123   .066600   .088100  -.0629300   -34.700    -5.900  
+9612 7 50424.00 I   .062411  .000125   .088604  .000183  I -.0647376  .0000097  1.7738 0.0053  I   -35.977     .484    -4.941     .143   .062400   .087300  -.0647300   -35.100    -5.800  
+9612 8 50425.00 I   .058416  .000100   .087729  .000146  I -.0664852  .0000077  1.7194 0.0056  I   -36.008     .281    -4.632     .298   .058100   .086500  -.0664800   -35.300    -5.700  
+9612 9 50426.00 I   .054504  .000107   .086825  .000109  I -.0681764  .0000056  1.6651 0.0046  I   -35.762     .230    -4.574     .298   .054200   .085500  -.0681700   -35.300    -5.700  
+961210 50427.00 I   .050574  .000087   .085999  .000107  I -.0698227  .0000052  1.6326 0.0041  I   -35.447     .109    -4.849     .298   .050200   .084500  -.0697800   -35.200    -5.600  
+961211 50428.00 I   .046689  .000087   .085384  .000136  I -.0714549  .0000061  1.6400 0.0042  I   -35.251     .109    -5.289     .298   .046200   .084100  -.0714300   -35.000    -5.500  
+961212 50429.00 I   .043010  .000072   .085082  .000135  I -.0731165  .0000067  1.6888 0.0046  I   -35.188     .109    -5.548     .298   .042500   .084000  -.0731400   -34.700    -5.500  
+961213 50430.00 I   .039688  .000075   .085081  .000133  I -.0748463  .0000069  1.7791 0.0047  I   -35.216     .109    -5.447     .298   .039400   .083000  -.0748400   -34.500    -5.400  
+961214 50431.00 I   .036684  .000072   .085362  .000110  I -.0766865  .0000065  1.9048 0.0081  I   -35.316     .791    -5.104     .298   .036500   .083600  -.0766400   -34.300    -5.300  
+961215 50432.00 I   .033789  .000075   .085870  .000136  I -.0786548  .0000147  2.0272 0.0056  I   -35.447     .791    -4.768     .298   .033500   .084600  -.0786500   -34.300    -5.100  
+961216 50433.00 I   .030822  .000083   .086510  .000124  I -.0807293  .0000090  2.1149 0.0086  I   -35.548     .791    -4.590     .298   .030700   .085300  -.0807400   -34.400    -5.000  
+961217 50434.00 I   .027731  .000068   .087197  .000091  I -.0828705  .0000089  2.1602 0.0064  I   -35.597     .791    -4.559     .298   .027600   .085900  -.0828800   -34.700    -4.900  
+961218 50435.00 I   .024552  .000067   .087895  .000080  I -.0850383  .0000091  2.1711 0.0064  I   -35.631     .791    -4.607     .298   .024300   .086600  -.0850300   -35.100    -4.800  
+961219 50436.00 I   .021384  .000063   .088618  .000083  I -.0871961  .0000092  2.1322 0.0066  I   -35.708     .791    -4.704     .298   .020900   .087200  -.0871800   -35.500    -4.700  
+961220 50437.00 I   .018204  .000063   .089314  .000083  I -.0892816  .0000096  2.0299 0.0066  I   -35.843     .791    -4.837     .298   .017800   .088000  -.0892900   -35.900    -4.700  
+961221 50438.00 I   .015098  .000069   .089938  .000077  I -.0912487  .0000095  1.9060 0.0086  I   -35.975     .791    -4.973     .298   .014900   .088500  -.0912500   -36.200    -4.600  
+961222 50439.00 I   .012264  .000074   .090507  .000088  I -.0931030  .0000143  1.8104 0.0083  I   -35.988     .791    -5.054     .298   .012000   .089100  -.0930700   -36.300    -4.700  
+961223 50440.00 I   .009778  .000072   .091040  .000090  I -.0948805  .0000137  1.7477 0.0093  I   -35.829     .791    -5.042     .298   .009400   .089700  -.0948400   -36.100    -4.700  
+961224 50441.00 I   .007387  .000072   .091534  .000117  I -.0966031  .0000118  1.6989 0.0090  I   -35.594     .791    -4.967     .298   .007100   .090300  -.0965900   -35.800    -4.800  
+961225 50442.00 I   .004683  .000088   .091983  .000124  I -.0982859  .0000117  1.6728 0.0076  I   -35.436     .791    -4.904     .298   .004500   .090800  -.0982600   -35.400    -4.800  
+961226 50443.00 I   .001482  .000082   .092365  .000119  I -.0999606  .0000096  1.6825 0.0074  I   -35.367     .791    -4.907     .298   .001200   .091300  -.0999300   -34.900    -4.900  
+961227 50444.00 I  -.002138  .000090   .092697  .000124  I -.1016613  .0000089  1.7234 0.0082  I   -35.263     .791    -4.967     .298  -.002300   .091500  -.1016500   -34.400    -4.900  
+961228 50445.00 I  -.006054  .000136   .093050  .000178  I -.1034141  .0000134  1.7844 0.0073  I   -35.073     .791    -5.038     .298  -.006000   .091700  -.1034200   -34.100    -4.800  
+961229 50446.00 I  -.010233  .000136   .093503  .000167  I -.1052325  .0000116  1.8527 0.0090  I   -34.891     .791    -5.087     .298  -.010500   .092100  -.1052300   -33.900    -4.900  
+961230 50447.00 I  -.014631  .000130   .094090  .000157  I -.1071181  .0000119  1.9167 0.0094  I   -34.767     .791    -5.091     .298  -.014800   .092900  -.1071000   -33.900    -5.000  
+961231 50448.00 I  -.019065  .000142   .094720  .000174  I -.1090616  .0000148  1.9677 0.0100  I   -34.607     .791    -5.044     .298  -.019400   .093700  -.1090300   -34.000    -5.000  
+97 1 1 50449.00 I  -.023125  .000146   .095292  .000182  I -.1110467  .0000160  1.9984 0.0113  I   -34.381     .791    -4.984     .298  -.023120   .095270  -.1110260   -34.400    -5.000  
+97 1 2 50450.00 I  -.026846  .000142   .095921  .000187  I -.1130512  .0000172  2.0075 0.0115  I   -34.279     .791    -4.977     .298  -.026650   .095730  -.1129910   -34.600    -4.900  
+97 1 3 50451.00 I  -.030356  .000162   .096804  .000162  I -.1150536  .0000165  1.9923 0.0133  I   -34.450     .791    -5.015     .298  -.030320   .096580  -.1150660   -34.900    -4.900  
+97 1 4 50452.00 I  -.033659  .000118   .098009  .000196  I -.1170274  .0000202  1.9518 0.0125  I   -34.718     .791    -4.984     .298  -.033580   .097740  -.1170900   -35.100    -5.000  
+97 1 5 50453.00 I  -.036894  .000155   .099459  .000190  I -.1189536  .0000187  1.9006 0.0112  I   -34.770     .791    -4.808     .298  -.036610   .099330  -.1189530   -35.100    -5.000  
+97 1 6 50454.00 I  -.040341  .000118   .100969  .000158  I -.1208302  .0000095  1.8544 0.0101  I   -34.580     .149    -4.610     .298  -.040400   .100800  -.1208340   -35.000    -5.000  
+97 1 7 50455.00 I  -.043963  .000104   .102347  .000140  I -.1226704  .0000074  1.8324 0.0061  I   -34.425     .133    -4.619     .298  -.044180   .102110  -.1226990   -34.800    -5.000  
+97 1 8 50456.00 I  -.047384  .000102   .103560  .000133  I -.1245101  .0000075  1.8557 0.0054  I   -34.505     .133    -4.870     .298  -.047410   .103440  -.1245240   -34.500    -4.900  
+97 1 9 50457.00 I  -.050363  .000099   .104719  .000127  I -.1263955  .0000080  1.9202 0.0055  I   -34.731     .133    -5.121     .298  -.050390   .104490  -.1264190   -34.300    -4.900  
+97 110 50458.00 I  -.053191  .000093   .105897  .000096  I -.1283614  .0000081  2.0173 0.0060  I   -34.874     .133    -5.094     .298  -.053080   .105700  -.1283790   -34.100    -4.700  
+97 111 50459.00 I  -.056069  .000096   .107078  .000093  I -.1304323  .0000090  2.1208 0.0055  I   -34.878     .133    -4.806     .298  -.055890   .106970  -.1304310   -33.900    -4.600  
+97 112 50460.00 I  -.058933  .000100   .108271  .000094  I -.1325930  .0000073  2.1942 0.0070  I   -34.848     .114    -4.504     .298  -.058900   .108160  -.1326170   -34.000    -4.600  
+97 113 50461.00 I  -.061619  .000116   .109534  .000101  I -.1348093  .0000107  2.2333 0.0061  I   -34.887     .791    -4.366     .298  -.061840   .109360  -.1347970   -34.200    -4.400  
+97 114 50462.00 I  -.063963  .000119   .110918  .000107  I -.1370449  .0000098  2.2282 0.0074  I   -35.030     .791    -4.371     .298  -.063990   .110710  -.1370150   -34.700    -4.500  
+97 115 50463.00 I  -.066046  .000156   .112506  .000139  I -.1392469  .0000103  2.1668 0.0071  I   -35.228     .791    -4.443     .298  -.065980   .112380  -.1392600   -35.100    -4.500  
+97 116 50464.00 I  -.068034  .000125   .114400  .000139  I -.1413615  .0000104  2.0543 0.0070  I   -35.360     .791    -4.572     .298  -.068080   .114440  -.1413620   -35.500    -4.500  
+97 117 50465.00 I  -.069960  .000132   .116470  .000138  I -.1433426  .0000094  1.9031 0.0071  I   -35.345     .791    -4.767     .298  -.069680   .115950  -.1433430   -35.900    -4.600  
+97 118 50466.00 I  -.071941  .000130   .118545  .000136  I -.1451623  .0000096  1.7359 0.0067  I   -35.228     .791    -4.960     .298  -.071350   .117780  -.1451910   -36.000    -4.600  
+97 119 50467.00 I  -.074152  .000130   .120610  .000244  I -.1468202  .0000095  1.5863 0.0059  I   -35.108     .791    -5.052     .298  -.074030   .120500  -.1468210   -36.000    -4.800  
+97 120 50468.00 I  -.076773  .000166   .122686  .000205  I -.1483487  .0000068  1.4781 0.0055  I   -35.019     .223    -5.031     .298  -.076670   .122510  -.1483560   -35.700    -4.900  
+97 121 50469.00 I  -.079660  .000171   .124753  .000176  I -.1497922  .0000055  1.4171 0.0043  I   -34.944     .216    -4.987     .298  -.079820   .124740  -.1498140   -35.400    -4.900  
+97 122 50470.00 I  -.082335  .000166   .126769  .000179  I -.1512048  .0000052  1.4217 0.0039  I   -34.909     .216    -5.005     .298  -.082480   .126830  -.1512110   -34.800    -5.000  
+97 123 50471.00 I  -.084929  .000156   .128732  .000181  I -.1526485  .0000054  1.4650 0.0036  I   -34.952     .216    -5.080     .298  -.084950   .128440  -.1526570   -34.300    -5.000  
+97 124 50472.00 I  -.087687  .000162   .130552  .000187  I -.1541269  .0000051  1.4847 0.0043  I   -34.969     .216    -5.153     .298  -.087820   .130140  -.1541470   -33.800    -5.100  
+97 125 50473.00 I  -.090542  .000161   .132279  .000096  I -.1556175  .0000066  1.5031 0.0043  I   -34.899     .216    -5.216     .298  -.090550   .132210  -.1556260   -33.400    -5.100  
+97 126 50474.00 I  -.093642  .000141   .133992  .000107  I -.1571430  .0000070  1.5513 0.0057  I   -34.808     .209    -5.307     .298  -.093280   .134020  -.1571430   -33.300    -5.200  
+97 127 50475.00 I  -.096798  .000113   .135756  .000122  I -.1587242  .0000093  1.6119 0.0051  I   -34.755     .189    -5.425     .298  -.096530   .135560  -.1586830   -33.400    -5.300  
+97 128 50476.00 I  -.099828  .000118   .137597  .000122  I -.1603627  .0000073  1.6602 0.0062  I   -34.704     .189    -5.513     .298  -.099770   .137440  -.1603380   -33.700    -5.400  
+97 129 50477.00 I  -.102756  .000123   .139487  .000119  I -.1620217  .0000082  1.6399 0.0056  I   -34.655     .189    -5.554     .298  -.102820   .139450  -.1620400   -34.100    -5.500  
+97 130 50478.00 I  -.105545  .000111   .141428  .000117  I -.1636253  .0000085  1.5680 0.0055  I   -34.763     .189    -5.622     .298  -.105760   .141240  -.1636150   -34.700    -5.600  
+97 131 50479.00 I  -.108317  .000105   .143490  .000121  I -.1651635  .0000072  1.5136 0.0054  I   -35.132     .189    -5.764     .298  -.108290   .143260  -.1651560   -35.300    -5.600  
+97 2 1 50480.00 I  -.111349  .000102   .145610  .000116  I -.1666459  .0000067  1.4394 0.0048  I   -35.556     .189    -5.875     .298  -.111150   .145580  -.1666790   -35.800    -5.700  
+97 2 2 50481.00 I  -.114391  .000113   .147746  .000155  I -.1680324  .0000065  1.3356 0.0046  I   -35.706     .791    -5.791     .298  -.114590   .147850  -.1680380   -36.100    -5.700  
+97 2 3 50482.00 I  -.117215  .000107   .149884  .000140  I -.1693291  .0000062  1.2680 0.0046  I   -35.581     .791    -5.537     .298  -.117340   .149870  -.1693300   -36.200    -5.700  
+97 2 4 50483.00 I  -.119869  .000110   .152077  .000176  I -.1705907  .0000065  1.2675 0.0043  I   -35.525     .791    -5.360     .298  -.119820   .151850  -.1705910   -36.200    -5.600  
+97 2 5 50484.00 I  -.122471  .000109   .154427  .000179  I -.1718955  .0000061  1.3612 0.0049  I   -35.763     .791    -5.423     .298  -.122580   .154440  -.1718900   -36.000    -5.500  
+97 2 6 50485.00 I  -.124786  .000109   .156838  .000172  I -.1733379  .0000073  1.5294 0.0048  I   -36.095     .791    -5.534     .298  -.124840   .156950  -.1733440   -35.700    -5.400  
+97 2 7 50486.00 I  -.126743  .000113   .159259  .000180  I -.1749635  .0000075  1.7251 0.0072  I   -36.191     .791    -5.494     .298  -.126880   .159370  -.1749570   -35.400    -5.200  
+97 2 8 50487.00 I  -.128511  .000105   .161777  .000137  I -.1767825  .0000125  1.9039 0.0060  I   -35.962     .791    -5.348     .298  -.128590   .161690  -.1767640   -35.100    -5.200  
+97 2 9 50488.00 I  -.130350  .000069   .164433  .000142  I -.1787477  .0000093  2.0120 0.0074  I   -35.581     .138    -5.255     .298  -.130260   .164120  -.1787810   -35.000    -5.100  
+97 210 50489.00 I  -.132250  .000067   .167200  .000123  I -.1807865  .0000081  2.0603 0.0061  I   -35.311     .109    -5.312     .298  -.132220   .167330  -.1807960   -35.000    -5.100  
+97 211 50490.00 I  -.134165  .000084   .170000  .000171  I -.1828595  .0000078  2.0824 0.0054  I   -35.302     .148    -5.428     .298  -.134060   .170390  -.1828460   -35.200    -5.200  
+97 212 50491.00 I  -.136191  .000085   .172752  .000174  I -.1849226  .0000070  2.0184 0.0057  I   -35.471     .148    -5.471     .298  -.136190   .172770  -.1849320   -35.600    -5.300  
+97 213 50492.00 I  -.138295  .000075   .175444  .000183  I -.1868678  .0000084  1.8689 0.0056  I   -35.584     .148    -5.462     .298  -.138400   .175240  -.1868910   -35.900    -5.400  
+97 214 50493.00 I  -.140379  .000071   .178160  .000181  I -.1886653  .0000087  1.7342 0.0060  I   -35.487     .148    -5.565     .298  -.140490   .178200  -.1886420   -36.100    -5.600  
+97 215 50494.00 I  -.142530  .000067   .180941  .000181  I -.1903477  .0000085  1.6342 0.0083  I   -35.305     .139    -5.819     .298  -.142460   .181110  -.1903170   -36.300    -5.700  
+97 216 50495.00 I  -.144884  .000101   .183697  .000184  I -.1919402  .0000141  1.5538 0.0071  I   -35.287     .226    -6.058     .298  -.144880   .183840  -.1919650   -36.400    -5.800  
+97 217 50496.00 I  -.147478  .000102   .186326  .000174  I -.1934698  .0000113  1.5171 0.0085  I   -35.505     .155    -6.179     .298  -.147590   .186540  -.1934630   -36.300    -6.000  
+97 218 50497.00 I  -.150122  .000101   .188791  .000102  I -.1949925  .0000096  1.5349 0.0067  I   -35.739     .155    -6.232     .298  -.150110   .188740  -.1949620   -36.000    -6.100  
+97 219 50498.00 I  -.152594  .000105   .191120  .000090  I -.1965482  .0000073  1.5785 0.0060  I   -35.750     .123    -6.277     .298  -.152560   .191020  -.1965500   -35.600    -6.200  
+97 220 50499.00 I  -.154965  .000116   .193390  .000078  I -.1981573  .0000072  1.6453 0.0049  I   -35.525     .123    -6.279     .298  -.155120   .193450  -.1981660   -35.200    -6.200  
+97 221 50500.00 I  -.157097  .000134   .195697  .000078  I -.1998452  .0000065  1.7311 0.0048  I   -35.228     .123    -6.218     .298  -.157190   .195650  -.1998380   -34.800    -6.300  
+97 222 50501.00 I  -.158915  .000124   .198140  .000088  I -.2016223  .0000062  1.8254 0.0056  I   -34.977     .123    -6.173     .298  -.158970   .197900  -.2016180   -34.500    -6.400  
+97 223 50502.00 I  -.160453  .000083   .200800  .000118  I -.2034968  .0000090  1.9221 0.0054  I   -34.788     .791    -6.245     .298  -.160360   .200690  -.2034930   -34.300    -6.400  
+97 224 50503.00 I  -.161818  .000148   .203733  .000118  I -.2054597  .0000089  1.9982 0.0067  I   -34.681     .791    -6.450     .298  -.161690   .203580  -.2054440   -34.300    -6.500  
+97 225 50504.00 I  -.163299  .000153   .206941  .000124  I -.2074795  .0000100  2.0331 0.0067  I   -34.661     .791    -6.689     .298  -.163220   .206860  -.2074620   -34.400    -6.700  
+97 226 50505.00 I  -.165215  .000153   .210308  .000136  I -.2095110  .0000101  2.0232 0.0073  I   -34.711     .791    -6.868     .298  -.165150   .210200  -.2095060   -34.600    -6.800  
+97 227 50506.00 I  -.167652  .000151   .213624  .000136  I -.2115238  .0000105  2.0070 0.0071  I   -34.862     .791    -7.007     .298  -.167740   .213480  -.2115180   -34.900    -6.900  
+97 228 50507.00 I  -.170138  .000162   .216857  .000122  I -.2135309  .0000101  2.0085 0.0073  I   -35.126     .791    -7.176     .298  -.170330   .216900  -.2135090   -35.000    -7.000  
+97 3 1 50508.00 I  -.172256  .000163   .220043  .000110  I -.2155422  .0000102  2.0143 0.0075  I   -35.357     .791    -7.340     .298  -.172380   .219970  -.2155140   -35.200    -7.200  
+97 3 2 50509.00 I  -.173879  .000105   .223231  .000067  I -.2175595  .0000110  2.0202 0.0064  I   -35.337     .791    -7.364     .298  -.173970   .223050  -.2175420   -35.100    -7.300  
+97 3 3 50510.00 I  -.174995  .000091   .226453  .000089  I -.2195849  .0000076  2.0331 0.0065  I   -35.080     .791    -7.214     .298  -.175070   .226410  -.2195720   -35.100    -7.200  
+97 3 4 50511.00 I  -.175740  .000099   .229709  .000083  I -.2216355  .0000068  2.0753 0.0048  I   -34.900     .791    -7.053     .298  -.175650   .229730  -.2216320   -34.800    -7.200  
+97 3 5 50512.00 I  -.176415  .000096   .232987  .000080  I -.2237518  .0000059  2.1662 0.0055  I   -35.052     .791    -7.057     .298  -.176420   .232950  -.2237540   -34.600    -7.100  
+97 3 6 50513.00 I  -.177300  .000079   .236295  .000083  I -.2259897  .0000086  2.3223 0.0051  I   -35.424     .791    -7.189     .298  -.177190   .236190  -.2259670   -34.400    -7.000  
+97 3 7 50514.00 I  -.178558  .000073   .239568  .000090  I -.2284127  .0000084  2.5272 0.0063  I   -35.676     .791    -7.254     .298  -.178250   .239750  -.2283680   -34.300    -6.800  
+97 3 8 50515.00 I  -.180253  .000078   .242757  .000087  I -.2310411  .0000091  2.7230 0.0064  I   -35.608     .791    -7.179     .298  -.180110   .242900  -.2310240   -34.200    -6.700  
+97 3 9 50516.00 I  -.182213  .000109   .245858  .000072  I -.2338346  .0000097  2.8466 0.0058  I   -35.322     .791    -7.099     .298  -.182130   .245820  -.2338510   -34.300    -6.700  
+97 310 50517.00 I  -.184152  .000128   .248905  .000135  I -.2367006  .0000071  2.8693 0.0059  I   -35.098     .103    -7.134     .298  -.184210   .248920  -.2366890   -34.600    -6.800  
+97 311 50518.00 I  -.185660  .000126   .251986  .000127  I -.2395438  .0000068  2.8040 0.0043  I   -35.175     .103    -7.215     .298  -.185880   .251900  -.2395060   -34.800    -6.900  
+97 312 50519.00 I  -.186433  .000131   .255167  .000121  I -.2422867  .0000047  2.6730 0.0045  I   -35.526     .103    -7.215     .298  -.186520   .255110  -.2422750   -35.100    -7.100  
+97 313 50520.00 I  -.186654  .000150   .258479  .000124  I -.2448840  .0000058  2.5247 0.0034  I   -35.821     .103    -7.181     .298  -.186640   .258650  -.2449000   -35.400    -7.300  
+97 314 50521.00 I  -.186547  .000147   .262016  .000128  I -.2473473  .0000050  2.4100 0.0039  I   -35.740     .103    -7.281     .298  -.186430   .261920  -.2473450   -35.700    -7.600  
+97 315 50522.00 I  -.186532  .000144   .265890  .000134  I -.2497212  .0000051  2.3468 0.0061  I   -35.356     .103    -7.561     .298  -.186230   .265770  -.2496900   -35.800    -7.800  
+97 316 50523.00 I  -.186927  .000126   .270001  .000109  I -.2520565  .0000111  2.3304 0.0043  I   -35.063     .791    -7.881     .298  -.186860   .270210  -.2520300   -35.800    -8.000  
+97 317 50524.00 I  -.187669  .000129   .274026  .000103  I -.2543915  .0000070  2.3424 0.0064  I   -35.074     .791    -8.085     .298  -.187830   .274290  -.2543900   -35.600    -8.100  
+97 318 50525.00 I  -.188374  .000175   .277782  .000095  I -.2567473  .0000064  2.3724 0.0048  I   -35.173     .791    -8.150     .298  -.188480   .277770  -.2567630   -35.400    -8.100  
+97 319 50526.00 I  -.188751  .000173   .281365  .000092  I -.2591447  .0000066  2.4278 0.0046  I   -35.067     .791    -8.134     .298  -.188700   .281390  -.2591520   -35.000    -8.000  
+97 320 50527.00 I  -.188948  .000172   .284964  .000075  I -.2615966  .0000065  2.4644 0.0049  I   -34.768     .791    -8.117     .298  -.188980   .285030  -.2615730   -34.600    -7.800  
+97 321 50528.00 I  -.189277  .000165   .288636  .000056  I -.2640664  .0000073  2.4797 0.0047  I   -34.492     .791    -8.026     .298  -.189010   .288660  -.2640260   -34.200    -7.800  
+97 322 50529.00 I  -.189584  .000178   .292340  .000082  I -.2665641  .0000068  2.5191 0.0051  I   -34.366     .791    -7.875     .298  -.189180   .292380  -.2665350   -34.000    -7.700  
+97 323 50530.00 I  -.189715  .000191   .296049  .000073  I -.2691041  .0000070  2.5568 0.0051  I   -34.331     .101    -7.790     .298  -.189770   .296020  -.2691000   -33.800    -7.700  
+97 324 50531.00 I  -.189650  .000163   .299799  .000107  I -.2716708  .0000076  2.5736 0.0050  I   -34.332     .103    -7.831     .298  -.189710   .299790  -.2716640   -33.800    -7.700  
+97 325 50532.00 I  -.189501  .000164   .303650  .000113  I -.2742439  .0000071  2.5680 0.0053  I   -34.402     .103    -7.949     .298  -.189510   .303670  -.2742150   -34.000    -7.800  
+97 326 50533.00 I  -.189576  .000159   .307593  .000127  I -.2767977  .0000073  2.5349 0.0047  I   -34.559     .103    -8.070     .298  -.189610   .307610  -.2767820   -34.200    -8.000  
+97 327 50534.00 I  -.190011  .000151   .311542  .000132  I -.2793072  .0000061  2.4822 0.0048  I   -34.749     .103    -8.162     .298  -.190060   .311790  -.2793000   -34.400    -8.200  
+97 328 50535.00 I  -.190465  .000149   .315430  .000127  I -.2817635  .0000062  2.4336 0.0041  I   -34.921     .124    -8.255     .298  -.190390   .315590  -.2817270   -34.500    -8.300  
+97 329 50536.00 I  -.190817  .000110   .319244  .000137  I -.2841799  .0000055  2.4013 0.0044  I   -35.031     .124    -8.350     .298  -.190700   .319190  -.2841550   -34.700    -8.500  
+97 330 50537.00 I  -.191111  .000083   .322923  .000142  I -.2865724  .0000063  2.3877 0.0038  I   -34.999     .791    -8.398     .298  -.191090   .323120  -.2865830   -34.700    -8.600  
+97 331 50538.00 I  -.191277  .000104   .326401  .000163  I -.2889662  .0000053  2.4069 0.0041  I   -34.783     .791    -8.379     .298  -.191330   .326200  -.2889710   -34.600    -8.600  
+97 4 1 50539.00 I  -.191095  .000120   .329761  .000156  I -.2913990  .0000053  2.4646 0.0037  I   -34.468     .791    -8.361     .298  -.191030   .329370  -.2913530   -34.400    -8.500  
+97 4 2 50540.00 I  -.190396  .000123   .333126  .000160  I -.2939066  .0000052  2.5558 0.0038  I   -34.207     .791    -8.419     .298  -.190470   .333060  -.2938890   -34.200    -8.400  
+97 4 3 50541.00 I  -.189369  .000135   .336798  .000177  I -.2965213  .0000054  2.6789 0.0035  I   -34.058     .791    -8.522     .298  -.189510   .336850  -.2965170   -34.100    -8.300  
+97 4 4 50542.00 I  -.188326  .000135   .340644  .000164  I -.2992707  .0000046  2.8209 0.0037  I   -33.981     .791    -8.529     .298  -.188320   .340750  -.2992520   -34.000    -8.100  
+97 4 5 50543.00 I  -.187324  .000171   .344421  .000165  I -.3021543  .0000051  2.9366 0.0037  I   -33.916     .791    -8.416     .298  -.186950   .344770  -.3021410   -34.000    -8.100  
+97 4 6 50544.00 I  -.186426  .000170   .348044  .000128  I -.3051186  .0000058  2.9763 0.0034  I   -33.866     .791    -8.336     .298  -.186080   .348490  -.3050560   -34.200    -8.200  
+97 4 7 50545.00 I  -.185573  .000167   .351535  .000123  I -.3080759  .0000045  2.9231 0.0037  I   -33.907     .120    -8.419     .110  -.185710   .351620  -.3080380   -34.300    -8.200  
+97 4 8 50546.00 I  -.184596  .000184   .354973  .000117  I -.3109377  .0000046  2.7887 0.0034  I   -34.134     .120    -8.601     .110  -.184670   .355100  -.3109070   -34.600    -8.400  
+97 4 9 50547.00 I  -.183491  .000179   .358473  .000107  I -.3136437  .0000051  2.6256 0.0036  I   -34.522     .120    -8.700     .110  -.183910   .358510  -.3136330   -34.800    -8.500  
+97 410 50548.00 I  -.182447  .000185   .362076  .000103  I -.3161928  .0000056  2.4741 0.0038  I   -34.817     .120    -8.675     .110  -.182710   .362170  -.3162020   -35.000    -8.700  
+97 411 50549.00 I  -.181423  .000179   .365823  .000104  I -.3185995  .0000056  2.3455 0.0042  I   -34.799     .120    -8.670     .110  -.181650   .365730  -.3186020   -35.200    -8.900  
+97 412 50550.00 I  -.180458  .000153   .369697  .000100  I -.3208959  .0000062  2.2531 0.0080  I   -34.553     .109    -8.825     .135  -.180750   .369460  -.3209070   -35.200    -9.100  
+97 413 50551.00 I  -.179741  .000104   .373586  .000106  I -.3231184  .0000150  2.1989 0.0054  I   -34.400     .791    -9.111     .298  -.179540   .373610  -.3231080   -35.200    -9.200  
+97 414 50552.00 I  -.179301  .000071   .377335  .000092  I -.3253059  .0000088  2.1815 0.0083  I   -34.496     .130    -9.379     .298  -.179130   .377460  -.3253080   -35.100    -9.300  
+97 415 50553.00 I  -.178935  .000074   .380843  .000086  I -.3274896  .0000069  2.1887 0.0055  I   -34.605     .139    -9.516     .298  -.179170   .381090  -.3275180   -34.900    -9.300  
+97 416 50554.00 I  -.178449  .000052   .384082  .000085  I -.3296974  .0000065  2.2383 0.0048  I   -34.418     .139    -9.494     .298  -.178510   .384090  -.3297100   -34.700    -9.300  
+97 417 50555.00 I  -.177770  .000058   .387200  .000086  I -.3319711  .0000067  2.3027 0.0045  I   -33.981     .139    -9.320     .298  -.177840   .387160  -.3319570   -34.500    -9.100  
+97 418 50556.00 I  -.176894  .000064   .390339  .000084  I -.3342906  .0000063  2.3307 0.0043  I   -33.660     .139    -9.081     .298  -.177080   .390390  -.3342830   -34.400    -9.000  
+97 419 50557.00 I  -.175898  .000106   .393568  .000075  I -.3366294  .0000055  2.3495 0.0042  I   -33.642     .139    -8.863     .298  -.175800   .393490  -.3366160   -34.100    -8.900  
+97 420 50558.00 I  -.174918  .000117   .396920  .000067  I -.3389891  .0000056  2.3670 0.0043  I   -33.799     .118    -8.727     .298  -.174820   .396880  -.3389690   -34.000    -8.800  
+97 421 50559.00 I  -.174074  .000129   .400408  .000141  I -.3413517  .0000066  2.3495 0.0044  I   -33.964     .791    -8.693     .298  -.173790   .400430  -.3413440   -34.000    -8.700  
+97 422 50560.00 I  -.173393  .000135   .404014  .000143  I -.3436733  .0000069  2.2876 0.0044  I   -34.105     .791    -8.741     .298  -.173130   .404030  -.3436830   -33.800    -8.700  
+97 423 50561.00 I  -.172759  .000132   .407606  .000146  I -.3459204  .0000059  2.2062 0.0046  I   -34.233     .791    -8.856     .298  -.172750   .407590  -.3459270   -33.800    -8.700  
+97 424 50562.00 I  -.171839  .000139   .410966  .000142  I -.3480859  .0000061  2.1254 0.0060  I   -34.323     .791    -9.017     .298  -.172230   .411360  -.3480680   -33.800    -8.900  
+97 425 50563.00 I  -.170499  .000140   .414152  .000134  I -.3501739  .0000105  2.0526 0.0064  I   -34.355     .791    -9.186     .298  -.170760   .414200  -.3501490   -33.800    -9.000  
+97 426 50564.00 I  -.168831  .000107   .417314  .000137  I -.3521945  .0000113  1.9901 0.0097  I   -34.397     .791    -9.300     .298  -.168870   .417240  -.3521830   -33.800    -9.100  
+97 427 50565.00 I  -.167106  .000096   .420468  .000094  I -.3541611  .0000163  1.9485 0.0081  I   -34.519     .791    -9.323     .298  -.167060   .420570  -.3541560   -33.800    -9.200  
+97 428 50566.00 I  -.165634  .000081   .423541  .000096  I -.3561064  .0000117  1.9516 0.0098  I   -34.669     .172    -9.280     .298  -.165470   .423620  -.3560980   -33.900    -9.300  
+97 429 50567.00 I  -.164540  .000088   .426430  .000106  I -.3580823  .0000110  2.0086 0.0081  I   -34.708     .214    -9.228     .298  -.164390   .426550  -.3580740   -34.000    -9.300  
+97 430 50568.00 I  -.163574  .000089   .429110  .000123  I -.3601397  .0000112  2.1139 0.0075  I   -34.556     .214    -9.186     .298  -.163620   .429130  -.3601370   -34.100    -9.200  
+97 5 1 50569.00 I  -.162447  .000089   .431668  .000129  I -.3623133  .0000101  2.2290 0.0077  I   -34.273     .214    -9.110     .298  -.162550   .431430  -.3623130   -34.200    -9.100  
+97 5 2 50570.00 I  -.160959  .000087   .434208  .000131  I -.3645947  .0000106  2.3352 0.0072  I   -33.968     .214    -8.955     .298  -.161010   .434320  -.3645950   -34.400    -9.000  
+97 5 3 50571.00 I  -.158952  .000103   .436863  .000138  I -.3669830  .0000102  2.4391 0.0082  I   -33.815     .204    -8.747     .298  -.159020   .437110  -.3669560   -34.600    -8.900  
+97 5 4 50572.00 I  -.156442  .000109   .439744  .000143  I -.3694605  .0000124  2.5056 0.0085  I   -33.919     .219    -8.616     .298  -.156530   .439490  -.3694140   -34.600    -8.900  
+97 5 5 50573.00 I  -.153614  .000126   .442881  .000121  I -.3719702  .0000136  2.5002 0.0095  I   -34.226     .138    -8.691     .298  -.153640   .442850  -.3719270   -34.700    -8.900  
+97 5 6 50574.00 I  -.150798  .000129   .446180  .000107  I -.3744440  .0000143  2.4443 0.0095  I   -34.595     .138    -8.937     .298  -.150610   .446380  -.3744430   -34.600    -9.000  
+97 5 7 50575.00 I  -.148127  .000122   .449423  .000102  I -.3768402  .0000132  2.3327 0.0096  I   -34.882     .138    -9.159     .298  -.148120   .449590  -.3768500   -34.600    -9.200  
+97 5 8 50576.00 I  -.145405  .000122   .452426  .000094  I -.3790965  .0000129  2.1824 0.0092  I   -34.940     .138    -9.207     .298  -.145420   .452510  -.3790580   -34.500    -9.300  
+97 5 9 50577.00 I  -.142482  .000119   .455307  .000094  I -.3812199  .0000127  2.0768 0.0093  I   -34.779     .138    -9.127     .298  -.142440   .455210  -.3811930   -34.500    -9.500  
+97 510 50578.00 I  -.139426  .000104   .458227  .000094  I -.3832642  .0000134  2.0135 0.0087  I   -34.582     .108    -9.090     .121  -.139410   .458130  -.3832900   -34.600    -9.700  
+97 511 50579.00 I  -.136461  .000055   .461179  .000059  I -.3852533  .0000118  1.9693 0.0071  I   -34.566     .791    -9.201     .298  -.136280   .461250  -.3852540   -34.600    -9.800  
+97 512 50580.00 I  -.133867  .000058   .464047  .000065  I -.3872115  .0000048  1.9511 0.0062  I   -34.773     .791    -9.406     .298  -.133860   .464070  -.3872040   -34.700    -9.800  
+97 513 50581.00 I  -.131687  .000055   .466720  .000053  I -.3891616  .0000040  1.9510 0.0032  I   -35.004     .791    -9.580     .298  -.131640   .466690  -.3891660   -34.800    -9.800  
+97 514 50582.00 I  -.129668  .000054   .469178  .000053  I -.3911154  .0000042  1.9564 0.0034  I   -35.037     .791    -9.646     .298  -.129560   .469170  -.3911180   -34.900    -9.700  
+97 515 50583.00 I  -.127578  .000064   .471457  .000066  I -.3930758  .0000056  1.9659 0.0035  I   -34.869     .791    -9.605     .298  -.127760   .471830  -.3930820   -35.000    -9.500  
+97 516 50584.00 I  -.125217  .000065   .473621  .000072  I -.3950526  .0000056  1.9916 0.0036  I   -34.723     .791    -9.498     .298  -.125190   .473650  -.3950390   -35.000    -9.300  
+97 517 50585.00 I  -.122492  .000065   .475779  .000070  I -.3970639  .0000045  2.0319 0.0040  I   -34.766     .791    -9.369     .298  -.122280   .475780  -.3970140   -35.200    -9.100  
+97 518 50586.00 I  -.119535  .000070   .478059  .000106  I -.3991144  .0000058  2.0661 0.0040  I   -34.977     .791    -9.233     .298  -.119350   .478040  -.3990670   -35.200    -8.900  
+97 519 50587.00 I  -.116559  .000102   .480544  .000135  I -.4011911  .0000066  2.0855 0.0042  I   -35.249     .791    -9.077     .147  -.116500   .480480  -.4011750   -35.400    -8.900  
+97 520 50588.00 I  -.113641  .000104   .483188  .000134  I -.4032818  .0000061  2.0943 0.0041  I   -35.502     .791    -8.914     .147  -.113540   .483140  -.4032990   -35.600    -8.800  
+97 521 50589.00 I  -.110776  .000101   .485865  .000136  I -.4053710  .0000050  2.0769 0.0040  I   -35.680     .791    -8.828     .147  -.110730   .485760  -.4053930   -35.700    -8.800  
+97 522 50590.00 I  -.108256  .000098   .488391  .000130  I -.4074257  .0000052  2.0298 0.0036  I   -35.739     .791    -8.904     .147  -.108270   .488390  -.4074370   -35.900    -9.000  
+97 523 50591.00 I  -.105963  .000097   .490720  .000133  I -.4094314  .0000052  1.9853 0.0048  I   -35.696     .791    -9.106     .147  -.105880   .490770  -.4094310   -36.000    -9.100  
+97 524 50592.00 I  -.103700  .000115   .492983  .000123  I -.4114046  .0000080  1.9658 0.0062  I   -35.675     .791    -9.283     .147  -.103560   .492970  -.4114080   -36.200    -9.100  
+97 525 50593.00 I  -.101447  .000098   .495252  .000136  I -.4133742  .0000112  1.9798 0.0065  I   -35.820     .791    -9.309     .298  -.101400   .495370  -.4133980   -36.300    -9.100  
+97 526 50594.00 I  -.099172  .000097   .497478  .000145  I -.4153798  .0000103  2.0406 0.0076  I   -36.147     .791    -9.202     .298  -.099110   .497570  -.4154060   -36.300    -9.100  
+97 527 50595.00 I  -.096785  .000094   .499650  .000144  I -.4174722  .0000103  2.1520 0.0076  I   -36.494     .791    -9.088     .298  -.096900   .499300  -.4174680   -36.400    -9.000  
+97 528 50596.00 I  -.094267  .000110   .501741  .000136  I -.4196953  .0000112  2.2981 0.0079  I   -36.635     .791    -9.060     .298  -.094180   .501500  -.4197000   -36.400    -8.800  
+97 529 50597.00 I  -.091850  .000123   .503791  .000125  I -.4220672  .0000120  2.4406 0.0078  I   -36.461     .791    -9.109     .298  -.091740   .504070  -.4220830   -36.500    -8.700  
+97 530 50598.00 I  -.089584  .000109   .505799  .000132  I -.4245581  .0000110  2.5279 0.0080  I   -36.098     .791    -9.146     .298  -.089680   .506070  -.4245640   -36.400    -8.500  
+97 531 50599.00 I  -.087350  .000082   .507760  .000100  I -.4270965  .0000106  2.5357 0.0091  I   -35.840     .791    -9.106     .298  -.087270   .507660  -.4271100   -36.500    -8.500  
+97 6 1 50600.00 I  -.085071  .000094   .509688  .000110  I -.4296072  .0000144  2.4765 0.0085  I   -35.945     .791    -9.018     .298  -.084900   .509870  -.4295920   -36.700    -8.400  
+97 6 2 50601.00 I  -.082698  .000126   .511540  .000111  I -.4320282  .0000134  2.3532 0.0097  I   -36.426     .791    -8.965     .118  -.082550   .511740  -.4320310   -36.800    -8.400  
+97 6 3 50602.00 I  -.080141  .000125   .513310  .000102  I -.4342951  .0000131  2.1742 0.0092  I   -37.021     .791    -8.971     .118  -.080290   .513170  -.4342640   -36.900    -8.400  
+97 6 4 50603.00 I  -.077450  .000117   .515008  .000106  I -.4363661  .0000127  1.9635 0.0091  I   -37.381     .791    -8.954     .118  -.077560   .514830  -.4363590   -37.000    -8.600  
+97 6 5 50604.00 I  -.074955  .000129   .516512  .000110  I -.4382224  .0000125  1.7536 0.0089  I   -37.331     .791    -8.885     .118  -.074960   .516480  -.4382470   -36.900    -8.600  
+97 6 6 50605.00 I  -.072662  .000138   .517746  .000111  I -.4398921  .0000125  1.6002 0.0080  I   -37.001     .121    -8.751     .135  -.072650   .517730  -.4398790   -36.800    -8.700  
+97 6 7 50606.00 I  -.070423  .000137   .518722  .000113  I -.4414443  .0000101  1.5113 0.0070  I   -36.680     .121    -8.626     .135  -.070340   .518680  -.4414520   -36.600    -8.800  
+97 6 8 50607.00 I  -.068053  .000133   .519422  .000101  I -.4429291  .0000062  1.4654 0.0056  I   -36.563     .791    -8.615     .298  -.068210   .519640  -.4429350   -36.600    -8.900  
+97 6 9 50608.00 I  -.065268  .000206   .519887  .000104  I -.4443891  .0000047  1.4613 0.0041  I   -36.649     .791    -8.724     .298  -.065500   .520040  -.4443870   -36.500    -8.900  
+97 610 50609.00 I  -.061898  .000192   .520349  .000101  I -.4458636  .0000053  1.4930 0.0043  I   -36.832     .791    -8.869     .298  -.061880   .520140  -.4458620   -36.600    -8.900  
+97 611 50610.00 I  -.058127  .000198   .521097  .000100  I -.4473822  .0000071  1.5460 0.0045  I   -37.022     .791    -8.965     .298  -.058090   .520920  -.4473900   -36.800    -8.800  
+97 612 50611.00 I  -.054367  .000194   .522302  .000102  I -.4489535  .0000073  1.5925 0.0066  I   -37.171     .791    -8.983     .298  -.054330   .522280  -.4489650   -37.100    -8.800  
+97 613 50612.00 I  -.050642  .000188   .523897  .000099  I -.4505582  .0000111  1.6118 0.0083  I   -37.269     .791    -8.943     .298  -.050440   .523870  -.4505490   -37.600    -8.600  
+97 614 50613.00 I  -.046970  .000196   .525560  .000130  I -.4521676  .0000149  1.6024 0.0087  I   -37.401     .791    -8.877     .298  -.046980   .525830  -.4521290   -37.900    -8.500  
+97 615 50614.00 I  -.043365  .000091   .526958  .000122  I -.4537530  .0000133  1.5628 0.0097  I   -37.713     .791    -8.789     .298  -.043510   .527200  -.4537240   -38.300    -8.300  
+97 616 50615.00 I  -.039765  .000164   .528003  .000117  I -.4552828  .0000124  1.4920 0.0090  I   -38.254     .211    -8.660     .298  -.039660   .528020  -.4552880   -38.600    -8.200  
+97 617 50616.00 I  -.036136  .000141   .528829  .000118  I -.4567309  .0000122  1.4028 0.0086  I   -38.847     .211    -8.493     .298  -.036070   .528570  -.4567500   -38.800    -8.200  
+97 618 50617.00 I  -.032476  .000130   .529592  .000112  I -.4580759  .0000119  1.2760 0.0081  I   -39.225     .211    -8.376     .298  -.032510   .529440  -.4580800   -38.900    -8.200  
+97 619 50618.00 I  -.028867  .000131   .530411  .000105  I -.4592748  .0000108  1.1248 0.0082  I   -39.279     .211    -8.393     .298  -.028920   .530430  -.4592590   -38.900    -8.200  
+97 620 50619.00 I  -.025152  .000126   .531246  .000092  I -.4603396  .0000112  1.0159 0.0071  I   -39.131     .211    -8.572     .298  -.025010   .531400  -.4603310   -38.800    -8.300  
+97 621 50620.00 I  -.021281  .000121   .532003  .000070  I -.4613282  .0000092  0.9717 0.0075  I   -38.983     .258    -8.777     .298  -.021080   .532140  -.4613360   -38.700    -8.300  
+97 622 50621.00 I  -.017250  .000072   .532620  .000070  I -.4623065  .0000099  0.9978 0.0059  I   -38.971     .791    -8.793     .298  -.017230   .532690  -.4623090   -38.700    -8.200  
+97 623 50622.00 I  -.013076  .000103   .533105  .000076  I -.4633442  .0000075  1.0850 0.0062  I   -39.142     .791    -8.558     .298  -.012820   .533010  -.4633470   -38.800    -8.200  
+97 624 50623.00 I  -.008959  .000096   .533538  .000073  I -.4644849  .0000076  1.1976 0.0054  I   -39.470     .791    -8.230     .298  -.008700   .533480  -.4644800   -39.000    -8.100  
+97 625 50624.00 I  -.005037  .000089   .533969  .000079  I -.4657361  .0000077  1.3005 0.0054  I   -39.836     .791    -8.028     .298  -.004870   .533880  -.4657440   -39.400    -7.900  
+97 626 50625.00 I  -.001305  .000106   .534422  .000086  I -.4670736  .0000076  1.3666 0.0055  I   -40.063     .791    -8.040     .298  -.001160   .534400  -.4670770   -39.800    -7.800  
+97 627 50626.00 I   .002419  .000096   .534850  .000092  I -.4684479  .0000078  1.3687 0.0056  I   -40.048     .791    -8.189     .298   .002430   .534990  -.4684540   -40.300    -7.800  
+97 628 50627.00 I   .006322  .000094   .535197  .000093  I -.4697869  .0000083  1.2987 0.0057  I   -39.896     .791    -8.344     .298   .006390   .535310  -.4698040   -40.700    -7.800  
+97 629 50628.00 I   .010476  .000105   .535489  .000106  I -.4710285  .0000084  1.1784 0.0061  I   -39.879     .791    -8.442     .298   .010520   .535600  -.4710160   -41.100    -8.000  
+97 630 50629.00 I   .014809  .000151   .535883  .000141  I -.4721332  .0000089  1.0266 0.0061  I   -40.213     .791    -8.514     .193   .014850   .535860  -.4721370   -41.200    -8.100  
+97 7 1 50630.00 I   .019205  .000148   .536419  .000137  I  .5269220  .0000088  0.8635 0.0060  I   -40.843     .791    -8.615     .193   .019150   .536250   .5268940   -41.200    -8.200  
+97 7 2 50631.00 I   .023493  .000143   .536912  .000142  I  .5261374  .0000080  0.7075 0.0060  I   -41.454     .791    -8.756     .193   .023390   .536680   .5261270   -41.200    -8.500  
+97 7 3 50632.00 I   .027452  .000138   .537141  .000140  I  .5254967  .0000083  0.5826 0.0058  I   -41.739     .791    -8.881     .193   .027540   .537280   .5255080   -41.100    -8.600  
+97 7 4 50633.00 I   .031223  .000137   .537076  .000141  I  .5249515  .0000085  0.5200 0.0063  I   -41.681     .791    -8.937     .193   .031140   .536960   .5249520   -41.000    -8.700  
+97 7 5 50634.00 I   .034962  .000149   .536773  .000149  I  .5244340  .0000096  0.5256 0.0070  I   -41.507     .791    -8.914     .193   .034850   .536560   .5244230   -40.900    -8.800  
+97 7 6 50635.00 I   .038689  .000134   .536291  .000078  I  .5238814  .0000111  0.5879 0.0061  I   -41.429     .791    -8.843     .298   .038740   .536120   .5238950   -40.900    -8.400  
+97 7 7 50636.00 I   .042534  .000138   .535696  .000119  I  .5232476  .0000076  0.6822 0.0066  I   -41.502     .328    -8.758     .298   .042370   .535810   .5232340   -41.000    -8.400  
+97 7 8 50637.00 I   .046663  .000142   .535063  .000132  I  .5225157  .0000073  0.7806 0.0053  I   -41.723     .394    -8.697     .298   .046380   .534930   .5224760   -41.300    -8.400  
+97 7 9 50638.00 I   .050873  .000151   .534487  .000141  I  .5216906  .0000073  0.8665 0.0056  I   -42.093     .394    -8.706     .298   .050830   .534450   .5216790   -41.800    -8.500  
+97 710 50639.00 I   .054750  .000149   .534055  .000145  I  .5207857  .0000084  0.9432 0.0054  I   -42.500     .394    -8.795     .298   .054960   .534310   .5207760   -42.300    -8.700  
+97 711 50640.00 I   .058275  .000144   .533596  .000152  I  .5198074  .0000080  1.0101 0.0065  I   -42.623     .394    -8.870     .298   .058500   .533840   .5198110   -42.900    -8.900  
+97 712 50641.00 I   .061703  .000152   .532970  .000144  I  .5187691  .0000099  1.0661 0.0067  I   -42.452     .387    -8.876     .298   .061880   .533060   .5187780   -43.400    -9.000  
+97 713 50642.00 I   .065250  .000140   .532212  .000114  I  .5176801  .0000107  1.1072 0.0067  I   -42.384     .414    -8.829     .298   .065180   .532240   .5176940   -43.700    -9.100  
+97 714 50643.00 I   .068880  .000155   .531426  .000113  I  .5165642  .0000091  1.1197 0.0075  I   -42.759     .288    -8.755     .224   .068750   .531420   .5166260   -44.000    -9.100  
+97 715 50644.00 I   .072463  .000151   .530698  .000105  I  .5154546  .0000104  1.0906 0.0066  I   -43.473     .288    -8.658     .224   .072390   .530670   .5154970   -44.100    -9.000  
+97 716 50645.00 I   .075783  .000151   .530020  .000105  I  .5143902  .0000096  1.0401 0.0071  I   -44.093     .288    -8.558     .224   .075690   .529910   .5143660   -44.000    -8.900  
+97 717 50646.00 I   .078739  .000165   .529321  .000102  I  .5133592  .0000097  1.0352 0.0062  I   -44.273     .288    -8.484     .224   .078780   .529110   .5133850   -43.900    -8.800  
+97 718 50647.00 I   .081527  .000162   .528578  .000106  I  .5122953  .0000079  1.1038 0.0063  I   -44.177     .234    -8.552     .269   .081560   .528620   .5123300   -43.700    -8.700  
+97 719 50648.00 I   .084482  .000141   .527746  .000106  I  .5111334  .0000081  1.2269 0.0093  I   -44.120     .234    -8.742     .269   .084430   .527980   .5111090   -43.600    -8.600  
+97 720 50649.00 I   .087863  .000117   .526792  .000101  I  .5098259  .0000169  1.3969 0.0068  I   -44.182     .791    -8.836     .298   .087470   .526420   .5098570   -43.500    -8.400  
+97 721 50650.00 I   .091630  .000118   .525731  .000126  I  .5083296  .0000110  1.5969 0.0096  I   -44.278     .433    -8.661     .298   .091370   .525420   .5083920   -43.600    -8.100  
+97 722 50651.00 I   .095462  .000129   .524636  .000152  I  .5066364  .0000093  1.7839 0.0072  I   -44.372     .459    -8.299     .298   .095540   .524670   .5066460   -43.800    -8.000  
+97 723 50652.00 I   .099070  .000127   .523593  .000153  I  .5047789  .0000093  1.9195 0.0071  I   -44.489     .459    -7.986     .298   .099300   .523600   .5047620   -44.100    -7.900  
+97 724 50653.00 I   .102369  .000116   .522632  .000144  I  .5028128  .0000108  2.0090 0.0070  I   -44.602     .459    -7.865     .298   .102360   .522590   .5028580   -44.700    -7.900  
+97 725 50654.00 I   .105406  .000111   .521769  .000147  I  .5007884  .0000105  2.0160 0.0075  I   -44.750     .459    -7.940     .298   .105250   .521650   .5008370   -45.100    -8.000  
+97 726 50655.00 I   .108028  .000112   .520860  .000139  I  .4988149  .0000105  1.9208 0.0072  I   -44.868     .459    -8.105     .298   .108100   .520900   .4987890   -45.600    -8.100  
+97 727 50656.00 I   .110314  .000115   .519815  .000128  I  .4969636  .0000099  1.7750 0.0061  I   -44.921     .484    -8.291     .298   .110350   .519920   .4969520   -45.900    -8.400  
+97 728 50657.00 I   .112515  .000114   .518611  .000089  I  .4952704  .0000062  1.6125 0.0062  I   -45.040     .131    -8.493     .170   .112490   .518700   .4952740   -46.000    -8.600  
+97 729 50658.00 I   .114866  .000119   .517276  .000095  I  .4937342  .0000074  1.4633 0.0040  I   -45.376     .131    -8.698     .170   .114770   .517170   .4937570   -45.900    -8.800  
+97 730 50659.00 I   .117460  .000113   .515918  .000093  I  .4923373  .0000050  1.3338 0.0049  I   -45.887     .131    -8.873     .170   .117530   .515740   .4923430   -45.700    -8.900  
+97 731 50660.00 I   .120245  .000108   .514597  .000092  I  .4910524  .0000064  1.2471 0.0041  I   -46.307     .131    -9.000     .170   .120280   .514540   .4910370   -45.400    -9.000  
+97 8 1 50661.00 I   .123226  .000113   .513301  .000116  I  .4898217  .0000064  1.2247 0.0046  I   -46.412     .131    -9.089     .170   .123130   .513310   .4898480   -45.000    -9.000  
+97 8 2 50662.00 I   .126325  .000131   .512004  .000129  I  .4885897  .0000065  1.2422 0.0059  I   -46.220     .131    -9.118     .170   .126390   .512030   .4885980   -44.700    -8.800  
+97 8 3 50663.00 I   .129373  .000118   .510695  .000159  I  .4873339  .0000098  1.2699 0.0050  I   -45.914     .791    -9.024     .298   .129470   .510800   .4872900   -44.600    -8.700  
+97 8 4 50664.00 I   .132300  .000125   .509356  .000172  I  .4860454  .0000077  1.3112 0.0064  I   -45.657     .109    -8.792     .298   .132500   .509470   .4860610   -44.700    -8.600  
+97 8 5 50665.00 I   .135125  .000131   .507996  .000161  I  .4847079  .0000083  1.3632 0.0053  I   -45.547     .109    -8.534     .298   .135210   .508000   .4847340   -45.000    -8.300  
+97 8 6 50666.00 I   .137790  .000141   .506654  .000155  I  .4833214  .0000074  1.4079 0.0056  I   -45.628     .109    -8.427     .298   .137730   .506730   .4833070   -45.300    -8.300  
+97 8 7 50667.00 I   .140168  .000141   .505311  .000147  I  .4818941  .0000074  1.4464 0.0051  I   -45.837     .109    -8.524     .298   .140190   .505620   .4819060   -45.800    -8.400  
+97 8 8 50668.00 I   .142406  .000119   .503863  .000127  I  .4804318  .0000071  1.4752 0.0046  I   -45.947     .109    -8.720     .298   .142330   .503990   .4804660   -46.300    -8.500  
+97 8 9 50669.00 I   .144656  .000098   .502196  .000100  I  .4789539  .0000055  1.4732 0.0063  I   -45.881     .791    -8.870     .298   .144720   .502370   .4789680   -46.600    -8.600  
+97 810 50670.00 I   .146894  .000086   .500298  .000107  I  .4774990  .0000104  1.4303 0.0045  I   -45.936     .791    -8.931     .298   .146900   .500350   .4775170   -46.900    -8.700  
+97 811 50671.00 I   .149084  .000121   .498268  .000164  I  .4761027  .0000071  1.3593 0.0063  I   -46.450     .250    -8.946     .298   .149040   .498160   .4761580   -47.100    -8.600  
+97 812 50672.00 I   .151262  .000122   .496249  .000171  I  .4747824  .0000070  1.2823 0.0051  I   -47.283     .250    -8.912     .298   .151260   .496060   .4748590   -47.200    -8.600  
+97 813 50673.00 I   .153451  .000127   .494316  .000178  I  .4735354  .0000072  1.2132 0.0062  I   -47.883     .250    -8.784     .298   .153290   .494020   .4735610   -47.200    -8.500  
+97 814 50674.00 I   .155554  .000128   .492263  .000186  I  .4723490  .0000102  1.1649 0.0063  I   -47.935     .250    -8.623     .298   .155510   .492230   .4723190   -47.100    -8.400  
+97 815 50675.00 I   .157518  .000128   .490077  .000189  I  .4711881  .0000103  1.1691 0.0071  I   -47.688     .305    -8.592     .298   .157600   .489950   .4711990   -47.200    -8.300  
+97 816 50676.00 I   .159400  .000136   .487885  .000192  I  .4699843  .0000099  1.2526 0.0105  I   -47.577     .305    -8.760     .298   .159390   .487510   .4700320   -47.200    -8.300  
+97 817 50677.00 I   .161219  .000099   .485756  .000141  I  .4686609  .0000184  1.4020 0.0082  I   -47.732     .791    -8.968     .298   .161220   .485780   .4686570   -47.200    -8.300  
+97 818 50678.00 I   .163022  .000114   .483664  .000200  I  .4671734  .0000131  1.5723 0.0106  I   -47.978     .181    -8.989     .298   .162940   .483860   .4671290   -47.400    -8.300  
+97 819 50679.00 I   .164917  .000123   .481520  .000219  I  .4655203  .0000107  1.7308 0.0076  I   -48.167     .185    -8.786     .298   .165070   .481530   .4655210   -47.700    -8.400  
+97 820 50680.00 I   .166942  .000116   .479299  .000206  I  .4637208  .0000078  1.8625 0.0068  I   -48.355     .185    -8.502     .298   .167120   .479130   .4637370   -48.000    -8.500  
+97 821 50681.00 I   .169141  .000117   .476973  .000199  I  .4618231  .0000085  1.9110 0.0057  I   -48.686     .185    -8.226     .298   .169090   .476780   .4617800   -48.300    -8.500  
+97 822 50682.00 I   .171583  .000112   .474577  .000210  I  .4599375  .0000082  1.8436 0.0059  I   -49.146     .185    -8.005     .298   .171650   .474590   .4599200   -48.600    -8.500  
+97 823 50683.00 I   .174167  .000121   .472261  .000220  I  .4581597  .0000082  1.7049 0.0069  I   -49.556     .185    -7.831     .298   .174220   .472300   .4581840   -48.700    -8.500  
+97 824 50684.00 I   .176778  .000119   .470154  .000232  I  .4565369  .0000112  1.5388 0.0089  I   -49.654     .144    -7.781     .298   .176740   .470020   .4564810   -48.600    -8.400  
+97 825 50685.00 I   .179283  .000126   .468296  .000231  I  .4550755  .0000157  1.3926 0.0099  I   -49.313     .113    -7.978     .298   .179480   .468220   .4549860   -48.400    -8.500  
+97 826 50686.00 I   .181563  .000124   .466641  .000233  I  .4537300  .0000164  1.3114 0.0111  I   -48.751     .113    -8.393     .298   .181730   .466700   .4537580   -47.900    -8.500  
+97 827 50687.00 I   .183704  .000117   .465033  .000241  I  .4524304  .0000158  1.2969 0.0111  I   -48.336     .113    -8.850     .298   .183670   .465180   .4524560   -47.400    -8.600  
+97 828 50688.00 I   .185879  .000134   .463308  .000248  I  .4511285  .0000149  1.3054 0.0110  I   -48.213     .113    -9.148     .298   .186040   .463220   .4510470   -46.700    -8.600  
+97 829 50689.00 I   .188047  .000144   .461515  .000245  I  .4498180  .0000152  1.3186 0.0101  I   -48.164     .113    -9.285     .298   .188200   .461540   .4497610   -46.200    -8.700  
+97 830 50690.00 I   .190182  .000149   .459681  .000224  I  .4484854  .0000136  1.3496 0.0096  I   -47.964     .127    -9.314     .298   .190040   .459870   .4484800   -45.700    -8.700  
+97 831 50691.00 I   .192397  .000160   .457851  .000154  I  .4471108  .0000117  1.4047 0.0078  I   -47.662     .791    -9.193     .298   .192810   .457580   .4470950   -45.500    -8.500  
+97 9 1 50692.00 I   .194835  .000174   .456075  .000171  I  .4456687  .0000076  1.4816 0.0070  I   -47.462     .254    -8.877     .298   .195190   .455900   .4456580   -45.500    -8.500  
+97 9 2 50693.00 I   .197512  .000171   .454304  .000189  I  .4441490  .0000077  1.5544 0.0054  I   -47.492     .270    -8.473     .298   .197410   .454390   .4441620   -45.800    -8.400  
+97 9 3 50694.00 I   .200273  .000164   .452464  .000209  I  .4425676  .0000078  1.6042 0.0059  I   -47.751     .270    -8.201     .298   .199960   .452670   .4425770   -46.500    -8.300  
+97 9 4 50695.00 I   .202819  .000142   .450531  .000186  I  .4409528  .0000090  1.6175 0.0058  I   -48.120     .270    -8.192     .298   .202790   .450800   .4409470   -47.100    -8.300  
+97 9 5 50696.00 I   .204834  .000143   .448415  .000196  I  .4393440  .0000086  1.5964 0.0058  I   -48.315     .270    -8.349     .298   .204910   .448760   .4393220   -47.900    -8.200  
+97 9 6 50697.00 I   .206425  .000166   .446053  .000203  I  .4377641  .0000074  1.5628 0.0057  I   -48.217     .270    -8.481     .298   .206370   .446300   .4377530   -48.500    -8.300  
+97 9 7 50698.00 I   .207950  .000174   .443508  .000201  I  .4362179  .0000074  1.5308 0.0048  I   -48.043     .285    -8.514     .298   .207950   .443560   .4362150   -49.000    -8.200  
+97 9 8 50699.00 I   .209613  .000162   .440941  .000193  I  .4347090  .0000060  1.4795 0.0048  I   -48.124     .483    -8.512     .298   .209570   .440850   .4347020   -49.200    -8.200  
+97 9 9 50700.00 I   .211333  .000170   .438467  .000190  I  .4332717  .0000060  1.3899 0.0041  I   -48.493     .483    -8.521     .298   .211080   .438240   .4332700   -49.200    -8.400  
+97 910 50701.00 I   .212799  .000167   .435949  .000193  I  .4319120  .0000057  1.3521 0.0045  I   -48.809     .483    -8.492     .298   .212530   .435910   .4319150   -48.900    -8.400  
+97 911 50702.00 I   .214129  .000165   .433249  .000189  I  .4305354  .0000068  1.4108 0.0044  I   -48.779     .483    -8.397     .298   .213980   .433250   .4305350   -48.500    -8.400  
+97 912 50703.00 I   .215443  .000153   .430595  .000177  I  .4290757  .0000068  1.5137 0.0053  I   -48.508     .483    -8.319     .298   .215450   .430440   .4290670   -48.000    -8.400  
+97 913 50704.00 I   .216636  .000129   .428209  .000196  I  .4274979  .0000082  1.6470 0.0090  I   -48.333     .396    -8.347     .298   .216880   .428020   .4275020   -47.600    -8.300  
+97 914 50705.00 I   .217447  .000131   .426056  .000196  I  .4257653  .0000167  1.8295 0.0084  I   -48.407     .791    -8.418     .298   .217650   .426110   .4257870   -47.300    -8.300  
+97 915 50706.00 I   .217825  .000126   .423914  .000238  I  .4238243  .0000146  2.0557 0.0106  I   -48.587     .791    -8.382     .298   .218030   .424210   .4238360   -47.200    -8.100  
+97 916 50707.00 I   .217939  .000126   .421578  .000230  I  .4216578  .0000130  2.2703 0.0089  I   -48.653     .791    -8.215     .298   .218030   .421720   .4216490   -47.300    -8.000  
+97 917 50708.00 I   .218042  .000125   .419053  .000230  I  .4193071  .0000103  2.4149 0.0090  I   -48.544     .791    -8.038     .298   .217980   .419020   .4192990   -47.600    -8.000  
+97 918 50709.00 I   .218405  .000133   .416554  .000223  I  .4168661  .0000124  2.4454 0.0081  I   -48.435     .791    -7.928     .298   .218320   .416550   .4168570   -48.000    -7.900  
+97 919 50710.00 I   .219060  .000129   .414120  .000198  I  .4144571  .0000126  2.3536 0.0086  I   -48.570     .791    -7.763     .298   .219070   .414120   .4144540   -48.300    -7.900  
+97 920 50711.00 I   .219824  .000096   .411705  .000193  I  .4121859  .0000119  2.1809 0.0080  I   -48.985     .791    -7.455     .298   .219840   .411700   .4122050   -48.500    -7.900  
+97 921 50712.00 I   .220552  .000104   .409271  .000134  I  .4100988  .0000100  1.9976 0.0070  I   -49.361     .791    -7.175     .298   .220580   .409270   .4101310   -48.600    -7.900  
+97 922 50713.00 I   .221159  .000144   .406759  .000213  I  .4081784  .0000074  1.8512 0.0066  I   -49.293     .235    -7.201     .298   .221230   .406690   .4081940   -48.500    -8.100  
+97 923 50714.00 I   .221544  .000146   .404116  .000221  I  .4063782  .0000087  1.7595 0.0053  I   -48.763     .235    -7.580     .298   .221670   .403970   .4063660   -48.200    -8.100  
+97 924 50715.00 I   .221639  .000140   .401267  .000227  I  .4046411  .0000076  1.7227 0.0058  I   -48.192     .235    -8.065     .298   .221680   .400980   .4046340   -47.700    -8.100  
+97 925 50716.00 I   .221427  .000141   .398075  .000233  I  .4029151  .0000078  1.7389 0.0054  I   -47.921     .235    -8.404     .298   .221290   .397770   .4029280   -47.200    -8.200  
+97 926 50717.00 I   .221148  .000144   .394691  .000238  I  .4011444  .0000076  1.8119 0.0065  I   -47.835     .235    -8.550     .298   .221180   .394520   .4011560   -46.700    -8.100  
+97 927 50718.00 I   .220967  .000146   .391340  .000234  I  .3992871  .0000104  1.8975 0.0108  I   -47.628     .235    -8.589     .298   .220850   .391110   .3992790   -46.300    -8.000  
+97 928 50719.00 I   .220896  .000092   .388141  .000257  I  .3973575  .0000203  1.9588 0.0101  I   -47.248     .791    -8.553     .298   .220760   .387900   .3973340   -46.100    -7.900  
+97 929 50720.00 I   .220936  .000073   .385125  .000253  I  .3953779  .0000174  1.9950 0.0139  I   -46.903     .791    -8.401     .298   .220990   .385200   .3953510   -46.100    -7.800  
+97 930 50721.00 I   .221029  .000117   .382236  .000255  I  .3933771  .0000189  2.0023 0.0127  I   -46.737     .791    -8.154     .298   .221110   .382080   .3933600   -46.300    -7.700  
+9710 1 50722.00 I   .221183  .000102   .379339  .000249  I  .3913652  .0000184  2.0337 0.0124  I   -46.719     .791    -7.924     .298   .221130   .379260   .3913550   -46.500    -7.700  
+9710 2 50723.00 I   .221438  .000101   .376317  .000250  I  .3893063  .0000162  2.0751 0.0110  I   -46.774     .791    -7.794     .298   .221440   .376660   .3893070   -47.000    -7.600  
+9710 3 50724.00 I   .221678  .000109   .373215  .000250  I  .3872347  .0000122  2.0574 0.0096  I   -46.842     .791    -7.740     .298   .221760   .373380   .3872330   -47.400    -7.600  
+9710 4 50725.00 I   .221852  .000108   .370134  .000229  I  .3852065  .0000103  1.9948 0.0088  I   -46.861     .791    -7.668     .298   .221910   .370090   .3852180   -47.600    -7.600  
+9710 5 50726.00 I   .222066  .000119   .367211  .000128  I  .3832492  .0000126  1.9198 0.0072  I   -46.822     .154    -7.544     .298   .222050   .367100   .3832650   -47.700    -7.700  
+9710 6 50727.00 I   .222328  .000137   .364524  .000163  I  .3813624  .0000100  1.8583 0.0080  I   -46.825     .262    -7.430     .298   .222270   .364270   .3813700   -47.600    -7.600  
+9710 7 50728.00 I   .222531  .000105   .362045  .000157  I  .3795218  .0000097  1.8292 0.0073  I   -46.942     .262    -7.398     .298   .222490   .361950   .3795040   -47.400    -7.600  
+9710 8 50729.00 I   .222583  .000122   .359663  .000155  I  .3776878  .0000105  1.8486 0.0078  I   -47.072     .262    -7.443     .298   .222450   .359630   .3776800   -47.000    -7.500  
+9710 9 50730.00 I   .222463  .000134   .357334  .000156  I  .3758081  .0000121  1.9175 0.0082  I   -47.075     .262    -7.459     .298   .222590   .357400   .3758620   -46.700    -7.400  
+971010 50731.00 I   .222364  .000146   .354973  .000158  I  .3738415  .0000127  2.0202 0.0082  I   -46.915     .262    -7.420     .298   .222250   .355160   .3739320   -46.300    -7.200  
+971011 50732.00 I   .222546  .000146   .352483  .000161  I  .3717573  .0000112  2.1539 0.0098  I   -46.705     .302    -7.356     .298   .222270   .352650   .3718190   -46.100    -6.900  
+971012 50733.00 I   .223066  .000141   .349874  .000130  I  .3695248  .0000150  2.3141 0.0087  I   -46.585     .791    -7.242     .298   .223130   .349980   .3695390   -45.900    -6.800  
+971013 50734.00 I   .223742  .000163   .347247  .000161  I  .3671319  .0000133  2.4658 0.0099  I   -46.587     .791    -7.041     .298   .224030   .347190   .3671270   -46.100    -6.700  
+971014 50735.00 I   .224350  .000149   .344654  .000155  I  .3646101  .0000129  2.5668 0.0084  I   -46.630     .791    -6.818     .298   .224420   .344320   .3646070   -46.400    -6.600  
+971015 50736.00 I   .224829  .000146   .342047  .000165  I  .3620202  .0000102  2.6020 0.0084  I   -46.608     .791    -6.732     .298   .224770   .341830   .3620180   -46.700    -6.700  
+971016 50737.00 I   .225233  .000116   .339341  .000149  I  .3594312  .0000107  2.5619 0.0071  I   -46.513     .791    -6.824     .298   .225390   .339510   .3594170   -47.200    -6.700  
+971017 50738.00 I   .225513  .000127   .336514  .000151  I  .3569238  .0000099  2.4395 0.0065  I   -46.494     .791    -6.907     .298   .225690   .336490   .3568930   -47.400    -6.900  
+971018 50739.00 I   .225723  .000153   .333587  .000161  I  .3545707  .0000074  2.2617 0.0058  I   -46.687     .791    -6.789     .298   .225510   .333780   .3545530   -47.600    -7.000  
+971019 50740.00 I   .226071  .000166   .330617  .000142  I  .3524006  .0000061  2.0834 0.0056  I   -46.951     .139    -6.552     .298   .225840   .330710   .3524200   -47.500    -7.000  
+971020 50741.00 I   .226734  .000169   .327713  .000135  I  .3503925  .0000084  1.9394 0.0052  I   -46.924     .245    -6.497     .298   .226660   .327500   .3504260   -47.200    -7.200  
+971021 50742.00 I   .227583  .000172   .324971  .000135  I  .3485072  .0000083  1.8391 0.0057  I   -46.449     .245    -6.781     .298   .227620   .324710   .3485250   -46.700    -7.200  
+971022 50743.00 I   .228135  .000168   .322377  .000130  I  .3467023  .0000078  1.7747 0.0058  I   -45.796     .245    -7.228     .298   .228130   .322290   .3467090   -46.000    -7.100  
+971023 50744.00 I   .228073  .000160   .319805  .000131  I  .3449452  .0000081  1.7482 0.0058  I   -45.361     .245    -7.559     .298   .227930   .319980   .3449410   -45.300    -7.000  
+971024 50745.00 I   .227639  .000155   .317177  .000124  I  .3431894  .0000085  1.7711 0.0061  I   -45.163     .245    -7.637     .298   .227590   .317140   .3431750   -44.600    -6.900  
+971025 50746.00 I   .227015  .000136   .314451  .000099  I  .3413928  .0000091  1.8246 0.0066  I   -44.955     .283    -7.529     .298   .227000   .314360   .3413770   -44.100    -6.800  
+971026 50747.00 I   .226260  .000090   .311590  .000078  I  .3395366  .0000101  1.8888 0.0054  I   -44.661     .791    -7.347     .298   .226170   .311650   .3395010   -43.800    -6.600  
+971027 50748.00 I   .225349  .000099   .308591  .000118  I  .3376163  .0000058  1.9498 0.0061  I   -44.450     .112    -7.124     .298   .225500   .308760   .3375650   -43.900    -6.500  
+971028 50749.00 I   .224117  .000108   .305464  .000188  I  .3356391  .0000067  2.0046 0.0042  I   -44.442     .791    -6.847     .298   .224230   .305610   .3356010   -44.000    -6.400  
+971029 50750.00 I   .222553  .000123   .302189  .000185  I  .3336090  .0000061  2.0538 0.0060  I   -44.579     .124    -6.528     .298   .222490   .302310   .3336030   -44.400    -6.400  
+971030 50751.00 I   .220887  .000128   .298780  .000192  I  .3315353  .0000100  2.0916 0.0059  I   -44.793     .124    -6.227     .298   .221120   .298860   .3315420   -44.900    -6.400  
+971031 50752.00 I   .219360  .000133   .295439  .000201  I  .3294279  .0000100  2.1230 0.0073  I   -45.044     .124    -6.085     .298   .219130   .295250   .3294120   -45.300    -6.500  
+9711 1 50753.00 I   .218197  .000144   .292331  .000193  I  .3272972  .0000106  2.1305 0.0073  I   -45.234     .142    -6.067     .102   .217830   .292330   .3272540   -45.500    -6.500  
+9711 2 50754.00 I   .217440  .000167   .289537  .000207  I  .3251797  .0000106  2.0995 0.0076  I   -45.248     .134    -6.117     .116   .217660   .290260   .3251270   -45.700    -6.700  
+9711 3 50755.00 I   .216911  .000178   .287060  .000151  I  .3231026  .0000109  2.0551 0.0076  I   -45.061     .268    -6.203     .298   .217200   .287420   .3230750   -45.500    -6.700  
+9711 4 50756.00 I   .216413  .000166   .284799  .000148  I  .3210639  .0000109  2.0274 0.0071  I   -44.809     .300    -6.281     .298   .216380   .285030   .3210520   -45.200    -6.600  
+9711 5 50757.00 I   .215856  .000183   .282591  .000139  I  .3190230  .0000090  2.0742 0.0071  I   -44.657     .300    -6.329     .298   .215990   .282700   .3190130   -44.800    -6.500  
+9711 6 50758.00 I   .215208  .000179   .280313  .000127  I  .3168940  .0000092  2.1854 0.0061  I   -44.621     .300    -6.355     .298   .215420   .280340   .3168880   -44.400    -6.300  
+9711 7 50759.00 I   .214575  .000162   .278014  .000127  I  .3146522  .0000083  2.2964 0.0061  I   -44.564     .300    -6.363     .298   .214490   .278000   .3146430   -43.900    -6.100  
+9711 8 50760.00 I   .213973  .000157   .275719  .000131  I  .3123008  .0000081  2.4085 0.0076  I   -44.381     .300    -6.318     .298   .213790   .275840   .3122920   -43.600    -5.900  
+9711 9 50761.00 I   .213355  .000122   .273388  .000123  I  .3098278  .0000127  2.5429 0.0064  I   -44.109     .791    -6.162     .298   .213340   .273380   .3098240   -43.400    -5.700  
+971110 50762.00 I   .212621  .000117   .271003  .000143  I  .3072218  .0000098  2.6569 0.0075  I   -43.886     .104    -5.878     .298   .212890   .271220   .3072180   -43.400    -5.500  
+971111 50763.00 I   .211614  .000138   .268548  .000158  I  .3045421  .0000079  2.6864 0.0059  I   -43.815     .127    -5.559     .298   .211850   .268880   .3045330   -43.400    -5.500  
+971112 50764.00 I   .210284  .000105   .266043  .000160  I  .3018805  .0000067  2.6213 0.0057  I   -43.858     .127    -5.363     .298   .210220   .266070   .3018760   -43.600    -5.400  
+971113 50765.00 I   .208730  .000107   .263564  .000159  I  .2993329  .0000081  2.4560 0.0053  I   -43.885     .127    -5.357     .298   .208840   .263480   .2993360   -43.900    -5.400  
+971114 50766.00 I   .207088  .000125   .261177  .000153  I  .2969961  .0000082  2.2080 0.0053  I   -43.872     .127    -5.503     .298   .207230   .261170   .2969980   -44.000    -5.500  
+971115 50767.00 I   .205438  .000123   .258919  .000153  I  .2949190  .0000068  1.9536 0.0052  I   -43.864     .127    -5.592     .298   .205490   .258980   .2949020   -44.100    -5.600  
+971116 50768.00 I   .203918  .000123   .256761  .000123  I  .2930689  .0000065  1.7592 0.0043  I   -43.844     .109    -5.507     .298   .203880   .256780   .2930260   -44.000    -5.700  
+971117 50769.00 I   .202655  .000104   .254669  .000127  I  .2913779  .0000051  1.6329 0.0044  I   -43.683     .271    -5.404     .108   .202760   .254780   .2913250   -43.800    -5.700  
+971118 50770.00 I   .201619  .000108   .252564  .000122  I  .2897821  .0000060  1.5697 0.0036  I   -43.276     .271    -5.471     .108   .201850   .252560   .2897440   -43.300    -5.700  
+971119 50771.00 I   .200584  .000112   .250318  .000124  I  .2882163  .0000050  1.5730 0.0043  I   -42.698     .319    -5.700     .298   .200620   .250230   .2882030   -42.800    -5.700  
+971120 50772.00 I   .199174  .000111   .247864  .000127  I  .2866280  .0000061  1.6004 0.0039  I   -42.119     .319    -5.990     .298   .199210   .248060   .2866450   -42.300    -5.600  
+971121 50773.00 I   .197451  .000106   .245209  .000124  I  .2850227  .0000059  1.6060 0.0043  I   -41.701     .319    -6.141     .298   .197520   .245450   .2850530   -41.800    -5.400  
+971122 50774.00 I   .195727  .000110   .242394  .000118  I  .2834162  .0000062  1.6110 0.0054  I   -41.511     .325    -6.071     .298   .195750   .242480   .2834300   -41.400    -5.300  
+971123 50775.00 I   .194034  .000111   .239527  .000111  I  .2817959  .0000091  1.6303 0.0046  I   -41.492     .319    -5.886     .298   .194200   .239640   .2817920   -41.400    -5.200  
+971124 50776.00 I   .192232  .000123   .236767  .000135  I  .2801566  .0000068  1.6466 0.0057  I   -41.639     .262    -5.687     .298   .192380   .236780   .2801520   -41.500    -5.200  
+971125 50777.00 I   .190303  .000103   .234238  .000142  I  .2785077  .0000068  1.6479 0.0042  I   -41.900     .791    -5.487     .298   .190440   .234200   .2785100   -41.800    -5.200  
+971126 50778.00 I   .188296  .000131   .231988  .000160  I  .2768699  .0000049  1.6218 0.0044  I   -42.131     .791    -5.255     .298   .188400   .232120   .2768720   -42.200    -5.200  
+971127 50779.00 I   .186082  .000138   .230043  .000159  I  .2752722  .0000055  1.5715 0.0036  I   -42.271     .791    -5.001     .298   .186160   .230400   .2752670   -42.600    -5.400  
+971128 50780.00 I   .183599  .000139   .228284  .000160  I  .2737303  .0000053  1.5111 0.0040  I   -42.382     .791    -4.787     .298   .183680   .228470   .2737230   -43.000    -5.400  
+971129 50781.00 I   .180925  .000158   .226584  .000185  I  .2722504  .0000057  1.4497 0.0042  I   -42.490     .791    -4.714     .298   .181050   .226750   .2722450   -43.300    -5.400  
+971130 50782.00 I   .178200  .000180   .224851  .000167  I  .2708259  .0000066  1.4034 0.0042  I   -42.496     .791    -4.830     .298   .178370   .225150   .2708160   -43.300    -5.500  
+9712 1 50783.00 I   .175672  .000204   .223009  .000148  I  .2694351  .0000063  1.3825 0.0046  I   -42.322     .390    -5.038     .298   .175500   .223390   .2694290   -43.100    -5.400  
+9712 2 50784.00 I   .173588  .000180   .221047  .000138  I  .2680505  .0000064  1.3926 0.0040  I   -42.080     .390    -5.155     .298   .173170   .221480   .2680540   -42.700    -5.300  
+9712 3 50785.00 I   .171942  .000168   .219053  .000126  I  .2666417  .0000050  1.4271 0.0049  I   -42.006     .390    -5.068     .298   .171810   .219080   .2666530   -42.200    -5.100  
+9712 4 50786.00 I   .170344  .000166   .217157  .000119  I  .2651830  .0000073  1.5013 0.0044  I   -42.230     .390    -4.811     .298   .170440   .217090   .2651940   -41.700    -4.900  
+9712 5 50787.00 I   .168527  .000155   .215369  .000116  I  .2636186  .0000072  1.6368 0.0051  I   -42.618     .390    -4.511     .298   .168750   .215580   .2636170   -41.300    -4.700  
+9712 6 50788.00 I   .166342  .000185   .213626  .000103  I  .2618905  .0000072  1.8291 0.0059  I   -42.925     .390    -4.247     .298   .166520   .213890   .2618910   -41.000    -4.400  
+9712 7 50789.00 I   .163903  .000169   .211872  .000089  I  .2599495  .0000093  2.0543 0.0051  I   -43.016     .588    -4.024     .112   .164000   .212020   .2599740   -41.000    -4.200  
+9712 8 50790.00 I   .161422  .000179   .210090  .000105  I  .2577994  .0000073  2.2272 0.0060  I   -42.932     .560    -3.850     .109   .161480   .210280   .2578130   -41.000    -4.100  
+9712 9 50791.00 I   .159046  .000187   .208306  .000115  I  .2555327  .0000077  2.2874 0.0047  I   -42.783     .560    -3.813     .109   .159250   .208410   .2555200   -41.300    -4.000  
+971210 50792.00 I   .157016  .000194   .206578  .000113  I  .2532611  .0000058  2.2380 0.0048  I   -42.626     .560    -4.029     .109   .157260   .206590   .2532500   -41.500    -4.000  
+971211 50793.00 I   .155360  .000209   .204964  .000149  I  .2510794  .0000058  2.1204 0.0041  I   -42.600     .560    -4.398     .109   .155610   .204990   .2510800   -41.900    -4.100  
+971212 50794.00 I   .153813  .000205   .203422  .000172  I  .2490314  .0000058  1.9696 0.0040  I   -42.606     .560    -4.778     .109   .154140   .203490   .2490330   -42.100    -4.100  
+971213 50795.00 I   .151960  .000160   .201840  .000174  I  .2471484  .0000055  1.7943 0.0047  I   -42.494     .375    -4.990     .298   .152290   .202230   .2471530   -42.200    -4.300  
+971214 50796.00 I   .149526  .000143   .200084  .000214  I  .2454378  .0000074  1.6339 0.0038  I   -42.261     .791    -4.916     .298   .149870   .200740   .2454510   -42.200    -4.400  
+971215 50797.00 I   .146669  .000119   .198079  .000168  I  .2438609  .0000052  1.5325 0.0046  I   -41.983     .791    -4.655     .298   .146680   .198370   .2438760   -42.000    -4.400  
+971216 50798.00 I   .143768  .000116   .195933  .000171  I  .2423544  .0000054  1.4862 0.0040  I   -41.722     .791    -4.447     .298   .143740   .196060   .2423610   -41.800    -4.400  
+971217 50799.00 I   .141075  .000119   .193902  .000158  I  .2408754  .0000060  1.4792 0.0048  I   -41.476     .791    -4.454     .298   .141160   .194160   .2408750   -41.500    -4.400  
+971218 50800.00 I   .138876  .000106   .192194  .000152  I  .2393864  .0000079  1.5008 0.0048  I   -41.221     .791    -4.656     .298   .139040   .192400   .2393930   -41.200    -4.400  
+971219 50801.00 I   .137047  .000112   .190980  .000155  I  .2378658  .0000076  1.5462 0.0056  I   -40.967     .791    -4.871     .298   .137340   .191070   .2378620   -41.000    -4.200  
+971220 50802.00 I   .134949  .000111   .190057  .000154  I  .2362873  .0000078  1.6115 0.0053  I   -40.779     .791    -4.977     .298   .135090   .190480   .2362660   -40.900    -4.200  
+971221 50803.00 I   .132348  .000123   .189130  .000155  I  .2346470  .0000075  1.6640 0.0060  I   -40.730     .301    -4.984     .125   .132320   .189640   .2346450   -40.900    -4.100  
+971222 50804.00 I   .129498  .000142   .188011  .000183  I  .2329639  .0000091  1.7025 0.0058  I   -40.851     .296    -4.928     .119   .129690   .188190   .2329880   -41.000    -4.100  
+971223 50805.00 I   .126543  .000131   .186609  .000184  I  .2312498  .0000088  1.7176 0.0055  I   -41.124     .296    -4.778     .119   .126820   .186830   .2312730   -41.300    -4.100  
+971224 50806.00 I   .123555  .000132   .184907  .000171  I  .2295451  .0000062  1.6837 0.0055  I   -41.502     .296    -4.467     .119   .123590   .185080   .2295430   -41.600    -4.200  
+971225 50807.00 I   .120723  .000124   .183142  .000166  I  .2278943  .0000065  1.6143 0.0042  I   -41.843     .296    -4.114     .119   .120750   .183220   .2278720   -41.800    -4.400  
+971226 50808.00 I   .118041  .000125   .181462  .000170  I  .2263238  .0000058  1.5231 0.0045  I   -42.089     .296    -3.801     .119   .118120   .181490   .2263070   -42.100    -4.500  
+971227 50809.00 I   .115429  .000087   .179926  .000125  I  .2248520  .0000063  1.4203 0.0040  I   -42.204     .292    -3.644     .112   .115510   .179870   .2248460   -42.200    -4.500  
+971228 50810.00 I   .112782  .000088   .178571  .000111  I  .2234795  .0000054  1.3285 0.0042  I   -42.139     .791    -3.756     .298   .112950   .178530   .2234750   -42.200    -4.600  
+971229 50811.00 I   .110042  .000095   .177409  .000110  I  .2221795  .0000055  1.2833 0.0039  I   -41.895     .137    -4.090     .298   .110410   .177530   .2221700   -42.000    -4.500  
+971230 50812.00 I   .107282  .000105   .176420  .000120  I  .2208913  .0000055  1.3029 0.0037  I   -41.541     .137    -4.423     .298   .107600   .176500   .2208800   -41.800    -4.500  
+971231 50813.00 I   .104669  .000106   .175520  .000121  I  .2195567  .0000050  1.3734 0.0041  I   -41.195     .137    -4.529     .298   .104840   .175500   .2195510   -41.500    -4.400  
+98 1 1 50814.00 I   .102330  .000113   .174610  .000130  I  .2181268  .0000062  1.4968 0.0040  I   -40.962     .137    -4.383     .298   .102620   .174590   .2181430   -40.900    -4.300  
+98 1 2 50815.00 I   .100067  .000111   .173756  .000125  I  .2165474  .0000063  1.6672 0.0048  I   -40.873     .137    -4.154     .298   .100400   .173810   .2165920   -40.700    -4.200  
+98 1 3 50816.00 I   .097717  .000110   .173046  .000119  I  .2147880  .0000074  1.8510 0.0080  I   -40.872     .137    -4.018     .298   .097860   .173110   .2148370   -40.500    -4.200  
+98 1 4 50817.00 I   .095308  .000125   .172486  .000151  I  .2128524  .0000148  2.0140 0.0064  I   -40.876     .791    -4.010     .298   .095580   .172660   .2128680   -40.400    -4.100  
+98 1 5 50818.00 I   .092881  .000076   .172032  .000104  I  .2107892  .0000104  2.0893 0.0094  I   -40.869     .791    -4.068     .298   .093160   .172300   .2107640   -40.500    -4.100  
+98 1 6 50819.00 I   .090425  .000082   .171596  .000107  I  .2087156  .0000116  2.0391 0.0073  I   -40.929     .791    -4.136     .298   .090530   .171710   .2086890   -40.600    -4.100  
+98 1 7 50820.00 I   .087912  .000087   .171078  .000123  I  .2067344  .0000102  1.9187 0.0078  I   -41.141     .791    -4.226     .298   .088120   .171270   .2067320   -40.800    -4.300  
+98 1 8 50821.00 I   .085272  .000073   .170438  .000113  I  .2048885  .0000105  1.7672 0.0066  I   -41.482     .791    -4.375     .298   .085520   .170630   .2048730   -41.200    -4.300  
+98 1 9 50822.00 I   .082407  .000089   .169723  .000115  I  .2032072  .0000084  1.5940 0.0065  I   -41.800     .791    -4.566     .298   .082510   .169920   .2031800   -41.500    -4.300  
+98 110 50823.00 I   .079324  .000146   .169046  .000104  I  .2016899  .0000078  1.4528 0.0056  I   -41.925     .791    -4.700     .298   .079420   .168960   .2016870   -41.700    -4.400  
+98 111 50824.00 I   .076072  .000175   .168515  .000120  I  .2002746  .0000074  1.3928 0.0047  I   -41.828     .791    -4.677     .298   .076390   .168470   .2002920   -41.700    -4.300  
+98 112 50825.00 I   .072675  .000164   .168147  .000132  I  .1988820  .0000054  1.3996 0.0045  I   -41.652     .791    -4.508     .298   .072880   .168310   .1988680   -41.700    -4.300  
+98 113 50826.00 I   .069398  .000165   .167910  .000141  I  .1974576  .0000052  1.4598 0.0037  I   -41.555     .791    -4.315     .298   .069500   .167940   .1974300   -41.600    -4.200  
+98 114 50827.00 I   .066566  .000164   .167768  .000128  I  .1959508  .0000050  1.5547 0.0037  I   -41.530     .791    -4.216     .298   .066770   .167790   .1959580   -41.300    -4.100  
+98 115 50828.00 I   .063657  .000165   .167608  .000131  I  .1943544  .0000052  1.6311 0.0034  I   -41.436     .791    -4.261     .298   .063900   .167680   .1943530   -41.100    -4.000  
+98 116 50829.00 I   .060477  .000169   .167464  .000144  I  .1927001  .0000047  1.6730 0.0038  I   -41.243     .791    -4.363     .298   .060510   .167550   .1926850   -41.000    -3.900  
+98 117 50830.00 I   .057260  .000124   .167422  .000157  I  .1910127  .0000055  1.7019 0.0050  I   -41.092     .791    -4.439     .298   .057290   .167510   .1910200   -41.000    -3.900  
+98 118 50831.00 I   .054151  .000098   .167516  .000142  I  .1892952  .0000089  1.7342 0.0044  I   -41.102     .791    -4.498     .298   .054110   .167560   .1893100   -41.000    -4.100  
+98 119 50832.00 I   .051100  .000093   .167743  .000212  I  .1875474  .0000069  1.7574 0.0057  I   -41.230     .791    -4.579     .298   .051120   .167890   .1875500   -41.100    -4.300  
+98 120 50833.00 I   .047984  .000098   .167986  .000188  I  .1857901  .0000072  1.7517 0.0051  I   -41.383     .791    -4.688     .298   .048350   .168320   .1857890   -41.200    -4.500  
+98 121 50834.00 I   .044715  .000094   .168028  .000189  I  .1840492  .0000076  1.7304 0.0051  I   -41.575     .791    -4.776     .298   .044980   .168210   .1840490   -41.400    -4.700  
+98 122 50835.00 I   .041334  .000141   .167746  .000194  I  .1823291  .0000072  1.7098 0.0053  I   -41.846     .791    -4.761     .298   .041680   .167720   .1823160   -41.500    -4.900  
+98 123 50836.00 I   .037958  .000129   .167314  .000200  I  .1806273  .0000074  1.6963 0.0054  I   -42.097     .791    -4.600     .298   .038150   .167320   .1806180   -41.600    -5.000  
+98 124 50837.00 I   .034688  .000147   .166906  .000193  I  .1789316  .0000080  1.6976 0.0061  I   -42.179     .791    -4.367     .298   .034730   .167260   .1789340   -41.600    -5.100  
+98 125 50838.00 I   .031556  .000204   .166612  .000198  I  .1772322  .0000097  1.6985 0.0062  I   -42.109     .252    -4.251     .298   .031770   .167010   .1772150   -41.500    -5.100  
+98 126 50839.00 I   .028551  .000255   .166534  .000136  I  .1755271  .0000096  1.7217 0.0065  I   -42.024     .325    -4.396     .298   .028770   .166600   .1754970   -41.600    -5.100  
+98 127 50840.00 I   .025795  .000237   .166733  .000149  I  .1737632  .0000087  1.8216 0.0069  I   -41.932     .309    -4.717     .298   .025910   .166860   .1737480   -41.600    -5.000  
+98 128 50841.00 I   .023234  .000233   .167166  .000172  I  .1718530  .0000098  2.0142 0.0066  I   -41.669     .309    -4.952     .298   .023200   .167300   .1718700   -41.700    -4.900  
+98 129 50842.00 I   .020394  .000227   .167717  .000153  I  .1697221  .0000099  2.2450 0.0069  I   -41.232     .309    -4.896     .298   .020430   .167790   .1697400   -41.700    -4.600  
+98 130 50843.00 I   .017343  .000215   .168286  .000145  I  .1673762  .0000096  2.4363 0.0073  I   -40.858     .309    -4.609     .298   .017520   .168400   .1673530   -41.800    -4.500  
+98 131 50844.00 I   .014234  .000225   .168811  .000174  I  .1648804  .0000108  2.5347 0.0070  I   -40.815     .324    -4.312     .298   .014380   .168980   .1648510   -41.900    -4.300  
+98 2 1 50845.00 I   .011108  .000100   .169359  .000193  I  .1623325  .0000103  2.5558 0.0073  I   -41.100     .288    -4.186     .298   .011350   .169560   .1623420   -41.800    -4.300  
+98 2 2 50846.00 I   .007955  .000132   .170019  .000191  I  .1597821  .0000098  2.5371 0.0071  I   -41.504     .236    -4.230     .298   .007820   .170180   .1597890   -41.800    -4.200  
+98 2 3 50847.00 I   .004686  .000139   .170837  .000204  I  .1572725  .0000099  2.4757 0.0070  I   -41.792     .210    -4.374     .298   .004450   .170980   .1572640   -41.800    -4.300  
+98 2 4 50848.00 I   .001321  .000137   .171691  .000178  I  .1548339  .0000099  2.4045 0.0072  I   -41.848     .210    -4.585     .298   .001740   .171850   .1548260   -41.700    -4.500  
+98 2 5 50849.00 I  -.001925  .000138   .172410  .000177  I  .1524679  .0000105  2.3200 0.0073  I   -41.771     .210    -4.780     .298  -.001740   .172650   .1524600   -41.700    -4.700  
+98 2 6 50850.00 I  -.004907  .000126   .173114  .000172  I  .1502065  .0000106  2.1976 0.0071  I   -41.668     .126    -4.940     .298  -.005010   .173440   .1501780   -41.600    -4.800  
+98 2 7 50851.00 I  -.007577  .000105   .173869  .000152  I  .1480662  .0000096  2.0947 0.0075  I   -41.528     .126    -5.048     .298  -.007560   .174140   .1480560   -41.500    -5.000  
+98 2 8 50852.00 I  -.010035  .000090   .174566  .000151  I  .1459967  .0000107  2.0529 0.0067  I   -41.311     .177    -5.089     .298  -.009850   .174770   .1460250   -41.400    -5.100  
+98 2 9 50853.00 I  -.012460  .000097   .175079  .000142  I  .1439594  .0000093  2.0142 0.0071  I   -41.075     .791    -5.100     .298  -.012370   .175200   .1439480   -41.300    -5.200  
+98 210 50854.00 I  -.015089  .000090   .175334  .000147  I  .1419780  .0000092  1.9465 0.0063  I   -40.931     .791    -5.144     .298  -.014930   .175270   .1419500   -41.200    -5.300  
+98 211 50855.00 I  -.017954  .000089   .175401  .000148  I  .1400520  .0000085  1.9221 0.0062  I   -40.897     .791    -5.254     .298  -.017950   .175420   .1400530   -41.100    -5.200  
+98 212 50856.00 I  -.020654  .000094   .175617  .000154  I  .1381159  .0000084  1.9516 0.0053  I   -40.867     .791    -5.403     .298  -.020750   .175750   .1381030   -41.100    -5.300  
+98 213 50857.00 I  -.023038  .000090   .176036  .000150  I  .1361486  .0000062  1.9818 0.0058  I   -40.806     .791    -5.533     .298  -.022900   .176290   .1361190   -41.100    -5.300  
+98 214 50858.00 I  -.025089  .000118   .176594  .000151  I  .1341574  .0000080  1.9969 0.0053  I   -40.836     .791    -5.620     .298  -.025110   .176620   .1341540   -41.100    -5.200  
+98 215 50859.00 I  -.026885  .000111   .177371  .000136  I  .1321610  .0000086  1.9935 0.0052  I   -41.044     .791    -5.694     .298  -.026860   .177210   .1321700   -41.200    -5.300  
+98 216 50860.00 I  -.028529  .000171   .178429  .000123  I  .1301747  .0000067  1.9773 0.0081  I   -41.337     .791    -5.766     .298  -.028500   .178670   .1302110   -41.300    -5.300  
+98 217 50861.00 I  -.030138  .000190   .179743  .000109  I  .1282129  .0000137  1.9415 0.0082  I   -41.576     .791    -5.835     .298  -.030170   .179970   .1282310   -41.400    -5.600  
+98 218 50862.00 I  -.032005  .000202   .181204  .000103  I  .1263079  .0000149  1.8572 0.0104  I   -41.767     .791    -5.900     .298  -.031880   .181340   .1262970   -41.600    -5.700  
+98 219 50863.00 I  -.034594  .000199   .182632  .000110  I  .1244965  .0000157  1.7789 0.0107  I   -41.950     .791    -5.932     .298  -.034580   .182820   .1244970   -41.800    -5.900  
+98 220 50864.00 I  -.037751  .000197   .183914  .000100  I  .1227247  .0000153  1.7767 0.0109  I   -41.988     .791    -5.839     .298  -.037650   .184250   .1227710   -41.900    -6.200  
+98 221 50865.00 I  -.041186  .000192   .185022  .000103  I  .1209348  .0000151  1.7994 0.0105  I   -41.714     .791    -5.577     .298  -.040900   .185400   .1210210   -41.800    -6.400  
+98 222 50866.00 I  -.044703  .000143   .185960  .000109  I  .1191234  .0000144  1.8295 0.0087  I   -41.280     .791    -5.296     .298  -.044630   .186210   .1191930   -41.800    -6.500  
+98 223 50867.00 I  -.047998  .000131   .186807  .000109  I  .1172605  .0000085  1.9055 0.0088  I   -41.081     .229    -5.276     .298  -.048200   .186940   .1172860   -41.700    -6.400  
+98 224 50868.00 I  -.050931  .000139   .187634  .000118  I  .1152941  .0000100  2.0362 0.0066  I   -41.243     .229    -5.624     .298  -.051200   .187930   .1152930   -41.400    -6.300  
+98 225 50869.00 I  -.053569  .000134   .188438  .000112  I  .1131771  .0000101  2.1997 0.0073  I   -41.415     .229    -6.106     .298  -.053470   .188800   .1131760   -41.300    -6.200  
+98 226 50870.00 I  -.055810  .000128   .189277  .000117  I  .1108978  .0000106  2.3538 0.0074  I   -41.196     .229    -6.323     .298  -.055720   .189430   .1109010   -41.200    -6.000  
+98 227 50871.00 I  -.057486  .000137   .190283  .000119  I  .1084770  .0000109  2.4855 0.0076  I   -40.664     .229    -6.183     .298  -.057560   .190280   .1084740   -41.300    -5.800  
+98 228 50872.00 I  -.058657  .000135   .191598  .000113  I  .1059310  .0000108  2.6046 0.0094  I   -40.261     .277    -5.936     .298  -.058660   .191770   .1059310   -41.200    -5.800  
+98 3 1 50873.00 I  -.059602  .000103   .193205  .000109  I  .1032905  .0000154  2.6554 0.0076  I   -40.292     .791    -5.822     .298  -.059380   .193340   .1033060   -41.300    -5.700  
+98 3 2 50874.00 I  -.060660  .000087   .194960  .000136  I  .1006586  .0000108  2.5909 0.0086  I   -40.673     .791    -5.862     .298  -.060390   .194970   .1006710   -41.300    -5.800  
+98 3 3 50875.00 I  -.062008  .000077   .196729  .000124  I  .0981386  .0000077  2.4367 0.0062  I   -41.088     .791    -5.956     .298  -.061790   .196830   .0981390   -41.300    -5.900  
+98 3 4 50876.00 I  -.063592  .000074   .198476  .000115  I  .0957946  .0000060  2.2542 0.0049  I   -41.276     .791    -6.067     .298  -.063460   .198530   .0957850   -41.200    -6.100  
+98 3 5 50877.00 I  -.065267  .000080   .200360  .000110  I  .0936210  .0000062  2.0992 0.0045  I   -41.195     .791    -6.227     .298  -.065140   .200500   .0935970   -41.200    -6.300  
+98 3 6 50878.00 I  -.066905  .000067   .202319  .000108  I  .0915841  .0000067  1.9806 0.0049  I   -41.001     .791    -6.417     .298  -.066730   .202460   .0915420   -41.000    -6.500  
+98 3 7 50879.00 I  -.068490  .000066   .204312  .000108  I  .0896482  .0000076  1.8969 0.0059  I   -40.864     .791    -6.560     .298  -.068240   .204350   .0895940   -40.700    -6.700  
+98 3 8 50880.00 I  -.070085  .000086   .206386  .000066  I  .0877786  .0000098  1.8483 0.0057  I   -40.799     .791    -6.628     .298  -.070080   .206330   .0877340   -40.500    -6.800  
+98 3 9 50881.00 I  -.071696  .000104   .208509  .000068  I  .0859377  .0000085  1.8416 0.0059  I   -40.701     .791    -6.676     .298  -.071740   .208600   .0859110   -40.300    -6.800  
+98 310 50882.00 I  -.073337  .000111   .210604  .000120  I  .0840788  .0000066  1.8849 0.0054  I   -40.530     .791    -6.748     .298  -.073230   .210690   .0840630   -40.100    -6.800  
+98 311 50883.00 I  -.074912  .000123   .212602  .000121  I  .0821452  .0000066  1.9965 0.0046  I   -40.360     .791    -6.807     .298  -.074840   .212610   .0821340   -40.100    -6.700  
+98 312 50884.00 I  -.076183  .000114   .214536  .000116  I  .0800910  .0000064  2.0925 0.0045  I   -40.252     .791    -6.791     .298  -.076150   .214770   .0801100   -40.200    -6.600  
+98 313 50885.00 I  -.077239  .000124   .216649  .000119  I  .0779831  .0000061  2.1197 0.0044  I   -40.181     .791    -6.707     .298  -.077210   .216810   .0780040   -40.400    -6.500  
+98 314 50886.00 I  -.078422  .000136   .218857  .000119  I  .0758586  .0000059  2.1257 0.0039  I   -40.158     .791    -6.655     .298  -.078270   .219010   .0758610   -40.500    -6.500  
+98 315 50887.00 I  -.080159  .000123   .220952  .000138  I  .0737412  .0000048  2.1031 0.0045  I   -40.221     .106    -6.703     .298  -.079990   .221170   .0737280   -40.600    -6.500  
+98 316 50888.00 I  -.082383  .000121   .222816  .000107  I  .0716611  .0000068  2.0544 0.0040  I   -40.319     .791    -6.811     .298  -.082370   .222800   .0716340   -40.700    -6.600  
+98 317 50889.00 I  -.084734  .000121   .224483  .000123  I  .0696389  .0000063  1.9862 0.0042  I   -40.378     .791    -6.915     .298  -.084700   .224290   .0696090   -40.600    -6.800  
+98 318 50890.00 I  -.086987  .000126   .226085  .000126  I  .0676945  .0000048  1.9006 0.0042  I   -40.443     .791    -7.026     .298  -.086850   .226100   .0676700   -40.400    -7.000  
+98 319 50891.00 I  -.089008  .000130   .227692  .000133  I  .0658367  .0000055  1.8181 0.0036  I   -40.573     .791    -7.176     .298  -.088830   .227490   .0658220   -40.200    -7.100  
+98 320 50892.00 I  -.090879  .000139   .229385  .000141  I  .0640498  .0000053  1.7609 0.0042  I   -40.611     .791    -7.293     .298  -.090580   .229270   .0640470   -39.900    -7.300  
+98 321 50893.00 I  -.092870  .000157   .231116  .000134  I  .0623018  .0000064  1.7428 0.0044  I   -40.309     .791    -7.236     .298  -.092640   .231340   .0623090   -39.700    -7.400  
+98 322 50894.00 I  -.094860  .000153   .232676  .000110  I  .0605486  .0000069  1.7716 0.0041  I   -39.756     .791    -7.022     .298  -.094790   .232860   .0605620   -39.500    -7.400  
+98 323 50895.00 I  -.096558  .000146   .234047  .000099  I  .0587419  .0000050  1.8506 0.0047  I   -39.397     .145    -6.895     .298  -.096640   .234260   .0587590   -39.500    -7.400  
+98 324 50896.00 I  -.097957  .000140   .235445  .000099  I  .0568313  .0000063  1.9780 0.0043  I   -39.491     .149    -7.066     .298  -.097950   .235530   .0568400   -39.600    -7.300  
+98 325 50897.00 I  -.099289  .000141   .237069  .000085  I  .0547597  .0000070  2.1842 0.0051  I   -39.743     .149    -7.426     .298  -.099210   .237070   .0547480   -39.800    -7.300  
+98 326 50898.00 I  -.100687  .000151   .238870  .000088  I  .0524515  .0000079  2.4248 0.0053  I   -39.699     .149    -7.644     .298  -.100620   .239000   .0524510   -39.900    -7.200  
+98 327 50899.00 I  -.102256  .000142   .240823  .000094  I  .0499414  .0000079  2.5714 0.0060  I   -39.350     .149    -7.571     .298  -.102090   .240920   .0499410   -40.100    -7.100  
+98 328 50900.00 I  -.104010  .000132   .242915  .000090  I  .0473499  .0000089  2.5942 0.0076  I   -39.095     .165    -7.379     .298  -.103850   .242890   .0473420   -40.300    -7.100  
+98 329 50901.00 I  -.105925  .000136   .245145  .000100  I  .0447784  .0000131  2.5408 0.0085  I   -39.277     .157    -7.307     .298  -.105760   .245180   .0447980   -40.400    -7.100  
+98 330 50902.00 I  -.107733  .000123   .247444  .000093  I  .0422919  .0000145  2.4168 0.0097  I   -39.840     .337    -7.389     .298  -.107670   .247570   .0423000   -40.400    -7.300  
+98 331 50903.00 I  -.109077  .000114   .249741  .000098  I  .0399650  .0000142  2.2323 0.0097  I   -40.383     .362    -7.530     .298  -.109050   .249770   .0399470   -40.300    -7.500  
+98 4 1 50904.00 I  -.109935  .000111   .252103  .000095  I  .0378239  .0000129  2.0578 0.0093  I   -40.478     .362    -7.721     .298  -.109830   .252060   .0378020   -40.100    -7.700  
+98 4 2 50905.00 I  -.110527  .000083   .254548  .000079  I  .0358353  .0000120  1.9254 0.0084  I   -40.005     .362    -8.040     .298  -.110280   .254420   .0358140   -39.900    -8.000  
+98 4 3 50906.00 I  -.111154  .000073   .256961  .000079  I  .0339580  .0000109  1.8384 0.0082  I   -39.385     .362    -8.425     .298  -.110870   .256870   .0339240   -39.700    -8.200  
+98 4 4 50907.00 I  -.112007  .000086   .259254  .000094  I  .0321401  .0000112  1.8066 0.0084  I   -39.175     .362    -8.682     .298  -.111810   .259280   .0321020   -39.500    -8.300  
+98 4 5 50908.00 I  -.112987  .000120   .261411  .000091  I  .0303301  .0000127  1.8188 0.0081  I   -39.426     .386    -8.722     .298  -.112870   .261520   .0303230   -39.300    -8.300  
+98 4 6 50909.00 I  -.113778  .000166   .263513  .000111  I  .0284947  .0000117  1.8544 0.0084  I   -39.763     .203    -8.616     .298  -.113770   .263420   .0285300   -39.300    -8.300  
+98 4 7 50910.00 I  -.114131  .000163   .265742  .000152  I  .0266181  .0000111  1.8996 0.0081  I   -39.820     .167    -8.467     .298  -.114090   .265490   .0266580   -39.400    -8.300  
+98 4 8 50911.00 I  -.114147  .000163   .268209  .000160  I  .0246935  .0000112  1.9507 0.0078  I   -39.588     .167    -8.277     .298  -.114030   .268270   .0247080   -39.500    -8.100  
+98 4 9 50912.00 I  -.113999  .000165   .270830  .000160  I  .0227298  .0000111  1.9623 0.0078  I   -39.282     .167    -8.007     .298  -.113910   .270980   .0227410   -39.600    -8.000  
+98 410 50913.00 I  -.113829  .000164   .273649  .000153  I  .0207804  .0000109  1.9392 0.0075  I   -39.061     .167    -7.717     .298  -.113910   .273640   .0207910   -39.700    -7.900  
+98 411 50914.00 I  -.113728  .000177   .276606  .000148  I  .0188422  .0000102  1.9437 0.0070  I   -38.940     .270    -7.557     .298  -.113670   .276770   .0188450   -39.900    -7.800  
+98 412 50915.00 I  -.113850  .000138   .279529  .000145  I  .0168902  .0000088  1.9565 0.0061  I   -38.906     .323    -7.612     .298  -.113640   .279590   .0168970   -39.900    -7.800  
+98 413 50916.00 I  -.114218  .000141   .282316  .000112  I  .0149437  .0000068  1.9257 0.0057  I   -38.984     .383    -7.808     .298  -.114090   .282260   .0149670   -39.900    -7.800  
+98 414 50917.00 I  -.114694  .000144   .284973  .000101  I  .0130558  .0000074  1.8438 0.0048  I   -39.226     .383    -8.019     .298  -.114540   .285160   .0130720   -39.800    -8.000  
+98 415 50918.00 I  -.115071  .000147   .287594  .000099  I  .0112541  .0000067  1.7678 0.0051  I   -39.680     .383    -8.204     .298  -.114800   .287640   .0112440   -39.600    -8.100  
+98 416 50919.00 I  -.114962  .000153   .290211  .000098  I  .0095180  .0000069  1.6983 0.0049  I   -40.211     .383    -8.376     .298  -.114880   .290140   .0095240   -39.200    -8.300  
+98 417 50920.00 I  -.114593  .000156   .292907  .000094  I  .0078497  .0000072  1.6521 0.0055  I   -40.640     .293    -8.529     .298  -.114520   .292890   .0078650   -38.900    -8.400  
+98 418 50921.00 I  -.114123  .000135   .295738  .000096  I  .0061926  .0000085  1.6695 0.0058  I   -40.732     .337    -8.569     .298  -.113800   .295860   .0061990   -38.600    -8.500  
+98 419 50922.00 I  -.113765  .000095   .298599  .000096  I  .0044955  .0000092  1.7326 0.0062  I   -40.401     .377    -8.438     .298  -.113500   .298690   .0044940   -38.500    -8.500  
+98 420 50923.00 I  -.113651  .000105   .301376  .000113  I  .0027115  .0000089  1.8435 0.0062  I   -39.886     .626    -8.255     .298  -.113460   .301360   .0027230   -38.400    -8.500  
+98 421 50924.00 I  -.113687  .000096   .304101  .000110  I  .0007922  .0000082  2.0033 0.0060  I   -39.482     .542    -8.204     .298  -.113590   .304190   .0008070   -38.500    -8.400  
+98 422 50925.00 I  -.113745  .000092   .306874  .000121  I -.0013031  .0000080  2.1872 0.0058  I   -39.217     .542    -8.300     .298  -.113720   .307070  -.0012960   -38.800    -8.300  
+98 423 50926.00 I  -.113785  .000082   .309763  .000124  I -.0035829  .0000082  2.3732 0.0056  I   -39.031     .542    -8.347     .298  -.113540   .309850  -.0035750   -39.200    -8.100  
+98 424 50927.00 I  -.113731  .000085   .312527  .000127  I -.0060392  .0000077  2.5285 0.0056  I   -38.874     .587    -8.235     .298  -.113670   .312610  -.0060240   -39.600    -8.100  
+98 425 50928.00 I  -.113542  .000083   .314955  .000126  I -.0086030  .0000075  2.5733 0.0053  I   -38.863     .587    -8.093     .298  -.113670   .315030  -.0085900   -39.900    -8.000  
+98 426 50929.00 I  -.113192  .000051   .317088  .000093  I -.0111380  .0000074  2.4748 0.0060  I   -39.145     .791    -8.093     .298  -.113000   .317070  -.0111280   -40.200    -8.000  
+98 427 50930.00 I  -.112849  .000081   .319179  .000117  I -.0135214  .0000093  2.2828 0.0062  I   -39.657     .791    -8.207     .298  -.112520   .319060  -.0135040   -40.200    -8.100  
+98 428 50931.00 I  -.112779  .000081   .321423  .000099  I -.0156958  .0000099  2.0673 0.0064  I   -40.104     .791    -8.288     .298  -.112730   .321280  -.0156750   -40.200    -8.200  
+98 429 50932.00 I  -.113003  .000070   .323784  .000094  I -.0176699  .0000089  1.8934 0.0065  I   -40.145     .791    -8.321     .298  -.113080   .323640  -.0176610   -40.000    -8.400  
+98 430 50933.00 I  -.113272  .000070   .326044  .000083  I -.0195105  .0000085  1.8032 0.0064  I   -39.678     .791    -8.452     .298  -.113090   .326010  -.0195140   -39.900    -8.500  
+98 5 1 50934.00 I  -.113419  .000076   .328142  .000084  I -.0212989  .0000091  1.7809 0.0055  I   -39.036     .791    -8.741     .298  -.113480   .328220  -.0213010   -39.600    -8.700  
+98 5 2 50935.00 I  -.113532  .000086   .330140  .000083  I -.0230801  .0000069  1.7821 0.0073  I   -38.752     .151    -9.045     .298  -.113460   .330150  -.0230680   -39.400    -8.800  
+98 5 3 50936.00 I  -.113724  .000063   .332146  .000070  I -.0248627  .0000115  1.7822 0.0060  I   -39.008     .203    -9.187     .298  -.113500   .332250  -.0248360   -39.400    -8.900  
+98 5 4 50937.00 I  -.113999  .000076   .334250  .000100  I -.0266435  .0000097  1.7793 0.0072  I   -39.441     .430    -9.146     .298  -.113900   .334350  -.0266060   -39.400    -8.800  
+98 5 5 50938.00 I  -.114283  .000077   .336511  .000100  I -.0284218  .0000086  1.7779 0.0065  I   -39.601     .373    -9.007     .298  -.114060   .336460  -.0283880   -39.500    -8.700  
+98 5 6 50939.00 I  -.114503  .000074   .338910  .000102  I -.0302130  .0000086  1.8177 0.0060  I   -39.484     .373    -8.819     .298  -.114270   .338950  -.0301970   -39.600    -8.600  
+98 5 7 50940.00 I  -.114596  .000078   .341219  .000098  I -.0320567  .0000083  1.8561 0.0060  I   -39.383     .373    -8.633     .298  -.114420   .341170  -.0320350   -39.800    -8.500  
+98 5 8 50941.00 I  -.114527  .000073   .343367  .000094  I -.0339048  .0000083  1.8333 0.0059  I   -39.509     .414    -8.437     .298  -.114350   .343360  -.0338790   -40.100    -8.400  
+98 5 9 50942.00 I  -.114308  .000066   .345406  .000088  I -.0357187  .0000085  1.7968 0.0048  I   -39.774     .414    -8.301     .298  -.114230   .345460  -.0357040   -40.200    -8.400  
+98 510 50943.00 I  -.113923  .000064   .347417  .000060  I -.0374953  .0000050  1.7510 0.0050  I   -39.995     .791    -8.307     .298  -.113610   .347380  -.0374910   -40.300    -8.400  
+98 511 50944.00 I  -.113420  .000059   .349476  .000043  I -.0392108  .0000052  1.6757 0.0031  I   -40.117     .791    -8.424     .298  -.113120   .349430  -.0392160   -40.200    -8.400  
+98 512 50945.00 I  -.112913  .000064   .351594  .000043  I -.0408403  .0000038  1.5814 0.0036  I   -40.162     .791    -8.554     .298  -.112850   .351680  -.0408540   -40.000    -8.600  
+98 513 50946.00 I  -.112428  .000064   .353738  .000046  I -.0423730  .0000049  1.4853 0.0039  I   -40.147     .791    -8.644     .298  -.112340   .353770  -.0423860   -39.800    -8.700  
+98 514 50947.00 I  -.111946  .000060   .355832  .000045  I -.0438200  .0000069  1.4165 0.0042  I   -40.133     .791    -8.716     .298  -.111790   .355760  -.0438250   -39.400    -8.700  
+98 515 50948.00 I  -.111435  .000061   .357812  .000045  I -.0452267  .0000069  1.4100 0.0050  I   -40.186     .791    -8.797     .298  -.111280   .357820  -.0452220   -39.100    -8.700  
+98 516 50949.00 I  -.110968  .000058   .359665  .000050  I -.0466667  .0000071  1.4838 0.0052  I   -40.257     .791    -8.860     .298  -.110670   .359770  -.0466610   -38.700    -8.700  
+98 517 50950.00 I  -.110639  .000053   .361433  .000086  I -.0482169  .0000079  1.6253 0.0046  I   -40.187     .791    -8.854     .298  -.110410   .361540  -.0482240   -38.600    -8.700  
+98 518 50951.00 I  -.110376  .000062   .363189  .000111  I -.0499317  .0000060  1.8098 0.0067  I   -39.900     .378    -8.767     .298  -.110290   .363160  -.0499590   -38.700    -8.600  
+98 519 50952.00 I  -.110102  .000065   .365018  .000125  I -.0518421  .0000109  2.0111 0.0061  I   -39.516     .309    -8.630     .298  -.110140   .365110  -.0518730   -38.900    -8.500  
+98 520 50953.00 I  -.109967  .000063   .367001  .000125  I -.0539483  .0000106  2.1957 0.0075  I   -39.247     .309    -8.461     .298  -.109960   .367090  -.0539550   -39.300    -8.300  
+98 521 50954.00 I  -.109945  .000065   .369171  .000127  I -.0562109  .0000104  2.3126 0.0074  I   -39.244     .309    -8.261     .298  -.109810   .369190  -.0561900   -39.800    -8.200  
+98 522 50955.00 I  -.109673  .000079   .371698  .000127  I -.0585571  .0000104  2.3804 0.0073  I   -39.540     .309    -8.064     .298  -.109650   .371850  -.0585430   -40.300    -8.100  
+98 523 50956.00 I  -.109234  .000075   .374455  .000122  I -.0609492  .0000103  2.3805 0.0079  I   -40.078     .309    -8.007     .298  -.109220   .374630  -.0609400   -40.800    -8.100  
+98 524 50957.00 I  -.108731  .000069   .377218  .000099  I -.0632756  .0000118  2.2529 0.0067  I   -40.715     .791    -8.186     .298  -.108590   .377330  -.0632740   -41.100    -8.100  
+98 525 50958.00 I  -.108142  .000074   .379851  .000092  I -.0654284  .0000087  2.0455 0.0073  I   -41.260     .791    -8.483     .298  -.107930   .379900  -.0654430   -41.200    -8.300  
+98 526 50959.00 I  -.107668  .000073   .382295  .000092  I -.0673610  .0000086  1.8211 0.0064  I   -41.535     .791    -8.665     .298  -.107430   .382270  -.0673860   -41.200    -8.400  
+98 527 50960.00 I  -.107472  .000072   .384492  .000108  I -.0690754  .0000095  1.6109 0.0075  I   -41.425     .791    -8.644     .298  -.107250   .384510  -.0690950   -41.100    -8.500  
+98 528 50961.00 I  -.107423  .000062   .386358  .000125  I -.0705961  .0000124  1.4405 0.0081  I   -40.965     .791    -8.568     .298  -.107410   .386640  -.0706060   -40.900    -8.600  
+98 529 50962.00 I  -.107151  .000056   .387984  .000127  I -.0719801  .0000130  1.3411 0.0092  I   -40.441     .791    -8.616     .298  -.107110   .388050  -.0719760   -40.600    -8.700  
+98 530 50963.00 I  -.106549  .000057   .389639  .000126  I -.0733016  .0000137  1.3117 0.0082  I   -40.264     .791    -8.772     .298  -.106410   .389480  -.0732880   -40.500    -8.800  
+98 531 50964.00 I  -.105883  .000054   .391519  .000103  I -.0746174  .0000099  1.3241 0.0080  I   -40.588     .791    -8.883     .298  -.105720   .391520  -.0746120   -40.400    -8.900  
+98 6 1 50965.00 I  -.105316  .000050   .393568  .000109  I -.0759485  .0000081  1.3327 0.0065  I   -41.107     .108    -8.865     .298  -.105150   .393650  -.0759530   -40.400    -8.800  
+98 6 2 50966.00 I  -.104823  .000055   .395623  .000097  I -.0772754  .0000085  1.3190 0.0053  I   -41.375     .108    -8.773     .298  -.104610   .395670  -.0772840   -40.700    -8.800  
+98 6 3 50967.00 I  -.104525  .000053   .397545  .000079  I -.0785861  .0000068  1.3039 0.0057  I   -41.311     .108    -8.683     .298  -.104580   .397600  -.0785930   -41.000    -8.700  
+98 6 4 50968.00 I  -.104589  .000055   .399252  .000086  I -.0798820  .0000076  1.2851 0.0048  I   -41.222     .108    -8.594     .298  -.104650   .399280  -.0798900   -41.500    -8.700  
+98 6 5 50969.00 I  -.104719  .000054   .400837  .000086  I -.0811497  .0000068  1.2466 0.0051  I   -41.360     .108    -8.470     .298  -.104610   .400860  -.0811560   -41.800    -8.500  
+98 6 6 50970.00 I  -.104591  .000055   .402402  .000087  I -.0823602  .0000067  1.1630 0.0054  I   -41.667     .108    -8.319     .298  -.104480   .402480  -.0823650   -42.200    -8.400  
+98 6 7 50971.00 I  -.104185  .000051   .403981  .000073  I -.0834622  .0000084  1.0389 0.0048  I   -41.952     .791    -8.190     .298  -.104060   .404030  -.0834740   -42.300    -8.400  
+98 6 8 50972.00 I  -.103655  .000047   .405577  .000069  I -.0844369  .0000069  0.9114 0.0053  I   -42.133     .791    -8.127     .298  -.103460   .405670  -.0844450   -42.400    -8.300  
+98 6 9 50973.00 I  -.103042  .000047   .407182  .000075  I -.0852883  .0000066  0.7938 0.0055  I   -42.198     .791    -8.147     .298  -.102880   .407220  -.0852860   -42.200    -8.200  
+98 610 50974.00 I  -.102217  .000050   .408801  .000072  I -.0860368  .0000086  0.7131 0.0059  I   -42.112     .791    -8.230     .298  -.102140   .408820  -.0860440   -41.900    -8.200  
+98 611 50975.00 I  -.101272  .000059   .410408  .000074  I -.0867308  .0000099  0.6813 0.0068  I   -41.898     .791    -8.318     .298  -.101130   .410530  -.0867380   -41.600    -8.100  
+98 612 50976.00 I  -.100513  .000063   .411926  .000077  I -.0874101  .0000106  0.6815 0.0071  I   -41.737     .791    -8.358     .298  -.100270   .412130  -.0873920   -41.200    -8.100  
+98 613 50977.00 I  -.100010  .000067   .413302  .000091  I -.0881062  .0000102  0.7188 0.0085  I   -41.806     .239    -8.341     .298  -.099840   .413470  -.0880890   -41.100    -8.000  
+98 614 50978.00 I  -.099407  .000072   .414567  .000098  I -.0888650  .0000133  0.8080 0.0082  I   -42.057     .334    -8.306     .298  -.099300   .414620  -.0888680   -41.100    -7.900  
+98 615 50979.00 I  -.098389  .000094   .415830  .000113  I -.0897238  .0000129  0.9019 0.0082  I   -42.265     .353    -8.274     .112  -.098200   .415820  -.0897280   -41.300    -7.900  
+98 616 50980.00 I  -.097022  .000098   .417175  .000110  I -.0906571  .0000097  0.9608 0.0079  I   -42.306     .318    -8.214     .298  -.096830   .417220  -.0906520   -41.700    -7.900  
+98 617 50981.00 I  -.095357  .000093   .418640  .000107  I -.0916396  .0000091  1.0022 0.0066  I   -42.294     .318    -8.064     .298  -.095290   .418670  -.0916240   -42.200    -7.800  
+98 618 50982.00 I  -.093646  .000092   .420329  .000110  I -.0926459  .0000089  0.9969 0.0064  I   -42.385     .318    -7.755     .298  -.093450   .420380  -.0926230   -42.900    -7.800  
+98 619 50983.00 I  -.091783  .000089   .421990  .000104  I -.0936208  .0000089  0.9539 0.0063  I   -42.737     .313    -7.381     .110  -.091580   .422120  -.0936100   -43.500    -7.900  
+98 620 50984.00 I  -.089921  .000082   .423490  .000101  I -.0945457  .0000090  0.8871 0.0051  I   -43.376     .271    -7.149     .115  -.089720   .423670  -.0945390   -44.000    -8.000  
+98 621 50985.00 I  -.088168  .000060   .424852  .000108  I -.0953771  .0000051  0.7668 0.0057  I   -44.109     .125    -7.200     .298  -.087960   .425010  -.0953740   -44.500    -8.000  
+98 622 50986.00 I  -.086454  .000066   .426143  .000102  I -.0960672  .0000071  0.6101 0.0048  I   -44.651     .791    -7.494     .114  -.086300   .426230  -.0960770   -44.700    -8.200  
+98 623 50987.00 I  -.084694  .000068   .427473  .000109  I -.0966007  .0000082  0.4635 0.0057  I   -44.835     .791    -7.869     .114  -.084560   .427490  -.0966180   -44.800    -8.300  
+98 624 50988.00 I  -.082934  .000075   .428940  .000114  I -.0970187  .0000090  0.3905 0.0061  I   -44.696     .791    -8.192     .114  -.082690   .428930  -.0970360   -44.600    -8.400  
+98 625 50989.00 I  -.081371  .000074   .430507  .000121  I -.0974038  .0000091  0.3838 0.0061  I   -44.427     .791    -8.360     .114  -.081120   .430630  -.0974240   -44.400    -8.500  
+98 626 50990.00 I  -.079860  .000077   .431951  .000122  I -.0977963  .0000083  0.4069 0.0067  I   -44.265     .139    -8.489     .298  -.079670   .432110  -.0978250   -44.100    -8.600  
+98 627 50991.00 I  -.078218  .000080   .433227  .000145  I -.0982229  .0000097  0.4457 0.0071  I   -44.404     .139    -8.676     .298  -.078070   .433380  -.0982420   -44.000    -8.700  
+98 628 50992.00 I  -.076251  .000088   .434408  .000137  I -.0986901  .0000114  0.4916 0.0069  I   -44.877     .506    -8.839     .298  -.076120   .434520  -.0986920   -44.000    -8.800  
+98 629 50993.00 I  -.073849  .000085   .435647  .000141  I -.0992107  .0000098  0.5513 0.0071  I   -45.473     .582    -8.861     .158  -.073650   .435640  -.0992050   -44.100    -8.800  
+98 630 50994.00 I  -.071100  .000086   .437082  .000153  I -.0997910  .0000086  0.6059 0.0065  I   -45.877     .511    -8.727     .139  -.070940   .437030  -.0997880   -44.400    -8.700  
+98 7 1 50995.00 I  -.068239  .000082   .438694  .000153  I -.1004131  .0000085  0.6322 0.0061  I   -45.942     .511    -8.503     .139  -.068160   .438840  -.1004110   -44.800    -8.700  
+98 7 2 50996.00 I  -.065567  .000082   .440347  .000157  I -.1010373  .0000086  0.6041 0.0056  I   -45.929     .511    -8.285     .139  -.065400   .440690  -.1010300   -45.200    -8.400  
+98 7 3 50997.00 I  -.063009  .000079   .441996  .000130  I -.1016024  .0000073  0.5193 0.0049  I   -45.994     .511    -8.101     .139  -.062830   .442190  -.1015890   -45.700    -8.200  
+98 7 4 50998.00 I  -.060511  .000080   .443674  .000135  I -.1020711  .0000049  0.4202 0.0043  I   -46.149     .513    -7.903     .158  -.060370   .443770  -.1020530   -46.000    -8.000  
+98 7 5 50999.00 I  -.057993  .000063   .445381  .000155  I -.1024447  .0000046  0.3268 0.0042  I   -46.368     .168    -7.676     .298  -.057850   .445490  -.1024230   -46.300    -7.800  
+98 7 6 51000.00 I  -.055404  .000098   .447048  .000156  I -.1027229  .0000069  0.2281 0.0042  I   -46.595     .791    -7.491     .298  -.055190   .447270  -.1027270   -46.900    -7.800  
+98 7 7 51001.00 I  -.052822  .000104   .448568  .000155  I -.1029010  .0000071  0.1297 0.0060  I   -46.748     .791    -7.448     .298  -.052640   .448880  -.1029200   -46.900    -7.700  
+98 7 8 51002.00 I  -.050272  .000103   .449909  .000152  I -.1029867  .0000099  0.0445 0.0061  I   -46.743     .791    -7.578     .298  -.050110   .450080  -.1030040   -46.600    -7.600  
+98 7 9 51003.00 I  -.047789  .000113   .451171  .000154  I -.1030078  .0000099  0.0129 0.0071  I   -46.598     .791    -7.777     .298  -.047580   .451160  -.1030060   -46.400    -7.600  
+98 710 51004.00 I  -.045284  .000107   .452559  .000144  I -.1030458  .0000102  0.0813 0.0067  I   -46.477     .791    -7.881     .298  -.045060   .452530  -.1030150   -46.200    -7.500  
+98 711 51005.00 I  -.042770  .000103   .454154  .000134  I -.1031990  .0000090  0.2354 0.0066  I   -46.574     .791    -7.811     .298  -.042630   .454360  -.1031510   -46.100    -7.600  
+98 712 51006.00 I  -.040237  .000073   .455835  .000120  I -.1035270  .0000083  0.4207 0.0062  I   -46.925     .791    -7.646     .298  -.040080   .456150  -.1034910   -46.100    -7.500  
+98 713 51007.00 I  -.037743  .000061   .457466  .000119  I -.1040342  .0000084  0.5875 0.0052  I   -47.361     .237    -7.531     .108  -.037610   .457660  -.1040260   -46.300    -7.600  
+98 714 51008.00 I  -.035317  .000060   .458998  .000113  I -.1046856  .0000062  0.7053 0.0052  I   -47.653     .239    -7.530     .298  -.035190   .459100  -.1046950   -46.600    -7.600  
+98 715 51009.00 I  -.032985  .000056   .460388  .000106  I -.1054234  .0000063  0.7591 0.0046  I   -47.720     .239    -7.593     .298  -.032820   .460470  -.1054320   -47.100    -7.700  
+98 716 51010.00 I  -.030768  .000055   .461506  .000101  I -.1061780  .0000069  0.7356 0.0046  I   -47.690     .239    -7.644     .298  -.030630   .461620  -.1061790   -47.600    -7.700  
+98 717 51011.00 I  -.028607  .000060   .462383  .000098  I -.1068703  .0000066  0.6392 0.0049  I   -47.745     .276    -7.711     .109  -.028420   .462510  -.1068730   -48.200    -7.800  
+98 718 51012.00 I  -.026513  .000066   .463176  .000094  I -.1074437  .0000070  0.5047 0.0044  I   -48.045     .276    -7.865     .109  -.026280   .463320  -.1074530   -48.700    -8.000  
+98 719 51013.00 I  -.024528  .000079   .463998  .000071  I -.1078768  .0000059  0.3615 0.0060  I   -48.570     .283    -8.132     .298  -.024350   .464190  -.1078910   -49.200    -8.100  
+98 720 51014.00 I  -.022578  .000108   .464858  .000082  I -.1081687  .0000097  0.2245 0.0062  I   -49.098     .280    -8.442     .298  -.022400   .465070  -.1081780   -49.500    -8.200  
+98 721 51015.00 I  -.020654  .000115   .465732  .000078  I -.1083355  .0000110  0.1168 0.0074  I   -49.392     .280    -8.620     .298  -.020390   .465930  -.1083390   -49.500    -8.300  
+98 722 51016.00 I  -.018742  .000107   .466609  .000081  I -.1084270  .0000112  0.0833 0.0078  I   -49.390     .280    -8.515     .298  -.018520   .466820  -.1084240   -49.500    -8.300  
+98 723 51017.00 I  -.016591  .000104   .467461  .000080  I -.1085268  .0000112  0.1239 0.0079  I   -49.144     .280    -8.246     .298  -.016390   .467730  -.1085030   -49.200    -8.400  
+98 724 51018.00 I  -.014288  .000101   .468234  .000078  I -.1086888  .0000112  0.2065 0.0082  I   -48.871     .280    -7.991     .298  -.014030   .468560  -.1086470   -48.900    -8.400  
+98 725 51019.00 I  -.011898  .000096   .468963  .000081  I -.1089490  .0000119  0.3169 0.0080  I   -48.771     .238    -7.853     .298  -.011710   .469190  -.1089050   -48.700    -8.400  
+98 726 51020.00 I  -.009354  .000054   .469790  .000064  I -.1093187  .0000113  0.4158 0.0078  I   -48.896     .791    -7.836     .298  -.009170   .469900  -.1092880   -48.700    -8.400  
+98 727 51021.00 I  -.006753  .000057   .470803  .000106  I -.1097667  .0000102  0.4729 0.0072  I   -49.207     .344    -7.885     .298  -.006510   .470970  -.1097590   -48.900    -8.400  
+98 728 51022.00 I  -.004257  .000119   .471952  .000148  I -.1102513  .0000088  0.4902 0.0074  I   -49.631     .281    -7.988     .298  -.004050   .472140  -.1102550   -49.400    -8.300  
+98 729 51023.00 I  -.001928  .000122   .473168  .000154  I -.1107348  .0000106  0.4705 0.0068  I   -50.038     .281    -8.169     .298  -.001800   .473270  -.1107300   -50.100    -8.200  
+98 730 51024.00 I   .000141  .000124   .474366  .000153  I -.1111979  .0000105  0.4675 0.0075  I   -50.280     .281    -8.393     .298   .000400   .474440  -.1111900   -51.000    -8.200  
+98 731 51025.00 I   .002045  .000122   .475412  .000157  I -.1116748  .0000106  0.4796 0.0074  I   -50.344     .281    -8.509     .298   .002240   .475590  -.1116620   -51.800    -8.200  
+98 8 1 51026.00 I   .003902  .000118   .476287  .000160  I -.1121413  .0000105  0.4444 0.0070  I   -50.405     .281    -8.419     .298   .004030   .476470  -.1121250   -52.400    -8.100  
+98 8 2 51027.00 I   .005810  .000139   .476934  .000165  I -.1125566  .0000092  0.3883 0.0084  I   -50.653     .791    -8.153     .298   .006030   .477150  -.1125390   -52.600    -8.100  
+98 8 3 51028.00 I   .007707  .000096   .477354  .000122  I -.1129220  .0000130  0.3444 0.0055  I   -51.055     .791    -7.825     .298   .007910   .477580  -.1129010   -52.500    -7.900  
+98 8 4 51029.00 I   .009695  .000101   .477689  .000120  I -.1132513  .0000060  0.3181 0.0072  I   -51.373     .791    -7.595     .298   .009780   .477780  -.1132420   -52.000    -7.700  
+98 8 5 51030.00 I   .011888  .000102   .478048  .000120  I -.1135649  .0000060  0.3117 0.0045  I   -51.435     .791    -7.588     .298   .011970   .478280  -.1135780   -51.300    -7.600  
+98 8 6 51031.00 I   .014036  .000107   .478378  .000108  I -.1138882  .0000067  0.3457 0.0044  I   -51.304     .791    -7.798     .298   .014310   .478670  -.1138850   -50.900    -7.500  
+98 8 7 51032.00 I   .016013  .000116   .478564  .000101  I -.1142787  .0000065  0.4469 0.0038  I   -51.165     .791    -8.045     .298   .016310   .478810  -.1142650   -50.600    -7.400  
+98 8 8 51033.00 I   .017857  .000099   .478532  .000092  I -.1147998  .0000035  0.6015 0.0040  I   -51.134     .791    -8.123     .298   .018000   .478750  -.1147840   -50.500    -7.400  
+98 8 9 51034.00 I   .019846  .000062   .478394  .000090  I -.1154884  .0000045  0.7763 0.0027  I   -51.238     .791    -7.997     .298   .020000   .478530  -.1154780   -50.400    -7.400  
+98 810 51035.00 I   .022136  .000106   .478349  .000116  I -.1163435  .0000040  0.9243 0.0032  I   -51.474     .130    -7.824     .298   .022360   .478550  -.1163610   -50.700    -7.500  
+98 811 51036.00 I   .024683  .000113   .478428  .000116  I -.1173135  .0000046  1.0016 0.0029  I   -51.813     .130    -7.773     .298   .024920   .478620  -.1172870   -51.100    -7.600  
+98 812 51037.00 I   .027507  .000118   .478537  .000120  I -.1183214  .0000043  1.0030 0.0035  I   -52.172     .130    -7.849     .298   .027750   .478650  -.1183140   -51.700    -7.800  
+98 813 51038.00 I   .030469  .000115   .478490  .000114  I -.1192916  .0000053  0.9207 0.0038  I   -52.470     .130    -7.924     .298   .030740   .478710  -.1193330   -52.400    -7.900  
+98 814 51039.00 I   .033161  .000123   .478373  .000115  I -.1201373  .0000062  0.7619 0.0048  I   -52.696     .130    -7.916     .298   .033470   .478530  -.1201380   -53.000    -8.000  
+98 815 51040.00 I   .035385  .000126   .478230  .000116  I -.1208043  .0000081  0.5699 0.0104  I   -52.929     .195    -7.861     .298   .035640   .478380  -.1208020   -53.400    -8.200  
+98 816 51041.00 I   .037338  .000076   .477954  .000077  I -.1212799  .0000198  0.3864 0.0104  I   -53.235     .284    -7.851     .298   .037440   .478200  -.1212930   -53.600    -8.200  
+98 817 51042.00 I   .039383  .000058   .477571  .000082  I -.1215970  .0000191  0.2625 0.0144  I   -53.541     .273    -7.944     .298   .039480   .477780  -.1215910   -53.600    -8.300  
+98 818 51043.00 I   .041616  .000039   .477282  .000079  I -.1218310  .0000209  0.2168 0.0146  I   -53.635     .273    -8.126     .298   .041850   .477460  -.1217910   -53.300    -8.300  
+98 819 51044.00 I   .043877  .000038   .477170  .000093  I -.1220399  .0000221  0.1988 0.0147  I   -53.354     .273    -8.330     .298   .044010   .477310  -.1220280   -52.900    -8.400  
+98 820 51045.00 I   .046118  .000032   .477172  .000087  I -.1222411  .0000206  0.2182 0.0153  I   -52.848     .273    -8.426     .298   .046250   .477350  -.1222380   -52.400    -8.400  
+98 821 51046.00 I   .048308  .000029   .477138  .000083  I -.1224999  .0000212  0.3085 0.0123  I   -52.357     .262    -8.392     .298   .048530   .477440  -.1224530   -52.100    -8.500  
+98 822 51047.00 I   .050364  .000033   .476926  .000086  I -.1228631  .0000134  0.4139 0.0121  I   -52.053     .207    -8.266     .298   .050550   .477220  -.1228260   -51.800    -8.500  
+98 823 51048.00 I   .052280  .000047   .476613  .000099  I -.1233193  .0000117  0.4943 0.0078  I   -51.975     .130    -8.090     .298   .052440   .476810  -.1233090   -51.700    -8.500  
+98 824 51049.00 I   .054145  .000049   .476412  .000124  I -.1238454  .0000078  0.5559 0.0069  I   -52.093     .310    -7.930     .144   .054320   .476560  -.1238350   -51.800    -8.500  
+98 825 51050.00 I   .056077  .000065   .476410  .000117  I -.1244283  .0000072  0.6093 0.0054  I   -52.360     .278    -7.882     .126   .056240   .476540  -.1244130   -52.200    -8.400  
+98 826 51051.00 I   .058154  .000067   .476479  .000122  I -.1250667  .0000074  0.6709 0.0053  I   -52.646     .278    -7.984     .126   .058240   .476620  -.1250660   -52.500    -8.200  
+98 827 51052.00 I   .060476  .000068   .476406  .000123  I -.1257468  .0000078  0.6626 0.0053  I   -52.766     .278    -8.121     .126   .060590   .476610  -.1257590   -52.900    -8.000  
+98 828 51053.00 I   .063022  .000067   .476301  .000123  I -.1263679  .0000075  0.5822 0.0056  I   -52.600     .278    -8.189     .126   .063220   .476500  -.1263670   -53.200    -7.900  
+98 829 51054.00 I   .065457  .000061   .476258  .000123  I -.1269255  .0000080  0.5446 0.0057  I   -52.391     .312    -8.140     .145   .065630   .476440  -.1268960   -53.500    -7.800  
+98 830 51055.00 I   .067554  .000058   .476216  .000070  I -.1274719  .0000086  0.5515 0.0072  I   -52.500     .145    -8.006     .298   .067680   .476380  -.1274620   -53.600    -7.700  
+98 831 51056.00 I   .069401  .000048   .476025  .000068  I -.1280318  .0000120  0.5684 0.0076  I   -52.913     .791    -7.856     .298   .069550   .476230  -.1280370   -53.500    -7.600  
+98 9 1 51057.00 I   .071120  .000054   .475561  .000064  I -.1286135  .0000126  0.5999 0.0086  I   -53.253     .791    -7.741     .298   .071240   .475620  -.1285890   -53.300    -8.100  
+98 9 2 51058.00 I   .072844  .000059   .474730  .000062  I -.1292386  .0000123  0.6526 0.0093  I   -53.270     .791    -7.722     .298   .073020   .474780  -.1292390   -53.100    -7.800  
+98 9 3 51059.00 I   .074764  .000068   .473551  .000055  I -.1299286  .0000137  0.7349 0.0093  I   -53.117     .791    -7.848     .298   .074860   .473590  -.1299930   -52.900    -7.400  
+98 9 4 51060.00 I   .077100  .000073   .472242  .000060  I -.1307263  .0000139  0.8707 0.0093  I   -53.060     .791    -8.050     .298   .077130   .472220  -.1307450   -52.800    -7.200  
+98 9 5 51061.00 I   .079771  .000071   .471042  .000062  I -.1316873  .0000126  1.0583 0.0090  I   -53.119     .791    -8.139     .298   .079940   .471050  -.1316510   -52.700    -7.200  
+98 9 6 51062.00 I   .082436  .000081   .470009  .000080  I -.1328413  .0000113  1.2412 0.0080  I   -53.159     .791    -7.991     .298   .082560   .470090  -.1328540   -52.700    -7.100  
+98 9 7 51063.00 I   .084904  .000090   .469113  .000105  I -.1341544  .0000099  1.3782 0.0076  I   -53.169     .791    -7.718     .298   .084950   .469130  -.1341840   -52.900    -7.300  
+98 9 8 51064.00 I   .087182  .000093   .468379  .000112  I -.1355772  .0000103  1.4537 0.0070  I   -53.291     .791    -7.561     .298   .087280   .468310  -.1355640   -53.200    -7.400  
+98 9 9 51065.00 I   .089300  .000094   .467798  .000116  I -.1370359  .0000099  1.4516 0.0075  I   -53.597     .791    -7.628     .298   .089440   .467860  -.1370290   -53.500    -7.700  
+98 910 51066.00 I   .091338  .000094   .467196  .000119  I -.1384475  .0000108  1.3507 0.0075  I   -53.976     .791    -7.806     .298   .091430   .467320  -.1384850   -53.900    -7.800  
+98 911 51067.00 I   .093191  .000104   .466586  .000113  I -.1397068  .0000112  1.1583 0.0078  I   -54.241     .791    -7.942     .298   .093290   .466610  -.1397250   -54.200    -8.000  
+98 912 51068.00 I   .094945  .000100   .466045  .000106  I -.1407607  .0000112  0.9558 0.0099  I   -54.286     .791    -8.017     .298   .095020   .466090  -.1407430   -54.300    -8.200  
+98 913 51069.00 I   .096838  .000077   .465596  .000083  I -.1416295  .0000163  0.7866 0.0082  I   -54.155     .791    -8.103     .298   .096750   .465690  -.1416630   -54.400    -8.300  
+98 914 51070.00 I   .099104  .000082   .465229  .000089  I -.1423579  .0000121  0.6895 0.0103  I   -53.982     .791    -8.212     .298   .099100   .465260  -.1423780   -54.200    -8.300  
+98 915 51071.00 I   .101649  .000086   .464944  .000092  I -.1430406  .0000127  0.6886 0.0089  I   -53.857     .791    -8.264     .298   .101750   .464930  -.1430220   -53.900    -8.300  
+98 916 51072.00 I   .104134  .000081   .464679  .000099  I -.1437528  .0000130  0.7410 0.0091  I   -53.728     .791    -8.182     .298   .104290   .464720  -.1437630   -53.600    -8.200  
+98 917 51073.00 I   .106262  .000070   .464282  .000096  I -.1445295  .0000129  0.8143 0.0091  I   -53.494     .791    -7.960     .298   .106540   .464300  -.1445660   -53.400    -8.000  
+98 918 51074.00 I   .107886  .000062   .463595  .000095  I -.1453877  .0000128  0.9064 0.0090  I   -53.178     .791    -7.703     .298   .108060   .463730  -.1453830   -53.200    -7.800  
+98 919 51075.00 I   .109157  .000064   .462458  .000095  I -.1463485  .0000126  1.0172 0.0084  I   -52.920     .791    -7.466     .298   .109190   .462570  -.1463160   -53.000    -7.500  
+98 920 51076.00 I   .110451  .000074   .461014  .000077  I -.1474139  .0000109  1.1035 0.0072  I   -52.815     .791    -7.210     .298   .110510   .460960  -.1474310   -52.900    -7.300  
+98 921 51077.00 I   .112012  .000069   .459494  .000120  I -.1485386  .0000068  1.1390 0.0060  I   -52.853     .791    -6.993     .298   .112080   .459540  -.1485550   -52.900    -7.200  
+98 922 51078.00 I   .113860  .000067   .457995  .000116  I -.1496770  .0000049  1.1299 0.0040  I   -52.965     .791    -6.951     .298   .113860   .458050  -.1496660   -52.800    -7.100  
+98 923 51079.00 I   .115813  .000066   .456545  .000116  I -.1507847  .0000044  1.0798 0.0047  I   -53.020     .791    -7.135     .298   .115840   .456520  -.1507800   -52.900    -7.100  
+98 924 51080.00 I   .117554  .000070   .455099  .000114  I -.1518337  .0000080  1.0211 0.0046  I   -52.833     .791    -7.414     .298   .117640   .455060  -.1518410   -53.000    -7.200  
+98 925 51081.00 I   .119055  .000074   .453760  .000119  I -.1528303  .0000080  0.9725 0.0054  I   -52.371     .791    -7.604     .298   .119170   .453760  -.1528260   -53.100    -7.300  
+98 926 51082.00 I   .120278  .000072   .452516  .000114  I -.1537788  .0000072  0.9240 0.0068  I   -51.923     .791    -7.638     .298   .120390   .452570  -.1537680   -53.100    -7.400  
+98 927 51083.00 I   .121282  .000062   .451171  .000065  I -.1546743  .0000110  0.8635 0.0060  I   -51.872     .791    -7.595     .298   .121340   .451320  -.1546930   -53.100    -7.500  
+98 928 51084.00 I   .122230  .000077   .449599  .000070  I -.1555100  .0000096  0.8156 0.0066  I   -52.222     .791    -7.548     .298   .122300   .449720  -.1555430   -53.100    -7.500  
+98 929 51085.00 I   .123196  .000068   .447930  .000083  I -.1563226  .0000073  0.8189 0.0051  I   -52.567     .791    -7.473     .298   .123320   .447910  -.1563210   -52.800    -7.400  
+98 930 51086.00 I   .124155  .000072   .446382  .000086  I -.1571765  .0000032  0.9082 0.0042  I   -52.603     .105    -7.352     .298   .124320   .446400  -.1571750   -52.500    -7.300  
+9810 1 51087.00 I   .125228  .000076   .445043  .000088  I -.1581667  .0000041  1.0806 0.0026  I   -52.453     .105    -7.258     .298   .125350   .445040  -.1581770   -52.100    -7.200  
+9810 2 51088.00 I   .126461  .000068   .443833  .000088  I -.1593503  .0000041  1.2909 0.0029  I   -52.389     .105    -7.256     .298   .126530   .443810  -.1593500   -51.600    -6.900  
+9810 3 51089.00 I   .128001  .000074   .442525  .000107  I -.1607548  .0000041  1.5201 0.0036  I   -52.397     .113    -7.265     .298   .128040   .442560  -.1607750   -51.300    -6.800  
+9810 4 51090.00 I   .129722  .000090   .441128  .000122  I -.1623864  .0000059  1.7371 0.0032  I   -52.287     .113    -7.141     .298   .129790   .441260  -.1623690   -51.000    -6.600  
+9810 5 51091.00 I   .131406  .000089   .439608  .000122  I -.1642040  .0000050  1.8792 0.0039  I   -52.014     .103    -6.918     .298   .131470   .439610  -.1641980   -51.000    -6.600  
+9810 6 51092.00 I   .132907  .000097   .437892  .000123  I -.1661050  .0000052  1.9019 0.0036  I   -51.778     .791    -6.808     .298   .132950   .438040  -.1661190   -51.100    -6.600  
+9810 7 51093.00 I   .134223  .000094   .435948  .000126  I -.1679813  .0000051  1.8451 0.0043  I   -51.803     .791    -6.917     .298   .134300   .436030  -.1679800   -51.300    -6.800  
+9810 8 51094.00 I   .135605  .000097   .433878  .000128  I -.1697762  .0000069  1.7314 0.0043  I   -52.125     .791    -7.060     .298   .135650   .433830  -.1697670   -51.700    -7.000  
+9810 9 51095.00 I   .137157  .000103   .431974  .000127  I -.1714343  .0000069  1.5884 0.0055  I   -52.567     .791    -7.073     .298   .137210   .431860  -.1714380   -52.000    -7.200  
+981010 51096.00 I   .138748  .000101   .430520  .000117  I -.1729566  .0000085  1.4561 0.0060  I   -52.863     .157    -7.019     .298   .138840   .430600  -.1729550   -52.100    -7.400  
+981011 51097.00 I   .140117  .000083   .429469  .000105  I -.1743488  .0000098  1.3307 0.0083  I   -52.836     .203    -7.074     .298   .140170   .429510  -.1743740   -52.200    -7.600  
+981012 51098.00 I   .141200  .000072   .428517  .000099  I -.1756317  .0000143  1.2465 0.0095  I   -52.544     .266    -7.283     .298   .141300   .428710  -.1756750   -52.200    -7.700  
+981013 51099.00 I   .142006  .000072   .427407  .000091  I -.1768635  .0000162  1.2275 0.0102  I   -52.205     .266    -7.500     .298   .142080   .427600  -.1768890   -51.900    -7.800  
+981014 51100.00 I   .142798  .000065   .426003  .000093  I -.1781129  .0000146  1.2870 0.0113  I   -51.932     .266    -7.589     .298   .142800   .426040  -.1781180   -51.700    -7.500  
+981015 51101.00 I   .143900  .000067   .424493  .000097  I -.1794441  .0000158  1.3663 0.0103  I   -51.698     .266    -7.537     .298   .144040   .424490  -.1794610   -51.300    -7.400  
+981016 51102.00 I   .145221  .000060   .423152  .000084  I -.1808316  .0000144  1.4038 0.0107  I   -51.366     .244    -7.426     .298   .145380   .423230  -.1808420   -51.100    -7.100  
+981017 51103.00 I   .146618  .000044   .422003  .000070  I -.1822482  .0000145  1.4308 0.0100  I   -50.973     .244    -7.266     .298   .146710   .422100  -.1822120   -50.800    -6.700  
+981018 51104.00 I   .147994  .000079   .420910  .000066  I -.1836933  .0000140  1.4580 0.0081  I   -50.694     .791    -7.001     .298   .148110   .420990  -.1836610   -50.700    -6.500  
+981019 51105.00 I   .149208  .000080   .419738  .000108  I -.1851581  .0000071  1.4667 0.0077  I   -50.590     .791    -6.664     .298   .149290   .419730  -.1851800   -50.600    -6.300  
+981020 51106.00 I   .150207  .000089   .418399  .000108  I -.1866206  .0000065  1.4571 0.0044  I   -50.562     .791    -6.414     .298   .150230   .418400  -.1866130   -50.500    -6.300  
+981021 51107.00 I   .151112  .000088   .416821  .000109  I -.1880692  .0000053  1.4383 0.0043  I   -50.471     .791    -6.367     .298   .151150   .416880  -.1880530   -50.500    -6.200  
+981022 51108.00 I   .151751  .000102   .414917  .000124  I -.1894838  .0000056  1.3794 0.0037  I   -50.226     .791    -6.463     .298   .151950   .414940  -.1895090   -50.500    -6.300  
+981023 51109.00 I   .152151  .000102   .412904  .000123  I -.1908182  .0000053  1.2913 0.0040  I   -49.858     .791    -6.543     .298   .152320   .412880  -.1908230   -50.500    -6.400  
+981024 51110.00 I   .152450  .000095   .411039  .000121  I -.1920780  .0000058  1.2380 0.0051  I   -49.542     .791    -6.540     .298   .152530   .411040  -.1920590   -50.600    -6.500  
+981025 51111.00 I   .152867  .000075   .409395  .000106  I -.1933079  .0000087  1.2257 0.0077  I   -49.513     .791    -6.517     .298   .152960   .409450  -.1933040   -50.600    -6.500  
+981026 51112.00 I   .153419  .000061   .407837  .000105  I -.1945276  .0000142  1.2080 0.0099  I   -49.786     .791    -6.526     .298   .153510   .407950  -.1945460   -50.500    -6.500  
+981027 51113.00 I   .154100  .000056   .406206  .000097  I -.1957206  .0000177  1.1804 0.0124  I   -50.066     .791    -6.493     .298   .154190   .406270  -.1957380   -50.400    -6.400  
+981028 51114.00 I   .154951  .000043   .404482  .000084  I -.1969058  .0000204  1.2052 0.0137  I   -50.074     .791    -6.323     .298   .155000   .404560  -.1969190   -50.100    -6.300  
+981029 51115.00 I   .155988  .000040   .402752  .000080  I -.1981645  .0000209  1.3307 0.0144  I   -49.866     .791    -6.052     .298   .156040   .402790  -.1981300   -49.700    -6.100  
+981030 51116.00 I   .157099  .000041   .401147  .000081  I -.1995944  .0000203  1.5378 0.0130  I   -49.718     .791    -5.815     .298   .157170   .401180  -.1995050   -49.200    -5.700  
+981031 51117.00 I   .158205  .000047   .399656  .000085  I -.2012498  .0000156  1.7739 0.0121  I   -49.732     .791    -5.650     .298   .158280   .399690  -.2011920   -48.800    -5.400  
+9811 1 51118.00 I   .159372  .000050   .398227  .000101  I -.2031347  .0000133  1.9874 0.0086  I   -49.763     .157    -5.481     .298   .159470   .398270  -.2031340   -48.600    -5.200  
+9811 2 51119.00 I   .160608  .000060   .396796  .000095  I -.2051970  .0000074  2.1180 0.0075  I   -49.695     .174    -5.314     .298   .160720   .396800  -.2052100   -48.500    -5.100  
+9811 3 51120.00 I   .161800  .000099   .395350  .000114  I -.2073295  .0000069  2.1252 0.0049  I   -49.557     .158    -5.299     .298   .161840   .395320  -.2073330   -48.800    -5.200  
+9811 4 51121.00 I   .162759  .000108   .393866  .000116  I -.2094137  .0000065  2.0315 0.0052  I   -49.432     .144    -5.497     .298   .162790   .393910  -.2094130   -48.900    -5.300  
+9811 5 51122.00 I   .163186  .000105   .392206  .000121  I -.2113694  .0000078  1.8684 0.0052  I   -49.428     .144    -5.680     .298   .163190   .392310  -.2113550   -49.100    -5.600  
+9811 6 51123.00 I   .163127  .000107   .390338  .000130  I -.2131451  .0000082  1.6890 0.0057  I   -49.549     .144    -5.581     .298   .163130   .390500  -.2131330   -49.200    -5.900  
+9811 7 51124.00 I   .163177  .000103   .388193  .000131  I -.2147595  .0000083  1.5458 0.0056  I   -49.636     .125    -5.360     .104   .163230   .388320  -.2147600   -49.200    -6.100  
+9811 8 51125.00 I   .163540  .000100   .385840  .000108  I -.2162531  .0000077  1.4518 0.0064  I   -49.512     .791    -5.337     .115   .163570   .385860  -.2162550   -49.200    -6.200  
+9811 9 51126.00 I   .164135  .000088   .383444  .000093  I -.2176863  .0000097  1.4273 0.0061  I   -49.208     .791    -5.588     .298   .164150   .383480  -.2176810   -49.100    -6.200  
+981110 51127.00 I   .164793  .000054   .381095  .000082  I -.2191246  .0000094  1.4534 0.0059  I   -48.999     .118    -5.897     .298   .164860   .381130  -.2191100   -49.000    -6.100  
+981111 51128.00 I   .165431  .000047   .378844  .000080  I -.2205953  .0000067  1.4861 0.0059  I   -49.097     .139    -5.996     .298   .165440   .378810  -.2205850   -48.900    -6.000  
+981112 51129.00 I   .166094  .000042   .376752  .000078  I -.2220940  .0000072  1.5104 0.0049  I   -49.362     .139    -5.854     .298   .166110   .376800  -.2220960   -48.800    -5.700  
+981113 51130.00 I   .166560  .000039   .374724  .000068  I -.2236125  .0000071  1.5240 0.0051  I   -49.450     .139    -5.638     .298   .166620   .374820  -.2236190   -48.700    -5.500  
+981114 51131.00 I   .166577  .000039   .372674  .000077  I -.2251363  .0000071  1.5206 0.0067  I   -49.255     .139    -5.453     .298   .166590   .372680  -.2251430   -48.600    -5.200  
+981115 51132.00 I   .166129  .000059   .370621  .000104  I -.2266458  .0000113  1.4936 0.0065  I   -48.998     .158    -5.261     .298   .166180   .370690  -.2266690   -48.500    -5.000  
+981116 51133.00 I   .165389  .000074   .368567  .000127  I -.2281187  .0000108  1.4521 0.0078  I   -48.866     .277    -5.020     .168   .165440   .368620  -.2281310   -48.400    -4.800  
+981117 51134.00 I   .164616  .000078   .366565  .000147  I -.2295496  .0000108  1.4095 0.0074  I   -48.796     .320    -4.794     .206   .164590   .366530  -.2295470   -48.300    -4.700  
+981118 51135.00 I   .164092  .000082   .364644  .000150  I -.2309382  .0000100  1.3685 0.0074  I   -48.669     .320    -4.680     .206   .164060   .364670  -.2309380   -48.200    -4.800  
+981119 51136.00 I   .163768  .000094   .362711  .000152  I -.2322849  .0000100  1.3225 0.0071  I   -48.483     .320    -4.688     .206   .163740   .362710  -.2322770   -48.200    -4.800  
+981120 51137.00 I   .163194  .000105   .360797  .000147  I -.2335826  .0000102  1.2746 0.0071  I   -48.271     .320    -4.743     .206   .163200   .360760  -.2335820   -48.000    -4.900  
+981121 51138.00 I   .161963  .000096   .358907  .000136  I -.2348381  .0000101  1.2386 0.0082  I   -48.057     .320    -4.794     .206   .162040   .359040  -.2348520   -47.900    -5.100  
+981122 51139.00 I   .160085  .000077   .356946  .000110  I -.2360666  .0000129  1.2230 0.0072  I   -47.909     .791    -4.855     .298   .160030   .357130  -.2360610   -47.900    -5.100  
+981123 51140.00 I   .158115  .000073   .354896  .000096  I -.2372925  .0000104  1.2326 0.0083  I   -47.900     .791    -4.940     .298   .158090   .354910  -.2372750   -47.800    -5.200  
+981124 51141.00 I   .156354  .000073   .352913  .000095  I -.2385373  .0000106  1.2587 0.0077  I   -47.984     .791    -4.990     .298   .156480   .352940  -.2385630   -47.800    -5.000  
+981125 51142.00 I   .154814  .000070   .351026  .000107  I -.2398232  .0000113  1.3249 0.0079  I   -48.015     .791    -4.929     .298   .154930   .351080  -.2398440   -47.800    -4.900  
+981126 51143.00 I   .153691  .000061   .349091  .000104  I -.2412084  .0000116  1.4552 0.0081  I   -47.909     .791    -4.754     .298   .153770   .349160  -.2411550   -47.900    -4.600  
+981127 51144.00 I   .152791  .000066   .347042  .000109  I -.2427449  .0000117  1.6196 0.0072  I   -47.717     .791    -4.534     .298   .152870   .347140  -.2427040   -48.000    -4.300  
+981128 51145.00 I   .151834  .000075   .344872  .000114  I -.2444448  .0000086  1.7758 0.0075  I   -47.529     .791    -4.314     .298   .151860   .344890  -.2444480   -48.200    -4.200  
+981129 51146.00 I   .150915  .000079   .342720  .000125  I -.2462737  .0000093  1.8635 0.0057  I   -47.408     .791    -4.096     .298   .150870   .342630  -.2462790   -48.300    -4.100  
+981130 51147.00 I   .150141  .000120   .340779  .000149  I -.2481367  .0000076  1.8457 0.0058  I   -47.416     .172    -3.926     .298   .150250   .340730  -.2481490   -48.300    -4.100  
+9812 1 51148.00 I   .149184  .000115   .339098  .000146  I -.2499346  .0000068  1.7362 0.0049  I   -47.585     .172    -3.925     .298   .149440   .339290  -.2499460   -48.200    -4.100  
+9812 2 51149.00 I   .147951  .000122   .337562  .000148  I -.2515983  .0000061  1.5942 0.0046  I   -47.851     .172    -4.124     .298   .148060   .337590  -.2516030   -48.000    -4.100  
+9812 3 51150.00 I   .147004  .000127   .336146  .000141  I -.2531202  .0000061  1.4438 0.0041  I   -48.101     .172    -4.343     .298   .146980   .336120  -.2531290   -47.600    -4.300  
+9812 4 51151.00 I   .146455  .000127   .335017  .000137  I -.2544842  .0000055  1.2884 0.0044  I   -48.253     .172    -4.343     .298   .146520   .335080  -.2544910   -47.100    -4.400  
+9812 5 51152.00 I   .145863  .000128   .334151  .000152  I -.2557114  .0000063  1.1762 0.0056  I   -48.247     .175    -4.125     .298   .145960   .334300  -.2557150   -46.600    -4.400  
+9812 6 51153.00 I   .144933  .000091   .333320  .000153  I -.2568539  .0000097  1.1161 0.0056  I   -48.011     .182    -3.953     .298   .144940   .333360  -.2568680   -46.200    -4.500  
+9812 7 51154.00 I   .143695  .000088   .332385  .000137  I -.2579548  .0000093  1.0898 0.0071  I   -47.577     .184    -4.035     .298   .143730   .332270  -.2579490   -46.100    -4.500  
+9812 8 51155.00 I   .142269  .000076   .331265  .000131  I -.2590365  .0000103  1.0724 0.0072  I   -47.158     .184    -4.267     .298   .142260   .331140  -.2590450   -46.200    -4.500  
+9812 9 51156.00 I   .140845  .000072   .329776  .000124  I -.2601098  .0000109  1.0855 0.0076  I   -46.970     .184    -4.371     .298   .140850   .329720  -.2601250   -46.600    -4.400  
+981210 51157.00 I   .139654  .000065   .327889  .000116  I -.2612229  .0000113  1.1448 0.0075  I   -47.019     .184    -4.194     .298   .139700   .327880  -.2611980   -47.000    -4.200  
+981211 51158.00 I   .138670  .000065   .325829  .000111  I -.2623954  .0000103  1.1922 0.0076  I   -47.070     .184    -3.875     .298   .138680   .325810  -.2623830   -47.500    -3.900  
+981212 51159.00 I   .137880  .000067   .323810  .000082  I -.2635872  .0000102  1.1795 0.0070  I   -47.029     .187    -3.617     .298   .137890   .323810  -.2636020   -48.000    -3.700  
+981213 51160.00 I   .137344  .000046   .321899  .000062  I -.2647402  .0000095  1.1244 0.0059  I   -47.057     .109    -3.473     .298   .137390   .321940  -.2647510   -47.800    -3.600  
+981214 51161.00 I   .137118  .000048   .320090  .000090  I -.2658320  .0000061  1.0574 0.0055  I   -47.250     .150    -3.394     .298   .137150   .320130  -.2658350   -47.800    -3.500  
+981215 51162.00 I   .137191  .000056   .318439  .000094  I -.2668510  .0000054  0.9784 0.0039  I   -47.470     .133    -3.369     .298   .137240   .318410  -.2668620   -47.700    -3.400  
+981216 51163.00 I   .137482  .000059   .317021  .000101  I -.2677836  .0000047  0.8839 0.0037  I   -47.551     .133    -3.440     .298   .137450   .316970  -.2677810   -47.500    -3.500  
+981217 51164.00 I   .137993  .000065   .315738  .000105  I -.2686203  .0000051  0.7938 0.0035  I   -47.540     .133    -3.572     .298   .137950   .315730  -.2686130   -47.200    -3.600  
+981218 51165.00 I   .138867  .000071   .314494  .000109  I -.2693765  .0000051  0.7196 0.0034  I   -47.491     .133    -3.726     .298   .138870   .314470  -.2693900   -46.900    -3.800  
+981219 51166.00 I   .140070  .000066   .313420  .000137  I -.2700640  .0000045  0.6589 0.0047  I   -47.356     .140    -3.882     .298   .140080   .313340  -.2700750   -46.700    -3.900  
+981220 51167.00 I   .141362  .000066   .312536  .000132  I -.2707054  .0000078  0.6316 0.0049  I   -47.095     .791    -3.999     .298   .141430   .312550  -.2706980   -46.500    -4.000  
+981221 51168.00 I   .142330  .000074   .311541  .000129  I -.2713404  .0000087  0.6439 0.0059  I   -46.816     .791    -4.034     .298   .142400   .311680  -.2713370   -46.400    -4.000  
+981222 51169.00 I   .142795  .000070   .310097  .000128  I -.2720051  .0000088  0.6921 0.0063  I   -46.683     .791    -3.973     .298   .142860   .310170  -.2720570   -46.500    -3.800  
+981223 51170.00 I   .142988  .000067   .308356  .000126  I -.2727487  .0000090  0.8128 0.0067  I   -46.726     .791    -3.851     .298   .143050   .308250  -.2727810   -46.700    -3.700  
+981224 51171.00 I   .143090  .000066   .306710  .000126  I -.2736544  .0000100  1.0047 0.0067  I   -46.816     .791    -3.722     .298   .143130   .306690  -.2735780   -47.000    -3.600  
+981225 51172.00 I   .143080  .000068   .305222  .000119  I -.2747547  .0000099  1.1863 0.0069  I   -46.819     .791    -3.604     .298   .143110   .305330  -.2746410   -47.300    -3.400  
+981226 51173.00 I   .142815  .000082   .303796  .000074  I -.2760051  .0000096  1.3021 0.0065  I   -46.732     .791    -3.485     .298   .142820   .303820  -.2759310   -47.700    -3.400  
+981227 51174.00 I   .142282  .000099   .302444  .000087  I -.2773357  .0000085  1.3481 0.0062  I   -46.676     .101    -3.368     .298   .142260   .302430  -.2773300   -47.900    -3.400  
+981228 51175.00 I   .141721  .000102   .301186  .000088  I -.2786798  .0000078  1.3295 0.0056  I   -46.797     .384    -3.313     .131   .141690   .301160  -.2787160   -48.200    -3.400  
+981229 51176.00 I   .141207  .000101   .299985  .000105  I -.2799819  .0000074  1.2726 0.0049  I   -47.126     .384    -3.412     .131   .141210   .299910  -.2800170   -48.200    -3.600  
+981230 51177.00 I   .140598  .000106   .298726  .000102  I -.2812143  .0000060  1.1838 0.0048  I   -47.528     .384    -3.676     .131   .140690   .298710  -.2812110   -48.000    -3.800  
+981231 51178.00 I   .139675  .000101   .297292  .000109  I -.2823385  .0000062  1.0620 0.0038  I   -47.820     .384    -3.956     .131   .139760   .297390  -.2823330   -47.600    -3.900  
+99 1 1 51179.00 I   .138577  .000110   .295556  .000106  I  .7166587  .0000048  0.9492 0.0055  I   -47.878     .384    -4.035     .131   .138510   .295650   .7166370   -47.500    -4.100  
+99 1 2 51180.00 I   .137785  .000081   .293577  .000107  I  .7157494  .0000091  0.8776 0.0069  I   -47.682     .465    -3.856     .160   .137710   .293550   .7157410   -47.100    -4.300  
+99 1 3 51181.00 I   .137421  .000087   .291696  .000111  I  .7148885  .0000130  0.8510 0.0081  I   -47.278     .791    -3.620     .298   .137480   .291660   .7148720   -46.700    -4.400  
+99 1 4 51182.00 I   .137132  .000091   .290124  .000104  I  .7140379  .0000135  0.8529 0.0099  I   -46.762     .791    -3.573     .298   .137190   .290230   .7139820   -46.300    -4.300  
+99 1 5 51183.00 I   .136733  .000083   .288814  .000104  I  .7131775  .0000149  0.8702 0.0106  I   -46.302     .791    -3.734     .298   .136690   .288940   .7131390   -46.100    -4.200  
+99 1 6 51184.00 I   .136397  .000085   .287754  .000094  I  .7122751  .0000163  0.9546 0.0118  I   -46.058     .791    -3.893     .298   .136360   .287820   .7122810   -46.000    -4.000  
+99 1 7 51185.00 I   .136247  .000073   .287027  .000097  I  .7112608  .0000182  1.0616 0.0117  I   -46.047     .791    -3.871     .298   .136280   .287070   .7112410   -45.900    -3.600  
+99 1 8 51186.00 I   .136118  .000062   .286554  .000090  I  .7101793  .0000169  1.0866 0.0127  I   -46.166     .791    -3.703     .298   .136140   .286610   .7101380   -46.000    -3.400  
+99 1 9 51187.00 I   .135752  .000055   .286182  .000095  I  .7091003  .0000177  1.0737 0.0118  I   -46.356     .791    -3.545     .298   .135710   .286270   .7091040   -46.200    -3.200  
+99 110 51188.00 I   .134909  .000076   .285701  .000092  I  .7080310  .0000165  1.0635 0.0108  I   -46.640     .791    -3.475     .298   .134860   .285720   .7080490   -46.400    -3.100  
+99 111 51189.00 I   .133471  .000118   .284873  .000094  I  .7069893  .0000125  1.0051 0.0105  I   -46.985     .181    -3.444     .136   .133490   .284910   .7069560   -46.700    -3.100  
+99 112 51190.00 I   .131464  .000115   .283567  .000090  I  .7060440  .0000130  0.8769 0.0083  I   -47.259     .176    -3.366     .112   .131450   .283640   .7060060   -46.900    -3.100  
+99 113 51191.00 I   .129180  .000120   .281889  .000095  I  .7052302  .0000109  0.7646 0.0085  I   -47.387     .176    -3.229     .112   .129100   .281870   .7052330   -47.000    -3.300  
+99 114 51192.00 I   .126962  .000119   .279990  .000100  I  .7044958  .0000109  0.7091 0.0073  I   -47.455     .176    -3.108     .112   .126900   .280070   .7044610   -47.000    -3.400  
+99 115 51193.00 I   .124851  .000120   .277989  .000092  I  .7038139  .0000096  0.6477 0.0074  I   -47.512     .176    -3.137     .112   .124920   .278020   .7037620   -47.000    -3.500  
+99 116 51194.00 I   .122714  .000123   .276071  .000093  I  .7032033  .0000100  0.5777 0.0074  I   -47.503     .176    -3.349     .112   .122820   .276030   .7031910   -46.900    -3.600  
+99 117 51195.00 I   .120630  .000073   .274356  .000090  I  .7026480  .0000113  0.5392 0.0064  I   -47.340     .166    -3.626     .298   .120620   .274400   .7026250   -46.700    -3.600  
+99 118 51196.00 I   .118887  .000096   .272823  .000106  I  .7021026  .0000080  0.5676 0.0069  I   -47.048     .791    -3.800     .298   .118870   .272900   .7020740   -46.600    -3.600  
+99 119 51197.00 I   .117525  .000094   .271405  .000098  I  .7014865  .0000080  0.6747 0.0069  I   -46.777     .791    -3.794     .298   .117530   .271450   .7014750   -46.500    -3.600  
+99 120 51198.00 I   .116539  .000099   .270022  .000113  I  .7007330  .0000112  0.8423 0.0084  I   -46.640     .791    -3.676     .298   .116510   .270050   .7007330   -46.500    -3.600  
+99 121 51199.00 I   .116083  .000100   .268687  .000100  I  .6997928  .0000147  1.0373 0.0091  I   -46.604     .791    -3.568     .298   .116100   .268630   .6997900   -46.600    -3.600  
+99 122 51200.00 I   .116118  .000102   .267493  .000102  I  .6986675  .0000144  1.2050 0.0121  I   -46.568     .791    -3.522     .298   .116140   .267460   .6986490   -46.700    -3.600  
+99 123 51201.00 I   .116223  .000124   .266420  .000119  I  .6973983  .0000193  1.3262 0.0144  I   -46.497     .791    -3.504     .298   .116330   .266480   .6973720   -46.800    -3.600  
+99 124 51202.00 I   .115937  .000140   .265363  .000117  I  .6960300  .0000249  1.4029 0.0134  I   -46.465     .791    -3.479     .298   .116040   .265470   .6960030   -47.000    -3.600  
+99 125 51203.00 I   .115117  .000162   .264256  .000133  I  .6946208  .0000187  1.3948 0.0141  I   -46.584     .200    -3.478     .106   .115100   .264350   .6945500   -47.100    -3.600  
+99 126 51204.00 I   .113991  .000180   .263174  .000146  I  .6932696  .0000133  1.2991 0.0108  I   -46.893     .296    -3.577     .115   .113920   .263120   .6931850   -47.100    -3.700  
+99 127 51205.00 I   .112826  .000169   .262237  .000149  I  .6920205  .0000109  1.2098 0.0087  I   -47.300     .312    -3.820     .100   .112830   .262250   .6919960   -47.000    -3.800  
+99 128 51206.00 I   .111620  .000171   .261398  .000146  I  .6908379  .0000111  1.1568 0.0062  I   -47.630     .312    -4.136     .100   .111640   .261450   .6908520   -46.800    -3.900  
+99 129 51207.00 I   .110525  .000159   .260526  .000142  I  .6896941  .0000057  1.1422 0.0066  I   -47.705     .312    -4.271     .100   .110480   .260550   .6896900   -46.600    -4.000  
+99 130 51208.00 I   .109379  .000160   .259573  .000132  I  .6885405  .0000072  1.1665 0.0048  I   -47.600     .312    -4.157     .100   .109400   .259590   .6885240   -46.300    -4.100  
+99 131 51209.00 I   .108080  .000135   .258531  .000117  I  .6873571  .0000077  1.2029 0.0058  I   -47.418     .348    -3.950     .298   .108160   .258590   .6873410   -46.100    -4.200  
+99 2 1 51210.00 I   .106598  .000138   .257341  .000121  I  .6861313  .0000092  1.2494 0.0086  I   -47.214     .262    -3.884     .298   .106640   .257490   .6861150   -46.900    -4.200  
+99 2 2 51211.00 I   .105004  .000172   .256005  .000104  I  .6848593  .0000153  1.2930 0.0090  I   -46.983     .171    -4.080     .298   .105030   .256080   .6848460   -46.700    -4.300  
+99 2 3 51212.00 I   .103396  .000174   .254659  .000128  I  .6835503  .0000155  1.3218 0.0108  I   -46.725     .171    -4.430     .298   .103380   .254690   .6835470   -46.500    -4.500  
+99 2 4 51213.00 I   .101922  .000189   .253439  .000157  I  .6822214  .0000153  1.3336 0.0105  I   -46.559     .171    -4.657     .298   .101750   .253390   .6822120   -46.600    -4.700  
+99 2 5 51214.00 I   .100916  .000190   .252566  .000161  I  .6808880  .0000142  1.3308 0.0105  I   -46.571     .171    -4.685     .298   .100770   .252400   .6808730   -47.200    -4.400  
+99 2 6 51215.00 I   .100335  .000184   .252224  .000166  I  .6795667  .0000143  1.3073 0.0109  I   -46.749     .129    -4.628     .298   .100380   .252260   .6795660   -47.200    -4.500  
+99 2 7 51216.00 I   .099624  .000192   .252238  .000170  I  .6782837  .0000165  1.2529 0.0096  I   -47.008     .244    -4.601     .298   .099720   .252380   .6782890   -46.900    -5.000  
+99 2 8 51217.00 I   .098384  .000190   .252265  .000170  I  .6770716  .0000129  1.1665 0.0107  I   -47.202     .301    -4.608     .166   .098340   .252260   .6770550   -47.200    -4.900  
+99 2 9 51218.00 I   .096675  .000188   .252190  .000162  I  .6759507  .0000137  1.0800 0.0096  I   -47.252     .301    -4.589     .166   .096530   .252090   .6759100   -47.300    -4.600  
+99 210 51219.00 I   .094706  .000186   .251925  .000143  I  .6749074  .0000142  1.0062 0.0103  I   -47.200     .261    -4.537     .157   .094590   .252050   .6748970   -47.300    -4.400  
+99 211 51220.00 I   .092622  .000185   .251225  .000147  I  .6739383  .0000155  0.9326 0.0105  I   -47.053     .261    -4.638     .157   .092500   .251370   .6739270   -47.200    -4.300  
+99 212 51221.00 I   .090736  .000180   .250235  .000147  I  .6730319  .0000154  0.8901 0.0103  I   -46.899     .261    -4.883     .157   .090610   .250190   .6730310   -46.700    -4.200  
+99 213 51222.00 I   .089163  .000171   .249217  .000149  I  .6721348  .0000135  0.9176 0.0121  I   -46.812     .232    -5.137     .175   .089120   .249330   .6721840   -46.300    -4.400  
+99 214 51223.00 I   .087881  .000120   .248238  .000112  I  .6711759  .0000186  1.0075 0.0096  I   -46.760     .791    -5.353     .125   .087860   .248370   .6711830   -45.900    -4.400  
+99 215 51224.00 I   .086783  .000124   .247431  .000101  I  .6701095  .0000137  1.1284 0.0114  I   -46.648     .791    -5.413     .298   .086770   .247460   .6701030   -45.600    -4.500  
+99 216 51225.00 I   .085708  .000113   .246965  .000111  I  .6689138  .0000131  1.2651 0.0090  I   -46.401     .134    -5.227     .298   .085630   .246990   .6689060   -45.600    -4.700  
+99 217 51226.00 I   .084445  .000109   .246805  .000094  I  .6675690  .0000117  1.4327 0.0090  I   -46.049     .134    -4.889     .298   .084480   .246820   .6675580   -45.700    -4.700  
+99 218 51227.00 I   .082689  .000111   .246707  .000096  I  .6660476  .0000124  1.6028 0.0084  I   -45.728     .134    -4.641     .298   .082810   .246830   .6660510   -46.100    -4.700  
+99 219 51228.00 I   .080806  .000104   .246425  .000086  I  .6643896  .0000121  1.6944 0.0087  I   -45.613     .134    -4.616     .298   .080790   .246630   .6643680   -46.500    -4.700  
+99 220 51229.00 I   .079313  .000097   .245935  .000081  I  .6626922  .0000121  1.6860 0.0105  I   -45.768     .134    -4.750     .298   .079250   .246020   .6626600   -46.900    -4.700  
+99 221 51230.00 I   .078445  .000095   .245457  .000104  I  .6610367  .0000171  1.6205 0.0095  I   -46.092     .144    -4.918     .298   .078400   .245460   .6610490   -47.400    -4.700  
+99 222 51231.00 I   .078153  .000092   .245134  .000087  I  .6594679  .0000147  1.5049 0.0111  I   -46.465     .791    -5.026     .298   .078140   .245200   .6594700   -47.500    -4.700  
+99 223 51232.00 I   .078078  .000088   .244906  .000087  I  .6580462  .0000142  1.3315 0.0099  I   -46.815     .791    -5.072     .298   .078080   .244950   .6580360   -47.400    -4.900  
+99 224 51233.00 I   .077632  .000084   .244669  .000081  I  .6568065  .0000133  1.1532 0.0099  I   -47.075     .791    -5.107     .298   .077660   .244710   .6568110   -47.100    -5.000  
+99 225 51234.00 I   .076472  .000088   .244314  .000081  I  .6557266  .0000137  1.0145 0.0096  I   -47.155     .791    -5.161     .298   .076510   .244390   .6557030   -46.700    -5.200  
+99 226 51235.00 I   .074875  .000093   .243781  .000079  I  .6547633  .0000139  0.9184 0.0097  I   -47.000     .791    -5.216     .298   .074890   .243870   .6547300   -46.200    -5.400  
+99 227 51236.00 I   .073082  .000090   .243098  .000081  I  .6538706  .0000136  0.8800 0.0110  I   -46.668     .791    -5.242     .298   .073090   .243170   .6538810   -45.800    -5.500  
+99 228 51237.00 I   .071378  .000063   .242384  .000056  I  .6529831  .0000170  0.9022 0.0088  I   -46.327     .791    -5.260     .298   .071330   .242420   .6529950   -45.500    -5.700  
+99 3 1 51238.00 I   .069983  .000071   .241806  .000065  I  .6520627  .0000113  0.9350 0.0116  I   -46.124     .791    -5.336     .298   .069960   .241870   .6520380   -45.400    -5.700  
+99 3 2 51239.00 I   .068909  .000072   .241487  .000067  I  .6511207  .0000158  0.9451 0.0102  I   -46.061     .791    -5.504     .298   .068880   .241600   .6510780   -45.400    -5.700  
+99 3 3 51240.00 I   .068114  .000078   .241469  .000077  I  .6501709  .0000171  0.9601 0.0113  I   -46.029     .791    -5.696     .298   .068060   .241560   .6501610   -45.700    -5.700  
+99 3 4 51241.00 I   .067407  .000076   .241688  .000069  I  .6491932  .0000162  0.9971 0.0115  I   -46.000     .791    -5.803     .298   .067400   .241730   .6492320   -46.000    -5.700  
+99 3 5 51242.00 I   .066561  .000073   .242023  .000068  I  .6481793  .0000153  1.0262 0.0098  I   -46.084     .791    -5.799     .298   .066570   .242140   .6481930   -46.300    -5.600  
+99 3 6 51243.00 I   .065483  .000102   .242354  .000090  I  .6471690  .0000110  0.9708 0.0095  I   -46.347     .791    -5.754     .298   .065460   .242450   .6471360   -46.500    -5.600  
+99 3 7 51244.00 I   .064348  .000125   .242547  .000088  I  .6462538  .0000113  0.8672 0.0066  I   -46.648     .791    -5.748     .298   .064360   .242600   .6462500   -46.600    -5.600  
+99 3 8 51245.00 I   .063278  .000145   .242556  .000090  I  .6454247  .0000074  0.7933 0.0062  I   -46.764     .791    -5.789     .298   .063280   .242620   .6454270   -46.600    -5.700  
+99 3 9 51246.00 I   .062192  .000158   .242377  .000085  I  .6446631  .0000049  0.7318 0.0043  I   -46.623     .791    -5.839     .298   .062040   .242430   .6446490   -46.400    -5.700  
+99 310 51247.00 I   .061032  .000159   .242009  .000087  I  .6439529  .0000045  0.6951 0.0034  I   -46.311     .791    -5.857     .298   .060890   .242050   .6439460   -46.200    -5.800  
+99 311 51248.00 I   .059969  .000168   .241610  .000090  I  .6432600  .0000048  0.6971 0.0033  I   -45.903     .791    -5.815     .298   .059840   .241620   .6432690   -46.000    -5.800  
+99 312 51249.00 I   .058807  .000157   .241239  .000067  I  .6425445  .0000047  0.7417 0.0036  I   -45.456     .791    -5.726     .298   .058710   .241350   .6425460   -45.800    -5.900  
+99 313 51250.00 I   .057514  .000158   .240861  .000080  I  .6417607  .0000054  0.8340 0.0035  I   -45.108     .791    -5.693     .298   .057520   .241090   .6417470   -45.700    -6.000  
+99 314 51251.00 I   .056092  .000126   .240433  .000098  I  .6408596  .0000052  0.9770 0.0039  I   -45.028     .791    -5.818     .298   .056150   .240550   .6408520   -45.500    -6.000  
+99 315 51252.00 I   .054562  .000114   .239984  .000120  I  .6397960  .0000055  1.1522 0.0040  I   -45.165     .791    -6.058     .298   .054620   .239970   .6397940   -45.200    -6.100  
+99 316 51253.00 I   .052907  .000112   .239553  .000136  I  .6385563  .0000061  1.3240 0.0041  I   -45.220     .791    -6.234     .298   .052900   .239580   .6385570   -45.000    -6.200  
+99 317 51254.00 I   .051136  .000104   .239142  .000136  I  .6371559  .0000062  1.4721 0.0048  I   -44.996     .791    -6.233     .298   .051100   .239140   .6371530   -44.700    -6.200  
+99 318 51255.00 I   .049793  .000103   .238932  .000138  I  .6356238  .0000073  1.5850 0.0048  I   -44.655     .791    -6.111     .298   .049790   .238830   .6356030   -44.600    -6.200  
+99 319 51256.00 I   .048941  .000108   .239050  .000123  I  .6340022  .0000073  1.6489 0.0061  I   -44.570     .791    -6.095     .298   .049020   .238990   .6339940   -44.700    -6.200  
+99 320 51257.00 I   .048186  .000086   .239430  .000114  I  .6323403  .0000098  1.6697 0.0075  I   -44.878     .106    -6.311     .298   .048260   .239470   .6323600   -44.900    -6.300  
+99 321 51258.00 I   .047327  .000075   .239931  .000093  I  .6306782  .0000130  1.6442 0.0067  I   -45.346     .791    -6.634     .298   .047260   .239980   .6306790   -45.100    -6.400  
+99 322 51259.00 I   .046514  .000071   .240468  .000077  I  .6290778  .0000091  1.5409 0.0081  I   -45.660     .791    -6.856     .298   .046430   .240490   .6290540   -45.300    -6.400  
+99 323 51260.00 I   .045565  .000101   .240966  .000097  I  .6276165  .0000096  1.3774 0.0064  I   -45.699     .791    -6.887     .298   .045540   .241010   .6275870   -45.400    -6.500  
+99 324 51261.00 I   .044085  .000107   .241305  .000094  I  .6263131  .0000090  1.2436 0.0065  I   -45.537     .791    -6.777     .298   .044080   .241400   .6263030   -45.400    -6.700  
+99 325 51262.00 I   .042017  .000112   .241415  .000105  I  .6251109  .0000087  1.1648 0.0060  I   -45.278     .791    -6.621     .298   .041950   .241450   .6251350   -45.100    -6.800  
+99 326 51263.00 I   .039295  .000116   .241416  .000107  I  .6239616  .0000080  1.1516 0.0063  I   -44.959     .791    -6.551     .298   .039180   .241430   .6239720   -44.700    -7.000  
+99 327 51264.00 I   .036347  .000107   .241357  .000125  I  .6227854  .0000090  1.2054 0.0066  I   -44.580     .791    -6.601     .298   .036230   .241480   .6227770   -44.200    -7.000  
+99 328 51265.00 I   .033747  .000103   .241145  .000129  I  .6215430  .0000106  1.2825 0.0063  I   -44.207     .791    -6.741     .298   .033680   .241250   .6215430   -43.800    -7.100  
+99 329 51266.00 I   .031676  .000060   .240915  .000124  I  .6202226  .0000089  1.3530 0.0076  I   -43.975     .791    -6.930     .298   .031690   .240850   .6202090   -43.600    -7.200  
+99 330 51267.00 I   .029843  .000062   .240902  .000124  I  .6188504  .0000108  1.3831 0.0071  I   -43.952     .791    -7.096     .298   .029860   .240820   .6188340   -43.600    -7.100  
+99 331 51268.00 I   .027952  .000047   .241025  .000114  I  .6174612  .0000110  1.3986 0.0074  I   -44.055     .791    -7.163     .298   .028000   .241080   .6174570   -43.900    -7.100  
+99 4 1 51269.00 I   .025972  .000046   .240966  .000119  I  .6160514  .0000102  1.4196 0.0071  I   -44.177     .791    -7.111     .298   .026030   .241020   .6160780   -44.100    -7.100  
+99 4 2 51270.00 I   .023889  .000045   .240663  .000104  I  .6146313  .0000091  1.4128 0.0069  I   -44.333     .791    -7.012     .298   .023870   .240740   .6146500   -44.600    -7.100  
+99 4 3 51271.00 I   .021820  .000048   .240215  .000098  I  .6132443  .0000094  1.3505 0.0076  I   -44.574     .791    -6.956     .298   .021760   .240220   .6132240   -45.000    -7.100  
+99 4 4 51272.00 I   .020052  .000105   .239794  .000087  I  .6119391  .0000121  1.2617 0.0072  I   -44.827     .791    -6.963     .298   .019960   .239760   .6119380   -45.200    -7.100  
+99 4 5 51273.00 I   .018652  .000146   .239541  .000087  I  .6107150  .0000110  1.1907 0.0081  I   -44.962     .791    -7.003     .298   .018570   .239620   .6107220   -45.300    -7.200  
+99 4 6 51274.00 I   .017384  .000149   .239548  .000084  I  .6095457  .0000107  1.1558 0.0077  I   -44.989     .791    -7.069     .298   .017320   .239570   .6095590   -45.100    -7.200  
+99 4 7 51275.00 I   .015829  .000147   .239833  .000081  I  .6084018  .0000107  1.1257 0.0074  I   -45.007     .791    -7.166     .298   .015740   .239860   .6084080   -44.900    -7.100  
+99 4 8 51276.00 I   .013791  .000150   .240165  .000085  I  .6072974  .0000103  1.0862 0.0073  I   -44.959     .791    -7.240     .298   .013730   .240310   .6072760   -44.500    -7.100  
+99 4 9 51277.00 I   .012055  .000153   .240386  .000095  I  .6062115  .0000098  1.1002 0.0069  I   -44.678     .791    -7.199     .298   .011990   .240520   .6061980   -44.200    -7.100  
+99 410 51278.00 I   .010998  .000155   .240635  .000095  I  .6050749  .0000092  1.1804 0.0068  I   -44.248     .791    -7.066     .298   .010940   .240690   .6050670   -43.900    -7.100  
+99 411 51279.00 I   .010475  .000088   .241143  .000134  I  .6038335  .0000095  1.3121 0.0078  I   -44.036     .791    -7.015     .298   .010500   .241200   .6038210   -43.700    -7.000  
+99 412 51280.00 I   .010119  .000083   .241969  .000151  I  .6024322  .0000127  1.4991 0.0061  I   -44.206     .791    -7.154     .298   .010210   .242090   .6024510   -43.700    -7.000  
+99 413 51281.00 I   .009565  .000082   .242944  .000147  I  .6008268  .0000078  1.7116 0.0076  I   -44.423     .791    -7.346     .298   .009610   .243030   .6008480   -43.800    -7.100  
+99 414 51282.00 I   .008800  .000080   .243956  .000148  I  .5990280  .0000084  1.8672 0.0061  I   -44.271     .791    -7.351     .298   .008830   .243840   .5990220   -44.000    -7.200  
+99 415 51283.00 I   .008074  .000078   .245070  .000131  I  .5971222  .0000095  1.9336 0.0060  I   -43.819     .791    -7.158     .298   .008020   .244910   .5971310   -44.100    -7.300  
+99 416 51284.00 I   .007323  .000066   .246324  .000126  I  .5951849  .0000086  1.9279 0.0065  I   -43.559     .791    -7.024     .298   .007300   .246280   .5951890   -44.100    -7.500  
+99 417 51285.00 I   .006257  .000079   .247512  .000132  I  .5932963  .0000088  1.8325 0.0065  I   -43.772     .791    -7.166     .298   .006360   .247560   .5932790   -44.100    -7.600  
+99 418 51286.00 I   .004782  .000089   .248353  .000086  I  .5915380  .0000098  1.6829 0.0063  I   -44.219     .251    -7.503     .298   .004820   .248450   .5915350   -44.100    -7.800  
+99 419 51287.00 I   .003071  .000099   .248755  .000092  I  .5899323  .0000091  1.5280 0.0073  I   -44.483     .325    -7.783     .298   .002980   .248890   .5899240   -43.900    -7.900  
+99 420 51288.00 I   .001508  .000100   .248848  .000098  I  .5884751  .0000108  1.3936 0.0069  I   -44.426     .295    -7.869     .298   .001420   .248930   .5884800   -43.800    -7.800  
+99 421 51289.00 I   .000190  .000098   .248839  .000093  I  .5871312  .0000105  1.3007 0.0076  I   -44.255     .295    -7.827     .298   .000130   .248850   .5871390   -43.800    -7.700  
+99 422 51290.00 I  -.001057  .000101   .248911  .000095  I  .5858655  .0000106  1.2325 0.0077  I   -44.189     .295    -7.787     .298  -.001090   .248950   .5858710   -43.600    -7.600  
+99 423 51291.00 I  -.002271  .000103   .249270  .000091  I  .5846479  .0000112  1.2194 0.0079  I   -44.270     .295    -7.720     .298  -.002300   .249360   .5846520   -43.500    -7.500  
+99 424 51292.00 I  -.003651  .000095   .249966  .000081  I  .5834051  .0000116  1.2713 0.0088  I   -44.363     .268    -7.615     .298  -.003630   .250120   .5833930   -43.300    -7.300  
+99 425 51293.00 I  -.005339  .000082   .250849  .000052  I  .5820993  .0000136  1.3412 0.0103  I   -44.286     .126    -7.538     .298  -.005340   .251010   .5820920   -43.200    -7.300  
+99 426 51294.00 I  -.006985  .000083   .251766  .000048  I  .5807227  .0000170  1.4110 0.0108  I   -44.026     .791    -7.532     .298  -.007100   .251870   .5807220   -43.200    -7.400  
+99 427 51295.00 I  -.008027  .000082   .252694  .000051  I  .5792838  .0000169  1.4614 0.0123  I   -43.778     .791    -7.563     .298  -.008140   .252780   .5792780   -43.200    -7.400  
+99 428 51296.00 I  -.008362  .000084   .253742  .000048  I  .5778108  .0000179  1.4793 0.0112  I   -43.721     .791    -7.570     .298  -.008440   .253770   .5777930   -43.400    -7.500  
+99 429 51297.00 I  -.008472  .000088   .255001  .000051  I  .5763380  .0000146  1.4590 0.0110  I   -43.841     .791    -7.479     .298  -.008530   .255000   .5763270   -43.500    -7.500  
+99 430 51298.00 I  -.008673  .000085   .256408  .000053  I  .5749065  .0000127  1.3976 0.0087  I   -44.012     .791    -7.403     .298  -.008740   .256480   .5749090   -43.600    -7.400  
+99 5 1 51299.00 I  -.009270  .000083   .257734  .000058  I  .5735522  .0000095  1.3080 0.0078  I   -44.149     .791    -7.461     .298  -.009340   .257860   .5735560   -43.700    -7.500  
+99 5 2 51300.00 I  -.010432  .000074   .258759  .000078  I  .5722963  .0000090  1.2009 0.0063  I   -44.211     .791    -7.610     .298  -.010510   .258900   .5723070   -44.600    -7.600  
+99 5 3 51301.00 I  -.011958  .000109   .259424  .000083  I  .5711562  .0000082  1.0765 0.0060  I   -44.192     .791    -7.742     .298  -.011990   .259570   .5711640   -43.800    -7.600  
+99 5 4 51302.00 I  -.013543  .000106   .259790  .000092  I  .5701485  .0000080  0.9365 0.0058  I   -44.182     .791    -7.836     .298  -.013550   .260020   .5701520   -44.000    -7.800  
+99 5 5 51303.00 I  -.014882  .000102   .260006  .000094  I  .5692803  .0000081  0.8051 0.0057  I   -44.287     .791    -7.953     .298  -.014970   .260010   .5692870   -44.100    -7.900  
+99 5 6 51304.00 I  -.015809  .000105   .260357  .000096  I  .5685207  .0000081  0.7264 0.0059  I   -44.407     .791    -8.060     .298  -.015880   .260350   .5685460   -44.200    -7.900  
+99 5 7 51305.00 I  -.016511  .000098   .260980  .000098  I  .5677978  .0000085  0.7369 0.0056  I   -44.268     .791    -8.067     .298  -.016510   .261130   .5678080   -44.200    -7.900  
+99 5 8 51306.00 I  -.017204  .000086   .261793  .000100  I  .5670197  .0000078  0.8292 0.0056  I   -43.824     .791    -7.917     .298  -.017230   .261940   .5670120   -44.100    -8.000  
+99 5 9 51307.00 I  -.017955  .000070   .262620  .000088  I  .5661215  .0000072  0.9753 0.0053  I   -43.452     .791    -7.735     .298  -.018020   .262790   .5661330   -44.000    -8.000  
+99 510 51308.00 I  -.018562  .000059   .263437  .000080  I  .5650617  .0000071  1.1437 0.0051  I   -43.527     .791    -7.720     .298  -.018650   .263510   .5650480   -43.900    -7.900  
+99 511 51309.00 I  -.018908  .000060   .264417  .000084  I  .5638338  .0000071  1.3131 0.0050  I   -43.943     .791    -7.868     .298  -.019000   .264410   .5638010   -44.000    -7.900  
+99 512 51310.00 I  -.019144  .000058   .265626  .000080  I  .5624372  .0000071  1.4776 0.0057  I   -44.298     .791    -7.963     .298  -.019220   .265740   .5624390   -44.100    -7.900  
+99 513 51311.00 I  -.019272  .000063   .267017  .000079  I  .5608976  .0000089  1.5852 0.0051  I   -44.466     .791    -7.888     .298  -.019370   .267170   .5609190   -44.500    -7.900  
+99 514 51312.00 I  -.019234  .000065   .268489  .000053  I  .5593032  .0000074  1.5837 0.0077  I   -44.718     .791    -7.796     .298  -.019350   .268670   .5593160   -44.800    -7.900  
+99 515 51313.00 I  -.019058  .000060   .270015  .000054  I  .5577666  .0000126  1.4732 0.0075  I   -45.251     .791    -7.884     .298  -.019090   .270160   .5577750   -45.100    -7.900  
+99 516 51314.00 I  -.019061  .000071   .271612  .000076  I  .5563793  .0000131  1.2952 0.0086  I   -45.840     .791    -8.099     .298  -.019060   .271710   .5563890   -45.300    -8.000  
+99 517 51315.00 I  -.019409  .000093   .273248  .000087  I  .5551731  .0000118  1.1264 0.0086  I   -46.060     .243    -8.216     .298  -.019510   .273340   .5551900   -45.300    -8.000  
+99 518 51316.00 I  -.019872  .000093   .274767  .000100  I  .5541002  .0000112  1.0364 0.0075  I   -45.710     .245    -8.136     .298  -.019990   .274890   .5541190   -45.200    -8.000  
+99 519 51317.00 I  -.020213  .000090   .275981  .000098  I  .5530726  .0000094  1.0296 0.0076  I   -44.993     .245    -7.971     .298  -.020290   .276130   .5530890   -44.800    -8.000  
+99 520 51318.00 I  -.020417  .000091   .276839  .000102  I  .5520267  .0000104  1.0657 0.0074  I   -44.367     .245    -7.836     .298  -.020500   .276930   .5520360   -44.400    -7.900  
+99 521 51319.00 I  -.020523  .000081   .277505  .000098  I  .5509281  .0000115  1.1411 0.0080  I   -44.264     .245    -7.766     .298  -.020570   .277570   .5509490   -44.000    -7.800  
+99 522 51320.00 I  -.020535  .000083   .278185  .000095  I  .5497378  .0000121  1.2366 0.0094  I   -44.634     .245    -7.706     .298  -.020560   .278270   .5497680   -43.700    -7.600  
+99 523 51321.00 I  -.020552  .000059   .278928  .000089  I  .5484707  .0000149  1.2848 0.0102  I   -45.026     .249    -7.668     .298  -.020600   .279020   .5484720   -43.700    -7.600  
+99 524 51322.00 I  -.020768  .000045   .279657  .000087  I  .5471797  .0000164  1.2986 0.0110  I   -45.105     .791    -7.703     .298  -.020760   .279750   .5471860   -43.800    -7.500  
+99 525 51323.00 I  -.021347  .000045   .280333  .000097  I  .5458784  .0000161  1.2976 0.0116  I   -44.999     .791    -7.756     .298  -.021300   .280390   .5459010   -44.300    -7.600  
+99 526 51324.00 I  -.022250  .000048   .280942  .000097  I  .5446124  .0000165  1.2129 0.0110  I   -45.073     .791    -7.702     .298  -.022280   .280980   .5446230   -44.900    -7.600  
+99 527 51325.00 I  -.023138  .000056   .281436  .000095  I  .5434745  .0000149  1.0624 0.0111  I   -45.450     .791    -7.524     .298  -.023170   .281490   .5434710   -45.500    -7.700  
+99 528 51326.00 I  -.023752  .000054   .281994  .000095  I  .5424832  .0000149  0.9251 0.0100  I   -45.914     .791    -7.374     .298  -.023750   .282100   .5424970   -46.100    -7.800  
+99 529 51327.00 I  -.024118  .000055   .282646  .000095  I  .5416162  .0000134  0.8120 0.0112  I   -46.216     .791    -7.389     .298  -.024110   .282830   .5416450   -46.500    -7.900  
+99 530 51328.00 I  -.024418  .000078   .283263  .000065  I  .5408527  .0000167  0.7183 0.0099  I   -46.294     .791    -7.528     .298  -.024450   .283410   .5408500   -46.600    -8.000  
+99 531 51329.00 I  -.024737  .000101   .283829  .000084  I  .5401746  .0000146  0.6394 0.0114  I   -46.230     .297    -7.652     .189  -.024820   .283930   .5401390   -46.600    -8.000  
+99 6 1 51330.00 I  -.025164  .000109   .284461  .000087  I  .5395663  .0000154  0.5832 0.0112  I   -46.156     .297    -7.711     .189  -.025220   .284540   .5395750   -46.300    -8.000  
+99 6 2 51331.00 I  -.025839  .000103   .285250  .000092  I  .5389945  .0000171  0.5682 0.0111  I   -46.188     .297    -7.788     .189  -.025920   .285380   .5390250   -45.900    -8.000  
+99 6 3 51332.00 I  -.026663  .000099   .286131  .000092  I  .5384164  .0000161  0.5937 0.0118  I   -46.313     .297    -7.945     .189  -.026790   .286290   .5383850   -45.400    -7.900  
+99 6 4 51333.00 I  -.027370  .000103   .287157  .000101  I  .5377925  .0000162  0.6631 0.0109  I   -46.331     .297    -8.086     .189  -.027450   .287220   .5377750   -45.100    -7.700  
+99 6 5 51334.00 I  -.027956  .000102   .288463  .000097  I  .5370730  .0000146  0.7841 0.0109  I   -46.074     .297    -8.070     .189  -.027980   .288510   .5371050   -44.800    -7.600  
+99 6 6 51335.00 I  -.028608  .000059   .290007  .000066  I  .5362194  .0000145  0.9199 0.0093  I   -45.676     .791    -7.905     .298  -.028550   .290050   .5362130   -45.000    -7.500  
+99 6 7 51336.00 I  -.029271  .000063   .291489  .000075  I  .5352299  .0000115  1.0656 0.0094  I   -45.471     .791    -7.737     .298  -.029220   .291540   .5352240   -45.300    -7.400  
+99 6 8 51337.00 I  -.029839  .000057   .292673  .000069  I  .5340830  .0000121  1.2269 0.0085  I   -45.625     .791    -7.657     .298  -.029720   .292750   .5341110   -45.500    -7.500  
+99 6 9 51338.00 I  -.030385  .000065   .293489  .000065  I  .5327898  .0000125  1.3471 0.0101  I   -46.017     .791    -7.604     .298  -.030240   .293620   .5327920   -45.900    -7.500  
+99 610 51339.00 I  -.030823  .000065   .293963  .000053  I  .5314166  .0000162  1.3839 0.0105  I   -46.500     .791    -7.512     .298  -.030730   .294060   .5314210   -46.200    -7.500  
+99 611 51340.00 I  -.031123  .000067   .294357  .000057  I  .5300536  .0000168  1.3260 0.0113  I   -47.061     .791    -7.455     .298  -.031020   .294390   .5300850   -46.600    -7.600  
+99 612 51341.00 I  -.031394  .000085   .294908  .000056  I  .5287990  .0000158  1.1650 0.0112  I   -47.695     .791    -7.529     .298  -.031350   .295020   .5287960   -46.900    -7.600  
+99 613 51342.00 I  -.031599  .000084   .295656  .000066  I  .5277405  .0000149  0.9534 0.0104  I   -48.241     .791    -7.651     .298  -.031630   .295780   .5277610   -47.100    -7.500  
+99 614 51343.00 I  -.031741  .000097   .296616  .000091  I  .5268796  .0000135  0.7795 0.0102  I   -48.468     .207    -7.639     .298  -.031650   .296690   .5269420   -47.300    -7.500  
+99 615 51344.00 I  -.032060  .000092   .297675  .000093  I  .5261601  .0000138  0.6697 0.0089  I   -48.269     .207    -7.472     .298  -.031880   .297780   .5261980   -47.300    -7.400  
+99 616 51345.00 I  -.032633  .000091   .298575  .000103  I  .5255218  .0000116  0.6151 0.0090  I   -47.774     .207    -7.348     .298  -.032610   .298690   .5255170   -47.400    -7.200  
+99 617 51346.00 I  -.033005  .000086   .299226  .000107  I  .5249077  .0000116  0.6271 0.0078  I   -47.313     .207    -7.410     .298  -.033010   .299280   .5249170   -47.400    -7.200  
+99 618 51347.00 I  -.033064  .000088   .299759  .000105  I  .5242518  .0000104  0.6865 0.0072  I   -47.275     .207    -7.561     .298  -.032930   .299810   .5242620   -47.500    -7.100  
+99 619 51348.00 I  -.032912  .000071   .300307  .000091  I  .5235385  .0000084  0.7347 0.0062  I   -47.735     .249    -7.611     .298  -.032790   .300340   .5235260   -47.600    -7.000  
+99 620 51349.00 I  -.032633  .000047   .301003  .000058  I  .5227939  .0000069  0.7486 0.0051  I   -48.309     .791    -7.538     .298  -.032530   .301030   .5228120   -47.700    -7.000  
+99 621 51350.00 I  -.032416  .000053   .301872  .000056  I  .5220497  .0000059  0.7375 0.0048  I   -48.556     .791    -7.477     .298  -.032260   .301940   .5220930   -47.900    -7.100  
+99 622 51351.00 I  -.032382  .000054   .302784  .000051  I  .5213298  .0000068  0.6938 0.0044  I   -48.490     .791    -7.492     .298  -.032230   .302870   .5213500   -48.100    -7.200  
+99 623 51352.00 I  -.032414  .000061   .303630  .000043  I  .5206856  .0000065  0.5797 0.0055  I   -48.534     .791    -7.487     .298  -.032430   .303730   .5206850   -48.400    -7.300  
+99 624 51353.00 I  -.032237  .000061   .304449  .000044  I  .5201823  .0000086  0.4301 0.0050  I   -48.969     .791    -7.358     .298  -.032270   .304480   .5202060   -48.800    -7.400  
+99 625 51354.00 I  -.032009  .000066   .305351  .000051  I  .5198199  .0000077  0.2972 0.0079  I   -49.610     .791    -7.161     .298  -.031920   .305360   .5198390   -49.100    -7.500  
+99 626 51355.00 I  -.032072  .000073   .306343  .000046  I  .5195874  .0000133  0.1658 0.0085  I   -50.118     .241    -7.027     .298  -.031990   .306370   .5195890   -49.400    -7.600  
+99 627 51356.00 I  -.032424  .000106   .307330  .000063  I  .5194846  .0000152  0.0453 0.0087  I   -50.367     .325    -7.009     .298  -.032390   .307400   .5195020   -49.700    -7.700  
+99 628 51357.00 I  -.032681  .000110   .308172  .000069  I  .5194888  .0000112 -0.0514 0.0094  I   -50.404     .209    -7.053     .121  -.032680   .308260   .5194870   -49.900    -7.600  
+99 629 51358.00 I  -.032643  .000108   .308831  .000083  I  .5195759  .0000112 -0.1138 0.0082  I   -50.260     .202    -7.108     .105  -.032600   .308870   .5196000   -49.900    -7.500  
+99 630 51359.00 I  -.032439  .000107   .309415  .000080  I  .5196994  .0000120 -0.1253 0.0086  I   -49.982     .202    -7.181     .105  -.032410   .309380   .5197040   -49.700    -7.300  
+99 7 1 51360.00 I  -.032311  .000104   .310020  .000081  I  .5198139  .0000131 -0.0989 0.0089  I   -49.792     .202    -7.248     .105  -.032200   .309950   .5198250   -49.400    -7.100  
+99 7 2 51361.00 I  -.032153  .000104   .310745  .000081  I  .5198820  .0000132 -0.0269 0.0090  I   -49.812     .202    -7.308     .105  -.032030   .310750   .5199040   -49.100    -6.800  
+99 7 3 51362.00 I  -.031784  .000099   .311739  .000082  I  .5198545  .0000123  0.0849 0.0102  I   -49.908     .139    -7.323     .119  -.031690   .311740   .5198140   -48.900    -6.600  
+99 7 4 51363.00 I  -.031170  .000062   .313045  .000072  I  .5196951  .0000155  0.2482 0.0115  I   -49.912     .179    -7.252     .298  -.031120   .313080   .5197340   -48.700    -6.500  
+99 7 5 51364.00 I  -.030442  .000060   .314529  .000047  I  .5193487  .0000194  0.4396 0.0104  I   -49.865     .791    -7.109     .298  -.030430   .314630   .5194090   -48.900    -6.500  
+99 7 6 51365.00 I  -.029704  .000062   .315956  .000057  I  .5188292  .0000139  0.5910 0.0111  I   -49.964     .791    -6.948     .298  -.029680   .316030   .5188420   -49.600    -6.500  
+99 7 7 51366.00 I  -.028910  .000063   .317201  .000047  I  .5181918  .0000109  0.6672 0.0106  I   -50.356     .791    -6.819     .298  -.028900   .317270   .5181920   -50.100    -6.700  
+99 7 8 51367.00 I  -.027983  .000062   .318324  .000048  I  .5175273  .0000159  0.6461 0.0100  I   -51.019     .791    -6.775     .298  -.028030   .318330   .5175160   -50.800    -6.800  
+99 7 9 51368.00 I  -.027037  .000063   .319481  .000048  I  .5169244  .0000168  0.5508 0.0115  I   -51.790     .791    -6.877     .298  -.027030   .319480   .5169230   -51.400    -7.000  
+99 710 51369.00 I  -.026224  .000073   .320703  .000082  I  .5164345  .0000165  0.4291 0.0107  I   -52.452     .791    -7.115     .298  -.026250   .320820   .5164500   -51.800    -7.100  
+99 711 51370.00 I  -.025557  .000062   .321908  .000082  I  .5160590  .0000133  0.3291 0.0098  I   -52.823     .791    -7.344     .298  -.025590   .321960   .5160600   -52.100    -7.100  
+99 712 51371.00 I  -.025019  .000067   .323101  .000077  I  .5157613  .0000105  0.2742 0.0084  I   -52.850     .106    -7.379     .111  -.024980   .323000   .5157550   -52.100    -7.100  
+99 713 51372.00 I  -.024667  .000087   .324344  .000137  I  .5154946  .0000104  0.2674 0.0065  I   -52.595     .118    -7.216     .298  -.024670   .324210   .5154940   -52.000    -7.000  
+99 714 51373.00 I  -.024535  .000085   .325578  .000146  I  .5152068  .0000075  0.3196 0.0078  I   -52.187     .118    -7.077     .298  -.024560   .325580   .5152140   -51.800    -6.900  
+99 715 51374.00 I  -.024744  .000084   .326635  .000143  I  .5148456  .0000116  0.4012 0.0068  I   -51.829     .118    -7.158     .298  -.024670   .326630   .5148690   -51.700    -6.800  
+99 716 51375.00 I  -.025238  .000077   .327339  .000132  I  .5144052  .0000114  0.4803 0.0083  I   -51.750     .118    -7.340     .298  -.025120   .327330   .5144310   -51.500    -6.800  
+99 717 51376.00 I  -.025708  .000078   .327855  .000133  I  .5138917  .0000120  0.5392 0.0096  I   -52.082     .118    -7.389     .298  -.025670   .327860   .5138980   -51.600    -6.800  
+99 718 51377.00 I  -.025892  .000083   .328401  .000146  I  .5133447  .0000155  0.5443 0.0112  I   -52.651     .167    -7.243     .298  -.025820   .328410   .5133180   -51.900    -6.800  
+99 719 51378.00 I  -.025770  .000061   .329054  .000067  I  .5128171  .0000189  0.5073 0.0123  I   -53.103     .179    -7.063     .298  -.025650   .329040   .5127800   -52.300    -6.900  
+99 720 51379.00 I  -.025384  .000061   .329837  .000060  I  .5123367  .0000192  0.4506 0.0115  I   -53.303     .179    -7.029     .298  -.025280   .329830   .5123250   -52.900    -6.900  
+99 721 51380.00 I  -.024803  .000063   .330652  .000064  I  .5119213  .0000131  0.3778 0.0123  I   -53.444     .179    -7.135     .298  -.024770   .330670   .5119280   -53.500    -7.000  
+99 722 51381.00 I  -.024166  .000066   .331389  .000055  I  .5115879  .0000155  0.2847 0.0110  I   -53.672     .179    -7.196     .298  -.024170   .331470   .5115740   -54.000    -7.100  
+99 723 51382.00 I  -.023612  .000066   .332029  .000048  I  .5113555  .0000177  0.1809 0.0120  I   -53.947     .179    -7.123     .298  -.023650   .332070   .5113690   -54.400    -7.200  
+99 724 51383.00 I  -.023436  .000062   .332554  .000046  I  .5112222  .0000184  0.0887 0.0126  I   -54.161     .164    -6.993     .298  -.023290   .332570   .5112600   -54.700    -7.300  
+99 725 51384.00 I  -.023690  .000060   .332988  .000049  I  .5111749  .0000179  0.0061 0.0119  I   -54.310     .791    -6.929     .298  -.023610   .333050   .5111350   -54.800    -7.400  
+99 726 51385.00 I  -.023985  .000068   .333417  .000062  I  .5111988  .0000150 -0.0428 0.0112  I   -54.453     .169    -6.980     .187  -.023970   .333480   .5111610   -54.700    -7.400  
+99 727 51386.00 I  -.024026  .000079   .333921  .000073  I  .5112401  .0000133 -0.0304 0.0109  I   -54.561     .139    -7.092     .153  -.023980   .333980   .5112580   -54.700    -7.300  
+99 728 51387.00 I  -.023857  .000080   .334593  .000073  I  .5112419  .0000158  0.0350 0.0118  I   -54.587     .139    -7.161     .153  -.023810   .334630   .5112540   -54.700    -7.300  
+99 729 51388.00 I  -.023510  .000081   .335462  .000078  I  .5111595  .0000195  0.1321 0.0126  I   -54.624     .139    -7.130     .153  -.023400   .335520   .5111360   -54.800    -7.200  
+99 730 51389.00 I  -.023000  .000083   .336427  .000080  I  .5109628  .0000197  0.2739 0.0120  I   -54.828     .139    -7.049     .153  -.022860   .336460   .5109910   -55.000    -7.000  
+99 731 51390.00 I  -.022362  .000078   .337453  .000067  I  .5105973  .0000140  0.4589 0.0123  I   -55.189     .139    -7.009     .153  -.022280   .337490   .5106410   -55.000    -6.900  
+99 8 1 51391.00 I  -.021640  .000062   .338517  .000066  I  .5100551  .0000147  0.6137 0.0103  I   -55.507     .791    -7.042     .298  -.021570   .338540   .5100190   -55.200    -6.900  
+99 8 2 51392.00 I  -.020859  .000053   .339539  .000045  I  .5093789  .0000151  0.7414 0.0098  I   -55.621     .791    -7.098     .298  -.020830   .339690   .5093670   -55.300    -6.800  
+99 8 3 51393.00 I  -.019878  .000055   .340497  .000049  I  .5085788  .0000130  0.8496 0.0085  I   -55.601     .791    -7.107     .298  -.019910   .340720   .5086130   -55.500    -6.900  
+99 8 4 51394.00 I  -.018583  .000051   .341387  .000050  I  .5077202  .0000079  0.8365 0.0079  I   -55.652     .791    -7.071     .298  -.018720   .341550   .5077120   -55.700    -7.000  
+99 8 5 51395.00 I  -.017112  .000050   .342266  .000049  I  .5069471  .0000089  0.6996 0.0054  I   -55.892     .791    -7.068     .298  -.017190   .342340   .5069180   -55.600    -7.100  
+99 8 6 51396.00 I  -.015757  .000051   .343305  .000056  I  .5063317  .0000074  0.5306 0.0072  I   -56.243     .791    -7.181     .298  -.015620   .343440   .5063360   -55.700    -7.300  
+99 8 7 51397.00 I  -.014781  .000051   .344488  .000060  I  .5058809  .0000113  0.3766 0.0084  I   -56.493     .791    -7.401     .298  -.014670   .344680   .5058930   -55.600    -7.400  
+99 8 8 51398.00 I  -.014169  .000062   .345595  .000064  I  .5055680  .0000151  0.2542 0.0086  I   -56.471     .214    -7.598     .298  -.014300   .345840   .5055380   -55.600    -7.400  
+99 8 9 51399.00 I  -.013827  .000059   .346439  .000059  I  .5053520  .0000130  0.1932 0.0102  I   -56.196     .151    -7.622     .298  -.013890   .346680   .5053180   -55.500    -7.500  
+99 810 51400.00 I  -.013570  .000065   .347008  .000059  I  .5051508  .0000138  0.2247 0.0095  I   -55.853     .144    -7.450     .298  -.013640   .347170   .5051770   -55.500    -7.400  
+99 811 51401.00 I  -.013229  .000067   .347413  .000060  I  .5048772  .0000139  0.3325 0.0096  I   -55.639     .144    -7.233     .298  -.013310   .347500   .5048890   -55.500    -7.300  
+99 812 51402.00 I  -.012743  .000086   .347767  .000061  I  .5044897  .0000134  0.4288 0.0098  I   -55.608     .144    -7.139     .298  -.012860   .347800   .5044840   -55.500    -7.000  
+99 813 51403.00 I  -.012239  .000092   .348141  .000064  I  .5040260  .0000138  0.5060 0.0091  I   -55.764     .144    -7.231     .298  -.012470   .348220   .5040580   -55.700    -6.900  
+99 814 51404.00 I  -.011835  .000098   .348680  .000078  I  .5034709  .0000123  0.6037 0.0120  I   -56.134     .184    -7.367     .298  -.011940   .348780   .5034900   -55.800    -6.700  
+99 815 51405.00 I  -.011719  .000090   .349523  .000078  I  .5028289  .0000196  0.6707 0.0114  I   -56.653     .225    -7.390     .298  -.011760   .349630   .5028440   -56.000    -6.700  
+99 816 51406.00 I  -.011590  .000088   .350631  .000073  I  .5021460  .0000193  0.6882 0.0149  I   -57.117     .298    -7.320     .298  -.011790   .350780   .5021740   -56.300    -6.700  
+99 817 51407.00 I  -.011034  .000089   .351884  .000073  I  .5014710  .0000225  0.6500 0.0164  I   -57.303     .298    -7.253     .298  -.011220   .352100   .5014320   -56.400    -6.800  
+99 818 51408.00 I  -.010319  .000072   .353153  .000075  I  .5008619  .0000264  0.5639 0.0175  I   -57.153     .298    -7.195     .298  -.010320   .353250   .5008390   -56.700    -7.000  
+99 819 51409.00 I  -.009797  .000066   .354359  .000072  I  .5003492  .0000267  0.4597 0.0192  I   -56.878     .298    -7.109     .298  -.009880   .354600   .5003400   -57.000    -7.200  
+99 820 51410.00 I  -.009297  .000063   .355503  .000073  I  .4999451  .0000279  0.3476 0.0172  I   -56.660     .298    -6.936     .298  -.009530   .355730   .4999570   -57.100    -7.400  
+99 821 51411.00 I  -.008671  .000065   .356678  .000060  I  .4996400  .0000217  0.2776 0.0184  I   -56.613     .304    -6.727     .298  -.008740   .356860   .4997250   -57.300    -7.600  
+99 822 51412.00 I  -.008248  .000084   .357883  .000068  I  .4993711  .0000241  0.2642 0.0143  I   -56.776     .113    -6.641     .298  -.008330   .358130   .4993600   -57.300    -7.700  
+99 823 51413.00 I  -.008243  .000087   .358996  .000068  I  .4991033  .0000185  0.2759 0.0142  I   -57.052     .791    -6.790     .298  -.008270   .359220   .4990810   -57.300    -7.800  
+99 824 51414.00 I  -.008483  .000103   .359983  .000065  I  .4988082  .0000151  0.3211 0.0114  I   -57.239     .791    -7.145     .298  -.008450   .360160   .4988010   -57.200    -7.800  
+99 825 51415.00 I  -.008694  .000105   .360823  .000067  I  .4984339  .0000132  0.4479 0.0108  I   -57.195     .791    -7.533     .298  -.008880   .360930   .4984360   -57.200    -7.600  
+99 826 51416.00 I  -.008598  .000104   .361514  .000062  I  .4978839  .0000155  0.6604 0.0101  I   -57.012     .791    -7.700     .298  -.008690   .361790   .4979120   -57.000    -7.500  
+99 827 51417.00 I  -.008393  .000100   .362115  .000056  I  .4971095  .0000154  0.8839 0.0109  I   -56.883     .791    -7.596     .298  -.008440   .362310   .4971170   -56.900    -7.400  
+99 828 51418.00 I  -.008182  .000097   .362641  .000055  I  .4961321  .0000152  1.0594 0.0109  I   -56.917     .791    -7.396     .298  -.008280   .362810   .4961240   -57.000    -7.300  
+99 829 51419.00 I  -.007823  .000074   .363098  .000081  I  .4950086  .0000154  1.1810 0.0100  I   -57.065     .791    -7.297     .298  -.007930   .363190   .4950170   -56.900    -7.200  
+99 830 51420.00 I  -.007289  .000058   .363548  .000088  I  .4937904  .0000130  1.2416 0.0099  I   -57.212     .791    -7.341     .298  -.007340   .363690   .4937990   -57.000    -7.100  
+99 831 51421.00 I  -.006771  .000061   .364020  .000086  I  .4925551  .0000125  1.2133 0.0071  I   -57.308     .791    -7.416     .298  -.006770   .364210   .4925550   -57.100    -7.200  
+99 9 1 51422.00 I  -.006488  .000058   .364447  .000089  I  .4913924  .0000059  1.0994 0.0092  I   -57.404     .791    -7.409     .298  -.006490   .364650   .4913850   -57.200    -7.300  
+99 9 2 51423.00 I  -.006386  .000060   .364778  .000102  I  .4903690  .0000136  0.9476 0.0078  I   -57.570     .791    -7.321     .298  -.006430   .364930   .4903750   -57.300    -7.300  
+99 9 3 51424.00 I  -.006220  .000056   .365077  .000095  I  .4894913  .0000145  0.8137 0.0100  I   -57.793     .791    -7.237     .298  -.006220   .365200   .4895020   -57.400    -7.400  
+99 9 4 51425.00 I  -.005751  .000045   .365411  .000091  I  .4887284  .0000147  0.7195 0.0108  I   -57.956     .791    -7.211     .298  -.005850   .365580   .4887250   -56.800    -9.400  
+99 9 5 51426.00 I  -.004880  .000053   .365869  .000083  I  .4880370  .0000160  0.6711 0.0103  I   -57.921     .791    -7.216     .298  -.004960   .366010   .4880420   -56.200    -9.400  
+99 9 6 51427.00 I  -.003703  .000047   .366428  .000092  I  .4873721  .0000145  0.6650 0.0109  I   -57.637     .176    -7.174     .191  -.003810   .366600   .4873840   -55.800    -9.000  
+99 9 7 51428.00 I  -.002464  .000048   .367003  .000095  I  .4866972  .0000149  0.6883 0.0087  I   -57.197     .176    -7.042     .191  -.002570   .367190   .4867060   -55.700    -8.200  
+99 9 8 51429.00 I  -.001309  .000049   .367620  .000085  I  .4859923  .0000095  0.7211 0.0090  I   -56.771     .176    -6.869     .191  -.001440   .367610   .4860000   -55.900    -7.100  
+99 9 9 51430.00 I  -.000411  .000049   .368267  .000086  I  .4852548  .0000101  0.7545 0.0068  I   -56.497     .176    -6.756     .191  -.000550   .368300   .4852780   -56.300    -5.900  
+99 910 51431.00 I   .000061  .000059   .368863  .000087  I  .4844846  .0000096  0.7841 0.0071  I   -56.414     .176    -6.745     .191   .000030   .369070   .4845120   -56.900    -5.000  
+99 911 51432.00 I   .000383  .000065   .369397  .000095  I  .4836996  .0000101  0.7749 0.0071  I   -56.488     .176    -6.782     .191   .000460   .369580   .4837120   -57.700    -4.500  
+99 912 51433.00 I   .000863  .000058   .369937  .000070  I  .4829502  .0000105  0.7195 0.0070  I   -56.684     .791    -6.805     .298   .000800   .370170   .4829480   -58.400    -4.500  
+99 913 51434.00 I   .001600  .000058   .370608  .000102  I  .4822679  .0000098  0.6420 0.0075  I   -56.957     .791    -6.839     .298   .001620   .370730   .4822670   -58.700    -4.900  
+99 914 51435.00 I   .002414  .000058   .371490  .000109  I  .4816712  .0000108  0.5494 0.0070  I   -57.187     .791    -6.950     .298   .002450   .371490   .4816770   -58.600    -5.800  
+99 915 51436.00 I   .003040  .000055   .372432  .000110  I  .4811759  .0000100  0.4365 0.0075  I   -57.197     .791    -7.113     .298   .003040   .372620   .4811780   -58.100    -6.800  
+99 916 51437.00 I   .003503  .000049   .373142  .000117  I  .4807967  .0000103  0.3283 0.0070  I   -56.947     .791    -7.205     .298   .003510   .373430   .4807760   -57.400    -8.000  
+99 917 51438.00 I   .003889  .000044   .373599  .000101  I  .4805028  .0000099  0.2697 0.0070  I   -56.642     .791    -7.142     .298   .003850   .373750   .4804930   -56.500    -9.000  
+99 918 51439.00 I   .004184  .000053   .373979  .000097  I  .4802355  .0000094  0.2764 0.0066  I   -56.571     .791    -6.984     .298   .004070   .374060   .4802480   -55.600    -9.500  
+99 919 51440.00 I   .004345  .000057   .374430  .000069  I  .4799329  .0000086  0.3345 0.0057  I   -56.794     .243    -6.865     .298   .004280   .374520   .4799360   -54.900    -9.600  
+99 920 51441.00 I   .004513  .000061   .374913  .000065  I  .4795506  .0000066  0.4404 0.0052  I   -57.069     .248    -6.869     .298   .004470   .375080   .4795610   -54.500    -9.100  
+99 921 51442.00 I   .004887  .000065   .375305  .000061  I  .4790321  .0000059  0.6064 0.0047  I   -57.122     .248    -6.971     .298   .004820   .375420   .4790480   -54.700    -8.300  
+99 922 51443.00 I   .005344  .000059   .375576  .000057  I  .4783296  .0000068  0.7971 0.0048  I   -56.919     .248    -7.059     .298   .005330   .375590   .4783330   -55.400    -7.200  
+99 923 51444.00 I   .005458  .000058   .375778  .000063  I  .4774428  .0000076  0.9730 0.0050  I   -56.693     .248    -7.003     .298   .005560   .375850   .4774500   -56.400    -6.000  
+99 924 51445.00 I   .005210  .000048   .375829  .000070  I  .4763994  .0000073  1.1015 0.0056  I   -56.571     .248    -6.753     .298   .005150   .376030   .4764010   -57.500    -5.100  
+99 925 51446.00 I   .004983  .000048   .375759  .000060  I  .4752638  .0000081  1.1581 0.0076  I   -56.552     .251    -6.423     .298   .004960   .375910   .4752540   -58.500    -4.500  
+99 926 51447.00 I   .005018  .000048   .375787  .000092  I  .4740970  .0000134  1.1735 0.0087  I   -56.643     .791    -6.241     .298   .005000   .375900   .4741130   -59.300    -4.400  
+99 927 51448.00 I   .005262  .000043   .376086  .000100  I  .4729339  .0000153  1.1377 0.0099  I   -56.864     .791    -6.341     .298   .005160   .376130   .4729480   -59.600    -4.800  
+99 928 51449.00 I   .005564  .000051   .376625  .000102  I  .4718474  .0000147  1.0245 0.0106  I   -57.166     .791    -6.617     .298   .005430   .376760   .4718160   -59.300    -5.600  
+99 929 51450.00 I   .005826  .000056   .377307  .000101  I  .4708876  .0000147  0.9034 0.0106  I   -57.432     .791    -6.823     .298   .005810   .377530   .4708750   -58.400    -6.600  
+99 930 51451.00 I   .006099  .000059   .378097  .000099  I  .4700343  .0000154  0.8014 0.0106  I   -57.567     .791    -6.800     .298   .006070   .378340   .4700530   -57.300    -7.600  
+9910 1 51452.00 I   .006366  .000064   .378984  .000099  I  .4692888  .0000152  0.6876 0.0098  I   -57.566     .791    -6.600     .298   .006260   .379200   .4692770   -56.000    -8.500  
+9910 2 51453.00 I   .006616  .000061   .379890  .000105  I  .4686406  .0000121  0.6295 0.0091  I   -57.458     .791    -6.380     .298   .006580   .380060   .4686480   -54.800    -8.900  
+9910 3 51454.00 I   .007091  .000060   .380748  .000084  I  .4679841  .0000099  0.7086 0.0083  I   -57.245     .791    -6.243     .298   .007070   .380920   .4680100   -54.000    -8.900  
+9910 4 51455.00 I   .007760  .000092   .381484  .000097  I  .4671962  .0000115  0.8692 0.0074  I   -56.917     .313    -6.196     .298   .007850   .381800   .4672270   -52.300    -6.000  
+9910 5 51456.00 I   .008441  .000096   .382034  .000096  I  .4662445  .0000110  1.0335 0.0076  I   -56.508     .313    -6.211     .298   .008400   .382310   .4662570   -53.600    -6.000  
+9910 6 51457.00 I   .009156  .000102   .382437  .000104  I  .4651338  .0000099  1.1840 0.0076  I   -56.129     .313    -6.271     .298   .009120   .382600   .4651320   -55.400    -6.200  
+9910 7 51458.00 I   .009792  .000100   .382579  .000105  I  .4638922  .0000106  1.2876 0.0074  I   -55.896     .313    -6.304     .298   .010140   .382930   .4639000   -56.800    -6.300  
+9910 8 51459.00 I   .010453  .000102   .382483  .000108  I  .4625759  .0000109  1.3394 0.0084  I   -55.847     .313    -6.270     .298   .010710   .382670   .4625900   -57.600    -6.200  
+9910 9 51460.00 I   .011302  .000118   .382442  .000095  I  .4612270  .0000131  1.3502 0.0076  I   -55.898     .315    -6.163     .298   .011250   .382540   .4612430   -57.600    -6.200  
+991010 51461.00 I   .012327  .000087   .382576  .000077  I  .4598931  .0000106  1.3080 0.0081  I   -55.943     .791    -6.021     .298   .012270   .382700   .4599020   -57.200    -6.000  
+991011 51462.00 I   .013291  .000088   .382712  .000077  I  .4586308  .0000094  1.2064 0.0077  I   -55.939     .791    -5.932     .298   .013270   .382940   .4586320   -56.500    -6.000  
+991012 51463.00 I   .013933  .000083   .382739  .000053  I  .4574905  .0000112  1.0741 0.0066  I   -55.842     .791    -5.953     .298   .013950   .382940   .4574970   -55.800    -5.900  
+991013 51464.00 I   .014160  .000085   .382696  .000052  I  .4564699  .0000094  0.9797 0.0072  I   -55.544     .791    -6.029     .298   .014140   .382830   .4564730   -55.300    -5.900  
+991014 51465.00 I   .014011  .000090   .382620  .000050  I  .4555245  .0000092  0.9049 0.0065  I   -55.045     .791    -6.071     .298   .013960   .382790   .4555120   -54.300    -5.900  
+991015 51466.00 I   .013764  .000093   .382574  .000049  I  .4546679  .0000091  0.8066 0.0063  I   -54.573     .791    -6.029     .298   .013670   .382780   .4546620   -52.700    -5.800  
+991016 51467.00 I   .013723  .000093   .382518  .000048  I  .4539027  .0000086  0.7339 0.0064  I   -54.432     .791    -5.960     .298   .013670   .382760   .4539230   -51.100    -5.600  
+991017 51468.00 I   .013998  .000082   .382379  .000048  I  .4531777  .0000089  0.7283 0.0057  I   -54.628     .791    -5.945     .298   .014010   .382650   .4531920   -50.300    -5.600  
+991018 51469.00 I   .014481  .000090   .382265  .000051  I  .4524251  .0000074  0.7857 0.0059  I   -54.817     .791    -5.976     .298   .014510   .382440   .4524180   -50.700    -5.600  
+991019 51470.00 I   .015066  .000087   .382334  .000050  I  .4515871  .0000077  0.9008 0.0052  I   -54.729     .791    -5.994     .298   .015050   .382360   .4515800   -52.000    -5.800  
+991020 51471.00 I   .015704  .000088   .382570  .000052  I  .4505951  .0000073  1.1013 0.0053  I   -54.466     .791    -5.968     .298   .015740   .382680   .4505970   -53.800    -5.800  
+991021 51472.00 I   .016335  .000079   .382806  .000059  I  .4493644  .0000074  1.3623 0.0049  I   -54.269     .791    -5.880     .298   .016400   .382990   .4493660   -55.300    -5.900  
+991022 51473.00 I   .016759  .000077   .382957  .000053  I  .4478747  .0000066  1.6108 0.0053  I   -54.163     .791    -5.685     .298   .016850   .383150   .4478740   -56.100    -5.900  
+991023 51474.00 I   .017150  .000065   .383056  .000064  I  .4461607  .0000076  1.8056 0.0057  I   -54.007     .791    -5.385     .298   .017130   .383230   .4461670   -55.800    -5.700  
+991024 51475.00 I   .017702  .000061   .383229  .000064  I  .4442931  .0000092  1.9114 0.0057  I   -53.796     .176    -5.130     .298   .017730   .383390   .4442970   -54.900    -5.500  
+991025 51476.00 I   .018291  .000058   .383481  .000062  I  .4423756  .0000086  1.9040 0.0064  I   -53.705     .168    -5.112     .298   .018330   .383650   .4423640   -54.000    -5.300  
+991026 51477.00 I   .018863  .000064   .383621  .000064  I  .4405168  .0000089  1.8015 0.0062  I   -53.831     .168    -5.329     .298   .018810   .383830   .4405100   -53.500    -5.300  
+991027 51478.00 I   .019452  .000065   .383532  .000057  I  .4388010  .0000089  1.6137 0.0064  I   -54.074     .168    -5.541     .298   .019430   .383710   .4387960   -54.000    -5.500  
+991028 51479.00 I   .019978  .000073   .383305  .000058  I  .4372960  .0000092  1.4062 0.0065  I   -54.225     .168    -5.543     .298   .019970   .383500   .4372900   -54.500    -5.600  
+991029 51480.00 I   .020327  .000077   .383128  .000059  I  .4359633  .0000095  1.2747 0.0069  I   -54.232     .168    -5.353     .298   .020230   .383360   .4359710   -54.800    -5.700  
+991030 51481.00 I   .020502  .000073   .383004  .000063  I  .4347173  .0000102  1.2311 0.0071  I   -54.157     .159    -5.162     .298   .020480   .383150   .4347180   -54.800    -5.800  
+991031 51482.00 I   .020765  .000076   .382805  .000106  I  .4334775  .0000105  1.2582 0.0072  I   -54.049     .791    -5.102     .298   .020760   .382990   .4334870   -54.600    -5.700  
+9911 1 51483.00 I   .021271  .000102   .382449  .000116  I  .4321898  .0000102  1.3188 0.0073  I   -53.920     .791    -5.144     .298   .021280   .382720   .4321920   -54.100    -5.600  
+9911 2 51484.00 I   .021918  .000097   .381946  .000117  I  .4308357  .0000100  1.3922 0.0070  I   -53.758     .791    -5.195     .298   .021890   .382230   .4308260   -53.700    -5.300  
+9911 3 51485.00 I   .022609  .000092   .381443  .000121  I  .4294020  .0000097  1.4758 0.0070  I   -53.560     .791    -5.220     .298   .022700   .381380   .4293980   -53.500    -5.200  
+9911 4 51486.00 I   .023219  .000089   .380905  .000120  I  .4278975  .0000098  1.5193 0.0067  I   -53.385     .791    -5.213     .298   .023300   .380950   .4279030   -53.100    -5.000  
+9911 5 51487.00 I   .023757  .000086   .380406  .000121  I  .4263817  .0000091  1.5080 0.0071  I   -53.322     .791    -5.134     .298   .023730   .380400   .4263920   -52.900    -4.800  
+9911 6 51488.00 I   .024151  .000095   .380035  .000121  I  .4248891  .0000102  1.4740 0.0065  I   -53.379     .791    -4.942     .298   .024130   .380020   .4249070   -52.900    -4.600  
+9911 7 51489.00 I   .024358  .000091   .379606  .000080  I  .4234446  .0000092  1.4073 0.0069  I   -53.452     .791    -4.689     .298   .024420   .379640   .4234540   -52.900    -4.600  
+9911 8 51490.00 I   .024501  .000073   .378912  .000122  I  .4220772  .0000094  1.3323 0.0075  I   -53.430     .791    -4.523     .298   .024490   .378880   .4220890   -52.900    -4.500  
+9911 9 51491.00 I   .024892  .000085   .378025  .000132  I  .4207713  .0000119  1.2833 0.0071  I   -53.264     .791    -4.539     .298   .024840   .377880   .4207840   -52.900    -4.600  
+991110 51492.00 I   .025657  .000089   .377145  .000141  I  .4195125  .0000107  1.2288 0.0082  I   -52.931     .791    -4.668     .298   .025610   .377150   .4195120   -52.800    -4.600  
+991111 51493.00 I   .026741  .000099   .376460  .000151  I  .4183198  .0000113  1.1562 0.0074  I   -52.458     .791    -4.749     .298   .026750   .376460   .4183170   -52.500    -4.700  
+991112 51494.00 I   .027764  .000108   .376262  .000156  I  .4171923  .0000103  1.1071 0.0082  I   -52.027     .791    -4.713     .298   .027750   .376140   .4172030   -52.100    -4.700  
+991113 51495.00 I   .028474  .000113   .376609  .000153  I  .4160811  .0000120  1.1317 0.0077  I   -51.874     .791    -4.640     .298   .028440   .376550   .4160980   -51.800    -4.800  
+991114 51496.00 I   .028809  .000125   .377310  .000129  I  .4149095  .0000114  1.2147 0.0079  I   -51.991     .791    -4.607     .298   .028950   .377350   .4149150   -51.500    -4.800  
+991115 51497.00 I   .028761  .000121   .377949  .000113  I  .4136514  .0000104  1.2986 0.0076  I   -52.073     .791    -4.572     .298   .028830   .378060   .4136300   -51.300    -4.600  
+991116 51498.00 I   .028712  .000105   .378224  .000111  I  .4123109  .0000100  1.3866 0.0069  I   -51.914     .791    -4.469     .298   .028600   .378260   .4122870   -51.300    -4.500  
+991117 51499.00 I   .029229  .000100   .378250  .000100  I  .4108591  .0000092  1.5319 0.0068  I   -51.686     .791    -4.342     .298   .029220   .378260   .4108540   -51.500    -4.400  
+991118 51500.00 I   .030311  .000085   .378225  .000091  I  .4092440  .0000093  1.6865 0.0057  I   -51.638     .791    -4.256     .298   .030320   .378220   .4092360   -51.800    -4.200  
+991119 51501.00 I   .031599  .000085   .378298  .000081  I  .4075068  .0000069  1.7789 0.0058  I   -51.743     .791    -4.188     .298   .031550   .378290   .4074990   -52.100    -4.000  
+991120 51502.00 I   .032630  .000070   .378479  .000070  I  .4057008  .0000068  1.8273 0.0067  I   -51.774     .791    -4.034     .298   .032670   .378520   .4057080   -52.400    -3.800  
+991121 51503.00 I   .033130  .000073   .378618  .000055  I  .4038769  .0000116  1.8015 0.0057  I   -51.673     .791    -3.805     .298   .033180   .378650   .4038770   -52.500    -3.900  
+991122 51504.00 I   .033152  .000080   .378618  .000052  I  .4021328  .0000092  1.6708 0.0075  I   -51.620     .791    -3.685     .298   .033160   .378630   .4021340   -52.400    -3.800  
+991123 51505.00 I   .032879  .000082   .378417  .000055  I  .4005584  .0000096  1.4707 0.0066  I   -51.707     .791    -3.783     .298   .032830   .378490   .4005630   -52.100    -3.800  
+991124 51506.00 I   .032516  .000075   .378016  .000054  I  .3991948  .0000095  1.2605 0.0070  I   -51.795     .791    -3.944     .298   .032520   .378050   .3991930   -51.700    -3.900  
+991125 51507.00 I   .032158  .000081   .377578  .000064  I  .3980249  .0000102  1.0878 0.0070  I   -51.724     .791    -3.926     .298   .032220   .377580   .3980150   -51.300    -4.000  
+991126 51508.00 I   .031840  .000081   .377205  .000066  I  .3969956  .0000103  0.9858 0.0068  I   -51.496     .791    -3.714     .298   .031880   .377270   .3969800   -50.800    -4.000  
+991127 51509.00 I   .031731  .000084   .376900  .000065  I  .3960227  .0000089  0.9757 0.0076  I   -51.223     .791    -3.529     .298   .031730   .376960   .3960080   -50.500    -4.000  
+991128 51510.00 I   .032013  .000080   .376716  .000076  I  .3950159  .0000112  1.0505 0.0069  I   -51.008     .791    -3.520     .298   .031980   .376690   .3950130   -50.300    -4.000  
+991129 51511.00 I   .032624  .000068   .376739  .000074  I  .3939021  .0000105  1.1841 0.0077  I   -50.914     .791    -3.599     .298   .032620   .376770   .3939120   -50.300    -3.900  
+991130 51512.00 I   .033305  .000076   .377007  .000074  I  .3926455  .0000107  1.3244 0.0073  I   -50.926     .791    -3.611     .298   .033360   .377030   .3926480   -50.400    -3.700  
+9912 1 51513.00 I   .033866  .000080   .377511  .000070  I  .3912784  .0000101  1.3893 0.0083  I   -50.925     .791    -3.552     .298   .033910   .377480   .3912670   -50.700    -3.500  
+9912 2 51514.00 I   .034243  .000076   .378168  .000064  I  .3898971  .0000126  1.3637 0.0083  I   -50.824     .791    -3.522     .298   .034260   .378180   .3898730   -51.000    -3.400  
+9912 3 51515.00 I   .034482  .000070   .378767  .000060  I  .3885654  .0000132  1.2948 0.0081  I   -50.710     .791    -3.534     .298   .034480   .378850   .3885010   -51.300    -3.200  
+9912 4 51516.00 I   .034712  .000069   .379145  .000084  I  .3873141  .0000101  1.2061 0.0094  I   -50.740     .791    -3.497     .298   .034700   .379180   .3872730   -51.500    -3.100  
+9912 5 51517.00 I   .034806  .000064   .379274  .000088  I  .3861571  .0000133  1.1056 0.0074  I   -50.909     .791    -3.374     .298   .034850   .379330   .3861610   -51.500    -3.000  
+9912 6 51518.00 I   .034636  .000073   .379187  .000085  I  .3851032  .0000108  1.0043 0.0086  I   -51.049     .791    -3.267     .298   .034710   .379240   .3851200   -51.400    -3.000  
+9912 7 51519.00 I   .034509  .000074   .378991  .000086  I  .3841467  .0000109  0.9086 0.0072  I   -51.030     .791    -3.288     .298   .034490   .378970   .3841480   -51.200    -3.200  
+9912 8 51520.00 I   .034839  .000075   .378900  .000087  I  .3832822  .0000096  0.8243 0.0076  I   -50.877     .791    -3.408     .298   .034790   .378900   .3832790   -50.800    -3.300  
+9912 9 51521.00 I   .035705  .000077   .379108  .000089  I  .3824822  .0000105  0.7878 0.0073  I   -50.690     .791    -3.522     .298   .035700   .379150   .3824670   -50.300    -3.500  
+991210 51522.00 I   .036626  .000078   .379542  .000089  I  .3816877  .0000109  0.8082 0.0077  I   -50.579     .791    -3.565     .298   .036690   .379600   .3816790   -50.000    -3.600  
+991211 51523.00 I   .037157  .000103   .379986  .000071  I  .3808536  .0000113  0.8652 0.0076  I   -50.647     .791    -3.582     .298   .037230   .380060   .3808610   -49.800    -3.600  
+991212 51524.00 I   .037313  .000105   .380340  .000073  I  .3799565  .0000105  0.9244 0.0071  I   -50.882     .791    -3.625     .298   .037340   .380370   .3799460   -49.700    -3.600  
+991213 51525.00 I   .037388  .000116   .380650  .000091  I  .3789967  .0000086  1.0077 0.0065  I   -51.095     .791    -3.633     .298   .037370   .380650   .3790130   -49.900    -3.500  
+991214 51526.00 I   .037709  .000113   .381013  .000090  I  .3779240  .0000075  1.1421 0.0054  I   -51.109     .791    -3.518     .298   .037680   .380980   .3779550   -50.300    -3.400  
+991215 51527.00 I   .038233  .000110   .381469  .000086  I  .3767143  .0000064  1.2709 0.0052  I   -50.970     .791    -3.325     .298   .038290   .381490   .3767130   -50.700    -3.200  
+991216 51528.00 I   .038411  .000118   .381831  .000085  I  .3753863  .0000073  1.3872 0.0045  I   -50.851     .791    -3.220     .298   .038470   .381890   .3753720   -51.100    -3.100  
+991217 51529.00 I   .038307  .000124   .381983  .000091  I  .3739443  .0000063  1.4902 0.0043  I   -50.764     .791    -3.231     .298   .038300   .381920   .3739430   -51.500    -3.000  
+991218 51530.00 I   .038151  .000112   .382020  .000084  I  .3724233  .0000045  1.5412 0.0046  I   -50.606     .791    -3.214     .298   .038120   .381980   .3724420   -51.800    -3.000  
+991219 51531.00 I   .037966  .000114   .382019  .000076  I  .3708867  .0000068  1.5180 0.0047  I   -50.434     .791    -3.085     .298   .037990   .382080   .3709520   -51.800    -3.000  
+991220 51532.00 I   .037613  .000104   .381863  .000104  I  .3694136  .0000083  1.4159 0.0053  I   -50.456     .791    -2.967     .298   .037700   .381940   .3694790   -51.700    -3.000  
+991221 51533.00 I   .037076  .000112   .381420  .000101  I  .3680734  .0000082  1.2581 0.0056  I   -50.710     .791    -3.008     .298   .037210   .381360   .3680520   -51.400    -3.000  
+991222 51534.00 I   .036608  .000121   .380682  .000118  I  .3668939  .0000074  1.1106 0.0055  I   -50.948     .791    -3.136     .298   .036610   .380730   .3668750   -50.900    -3.100  
+991223 51535.00 I   .036570  .000122   .379666  .000122  I  .3658297  .0000074  1.0306 0.0052  I   -50.913     .791    -3.137     .298   .036540   .379730   .3658610   -50.400    -3.100  
+991224 51536.00 I   .037112  .000109   .378618  .000115  I  .3648087  .0000074  1.0226 0.0079  I   -50.581     .791    -2.970     .298   .037160   .378560   .3647670   -49.900    -3.100  
+991225 51537.00 I   .038004  .000116   .377845  .000126  I  .3637629  .0000139  1.0793 0.0063  I   -50.118     .791    -2.834     .298   .038060   .377680   .3636580   -49.500    -3.100  
+991226 51538.00 I   .039000  .000125   .377510  .000119  I  .3626369  .0000103  1.1755 0.0079  I   -49.742     .791    -2.876     .298   .039020   .377410   .3625410   -49.400    -2.900  
+991227 51539.00 I   .040049  .000117   .377548  .000129  I  .3614129  .0000075  1.2687 0.0068  I   -49.636     .791    -2.972     .298   .040050   .377580   .3613690   -49.400    -2.800  
+991228 51540.00 I   .041137  .000109   .377788  .000125  I  .3601152  .0000090  1.3147 0.0062  I   -49.841     .791    -2.907     .298   .041120   .377740   .3601150   -49.800    -2.700  
+991229 51541.00 I   .042052  .000088   .378106  .000111  I  .3588103  .0000099  1.2804 0.0067  I   -50.176     .791    -2.678     .298   .041930   .378160   .3588120   -50.100    -2.600  
+991230 51542.00 I   .042547  .000089   .378200  .000110  I  .3575773  .0000100  1.1776 0.0068  I   -50.392     .791    -2.493     .298   .042450   .378270   .3575540   -50.500    -2.500  
+991231 51543.00 I   .042890  .000090   .377991  .000108  I  .3564633  .0000093  1.0504 0.0070  I   -50.473     .791    -2.486     .298   .042860   .377980   .3564530   -50.800    -2.400  
+ 0 1 1 51544.00 I   .043215  .000092   .377697  .000099  I  .3554732  .0000099  0.9333 0.0076  I   -50.607     .791    -2.585     .298   .043190   .377700   .3554990   -50.900    -2.500  
+ 0 1 2 51545.00 I   .043455  .000058   .377468  .000073  I  .3545966  .0000119  0.8165 0.0074  I   -50.218     .740    -2.679     .340   .043480   .377510   .3546330   -50.800    -2.500  
+ 0 1 3 51546.00 I   .043512  .000069   .377238  .000077  I  .3538464  .0000109  0.6813 0.0083  I   -50.459     .740    -2.699     .340   .043590   .377270   .3538640   -50.600    -2.500  
+ 0 1 4 51547.00 I   .043363  .000071   .376945  .000089  I  .3532360  .0000115  0.5403 0.0107  I   -50.437     .740    -2.766     .340   .043410   .376940   .3532310   -50.300    -2.800  
+ 0 1 5 51548.00 I   .043134  .000080   .376623  .000088  I  .3527537  .0000184  0.4358 0.0106  I   -50.188     .740    -2.893     .340   .043160   .376600   .3527960   -49.900    -2.900  
+ 0 1 6 51549.00 I   .043149  .000087   .376297  .000093  I  .3523431  .0000177  0.3954 0.0127  I   -49.678     .740    -3.066     .340   .043140   .376270   .3524010   -49.400    -2.900  
+ 0 1 7 51550.00 I   .043422  .000076   .376104  .000091  I  .3519484  .0000176  0.3987 0.0121  I   -49.409     .740    -3.103     .340   .043440   .376070   .3519450   -49.000    -2.900  
+ 0 1 8 51551.00 I   .043659  .000106   .375969  .000093  I  .3515404  .0000164  0.4181 0.0125  I   -49.211     .740    -3.090     .340   .043730   .376020   .3515040   -48.500    -2.800  
+ 0 1 9 51552.00 I   .043675  .000124   .375686  .000099  I  .3510966  .0000177  0.4845 0.0103  I   -49.134     .740    -3.078     .340   .043690   .375760   .3510900   -48.400    -2.600  
+ 0 110 51553.00 I   .043604  .000128   .375254  .000094  I  .3505534  .0000125  0.6050 0.0098  I   -49.197     .740    -3.071     .340   .043590   .375250   .3505590   -48.300    -2.400  
+ 0 111 51554.00 I   .043745  .000128   .374881  .000096  I  .3498790  .0000082  0.7484 0.0073  I   -49.336     .740    -3.037     .340   .043740   .374760   .3498330   -48.600    -2.200  
+ 0 112 51555.00 I   .044251  .000132   .374731  .000092  I  .3490532  .0000074  0.9018 0.0057  I   -49.474     .740    -2.995     .340   .044220   .374640   .3489670   -48.900    -2.100  
+ 0 113 51556.00 I   .045178  .000133   .374726  .000097  I  .3480742  .0000078  1.0586 0.0052  I   -49.629     .142    -2.979     .112   .045170   .374690   .3479980   -49.400    -2.100  
+ 0 114 51557.00 I   .046378  .000128   .374757  .000097  I  .3469483  .0000073  1.1786 0.0069  I   -49.677     .142    -3.035     .112   .046490   .374730   .3469090   -49.900    -2.100  
+ 0 115 51558.00 I   .047518  .000120   .374783  .000091  I  .3457480  .0000113  1.2057 0.0077  I   -49.689     .740    -3.048     .340   .047640   .374810   .3457280   -50.400    -2.200  
+ 0 116 51559.00 I   .048487  .000104   .374789  .000081  I  .3445662  .0000136  1.1447 0.0091  I   -49.800     .740    -2.966     .340   .048510   .374820   .3445610   -50.700    -2.300  
+ 0 117 51560.00 I   .049484  .000107   .374848  .000094  I  .3434739  .0000143  1.0376 0.0102  I   -50.181     .427    -2.879     .247   .049450   .374840   .3435070   -50.900    -2.500  
+ 0 118 51561.00 I   .050671  .000095   .375099  .000098  I  .3424895  .0000152  0.9349 0.0111  I   -50.789     .427    -2.914     .247   .050690   .375130   .3425460   -50.800    -2.600  
+ 0 119 51562.00 I   .051944  .000097   .375468  .000094  I  .3415968  .0000169  0.8541 0.0109  I   -51.314     .427    -3.044     .247   .051980   .375540   .3416250   -50.500    -2.600  
+ 0 120 51563.00 I   .053115  .000095   .375682  .000096  I  .3407635  .0000157  0.8269 0.0103  I   -51.604     .220    -3.008     .340   .053160   .375720   .3407420   -50.200    -2.600  
+ 0 121 51564.00 I   .054190  .000089   .375869  .000089  I  .3399193  .0000118  0.8709 0.0106  I   -51.228     .220    -2.945     .340   .054210   .375830   .3398860   -49.700    -2.500  
+ 0 122 51565.00 I   .055094  .000090   .376229  .000090  I  .3390041  .0000143  0.9677 0.0097  I   -50.536     .220    -2.903     .340   .055110   .376210   .3389880   -49.300    -2.500  
+ 0 123 51566.00 I   .055780  .000082   .376695  .000064  I  .3379742  .0000155  1.0937 0.0100  I   -49.812     .740    -3.019     .340   .055820   .376690   .3379660   -49.100    -2.300  
+ 0 124 51567.00 I   .056228  .000090   .377100  .000063  I  .3368184  .0000141  1.2145 0.0106  I   -49.344     .740    -3.190     .340   .056210   .377100   .3368040   -49.200    -2.100  
+ 0 125 51568.00 I   .056440  .000089   .377321  .000064  I  .3355604  .0000145  1.2895 0.0105  I   -49.289     .740    -3.182     .340   .056420   .377350   .3355310   -49.500    -2.000  
+ 0 126 51569.00 I   .056569  .000089   .377302  .000061  I  .3342584  .0000155  1.3073 0.0110  I   -49.551     .740    -2.941     .340   .056570   .377290   .3342110   -49.800    -2.000  
+ 0 127 51570.00 I   .056930  .000091   .377024  .000065  I  .3329659  .0000166  1.2647 0.0109  I   -49.778     .740    -2.721     .340   .056980   .376990   .3329220   -50.400    -1.900  
+ 0 128 51571.00 I   .057279  .000089   .376691  .000067  I  .3317549  .0000153  1.1442 0.0114  I   -50.052     .740    -2.653     .340   .057300   .376720   .3317330   -50.800    -2.100  
+ 0 129 51572.00 I   .057260  .000083   .376406  .000067  I  .3306897  .0000155  0.9869 0.0123  I   -50.347     .740    -2.785     .340   .057180   .376380   .3306810   -51.100    -2.300  
+ 0 130 51573.00 I   .057053  .000088   .376159  .000060  I  .3297764  .0000193  0.8438 0.0097  I   -50.674     .740    -2.964     .340   .056980   .376130   .3297760   -51.200    -2.500  
+ 0 131 51574.00 I   .056920  .000110   .375899  .000086  I  .3289928  .0000117  0.7287 0.0110  I   -50.834     .740    -3.087     .147   .056930   .375950   .3290060   -51.000    -2.700  
+ 0 2 1 51575.00 I   .056955  .000111   .375515  .000086  I  .3283083  .0000105  0.6455 0.0073  I   -50.661     .740    -3.175     .147   .056890   .375520   .3283220   -50.700    -3.000  
+ 0 2 2 51576.00 I   .057214  .000109   .374985  .000084  I  .3276983  .0000086  0.5726 0.0069  I   -50.262     .740    -3.274     .147   .057200   .375010   .3276990   -50.300    -3.100  
+ 0 2 3 51577.00 I   .057620  .000109   .374341  .000083  I  .3271605  .0000091  0.5076 0.0062  I   -49.874     .740    -3.397     .166   .057660   .374400   .3271500   -49.800    -3.100  
+ 0 2 4 51578.00 I   .058222  .000106   .373714  .000086  I  .3266641  .0000088  0.4997 0.0066  I   -49.607     .740    -3.461     .166   .058190   .373750   .3266670   -49.400    -3.000  
+ 0 2 5 51579.00 I   .058993  .000109   .373135  .000091  I  .3261312  .0000095  0.5813 0.0076  I   -49.395     .740    -3.453     .166   .059040   .373130   .3261480   -49.000    -2.900  
+ 0 2 6 51580.00 I   .059938  .000098   .372624  .000092  I  .3254829  .0000123  0.7185 0.0070  I   -49.234     .740    -3.387     .340   .059970   .372620   .3254630   -48.600    -3.100  
+ 0 2 7 51581.00 I   .061017  .000103   .372235  .000095  I  .3246859  .0000104  0.8805 0.0080  I   -49.211     .170    -3.314     .340   .061040   .372220   .3245860   -48.400    -3.300  
+ 0 2 8 51582.00 I   .062195  .000108   .371971  .000091  I  .3237181  .0000101  1.0540 0.0082  I   -49.352     .170    -3.296     .340   .062240   .371940   .3236430   -48.300    -3.300  
+ 0 2 9 51583.00 I   .063502  .000107   .371823  .000091  I  .3225902  .0000128  1.1908 0.0085  I   -49.554     .170    -3.361     .340   .063470   .371980   .3225890   -48.700    -3.300  
+ 0 210 51584.00 I   .064771  .000109   .371580  .000086  I  .3213657  .0000136  1.2402 0.0092  I   -49.669     .140    -3.503     .340   .064720   .371710   .3213750   -49.200    -3.300  
+ 0 211 51585.00 I   .065761  .000109   .371153  .000089  I  .3201477  .0000132  1.1759 0.0087  I   -49.735     .140    -3.605     .340   .065790   .371160   .3201260   -49.800    -3.500  
+ 0 212 51586.00 I   .066202  .000092   .370649  .000093  I  .3190448  .0000108  1.0187 0.0094  I   -49.789     .140    -3.637     .340   .066240   .370670   .3190220   -50.400    -3.600  
+ 0 213 51587.00 I   .066157  .000113   .370171  .000074  I  .3181117  .0000133  0.8572 0.0077  I   -49.927     .740    -3.614     .340   .066190   .370140   .3181430   -50.800    -3.700  
+ 0 214 51588.00 I   .066000  .000151   .369722  .000093  I  .3173178  .0000109  0.7336 0.0080  I   -50.204     .131    -3.618     .340   .065970   .369710   .3173170   -51.000    -3.900  
+ 0 215 51589.00 I   .066136  .000148   .369254  .000097  I  .3166298  .0000090  0.6541 0.0065  I   -50.548     .131    -3.721     .340   .066020   .369250   .3166280   -50.800    -3.900  
+ 0 216 51590.00 I   .066699  .000155   .368822  .000099  I  .3159886  .0000069  0.6376 0.0066  I   -50.779     .131    -3.891     .340   .066650   .368810   .3159960   -50.600    -3.900  
+ 0 217 51591.00 I   .067329  .000159   .368509  .000103  I  .3153378  .0000097  0.6714 0.0059  I   -50.742     .131    -4.014     .340   .067380   .368490   .3153000   -50.200    -3.900  
+ 0 218 51592.00 I   .067939  .000142   .368369  .000104  I  .3146275  .0000096  0.7600 0.0072  I   -50.430     .131    -4.036     .340   .067960   .368340   .3145900   -49.700    -3.900  
+ 0 219 51593.00 I   .068516  .000127   .368291  .000092  I  .3137951  .0000107  0.9168 0.0102  I   -49.958     .131    -4.056     .340   .068520   .368310   .3138080   -49.400    -4.000  
+ 0 220 51594.00 I   .069027  .000069   .368146  .000076  I  .3127835  .0000179  1.1050 0.0101  I   -49.483     .740    -4.198     .340   .069000   .368190   .3128270   -49.100    -4.000  
+ 0 221 51595.00 I   .069214  .000071   .367844  .000076  I  .3116045  .0000172  1.2351 0.0124  I   -49.155     .184    -4.423     .340   .069170   .367900   .3116160   -49.000    -4.000  
+ 0 222 51596.00 I   .069035  .000071   .367356  .000078  I  .3103486  .0000173  1.2591 0.0121  I   -49.085     .184    -4.532     .340   .068980   .367350   .3103220   -49.100    -4.200  
+ 0 223 51597.00 I   .068730  .000069   .366746  .000074  I  .3091149  .0000169  1.1971 0.0119  I   -49.273     .184    -4.415     .340   .068720   .366770   .3091040   -49.300    -4.300  
+ 0 224 51598.00 I   .068361  .000058   .366024  .000067  I  .3079714  .0000164  1.0840 0.0117  I   -49.615     .184    -4.191     .340   .068400   .366050   .3079750   -49.600    -4.500  
+ 0 225 51599.00 I   .067890  .000060   .365197  .000066  I  .3069563  .0000163  0.9427 0.0125  I   -50.009     .184    -4.080     .340   .067930   .365220   .3069360   -49.900    -4.600  
+ 0 226 51600.00 I   .067390  .000070   .364288  .000067  I  .3060864  .0000188  0.8002 0.0125  I   -50.396     .184    -4.160     .340   .067440   .364350   .3060680   -50.100    -4.700  
+ 0 227 51601.00 I   .066951  .000060   .363382  .000069  I  .3053466  .0000190  0.6857 0.0119  I   -50.665     .740    -4.331     .340   .066970   .363430   .3053490   -50.200    -4.700  
+ 0 228 51602.00 I   .066617  .000067   .362566  .000102  I  .3047064  .0000147  0.5970 0.0113  I   -50.640     .740    -4.465     .340   .066620   .362640   .3046720   -50.100    -4.700  
+ 0 229 51603.00 I   .066422  .000081   .361905  .000110  I  .3041427  .0000124  0.5380 0.0096  I   -50.248     .740    -4.535     .340   .066450   .361910   .3041150   -49.800    -4.600  
+ 0 3 1 51604.00 I   .066401  .000092   .361388  .000115  I  .3036098  .0000123  0.5409 0.0111  I   -49.671     .740    -4.593     .340   .066400   .361470   .3036080   -49.500    -4.600  
+ 0 3 2 51605.00 I   .066648  .000097   .360890  .000117  I  .3030436  .0000184  0.5956 0.0099  I   -49.399     .167    -4.629     .340   .066580   .360960   .3030390   -49.200    -4.500  
+ 0 3 3 51606.00 I   .067130  .000097   .360477  .000115  I  .3024079  .0000156  0.6822 0.0118  I   -49.141     .167    -4.709     .340   .067140   .360330   .3024040   -48.900    -4.500  
+ 0 3 4 51607.00 I   .067620  .000095   .360236  .000123  I  .3016646  .0000149  0.8126 0.0122  I   -49.024     .167    -4.718     .340   .067760   .360170   .3016670   -48.700    -4.400  
+ 0 3 5 51608.00 I   .067948  .000130   .360099  .000108  I  .3007691  .0000187  0.9838 0.0105  I   -48.940     .740    -4.618     .340   .067950   .360180   .3007680   -48.600    -4.400  
+ 0 3 6 51609.00 I   .068216  .000141   .359961  .000113  I  .2996940  .0000148  1.1639 0.0120  I   -48.886     .399    -4.461     .192   .068160   .360020   .2996990   -48.700    -4.400  
+ 0 3 7 51610.00 I   .068525  .000136   .359801  .000111  I  .2984518  .0000149  1.3123 0.0080  I   -48.900     .399    -4.379     .192   .068600   .359770   .2984570   -48.900    -4.500  
+ 0 3 8 51611.00 I   .068828  .000132   .359586  .000115  I  .2970918  .0000061  1.3933 0.0088  I   -48.978     .399    -4.470     .192   .068820   .359720   .2970870   -49.200    -4.600  
+ 0 3 9 51612.00 I   .069387  .000128   .359217  .000117  I  .2956919  .0000095  1.3941 0.0062  I   -49.184     .311    -4.698     .189   .069280   .359300   .2956890   -49.500    -4.700  
+ 0 310 51613.00 I   .070402  .000135   .358866  .000109  I  .2943263  .0000107  1.3268 0.0056  I   -49.341     .311    -4.918     .189   .070270   .358810   .2943390   -49.700    -4.900  
+ 0 311 51614.00 I   .071773  .000106   .358686  .000135  I  .2930544  .0000061  1.2113 0.0064  I   -49.548     .311    -5.030     .189   .071690   .358680   .2930720   -49.900    -5.000  
+ 0 312 51615.00 I   .073083  .000099   .358599  .000130  I  .2919128  .0000070  1.0687 0.0049  I   -49.769     .740    -5.045     .340   .073060   .358650   .2919190   -49.900    -5.000  
+ 0 313 51616.00 I   .073990  .000101   .358364  .000130  I  .2909174  .0000076  0.9250 0.0052  I   -49.912     .250    -5.047     .139   .073940   .358480   .2909220   -49.800    -5.100  
+ 0 314 51617.00 I   .074480  .000102   .357854  .000132  I  .2900542  .0000077  0.8072 0.0051  I   -49.873     .250    -5.102     .139   .074370   .357960   .2900640   -49.500    -5.100  
+ 0 315 51618.00 I   .074671  .000109   .357111  .000133  I  .2892761  .0000067  0.7701 0.0052  I   -49.604     .250    -5.206     .139   .074620   .357200   .2892770   -49.200    -5.100  
+ 0 316 51619.00 I   .074758  .000099   .356241  .000136  I  .2884848  .0000071  0.8205 0.0048  I   -48.898     .740    -5.234     .340   .074740   .356290   .2884850   -48.800    -5.100  
+ 0 317 51620.00 I   .074596  .000088   .355401  .000127  I  .2876281  .0000068  0.8920 0.0074  I   -48.468     .740    -5.282     .340   .074570   .355470   .2876310   -48.400    -5.000  
+ 0 318 51621.00 I   .074245  .000098   .354616  .000106  I  .2867043  .0000130  0.9529 0.0072  I   -48.190     .740    -5.320     .340   .074260   .354760   .2867060   -48.100    -5.100  
+ 0 319 51622.00 I   .073913  .000081   .353859  .000087  I  .2857300  .0000126  0.9908 0.0087  I   -48.112     .740    -5.425     .340   .073980   .353950   .2857400   -47.900    -5.200  
+ 0 320 51623.00 I   .073747  .000083   .353172  .000077  I  .2847254  .0000117  1.0207 0.0087  I   -48.156     .178    -5.601     .340   .073730   .353110   .2847510   -47.800    -5.300  
+ 0 321 51624.00 I   .073826  .000089   .352606  .000074  I  .2836875  .0000120  1.0539 0.0084  I   -48.231     .178    -5.732     .340   .073800   .352620   .2837170   -47.900    -5.500  
+ 0 322 51625.00 I   .073986  .000082   .352100  .000070  I  .2826302  .0000121  1.0495 0.0099  I   -48.329     .178    -5.708     .340   .074010   .352130   .2826370   -48.000    -5.600  
+ 0 323 51626.00 I   .073990  .000085   .351575  .000076  I  .2816146  .0000158  0.9669 0.0098  I   -48.514     .166    -5.621     .340   .073980   .351510   .2815880   -48.100    -5.900  
+ 0 324 51627.00 I   .073949  .000092   .351028  .000071  I  .2807146  .0000154  0.8296 0.0116  I   -48.847     .166    -5.510     .340   .073900   .351030   .2807060   -48.400    -6.000  
+ 0 325 51628.00 I   .073762  .000089   .350394  .000081  I  .2799580  .0000169  0.6845 0.0140  I   -49.222     .166    -5.532     .340   .073770   .350450   .2799650   -48.500    -6.000  
+ 0 326 51629.00 I   .073420  .000085   .349596  .000075  I  .2793488  .0000234  0.5296 0.0131  I   -49.440     .740    -5.651     .340   .073380   .349600   .2793210   -48.600    -6.100  
+ 0 327 51630.00 I   .073080  .000079   .348740  .000068  I  .2788852  .0000201  0.4153 0.0155  I   -49.344     .740    -5.769     .340   .073100   .348650   .2789080   -48.500    -6.000  
+ 0 328 51631.00 I   .072772  .000078   .347935  .000072  I  .2784878  .0000204  0.3922 0.0125  I   -48.936     .740    -5.841     .340   .072860   .347950   .2785310   -48.500    -5.900  
+ 0 329 51632.00 I   .072757  .000081   .347135  .000068  I  .2780890  .0000149  0.4043 0.0125  I   -48.369     .740    -5.887     .340   .072710   .347190   .2780890   -48.300    -5.800  
+ 0 330 51633.00 I   .073380  .000091   .346415  .000064  I  .2776678  .0000145  0.4504 0.0086  I   -47.929     .740    -5.952     .104   .073380   .346410   .2776610   -47.900    -5.600  
+ 0 331 51634.00 I   .074374  .000102   .345977  .000056  I  .2771617  .0000086  0.5760 0.0087  I   -47.625     .740    -5.998     .104   .074460   .346060   .2771740   -47.600    -5.500  
+ 0 4 1 51635.00 I   .075198  .000105   .345904  .000046  I  .2764928  .0000096  0.7705 0.0078  I   -47.582     .740    -6.012     .104   .075220   .345980   .2765170   -47.200    -5.400  
+ 0 4 2 51636.00 I   .075808  .000117   .346131  .000055  I  .2756134  .0000130  0.9868 0.0074  I   -47.702     .740    -5.949     .340   .075680   .346120   .2756560   -47.000    -5.300  
+ 0 4 3 51637.00 I   .076481  .000128   .346510  .000060  I  .2745277  .0000113  1.1777 0.0088  I   -47.809     .351    -5.803     .340   .076340   .346510   .2745730   -46.900    -5.400  
+ 0 4 4 51638.00 I   .076985  .000129   .346828  .000063  I  .2732774  .0000119  1.3104 0.0080  I   -47.813     .351    -5.668     .340   .077030   .346880   .2732930   -47.000    -5.500  
+ 0 4 5 51639.00 I   .077059  .000113   .346914  .000075  I  .2719400  .0000112  1.3436 0.0082  I   -47.797     .351    -5.690     .340   .077060   .346950   .2719320   -47.200    -5.600  
+ 0 4 6 51640.00 I   .077093  .000094   .346707  .000077  I  .2706251  .0000113  1.2720 0.0077  I   -47.915     .351    -5.922     .340   .077060   .346750   .2706330   -47.500    -5.800  
+ 0 4 7 51641.00 I   .077296  .000096   .346262  .000083  I  .2694147  .0000105  1.1443 0.0077  I   -48.219     .352    -6.267     .340   .077310   .346310   .2694350   -47.800    -6.000  
+ 0 4 8 51642.00 I   .077573  .000097   .345685  .000098  I  .2683416  .0000104  1.0013 0.0077  I   -48.592     .352    -6.499     .340   .077600   .345730   .2683450   -48.100    -6.200  
+ 0 4 9 51643.00 I   .077806  .000093   .345015  .000092  I  .2674068  .0000113  0.8740 0.0065  I   -48.875     .740    -6.543     .340   .077800   .345120   .2674040   -48.200    -6.300  
+ 0 410 51644.00 I   .078238  .000094   .344262  .000097  I  .2665760  .0000078  0.7993 0.0069  I   -48.961     .392    -6.454     .340   .078180   .344290   .2665780   -48.200    -6.300  
+ 0 411 51645.00 I   .079142  .000110   .343638  .000098  I  .2657834  .0000078  0.7990 0.0056  I   -48.819     .392    -6.348     .340   .079090   .343600   .2657810   -48.100    -6.400  
+ 0 412 51646.00 I   .080330  .000125   .343365  .000091  I  .2649542  .0000080  0.8700 0.0061  I   -48.458     .392    -6.306     .340   .080370   .343360   .2649460   -47.900    -6.400  
+ 0 413 51647.00 I   .081333  .000126   .343380  .000089  I  .2640252  .0000093  0.9957 0.0061  I   -48.173     .625    -6.329     .340   .081460   .343390   .2640320   -47.600    -6.300  
+ 0 414 51648.00 I   .081906  .000121   .343576  .000084  I  .2629513  .0000091  1.1558 0.0065  I   -47.670     .625    -6.383     .340   .081990   .343570   .2629550   -47.400    -6.200  
+ 0 415 51649.00 I   .082086  .000111   .343849  .000077  I  .2617145  .0000092  1.3133 0.0074  I   -47.392     .625    -6.431     .340   .082090   .343870   .2617140   -47.400    -6.100  
+ 0 416 51650.00 I   .082136  .000096   .344069  .000100  I  .2603384  .0000117  1.4297 0.0071  I   -47.442     .740    -6.469     .340   .082110   .344100   .2603470   -47.500    -6.100  
+ 0 417 51651.00 I   .082226  .000127   .344150  .000109  I  .2588795  .0000109  1.4728 0.0085  I   -47.718     .379    -6.494     .340   .082280   .344150   .2588640   -47.600    -6.100  
+ 0 418 51652.00 I   .082276  .000131   .344044  .000108  I  .2574198  .0000122  1.4348 0.0083  I   -47.987     .379    -6.477     .340   .082430   .344110   .2574370   -48.000    -6.100  
+ 0 419 51653.00 I   .082219  .000131   .343715  .000108  I  .2560309  .0000124  1.3337 0.0087  I   -48.118     .379    -6.400     .340   .082280   .343740   .2560440   -48.300    -6.200  
+ 0 420 51654.00 I   .082294  .000139   .343161  .000107  I  .2547592  .0000124  1.2120 0.0087  I   -48.421     .149    -6.275     .340   .082350   .343120   .2547520   -48.600    -6.400  
+ 0 421 51655.00 I   .082424  .000141   .342461  .000101  I  .2536047  .0000123  1.0968 0.0086  I   -48.584     .149    -6.241     .340   .082580   .342510   .2536740   -48.800    -6.600  
+ 0 422 51656.00 I   .082541  .000134   .341696  .000093  I  .2525701  .0000118  0.9681 0.0097  I   -48.813     .149    -6.308     .340   .082560   .341730   .2526650   -48.800    -6.700  
+ 0 423 51657.00 I   .082838  .000127   .340923  .000060  I  .2516714  .0000149  0.8306 0.0072  I   -48.944     .740    -6.435     .340   .082870   .340860   .2517210   -48.800    -6.900  
+ 0 424 51658.00 I   .083396  .000099   .340229  .000058  I  .2509021  .0000083  0.7134 0.0093  I   -48.882     .179    -6.565     .111   .083440   .340130   .2509180   -48.500    -6.900  
+ 0 425 51659.00 I   .084129  .000080   .339694  .000057  I  .2502312  .0000111  0.6365 0.0073  I   -48.658     .179    -6.675     .111   .084160   .339640   .2502580   -48.200    -6.900  
+ 0 426 51660.00 I   .084942  .000061   .339305  .000058  I  .2496080  .0000119  0.6229 0.0090  I   -48.308     .179    -6.752     .111   .084990   .339270   .2496080   -47.900    -6.700  
+ 0 427 51661.00 I   .085776  .000055   .338979  .000069  I  .2489548  .0000142  0.7012 0.0093  I   -47.776     .109    -6.773     .118   .085840   .338870   .2489720   -47.600    -6.400  
+ 0 428 51662.00 I   .086596  .000053   .338677  .000074  I  .2481768  .0000144  0.8660 0.0100  I   -47.357     .109    -6.727     .118   .086640   .338570   .2481970   -47.400    -6.200  
+ 0 429 51663.00 I   .087438  .000097   .338335  .000089  I  .2472104  .0000142  1.0679 0.0124  I   -47.247     .109    -6.683     .118   .087450   .338270   .2471880   -47.300    -6.100  
+ 0 430 51664.00 I   .088310  .000091   .337912  .000088  I  .2460469  .0000202  1.2521 0.0103  I   -47.527     .740    -6.672     .340   .088320   .337850   .2460670   -47.200    -5.900  
+ 0 5 1 51665.00 I   .089074  .000090   .337501  .000097  I  .2447165  .0000148  1.4056 0.0133  I   -47.919     .740    -6.638     .186   .089060   .337400   .2447320   -47.300    -5.900  
+ 0 5 2 51666.00 I   .089646  .000093   .337119  .000103  I  .2432550  .0000172  1.5014 0.0105  I   -48.105     .740    -6.532     .186   .089650   .337110   .2432530   -47.500    -5.900  
+ 0 5 3 51667.00 I   .089943  .000096   .336653  .000107  I  .2417404  .0000149  1.5171 0.0112  I   -48.118     .740    -6.439     .186   .089980   .336700   .2417460   -47.800    -6.200  
+ 0 5 4 51668.00 I   .089876  .000100   .335967  .000104  I  .2402498  .0000143  1.4458 0.0101  I   -48.260     .740    -6.541     .206   .089850   .336090   .2402460   -48.100    -6.300  
+ 0 5 5 51669.00 I   .089512  .000061   .335028  .000093  I  .2388797  .0000137  1.2819 0.0097  I   -48.636     .740    -6.820     .206   .089490   .334990   .2388380   -48.500    -6.500  
+ 0 5 6 51670.00 I   .089040  .000060   .333967  .000084  I  .2376950  .0000131  1.0912 0.0105  I   -49.026     .740    -7.107     .206   .089050   .333810   .2376060   -48.600    -6.800  
+ 0 5 7 51671.00 I   .088496  .000057   .332907  .000085  I  .2366841  .0000159  0.9403 0.0087  I   -49.157     .740    -7.191     .340   .088530   .332910   .2365990   -48.500    -6.900  
+ 0 5 8 51672.00 I   .087926  .000071   .331857  .000107  I  .2357910  .0000115  0.8598 0.0098  I   -49.011     .740    -7.051     .109   .087960   .331860   .2357550   -48.600    -7.000  
+ 0 5 9 51673.00 I   .087469  .000061   .330886  .000093  I  .2349382  .0000113  0.8583 0.0082  I   -48.770     .740    -6.842     .109   .087530   .330830   .2349480   -48.500    -6.900  
+ 0 510 51674.00 I   .087221  .000051   .330148  .000083  I  .2340443  .0000118  0.9472 0.0091  I   -48.563     .740    -6.721     .109   .087260   .330140   .2340480   -48.400    -6.800  
+ 0 511 51675.00 I   .087388  .000050   .329677  .000082  I  .2330322  .0000143  1.0710 0.0093  I   -48.366     .740    -6.722     .109   .087380   .329760   .2330110   -48.400    -6.700  
+ 0 512 51676.00 I   .088025  .000069   .329575  .000088  I  .2319108  .0000144  1.1694 0.0121  I   -48.124     .740    -6.789     .109   .088070   .329580   .2318930   -48.400    -6.400  
+ 0 513 51677.00 I   .088812  .000067   .329617  .000091  I  .2307012  .0000196  1.2444 0.0157  I   -47.896     .740    -6.855     .109   .088870   .329580   .2307180   -48.500    -6.300  
+ 0 514 51678.00 I   .089475  .000070   .329668  .000078  I  .2294361  .0000278  1.2772 0.0170  I   -47.849     .740    -6.865     .340   .089470   .329690   .2294520   -48.600    -6.200  
+ 0 515 51679.00 I   .090028  .000086   .329588  .000044  I  .2281687  .0000278  1.2444 0.0200  I   -48.073     .623    -6.784     .182   .090050   .329610   .2281330   -48.800    -6.100  
+ 0 516 51680.00 I   .090488  .000085   .329187  .000044  I  .2269698  .0000288  1.1440 0.0194  I   -48.453     .623    -6.617     .182   .090540   .329170   .2269170   -49.000    -6.200  
+ 0 517 51681.00 I   .090846  .000086   .328477  .000042  I  .2258826  .0000272  1.0377 0.0195  I   -48.775     .623    -6.439     .182   .090870   .328450   .2258710   -49.200    -6.300  
+ 0 518 51682.00 I   .091393  .000068   .327755  .000039  I  .2248928  .0000262  0.9362 0.0159  I   -49.026     .535    -6.400     .146   .091390   .327710   .2248910   -49.300    -6.600  
+ 0 519 51683.00 I   .092217  .000068   .327154  .000038  I  .2240194  .0000163  0.8072 0.0136  I   -49.101     .535    -6.484     .146   .092280   .327100   .2239890   -49.400    -6.800  
+ 0 520 51684.00 I   .093187  .000087   .326636  .000045  I  .2232798  .0000074  0.6739 0.0086  I   -49.147     .535    -6.658     .146   .093220   .326590   .2232440   -49.300    -7.000  
+ 0 521 51685.00 I   .093994  .000065   .326187  .000045  I  .2226631  .0000056  0.5661 0.0046  I   -49.147     .740    -6.808     .340   .094040   .326130   .2226540   -49.000    -7.100  
+ 0 522 51686.00 I   .094457  .000067   .325675  .000053  I  .2221314  .0000056  0.5070 0.0044  I   -49.124     .184    -6.895     .340   .094520   .325650   .2221350   -48.800    -7.200  
+ 0 523 51687.00 I   .094541  .000067   .324805  .000055  I  .2216301  .0000069  0.5048 0.0046  I   -49.145     .184    -6.969     .340   .094640   .324730   .2216290   -48.400    -7.200  
+ 0 524 51688.00 I   .094765  .000064   .323507  .000053  I  .2211125  .0000074  0.5306 0.0052  I   -49.144     .184    -7.041     .340   .094850   .323410   .2210940   -48.000    -7.100  
+ 0 525 51689.00 I   .095304  .000062   .322391  .000053  I  .2205648  .0000077  0.5689 0.0053  I   -48.814     .740    -6.995     .340   .095340   .322360   .2205220   -47.800    -6.800  
+ 0 526 51690.00 I   .096112  .000055   .321571  .000055  I  .2199668  .0000076  0.6310 0.0074  I   -48.451     .740    -6.855     .340   .096140   .321540   .2199430   -47.800    -6.600  
+ 0 527 51691.00 I   .097312  .000036   .320835  .000054  I  .2192941  .0000127  0.7193 0.0109  I   -48.306     .740    -6.697     .340   .097340   .320780   .2193000   -47.900    -6.500  
+ 0 528 51692.00 I   .098918  .000035   .320093  .000035  I  .2185232  .0000205  0.8225 0.0108  I   -48.673     .740    -6.666     .340   .098910   .320120   .2185070   -48.200    -6.400  
+ 0 529 51693.00 I   .100678  .000044   .319359  .000037  I  .2176552  .0000174  0.9072 0.0135  I   -49.336     .542    -6.731     .142   .100690   .319400   .2176300   -48.500    -6.300  
+ 0 530 51694.00 I   .102331  .000055   .318691  .000051  I  .2167245  .0000175  0.9446 0.0127  I   -49.820     .542    -6.713     .142   .102300   .318690   .2167470   -48.900    -6.400  
+ 0 531 51695.00 I   .103800  .000060   .318055  .000053  I  .2157888  .0000185  0.9134 0.0126  I   -50.005     .542    -6.561     .142   .103790   .318060   .2157980   -49.200    -6.600  
+ 0 6 1 51696.00 I   .105087  .000063   .317341  .000055  I  .2149209  .0000180  0.8126 0.0134  I   -49.884     .207    -6.457     .140   .105050   .317360   .2149210   -49.600    -6.700  
+ 0 6 2 51697.00 I   .106400  .000062   .316492  .000053  I  .2141761  .0000193  0.6743 0.0115  I   -50.305     .207    -6.564     .140   .106310   .316510   .2141970   -49.700    -6.800  
+ 0 6 3 51698.00 I   .107684  .000054   .315507  .000055  I  .2135716  .0000142  0.5381 0.0119  I   -50.689     .207    -6.793     .140   .107760   .315500   .2135470   -49.800    -6.800  
+ 0 6 4 51699.00 I   .108770  .000048   .314486  .000074  I  .2130846  .0000139  0.4479 0.0087  I   -50.700     .740    -6.902     .340   .108820   .314500   .2130840   -49.700    -6.800  
+ 0 6 5 51700.00 I   .109818  .000055   .313510  .000073  I  .2126473  .0000101  0.4432 0.0083  I   -50.344     .740    -6.805     .143   .109820   .313540   .2126560   -49.600    -6.800  
+ 0 6 6 51701.00 I   .110928  .000054   .312639  .000077  I  .2121691  .0000092  0.5257 0.0080  I   -49.961     .740    -6.645     .143   .110970   .312620   .2121930   -49.600    -6.600  
+ 0 6 7 51702.00 I   .111937  .000063   .311860  .000087  I  .2115752  .0000123  0.6704 0.0080  I   -49.858     .740    -6.578     .143   .111870   .311870   .2115840   -49.600    -6.400  
+ 0 6 8 51703.00 I   .112461  .000059   .311070  .000088  I  .2108262  .0000131  0.8215 0.0091  I   -50.050     .740    -6.599     .143   .112450   .311120   .2108200   -49.800    -6.300  
+ 0 6 9 51704.00 I   .112535  .000063   .310143  .000085  I  .2099522  .0000134  0.9125 0.0092  I   -50.304     .740    -6.626     .143   .112510   .310180   .2099400   -49.900    -6.200  
+ 0 610 51705.00 I   .112490  .000070   .308914  .000079  I  .2090299  .0000128  0.9172 0.0121  I   -50.397     .740    -6.629     .143   .112470   .308970   .2090050   -50.200    -6.000  
+ 0 611 51706.00 I   .112729  .000070   .307475  .000066  I  .2081422  .0000202  0.8485 0.0125  I   -50.353     .740    -6.624     .340   .112680   .307450   .2081520   -50.600    -6.000  
+ 0 612 51707.00 I   .113194  .000112   .306099  .000065  I  .2073506  .0000214  0.7266 0.0151  I   -50.414     .413    -6.581     .114   .113130   .306060   .2073440   -50.900    -6.100  
+ 0 613 51708.00 I   .113500  .000106   .304864  .000059  I  .2067048  .0000225  0.5574 0.0192  I   -50.757     .413    -6.452     .114   .113470   .304900   .2067100   -51.300    -6.200  
+ 0 614 51709.00 I   .113577  .000106   .303639  .000060  I  .2062475  .0000320  0.3531 0.0203  I   -51.267     .413    -6.293     .114   .113620   .303680   .2062480   -51.600    -6.300  
+ 0 615 51710.00 I   .113645  .000101   .302472  .000061  I  .2060009  .0000339  0.1417 0.0243  I   -51.963     .740    -6.236     .125   .113600   .302480   .2059850   -51.800    -6.500  
+ 0 616 51711.00 I   .113161  .000093   .301332  .000067  I  .2059520  .0000367 -0.0336 0.0233  I   -52.091     .740    -6.379     .125   .113100   .301300   .2059900   -51.900    -6.700  
+ 0 617 51712.00 I   .112189  .000089   .300267  .000086  I  .2060449  .0000319 -0.1394 0.0229  I   -52.031     .740    -6.599     .125   .112160   .300250   .2060860   -51.900    -6.700  
+ 0 618 51713.00 I   .110999  .000056   .299135  .000090  I  .2062115  .0000274 -0.1873 0.0196  I   -51.918     .740    -6.712     .340   .111010   .299210   .2061740   -52.000    -6.800  
+ 0 619 51714.00 I   .109831  .000049   .297643  .000093  I  .2064062  .0000227 -0.1952 0.0176  I   -51.892     .740    -6.675     .212   .109870   .297710   .2063480   -51.900    -6.700  
+ 0 620 51715.00 I   .108880  .000040   .295971  .000092  I  .2065859  .0000221 -0.1552 0.0145  I   -52.050     .740    -6.612     .212   .108870   .296020   .2065770   -52.000    -6.600  
+ 0 621 51716.00 I   .108124  .000037   .294316  .000092  I  .2066983  .0000182 -0.0604 0.0141  I   -52.318     .740    -6.615     .212   .108140   .294340   .2067080   -52.100    -6.400  
+ 0 622 51717.00 I   .107741  .000036   .292556  .000087  I  .2067001  .0000176  0.0543 0.0115  I   -52.368     .740    -6.578     .180   .107770   .292570   .2066590   -52.300    -6.200  
+ 0 623 51718.00 I   .107635  .000032   .290771  .000074  I  .2065948  .0000142  0.1537 0.0117  I   -52.267     .740    -6.453     .180   .107700   .290810   .2065550   -52.600    -6.000  
+ 0 624 51719.00 I   .107822  .000023   .289158  .000053  I  .2063949  .0000153  0.2466 0.0131  I   -52.198     .740    -6.262     .180   .107890   .289170   .2064170   -52.900    -5.900  
+ 0 625 51720.00 I   .108185  .000024   .287884  .000045  I  .2061000  .0000220  0.3445 0.0102  I   -52.525     .740    -6.186     .340   .108190   .287890   .2061060   -53.300    -5.800  
+ 0 626 51721.00 I   .108554  .000038   .286664  .000076  I  .2057138  .0000135  0.4187 0.0130  I   -53.213     .114    -6.273     .152   .108550   .286670   .2056990   -53.600    -5.800  
+ 0 627 51722.00 I   .108995  .000037   .285324  .000078  I  .2052783  .0000137  0.4457 0.0103  I   -53.860     .114    -6.351     .152   .108990   .285330   .2053220   -53.900    -5.900  
+ 0 628 51723.00 I   .109531  .000041   .283989  .000077  I  .2048415  .0000156  0.4155 0.0103  I   -54.227     .114    -6.282     .152   .109520   .283980   .2048650   -54.000    -6.000  
+ 0 629 51724.00 I   .110044  .000040   .282821  .000076  I  .2044716  .0000155  0.3125 0.0111  I   -54.412     .740    -6.220     .207   .110020   .282840   .2044680   -54.000    -6.200  
+ 0 630 51725.00 I   .110221  .000041   .281535  .000076  I  .2042304  .0000157  0.1678 0.0102  I   -54.720     .740    -6.243     .207   .110210   .281560   .2042410   -54.000    -6.400  
+ 0 7 1 51726.00 I   .110169  .000046   .280039  .000081  I  .2041293  .0000133  0.0432 0.0142  I   -54.966     .740    -6.397     .207   .110140   .280000   .2040530   -53.800    -6.500  
+ 0 7 2 51727.00 I   .109967  .000048   .278617  .000054  I  .2041166  .0000236  0.0009 0.0139  I   -54.896     .740    -6.493     .340   .109940   .278620   .2039720   -53.600    -6.600  
+ 0 7 3 51728.00 I   .109546  .000047   .277387  .000044  I  .2040917  .0000244  0.0662 0.0142  I   -54.496     .740    -6.450     .340   .109520   .277410   .2039550   -53.700    -6.700  
+ 0 7 4 51729.00 I   .108963  .000068   .276322  .000064  I  .2039568  .0000157  0.2139 0.0143  I   -54.053     .740    -6.399     .340   .108970   .276340   .2038820   -53.700    -6.700  
+ 0 7 5 51730.00 I   .108195  .000068   .275448  .000064  I  .2036533  .0000151  0.3933 0.0111  I   -53.906     .740    -6.453     .340   .108210   .275460   .2036680   -53.800    -6.600  
+ 0 7 6 51731.00 I   .107386  .000068   .274682  .000062  I  .2031755  .0000157  0.5566 0.0109  I   -54.247     .740    -6.583     .340   .107390   .274680   .2031930   -54.200    -6.500  
+ 0 7 7 51732.00 I   .107012  .000068   .273910  .000062  I  .2025619  .0000157  0.6544 0.0111  I   -54.786     .740    -6.527     .340   .106990   .273890   .2025540   -54.500    -6.400  
+ 0 7 8 51733.00 I   .106830  .000058   .273235  .000070  I  .2018964  .0000156  0.6630 0.0094  I   -55.211     .740    -6.382     .340   .106810   .273230   .2018930   -55.000    -6.400  
+ 0 7 9 51734.00 I   .106345  .000056   .272676  .000075  I  .2012574  .0000105  0.6070 0.0090  I   -55.289     .740    -6.317     .340   .106320   .272700   .2012680   -55.300    -6.300  
+ 0 710 51735.00 I   .105472  .000041   .272081  .000062  I  .2006965  .0000088  0.5085 0.0068  I   -55.187     .740    -6.389     .340   .105450   .272070   .2007080   -55.600    -6.300  
+ 0 711 51736.00 I   .104471  .000049   .271366  .000070  I  .2002445  .0000087  0.3976 0.0068  I   -55.276     .740    -6.480     .340   .104450   .271300   .2002440   -55.700    -6.400  
+ 0 712 51737.00 I   .103658  .000051   .270383  .000072  I  .1999051  .0000103  0.2753 0.0069  I   -55.675     .740    -6.494     .340   .103620   .270380   .1999000   -55.700    -6.500  
+ 0 713 51738.00 I   .102881  .000054   .269207  .000075  I  .1996952  .0000106  0.1490 0.0075  I   -56.100     .740    -6.514     .340   .102910   .269240   .1996870   -55.700    -6.600  
+ 0 714 51739.00 I   .102127  .000065   .268078  .000082  I  .1996037  .0000108  0.0330 0.0081  I   -56.296     .740    -6.606     .340   .102150   .268100   .1996080   -55.500    -6.700  
+ 0 715 51740.00 I   .101213  .000073   .266972  .000078  I  .1996296  .0000123 -0.0841 0.0086  I   -56.234     .740    -6.749     .340   .101200   .267020   .1996010   -55.400    -6.700  
+ 0 716 51741.00 I   .100163  .000071   .265843  .000081  I  .1997601  .0000133 -0.1659 0.0088  I   -56.104     .740    -6.790     .340   .100110   .265850   .1997040   -55.300    -6.800  
+ 0 717 51742.00 I   .099235  .000060   .264837  .000089  I  .1999437  .0000126 -0.1947 0.0088  I   -56.067     .257    -6.666     .340   .099230   .264830   .1999110   -55.500    -6.700  
+ 0 718 51743.00 I   .098285  .000059   .263948  .000091  I  .2001382  .0000114 -0.1896 0.0079  I   -56.188     .257    -6.490     .340   .098270   .263900   .2001430   -55.700    -6.500  
+ 0 719 51744.00 I   .097161  .000061   .262947  .000094  I  .2003128  .0000096 -0.1541 0.0073  I   -56.443     .257    -6.405     .340   .097120   .262910   .2003170   -56.000    -6.400  
+ 0 720 51745.00 I   .096019  .000039   .261634  .000077  I  .2004413  .0000091 -0.1032 0.0066  I   -56.616     .168    -6.395     .340   .096020   .261600   .2004180   -56.500    -6.300  
+ 0 721 51746.00 I   .094818  .000032   .260257  .000076  I  .2005212  .0000091 -0.0584 0.0074  I   -56.737     .168    -6.364     .340   .094860   .260160   .2005040   -56.900    -6.100  
+ 0 722 51747.00 I   .093439  .000036   .259074  .000087  I  .2005555  .0000116 -0.0057 0.0091  I   -56.768     .168    -6.262     .340   .093450   .259050   .2005770   -57.500    -6.000  
+ 0 723 51748.00 I   .092112  .000070   .258017  .000080  I  .2005297  .0000157  0.0558 0.0078  I   -56.942     .740    -6.187     .340   .092080   .258090   .2005480   -57.900    -6.000  
+ 0 724 51749.00 I   .091249  .000083   .256975  .000078  I  .2004521  .0000105  0.0926 0.0100  I   -57.381     .192    -6.224     .340   .091250   .257010   .2004300   -58.200    -6.000  
+ 0 725 51750.00 I   .090774  .000082   .256140  .000077  I  .2003567  .0000124  0.0929 0.0081  I   -57.939     .192    -6.319     .340   .090800   .256130   .2003190   -58.500    -6.100  
+ 0 726 51751.00 I   .090467  .000078   .255547  .000079  I  .2002801  .0000124  0.0519 0.0087  I   -58.399     .192    -6.373     .340   .090420   .255640   .2002770   -58.500    -6.300  
+ 0 727 51752.00 I   .090291  .000079   .255049  .000082  I  .2002659  .0000123 -0.0281 0.0078  I   -58.696     .192    -6.403     .340   .090210   .255130   .2002820   -58.500    -6.500  
+ 0 728 51753.00 I   .089877  .000082   .254516  .000073  I  .2003391  .0000096 -0.1165 0.0073  I   -58.859     .192    -6.486     .340   .089820   .254560   .2003350   -58.200    -6.600  
+ 0 729 51754.00 I   .089117  .000059   .253907  .000077  I  .2004866  .0000080 -0.1679 0.0076  I   -58.870     .192    -6.593     .340   .089090   .253970   .2004870   -58.100    -6.700  
+ 0 730 51755.00 I   .088353  .000032   .253164  .000072  I  .2006483  .0000119 -0.1395 0.0064  I   -58.697     .740    -6.594     .340   .088320   .253210   .2006530   -57.800    -6.700  
+ 0 731 51756.00 I   .087624  .000072   .252369  .000069  I  .2007368  .0000100 -0.0249 0.0075  I   -58.397     .194    -6.476     .340   .087600   .252400   .2007420   -57.700    -6.800  
+ 0 8 1 51757.00 I   .086922  .000075   .251692  .000063  I  .2006819  .0000091  0.1382 0.0068  I   -58.112     .194    -6.405     .340   .086920   .251760   .2006950   -57.800    -6.800  
+ 0 8 2 51758.00 I   .086416  .000080   .251127  .000061  I  .2004629  .0000093  0.2938 0.0066  I   -58.002     .194    -6.492     .340   .086350   .251190   .2004710   -58.000    -6.600  
+ 0 8 3 51759.00 I   .085879  .000081   .250675  .000062  I  .2001140  .0000095  0.3904 0.0071  I   -58.228     .161    -6.610     .340   .085880   .250750   .2000900   -58.300    -6.400  
+ 0 8 4 51760.00 I   .085277  .000084   .250228  .000029  I  .1997081  .0000107  0.4090 0.0070  I   -58.724     .161    -6.528     .340   .085310   .250310   .1997160   -58.600    -6.300  
+ 0 8 5 51761.00 I   .084583  .000083   .249655  .000030  I  .1993192  .0000103  0.3579 0.0079  I   -59.261     .161    -6.281     .340   .084600   .249680   .1993260   -59.000    -6.200  
+ 0 8 6 51762.00 I   .083909  .000039   .249042  .000030  I  .1990109  .0000116  0.2512 0.0070  I   -59.498     .740    -6.134     .340   .083890   .249060   .1990050   -59.400    -6.200  
+ 0 8 7 51763.00 I   .083208  .000040   .248525  .000053  I  .1988276  .0000094  0.1123 0.0073  I   -59.391     .245    -6.259     .340   .083200   .248580   .1988220   -59.600    -6.200  
+ 0 8 8 51764.00 I   .082305  .000039   .248115  .000054  I  .1987890  .0000089 -0.0348 0.0064  I   -59.255     .245    -6.536     .340   .082270   .248180   .1987940   -59.600    -6.400  
+ 0 8 9 51765.00 I   .081267  .000037   .247773  .000056  I  .1988874  .0000086 -0.1527 0.0062  I   -59.347     .245    -6.728     .340   .081170   .247790   .1988930   -59.500    -6.600  
+ 0 810 51766.00 I   .079976  .000035   .247487  .000060  I  .1990782  .0000086 -0.2222 0.0059  I   -59.413     .420    -6.725     .340   .079980   .247550   .1990860   -59.300    -6.800  
+ 0 811 51767.00 I   .078346  .000038   .247089  .000058  I  .1993185  .0000081 -0.2518 0.0056  I   -59.538     .420    -6.702     .340   .078360   .247210   .1993320   -59.000    -6.900  
+ 0 812 51768.00 I   .076607  .000037   .246398  .000061  I  .1995682  .0000072 -0.2405 0.0073  I   -59.497     .420    -6.712     .340   .076600   .246450   .1995820   -58.600    -7.000  
+ 0 813 51769.00 I   .075050  .000033   .245638  .000053  I  .1997864  .0000122 -0.1898 0.0074  I   -59.430     .740    -6.715     .340   .075080   .245660   .1997900   -58.400    -6.900  
+ 0 814 51770.00 I   .073685  .000070   .245040  .000049  I  .1999357  .0000130 -0.1026 0.0095  I   -59.411     .835    -6.628     .340   .073680   .245120   .1999360   -58.200    -6.800  
+ 0 815 51771.00 I   .072650  .000075   .244535  .000046  I  .1999812  .0000145  0.0159 0.0090  I   -59.410     .835    -6.458     .340   .072610   .244590   .1999960   -58.200    -6.600  
+ 0 816 51772.00 I   .071939  .000079   .244191  .000043  I  .1998923  .0000124  0.1694 0.0095  I   -59.437     .835    -6.297     .340   .071990   .244180   .1999100   -58.400    -6.400  
+ 0 817 51773.00 I   .071315  .000076   .244107  .000044  I  .1996358  .0000123  0.3429 0.0090  I   -59.385     .669    -6.235     .340   .071320   .244120   .1996310   -58.700    -6.100  
+ 0 818 51774.00 I   .070374  .000074   .244145  .000044  I  .1992137  .0000131  0.4948 0.0095  I   -59.542     .669    -6.225     .340   .070350   .244190   .1992030   -59.200    -6.000  
+ 0 819 51775.00 I   .069083  .000076   .244139  .000033  I  .1986598  .0000144  0.6058 0.0076  I   -59.648     .669    -6.231     .340   .069080   .244150   .1986760   -59.600    -5.900  
+ 0 820 51776.00 I   .067529  .000032   .244006  .000031  I  .1980179  .0000077  0.6696 0.0080  I   -59.722     .740    -6.224     .340   .067510   .244020   .1980460   -60.100    -5.900  
+ 0 821 51777.00 I   .065843  .000033   .243813  .000036  I  .1973375  .0000071  0.6829 0.0066  I   -59.896     .740    -6.218     .340   .065840   .243870   .1973380   -60.400    -6.000  
+ 0 822 51778.00 I   .063909  .000033   .243619  .000044  I  .1966686  .0000108  0.6467 0.0065  I   -60.247     .740    -6.239     .340   .063950   .243660   .1966380   -60.700    -6.100  
+ 0 823 51779.00 I   .062076  .000034   .243350  .000045  I  .1960529  .0000109  0.5841 0.0076  I   -60.681     .740    -6.304     .340   .062070   .243450   .1960370   -60.700    -6.200  
+ 0 824 51780.00 I   .060596  .000037   .243028  .000053  I  .1955095  .0000108  0.4940 0.0075  I   -61.006     .740    -6.416     .340   .060540   .243110   .1955580   -60.600    -6.300  
+ 0 825 51781.00 I   .059118  .000031   .242771  .000052  I  .1950637  .0000102  0.4073 0.0086  I   -61.072     .740    -6.543     .340   .059070   .242770   .1951390   -60.500    -6.400  
+ 0 826 51782.00 I   .057453  .000040   .242702  .000055  I  .1946679  .0000133  0.4015 0.0107  I   -60.870     .740    -6.584     .340   .057380   .242750   .1946870   -60.300    -6.500  
+ 0 827 51783.00 I   .055584  .000041   .242638  .000057  I  .1942311  .0000187  0.4844 0.0084  I   -60.556     .740    -6.441     .340   .055540   .242700   .1942120   -60.300    -6.400  
+ 0 828 51784.00 I   .053762  .000061   .242461  .000045  I  .1936784  .0000101  0.6295 0.0109  I   -60.326     .857    -6.159     .170   .053770   .242480   .1936680   -60.400    -6.200  
+ 0 829 51785.00 I   .052242  .000063   .242168  .000040  I  .1929644  .0000111  0.7977 0.0076  I   -60.255     .857    -5.940     .170   .052240   .242200   .1929680   -60.600    -6.000  
+ 0 830 51786.00 I   .051091  .000062   .241829  .000034  I  .1920968  .0000113  0.9246 0.0078  I   -60.295     .857    -5.939     .170   .051050   .241840   .1920900   -60.900    -5.800  
+ 0 831 51787.00 I   .050026  .000063   .241689  .000036  I  .1911429  .0000110  0.9686 0.0079  I   -60.525     .673    -5.991     .340   .050050   .241700   .1911300   -61.300    -5.700  
+ 0 9 1 51788.00 I   .049004  .000063   .241787  .000036  I  .1901885  .0000111  0.9259 0.0076  I   -60.780     .673    -5.998     .340   .049000   .241830   .1902050   -61.600    -5.600  
+ 0 9 2 51789.00 I   .047868  .000064   .241878  .000038  I  .1893152  .0000104  0.8109 0.0093  I   -61.135     .673    -5.832     .340   .047870   .241900   .1893390   -62.300    -5.500  
+ 0 9 3 51790.00 I   .046627  .000053   .241895  .000040  I  .1885815  .0000150  0.6516 0.0067  I   -61.383     .349    -5.698     .189   .046610   .241930   .1885410   -62.500    -5.700  
+ 0 9 4 51791.00 I   .045163  .000067   .241966  .000053  I  .1880169  .0000086  0.4775 0.0095  I   -61.369     .260    -5.808     .177   .045120   .242050   .1879720   -61.700    -5.900  
+ 0 9 5 51792.00 I   .043394  .000068   .242041  .000052  I  .1876245  .0000116  0.3095 0.0069  I   -61.176     .260    -6.107     .177   .043370   .242120   .1875600   -61.400    -6.200  
+ 0 9 6 51793.00 I   .041253  .000070   .242044  .000048  I  .1873924  .0000108  0.1581 0.0080  I   -60.996     .260    -6.344     .177   .041300   .242150   .1873680   -61.100    -6.500  
+ 0 9 7 51794.00 I   .038987  .000070   .241874  .000050  I  .1872994  .0000111  0.0333 0.0077  I   -60.682     .249    -6.379     .178   .038960   .241980   .1873270   -60.600    -6.600  
+ 0 9 8 51795.00 I   .036646  .000072   .241519  .000057  I  .1873119  .0000110 -0.0499 0.0084  I   -60.586     .249    -6.270     .178   .036650   .241590   .1873080   -60.100    -6.700  
+ 0 9 9 51796.00 I   .034228  .000066   .241045  .000056  I  .1873757  .0000126 -0.0627 0.0094  I   -60.499     .249    -6.197     .178   .034220   .241120   .1874040   -59.800    -6.700  
+ 0 910 51797.00 I   .031863  .000042   .240490  .000049  I  .1874162  .0000153 -0.0122 0.0078  I   -60.471     .740    -6.213     .191   .031860   .240520   .1874630   -59.600    -6.600  
+ 0 911 51798.00 I   .029800  .000047   .240042  .000056  I  .1873909  .0000092  0.0660 0.0089  I   -60.459     .274    -6.227     .143   .029850   .240070   .1874080   -59.700    -6.400  
+ 0 912 51799.00 I   .027756  .000046   .239711  .000062  I  .1872765  .0000092  0.1672 0.0067  I   -60.358     .274    -6.125     .143   .027770   .239730   .1872670   -59.900    -6.100  
+ 0 913 51800.00 I   .025131  .000066   .239553  .000069  I  .1870392  .0000097  0.3203 0.0067  I   -60.185     .274    -5.898     .143   .025180   .239550   .1870400   -60.300    -5.900  
+ 0 914 51801.00 I   .022100  .000068   .239613  .000070  I  .1866261  .0000097  0.5028 0.0061  I   -60.105     .261    -5.689     .156   .022170   .239660   .1866420   -60.600    -5.700  
+ 0 915 51802.00 I   .019235  .000065   .239702  .000068  I  .1860446  .0000075  0.6525 0.0067  I   -60.160     .261    -5.586     .156   .019230   .239770   .1860490   -61.000    -5.600  
+ 0 916 51803.00 I   .016892  .000062   .239768  .000070  I  .1853451  .0000092  0.7304 0.0051  I   -60.265     .261    -5.648     .156   .016920   .239800   .1853450   -61.300    -5.600  
+ 0 917 51804.00 I   .015146  .000050   .239926  .000085  I  .1846132  .0000069  0.7203 0.0059  I   -60.308     .109    -5.775     .340   .015190   .239960   .1845970   -61.400    -5.600  
+ 0 918 51805.00 I   .013673  .000055   .240390  .000083  I  .1839280  .0000074  0.6397 0.0050  I   -60.331     .740    -5.843     .340   .013700   .240450   .1839060   -61.300    -5.700  
+ 0 919 51806.00 I   .012234  .000046   .241177  .000078  I  .1833480  .0000072  0.5165 0.0053  I   -60.453     .740    -5.826     .340   .012240   .241170   .1833410   -61.000    -5.700  
+ 0 920 51807.00 I   .010639  .000049   .242107  .000084  I  .1828855  .0000077  0.4218 0.0053  I   -60.681     .740    -5.794     .340   .010580   .242100   .1828890   -60.700    -5.800  
+ 0 921 51808.00 I   .008784  .000049   .242995  .000082  I  .1824901  .0000079  0.3702 0.0053  I   -60.780     .125    -5.845     .340   .008730   .243030   .1824890   -60.100    -5.800  
+ 0 922 51809.00 I   .006877  .000049   .243489  .000079  I  .1821322  .0000073  0.3574 0.0065  I   -60.690     .125    -5.890     .340   .006910   .243550   .1821650   -59.600    -5.800  
+ 0 923 51810.00 I   .005374  .000058   .243521  .000063  I  .1817526  .0000102  0.4127 0.0060  I   -60.319     .125    -5.852     .340   .005410   .243510   .1817180   -59.300    -5.700  
+ 0 924 51811.00 I   .004180  .000061   .243406  .000056  I  .1812846  .0000096  0.5348 0.0072  I   -59.864     .222    -5.649     .137   .004180   .243400   .1812110   -59.000    -5.700  
+ 0 925 51812.00 I   .002961  .000071   .243379  .000042  I  .1806641  .0000102  0.7138 0.0067  I   -59.581     .289    -5.324     .158   .002940   .243410   .1806320   -58.900    -5.500  
+ 0 926 51813.00 I   .001685  .000068   .243522  .000042  I  .1798529  .0000094  0.9053 0.0074  I   -59.566     .289    -5.044     .158   .001720   .243530   .1798730   -59.000    -5.300  
+ 0 927 51814.00 I   .000637  .000065   .243908  .000034  I  .1788651  .0000107  1.0620 0.0069  I   -59.696     .289    -4.961     .158   .000670   .243960   .1788750   -59.200    -5.100  
+ 0 928 51815.00 I  -.000618  .000065   .244567  .000036  I  .1777557  .0000102  1.1383 0.0074  I   -59.804     .289    -5.055     .158  -.000600   .244620   .1777340   -59.400    -5.000  
+ 0 929 51816.00 I  -.002327  .000067   .245461  .000042  I  .1766239  .0000101  1.1083 0.0081  I   -59.878     .297    -5.130     .188  -.002350   .245470   .1766000   -59.600    -4.900  
+ 0 930 51817.00 I  -.004224  .000059   .246291  .000072  I  .1755666  .0000126  0.9957 0.0135  I   -59.920     .297    -5.130     .188  -.004230   .246310   .1756310   -59.800    -4.900  
+ 010 1 51818.00 I  -.005825  .000065   .246948  .000107  I  .1746510  .0000250  0.8278 0.0089  I   -59.969     .176    -5.101     .119  -.005830   .246970   .1747260   -59.800    -5.000  
+ 010 2 51819.00 I  -.007317  .000079   .247621  .000126  I  .1739216  .0000126  0.6283 0.0136  I   -59.939     .239    -5.189     .340  -.007300   .247640   .1739250   -59.700    -5.100  
+ 010 3 51820.00 I  -.009224  .000082   .248297  .000128  I  .1733949  .0000107  0.4272 0.0084  I   -59.745     .239    -5.387     .340  -.009170   .248320   .1733650   -59.400    -5.200  
+ 010 4 51821.00 I  -.011143  .000084   .248950  .000125  I  .1730499  .0000112  0.2778 0.0078  I   -59.402     .239    -5.525     .340  -.011180   .248870   .1730460   -58.900    -5.400  
+ 010 5 51822.00 I  -.013069  .000084   .249438  .000126  I  .1728087  .0000114  0.2201 0.0079  I   -59.020     .245    -5.434     .340  -.013050   .249390   .1728150   -58.600    -5.500  
+ 010 6 51823.00 I  -.014665  .000080   .249912  .000114  I  .1725813  .0000111  0.2476 0.0070  I   -58.714     .245    -5.280     .340  -.014670   .249930   .1725740   -58.100    -5.500  
+ 010 7 51824.00 I  -.016139  .000064   .250291  .000085  I  .1722903  .0000080  0.3446 0.0156  I   -58.558     .245    -5.181     .340  -.016150   .250310   .1723010   -57.800    -5.400  
+ 010 8 51825.00 I  -.017636  .000033   .250548  .000036  I  .1718754  .0000291  0.4916 0.0078  I   -58.518     .292    -5.207     .340  -.017650   .250520   .1718640   -57.600    -5.300  
+ 010 9 51826.00 I  -.019065  .000053   .250992  .000078  I  .1712972  .0000135  0.6682 0.0155  I   -58.483     .285    -5.261     .106  -.019100   .250930   .1712420   -57.700    -5.200  
+ 01010 51827.00 I  -.020494  .000061   .251829  .000106  I  .1705340  .0000104  0.8597 0.0086  I   -58.380     .285    -5.183     .106  -.020520   .251850   .1704830   -57.800    -5.000  
+ 01011 51828.00 I  -.021762  .000063   .253062  .000105  I  .1695809  .0000106  1.0417 0.0077  I   -58.247     .285    -4.908     .106  -.021790   .253120   .1695750   -58.100    -4.800  
+ 01012 51829.00 I  -.022955  .000061   .254629  .000103  I  .1684621  .0000114  1.1891 0.0077  I   -58.260     .333    -4.550     .105  -.022930   .254700   .1684640   -58.500    -4.600  
+ 01013 51830.00 I  -.024334  .000062   .256374  .000104  I  .1672188  .0000111  1.2883 0.0075  I   -58.296     .333    -4.314     .105  -.024370   .256430   .1672620   -58.800    -4.500  
+ 01014 51831.00 I  -.026199  .000063   .257929  .000105  I  .1659060  .0000097  1.3260 0.0073  I   -58.363     .314    -4.345     .173  -.026230   .257910   .1658840   -59.000    -4.400  
+ 01015 51832.00 I  -.028276  .000043   .259197  .000087  I  .1645897  .0000094  1.2951 0.0066  I   -58.380     .320    -4.573     .211  -.028270   .259150   .1644540   -59.100    -4.400  
+ 01016 51833.00 I  -.030364  .000046   .260226  .000042  I  .1633355  .0000090  1.2053 0.0061  I   -58.349     .341    -4.784     .193  -.030370   .260220   .1631760   -59.000    -4.400  
+ 01017 51834.00 I  -.032237  .000041   .261068  .000050  I  .1621903  .0000078  1.0821 0.0062  I   -58.332     .341    -4.817     .193  -.032240   .261070   .1621250   -58.700    -4.500  
+ 01018 51835.00 I  -.034006  .000040   .261825  .000051  I  .1611638  .0000085  0.9816 0.0056  I   -58.350     .341    -4.690     .193  -.033940   .261820   .1611760   -58.100    -4.500  
+ 01019 51836.00 I  -.035697  .000040   .262610  .000050  I  .1602119  .0000081  0.9269 0.0054  I   -58.430     .396    -4.520     .194  -.035650   .262640   .1601930   -57.600    -4.600  
+ 01020 51837.00 I  -.037440  .000042   .263490  .000056  I  .1592914  .0000067  0.9281 0.0059  I   -58.265     .396    -4.410     .194  -.037410   .263500   .1593100   -57.100    -4.500  
+ 01021 51838.00 I  -.039119  .000047   .264415  .000053  I  .1583268  .0000085  1.0159 0.0043  I   -57.924     .474    -4.329     .101  -.039160   .264420   .1584290   -56.800    -4.500  
+ 01022 51839.00 I  -.040594  .000031   .265452  .000052  I  .1572384  .0000054  1.1673 0.0048  I   -57.520     .740    -4.219     .340  -.040620   .265410   .1573430   -56.600    -4.400  
+ 01023 51840.00 I  -.041999  .000037   .266789  .000047  I  .1559864  .0000046  1.3361 0.0032  I   -57.248     .740    -4.066     .105  -.042050   .266770   .1560110   -56.600    -4.200  
+ 01024 51841.00 I  -.043325  .000037   .268351  .000047  I  .1545737  .0000034  1.4824 0.0030  I   -57.230     .740    -3.925     .105  -.043340   .268370   .1545450   -56.900    -4.100  
+ 01025 51842.00 I  -.044307  .000038   .269937  .000058  I  .1530409  .0000037  1.5707 0.0024  I   -57.410     .740    -3.869     .105  -.044330   .269950   .1530330   -57.300    -4.000  
+ 01026 51843.00 I  -.045559  .000044   .271645  .000058  I  .1514582  .0000035  1.5812 0.0023  I   -57.603     .740    -3.911     .105  -.045520   .271650   .1514710   -57.600    -3.900  
+ 01027 51844.00 I  -.046947  .000037   .273406  .000063  I  .1499040  .0000027  1.5152 0.0035  I   -57.650     .740    -3.985     .105  -.046990   .273440   .1499100   -57.800    -3.900  
+ 01028 51845.00 I  -.048312  .000042   .274938  .000068  I  .1484473  .0000060  1.3905 0.0034  I   -57.545     .740    -4.029     .105  -.048330   .274930   .1484300   -58.000    -3.900  
+ 01029 51846.00 I  -.049258  .000049   .276441  .000065  I  .1471312  .0000062  1.2411 0.0050  I   -57.393     .242    -4.061     .145  -.049290   .276360   .1471610   -57.900    -4.000  
+ 01030 51847.00 I  -.050029  .000053   .278336  .000063  I  .1459646  .0000079  1.0933 0.0048  I   -57.247     .197    -4.138     .178  -.050100   .278260   .1460480   -57.600    -4.100  
+ 01031 51848.00 I  -.051245  .000056   .280534  .000055  I  .1449376  .0000074  0.9666 0.0053  I   -57.032     .197    -4.240     .178  -.051190   .280530   .1449780   -57.200    -4.200  
+ 011 1 51849.00 I  -.052365  .000053   .282579  .000069  I  .1440273  .0000070  0.8524 0.0051  I   -56.665     .197    -4.258     .178  -.052430   .282600   .1440070   -56.600    -4.500  
+ 011 2 51850.00 I  -.053768  .000049   .284278  .000065  I  .1432139  .0000071  0.7951 0.0050  I   -56.224     .197    -4.134     .178  -.053760   .284260   .1432280   -55.800    -4.300  
+ 011 3 51851.00 I  -.055168  .000049   .285920  .000067  I  .1424100  .0000071  0.8192 0.0054  I   -55.902     .197    -3.954     .178  -.055120   .285960   .1424240   -55.300    -4.200  
+ 011 4 51852.00 I  -.056466  .000041   .287605  .000071  I  .1415673  .0000081  0.8678 0.0046  I   -55.779     .197    -3.855     .178  -.056420   .287660   .1415700   -55.000    -4.100  
+ 011 5 51853.00 I  -.057701  .000038   .289318  .000071  I  .1406684  .0000059  0.9344 0.0049  I   -55.737     .435    -3.867     .340  -.057720   .289250   .1406590   -54.900    -4.000  
+ 011 6 51854.00 I  -.059197  .000032   .291235  .000073  I  .1396914  .0000054  1.0221 0.0047  I   -55.656     .391    -3.893     .340  -.059220   .291140   .1396830   -55.000    -4.000  
+ 011 7 51855.00 I  -.060735  .000034   .293436  .000055  I  .1386201  .0000072  1.1219 0.0048  I   -55.577     .391    -3.811     .340  -.060760   .293410   .1386150   -55.300    -4.000  
+ 011 8 51856.00 I  -.062207  .000032   .295705  .000054  I  .1374452  .0000080  1.2287 0.0051  I   -55.610     .391    -3.571     .340  -.062220   .295680   .1374380   -55.700    -4.000  
+ 011 9 51857.00 I  -.063703  .000031   .297752  .000052  I  .1361688  .0000072  1.3172 0.0066  I   -55.736     .391    -3.226     .340  -.063730   .297700   .1361610   -56.100    -3.900  
+ 01110 51858.00 I  -.065070  .000045   .299403  .000065  I  .1348301  .0000105  1.3480 0.0095  I   -55.822     .391    -2.921     .340  -.065100   .299370   .1348130   -56.600    -3.800  
+ 01111 51859.00 I  -.065971  .000044   .300644  .000081  I  .1335009  .0000176  1.2946 0.0123  I   -55.801     .409    -2.825     .340  -.066020   .300590   .1334650   -56.800    -3.700  
+ 01112 51860.00 I  -.066267  .000059   .301799  .000080  I  .1322709  .0000222  1.1511 0.0107  I   -55.718     .369    -2.987     .340  -.066280   .301690   .1322310   -56.900    -3.500  
+ 01113 51861.00 I  -.066492  .000068   .303333  .000075  I  .1312182  .0000122  0.9491 0.0125  I   -55.618     .389    -3.254     .340  -.066450   .303290   .1312040   -56.600    -3.400  
+ 01114 51862.00 I  -.067493  .000071   .305215  .000075  I  .1303689  .0000116  0.7585 0.0085  I   -55.481     .389    -3.380     .340  -.067400   .305250   .1303790   -56.200    -3.300  
+ 01115 51863.00 I  -.069107  .000069   .306966  .000073  I  .1296774  .0000119  0.6393 0.0080  I   -55.297     .389    -3.254     .340  -.069160   .307050   .1296860   -55.700    -3.200  
+ 01116 51864.00 I  -.071252  .000069   .308424  .000065  I  .1290646  .0000111  0.5971 0.0069  I   -55.106     .389    -2.984     .340  -.071220   .308460   .1290580   -55.000    -3.200  
+ 01117 51865.00 I  -.073154  .000075   .309840  .000046  I  .1284567  .0000071  0.6344 0.0067  I   -54.940     .389    -2.766     .340  -.073120   .309810   .1284520   -54.500    -3.100  
+ 01118 51866.00 I  -.074727  .000064   .311485  .000040  I  .1277697  .0000074  0.7501 0.0072  I   -54.776     .358    -2.695     .340  -.074710   .311500   .1277780   -54.100    -3.100  
+ 01119 51867.00 I  -.075653  .000057   .313425  .000046  I  .1269444  .0000126  0.9020 0.0071  I   -54.594     .740    -2.727     .103  -.075660   .313440   .1269430   -54.000    -3.200  
+ 01120 51868.00 I  -.075902  .000065   .315632  .000067  I  .1259690  .0000122  1.0441 0.0087  I   -54.447     .740    -2.780     .103  -.075870   .315600   .1259530   -54.000    -3.100  
+ 01121 51869.00 I  -.075832  .000065   .317969  .000067  I  .1248704  .0000120  1.1438 0.0087  I   -54.435     .740    -2.817     .103  -.075810   .317980   .1248720   -54.200    -3.100  
+ 01122 51870.00 I  -.075847  .000066   .320194  .000067  I  .1236988  .0000124  1.1914 0.0087  I   -54.603     .740    -2.842     .103  -.075840   .320240   .1237180   -54.600    -3.000  
+ 01123 51871.00 I  -.076126  .000052   .322251  .000070  I  .1225155  .0000127  1.1555 0.0088  I   -54.868     .740    -2.850     .103  -.076120   .322250   .1224790   -54.900    -2.900  
+ 01124 51872.00 I  -.076569  .000052   .324131  .000071  I  .1214167  .0000124  1.0330 0.0091  I   -55.054     .740    -2.830     .103  -.076580   .324110   .1213050   -55.200    -2.800  
+ 01125 51873.00 I  -.077090  .000052   .325916  .000070  I  .1204631  .0000129  0.8696 0.0073  I   -55.037     .740    -2.798     .103  -.077060   .325860   .1203310   -55.300    -2.700  
+ 01126 51874.00 I  -.077734  .000046   .327749  .000049  I  .1196786  .0000076  0.7027 0.0071  I   -54.853     .194    -2.810     .254  -.077740   .327710   .1195830   -55.200    -2.700  
+ 01127 51875.00 I  -.078464  .000053   .329650  .000070  I  .1190476  .0000061  0.5661 0.0053  I   -54.621     .194    -2.900     .254  -.078510   .329660   .1190050   -54.900    -2.700  
+ 01128 51876.00 I  -.079156  .000052   .331507  .000069  I  .1185260  .0000073  0.4908 0.0049  I   -54.377     .194    -3.003     .254  -.079060   .331530   .1185190   -54.300    -2.700  
+ 01129 51877.00 I  -.079387  .000049   .333385  .000065  I  .1180405  .0000076  0.4920 0.0053  I   -54.081     .194    -3.001     .254  -.079330   .333330   .1180470   -53.600    -2.700  
+ 01130 51878.00 I  -.079572  .000049   .335195  .000064  I  .1175256  .0000077  0.5425 0.0052  I   -53.785     .194    -2.868     .254  -.079610   .335150   .1175220   -53.000    -2.700  
+ 012 1 51879.00 I  -.080077  .000052   .336962  .000063  I  .1169465  .0000071  0.6199 0.0060  I   -53.640     .194    -2.711     .254  -.080070   .336980   .1169220   -52.500    -2.700  
+ 012 2 51880.00 I  -.080633  .000053   .338725  .000066  I  .1162730  .0000093  0.7357 0.0092  I   -53.606     .155    -2.570     .158  -.080610   .338690   .1162800   -52.500    -2.800  
+ 012 3 51881.00 I  -.080874  .000041   .340453  .000038  I  .1154659  .0000170  0.8793 0.0068  I   -53.633     .142    -2.597     .340  -.080890   .340360   .1155090   -52.500    -2.700  
+ 012 4 51882.00 I  -.080959  .000066   .342134  .000071  I  .1145151  .0000100  1.0210 0.0099  I   -53.534     .133    -2.617     .340  -.081000   .342100   .1145360   -52.700    -2.700  
+ 012 5 51883.00 I  -.081277  .000097   .343792  .000082  I  .1134289  .0000103  1.1477 0.0071  I   -53.453     .177    -2.579     .190  -.081290   .343710   .1134120   -53.100    -2.600  
+ 012 6 51884.00 I  -.081778  .000099   .345435  .000083  I  .1122263  .0000102  1.2545 0.0074  I   -53.580     .177    -2.488     .190  -.081830   .345320   .1122210   -53.400    -2.500  
+ 012 7 51885.00 I  -.082321  .000100   .347178  .000086  I  .1109317  .0000105  1.3258 0.0072  I   -53.798     .211    -2.360     .166  -.082340   .347130   .1109530   -53.700    -2.400  
+ 012 8 51886.00 I  -.082764  .000097   .349073  .000087  I  .1095995  .0000102  1.3226 0.0073  I   -53.940     .211    -2.098     .166  -.082810   .349020   .1095820   -54.000    -2.200  
+ 012 9 51887.00 I  -.083349  .000097   .350969  .000089  I  .1083132  .0000102  1.2392 0.0063  I   -53.926     .211    -1.843     .166  -.083330   .350940   .1083340   -54.100    -2.200  
+ 01210 51888.00 I  -.083714  .000094   .352612  .000108  I  .1071406  .0000073  1.0974 0.0057  I   -53.922     .247    -1.782     .292  -.083680   .352570   .1071950   -54.100    -2.100  
+ 01211 51889.00 I  -.083829  .000083   .353999  .000106  I  .1061295  .0000049  0.9222 0.0043  I   -53.971     .236    -1.964     .268  -.083840   .354000   .1061520   -53.900    -2.000  
+ 01212 51890.00 I  -.083649  .000088   .355350  .000103  I  .1052914  .0000044  0.7614 0.0037  I   -53.885     .221    -2.184     .278  -.083610   .355370   .1052740   -53.600    -2.000  
+ 01213 51891.00 I  -.082749  .000087   .357031  .000098  I  .1045855  .0000056  0.6643 0.0036  I   -53.532     .221    -2.197     .278  -.082670   .356990   .1045790   -53.100    -2.000  
+ 01214 51892.00 I  -.081454  .000086   .359066  .000096  I  .1039318  .0000056  0.6601 0.0039  I   -52.962     .139    -1.961     .254  -.081380   .359130   .1039370   -52.500    -1.900  
+ 01215 51893.00 I  -.080187  .000089   .361234  .000096  I  .1032411  .0000055  0.7284 0.0042  I   -52.615     .139    -1.742     .254  -.080170   .361250   .1032250   -52.100    -1.900  
+ 01216 51894.00 I  -.079088  .000083   .363461  .000065  I  .1024684  .0000062  0.8166 0.0046  I   -52.487     .373    -1.678     .220  -.079090   .363400   .1024390   -51.700    -1.800  
+ 01217 51895.00 I  -.078140  .000064   .365712  .000067  I  .1016101  .0000074  0.8984 0.0066  I   -52.494     .381    -1.737     .163  -.078160   .365680   .1014840   -51.700    -1.800  
+ 01218 51896.00 I  -.077838  .000071   .367832  .000075  I  .1006813  .0000117  0.9512 0.0065  I   -52.538     .373    -1.801     .151  -.077860   .367890   .1004880   -51.800    -1.800  
+ 01219 51897.00 I  -.078183  .000072   .369725  .000074  I  .0997235  .0000107  0.9567 0.0083  I   -52.608     .373    -1.834     .151  -.078140   .369680   .0996230   -52.200    -1.900  
+ 01220 51898.00 I  -.078693  .000069   .371616  .000074  I  .0987847  .0000118  0.9117 0.0080  I   -52.751     .373    -1.870     .151  -.078760   .371540   .0987910   -52.600    -2.000  
+ 01221 51899.00 I  -.078656  .000068   .373822  .000074  I  .0979190  .0000118  0.8099 0.0083  I   -52.968     .397    -1.954     .161  -.078790   .373900   .0978530   -53.000    -2.100  
+ 01222 51900.00 I  -.078397  .000073   .376311  .000077  I  .0971744  .0000118  0.6794 0.0087  I   -53.238     .397    -1.956     .161  -.078300   .376360   .0971970   -53.400    -2.300  
+ 01223 51901.00 I  -.078015  .000076   .378911  .000064  I  .0965589  .0000128  0.5527 0.0095  I   -53.380     .162    -1.922     .116  -.077960   .378920   .0966350   -53.600    -2.300  
+ 01224 51902.00 I  -.077568  .000053   .381524  .000051  I  .0960664  .0000150  0.4337 0.0109  I   -53.296     .740    -1.938     .340  -.077570   .381510   .0961140   -53.500    -2.300  
+ 01225 51903.00 I  -.077052  .000056   .384097  .000057  I  .0956855  .0000176  0.3328 0.0080  I   -53.041     .209    -2.062     .340  -.077090   .384100   .0956820   -53.300    -2.300  
+ 01226 51904.00 I  -.076627  .000087   .386538  .000077  I  .0953894  .0000055  0.2661 0.0093  I   -52.739     .192    -2.219     .340  -.076580   .386520   .0953610   -53.100    -2.300  
+ 01227 51905.00 I  -.076313  .000079   .388743  .000073  I  .0951401  .0000060  0.2388 0.0043  I   -52.473     .192    -2.267     .340  -.076400   .388660   .0951180   -52.600    -2.300  
+ 01228 51906.00 I  -.076058  .000079   .390710  .000072  I  .0948989  .0000065  0.2500 0.0043  I   -52.307     .192    -2.175     .340  -.076070   .390660   .0948940   -52.300    -2.100  
+ 01229 51907.00 I  -.075340  .000079   .392637  .000074  I  .0946252  .0000063  0.3057 0.0048  I   -52.316     .192    -2.062     .340  -.075390   .392710   .0946300   -51.900    -2.000  
+ 01230 51908.00 I  -.074867  .000076   .394535  .000076  I  .0942721  .0000070  0.4076 0.0047  I   -52.467     .192    -2.049     .340  -.074860   .394520   .0942860   -51.700    -2.000  
+ 01231 51909.00 I  -.074232  .000073   .396311  .000078  I  .0938013  .0000069  0.5356 0.0038  I   -52.572     .191    -2.112     .340  -.074200   .396280   .0938240   -51.600    -1.900  
+ 1 1 1 51910.00 I  -.073475  .000040   .398000  .000052  I  .0932012  .0000032  0.6628 0.0037  I   -52.513     .174    -2.159     .340  -.073450   .397930   .0932210   -51.600    -2.000  
+ 1 1 2 51911.00 I  -.072616  .000042   .399776  .000057  I  .0924807  .0000029  0.7749 0.0022  I   -52.431     .174    -2.184     .340  -.072570   .399630   .0924880   -51.800    -2.100  
+ 1 1 3 51912.00 I  -.071574  .000046   .401696  .000058  I  .0916585  .0000029  0.8655 0.0022  I   -52.522     .174    -2.242     .340  -.071560   .401750   .0916580   -52.100    -2.300  
+ 1 1 4 51913.00 I  -.071043  .000043   .403552  .000055  I  .0907643  .0000033  0.9123 0.0022  I   -52.722     .174    -2.278     .340  -.071020   .403630   .0907650   -52.600    -2.400  
+ 1 1 5 51914.00 I  -.070727  .000043   .405169  .000052  I  .0898571  .0000033  0.8897 0.0036  I   -52.837     .174    -2.140     .340  -.070740   .405100   .0898580   -53.100    -1.900  
+ 1 1 6 51915.00 I  -.070375  .000043   .406607  .000044  I  .0890085  .0000064  0.7965 0.0061  I   -52.917     .189    -1.821     .340  -.070400   .406550   .0890200   -53.500    -1.800  
+ 1 1 7 51916.00 I  -.070175  .000047   .407955  .000050  I  .0882806  .0000117  0.6539 0.0053  I   -53.215     .183    -1.575     .340  -.070180   .407910   .0883060   -53.700    -1.800  
+ 1 1 8 51917.00 I  -.070202  .000045   .409286  .000070  I  .0876993  .0000084  0.5154 0.0068  I   -53.713     .196    -1.647     .340  -.070120   .409250   .0877260   -53.800    -1.800  
+ 1 1 9 51918.00 I  -.070110  .000056   .410656  .000066  I  .0872305  .0000069  0.4348 0.0056  I   -53.979     .196    -1.955     .340  -.070060   .410700   .0872420   -53.500    -2.000  
+ 1 110 51919.00 I  -.069867  .000052   .412121  .000066  I  .0868107  .0000075  0.4113 0.0051  I   -53.649     .196    -2.180     .340  -.069810   .412100   .0868070   -53.200    -2.200  
+ 1 111 51920.00 I  -.069253  .000052   .413913  .000065  I  .0863871  .0000074  0.4502 0.0048  I   -52.624     .740    -2.182     .340  -.069330   .413860   .0863720   -52.700    -2.400  
+ 1 112 51921.00 I  -.068491  .000054   .415946  .000067  I  .0858811  .0000061  0.5766 0.0053  I   -51.878     .740    -2.036     .340  -.068490   .415910   .0858980   -52.300    -2.600  
+ 1 113 51922.00 I  -.067476  .000048   .418035  .000067  I  .0852171  .0000075  0.7532 0.0059  I   -51.498     .740    -1.953     .340  -.067480   .418010   .0852290   -52.000    -2.400  
+ 1 114 51923.00 I  -.066439  .000049   .420036  .000049  I  .0843862  .0000101  0.8952 0.0050  I   -51.499     .330    -1.918     .206  -.066440   .420030   .0843870   -51.900    -2.300  
+ 1 115 51924.00 I  -.065330  .000036   .421837  .000061  I  .0834544  .0000067  0.9543 0.0062  I   -51.709     .311    -1.818     .182  -.065340   .421820   .0834490   -51.900    -2.100  
+ 1 116 51925.00 I  -.063996  .000041   .423433  .000081  I  .0825093  .0000072  0.9185 0.0055  I   -51.949     .311    -1.677     .182  -.064050   .423420   .0825050   -52.000    -1.900  
+ 1 117 51926.00 I  -.062707  .000040   .424929  .000081  I  .0816517  .0000088  0.7799 0.0056  I   -52.141     .311    -1.643     .182  -.062730   .424880   .0816480   -52.200    -1.900  
+ 1 118 51927.00 I  -.061468  .000041   .426305  .000081  I  .0809688  .0000085  0.5836 0.0059  I   -52.315     .332    -1.770     .181  -.061460   .426190   .0809690   -52.400    -1.900  
+ 1 119 51928.00 I  -.060311  .000045   .427747  .000088  I  .0804814  .0000080  0.3968 0.0058  I   -52.581     .332    -1.948     .181  -.060340   .427710   .0804850   -52.600    -2.100  
+ 1 120 51929.00 I  -.059229  .000050   .429112  .000088  I  .0801680  .0000078  0.2317 0.0061  I   -52.834     .332    -2.065     .181  -.059270   .429150   .0801190   -52.600    -2.200  
+ 1 121 51930.00 I  -.058108  .000049   .430179  .000088  I  .0800094  .0000093  0.0923 0.0060  I   -52.871     .122    -2.149     .189  -.058160   .430160   .0799550   -52.500    -2.300  
+ 1 122 51931.00 I  -.056802  .000062   .431143  .000063  I  .0799699  .0000090 -0.0066 0.0057  I   -52.610     .125    -2.279     .211  -.056820   .431050   .0799820   -52.400    -2.400  
+ 1 123 51932.00 I  -.055433  .000062   .432399  .000068  I  .0800133  .0000065 -0.0777 0.0055  I   -52.196     .125    -2.422     .211  -.055400   .432420   .0800690   -52.100    -2.400  
+ 1 124 51933.00 I  -.054105  .000061   .434088  .000066  I  .0800967  .0000064 -0.0631 0.0046  I   -51.846     .125    -2.457     .211  -.054080   .434170   .0801060   -51.700    -2.300  
+ 1 125 51934.00 I  -.052244  .000061   .435907  .000063  I  .0801067  .0000064  0.0500 0.0045  I   -51.605     .196    -2.312     .172  -.052310   .435870   .0800960   -51.400    -2.100  
+ 1 126 51935.00 I  -.050480  .000061   .437795  .000065  I  .0799898  .0000062  0.1837 0.0052  I   -51.624     .196    -2.188     .172  -.050530   .437790   .0799840   -51.000    -2.100  
+ 1 127 51936.00 I  -.049071  .000061   .439653  .000076  I  .0797378  .0000083  0.3217 0.0047  I   -51.770     .196    -2.174     .172  -.049080   .439600   .0797570   -50.900    -2.100  
+ 1 128 51937.00 I  -.047516  .000040   .441457  .000081  I  .0793490  .0000070  0.4518 0.0054  I   -51.917     .125    -2.262     .200  -.047500   .441380   .0794010   -50.800    -2.100  
+ 1 129 51938.00 I  -.045591  .000037   .443202  .000100  I  .0788464  .0000068  0.5454 0.0048  I   -51.961     .176    -2.358     .171  -.045590   .443170   .0789010   -50.900    -2.300  
+ 1 130 51939.00 I  -.043693  .000038   .444798  .000100  I  .0782754  .0000067  0.5875 0.0048  I   -51.946     .176    -2.440     .171  -.043680   .444700   .0782950   -51.100    -2.400  
+ 1 131 51940.00 I  -.042033  .000035   .446206  .000097  I  .0776959  .0000069  0.5562 0.0048  I   -51.980     .176    -2.569     .171  -.042080   .446190   .0776930   -51.600    -2.700  
+ 1 2 1 51941.00 I  -.040636  .000032   .447190  .000096  I  .0771815  .0000069  0.4696 0.0044  I   -51.985     .124    -2.765     .137  -.040620   .447160   .0771880   -52.100    -3.000  
+ 1 2 2 51942.00 I  -.039126  .000029   .447931  .000089  I  .0767659  .0000053  0.3556 0.0043  I   -51.998     .124    -2.769     .137  -.039100   .447830   .0767630   -52.600    -3.200  
+ 1 2 3 51943.00 I  -.037703  .000032   .448626  .000084  I  .0764797  .0000051  0.2136 0.0036  I   -52.080     .124    -2.567     .137  -.037700   .448620   .0764750   -52.900    -3.400  
+ 1 2 4 51944.00 I  -.036061  .000043   .449368  .000035  I  .0763355  .0000049  0.0814 0.0038  I   -52.499     .740    -2.361     .340  -.036070   .449360   .0763320   -53.300    -3.500  
+ 1 2 5 51945.00 I  -.034020  .000069   .450341  .000038  I  .0762962  .0000055  0.0113 0.0035  I   -53.224     .740    -2.435     .152  -.034080   .450270   .0763010   -53.400    -3.500  
+ 1 2 6 51946.00 I  -.032180  .000068   .451589  .000039  I  .0762796  .0000049  0.0409 0.0038  I   -53.761     .740    -2.795     .152  -.032090   .451620   .0762880   -53.500    -3.500  
+ 1 2 7 51947.00 I  -.030365  .000068   .453000  .000040  I  .0761678  .0000053  0.2102 0.0037  I   -53.620     .740    -3.153     .152  -.030410   .453010   .0761720   -53.400    -3.300  
+ 1 2 8 51948.00 I  -.028369  .000069   .454523  .000037  I  .0758262  .0000056  0.4784 0.0039  I   -52.834     .740    -3.243     .180  -.028520   .454470   .0758360   -53.200    -3.100  
+ 1 2 9 51949.00 I  -.026513  .000069   .456074  .000063  I  .0752100  .0000057  0.7497 0.0046  I   -51.910     .740    -3.188     .180  -.026520   .456090   .0751990   -52.900    -3.000  
+ 1 210 51950.00 I  -.024700  .000065   .457538  .000069  I  .0743443  .0000074  0.9684 0.0047  I   -51.319     .176    -3.125     .149  -.024720   .457560   .0743150   -52.600    -2.600  
+ 1 211 51951.00 I  -.023433  .000034   .458809  .000078  I  .0733026  .0000076  1.0990 0.0051  I   -51.218     .179    -3.057     .340  -.023390   .458870   .0732830   -52.300    -2.500  
+ 1 212 51952.00 I  -.022363  .000038   .459792  .000083  I  .0721740  .0000069  1.1465 0.0052  I   -51.475     .175    -2.877     .340  -.022340   .459760   .0721790   -52.100    -2.400  
+ 1 213 51953.00 I  -.020906  .000039   .460725  .000082  I  .0710272  .0000071  1.1411 0.0054  I   -51.830     .175    -2.633     .340  -.020910   .460680   .0710400   -51.900    -2.500  
+ 1 214 51954.00 I  -.019204  .000041   .461937  .000080  I  .0699186  .0000084  1.0554 0.0057  I   -52.076     .175    -2.547     .340  -.019230   .461970   .0699210   -51.900    -2.600  
+ 1 215 51955.00 I  -.017628  .000038   .463232  .000068  I  .0689386  .0000089  0.9030 0.0060  I   -52.241     .170    -2.717     .340  -.017580   .463260   .0689260   -51.700    -2.700  
+ 1 216 51956.00 I  -.015910  .000038   .464556  .000068  I  .0681089  .0000085  0.7617 0.0064  I   -52.437     .170    -3.066     .340  -.015900   .464530   .0681100   -51.700    -3.000  
+ 1 217 51957.00 I  -.014070  .000038   .465865  .000067  I  .0674058  .0000092  0.6487 0.0109  I   -52.687     .107    -3.340     .340  -.014070   .465820   .0673560   -51.500    -3.300  
+ 1 218 51958.00 I  -.012282  .000030   .467076  .000057  I  .0667945  .0000200  0.5868 0.0067  I   -52.746     .740    -3.470     .182  -.012260   .467050   .0667340   -51.400    -3.400  
+ 1 219 51959.00 I  -.010501  .000041   .468197  .000057  I  .0662114  .0000097  0.5874 0.0104  I   -52.415     .139    -3.529     .178  -.010490   .468140   .0661950   -51.200    -3.500  
+ 1 220 51960.00 I  -.008318  .000059   .469373  .000058  I  .0656072  .0000059  0.6255 0.0056  I   -51.832     .139    -3.558     .178  -.008260   .469310   .0656190   -51.000    -3.500  
+ 1 221 51961.00 I  -.005520  .000056   .470698  .000052  I  .0649585  .0000054  0.6693 0.0038  I   -51.326     .139    -3.500     .178  -.005540   .470720   .0649590   -50.800    -3.500  
+ 1 222 51962.00 I  -.002296  .000056   .472165  .000052  I  .0642607  .0000048  0.7366 0.0037  I   -50.976     .171    -3.335     .157  -.002380   .472160   .0642450   -50.700    -3.400  
+ 1 223 51963.00 I   .000948  .000058   .473902  .000050  I  .0634634  .0000051  0.8694 0.0036  I   -50.956     .171    -3.147     .157   .001010   .473850   .0634730   -50.500    -3.200  
+ 1 224 51964.00 I   .004069  .000059   .475723  .000048  I  .0625081  .0000053  1.0437 0.0038  I   -51.055     .171    -3.074     .157   .004070   .475750   .0625000   -50.500    -3.200  
+ 1 225 51965.00 I   .006803  .000060   .477346  .000044  I  .0613757  .0000057  1.2189 0.0042  I   -51.200     .740    -3.150     .116   .006820   .477360   .0613410   -50.600    -3.200  
+ 1 226 51966.00 I   .009390  .000056   .478757  .000069  I  .0600802  .0000066  1.3642 0.0055  I   -51.325     .169    -3.303     .340   .009410   .478700   .0600530   -50.700    -3.300  
+ 1 227 51967.00 I   .011992  .000059   .479986  .000071  I  .0586678  .0000093  1.4479 0.0057  I   -51.384     .169    -3.467     .340   .011990   .479910   .0586680   -50.900    -3.500  
+ 1 228 51968.00 I   .014200  .000057   .481045  .000070  I  .0572087  .0000093  1.4586 0.0063  I   -51.395     .169    -3.630     .340   .014300   .481000   .0572140   -51.300    -3.800  
+ 1 3 1 51969.00 I   .016153  .000057   .481998  .000072  I  .0557767  .0000086  1.3911 0.0064  I   -51.668     .685    -3.826     .106   .016090   .481990   .0557520   -51.600    -4.100  
+ 1 3 2 51970.00 I   .017276  .000071   .482700  .000077  I  .0544494  .0000087  1.2550 0.0062  I   -51.632     .685    -3.900     .106   .017280   .482630   .0544200   -51.800    -4.400  
+ 1 3 3 51971.00 I   .018496  .000066   .483232  .000083  I  .0532711  .0000088  1.1058 0.0053  I   -51.638     .145    -3.870     .340   .018470   .483100   .0532710   -52.000    -4.700  
+ 1 3 4 51972.00 I   .020341  .000053   .483840  .000087  I  .0522268  .0000060  0.9897 0.0061  I   -51.810     .740    -3.842     .340   .020380   .483750   .0522360   -52.200    -4.800  
+ 1 3 5 51973.00 I   .022588  .000087   .484558  .000097  I  .0512724  .0000084  0.9316 0.0051  I   -52.135     .412    -3.975     .340   .022620   .484550   .0512640   -52.200    -4.900  
+ 1 3 6 51974.00 I   .024503  .000096   .485112  .000093  I  .0503386  .0000083  0.9484 0.0059  I   -52.357     .412    -4.273     .340   .024500   .485160   .0503260   -52.100    -4.800  
+ 1 3 7 51975.00 I   .026284  .000095   .485380  .000093  I  .0493333  .0000083  1.0920 0.0059  I   -52.202     .412    -4.545     .340   .026380   .485330   .0493410   -52.000    -4.500  
+ 1 3 8 51976.00 I   .028795  .000088   .485434  .000088  I  .0481232  .0000083  1.3296 0.0058  I   -51.688     .412    -4.630     .340   .028660   .485440   .0481510   -51.800    -4.300  
+ 1 3 9 51977.00 I   .031018  .000088   .485399  .000083  I  .0466901  .0000082  1.5191 0.0071  I   -51.107     .412    -4.587     .340   .031050   .485380   .0466690   -51.600    -4.100  
+ 1 310 51978.00 I   .033359  .000092   .485373  .000062  I  .0451120  .0000116  1.6277 0.0063  I   -50.747     .412    -4.572     .340   .033350   .485300   .0451310   -51.400    -3.900  
+ 1 311 51979.00 I   .035564  .000055   .485413  .000035  I  .0434664  .0000096  1.6413 0.0069  I   -50.712     .740    -4.590     .340   .035550   .485400   .0435500   -51.300    -3.800  
+ 1 312 51980.00 I   .038022  .000034   .485524  .000052  I  .0418693  .0000075  1.5354 0.0068  I   -50.933     .412    -4.513     .340   .037980   .485480   .0419400   -51.100    -3.800  
+ 1 313 51981.00 I   .040915  .000033   .485734  .000052  I  .0404265  .0000096  1.3370 0.0064  I   -51.229     .412    -4.330     .340   .040870   .485670   .0404350   -51.000    -3.900  
+ 1 314 51982.00 I   .043874  .000028   .486103  .000048  I  .0392186  .0000104  1.0688 0.0072  I   -51.433     .412    -4.233     .340   .043920   .486220   .0392130   -50.900    -4.200  
+ 1 315 51983.00 I   .046406  .000027   .486377  .000050  I  .0382843  .0000107  0.8144 0.0074  I   -51.535     .412    -4.391     .340   .046450   .486420   .0383080   -50.700    -4.500  
+ 1 316 51984.00 I   .048681  .000028   .486655  .000054  I  .0375617  .0000106  0.6443 0.0073  I   -51.665     .412    -4.724     .340   .048650   .486610   .0375840   -50.600    -4.800  
+ 1 317 51985.00 I   .050978  .000030   .487043  .000053  I  .0369778  .0000099  0.5279 0.0081  I   -51.841     .412    -4.996     .340   .050980   .487060   .0369820   -50.400    -5.100  
+ 1 318 51986.00 I   .053555  .000027   .487408  .000066  I  .0364941  .0000122  0.4468 0.0070  I   -51.824     .740    -5.080     .340   .053540   .487400   .0364890   -50.300    -5.200  
+ 1 319 51987.00 I   .056329  .000044   .487821  .000069  I  .0360676  .0000100  0.4153 0.0072  I   -51.399     .374    -5.031     .176   .056310   .487770   .0360630   -50.200    -5.300  
+ 1 320 51988.00 I   .059135  .000061   .488394  .000086  I  .0356430  .0000077  0.4451 0.0067  I   -50.698     .374    -4.944     .176   .059060   .488390   .0356370   -50.200    -5.100  
+ 1 321 51989.00 I   .061688  .000061   .488864  .000087  I  .0351568  .0000088  0.5365 0.0058  I   -50.111     .374    -4.828     .176   .061640   .488880   .0351550   -50.100    -5.000  
+ 1 322 51990.00 I   .064613  .000062   .489050  .000087  I  .0345635  .0000088  0.6473 0.0064  I   -49.860     .419    -4.696     .152   .064420   .489130   .0345770   -50.100    -4.800  
+ 1 323 51991.00 I   .067318  .000067   .489461  .000088  I  .0338698  .0000092  0.7354 0.0063  I   -49.913     .419    -4.489     .152   .067340   .489510   .0338570   -50.100    -4.600  
+ 1 324 51992.00 I   .070277  .000066   .490025  .000073  I  .0330839  .0000090  0.8498 0.0057  I   -50.077     .419    -4.351     .152   .070240   .490030   .0330900   -50.200    -4.500  
+ 1 325 51993.00 I   .073332  .000083   .490497  .000064  I  .0321623  .0000069  0.9878 0.0053  I   -50.258     .109    -4.368     .340   .073320   .490500   .0321940   -50.200    -4.500  
+ 1 326 51994.00 I   .076391  .000085   .490842  .000038  I  .0311235  .0000056  1.0803 0.0044  I   -50.411     .103    -4.541     .340   .076380   .490860   .0311380   -50.200    -4.600  
+ 1 327 51995.00 I   .079270  .000092   .491052  .000038  I  .0300255  .0000054  1.1015 0.0039  I   -50.482     .140    -4.793     .144   .079230   .491060   .0300160   -50.300    -4.800  
+ 1 328 51996.00 I   .081797  .000092   .491117  .000039  I  .0289424  .0000054  1.0571 0.0037  I   -50.483     .140    -5.020     .144   .081730   .491080   .0289390   -50.200    -5.000  
+ 1 329 51997.00 I   .084181  .000090   .491078  .000040  I  .0279322  .0000052  0.9502 0.0037  I   -50.420     .173    -5.139     .132   .084070   .491050   .0279420   -50.100    -5.300  
+ 1 330 51998.00 I   .086363  .000091   .490780  .000043  I  .0270475  .0000052  0.8270 0.0040  I   -50.514     .173    -5.190     .132   .086360   .490830   .0270520   -49.900    -5.600  
+ 1 331 51999.00 I   .088580  .000077   .490263  .000049  I  .0262681  .0000061  0.7341 0.0044  I   -50.623     .205    -5.212     .153   .088550   .490340   .0262730   -49.800    -5.700  
+ 1 4 1 52000.00 I   .090657  .000062   .489693  .000041  I  .0255680  .0000071  0.6750 0.0041  I   -50.626     .348    -5.274     .164   .090660   .489730   .0255590   -49.700    -5.800  
+ 1 4 2 52001.00 I   .092713  .000034   .489218  .000061  I  .0248977  .0000056  0.6770 0.0043  I   -50.444     .330    -5.404     .340   .092710   .489170   .0248780   -49.600    -5.800  
+ 1 4 3 52002.00 I   .094742  .000033   .488819  .000071  I  .0241865  .0000050  0.7616 0.0040  I   -50.089     .330    -5.549     .340   .094770   .488820   .0241770   -49.500    -5.800  
+ 1 4 4 52003.00 I   .096970  .000033   .488212  .000068  I  .0233415  .0000056  0.9453 0.0039  I   -49.680     .330    -5.609     .340   .097060   .488230   .0233470   -49.600    -5.600  
+ 1 4 5 52004.00 I   .099146  .000034   .487316  .000068  I  .0222831  .0000061  1.1674 0.0048  I   -49.398     .330    -5.542     .340   .099230   .487290   .0222820   -49.600    -5.400  
+ 1 4 6 52005.00 I   .101639  .000034   .486689  .000076  I  .0210200  .0000078  1.3495 0.0055  I   -49.370     .330    -5.440     .340   .101620   .486670   .0210190   -50.000    -5.500  
+ 1 4 7 52006.00 I   .104372  .000034   .486451  .000081  I  .0196018  .0000091  1.4786 0.0067  I   -49.565     .205    -5.436     .340   .104330   .486450   .0196170   -50.200    -5.500  
+ 1 4 8 52007.00 I   .106684  .000029   .486230  .000070  I  .0181065  .0000108  1.4768 0.0066  I   -49.839     .740    -5.535     .340   .106650   .486230   .0181290   -50.300    -5.500  
+ 1 4 9 52008.00 I   .108446  .000054   .485677  .000078  I  .0167021  .0000095  1.3133 0.0075  I   -50.069     .156    -5.600     .340   .108450   .485660   .0167110   -50.400    -5.500  
+ 1 410 52009.00 I   .110408  .000053   .484787  .000077  I  .0155036  .0000103  1.0787 0.0068  I   -50.216     .156    -5.543     .340   .110420   .484780   .0155010   -50.400    -5.500  
+ 1 411 52010.00 I   .112966  .000050   .483921  .000076  I  .0145483  .0000097  0.8332 0.0065  I   -50.299     .156    -5.457     .340   .113020   .483870   .0145520   -50.300    -5.600  
+ 1 412 52011.00 I   .115877  .000049   .483273  .000073  I  .0138282  .0000078  0.6148 0.0058  I   -50.502     .193    -5.557     .340   .115940   .483200   .0138300   -50.000    -5.600  
+ 1 413 52012.00 I   .118552  .000049   .482764  .000070  I  .0133060  .0000062  0.4346 0.0057  I   -50.635     .193    -5.740     .340   .118580   .482740   .0132910   -49.700    -5.600  
+ 1 414 52013.00 I   .120993  .000048   .482036  .000069  I  .0129373  .0000082  0.3195 0.0054  I   -50.769     .193    -5.911     .340   .121010   .481980   .0129400   -49.500    -5.600  
+ 1 415 52014.00 I   .123648  .000030   .481138  .000065  I  .0126343  .0000089  0.3024 0.0054  I   -50.707     .740    -5.950     .340   .123680   .481070   .0126510   -49.100    -5.700  
+ 1 416 52015.00 I   .126521  .000050   .480220  .000061  I  .0122983  .0000071  0.3877 0.0056  I   -50.303     .299    -5.886     .340   .126590   .480190   .0123230   -48.800    -5.800  
+ 1 417 52016.00 I   .129576  .000061   .479260  .000069  I  .0118375  .0000067  0.5371 0.0050  I   -49.685     .285    -5.814     .340   .129610   .479320   .0118590   -48.600    -5.800  
+ 1 418 52017.00 I   .132758  .000060   .478579  .000071  I  .0112160  .0000070  0.7109 0.0049  I   -49.187     .285    -5.762     .340   .132870   .478610   .0112190   -48.500    -5.800  
+ 1 419 52018.00 I   .136404  .000060   .478052  .000070  I  .0104241  .0000071  0.8595 0.0048  I   -49.053     .285    -5.683     .340   .136310   .478060   .0104330   -48.600    -5.900  
+ 1 420 52019.00 I   .139400  .000061   .477581  .000070  I  .0095180  .0000065  0.9450 0.0052  I   -49.257     .285    -5.545     .340   .139470   .477590   .0095310   -48.800    -5.900  
+ 1 421 52020.00 I   .142322  .000063   .476918  .000059  I  .0085454  .0000075  0.9967 0.0056  I   -49.603     .336    -5.390     .340   .142300   .476880   .0085640   -49.100    -5.900  
+ 1 422 52021.00 I   .144945  .000047   .476117  .000058  I  .0075411  .0000092  0.9988 0.0061  I   -49.917     .256    -5.317     .340   .144920   .476040   .0075520   -49.300    -5.900  
+ 1 423 52022.00 I   .147340  .000050   .475404  .000049  I  .0065665  .0000095  0.9449 0.0069  I   -50.103     .177    -5.408     .106   .147380   .475340   .0065630   -49.600    -5.900  
+ 1 424 52023.00 I   .149333  .000050   .474680  .000048  I  .0056600  .0000102  0.8648 0.0069  I   -50.147     .177    -5.648     .106   .149330   .474630   .0056590   -49.800    -6.000  
+ 1 425 52024.00 I   .151014  .000050   .473687  .000041  I  .0048331  .0000100  0.7961 0.0066  I   -50.130     .177    -5.922     .106   .151040   .473690   .0048440   -49.800    -6.000  
+ 1 426 52025.00 I   .153471  .000050   .472377  .000041  I  .0040681  .0000085  0.7267 0.0068  I   -50.319     .160    -6.143     .145   .153320   .472380   .0040670   -49.800    -6.000  
+ 1 427 52026.00 I   .155789  .000051   .471026  .000044  I  .0033913  .0000091  0.6227 0.0068  I   -50.514     .160    -6.178     .145   .155790   .470940   .0033730   -49.700    -6.100  
+ 1 428 52027.00 I   .157806  .000050   .469570  .000044  I  .0028082  .0000106  0.5622 0.0110  I   -50.740     .160    -6.152     .145   .157800   .469490   .0028290   -49.500    -6.100  
+ 1 429 52028.00 I   .159603  .000022   .467985  .000029  I  .0022319  .0000200  0.6068 0.0070  I   -50.776     .740    -6.154     .340   .159600   .467950   .0022510   -49.300    -6.200  
+ 1 430 52029.00 I   .161444  .000040   .466243  .000051  I  .0015624  .0000090  0.7479 0.0108  I   -50.478     .317    -6.198     .340   .161460   .466200   .0015540   -49.100    -6.100  
+ 1 5 1 52030.00 I   .163356  .000072   .464304  .000091  I  .0007169  .0000083  0.9465 0.0062  I   -49.916     .303    -6.223     .340   .163170   .464260   .0006960   -49.000    -6.200  
+ 1 5 2 52031.00 I   .164950  .000071   .462522  .000090  I -.0003369  .0000084  1.1639 0.0059  I   -49.361     .303    -6.169     .340   .164950   .462500  -.0003420   -49.000    -6.200  
+ 1 5 3 52032.00 I   .167277  .000072   .461283  .000090  I -.0016044  .0000083  1.3619 0.0056  I   -49.158     .335    -6.060     .103   .167170   .461160  -.0015850   -49.100    -6.100  
+ 1 5 4 52033.00 I   .169451  .000074   .460079  .000091  I -.0030462  .0000075  1.5163 0.0055  I   -49.369     .335    -5.928     .103   .169470   .460060  -.0030340   -49.400    -6.200  
+ 1 5 5 52034.00 I   .171731  .000074   .458504  .000091  I -.0046043  .0000071  1.5725 0.0053  I   -49.905     .335    -5.879     .103   .171710   .458490  -.0046170   -49.600    -6.200  
+ 1 5 6 52035.00 I   .173634  .000070   .456540  .000090  I -.0061441  .0000075  1.4874 0.0045  I   -50.461     .328    -5.930     .130   .173660   .456560  -.0061660   -49.800    -5.900  
+ 1 5 7 52036.00 I   .175770  .000036   .454417  .000040  I -.0075479  .0000055  1.3086 0.0051  I   -50.780     .740    -5.998     .103   .175750   .454430  -.0075520   -50.200    -5.900  
+ 1 5 8 52037.00 I   .178361  .000036   .452275  .000039  I -.0087514  .0000070  1.0999 0.0043  I   -50.827     .740    -6.001     .103   .178400   .452300  -.0087500   -50.400    -5.900  
+ 1 5 9 52038.00 I   .181152  .000037   .450055  .000041  I -.0097479  .0000067  0.8917 0.0047  I   -50.758     .740    -5.956     .103   .181190   .450110  -.0097520   -50.500    -6.100  
+ 1 510 52039.00 I   .183899  .000031   .447804  .000040  I -.0105429  .0000064  0.7078 0.0048  I   -50.800     .740    -5.985     .340   .183850   .447870  -.0105440   -50.400    -6.200  
+ 1 511 52040.00 I   .186558  .000031   .445713  .000041  I -.0111874  .0000068  0.5955 0.0057  I   -50.907     .740    -6.067     .340   .186520   .445780  -.0111840   -50.300    -6.300  
+ 1 512 52041.00 I   .189162  .000028   .444034  .000022  I -.0117606  .0000095  0.5633 0.0055  I   -51.016     .740    -6.164     .340   .189130   .444060  -.0117580   -50.000    -6.400  
+ 1 513 52042.00 I   .191399  .000066   .442523  .000038  I -.0123357  .0000086  0.5963 0.0061  I   -50.989     .740    -6.207     .340   .191380   .442540  -.0123210   -49.800    -6.400  
+ 1 514 52043.00 I   .193310  .000069   .440749  .000046  I -.0129715  .0000075  0.6841 0.0053  I   -50.779     .231    -6.214     .340   .193240   .440740  -.0129560   -49.600    -6.400  
+ 1 515 52044.00 I   .194797  .000077   .438595  .000063  I -.0137153  .0000062  0.8060 0.0048  I   -50.458     .268    -6.243     .113   .194970   .438620  -.0137180   -49.500    -6.300  
+ 1 516 52045.00 I   .196885  .000076   .436321  .000061  I -.0145805  .0000061  0.9189 0.0043  I   -50.181     .268    -6.288     .113   .196880   .436330  -.0145870   -49.500    -6.300  
+ 1 517 52046.00 I   .198612  .000076   .433526  .000061  I -.0155485  .0000061  1.0181 0.0040  I   -50.143     .269    -6.271     .114   .198680   .433650  -.0155280   -49.800    -6.100  
+ 1 518 52047.00 I   .200793  .000076   .430724  .000061  I -.0166117  .0000051  1.1022 0.0043  I   -50.403     .269    -6.182     .114   .200760   .430710  -.0166130   -50.200    -6.000  
+ 1 519 52048.00 I   .203161  .000053   .427891  .000058  I -.0177380  .0000061  1.1414 0.0038  I   -50.912     .324    -6.028     .134   .203180   .427890  -.0177410   -50.500    -5.900  
+ 1 520 52049.00 I   .205174  .000060   .425164  .000072  I -.0188736  .0000055  1.1179 0.0039  I   -51.429     .459    -5.874     .155   .205110   .425180  -.0188620   -50.900    -5.900  
+ 1 521 52050.00 I   .206955  .000057   .422557  .000053  I -.0199526  .0000049  1.0310 0.0037  I   -51.730     .417    -5.801     .104   .206910   .422590  -.0199380   -51.200    -5.900  
+ 1 522 52051.00 I   .208746  .000055   .420125  .000053  I -.0209135  .0000050  0.8779 0.0034  I   -51.794     .417    -5.870     .104   .208850   .420250  -.0209080   -51.400    -6.000  
+ 1 523 52052.00 I   .210426  .000056   .417989  .000061  I -.0216892  .0000048  0.6671 0.0035  I   -51.787     .389    -6.069     .111   .210410   .418040  -.0216940   -51.400    -6.100  
+ 1 524 52053.00 I   .212039  .000057   .415612  .000062  I -.0222479  .0000048  0.4568 0.0032  I   -51.841     .390    -6.275     .103   .211890   .415630  -.0222490   -51.300    -6.300  
+ 1 525 52054.00 I   .213256  .000058   .413088  .000063  I -.0226142  .0000042  0.2808 0.0030  I   -51.985     .390    -6.385     .103   .213350   .413120  -.0226100   -51.200    -6.400  
+ 1 526 52055.00 I   .214679  .000047   .410347  .000042  I -.0228347  .0000036  0.1805 0.0035  I   -52.084     .252    -6.357     .340   .214620   .410370  -.0228330   -50.900    -6.500  
+ 1 527 52056.00 I   .215942  .000032   .407372  .000047  I -.0230140  .0000056  0.1968 0.0032  I   -52.048     .298    -6.267     .114   .215880   .407350  -.0230120   -50.600    -6.500  
+ 1 528 52057.00 I   .217326  .000033   .404456  .000051  I -.0232574  .0000053  0.3006 0.0043  I   -51.860     .226    -6.203     .340   .217320   .404480  -.0232580   -50.500    -6.400  
+ 1 529 52058.00 I   .219019  .000037   .401751  .000063  I -.0236287  .0000065  0.4449 0.0041  I   -51.561     .333    -6.177     .117   .219030   .401800  -.0236350   -50.600    -6.300  
+ 1 530 52059.00 I   .220923  .000039   .399153  .000064  I -.0241428  .0000062  0.5758 0.0045  I   -51.251     .333    -6.150     .117   .220930   .399220  -.0241480   -50.900    -6.200  
+ 1 531 52060.00 I   .222844  .000037   .396603  .000062  I -.0247593  .0000062  0.6435 0.0043  I   -51.097     .331    -6.125     .340   .222860   .396660  -.0247580   -51.400    -6.000  
+ 1 6 1 52061.00 I   .224462  .000037   .394024  .000063  I -.0254085  .0000061  0.6472 0.0049  I   -51.244     .331    -6.036     .340   .224420   .394050  -.0253810   -52.000    -5.900  
+ 1 6 2 52062.00 I   .225856  .000043   .391301  .000067  I -.0260285  .0000075  0.5753 0.0047  I   -51.720     .331    -5.948     .340   .225790   .391340  -.0260010   -52.700    -5.800  
+ 1 6 3 52063.00 I   .227208  .000067   .388448  .000070  I -.0265294  .0000072  0.4143 0.0049  I   -52.338     .329    -5.884     .340   .227200   .388480  -.0265140   -53.100    -5.700  
+ 1 6 4 52064.00 I   .228755  .000066   .385506  .000065  I -.0268396  .0000062  0.2008 0.0050  I   -52.808     .154    -5.848     .340   .228700   .385520  -.0268270   -52.600    -5.600  
+ 1 6 5 52065.00 I   .230305  .000066   .382461  .000063  I -.0269233  .0000069 -0.0358 0.0047  I   -52.956     .154    -5.839     .340   .230520   .382550  -.0268950   -52.500    -5.700  
+ 1 6 6 52066.00 I   .232394  .000065   .379951  .000061  I -.0267701  .0000070 -0.2660 0.0050  I   -52.868     .154    -5.872     .340   .232380   .380060  -.0267660   -52.400    -5.800  
+ 1 6 7 52067.00 I   .234494  .000065   .377269  .000062  I -.0264129  .0000072 -0.4314 0.0045  I   -52.701     .175    -5.941     .340   .234400   .377400  -.0264180   -52.200    -6.100  
+ 1 6 8 52068.00 I   .236427  .000064   .374759  .000060  I -.0259398  .0000058 -0.4994 0.0049  I   -52.706     .175    -6.044     .340   .236450   .374730  -.0259510   -51.900    -6.400  
+ 1 6 9 52069.00 I   .238000  .000036   .372435  .000054  I -.0254444  .0000067 -0.4764 0.0048  I   -52.778     .211    -6.101     .340   .237980   .372450  -.0254460   -51.800    -6.600  
+ 1 610 52070.00 I   .239149  .000032   .370013  .000053  I -.0250118  .0000076 -0.3791 0.0043  I   -52.859     .194    -6.102     .108   .239120   .370100  -.0250090   -51.800    -6.700  
+ 1 611 52071.00 I   .240122  .000057   .367384  .000058  I -.0247004  .0000054 -0.2394 0.0049  I   -52.955     .130    -6.113     .155   .240140   .367450  -.0246970   -51.900    -6.800  
+ 1 612 52072.00 I   .241075  .000056   .364662  .000077  I -.0245354  .0000063 -0.0925 0.0046  I   -53.041     .159    -6.179     .154   .241010   .364630  -.0245310   -52.300    -6.700  
+ 1 613 52073.00 I   .241862  .000055   .361668  .000077  I -.0245107  .0000074  0.0404 0.0049  I   -53.044     .159    -6.245     .154   .241850   .361720  -.0245030   -52.700    -6.400  
+ 1 614 52074.00 I   .243205  .000058   .358359  .000078  I -.0245966  .0000074  0.1143 0.0053  I   -53.123     .135    -6.189     .172   .243170   .358550  -.0245960   -53.300    -6.200  
+ 1 615 52075.00 I   .244809  .000059   .355242  .000078  I -.0247260  .0000075  0.1481 0.0059  I   -53.318     .135    -6.073     .172   .244820   .355300  -.0247330   -53.700    -5.900  
+ 1 616 52076.00 I   .246266  .000086   .352304  .000074  I -.0248955  .0000093  0.1900 0.0064  I   -53.895     .132    -5.936     .196   .246230   .352330  -.0248900   -54.300    -5.700  
+ 1 617 52077.00 I   .247366  .000074   .349444  .000070  I -.0250968  .0000104  0.2043 0.0072  I   -54.637     .106    -5.811     .183   .247330   .349550  -.0250730   -54.600    -5.500  
+ 1 618 52078.00 I   .248189  .000080   .346490  .000067  I -.0252880  .0000111  0.1705 0.0069  I   -55.150     .280    -5.682     .128   .248170   .346560  -.0252650   -54.800    -5.400  
+ 1 619 52079.00 I   .248439  .000077   .343539  .000069  I -.0254205  .0000091  0.0850 0.0073  I   -55.314     .298    -5.593     .134   .248480   .343620  -.0254190   -54.900    -5.500  
+ 1 620 52080.00 I   .248322  .000073   .340726  .000069  I -.0254431  .0000094 -0.0451 0.0066  I   -55.352     .298    -5.648     .134   .248350   .340790  -.0254530   -54.900    -5.600  
+ 1 621 52081.00 I   .248209  .000073   .337798  .000069  I -.0253430  .0000096 -0.1369 0.0064  I   -55.457     .306    -5.813     .340   .248270   .337830  -.0253190   -54.800    -5.700  
+ 1 622 52082.00 I   .248422  .000039   .334848  .000063  I -.0251893  .0000086 -0.1686 0.0066  I   -55.519     .306    -6.047     .340   .248430   .334900  -.0252160   -54.500    -5.800  
+ 1 623 52083.00 I   .248662  .000040   .331828  .000064  I -.0250212  .0000092 -0.1540 0.0058  I   -55.387     .306    -6.118     .340   .248720   .331890  -.0249870   -54.400    -6.000  
+ 1 624 52084.00 I   .248931  .000042   .328775  .000072  I -.0249081  .0000078 -0.0592 0.0063  I   -55.131     .234    -6.019     .340   .248970   .328800  -.0248170   -54.300    -6.100  
+ 1 625 52085.00 I   .249514  .000047   .325692  .000079  I -.0249313  .0000085  0.1209 0.0056  I   -54.975     .134    -5.893     .340   .249480   .325730  -.0248560   -54.300    -6.100  
+ 1 626 52086.00 I   .250305  .000066   .322581  .000103  I -.0251681  .0000080  0.3557 0.0057  I   -55.024     .191    -5.851     .340   .250410   .322600  -.0251460   -54.600    -6.100  
+ 1 627 52087.00 I   .251263  .000065   .319736  .000103  I -.0256329  .0000077  0.5613 0.0054  I   -55.177     .191    -5.870     .340   .251270   .319770  -.0256330   -54.900    -6.000  
+ 1 628 52088.00 I   .252011  .000065   .317012  .000103  I -.0262455  .0000072  0.6315 0.0051  I   -55.395     .217    -5.880     .340   .251940   .316990  -.0262050   -55.500    -6.000  
+ 1 629 52089.00 I   .252360  .000066   .313934  .000104  I -.0268484  .0000066  0.5593 0.0053  I   -55.492     .217    -5.840     .340   .252380   .313920  -.0268050   -56.000    -5.900  
+ 1 630 52090.00 I   .252992  .000058   .310715  .000092  I -.0273415  .0000078  0.4193 0.0052  I   -55.700     .259    -5.779     .340   .252990   .310720  -.0273470   -56.400    -5.800  
+ 1 7 1 52091.00 I   .253731  .000063   .307575  .000087  I -.0276738  .0000081  0.2396 0.0058  I   -56.098     .425    -5.717     .340   .253780   .307610  -.0277010   -56.600    -5.700  
+ 1 7 2 52092.00 I   .253964  .000047   .304394  .000058  I -.0278178  .0000086  0.0511 0.0060  I   -56.574     .347    -5.664     .340   .253940   .304430  -.0278370   -56.600    -5.700  
+ 1 7 3 52093.00 I   .253753  .000046   .301145  .000057  I -.0277834  .0000088 -0.1151 0.0062  I   -56.892     .347    -5.662     .340   .253750   .301190  -.0277890   -56.300    -5.700  
+ 1 7 4 52094.00 I   .254215  .000045   .298188  .000056  I -.0276027  .0000088 -0.2358 0.0064  I   -56.929     .347    -5.763     .340  0.254090  0.298230 -0.0276160   -56.000    -5.800  
+ 1 7 5 52095.00 I   .254232  .000044   .295468  .000055  I -.0273324  .0000092 -0.2945 0.0065  I   -56.882     .421    -5.943     .340  0.254290  0.295550 -0.0273580   -56.900    -6.000  
+ 1 7 6 52096.00 I   .253911  .000046   .292761  .000059  I -.0270278  .0000095 -0.3110 0.0082  I   -56.746     .421    -6.098     .340  0.253890  0.292770 -0.0270370   -57.200    -6.100  
+ 1 7 7 52097.00 I   .253524  .000032   .290040  .000051  I -.0267282  .0000135 -0.2743 0.0061  I   -56.724     .740    -6.097     .340  0.253530  0.290050 -0.0267140   -57.300    -6.100  
+ 1 7 8 52098.00 I   .252708  .000038   .287256  .000034  I -.0264986  .0000075 -0.1795 0.0078  I   -56.847     .740    -5.961     .340  0.252700  0.287310 -0.0264760   -57.300    -6.000  
+ 1 7 9 52099.00 I   .251813  .000041   .284150  .000049  I -.0263669  .0000079 -0.0917 0.0050  I   -57.119     .195    -5.844     .127  0.251720  0.284140 -0.0263590   -57.300    -6.000  
+ 1 710 52100.00 I   .251686  .000041   .280793  .000052  I -.0263067  .0000067 -0.0296 0.0051  I   -57.433     .365    -5.847     .101  0.251690  0.280760 -0.0263210   -57.300    -6.000  
+ 1 711 52101.00 I   .251944  .000042   .277620  .000052  I -.0263047  .0000066  0.0234 0.0046  I   -57.574     .365    -5.891     .101  0.251980  0.277670 -0.0263080   -57.300    -5.900  
+ 1 712 52102.00 I   .251507  .000041   .274653  .000050  I -.0263530  .0000064  0.0747 0.0042  I   -57.562     .303    -5.843     .104  0.251470  0.274620 -0.0263440   -57.600    -5.900  
+ 1 713 52103.00 I   .250977  .000042   .271726  .000050  I -.0264534  .0000051  0.1238 0.0045  I   -57.597     .303    -5.707     .104  0.250880  0.271650 -0.0264360   -58.100    -5.800  
+ 1 714 52104.00 I   .250420  .000038   .268800  .000055  I -.0265857  .0000062  0.1281 0.0039  I   -58.082     .303    -5.628     .104  0.250360  0.268790 -0.0265840   -58.500    -5.800  
+ 1 715 52105.00 I   .249489  .000041   .265680  .000041  I -.0266888  .0000060  0.0699 0.0053  I   -58.901     .440    -5.654     .340  0.249470  0.265690 -0.0266870   -59.000    -5.900  
+ 1 716 52106.00 I   .248487  .000054   .262337  .000040  I -.0267108  .0000086 -0.0323 0.0048  I   -59.563     .317    -5.671     .340  0.248500  0.262310 -0.0266970   -59.300    -5.800  
+ 1 717 52107.00 I   .248045  .000064   .259044  .000056  I -.0266166  .0000074 -0.1572 0.0056  I   -59.795     .317    -5.608     .340  0.248020  0.259030 -0.0265990   -59.400    -5.700  
+ 1 718 52108.00 I   .247899  .000062   .255898  .000054  I -.0263879  .0000073 -0.3075 0.0052  I   -59.785     .317    -5.582     .340  0.247930  0.255930 -0.0263840   -59.300    -5.600  
+ 1 719 52109.00 I   .247886  .000061   .252693  .000054  I -.0260054  .0000073 -0.4463 0.0052  I   -59.727     .264    -5.746     .340  0.247880  0.252780 -0.0260360   -58.700    -5.500  
+ 1 720 52110.00 I   .247644  .000062   .249621  .000055  I -.0255194  .0000073 -0.5128 0.0049  I   -59.684     .264    -5.995     .340  0.247620  0.249630 -0.0255320   -58.100    -5.400  
+ 1 721 52111.00 I   .247124  .000055   .246549  .000058  I -.0250172  .0000066 -0.4672 0.0051  I   -59.436     .264    -6.113     .340  0.247110  0.246510 -0.0250070   -57.700    -5.400  
+ 1 722 52112.00 I   .246308  .000062   .243497  .000077  I -.0246244  .0000072 -0.3034 0.0066  I   -59.081     .417    -6.020     .138  0.246310  0.243480 -0.0245960   -57.500    -5.500  
+ 1 723 52113.00 I   .245303  .000050   .240604  .000071  I -.0244283  .0000115 -0.0859 0.0062  I   -58.929     .293    -5.875     .340  0.245290  0.240600 -0.0243980   -57.800    -5.500  
+ 1 724 52114.00 I   .244181  .000052   .237844  .000067  I -.0244490  .0000100  0.1207 0.0080  I   -59.145     .329    -5.833     .112  0.244230  0.237850 -0.0244370   -58.400    -5.700  
+ 1 725 52115.00 I   .243624  .000051   .235369  .000063  I -.0246366  .0000110  0.2279 0.0074  I   -59.594     .329    -5.858     .112  0.243550  0.235400 -0.0246340   -59.500    -5.900  
+ 1 726 52116.00 I   .242989  .000051   .232644  .000063  I -.0248666  .0000110  0.2204 0.0078  I   -60.059     .330    -5.815     .114  0.242970  0.232700 -0.0248580   -60.200    -6.000  
+ 1 727 52117.00 I   .242022  .000050   .229827  .000063  I -.0250474  .0000111  0.1229 0.0077  I   -60.239     .330    -5.745     .114  0.242000  0.229780 -0.0250340   -60.800    -6.100  
+ 1 728 52118.00 I   .240668  .000027   .226879  .000040  I -.0250822  .0000109 -0.0650 0.0077  I   -60.241     .230    -5.735     .113  0.240630  0.226830 -0.0250700   -61.100    -6.100  
+ 1 729 52119.00 I   .239018  .000029   .223779  .000041  I -.0249085  .0000107 -0.2794 0.0072  I   -60.276     .335    -5.819     .172  0.239010  0.223820 -0.0249070   -61.200    -6.000  
+ 1 730 52120.00 I   .237308  .000022   .220720  .000104  I -.0245315  .0000094 -0.4693 0.0060  I   -60.493     .414    -5.916     .340  0.237300  0.220680 -0.0245330   -61.000    -6.100  
+ 1 731 52121.00 I   .235872  .000045   .217961  .000107  I -.0239862  .0000053 -0.6102 0.0056  I   -60.798     .414    -5.972     .340  0.235850  0.217860 -0.0239860   -60.700    -6.100  
+ 1 8 1 52122.00 I   .234708  .000045   .215461  .000111  I -.0233285  .0000060 -0.6990 0.0040  I   -60.947     .414    -6.043     .340  0.234740  0.215420 -0.0233300   -60.300    -6.000  
+ 1 8 2 52123.00 I   .233413  .000045   .213496  .000111  I -.0226143  .0000060 -0.7095 0.0042  I   -60.854     .432    -6.157     .340  0.233290  0.213440 -0.0226280   -60.000    -6.100  
+ 1 8 3 52124.00 I   .230934  .000046   .211330  .000113  I -.0219419  .0000058 -0.6228 0.0042  I   -60.604     .415    -6.249     .340  0.230970  0.211380 -0.0219510   -59.900    -6.000  
+ 1 8 4 52125.00 I   .228252  .000047   .208668  .000113  I -.0213853  .0000059 -0.4862 0.0040  I   -60.455     .415    -6.162     .340  0.228190  0.208600 -0.0213740   -59.700    -5.900  
+ 1 8 5 52126.00 I   .225848  .000061   .205783  .000079  I -.0209760  .0000054 -0.3301 0.0050  I   -60.486     .151    -5.890     .340  0.225820  0.205680 -0.0209610   -59.700    -5.700  
+ 1 8 6 52127.00 I   .223854  .000045   .203137  .000065  I -.0207247  .0000080 -0.1752 0.0045  I   -60.672     .252    -5.623     .340  0.223840  0.203130 -0.0207250   -59.900    -5.700  
+ 1 8 7 52128.00 I   .221846  .000052   .200695  .000087  I -.0206206  .0000073 -0.0354 0.0057  I   -60.888     .234    -5.527     .340  0.221850  0.200830 -0.0206260   -60.000    -5.600  
+ 1 8 8 52129.00 I   .219641  .000050   .198602  .000082  I -.0206566  .0000081  0.1125 0.0055  I   -60.949     .234    -5.555     .340  0.219630  0.198630 -0.0206580   -60.300    -5.600  
+ 1 8 9 52130.00 I   .217156  .000050   .196301  .000081  I -.0208393  .0000082  0.2417 0.0057  I   -60.873     .149    -5.546     .340  0.217110  0.196290 -0.0208430   -60.600    -5.600  
+ 1 810 52131.00 I   .214270  .000052   .194048  .000082  I -.0211293  .0000079  0.3390 0.0061  I   -60.766     .149    -5.456     .340  0.214300  0.194010 -0.0211170   -60.900    -5.700  
+ 1 811 52132.00 I   .211292  .000038   .191558  .000079  I -.0214996  .0000089  0.3834 0.0058  I   -61.056     .175    -5.438     .340  0.211260  0.191530 -0.0215140   -61.300    -5.700  
+ 1 812 52133.00 I   .208443  .000041   .188809  .000082  I -.0218695  .0000086  0.3482 0.0053  I   -61.739     .114    -5.583     .340  0.208400  0.188780 -0.0219090   -61.600    -5.800  
+ 1 813 52134.00 I   .205986  .000044   .186155  .000048  I -.0221796  .0000059  0.2638 0.0048  I   -62.391     .185    -5.769     .340  0.206010  0.186150 -0.0222080   -61.900    -5.800  
+ 1 814 52135.00 I   .203661  .000076   .183652  .000046  I -.0223872  .0000042  0.1497 0.0036  I   -62.647     .185    -5.835     .340  0.203770  0.183640 -0.0223950   -62.000    -5.900  
+ 1 815 52136.00 I   .201030  .000075   .181204  .000047  I -.0224832  .0000042  0.0482 0.0030  I   -62.552     .185    -5.807     .340  0.201050  0.181170 -0.0224850   -62.000    -5.900  
+ 1 816 52137.00 I   .198726  .000076   .178978  .000052  I -.0225064  .0000042  0.0151 0.0030  I   -62.292     .173    -5.825     .340  0.198400  0.178940 -0.0225100   -62.000    -5.900  
+ 1 817 52138.00 I   .195935  .000076   .176831  .000047  I -.0225537  .0000044  0.1029 0.0033  I   -62.068     .173    -5.896     .340  0.196010  0.176810 -0.0225520   -61.800    -5.800  
+ 1 818 52139.00 I   .193417  .000076   .174597  .000045  I -.0227462  .0000050  0.2926 0.0029  I   -61.794     .172    -5.859     .340  0.193410  0.174570 -0.0227320   -61.700    -5.700  
+ 1 819 52140.00 I   .190985  .000121   .172340  .000074  I -.0231581  .0000037  0.5398 0.0032  I   -61.555     .740    -5.665     .340  0.190970  0.172310 -0.0231440   -61.600    -5.700  
+ 1 820 52141.00 I   .188799  .000103   .170234  .000072  I -.0238223  .0000041  0.7765 0.0031  I   -61.531     .219    -5.490     .340  0.188770  0.170270 -0.0238140   -61.700    -5.600  
+ 1 821 52142.00 I   .186205  .000098   .168172  .000069  I -.0246828  .0000050  0.9284 0.0033  I   -61.781     .318    -5.490     .340  0.186480  0.168150 -0.0246870   -62.000    -5.500  
+ 1 822 52143.00 I   .183852  .000096   .165911  .000064  I -.0256448  .0000051  0.9776 0.0035  I   -62.198     .318    -5.571     .340  0.183820  0.165890 -0.0256510   -62.300    -5.500  
+ 1 823 52144.00 I   .181210  .000098   .163907  .000064  I -.0265834  .0000049  0.8630 0.0034  I   -62.590     .233    -5.547     .340  0.181090  0.163910 -0.0265740   -62.800    -5.500  
+ 1 824 52145.00 I   .178677  .000099   .162045  .000065  I -.0273417  .0000045  0.6609 0.0040  I   -62.832     .233    -5.426     .340  0.178690  0.162090 -0.0273220   -63.200    -5.500  
+ 1 825 52146.00 I   .176475  .000040   .160131  .000038  I -.0278971  .0000064  0.4344 0.0045  I   -62.813     .233    -5.430     .340  0.176480  0.160180 -0.0279020   -63.600    -5.600  
+ 1 826 52147.00 I   .174361  .000038   .158092  .000099  I -.0282024  .0000077  0.1835 0.0048  I   -62.640     .213    -5.661     .340  0.174360  0.158120 -0.0282170   -63.700    -5.700  
+ 1 827 52148.00 I   .172421  .000039   .156000  .000101  I -.0282814  .0000072 -0.0154 0.0051  I   -62.562     .740    -5.953     .340  0.172380  0.156010 -0.0282800   -63.600    -5.800  
+ 1 828 52149.00 I   .170433  .000045   .153890  .000104  I -.0281922  .0000068 -0.1524 0.0050  I   -62.687     .740    -6.089     .340  0.170520  0.154080 -0.0281840   -63.300    -5.900  
+ 1 829 52150.00 I   .168322  .000042   .152401  .000102  I -.0279987  .0000070 -0.2230 0.0049  I   -62.825     .740    -6.058     .340  0.168310  0.152450 -0.0280010   -62.800    -6.000  
+ 1 830 52151.00 I   .166083  .000041   .150982  .000102  I -.0277811  .0000070 -0.1888 0.0048  I   -62.747     .740    -5.994     .340  0.165940  0.151010 -0.0277760   -62.300    -5.900  
+ 1 831 52152.00 I   .163282  .000042   .149983  .000105  I -.0276358  .0000066 -0.1106 0.0050  I   -62.479     .740    -5.946     .340  0.163320  0.150020 -0.0276410   -61.900    -5.800  
+ 1 9 1 52153.00 I   .160290  .000042   .149077  .000087  I -.0275677  .0000072 -0.0092 0.0055  I   -62.233     .525    -5.817     .123  0.160240  0.149120 -0.0275220   -61.500    -5.800  
+ 1 9 2 52154.00 I   .157071  .000044   .147756  .000091  I -.0276349  .0000089  0.1446 0.0062  I   -62.123     .772    -5.551     .180  0.157030  0.147790 -0.0275400   -61.300    -5.500  
+ 1 9 3 52155.00 I   .153925  .000051   .146230  .000078  I -.0278569  .0000101  0.2984 0.0063  I   -62.097     .537    -5.258     .116  0.153930  0.146230 -0.0278290   -61.700    -4.700  
+ 1 9 4 52156.00 I   .150804  .000061   .144646  .000078  I -.0282213  .0000089  0.4210 0.0066  I   -62.065     .446    -5.105     .113  0.150800  0.144700 -0.0282350   -61.800    -4.900  
+ 1 9 5 52157.00 I   .147452  .000061   .142777  .000078  I -.0286907  .0000086  0.5191 0.0062  I   -61.974     .446    -5.114     .113  0.147470  0.142820 -0.0286930   -61.800    -5.100  
+ 1 9 6 52158.00 I   .144611  .000061   .140770  .000079  I -.0292332  .0000086  0.5383 0.0056  I   -61.811     .438    -5.156     .110  0.144570  0.140680 -0.0292230   -61.700    -5.100  
+ 1 9 7 52159.00 I   .141812  .000057   .139100  .000041  I -.0297340  .0000071  0.4575 0.0055  I   -61.701     .127    -5.158     .340  0.141940  0.139040 -0.0296950   -61.500    -5.000  
+ 1 9 8 52160.00 I   .139469  .000054   .137762  .000045  I -.0301363  .0000067  0.3410 0.0059  I   -61.821     .127    -5.181     .340  0.139480  0.137780 -0.0301450   -61.400    -4.900  
+ 1 9 9 52161.00 I   .137173  .000049   .136680  .000079  I -.0304103  .0000093  0.2075 0.0060  I   -62.223     .247    -5.312     .110  0.137220  0.136710 -0.0304570   -61.500    -5.000  
+ 1 910 52162.00 I   .134793  .000057   .135598  .000084  I -.0305519  .0000099  0.0759 0.0068  I   -62.691     .427    -5.504     .130  0.134820  0.135600 -0.0305810   -61.600    -5.200  
+ 1 911 52163.00 I   .131937  .000083   .134534  .000088  I -.0305697  .0000099 -0.0328 0.0070  I   -62.921     .359    -5.625     .119  0.132050  0.134440 -0.0305690   -61.800    -5.400  
+ 1 912 52164.00 I   .128799  .000081   .133191  .000085  I -.0305005  .0000100 -0.0989 0.0070  I   -62.802     .359    -5.617     .119  0.128800  0.133200 -0.0304980   -61.900    -5.600  
+ 1 913 52165.00 I   .125569  .000081   .132151  .000083  I -.0304014  .0000100 -0.0765 0.0071  I   -62.508     .376    -5.557     .112  0.125660  0.132030 -0.0304240   -61.800    -5.700  
+ 1 914 52166.00 I   .122462  .000082   .130991  .000082  I -.0303880  .0000101  0.0674 0.0070  I   -62.083     .376    -5.391     .112  0.122490  0.130970 -0.0304260   -61.700    -5.700  
+ 1 915 52167.00 I   .118823  .000083   .129921  .000062  I -.0305704  .0000099  0.3140 0.0070  I   -61.758     .419    -5.100     .340  0.118860  0.129900 -0.0305830   -61.600    -5.500  
+ 1 916 52168.00 I   .115134  .000073   .129008  .000051  I -.0310339  .0000098  0.6144 0.0063  I   -61.672     .740    -4.738     .340  0.115160  0.129020 -0.0310310   -61.700    -5.400  
+ 1 917 52169.00 I   .111702  .000050   .128161  .000040  I -.0317828  .0000077  0.8655 0.0055  I   -61.835     .193    -4.519     .134  0.111710  0.128160 -0.0317820   -61.700    -5.200  
+ 1 918 52170.00 I   .108312  .000050   .127305  .000041  I -.0327193  .0000051  0.9797 0.0047  I   -62.089     .181    -4.590     .188  0.108320  0.127300 -0.0327230   -62.000    -5.100  
+ 1 919 52171.00 I   .104560  .000049   .126560  .000041  I -.0336945  .0000055  0.9507 0.0037  I   -62.278     .181    -4.809     .188  0.104560  0.126580 -0.0336970   -62.300    -5.000  
+ 1 920 52172.00 I   .101202  .000049   .125935  .000041  I -.0345894  .0000054  0.8276 0.0038  I   -62.399     .181    -4.899     .188  0.101130  0.125960 -0.0345850   -62.500    -4.900  
+ 1 921 52173.00 I   .097832  .000051   .125409  .000046  I -.0353300  .0000052  0.6454 0.0052  I   -62.485     .181    -4.809     .188  0.097880  0.125390 -0.0353410   -62.700    -4.900  
+ 1 922 52174.00 I   .094515  .000052   .124864  .000045  I -.0358762  .0000088  0.4514 0.0061  I   -62.467     .181    -4.784     .188  0.094530  0.124880 -0.0358710   -62.800    -5.000  
+ 1 923 52175.00 I   .091429  .000052   .124165  .000040  I -.0362401  .0000111  0.2794 0.0077  I   -62.293     .196    -5.012     .204  0.091460  0.124210 -0.0362300   -62.700    -5.000  
+ 1 924 52176.00 I   .088536  .000068   .123182  .000073  I -.0364427  .0000127  0.1304 0.0074  I   -62.084     .158    -5.354     .226  0.088580  0.123200 -0.0364470   -62.500    -5.200  
+ 1 925 52177.00 I   .085411  .000069   .122051  .000100  I -.0365260  .0000097  0.0565 0.0080  I   -61.995     .196    -5.511     .194  0.085470  0.122040 -0.0365380   -62.100    -5.400  
+ 1 926 52178.00 I   .082376  .000068   .120940  .000099  I -.0365832  .0000097  0.0649 0.0069  I   -61.970     .196    -5.388     .194  0.082290  0.120950 -0.0365820   -61.800    -5.500  
+ 1 927 52179.00 I   .078852  .000069   .119978  .000099  I -.0366762  .0000098  0.1343 0.0063  I   -61.826     .196    -5.158     .194  0.078960  0.119830 -0.0366910   -61.300    -5.600  
+ 1 928 52180.00 I   .075375  .000070   .119070  .000101  I -.0368739  .0000079  0.2699 0.0059  I   -61.540     .196    -5.002     .194  0.075340  0.118980 -0.0368680   -60.900    -5.400  
+ 1 929 52181.00 I   .071799  .000055   .118503  .000103  I -.0372183  .0000067  0.4125 0.0047  I   -61.327     .235    -4.868     .238  0.071810  0.118500 -0.0372090   -60.500    -5.300  
+ 1 930 52182.00 I   .068626  .000038   .118099  .000091  I -.0376932  .0000052  0.5382 0.0045  I   -61.159     .229    -4.730     .155  0.068640  0.118080 -0.0376640   -60.200    -5.100  
+ 110 1 52183.00 I   .065428  .000028   .117780  .000066  I -.0382971  .0000061  0.6713 0.0038  I   -61.020     .193    -4.515     .234  0.065420  0.117780 -0.0382510   -60.200    -4.800  
+ 110 2 52184.00 I   .061701  .000034   .117636  .000064  I -.0390380  .0000055  0.8111 0.0043  I   -60.852     .164    -4.319     .200  0.061680  0.117630 -0.0390180   -60.200    -4.600  
+ 110 3 52185.00 I   .057927  .000034   .117397  .000065  I -.0398955  .0000061  0.8794 0.0041  I   -60.708     .164    -4.243     .200  0.057890  0.117430 -0.0399000   -60.400    -4.400  
+ 110 4 52186.00 I   .054765  .000032   .117299  .000064  I -.0407777  .0000060  0.8904 0.0041  I   -60.638     .164    -4.286     .200  0.054770  0.117230 -0.0407620   -60.300    -4.300  
+ 110 5 52187.00 I   .052007  .000031   .117381  .000064  I -.0416711  .0000056  0.8858 0.0043  I   -60.609     .164    -4.376     .200  0.052010  0.117390 -0.0416520   -60.200    -4.300  
+ 110 6 52188.00 I   .048970  .000030   .117600  .000061  I -.0425184  .0000063  0.7883 0.0048  I   -60.594     .173    -4.416     .233  0.049030  0.117650 -0.0425430   -60.100    -4.300  
+ 110 7 52189.00 I   .045981  .000030   .117726  .000036  I -.0432326  .0000077  0.6454 0.0053  I   -60.665     .740    -4.487     .116  0.045990  0.117750 -0.0432790   -60.100    -4.400  
+ 110 8 52190.00 I   .043041  .000044   .117743  .000038  I -.0438207  .0000086  0.5370 0.0066  I   -60.806     .139    -4.557     .177  0.043020  0.117720 -0.0438420   -60.200    -4.500  
+ 110 9 52191.00 I   .039838  .000051   .117721  .000033  I -.0443233  .0000106  0.4788 0.0067  I   -60.881     .172    -4.608     .238  0.039870  0.117720 -0.0443200   -60.300    -4.500  
+ 11010 52192.00 I   .036029  .000043   .117573  .000037  I -.0448060  .0000104  0.5037 0.0075  I   -60.725     .172    -4.602     .238  0.036070  0.117610 -0.0447990   -60.500    -4.400  
+ 11011 52193.00 I   .031916  .000043   .117281  .000037  I -.0453585  .0000105  0.6119 0.0073  I   -60.301     .172    -4.494     .238  0.031890  0.117280 -0.0453630   -60.700    -4.100  
+ 11012 52194.00 I   .027453  .000044   .117238  .000040  I -.0460502  .0000102  0.7812 0.0074  I   -59.763     .172    -4.243     .238  0.027530  0.117290 -0.0460900   -60.700    -3.700  
+ 11013 52195.00 I   .023095  .000044   .117487  .000041  I -.0469466  .0000103  1.0276 0.0101  I   -59.434     .158    -3.774     .178  0.023160  0.117520 -0.0469310   -60.800    -3.200  
+ 11014 52196.00 I   .019346  .000048   .117756  .000045  I -.0481163  .0000174  1.3066 0.0067  I   -59.428     .161    -3.358     .168  0.019360  0.117720 -0.0480670   -60.700    -2.900  
+ 11015 52197.00 I   .015946  .000043   .118233  .000049  I -.0495361  .0000087  1.5143 0.0097  I   -59.739     .256    -3.137     .340  0.015960  0.118200 -0.0495160   -60.700    -2.900  
+ 11016 52198.00 I   .012831  .000046   .118897  .000053  I -.0511037  .0000084  1.5982 0.0059  I   -60.061     .214    -3.245     .222  0.012800  0.118850 -0.0511100   -60.500    -3.100  
+ 11017 52199.00 I   .009771  .000047   .119369  .000051  I -.0526870  .0000081  1.5455 0.0060  I   -60.138     .230    -3.553     .197  0.009780  0.119370 -0.0526840   -60.300    -3.600  
+ 11018 52200.00 I   .006624  .000051   .119915  .000054  I -.0541499  .0000087  1.3586 0.0060  I   -59.996     .230    -3.771     .197  0.006640  0.119920 -0.0541550   -60.100    -4.100  
+ 11019 52201.00 I   .003117  .000053   .120419  .000059  I -.0553755  .0000088  1.0858 0.0061  I   -59.832     .230    -3.767     .197  0.003130  0.120460 -0.0553510   -59.800    -4.500  
+ 11020 52202.00 I  -.000633  .000044   .121004  .000056  I -.0563214  .0000086  0.8128 0.0056  I   -59.724     .248    -3.718     .211 -0.000630  0.121030 -0.0563460   -59.700    -4.900  
+ 11021 52203.00 I  -.004548  .000066   .121787  .000067  I -.0570287  .0000070  0.6226 0.0053  I   -59.583     .218    -3.803     .208 -0.004550  0.121810 -0.0571110   -59.400    -5.000  
+ 11022 52204.00 I  -.008592  .000064   .122717  .000064  I -.0575979  .0000061  0.5264 0.0045  I   -59.362     .265    -3.998     .340 -0.008610  0.122720 -0.0576820   -59.200    -4.700  
+ 11023 52205.00 I  -.012764  .000089   .123691  .000066  I -.0581052  .0000056  0.5014 0.0039  I   -59.119     .217    -4.072     .340 -0.012680  0.123650 -0.0581450   -58.900    -4.400  
+ 11024 52206.00 I  -.016471  .000091   .124646  .000067  I -.0586147  .0000050  0.5183 0.0037  I   -58.891     .217    -3.907     .340 -0.016540  0.124620 -0.0586110   -58.500    -4.000  
+ 11025 52207.00 I  -.020057  .000088   .125720  .000060  I -.0591595  .0000048  0.5882 0.0032  I   -58.646     .217    -3.644     .340 -0.020140  0.125640 -0.0591720   -57.900    -3.700  
+ 11026 52208.00 I  -.023592  .000090   .126873  .000061  I -.0598171  .0000039  0.7361 0.0048  I   -58.399     .217    -3.492     .340 -0.023560  0.126840 -0.0598170   -57.300    -3.400  
+ 11027 52209.00 I  -.027016  .000080   .128062  .000050  I -.0606384  .0000083  0.9041 0.0036  I   -58.233     .199    -3.479     .340 -0.027010  0.128060 -0.0606310   -56.900    -3.300  
+ 11028 52210.00 I  -.030363  .000080   .129127  .000046  I -.0616118  .0000060  1.0317 0.0050  I   -58.165     .740    -3.457     .340 -0.030400  0.129080 -0.0616080   -56.600    -3.200  
+ 11029 52211.00 I  -.033489  .000047   .130207  .000045  I -.0626844  .0000056  1.1070 0.0041  I   -58.113     .127    -3.292     .209 -0.033430  0.130180 -0.0626800   -56.600    -3.200  
+ 11030 52212.00 I  -.036312  .000041   .131409  .000056  I -.0638054  .0000056  1.1210 0.0040  I   -58.035     .250    -3.013     .182 -0.036440  0.131310 -0.0637910   -56.900    -3.100  
+ 11031 52213.00 I  -.039723  .000040   .132495  .000054  I -.0649117  .0000057  1.0909 0.0040  I   -57.997     .250    -2.778     .182 -0.039670  0.132430 -0.0649050   -57.500    -3.000  
+ 111 1 52214.00 I  -.042998  .000039   .133687  .000053  I -.0659738  .0000056  1.0205 0.0035  I   -58.039     .250    -2.727     .182 -0.043040  0.133660 -0.0659630   -58.000    -2.800  
+ 111 2 52215.00 I  -.046638  .000037   .134773  .000054  I -.0669227  .0000041  0.8600 0.0040  I   -58.071     .250    -2.858     .182 -0.046640  0.134770 -0.0669370   -58.300    -2.900  
+ 111 3 52216.00 I  -.050417  .000025   .135809  .000053  I -.0676901  .0000057  0.6885 0.0036  I   -58.075     .223    -2.983     .238 -0.050340  0.135800 -0.0676910   -58.300    -2.900  
+ 111 4 52217.00 I  -.053930  .000091   .136813  .000047  I -.0683200  .0000060  0.5779 0.0039  I   -57.894     .173    -3.079     .149 -0.053880  0.136820 -0.0683050   -58.000    -3.000  
+ 111 5 52218.00 I  -.056874  .000095   .137877  .000043  I -.0688598  .0000053  0.5092 0.0042  I   -57.726     .277    -3.062     .340 -0.056890  0.137870 -0.0688520   -57.500    -3.100  
+ 111 6 52219.00 I  -.059567  .000094   .139242  .000055  I -.0693496  .0000058  0.4743 0.0038  I   -57.586     .256    -3.002     .116 -0.059350  0.139300 -0.0693540   -57.100    -3.100  
+ 111 7 52220.00 I  -.061636  .000095   .140902  .000054  I -.0698298  .0000055  0.5037 0.0038  I   -57.366     .256    -2.957     .116 -0.061630  0.140970 -0.0698350   -56.700    -3.000  
+ 111 8 52221.00 I  -.063465  .000094   .142802  .000059  I -.0704038  .0000050  0.6735 0.0037  I   -56.978     .256    -2.896     .116 -0.063600  0.142770 -0.0703970   -56.700    -2.700  
+ 111 9 52222.00 I  -.065483  .000095   .144986  .000060  I -.0712220  .0000050  0.9788 0.0038  I   -56.506     .256    -2.745     .116 -0.065480  0.145000 -0.0712120   -56.600    -2.300  
+ 11110 52223.00 I  -.067793  .000044   .147468  .000062  I -.0723560  .0000058  1.2679 0.0045  I   -56.183     .313    -2.422     .340 -0.067800  0.147470 -0.0723670   -56.400    -1.800  
+ 11111 52224.00 I  -.069810  .000044   .149958  .000070  I -.0737262  .0000076  1.4620 0.0040  I   -56.234     .305    -2.145     .340 -0.069820  0.149910 -0.0737430   -56.400    -1.500  
+ 11112 52225.00 I  -.071468  .000046   .152519  .000070  I -.0752515  .0000054  1.5704 0.0046  I   -56.621     .260    -1.998     .340 -0.071500  0.152520 -0.0752510   -56.400    -1.400  
+ 11113 52226.00 I  -.073350  .000062   .155089  .000081  I -.0768355  .0000051  1.5841 0.0037  I   -57.063     .246    -2.076     .340 -0.073310  0.155070 -0.0768330   -56.600    -1.700  
+ 11114 52227.00 I  -.075332  .000063   .157245  .000076  I -.0783711  .0000051  1.4520 0.0036  I   -57.250     .244    -2.311     .340 -0.075350  0.157260 -0.0783700   -56.800    -2.400  
+ 11115 52228.00 I  -.077750  .000063   .159099  .000076  I -.0796914  .0000050  1.1755 0.0035  I   -57.138     .244    -2.507     .340 -0.077860  0.159060 -0.0796860   -57.000    -2.600  
+ 11116 52229.00 I  -.080457  .000063   .160740  .000077  I -.0807265  .0000048  0.9122 0.0033  I   -56.888     .244    -2.540     .340 -0.080460  0.160730 -0.0807140   -56.900    -2.600  
+ 11117 52230.00 I  -.082630  .000055   .162556  .000067  I -.0815412  .0000042  0.7253 0.0035  I   -56.701     .132    -2.458     .340 -0.082630  0.162600 -0.0815400   -56.700    -2.700  
+ 11118 52231.00 I  -.084537  .000051   .164432  .000057  I -.0821981  .0000051  0.6015 0.0032  I   -56.544     .182    -2.428     .340 -0.084550  0.164490 -0.0822050   -56.500    -2.500  
+ 11119 52232.00 I  -.086599  .000052   .166367  .000063  I -.0827684  .0000048  0.5503 0.0036  I   -56.355     .740    -2.463     .340 -0.086610  0.166400 -0.0827700   -56.200    -2.500  
+ 11120 52233.00 I  -.088961  .000053   .168447  .000064  I -.0833152  .0000050  0.5485 0.0037  I   -56.088     .740    -2.443     .340 -0.088970  0.168490 -0.0833120   -55.900    -2.400  
+ 11121 52234.00 I  -.091666  .000051   .170548  .000062  I -.0838756  .0000057  0.5773 0.0038  I   -55.798     .740    -2.295     .340 -0.091650  0.170580 -0.0838760   -55.700    -2.300  
+ 11122 52235.00 I  -.094439  .000051   .172912  .000061  I -.0844874  .0000056  0.6589 0.0042  I   -55.576     .740    -2.119     .340 -0.094470  0.172930 -0.0844770   -55.400    -2.200  
+ 11123 52236.00 I  -.097417  .000053   .175528  .000066  I -.0852001  .0000061  0.7607 0.0039  I   -55.477     .740    -2.063     .340 -0.097430  0.175550 -0.0851540   -55.200    -1.800  
+ 11124 52237.00 I  -.100610  .000055   .178082  .000068  I -.0860027  .0000053  0.8441 0.0071  I   -55.480     .740    -2.121     .340 -0.100640  0.178070 -0.0859420   -55.100    -1.600  
+ 11125 52238.00 I  -.103621  .000023   .180536  .000046  I -.0868873  .0000128  0.9244 0.0041  I   -55.538     .740    -2.153     .340 -0.103680  0.180540 -0.0868450   -55.200    -1.500  
+ 11126 52239.00 I  -.106097  .000038   .183056  .000084  I -.0878437  .0000062  0.9813 0.0067  I   -55.601     .740    -1.994     .340 -0.106080  0.183040 -0.0878340   -55.300    -1.300  
+ 11127 52240.00 I  -.108161  .000040   .185529  .000085  I -.0888340  .0000040  0.9906 0.0037  I   -55.682     .308    -1.653     .340 -0.108120  0.185520 -0.0888440   -55.500    -1.300  
+ 11128 52241.00 I  -.110184  .000043   .187897  .000086  I -.0898046  .0000042  0.9389 0.0029  I   -55.807     .308    -1.298     .340 -0.110230  0.187940 -0.0898050   -55.600    -1.300  
+ 11129 52242.00 I  -.112626  .000044   .190242  .000084  I -.0906958  .0000041  0.8391 0.0029  I   -55.944     .308    -1.132     .340 -0.112570  0.190300 -0.0906720   -55.700    -1.400  
+ 11130 52243.00 I  -.115319  .000046   .192771  .000084  I -.0914748  .0000041  0.7155 0.0030  I   -56.001     .308    -1.236     .340 -0.115330  0.192810 -0.0914570   -55.600    -1.600  
+ 112 1 52244.00 I  -.118472  .000051   .195444  .000084  I -.0921087  .0000043  0.5378 0.0025  I   -55.854     .344    -1.497     .340 -0.118480  0.195510 -0.0921210   -55.400    -1.800  
+ 112 2 52245.00 I  -.121686  .000100   .197956  .000057  I -.0925504  .0000027  0.3601 0.0024  I   -55.603     .505    -1.723     .340 -0.121680  0.198010 -0.0925740   -55.000    -1.800  
+ 112 3 52246.00 I  -.124538  .000107   .200158  .000055  I -.0928592  .0000022  0.2733 0.0016  I   -55.272     .720    -1.766     .126 -0.124610  0.200230 -0.0928610   -54.600    -1.900  
+ 112 4 52247.00 I  -.127367  .000104   .202499  .000057  I -.0931227  .0000018  0.2637 0.0015  I   -54.934     .720    -1.659     .126 -0.127150  0.202470 -0.0931050   -54.200    -1.800  
+ 112 5 52248.00 I  -.129424  .000103   .205211  .000058  I -.0934180  .0000020  0.3484 0.0013  I   -54.617     .720    -1.531     .126 -0.129470  0.205230 -0.0934140   -53.800    -1.600  
+ 112 6 52249.00 I  -.131746  .000103   .208532  .000057  I -.0938524  .0000020  0.5318 0.0015  I   -54.321     .720    -1.461     .126 -0.131820  0.208530 -0.0938520   -53.600    -1.300  
+ 112 7 52250.00 I  -.134559  .000102   .211661  .000057  I -.0944922  .0000021  0.7472 0.0023  I   -54.076     .720    -1.430     .126 -0.134560  0.211710 -0.0944960   -53.700    -1.100  
+ 112 8 52251.00 I  -.137796  .000068   .214373  .000066  I -.0953455  .0000042  0.9589 0.0027  I   -53.894     .526    -1.389     .295 -0.137790  0.214400 -0.0953380   -54.000    -0.900  
+ 112 9 52252.00 I  -.140993  .000056   .216887  .000054  I -.0963972  .0000050  1.1321 0.0047  I   -54.018     .492    -1.326     .344 -0.140970  0.216840 -0.0963730   -54.400    -0.700  
+ 11210 52253.00 I  -.143845  .000070   .219614  .000052  I -.0975759  .0000085  1.2038 0.0055  I   -54.377     .413    -1.286     .288 -0.143840  0.219610 -0.0975250   -55.000    -0.500  
+ 11211 52254.00 I  -.146663  .000067   .222530  .000051  I -.0987595  .0000098  1.1397 0.0063  I   -54.824     .413    -1.304     .288 -0.146720  0.222550 -0.0986800   -55.500    -0.500  
+ 11212 52255.00 I  -.149955  .000071   .225288  .000059  I -.0998250  .0000094  0.9849 0.0067  I   -55.220     .293    -1.361     .268 -0.149810  0.225270 -0.0997520   -55.900    -0.600  
+ 11213 52256.00 I  -.152619  .000073   .227892  .000058  I -.1007198  .0000091  0.8018 0.0066  I   -55.247     .293    -1.421     .268 -0.152690  0.227810 -0.1006790   -56.100    -0.800  
+ 11214 52257.00 I  -.155312  .000077   .230578  .000060  I -.1014175  .0000093  0.5856 0.0063  I   -55.031     .293    -1.435     .268 -0.155270  0.230440 -0.1014000   -55.900    -1.000  
+ 11215 52258.00 I  -.157721  .000066   .233138  .000055  I -.1018974  .0000088  0.3888 0.0074  I   -54.743     .108    -1.424     .185 -0.157710  0.233150 -0.1018990   -55.500    -1.200  
+ 11216 52259.00 I  -.159985  .000041   .235805  .000052  I -.1022319  .0000115  0.3026 0.0054  I   -54.524     .740    -1.446     .187 -0.159980  0.235780 -0.1022370   -54.800    -1.400  
+ 11217 52260.00 I  -.161987  .000045   .238560  .000048  I -.1025397  .0000063  0.3278 0.0064  I   -54.370     .195    -1.487     .168 -0.161990  0.238560 -0.1025390   -54.200    -1.600  
+ 11218 52261.00 I  -.163912  .000064   .241535  .000043  I -.1029096  .0000058  0.4194 0.0044  I   -54.210     .264    -1.480     .152 -0.163910  0.241570 -0.1029110   -53.700    -1.600  
+ 11219 52262.00 I  -.165861  .000058   .244801  .000042  I -.1033804  .0000062  0.5169 0.0041  I   -54.050     .264    -1.401     .152 -0.165910  0.244800 -0.1033780   -53.400    -1.400  
+ 11220 52263.00 I  -.167850  .000056   .248402  .000047  I -.1039633  .0000059  0.6742 0.0043  I   -53.984     .264    -1.336     .152 -0.167810  0.248290 -0.1039500   -53.200    -1.200  
+ 11221 52264.00 I  -.169230  .000060   .251819  .000048  I -.1047528  .0000059  0.9034 0.0042  I   -54.043     .264    -1.379     .152 -0.169310  0.251770 -0.1047380   -53.500    -0.900  
+ 11222 52265.00 I  -.170330  .000062   .255332  .000051  I -.1057456  .0000059  1.0590 0.0045  I   -53.924     .740    -1.588     .340 -0.170420  0.255330 -0.1057660   -53.900    -0.600  
+ 11223 52266.00 I  -.171339  .000066   .258892  .000058  I -.1068441  .0000069  1.1347 0.0063  I   -53.942     .740    -1.631     .340 -0.171340  0.258950 -0.1068720   -54.200    -0.500  
+ 11224 52267.00 I  -.172082  .000030   .262522  .000046  I -.1080049  .0000112  1.1803 0.0052  I   -53.955     .740    -1.498     .340 -0.172130  0.262500 -0.1080060   -54.800    -0.400  
+ 11225 52268.00 I  -.172610  .000031   .266399  .000052  I -.1091917  .0000078  1.1868 0.0058  I   -54.085     .740    -1.216     .340 -0.172630  0.266380 -0.1091660   -55.100    -0.600  
+ 11226 52269.00 I  -.173029  .000037   .270501  .000068  I -.1103666  .0000027  1.1574 0.0041  I   -54.327     .194     -.902     .340 -0.172980  0.270520 -0.1103540   -55.400    -0.700  
+ 11227 52270.00 I  -.173564  .000043   .274604  .000066  I -.1114947  .0000027  1.0925 0.0021  I   -54.556     .194     -.691     .340 -0.173600  0.274890 -0.1114930   -55.500    -0.800  
+ 11228 52271.00 I  -.174569  .000043   .278560  .000066  I -.1125366  .0000031  0.9827 0.0035  I   -54.667     .165     -.692     .340 -0.174540  0.278470 -0.1125380   -55.500    -1.000  
+ 11229 52272.00 I  -.175316  .000040   .282350  .000086  I -.1134552  .0000065  0.8583 0.0037  I   -54.728     .165     -.941     .340 -0.175190  0.282350 -0.1134560   -55.200    -1.000  
+ 11230 52273.00 I  -.175984  .000040   .286373  .000079  I -.1142677  .0000067  0.7774 0.0048  I   -54.662     .165    -1.294     .340 -0.175920  0.286410 -0.1142540   -54.800    -1.000  
+ 11231 52274.00 I  -.176653  .000042   .290272  .000072  I -.1150324  .0000070  0.7636 0.0094  I   -54.357     .165    -1.518     .340 -0.176950  0.290250 -0.1149770   -54.300    -0.900  
+ 2 1 1 52275.00 I  -.177016  .000041   .293921  .000069  I -.1158152  .0000175  0.8108 0.0060  I   -53.821     .740    -1.481     .340 -0.177000  0.293620 -0.1158230   -52.800    -1.100  
+ 2 1 2 52276.00 I  -.177495  .000033   .297612  .000080  I -.1166808  .0000098  0.9385 0.0103  I   -53.265     .265    -1.268     .340 -0.177470  0.297200 -0.1166920   -52.600    -1.200  
+ 2 1 3 52277.00 I  -.178336  .000037   .300810  .000073  I -.1177100  .0000108  1.1198 0.0070  I   -52.938     .265    -1.076     .340 -0.178340  0.300940 -0.1177150   -52.600    -1.100  
+ 2 1 4 52278.00 I  -.179101  .000042   .303585  .000061  I -.1189101  .0000099  1.2704 0.0072  I   -52.906     .265    -1.021     .340 -0.179090  0.303680 -0.1189030   -52.800    -1.100  
+ 2 1 5 52279.00 I  -.179532  .000046   .306461  .000060  I -.1202228  .0000094  1.3372 0.0062  I   -53.091     .259    -1.064     .119 -0.179550  0.306440 -0.1202550   -53.100    -1.000  
+ 2 1 6 52280.00 I  -.180020  .000057   .309511  .000089  I -.1215588  .0000074  1.3270 0.0061  I   -53.330     .208    -1.116     .122 -0.179970  0.309560 -0.1216410   -53.500    -0.900  
+ 2 1 7 52281.00 I  -.180675  .000057   .312707  .000088  I -.1228480  .0000077  1.2304 0.0057  I   -53.622     .208    -1.132     .122 -0.180630  0.312730 -0.1229070   -53.800    -0.900  
+ 2 1 8 52282.00 I  -.181819  .000065   .315313  .000071  I -.1239725  .0000088  0.9923 0.0057  I   -53.953     .140    -1.120     .124 -0.181690  0.315560 -0.1239680   -54.100    -0.800  
+ 2 1 9 52283.00 I  -.182661  .000094   .318169  .000076  I -.1248182  .0000085  0.7108 0.0061  I   -54.210     .226    -1.105     .101 -0.182690  0.318280 -0.1248070   -54.100    -0.800  
+ 2 110 52284.00 I  -.183034  .000088   .321152  .000076  I -.1254127  .0000084  0.4850 0.0062  I   -54.304     .226    -1.081     .101 -0.183060  0.321190 -0.1254210   -54.000    -0.900  
+ 2 111 52285.00 I  -.183124  .000081   .324202  .000082  I -.1258003  .0000089  0.2955 0.0071  I   -54.136     .226    -1.087     .101 -0.183040  0.324220 -0.1257940   -53.700    -1.000  
+ 2 112 52286.00 I  -.182606  .000079   .327370  .000036  I -.1260104  .0000114  0.1261 0.0060  I   -53.756     .231    -1.161     .340 -0.182770  0.327380 -0.1260400   -53.500    -1.200  
+ 2 113 52287.00 I  -.182290  .000085   .330466  .000067  I -.1260705  .0000081  0.0106 0.0071  I   -53.448     .211    -1.310     .129 -0.182260  0.330390 -0.1261170   -53.300    -1.400  
+ 2 114 52288.00 I  -.181735  .000084   .333568  .000075  I -.1260606  .0000084 -0.0178 0.0052  I   -53.288     .211    -1.459     .129 -0.181660  0.333550 -0.1260720   -53.200    -1.500  
+ 2 115 52289.00 I  -.181305  .000047   .336868  .000079  I -.1260645  .0000066  0.0426 0.0047  I   -53.250     .189    -1.511     .174 -0.181260  0.336820 -0.1260550   -53.200    -1.600  
+ 2 116 52290.00 I  -.180666  .000048   .339816  .000072  I -.1261542  .0000042  0.1280 0.0039  I   -53.278     .149    -1.462     .133 -0.180660  0.339780 -0.1261430   -53.200    -1.600  
+ 2 117 52291.00 I  -.179799  .000050   .342945  .000074  I -.1263182  .0000041  0.2064 0.0029  I   -53.374     .149    -1.419     .133 -0.179880  0.342870 -0.1263090   -53.300    -1.600  
+ 2 118 52292.00 I  -.179235  .000049   .346080  .000082  I -.1265902  .0000040  0.3543 0.0029  I   -53.504     .149    -1.476     .133 -0.179240  0.346120 -0.1265880   -53.400    -1.500  
+ 2 119 52293.00 I  -.178756  .000032   .349176  .000059  I -.1270231  .0000040  0.4911 0.0027  I   -53.538     .740    -1.620     .340 -0.178740  0.349230 -0.1270260   -53.300    -1.400  
+ 2 120 52294.00 I  -.178420  .000029   .352206  .000054  I -.1275466  .0000037  0.5505 0.0027  I   -53.402     .740    -1.665     .340 -0.178410  0.352280 -0.1275570   -53.200    -1.300  
+ 2 121 52295.00 I  -.178175  .000098   .355254  .000052  I -.1281161  .0000036  0.5861 0.0024  I   -53.270     .223    -1.611     .340 -0.178170  0.355300 -0.1281260   -53.000    -1.200  
+ 2 122 52296.00 I  -.177717  .000100   .358445  .000045  I -.1287129  .0000032  0.6041 0.0024  I   -53.356     .302    -1.520     .340 -0.177750  0.358510 -0.1287150   -53.000    -1.200  
+ 2 123 52297.00 I  -.177120  .000106   .361868  .000045  I -.1293129  .0000032  0.5879 0.0023  I   -53.632     .244    -1.427     .340 -0.177120  0.361930 -0.1293080   -53.000    -1.200  
+ 2 124 52298.00 I  -.176579  .000107   .365288  .000045  I -.1298667  .0000032  0.5058 0.0022  I   -53.901     .244    -1.315     .340 -0.176510  0.365290 -0.1298590   -53.400    -1.200  
+ 2 125 52299.00 I  -.175468  .000109   .368698  .000047  I -.1303104  .0000031  0.3813 0.0023  I   -54.125     .244    -1.241     .340 -0.175730  0.368690 -0.1303060   -53.800    -1.200  
+ 2 126 52300.00 I  -.174998  .000119   .372115  .000048  I -.1306448  .0000032  0.3033 0.0030  I   -54.387     .223    -1.352     .340 -0.175000  0.372170 -0.1306280   -54.100    -1.400  
+ 2 127 52301.00 I  -.174316  .000076   .375411  .000057  I -.1309512  .0000051  0.3277 0.0032  I   -54.671     .321    -1.735     .340 -0.174370  0.375470 -0.1309230   -54.200    -1.700  
+ 2 128 52302.00 I  -.173598  .000075   .378730  .000059  I -.1313384  .0000055  0.4665 0.0044  I   -54.583     .321    -2.157     .340 -0.173550  0.378710 -0.1313120   -54.100    -1.900  
+ 2 129 52303.00 I  -.172607  .000062   .382056  .000058  I -.1319137  .0000071  0.6939 0.0046  I   -53.935     .421    -2.303     .340 -0.172820  0.382030 -0.1319070   -54.000    -2.200  
+ 2 130 52304.00 I  -.172389  .000111   .385066  .000051  I -.1327493  .0000073  0.9908 0.0051  I   -53.054     .401    -2.100     .159 -0.172060  0.385140 -0.1327840   -53.400    -2.100  
+ 2 131 52305.00 I  -.170719  .000113   .388310  .000051  I -.1338791  .0000074  1.2392 0.0052  I   -52.497     .401    -1.785     .159 -0.170750  0.388250 -0.1339120   -52.900    -1.900  
+ 2 2 1 52306.00 I  -.168875  .000103   .391546  .000048  I -.1351973  .0000073  1.3964 0.0047  I   -52.497     .401    -1.619     .159 -0.168940  0.391530 -0.1352080   -52.400    -1.600  
+ 2 2 2 52307.00 I  -.167477  .000110   .394739  .000066  I -.1366556  .0000057  1.5046 0.0044  I   -52.845     .379    -1.638     .212 -0.167400  0.394750 -0.1365590   -52.300    -1.500  
+ 2 2 3 52308.00 I  -.166425  .000068   .397897  .000101  I -.1381554  .0000049  1.4595 0.0038  I   -53.214     .337    -1.696     .224 -0.166390  0.397840 -0.1380160   -52.500    -1.400  
+ 2 2 4 52309.00 I  -.165568  .000064   .400893  .000101  I -.1395328  .0000049  1.2892 0.0034  I   -53.473     .337    -1.725     .224 -0.165460  0.400780 -0.1394740   -52.800    -1.400  
+ 2 2 5 52310.00 I  -.164218  .000045   .403149  .000104  I -.1407285  .0000048  1.1032 0.0034  I   -53.665     .290    -1.751     .236 -0.164370  0.403580 -0.1407190   -54.000    -1.400  
+ 2 2 6 52311.00 I  -.163276  .000056   .406446  .000100  I -.1417207  .0000046  0.8616 0.0033  I   -53.843     .263    -1.804     .168 -0.163320  0.406310 -0.1417360   -54.100    -1.500  
+ 2 2 7 52312.00 I  -.162284  .000055   .409364  .000101  I -.1424592  .0000045  0.6423 0.0031  I   -53.959     .263    -1.858     .168 -0.162230  0.409320 -0.1424540   -53.900    -1.600  
+ 2 2 8 52313.00 I  -.161022  .000053   .412491  .000100  I -.1430272  .0000043  0.4886 0.0044  I   -53.893     .263    -1.909     .168 -0.161130  0.412430 -0.1430230   -53.400    -1.800  
+ 2 2 9 52314.00 I  -.159842  .000049   .415394  .000050  I -.1434514  .0000075  0.3800 0.0047  I   -53.498     .134    -2.001     .340 -0.159750  0.415430 -0.1434440   -53.000    -1.900  
+ 2 210 52315.00 I  -.157943  .000056   .418781  .000106  I -.1438265  .0000084  0.3895 0.0054  I   -53.099     .454    -2.173     .165 -0.157950  0.418820 -0.1438150   -52.600    -2.000  
+ 2 211 52316.00 I  -.156070  .000058   .422412  .000119  I -.1442607  .0000077  0.4900 0.0058  I   -52.804     .454    -2.328     .165 -0.156050  0.422450 -0.1442500   -52.500    -2.100  
+ 2 212 52317.00 I  -.154331  .000050   .425633  .000119  I -.1448045  .0000079  0.5844 0.0052  I   -52.715     .628    -2.341     .233 -0.154400  0.425740 -0.1447930   -52.500    -2.100  
+ 2 213 52318.00 I  -.153348  .000073   .428706  .000121  I -.1454279  .0000071  0.6738 0.0052  I   -52.787     .451    -2.211     .174 -0.153250  0.428820 -0.1454300   -52.600    -2.100  
+ 2 214 52319.00 I  -.152471  .000068   .431713  .000120  I -.1461593  .0000069  0.7846 0.0045  I   -52.928     .451    -2.084     .174 -0.152390  0.431700 -0.1461620   -52.700    -2.000  
+ 2 215 52320.00 I  -.151431  .000074   .434433  .000119  I -.1469919  .0000055  0.8798 0.0038  I   -53.037     .451    -2.090     .174 -0.151390  0.434420 -0.1469910   -52.800    -2.000  
+ 2 216 52321.00 I  -.150089  .000069   .437078  .000077  I -.1479225  .0000033  0.9858 0.0033  I   -53.009     .122    -2.215     .340 -0.150100  0.437100 -0.1479090   -52.700    -2.000  
+ 2 217 52322.00 I  -.148485  .000065   .439730  .000045  I -.1489536  .0000035  1.0621 0.0032  I   -52.796     .122    -2.298     .340 -0.148490  0.439730 -0.1489370   -52.500    -2.100  
+ 2 218 52323.00 I  -.146818  .000112   .442441  .000075  I -.1500204  .0000054  1.0593 0.0035  I   -52.597     .740    -2.350     .340 -0.146780  0.442390 -0.1500170   -52.300    -2.100  
+ 2 219 52324.00 I  -.145248  .000100   .445256  .000077  I -.1510529  .0000060  0.9984 0.0040  I   -52.626     .740    -2.458     .340 -0.145260  0.445150 -0.1510560   -52.100    -2.100  
+ 2 220 52325.00 I  -.143908  .000104   .447804  .000077  I -.1520116  .0000060  0.9206 0.0043  I   -52.833     .226    -2.622     .340 -0.143730  0.447920 -0.1520210   -52.100    -2.200  
+ 2 221 52326.00 I  -.142017  .000105   .450844  .000075  I -.1528875  .0000061  0.8229 0.0043  I   -52.990     .226    -2.701     .340 -0.142030  0.450810 -0.1528980   -52.200    -2.400  
+ 2 222 52327.00 I  -.140236  .000105   .453903  .000075  I -.1536583  .0000061  0.7279 0.0042  I   -53.081     .226    -2.644     .340 -0.140240  0.453930 -0.1536530   -52.500    -2.700  
+ 2 223 52328.00 I  -.138470  .000104   .457214  .000070  I -.1543530  .0000058  0.6621 0.0039  I   -53.260     .187    -2.679     .340 -0.138440  0.457220 -0.1543870   -52.800    -3.000  
+ 2 224 52329.00 I  -.136564  .000139   .460595  .000023  I -.1550116  .0000047  0.6835 0.0038  I   -53.660     .294    -2.970     .340 -0.136510  0.460600 -0.1551080   -53.100    -3.300  
+ 2 225 52330.00 I  -.134094  .000156   .463762  .000021  I -.1557661  .0000049  0.8433 0.0031  I   -53.763     .294    -3.449     .340 -0.134050  0.463790 -0.1558460   -53.200    -3.600  
+ 2 226 52331.00 I  -.131075  .000147   .466899  .000032  I -.1567270  .0000040  1.0894 0.0031  I   -53.205     .335    -3.740     .340 -0.131080  0.466900 -0.1567280   -53.100    -3.700  
+ 2 227 52332.00 I  -.128411  .000142   .470196  .000051  I -.1579544  .0000038  1.3639 0.0028  I   -52.247     .241    -3.628     .340 -0.128360  0.470160 -0.1579220   -52.700    -3.700  
+ 2 228 52333.00 I  -.125976  .000133   .473454  .000051  I -.1594256  .0000038  1.5508 0.0027  I   -51.558     .241    -3.297     .340 -0.125970  0.473440 -0.1594490   -52.100    -3.400  
+ 2 3 1 52334.00 I  -.123473  .000133   .476420  .000052  I -.1610247  .0000039  1.6439 0.0026  I   -51.514     .241    -3.071     .340 -0.123360  0.476540 -0.1610230   -51.400    -3.000  
+ 2 3 2 52335.00 I  -.120773  .000034   .479750  .000055  I -.1626879  .0000035  1.6606 0.0024  I   -51.996     .740    -3.026     .340 -0.120890  0.479830 -0.1626750   -51.300    -2.700  
+ 2 3 3 52336.00 I  -.118537  .000073   .482904  .000054  I -.1642927  .0000028  1.5177 0.0022  I   -52.427     .168    -3.060     .340 -0.118380  0.482830 -0.1642830   -51.200    -2.400  
+ 2 3 4 52337.00 I  -.116662  .000087   .485901  .000053  I -.1656830  .0000025  1.2538 0.0022  I   -52.637     .152    -3.081     .340 -0.116640  0.485860 -0.1656820   -51.400    -2.400  
+ 2 3 5 52338.00 I  -.114402  .000076   .488895  .000075  I -.1668039  .0000034  1.0006 0.0018  I   -52.661     .183    -3.165     .340 -0.114530  0.488880 -0.1668110   -51.700    -2.500  
+ 2 3 6 52339.00 I  -.111601  .000075   .491674  .000054  I -.1677040  .0000027  0.8068 0.0021  I   -52.662     .217    -3.359     .340 -0.111870  0.491710 -0.1677050   -51.900    -2.800  
+ 2 3 7 52340.00 I  -.108721  .000076   .494389  .000053  I -.1684320  .0000025  0.6566 0.0018  I   -52.744     .217    -3.560     .340 -0.108770  0.494280 -0.1684280   -52.100    -3.100  
+ 2 3 8 52341.00 I  -.105539  .000074   .497043  .000053  I -.1690387  .0000023  0.5710 0.0018  I   -52.818     .217    -3.656     .340 -0.105510  0.497000 -0.1690330   -52.100    -3.500  
+ 2 3 9 52342.00 I  -.102297  .000069   .499702  .000054  I -.1695899  .0000025  0.5331 0.0019  I   -52.614     .172    -3.662     .340 -0.102260  0.499710 -0.1696080   -51.900    -3.700  
+ 2 310 52343.00 I  -.099240  .000088   .502427  .000058  I -.1701263  .0000031  0.5596 0.0020  I   -52.203     .160    -3.678     .340 -0.099160  0.502440 -0.1701600   -51.600    -3.800  
+ 2 311 52344.00 I  -.096907  .000097   .505134  .000050  I -.1707381  .0000032  0.6733 0.0023  I   -51.725     .160    -3.677     .340 -0.096810  0.505150 -0.1707500   -51.400    -3.700  
+ 2 312 52345.00 I  -.095010  .000103   .507537  .000040  I -.1714858  .0000035  0.8257 0.0023  I   -51.455     .740    -3.552     .340 -0.095040  0.507560 -0.1714790   -51.300    -3.500  
+ 2 313 52346.00 I  -.092703  .000098   .509844  .000045  I -.1723819  .0000034  0.9554 0.0025  I   -51.475     .206    -3.291     .340 -0.092840  0.509890 -0.1723820   -51.200    -3.300  
+ 2 314 52347.00 I  -.090280  .000108   .512133  .000050  I -.1733703  .0000036  1.0061 0.0025  I   -51.626     .206    -3.058     .340 -0.090220  0.512070 -0.1733690   -51.300    -3.200  
+ 2 315 52348.00 I  -.087647  .000109   .514321  .000054  I -.1743650  .0000037  0.9693 0.0025  I   -51.748     .206    -3.018     .340 -0.087660  0.514370 -0.1743580   -51.300    -3.100  
+ 2 316 52349.00 I  -.084887  .000041   .516458  .000050  I -.1753014  .0000036  0.9098 0.0030  I   -51.739     .247    -3.159     .340 -0.084860  0.516470 -0.1753050   -51.300    -3.000  
+ 2 317 52350.00 I  -.081872  .000095   .518429  .000045  I -.1761905  .0000047  0.8680 0.0030  I   -51.598     .141    -3.339     .340 -0.081840  0.518430 -0.1762050   -51.200    -3.100  
+ 2 318 52351.00 I  -.078750  .000095   .520307  .000058  I -.1770307  .0000047  0.8060 0.0034  I   -51.451     .141    -3.494     .340 -0.078700  0.520250 -0.1770390   -51.000    -3.200  
+ 2 319 52352.00 I  -.075580  .000088   .522229  .000057  I -.1777808  .0000048  0.6788 0.0032  I   -51.449     .140    -3.689     .340 -0.075630  0.522210 -0.1777670   -50.900    -3.300  
+ 2 320 52353.00 I  -.072564  .000086   .523982  .000061  I -.1783793  .0000042  0.5245 0.0031  I   -51.564     .233    -3.948     .340 -0.072570  0.523990 -0.1783600   -50.800    -3.500  
+ 2 321 52354.00 I  -.069255  .000081   .525681  .000057  I -.1788451  .0000040  0.4162 0.0029  I   -51.599     .233    -4.144     .340 -0.069250  0.525600 -0.1788340   -50.800    -3.700  
+ 2 322 52355.00 I  -.065799  .000084   .527523  .000057  I -.1792335  .0000039  0.3735 0.0024  I   -51.492     .233    -4.169     .340 -0.065770  0.527460 -0.1792290   -50.900    -4.000  
+ 2 323 52356.00 I  -.062329  .000036   .529144  .000062  I -.1796148  .0000026  0.3991 0.0023  I   -51.432     .298    -4.139     .122 -0.062270  0.529100 -0.1796230   -51.000    -4.400  
+ 2 324 52357.00 I  -.058837  .000046   .530650  .000056  I -.1800603  .0000024  0.5107 0.0018  I   -51.530     .214    -4.282     .127 -0.058810  0.530710 -0.1801030   -51.100    -4.700  
+ 2 325 52358.00 I  -.055246  .000055   .532291  .000056  I -.1806694  .0000026  0.7219 0.0019  I   -51.554     .214    -4.608     .127 -0.055180  0.532160 -0.1807120   -51.000    -4.900  
+ 2 326 52359.00 I  -.051312  .000054   .534075  .000049  I -.1815255  .0000030  0.9975 0.0021  I   -51.213     .201    -4.844     .340 -0.051350  0.533960 -0.1815250   -50.800    -4.900  
+ 2 327 52360.00 I  -.048010  .000062   .535792  .000049  I -.1826741  .0000033  1.3024 0.0023  I   -50.631     .200    -4.779     .340 -0.047900  0.535800 -0.1826590   -50.500    -4.800  
+ 2 328 52361.00 I  -.045031  .000063   .537281  .000048  I -.1841051  .0000034  1.5317 0.0024  I   -50.267     .200    -4.510     .340 -0.044850  0.537270 -0.1841020   -50.200    -4.500  
+ 2 329 52362.00 I  -.041718  .000069   .538545  .000050  I -.1856662  .0000036  1.5469 0.0028  I   -50.401     .200    -4.327     .340 -0.041730  0.538490 -0.1856720   -49.900    -4.300  
+ 2 330 52363.00 I  -.038236  .000045   .539546  .000037  I -.1871407  .0000044  1.3874 0.0030  I   -50.867     .278    -4.318     .340 -0.038250  0.539420 -0.1871400   -49.700    -4.100  
+ 2 331 52364.00 I  -.034512  .000028   .540256  .000052  I -.1884214  .0000047  1.1690 0.0043  I   -51.291     .278    -4.347     .340 -0.034510  0.540310 -0.1884220   -49.600    -4.100  
+ 2 4 1 52365.00 I  -.030784  .000064   .540741  .000079  I -.1894701  .0000074  0.9247 0.0037  I   -51.432     .440    -4.340     .340 -0.030760  0.540660 -0.1894800   -49.700    -4.200  
+ 2 4 2 52366.00 I  -.027006  .000076   .541124  .000079  I -.1902749  .0000058  0.6928 0.0045  I   -51.299     .385    -4.416     .130 -0.026850  0.541150 -0.1902860   -49.900    -4.400  
+ 2 4 3 52367.00 I  -.022763  .000073   .541626  .000083  I -.1908693  .0000052  0.5017 0.0039  I   -51.097     .319    -4.663     .111 -0.022980  0.541720 -0.1908570   -50.100    -4.700  
+ 2 4 4 52368.00 I  -.018616  .000073   .542855  .000085  I -.1913055  .0000052  0.3922 0.0037  I   -51.094     .326    -4.944     .340 -0.018830  0.542820 -0.1912760   -50.500    -4.900  
+ 2 4 5 52369.00 I  -.014936  .000076   .544230  .000079  I -.1916850  .0000053  0.3767 0.0034  I   -51.221     .326    -5.060     .340 -0.014960  0.544200 -0.1916810   -50.700    -5.000  
+ 2 4 6 52370.00 I  -.011584  .000083   .545683  .000068  I -.1920849  .0000044  0.4394 0.0031  I   -51.255     .326    -4.972     .340 -0.011580  0.545710 -0.1920770   -50.900    -5.000  
+ 2 4 7 52371.00 I  -.008818  .000102   .546912  .000047  I -.1925759  .0000034  0.5386 0.0027  I   -50.958     .324    -4.815     .340 -0.008710  0.546960 -0.1925570   -50.800    -4.900  
+ 2 4 8 52372.00 I  -.006177  .000099   .547837  .000058  I -.1931609  .0000033  0.6344 0.0024  I   -50.455     .282    -4.670     .340 -0.006160  0.547850 -0.1931530   -50.700    -4.700  
+ 2 4 9 52373.00 I  -.003471  .000102   .548651  .000057  I -.1938494  .0000035  0.7444 0.0023  I   -50.118     .337    -4.481     .340 -0.003780  0.548650 -0.1938450   -50.600    -4.400  
+ 2 410 52374.00 I  -.001639  .000100   .549319  .000064  I -.1946504  .0000031  0.8567 0.0023  I   -50.146     .276    -4.205     .340 -0.001790  0.549280 -0.1946360   -50.500    -4.100  
+ 2 411 52375.00 I   .000751  .000093   .549735  .000065  I -.1955552  .0000030  0.9464 0.0022  I   -50.402     .276    -3.956     .340  0.000630  0.549660 -0.1955480   -50.500    -4.000  
+ 2 412 52376.00 I   .003786  .000091   .550390  .000071  I -.1965192  .0000030  0.9642 0.0025  I   -50.645     .276    -3.903     .340  0.003750  0.550380 -0.1965140   -50.500    -3.900  
+ 2 413 52377.00 I   .006859  .000056   .551221  .000072  I -.1974561  .0000040  0.8995 0.0029  I   -50.849     .134    -4.084     .340  0.006850  0.551270 -0.1974570   -50.400    -4.000  
+ 2 414 52378.00 I   .009914  .000065   .551919  .000060  I -.1983019  .0000050  0.7859 0.0032  I   -50.826     .225    -4.365     .340  0.009940  0.551890 -0.1983280   -50.200    -4.200  
+ 2 415 52379.00 I   .012891  .000066   .552556  .000056  I -.1990194  .0000050  0.6467 0.0036  I   -50.741     .225    -4.611     .340  0.012910  0.552530 -0.1990500   -50.100    -4.400  
+ 2 416 52380.00 I   .015666  .000067   .553134  .000041  I -.1995747  .0000053  0.4458 0.0035  I   -50.701     .303    -4.802     .119  0.015700  0.553150 -0.1995690   -50.100    -4.600  
+ 2 417 52381.00 I   .018798  .000070   .553633  .000042  I -.1999273  .0000050  0.2940 0.0036  I   -50.764     .240    -4.957     .340  0.018850  0.553680 -0.1999170   -50.200    -4.800  
+ 2 418 52382.00 I   .022135  .000072   .554122  .000041  I -.2002060  .0000050  0.2722 0.0034  I   -50.844     .240    -5.110     .340  0.022150  0.554150 -0.2001990   -50.300    -5.000  
+ 2 419 52383.00 I   .025465  .000075   .554554  .000042  I -.2004662  .0000045  0.2341 0.0032  I   -50.793     .240    -5.191     .340  0.025500  0.554540 -0.2004610   -50.400    -5.100  
+ 2 420 52384.00 I   .028893  .000034   .554819  .000040  I -.2006861  .0000039  0.2318 0.0028  I   -50.580     .153    -5.218     .340  0.028950  0.554890 -0.2006920   -50.300    -5.200  
+ 2 421 52385.00 I   .032006  .000078   .554948  .000052  I -.2009777  .0000033  0.3734 0.0026  I   -50.295     .207    -5.273     .340  0.032050  0.555000 -0.2009790   -50.200    -5.300  
+ 2 422 52386.00 I   .034837  .000078   .554923  .000056  I -.2014742  .0000035  0.6391 0.0024  I   -50.010     .207    -5.370     .340  0.034890  0.554920 -0.2014710   -50.000    -5.300  
+ 2 423 52387.00 I   .037858  .000075   .554783  .000059  I -.2022657  .0000034  0.9341 0.0027  I   -49.772     .250    -5.391     .340  0.037860  0.554810 -0.2022590   -49.800    -5.300  
+ 2 424 52388.00 I   .041216  .000076   .554620  .000066  I -.2033246  .0000040  1.1756 0.0026  I   -49.700     .242    -5.246     .340  0.041250  0.554620 -0.2033180   -49.700    -5.200  
+ 2 425 52389.00 I   .044875  .000076   .554471  .000066  I -.2045984  .0000040  1.3617 0.0027  I   -49.933     .242    -5.033     .340  0.044890  0.554490 -0.2045770   -49.800    -5.100  
+ 2 426 52390.00 I   .048626  .000078   .554293  .000069  I -.2060056  .0000037  1.4204 0.0037  I   -50.438     .242    -4.937     .340  0.048700  0.554360 -0.2059990   -50.000    -4.900  
+ 2 427 52391.00 I   .052514  .000059   .553903  .000073  I -.2073772  .0000063  1.2929 0.0035  I   -51.044     .374    -4.990     .340  0.052510  0.553960 -0.2073770   -50.200    -4.800  
+ 2 428 52392.00 I   .056207  .000067   .553344  .000098  I -.2085449  .0000060  1.0260 0.0043  I   -51.380     .340    -5.040     .340  0.056270  0.553350 -0.2085470   -50.400    -4.800  
+ 2 429 52393.00 I   .059895  .000068   .552842  .000098  I -.2094192  .0000060  0.7292 0.0043  I   -51.411     .340    -5.008     .340  0.059990  0.552900 -0.2094170   -50.600    -4.900  
+ 2 430 52394.00 I   .063457  .000058   .552433  .000099  I -.2100075  .0000061  0.4450 0.0041  I   -51.188     .302    -5.017     .340  0.063540  0.552680 -0.2099850   -50.700    -5.100  
+ 2 5 1 52395.00 I   .066763  .000059   .552386  .000099  I -.2103409  .0000055  0.2560 0.0041  I   -50.882     .250    -5.204     .340  0.066810  0.552550 -0.2103280   -50.900    -5.400  
+ 2 5 2 52396.00 I   .069968  .000057   .552234  .000095  I -.2105649  .0000055  0.2032 0.0034  I   -50.717     .250    -5.482     .340  0.070040  0.552260 -0.2105750   -51.000    -5.600  
+ 2 5 3 52397.00 I   .073288  .000052   .551921  .000090  I -.2107657  .0000041  0.2057 0.0033  I   -50.781     .250    -5.625     .340  0.073400  0.551980 -0.2107650   -50.800    -5.600  
+ 2 5 4 52398.00 I   .076875  .000043   .551796  .000056  I -.2110038  .0000037  0.2908 0.0030  I   -50.900     .172    -5.519     .340  0.076890  0.551810 -0.2110110   -50.500    -5.500  
+ 2 5 5 52399.00 I   .080648  .000051   .551735  .000052  I -.2113748  .0000044  0.4585 0.0032  I   -50.784     .370    -5.340     .204  0.080730  0.551700 -0.2114160   -50.100    -5.300  
+ 2 5 6 52400.00 I   .084621  .000056   .551579  .000048  I -.2119226  .0000052  0.6317 0.0035  I   -50.470     .307    -5.202     .185  0.084750  0.551580 -0.2119720   -49.900    -4.900  
+ 2 5 7 52401.00 I   .088539  .000060   .551194  .000048  I -.2126279  .0000055  0.7740 0.0037  I   -50.271     .308    -5.088     .192  0.088540  0.551260 -0.2126300   -49.900    -4.500  
+ 2 5 8 52402.00 I   .091562  .000056   .550682  .000050  I -.2134579  .0000053  0.8781 0.0038  I   -50.404     .264    -4.919     .171  0.091560  0.550730 -0.2134090   -50.200    -4.400  
+ 2 5 9 52403.00 I   .094243  .000059   .549881  .000051  I -.2143723  .0000052  0.9469 0.0037  I   -50.790     .264    -4.708     .171  0.094320  0.549950 -0.2143480   -50.600    -4.400  
+ 2 510 52404.00 I   .097639  .000059   .549119  .000052  I -.2153390  .0000051  0.9776 0.0040  I   -51.167     .264    -4.598     .171  0.097670  0.549210 -0.2153350   -51.000    -4.600  
+ 2 511 52405.00 I   .101069  .000061   .548815  .000052  I -.2163055  .0000060  0.9424 0.0039  I   -51.344     .145    -4.696     .105  0.101130  0.548850 -0.2163140   -51.100    -4.800  
+ 2 512 52406.00 I   .103917  .000071   .548350  .000060  I -.2172079  .0000058  0.8597 0.0052  I   -51.418     .178    -4.959     .340  0.104010  0.548380 -0.2172330   -51.000    -5.000  
+ 2 513 52407.00 I   .106766  .000072   .547632  .000063  I -.2180235  .0000086  0.7730 0.0050  I   -51.369     .140    -5.248     .340  0.106820  0.547560 -0.2180410   -50.900    -5.200  
+ 2 514 52408.00 I   .109822  .000105   .546781  .000060  I -.2187407  .0000081  0.6468 0.0055  I   -51.284     .143    -5.441     .340  0.109690  0.546720 -0.2187190   -50.800    -5.300  
+ 2 515 52409.00 I   .112006  .000103   .545573  .000055  I -.2193284  .0000069  0.5543 0.0053  I   -51.291     .169    -5.527     .340  0.112130  0.545580 -0.2193030   -50.900    -5.400  
+ 2 516 52410.00 I   .114375  .000097   .544014  .000055  I -.2198677  .0000069  0.5168 0.0046  I   -51.445     .169    -5.558     .340  0.114200  0.544020 -0.2198500   -51.000    -5.500  
+ 2 517 52411.00 I   .116538  .000092   .542416  .000054  I -.2203632  .0000060  0.4829 0.0040  I   -51.589     .169    -5.604     .340  0.116510  0.542440 -0.2203590   -51.100    -5.500  
+ 2 518 52412.00 I   .119081  .000062   .541230  .000045  I -.2208752  .0000041  0.5741 0.0033  I   -51.504     .164    -5.670     .340  0.119110  0.541240 -0.2208620   -50.900    -5.500  
+ 2 519 52413.00 I   .121980  .000062   .540478  .000044  I -.2215514  .0000028  0.7854 0.0024  I   -51.131     .263    -5.711     .340  0.121990  0.540490 -0.2215070   -50.700    -5.500  
+ 2 520 52414.00 I   .125203  .000040   .539959  .000035  I -.2224608  .0000023  1.0412 0.0017  I   -50.660     .317    -5.665     .340  0.125270  0.539970 -0.2224240   -50.500    -5.400  
+ 2 521 52415.00 I   .128372  .000037   .539462  .000033  I -.2236311  .0000019  1.2889 0.0015  I   -50.405     .393    -5.503     .340  0.128480  0.539480 -0.2236270   -50.500    -5.300  
+ 2 522 52416.00 I   .131149  .000049   .538798  .000050  I -.2249998  .0000020  1.4200 0.0014  I   -50.596     .287    -5.264     .340  0.131230  0.538810 -0.2250060   -50.900    -5.300  
+ 2 523 52417.00 I   .133610  .000045   .537948  .000049  I -.2264286  .0000020  1.4234 0.0014  I   -51.214     .287    -5.064     .340  0.133700  0.537930 -0.2264180   -51.400    -5.200  
+ 2 524 52418.00 I   .135820  .000043   .537054  .000049  I -.2278096  .0000020  1.3158 0.0016  I   -51.985     .287    -5.008     .340  0.135990  0.537030 -0.2278030   -51.900    -5.100  
+ 2 525 52419.00 I   .138173  .000036   .536021  .000048  I -.2290156  .0000026  1.0744 0.0016  I   -52.570     .101    -5.081     .100  0.138220  0.536070 -0.2290250   -52.100    -4.900  
+ 2 526 52420.00 I   .140457  .000034   .534941  .000055  I -.2299334  .0000024  0.7581 0.0019  I   -52.794     .101    -5.152     .100  0.140550  0.534950 -0.2299640   -52.100    -4.800  
+ 2 527 52421.00 I   .143105  .000051   .533782  .000060  I -.2305356  .0000027  0.4533 0.0020  I   -52.706     .206    -5.138     .102  0.143240  0.533780 -0.2305660   -52.000    -4.800  
+ 2 528 52422.00 I   .146195  .000046   .532652  .000049  I -.2308616  .0000031  0.2133 0.0021  I   -52.458     .274    -5.122     .104  0.146250  0.532660 -0.2308730   -51.800    -4.900  
+ 2 529 52423.00 I   .148920  .000049   .531341  .000047  I -.2309940  .0000031  0.0685 0.0022  I   -52.198     .336    -5.234     .340  0.149140  0.531430 -0.2309950   -51.700    -5.000  
+ 2 530 52424.00 I   .151861  .000049   .529999  .000047  I -.2310346  .0000031  0.0315 0.0022  I   -52.057     .336    -5.455     .340  0.151940  0.530030 -0.2310410   -51.700    -5.300  
+ 2 531 52425.00 I   .154660  .000052   .528524  .000051  I -.2310732  .0000032  0.0429 0.0022  I   -52.101     .336    -5.618     .340  0.154720  0.528570 -0.2310720   -51.900    -5.600  
+ 2 6 1 52426.00 I   .157254  .000051   .526992  .000049  I -.2311274  .0000031  0.0753 0.0023  I   -52.252     .336    -5.620     .340  0.157340  0.527040 -0.2311240   -51.800    -5.700  
+ 2 6 2 52427.00 I   .159754  .000052   .525267  .000059  I -.2312328  .0000032  0.1348 0.0022  I   -52.354     .308    -5.534     .340  0.159880  0.525310 -0.2312140   -51.700    -5.600  
+ 2 6 3 52428.00 I   .162414  .000051   .523380  .000059  I -.2313968  .0000030  0.1936 0.0022  I   -52.360     .296    -5.478     .340  0.162500  0.523430 -0.2313740   -51.700    -5.500  
+ 2 6 4 52429.00 I   .165131  .000048   .521795  .000055  I -.2316129  .0000029  0.2312 0.0021  I   -52.404     .183    -5.446     .117  0.165300  0.521670 -0.2316120   -51.800    -5.300  
+ 2 6 5 52430.00 I   .168230  .000074   .520114  .000054  I -.2318286  .0000029  0.1769 0.0021  I   -52.639     .193    -5.335     .131  0.168340  0.520030 -0.2318370   -52.300    -5.100  
+ 2 6 6 52431.00 I   .171414  .000075   .518441  .000054  I -.2319476  .0000029  0.0654 0.0021  I   -53.061     .193    -5.113     .131  0.171450  0.518430 -0.2319380   -52.900    -5.000  
+ 2 6 7 52432.00 I   .174114  .000081   .516725  .000075  I -.2319645  .0000031 -0.0308 0.0028  I   -53.538     .193    -4.888     .131  0.174530  0.516740 -0.2319570   -53.500    -5.000  
+ 2 6 8 52433.00 I   .177634  .000075   .515071  .000060  I -.2318775  .0000049 -0.1524 0.0028  I   -53.804     .318    -4.835     .148  0.177650  0.515060 -0.2318840   -53.800    -5.100  
+ 2 6 9 52434.00 I   .180641  .000078   .513456  .000062  I -.2316492  .0000046 -0.3053 0.0042  I   -54.050     .247    -4.977     .103  0.180710  0.513450 -0.2316690   -53.800    -5.200  
+ 2 610 52435.00 I   .183530  .000090   .511787  .000067  I -.2312677  .0000068 -0.4556 0.0048  I   -54.114     .247    -5.253     .103  0.183570  0.511780 -0.2312790   -53.800    -5.300  
+ 2 611 52436.00 I   .186077  .000081   .509890  .000077  I -.2307375  .0000085 -0.6072 0.0054  I   -54.054     .173    -5.506     .340  0.186120  0.509890 -0.2307300   -53.800    -5.400  
+ 2 612 52437.00 I   .188424  .000085   .507805  .000080  I -.2300812  .0000083 -0.6750 0.0059  I   -54.008     .163    -5.620     .340  0.188420  0.507800 -0.2300660   -53.800    -5.500  
+ 2 613 52438.00 I   .190960  .000083   .505694  .000065  I -.2294015  .0000082 -0.7007 0.0058  I   -54.077     .163    -5.607     .340  0.190770  0.505750 -0.2293550   -53.900    -5.500  
+ 2 614 52439.00 I   .193075  .000086   .503992  .000071  I -.2287126  .0000081 -0.6280 0.0067  I   -54.204     .163    -5.568     .340  0.193220  0.503920 -0.2287030   -53.900    -5.500  
+ 2 615 52440.00 I   .195887  .000082   .502180  .000071  I -.2281968  .0000105 -0.4015 0.0054  I   -54.182     .221    -5.589     .340  0.195910  0.502180 -0.2282220   -53.700    -5.500  
+ 2 616 52441.00 I   .199050  .000072   .500101  .000063  I -.2279198  .0000072 -0.1445 0.0065  I   -53.994     .222    -5.603     .340  0.199100  0.500090 -0.2279630   -53.400    -5.500  
+ 2 617 52442.00 I   .202216  .000052   .497766  .000062  I -.2279049  .0000078  0.1038 0.0055  I   -53.706     .200    -5.541     .340  0.202290  0.497760 -0.2279310   -53.300    -5.500  
+ 2 618 52443.00 I   .204825  .000041   .495346  .000055  I -.2280920  .0000082  0.2461 0.0056  I   -53.574     .226    -5.371     .124  0.204940  0.495220 -0.2280790   -53.400    -5.400  
+ 2 619 52444.00 I   .207395  .000051   .492822  .000068  I -.2283708  .0000080  0.3097 0.0057  I   -53.810     .328    -5.149     .103  0.207500  0.492730 -0.2283380   -53.900    -5.200  
+ 2 620 52445.00 I   .210129  .000052   .490564  .000066  I -.2286949  .0000079  0.3240 0.0054  I   -54.417     .328    -4.970     .103  0.210240  0.490560 -0.2286600   -54.500    -5.000  
+ 2 621 52446.00 I   .212475  .000055   .488780  .000064  I -.2289991  .0000072  0.2792 0.0059  I   -55.163     .328    -4.896     .103  0.212630  0.488670 -0.2289890   -55.100    -4.800  
+ 2 622 52447.00 I   .214565  .000054   .486718  .000080  I -.2292301  .0000088  0.1649 0.0045  I   -55.677     .459    -4.937     .340  0.214630  0.486680 -0.2292480   -55.500    -4.700  
+ 2 623 52448.00 I   .216538  .000061   .484377  .000075  I -.2293139  .0000054  0.0058 0.0049  I   -55.889     .331    -4.997     .340  0.216550  0.484340 -0.2293260   -55.500    -4.700  
+ 2 624 52449.00 I   .218215  .000057   .481849  .000074  I -.2292565  .0000044 -0.1088 0.0032  I   -55.778     .331    -5.051     .340  0.218230  0.481840 -0.2292470   -55.300    -4.800  
+ 2 625 52450.00 I   .219488  .000047   .479106  .000056  I -.2291217  .0000036 -0.1472 0.0029  I   -55.532     .740    -5.117     .340  0.219580  0.479140 -0.2291160   -55.000    -5.000  
+ 2 626 52451.00 I   .220855  .000053   .476274  .000048  I -.2289744  .0000038 -0.1487 0.0026  I   -55.340     .178    -5.242     .140  0.220900  0.476260 -0.2289770   -54.800    -5.200  
+ 2 627 52452.00 I   .222495  .000050   .473233  .000048  I -.2288397  .0000038 -0.1040 0.0025  I   -55.293     .178    -5.406     .140  0.222530  0.473240 -0.2288370   -54.700    -5.500  
+ 2 628 52453.00 I   .224475  .000049   .470176  .000041  I -.2287886  .0000034  0.0072 0.0025  I   -55.398     .178    -5.518     .140  0.224420  0.470060 -0.2287840   -54.900    -5.600  
+ 2 629 52454.00 I   .225974  .000042   .467245  .000030  I -.2288471  .0000032  0.0974 0.0021  I   -55.673     .233    -5.563     .117  0.226010  0.467180 -0.2288750   -55.200    -5.700  
+ 2 630 52455.00 I   .227145  .000056   .464496  .000035  I -.2289808  .0000025  0.1799 0.0021  I   -55.957     .195    -5.524     .102  0.227260  0.464490 -0.2290160   -55.700    -5.600  
+ 2 7 1 52456.00 I   .228085  .000056   .461571  .000040  I -.2292183  .0000027  0.2966 0.0017  I   -56.223     .172    -5.510     .340  0.228170  0.461610 -0.2292270   -56.100    -5.400  
+ 2 7 2 52457.00 I   .228670  .000050   .458420  .000045  I -.2295755  .0000024  0.4177 0.0018  I   -56.422     .131    -5.490     .340  0.228800  0.458450 -0.2295720   -56.300    -5.300  
+ 2 7 3 52458.00 I   .229477  .000054   .455657  .000050  I -.2300230  .0000025  0.4468 0.0017  I   -56.604     .131    -5.365     .340  0.229470  0.455480 -0.2300200   -56.400    -5.300  
+ 2 7 4 52459.00 I   .229929  .000056   .452705  .000050  I -.2304413  .0000025  0.3926 0.0018  I   -56.916     .131    -5.120     .340  0.230020  0.452640 -0.2304250   -56.500    -5.300  
+ 2 7 5 52460.00 I   .230325  .000055   .449629  .000047  I -.2307979  .0000025  0.3073 0.0024  I   -57.440     .131    -4.873     .340  0.230440  0.449620 -0.2307870   -57.000    -5.300  
+ 2 7 6 52461.00 I   .230813  .000042   .446335  .000056  I -.2310385  .0000040  0.1696 0.0021  I   -58.055     .112    -4.771     .340  0.230840  0.446330 -0.2310450   -57.400    -5.300  
+ 2 7 7 52462.00 I   .231538  .000033   .442868  .000064  I -.2311346  .0000034  0.0245 0.0037  I   -58.514     .149    -4.874     .215  0.231560  0.442800 -0.2311160   -57.800    -5.300  
+ 2 7 8 52463.00 I   .232357  .000037   .439566  .000096  I -.2310913  .0000062 -0.1093 0.0034  I   -58.673     .149    -5.132     .215  0.232370  0.439500 -0.2310570   -58.200    -5.300  
+ 2 7 9 52464.00 I   .233101  .000043   .436299  .000090  I -.2309258  .0000060 -0.2139 0.0042  I   -58.599     .440    -5.431     .182  0.233070  0.436310 -0.2309210   -58.400    -5.300  
+ 2 710 52465.00 I   .234036  .000040   .433127  .000093  I -.2306797  .0000058 -0.2699 0.0042  I   -58.451     .427    -5.639     .150  0.234030  0.433250 -0.2306780   -58.300    -5.300  
+ 2 711 52466.00 I   .235477  .000040   .430263  .000093  I -.2304023  .0000058 -0.2769 0.0040  I   -58.317     .427    -5.675     .150  0.235400  0.430340 -0.2303350   -58.000    -5.400  
+ 2 712 52467.00 I   .236735  .000037   .427399  .000091  I -.2301805  .0000056 -0.1201 0.0045  I   -58.212     .427    -5.573     .150  0.236820  0.427380 -0.2301690   -57.800    -5.400  
+ 2 713 52468.00 I   .238121  .000045   .424430  .000081  I -.2301960  .0000068  0.1383 0.0038  I   -58.224     .449    -5.447     .171  0.238100  0.424380 -0.2302190   -57.600    -5.500  
+ 2 714 52469.00 I   .239167  .000063   .421365  .000053  I -.2304459  .0000051  0.3633 0.0047  I   -58.244     .421    -5.392     .340  0.239200  0.421320 -0.2304620   -57.700    -5.500  
+ 2 715 52470.00 I   .240194  .000061   .418308  .000040  I -.2309062  .0000064  0.5393 0.0039  I   -58.353     .338    -5.388     .340  0.240220  0.418270 -0.2308980   -57.900    -5.400  
+ 2 716 52471.00 I   .241186  .000066   .415505  .000037  I -.2314847  .0000060  0.5958 0.0041  I   -58.531     .121    -5.362     .340  0.241160  0.415450 -0.2314830   -58.200    -5.300  
+ 2 717 52472.00 I   .241880  .000062   .412784  .000043  I -.2320585  .0000050  0.5343 0.0039  I   -58.784     .150    -5.277     .109  0.241970  0.412750 -0.2320660   -58.500    -5.300  
+ 2 718 52473.00 I   .242880  .000069   .410344  .000044  I -.2325272  .0000050  0.3944 0.0035  I   -59.135     .150    -5.159     .109  0.242950  0.410280 -0.2324960   -58.800    -5.200  
+ 2 719 52474.00 I   .244016  .000068   .408159  .000041  I -.2328183  .0000049  0.1674 0.0036  I   -59.570     .150    -5.056     .109  0.243920  0.408040 -0.2328090   -59.100    -5.200  
+ 2 720 52475.00 I   .244296  .000055   .405615  .000045  I -.2328375  .0000052 -0.1331 0.0030  I   -59.978     .173    -5.050     .105  0.244380  0.405560 -0.2328360   -59.400    -5.100  
+ 2 721 52476.00 I   .244742  .000064   .402725  .000054  I -.2325613  .0000034 -0.4061 0.0031  I   -60.203     .201    -5.063     .340  0.244780  0.402680 -0.2325410   -59.700    -5.100  
+ 2 722 52477.00 I   .245495  .000067   .399749  .000060  I -.2320551  .0000034 -0.5896 0.0022  I   -60.157     .201    -5.150     .340  0.245540  0.399670 -0.2320260   -59.900    -5.100  
+ 2 723 52478.00 I   .246117  .000050   .396855  .000064  I -.2314115  .0000029 -0.6850 0.0022  I   -59.923     .242    -5.304     .340  0.246260  0.396850 -0.2314050   -59.800    -5.100  
+ 2 724 52479.00 I   .246666  .000063   .394095  .000071  I -.2307041  .0000029 -0.7235 0.0021  I   -59.694     .310    -5.477     .340  0.246670  0.394040 -0.2307260   -59.600    -5.200  
+ 2 725 52480.00 I   .246900  .000063   .391189  .000074  I -.2299998  .0000030 -0.6562 0.0021  I   -59.614     .310    -5.580     .340  0.246940  0.391050 -0.2300230   -59.200    -5.300  
+ 2 726 52481.00 I   .246744  .000068   .387877  .000069  I -.2294179  .0000030 -0.5102 0.0020  I   -59.699     .310    -5.544     .340  0.246920  0.387830 -0.2294130   -58.900    -5.400  
+ 2 727 52482.00 I   .246820  .000061   .384346  .000072  I -.2289761  .0000027 -0.3739 0.0020  I   -59.932     .349    -5.396     .340  0.246880  0.384320 -0.2289960   -58.700    -5.400  
+ 2 728 52483.00 I   .247096  .000068   .380592  .000076  I -.2286673  .0000025 -0.2460 0.0020  I   -60.218     .362    -5.262     .340  0.247190  0.380540 -0.2287050   -58.700    -5.300  
+ 2 729 52484.00 I   .247619  .000080   .376810  .000076  I -.2284689  .0000030 -0.1638 0.0019  I   -60.495     .412    -5.219     .340  0.247690  0.376780 -0.2284900   -59.000    -5.200  
+ 2 730 52485.00 I   .248459  .000054   .373119  .000067  I -.2283114  .0000029 -0.1664 0.0020  I   -60.621     .386    -5.202     .340  0.248530  0.373170 -0.2283280   -59.500    -5.100  
+ 2 731 52486.00 I   .249767  .000055   .369842  .000075  I -.2281178  .0000025 -0.2239 0.0019  I   -60.603     .462    -5.096     .119  0.249840  0.369800 -0.2281490   -60.200    -5.100  
+ 2 8 1 52487.00 I   .251195  .000055   .366848  .000072  I -.2278583  .0000025 -0.2971 0.0018  I   -60.708     .462    -4.915     .119  0.251250  0.366800 -0.2278600   -60.800    -5.000  
+ 2 8 2 52488.00 I   .252209  .000059   .364110  .000067  I -.2275238  .0000025 -0.3695 0.0024  I   -61.195     .462    -4.807     .119  0.252240  0.364080 -0.2275160   -61.400    -5.000  
+ 2 8 3 52489.00 I   .252951  .000045   .361548  .000041  I -.2271092  .0000042 -0.4721 0.0023  I   -61.936     .527    -4.876     .167  0.253020  0.361480 -0.2271160   -61.600    -5.100  
+ 2 8 4 52490.00 I   .253921  .000054   .358980  .000054  I -.2265683  .0000038 -0.6085 0.0028  I   -62.484     .559    -5.071     .141  0.253980  0.358920 -0.2265870   -61.700    -5.200  
+ 2 8 5 52491.00 I   .254925  .000055   .356325  .000053  I -.2259038  .0000036 -0.7099 0.0028  I   -62.551     .559    -5.284     .141  0.254980  0.356280 -0.2259130   -61.500    -5.300  
+ 2 8 6 52492.00 I   .255633  .000053   .353419  .000054  I -.2251723  .0000040 -0.7399 0.0026  I   -62.268     .590    -5.471     .107  0.255770  0.353310 -0.2251650   -61.200    -5.400  
+ 2 8 7 52493.00 I   .256726  .000055   .350230  .000068  I -.2244566  .0000038 -0.6723 0.0028  I   -61.992     .584    -5.630     .172  0.256720  0.350180 -0.2244500   -60.900    -5.400  
+ 2 8 8 52494.00 I   .257858  .000050   .347410  .000068  I -.2238681  .0000038 -0.4836 0.0025  I   -61.705     .584    -5.649     .172  0.257930  0.347350 -0.2238700   -60.700    -5.300  
+ 2 8 9 52495.00 I   .259013  .000051   .344903  .000071  I -.2235232  .0000032 -0.1934 0.0033  I   -61.472     .584    -5.498     .172  0.259060  0.344730 -0.2235210   -60.600    -5.300  
+ 2 810 52496.00 I   .259881  .000040   .342253  .000065  I -.2234895  .0000053  0.1214 0.0033  I   -61.376     .504    -5.272     .209  0.259930  0.342210 -0.2235000   -60.900    -5.200  
+ 2 811 52497.00 I   .260387  .000055   .339692  .000080  I -.2237494  .0000057  0.3863 0.0039  I   -61.547     .426    -5.161     .194  0.260430  0.339680 -0.2237760   -61.300    -5.200  
+ 2 812 52498.00 I   .260493  .000057   .336844  .000081  I -.2242302  .0000056  0.5554 0.0043  I   -61.955     .426    -5.238     .194  0.260510  0.336830 -0.2242470   -61.800    -5.200  
+ 2 813 52499.00 I   .260687  .000060   .333646  .000067  I -.2248094  .0000065  0.5720 0.0042  I   -62.386     .331    -5.385     .178  0.260660  0.333730 -0.2248020   -62.300    -5.200  
+ 2 814 52500.00 I   .260828  .000066   .330859  .000060  I -.2253333  .0000062  0.4658 0.0045  I   -62.664     .354    -5.440     .131  0.260880  0.330800 -0.2253290   -62.600    -5.200  
+ 2 815 52501.00 I   .260774  .000076   .328167  .000060  I -.2257140  .0000063  0.2786 0.0039  I   -62.794     .354    -5.369     .131  0.260800  0.328160 -0.2257190   -62.800    -5.200  
+ 2 816 52502.00 I   .260540  .000071   .325597  .000060  I -.2258714  .0000048  0.0340 0.0040  I   -62.898     .354    -5.259     .131  0.260590  0.325560 -0.2258670   -62.700    -5.300  
+ 2 817 52503.00 I   .260229  .000064   .322975  .000040  I -.2257922  .0000049 -0.1799 0.0031  I   -63.109     .336    -5.181     .340  0.260270  0.322960 -0.2258040   -62.600    -5.300  
+ 2 818 52504.00 I   .259728  .000064   .320286  .000048  I -.2255360  .0000039 -0.3204 0.0034  I   -63.271     .295    -5.171     .340  0.259760  0.320270 -0.2255640   -62.600    -5.300  
+ 2 819 52505.00 I   .259106  .000058   .317546  .000044  I -.2251760  .0000046 -0.3871 0.0028  I   -63.295     .295    -5.225     .340  0.259100  0.317490 -0.2251930   -62.500    -5.300  
+ 2 820 52506.00 I   .258503  .000059   .314720  .000047  I -.2247865  .0000041 -0.3797 0.0030  I   -63.114     .246    -5.344     .340  0.258500  0.314620 -0.2247810   -62.500    -5.300  
+ 2 821 52507.00 I   .257794  .000052   .311720  .000050  I -.2244519  .0000039 -0.2664 0.0028  I   -62.800     .214    -5.503     .340  0.257860  0.311680 -0.2244460   -62.500    -5.400  
+ 2 822 52508.00 I   .257201  .000051   .308634  .000050  I -.2242797  .0000039 -0.0752 0.0027  I   -62.594     .214    -5.530     .340  0.257220  0.308630 -0.2242690   -62.500    -5.300  
+ 2 823 52509.00 I   .256792  .000051   .305386  .000045  I -.2242788  .0000036  0.0478 0.0030  I   -62.554     .214    -5.346     .340  0.256760  0.305340 -0.2242730   -62.600    -5.300  
+ 2 824 52510.00 I   .256222  .000040   .302075  .000030  I -.2243658  .0000045  0.1422 0.0025  I   -62.624     .223    -5.006     .340  0.256260  0.302050 -0.2243560   -62.500    -5.100  
+ 2 825 52511.00 I   .255523  .000041   .298675  .000044  I -.2245688  .0000035  0.2534 0.0029  I   -62.760     .187    -4.726     .340  0.255550  0.298650 -0.2245560   -62.400    -5.000  
+ 2 826 52512.00 I   .254858  .000039   .295170  .000043  I -.2248545  .0000035  0.3102 0.0024  I   -62.855     .187    -4.640     .340  0.254880  0.295130 -0.2248520   -62.400    -4.800  
+ 2 827 52513.00 I   .254463  .000040   .291714  .000051  I -.2251742  .0000034  0.3218 0.0023  I   -62.799     .143    -4.680     .340  0.254570  0.291610 -0.2251720   -62.300    -4.700  
+ 2 828 52514.00 I   .254529  .000033   .288453  .000053  I -.2254807  .0000031  0.2811 0.0023  I   -62.610     .105    -4.689     .340  0.254600  0.288320 -0.2254850   -62.400    -4.600  
+ 2 829 52515.00 I   .254519  .000036   .285396  .000053  I -.2257157  .0000032  0.1784 0.0022  I   -62.614     .140    -4.648     .340  0.254560  0.285420 -0.2257490   -62.500    -4.600  
+ 2 830 52516.00 I   .254091  .000033   .282309  .000058  I -.2258262  .0000030  0.0419 0.0023  I   -63.009     .140    -4.718     .340  0.254150  0.282230 -0.2258220   -62.700    -4.700  
+ 2 831 52517.00 I   .253545  .000028   .279192  .000059  I -.2258030  .0000034 -0.0841 0.0028  I   -63.710     .740    -4.964     .340  0.253570  0.279080 -0.2258010   -62.800    -4.800  
+ 2 9 1 52518.00 I   .252756  .000028   .276056  .000060  I -.2256657  .0000047 -0.1871 0.0032  I   -64.185     .740    -5.236     .340  0.252800  0.276040 -0.2256610   -62.900    -4.900  
+ 2 9 2 52519.00 I   .251578  .000042   .272855  .000063  I -.2254402  .0000055 -0.2560 0.0041  I   -64.067     .740    -5.341     .340  0.251620  0.272820 -0.2254090   -62.800    -5.100  
+ 2 9 3 52520.00 I   .250129  .000052   .269689  .000070  I -.2251797  .0000067 -0.2468 0.0042  I   -63.531     .238    -5.274     .132  0.250140  0.269660 -0.2251190   -62.700    -5.100  
+ 2 9 4 52521.00 I   .248953  .000054   .266676  .000066  I -.2249860  .0000064 -0.1192 0.0046  I   -63.001     .233    -5.163     .175  0.248990  0.266590 -0.2249760   -62.600    -5.100  
+ 2 9 5 52522.00 I   .248303  .000058   .263660  .000068  I -.2249649  .0000064  0.0792 0.0045  I   -62.677     .233    -5.043     .175  0.248290  0.263550 -0.2249850   -62.500    -5.000  
+ 2 9 6 52523.00 I   .247358  .000064   .260693  .000059  I -.2251708  .0000062  0.3568 0.0043  I   -62.523     .233    -4.839     .175  0.247480  0.260610 -0.2251690   -62.600    -4.900  
+ 2 9 7 52524.00 I   .246202  .000078   .258028  .000064  I -.2257018  .0000057  0.7042 0.0042  I   -62.544     .233    -4.586     .175  0.246250  0.257990 -0.2256930   -62.800    -4.900  
+ 2 9 8 52525.00 I   .244623  .000075   .255658  .000047  I -.2265511  .0000056  0.9673 0.0040  I   -62.801     .239    -4.485     .170  0.244650  0.255640 -0.2265270   -62.900    -4.900  
+ 2 9 9 52526.00 I   .242763  .000070   .253201  .000041  I -.2275770  .0000056  1.0521 0.0042  I   -63.210     .172    -4.652     .172  0.242770  0.253220 -0.2275570   -63.000    -4.900  
+ 2 910 52527.00 I   .241036  .000074   .250576  .000074  I -.2285921  .0000062  0.9474 0.0038  I   -63.549     .650    -4.924     .340  0.241050  0.250530 -0.2286070   -63.100    -4.900  
+ 2 911 52528.00 I   .239272  .000075   .248027  .000075  I -.2294206  .0000052  0.6893 0.0040  I   -63.692     .544    -5.056     .340  0.239380  0.247960 -0.2294510   -63.300    -4.900  
+ 2 912 52529.00 I   .237442  .000068   .245443  .000075  I -.2299704  .0000052  0.4304 0.0036  I   -63.689     .544    -4.974     .340  0.237550  0.245520 -0.2299690   -63.400    -4.900  
+ 2 913 52530.00 I   .235727  .000062   .242781  .000069  I -.2303005  .0000051  0.2288 0.0032  I   -63.661     .544    -4.873     .340  0.235820  0.242720 -0.2302950   -63.400    -4.900  
+ 2 914 52531.00 I   .234195  .000049   .239697  .000073  I -.2304384  .0000038  0.0584 0.0029  I   -63.678     .663    -4.891     .106  0.234230  0.239620 -0.2304410   -63.300    -4.900  
+ 2 915 52532.00 I   .232872  .000048   .236809  .000070  I -.2304447  .0000028 -0.0298 0.0024  I   -63.738     .549    -4.972     .340  0.232900  0.236760 -0.2304400   -63.300    -4.900  
+ 2 916 52533.00 I   .231666  .000046   .234534  .000049  I -.2304053  .0000029 -0.0383 0.0022  I   -63.764     .165    -5.008     .340  0.231690  0.234540 -0.2303880   -63.100    -4.900  
+ 2 917 52534.00 I   .230134  .000044   .232605  .000039  I -.2303821  .0000033 -0.0046 0.0023  I   -63.641     .125    -4.999     .340  0.230150  0.232650 -0.2303780   -62.900    -4.800  
+ 2 918 52535.00 I   .227992  .000045   .230846  .000039  I -.2304091  .0000036  0.0681 0.0024  I   -63.353     .190    -4.995     .340  0.228000  0.230880 -0.2304330   -62.800    -4.800  
+ 2 919 52536.00 I   .225292  .000045   .229151  .000036  I -.2305297  .0000036  0.1751 0.0025  I   -63.037     .190    -4.949     .340  0.225360  0.229150 -0.2305380   -62.600    -4.700  
+ 2 920 52537.00 I   .222316  .000045   .227178  .000037  I -.2307597  .0000034  0.2835 0.0030  I   -62.845     .190    -4.740     .340  0.222380  0.227190 -0.2307560   -62.500    -4.600  
+ 2 921 52538.00 I   .219435  .000041   .224964  .000036  I -.2310915  .0000049  0.3763 0.0027  I   -62.688     .176    -4.357     .340  0.219440  0.224910 -0.2310880   -62.400    -4.400  
+ 2 922 52539.00 I   .216745  .000051   .222702  .000050  I -.2315018  .0000042  0.4375 0.0036  I   -62.654     .203    -4.030     .340  0.216770  0.222670 -0.2314890   -62.200    -4.300  
+ 2 923 52540.00 I   .214368  .000050   .220362  .000050  I -.2319528  .0000054  0.4577 0.0031  I   -62.560     .203    -3.932     .340  0.214380  0.220350 -0.2319350   -62.100    -4.100  
+ 2 924 52541.00 I   .212100  .000045   .217747  .000053  I -.2323967  .0000045  0.4166 0.0036  I   -62.376     .273    -4.040     .114  0.212310  0.217770 -0.2323930   -61.900    -4.100  
+ 2 925 52542.00 I   .210452  .000047   .215446  .000060  I -.2327672  .0000048  0.3192 0.0033  I   -62.154     .273    -4.177     .103  0.210510  0.215420 -0.2327630   -61.700    -4.100  
+ 2 926 52543.00 I   .208767  .000051   .213100  .000059  I -.2330159  .0000048  0.1643 0.0033  I   -62.067     .273    -4.266     .103  0.208750  0.213110 -0.2329580   -61.600    -4.200  
+ 2 927 52544.00 I   .206775  .000049   .210842  .000060  I -.2331152  .0000045  0.0676 0.0035  I   -62.297     .273    -4.404     .103  0.206800  0.210720 -0.2330990   -61.600    -4.400  
+ 2 928 52545.00 I   .204745  .000034   .208471  .000052  I -.2331567  .0000050 -0.0121 0.0030  I   -62.746     .297    -4.656     .340  0.204730  0.208400 -0.2332060   -61.700    -4.500  
+ 2 929 52546.00 I   .202677  .000043   .205888  .000058  I -.2330781  .0000041 -0.1304 0.0042  I   -63.007     .264    -4.880     .340  0.202690  0.205880 -0.2331410   -61.800    -4.600  
+ 2 930 52547.00 I   .200592  .000049   .203187  .000058  I -.2329247  .0000068 -0.1624 0.0033  I   -62.773     .231    -4.869     .340  0.200580  0.203140 -0.2329430   -61.800    -4.600  
+ 210 1 52548.00 I   .198430  .000053   .200445  .000053  I -.2327953  .0000052 -0.0683 0.0041  I   -62.175     .179    -4.617     .340  0.198520  0.200480 -0.2328000   -61.800    -4.500  
+ 210 2 52549.00 I   .196564  .000055   .198099  .000057  I -.2328142  .0000045  0.1040 0.0034  I   -61.543     .142    -4.279     .106  0.196610  0.198090 -0.2328330   -61.500    -4.200  
+ 210 3 52550.00 I   .194659  .000053   .195695  .000059  I -.2330134  .0000045  0.3065 0.0031  I   -61.224     .142    -4.028     .106  0.194670  0.195650 -0.2330330   -61.200    -3.800  
+ 210 4 52551.00 I   .192452  .000050   .193467  .000053  I -.2334663  .0000042  0.6264 0.0034  I   -61.158     .142    -3.786     .106  0.192520  0.193410 -0.2334550   -61.000    -3.600  
+ 210 5 52552.00 I   .189854  .000047   .191374  .000046  I -.2342676  .0000051  0.9507 0.0028  I   -61.357     .126    -3.547     .132  0.189930  0.191370 -0.2343080   -60.900    -3.600  
+ 210 6 52553.00 I   .187313  .000042   .189304  .000053  I -.2353188  .0000037  1.1285 0.0033  I   -61.641     .106    -3.467     .116  0.187340  0.189270 -0.2353840   -61.000    -3.800  
+ 210 7 52554.00 I   .184951  .000051   .187197  .000066  I -.2364692  .0000042  1.1403 0.0032  I   -61.884     .740    -3.665     .106  0.184980  0.187140 -0.2364880   -61.200    -4.000  
+ 210 8 52555.00 I   .182522  .000046   .185011  .000070  I -.2375451  .0000053  0.9894 0.0032  I   -61.896     .740    -3.967     .340  0.182620  0.184970 -0.2375410   -61.500    -4.100  
+ 210 9 52556.00 I   .180036  .000055   .182676  .000067  I -.2384155  .0000048  0.7411 0.0036  I   -61.714     .740    -4.071     .340  0.180170  0.182730 -0.2384380   -61.600    -4.000  
+ 21010 52557.00 I   .177936  .000056   .180677  .000067  I -.2390205  .0000049  0.4722 0.0035  I   -61.543     .740    -3.931     .340  0.177910  0.180620 -0.2390100   -61.700    -3.800  
+ 21011 52558.00 I   .175524  .000057   .178743  .000064  I -.2393753  .0000050  0.2493 0.0038  I   -61.483     .740    -3.811     .340  0.175550  0.178830 -0.2393640   -61.600    -3.700  
+ 21012 52559.00 I   .173126  .000067   .176892  .000062  I -.2395524  .0000057  0.1262 0.0031  I   -61.469     .143    -3.917     .340  0.173160  0.176910 -0.2395600   -61.400    -3.700  
+ 21013 52560.00 I   .170913  .000055   .175068  .000054  I -.2396689  .0000038  0.1265 0.0040  I   -61.444     .160    -4.130     .110  0.170950  0.175020 -0.2396870   -61.200    -3.700  
+ 21014 52561.00 I   .168481  .000055   .173411  .000070  I -.2398339  .0000055  0.2121 0.0033  I   -61.408     .173    -4.193     .120  0.168490  0.173340 -0.2398360   -61.100    -3.800  
+ 21015 52562.00 I   .165852  .000049   .171533  .000064  I -.2401034  .0000053  0.3284 0.0035  I   -61.316     .186    -4.051     .147  0.165930  0.171640 -0.2400990   -60.900    -3.900  
+ 21016 52563.00 I   .163384  .000053   .170132  .000062  I -.2404951  .0000043  0.4578 0.0032  I   -61.189     .240    -3.878     .149  0.163530  0.170080 -0.2405390   -60.900    -3.900  
+ 21017 52564.00 I   .161183  .000053   .168869  .000058  I -.2410285  .0000037  0.6158 0.0027  I   -60.879     .230    -3.750     .150  0.161140  0.168790 -0.2410910   -60.800    -3.700  
+ 21018 52565.00 I   .158396  .000053   .167813  .000057  I -.2417084  .0000034  0.7188 0.0027  I   -60.641     .228    -3.602     .145  0.158390  0.167740 -0.2417050   -60.700    -3.500  
+ 21019 52566.00 I   .154524  .000049   .166697  .000051  I -.2424420  .0000039  0.7493 0.0023  I   -60.525     .156    -3.349     .133  0.154650  0.166660 -0.2424460   -60.600    -3.300  
+ 21020 52567.00 I   .150245  .000047   .165404  .000045  I -.2432013  .0000030  0.7626 0.0025  I   -60.432     .159    -3.074     .120  0.150440  0.165330 -0.2432310   -60.500    -3.100  
+ 21021 52568.00 I   .146611  .000056   .163868  .000047  I -.2439578  .0000032  0.7479 0.0023  I   -60.281     .176    -2.956     .115  0.146600  0.163910 -0.2439770   -60.300    -3.100  
+ 21022 52569.00 I   .143148  .000054   .162588  .000052  I -.2446889  .0000034  0.7088 0.0024  I   -60.108     .218    -3.046     .101  0.143220  0.162700 -0.2446860   -60.100    -3.100  
+ 21023 52570.00 I   .139888  .000054   .161306  .000057  I -.2453597  .0000035  0.6228 0.0025  I   -59.977     .205    -3.223     .340  0.139950  0.161420 -0.2453520   -60.000    -3.100  
+ 21024 52571.00 I   .136555  .000056   .160043  .000065  I -.2459212  .0000036  0.4971 0.0023  I   -59.916     .181    -3.365     .340  0.136600  0.159880 -0.2459250   -59.900    -3.200  
+ 21025 52572.00 I   .132959  .000059   .158365  .000065  I -.2463482  .0000031  0.3545 0.0023  I   -59.933     .171    -3.478     .340  0.133050  0.158470 -0.2463450   -59.900    -3.400  
+ 21026 52573.00 I   .129479  .000059   .156684  .000064  I -.2466410  .0000030  0.2445 0.0021  I   -60.121     .186    -3.625     .340  0.129500  0.156810 -0.2466280   -59.800    -3.500  
+ 21027 52574.00 I   .125826  .000049   .155193  .000067  I -.2468584  .0000029  0.1982 0.0021  I   -60.069     .179    -3.722     .340  0.125880  0.155240 -0.2468390   -59.600    -3.500  
+ 21028 52575.00 I   .122277  .000045   .154017  .000060  I -.2470415  .0000029  0.1641 0.0020  I   -59.781     .185    -3.670     .340  0.122280  0.153930 -0.2470170   -59.400    -3.500  
+ 21029 52576.00 I   .118729  .000043   .152877  .000050  I -.2472156  .0000027  0.2170 0.0019  I   -59.302     .212    -3.449     .104  0.118900  0.152880 -0.2472120   -59.000    -3.300  
+ 21030 52577.00 I   .115378  .000042   .152147  .000042  I -.2475286  .0000026  0.4292 0.0019  I   -58.829     .260    -3.171     .340  0.115420  0.152150 -0.2475260   -58.600    -3.100  
+ 21031 52578.00 I   .111554  .000040   .151452  .000041  I -.2480936  .0000026  0.7003 0.0019  I   -58.526     .263    -2.923     .340  0.111560  0.151430 -0.2480760   -58.200    -2.800  
+ 211 1 52579.00 I   .107456  .000041   .150724  .000037  I -.2489322  .0000027  0.9806 0.0026  I   -58.468     .263    -2.682     .340  0.107540  0.150700 -0.2489260   -58.100    -2.600  
+ 211 2 52580.00 I   .103551  .000058   .149991  .000053  I -.2500339  .0000046  1.1983 0.0028  I   -58.713     .270    -2.440     .340  0.103570  0.149990 -0.2500350   -58.200    -2.500  
+ 211 3 52581.00 I   .099863  .000070   .149384  .000076  I -.2512752  .0000049  1.2554 0.0037  I   -59.078     .262    -2.330     .340  0.099910  0.149380 -0.2512750   -58.500    -2.400  
+ 211 4 52582.00 I   .096363  .000098   .148926  .000092  I -.2525032  .0000058  1.1882 0.0047  I   -59.338     .244    -2.461     .101  0.096410  0.148870 -0.2524980   -58.700    -2.500  
+ 211 5 52583.00 I   .092454  .000103   .148083  .000102  I -.2535988  .0000080  0.9627 0.0046  I   -59.264     .168    -2.700     .108  0.092750  0.148250 -0.2535730   -58.900    -2.500  
+ 211 6 52584.00 I   .088989  .000093   .147567  .000097  I -.2544123  .0000072  0.6882 0.0053  I   -58.934     .181    -2.773     .340  0.088800  0.147510 -0.2544100   -58.800    -2.400  
+ 211 7 52585.00 I   .084733  .000092   .146842  .000097  I -.2549895  .0000071  0.4567 0.0048  I   -58.632     .181    -2.613     .340  0.084780  0.146840 -0.2549740   -58.500    -2.400  
+ 211 8 52586.00 I   .080605  .000091   .146169  .000104  I -.2553384  .0000064  0.2633 0.0046  I   -58.494     .181    -2.470     .340  0.080700  0.146110 -0.2553240   -58.100    -2.400  
+ 211 9 52587.00 I   .076653  .000088   .145400  .000075  I -.2555385  .0000059  0.1369 0.0041  I   -58.406     .210    -2.575     .340  0.076660  0.145430 -0.2555430   -57.700    -2.400  
+ 21110 52588.00 I   .072792  .000055   .144872  .000074  I -.2556375  .0000050  0.0863 0.0041  I   -58.265     .283    -2.822     .340  0.072840  0.144860 -0.2556750   -57.500    -2.400  
+ 21111 52589.00 I   .069204  .000055   .144512  .000072  I -.2557455  .0000056  0.1388 0.0038  I   -58.127     .283    -2.898     .340  0.069250  0.144500 -0.2557700   -57.400    -2.500  
+ 21112 52590.00 I   .065865  .000057   .144240  .000077  I -.2559352  .0000056  0.2514 0.0038  I   -58.043     .365    -2.689     .340  0.065950  0.144170 -0.2559250   -57.500    -2.500  
+ 21113 52591.00 I   .062931  .000074   .143850  .000080  I -.2562653  .0000050  0.4149 0.0038  I   -58.046     .266    -2.389     .103  0.063020  0.143850 -0.2562560   -57.600    -2.500  
+ 21114 52592.00 I   .059962  .000071   .143707  .000079  I -.2567568  .0000050  0.5539 0.0035  I   -57.926     .266    -2.198     .103  0.059990  0.143740 -0.2567520   -57.900    -2.400  
+ 21115 52593.00 I   .056305  .000067   .143674  .000082  I -.2573501  .0000049  0.6238 0.0044  I   -57.843     .266    -2.109     .103  0.056470  0.143620 -0.2573380   -58.000    -2.200  
+ 21116 52594.00 I   .052983  .000072   .143654  .000094  I -.2579905  .0000073  0.6522 0.0038  I   -57.837     .274    -1.975     .116  0.053070  0.143640 -0.2579920   -58.000    -2.000  
+ 21117 52595.00 I   .049731  .000071   .143605  .000106  I -.2586403  .0000057  0.6382 0.0048  I   -57.825     .231    -1.757     .340  0.049760  0.143600 -0.2586500   -57.800    -1.700  
+ 21118 52596.00 I   .046266  .000075   .143164  .000109  I -.2592524  .0000063  0.5806 0.0046  I   -57.751     .231    -1.586     .340  0.046290  0.143150 -0.2592520   -57.500    -1.600  
+ 21119 52597.00 I   .042668  .000066   .142504  .000111  I -.2597790  .0000072  0.4556 0.0046  I   -57.685     .341    -1.590     .340  0.042760  0.142590 -0.2597580   -57.200    -1.600  
+ 21120 52598.00 I   .039023  .000068   .142080  .000100  I -.2601516  .0000066  0.2946 0.0049  I   -57.693     .440    -1.754     .125  0.039060  0.142050 -0.2601350   -57.000    -1.700  
+ 21121 52599.00 I   .035643  .000069   .141645  .000099  I -.2603927  .0000066  0.2072 0.0045  I   -57.695     .440    -1.957     .125  0.035610  0.141620 -0.2603750   -56.800    -1.900  
+ 21122 52600.00 I   .032382  .000070   .141439  .000086  I -.2605844  .0000062  0.1752 0.0047  I   -57.564     .440    -2.096     .125  0.032530  0.141440 -0.2605540   -56.700    -2.100  
+ 21123 52601.00 I   .029455  .000060   .141705  .000056  I -.2607451  .0000067  0.1493 0.0039  I   -57.270     .518    -2.145     .340  0.029500  0.141590 -0.2607550   -56.600    -2.200  
+ 21124 52602.00 I   .026278  .000057   .141919  .000061  I -.2609050  .0000047  0.1894 0.0040  I   -56.936     .422    -2.143     .340  0.026290  0.141920 -0.2609170   -56.600    -2.200  
+ 21125 52603.00 I   .023146  .000053   .142072  .000060  I -.2611561  .0000042  0.3263 0.0032  I   -56.599     .389    -2.102     .340  0.023140  0.142090 -0.2611320   -56.500    -2.100  
+ 21126 52604.00 I   .020419  .000036   .142111  .000058  I -.2615777  .0000042  0.5237 0.0030  I   -56.262     .228    -2.032     .340  0.020440  0.142180 -0.2615560   -56.400    -2.000  
+ 21127 52605.00 I   .017702  .000038   .141972  .000056  I -.2622236  .0000042  0.7814 0.0030  I   -55.941     .228    -1.938     .340  0.017780  0.141910 -0.2622010   -56.300    -2.000  
+ 21128 52606.00 I   .014598  .000038   .141668  .000055  I -.2631419  .0000042  1.0434 0.0029  I   -55.707     .228    -1.807     .340  0.014610  0.141600 -0.2631340   -56.300    -1.800  
+ 21129 52607.00 I   .011103  .000038   .141506  .000058  I -.2642958  .0000041  1.2617 0.0040  I   -55.671     .228    -1.616     .340  0.011120  0.141530 -0.2642190   -56.300    -1.600  
+ 21130 52608.00 I   .007579  .000035   .141384  .000061  I -.2656414  .0000067  1.4082 0.0024  I   -56.053     .199    -1.385     .340  0.007650  0.141380 -0.2655300   -56.300    -1.300  
+ 212 1 52609.00 I   .004190  .000043   .141181  .000057  I -.2670650  .0000023  1.4130 0.0036  I   -56.523     .273    -1.234     .340  0.004230  0.141160 -0.2669760   -56.400    -1.100  
+ 212 2 52610.00 I   .000976  .000043   .141175  .000064  I -.2684199  .0000028  1.2753 0.0028  I   -56.957     .273    -1.261     .340  0.000980  0.141150 -0.2683720   -56.700    -1.500  
+ 212 3 52611.00 I  -.002188  .000055   .141366  .000082  I -.2695781  .0000050  1.0250 0.0027  I   -57.095     .366    -1.407     .340 -0.002100  0.141540 -0.2695620   -56.800    -1.400  
+ 212 4 52612.00 I  -.005190  .000058   .142182  .000082  I -.2704599  .0000046  0.7448 0.0034  I   -56.926     .353    -1.485     .340 -0.005140  0.142250 -0.2704590   -56.600    -1.300  
+ 212 5 52613.00 I  -.008410  .000060   .143068  .000080  I -.2710687  .0000046  0.4678 0.0033  I   -56.663     .353    -1.411     .340 -0.008540  0.143130 -0.2710520   -56.300    -1.300  
+ 212 6 52614.00 I  -.012360  .000066   .144207  .000078  I -.2714357  .0000046  0.3116 0.0041  I   -56.452     .353    -1.326     .340 -0.012430  0.144160 -0.2714150   -55.900    -1.300  
+ 212 7 52615.00 I  -.016720  .000076   .145538  .000076  I -.2717517  .0000068  0.3349 0.0036  I   -56.242     .350    -1.406     .340 -0.016710  0.145520 -0.2717620   -55.600    -1.300  
+ 212 8 52616.00 I  -.020888  .000062   .146709  .000092  I -.2721330  .0000056  0.4408 0.0047  I   -55.972     .301    -1.596     .340 -0.020860  0.146750 -0.2721450   -55.500    -1.400  
+ 212 9 52617.00 I  -.024269  .000055   .147737  .000096  I -.2726535  .0000065  0.6073 0.0038  I   -55.727     .327    -1.660     .340 -0.024190  0.147790 -0.2726310   -55.500    -1.400  
+ 21210 52618.00 I  -.027099  .000052   .149028  .000066  I -.2733480  .0000050  0.7750 0.0041  I   -55.631     .145    -1.480     .340 -0.027050  0.149110 -0.2733250   -55.600    -1.400  
+ 21211 52619.00 I  -.030097  .000054   .150624  .000068  I -.2742030  .0000049  0.9413 0.0035  I   -55.665     .147    -1.203     .340 -0.029990  0.150660 -0.2742050   -55.700    -1.300  
+ 21212 52620.00 I  -.032910  .000054   .152253  .000070  I -.2751980  .0000049  1.0100 0.0034  I   -55.794     .147    -1.033     .340 -0.032910  0.152320 -0.2751850   -55.700    -1.200  
+ 21213 52621.00 I  -.036521  .000056   .153643  .000074  I -.2762097  .0000048  1.0389 0.0040  I   -55.943     .147     -.985     .340 -0.036380  0.153690 -0.2762010   -55.700    -1.000  
+ 21214 52622.00 I  -.040135  .000058   .154901  .000064  I -.2772696  .0000063  1.0493 0.0037  I   -56.075     .163     -.905     .340 -0.040130  0.154810 -0.2772850   -55.800    -0.800  
+ 21215 52623.00 I  -.044170  .000055   .155905  .000084  I -.2782719  .0000057  0.9506 0.0045  I   -56.135     .258     -.706     .340 -0.044120  0.155800 -0.2783150   -55.800    -0.600  
+ 21216 52624.00 I  -.048085  .000063   .156669  .000091  I -.2791553  .0000064  0.8055 0.0042  I   -56.122     .243     -.487     .340 -0.048100  0.156550 -0.2791780   -55.900    -0.400  
+ 21217 52625.00 I  -.051623  .000048   .157274  .000096  I -.2798786  .0000063  0.6475 0.0043  I   -56.130     .372     -.424     .340 -0.051560  0.157350 -0.2798630   -56.000    -0.500  
+ 21218 52626.00 I  -.054557  .000068   .158497  .000091  I -.2804448  .0000058  0.4730 0.0043  I   -56.238     .301     -.580     .340 -0.054500  0.158360 -0.2804270   -56.000    -0.600  
+ 21219 52627.00 I  -.056950  .000064   .159883  .000092  I -.2808192  .0000060  0.2824 0.0040  I   -56.288     .301     -.884     .340 -0.056950  0.159770 -0.2808220   -56.000    -0.800  
+ 21220 52628.00 I  -.059147  .000061   .161774  .000082  I -.2810419  .0000055  0.1889 0.0042  I   -56.129     .301    -1.142     .340 -0.059110  0.161760 -0.2810310   -55.900    -1.100  
+ 21221 52629.00 I  -.061584  .000055   .164084  .000047  I -.2812224  .0000058  0.1718 0.0041  I   -55.769     .274    -1.188     .340 -0.061540  0.164060 -0.2812280   -55.600    -1.300  
+ 21222 52630.00 I  -.064259  .000063   .166441  .000043  I -.2813994  .0000062  0.1965 0.0042  I   -55.283     .274    -1.132     .340 -0.064250  0.166410 -0.2814080   -55.000    -1.400  
+ 21223 52631.00 I  -.066758  .000059   .168646  .000046  I -.2816508  .0000061  0.3274 0.0110  I   -54.871     .104    -1.044     .340 -0.066770  0.168630 -0.2816380   -54.400    -1.500  
+ 21224 52632.00 I  -.069104  .000042   .170874  .000042  I -.2820935  .0000212  0.5763 0.0095  I   -54.583     .740    -1.015     .340 -0.069100  0.170830 -0.2820620   -53.900    -1.400  
+ 21225 52633.00 I  -.071648  .000041   .173153  .000039  I -.2828125  .0000180  0.8527 0.0135  I   -54.399     .740    -1.032     .340 -0.071640  0.173150 -0.2827880   -53.500    -1.300  
+ 21226 52634.00 I  -.074203  .000041   .175133  .000034  I -.2837772  .0000166  1.0642 0.0097  I   -54.322     .740    -1.015     .340 -0.074230  0.175140 -0.2837740   -53.300    -1.100  
+ 21227 52635.00 I  -.076492  .000044   .176860  .000026  I -.2849084  .0000072  1.1783 0.0095  I   -54.406     .740     -.910     .340 -0.076500  0.176820 -0.2849120   -53.500    -0.900  
+ 21228 52636.00 I  -.078749  .000059   .178859  .000039  I -.2860947  .0000092  1.1751 0.0049  I   -54.548     .237     -.740     .121 -0.078730  0.178830 -0.2860920   -54.000    -0.700  
+ 21229 52637.00 I  -.081140  .000060   .181116  .000050  I -.2872199  .0000067  1.0558 0.0057  I   -54.991     .160     -.587     .108 -0.081160  0.181130 -0.2872110   -54.600    -0.500  
+ 21230 52638.00 I  -.083425  .000058   .183254  .000051  I -.2881773  .0000068  0.8494 0.0044  I   -55.413     .160     -.527     .108 -0.083470  0.183210 -0.2881680   -55.100    -0.400  
+ 21231 52639.00 I  -.085587  .000075   .185612  .000050  I -.2889136  .0000058  0.6277 0.0042  I   -55.626     .141     -.568     .340 -0.085710  0.185460 -0.2889120   -55.500    -0.400  
+ 3 1 1 52640.00 I  -.088506  .000087   .187936  .000054  I -.2894400  .0000050  0.4280 0.0038  I   -55.580     .132     -.644     .340 -0.088460  0.187930 -0.2894480   -55.500    -0.500  
+ 3 1 2 52641.00 I  -.091525  .000081   .190250  .000054  I -.2897971  .0000050  0.3107 0.0035  I   -55.381     .132     -.698     .340 -0.091550  0.190200 -0.2897920   -55.300    -0.600  
+ 3 1 3 52642.00 I  -.094535  .000063   .192681  .000051  I -.2901029  .0000050  0.3178 0.0037  I   -55.151     .132     -.756     .340 -0.094510  0.192640 -0.2900920   -54.900    -0.700  
+ 3 1 4 52643.00 I  -.097547  .000065   .195185  .000044  I -.2904422  .0000055  0.3533 0.0035  I   -54.916     .740     -.876     .340 -0.097560  0.195210 -0.2904570   -54.500    -0.800  
+ 3 1 5 52644.00 I  -.100674  .000059   .197402  .000045  I -.2908236  .0000050  0.4311 0.0038  I   -54.663     .740    -1.025     .340 -0.100680  0.197410 -0.2908460   -54.300    -0.900  
+ 3 1 6 52645.00 I  -.103497  .000061   .199315  .000044  I -.2913364  .0000051  0.6049 0.0034  I   -54.443     .740    -1.079     .340 -0.103540  0.199310 -0.2913490   -54.100    -1.000  
+ 3 1 7 52646.00 I  -.105918  .000043   .201164  .000043  I -.2920312  .0000047  0.7721 0.0035  I   -54.366     .186     -.977     .136 -0.105960  0.201290 -0.2920180   -54.100    -1.000  
+ 3 1 8 52647.00 I  -.108609  .000049   .203326  .000045  I -.2928525  .0000047  0.8551 0.0033  I   -54.485     .194     -.817     .141 -0.108520  0.203380 -0.2928610   -54.300    -1.000  
+ 3 1 9 52648.00 I  -.111066  .000052   .205508  .000044  I -.2937266  .0000047  0.8936 0.0034  I   -54.737     .194     -.733     .141 -0.111060  0.205470 -0.2937380   -54.500    -0.900  
+ 3 110 52649.00 I  -.113231  .000046   .207881  .000043  I -.2946374  .0000048  0.9252 0.0035  I   -54.991     .194     -.724     .141 -0.113170  0.207880 -0.2946290   -54.700    -0.800  
+ 3 111 52650.00 I  -.115263  .000036   .210338  .000038  I -.2955678  .0000052  0.9293 0.0033  I   -55.122     .120     -.632     .195 -0.115240  0.210360 -0.2955520   -54.800    -0.600  
+ 3 112 52651.00 I  -.117288  .000031   .212631  .000038  I -.2964717  .0000044  0.8607 0.0034  I   -55.163     .120     -.462     .171 -0.117260  0.212610 -0.2964480   -54.900    -0.400  
+ 3 113 52652.00 I  -.118995  .000036   .214760  .000036  I -.2972660  .0000045  0.7227 0.0032  I   -55.158     .120     -.272     .154 -0.119000  0.214730 -0.2972470   -54.900    -0.300  
+ 3 114 52653.00 I  -.120321  .000035   .216851  .000046  I -.2979152  .0000046  0.5791 0.0030  I   -55.190     .121     -.227     .210 -0.120310  0.216890 -0.2979030   -55.000    -0.300  
+ 3 115 52654.00 I  -.121679  .000033   .219146  .000044  I -.2984219  .0000039  0.4288 0.0029  I   -55.273     .121     -.412     .181 -0.121570  0.219120 -0.2984020   -55.100    -0.600  
+ 3 116 52655.00 I  -.122808  .000035   .221634  .000041  I -.2987735  .0000035  0.2806 0.0024  I   -55.330     .121     -.762     .181 -0.122870  0.221530 -0.2987500   -55.200    -0.900  
+ 3 117 52656.00 I  -.124297  .000038   .224033  .000040  I -.2990067  .0000029  0.2030 0.0025  I   -55.258     .122    -1.113     .340 -0.124280  0.224060 -0.2989930   -55.100    -1.100  
+ 3 118 52657.00 I  -.126047  .000045   .226509  .000041  I -.2992149  .0000037  0.2315 0.0024  I   -54.974     .137    -1.297     .340 -0.125980  0.226530 -0.2992100   -54.900    -1.300  
+ 3 119 52658.00 I  -.127673  .000043   .228811  .000045  I -.2994968  .0000039  0.3411 0.0028  I   -54.527     .132    -1.257     .340 -0.127580  0.228790 -0.2994690   -54.500    -1.300  
+ 3 120 52659.00 I  -.128873  .000038   .231030  .000050  I -.2999075  .0000043  0.4818 0.0039  I   -54.053     .168    -1.086     .340 -0.128840  0.231010 -0.2998440   -54.100    -1.100  
+ 3 121 52660.00 I  -.129940  .000051   .233478  .000061  I -.3004639  .0000067  0.6331 0.0042  I   -53.724     .287     -.943     .340 -0.129910  0.233420 -0.3004010   -53.700    -1.000  
+ 3 122 52661.00 I  -.131006  .000056   .236356  .000068  I -.3011699  .0000072  0.7731 0.0052  I   -53.649     .308     -.913     .110 -0.130970  0.236350 -0.3011540   -53.400    -0.900  
+ 3 123 52662.00 I  -.132220  .000052   .239582  .000070  I -.3019920  .0000079  0.8588 0.0052  I   -53.817     .308     -.956     .110 -0.132140  0.239610 -0.3020130   -53.400    -0.900  
+ 3 124 52663.00 I  -.133567  .000049   .242395  .000071  I -.3028736  .0000076  0.9028 0.0054  I   -54.132     .308     -.979     .110 -0.133540  0.242440 -0.3028670   -53.600    -0.900  
+ 3 125 52664.00 I  -.135144  .000051   .244806  .000067  I -.3037659  .0000073  0.8515 0.0048  I   -54.457     .283     -.916     .101 -0.135120  0.244810 -0.3037500   -53.900    -0.900  
+ 3 126 52665.00 I  -.136792  .000049   .247042  .000053  I -.3045278  .0000059  0.6536 0.0045  I   -54.774     .287     -.812     .115 -0.136780  0.247020 -0.3045260   -54.400    -0.800  
+ 3 127 52666.00 I  -.138126  .000042   .249381  .000044  I -.3050546  .0000053  0.4003 0.0038  I   -54.991     .287     -.705     .115 -0.138090  0.249450 -0.3050560   -54.700    -0.700  
+ 3 128 52667.00 I  -.138992  .000037   .252107  .000042  I -.3053340  .0000049  0.1640 0.0034  I   -55.045     .181     -.652     .104 -0.138880  0.252210 -0.3053250   -54.900    -0.700  
+ 3 129 52668.00 I  -.139269  .000049   .255088  .000043  I -.3054123  .0000043  0.0170 0.0033  I   -54.910     .153     -.689     .101 -0.139280  0.255170 -0.3054140   -54.800    -0.800  
+ 3 130 52669.00 I  -.139478  .000051   .258219  .000043  I -.3054011  .0000043 -0.0306 0.0029  I   -54.642     .167     -.837     .340 -0.139380  0.258240 -0.3053970   -54.500    -0.900  
+ 3 131 52670.00 I  -.139794  .000051   .261292  .000044  I -.3053653  .0000039 -0.0356 0.0036  I   -54.354     .167    -1.031     .340 -0.139870  0.261290 -0.3053620   -54.200    -1.100  
+ 3 2 1 52671.00 I  -.141155  .000059   .264059  .000065  I -.3053500  .0000057  0.0198 0.0031  I   -54.147     .116    -1.238     .340 -0.141130  0.264050 -0.3053730   -53.900    -1.200  
+ 3 2 2 52672.00 I  -.142642  .000061   .266788  .000072  I -.3054336  .0000047  0.1613 0.0035  I   -54.035     .125    -1.388     .340 -0.142570  0.266750 -0.3054490   -53.800    -1.200  
+ 3 2 3 52673.00 I  -.144127  .000075   .269650  .000076  I -.3056955  .0000040  0.3714 0.0030  I   -53.981     .125    -1.417     .340 -0.144090  0.269670 -0.3056800   -53.800    -1.200  
+ 3 2 4 52674.00 I  -.145231  .000062   .272399  .000079  I -.3061850  .0000036  0.6074 0.0027  I   -53.980     .251    -1.337     .340 -0.145260  0.272440 -0.3061760   -53.800    -1.200  
+ 3 2 5 52675.00 I  -.146060  .000059   .275320  .000074  I -.3068810  .0000035  0.7555 0.0025  I   -54.077     .227    -1.247     .340 -0.146160  0.275280 -0.3068960   -53.900    -1.200  
+ 3 2 6 52676.00 I  -.147316  .000056   .277939  .000075  I -.3076581  .0000036  0.7898 0.0025  I   -54.265     .227    -1.235     .340 -0.147270  0.278000 -0.3076810   -54.000    -1.200  
+ 3 2 7 52677.00 I  -.148426  .000054   .280393  .000067  I -.3084369  .0000035  0.7530 0.0027  I   -54.433     .227    -1.276     .340 -0.148390  0.280430 -0.3084350   -54.000    -1.200  
+ 3 2 8 52678.00 I  -.149667  .000055   .283189  .000061  I -.3091443  .0000041  0.6567 0.0025  I   -54.482     .268    -1.273     .340 -0.149610  0.283160 -0.3091510   -53.900    -1.200  
+ 3 2 9 52679.00 I  -.151304  .000036   .285945  .000063  I -.3097464  .0000035  0.5485 0.0029  I   -54.451     .296    -1.207     .340 -0.151270  0.285920 -0.3097880   -53.900    -1.100  
+ 3 210 52680.00 I  -.152949  .000036   .288612  .000061  I -.3102376  .0000040  0.4292 0.0030  I   -54.446     .273    -1.168     .340 -0.152910  0.288570 -0.3102810   -53.900    -1.100  
+ 3 211 52681.00 I  -.154297  .000048   .291302  .000062  I -.3106009  .0000049  0.2984 0.0030  I   -54.478     .472    -1.246     .162 -0.154280  0.291370 -0.3106030   -54.000    -1.300  
+ 3 212 52682.00 I  -.155365  .000055   .294094  .000057  I -.3108547  .0000045  0.2286 0.0034  I   -54.442     .411    -1.434     .152 -0.155300  0.294100 -0.3108090   -54.200    -1.500  
+ 3 213 52683.00 I  -.155875  .000057   .296851  .000058  I -.3110916  .0000046  0.2591 0.0032  I   -54.381     .411    -1.706     .152 -0.155910  0.296780 -0.3110340   -54.300    -1.900  
+ 3 214 52684.00 I  -.156091  .000060   .299473  .000055  I -.3113852  .0000046  0.3263 0.0035  I   -54.325     .411    -2.010     .152 -0.156160  0.299500 -0.3113730   -54.300    -2.100  
+ 3 215 52685.00 I  -.156343  .000059   .302128  .000032  I -.3117429  .0000053  0.3897 0.0034  I   -54.237     .465    -2.272     .193 -0.156320  0.302110 -0.3117780   -54.100    -2.200  
+ 3 216 52686.00 I  -.156302  .000073   .304583  .000039  I -.3121947  .0000049  0.5434 0.0034  I   -53.957     .439    -2.366     .176 -0.156290  0.304550 -0.3122450   -53.700    -2.100  
+ 3 217 52687.00 I  -.155862  .000062   .307063  .000046  I -.3128650  .0000041  0.8029 0.0029  I   -53.456     .155    -2.230     .118 -0.155780  0.307060 -0.3128800   -53.300    -1.900  
+ 3 218 52688.00 I  -.155323  .000064   .310072  .000049  I -.3137985  .0000031  1.0565 0.0024  I   -52.998     .186    -1.976     .340 -0.155280  0.310030 -0.3137900   -53.000    -1.800  
+ 3 219 52689.00 I  -.154911  .000071   .313488  .000054  I -.3149478  .0000026  1.2194 0.0020  I   -52.877     .231    -1.828     .340 -0.154890  0.313550 -0.3149440   -52.900    -1.700  
+ 3 220 52690.00 I  -.154784  .000069   .317584  .000051  I -.3161986  .0000025  1.2662 0.0018  I   -53.220     .231    -1.863     .340 -0.154700  0.317550 -0.3161840   -53.100    -1.900  
+ 3 221 52691.00 I  -.155311  .000071   .321366  .000048  I -.3174385  .0000025  1.1880 0.0021  I   -53.721     .231    -2.012     .340 -0.155090  0.321380 -0.3174310   -53.400    -2.000  
+ 3 222 52692.00 I  -.155475  .000071   .324475  .000045  I -.3185303  .0000033  0.9769 0.0020  I   -54.093     .229    -2.117     .340 -0.155490  0.324470 -0.3185360   -53.800    -2.100  
+ 3 223 52693.00 I  -.155454  .000070   .327396  .000056  I -.3193753  .0000030  0.7149 0.0023  I   -54.254     .180    -2.103     .340 -0.155400  0.327390 -0.3193890   -54.100    -2.000  
+ 3 224 52694.00 I  -.155123  .000065   .330443  .000054  I -.3199700  .0000031  0.4827 0.0023  I   -54.273     .180    -2.014     .340 -0.155070  0.330430 -0.3199750   -54.200    -2.000  
+ 3 225 52695.00 I  -.154614  .000047   .333493  .000050  I -.3203602  .0000035  0.3088 0.0025  I   -54.202     .200    -1.938     .340 -0.154650  0.333430 -0.3203540   -54.000    -1.900  
+ 3 226 52696.00 I  -.154637  .000045   .336386  .000053  I -.3206182  .0000038  0.2265 0.0026  I   -54.016     .240    -1.948     .102 -0.154530  0.336370 -0.3206160   -53.700    -1.900  
+ 3 227 52697.00 I  -.154656  .000045   .339306  .000059  I -.3208399  .0000038  0.2244 0.0025  I   -53.693     .240    -2.073     .102 -0.154610  0.339280 -0.3208330   -53.200    -2.000  
+ 3 228 52698.00 I  -.154375  .000041   .342141  .000060  I -.3210741  .0000032  0.2437 0.0039  I   -53.310     .240    -2.286     .102 -0.154360  0.342110 -0.3210690   -52.800    -2.200  
+ 3 3 1 52699.00 I  -.153841  .000023   .344649  .000029  I -.3213467  .0000067  0.3212 0.0027  I   -53.008     .295    -2.471     .105 -0.153800  0.344630 -0.3213410   -52.500    -2.200  
+ 3 3 2 52700.00 I  -.153198  .000024   .347240  .000031  I -.3217410  .0000043  0.4720 0.0042  I   -52.919     .277    -2.570     .340 -0.153150  0.347190 -0.3217350   -52.600    -2.300  
+ 3 3 3 52701.00 I  -.152880  .000024   .350202  .000029  I -.3222894  .0000050  0.6188 0.0034  I   -52.973     .277    -2.491     .340 -0.152810  0.350190 -0.3222940   -52.800    -2.300  
+ 3 3 4 52702.00 I  -.153092  .000026   .353080  .000025  I -.3229464  .0000052  0.6689 0.0036  I   -53.052     .211    -2.302     .340 -0.153040  0.353130 -0.3229420   -53.000    -2.200  
+ 3 3 5 52703.00 I  -.153422  .000036   .355842  .000018  I -.3236106  .0000051  0.6690 0.0036  I   -53.122     .307    -2.166     .340 -0.153370  0.355830 -0.3236090   -53.200    -2.200  
+ 3 3 6 52704.00 I  -.153763  .000039   .358316  .000018  I -.3242961  .0000051  0.7040 0.0035  I   -53.174     .307    -2.176     .340 -0.153700  0.358310 -0.3242970   -53.300    -2.200  
+ 3 3 7 52705.00 I  -.154354  .000038   .360787  .000017  I -.3250005  .0000048  0.6846 0.0039  I   -53.177     .307    -2.289     .340 -0.154090  0.360750 -0.3249950   -53.300    -2.200  
+ 3 3 8 52706.00 I  -.154534  .000047   .363477  .000020  I -.3256463  .0000059  0.6082 0.0032  I   -53.092     .300    -2.393     .340 -0.154490  0.363470 -0.3256600   -53.100    -2.300  
+ 3 3 9 52707.00 I  -.154768  .000044   .366339  .000024  I -.3262172  .0000043  0.5326 0.0036  I   -53.001     .255    -2.479     .340 -0.154690  0.366290 -0.3262470   -53.000    -2.400  
+ 3 310 52708.00 I  -.154911  .000043   .369337  .000029  I -.3267117  .0000041  0.4574 0.0027  I   -53.008     .255    -2.630     .340 -0.154860  0.369300 -0.3267360   -52.800    -2.600  
+ 3 311 52709.00 I  -.155587  .000042   .372368  .000040  I -.3271255  .0000034  0.3627 0.0027  I   -53.033     .184    -2.864     .111 -0.155520  0.372380 -0.3271240   -52.800    -2.700  
+ 3 312 52710.00 I  -.156437  .000052   .375474  .000041  I -.3274535  .0000034  0.3174 0.0024  I   -52.883     .184    -3.071     .101 -0.156400  0.375480 -0.3274420   -52.700    -2.900  
+ 3 313 52711.00 I  -.157026  .000054   .378713  .000041  I -.3277792  .0000034  0.3289 0.0023  I   -52.580     .184    -3.187     .101 -0.157030  0.378590 -0.3277560   -52.700    -3.100  
+ 3 314 52712.00 I  -.157691  .000058   .381330  .000041  I -.3281342  .0000032  0.4089 0.0024  I   -52.385     .184    -3.316     .101 -0.157290  0.381370 -0.3281250   -52.500    -3.300  
+ 3 315 52713.00 I  -.157076  .000065   .384012  .000042  I -.3286353  .0000034  0.6036 0.0021  I   -52.390     .181    -3.571     .340 -0.157110  0.384010 -0.3286480   -52.100    -3.400  
+ 3 316 52714.00 I  -.156943  .000073   .386870  .000040  I -.3293660  .0000028  0.8722 0.0025  I   -52.324     .157    -3.839     .340 -0.156910  0.386880 -0.3293890   -51.700    -3.500  
+ 3 317 52715.00 I  -.156743  .000078   .389761  .000063  I -.3303950  .0000036  1.1868 0.0021  I   -51.935     .168    -3.868     .340 -0.156650  0.389760 -0.3304080   -51.300    -3.600  
+ 3 318 52716.00 I  -.155936  .000066   .392624  .000065  I -.3317189  .0000032  1.4392 0.0023  I   -51.450     .115    -3.601     .114 -0.155950  0.392630 -0.3317120   -51.200    -3.600  
+ 3 319 52717.00 I  -.154945  .000064   .395674  .000066  I -.3332271  .0000030  1.5525 0.0022  I   -51.354     .740    -3.289     .340 -0.154890  0.395810 -0.3332170   -51.300    -3.600  
+ 3 320 52718.00 I  -.153875  .000060   .399566  .000065  I -.3347675  .0000029  1.4965 0.0020  I   -51.788     .740    -3.210     .340 -0.153840  0.399600 -0.3347600   -51.700    -3.500  
+ 3 321 52719.00 I  -.153268  .000057   .403517  .000069  I -.3361673  .0000026  1.2819 0.0019  I   -52.385     .740    -3.369     .340 -0.153080  0.403390 -0.3361610   -52.100    -3.500  
+ 3 322 52720.00 I  -.152322  .000046   .406664  .000083  I -.3373037  .0000026  0.9843 0.0019  I   -52.733     .740    -3.562     .340 -0.152280  0.406640 -0.3373110   -52.500    -3.500  
+ 3 323 52721.00 I  -.151525  .000051   .409589  .000049  I -.3381325  .0000027  0.6764 0.0019  I   -52.701     .740    -3.638     .340 -0.151440  0.409600 -0.3381530   -52.600    -3.500  
+ 3 324 52722.00 I  -.150576  .000048   .412494  .000048  I -.3386755  .0000028  0.4255 0.0021  I   -52.520     .740    -3.643     .340 -0.150480  0.412410 -0.3386950   -52.500    -3.500  
+ 3 325 52723.00 I  -.148982  .000039   .415439  .000048  I -.3390145  .0000033  0.2677 0.0022  I   -52.377     .740    -3.669     .340 -0.149060  0.415390 -0.3390110   -52.200    -3.600  
+ 3 326 52724.00 I  -.147578  .000046   .418309  .000058  I -.3392556  .0000033  0.2439 0.0024  I   -52.275     .740    -3.735     .340 -0.147490  0.418350 -0.3392450   -51.900    -3.800  
+ 3 327 52725.00 I  -.145779  .000046   .421212  .000059  I -.3395425  .0000034  0.3405 0.0023  I   -52.098     .740    -3.813     .340 -0.145670  0.421160 -0.3395480   -51.700    -3.900  
+ 3 328 52726.00 I  -.143452  .000042   .424036  .000052  I -.3399480  .0000033  0.4712 0.0024  I   -51.784     .740    -3.895     .340 -0.143350  0.424030 -0.3399430   -51.500    -3.900  
+ 3 329 52727.00 I  -.140613  .000039   .426885  .000055  I -.3404906  .0000034  0.6191 0.0024  I   -51.437     .102    -3.957     .340 -0.140570  0.426870 -0.3404850   -51.500    -3.800  
+ 3 330 52728.00 I  -.137880  .000051   .429885  .000054  I -.3411865  .0000035  0.7679 0.0027  I   -51.244     .183    -3.908     .340 -0.137770  0.429840 -0.3412060   -51.500    -3.700  
+ 3 331 52729.00 I  -.135448  .000048   .432832  .000050  I -.3420147  .0000041  0.8816 0.0025  I   -51.280     .183    -3.679     .340 -0.135380  0.432830 -0.3420440   -51.500    -3.500  
+ 3 4 1 52730.00 I  -.133253  .000052   .435574  .000036  I -.3429313  .0000036  0.9401 0.0028  I   -51.438     .459    -3.347     .149 -0.133250  0.435620 -0.3429290   -51.400    -3.400  
+ 3 4 2 52731.00 I  -.131337  .000052   .438596  .000042  I -.3438876  .0000039  0.9767 0.0027  I   -51.553     .522    -3.109     .152 -0.131170  0.438590 -0.3438740   -51.200    -3.300  
+ 3 4 3 52732.00 I  -.129096  .000055   .441736  .000043  I -.3448712  .0000039  0.9728 0.0027  I   -51.586     .522    -3.153     .152 -0.129050  0.441740 -0.3448740   -51.000    -3.400  
+ 3 4 4 52733.00 I  -.127130  .000055   .444678  .000040  I -.3458013  .0000038  0.8732 0.0028  I   -51.514     .522    -3.390     .152 -0.126950  0.444670 -0.3457940   -50.800    -3.500  
+ 3 4 5 52734.00 I  -.124629  .000045   .447542  .000035  I -.3466152  .0000039  0.7663 0.0025  I   -51.368     .560    -3.641     .151 -0.124580  0.447540 -0.3466170   -50.800    -3.700  
+ 3 4 6 52735.00 I  -.122177  .000053   .450759  .000034  I -.3473352  .0000032  0.6632 0.0029  I   -51.258     .454    -3.842     .144 -0.122080  0.450760 -0.3473310   -50.900    -3.900  
+ 3 4 7 52736.00 I  -.119826  .000058   .454061  .000046  I -.3479283  .0000042  0.5202 0.0026  I   -51.285     .454    -4.074     .144 -0.119780  0.454050 -0.3479260   -51.000    -4.100  
+ 3 4 8 52737.00 I  -.117581  .000064   .456994  .000053  I -.3483759  .0000041  0.3781 0.0029  I   -51.342     .265    -4.359     .111 -0.117590  0.456990 -0.3483700   -51.100    -4.200  
+ 3 4 9 52738.00 I  -.116238  .000061   .459671  .000056  I -.3487087  .0000041  0.3087 0.0029  I   -51.174     .225    -4.558     .104 -0.115960  0.459710 -0.3487010   -50.900    -4.300  
+ 3 410 52739.00 I  -.114690  .000060   .462343  .000060  I -.3490285  .0000041  0.3449 0.0029  I   -50.751     .225    -4.567     .104 -0.114680  0.462350 -0.3490260   -50.700    -4.400  
+ 3 411 52740.00 I  -.113539  .000059   .464834  .000059  I -.3494229  .0000041  0.4544 0.0034  I   -50.391     .225    -4.532     .104 -0.113230  0.464850 -0.3494180   -50.300    -4.600  
+ 3 412 52741.00 I  -.111408  .000064   .467511  .000063  I -.3499690  .0000054  0.6588 0.0025  I   -50.343     .290    -4.688     .340 -0.111410  0.467490 -0.3499570   -49.900    -4.700  
+ 3 413 52742.00 I  -.109399  .000055   .470304  .000041  I -.3507694  .0000029  0.9497 0.0030  I   -50.418     .250    -5.004     .340 -0.109290  0.470290 -0.3507470   -49.600    -4.900  
+ 3 414 52743.00 I  -.107268  .000048   .472883  .000034  I -.3518708  .0000028  1.2479 0.0024  I   -50.297     .151    -5.153     .340 -0.107170  0.472880 -0.3518560   -49.600    -4.900  
+ 3 415 52744.00 I  -.105224  .000044   .475253  .000038  I -.3532336  .0000037  1.4514 0.0024  I   -50.073     .189    -4.923     .340 -0.105260  0.475280 -0.3532260   -49.800    -4.800  
+ 3 416 52745.00 I  -.103976  .000048   .477415  .000038  I -.3547178  .0000040  1.4874 0.0032  I   -50.363     .136    -4.461     .340 -0.103790  0.477410 -0.3547140   -50.200    -4.600  
+ 3 417 52746.00 I  -.102241  .000048   .479238  .000039  I -.3561391  .0000053  1.3154 0.0034  I   -50.934     .136    -4.223     .340 -0.102200  0.479230 -0.3561370   -50.700    -4.300  
+ 3 418 52747.00 I  -.100347  .000046   .481207  .000040  I -.3573084  .0000054  1.0228 0.0062  I   -51.604     .136    -4.276     .340 -0.100290  0.481160 -0.3572750   -51.000    -4.200  
+ 3 419 52748.00 I  -.098110  .000046   .483411  .000051  I -.3581655  .0000113  0.6726 0.0043  I   -51.877     .130    -4.406     .340 -0.098020  0.483390 -0.3581420   -51.100    -4.200  
+ 3 420 52749.00 I  -.095579  .000059   .485678  .000071  I -.3586492  .0000067  0.3093 0.0067  I   -51.679     .124    -4.532     .340 -0.095490  0.485660 -0.3586890   -50.900    -4.400  
+ 3 421 52750.00 I  -.093129  .000060   .487711  .000085  I -.3588349  .0000071  0.0983 0.0045  I   -51.252     .435    -4.668     .256 -0.093040  0.487740 -0.3589020   -50.700    -4.700  
+ 3 422 52751.00 I  -.090755  .000056   .489518  .000091  I -.3588929  .0000060  0.0284 0.0042  I   -50.908     .405    -4.882     .238 -0.090700  0.489620 -0.3588960   -50.500    -4.900  
+ 3 423 52752.00 I  -.088601  .000058   .491625  .000089  I -.3589248  .0000045  0.0578 0.0037  I   -50.782     .352    -5.082     .204 -0.088490  0.491700 -0.3588860   -50.400    -5.100  
+ 3 424 52753.00 I  -.086073  .000056   .493677  .000089  I -.3590416  .0000044  0.1867 0.0026  I   -50.792     .352    -5.122     .204 -0.085900  0.493740 -0.3590440   -50.400    -5.100  
+ 3 425 52754.00 I  -.082835  .000055   .495853  .000083  I -.3593016  .0000025  0.3259 0.0025  I   -50.741     .352    -5.002     .204 -0.082770  0.495770 -0.3592960   -50.500    -4.900  
+ 3 426 52755.00 I  -.080055  .000051   .497996  .000080  I -.3596948  .0000025  0.4691 0.0016  I   -50.540     .413    -4.824     .234 -0.079990  0.497960 -0.3596890   -50.400    -4.700  
+ 3 427 52756.00 I  -.077683  .000063   .500185  .000068  I -.3602410  .0000019  0.6157 0.0015  I   -50.334     .137    -4.628     .340 -0.077590  0.500160 -0.3602360   -50.300    -4.500  
+ 3 428 52757.00 I  -.075264  .000065   .502396  .000062  I -.3609113  .0000018  0.7175 0.0013  I   -50.344     .133    -4.349     .340 -0.075190  0.502440 -0.3609080   -50.300    -4.200  
+ 3 429 52758.00 I  -.072945  .000060   .504336  .000057  I -.3616574  .0000017  0.7635 0.0013  I   -50.590     .132    -4.013     .340 -0.072840  0.504420 -0.3616510   -50.400    -4.000  
+ 3 430 52759.00 I  -.070452  .000057   .506168  .000059  I -.3624152  .0000018  0.7402 0.0013  I   -50.875     .132    -3.814     .340 -0.070270  0.506180 -0.3624080   -50.800    -3.900  
+ 3 5 1 52760.00 I  -.066884  .000056   .508018  .000058  I -.3631068  .0000020  0.6237 0.0014  I   -51.014     .132    -3.925     .340 -0.066850  0.508020 -0.3631080   -51.100    -3.900  
+ 3 5 2 52761.00 I  -.063134  .000054   .509849  .000043  I -.3636406  .0000022  0.4410 0.0022  I   -50.976     .132    -4.288     .340 -0.063020  0.509870 -0.3636450   -51.200    -4.100  
+ 3 5 3 52762.00 I  -.059460  .000045   .511574  .000040  I -.3639858  .0000039  0.2491 0.0026  I   -50.818     .164    -4.673     .340 -0.059380  0.511610 -0.3639710   -51.100    -4.400  
+ 3 5 4 52763.00 I  -.056188  .000041   .513428  .000044  I -.3641403  .0000047  0.0615 0.0038  I   -50.705     .173    -4.909     .340 -0.056110  0.513430 -0.3640950   -50.900    -4.800  
+ 3 5 5 52764.00 I  -.053251  .000048   .515445  .000043  I -.3641271  .0000066 -0.0712 0.0037  I   -50.721     .173    -5.059     .340 -0.053140  0.515420 -0.3640860   -50.700    -5.100  
+ 3 5 6 52765.00 I  -.050102  .000064   .517481  .000047  I -.3640312  .0000057 -0.1038 0.0042  I   -50.831     .213    -5.218     .340 -0.050100  0.517520 -0.3640240   -50.600    -5.200  
+ 3 5 7 52766.00 I  -.047177  .000062   .519684  .000067  I -.3639292  .0000052 -0.1072 0.0038  I   -50.830     .284    -5.344     .340 -0.047150  0.519710 -0.3639140   -50.400    -5.300  
+ 3 5 8 52767.00 I  -.044759  .000066   .521707  .000067  I -.3638449  .0000051 -0.0264 0.0035  I   -50.584     .284    -5.339     .340 -0.044670  0.521610 -0.3638350   -50.300    -5.300  
+ 3 5 9 52768.00 I  -.042316  .000062   .523206  .000065  I -.3639183  .0000048  0.1802 0.0036  I   -50.240     .284    -5.263     .340 -0.042140  0.523330 -0.3639110   -50.300    -5.300  
+ 3 510 52769.00 I  -.039744  .000061   .525015  .000067  I -.3642129  .0000052  0.4095 0.0030  I   -49.972     .486    -5.277     .103 -0.039680  0.525050 -0.3642000   -50.100    -5.400  
+ 3 511 52770.00 I  -.037767  .000052   .526629  .000061  I -.3647435  .0000035  0.6574 0.0028  I   -49.993     .330    -5.449     .340 -0.037690  0.526600 -0.3647260   -49.900    -5.400  
+ 3 512 52771.00 I  -.036128  .000046   .527945  .000077  I -.3655317  .0000022  0.9174 0.0022  I   -50.086     .300    -5.525     .340 -0.036010  0.527970 -0.3655100   -49.900    -5.400  
+ 3 513 52772.00 I  -.034478  .000036   .529264  .000063  I -.3665666  .0000028  1.1423 0.0019  I   -50.269     .106    -5.298     .340 -0.034400  0.529290 -0.3665540   -50.000    -5.300  
+ 3 514 52773.00 I  -.032655  .000033   .530583  .000072  I -.3677702  .0000030  1.2291 0.0022  I   -50.726     .134    -4.887     .340 -0.032560  0.530530 -0.3677770   -50.600    -5.100  
+ 3 515 52774.00 I  -.030523  .000035   .531887  .000073  I -.3689547  .0000033  1.1058 0.0022  I   -51.457     .134    -4.605     .340 -0.030430  0.531850 -0.3689700   -51.200    -4.800  
+ 3 516 52775.00 I  -.028293  .000036   .533225  .000075  I -.3699223  .0000031  0.8039 0.0036  I   -52.127     .134    -4.587     .340 -0.028250  0.533180 -0.3699180   -51.700    -4.600  
+ 3 517 52776.00 I  -.025981  .000045   .534353  .000088  I -.3705531  .0000065  0.4736 0.0032  I   -52.379     .157    -4.675     .340 -0.025890  0.534300 -0.3705330   -51.900    -4.500  
+ 3 518 52777.00 I  -.023693  .000043   .535468  .000064  I -.3709000  .0000055  0.2347 0.0043  I   -52.128     .143    -4.799     .118 -0.023560  0.535470 -0.3708860   -51.600    -4.700  
+ 3 519 52778.00 I  -.021358  .000051   .536500  .000059  I -.3710701  .0000055  0.1385 0.0035  I   -51.589     .150    -4.999     .112 -0.021250  0.536550 -0.3710520   -51.200    -4.900  
+ 3 520 52779.00 I  -.018620  .000054   .537522  .000045  I -.3712217  .0000043  0.1764 0.0034  I   -51.061     .117    -5.335     .128 -0.018500  0.537530 -0.3712130   -50.800    -5.200  
+ 3 521 52780.00 I  -.015410  .000064   .538602  .000043  I -.3714380  .0000039  0.2598 0.0029  I   -50.794     .119    -5.642     .115 -0.015430  0.538610 -0.3714480   -50.600    -5.400  
+ 3 522 52781.00 I  -.012603  .000064   .539688  .000042  I -.3717551  .0000039  0.3847 0.0026  I   -50.863     .119    -5.672     .115 -0.012490  0.539640 -0.3717310   -50.600    -5.500  
+ 3 523 52782.00 I  -.009866  .000058   .540575  .000035  I -.3722105  .0000033  0.5188 0.0026  I   -51.090     .119    -5.399     .115 -0.009800  0.540440 -0.3722000   -50.900    -5.400  
+ 3 524 52783.00 I  -.007004  .000061   .540972  .000036  I -.3727809  .0000035  0.6174 0.0024  I   -51.212     .122    -5.042     .340 -0.006960  0.540960 -0.3727690   -51.200    -5.100  
+ 3 525 52784.00 I  -.004106  .000052   .541211  .000052  I -.3734260  .0000034  0.6579 0.0025  I   -51.203     .102    -4.775     .102 -0.004030  0.541340 -0.3733950   -51.400    -4.800  
+ 3 526 52785.00 I  -.001056  .000053   .541528  .000074  I -.3740728  .0000035  0.6265 0.0027  I   -51.287     .131    -4.572     .340 -0.000940  0.541610 -0.3740370   -51.700    -4.500  
+ 3 527 52786.00 I   .002298  .000052   .541977  .000096  I -.3746654  .0000042  0.5543 0.0024  I   -51.609     .154    -4.366     .197  0.002370  0.541990 -0.3746410   -51.800    -4.300  
+ 3 528 52787.00 I   .005837  .000072   .542598  .000087  I -.3751705  .0000032  0.4494 0.0026  I   -51.982     .214    -4.234     .172  0.005880  0.542640 -0.3751620   -52.000    -4.300  
+ 3 529 52788.00 I   .009260  .000080   .543591  .000086  I -.3755531  .0000032  0.3113 0.0022  I   -52.260     .214    -4.353     .172  0.009390  0.543690 -0.3755520   -52.100    -4.500  
+ 3 530 52789.00 I   .012429  .000072   .544507  .000081  I -.3757844  .0000029  0.1470 0.0021  I   -52.338     .214    -4.716     .172  0.012550  0.544580 -0.3757760   -52.200    -4.700  
+ 3 531 52790.00 I   .015576  .000071   .545546  .000078  I -.3758359  .0000027 -0.0510 0.0019  I   -52.300     .210    -5.116     .167  0.015640  0.545500 -0.3758550   -52.100    -5.000  
+ 3 6 1 52791.00 I   .018416  .000063   .546437  .000067  I -.3756855  .0000025 -0.2398 0.0018  I   -52.244     .210    -5.342     .172  0.018520  0.546410 -0.3757030   -52.000    -5.100  
+ 3 6 2 52792.00 I   .021231  .000064   .547100  .000061  I -.3753793  .0000025 -0.3595 0.0020  I   -52.247     .200    -5.371     .340  0.021340  0.546990 -0.3753690   -51.900    -5.200  
+ 3 6 3 52793.00 I   .024311  .000037   .547652  .000042  I -.3749825  .0000030 -0.4311 0.0022  I   -52.359     .154    -5.329     .135  0.024380  0.547540 -0.3749710   -51.900    -5.200  
+ 3 6 4 52794.00 I   .027719  .000039   .547855  .000057  I -.3745271  .0000037 -0.4726 0.0025  I   -52.524     .280    -5.312     .148  0.027810  0.547850 -0.3745520   -52.000    -5.300  
+ 3 6 5 52795.00 I   .031430  .000044   .547882  .000058  I -.3740730  .0000040 -0.4068 0.0028  I   -52.585     .280    -5.308     .148  0.031540  0.547890 -0.3740740   -52.100    -5.400  
+ 3 6 6 52796.00 I   .034917  .000044   .547818  .000051  I -.3737576  .0000042 -0.2085 0.0033  I   -52.455     .280    -5.286     .148  0.035020  0.547910 -0.3737490   -52.300    -5.400  
+ 3 6 7 52797.00 I   .038130  .000034   .547817  .000051  I -.3736737  .0000052  0.0429 0.0029  I   -52.218     .369    -5.218     .196  0.038190  0.547810 -0.3736780   -52.300    -5.300  
+ 3 6 8 52798.00 I   .041333  .000064   .547855  .000050  I -.3738447  .0000041  0.2983 0.0033  I   -52.115     .323    -5.183     .161  0.041410  0.547810 -0.3738780   -52.400    -5.200  
+ 3 6 9 52799.00 I   .044736  .000066   .547922  .000047  I -.3742503  .0000040  0.4937 0.0027  I   -52.278     .323    -5.064     .161  0.044810  0.547930 -0.3742770   -52.600    -4.900  
+ 3 610 52800.00 I   .048281  .000072   .548027  .000046  I -.3747833  .0000036  0.5426 0.0026  I   -52.767     .418    -4.808     .340  0.048330  0.548050 -0.3747790   -52.800    -4.700  
+ 3 611 52801.00 I   .051673  .000073   .548061  .000051  I -.3752832  .0000033  0.4346 0.0022  I   -53.549     .637    -4.534     .148  0.051790  0.548060 -0.3752700   -53.100    -4.500  
+ 3 612 52802.00 I   .055139  .000073   .547756  .000058  I -.3756082  .0000026  0.1937 0.0020  I   -54.346     .637    -4.383     .148  0.055260  0.547750 -0.3756120   -53.400    -4.500  
+ 3 613 52803.00 I   .058556  .000073   .547201  .000053  I -.3756412  .0000024 -0.1354 0.0030  I   -54.883     .637    -4.430     .148  0.058730  0.547230 -0.3756430   -53.700    -4.500  
+ 3 614 52804.00 I   .062094  .000037   .546689  .000057  I -.3753452  .0000054 -0.4411 0.0025  I   -55.004     .766    -4.554     .182  0.062170  0.546630 -0.3753230   -54.000    -4.700  
+ 3 615 52805.00 I   .065802  .000052   .546171  .000058  I -.3747957  .0000044 -0.6369 0.0035  I   -54.781     .667    -4.661     .159  0.065870  0.546100 -0.3747700   -54.100    -4.900  
+ 3 616 52806.00 I   .069733  .000051   .545588  .000054  I -.3741172  .0000044 -0.6955 0.0027  I   -54.378     .664    -4.826     .158  0.069830  0.545570 -0.3741000   -54.100    -5.100  
+ 3 617 52807.00 I   .074052  .000069   .545079  .000057  I -.3734464  .0000033 -0.6290 0.0027  I   -53.954     .405    -5.129     .128  0.074140  0.545040 -0.3734360   -54.100    -5.300  
+ 3 618 52808.00 I   .078547  .000067   .544862  .000057  I -.3728845  .0000032 -0.4864 0.0023  I   -53.629     .428    -5.435     .184  0.078630  0.544840 -0.3728820   -53.900    -5.400  
+ 3 619 52809.00 I   .082892  .000068   .544756  .000066  I -.3724844  .0000033 -0.3112 0.0023  I   -53.717     .428    -5.479     .184  0.083140  0.544680 -0.3724930   -53.800    -5.300  
+ 3 620 52810.00 I   .087700  .000069   .544532  .000061  I -.3722530  .0000032 -0.1633 0.0024  I   -54.102     .428    -5.203     .184  0.087770  0.544520 -0.3722550   -53.800    -5.200  
+ 3 621 52811.00 I   .091956  .000069   .544384  .000065  I -.3721348  .0000034 -0.0846 0.0022  I   -54.527     .391    -4.827     .199  0.092070  0.544420 -0.3721070   -53.800    -5.000  
+ 3 622 52812.00 I   .095888  .000078   .544085  .000066  I -.3720627  .0000031 -0.0694 0.0027  I   -54.812     .260    -4.589     .177  0.095980  0.544080 -0.3720050   -54.100    -4.800  
+ 3 623 52813.00 I   .099772  .000063   .543616  .000063  I -.3719772  .0000041 -0.1107 0.0025  I   -55.043     .308    -4.499     .227  0.099880  0.543620 -0.3719230   -54.400    -4.600  
+ 3 624 52814.00 I   .103554  .000057   .542996  .000067  I -.3718238  .0000038 -0.2046 0.0028  I   -55.376     .328    -4.425     .197  0.103640  0.543060 -0.3718090   -54.900    -4.400  
+ 3 625 52815.00 I   .107221  .000062   .542491  .000075  I -.3715540  .0000039 -0.3405 0.0030  I   -55.789     .345    -4.346     .212  0.107340  0.542480 -0.3715590   -55.400    -4.400  
+ 3 626 52816.00 I   .110928  .000058   .542076  .000073  I -.3711389  .0000046 -0.4880 0.0030  I   -56.129     .345    -4.388     .212  0.111030  0.542050 -0.3711060   -55.900    -4.400  
+ 3 627 52817.00 I   .114722  .000059   .541683  .000071  I -.3705786  .0000045 -0.6337 0.0030  I   -56.334     .345    -4.638     .212  0.114840  0.541520 -0.3705660   -56.200    -4.500  
+ 3 628 52818.00 I   .118619  .000038   .541064  .000064  I -.3698746  .0000040 -0.7700 0.0028  I   -56.407     .402    -4.976     .234  0.118730  0.540990 -0.3698540   -56.400    -4.700  
+ 3 629 52819.00 I   .122623  .000072   .540447  .000067  I -.3690496  .0000032 -0.8731 0.0025  I   -56.445     .270    -5.234     .165  0.122740  0.540420 -0.3690180   -56.300    -4.900  
+ 3 630 52820.00 I   .126689  .000078   .539883  .000064  I -.3681479  .0000029 -0.9175 0.0023  I   -56.421     .259    -5.271     .158  0.126790  0.539860 -0.3681090   -56.200    -5.100  
+ 3 7 1 52821.00 I   .130468  .000079   .539313  .000048  I -.3672420  .0000032 -0.8796 0.0020  I   -56.414     .226    -5.141     .340  0.130560  0.539290 -0.3672270   -56.200    -5.200  
+ 3 7 2 52822.00 I   .133821  .000080   .538582  .000051  I -.3664106  .0000027 -0.7761 0.0021  I   -56.477     .204    -5.030     .107  0.133980  0.538570 -0.3664210   -56.200    -5.100  
+ 3 7 3 52823.00 I   .136813  .000080   .537563  .000051  I -.3657010  .0000027 -0.6389 0.0020  I   -56.630     .204    -4.988     .107  0.137040  0.537500 -0.3656920   -56.400    -5.000  
+ 3 7 4 52824.00 I   .139880  .000072   .535923  .000049  I -.3651475  .0000029 -0.4576 0.0027  I   -56.681     .204    -5.024     .107  0.139980  0.535890 -0.3651290   -56.500    -4.800  
+ 3 7 5 52825.00 I   .143073  .000046   .534157  .000048  I -.3647861  .0000046 -0.2753 0.0029  I   -56.584     .238    -5.033     .101  0.143200  0.534100 -0.3647340   -56.500    -4.700  
+ 3 7 6 52826.00 I   .146403  .000056   .532623  .000055  I -.3645739  .0000050 -0.1620 0.0039  I   -56.504     .200    -4.928     .340  0.146510  0.532570 -0.3644890   -56.600    -4.600  
+ 3 7 7 52827.00 I   .149616  .000049   .531187  .000055  I -.3644331  .0000064 -0.1359 0.0031  I   -56.690     .740    -4.711     .340  0.149680  0.531190 -0.3643570   -56.800    -4.500  
+ 3 7 8 52828.00 I   .153014  .000057   .529717  .000057  I -.3642754  .0000038 -0.1901 0.0037  I   -57.246     .217    -4.463     .340  0.153090  0.529730 -0.3642560   -57.000    -4.500  
+ 3 7 9 52829.00 I   .156652  .000066   .528214  .000059  I -.3640177  .0000038 -0.3499 0.0027  I   -58.023     .280    -4.301     .340  0.156730  0.528180 -0.3640300   -57.400    -4.400  
+ 3 710 52830.00 I   .160400  .000070   .526692  .000060  I -.3635461  .0000037 -0.5983 0.0025  I   -58.701     .280    -4.300     .340  0.160450  0.526650 -0.3635320   -57.800    -4.400  
+ 3 711 52831.00 I   .163893  .000069   .525208  .000058  I -.3628283  .0000033 -0.8250 0.0022  I   -59.026     .280    -4.431     .340  0.164060  0.525270 -0.3628150   -58.100    -4.400  
+ 3 712 52832.00 I   .167447  .000065   .523922  .000055  I -.3619299  .0000025 -0.9503 0.0020  I   -58.947     .328    -4.642     .340  0.167540  0.523940 -0.3619300   -58.200    -4.400  
+ 3 713 52833.00 I   .170540  .000070   .522624  .000054  I -.3609656  .0000021 -0.9618 0.0018  I   -58.730     .298    -4.737     .129  0.170640  0.522650 -0.3609700   -58.100    -4.400  
+ 3 714 52834.00 I   .173119  .000070   .521200  .000052  I -.3600377  .0000025 -0.8791 0.0015  I   -58.502     .276    -4.826     .130  0.173240  0.521190 -0.3600350   -58.000    -4.500  
+ 3 715 52835.00 I   .175615  .000055   .519726  .000043  I -.3592355  .0000021 -0.7122 0.0016  I   -58.316     .380    -4.986     .138  0.175690  0.519660 -0.3592290   -58.000    -4.800  
+ 3 716 52836.00 I   .178216  .000065   .518207  .000044  I -.3586320  .0000020 -0.4892 0.0015  I   -58.214     .454    -5.159     .140  0.178280  0.518100 -0.3586240   -58.200    -5.100  
+ 3 717 52837.00 I   .180961  .000061   .516617  .000045  I -.3582506  .0000021 -0.2857 0.0014  I   -58.310     .454    -5.182     .140  0.180970  0.516510 -0.3582320   -58.500    -5.200  
+ 3 718 52838.00 I   .183442  .000058   .514713  .000042  I -.3580347  .0000019 -0.1600 0.0015  I   -58.665     .454    -4.996     .140  0.183560  0.514700 -0.3580200   -59.000    -5.000  
+ 3 719 52839.00 I   .186304  .000056   .512678  .000044  I -.3579064  .0000020 -0.1070 0.0014  I   -59.164     .523    -4.774     .340  0.186360  0.512610 -0.3579100   -59.500    -4.900  
+ 3 720 52840.00 I   .189291  .000067   .510645  .000045  I -.3577985  .0000020 -0.1208 0.0015  I   -59.610     .490    -4.637     .340  0.189360  0.510580 -0.3577950   -59.900    -4.700  
+ 3 721 52841.00 I   .191970  .000070   .508531  .000044  I -.3576469  .0000023 -0.1883 0.0022  I   -59.920     .518    -4.622     .340  0.192020  0.508560 -0.3576290   -60.300    -4.600  
+ 3 722 52842.00 I   .194308  .000050   .506211  .000049  I -.3574124  .0000040 -0.2841 0.0026  I   -60.157     .311    -4.602     .340  0.194430  0.506240 -0.3573990   -60.400    -4.500  
+ 3 723 52843.00 I   .197124  .000062   .503868  .000054  I -.3570731  .0000047 -0.3965 0.0032  I   -60.399     .340    -4.523     .179  0.197180  0.503790 -0.3570760   -60.400    -4.500  
+ 3 724 52844.00 I   .199921  .000062   .501622  .000059  I -.3566195  .0000049 -0.5085 0.0034  I   -60.655     .340    -4.497     .179  0.200020  0.501600 -0.3566200   -60.300    -4.500  
+ 3 725 52845.00 I   .202476  .000064   .499419  .000064  I -.3560571  .0000049 -0.6175 0.0046  I   -60.884     .340    -4.654     .179  0.202520  0.499500 -0.3560510   -60.300    -4.500  
+ 3 726 52846.00 I   .204685  .000051   .497220  .000070  I -.3554030  .0000079 -0.6711 0.0044  I   -60.932     .355    -4.999     .229  0.204770  0.497210 -0.3554020   -60.400    -4.600  
+ 3 727 52847.00 I   .206809  .000062   .494817  .000074  I -.3547428  .0000073 -0.6410 0.0051  I   -60.930     .390    -5.298     .194  0.206880  0.494770 -0.3547510   -60.600    -4.800  
+ 3 728 52848.00 I   .208980  .000069   .492100  .000073  I -.3541361  .0000066 -0.5655 0.0046  I   -60.779     .390    -5.410     .207  0.209050  0.492120 -0.3541420   -60.700    -5.000  
+ 3 729 52849.00 I   .211303  .000059   .489142  .000070  I -.3536200  .0000056 -0.4653 0.0042  I   -60.583     .409    -5.305     .340  0.211370  0.489240 -0.3536150   -60.800    -5.200  
+ 3 730 52850.00 I   .213815  .000065   .486413  .000070  I -.3532271  .0000053 -0.3001 0.0040  I   -60.430     .370    -5.113     .340  0.213900  0.486430 -0.3532260   -60.700    -5.200  
+ 3 731 52851.00 I   .216500  .000063   .483887  .000055  I -.3530459  .0000057 -0.0565 0.0040  I   -60.469     .370    -4.992     .340  0.216580  0.483900 -0.3530440   -60.500    -5.200  
+ 3 8 1 52852.00 I   .219112  .000056   .481538  .000058  I -.3531033  .0000060  0.1548 0.0047  I   -60.570     .370    -5.017     .340  0.219220  0.481570 -0.3530920   -60.300    -5.100  
+ 3 8 2 52853.00 I   .221386  .000042   .479513  .000049  I -.3533306  .0000075  0.2915 0.0046  I   -60.663     .116    -5.101     .340  0.221430  0.479470 -0.3533400   -60.300    -5.000  
+ 3 8 3 52854.00 I   .223071  .000054   .477318  .000053  I -.3536689  .0000071  0.3761 0.0051  I   -60.785     .740    -5.106     .340  0.223150  0.477200 -0.3537200   -60.500    -4.900  
+ 3 8 4 52855.00 I   .224748  .000056   .474861  .000051  I -.3540496  .0000069  0.3611 0.0053  I   -61.041     .740    -4.981     .340  0.224830  0.474730 -0.3540840   -60.900    -5.100  
+ 3 8 5 52856.00 I   .226443  .000051   .472367  .000048  I -.3543334  .0000080  0.1730 0.0051  I   -61.473     .156    -4.800     .101  0.226500  0.472310 -0.3543240   -61.300    -4.700  
+ 3 8 6 52857.00 I   .228150  .000054   .469614  .000060  I -.3543726  .0000074 -0.0840 0.0053  I   -61.973     .162    -4.676     .340  0.228230  0.469600 -0.3543660   -61.700    -4.600  
+ 3 8 7 52858.00 I   .230141  .000059   .466612  .000053  I -.3541745  .0000069 -0.3135 0.0046  I   -62.345     .162    -4.658     .340  0.230230  0.466580 -0.3541780   -62.000    -4.600  
+ 3 8 8 52859.00 I   .232124  .000064   .463704  .000055  I -.3537567  .0000056 -0.5096 0.0039  I   -62.450     .162    -4.723     .340  0.232240  0.463660 -0.3537490   -62.200    -4.700  
+ 3 8 9 52860.00 I   .234060  .000039   .461100  .000076  I -.3531909  .0000036 -0.5988 0.0033  I   -62.304     .178    -4.828     .340  0.234120  0.461030 -0.3531860   -62.200    -4.700  
+ 3 810 52861.00 I   .235882  .000049   .458865  .000093  I -.3526009  .0000035 -0.5626 0.0025  I   -62.063     .185    -4.892     .340  0.235980  0.458830 -0.3525970   -62.000    -4.800  
+ 3 811 52862.00 I   .237707  .000047   .456702  .000093  I -.3521001  .0000035 -0.4230 0.0022  I   -61.866     .269    -4.930     .126  0.237790  0.456760 -0.3520940   -61.800    -4.900  
+ 3 812 52863.00 I   .239487  .000044   .454309  .000095  I -.3517812  .0000028 -0.2043 0.0022  I   -61.759     .290    -4.952     .139  0.239600  0.454460 -0.3517730   -61.500    -4.900  
+ 3 813 52864.00 I   .241314  .000053   .452088  .000096  I -.3516894  .0000027  0.0082 0.0021  I   -61.729     .331    -4.936     .123  0.241370  0.452080 -0.3516780   -61.300    -5.000  
+ 3 814 52865.00 I   .243093  .000056   .449784  .000095  I -.3517784  .0000031  0.1630 0.0020  I   -61.792     .331    -4.837     .123  0.243160  0.449760 -0.3517770   -61.300    -4.900  
+ 3 815 52866.00 I   .244570  .000059   .447324  .000091  I -.3520012  .0000029  0.2750 0.0026  I   -62.004     .331    -4.668     .123  0.244710  0.447420 -0.3519870   -61.500    -4.700  
+ 3 816 52867.00 I   .246011  .000053   .444752  .000071  I -.3523064  .0000042  0.3208 0.0026  I   -62.371     .369    -4.537     .124  0.246050  0.444730 -0.3523300   -61.800    -4.500  
+ 3 817 52868.00 I   .247532  .000064   .441804  .000078  I -.3526161  .0000043  0.2865 0.0030  I   -62.774     .284    -4.520     .340  0.247620  0.441710 -0.3526850   -62.200    -4.400  
+ 3 818 52869.00 I   .249124  .000069   .438677  .000078  I -.3528512  .0000044  0.1676 0.0037  I   -63.052     .284    -4.575     .340  0.249190  0.438650 -0.3529040   -62.600    -4.400  
+ 3 819 52870.00 I   .250680  .000060   .435417  .000076  I -.3529173  .0000060 -0.0534 0.0035  I   -63.148     .187    -4.583     .126  0.250720  0.435430 -0.3529140   -63.000    -4.400  
+ 3 820 52871.00 I   .252254  .000061   .432462  .000075  I -.3527417  .0000055 -0.2824 0.0045  I   -63.177     .223    -4.521     .147  0.252330  0.432380 -0.3527250   -63.200    -4.500  
+ 3 821 52872.00 I   .253774  .000059   .429753  .000077  I -.3523732  .0000068 -0.4492 0.0042  I   -63.280     .223    -4.508     .147  0.253840  0.429680 -0.3523710   -63.300    -4.700  
+ 3 822 52873.00 I   .254928  .000054   .427262  .000067  I -.3518548  .0000064 -0.5814 0.0054  I   -63.427     .223    -4.657     .147  0.255050  0.427260 -0.3518440   -63.400    -4.800  
+ 3 823 52874.00 I   .255947  .000040   .424872  .000060  I -.3512339  .0000083 -0.6430 0.0049  I   -63.524     .351    -4.911     .174  0.255980  0.424890 -0.3512340   -63.300    -4.900  
+ 3 824 52875.00 I   .256958  .000042   .422061  .000081  I -.3505935  .0000073 -0.6306 0.0065  I   -63.294     .339    -5.146     .144  0.257020  0.422020 -0.3506380   -63.300    -5.000  
+ 3 825 52876.00 I   .258249  .000045   .418880  .000085  I -.3499998  .0000099 -0.5371 0.0056  I   -62.925     .383    -5.216     .135  0.258310  0.418840 -0.3500360   -63.100    -5.000  
+ 3 826 52877.00 I   .259210  .000049   .415542  .000081  I -.3495442  .0000085 -0.3689 0.0060  I   -62.620     .377    -5.104     .340  0.259350  0.415660 -0.3495410   -62.900    -5.000  
+ 3 827 52878.00 I   .260146  .000064   .412296  .000080  I -.3493055  .0000067 -0.0703 0.0055  I   -62.496     .330    -4.889     .340  0.260160  0.412320 -0.3493060   -62.700    -4.900  
+ 3 828 52879.00 I   .261170  .000065   .409038  .000080  I -.3494278  .0000070  0.3008 0.0048  I   -62.525     .334    -4.691     .340  0.261270  0.409070 -0.3493980   -62.500    -4.800  
+ 3 829 52880.00 I   .262134  .000067   .406067  .000072  I -.3498745  .0000069  0.5742 0.0051  I   -62.673     .334    -4.655     .340  0.262410  0.406130 -0.3498590   -62.400    -4.700  
+ 3 830 52881.00 I   .263337  .000073   .403308  .000053  I -.3505380  .0000075  0.7329 0.0043  I   -62.908     .291    -4.780     .340  0.263420  0.403290 -0.3505460   -62.400    -4.700  
+ 3 831 52882.00 I   .264430  .000064   .400496  .000055  I -.3512890  .0000050  0.7375 0.0043  I   -63.199     .164    -4.951     .136  0.264490  0.400450 -0.3513040   -62.700    -4.600  
+ 3 9 1 52883.00 I   .265244  .000064   .397526  .000047  I -.3519601  .0000040  0.5833 0.0031  I   -63.490     .170    -5.018     .127  0.265340  0.397440 -0.3519670   -63.000    -4.600  
+ 3 9 2 52884.00 I   .265514  .000047   .394414  .000040  I -.3524292  .0000035  0.3497 0.0025  I   -63.733     .299    -4.937     .148  0.265610  0.394360 -0.3524220   -63.300    -4.500  
+ 3 9 3 52885.00 I   .265529  .000049   .391195  .000047  I -.3526473  .0000031  0.0793 0.0024  I   -63.905     .364    -4.779     .120  0.265540  0.391190 -0.3526340   -63.500    -4.500  
+ 3 9 4 52886.00 I   .265386  .000049   .387938  .000051  I -.3525985  .0000034 -0.1589 0.0023  I   -63.983     .364    -4.648     .120  0.265490  0.387910 -0.3525990   -63.600    -4.500  
+ 3 9 5 52887.00 I   .265113  .000048   .384441  .000050  I -.3523713  .0000034 -0.2716 0.0022  I   -63.941     .364    -4.591     .120  0.265320  0.384430 -0.3523630   -63.500    -4.600  
+ 3 9 6 52888.00 I   .265182  .000048   .380910  .000043  I -.3520822  .0000027 -0.3037 0.0022  I   -63.670     .252    -4.587     .101  0.265190  0.380840 -0.3520800   -63.300    -4.600  
+ 3 9 7 52889.00 I   .265053  .000057   .377505  .000058  I -.3517877  .0000029 -0.2645 0.0020  I   -63.406     .240    -4.632     .340  0.265120  0.377410 -0.3518070   -63.100    -4.700  
+ 3 9 8 52890.00 I   .264911  .000058   .374278  .000057  I -.3515887  .0000030 -0.1189 0.0021  I   -63.134     .240    -4.670     .340  0.264960  0.374190 -0.3515900   -62.900    -4.700  
+ 3 9 9 52891.00 I   .264699  .000054   .371156  .000068  I -.3515693  .0000030  0.0850 0.0020  I   -62.942     .201    -4.645     .340  0.264780  0.371110 -0.3515600   -62.800    -4.600  
+ 3 910 52892.00 I   .264511  .000053   .367894  .000064  I -.3517499  .0000027  0.2625 0.0020  I   -62.871     .220    -4.506     .340  0.264640  0.367820 -0.3517590   -62.800    -4.500  
+ 3 911 52893.00 I   .264605  .000054   .364586  .000065  I -.3520732  .0000027  0.3768 0.0018  I   -62.895     .220    -4.260     .340  0.264740  0.364530 -0.3520710   -62.900    -4.300  
+ 3 912 52894.00 I   .264519  .000052   .361280  .000066  I -.3524733  .0000024  0.4005 0.0024  I   -62.988     .220    -4.007     .340  0.264560  0.361260 -0.3524620   -63.100    -4.100  
+ 3 913 52895.00 I   .264233  .000041   .357987  .000064  I -.3528459  .0000040  0.3393 0.0022  I   -63.229     .265    -3.929     .340  0.264300  0.357910 -0.3528470   -63.300    -3.900  
+ 3 914 52896.00 I   .264223  .000047   .354809  .000067  I -.3531448  .0000036  0.2568 0.0032  I   -63.451     .308    -4.011     .340  0.264280  0.354740 -0.3531440   -63.400    -3.800  
+ 3 915 52897.00 I   .264230  .000044   .351772  .000050  I -.3533557  .0000050  0.1627 0.0028  I   -63.600     .295    -4.181     .156  0.264320  0.351740 -0.3533550   -63.600    -3.900  
+ 3 916 52898.00 I   .264141  .000044   .348778  .000052  I -.3534664  .0000042  0.0571 0.0032  I   -63.576     .298    -4.279     .184  0.264230  0.348800 -0.3534580   -63.600    -4.100  
+ 3 917 52899.00 I   .264034  .000047   .345932  .000049  I -.3534705  .0000041 -0.0465 0.0030  I   -63.473     .299    -4.285     .161  0.264090  0.345900 -0.3534420   -63.600    -4.300  
+ 3 918 52900.00 I   .264003  .000050   .342995  .000049  I -.3533594  .0000044 -0.1919 0.0029  I   -63.463     .299    -4.331     .161  0.264010  0.342920 -0.3533250   -63.400    -4.500  
+ 3 919 52901.00 I   .263699  .000050   .339808  .000051  I -.3531016  .0000040 -0.2925 0.0031  I   -63.510     .299    -4.500     .161  0.263760  0.339770 -0.3530900   -63.100    -4.600  
+ 3 920 52902.00 I   .263057  .000045   .336422  .000048  I -.3528096  .0000043 -0.2876 0.0027  I   -63.385     .259    -4.688     .185  0.263100  0.336400 -0.3528150   -62.700    -4.700  
+ 3 921 52903.00 I   .262536  .000056   .332948  .000072  I -.3525464  .0000035 -0.2227 0.0030  I   -62.909     .227    -4.743     .113  0.262570  0.332800 -0.3525610   -62.300    -4.700  
+ 3 922 52904.00 I   .262588  .000059   .329728  .000078  I -.3524015  .0000041 -0.0457 0.0035  I   -62.352     .226    -4.629     .105  0.262610  0.329590 -0.3523970   -61.900    -4.600  
+ 3 923 52905.00 I   .262483  .000062   .326855  .000106  I -.3524815  .0000060  0.2110 0.0030  I   -62.056     .116    -4.439     .101  0.262550  0.326880 -0.3524690   -61.700    -4.500  
+ 3 924 52906.00 I   .261881  .000073   .324088  .000096  I -.3528272  .0000044  0.4787 0.0038  I   -62.092     .126    -4.242     .118  0.261930  0.324050 -0.3528410   -61.700    -4.400  
+ 3 925 52907.00 I   .260919  .000073   .320822  .000085  I -.3534321  .0000048  0.7259 0.0033  I   -62.279     .126    -4.059     .118  0.260980  0.320860 -0.3534380   -61.800    -4.200  
+ 3 926 52908.00 I   .260058  .000080   .317272  .000086  I -.3542544  .0000048  0.8992 0.0042  I   -62.478     .126    -3.950     .118  0.260120  0.317310 -0.3542520   -62.100    -4.100  
+ 3 927 52909.00 I   .259522  .000071   .314021  .000064  I -.3551846  .0000069  0.9349 0.0035  I   -62.681     .167    -4.013     .124  0.259650  0.313970 -0.3551960   -62.400    -4.100  
+ 3 928 52910.00 I   .259354  .000067   .311099  .000066  I -.3560733  .0000050  0.8178 0.0043  I   -62.868     .236    -4.232     .134  0.259430  0.311050 -0.3560970   -62.700    -4.100  
+ 3 929 52911.00 I   .259186  .000062   .308482  .000064  I -.3567895  .0000051  0.6085 0.0034  I   -62.960     .236    -4.424     .134  0.259270  0.308390 -0.3568040   -62.900    -4.200  
+ 3 930 52912.00 I   .259199  .000043   .306212  .000053  I -.3572758  .0000045  0.3558 0.0032  I   -62.943     .274    -4.416     .128  0.259180  0.306160 -0.3572700   -63.000    -4.300  
+ 310 1 52913.00 I   .258446  .000051   .304036  .000064  I -.3575077  .0000039  0.1228 0.0029  I   -62.900     .232    -4.236     .131  0.258530  0.304000 -0.3575060   -63.100    -4.300  
+ 310 2 52914.00 I   .257373  .000050   .301433  .000064  I -.3575563  .0000037 -0.0054 0.0023  I   -62.886     .232    -4.063     .131  0.257380  0.301390 -0.3575580   -63.000    -4.200  
+ 310 3 52915.00 I   .256284  .000049   .298629  .000065  I -.3575382  .0000024 -0.0096 0.0030  I   -62.846     .232    -4.019     .131  0.256250  0.298690 -0.3575300   -62.800    -4.200  
+ 310 4 52916.00 I   .254925  .000047   .296135  .000058  I -.3575692  .0000046  0.0824 0.0027  I   -62.688     .142    -4.072     .160  0.254990  0.296150 -0.3575800   -62.400    -4.100  
+ 310 5 52917.00 I   .253564  .000054   .293452  .000058  I -.3577244  .0000049  0.2384 0.0033  I   -62.392     .127    -4.127     .157  0.253630  0.293450 -0.3577210   -62.000    -4.000  
+ 310 6 52918.00 I   .252217  .000055   .290461  .000056  I -.3580632  .0000048  0.4459 0.0049  I   -62.034     .127    -4.134     .157  0.252280  0.290420 -0.3580400   -61.600    -4.000  
+ 310 7 52919.00 I   .250916  .000048   .287405  .000046  I -.3586134  .0000084  0.6449 0.0040  I   -61.739     .151    -4.070     .182  0.250960  0.287370 -0.3586000   -61.400    -3.800  
+ 310 8 52920.00 I   .249548  .000045   .284675  .000051  I -.3593205  .0000065  0.7465 0.0053  I   -61.621     .157    -3.933     .181  0.249600  0.284630 -0.3593330   -61.500    -3.700  
+ 310 9 52921.00 I   .248049  .000043   .282079  .000053  I -.3600671  .0000065  0.7304 0.0043  I   -61.614     .157    -3.630     .181  0.248110  0.282020 -0.3600770   -61.500    -3.600  
+ 31010 52922.00 I   .246399  .000042   .279260  .000055  I -.3607698  .0000057  0.6799 0.0041  I   -61.646     .157    -3.308     .181  0.246430  0.279220 -0.3607580   -61.300    -3.400  
+ 31011 52923.00 I   .244458  .000037   .276479  .000057  I -.3614181  .0000049  0.6031 0.0035  I   -61.667     .138    -3.140     .147  0.244420  0.276420 -0.3614210   -61.100    -3.300  
+ 31012 52924.00 I   .242041  .000041   .273677  .000061  I -.3619580  .0000041  0.4724 0.0028  I   -61.667     .147    -3.262     .152  0.242100  0.273640 -0.3619840   -61.100    -3.300  
+ 31013 52925.00 I   .239770  .000038   .270720  .000060  I -.3623574  .0000028  0.3253 0.0025  I   -61.634     .135    -3.533     .138  0.239820  0.270690 -0.3623750   -61.200    -3.400  
+ 31014 52926.00 I   .237782  .000038   .267465  .000058  I -.3626070  .0000028  0.1733 0.0019  I   -61.524     .177    -3.742     .115  0.237790  0.267490 -0.3625980   -61.300    -3.600  
+ 31015 52927.00 I   .235760  .000048   .264252  .000064  I -.3627098  .0000027  0.0387 0.0019  I   -61.380     .219    -3.818     .107  0.235850  0.264180 -0.3626930   -61.300    -3.800  
+ 31016 52928.00 I   .233828  .000049   .261079  .000063  I -.3626966  .0000027 -0.0591 0.0019  I   -61.312     .219    -3.880     .107  0.233880  0.261050 -0.3626940   -61.200    -4.000  
+ 31017 52929.00 I   .231726  .000054   .257910  .000063  I -.3626117  .0000027 -0.0965 0.0018  I   -61.273     .219    -4.010     .107  0.231840  0.257960 -0.3626070   -60.900    -4.100  
+ 31018 52930.00 I   .229788  .000052   .255053  .000063  I -.3625381  .0000025 -0.0305 0.0017  I   -61.048     .259    -4.108     .340  0.229830  0.254970 -0.3625330   -60.500    -4.000  
+ 31019 52931.00 I   .227911  .000056   .252528  .000058  I -.3625861  .0000021  0.1416 0.0016  I   -60.491     .259    -3.997     .340  0.227960  0.252450 -0.3626020   -60.000    -3.900  
+ 31020 52932.00 I   .225989  .000058   .250050  .000058  I -.3628492  .0000021  0.3973 0.0017  I   -59.908     .244    -3.728     .340  0.226020  0.250040 -0.3628760   -59.600    -3.700  
+ 31021 52933.00 I   .224050  .000066   .247358  .000060  I -.3633940  .0000027  0.6930 0.0018  I   -59.673     .196    -3.487     .340  0.224070  0.247350 -0.3633930   -59.400    -3.400  
+ 31022 52934.00 I   .221858  .000080   .244719  .000071  I -.3642305  .0000029  0.9746 0.0019  I   -59.826     .175    -3.349     .340  0.221970  0.244690 -0.3641960   -59.500    -3.200  
+ 31023 52935.00 I   .220267  .000077   .242197  .000071  I -.3653286  .0000027  1.2122 0.0020  I   -60.112     .175    -3.211     .340  0.220270  0.242220 -0.3652860   -59.700    -3.000  
+ 31024 52936.00 I   .219367  .000076   .239953  .000066  I -.3666147  .0000027  1.3290 0.0028  I   -60.338     .175    -3.020     .340  0.219290  0.239920 -0.3665910   -60.100    -2.900  
+ 31025 52937.00 I   .218599  .000082   .237824  .000080  I -.3679262  .0000049  1.2645 0.0028  I   -60.532     .179    -2.919     .340  0.218640  0.237720 -0.3679320   -60.300    -2.900  
+ 31026 52938.00 I   .218068  .000074   .235794  .000085  I -.3690870  .0000048  1.0298 0.0036  I   -60.561     .159    -3.018     .340  0.218130  0.235680 -0.3690970   -60.300    -2.900  
+ 31027 52939.00 I   .217046  .000066   .233766  .000079  I -.3699470  .0000054  0.6786 0.0038  I   -60.390     .287    -3.198     .135  0.217130  0.233710 -0.3699450   -60.200    -3.000  
+ 31028 52940.00 I   .215529  .000041   .231563  .000061  I -.3704375  .0000059  0.3074 0.0041  I   -60.090     .327    -3.224     .196  0.215490  0.231520 -0.3704240   -60.000    -3.100  
+ 31029 52941.00 I   .213730  .000046   .229233  .000065  I -.3705941  .0000061  0.0333 0.0043  I   -59.883     .287    -3.071     .217  0.213800  0.229150 -0.3705920   -59.900    -3.200  
+ 31030 52942.00 I   .212408  .000047   .226978  .000068  I -.3705533  .0000062 -0.0932 0.0043  I   -59.873     .287    -2.958     .217  0.212460  0.226900 -0.3705570   -59.700    -3.300  
+ 31031 52943.00 I   .210965  .000048   .225003  .000059  I -.3704455  .0000060 -0.1065 0.0051  I   -59.915     .287    -3.040     .217  0.211060  0.225010 -0.3704340   -59.600    -3.400  
+ 311 1 52944.00 I   .209202  .000048   .223344  .000043  I -.3703748  .0000082 -0.0165 0.0044  I   -59.866     .352    -3.211     .255  0.209260  0.223310 -0.3703860   -59.400    -3.400  
+ 311 2 52945.00 I   .207198  .000050   .221632  .000052  I -.3704382  .0000064  0.1505 0.0052  I   -59.622     .182    -3.240     .188  0.207240  0.221550 -0.3704470   -59.400    -3.200  
+ 311 3 52946.00 I   .205026  .000051   .219556  .000051  I -.3706830  .0000063  0.3392 0.0043  I   -59.334     .298    -3.088     .177  0.205080  0.219480 -0.3706680   -59.200    -3.100  
+ 311 4 52947.00 I   .202693  .000065   .217022  .000067  I -.3711065  .0000058  0.4975 0.0041  I   -59.110     .319    -2.873     .142  0.202740  0.216980 -0.3710900   -59.000    -2.900  
+ 311 5 52948.00 I   .200301  .000062   .214160  .000066  I -.3716507  .0000054  0.5740 0.0039  I   -59.009     .294    -2.665     .128  0.200360  0.214070 -0.3716580   -58.900    -2.800  
+ 311 6 52949.00 I   .198096  .000057   .211227  .000067  I -.3722245  .0000052  0.5605 0.0037  I   -59.020     .294    -2.429     .128  0.198250  0.211100 -0.3722220   -58.900    -2.700  
+ 311 7 52950.00 I   .196256  .000056   .208248  .000067  I -.3727506  .0000050  0.4838 0.0040  I   -59.050     .294    -2.170     .128  0.196320  0.208230 -0.3727430   -58.900    -2.300  
+ 311 8 52951.00 I   .194561  .000057   .205709  .000075  I -.3731858  .0000061  0.3880 0.0038  I   -59.077     .338    -2.023     .143  0.194580  0.205620 -0.3731760   -58.900    -2.000  
+ 311 9 52952.00 I   .192665  .000057   .203467  .000080  I -.3735243  .0000056  0.2851 0.0043  I   -58.943     .275    -2.138     .147  0.192750  0.203370 -0.3735510   -58.900    -2.100  
+ 31110 52953.00 I   .190182  .000045   .201124  .000068  I -.3737455  .0000061  0.1508 0.0044  I   -58.800     .276    -2.438     .340  0.190250  0.201140 -0.3737820   -58.900    -2.300  
+ 31111 52954.00 I   .187191  .000039   .198488  .000071  I -.3738187  .0000067 -0.0051 0.0044  I   -58.676     .291    -2.706     .103  0.187240  0.198490 -0.3738110   -58.800    -2.600  
+ 31112 52955.00 I   .184483  .000056   .196120  .000076  I -.3737645  .0000063 -0.0733 0.0046  I   -58.557     .323    -2.815     .340  0.184520  0.195980 -0.3737240   -58.600    -2.800  
+ 31113 52956.00 I   .182109  .000058   .193783  .000074  I -.3737076  .0000062 -0.0348 0.0041  I   -58.441     .323    -2.843     .340  0.182190  0.193700 -0.3736830   -58.300    -2.900  
+ 31114 52957.00 I   .179892  .000056   .191375  .000061  I -.3737002  .0000052  0.0197 0.0044  I   -58.287     .323    -2.892     .340  0.179870  0.191370 -0.3736890   -57.900    -2.800  
+ 31115 52958.00 I   .177373  .000063   .189100  .000045  I -.3737560  .0000063  0.1012 0.0033  I   -57.999     .352    -2.888     .340  0.177470  0.189040 -0.3737660   -57.600    -2.800  
+ 31116 52959.00 I   .175135  .000075   .187125  .000050  I -.3739256  .0000042  0.2518 0.0040  I   -57.520     .299    -2.754     .340  0.175190  0.187030 -0.3739370   -57.300    -2.600  
+ 31117 52960.00 I   .172811  .000080   .185478  .000049  I -.3742828  .0000049  0.4721 0.0033  I   -57.072     .299    -2.507     .340  0.172860  0.185420 -0.3742740   -57.000    -2.500  
+ 31118 52961.00 I   .170301  .000075   .183783  .000060  I -.3748732  .0000052  0.7026 0.0033  I   -56.911     .214    -2.325     .340  0.170180  0.183740 -0.3748600   -56.800    -2.300  
+ 31119 52962.00 I   .167255  .000066   .181967  .000073  I -.3756701  .0000043  0.8793 0.0034  I   -57.003     .328    -2.270     .340  0.167340  0.181890 -0.3756920   -56.800    -2.200  
+ 31120 52963.00 I   .164407  .000069   .180384  .000077  I -.3766149  .0000043  1.0052 0.0030  I   -57.258     .328    -2.177     .340  0.164430  0.180360 -0.3766400   -56.900    -2.000  
+ 31121 52964.00 I   .161871  .000068   .179057  .000075  I -.3776704  .0000042  1.1006 0.0031  I   -57.539     .328    -1.930     .340  0.161850  0.179120 -0.3776600   -57.100    -1.900  
+ 31122 52965.00 I   .159315  .000040   .178382  .000072  I -.3787681  .0000045  1.0519 0.0030  I   -57.855     .335    -1.673     .340  0.159410  0.178310 -0.3787760   -57.300    -1.900  
+ 31123 52966.00 I   .156598  .000049   .177794  .000067  I -.3797096  .0000042  0.8094 0.0030  I   -58.088     .267    -1.631     .340  0.156660  0.177700 -0.3797530   -57.500    -1.900  
+ 31124 52967.00 I   .153411  .000047   .176861  .000059  I -.3803632  .0000041  0.4958 0.0032  I   -58.025     .338    -1.781     .340  0.153490  0.176770 -0.3803940   -57.600    -1.900  
+ 31125 52968.00 I   .149829  .000054   .175601  .000048  I -.3807043  .0000049  0.1915 0.0032  I   -57.674     .316    -1.877     .340  0.149710  0.175550 -0.3806940   -57.400    -1.900  
+ 31126 52969.00 I   .145159  .000054   .174197  .000045  I -.3807785  .0000048 -0.0158 0.0034  I   -57.318     .316    -1.819     .340  0.145320  0.174190 -0.3807670   -57.100    -1.800  
+ 31127 52970.00 I   .141261  .000050   .172966  .000044  I -.3807255  .0000048 -0.0647 0.0032  I   -57.158     .316    -1.792     .340  0.141260  0.172870 -0.3807190   -56.700    -1.800  
+ 31128 52971.00 I   .137856  .000046   .171859  .000043  I -.3806939  .0000043  0.0208 0.0031  I   -57.100     .316    -1.954     .340  0.137930  0.171760 -0.3807010   -56.300    -1.700  
+ 31129 52972.00 I   .134327  .000046   .170606  .000042  I -.3807906  .0000040  0.1768 0.0027  I   -57.013     .420    -2.151     .340  0.134400  0.170550 -0.3808080   -56.200    -1.700  
+ 31130 52973.00 I   .130325  .000045   .169150  .000053  I -.3810472  .0000031  0.3320 0.0026  I   -56.869     .279    -2.095     .340  0.130400  0.169040 -0.3810580   -56.300    -1.600  
+ 312 1 52974.00 I   .126424  .000040   .167828  .000050  I -.3814438  .0000034  0.4547 0.0022  I   -56.817     .279    -1.743     .340  0.126440  0.167710 -0.3814390   -56.500    -1.400  
+ 312 2 52975.00 I   .123273  .000045   .166663  .000061  I -.3819391  .0000032  0.5247 0.0023  I   -56.871     .258    -1.344     .340  0.123160  0.166610 -0.3819270   -56.800    -1.300  
+ 312 3 52976.00 I   .120088  .000061   .165470  .000081  I -.3824681  .0000030  0.5193 0.0022  I   -56.964     .220    -1.114     .340  0.120170  0.165390 -0.3824720   -57.000    -1.100  
+ 312 4 52977.00 I   .116990  .000067   .164469  .000079  I -.3829558  .0000030  0.4481 0.0021  I   -57.058     .220    -1.023     .340  0.117050  0.164370 -0.3829730   -57.000    -1.000  
+ 312 5 52978.00 I   .113970  .000064   .163757  .000076  I -.3833545  .0000028  0.3475 0.0026  I   -57.110     .220     -.946     .340  0.114130  0.163830 -0.3833480   -57.000    -1.000  
+ 312 6 52979.00 I   .111335  .000070   .163476  .000071  I -.3836462  .0000043  0.2333 0.0025  I   -57.055     .137     -.894     .340  0.111400  0.163450 -0.3836440   -57.000    -1.000  
+ 312 7 52980.00 I   .108466  .000073   .163098  .000072  I -.3838085  .0000041  0.0814 0.0027  I   -56.885     .121    -1.002     .340  0.108520  0.162960 -0.3838250   -56.900    -1.200  
+ 312 8 52981.00 I   .105420  .000066   .162535  .000073  I -.3837934  .0000033 -0.1174 0.0026  I   -56.717     .116    -1.276     .340  0.105460  0.162450 -0.3837940   -56.800    -1.300  
+ 312 9 52982.00 I   .102491  .000054   .162009  .000057  I -.3835759  .0000033 -0.3097 0.0024  I   -56.625     .252    -1.554     .340  0.102520  0.162010 -0.3835510   -56.700    -1.500  
+ 31210 52983.00 I   .099884  .000058   .161513  .000066  I -.3832108  .0000034 -0.3917 0.0024  I   -56.545     .312    -1.686     .102  0.099840  0.161520 -0.3832000   -56.400    -1.600  
+ 31211 52984.00 I   .096736  .000057   .160989  .000064  I -.3828200  .0000035 -0.3913 0.0024  I   -56.362     .312    -1.683     .102  0.096870  0.160960 -0.3828340   -56.000    -1.700  
+ 31212 52985.00 I   .093238  .000055   .160263  .000072  I -.3824450  .0000033 -0.3407 0.0023  I   -56.048     .312    -1.653     .102  0.093360  0.160300 -0.3824450   -55.500    -1.700  
+ 31213 52986.00 I   .089737  .000051   .159501  .000077  I -.3821565  .0000029 -0.2364 0.0024  I   -55.658     .297    -1.633     .340  0.089740  0.159450 -0.3821680   -55.000    -1.700  
+ 31214 52987.00 I   .086417  .000050   .158868  .000094  I -.3819878  .0000034 -0.0852 0.0023  I   -55.274     .329    -1.574     .122  0.086450  0.158710 -0.3819980   -54.700    -1.600  
+ 31215 52988.00 I   .083454  .000050   .158563  .000088  I -.3820082  .0000036  0.1323 0.0023  I   -54.999     .321    -1.473     .139  0.083500  0.158510 -0.3820050   -54.500    -1.500  
+ 31216 52989.00 I   .080534  .000038   .158331  .000075  I -.3822631  .0000030  0.3820 0.0024  I   -54.906     .171    -1.405     .112  0.080610  0.158360 -0.3822560   -54.600    -1.400  
+ 31217 52990.00 I   .077537  .000044   .158131  .000076  I -.3827676  .0000032  0.6181 0.0022  I   -54.912     .180    -1.396     .340  0.077620  0.158050 -0.3827510   -54.800    -1.500  
+ 31218 52991.00 I   .074455  .000045   .157692  .000070  I -.3834652  .0000032  0.7520 0.0023  I   -55.073     .180    -1.304     .340  0.074480  0.157650 -0.3834390   -55.000    -1.300  
+ 31219 52992.00 I   .071386  .000045   .157191  .000065  I -.3842398  .0000032  0.7906 0.0026  I   -55.383     .180    -1.057     .340  0.071480  0.157210 -0.3842270   -55.200    -1.100  
+ 31220 52993.00 I   .068869  .000045   .156874  .000038  I -.3850195  .0000042  0.7485 0.0028  I   -55.907     .177     -.787     .340  0.068890  0.156860 -0.3850240   -55.500    -0.900  
+ 31221 52994.00 I   .066299  .000047   .156453  .000038  I -.3856933  .0000047  0.5758 0.0037  I   -56.487     .167     -.727     .340  0.066350  0.156400 -0.3857350   -55.800    -0.800  
+ 31222 52995.00 I   .063459  .000050   .155926  .000042  I -.3861387  .0000060  0.3058 0.0043  I   -56.792     .163     -.907     .340  0.063520  0.155850 -0.3861900   -56.000    -0.900  
+ 31223 52996.00 I   .060462  .000048   .155491  .000032  I -.3863147  .0000071  0.0651 0.0046  I   -56.671     .194    -1.113     .340  0.060390  0.155410 -0.3863100   -56.200    -1.000  
+ 31224 52997.00 I   .056600  .000045   .155110  .000034  I -.3863313  .0000071  0.0114 0.0050  I   -56.269     .167    -1.155     .340  0.056690  0.155070 -0.3862360   -56.100    -1.200  
+ 31225 52998.00 I   .052596  .000044   .154775  .000040  I -.3863873  .0000071  0.1069 0.0049  I   -55.891     .167    -1.176     .340  0.052620  0.154710 -0.3862160   -55.900    -1.400  
+ 31226 52999.00 I   .048916  .000048   .154525  .000036  I -.3865594  .0000067  0.2452 0.0047  I   -55.553     .167    -1.292     .340  0.048950  0.154440 -0.3863760   -55.800    -1.500  
+ 31227 53000.00 I   .045817  .000043   .154540  .000042  I -.3868836  .0000062  0.4011 0.0038  I   -55.263     .402    -1.407     .340  0.045870  0.154430 -0.3867280   -55.600    -1.400  
+ 31228 53001.00 I   .042882  .000042   .154743  .000044  I -.3873512  .0000034  0.5258 0.0037  I   -55.121     .288    -1.284     .340  0.042940  0.154660 -0.3872450   -55.600    -1.100  
+ 31229 53002.00 I   .039839  .000042   .154760  .000045  I -.3879174  .0000041  0.5970 0.0028  I   -55.265     .288     -.881     .340  0.039910  0.154710 -0.3878690   -55.600    -0.800  
+ 31230 53003.00 I   .037049  .000044   .154451  .000046  I -.3885246  .0000044  0.6066 0.0032  I   -55.612     .288     -.464     .340  0.037030  0.154430 -0.3885140   -55.800    -0.500  
+ 31231 53004.00 I   .034038  .000052   .154141  .000045  I -.3891136  .0000050  0.5654 0.0033  I   -55.957     .288     -.296     .340  0.034120  0.154100 -0.3890920   -55.900    -0.200  
+ 4 1 1 53005.00 I   .031243  .000047   .153836  .000043  I -.3896476  .0000049  0.5008 0.0035  I   -56.182     .288     -.356     .340  0.031250  0.153770 -0.3895920   -56.100    -0.100  
+ 4 1 2 53006.00 I   .028845  .000054   .153671  .000039  I -.3901039  .0000050  0.4025 0.0046  I   -56.267     .288     -.447     .340  0.028870  0.153550 -0.3900740   -56.100    -0.200  
+ 4 1 3 53007.00 I   .026638  .000051   .153848  .000031  I -.3904342  .0000077  0.2492 0.0037  I   -56.230     .740     -.510     .340  0.026700  0.153740 -0.3904550   -56.000    -0.400  
+ 4 1 4 53008.00 I   .024195  .000056   .154295  .000031  I -.3905978  .0000054  0.0825 0.0046  I   -56.014     .244     -.595     .340  0.024260  0.154230 -0.3906300   -55.900    -0.600  
+ 4 1 5 53009.00 I   .021490  .000054   .154845  .000028  I -.3906122  .0000051 -0.0453 0.0037  I   -55.791     .244     -.810     .340  0.021590  0.154800 -0.3906200   -55.700    -0.900  
+ 4 1 6 53010.00 I   .018789  .000053   .155467  .000041  I -.3905340  .0000051 -0.0926 0.0037  I   -55.682     .285    -1.079     .340  0.018790  0.155440 -0.3905240   -55.500    -1.000  
+ 4 1 7 53011.00 I   .015861  .000055   .156209  .000065  I -.3904593  .0000053 -0.0429 0.0037  I   -55.620     .244    -1.244     .340  0.015960  0.156150 -0.3904520   -55.200    -1.100  
+ 4 1 8 53012.00 I   .012902  .000050   .156907  .000066  I -.3904692  .0000053  0.0698 0.0035  I   -55.433     .244    -1.241     .340  0.012990  0.156850 -0.3904660   -54.900    -1.100  
+ 4 1 9 53013.00 I   .010072  .000046   .157332  .000060  I -.3906221  .0000047  0.2522 0.0038  I   -55.072     .244    -1.140     .340  0.010150  0.157360 -0.3906230   -54.600    -1.100  
+ 4 110 53014.00 I   .007588  .000057   .157654  .000069  I -.3909924  .0000054  0.4911 0.0033  I   -54.671     .255    -1.050     .340  0.007710  0.157630 -0.3909980   -54.400    -1.200  
+ 4 111 53015.00 I   .005350  .000068   .157928  .000068  I -.3916090  .0000045  0.7442 0.0035  I   -54.387     .266    -1.023     .340  0.005380  0.157840 -0.3916020   -54.200    -1.300  
+ 4 112 53016.00 I   .003047  .000066   .158344  .000065  I -.3924797  .0000046  0.9938 0.0029  I   -54.273     .252    -1.041     .149  0.003030  0.158320 -0.3924550   -54.100    -1.300  
+ 4 113 53017.00 I   .001080  .000059   .159023  .000048  I -.3935837  .0000038  1.2045 0.0030  I   -54.289     .258    -1.066     .169  0.001150  0.159010 -0.3935690   -54.100    -1.100  
+ 4 114 53018.00 I  -.001154  .000067   .160078  .000053  I -.3948469  .0000039  1.2899 0.0028  I   -54.306     .293    -1.052     .143 -0.000940  0.159990 -0.3948590   -54.100    -0.700  
+ 4 115 53019.00 I  -.003301  .000073   .161382  .000055  I -.3961148  .0000040  1.2293 0.0028  I   -54.461     .293     -.939     .143 -0.003280  0.161360 -0.3961320   -54.400    -0.600  
+ 4 116 53020.00 I  -.005375  .000068   .162622  .000050  I -.3972758  .0000039  1.0795 0.0033  I   -54.740     .293     -.738     .143 -0.005390  0.162610 -0.3972730   -54.900    -0.600  
+ 4 117 53021.00 I  -.007599  .000050   .163770  .000046  I -.3982441  .0000053  0.8407 0.0029  I   -55.192     .296     -.564     .179 -0.007540  0.163700 -0.3982610   -55.300    -0.700  
+ 4 118 53022.00 I  -.010223  .000049   .165081  .000059  I -.3989457  .0000042  0.5676 0.0031  I   -55.710     .282     -.569     .340 -0.010170  0.165030 -0.3989770   -55.700    -0.700  
+ 4 119 53023.00 I  -.013233  .000052   .166330  .000086  I -.3993949  .0000031  0.3415 0.0026  I   -56.056     .248     -.777     .340 -0.013220  0.166280 -0.3994140   -55.900    -0.800  
+ 4 120 53024.00 I  -.016197  .000051   .167243  .000092  I -.3996602  .0000031  0.2100 0.0023  I   -56.077     .220    -1.037     .340 -0.016180  0.167220 -0.3996580   -55.900    -0.900  
+ 4 121 53025.00 I  -.018658  .000061   .168258  .000104  I -.3998530  .0000033  0.1930 0.0023  I   -55.842     .244    -1.234     .117 -0.018640  0.168310 -0.3998410   -55.600    -1.100  
+ 4 122 53026.00 I  -.021139  .000064   .169988  .000103  I -.4000827  .0000035  0.2857 0.0024  I   -55.472     .244    -1.277     .117 -0.021090  0.169990 -0.4000790   -55.300    -1.200  
+ 4 123 53027.00 I  -.024162  .000059   .171751  .000102  I -.4004467  .0000035  0.4455 0.0030  I   -55.057     .244    -1.314     .117 -0.023830  0.171790 -0.4004350   -54.900    -1.300  
+ 4 124 53028.00 I  -.026309  .000058   .173183  .000099  I -.4009745  .0000048  0.6078 0.0032  I   -54.694     .266    -1.358     .123 -0.026220  0.173120 -0.4009750   -54.800    -1.300  
+ 4 125 53029.00 I  -.027928  .000065   .174510  .000053  I -.4016567  .0000053  0.7534 0.0036  I   -54.517     .338    -1.256     .340 -0.027880  0.174430 -0.4016610   -54.800    -1.100  
+ 4 126 53030.00 I  -.029159  .000066   .175921  .000048  I -.4024644  .0000054  0.8483 0.0038  I   -54.690     .338    -1.005     .340 -0.029110  0.175870 -0.4024540   -55.000    -0.900  
+ 4 127 53031.00 I  -.030234  .000040   .177464  .000025  I -.4033160  .0000055  0.8313 0.0037  I   -55.141     .329     -.780     .340 -0.030220  0.177420 -0.4033020   -55.300    -0.700  
+ 4 128 53032.00 I  -.031666  .000044   .179219  .000030  I -.4040869  .0000052  0.6940 0.0038  I   -55.636     .309     -.764     .340 -0.031610  0.179160 -0.4040940   -55.500    -0.700  
+ 4 129 53033.00 I  -.033515  .000046   .181091  .000030  I -.4046819  .0000052  0.4903 0.0030  I   -55.982     .309     -.922     .340 -0.033490  0.181000 -0.4046910   -55.700    -0.800  
+ 4 130 53034.00 I  -.036001  .000045   .182952  .000029  I -.4050549  .0000031  0.2489 0.0035  I   -56.103     .309    -1.066     .340 -0.035950  0.182930 -0.4050420   -55.700    -1.000  
+ 4 131 53035.00 I  -.038965  .000042   .184614  .000041  I -.4051878  .0000047  0.0319 0.0027  I   -55.988     .229    -1.133     .340 -0.038950  0.184640 -0.4051900   -55.600    -1.200  
+ 4 2 1 53036.00 I  -.042056  .000061   .185945  .000071  I -.4051408  .0000045 -0.1188 0.0033  I   -55.661     .215    -1.171     .127 -0.042020  0.185890 -0.4051460   -55.300    -1.300  
+ 4 2 2 53037.00 I  -.045030  .000060   .187071  .000067  I -.4049664  .0000047 -0.2210 0.0031  I   -55.286     .208    -1.332     .127 -0.044930  0.186970 -0.4049600   -55.000    -1.500  
+ 4 2 3 53038.00 I  -.047780  .000056   .188139  .000071  I -.4047150  .0000042 -0.2745 0.0039  I   -55.040     .161    -1.596     .149 -0.047970  0.188030 -0.4047120   -54.800    -1.600  
+ 4 2 4 53039.00 I  -.051232  .000058   .189043  .000077  I -.4044364  .0000063 -0.2709 0.0038  I   -54.939     .145    -1.815     .152 -0.051200  0.188970 -0.4044510   -54.600    -1.700  
+ 4 2 5 53040.00 I  -.054195  .000062   .189858  .000077  I -.4042062  .0000063 -0.1684 0.0045  I   -54.847     .145    -1.866     .152 -0.054160  0.189780 -0.4042020   -54.500    -1.700  
+ 4 2 6 53041.00 I  -.056738  .000060   .190689  .000076  I -.4041262  .0000064  0.0137 0.0046  I   -54.656     .145    -1.756     .152 -0.056640  0.190710 -0.4041170   -54.400    -1.800  
+ 4 2 7 53042.00 I  -.058821  .000042   .192020  .000065  I -.4042553  .0000066  0.2607 0.0044  I   -54.412     .137    -1.598     .151 -0.058780  0.191970 -0.4042390   -54.300    -1.800  
+ 4 2 8 53043.00 I  -.060682  .000050   .193628  .000083  I -.4046569  .0000059  0.5355 0.0045  I   -54.251     .190    -1.522     .140 -0.060610  0.193540 -0.4046350   -54.200    -1.700  
+ 4 2 9 53044.00 I  -.062590  .000053   .195344  .000085  I -.4053066  .0000060  0.7512 0.0035  I   -54.236     .190    -1.559     .140 -0.062540  0.195330 -0.4052940   -54.300    -1.600  
+ 4 210 53045.00 I  -.064870  .000048   .197068  .000078  I -.4061326  .0000036  0.8866 0.0034  I   -54.363     .194    -1.640     .155 -0.064880  0.197140 -0.4061210   -54.300    -1.500  
+ 4 211 53046.00 I  -.067746  .000048   .199078  .000087  I -.4070517  .0000033  0.9375 0.0025  I   -54.587     .170    -1.669     .161 -0.067650  0.199070 -0.4070350   -54.500    -1.400  
+ 4 212 53047.00 I  -.070770  .000045   .200926  .000090  I -.4079707  .0000034  0.8775 0.0024  I   -54.851     .170    -1.607     .161 -0.070720  0.200890 -0.4079620   -54.700    -1.300  
+ 4 213 53048.00 I  -.073863  .000045   .202589  .000084  I -.4087685  .0000034  0.7028 0.0021  I   -55.095     .170    -1.498     .161 -0.073760  0.202610 -0.4087570   -54.900    -1.300  
+ 4 214 53049.00 I  -.076809  .000031   .204460  .000067  I -.4093573  .0000026  0.4712 0.0021  I   -55.325     .132    -1.423     .150 -0.076740  0.204440 -0.4093720   -55.100    -1.300  
+ 4 215 53050.00 I  -.079498  .000033   .206272  .000075  I -.4097155  .0000025  0.2534 0.0018  I   -55.385     .116    -1.463     .129 -0.079460  0.206200 -0.4097500   -55.100    -1.400  
+ 4 216 53051.00 I  -.081563  .000035   .208061  .000076  I -.4098881  .0000025  0.1077 0.0020  I   -55.305     .169    -1.621     .116 -0.081510  0.208070 -0.4099010   -55.100    -1.500  
+ 4 217 53052.00 I  -.082858  .000041   .210006  .000090  I -.4099668  .0000031  0.0698 0.0020  I   -55.105     .348    -1.830     .130 -0.082870  0.210050 -0.4099550   -54.900    -1.800  
+ 4 218 53053.00 I  -.084378  .000051   .212353  .000110  I -.4100719  .0000031  0.1646 0.0023  I   -54.856     .313    -1.995     .117 -0.084210  0.212270 -0.4100770   -54.600    -2.000  
+ 4 219 53054.00 I  -.086640  .000048   .214811  .000105  I -.4103323  .0000033  0.3683 0.0023  I   -54.616     .313    -2.072     .117 -0.086570  0.214860 -0.4103380   -54.400    -2.100  
+ 4 220 53055.00 I  -.090048  .000045   .217043  .000093  I -.4108259  .0000034  0.6241 0.0030  I   -54.378     .311    -2.117     .113 -0.089840  0.217220 -0.4108150   -54.300    -2.100  
+ 4 221 53056.00 I  -.093073  .000054   .219531  .000095  I -.4115704  .0000049  0.8495 0.0024  I   -54.168     .359    -2.091     .118 -0.093010  0.219500 -0.4115850   -54.300    -2.100  
+ 4 222 53057.00 I  -.095702  .000062   .222041  .000102  I -.4125003  .0000034  1.0012 0.0029  I   -54.053     .333    -2.024     .122 -0.095640  0.221970 -0.4125160   -54.300    -2.000  
+ 4 223 53058.00 I  -.097955  .000056   .224286  .000092  I -.4135463  .0000032  1.0734 0.0022  I   -54.143     .209    -1.939     .340 -0.097910  0.224270 -0.4135320   -54.400    -1.900  
+ 4 224 53059.00 I  -.099789  .000055   .226364  .000068  I -.4146110  .0000027  1.0372 0.0022  I   -54.460     .378    -1.922     .340 -0.099810  0.226380 -0.4145990   -54.500    -1.900  
+ 4 225 53060.00 I  -.101338  .000077   .228642  .000070  I -.4155850  .0000029  0.8939 0.0021  I   -54.885     .338    -2.035     .340 -0.101260  0.228610 -0.4155980   -54.600    -2.000  
+ 4 226 53061.00 I  -.102537  .000074   .231016  .000075  I -.4163810  .0000032  0.6971 0.0022  I   -55.230     .338    -2.216     .340 -0.102480  0.230940 -0.4163790   -54.700    -2.200  
+ 4 227 53062.00 I  -.104291  .000071   .233424  .000069  I -.4169806  .0000033  0.5046 0.0028  I   -55.343     .338    -2.330     .340 -0.103900  0.233390 -0.4169680   -54.600    -2.400  
+ 4 228 53063.00 I  -.105954  .000069   .235830  .000049  I -.4173787  .0000047  0.2777 0.0027  I   -55.261     .455    -2.309     .340 -0.105970  0.235800 -0.4173890   -54.500    -2.500  
+ 4 229 53064.00 I  -.108397  .000066   .237913  .000045  I -.4175324  .0000042  0.0400 0.0032  I   -54.831     .373    -2.338     .340 -0.108330  0.237830 -0.4175520   -54.200    -2.600  
+ 4 3 1 53065.00 I  -.110491  .000060   .239912  .000044  I -.4174875  .0000042 -0.1112 0.0027  I   -54.292     .373    -2.495     .340 -0.110480  0.239820 -0.4174860   -53.900    -2.600  
+ 4 3 2 53066.00 I  -.112159  .000052   .242099  .000055  I -.4173486  .0000033 -0.1467 0.0026  I   -53.828     .305    -2.763     .340 -0.112280  0.242030 -0.4173340   -53.600    -2.700  
+ 4 3 3 53067.00 I  -.114042  .000064   .244217  .000063  I -.4172252  .0000030 -0.0890 0.0023  I   -53.572     .266    -3.017     .112 -0.113870  0.244170 -0.4172170   -53.400    -2.900  
+ 4 3 4 53068.00 I  -.115336  .000065   .246373  .000067  I -.4171986  .0000031  0.0525 0.0021  I   -53.530     .266    -3.146     .112 -0.115450  0.246340 -0.4171940   -53.300    -3.100  
+ 4 3 5 53069.00 I  -.117603  .000061   .248810  .000070  I -.4173536  .0000029  0.2640 0.0020  I   -53.585     .266    -3.125     .112 -0.117240  0.248810 -0.4173440   -53.100    -3.200  
+ 4 3 6 53070.00 I  -.118999  .000074   .251503  .000074  I -.4177371  .0000025  0.5068 0.0019  I   -53.658     .346    -2.977     .154 -0.118920  0.251420 -0.4177250   -53.000    -3.200  
+ 4 3 7 53071.00 I  -.120336  .000080   .254069  .000075  I -.4183644  .0000024  0.7412 0.0018  I   -53.590     .308    -2.832     .143 -0.120260  0.254030 -0.4183150   -52.900    -3.000  
+ 4 3 8 53072.00 I  -.121594  .000088   .256227  .000077  I -.4191954  .0000027  0.9035 0.0018  I   -53.561     .268    -2.758     .158 -0.121520  0.256150 -0.4191520   -53.000    -2.800  
+ 4 3 9 53073.00 I  -.122122  .000069   .258077  .000068  I -.4201289  .0000028  0.9385 0.0020  I   -53.713     .255    -2.786     .126 -0.122270  0.258000 -0.4201170   -53.300    -2.700  
+ 4 310 53074.00 I  -.123028  .000069   .260069  .000080  I -.4210231  .0000029  0.8256 0.0024  I   -54.060     .224    -2.873     .109 -0.122910  0.260000 -0.4210240   -54.000    -2.800  
+ 4 311 53075.00 I  -.123778  .000074   .262111  .000077  I -.4217423  .0000038  0.5994 0.0024  I   -54.444     .224    -2.951     .109 -0.123690  0.262090 -0.4217560   -54.400    -2.900  
+ 4 312 53076.00 I  -.124453  .000069   .264041  .000080  I -.4222139  .0000039  0.3490 0.0037  I   -54.663     .224    -2.992     .109 -0.124390  0.264080 -0.4222000   -54.600    -3.000  
+ 4 313 53077.00 I  -.125195  .000051   .266227  .000081  I -.4224543  .0000064  0.1404 0.0035  I   -54.689     .391    -2.989     .120 -0.125150  0.266190 -0.4224630   -54.500    -3.100  
+ 4 314 53078.00 I  -.126023  .000046   .268480  .000073  I -.4225155  .0000059 -0.0057 0.0043  I   -54.399     .414    -3.020     .340 -0.125960  0.268470 -0.4225910   -54.100    -3.100  
+ 4 315 53079.00 I  -.126916  .000045   .270668  .000069  I -.4224794  .0000058 -0.0423 0.0044  I   -53.973     .414    -3.087     .340 -0.126830  0.270610 -0.4225390   -53.500    -3.100  
+ 4 316 53080.00 I  -.128139  .000055   .272811  .000039  I -.4224683  .0000064  0.0333 0.0047  I   -53.526     .553    -3.199     .340 -0.128020  0.272740 -0.4224570   -52.900    -3.100  
+ 4 317 53081.00 I  -.129178  .000057   .274874  .000038  I -.4225790  .0000073  0.2078 0.0053  I   -53.153     .466    -3.328     .340 -0.129130  0.274780 -0.4225720   -52.600    -3.200  
+ 4 318 53082.00 I  -.130049  .000059   .277122  .000035  I -.4229104  .0000084  0.4619 0.0055  I   -52.919     .466    -3.416     .340 -0.130030  0.277030 -0.4229130   -52.600    -3.300  
+ 4 319 53083.00 I  -.131304  .000062   .279565  .000034  I -.4235105  .0000081  0.7390 0.0065  I   -52.835     .466    -3.414     .340 -0.131010  0.279470 -0.4234930   -52.700    -3.400  
+ 4 320 53084.00 I  -.131697  .000068   .281978  .000039  I -.4243780  .0000098  0.9848 0.0057  I   -52.840     .335    -3.307     .106 -0.131650  0.281880 -0.4243930   -52.800    -3.400  
+ 4 321 53085.00 I  -.131870  .000068   .284727  .000048  I -.4254451  .0000080  1.1257 0.0064  I   -52.883     .288    -3.163     .340 -0.131780  0.284630 -0.4255200   -53.000    -3.300  
+ 4 322 53086.00 I  -.131969  .000075   .287888  .000052  I -.4265916  .0000081  1.1531 0.0053  I   -52.936     .288    -3.065     .340 -0.131850  0.287800 -0.4266280   -53.100    -3.200  
+ 4 323 53087.00 I  -.131752  .000060   .291271  .000057  I -.4277318  .0000071  1.1220 0.0049  I   -53.051     .254    -3.096     .340 -0.131810  0.291260 -0.4277180   -53.100    -3.200  
+ 4 324 53088.00 I  -.132110  .000060   .294885  .000067  I -.4288185  .0000056  1.0395 0.0045  I   -53.258     .294    -3.257     .340 -0.132020  0.294870 -0.4288330   -53.100    -3.200  
+ 4 325 53089.00 I  -.132884  .000061   .298383  .000067  I -.4297922  .0000055  0.9013 0.0038  I   -53.471     .294    -3.447     .340 -0.132830  0.298330 -0.4298080   -53.000    -3.400  
+ 4 326 53090.00 I  -.134150  .000062   .301745  .000060  I -.4306106  .0000052  0.7315 0.0039  I   -53.538     .294    -3.556     .340 -0.134010  0.301770 -0.4306060   -53.000    -3.600  
+ 4 327 53091.00 I  -.135581  .000052   .305184  .000061  I -.4312531  .0000055  0.5554 0.0032  I   -53.405     .345    -3.587     .102 -0.135510  0.305200 -0.4312590   -52.800    -3.700  
+ 4 328 53092.00 I  -.136937  .000060   .308389  .000081  I -.4317347  .0000038  0.4193 0.0033  I   -53.051     .301    -3.674     .340 -0.136880  0.308330 -0.4317360   -52.600    -3.900  
+ 4 329 53093.00 I  -.137966  .000061   .311513  .000079  I -.4321212  .0000037  0.3719 0.0025  I   -52.560     .301    -3.881     .340 -0.137840  0.311480 -0.4321140   -52.400    -4.000  
+ 4 330 53094.00 I  -.138634  .000061   .314635  .000073  I -.4325099  .0000032  0.4188 0.0024  I   -52.010     .204    -4.140     .340 -0.138680  0.314710 -0.4325020   -52.000    -4.100  
+ 4 331 53095.00 I  -.139515  .000065   .317748  .000072  I -.4329864  .0000031  0.5484 0.0023  I   -51.575     .254    -4.347     .108 -0.139440  0.317740 -0.4329880   -51.700    -4.300  
+ 4 4 1 53096.00 I  -.140248  .000065   .320643  .000076  I -.4336295  .0000034  0.7464 0.0022  I   -51.461     .254    -4.481     .108 -0.140150  0.320590 -0.4336250   -51.400    -4.500  
+ 4 4 2 53097.00 I  -.140804  .000071   .323365  .000071  I -.4344855  .0000031  0.9634 0.0023  I   -51.665     .254    -4.562     .108 -0.140650  0.323290 -0.4344790   -51.300    -4.600  
+ 4 4 3 53098.00 I  -.140861  .000064   .326042  .000041  I -.4355652  .0000031  1.2070 0.0021  I   -51.948     .435    -4.493     .259 -0.140730  0.325970 -0.4355490   -51.400    -4.600  
+ 4 4 4 53099.00 I  -.141000  .000065   .328657  .000045  I -.4368965  .0000028  1.4414 0.0021  I   -52.016     .455    -4.318     .166 -0.140850  0.328620 -0.4368240   -51.600    -4.400  
+ 4 4 5 53100.00 I  -.141192  .000062   .331008  .000050  I -.4384084  .0000028  1.5570 0.0024  I   -52.007     .409    -4.039     .198 -0.141110  0.330940 -0.4383520   -52.100    -4.100  
+ 4 4 6 53101.00 I  -.140714  .000057   .333326  .000066  I -.4399626  .0000040  1.5290 0.0023  I   -52.215     .380    -3.846     .166 -0.140720  0.333270 -0.4399620   -52.500    -4.000  
+ 4 4 7 53102.00 I  -.140149  .000053   .336180  .000070  I -.4414172  .0000036  1.3535 0.0034  I   -52.703     .380    -3.878     .166 -0.140070  0.336140 -0.4414210   -52.700    -4.100  
+ 4 4 8 53103.00 I  -.139967  .000052   .339273  .000066  I -.4426289  .0000056  1.0558 0.0033  I   -53.187     .380    -4.088     .166 -0.139870  0.339300 -0.4426250   -53.000    -4.100  
+ 4 4 9 53104.00 I  -.139743  .000041   .342518  .000074  I -.4435322  .0000056  0.7682 0.0047  I   -53.342     .380    -4.317     .166 -0.139720  0.342460 -0.4435230   -53.300    -4.200  
+ 4 410 53105.00 I  -.139228  .000040   .345818  .000079  I -.4441832  .0000075  0.5344 0.0041  I   -53.112     .127    -4.460     .178 -0.139160  0.345730 -0.4441910   -53.200    -4.200  
+ 4 411 53106.00 I  -.138787  .000041   .349170  .000075  I -.4446215  .0000061  0.3621 0.0052  I   -52.691     .119    -4.524     .340 -0.138700  0.349120 -0.4446890   -52.800    -4.300  
+ 4 412 53107.00 I  -.138905  .000039   .352330  .000076  I -.4449578  .0000073  0.3411 0.0038  I   -52.262     .205    -4.562     .340 -0.138820  0.352320 -0.4450120   -52.300    -4.400  
+ 4 413 53108.00 I  -.139082  .000054   .355170  .000071  I -.4453570  .0000045  0.4801 0.0038  I   -51.886     .187    -4.603     .340 -0.139050  0.355180 -0.4453520   -51.800    -4.500  
+ 4 414 53109.00 I  -.139115  .000058   .357994  .000076  I -.4459439  .0000024  0.6968 0.0026  I   -51.560     .235    -4.641     .340 -0.139020  0.357970 -0.4459340   -51.500    -4.600  
+ 4 415 53110.00 I  -.138580  .000058   .360790  .000064  I -.4467417  .0000024  0.8869 0.0018  I   -51.317     .235    -4.643     .340 -0.138500  0.360750 -0.4467570   -51.300    -4.500  
+ 4 416 53111.00 I  -.137662  .000058   .363695  .000064  I -.4476964  .0000026  1.0131 0.0018  I   -51.236     .235    -4.559     .340 -0.137280  0.363660 -0.4476920   -51.200    -4.400  
+ 4 417 53112.00 I  -.135955  .000074   .367039  .000059  I -.4487422  .0000027  1.0621 0.0018  I   -51.344     .275    -4.359     .124 -0.135880  0.367000 -0.4487340   -51.200    -4.200  
+ 4 418 53113.00 I  -.134731  .000076   .370453  .000059  I -.4497902  .0000026  1.0198 0.0019  I   -51.543     .388    -4.089     .144 -0.134630  0.370400 -0.4498170   -51.300    -3.900  
+ 4 419 53114.00 I  -.133524  .000073   .373815  .000056  I -.4507553  .0000026  0.8978 0.0023  I   -51.689     .388    -3.888     .144 -0.133420  0.373690 -0.4507790   -51.300    -3.800  
+ 4 420 53115.00 I  -.132568  .000068   .377234  .000057  I -.4515622  .0000038  0.7048 0.0024  I   -51.740     .550    -3.894     .148 -0.132720  0.377200 -0.4515530   -51.300    -3.700  
+ 4 421 53116.00 I  -.132997  .000077   .380324  .000064  I -.4521541  .0000040  0.4794 0.0028  I   -51.660     .418    -4.075     .340 -0.132860  0.380270 -0.4521440   -51.300    -3.900  
+ 4 422 53117.00 I  -.133111  .000072   .382857  .000064  I -.4525177  .0000042  0.2442 0.0029  I   -51.707     .418    -4.367     .340 -0.133030  0.382740 -0.4525130   -51.200    -4.100  
+ 4 423 53118.00 I  -.132957  .000069   .385157  .000066  I -.4526419  .0000041  0.0072 0.0032  I   -51.718     .418    -4.562     .340 -0.132900  0.385140 -0.4526290   -51.200    -4.400  
+ 4 424 53119.00 I  -.132759  .000060   .387283  .000057  I -.4525565  .0000049 -0.1566 0.0031  I   -51.588     .353    -4.654     .340 -0.132680  0.387240 -0.4525500   -51.200    -4.700  
+ 4 425 53120.00 I  -.132086  .000067   .389107  .000055  I -.4523604  .0000046 -0.2251 0.0034  I   -51.481     .419    -4.778     .340 -0.132000  0.389000 -0.4523700   -51.200    -4.900  
+ 4 426 53121.00 I  -.130578  .000068   .390893  .000058  I -.4521354  .0000046 -0.2061 0.0033  I   -51.281     .419    -4.997     .340 -0.130460  0.390820 -0.4521400   -51.100    -5.100  
+ 4 427 53122.00 I  -.128362  .000068   .392863  .000040  I -.4519818  .0000047 -0.0861 0.0031  I   -50.896     .395    -5.203     .340 -0.128460  0.392810 -0.4519700   -51.000    -5.200  
+ 4 428 53123.00 I  -.126734  .000083   .395216  .000053  I -.4519886  .0000042  0.1101 0.0032  I   -50.413     .330    -5.284     .126 -0.126640  0.395120 -0.4519830   -50.700    -5.200  
+ 4 429 53124.00 I  -.125093  .000085   .398005  .000050  I -.4522144  .0000044  0.3436 0.0030  I   -50.160     .330    -5.302     .126 -0.125000  0.397910 -0.4522110   -50.400    -5.300  
+ 4 430 53125.00 I  -.123717  .000077   .401060  .000050  I -.4526883  .0000043  0.6147 0.0034  I   -50.344     .330    -5.381     .126 -0.123340  0.401040 -0.4526730   -50.300    -5.200  
+ 4 5 1 53126.00 I  -.121531  .000059   .404238  .000050  I -.4534459  .0000052  0.8921 0.0031  I   -50.751     .157    -5.461     .144 -0.121380  0.404210 -0.4534520   -50.300    -5.100  
+ 4 5 2 53127.00 I  -.119766  .000051   .407433  .000052  I -.4544442  .0000045  1.0845 0.0036  I   -51.038     .169    -5.319     .121 -0.119660  0.407360 -0.4544520   -50.500    -4.800  
+ 4 5 3 53128.00 I  -.118495  .000049   .410523  .000049  I -.4555747  .0000049  1.1563 0.0036  I   -51.220     .169    -4.895     .121 -0.118390  0.410450 -0.4555700   -51.000    -4.600  
+ 4 5 4 53129.00 I  -.117063  .000032   .413497  .000054  I -.4567189  .0000056  1.1145 0.0035  I   -51.608     .242    -4.458     .340 -0.117060  0.413470 -0.4567090   -51.600    -4.500  
+ 4 5 5 53130.00 I  -.115916  .000031   .416444  .000057  I -.4577677  .0000051  0.9647 0.0039  I   -52.325     .217    -4.360     .340 -0.115790  0.416380 -0.4577810   -52.200    -4.500  
+ 4 5 6 53131.00 I  -.115002  .000031   .419218  .000057  I -.4586251  .0000053  0.7453 0.0036  I   -52.882     .217    -4.611     .340 -0.114930  0.419100 -0.4586060   -52.600    -4.700  
+ 4 5 7 53132.00 I  -.113991  .000033   .421556  .000061  I -.4592559  .0000051  0.5187 0.0041  I   -52.928     .217    -4.982     .340 -0.113920  0.421550 -0.4592340   -52.800    -5.000  
+ 4 5 8 53133.00 I  -.112547  .000036   .423888  .000062  I -.4596849  .0000062  0.3594 0.0034  I   -52.484     .249    -5.252     .340 -0.112480  0.423880 -0.4597010   -52.700    -5.200  
+ 4 5 9 53134.00 I  -.111160  .000032   .426264  .000058  I -.4600193  .0000044  0.3339 0.0038  I   -51.911     .241    -5.400     .340 -0.111080  0.426160 -0.4600310   -52.300    -5.400  
+ 4 510 53135.00 I  -.109687  .000023   .428824  .000057  I -.4603924  .0000043  0.4274 0.0030  I   -51.517     .236    -5.483     .340 -0.109620  0.428730 -0.4603710   -51.900    -5.500  
+ 4 511 53136.00 I  -.107952  .000028   .431626  .000040  I -.4608927  .0000042  0.5765 0.0032  I   -51.348     .278    -5.489     .340 -0.107920  0.431630 -0.4608740   -51.400    -5.500  
+ 4 512 53137.00 I  -.106062  .000037   .434487  .000038  I -.4615510  .0000047  0.7423 0.0032  I   -51.294     .243    -5.393     .340 -0.105990  0.434460 -0.4615630   -51.100    -5.400  
+ 4 513 53138.00 I  -.103997  .000039   .437286  .000036  I -.4623751  .0000047  0.9016 0.0033  I   -51.244     .243    -5.179     .340 -0.103920  0.437210 -0.4623610   -51.000    -5.200  
+ 4 514 53139.00 I  -.102431  .000037   .440008  .000035  I -.4633384  .0000045  1.0135 0.0038  I   -51.228     .243    -4.938     .340 -0.102230  0.439990 -0.4633250   -51.100    -4.900  
+ 4 515 53140.00 I  -.101285  .000044   .442347  .000039  I -.4643778  .0000059  1.0526 0.0032  I   -51.342     .256    -4.688     .340 -0.101230  0.442280 -0.4643800   -51.400    -4.500  
+ 4 516 53141.00 I  -.100663  .000040   .444171  .000051  I -.4654145  .0000046  1.0045 0.0040  I   -51.607     .208    -4.428     .340 -0.100590  0.444050 -0.4654470   -51.700    -4.300  
+ 4 517 53142.00 I  -.100222  .000044   .445634  .000057  I -.4663622  .0000054  0.8825 0.0035  I   -51.895     .215    -4.242     .340 -0.100120  0.445550 -0.4663810   -52.000    -4.200  
+ 4 518 53143.00 I  -.099643  .000047   .446834  .000061  I -.4671623  .0000053  0.7090 0.0037  I   -52.053     .248    -4.287     .153 -0.099630  0.446880 -0.4671500   -52.100    -4.300  
+ 4 519 53144.00 I  -.098872  .000046   .448195  .000059  I -.4677718  .0000051  0.5104 0.0037  I   -52.067     .248    -4.613     .153 -0.098880  0.448150 -0.4677680   -52.100    -4.600  
+ 4 520 53145.00 I  -.097892  .000043   .449518  .000059  I -.4681787  .0000052  0.2987 0.0035  I   -52.023     .248    -5.047     .153 -0.097620  0.449480 -0.4681660   -52.100    -5.000  
+ 4 521 53146.00 I  -.096351  .000043   .451032  .000060  I -.4683770  .0000049  0.1103 0.0048  I   -51.996     .248    -5.335     .153 -0.096230  0.450990 -0.4683520   -52.200    -5.300  
+ 4 522 53147.00 I  -.095216  .000048   .452371  .000051  I -.4684214  .0000080 -0.0119 0.0042  I   -51.986     .245    -5.396     .196 -0.095120  0.452300 -0.4684340   -52.400    -5.400  
+ 4 523 53148.00 I  -.094283  .000055   .453634  .000062  I -.4683734  .0000067 -0.0734 0.0054  I   -52.101     .213    -5.402     .182 -0.094210  0.453520 -0.4684300   -52.600    -5.400  
+ 4 524 53149.00 I  -.093244  .000046   .455065  .000049  I -.4683010  .0000072 -0.0557 0.0049  I   -52.215     .451    -5.476     .211 -0.093170  0.454970 -0.4683400   -52.500    -5.400  
+ 4 525 53150.00 I  -.091746  .000047   .456635  .000045  I -.4682783  .0000072  0.0110 0.0050  I   -52.133     .408    -5.552     .192 -0.091820  0.456610 -0.4682760   -52.100    -5.400  
+ 4 526 53151.00 I  -.090929  .000056   .458356  .000048  I -.4683310  .0000070  0.1018 0.0050  I   -51.802     .412    -5.497     .195 -0.090800  0.458290 -0.4683110   -51.500    -5.600  
+ 4 527 53152.00 I  -.090161  .000057   .459926  .000050  I -.4685068  .0000070  0.2674 0.0045  I   -51.502     .412    -5.362     .195 -0.090100  0.459820 -0.4684840   -51.100    -5.600  
+ 4 528 53153.00 I  -.089505  .000054   .461504  .000054  I -.4688631  .0000057  0.4248 0.0054  I   -51.572     .412    -5.327     .195 -0.089290  0.461430 -0.4688530   -51.200    -5.500  
+ 4 529 53154.00 I  -.088327  .000043   .463072  .000046  I -.4693344  .0000081  0.5160 0.0042  I   -51.996     .529    -5.346     .275 -0.088260  0.463050 -0.4693440   -51.600    -5.200  
+ 4 530 53155.00 I  -.087159  .000044   .464429  .000059  I -.4698815  .0000063  0.5664 0.0048  I   -52.507     .324    -5.277     .193 -0.087050  0.464430 -0.4699190   -52.200    -4.900  
+ 4 531 53156.00 I  -.085652  .000058   .465710  .000072  I -.4704374  .0000052  0.5275 0.0040  I   -52.991     .275    -4.908     .161 -0.085550  0.465620 -0.4704730   -53.000    -4.600  
+ 4 6 1 53157.00 I  -.083861  .000049   .467217  .000076  I -.4708996  .0000050  0.3780 0.0036  I   -53.601     .170    -4.453     .340 -0.083740  0.467120 -0.4709090   -53.800    -4.300  
+ 4 6 2 53158.00 I  -.081805  .000050   .468919  .000081  I -.4711655  .0000049  0.1448 0.0035  I   -54.338     .141    -4.292     .340 -0.081900  0.468990 -0.4711550   -54.300    -4.200  
+ 4 6 3 53159.00 I  -.079966  .000054   .470697  .000072  I -.4711933  .0000049 -0.0760 0.0035  I   -54.846     .141    -4.528     .340 -0.079860  0.470660 -0.4711760   -54.600    -4.400  
+ 4 6 4 53160.00 I  -.077622  .000058   .472199  .000072  I -.4710285  .0000051 -0.2518 0.0035  I   -54.789     .141    -4.911     .340 -0.077750  0.472270 -0.4710280   -54.500    -4.700  
+ 4 6 5 53161.00 I  -.075844  .000053   .473768  .000069  I -.4707260  .0000050 -0.3185 0.0037  I   -54.246     .263    -5.194     .117 -0.075560  0.473570 -0.4707200   -54.400    -5.000  
+ 4 6 6 53162.00 I  -.074538  .000055   .475431  .000074  I -.4704486  .0000054 -0.2137 0.0037  I   -53.616     .376    -5.367     .145 -0.074410  0.475460 -0.4705960   -54.200    -5.100  
+ 4 6 7 53163.00 I  -.073384  .000058   .476953  .000070  I -.4703249  .0000054 -0.0300 0.0038  I   -53.236     .350    -5.497     .170 -0.073240  0.476850 -0.4704280   -54.000    -5.200  
+ 4 6 8 53164.00 I  -.071790  .000061   .478291  .000060  I -.4703857  .0000054  0.1452 0.0038  I   -53.205     .411    -5.507     .187 -0.071780  0.478280 -0.4703790   -53.700    -5.300  
+ 4 6 9 53165.00 I  -.070023  .000064   .479927  .000062  I -.4706120  .0000053  0.3106 0.0037  I   -53.438     .349    -5.272     .164 -0.069820  0.479820 -0.4706000   -53.300    -5.400  
+ 4 610 53166.00 I  -.068415  .000063   .481560  .000061  I -.4709900  .0000052  0.4251 0.0033  I   -53.752     .349    -4.856     .164 -0.068360  0.481590 -0.4710010   -53.400    -5.100  
+ 4 611 53167.00 I  -.066916  .000066   .483053  .000061  I -.4714385  .0000039  0.4683 0.0035  I   -53.995     .349    -4.488     .164 -0.066580  0.482870 -0.4714330   -53.700    -4.600  
+ 4 612 53168.00 I  -.064946  .000052   .484490  .000040  I -.4719083  .0000048  0.4566 0.0032  I   -54.173     .237    -4.302     .140 -0.064920  0.484510 -0.4719020   -53.900    -4.200  
+ 4 613 53169.00 I  -.062809  .000060   .485929  .000048  I -.4723215  .0000050  0.3544 0.0035  I   -54.418     .354    -4.242     .340 -0.062670  0.485760 -0.4723950   -54.000    -4.000  
+ 4 614 53170.00 I  -.060448  .000055   .487271  .000053  I -.4725890  .0000050  0.1679 0.0039  I   -54.761     .351    -4.239     .106 -0.060290  0.487340 -0.4726390   -54.100    -4.100  
+ 4 615 53171.00 I  -.057872  .000045   .488518  .000063  I -.4726355  .0000059 -0.0841 0.0039  I   -55.061     .424    -4.370     .113 -0.057860  0.488480 -0.4726280   -54.400    -4.300  
+ 4 616 53172.00 I  -.054925  .000049   .489957  .000067  I -.4724252  .0000061 -0.3231 0.0043  I   -55.187     .388    -4.723     .111 -0.054740  0.489970 -0.4724330   -55.000    -4.500  
+ 4 617 53173.00 I  -.051591  .000048   .491565  .000068  I -.4720151  .0000063 -0.4847 0.0045  I   -55.181     .388    -5.181     .111 -0.051580  0.491350 -0.4720090   -55.400    -4.900  
+ 4 618 53174.00 I  -.048338  .000050   .493356  .000060  I -.4714770  .0000065 -0.5826 0.0051  I   -55.174     .388    -5.476     .111 -0.048070  0.493340 -0.4714770   -55.300    -5.300  
+ 4 619 53175.00 I  -.045151  .000046   .495324  .000063  I -.4708767  .0000080 -0.6002 0.0048  I   -55.250     .308    -5.462     .125 -0.045250  0.495410 -0.4708420   -55.300    -5.400  
+ 4 620 53176.00 I  -.041917  .000078   .497013  .000075  I -.4702972  .0000071 -0.5561 0.0053  I   -55.425     .288    -5.272     .340 -0.041700  0.496900 -0.4702260   -55.300    -5.400  
+ 4 621 53177.00 I  -.038587  .000075   .498380  .000073  I -.4697795  .0000071 -0.4668 0.0051  I   -55.643     .288    -5.124     .340 -0.038620  0.498330 -0.4697110   -55.300    -5.300  
+ 4 622 53178.00 I  -.035038  .000080   .499642  .000083  I -.4693810  .0000074 -0.3251 0.0052  I   -55.762     .277    -5.064     .125 -0.035040  0.499680 -0.4693630   -55.200    -5.100  
+ 4 623 53179.00 I  -.031932  .000081   .500942  .000089  I -.4691325  .0000075 -0.1737 0.0053  I   -55.669     .312    -4.978     .164 -0.031760  0.500860 -0.4691500   -55.200    -5.000  
+ 4 624 53180.00 I  -.028534  .000080   .502083  .000090  I -.4690415  .0000075  0.0015 0.0051  I   -55.478     .312    -4.827     .164 -0.028540  0.502170 -0.4690560   -55.200    -5.000  
+ 4 625 53181.00 I  -.025193  .000078   .503189  .000078  I -.4691332  .0000070  0.1698 0.0054  I   -55.476     .312    -4.719     .164 -0.024900  0.503120 -0.4691160   -55.500    -4.900  
+ 4 626 53182.00 I  -.022008  .000050   .504710  .000071  I -.4693442  .0000079  0.2272 0.0048  I   -55.825     .282    -4.720     .207 -0.022050  0.504870 -0.4693900   -55.900    -4.800  
+ 4 627 53183.00 I  -.019193  .000068   .506084  .000082  I -.4695550  .0000065  0.1866 0.0054  I   -56.434     .231    -4.706     .184 -0.019040  0.505910 -0.4695710   -56.500    -4.500  
+ 4 628 53184.00 I  -.016272  .000064   .507094  .000078  I -.4696938  .0000073  0.0750 0.0055  I   -57.147     .231    -4.546     .184 -0.016150  0.507110 -0.4696610   -57.100    -4.300  
+ 4 629 53185.00 I  -.013092  .000059   .507954  .000066  I -.4696846  .0000088 -0.0979 0.0055  I   -57.873     .163    -4.337     .158 -0.013090  0.507900 -0.4696690   -57.700    -4.200  
+ 4 630 53186.00 I  -.010372  .000058   .509048  .000084  I -.4694788  .0000082 -0.3285 0.0060  I   -58.474     .210    -4.317     .164 -0.010180  0.508950 -0.4694860   -58.000    -4.300  
+ 4 7 1 53187.00 I  -.007575  .000058   .510126  .000084  I -.4690288  .0000083 -0.5554 0.0058  I   -58.723     .210    -4.542     .164 -0.007570  0.510130 -0.4689890   -58.100    -4.500  
+ 4 7 2 53188.00 I  -.004600  .000060   .510900  .000081  I -.4683871  .0000083 -0.7249 0.0084  I   -58.525     .210    -4.803     .164 -0.004540  0.510830 -0.4683790   -58.000    -4.800  
+ 4 7 3 53189.00 I  -.001920  .000037   .511647  .000070  I -.4676191  .0000146 -0.7742 0.0062  I   -58.071     .328    -4.925     .202 -0.001750  0.511710 -0.4676050   -57.900    -5.100  
+ 4 7 4 53190.00 I   .000401  .000040   .512396  .000071  I -.4669017  .0000091 -0.6345 0.0076  I   -57.656     .310    -4.981     .190  0.000410  0.512320 -0.4671130   -57.700    -5.200  
+ 4 7 5 53191.00 I   .002673  .000069   .513106  .000070  I -.4663809  .0000040 -0.4025 0.0047  I   -57.444     .279    -5.093     .165  0.002740  0.512910 -0.4665540   -57.700    -5.200  
+ 4 7 6 53192.00 I   .005049  .000075   .513764  .000052  I -.4660892  .0000025 -0.1930 0.0023  I   -57.499     .289    -5.165     .143  0.005140  0.513650 -0.4661270   -57.700    -5.100  
+ 4 7 7 53193.00 I   .007454  .000087   .514361  .000066  I -.4659612  .0000023 -0.0847 0.0017  I   -57.852     .362    -4.989     .120  0.007380  0.514200 -0.4659470   -57.900    -4.800  
+ 4 7 8 53194.00 I   .009556  .000097   .514766  .000064  I -.4658828  .0000022 -0.0873 0.0017  I   -58.406     .362    -4.582     .120  0.009680  0.514750 -0.4659020   -58.100    -4.500  
+ 4 7 9 53195.00 I   .012039  .000096   .515216  .000056  I -.4657611  .0000025 -0.1665 0.0017  I   -58.912     .350    -4.237     .340  0.012350  0.515160 -0.4657470   -58.300    -4.200  
+ 4 710 53196.00 I   .015246  .000094   .516120  .000052  I -.4655330  .0000027 -0.2962 0.0026  I   -59.201     .330    -4.182     .340  0.015460  0.516110 -0.4655440   -58.600    -4.000  
+ 4 711 53197.00 I   .018248  .000089   .517022  .000046  I -.4651594  .0000045 -0.4538 0.0028  I   -59.368     .340    -4.343     .340  0.018290  0.517010 -0.4651960   -59.000    -4.000  
+ 4 712 53198.00 I   .021364  .000088   .517837  .000045  I -.4646179  .0000049 -0.6341 0.0037  I   -59.584     .337    -4.515     .340  0.021370  0.517650 -0.4646290   -59.300    -4.200  
+ 4 713 53199.00 I   .024113  .000060   .518714  .000042  I -.4638838  .0000058 -0.8368 0.0037  I   -59.834     .195    -4.655     .340  0.024340  0.518770 -0.4638740   -59.600    -4.500  
+ 4 714 53200.00 I   .026250  .000061   .519365  .000056  I -.4629601  .0000056 -0.9921 0.0039  I   -59.973     .176    -4.874     .340  0.026250  0.519040 -0.4629580   -59.800    -4.900  
+ 4 715 53201.00 I   .028217  .000059   .519926  .000056  I -.4619325  .0000053 -1.0487 0.0039  I   -59.978     .176    -5.185     .340  0.028290  0.519920 -0.4619040   -59.800    -5.200  
+ 4 716 53202.00 I   .030203  .000058   .520327  .000054  I -.4608838  .0000054 -1.0421 0.0038  I   -59.958     .176    -5.409     .340  0.030460  0.520300 -0.4608800   -59.800    -5.400  
+ 4 717 53203.00 I   .032705  .000038   .520670  .000056  I -.4598706  .0000055 -0.9687 0.0035  I   -59.985     .214    -5.367     .102  0.032540  0.520650 -0.4598740   -59.800    -5.400  
+ 4 718 53204.00 I   .035672  .000056   .520840  .000054  I -.4589705  .0000043 -0.8230 0.0035  I   -60.037     .190    -5.103     .340  0.035970  0.520750 -0.4591510   -59.900    -5.200  
+ 4 719 53205.00 I   .038660  .000060   .520771  .000052  I -.4582343  .0000043 -0.6481 0.0030  I   -60.095     .330    -4.833     .340  0.038570  0.520680 -0.4583560   -59.900    -4.900  
+ 4 720 53206.00 I   .041511  .000057   .520529  .000041  I -.4576796  .0000043 -0.4574 0.0032  I   -60.153     .407    -4.704     .340  0.041520  0.520480 -0.4576720   -59.900    -4.700  
+ 4 721 53207.00 I   .043961  .000066   .520203  .000063  I -.4573299  .0000048 -0.2355 0.0032  I   -60.166     .391    -4.686     .340  0.044090  0.520190 -0.4573140   -59.900    -4.600  
+ 4 722 53208.00 I   .046939  .000067   .519847  .000064  I -.4571998  .0000048 -0.0400 0.0034  I   -60.113     .391    -4.669     .340  0.046900  0.519710 -0.4572010   -59.900    -4.600  
+ 4 723 53209.00 I   .049952  .000060   .519564  .000057  I -.4572198  .0000047  0.0651 0.0049  I   -60.105     .391    -4.615     .340  0.050240  0.519410 -0.4572220   -60.200    -4.600  
+ 4 724 53210.00 I   .053021  .000055   .519248  .000066  I -.4573009  .0000085  0.0829 0.0050  I   -60.320     .432    -4.562     .144  0.053030  0.519170 -0.4573080   -60.600    -4.600  
+ 4 725 53211.00 I   .056051  .000065   .519070  .000073  I -.4573547  .0000089  0.0078 0.0061  I   -60.829     .253    -4.530     .187  0.056170  0.518960 -0.4575160   -61.200    -4.500  
+ 4 726 53212.00 I   .059028  .000070   .519102  .000071  I -.4572835  .0000088 -0.1665 0.0065  I   -61.514     .253    -4.513     .187  0.059160  0.518990 -0.4573630   -61.700    -4.400  
+ 4 727 53213.00 I   .061412  .000057   .519119  .000062  I -.4570009  .0000096 -0.4032 0.0063  I   -62.137     .250    -4.549     .209  0.061620  0.519010 -0.4569860   -62.100    -4.300  
+ 4 728 53214.00 I   .063962  .000063   .518775  .000095  I -.4564814  .0000090 -0.6269 0.0067  I   -62.446     .326    -4.685     .192  0.064010  0.518560 -0.4565000   -62.000    -4.500  
+ 4 729 53215.00 I   .066369  .000063   .518305  .000107  I -.4557825  .0000094 -0.7442 0.0063  I   -62.337     .326    -4.863     .192  0.066520  0.518320 -0.4557640   -61.800    -4.700  
+ 4 730 53216.00 I   .068319  .000063   .517607  .000108  I -.4550403  .0000089 -0.7195 0.0061  I   -61.963     .326    -4.924     .192  0.068510  0.517600 -0.4550260   -61.400    -4.900  
+ 4 731 53217.00 I   .070361  .000047   .516935  .000098  I -.4543862  .0000078 -0.5666 0.0055  I   -61.633     .425    -4.800     .114  0.070360  0.516990 -0.4544200   -61.200    -5.100  
+ 4 8 1 53218.00 I   .072781  .000043   .516275  .000090  I -.4539457  .0000063 -0.2977 0.0052  I   -61.521     .372    -4.645     .340  0.073010  0.516150 -0.4539160   -61.200    -5.100  
+ 4 8 2 53219.00 I   .075510  .000043   .515710  .000083  I -.4538001  .0000070 -0.0008 0.0052  I   -61.553     .372    -4.644     .340  0.075510  0.515600 -0.4537780   -61.300    -5.000  
+ 4 8 3 53220.00 I   .078334  .000040   .515337  .000041  I -.4539125  .0000084  0.2015 0.0054  I   -61.649     .286    -4.753     .340                                                     
+ 4 8 4 53221.00 I   .081083  .000059   .515209  .000058  I -.4541573  .0000081  0.2665 0.0057  I   -61.898     .243    -4.747     .340                                                     
+ 4 8 5 53222.00 I   .083897  .000056   .515109  .000058  I -.4544068  .0000078  0.2155 0.0056  I   -62.389     .243    -4.551     .340                                                     
+ 4 8 6 53223.00 I   .086289  .000056   .514972  .000062  I -.4545710  .0000078  0.1127 0.0078  I   -62.967     .243    -4.364     .340                                                     
+ 4 8 7 53224.00 I   .088893  .000061   .514756  .000060  I -.4546235  .0000134 -0.0162 0.0066  I   -63.362     .198    -4.403     .131                                                     
+ 4 8 8 53225.00 I   .091703  .000068   .514261  .000055  I -.4545264  .0000107 -0.1816 0.0084  I   -63.499     .204    -4.619     .180                                                     
+ 4 8 9 53226.00 I   .094703  .000073   .513748  .000056  I -.4542593  .0000101 -0.3498 0.0077  I   -63.512     .204    -4.789     .180                                                     
+ 4 810 53227.00 I   .097328  .000057   .513284  .000048  I -.4538279  .0000110 -0.5146 0.0075  I   -63.499     .190    -4.820     .252                                                     
+ 4 811 53228.00 I   .099793  .000053   .512913  .000044  I -.4532509  .0000110 -0.6170 0.0077  I   -63.429     .190    -4.835     .252                                                     
+ 4 812 53229.00 I   .102671  .000053   .512912  .000045  I -.4526176  .0000109 -0.6481 0.0071  I   -63.295     .190    -4.959     .252                                                     
+ 4 813 53230.00 I   .105716  .000053   .513243  .000051  I -.4519727  .0000091 -0.6250 0.0075  I   -63.171     .190    -5.123     .252                                                     
+ 4 814 53231.00 I   .108723  .000025   .513578  .000059  I -.4513936  .0000103 -0.5240 0.0059  I   -63.092     .128    -5.149     .182                                                     
+ 4 815 53232.00 I   .111601  .000036   .513710  .000073  I -.4509461  .0000075 -0.3589 0.0063  I   -63.011     .740    -4.970     .131                                                     
+ 4 816 53233.00 I   .114518  .000039   .513525  .000078  I -.4506962  .0000074 -0.1327 0.0053  I   -62.912     .740    -4.710     .131                                                     
+ 4 817 53234.00 I   .117350  .000044   .513055  .000071  I -.4506875  .0000074  0.1139 0.0051  I   -62.857     .740    -4.550     .340                                                     
+ 4 818 53235.00 I   .119692  .000074   .512403  .000087  I -.4509139  .0000071  0.3302 0.0052  I   -62.883     .160    -4.566     .114                                                     
+ 4 819 53236.00 I   .122032  .000073   .511787  .000084  I -.4513301  .0000072  0.4931 0.0050  I   -62.940     .160    -4.674     .114                                                     
+ 4 820 53237.00 I   .124064  .000080   .511184  .000087  I -.4518682  .0000069  0.5603 0.0050  I   -63.005     .160    -4.739     .114                                                     
+ 4 821 53238.00 I   .126381  .000083   .510397  .000089  I -.4524049  .0000070  0.4901 0.0043  I   -63.161     .200    -4.703     .133                                                     
+ 4 822 53239.00 I   .128317  .000078   .509289  .000087  I -.4528121  .0000052  0.3109 0.0046  I   -63.495     .172    -4.622     .113                                                     
+ 4 823 53240.00 I   .130124  .000087   .507871  .000091  I -.4530115  .0000061  0.0861 0.0042  I   -63.939     .172    -4.597     .113                                                     
+ 4 824 53241.00 I   .132327  .000037   .506361  .000067  I -.4529954  .0000067 -0.1055 0.0050  I   -64.263     .740    -4.676     .340                                                     
+ 4 825 53242.00 I   .134350  .000050   .504927  .000068  I -.4528134  .0000080 -0.2584 0.0052  I   -64.246     .126    -4.815     .340                                                     
+ 4 826 53243.00 I   .136176  .000051   .503711  .000068  I -.4524934  .0000079 -0.3669 0.0057  I   -63.877     .126    -4.893     .340                                                     
+ 4 827 53244.00 I   .138473  .000054   .502455  .000068  I -.4521208  .0000080 -0.3518 0.0087  I   -63.406     .126    -4.799     .340                                                     
+ 4 828 53245.00 I   .141017  .000057   .501217  .000063  I -.4518467  .0000155 -0.1661 0.0076  P   -63.172     .600    -4.511     .600                                                     
+ 4 829 53246.00 I   .143724  .000058   .499942  .000066  I -.4518233  .0000130  0.1237 0.0100  P   -63.216     .600    -4.226     .600                                                     
+ 4 830 53247.00 I   .146735  .000058   .498642  .000065  I -.4520879  .0000126  0.3948 0.0091  P   -63.377     .600    -4.094     .600                                                     
+ 4 831 53248.00 I   .149528  .000048   .497266  .000060  I -.4525797  .0000126  0.5664 0.0090  P   -63.473     .600    -4.134     .600                                                     
+ 4 9 1 53249.00 I   .152127  .000048   .495657  .000060  I -.4531729  .0000128  0.5948 0.0103  P   -63.565     .600    -4.206     .600                                                     
+ 4 9 2 53250.00 I   .154737  .000045   .493902  .000060  I -.4537242  .0000163  0.4880 0.0104  P   -63.844     .600    -4.210     .600                                                     
+ 4 9 3 53251.00 I   .157024  .000046   .491950  .000057  I -.4541177  .0000163  0.2875 0.0116  P   -64.326     .600    -4.205     .600                                                     
+ 4 9 4 53252.00 I   .159212  .000045   .489734  .000056  I -.4542779  .0000166  0.0231 0.0115  P   -64.786     .600    -4.298     .600                                                     
+ 4 9 5 53253.00 I   .161557  .000043   .487316  .000049  I -.4541614  .0000163 -0.2489 0.0109  P   -64.991     .600    -4.446     .600                                                     
+ 4 9 6 53254.00 I   .163658  .000033   .484857  .000040  I -.4537921  .0000140 -0.4849 0.0108  P   -64.896     .600    -4.501     .600                                                     
+ 4 9 7 53255.00 I   .165419  .000032   .482637  .000037  I -.4532001  .0000143 -0.6952 0.0087  P   -64.606     .600    -4.416     .600                                                     
+ 4 9 8 53256.00 I   .167322  .000027   .480727  .000026  I -.4524551  .0000105 -0.7451 0.0087  P   -64.240     .600    -4.329     .600                                                     
+ 4 9 9 53257.00 I   .169420  .000023   .478990  .000018  I -.4516544  .0000098                 P   -63.885     .600    -4.385     .600                                                     
+ 4 910 53258.00 P  0.171467 0.004200  0.477353 0.004200  P -.4509247  .0000094                 P   -63.605     .600    -4.550     .600                                                     
+ 4 911 53259.00 P  0.173411 0.005100  0.475723 0.005100  P -.4503884  .0000096                 P   -63.419     .600    -4.649     .600                                                     
+ 4 912 53260.00 P  0.175264 0.005713  0.474087 0.005713  P -.4501214  .0000120                 P   -63.312     .600    -4.560     .600                                                     
+ 4 913 53261.00 P  0.177037 0.006192  0.472436 0.006192  P -.4501491  .0000114                 P   -63.272     .600    -4.327     .600                                                     
+ 4 914 53262.00 P  0.178738 0.006591  0.470764 0.006591  P -.4504461  .0000106                 P   -63.307     .600    -4.111     .600                                                     
+ 4 915 53263.00 P  0.180376 0.006936  0.469070 0.006936  P-0.4509294 0.0003000                 P   -63.406     .600    -4.047     .600                                                     
+ 4 916 53264.00 P  0.181956 0.007242  0.467351 0.007242  P-0.4514721 0.0005048                 P   -63.522     .600    -4.143     .600                                                     
+ 4 917 53265.00 P  0.183483 0.007518  0.465607 0.007518  P-0.4519581 0.0006844                 P   -63.631     .600    -4.283     .600                                                     
+ 4 918 53266.00 P  0.184961 0.007770  0.463838 0.007770  P-0.4523358 0.0008494                 P   -63.761     .600    -4.340     .600                                                     
+ 4 919 53267.00 P  0.186394 0.008003  0.462044 0.008003  P-0.4525694 0.0010043                 P   -63.935     .600    -4.296     .600                                                     
+ 4 920 53268.00 P  0.187784 0.008219  0.460226 0.008219  P-0.4526379 0.0011516                 P   -64.084     .600    -4.237     .600                                                     
+ 4 921 53269.00 P  0.189133 0.008422  0.458385 0.008422  P-0.4525611 0.0012929                 P   -64.063     .600    -4.241     .600                                                     
+ 4 922 53270.00 P  0.190444 0.008613  0.456520 0.008613  P-0.4524069 0.0014292                 P   -63.767     .600    -4.287     .600                                                     
+ 4 923 53271.00 P  0.191717 0.008794  0.454633 0.008794  P-0.4522814 0.0015613                 P   -63.254     .600    -4.291     .600                                                     
+ 4 924 53272.00 P  0.192953 0.008965  0.452724 0.008965  P-0.4523216 0.0016899                 P   -62.740     .600    -4.187     .600                                                     
+ 4 925 53273.00 P  0.194154 0.009129  0.450794 0.009129  P-0.4526384 0.0018152                 P   -62.445     .600    -3.986     .600                                                     
+ 4 926 53274.00 P  0.195320 0.009285  0.448843 0.009285  P-0.4532626 0.0019377                 P   -62.421     .600    -3.759     .600                                                     
+ 4 927 53275.00 P  0.196451 0.009435  0.446873 0.009435  P-0.4541610 0.0020577                 P   -62.529     .600    -3.584     .600                                                     
+ 4 928 53276.00 P  0.197548 0.009579  0.444882 0.009579  P-0.4552339 0.0021755                 P   -62.603     .600    -3.495     .600                                                     
+ 4 929 53277.00 P  0.198610 0.009717  0.442876 0.009717  P-0.4563520 0.0022911                 P   -62.624     .600    -3.476     .600                                                     
+ 4 930 53278.00 P  0.199639 0.009851  0.440847 0.009851  P-0.4573954 0.0024048                 P   -62.720     .600    -3.511     .600                                                     
+ 410 1 53279.00 P  0.200633 0.009980  0.438801 0.009980  P-0.4582793 0.0025168                 P   -62.992     .600    -3.612     .600                                                     
+ 410 2 53280.00 P  0.201593 0.010105  0.436738 0.010105  P-0.4589605 0.0026272                 P   -63.350     .600    -3.771     .600                                                     
+ 410 3 53281.00 P  0.202519 0.010226  0.434658 0.010226  P-0.4594370 0.0027360                 P   -63.560     .600    -3.904     .600                                                     
+ 410 4 53282.00 P  0.203410 0.010344  0.432562 0.010344  P-0.4597357 0.0028434                 P   -63.441     .600    -3.908     .600                                                     
+ 410 5 53283.00 P  0.204268 0.010458  0.430450 0.010458  P-0.4599121 0.0029495                 P   -63.020     .600    -3.802     .600                                                     
+ 410 6 53284.00 P  0.205090 0.010569  0.428324 0.010569  P-0.4600421 0.0030543                 P   -62.477     .600    -3.737     .600                                                     
+ 410 7 53285.00 P  0.205879 0.010677  0.426182 0.010677  P-0.4602003 0.0031580                 P   -61.967     .600    -3.823     .600                                                     
+ 410 8 53286.00 P  0.206632 0.010783  0.424026 0.010783  P-0.4604591 0.0032605                 P   -61.539     .600    -3.988     .600                                                     
+ 410 9 53287.00 P  0.207351 0.010885  0.421857 0.010885  P-0.4608814 0.0033620                 P   -61.222     .600    -4.054     .600                                                     
+ 41010 53288.00 P  0.208034 0.010986  0.419674 0.010986  P-0.4615152 0.0034624                 P   -61.099     .600    -3.928     .600                                                     
+ 41011 53289.00 P  0.208683 0.011084  0.417479 0.011084  P-0.4623817 0.0035619                 P   -61.220     .600    -3.664     .600                                                     
+ 41012 53290.00 P  0.209297 0.011180  0.415272 0.011180  P-0.4634664 0.0036605                 P   -61.490     .600    -3.385     .600                                                     
+ 41013 53291.00 P  0.209875 0.011274  0.413054 0.011274  P-0.4647095 0.0037582                 P   -61.727     .600    -3.195     .600                                                     
+ 41014 53292.00 P  0.210419 0.011365  0.410825 0.011365  P-0.4660094 0.0038551                 P   -61.835     .600    -3.154     .600                                                     
+ 41015 53293.00 P  0.210927 0.011455  0.408585 0.011455  P-0.4672379 0.0039512                 P   -61.854     .600    -3.251     .600                                                     
+ 41016 53294.00 P  0.211399 0.011544  0.406335 0.011544  P-0.4682771 0.0040465                 P   -61.852     .600    -3.399     .600                                                     
+ 41017 53295.00 P  0.211837 0.011630  0.404076 0.011630  P-0.4690588 0.0041411                 P   -61.826     .600    -3.484     .600                                                     
+ 41018 53296.00 P  0.212239 0.011715  0.401809 0.011715  P-0.4695842 0.0042349                 P   -61.727     .600    -3.468     .600                                                     
+ 41019 53297.00 P  0.212606 0.011798  0.399533 0.011798  P-0.4699288 0.0043281                 P   -61.512     .600    -3.407     .600                                                     
+ 41020 53298.00 P  0.212937 0.011880  0.397249 0.011880  P-0.4702173 0.0044206                 P   -61.163     .600    -3.369     .600                                                     
+ 41021 53299.00 P  0.213233 0.011961  0.394959 0.011961  P-0.4705836 0.0045125                 P   -60.705     .600    -3.358     .600                                                     
+ 41022 53300.00 P  0.213494 0.012040  0.392661 0.012040  P-0.4711389 0.0046037                 P   -60.231     .600    -3.329     .600                                                     
+ 41023 53301.00 P  0.213720 0.012118  0.390358 0.012118  P-0.4719346 0.0046944                 P   -59.867     .600    -3.247     .600                                                     
+ 41024 53302.00 P  0.213911 0.012194  0.388050 0.012194  P-0.4729557 0.0047844                 P   -59.696     .600    -3.112     .600                                                     
+ 41025 53303.00 P  0.214067 0.012269  0.385736 0.012269  P-0.4741376 0.0048740                 P   -59.701     .600    -2.940     .600                                                     
+ 41026 53304.00 P  0.214188 0.012343  0.383419 0.012343  P-0.4753802 0.0049629                 P   -59.782     .600    -2.749     .600                                                     
+ 41027 53305.00 P  0.214244 0.012416  0.381097 0.012416  P-0.4765759 0.0050514                 P   -59.837     .600    -2.585     .600                                                     
+ 41028 53306.00 P  0.214299 0.012488  0.378772 0.012488  P-0.4776326 0.0051393                 P   -59.839     .600    -2.523     .600                                                     
+ 41029 53307.00 P  0.214319 0.012559  0.376445 0.012559  P-0.4784898 0.0052268                 P   -59.858     .600    -2.623     .600                                                     
+ 41030 53308.00 P  0.214305 0.012629  0.374115 0.012629  P-0.4791295 0.0053137                 P   -59.949     .600    -2.851     .600                                                     
+ 41031 53309.00 P  0.214256 0.012698  0.371785 0.012698  P-0.4795725 0.0054002                 P   -60.031     .600    -3.063     .600                                                     
+ 411 1 53310.00 P  0.214173 0.012766  0.369453 0.012766  P-0.4798642 0.0054862                 P   -59.933     .600    -3.127     .600                                                     
+ 411 2 53311.00 P  0.214056 0.012833  0.367120 0.012833  P-0.4800693 0.0055718                 P   -59.593     .600    -3.064     .600                                                     
+ 411 3 53312.00 P  0.213905 0.012899  0.364788 0.012899  P-0.4802650 0.0056570                 P   -59.120     .600    -3.029     .600                                                     
+ 411 4 53313.00 P  0.213721 0.012964  0.362456 0.012964  P-0.4805271 0.0057417                 P   -58.635     .600    -3.108     .600                                                     
+ 411 5 53314.00 P  0.213503 0.013028  0.360126 0.013028  P-0.4809198 0.0058260                 P   -58.155     .600    -3.196     .600                                                     
+ 411 6 53315.00 P  0.213252 0.013092  0.357798 0.013092  P-0.4814890 0.0059099                 P   -57.729     .600    -3.129     .600                                                     
+ 411 7 53316.00 P  0.212968 0.013155  0.355472 0.013155  P-0.4822615 0.0059934                 P   -57.552     .600    -2.892     .600                                                     
+ 411 8 53317.00 P  0.212652 0.013217  0.353148 0.013217  P-0.4832419 0.0060766                 P   -57.772     .600    -2.612     .600                                                     
+ 411 9 53318.00 P  0.212303 0.013278  0.350829 0.013278  P-0.4844015 0.0061593                 P   -58.248     .600    -2.372     .600                                                     
+ 41110 53319.00 P  0.211922 0.013339  0.348513 0.013339  P-0.4856693 0.0062417                 P   -58.656     .600    -2.159     .600                                                     
+ 41111 53320.00 P  0.211509 0.013399  0.346202 0.013399  P-0.4869359 0.0063237                 P   -58.830     .600    -1.987     .600                                                     
+ 41112 53321.00 P  0.211064 0.013458  0.343895 0.013458  P-0.4880774 0.0064054                 P   -58.840     .600    -1.960     .600                                                     
+ 41113 53322.00 P  0.210588 0.013517  0.341595 0.013517  P-0.4889940 0.0064868                 P   -58.763     .600    -2.124     .600                                                     
+ 41114 53323.00 P  0.210080 0.013574  0.339300 0.013574  P-0.4896480 0.0065677                 P   -58.573     .600    -2.343     .600                                                     
+ 41115 53324.00 P  0.209542 0.013632  0.337013 0.013632  P-0.4900829 0.0066484                 P   -58.275     .600    -2.425     .600                                                     
+ 41116 53325.00 P  0.208973 0.013688  0.334732 0.013688  P-0.4904113 0.0067288                 P   -57.988     .600    -2.343     .600                                                     
+ 41117 53326.00 P  0.208374 0.013744  0.332459 0.013744  P-0.4907765 0.0068088                 P   -57.796     .600    -2.241     .600                                                     
+ 41118 53327.00 P  0.207745 0.013800  0.330195 0.013800  P-0.4913008 0.0068885                 P   -57.631     .600    -2.220     .600                                                     
+ 41119 53328.00 P  0.207086 0.013855  0.327939 0.013855  P-0.4920473 0.0069679                 P   -57.402     .600    -2.228     .600                                                     
+ 41120 53329.00 P  0.206397 0.013909  0.325692 0.013909  P-0.4930066 0.0070470                 P   -57.147     .600    -2.156     .600                                                     
+ 41121 53330.00 P  0.205680 0.013963  0.323456 0.013963  P-0.4941120 0.0071258                 P   -56.995     .600    -1.986     .600                                                     
+ 41122 53331.00 P  0.204933 0.014016  0.321229 0.014016  P-0.4952659 0.0072044                 P   -57.018     .600    -1.781     .600                                                     
+ 41123 53332.00 P  0.204158 0.014069  0.319013 0.014069  P-0.4963647 0.0072826                 P   -57.155     .600    -1.586     .600                                                     
+ 41124 53333.00 P  0.203355 0.014121  0.316809 0.014121  P-0.4973191 0.0073606                 P   -57.259     .600    -1.417     .600                                                     
+ 41125 53334.00 P  0.202524 0.014173  0.314617 0.014173  P-0.4980683 0.0074383                 P   -57.213     .600    -1.326     .600                                                     
+ 41126 53335.00 P  0.201665 0.014224  0.312436 0.014224  P-0.4985873 0.0075157                                                                                                             
+ 41127 53336.00 P  0.200779 0.014275  0.310269 0.014275  P-0.4988853 0.0075928                                                                                                             
+ 41128 53337.00 P  0.199866 0.014326  0.308115 0.014326  P-0.4990014 0.0076697                                                                                                             
+ 41129 53338.00 P  0.198926 0.014376  0.305974 0.014376  P-0.4989981 0.0077464                                                                                                             
+ 41130 53339.00 P  0.197960 0.014425  0.303848 0.014425  P-0.4989511 0.0078228                                                                                                             
+ 412 1 53340.00 P  0.196967 0.014474  0.301736 0.014474  P-0.4989372 0.0078989                                                                                                             
+ 412 2 53341.00 P  0.195949 0.014523  0.299639 0.014523  P-0.4990270 0.0079748                                                                                                             
+ 412 3 53342.00 P  0.194906 0.014571  0.297558 0.014571  P-0.4992734 0.0080505                                                                                                             
+ 412 4 53343.00 P  0.193837 0.014619  0.295494 0.014619  P-0.4997076 0.0081259                                                                                                             
+ 412 5 53344.00 P  0.192744 0.014666  0.293445 0.014666  P-0.5003367 0.0082011                                                                                                             
+ 412 6 53345.00 P  0.191626 0.014713  0.291413 0.014713  P-0.5011417 0.0082761                                                                                                             
+ 412 7 53346.00 P  0.190485 0.014760  0.289399 0.014760  P-0.5020732 0.0083508                                                                                                             
+ 412 8 53347.00 P  0.189319 0.014806  0.287403 0.014806  P-0.5030459 0.0084254                                                                                                             
+ 412 9 53348.00 P  0.188130 0.014852  0.285424 0.014852  P-0.5039492 0.0084997                                                                                                             
+ 41210 53349.00 P  0.186918 0.014897  0.283464 0.014897  P-0.5046735 0.0085737                                                                                                             
+ 41211 53350.00 P  0.185683 0.014943  0.281523 0.014943  P-0.5051471 0.0086476                                                                                                             
+ 41212 53351.00 P  0.184425 0.014987  0.279602 0.014987  P-0.5053720 0.0087213                                                                                                             
+ 41213 53352.00 P  0.183146 0.015032  0.277700 0.015032  P-0.5054332 0.0087948                                                                                                             
+ 41214 53353.00 P  0.181844 0.015076  0.275819 0.015076  P-0.5054706 0.0088680                                                                                                             
+ 41215 53354.00 P  0.180522 0.015120  0.273958 0.015120  P-0.5056282 0.0089411                                                                                                             
+ 41216 53355.00 P  0.179178 0.015163  0.272118 0.015163  P-0.5059987 0.0090139                                                                                                             
+ 41217 53356.00 P  0.177813 0.015206  0.270299 0.015206  P-0.5065953 0.0090866                                                                                                             
+ 41218 53357.00 P  0.176428 0.015249  0.268502 0.015249  P-0.5073581 0.0091591                                                                                                             
+ 41219 53358.00 P  0.175023 0.015292  0.266726 0.015292  P-0.5081832 0.0092314                                                                                                             
+ 41220 53359.00 P  0.173598 0.015334  0.264974 0.015334  P-0.5089593 0.0093035                                                                                                             
+ 41221 53360.00 P  0.172153 0.015376  0.263243 0.015376  P-0.5095954 0.0093754                                                                                                             
+ 41222 53361.00 P  0.170690 0.015418  0.261536 0.015418  P-0.5100343 0.0094471                                                                                                             
+ 41223 53362.00 P  0.169208 0.015459  0.259852 0.015459  P-0.5102545 0.0095186                                                                                                             
+ 41224 53363.00 P  0.167707 0.015500  0.258192 0.015500  P-0.5103288 0.0095900                                                                                                             
+ 41225 53364.00 P  0.166188 0.015541  0.256555 0.015541  P-0.5102251 0.0096612                                                                                                             
+ 41226 53365.00 P  0.164651 0.015581  0.254943 0.015581  P-0.5099981 0.0097322                                                                                                             
+ 41227 53366.00 P  0.163097 0.015622  0.253355 0.015622  P-0.5097180 0.0098031                                                                                                             
+ 41228 53367.00 P  0.161526 0.015662  0.251792 0.015662  P-0.5094608 0.0098738                                                                                                             
+ 41229 53368.00 P  0.159938 0.015701  0.250255 0.015701  P-0.5092973 0.0099443                                                                                                             
+ 41230 53369.00 P  0.158334 0.015741  0.248742 0.015741  P-0.5092826 0.0100146                                                                                                             
+ 41231 53370.00 P  0.156714 0.015780  0.247255 0.015780  P-0.5094488 0.0100848                                                                                                             
+ 5 1 1 53371.00 P  0.155077 0.015819  0.245794 0.015819  P-0.5098034 0.0101548                                                                                                             
+ 5 1 2 53372.00 P  0.153426 0.015858  0.244359 0.015858  P-0.5103293 0.0102247                                                                                                             
+ 5 1 3 53373.00 P  0.151759 0.015896  0.242950 0.015896  P-0.5109851 0.0102944                                                                                                             
+ 5 1 4 53374.00 P  0.150077 0.015935  0.241568 0.015935  P-0.5117037 0.0103639                                                                                                             
+ 5 1 5 53375.00 P  0.148381 0.015973  0.240213 0.015973  P-0.5123955 0.0104333                                                                                                             
+ 5 1 6 53376.00 P  0.146671 0.016010  0.238885 0.016010  P-0.5129620 0.0105026                                                                                                             
+ 5 1 7 53377.00 P  0.144947 0.016048  0.237584 0.016048  P-0.5133239 0.0105716                                                                                                             
+ 5 1 8 53378.00 P  0.143210 0.016085  0.236310 0.016085  P-0.5134554 0.0106406                                                                                                             
+ 5 1 9 53379.00 P  0.141459 0.016122  0.235064 0.016122  P-0.5134074 0.0107094                                                                                                             
+ 5 110 53380.00 P  0.139696 0.016159  0.233845 0.016159  P-0.5133019 0.0107780                                                                                                             
+ 5 111 53381.00 P  0.137920 0.016196  0.232655 0.016196  P-0.5132921 0.0108465                                                                                                             
+ 5 112 53382.00 P  0.136132 0.016232  0.231492 0.016232  P-0.5135043 0.0109149                                                                                                             
+ 5 113 53383.00 P  0.134333 0.016269  0.230358 0.016269  P-0.5139901 0.0109831                                                                                                             
+ 5 114 53384.00 P  0.132521 0.016305  0.229252 0.016305  P-0.5147105 0.0110512                                                                                                             
+ 5 115 53385.00 P  0.130699 0.016341  0.228175 0.016341  P-0.5155576 0.0111191                                                                                                             
+ 5 116 53386.00 P  0.128866 0.016376  0.227126 0.016376  P-0.5163974 0.0111869                                                                                                             
+ 5 117 53387.00 P  0.127022 0.016412  0.226107 0.016412  P-0.5171127 0.0112546                                                                                                             
+ 5 118 53388.00 P  0.125168 0.016447  0.225116 0.016447  P-0.5176277 0.0113221                                                                                                             
+ 5 119 53389.00 P  0.123305 0.016482  0.224154 0.016482  P-0.5179132 0.0113895                                                                                                             
+ 5 120 53390.00 P  0.121431 0.016517  0.223221 0.016517  P-0.5179776 0.0114567                                                                                                             
+ 5 121 53391.00 P  0.119549 0.016552  0.222318 0.016552  P-0.5178570 0.0115239                                                                                                             
+ 5 122 53392.00 P  0.117658 0.016586  0.221444 0.016586  P-0.5176063 0.0115909                                                                                                             
+ 5 123 53393.00 P  0.115758 0.016620  0.220599 0.016620  P-0.5172938 0.0116577                                                                                                             
+ 5 124 53394.00 P  0.113850 0.016654  0.219783 0.016654  P-0.5169945 0.0117245                                                                                                             
+ 5 125 53395.00 P  0.111934 0.016688  0.218998 0.016688  P-0.5167813 0.0117911                                                                                                             
+ 5 126 53396.00 P  0.110011 0.016722  0.218241 0.016722  P-0.5167145 0.0118576                                                                                                             
+ 5 127 53397.00 P  0.108080 0.016756  0.217515 0.016756  P-0.5168331 0.0119240                                                                                                             
+ 5 128 53398.00 P  0.106142 0.016789  0.216818 0.016789  P-0.5171485 0.0119902                                                                                                             
+ 5 129 53399.00 P  0.104198 0.016822  0.216151 0.016822  P-0.5176434 0.0120563                                                                                                             
+ 5 130 53400.00 P  0.102248 0.016856  0.215513 0.016856  P-0.5182740 0.0121223                                                                                                             
+ 5 131 53401.00 P  0.100292 0.016888  0.214906 0.016888  P-0.5189751 0.0121882                                                                                                             
+ 5 2 1 53402.00 P  0.098330 0.016921  0.214328 0.016921  P-0.5196660 0.0122540                                                                                                             
+ 5 2 2 53403.00 P  0.096363 0.016954  0.213780 0.016954  P-0.5202611 0.0123196                                                                                                             
+ 5 2 3 53404.00 P  0.094391 0.016986  0.213261 0.016986  P-0.5206869 0.0123852                                                                                                             
+ 5 2 4 53405.00 P  0.092415 0.017019  0.212773 0.017019  P-0.5209070 0.0124506                                                                                                             
+ 5 2 5 53406.00 P  0.090434 0.017051  0.212314 0.017051  P-0.5209454 0.0125159                                                                                                             
+ 5 2 6 53407.00 P  0.088449 0.017083  0.211885 0.017083  P-0.5208947 0.0125811                                                                                                             
+ 5 2 7 53408.00 P  0.086461 0.017114  0.211486 0.017114  P-0.5208967 0.0126462                                                                                                             
+ 5 2 8 53409.00 P  0.084470 0.017146  0.211116 0.017146  P-0.5210965 0.0127112                                                                                                             
+ 5 2 9 53410.00 P  0.082475 0.017178  0.210776 0.017178  P-0.5215885 0.0127760                                                                                                             
+ 5 210 53411.00 P  0.080479 0.017209  0.210465 0.017209  P-0.5223791 0.0128408                                                                                                             
+ 5 211 53412.00 P  0.078480 0.017240  0.210184 0.017240  P-0.5233845 0.0129054                                                                                                             
+ 5 212 53413.00 P  0.076479 0.017271  0.209933 0.017271  P-0.5244640 0.0129699                                                                                                             
+ 5 213 53414.00 P  0.074476 0.017302  0.209711 0.017302  P-0.5254701 0.0130344                                                                                                             
+ 5 214 53415.00 P  0.072473 0.017333  0.209518 0.017333  P-0.5262913 0.0130987                                                                                                             
+ 5 215 53416.00 P  0.070468 0.017364  0.209355 0.017364  P-0.5268723 0.0131629                                                                                                             
+ 5 216 53417.00 P  0.068463 0.017394  0.209221 0.017394  P-0.5272107 0.0132270                                                                                                             
+ 5 217 53418.00 P  0.066458 0.017425  0.209116 0.017425  P-0.5273423 0.0132910                                                                                                             
+ 5 218 53419.00 P  0.064453 0.017455  0.209039 0.017455  P-0.5273257 0.0133550                                                                                                             
+ 5 219 53420.00 P  0.062449 0.017485  0.208992 0.017485  P-0.5272315 0.0134188                                                                                                             
+ 5 220 53421.00 P  0.060445 0.017515  0.208974 0.017515  P-0.5271361 0.0134825                                                                                                             
+ 5 221 53422.00 P  0.058443 0.017545  0.208984 0.017545  P-0.5271148 0.0135461                                                                                                             
+ 5 222 53423.00 P  0.056442 0.017574  0.209023 0.017574  P-0.5272339 0.0136096                                                                                                             
+ 5 223 53424.00 P  0.054443 0.017604  0.209090 0.017604  P-0.5275409 0.0136730                                                                                                             
+ 5 224 53425.00 P  0.052447 0.017633  0.209186 0.017633  P-0.5280573 0.0137363                                                                                                             
+ 5 225 53426.00 P  0.050453 0.017663  0.209309 0.017663  P-0.5287729 0.0137995                                                                                                             
+ 5 226 53427.00 P  0.048462 0.017692  0.209461 0.017692  P-0.5296448 0.0138627                                                                                                             
+ 5 227 53428.00 P  0.046475 0.017721  0.209641 0.017721  P-0.5306033 0.0139257                                                                                                             
+ 5 228 53429.00 P  0.044491 0.017750  0.209848 0.017750  P-0.5315616 0.0139886                                                                                                             
+ 5 3 1 53430.00 P  0.042511 0.017779  0.210083 0.017779  P-0.5324305 0.0140514                                                                                                             
+ 5 3 2 53431.00 P  0.040536 0.017808  0.210346 0.017808  P-0.5331361 0.0141142                                                                                                             
+ 5 3 3 53432.00 P  0.038565 0.017836  0.210636 0.017836  P-0.5336392 0.0141768                                                                                                             
+ 5 3 4 53433.00 P  0.036600 0.017865  0.210953 0.017865  P-0.5339518 0.0142394                                                                                                             
+ 5 3 5 53434.00 P  0.034640 0.017893  0.211297 0.017893  P-0.5341455 0.0143019                                                                                                             
+ 5 3 6 53435.00 P  0.032686 0.017921  0.211667 0.017921  P-0.5343417 0.0143642                                                                                                             
+ 5 3 7 53436.00 P  0.030739 0.017949  0.212065 0.017949  P-0.5346819 0.0144265                                                                                                             
+ 5 3 8 53437.00 P  0.028797 0.017977  0.212488 0.017977  P-0.5352835 0.0144887                                                                                                             
+ 5 3 9 53438.00 P  0.026863 0.018005  0.212938 0.018005  P-0.5361962 0.0145508                                                                                                             
+ 5 310 53439.00 P  0.024936 0.018033  0.213414 0.018033  P-0.5373812 0.0146129                                                                                                             
+ 5 311 53440.00 P  0.023017 0.018061  0.213916 0.018061  P-0.5387212 0.0146748                                                                                                             
+ 5 312 53441.00 P  0.021106 0.018088  0.214444 0.018088  P-0.5400604 0.0147367                                                                                                             
+ 5 313 53442.00 P  0.019204 0.018116  0.214997 0.018116  P-0.5412527 0.0147984                                                                                                             
+ 5 314 53443.00 P  0.017310 0.018143  0.215575 0.018143  P-0.5421992 0.0148601                                                                                                             
+ 5 315 53444.00 P  0.015425 0.018170  0.216178 0.018170  P-0.5428621 0.0149217                                                                                                             
+ 5 316 53445.00 P  0.013550 0.018198  0.216807 0.018198  P-0.5432584 0.0149832                                                                                                             
+ 5 317 53446.00 P  0.011684 0.018225  0.217459 0.018225  P-0.5434432 0.0150446                                                                                                             
+ 5 318 53447.00 P  0.009829 0.018252  0.218137 0.018252  P-0.5434936 0.0151060                                                                                                             
+ 5 319 53448.00 P  0.008012 0.018278  0.218824 0.018278  P-0.5434951 0.0151672                                                                                                             
+ 5 320 53449.00 P  0.006205 0.018305  0.219536 0.018305  P-0.5435302 0.0152284                                                                                                             
+ 5 321 53450.00 P  0.004410 0.018332  0.220271 0.018332  P-0.5436681 0.0152895                                                                                                             
+ 5 322 53451.00 P  0.002626 0.018358  0.221030 0.018358  P-0.5439554 0.0153505                                                                                                             
+ 5 323 53452.00 P  0.000854 0.018385  0.221812 0.018385  P-0.5444110 0.0154114                                                                                                             
+ 5 324 53453.00 P -0.000905 0.018411  0.222617 0.018411  P-0.5450251 0.0154723                                                                                                             
+ 5 325 53454.00 P -0.002651 0.018437  0.223445 0.018437  P-0.5457597 0.0155331                                                                                                             
+ 5 326 53455.00 P -0.004384 0.018464  0.224295 0.018464  P-0.5465522 0.0155938                                                                                                             
+ 5 327 53456.00 P -0.006103 0.018490  0.225168 0.018490  P-0.5473227 0.0156544                                                                                                             
+ 5 328 53457.00 P -0.007809 0.018516  0.226063 0.018516  P-0.5479887 0.0157149                                                                                                             
+ 5 329 53458.00 P -0.009500 0.018541  0.226979 0.018541  P-0.5484844 0.0157754                                                                                                             
+ 5 330 53459.00 P -0.011177 0.018567  0.227918 0.018567  P-0.5487794 0.0158358                                                                                                             
+ 5 331 53460.00 P -0.012839 0.018593  0.228877 0.018593  P-0.5488933 0.0158961                                                                                                             
+ 5 4 1 53461.00 P -0.014485 0.018619  0.229858 0.018619  P-0.5488997 0.0159563                                                                                                             
+ 5 4 2 53462.00 P -0.016115 0.018644  0.230860 0.018644  P-0.5489169 0.0160165                                                                                                             
+ 5 4 3 53463.00 P -0.017730 0.018669  0.231882 0.018669  P-0.5490819 0.0160765                                                                                                             
+ 5 4 4 53464.00 P -0.019328 0.018695  0.232924 0.018695  P-0.5495117 0.0161366                                                                                                             
+ 5 4 5 53465.00 P -0.020909 0.018720  0.233987 0.018720  P-0.5502646 0.0161965                                                                                                             
+ 5 4 6 53466.00 P -0.022474 0.018745  0.235069 0.018745  P-0.5513150 0.0162563                                                                                                             
+ 5 4 7 53467.00 P -0.024020 0.018770  0.236171 0.018770  P-0.5525573 0.0163161                                                                                                             
+ 5 4 8 53468.00 P -0.025549 0.018795  0.237292 0.018795  P-0.5538381 0.0163758                                                                                                             
+ 5 4 9 53469.00 P -0.027060 0.018820  0.238432 0.018820  P-0.5550027 0.0164355                                                                                                             
+ 5 410 53470.00 P -0.028552 0.018845  0.239591 0.018845  P-0.5559384 0.0164951                                                                                                             
+ 5 411 53471.00 P -0.030026 0.018870  0.240768 0.018870  P-0.5565964 0.0165546                                                                                                             
+ 5 412 53472.00 P -0.031480 0.018894  0.241963 0.018894  P-0.5569916 0.0166140                                                                                                             
+ 5 413 53473.00 P -0.032915 0.018919  0.243176 0.018919  P-0.5571830 0.0166733                                                                                                             
+ 5 414 53474.00 P -0.034331 0.018943  0.244407 0.018943  P-0.5572454 0.0167326                                                                                                             
+ 5 415 53475.00 P -0.035726 0.018968  0.245656 0.018968  P-0.5572518 0.0167919                                                                                                             
+ 5 416 53476.00 P -0.037100 0.018992  0.246921 0.018992  P-0.5572716 0.0168510                                                                                                             
+ 5 417 53477.00 P -0.038454 0.019016  0.248203 0.019016  P-0.5573643 0.0169101                                                                                                             
+ 5 418 53478.00 P -0.039787 0.019041  0.249501 0.019041  P-0.5575677 0.0169691                                                                                                             
+ 5 419 53479.00 P -0.041099 0.019065  0.250816 0.019065  P-0.5579013 0.0170280                                                                                                             
+ 5 420 53480.00 P -0.042388 0.019089  0.252147 0.019089  P-0.5583943 0.0170869                                                                                                             
+ 5 421 53481.00 P -0.043656 0.019113  0.253493 0.019113  P-0.5590472 0.0171457                                                                                                             
+ 5 422 53482.00 P -0.044902 0.019136  0.254855 0.019136  P-0.5598039 0.0172045                                                                                                             
+ 5 423 53483.00 P -0.046125 0.019160  0.256232 0.019160  P-0.5605783 0.0172631                                                                                                             
+ 5 424 53484.00 P -0.047326 0.019184  0.257623 0.019184  P-0.5613063 0.0173218                                                                                                             
+ 5 425 53485.00 P -0.048503 0.019208  0.259029 0.019208  P-0.5618863 0.0173803                                                                                                             
+ 5 426 53486.00 P -0.049657 0.019231  0.260450 0.019231  P-0.5622709 0.0174388                                                                                                             
+ 5 427 53487.00 P -0.050788 0.019255  0.261884 0.019255  P-0.5624379 0.0174972                                                                                                             
+ 5 428 53488.00 P -0.051894 0.019278  0.263332 0.019278  P-0.5624141 0.0175555                                                                                                             
+ 5 429 53489.00 P -0.052977 0.019301  0.264793 0.019301  P-0.5622979 0.0176138                                                                                                             
+ 5 430 53490.00 P -0.054035 0.019325  0.266267 0.019325  P-0.5622364 0.0176720                                                                                                             
+ 5 5 1 53491.00 P -0.055069 0.019348  0.267754 0.019348  P-0.5623966 0.0177302                                                                                                             
+ 5 5 2 53492.00 P -0.056077 0.019371  0.269253 0.019371  P-0.5628463 0.0177883                                                                                                             
+ 5 5 3 53493.00 P -0.057061 0.019394  0.270764 0.019394  P-0.5635855 0.0178463                                                                                                             
+ 5 5 4 53494.00 P -0.058019 0.019417  0.272287 0.019417  P-0.5645240 0.0179043                                                                                                             
+ 5 5 5 53495.00 P -0.058952 0.019440  0.273822 0.019440  P-0.5655133 0.0179622                                                                                                             
+ 5 5 6 53496.00 P -0.059860 0.019463  0.275368 0.019463  P-0.5664359 0.0180200                                                                                                             
+ 5 5 7 53497.00 P -0.060741 0.019485  0.276925 0.019485  P-0.5672243 0.0180778                                                                                                             
+ 5 5 8 53498.00 P -0.061597 0.019508  0.278493 0.019508  P-0.5678252 0.0181355                                                                                                             
+ 5 5 9 53499.00 P -0.062426 0.019531  0.280071 0.019531  P-0.5682104 0.0181932                                                                                                             
+ 5 510 53500.00 P -0.063228 0.019553  0.281658 0.019553  P-0.5684041 0.0182508                                                                                                             
+ 5 511 53501.00 P -0.064004 0.019576  0.283256 0.019576  P-0.5684424 0.0183083                                                                                                             
+ 5 512 53502.00 P -0.064754 0.019598  0.284863 0.019598  P-0.5683919 0.0183658                                                                                                             
+ 5 513 53503.00 P -0.065476 0.019621  0.286479 0.019621  P-0.5683328 0.0184232                                                                                                             
+ 5 514 53504.00 P -0.066171 0.019643  0.288104 0.019643  P-0.5683574 0.0184806                                                                                                             
+ 5 515 53505.00 P -0.066840 0.019665  0.289738 0.019665  P-0.5685207 0.0185379                                                                                                             
+ 5 516 53506.00 P -0.067480 0.019687  0.291379 0.019687  P-0.5688847 0.0185951                                                                                                             
+ 5 517 53507.00 P -0.068094 0.019709  0.293029 0.019709  P-0.5694687 0.0186523                                                                                                             
+ 5 518 53508.00 P -0.068680 0.019731  0.294686 0.019731  P-0.5702051 0.0187094                                                                                                             
+ 5 519 53509.00 P -0.069238 0.019753  0.296350 0.019753  P-0.5710462 0.0187665                                                                                                             
+ 5 520 53510.00 P -0.069768 0.019775  0.298021 0.019775  P-0.5719304 0.0188235                                                                                                             
+ 5 521 53511.00 P -0.070271 0.019797  0.299699 0.019797  P-0.5727614 0.0188805                                                                                                             
+ 5 522 53512.00 P -0.070745 0.019819  0.301383 0.019819  P-0.5734194 0.0189374                                                                                                             
+ 5 523 53513.00 P -0.071192 0.019841  0.303073 0.019841  P-0.5738198 0.0189942                                                                                                             
+ 5 524 53514.00 P -0.071610 0.019862  0.304769 0.019862  P-0.5738968 0.0190510                                                                                                             
+ 5 525 53515.00 P -0.072001 0.019884  0.306470 0.019884  P-0.5736789 0.0191077                                                                                                             
+ 5 526 53516.00 P -0.072363 0.019906  0.308177 0.019906  P-0.5732932 0.0191644                                                                                                             
+ 5 527 53517.00 P -0.072697 0.019927  0.309888 0.019927  P-0.5728922 0.0192210                                                                                                             
+ 5 528 53518.00 P -0.073003 0.019948  0.311603 0.019948  P-0.5726124 0.0192776                                                                                                             
+ 5 529 53519.00 P -0.073281 0.019970  0.313323 0.019970  P-0.5725106 0.0193341                                                                                                             
+ 5 530 53520.00 P -0.073530 0.019991  0.315046 0.019991  P-0.5725870 0.0193905                                                                                                             
+ 5 531 53521.00 P -0.073751 0.020012  0.316773 0.020012  P-0.5728159 0.0194469                                                                                                             
+ 5 6 1 53522.00 P -0.073944 0.020034  0.318504 0.020034  P-0.5731251 0.0195032                                                                                                             
+ 5 6 2 53523.00 P -0.074109 0.020055  0.320237 0.020055  P-0.5734127 0.0195595                                                                                                             
+ 5 6 3 53524.00 P -0.074245 0.020076  0.321972 0.020076  P-0.5735879 0.0196158                                                                                                             
+ 5 6 4 53525.00 P -0.074354 0.020097  0.323710 0.020097  P-0.5735794 0.0196719                                                                                                             
+ 5 6 5 53526.00 P -0.074434 0.020118  0.325450 0.020118  P-0.5733438 0.0197281                                                                                                             
+ 5 6 6 53527.00 P -0.074486 0.020139  0.327191 0.020139  P-0.5728598 0.0197841                                                                                                             
+ 5 6 7 53528.00 P -0.074510 0.020160  0.328933 0.020160  P-0.5721281 0.0198402                                                                                                             
+ 5 6 8 53529.00 P -0.074506 0.020180  0.330677 0.020180  P-0.5712797 0.0198961                                                                                                             
+ 5 6 9 53530.00 P -0.074475 0.020201  0.332421 0.020201  P-0.5704031 0.0199521                                                                                                             
+ 5 610 53531.00 P -0.074416 0.020222  0.334166 0.020222  P-0.5695097 0.0200079                                                                                                             
+ 5 611 53532.00 P -0.074329 0.020242  0.335910 0.020242  P-0.5686014 0.0200637                                                                                                             
+ 5 612 53533.00 P -0.074214 0.020263  0.337654 0.020263  P-0.5677307 0.0201195                                                                                                             
+ 5 613 53534.00 P -0.074072 0.020284  0.339398 0.020284  P-0.5669817 0.0201752                                                                                                             
+ 5 614 53535.00 P -0.073903 0.020304  0.341141 0.020304  P-0.5663464 0.0202309                                                                                                             
+ 5 615 53536.00 P -0.073707 0.020324  0.342882 0.020324  P-0.5658082 0.0202865                                                                                                             
+ 5 616 53537.00 P -0.073484 0.020345  0.344622 0.020345  P-0.5653532 0.0203420                                                                                                             
+ 5 617 53538.00 P -0.073233 0.020365  0.346360 0.020365  P-0.5649379 0.0203975                                                                                                             
+ 5 618 53539.00 P -0.072956 0.020385  0.348095 0.020385  P-0.5644983 0.0204530                                                                                                             
+ 5 619 53540.00 P -0.072653 0.020406  0.349828 0.020406  P-0.5639137 0.0205084                                                                                                             
+ 5 620 53541.00 P -0.072323 0.020426  0.351559 0.020426  P-0.5631150 0.0205638                                                                                                             
+ 5 621 53542.00 P -0.071967 0.020446  0.353286 0.020446  P-0.5620583 0.0206191                                                                                                             
+ 5 622 53543.00 P -0.071585 0.020466  0.355009 0.020466  P-0.5608633 0.0206743                                                                                                             
+ 5 623 53544.00 P -0.071178 0.020486  0.356729 0.020486  P-0.5596658 0.0207295                                                                                                             
+ 5 624 53545.00 P -0.070745 0.020506  0.358445 0.020506  P-0.5585709 0.0207847                                                                                                             
+ 5 625 53546.00 P -0.070286 0.020526  0.360156 0.020526  P-0.5576565 0.0208398                                                                                                             
+ 5 626 53547.00 P -0.069802 0.020546  0.361863 0.020546  P-0.5569753 0.0208949                                                                                                             
+ 5 627 53548.00 P -0.069294 0.020566  0.363565 0.020566  P-0.5565155 0.0209499                                                                                                             
+ 5 628 53549.00 P -0.068761 0.020585  0.365261 0.020585  P-0.5561814 0.0210048                                                                                                             
+ 5 629 53550.00 P -0.068203 0.020605  0.366951 0.020605  P-0.5558311 0.0210598                                                                                                             
+ 5 630 53551.00 P -0.067621 0.020625  0.368636 0.020625  P-0.5553461 0.0211146                                                                                                             
+ 5 7 1 53552.00 P -0.067016 0.020644  0.370314 0.020644  P-0.5546563 0.0211695                                                                                                             
+ 5 7 2 53553.00 P -0.066386 0.020664  0.371986 0.020664  P-0.5537249 0.0212242                                                                                                             
+ 5 7 3 53554.00 P -0.065734 0.020683  0.373650 0.020683  P-0.5525553 0.0212790                                                                                                             
+ 5 7 4 53555.00 P -0.065058 0.020703  0.375308 0.020703  P-0.5511996 0.0213337                                                                                                             
+ 5 7 5 53556.00 P -0.064360 0.020722  0.376958 0.020722  P-0.5497363 0.0213883                                                                                                             
+ 5 7 6 53557.00 P -0.063639 0.020742  0.378600 0.020742  P-0.5482537 0.0214429                                                                                                             
+ 5 7 7 53558.00 P -0.062895 0.020761  0.380234 0.020761  P-0.5468062 0.0214974                                                                                                             
+ 5 7 8 53559.00 P -0.062130 0.020780  0.381860 0.020780  P-0.5454592 0.0215519                                                                                                             
+ 5 7 9 53560.00 P -0.061344 0.020800  0.383477 0.020800  P-0.5442381 0.0216064                                                                                                             
+ 5 710 53561.00 P -0.060536 0.020819  0.385084 0.020819  P-0.5431233 0.0216608                                                                                                             
+ 5 711 53562.00 P -0.059707 0.020838  0.386683 0.020838  P-0.5421922 0.0217152                                                                                                             
+ 5 712 53563.00 P -0.058858 0.020857  0.388272 0.020857  P-0.5414819 0.0217695                                                                                                             
+ 5 713 53564.00 P -0.057988 0.020876  0.389851 0.020876  P-0.5409254 0.0218238                                                                                                             
+ 5 714 53565.00 P -0.057099 0.020895  0.391419 0.020895  P-0.5404186 0.0218780                                                                                                             
+ 5 715 53566.00 P -0.056189 0.020914  0.392978 0.020914  P-0.5398830 0.0219322                                                                                                             
+ 5 716 53567.00 P -0.055261 0.020933  0.394525 0.020933  P-0.5392463 0.0219863                                                                                                             
+ 5 717 53568.00 P -0.054313 0.020952  0.396062 0.020952  P-0.5384172 0.0220404                                                                                                             
+ 5 718 53569.00 P -0.053347 0.020971  0.397587 0.020971  P-0.5373040 0.0220944                                                                                                             
+ 5 719 53570.00 P -0.052363 0.020989  0.399100 0.020989  P-0.5359209 0.0221485                                                                                                             
+ 5 720 53571.00 P -0.051361 0.021008  0.400601 0.021008  P-0.5344400 0.0222024                                                                                                             
+ 5 721 53572.00 P -0.050342 0.021027  0.402091 0.021027  P-0.5330005 0.0222563                                                                                                             
+ 5 722 53573.00 P -0.049305 0.021046  0.403567 0.021046  P-0.5317176 0.0223102                                                                                                             
+ 5 723 53574.00 P -0.048252 0.021064  0.405031 0.021064  P-0.5306862 0.0223640                                                                                                             
+ 5 724 53575.00 P -0.047182 0.021083  0.406482 0.021083  P-0.5299442 0.0224178                                                                                                             
+ 5 725 53576.00 P -0.046096 0.021101  0.407920 0.021101  P-0.5294145 0.0224716                                                                                                             
+ 5 726 53577.00 P -0.044994 0.021120  0.409344 0.021120  P-0.5289379 0.0225253                                                                                                             
+ 5 727 53578.00 P -0.043878 0.021138  0.410755 0.021138  P-0.5283369 0.0225790                                                                                                             
+ 5 728 53579.00 P -0.042746 0.021157  0.412151 0.021157  P-0.5274951 0.0226326                                                                                                             
+ 5 729 53580.00 P -0.041600 0.021175  0.413533 0.021175  P-0.5263843 0.0226862                                                                                                             
+ 5 730 53581.00 P -0.040439 0.021193  0.414900 0.021193  P-0.5250389 0.0227397                                                                                                             
+ 5 731 53582.00 P -0.039265 0.021212  0.416252 0.021212  P-0.5235308 0.0227932                                                                                                             
+ 5 8 1 53583.00 P -0.038078 0.021230  0.417590 0.021230  P-0.5219161 0.0228466                                                                                                             
+ 5 8 2 53584.00 P -0.036877 0.021248  0.418912 0.021248  P-0.5202708 0.0229000                                                                                                             
+ 5 8 3 53585.00 P -0.035664 0.021266  0.420218 0.021266  P-0.5187071 0.0229534                                                                                                             
+ 5 8 4 53586.00 P -0.034439 0.021285  0.421509 0.021285  P-0.5173021 0.0230067                                                                                                             
+ 5 8 5 53587.00 P -0.033202 0.021303  0.422783 0.021303  P-0.5161053 0.0230600                                                                                                             
+ 5 8 6 53588.00 P -0.031953 0.021321  0.424042 0.021321  P-0.5151366 0.0231133                                                                                                             
+ 5 8 7 53589.00 P -0.030693 0.021339  0.425283 0.021339  P-0.5143909 0.0231665                                                                                                             
+ 5 8 8 53590.00 P -0.029423 0.021357  0.426508 0.021357  P-0.5138229 0.0232197                                                                                                             
+ 5 8 9 53591.00 P -0.028142 0.021375  0.427717 0.021375  P-0.5133909 0.0232728                                                                                                             
+ 5 810 53592.00 P -0.026852 0.021393  0.428907 0.021393  P-0.5130107 0.0233259                                                                                                             
+ 5 811 53593.00 P -0.025551 0.021410  0.430081 0.021410  P-0.5125769 0.0233789                                                                                                             
+ 5 812 53594.00 P -0.024242 0.021428  0.431237 0.021428  P-0.5120121 0.0234319                                                                                                             
+ 5 813 53595.00 P -0.022924 0.021446  0.432375 0.021446  P-0.5112680 0.0234849                                                                                                             
+ 5 814 53596.00 P -0.021597 0.021464  0.433495 0.021464  P-0.5103416 0.0235378                                                                                                             
+ 5 815 53597.00 P -0.020262 0.021481  0.434597 0.021481  P-0.5092561 0.0235907                                                                                                             
+ 5 816 53598.00 P -0.018920 0.021499  0.435680 0.021499  P-0.5080685 0.0236435                                                                                                             
+ 5 817 53599.00 P -0.017570 0.021517  0.436745 0.021517  P-0.5068980 0.0236963                                                                                                             
+ 5 818 53600.00 P -0.016213 0.021534  0.437791 0.021534  P-0.5058864 0.0237491                                                                                                             
+ 5 819 53601.00 P -0.014850 0.021552  0.438819 0.021552  P-0.5051650 0.0238018                                                                                                             
+ 5 820 53602.00 P -0.013480 0.021569  0.439827 0.021569  P-0.5047915 0.0238545                                                                                                             
+ 5 821 53603.00 P -0.012105 0.021587  0.440815 0.021587  P-0.5047204 0.0239072                                                                                                             
+ 5 822 53604.00 P -0.010724 0.021604  0.441785 0.021604  P-0.5048754 0.0239598                                                                                                             
+ 5 823 53605.00 P -0.009338 0.021622  0.442734 0.021622  P-0.5050711 0.0240124                                                                                                             
+ 5 824 53606.00 P -0.007947 0.021639  0.443664 0.021639  P-0.5050958 0.0240649                                                                                                             
+ 5 825 53607.00 P -0.006551 0.021657  0.444574 0.021657  P-0.5048353 0.0241174                                                                                                             
+ 5 826 53608.00 P -0.005151 0.021674  0.445464 0.021674  P-0.5042904 0.0241699                                                                                                             
+ 5 827 53609.00 P -0.003748 0.021691  0.446334 0.021691  P-0.5035055 0.0242223                                                                                                             
+ 5 828 53610.00 P -0.002341 0.021708  0.447183 0.021708  P-0.5025326 0.0242747                                                                                                             
+ 5 829 53611.00 P -0.000930 0.021726  0.448012 0.021726  P-0.5014525 0.0243270                                                                                                             
+ 5 830 53612.00 P  0.000483 0.021743  0.448820 0.021743  P-0.5003552 0.0243793                                                                                                             
+ 5 831 53613.00 P  0.001898 0.021760  0.449608 0.021760  P-0.4993236 0.0244316                                                                                                             
+ 5 9 1 53614.00 P  0.003316 0.021777  0.450374 0.021777  P-0.4984066 0.0244838                                                                                                             
+ 5 9 2 53615.00 P  0.004736 0.021794  0.451120 0.021794  P-0.4976803 0.0245360                                                                                                             
+ 5 9 3 53616.00 P  0.006157 0.021811  0.451845 0.021811  P-0.4971752 0.0245882                                                                                                             
+ 5 9 4 53617.00 P  0.007580 0.021828  0.452548 0.021828  P-0.4968823 0.0246403                                                                                                             
+ 5 9 5 53618.00 P  0.009004 0.021845  0.453230 0.021845  P-0.4967654 0.0246924                                                                                                             
+ 5 9 6 53619.00 P  0.010428 0.021862  0.453891 0.021862  P-0.4967675 0.0247445                                                                                                             
+ 5 9 7 53620.00 P  0.011853 0.021879  0.454530 0.021879  P-0.4967874 0.0247965                                                                                                             
+ 5 9 8 53621.00 P  0.013278 0.021896  0.455148 0.021896  P-0.4967251 0.0248485                                                                                                             
+ 5 9 9 53622.00 P  0.014703 0.021912  0.455744 0.021912  P-0.4965391 0.0249004                                                                                                             
+ 5 910 53623.00 P  0.016128 0.021929  0.456319 0.021929  P-0.4961973 0.0249523                                                                                                             
+ 5 911 53624.00 P  0.017552 0.021946  0.456871 0.021946  P-0.4956907 0.0250042                                                                                                             
+ 5 912 53625.00 P  0.018975 0.021963  0.457402 0.021963  P-0.4951242 0.0250560                                                                                                             
+ 5 913 53626.00 P  0.020397 0.021979  0.457911 0.021979  P-0.4945260 0.0251078                                                                                                             
+ 5 914 53627.00 P  0.021817 0.021996  0.458399 0.021996  P-0.4940029 0.0251596                                                                                                             
+ 5 915 53628.00                                                                                                                                                                            
+ 5 916 53629.00                                                                                                                                                                            
+ 5 917 53630.00                                                                                                                                                                            
+ 5 918 53631.00                                                                                                                                                                            
+ 5 919 53632.00                                                                                                                                                                            
+ 5 920 53633.00                                                                                                                                                                            
+ 5 921 53634.00                                                                                                                                                                            
+ 5 922 53635.00                                                                                                                                                                            
+ 5 923 53636.00                                                                                                                                                                            
+ 5 924 53637.00                                                                                                                                                                            
+ 5 925 53638.00                                                                                                                                                                            
+ 5 926 53639.00                                                                                                                                                                            
+ 5 927 53640.00                                                                                                                                                                            
+ 5 928 53641.00                                                                                                                                                                            
+ 5 929 53642.00                                                                                                                                                                            
+ 5 930 53643.00                                                                                                                                                                            
+ 510 1 53644.00                                                                                                                                                                            
+ 510 2 53645.00                                                                                                                                                                            
+ 510 3 53646.00                                                                                                                                                                            
+ 510 4 53647.00                                                                                                                                                                            
+ 510 5 53648.00                                                                                                                                                                            
+ 510 6 53649.00                                                                                                                                                                            
+ 510 7 53650.00                                                                                                                                                                            
+ 510 8 53651.00                                                                                                                                                                            
+ 510 9 53652.00                                                                                                                                                                            
+ 51010 53653.00                                                                                                                                                                            
+ 51011 53654.00                                                                                                                                                                            
+ 51012 53655.00                                                                                                                                                                            
+ 51013 53656.00                                                                                                                                                                            
+ 51014 53657.00                                                                                                                                                                            
+ 51015 53658.00                                                                                                                                                                            
+ 51016 53659.00                                                                                                                                                                            
+ 51017 53660.00                                                                                                                                                                            
+ 51018 53661.00                                                                                                                                                                            
+ 51019 53662.00                                                                                                                                                                            
+ 51020 53663.00                                                                                                                                                                            
+ 51021 53664.00                                                                                                                                                                            
+ 51022 53665.00                                                                                                                                                                            
+ 51023 53666.00                                                                                                                                                                            
+ 51024 53667.00                                                                                                                                                                            
+ 51025 53668.00                                                                                                                                                                            
+ 51026 53669.00                                                                                                                                                                            
+ 51027 53670.00                                                                                                                                                                            
+ 51028 53671.00                                                                                                                                                                            
+ 51029 53672.00                                                                                                                                                                            
+ 51030 53673.00                                                                                                                                                                            
+ 51031 53674.00                                                                                                                                                                            
+ 511 1 53675.00                                                                                                                                                                            
+ 511 2 53676.00                                                                                                                                                                            
+ 511 3 53677.00                                                                                                                                                                            
Index: /tags/sj_tags/sj_root_20080929/psLib/share/iers_corr.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/iers_corr.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/iers_corr.dat	(revision 22322)
@@ -0,0 +1,11658 @@
+#
+#  Stripped version of finals.all.orig file
+#
+#
+#           Bull A  Bull A  Bull B  Bull B
+#  MJD        dX      dY      dX      dY
+#
+41684.00    44.969  2.839   0.000   0.000
+41685.00    45.005  2.762   0.000   0.000
+41686.00    45.122  2.851   0.000   0.000
+41687.00    45.344  3.02    0.000   0.000
+41688.00    45.623  3.115   0.000   0.000
+41689.00    45.812  3.086   0.000   0.000
+41690.00    45.804  3.014   0.000   0.000
+41691.00    45.647  2.993   0.000   0.000
+41692.00    45.466  3.049   0.000   0.000
+41693.00    45.326  3.164   0.000   0.000
+41694.00    45.177  3.328   0.000   0.000
+41695.00    44.931  3.504   0.000   0.000
+41696.00    44.607  3.585   0.000   0.000
+41697.00    44.381  3.456   0.000   0.000
+41698.00    44.444  3.159   0.000   0.000
+41699.00    44.789  2.911   0.000   0.000
+41700.00    45.213  2.902   0.000   0.000
+41701.00    45.571  3.082   0.000   0.000
+41702.00    45.88   3.232   0.000   0.000
+41703.00    46.144  3.212   0.000   0.000
+41704.00    46.215  3.08    0.000   0.000
+41705.00    45.986  2.953   0.000   0.000
+41706.00    45.602  2.872   0.000   0.000
+41707.00    45.321  2.83    0.000   0.000
+41708.00    45.204  2.845   0.000   0.000
+41709.00    45.127  2.911   0.000   0.000
+41710.00    45.037  2.932   0.000   0.000
+41711.00    45.034  2.807   0.000   0.000
+41712.00    45.159  2.575   0.000   0.000
+41713.00    45.302  2.396   0.000   0.000
+41714.00    45.368  2.374   0.000   0.000
+41715.00    45.424  2.457   0.000   0.000
+41716.00    45.573  2.528   0.000   0.000
+41717.00    45.777  2.546   0.000   0.000
+41718.00    45.873  2.551   0.000   0.000
+41719.00    45.754  2.591   0.000   0.000
+41720.00    45.463  2.668   0.000   0.000
+41721.00    45.133  2.756   0.000   0.000
+41722.00    44.868  2.829   0.000   0.000
+41723.00    44.688  2.858   0.000   0.000
+41724.00    44.561  2.796   0.000   0.000
+41725.00    44.488  2.592   0.000   0.000
+41726.00    44.523  2.256   0.000   0.000
+41727.00    44.706  1.91    0.000   0.000
+41728.00    45.014  1.74    0.000   0.000
+41729.00    45.369  1.834   0.000   0.000
+41730.00    45.69   2.09    0.000   0.000
+41731.00    45.896  2.292   0.000   0.000
+41732.00    45.915  2.292   0.000   0.000
+41733.00    45.743  2.108   0.000   0.000
+41734.00    45.478  1.871   0.000   0.000
+41735.00    45.236  1.717   0.000   0.000
+41736.00    45.062  1.699   0.000   0.000
+41737.00    44.96   1.744   0.000   0.000
+41738.00    44.978  1.702   0.000   0.000
+41739.00    45.179  1.485   0.000   0.000
+41740.00    45.518  1.18    0.000   0.000
+41741.00    45.826  0.966   0.000   0.000
+41742.00    45.957  0.925   0.000   0.000
+41743.00    45.948  0.981   0.000   0.000
+41744.00    45.972  1.036   0.000   0.000
+41745.00    46.134  1.098   0.000   0.000
+41746.00    46.333  1.23    0.000   0.000
+41747.00    46.349  1.411   0.000   0.000
+41748.00    46.073  1.53    0.000   0.000
+41749.00    45.63   1.491   0.000   0.000
+41750.00    45.282  1.305   0.000   0.000
+41751.00    45.2    1.053   0.000   0.000
+41752.00    45.348  0.809   0.000   0.000
+41753.00    45.567  0.583   0.000   0.000
+41754.00    45.749  0.342   0.000   0.000
+41755.00    45.908  0.085   0.000   0.000
+41756.00    46.111  -0.101  0.000   0.000
+41757.00    46.359  -0.097  0.000   0.000
+41758.00    46.565  0.113   0.000   0.000
+41759.00    46.646  0.377   0.000   0.000
+41760.00    46.625  0.491   0.000   0.000
+41761.00    46.599  0.371   0.000   0.000
+41762.00    46.602  0.123   0.000   0.000
+41763.00    46.573  -0.074  0.000   0.000
+41764.00    46.479  -0.128  0.000   0.000
+41765.00    46.407  -0.113  0.000   0.000
+41766.00    46.482  -0.191  0.000   0.000
+41767.00    46.738  -0.435  0.000   0.000
+41768.00    47.096  -0.719  0.000   0.000
+41769.00    47.439  -0.841  0.000   0.000
+41770.00    47.66   -0.755  0.000   0.000
+41771.00    47.708  -0.619  0.000   0.000
+41772.00    47.656  -0.586  0.000   0.000
+41773.00    47.657  -0.594  0.000   0.000
+41774.00    47.755  -0.472  0.000   0.000
+41775.00    47.777  -0.208  0.000   0.000
+41776.00    47.525  -0.014  0.000   0.000
+41777.00    47.067  -0.098  0.000   0.000
+41778.00    46.721  -0.445  0.000   0.000
+41779.00    46.735  -0.857  0.000   0.000
+41780.00    47.048  -1.157  0.000   0.000
+41781.00    47.407  -1.31   0.000   0.000
+41782.00    47.621  -1.381  0.000   0.000
+41783.00    47.697  -1.438  0.000   0.000
+41784.00    47.76   -1.487  0.000   0.000
+41785.00    47.893  -1.478  0.000   0.000
+41786.00    48.042  -1.369  0.000   0.000
+41787.00    48.11   -1.192  0.000   0.000
+41788.00    48.103  -1.063  0.000   0.000
+41789.00    48.13   -1.09   0.000   0.000
+41790.00    48.23   -1.274  0.000   0.000
+41791.00    48.307  -1.497  0.000   0.000
+41792.00    48.285  -1.633  0.000   0.000
+41793.00    48.252  -1.673  0.000   0.000
+41794.00    48.328  -1.729  0.000   0.000
+41795.00    48.496  -1.89   0.000   0.000
+41796.00    48.672  -2.069  0.000   0.000
+41797.00    48.855  -2.076  0.000   0.000
+41798.00    49.072  -1.865  0.000   0.000
+41799.00    49.227  -1.639  0.000   0.000
+41800.00    49.192  -1.624  0.000   0.000
+41801.00    49.002  -1.766  0.000   0.000
+41802.00    48.804  -1.785  0.000   0.000
+41803.00    48.609  -1.553  0.000   0.000
+41804.00    48.303  -1.283  0.000   0.000
+41805.00    47.912  -1.272  0.000   0.000
+41806.00    47.676  -1.556  0.000   0.000
+41807.00    47.783  -1.901  0.000   0.000
+41808.00    48.144  -2.105  0.000   0.000
+41809.00    48.506  -2.159  0.000   0.000
+41810.00    48.682  -2.135  0.000   0.000
+41811.00    48.646  -2.059  0.000   0.000
+41812.00    48.495  -1.934  0.000   0.000
+41813.00    48.38   -1.805  0.000   0.000
+41814.00    48.391  -1.724  0.000   0.000
+41815.00    48.47   -1.681  0.000   0.000
+41816.00    48.493  -1.642  0.000   0.000
+41817.00    48.429  -1.642  0.000   0.000
+41818.00    48.338  -1.764  0.000   0.000
+41819.00    48.257  -2.005  0.000   0.000
+41820.00    48.19   -2.232  0.000   0.000
+41821.00    48.184  -2.313  0.000   0.000
+41822.00    48.271  -2.27   0.000   0.000
+41823.00    48.355  -2.237  0.000   0.000
+41824.00    48.321  -2.263  0.000   0.000
+41825.00    48.237  -2.235  0.000   0.000
+41826.00    48.279  -2.059  0.000   0.000
+41827.00    48.438  -1.855  0.000   0.000
+41828.00    48.477  -1.837  0.000   0.000
+41829.00    48.233  -2.004  0.000   0.000
+41830.00    47.789  -2.097  0.000   0.000
+41831.00    47.316  -1.928  0.000   0.000
+41832.00    46.906  -1.643  0.000   0.000
+41833.00    46.634  -1.536  0.000   0.000
+41834.00    46.601  -1.673  0.000   0.000
+41835.00    46.812  -1.851  0.000   0.000
+41836.00    47.124  -1.909  0.000   0.000
+41837.00    47.384  -1.899  0.000   0.000
+41838.00    47.513  -1.906  0.000   0.000
+41839.00    47.43   -1.863  0.000   0.000
+41840.00    47.075  -1.675  0.000   0.000
+41841.00    46.583  -1.423  0.000   0.000
+41842.00    46.25   -1.296  0.000   0.000
+41843.00    46.209  -1.348  0.000   0.000
+41844.00    46.255  -1.447  0.000   0.000
+41845.00    46.116  -1.494  0.000   0.000
+41846.00    45.778  -1.564  0.000   0.000
+41847.00    45.432  -1.753  0.000   0.000
+41848.00    45.234  -1.978  0.000   0.000
+41849.00    45.208  -2.043  0.000   0.000
+41850.00    45.288  -1.886  0.000   0.000
+41851.00    45.332  -1.668  0.000   0.000
+41852.00    45.194  -1.573  0.000   0.000
+41853.00    44.903  -1.6    0.000   0.000
+41854.00    44.673  -1.617  0.000   0.000
+41855.00    44.646  -1.579  0.000   0.000
+41856.00    44.685  -1.573  0.000   0.000
+41857.00    44.53   -1.64   0.000   0.000
+41858.00    44.084  -1.662  0.000   0.000
+41859.00    43.488  -1.528  0.000   0.000
+41860.00    42.99   -1.336  0.000   0.000
+41861.00    42.793  -1.289  0.000   0.000
+41862.00    42.935  -1.412  0.000   0.000
+41863.00    43.238  -1.515  0.000   0.000
+41864.00    43.467  -1.477  0.000   0.000
+41865.00    43.568  -1.406  0.000   0.000
+41866.00    43.619  -1.435  0.000   0.000
+41867.00    43.555  -1.481  0.000   0.000
+41868.00    43.148  -1.365  0.000   0.000
+41869.00    42.41   -1.11   0.000   0.000
+41870.00    41.75   -0.951  0.000   0.000
+41871.00    41.54   -1.025  0.000   0.000
+41872.00    41.654  -1.203  0.000   0.000
+41873.00    41.661  -1.301  0.000   0.000
+41874.00    41.377  -1.323  0.000   0.000
+41875.00    41.003  -1.397  0.000   0.000
+41876.00    40.774  -1.523  0.000   0.000
+41877.00    40.719  -1.543  0.000   0.000
+41878.00    40.75   -1.368  0.000   0.000
+41879.00    40.772  -1.121  0.000   0.000
+41880.00    40.683  -1.011  0.000   0.000
+41881.00    40.421  -1.103  0.000   0.000
+41882.00    40.073  -1.28   0.000   0.000
+41883.00    39.825  -1.4    0.000   0.000
+41884.00    39.743  -1.425  0.000   0.000
+41885.00    39.684  -1.388  0.000   0.000
+41886.00    39.458  -1.31   0.000   0.000
+41887.00    39.044  -1.212  0.000   0.000
+41888.00    38.635  -1.178  0.000   0.000
+41889.00    38.475  -1.3    0.000   0.000
+41890.00    38.632  -1.529  0.000   0.000
+41891.00    38.911  -1.67   0.000   0.000
+41892.00    39.057  -1.601  0.000   0.000
+41893.00    39.048  -1.422  0.000   0.000
+41894.00    39.042  -1.32   0.000   0.000
+41895.00    39.03   -1.312  0.000   0.000
+41896.00    38.75   -1.259  0.000   0.000
+41897.00    38.079  -1.104  0.000   0.000
+41898.00    37.356  -0.98   0.000   0.000
+41899.00    37.053  -1.016  0.000   0.000
+41900.00    37.223  -1.147  0.000   0.000
+41901.00    37.493  -1.221  0.000   0.000
+41902.00    37.566  -1.207  0.000   0.000
+41903.00    37.487  -1.2    0.000   0.000
+41904.00    37.412  -1.241  0.000   0.000
+41905.00    37.363  -1.248  0.000   0.000
+41906.00    37.3    -1.156  0.000   0.000
+41907.00    37.253  -1.025  0.000   0.000
+41908.00    37.235  -0.981  0.000   0.000
+41909.00    37.148  -1.064  0.000   0.000
+41910.00    36.897  -1.202  0.000   0.000
+41911.00    36.55   -1.292  0.000   0.000
+41912.00    36.278  -1.285  0.000   0.000
+41913.00    36.167  -1.206  0.000   0.000
+41914.00    36.149  -1.113  0.000   0.000
+41915.00    36.119  -1.077  0.000   0.000
+41916.00    36.066  -1.158  0.000   0.000
+41917.00    36.072  -1.377  0.000   0.000
+41918.00    36.178  -1.657  0.000   0.000
+41919.00    36.302  -1.839  0.000   0.000
+41920.00    36.331  -1.793  0.000   0.000
+41921.00    36.274  -1.543  0.000   0.000
+41922.00    36.251  -1.253  0.000   0.000
+41923.00    36.267  -1.065  0.000   0.000
+41924.00    36.134  -0.987  0.000   0.000
+41925.00    35.708  -0.958  0.000   0.000
+41926.00    35.162  -0.955  0.000   0.000
+41927.00    34.859  -0.99   0.000   0.000
+41928.00    34.981  -1.038  0.000   0.000
+41929.00    35.383  -1.053  0.000   0.000
+41930.00    35.812  -1.041  0.000   0.000
+41931.00    36.129  -1.055  0.000   0.000
+41932.00    36.294  -1.103  0.000   0.000
+41933.00    36.287  -1.135  0.000   0.000
+41934.00    36.14   -1.123  0.000   0.000
+41935.00    35.984  -1.115  0.000   0.000
+41936.00    35.929  -1.153  0.000   0.000
+41937.00    35.93   -1.203  0.000   0.000
+41938.00    35.836  -1.194  0.000   0.000
+41939.00    35.575  -1.11   0.000   0.000
+41940.00    35.255  -0.996  0.000   0.000
+41941.00    35.063  -0.901  0.000   0.000
+41942.00    35.106  -0.851  0.000   0.000
+41943.00    35.349  -0.862  0.000   0.000
+41944.00    35.671  -0.95   0.000   0.000
+41945.00    35.952  -1.113  0.000   0.000
+41946.00    36.134  -1.315  0.000   0.000
+41947.00    36.213  -1.471  0.000   0.000
+41948.00    36.226  -1.476  0.000   0.000
+41949.00    36.225  -1.273  0.000   0.000
+41950.00    36.241  -0.933  0.000   0.000
+41951.00    36.243  -0.625  0.000   0.000
+41952.00    36.159  -0.491  0.000   0.000
+41953.00    35.95   -0.542  0.000   0.000
+41954.00    35.683  -0.681  0.000   0.000
+41955.00    35.517  -0.796  0.000   0.000
+41956.00    35.609  -0.833  0.000   0.000
+41957.00    36.017  -0.814  0.000   0.000
+41958.00    36.629  -0.805  0.000   0.000
+41959.00    37.213  -0.849  0.000   0.000
+41960.00    37.556  -0.911  0.000   0.000
+41961.00    37.6    -0.92   0.000   0.000
+41962.00    37.446  -0.881  0.000   0.000
+41963.00    37.245  -0.885  0.000   0.000
+41964.00    37.102  -0.977  0.000   0.000
+41965.00    37.042  -1.063  0.000   0.000
+41966.00    37.031  -1.003  0.000   0.000
+41967.00    37.007  -0.791  0.000   0.000
+41968.00    36.954  -0.561  0.000   0.000
+41969.00    36.937  -0.429  0.000   0.000
+41970.00    37.043  -0.391  0.000   0.000
+41971.00    37.283  -0.383  0.000   0.000
+41972.00    37.574  -0.385  0.000   0.000
+41973.00    37.828  -0.427  0.000   0.000
+41974.00    38.023  -0.522  0.000   0.000
+41975.00    38.185  -0.62   0.000   0.000
+41976.00    38.342  -0.634  0.000   0.000
+41977.00    38.486  -0.494  0.000   0.000
+41978.00    38.559  -0.216  0.000   0.000
+41979.00    38.511  0.08    0.000   0.000
+41980.00    38.369  0.243   0.000   0.000
+41981.00    38.235  0.206   0.000   0.000
+41982.00    38.179  0.031   0.000   0.000
+41983.00    38.212  -0.141  0.000   0.000
+41984.00    38.373  -0.207  0.000   0.000
+41985.00    38.756  -0.167  0.000   0.000
+41986.00    39.353  -0.103  0.000   0.000
+41987.00    39.94   -0.083  0.000   0.000
+41988.00    40.252  -0.079  0.000   0.000
+41989.00    40.248  -0.008  0.000   0.000
+41990.00    40.092  0.127   0.000   0.000
+41991.00    39.917  0.188   0.000   0.000
+41992.00    39.731  0.072   0.000   0.000
+41993.00    39.552  -0.116  0.000   0.000
+41994.00    39.482  -0.147  0.000   0.000
+41995.00    39.596  0.06    0.000   0.000
+41996.00    39.861  0.336   0.000   0.000
+41997.00    40.194  0.487   0.000   0.000
+41998.00    40.509  0.512   0.000   0.000
+41999.00    40.699  0.533   0.000   0.000
+42000.00    40.689  0.589   0.000   0.000
+42001.00    40.555  0.593   0.000   0.000
+42002.00    40.495  0.499   0.000   0.000
+42003.00    40.625  0.398   0.000   0.000
+42004.00    40.87   0.418   0.000   0.000
+42005.00    41.069  0.588   0.000   0.000
+42006.00    41.114  0.828   0.000   0.000
+42007.00    40.995  1.04    0.000   0.000
+42008.00    40.787  1.161   0.000   0.000
+42009.00    40.62   1.162   0.000   0.000
+42010.00    40.598  1.053   0.000   0.000
+42011.00    40.715  0.905   0.000   0.000
+42012.00    40.934  0.829   0.000   0.000
+42013.00    41.279  0.889   0.000   0.000
+42014.00    41.744  1.032   0.000   0.000
+42015.00    42.157  1.153   0.000   0.000
+42016.00    42.298  1.218   0.000   0.000
+42017.00    42.167  1.301   0.000   0.000
+42018.00    41.979  1.442   0.000   0.000
+42019.00    41.86   1.532   0.000   0.000
+42020.00    41.731  1.43    0.000   0.000
+42021.00    41.515  1.201   0.000   0.000
+42022.00    41.311  1.094   0.000   0.000
+42023.00    41.281  1.245   0.000   0.000
+42024.00    41.489  1.491   0.000   0.000
+42025.00    41.9    1.585   0.000   0.000
+42026.00    42.391  1.508   0.000   0.000
+42027.00    42.74   1.444   0.000   0.000
+42028.00    42.751  1.474   0.000   0.000
+42029.00    42.499  1.469   0.000   0.000
+42030.00    42.301  1.33    0.000   0.000
+42031.00    42.358  1.184   0.000   0.000
+42032.00    42.537  1.228   0.000   0.000
+42033.00    42.603  1.462   0.000   0.000
+42034.00    42.509  1.696   0.000   0.000
+42035.00    42.35   1.793   0.000   0.000
+42036.00    42.178  1.784   0.000   0.000
+42037.00    42.000  1.749   0.000   0.000
+42038.00    41.879  1.69    0.000   0.000
+42039.00    41.897  1.591   0.000   0.000
+42040.00    42.071  1.522   0.000   0.000
+42041.00    42.365  1.587   0.000   0.000
+42042.00    42.717  1.779   0.000   0.000
+42043.00    42.993  1.955   0.000   0.000
+42044.00    43.04   2.006   0.000   0.000
+42045.00    42.865  1.983   0.000   0.000
+42046.00    42.663  2.001   0.000   0.000
+42047.00    42.577  2.053   0.000   0.000
+42048.00    42.536  2.028   0.000   0.000
+42049.00    42.386  1.917   0.000   0.000
+42050.00    42.104  1.876   0.000   0.000
+42051.00    41.796  2.011   0.000   0.000
+42052.00    41.601  2.176   0.000   0.000
+42053.00    41.651  2.135   0.000   0.000
+42054.00    41.997  1.89    0.000   0.000
+42055.00    42.478  1.691   0.000   0.000
+42056.00    42.808  1.697   0.000   0.000
+42057.00    42.893  1.772   0.000   0.000
+42058.00    42.921  1.726   0.000   0.000
+42059.00    43.036  1.61    0.000   0.000
+42060.00    43.106  1.629   0.000   0.000
+42061.00    42.96   1.814   0.000   0.000
+42062.00    42.702  1.961   0.000   0.000
+42063.00    42.551  1.913   0.000   0.000
+42064.00    42.501  1.745   0.000   0.000
+42065.00    42.372  1.61    0.000   0.000
+42066.00    42.14   1.53    0.000   0.000
+42067.00    42.000  1.434   0.000   0.000
+42068.00    42.095  1.347   0.000   0.000
+42069.00    42.37   1.385   0.000   0.000
+42070.00    42.684  1.572   0.000   0.000
+42071.00    42.926  1.757   0.000   0.000
+42072.00    43.013  1.775   0.000   0.000
+42073.00    42.924  1.642   0.000   0.000
+42074.00    42.756  1.513   0.000   0.000
+42075.00    42.646  1.491   0.000   0.000
+42076.00    42.608  1.536   0.000   0.000
+42077.00    42.526  1.583   0.000   0.000
+42078.00    42.286  1.647   0.000   0.000
+42079.00    41.889  1.741   0.000   0.000
+42080.00    41.451  1.752   0.000   0.000
+42081.00    41.179  1.531   0.000   0.000
+42082.00    41.248  1.134   0.000   0.000
+42083.00    41.62   0.845   0.000   0.000
+42084.00    42.053  0.877   0.000   0.000
+42085.00    42.366  1.122   0.000   0.000
+42086.00    42.585  1.301   0.000   0.000
+42087.00    42.752  1.300   0.000   0.000
+42088.00    42.759  1.237   0.000   0.000
+42089.00    42.532  1.215   0.000   0.000
+42090.00    42.258  1.163   0.000   0.000
+42091.00    42.183  0.995   0.000   0.000
+42092.00    42.254  0.768   0.000   0.000
+42093.00    42.2    0.601   0.000   0.000
+42094.00    41.966  0.495   0.000   0.000
+42095.00    41.819  0.369   0.000   0.000
+42096.00    41.977  0.235   0.000   0.000
+42097.00    42.344  0.216   0.000   0.000
+42098.00    42.693  0.359   0.000   0.000
+42099.00    42.928  0.533   0.000   0.000
+42100.00    43.065  0.565   0.000   0.000
+42101.00    43.098  0.431   0.000   0.000
+42102.00    43.02   0.263   0.000   0.000
+42103.00    42.899  0.188   0.000   0.000
+42104.00    42.814  0.218   0.000   0.000
+42105.00    42.76   0.287   0.000   0.000
+42106.00    42.654  0.333   0.000   0.000
+42107.00    42.447  0.309   0.000   0.000
+42108.00    42.193  0.15    0.000   0.000
+42109.00    42.03   -0.189  0.000   0.000
+42110.00    42.079  -0.62   0.000   0.000
+42111.00    42.323  -0.906  0.000   0.000
+42112.00    42.607  -0.845  0.000   0.000
+42113.00    42.798  -0.495  0.000   0.000
+42114.00    42.895  -0.139  0.000   0.000
+42115.00    42.943  -0.023  0.000   0.000
+42116.00    42.915  -0.159  0.000   0.000
+42117.00    42.797  -0.399  0.000   0.000
+42118.00    42.704  -0.623  0.000   0.000
+42119.00    42.75   -0.791  0.000   0.000
+42120.00    42.845  -0.893  0.000   0.000
+42121.00    42.795  -0.943  0.000   0.000
+42122.00    42.62   -1.011  0.000   0.000
+42123.00    42.601  -1.158  0.000   0.000
+42124.00    42.912  -1.331  0.000   0.000
+42125.00    43.394  -1.393  0.000   0.000
+42126.00    43.753  -1.298  0.000   0.000
+42127.00    43.896  -1.153  0.000   0.000
+42128.00    43.931  -1.099  0.000   0.000
+42129.00    43.944  -1.155  0.000   0.000
+42130.00    43.909  -1.236  0.000   0.000
+42131.00    43.802  -1.277  0.000   0.000
+42132.00    43.678  -1.29   0.000   0.000
+42133.00    43.611  -1.327  0.000   0.000
+42134.00    43.617  -1.415  0.000   0.000
+42135.00    43.662  -1.559  0.000   0.000
+42136.00    43.72   -1.767  0.000   0.000
+42137.00    43.804  -2.037  0.000   0.000
+42138.00    43.947  -2.323  0.000   0.000
+42139.00    44.14   -2.508  0.000   0.000
+42140.00    44.313  -2.462  0.000   0.000
+42141.00    44.379  -2.172  0.000   0.000
+42142.00    44.321  -1.813  0.000   0.000
+42143.00    44.201  -1.636  0.000   0.000
+42144.00    44.112  -1.758  0.000   0.000
+42145.00    44.124  -2.066  0.000   0.000
+42146.00    44.244  -2.336  0.000   0.000
+42147.00    44.388  -2.424  0.000   0.000
+42148.00    44.422  -2.349  0.000   0.000
+42149.00    44.295  -2.249  0.000   0.000
+42150.00    44.145  -2.268  0.000   0.000
+42151.00    44.197  -2.435  0.000   0.000
+42152.00    44.526  -2.632  0.000   0.000
+42153.00    44.958  -2.704  0.000   0.000
+42154.00    45.229  -2.623  0.000   0.000
+42155.00    45.224  -2.516  0.000   0.000
+42156.00    45.042  -2.495  0.000   0.000
+42157.00    44.843  -2.528  0.000   0.000
+42158.00    44.693  -2.507  0.000   0.000
+42159.00    44.557  -2.427  0.000   0.000
+42160.00    44.413  -2.4    0.000   0.000
+42161.00    44.33   -2.516  0.000   0.000
+42162.00    44.397  -2.735  0.000   0.000
+42163.00    44.612  -2.942  0.000   0.000
+42164.00    44.856  -3.06   0.000   0.000
+42165.00    45.006  -3.102  0.000   0.000
+42166.00    45.051  -3.122  0.000   0.000
+42167.00    45.085  -3.14   0.000   0.000
+42168.00    45.178  -3.11   0.000   0.000
+42169.00    45.285  -2.968  0.000   0.000
+42170.00    45.283  -2.742  0.000   0.000
+42171.00    45.127  -2.576  0.000   0.000
+42172.00    44.938  -2.611  0.000   0.000
+42173.00    44.9    -2.829  0.000   0.000
+42174.00    45.05   -3.057  0.000   0.000
+42175.00    45.225  -3.122  0.000   0.000
+42176.00    45.247  -3.008  0.000   0.000
+42177.00    45.125  -2.86   0.000   0.000
+42178.00    45.019  -2.846  0.000   0.000
+42179.00    45.046  -2.998  0.000   0.000
+42180.00    45.195  -3.182  0.000   0.000
+42181.00    45.385  -3.228  0.000   0.000
+42182.00    45.511  -3.119  0.000   0.000
+42183.00    45.452  -3.01   0.000   0.000
+42184.00    45.147  -3.032  0.000   0.000
+42185.00    44.695  -3.108  0.000   0.000
+42186.00    44.289  -3.055  0.000   0.000
+42187.00    44.03   -2.848  0.000   0.000
+42188.00    43.889  -2.686  0.000   0.000
+42189.00    43.849  -2.76   0.000   0.000
+42190.00    43.978  -3.022  0.000   0.000
+42191.00    44.281  -3.24   0.000   0.000
+42192.00    44.586  -3.251  0.000   0.000
+42193.00    44.673  -3.086  0.000   0.000
+42194.00    44.498  -2.882  0.000   0.000
+42195.00    44.226  -2.737  0.000   0.000
+42196.00    44.069  -2.659  0.000   0.000
+42197.00    44.081  -2.607  0.000   0.000
+42198.00    44.123  -2.544  0.000   0.000
+42199.00    44.013  -2.486  0.000   0.000
+42200.00    43.737  -2.491  0.000   0.000
+42201.00    43.476  -2.592  0.000   0.000
+42202.00    43.39   -2.748  0.000   0.000
+42203.00    43.438  -2.852  0.000   0.000
+42204.00    43.475  -2.829  0.000   0.000
+42205.00    43.459  -2.712  0.000   0.000
+42206.00    43.439  -2.624  0.000   0.000
+42207.00    43.4    -2.659  0.000   0.000
+42208.00    43.285  -2.77   0.000   0.000
+42209.00    43.153  -2.811  0.000   0.000
+42210.00    43.119  -2.716  0.000   0.000
+42211.00    43.109  -2.602  0.000   0.000
+42212.00    42.864  -2.623  0.000   0.000
+42213.00    42.283  -2.735  0.000   0.000
+42214.00    41.601  -2.723  0.000   0.000
+42215.00    41.133  -2.496  0.000   0.000
+42216.00    40.981  -2.251  0.000   0.000
+42217.00    41.067  -2.249  0.000   0.000
+42218.00    41.313  -2.484  0.000   0.000
+42219.00    41.637  -2.689  0.000   0.000
+42220.00    41.876  -2.659  0.000   0.000
+42221.00    41.859  -2.437  0.000   0.000
+42222.00    41.552  -2.187  0.000   0.000
+42223.00    41.071  -1.986  0.000   0.000
+42224.00    40.592  -1.838  0.000   0.000
+42225.00    40.266  -1.767  0.000   0.000
+42226.00    40.148  -1.819  0.000   0.000
+42227.00    40.115  -1.959  0.000   0.000
+42228.00    39.97   -2.094  0.000   0.000
+42229.00    39.657  -2.176  0.000   0.000
+42230.00    39.329  -2.247  0.000   0.000
+42231.00    39.149  -2.332  0.000   0.000
+42232.00    39.134  -2.365  0.000   0.000
+42233.00    39.202  -2.263  0.000   0.000
+42234.00    39.257  -2.069  0.000   0.000
+42235.00    39.185  -1.942  0.000   0.000
+42236.00    38.916  -1.978  0.000   0.000
+42237.00    38.579  -2.095  0.000   0.000
+42238.00    38.425  -2.147  0.000   0.000
+42239.00    38.479  -2.122  0.000   0.000
+42240.00    38.418  -2.139  0.000   0.000
+42241.00    37.945  -2.237  0.000   0.000
+42242.00    37.181  -2.28   0.000   0.000
+42243.00    36.551  -2.161  0.000   0.000
+42244.00    36.355  -2.000  0.000   0.000
+42245.00    36.571  -2.015  0.000   0.000
+42246.00    36.975  -2.212  0.000   0.000
+42247.00    37.31   -2.356  0.000   0.000
+42248.00    37.402  -2.274  0.000   0.000
+42249.00    37.243  -2.058  0.000   0.000
+42250.00    36.943  -1.879  0.000   0.000
+42251.00    36.55   -1.748  0.000   0.000
+42252.00    36.02   -1.579  0.000   0.000
+42253.00    35.445  -1.427  0.000   0.000
+42254.00    35.112  -1.465  0.000   0.000
+42255.00    35.18   -1.731  0.000   0.000
+42256.00    35.413  -2.03   0.000   0.000
+42257.00    35.435  -2.161  0.000   0.000
+42258.00    35.179  -2.138  0.000   0.000
+42259.00    34.905  -2.096  0.000   0.000
+42260.00    34.827  -2.062  0.000   0.000
+42261.00    34.913  -1.938  0.000   0.000
+42262.00    34.999  -1.7    0.000   0.000
+42263.00    34.949  -1.504  0.000   0.000
+42264.00    34.709  -1.521  0.000   0.000
+42265.00    34.374  -1.735  0.000   0.000
+42266.00    34.161  -1.957  0.000   0.000
+42267.00    34.188  -2.055  0.000   0.000
+42268.00    34.267  -2.07   0.000   0.000
+42269.00    34.08   -2.1    0.000   0.000
+42270.00    33.561  -2.146  0.000   0.000
+42271.00    33.005  -2.159  0.000   0.000
+42272.00    32.777  -2.181  0.000   0.000
+42273.00    32.986  -2.301  0.000   0.000
+42274.00    33.422  -2.475  0.000   0.000
+42275.00    33.721  -2.51   0.000   0.000
+42276.00    33.663  -2.309  0.000   0.000
+42277.00    33.364  -2.032  0.000   0.000
+42278.00    33.119  -1.89   0.000   0.000
+42279.00    32.997  -1.863  0.000   0.000
+42280.00    32.754  -1.769  0.000   0.000
+42281.00    32.259  -1.581  0.000   0.000
+42282.00    31.83   -1.506  0.000   0.000
+42283.00    31.882  -1.688  0.000   0.000
+42284.00    32.354  -1.987  0.000   0.000
+42285.00    32.762  -2.14   0.000   0.000
+42286.00    32.811  -2.079  0.000   0.000
+42287.00    32.675  -1.945  0.000   0.000
+42288.00    32.641  -1.847  0.000   0.000
+42289.00    32.732  -1.744  0.000   0.000
+42290.00    32.797  -1.59   0.000   0.000
+42291.00    32.753  -1.47   0.000   0.000
+42292.00    32.606  -1.516  0.000   0.000
+42293.00    32.381  -1.725  0.000   0.000
+42294.00    32.152  -1.944  0.000   0.000
+42295.00    32.043  -2.035  0.000   0.000
+42296.00    32.09   -2.002  0.000   0.000
+42297.00    32.16   -1.949  0.000   0.000
+42298.00    32.098  -1.957  0.000   0.000
+42299.00    31.934  -2.042  0.000   0.000
+42300.00    31.865  -2.204  0.000   0.000
+42301.00    32.036  -2.418  0.000   0.000
+42302.00    32.361  -2.583  0.000   0.000
+42303.00    32.561  -2.543  0.000   0.000
+42304.00    32.438  -2.256  0.000   0.000
+42305.00    32.118  -1.903  0.000   0.000
+42306.00    31.946  -1.726  0.000   0.000
+42307.00    32.054  -1.762  0.000   0.000
+42308.00    32.171  -1.814  0.000   0.000
+42309.00    32.000  -1.735  0.000   0.000
+42310.00    31.682  -1.606  0.000   0.000
+42311.00    31.644  -1.598  0.000   0.000
+42312.00    32.035  -1.718  0.000   0.000
+42313.00    32.555  -1.817  0.000   0.000
+42314.00    32.888  -1.805  0.000   0.000
+42315.00    33.047  -1.74   0.000   0.000
+42316.00    33.191  -1.701  0.000   0.000
+42317.00    33.322  -1.682  0.000   0.000
+42318.00    33.336  -1.655  0.000   0.000
+42319.00    33.242  -1.647  0.000   0.000
+42320.00    33.135  -1.698  0.000   0.000
+42321.00    33.024  -1.776  0.000   0.000
+42322.00    32.856  -1.794  0.000   0.000
+42323.00    32.676  -1.713  0.000   0.000
+42324.00    32.634  -1.58   0.000   0.000
+42325.00    32.805  -1.479  0.000   0.000
+42326.00    33.102  -1.466  0.000   0.000
+42327.00    33.393  -1.554  0.000   0.000
+42328.00    33.64   -1.722  0.000   0.000
+42329.00    33.881  -1.911  0.000   0.000
+42330.00    34.116  -2.027  0.000   0.000
+42331.00    34.245  -1.963  0.000   0.000
+42332.00    34.178  -1.693  0.000   0.000
+42333.00    33.988  -1.34   0.000   0.000
+42334.00    33.896  -1.11   0.000   0.000
+42335.00    34.031  -1.114  0.000   0.000
+42336.00    34.266  -1.259  0.000   0.000
+42337.00    34.37   -1.351  0.000   0.000
+42338.00    34.319  -1.298  0.000   0.000
+42339.00    34.331  -1.169  0.000   0.000
+42340.00    34.583  -1.071  0.000   0.000
+42341.00    35.022  -1.042  0.000   0.000
+42342.00    35.482  -1.066  0.000   0.000
+42343.00    35.866  -1.121  0.000   0.000
+42344.00    36.134  -1.181  0.000   0.000
+42345.00    36.232  -1.215  0.000   0.000
+42346.00    36.132  -1.223  0.000   0.000
+42347.00    35.93   -1.238  0.000   0.000
+42348.00    35.772  -1.259  0.000   0.000
+42349.00    35.697  -1.21   0.000   0.000
+42350.00    35.635  -1.031  0.000   0.000
+42351.00    35.555  -0.777  0.000   0.000
+42352.00    35.548  -0.58   0.000   0.000
+42353.00    35.724  -0.519  0.000   0.000
+42354.00    36.079  -0.567  0.000   0.000
+42355.00    36.484  -0.651  0.000   0.000
+42356.00    36.804  -0.726  0.000   0.000
+42357.00    36.995  -0.776  0.000   0.000
+42358.00    37.092  -0.78   0.000   0.000
+42359.00    37.136  -0.7    0.000   0.000
+42360.00    37.135  -0.509  0.000   0.000
+42361.00    37.085  -0.24   0.000   0.000
+42362.00    37.016  -0.005  0.000   0.000
+42363.00    36.982  0.067   0.000   0.000
+42364.00    37.025  -0.047  0.000   0.000
+42365.00    37.146  -0.221  0.000   0.000
+42366.00    37.319  -0.28   0.000   0.000
+42367.00    37.523  -0.16   0.000   0.000
+42368.00    37.778  0.047   0.000   0.000
+42369.00    38.116  0.197   0.000   0.000
+42370.00    38.519  0.215   0.000   0.000
+42371.00    38.874  0.129   0.000   0.000
+42372.00    39.038  0.037   0.000   0.000
+42373.00    38.959  0.013   0.000   0.000
+42374.00    38.721  0.042   0.000   0.000
+42375.00    38.457  0.046   0.000   0.000
+42376.00    38.251  0.007   0.000   0.000
+42377.00    38.129  0.026   0.000   0.000
+42378.00    38.095  0.205   0.000   0.000
+42379.00    38.147  0.477   0.000   0.000
+42380.00    38.287  0.641   0.000   0.000
+42381.00    38.522  0.578   0.000   0.000
+42382.00    38.831  0.381   0.000   0.000
+42383.00    39.12   0.242   0.000   0.000
+42384.00    39.263  0.251   0.000   0.000
+42385.00    39.218  0.355   0.000   0.000
+42386.00    39.079  0.466   0.000   0.000
+42387.00    38.979  0.56    0.000   0.000
+42388.00    38.961  0.669   0.000   0.000
+42389.00    38.957  0.815   0.000   0.000
+42390.00    38.867  0.961   0.000   0.000
+42391.00    38.676  1.027   0.000   0.000
+42392.00    38.495  0.963   0.000   0.000
+42393.00    38.486  0.819   0.000   0.000
+42394.00    38.704  0.724   0.000   0.000
+42395.00    39.036  0.776   0.000   0.000
+42396.00    39.343  0.954   0.000   0.000
+42397.00    39.606  1.142   0.000   0.000
+42398.00    39.857  1.228   0.000   0.000
+42399.00    40.025  1.194   0.000   0.000
+42400.00    39.984  1.121   0.000   0.000
+42401.00    39.751  1.100   0.000   0.000
+42402.00    39.503  1.135   0.000   0.000
+42403.00    39.346  1.128   0.000   0.000
+42404.00    39.21   1.013   0.000   0.000
+42405.00    39.019  0.889   0.000   0.000
+42406.00    38.851  0.924   0.000   0.000
+42407.00    38.83   1.117   0.000   0.000
+42408.00    38.977  1.24    0.000   0.000
+42409.00    39.237  1.096   0.000   0.000
+42410.00    39.556  0.777   0.000   0.000
+42411.00    39.851  0.568   0.000   0.000
+42412.00    39.981  0.627   0.000   0.000
+42413.00    39.881  0.847   0.000   0.000
+42414.00    39.658  1.037   0.000   0.000
+42415.00    39.483  1.129   0.000   0.000
+42416.00    39.405  1.174   0.000   0.000
+42417.00    39.348  1.204   0.000   0.000
+42418.00    39.226  1.192   0.000   0.000
+42419.00    39.023  1.112   0.000   0.000
+42420.00    38.799  0.992   0.000   0.000
+42421.00    38.672  0.884   0.000   0.000
+42422.00    38.736  0.825   0.000   0.000
+42423.00    38.961  0.838   0.000   0.000
+42424.00    39.219  0.932   0.000   0.000
+42425.00    39.431  1.082   0.000   0.000
+42426.00    39.588  1.21    0.000   0.000
+42427.00    39.642  1.239   0.000   0.000
+42428.00    39.522  1.167   0.000   0.000
+42429.00    39.297  1.079   0.000   0.000
+42430.00    39.166  1.035   0.000   0.000
+42431.00    39.193  0.979   0.000   0.000
+42432.00    39.185  0.819   0.000   0.000
+42433.00    38.957  0.601   0.000   0.000
+42434.00    38.595  0.511   0.000   0.000
+42435.00    38.326  0.626   0.000   0.000
+42436.00    38.256  0.752   0.000   0.000
+42437.00    38.354  0.637   0.000   0.000
+42438.00    38.593  0.314   0.000   0.000
+42439.00    38.925  0.093   0.000   0.000
+42440.00    39.206  0.188   0.000   0.000
+42441.00    39.306  0.475   0.000   0.000
+42442.00    39.26   0.689   0.000   0.000
+42443.00    39.181  0.734   0.000   0.000
+42444.00    39.094  0.697   0.000   0.000
+42445.00    38.964  0.628   0.000   0.000
+42446.00    38.826  0.468   0.000   0.000
+42447.00    38.736  0.203   0.000   0.000
+42448.00    38.642  -0.053  0.000   0.000
+42449.00    38.471  -0.185  0.000   0.000
+42450.00    38.296  -0.206  0.000   0.000
+42451.00    38.28   -0.197  0.000   0.000
+42452.00    38.47   -0.171  0.000   0.000
+42453.00    38.756  -0.075  0.000   0.000
+42454.00    39.002  0.075   0.000   0.000
+42455.00    39.124  0.152   0.000   0.000
+42456.00    39.092  0.059   0.000   0.000
+42457.00    38.986  -0.146  0.000   0.000
+42458.00    38.969  -0.328  0.000   0.000
+42459.00    39.099  -0.448  0.000   0.000
+42460.00    39.194  -0.588  0.000   0.000
+42461.00    39.041  -0.782  0.000   0.000
+42462.00    38.674  -0.915  0.000   0.000
+42463.00    38.323  -0.883  0.000   0.000
+42464.00    38.14   -0.796  0.000   0.000
+42465.00    38.127  -0.867  0.000   0.000
+42466.00    38.252  -1.11   0.000   0.000
+42467.00    38.485  -1.267  0.000   0.000
+42468.00    38.732  -1.13   0.000   0.000
+42469.00    38.893  -0.821  0.000   0.000
+42470.00    38.975  -0.635  0.000   0.000
+42471.00    39.018  -0.681  0.000   0.000
+42472.00    38.985  -0.828  0.000   0.000
+42473.00    38.853  -0.969  0.000   0.000
+42474.00    38.758  -1.159  0.000   0.000
+42475.00    38.824  -1.454  0.000   0.000
+42476.00    38.914  -1.745  0.000   0.000
+42477.00    38.778  -1.889  0.000   0.000
+42478.00    38.452  -1.896  0.000   0.000
+42479.00    38.286  -1.89   0.000   0.000
+42480.00    38.513  -1.901  0.000   0.000
+42481.00    38.986  -1.835  0.000   0.000
+42482.00    39.396  -1.665  0.000   0.000
+42483.00    39.591  -1.533  0.000   0.000
+42484.00    39.605  -1.605  0.000   0.000
+42485.00    39.548  -1.867  0.000   0.000
+42486.00    39.541  -2.147  0.000   0.000
+42487.00    39.65   -2.319  0.000   0.000
+42488.00    39.795  -2.415  0.000   0.000
+42489.00    39.814  -2.52   0.000   0.000
+42490.00    39.657  -2.629  0.000   0.000
+42491.00    39.464  -2.682  0.000   0.000
+42492.00    39.396  -2.701  0.000   0.000
+42493.00    39.488  -2.786  0.000   0.000
+42494.00    39.668  -2.936  0.000   0.000
+42495.00    39.827  -2.979  0.000   0.000
+42496.00    39.877  -2.78   0.000   0.000
+42497.00    39.812  -2.461  0.000   0.000
+42498.00    39.733  -2.302  0.000   0.000
+42499.00    39.715  -2.422  0.000   0.000
+42500.00    39.702  -2.665  0.000   0.000
+42501.00    39.636  -2.841  0.000   0.000
+42502.00    39.626  -2.938  0.000   0.000
+42503.00    39.785  -3.052  0.000   0.000
+42504.00    39.958  -3.19   0.000   0.000
+42505.00    39.852  -3.28   0.000   0.000
+42506.00    39.493  -3.32   0.000   0.000
+42507.00    39.294  -3.379  0.000   0.000
+42508.00    39.556  -3.449  0.000   0.000
+42509.00    40.091  -3.423  0.000   0.000
+42510.00    40.474  -3.259  0.000   0.000
+42511.00    40.524  -3.09   0.000   0.000
+42512.00    40.372  -3.094  0.000   0.000
+42513.00    40.203  -3.293  0.000   0.000
+42514.00    40.109  -3.548  0.000   0.000
+42515.00    40.136  -3.724  0.000   0.000
+42516.00    40.285  -3.809  0.000   0.000
+42517.00    40.46   -3.869  0.000   0.000
+42518.00    40.538  -3.941  0.000   0.000
+42519.00    40.515  -4.017  0.000   0.000
+42520.00    40.515  -4.089  0.000   0.000
+42521.00    40.648  -4.168  0.000   0.000
+42522.00    40.892  -4.224  0.000   0.000
+42523.00    41.099  -4.16   0.000   0.000
+42524.00    41.112  -3.916  0.000   0.000
+42525.00    40.908  -3.593  0.000   0.000
+42526.00    40.627  -3.417  0.000   0.000
+42527.00    40.429  -3.518  0.000   0.000
+42528.00    40.357  -3.783  0.000   0.000
+42529.00    40.374  -3.977  0.000   0.000
+42530.00    40.49   -3.987  0.000   0.000
+42531.00    40.706  -3.887  0.000   0.000
+42532.00    40.87   -3.81   0.000   0.000
+42533.00    40.772  -3.829  0.000   0.000
+42534.00    40.455  -3.952  0.000   0.000
+42535.00    40.256  -4.145  0.000   0.000
+42536.00    40.414  -4.312  0.000   0.000
+42537.00    40.754  -4.337  0.000   0.000
+42538.00    40.897  -4.2    0.000   0.000
+42539.00    40.691  -4.027  0.000   0.000
+42540.00    40.305  -3.963  0.000   0.000
+42541.00    39.96   -4.026  0.000   0.000
+42542.00    39.749  -4.136  0.000   0.000
+42543.00    39.691  -4.231  0.000   0.000
+42544.00    39.806  -4.32   0.000   0.000
+42545.00    40.054  -4.417  0.000   0.000
+42546.00    40.291  -4.493  0.000   0.000
+42547.00    40.377  -4.511  0.000   0.000
+42548.00    40.308  -4.47   0.000   0.000
+42549.00    40.209  -4.401  0.000   0.000
+42550.00    40.208  -4.319  0.000   0.000
+42551.00    40.312  -4.196  0.000   0.000
+42552.00    40.399  -3.995  0.000   0.000
+42553.00    40.331  -3.742  0.000   0.000
+42554.00    40.074  -3.556  0.000   0.000
+42555.00    39.733  -3.562  0.000   0.000
+42556.00    39.479  -3.748  0.000   0.000
+42557.00    39.434  -3.941  0.000   0.000
+42558.00    39.61   -3.962  0.000   0.000
+42559.00    39.883  -3.8    0.000   0.000
+42560.00    40.054  -3.609  0.000   0.000
+42561.00    39.99   -3.564  0.000   0.000
+42562.00    39.751  -3.721  0.000   0.000
+42563.00    39.531  -3.991  0.000   0.000
+42564.00    39.455  -4.207  0.000   0.000
+42565.00    39.452  -4.244  0.000   0.000
+42566.00    39.34   -4.116  0.000   0.000
+42567.00    39.017  -3.955  0.000   0.000
+42568.00    38.532  -3.872  0.000   0.000
+42569.00    38.028  -3.851  0.000   0.000
+42570.00    37.638  -3.82   0.000   0.000
+42571.00    37.447  -3.788  0.000   0.000
+42572.00    37.489  -3.842  0.000   0.000
+42573.00    37.726  -3.999  0.000   0.000
+42574.00    38.032  -4.133  0.000   0.000
+42575.00    38.207  -4.097  0.000   0.000
+42576.00    38.093  -3.87   0.000   0.000
+42577.00    37.703  -3.571  0.000   0.000
+42578.00    37.236  -3.338  0.000   0.000
+42579.00    36.936  -3.225  0.000   0.000
+42580.00    36.893  -3.183  0.000   0.000
+42581.00    36.96   -3.137  0.000   0.000
+42582.00    36.889  -3.073  0.000   0.000
+42583.00    36.559  -3.057  0.000   0.000
+42584.00    36.107  -3.149  0.000   0.000
+42585.00    35.815  -3.303  0.000   0.000
+42586.00    35.839  -3.385  0.000   0.000
+42587.00    36.067  -3.31   0.000   0.000
+42588.00    36.253  -3.142  0.000   0.000
+42589.00    36.257  -3.036  0.000   0.000
+42590.00    36.105  -3.092  0.000   0.000
+42591.00    35.867  -3.276  0.000   0.000
+42592.00    35.587  -3.452  0.000   0.000
+42593.00    35.326  -3.499  0.000   0.000
+42594.00    35.137  -3.419  0.000   0.000
+42595.00    34.943  -3.326  0.000   0.000
+42596.00    34.572  -3.321  0.000   0.000
+42597.00    33.978  -3.36   0.000   0.000
+42598.00    33.37   -3.33   0.000   0.000
+42599.00    33.03   -3.23   0.000   0.000
+42600.00    33.057  -3.206  0.000   0.000
+42601.00    33.337  -3.35   0.000   0.000
+42602.00    33.678  -3.532  0.000   0.000
+42603.00    33.892  -3.509  0.000   0.000
+42604.00    33.802  -3.201  0.000   0.000
+42605.00    33.336  -2.778  0.000   0.000
+42606.00    32.638  -2.481  0.000   0.000
+42607.00    32.01   -2.412  0.000   0.000
+42608.00    31.697  -2.514  0.000   0.000
+42609.00    31.705  -2.672  0.000   0.000
+42610.00    31.813  -2.809  0.000   0.000
+42611.00    31.758  -2.908  0.000   0.000
+42612.00    31.465  -2.988  0.000   0.000
+42613.00    31.118  -3.063  0.000   0.000
+42614.00    30.958  -3.114  0.000   0.000
+42615.00    31.029  -3.095  0.000   0.000
+42616.00    31.17   -2.985  0.000   0.000
+42617.00    31.223  -2.825  0.000   0.000
+42618.00    31.149  -2.704  0.000   0.000
+42619.00    30.942  -2.698  0.000   0.000
+42620.00    30.617  -2.799  0.000   0.000
+42621.00    30.316  -2.919  0.000   0.000
+42622.00    30.229  -2.988  0.000   0.000
+42623.00    30.303  -3.042  0.000   0.000
+42624.00    30.19   -3.161  0.000   0.000
+42625.00    29.655  -3.324  0.000   0.000
+42626.00    28.937  -3.394  0.000   0.000
+42627.00    28.525  -3.304  0.000   0.000
+42628.00    28.634  -3.183  0.000   0.000
+42629.00    29.054  -3.205  0.000   0.000
+42630.00    29.439  -3.327  0.000   0.000
+42631.00    29.583  -3.32   0.000   0.000
+42632.00    29.436  -3.059  0.000   0.000
+42633.00    29.046  -2.692  0.000   0.000
+42634.00    28.527  -2.455  0.000   0.000
+42635.00    28.016  -2.414  0.000   0.000
+42636.00    27.617  -2.481  0.000   0.000
+42637.00    27.419  -2.591  0.000   0.000
+42638.00    27.478  -2.759  0.000   0.000
+42639.00    27.724  -2.968  0.000   0.000
+42640.00    27.938  -3.119  0.000   0.000
+42641.00    27.962  -3.138  0.000   0.000
+42642.00    27.861  -3.062  0.000   0.000
+42643.00    27.81   -2.969  0.000   0.000
+42644.00    27.857  -2.863  0.000   0.000
+42645.00    27.903  -2.693  0.000   0.000
+42646.00    27.854  -2.477  0.000   0.000
+42647.00    27.677  -2.344  0.000   0.000
+42648.00    27.403  -2.405  0.000   0.000
+42649.00    27.172  -2.623  0.000   0.000
+42650.00    27.183  -2.857  0.000   0.000
+42651.00    27.423  -3.033  0.000   0.000
+42652.00    27.55   -3.201  0.000   0.000
+42653.00    27.248  -3.396  0.000   0.000
+42654.00    26.664  -3.527  0.000   0.000
+42655.00    26.303  -3.484  0.000   0.000
+42656.00    26.468  -3.325  0.000   0.000
+42657.00    26.965  -3.221  0.000   0.000
+42658.00    27.361  -3.219  0.000   0.000
+42659.00    27.395  -3.177  0.000   0.000
+42660.00    27.125  -2.992  0.000   0.000
+42661.00    26.806  -2.774  0.000   0.000
+42662.00    26.652  -2.685  0.000   0.000
+42663.00    26.621  -2.694  0.000   0.000
+42664.00    26.478  -2.635  0.000   0.000
+42665.00    26.144  -2.494  0.000   0.000
+42666.00    25.913  -2.458  0.000   0.000
+42667.00    26.119  -2.64   0.000   0.000
+42668.00    26.679  -2.891  0.000   0.000
+42669.00    27.169  -2.977  0.000   0.000
+42670.00    27.34   -2.86   0.000   0.000
+42671.00    27.333  -2.689  0.000   0.000
+42672.00    27.363  -2.566  0.000   0.000
+42673.00    27.426  -2.442  0.000   0.000
+42674.00    27.398  -2.269  0.000   0.000
+42675.00    27.245  -2.138  0.000   0.000
+42676.00    27.039  -2.181  0.000   0.000
+42677.00    26.888  -2.395  0.000   0.000
+42678.00    26.908  -2.632  0.000   0.000
+42679.00    27.127  -2.776  0.000   0.000
+42680.00    27.376  -2.859  0.000   0.000
+42681.00    27.412  -2.962  0.000   0.000
+42682.00    27.224  -3.069  0.000   0.000
+42683.00    27.096  -3.086  0.000   0.000
+42684.00    27.287  -2.99   0.000   0.000
+42685.00    27.73   -2.87   0.000   0.000
+42686.00    28.1    -2.778  0.000   0.000
+42687.00    28.134  -2.657  0.000   0.000
+42688.00    27.87   -2.469  0.000   0.000
+42689.00    27.631  -2.328  0.000   0.000
+42690.00    27.737  -2.357  0.000   0.000
+42691.00    28.141  -2.465  0.000   0.000
+42692.00    28.408  -2.407  0.000   0.000
+42693.00    28.222  -2.123  0.000   0.000
+42694.00    27.839  -1.865  0.000   0.000
+42695.00    27.822  -1.894  0.000   0.000
+42696.00    28.345  -2.158  0.000   0.000
+42697.00    29.005  -2.373  0.000   0.000
+42698.00    29.382  -2.375  0.000   0.000
+42699.00    29.494  -2.249  0.000   0.000
+42700.00    29.578  -2.132  0.000   0.000
+42701.00    29.679  -2.038  0.000   0.000
+42702.00    29.671  -1.92   0.000   0.000
+42703.00    29.538  -1.81   0.000   0.000
+42704.00    29.396  -1.786  0.000   0.000
+42705.00    29.319  -1.848  0.000   0.000
+42706.00    29.308  -1.903  0.000   0.000
+42707.00    29.393  -1.888  0.000   0.000
+42708.00    29.6    -1.846  0.000   0.000
+42709.00    29.863  -1.857  0.000   0.000
+42710.00    30.073  -1.929  0.000   0.000
+42711.00    30.229  -1.99   0.000   0.000
+42712.00    30.428  -1.982  0.000   0.000
+42713.00    30.714  -1.903  0.000   0.000
+42714.00    30.982  -1.764  0.000   0.000
+42715.00    31.066  -1.546  0.000   0.000
+42716.00    30.926  -1.273  0.000   0.000
+42717.00    30.755  -1.077  0.000   0.000
+42718.00    30.838  -1.097  0.000   0.000
+42719.00    31.237  -1.281  0.000   0.000
+42720.00    31.649  -1.381  0.000   0.000
+42721.00    31.733  -1.225  0.000   0.000
+42722.00    31.56   -0.937  0.000   0.000
+42723.00    31.549  -0.794  0.000   0.000
+42724.00    31.932  -0.909  0.000   0.000
+42725.00    32.475  -1.133  0.000   0.000
+42726.00    32.824  -1.274  0.000   0.000
+42727.00    32.93   -1.283  0.000   0.000
+42728.00    32.958  -1.22   0.000   0.000
+42729.00    32.967  -1.125  0.000   0.000
+42730.00    32.886  -1.006  0.000   0.000
+42731.00    32.735  -0.89   0.000   0.000
+42732.00    32.641  -0.802  0.000   0.000
+42733.00    32.635  -0.723  0.000   0.000
+42734.00    32.626  -0.618  0.000   0.000
+42735.00    32.596  -0.512  0.000   0.000
+42736.00    32.67   -0.476  0.000   0.000
+42737.00    32.934  -0.54   0.000   0.000
+42738.00    33.292  -0.65   0.000   0.000
+42739.00    33.569  -0.722  0.000   0.000
+42740.00    33.697  -0.709  0.000   0.000
+42741.00    33.739  -0.613  0.000   0.000
+42742.00    33.773  -0.438  0.000   0.000
+42743.00    33.791  -0.186  0.000   0.000
+42744.00    33.735  0.112   0.000   0.000
+42745.00    33.609  0.348   0.000   0.000
+42746.00    33.523  0.389   0.000   0.000
+42747.00    33.583  0.209   0.000   0.000
+42748.00    33.763  -0.037  0.000   0.000
+42749.00    33.946  -0.121  0.000   0.000
+42750.00    34.104  0.028   0.000   0.000
+42751.00    34.334  0.264   0.000   0.000
+42752.00    34.688  0.38    0.000   0.000
+42753.00    35.044  0.309   0.000   0.000
+42754.00    35.233  0.144   0.000   0.000
+42755.00    35.22   0.012   0.000   0.000
+42756.00    35.09   -0.023  0.000   0.000
+42757.00    34.915  0.034   0.000   0.000
+42758.00    34.726  0.128   0.000   0.000
+42759.00    34.587  0.196   0.000   0.000
+42760.00    34.567  0.232   0.000   0.000
+42761.00    34.63   0.288   0.000   0.000
+42762.00    34.658  0.392   0.000   0.000
+42763.00    34.618  0.466   0.000   0.000
+42764.00    34.63   0.399   0.000   0.000
+42765.00    34.823  0.196   0.000   0.000
+42766.00    35.16   0.004   0.000   0.000
+42767.00    35.443  -0.031  0.000   0.000
+42768.00    35.502  0.108   0.000   0.000
+42769.00    35.34   0.328   0.000   0.000
+42770.00    35.106  0.535   0.000   0.000
+42771.00    34.943  0.706   0.000   0.000
+42772.00    34.869  0.856   0.000   0.000
+42773.00    34.796  0.972   0.000   0.000
+42774.00    34.646  0.995   0.000   0.000
+42775.00    34.452  0.864   0.000   0.000
+42776.00    34.34   0.618   0.000   0.000
+42777.00    34.425  0.421   0.000   0.000
+42778.00    34.708  0.435   0.000   0.000
+42779.00    35.073  0.657   0.000   0.000
+42780.00    35.373  0.909   0.000   0.000
+42781.00    35.531  1.005   0.000   0.000
+42782.00    35.547  0.908   0.000   0.000
+42783.00    35.439  0.728   0.000   0.000
+42784.00    35.221  0.594   0.000   0.000
+42785.00    34.957  0.551   0.000   0.000
+42786.00    34.765  0.54    0.000   0.000
+42787.00    34.716  0.476   0.000   0.000
+42788.00    34.767  0.353   0.000   0.000
+42789.00    34.811  0.267   0.000   0.000
+42790.00    34.794  0.300   0.000   0.000
+42791.00    34.754  0.377   0.000   0.000
+42792.00    34.769  0.325   0.000   0.000
+42793.00    34.906  0.089   0.000   0.000
+42794.00    35.17   -0.155  0.000   0.000
+42795.00    35.454  -0.171  0.000   0.000
+42796.00    35.579  0.086   0.000   0.000
+42797.00    35.44   0.431   0.000   0.000
+42798.00    35.124  0.646   0.000   0.000
+42799.00    34.836  0.665   0.000   0.000
+42800.00    34.694  0.568   0.000   0.000
+42801.00    34.641  0.454   0.000   0.000
+42802.00    34.536  0.352   0.000   0.000
+42803.00    34.323  0.228   0.000   0.000
+42804.00    34.108  0.06    0.000   0.000
+42805.00    34.068  -0.096  0.000   0.000
+42806.00    34.263  -0.133  0.000   0.000
+42807.00    34.553  -0.01   0.000   0.000
+42808.00    34.735  0.192   0.000   0.000
+42809.00    34.75   0.323   0.000   0.000
+42810.00    34.685  0.294   0.000   0.000
+42811.00    34.593  0.135   0.000   0.000
+42812.00    34.446  -0.061  0.000   0.000
+42813.00    34.274  -0.229  0.000   0.000
+42814.00    34.208  -0.383  0.000   0.000
+42815.00    34.293  -0.59   0.000   0.000
+42816.00    34.38   -0.858  0.000   0.000
+42817.00    34.325  -1.072  0.000   0.000
+42818.00    34.19   -1.078  0.000   0.000
+42819.00    34.139  -0.896  0.000   0.000
+42820.00    34.206  -0.757  0.000   0.000
+42821.00    34.312  -0.86   0.000   0.000
+42822.00    34.448  -1.114  0.000   0.000
+42823.00    34.66   -1.217  0.000   0.000
+42824.00    34.884  -1.014  0.000   0.000
+42825.00    34.957  -0.678  0.000   0.000
+42826.00    34.822  -0.5    0.000   0.000
+42827.00    34.595  -0.593  0.000   0.000
+42828.00    34.424  -0.844  0.000   0.000
+42829.00    34.339  -1.102  0.000   0.000
+42830.00    34.283  -1.312  0.000   0.000
+42831.00    34.198  -1.485  0.000   0.000
+42832.00    34.084  -1.619  0.000   0.000
+42833.00    34.017  -1.698  0.000   0.000
+42834.00    34.082  -1.716  0.000   0.000
+42835.00    34.266  -1.682  0.000   0.000
+42836.00    34.445  -1.606  0.000   0.000
+42837.00    34.527  -1.516  0.000   0.000
+42838.00    34.541  -1.471  0.000   0.000
+42839.00    34.536  -1.539  0.000   0.000
+42840.00    34.508  -1.735  0.000   0.000
+42841.00    34.481  -2.002  0.000   0.000
+42842.00    34.561  -2.272  0.000   0.000
+42843.00    34.759  -2.538  0.000   0.000
+42844.00    34.88   -2.824  0.000   0.000
+42845.00    34.772  -3.058  0.000   0.000
+42846.00    34.585  -3.076  0.000   0.000
+42847.00    34.595  -2.822  0.000   0.000
+42848.00    34.818  -2.505  0.000   0.000
+42849.00    35.016  -2.426  0.000   0.000
+42850.00    35.059  -2.626  0.000   0.000
+42851.00    35.068  -2.83   0.000   0.000
+42852.00    35.167  -2.783  0.000   0.000
+42853.00    35.289  -2.563  0.000   0.000
+42854.00    35.306  -2.443  0.000   0.000
+42855.00    35.19   -2.552  0.000   0.000
+42856.00    35.006  -2.776  0.000   0.000
+42857.00    34.848  -2.98   0.000   0.000
+42858.00    34.805  -3.161  0.000   0.000
+42859.00    34.892  -3.358  0.000   0.000
+42860.00    34.984  -3.521  0.000   0.000
+42861.00    34.953  -3.577  0.000   0.000
+42862.00    34.861  -3.551  0.000   0.000
+42863.00    34.903  -3.524  0.000   0.000
+42864.00    35.128  -3.502  0.000   0.000
+42865.00    35.376  -3.418  0.000   0.000
+42866.00    35.485  -3.279  0.000   0.000
+42867.00    35.452  -3.219  0.000   0.000
+42868.00    35.37   -3.366  0.000   0.000
+42869.00    35.343  -3.687  0.000   0.000
+42870.00    35.454  -4.023  0.000   0.000
+42871.00    35.683  -4.271  0.000   0.000
+42872.00    35.842  -4.453  0.000   0.000
+42873.00    35.769  -4.601  0.000   0.000
+42874.00    35.592  -4.633  0.000   0.000
+42875.00    35.623  -4.456  0.000   0.000
+42876.00    35.957  -4.164  0.000   0.000
+42877.00    36.333  -3.998  0.000   0.000
+42878.00    36.47   -4.066  0.000   0.000
+42879.00    36.362  -4.2    0.000   0.000
+42880.00    36.184  -4.178  0.000   0.000
+42881.00    36.049  -4.022  0.000   0.000
+42882.00    35.947  -3.945  0.000   0.000
+42883.00    35.821  -4.045  0.000   0.000
+42884.00    35.634  -4.194  0.000   0.000
+42885.00    35.448  -4.264  0.000   0.000
+42886.00    35.437  -4.314  0.000   0.000
+42887.00    35.686  -4.457  0.000   0.000
+42888.00    35.98   -4.652  0.000   0.000
+42889.00    35.988  -4.762  0.000   0.000
+42890.00    35.7    -4.754  0.000   0.000
+42891.00    35.482  -4.722  0.000   0.000
+42892.00    35.596  -4.7    0.000   0.000
+42893.00    35.865  -4.608  0.000   0.000
+42894.00    35.938  -4.418  0.000   0.000
+42895.00    35.729  -4.275  0.000   0.000
+42896.00    35.441  -4.356  0.000   0.000
+42897.00    35.289  -4.659  0.000   0.000
+42898.00    35.342  -4.994  0.000   0.000
+42899.00    35.544  -5.191  0.000   0.000
+42900.00    35.744  -5.254  0.000   0.000
+42901.00    35.771  -5.278  0.000   0.000
+42902.00    35.618  -5.294  0.000   0.000
+42903.00    35.509  -5.242  0.000   0.000
+42904.00    35.66   -5.106  0.000   0.000
+42905.00    36.018  -4.963  0.000   0.000
+42906.00    36.316  -4.873  0.000   0.000
+42907.00    36.34   -4.773  0.000   0.000
+42908.00    36.081  -4.577  0.000   0.000
+42909.00    35.686  -4.353  0.000   0.000
+42910.00    35.326  -4.278  0.000   0.000
+42911.00    35.081  -4.407  0.000   0.000
+42912.00    34.92   -4.573  0.000   0.000
+42913.00    34.829  -4.601  0.000   0.000
+42914.00    34.921  -4.531  0.000   0.000
+42915.00    35.272  -4.538  0.000   0.000
+42916.00    35.653  -4.68   0.000   0.000
+42917.00    35.662  -4.847  0.000   0.000
+42918.00    35.218  -4.94   0.000   0.000
+42919.00    34.722  -4.973  0.000   0.000
+42920.00    34.569  -4.973  0.000   0.000
+42921.00    34.662  -4.898  0.000   0.000
+42922.00    34.604  -4.727  0.000   0.000
+42923.00    34.243  -4.571  0.000   0.000
+42924.00    33.798  -4.578  0.000   0.000
+42925.00    33.522  -4.764  0.000   0.000
+42926.00    33.468  -4.989  0.000   0.000
+42927.00    33.572  -5.113  0.000   0.000
+42928.00    33.756  -5.119  0.000   0.000
+42929.00    33.886  -5.076  0.000   0.000
+42930.00    33.816  -5.034  0.000   0.000
+42931.00    33.554  -4.99   0.000   0.000
+42932.00    33.306  -4.919  0.000   0.000
+42933.00    33.274  -4.811  0.000   0.000
+42934.00    33.439  -4.65   0.000   0.000
+42935.00    33.58   -4.407  0.000   0.000
+42936.00    33.474  -4.092  0.000   0.000
+42937.00    33.081  -3.817  0.000   0.000
+42938.00    32.557  -3.744  0.000   0.000
+42939.00    32.112  -3.916  0.000   0.000
+42940.00    31.856  -4.169  0.000   0.000
+42941.00    31.804  -4.279  0.000   0.000
+42942.00    31.968  -4.188  0.000   0.000
+42943.00    32.315  -4.048  0.000   0.000
+42944.00    32.631  -4.035  0.000   0.000
+42945.00    32.594  -4.168  0.000   0.000
+42946.00    32.118  -4.349  0.000   0.000
+42947.00    31.502  -4.488  0.000   0.000
+42948.00    31.108  -4.546  0.000   0.000
+42949.00    30.959  -4.508  0.000   0.000
+42950.00    30.791  -4.394  0.000   0.000
+42951.00    30.44   -4.282  0.000   0.000
+42952.00    30.009  -4.253  0.000   0.000
+42953.00    29.662  -4.305  0.000   0.000
+42954.00    29.449  -4.373  0.000   0.000
+42955.00    29.376  -4.417  0.000   0.000
+42956.00    29.465  -4.443  0.000   0.000
+42957.00    29.664  -4.447  0.000   0.000
+42958.00    29.768  -4.392  0.000   0.000
+42959.00    29.579  -4.262  0.000   0.000
+42960.00    29.122  -4.1    0.000   0.000
+42961.00    28.647  -3.961  0.000   0.000
+42962.00    28.398  -3.85   0.000   0.000
+42963.00    28.401  -3.727  0.000   0.000
+42964.00    28.46   -3.574  0.000   0.000
+42965.00    28.341  -3.441  0.000   0.000
+42966.00    27.962  -3.432  0.000   0.000
+42967.00    27.439  -3.606  0.000   0.000
+42968.00    26.996  -3.885  0.000   0.000
+42969.00    26.81   -4.08   0.000   0.000
+42970.00    26.92   -4.053  0.000   0.000
+42971.00    27.202  -3.857  0.000   0.000
+42972.00    27.424  -3.682  0.000   0.000
+42973.00    27.379  -3.672  0.000   0.000
+42974.00    27.028  -3.813  0.000   0.000
+42975.00    26.525  -3.987  0.000   0.000
+42976.00    26.082  -4.096  0.000   0.000
+42977.00    25.8    -4.114  0.000   0.000
+42978.00    25.637  -4.084  0.000   0.000
+42979.00    25.48   -4.067  0.000   0.000
+42980.00    25.243  -4.085  0.000   0.000
+42981.00    24.912  -4.097  0.000   0.000
+42982.00    24.574  -4.063  0.000   0.000
+42983.00    24.377  -4.02   0.000   0.000
+42984.00    24.427  -4.043  0.000   0.000
+42985.00    24.679  -4.122  0.000   0.000
+42986.00    24.928  -4.128  0.000   0.000
+42987.00    24.933  -3.959  0.000   0.000
+42988.00    24.587  -3.672  0.000   0.000
+42989.00    24.008  -3.435  0.000   0.000
+42990.00    23.474  -3.362  0.000   0.000
+42991.00    23.222  -3.427  0.000   0.000
+42992.00    23.278  -3.528  0.000   0.000
+42993.00    23.444  -3.601  0.000   0.000
+42994.00    23.466  -3.664  0.000   0.000
+42995.00    23.237  -3.774  0.000   0.000
+42996.00    22.881  -3.946  0.000   0.000
+42997.00    22.645  -4.097  0.000   0.000
+42998.00    22.668  -4.107  0.000   0.000
+42999.00    22.859  -3.942  0.000   0.000
+43000.00    23.000  -3.703  0.000   0.000
+43001.00    22.955  -3.54   0.000   0.000
+43002.00    22.739  -3.523  0.000   0.000
+43003.00    22.425  -3.612  0.000   0.000
+43004.00    22.079  -3.732  0.000   0.000
+43005.00    21.816  -3.839  0.000   0.000
+43006.00    21.756  -3.934  0.000   0.000
+43007.00    21.858  -4.04   0.000   0.000
+43008.00    21.88   -4.15   0.000   0.000
+43009.00    21.641  -4.205  0.000   0.000
+43010.00    21.274  -4.142  0.000   0.000
+43011.00    21.091  -4.001  0.000   0.000
+43012.00    21.229  -3.918  0.000   0.000
+43013.00    21.531  -3.972  0.000   0.000
+43014.00    21.749  -4.05   0.000   0.000
+43015.00    21.756  -3.96   0.000   0.000
+43016.00    21.547  -3.674  0.000   0.000
+43017.00    21.178  -3.375  0.000   0.000
+43018.00    20.757  -3.256  0.000   0.000
+43019.00    20.435  -3.331  0.000   0.000
+43020.00    20.321  -3.468  0.000   0.000
+43021.00    20.414  -3.565  0.000   0.000
+43022.00    20.621  -3.626  0.000   0.000
+43023.00    20.817  -3.692  0.000   0.000
+43024.00    20.93   -3.767  0.000   0.000
+43025.00    20.991  -3.811  0.000   0.000
+43026.00    21.076  -3.777  0.000   0.000
+43027.00    21.182  -3.653  0.000   0.000
+43028.00    21.213  -3.465  0.000   0.000
+43029.00    21.109  -3.267  0.000   0.000
+43030.00    20.922  -3.125  0.000   0.000
+43031.00    20.716  -3.095  0.000   0.000
+43032.00    20.512  -3.195  0.000   0.000
+43033.00    20.385  -3.388  0.000   0.000
+43034.00    20.482  -3.603  0.000   0.000
+43035.00    20.795  -3.791  0.000   0.000
+43036.00    21.035  -3.944  0.000   0.000
+43037.00    20.94   -4.032  0.000   0.000
+43038.00    20.654  -3.981  0.000   0.000
+43039.00    20.582  -3.778  0.000   0.000
+43040.00    20.877  -3.559  0.000   0.000
+43041.00    21.259  -3.495  0.000   0.000
+43042.00    21.396  -3.579  0.000   0.000
+43043.00    21.27   -3.611  0.000   0.000
+43044.00    21.096  -3.456  0.000   0.000
+43045.00    21.034  -3.213  0.000   0.000
+43046.00    21.062  -3.065  0.000   0.000
+43047.00    21.047  -3.041  0.000   0.000
+43048.00    20.891  -3.023  0.000   0.000
+43049.00    20.665  -2.964  0.000   0.000
+43050.00    20.606  -2.958  0.000   0.000
+43051.00    20.896  -3.072  0.000   0.000
+43052.00    21.436  -3.219  0.000   0.000
+43053.00    21.93   -3.253  0.000   0.000
+43054.00    22.19   -3.15   0.000   0.000
+43055.00    22.245  -2.998  0.000   0.000
+43056.00    22.189  -2.862  0.000   0.000
+43057.00    22.062  -2.722  0.000   0.000
+43058.00    21.9    -2.563  0.000   0.000
+43059.00    21.769  -2.455  0.000   0.000
+43060.00    21.712  -2.49   0.000   0.000
+43061.00    21.765  -2.662  0.000   0.000
+43062.00    21.997  -2.862  0.000   0.000
+43063.00    22.391  -2.999  0.000   0.000
+43064.00    22.727  -3.081  0.000   0.000
+43065.00    22.777  -3.141  0.000   0.000
+43066.00    22.64   -3.124  0.000   0.000
+43067.00    22.677  -2.955  0.000   0.000
+43068.00    23.05   -2.691  0.000   0.000
+43069.00    23.493  -2.506  0.000   0.000
+43070.00    23.65   -2.486  0.000   0.000
+43071.00    23.501  -2.509  0.000   0.000
+43072.00    23.339  -2.429  0.000   0.000
+43073.00    23.443  -2.279  0.000   0.000
+43074.00    23.824  -2.196  0.000   0.000
+43075.00    24.216  -2.181  0.000   0.000
+43076.00    24.282  -2.095  0.000   0.000
+43077.00    23.954  -1.914  0.000   0.000
+43078.00    23.6    -1.829  0.000   0.000
+43079.00    23.695  -1.992  0.000   0.000
+43080.00    24.277  -2.275  0.000   0.000
+43081.00    24.901  -2.404  0.000   0.000
+43082.00    25.164  -2.28   0.000   0.000
+43083.00    25.097  -2.055  0.000   0.000
+43084.00    24.967  -1.893  0.000   0.000
+43085.00    24.889  -1.793  0.000   0.000
+43086.00    24.821  -1.676  0.000   0.000
+43087.00    24.763  -1.551  0.000   0.000
+43088.00    24.785  -1.504  0.000   0.000
+43089.00    24.913  -1.554  0.000   0.000
+43090.00    25.125  -1.616  0.000   0.000
+43091.00    25.391  -1.62   0.000   0.000
+43092.00    25.64   -1.606  0.000   0.000
+43093.00    25.763  -1.642  0.000   0.000
+43094.00    25.77   -1.696  0.000   0.000
+43095.00    25.843  -1.655  0.000   0.000
+43096.00    26.113  -1.472  0.000   0.000
+43097.00    26.458  -1.241  0.000   0.000
+43098.00    26.637  -1.063  0.000   0.000
+43099.00    26.571  -0.922  0.000   0.000
+43100.00    26.419  -0.764  0.000   0.000
+43101.00    26.418  -0.646  0.000   0.000
+43102.00    26.677  -0.685  0.000   0.000
+43103.00    27.07   -0.844  0.000   0.000
+43104.00    27.292  -0.914  0.000   0.000
+43105.00    27.163  -0.789  0.000   0.000
+43106.00    26.907  -0.636  0.000   0.000
+43107.00    26.96   -0.697  0.000   0.000
+43108.00    27.42   -0.954  0.000   0.000
+43109.00    27.886  -1.144  0.000   0.000
+43110.00    27.954  -1.089  0.000   0.000
+43111.00    27.696  -0.881  0.000   0.000
+43112.00    27.472  -0.71   0.000   0.000
+43113.00    27.432  -0.632  0.000   0.000
+43114.00    27.453  -0.578  0.000   0.000
+43115.00    27.45   -0.507  0.000   0.000
+43116.00    27.491  -0.453  0.000   0.000
+43117.00    27.602  -0.428  0.000   0.000
+43118.00    27.705  -0.394  0.000   0.000
+43119.00    27.768  -0.342  0.000   0.000
+43120.00    27.862  -0.33   0.000   0.000
+43121.00    28.02   -0.41   0.000   0.000
+43122.00    28.177  -0.537  0.000   0.000
+43123.00    28.281  -0.597  0.000   0.000
+43124.00    28.363  -0.511  0.000   0.000
+43125.00    28.457  -0.303  0.000   0.000
+43126.00    28.527  -0.04   0.000   0.000
+43127.00    28.505  0.238   0.000   0.000
+43128.00    28.376  0.488   0.000   0.000
+43129.00    28.2    0.605   0.000   0.000
+43130.00    28.088  0.475   0.000   0.000
+43131.00    28.099  0.143   0.000   0.000
+43132.00    28.163  -0.144  0.000   0.000
+43133.00    28.178  -0.162  0.000   0.000
+43134.00    28.196  0.045   0.000   0.000
+43135.00    28.399  0.22    0.000   0.000
+43136.00    28.792  0.197   0.000   0.000
+43137.00    29.086  0.057   0.000   0.000
+43138.00    29.013  -0.023  0.000   0.000
+43139.00    28.672  0.007   0.000   0.000
+43140.00    28.384  0.065   0.000   0.000
+43141.00    28.294  0.083   0.000   0.000
+43142.00    28.296  0.066   0.000   0.000
+43143.00    28.308  0.04    0.000   0.000
+43144.00    28.381  0.022   0.000   0.000
+43145.00    28.52   0.028   0.000   0.000
+43146.00    28.608  0.065   0.000   0.000
+43147.00    28.591  0.088   0.000   0.000
+43148.00    28.606  0.025   0.000   0.000
+43149.00    28.788  -0.122  0.000   0.000
+43150.00    29.069  -0.26   0.000   0.000
+43151.00    29.249  -0.289  0.000   0.000
+43152.00    29.23   -0.188  0.000   0.000
+43153.00    29.088  -0.007  0.000   0.000
+43154.00    28.954  0.201   0.000   0.000
+43155.00    28.867  0.41    0.000   0.000
+43156.00    28.767  0.582   0.000   0.000
+43157.00    28.577  0.633   0.000   0.000
+43158.00    28.301  0.473   0.000   0.000
+43159.00    28.032  0.121   0.000   0.000
+43160.00    27.867  -0.236  0.000   0.000
+43161.00    27.845  -0.354  0.000   0.000
+43162.00    27.959  -0.157  0.000   0.000
+43163.00    28.187  0.173   0.000   0.000
+43164.00    28.445  0.361   0.000   0.000
+43165.00    28.582  0.293   0.000   0.000
+43166.00    28.498  0.069   0.000   0.000
+43167.00    28.252  -0.145  0.000   0.000
+43168.00    27.995  -0.276  0.000   0.000
+43169.00    27.81   -0.353  0.000   0.000
+43170.00    27.702  -0.435  0.000   0.000
+43171.00    27.693  -0.542  0.000   0.000
+43172.00    27.829  -0.641  0.000   0.000
+43173.00    28.064  -0.672  0.000   0.000
+43174.00    28.252  -0.625  0.000   0.000
+43175.00    28.313  -0.591  0.000   0.000
+43176.00    28.338  -0.679  0.000   0.000
+43177.00    28.479  -0.874  0.000   0.000
+43178.00    28.75   -1.022  0.000   0.000
+43179.00    28.993  -0.992  0.000   0.000
+43180.00    29.049  -0.811  0.000   0.000
+43181.00    28.902  -0.629  0.000   0.000
+43182.00    28.679  -0.551  0.000   0.000
+43183.00    28.516  -0.575  0.000   0.000
+43184.00    28.433  -0.641  0.000   0.000
+43185.00    28.35   -0.73   0.000   0.000
+43186.00    28.185  -0.876  0.000   0.000
+43187.00    27.964  -1.107  0.000   0.000
+43188.00    27.807  -1.365  0.000   0.000
+43189.00    27.82   -1.502  0.000   0.000
+43190.00    27.986  -1.407  0.000   0.000
+43191.00    28.176  -1.146  0.000   0.000
+43192.00    28.27   -0.943  0.000   0.000
+43193.00    28.258  -0.986  0.000   0.000
+43194.00    28.206  -1.26   0.000   0.000
+43195.00    28.145  -1.593  0.000   0.000
+43196.00    28.031  -1.837  0.000   0.000
+43197.00    27.853  -1.978  0.000   0.000
+43198.00    27.705  -2.092  0.000   0.000
+43199.00    27.713  -2.234  0.000   0.000
+43200.00    27.903  -2.371  0.000   0.000
+43201.00    28.188  -2.407  0.000   0.000
+43202.00    28.465  -2.296  0.000   0.000
+43203.00    28.675  -2.144  0.000   0.000
+43204.00    28.796  -2.137  0.000   0.000
+43205.00    28.857  -2.334  0.000   0.000
+43206.00    28.937  -2.567  0.000   0.000
+43207.00    29.093  -2.604  0.000   0.000
+43208.00    29.261  -2.412  0.000   0.000
+43209.00    29.307  -2.195  0.000   0.000
+43210.00    29.187  -2.172  0.000   0.000
+43211.00    29.005  -2.365  0.000   0.000
+43212.00    28.887  -2.63   0.000   0.000
+43213.00    28.848  -2.826  0.000   0.000
+43214.00    28.811  -2.931  0.000   0.000
+43215.00    28.743  -3.005  0.000   0.000
+43216.00    28.716  -3.095  0.000   0.000
+43217.00    28.835  -3.172  0.000   0.000
+43218.00    29.092  -3.168  0.000   0.000
+43219.00    29.335  -3.069  0.000   0.000
+43220.00    29.406  -2.961  0.000   0.000
+43221.00    29.323  -2.962  0.000   0.000
+43222.00    29.246  -3.111  0.000   0.000
+43223.00    29.252  -3.341  0.000   0.000
+43224.00    29.254  -3.559  0.000   0.000
+43225.00    29.177  -3.724  0.000   0.000
+43226.00    29.111  -3.862  0.000   0.000
+43227.00    29.18   -4.007  0.000   0.000
+43228.00    29.347  -4.144  0.000   0.000
+43229.00    29.515  -4.188  0.000   0.000
+43230.00    29.708  -4.053  0.000   0.000
+43231.00    30.002  -3.795  0.000   0.000
+43232.00    30.295  -3.633  0.000   0.000
+43233.00    30.373  -3.746  0.000   0.000
+43234.00    30.223  -4.042  0.000   0.000
+43235.00    30.088  -4.219  0.000   0.000
+43236.00    30.143  -4.103  0.000   0.000
+43237.00    30.266  -3.853  0.000   0.000
+43238.00    30.238  -3.769  0.000   0.000
+43239.00    30.031  -3.961  0.000   0.000
+43240.00    29.805  -4.268  0.000   0.000
+43241.00    29.696  -4.487  0.000   0.000
+43242.00    29.703  -4.562  0.000   0.000
+43243.00    29.75   -4.563  0.000   0.000
+43244.00    29.79   -4.552  0.000   0.000
+43245.00    29.848  -4.544  0.000   0.000
+43246.00    29.978  -4.535  0.000   0.000
+43247.00    30.16   -4.521  0.000   0.000
+43248.00    30.269  -4.491  0.000   0.000
+43249.00    30.213  -4.44   0.000   0.000
+43250.00    30.054  -4.4    0.000   0.000
+43251.00    29.923  -4.434  0.000   0.000
+43252.00    29.86   -4.576  0.000   0.000
+43253.00    29.846  -4.787  0.000   0.000
+43254.00    29.908  -4.982  0.000   0.000
+43255.00    30.055  -5.12   0.000   0.000
+43256.00    30.16   -5.222  0.000   0.000
+43257.00    30.111  -5.286  0.000   0.000
+43258.00    30.061  -5.235  0.000   0.000
+43259.00    30.276  -5.026  0.000   0.000
+43260.00    30.714  -4.79   0.000   0.000
+43261.00    30.982  -4.741  0.000   0.000
+43262.00    30.823  -4.916  0.000   0.000
+43263.00    30.438  -5.08   0.000   0.000
+43264.00    30.183  -5.000  0.000   0.000
+43265.00    30.125  -4.748  0.000   0.000
+43266.00    30.058  -4.613  0.000   0.000
+43267.00    29.841  -4.743  0.000   0.000
+43268.00    29.561  -4.998  0.000   0.000
+43269.00    29.403  -5.171  0.000   0.000
+43270.00    29.463  -5.225  0.000   0.000
+43271.00    29.663  -5.242  0.000   0.000
+43272.00    29.784  -5.249  0.000   0.000
+43273.00    29.666  -5.211  0.000   0.000
+43274.00    29.409  -5.147  0.000   0.000
+43275.00    29.264  -5.124  0.000   0.000
+43276.00    29.312  -5.141  0.000   0.000
+43277.00    29.349  -5.11   0.000   0.000
+43278.00    29.175  -4.999  0.000   0.000
+43279.00    28.849  -4.918  0.000   0.000
+43280.00    28.587  -5.003  0.000   0.000
+43281.00    28.527  -5.236  0.000   0.000
+43282.00    28.662  -5.456  0.000   0.000
+43283.00    28.875  -5.542  0.000   0.000
+43284.00    28.964  -5.539  0.000   0.000
+43285.00    28.791  -5.558  0.000   0.000
+43286.00    28.503  -5.599  0.000   0.000
+43287.00    28.458  -5.552  0.000   0.000
+43288.00    28.791  -5.382  0.000   0.000
+43289.00    29.185  -5.204  0.000   0.000
+43290.00    29.219  -5.118  0.000   0.000
+43291.00    28.839  -5.056  0.000   0.000
+43292.00    28.339  -4.892  0.000   0.000
+43293.00    27.967  -4.673  0.000   0.000
+43294.00    27.728  -4.602  0.000   0.000
+43295.00    27.514  -4.759  0.000   0.000
+43296.00    27.293  -4.979  0.000   0.000
+43297.00    27.156  -5.066  0.000   0.000
+43298.00    27.237  -5.035  0.000   0.000
+43299.00    27.52   -5.035  0.000   0.000
+43300.00    27.719  -5.097  0.000   0.000
+43301.00    27.497  -5.117  0.000   0.000
+43302.00    26.888  -5.058  0.000   0.000
+43303.00    26.346  -5.015  0.000   0.000
+43304.00    26.217  -5.058  0.000   0.000
+43305.00    26.322  -5.108  0.000   0.000
+43306.00    26.229  -5.068  0.000   0.000
+43307.00    25.824  -4.992  0.000   0.000
+43308.00    25.389  -5.022  0.000   0.000
+43309.00    25.207  -5.179  0.000   0.000
+43310.00    25.294  -5.32   0.000   0.000
+43311.00    25.485  -5.314  0.000   0.000
+43312.00    25.593  -5.203  0.000   0.000
+43313.00    25.475  -5.137  0.000   0.000
+43314.00    25.145  -5.184  0.000   0.000
+43315.00    24.834  -5.257  0.000   0.000
+43316.00    24.795  -5.233  0.000   0.000
+43317.00    25.004  -5.082  0.000   0.000
+43318.00    25.155  -4.86   0.000   0.000
+43319.00    24.986  -4.612  0.000   0.000
+43320.00    24.508  -4.373  0.000   0.000
+43321.00    23.933  -4.239  0.000   0.000
+43322.00    23.461  -4.329  0.000   0.000
+43323.00    23.157  -4.613  0.000   0.000
+43324.00    22.968  -4.869  0.000   0.000
+43325.00    22.854  -4.89   0.000   0.000
+43326.00    22.878  -4.726  0.000   0.000
+43327.00    23.087  -4.606  0.000   0.000
+43328.00    23.282  -4.652  0.000   0.000
+43329.00    23.101  -4.759  0.000   0.000
+43330.00    22.459  -4.794  0.000   0.000
+43331.00    21.758  -4.779  0.000   0.000
+43332.00    21.455  -4.809  0.000   0.000
+43333.00    21.511  -4.884  0.000   0.000
+43334.00    21.501  -4.925  0.000   0.000
+43335.00    21.196  -4.906  0.000   0.000
+43336.00    20.794  -4.894  0.000   0.000
+43337.00    20.574  -4.922  0.000   0.000
+43338.00    20.569  -4.938  0.000   0.000
+43339.00    20.66   -4.885  0.000   0.000
+43340.00    20.764  -4.786  0.000   0.000
+43341.00    20.81   -4.717  0.000   0.000
+43342.00    20.686  -4.72   0.000   0.000
+43343.00    20.37   -4.765  0.000   0.000
+43344.00    20.04   -4.791  0.000   0.000
+43345.00    19.913  -4.744  0.000   0.000
+43346.00    19.993  -4.607  0.000   0.000
+43347.00    20.061  -4.408  0.000   0.000
+43348.00    19.897  -4.232  0.000   0.000
+43349.00    19.477  -4.202  0.000   0.000
+43350.00    18.967  -4.397  0.000   0.000
+43351.00    18.552  -4.749  0.000   0.000
+43352.00    18.297  -5.033  0.000   0.000
+43353.00    18.17   -5.041  0.000   0.000
+43354.00    18.158  -4.792  0.000   0.000
+43355.00    18.28   -4.525  0.000   0.000
+43356.00    18.439  -4.453  0.000   0.000
+43357.00    18.387  -4.562  0.000   0.000
+43358.00    17.984  -4.682  0.000   0.000
+43359.00    17.416  -4.714  0.000   0.000
+43360.00    17.021  -4.698  0.000   0.000
+43361.00    16.907  -4.707  0.000   0.000
+43362.00    16.893  -4.744  0.000   0.000
+43363.00    16.806  -4.77   0.000   0.000
+43364.00    16.673  -4.762  0.000   0.000
+43365.00    16.589  -4.724  0.000   0.000
+43366.00    16.55   -4.672  0.000   0.000
+43367.00    16.522  -4.641  0.000   0.000
+43368.00    16.551  -4.654  0.000   0.000
+43369.00    16.664  -4.681  0.000   0.000
+43370.00    16.737  -4.656  0.000   0.000
+43371.00    16.599  -4.555  0.000   0.000
+43372.00    16.261  -4.439  0.000   0.000
+43373.00    15.942  -4.372  0.000   0.000
+43374.00    15.845  -4.351  0.000   0.000
+43375.00    15.961  -4.328  0.000   0.000
+43376.00    16.094  -4.298  0.000   0.000
+43377.00    16.061  -4.324  0.000   0.000
+43378.00    15.837  -4.47   0.000   0.000
+43379.00    15.536  -4.713  0.000   0.000
+43380.00    15.302  -4.922  0.000   0.000
+43381.00    15.206  -4.939  0.000   0.000
+43382.00    15.24   -4.726  0.000   0.000
+43383.00    15.354  -4.423  0.000   0.000
+43384.00    15.465  -4.24   0.000   0.000
+43385.00    15.47   -4.258  0.000   0.000
+43386.00    15.298  -4.371  0.000   0.000
+43387.00    14.974  -4.433  0.000   0.000
+43388.00    14.625  -4.409  0.000   0.000
+43389.00    14.398  -4.377  0.000   0.000
+43390.00    14.379  -4.401  0.000   0.000
+43391.00    14.547  -4.468  0.000   0.000
+43392.00    14.777  -4.517  0.000   0.000
+43393.00    14.924  -4.507  0.000   0.000
+43394.00    14.926  -4.459  0.000   0.000
+43395.00    14.854  -4.448  0.000   0.000
+43396.00    14.824  -4.529  0.000   0.000
+43397.00    14.877  -4.641  0.000   0.000
+43398.00    14.937  -4.634  0.000   0.000
+43399.00    14.9    -4.427  0.000   0.000
+43400.00    14.738  -4.12   0.000   0.000
+43401.00    14.527  -3.911  0.000   0.000
+43402.00    14.387  -3.895  0.000   0.000
+43403.00    14.39   -3.997  0.000   0.000
+43404.00    14.505  -4.089  0.000   0.000
+43405.00    14.64   -4.125  0.000   0.000
+43406.00    14.717  -4.148  0.000   0.000
+43407.00    14.737  -4.204  0.000   0.000
+43408.00    14.764  -4.273  0.000   0.000
+43409.00    14.858  -4.277  0.000   0.000
+43410.00    15.015  -4.16   0.000   0.000
+43411.00    15.153  -3.956  0.000   0.000
+43412.00    15.196  -3.779  0.000   0.000
+43413.00    15.146  -3.714  0.000   0.000
+43414.00    15.07   -3.741  0.000   0.000
+43415.00    14.988  -3.771  0.000   0.000
+43416.00    14.877  -3.768  0.000   0.000
+43417.00    14.8    -3.768  0.000   0.000
+43418.00    14.924  -3.819  0.000   0.000
+43419.00    15.321  -3.911  0.000   0.000
+43420.00    15.803  -3.993  0.000   0.000
+43421.00    16.099  -4.018  0.000   0.000
+43422.00    16.159  -3.975  0.000   0.000
+43423.00    16.166  -3.913  0.000   0.000
+43424.00    16.242  -3.911  0.000   0.000
+43425.00    16.31   -3.978  0.000   0.000
+43426.00    16.275  -3.99   0.000   0.000
+43427.00    16.203  -3.803  0.000   0.000
+43428.00    16.224  -3.445  0.000   0.000
+43429.00    16.342  -3.139  0.000   0.000
+43430.00    16.45   -3.076  0.000   0.000
+43431.00    16.462  -3.218  0.000   0.000
+43432.00    16.392  -3.368  0.000   0.000
+43433.00    16.318  -3.396  0.000   0.000
+43434.00    16.332  -3.337  0.000   0.000
+43435.00    16.487  -3.282  0.000   0.000
+43436.00    16.752  -3.254  0.000   0.000
+43437.00    17.032  -3.211  0.000   0.000
+43438.00    17.238  -3.125  0.000   0.000
+43439.00    17.321  -3.018  0.000   0.000
+43440.00    17.276  -2.93   0.000   0.000
+43441.00    17.17   -2.864  0.000   0.000
+43442.00    17.123  -2.8    0.000   0.000
+43443.00    17.199  -2.729  0.000   0.000
+43444.00    17.342  -2.681  0.000   0.000
+43445.00    17.51   -2.686  0.000   0.000
+43446.00    17.78   -2.737  0.000   0.000
+43447.00    18.214  -2.798  0.000   0.000
+43448.00    18.658  -2.85   0.000   0.000
+43449.00    18.882  -2.875  0.000   0.000
+43450.00    18.912  -2.837  0.000   0.000
+43451.00    19.006  -2.712  0.000   0.000
+43452.00    19.255  -2.559  0.000   0.000
+43453.00    19.438  -2.478  0.000   0.000
+43454.00    19.362  -2.456  0.000   0.000
+43455.00    19.183  -2.354  0.000   0.000
+43456.00    19.208  -2.099  0.000   0.000
+43457.00    19.508  -1.837  0.000   0.000
+43458.00    19.863  -1.785  0.000   0.000
+43459.00    20.019  -1.953  0.000   0.000
+43460.00    19.895  -2.131  0.000   0.000
+43461.00    19.608  -2.148  0.000   0.000
+43462.00    19.395  -2.056  0.000   0.000
+43463.00    19.442  -1.988  0.000   0.000
+43464.00    19.708  -1.958  0.000   0.000
+43465.00    19.954  -1.884  0.000   0.000
+43466.00    20.005  -1.753  0.000   0.000
+43467.00    19.906  -1.659  0.000   0.000
+43468.00    19.796  -1.657  0.000   0.000
+43469.00    19.743  -1.679  0.000   0.000
+43470.00    19.756  -1.626  0.000   0.000
+43471.00    19.85   -1.493  0.000   0.000
+43472.00    20.022  -1.374  0.000   0.000
+43473.00    20.229  -1.331  0.000   0.000
+43474.00    20.458  -1.338  0.000   0.000
+43475.00    20.722  -1.348  0.000   0.000
+43476.00    20.952  -1.369  0.000   0.000
+43477.00    21.035  -1.426  0.000   0.000
+43478.00    21.023  -1.466  0.000   0.000
+43479.00    21.134  -1.387  0.000   0.000
+43480.00    21.426  -1.184  0.000   0.000
+43481.00    21.652  -0.985  0.000   0.000
+43482.00    21.575  -0.889  0.000   0.000
+43483.00    21.31   -0.833  0.000   0.000
+43484.00    21.178  -0.707  0.000   0.000
+43485.00    21.308  -0.569  0.000   0.000
+43486.00    21.537  -0.595  0.000   0.000
+43487.00    21.638  -0.809  0.000   0.000
+43488.00    21.523  -1.002  0.000   0.000
+43489.00    21.276  -0.988  0.000   0.000
+43490.00    21.109  -0.838  0.000   0.000
+43491.00    21.205  -0.746  0.000   0.000
+43492.00    21.494  -0.746  0.000   0.000
+43493.00    21.663  -0.703  0.000   0.000
+43494.00    21.518  -0.559  0.000   0.000
+43495.00    21.229  -0.443  0.000   0.000
+43496.00    21.094  -0.48   0.000   0.000
+43497.00    21.163  -0.61   0.000   0.000
+43498.00    21.256  -0.672  0.000   0.000
+43499.00    21.28   -0.607  0.000   0.000
+43500.00    21.309  -0.502  0.000   0.000
+43501.00    21.402  -0.441  0.000   0.000
+43502.00    21.527  -0.417  0.000   0.000
+43503.00    21.649  -0.399  0.000   0.000
+43504.00    21.769  -0.424  0.000   0.000
+43505.00    21.867  -0.535  0.000   0.000
+43506.00    21.936  -0.679  0.000   0.000
+43507.00    22.033  -0.718  0.000   0.000
+43508.00    22.183  -0.586  0.000   0.000
+43509.00    22.271  -0.376  0.000   0.000
+43510.00    22.16   -0.223  0.000   0.000
+43511.00    21.9    -0.148  0.000   0.000
+43512.00    21.668  -0.089  0.000   0.000
+43513.00    21.535  -0.063  0.000   0.000
+43514.00    21.412  -0.176  0.000   0.000
+43515.00    21.213  -0.431  0.000   0.000
+43516.00    20.96   -0.624  0.000   0.000
+43517.00    20.763  -0.565  0.000   0.000
+43518.00    20.769  -0.324  0.000   0.000
+43519.00    21.062  -0.155  0.000   0.000
+43520.00    21.495  -0.169  0.000   0.000
+43521.00    21.719  -0.232  0.000   0.000
+43522.00    21.535  -0.203  0.000   0.000
+43523.00    21.165  -0.136  0.000   0.000
+43524.00    20.984  -0.177  0.000   0.000
+43525.00    21.054  -0.342  0.000   0.000
+43526.00    21.131  -0.503  0.000   0.000
+43527.00    21.08   -0.569  0.000   0.000
+43528.00    21.041  -0.567  0.000   0.000
+43529.00    21.149  -0.552  0.000   0.000
+43530.00    21.333  -0.537  0.000   0.000
+43531.00    21.483  -0.528  0.000   0.000
+43532.00    21.623  -0.57   0.000   0.000
+43533.00    21.814  -0.698  0.000   0.000
+43534.00    22.007  -0.86   0.000   0.000
+43535.00    22.102  -0.947  0.000   0.000
+43536.00    22.073  -0.902  0.000   0.000
+43537.00    21.961  -0.776  0.000   0.000
+43538.00    21.805  -0.66   0.000   0.000
+43539.00    21.635  -0.596  0.000   0.000
+43540.00    21.479  -0.58   0.000   0.000
+43541.00    21.316  -0.635  0.000   0.000
+43542.00    21.088  -0.813  0.000   0.000
+43543.00    20.784  -1.087  0.000   0.000
+43544.00    20.482  -1.282  0.000   0.000
+43545.00    20.298  -1.222  0.000   0.000
+43546.00    20.338  -0.955  0.000   0.000
+43547.00    20.638  -0.739  0.000   0.000
+43548.00    21.06   -0.771  0.000   0.000
+43549.00    21.321  -0.984  0.000   0.000
+43550.00    21.242  -1.172  0.000   0.000
+43551.00    20.955  -1.244  0.000   0.000
+43552.00    20.74   -1.279  0.000   0.000
+43553.00    20.68   -1.371  0.000   0.000
+43554.00    20.642  -1.512  0.000   0.000
+43555.00    20.588  -1.643  0.000   0.000
+43556.00    20.682  -1.728  0.000   0.000
+43557.00    21.016  -1.762  0.000   0.000
+43558.00    21.422  -1.76   0.000   0.000
+43559.00    21.685  -1.766  0.000   0.000
+43560.00    21.815  -1.83   0.000   0.000
+43561.00    21.962  -1.949  0.000   0.000
+43562.00    22.154  -2.048  0.000   0.000
+43563.00    22.267  -2.064  0.000   0.000
+43564.00    22.224  -2.018  0.000   0.000
+43565.00    22.088  -1.983  0.000   0.000
+43566.00    21.968  -1.998  0.000   0.000
+43567.00    21.902  -2.045  0.000   0.000
+43568.00    21.862  -2.101  0.000   0.000
+43569.00    21.804  -2.186  0.000   0.000
+43570.00    21.717  -2.341  0.000   0.000
+43571.00    21.632  -2.554  0.000   0.000
+43572.00    21.597  -2.714  0.000   0.000
+43573.00    21.64   -2.692  0.000   0.000
+43574.00    21.757  -2.489  0.000   0.000
+43575.00    21.925  -2.292  0.000   0.000
+43576.00    22.1    -2.316  0.000   0.000
+43577.00    22.209  -2.585  0.000   0.000
+43578.00    22.202  -2.908  0.000   0.000
+43579.00    22.099  -3.088  0.000   0.000
+43580.00    21.959  -3.101  0.000   0.000
+43581.00    21.809  -3.069  0.000   0.000
+43582.00    21.67   -3.102  0.000   0.000
+43583.00    21.649  -3.207  0.000   0.000
+43584.00    21.885  -3.318  0.000   0.000
+43585.00    22.368  -3.368  0.000   0.000
+43586.00    22.875  -3.353  0.000   0.000
+43587.00    23.167  -3.344  0.000   0.000
+43588.00    23.209  -3.415  0.000   0.000
+43589.00    23.155  -3.539  0.000   0.000
+43590.00    23.148  -3.599  0.000   0.000
+43591.00    23.197  -3.52   0.000   0.000
+43592.00    23.236  -3.384  0.000   0.000
+43593.00    23.223  -3.352  0.000   0.000
+43594.00    23.173  -3.486  0.000   0.000
+43595.00    23.11   -3.699  0.000   0.000
+43596.00    23.037  -3.862  0.000   0.000
+43597.00    22.95   -3.926  0.000   0.000
+43598.00    22.876  -3.942  0.000   0.000
+43599.00    22.887  -3.972  0.000   0.000
+43600.00    23.052  -4.02   0.000   0.000
+43601.00    23.356  -4.021  0.000   0.000
+43602.00    23.669  -3.927  0.000   0.000
+43603.00    23.827  -3.797  0.000   0.000
+43604.00    23.771  -3.77   0.000   0.000
+43605.00    23.608  -3.924  0.000   0.000
+43606.00    23.502  -4.168  0.000   0.000
+43607.00    23.503  -4.332  0.000   0.000
+43608.00    23.508  -4.338  0.000   0.000
+43609.00    23.417  -4.271  0.000   0.000
+43610.00    23.29   -4.26   0.000   0.000
+43611.00    23.284  -4.351  0.000   0.000
+43612.00    23.476  -4.483  0.000   0.000
+43613.00    23.8    -4.564  0.000   0.000
+43614.00    24.131  -4.553  0.000   0.000
+43615.00    24.361  -4.511  0.000   0.000
+43616.00    24.408  -4.537  0.000   0.000
+43617.00    24.247  -4.649  0.000   0.000
+43618.00    23.983  -4.723  0.000   0.000
+43619.00    23.805  -4.626  0.000   0.000
+43620.00    23.798  -4.406  0.000   0.000
+43621.00    23.859  -4.285  0.000   0.000
+43622.00    23.832  -4.429  0.000   0.000
+43623.00    23.689  -4.757  0.000   0.000
+43624.00    23.51   -5.028  0.000   0.000
+43625.00    23.351  -5.074  0.000   0.000
+43626.00    23.208  -4.933  0.000   0.000
+43627.00    23.102  -4.758  0.000   0.000
+43628.00    23.124  -4.668  0.000   0.000
+43629.00    23.344  -4.669  0.000   0.000
+43630.00    23.682  -4.691  0.000   0.000
+43631.00    23.915  -4.678  0.000   0.000
+43632.00    23.857  -4.645  0.000   0.000
+43633.00    23.555  -4.642  0.000   0.000
+43634.00    23.26   -4.683  0.000   0.000
+43635.00    23.16   -4.736  0.000   0.000
+43636.00    23.206  -4.77   0.000   0.000
+43637.00    23.25   -4.8    0.000   0.000
+43638.00    23.266  -4.858  0.000   0.000
+43639.00    23.319  -4.958  0.000   0.000
+43640.00    23.388  -5.083  0.000   0.000
+43641.00    23.402  -5.189  0.000   0.000
+43642.00    23.423  -5.217  0.000   0.000
+43643.00    23.583  -5.156  0.000   0.000
+43644.00    23.796  -5.083  0.000   0.000
+43645.00    23.764  -5.088  0.000   0.000
+43646.00    23.364  -5.132  0.000   0.000
+43647.00    22.868  -5.063  0.000   0.000
+43648.00    22.612  -4.827  0.000   0.000
+43649.00    22.6    -4.609  0.000   0.000
+43650.00    22.571  -4.65   0.000   0.000
+43651.00    22.389  -4.954  0.000   0.000
+43652.00    22.164  -5.251  0.000   0.000
+43653.00    22.028  -5.291  0.000   0.000
+43654.00    21.956  -5.079  0.000   0.000
+43655.00    21.84   -4.815  0.000   0.000
+43656.00    21.66   -4.666  0.000   0.000
+43657.00    21.518  -4.663  0.000   0.000
+43658.00    21.532  -4.75   0.000   0.000
+43659.00    21.675  -4.858  0.000   0.000
+43660.00    21.748  -4.924  0.000   0.000
+43661.00    21.568  -4.905  0.000   0.000
+43662.00    21.184  -4.824  0.000   0.000
+43663.00    20.832  -4.767  0.000   0.000
+43664.00    20.687  -4.81   0.000   0.000
+43665.00    20.735  -4.938  0.000   0.000
+43666.00    20.881  -5.055  0.000   0.000
+43667.00    21.029  -5.1    0.000   0.000
+43668.00    21.041  -5.111  0.000   0.000
+43669.00    20.838  -5.161  0.000   0.000
+43670.00    20.593  -5.23   0.000   0.000
+43671.00    20.612  -5.22   0.000   0.000
+43672.00    20.898  -5.104  0.000   0.000
+43673.00    21.022  -4.984  0.000   0.000
+43674.00    20.623  -4.936  0.000   0.000
+43675.00    19.883  -4.882  0.000   0.000
+43676.00    19.302  -4.713  0.000   0.000
+43677.00    19.093  -4.503  0.000   0.000
+43678.00    19.043  -4.466  0.000   0.000
+43679.00    18.901  -4.663  0.000   0.000
+43680.00    18.682  -4.883  0.000   0.000
+43681.00    18.545  -4.892  0.000   0.000
+43682.00    18.544  -4.698  0.000   0.000
+43683.00    18.553  -4.502  0.000   0.000
+43684.00    18.388  -4.428  0.000   0.000
+43685.00    17.998  -4.445  0.000   0.000
+43686.00    17.58   -4.503  0.000   0.000
+43687.00    17.42   -4.616  0.000   0.000
+43688.00    17.548  -4.775  0.000   0.000
+43689.00    17.649  -4.884  0.000   0.000
+43690.00    17.42   -4.868  0.000   0.000
+43691.00    16.936  -4.798  0.000   0.000
+43692.00    16.544  -4.81   0.000   0.000
+43693.00    16.467  -4.924  0.000   0.000
+43694.00    16.642  -5.004  0.000   0.000
+43695.00    16.858  -4.94   0.000   0.000
+43696.00    16.909  -4.799  0.000   0.000
+43697.00    16.701  -4.747  0.000   0.000
+43698.00    16.367  -4.838  0.000   0.000
+43699.00    16.204  -4.943  0.000   0.000
+43700.00    16.336  -4.926  0.000   0.000
+43701.00    16.474  -4.807  0.000   0.000
+43702.00    16.214  -4.703  0.000   0.000
+43703.00    15.538  -4.648  0.000   0.000
+43704.00    14.839  -4.581  0.000   0.000
+43705.00    14.448  -4.503  0.000   0.000
+43706.00    14.327  -4.521  0.000   0.000
+43707.00    14.242  -4.665  0.000   0.000
+43708.00    14.073  -4.783  0.000   0.000
+43709.00    13.895  -4.718  0.000   0.000
+43710.00    13.84   -4.534  0.000   0.000
+43711.00    13.919  -4.436  0.000   0.000
+43712.00    13.925  -4.489  0.000   0.000
+43713.00    13.607  -4.548  0.000   0.000
+43714.00    13.009  -4.505  0.000   0.000
+43715.00    12.544  -4.453  0.000   0.000
+43716.00    12.545  -4.534  0.000   0.000
+43717.00    12.822  -4.719  0.000   0.000
+43718.00    12.89   -4.855  0.000   0.000
+43719.00    12.577  -4.881  0.000   0.000
+43720.00    12.193  -4.878  0.000   0.000
+43721.00    12.081  -4.908  0.000   0.000
+43722.00    12.24   -4.905  0.000   0.000
+43723.00    12.449  -4.785  0.000   0.000
+43724.00    12.539  -4.596  0.000   0.000
+43725.00    12.458  -4.486  0.000   0.000
+43726.00    12.237  -4.536  0.000   0.000
+43727.00    12.007  -4.668  0.000   0.000
+43728.00    11.923  -4.743  0.000   0.000
+43729.00    11.968  -4.709  0.000   0.000
+43730.00    11.924  -4.624  0.000   0.000
+43731.00    11.626  -4.563  0.000   0.000
+43732.00    11.162  -4.55   0.000   0.000
+43733.00    10.759  -4.599  0.000   0.000
+43734.00    10.541  -4.729  0.000   0.000
+43735.00    10.451  -4.894  0.000   0.000
+43736.00    10.363  -4.949  0.000   0.000
+43737.00    10.226  -4.799  0.000   0.000
+43738.00    10.132  -4.556  0.000   0.000
+43739.00    10.195  -4.45   0.000   0.000
+43740.00    10.33   -4.546  0.000   0.000
+43741.00    10.244  -4.648  0.000   0.000
+43742.00    9.784   -4.557  0.000   0.000
+43743.00    9.223   -4.334  0.000   0.000
+43744.00    9.000   -4.218  0.000   0.000
+43745.00    9.183   -4.329  0.000   0.000
+43746.00    9.42    -4.554  0.000   0.000
+43747.00    9.44    -4.726  0.000   0.000
+43748.00    9.353   -4.793  0.000   0.000
+43749.00    9.387   -4.798  0.000   0.000
+43750.00    9.535   -4.767  0.000   0.000
+43751.00    9.641   -4.692  0.000   0.000
+43752.00    9.664   -4.584  0.000   0.000
+43753.00    9.66    -4.48   0.000   0.000
+43754.00    9.614   -4.409  0.000   0.000
+43755.00    9.467   -4.375  0.000   0.000
+43756.00    9.284   -4.366  0.000   0.000
+43757.00    9.212   -4.367  0.000   0.000
+43758.00    9.28    -4.357  0.000   0.000
+43759.00    9.343   -4.336  0.000   0.000
+43760.00    9.258   -4.336  0.000   0.000
+43761.00    9.047   -4.406  0.000   0.000
+43762.00    8.85    -4.556  0.000   0.000
+43763.00    8.777   -4.714  0.000   0.000
+43764.00    8.809   -4.74   0.000   0.000
+43765.00    8.848   -4.556  0.000   0.000
+43766.00    8.867   -4.262  0.000   0.000
+43767.00    8.934   -4.077  0.000   0.000
+43768.00    9.074   -4.113  0.000   0.000
+43769.00    9.144   -4.239  0.000   0.000
+43770.00    8.975   -4.224  0.000   0.000
+43771.00    8.63    -4.015  0.000   0.000
+43772.00    8.387   -3.803  0.000   0.000
+43773.00    8.436   -3.796  0.000   0.000
+43774.00    8.708   -4.002  0.000   0.000
+43775.00    9.031   -4.261  0.000   0.000
+43776.00    9.336   -4.428  0.000   0.000
+43777.00    9.609   -4.477  0.000   0.000
+43778.00    9.78    -4.465  0.000   0.000
+43779.00    9.795   -4.447  0.000   0.000
+43780.00    9.736   -4.416  0.000   0.000
+43781.00    9.732   -4.315  0.000   0.000
+43782.00    9.78    -4.097  0.000   0.000
+43783.00    9.76    -3.813  0.000   0.000
+43784.00    9.639   -3.603  0.000   0.000
+43785.00    9.543   -3.57   0.000   0.000
+43786.00    9.593   -3.671  0.000   0.000
+43787.00    9.743   -3.779  0.000   0.000
+43788.00    9.843   -3.814  0.000   0.000
+43789.00    9.815   -3.807  0.000   0.000
+43790.00    9.738   -3.821  0.000   0.000
+43791.00    9.75    -3.852  0.000   0.000
+43792.00    9.906   -3.825  0.000   0.000
+43793.00    10.132  -3.675  0.000   0.000
+43794.00    10.314  -3.432  0.000   0.000
+43795.00    10.403  -3.231  0.000   0.000
+43796.00    10.44   -3.193  0.000   0.000
+43797.00    10.477  -3.288  0.000   0.000
+43798.00    10.514  -3.351  0.000   0.000
+43799.00    10.529  -3.26   0.000   0.000
+43800.00    10.545  -3.085  0.000   0.000
+43801.00    10.648  -3.007  0.000   0.000
+43802.00    10.924  -3.116  0.000   0.000
+43803.00    11.378  -3.327  0.000   0.000
+43804.00    11.889  -3.494  0.000   0.000
+43805.00    12.283  -3.548  0.000   0.000
+43806.00    12.465  -3.521  0.000   0.000
+43807.00    12.479  -3.475  0.000   0.000
+43808.00    12.444  -3.422  0.000   0.000
+43809.00    12.426  -3.296  0.000   0.000
+43810.00    12.414  -3.017  0.000   0.000
+43811.00    12.383  -2.619  0.000   0.000
+43812.00    12.351  -2.287  0.000   0.000
+43813.00    12.358  -2.216  0.000   0.000
+43814.00    12.415  -2.416  0.000   0.000
+43815.00    12.482  -2.683  0.000   0.000
+43816.00    12.503  -2.792  0.000   0.000
+43817.00    12.451  -2.693  0.000   0.000
+43818.00    12.373  -2.499  0.000   0.000
+43819.00    12.365  -2.337  0.000   0.000
+43820.00    12.49   -2.24   0.000   0.000
+43821.00    12.709  -2.166  0.000   0.000
+43822.00    12.898  -2.078  0.000   0.000
+43823.00    12.945  -1.998  0.000   0.000
+43824.00    12.853  -1.978  0.000   0.000
+43825.00    12.753  -2.025  0.000   0.000
+43826.00    12.796  -2.061  0.000   0.000
+43827.00    12.999  -2.005  0.000   0.000
+43828.00    13.24   -1.876  0.000   0.000
+43829.00    13.43   -1.785  0.000   0.000
+43830.00    13.644  -1.811  0.000   0.000
+43831.00    13.988  -1.918  0.000   0.000
+43832.00    14.399  -2.012  0.000   0.000
+43833.00    14.697  -2.033  0.000   0.000
+43834.00    14.825  -1.983  0.000   0.000
+43835.00    14.898  -1.893  0.000   0.000
+43836.00    14.978  -1.794  0.000   0.000
+43837.00    14.963  -1.683  0.000   0.000
+43838.00    14.78   -1.509  0.000   0.000
+43839.00    14.562  -1.233  0.000   0.000
+43840.00    14.505  -0.942  0.000   0.000
+43841.00    14.62   -0.836  0.000   0.000
+43842.00    14.744  -1.024  0.000   0.000
+43843.00    14.76   -1.356  0.000   0.000
+43844.00    14.688  -1.534  0.000   0.000
+43845.00    14.593  -1.404  0.000   0.000
+43846.00    14.513  -1.082  0.000   0.000
+43847.00    14.472  -0.795  0.000   0.000
+43848.00    14.488  -0.663  0.000   0.000
+43849.00    14.541  -0.656  0.000   0.000
+43850.00    14.574  -0.707  0.000   0.000
+43851.00    14.537  -0.785  0.000   0.000
+43852.00    14.428  -0.878  0.000   0.000
+43853.00    14.314  -0.945  0.000   0.000
+43854.00    14.308  -0.933  0.000   0.000
+43855.00    14.459  -0.835  0.000   0.000
+43856.00    14.676  -0.714  0.000   0.000
+43857.00    14.832  -0.651  0.000   0.000
+43858.00    14.937  -0.67   0.000   0.000
+43859.00    15.096  -0.727  0.000   0.000
+43860.00    15.313  -0.777  0.000   0.000
+43861.00    15.469  -0.805  0.000   0.000
+43862.00    15.545  -0.799  0.000   0.000
+43863.00    15.659  -0.74   0.000   0.000
+43864.00    15.812  -0.654  0.000   0.000
+43865.00    15.782  -0.608  0.000   0.000
+43866.00    15.448  -0.609  0.000   0.000
+43867.00    15.042  -0.554  0.000   0.000
+43868.00    14.892  -0.375  0.000   0.000
+43869.00    14.995  -0.196  0.000   0.000
+43870.00    15.069  -0.222  0.000   0.000
+43871.00    14.958  -0.461  0.000   0.000
+43872.00    14.798  -0.658  0.000   0.000
+43873.00    14.781  -0.583  0.000   0.000
+43874.00    14.925  -0.286  0.000   0.000
+43875.00    15.115  -0.007  0.000   0.000
+43876.00    15.221  0.113   0.000   0.000
+43877.00    15.172  0.114   0.000   0.000
+43878.00    15.003  0.067   0.000   0.000
+43879.00    14.845  -0.04   0.000   0.000
+43880.00    14.795  -0.219  0.000   0.000
+43881.00    14.818  -0.39   0.000   0.000
+43882.00    14.832  -0.443  0.000   0.000
+43883.00    14.836  -0.374  0.000   0.000
+43884.00    14.878  -0.289  0.000   0.000
+43885.00    14.957  -0.277  0.000   0.000
+43886.00    15.047  -0.322  0.000   0.000
+43887.00    15.168  -0.362  0.000   0.000
+43888.00    15.321  -0.392  0.000   0.000
+43889.00    15.441  -0.45   0.000   0.000
+43890.00    15.501  -0.529  0.000   0.000
+43891.00    15.561  -0.563  0.000   0.000
+43892.00    15.613  -0.537  0.000   0.000
+43893.00    15.489  -0.541  0.000   0.000
+43894.00    15.107  -0.639  0.000   0.000
+43895.00    14.7    -0.733  0.000   0.000
+43896.00    14.567  -0.66   0.000   0.000
+43897.00    14.653  -0.448  0.000   0.000
+43898.00    14.607  -0.324  0.000   0.000
+43899.00    14.276  -0.433  0.000   0.000
+43900.00    13.919  -0.628  0.000   0.000
+43901.00    13.885  -0.664  0.000   0.000
+43902.00    14.245  -0.51   0.000   0.000
+43903.00    14.767  -0.354  0.000   0.000
+43904.00    15.125  -0.312  0.000   0.000
+43905.00    15.115  -0.313  0.000   0.000
+43906.00    14.803  -0.278  0.000   0.000
+43907.00    14.502  -0.286  0.000   0.000
+43908.00    14.473  -0.449  0.000   0.000
+43909.00    14.64   -0.721  0.000   0.000
+43910.00    14.731  -0.937  0.000   0.000
+43911.00    14.659  -1.017  0.000   0.000
+43912.00    14.607  -1.03   0.000   0.000
+43913.00    14.735  -1.06   0.000   0.000
+43914.00    14.988  -1.092  0.000   0.000
+43915.00    15.238  -1.077  0.000   0.000
+43916.00    15.439  -1.046  0.000   0.000
+43917.00    15.584  -1.081  0.000   0.000
+43918.00    15.645  -1.193  0.000   0.000
+43919.00    15.611  -1.3    0.000   0.000
+43920.00    15.492  -1.344  0.000   0.000
+43921.00    15.275  -1.376  0.000   0.000
+43922.00    14.975  -1.477  0.000   0.000
+43923.00    14.755  -1.605  0.000   0.000
+43924.00    14.791  -1.625  0.000   0.000
+43925.00    14.998  -1.509  0.000   0.000
+43926.00    15.06   -1.412  0.000   0.000
+43927.00    14.807  -1.483  0.000   0.000
+43928.00    14.447  -1.657  0.000   0.000
+43929.00    14.336  -1.743  0.000   0.000
+43930.00    14.622  -1.695  0.000   0.000
+43931.00    15.137  -1.657  0.000   0.000
+43932.00    15.544  -1.728  0.000   0.000
+43933.00    15.574  -1.808  0.000   0.000
+43934.00    15.252  -1.774  0.000   0.000
+43935.00    14.917  -1.692  0.000   0.000
+43936.00    14.896  -1.75   0.000   0.000
+43937.00    15.13   -2.004  0.000   0.000
+43938.00    15.287  -2.314  0.000   0.000
+43939.00    15.239  -2.529  0.000   0.000
+43940.00    15.231  -2.627  0.000   0.000
+43941.00    15.501  -2.654  0.000   0.000
+43942.00    15.948  -2.622  0.000   0.000
+43943.00    16.305  -2.525  0.000   0.000
+43944.00    16.484  -2.41   0.000   0.000
+43945.00    16.567  -2.36   0.000   0.000
+43946.00    16.592  -2.402  0.000   0.000
+43947.00    16.514  -2.489  0.000   0.000
+43948.00    16.332  -2.572  0.000   0.000
+43949.00    16.126  -2.654  0.000   0.000
+43950.00    15.965  -2.761  0.000   0.000
+43951.00    15.904  -2.874  0.000   0.000
+43952.00    15.99   -2.939  0.000   0.000
+43953.00    16.209  -2.944  0.000   0.000
+43954.00    16.442  -2.956  0.000   0.000
+43955.00    16.574  -3.034  0.000   0.000
+43956.00    16.61   -3.124  0.000   0.000
+43957.00    16.648  -3.117  0.000   0.000
+43958.00    16.753  -3.014  0.000   0.000
+43959.00    16.895  -2.964  0.000   0.000
+43960.00    16.969  -3.08   0.000   0.000
+43961.00    16.872  -3.273  0.000   0.000
+43962.00    16.63   -3.355  0.000   0.000
+43963.00    16.44   -3.29   0.000   0.000
+43964.00    16.487  -3.238  0.000   0.000
+43965.00    16.698  -3.352  0.000   0.000
+43966.00    16.815  -3.605  0.000   0.000
+43967.00    16.762  -3.853  0.000   0.000
+43968.00    16.775  -3.993  0.000   0.000
+43969.00    17.069  -4.014  0.000   0.000
+43970.00    17.518  -3.947  0.000   0.000
+43971.00    17.817  -3.826  0.000   0.000
+43972.00    17.856  -3.697  0.000   0.000
+43973.00    17.769  -3.594  0.000   0.000
+43974.00    17.678  -3.526  0.000   0.000
+43975.00    17.569  -3.504  0.000   0.000
+43976.00    17.426  -3.558  0.000   0.000
+43977.00    17.309  -3.702  0.000   0.000
+43978.00    17.26   -3.888  0.000   0.000
+43979.00    17.229  -4.029  0.000   0.000
+43980.00    17.165  -4.083  0.000   0.000
+43981.00    17.119  -4.081  0.000   0.000
+43982.00    17.205  -4.086  0.000   0.000
+43983.00    17.483  -4.111  0.000   0.000
+43984.00    17.885  -4.101  0.000   0.000
+43985.00    18.239  -3.988  0.000   0.000
+43986.00    18.37   -3.795  0.000   0.000
+43987.00    18.217  -3.659  0.000   0.000
+43988.00    17.874  -3.714  0.000   0.000
+43989.00    17.521  -3.929  0.000   0.000
+43990.00    17.32   -4.122  0.000   0.000
+43991.00    17.344  -4.153  0.000   0.000
+43992.00    17.534  -4.077  0.000   0.000
+43993.00    17.721  -4.064  0.000   0.000
+43994.00    17.755  -4.198  0.000   0.000
+43995.00    17.658  -4.409  0.000   0.000
+43996.00    17.615  -4.573  0.000   0.000
+43997.00    17.765  -4.628  0.000   0.000
+43998.00    18.027  -4.594  0.000   0.000
+43999.00    18.196  -4.521  0.000   0.000
+44000.00    18.152  -4.44   0.000   0.000
+44001.00    17.938  -4.343  0.000   0.000
+44002.00    17.668  -4.2    0.000   0.000
+44003.00    17.43   -4.039  0.000   0.000
+44004.00    17.28   -3.969  0.000   0.000
+44005.00    17.239  -4.081  0.000   0.000
+44006.00    17.267  -4.329  0.000   0.000
+44007.00    17.257  -4.539  0.000   0.000
+44008.00    17.119  -4.575  0.000   0.000
+44009.00    16.876  -4.455  0.000   0.000
+44010.00    16.676  -4.3    0.000   0.000
+44011.00    16.693  -4.203  0.000   0.000
+44012.00    16.984  -4.158  0.000   0.000
+44013.00    17.407  -4.098  0.000   0.000
+44014.00    17.678  -3.985  0.000   0.000
+44015.00    17.565  -3.866  0.000   0.000
+44016.00    17.079  -3.841  0.000   0.000
+44017.00    16.499  -3.953  0.000   0.000
+44018.00    16.161  -4.121  0.000   0.000
+44019.00    16.197  -4.217  0.000   0.000
+44020.00    16.45   -4.209  0.000   0.000
+44021.00    16.656  -4.185  0.000   0.000
+44022.00    16.69   -4.236  0.000   0.000
+44023.00    16.608  -4.356  0.000   0.000
+44024.00    16.509  -4.476  0.000   0.000
+44025.00    16.445  -4.546  0.000   0.000
+44026.00    16.437  -4.562  0.000   0.000
+44027.00    16.474  -4.542  0.000   0.000
+44028.00    16.448  -4.499  0.000   0.000
+44029.00    16.196  -4.419  0.000   0.000
+44030.00    15.694  -4.266  0.000   0.000
+44031.00    15.157  -4.032  0.000   0.000
+44032.00    14.844  -3.813  0.000   0.000
+44033.00    14.802  -3.765  0.000   0.000
+44034.00    14.87   -3.941  0.000   0.000
+44035.00    14.886  -4.183  0.000   0.000
+44036.00    14.804  -4.255  0.000   0.000
+44037.00    14.634  -4.078  0.000   0.000
+44038.00    14.393  -3.792  0.000   0.000
+44039.00    14.146  -3.603  0.000   0.000
+44040.00    14.028  -3.593  0.000   0.000
+44041.00    14.118  -3.693  0.000   0.000
+44042.00    14.316  -3.789  0.000   0.000
+44043.00    14.369  -3.826  0.000   0.000
+44044.00    14.079  -3.828  0.000   0.000
+44045.00    13.513  -3.851  0.000   0.000
+44046.00    12.983  -3.917  0.000   0.000
+44047.00    12.767  -4.005  0.000   0.000
+44048.00    12.865  -4.084  0.000   0.000
+44049.00    13.061  -4.139  0.000   0.000
+44050.00    13.187  -4.159  0.000   0.000
+44051.00    13.209  -4.14   0.000   0.000
+44052.00    13.114  -4.11   0.000   0.000
+44053.00    12.886  -4.119  0.000   0.000
+44054.00    12.635  -4.176  0.000   0.000
+44055.00    12.542  -4.232  0.000   0.000
+44056.00    12.554  -4.237  0.000   0.000
+44057.00    12.335  -4.194  0.000   0.000
+44058.00    11.678  -4.108  0.000   0.000
+44059.00    10.853  -3.942  0.000   0.000
+44060.00    10.345  -3.697  0.000   0.000
+44061.00    10.295  -3.506  0.000   0.000
+44062.00    10.425  -3.526  0.000   0.000
+44063.00    10.456  -3.727  0.000   0.000
+44064.00    10.384  -3.876  0.000   0.000
+44065.00    10.318  -3.79   0.000   0.000
+44066.00    10.231  -3.546  0.000   0.000
+44067.00    10.016  -3.371  0.000   0.000
+44068.00    9.674   -3.389  0.000   0.000
+44069.00    9.359   -3.533  0.000   0.000
+44070.00    9.234   -3.688  0.000   0.000
+44071.00    9.299   -3.811  0.000   0.000
+44072.00    9.356   -3.913  0.000   0.000
+44073.00    9.183   -3.994  0.000   0.000
+44074.00    8.783   -4.049  0.000   0.000
+44075.00    8.405   -4.11   0.000   0.000
+44076.00    8.286   -4.208  0.000   0.000
+44077.00    8.427   -4.304  0.000   0.000
+44078.00    8.66    -4.293  0.000   0.000
+44079.00    8.822   -4.127  0.000   0.000
+44080.00    8.799   -3.905  0.000   0.000
+44081.00    8.553   -3.801  0.000   0.000
+44082.00    8.225   -3.882  0.000   0.000
+44083.00    8.073   -4.039  0.000   0.000
+44084.00    8.128   -4.13   0.000   0.000
+44085.00    8.041   -4.142  0.000   0.000
+44086.00    7.489   -4.138  0.000   0.000
+44087.00    6.665   -4.105  0.000   0.000
+44088.00    6.111   -3.972  0.000   0.000
+44089.00    6.076   -3.777  0.000   0.000
+44090.00    6.28    -3.693  0.000   0.000
+44091.00    6.349   -3.803  0.000   0.000
+44092.00    6.239   -3.963  0.000   0.000
+44093.00    6.139   -3.973  0.000   0.000
+44094.00    6.13    -3.837  0.000   0.000
+44095.00    6.091   -3.73   0.000   0.000
+44096.00    5.853   -3.731  0.000   0.000
+44097.00    5.401   -3.746  0.000   0.000
+44098.00    4.945   -3.7    0.000   0.000
+44099.00    4.781   -3.683  0.000   0.000
+44100.00    4.988   -3.813  0.000   0.000
+44101.00    5.287   -4.058  0.000   0.000
+44102.00    5.326   -4.268  0.000   0.000
+44103.00    5.09    -4.374  0.000   0.000
+44104.00    4.893   -4.427  0.000   0.000
+44105.00    4.971   -4.455  0.000   0.000
+44106.00    5.245   -4.378  0.000   0.000
+44107.00    5.486   -4.127  0.000   0.000
+44108.00    5.543   -3.798  0.000   0.000
+44109.00    5.404   -3.604  0.000   0.000
+44110.00    5.182   -3.653  0.000   0.000
+44111.00    5.069   -3.841  0.000   0.000
+44112.00    5.142   -3.984  0.000   0.000
+44113.00    5.199   -4.027  0.000   0.000
+44114.00    4.952   -4.047  0.000   0.000
+44115.00    4.426   -4.087  0.000   0.000
+44116.00    3.99    -4.086  0.000   0.000
+44117.00    3.92    -4.009  0.000   0.000
+44118.00    4.098   -3.943  0.000   0.000
+44119.00    4.224   -3.97   0.000   0.000
+44120.00    4.18    -4.024  0.000   0.000
+44121.00    4.083   -3.977  0.000   0.000
+44122.00    4.068   -3.844  0.000   0.000
+44123.00    4.134   -3.77   0.000   0.000
+44124.00    4.123   -3.78   0.000   0.000
+44125.00    3.855   -3.719  0.000   0.000
+44126.00    3.364   -3.491  0.000   0.000
+44127.00    2.996   -3.273  0.000   0.000
+44128.00    3.09    -3.333  0.000   0.000
+44129.00    3.552   -3.698  0.000   0.000
+44130.00    3.93    -4.12   0.000   0.000
+44131.00    3.963   -4.359  0.000   0.000
+44132.00    3.858   -4.392  0.000   0.000
+44133.00    3.938   -4.314  0.000   0.000
+44134.00    4.217   -4.163  0.000   0.000
+44135.00    4.478   -3.911  0.000   0.000
+44136.00    4.594   -3.602  0.000   0.000
+44137.00    4.603   -3.367  0.000   0.000
+44138.00    4.566   -3.305  0.000   0.000
+44139.00    4.529   -3.379  0.000   0.000
+44140.00    4.554   -3.472  0.000   0.000
+44141.00    4.637   -3.519  0.000   0.000
+44142.00    4.656   -3.547  0.000   0.000
+44143.00    4.516   -3.594  0.000   0.000
+44144.00    4.301   -3.649  0.000   0.000
+44145.00    4.197   -3.676  0.000   0.000
+44146.00    4.278   -3.677  0.000   0.000
+44147.00    4.464   -3.653  0.000   0.000
+44148.00    4.628   -3.551  0.000   0.000
+44149.00    4.709   -3.33   0.000   0.000
+44150.00    4.736   -3.081  0.000   0.000
+44151.00    4.787   -2.976  0.000   0.000
+44152.00    4.87    -3.047  0.000   0.000
+44153.00    4.861   -3.1    0.000   0.000
+44154.00    4.659   -2.948  0.000   0.000
+44155.00    4.418   -2.693  0.000   0.000
+44156.00    4.45    -2.643  0.000   0.000
+44157.00    4.829   -2.943  0.000   0.000
+44158.00    5.266   -3.407  0.000   0.000
+44159.00    5.486   -3.723  0.000   0.000
+44160.00    5.558   -3.764  0.000   0.000
+44161.00    5.72    -3.616  0.000   0.000
+44162.00    6.001   -3.406  0.000   0.000
+44163.00    6.238   -3.179  0.000   0.000
+44164.00    6.353   -2.93   0.000   0.000
+44165.00    6.416   -2.673  0.000   0.000
+44166.00    6.458   -2.455  0.000   0.000
+44167.00    6.423   -2.328  0.000   0.000
+44168.00    6.334   -2.314  0.000   0.000
+44169.00    6.317   -2.392  0.000   0.000
+44170.00    6.422   -2.497  0.000   0.000
+44171.00    6.533   -2.569  0.000   0.000
+44172.00    6.52    -2.592  0.000   0.000
+44173.00    6.423   -2.591  0.000   0.000
+44174.00    6.402   -2.578  0.000   0.000
+44175.00    6.568   -2.513  0.000   0.000
+44176.00    6.86    -2.329  0.000   0.000
+44177.00    7.101   -2.019  0.000   0.000
+44178.00    7.157   -1.706  0.000   0.000
+44179.00    7.065   -1.576  0.000   0.000
+44180.00    6.971   -1.696  0.000   0.000
+44181.00    6.958   -1.906  0.000   0.000
+44182.00    6.99    -1.963  0.000   0.000
+44183.00    7.04    -1.814  0.000   0.000
+44184.00    7.178   -1.669  0.000   0.000
+44185.00    7.454   -1.745  0.000   0.000
+44186.00    7.786   -2.017  0.000   0.000
+44187.00    8.056   -2.26   0.000   0.000
+44188.00    8.269   -2.296  0.000   0.000
+44189.00    8.495   -2.148  0.000   0.000
+44190.00    8.715   -1.941  0.000   0.000
+44191.00    8.84    -1.758  0.000   0.000
+44192.00    8.85    -1.578  0.000   0.000
+44193.00    8.811   -1.341  0.000   0.000
+44194.00    8.744   -1.044  0.000   0.000
+44195.00    8.607   -0.783  0.000   0.000
+44196.00    8.429   -0.697  0.000   0.000
+44197.00    8.351   -0.835  0.000   0.000
+44198.00    8.464   -1.08   0.000   0.000
+44199.00    8.669   -1.238  0.000   0.000
+44200.00    8.78    -1.219  0.000   0.000
+44201.00    8.73    -1.087  0.000   0.000
+44202.00    8.64    -0.952  0.000   0.000
+44203.00    8.674   -0.845  0.000   0.000
+44204.00    8.863   -0.719  0.000   0.000
+44205.00    9.06    -0.54   0.000   0.000
+44206.00    9.075   -0.363  0.000   0.000
+44207.00    8.85    -0.306  0.000   0.000
+44208.00    8.526   -0.436  0.000   0.000
+44209.00    8.316   -0.67   0.000   0.000
+44210.00    8.335   -0.819  0.000   0.000
+44211.00    8.53    -0.763  0.000   0.000
+44212.00    8.772   -0.586  0.000   0.000
+44213.00    8.983   -0.484  0.000   0.000
+44214.00    9.18    -0.545  0.000   0.000
+44215.00    9.411   -0.67   0.000   0.000
+44216.00    9.671   -0.711  0.000   0.000
+44217.00    9.884   -0.636  0.000   0.000
+44218.00    9.982   -0.52   0.000   0.000
+44219.00    9.963   -0.433  0.000   0.000
+44220.00    9.862   -0.359  0.000   0.000
+44221.00    9.698   -0.227  0.000   0.000
+44222.00    9.476   0.004   0.000   0.000
+44223.00    9.245   0.271   0.000   0.000
+44224.00    9.098   0.417   0.000   0.000
+44225.00    9.097   0.316   0.000   0.000
+44226.00    9.23    0.024   0.000   0.000
+44227.00    9.429   -0.23   0.000   0.000
+44228.00    9.61    -0.242  0.000   0.000
+44229.00    9.711   -0.017  0.000   0.000
+44230.00    9.722   0.264   0.000   0.000
+44231.00    9.694   0.434   0.000   0.000
+44232.00    9.686   0.468   0.000   0.000
+44233.00    9.689   0.434   0.000   0.000
+44234.00    9.614   0.385   0.000   0.000
+44235.00    9.389   0.313   0.000   0.000
+44236.00    9.058   0.19    0.000   0.000
+44237.00    8.791   0.041   0.000   0.000
+44238.00    8.751   -0.051  0.000   0.000
+44239.00    8.94    -0.017  0.000   0.000
+44240.00    9.19    0.106   0.000   0.000
+44241.00    9.345   0.200   0.000   0.000
+44242.00    9.427   0.185   0.000   0.000
+44243.00    9.562   0.103   0.000   0.000
+44244.00    9.761   0.041   0.000   0.000
+44245.00    9.887   0.03    0.000   0.000
+44246.00    9.853   0.029   0.000   0.000
+44247.00    9.728   0.005   0.000   0.000
+44248.00    9.586   -0.031  0.000   0.000
+44249.00    9.381   -0.038  0.000   0.000
+44250.00    9.088   0.037   0.000   0.000
+44251.00    8.847   0.223   0.000   0.000
+44252.00    8.811   0.449   0.000   0.000
+44253.00    8.917   0.539   0.000   0.000
+44254.00    8.977   0.376   0.000   0.000
+44255.00    8.964   0.074   0.000   0.000
+44256.00    9.054   -0.093  0.000   0.000
+44257.00    9.34    0.037   0.000   0.000
+44258.00    9.674   0.347   0.000   0.000
+44259.00    9.832   0.585   0.000   0.000
+44260.00    9.75    0.62    0.000   0.000
+44261.00    9.532   0.504   0.000   0.000
+44262.00    9.301   0.348   0.000   0.000
+44263.00    9.107   0.196   0.000   0.000
+44264.00    8.949   0.046   0.000   0.000
+44265.00    8.845   -0.083  0.000   0.000
+44266.00    8.849   -0.149  0.000   0.000
+44267.00    8.993   -0.145  0.000   0.000
+44268.00    9.205   -0.124  0.000   0.000
+44269.00    9.365   -0.147  0.000   0.000
+44270.00    9.447   -0.215  0.000   0.000
+44271.00    9.533   -0.28   0.000   0.000
+44272.00    9.645   -0.312  0.000   0.000
+44273.00    9.674   -0.344  0.000   0.000
+44274.00    9.553   -0.413  0.000   0.000
+44275.00    9.38    -0.508  0.000   0.000
+44276.00    9.239   -0.605  0.000   0.000
+44277.00    9.059   -0.706  0.000   0.000
+44278.00    8.803   -0.787  0.000   0.000
+44279.00    8.657   -0.748  0.000   0.000
+44280.00    8.806   -0.528  0.000   0.000
+44281.00    9.07    -0.271  0.000   0.000
+44282.00    9.07    -0.245  0.000   0.000
+44283.00    8.773   -0.538  0.000   0.000
+44284.00    8.600   -0.902  0.000   0.000
+44285.00    8.907   -1.017  0.000   0.000
+44286.00    9.531   -0.834  0.000   0.000
+44287.00    9.988   -0.589  0.000   0.000
+44288.00    9.984   -0.494  0.000   0.000
+44289.00    9.62    -0.546  0.000   0.000
+44290.00    9.213   -0.653  0.000   0.000
+44291.00    9.023   -0.799  0.000   0.000
+44292.00    9.088   -1.012  0.000   0.000
+44293.00    9.253   -1.247  0.000   0.000
+44294.00    9.363   -1.406  0.000   0.000
+44295.00    9.428   -1.462  0.000   0.000
+44296.00    9.567   -1.485  0.000   0.000
+44297.00    9.807   -1.531  0.000   0.000
+44298.00    10.059  -1.561  0.000   0.000
+44299.00    10.245  -1.512  0.000   0.000
+44300.00    10.341  -1.411  0.000   0.000
+44301.00    10.322  -1.368  0.000   0.000
+44302.00    10.183  -1.438  0.000   0.000
+44303.00    10.003  -1.566  0.000   0.000
+44304.00    9.859   -1.683  0.000   0.000
+44305.00    9.709   -1.805  0.000   0.000
+44306.00    9.529   -1.967  0.000   0.000
+44307.00    9.501   -2.083  0.000   0.000
+44308.00    9.821   -2.009  0.000   0.000
+44309.00    10.311  -1.778  0.000   0.000
+44310.00    10.511  -1.652  0.000   0.000
+44311.00    10.26   -1.839  0.000   0.000
+44312.00    9.959   -2.21   0.000   0.000
+44313.00    10.086  -2.436  0.000   0.000
+44314.00    10.612  -2.372  0.000   0.000
+44315.00    11.062  -2.187  0.000   0.000
+44316.00    11.045  -2.093  0.000   0.000
+44317.00    10.598  -2.105  0.000   0.000
+44318.00    10.098  -2.141  0.000   0.000
+44319.00    9.936   -2.226  0.000   0.000
+44320.00    10.203  -2.45   0.000   0.000
+44321.00    10.612  -2.78   0.000   0.000
+44322.00    10.79   -3.045  0.000   0.000
+44323.00    10.696  -3.135  0.000   0.000
+44324.00    10.646  -3.102  0.000   0.000
+44325.00    10.888  -3.033  0.000   0.000
+44326.00    11.307  -2.922  0.000   0.000
+44327.00    11.621  -2.72   0.000   0.000
+44328.00    11.707  -2.479  0.000   0.000
+44329.00    11.627  -2.341  0.000   0.000
+44330.00    11.467  -2.383  0.000   0.000
+44331.00    11.284  -2.532  0.000   0.000
+44332.00    11.125  -2.677  0.000   0.000
+44333.00    10.989  -2.801  0.000   0.000
+44334.00    10.859  -2.962  0.000   0.000
+44335.00    10.829  -3.143  0.000   0.000
+44336.00    11.067  -3.227  0.000   0.000
+44337.00    11.551  -3.157  0.000   0.000
+44338.00    11.991  -3.051  0.000   0.000
+44339.00    12.138  -3.067  0.000   0.000
+44340.00    12.078  -3.185  0.000   0.000
+44341.00    12.069  -3.228  0.000   0.000
+44342.00    12.169  -3.123  0.000   0.000
+44343.00    12.176  -3.007  0.000   0.000
+44344.00    11.904  -3.031  0.000   0.000
+44345.00    11.408  -3.148  0.000   0.000
+44346.00    10.976  -3.216  0.000   0.000
+44347.00    10.925  -3.24   0.000   0.000
+44348.00    11.322  -3.367  0.000   0.000
+44349.00    11.839  -3.647  0.000   0.000
+44350.00    12.018  -3.925  0.000   0.000
+44351.00    11.772  -4.027  0.000   0.000
+44352.00    11.503  -3.946  0.000   0.000
+44353.00    11.61   -3.787  0.000   0.000
+44354.00    12.018  -3.604  0.000   0.000
+44355.00    12.332  -3.379  0.000   0.000
+44356.00    12.341  -3.135  0.000   0.000
+44357.00    12.156  -2.967  0.000   0.000
+44358.00    11.947  -2.95   0.000   0.000
+44359.00    11.769  -3.06   0.000   0.000
+44360.00    11.635  -3.213  0.000   0.000
+44361.00    11.567  -3.356  0.000   0.000
+44362.00    11.534  -3.489  0.000   0.000
+44363.00    11.476  -3.617  0.000   0.000
+44364.00    11.438  -3.71   0.000   0.000
+44365.00    11.552  -3.734  0.000   0.000
+44366.00    11.871  -3.689  0.000   0.000
+44367.00    12.282  -3.599  0.000   0.000
+44368.00    12.601  -3.453  0.000   0.000
+44369.00    12.684  -3.232  0.000   0.000
+44370.00    12.464  -3.001  0.000   0.000
+44371.00    11.97   -2.918  0.000   0.000
+44372.00    11.347  -3.075  0.000   0.000
+44373.00    10.806  -3.353  0.000   0.000
+44374.00    10.542  -3.528  0.000   0.000
+44375.00    10.66   -3.527  0.000   0.000
+44376.00    11.099  -3.498  0.000   0.000
+44377.00    11.564  -3.596  0.000   0.000
+44378.00    11.695  -3.784  0.000   0.000
+44379.00    11.414  -3.897  0.000   0.000
+44380.00    11.044  -3.859  0.000   0.000
+44381.00    10.958  -3.738  0.000   0.000
+44382.00    11.144  -3.616  0.000   0.000
+44383.00    11.281  -3.499  0.000   0.000
+44384.00    11.158  -3.354  0.000   0.000
+44385.00    10.86   -3.186  0.000   0.000
+44386.00    10.559  -3.045  0.000   0.000
+44387.00    10.331  -2.99   0.000   0.000
+44388.00    10.201  -3.048  0.000   0.000
+44389.00    10.206  -3.188  0.000   0.000
+44390.00    10.305  -3.328  0.000   0.000
+44391.00    10.33   -3.394  0.000   0.000
+44392.00    10.163  -3.384  0.000   0.000
+44393.00    9.916   -3.348  0.000   0.000
+44394.00    9.844   -3.314  0.000   0.000
+44395.00    10.078  -3.245  0.000   0.000
+44396.00    10.469  -3.083  0.000   0.000
+44397.00    10.684  -2.835  0.000   0.000
+44398.00    10.457  -2.607  0.000   0.000
+44399.00    9.787   -2.552  0.000   0.000
+44400.00    8.946   -2.741  0.000   0.000
+44401.00    8.299   -3.07   0.000   0.000
+44402.00    8.073   -3.317  0.000   0.000
+44403.00    8.267   -3.35   0.000   0.000
+44404.00    8.683   -3.247  0.000   0.000
+44405.00    9.045   -3.188  0.000   0.000
+44406.00    9.135   -3.248  0.000   0.000
+44407.00    8.92    -3.34   0.000   0.000
+44408.00    8.566   -3.371  0.000   0.000
+44409.00    8.28    -3.351  0.000   0.000
+44410.00    8.121   -3.343  0.000   0.000
+44411.00    7.988   -3.349  0.000   0.000
+44412.00    7.765   -3.3    0.000   0.000
+44413.00    7.422   -3.137  0.000   0.000
+44414.00    7.01    -2.883  0.000   0.000
+44415.00    6.629   -2.646  0.000   0.000
+44416.00    6.395   -2.556  0.000   0.000
+44417.00    6.382   -2.662  0.000   0.000
+44418.00    6.533   -2.864  0.000   0.000
+44419.00    6.666   -2.992  0.000   0.000
+44420.00    6.614   -2.956  0.000   0.000
+44421.00    6.375   -2.827  0.000   0.000
+44422.00    6.114   -2.732  0.000   0.000
+44423.00    6.016   -2.716  0.000   0.000
+44424.00    6.12    -2.723  0.000   0.000
+44425.00    6.263   -2.691  0.000   0.000
+44426.00    6.193   -2.637  0.000   0.000
+44427.00    5.761   -2.644  0.000   0.000
+44428.00    5.068   -2.776  0.000   0.000
+44429.00    4.42    -3.011  0.000   0.000
+44430.00    4.12    -3.234  0.000   0.000
+44431.00    4.256   -3.328  0.000   0.000
+44432.00    4.644   -3.288  0.000   0.000
+44433.00    5.002   -3.203  0.000   0.000
+44434.00    5.152   -3.154  0.000   0.000
+44435.00    5.073   -3.137  0.000   0.000
+44436.00    4.808   -3.125  0.000   0.000
+44437.00    4.418   -3.142  0.000   0.000
+44438.00    4.024   -3.22   0.000   0.000
+44439.00    3.763   -3.32   0.000   0.000
+44440.00    3.629   -3.341  0.000   0.000
+44441.00    3.428   -3.218  0.000   0.000
+44442.00    3.007   -2.976  0.000   0.000
+44443.00    2.484   -2.713  0.000   0.000
+44444.00    2.138   -2.558  0.000   0.000
+44445.00    2.101   -2.609  0.000   0.000
+44446.00    2.251   -2.84   0.000   0.000
+44447.00    2.396   -3.078  0.000   0.000
+44448.00    2.457   -3.132  0.000   0.000
+44449.00    2.433   -2.977  0.000   0.000
+44450.00    2.307   -2.78   0.000   0.000
+44451.00    2.085   -2.715  0.000   0.000
+44452.00    1.853   -2.803  0.000   0.000
+44453.00    1.712   -2.935  0.000   0.000
+44454.00    1.663   -3.022  0.000   0.000
+44455.00    1.582   -3.072  0.000   0.000
+44456.00    1.357   -3.143  0.000   0.000
+44457.00    1.028   -3.262  0.000   0.000
+44458.00    0.797   -3.398  0.000   0.000
+44459.00    0.852   -3.5    0.000   0.000
+44460.00    1.178   -3.533  0.000   0.000
+44461.00    1.569   -3.487  0.000   0.000
+44462.00    1.827   -3.358  0.000   0.000
+44463.00    1.881   -3.163  0.000   0.000
+44464.00    1.728   -2.979  0.000   0.000
+44465.00    1.393   -2.921  0.000   0.000
+44466.00    1.033   -3.029  0.000   0.000
+44467.00    0.895   -3.205  0.000   0.000
+44468.00    1.017   -3.297  0.000   0.000
+44469.00    1.082   -3.255  0.000   0.000
+44470.00    0.771   -3.136  0.000   0.000
+44471.00    0.200   -3.009  0.000   0.000
+44472.00    -0.191  -2.91   0.000   0.000
+44473.00    -0.189  -2.895  0.000   0.000
+44474.00    0.000   -3.025  0.000   0.000
+44475.00    0.109   -3.242  0.000   0.000
+44476.00    0.14    -3.348  0.000   0.000
+44477.00    0.207   -3.213  0.000   0.000
+44478.00    0.265   -2.945  0.000   0.000
+44479.00    0.145   -2.779  0.000   0.000
+44480.00    -0.195  -2.809  0.000   0.000
+44481.00    -0.591  -2.925  0.000   0.000
+44482.00    -0.829  -2.997  0.000   0.000
+44483.00    -0.83   -3.032  0.000   0.000
+44484.00    -0.689  -3.114  0.000   0.000
+44485.00    -0.583  -3.254  0.000   0.000
+44486.00    -0.58   -3.38   0.000   0.000
+44487.00    -0.562  -3.445  0.000   0.000
+44488.00    -0.375  -3.457  0.000   0.000
+44489.00    -0.035  -3.409  0.000   0.000
+44490.00    0.288   -3.246  0.000   0.000
+44491.00    0.45    -2.942  0.000   0.000
+44492.00    0.421   -2.612  0.000   0.000
+44493.00    0.264   -2.449  0.000   0.000
+44494.00    0.133   -2.537  0.000   0.000
+44495.00    0.227   -2.753  0.000   0.000
+44496.00    0.562   -2.904  0.000   0.000
+44497.00    0.823   -2.937  0.000   0.000
+44498.00    0.659   -2.944  0.000   0.000
+44499.00    0.159   -2.978  0.000   0.000
+44500.00    -0.207  -2.983  0.000   0.000
+44501.00    -0.154  -2.921  0.000   0.000
+44502.00    0.119   -2.873  0.000   0.000
+44503.00    0.289   -2.903  0.000   0.000
+44504.00    0.329   -2.918  0.000   0.000
+44505.00    0.411   -2.77   0.000   0.000
+44506.00    0.563   -2.503  0.000   0.000
+44507.00    0.623   -2.327  0.000   0.000
+44508.00    0.467   -2.344  0.000   0.000
+44509.00    -0.66   -2.476  0.000   0.000
+44510.00    -0.993  -2.515  0.000   0.000
+44511.00    -1.152  -2.512  0.000   0.000
+44512.00    -1.074  -2.591  0.000   0.000
+44513.00    -0.882  -2.771  0.000   0.000
+44514.00    -0.806  -2.929  0.000   0.000
+44515.00    -0.93   -2.945  0.000   0.000
+44516.00    -1.047  -2.867  0.000   0.000
+44517.00    -0.949  -2.792  0.000   0.000
+44518.00    -0.721  -2.706  0.000   0.000
+44519.00    -0.58   -2.539  0.000   0.000
+44520.00    -0.6    -2.332  0.000   0.000
+44521.00    -0.682  -2.248  0.000   0.000
+44522.00    -0.679  -2.389  0.000   0.000
+44523.00    -0.488  -2.663  0.000   0.000
+44524.00    -0.112  -2.889  0.000   0.000
+44525.00    0.27    -2.996  0.000   0.000
+44526.00    0.426   -3.053  0.000   0.000
+44527.00    0.377   -3.098  0.000   0.000
+44528.00    0.448   -3.038  0.000   0.000
+44529.00    0.914   -2.765  0.000   0.000
+44530.00    1.707   -2.315  0.000   0.000
+44531.00    2.454   -1.924  0.000   0.000
+44532.00    2.932   -1.681  0.000   0.000
+44533.00    3.217   -1.473  0.000   0.000
+44534.00    3.424   -1.304  0.000   0.000
+44535.00    3.623   -1.282  0.000   0.000
+44536.00    3.776   -1.448  0.000   0.000
+44537.00    3.846   -1.637  0.000   0.000
+44538.00    3.88    -1.654  0.000   0.000
+44539.00    3.991   -1.576  0.000   0.000
+44540.00    4.303   -1.656  0.000   0.000
+44541.00    4.754   -1.974  0.000   0.000
+44542.00    5.083   -2.304  0.000   0.000
+44543.00    5.15    -2.395  0.000   0.000
+44544.00    5.151   -2.247  0.000   0.000
+44545.00    5.359   -2.037  0.000   0.000
+44546.00    5.749   -1.87   0.000   0.000
+44547.00    6.058   -1.705  0.000   0.000
+44548.00    6.152   -1.504  0.000   0.000
+44549.00    6.138   -1.333  0.000   0.000
+44550.00    6.149   -1.285  0.000   0.000
+44551.00    6.215   -1.363  0.000   0.000
+44552.00    6.325   -1.489  0.000   0.000
+44553.00    6.457   -1.602  0.000   0.000
+44554.00    6.532   -1.71   0.000   0.000
+44555.00    6.468   -1.825  0.000   0.000
+44556.00    6.313   -1.899  0.000   0.000
+44557.00    6.222   -1.867  0.000   0.000
+44558.00    6.284   -1.724  0.000   0.000
+44559.00    6.454   -1.52   0.000   0.000
+44560.00    6.627   -1.287  0.000   0.000
+44561.00    6.698   -1.036  0.000   0.000
+44562.00    6.591   -0.847  0.000   0.000
+44563.00    6.321   -0.861  0.000   0.000
+44564.00    6.026   -1.095  0.000   0.000
+44565.00    5.843   -1.34   0.000   0.000
+44566.00    5.802   -1.351  0.000   0.000
+44567.00    5.887   -1.149  0.000   0.000
+44568.00    6.122   -1.01   0.000   0.000
+44569.00    6.463   -1.12   0.000   0.000
+44570.00    6.723   -1.343  0.000   0.000
+44571.00    6.755   -1.404  0.000   0.000
+44572.00    6.668   -1.216  0.000   0.000
+44573.00    6.682   -0.931  0.000   0.000
+44574.00    6.81    -0.72   0.000   0.000
+44575.00    6.859   -0.593  0.000   0.000
+44576.00    6.738   -0.464  0.000   0.000
+44577.00    6.567   -0.297  0.000   0.000
+44578.00    6.467   -0.144  0.000   0.000
+44579.00    6.424   -0.084  0.000   0.000
+44580.00    6.414   -0.147  0.000   0.000
+44581.00    6.513   -0.292  0.000   0.000
+44582.00    6.753   -0.43   0.000   0.000
+44583.00    7.011   -0.487  0.000   0.000
+44584.00    7.125   -0.447  0.000   0.000
+44585.00    7.097   -0.348  0.000   0.000
+44586.00    7.073   -0.226  0.000   0.000
+44587.00    7.173   -0.082  0.000   0.000
+44588.00    7.355   0.104   0.000   0.000
+44589.00    7.447   0.314   0.000   0.000
+44590.00    7.287   0.459   0.000   0.000
+44591.00    6.876   0.426   0.000   0.000
+44592.00    6.414   0.208   0.000   0.000
+44593.00    6.137   -0.028  0.000   0.000
+44594.00    6.146   -0.061  0.000   0.000
+44595.00    6.389   0.151   0.000   0.000
+44596.00    6.78    0.399   0.000   0.000
+44597.00    7.231   0.452   0.000   0.000
+44598.00    7.625   0.317   0.000   0.000
+44599.00    7.853   0.204   0.000   0.000
+44600.00    7.91    0.267   0.000   0.000
+44601.00    7.867   0.445   0.000   0.000
+44602.00    7.749   0.587   0.000   0.000
+44603.00    7.536   0.639   0.000   0.000
+44604.00    7.275   0.677   0.000   0.000
+44605.00    7.091   0.78    0.000   0.000
+44606.00    7.042   0.932   0.000   0.000
+44607.00    7.058   1.024   0.000   0.000
+44608.00    7.082   0.949   0.000   0.000
+44609.00    7.173   0.707   0.000   0.000
+44610.00    7.41    0.436   0.000   0.000
+44611.00    7.738   0.309   0.000   0.000
+44612.00    7.998   0.377   0.000   0.000
+44613.00    8.095   0.534   0.000   0.000
+44614.00    8.075   0.642   0.000   0.000
+44615.00    8.039   0.663   0.000   0.000
+44616.00    8.014   0.65    0.000   0.000
+44617.00    7.922   0.651   0.000   0.000
+44618.00    7.666   0.642   0.000   0.000
+44619.00    7.246   0.563   0.000   0.000
+44620.00    6.809   0.392   0.000   0.000
+44621.00    6.555   0.207   0.000   0.000
+44622.00    6.596   0.13    0.000   0.000
+44623.00    6.874   0.209   0.000   0.000
+44624.00    7.24    0.336   0.000   0.000
+44625.00    7.575   0.34    0.000   0.000
+44626.00    7.843   0.174   0.000   0.000
+44627.00    8.038   -0.033  0.000   0.000
+44628.00    8.108   -0.131  0.000   0.000
+44629.00    7.982   -0.106  0.000   0.000
+44630.00    7.664   -0.062  0.000   0.000
+44631.00    7.283   -0.065  0.000   0.000
+44632.00    7.014   -0.078  0.000   0.000
+44633.00    6.942   -0.036  0.000   0.000
+44634.00    7.028   0.07    0.000   0.000
+44635.00    7.181   0.164   0.000   0.000
+44636.00    7.331   0.13    0.000   0.000
+44637.00    7.448   -0.091  0.000   0.000
+44638.00    7.552   -0.421  0.000   0.000
+44639.00    7.718   -0.665  0.000   0.000
+44640.00    8.005   -0.668  0.000   0.000
+44641.00    8.358   -0.47   0.000   0.000
+44642.00    8.617   -0.271  0.000   0.000
+44643.00    8.647   -0.236  0.000   0.000
+44644.00    8.459   -0.366  0.000   0.000
+44645.00    8.165   -0.551  0.000   0.000
+44646.00    7.869   -0.7    0.000   0.000
+44647.00    7.62    -0.801  0.000   0.000
+44648.00    7.469   -0.885  0.000   0.000
+44649.00    7.494   -0.968  0.000   0.000
+44650.00    7.751   -1.034  0.000   0.000
+44651.00    8.178   -1.07   0.000   0.000
+44652.00    8.604   -1.112  0.000   0.000
+44653.00    8.878   -1.22   0.000   0.000
+44654.00    9.003   -1.401  0.000   0.000
+44655.00    9.078   -1.577  0.000   0.000
+44656.00    9.115   -1.662  0.000   0.000
+44657.00    9.008   -1.663  0.000   0.000
+44658.00    8.725   -1.653  0.000   0.000
+44659.00    8.427   -1.678  0.000   0.000
+44660.00    8.301   -1.722  0.000   0.000
+44661.00    8.363   -1.757  0.000   0.000
+44662.00    8.52    -1.768  0.000   0.000
+44663.00    8.753   -1.751  0.000   0.000
+44664.00    9.072   -1.734  0.000   0.000
+44665.00    9.347   -1.809  0.000   0.000
+44666.00    9.41    -2.049  0.000   0.000
+44667.00    9.351   -2.368  0.000   0.000
+44668.00    9.494   -2.537  0.000   0.000
+44669.00    9.979   -2.422  0.000   0.000
+44670.00    10.513  -2.158  0.000   0.000
+44671.00    10.673  -2.024  0.000   0.000
+44672.00    10.361  -2.153  0.000   0.000
+44673.00    9.865   -2.435  0.000   0.000
+44674.00    9.521   -2.679  0.000   0.000
+44675.00    9.448   -2.802  0.000   0.000
+44676.00    9.568   -2.843  0.000   0.000
+44677.00    9.786   -2.862  0.000   0.000
+44678.00    10.086  -2.887  0.000   0.000
+44679.00    10.482  -2.933  0.000   0.000
+44680.00    10.91   -3.018  0.000   0.000
+44681.00    11.232  -3.13   0.000   0.000
+44682.00    11.369  -3.209  0.000   0.000
+44683.00    11.377  -3.191  0.000   0.000
+44684.00    11.335  -3.088  0.000   0.000
+44685.00    11.23   -2.995  0.000   0.000
+44686.00    11.045  -2.988  0.000   0.000
+44687.00    10.884  -3.06   0.000   0.000
+44688.00    10.855  -3.161  0.000   0.000
+44689.00    10.907  -3.285  0.000   0.000
+44690.00    10.952  -3.444  0.000   0.000
+44691.00    11.097  -3.573  0.000   0.000
+44692.00    11.502  -3.573  0.000   0.000
+44693.00    12.021  -3.482  0.000   0.000
+44694.00    12.267  -3.493  0.000   0.000
+44695.00    12.143  -3.708  0.000   0.000
+44696.00    12.038  -3.955  0.000   0.000
+44697.00    12.305  -3.967  0.000   0.000
+44698.00    12.74   -3.735  0.000   0.000
+44699.00    12.817  -3.539  0.000   0.000
+44700.00    12.349  -3.61   0.000   0.000
+44701.00    11.686  -3.883  0.000   0.000
+44702.00    11.319  -4.131  0.000   0.000
+44703.00    11.417  -4.246  0.000   0.000
+44704.00    11.779  -4.286  0.000   0.000
+44705.00    12.088  -4.313  0.000   0.000
+44706.00    12.198  -4.317  0.000   0.000
+44707.00    12.231  -4.291  0.000   0.000
+44708.00    12.391  -4.274  0.000   0.000
+44709.00    12.692  -4.275  0.000   0.000
+44710.00    12.941  -4.214  0.000   0.000
+44711.00    12.972  -4.018  0.000   0.000
+44712.00    12.813  -3.749  0.000   0.000
+44713.00    12.589  -3.577  0.000   0.000
+44714.00    12.391  -3.604  0.000   0.000
+44715.00    12.277  -3.762  0.000   0.000
+44716.00    12.253  -3.93   0.000   0.000
+44717.00    12.221  -4.089  0.000   0.000
+44718.00    12.1    -4.295  0.000   0.000
+44719.00    12.031  -4.51   0.000   0.000
+44720.00    12.278  -4.572  0.000   0.000
+44721.00    12.818  -4.416  0.000   0.000
+44722.00    13.242  -4.213  0.000   0.000
+44723.00    13.24   -4.179  0.000   0.000
+44724.00    12.987  -4.292  0.000   0.000
+44725.00    12.84   -4.323  0.000   0.000
+44726.00    12.801  -4.175  0.000   0.000
+44727.00    12.536  -4.03   0.000   0.000
+44728.00    11.898  -4.094  0.000   0.000
+44729.00    11.189  -4.313  0.000   0.000
+44730.00    10.86   -4.477  0.000   0.000
+44731.00    11.088  -4.511  0.000   0.000
+44732.00    11.638  -4.53   0.000   0.000
+44733.00    12.052  -4.604  0.000   0.000
+44734.00    12.015  -4.65   0.000   0.000
+44735.00    11.639  -4.579  0.000   0.000
+44736.00    11.356  -4.441  0.000   0.000
+44737.00    11.432  -4.33   0.000   0.000
+44738.00    11.674  -4.222  0.000   0.000
+44739.00    11.7    -4.027  0.000   0.000
+44740.00    11.398  -3.762  0.000   0.000
+44741.00    10.979  -3.586  0.000   0.000
+44742.00    10.67   -3.616  0.000   0.000
+44743.00    10.523  -3.792  0.000   0.000
+44744.00    10.471  -3.961  0.000   0.000
+44745.00    10.403  -4.067  0.000   0.000
+44746.00    10.221  -4.171  0.000   0.000
+44747.00    9.963   -4.305  0.000   0.000
+44748.00    9.834   -4.371  0.000   0.000
+44749.00    9.977   -4.259  0.000   0.000
+44750.00    10.24   -4.015  0.000   0.000
+44751.00    10.32   -3.798  0.000   0.000
+44752.00    10.094  -3.684  0.000   0.000
+44753.00    9.659   -3.604  0.000   0.000
+44754.00    9.099   -3.51   0.000   0.000
+44755.00    8.398   -3.492  0.000   0.000
+44756.00    7.611   -3.636  0.000   0.000
+44757.00    6.967   -3.838  0.000   0.000
+44758.00    6.715   -3.902  0.000   0.000
+44759.00    6.93    -3.808  0.000   0.000
+44760.00    7.427   -3.743  0.000   0.000
+44761.00    7.812   -3.829  0.000   0.000
+44762.00    7.712   -3.947  0.000   0.000
+44763.00    7.121   -3.918  0.000   0.000
+44764.00    6.457   -3.744  0.000   0.000
+44765.00    6.132   -3.579  0.000   0.000
+44766.00    6.094   -3.497  0.000   0.000
+44767.00    5.952   -3.419  0.000   0.000
+44768.00    5.503   -3.267  0.000   0.000
+44769.00    4.929   -3.098  0.000   0.000
+44770.00    4.485   -3.03   0.000   0.000
+44771.00    4.218   -3.088  0.000   0.000
+44772.00    4.044   -3.193  0.000   0.000
+44773.00    3.901   -3.259  0.000   0.000
+44774.00    3.722   -3.272  0.000   0.000
+44775.00    3.418   -3.268  0.000   0.000
+44776.00    3.002   -3.254  0.000   0.000
+44777.00    2.643   -3.198  0.000   0.000
+44778.00    2.482   -3.07   0.000   0.000
+44779.00    2.444   -2.886  0.000   0.000
+44780.00    2.289   -2.686  0.000   0.000
+44781.00    1.816   -2.515  0.000   0.000
+44782.00    1.002   -2.437  0.000   0.000
+44783.00    0.012   -2.522  0.000   0.000
+44784.00    -0.884  -2.761  0.000   0.000
+44785.00    -1.453  -2.995  0.000   0.000
+44786.00    -1.627  -3.033  0.000   0.000
+44787.00    -1.491  -2.871  0.000   0.000
+44788.00    -1.212  -2.716  0.000   0.000
+44789.00    -1.024  -2.743  0.000   0.000
+44790.00    -1.184  -2.885  0.000   0.000
+44791.00    -1.778  -2.932  0.000   0.000
+44792.00    -2.583  -2.81   0.000   0.000
+44793.00    -3.251  -2.653  0.000   0.000
+44794.00    -3.671  -2.597  0.000   0.000
+44795.00    -4.022  -2.603  0.000   0.000
+44796.00    -4.472  -2.541  0.000   0.000
+44797.00    -4.978  -2.366  0.000   0.000
+44798.00    -5.431  -2.175  0.000   0.000
+44799.00    -5.825  -2.097  0.000   0.000
+44800.00    -6.181  -2.168  0.000   0.000
+44801.00    -6.448  -2.309  0.000   0.000
+44802.00    -6.597  -2.393  0.000   0.000
+44803.00    -6.749  -2.353  0.000   0.000
+44804.00    -7.057  -2.232  0.000   0.000
+44805.00    -7.495  -2.129  0.000   0.000
+44806.00    -7.868  -2.086  0.000   0.000
+44807.00    -8.062  -2.051  0.000   0.000
+44808.00    -8.198  -1.963  0.000   0.000
+44809.00    -8.523  -1.839  0.000   0.000
+44810.00    -9.169  -1.774  0.000   0.000
+44811.00    -10.015 -1.854  0.000   0.000
+44812.00    -10.769 -2.063  0.000   0.000
+44813.00    -11.185 -2.267  0.000   0.000
+44814.00    -11.222 -2.316  0.000   0.000
+44815.00    -11.029 -2.18   0.000   0.000
+44816.00    -10.802 -1.997  0.000   0.000
+44817.00    -10.691 -1.93   0.000   0.000
+44818.00    -10.809 -1.983  0.000   0.000
+44819.00    -11.207 -2.018  0.000   0.000
+44820.00    -11.829 -1.944  0.000   0.000
+44821.00    -12.498 -1.835  0.000   0.000
+44822.00    -13.03  -1.807  0.000   0.000
+44823.00    -13.351 -1.847  0.000   0.000
+44824.00    -13.52  -1.826  0.000   0.000
+44825.00    -13.674 -1.67   0.000   0.000
+44826.00    -13.941 -1.459  0.000   0.000
+44827.00    -14.349 -1.356  0.000   0.000
+44828.00    -14.78  -1.455  0.000   0.000
+44829.00    -15.058 -1.692  0.000   0.000
+44830.00    -15.112 -1.888  0.000   0.000
+44831.00    -15.047 -1.88   0.000   0.000
+44832.00    -15.044 -1.668  0.000   0.000
+44833.00    -15.192 -1.42   0.000   0.000
+44834.00    -15.445 -1.312  0.000   0.000
+44835.00    -15.702 -1.367  0.000   0.000
+44836.00    -15.925 -1.466  0.000   0.000
+44837.00    -16.16  -1.501  0.000   0.000
+44838.00    -16.474 -1.473  0.000   0.000
+44839.00    -16.855 -1.456  0.000   0.000
+44840.00    -17.191 -1.495  0.000   0.000
+44841.00    -17.331 -1.561  0.000   0.000
+44842.00    -17.205 -1.584  0.000   0.000
+44843.00    -16.882 -1.519  0.000   0.000
+44844.00    -16.534 -1.399  0.000   0.000
+44845.00    -16.316 -1.29   0.000   0.000
+44846.00    -16.283 -1.215  0.000   0.000
+44847.00    -16.412 -1.135  0.000   0.000
+44848.00    -16.67  -1.035  0.000   0.000
+44849.00    -17.002 -0.98   0.000   0.000
+44850.00    -17.26  -1.033  0.000   0.000
+44851.00    -17.259 -1.147  0.000   0.000
+44852.00    -16.985 -1.195  0.000   0.000
+44853.00    -16.7   -1.121  0.000   0.000
+44854.00    -16.698 -1.009  0.000   0.000
+44855.00    -16.98  -0.988  0.000   0.000
+44856.00    -17.252 -1.106  0.000   0.000
+44857.00    -17.272 -1.306  0.000   0.000
+44858.00    -17.063 -1.463  0.000   0.000
+44859.00    -16.79  -1.446  0.000   0.000
+44860.00    -16.55  -1.211  0.000   0.000
+44861.00    -16.371 -0.88   0.000   0.000
+44862.00    -16.3   -0.683  0.000   0.000
+44863.00    -16.378 -0.758  0.000   0.000
+44864.00    -16.544 -1.015  0.000   0.000
+44865.00    -16.666 -1.233  0.000   0.000
+44866.00    -16.666 -1.274  0.000   0.000
+44867.00    -16.562 -1.181  0.000   0.000
+44868.00    -16.403 -1.081  0.000   0.000
+44869.00    -16.184 -1.045  0.000   0.000
+44870.00    -15.851 -1.061  0.000   0.000
+44871.00    -15.377 -1.086  0.000   0.000
+44872.00    -14.834 -1.092  0.000   0.000
+44873.00    -14.361 -1.057  0.000   0.000
+44874.00    -14.04  -0.956  0.000   0.000
+44875.00    -13.835 -0.787  0.000   0.000
+44876.00    -13.666 -0.622  0.000   0.000
+44877.00    -13.482 -0.587  0.000   0.000
+44878.00    -13.205 -0.736  0.000   0.000
+44879.00    -12.702 -0.969  0.000   0.000
+44880.00    -11.976 -1.126  0.000   0.000
+44881.00    -11.306 -1.161  0.000   0.000
+44882.00    -11.003 -1.168  0.000   0.000
+44883.00    -10.996 -1.23   0.000   0.000
+44884.00    -10.879 -1.314  0.000   0.000
+44885.00    -10.405 -1.349  0.000   0.000
+44886.00    -9.746  -1.327  0.000   0.000
+44887.00    -9.179  -1.255  0.000   0.000
+44888.00    -8.722  -1.096  0.000   0.000
+44889.00    -8.238  -0.85   0.000   0.000
+44890.00    -7.731  -0.667  0.000   0.000
+44891.00    -7.332  -0.74   0.000   0.000
+44892.00    -7.055  -1.057  0.000   0.000
+44893.00    -6.75   -1.37   0.000   0.000
+44894.00    -6.302  -1.455  0.000   0.000
+44895.00    -5.734  -1.343  0.000   0.000
+44896.00    -5.134  -1.234  0.000   0.000
+44897.00    -4.548  -1.249  0.000   0.000
+44898.00    -3.957  -1.347  0.000   0.000
+44899.00    -3.296  -1.445  0.000   0.000
+44900.00    -2.525  -1.525  0.000   0.000
+44901.00    -1.709  -1.585  0.000   0.000
+44902.00    -0.98   -1.57   0.000   0.000
+44903.00    -0.394  -1.436  0.000   0.000
+44904.00    0.13    -1.258  0.000   0.000
+44905.00    0.702   -1.208  0.000   0.000
+44906.00    1.39    -1.377  0.000   0.000
+44907.00    2.224   -1.659  0.000   0.000
+44908.00    3.143   -1.868  0.000   0.000
+44909.00    3.925   -1.946  0.000   0.000
+44910.00    4.339   -1.982  0.000   0.000
+44911.00    4.467   -2.035  0.000   0.000
+44912.00    4.675   -2.028  0.000   0.000
+44913.00    5.169   -1.878  0.000   0.000
+44914.00    5.739   -1.652  0.000   0.000
+44915.00    6.083   -1.478  0.000   0.000
+44916.00    6.200   -1.35   0.000   0.000
+44917.00    6.278   -1.16   0.000   0.000
+44918.00    6.344   -0.924  0.000   0.000
+44919.00    6.262   -0.814  0.000   0.000
+44920.00    6.013   -0.901  0.000   0.000
+44921.00    5.748   -0.992  0.000   0.000
+44922.00    5.557   -0.852  0.000   0.000
+44923.00    5.364   -0.511  0.000   0.000
+44924.00    5.046   -0.211  0.000   0.000
+44925.00    4.531   -0.08   0.000   0.000
+44926.00    3.789   0.007   0.000   0.000
+44927.00    2.864   0.22    0.000   0.000
+44928.00    1.89    0.546   0.000   0.000
+44929.00    1.018   0.857   0.000   0.000
+44930.00    0.179   1.13    0.000   0.000
+44931.00    -0.792  1.452   0.000   0.000
+44932.00    -1.873  1.837   0.000   0.000
+44933.00    -2.879  2.155   0.000   0.000
+44934.00    -3.679  2.291   0.000   0.000
+44935.00    -4.285  2.297   0.000   0.000
+44936.00    -4.795  2.328   0.000   0.000
+44937.00    -5.338  2.46    0.000   0.000
+44938.00    -6.041  2.629   0.000   0.000
+44939.00    -6.891  2.779   0.000   0.000
+44940.00    -7.7    2.968   0.000   0.000
+44941.00    -8.322  3.266   0.000   0.000
+44942.00    -8.835  3.608   0.000   0.000
+44943.00    -9.399  3.858   0.000   0.000
+44944.00    -10.018 4.003   0.000   0.000
+44945.00    -10.612 4.174   0.000   0.000
+44946.00    -11.231 4.43    0.000   0.000
+44947.00    -11.999 4.641   0.000   0.000
+44948.00    -12.839 4.69    0.000   0.000
+44949.00    -13.484 4.686   0.000   0.000
+44950.00    -13.767 4.833   0.000   0.000
+44951.00    -13.762 5.121   0.000   0.000
+44952.00    -13.655 5.314   0.000   0.000
+44953.00    -13.612 5.283   0.000   0.000
+44954.00    -13.767 5.201   0.000   0.000
+44955.00    -14.153 5.309   0.000   0.000
+44956.00    -14.622 5.604   0.000   0.000
+44957.00    -14.989 5.872   0.000   0.000
+44958.00    -15.283 5.981   0.000   0.000
+44959.00    -15.681 6.003   0.000   0.000
+44960.00    -16.206 6.05    0.000   0.000
+44961.00    -16.649 6.108   0.000   0.000
+44962.00    -16.839 6.083   0.000   0.000
+44963.00    -16.827 5.958   0.000   0.000
+44964.00    -16.746 5.823   0.000   0.000
+44965.00    -16.654 5.756   0.000   0.000
+44966.00    -16.581 5.752   0.000   0.000
+44967.00    -16.589 5.769   0.000   0.000
+44968.00    -16.706 5.798   0.000   0.000
+44969.00    -16.873 5.852   0.000   0.000
+44970.00    -17.013 5.904   0.000   0.000
+44971.00    -17.095 5.903   0.000   0.000
+44972.00    -17.12  5.863   0.000   0.000
+44973.00    -17.145 5.867   0.000   0.000
+44974.00    -17.319 5.947   0.000   0.000
+44975.00    -17.752 6.005   0.000   0.000
+44976.00    -18.316 5.936   0.000   0.000
+44977.00    -18.684 5.800   0.000   0.000
+44978.00    -18.614 5.759   0.000   0.000
+44979.00    -18.119 5.834   0.000   0.000
+44980.00    -17.38  5.843   0.000   0.000
+44981.00    -16.626 5.66    0.000   0.000
+44982.00    -16.081 5.423   0.000   0.000
+44983.00    -15.882 5.383   0.000   0.000
+44984.00    -15.953 5.587   0.000   0.000
+44985.00    -16.09  5.816   0.000   0.000
+44986.00    -16.205 5.859   0.000   0.000
+44987.00    -16.35  5.725   0.000   0.000
+44988.00    -16.501 5.56    0.000   0.000
+44989.00    -16.468 5.44    0.000   0.000
+44990.00    -16.135 5.317   0.000   0.000
+44991.00    -15.627 5.127   0.000   0.000
+44992.00    -15.135 4.888   0.000   0.000
+44993.00    -14.689 4.679   0.000   0.000
+44994.00    -14.22  4.568   0.000   0.000
+44995.00    -13.76  4.555   0.000   0.000
+44996.00    -13.43  4.572   0.000   0.000
+44997.00    -13.265 4.534   0.000   0.000
+44998.00    -13.161 4.403   0.000   0.000
+44999.00    -13.006 4.219   0.000   0.000
+45000.00    -12.789 4.062   0.000   0.000
+45001.00    -12.59  3.982   0.000   0.000
+45002.00    -12.514 3.955   0.000   0.000
+45003.00    -12.588 3.892   0.000   0.000
+45004.00    -12.694 3.728   0.000   0.000
+45005.00    -12.625 3.501   0.000   0.000
+45006.00    -12.25  3.322   0.000   0.000
+45007.00    -11.6   3.225   0.000   0.000
+45008.00    -10.801 3.104   0.000   0.000
+45009.00    -9.992  2.85    0.000   0.000
+45010.00    -9.307  2.53    0.000   0.000
+45011.00    -8.86   2.346   0.000   0.000
+45012.00    -8.68   2.391   0.000   0.000
+45013.00    -8.688  2.53    0.000   0.000
+45014.00    -8.782  2.559   0.000   0.000
+45015.00    -8.864  2.423   0.000   0.000
+45016.00    -8.799  2.228   0.000   0.000
+45017.00    -8.435  2.072   0.000   0.000
+45018.00    -7.754  1.943   0.000   0.000
+45019.00    -6.945  1.769   0.000   0.000
+45020.00    -6.239  1.526   0.000   0.000
+45021.00    -5.696  1.275   0.000   0.000
+45022.00    -5.22   1.121   0.000   0.000
+45023.00    -4.739  1.122   0.000   0.000
+45024.00    -4.281  1.212   0.000   0.000
+45025.00    -3.909  1.24    0.000   0.000
+45026.00    -3.639  1.098   0.000   0.000
+45027.00    -3.454  0.826   0.000   0.000
+45028.00    -3.341  0.558   0.000   0.000
+45029.00    -3.284  0.397   0.000   0.000
+45030.00    -3.235  0.332   0.000   0.000
+45031.00    -3.108  0.282   0.000   0.000
+45032.00    -2.811  0.183   0.000   0.000
+45033.00    -2.293  0.031   0.000   0.000
+45034.00    -1.605  -0.129  0.000   0.000
+45035.00    -0.894  -0.266  0.000   0.000
+45036.00    -0.304  -0.412  0.000   0.000
+45037.00    0.118   -0.618  0.000   0.000
+45038.00    0.433   -0.858  0.000   0.000
+45039.00    0.698   -1.018  0.000   0.000
+45040.00    0.888   -1.015  0.000   0.000
+45041.00    0.922   -0.91   0.000   0.000
+45042.00    0.794   -0.848  0.000   0.000
+45043.00    0.644   -0.899  0.000   0.000
+45044.00    0.68    -1.011  0.000   0.000
+45045.00    1.02    -1.114  0.000   0.000
+45046.00    1.624   -1.198  0.000   0.000
+45047.00    2.346   -1.297  0.000   0.000
+45048.00    3.017   -1.425  0.000   0.000
+45049.00    3.496   -1.558  0.000   0.000
+45050.00    3.743   -1.648  0.000   0.000
+45051.00    3.857   -1.636  0.000   0.000
+45052.00    4.009   -1.511  0.000   0.000
+45053.00    4.25    -1.368  0.000   0.000
+45054.00    4.443   -1.361  0.000   0.000
+45055.00    4.41    -1.558  0.000   0.000
+45056.00    4.147   -1.856  0.000   0.000
+45057.00    3.827   -2.077  0.000   0.000
+45058.00    3.644   -2.124  0.000   0.000
+45059.00    3.68    -2.037  0.000   0.000
+45060.00    3.93    -1.926  0.000   0.000
+45061.00    4.362   -1.876  0.000   0.000
+45062.00    4.912   -1.902  0.000   0.000
+45063.00    5.442   -1.976  0.000   0.000
+45064.00    5.772   -2.06   0.000   0.000
+45065.00    5.807   -2.127  0.000   0.000
+45066.00    5.636   -2.146  0.000   0.000
+45067.00    5.453   -2.084  0.000   0.000
+45068.00    5.351   -1.949  0.000   0.000
+45069.00    5.237   -1.814  0.000   0.000
+45070.00    5.011   -1.762  0.000   0.000
+45071.00    4.74    -1.807  0.000   0.000
+45072.00    4.59    -1.895  0.000   0.000
+45073.00    4.623   -1.987  0.000   0.000
+45074.00    4.795   -2.084  0.000   0.000
+45075.00    5.101   -2.166  0.000   0.000
+45076.00    5.541   -2.188  0.000   0.000
+45077.00    5.936   -2.151  0.000   0.000
+45078.00    5.998   -2.129  0.000   0.000
+45079.00    5.685   -2.155  0.000   0.000
+45080.00    5.308   -2.147  0.000   0.000
+45081.00    5.144   -2.031  0.000   0.000
+45082.00    5.065   -1.898  0.000   0.000
+45083.00    4.745   -1.932  0.000   0.000
+45084.00    4.135   -2.176  0.000   0.000
+45085.00    3.551   -2.449  0.000   0.000
+45086.00    3.295   -2.541  0.000   0.000
+45087.00    3.354   -2.423  0.000   0.000
+45088.00    3.508   -2.239  0.000   0.000
+45089.00    3.61    -2.129  0.000   0.000
+45090.00    3.683   -2.123  0.000   0.000
+45091.00    3.795   -2.18   0.000   0.000
+45092.00    3.901   -2.24   0.000   0.000
+45093.00    3.845   -2.244  0.000   0.000
+45094.00    3.538   -2.138  0.000   0.000
+45095.00    3.079   -1.92   0.000   0.000
+45096.00    2.657   -1.688  0.000   0.000
+45097.00    2.348   -1.583  0.000   0.000
+45098.00    2.102   -1.664  0.000   0.000
+45099.00    1.894   -1.848  0.000   0.000
+45100.00    1.747   -2.008  0.000   0.000
+45101.00    1.624   -2.115  0.000   0.000
+45102.00    1.471   -2.224  0.000   0.000
+45103.00    1.397   -2.327  0.000   0.000
+45104.00    1.589   -2.32   0.000   0.000
+45105.00    1.948   -2.17   0.000   0.000
+45106.00    2.051   -2.026  0.000   0.000
+45107.00    1.647   -2.047  0.000   0.000
+45108.00    1.008   -2.166  0.000   0.000
+45109.00    0.566   -2.165  0.000   0.000
+45110.00    0.336   -1.992  0.000   0.000
+45111.00    -0.016  -1.857  0.000   0.000
+45112.00    -0.591  -1.947  0.000   0.000
+45113.00    -1.049  -2.173  0.000   0.000
+45114.00    -1.033  -2.294  0.000   0.000
+45115.00    -0.595  -2.234  0.000   0.000
+45116.00    -0.118  -2.129  0.000   0.000
+45117.00    0.08    -2.104  0.000   0.000
+45118.00    -0.009  -2.13   0.000   0.000
+45119.00    -0.151  -2.127  0.000   0.000
+45120.00    -0.128  -2.086  0.000   0.000
+45121.00    0.03    -2.033  0.000   0.000
+45122.00    0.08    -1.932  0.000   0.000
+45123.00    -0.132  -1.739  0.000   0.000
+45124.00    -0.484  -1.527  0.000   0.000
+45125.00    -0.716  -1.464  0.000   0.000
+45126.00    -0.705  -1.624  0.000   0.000
+45127.00    -0.509  -1.877  0.000   0.000
+45128.00    -0.251  -2.03   0.000   0.000
+45129.00    -0.066  -2.039  0.000   0.000
+45130.00    -0.03   -2.022  0.000   0.000
+45131.00    -0.037  -2.051  0.000   0.000
+45132.00    0.165   -2.031  0.000   0.000
+45133.00    0.637   -1.869  0.000   0.000
+45134.00    1.066   -1.662  0.000   0.000
+45135.00    1.111   -1.606  0.000   0.000
+45136.00    0.844   -1.717  0.000   0.000
+45137.00    0.631   -1.788  0.000   0.000
+45138.00    0.622   -1.683  0.000   0.000
+45139.00    0.644   -1.526  0.000   0.000
+45140.00    0.59    -1.512  0.000   0.000
+45141.00    0.653   -1.599  0.000   0.000
+45142.00    1.058   -1.57   0.000   0.000
+45143.00    1.797   -1.4    0.000   0.000
+45144.00    2.571   -1.255  0.000   0.000
+45145.00    3.028   -1.219  0.000   0.000
+45146.00    3.035   -1.165  0.000   0.000
+45147.00    2.772   -0.945  0.000   0.000
+45148.00    2.601   -0.617  0.000   0.000
+45149.00    2.729   -0.363  0.000   0.000
+45150.00    2.979   -0.237  0.000   0.000
+45151.00    3.013   -0.13   0.000   0.000
+45152.00    2.769   0.029   0.000   0.000
+45153.00    2.524   0.152   0.000   0.000
+45154.00    2.531   0.134   0.000   0.000
+45155.00    2.763   0.036   0.000   0.000
+45156.00    3.038   0.032   0.000   0.000
+45157.00    3.216   0.196   0.000   0.000
+45158.00    3.242   0.421   0.000   0.000
+45159.00    3.147   0.577   0.000   0.000
+45160.00    3.069   0.674   0.000   0.000
+45161.00    3.146   0.81    0.000   0.000
+45162.00    3.308   1.000   0.000   0.000
+45163.00    3.322   1.143   0.000   0.000
+45164.00    3.066   1.174   0.000   0.000
+45165.00    2.652   1.165   0.000   0.000
+45166.00    2.252   1.201   0.000   0.000
+45167.00    1.939   1.243   0.000   0.000
+45168.00    1.762   1.202   0.000   0.000
+45169.00    1.809   1.124   0.000   0.000
+45170.00    2.115   1.14    0.000   0.000
+45171.00    2.576   1.251   0.000   0.000
+45172.00    3.015   1.289   0.000   0.000
+45173.00    3.258   1.167   0.000   0.000
+45174.00    3.166   1.055   0.000   0.000
+45175.00    2.752   1.168   0.000   0.000
+45176.00    2.278   1.463   0.000   0.000
+45177.00    2.06    1.665   0.000   0.000
+45178.00    2.12    1.61    0.000   0.000
+45179.00    2.177   1.421   0.000   0.000
+45180.00    2.038   1.305   0.000   0.000
+45181.00    1.831   1.304   0.000   0.000
+45182.00    1.765   1.300   0.000   0.000
+45183.00    1.839   1.224   0.000   0.000
+45184.00    1.923   1.147   0.000   0.000
+45185.00    1.976   1.174   0.000   0.000
+45186.00    2.028   1.306   0.000   0.000
+45187.00    2.047   1.448   0.000   0.000
+45188.00    1.981   1.514   0.000   0.000
+45189.00    1.883   1.505   0.000   0.000
+45190.00    1.849   1.474   0.000   0.000
+45191.00    1.848   1.461   0.000   0.000
+45192.00    1.724   1.462   0.000   0.000
+45193.00    1.381   1.448   0.000   0.000
+45194.00    0.904   1.384   0.000   0.000
+45195.00    0.507   1.249   0.000   0.000
+45196.00    0.384   1.071   0.000   0.000
+45197.00    0.57    0.959   0.000   0.000
+45198.00    0.904   1.019   0.000   0.000
+45199.00    1.17    1.205   0.000   0.000
+45200.00    1.277   1.32    0.000   0.000
+45201.00    1.264   1.234   0.000   0.000
+45202.00    1.135   1.063   0.000   0.000
+45203.00    0.83    1.033   0.000   0.000
+45204.00    0.396   1.178   0.000   0.000
+45205.00    0.036   1.281   0.000   0.000
+45206.00    -0.097  1.151   0.000   0.000
+45207.00    -0.065  0.861   0.000   0.000
+45208.00    -0.017  0.643   0.000   0.000
+45209.00    0.000   0.609   0.000   0.000
+45210.00    0.012   0.649   0.000   0.000
+45211.00    -0.004  0.607   0.000   0.000
+45212.00    -0.075  0.462   0.000   0.000
+45213.00    -0.127  0.337   0.000   0.000
+45214.00    -0.065  0.355   0.000   0.000
+45215.00    0.056   0.51    0.000   0.000
+45216.00    0.077   0.668   0.000   0.000
+45217.00    -0.056  0.683   0.000   0.000
+45218.00    -0.235  0.538   0.000   0.000
+45219.00    -0.366  0.35    0.000   0.000
+45220.00    -0.506  0.246   0.000   0.000
+45221.00    -0.771  0.237   0.000   0.000
+45222.00    -1.15   0.232   0.000   0.000
+45223.00    -1.461  0.159   0.000   0.000
+45224.00    -1.508  0.046   0.000   0.000
+45225.00    -1.266  -0.001  0.000   0.000
+45226.00    -0.933  0.100   0.000   0.000
+45227.00    -0.758  0.311   0.000   0.000
+45228.00    -0.827  0.484   0.000   0.000
+45229.00    -1.028  0.493   0.000   0.000
+45230.00    -1.222  0.368   0.000   0.000
+45231.00    -1.398  0.256   0.000   0.000
+45232.00    -1.624  0.231   0.000   0.000
+45233.00    -1.892  0.198   0.000   0.000
+45234.00    -2.087  0.037   0.000   0.000
+45235.00    -2.092  -0.209  0.000   0.000
+45236.00    -1.916  -0.369  0.000   0.000
+45237.00    -1.687  -0.357  0.000   0.000
+45238.00    -1.556  -0.277  0.000   0.000
+45239.00    -1.582  -0.292  0.000   0.000
+45240.00    -1.693  -0.439  0.000   0.000
+45241.00    -1.764  -0.591  0.000   0.000
+45242.00    -1.75   -0.588  0.000   0.000
+45243.00    -1.725  -0.385  0.000   0.000
+45244.00    -1.793  -0.107  0.000   0.000
+45245.00    -1.973  0.036   0.000   0.000
+45246.00    -2.195  -0.075  0.000   0.000
+45247.00    -2.395  -0.355  0.000   0.000
+45248.00    -2.569  -0.591  0.000   0.000
+45249.00    -2.756  -0.641  0.000   0.000
+45250.00    -2.958  -0.535  0.000   0.000
+45251.00    -3.107  -0.404  0.000   0.000
+45252.00    -3.109  -0.327  0.000   0.000
+45253.00    -2.946  -0.287  0.000   0.000
+45254.00    -2.724  -0.228  0.000   0.000
+45255.00    -2.612  -0.132  0.000   0.000
+45256.00    -2.717  -0.039  0.000   0.000
+45257.00    -2.995  0.004   0.000   0.000
+45258.00    -3.294  -0.01   0.000   0.000
+45259.00    -3.496  -0.038  0.000   0.000
+45260.00    -3.609  -0.063  0.000   0.000
+45261.00    -3.72   -0.132  0.000   0.000
+45262.00    -3.832  -0.279  0.000   0.000
+45263.00    -3.83   -0.435  0.000   0.000
+45264.00    -3.641  -0.47   0.000   0.000
+45265.00    -3.387  -0.352  0.000   0.000
+45266.00    -3.281  -0.199  0.000   0.000
+45267.00    -3.367  -0.142  0.000   0.000
+45268.00    -3.488  -0.18   0.000   0.000
+45269.00    -3.532  -0.197  0.000   0.000
+45270.00    -3.589  -0.089  0.000   0.000
+45271.00    -3.792  0.167   0.000   0.000
+45272.00    -4.107  0.51    0.000   0.000
+45273.00    -4.388  0.81    0.000   0.000
+45274.00    -4.575  0.915   0.000   0.000
+45275.00    -4.717  0.79    0.000   0.000
+45276.00    -4.839  0.589   0.000   0.000
+45277.00    -4.908  0.543   0.000   0.000
+45278.00    -4.922  0.748   0.000   0.000
+45279.00    -4.938  1.082   0.000   0.000
+45280.00    -4.983  1.351   0.000   0.000
+45281.00    -5.018  1.471   0.000   0.000
+45282.00    -5.008  1.494   0.000   0.000
+45283.00    -4.993  1.506   0.000   0.000
+45284.00    -5.075  1.546   0.000   0.000
+45285.00    -5.314  1.621   0.000   0.000
+45286.00    -5.654  1.74    0.000   0.000
+45287.00    -5.943  1.898   0.000   0.000
+45288.00    -6.068  2.039   0.000   0.000
+45289.00    -6.049  2.077   0.000   0.000
+45290.00    -5.965  1.994   0.000   0.000
+45291.00    -5.818  1.901   0.000   0.000
+45292.00    -5.576  1.941   0.000   0.000
+45293.00    -5.332  2.13    0.000   0.000
+45294.00    -5.247  2.332   0.000   0.000
+45295.00    -5.301  2.436   0.000   0.000
+45296.00    -5.281  2.475   0.000   0.000
+45297.00    -5.094  2.547   0.000   0.000
+45298.00    -4.936  2.672   0.000   0.000
+45299.00    -5.003  2.816   0.000   0.000
+45300.00    -5.185  3.002   0.000   0.000
+45301.00    -5.231  3.272   0.000   0.000
+45302.00    -5.108  3.537   0.000   0.000
+45303.00    -4.961  3.616   0.000   0.000
+45304.00    -4.801  3.471   0.000   0.000
+45305.00    -4.454  3.306   0.000   0.000
+45306.00    -3.859  3.343   0.000   0.000
+45307.00    -3.2    3.557   0.000   0.000
+45308.00    -2.684  3.729   0.000   0.000
+45309.00    -2.326  3.721   0.000   0.000
+45310.00    -2.005  3.607   0.000   0.000
+45311.00    -1.641  3.517   0.000   0.000
+45312.00    -1.257  3.475   0.000   0.000
+45313.00    -0.929  3.433   0.000   0.000
+45314.00    -0.697  3.388   0.000   0.000
+45315.00    -0.486  3.378   0.000   0.000
+45316.00    -0.134  3.37    0.000   0.000
+45317.00    0.468   3.255   0.000   0.000
+45318.00    1.276   2.983   0.000   0.000
+45319.00    2.18    2.663   0.000   0.000
+45320.00    3.089   2.463   0.000   0.000
+45321.00    3.871   2.424   0.000   0.000
+45322.00    4.429   2.414   0.000   0.000
+45323.00    4.819   2.32    0.000   0.000
+45324.00    5.242   2.183   0.000   0.000
+45325.00    5.787   2.088   0.000   0.000
+45326.00    6.278   2.012   0.000   0.000
+45327.00    6.528   1.88    0.000   0.000
+45328.00    6.624   1.758   0.000   0.000
+45329.00    6.753   1.819   0.000   0.000
+45330.00    6.848   2.07    0.000   0.000
+45331.00    6.705   2.253   0.000   0.000
+45332.00    6.417   2.141   0.000   0.000
+45333.00    6.398   1.833   0.000   0.000
+45334.00    6.899   1.624   0.000   0.000
+45335.00    7.695   1.63    0.000   0.000
+45336.00    8.358   1.688   0.000   0.000
+45337.00    8.666   1.656   0.000   0.000
+45338.00    8.662   1.614   0.000   0.000
+45339.00    8.483   1.702   0.000   0.000
+45340.00    8.244   1.872   0.000   0.000
+45341.00    7.987   1.95    0.000   0.000
+45342.00    7.667   1.869   0.000   0.000
+45343.00    7.247   1.739   0.000   0.000
+45344.00    6.84    1.654   0.000   0.000
+45345.00    6.649   1.567   0.000   0.000
+45346.00    6.733   1.401   0.000   0.000
+45347.00    6.936   1.211   0.000   0.000
+45348.00    7.065   1.14    0.000   0.000
+45349.00    7.029   1.229   0.000   0.000
+45350.00    6.829   1.357   0.000   0.000
+45351.00    6.521   1.397   0.000   0.000
+45352.00    6.219   1.362   0.000   0.000
+45353.00    6.003   1.332   0.000   0.000
+45354.00    5.817   1.307   0.000   0.000
+45355.00    5.568   1.223   0.000   0.000
+45356.00    5.29    1.128   0.000   0.000
+45357.00    5.029   1.193   0.000   0.000
+45358.00    4.633   1.467   0.000   0.000
+45359.00    3.886   1.732   0.000   0.000
+45360.00    2.925   1.715   0.000   0.000
+45361.00    2.273   1.438   0.000   0.000
+45362.00    2.332   1.183   0.000   0.000
+45363.00    2.972   1.139   0.000   0.000
+45364.00    3.714   1.214   0.000   0.000
+45365.00    4.169   1.269   0.000   0.000
+45366.00    4.222   1.363   0.000   0.000
+45367.00    3.959   1.628   0.000   0.000
+45368.00    3.572   1.993   0.000   0.000
+45369.00    3.24    2.200   0.000   0.000
+45370.00    2.983   2.117   0.000   0.000
+45371.00    2.712   1.881   0.000   0.000
+45372.00    2.474   1.704   0.000   0.000
+45373.00    2.501   1.638   0.000   0.000
+45374.00    2.928   1.601   0.000   0.000
+45375.00    3.581   1.571   0.000   0.000
+45376.00    4.159   1.624   0.000   0.000
+45377.00    4.536   1.808   0.000   0.000
+45378.00    4.766   2.038   0.000   0.000
+45379.00    4.919   2.186   0.000   0.000
+45380.00    5.026   2.203   0.000   0.000
+45381.00    5.132   2.14    0.000   0.000
+45382.00    5.281   2.053   0.000   0.000
+45383.00    5.469   1.959   0.000   0.000
+45384.00    5.676   1.888   0.000   0.000
+45385.00    5.865   1.908   0.000   0.000
+45386.00    5.933   2.024   0.000   0.000
+45387.00    5.77    2.106   0.000   0.000
+45388.00    5.459   1.996   0.000   0.000
+45389.00    5.311   1.711   0.000   0.000
+45390.00    5.58    1.453   0.000   0.000
+45391.00    6.197   1.363   0.000   0.000
+45392.00    6.862   1.369   0.000   0.000
+45393.00    7.307   1.338   0.000   0.000
+45394.00    7.414   1.296   0.000   0.000
+45395.00    7.194   1.378   0.000   0.000
+45396.00    6.767   1.568   0.000   0.000
+45397.00    6.298   1.65    0.000   0.000
+45398.00    5.851   1.462   0.000   0.000
+45399.00    5.385   1.098   0.000   0.000
+45400.00    4.949   0.789   0.000   0.000
+45401.00    4.763   0.637   0.000   0.000
+45402.00    4.963   0.572   0.000   0.000
+45403.00    5.363   0.51    0.000   0.000
+45404.00    5.604   0.472   0.000   0.000
+45405.00    5.52    0.516   0.000   0.000
+45406.00    5.211   0.628   0.000   0.000
+45407.00    4.824   0.709   0.000   0.000
+45408.00    4.419   0.656   0.000   0.000
+45409.00    4.034   0.448   0.000   0.000
+45410.00    3.722   0.156   0.000   0.000
+45411.00    3.482   -0.105  0.000   0.000
+45412.00    3.245   -0.262  0.000   0.000
+45413.00    2.965   -0.321  0.000   0.000
+45414.00    2.69    -0.35   0.000   0.000
+45415.00    2.519   -0.435  0.000   0.000
+45416.00    2.531   -0.609  0.000   0.000
+45417.00    2.738   -0.816  0.000   0.000
+45418.00    3.049   -0.948  0.000   0.000
+45419.00    3.301   -0.955  0.000   0.000
+45420.00    3.38    -0.916  0.000   0.000
+45421.00    3.304   -0.942  0.000   0.000
+45422.00    3.171   -1.028  0.000   0.000
+45423.00    3.035   -1.055  0.000   0.000
+45424.00    2.891   -0.968  0.000   0.000
+45425.00    2.723   -0.881  0.000   0.000
+45426.00    2.532   -0.931  0.000   0.000
+45427.00    2.344   -1.1    0.000   0.000
+45428.00    2.274   -1.238  0.000   0.000
+45429.00    2.501   -1.256  0.000   0.000
+45430.00    3.093   -1.208  0.000   0.000
+45431.00    3.864   -1.184  0.000   0.000
+45432.00    4.481   -1.184  0.000   0.000
+45433.00    4.733   -1.138  0.000   0.000
+45434.00    4.664   -1.005  0.000   0.000
+45435.00    4.461   -0.834  0.000   0.000
+45436.00    4.29    -0.733  0.000   0.000
+45437.00    4.235   -0.79   0.000   0.000
+45438.00    4.287   -0.994  0.000   0.000
+45439.00    4.358   -1.231  0.000   0.000
+45440.00    4.341   -1.372  0.000   0.000
+45441.00    4.226   -1.383  0.000   0.000
+45442.00    4.15    -1.34   0.000   0.000
+45443.00    4.301   -1.327  0.000   0.000
+45444.00    4.772   -1.361  0.000   0.000
+45445.00    5.469   -1.391  0.000   0.000
+45446.00    6.141   -1.364  0.000   0.000
+45447.00    6.524   -1.273  0.000   0.000
+45448.00    6.51    -1.164  0.000   0.000
+45449.00    6.219   -1.098  0.000   0.000
+45450.00    5.91    -1.088  0.000   0.000
+45451.00    5.781   -1.08   0.000   0.000
+45452.00    5.828   -1.028  0.000   0.000
+45453.00    5.897   -0.971  0.000   0.000
+45454.00    5.863   -0.981  0.000   0.000
+45455.00    5.755   -1.059  0.000   0.000
+45456.00    5.725   -1.129  0.000   0.000
+45457.00    5.902   -1.151  0.000   0.000
+45458.00    6.293   -1.169  0.000   0.000
+45459.00    6.795   -1.234  0.000   0.000
+45460.00    7.231   -1.316  0.000   0.000
+45461.00    7.403   -1.342  0.000   0.000
+45462.00    7.199   -1.282  0.000   0.000
+45463.00    6.714   -1.164  0.000   0.000
+45464.00    6.22    -1.041  0.000   0.000
+45465.00    5.938   -0.976  0.000   0.000
+45466.00    5.837   -1.031  0.000   0.000
+45467.00    5.722   -1.214  0.000   0.000
+45468.00    5.489   -1.438  0.000   0.000
+45469.00    5.232   -1.581  0.000   0.000
+45470.00    5.105   -1.601  0.000   0.000
+45471.00    5.172   -1.554  0.000   0.000
+45472.00    5.391   -1.518  0.000   0.000
+45473.00    5.686   -1.512  0.000   0.000
+45474.00    5.96    -1.509  0.000   0.000
+45475.00    6.076   -1.476  0.000   0.000
+45476.00    5.901   -1.406  0.000   0.000
+45477.00    5.417   -1.308  0.000   0.000
+45478.00    4.793   -1.199  0.000   0.000
+45479.00    4.289   -1.104  0.000   0.000
+45480.00    4.048   -1.058  0.000   0.000
+45481.00    3.977   -1.099  0.000   0.000
+45482.00    3.888   -1.223  0.000   0.000
+45483.00    3.71    -1.36   0.000   0.000
+45484.00    3.515   -1.429  0.000   0.000
+45485.00    3.361   -1.439  0.000   0.000
+45486.00    3.238   -1.472  0.000   0.000
+45487.00    3.174   -1.561  0.000   0.000
+45488.00    3.22    -1.625  0.000   0.000
+45489.00    3.258   -1.583  0.000   0.000
+45490.00    2.994   -1.463  0.000   0.000
+45491.00    2.308   -1.346  0.000   0.000
+45492.00    1.481   -1.231  0.000   0.000
+45493.00    0.902   -1.065  0.000   0.000
+45494.00    0.616   -0.892  0.000   0.000
+45495.00    0.368   -0.857  0.000   0.000
+45496.00    0.041   -1.014  0.000   0.000
+45497.00    -0.175  -1.218  0.000   0.000
+45498.00    -0.095  -1.275  0.000   0.000
+45499.00    0.171   -1.159  0.000   0.000
+45500.00    0.351   -0.995  0.000   0.000
+45501.00    0.322   -0.882  0.000   0.000
+45502.00    0.179   -0.795  0.000   0.000
+45503.00    0.052   -0.673  0.000   0.000
+45504.00    -0.072  -0.5    0.000   0.000
+45505.00    -0.322  -0.303  0.000   0.000
+45506.00    -0.773  -0.093  0.000   0.000
+45507.00    -1.306  0.107   0.000   0.000
+45508.00    -1.693  0.232   0.000   0.000
+45509.00    -1.824  0.204   0.000   0.000
+45510.00    -1.806  0.051   0.000   0.000
+45511.00    -1.807  -0.077  0.000   0.000
+45512.00    -1.913  -0.039  0.000   0.000
+45513.00    -2.152  0.137   0.000   0.000
+45514.00    -2.517  0.286   0.000   0.000
+45515.00    -2.883  0.325   0.000   0.000
+45516.00    -3.047  0.353   0.000   0.000
+45517.00    -3.031  0.491   0.000   0.000
+45518.00    -3.2    0.682   0.000   0.000
+45519.00    -3.864  0.772   0.000   0.000
+45520.00    -4.842  0.749   0.000   0.000
+45521.00    -5.649  0.771   0.000   0.000
+45522.00    -6.088  0.904   0.000   0.000
+45523.00    -6.368  0.986   0.000   0.000
+45524.00    -6.657  0.846   0.000   0.000
+45525.00    -6.806  0.57    0.000   0.000
+45526.00    -6.648  0.403   0.000   0.000
+45527.00    -6.338  0.433   0.000   0.000
+45528.00    -6.223  0.515   0.000   0.000
+45529.00    -6.469  0.521   0.000   0.000
+45530.00    -6.938  0.507   0.000   0.000
+45531.00    -7.368  0.577   0.000   0.000
+45532.00    -7.596  0.692   0.000   0.000
+45533.00    -7.668  0.726   0.000   0.000
+45534.00    -7.777  0.651   0.000   0.000
+45535.00    -8.041  0.552   0.000   0.000
+45536.00    -8.338  0.465   0.000   0.000
+45537.00    -8.428  0.317   0.000   0.000
+45538.00    -8.23   0.073   0.000   0.000
+45539.00    -7.898  -0.142  0.000   0.000
+45540.00    -7.626  -0.156  0.000   0.000
+45541.00    -7.49   0.029   0.000   0.000
+45542.00    -7.454  0.215   0.000   0.000
+45543.00    -7.386  0.246   0.000   0.000
+45544.00    -7.111  0.186   0.000   0.000
+45545.00    -6.616  0.188   0.000   0.000
+45546.00    -6.19   0.25    0.000   0.000
+45547.00    -6.177  0.209   0.000   0.000
+45548.00    -6.542  -0.001  0.000   0.000
+45549.00    -6.882  -0.229  0.000   0.000
+45550.00    -6.892  -0.322  0.000   0.000
+45551.00    -6.628  -0.342  0.000   0.000
+45552.00    -6.264  -0.443  0.000   0.000
+45553.00    -5.827  -0.602  0.000   0.000
+45554.00    -5.313  -0.642  0.000   0.000
+45555.00    -4.876  -0.522  0.000   0.000
+45556.00    -4.724  -0.418  0.000   0.000
+45557.00    -4.903  -0.461  0.000   0.000
+45558.00    -5.306  -0.527  0.000   0.000
+45559.00    -5.779  -0.436  0.000   0.000
+45560.00    -6.162  -0.247  0.000   0.000
+45561.00    -6.358  -0.203  0.000   0.000
+45562.00    -6.459  -0.398  0.000   0.000
+45563.00    -6.683  -0.653  0.000   0.000
+45564.00    -7.1    -0.762  0.000   0.000
+45565.00    -7.513  -0.734  0.000   0.000
+45566.00    -7.732  -0.703  0.000   0.000
+45567.00    -7.819  -0.687  0.000   0.000
+45568.00    -7.973  -0.566  0.000   0.000
+45569.00    -8.275  -0.282  0.000   0.000
+45570.00    -8.654  0.039   0.000   0.000
+45571.00    -9.026  0.229   0.000   0.000
+45572.00    -9.346  0.27    0.000   0.000
+45573.00    -9.614  0.285   0.000   0.000
+45574.00    -9.938  0.341   0.000   0.000
+45575.00    -10.5   0.368   0.000   0.000
+45576.00    -11.346 0.286   0.000   0.000
+45577.00    -12.265 0.144   0.000   0.000
+45578.00    -12.966 0.058   0.000   0.000
+45579.00    -13.323 0.065   0.000   0.000
+45580.00    -13.395 0.129   0.000   0.000
+45581.00    -13.328 0.271   0.000   0.000
+45582.00    -13.32  0.558   0.000   0.000
+45583.00    -13.546 0.923   0.000   0.000
+45584.00    -14.009 1.136   0.000   0.000
+45585.00    -14.524 1.062   0.000   0.000
+45586.00    -14.948 0.868   0.000   0.000
+45587.00    -15.305 0.822   0.000   0.000
+45588.00    -15.634 0.936   0.000   0.000
+45589.00    -15.836 0.95    0.000   0.000
+45590.00    -15.828 0.695   0.000   0.000
+45591.00    -15.706 0.335   0.000   0.000
+45592.00    -15.598 0.157   0.000   0.000
+45593.00    -15.453 0.226   0.000   0.000
+45594.00    -15.133 0.358   0.000   0.000
+45595.00    -14.655 0.394   0.000   0.000
+45596.00    -14.168 0.371   0.000   0.000
+45597.00    -13.71  0.412   0.000   0.000
+45598.00    -13.188 0.531   0.000   0.000
+45599.00    -12.558 0.618   0.000   0.000
+45600.00    -11.888 0.566   0.000   0.000
+45601.00    -11.219 0.383   0.000   0.000
+45602.00    -10.54  0.159   0.000   0.000
+45603.00    -9.876  -0.035  0.000   0.000
+45604.00    -9.313  -0.193  0.000   0.000
+45605.00    -8.87   -0.335  0.000   0.000
+45606.00    -8.422  -0.457  0.000   0.000
+45607.00    -7.793  -0.523  0.000   0.000
+45608.00    -6.93   -0.475  0.000   0.000
+45609.00    -5.999  -0.251  0.000   0.000
+45610.00    -5.303  0.157   0.000   0.000
+45611.00    -5.058  0.62    0.000   0.000
+45612.00    -5.177  0.900   0.000   0.000
+45613.00    -5.331  0.866   0.000   0.000
+45614.00    -5.286  0.651   0.000   0.000
+45615.00    -5.122  0.527   0.000   0.000
+45616.00    -5.047  0.595   0.000   0.000
+45617.00    -5.099  0.687   0.000   0.000
+45618.00    -5.137  0.623   0.000   0.000
+45619.00    -5.069  0.474   0.000   0.000
+45620.00    -4.933  0.464   0.000   0.000
+45621.00    -4.785  0.667   0.000   0.000
+45622.00    -4.643  0.916   0.000   0.000
+45623.00    -4.559  1.02    0.000   0.000
+45624.00    -4.605  0.984   0.000   0.000
+45625.00    -4.766  0.977   0.000   0.000
+45626.00    -4.944  1.123   0.000   0.000
+45627.00    -5.109  1.374   0.000   0.000
+45628.00    -5.325  1.562   0.000   0.000
+45629.00    -5.613  1.561   0.000   0.000
+45630.00    -5.883  1.395   0.000   0.000
+45631.00    -6.047  1.211   0.000   0.000
+45632.00    -6.141  1.135   0.000   0.000
+45633.00    -6.246  1.169   0.000   0.000
+45634.00    -6.334  1.23    0.000   0.000
+45635.00    -6.275  1.268   0.000   0.000
+45636.00    -5.994  1.331   0.000   0.000
+45637.00    -5.605  1.503   0.000   0.000
+45638.00    -5.371  1.804   0.000   0.000
+45639.00    -5.491  2.14    0.000   0.000
+45640.00    -5.903  2.359   0.000   0.000
+45641.00    -6.312  2.371   0.000   0.000
+45642.00    -6.449  2.236   0.000   0.000
+45643.00    -6.303  2.12    0.000   0.000
+45644.00    -6.073  2.122   0.000   0.000
+45645.00    -5.911  2.184   0.000   0.000
+45646.00    -5.773  2.201   0.000   0.000
+45647.00    -5.517  2.193   0.000   0.000
+45648.00    -5.078  2.275   0.000   0.000
+45649.00    -4.537  2.479   0.000   0.000
+45650.00    -4.027  2.667   0.000   0.000
+45651.00    -3.631  2.698   0.000   0.000
+45652.00    -3.335  2.603   0.000   0.000
+45653.00    -3.084  2.56    0.000   0.000
+45654.00    -2.85   2.708   0.000   0.000
+45655.00    -2.642  3.014   0.000   0.000
+45656.00    -2.452  3.303   0.000   0.000
+45657.00    -2.233  3.392   0.000   0.000
+45658.00    -1.918  3.227   0.000   0.000
+45659.00    -1.491  2.924   0.000   0.000
+45660.00    -1.004  2.688   0.000   0.000
+45661.00    -0.538  2.643   0.000   0.000
+45662.00    -0.136  2.751   0.000   0.000
+45663.00    0.206   2.886   0.000   0.000
+45664.00    0.500   2.984   0.000   0.000
+45665.00    0.717   3.07    0.000   0.000
+45666.00    0.771   3.192   0.000   0.000
+45667.00    0.573   3.349   0.000   0.000
+45668.00    0.138   3.504   0.000   0.000
+45669.00    -0.374  3.619   0.000   0.000
+45670.00    -0.74   3.696   0.000   0.000
+45671.00    -0.826  3.762   0.000   0.000
+45672.00    -0.682  3.829   0.000   0.000
+45673.00    -0.472  3.872   0.000   0.000
+45674.00    -0.302  3.875   0.000   0.000
+45675.00    -0.132  3.89    0.000   0.000
+45676.00    0.122   3.994   0.000   0.000
+45677.00    0.432   4.171   0.000   0.000
+45678.00    0.678   4.300   0.000   0.000
+45679.00    0.821   4.289   0.000   0.000
+45680.00    0.948   4.206   0.000   0.000
+45681.00    1.109   4.207   0.000   0.000
+45682.00    1.200   4.376   0.000   0.000
+45683.00    1.11    4.663   0.000   0.000
+45684.00    0.904   4.956   0.000   0.000
+45685.00    0.748   5.139   0.000   0.000
+45686.00    0.708   5.107   0.000   0.000
+45687.00    0.744   4.827   0.000   0.000
+45688.00    0.883   4.42    0.000   0.000
+45689.00    1.22    4.106   0.000   0.000
+45690.00    1.725   4.012   0.000   0.000
+45691.00    2.184   4.053   0.000   0.000
+45692.00    2.392   4.045   0.000   0.000
+45693.00    2.331   3.903   0.000   0.000
+45694.00    2.113   3.682   0.000   0.000
+45695.00    1.81    3.48    0.000   0.000
+45696.00    1.414   3.336   0.000   0.000
+45697.00    0.937   3.242   0.000   0.000
+45698.00    0.478   3.184   0.000   0.000
+45699.00    0.198   3.15    0.000   0.000
+45700.00    0.182   3.101   0.000   0.000
+45701.00    0.347   2.996   0.000   0.000
+45702.00    0.515   2.862   0.000   0.000
+45703.00    0.586   2.816   0.000   0.000
+45704.00    0.598   2.975   0.000   0.000
+45705.00    0.606   3.323   0.000   0.000
+45706.00    0.619   3.691   0.000   0.000
+45707.00    0.701   3.943   0.000   0.000
+45708.00    0.998   4.102   0.000   0.000
+45709.00    1.545   4.239   0.000   0.000
+45710.00    2.145   4.321   0.000   0.000
+45711.00    2.564   4.283   0.000   0.000
+45712.00    2.823   4.147   0.000   0.000
+45713.00    3.075   4.000   0.000   0.000
+45714.00    3.274   3.828   0.000   0.000
+45715.00    3.245   3.487   0.000   0.000
+45716.00    3.103   2.931   0.000   0.000
+45717.00    3.236   2.387   0.000   0.000
+45718.00    3.792   2.171   0.000   0.000
+45719.00    4.429   2.34    0.000   0.000
+45720.00    4.676   2.656   0.000   0.000
+45721.00    4.42    2.896   0.000   0.000
+45722.00    3.921   3.067   0.000   0.000
+45723.00    3.467   3.301   0.000   0.000
+45724.00    3.155   3.642   0.000   0.000
+45725.00    2.967   4.032   0.000   0.000
+45726.00    2.892   4.395   0.000   0.000
+45727.00    3.013   4.698   0.000   0.000
+45728.00    3.459   4.876   0.000   0.000
+45729.00    4.264   4.798   0.000   0.000
+45730.00    5.271   4.396   0.000   0.000
+45731.00    6.169   3.835   0.000   0.000
+45732.00    6.729   3.352   0.000   0.000
+45733.00    6.862   3.04    0.000   0.000
+45734.00    6.524   2.835   0.000   0.000
+45735.00    5.700   2.706   0.000   0.000
+45736.00    4.621   2.704   0.000   0.000
+45737.00    3.543   2.888   0.000   0.000
+45738.00    2.534   3.189   0.000   0.000
+45739.00    1.682   3.454   0.000   0.000
+45740.00    1.311   3.656   0.000   0.000
+45741.00    1.545   3.921   0.000   0.000
+45742.00    2.123   4.256   0.000   0.000
+45743.00    2.619   4.401   0.000   0.000
+45744.00    2.911   4.101   0.000   0.000
+45745.00    3.276   3.457   0.000   0.000
+45746.00    3.991   2.879   0.000   0.000
+45747.00    4.812   2.613   0.000   0.000
+45748.00    5.195   2.518   0.000   0.000
+45749.00    4.947   2.357   0.000   0.000
+45750.00    4.377   2.144   0.000   0.000
+45751.00    3.871   2.033   0.000   0.000
+45752.00    3.602   2.053   0.000   0.000
+45753.00    3.549   2.085   0.000   0.000
+45754.00    3.615   2.083   0.000   0.000
+45755.00    3.700   2.145   0.000   0.000
+45756.00    3.473   2.222   0.000   0.000
+45757.00    3.065   2.233   0.000   0.000
+45758.00    2.748   2.146   0.000   0.000
+45759.00    2.557   2.054   0.000   0.000
+45760.00    2.482   2.132   0.000   0.000
+45761.00    2.507   2.395   0.000   0.000
+45762.00    2.658   2.659   0.000   0.000
+45763.00    2.971   2.74    0.000   0.000
+45764.00    3.409   2.639   0.000   0.000
+45765.00    3.855   2.468   0.000   0.000
+45766.00    4.17    2.284   0.000   0.000
+45767.00    4.258   2.043   0.000   0.000
+45768.00    4.195   1.769   0.000   0.000
+45769.00    4.164   1.629   0.000   0.000
+45770.00    4.182   1.733   0.000   0.000
+45771.00    4.088   1.91    0.000   0.000
+45772.00    3.894   1.892   0.000   0.000
+45773.00    3.94    1.66    0.000   0.000
+45774.00    4.51    1.46    0.000   0.000
+45775.00    5.415   1.448   0.000   0.000
+45776.00    6.114   1.493   0.000   0.000
+45777.00    6.267   1.367   0.000   0.000
+45778.00    5.933   1.06    0.000   0.000
+45779.00    5.325   0.756   0.000   0.000
+45780.00    4.562   0.53    0.000   0.000
+45781.00    3.727   0.259   0.000   0.000
+45782.00    2.824   -0.121  0.000   0.000
+45783.00    1.819   -0.434  0.000   0.000
+45784.00    0.843   -0.457  0.000   0.000
+45785.00    0.23    -0.167  0.000   0.000
+45786.00    0.162   0.266   0.000   0.000
+45787.00    0.455   0.734   0.000   0.000
+45788.00    0.741   1.27    0.000   0.000
+45789.00    0.859   1.874   0.000   0.000
+45790.00    0.942   2.386   0.000   0.000
+45791.00    1.186   2.587   0.000   0.000
+45792.00    1.668   2.395   0.000   0.000
+45793.00    2.373   1.886   0.000   0.000
+45794.00    3.241   1.144   0.000   0.000
+45795.00    4.171   0.165   0.000   0.000
+45796.00    5.044   -0.964  0.000   0.000
+45797.00    5.821   -2.041  0.000   0.000
+45798.00    6.491   -2.846  0.000   0.000
+45799.00    6.949   -3.227  0.000   0.000
+45800.00    7.063   -3.109  0.000   0.000
+45801.00    6.909   -2.583  0.000   0.000
+45802.00    6.623   -1.726  0.000   0.000
+45803.00    6.224   -0.678  0.000   0.000
+45804.00    5.713   0.222   0.000   0.000
+45805.00    5.247   0.592   0.000   0.000
+45806.00    4.967   0.474   0.000   0.000
+45807.00    4.911   0.194   0.000   0.000
+45808.00    5.012   -0.006  0.000   0.000
+45809.00    5.125   -0.07   0.000   0.000
+45810.00    5.088   0.016   0.000   0.000
+45811.00    5.144   0.005   0.000   0.000
+45812.00    5.455   -0.128  0.000   0.000
+45813.00    5.953   -0.199  0.000   0.000
+45814.00    6.565   -0.198  0.000   0.000
+45815.00    6.994   -0.115  0.000   0.000
+45816.00    7.055   0.066   0.000   0.000
+45817.00    6.831   0.348   0.000   0.000
+45818.00    6.676   0.616   0.000   0.000
+45819.00    6.677   0.723   0.000   0.000
+45820.00    6.634   0.603   0.000   0.000
+45821.00    6.501   0.31    0.000   0.000
+45822.00    6.311   -0.013  0.000   0.000
+45823.00    6.039   -0.24   0.000   0.000
+45824.00    5.62    -0.352  0.000   0.000
+45825.00    5.087   -0.411  0.000   0.000
+45826.00    4.643   -0.481  0.000   0.000
+45827.00    4.535   -0.569  0.000   0.000
+45828.00    4.848   -0.625  0.000   0.000
+45829.00    5.448   -0.578  0.000   0.000
+45830.00    6.096   -0.386  0.000   0.000
+45831.00    6.529   -0.088  0.000   0.000
+45832.00    6.635   0.192   0.000   0.000
+45833.00    6.526   0.329   0.000   0.000
+45834.00    6.352   0.31    0.000   0.000
+45835.00    6.125   0.229   0.000   0.000
+45836.00    5.837   0.153   0.000   0.000
+45837.00    5.497   0.028   0.000   0.000
+45838.00    5.139   -0.219  0.000   0.000
+45839.00    4.81    -0.52   0.000   0.000
+45840.00    4.588   -0.705  0.000   0.000
+45841.00    4.521   -0.714  0.000   0.000
+45842.00    4.632   -0.65   0.000   0.000
+45843.00    4.82    -0.62   0.000   0.000
+45844.00    4.832   -0.59   0.000   0.000
+45845.00    4.443   -0.459  0.000   0.000
+45846.00    3.700   -0.213  0.000   0.000
+45847.00    2.843   0.023   0.000   0.000
+45848.00    2.126   0.099   0.000   0.000
+45849.00    1.736   -0.034  0.000   0.000
+45850.00    1.76    -0.289  0.000   0.000
+45851.00    2.069   -0.516  0.000   0.000
+45852.00    2.429   -0.606  0.000   0.000
+45853.00    2.681   -0.562  0.000   0.000
+45854.00    2.81    -0.462  0.000   0.000
+45855.00    2.87    -0.354  0.000   0.000
+45856.00    2.931   -0.229  0.000   0.000
+45857.00    2.935   -0.057  0.000   0.000
+45858.00    2.72    0.139   0.000   0.000
+45859.00    2.185   0.264   0.000   0.000
+45860.00    1.416   0.209   0.000   0.000
+45861.00    0.642   -0.027  0.000   0.000
+45862.00    0.129   -0.356  0.000   0.000
+45863.00    0.024   -0.653  0.000   0.000
+45864.00    0.278   -0.799  0.000   0.000
+45865.00    0.686   -0.8    0.000   0.000
+45866.00    1.028   -0.827  0.000   0.000
+45867.00    1.229   -0.861  0.000   0.000
+45868.00    1.293   -0.847  0.000   0.000
+45869.00    1.249   -0.799  0.000   0.000
+45870.00    1.124   -0.817  0.000   0.000
+45871.00    0.98    -0.935  0.000   0.000
+45872.00    0.849   -1.046  0.000   0.000
+45873.00    0.654   -1.009  0.000   0.000
+45874.00    0.256   -0.8    0.000   0.000
+45875.00    -0.363  -0.521  0.000   0.000
+45876.00    -0.993  -0.319  0.000   0.000
+45877.00    -1.337  -0.268  0.000   0.000
+45878.00    -1.273  -0.359  0.000   0.000
+45879.00    -0.893  -0.549  0.000   0.000
+45880.00    -0.328  -0.761  0.000   0.000
+45881.00    0.317   -0.897  0.000   0.000
+45882.00    0.94    -0.925  0.000   0.000
+45883.00    1.377   -0.895  0.000   0.000
+45884.00    1.422   -0.855  0.000   0.000
+45885.00    0.94    -0.777  0.000   0.000
+45886.00    0.045   -0.625  0.000   0.000
+45887.00    -1.071  -0.404  0.000   0.000
+45888.00    -2.249  -0.166  0.000   0.000
+45889.00    -3.299  0.021   0.000   0.000
+45890.00    -3.991  0.091   0.000   0.000
+45891.00    -4.274  0.014   0.000   0.000
+45892.00    -4.148  -0.212  0.000   0.000
+45893.00    -3.775  -0.546  0.000   0.000
+45894.00    -3.43   -0.882  0.000   0.000
+45895.00    -3.33   -1.071  0.000   0.000
+45896.00    -3.47   -1.037  0.000   0.000
+45897.00    -3.744  -0.851  0.000   0.000
+45898.00    -4.05   -0.669  0.000   0.000
+45899.00    -4.238  -0.566  0.000   0.000
+45900.00    -4.15   -0.465  0.000   0.000
+45901.00    -3.888  -0.259  0.000   0.000
+45902.00    -3.829  0.007   0.000   0.000
+45903.00    -4.258  0.174   0.000   0.000
+45904.00    -5.006  0.163   0.000   0.000
+45905.00    -5.582  0.027   0.000   0.000
+45906.00    -5.734  -0.15   0.000   0.000
+45907.00    -5.627  -0.379  0.000   0.000
+45908.00    -5.503  -0.684  0.000   0.000
+45909.00    -5.402  -0.991  0.000   0.000
+45910.00    -5.267  -1.153  0.000   0.000
+45911.00    -5.078  -0.887  0.000   0.000
+45912.00    -5.046  -0.715  0.000   0.000
+45913.00    -5.231  -0.787  0.000   0.000
+45914.00    -5.508  -0.933  0.000   0.000
+45915.00    -5.788  -0.954  0.000   0.000
+45916.00    -6.044  -0.811  0.000   0.000
+45917.00    -6.298  -0.586  0.000   0.000
+45918.00    -6.455  -0.274  0.000   0.000
+45919.00    -6.273  0.211   0.000   0.000
+45920.00    -6.014  0.447   0.000   0.000
+45921.00    -5.757  0.300   0.000   0.000
+45922.00    -5.544  -0.069  0.000   0.000
+45923.00    -5.493  -0.405  0.000   0.000
+45924.00    -5.615  -0.498  0.000   0.000
+45925.00    -5.804  -0.337  0.000   0.000
+45926.00    -5.989  -0.125  0.000   0.000
+45927.00    -6.126  -0.019  0.000   0.000
+45928.00    -6.173  0.04    0.000   0.000
+45929.00    -6.253  0.169   0.000   0.000
+45930.00    -6.727  0.311   0.000   0.000
+45931.00    -7.793  0.277   0.000   0.000
+45932.00    -9.128  0.015   0.000   0.000
+45933.00    -10.055 -0.29   0.000   0.000
+45934.00    -10.135 -0.452  0.000   0.000
+45935.00    -9.488  -0.505  0.000   0.000
+45936.00    -8.615  -0.559  0.000   0.000
+45937.00    -7.887  -0.571  0.000   0.000
+45938.00    -7.502  -0.39   0.000   0.000
+45939.00    -7.558  -0.05   0.000   0.000
+45940.00    -7.946  0.174   0.000   0.000
+45941.00    -8.531  0.097   0.000   0.000
+45942.00    -9.16   -0.179  0.000   0.000
+45943.00    -9.733  -0.343  0.000   0.000
+45944.00    -10.12  -0.286  0.000   0.000
+45945.00    -9.912  -0.294  0.000   0.000
+45946.00    -9.42   -0.451  0.000   0.000
+45947.00    -9.152  -0.597  0.000   0.000
+45948.00    -9.011  -0.637  0.000   0.000
+45949.00    -8.773  -0.62   0.000   0.000
+45950.00    -8.33   -0.622  0.000   0.000
+45951.00    -7.829  -0.574  0.000   0.000
+45952.00    -7.469  -0.33   0.000   0.000
+45953.00    -7.296  0.104   0.000   0.000
+45954.00    -7.257  0.526   0.000   0.000
+45955.00    -7.313  0.762   0.000   0.000
+45956.00    -7.402  0.844   0.000   0.000
+45957.00    -7.512  0.922   0.000   0.000
+45958.00    -7.751  1.025   0.000   0.000
+45959.00    -8.202  1.015   0.000   0.000
+45960.00    -8.658  0.826   0.000   0.000
+45961.00    -8.74   0.627   0.000   0.000
+45962.00    -8.267  0.637   0.000   0.000
+45963.00    -7.486  0.851   0.000   0.000
+45964.00    -6.863  1.096   0.000   0.000
+45965.00    -6.709  1.288   0.000   0.000
+45966.00    -7.026  1.483   0.000   0.000
+45967.00    -7.703  1.659   0.000   0.000
+45968.00    -8.54   1.623   0.000   0.000
+45969.00    -9.191  1.28    0.000   0.000
+45970.00    -9.381  0.844   0.000   0.000
+45971.00    -9.212  0.631   0.000   0.000
+45972.00    -8.964  0.665   0.000   0.000
+45973.00    -8.751  0.678   0.000   0.000
+45974.00    -8.525  0.506   0.000   0.000
+45975.00    -8.289  0.32    0.000   0.000
+45976.00    -8.079  0.362   0.000   0.000
+45977.00    -7.847  0.627   0.000   0.000
+45978.00    -7.518  0.914   0.000   0.000
+45979.00    -7.137  1.104   0.000   0.000
+45980.00    -6.826  1.264   0.000   0.000
+45981.00    -6.608  1.481   0.000   0.000
+45982.00    -6.383  1.674   0.000   0.000
+45983.00    -6.093  1.69    0.000   0.000
+45984.00    -5.789  1.517   0.000   0.000
+45985.00    -5.533  1.31    0.000   0.000
+45986.00    -5.362  1.184   0.000   0.000
+45987.00    -5.302  1.11    0.000   0.000
+45988.00    -5.324  1.029   0.000   0.000
+45989.00    -5.304  0.982   0.000   0.000
+45990.00    -5.132  1.071   0.000   0.000
+45991.00    -4.843  1.312   0.000   0.000
+45992.00    -4.601  1.61    0.000   0.000
+45993.00    -4.567  1.903   0.000   0.000
+45994.00    -4.839  2.207   0.000   0.000
+45995.00    -5.427  2.483   0.000   0.000
+45996.00    -6.147  2.569   0.000   0.000
+45997.00    -6.646  2.373   0.000   0.000
+45998.00    -6.695  2.076   0.000   0.000
+45999.00    -6.431  1.974   0.000   0.000
+46000.00    -6.145  2.113   0.000   0.000
+46001.00    -5.933  2.219   0.000   0.000
+46002.00    -5.653  2.076   0.000   0.000
+46003.00    -5.232  1.817   0.000   0.000
+46004.00    -4.804  1.741   0.000   0.000
+46005.00    -4.514  1.923   0.000   0.000
+46006.00    -4.335  2.161   0.000   0.000
+46007.00    -4.22   2.271   0.000   0.000
+46008.00    -4.192  2.299   0.000   0.000
+46009.00    -4.222  2.398   0.000   0.000
+46010.00    -4.185  2.588   0.000   0.000
+46011.00    -4.051  2.727   0.000   0.000
+46012.00    -3.927  2.691   0.000   0.000
+46013.00    -3.887  2.512   0.000   0.000
+46014.00    -3.862  2.322   0.000   0.000
+46015.00    -3.758  2.226   0.000   0.000
+46016.00    -3.56   2.234   0.000   0.000
+46017.00    -3.311  2.304   0.000   0.000
+46018.00    -3.016  2.405   0.000   0.000
+46019.00    -2.634  2.536   0.000   0.000
+46020.00    -2.166  2.706   0.000   0.000
+46021.00    -1.726  2.936   0.000   0.000
+46022.00    -1.495  3.23    0.000   0.000
+46023.00    -1.585  3.521   0.000   0.000
+46024.00    -1.905  3.66    0.000   0.000
+46025.00    -2.181  3.544   0.000   0.000
+46026.00    -2.179  3.27    0.000   0.000
+46027.00    -1.952  3.062   0.000   0.000
+46028.00    -1.733  3.023   0.000   0.000
+46029.00    -1.593  3.036   0.000   0.000
+46030.00    -1.371  2.976   0.000   0.000
+46031.00    -0.975  2.906   0.000   0.000
+46032.00    -0.532  3.015   0.000   0.000
+46033.00    -0.244  3.343   0.000   0.000
+46034.00    -0.202  3.69    0.000   0.000
+46035.00    -0.402  3.842   0.000   0.000
+46036.00    -0.783  3.831   0.000   0.000
+46037.00    -1.25   3.845   0.000   0.000
+46038.00    -1.696  3.975   0.000   0.000
+46039.00    -2.072  4.128   0.000   0.000
+46040.00    -2.405  4.148   0.000   0.000
+46041.00    -2.71   3.973   0.000   0.000
+46042.00    -2.885  3.696   0.000   0.000
+46043.00    -2.802  3.48    0.000   0.000
+46044.00    -2.482  3.418   0.000   0.000
+46045.00    -2.088  3.464   0.000   0.000
+46046.00    -1.747  3.503   0.000   0.000
+46047.00    -1.474  3.474   0.000   0.000
+46048.00    -1.242  3.42    0.000   0.000
+46049.00    -1.064  3.434   0.000   0.000
+46050.00    -0.995  3.574   0.000   0.000
+46051.00    -1.081  3.798   0.000   0.000
+46052.00    -1.25   4.011   0.000   0.000
+46053.00    -1.317  4.124   0.000   0.000
+46054.00    -1.119  4.119   0.000   0.000
+46055.00    -0.658  4.044   0.000   0.000
+46056.00    -0.104  3.958   0.000   0.000
+46057.00    0.385   3.861   0.000   0.000
+46058.00    0.772   3.742   0.000   0.000
+46059.00    1.096   3.661   0.000   0.000
+46060.00    1.344   3.718   0.000   0.000
+46061.00    1.428   3.92    0.000   0.000
+46062.00    1.300   4.137   0.000   0.000
+46063.00    1.033   4.246   0.000   0.000
+46064.00    0.766   4.277   0.000   0.000
+46065.00    0.603   4.351   0.000   0.000
+46066.00    0.541   4.509   0.000   0.000
+46067.00    0.515   4.639   0.000   0.000
+46068.00    0.487   4.588   0.000   0.000
+46069.00    0.49    4.301   0.000   0.000
+46070.00    0.604   3.862   0.000   0.000
+46071.00    0.873   3.451   0.000   0.000
+46072.00    1.278   3.244   0.000   0.000
+46073.00    1.721   3.317   0.000   0.000
+46074.00    2.021   3.607   0.000   0.000
+46075.00    1.97    3.983   0.000   0.000
+46076.00    1.522   4.333   0.000   0.000
+46077.00    0.800   4.626   0.000   0.000
+46078.00    0.003   4.884   0.000   0.000
+46079.00    -0.671  5.09    0.000   0.000
+46080.00    -1.063  5.179   0.000   0.000
+46081.00    -1.137  5.122   0.000   0.000
+46082.00    -0.903  4.914   0.000   0.000
+46083.00    -0.402  4.564   0.000   0.000
+46084.00    0.239   4.119   0.000   0.000
+46085.00    0.831   3.671   0.000   0.000
+46086.00    1.249   3.32    0.000   0.000
+46087.00    1.475   3.169   0.000   0.000
+46088.00    1.548   3.278   0.000   0.000
+46089.00    1.472   3.607   0.000   0.000
+46090.00    1.226   4.012   0.000   0.000
+46091.00    0.992   4.19    0.000   0.000
+46092.00    0.912   4.128   0.000   0.000
+46093.00    0.963   4.026   0.000   0.000
+46094.00    0.991   3.978   0.000   0.000
+46095.00    0.832   3.961   0.000   0.000
+46096.00    0.549   3.906   0.000   0.000
+46097.00    0.323   3.793   0.000   0.000
+46098.00    0.225   3.626   0.000   0.000
+46099.00    0.241   3.383   0.000   0.000
+46100.00    0.467   3.09    0.000   0.000
+46101.00    1.034   2.895   0.000   0.000
+46102.00    1.873   2.916   0.000   0.000
+46103.00    2.619   3.081   0.000   0.000
+46104.00    2.885   3.215   0.000   0.000
+46105.00    2.599   3.245   0.000   0.000
+46106.00    2.041   3.244   0.000   0.000
+46107.00    1.506   3.328   0.000   0.000
+46108.00    1.084   3.526   0.000   0.000
+46109.00    0.759   3.752   0.000   0.000
+46110.00    0.557   3.889   0.000   0.000
+46111.00    0.559   3.866   0.000   0.000
+46112.00    0.807   3.671   0.000   0.000
+46113.00    1.223   3.349   0.000   0.000
+46114.00    1.638   3.02    0.000   0.000
+46115.00    1.941   2.836   0.000   0.000
+46116.00    2.152   2.87    0.000   0.000
+46117.00    2.339   3.038   0.000   0.000
+46118.00    2.508   3.169   0.000   0.000
+46119.00    2.621   3.173   0.000   0.000
+46120.00    2.68    3.124   0.000   0.000
+46121.00    2.716   3.115   0.000   0.000
+46122.00    2.673   3.108   0.000   0.000
+46123.00    2.52    2.986   0.000   0.000
+46124.00    2.441   2.741   0.000   0.000
+46125.00    2.681   2.498   0.000   0.000
+46126.00    3.134   2.321   0.000   0.000
+46127.00    3.441   2.091   0.000   0.000
+46128.00    3.475   1.706   0.000   0.000
+46129.00    3.458   1.324   0.000   0.000
+46130.00    3.513   1.232   0.000   0.000
+46131.00    3.527   1.484   0.000   0.000
+46132.00    3.198   1.803   0.000   0.000
+46133.00    2.45    1.91    0.000   0.000
+46134.00    1.659   1.807   0.000   0.000
+46135.00    1.208   1.694   0.000   0.000
+46136.00    1.145   1.676   0.000   0.000
+46137.00    1.296   1.687   0.000   0.000
+46138.00    1.542   1.644   0.000   0.000
+46139.00    1.916   1.546   0.000   0.000
+46140.00    2.525   1.418   0.000   0.000
+46141.00    3.359   1.263   0.000   0.000
+46142.00    4.232   1.117   0.000   0.000
+46143.00    4.892   1.094   0.000   0.000
+46144.00    5.209   1.287   0.000   0.000
+46145.00    5.231   1.619   0.000   0.000
+46146.00    5.113   1.86    0.000   0.000
+46147.00    4.972   1.862   0.000   0.000
+46148.00    4.868   1.700   0.000   0.000
+46149.00    4.822   1.537   0.000   0.000
+46150.00    4.771   1.408   0.000   0.000
+46151.00    4.651   1.217   0.000   0.000
+46152.00    4.587   0.966   0.000   0.000
+46153.00    4.795   0.815   0.000   0.000
+46154.00    5.236   0.84    0.000   0.000
+46155.00    5.587   0.85    0.000   0.000
+46156.00    5.687   0.609   0.000   0.000
+46157.00    5.794   0.202   0.000   0.000
+46158.00    6.171   -0.01   0.000   0.000
+46159.00    6.598   0.176   0.000   0.000
+46160.00    6.582   0.542   0.000   0.000
+46161.00    6.007   0.734   0.000   0.000
+46162.00    5.27    0.674   0.000   0.000
+46163.00    4.817   0.549   0.000   0.000
+46164.00    4.747   0.484   0.000   0.000
+46165.00    4.879   0.41    0.000   0.000
+46166.00    5.000   0.264   0.000   0.000
+46167.00    5.018   0.147   0.000   0.000
+46168.00    5.002   0.178   0.000   0.000
+46169.00    5.118   0.305   0.000   0.000
+46170.00    5.435   0.385   0.000   0.000
+46171.00    5.792   0.427   0.000   0.000
+46172.00    5.963   0.559   0.000   0.000
+46173.00    5.914   0.798   0.000   0.000
+46174.00    5.798   0.988   0.000   0.000
+46175.00    5.751   0.996   0.000   0.000
+46176.00    5.808   0.823   0.000   0.000
+46177.00    5.945   0.577   0.000   0.000
+46178.00    6.100   0.27    0.000   0.000
+46179.00    6.22    -0.28   0.000   0.000
+46180.00    6.345   -1.178  0.000   0.000
+46181.00    6.419   -1.71   0.000   0.000
+46182.00    6.466   -1.571  0.000   0.000
+46183.00    6.454   -1.093  0.000   0.000
+46184.00    6.334   -0.701  0.000   0.000
+46185.00    6.266   -0.602  0.000   0.000
+46186.00    6.366   -0.563  0.000   0.000
+46187.00    6.471   -0.335  0.000   0.000
+46188.00    6.279   -0.01   0.000   0.000
+46189.00    5.732   0.155   0.000   0.000
+46190.00    5.101   0.103   0.000   0.000
+46191.00    4.715   -0.028  0.000   0.000
+46192.00    4.668   -0.177  0.000   0.000
+46193.00    4.843   -0.456  0.000   0.000
+46194.00    5.042   -0.895  0.000   0.000
+46195.00    5.124   -1.102  0.000   0.000
+46196.00    5.19    -0.987  0.000   0.000
+46197.00    5.403   -0.726  0.000   0.000
+46198.00    5.748   -0.464  0.000   0.000
+46199.00    5.977   -0.26   0.000   0.000
+46200.00    5.669   0.011   0.000   0.000
+46201.00    4.864   0.368   0.000   0.000
+46202.00    4.003   0.615   0.000   0.000
+46203.00    3.394   0.551   0.000   0.000
+46204.00    3.07    0.192   0.000   0.000
+46205.00    2.955   -0.236  0.000   0.000
+46206.00    2.968   -0.514  0.000   0.000
+46207.00    3.02    -0.613  0.000   0.000
+46208.00    3.03    -0.644  0.000   0.000
+46209.00    3.009   -0.692  0.000   0.000
+46210.00    3.07    -0.751  0.000   0.000
+46211.00    3.289   -0.8    0.000   0.000
+46212.00    3.602   -0.845  0.000   0.000
+46213.00    3.868   -0.851  0.000   0.000
+46214.00    3.957   -0.718  0.000   0.000
+46215.00    3.789   -0.395  0.000   0.000
+46216.00    3.37    -0.014  0.000   0.000
+46217.00    2.877   0.217   0.000   0.000
+46218.00    2.564   0.234   0.000   0.000
+46219.00    2.54    0.149   0.000   0.000
+46220.00    2.724   0.039   0.000   0.000
+46221.00    3.011   -0.165  0.000   0.000
+46222.00    3.301   -0.511  0.000   0.000
+46223.00    3.412   -0.837  0.000   0.000
+46224.00    3.171   -0.907  0.000   0.000
+46225.00    2.626   -0.699  0.000   0.000
+46226.00    2.037   -0.43   0.000   0.000
+46227.00    1.575   -0.279  0.000   0.000
+46228.00    1.164   -0.203  0.000   0.000
+46229.00    0.668   -0.062  0.000   0.000
+46230.00    0.128   0.145   0.000   0.000
+46231.00    -0.309  0.237   0.000   0.000
+46232.00    -0.576  0.084   0.000   0.000
+46233.00    -0.683  -0.247  0.000   0.000
+46234.00    -0.629  -0.591  0.000   0.000
+46235.00    -0.406  -0.84   0.000   0.000
+46236.00    0.100   -0.766  0.000   0.000
+46237.00    0.599   -0.619  0.000   0.000
+46238.00    0.909   -0.661  0.000   0.000
+46239.00    1.188   -0.79   0.000   0.000
+46240.00    1.488   -0.818  0.000   0.000
+46241.00    1.671   -0.649  0.000   0.000
+46242.00    1.531   -0.3    0.000   0.000
+46243.00    1.000   0.123   0.000   0.000
+46244.00    0.25    0.479   0.000   0.000
+46245.00    -0.387  0.652   0.000   0.000
+46246.00    -0.679  0.642   0.000   0.000
+46247.00    -0.633  0.54    0.000   0.000
+46248.00    -0.425  0.413   0.000   0.000
+46249.00    -0.21   0.237   0.000   0.000
+46250.00    -0.067  -0.021  0.000   0.000
+46251.00    -0.07   -0.285  0.000   0.000
+46252.00    -0.338  -0.419  0.000   0.000
+46253.00    -0.908  -0.411  0.000   0.000
+46254.00    -1.633  -0.397  0.000   0.000
+46255.00    -2.329  -0.374  0.000   0.000
+46256.00    -2.91   -0.288  0.000   0.000
+46257.00    -3.406  -0.087  0.000   0.000
+46258.00    -3.85   0.168   0.000   0.000
+46259.00    -4.185  0.273   0.000   0.000
+46260.00    -4.296  0.059   0.000   0.000
+46261.00    -4.144  -0.401  0.000   0.000
+46262.00    -3.766  -0.865  0.000   0.000
+46263.00    -3.285  -1.122  0.000   0.000
+46264.00    -2.914  -1.097  0.000   0.000
+46265.00    -2.846  -0.871  0.000   0.000
+46266.00    -3.036  -0.635  0.000   0.000
+46267.00    -3.29   -0.527  0.000   0.000
+46268.00    -3.438  -0.549  0.000   0.000
+46269.00    -3.518  -0.583  0.000   0.000
+46270.00    -3.755  -0.485  0.000   0.000
+46271.00    -4.208  -0.295  0.000   0.000
+46272.00    -4.758  -0.117  0.000   0.000
+46273.00    -5.211  -0.007  0.000   0.000
+46274.00    -5.413  0.03    0.000   0.000
+46275.00    -5.33   0.01    0.000   0.000
+46276.00    -5.056  -0.077  0.000   0.000
+46277.00    -4.763  -0.188  0.000   0.000
+46278.00    -4.578  -0.274  0.000   0.000
+46279.00    -4.597  -0.27   0.000   0.000
+46280.00    -4.918  -0.138  0.000   0.000
+46281.00    -5.541  0.067   0.000   0.000
+46282.00    -6.339  0.22    0.000   0.000
+46283.00    -7.081  0.252   0.000   0.000
+46284.00    -7.519  0.238   0.000   0.000
+46285.00    -7.551  0.285   0.000   0.000
+46286.00    -7.398  0.369   0.000   0.000
+46287.00    -7.34   0.341   0.000   0.000
+46288.00    -7.451  0.11    0.000   0.000
+46289.00    -7.6    -0.239  0.000   0.000
+46290.00    -7.355  -0.509  0.000   0.000
+46291.00    -6.762  -0.587  0.000   0.000
+46292.00    -6.136  -0.485  0.000   0.000
+46293.00    -5.663  -0.281  0.000   0.000
+46294.00    -5.411  -0.089  0.000   0.000
+46295.00    -5.438  -0.017  0.000   0.000
+46296.00    -5.741  -0.086  0.000   0.000
+46297.00    -6.235  -0.2    0.000   0.000
+46298.00    -6.791  -0.226  0.000   0.000
+46299.00    -7.304  -0.115  0.000   0.000
+46300.00    -7.716  0.077   0.000   0.000
+46301.00    -7.995  0.235   0.000   0.000
+46302.00    -8.094  0.269   0.000   0.000
+46303.00    -7.964  0.14    0.000   0.000
+46304.00    -7.674  -0.116  0.000   0.000
+46305.00    -7.395  -0.406  0.000   0.000
+46306.00    -7.26   -0.616  0.000   0.000
+46307.00    -7.334  -0.651  0.000   0.000
+46308.00    -7.6    -0.504  0.000   0.000
+46309.00    -8.019  -0.29   0.000   0.000
+46310.00    -8.558  -0.157  0.000   0.000
+46311.00    -9.102  -0.153  0.000   0.000
+46312.00    -9.45   -0.155  0.000   0.000
+46313.00    -9.462  -0.023  0.000   0.000
+46314.00    -9.194  0.214   0.000   0.000
+46315.00    -8.915  0.276   0.000   0.000
+46316.00    -8.668  0.043   0.000   0.000
+46317.00    -8.179  -0.281  0.000   0.000
+46318.00    -7.382  -0.477  0.000   0.000
+46319.00    -6.597  -0.497  0.000   0.000
+46320.00    -6.201  -0.409  0.000   0.000
+46321.00    -6.236  -0.27   0.000   0.000
+46322.00    -6.504  -0.091  0.000   0.000
+46323.00    -6.869  0.049   0.000   0.000
+46324.00    -7.295  0.022   0.000   0.000
+46325.00    -7.705  -0.18   0.000   0.000
+46326.00    -7.985  -0.373  0.000   0.000
+46327.00    -8.12   -0.384  0.000   0.000
+46328.00    -8.189  -0.243  0.000   0.000
+46329.00    -8.223  -0.117  0.000   0.000
+46330.00    -8.153  -0.099  0.000   0.000
+46331.00    -7.918  -0.143  0.000   0.000
+46332.00    -7.543  -0.167  0.000   0.000
+46333.00    -7.144  -0.134  0.000   0.000
+46334.00    -6.895  -0.024  0.000   0.000
+46335.00    -6.938  0.214   0.000   0.000
+46336.00    -7.223  0.595   0.000   0.000
+46337.00    -7.566  1.000   0.000   0.000
+46338.00    -7.81   1.225   0.000   0.000
+46339.00    -7.853  1.188   0.000   0.000
+46340.00    -7.726  0.958   0.000   0.000
+46341.00    -7.482  0.712   0.000   0.000
+46342.00    -7.214  0.52    0.000   0.000
+46343.00    -7.045  0.283   0.000   0.000
+46344.00    -6.908  -0.018  0.000   0.000
+46345.00    -6.553  -0.203  0.000   0.000
+46346.00    -5.916  -0.111  0.000   0.000
+46347.00    -5.31   0.181   0.000   0.000
+46348.00    -5.105  0.459   0.000   0.000
+46349.00    -5.336  0.655   0.000   0.000
+46350.00    -5.772  0.876   0.000   0.000
+46351.00    -6.224  1.148   0.000   0.000
+46352.00    -6.6    1.303   0.000   0.000
+46353.00    -6.774  1.227   0.000   0.000
+46354.00    -6.636  1.06    0.000   0.000
+46355.00    -6.277  1.023   0.000   0.000
+46356.00    -5.943  1.111   0.000   0.000
+46357.00    -5.711  1.102   0.000   0.000
+46358.00    -5.42   0.892   0.000   0.000
+46359.00    -4.949  0.659   0.000   0.000
+46360.00    -4.379  0.642   0.000   0.000
+46361.00    -3.874  0.848   0.000   0.000
+46362.00    -3.555  1.112   0.000   0.000
+46363.00    -3.492  1.337   0.000   0.000
+46364.00    -3.692  1.533   0.000   0.000
+46365.00    -4.077  1.653   0.000   0.000
+46366.00    -4.493  1.613   0.000   0.000
+46367.00    -4.842  1.419   0.000   0.000
+46368.00    -5.076  1.301   0.000   0.000
+46369.00    -5.191  1.444   0.000   0.000
+46370.00    -5.313  1.612   0.000   0.000
+46371.00    -5.461  1.635   0.000   0.000
+46372.00    -5.536  1.503   0.000   0.000
+46373.00    -5.374  1.373   0.000   0.000
+46374.00    -4.916  1.442   0.000   0.000
+46375.00    -4.313  1.672   0.000   0.000
+46376.00    -3.818  1.82    0.000   0.000
+46377.00    -3.533  1.855   0.000   0.000
+46378.00    -3.405  1.947   0.000   0.000
+46379.00    -3.451  2.194   0.000   0.000
+46380.00    -3.703  2.441   0.000   0.000
+46381.00    -3.959  2.484   0.000   0.000
+46382.00    -3.977  2.38    0.000   0.000
+46383.00    -3.803  2.359   0.000   0.000
+46384.00    -3.671  2.445   0.000   0.000
+46385.00    -3.605  2.394   0.000   0.000
+46386.00    -3.421  2.08    0.000   0.000
+46387.00    -3.035  1.731   0.000   0.000
+46388.00    -2.636  1.676   0.000   0.000
+46389.00    -2.489  1.955   0.000   0.000
+46390.00    -2.672  2.339   0.000   0.000
+46391.00    -2.886  2.792   0.000   0.000
+46392.00    -3.109  3.227   0.000   0.000
+46393.00    -3.346  3.575   0.000   0.000
+46394.00    -3.396  3.77    0.000   0.000
+46395.00    -3.139  3.674   0.000   0.000
+46396.00    -2.705  3.317   0.000   0.000
+46397.00    -2.239  2.914   0.000   0.000
+46398.00    -1.746  2.65    0.000   0.000
+46399.00    -1.217  2.54    0.000   0.000
+46400.00    -0.735  2.506   0.000   0.000
+46401.00    -0.37   2.514   0.000   0.000
+46402.00    -0.163  2.592   0.000   0.000
+46403.00    -0.158  2.724   0.000   0.000
+46404.00    -0.361  2.828   0.000   0.000
+46405.00    -0.703  2.892   0.000   0.000
+46406.00    -1.141  3.029   0.000   0.000
+46407.00    -1.72   3.297   0.000   0.000
+46408.00    -2.431  3.548   0.000   0.000
+46409.00    -3.044  3.586   0.000   0.000
+46410.00    -3.266  3.442   0.000   0.000
+46411.00    -3.079  3.334   0.000   0.000
+46412.00    -2.738  3.366   0.000   0.000
+46413.00    -2.413  3.39    0.000   0.000
+46414.00    -2.045  3.257   0.000   0.000
+46415.00    -1.605  3.081   0.000   0.000
+46416.00    -1.27   3.127   0.000   0.000
+46417.00    -1.22   3.43    0.000   0.000
+46418.00    -1.39   3.731   0.000   0.000
+46419.00    -1.57   3.809   0.000   0.000
+46420.00    -1.641  3.722   0.000   0.000
+46421.00    -1.586  3.656   0.000   0.000
+46422.00    -1.409  3.653   0.000   0.000
+46423.00    -1.159  3.596   0.000   0.000
+46424.00    -0.97   3.41    0.000   0.000
+46425.00    -0.918  3.19    0.000   0.000
+46426.00    -0.892  3.094   0.000   0.000
+46427.00    -0.713  3.174   0.000   0.000
+46428.00    -0.363  3.344   0.000   0.000
+46429.00    -0.003  3.484   0.000   0.000
+46430.00    0.207   3.534   0.000   0.000
+46431.00    0.236   3.517   0.000   0.000
+46432.00    0.156   3.491   0.000   0.000
+46433.00    0.041   3.521   0.000   0.000
+46434.00    -0.089  3.653   0.000   0.000
+46435.00    -0.269  3.862   0.000   0.000
+46436.00    -0.522  4.033   0.000   0.000
+46437.00    -0.722  4.047   0.000   0.000
+46438.00    -0.672  3.93    0.000   0.000
+46439.00    -0.314  3.872   0.000   0.000
+46440.00    0.206   4.034   0.000   0.000
+46441.00    0.543   4.165   0.000   0.000
+46442.00    0.604   4.065   0.000   0.000
+46443.00    0.507   3.896   0.000   0.000
+46444.00    0.243   3.916   0.000   0.000
+46445.00    -0.189  4.236   0.000   0.000
+46446.00    -0.755  4.589   0.000   0.000
+46447.00    -1.339  4.65    0.000   0.000
+46448.00    -1.765  4.474   0.000   0.000
+46449.00    -1.93   4.317   0.000   0.000
+46450.00    -1.808  4.296   0.000   0.000
+46451.00    -1.489  4.257   0.000   0.000
+46452.00    -1.131  4.052   0.000   0.000
+46453.00    -0.855  3.712   0.000   0.000
+46454.00    -0.63   3.382   0.000   0.000
+46455.00    -0.308  3.167   0.000   0.000
+46456.00    0.16    3.094   0.000   0.000
+46457.00    0.659   3.108   0.000   0.000
+46458.00    1.007   3.158   0.000   0.000
+46459.00    1.077   3.28    0.000   0.000
+46460.00    0.84    3.564   0.000   0.000
+46461.00    0.484   3.764   0.000   0.000
+46462.00    0.107   3.821   0.000   0.000
+46463.00    -0.289  3.821   0.000   0.000
+46464.00    -0.711  3.812   0.000   0.000
+46465.00    -1.141  3.826   0.000   0.000
+46466.00    -1.376  3.744   0.000   0.000
+46467.00    -1.344  3.558   0.000   0.000
+46468.00    -1.119  3.376   0.000   0.000
+46469.00    -0.767  3.265   0.000   0.000
+46470.00    -0.32   3.233   0.000   0.000
+46471.00    0.158   3.27    0.000   0.000
+46472.00    0.523   3.368   0.000   0.000
+46473.00    0.624   3.479   0.000   0.000
+46474.00    0.422   3.518   0.000   0.000
+46475.00    0.027   3.457   0.000   0.000
+46476.00    -0.355  3.38    0.000   0.000
+46477.00    -0.552  3.389   0.000   0.000
+46478.00    -0.499  3.459   0.000   0.000
+46479.00    -0.228  3.437   0.000   0.000
+46480.00    0.166   3.206   0.000   0.000
+46481.00    0.536   2.825   0.000   0.000
+46482.00    0.759   2.45    0.000   0.000
+46483.00    0.824   2.201   0.000   0.000
+46484.00    0.838   2.122   0.000   0.000
+46485.00    0.924   2.206   0.000   0.000
+46486.00    1.083   2.375   0.000   0.000
+46487.00    1.159   2.525   0.000   0.000
+46488.00    0.987   2.592   0.000   0.000
+46489.00    0.571   2.581   0.000   0.000
+46490.00    0.096   2.537   0.000   0.000
+46491.00    -0.23   2.493   0.000   0.000
+46492.00    -0.313  2.454   0.000   0.000
+46493.00    -0.187  2.396   0.000   0.000
+46494.00    0.069   2.293   0.000   0.000
+46495.00    0.386   2.137   0.000   0.000
+46496.00    0.708   1.968   0.000   0.000
+46497.00    0.981   1.875   0.000   0.000
+46498.00    1.169   1.943   0.000   0.000
+46499.00    1.282   2.192   0.000   0.000
+46500.00    1.367   2.551   0.000   0.000
+46501.00    1.453   2.825   0.000   0.000
+46502.00    1.529   2.81    0.000   0.000
+46503.00    1.595   2.444   0.000   0.000
+46504.00    1.577   2.029   0.000   0.000
+46505.00    1.638   1.785   0.000   0.000
+46506.00    1.906   1.663   0.000   0.000
+46507.00    2.331   1.526   0.000   0.000
+46508.00    2.858   1.326   0.000   0.000
+46509.00    3.406   1.15    0.000   0.000
+46510.00    3.784   1.08    0.000   0.000
+46511.00    3.803   1.08    0.000   0.000
+46512.00    3.525   1.087   0.000   0.000
+46513.00    3.267   1.146   0.000   0.000
+46514.00    3.229   1.327   0.000   0.000
+46515.00    3.234   1.55    0.000   0.000
+46516.00    2.965   1.619   0.000   0.000
+46517.00    2.404   1.416   0.000   0.000
+46518.00    1.896   1.028   0.000   0.000
+46519.00    1.779   0.66    0.000   0.000
+46520.00    2.07    0.432   0.000   0.000
+46521.00    2.538   0.328   0.000   0.000
+46522.00    2.972   0.287   0.000   0.000
+46523.00    3.279   0.268   0.000   0.000
+46524.00    3.407   0.246   0.000   0.000
+46525.00    3.293   0.228   0.000   0.000
+46526.00    3.19    0.304   0.000   0.000
+46527.00    3.276   0.545   0.000   0.000
+46528.00    3.503   0.891   0.000   0.000
+46529.00    3.852   1.175   0.000   0.000
+46530.00    4.223   1.239   0.000   0.000
+46531.00    4.511   1.075   0.000   0.000
+46532.00    4.699   0.844   0.000   0.000
+46533.00    4.841   0.702   0.000   0.000
+46534.00    4.954   0.624   0.000   0.000
+46535.00    5.009   0.471   0.000   0.000
+46536.00    5.081   0.213   0.000   0.000
+46537.00    5.29    -0.016  0.000   0.000
+46538.00    5.558   -0.121  0.000   0.000
+46539.00    5.616   -0.211  0.000   0.000
+46540.00    5.386   -0.414  0.000   0.000
+46541.00    5.129   -0.609  0.000   0.000
+46542.00    5.095   -0.507  0.000   0.000
+46543.00    5.106   -0.038  0.000   0.000
+46544.00    4.743   0.507   0.000   0.000
+46545.00    3.941   0.786   0.000   0.000
+46546.00    3.16    0.733   0.000   0.000
+46547.00    2.866   0.546   0.000   0.000
+46548.00    3.089   0.386   0.000   0.000
+46549.00    3.553   0.224   0.000   0.000
+46550.00    4.032   -0.009  0.000   0.000
+46551.00    4.423   -0.239  0.000   0.000
+46552.00    4.716   -0.359  0.000   0.000
+46553.00    4.895   -0.35   0.000   0.000
+46554.00    4.888   -0.24   0.000   0.000
+46555.00    4.777   -0.085  0.000   0.000
+46556.00    4.61    0.108   0.000   0.000
+46557.00    4.468   0.263   0.000   0.000
+46558.00    4.441   0.233   0.000   0.000
+46559.00    4.507   -0.005  0.000   0.000
+46560.00    4.574   -0.246  0.000   0.000
+46561.00    4.600   -0.292  0.000   0.000
+46562.00    4.567   -0.178  0.000   0.000
+46563.00    4.458   -0.148  0.000   0.000
+46564.00    4.353   -0.395  0.000   0.000
+46565.00    4.42    -0.856  0.000   0.000
+46566.00    4.631   -0.962  0.000   0.000
+46567.00    4.761   -0.606  0.000   0.000
+46568.00    4.713   -0.336  0.000   0.000
+46569.00    4.612   -0.291  0.000   0.000
+46570.00    4.623   -0.162  0.000   0.000
+46571.00    4.658   0.222   0.000   0.000
+46572.00    4.43    0.623   0.000   0.000
+46573.00    3.913   0.668   0.000   0.000
+46574.00    3.446   0.302   0.000   0.000
+46575.00    3.318   -0.189  0.000   0.000
+46576.00    3.503   -0.57   0.000   0.000
+46577.00    3.748   -0.844  0.000   0.000
+46578.00    3.846   -1.049  0.000   0.000
+46579.00    3.766   -1.078  0.000   0.000
+46580.00    3.622   -0.82   0.000   0.000
+46581.00    3.537   -0.38   0.000   0.000
+46582.00    3.595   0.001   0.000   0.000
+46583.00    3.723   0.209   0.000   0.000
+46584.00    3.703   0.326   0.000   0.000
+46585.00    3.396   0.412   0.000   0.000
+46586.00    2.922   0.368   0.000   0.000
+46587.00    2.444   0.103   0.000   0.000
+46588.00    1.995   -0.247  0.000   0.000
+46589.00    1.551   -0.4    0.000   0.000
+46590.00    1.124   -0.231  0.000   0.000
+46591.00    0.738   0.072   0.000   0.000
+46592.00    0.452   0.23    0.000   0.000
+46593.00    0.407   0.137   0.000   0.000
+46594.00    0.725   -0.181  0.000   0.000
+46595.00    1.32    -0.729  0.000   0.000
+46596.00    1.819   -1.207  0.000   0.000
+46597.00    1.94    -1.369  0.000   0.000
+46598.00    1.603   -0.949  0.000   0.000
+46599.00    0.806   0.112   0.000   0.000
+46600.00    -0.121  0.732   0.000   0.000
+46601.00    -0.865  0.731   0.000   0.000
+46602.00    -1.169  0.461   0.000   0.000
+46603.00    -0.954  0.146   0.000   0.000
+46604.00    -0.436  -0.058  0.000   0.000
+46605.00    0.083   -0.227  0.000   0.000
+46606.00    0.416   -0.434  0.000   0.000
+46607.00    0.433   -0.528  0.000   0.000
+46608.00    0.037   -0.3    0.000   0.000
+46609.00    -0.667  0.201   0.000   0.000
+46610.00    -1.368  0.677   0.000   0.000
+46611.00    -1.836  0.896   0.000   0.000
+46612.00    -2.137  0.909   0.000   0.000
+46613.00    -2.431  0.853   0.000   0.000
+46614.00    -2.703  0.686   0.000   0.000
+46615.00    -2.815  0.256   0.000   0.000
+46616.00    -2.748  -0.343  0.000   0.000
+46617.00    -2.588  -0.765  0.000   0.000
+46618.00    -2.382  -0.724  0.000   0.000
+46619.00    -2.168  -0.355  0.000   0.000
+46620.00    -2.023  -0.012  0.000   0.000
+46621.00    -1.974  0.132   0.000   0.000
+46622.00    -1.955  0.138   0.000   0.000
+46623.00    -1.915  0.14    0.000   0.000
+46624.00    -1.927  0.21    0.000   0.000
+46625.00    -2.155  0.343   0.000   0.000
+46626.00    -2.699  0.484   0.000   0.000
+46627.00    -3.492  0.574   0.000   0.000
+46628.00    -4.316  0.505   0.000   0.000
+46629.00    -4.909  0.203   0.000   0.000
+46630.00    -5.199  -0.04   0.000   0.000
+46631.00    -5.252  -0.015  0.000   0.000
+46632.00    -5.208  0.239   0.000   0.000
+46633.00    -5.075  0.47    0.000   0.000
+46634.00    -4.673  0.409   0.000   0.000
+46635.00    -3.925  0.028   0.000   0.000
+46636.00    -3.423  -0.284  0.000   0.000
+46637.00    -3.883  -0.189  0.000   0.000
+46638.00    -5.065  0.139   0.000   0.000
+46639.00    -6.121  0.35    0.000   0.000
+46640.00    -6.605  0.482   0.000   0.000
+46641.00    -6.661  0.679   0.000   0.000
+46642.00    -6.537  0.89    0.000   0.000
+46643.00    -6.35   0.876   0.000   0.000
+46644.00    -6.215  0.458   0.000   0.000
+46645.00    -6.247  -0.223  0.000   0.000
+46646.00    -6.523  -0.616  0.000   0.000
+46647.00    -6.937  -0.53   0.000   0.000
+46648.00    -7.341  -0.184  0.000   0.000
+46649.00    -7.655  0.158   0.000   0.000
+46650.00    -7.769  0.353   0.000   0.000
+46651.00    -7.657  0.451   0.000   0.000
+46652.00    -7.41   0.569   0.000   0.000
+46653.00    -7.225  0.745   0.000   0.000
+46654.00    -7.301  0.902   0.000   0.000
+46655.00    -7.673  0.923   0.000   0.000
+46656.00    -8.099  0.758   0.000   0.000
+46657.00    -8.275  0.462   0.000   0.000
+46658.00    -8.092  0.162   0.000   0.000
+46659.00    -7.676  -0.014  0.000   0.000
+46660.00    -7.233  -0.032  0.000   0.000
+46661.00    -7.009  0.056   0.000   0.000
+46662.00    -7.149  0.191   0.000   0.000
+46663.00    -7.574  0.31    0.000   0.000
+46664.00    -8.197  0.361   0.000   0.000
+46665.00    -8.983  0.266   0.000   0.000
+46666.00    -9.754  -0.003  0.000   0.000
+46667.00    -10.164 -0.383  0.000   0.000
+46668.00    -9.916  -0.667  0.000   0.000
+46669.00    -9.015  -0.619  0.000   0.000
+46670.00    -8.206  -0.369  0.000   0.000
+46671.00    -7.786  -0.154  0.000   0.000
+46672.00    -7.673  -0.097  0.000   0.000
+46673.00    -7.751  -0.137  0.000   0.000
+46674.00    -7.88   -0.104  0.000   0.000
+46675.00    -7.919  0.089   0.000   0.000
+46676.00    -7.869  0.367   0.000   0.000
+46677.00    -7.808  0.565   0.000   0.000
+46678.00    -7.794  0.561   0.000   0.000
+46679.00    -7.873  0.347   0.000   0.000
+46680.00    -8.114  0.048   0.000   0.000
+46681.00    -8.54   -0.151  0.000   0.000
+46682.00    -9.071  -0.214  0.000   0.000
+46683.00    -9.543  -0.197  0.000   0.000
+46684.00    -9.743  -0.137  0.000   0.000
+46685.00    -9.535  0.004   0.000   0.000
+46686.00    -9.018  0.219   0.000   0.000
+46687.00    -8.459  0.428   0.000   0.000
+46688.00    -8.112  0.498   0.000   0.000
+46689.00    -8.084  0.253   0.000   0.000
+46690.00    -8.296  -0.352  0.000   0.000
+46691.00    -8.272  -0.317  0.000   0.000
+46692.00    -8.133  0.085   0.000   0.000
+46693.00    -8.186  0.299   0.000   0.000
+46694.00    -8.448  0.356   0.000   0.000
+46695.00    -8.762  0.35    0.000   0.000
+46696.00    -8.891  0.407   0.000   0.000
+46697.00    -8.7    0.600   0.000   0.000
+46698.00    -8.267  0.852   0.000   0.000
+46699.00    -7.799  0.972   0.000   0.000
+46700.00    -7.441  0.86    0.000   0.000
+46701.00    -7.19   0.634   0.000   0.000
+46702.00    -7.034  0.492   0.000   0.000
+46703.00    -7.031  0.521   0.000   0.000
+46704.00    -7.188  0.674   0.000   0.000
+46705.00    -7.361  0.871   0.000   0.000
+46706.00    -7.407  1.035   0.000   0.000
+46707.00    -7.329  1.095   0.000   0.000
+46708.00    -7.253  1.017   0.000   0.000
+46709.00    -7.298  0.842   0.000   0.000
+46710.00    -7.481  0.649   0.000   0.000
+46711.00    -7.687  0.498   0.000   0.000
+46712.00    -7.781  0.383   0.000   0.000
+46713.00    -7.663  0.286   0.000   0.000
+46714.00    -7.283  0.249   0.000   0.000
+46715.00    -6.668  0.339   0.000   0.000
+46716.00    -5.993  0.547   0.000   0.000
+46717.00    -5.429  0.813   0.000   0.000
+46718.00    -5.071  1.07    0.000   0.000
+46719.00    -4.953  1.232   0.000   0.000
+46720.00    -5.071  1.201   0.000   0.000
+46721.00    -5.309  1.085   0.000   0.000
+46722.00    -5.595  0.969   0.000   0.000
+46723.00    -5.92   0.829   0.000   0.000
+46724.00    -6.152  0.737   0.000   0.000
+46725.00    -6.104  0.742   0.000   0.000
+46726.00    -5.762  0.811   0.000   0.000
+46727.00    -5.297  0.85    0.000   0.000
+46728.00    -4.856  0.86    0.000   0.000
+46729.00    -4.473  0.92    0.000   0.000
+46730.00    -4.226  1.082   0.000   0.000
+46731.00    -4.287  1.307   0.000   0.000
+46732.00    -4.725  1.48    0.000   0.000
+46733.00    -5.31   1.572   0.000   0.000
+46734.00    -5.665  1.661   0.000   0.000
+46735.00    -5.603  1.789   0.000   0.000
+46736.00    -5.247  2.027   0.000   0.000
+46737.00    -4.795  2.200   0.000   0.000
+46738.00    -4.383  2.101   0.000   0.000
+46739.00    -4.163  1.671   0.000   0.000
+46740.00    -4.07   1.343   0.000   0.000
+46741.00    -3.884  1.189   0.000   0.000
+46742.00    -3.586  1.186   0.000   0.000
+46743.00    -3.452  1.514   0.000   0.000
+46744.00    -3.482  2.068   0.000   0.000
+46745.00    -3.573  2.524   0.000   0.000
+46746.00    -3.73   2.747   0.000   0.000
+46747.00    -3.938  2.787   0.000   0.000
+46748.00    -4.109  2.724   0.000   0.000
+46749.00    -4.187  2.591   0.000   0.000
+46750.00    -4.231  2.376   0.000   0.000
+46751.00    -4.336  2.087   0.000   0.000
+46752.00    -4.5    1.85    0.000   0.000
+46753.00    -4.578  1.81    0.000   0.000
+46754.00    -4.401  1.935   0.000   0.000
+46755.00    -3.925  2.033   0.000   0.000
+46756.00    -3.228  2.015   0.000   0.000
+46757.00    -2.399  2.005   0.000   0.000
+46758.00    -1.596  2.141   0.000   0.000
+46759.00    -1.127  2.365   0.000   0.000
+46760.00    -1.218  2.495   0.000   0.000
+46761.00    -1.718  2.511   0.000   0.000
+46762.00    -2.288  2.584   0.000   0.000
+46763.00    -2.768  2.799   0.000   0.000
+46764.00    -3.167  2.989   0.000   0.000
+46765.00    -3.401  2.977   0.000   0.000
+46766.00    -3.328  2.828   0.000   0.000
+46767.00    -3.03   2.765   0.000   0.000
+46768.00    -2.817  2.813   0.000   0.000
+46769.00    -2.876  2.716   0.000   0.000
+46770.00    -3.056  2.302   0.000   0.000
+46771.00    -2.962  2.037   0.000   0.000
+46772.00    -2.605  2.200   0.000   0.000
+46773.00    -2.239  2.655   0.000   0.000
+46774.00    -2.014  3.083   0.000   0.000
+46775.00    -2.097  3.282   0.000   0.000
+46776.00    -2.396  3.306   0.000   0.000
+46777.00    -2.713  3.256   0.000   0.000
+46778.00    -2.944  3.107   0.000   0.000
+46779.00    -3.081  2.826   0.000   0.000
+46780.00    -3.127  2.533   0.000   0.000
+46781.00    -3.062  2.424   0.000   0.000
+46782.00    -2.802  2.556   0.000   0.000
+46783.00    -2.301  2.787   0.000   0.000
+46784.00    -1.635  2.969   0.000   0.000
+46785.00    -0.951  3.119   0.000   0.000
+46786.00    -0.381  3.318   0.000   0.000
+46787.00    -0.066  3.501   0.000   0.000
+46788.00    -0.063  3.487   0.000   0.000
+46789.00    -0.257  3.258   0.000   0.000
+46790.00    -0.498  3.023   0.000   0.000
+46791.00    -0.81   2.936   0.000   0.000
+46792.00    -1.293  2.867   0.000   0.000
+46793.00    -1.816  2.612   0.000   0.000
+46794.00    -2.078  2.249   0.000   0.000
+46795.00    -2.023  2.113   0.000   0.000
+46796.00    -1.921  2.344   0.000   0.000
+46797.00    -2.019  2.725   0.000   0.000
+46798.00    -2.244  2.996   0.000   0.000
+46799.00    -2.388  3.176   0.000   0.000
+46800.00    -2.406  3.44    0.000   0.000
+46801.00    -2.419  3.783   0.000   0.000
+46802.00    -2.418  3.968   0.000   0.000
+46803.00    -2.267  3.844   0.000   0.000
+46804.00    -1.948  3.542   0.000   0.000
+46805.00    -1.598  3.273   0.000   0.000
+46806.00    -1.314  3.066   0.000   0.000
+46807.00    -1.168  2.818   0.000   0.000
+46808.00    -1.258  2.538   0.000   0.000
+46809.00    -1.612  2.391   0.000   0.000
+46810.00    -2.059  2.498   0.000   0.000
+46811.00    -2.16   2.779   0.000   0.000
+46812.00    -1.797  3.034   0.000   0.000
+46813.00    -1.227  3.15    0.000   0.000
+46814.00    -0.725  3.136   0.000   0.000
+46815.00    -0.46   2.994   0.000   0.000
+46816.00    -0.434  2.959   0.000   0.000
+46817.00    -0.627  3.006   0.000   0.000
+46818.00    -0.939  3.089   0.000   0.000
+46819.00    -1.338  3.264   0.000   0.000
+46820.00    -1.833  3.406   0.000   0.000
+46821.00    -2.308  3.324   0.000   0.000
+46822.00    -2.492  3.018   0.000   0.000
+46823.00    -2.264  2.722   0.000   0.000
+46824.00    -1.856  2.595   0.000   0.000
+46825.00    -1.619  2.529   0.000   0.000
+46826.00    -1.688  2.507   0.000   0.000
+46827.00    -2.003  2.552   0.000   0.000
+46828.00    -2.522  2.706   0.000   0.000
+46829.00    -3.146  2.899   0.000   0.000
+46830.00    -3.543  2.877   0.000   0.000
+46831.00    -3.415  2.624   0.000   0.000
+46832.00    -3.125  2.476   0.000   0.000
+46833.00    -2.914  2.582   0.000   0.000
+46834.00    -2.667  2.719   0.000   0.000
+46835.00    -2.261  2.539   0.000   0.000
+46836.00    -1.922  2.259   0.000   0.000
+46837.00    -1.844  2.163   0.000   0.000
+46838.00    -1.933  2.319   0.000   0.000
+46839.00    -1.942  2.635   0.000   0.000
+46840.00    -1.689  2.886   0.000   0.000
+46841.00    -1.18   2.971   0.000   0.000
+46842.00    -0.623  2.91    0.000   0.000
+46843.00    -0.217  2.767   0.000   0.000
+46844.00    -0.066  2.59    0.000   0.000
+46845.00    -0.227  2.391   0.000   0.000
+46846.00    -0.559  2.235   0.000   0.000
+46847.00    -0.942  2.147   0.000   0.000
+46848.00    -1.33   2.078   0.000   0.000
+46849.00    -1.615  1.967   0.000   0.000
+46850.00    -1.62   1.819   0.000   0.000
+46851.00    -1.372  1.562   0.000   0.000
+46852.00    -0.824  1.458   0.000   0.000
+46853.00    -0.069  1.607   0.000   0.000
+46854.00    0.667   1.802   0.000   0.000
+46855.00    1.163   1.907   0.000   0.000
+46856.00    1.253   1.94    0.000   0.000
+46857.00    0.895   1.931   0.000   0.000
+46858.00    0.264   1.831   0.000   0.000
+46859.00    -0.342  1.61    0.000   0.000
+46860.00    -0.742  1.381   0.000   0.000
+46861.00    -0.914  1.287   0.000   0.000
+46862.00    -0.872  1.312   0.000   0.000
+46863.00    -0.623  1.288   0.000   0.000
+46864.00    -0.259  1.122   0.000   0.000
+46865.00    0.046   0.91    0.000   0.000
+46866.00    0.167   0.815   0.000   0.000
+46867.00    0.146   0.884   0.000   0.000
+46868.00    0.100   1.018   0.000   0.000
+46869.00    0.054   1.078   0.000   0.000
+46870.00    -0.092  1.006   0.000   0.000
+46871.00    -0.389  0.864   0.000   0.000
+46872.00    -0.803  0.771   0.000   0.000
+46873.00    -1.226  0.825   0.000   0.000
+46874.00    -1.483  1.081   0.000   0.000
+46875.00    -1.407  1.511   0.000   0.000
+46876.00    -0.987  1.678   0.000   0.000
+46877.00    -0.374  1.436   0.000   0.000
+46878.00    0.211   0.92    0.000   0.000
+46879.00    0.563   0.279   0.000   0.000
+46880.00    0.565   -0.283  0.000   0.000
+46881.00    0.453   -0.256  0.000   0.000
+46882.00    0.446   0.102   0.000   0.000
+46883.00    0.536   0.362   0.000   0.000
+46884.00    0.600   0.500   0.000   0.000
+46885.00    0.514   0.543   0.000   0.000
+46886.00    0.296   0.506   0.000   0.000
+46887.00    0.081   0.431   0.000   0.000
+46888.00    0.009   0.408   0.000   0.000
+46889.00    0.16    0.481   0.000   0.000
+46890.00    0.55    0.552   0.000   0.000
+46891.00    1.122   0.464   0.000   0.000
+46892.00    1.729   0.166   0.000   0.000
+46893.00    2.151   -0.215  0.000   0.000
+46894.00    2.182   -0.513  0.000   0.000
+46895.00    1.782   -0.67   0.000   0.000
+46896.00    1.289   -0.61   0.000   0.000
+46897.00    1.04    -0.328  0.000   0.000
+46898.00    1.042   0.059   0.000   0.000
+46899.00    1.075   0.427   0.000   0.000
+46900.00    0.888   0.634   0.000   0.000
+46901.00    0.584   0.506   0.000   0.000
+46902.00    0.436   0.085   0.000   0.000
+46903.00    0.618   -0.416  0.000   0.000
+46904.00    1.113   -0.792  0.000   0.000
+46905.00    1.76    -0.887  0.000   0.000
+46906.00    2.315   -0.814  0.000   0.000
+46907.00    2.617   -0.744  0.000   0.000
+46908.00    2.629   -0.798  0.000   0.000
+46909.00    2.53    -0.853  0.000   0.000
+46910.00    2.565   -0.712  0.000   0.000
+46911.00    2.743   -0.408  0.000   0.000
+46912.00    2.986   -0.124  0.000   0.000
+46913.00    3.201   -0.041  0.000   0.000
+46914.00    3.229   -0.241  0.000   0.000
+46915.00    2.93    -0.662  0.000   0.000
+46916.00    2.627   -1.03   0.000   0.000
+46917.00    2.551   -1.156  0.000   0.000
+46918.00    2.695   -1.062  0.000   0.000
+46919.00    2.917   -0.954  0.000   0.000
+46920.00    3.246   -1.22   0.000   0.000
+46921.00    3.674   -1.582  0.000   0.000
+46922.00    3.998   -1.634  0.000   0.000
+46923.00    3.928   -1.353  0.000   0.000
+46924.00    3.409   -0.962  0.000   0.000
+46925.00    2.699   -0.637  0.000   0.000
+46926.00    2.101   -0.335  0.000   0.000
+46927.00    1.637   -0.061  0.000   0.000
+46928.00    1.152   0.027   0.000   0.000
+46929.00    0.704   -0.178  0.000   0.000
+46930.00    0.633   -0.561  0.000   0.000
+46931.00    1.138   -0.884  0.000   0.000
+46932.00    2.025   -1.007  0.000   0.000
+46933.00    2.882   -0.959  0.000   0.000
+46934.00    3.403   -0.849  0.000   0.000
+46935.00    3.536   -0.75   0.000   0.000
+46936.00    3.418   -0.675  0.000   0.000
+46937.00    3.161   -0.597  0.000   0.000
+46938.00    2.77    -0.481  0.000   0.000
+46939.00    2.225   -0.308  0.000   0.000
+46940.00    1.611   -0.14   0.000   0.000
+46941.00    1.208   -0.193  0.000   0.000
+46942.00    1.113   -0.549  0.000   0.000
+46943.00    1.161   -1.032  0.000   0.000
+46944.00    1.179   -1.34   0.000   0.000
+46945.00    1.103   -1.262  0.000   0.000
+46946.00    0.974   -1.017  0.000   0.000
+46947.00    0.852   -0.875  0.000   0.000
+46948.00    0.828   -0.884  0.000   0.000
+46949.00    1.019   -0.894  0.000   0.000
+46950.00    1.395   -0.77   0.000   0.000
+46951.00    1.621   -0.588  0.000   0.000
+46952.00    1.389   -0.519  0.000   0.000
+46953.00    0.772   -0.539  0.000   0.000
+46954.00    0.124   -0.444  0.000   0.000
+46955.00    -0.348  -0.171  0.000   0.000
+46956.00    -0.71   0.05    0.000   0.000
+46957.00    -0.928  -0.054  0.000   0.000
+46958.00    -0.807  -0.419  0.000   0.000
+46959.00    -0.282  -0.717  0.000   0.000
+46960.00    0.35    -0.739  0.000   0.000
+46961.00    0.694   -0.581  0.000   0.000
+46962.00    0.593   -0.42   0.000   0.000
+46963.00    0.179   -0.295  0.000   0.000
+46964.00    -0.311  -0.177  0.000   0.000
+46965.00    -0.683  -0.125  0.000   0.000
+46966.00    -0.751  -0.236  0.000   0.000
+46967.00    -0.609  -0.463  0.000   0.000
+46968.00    -0.539  -0.641  0.000   0.000
+46969.00    -0.718  -0.657  0.000   0.000
+46970.00    -1.184  -0.532  0.000   0.000
+46971.00    -1.594  -0.649  0.000   0.000
+46972.00    -1.945  -0.891  0.000   0.000
+46973.00    -2.344  -0.879  0.000   0.000
+46974.00    -2.655  -0.559  0.000   0.000
+46975.00    -2.707  -0.154  0.000   0.000
+46976.00    -2.274  -0.082  0.000   0.000
+46977.00    -1.457  -0.241  0.000   0.000
+46978.00    -0.698  -0.195  0.000   0.000
+46979.00    -0.394  0.046   0.000   0.000
+46980.00    -0.789  0.147   0.000   0.000
+46981.00    -1.793  0.018   0.000   0.000
+46982.00    -2.994  -0.114  0.000   0.000
+46983.00    -4.034  -0.045  0.000   0.000
+46984.00    -4.8    0.136   0.000   0.000
+46985.00    -5.22   0.197   0.000   0.000
+46986.00    -5.067  0.109   0.000   0.000
+46987.00    -4.402  0.074   0.000   0.000
+46988.00    -3.672  0.191   0.000   0.000
+46989.00    -3.319  0.291   0.000   0.000
+46990.00    -3.523  0.203   0.000   0.000
+46991.00    -4.13   0.029   0.000   0.000
+46992.00    -4.927  -0.021  0.000   0.000
+46993.00    -5.712  0.047   0.000   0.000
+46994.00    -6.226  0.012   0.000   0.000
+46995.00    -6.433  -0.129  0.000   0.000
+46996.00    -6.574  -0.172  0.000   0.000
+46997.00    -6.743  -0.13   0.000   0.000
+46998.00    -6.845  -0.246  0.000   0.000
+46999.00    -6.713  -0.728  0.000   0.000
+47000.00    -6.47   -0.981  0.000   0.000
+47001.00    -6.292  -0.888  0.000   0.000
+47002.00    -6.218  -0.512  0.000   0.000
+47003.00    -6.222  -0.04   0.000   0.000
+47004.00    -6.281  0.264   0.000   0.000
+47005.00    -6.363  0.338   0.000   0.000
+47006.00    -6.43   0.314   0.000   0.000
+47007.00    -6.528  0.273   0.000   0.000
+47008.00    -6.793  0.148   0.000   0.000
+47009.00    -7.282  -0.111  0.000   0.000
+47010.00    -7.851  -0.391  0.000   0.000
+47011.00    -8.282  -0.534  0.000   0.000
+47012.00    -8.423  -0.533  0.000   0.000
+47013.00    -8.236  -0.506  0.000   0.000
+47014.00    -7.793  -0.467  0.000   0.000
+47015.00    -7.319  -0.311  0.000   0.000
+47016.00    -7.053  -0.078  0.000   0.000
+47017.00    -6.993  -0.052  0.000   0.000
+47018.00    -6.859  -0.466  0.000   0.000
+47019.00    -6.603  -1.1    0.000   0.000
+47020.00    -6.641  -1.438  0.000   0.000
+47021.00    -7.136  -1.366  0.000   0.000
+47022.00    -7.828  -1.18   0.000   0.000
+47023.00    -8.349  -1.141  0.000   0.000
+47024.00    -8.597  -1.197  0.000   0.000
+47025.00    -8.723  -1.16   0.000   0.000
+47026.00    -8.859  -1.005  0.000   0.000
+47027.00    -8.969  -0.88   0.000   0.000
+47028.00    -9.025  -0.842  0.000   0.000
+47029.00    -9.12   -0.749  0.000   0.000
+47030.00    -9.337  -0.483  0.000   0.000
+47031.00    -9.611  -0.287  0.000   0.000
+47032.00    -9.766  -0.312  0.000   0.000
+47033.00    -9.61   -0.402  0.000   0.000
+47034.00    -9.027  -0.281  0.000   0.000
+47035.00    -8.509  -0.174  0.000   0.000
+47036.00    -8.48   -0.151  0.000   0.000
+47037.00    -8.932  -0.194  0.000   0.000
+47038.00    -9.637  -0.308  0.000   0.000
+47039.00    -10.255 -0.45   0.000   0.000
+47040.00    -10.417 -0.573  0.000   0.000
+47041.00    -10.029 -0.651  0.000   0.000
+47042.00    -9.29   -0.624  0.000   0.000
+47043.00    -8.587  -0.416  0.000   0.000
+47044.00    -8.29   -0.075  0.000   0.000
+47045.00    -8.45   0.183   0.000   0.000
+47046.00    -8.761  0.165   0.000   0.000
+47047.00    -8.99   -0.054  0.000   0.000
+47048.00    -9.275  -0.206  0.000   0.000
+47049.00    -9.842  -0.165  0.000   0.000
+47050.00    -10.563 -0.089  0.000   0.000
+47051.00    -11.005 -0.149  0.000   0.000
+47052.00    -10.949 -0.292  0.000   0.000
+47053.00    -10.581 -0.333  0.000   0.000
+47054.00    -10.176 -0.225  0.000   0.000
+47055.00    -9.859  -0.114  0.000   0.000
+47056.00    -9.614  -0.112  0.000   0.000
+47057.00    -9.438  -0.129  0.000   0.000
+47058.00    -9.309  -0.007  0.000   0.000
+47059.00    -9.117  0.263   0.000   0.000
+47060.00    -8.764  0.515   0.000   0.000
+47061.00    -8.383  0.58    0.000   0.000
+47062.00    -8.196  0.436   0.000   0.000
+47063.00    -8.331  0.203   0.000   0.000
+47064.00    -8.724  0.02    0.000   0.000
+47065.00    -9.242  -0.074  0.000   0.000
+47066.00    -9.577  -0.147  0.000   0.000
+47067.00    -9.584  -0.248  0.000   0.000
+47068.00    -9.251  -0.374  0.000   0.000
+47069.00    -8.618  -0.444  0.000   0.000
+47070.00    -7.876  -0.359  0.000   0.000
+47071.00    -7.311  -0.089  0.000   0.000
+47072.00    -7.129  0.293   0.000   0.000
+47073.00    -7.287  0.627   0.000   0.000
+47074.00    -7.541  0.756   0.000   0.000
+47075.00    -7.731  0.651   0.000   0.000
+47076.00    -7.946  0.444   0.000   0.000
+47077.00    -8.366  0.274   0.000   0.000
+47078.00    -8.949  0.16    0.000   0.000
+47079.00    -9.362  0.082   0.000   0.000
+47080.00    -9.281  0.097   0.000   0.000
+47081.00    -8.711  0.264   0.000   0.000
+47082.00    -7.906  0.515   0.000   0.000
+47083.00    -7.139  0.67    0.000   0.000
+47084.00    -6.603  0.608   0.000   0.000
+47085.00    -6.414  0.389   0.000   0.000
+47086.00    -6.384  0.27    0.000   0.000
+47087.00    -6.344  0.39    0.000   0.000
+47088.00    -6.341  0.658   0.000   0.000
+47089.00    -6.444  0.948   0.000   0.000
+47090.00    -6.725  1.123   0.000   0.000
+47091.00    -7.055  1.224   0.000   0.000
+47092.00    -7.282  1.206   0.000   0.000
+47093.00    -7.361  1.096   0.000   0.000
+47094.00    -7.273  1.006   0.000   0.000
+47095.00    -6.979  0.973   0.000   0.000
+47096.00    -6.469  0.959   0.000   0.000
+47097.00    -5.804  0.947   0.000   0.000
+47098.00    -5.128  0.961   0.000   0.000
+47099.00    -4.629  1.007   0.000   0.000
+47100.00    -4.44   1.06    0.000   0.000
+47101.00    -4.597  1.185   0.000   0.000
+47102.00    -4.908  1.365   0.000   0.000
+47103.00    -5.121  1.497   0.000   0.000
+47104.00    -5.123  1.489   0.000   0.000
+47105.00    -5.033  1.279   0.000   0.000
+47106.00    -5.125  1.015   0.000   0.000
+47107.00    -5.393  0.856   0.000   0.000
+47108.00    -5.526  0.869   0.000   0.000
+47109.00    -5.24   1.06    0.000   0.000
+47110.00    -4.455  1.315   0.000   0.000
+47111.00    -3.587  1.43    0.000   0.000
+47112.00    -2.974  1.36    0.000   0.000
+47113.00    -2.673  1.272   0.000   0.000
+47114.00    -2.695  1.326   0.000   0.000
+47115.00    -3.032  1.518   0.000   0.000
+47116.00    -3.601  1.699   0.000   0.000
+47117.00    -4.19   1.766   0.000   0.000
+47118.00    -4.579  1.733   0.000   0.000
+47119.00    -4.704  1.656   0.000   0.000
+47120.00    -4.653  1.566   0.000   0.000
+47121.00    -4.532  1.498   0.000   0.000
+47122.00    -4.375  1.518   0.000   0.000
+47123.00    -4.212  1.647   0.000   0.000
+47124.00    -4.126  1.807   0.000   0.000
+47125.00    -4.098  1.91    0.000   0.000
+47126.00    -4.076  1.935   0.000   0.000
+47127.00    -4.039  1.941   0.000   0.000
+47128.00    -3.996  2.006   0.000   0.000
+47129.00    -3.955  2.164   0.000   0.000
+47130.00    -3.876  2.389   0.000   0.000
+47131.00    -3.763  2.548   0.000   0.000
+47132.00    -3.563  2.569   0.000   0.000
+47133.00    -3.235  2.47    0.000   0.000
+47134.00    -2.848  2.352   0.000   0.000
+47135.00    -2.527  2.391   0.000   0.000
+47136.00    -2.336  2.569   0.000   0.000
+47137.00    -2.222  2.66    0.000   0.000
+47138.00    -2.123  2.326   0.000   0.000
+47139.00    -1.873  1.954   0.000   0.000
+47140.00    -1.522  1.794   0.000   0.000
+47141.00    -1.224  1.859   0.000   0.000
+47142.00    -1.099  2.151   0.000   0.000
+47143.00    -1.287  2.483   0.000   0.000
+47144.00    -1.826  2.586   0.000   0.000
+47145.00    -2.532  2.373   0.000   0.000
+47146.00    -3.189  2.099   0.000   0.000
+47147.00    -3.696  2.006   0.000   0.000
+47148.00    -4.066  2.118   0.000   0.000
+47149.00    -4.232  2.356   0.000   0.000
+47150.00    -3.987  2.35    0.000   0.000
+47151.00    -3.544  2.226   0.000   0.000
+47152.00    -3.221  2.218   0.000   0.000
+47153.00    -3.113  2.251   0.000   0.000
+47154.00    -3.119  2.212   0.000   0.000
+47155.00    -3.094  2.148   0.000   0.000
+47156.00    -3.027  2.192   0.000   0.000
+47157.00    -3.027  2.368   0.000   0.000
+47158.00    -3.147  2.575   0.000   0.000
+47159.00    -3.321  2.709   0.000   0.000
+47160.00    -3.441  2.728   0.000   0.000
+47161.00    -3.42   2.636   0.000   0.000
+47162.00    -3.284  2.451   0.000   0.000
+47163.00    -3.151  2.267   0.000   0.000
+47164.00    -3.083  2.276   0.000   0.000
+47165.00    -2.964  2.600   0.000   0.000
+47166.00    -2.683  2.845   0.000   0.000
+47167.00    -2.267  2.817   0.000   0.000
+47168.00    -1.882  2.728   0.000   0.000
+47169.00    -1.681  2.872   0.000   0.000
+47170.00    -1.559  3.005   0.000   0.000
+47171.00    -1.647  2.958   0.000   0.000
+47172.00    -2.132  2.731   0.000   0.000
+47173.00    -2.832  2.446   0.000   0.000
+47174.00    -3.436  2.38    0.000   0.000
+47175.00    -3.626  2.377   0.000   0.000
+47176.00    -3.527  2.294   0.000   0.000
+47177.00    -3.273  2.121   0.000   0.000
+47178.00    -2.855  2.083   0.000   0.000
+47179.00    -2.454  2.135   0.000   0.000
+47180.00    -2.19   2.134   0.000   0.000
+47181.00    -2.2    2.117   0.000   0.000
+47182.00    -2.504  2.021   0.000   0.000
+47183.00    -2.961  1.918   0.000   0.000
+47184.00    -3.497  2.019   0.000   0.000
+47185.00    -3.997  2.221   0.000   0.000
+47186.00    -4.368  2.345   0.000   0.000
+47187.00    -4.502  2.359   0.000   0.000
+47188.00    -4.355  2.416   0.000   0.000
+47189.00    -4.003  2.642   0.000   0.000
+47190.00    -3.54   2.728   0.000   0.000
+47191.00    -3.07   2.534   0.000   0.000
+47192.00    -2.683  2.208   0.000   0.000
+47193.00    -2.379  1.975   0.000   0.000
+47194.00    -2.042  1.928   0.000   0.000
+47195.00    -1.592  1.964   0.000   0.000
+47196.00    -1.177  2.000   0.000   0.000
+47197.00    -0.958  2.033   0.000   0.000
+47198.00    -0.985  2.086   0.000   0.000
+47199.00    -1.242  2.077   0.000   0.000
+47200.00    -1.663  1.831   0.000   0.000
+47201.00    -2.093  1.400   0.000   0.000
+47202.00    -2.409  1.094   0.000   0.000
+47203.00    -2.685  1.172   0.000   0.000
+47204.00    -3.087  1.546   0.000   0.000
+47205.00    -3.499  1.716   0.000   0.000
+47206.00    -3.561  1.551   0.000   0.000
+47207.00    -3.088  1.375   0.000   0.000
+47208.00    -2.366  1.432   0.000   0.000
+47209.00    -1.903  1.61    0.000   0.000
+47210.00    -1.942  1.665   0.000   0.000
+47211.00    -2.322  1.56    0.000   0.000
+47212.00    -2.798  1.458   0.000   0.000
+47213.00    -3.213  1.399   0.000   0.000
+47214.00    -3.384  1.199   0.000   0.000
+47215.00    -3.133  0.738   0.000   0.000
+47216.00    -2.691  0.267   0.000   0.000
+47217.00    -2.334  0.043   0.000   0.000
+47218.00    -2.133  0.049   0.000   0.000
+47219.00    -2.008  0.107   0.000   0.000
+47220.00    -1.886  0.152   0.000   0.000
+47221.00    -1.806  0.305   0.000   0.000
+47222.00    -1.78   0.644   0.000   0.000
+47223.00    -1.746  1.027   0.000   0.000
+47224.00    -1.693  1.236   0.000   0.000
+47225.00    -1.69   1.215   0.000   0.000
+47226.00    -1.765  1.079   0.000   0.000
+47227.00    -1.843  0.932   0.000   0.000
+47228.00    -1.821  0.76    0.000   0.000
+47229.00    -1.631  0.521   0.000   0.000
+47230.00    -1.299  0.274   0.000   0.000
+47231.00    -1.074  0.205   0.000   0.000
+47232.00    -1.146  0.257   0.000   0.000
+47233.00    -1.442  0.181   0.000   0.000
+47234.00    -1.67   -0.115  0.000   0.000
+47235.00    -1.614  -0.447  0.000   0.000
+47236.00    -1.494  -0.338  0.000   0.000
+47237.00    -1.542  0.149   0.000   0.000
+47238.00    -1.726  0.531   0.000   0.000
+47239.00    -1.862  0.477   0.000   0.000
+47240.00    -1.998  0.03    0.000   0.000
+47241.00    -2.134  -0.358  0.000   0.000
+47242.00    -2.077  -0.61   0.000   0.000
+47243.00    -1.661  -0.857  0.000   0.000
+47244.00    -1.017  -1.026  0.000   0.000
+47245.00    -0.501  -0.986  0.000   0.000
+47246.00    -0.273  -0.762  0.000   0.000
+47247.00    -0.204  -0.548  0.000   0.000
+47248.00    -0.151  -0.456  0.000   0.000
+47249.00    -0.14   -0.392  0.000   0.000
+47250.00    -0.229  -0.239  0.000   0.000
+47251.00    -0.383  -0.046  0.000   0.000
+47252.00    -0.525  0.02    0.000   0.000
+47253.00    -0.657  -0.109  0.000   0.000
+47254.00    -0.822  -0.3    0.000   0.000
+47255.00    -0.987  -0.393  0.000   0.000
+47256.00    -1.071  -0.373  0.000   0.000
+47257.00    -1.048  -0.34   0.000   0.000
+47258.00    -0.947  -0.433  0.000   0.000
+47259.00    -0.807  -0.769  0.000   0.000
+47260.00    -0.636  -1.421  0.000   0.000
+47261.00    -0.403  -2.123  0.000   0.000
+47262.00    -0.055  -2.436  0.000   0.000
+47263.00    0.346   -2.302  0.000   0.000
+47264.00    0.616   -1.924  0.000   0.000
+47265.00    0.594   -1.606  0.000   0.000
+47266.00    0.456   -1.322  0.000   0.000
+47267.00    0.301   -1.09   0.000   0.000
+47268.00    0.038   -0.979  0.000   0.000
+47269.00    -0.402  -0.982  0.000   0.000
+47270.00    -0.975  -1.118  0.000   0.000
+47271.00    -1.409  -1.409  0.000   0.000
+47272.00    -1.415  -1.748  0.000   0.000
+47273.00    -0.92   -1.979  0.000   0.000
+47274.00    -0.472  -2.042  0.000   0.000
+47275.00    -0.354  -2.148  0.000   0.000
+47276.00    -0.41   -2.343  0.000   0.000
+47277.00    -0.513  -2.398  0.000   0.000
+47278.00    -0.625  -2.05   0.000   0.000
+47279.00    -0.626  -1.265  0.000   0.000
+47280.00    -0.652  -0.638  0.000   0.000
+47281.00    -0.733  -0.478  0.000   0.000
+47282.00    -0.802  -0.687  0.000   0.000
+47283.00    -0.839  -1.071  0.000   0.000
+47284.00    -0.812  -1.447  0.000   0.000
+47285.00    -0.632  -1.624  0.000   0.000
+47286.00    -0.298  -1.616  0.000   0.000
+47287.00    0.129   -1.546  0.000   0.000
+47288.00    0.572   -1.493  0.000   0.000
+47289.00    0.911   -1.503  0.000   0.000
+47290.00    1.015   -1.596  0.000   0.000
+47291.00    0.865   -1.71   0.000   0.000
+47292.00    0.517   -1.748  0.000   0.000
+47293.00    0.07    -1.642  0.000   0.000
+47294.00    -0.326  -1.402  0.000   0.000
+47295.00    -0.508  -1.109  0.000   0.000
+47296.00    -0.454  -0.913  0.000   0.000
+47297.00    -0.221  -0.918  0.000   0.000
+47298.00    0.118   -1.132  0.000   0.000
+47299.00    0.454   -1.465  0.000   0.000
+47300.00    0.772   -1.567  0.000   0.000
+47301.00    0.863   -1.53   0.000   0.000
+47302.00    0.63    -1.513  0.000   0.000
+47303.00    0.237   -1.614  0.000   0.000
+47304.00    -0.041  -1.736  0.000   0.000
+47305.00    -0.139  -1.718  0.000   0.000
+47306.00    -0.219  -1.497  0.000   0.000
+47307.00    -0.421  -1.172  0.000   0.000
+47308.00    -0.728  -0.94   0.000   0.000
+47309.00    -0.988  -0.921  0.000   0.000
+47310.00    -1.079  -1.093  0.000   0.000
+47311.00    -1.045  -1.351  0.000   0.000
+47312.00    -1.024  -1.623  0.000   0.000
+47313.00    -1.092  -1.871  0.000   0.000
+47314.00    -1.225  -2.034  0.000   0.000
+47315.00    -1.407  -2.049  0.000   0.000
+47316.00    -1.579  -1.964  0.000   0.000
+47317.00    -1.653  -1.895  0.000   0.000
+47318.00    -1.499  -1.945  0.000   0.000
+47319.00    -1.232  -2.061  0.000   0.000
+47320.00    -1.091  -2.097  0.000   0.000
+47321.00    -1.117  -1.979  0.000   0.000
+47322.00    -1.291  -1.717  0.000   0.000
+47323.00    -1.58   -1.402  0.000   0.000
+47324.00    -1.849  -1.181  0.000   0.000
+47325.00    -1.906  -1.144  0.000   0.000
+47326.00    -1.707  -1.243  0.000   0.000
+47327.00    -1.391  -1.326  0.000   0.000
+47328.00    -1.161  -1.263  0.000   0.000
+47329.00    -1.154  -1.08   0.000   0.000
+47330.00    -1.407  -0.964  0.000   0.000
+47331.00    -1.763  -1.049  0.000   0.000
+47332.00    -2.111  -1.318  0.000   0.000
+47333.00    -2.451  -1.542  0.000   0.000
+47334.00    -2.872  -1.467  0.000   0.000
+47335.00    -3.436  -1.377  0.000   0.000
+47336.00    -4.059  -1.476  0.000   0.000
+47337.00    -4.558  -1.672  0.000   0.000
+47338.00    -4.718  -1.735  0.000   0.000
+47339.00    -4.521  -1.491  0.000   0.000
+47340.00    -4.186  -1.094  0.000   0.000
+47341.00    -3.914  -0.826  0.000   0.000
+47342.00    -3.73   -0.75   0.000   0.000
+47343.00    -3.662  -0.827  0.000   0.000
+47344.00    -3.921  -1.103  0.000   0.000
+47345.00    -4.717  -1.679  0.000   0.000
+47346.00    -5.741  -2.148  0.000   0.000
+47347.00    -6.628  -2.04   0.000   0.000
+47348.00    -7.024  -1.525  0.000   0.000
+47349.00    -7.052  -0.787  0.000   0.000
+47350.00    -6.886  -0.459  0.000   0.000
+47351.00    -6.928  -0.619  0.000   0.000
+47352.00    -7.301  -0.988  0.000   0.000
+47353.00    -7.643  -1.347  0.000   0.000
+47354.00    -7.765  -1.655  0.000   0.000
+47355.00    -7.874  -2.1    0.000   0.000
+47356.00    -7.869  -2.41   0.000   0.000
+47357.00    -7.635  -2.093  0.000   0.000
+47358.00    -7.327  -1.325  0.000   0.000
+47359.00    -7.175  -0.574  0.000   0.000
+47360.00    -7.375  -0.135  0.000   0.000
+47361.00    -7.818  0.132   0.000   0.000
+47362.00    -8.289  0.326   0.000   0.000
+47363.00    -8.751  0.346   0.000   0.000
+47364.00    -9.22   -0.01   0.000   0.000
+47365.00    -9.503  -0.741  0.000   0.000
+47366.00    -9.535  -1.369  0.000   0.000
+47367.00    -9.456  -1.491  0.000   0.000
+47368.00    -9.497  -1.227  0.000   0.000
+47369.00    -9.521  -1.108  0.000   0.000
+47370.00    -9.421  -1.333  0.000   0.000
+47371.00    -9.136  -1.485  0.000   0.000
+47372.00    -8.803  -1.305  0.000   0.000
+47373.00    -8.733  -1.059  0.000   0.000
+47374.00    -9.075  -1.083  0.000   0.000
+47375.00    -9.672  -1.442  0.000   0.000
+47376.00    -10.262 -1.842  0.000   0.000
+47377.00    -10.662 -1.939  0.000   0.000
+47378.00    -10.747 -1.632  0.000   0.000
+47379.00    -10.588 -1.303  0.000   0.000
+47380.00    -10.368 -1.33   0.000   0.000
+47381.00    -10.192 -1.527  0.000   0.000
+47382.00    -10.022 -1.683  0.000   0.000
+47383.00    -9.794  -1.786  0.000   0.000
+47384.00    -9.552  -1.731  0.000   0.000
+47385.00    -9.397  -1.403  0.000   0.000
+47386.00    -9.376  -0.883  0.000   0.000
+47387.00    -9.491  -0.448  0.000   0.000
+47388.00    -9.72   -0.316  0.000   0.000
+47389.00    -9.971  -0.437  0.000   0.000
+47390.00    -10.123 -0.604  0.000   0.000
+47391.00    -10.205 -0.747  0.000   0.000
+47392.00    -10.397 -0.966  0.000   0.000
+47393.00    -10.766 -1.291  0.000   0.000
+47394.00    -11.131 -1.542  0.000   0.000
+47395.00    -11.277 -1.537  0.000   0.000
+47396.00    -11.185 -1.335  0.000   0.000
+47397.00    -10.99  -1.151  0.000   0.000
+47398.00    -10.81  -1.05   0.000   0.000
+47399.00    -10.733 -0.886  0.000   0.000
+47400.00    -10.846 -0.589  0.000   0.000
+47401.00    -11.141 -0.362  0.000   0.000
+47402.00    -11.436 -0.409  0.000   0.000
+47403.00    -11.607 -0.636  0.000   0.000
+47404.00    -11.795 -0.832  0.000   0.000
+47405.00    -12.2   -1.02   0.000   0.000
+47406.00    -12.762 -1.204  0.000   0.000
+47407.00    -13.165 -1.448  0.000   0.000
+47408.00    -13.239 -1.7    0.000   0.000
+47409.00    -13.053 -1.797  0.000   0.000
+47410.00    -12.622 -1.765  0.000   0.000
+47411.00    -11.938 -1.708  0.000   0.000
+47412.00    -11.08  -1.588  0.000   0.000
+47413.00    -10.257 -1.228  0.000   0.000
+47414.00    -9.698  -0.574  0.000   0.000
+47415.00    -9.448  -0.053  0.000   0.000
+47416.00    -9.473  0.009   0.000   0.000
+47417.00    -9.709  -0.336  0.000   0.000
+47418.00    -10.03  -0.813  0.000   0.000
+47419.00    -10.321 -1.176  0.000   0.000
+47420.00    -10.584 -1.381  0.000   0.000
+47421.00    -10.851 -1.522  0.000   0.000
+47422.00    -11.036 -1.624  0.000   0.000
+47423.00    -10.958 -1.642  0.000   0.000
+47424.00    -10.562 -1.58   0.000   0.000
+47425.00    -9.966  -1.506  0.000   0.000
+47426.00    -9.532  -1.382  0.000   0.000
+47427.00    -9.523  -1.096  0.000   0.000
+47428.00    -9.996  -0.668  0.000   0.000
+47429.00    -10.757 -0.33   0.000   0.000
+47430.00    -11.373 -0.294  0.000   0.000
+47431.00    -11.611 -0.456  0.000   0.000
+47432.00    -11.68  -0.521  0.000   0.000
+47433.00    -11.885 -0.427  0.000   0.000
+47434.00    -12.136 -0.45   0.000   0.000
+47435.00    -12.027 -0.829  0.000   0.000
+47436.00    -11.425 -1.362  0.000   0.000
+47437.00    -10.659 -1.612  0.000   0.000
+47438.00    -10.096 -1.346  0.000   0.000
+47439.00    -9.802  -0.615  0.000   0.000
+47440.00    -9.68   0.472   0.000   0.000
+47441.00    -9.673  1.342   0.000   0.000
+47442.00    -9.803  1.47    0.000   0.000
+47443.00    -10.097 1.079   0.000   0.000
+47444.00    -10.541 0.424   0.000   0.000
+47445.00    -11.122 -0.145  0.000   0.000
+47446.00    -11.735 -0.439  0.000   0.000
+47447.00    -12.164 -0.406  0.000   0.000
+47448.00    -12.223 -0.145  0.000   0.000
+47449.00    -11.874 0.093   0.000   0.000
+47450.00    -11.175 0.064   0.000   0.000
+47451.00    -10.311 -0.235  0.000   0.000
+47452.00    -9.469  -0.659  0.000   0.000
+47453.00    -8.822  -1.05   0.000   0.000
+47454.00    -8.561  -1.229  0.000   0.000
+47455.00    -8.456  -1.1    0.000   0.000
+47456.00    -8.48   -0.713  0.000   0.000
+47457.00    -8.623  -0.29   0.000   0.000
+47458.00    -8.619  -0.065  0.000   0.000
+47459.00    -8.291  -0.055  0.000   0.000
+47460.00    -7.836  -0.072  0.000   0.000
+47461.00    -7.589  0.022   0.000   0.000
+47462.00    -7.577  0.13    0.000   0.000
+47463.00    -7.49   0.088   0.000   0.000
+47464.00    -7.131  -0.066  0.000   0.000
+47465.00    -6.675  -0.142  0.000   0.000
+47466.00    -6.359  -0.035  0.000   0.000
+47467.00    -6.152  0.163   0.000   0.000
+47468.00    -5.85   0.359   0.000   0.000
+47469.00    -5.48   0.629   0.000   0.000
+47470.00    -5.301  1.072   0.000   0.000
+47471.00    -5.472  1.329   0.000   0.000
+47472.00    -5.851  1.258   0.000   0.000
+47473.00    -6.198  1.09    0.000   0.000
+47474.00    -6.467  0.902   0.000   0.000
+47475.00    -6.609  0.694   0.000   0.000
+47476.00    -6.371  0.536   0.000   0.000
+47477.00    -5.85   0.561   0.000   0.000
+47478.00    -5.318  0.737   0.000   0.000
+47479.00    -4.851  0.864   0.000   0.000
+47480.00    -4.436  0.84    0.000   0.000
+47481.00    -4.045  0.73    0.000   0.000
+47482.00    -3.721  0.67    0.000   0.000
+47483.00    -3.585  0.753   0.000   0.000
+47484.00    -3.753  0.979   0.000   0.000
+47485.00    -4.185  1.249   0.000   0.000
+47486.00    -4.658  1.407   0.000   0.000
+47487.00    -4.955  1.378   0.000   0.000
+47488.00    -5.071  1.231   0.000   0.000
+47489.00    -5.156  1.098   0.000   0.000
+47490.00    -5.282  1.044   0.000   0.000
+47491.00    -5.325  1.064   0.000   0.000
+47492.00    -5.158  1.158   0.000   0.000
+47493.00    -4.809  1.342   0.000   0.000
+47494.00    -4.386  1.581   0.000   0.000
+47495.00    -3.903  1.784   0.000   0.000
+47496.00    -3.338  1.883   0.000   0.000
+47497.00    -2.752  1.935   0.000   0.000
+47498.00    -2.286  2.025   0.000   0.000
+47499.00    -2.072  2.101   0.000   0.000
+47500.00    -2.17   2.024   0.000   0.000
+47501.00    -2.503  1.759   0.000   0.000
+47502.00    -2.969  1.422   0.000   0.000
+47503.00    -3.49   1.18    0.000   0.000
+47504.00    -3.964  1.131   0.000   0.000
+47505.00    -4.273  1.279   0.000   0.000
+47506.00    -4.278  1.485   0.000   0.000
+47507.00    -3.896  1.577   0.000   0.000
+47508.00    -3.323  1.53    0.000   0.000
+47509.00    -2.854  1.438   0.000   0.000
+47510.00    -2.721  1.42    0.000   0.000
+47511.00    -2.887  1.446   0.000   0.000
+47512.00    -3.226  1.524   0.000   0.000
+47513.00    -3.616  1.717   0.000   0.000
+47514.00    -3.893  2.052   0.000   0.000
+47515.00    -3.901  2.485   0.000   0.000
+47516.00    -3.67   2.594   0.000   0.000
+47517.00    -3.282  2.315   0.000   0.000
+47518.00    -2.825  1.921   0.000   0.000
+47519.00    -2.395  1.664   0.000   0.000
+47520.00    -2.047  1.631   0.000   0.000
+47521.00    -1.787  1.731   0.000   0.000
+47522.00    -1.611  1.817   0.000   0.000
+47523.00    -1.541  1.788   0.000   0.000
+47524.00    -1.61   1.679   0.000   0.000
+47525.00    -1.815  1.629   0.000   0.000
+47526.00    -2.118  1.714   0.000   0.000
+47527.00    -2.471  1.844   0.000   0.000
+47528.00    -2.806  1.872   0.000   0.000
+47529.00    -3.02   1.781   -1.200  0.700
+47530.00    -3.061  1.702   -1.200  0.600
+47531.00    -3.009  1.731   -1.300  0.500
+47532.00    -2.991  1.814   -1.500  0.400
+47533.00    -3.001  1.855   -1.600  0.300
+47534.00    -2.909  1.834   -1.700  0.200
+47535.00    -2.668  1.789   -1.800  0.300
+47536.00    -2.437  1.734   -1.900  0.300
+47537.00    -2.425  1.635   -2.100  0.400
+47538.00    -2.674  1.500   -2.100  0.600
+47539.00    -3.06   1.435   -2.200  0.700
+47540.00    -3.433  1.548   -2.100  0.800
+47541.00    -3.619  1.685   -2.000  0.800
+47542.00    -3.626  1.772   -1.900  0.900
+47543.00    -3.488  1.762   -1.800  1.000
+47544.00    -3.22   1.622   -1.700  1.000
+47545.00    -2.864  1.38    -1.700  1.000
+47546.00    -2.5    1.145   -1.700  1.000
+47547.00    -2.255  1.065   -1.700  1.000
+47548.00    -2.205  1.229   -1.700  0.900
+47549.00    -2.27   1.593   -1.800  0.800
+47550.00    -2.289  1.987   -2.100  0.700
+47551.00    -2.17   2.095   -2.400  0.600
+47552.00    -2.029  1.896   -2.600  0.500
+47553.00    -2.004  1.627   -2.900  0.400
+47554.00    -2.144  1.468   -3.000  0.300
+47555.00    -2.479  1.417   -2.700  0.300
+47556.00    -2.938  1.312   -2.300  0.400
+47557.00    -3.355  1.124   -1.900  0.500
+47558.00    -3.579  0.996   -1.400  0.600
+47559.00    -3.623  0.965   -1.000  0.700
+47560.00    -3.594  0.84    -0.300  1.000
+47561.00    -3.554  0.681   0.000   1.100
+47562.00    -3.323  0.617   0.200   1.100
+47563.00    -2.796  0.756   0.400   1.100
+47564.00    -2.277  1.069   0.300   1.100
+47565.00    -2.2    1.304   -0.200  0.900
+47566.00    -2.483  1.477   -0.800  0.700
+47567.00    -2.843  1.548   -1.500  0.500
+47568.00    -3.115  1.371   -2.200  0.300
+47569.00    -3.28   0.854   -2.800  0.100
+47570.00    -3.317  0.336   -2.800  -0.100
+47571.00    -3.205  -0.036  -2.800  -0.200
+47572.00    -2.952  -0.259  -2.700  -0.300
+47573.00    -2.609  -0.32   -2.600  -0.300
+47574.00    -2.269  -0.242  -2.500  -0.400
+47575.00    -2.044  -0.055  -2.500  -0.300
+47576.00    -2.02   0.113   -2.600  -0.200
+47577.00    -1.96   0.328   -2.800  -0.100
+47578.00    -1.794  0.592   -2.900  -0.100
+47579.00    -1.752  0.733   -3.000  0.000
+47580.00    -1.737  0.631   -3.000  -0.200
+47581.00    -1.716  0.394   -3.100  -0.400
+47582.00    -1.682  0.19    -3.100  -0.600
+47583.00    -1.614  0.022   -3.000  -0.800
+47584.00    -1.562  -0.203  -2.900  -1.000
+47585.00    -1.565  -0.471  -2.700  -1.100
+47586.00    -1.585  -0.744  -2.500  -1.100
+47587.00    -1.712  -0.899  -2.200  -1.200
+47588.00    -2.125  -1.004  -2.100  -1.200
+47589.00    -2.779  -1.219  -1.900  -1.100
+47590.00    -3.303  -1.423  -2.200  -1.000
+47591.00    -3.291  -1.42   -2.400  -0.800
+47592.00    -2.92   -1.089  -2.600  -0.700
+47593.00    -2.722  -0.613  -2.800  -0.500
+47594.00    -2.909  -0.34   -2.900  -0.500
+47595.00    -3.25   -0.371  -2.600  -0.700
+47596.00    -3.438  -0.559  -2.300  -1.000
+47597.00    -3.346  -0.792  -2.000  -1.400
+47598.00    -2.976  -1.099  -1.600  -1.700
+47599.00    -2.402  -1.481  -1.300  -2.100
+47600.00    -1.825  -1.792  -1.200  -2.300
+47601.00    -1.467  -1.894  -1.100  -2.500
+47602.00    -1.37   -1.811  -1.100  -2.700
+47603.00    -1.411  -1.674  -1.100  -2.800
+47604.00    -1.45   -1.5    -1.100  -2.800
+47605.00    -1.426  -1.205  -1.100  -2.700
+47606.00    -1.333  -0.837  -1.100  -2.500
+47607.00    -1.191  -0.622  -1.100  -2.400
+47608.00    -1.062  -0.755  -1.000  -2.200
+47609.00    -0.992  -1.208  -0.800  -2.100
+47610.00    -0.932  -1.799  -0.400  -2.200
+47611.00    -0.817  -2.283  0.100   -2.300
+47612.00    -0.676  -2.505  0.500   -2.500
+47613.00    -0.589  -2.497  0.900   -2.700
+47614.00    -0.601  -2.379  1.300   -2.900
+47615.00    -0.747  -2.296  1.400   -2.900
+47616.00    -1.083  -2.205  1.300   -2.900
+47617.00    -1.483  -2.227  1.100   -2.800
+47618.00    -1.605  -2.406  0.900   -2.800
+47619.00    -1.274  -2.457  0.500   -2.700
+47620.00    -0.737  -2.163  -0.400  -2.600
+47621.00    -0.446  -1.693  -1.100  -2.700
+47622.00    -0.585  -1.429  -1.700  -2.900
+47623.00    -0.953  -1.531  -2.200  -3.000
+47624.00    -1.275  -1.831  -2.500  -3.200
+47625.00    -1.416  -2.156  -2.200  -3.300
+47626.00    -1.283  -2.54   -1.600  -3.400
+47627.00    -0.88   -2.963  -1.000  -3.400
+47628.00    -0.458  -3.231  -0.400  -3.500
+47629.00    -0.354  -3.152  0.200   -3.500
+47630.00    -0.609  -2.809  0.400   -3.400
+47631.00    -0.926  -2.47   0.600   -3.300
+47632.00    -1.006  -2.277  0.600   -3.100
+47633.00    -0.847  -2.136  0.600   -3.000
+47634.00    -0.624  -1.949  0.600   -2.900
+47635.00    -0.455  -1.834  0.500   -2.900
+47636.00    -0.352  -2.123  0.500   -2.900
+47637.00    -0.307  -2.737  0.400   -2.900
+47638.00    -0.262  -3.226  0.400   -2.900
+47639.00    -0.105  -3.333  0.400   -2.900
+47640.00    -0.029  -3.419  0.400   -2.900
+47641.00    -0.132  -3.434  0.400   -2.900
+47642.00    -0.305  -3.219  0.400   -2.800
+47643.00    -0.448  -2.993  0.300   -2.800
+47644.00    -0.46   -3.073  0.100   -2.800
+47645.00    -0.417  -3.312  -0.500  -2.700
+47646.00    -0.329  -3.531  -1.200  -2.600
+47647.00    -0.221  -3.544  -1.900  -2.600
+47648.00    -0.259  -3.226  -2.500  -2.600
+47649.00    -0.592  -2.738  -3.000  -2.600
+47650.00    -1.131  -2.414  -2.900  -3.000
+47651.00    -1.632  -2.428  -2.500  -3.300
+47652.00    -1.911  -2.665  -2.000  -3.600
+47653.00    -1.927  -2.937  -1.500  -3.900
+47654.00    -1.697  -3.193  -1.100  -4.100
+47655.00    -1.315  -3.433  -1.000  -4.100
+47656.00    -0.908  -3.552  -1.000  -3.900
+47657.00    -0.807  -3.416  -1.000  -3.800
+47658.00    -1.149  -3.086  -1.100  -3.600
+47659.00    -1.591  -2.817  -1.300  -3.400
+47660.00    -1.73   -2.761  -1.400  -3.400
+47661.00    -1.542  -2.817  -1.600  -3.400
+47662.00    -1.304  -2.821  -1.800  -3.400
+47663.00    -1.217  -2.794  -1.900  -3.500
+47664.00    -1.289  -2.88   -1.900  -3.500
+47665.00    -1.474  -3.109  -1.800  -3.600
+47666.00    -1.742  -3.325  -1.500  -3.800
+47667.00    -2.029  -3.351  -1.300  -3.900
+47668.00    -2.271  -3.175  -1.100  -3.900
+47669.00    -2.461  -2.953  -1.000  -3.900
+47670.00    -2.646  -2.856  -1.000  -3.600
+47671.00    -2.844  -2.924  -1.200  -3.300
+47672.00    -2.892  -3.049  -1.300  -3.000
+47673.00    -2.671  -3.159  -1.600  -2.600
+47674.00    -2.25   -3.256  -1.900  -2.300
+47675.00    -1.815  -3.331  -2.000  -2.300
+47676.00    -1.617  -3.317  -2.000  -2.400
+47677.00    -1.819  -3.206  -2.000  -2.500
+47678.00    -2.379  -3.079  -1.900  -2.700
+47679.00    -3.02   -3.004  -1.700  -2.900
+47680.00    -3.5    -2.928  -2.500  -4.000
+47681.00    -3.764  -2.961  -1.800  -4.200
+47682.00    -3.751  -3.207  -1.100  -4.200
+47683.00    -3.297  -3.377  -0.400  -4.300
+47684.00    -2.656  -3.34   0.100   -4.300
+47685.00    -2.366  -3.131  -0.100  -4.100
+47686.00    -2.653  -2.815  -0.400  -3.900
+47687.00    -3.246  -2.562  -0.900  -3.600
+47688.00    -3.702  -2.517  -1.500  -3.500
+47689.00    -3.868  -2.623  -2.000  -3.300
+47690.00    -3.926  -2.667  -2.500  -3.400
+47691.00    -4.068  -2.62   -2.800  -3.600
+47692.00    -4.307  -2.62   -3.200  -3.800
+47693.00    -4.571  -2.736  -3.500  -4.000
+47694.00    -4.813  -2.873  -3.800  -4.200
+47695.00    -5.008  -2.898  -3.700  -4.100
+47696.00    -5.133  -2.772  -3.700  -3.900
+47697.00    -5.205  -2.578  -3.600  -3.800
+47698.00    -5.312  -2.425  -3.600  -3.600
+47699.00    -5.569  -2.37   -3.600  -3.500
+47700.00    -6.026  -2.393  -4.000  -3.400
+47701.00    -6.518  -2.412  -4.500  -3.400
+47702.00    -6.923  -2.389  -5.000  -3.400
+47703.00    -7.211  -2.343  -5.300  -3.500
+47704.00    -7.374  -2.319  -5.500  -3.600
+47705.00    -7.405  -2.351  -5.100  -3.700
+47706.00    -7.32   -2.35   -4.500  -3.900
+47707.00    -7.167  -2.34   -3.800  -4.100
+47708.00    -7.011  -2.417  -3.000  -4.300
+47709.00    -6.922  -2.596  -2.300  -4.400
+47710.00    -6.962  -2.833  -2.100  -3.700
+47711.00    -6.994  -2.92   -1.800  -3.400
+47712.00    -7.04   -2.777  -1.800  -3.100
+47713.00    -7.29   -2.5    -1.900  -2.900
+47714.00    -7.804  -2.239  -2.200  -2.700
+47715.00    -8.394  -2.153  -3.200  -2.700
+47716.00    -8.816  -2.192  -4.400  -2.800
+47717.00    -8.905  -2.159  -5.600  -3.000
+47718.00    -8.809  -2.14   -6.900  -3.100
+47719.00    -8.886  -2.266  -8.000  -3.200
+47720.00    -9.428  -2.596  -8.400  -3.000
+47721.00    -10.19  -2.93   -8.600  -2.900
+47722.00    -10.698 -2.919  -8.700  -2.700
+47723.00    -10.795 -2.614  -8.800  -2.500
+47724.00    -10.463 -2.132  -8.900  -2.300
+47725.00    -10.059 -2.055  -9.200  -2.100
+47726.00    -9.743  -2.295  -9.600  -2.000
+47727.00    -9.567  -2.371  -10.000 -1.900
+47728.00    -9.661  -2.255  -10.300 -1.900
+47729.00    -10.131 -2.18   -10.600 -1.900
+47730.00    -10.909 -2.338  -10.800 -2.000
+47731.00    -11.646 -2.566  -10.900 -2.200
+47732.00    -11.967 -2.561  -11.000 -2.400
+47733.00    -11.822 -2.305  -10.900 -2.700
+47734.00    -11.589 -2.28   -10.600 -2.900
+47735.00    -11.568 -2.484  -9.900  -3.000
+47736.00    -11.665 -2.69   -9.100  -3.100
+47737.00    -11.771 -2.791  -8.200  -3.200
+47738.00    -11.868 -2.721  -7.400  -3.200
+47739.00    -11.954 -2.494  -6.700  -3.200
+47740.00    -12.066 -2.239  -7.500  -3.000
+47741.00    -12.031 -2.043  -7.300  -2.900
+47742.00    -11.897 -1.859  -7.300  -2.800
+47743.00    -11.788 -1.9    -7.500  -2.700
+47744.00    -11.989 -2.102  -7.800  -2.600
+47745.00    -12.279 -2.116  -8.900  -2.900
+47746.00    -12.396 -2.014  -10.100 -3.100
+47747.00    -12.372 -2.12   -11.300 -3.400
+47748.00    -12.374 -2.245  -12.500 -3.600
+47749.00    -12.301 -2.371  -13.400 -3.800
+47750.00    -12.112 -2.497  -13.400 -3.500
+47751.00    -11.765 -2.541  -13.100 -3.200
+47752.00    -11.373 -2.544  -12.700 -2.800
+47753.00    -11.104 -2.612  -12.200 -2.500
+47754.00    -11.035 -2.702  -11.500 -2.200
+47755.00    -11.202 -2.649  -11.900 -2.100
+47756.00    -11.658 -2.397  -12.300 -2.100
+47757.00    -12.438 -2.064  -12.900 -2.100
+47758.00    -13.39  -1.855  -13.400 -2.200
+47759.00    -14.183 -1.884  -13.900 -2.300
+47760.00    -14.54  -2.158  -14.000 -2.400
+47761.00    -14.651 -2.453  -14.000 -2.600
+47762.00    -14.908 -2.502  -13.900 -2.700
+47763.00    -15.105 -2.373  -13.700 -2.800
+47764.00    -14.867 -2.227  -13.400 -2.800
+47765.00    -14.528 -2.188  -12.800 -2.600
+47766.00    -14.207 -2.234  -12.100 -2.300
+47767.00    -13.819 -2.244  -11.500 -2.000
+47768.00    -13.393 -2.093  -11.000 -1.600
+47769.00    -12.986 -1.746  -10.600 -1.300
+47770.00    -12.682 -1.276  -11.100 -1.300
+47771.00    -12.674 -1.056  -11.700 -1.300
+47772.00    -12.995 -1.235  -12.500 -1.300
+47773.00    -13.447 -1.6    -13.200 -1.400
+47774.00    -13.798 -1.912  -14.000 -1.600
+47775.00    -13.998 -2.114  -15.200 -2.200
+47776.00    -14.18  -2.328  -15.200 -2.400
+47777.00    -14.405 -2.593  -15.000 -2.600
+47778.00    -14.475 -2.758  -14.700 -2.800
+47779.00    -14.179 -2.633  -14.200 -2.900
+47780.00    -13.567 -2.263  -13.600 -2.700
+47781.00    -13.092 -2.005  -12.900 -2.500
+47782.00    -12.949 -1.963  -12.200 -2.300
+47783.00    -13.034 -1.931  -11.500 -2.100
+47784.00    -13.265 -1.787  -11.000 -2.000
+47785.00    -13.568 -1.646  -11.000 -2.200
+47786.00    -13.888 -1.689  -11.200 -2.400
+47787.00    -14.181 -1.879  -11.400 -2.700
+47788.00    -14.344 -1.963  -11.600 -2.900
+47789.00    -14.445 -1.837  -11.800 -3.200
+47790.00    -14.505 -1.616  -11.800 -3.200
+47791.00    -14.337 -1.591  -11.700 -3.200
+47792.00    -13.938 -1.739  -11.500 -3.100
+47793.00    -13.522 -1.829  -11.400 -3.000
+47794.00    -13.256 -1.784  -11.300 -2.800
+47795.00    -13.175 -1.648  -11.400 -2.500
+47796.00    -13.275 -1.498  -11.500 -2.100
+47797.00    -13.537 -1.356  -11.600 -1.800
+47798.00    -13.477 -1.04   -11.800 -1.500
+47799.00    -13.14  -0.722  -11.900 -1.400
+47800.00    -12.924 -0.7    -12.000 -1.500
+47801.00    -12.882 -0.934  -12.000 -1.700
+47802.00    -12.851 -1.204  -12.000 -2.000
+47803.00    -12.77  -1.428  -12.000 -2.300
+47804.00    -12.802 -1.725  -11.900 -2.600
+47805.00    -13.131 -2.168  -11.200 -2.500
+47806.00    -13.389 -2.349  -10.800 -2.500
+47807.00    -13.249 -2.073  -10.500 -2.400
+47808.00    -12.736 -1.633  -10.200 -2.400
+47809.00    -12.048 -1.419  -10.000 -2.300
+47810.00    -11.547 -1.452  -10.200 -2.200
+47811.00    -11.609 -1.575  -10.400 -2.200
+47812.00    -12.103 -1.596  -10.700 -2.100
+47813.00    -12.606 -1.465  -11.000 -2.100
+47814.00    -12.726 -1.359  -11.200 -2.000
+47815.00    -12.292 -1.33   -10.800 -2.000
+47816.00    -11.657 -1.269  -10.400 -2.000
+47817.00    -11.31  -1.163  -10.000 -2.000
+47818.00    -11.338 -1.147  -9.700  -2.000
+47819.00    -11.348 -1.336  -9.500  -1.900
+47820.00    -11.157 -1.486  -9.900  -1.700
+47821.00    -10.927 -1.355  -10.400 -1.500
+47822.00    -10.805 -1.037  -11.000 -1.300
+47823.00    -10.745 -0.752  -11.600 -1.100
+47824.00    -10.569 -0.451  -12.100 -0.900
+47825.00    -10.252 -0.166  -12.300 -0.900
+47826.00    -10.005 0.038   -12.400 -1.000
+47827.00    -9.895  0.103   -12.300 -1.000
+47828.00    -9.975  -0.046  -12.200 -1.100
+47829.00    -10.292 -0.433  -12.000 -1.200
+47830.00    -10.684 -0.863  -11.600 -1.200
+47831.00    -10.804 -1.026  -11.100 -1.200
+47832.00    -10.512 -0.958  -10.600 -1.200
+47833.00    -9.907  -0.74   -9.900  -1.200
+47834.00    -9.335  -0.504  -9.300  -1.200
+47835.00    -9.139  -0.445  -8.700  -1.300
+47836.00    -9.19   -0.484  -8.100  -1.300
+47837.00    -9.193  -0.478  -7.400  -1.200
+47838.00    -9.015  -0.393  -7.000  -1.100
+47839.00    -8.806  -0.334  -6.500  -2.000
+47840.00    -8.863  -0.142  -6.100  -2.100
+47841.00    -8.961  0.032   -5.600  -2.200
+47842.00    -8.674  -0.059  -5.200  -2.300
+47843.00    -7.872  -0.305  -4.700  -2.200
+47844.00    -7.039  -0.195  -4.300  -2.100
+47845.00    -6.495  0.191   -4.300  -1.900
+47846.00    -6.337  0.431   -4.300  -1.700
+47847.00    -6.434  0.300   -4.300  -1.600
+47848.00    -6.643  -0.055  -4.400  -1.400
+47849.00    -6.754  -0.067  -4.400  -1.300
+47850.00    -6.776  0.284   -4.600  -1.200
+47851.00    -6.885  0.718   -4.900  -1.100
+47852.00    -6.957  0.939   -5.100  -1.000
+47853.00    -6.907  0.842   -5.300  -1.000
+47854.00    -6.854  0.687   -5.400  -1.100
+47855.00    -6.879  0.709   -5.400  -1.200
+47856.00    -6.941  0.758   -5.400  -1.300
+47857.00    -6.964  0.658   -5.400  -1.400
+47858.00    -6.926  0.529   -5.200  -1.600
+47859.00    -6.771  0.53    -5.000  -1.600
+47860.00    -6.457  0.638   -4.600  -1.600
+47861.00    -6.052  0.723   -4.100  -1.500
+47862.00    -5.744  0.715   -3.700  -1.300
+47863.00    -5.673  0.678   -3.300  -1.200
+47864.00    -5.83   0.73    -3.100  -1.100
+47865.00    -6.112  0.942   -2.800  -0.500
+47866.00    -6.361  1.201   -3.700  -0.200
+47867.00    -6.593  1.458   -4.700  0.100
+47868.00    -6.927  1.700   -5.600  0.000
+47869.00    -7.324  1.798   -6.100  -0.100
+47870.00    -7.52   1.614   -6.400  -0.300
+47871.00    -7.271  1.201   -6.200  -0.500
+47872.00    -6.656  0.818   -5.400  -0.800
+47873.00    -6.017  0.674   -4.300  -0.900
+47874.00    -5.577  0.722   -3.100  -0.900
+47875.00    -5.311  0.809   -1.900  -1.000
+47876.00    -5.099  0.845   -1.100  -0.900
+47877.00    -4.999  0.886   -0.600  -0.800
+47878.00    -5.115  0.98    -0.700  -0.600
+47879.00    -5.335  1.034   -1.100  -0.500
+47880.00    -5.551  0.928   -1.600  -0.200
+47881.00    -5.702  0.781   -2.200  -0.100
+47882.00    -5.778  0.77    -2.800  -0.100
+47883.00    -5.733  0.914   -3.200  -0.300
+47884.00    -5.662  0.99    -3.200  -0.500
+47885.00    -5.698  0.868   -3.300  -0.800
+47886.00    -5.835  0.701   -3.100  -0.900
+47887.00    -6.002  0.667   -2.800  -0.800
+47888.00    -6.036  0.809   -2.300  -0.700
+47889.00    -5.836  1.041   -1.900  -0.500
+47890.00    -5.536  1.174   -1.600  -0.300
+47891.00    -5.317  1.154   -1.700  -0.200
+47892.00    -5.258  1.052   -2.100  -0.100
+47893.00    -5.325  0.955   -2.700  -0.100
+47894.00    -5.434  0.921   -3.500  -0.100
+47895.00    -5.532  0.96    -4.500  1.000
+47896.00    -5.645  1.037   -6.100  0.400
+47897.00    -5.798  1.083   -6.300  0.000
+47898.00    -5.935  1.027   -5.300  -1.100
+47899.00    -5.982  0.875   -3.200  -1.500
+47900.00    -5.952  0.752   -2.100  -2.000
+47901.00    -5.846  0.787   -0.600  -2.100
+47902.00    -5.614  0.99    0.500   -1.600
+47903.00    -5.156  1.202   0.700   -0.500
+47904.00    -4.657  1.186   0.300   0.600
+47905.00    -4.558  0.791   0.300   1.300
+47906.00    -4.895  0.227   -0.500  1.700
+47907.00    -5.367  -0.097  -1.900  1.600
+47908.00    -5.625  0.174   -3.500  1.200
+47909.00    -5.688  0.676   -4.900  0.500
+47910.00    -5.794  0.749   -6.500  -0.200
+47911.00    -6.115  0.316   -7.100  -0.900
+47912.00    -6.348  -0.22   -6.600  -1.300
+47913.00    -6.056  -0.296  -5.100  -1.600
+47914.00    -5.523  0.153   -3.400  -1.600
+47915.00    -5.199  0.61    -2.100  -1.600
+47916.00    -5.239  0.833   -1.300  -1.500
+47917.00    -5.306  0.528   -0.800  -1.200
+47918.00    -5.072  0.255   -1.000  -0.700
+47919.00    -4.695  0.241   -1.500  -0.300
+47920.00    -4.513  0.38    -2.000  0.000
+47921.00    -4.646  0.543   -2.600  0.100
+47922.00    -4.979  0.595   -3.500  0.200
+47923.00    -5.387  0.569   -4.100  -0.100
+47924.00    -5.757  0.544   -4.400  -0.600
+47925.00    -5.924  0.521   -5.600  -1.100
+47926.00    -5.909  0.493   -5.000  -1.900
+47927.00    -5.812  0.395   -3.700  -2.300
+47928.00    -5.695  0.18    -2.100  -2.300
+47929.00    -5.608  -0.101  -0.800  -2.100
+47930.00    -5.613  -0.236  0.200   -1.900
+47931.00    -5.538  -0.095  0.400   -1.500
+47932.00    -5.155  0.128   -0.300  -1.100
+47933.00    -4.645  0.42    -1.600  -0.800
+47934.00    -4.496  0.322   -3.000  -0.500
+47935.00    -4.55   0.047   -3.700  -0.300
+47936.00    -4.588  -0.052  -4.100  -0.500
+47937.00    -4.58   -0.123  -3.600  -0.800
+47938.00    -4.658  -0.063  -2.100  -1.400
+47939.00    -4.771  -0.063  -0.200  -1.800
+47940.00    -4.891  -0.211  1.400   -2.300
+47941.00    -5.017  -0.584  2.700   -2.700
+47942.00    -5.124  -0.975  3.900   -3.000
+47943.00    -5.061  -1.128  4.800   -3.000
+47944.00    -4.895  -1.067  5.000   -2.700
+47945.00    -4.818  -1.115  4.500   -2.400
+47946.00    -4.681  -1.271  3.100   -2.100
+47947.00    -4.374  -1.311  1.100   -1.700
+47948.00    -4.12   -1.136  -1.500  -1.600
+47949.00    -4.212  -0.833  -3.200  -1.600
+47950.00    -4.643  -0.531  -4.200  -1.800
+47951.00    -5.149  -0.441  -4.700  -2.000
+47952.00    -5.455  -0.687  -4.700  -2.400
+47953.00    -5.458  -1.084  -4.000  -2.800
+47954.00    -5.273  -1.425  -3.700  -3.100
+47955.00    -5.072  -1.603  -2.900  -4.400
+47956.00    -4.927  -1.679  -1.600  -4.500
+47957.00    -4.81   -1.754  -0.500  -4.200
+47958.00    -4.685  -1.871  -0.100  -3.900
+47959.00    -4.569  -2.021  -0.800  -2.900
+47960.00    -4.455  -2.075  -0.200  -2.200
+47961.00    -4.336  -2.058  -1.100  -1.800
+47962.00    -4.191  -2.087  -2.300  -2.000
+47963.00    -4.072  -2.19   -3.200  -2.800
+47964.00    -4.119  -2.247  -4.100  -3.500
+47965.00    -4.374  -2.114  -5.400  -4.000
+47966.00    -4.518  -2.007  -6.100  -4.400
+47967.00    -4.489  -2.115  -5.800  -5.100
+47968.00    -4.359  -2.468  -4.700  -5.400
+47969.00    -4.103  -2.917  -3.400  -5.600
+47970.00    -3.685  -3.155  -1.600  -5.500
+47971.00    -3.27   -3.08   -0.400  -5.400
+47972.00    -3.127  -2.907  -0.100  -4.700
+47973.00    -3.345  -2.908  -0.500  -4.000
+47974.00    -3.641  -3.098  -1.200  -3.500
+47975.00    -3.63   -3.103  -1.700  -3.100
+47976.00    -3.45   -2.746  -2.600  -2.900
+47977.00    -3.53   -2.289  -3.400  -2.800
+47978.00    -3.969  -2.087  -3.900  -3.200
+47979.00    -4.431  -2.231  -4.100  -3.600
+47980.00    -4.584  -2.575  -4.000  -4.100
+47981.00    -4.402  -2.928  -3.400  -4.700
+47982.00    -4.078  -3.196  -2.400  -4.800
+47983.00    -3.828  -3.361  -1.100  -4.700
+47984.00    -3.771  -3.404  -0.300  -4.700
+47985.00    -3.878  -3.323  1.800   -4.900
+47986.00    -3.883  -3.167  2.300   -4.600
+47987.00    -3.741  -3.132  1.700   -4.100
+47988.00    -3.512  -3.186  0.200   -3.700
+47989.00    -3.248  -3.205  -2.000  -3.800
+47990.00    -2.983  -3.162  -3.500  -3.700
+47991.00    -2.75   -3.182  -4.500  -3.700
+47992.00    -2.623  -3.343  -4.700  -4.100
+47993.00    -2.629  -3.572  -3.400  -4.700
+47994.00    -2.656  -3.761  -2.500  -5.400
+47995.00    -2.6    -3.839  -1.600  -6.000
+47996.00    -2.5    -3.965  -0.500  -6.500
+47997.00    -2.449  -4.201  0.700   -6.700
+47998.00    -2.44   -4.385  1.900   -6.300
+47999.00    -2.42   -4.363  2.400   -5.700
+48000.00    -2.36   -4.108  3.000   -4.900
+48001.00    -2.168  -4.005  2.700   -4.400
+48002.00    -2.009  -4.077  1.800   -3.800
+48003.00    -1.889  -4.052  0.300   -3.500
+48004.00    -1.92   -3.759  -1.300  -3.600
+48005.00    -2.345  -3.414  -2.700  -3.900
+48006.00    -3.129  -3.306  -3.600  -4.400
+48007.00    -3.837  -3.545  -3.700  -4.800
+48008.00    -4.046  -3.976  -3.000  -5.500
+48009.00    -3.799  -4.347  -2.000  -5.900
+48010.00    -3.343  -4.605  -1.000  -6.200
+48011.00    -2.922  -4.77   -0.400  -6.300
+48012.00    -2.749  -4.768  0.000   -5.900
+48013.00    -2.935  -4.54   -0.400  -5.300
+48014.00    -3.341  -4.205  -0.800  -5.100
+48015.00    -3.624  -4.009  -2.400  -5.100
+48016.00    -3.476  -3.93   -2.000  -5.300
+48017.00    -3.004  -3.852  -1.600  -5.500
+48018.00    -2.551  -3.796  -1.100  -5.700
+48019.00    -2.348  -3.915  -0.600  -5.800
+48020.00    -2.366  -4.162  -0.300  -5.900
+48021.00    -2.447  -4.518  0.100   -5.800
+48022.00    -2.499  -4.814  0.300   -5.800
+48023.00    -2.506  -4.9    0.500   -5.600
+48024.00    -2.579  -4.84   0.700   -5.500
+48025.00    -2.889  -4.732  0.600   -5.100
+48026.00    -3.287  -4.619  0.500   -4.800
+48027.00    -3.416  -4.493  0.300   -4.400
+48028.00    -3.169  -4.325  0.000   -4.000
+48029.00    -2.805  -4.245  -0.400  -3.700
+48030.00    -2.654  -4.302  -0.900  -3.600
+48031.00    -2.79   -4.313  -1.400  -3.500
+48032.00    -3.185  -4.11   -1.900  -3.400
+48033.00    -3.788  -3.83   -2.400  -3.400
+48034.00    -4.365  -3.772  -2.700  -3.500
+48035.00    -4.574  -4.002  -2.400  -3.700
+48036.00    -4.438  -4.247  -2.100  -4.000
+48037.00    -4.175  -4.29   -1.700  -4.200
+48038.00    -3.933  -4.241  -1.300  -4.500
+48039.00    -3.807  -4.307  -1.100  -4.700
+48040.00    -3.956  -4.5    -1.600  -4.700
+48041.00    -4.445  -4.561  -2.300  -4.700
+48042.00    -5.012  -4.352  -2.900  -4.600
+48043.00    -5.291  -4.111  -3.500  -4.600
+48044.00    -5.121  -4.051  -4.000  -4.500
+48045.00    -4.782  -4.117  -4.200  -4.500
+48046.00    -4.617  -4.167  -3.800  -4.600
+48047.00    -4.691  -4.241  -3.500  -4.700
+48048.00    -4.816  -4.484  -3.200  -4.800
+48049.00    -4.988  -4.81   -2.800  -4.900
+48050.00    -5.352  -4.919  -3.100  -5.000
+48051.00    -5.733  -4.713  -3.200  -5.100
+48052.00    -5.847  -4.407  -3.500  -5.200
+48053.00    -5.677  -4.286  -3.400  -5.200
+48054.00    -5.566  -4.224  -3.600  -5.100
+48055.00    -5.668  -4.056  -3.900  -5.000
+48056.00    -5.743  -3.892  -4.200  -4.900
+48057.00    -5.677  -3.856  -4.500  -5.000
+48058.00    -5.61   -3.958  -4.600  -5.100
+48059.00    -5.738  -4.124  -4.700  -5.100
+48060.00    -6.171  -4.257  -4.700  -5.000
+48061.00    -6.752  -4.225  -4.500  -5.300
+48062.00    -7.158  -3.999  -4.500  -5.300
+48063.00    -7.316  -4.027  -4.400  -5.500
+48064.00    -7.168  -4.136  -4.500  -5.400
+48065.00    -6.926  -4.188  -4.800  -5.200
+48066.00    -6.675  -4.12   -5.400  -5.000
+48067.00    -6.414  -4.02   -5.800  -4.800
+48068.00    -6.327  -3.953  -6.400  -4.300
+48069.00    -6.739  -3.849  -6.800  -4.300
+48070.00    -7.601  -3.766  -7.100  -4.200
+48071.00    -8.43   -3.773  -7.200  -4.500
+48072.00    -8.728  -3.828  -7.300  -4.500
+48073.00    -8.542  -3.825  -7.600  -4.700
+48074.00    -8.459  -3.787  -7.500  -4.800
+48075.00    -8.787  -3.754  -7.300  -4.900
+48076.00    -9.397  -3.703  -7.900  -4.700
+48077.00    -9.992  -3.676  -8.400  -4.300
+48078.00    -10.323 -3.672  -8.800  -4.000
+48079.00    -10.246 -3.639  -9.200  -4.100
+48080.00    -10.004 -3.487  -9.700  -4.000
+48081.00    -9.772  -3.317  -9.700  -4.100
+48082.00    -9.655  -3.273  -9.800  -4.200
+48083.00    -9.746  -3.366  -9.400  -4.300
+48084.00    -10.081 -3.539  -9.100  -4.400
+48085.00    -10.332 -3.763  -9.100  -4.500
+48086.00    -10.065 -3.973  -9.300  -4.700
+48087.00    -9.812  -4.033  -9.600  -4.800
+48088.00    -9.97   -3.942  -9.800  -4.900
+48089.00    -10.401 -3.848  -10.000 -4.700
+48090.00    -10.858 -3.894  -10.000 -4.200
+48091.00    -11.204 -4.072  -10.000 -4.100
+48092.00    -11.449 -4.181  -9.400  -4.000
+48093.00    -11.626 -4.029  -9.200  -3.900
+48094.00    -11.579 -3.786  -9.000  -3.800
+48095.00    -11.343 -3.696  -9.600  -4.100
+48096.00    -11.146 -3.725  -10.100 -4.300
+48097.00    -11.354 -3.702  -10.700 -4.500
+48098.00    -12.138 -3.742  -10.800 -5.100
+48099.00    -13.139 -3.769  -10.900 -5.400
+48100.00    -13.962 -3.601  -11.200 -5.500
+48101.00    -14.262 -3.468  -11.600 -5.200
+48102.00    -14.137 -3.41   -12.000 -4.800
+48103.00    -13.945 -3.297  -12.200 -4.000
+48104.00    -13.87  -3.145  -12.900 -3.600
+48105.00    -13.848 -3.185  -13.200 -3.300
+48106.00    -13.945 -3.272  -13.400 -2.800
+48107.00    -14.23  -3.269  -13.500 -2.500
+48108.00    -14.425 -3.215  -14.000 -2.300
+48109.00    -14.37  -3.177  -14.000 -2.400
+48110.00    -14.208 -3.2    -14.200 -2.400
+48111.00    -14.182 -3.268  -14.400 -2.600
+48112.00    -14.459 -3.311  -14.700 -2.900
+48113.00    -14.914 -3.134  -14.700 -3.500
+48114.00    -15.264 -3.016  -14.800 -3.800
+48115.00    -15.387 -3.111  -14.400 -4.000
+48116.00    -15.368 -3.36   -13.900 -3.900
+48117.00    -15.286 -3.611  -13.400 -3.900
+48118.00    -15.186 -3.664  -12.600 -3.700
+48119.00    -15.126 -3.537  -11.500 -4.100
+48120.00    -15.245 -3.377  -11.200 -4.400
+48121.00    -15.362 -3.271  -11.800 -4.500
+48122.00    -15.271 -3.181  -12.800 -4.400
+48123.00    -14.941 -3.074  -14.100 -4.200
+48124.00    -14.468 -2.992  -15.100 -3.700
+48125.00    -14.067 -2.996  -15.300 -3.600
+48126.00    -14.02  -3.104  -15.100 -3.200
+48127.00    -14.442 -3.273  -15.100 -3.600
+48128.00    -15.057 -3.432  -14.900 -4.000
+48129.00    -15.493 -3.501  -14.700 -4.300
+48130.00    -15.571 -3.458  -15.000 -4.100
+48131.00    -15.369 -3.368  -14.900 -4.000
+48132.00    -15.088 -3.322  -14.500 -3.600
+48133.00    -14.878 -3.311  -13.100 -3.600
+48134.00    -14.829 -3.26   -12.500 -3.600
+48135.00    -14.952 -3.135  -12.400 -3.500
+48136.00    -15.171 -2.994  -12.600 -3.400
+48137.00    -15.405 -2.924  -13.000 -3.300
+48138.00    -15.643 -2.938  -13.200 -3.400
+48139.00    -15.972 -2.998  -13.700 -3.300
+48140.00    -16.51  -3.067  -14.300 -3.300
+48141.00    -17.064 -2.839  -14.500 -3.500
+48142.00    -17.382 -2.65   -14.200 -3.600
+48143.00    -17.322 -2.727  -14.500 -3.600
+48144.00    -16.91  -2.871  -14.600 -3.900
+48145.00    -16.405 -2.998  -14.900 -3.500
+48146.00    -15.884 -3.069  -15.100 -3.500
+48147.00    -15.574 -3.121  -15.200 -3.400
+48148.00    -15.571 -3.195  -15.200 -3.800
+48149.00    -15.637 -3.255  -14.800 -3.800
+48150.00    -15.505 -3.212  -14.600 -4.000
+48151.00    -15.167 -3.000  -14.700 -4.000
+48152.00    -14.817 -2.653  -14.800 -3.900
+48153.00    -14.667 -2.311  -14.800 -3.600
+48154.00    -14.846 -2.125  -14.800 -3.400
+48155.00    -15.359 -2.145  -14.800 -3.300
+48156.00    -15.866 -2.293  -14.700 -3.300
+48157.00    -16.053 -2.428  -14.500 -3.200
+48158.00    -15.806 -2.481  -14.200 -3.200
+48159.00    -15.223 -2.527  -14.300 -3.200
+48160.00    -14.637 -2.687  -14.300 -3.300
+48161.00    -14.285 -2.973  -14.300 -3.500
+48162.00    -14.164 -3.2    -14.100 -3.500
+48163.00    -14.147 -3.201  -14.000 -3.800
+48164.00    -14.274 -3.043  -13.900 -3.700
+48165.00    -14.698 -2.874  -14.100 -3.700
+48166.00    -15.259 -2.748  -14.400 -3.300
+48167.00    -15.616 -2.614  -14.500 -3.200
+48168.00    -15.537 -2.492  -14.300 -3.100
+48169.00    -15.169 -2.451  -13.900 -3.300
+48170.00    -14.752 -2.508  -14.400 -3.700
+48171.00    -14.406 -2.58   -13.700 -3.600
+48172.00    -14.125 -2.585  -13.000 -3.700
+48173.00    -13.94  -2.523  -12.600 -3.400
+48174.00    -14.111 -2.253  -12.800 -2.600
+48175.00    -14.374 -2.129  -12.800 -2.400
+48176.00    -14.419 -2.386  -13.200 -1.900
+48177.00    -14.192 -2.584  -13.100 -2.100
+48178.00    -13.791 -2.46   -13.000 -2.300
+48179.00    -13.375 -2.186  -12.900 -2.500
+48180.00    -13.114 -1.953  -12.900 -2.400
+48181.00    -13.025 -1.816  -12.700 -2.200
+48182.00    -12.995 -1.782  -12.600 -2.200
+48183.00    -12.987 -1.859  -12.100 -2.200
+48184.00    -13.034 -1.984  -11.900 -2.500
+48185.00    -13.137 -2.195  -12.000 -2.300
+48186.00    -13.082 -2.385  -12.400 -2.100
+48187.00    -12.685 -2.458  -12.000 -2.100
+48188.00    -12.047 -2.444  -10.800 -2.300
+48189.00    -11.556 -2.427  -9.700  -2.900
+48190.00    -11.444 -2.398  -9.300  -2.900
+48191.00    -11.368 -2.274  -9.400  -3.000
+48192.00    -11.158 -2.056  -9.900  -2.900
+48193.00    -11.03  -1.851  -10.600 -2.800
+48194.00    -11.317 -1.9    -10.900 -2.800
+48195.00    -11.651 -2.035  -10.800 -2.600
+48196.00    -11.755 -2.037  -10.600 -2.600
+48197.00    -11.62  -1.949  -10.100 -2.600
+48198.00    -11.27  -1.92   -9.600  -2.400
+48199.00    -10.804 -1.935  -9.500  -2.400
+48200.00    -10.492 -1.842  -10.000 -2.600
+48201.00    -10.567 -1.633  -10.900 -2.300
+48202.00    -10.943 -1.501  -11.200 -2.100
+48203.00    -11.255 -1.575  -11.100 -2.100
+48204.00    -11.248 -1.724  -10.900 -2.200
+48205.00    -10.997 -1.723  -10.300 -2.300
+48206.00    -10.717 -1.513  -9.800  -2.200
+48207.00    -10.492 -1.218  -9.400  -2.100
+48208.00    -10.299 -0.945  -9.000  -2.100
+48209.00    -10.141 -0.687  -9.100  -1.900
+48210.00    -10.061 -0.459  -9.000  -1.800
+48211.00    -10.119 -0.484  -8.900  -1.800
+48212.00    -10.25  -0.798  -8.900  -1.900
+48213.00    -10.285 -1.136  -8.900  -1.800
+48214.00    -10.021 -1.186  -9.000  -1.900
+48215.00    -9.368  -0.974  -9.100  -1.900
+48216.00    -8.648  -0.732  -9.200  -1.600
+48217.00    -8.373  -0.651  -9.300  -1.600
+48218.00    -8.595  -0.685  -9.300  -1.500
+48219.00    -8.985  -0.695  -9.400  -1.300
+48220.00    -9.224  -0.69   -9.200  -1.300
+48221.00    -9.307  -0.762  -9.000  -1.300
+48222.00    -9.369  -0.846  -8.800  -1.300
+48223.00    -9.474  -0.709  -8.300  -1.500
+48224.00    -9.602  -0.429  -7.900  -1.500
+48225.00    -9.691  -0.318  -7.500  -1.400
+48226.00    -9.439  -0.431  -7.000  -1.400
+48227.00    -8.764  -0.601  -6.600  -1.400
+48228.00    -8.039  -0.585  -6.200  -1.200
+48229.00    -7.766  -0.311  -6.000  -1.200
+48230.00    -8.074  -0.016  -6.900  -1.500
+48231.00    -8.507  -0.03   -7.000  -1.600
+48232.00    -8.601  -0.364  -7.100  -1.600
+48233.00    -8.434  -0.652  -7.100  -1.700
+48234.00    -8.327  -0.652  -7.200  -1.700
+48235.00    -8.364  -0.441  -7.200  -1.600
+48236.00    -8.415  -0.259  -7.200  -1.600
+48237.00    -8.443  -0.2    -7.200  -1.500
+48238.00    -8.454  -0.113  -7.100  -1.400
+48239.00    -8.431  0.047   -7.100  -1.300
+48240.00    -8.305  0.185   -7.100  -1.200
+48241.00    -8.147  0.135   -7.100  -1.100
+48242.00    -8.037  0.003   -7.100  -0.900
+48243.00    -7.999  -0.026  -7.100  -0.800
+48244.00    -8.092  0.036   -7.200  -0.700
+48245.00    -8.252  0.117   -7.200  -0.700
+48246.00    -8.381  0.132   -7.100  -0.800
+48247.00    -8.297  0.063   -7.100  -0.900
+48248.00    -7.96   -0.016  -7.100  -0.900
+48249.00    -7.504  -0.072  -7.000  -1.000
+48250.00    -7.092  -0.118  -7.000  -1.100
+48251.00    -6.893  -0.101  -6.900  -1.100
+48252.00    -7.061  0.023   -6.900  -1.200
+48253.00    -7.614  0.125   -6.800  -1.100
+48254.00    -8.167  0.061   -6.800  -1.100
+48255.00    -8.243  -0.09   -6.800  -1.000
+48256.00    -7.885  -0.098  -6.700  -0.900
+48257.00    -7.51   0.134   -6.700  -0.800
+48258.00    -7.378  0.394   -6.600  -0.800
+48259.00    -7.377  0.386   -6.600  -0.700
+48260.00    -7.33   0.106   -6.600  -0.900
+48261.00    -7.116  -0.012  -6.700  -0.900
+48262.00    -6.952  0.157   -6.700  -0.900
+48263.00    -7.033  0.248   -6.600  -1.000
+48264.00    -7.208  0.045   -6.600  -1.000
+48265.00    -7.248  -0.143  -6.500  -1.000
+48266.00    -7.294  -0.146  -6.500  -1.000
+48267.00    -7.46   -0.042  -6.400  -1.000
+48268.00    -7.638  0.008   -6.400  -1.000
+48269.00    -7.753  -0.007  -6.400  -1.000
+48270.00    -7.869  0.062   -6.500  -0.900
+48271.00    -7.962  0.261   -6.600  -0.900
+48272.00    -7.901  0.404   -6.700  -0.800
+48273.00    -7.606  0.232   -6.800  -0.800
+48274.00    -7.207  -0.097  -6.900  -0.700
+48275.00    -6.976  -0.338  -7.000  -0.700
+48276.00    -6.944  -0.403  -7.100  -0.700
+48277.00    -6.989  -0.314  -7.200  -0.700
+48278.00    -7.048  -0.151  -7.300  -0.700
+48279.00    -7.174  0.019   -7.300  -0.700
+48280.00    -7.469  0.128   -7.400  -0.800
+48281.00    -7.933  0.155   -7.300  -0.800
+48282.00    -8.348  0.015   -7.200  -0.900
+48283.00    -8.406  -0.3    -7.100  -0.900
+48284.00    -7.969  -0.61   -6.800  -1.000
+48285.00    -7.17   -0.738  -6.500  -1.100
+48286.00    -6.388  -0.746  -6.200  -1.200
+48287.00    -5.811  -0.753  -5.800  -1.400
+48288.00    -5.482  -0.738  -5.500  -1.500
+48289.00    -5.521  -0.698  -5.100  -1.600
+48290.00    -5.999  -0.667  -5.600  -1.800
+48291.00    -6.696  -0.669  -5.500  -1.900
+48292.00    -7.245  -0.728  -5.400  -1.900
+48293.00    -7.48   -0.807  -5.300  -2.000
+48294.00    -7.517  -0.78   -5.200  -2.000
+48295.00    -7.461  -0.625  -5.100  -2.100
+48296.00    -7.282  -0.686  -5.000  -2.100
+48297.00    -6.97   -1.042  -5.000  -2.200
+48298.00    -6.671  -1.419  -4.900  -2.200
+48299.00    -6.553  -1.57   -4.900  -2.200
+48300.00    -6.611  -1.453  -5.000  -2.200
+48301.00    -6.635  -1.368  -5.100  -2.200
+48302.00    -6.576  -1.369  -5.200  -2.200
+48303.00    -6.571  -1.365  -5.300  -2.300
+48304.00    -6.721  -1.356  -5.500  -2.300
+48305.00    -6.999  -1.374  -5.600  -2.300
+48306.00    -7.288  -1.409  -5.800  -2.400
+48307.00    -7.482  -1.429  -5.900  -2.400
+48308.00    -7.553  -1.416  -6.100  -2.400
+48309.00    -7.587  -1.468  -6.200  -2.500
+48310.00    -7.667  -1.725  -6.300  -2.500
+48311.00    -7.708  -2.149  -6.300  -2.600
+48312.00    -7.582  -2.536  -6.400  -2.600
+48313.00    -7.253  -2.63   -6.400  -2.700
+48314.00    -6.828  -2.375  -6.400  -2.700
+48315.00    -6.463  -1.961  -6.400  -2.800
+48316.00    -6.35   -1.712  -6.400  -2.800
+48317.00    -6.423  -1.71   -6.400  -2.800
+48318.00    -6.584  -1.861  -6.400  -2.900
+48319.00    -6.804  -2.056  -6.500  -3.000
+48320.00    -7.014  -2.173  -6.300  -2.400
+48321.00    -7.138  -2.17   -6.300  -2.400
+48322.00    -7.128  -2.125  -6.300  -2.500
+48323.00    -6.91   -2.205  -6.400  -2.600
+48324.00    -6.464  -2.473  -6.500  -2.600
+48325.00    -5.902  -2.802  -6.600  -2.700
+48326.00    -5.448  -3.012  -6.700  -2.800
+48327.00    -5.317  -3.024  -6.900  -2.900
+48328.00    -5.544  -2.927  -7.000  -3.000
+48329.00    -5.948  -2.852  -7.100  -3.200
+48330.00    -6.35   -2.822  -7.100  -3.200
+48331.00    -6.707  -2.766  -7.100  -3.300
+48332.00    -6.887  -2.692  -7.000  -3.400
+48333.00    -6.928  -2.682  -6.900  -3.500
+48334.00    -6.963  -2.791  -6.800  -3.600
+48335.00    -6.974  -3.001  -6.600  -3.600
+48336.00    -6.848  -3.244  -6.400  -3.700
+48337.00    -6.577  -3.461  -6.200  -3.800
+48338.00    -6.261  -3.657  -6.000  -3.800
+48339.00    -5.961  -3.888  -5.800  -3.900
+48340.00    -5.617  -4.176  -5.600  -3.900
+48341.00    -5.165  -4.393  -5.400  -4.000
+48342.00    -4.907  -4.402  -5.200  -4.000
+48343.00    -4.92   -4.183  -5.000  -4.100
+48344.00    -5.118  -3.877  -4.900  -4.200
+48345.00    -5.334  -3.696  -4.600  -4.200
+48346.00    -5.428  -3.744  -4.400  -4.300
+48347.00    -5.355  -3.941  -4.200  -4.400
+48348.00    -5.172  -4.111  -4.100  -4.400
+48349.00    -4.967  -4.15   -4.000  -4.500
+48350.00    -4.883  -4.132  -4.000  -4.600
+48351.00    -4.834  -4.203  -4.100  -4.600
+48352.00    -4.711  -4.438  -4.100  -4.700
+48353.00    -4.535  -4.731  -4.100  -4.700
+48354.00    -4.408  -4.865  -4.100  -4.800
+48355.00    -4.435  -4.746  -4.100  -4.900
+48356.00    -4.593  -4.552  -4.100  -4.900
+48357.00    -4.697  -4.512  -4.200  -5.000
+48358.00    -4.612  -4.584  -4.200  -5.000
+48359.00    -4.379  -4.544  -4.300  -5.100
+48360.00    -4.256  -4.288  -4.400  -5.200
+48361.00    -4.453  -4.023  -4.500  -5.200
+48362.00    -4.898  -4.03   -4.600  -5.300
+48363.00    -5.316  -4.368  -4.800  -5.400
+48364.00    -5.479  -4.811  -5.100  -5.500
+48365.00    -5.34   -5.119  -5.200  -5.600
+48366.00    -5.1    -5.305  -5.400  -5.800
+48367.00    -4.95   -5.45   -5.500  -5.900
+48368.00    -4.888  -5.578  -5.600  -6.100
+48369.00    -4.781  -5.646  -5.600  -6.300
+48370.00    -4.551  -5.561  -5.500  -6.600
+48371.00    -4.358  -5.267  -5.400  -6.800
+48372.00    -4.312  -4.897  -5.200  -7.000
+48373.00    -4.342  -4.727  -5.000  -7.300
+48374.00    -4.325  -4.873  -4.800  -7.400
+48375.00    -4.234  -5.223  -4.600  -7.600
+48376.00    -4.196  -5.528  -4.500  -7.700
+48377.00    -4.323  -5.593  -4.300  -7.800
+48378.00    -4.506  -5.526  -4.200  -7.800
+48379.00    -4.559  -5.577  -4.100  -7.800
+48380.00    -4.478  -5.739  -3.800  -7.600
+48381.00    -4.345  -5.881  -3.900  -7.400
+48382.00    -4.17   -5.861  -4.000  -7.300
+48383.00    -3.938  -5.643  -4.200  -7.100
+48384.00    -3.766  -5.402  -4.300  -7.000
+48385.00    -3.882  -5.315  -4.400  -6.800
+48386.00    -4.231  -5.414  -4.500  -6.600
+48387.00    -4.546  -5.495  -4.700  -6.400
+48388.00    -4.762  -5.377  -4.900  -6.300
+48389.00    -5.019  -5.165  -5.100  -6.100
+48390.00    -5.347  -5.087  -5.400  -6.000
+48391.00    -5.545  -5.2    -5.800  -5.800
+48392.00    -5.432  -5.414  -6.200  -5.600
+48393.00    -5.108  -5.555  -6.700  -5.500
+48394.00    -4.869  -5.615  -7.100  -5.400
+48395.00    -4.892  -5.662  -7.400  -5.300
+48396.00    -5.14   -5.695  -7.700  -5.200
+48397.00    -5.432  -5.668  -7.900  -5.100
+48398.00    -5.63   -5.571  -8.000  -5.000
+48399.00    -5.81   -5.435  -8.000  -5.000
+48400.00    -5.978  -5.284  -7.900  -5.000
+48401.00    -6.086  -5.144  -7.700  -5.100
+48402.00    -6.133  -5.105  -7.500  -5.200
+48403.00    -6.087  -5.301  -7.300  -5.300
+48404.00    -5.953  -5.782  -7.000  -5.400
+48405.00    -5.781  -6.379  -6.800  -5.500
+48406.00    -5.569  -6.709  -6.500  -5.600
+48407.00    -5.318  -6.566  -6.300  -5.700
+48408.00    -5.291  -6.261  -6.100  -5.900
+48409.00    -5.61   -6.039  -5.900  -6.100
+48410.00    -6.055  -5.823  -6.500  -6.000
+48411.00    -6.284  -5.518  -6.700  -5.800
+48412.00    -6.168  -5.25   -6.800  -5.700
+48413.00    -6.03   -5.246  -7.000  -5.500
+48414.00    -6.211  -5.496  -7.200  -5.400
+48415.00    -6.785  -5.717  -7.400  -5.300
+48416.00    -7.525  -5.661  -7.700  -5.200
+48417.00    -8.189  -5.415  -8.000  -5.100
+48418.00    -8.576  -5.255  -8.300  -5.100
+48419.00    -8.545  -5.269  -8.600  -5.000
+48420.00    -8.255  -5.288  -9.000  -5.000
+48421.00    -7.917  -5.209  -9.200  -5.000
+48422.00    -7.706  -5.161  -9.400  -5.100
+48423.00    -7.679  -5.232  -9.600  -5.100
+48424.00    -7.83   -5.299  -9.600  -5.200
+48425.00    -8.136  -5.176  -9.500  -5.200
+48426.00    -8.462  -4.875  -9.300  -5.300
+48427.00    -8.565  -4.645  -9.000  -5.400
+48428.00    -8.332  -4.638  -8.700  -5.500
+48429.00    -7.965  -4.8    -8.300  -5.600
+48430.00    -7.811  -4.982  -7.900  -5.700
+48431.00    -8.017  -5.152  -7.400  -5.800
+48432.00    -8.521  -5.326  -7.000  -5.900
+48433.00    -9.139  -5.398  -6.700  -6.100
+48434.00    -9.525  -5.267  -6.400  -6.200
+48435.00    -9.661  -5.17   -6.200  -6.400
+48436.00    -9.769  -5.053  -6.100  -6.600
+48437.00    -10.016 -4.882  -6.100  -6.800
+48438.00    -10.402 -4.829  -6.200  -6.900
+48439.00    -10.71  -4.824  -6.300  -7.100
+48440.00    -10.699 -4.686  -6.800  -7.000
+48441.00    -10.451 -4.601  -7.200  -6.800
+48442.00    -10.264 -4.665  -7.900  -6.600
+48443.00    -10.327 -4.746  -8.400  -6.400
+48444.00    -10.606 -4.691  -8.800  -6.200
+48445.00    -10.986 -4.555  -9.600  -6.200
+48446.00    -11.405 -4.539  -10.200 -6.100
+48447.00    -11.798 -4.726  -10.900 -5.800
+48448.00    -11.999 -4.998  -11.400 -5.700
+48449.00    -12.132 -5.069  -11.800 -5.600
+48450.00    -12.357 -4.983  -11.500 -5.500
+48451.00    -12.478 -4.854  -11.600 -5.400
+48452.00    -12.266 -4.788  -11.700 -5.300
+48453.00    -12.268 -4.726  -11.800 -5.400
+48454.00    -12.998 -4.642  -11.800 -5.500
+48455.00    -13.903 -4.66   -12.000 -5.500
+48456.00    -14.393 -4.676  -12.100 -5.600
+48457.00    -14.254 -4.778  -12.300 -5.600
+48458.00    -14.009 -4.872  -12.600 -5.600
+48459.00    -14.205 -4.856  -12.900 -5.500
+48460.00    -14.833 -4.836  -13.300 -5.500
+48461.00    -15.457 -4.746  -13.700 -5.400
+48462.00    -15.667 -4.748  -14.100 -5.400
+48463.00    -15.531 -4.763  -14.600 -5.300
+48464.00    -15.331 -4.566  -15.000 -5.300
+48465.00    -15.227 -4.295  -15.300 -5.300
+48466.00    -15.26  -4.151  -15.700 -5.300
+48467.00    -15.242 -4.454  -16.000 -5.300
+48468.00    -15.1   -4.834  -16.200 -5.300
+48469.00    -14.997 -4.921  -16.400 -5.400
+48470.00    -15.069 -4.824  -16.600 -5.500
+48471.00    -15.354 -4.773  -16.800 -5.600
+48472.00    -15.846 -4.704  -17.000 -5.600
+48473.00    -16.345 -4.719  -17.200 -5.700
+48474.00    -16.595 -4.836  -15.800 -5.400
+48475.00    -16.529 -4.86   -16.000 -5.400
+48476.00    -16.317 -4.781  -16.200 -5.300
+48477.00    -16.217 -4.613  -16.400 -5.200
+48478.00    -16.291 -4.488  -16.700 -5.000
+48479.00    -16.385 -4.495  -16.900 -4.900
+48480.00    -16.46  -4.625  -17.200 -4.700
+48481.00    -16.781 -4.687  -17.500 -4.600
+48482.00    -17.533 -4.58   -17.800 -4.400
+48483.00    -18.378 -4.443  -18.100 -4.300
+48484.00    -18.744 -4.439  -18.300 -4.300
+48485.00    -18.442 -4.516  -18.400 -4.200
+48486.00    -17.924 -4.5    -18.400 -4.300
+48487.00    -17.63  -4.334  -18.300 -4.300
+48488.00    -17.572 -4.122  -18.200 -4.400
+48489.00    -17.533 -4.009  -18.100 -4.500
+48490.00    -17.306 -4.081  -17.900 -4.600
+48491.00    -16.927 -4.043  -17.800 -4.700
+48492.00    -16.758 -3.944  -17.700 -4.800
+48493.00    -16.866 -3.992  -17.600 -4.900
+48494.00    -17.094 -4.229  -17.600 -5.000
+48495.00    -17.324 -4.543  -17.500 -5.100
+48496.00    -17.565 -4.617  -17.600 -5.200
+48497.00    -17.813 -4.468  -17.600 -5.300
+48498.00    -18.113 -4.51   -17.700 -5.300
+48499.00    -18.328 -4.642  -17.800 -5.300
+48500.00    -18.443 -4.739  -18.000 -5.300
+48501.00    -18.529 -4.849  -18.100 -5.300
+48502.00    -18.582 -4.977  -18.400 -5.300
+48503.00    -18.582 -5.07   -18.700 -5.200
+48504.00    -18.493 -5.058  -19.000 -5.100
+48505.00    -18.352 -4.901  -19.100 -5.100
+48506.00    -18.377 -4.586  -19.300 -4.900
+48507.00    -18.509 -4.302  -19.500 -4.800
+48508.00    -18.51  -4.146  -19.600 -4.700
+48509.00    -18.391 -3.983  -19.700 -4.500
+48510.00    -18.644 -3.835  -19.600 -4.500
+48511.00    -19.249 -3.777  -19.400 -4.400
+48512.00    -19.719 -3.871  -19.300 -4.400
+48513.00    -19.703 -4.107  -19.000 -4.400
+48514.00    -19.204 -4.236  -18.700 -4.400
+48515.00    -18.511 -4.178  -18.400 -4.500
+48516.00    -17.877 -4.07   -18.100 -4.500
+48517.00    -17.416 -4.011  -17.900 -4.600
+48518.00    -17.214 -3.972  -17.700 -4.700
+48519.00    -17.332 -3.912  -17.600 -4.700
+48520.00    -17.725 -3.871  -17.600 -4.700
+48521.00    -18.181 -3.907  -17.700 -4.700
+48522.00    -18.536 -4.007  -17.800 -4.700
+48523.00    -18.737 -4.092  -17.900 -4.600
+48524.00    -18.768 -4.082  -18.100 -4.600
+48525.00    -18.671 -3.937  -18.100 -4.600
+48526.00    -18.468 -3.834  -18.100 -4.600
+48527.00    -18.199 -3.786  -18.100 -4.600
+48528.00    -17.883 -3.782  -18.000 -4.700
+48529.00    -17.537 -3.835  -17.800 -4.700
+48530.00    -17.176 -3.925  -17.700 -4.800
+48531.00    -16.831 -4.028  -17.600 -4.800
+48532.00    -16.627 -4.184  -17.500 -4.900
+48533.00    -16.658 -4.398  -17.400 -5.000
+48534.00    -16.845 -4.365  -17.400 -5.000
+48535.00    -17.038 -4.03   -17.400 -4.400
+48536.00    -17.147 -3.632  -17.400 -4.300
+48537.00    -17.156 -3.38   -17.400 -4.300
+48538.00    -17.118 -3.353  -17.300 -4.200
+48539.00    -17.198 -3.475  -17.200 -4.200
+48540.00    -17.302 -3.597  -17.100 -4.200
+48541.00    -17.215 -3.614  -17.000 -4.200
+48542.00    -16.857 -3.533  -16.800 -4.200
+48543.00    -16.315 -3.44   -16.600 -4.100
+48544.00    -15.775 -3.412  -16.300 -4.100
+48545.00    -15.401 -3.44   -16.100 -4.100
+48546.00    -15.23  -3.454  -15.900 -4.100
+48547.00    -15.191 -3.447  -15.600 -4.000
+48548.00    -15.376 -3.513  -15.400 -4.000
+48549.00    -15.685 -3.635  -15.300 -3.900
+48550.00    -15.882 -3.698  -15.200 -3.800
+48551.00    -15.9   -3.618  -15.000 -3.800
+48552.00    -15.848 -3.452  -14.900 -3.700
+48553.00    -15.783 -3.372  -14.800 -3.600
+48554.00    -15.673 -3.291  -14.800 -3.500
+48555.00    -15.454 -3.183  -14.700 -3.500
+48556.00    -15.125 -3.064  -14.700 -3.400
+48557.00    -14.759 -2.967  -14.700 -3.400
+48558.00    -14.422 -2.955  -14.700 -3.400
+48559.00    -14.129 -3.04   -14.700 -3.400
+48560.00    -13.868 -3.116  -14.800 -3.400
+48561.00    -13.691 -3.089  -14.800 -3.400
+48562.00    -13.684 -2.916  -14.700 -3.300
+48563.00    -13.719 -2.618  -14.600 -3.300
+48564.00    -13.698 -2.313  -14.400 -3.300
+48565.00    -13.595 -2.146  -14.400 -3.300
+48566.00    -13.471 -2.165  -14.400 -3.200
+48567.00    -13.386 -2.313  -14.400 -3.200
+48568.00    -13.287 -2.488  -14.100 -3.100
+48569.00    -13.136 -2.61   -13.800 -3.100
+48570.00    -13.035 -2.539  -13.500 -3.000
+48571.00    -12.898 -2.346  -13.200 -3.000
+48572.00    -12.593 -2.227  -13.000 -2.900
+48573.00    -12.21  -2.225  -12.800 -2.900
+48574.00    -12.014 -2.201  -12.600 -2.800
+48575.00    -11.987 -2.108  -12.500 -2.800
+48576.00    -12.041 -2.059  -12.400 -2.900
+48577.00    -12.194 -2.232  -12.400 -2.900
+48578.00    -12.421 -2.529  -12.400 -2.900
+48579.00    -12.615 -2.634  -12.400 -2.900
+48580.00    -12.702 -2.403  -12.400 -2.800
+48581.00    -12.665 -2.051  -12.400 -2.700
+48582.00    -12.373 -1.836  -12.400 -2.600
+48583.00    -11.997 -1.823  -12.400 -2.500
+48584.00    -11.728 -1.85   -12.400 -2.400
+48585.00    -11.627 -1.807  -12.400 -2.300
+48586.00    -11.648 -1.716  -12.400 -2.300
+48587.00    -11.67  -1.59   -12.500 -2.200
+48588.00    -11.741 -1.381  -12.500 -2.200
+48589.00    -11.722 -1.306  -12.500 -2.100
+48590.00    -11.67  -1.345  -12.400 -2.100
+48591.00    -11.714 -1.423  -12.400 -2.100
+48592.00    -11.882 -1.498  -12.400 -2.100
+48593.00    -12.099 -1.604  -12.300 -2.100
+48594.00    -12.168 -1.767  -12.200 -2.100
+48595.00    -11.861 -1.645  -12.000 -2.000
+48596.00    -11.349 -1.441  -11.800 -1.900
+48597.00    -10.898 -1.435  -11.700 -1.800
+48598.00    -10.537 -1.527  -11.600 -1.800
+48599.00    -10.209 -1.561  -11.500 -1.800
+48600.00    -9.989  -1.435  -11.400 -1.700
+48601.00    -10.029 -1.154  -11.300 -1.700
+48602.00    -10.288 -0.99   -11.300 -1.700
+48603.00    -10.438 -0.795  -11.300 -1.700
+48604.00    -10.313 -0.721  -11.200 -1.700
+48605.00    -10.178 -0.909  -11.200 -1.700
+48606.00    -10.387 -1.177  -11.100 -1.700
+48607.00    -10.792 -1.313  -11.100 -1.700
+48608.00    -11.055 -1.229  -11.100 -1.600
+48609.00    -11.019 -0.986  -11.100 -1.600
+48610.00    -10.627 -0.79   -11.000 -1.600
+48611.00    -10.018 -0.874  -11.000 -1.600
+48612.00    -9.412  -1.084  -11.000 -1.600
+48613.00    -9.1    -1.19   -11.000 -1.600
+48614.00    -9.251  -1.222  -10.900 -1.600
+48615.00    -9.653  -1.291  -10.800 -1.600
+48616.00    -9.981  -1.355  -10.600 -1.600
+48617.00    -10.162 -1.266  -10.500 -1.600
+48618.00    -10.284 -1.032  -10.300 -1.700
+48619.00    -10.401 -0.823  -10.200 -1.700
+48620.00    -10.471 -0.767  -10.100 -1.700
+48621.00    -10.473 -0.831  -10.000 -1.700
+48622.00    -10.437 -0.917  -9.900  -1.700
+48623.00    -10.354 -0.996  -9.900  -1.700
+48624.00    -10.201 -1.068  -9.900  -1.700
+48625.00    -10.003 -1.066  -10.100 -1.500
+48626.00    -9.781  -0.909  -10.100 -1.400
+48627.00    -9.535  -0.646  -10.000 -1.400
+48628.00    -9.315  -0.461  -9.900  -1.400
+48629.00    -9.222  -0.468  -9.900  -1.400
+48630.00    -9.265  -0.513  -9.800  -1.400
+48631.00    -9.283  -0.499  -9.800  -1.400
+48632.00    -9.144  -0.527  -9.800  -1.400
+48633.00    -8.96   -0.691  -9.800  -1.400
+48634.00    -8.956  -0.927  -9.800  -1.400
+48635.00    -9.257  -0.997  -9.900  -1.300
+48636.00    -9.832  -0.773  -10.000 -1.300
+48637.00    -10.496 -0.556  -10.200 -1.300
+48638.00    -10.968 -0.626  -10.400 -1.300
+48639.00    -10.999 -0.965  -10.500 -1.300
+48640.00    -10.616 -1.297  -10.500 -1.400
+48641.00    -10.135 -1.374  -10.500 -1.400
+48642.00    -9.818  -1.239  -10.500 -1.500
+48643.00    -9.6    -1.12   -10.400 -1.600
+48644.00    -9.442  -1.125  -10.300 -1.700
+48645.00    -9.437  -1.097  -10.200 -1.800
+48646.00    -9.644  -0.96   -10.100 -1.800
+48647.00    -9.925  -0.876  -9.900  -1.900
+48648.00    -10.025 -0.983  -9.800  -2.000
+48649.00    -9.89   -1.19   -9.700  -2.000
+48650.00    -9.674  -1.436  -9.600  -2.100
+48651.00    -9.421  -1.643  -9.500  -2.100
+48652.00    -9.104  -1.771  -9.500  -2.200
+48653.00    -8.786  -1.815  -9.500  -2.200
+48654.00    -8.534  -1.695  -9.700  -2.300
+48655.00    -8.416  -1.427  -9.600  -2.200
+48656.00    -8.473  -1.213  -9.400  -2.200
+48657.00    -8.697  -1.266  -9.200  -2.300
+48658.00    -9.004  -1.552  -9.000  -2.300
+48659.00    -9.303  -1.864  -9.100  -2.300
+48660.00    -9.505  -2.042  -9.200  -2.400
+48661.00    -9.55   -2.086  -9.400  -2.400
+48662.00    -9.572  -2.036  -9.500  -2.400
+48663.00    -9.777  -1.844  -9.700  -2.400
+48664.00    -10.21  -1.526  -9.800  -2.400
+48665.00    -10.677 -1.413  -10.000 -2.300
+48666.00    -11.01  -1.693  -10.200 -2.300
+48667.00    -11.097 -2.234  -10.300 -2.400
+48668.00    -10.912 -2.651  -10.500 -2.400
+48669.00    -10.611 -2.67   -10.600 -2.500
+48670.00    -10.353 -2.396  -10.700 -2.500
+48671.00    -10.123 -2.147  -10.700 -2.600
+48672.00    -9.889  -2.105  -10.800 -2.600
+48673.00    -9.71   -2.142  -10.800 -2.700
+48674.00    -9.651  -2.107  -10.800 -2.700
+48675.00    -9.67   -2.212  -10.800 -2.800
+48676.00    -9.614  -2.581  -10.700 -2.800
+48677.00    -9.548  -3.032  -10.700 -2.900
+48678.00    -9.711  -3.3    -10.600 -3.000
+48679.00    -10.007 -3.272  -10.500 -3.000
+48680.00    -10.167 -3.161  -10.300 -3.100
+48681.00    -10.006 -3.175  -10.100 -3.200
+48682.00    -9.601  -3.219  -9.900  -3.200
+48683.00    -9.196  -3.154  -9.700  -3.300
+48684.00    -8.927  -3.034  -9.500  -3.400
+48685.00    -8.78   -3.013  -8.800  -3.500
+48686.00    -8.712  -3.165  -8.700  -3.600
+48687.00    -8.712  -3.375  -8.600  -3.700
+48688.00    -8.72   -3.487  -8.500  -3.800
+48689.00    -8.57   -3.404  -8.500  -3.900
+48690.00    -8.316  -3.238  -8.500  -4.000
+48691.00    -8.202  -3.195  -8.600  -4.100
+48692.00    -8.423  -3.444  -8.700  -4.200
+48693.00    -8.89   -3.756  -8.700  -4.300
+48694.00    -9.379  -4.126  -8.800  -4.400
+48695.00    -9.542  -4.57   -8.900  -4.500
+48696.00    -9.272  -4.835  -9.000  -4.600
+48697.00    -8.884  -4.74   -9.000  -4.600
+48698.00    -8.774  -4.405  -9.000  -4.700
+48699.00    -9.03   -4.136  -9.000  -4.800
+48700.00    -9.299  -4.072  -8.900  -4.800
+48701.00    -9.392  -4.157  -8.900  -4.900
+48702.00    -9.326  -4.25   -8.800  -5.000
+48703.00    -9.135  -4.393  -8.600  -5.000
+48704.00    -8.781  -4.595  -8.500  -5.100
+48705.00    -8.367  -4.743  -8.400  -5.100
+48706.00    -8.153  -4.766  -8.400  -5.100
+48707.00    -8.233  -4.741  -8.300  -5.100
+48708.00    -8.38   -4.791  -8.300  -5.100
+48709.00    -8.381  -4.931  -8.400  -5.100
+48710.00    -8.272  -5.027  -8.400  -5.100
+48711.00    -8.252  -4.98   -8.500  -5.200
+48712.00    -8.359  -4.865  -8.600  -5.200
+48713.00    -8.384  -4.838  -8.700  -5.300
+48714.00    -8.108  -4.966  -8.800  -5.400
+48715.00    -7.774  -5.11   -8.600  -5.600
+48716.00    -7.78   -5.096  -8.500  -5.700
+48717.00    -8.052  -4.95   -8.300  -5.800
+48718.00    -8.319  -4.813  -8.200  -5.900
+48719.00    -8.369  -4.828  -8.200  -6.000
+48720.00    -8.188  -5.034  -8.200  -6.000
+48721.00    -7.961  -5.431  -8.300  -6.100
+48722.00    -7.72   -5.849  -8.400  -6.100
+48723.00    -7.475  -6.328  -8.500  -6.100
+48724.00    -7.264  -6.729  -8.600  -6.000
+48725.00    -7.173  -6.812  -8.600  -6.000
+48726.00    -7.289  -6.448  -8.700  -5.900
+48727.00    -7.598  -5.794  -8.700  -5.800
+48728.00    -7.914  -5.348  -8.700  -5.800
+48729.00    -8.16   -5.304  -8.700  -5.700
+48730.00    -8.288  -5.535  -8.600  -5.700
+48731.00    -8.269  -5.795  -8.500  -5.800
+48732.00    -8.171  -5.904  -8.400  -5.800
+48733.00    -8.15   -5.835  -8.300  -5.900
+48734.00    -8.251  -5.76   -8.200  -6.000
+48735.00    -8.374  -5.847  -8.100  -6.100
+48736.00    -8.323  -6.084  -7.900  -6.300
+48737.00    -7.965  -6.341  -7.800  -6.400
+48738.00    -7.453  -6.459  -7.700  -6.500
+48739.00    -7.106  -6.406  -7.600  -6.600
+48740.00    -7.126  -6.319  -7.500  -6.700
+48741.00    -7.404  -6.331  -7.500  -6.700
+48742.00    -7.558  -6.36   -7.500  -6.800
+48743.00    -7.533  -6.293  -7.500  -6.800
+48744.00    -7.501  -6.12   -7.500  -6.900
+48745.00    -7.542  -5.969  -7.700  -7.000
+48746.00    -7.62   -5.988  -7.800  -7.000
+48747.00    -7.679  -6.229  -7.800  -7.100
+48748.00    -7.682  -6.579  -7.900  -7.100
+48749.00    -7.567  -6.751  -7.900  -7.000
+48750.00    -7.42   -6.728  -7.900  -7.000
+48751.00    -7.278  -6.665  -7.900  -6.900
+48752.00    -7.084  -6.613  -7.900  -6.900
+48753.00    -6.797  -6.52   -7.900  -6.800
+48754.00    -6.485  -6.319  -7.800  -6.700
+48755.00    -6.393  -6.038  -7.700  -6.700
+48756.00    -6.689  -5.78   -7.700  -6.700
+48757.00    -7.14   -5.779  -7.600  -6.600
+48758.00    -7.495  -6.172  -7.600  -6.600
+48759.00    -7.641  -6.65   -7.700  -6.600
+48760.00    -7.555  -6.759  -7.700  -6.600
+48761.00    -7.372  -6.486  -7.900  -6.700
+48762.00    -7.283  -6.167  -8.100  -6.700
+48763.00    -7.418  -6.02   -8.400  -6.700
+48764.00    -7.812  -6.119  -8.700  -6.700
+48765.00    -8.062  -6.315  -8.900  -6.700
+48766.00    -8.052  -6.411  -9.100  -6.700
+48767.00    -7.984  -6.373  -9.400  -6.700
+48768.00    -8.16   -6.321  -9.500  -6.700
+48769.00    -8.719  -6.393  -9.700  -6.700
+48770.00    -9.404  -6.533  -9.800  -6.700
+48771.00    -9.853  -6.541  -9.900  -6.700
+48772.00    -10.001 -6.26   -9.900  -6.600
+48773.00    -10.019 -5.954  -9.900  -6.600
+48774.00    -10.041 -5.989  -10.300 -6.900
+48775.00    -9.928  -6.266  -9.600  -6.800
+48776.00    -9.536  -6.524  -9.200  -6.700
+48777.00    -9.196  -6.637  -8.900  -6.500
+48778.00    -9.147  -6.518  -8.700  -6.400
+48779.00    -9.249  -6.32   -8.700  -6.200
+48780.00    -9.325  -6.156  -8.900  -6.000
+48781.00    -9.268  -6.017  -9.300  -6.000
+48782.00    -9.13   -5.843  -9.800  -6.000
+48783.00    -9.122  -5.605  -10.200 -6.100
+48784.00    -9.404  -5.387  -10.700 -6.300
+48785.00    -9.962  -5.328  -11.000 -6.600
+48786.00    -10.577 -5.546  -11.100 -6.900
+48787.00    -11.015 -6.052  -10.900 -7.100
+48788.00    -11.238 -6.525  -10.600 -7.300
+48789.00    -11.461 -6.625  -10.300 -7.300
+48790.00    -11.671 -6.476  -10.100 -7.200
+48791.00    -11.576 -6.302  -10.000 -7.000
+48792.00    -11.402 -6.297  -10.200 -6.800
+48793.00    -11.581 -6.449  -10.500 -6.500
+48794.00    -11.896 -6.23   -11.100 -6.300
+48795.00    -11.841 -5.916  -11.800 -6.100
+48796.00    -11.617 -5.875  -12.600 -6.000
+48797.00    -11.86  -6.016  -13.400 -6.000
+48798.00    -12.582 -6.266  -14.200 -6.100
+48799.00    -13.435 -6.367  -14.700 -6.300
+48800.00    -14.003 -6.152  -14.900 -6.400
+48801.00    -14.186 -5.812  -14.700 -6.600
+48802.00    -14.124 -5.677  -14.500 -6.700
+48803.00    -13.968 -5.833  -14.300 -6.800
+48804.00    -13.824 -6.085  -13.900 -6.900
+48805.00    -13.758 -6.227  -13.600 -7.000
+48806.00    -13.855 -6.156  -13.900 -7.000
+48807.00    -14.145 -5.945  -14.300 -6.800
+48808.00    -14.556 -5.731  -14.800 -6.500
+48809.00    -14.958 -5.56   -15.400 -6.400
+48810.00    -15.276 -5.493  -16.000 -6.300
+48811.00    -15.475 -5.591  -16.400 -6.300
+48812.00    -15.508 -5.743  -16.600 -6.200
+48813.00    -15.635 -5.839  -16.800 -6.200
+48814.00    -15.918 -5.844  -16.700 -6.200
+48815.00    -16.2   -5.827  -16.400 -6.200
+48816.00    -16.348 -5.829  -16.200 -6.200
+48817.00    -16.372 -5.812  -16.000 -6.200
+48818.00    -16.368 -5.72   -16.200 -6.200
+48819.00    -16.389 -5.603  -16.500 -6.300
+48820.00    -16.483 -5.6    -17.000 -6.300
+48821.00    -16.639 -5.65   -17.500 -6.200
+48822.00    -16.815 -5.698  -18.200 -6.200
+48823.00    -16.864 -5.707  -19.000 -6.200
+48824.00    -16.785 -5.748  -19.600 -6.200
+48825.00    -16.887 -5.896  -20.100 -6.200
+48826.00    -17.38  -6.042  -20.500 -6.200
+48827.00    -18.06  -6.1    -20.600 -6.300
+48828.00    -18.518 -6.026  -20.400 -6.300
+48829.00    -18.481 -5.905  -20.000 -6.400
+48830.00    -18.074 -5.913  -19.600 -6.400
+48831.00    -17.673 -6.096  -19.100 -6.400
+48832.00    -17.47  -6.281  -18.600 -6.400
+48833.00    -17.567 -6.235  -18.400 -6.200
+48834.00    -17.998 -5.994  -18.300 -6.100
+48835.00    -18.641 -5.745  -18.400 -5.900
+48836.00    -19.285 -5.597  -18.700 -5.700
+48837.00    -19.786 -5.452  -19.100 -5.600
+48838.00    -20.107 -5.315  -19.400 -5.500
+48839.00    -20.223 -5.378  -19.900 -5.500
+48840.00    -20.052 -5.604  -20.200 -5.700
+48841.00    -19.775 -5.851  -20.500 -5.800
+48842.00    -19.707 -6.000  -20.400 -5.800
+48843.00    -19.894 -5.974  -20.300 -5.900
+48844.00    -20.048 -5.886  -19.800 -6.000
+48845.00    -19.997 -5.892  -19.700 -5.900
+48846.00    -19.901 -5.885  -19.800 -5.700
+48847.00    -19.828 -5.694  -20.100 -5.700
+48848.00    -19.995 -5.393  -20.500 -5.700
+48849.00    -20.418 -5.267  -21.100 -5.600
+48850.00    -20.925 -5.393  -21.700 -5.700
+48851.00    -21.248 -5.604  -22.200 -5.900
+48852.00    -21.189 -5.773  -22.600 -6.100
+48853.00    -20.899 -5.909  -22.700 -6.200
+48854.00    -20.838 -5.998  -22.400 -6.400
+48855.00    -21.209 -6.045  -21.900 -6.600
+48856.00    -21.78  -6.043  -21.300 -6.700
+48857.00    -21.812 -6.065  -20.800 -6.800
+48858.00    -21.32  -6.229  -20.300 -6.800
+48859.00    -20.771 -6.454  -20.000 -6.700
+48860.00    -20.499 -6.459  -20.000 -6.600
+48861.00    -20.712 -6.126  -20.300 -6.500
+48862.00    -21.237 -5.714  -20.700 -6.200
+48863.00    -21.607 -5.375  -21.400 -6.100
+48864.00    -21.624 -5.364  -22.100 -6.000
+48865.00    -21.714 -5.457  -22.700 -6.000
+48866.00    -22.079 -5.363  -23.200 -6.100
+48867.00    -22.354 -5.245  -23.300 -6.200
+48868.00    -22.168 -5.472  -23.200 -6.400
+48869.00    -21.532 -5.923  -23.000 -6.400
+48870.00    -20.903 -6.154  -22.700 -6.500
+48871.00    -20.689 -6.054  -22.000 -6.300
+48872.00    -20.853 -5.84   -21.400 -6.100
+48873.00    -21.168 -5.726  -20.900 -5.800
+48874.00    -21.529 -5.696  -20.800 -5.600
+48875.00    -21.797 -5.542  -20.900 -5.600
+48876.00    -21.955 -5.319  -21.200 -5.500
+48877.00    -22.048 -5.271  -21.600 -5.600
+48878.00    -22.091 -5.423  -22.200 -5.700
+48879.00    -22.054 -5.592  -22.500 -5.800
+48880.00    -21.857 -5.597  -22.900 -6.000
+48881.00    -21.544 -5.438  -22.800 -6.300
+48882.00    -21.401 -5.292  -22.500 -6.300
+48883.00    -21.524 -5.297  -22.200 -6.400
+48884.00    -21.76  -5.428  -21.800 -6.500
+48885.00    -21.891 -5.629  -21.300 -6.500
+48886.00    -21.759 -5.859  -21.000 -6.500
+48887.00    -21.433 -6.023  -20.800 -6.300
+48888.00    -21.182 -5.954  -20.700 -6.200
+48889.00    -21.242 -5.55   -20.800 -6.000
+48890.00    -21.562 -5.104  -21.000 -5.800
+48891.00    -21.819 -4.999  -21.300 -5.700
+48892.00    -21.78  -5.21   -21.600 -5.600
+48893.00    -21.641 -5.436  -21.800 -5.700
+48894.00    -21.721 -5.411  -21.900 -5.700
+48895.00    -21.956 -5.178  -21.800 -5.700
+48896.00    -21.949 -5.002  -21.500 -5.800
+48897.00    -21.509 -4.989  -21.100 -5.800
+48898.00    -20.922 -5.033  -20.600 -5.700
+48899.00    -20.563 -5.014  -20.100 -5.700
+48900.00    -20.472 -4.978  -20.000 -5.600
+48901.00    -20.475 -5.009  -19.900 -5.500
+48902.00    -20.52  -5.09   -20.200 -5.300
+48903.00    -20.652 -5.16   -20.600 -5.200
+48904.00    -20.838 -5.226  -21.200 -5.100
+48905.00    -20.948 -5.288  -21.700 -5.100
+48906.00    -20.956 -5.317  -21.900 -5.100
+48907.00    -20.915 -5.241  -22.000 -5.200
+48908.00    -20.796 -5.015  -21.900 -5.200
+48909.00    -20.515 -4.702  -21.400 -5.400
+48910.00    -20.151 -4.438  -20.700 -5.500
+48911.00    -19.851 -4.337  -19.800 -5.500
+48912.00    -19.728 -4.431  -19.200 -5.500
+48913.00    -19.704 -4.645  -18.600 -5.500
+48914.00    -19.601 -4.865  -18.200 -5.400
+48915.00    -19.281 -4.958  -18.000 -5.100
+48916.00    -18.761 -4.843  -18.000 -4.900
+48917.00    -18.351 -4.507  -18.200 -4.600
+48918.00    -18.228 -4.019  -18.600 -4.400
+48919.00    -18.293 -3.578  -18.900 -4.300
+48920.00    -18.35  -3.468  -19.100 -4.200
+48921.00    -18.391 -3.642  -19.200 -4.300
+48922.00    -18.517 -3.834  -19.100 -4.400
+48923.00    -18.692 -3.901  -18.700 -4.500
+48924.00    -18.711 -3.915  -18.200 -4.600
+48925.00    -18.381 -3.992  -17.700 -4.700
+48926.00    -17.788 -4.127  -17.100 -4.700
+48927.00    -17.256 -4.159  -16.600 -4.800
+48928.00    -16.939 -4.125  -16.300 -4.600
+48929.00    -16.823 -4.195  -16.300 -4.600
+48930.00    -16.88  -4.389  -15.800 -4.600
+48931.00    -17.026 -4.474  -16.200 -4.600
+48932.00    -17.23  -4.389  -16.800 -4.700
+48933.00    -17.402 -4.3    -17.400 -4.700
+48934.00    -17.483 -4.223  -18.000 -4.800
+48935.00    -17.5   -4.032  -18.500 -4.800
+48936.00    -17.451 -3.687  -18.400 -4.700
+48937.00    -17.249 -3.316  -18.000 -4.600
+48938.00    -16.86  -3.215  -17.300 -4.400
+48939.00    -16.307 -3.488  -16.700 -4.100
+48940.00    -15.731 -4.053  -16.100 -3.900
+48941.00    -15.464 -4.279  -15.800 -3.700
+48942.00    -15.578 -4.006  -15.600 -3.500
+48943.00    -15.855 -3.657  -15.600 -3.300
+48944.00    -16.111 -3.492  -16.000 -3.300
+48945.00    -16.326 -3.368  -16.600 -3.300
+48946.00    -16.531 -3.183  -17.200 -3.400
+48947.00    -16.739 -2.949  -17.300 -3.500
+48948.00    -16.915 -2.788  -17.300 -3.700
+48949.00    -16.98  -2.771  -17.100 -3.800
+48950.00    -16.881 -2.821  -16.600 -4.100
+48951.00    -16.62  -2.799  -15.900 -4.100
+48952.00    -16.254 -2.636  -15.200 -4.100
+48953.00    -15.821 -2.416  -14.400 -4.000
+48954.00    -15.352 -2.292  -13.600 -3.900
+48955.00    -14.899 -2.352  -13.000 -3.600
+48956.00    -14.56  -2.573  -12.700 -3.300
+48957.00    -14.452 -2.842  -12.700 -3.100
+48958.00    -14.536 -3.012  -13.100 -3.000
+48959.00    -14.543 -2.958  -13.600 -2.800
+48960.00    -14.584 -2.749  -14.400 -2.800
+48961.00    -14.752 -2.609  -15.100 -2.800
+48962.00    -14.959 -2.53   -15.500 -3.000
+48963.00    -15.06  -2.371  -15.700 -3.200
+48964.00    -14.963 -2.137  -15.700 -3.200
+48965.00    -14.659 -2.023  -15.600 -3.300
+48966.00    -14.29  -2.251  -15.300 -3.300
+48967.00    -13.928 -2.668  -14.900 -3.200
+48968.00    -13.673 -2.93   -14.500 -3.100
+48969.00    -13.636 -2.892  -14.200 -3.100
+48970.00    -13.827 -2.614  -13.900 -3.000
+48971.00    -14.091 -2.203  -13.900 -3.000
+48972.00    -14.26  -1.834  -13.900 -2.700
+48973.00    -14.219 -1.402  -14.200 -2.700
+48974.00    -14.027 -1.186  -14.400 -2.600
+48975.00    -13.866 -1.286  -14.700 -2.600
+48976.00    -13.858 -1.557  -14.800 -2.600
+48977.00    -13.984 -1.858  -14.800 -2.500
+48978.00    -14.04  -2.11   -14.700 -2.600
+48979.00    -13.887 -2.222  -14.500 -2.600
+48980.00    -13.621 -2.154  -14.200 -2.500
+48981.00    -13.39  -1.948  -13.800 -2.400
+48982.00    -13.226 -1.735  -13.500 -2.200
+48983.00    -13.07  -1.652  -13.200 -2.100
+48984.00    -12.926 -1.731  -13.100 -1.900
+48985.00    -12.865 -1.879  -13.100 -1.800
+48986.00    -12.859 -1.962  -13.000 -1.800
+48987.00    -12.761 -1.923  -13.100 -1.800
+48988.00    -12.718 -1.862  -13.300 -1.900
+48989.00    -12.881 -1.893  -13.400 -2.000
+48990.00    -13.238 -1.969  -13.400 -2.200
+48991.00    -13.66  -1.947  -13.500 -2.500
+48992.00    -13.951 -1.822  -13.400 -2.600
+48993.00    -14.002 -1.78   -13.400 -2.700
+48994.00    -13.838 -1.928  -13.400 -2.700
+48995.00    -13.503 -2.188  -13.400 -2.700
+48996.00    -13.108 -2.304  -13.400 -2.500
+48997.00    -12.856 -2.14   -13.500 -2.300
+48998.00    -12.921 -1.841  -13.700 -2.100
+48999.00    -13.266 -1.665  -14.000 -2.000
+49000.00    -13.68  -1.722  -14.200 -2.000
+49001.00    -13.987 -1.89   -14.400 -2.100
+49002.00    -14.172 -2.023  -14.600 -2.200
+49003.00    -14.29  -2.05   -14.500 -2.400
+49004.00    -14.398 -2.028  -14.300 -2.600
+49005.00    -14.485 -2.037  -14.000 -2.700
+49006.00    -14.469 -2.128  -13.700 -2.800
+49007.00    -14.282 -2.283  -13.600 -2.800
+49008.00    -13.979 -2.328  -13.500 -2.700
+49009.00    -13.722 -2.199  -13.500 -2.500
+49010.00    -13.625 -1.974  -13.600 -2.300
+49011.00    -13.642 -1.772  -13.700 -2.200
+49012.00    -13.705 -1.7    -13.900 -2.000
+49013.00    -13.793 -1.778  -14.200 -2.000
+49014.00    -13.835 -1.98   -14.500 -2.100
+49015.00    -13.777 -2.246  -14.600 -2.200
+49016.00    -13.629 -2.35   -14.800 -2.400
+49017.00    -13.471 -2.481  -15.000 -2.700
+49018.00    -13.492 -2.715  -14.900 -2.900
+49019.00    -13.725 -2.84   -14.900 -3.200
+49020.00    -14.044 -2.728  0.000   0.000
+49021.00    -14.312 -2.545  -14.700 -3.500
+49022.00    -14.286 -2.595  -14.400 -3.600
+49023.00    -13.832 -2.859  -14.100 -3.500
+49024.00    -13.309 -3.09   -13.800 -3.400
+49025.00    -12.956 -2.999  -13.600 -3.100
+49026.00    -12.944 -2.688  -13.600 -3.000
+49027.00    -13.242 -2.45   -13.500 -2.900
+49028.00    -13.628 -2.43   -13.700 -3.000
+49029.00    -13.898 -2.563  -14.000 -3.100
+49030.00    -14.001 -2.714  -14.200 -3.300
+49031.00    -14.073 -2.963  -14.500 -3.500
+49032.00    -14.148 -3.302  -14.600 -3.800
+49033.00    -14.258 -3.616  -14.600 -3.900
+49034.00    -14.457 -3.825  -14.600 -4.100
+49035.00    -14.627 -3.899  -14.400 -4.100
+49036.00    -14.554 -3.815  -14.400 -4.000
+49037.00    -14.28  -3.669  -14.300 -3.900
+49038.00    -14.013 -3.503  -14.200 -3.700
+49039.00    -13.856 -3.342  -14.100 -3.700
+49040.00    -13.793 -3.277  -14.100 -3.700
+49041.00    -13.752 -3.382  -14.200 -3.700
+49042.00    -13.63  -3.583  -14.000 -3.900
+49043.00    -13.369 -3.741  -13.900 -4.000
+49044.00    -13.021 -3.814  -13.600 -4.300
+49045.00    -12.853 -3.892  -13.600 -4.500
+49046.00    -13.068 -4.038  -13.500 -4.600
+49047.00    -13.574 -4.168  -13.500 -4.800
+49048.00    -14.056 -4.186  -13.500 -4.900
+49049.00    -14.283 -4.184  -13.600 -4.900
+49050.00    -14.29  -4.308  -13.300 -5.100
+49051.00    -14.149 -4.555  -13.500 -5.000
+49052.00    -13.856 -4.781  -13.700 -5.000
+49053.00    -13.524 -4.761  -14.000 -4.800
+49054.00    -13.331 -4.526  -13.900 -4.800
+49055.00    -13.303 -4.325  -14.000 -4.800
+49056.00    -13.36  -4.288  -14.100 -4.800
+49057.00    -13.491 -4.377  -14.200 -4.900
+49058.00    -13.631 -4.483  -14.200 -5.000
+49059.00    -13.616 -4.644  -14.100 -5.100
+49060.00    -13.364 -4.923  -13.900 -5.400
+49061.00    -13.045 -5.234  -13.500 -5.600
+49062.00    -12.943 -5.445  -13.300 -5.600
+49063.00    -13.092 -5.523  -13.000 -5.700
+49064.00    -13.231 -5.469  -12.900 -5.700
+49065.00    -13.129 -5.414  -12.800 -5.700
+49066.00    -12.81  -5.324  -12.700 -5.500
+49067.00    -12.526 -5.138  -12.700 -5.500
+49068.00    -12.435 -4.961  -12.600 -5.500
+49069.00    -12.483 -4.949  -12.700 -5.600
+49070.00    -12.558 -5.167  -13.000 -5.700
+49071.00    -12.61  -5.614  -13.000 -5.900
+49072.00    -12.565 -5.919  -13.100 -6.100
+49073.00    -12.393 -6.054  -13.100 -6.400
+49074.00    -12.261 -6.159  -12.900 -6.600
+49075.00    -12.291 -6.262  -12.700 -6.800
+49076.00    -12.384 -6.312  -12.300 -6.800
+49077.00    -12.374 -6.328  -12.000 -6.900
+49078.00    -12.193 -6.406  -11.700 -6.800
+49079.00    -11.913 -6.562  -11.600 -6.600
+49080.00    -11.669 -6.63   -11.000 -6.500
+49081.00    -11.588 -6.45   -11.400 -6.400
+49082.00    -11.705 -6.115  -12.000 -6.300
+49083.00    -11.894 -5.905  -12.300 -6.300
+49084.00    -12.000 -5.965  -12.600 -6.300
+49085.00    -12.022 -6.176  -12.700 -6.500
+49086.00    -12.033 -6.353  -12.700 -6.700
+49087.00    -11.973 -6.465  -12.700 -6.800
+49088.00    -11.743 -6.582  -12.500 -6.900
+49089.00    -11.481 -6.689  -12.300 -7.100
+49090.00    -11.477 -6.677  -12.000 -7.200
+49091.00    -11.738 -6.562  -11.900 -7.200
+49092.00    -11.886 -6.561  -11.800 -7.200
+49093.00    -11.677 -6.775  -11.800 -7.200
+49094.00    -11.179 -6.919  -11.700 -7.100
+49095.00    -10.809 -6.854  -11.600 -7.100
+49096.00    -10.838 -6.73   -11.600 -7.100
+49097.00    -11.19  -6.775  -11.700 -7.200
+49098.00    -11.576 -7.003  -11.700 -7.400
+49099.00    -11.622 -7.146  -11.700 -7.600
+49100.00    -11.47  -7.299  -11.500 -7.700
+49101.00    -11.39  -7.341  -11.300 -8.000
+49102.00    -11.502 -7.297  -11.000 -8.200
+49103.00    -11.837 -7.303  -10.800 -8.200
+49104.00    -12.288 -7.42   -10.500 -8.300
+49105.00    -12.52  -7.61   -10.400 -8.200
+49106.00    -12.123 -7.69   -10.500 -8.200
+49107.00    -11.325 -7.647  -10.700 -8.000
+49108.00    -10.625 -7.446  -11.100 -7.900
+49109.00    -10.323 -7.069  -11.600 -7.700
+49110.00    -10.46  -6.657  -12.200 -7.700
+49111.00    -10.874 -6.478  -12.800 -7.600
+49112.00    -11.347 -6.692  -13.300 -7.600
+49113.00    -11.792 -7.115  -13.500 -7.700
+49114.00    -12.174 -7.366  -13.500 -7.800
+49115.00    -12.284 -7.404  -13.200 -7.800
+49116.00    -12.05  -7.37   -12.800 -7.900
+49117.00    -11.693 -7.354  -12.300 -7.800
+49118.00    -11.533 -7.374  -11.800 -7.800
+49119.00    -11.656 -7.402  -11.400 -7.700
+49120.00    -11.782 -7.402  -11.100 -7.500
+49121.00    -11.599 -7.368  -10.900 -7.500
+49122.00    -11.239 -7.276  -11.000 -7.400
+49123.00    -11.088 -7.121  -11.300 -7.400
+49124.00    -11.308 -7.01   -11.700 -7.500
+49125.00    -11.718 -7.106  -12.300 -7.600
+49126.00    -12.046 -7.431  -12.900 -7.800
+49127.00    -12.255 -7.694  -13.300 -7.900
+49128.00    -12.37  -7.751  -13.500 -8.100
+49129.00    -12.385 -7.664  -13.500 -8.300
+49130.00    -12.34  -7.541  -13.300 -8.300
+49131.00    -12.299 -7.459  -13.000 -8.300
+49132.00    -12.297 -7.467  -12.700 -8.200
+49133.00    -12.264 -7.597  -12.500 -8.000
+49134.00    -12.208 -7.764  -12.300 -7.900
+49135.00    -12.087 -7.86   -12.300 -7.600
+49136.00    -11.91  -7.834  -12.500 -7.400
+49137.00    -11.812 -7.665  -12.900 -7.300
+49138.00    -11.935 -7.392  -13.300 -7.200
+49139.00    -12.301 -7.129  -13.600 -7.300
+49140.00    -12.803 -6.996  -13.800 -7.300
+49141.00    -13.288 -6.985  -13.900 -7.300
+49142.00    -13.599 -6.988  -14.000 -7.500
+49143.00    -13.654 -6.902  -13.800 -7.600
+49144.00    -13.514 -6.721  -13.600 -7.500
+49145.00    -13.344 -6.582  -13.500 -7.500
+49146.00    -13.296 -6.641  -13.300 -7.500
+49147.00    -13.369 -6.942  -13.300 -7.400
+49148.00    -13.533 -7.344  -13.500 -7.300
+49149.00    -13.633 -7.653  -13.800 -7.300
+49150.00    -13.643 -7.716  -14.300 -7.300
+49151.00    -13.73  -7.56   -14.700 -7.500
+49152.00    -14.024 -7.349  -15.200 -7.600
+49153.00    -14.497 -7.256  -15.700 -7.800
+49154.00    -15.026 -7.312  -16.000 -8.000
+49155.00    -15.473 -7.313  -16.200 -8.200
+49156.00    -15.752 -7.207  -16.200 -8.300
+49157.00    -15.816 -7.109  -16.200 -8.400
+49158.00    -15.676 -7.109  -16.000 -8.400
+49159.00    -15.454 -7.179  -15.900 -8.400
+49160.00    -15.366 -7.253  -15.800 -8.200
+49161.00    -15.564 -7.291  -15.900 -7.900
+49162.00    -15.91  -7.107  -16.000 -7.700
+49163.00    -16.328 -6.821  -16.400 -7.400
+49164.00    -16.764 -6.642  -16.800 -7.200
+49165.00    -17.057 -6.586  -17.200 -7.100
+49166.00    -17.229 -6.567  -17.700 -7.000
+49167.00    -17.427 -6.535  -18.100 -7.100
+49168.00    -17.718 -6.527  -18.300 -7.100
+49169.00    -18.047 -6.655  -18.400 -7.200
+49170.00    -18.203 -6.887  -18.400 -7.300
+49171.00    -18.031 -7.056  -18.200 -7.300
+49172.00    -17.62  -7.038  -17.900 -7.300
+49173.00    -17.22  -6.855  -17.800 -7.300
+49174.00    -17.022 -6.637  -17.700 -7.300
+49175.00    -17.051 -6.473  -17.700 -7.200
+49176.00    -17.247 -6.35   -17.900 -7.100
+49177.00    -17.535 -6.4    -18.400 -6.900
+49178.00    -17.884 -6.558  -18.800 -6.900
+49179.00    -18.225 -6.7    -19.300 -6.900
+49180.00    -18.552 -6.842  -19.800 -6.900
+49181.00    -18.97  -7.001  -20.100 -7.000
+49182.00    -19.529 -7.099  -20.300 -7.100
+49183.00    -20.135 -7.114  -20.400 -7.300
+49184.00    -20.478 -7.131  -20.300 -7.400
+49185.00    -20.439 -7.089  -20.200 -7.500
+49186.00    -20.201 -7.122  -20.100 -7.500
+49187.00    -19.96  -7.267  -20.100 -7.500
+49188.00    -19.857 -7.238  -20.100 -7.400
+49189.00    -20.031 -6.9    -20.300 -7.300
+49190.00    -20.523 -6.462  -20.700 -7.100
+49191.00    -21.227 -6.173  -21.100 -7.000
+49192.00    -21.856 -6.12   -21.500 -7.000
+49193.00    -22.149 -6.24   -22.000 -6.900
+49194.00    -22.081 -6.417  -22.400 -7.000
+49195.00    -21.87  -6.574  -22.500 -7.000
+49196.00    -21.791 -6.7    -22.600 -7.200
+49197.00    -21.953 -6.824  -22.500 -7.200
+49198.00    -22.231 -7.024  -22.400 -7.300
+49199.00    -22.339 -7.165  -22.100 -7.300
+49200.00    -22.196 -7.121  -22.000 -7.300
+49201.00    -21.996 -6.932  -21.900 -7.200
+49202.00    -21.952 -6.722  -22.000 -7.000
+49203.00    -22.146 -6.65   -22.300 -6.900
+49204.00    -22.525 -6.75   -22.700 -6.900
+49205.00    -22.986 -6.888  -22.600 -6.700
+49206.00    -23.322 -6.985  -23.000 -6.800
+49207.00    -23.344 -7.016  -23.500 -6.900
+49208.00    -23.125 -7.016  -23.800 -7.200
+49209.00    -23.045 -7.069  -24.100 -7.400
+49210.00    -23.423 -7.159  -24.200 -7.700
+49211.00    -24.027 -7.143  -24.200 -8.000
+49212.00    -24.426 -7.005  -24.100 -8.100
+49213.00    -24.397 -6.89   -23.900 -8.200
+49214.00    -24.072 -6.957  -23.800 -8.200
+49215.00    -23.742 -7.175  -23.700 -8.100
+49216.00    -23.598 -7.312  -23.700 -8.100
+49217.00    -23.687 -7.198  -23.900 -7.900
+49218.00    -23.953 -6.829  -24.200 -7.700
+49219.00    -24.412 -6.574  -24.600 -7.600
+49220.00    -24.882 -6.507  -25.000 -7.500
+49221.00    -25.156 -6.484  -25.300 -7.400
+49222.00    -25.153 -6.494  -25.500 -7.400
+49223.00    -24.816 -6.634  -25.500 -7.300
+49224.00    -24.203 -6.953  -25.400 -7.400
+49225.00    -23.69  -7.229  -25.100 -7.400
+49226.00    -23.59  -7.299  -24.700 -7.300
+49227.00    -23.844 -7.191  -24.400 -7.200
+49228.00    -24.192 -7.003  -24.100 -7.100
+49229.00    -24.464 -6.777  -24.000 -6.900
+49230.00    -24.611 -6.508  -24.000 -6.700
+49231.00    -24.61  -6.203  -24.200 -6.600
+49232.00    -24.508 -6.013  -24.500 -6.600
+49233.00    -24.446 -6.06   -24.800 -6.700
+49234.00    -24.52  -6.269  -25.200 -6.800
+49235.00    -24.583 -6.497  -25.600 -6.800
+49236.00    -24.514 -6.687  -25.600 -7.200
+49237.00    -24.51  -6.907  -25.600 -7.500
+49238.00    -24.824 -7.199  -25.400 -7.700
+49239.00    -25.359 -7.424  -25.100 -7.900
+49240.00    -25.685 -7.44   -24.800 -7.900
+49241.00    -25.513 -7.296  -24.600 -8.000
+49242.00    -25.009 -7.185  -24.300 -7.900
+49243.00    -24.59  -7.19   -24.300 -7.700
+49244.00    -24.535 -7.185  -24.400 -7.600
+49245.00    -24.83  -7.05   -24.800 -7.500
+49246.00    -25.253 -6.86   -25.200 -7.300
+49247.00    -25.517 -6.685  -25.700 -7.100
+49248.00    -25.554 -6.637  -26.200 -7.000
+49249.00    -25.586 -6.634  -26.500 -7.100
+49250.00    -25.778 -6.554  -26.700 -7.000
+49251.00    -25.92  -6.489  -26.700 -7.000
+49252.00    -25.653 -6.49   -26.500 -7.100
+49253.00    -24.982 -6.457  -26.200 -7.000
+49254.00    -24.493 -6.516  -25.800 -7.000
+49255.00    -24.45  -6.519  -25.400 -6.900
+49256.00    -24.583 -6.379  -25.000 -6.800
+49257.00    -24.665 -6.258  -24.800 -6.700
+49258.00    -24.652 -6.169  -24.800 -6.500
+49259.00    -24.637 -6.102  -24.900 -6.500
+49260.00    -24.633 -6.15   -25.100 -6.500
+49261.00    -24.496 -6.232  -25.300 -6.600
+49262.00    -24.49  -6.368  -25.400 -6.700
+49263.00    -24.506 -6.599  -25.400 -6.900
+49264.00    -24.422 -6.768  -25.300 -7.100
+49265.00    -24.311 -6.86   -25.100 -7.300
+49266.00    -24.354 -6.912  -24.600 -7.400
+49267.00    -24.537 -6.871  -24.200 -7.400
+49268.00    -24.531 -6.708  -23.900 -7.400
+49269.00    -24.249 -6.498  -23.500 -7.300
+49270.00    -23.848 -6.358  -23.400 -7.200
+49271.00    -23.561 -6.329  -23.400 -6.900
+49272.00    -23.573 -6.317  -23.600 -6.700
+49273.00    -23.811 -6.234  -23.900 -6.600
+49274.00    -24.012 -6.229  -24.200 -6.400
+49275.00    -23.934 -6.287  -24.600 -6.300
+49276.00    -23.489 -6.331  -24.800 -6.300
+49277.00    -23.037 -6.208  -24.900 -6.300
+49278.00    -23.032 -5.863  -24.800 -6.300
+49279.00    -23.383 -5.557  -24.600 -6.300
+49280.00    -23.567 -5.575  -24.200 -6.300
+49281.00    -23.227 -5.774  -23.600 -6.300
+49282.00    -22.639 -5.927  -23.000 -6.300
+49283.00    -22.252 -5.911  -22.500 -6.300
+49284.00    -22.117 -5.793  -22.100 -6.200
+49285.00    -21.998 -5.717  -21.900 -6.200
+49286.00    -21.814 -5.684  -21.900 -6.100
+49287.00    -21.706 -5.613  -22.000 -6.100
+49288.00    -21.801 -5.524  -22.300 -6.000
+49289.00    -22.01  -5.508  -22.500 -5.900
+49290.00    -22.205 -5.578  -22.700 -6.000
+49291.00    -22.328 -5.637  -22.700 -6.000
+49292.00    -22.31  -5.61   -22.600 -6.000
+49293.00    -22.086 -5.541  -22.400 -6.100
+49294.00    -21.692 -5.534  -22.000 -6.000
+49295.00    -21.259 -5.651  -21.800 -6.200
+49296.00    -20.892 -5.748  -21.500 -6.000
+49297.00    -20.567 -5.708  -21.200 -5.800
+49298.00    -20.264 -5.554  -21.100 -5.600
+49299.00    -20.108 -5.336  -21.200 -5.400
+49300.00    -20.288 -5.072  -21.400 -5.300
+49301.00    -20.836 -4.786  -21.600 -5.300
+49302.00    -21.361 -4.585  -21.800 -5.200
+49303.00    -21.49  -4.602  -22.000 -5.100
+49304.00    -21.142 -4.799  -22.000 -4.900
+49305.00    -20.615 -4.935  -21.800 -4.900
+49306.00    -20.322 -4.825  -21.500 -4.900
+49307.00    -20.309 -4.579  -21.000 -4.900
+49308.00    -20.213 -4.482  -20.400 -4.800
+49309.00    -19.782 -4.655  -19.900 -4.800
+49310.00    -19.221 -4.918  -19.400 -4.800
+49311.00    -18.897 -5.055  -19.000 -4.800
+49312.00    -18.869 -5.032  -18.800 -4.700
+49313.00    -18.924 -4.93   -18.800 -4.800
+49314.00    -18.947 -4.794  -19.000 -4.800
+49315.00    -19.013 -4.619  -19.200 -4.900
+49316.00    -19.151 -4.431  -19.500 -4.900
+49317.00    -19.261 -4.283  -19.800 -4.900
+49318.00    -19.29  -4.178  -19.800 -4.900
+49319.00    -19.308 -4.071  -19.800 -4.800
+49320.00    -19.336 -3.958  -19.500 -4.700
+49321.00    -19.259 -3.909  -19.200 -4.600
+49322.00    -18.993 -3.986  -18.800 -4.500
+49323.00    -18.689 -4.162  -18.400 -4.400
+49324.00    -18.501 -4.304  -18.300 -4.300
+49325.00    -18.472 -4.324  -18.100 -4.000
+49326.00    -18.535 -4.221  -18.200 -4.000
+49327.00    -18.592 -4.02   -18.400 -4.000
+49328.00    -18.624 -3.748  -18.700 -3.900
+49329.00    -18.67  -3.454  -19.000 -4.000
+49330.00    -18.735 -3.262  -19.300 -4.000
+49331.00    -18.736 -3.232  -19.400 -4.200
+49332.00    -18.59  -3.287  -19.300 -4.300
+49333.00    -18.334 -3.351  -19.100 -4.200
+49334.00    -18.112 -3.402  -18.700 -4.200
+49335.00    -17.959 -3.356  -18.100 -4.100
+49336.00    -17.747 -3.247  -17.600 -3.900
+49337.00    -17.388 -3.138  -17.000 -3.700
+49338.00    -17.021 -3.137  -16.800 -3.600
+49339.00    -16.866 -3.278  -16.700 -3.600
+49340.00    -16.964 -3.465  -16.800 -3.500
+49341.00    -17.184 -3.572  -17.100 -3.500
+49342.00    -17.423 -3.527  -17.500 -3.500
+49343.00    -17.655 -3.359  -17.900 -3.600
+49344.00    -17.813 -3.224  -18.300 -3.700
+49345.00    -17.835 -3.182  -18.600 -3.800
+49346.00    -17.752 -3.188  -18.700 -3.900
+49347.00    -17.692 -3.178  -18.700 -4.000
+49348.00    -17.717 -3.171  -18.400 -4.000
+49349.00    -17.717 -3.245  -18.100 -3.900
+49350.00    -17.537 -3.412  -17.900 -3.800
+49351.00    -17.187 -3.556  -17.600 -3.700
+49352.00    -16.805 -3.58   -17.300 -3.600
+49353.00    -16.588 -3.467  -17.200 -3.400
+49354.00    -16.618 -3.28   -17.300 -3.300
+49355.00    -16.799 -3.093  -17.500 -3.100
+49356.00    -17.011 -2.927  -17.800 -3.100
+49357.00    -17.249 -2.762  -18.100 -3.100
+49358.00    -17.614 -2.611  -18.500 -3.100
+49359.00    -18.139 -2.669  -18.700 -3.200
+49360.00    -18.653 -2.888  -18.900 -3.200
+49361.00    -18.969 -3.116  -18.900 -3.200
+49362.00    -19.013 -3.245  -18.800 -3.200
+49363.00    -18.808 -3.165  -18.600 -3.200
+49364.00    -18.485 -2.917  -18.400 -3.200
+49365.00    -18.199 -2.67   -18.300 -3.100
+49366.00    -18.008 -2.589  -18.300 -3.000
+49367.00    -17.898 -2.705  -18.400 -3.000
+49368.00    -17.9   -2.905  -18.500 -3.000
+49369.00    -18.03  -3.045  -18.700 -3.000
+49370.00    -18.224 -3.039  -18.900 -3.100
+49371.00    -18.379 -2.996  -19.100 -3.300
+49372.00    -18.417 -3.058  -19.300 -3.500
+49373.00    -18.335 -3.289  -19.400 -3.700
+49374.00    -18.256 -3.491  -19.300 -3.800
+49375.00    -18.261 -3.495  -19.200 -4.100
+49376.00    -18.317 -3.434  -18.800 -4.100
+49377.00    -18.314 -3.467  -18.600 -4.200
+49378.00    -18.145 -3.654  -18.300 -4.100
+49379.00    -17.786 -3.898  -18.100 -4.000
+49380.00    -17.406 -3.954  -18.100 -3.900
+49381.00    -17.259 -3.737  -18.200 -3.700
+49382.00    -17.486 -3.416  -18.500 -3.600
+49383.00    -18.006 -3.203  -18.800 -3.600
+49384.00    -18.573 -3.17   -19.200 -3.500
+49385.00    -19.000 -3.232  -19.800 -3.400
+49386.00    -19.285 -3.282  -20.000 -3.600
+49387.00    -19.509 -3.312  -20.100 -3.900
+49388.00    -19.753 -3.357  -19.900 -4.100
+49389.00    -19.927 -3.485  -19.800 -4.200
+49390.00    -19.846 -3.683  -19.400 -4.300
+49391.00    -19.415 -3.875  -19.100 -4.400
+49392.00    -18.747 -3.988  -18.800 -4.400
+49393.00    -18.069 -3.912  -18.500 -4.400
+49394.00    -17.606 -3.787  -18.400 -4.300
+49395.00    -17.486 -3.812  -18.500 -4.400
+49396.00    -17.668 -3.999  -18.700 -4.500
+49397.00    -18.062 -4.224  -19.100 -4.600
+49398.00    -18.546 -4.383  -19.400 -4.800
+49399.00    -18.9   -4.473  -19.700 -5.000
+49400.00    -18.964 -4.576  -19.900 -5.300
+49401.00    -18.792 -4.715  -20.100 -5.500
+49402.00    -18.583 -4.813  -20.200 -5.700
+49403.00    -18.411 -4.749  -20.000 -5.900
+49404.00    -18.227 -4.549  -19.900 -5.900
+49405.00    -18.057 -4.46   -19.700 -5.900
+49406.00    -17.986 -4.706  -19.700 -5.900
+49407.00    -17.926 -5.112  -19.500 -5.700
+49408.00    -17.79  -5.34   -19.400 -5.600
+49409.00    -17.651 -5.222  -19.300 -5.500
+49410.00    -17.683 -4.921  -19.400 -5.300
+49411.00    -17.934 -4.737  -19.500 -5.300
+49412.00    -18.226 -4.796  -19.600 -5.400
+49413.00    -18.342 -4.976  -19.500 -5.500
+49414.00    -18.225 -5.096  -19.600 -5.600
+49415.00    -18.003 -5.108  -19.400 -5.800
+49416.00    -17.834 -5.075  -19.300 -6.000
+49417.00    -17.782 -5.066  -19.100 -6.000
+49418.00    -17.814 -5.126  -18.900 -6.100
+49419.00    -17.885 -5.293  -18.700 -6.100
+49420.00    -18.033 -5.567  -18.600 -6.100
+49421.00    -18.286 -5.728  -18.400 -6.100
+49422.00    -18.576 -5.73   -18.200 -6.000
+49423.00    -18.702 -5.692  -18.100 -6.000
+49424.00    -18.58  -5.719  -18.100 -6.200
+49425.00    -18.319 -5.848  -18.100 -6.300
+49426.00    -18.031 -6.038  -18.100 -6.500
+49427.00    -17.744 -6.239  -18.000 -6.700
+49428.00    -17.558 -6.43   -17.900 -7.000
+49429.00    -17.573 -6.625  -17.700 -7.100
+49430.00    -17.734 -6.821  -17.600 -7.300
+49431.00    -17.862 -6.888  -17.200 -7.300
+49432.00    -17.766 -6.727  -16.900 -7.300
+49433.00    -17.46  -6.496  -16.800 -7.300
+49434.00    -17.222 -6.469  -16.600 -7.200
+49435.00    -17.252 -6.681  -16.600 -7.000
+49436.00    -17.354 -6.848  -16.800 -6.900
+49437.00    -17.361 -6.747  -17.100 -6.800
+49438.00    -17.347 -6.479  -17.500 -6.900
+49439.00    -17.417 -6.336  -17.800 -6.900
+49440.00    -17.544 -6.472  -18.100 -7.100
+49441.00    -17.627 -6.763  -18.200 -7.400
+49442.00    -17.603 -7.03   -18.300 -7.600
+49443.00    -17.464 -7.234  -18.200 -7.800
+49444.00    -17.254 -7.413  -17.900 -7.900
+49445.00    -17.09  -7.541  -17.700 -8.000
+49446.00    -17.076 -7.561  -17.500 -8.100
+49447.00    -17.153 -7.499  -17.500 -8.000
+49448.00    -17.148 -7.436  -17.500 -7.900
+49449.00    -17.013 -7.383  -17.500 -7.900
+49450.00    -16.894 -7.28   -17.400 -7.800
+49451.00    -16.937 -7.124  -17.400 -7.800
+49452.00    -17.124 -7.045  -17.500 -7.800
+49453.00    -17.334 -7.171  -17.500 -7.900
+49454.00    -17.448 -7.473  -17.400 -8.000
+49455.00    -17.371 -7.792  -17.200 -8.200
+49456.00    -17.103 -8.018  -17.000 -8.400
+49457.00    -16.833 -8.179  -16.800 -8.500
+49458.00    -16.769 -8.318  -16.500 -8.400
+49459.00    -16.844 -8.365  -16.300 -8.400
+49460.00    -16.757 -8.215  -16.000 -8.300
+49461.00    -16.367 -7.933  -15.900 -8.200
+49462.00    -15.908 -7.744  -16.000 -8.100
+49463.00    -15.709 -7.768  -16.100 -7.900
+49464.00    -15.837 -7.849  -16.400 -7.700
+49465.00    -16.13  -7.766  -16.600 -7.700
+49466.00    -16.435 -7.537  -17.000 -7.700
+49467.00    -16.68  -7.404  -17.300 -7.800
+49468.00    -16.834 -7.499  -17.500 -7.900
+49469.00    -16.894 -7.699  -17.600 -8.200
+49470.00    -16.887 -7.817  -17.500 -8.300
+49471.00    -16.831 -7.94   -17.400 -8.500
+49472.00    -16.631 -8.134  -17.200 -8.600
+49473.00    -16.392 -8.311  -16.900 -8.600
+49474.00    -16.413 -8.351  -16.900 -8.600
+49475.00    -16.728 -8.242  -17.000 -8.700
+49476.00    -16.996 -8.111  -17.200 -8.600
+49477.00    -16.932 -8.031  -17.400 -8.500
+49478.00    -16.682 -7.929  -17.600 -8.400
+49479.00    -16.611 -7.751  -17.900 -8.400
+49480.00    -16.842 -7.619  -18.000 -8.400
+49481.00    -17.177 -7.719  -18.200 -8.600
+49482.00    -17.383 -8.067  -18.200 -8.800
+49483.00    -17.392 -8.476  -18.100 -8.900
+49484.00    -17.311 -8.763  -17.800 -9.100
+49485.00    -17.209 -8.917  -17.400 -9.200
+49486.00    -17.242 -8.959  -17.100 -9.300
+49487.00    -17.469 -8.904  -16.700 -9.300
+49488.00    -17.675 -8.75   -16.600 -9.200
+49489.00    -17.613 -8.515  -16.600 -9.100
+49490.00    -17.309 -8.325  -16.800 -9.000
+49491.00    -17.047 -8.289  -17.000 -8.800
+49492.00    -17.028 -8.248  -17.300 -8.600
+49493.00    -17.269 -8.051  -17.800 -8.500
+49494.00    -17.634 -7.775  -18.100 -8.500
+49495.00    -17.93  -7.622  -18.300 -8.400
+49496.00    -18.058 -7.683  -18.500 -8.400
+49497.00    -18.066 -7.809  -18.500 -8.500
+49498.00    -17.983 -7.803  -18.400 -8.600
+49499.00    -17.762 -7.691  -18.100 -8.600
+49500.00    -17.346 -7.651  -17.900 -8.700
+49501.00    -16.919 -7.749  -17.800 -8.600
+49502.00    -16.872 -7.874  -17.700 -8.600
+49503.00    -17.343 -7.936  -17.900 -8.500
+49504.00    -17.975 -7.981  -18.200 -8.400
+49505.00    -18.333 -8.073  -18.700 -8.500
+49506.00    -18.405 -8.13   -19.200 -8.500
+49507.00    -18.54  -8.049  -19.600 -8.500
+49508.00    -18.938 -7.915  -20.000 -8.600
+49509.00    -19.405 -7.902  -20.300 -8.600
+49510.00    -19.659 -8.062  -20.400 -8.800
+49511.00    -19.626 -8.274  -20.300 -8.900
+49512.00    -19.394 -8.404  -20.200 -9.000
+49513.00    -19.114 -8.469  -20.000 -9.100
+49514.00    -18.937 -8.546  -19.800 -9.100
+49515.00    -18.989 -8.653  -19.600 -9.100
+49516.00    -19.243 -8.717  -19.600 -9.000
+49517.00    -19.475 -8.624  -19.800 -8.800
+49518.00    -19.454 -8.279  -20.100 -8.700
+49519.00    -19.19  -7.649  -20.500 -8.400
+49520.00    -19.016 -7.061  -21.000 -8.300
+49521.00    -19.208 -6.696  -21.400 -8.100
+49522.00    -19.769 -6.573  -21.800 -8.000
+49523.00    -20.493 -6.755  -22.000 -8.000
+49524.00    -21.146 -7.19   -22.100 -8.000
+49525.00    -21.641 -7.629  -22.100 -8.000
+49526.00    -22.029 -7.815  -21.900 -8.100
+49527.00    -22.261 -7.712  -21.800 -8.100
+49528.00    -22.21  -7.516  -21.600 -8.100
+49529.00    -21.905 -7.442  -21.700 -8.000
+49530.00    -21.637 -7.526  -21.800 -8.000
+49531.00    -21.735 -7.65   -22.100 -8.100
+49532.00    -22.113 -7.727  -22.600 -8.000
+49533.00    -22.429 -7.778  -23.100 -8.100
+49534.00    -22.586 -7.896  -23.700 -8.200
+49535.00    -22.711 -7.977  -24.300 -7.700
+49536.00    -22.984 -7.891  -24.300 -7.800
+49537.00    -23.345 -7.78   -24.200 -7.800
+49538.00    -23.627 -7.733  -23.900 -7.900
+49539.00    -23.785 -7.754  -23.700 -7.900
+49540.00    -23.846 -7.837  -23.400 -7.900
+49541.00    -23.815 -7.887  -23.100 -7.800
+49542.00    -23.717 -7.84   -22.900 -7.700
+49543.00    -23.683 -7.763  -22.900 -7.600
+49544.00    -23.895 -7.649  -23.100 -7.600
+49545.00    -24.337 -7.524  -23.400 -7.400
+49546.00    -24.771 -7.427  -23.900 -7.400
+49547.00    -24.962 -7.384  -24.500 -7.400
+49548.00    -24.879 -7.363  -25.000 -7.400
+49549.00    -24.79  -7.261  -25.500 -7.400
+49550.00    -24.951 -7.108  -25.900 -7.500
+49551.00    -25.392 -7.07   -26.100 -7.700
+49552.00    -25.979 -7.267  -26.000 -7.800
+49553.00    -26.503 -7.666  -25.900 -7.900
+49554.00    -26.72  -8.016  -25.700 -7.900
+49555.00    -26.485 -8.144  -25.500 -8.000
+49556.00    -26.031 -8.003  -25.300 -8.000
+49557.00    -25.675 -7.741  -25.300 -7.900
+49558.00    -25.608 -7.572  -25.400 -7.800
+49559.00    -25.894 -7.555  -25.700 -7.700
+49560.00    -26.385 -7.649  -26.200 -7.700
+49561.00    -26.828 -7.806  -26.700 -7.600
+49562.00    -27.103 -7.879  -27.200 -7.600
+49563.00    -27.271 -7.739  -27.600 -7.600
+49564.00    -27.44  -7.519  -27.800 -7.700
+49565.00    -27.676 -7.396  -28.000 -7.700
+49566.00    -27.985 -7.481  -27.800 -7.700
+49567.00    -28.297 -7.815  -27.700 -7.700
+49568.00    -28.465 -8.208  -27.500 -7.700
+49569.00    -28.347 -8.552  -27.100 -7.700
+49570.00    -28.089 -8.744  -27.600 -7.400
+49571.00    -27.916 -8.671  -27.700 -7.400
+49572.00    -27.955 -8.318  -27.800 -7.400
+49573.00    -28.228 -7.808  -28.000 -7.200
+49574.00    -28.56  -7.362  -28.200 -7.300
+49575.00    -28.757 -7.088  -28.500 -7.400
+49576.00    -28.588 -6.841  -28.800 -7.500
+49577.00    -28.281 -6.72   -29.100 -7.600
+49578.00    -28.145 -6.823  -29.300 -7.700
+49579.00    -28.281 -7.082  -29.300 -7.900
+49580.00    -28.667 -7.455  -29.200 -7.900
+49581.00    -29.099 -7.862  -29.100 -7.900
+49582.00    -29.33  -8.157  -28.800 -7.900
+49583.00    -29.25  -8.214  -28.500 -7.800
+49584.00    -28.961 -8.018  -28.400 -7.700
+49585.00    -28.685 -7.697  -28.400 -7.600
+49586.00    -28.6   -7.439  -28.600 -7.400
+49587.00    -28.752 -7.346  -28.900 -7.300
+49588.00    -29.122 -7.395  -29.400 -7.300
+49589.00    -29.669 -7.508  -29.800 -7.300
+49590.00    -30.183 -7.619  -30.200 -7.400
+49591.00    -30.349 -7.719  -30.500 -7.500
+49592.00    -30.126 -7.837  -30.600 -7.700
+49593.00    -29.812 -7.971  -30.500 -7.800
+49594.00    -29.713 -8.053  -30.200 -7.900
+49595.00    -29.858 -8.001  -30.000 -7.900
+49596.00    -29.913 -7.856  -29.600 -7.900
+49597.00    -29.7   -7.829  -29.200 -7.800
+49598.00    -29.379 -7.949  -29.100 -7.700
+49599.00    -29.182 -8.049  -29.000 -7.500
+49600.00    -29.213 -7.953  -29.400 -7.400
+49601.00    -29.478 -7.625  -29.700 -7.300
+49602.00    -29.918 -7.24   -30.100 -7.200
+49603.00    -30.414 -7.01   -30.400 -7.200
+49604.00    -30.813 -7.039  -30.700 -7.200
+49605.00    -30.956 -7.18   -30.800 -7.400
+49606.00    -30.831 -7.279  -30.900 -7.400
+49607.00    -30.566 -7.349  -30.600 -7.500
+49608.00    -30.308 -7.449  -30.400 -7.600
+49609.00    -30.158 -7.589  -30.100 -7.500
+49610.00    -30.105 -7.71   -29.800 -7.500
+49611.00    -30.049 -7.73   -29.600 -7.300
+49612.00    -29.92  -7.609  -29.400 -7.200
+49613.00    -29.808 -7.388  -29.400 -7.100
+49614.00    -29.836 -7.172  -29.500 -6.900
+49615.00    -30.017 -7.061  -29.700 -7.000
+49616.00    -30.328 -7.099  -30.100 -7.000
+49617.00    -30.69  -7.186  -30.300 -7.100
+49618.00    -30.923 -7.29   -30.600 -7.200
+49619.00    -30.778 -7.429  -30.600 -7.300
+49620.00    -30.261 -7.585  -30.500 -7.600
+49621.00    -29.706 -7.744  -30.200 -7.700
+49622.00    -29.502 -7.86   -29.900 -7.700
+49623.00    -29.675 -7.841  -29.500 -7.700
+49624.00    -29.819 -7.68   -29.100 -7.600
+49625.00    -29.697 -7.504  -28.800 -7.500
+49626.00    -29.482 -7.481  -28.700 -7.300
+49627.00    -29.431 -7.604  -28.700 -7.100
+49628.00    -29.544 -7.67   -28.800 -7.000
+49629.00    -29.597 -7.515  -29.100 -6.900
+49630.00    -29.43  -7.233  -29.300 -6.700
+49631.00    -29.179 -7.115  -29.600 -6.900
+49632.00    -29.178 -7.176  -29.800 -6.900
+49633.00    -29.386 -7.226  -29.800 -7.100
+49634.00    -29.605 -7.178  -29.600 -7.200
+49635.00    -29.663 -7.125  -29.300 -7.200
+49636.00    -29.374 -7.157  -29.000 -7.200
+49637.00    -28.789 -7.217  -28.500 -7.100
+49638.00    -28.195 -7.171  -28.100 -7.000
+49639.00    -27.833 -6.981  -27.800 -6.700
+49640.00    -27.732 -6.73   -27.500 -6.600
+49641.00    -27.799 -6.522  -27.400 -6.400
+49642.00    -27.949 -6.388  -27.400 -6.300
+49643.00    -28.097 -6.342  -27.600 -6.200
+49644.00    -28.159 -6.428  -27.600 -6.200
+49645.00    -28.151 -6.598  -27.800 -6.300
+49646.00    -28.099 -6.777  -27.900 -6.600
+49647.00    -27.906 -6.895  -27.900 -6.700
+49648.00    -27.529 -6.919  -27.700 -6.900
+49649.00    -27.136 -6.9    -27.400 -7.000
+49650.00    -26.957 -6.902  -27.100 -7.100
+49651.00    -26.972 -6.898  -26.800 -7.100
+49652.00    -26.961 -6.781  -26.600 -6.900
+49653.00    -26.804 -6.581  -26.400 -6.700
+49654.00    -26.673 -6.388  -26.500 -6.600
+49655.00    -26.803 -6.27   -26.600 -6.300
+49656.00    -27.115 -6.19   -26.900 -6.300
+49657.00    -27.224 -6.064  -27.100 -6.200
+49658.00    -26.818 -5.963  -27.300 -6.200
+49659.00    -26.109 -5.946  -27.500 -6.200
+49660.00    -25.467 -5.987  -28.000 -6.100
+49661.00    -25.234 -5.951  -27.700 -6.200
+49662.00    -25.52  -5.795  -27.200 -6.200
+49663.00    -26.008 -5.692  -26.700 -6.100
+49664.00    -26.181 -5.819  -26.100 -6.100
+49665.00    -25.847 -6.093  -25.600 -5.900
+49666.00    -25.301 -6.27   -25.300 -5.800
+49667.00    -24.982 -6.215  -25.100 -5.500
+49668.00    -24.995 -6.033  -24.900 -5.400
+49669.00    -25.125 -5.89   -25.000 -5.300
+49670.00    -25.22  -5.802  -25.200 -5.300
+49671.00    -25.299 -5.682  -25.300 -5.400
+49672.00    -25.366 -5.524  -25.400 -5.500
+49673.00    -25.355 -5.41   -25.500 -5.500
+49674.00    -25.239 -5.379  -25.600 -5.700
+49675.00    -25.045 -5.368  -25.400 -5.800
+49676.00    -24.808 -5.328  -25.200 -5.900
+49677.00    -24.601 -5.319  -24.900 -5.800
+49678.00    -24.547 -5.435  -24.500 -5.800
+49679.00    -24.659 -5.642  -24.200 -5.600
+49680.00    -24.704 -5.716  -24.000 -5.400
+49681.00    -24.517 -5.571  -23.700 -5.200
+49682.00    -24.21  -5.329  -23.600 -5.000
+49683.00    -24.031 -5.127  -23.700 -4.800
+49684.00    -24.087 -4.977  -23.800 -4.800
+49685.00    -24.25  -4.831  -23.900 -4.700
+49686.00    -24.308 -4.744  -23.900 -4.700
+49687.00    -24.154 -4.797  -24.000 -4.900
+49688.00    -23.86  -4.966  -24.000 -5.000
+49689.00    -23.675 -5.06   -23.900 -5.100
+49690.00    -23.792 -4.944  -24.000 -5.300
+49691.00    -24.054 -4.767  -23.500 -5.300
+49692.00    -24.048 -4.791  -23.300 -5.200
+49693.00    -23.616 -5.034  -23.300 -5.000
+49694.00    -23.189 -5.183  -23.000 -4.800
+49695.00    -23.153 -5.046  -22.900 -4.700
+49696.00    -23.433 -4.75   -22.800 -4.400
+49697.00    -23.665 -4.531  -22.900 -4.400
+49698.00    -23.655 -4.47   -22.900 -4.300
+49699.00    -23.518 -4.502  -22.900 -4.400
+49700.00    -23.379 -4.584  -23.000 -4.400
+49701.00    -23.276 -4.656  -23.000 -4.500
+49702.00    -23.179 -4.71   -23.000 -4.800
+49703.00    -23.123 -4.723  -22.800 -4.900
+49704.00    -23.12  -4.675  -22.700 -4.900
+49705.00    -23.084 -4.632  -22.500 -5.000
+49706.00    -22.946 -4.67   -22.400 -4.900
+49707.00    -22.74  -4.765  -22.300 -4.700
+49708.00    -22.605 -4.798  -22.300 -4.400
+49709.00    -22.55  -4.68   -22.200 -4.100
+49710.00    -22.525 -4.434  -22.300 -3.800
+49711.00    -22.553 -4.146  -22.500 -3.500
+49712.00    -22.682 -3.874  -22.600 -3.300
+49713.00    -22.908 -3.651  -22.700 -3.200
+49714.00    -23.15  -3.573  -22.700 -3.100
+49715.00    -23.281 -3.761  -22.600 -3.200
+49716.00    -23.24  -4.218  -22.300 -3.300
+49717.00    -23.114 -4.629  -21.900 -3.300
+49718.00    -23.041 -4.69   -21.600 -3.400
+49719.00    -23.011 -4.436  -21.200 -3.400
+49720.00    -22.859 -4.143  -22.600 -3.900
+49721.00    -22.54  -4.023  -22.700 -3.800
+49722.00    -22.305 -4.043  -22.800 -3.900
+49723.00    -22.431 -4.051  -22.900 -3.900
+49724.00    -22.839 -4.004  -23.000 -3.900
+49725.00    -23.19  -3.985  -23.300 -4.000
+49726.00    -23.31  -4.048  -23.500 -4.100
+49727.00    -23.314 -4.168  -23.500 -4.300
+49728.00    -23.318 -4.303  -23.500 -4.600
+49729.00    -23.243 -4.386  -23.300 -4.800
+49730.00    -23.043 -4.399  -23.100 -5.000
+49731.00    -22.842 -4.343  -22.800 -5.100
+49732.00    -22.76  -4.265  -22.400 -5.200
+49733.00    -22.734 -4.27   -22.100 -5.200
+49734.00    -22.655 -4.429  -21.900 -5.000
+49735.00    -22.589 -4.717  -21.800 -4.800
+49736.00    -22.721 -5.033  -21.800 -4.600
+49737.00    -22.958 -5.129  -21.900 -4.300
+49738.00    -23.146 -4.947  -22.200 -4.100
+49739.00    -23.225 -4.613  -22.400 -4.000
+49740.00    -23.209 -4.237  -22.800 -3.900
+49741.00    -23.207 -3.924  -22.900 -3.900
+49742.00    -23.344 -3.81   -22.900 -3.900
+49743.00    -23.656 -3.985  -22.900 -4.100
+49744.00    -24.026 -4.436  -22.800 -4.200
+49745.00    -24.304 -4.978  -22.700 -4.200
+49746.00    -24.434 -5.29   -22.600 -4.300
+49747.00    -24.403 -5.194  -22.500 -4.200
+49748.00    -24.191 -4.772  -22.500 -4.200
+49749.00    -23.847 -4.282  -22.700 -4.100
+49750.00    -23.596 -4.132  -23.200 -4.200
+49751.00    -23.576 -4.355  -23.400 -4.200
+49752.00    -23.713 -4.654  -23.800 -4.300
+49753.00    -23.852 -4.843  -24.100 -4.300
+49754.00    -23.933 -4.939  -24.200 -4.500
+49755.00    -24.006 -5.004  -24.300 -4.700
+49756.00    -24.099 -5.09   -24.200 -5.000
+49757.00    -24.164 -5.204  -24.100 -5.200
+49758.00    -24.165 -5.293  -23.900 -5.500
+49759.00    -24.181 -5.306  -23.600 -5.600
+49760.00    -24.282 -5.286  -23.400 -5.800
+49761.00    -24.354 -5.329  -23.200 -5.800
+49762.00    -24.172 -5.453  -23.200 -5.700
+49763.00    -23.653 -5.564  -23.200 -5.700
+49764.00    -23.077 -5.637  -23.300 -5.600
+49765.00    -22.779 -5.567  -23.500 -5.400
+49766.00    -22.832 -5.402  -23.700 -5.400
+49767.00    -23.087 -5.282  -23.900 -5.300
+49768.00    -23.374 -5.248  -23.900 -5.300
+49769.00    -23.63  -5.279  -23.800 -5.400
+49770.00    -23.905 -5.384  -23.500 -5.400
+49771.00    -24.254 -5.613  -23.200 -5.500
+49772.00    -24.606 -5.941  -22.800 -5.600
+49773.00    -24.792 -6.236  -22.400 -5.600
+49774.00    -24.734 -6.357  -22.100 -5.600
+49775.00    -24.473 -6.212  -21.900 -5.600
+49776.00    -24.144 -5.883  -21.800 -5.500
+49777.00    -23.881 -5.624  -23.600 -5.400
+49778.00    -23.719 -5.663  -23.700 -5.400
+49779.00    -23.603 -5.974  -24.000 -5.500
+49780.00    -23.497 -6.334  -24.100 -5.600
+49781.00    -23.45  -6.566  -24.300 -5.800
+49782.00    -23.513 -6.647  -24.300 -6.100
+49783.00    -23.639 -6.679  -24.100 -6.300
+49784.00    -23.701 -6.77   -23.700 -6.600
+49785.00    -23.581 -6.931  -23.300 -6.900
+49786.00    -23.314 -7.06   -23.000 -7.000
+49787.00    -23.03  -7.06   -22.600 -7.200
+49788.00    -22.835 -6.971  -22.300 -7.200
+49789.00    -22.769 -6.939  -22.100 -7.300
+49790.00    -22.794 -7.025  -22.100 -7.200
+49791.00    -22.814 -7.093  -22.200 -7.200
+49792.00    -22.722 -7.002  -22.300 -7.100
+49793.00    -22.567 -6.745  -22.500 -7.100
+49794.00    -22.488 -6.507  -22.700 -7.000
+49795.00    -22.534 -6.498  -22.800 -7.100
+49796.00    -22.617 -6.743  -22.800 -7.100
+49797.00    -22.639 -7.087  -22.700 -7.300
+49798.00    -22.633 -7.372  -22.300 -7.300
+49799.00    -22.722 -7.56   -22.100 -7.400
+49800.00    -22.899 -7.7    -21.800 -7.400
+49801.00    -23.001 -7.829  -21.700 -7.500
+49802.00    -22.894 -7.922  -21.600 -7.400
+49803.00    -22.605 -7.91   -21.600 -7.300
+49804.00    -22.317 -7.772  -21.800 -7.300
+49805.00    -22.216 -7.598  -22.100 -7.300
+49806.00    -22.259 -7.539  -22.500 -7.300
+49807.00    -22.27  -7.66   -22.700 -7.400
+49808.00    -22.159 -7.887  -23.000 -7.500
+49809.00    -22.027 -8.095  -23.000 -7.700
+49810.00    -22.004 -8.223  -22.900 -8.000
+49811.00    -22.061 -8.307  -22.600 -8.200
+49812.00    -22.084 -8.415  -22.200 -8.400
+49813.00    -22.077 -8.566  -21.800 -8.600
+49814.00    -22.107 -8.676  -21.400 -8.700
+49815.00    -22.142 -8.634  -21.000 -8.700
+49816.00    -22.082 -8.449  -20.800 -8.600
+49817.00    -21.956 -8.302  -20.600 -8.600
+49818.00    -21.911 -8.361  -20.700 -8.500
+49819.00    -21.966 -8.55   -21.000 -8.400
+49820.00    -21.985 -8.591  -21.400 -8.400
+49821.00    -21.738 -8.385  -21.700 -8.400
+49822.00    -21.434 -8.108  -22.100 -8.400
+49823.00    -21.37  -8.04   -22.400 -8.500
+49824.00    -21.526 -8.275  -22.400 -8.500
+49825.00    -21.732 -8.624  -22.400 -8.700
+49826.00    -21.871 -8.839  -22.100 -8.800
+49827.00    -21.966 -8.843  -21.800 -8.800
+49828.00    -22.131 -8.791  -21.400 -8.800
+49829.00    -22.304 -8.765  -21.100 -8.800
+49830.00    -22.35  -8.753  -20.900 -8.700
+49831.00    -22.22  -8.741  -20.900 -8.600
+49832.00    -21.969 -8.709  -21.100 -8.600
+49833.00    -21.778 -8.637  -21.300 -8.500
+49834.00    -21.811 -8.532  -21.700 -8.600
+49835.00    -22.006 -8.444  -22.100 -8.600
+49836.00    -22.183 -8.443  -22.300 -8.700
+49837.00    -22.26  -8.56   -22.500 -8.900
+49838.00    -22.26  -8.763  -22.500 -9.100
+49839.00    -22.173 -8.993  -22.300 -9.200
+49840.00    -21.978 -9.223  -21.800 -9.200
+49841.00    -21.787 -9.398  -21.400 -9.300
+49842.00    -21.736 -9.496  -21.000 -9.300
+49843.00    -21.732 -9.46   -20.600 -9.200
+49844.00    -21.524 -9.247  -20.500 -9.100
+49845.00    -21.105 -8.972  -20.400 -9.000
+49846.00    -20.845 -8.852  -20.600 -8.900
+49847.00    -21.061 -8.949  -21.000 -8.900
+49848.00    -21.632 -9.068  -21.400 -8.800
+49849.00    -22.134 -8.967  -22.000 -8.700
+49850.00    -22.377 -8.702  -22.300 -8.700
+49851.00    -22.49  -8.566  -22.600 -8.900
+49852.00    -22.604 -8.71   -22.700 -8.900
+49853.00    -22.693 -8.98   -22.700 -8.900
+49854.00    -22.662 -9.132  -22.500 -8.900
+49855.00    -22.48  -9.105  -22.100 -8.900
+49856.00    -22.234 -9.000  -21.700 -8.800
+49857.00    -22.089 -8.885  -21.500 -8.800
+49858.00    -22.162 -8.757  -21.300 -8.700
+49859.00    -22.379 -8.644  -21.300 -8.600
+49860.00    -22.542 -8.621  -21.500 -8.500
+49861.00    -22.61  -8.696  -21.800 -8.600
+49862.00    -22.787 -8.781  -22.300 -8.600
+49863.00    -23.171 -8.783  -22.700 -8.700
+49864.00    -23.641 -8.767  -23.100 -8.700
+49865.00    -23.974 -8.865  -23.300 -8.800
+49866.00    -24.035 -9.068  -23.400 -9.000
+49867.00    -23.855 -9.227  -23.300 -9.100
+49868.00    -23.581 -9.231  -23.100 -9.100
+49869.00    -23.435 -9.142  -22.800 -9.100
+49870.00    -23.587 -9.132  -22.600 -9.100
+49871.00    -23.919 -9.219  -22.400 -9.100
+49872.00    -24.056 -9.259  -22.400 -8.900
+49873.00    -23.793 -9.158  -22.700 -8.800
+49874.00    -23.391 -8.974  -22.900 -8.800
+49875.00    -23.28  -8.779  -23.400 -8.800
+49876.00    -23.56  -8.498  -23.900 -8.700
+49877.00    -23.983 -8.092  -24.300 -8.700
+49878.00    -24.31  -7.727  -24.700 -8.700
+49879.00    -24.504 -7.645  -24.900 -8.800
+49880.00    -24.67  -7.906  -24.900 -8.800
+49881.00    -24.913 -8.312  -24.900 -8.800
+49882.00    -25.237 -8.609  -24.700 -8.700
+49883.00    -25.532 -8.757  -24.500 -8.600
+49884.00    -25.586 -8.817  -24.300 -8.500
+49885.00    -25.507 -8.823  -24.300 -8.300
+49886.00    -25.63  -8.728  -24.300 -8.200
+49887.00    -26.013 -8.539  -24.600 -8.100
+49888.00    -26.32  -8.406  -25.100 -8.000
+49889.00    -26.224 -8.444  -25.500 -8.000
+49890.00    -25.768 -8.55   -26.100 -8.000
+49891.00    -25.413 -8.583  -26.600 -8.200
+49892.00    -25.425 -8.56   -26.900 -8.300
+49893.00    -25.639 -8.617  -27.100 -8.400
+49894.00    -25.85  -8.795  -27.200 -8.500
+49895.00    -26.137 -8.961  -27.100 -8.600
+49896.00    -26.7   -8.958  -26.900 -8.600
+49897.00    -27.309 -8.713  -26.700 -8.400
+49898.00    -27.865 -8.474  -26.500 -8.300
+49899.00    -28.378 -8.417  -26.400 -8.200
+49900.00    -28.702 -8.45   -26.400 -8.000
+49901.00    -28.656 -8.429  -26.600 -7.900
+49902.00    -28.335 -8.308  -27.000 -7.900
+49903.00    -28.129 -8.148  -27.500 -7.900
+49904.00    -28.393 -8.005  -27.900 -8.000
+49905.00    -29.156 -7.91   -28.500 -8.000
+49906.00    -30.083 -7.923  -28.900 -8.100
+49907.00    -30.805 -8.127  -29.300 -8.300
+49908.00    -31.152 -8.474  -29.500 -8.400
+49909.00    -31.126 -8.724  -29.600 -8.400
+49910.00    -30.798 -8.676  -29.800 -8.400
+49911.00    -30.419 -8.516  -29.700 -8.400
+49912.00    -30.024 -8.405  -29.500 -8.300
+49913.00    -29.694 -8.404  -29.500 -8.200
+49914.00    -29.729 -8.44   -29.700 -8.200
+49915.00    -30.26  -8.406  -30.000 -8.200
+49916.00    -30.944 -8.365  -30.300 -8.300
+49917.00    -31.292 -8.442  -30.700 -8.400
+49918.00    -31.247 -8.575  -31.400 -8.500
+49919.00    -31.216 -8.638  -31.900 -8.600
+49920.00    -31.487 -8.598  -32.400 -8.800
+49921.00    -31.891 -8.54   -32.700 -8.900
+49922.00    -32.115 -8.534  -32.800 -8.900
+49923.00    -32.1   -8.554  -32.800 -8.900
+49924.00    -31.971 -8.512  -32.800 -8.900
+49925.00    -31.774 -8.272  -32.500 -8.700
+49926.00    -31.577 -7.928  -32.200 -8.500
+49927.00    -31.515 -7.639  -31.900 -8.400
+49928.00    -31.664 -7.469  -31.900 -8.300
+49929.00    -31.932 -7.431  -32.000 -8.200
+49930.00    -32.183 -7.568  -32.000 -8.200
+49931.00    -32.433 -7.912  -32.300 -8.200
+49932.00    -32.662 -8.138  -32.700 -8.400
+49933.00    -32.963 -8.181  -33.200 -8.500
+49934.00    -33.362 -8.205  -33.500 -8.600
+49935.00    -33.726 -8.336  -33.800 -8.800
+49936.00    -33.948 -8.565  -34.000 -8.800
+49937.00    -34.077 -8.742  -34.000 -8.800
+49938.00    -34.252 -8.709  -34.200 -8.700
+49939.00    -34.508 -8.427  -34.100 -8.600
+49940.00    -34.609 -8.133  -34.000 -8.500
+49941.00    -34.488 -8.056  -34.000 -8.300
+49942.00    -34.401 -8.171  -34.200 -8.200
+49943.00    -34.593 -8.298  -34.400 -8.200
+49944.00    -34.989 -8.335  -34.700 -8.200
+49945.00    -35.29  -8.333  -34.900 -8.300
+49946.00    -35.361 -8.37   -35.200 -8.400
+49947.00    -35.305 -8.443  -35.400 -8.500
+49948.00    -35.293 -8.503  -35.500 -8.700
+49949.00    -35.336 -8.53   -35.400 -8.700
+49950.00    -35.346 -8.527  -35.200 -8.700
+49951.00    -35.347 -8.499  -35.000 -8.700
+49952.00    -35.427 -8.466  -34.900 -8.500
+49953.00    -35.563 -8.46   -34.700 -8.400
+49954.00    -35.521 -8.475  -34.700 -8.200
+49955.00    -35.362 -8.394  -34.800 -8.100
+49956.00    -35.373 -8.223  -35.100 -8.000
+49957.00    -35.688 -8.026  -35.300 -8.000
+49958.00    -36.091 -7.956  -35.700 -8.100
+49959.00    -36.37  -8.076  -36.000 -8.200
+49960.00    -36.548 -8.135  -36.200 -8.400
+49961.00    -36.726 -8.08   -36.500 -8.600
+49962.00    -36.847 -8.078  -36.600 -8.800
+49963.00    -36.861 -8.229  -36.500 -9.000
+49964.00    -36.779 -8.488  -36.400 -9.100
+49965.00    -36.615 -8.714  -36.300 -9.100
+49966.00    -36.445 -8.75   -36.200 -9.100
+49967.00    -36.352 -8.556  -36.000 -8.800
+49968.00    -36.269 -8.263  -35.900 -8.700
+49969.00    -36.151 -8.079  -36.000 -8.500
+49970.00    -36.077 -8.092  -36.000 -8.300
+49971.00    -36.148 -8.197  -36.100 -8.200
+49972.00    -36.373 -8.241  -36.200 -8.100
+49973.00    -36.631 -8.195  -36.200 -8.200
+49974.00    -36.762 -8.15   -36.200 -8.300
+49975.00    -36.676 -8.19   -36.100 -8.400
+49976.00    -36.397 -8.304  -35.900 -8.600
+49977.00    -36.035 -8.419  -35.600 -8.600
+49978.00    -35.742 -8.462  -35.300 -8.600
+49979.00    -35.624 -8.408  -35.000 -8.500
+49980.00    -35.667 -8.328  -34.800 -8.400
+49981.00    -35.733 -8.43   -34.700 -8.300
+49982.00    -35.721 -8.631  -34.700 -8.000
+49983.00    -35.649 -8.651  -34.900 -7.800
+49984.00    -35.618 -8.403  -35.100 -7.700
+49985.00    -35.683 -8.016  -35.500 -7.600
+49986.00    -35.797 -7.726  -35.700 -7.600
+49987.00    -35.857 -7.672  -35.900 -7.600
+49988.00    -35.792 -7.761  -36.000 -7.700
+49989.00    -35.636 -7.849  -36.100 -7.900
+49990.00    -35.486 -7.879  -35.900 -8.000
+49991.00    -35.414 -7.885  -35.700 -8.000
+49992.00    -35.417 -7.91   -35.500 -8.100
+49993.00    -35.442 -7.94   -35.300 -8.000
+49994.00    -35.442 -7.919  -35.200 -7.900
+49995.00    -35.406 -7.857  -35.000 -7.700
+49996.00    -35.359 -7.743  -35.000 -7.600
+49997.00    -35.325 -7.601  -35.000 -7.400
+49998.00    -35.303 -7.521  -35.100 -7.400
+49999.00    -35.274 -7.504  -35.200 -7.400
+50000.00    -35.261 -7.468  -35.300 -7.400
+50001.00    -35.331 -7.377  -35.200 -7.600
+50002.00    -35.412 -7.317  -35.100 -7.700
+50003.00    -35.282 -7.447  -34.900 -7.800
+50004.00    -34.828 -7.641  -34.700 -7.900
+50005.00    -34.21  -7.787  -34.300 -7.900
+50006.00    -33.717 -7.838  -34.000 -7.900
+50007.00    -33.484 -7.776  -33.700 -7.800
+50008.00    -33.416 -7.655  -33.600 -7.600
+50009.00    -33.396 -7.58   -33.400 -7.400
+50010.00    -33.437 -7.586  -33.400 -7.200
+50011.00    -33.575 -7.568  -33.400 -6.900
+50012.00    -33.726 -7.383  -33.500 -6.800
+50013.00    -33.747 -7.048  -33.500 -6.600
+50014.00    -33.596 -6.774  -33.400 -6.700
+50015.00    -33.367 -6.75   -33.200 -6.700
+50016.00    -33.227 -6.921  -33.100 -6.700
+50017.00    -33.224 -7.077  -33.000 -6.900
+50018.00    -33.307 -7.08   -32.800 -6.900
+50019.00    -33.384 -6.962  -32.500 -6.900
+50020.00    -33.363 -6.831  -32.300 -6.800
+50021.00    -33.213 -6.74   -32.100 -6.700
+50022.00    -32.986 -6.667  -32.200 -6.600
+50023.00    -32.769 -6.592  -32.100 -6.400
+50024.00    -32.641 -6.528  -32.100 -6.200
+50025.00    -32.633 -6.5    -32.100 -6.100
+50026.00    -32.694 -6.508  -32.200 -6.000
+50027.00    -32.713 -6.507  -32.200 -6.100
+50028.00    -32.643 -6.442  -32.000 -6.100
+50029.00    -32.577 -6.293  -31.700 -6.100
+50030.00    -32.616 -6.082  -31.500 -6.400
+50031.00    -32.588 -5.914  -31.200 -6.500
+50032.00    -32.224 -5.906  -30.700 -6.600
+50033.00    -31.607 -6.055  -30.400 -6.700
+50034.00    -31.082 -6.26   -30.100 -6.600
+50035.00    -30.819 -6.394  -29.900 -6.600
+50036.00    -30.711 -6.366  -29.900 -6.400
+50037.00    -30.607 -6.214  -30.000 -6.200
+50038.00    -30.601 -6.085  -30.100 -5.900
+50039.00    -30.807 -6.032  -30.300 -5.800
+50040.00    -31.079 -5.93   -30.500 -5.600
+50041.00    -31.117 -5.695  -30.700 -5.500
+50042.00    -30.82  -5.463  -30.600 -5.600
+50043.00    -30.385 -5.474  -30.500 -5.600
+50044.00    -30.102 -5.804  -30.400 -5.600
+50045.00    -30.101 -6.219  -30.200 -5.800
+50046.00    -30.323 -6.466  -29.900 -5.900
+50047.00    -30.582 -6.517  -29.800 -5.900
+50048.00    -30.681 -6.465  -29.600 -5.900
+50049.00    -30.591 -6.31   -29.600 -5.800
+50050.00    -30.483 -5.984  -29.800 -5.700
+50051.00    -30.525 -5.636  -30.000 -5.600
+50052.00    -30.702 -5.408  -30.200 -5.400
+50053.00    -30.883 -5.347  -30.400 -5.200
+50054.00    -30.973 -5.378  -30.600 -5.100
+50055.00    -30.935 -5.38   -30.600 -5.200
+50056.00    -30.736 -5.315  -30.600 -5.200
+50057.00    -30.376 -5.244  -30.300 -5.200
+50058.00    -29.921 -5.22   -30.100 -5.400
+50059.00    -29.527 -5.23   -29.600 -5.400
+50060.00    -29.226 -5.249  -29.300 -5.500
+50061.00    -29.022 -5.293  -29.000 -5.500
+50062.00    -28.989 -5.385  -28.600 -5.500
+50063.00    -29.081 -5.472  -28.600 -5.400
+50064.00    -29.082 -5.462  -28.600 -5.300
+50065.00    -28.888 -5.347  -28.800 -5.100
+50066.00    -28.737 -5.185  -28.900 -5.000
+50067.00    -28.889 -5.061  -29.100 -4.800
+50068.00    -29.254 -4.953  -29.200 -4.800
+50069.00    -29.504 -4.784  -29.200 -4.800
+50070.00    -29.474 -4.651  -29.100 -4.800
+50071.00    -29.3   -4.766  -28.900 -4.800
+50072.00    -29.2   -5.091  -28.700 -4.900
+50073.00    -29.275 -5.38   -28.400 -5.000
+50074.00    -29.48  -5.421  -28.300 -5.100
+50075.00    -29.656 -5.252  -28.200 -5.100
+50076.00    -29.628 -5.083  -28.200 -5.100
+50077.00    -29.412 -4.994  -28.400 -4.900
+50078.00    -29.256 -4.89   -28.700 -4.900
+50079.00    -29.357 -4.719  -29.000 -4.800
+50080.00    -29.591 -4.596  -29.100 -4.700
+50081.00    -29.68  -4.643  -29.400 -4.600
+50082.00    -29.557 -4.797  -29.500 -4.500
+50083.00    -29.394 -4.888  -29.400 -4.600
+50084.00    -29.306 -4.849  -29.300 -4.600
+50085.00    -29.226 -4.768  -28.800 -4.700
+50086.00    -29.079 -4.739  -28.400 -4.800
+50087.00    -28.892 -4.756  -28.000 -5.000
+50088.00    -28.71  -4.783  -27.600 -5.200
+50089.00    -28.546 -4.849  -27.400 -5.300
+50090.00    -28.421 -5.007  -27.400 -5.200
+50091.00    -28.349 -5.208  -27.600 -5.300
+50092.00    -28.276 -5.31   -27.800 -5.300
+50093.00    -28.168 -5.232  -28.100 -5.200
+50094.00    -28.132 -5.062  -28.600 -5.100
+50095.00    -28.313 -4.945  -28.900 -5.000
+50096.00    -28.673 -4.904  -29.100 -5.000
+50097.00    -29.01  -4.864  -29.300 -4.900
+50098.00    -29.212 -4.833  -29.200 -4.900
+50099.00    -29.356 -4.937  -29.100 -5.000
+50100.00    -29.552 -5.217  -28.800 -5.000
+50101.00    -29.802 -5.482  -28.700 -5.000
+50102.00    -30.012 -5.505  -28.500 -5.000
+50103.00    -30.053 -5.306  -28.300 -5.000
+50104.00    -29.834 -5.112  -28.400 -5.000
+50105.00    -29.454 -5.049  -28.600 -4.900
+50106.00    -29.222 -5.015  -29.000 -4.900
+50107.00    -29.365 -4.949  -29.300 -4.900
+50108.00    -29.717 -4.913  -29.700 -5.000
+50109.00    -29.911 -5.02   -29.900 -5.100
+50110.00    -29.841 -5.262  -30.000 -5.200
+50111.00    -29.729 -5.495  -29.900 -5.400
+50112.00    -29.714 -5.618  -29.600 -5.600
+50113.00    -29.645 -5.663  -29.200 -5.700
+50114.00    -29.423 -5.681  -28.700 -5.800
+50115.00    -29.133 -5.67   -28.500 -5.800
+50116.00    -28.932 -5.62   -28.200 -5.800
+50117.00    -28.852 -5.589  -28.100 -5.800
+50118.00    -28.845 -5.645  -28.000 -5.800
+50119.00    -28.935 -5.767  -28.200 -5.800
+50120.00    -29.209 -5.857  -28.300 -5.800
+50121.00    -29.711 -5.902  -28.200 -5.900
+50122.00    -30.118 -5.896  -28.200 -6.000
+50123.00    -30.281 -5.882  -28.200 -6.100
+50124.00    -30.265 -5.893  -28.300 -6.200
+50125.00    -30.117 -5.894  -28.600 -6.100
+50126.00    -29.92  -5.875  -29.100 -6.000
+50127.00    -29.779 -5.885  -29.600 -5.900
+50128.00    -29.609 -5.96   -29.800 -5.700
+50129.00    -29.461 -6.013  -29.800 -5.600
+50130.00    -29.437 -5.924  -29.700 -5.600
+50131.00    -29.438 -5.71   -29.300 -5.600
+50132.00    -29.353 -5.582  -29.000 -5.700
+50133.00    -29.218 -5.715  -28.800 -5.900
+50134.00    -29.233 -6.025  -28.800 -6.000
+50135.00    -29.54  -6.278  -29.100 -6.100
+50136.00    -30.009 -6.389  -29.500 -6.300
+50137.00    -30.356 -6.464  -30.000 -6.400
+50138.00    -30.495 -6.594  -30.300 -6.500
+50139.00    -30.591 -6.764  -30.500 -6.600
+50140.00    -30.729 -6.92   -30.500 -6.900
+50141.00    -30.727 -7.034  -30.100 -7.000
+50142.00    -30.42  -7.097  -29.700 -7.100
+50143.00    -29.944 -7.096  -29.200 -7.100
+50144.00    -29.568 -7.053  -28.700 -7.200
+50145.00    -29.366 -7.034  -28.400 -7.200
+50146.00    -29.21  -7.087  -28.200 -7.100
+50147.00    -29.035 -7.179  -28.200 -7.100
+50148.00    -28.924 -7.249  -28.400 -7.200
+50149.00    -28.931 -7.285  -28.700 -7.200
+50150.00    -29.022 -7.314  -29.100 -7.300
+50151.00    -29.113 -7.363  -29.400 -7.300
+50152.00    -29.17  -7.426  -29.500 -7.400
+50153.00    -29.216 -7.48   -29.500 -7.500
+50154.00    -29.273 -7.534  -29.400 -7.600
+50155.00    -29.332 -7.631  -29.100 -7.600
+50156.00    -29.352 -7.78   -28.700 -7.800
+50157.00    -29.321 -7.891  -28.400 -7.800
+50158.00    -29.256 -7.841  -28.100 -7.900
+50159.00    -29.18  -7.622  -28.000 -7.900
+50160.00    -29.102 -7.415  -28.100 -7.900
+50161.00    -29.037 -7.436  -28.200 -7.900
+50162.00    -29.044 -7.72   -28.400 -8.000
+50163.00    -29.179 -8.078  -28.500 -7.900
+50164.00    -29.403 -8.294  -28.700 -7.900
+50165.00    -29.629 -8.32   -28.800 -8.000
+50166.00    -29.829 -8.268  -28.800 -8.000
+50167.00    -30.011 -8.254  -28.700 -8.100
+50168.00    -30.084 -8.305  -28.600 -8.200
+50169.00    -29.876 -8.364  -28.300 -8.300
+50170.00    -29.464 -8.392  -27.900 -8.500
+50171.00    -28.989 -8.361  -27.700 -8.500
+50172.00    -28.666 -8.305  -27.400 -8.500
+50173.00    -28.606 -8.301  -27.400 -8.500
+50174.00    -28.693 -8.367  -27.500 -8.400
+50175.00    -28.784 -8.423  -28.000 -8.300
+50176.00    -28.867 -8.396  -28.400 -8.300
+50177.00    -28.994 -8.338  -28.700 -8.400
+50178.00    -29.092 -8.369  -29.100 -8.400
+50179.00    -29.093 -8.541  -29.400 -8.500
+50180.00    -29.012 -8.775  -29.500 -8.700
+50181.00    -28.909 -8.941  -29.400 -8.800
+50182.00    -28.84  -8.991  -29.200 -8.800
+50183.00    -28.802 -8.973  -28.900 -8.900
+50184.00    -28.733 -8.959  -28.400 -8.900
+50185.00    -28.558 -8.964  -28.000 -8.900
+50186.00    -28.286 -8.932  -27.700 -8.900
+50187.00    -28.04  -8.816  -27.500 -8.800
+50188.00    -27.977 -8.672  -27.500 -8.800
+50189.00    -28.144 -8.65   -27.600 -8.800
+50190.00    -28.426 -8.844  -27.800 -8.900
+50191.00    -28.662 -9.175  -28.100 -9.000
+50192.00    -28.763 -9.444  -28.400 -9.100
+50193.00    -28.796 -9.531  -28.500 -9.300
+50194.00    -28.874 -9.49   -28.500 -9.300
+50195.00    -28.979 -9.454  -28.500 -9.400
+50196.00    -28.959 -9.48   -28.200 -9.400
+50197.00    -28.674 -9.499  -27.800 -9.400
+50198.00    -28.166 -9.45   -27.500 -9.300
+50199.00    -27.711 -9.384  -27.200 -9.200
+50200.00    -27.474 -9.296  -27.000 -9.100
+50201.00    -27.509 -9.247  -27.000 -9.100
+50202.00    -27.81  -9.282  -27.100 -9.000
+50203.00    -28.257 -9.318  -27.500 -9.100
+50204.00    -28.644 -9.243  -27.900 -9.200
+50205.00    -28.806 -9.089  -28.400 -9.300
+50206.00    -28.598 -8.975  -28.900 -9.500
+50207.00    -28.151 -9.038  -29.300 -9.600
+50208.00    -27.721 -9.255  -29.400 -9.700
+50209.00    -27.502 -9.442  -29.400 -9.800
+50210.00    -27.703 -9.49   -29.300 -9.700
+50211.00    -28.404 -9.46   -29.000 -9.700
+50212.00    -29.094 -9.383  -28.700 -9.500
+50213.00    -29.457 -9.335  -28.400 -9.300
+50214.00    -29.424 -9.336  -28.300 -9.200
+50215.00    -29.11  -9.319  -28.100 -9.100
+50216.00    -28.799 -9.232  -28.100 -9.000
+50217.00    -28.748 -9.102  -28.300 -8.900
+50218.00    -28.967 -9.008  -28.500 -9.000
+50219.00    -29.226 -8.988  -28.600 -9.000
+50220.00    -29.296 -9.005  -28.800 -9.000
+50221.00    -29.186 -9.005  -28.800 -9.200
+50222.00    -29.065 -8.998  -28.700 -9.300
+50223.00    -29.007 -9.042  -28.500 -9.300
+50224.00    -28.956 -9.186  -28.300 -9.300
+50225.00    -28.895 -9.42   -28.000 -9.300
+50226.00    -28.9   -9.618  -27.900 -9.200
+50227.00    -28.934 -9.69   -27.900 -9.200
+50228.00    -28.871 -9.625  -28.000 -9.000
+50229.00    -28.756 -9.503  -28.300 -9.000
+50230.00    -28.864 -9.426  -28.600 -9.000
+50231.00    -29.358 -9.384  -29.000 -9.000
+50232.00    -30.023 -9.256  -29.400 -9.100
+50233.00    -30.487 -9.008  -29.700 -9.100
+50234.00    -30.61  -8.791  -30.000 -9.200
+50235.00    -30.561 -8.81   -30.100 -9.200
+50236.00    -30.532 -9.069  -30.200 -9.200
+50237.00    -30.547 -9.326  -30.100 -9.100
+50238.00    -30.581 -9.359  -29.900 -9.100
+50239.00    -30.692 -9.192  -30.000 -9.000
+50240.00    -30.93  -8.975  -29.900 -8.900
+50241.00    -31.233 -8.864  -30.000 -8.800
+50242.00    -31.444 -8.892  -30.200 -8.700
+50243.00    -31.444 -8.991  -30.500 -8.700
+50244.00    -31.287 -9.077  -30.900 -8.800
+50245.00    -31.193 -9.087  -31.200 -8.800
+50246.00    -31.343 -8.964  -31.500 -9.000
+50247.00    -31.651 -8.626  -31.700 -9.200
+50248.00    -31.912 -8.354  -31.900 -9.300
+50249.00    -31.982 -8.356  -32.000 -9.400
+50250.00    -31.881 -8.552  -31.800 -9.500
+50251.00    -31.732 -8.801  -31.800 -9.400
+50252.00    -31.645 -8.996  -31.600 -9.400
+50253.00    -31.733 -9.125  -31.500 -9.200
+50254.00    -32.068 -9.204  -31.500 -9.000
+50255.00    -32.526 -9.185  -31.600 -8.900
+50256.00    -32.804 -9.013  -31.800 -8.900
+50257.00    -32.842 -8.803  -32.100 -8.800
+50258.00    -32.954 -8.676  -32.500 -8.800
+50259.00    -33.423 -8.732  -32.800 -8.900
+50260.00    -34.065 -8.894  -33.200 -8.900
+50261.00    -34.561 -8.855  -33.700 -8.900
+50262.00    -34.691 -8.649  -34.100 -8.900
+50263.00    -34.607 -8.556  -34.500 -8.900
+50264.00    -34.604 -8.675  -34.800 -8.700
+50265.00    -34.769 -8.814  -34.900 -8.600
+50266.00    -34.999 -8.744  -35.000 -8.500
+50267.00    -35.206 -8.482  -35.000 -8.400
+50268.00    -35.411 -8.248  -35.000 -8.400
+50269.00    -35.707 -8.181  -35.000 -8.400
+50270.00    -36.123 -8.244  -35.200 -8.300
+50271.00    -36.514 -8.36   -35.200 -8.500
+50272.00    -36.655 -8.526  -35.400 -8.600
+50273.00    -36.524 -8.726  -35.500 -8.800
+50274.00    -36.416 -8.858  -35.700 -8.800
+50275.00    -36.639 -8.827  -35.900 -9.000
+50276.00    -37.114 -8.713  -36.000 -9.000
+50277.00    -37.464 -8.688  -36.000 -8.900
+50278.00    -37.448 -8.781  -36.100 -8.800
+50279.00    -37.134 -8.855  -36.100 -8.700
+50280.00    -36.745 -8.79   -36.100 -8.600
+50281.00    -36.504 -8.636  -36.200 -8.600
+50282.00    -36.58  -8.534  -36.200 -8.400
+50283.00    -36.866 -8.572  -36.300 -8.400
+50284.00    -37.048 -8.664  -36.500 -8.400
+50285.00    -36.97  -8.684  -36.700 -8.500
+50286.00    -36.881 -8.648  -36.900 -8.500
+50287.00    -37.183 -8.681  -37.200 -8.700
+50288.00    -37.914 -8.81   -37.500 -8.900
+50289.00    -38.712 -8.841  -37.800 -9.000
+50290.00    -39.162 -8.761  -38.100 -9.200
+50291.00    -39.23  -8.752  -38.200 -9.300
+50292.00    -39.206 -8.875  -38.500 -9.300
+50293.00    -39.316 -8.994  -38.700 -9.200
+50294.00    -39.543 -8.94   -38.900 -9.000
+50295.00    -39.717 -8.754  -39.200 -8.800
+50296.00    -39.727 -8.625  -39.500 -8.700
+50297.00    -39.698 -8.628  -39.800 -8.400
+50298.00    -39.895 -8.631  -40.100 -8.400
+50299.00    -40.375 -8.529  -40.300 -8.300
+50300.00    -40.82  -8.427  -40.300 -8.300
+50301.00    -40.895 -8.496  -40.300 -8.400
+50302.00    -40.729 -8.727  -40.300 -8.400
+50303.00    -40.798 -8.954  -40.400 -8.600
+50304.00    -41.176 -9.03   -40.600 -8.800
+50305.00    -41.526 -9.004  -40.700 -8.800
+50306.00    -41.524 -8.971  -40.800 -8.900
+50307.00    -41.175 -8.898  -40.800 -8.800
+50308.00    -40.735 -8.723  -40.600 -8.800
+50309.00    -40.421 -8.51   -40.100 -8.700
+50310.00    -40.32  -8.397  -39.600 -8.500
+50311.00    -40.398 -8.468  -39.400 -8.400
+50312.00    -40.518 -8.647  -39.400 -8.400
+50313.00    -40.537 -8.782  -39.400 -8.400
+50314.00    -40.479 -8.808  -39.500 -8.400
+50315.00    -40.559 -8.782  -39.800 -8.500
+50316.00    -40.933 -8.773  -40.100 -8.700
+50317.00    -41.481 -8.794  -40.200 -8.800
+50318.00    -41.919 -8.842  -40.300 -9.000
+50319.00    -42.099 -8.937  -40.500 -9.100
+50320.00    -42.109 -9.062  -40.700 -9.200
+50321.00    -42.127 -9.102  -41.000 -9.100
+50322.00    -42.204 -8.971  -41.300 -9.100
+50323.00    -42.215 -8.755  -41.600 -8.900
+50324.00    -42.082 -8.621  -41.900 -8.700
+50325.00    -41.825 -8.648  -42.100 -8.500
+50326.00    -41.71  -8.67   -42.200 -8.300
+50327.00    -41.977 -8.489  -42.200 -8.100
+50328.00    -42.43  -8.184  -42.200 -8.000
+50329.00    -42.608 -8.023  -42.100 -8.100
+50330.00    -42.366 -8.154  -42.000 -8.100
+50331.00    -42.104 -8.47   -41.700 -8.300
+50332.00    -42.107 -8.807  -41.700 -8.500
+50333.00    -42.159 -8.926  -41.700 -8.600
+50334.00    -42.023 -8.902  -41.600 -8.800
+50335.00    -41.746 -8.853  -41.600 -8.700
+50336.00    -41.537 -8.756  -41.600 -8.700
+50337.00    -41.497 -8.62   -41.700 -8.600
+50338.00    -41.534 -8.506  -41.700 -8.400
+50339.00    -41.571 -8.43   -41.800 -8.300
+50340.00    -41.639 -8.378  -41.900 -8.200
+50341.00    -41.718 -8.343  -41.900 -8.200
+50342.00    -41.709 -8.328  -41.800 -8.200
+50343.00    -41.606 -8.354  -41.800 -8.200
+50344.00    -41.559 -8.432  -41.600 -8.400
+50345.00    -41.688 -8.488  -41.200 -8.500
+50346.00    -41.923 -8.491  -41.000 -8.500
+50347.00    -42.12  -8.456  -40.700 -8.600
+50348.00    -42.21  -8.452  -40.700 -8.500
+50349.00    -42.221 -8.439  -40.700 -8.500
+50350.00    -42.204 -8.344  -41.000 -8.400
+50351.00    -42.146 -8.206  -41.300 -8.200
+50352.00    -42.005 -8.075  -41.700 -8.100
+50353.00    -41.738 -8.036  -42.100 -7.900
+50354.00    -41.488 -8.011  -42.300 -7.700
+50355.00    -41.48  -7.819  -42.300 -7.600
+50356.00    -41.709 -7.466  -42.200 -7.400
+50357.00    -41.89  -7.213  -41.900 -7.400
+50358.00    -41.789 -7.323  -41.500 -7.500
+50359.00    -41.471 -7.677  -41.000 -7.600
+50360.00    -41.054 -8.029  -40.700 -7.800
+50361.00    -40.668 -8.307  -40.300 -8.100
+50362.00    -40.416 -8.47   -40.000 -8.300
+50363.00    -40.347 -8.503  -39.900 -8.400
+50364.00    -40.475 -8.431  -39.900 -8.300
+50365.00    -40.692 -8.256  -40.100 -8.200
+50366.00    -40.814 -7.997  -40.300 -8.000
+50367.00    -40.798 -7.678  -40.500 -7.800
+50368.00    -40.751 -7.368  -40.500 -7.500
+50369.00    -40.738 -7.164  -40.500 -7.300
+50370.00    -40.669 -7.141  -40.400 -7.200
+50371.00    -40.444 -7.256  -40.200 -7.100
+50372.00    -40.135 -7.379  -39.800 -7.100
+50373.00    -39.988 -7.476  -39.400 -7.200
+50374.00    -40.099 -7.516  -39.100 -7.300
+50375.00    -40.282 -7.501  -38.900 -7.400
+50376.00    -40.347 -7.413  -38.800 -7.300
+50377.00    -40.237 -7.237  -38.700 -7.300
+50378.00    -40.009 -7.016  -38.800 -7.200
+50379.00    -39.77  -6.84   -39.000 -6.900
+50380.00    -39.617 -6.724  -39.100 -6.700
+50381.00    -39.515 -6.738  -39.300 -6.500
+50382.00    -39.402 -6.863  -39.400 -6.400
+50383.00    -39.287 -6.935  -39.500 -6.300
+50384.00    -39.198 -6.828  -39.300 -6.300
+50385.00    -39.11  -6.614  -39.000 -6.400
+50386.00    -38.94  -6.509  -38.600 -6.500
+50387.00    -38.613 -6.647  -38.200 -6.600
+50388.00    -38.142 -6.945  -37.700 -6.700
+50389.00    -37.631 -7.213  -37.300 -6.800
+50390.00    -37.232 -7.315  -37.000 -7.000
+50391.00    -37.058 -7.226  -36.800 -7.100
+50392.00    -37.116 -7.019  -36.700 -7.000
+50393.00    -37.305 -6.899  -36.800 -7.000
+50394.00    -37.469 -6.823  -36.900 -7.000
+50395.00    -37.495 -6.638  -37.000 -6.900
+50396.00    -37.514 -6.265  -37.100 -6.800
+50397.00    -37.607 -5.87   -37.200 -6.700
+50398.00    -37.67  -5.722  -37.100 -6.700
+50399.00    -37.586 -5.928  -37.000 -6.700
+50400.00    -37.337 -6.343  -36.800 -6.600
+50401.00    -37.116 -6.538  -36.600 -6.600
+50402.00    -37.000 -6.398  -36.300 -6.500
+50403.00    -37.006 -6.1    -36.200 -6.400
+50404.00    -37.069 -5.847  -36.200 -6.100
+50405.00    -37.076 -5.711  -36.300 -6.000
+50406.00    -37.013 -5.659  -36.500 -5.800
+50407.00    -36.968 -5.653  -36.700 -5.600
+50408.00    -37.042 -5.707  -36.900 -5.400
+50409.00    -37.352 -5.838  -37.000 -5.300
+50410.00    -37.89  -6.207  -37.000 -5.200
+50411.00    -38.389 -6.721  -36.800 -5.300
+50412.00    -38.547 -7.006  -36.400 -5.300
+50413.00    -38.213 -6.785  -35.900 -5.300
+50414.00    -37.349 -6.024  -35.400 -5.400
+50415.00    -36.506 -5.425  -34.800 -5.500
+50416.00    -35.843 -5.25   -34.300 -5.700
+50417.00    -35.312 -5.33   -34.000 -5.800
+50418.00    -35.002 -5.499  -33.700 -5.900
+50419.00    -34.933 -5.663  -33.700 -6.000
+50420.00    -34.965 -5.79   -33.800 -6.000
+50421.00    -34.992 -5.849  -34.000 -6.000
+50422.00    -35.211 -5.693  -34.400 -6.000
+50423.00    -35.635 -5.341  -34.700 -5.900
+50424.00    -35.977 -4.941  -35.100 -5.800
+50425.00    -36.008 -4.632  -35.300 -5.700
+50426.00    -35.762 -4.574  -35.300 -5.700
+50427.00    -35.447 -4.849  -35.200 -5.600
+50428.00    -35.251 -5.289  -35.000 -5.500
+50429.00    -35.188 -5.548  -34.700 -5.500
+50430.00    -35.216 -5.447  -34.500 -5.400
+50431.00    -35.316 -5.104  -34.300 -5.300
+50432.00    -35.447 -4.768  -34.300 -5.100
+50433.00    -35.548 -4.59   -34.400 -5.000
+50434.00    -35.597 -4.559  -34.700 -4.900
+50435.00    -35.631 -4.607  -35.100 -4.800
+50436.00    -35.708 -4.704  -35.500 -4.700
+50437.00    -35.843 -4.837  -35.900 -4.700
+50438.00    -35.975 -4.973  -36.200 -4.600
+50439.00    -35.988 -5.054  -36.300 -4.700
+50440.00    -35.829 -5.042  -36.100 -4.700
+50441.00    -35.594 -4.967  -35.800 -4.800
+50442.00    -35.436 -4.904  -35.400 -4.800
+50443.00    -35.367 -4.907  -34.900 -4.900
+50444.00    -35.263 -4.967  -34.400 -4.900
+50445.00    -35.073 -5.038  -34.100 -4.800
+50446.00    -34.891 -5.087  -33.900 -4.900
+50447.00    -34.767 -5.091  -33.900 -5.000
+50448.00    -34.607 -5.044  -34.000 -5.000
+50449.00    -34.381 -4.984  -34.400 -5.000
+50450.00    -34.279 -4.977  -34.600 -4.900
+50451.00    -34.45  -5.015  -34.900 -4.900
+50452.00    -34.718 -4.984  -35.100 -5.000
+50453.00    -34.77  -4.808  -35.100 -5.000
+50454.00    -34.58  -4.61   -35.000 -5.000
+50455.00    -34.425 -4.619  -34.800 -5.000
+50456.00    -34.505 -4.87   -34.500 -4.900
+50457.00    -34.731 -5.121  -34.300 -4.900
+50458.00    -34.874 -5.094  -34.100 -4.700
+50459.00    -34.878 -4.806  -33.900 -4.600
+50460.00    -34.848 -4.504  -34.000 -4.600
+50461.00    -34.887 -4.366  -34.200 -4.400
+50462.00    -35.03  -4.371  -34.700 -4.500
+50463.00    -35.228 -4.443  -35.100 -4.500
+50464.00    -35.36  -4.572  -35.500 -4.500
+50465.00    -35.345 -4.767  -35.900 -4.600
+50466.00    -35.228 -4.96   -36.000 -4.600
+50467.00    -35.108 -5.052  -36.000 -4.800
+50468.00    -35.019 -5.031  -35.700 -4.900
+50469.00    -34.944 -4.987  -35.400 -4.900
+50470.00    -34.909 -5.005  -34.800 -5.000
+50471.00    -34.952 -5.08   -34.300 -5.000
+50472.00    -34.969 -5.153  -33.800 -5.100
+50473.00    -34.899 -5.216  -33.400 -5.100
+50474.00    -34.808 -5.307  -33.300 -5.200
+50475.00    -34.755 -5.425  -33.400 -5.300
+50476.00    -34.704 -5.513  -33.700 -5.400
+50477.00    -34.655 -5.554  -34.100 -5.500
+50478.00    -34.763 -5.622  -34.700 -5.600
+50479.00    -35.132 -5.764  -35.300 -5.600
+50480.00    -35.556 -5.875  -35.800 -5.700
+50481.00    -35.706 -5.791  -36.100 -5.700
+50482.00    -35.581 -5.537  -36.200 -5.700
+50483.00    -35.525 -5.36   -36.200 -5.600
+50484.00    -35.763 -5.423  -36.000 -5.500
+50485.00    -36.095 -5.534  -35.700 -5.400
+50486.00    -36.191 -5.494  -35.400 -5.200
+50487.00    -35.962 -5.348  -35.100 -5.200
+50488.00    -35.581 -5.255  -35.000 -5.100
+50489.00    -35.311 -5.312  -35.000 -5.100
+50490.00    -35.302 -5.428  -35.200 -5.200
+50491.00    -35.471 -5.471  -35.600 -5.300
+50492.00    -35.584 -5.462  -35.900 -5.400
+50493.00    -35.487 -5.565  -36.100 -5.600
+50494.00    -35.305 -5.819  -36.300 -5.700
+50495.00    -35.287 -6.058  -36.400 -5.800
+50496.00    -35.505 -6.179  -36.300 -6.000
+50497.00    -35.739 -6.232  -36.000 -6.100
+50498.00    -35.75  -6.277  -35.600 -6.200
+50499.00    -35.525 -6.279  -35.200 -6.200
+50500.00    -35.228 -6.218  -34.800 -6.300
+50501.00    -34.977 -6.173  -34.500 -6.400
+50502.00    -34.788 -6.245  -34.300 -6.400
+50503.00    -34.681 -6.45   -34.300 -6.500
+50504.00    -34.661 -6.689  -34.400 -6.700
+50505.00    -34.711 -6.868  -34.600 -6.800
+50506.00    -34.862 -7.007  -34.900 -6.900
+50507.00    -35.126 -7.176  -35.000 -7.000
+50508.00    -35.357 -7.34   -35.200 -7.200
+50509.00    -35.337 -7.364  -35.100 -7.300
+50510.00    -35.08  -7.214  -35.100 -7.200
+50511.00    -34.9   -7.053  -34.800 -7.200
+50512.00    -35.052 -7.057  -34.600 -7.100
+50513.00    -35.424 -7.189  -34.400 -7.000
+50514.00    -35.676 -7.254  -34.300 -6.800
+50515.00    -35.608 -7.179  -34.200 -6.700
+50516.00    -35.322 -7.099  -34.300 -6.700
+50517.00    -35.098 -7.134  -34.600 -6.800
+50518.00    -35.175 -7.215  -34.800 -6.900
+50519.00    -35.526 -7.215  -35.100 -7.100
+50520.00    -35.821 -7.181  -35.400 -7.300
+50521.00    -35.74  -7.281  -35.700 -7.600
+50522.00    -35.356 -7.561  -35.800 -7.800
+50523.00    -35.063 -7.881  -35.800 -8.000
+50524.00    -35.074 -8.085  -35.600 -8.100
+50525.00    -35.173 -8.15   -35.400 -8.100
+50526.00    -35.067 -8.134  -35.000 -8.000
+50527.00    -34.768 -8.117  -34.600 -7.800
+50528.00    -34.492 -8.026  -34.200 -7.800
+50529.00    -34.366 -7.875  -34.000 -7.700
+50530.00    -34.331 -7.79   -33.800 -7.700
+50531.00    -34.332 -7.831  -33.800 -7.700
+50532.00    -34.402 -7.949  -34.000 -7.800
+50533.00    -34.559 -8.07   -34.200 -8.000
+50534.00    -34.749 -8.162  -34.400 -8.200
+50535.00    -34.921 -8.255  -34.500 -8.300
+50536.00    -35.031 -8.35   -34.700 -8.500
+50537.00    -34.999 -8.398  -34.700 -8.600
+50538.00    -34.783 -8.379  -34.600 -8.600
+50539.00    -34.468 -8.361  -34.400 -8.500
+50540.00    -34.207 -8.419  -34.200 -8.400
+50541.00    -34.058 -8.522  -34.100 -8.300
+50542.00    -33.981 -8.529  -34.000 -8.100
+50543.00    -33.916 -8.416  -34.000 -8.100
+50544.00    -33.866 -8.336  -34.200 -8.200
+50545.00    -33.907 -8.419  -34.300 -8.200
+50546.00    -34.134 -8.601  -34.600 -8.400
+50547.00    -34.522 -8.7    -34.800 -8.500
+50548.00    -34.817 -8.675  -35.000 -8.700
+50549.00    -34.799 -8.67   -35.200 -8.900
+50550.00    -34.553 -8.825  -35.200 -9.100
+50551.00    -34.4   -9.111  -35.200 -9.200
+50552.00    -34.496 -9.379  -35.100 -9.300
+50553.00    -34.605 -9.516  -34.900 -9.300
+50554.00    -34.418 -9.494  -34.700 -9.300
+50555.00    -33.981 -9.32   -34.500 -9.100
+50556.00    -33.66  -9.081  -34.400 -9.000
+50557.00    -33.642 -8.863  -34.100 -8.900
+50558.00    -33.799 -8.727  -34.000 -8.800
+50559.00    -33.964 -8.693  -34.000 -8.700
+50560.00    -34.105 -8.741  -33.800 -8.700
+50561.00    -34.233 -8.856  -33.800 -8.700
+50562.00    -34.323 -9.017  -33.800 -8.900
+50563.00    -34.355 -9.186  -33.800 -9.000
+50564.00    -34.397 -9.3    -33.800 -9.100
+50565.00    -34.519 -9.323  -33.800 -9.200
+50566.00    -34.669 -9.28   -33.900 -9.300
+50567.00    -34.708 -9.228  -34.000 -9.300
+50568.00    -34.556 -9.186  -34.100 -9.200
+50569.00    -34.273 -9.11   -34.200 -9.100
+50570.00    -33.968 -8.955  -34.400 -9.000
+50571.00    -33.815 -8.747  -34.600 -8.900
+50572.00    -33.919 -8.616  -34.600 -8.900
+50573.00    -34.226 -8.691  -34.700 -8.900
+50574.00    -34.595 -8.937  -34.600 -9.000
+50575.00    -34.882 -9.159  -34.600 -9.200
+50576.00    -34.94  -9.207  -34.500 -9.300
+50577.00    -34.779 -9.127  -34.500 -9.500
+50578.00    -34.582 -9.09   -34.600 -9.700
+50579.00    -34.566 -9.201  -34.600 -9.800
+50580.00    -34.773 -9.406  -34.700 -9.800
+50581.00    -35.004 -9.58   -34.800 -9.800
+50582.00    -35.037 -9.646  -34.900 -9.700
+50583.00    -34.869 -9.605  -35.000 -9.500
+50584.00    -34.723 -9.498  -35.000 -9.300
+50585.00    -34.766 -9.369  -35.200 -9.100
+50586.00    -34.977 -9.233  -35.200 -8.900
+50587.00    -35.249 -9.077  -35.400 -8.900
+50588.00    -35.502 -8.914  -35.600 -8.800
+50589.00    -35.68  -8.828  -35.700 -8.800
+50590.00    -35.739 -8.904  -35.900 -9.000
+50591.00    -35.696 -9.106  -36.000 -9.100
+50592.00    -35.675 -9.283  -36.200 -9.100
+50593.00    -35.82  -9.309  -36.300 -9.100
+50594.00    -36.147 -9.202  -36.300 -9.100
+50595.00    -36.494 -9.088  -36.400 -9.000
+50596.00    -36.635 -9.06   -36.400 -8.800
+50597.00    -36.461 -9.109  -36.500 -8.700
+50598.00    -36.098 -9.146  -36.400 -8.500
+50599.00    -35.84  -9.106  -36.500 -8.500
+50600.00    -35.945 -9.018  -36.700 -8.400
+50601.00    -36.426 -8.965  -36.800 -8.400
+50602.00    -37.021 -8.971  -36.900 -8.400
+50603.00    -37.381 -8.954  -37.000 -8.600
+50604.00    -37.331 -8.885  -36.900 -8.600
+50605.00    -37.001 -8.751  -36.800 -8.700
+50606.00    -36.68  -8.626  -36.600 -8.800
+50607.00    -36.563 -8.615  -36.600 -8.900
+50608.00    -36.649 -8.724  -36.500 -8.900
+50609.00    -36.832 -8.869  -36.600 -8.900
+50610.00    -37.022 -8.965  -36.800 -8.800
+50611.00    -37.171 -8.983  -37.100 -8.800
+50612.00    -37.269 -8.943  -37.600 -8.600
+50613.00    -37.401 -8.877  -37.900 -8.500
+50614.00    -37.713 -8.789  -38.300 -8.300
+50615.00    -38.254 -8.66   -38.600 -8.200
+50616.00    -38.847 -8.493  -38.800 -8.200
+50617.00    -39.225 -8.376  -38.900 -8.200
+50618.00    -39.279 -8.393  -38.900 -8.200
+50619.00    -39.131 -8.572  -38.800 -8.300
+50620.00    -38.983 -8.777  -38.700 -8.300
+50621.00    -38.971 -8.793  -38.700 -8.200
+50622.00    -39.142 -8.558  -38.800 -8.200
+50623.00    -39.47  -8.23   -39.000 -8.100
+50624.00    -39.836 -8.028  -39.400 -7.900
+50625.00    -40.063 -8.04   -39.800 -7.800
+50626.00    -40.048 -8.189  -40.300 -7.800
+50627.00    -39.896 -8.344  -40.700 -7.800
+50628.00    -39.879 -8.442  -41.100 -8.000
+50629.00    -40.213 -8.514  -41.200 -8.100
+50630.00    -40.843 -8.615  -41.200 -8.200
+50631.00    -41.454 -8.756  -41.200 -8.500
+50632.00    -41.739 -8.881  -41.100 -8.600
+50633.00    -41.681 -8.937  -41.000 -8.700
+50634.00    -41.507 -8.914  -40.900 -8.800
+50635.00    -41.429 -8.843  -40.900 -8.400
+50636.00    -41.502 -8.758  -41.000 -8.400
+50637.00    -41.723 -8.697  -41.300 -8.400
+50638.00    -42.093 -8.706  -41.800 -8.500
+50639.00    -42.5   -8.795  -42.300 -8.700
+50640.00    -42.623 -8.87   -42.900 -8.900
+50641.00    -42.452 -8.876  -43.400 -9.000
+50642.00    -42.384 -8.829  -43.700 -9.100
+50643.00    -42.759 -8.755  -44.000 -9.100
+50644.00    -43.473 -8.658  -44.100 -9.000
+50645.00    -44.093 -8.558  -44.000 -8.900
+50646.00    -44.273 -8.484  -43.900 -8.800
+50647.00    -44.177 -8.552  -43.700 -8.700
+50648.00    -44.12  -8.742  -43.600 -8.600
+50649.00    -44.182 -8.836  -43.500 -8.400
+50650.00    -44.278 -8.661  -43.600 -8.100
+50651.00    -44.372 -8.299  -43.800 -8.000
+50652.00    -44.489 -7.986  -44.100 -7.900
+50653.00    -44.602 -7.865  -44.700 -7.900
+50654.00    -44.75  -7.94   -45.100 -8.000
+50655.00    -44.868 -8.105  -45.600 -8.100
+50656.00    -44.921 -8.291  -45.900 -8.400
+50657.00    -45.04  -8.493  -46.000 -8.600
+50658.00    -45.376 -8.698  -45.900 -8.800
+50659.00    -45.887 -8.873  -45.700 -8.900
+50660.00    -46.307 -9.000  -45.400 -9.000
+50661.00    -46.412 -9.089  -45.000 -9.000
+50662.00    -46.22  -9.118  -44.700 -8.800
+50663.00    -45.914 -9.024  -44.600 -8.700
+50664.00    -45.657 -8.792  -44.700 -8.600
+50665.00    -45.547 -8.534  -45.000 -8.300
+50666.00    -45.628 -8.427  -45.300 -8.300
+50667.00    -45.837 -8.524  -45.800 -8.400
+50668.00    -45.947 -8.72   -46.300 -8.500
+50669.00    -45.881 -8.87   -46.600 -8.600
+50670.00    -45.936 -8.931  -46.900 -8.700
+50671.00    -46.45  -8.946  -47.100 -8.600
+50672.00    -47.283 -8.912  -47.200 -8.600
+50673.00    -47.883 -8.784  -47.200 -8.500
+50674.00    -47.935 -8.623  -47.100 -8.400
+50675.00    -47.688 -8.592  -47.200 -8.300
+50676.00    -47.577 -8.76   -47.200 -8.300
+50677.00    -47.732 -8.968  -47.200 -8.300
+50678.00    -47.978 -8.989  -47.400 -8.300
+50679.00    -48.167 -8.786  -47.700 -8.400
+50680.00    -48.355 -8.502  -48.000 -8.500
+50681.00    -48.686 -8.226  -48.300 -8.500
+50682.00    -49.146 -8.005  -48.600 -8.500
+50683.00    -49.556 -7.831  -48.700 -8.500
+50684.00    -49.654 -7.781  -48.600 -8.400
+50685.00    -49.313 -7.978  -48.400 -8.500
+50686.00    -48.751 -8.393  -47.900 -8.500
+50687.00    -48.336 -8.85   -47.400 -8.600
+50688.00    -48.213 -9.148  -46.700 -8.600
+50689.00    -48.164 -9.285  -46.200 -8.700
+50690.00    -47.964 -9.314  -45.700 -8.700
+50691.00    -47.662 -9.193  -45.500 -8.500
+50692.00    -47.462 -8.877  -45.500 -8.500
+50693.00    -47.492 -8.473  -45.800 -8.400
+50694.00    -47.751 -8.201  -46.500 -8.300
+50695.00    -48.12  -8.192  -47.100 -8.300
+50696.00    -48.315 -8.349  -47.900 -8.200
+50697.00    -48.217 -8.481  -48.500 -8.300
+50698.00    -48.043 -8.514  -49.000 -8.200
+50699.00    -48.124 -8.512  -49.200 -8.200
+50700.00    -48.493 -8.521  -49.200 -8.400
+50701.00    -48.809 -8.492  -48.900 -8.400
+50702.00    -48.779 -8.397  -48.500 -8.400
+50703.00    -48.508 -8.319  -48.000 -8.400
+50704.00    -48.333 -8.347  -47.600 -8.300
+50705.00    -48.407 -8.418  -47.300 -8.300
+50706.00    -48.587 -8.382  -47.200 -8.100
+50707.00    -48.653 -8.215  -47.300 -8.000
+50708.00    -48.544 -8.038  -47.600 -8.000
+50709.00    -48.435 -7.928  -48.000 -7.900
+50710.00    -48.57  -7.763  -48.300 -7.900
+50711.00    -48.985 -7.455  -48.500 -7.900
+50712.00    -49.361 -7.175  -48.600 -7.900
+50713.00    -49.293 -7.201  -48.500 -8.100
+50714.00    -48.763 -7.58   -48.200 -8.100
+50715.00    -48.192 -8.065  -47.700 -8.100
+50716.00    -47.921 -8.404  -47.200 -8.200
+50717.00    -47.835 -8.55   -46.700 -8.100
+50718.00    -47.628 -8.589  -46.300 -8.000
+50719.00    -47.248 -8.553  -46.100 -7.900
+50720.00    -46.903 -8.401  -46.100 -7.800
+50721.00    -46.737 -8.154  -46.300 -7.700
+50722.00    -46.719 -7.924  -46.500 -7.700
+50723.00    -46.774 -7.794  -47.000 -7.600
+50724.00    -46.842 -7.74   -47.400 -7.600
+50725.00    -46.861 -7.668  -47.600 -7.600
+50726.00    -46.822 -7.544  -47.700 -7.700
+50727.00    -46.825 -7.43   -47.600 -7.600
+50728.00    -46.942 -7.398  -47.400 -7.600
+50729.00    -47.072 -7.443  -47.000 -7.500
+50730.00    -47.075 -7.459  -46.700 -7.400
+50731.00    -46.915 -7.42   -46.300 -7.200
+50732.00    -46.705 -7.356  -46.100 -6.900
+50733.00    -46.585 -7.242  -45.900 -6.800
+50734.00    -46.587 -7.041  -46.100 -6.700
+50735.00    -46.63  -6.818  -46.400 -6.600
+50736.00    -46.608 -6.732  -46.700 -6.700
+50737.00    -46.513 -6.824  -47.200 -6.700
+50738.00    -46.494 -6.907  -47.400 -6.900
+50739.00    -46.687 -6.789  -47.600 -7.000
+50740.00    -46.951 -6.552  -47.500 -7.000
+50741.00    -46.924 -6.497  -47.200 -7.200
+50742.00    -46.449 -6.781  -46.700 -7.200
+50743.00    -45.796 -7.228  -46.000 -7.100
+50744.00    -45.361 -7.559  -45.300 -7.000
+50745.00    -45.163 -7.637  -44.600 -6.900
+50746.00    -44.955 -7.529  -44.100 -6.800
+50747.00    -44.661 -7.347  -43.800 -6.600
+50748.00    -44.45  -7.124  -43.900 -6.500
+50749.00    -44.442 -6.847  -44.000 -6.400
+50750.00    -44.579 -6.528  -44.400 -6.400
+50751.00    -44.793 -6.227  -44.900 -6.400
+50752.00    -45.044 -6.085  -45.300 -6.500
+50753.00    -45.234 -6.067  -45.500 -6.500
+50754.00    -45.248 -6.117  -45.700 -6.700
+50755.00    -45.061 -6.203  -45.500 -6.700
+50756.00    -44.809 -6.281  -45.200 -6.600
+50757.00    -44.657 -6.329  -44.800 -6.500
+50758.00    -44.621 -6.355  -44.400 -6.300
+50759.00    -44.564 -6.363  -43.900 -6.100
+50760.00    -44.381 -6.318  -43.600 -5.900
+50761.00    -44.109 -6.162  -43.400 -5.700
+50762.00    -43.886 -5.878  -43.400 -5.500
+50763.00    -43.815 -5.559  -43.400 -5.500
+50764.00    -43.858 -5.363  -43.600 -5.400
+50765.00    -43.885 -5.357  -43.900 -5.400
+50766.00    -43.872 -5.503  -44.000 -5.500
+50767.00    -43.864 -5.592  -44.100 -5.600
+50768.00    -43.844 -5.507  -44.000 -5.700
+50769.00    -43.683 -5.404  -43.800 -5.700
+50770.00    -43.276 -5.471  -43.300 -5.700
+50771.00    -42.698 -5.7    -42.800 -5.700
+50772.00    -42.119 -5.99   -42.300 -5.600
+50773.00    -41.701 -6.141  -41.800 -5.400
+50774.00    -41.511 -6.071  -41.400 -5.300
+50775.00    -41.492 -5.886  -41.400 -5.200
+50776.00    -41.639 -5.687  -41.500 -5.200
+50777.00    -41.9   -5.487  -41.800 -5.200
+50778.00    -42.131 -5.255  -42.200 -5.200
+50779.00    -42.271 -5.001  -42.600 -5.400
+50780.00    -42.382 -4.787  -43.000 -5.400
+50781.00    -42.49  -4.714  -43.300 -5.400
+50782.00    -42.496 -4.83   -43.300 -5.500
+50783.00    -42.322 -5.038  -43.100 -5.400
+50784.00    -42.08  -5.155  -42.700 -5.300
+50785.00    -42.006 -5.068  -42.200 -5.100
+50786.00    -42.23  -4.811  -41.700 -4.900
+50787.00    -42.618 -4.511  -41.300 -4.700
+50788.00    -42.925 -4.247  -41.000 -4.400
+50789.00    -43.016 -4.024  -41.000 -4.200
+50790.00    -42.932 -3.85   -41.000 -4.100
+50791.00    -42.783 -3.813  -41.300 -4.000
+50792.00    -42.626 -4.029  -41.500 -4.000
+50793.00    -42.6   -4.398  -41.900 -4.100
+50794.00    -42.606 -4.778  -42.100 -4.100
+50795.00    -42.494 -4.99   -42.200 -4.300
+50796.00    -42.261 -4.916  -42.200 -4.400
+50797.00    -41.983 -4.655  -42.000 -4.400
+50798.00    -41.722 -4.447  -41.800 -4.400
+50799.00    -41.476 -4.454  -41.500 -4.400
+50800.00    -41.221 -4.656  -41.200 -4.400
+50801.00    -40.967 -4.871  -41.000 -4.200
+50802.00    -40.779 -4.977  -40.900 -4.200
+50803.00    -40.73  -4.984  -40.900 -4.100
+50804.00    -40.851 -4.928  -41.000 -4.100
+50805.00    -41.124 -4.778  -41.300 -4.100
+50806.00    -41.502 -4.467  -41.600 -4.200
+50807.00    -41.843 -4.114  -41.800 -4.400
+50808.00    -42.089 -3.801  -42.100 -4.500
+50809.00    -42.204 -3.644  -42.200 -4.500
+50810.00    -42.139 -3.756  -42.200 -4.600
+50811.00    -41.895 -4.09   -42.000 -4.500
+50812.00    -41.541 -4.423  -41.800 -4.500
+50813.00    -41.195 -4.529  -41.500 -4.400
+50814.00    -40.962 -4.383  -40.900 -4.300
+50815.00    -40.873 -4.154  -40.700 -4.200
+50816.00    -40.872 -4.018  -40.500 -4.200
+50817.00    -40.876 -4.01   -40.400 -4.100
+50818.00    -40.869 -4.068  -40.500 -4.100
+50819.00    -40.929 -4.136  -40.600 -4.100
+50820.00    -41.141 -4.226  -40.800 -4.300
+50821.00    -41.482 -4.375  -41.200 -4.300
+50822.00    -41.8   -4.566  -41.500 -4.300
+50823.00    -41.925 -4.7    -41.700 -4.400
+50824.00    -41.828 -4.677  -41.700 -4.300
+50825.00    -41.652 -4.508  -41.700 -4.300
+50826.00    -41.555 -4.315  -41.600 -4.200
+50827.00    -41.53  -4.216  -41.300 -4.100
+50828.00    -41.436 -4.261  -41.100 -4.000
+50829.00    -41.243 -4.363  -41.000 -3.900
+50830.00    -41.092 -4.439  -41.000 -3.900
+50831.00    -41.102 -4.498  -41.000 -4.100
+50832.00    -41.23  -4.579  -41.100 -4.300
+50833.00    -41.383 -4.688  -41.200 -4.500
+50834.00    -41.575 -4.776  -41.400 -4.700
+50835.00    -41.846 -4.761  -41.500 -4.900
+50836.00    -42.097 -4.6    -41.600 -5.000
+50837.00    -42.179 -4.367  -41.600 -5.100
+50838.00    -42.109 -4.251  -41.500 -5.100
+50839.00    -42.024 -4.396  -41.600 -5.100
+50840.00    -41.932 -4.717  -41.600 -5.000
+50841.00    -41.669 -4.952  -41.700 -4.900
+50842.00    -41.232 -4.896  -41.700 -4.600
+50843.00    -40.858 -4.609  -41.800 -4.500
+50844.00    -40.815 -4.312  -41.900 -4.300
+50845.00    -41.1   -4.186  -41.800 -4.300
+50846.00    -41.504 -4.23   -41.800 -4.200
+50847.00    -41.792 -4.374  -41.800 -4.300
+50848.00    -41.848 -4.585  -41.700 -4.500
+50849.00    -41.771 -4.78   -41.700 -4.700
+50850.00    -41.668 -4.94   -41.600 -4.800
+50851.00    -41.528 -5.048  -41.500 -5.000
+50852.00    -41.311 -5.089  -41.400 -5.100
+50853.00    -41.075 -5.1    -41.300 -5.200
+50854.00    -40.931 -5.144  -41.200 -5.300
+50855.00    -40.897 -5.254  -41.100 -5.200
+50856.00    -40.867 -5.403  -41.100 -5.300
+50857.00    -40.806 -5.533  -41.100 -5.300
+50858.00    -40.836 -5.62   -41.100 -5.200
+50859.00    -41.044 -5.694  -41.200 -5.300
+50860.00    -41.337 -5.766  -41.300 -5.300
+50861.00    -41.576 -5.835  -41.400 -5.600
+50862.00    -41.767 -5.9    -41.600 -5.700
+50863.00    -41.95  -5.932  -41.800 -5.900
+50864.00    -41.988 -5.839  -41.900 -6.200
+50865.00    -41.714 -5.577  -41.800 -6.400
+50866.00    -41.28  -5.296  -41.800 -6.500
+50867.00    -41.081 -5.276  -41.700 -6.400
+50868.00    -41.243 -5.624  -41.400 -6.300
+50869.00    -41.415 -6.106  -41.300 -6.200
+50870.00    -41.196 -6.323  -41.200 -6.000
+50871.00    -40.664 -6.183  -41.300 -5.800
+50872.00    -40.261 -5.936  -41.200 -5.800
+50873.00    -40.292 -5.822  -41.300 -5.700
+50874.00    -40.673 -5.862  -41.300 -5.800
+50875.00    -41.088 -5.956  -41.300 -5.900
+50876.00    -41.276 -6.067  -41.200 -6.100
+50877.00    -41.195 -6.227  -41.200 -6.300
+50878.00    -41.001 -6.417  -41.000 -6.500
+50879.00    -40.864 -6.56   -40.700 -6.700
+50880.00    -40.799 -6.628  -40.500 -6.800
+50881.00    -40.701 -6.676  -40.300 -6.800
+50882.00    -40.53  -6.748  -40.100 -6.800
+50883.00    -40.36  -6.807  -40.100 -6.700
+50884.00    -40.252 -6.791  -40.200 -6.600
+50885.00    -40.181 -6.707  -40.400 -6.500
+50886.00    -40.158 -6.655  -40.500 -6.500
+50887.00    -40.221 -6.703  -40.600 -6.500
+50888.00    -40.319 -6.811  -40.700 -6.600
+50889.00    -40.378 -6.915  -40.600 -6.800
+50890.00    -40.443 -7.026  -40.400 -7.000
+50891.00    -40.573 -7.176  -40.200 -7.100
+50892.00    -40.611 -7.293  -39.900 -7.300
+50893.00    -40.309 -7.236  -39.700 -7.400
+50894.00    -39.756 -7.022  -39.500 -7.400
+50895.00    -39.397 -6.895  -39.500 -7.400
+50896.00    -39.491 -7.066  -39.600 -7.300
+50897.00    -39.743 -7.426  -39.800 -7.300
+50898.00    -39.699 -7.644  -39.900 -7.200
+50899.00    -39.35  -7.571  -40.100 -7.100
+50900.00    -39.095 -7.379  -40.300 -7.100
+50901.00    -39.277 -7.307  -40.400 -7.100
+50902.00    -39.84  -7.389  -40.400 -7.300
+50903.00    -40.383 -7.53   -40.300 -7.500
+50904.00    -40.478 -7.721  -40.100 -7.700
+50905.00    -40.005 -8.04   -39.900 -8.000
+50906.00    -39.385 -8.425  -39.700 -8.200
+50907.00    -39.175 -8.682  -39.500 -8.300
+50908.00    -39.426 -8.722  -39.300 -8.300
+50909.00    -39.763 -8.616  -39.300 -8.300
+50910.00    -39.82  -8.467  -39.400 -8.300
+50911.00    -39.588 -8.277  -39.500 -8.100
+50912.00    -39.282 -8.007  -39.600 -8.000
+50913.00    -39.061 -7.717  -39.700 -7.900
+50914.00    -38.94  -7.557  -39.900 -7.800
+50915.00    -38.906 -7.612  -39.900 -7.800
+50916.00    -38.984 -7.808  -39.900 -7.800
+50917.00    -39.226 -8.019  -39.800 -8.000
+50918.00    -39.68  -8.204  -39.600 -8.100
+50919.00    -40.211 -8.376  -39.200 -8.300
+50920.00    -40.64  -8.529  -38.900 -8.400
+50921.00    -40.732 -8.569  -38.600 -8.500
+50922.00    -40.401 -8.438  -38.500 -8.500
+50923.00    -39.886 -8.255  -38.400 -8.500
+50924.00    -39.482 -8.204  -38.500 -8.400
+50925.00    -39.217 -8.3    -38.800 -8.300
+50926.00    -39.031 -8.347  -39.200 -8.100
+50927.00    -38.874 -8.235  -39.600 -8.100
+50928.00    -38.863 -8.093  -39.900 -8.000
+50929.00    -39.145 -8.093  -40.200 -8.000
+50930.00    -39.657 -8.207  -40.200 -8.100
+50931.00    -40.104 -8.288  -40.200 -8.200
+50932.00    -40.145 -8.321  -40.000 -8.400
+50933.00    -39.678 -8.452  -39.900 -8.500
+50934.00    -39.036 -8.741  -39.600 -8.700
+50935.00    -38.752 -9.045  -39.400 -8.800
+50936.00    -39.008 -9.187  -39.400 -8.900
+50937.00    -39.441 -9.146  -39.400 -8.800
+50938.00    -39.601 -9.007  -39.500 -8.700
+50939.00    -39.484 -8.819  -39.600 -8.600
+50940.00    -39.383 -8.633  -39.800 -8.500
+50941.00    -39.509 -8.437  -40.100 -8.400
+50942.00    -39.774 -8.301  -40.200 -8.400
+50943.00    -39.995 -8.307  -40.300 -8.400
+50944.00    -40.117 -8.424  -40.200 -8.400
+50945.00    -40.162 -8.554  -40.000 -8.600
+50946.00    -40.147 -8.644  -39.800 -8.700
+50947.00    -40.133 -8.716  -39.400 -8.700
+50948.00    -40.186 -8.797  -39.100 -8.700
+50949.00    -40.257 -8.86   -38.700 -8.700
+50950.00    -40.187 -8.854  -38.600 -8.700
+50951.00    -39.9   -8.767  -38.700 -8.600
+50952.00    -39.516 -8.63   -38.900 -8.500
+50953.00    -39.247 -8.461  -39.300 -8.300
+50954.00    -39.244 -8.261  -39.800 -8.200
+50955.00    -39.54  -8.064  -40.300 -8.100
+50956.00    -40.078 -8.007  -40.800 -8.100
+50957.00    -40.715 -8.186  -41.100 -8.100
+50958.00    -41.26  -8.483  -41.200 -8.300
+50959.00    -41.535 -8.665  -41.200 -8.400
+50960.00    -41.425 -8.644  -41.100 -8.500
+50961.00    -40.965 -8.568  -40.900 -8.600
+50962.00    -40.441 -8.616  -40.600 -8.700
+50963.00    -40.264 -8.772  -40.500 -8.800
+50964.00    -40.588 -8.883  -40.400 -8.900
+50965.00    -41.107 -8.865  -40.400 -8.800
+50966.00    -41.375 -8.773  -40.700 -8.800
+50967.00    -41.311 -8.683  -41.000 -8.700
+50968.00    -41.222 -8.594  -41.500 -8.700
+50969.00    -41.36  -8.47   -41.800 -8.500
+50970.00    -41.667 -8.319  -42.200 -8.400
+50971.00    -41.952 -8.19   -42.300 -8.400
+50972.00    -42.133 -8.127  -42.400 -8.300
+50973.00    -42.198 -8.147  -42.200 -8.200
+50974.00    -42.112 -8.23   -41.900 -8.200
+50975.00    -41.898 -8.318  -41.600 -8.100
+50976.00    -41.737 -8.358  -41.200 -8.100
+50977.00    -41.806 -8.341  -41.100 -8.000
+50978.00    -42.057 -8.306  -41.100 -7.900
+50979.00    -42.265 -8.274  -41.300 -7.900
+50980.00    -42.306 -8.214  -41.700 -7.900
+50981.00    -42.294 -8.064  -42.200 -7.800
+50982.00    -42.385 -7.755  -42.900 -7.800
+50983.00    -42.737 -7.381  -43.500 -7.900
+50984.00    -43.376 -7.149  -44.000 -8.000
+50985.00    -44.109 -7.2    -44.500 -8.000
+50986.00    -44.651 -7.494  -44.700 -8.200
+50987.00    -44.835 -7.869  -44.800 -8.300
+50988.00    -44.696 -8.192  -44.600 -8.400
+50989.00    -44.427 -8.36   -44.400 -8.500
+50990.00    -44.265 -8.489  -44.100 -8.600
+50991.00    -44.404 -8.676  -44.000 -8.700
+50992.00    -44.877 -8.839  -44.000 -8.800
+50993.00    -45.473 -8.861  -44.100 -8.800
+50994.00    -45.877 -8.727  -44.400 -8.700
+50995.00    -45.942 -8.503  -44.800 -8.700
+50996.00    -45.929 -8.285  -45.200 -8.400
+50997.00    -45.994 -8.101  -45.700 -8.200
+50998.00    -46.149 -7.903  -46.000 -8.000
+50999.00    -46.368 -7.676  -46.300 -7.800
+51000.00    -46.595 -7.491  -46.900 -7.800
+51001.00    -46.748 -7.448  -46.900 -7.700
+51002.00    -46.743 -7.578  -46.600 -7.600
+51003.00    -46.598 -7.777  -46.400 -7.600
+51004.00    -46.477 -7.881  -46.200 -7.500
+51005.00    -46.574 -7.811  -46.100 -7.600
+51006.00    -46.925 -7.646  -46.100 -7.500
+51007.00    -47.361 -7.531  -46.300 -7.600
+51008.00    -47.653 -7.53   -46.600 -7.600
+51009.00    -47.72  -7.593  -47.100 -7.700
+51010.00    -47.69  -7.644  -47.600 -7.700
+51011.00    -47.745 -7.711  -48.200 -7.800
+51012.00    -48.045 -7.865  -48.700 -8.000
+51013.00    -48.57  -8.132  -49.200 -8.100
+51014.00    -49.098 -8.442  -49.500 -8.200
+51015.00    -49.392 -8.62   -49.500 -8.300
+51016.00    -49.39  -8.515  -49.500 -8.300
+51017.00    -49.144 -8.246  -49.200 -8.400
+51018.00    -48.871 -7.991  -48.900 -8.400
+51019.00    -48.771 -7.853  -48.700 -8.400
+51020.00    -48.896 -7.836  -48.700 -8.400
+51021.00    -49.207 -7.885  -48.900 -8.400
+51022.00    -49.631 -7.988  -49.400 -8.300
+51023.00    -50.038 -8.169  -50.100 -8.200
+51024.00    -50.28  -8.393  -51.000 -8.200
+51025.00    -50.344 -8.509  -51.800 -8.200
+51026.00    -50.405 -8.419  -52.400 -8.100
+51027.00    -50.653 -8.153  -52.600 -8.100
+51028.00    -51.055 -7.825  -52.500 -7.900
+51029.00    -51.373 -7.595  -52.000 -7.700
+51030.00    -51.435 -7.588  -51.300 -7.600
+51031.00    -51.304 -7.798  -50.900 -7.500
+51032.00    -51.165 -8.045  -50.600 -7.400
+51033.00    -51.134 -8.123  -50.500 -7.400
+51034.00    -51.238 -7.997  -50.400 -7.400
+51035.00    -51.474 -7.824  -50.700 -7.500
+51036.00    -51.813 -7.773  -51.100 -7.600
+51037.00    -52.172 -7.849  -51.700 -7.800
+51038.00    -52.47  -7.924  -52.400 -7.900
+51039.00    -52.696 -7.916  -53.000 -8.000
+51040.00    -52.929 -7.861  -53.400 -8.200
+51041.00    -53.235 -7.851  -53.600 -8.200
+51042.00    -53.541 -7.944  -53.600 -8.300
+51043.00    -53.635 -8.126  -53.300 -8.300
+51044.00    -53.354 -8.33   -52.900 -8.400
+51045.00    -52.848 -8.426  -52.400 -8.400
+51046.00    -52.357 -8.392  -52.100 -8.500
+51047.00    -52.053 -8.266  -51.800 -8.500
+51048.00    -51.975 -8.09   -51.700 -8.500
+51049.00    -52.093 -7.93   -51.800 -8.500
+51050.00    -52.36  -7.882  -52.200 -8.400
+51051.00    -52.646 -7.984  -52.500 -8.200
+51052.00    -52.766 -8.121  -52.900 -8.000
+51053.00    -52.6   -8.189  -53.200 -7.900
+51054.00    -52.391 -8.14   -53.500 -7.800
+51055.00    -52.5   -8.006  -53.600 -7.700
+51056.00    -52.913 -7.856  -53.500 -7.600
+51057.00    -53.253 -7.741  -53.300 -8.100
+51058.00    -53.27  -7.722  -53.100 -7.800
+51059.00    -53.117 -7.848  -52.900 -7.400
+51060.00    -53.06  -8.05   -52.800 -7.200
+51061.00    -53.119 -8.139  -52.700 -7.200
+51062.00    -53.159 -7.991  -52.700 -7.100
+51063.00    -53.169 -7.718  -52.900 -7.300
+51064.00    -53.291 -7.561  -53.200 -7.400
+51065.00    -53.597 -7.628  -53.500 -7.700
+51066.00    -53.976 -7.806  -53.900 -7.800
+51067.00    -54.241 -7.942  -54.200 -8.000
+51068.00    -54.286 -8.017  -54.300 -8.200
+51069.00    -54.155 -8.103  -54.400 -8.300
+51070.00    -53.982 -8.212  -54.200 -8.300
+51071.00    -53.857 -8.264  -53.900 -8.300
+51072.00    -53.728 -8.182  -53.600 -8.200
+51073.00    -53.494 -7.96   -53.400 -8.000
+51074.00    -53.178 -7.703  -53.200 -7.800
+51075.00    -52.92  -7.466  -53.000 -7.500
+51076.00    -52.815 -7.21   -52.900 -7.300
+51077.00    -52.853 -6.993  -52.900 -7.200
+51078.00    -52.965 -6.951  -52.800 -7.100
+51079.00    -53.02  -7.135  -52.900 -7.100
+51080.00    -52.833 -7.414  -53.000 -7.200
+51081.00    -52.371 -7.604  -53.100 -7.300
+51082.00    -51.923 -7.638  -53.100 -7.400
+51083.00    -51.872 -7.595  -53.100 -7.500
+51084.00    -52.222 -7.548  -53.100 -7.500
+51085.00    -52.567 -7.473  -52.800 -7.400
+51086.00    -52.603 -7.352  -52.500 -7.300
+51087.00    -52.453 -7.258  -52.100 -7.200
+51088.00    -52.389 -7.256  -51.600 -6.900
+51089.00    -52.397 -7.265  -51.300 -6.800
+51090.00    -52.287 -7.141  -51.000 -6.600
+51091.00    -52.014 -6.918  -51.000 -6.600
+51092.00    -51.778 -6.808  -51.100 -6.600
+51093.00    -51.803 -6.917  -51.300 -6.800
+51094.00    -52.125 -7.06   -51.700 -7.000
+51095.00    -52.567 -7.073  -52.000 -7.200
+51096.00    -52.863 -7.019  -52.100 -7.400
+51097.00    -52.836 -7.074  -52.200 -7.600
+51098.00    -52.544 -7.283  -52.200 -7.700
+51099.00    -52.205 -7.5    -51.900 -7.800
+51100.00    -51.932 -7.589  -51.700 -7.500
+51101.00    -51.698 -7.537  -51.300 -7.400
+51102.00    -51.366 -7.426  -51.100 -7.100
+51103.00    -50.973 -7.266  -50.800 -6.700
+51104.00    -50.694 -7.001  -50.700 -6.500
+51105.00    -50.59  -6.664  -50.600 -6.300
+51106.00    -50.562 -6.414  -50.500 -6.300
+51107.00    -50.471 -6.367  -50.500 -6.200
+51108.00    -50.226 -6.463  -50.500 -6.300
+51109.00    -49.858 -6.543  -50.500 -6.400
+51110.00    -49.542 -6.54   -50.600 -6.500
+51111.00    -49.513 -6.517  -50.600 -6.500
+51112.00    -49.786 -6.526  -50.500 -6.500
+51113.00    -50.066 -6.493  -50.400 -6.400
+51114.00    -50.074 -6.323  -50.100 -6.300
+51115.00    -49.866 -6.052  -49.700 -6.100
+51116.00    -49.718 -5.815  -49.200 -5.700
+51117.00    -49.732 -5.65   -48.800 -5.400
+51118.00    -49.763 -5.481  -48.600 -5.200
+51119.00    -49.695 -5.314  -48.500 -5.100
+51120.00    -49.557 -5.299  -48.800 -5.200
+51121.00    -49.432 -5.497  -48.900 -5.300
+51122.00    -49.428 -5.68   -49.100 -5.600
+51123.00    -49.549 -5.581  -49.200 -5.900
+51124.00    -49.636 -5.36   -49.200 -6.100
+51125.00    -49.512 -5.337  -49.200 -6.200
+51126.00    -49.208 -5.588  -49.100 -6.200
+51127.00    -48.999 -5.897  -49.000 -6.100
+51128.00    -49.097 -5.996  -48.900 -6.000
+51129.00    -49.362 -5.854  -48.800 -5.700
+51130.00    -49.45  -5.638  -48.700 -5.500
+51131.00    -49.255 -5.453  -48.600 -5.200
+51132.00    -48.998 -5.261  -48.500 -5.000
+51133.00    -48.866 -5.02   -48.400 -4.800
+51134.00    -48.796 -4.794  -48.300 -4.700
+51135.00    -48.669 -4.68   -48.200 -4.800
+51136.00    -48.483 -4.688  -48.200 -4.800
+51137.00    -48.271 -4.743  -48.000 -4.900
+51138.00    -48.057 -4.794  -47.900 -5.100
+51139.00    -47.909 -4.855  -47.900 -5.100
+51140.00    -47.9   -4.94   -47.800 -5.200
+51141.00    -47.984 -4.99   -47.800 -5.000
+51142.00    -48.015 -4.929  -47.800 -4.900
+51143.00    -47.909 -4.754  -47.900 -4.600
+51144.00    -47.717 -4.534  -48.000 -4.300
+51145.00    -47.529 -4.314  -48.200 -4.200
+51146.00    -47.408 -4.096  -48.300 -4.100
+51147.00    -47.416 -3.926  -48.300 -4.100
+51148.00    -47.585 -3.925  -48.200 -4.100
+51149.00    -47.851 -4.124  -48.000 -4.100
+51150.00    -48.101 -4.343  -47.600 -4.300
+51151.00    -48.253 -4.343  -47.100 -4.400
+51152.00    -48.247 -4.125  -46.600 -4.400
+51153.00    -48.011 -3.953  -46.200 -4.500
+51154.00    -47.577 -4.035  -46.100 -4.500
+51155.00    -47.158 -4.267  -46.200 -4.500
+51156.00    -46.97  -4.371  -46.600 -4.400
+51157.00    -47.019 -4.194  -47.000 -4.200
+51158.00    -47.07  -3.875  -47.500 -3.900
+51159.00    -47.029 -3.617  -48.000 -3.700
+51160.00    -47.057 -3.473  -47.800 -3.600
+51161.00    -47.25  -3.394  -47.800 -3.500
+51162.00    -47.47  -3.369  -47.700 -3.400
+51163.00    -47.551 -3.44   -47.500 -3.500
+51164.00    -47.54  -3.572  -47.200 -3.600
+51165.00    -47.491 -3.726  -46.900 -3.800
+51166.00    -47.356 -3.882  -46.700 -3.900
+51167.00    -47.095 -3.999  -46.500 -4.000
+51168.00    -46.816 -4.034  -46.400 -4.000
+51169.00    -46.683 -3.973  -46.500 -3.800
+51170.00    -46.726 -3.851  -46.700 -3.700
+51171.00    -46.816 -3.722  -47.000 -3.600
+51172.00    -46.819 -3.604  -47.300 -3.400
+51173.00    -46.732 -3.485  -47.700 -3.400
+51174.00    -46.676 -3.368  -47.900 -3.400
+51175.00    -46.797 -3.313  -48.200 -3.400
+51176.00    -47.126 -3.412  -48.200 -3.600
+51177.00    -47.528 -3.676  -48.000 -3.800
+51178.00    -47.82  -3.956  -47.600 -3.900
+51179.00    -47.878 -4.035  -47.500 -4.100
+51180.00    -47.682 -3.856  -47.100 -4.300
+51181.00    -47.278 -3.62   -46.700 -4.400
+51182.00    -46.762 -3.573  -46.300 -4.300
+51183.00    -46.302 -3.734  -46.100 -4.200
+51184.00    -46.058 -3.893  -46.000 -4.000
+51185.00    -46.047 -3.871  -45.900 -3.600
+51186.00    -46.166 -3.703  -46.000 -3.400
+51187.00    -46.356 -3.545  -46.200 -3.200
+51188.00    -46.64  -3.475  -46.400 -3.100
+51189.00    -46.985 -3.444  -46.700 -3.100
+51190.00    -47.259 -3.366  -46.900 -3.100
+51191.00    -47.387 -3.229  -47.000 -3.300
+51192.00    -47.455 -3.108  -47.000 -3.400
+51193.00    -47.512 -3.137  -47.000 -3.500
+51194.00    -47.503 -3.349  -46.900 -3.600
+51195.00    -47.34  -3.626  -46.700 -3.600
+51196.00    -47.048 -3.8    -46.600 -3.600
+51197.00    -46.777 -3.794  -46.500 -3.600
+51198.00    -46.64  -3.676  -46.500 -3.600
+51199.00    -46.604 -3.568  -46.600 -3.600
+51200.00    -46.568 -3.522  -46.700 -3.600
+51201.00    -46.497 -3.504  -46.800 -3.600
+51202.00    -46.465 -3.479  -47.000 -3.600
+51203.00    -46.584 -3.478  -47.100 -3.600
+51204.00    -46.893 -3.577  -47.100 -3.700
+51205.00    -47.3   -3.82   -47.000 -3.800
+51206.00    -47.63  -4.136  -46.800 -3.900
+51207.00    -47.705 -4.271  -46.600 -4.000
+51208.00    -47.6   -4.157  -46.300 -4.100
+51209.00    -47.418 -3.95   -46.100 -4.200
+51210.00    -47.214 -3.884  -46.900 -4.200
+51211.00    -46.983 -4.08   -46.700 -4.300
+51212.00    -46.725 -4.43   -46.500 -4.500
+51213.00    -46.559 -4.657  -46.600 -4.700
+51214.00    -46.571 -4.685  -47.200 -4.400
+51215.00    -46.749 -4.628  -47.200 -4.500
+51216.00    -47.008 -4.601  -46.900 -5.000
+51217.00    -47.202 -4.608  -47.200 -4.900
+51218.00    -47.252 -4.589  -47.300 -4.600
+51219.00    -47.2   -4.537  -47.300 -4.400
+51220.00    -47.053 -4.638  -47.200 -4.300
+51221.00    -46.899 -4.883  -46.700 -4.200
+51222.00    -46.812 -5.137  -46.300 -4.400
+51223.00    -46.76  -5.353  -45.900 -4.400
+51224.00    -46.648 -5.413  -45.600 -4.500
+51225.00    -46.401 -5.227  -45.600 -4.700
+51226.00    -46.049 -4.889  -45.700 -4.700
+51227.00    -45.728 -4.641  -46.100 -4.700
+51228.00    -45.613 -4.616  -46.500 -4.700
+51229.00    -45.768 -4.75   -46.900 -4.700
+51230.00    -46.092 -4.918  -47.400 -4.700
+51231.00    -46.465 -5.026  -47.500 -4.700
+51232.00    -46.815 -5.072  -47.400 -4.900
+51233.00    -47.075 -5.107  -47.100 -5.000
+51234.00    -47.155 -5.161  -46.700 -5.200
+51235.00    -47.000 -5.216  -46.200 -5.400
+51236.00    -46.668 -5.242  -45.800 -5.500
+51237.00    -46.327 -5.26   -45.500 -5.700
+51238.00    -46.124 -5.336  -45.400 -5.700
+51239.00    -46.061 -5.504  -45.400 -5.700
+51240.00    -46.029 -5.696  -45.700 -5.700
+51241.00    -46.000 -5.803  -46.000 -5.700
+51242.00    -46.084 -5.799  -46.300 -5.600
+51243.00    -46.347 -5.754  -46.500 -5.600
+51244.00    -46.648 -5.748  -46.600 -5.600
+51245.00    -46.764 -5.789  -46.600 -5.700
+51246.00    -46.623 -5.839  -46.400 -5.700
+51247.00    -46.311 -5.857  -46.200 -5.800
+51248.00    -45.903 -5.815  -46.000 -5.800
+51249.00    -45.456 -5.726  -45.800 -5.900
+51250.00    -45.108 -5.693  -45.700 -6.000
+51251.00    -45.028 -5.818  -45.500 -6.000
+51252.00    -45.165 -6.058  -45.200 -6.100
+51253.00    -45.22  -6.234  -45.000 -6.200
+51254.00    -44.996 -6.233  -44.700 -6.200
+51255.00    -44.655 -6.111  -44.600 -6.200
+51256.00    -44.57  -6.095  -44.700 -6.200
+51257.00    -44.878 -6.311  -44.900 -6.300
+51258.00    -45.346 -6.634  -45.100 -6.400
+51259.00    -45.66  -6.856  -45.300 -6.400
+51260.00    -45.699 -6.887  -45.400 -6.500
+51261.00    -45.537 -6.777  -45.400 -6.700
+51262.00    -45.278 -6.621  -45.100 -6.800
+51263.00    -44.959 -6.551  -44.700 -7.000
+51264.00    -44.58  -6.601  -44.200 -7.000
+51265.00    -44.207 -6.741  -43.800 -7.100
+51266.00    -43.975 -6.93   -43.600 -7.200
+51267.00    -43.952 -7.096  -43.600 -7.100
+51268.00    -44.055 -7.163  -43.900 -7.100
+51269.00    -44.177 -7.111  -44.100 -7.100
+51270.00    -44.333 -7.012  -44.600 -7.100
+51271.00    -44.574 -6.956  -45.000 -7.100
+51272.00    -44.827 -6.963  -45.200 -7.100
+51273.00    -44.962 -7.003  -45.300 -7.200
+51274.00    -44.989 -7.069  -45.100 -7.200
+51275.00    -45.007 -7.166  -44.900 -7.100
+51276.00    -44.959 -7.24   -44.500 -7.100
+51277.00    -44.678 -7.199  -44.200 -7.100
+51278.00    -44.248 -7.066  -43.900 -7.100
+51279.00    -44.036 -7.015  -43.700 -7.000
+51280.00    -44.206 -7.154  -43.700 -7.000
+51281.00    -44.423 -7.346  -43.800 -7.100
+51282.00    -44.271 -7.351  -44.000 -7.200
+51283.00    -43.819 -7.158  -44.100 -7.300
+51284.00    -43.559 -7.024  -44.100 -7.500
+51285.00    -43.772 -7.166  -44.100 -7.600
+51286.00    -44.219 -7.503  -44.100 -7.800
+51287.00    -44.483 -7.783  -43.900 -7.900
+51288.00    -44.426 -7.869  -43.800 -7.800
+51289.00    -44.255 -7.827  -43.800 -7.700
+51290.00    -44.189 -7.787  -43.600 -7.600
+51291.00    -44.27  -7.72   -43.500 -7.500
+51292.00    -44.363 -7.615  -43.300 -7.300
+51293.00    -44.286 -7.538  -43.200 -7.300
+51294.00    -44.026 -7.532  -43.200 -7.400
+51295.00    -43.778 -7.563  -43.200 -7.400
+51296.00    -43.721 -7.57   -43.400 -7.500
+51297.00    -43.841 -7.479  -43.500 -7.500
+51298.00    -44.012 -7.403  -43.600 -7.400
+51299.00    -44.149 -7.461  -43.700 -7.500
+51300.00    -44.211 -7.61   -44.600 -7.600
+51301.00    -44.192 -7.742  -43.800 -7.600
+51302.00    -44.182 -7.836  -44.000 -7.800
+51303.00    -44.287 -7.953  -44.100 -7.900
+51304.00    -44.407 -8.06   -44.200 -7.900
+51305.00    -44.268 -8.067  -44.200 -7.900
+51306.00    -43.824 -7.917  -44.100 -8.000
+51307.00    -43.452 -7.735  -44.000 -8.000
+51308.00    -43.527 -7.72   -43.900 -7.900
+51309.00    -43.943 -7.868  -44.000 -7.900
+51310.00    -44.298 -7.963  -44.100 -7.900
+51311.00    -44.466 -7.888  -44.500 -7.900
+51312.00    -44.718 -7.796  -44.800 -7.900
+51313.00    -45.251 -7.884  -45.100 -7.900
+51314.00    -45.84  -8.099  -45.300 -8.000
+51315.00    -46.06  -8.216  -45.300 -8.000
+51316.00    -45.71  -8.136  -45.200 -8.000
+51317.00    -44.993 -7.971  -44.800 -8.000
+51318.00    -44.367 -7.836  -44.400 -7.900
+51319.00    -44.264 -7.766  -44.000 -7.800
+51320.00    -44.634 -7.706  -43.700 -7.600
+51321.00    -45.026 -7.668  -43.700 -7.600
+51322.00    -45.105 -7.703  -43.800 -7.500
+51323.00    -44.999 -7.756  -44.300 -7.600
+51324.00    -45.073 -7.702  -44.900 -7.600
+51325.00    -45.45  -7.524  -45.500 -7.700
+51326.00    -45.914 -7.374  -46.100 -7.800
+51327.00    -46.216 -7.389  -46.500 -7.900
+51328.00    -46.294 -7.528  -46.600 -8.000
+51329.00    -46.23  -7.652  -46.600 -8.000
+51330.00    -46.156 -7.711  -46.300 -8.000
+51331.00    -46.188 -7.788  -45.900 -8.000
+51332.00    -46.313 -7.945  -45.400 -7.900
+51333.00    -46.331 -8.086  -45.100 -7.700
+51334.00    -46.074 -8.07   -44.800 -7.600
+51335.00    -45.676 -7.905  -45.000 -7.500
+51336.00    -45.471 -7.737  -45.300 -7.400
+51337.00    -45.625 -7.657  -45.500 -7.500
+51338.00    -46.017 -7.604  -45.900 -7.500
+51339.00    -46.5   -7.512  -46.200 -7.500
+51340.00    -47.061 -7.455  -46.600 -7.600
+51341.00    -47.695 -7.529  -46.900 -7.600
+51342.00    -48.241 -7.651  -47.100 -7.500
+51343.00    -48.468 -7.639  -47.300 -7.500
+51344.00    -48.269 -7.472  -47.300 -7.400
+51345.00    -47.774 -7.348  -47.400 -7.200
+51346.00    -47.313 -7.41   -47.400 -7.200
+51347.00    -47.275 -7.561  -47.500 -7.100
+51348.00    -47.735 -7.611  -47.600 -7.000
+51349.00    -48.309 -7.538  -47.700 -7.000
+51350.00    -48.556 -7.477  -47.900 -7.100
+51351.00    -48.49  -7.492  -48.100 -7.200
+51352.00    -48.534 -7.487  -48.400 -7.300
+51353.00    -48.969 -7.358  -48.800 -7.400
+51354.00    -49.61  -7.161  -49.100 -7.500
+51355.00    -50.118 -7.027  -49.400 -7.600
+51356.00    -50.367 -7.009  -49.700 -7.700
+51357.00    -50.404 -7.053  -49.900 -7.600
+51358.00    -50.26  -7.108  -49.900 -7.500
+51359.00    -49.982 -7.181  -49.700 -7.300
+51360.00    -49.792 -7.248  -49.400 -7.100
+51361.00    -49.812 -7.308  -49.100 -6.800
+51362.00    -49.908 -7.323  -48.900 -6.600
+51363.00    -49.912 -7.252  -48.700 -6.500
+51364.00    -49.865 -7.109  -48.900 -6.500
+51365.00    -49.964 -6.948  -49.600 -6.500
+51366.00    -50.356 -6.819  -50.100 -6.700
+51367.00    -51.019 -6.775  -50.800 -6.800
+51368.00    -51.79  -6.877  -51.400 -7.000
+51369.00    -52.452 -7.115  -51.800 -7.100
+51370.00    -52.823 -7.344  -52.100 -7.100
+51371.00    -52.85  -7.379  -52.100 -7.100
+51372.00    -52.595 -7.216  -52.000 -7.000
+51373.00    -52.187 -7.077  -51.800 -6.900
+51374.00    -51.829 -7.158  -51.700 -6.800
+51375.00    -51.75  -7.34   -51.500 -6.800
+51376.00    -52.082 -7.389  -51.600 -6.800
+51377.00    -52.651 -7.243  -51.900 -6.800
+51378.00    -53.103 -7.063  -52.300 -6.900
+51379.00    -53.303 -7.029  -52.900 -6.900
+51380.00    -53.444 -7.135  -53.500 -7.000
+51381.00    -53.672 -7.196  -54.000 -7.100
+51382.00    -53.947 -7.123  -54.400 -7.200
+51383.00    -54.161 -6.993  -54.700 -7.300
+51384.00    -54.31  -6.929  -54.800 -7.400
+51385.00    -54.453 -6.98   -54.700 -7.400
+51386.00    -54.561 -7.092  -54.700 -7.300
+51387.00    -54.587 -7.161  -54.700 -7.300
+51388.00    -54.624 -7.13   -54.800 -7.200
+51389.00    -54.828 -7.049  -55.000 -7.000
+51390.00    -55.189 -7.009  -55.000 -6.900
+51391.00    -55.507 -7.042  -55.200 -6.900
+51392.00    -55.621 -7.098  -55.300 -6.800
+51393.00    -55.601 -7.107  -55.500 -6.900
+51394.00    -55.652 -7.071  -55.700 -7.000
+51395.00    -55.892 -7.068  -55.600 -7.100
+51396.00    -56.243 -7.181  -55.700 -7.300
+51397.00    -56.493 -7.401  -55.600 -7.400
+51398.00    -56.471 -7.598  -55.600 -7.400
+51399.00    -56.196 -7.622  -55.500 -7.500
+51400.00    -55.853 -7.45   -55.500 -7.400
+51401.00    -55.639 -7.233  -55.500 -7.300
+51402.00    -55.608 -7.139  -55.500 -7.000
+51403.00    -55.764 -7.231  -55.700 -6.900
+51404.00    -56.134 -7.367  -55.800 -6.700
+51405.00    -56.653 -7.39   -56.000 -6.700
+51406.00    -57.117 -7.32   -56.300 -6.700
+51407.00    -57.303 -7.253  -56.400 -6.800
+51408.00    -57.153 -7.195  -56.700 -7.000
+51409.00    -56.878 -7.109  -57.000 -7.200
+51410.00    -56.66  -6.936  -57.100 -7.400
+51411.00    -56.613 -6.727  -57.300 -7.600
+51412.00    -56.776 -6.641  -57.300 -7.700
+51413.00    -57.052 -6.79   -57.300 -7.800
+51414.00    -57.239 -7.145  -57.200 -7.800
+51415.00    -57.195 -7.533  -57.200 -7.600
+51416.00    -57.012 -7.7    -57.000 -7.500
+51417.00    -56.883 -7.596  -56.900 -7.400
+51418.00    -56.917 -7.396  -57.000 -7.300
+51419.00    -57.065 -7.297  -56.900 -7.200
+51420.00    -57.212 -7.341  -57.000 -7.100
+51421.00    -57.308 -7.416  -57.100 -7.200
+51422.00    -57.404 -7.409  -57.200 -7.300
+51423.00    -57.57  -7.321  -57.300 -7.300
+51424.00    -57.793 -7.237  -57.400 -7.400
+51425.00    -57.956 -7.211  -56.800 -9.400
+51426.00    -57.921 -7.216  -56.200 -9.400
+51427.00    -57.637 -7.174  -55.800 -9.000
+51428.00    -57.197 -7.042  -55.700 -8.200
+51429.00    -56.771 -6.869  -55.900 -7.100
+51430.00    -56.497 -6.756  -56.300 -5.900
+51431.00    -56.414 -6.745  -56.900 -5.000
+51432.00    -56.488 -6.782  -57.700 -4.500
+51433.00    -56.684 -6.805  -58.400 -4.500
+51434.00    -56.957 -6.839  -58.700 -4.900
+51435.00    -57.187 -6.95   -58.600 -5.800
+51436.00    -57.197 -7.113  -58.100 -6.800
+51437.00    -56.947 -7.205  -57.400 -8.000
+51438.00    -56.642 -7.142  -56.500 -9.000
+51439.00    -56.571 -6.984  -55.600 -9.500
+51440.00    -56.794 -6.865  -54.900 -9.600
+51441.00    -57.069 -6.869  -54.500 -9.100
+51442.00    -57.122 -6.971  -54.700 -8.300
+51443.00    -56.919 -7.059  -55.400 -7.200
+51444.00    -56.693 -7.003  -56.400 -6.000
+51445.00    -56.571 -6.753  -57.500 -5.100
+51446.00    -56.552 -6.423  -58.500 -4.500
+51447.00    -56.643 -6.241  -59.300 -4.400
+51448.00    -56.864 -6.341  -59.600 -4.800
+51449.00    -57.166 -6.617  -59.300 -5.600
+51450.00    -57.432 -6.823  -58.400 -6.600
+51451.00    -57.567 -6.8    -57.300 -7.600
+51452.00    -57.566 -6.6    -56.000 -8.500
+51453.00    -57.458 -6.38   -54.800 -8.900
+51454.00    -57.245 -6.243  -54.000 -8.900
+51455.00    -56.917 -6.196  -52.300 -6.000
+51456.00    -56.508 -6.211  -53.600 -6.000
+51457.00    -56.129 -6.271  -55.400 -6.200
+51458.00    -55.896 -6.304  -56.800 -6.300
+51459.00    -55.847 -6.27   -57.600 -6.200
+51460.00    -55.898 -6.163  -57.600 -6.200
+51461.00    -55.943 -6.021  -57.200 -6.000
+51462.00    -55.939 -5.932  -56.500 -6.000
+51463.00    -55.842 -5.953  -55.800 -5.900
+51464.00    -55.544 -6.029  -55.300 -5.900
+51465.00    -55.045 -6.071  -54.300 -5.900
+51466.00    -54.573 -6.029  -52.700 -5.800
+51467.00    -54.432 -5.96   -51.100 -5.600
+51468.00    -54.628 -5.945  -50.300 -5.600
+51469.00    -54.817 -5.976  -50.700 -5.600
+51470.00    -54.729 -5.994  -52.000 -5.800
+51471.00    -54.466 -5.968  -53.800 -5.800
+51472.00    -54.269 -5.88   -55.300 -5.900
+51473.00    -54.163 -5.685  -56.100 -5.900
+51474.00    -54.007 -5.385  -55.800 -5.700
+51475.00    -53.796 -5.13   -54.900 -5.500
+51476.00    -53.705 -5.112  -54.000 -5.300
+51477.00    -53.831 -5.329  -53.500 -5.300
+51478.00    -54.074 -5.541  -54.000 -5.500
+51479.00    -54.225 -5.543  -54.500 -5.600
+51480.00    -54.232 -5.353  -54.800 -5.700
+51481.00    -54.157 -5.162  -54.800 -5.800
+51482.00    -54.049 -5.102  -54.600 -5.700
+51483.00    -53.92  -5.144  -54.100 -5.600
+51484.00    -53.758 -5.195  -53.700 -5.300
+51485.00    -53.56  -5.22   -53.500 -5.200
+51486.00    -53.385 -5.213  -53.100 -5.000
+51487.00    -53.322 -5.134  -52.900 -4.800
+51488.00    -53.379 -4.942  -52.900 -4.600
+51489.00    -53.452 -4.689  -52.900 -4.600
+51490.00    -53.43  -4.523  -52.900 -4.500
+51491.00    -53.264 -4.539  -52.900 -4.600
+51492.00    -52.931 -4.668  -52.800 -4.600
+51493.00    -52.458 -4.749  -52.500 -4.700
+51494.00    -52.027 -4.713  -52.100 -4.700
+51495.00    -51.874 -4.64   -51.800 -4.800
+51496.00    -51.991 -4.607  -51.500 -4.800
+51497.00    -52.073 -4.572  -51.300 -4.600
+51498.00    -51.914 -4.469  -51.300 -4.500
+51499.00    -51.686 -4.342  -51.500 -4.400
+51500.00    -51.638 -4.256  -51.800 -4.200
+51501.00    -51.743 -4.188  -52.100 -4.000
+51502.00    -51.774 -4.034  -52.400 -3.800
+51503.00    -51.673 -3.805  -52.500 -3.900
+51504.00    -51.62  -3.685  -52.400 -3.800
+51505.00    -51.707 -3.783  -52.100 -3.800
+51506.00    -51.795 -3.944  -51.700 -3.900
+51507.00    -51.724 -3.926  -51.300 -4.000
+51508.00    -51.496 -3.714  -50.800 -4.000
+51509.00    -51.223 -3.529  -50.500 -4.000
+51510.00    -51.008 -3.52   -50.300 -4.000
+51511.00    -50.914 -3.599  -50.300 -3.900
+51512.00    -50.926 -3.611  -50.400 -3.700
+51513.00    -50.925 -3.552  -50.700 -3.500
+51514.00    -50.824 -3.522  -51.000 -3.400
+51515.00    -50.71  -3.534  -51.300 -3.200
+51516.00    -50.74  -3.497  -51.500 -3.100
+51517.00    -50.909 -3.374  -51.500 -3.000
+51518.00    -51.049 -3.267  -51.400 -3.000
+51519.00    -51.03  -3.288  -51.200 -3.200
+51520.00    -50.877 -3.408  -50.800 -3.300
+51521.00    -50.69  -3.522  -50.300 -3.500
+51522.00    -50.579 -3.565  -50.000 -3.600
+51523.00    -50.647 -3.582  -49.800 -3.600
+51524.00    -50.882 -3.625  -49.700 -3.600
+51525.00    -51.095 -3.633  -49.900 -3.500
+51526.00    -51.109 -3.518  -50.300 -3.400
+51527.00    -50.97  -3.325  -50.700 -3.200
+51528.00    -50.851 -3.22   -51.100 -3.100
+51529.00    -50.764 -3.231  -51.500 -3.000
+51530.00    -50.606 -3.214  -51.800 -3.000
+51531.00    -50.434 -3.085  -51.800 -3.000
+51532.00    -50.456 -2.967  -51.700 -3.000
+51533.00    -50.71  -3.008  -51.400 -3.000
+51534.00    -50.948 -3.136  -50.900 -3.100
+51535.00    -50.913 -3.137  -50.400 -3.100
+51536.00    -50.581 -2.97   -49.900 -3.100
+51537.00    -50.118 -2.834  -49.500 -3.100
+51538.00    -49.742 -2.876  -49.400 -2.900
+51539.00    -49.636 -2.972  -49.400 -2.800
+51540.00    -49.841 -2.907  -49.800 -2.700
+51541.00    -50.176 -2.678  -50.100 -2.600
+51542.00    -50.392 -2.493  -50.500 -2.500
+51543.00    -50.473 -2.486  -50.800 -2.400
+51544.00    -50.607 -2.585  -50.900 -2.500
+51545.00    -50.218 -2.679  -50.800 -2.500
+51546.00    -50.459 -2.699  -50.600 -2.500
+51547.00    -50.437 -2.766  -50.300 -2.800
+51548.00    -50.188 -2.893  -49.900 -2.900
+51549.00    -49.678 -3.066  -49.400 -2.900
+51550.00    -49.409 -3.103  -49.000 -2.900
+51551.00    -49.211 -3.09   -48.500 -2.800
+51552.00    -49.134 -3.078  -48.400 -2.600
+51553.00    -49.197 -3.071  -48.300 -2.400
+51554.00    -49.336 -3.037  -48.600 -2.200
+51555.00    -49.474 -2.995  -48.900 -2.100
+51556.00    -49.629 -2.979  -49.400 -2.100
+51557.00    -49.677 -3.035  -49.900 -2.100
+51558.00    -49.689 -3.048  -50.400 -2.200
+51559.00    -49.8   -2.966  -50.700 -2.300
+51560.00    -50.181 -2.879  -50.900 -2.500
+51561.00    -50.789 -2.914  -50.800 -2.600
+51562.00    -51.314 -3.044  -50.500 -2.600
+51563.00    -51.604 -3.008  -50.200 -2.600
+51564.00    -51.228 -2.945  -49.700 -2.500
+51565.00    -50.536 -2.903  -49.300 -2.500
+51566.00    -49.812 -3.019  -49.100 -2.300
+51567.00    -49.344 -3.19   -49.200 -2.100
+51568.00    -49.289 -3.182  -49.500 -2.000
+51569.00    -49.551 -2.941  -49.800 -2.000
+51570.00    -49.778 -2.721  -50.400 -1.900
+51571.00    -50.052 -2.653  -50.800 -2.100
+51572.00    -50.347 -2.785  -51.100 -2.300
+51573.00    -50.674 -2.964  -51.200 -2.500
+51574.00    -50.834 -3.087  -51.000 -2.700
+51575.00    -50.661 -3.175  -50.700 -3.000
+51576.00    -50.262 -3.274  -50.300 -3.100
+51577.00    -49.874 -3.397  -49.800 -3.100
+51578.00    -49.607 -3.461  -49.400 -3.000
+51579.00    -49.395 -3.453  -49.000 -2.900
+51580.00    -49.234 -3.387  -48.600 -3.100
+51581.00    -49.211 -3.314  -48.400 -3.300
+51582.00    -49.352 -3.296  -48.300 -3.300
+51583.00    -49.554 -3.361  -48.700 -3.300
+51584.00    -49.669 -3.503  -49.200 -3.300
+51585.00    -49.735 -3.605  -49.800 -3.500
+51586.00    -49.789 -3.637  -50.400 -3.600
+51587.00    -49.927 -3.614  -50.800 -3.700
+51588.00    -50.204 -3.618  -51.000 -3.900
+51589.00    -50.548 -3.721  -50.800 -3.900
+51590.00    -50.779 -3.891  -50.600 -3.900
+51591.00    -50.742 -4.014  -50.200 -3.900
+51592.00    -50.43  -4.036  -49.700 -3.900
+51593.00    -49.958 -4.056  -49.400 -4.000
+51594.00    -49.483 -4.198  -49.100 -4.000
+51595.00    -49.155 -4.423  -49.000 -4.000
+51596.00    -49.085 -4.532  -49.100 -4.200
+51597.00    -49.273 -4.415  -49.300 -4.300
+51598.00    -49.615 -4.191  -49.600 -4.500
+51599.00    -50.009 -4.08   -49.900 -4.600
+51600.00    -50.396 -4.16   -50.100 -4.700
+51601.00    -50.665 -4.331  -50.200 -4.700
+51602.00    -50.64  -4.465  -50.100 -4.700
+51603.00    -50.248 -4.535  -49.800 -4.600
+51604.00    -49.671 -4.593  -49.500 -4.600
+51605.00    -49.399 -4.629  -49.200 -4.500
+51606.00    -49.141 -4.709  -48.900 -4.500
+51607.00    -49.024 -4.718  -48.700 -4.400
+51608.00    -48.94  -4.618  -48.600 -4.400
+51609.00    -48.886 -4.461  -48.700 -4.400
+51610.00    -48.9   -4.379  -48.900 -4.500
+51611.00    -48.978 -4.47   -49.200 -4.600
+51612.00    -49.184 -4.698  -49.500 -4.700
+51613.00    -49.341 -4.918  -49.700 -4.900
+51614.00    -49.548 -5.03   -49.900 -5.000
+51615.00    -49.769 -5.045  -49.900 -5.000
+51616.00    -49.912 -5.047  -49.800 -5.100
+51617.00    -49.873 -5.102  -49.500 -5.100
+51618.00    -49.604 -5.206  -49.200 -5.100
+51619.00    -48.898 -5.234  -48.800 -5.100
+51620.00    -48.468 -5.282  -48.400 -5.000
+51621.00    -48.19  -5.32   -48.100 -5.100
+51622.00    -48.112 -5.425  -47.900 -5.200
+51623.00    -48.156 -5.601  -47.800 -5.300
+51624.00    -48.231 -5.732  -47.900 -5.500
+51625.00    -48.329 -5.708  -48.000 -5.600
+51626.00    -48.514 -5.621  -48.100 -5.900
+51627.00    -48.847 -5.51   -48.400 -6.000
+51628.00    -49.222 -5.532  -48.500 -6.000
+51629.00    -49.44  -5.651  -48.600 -6.100
+51630.00    -49.344 -5.769  -48.500 -6.000
+51631.00    -48.936 -5.841  -48.500 -5.900
+51632.00    -48.369 -5.887  -48.300 -5.800
+51633.00    -47.929 -5.952  -47.900 -5.600
+51634.00    -47.625 -5.998  -47.600 -5.500
+51635.00    -47.582 -6.012  -47.200 -5.400
+51636.00    -47.702 -5.949  -47.000 -5.300
+51637.00    -47.809 -5.803  -46.900 -5.400
+51638.00    -47.813 -5.668  -47.000 -5.500
+51639.00    -47.797 -5.69   -47.200 -5.600
+51640.00    -47.915 -5.922  -47.500 -5.800
+51641.00    -48.219 -6.267  -47.800 -6.000
+51642.00    -48.592 -6.499  -48.100 -6.200
+51643.00    -48.875 -6.543  -48.200 -6.300
+51644.00    -48.961 -6.454  -48.200 -6.300
+51645.00    -48.819 -6.348  -48.100 -6.400
+51646.00    -48.458 -6.306  -47.900 -6.400
+51647.00    -48.173 -6.329  -47.600 -6.300
+51648.00    -47.67  -6.383  -47.400 -6.200
+51649.00    -47.392 -6.431  -47.400 -6.100
+51650.00    -47.442 -6.469  -47.500 -6.100
+51651.00    -47.718 -6.494  -47.600 -6.100
+51652.00    -47.987 -6.477  -48.000 -6.100
+51653.00    -48.118 -6.4    -48.300 -6.200
+51654.00    -48.421 -6.275  -48.600 -6.400
+51655.00    -48.584 -6.241  -48.800 -6.600
+51656.00    -48.813 -6.308  -48.800 -6.700
+51657.00    -48.944 -6.435  -48.800 -6.900
+51658.00    -48.882 -6.565  -48.500 -6.900
+51659.00    -48.658 -6.675  -48.200 -6.900
+51660.00    -48.308 -6.752  -47.900 -6.700
+51661.00    -47.776 -6.773  -47.600 -6.400
+51662.00    -47.357 -6.727  -47.400 -6.200
+51663.00    -47.247 -6.683  -47.300 -6.100
+51664.00    -47.527 -6.672  -47.200 -5.900
+51665.00    -47.919 -6.638  -47.300 -5.900
+51666.00    -48.105 -6.532  -47.500 -5.900
+51667.00    -48.118 -6.439  -47.800 -6.200
+51668.00    -48.26  -6.541  -48.100 -6.300
+51669.00    -48.636 -6.82   -48.500 -6.500
+51670.00    -49.026 -7.107  -48.600 -6.800
+51671.00    -49.157 -7.191  -48.500 -6.900
+51672.00    -49.011 -7.051  -48.600 -7.000
+51673.00    -48.77  -6.842  -48.500 -6.900
+51674.00    -48.563 -6.721  -48.400 -6.800
+51675.00    -48.366 -6.722  -48.400 -6.700
+51676.00    -48.124 -6.789  -48.400 -6.400
+51677.00    -47.896 -6.855  -48.500 -6.300
+51678.00    -47.849 -6.865  -48.600 -6.200
+51679.00    -48.073 -6.784  -48.800 -6.100
+51680.00    -48.453 -6.617  -49.000 -6.200
+51681.00    -48.775 -6.439  -49.200 -6.300
+51682.00    -49.026 -6.4    -49.300 -6.600
+51683.00    -49.101 -6.484  -49.400 -6.800
+51684.00    -49.147 -6.658  -49.300 -7.000
+51685.00    -49.147 -6.808  -49.000 -7.100
+51686.00    -49.124 -6.895  -48.800 -7.200
+51687.00    -49.145 -6.969  -48.400 -7.200
+51688.00    -49.144 -7.041  -48.000 -7.100
+51689.00    -48.814 -6.995  -47.800 -6.800
+51690.00    -48.451 -6.855  -47.800 -6.600
+51691.00    -48.306 -6.697  -47.900 -6.500
+51692.00    -48.673 -6.666  -48.200 -6.400
+51693.00    -49.336 -6.731  -48.500 -6.300
+51694.00    -49.82  -6.713  -48.900 -6.400
+51695.00    -50.005 -6.561  -49.200 -6.600
+51696.00    -49.884 -6.457  -49.600 -6.700
+51697.00    -50.305 -6.564  -49.700 -6.800
+51698.00    -50.689 -6.793  -49.800 -6.800
+51699.00    -50.7   -6.902  -49.700 -6.800
+51700.00    -50.344 -6.805  -49.600 -6.800
+51701.00    -49.961 -6.645  -49.600 -6.600
+51702.00    -49.858 -6.578  -49.600 -6.400
+51703.00    -50.05  -6.599  -49.800 -6.300
+51704.00    -50.304 -6.626  -49.900 -6.200
+51705.00    -50.397 -6.629  -50.200 -6.000
+51706.00    -50.353 -6.624  -50.600 -6.000
+51707.00    -50.414 -6.581  -50.900 -6.100
+51708.00    -50.757 -6.452  -51.300 -6.200
+51709.00    -51.267 -6.293  -51.600 -6.300
+51710.00    -51.963 -6.236  -51.800 -6.500
+51711.00    -52.091 -6.379  -51.900 -6.700
+51712.00    -52.031 -6.599  -51.900 -6.700
+51713.00    -51.918 -6.712  -52.000 -6.800
+51714.00    -51.892 -6.675  -51.900 -6.700
+51715.00    -52.05  -6.612  -52.000 -6.600
+51716.00    -52.318 -6.615  -52.100 -6.400
+51717.00    -52.368 -6.578  -52.300 -6.200
+51718.00    -52.267 -6.453  -52.600 -6.000
+51719.00    -52.198 -6.262  -52.900 -5.900
+51720.00    -52.525 -6.186  -53.300 -5.800
+51721.00    -53.213 -6.273  -53.600 -5.800
+51722.00    -53.86  -6.351  -53.900 -5.900
+51723.00    -54.227 -6.282  -54.000 -6.000
+51724.00    -54.412 -6.22   -54.000 -6.200
+51725.00    -54.72  -6.243  -54.000 -6.400
+51726.00    -54.966 -6.397  -53.800 -6.500
+51727.00    -54.896 -6.493  -53.600 -6.600
+51728.00    -54.496 -6.45   -53.700 -6.700
+51729.00    -54.053 -6.399  -53.700 -6.700
+51730.00    -53.906 -6.453  -53.800 -6.600
+51731.00    -54.247 -6.583  -54.200 -6.500
+51732.00    -54.786 -6.527  -54.500 -6.400
+51733.00    -55.211 -6.382  -55.000 -6.400
+51734.00    -55.289 -6.317  -55.300 -6.300
+51735.00    -55.187 -6.389  -55.600 -6.300
+51736.00    -55.276 -6.48   -55.700 -6.400
+51737.00    -55.675 -6.494  -55.700 -6.500
+51738.00    -56.1   -6.514  -55.700 -6.600
+51739.00    -56.296 -6.606  -55.500 -6.700
+51740.00    -56.234 -6.749  -55.400 -6.700
+51741.00    -56.104 -6.79   -55.300 -6.800
+51742.00    -56.067 -6.666  -55.500 -6.700
+51743.00    -56.188 -6.49   -55.700 -6.500
+51744.00    -56.443 -6.405  -56.000 -6.400
+51745.00    -56.616 -6.395  -56.500 -6.300
+51746.00    -56.737 -6.364  -56.900 -6.100
+51747.00    -56.768 -6.262  -57.500 -6.000
+51748.00    -56.942 -6.187  -57.900 -6.000
+51749.00    -57.381 -6.224  -58.200 -6.000
+51750.00    -57.939 -6.319  -58.500 -6.100
+51751.00    -58.399 -6.373  -58.500 -6.300
+51752.00    -58.696 -6.403  -58.500 -6.500
+51753.00    -58.859 -6.486  -58.200 -6.600
+51754.00    -58.87  -6.593  -58.100 -6.700
+51755.00    -58.697 -6.594  -57.800 -6.700
+51756.00    -58.397 -6.476  -57.700 -6.800
+51757.00    -58.112 -6.405  -57.800 -6.800
+51758.00    -58.002 -6.492  -58.000 -6.600
+51759.00    -58.228 -6.61   -58.300 -6.400
+51760.00    -58.724 -6.528  -58.600 -6.300
+51761.00    -59.261 -6.281  -59.000 -6.200
+51762.00    -59.498 -6.134  -59.400 -6.200
+51763.00    -59.391 -6.259  -59.600 -6.200
+51764.00    -59.255 -6.536  -59.600 -6.400
+51765.00    -59.347 -6.728  -59.500 -6.600
+51766.00    -59.413 -6.725  -59.300 -6.800
+51767.00    -59.538 -6.702  -59.000 -6.900
+51768.00    -59.497 -6.712  -58.600 -7.000
+51769.00    -59.43  -6.715  -58.400 -6.900
+51770.00    -59.411 -6.628  -58.200 -6.800
+51771.00    -59.41  -6.458  -58.200 -6.600
+51772.00    -59.437 -6.297  -58.400 -6.400
+51773.00    -59.385 -6.235  -58.700 -6.100
+51774.00    -59.542 -6.225  -59.200 -6.000
+51775.00    -59.648 -6.231  -59.600 -5.900
+51776.00    -59.722 -6.224  -60.100 -5.900
+51777.00    -59.896 -6.218  -60.400 -6.000
+51778.00    -60.247 -6.239  -60.700 -6.100
+51779.00    -60.681 -6.304  -60.700 -6.200
+51780.00    -61.006 -6.416  -60.600 -6.300
+51781.00    -61.072 -6.543  -60.500 -6.400
+51782.00    -60.87  -6.584  -60.300 -6.500
+51783.00    -60.556 -6.441  -60.300 -6.400
+51784.00    -60.326 -6.159  -60.400 -6.200
+51785.00    -60.255 -5.94   -60.600 -6.000
+51786.00    -60.295 -5.939  -60.900 -5.800
+51787.00    -60.525 -5.991  -61.300 -5.700
+51788.00    -60.78  -5.998  -61.600 -5.600
+51789.00    -61.135 -5.832  -62.300 -5.500
+51790.00    -61.383 -5.698  -62.500 -5.700
+51791.00    -61.369 -5.808  -61.700 -5.900
+51792.00    -61.176 -6.107  -61.400 -6.200
+51793.00    -60.996 -6.344  -61.100 -6.500
+51794.00    -60.682 -6.379  -60.600 -6.600
+51795.00    -60.586 -6.27   -60.100 -6.700
+51796.00    -60.499 -6.197  -59.800 -6.700
+51797.00    -60.471 -6.213  -59.600 -6.600
+51798.00    -60.459 -6.227  -59.700 -6.400
+51799.00    -60.358 -6.125  -59.900 -6.100
+51800.00    -60.185 -5.898  -60.300 -5.900
+51801.00    -60.105 -5.689  -60.600 -5.700
+51802.00    -60.16  -5.586  -61.000 -5.600
+51803.00    -60.265 -5.648  -61.300 -5.600
+51804.00    -60.308 -5.775  -61.400 -5.600
+51805.00    -60.331 -5.843  -61.300 -5.700
+51806.00    -60.453 -5.826  -61.000 -5.700
+51807.00    -60.681 -5.794  -60.700 -5.800
+51808.00    -60.78  -5.845  -60.100 -5.800
+51809.00    -60.69  -5.89   -59.600 -5.800
+51810.00    -60.319 -5.852  -59.300 -5.700
+51811.00    -59.864 -5.649  -59.000 -5.700
+51812.00    -59.581 -5.324  -58.900 -5.500
+51813.00    -59.566 -5.044  -59.000 -5.300
+51814.00    -59.696 -4.961  -59.200 -5.100
+51815.00    -59.804 -5.055  -59.400 -5.000
+51816.00    -59.878 -5.13   -59.600 -4.900
+51817.00    -59.92  -5.13   -59.800 -4.900
+51818.00    -59.969 -5.101  -59.800 -5.000
+51819.00    -59.939 -5.189  -59.700 -5.100
+51820.00    -59.745 -5.387  -59.400 -5.200
+51821.00    -59.402 -5.525  -58.900 -5.400
+51822.00    -59.02  -5.434  -58.600 -5.500
+51823.00    -58.714 -5.28   -58.100 -5.500
+51824.00    -58.558 -5.181  -57.800 -5.400
+51825.00    -58.518 -5.207  -57.600 -5.300
+51826.00    -58.483 -5.261  -57.700 -5.200
+51827.00    -58.38  -5.183  -57.800 -5.000
+51828.00    -58.247 -4.908  -58.100 -4.800
+51829.00    -58.26  -4.55   -58.500 -4.600
+51830.00    -58.296 -4.314  -58.800 -4.500
+51831.00    -58.363 -4.345  -59.000 -4.400
+51832.00    -58.38  -4.573  -59.100 -4.400
+51833.00    -58.349 -4.784  -59.000 -4.400
+51834.00    -58.332 -4.817  -58.700 -4.500
+51835.00    -58.35  -4.69   -58.100 -4.500
+51836.00    -58.43  -4.52   -57.600 -4.600
+51837.00    -58.265 -4.41   -57.100 -4.500
+51838.00    -57.924 -4.329  -56.800 -4.500
+51839.00    -57.52  -4.219  -56.600 -4.400
+51840.00    -57.248 -4.066  -56.600 -4.200
+51841.00    -57.23  -3.925  -56.900 -4.100
+51842.00    -57.41  -3.869  -57.300 -4.000
+51843.00    -57.603 -3.911  -57.600 -3.900
+51844.00    -57.65  -3.985  -57.800 -3.900
+51845.00    -57.545 -4.029  -58.000 -3.900
+51846.00    -57.393 -4.061  -57.900 -4.000
+51847.00    -57.247 -4.138  -57.600 -4.100
+51848.00    -57.032 -4.24   -57.200 -4.200
+51849.00    -56.665 -4.258  -56.600 -4.500
+51850.00    -56.224 -4.134  -55.800 -4.300
+51851.00    -55.902 -3.954  -55.300 -4.200
+51852.00    -55.779 -3.855  -55.000 -4.100
+51853.00    -55.737 -3.867  -54.900 -4.000
+51854.00    -55.656 -3.893  -55.000 -4.000
+51855.00    -55.577 -3.811  -55.300 -4.000
+51856.00    -55.61  -3.571  -55.700 -4.000
+51857.00    -55.736 -3.226  -56.100 -3.900
+51858.00    -55.822 -2.921  -56.600 -3.800
+51859.00    -55.801 -2.825  -56.800 -3.700
+51860.00    -55.718 -2.987  -56.900 -3.500
+51861.00    -55.618 -3.254  -56.600 -3.400
+51862.00    -55.481 -3.38   -56.200 -3.300
+51863.00    -55.297 -3.254  -55.700 -3.200
+51864.00    -55.106 -2.984  -55.000 -3.200
+51865.00    -54.94  -2.766  -54.500 -3.100
+51866.00    -54.776 -2.695  -54.100 -3.100
+51867.00    -54.594 -2.727  -54.000 -3.200
+51868.00    -54.447 -2.78   -54.000 -3.100
+51869.00    -54.435 -2.817  -54.200 -3.100
+51870.00    -54.603 -2.842  -54.600 -3.000
+51871.00    -54.868 -2.85   -54.900 -2.900
+51872.00    -55.054 -2.83   -55.200 -2.800
+51873.00    -55.037 -2.798  -55.300 -2.700
+51874.00    -54.853 -2.81   -55.200 -2.700
+51875.00    -54.621 -2.9    -54.900 -2.700
+51876.00    -54.377 -3.003  -54.300 -2.700
+51877.00    -54.081 -3.001  -53.600 -2.700
+51878.00    -53.785 -2.868  -53.000 -2.700
+51879.00    -53.64  -2.711  -52.500 -2.700
+51880.00    -53.606 -2.57   -52.500 -2.800
+51881.00    -53.633 -2.597  -52.500 -2.700
+51882.00    -53.534 -2.617  -52.700 -2.700
+51883.00    -53.453 -2.579  -53.100 -2.600
+51884.00    -53.58  -2.488  -53.400 -2.500
+51885.00    -53.798 -2.36   -53.700 -2.400
+51886.00    -53.94  -2.098  -54.000 -2.200
+51887.00    -53.926 -1.843  -54.100 -2.200
+51888.00    -53.922 -1.782  -54.100 -2.100
+51889.00    -53.971 -1.964  -53.900 -2.000
+51890.00    -53.885 -2.184  -53.600 -2.000
+51891.00    -53.532 -2.197  -53.100 -2.000
+51892.00    -52.962 -1.961  -52.500 -1.900
+51893.00    -52.615 -1.742  -52.100 -1.900
+51894.00    -52.487 -1.678  -51.700 -1.800
+51895.00    -52.494 -1.737  -51.700 -1.800
+51896.00    -52.538 -1.801  -51.800 -1.800
+51897.00    -52.608 -1.834  -52.200 -1.900
+51898.00    -52.751 -1.87   -52.600 -2.000
+51899.00    -52.968 -1.954  -53.000 -2.100
+51900.00    -53.238 -1.956  -53.400 -2.300
+51901.00    -53.38  -1.922  -53.600 -2.300
+51902.00    -53.296 -1.938  -53.500 -2.300
+51903.00    -53.041 -2.062  -53.300 -2.300
+51904.00    -52.739 -2.219  -53.100 -2.300
+51905.00    -52.473 -2.267  -52.600 -2.300
+51906.00    -52.307 -2.175  -52.300 -2.100
+51907.00    -52.316 -2.062  -51.900 -2.000
+51908.00    -52.467 -2.049  -51.700 -2.000
+51909.00    -52.572 -2.112  -51.600 -1.900
+51910.00    -52.513 -2.159  -51.600 -2.000
+51911.00    -52.431 -2.184  -51.800 -2.100
+51912.00    -52.522 -2.242  -52.100 -2.300
+51913.00    -52.722 -2.278  -52.600 -2.400
+51914.00    -52.837 -2.14   -53.100 -1.900
+51915.00    -52.917 -1.821  -53.500 -1.800
+51916.00    -53.215 -1.575  -53.700 -1.800
+51917.00    -53.713 -1.647  -53.800 -1.800
+51918.00    -53.979 -1.955  -53.500 -2.000
+51919.00    -53.649 -2.18   -53.200 -2.200
+51920.00    -52.624 -2.182  -52.700 -2.400
+51921.00    -51.878 -2.036  -52.300 -2.600
+51922.00    -51.498 -1.953  -52.000 -2.400
+51923.00    -51.499 -1.918  -51.900 -2.300
+51924.00    -51.709 -1.818  -51.900 -2.100
+51925.00    -51.949 -1.677  -52.000 -1.900
+51926.00    -52.141 -1.643  -52.200 -1.900
+51927.00    -52.315 -1.77   -52.400 -1.900
+51928.00    -52.581 -1.948  -52.600 -2.100
+51929.00    -52.834 -2.065  -52.600 -2.200
+51930.00    -52.871 -2.149  -52.500 -2.300
+51931.00    -52.61  -2.279  -52.400 -2.400
+51932.00    -52.196 -2.422  -52.100 -2.400
+51933.00    -51.846 -2.457  -51.700 -2.300
+51934.00    -51.605 -2.312  -51.400 -2.100
+51935.00    -51.624 -2.188  -51.000 -2.100
+51936.00    -51.77  -2.174  -50.900 -2.100
+51937.00    -51.917 -2.262  -50.800 -2.100
+51938.00    -51.961 -2.358  -50.900 -2.300
+51939.00    -51.946 -2.44   -51.100 -2.400
+51940.00    -51.98  -2.569  -51.600 -2.700
+51941.00    -51.985 -2.765  -52.100 -3.000
+51942.00    -51.998 -2.769  -52.600 -3.200
+51943.00    -52.08  -2.567  -52.900 -3.400
+51944.00    -52.499 -2.361  -53.300 -3.500
+51945.00    -53.224 -2.435  -53.400 -3.500
+51946.00    -53.761 -2.795  -53.500 -3.500
+51947.00    -53.62  -3.153  -53.400 -3.300
+51948.00    -52.834 -3.243  -53.200 -3.100
+51949.00    -51.91  -3.188  -52.900 -3.000
+51950.00    -51.319 -3.125  -52.600 -2.600
+51951.00    -51.218 -3.057  -52.300 -2.500
+51952.00    -51.475 -2.877  -52.100 -2.400
+51953.00    -51.83  -2.633  -51.900 -2.500
+51954.00    -52.076 -2.547  -51.900 -2.600
+51955.00    -52.241 -2.717  -51.700 -2.700
+51956.00    -52.437 -3.066  -51.700 -3.000
+51957.00    -52.687 -3.34   -51.500 -3.300
+51958.00    -52.746 -3.47   -51.400 -3.400
+51959.00    -52.415 -3.529  -51.200 -3.500
+51960.00    -51.832 -3.558  -51.000 -3.500
+51961.00    -51.326 -3.5    -50.800 -3.500
+51962.00    -50.976 -3.335  -50.700 -3.400
+51963.00    -50.956 -3.147  -50.500 -3.200
+51964.00    -51.055 -3.074  -50.500 -3.200
+51965.00    -51.2   -3.15   -50.600 -3.200
+51966.00    -51.325 -3.303  -50.700 -3.300
+51967.00    -51.384 -3.467  -50.900 -3.500
+51968.00    -51.395 -3.63   -51.300 -3.800
+51969.00    -51.668 -3.826  -51.600 -4.100
+51970.00    -51.632 -3.9    -51.800 -4.400
+51971.00    -51.638 -3.87   -52.000 -4.700
+51972.00    -51.81  -3.842  -52.200 -4.800
+51973.00    -52.135 -3.975  -52.200 -4.900
+51974.00    -52.357 -4.273  -52.100 -4.800
+51975.00    -52.202 -4.545  -52.000 -4.500
+51976.00    -51.688 -4.63   -51.800 -4.300
+51977.00    -51.107 -4.587  -51.600 -4.100
+51978.00    -50.747 -4.572  -51.400 -3.900
+51979.00    -50.712 -4.59   -51.300 -3.800
+51980.00    -50.933 -4.513  -51.100 -3.800
+51981.00    -51.229 -4.33   -51.000 -3.900
+51982.00    -51.433 -4.233  -50.900 -4.200
+51983.00    -51.535 -4.391  -50.700 -4.500
+51984.00    -51.665 -4.724  -50.600 -4.800
+51985.00    -51.841 -4.996  -50.400 -5.100
+51986.00    -51.824 -5.08   -50.300 -5.200
+51987.00    -51.399 -5.031  -50.200 -5.300
+51988.00    -50.698 -4.944  -50.200 -5.100
+51989.00    -50.111 -4.828  -50.100 -5.000
+51990.00    -49.86  -4.696  -50.100 -4.800
+51991.00    -49.913 -4.489  -50.100 -4.600
+51992.00    -50.077 -4.351  -50.200 -4.500
+51993.00    -50.258 -4.368  -50.200 -4.500
+51994.00    -50.411 -4.541  -50.200 -4.600
+51995.00    -50.482 -4.793  -50.300 -4.800
+51996.00    -50.483 -5.02   -50.200 -5.000
+51997.00    -50.42  -5.139  -50.100 -5.300
+51998.00    -50.514 -5.19   -49.900 -5.600
+51999.00    -50.623 -5.212  -49.800 -5.700
+52000.00    -50.626 -5.274  -49.700 -5.800
+52001.00    -50.444 -5.404  -49.600 -5.800
+52002.00    -50.089 -5.549  -49.500 -5.800
+52003.00    -49.68  -5.609  -49.600 -5.600
+52004.00    -49.398 -5.542  -49.600 -5.400
+52005.00    -49.37  -5.44   -50.000 -5.500
+52006.00    -49.565 -5.436  -50.200 -5.500
+52007.00    -49.839 -5.535  -50.300 -5.500
+52008.00    -50.069 -5.6    -50.400 -5.500
+52009.00    -50.216 -5.543  -50.400 -5.500
+52010.00    -50.299 -5.457  -50.300 -5.600
+52011.00    -50.502 -5.557  -50.000 -5.600
+52012.00    -50.635 -5.74   -49.700 -5.600
+52013.00    -50.769 -5.911  -49.500 -5.600
+52014.00    -50.707 -5.95   -49.100 -5.700
+52015.00    -50.303 -5.886  -48.800 -5.800
+52016.00    -49.685 -5.814  -48.600 -5.800
+52017.00    -49.187 -5.762  -48.500 -5.800
+52018.00    -49.053 -5.683  -48.600 -5.900
+52019.00    -49.257 -5.545  -48.800 -5.900
+52020.00    -49.603 -5.39   -49.100 -5.900
+52021.00    -49.917 -5.317  -49.300 -5.900
+52022.00    -50.103 -5.408  -49.600 -5.900
+52023.00    -50.147 -5.648  -49.800 -6.000
+52024.00    -50.13  -5.922  -49.800 -6.000
+52025.00    -50.319 -6.143  -49.800 -6.000
+52026.00    -50.514 -6.178  -49.700 -6.100
+52027.00    -50.74  -6.152  -49.500 -6.100
+52028.00    -50.776 -6.154  -49.300 -6.200
+52029.00    -50.478 -6.198  -49.100 -6.100
+52030.00    -49.916 -6.223  -49.000 -6.200
+52031.00    -49.361 -6.169  -49.000 -6.200
+52032.00    -49.158 -6.06   -49.100 -6.100
+52033.00    -49.369 -5.928  -49.400 -6.200
+52034.00    -49.905 -5.879  -49.600 -6.200
+52035.00    -50.461 -5.93   -49.800 -5.900
+52036.00    -50.78  -5.998  -50.200 -5.900
+52037.00    -50.827 -6.001  -50.400 -5.900
+52038.00    -50.758 -5.956  -50.500 -6.100
+52039.00    -50.8   -5.985  -50.400 -6.200
+52040.00    -50.907 -6.067  -50.300 -6.300
+52041.00    -51.016 -6.164  -50.000 -6.400
+52042.00    -50.989 -6.207  -49.800 -6.400
+52043.00    -50.779 -6.214  -49.600 -6.400
+52044.00    -50.458 -6.243  -49.500 -6.300
+52045.00    -50.181 -6.288  -49.500 -6.300
+52046.00    -50.143 -6.271  -49.800 -6.100
+52047.00    -50.403 -6.182  -50.200 -6.000
+52048.00    -50.912 -6.028  -50.500 -5.900
+52049.00    -51.429 -5.874  -50.900 -5.900
+52050.00    -51.73  -5.801  -51.200 -5.900
+52051.00    -51.794 -5.87   -51.400 -6.000
+52052.00    -51.787 -6.069  -51.400 -6.100
+52053.00    -51.841 -6.275  -51.300 -6.300
+52054.00    -51.985 -6.385  -51.200 -6.400
+52055.00    -52.084 -6.357  -50.900 -6.500
+52056.00    -52.048 -6.267  -50.600 -6.500
+52057.00    -51.86  -6.203  -50.500 -6.400
+52058.00    -51.561 -6.177  -50.600 -6.300
+52059.00    -51.251 -6.15   -50.900 -6.200
+52060.00    -51.097 -6.125  -51.400 -6.000
+52061.00    -51.244 -6.036  -52.000 -5.900
+52062.00    -51.72  -5.948  -52.700 -5.800
+52063.00    -52.338 -5.884  -53.100 -5.700
+52064.00    -52.808 -5.848  -52.600 -5.600
+52065.00    -52.956 -5.839  -52.500 -5.700
+52066.00    -52.868 -5.872  -52.400 -5.800
+52067.00    -52.701 -5.941  -52.200 -6.100
+52068.00    -52.706 -6.044  -51.900 -6.400
+52069.00    -52.778 -6.101  -51.800 -6.600
+52070.00    -52.859 -6.102  -51.800 -6.700
+52071.00    -52.955 -6.113  -51.900 -6.800
+52072.00    -53.041 -6.179  -52.300 -6.700
+52073.00    -53.044 -6.245  -52.700 -6.400
+52074.00    -53.123 -6.189  -53.300 -6.200
+52075.00    -53.318 -6.073  -53.700 -5.900
+52076.00    -53.895 -5.936  -54.300 -5.700
+52077.00    -54.637 -5.811  -54.600 -5.500
+52078.00    -55.15  -5.682  -54.800 -5.400
+52079.00    -55.314 -5.593  -54.900 -5.500
+52080.00    -55.352 -5.648  -54.900 -5.600
+52081.00    -55.457 -5.813  -54.800 -5.700
+52082.00    -55.519 -6.047  -54.500 -5.800
+52083.00    -55.387 -6.118  -54.400 -6.000
+52084.00    -55.131 -6.019  -54.300 -6.100
+52085.00    -54.975 -5.893  -54.300 -6.100
+52086.00    -55.024 -5.851  -54.600 -6.100
+52087.00    -55.177 -5.87   -54.900 -6.000
+52088.00    -55.395 -5.88   -55.500 -6.000
+52089.00    -55.492 -5.84   -56.000 -5.900
+52090.00    -55.7   -5.779  -56.400 -5.800
+52091.00    -56.098 -5.717  -56.600 -5.700
+52092.00    -56.574 -5.664  -56.600 -5.700
+52093.00    -56.892 -5.662  -56.300 -5.700
+52094.00    -56.929 -5.763  -56.000 -5.800
+52095.00    -56.882 -5.943  -56.900 -6.000
+52096.00    -56.746 -6.098  -57.200 -6.100
+52097.00    -56.724 -6.097  -57.300 -6.100
+52098.00    -56.847 -5.961  -57.300 -6.000
+52099.00    -57.119 -5.844  -57.300 -6.000
+52100.00    -57.433 -5.847  -57.300 -6.000
+52101.00    -57.574 -5.891  -57.300 -5.900
+52102.00    -57.562 -5.843  -57.600 -5.900
+52103.00    -57.597 -5.707  -58.100 -5.800
+52104.00    -58.082 -5.628  -58.500 -5.800
+52105.00    -58.901 -5.654  -59.000 -5.900
+52106.00    -59.563 -5.671  -59.300 -5.800
+52107.00    -59.795 -5.608  -59.400 -5.700
+52108.00    -59.785 -5.582  -59.300 -5.600
+52109.00    -59.727 -5.746  -58.700 -5.500
+52110.00    -59.684 -5.995  -58.100 -5.400
+52111.00    -59.436 -6.113  -57.700 -5.400
+52112.00    -59.081 -6.02   -57.500 -5.500
+52113.00    -58.929 -5.875  -57.800 -5.500
+52114.00    -59.145 -5.833  -58.400 -5.700
+52115.00    -59.594 -5.858  -59.500 -5.900
+52116.00    -60.059 -5.815  -60.200 -6.000
+52117.00    -60.239 -5.745  -60.800 -6.100
+52118.00    -60.241 -5.735  -61.100 -6.100
+52119.00    -60.276 -5.819  -61.200 -6.000
+52120.00    -60.493 -5.916  -61.000 -6.100
+52121.00    -60.798 -5.972  -60.700 -6.100
+52122.00    -60.947 -6.043  -60.300 -6.000
+52123.00    -60.854 -6.157  -60.000 -6.100
+52124.00    -60.604 -6.249  -59.900 -6.000
+52125.00    -60.455 -6.162  -59.700 -5.900
+52126.00    -60.486 -5.89   -59.700 -5.700
+52127.00    -60.672 -5.623  -59.900 -5.700
+52128.00    -60.888 -5.527  -60.000 -5.600
+52129.00    -60.949 -5.555  -60.300 -5.600
+52130.00    -60.873 -5.546  -60.600 -5.600
+52131.00    -60.766 -5.456  -60.900 -5.700
+52132.00    -61.056 -5.438  -61.300 -5.700
+52133.00    -61.739 -5.583  -61.600 -5.800
+52134.00    -62.391 -5.769  -61.900 -5.800
+52135.00    -62.647 -5.835  -62.000 -5.900
+52136.00    -62.552 -5.807  -62.000 -5.900
+52137.00    -62.292 -5.825  -62.000 -5.900
+52138.00    -62.068 -5.896  -61.800 -5.800
+52139.00    -61.794 -5.859  -61.700 -5.700
+52140.00    -61.555 -5.665  -61.600 -5.700
+52141.00    -61.531 -5.49   -61.700 -5.600
+52142.00    -61.781 -5.49   -62.000 -5.500
+52143.00    -62.198 -5.571  -62.300 -5.500
+52144.00    -62.59  -5.547  -62.800 -5.500
+52145.00    -62.832 -5.426  -63.200 -5.500
+52146.00    -62.813 -5.43   -63.600 -5.600
+52147.00    -62.64  -5.661  -63.700 -5.700
+52148.00    -62.562 -5.953  -63.600 -5.800
+52149.00    -62.687 -6.089  -63.300 -5.900
+52150.00    -62.825 -6.058  -62.800 -6.000
+52151.00    -62.747 -5.994  -62.300 -5.900
+52152.00    -62.479 -5.946  -61.900 -5.800
+52153.00    -62.233 -5.817  -61.500 -5.800
+52154.00    -62.123 -5.551  -61.300 -5.500
+52155.00    -62.097 -5.258  -61.700 -4.700
+52156.00    -62.065 -5.105  -61.800 -4.900
+52157.00    -61.974 -5.114  -61.800 -5.100
+52158.00    -61.811 -5.156  -61.700 -5.100
+52159.00    -61.701 -5.158  -61.500 -5.000
+52160.00    -61.821 -5.181  -61.400 -4.900
+52161.00    -62.223 -5.312  -61.500 -5.000
+52162.00    -62.691 -5.504  -61.600 -5.200
+52163.00    -62.921 -5.625  -61.800 -5.400
+52164.00    -62.802 -5.617  -61.900 -5.600
+52165.00    -62.508 -5.557  -61.800 -5.700
+52166.00    -62.083 -5.391  -61.700 -5.700
+52167.00    -61.758 -5.1    -61.600 -5.500
+52168.00    -61.672 -4.738  -61.700 -5.400
+52169.00    -61.835 -4.519  -61.700 -5.200
+52170.00    -62.089 -4.59   -62.000 -5.100
+52171.00    -62.278 -4.809  -62.300 -5.000
+52172.00    -62.399 -4.899  -62.500 -4.900
+52173.00    -62.485 -4.809  -62.700 -4.900
+52174.00    -62.467 -4.784  -62.800 -5.000
+52175.00    -62.293 -5.012  -62.700 -5.000
+52176.00    -62.084 -5.354  -62.500 -5.200
+52177.00    -61.995 -5.511  -62.100 -5.400
+52178.00    -61.97  -5.388  -61.800 -5.500
+52179.00    -61.826 -5.158  -61.300 -5.600
+52180.00    -61.54  -5.002  -60.900 -5.400
+52181.00    -61.327 -4.868  -60.500 -5.300
+52182.00    -61.159 -4.73   -60.200 -5.100
+52183.00    -61.02  -4.515  -60.200 -4.800
+52184.00    -60.852 -4.319  -60.200 -4.600
+52185.00    -60.708 -4.243  -60.400 -4.400
+52186.00    -60.638 -4.286  -60.300 -4.300
+52187.00    -60.609 -4.376  -60.200 -4.300
+52188.00    -60.594 -4.416  -60.100 -4.300
+52189.00    -60.665 -4.487  -60.100 -4.400
+52190.00    -60.806 -4.557  -60.200 -4.500
+52191.00    -60.881 -4.608  -60.300 -4.500
+52192.00    -60.725 -4.602  -60.500 -4.400
+52193.00    -60.301 -4.494  -60.700 -4.100
+52194.00    -59.763 -4.243  -60.700 -3.700
+52195.00    -59.434 -3.774  -60.800 -3.200
+52196.00    -59.428 -3.358  -60.700 -2.900
+52197.00    -59.739 -3.137  -60.700 -2.900
+52198.00    -60.061 -3.245  -60.500 -3.100
+52199.00    -60.138 -3.553  -60.300 -3.600
+52200.00    -59.996 -3.771  -60.100 -4.100
+52201.00    -59.832 -3.767  -59.800 -4.500
+52202.00    -59.724 -3.718  -59.700 -4.900
+52203.00    -59.583 -3.803  -59.400 -5.000
+52204.00    -59.362 -3.998  -59.200 -4.700
+52205.00    -59.119 -4.072  -58.900 -4.400
+52206.00    -58.891 -3.907  -58.500 -4.000
+52207.00    -58.646 -3.644  -57.900 -3.700
+52208.00    -58.399 -3.492  -57.300 -3.400
+52209.00    -58.233 -3.479  -56.900 -3.300
+52210.00    -58.165 -3.457  -56.600 -3.200
+52211.00    -58.113 -3.292  -56.600 -3.200
+52212.00    -58.035 -3.013  -56.900 -3.100
+52213.00    -57.997 -2.778  -57.500 -3.000
+52214.00    -58.039 -2.727  -58.000 -2.800
+52215.00    -58.071 -2.858  -58.300 -2.900
+52216.00    -58.075 -2.983  -58.300 -2.900
+52217.00    -57.894 -3.079  -58.000 -3.000
+52218.00    -57.726 -3.062  -57.500 -3.100
+52219.00    -57.586 -3.002  -57.100 -3.100
+52220.00    -57.366 -2.957  -56.700 -3.000
+52221.00    -56.978 -2.896  -56.700 -2.700
+52222.00    -56.506 -2.745  -56.600 -2.300
+52223.00    -56.183 -2.422  -56.400 -1.800
+52224.00    -56.234 -2.145  -56.400 -1.500
+52225.00    -56.621 -1.998  -56.400 -1.400
+52226.00    -57.063 -2.076  -56.600 -1.700
+52227.00    -57.25  -2.311  -56.800 -2.400
+52228.00    -57.138 -2.507  -57.000 -2.600
+52229.00    -56.888 -2.54   -56.900 -2.600
+52230.00    -56.701 -2.458  -56.700 -2.700
+52231.00    -56.544 -2.428  -56.500 -2.500
+52232.00    -56.355 -2.463  -56.200 -2.500
+52233.00    -56.088 -2.443  -55.900 -2.400
+52234.00    -55.798 -2.295  -55.700 -2.300
+52235.00    -55.576 -2.119  -55.400 -2.200
+52236.00    -55.477 -2.063  -55.200 -1.800
+52237.00    -55.48  -2.121  -55.100 -1.600
+52238.00    -55.538 -2.153  -55.200 -1.500
+52239.00    -55.601 -1.994  -55.300 -1.300
+52240.00    -55.682 -1.653  -55.500 -1.300
+52241.00    -55.807 -1.298  -55.600 -1.300
+52242.00    -55.944 -1.132  -55.700 -1.400
+52243.00    -56.001 -1.236  -55.600 -1.600
+52244.00    -55.854 -1.497  -55.400 -1.800
+52245.00    -55.603 -1.723  -55.000 -1.800
+52246.00    -55.272 -1.766  -54.600 -1.900
+52247.00    -54.934 -1.659  -54.200 -1.800
+52248.00    -54.617 -1.531  -53.800 -1.600
+52249.00    -54.321 -1.461  -53.600 -1.300
+52250.00    -54.076 -1.43   -53.700 -1.100
+52251.00    -53.894 -1.389  -54.000 -0.900
+52252.00    -54.018 -1.326  -54.400 -0.700
+52253.00    -54.377 -1.286  -55.000 -0.500
+52254.00    -54.824 -1.304  -55.500 -0.500
+52255.00    -55.22  -1.361  -55.900 -0.600
+52256.00    -55.247 -1.421  -56.100 -0.800
+52257.00    -55.031 -1.435  -55.900 -1.000
+52258.00    -54.743 -1.424  -55.500 -1.200
+52259.00    -54.524 -1.446  -54.800 -1.400
+52260.00    -54.37  -1.487  -54.200 -1.600
+52261.00    -54.21  -1.48   -53.700 -1.600
+52262.00    -54.05  -1.401  -53.400 -1.400
+52263.00    -53.984 -1.336  -53.200 -1.200
+52264.00    -54.043 -1.379  -53.500 -0.900
+52265.00    -53.924 -1.588  -53.900 -0.600
+52266.00    -53.942 -1.631  -54.200 -0.500
+52267.00    -53.955 -1.498  -54.800 -0.400
+52268.00    -54.085 -1.216  -55.100 -0.600
+52269.00    -54.327 -0.902  -55.400 -0.700
+52270.00    -54.556 -0.691  -55.500 -0.800
+52271.00    -54.667 -0.692  -55.500 -1.000
+52272.00    -54.728 -0.941  -55.200 -1.000
+52273.00    -54.662 -1.294  -54.800 -1.000
+52274.00    -54.357 -1.518  -54.300 -0.900
+52275.00    -53.821 -1.481  -52.800 -1.100
+52276.00    -53.265 -1.268  -52.600 -1.200
+52277.00    -52.938 -1.076  -52.600 -1.100
+52278.00    -52.906 -1.021  -52.800 -1.100
+52279.00    -53.091 -1.064  -53.100 -1.000
+52280.00    -53.33  -1.116  -53.500 -0.900
+52281.00    -53.622 -1.132  -53.800 -0.900
+52282.00    -53.953 -1.12   -54.100 -0.800
+52283.00    -54.21  -1.105  -54.100 -0.800
+52284.00    -54.304 -1.081  -54.000 -0.900
+52285.00    -54.136 -1.087  -53.700 -1.000
+52286.00    -53.756 -1.161  -53.500 -1.200
+52287.00    -53.448 -1.31   -53.300 -1.400
+52288.00    -53.288 -1.459  -53.200 -1.500
+52289.00    -53.25  -1.511  -53.200 -1.600
+52290.00    -53.278 -1.462  -53.200 -1.600
+52291.00    -53.374 -1.419  -53.300 -1.600
+52292.00    -53.504 -1.476  -53.400 -1.500
+52293.00    -53.538 -1.62   -53.300 -1.400
+52294.00    -53.402 -1.665  -53.200 -1.300
+52295.00    -53.27  -1.611  -53.000 -1.200
+52296.00    -53.356 -1.52   -53.000 -1.200
+52297.00    -53.632 -1.427  -53.000 -1.200
+52298.00    -53.901 -1.315  -53.400 -1.200
+52299.00    -54.125 -1.241  -53.800 -1.200
+52300.00    -54.387 -1.352  -54.100 -1.400
+52301.00    -54.671 -1.735  -54.200 -1.700
+52302.00    -54.583 -2.157  -54.100 -1.900
+52303.00    -53.935 -2.303  -54.000 -2.200
+52304.00    -53.054 -2.1    -53.400 -2.100
+52305.00    -52.497 -1.785  -52.900 -1.900
+52306.00    -52.497 -1.619  -52.400 -1.600
+52307.00    -52.845 -1.638  -52.300 -1.500
+52308.00    -53.214 -1.696  -52.500 -1.400
+52309.00    -53.473 -1.725  -52.800 -1.400
+52310.00    -53.665 -1.751  -54.000 -1.400
+52311.00    -53.843 -1.804  -54.100 -1.500
+52312.00    -53.959 -1.858  -53.900 -1.600
+52313.00    -53.893 -1.909  -53.400 -1.800
+52314.00    -53.498 -2.001  -53.000 -1.900
+52315.00    -53.099 -2.173  -52.600 -2.000
+52316.00    -52.804 -2.328  -52.500 -2.100
+52317.00    -52.715 -2.341  -52.500 -2.100
+52318.00    -52.787 -2.211  -52.600 -2.100
+52319.00    -52.928 -2.084  -52.700 -2.000
+52320.00    -53.037 -2.09   -52.800 -2.000
+52321.00    -53.009 -2.215  -52.700 -2.000
+52322.00    -52.796 -2.298  -52.500 -2.100
+52323.00    -52.597 -2.35   -52.300 -2.100
+52324.00    -52.626 -2.458  -52.100 -2.100
+52325.00    -52.833 -2.622  -52.100 -2.200
+52326.00    -52.99  -2.701  -52.200 -2.400
+52327.00    -53.081 -2.644  -52.500 -2.700
+52328.00    -53.26  -2.679  -52.800 -3.000
+52329.00    -53.66  -2.97   -53.100 -3.300
+52330.00    -53.763 -3.449  -53.200 -3.600
+52331.00    -53.205 -3.74   -53.100 -3.700
+52332.00    -52.247 -3.628  -52.700 -3.700
+52333.00    -51.558 -3.297  -52.100 -3.400
+52334.00    -51.514 -3.071  -51.400 -3.000
+52335.00    -51.996 -3.026  -51.300 -2.700
+52336.00    -52.427 -3.06   -51.200 -2.400
+52337.00    -52.637 -3.081  -51.400 -2.400
+52338.00    -52.661 -3.165  -51.700 -2.500
+52339.00    -52.662 -3.359  -51.900 -2.800
+52340.00    -52.744 -3.56   -52.100 -3.100
+52341.00    -52.818 -3.656  -52.100 -3.500
+52342.00    -52.614 -3.662  -51.900 -3.700
+52343.00    -52.203 -3.678  -51.600 -3.800
+52344.00    -51.725 -3.677  -51.400 -3.700
+52345.00    -51.455 -3.552  -51.300 -3.500
+52346.00    -51.475 -3.291  -51.200 -3.300
+52347.00    -51.626 -3.058  -51.300 -3.200
+52348.00    -51.748 -3.018  -51.300 -3.100
+52349.00    -51.739 -3.159  -51.300 -3.000
+52350.00    -51.598 -3.339  -51.200 -3.100
+52351.00    -51.451 -3.494  -51.000 -3.200
+52352.00    -51.449 -3.689  -50.900 -3.300
+52353.00    -51.564 -3.948  -50.800 -3.500
+52354.00    -51.599 -4.144  -50.800 -3.700
+52355.00    -51.492 -4.169  -50.900 -4.000
+52356.00    -51.432 -4.139  -51.000 -4.400
+52357.00    -51.53  -4.282  -51.100 -4.700
+52358.00    -51.554 -4.608  -51.000 -4.900
+52359.00    -51.213 -4.844  -50.800 -4.900
+52360.00    -50.631 -4.779  -50.500 -4.800
+52361.00    -50.267 -4.51   -50.200 -4.500
+52362.00    -50.401 -4.327  -49.900 -4.300
+52363.00    -50.867 -4.318  -49.700 -4.100
+52364.00    -51.291 -4.347  -49.600 -4.100
+52365.00    -51.432 -4.34   -49.700 -4.200
+52366.00    -51.299 -4.416  -49.900 -4.400
+52367.00    -51.097 -4.663  -50.100 -4.700
+52368.00    -51.094 -4.944  -50.500 -4.900
+52369.00    -51.221 -5.06   -50.700 -5.000
+52370.00    -51.255 -4.972  -50.900 -5.000
+52371.00    -50.958 -4.815  -50.800 -4.900
+52372.00    -50.455 -4.67   -50.700 -4.700
+52373.00    -50.118 -4.481  -50.600 -4.400
+52374.00    -50.146 -4.205  -50.500 -4.100
+52375.00    -50.402 -3.956  -50.500 -4.000
+52376.00    -50.645 -3.903  -50.500 -3.900
+52377.00    -50.849 -4.084  -50.400 -4.000
+52378.00    -50.826 -4.365  -50.200 -4.200
+52379.00    -50.741 -4.611  -50.100 -4.400
+52380.00    -50.701 -4.802  -50.100 -4.600
+52381.00    -50.764 -4.957  -50.200 -4.800
+52382.00    -50.844 -5.11   -50.300 -5.000
+52383.00    -50.793 -5.191  -50.400 -5.100
+52384.00    -50.58  -5.218  -50.300 -5.200
+52385.00    -50.295 -5.273  -50.200 -5.300
+52386.00    -50.01  -5.37   -50.000 -5.300
+52387.00    -49.772 -5.391  -49.800 -5.300
+52388.00    -49.7   -5.246  -49.700 -5.200
+52389.00    -49.933 -5.033  -49.800 -5.100
+52390.00    -50.438 -4.937  -50.000 -4.900
+52391.00    -51.044 -4.99   -50.200 -4.800
+52392.00    -51.38  -5.04   -50.400 -4.800
+52393.00    -51.411 -5.008  -50.600 -4.900
+52394.00    -51.188 -5.017  -50.700 -5.100
+52395.00    -50.882 -5.204  -50.900 -5.400
+52396.00    -50.717 -5.482  -51.000 -5.600
+52397.00    -50.781 -5.625  -50.800 -5.600
+52398.00    -50.9   -5.519  -50.500 -5.500
+52399.00    -50.784 -5.34   -50.100 -5.300
+52400.00    -50.47  -5.202  -49.900 -4.900
+52401.00    -50.271 -5.088  -49.900 -4.500
+52402.00    -50.404 -4.919  -50.200 -4.400
+52403.00    -50.79  -4.708  -50.600 -4.400
+52404.00    -51.167 -4.598  -51.000 -4.600
+52405.00    -51.344 -4.696  -51.100 -4.800
+52406.00    -51.418 -4.959  -51.000 -5.000
+52407.00    -51.369 -5.248  -50.900 -5.200
+52408.00    -51.284 -5.441  -50.800 -5.300
+52409.00    -51.291 -5.527  -50.900 -5.400
+52410.00    -51.445 -5.558  -51.000 -5.500
+52411.00    -51.589 -5.604  -51.100 -5.500
+52412.00    -51.504 -5.67   -50.900 -5.500
+52413.00    -51.131 -5.711  -50.700 -5.500
+52414.00    -50.66  -5.665  -50.500 -5.400
+52415.00    -50.405 -5.503  -50.500 -5.300
+52416.00    -50.596 -5.264  -50.900 -5.300
+52417.00    -51.214 -5.064  -51.400 -5.200
+52418.00    -51.985 -5.008  -51.900 -5.100
+52419.00    -52.57  -5.081  -52.100 -4.900
+52420.00    -52.794 -5.152  -52.100 -4.800
+52421.00    -52.706 -5.138  -52.000 -4.800
+52422.00    -52.458 -5.122  -51.800 -4.900
+52423.00    -52.198 -5.234  -51.700 -5.000
+52424.00    -52.057 -5.455  -51.700 -5.300
+52425.00    -52.101 -5.618  -51.900 -5.600
+52426.00    -52.252 -5.62   -51.800 -5.700
+52427.00    -52.354 -5.534  -51.700 -5.600
+52428.00    -52.36  -5.478  -51.700 -5.500
+52429.00    -52.404 -5.446  -51.800 -5.300
+52430.00    -52.639 -5.335  -52.300 -5.100
+52431.00    -53.061 -5.113  -52.900 -5.000
+52432.00    -53.538 -4.888  -53.500 -5.000
+52433.00    -53.804 -4.835  -53.800 -5.100
+52434.00    -54.05  -4.977  -53.800 -5.200
+52435.00    -54.114 -5.253  -53.800 -5.300
+52436.00    -54.054 -5.506  -53.800 -5.400
+52437.00    -54.008 -5.62   -53.800 -5.500
+52438.00    -54.077 -5.607  -53.900 -5.500
+52439.00    -54.204 -5.568  -53.900 -5.500
+52440.00    -54.182 -5.589  -53.700 -5.500
+52441.00    -53.994 -5.603  -53.400 -5.500
+52442.00    -53.706 -5.541  -53.300 -5.500
+52443.00    -53.574 -5.371  -53.400 -5.400
+52444.00    -53.81  -5.149  -53.900 -5.200
+52445.00    -54.417 -4.97   -54.500 -5.000
+52446.00    -55.163 -4.896  -55.100 -4.800
+52447.00    -55.677 -4.937  -55.500 -4.700
+52448.00    -55.889 -4.997  -55.500 -4.700
+52449.00    -55.778 -5.051  -55.300 -4.800
+52450.00    -55.532 -5.117  -55.000 -5.000
+52451.00    -55.34  -5.242  -54.800 -5.200
+52452.00    -55.293 -5.406  -54.700 -5.500
+52453.00    -55.398 -5.518  -54.900 -5.600
+52454.00    -55.673 -5.563  -55.200 -5.700
+52455.00    -55.957 -5.524  -55.700 -5.600
+52456.00    -56.223 -5.51   -56.100 -5.400
+52457.00    -56.422 -5.49   -56.300 -5.300
+52458.00    -56.604 -5.365  -56.400 -5.300
+52459.00    -56.916 -5.12   -56.500 -5.300
+52460.00    -57.44  -4.873  -57.000 -5.300
+52461.00    -58.055 -4.771  -57.400 -5.300
+52462.00    -58.514 -4.874  -57.800 -5.300
+52463.00    -58.673 -5.132  -58.200 -5.300
+52464.00    -58.599 -5.431  -58.400 -5.300
+52465.00    -58.451 -5.639  -58.300 -5.300
+52466.00    -58.317 -5.675  -58.000 -5.400
+52467.00    -58.212 -5.573  -57.800 -5.400
+52468.00    -58.224 -5.447  -57.600 -5.500
+52469.00    -58.244 -5.392  -57.700 -5.500
+52470.00    -58.353 -5.388  -57.900 -5.400
+52471.00    -58.531 -5.362  -58.200 -5.300
+52472.00    -58.784 -5.277  -58.500 -5.300
+52473.00    -59.135 -5.159  -58.800 -5.200
+52474.00    -59.57  -5.056  -59.100 -5.200
+52475.00    -59.978 -5.05   -59.400 -5.100
+52476.00    -60.203 -5.063  -59.700 -5.100
+52477.00    -60.157 -5.15   -59.900 -5.100
+52478.00    -59.923 -5.304  -59.800 -5.100
+52479.00    -59.694 -5.477  -59.600 -5.200
+52480.00    -59.614 -5.58   -59.200 -5.300
+52481.00    -59.699 -5.544  -58.900 -5.400
+52482.00    -59.932 -5.396  -58.700 -5.400
+52483.00    -60.218 -5.262  -58.700 -5.300
+52484.00    -60.495 -5.219  -59.000 -5.200
+52485.00    -60.621 -5.202  -59.500 -5.100
+52486.00    -60.603 -5.096  -60.200 -5.100
+52487.00    -60.708 -4.915  -60.800 -5.000
+52488.00    -61.195 -4.807  -61.400 -5.000
+52489.00    -61.936 -4.876  -61.600 -5.100
+52490.00    -62.484 -5.071  -61.700 -5.200
+52491.00    -62.551 -5.284  -61.500 -5.300
+52492.00    -62.268 -5.471  -61.200 -5.400
+52493.00    -61.992 -5.63   -60.900 -5.400
+52494.00    -61.705 -5.649  -60.700 -5.300
+52495.00    -61.472 -5.498  -60.600 -5.300
+52496.00    -61.376 -5.272  -60.900 -5.200
+52497.00    -61.547 -5.161  -61.300 -5.200
+52498.00    -61.955 -5.238  -61.800 -5.200
+52499.00    -62.386 -5.385  -62.300 -5.200
+52500.00    -62.664 -5.44   -62.600 -5.200
+52501.00    -62.794 -5.369  -62.800 -5.200
+52502.00    -62.898 -5.259  -62.700 -5.300
+52503.00    -63.109 -5.181  -62.600 -5.300
+52504.00    -63.271 -5.171  -62.600 -5.300
+52505.00    -63.295 -5.225  -62.500 -5.300
+52506.00    -63.114 -5.344  -62.500 -5.300
+52507.00    -62.8   -5.503  -62.500 -5.400
+52508.00    -62.594 -5.53   -62.500 -5.300
+52509.00    -62.554 -5.346  -62.600 -5.300
+52510.00    -62.624 -5.006  -62.500 -5.100
+52511.00    -62.76  -4.726  -62.400 -5.000
+52512.00    -62.855 -4.64   -62.400 -4.800
+52513.00    -62.799 -4.68   -62.300 -4.700
+52514.00    -62.61  -4.689  -62.400 -4.600
+52515.00    -62.614 -4.648  -62.500 -4.600
+52516.00    -63.009 -4.718  -62.700 -4.700
+52517.00    -63.71  -4.964  -62.800 -4.800
+52518.00    -64.185 -5.236  -62.900 -4.900
+52519.00    -64.067 -5.341  -62.800 -5.100
+52520.00    -63.531 -5.274  -62.700 -5.100
+52521.00    -63.001 -5.163  -62.600 -5.100
+52522.00    -62.677 -5.043  -62.500 -5.000
+52523.00    -62.523 -4.839  -62.600 -4.900
+52524.00    -62.544 -4.586  -62.800 -4.900
+52525.00    -62.801 -4.485  -62.900 -4.900
+52526.00    -63.21  -4.652  -63.000 -4.900
+52527.00    -63.549 -4.924  -63.100 -4.900
+52528.00    -63.692 -5.056  -63.300 -4.900
+52529.00    -63.689 -4.974  -63.400 -4.900
+52530.00    -63.661 -4.873  -63.400 -4.900
+52531.00    -63.678 -4.891  -63.300 -4.900
+52532.00    -63.738 -4.972  -63.300 -4.900
+52533.00    -63.764 -5.008  -63.100 -4.900
+52534.00    -63.641 -4.999  -62.900 -4.800
+52535.00    -63.353 -4.995  -62.800 -4.800
+52536.00    -63.037 -4.949  -62.600 -4.700
+52537.00    -62.845 -4.74   -62.500 -4.600
+52538.00    -62.688 -4.357  -62.400 -4.400
+52539.00    -62.654 -4.03   -62.200 -4.300
+52540.00    -62.56  -3.932  -62.100 -4.100
+52541.00    -62.376 -4.04   -61.900 -4.100
+52542.00    -62.154 -4.177  -61.700 -4.100
+52543.00    -62.067 -4.266  -61.600 -4.200
+52544.00    -62.297 -4.404  -61.600 -4.400
+52545.00    -62.746 -4.656  -61.700 -4.500
+52546.00    -63.007 -4.88   -61.800 -4.600
+52547.00    -62.773 -4.869  -61.800 -4.600
+52548.00    -62.175 -4.617  -61.800 -4.500
+52549.00    -61.543 -4.279  -61.500 -4.200
+52550.00    -61.224 -4.028  -61.200 -3.800
+52551.00    -61.158 -3.786  -61.000 -3.600
+52552.00    -61.357 -3.547  -60.900 -3.600
+52553.00    -61.641 -3.467  -61.000 -3.800
+52554.00    -61.884 -3.665  -61.200 -4.000
+52555.00    -61.896 -3.967  -61.500 -4.100
+52556.00    -61.714 -4.071  -61.600 -4.000
+52557.00    -61.543 -3.931  -61.700 -3.800
+52558.00    -61.483 -3.811  -61.600 -3.700
+52559.00    -61.469 -3.917  -61.400 -3.700
+52560.00    -61.444 -4.13   -61.200 -3.700
+52561.00    -61.408 -4.193  -61.100 -3.800
+52562.00    -61.316 -4.051  -60.900 -3.900
+52563.00    -61.189 -3.878  -60.900 -3.900
+52564.00    -60.879 -3.75   -60.800 -3.700
+52565.00    -60.641 -3.602  -60.700 -3.500
+52566.00    -60.525 -3.349  -60.600 -3.300
+52567.00    -60.432 -3.074  -60.500 -3.100
+52568.00    -60.281 -2.956  -60.300 -3.100
+52569.00    -60.108 -3.046  -60.100 -3.100
+52570.00    -59.977 -3.223  -60.000 -3.100
+52571.00    -59.916 -3.365  -59.900 -3.200
+52572.00    -59.933 -3.478  -59.900 -3.400
+52573.00    -60.121 -3.625  -59.800 -3.500
+52574.00    -60.069 -3.722  -59.600 -3.500
+52575.00    -59.781 -3.67   -59.400 -3.500
+52576.00    -59.302 -3.449  -59.000 -3.300
+52577.00    -58.829 -3.171  -58.600 -3.100
+52578.00    -58.526 -2.923  -58.200 -2.800
+52579.00    -58.468 -2.682  -58.100 -2.600
+52580.00    -58.713 -2.44   -58.200 -2.500
+52581.00    -59.078 -2.33   -58.500 -2.400
+52582.00    -59.338 -2.461  -58.700 -2.500
+52583.00    -59.264 -2.7    -58.900 -2.500
+52584.00    -58.934 -2.773  -58.800 -2.400
+52585.00    -58.632 -2.613  -58.500 -2.400
+52586.00    -58.494 -2.47   -58.100 -2.400
+52587.00    -58.406 -2.575  -57.700 -2.400
+52588.00    -58.265 -2.822  -57.500 -2.400
+52589.00    -58.127 -2.898  -57.400 -2.500
+52590.00    -58.043 -2.689  -57.500 -2.500
+52591.00    -58.046 -2.389  -57.600 -2.500
+52592.00    -57.926 -2.198  -57.900 -2.400
+52593.00    -57.843 -2.109  -58.000 -2.200
+52594.00    -57.837 -1.975  -58.000 -2.000
+52595.00    -57.825 -1.757  -57.800 -1.700
+52596.00    -57.751 -1.586  -57.500 -1.600
+52597.00    -57.685 -1.59   -57.200 -1.600
+52598.00    -57.693 -1.754  -57.000 -1.700
+52599.00    -57.695 -1.957  -56.800 -1.900
+52600.00    -57.564 -2.096  -56.700 -2.100
+52601.00    -57.27  -2.145  -56.600 -2.200
+52602.00    -56.936 -2.143  -56.600 -2.200
+52603.00    -56.599 -2.102  -56.500 -2.100
+52604.00    -56.262 -2.032  -56.400 -2.000
+52605.00    -55.941 -1.938  -56.300 -2.000
+52606.00    -55.707 -1.807  -56.300 -1.800
+52607.00    -55.671 -1.616  -56.300 -1.600
+52608.00    -56.053 -1.385  -56.300 -1.300
+52609.00    -56.523 -1.234  -56.400 -1.100
+52610.00    -56.957 -1.261  -56.700 -1.500
+52611.00    -57.095 -1.407  -56.800 -1.400
+52612.00    -56.926 -1.485  -56.600 -1.300
+52613.00    -56.663 -1.411  -56.300 -1.300
+52614.00    -56.452 -1.326  -55.900 -1.300
+52615.00    -56.242 -1.406  -55.600 -1.300
+52616.00    -55.972 -1.596  -55.500 -1.400
+52617.00    -55.727 -1.66   -55.500 -1.400
+52618.00    -55.631 -1.48   -55.600 -1.400
+52619.00    -55.665 -1.203  -55.700 -1.300
+52620.00    -55.794 -1.033  -55.700 -1.200
+52621.00    -55.943 -0.985  -55.700 -1.000
+52622.00    -56.075 -0.905  -55.800 -0.800
+52623.00    -56.135 -0.706  -55.800 -0.600
+52624.00    -56.122 -0.487  -55.900 -0.400
+52625.00    -56.13  -0.424  -56.000 -0.500
+52626.00    -56.238 -0.58   -56.000 -0.600
+52627.00    -56.288 -0.884  -56.000 -0.800
+52628.00    -56.129 -1.142  -55.900 -1.100
+52629.00    -55.769 -1.188  -55.600 -1.300
+52630.00    -55.283 -1.132  -55.000 -1.400
+52631.00    -54.871 -1.044  -54.400 -1.500
+52632.00    -54.583 -1.015  -53.900 -1.400
+52633.00    -54.399 -1.032  -53.500 -1.300
+52634.00    -54.322 -1.015  -53.300 -1.100
+52635.00    -54.406 -0.91   -53.500 -0.900
+52636.00    -54.548 -0.74   -54.000 -0.700
+52637.00    -54.991 -0.587  -54.600 -0.500
+52638.00    -55.413 -0.527  -55.100 -0.400
+52639.00    -55.626 -0.568  -55.500 -0.400
+52640.00    -55.58  -0.644  -55.500 -0.500
+52641.00    -55.381 -0.698  -55.300 -0.600
+52642.00    -55.151 -0.756  -54.900 -0.700
+52643.00    -54.916 -0.876  -54.500 -0.800
+52644.00    -54.663 -1.025  -54.300 -0.900
+52645.00    -54.443 -1.079  -54.100 -1.000
+52646.00    -54.366 -0.977  -54.100 -1.000
+52647.00    -54.485 -0.817  -54.300 -1.000
+52648.00    -54.737 -0.733  -54.500 -0.900
+52649.00    -54.991 -0.724  -54.700 -0.800
+52650.00    -55.122 -0.632  -54.800 -0.600
+52651.00    -55.163 -0.462  -54.900 -0.400
+52652.00    -55.158 -0.272  -54.900 -0.300
+52653.00    -55.19  -0.227  -55.000 -0.300
+52654.00    -55.273 -0.412  -55.100 -0.600
+52655.00    -55.33  -0.762  -55.200 -0.900
+52656.00    -55.258 -1.113  -55.100 -1.100
+52657.00    -54.974 -1.297  -54.900 -1.300
+52658.00    -54.527 -1.257  -54.500 -1.300
+52659.00    -54.053 -1.086  -54.100 -1.100
+52660.00    -53.724 -0.943  -53.700 -1.000
+52661.00    -53.649 -0.913  -53.400 -0.900
+52662.00    -53.817 -0.956  -53.400 -0.900
+52663.00    -54.132 -0.979  -53.600 -0.900
+52664.00    -54.457 -0.916  -53.900 -0.900
+52665.00    -54.774 -0.812  -54.400 -0.800
+52666.00    -54.991 -0.705  -54.700 -0.700
+52667.00    -55.045 -0.652  -54.900 -0.700
+52668.00    -54.91  -0.689  -54.800 -0.800
+52669.00    -54.642 -0.837  -54.500 -0.900
+52670.00    -54.354 -1.031  -54.200 -1.100
+52671.00    -54.147 -1.238  -53.900 -1.200
+52672.00    -54.035 -1.388  -53.800 -1.200
+52673.00    -53.981 -1.417  -53.800 -1.200
+52674.00    -53.98  -1.337  -53.800 -1.200
+52675.00    -54.077 -1.247  -53.900 -1.200
+52676.00    -54.265 -1.235  -54.000 -1.200
+52677.00    -54.433 -1.276  -54.000 -1.200
+52678.00    -54.482 -1.273  -53.900 -1.200
+52679.00    -54.451 -1.207  -53.900 -1.100
+52680.00    -54.446 -1.168  -53.900 -1.100
+52681.00    -54.478 -1.246  -54.000 -1.300
+52682.00    -54.442 -1.434  -54.200 -1.500
+52683.00    -54.381 -1.706  -54.300 -1.900
+52684.00    -54.325 -2.01   -54.300 -2.100
+52685.00    -54.237 -2.272  -54.100 -2.200
+52686.00    -53.957 -2.366  -53.700 -2.100
+52687.00    -53.456 -2.23   -53.300 -1.900
+52688.00    -52.998 -1.976  -53.000 -1.800
+52689.00    -52.877 -1.828  -52.900 -1.700
+52690.00    -53.22  -1.863  -53.100 -1.900
+52691.00    -53.721 -2.012  -53.400 -2.000
+52692.00    -54.093 -2.117  -53.800 -2.100
+52693.00    -54.254 -2.103  -54.100 -2.000
+52694.00    -54.273 -2.014  -54.200 -2.000
+52695.00    -54.202 -1.938  -54.000 -1.900
+52696.00    -54.016 -1.948  -53.700 -1.900
+52697.00    -53.693 -2.073  -53.200 -2.000
+52698.00    -53.31  -2.286  -52.800 -2.200
+52699.00    -53.008 -2.471  -52.500 -2.200
+52700.00    -52.919 -2.57   -52.600 -2.300
+52701.00    -52.973 -2.491  -52.800 -2.300
+52702.00    -53.052 -2.302  -53.000 -2.200
+52703.00    -53.122 -2.166  -53.200 -2.200
+52704.00    -53.174 -2.176  -53.300 -2.200
+52705.00    -53.177 -2.289  -53.300 -2.200
+52706.00    -53.092 -2.393  -53.100 -2.300
+52707.00    -53.001 -2.479  -53.000 -2.400
+52708.00    -53.008 -2.63   -52.800 -2.600
+52709.00    -53.033 -2.864  -52.800 -2.700
+52710.00    -52.883 -3.071  -52.700 -2.900
+52711.00    -52.58  -3.187  -52.700 -3.100
+52712.00    -52.385 -3.316  -52.500 -3.300
+52713.00    -52.39  -3.571  -52.100 -3.400
+52714.00    -52.324 -3.839  -51.700 -3.500
+52715.00    -51.935 -3.868  -51.300 -3.600
+52716.00    -51.45  -3.601  -51.200 -3.600
+52717.00    -51.354 -3.289  -51.300 -3.600
+52718.00    -51.788 -3.21   -51.700 -3.500
+52719.00    -52.385 -3.369  -52.100 -3.500
+52720.00    -52.733 -3.562  -52.500 -3.500
+52721.00    -52.701 -3.638  -52.600 -3.500
+52722.00    -52.52  -3.643  -52.500 -3.500
+52723.00    -52.377 -3.669  -52.200 -3.600
+52724.00    -52.275 -3.735  -51.900 -3.800
+52725.00    -52.098 -3.813  -51.700 -3.900
+52726.00    -51.784 -3.895  -51.500 -3.900
+52727.00    -51.437 -3.957  -51.500 -3.800
+52728.00    -51.244 -3.908  -51.500 -3.700
+52729.00    -51.28  -3.679  -51.500 -3.500
+52730.00    -51.438 -3.347  -51.400 -3.400
+52731.00    -51.553 -3.109  -51.200 -3.300
+52732.00    -51.586 -3.153  -51.000 -3.400
+52733.00    -51.514 -3.39   -50.800 -3.500
+52734.00    -51.368 -3.641  -50.800 -3.700
+52735.00    -51.258 -3.842  -50.900 -3.900
+52736.00    -51.285 -4.074  -51.000 -4.100
+52737.00    -51.342 -4.359  -51.100 -4.200
+52738.00    -51.174 -4.558  -50.900 -4.300
+52739.00    -50.751 -4.567  -50.700 -4.400
+52740.00    -50.391 -4.532  -50.300 -4.600
+52741.00    -50.343 -4.688  -49.900 -4.700
+52742.00    -50.418 -5.004  -49.600 -4.900
+52743.00    -50.297 -5.153  -49.600 -4.900
+52744.00    -50.073 -4.923  -49.800 -4.800
+52745.00    -50.363 -4.461  -50.200 -4.600
+52746.00    -50.934 -4.223  -50.700 -4.300
+52747.00    -51.604 -4.276  -51.000 -4.200
+52748.00    -51.877 -4.406  -51.100 -4.200
+52749.00    -51.679 -4.532  -50.900 -4.400
+52750.00    -51.252 -4.668  -50.700 -4.700
+52751.00    -50.908 -4.882  -50.500 -4.900
+52752.00    -50.782 -5.082  -50.400 -5.100
+52753.00    -50.792 -5.122  -50.400 -5.100
+52754.00    -50.741 -5.002  -50.500 -4.900
+52755.00    -50.54  -4.824  -50.400 -4.700
+52756.00    -50.334 -4.628  -50.300 -4.500
+52757.00    -50.344 -4.349  -50.300 -4.200
+52758.00    -50.59  -4.013  -50.400 -4.000
+52759.00    -50.875 -3.814  -50.800 -3.900
+52760.00    -51.014 -3.925  -51.100 -3.900
+52761.00    -50.976 -4.288  -51.200 -4.100
+52762.00    -50.818 -4.673  -51.100 -4.400
+52763.00    -50.705 -4.909  -50.900 -4.800
+52764.00    -50.721 -5.059  -50.700 -5.100
+52765.00    -50.831 -5.218  -50.600 -5.200
+52766.00    -50.83  -5.344  -50.400 -5.300
+52767.00    -50.584 -5.339  -50.300 -5.300
+52768.00    -50.24  -5.263  -50.300 -5.300
+52769.00    -49.972 -5.277  -50.100 -5.400
+52770.00    -49.993 -5.449  -49.900 -5.400
+52771.00    -50.086 -5.525  -49.900 -5.400
+52772.00    -50.269 -5.298  -50.000 -5.300
+52773.00    -50.726 -4.887  -50.600 -5.100
+52774.00    -51.457 -4.605  -51.200 -4.800
+52775.00    -52.127 -4.587  -51.700 -4.600
+52776.00    -52.379 -4.675  -51.900 -4.500
+52777.00    -52.128 -4.799  -51.600 -4.700
+52778.00    -51.589 -4.999  -51.200 -4.900
+52779.00    -51.061 -5.335  -50.800 -5.200
+52780.00    -50.794 -5.642  -50.600 -5.400
+52781.00    -50.863 -5.672  -50.600 -5.500
+52782.00    -51.09  -5.399  -50.900 -5.400
+52783.00    -51.212 -5.042  -51.200 -5.100
+52784.00    -51.203 -4.775  -51.400 -4.800
+52785.00    -51.287 -4.572  -51.700 -4.500
+52786.00    -51.609 -4.366  -51.800 -4.300
+52787.00    -51.982 -4.234  -52.000 -4.300
+52788.00    -52.26  -4.353  -52.100 -4.500
+52789.00    -52.338 -4.716  -52.200 -4.700
+52790.00    -52.3   -5.116  -52.100 -5.000
+52791.00    -52.244 -5.342  -52.000 -5.100
+52792.00    -52.247 -5.371  -51.900 -5.200
+52793.00    -52.359 -5.329  -51.900 -5.200
+52794.00    -52.524 -5.312  -52.000 -5.300
+52795.00    -52.585 -5.308  -52.100 -5.400
+52796.00    -52.455 -5.286  -52.300 -5.400
+52797.00    -52.218 -5.218  -52.300 -5.300
+52798.00    -52.115 -5.183  -52.400 -5.200
+52799.00    -52.278 -5.064  -52.600 -4.900
+52800.00    -52.767 -4.808  -52.800 -4.700
+52801.00    -53.549 -4.534  -53.100 -4.500
+52802.00    -54.346 -4.383  -53.400 -4.500
+52803.00    -54.883 -4.43   -53.700 -4.500
+52804.00    -55.004 -4.554  -54.000 -4.700
+52805.00    -54.781 -4.661  -54.100 -4.900
+52806.00    -54.378 -4.826  -54.100 -5.100
+52807.00    -53.954 -5.129  -54.100 -5.300
+52808.00    -53.629 -5.435  -53.900 -5.400
+52809.00    -53.717 -5.479  -53.800 -5.300
+52810.00    -54.102 -5.203  -53.800 -5.200
+52811.00    -54.527 -4.827  -53.800 -5.000
+52812.00    -54.812 -4.589  -54.100 -4.800
+52813.00    -55.043 -4.499  -54.400 -4.600
+52814.00    -55.376 -4.425  -54.900 -4.400
+52815.00    -55.789 -4.346  -55.400 -4.400
+52816.00    -56.129 -4.388  -55.900 -4.400
+52817.00    -56.334 -4.638  -56.200 -4.500
+52818.00    -56.407 -4.976  -56.400 -4.700
+52819.00    -56.445 -5.234  -56.300 -4.900
+52820.00    -56.421 -5.271  -56.200 -5.100
+52821.00    -56.414 -5.141  -56.200 -5.200
+52822.00    -56.477 -5.03   -56.200 -5.100
+52823.00    -56.63  -4.988  -56.400 -5.000
+52824.00    -56.681 -5.024  -56.500 -4.800
+52825.00    -56.584 -5.033  -56.500 -4.700
+52826.00    -56.504 -4.928  -56.600 -4.600
+52827.00    -56.69  -4.711  -56.800 -4.500
+52828.00    -57.246 -4.463  -57.000 -4.500
+52829.00    -58.023 -4.301  -57.400 -4.400
+52830.00    -58.701 -4.3    -57.800 -4.400
+52831.00    -59.026 -4.431  -58.100 -4.400
+52832.00    -58.947 -4.642  -58.200 -4.400
+52833.00    -58.73  -4.737  -58.100 -4.400
+52834.00    -58.502 -4.826  -58.000 -4.500
+52835.00    -58.316 -4.986  -58.000 -4.800
+52836.00    -58.214 -5.159  -58.200 -5.100
+52837.00    -58.31  -5.182  -58.500 -5.200
+52838.00    -58.665 -4.996  -59.000 -5.000
+52839.00    -59.164 -4.774  -59.500 -4.900
+52840.00    -59.61  -4.637  -59.900 -4.700
+52841.00    -59.92  -4.622  -60.300 -4.600
+52842.00    -60.157 -4.602  -60.400 -4.500
+52843.00    -60.399 -4.523  -60.400 -4.500
+52844.00    -60.655 -4.497  -60.300 -4.500
+52845.00    -60.884 -4.654  -60.300 -4.500
+52846.00    -60.932 -4.999  -60.400 -4.600
+52847.00    -60.93  -5.298  -60.600 -4.800
+52848.00    -60.779 -5.41   -60.700 -5.000
+52849.00    -60.583 -5.305  -60.800 -5.200
+52850.00    -60.43  -5.113  -60.700 -5.200
+52851.00    -60.469 -4.992  -60.500 -5.200
+52852.00    -60.57  -5.017  -60.300 -5.100
+52853.00    -60.663 -5.101  -60.300 -5.000
+52854.00    -60.785 -5.106  -60.500 -4.900
+52855.00    -61.041 -4.981  -60.900 -5.100
+52856.00    -61.473 -4.8    -61.300 -4.700
+52857.00    -61.973 -4.676  -61.700 -4.600
+52858.00    -62.345 -4.658  -62.000 -4.600
+52859.00    -62.45  -4.723  -62.200 -4.700
+52860.00    -62.304 -4.828  -62.200 -4.700
+52861.00    -62.063 -4.892  -62.000 -4.800
+52862.00    -61.866 -4.93   -61.800 -4.900
+52863.00    -61.759 -4.952  -61.500 -4.900
+52864.00    -61.729 -4.936  -61.300 -5.000
+52865.00    -61.792 -4.837  -61.300 -4.900
+52866.00    -62.004 -4.668  -61.500 -4.700
+52867.00    -62.371 -4.537  -61.800 -4.500
+52868.00    -62.774 -4.52   -62.200 -4.400
+52869.00    -63.052 -4.575  -62.600 -4.400
+52870.00    -63.148 -4.583  -63.000 -4.400
+52871.00    -63.177 -4.521  -63.200 -4.500
+52872.00    -63.28  -4.508  -63.300 -4.700
+52873.00    -63.427 -4.657  -63.400 -4.800
+52874.00    -63.524 -4.911  -63.300 -4.900
+52875.00    -63.294 -5.146  -63.300 -5.000
+52876.00    -62.925 -5.216  -63.100 -5.000
+52877.00    -62.62  -5.104  -62.900 -5.000
+52878.00    -62.496 -4.889  -62.700 -4.900
+52879.00    -62.525 -4.691  -62.500 -4.800
+52880.00    -62.673 -4.655  -62.400 -4.700
+52881.00    -62.908 -4.78   -62.400 -4.700
+52882.00    -63.199 -4.951  -62.700 -4.600
+52883.00    -63.49  -5.018  -63.000 -4.600
+52884.00    -63.733 -4.937  -63.300 -4.500
+52885.00    -63.905 -4.779  -63.500 -4.500
+52886.00    -63.983 -4.648  -63.600 -4.500
+52887.00    -63.941 -4.591  -63.500 -4.600
+52888.00    -63.67  -4.587  -63.300 -4.600
+52889.00    -63.406 -4.632  -63.100 -4.700
+52890.00    -63.134 -4.67   -62.900 -4.700
+52891.00    -62.942 -4.645  -62.800 -4.600
+52892.00    -62.871 -4.506  -62.800 -4.500
+52893.00    -62.895 -4.26   -62.900 -4.300
+52894.00    -62.988 -4.007  -63.100 -4.100
+52895.00    -63.229 -3.929  -63.300 -3.900
+52896.00    -63.451 -4.011  -63.400 -3.800
+52897.00    -63.6   -4.181  -63.600 -3.900
+52898.00    -63.576 -4.279  -63.600 -4.100
+52899.00    -63.473 -4.285  -63.600 -4.300
+52900.00    -63.463 -4.331  -63.400 -4.500
+52901.00    -63.51  -4.5    -63.100 -4.600
+52902.00    -63.385 -4.688  -62.700 -4.700
+52903.00    -62.909 -4.743  -62.300 -4.700
+52904.00    -62.352 -4.629  -61.900 -4.600
+52905.00    -62.056 -4.439  -61.700 -4.500
+52906.00    -62.092 -4.242  -61.700 -4.400
+52907.00    -62.279 -4.059  -61.800 -4.200
+52908.00    -62.478 -3.95   -62.100 -4.100
+52909.00    -62.681 -4.013  -62.400 -4.100
+52910.00    -62.868 -4.232  -62.700 -4.100
+52911.00    -62.96  -4.424  -62.900 -4.200
+52912.00    -62.943 -4.416  -63.000 -4.300
+52913.00    -62.9   -4.236  -63.100 -4.300
+52914.00    -62.886 -4.063  -63.000 -4.200
+52915.00    -62.846 -4.019  -62.800 -4.200
+52916.00    -62.688 -4.072  -62.400 -4.100
+52917.00    -62.392 -4.127  -62.000 -4.000
+52918.00    -62.034 -4.134  -61.600 -4.000
+52919.00    -61.739 -4.07   -61.400 -3.800
+52920.00    -61.621 -3.933  -61.500 -3.700
+52921.00    -61.614 -3.63   -61.500 -3.600
+52922.00    -61.646 -3.308  -61.300 -3.400
+52923.00    -61.667 -3.14   -61.100 -3.300
+52924.00    -61.667 -3.262  -61.100 -3.300
+52925.00    -61.634 -3.533  -61.200 -3.400
+52926.00    -61.524 -3.742  -61.300 -3.600
+52927.00    -61.38  -3.818  -61.300 -3.800
+52928.00    -61.312 -3.88   -61.200 -4.000
+52929.00    -61.273 -4.01   -60.900 -4.100
+52930.00    -61.048 -4.108  -60.500 -4.000
+52931.00    -60.491 -3.997  -60.000 -3.900
+52932.00    -59.908 -3.728  -59.600 -3.700
+52933.00    -59.673 -3.487  -59.400 -3.400
+52934.00    -59.826 -3.349  -59.500 -3.200
+52935.00    -60.112 -3.211  -59.700 -3.000
+52936.00    -60.338 -3.02   -60.100 -2.900
+52937.00    -60.532 -2.919  -60.300 -2.900
+52938.00    -60.561 -3.018  -60.300 -2.900
+52939.00    -60.39  -3.198  -60.200 -3.000
+52940.00    -60.09  -3.224  -60.000 -3.100
+52941.00    -59.883 -3.071  -59.900 -3.200
+52942.00    -59.873 -2.958  -59.700 -3.300
+52943.00    -59.915 -3.04   -59.600 -3.400
+52944.00    -59.866 -3.211  -59.400 -3.400
+52945.00    -59.622 -3.24   -59.400 -3.200
+52946.00    -59.334 -3.088  -59.200 -3.100
+52947.00    -59.11  -2.873  -59.000 -2.900
+52948.00    -59.009 -2.665  -58.900 -2.800
+52949.00    -59.02  -2.429  -58.900 -2.700
+52950.00    -59.05  -2.17   -58.900 -2.300
+52951.00    -59.077 -2.023  -58.900 -2.000
+52952.00    -58.943 -2.138  -58.900 -2.100
+52953.00    -58.8   -2.438  -58.900 -2.300
+52954.00    -58.676 -2.706  -58.800 -2.600
+52955.00    -58.557 -2.815  -58.600 -2.800
+52956.00    -58.441 -2.843  -58.300 -2.900
+52957.00    -58.287 -2.892  -57.900 -2.800
+52958.00    -57.999 -2.888  -57.600 -2.800
+52959.00    -57.52  -2.754  -57.300 -2.600
+52960.00    -57.072 -2.507  -57.000 -2.500
+52961.00    -56.911 -2.325  -56.800 -2.300
+52962.00    -57.003 -2.27   -56.800 -2.200
+52963.00    -57.258 -2.177  -56.900 -2.000
+52964.00    -57.539 -1.93   -57.100 -1.900
+52965.00    -57.855 -1.673  -57.300 -1.900
+52966.00    -58.088 -1.631  -57.500 -1.900
+52967.00    -58.025 -1.781  -57.600 -1.900
+52968.00    -57.674 -1.877  -57.400 -1.900
+52969.00    -57.318 -1.819  -57.100 -1.800
+52970.00    -57.158 -1.792  -56.700 -1.800
+52971.00    -57.1   -1.954  -56.300 -1.700
+52972.00    -57.013 -2.151  -56.200 -1.700
+52973.00    -56.869 -2.095  -56.300 -1.600
+52974.00    -56.817 -1.743  -56.500 -1.400
+52975.00    -56.871 -1.344  -56.800 -1.300
+52976.00    -56.964 -1.114  -57.000 -1.100
+52977.00    -57.058 -1.023  -57.000 -1.000
+52978.00    -57.11  -0.946  -57.000 -1.000
+52979.00    -57.055 -0.894  -57.000 -1.000
+52980.00    -56.885 -1.002  -56.900 -1.200
+52981.00    -56.717 -1.276  -56.800 -1.300
+52982.00    -56.625 -1.554  -56.700 -1.500
+52983.00    -56.545 -1.686  -56.400 -1.600
+52984.00    -56.362 -1.683  -56.000 -1.700
+52985.00    -56.048 -1.653  -55.500 -1.700
+52986.00    -55.658 -1.633  -55.000 -1.700
+52987.00    -55.274 -1.574  -54.700 -1.600
+52988.00    -54.999 -1.473  -54.500 -1.500
+52989.00    -54.906 -1.405  -54.600 -1.400
+52990.00    -54.912 -1.396  -54.800 -1.500
+52991.00    -55.073 -1.304  -55.000 -1.300
+52992.00    -55.383 -1.057  -55.200 -1.100
+52993.00    -55.907 -0.787  -55.500 -0.900
+52994.00    -56.487 -0.727  -55.800 -0.800
+52995.00    -56.792 -0.907  -56.000 -0.900
+52996.00    -56.671 -1.113  -56.200 -1.000
+52997.00    -56.269 -1.155  -56.100 -1.200
+52998.00    -55.891 -1.176  -55.900 -1.400
+52999.00    -55.553 -1.292  -55.800 -1.500
+53000.00    -55.263 -1.407  -55.600 -1.400
+53001.00    -55.121 -1.284  -55.600 -1.100
+53002.00    -55.265 -0.881  -55.600 -0.800
+53003.00    -55.612 -0.464  -55.800 -0.500
+53004.00    -55.957 -0.296  -55.900 -0.200
+53005.00    -56.182 -0.356  -56.100 -0.100
+53006.00    -56.267 -0.447  -56.100 -0.200
+53007.00    -56.23  -0.51   -56.000 -0.400
+53008.00    -56.014 -0.595  -55.900 -0.600
+53009.00    -55.791 -0.81   -55.700 -0.900
+53010.00    -55.682 -1.079  -55.500 -1.000
+53011.00    -55.62  -1.244  -55.200 -1.100
+53012.00    -55.433 -1.241  -54.900 -1.100
+53013.00    -55.072 -1.14   -54.600 -1.100
+53014.00    -54.671 -1.05   -54.400 -1.200
+53015.00    -54.387 -1.023  -54.200 -1.300
+53016.00    -54.273 -1.041  -54.100 -1.300
+53017.00    -54.289 -1.066  -54.100 -1.100
+53018.00    -54.306 -1.052  -54.100 -0.700
+53019.00    -54.461 -0.939  -54.400 -0.600
+53020.00    -54.74  -0.738  -54.900 -0.600
+53021.00    -55.192 -0.564  -55.300 -0.700
+53022.00    -55.71  -0.569  -55.700 -0.700
+53023.00    -56.056 -0.777  -55.900 -0.800
+53024.00    -56.077 -1.037  -55.900 -0.900
+53025.00    -55.842 -1.234  -55.600 -1.100
+53026.00    -55.472 -1.277  -55.300 -1.200
+53027.00    -55.057 -1.314  -54.900 -1.300
+53028.00    -54.694 -1.358  -54.800 -1.300
+53029.00    -54.517 -1.256  -54.800 -1.100
+53030.00    -54.69  -1.005  -55.000 -0.900
+53031.00    -55.141 -0.78   -55.300 -0.700
+53032.00    -55.636 -0.764  -55.500 -0.700
+53033.00    -55.982 -0.922  -55.700 -0.800
+53034.00    -56.103 -1.066  -55.700 -1.000
+53035.00    -55.988 -1.133  -55.600 -1.200
+53036.00    -55.661 -1.171  -55.300 -1.300
+53037.00    -55.286 -1.332  -55.000 -1.500
+53038.00    -55.04  -1.596  -54.800 -1.600
+53039.00    -54.939 -1.815  -54.600 -1.700
+53040.00    -54.847 -1.866  -54.500 -1.700
+53041.00    -54.656 -1.756  -54.400 -1.800
+53042.00    -54.412 -1.598  -54.300 -1.800
+53043.00    -54.251 -1.522  -54.200 -1.700
+53044.00    -54.236 -1.559  -54.300 -1.600
+53045.00    -54.363 -1.64   -54.300 -1.500
+53046.00    -54.587 -1.669  -54.500 -1.400
+53047.00    -54.851 -1.607  -54.700 -1.300
+53048.00    -55.095 -1.498  -54.900 -1.300
+53049.00    -55.325 -1.423  -55.100 -1.300
+53050.00    -55.385 -1.463  -55.100 -1.400
+53051.00    -55.305 -1.621  -55.100 -1.500
+53052.00    -55.105 -1.83   -54.900 -1.800
+53053.00    -54.856 -1.995  -54.600 -2.000
+53054.00    -54.616 -2.072  -54.400 -2.100
+53055.00    -54.378 -2.117  -54.300 -2.100
+53056.00    -54.168 -2.091  -54.300 -2.100
+53057.00    -54.053 -2.024  -54.300 -2.000
+53058.00    -54.143 -1.939  -54.400 -1.900
+53059.00    -54.46  -1.922  -54.500 -1.900
+53060.00    -54.885 -2.035  -54.600 -2.000
+53061.00    -55.23  -2.216  -54.700 -2.200
+53062.00    -55.343 -2.33   -54.600 -2.400
+53063.00    -55.261 -2.309  -54.500 -2.500
+53064.00    -54.831 -2.338  -54.200 -2.600
+53065.00    -54.292 -2.495  -53.900 -2.600
+53066.00    -53.828 -2.763  -53.600 -2.700
+53067.00    -53.572 -3.017  -53.400 -2.900
+53068.00    -53.53  -3.146  -53.300 -3.100
+53069.00    -53.585 -3.125  -53.100 -3.200
+53070.00    -53.658 -2.977  -53.000 -3.200
+53071.00    -53.59  -2.832  -52.900 -3.000
+53072.00    -53.561 -2.758  -53.000 -2.800
+53073.00    -53.713 -2.786  -53.300 -2.700
+53074.00    -54.06  -2.873  -54.000 -2.800
+53075.00    -54.444 -2.951  -54.400 -2.900
+53076.00    -54.663 -2.992  -54.600 -3.000
+53077.00    -54.689 -2.989  -54.500 -3.100
+53078.00    -54.399 -3.02   -54.100 -3.100
+53079.00    -53.973 -3.087  -53.500 -3.100
+53080.00    -53.526 -3.199  -52.900 -3.100
+53081.00    -53.153 -3.328  -52.600 -3.200
+53082.00    -52.919 -3.416  -52.600 -3.300
+53083.00    -52.835 -3.414  -52.700 -3.400
+53084.00    -52.84  -3.307  -52.800 -3.400
+53085.00    -52.883 -3.163  -53.000 -3.300
+53086.00    -52.936 -3.065  -53.100 -3.200
+53087.00    -53.051 -3.096  -53.100 -3.200
+53088.00    -53.258 -3.257  -53.100 -3.200
+53089.00    -53.471 -3.447  -53.000 -3.400
+53090.00    -53.538 -3.556  -53.000 -3.600
+53091.00    -53.405 -3.587  -52.800 -3.700
+53092.00    -53.051 -3.674  -52.600 -3.900
+53093.00    -52.56  -3.881  -52.400 -4.000
+53094.00    -52.01  -4.14   -52.000 -4.100
+53095.00    -51.575 -4.347  -51.700 -4.300
+53096.00    -51.461 -4.481  -51.400 -4.500
+53097.00    -51.665 -4.562  -51.300 -4.600
+53098.00    -51.948 -4.493  -51.400 -4.600
+53099.00    -52.016 -4.318  -51.600 -4.400
+53100.00    -52.007 -4.039  -52.100 -4.100
+53101.00    -52.215 -3.846  -52.500 -4.000
+53102.00    -52.703 -3.878  -52.700 -4.100
+53103.00    -53.187 -4.088  -53.000 -4.100
+53104.00    -53.342 -4.317  -53.300 -4.200
+53105.00    -53.112 -4.46   -53.200 -4.200
+53106.00    -52.691 -4.524  -52.800 -4.300
+53107.00    -52.262 -4.562  -52.300 -4.400
+53108.00    -51.886 -4.603  -51.800 -4.500
+53109.00    -51.56  -4.641  -51.500 -4.600
+53110.00    -51.317 -4.643  -51.300 -4.500
+53111.00    -51.236 -4.559  -51.200 -4.400
+53112.00    -51.344 -4.359  -51.200 -4.200
+53113.00    -51.543 -4.089  -51.300 -3.900
+53114.00    -51.689 -3.888  -51.300 -3.800
+53115.00    -51.74  -3.894  -51.300 -3.700
+53116.00    -51.66  -4.075  -51.300 -3.900
+53117.00    -51.707 -4.367  -51.200 -4.100
+53118.00    -51.718 -4.562  -51.200 -4.400
+53119.00    -51.588 -4.654  -51.200 -4.700
+53120.00    -51.481 -4.778  -51.200 -4.900
+53121.00    -51.281 -4.997  -51.100 -5.100
+53122.00    -50.896 -5.203  -51.000 -5.200
+53123.00    -50.413 -5.284  -50.700 -5.200
+53124.00    -50.16  -5.302  -50.400 -5.300
+53125.00    -50.344 -5.381  -50.300 -5.200
+53126.00    -50.751 -5.461  -50.300 -5.100
+53127.00    -51.038 -5.319  -50.500 -4.800
+53128.00    -51.22  -4.895  -51.000 -4.600
+53129.00    -51.608 -4.458  -51.600 -4.500
+53130.00    -52.325 -4.36   -52.200 -4.500
+53131.00    -52.882 -4.611  -52.600 -4.700
+53132.00    -52.928 -4.982  -52.800 -5.000
+53133.00    -52.484 -5.252  -52.700 -5.200
+53134.00    -51.911 -5.4    -52.300 -5.400
+53135.00    -51.517 -5.483  -51.900 -5.500
+53136.00    -51.348 -5.489  -51.400 -5.500
+53137.00    -51.294 -5.393  -51.100 -5.400
+53138.00    -51.244 -5.179  -51.000 -5.200
+53139.00    -51.228 -4.938  -51.100 -4.900
+53140.00    -51.342 -4.688  -51.400 -4.500
+53141.00    -51.607 -4.428  -51.700 -4.300
+53142.00    -51.895 -4.242  -52.000 -4.200
+53143.00    -52.053 -4.287  -52.100 -4.300
+53144.00    -52.067 -4.613  -52.100 -4.600
+53145.00    -52.023 -5.047  -52.100 -5.000
+53146.00    -51.996 -5.335  -52.200 -5.300
+53147.00    -51.986 -5.396  -52.400 -5.400
+53148.00    -52.101 -5.402  -52.600 -5.400
+53149.00    -52.215 -5.476  -52.500 -5.400
+53150.00    -52.133 -5.552  -52.100 -5.400
+53151.00    -51.802 -5.497  -51.500 -5.600
+53152.00    -51.502 -5.362  -51.100 -5.600
+53153.00    -51.572 -5.327  -51.200 -5.500
+53154.00    -51.996 -5.346  -51.600 -5.200
+53155.00    -52.507 -5.277  -52.200 -4.900
+53156.00    -52.991 -4.908  -53.000 -4.600
+53157.00    -53.601 -4.453  -53.800 -4.300
+53158.00    -54.338 -4.292  -54.300 -4.200
+53159.00    -54.846 -4.528  -54.600 -4.400
+53160.00    -54.789 -4.911  -54.500 -4.700
+53161.00    -54.246 -5.194  -54.400 -5.000
+53162.00    -53.616 -5.367  -54.200 -5.100
+53163.00    -53.236 -5.497  -54.000 -5.200
+53164.00    -53.205 -5.507  -53.700 -5.300
+53165.00    -53.438 -5.272  -53.300 -5.400
+53166.00    -53.752 -4.856  -53.400 -5.100
+53167.00    -53.995 -4.488  -53.700 -4.600
+53168.00    -54.173 -4.302  -53.900 -4.200
+53169.00    -54.418 -4.242  -54.000 -4.000
+53170.00    -54.761 -4.239  -54.100 -4.100
+53171.00    -55.061 -4.37   -54.400 -4.300
+53172.00    -55.187 -4.723  -55.000 -4.500
+53173.00    -55.181 -5.181  -55.400 -4.900
+53174.00    -55.174 -5.476  -55.300 -5.300
+53175.00    -55.25  -5.462  -55.300 -5.400
+53176.00    -55.425 -5.272  -55.300 -5.400
+53177.00    -55.643 -5.124  -55.300 -5.300
+53178.00    -55.762 -5.064  -55.200 -5.100
+53179.00    -55.669 -4.978  -55.200 -5.000
+53180.00    -55.478 -4.827  -55.200 -5.000
+53181.00    -55.476 -4.719  -55.500 -4.900
+53182.00    -55.825 -4.72   -55.900 -4.800
+53183.00    -56.434 -4.706  -56.500 -4.500
+53184.00    -57.147 -4.546  -57.100 -4.300
+53185.00    -57.873 -4.337  -57.700 -4.200
+53186.00    -58.474 -4.317  -58.000 -4.300
+53187.00    -58.723 -4.542  -58.100 -4.500
+53188.00    -58.525 -4.803  -58.000 -4.800
+53189.00    -58.071 -4.925  -57.900 -5.100
+53190.00    -57.656 -4.981  -57.700 -5.200
+53191.00    -57.444 -5.093  -57.700 -5.200
+53192.00    -57.499 -5.165  -57.700 -5.100
+53193.00    -57.852 -4.989  -57.900 -4.800
+53194.00    -58.406 -4.582  -58.100 -4.500
+53195.00    -58.912 -4.237  -58.300 -4.200
+53196.00    -59.201 -4.182  -58.600 -4.000
+53197.00    -59.368 -4.343  -59.000 -4.000
+53198.00    -59.584 -4.515  -59.300 -4.200
+53199.00    -59.834 -4.655  -59.600 -4.500
+53200.00    -59.973 -4.874  -59.800 -4.900
+53201.00    -59.978 -5.185  -59.800 -5.200
+53202.00    -59.958 -5.409  -59.800 -5.400
+53203.00    -59.985 -5.367  -59.800 -5.400
+53204.00    -60.037 -5.103  -59.900 -5.200
+53205.00    -60.095 -4.833  -59.900 -4.900
+53206.00    -60.153 -4.704  -59.900 -4.700
+53207.00    -60.166 -4.686  -59.900 -4.600
+53208.00    -60.113 -4.669  -59.900 -4.600
+53209.00    -60.105 -4.615  -60.200 -4.600
+53210.00    -60.32  -4.562  -60.600 -4.600
+53211.00    -60.829 -4.53   -61.200 -4.500
+53212.00    -61.514 -4.513  -61.700 -4.400
+53213.00    -62.137 -4.549  -62.100 -4.300
+53214.00    -62.446 -4.685  -62.000 -4.500
+53215.00    -62.337 -4.863  -61.800 -4.700
+53216.00    -61.963 -4.924  -61.400 -4.900
+53217.00    -61.633 -4.8    -61.200 -5.100
+53218.00    -61.521 -4.645  -61.200 -5.100
+53219.00    -61.553 -4.644  -61.300 -5.000
+53220.00    -61.649 -4.753  0.000   0.000
+53221.00    -61.898 -4.747  0.000   0.000
+53222.00    -62.389 -4.551  0.000   0.000
+53223.00    -62.967 -4.364  0.000   0.000
+53224.00    -63.362 -4.403  0.000   0.000
+53225.00    -63.499 -4.619  0.000   0.000
+53226.00    -63.512 -4.789  0.000   0.000
+53227.00    -63.499 -4.82   0.000   0.000
+53228.00    -63.429 -4.835  0.000   0.000
+53229.00    -63.295 -4.959  0.000   0.000
+53230.00    -63.171 -5.123  0.000   0.000
+53231.00    -63.092 -5.149  0.000   0.000
+53232.00    -63.011 -4.97   0.000   0.000
+53233.00    -62.912 -4.71   0.000   0.000
+53234.00    -62.857 -4.55   0.000   0.000
+53235.00    -62.883 -4.566  0.000   0.000
+53236.00    -62.94  -4.674  0.000   0.000
+53237.00    -63.005 -4.739  0.000   0.000
+53238.00    -63.161 -4.703  0.000   0.000
+53239.00    -63.495 -4.622  0.000   0.000
+53240.00    -63.939 -4.597  0.000   0.000
+53241.00    -64.263 -4.676  0.000   0.000
+53242.00    -64.246 -4.815  0.000   0.000
+53243.00    -63.877 -4.893  0.000   0.000
+53244.00    -63.406 -4.799  0.000   0.000
+53245.00    -63.172 -4.511  0.000   0.000
+53246.00    -63.216 -4.226  0.000   0.000
+53247.00    -63.377 -4.094  0.000   0.000
+53248.00    -63.473 -4.134  0.000   0.000
+53249.00    -63.565 -4.206  0.000   0.000
+53250.00    -63.844 -4.21   0.000   0.000
+53251.00    -64.326 -4.205  0.000   0.000
+53252.00    -64.786 -4.298  0.000   0.000
+53253.00    -64.991 -4.446  0.000   0.000
+53254.00    -64.896 -4.501  0.000   0.000
+53255.00    -64.606 -4.416  0.000   0.000
+53256.00    -64.24  -4.329  0.000   0.000
+53257.00    -63.885 -4.385  0.000   0.000
+53258.00    -63.605 -4.55   0.000   0.000
+53259.00    -63.419 -4.649  0.000   0.000
+53260.00    -63.312 -4.56   0.000   0.000
+53261.00    -63.272 -4.327  0.000   0.000
+53262.00    -63.307 -4.111  0.000   0.000
+53263.00    -63.406 -4.047  0.000   0.000
+53264.00    -63.522 -4.143  0.000   0.000
+53265.00    -63.631 -4.283  0.000   0.000
+53266.00    -63.761 -4.34   0.000   0.000
+53267.00    -63.935 -4.296  0.000   0.000
+53268.00    -64.084 -4.237  0.000   0.000
+53269.00    -64.063 -4.241  0.000   0.000
+53270.00    -63.767 -4.287  0.000   0.000
+53271.00    -63.254 -4.291  0.000   0.000
+53272.00    -62.74  -4.187  0.000   0.000
+53273.00    -62.445 -3.986  0.000   0.000
+53274.00    -62.421 -3.759  0.000   0.000
+53275.00    -62.529 -3.584  0.000   0.000
+53276.00    -62.603 -3.495  0.000   0.000
+53277.00    -62.624 -3.476  0.000   0.000
+53278.00    -62.72  -3.511  0.000   0.000
+53279.00    -62.992 -3.612  0.000   0.000
+53280.00    -63.35  -3.771  0.000   0.000
+53281.00    -63.56  -3.904  0.000   0.000
+53282.00    -63.441 -3.908  0.000   0.000
+53283.00    -63.02  -3.802  0.000   0.000
+53284.00    -62.477 -3.737  0.000   0.000
+53285.00    -61.967 -3.823  0.000   0.000
+53286.00    -61.539 -3.988  0.000   0.000
+53287.00    -61.222 -4.054  0.000   0.000
+53288.00    -61.099 -3.928  0.000   0.000
+53289.00    -61.22  -3.664  0.000   0.000
+53290.00    -61.49  -3.385  0.000   0.000
+53291.00    -61.727 -3.195  0.000   0.000
+53292.00    -61.835 -3.154  0.000   0.000
+53293.00    -61.854 -3.251  0.000   0.000
+53294.00    -61.852 -3.399  0.000   0.000
+53295.00    -61.826 -3.484  0.000   0.000
+53296.00    -61.727 -3.468  0.000   0.000
+53297.00    -61.512 -3.407  0.000   0.000
+53298.00    -61.163 -3.369  0.000   0.000
+53299.00    -60.705 -3.358  0.000   0.000
+53300.00    -60.231 -3.329  0.000   0.000
+53301.00    -59.867 -3.247  0.000   0.000
+53302.00    -59.696 -3.112  0.000   0.000
+53303.00    -59.701 -2.94   0.000   0.000
+53304.00    -59.782 -2.749  0.000   0.000
+53305.00    -59.837 -2.585  0.000   0.000
+53306.00    -59.839 -2.523  0.000   0.000
+53307.00    -59.858 -2.623  0.000   0.000
+53308.00    -59.949 -2.851  0.000   0.000
+53309.00    -60.031 -3.063  0.000   0.000
+53310.00    -59.933 -3.127  0.000   0.000
+53311.00    -59.593 -3.064  0.000   0.000
+53312.00    -59.12  -3.029  0.000   0.000
+53313.00    -58.635 -3.108  0.000   0.000
+53314.00    -58.155 -3.196  0.000   0.000
+53315.00    -57.729 -3.129  0.000   0.000
+53316.00    -57.552 -2.892  0.000   0.000
+53317.00    -57.772 -2.612  0.000   0.000
+53318.00    -58.248 -2.372  0.000   0.000
+53319.00    -58.656 -2.159  0.000   0.000
+53320.00    -58.83  -1.987  0.000   0.000
+53321.00    -58.84  -1.96   0.000   0.000
+53322.00    -58.763 -2.124  0.000   0.000
+53323.00    -58.573 -2.343  0.000   0.000
+53324.00    -58.275 -2.425  0.000   0.000
+53325.00    -57.988 -2.343  0.000   0.000
+53326.00    -57.796 -2.241  0.000   0.000
+53327.00    -57.631 -2.22   0.000   0.000
+53328.00    -57.402 -2.228  0.000   0.000
+53329.00    -57.147 -2.156  0.000   0.000
+53330.00    -56.995 -1.986  0.000   0.000
+53331.00    -57.018 -1.781  0.000   0.000
+53332.00    -57.155 -1.586  0.000   0.000
+53333.00    -57.259 -1.417  0.000   0.000
+53334.00    -57.213 -1.326  0.000   0.000
Index: /tags/sj_tags/sj_root_20080929/psLib/share/readme.eopc01
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/readme.eopc01	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/readme.eopc01	(revision 22322)
@@ -0,0 +1,82 @@
+This table comes from:
+
+ http://hpiers.obspm.fr/eoppc/eop/eopc01/eopc01.1900-2004
+ 
+As referenced in the Pan-STARRS ADD
+
+
+
+                              EOP(IERS) C 01
+                         ----------------------------
+ 
+ EOP(IERS) C 01 is a series of the earth Orientation Parameters given 
+ in the 1997 IERS system at 0.1 year interval (1846 - 1889) and 0.05 year 
+ interval (1890 - now). It is regularly recomputed to take advantage on one 
+ hand of the improvement of the various individual contributions and in the 
+ other hand of the refinement of the analyses procedures. This series is the
+ basis of the IERS system.
+ 
+ 
+ EOP(IERS) C 01 is based on the following data:
+ 
+ 
+ 1993 - now  : IERS solution of x, y, UT1-UTC, LOD, dPsi, dEpsilon (normal points),
+               based on VLBI, LLR, GPS  and SLR.
+               
+ 1980 - 1992 : IERS solution of x, y, UT1-UTC, dPsi, dEpsilon (normal points),
+               based on VLBI, LLR, GPS (from 1992), and SLR.
+
+ 1962 - 1979 : IERS and BIH solutions, giving x, y, UT1-UTC (normal points). 
+               Space techniques are introduced starting with 1969 (LLR: UT)
+               and 1972 (Doppler: polar motion).
+
+ 1900 - 1961 : Vondrak(1995), Solution derived from optical astrometry 
+               analyses the series give polar motion, celestial pole offsets 
+               and Universal Time (since 1956).
+     
+		
+ 
+ 1846 - 1899 :
+
+               The solution derived by L.V. Rikhlova (Fedorov et al., 1976) 
+               from three series of absolute declination programs (Pulkovo, 
+               Greenwhich, Washington). The frequency band of the variations 
+               kept encompasses only the annual and Chandler terms. The y
+               coordinate of the pole is missing from 1858.9 through 1860.9.
+ 
+ 
+ PRECISION AND ACCURACY
+
+ The individual values are accompanied by uncertainties which attempt to 
+ reflect their quality. For the IERS/BIH solutions, they are  derived from the
+ scattering of the independant measurements contributing to each normal point,
+ with added variance taking into account possible systematic errors. For the 
+ earlier period, they are based on the estimations given by Fedorov et al. 
+ (1976), scaled to match the agreement woth the IERS/BIH solution over
+ 1962-1968. In addition some isolated outliers are deweighted (uncertainty
+ multiplied by 3). 
+
+ The following remarks can be made concerning the drift and the annual term
+ before 1962:
+
+ Trend: as a result of the Fedorov et al. treatment, any drift in polar
+        motion existing before 1900 is ignored in the series. For the period
+        starting with 1900, although no external evidence is available for 
+        the pre-1972 polar motion, it should be mentionned that the low 
+        frequency spectrum of polar motion both before and after the advent 
+        of space techniques (giving the possibility of cross-checks) is that
+        pf random walk at a similar level.
+ 
+ Annual term: it is influenced by errors in nutation and local seasonal errors.
+        Comparisons of the Fedorov et al. solution with the IERS/BIH one over 
+        1962 - 1968 suggest that the amplitude of the annual term is accurate
+        to +/- 0.03" over 1900 - 1962.
+ 
+ REFERENCES
+ 
+ - Fedorov,E.P., Korsun,A.A, Mayor,S.P., Pantschenko,N.I., Tarady,V.K., and
+    Yatskiv,Y.S., 1972: Dvizhenie polyusa Zemli s 1890.0 po 1969.0. Naukova 
+    Dumka, Kiev. [English translation of the text available].
+ - Vondrak J., Ron C., Pesek I., Cepek A., 1995, Astron. and Astrophys. 
+    297,899-906.
+ - IERS Annual reports
Index: /tags/sj_tags/sj_root_20080929/psLib/share/readme.ser7
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/readme.ser7	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/readme.ser7	(revision 22322)
@@ -0,0 +1,7 @@
+This file comes from the site:
+
+http://maia.usno.navy.mil (Click on General Information about Earth Orientation)
+
+This file may also be directly downloaded from:
+
+ftp://maia.usno.navy.mil/ser7/ser7.dat
Index: /tags/sj_tags/sj_root_20080929/psLib/share/readme.tai_utc
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/readme.tai_utc	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/readme.tai_utc	(revision 22322)
@@ -0,0 +1,7 @@
+This file comes from the site:
+
+http://maia.usno.navy.mil (Click on table of leap seconds since 1961)
+
+This file may also be directly downloaded from:
+
+ftp://maia.usno.navy.mil/ser7/tai-utc.dat
Index: /tags/sj_tags/sj_root_20080929/psLib/share/ser7.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/ser7.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/ser7.dat	(revision 22322)
@@ -0,0 +1,380 @@
+#  ser7.dat
+#
+#  This file comes from http://maia.usno.navy.mil (Click on General Information about Earth Orientation). This
+#  file may also be directly downloaded from ftp://maia.usno.navy.mil/ser7/ser7.dat. See readme.ser7 (located
+#  in the psLib/data directory) for details.
+#
+#  @author Ross Harman, MHPCC
+#
+#  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+#  @date $Date: 2006-09-23 02:20:18 $
+#
+#
+#        MJD         PM-x        PM-y       UT1-UTC
+#      (days)      (arcsec)    (arcsec)      (sec)
+#        psF64        psF64       psF64       psF64
+        53244       0.1377      0.5027     -0.45210
+        53245       0.1394      0.5015     -0.45190
+        53246       0.1411      0.5002     -0.45197
+        53247       0.1429      0.4989     -0.45234
+        53248       0.1447      0.4976     -0.45293
+        53249       0.1465      0.4962     -0.45361
+        53250       0.1484      0.4948     -0.45424
+        53251       0.1503      0.4934     -0.45476
+        53252       0.1521      0.4920     -0.45512
+        53253       0.1540      0.4905     -0.45530
+        53254       0.1558      0.4890     -0.45531
+        53255       0.1577      0.4874     -0.45519
+        53256       0.1595      0.4859     -0.45497
+        53257       0.1613      0.4843     -0.45474
+        53258       0.1631      0.4827     -0.45458
+        53259       0.1649      0.4810     -0.45456
+        53260       0.1667      0.4794     -0.45474
+        53261       0.1684      0.4777     -0.45514
+        53262       0.1701      0.4760     -0.45578
+        53263       0.1717      0.4742     -0.45660
+        53264       0.1734      0.4725     -0.45754
+        53265       0.1750      0.4707     -0.45849
+        53266       0.1765      0.4689     -0.45932
+        53267       0.1780      0.4671     -0.45996
+        53268       0.1795      0.4652     -0.46036
+        53269       0.1810      0.4634     -0.46055
+        53270       0.1824      0.4615     -0.46061
+        53271       0.1838      0.4596     -0.46068
+        53272       0.1852      0.4576     -0.46086
+        53273       0.1865      0.4557     -0.46126
+        53274       0.1878      0.4537     -0.46192
+        53275       0.1890      0.4517     -0.46280
+        53276       0.1902      0.4497     -0.46382
+        53277       0.1914      0.4477     -0.46486
+        53278       0.1925      0.4457     -0.46581
+        53279       0.1936      0.4436     -0.46658
+        53280       0.1946      0.4415     -0.46715
+        53281       0.1956      0.4394     -0.46750
+        53282       0.1966      0.4373     -0.46766
+        53283       0.1976      0.4352     -0.46772
+        53284       0.1985      0.4331     -0.46773
+        53285       0.1993      0.4309     -0.46778
+        53286       0.2001      0.4288     -0.46795
+        53287       0.2009      0.4266     -0.46830
+        53288       0.2017      0.4244     -0.46887
+        53289       0.2024      0.4222     -0.46968
+        53290       0.2031      0.4200     -0.47071
+        53291       0.2037      0.4178     -0.47191
+        53292       0.2043      0.4155     -0.47317
+        53293       0.2049      0.4133     -0.47435
+        53294       0.2054      0.4111     -0.47536
+        53295       0.2059      0.4088     -0.47610
+        53296       0.2063      0.4065     -0.47659
+        53297       0.2067      0.4042     -0.47691
+        53298       0.2071      0.4020     -0.47718
+        53299       0.2075      0.3997     -0.47755
+        53300       0.2078      0.3974     -0.47811
+        53301       0.2080      0.3951     -0.47893
+        53302       0.2083      0.3928     -0.47998
+        53303       0.2085      0.3905     -0.48119
+        53304       0.2086      0.3881     -0.48247
+        53305       0.2088      0.3858     -0.48369
+        53306       0.2089      0.3835     -0.48477
+        53307       0.2089      0.3812     -0.48565
+        53308       0.2090      0.3789     -0.48630
+        53309       0.2089      0.3765     -0.48674
+        53310       0.2089      0.3742     -0.48701
+        53311       0.2088      0.3719     -0.48718
+        53312       0.2088      0.3696     -0.48732
+        53313       0.2086      0.3672     -0.48753
+        53314       0.2085      0.3649     -0.48785
+        53315       0.2082      0.3626     -0.48835
+        53316       0.2080      0.3603     -0.48906
+        53317       0.2077      0.3579     -0.48997
+        53318       0.2074      0.3556     -0.49106
+        53319       0.2071      0.3533     -0.49226
+        53320       0.2067      0.3510     -0.49344
+        53321       0.2063      0.3487     -0.49449
+        53322       0.2059      0.3464     -0.49531
+        53323       0.2054      0.3441     -0.49585
+        53324       0.2049      0.3418     -0.49618
+        53325       0.2044      0.3396     -0.49639
+        53326       0.2038      0.3373     -0.49664
+        53327       0.2032      0.3350     -0.49705
+        53328       0.2026      0.3328     -0.49769
+        53329       0.2020      0.3305     -0.49854
+        53330       0.2013      0.3283     -0.49953
+        53331       0.2006      0.3261     -0.50058
+        53332       0.1999      0.3239     -0.50158
+        53333       0.1991      0.3217     -0.50243
+        53334       0.1983      0.3195     -0.50308
+        53335       0.1975      0.3173     -0.50350
+        53336       0.1967      0.3151     -0.50369
+        53337       0.1958      0.3130     -0.50370
+        53338       0.1949      0.3108     -0.50358
+        53339       0.1940      0.3087     -0.50342
+        53340       0.1931      0.3066     -0.50329
+        53341       0.1921      0.3045     -0.50327
+        53342       0.1911      0.3024     -0.50340
+        53343       0.1901      0.3004     -0.50372
+        53344       0.1890      0.2983     -0.50423
+        53345       0.1880      0.2963     -0.50493
+        53346       0.1869      0.2943     -0.50574
+        53347       0.1858      0.2923     -0.50660
+        53348       0.1846      0.2903     -0.50739
+        53349       0.1835      0.2883     -0.50808
+        53350       0.1823      0.2864     -0.50851
+        53351       0.1811      0.2844     -0.50870
+        53352       0.1799      0.2825     -0.50874
+        53353       0.1786      0.2807     -0.50875
+        53354       0.1773      0.2788     -0.50890
+        53355       0.1760      0.2769     -0.50927
+        53356       0.1747      0.2751     -0.50987
+        53357       0.1734      0.2733     -0.51064
+        53358       0.1720      0.2715     -0.51148
+        53359       0.1707      0.2698     -0.51228
+        53360       0.1693      0.2680     -0.51295
+        53361       0.1679      0.2663     -0.51342
+        53362       0.1664      0.2646     -0.51367
+        53363       0.1650      0.2629     -0.51371
+        53364       0.1635      0.2613     -0.51357
+        53365       0.1620      0.2596     -0.51330
+        53366       0.1605      0.2580     -0.51298
+        53367       0.1590      0.2565     -0.51268
+        53368       0.1575      0.2549     -0.51247
+        53369       0.1559      0.2534     -0.51241
+        53370       0.1543      0.2519     -0.51253
+        53371       0.1528      0.2504     -0.51283
+        53372       0.1511      0.2489     -0.51331
+        53373       0.1495      0.2475     -0.51391
+        53374       0.1479      0.2461     -0.51458
+        53375       0.1463      0.2447     -0.51522
+        53376       0.1446      0.2434     -0.51574
+        53377       0.1429      0.2420     -0.51605
+        53378       0.1412      0.2407     -0.51614
+        53379       0.1395      0.2395     -0.51605
+        53380       0.1378      0.2382     -0.51590
+        53381       0.1361      0.2370     -0.51585
+        53382       0.1344      0.2358     -0.51603
+        53383       0.1326      0.2347     -0.51648
+        53384       0.1308      0.2335     -0.51717
+        53385       0.1291      0.2324     -0.51800
+        53386       0.1273      0.2313     -0.51881
+        53387       0.1255      0.2303     -0.51951
+        53388       0.1237      0.2293     -0.52000
+        53389       0.1219      0.2283     -0.52027
+        53390       0.1200      0.2273     -0.52032
+        53391       0.1182      0.2264     -0.52018
+        53392       0.1164      0.2255     -0.51992
+        53393       0.1145      0.2246     -0.51959
+        53394       0.1126      0.2237     -0.51928
+        53395       0.1108      0.2229     -0.51905
+        53396       0.1089      0.2221     -0.51897
+        53397       0.1070      0.2213     -0.51908
+        53398       0.1051      0.2206     -0.51939
+        53399       0.1032      0.2199     -0.51987
+        53400       0.1013      0.2192     -0.52050
+        53401       0.0994      0.2186     -0.52119
+        53402       0.0975      0.2180     -0.52187
+        53403       0.0956      0.2174     -0.52246
+        53404       0.0936      0.2168     -0.52288
+        53405       0.0917      0.2163     -0.52309
+        53406       0.0898      0.2158     -0.52311
+        53407       0.0878      0.2153     -0.52305
+        53408       0.0859      0.2149     -0.52303
+        53409       0.0840      0.2144     -0.52321
+        53410       0.0820      0.2141     -0.52368
+        53411       0.0801      0.2137     -0.52444
+        53412       0.0781      0.2134     -0.52542
+        53413       0.0761      0.2131     -0.52646
+        53414       0.0742      0.2128     -0.52744
+        53415       0.0722      0.2126     -0.52822
+        53416       0.0703      0.2124     -0.52877
+        53417       0.0683      0.2122     -0.52907
+        53418       0.0663      0.2120     -0.52916
+        53419       0.0644      0.2119     -0.52910
+        53420       0.0624      0.2118     -0.52897
+        53421       0.0605      0.2117     -0.52883
+        53422       0.0585      0.2117     -0.52876
+        53423       0.0566      0.2117     -0.52882
+        53424       0.0546      0.2117     -0.52907
+        53425       0.0526      0.2118     -0.52951
+        53426       0.0507      0.2118     -0.53014
+        53427       0.0487      0.2119     -0.53091
+        53428       0.0468      0.2121     -0.53174
+        53429       0.0449      0.2122     -0.53254
+        53430       0.0429      0.2124     -0.53321
+        53431       0.0410      0.2126     -0.53368
+        53432       0.0391      0.2129     -0.53392
+        53433       0.0372      0.2131     -0.53393
+        53434       0.0352      0.2134     -0.53380
+        53435       0.0333      0.2138     -0.53366
+        53436       0.0314      0.2141     -0.53365
+        53437       0.0296      0.2145     -0.53389
+        53438       0.0277      0.2149     -0.53443
+        53439       0.0258      0.2154     -0.53526
+        53440       0.0239      0.2159     -0.53627
+        53441       0.0221      0.2164     -0.53733
+        53442       0.0202      0.2169     -0.53830
+        53443       0.0184      0.2174     -0.53908
+        53444       0.0166      0.2180     -0.53964
+        53445       0.0147      0.2186     -0.53998
+        53446       0.0129      0.2192     -0.54014
+        53447       0.0111      0.2198     -0.54018
+        53448       0.0093      0.2205     -0.54018
+        53449       0.0076      0.2212     -0.54022
+        53450       0.0058      0.2219     -0.54037
+        53451       0.0040      0.2227     -0.54069
+        53452       0.0023      0.2234     -0.54118
+        53453       0.0005      0.2242     -0.54185
+        53454      -0.0012      0.2250     -0.54266
+        53455      -0.0029      0.2258     -0.54356
+        53456      -0.0046      0.2267     -0.54447
+        53457      -0.0063      0.2276     -0.54530
+        53458      -0.0079      0.2285     -0.54598
+        53459      -0.0096      0.2294     -0.54647
+        53460      -0.0112      0.2303     -0.54675
+        53461      -0.0128      0.2313     -0.54688
+        53462      -0.0144      0.2323     -0.54699
+        53463      -0.0160      0.2333     -0.54724
+        53464      -0.0176      0.2343     -0.54775
+        53465      -0.0191      0.2353     -0.54858
+        53466      -0.0207      0.2364     -0.54970
+        53467      -0.0222      0.2375     -0.55097
+        53468      -0.0237      0.2386     -0.55228
+        53469      -0.0252      0.2397     -0.55354
+        53470      -0.0267      0.2408     -0.55465
+        53471      -0.0281      0.2420     -0.55556
+        53472      -0.0295      0.2431     -0.55626
+        53473      -0.0310      0.2443     -0.55677
+        53474      -0.0323      0.2455     -0.55715
+        53475      -0.0337      0.2468     -0.55748
+        53476      -0.0351      0.2480     -0.55785
+        53477      -0.0364      0.2493     -0.55833
+        53478      -0.0377      0.2505     -0.55899
+        53479      -0.0390      0.2518     -0.55988
+        53480      -0.0403      0.2531     -0.56095
+        53481      -0.0415      0.2545     -0.56216
+        53482      -0.0427      0.2558     -0.56347
+        53483      -0.0439      0.2572     -0.56478
+        53484      -0.0451      0.2585     -0.56599
+        53485      -0.0463      0.2599     -0.56699
+        53486      -0.0474      0.2613     -0.56769
+        53487      -0.0485      0.2627     -0.56808
+        53488      -0.0496      0.2641     -0.56824
+        53489      -0.0507      0.2656     -0.56830
+        53490      -0.0517      0.2670     -0.56839
+        53491      -0.0527      0.2685     -0.56860
+        53492      -0.0537      0.2700     -0.56897
+        53493      -0.0547      0.2714     -0.56955
+        53494      -0.0556      0.2729     -0.57032
+        53495      -0.0565      0.2744     -0.57119
+        53496      -0.0574      0.2760     -0.57204
+        53497      -0.0583      0.2775     -0.57277
+        53498      -0.0591      0.2790     -0.57329
+        53499      -0.0600      0.2806     -0.57354
+        53500      -0.0607      0.2821     -0.57350
+        53501      -0.0615      0.2837     -0.57328
+        53502      -0.0622      0.2853     -0.57298
+        53503      -0.0630      0.2869     -0.57261
+        53504      -0.0636      0.2885     -0.57218
+        53505      -0.0643      0.2901     -0.57175
+        53506      -0.0649      0.2917     -0.57143
+        53507      -0.0655      0.2933     -0.57124
+        53508      -0.0661      0.2949     -0.57117
+        53509      -0.0667      0.2966     -0.57124
+        53510      -0.0672      0.2982     -0.57139
+        53511      -0.0677      0.2999     -0.57158
+        53512      -0.0682      0.3015     -0.57166
+        53513      -0.0686      0.3032     -0.57155
+        53514      -0.0690      0.3048     -0.57116
+        53515      -0.0694      0.3065     -0.57057
+        53516      -0.0698      0.3082     -0.56989
+        53517      -0.0701      0.3098     -0.56921
+        53518      -0.0704      0.3115     -0.56860
+        53519      -0.0707      0.3132     -0.56818
+        53520      -0.0710      0.3149     -0.56799
+        53521      -0.0712      0.3166     -0.56800
+        53522      -0.0714      0.3183     -0.56809
+        53523      -0.0716      0.3200     -0.56815
+        53524      -0.0717      0.3217     -0.56808
+        53525      -0.0718      0.3234     -0.56781
+        53526      -0.0719      0.3251     -0.56728
+        53527      -0.0720      0.3268     -0.56651
+        53528      -0.0720      0.3285     -0.56557
+        53529      -0.0720      0.3302     -0.56454
+        53530      -0.0720      0.3319     -0.56347
+        53531      -0.0720      0.3336     -0.56244
+        53532      -0.0719      0.3353     -0.56148
+        53533      -0.0718      0.3370     -0.56060
+        53534      -0.0717      0.3387     -0.55990
+        53535      -0.0715      0.3404     -0.55943
+        53536      -0.0713      0.3421     -0.55916
+        53537      -0.0711      0.3438     -0.55897
+        53538      -0.0709      0.3455     -0.55881
+        53539      -0.0707      0.3472     -0.55859
+        53540      -0.0704      0.3489     -0.55820
+        53541      -0.0701      0.3506     -0.55751
+        53542      -0.0697      0.3523     -0.55650
+        53543      -0.0694      0.3540     -0.55529
+        53544      -0.0690      0.3557     -0.55402
+        53545      -0.0686      0.3573     -0.55279
+        53546      -0.0682      0.3590     -0.55173
+        53547      -0.0677      0.3607     -0.55093
+        53548      -0.0673      0.3623     -0.55037
+        53549      -0.0668      0.3640     -0.54994
+        53550      -0.0662      0.3656     -0.54947
+        53551      -0.0657      0.3673     -0.54884
+        53552      -0.0651      0.3689     -0.54798
+        53553      -0.0645      0.3705     -0.54689
+        53554      -0.0639      0.3721     -0.54560
+        53555      -0.0633      0.3738     -0.54414
+        53556      -0.0626      0.3754     -0.54257
+        53557      -0.0620      0.3770     -0.54101
+        53558      -0.0613      0.3786     -0.53952
+        53559      -0.0605      0.3801     -0.53819
+        53560      -0.0598      0.3817     -0.53705
+        53561      -0.0590      0.3833     -0.53611
+        53562      -0.0583      0.3848     -0.53535
+        53563      -0.0575      0.3864     -0.53475
+        53564      -0.0566      0.3879     -0.53425
+        53565      -0.0558      0.3895     -0.53374
+        53566      -0.0549      0.3910     -0.53315
+        53567      -0.0541      0.3925     -0.53243
+        53568      -0.0532      0.3940     -0.53152
+        53569      -0.0523      0.3955     -0.53042
+        53570      -0.0513      0.3969     -0.52913
+        53571      -0.0504      0.3984     -0.52776
+        53572      -0.0494      0.3998     -0.52644
+        53573      -0.0485      0.4013     -0.52532
+        53574      -0.0475      0.4027     -0.52451
+        53575      -0.0464      0.4041     -0.52400
+        53576      -0.0454      0.4055     -0.52377
+        53577      -0.0444      0.4069     -0.52366
+        53578      -0.0433      0.4083     -0.52344
+        53579      -0.0423      0.4096     -0.52299
+        53580      -0.0412      0.4110     -0.52228
+        53581      -0.0401      0.4123     -0.52130
+        53582      -0.0390      0.4136     -0.52010
+        53583      -0.0378      0.4149     -0.51872
+        53584      -0.0367      0.4162     -0.51725
+        53585      -0.0356      0.4175     -0.51578
+        53586      -0.0344      0.4188     -0.51436
+        53587      -0.0332      0.4200     -0.51310
+        53588      -0.0320      0.4212     -0.51204
+        53589      -0.0309      0.4224     -0.51120
+        53590      -0.0297      0.4236     -0.51055
+        53591      -0.0284      0.4248     -0.51006
+        53592      -0.0272      0.4260     -0.50966
+        53593      -0.0260      0.4271     -0.50922
+        53594      -0.0247      0.4283     -0.50872
+        53595      -0.0235      0.4294     -0.50809
+        53596      -0.0222      0.4305     -0.50729
+        53597      -0.0210      0.4315     -0.50638
+        53598      -0.0197      0.4326     -0.50535
+        53599      -0.0184      0.4336     -0.50431
+        53600      -0.0171      0.4347     -0.50339
+        53601      -0.0158      0.4357     -0.50273
+        53602      -0.0145      0.4367     -0.50244
+        53603      -0.0132      0.4376     -0.50247
+        53604      -0.0119      0.4386     -0.50267
+        53605      -0.0106      0.4395     -0.50286
+        53606      -0.0093      0.4404     -0.50287
+        53607      -0.0079      0.4413     -0.50263
+        53608      -0.0066      0.4422     -0.50217
Index: /tags/sj_tags/sj_root_20080929/psLib/share/ser7.raw
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/ser7.raw	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/ser7.raw	(revision 22322)
@@ -0,0 +1,475 @@
+                                                                                
+      **********************************************************************    
+      *                                                                    *    
+      *                   I E R S   B U L L E T I N - A                    *    
+      *                                                                    *    
+      *           Rapid Service/Prediction of Earth Orientation            *    
+      **********************************************************************    
+      26 August 2004                                       Vol. XVII No. 034    
+      ______________________________________________________________________    
+      GENERAL INFORMATION:                                                      
+         To receive this information electronically, contact:                   
+            ser7@maia.usno.navy.mil or use http://maia.usno.navy.mil/           
+         MJD = Julian Date - 2 400 000.5 days                                   
+         UT2-UT1 = 0.022 sin(2*pi*T) - 0.012 cos(2*pi*T)                        
+                                     - 0.006 sin(4*pi*T) + 0.007 cos(4*pi*T)    
+            where pi = 3.14159265... and T is the date in Besselian years.      
+         TT = TAI + 32.184 seconds                                              
+         DUT1= (UT1-UTC) transmitted with time signals                          
+             =  -0.5 seconds beginning 29 April    2004 at 0000 UTC             
+         Beginning 1 January 1999:                                              
+            TAI-UTC(BIPM) = 32.000 000 seconds                                  
+      **********************************************************************    
+      *                 No leap second will be introduced                  *    
+      *                    in UTC on 31 December 2004.                     *    
+      *       Bulletin A is now compliant with IAU 2000 resolutions        *    
+      *                See notice at the end of this Bulletin A            *    
+      **********************************************************************    
+     ________________________________________________________________________   
+       The contributed observations used in the preparation of this Bulletin    
+       are available at ftp://maia.usno.navy.mil/bulla-data.html.  The          
+       contributed analysis results are based on data from Very Long Baseline   
+       Interferometry (VLBI), Satellite Laser Ranging (SLR), the Global         
+       Positioning System (GPS) satellites, Lunar Laser Ranging (LLR), and      
+       meteorological predictions of variations in Atmospheric Angular          
+       Momentum (AAM).                                                          
+     ________________________________________________________________________   
+                                                                                
+      COMBINED EARTH ORIENTATION PARAMETERS:                                    
+                                                                                
+                              IERS Rapid Service                                
+              MJD      x    error     y    error   UT1-UTC   error              
+                       "      "       "      "        s        s                
+    4  8 20  53237  .12423 .00003  .51109 .00006  -.451871  .000012          
+    4  8 21  53238  .12644 .00003  .51031 .00005  -.452406  .000014          
+    4  8 22  53239  .12829 .00003  .50925 .00005  -.452813  .000014          
+    4  8 23  53240  .13014 .00002  .50785 .00004  -.453011  .000012          
+    4  8 24  53241  .13227 .00002  .50627 .00004  -.452994  .000013          
+    4  8 25  53242  .13439 .00002  .50488 .00003  -.452786  .000012          
+    4  8 26  53243  .13604 .00002  .50376 .00003  -.452446  .000011          
+                                                                                
+      _______________________________________________________________________   
+                                                                                
+      PREDICTIONS:                                                              
+      The following formulas will not reproduce the predictions given below,    
+      but may be used to extend the predictions beyond the end of this table.   
+                                                                                
+      x =   .0530 +  .0660 cos A -  .0075 sin A +  .0182 cos C +  .1564 sin C   
+      y =   .3501 -  .0070 cos A -  .0542 sin A +  .1564 cos C -  .0182 sin C   
+         UT1-UTC =  -.4952 -  .00023 (MJD - 53248) - (UT2-UT1)                  
+                                                                                
+      where A = 2*pi*(MJD-53243)/365.25 and C = 2*pi*(MJD-53243)/435.           
+                                                                                
+         TAI-UTC(MJD 53244) = 32.0                                              
+      The accuracy may be estimated from the expressions:                       
+      S x,y = 0.0042 (MJD-53243)**0.28    S t = 0.0003 (MJD-53248)**0.75        
+      Estimated accuracies are:  Predictions     10 d   20 d   30 d   40 d      
+                                 Polar coord's  0.004  0.006  0.009  0.011      
+                                 UT1-UTC        0.0017 0.0028 0.0039 0.0048     
+                                                                                
+                    MJD      x(arcsec)   y(arcsec)   UT1-UTC(sec)               
+       2004  8 27  53244       0.1377      0.5027     -0.45210              
+       2004  8 28  53245       0.1394      0.5015     -0.45190              
+       2004  8 29  53246       0.1411      0.5002     -0.45197              
+       2004  8 30  53247       0.1429      0.4989     -0.45234              
+       2004  8 31  53248       0.1447      0.4976     -0.45293              
+       2004  9  1  53249       0.1465      0.4962     -0.45361              
+       2004  9  2  53250       0.1484      0.4948     -0.45424              
+       2004  9  3  53251       0.1503      0.4934     -0.45476              
+       2004  9  4  53252       0.1521      0.4920     -0.45512              
+       2004  9  5  53253       0.1540      0.4905     -0.45530              
+       2004  9  6  53254       0.1558      0.4890     -0.45531              
+       2004  9  7  53255       0.1577      0.4874     -0.45519              
+       2004  9  8  53256       0.1595      0.4859     -0.45497              
+       2004  9  9  53257       0.1613      0.4843     -0.45474              
+       2004  9 10  53258       0.1631      0.4827     -0.45458              
+       2004  9 11  53259       0.1649      0.4810     -0.45456              
+       2004  9 12  53260       0.1667      0.4794     -0.45474              
+       2004  9 13  53261       0.1684      0.4777     -0.45514              
+       2004  9 14  53262       0.1701      0.4760     -0.45578              
+       2004  9 15  53263       0.1717      0.4742     -0.45660              
+       2004  9 16  53264       0.1734      0.4725     -0.45754              
+       2004  9 17  53265       0.1750      0.4707     -0.45849              
+       2004  9 18  53266       0.1765      0.4689     -0.45932              
+       2004  9 19  53267       0.1780      0.4671     -0.45996              
+       2004  9 20  53268       0.1795      0.4652     -0.46036              
+       2004  9 21  53269       0.1810      0.4634     -0.46055              
+       2004  9 22  53270       0.1824      0.4615     -0.46061              
+       2004  9 23  53271       0.1838      0.4596     -0.46068              
+       2004  9 24  53272       0.1852      0.4576     -0.46086              
+       2004  9 25  53273       0.1865      0.4557     -0.46126              
+       2004  9 26  53274       0.1878      0.4537     -0.46192              
+       2004  9 27  53275       0.1890      0.4517     -0.46280              
+       2004  9 28  53276       0.1902      0.4497     -0.46382              
+       2004  9 29  53277       0.1914      0.4477     -0.46486              
+       2004  9 30  53278       0.1925      0.4457     -0.46581              
+       2004 10  1  53279       0.1936      0.4436     -0.46658              
+       2004 10  2  53280       0.1946      0.4415     -0.46715              
+       2004 10  3  53281       0.1956      0.4394     -0.46750              
+       2004 10  4  53282       0.1966      0.4373     -0.46766              
+       2004 10  5  53283       0.1976      0.4352     -0.46772              
+       2004 10  6  53284       0.1985      0.4331     -0.46773              
+       2004 10  7  53285       0.1993      0.4309     -0.46778              
+       2004 10  8  53286       0.2001      0.4288     -0.46795              
+       2004 10  9  53287       0.2009      0.4266     -0.46830              
+       2004 10 10  53288       0.2017      0.4244     -0.46887              
+       2004 10 11  53289       0.2024      0.4222     -0.46968              
+       2004 10 12  53290       0.2031      0.4200     -0.47071              
+       2004 10 13  53291       0.2037      0.4178     -0.47191              
+       2004 10 14  53292       0.2043      0.4155     -0.47317              
+       2004 10 15  53293       0.2049      0.4133     -0.47435              
+       2004 10 16  53294       0.2054      0.4111     -0.47536              
+       2004 10 17  53295       0.2059      0.4088     -0.47610              
+       2004 10 18  53296       0.2063      0.4065     -0.47659              
+       2004 10 19  53297       0.2067      0.4042     -0.47691              
+       2004 10 20  53298       0.2071      0.4020     -0.47718              
+       2004 10 21  53299       0.2075      0.3997     -0.47755              
+       2004 10 22  53300       0.2078      0.3974     -0.47811              
+       2004 10 23  53301       0.2080      0.3951     -0.47893              
+       2004 10 24  53302       0.2083      0.3928     -0.47998              
+       2004 10 25  53303       0.2085      0.3905     -0.48119              
+       2004 10 26  53304       0.2086      0.3881     -0.48247              
+       2004 10 27  53305       0.2088      0.3858     -0.48369              
+       2004 10 28  53306       0.2089      0.3835     -0.48477              
+       2004 10 29  53307       0.2089      0.3812     -0.48565              
+       2004 10 30  53308       0.2090      0.3789     -0.48630              
+       2004 10 31  53309       0.2089      0.3765     -0.48674              
+       2004 11  1  53310       0.2089      0.3742     -0.48701              
+       2004 11  2  53311       0.2088      0.3719     -0.48718              
+       2004 11  3  53312       0.2088      0.3696     -0.48732              
+       2004 11  4  53313       0.2086      0.3672     -0.48753              
+       2004 11  5  53314       0.2085      0.3649     -0.48785              
+       2004 11  6  53315       0.2082      0.3626     -0.48835              
+       2004 11  7  53316       0.2080      0.3603     -0.48906              
+       2004 11  8  53317       0.2077      0.3579     -0.48997              
+       2004 11  9  53318       0.2074      0.3556     -0.49106              
+       2004 11 10  53319       0.2071      0.3533     -0.49226              
+       2004 11 11  53320       0.2067      0.3510     -0.49344              
+       2004 11 12  53321       0.2063      0.3487     -0.49449              
+       2004 11 13  53322       0.2059      0.3464     -0.49531              
+       2004 11 14  53323       0.2054      0.3441     -0.49585              
+       2004 11 15  53324       0.2049      0.3418     -0.49618              
+       2004 11 16  53325       0.2044      0.3396     -0.49639              
+       2004 11 17  53326       0.2038      0.3373     -0.49664              
+       2004 11 18  53327       0.2032      0.3350     -0.49705              
+       2004 11 19  53328       0.2026      0.3328     -0.49769              
+       2004 11 20  53329       0.2020      0.3305     -0.49854              
+       2004 11 21  53330       0.2013      0.3283     -0.49953              
+       2004 11 22  53331       0.2006      0.3261     -0.50058              
+       2004 11 23  53332       0.1999      0.3239     -0.50158              
+       2004 11 24  53333       0.1991      0.3217     -0.50243              
+       2004 11 25  53334       0.1983      0.3195     -0.50308              
+       2004 11 26  53335       0.1975      0.3173     -0.50350              
+       2004 11 27  53336       0.1967      0.3151     -0.50369              
+       2004 11 28  53337       0.1958      0.3130     -0.50370              
+       2004 11 29  53338       0.1949      0.3108     -0.50358              
+       2004 11 30  53339       0.1940      0.3087     -0.50342              
+       2004 12  1  53340       0.1931      0.3066     -0.50329              
+       2004 12  2  53341       0.1921      0.3045     -0.50327              
+       2004 12  3  53342       0.1911      0.3024     -0.50340              
+       2004 12  4  53343       0.1901      0.3004     -0.50372              
+       2004 12  5  53344       0.1890      0.2983     -0.50423              
+       2004 12  6  53345       0.1880      0.2963     -0.50493              
+       2004 12  7  53346       0.1869      0.2943     -0.50574              
+       2004 12  8  53347       0.1858      0.2923     -0.50660              
+       2004 12  9  53348       0.1846      0.2903     -0.50739              
+       2004 12 10  53349       0.1835      0.2883     -0.50808              
+       2004 12 11  53350       0.1823      0.2864     -0.50851              
+       2004 12 12  53351       0.1811      0.2844     -0.50870              
+       2004 12 13  53352       0.1799      0.2825     -0.50874              
+       2004 12 14  53353       0.1786      0.2807     -0.50875              
+       2004 12 15  53354       0.1773      0.2788     -0.50890              
+       2004 12 16  53355       0.1760      0.2769     -0.50927              
+       2004 12 17  53356       0.1747      0.2751     -0.50987              
+       2004 12 18  53357       0.1734      0.2733     -0.51064              
+       2004 12 19  53358       0.1720      0.2715     -0.51148              
+       2004 12 20  53359       0.1707      0.2698     -0.51228              
+       2004 12 21  53360       0.1693      0.2680     -0.51295              
+       2004 12 22  53361       0.1679      0.2663     -0.51342              
+       2004 12 23  53362       0.1664      0.2646     -0.51367              
+       2004 12 24  53363       0.1650      0.2629     -0.51371              
+       2004 12 25  53364       0.1635      0.2613     -0.51357              
+       2004 12 26  53365       0.1620      0.2596     -0.51330              
+       2004 12 27  53366       0.1605      0.2580     -0.51298              
+       2004 12 28  53367       0.1590      0.2565     -0.51268              
+       2004 12 29  53368       0.1575      0.2549     -0.51247              
+       2004 12 30  53369       0.1559      0.2534     -0.51241              
+       2004 12 31  53370       0.1543      0.2519     -0.51253              
+       2005  1  1  53371       0.1528      0.2504     -0.51283              
+       2005  1  2  53372       0.1511      0.2489     -0.51331              
+       2005  1  3  53373       0.1495      0.2475     -0.51391              
+       2005  1  4  53374       0.1479      0.2461     -0.51458              
+       2005  1  5  53375       0.1463      0.2447     -0.51522              
+       2005  1  6  53376       0.1446      0.2434     -0.51574              
+       2005  1  7  53377       0.1429      0.2420     -0.51605              
+       2005  1  8  53378       0.1412      0.2407     -0.51614              
+       2005  1  9  53379       0.1395      0.2395     -0.51605              
+       2005  1 10  53380       0.1378      0.2382     -0.51590              
+       2005  1 11  53381       0.1361      0.2370     -0.51585              
+       2005  1 12  53382       0.1344      0.2358     -0.51603              
+       2005  1 13  53383       0.1326      0.2347     -0.51648              
+       2005  1 14  53384       0.1308      0.2335     -0.51717              
+       2005  1 15  53385       0.1291      0.2324     -0.51800              
+       2005  1 16  53386       0.1273      0.2313     -0.51881              
+       2005  1 17  53387       0.1255      0.2303     -0.51951              
+       2005  1 18  53388       0.1237      0.2293     -0.52000              
+       2005  1 19  53389       0.1219      0.2283     -0.52027              
+       2005  1 20  53390       0.1200      0.2273     -0.52032              
+       2005  1 21  53391       0.1182      0.2264     -0.52018              
+       2005  1 22  53392       0.1164      0.2255     -0.51992              
+       2005  1 23  53393       0.1145      0.2246     -0.51959              
+       2005  1 24  53394       0.1126      0.2237     -0.51928              
+       2005  1 25  53395       0.1108      0.2229     -0.51905              
+       2005  1 26  53396       0.1089      0.2221     -0.51897              
+       2005  1 27  53397       0.1070      0.2213     -0.51908              
+       2005  1 28  53398       0.1051      0.2206     -0.51939              
+       2005  1 29  53399       0.1032      0.2199     -0.51987              
+       2005  1 30  53400       0.1013      0.2192     -0.52050              
+       2005  1 31  53401       0.0994      0.2186     -0.52119              
+       2005  2  1  53402       0.0975      0.2180     -0.52187              
+       2005  2  2  53403       0.0956      0.2174     -0.52246              
+       2005  2  3  53404       0.0936      0.2168     -0.52288              
+       2005  2  4  53405       0.0917      0.2163     -0.52309              
+       2005  2  5  53406       0.0898      0.2158     -0.52311              
+       2005  2  6  53407       0.0878      0.2153     -0.52305              
+       2005  2  7  53408       0.0859      0.2149     -0.52303              
+       2005  2  8  53409       0.0840      0.2144     -0.52321              
+       2005  2  9  53410       0.0820      0.2141     -0.52368              
+       2005  2 10  53411       0.0801      0.2137     -0.52444              
+       2005  2 11  53412       0.0781      0.2134     -0.52542              
+       2005  2 12  53413       0.0761      0.2131     -0.52646              
+       2005  2 13  53414       0.0742      0.2128     -0.52744              
+       2005  2 14  53415       0.0722      0.2126     -0.52822              
+       2005  2 15  53416       0.0703      0.2124     -0.52877              
+       2005  2 16  53417       0.0683      0.2122     -0.52907              
+       2005  2 17  53418       0.0663      0.2120     -0.52916              
+       2005  2 18  53419       0.0644      0.2119     -0.52910              
+       2005  2 19  53420       0.0624      0.2118     -0.52897              
+       2005  2 20  53421       0.0605      0.2117     -0.52883              
+       2005  2 21  53422       0.0585      0.2117     -0.52876              
+       2005  2 22  53423       0.0566      0.2117     -0.52882              
+       2005  2 23  53424       0.0546      0.2117     -0.52907              
+       2005  2 24  53425       0.0526      0.2118     -0.52951              
+       2005  2 25  53426       0.0507      0.2118     -0.53014              
+       2005  2 26  53427       0.0487      0.2119     -0.53091              
+       2005  2 27  53428       0.0468      0.2121     -0.53174              
+       2005  2 28  53429       0.0449      0.2122     -0.53254              
+       2005  3  1  53430       0.0429      0.2124     -0.53321              
+       2005  3  2  53431       0.0410      0.2126     -0.53368              
+       2005  3  3  53432       0.0391      0.2129     -0.53392              
+       2005  3  4  53433       0.0372      0.2131     -0.53393              
+       2005  3  5  53434       0.0352      0.2134     -0.53380              
+       2005  3  6  53435       0.0333      0.2138     -0.53366              
+       2005  3  7  53436       0.0314      0.2141     -0.53365              
+       2005  3  8  53437       0.0296      0.2145     -0.53389              
+       2005  3  9  53438       0.0277      0.2149     -0.53443              
+       2005  3 10  53439       0.0258      0.2154     -0.53526              
+       2005  3 11  53440       0.0239      0.2159     -0.53627              
+       2005  3 12  53441       0.0221      0.2164     -0.53733              
+       2005  3 13  53442       0.0202      0.2169     -0.53830              
+       2005  3 14  53443       0.0184      0.2174     -0.53908              
+       2005  3 15  53444       0.0166      0.2180     -0.53964              
+       2005  3 16  53445       0.0147      0.2186     -0.53998              
+       2005  3 17  53446       0.0129      0.2192     -0.54014              
+       2005  3 18  53447       0.0111      0.2198     -0.54018              
+       2005  3 19  53448       0.0093      0.2205     -0.54018              
+       2005  3 20  53449       0.0076      0.2212     -0.54022              
+       2005  3 21  53450       0.0058      0.2219     -0.54037              
+       2005  3 22  53451       0.0040      0.2227     -0.54069              
+       2005  3 23  53452       0.0023      0.2234     -0.54118              
+       2005  3 24  53453       0.0005      0.2242     -0.54185              
+       2005  3 25  53454      -0.0012      0.2250     -0.54266              
+       2005  3 26  53455      -0.0029      0.2258     -0.54356              
+       2005  3 27  53456      -0.0046      0.2267     -0.54447              
+       2005  3 28  53457      -0.0063      0.2276     -0.54530              
+       2005  3 29  53458      -0.0079      0.2285     -0.54598              
+       2005  3 30  53459      -0.0096      0.2294     -0.54647              
+       2005  3 31  53460      -0.0112      0.2303     -0.54675              
+       2005  4  1  53461      -0.0128      0.2313     -0.54688              
+       2005  4  2  53462      -0.0144      0.2323     -0.54699              
+       2005  4  3  53463      -0.0160      0.2333     -0.54724              
+       2005  4  4  53464      -0.0176      0.2343     -0.54775              
+       2005  4  5  53465      -0.0191      0.2353     -0.54858              
+       2005  4  6  53466      -0.0207      0.2364     -0.54970              
+       2005  4  7  53467      -0.0222      0.2375     -0.55097              
+       2005  4  8  53468      -0.0237      0.2386     -0.55228              
+       2005  4  9  53469      -0.0252      0.2397     -0.55354              
+       2005  4 10  53470      -0.0267      0.2408     -0.55465              
+       2005  4 11  53471      -0.0281      0.2420     -0.55556              
+       2005  4 12  53472      -0.0295      0.2431     -0.55626              
+       2005  4 13  53473      -0.0310      0.2443     -0.55677              
+       2005  4 14  53474      -0.0323      0.2455     -0.55715              
+       2005  4 15  53475      -0.0337      0.2468     -0.55748              
+       2005  4 16  53476      -0.0351      0.2480     -0.55785              
+       2005  4 17  53477      -0.0364      0.2493     -0.55833              
+       2005  4 18  53478      -0.0377      0.2505     -0.55899              
+       2005  4 19  53479      -0.0390      0.2518     -0.55988              
+       2005  4 20  53480      -0.0403      0.2531     -0.56095              
+       2005  4 21  53481      -0.0415      0.2545     -0.56216              
+       2005  4 22  53482      -0.0427      0.2558     -0.56347              
+       2005  4 23  53483      -0.0439      0.2572     -0.56478              
+       2005  4 24  53484      -0.0451      0.2585     -0.56599              
+       2005  4 25  53485      -0.0463      0.2599     -0.56699              
+       2005  4 26  53486      -0.0474      0.2613     -0.56769              
+       2005  4 27  53487      -0.0485      0.2627     -0.56808              
+       2005  4 28  53488      -0.0496      0.2641     -0.56824              
+       2005  4 29  53489      -0.0507      0.2656     -0.56830              
+       2005  4 30  53490      -0.0517      0.2670     -0.56839              
+       2005  5  1  53491      -0.0527      0.2685     -0.56860              
+       2005  5  2  53492      -0.0537      0.2700     -0.56897              
+       2005  5  3  53493      -0.0547      0.2714     -0.56955              
+       2005  5  4  53494      -0.0556      0.2729     -0.57032              
+       2005  5  5  53495      -0.0565      0.2744     -0.57119              
+       2005  5  6  53496      -0.0574      0.2760     -0.57204              
+       2005  5  7  53497      -0.0583      0.2775     -0.57277              
+       2005  5  8  53498      -0.0591      0.2790     -0.57329              
+       2005  5  9  53499      -0.0600      0.2806     -0.57354              
+       2005  5 10  53500      -0.0607      0.2821     -0.57350              
+       2005  5 11  53501      -0.0615      0.2837     -0.57328              
+       2005  5 12  53502      -0.0622      0.2853     -0.57298              
+       2005  5 13  53503      -0.0630      0.2869     -0.57261              
+       2005  5 14  53504      -0.0636      0.2885     -0.57218              
+       2005  5 15  53505      -0.0643      0.2901     -0.57175              
+       2005  5 16  53506      -0.0649      0.2917     -0.57143              
+       2005  5 17  53507      -0.0655      0.2933     -0.57124              
+       2005  5 18  53508      -0.0661      0.2949     -0.57117              
+       2005  5 19  53509      -0.0667      0.2966     -0.57124              
+       2005  5 20  53510      -0.0672      0.2982     -0.57139              
+       2005  5 21  53511      -0.0677      0.2999     -0.57158              
+       2005  5 22  53512      -0.0682      0.3015     -0.57166              
+       2005  5 23  53513      -0.0686      0.3032     -0.57155              
+       2005  5 24  53514      -0.0690      0.3048     -0.57116              
+       2005  5 25  53515      -0.0694      0.3065     -0.57057              
+       2005  5 26  53516      -0.0698      0.3082     -0.56989              
+       2005  5 27  53517      -0.0701      0.3098     -0.56921              
+       2005  5 28  53518      -0.0704      0.3115     -0.56860              
+       2005  5 29  53519      -0.0707      0.3132     -0.56818              
+       2005  5 30  53520      -0.0710      0.3149     -0.56799              
+       2005  5 31  53521      -0.0712      0.3166     -0.56800              
+       2005  6  1  53522      -0.0714      0.3183     -0.56809              
+       2005  6  2  53523      -0.0716      0.3200     -0.56815              
+       2005  6  3  53524      -0.0717      0.3217     -0.56808              
+       2005  6  4  53525      -0.0718      0.3234     -0.56781              
+       2005  6  5  53526      -0.0719      0.3251     -0.56728              
+       2005  6  6  53527      -0.0720      0.3268     -0.56651              
+       2005  6  7  53528      -0.0720      0.3285     -0.56557              
+       2005  6  8  53529      -0.0720      0.3302     -0.56454              
+       2005  6  9  53530      -0.0720      0.3319     -0.56347              
+       2005  6 10  53531      -0.0720      0.3336     -0.56244              
+       2005  6 11  53532      -0.0719      0.3353     -0.56148              
+       2005  6 12  53533      -0.0718      0.3370     -0.56060              
+       2005  6 13  53534      -0.0717      0.3387     -0.55990              
+       2005  6 14  53535      -0.0715      0.3404     -0.55943              
+       2005  6 15  53536      -0.0713      0.3421     -0.55916              
+       2005  6 16  53537      -0.0711      0.3438     -0.55897              
+       2005  6 17  53538      -0.0709      0.3455     -0.55881              
+       2005  6 18  53539      -0.0707      0.3472     -0.55859              
+       2005  6 19  53540      -0.0704      0.3489     -0.55820              
+       2005  6 20  53541      -0.0701      0.3506     -0.55751              
+       2005  6 21  53542      -0.0697      0.3523     -0.55650              
+       2005  6 22  53543      -0.0694      0.3540     -0.55529              
+       2005  6 23  53544      -0.0690      0.3557     -0.55402              
+       2005  6 24  53545      -0.0686      0.3573     -0.55279              
+       2005  6 25  53546      -0.0682      0.3590     -0.55173              
+       2005  6 26  53547      -0.0677      0.3607     -0.55093              
+       2005  6 27  53548      -0.0673      0.3623     -0.55037              
+       2005  6 28  53549      -0.0668      0.3640     -0.54994              
+       2005  6 29  53550      -0.0662      0.3656     -0.54947              
+       2005  6 30  53551      -0.0657      0.3673     -0.54884              
+       2005  7  1  53552      -0.0651      0.3689     -0.54798              
+       2005  7  2  53553      -0.0645      0.3705     -0.54689              
+       2005  7  3  53554      -0.0639      0.3721     -0.54560              
+       2005  7  4  53555      -0.0633      0.3738     -0.54414              
+       2005  7  5  53556      -0.0626      0.3754     -0.54257              
+       2005  7  6  53557      -0.0620      0.3770     -0.54101              
+       2005  7  7  53558      -0.0613      0.3786     -0.53952              
+       2005  7  8  53559      -0.0605      0.3801     -0.53819              
+       2005  7  9  53560      -0.0598      0.3817     -0.53705              
+       2005  7 10  53561      -0.0590      0.3833     -0.53611              
+       2005  7 11  53562      -0.0583      0.3848     -0.53535              
+       2005  7 12  53563      -0.0575      0.3864     -0.53475              
+       2005  7 13  53564      -0.0566      0.3879     -0.53425              
+       2005  7 14  53565      -0.0558      0.3895     -0.53374              
+       2005  7 15  53566      -0.0549      0.3910     -0.53315              
+       2005  7 16  53567      -0.0541      0.3925     -0.53243              
+       2005  7 17  53568      -0.0532      0.3940     -0.53152              
+       2005  7 18  53569      -0.0523      0.3955     -0.53042              
+       2005  7 19  53570      -0.0513      0.3969     -0.52913              
+       2005  7 20  53571      -0.0504      0.3984     -0.52776              
+       2005  7 21  53572      -0.0494      0.3998     -0.52644              
+       2005  7 22  53573      -0.0485      0.4013     -0.52532              
+       2005  7 23  53574      -0.0475      0.4027     -0.52451              
+       2005  7 24  53575      -0.0464      0.4041     -0.52400              
+       2005  7 25  53576      -0.0454      0.4055     -0.52377              
+       2005  7 26  53577      -0.0444      0.4069     -0.52366              
+       2005  7 27  53578      -0.0433      0.4083     -0.52344              
+       2005  7 28  53579      -0.0423      0.4096     -0.52299              
+       2005  7 29  53580      -0.0412      0.4110     -0.52228              
+       2005  7 30  53581      -0.0401      0.4123     -0.52130              
+       2005  7 31  53582      -0.0390      0.4136     -0.52010              
+       2005  8  1  53583      -0.0378      0.4149     -0.51872              
+       2005  8  2  53584      -0.0367      0.4162     -0.51725              
+       2005  8  3  53585      -0.0356      0.4175     -0.51578              
+       2005  8  4  53586      -0.0344      0.4188     -0.51436              
+       2005  8  5  53587      -0.0332      0.4200     -0.51310              
+       2005  8  6  53588      -0.0320      0.4212     -0.51204              
+       2005  8  7  53589      -0.0309      0.4224     -0.51120              
+       2005  8  8  53590      -0.0297      0.4236     -0.51055              
+       2005  8  9  53591      -0.0284      0.4248     -0.51006              
+       2005  8 10  53592      -0.0272      0.4260     -0.50966              
+       2005  8 11  53593      -0.0260      0.4271     -0.50922              
+       2005  8 12  53594      -0.0247      0.4283     -0.50872              
+       2005  8 13  53595      -0.0235      0.4294     -0.50809              
+       2005  8 14  53596      -0.0222      0.4305     -0.50729              
+       2005  8 15  53597      -0.0210      0.4315     -0.50638              
+       2005  8 16  53598      -0.0197      0.4326     -0.50535              
+       2005  8 17  53599      -0.0184      0.4336     -0.50431              
+       2005  8 18  53600      -0.0171      0.4347     -0.50339              
+       2005  8 19  53601      -0.0158      0.4357     -0.50273              
+       2005  8 20  53602      -0.0145      0.4367     -0.50244              
+       2005  8 21  53603      -0.0132      0.4376     -0.50247              
+       2005  8 22  53604      -0.0119      0.4386     -0.50267              
+       2005  8 23  53605      -0.0106      0.4395     -0.50286              
+       2005  8 24  53606      -0.0093      0.4404     -0.50287              
+       2005  8 25  53607      -0.0079      0.4413     -0.50263              
+       2005  8 26  53608      -0.0066      0.4422     -0.50217              
+      These predictions are based on all announced leap seconds.                
+                                                                                
+    The IERS Conventions recommended software to predict celestial pole         
+    offsets is available at ftp://maia.usno.navy.mil/conventions/chapter5/      
+    ceppred.f and software for the calculation of the dX and dY with            
+    respect to IAU2000A Nutation/Precession Theory can be found at the          
+    http://maia.usno.navy.mil/conv2000.html web site in section chapter 5.      
+                                                                                
+  **************************************************************************    
+       Important Notice - IAU 2000 Resolution Compliancy Information            
+  **************************************************************************    
+                                                                                
+  Resolutions passed at the 24th General Assembly of the International          
+  Astronomical Union (IAU) recommend the implementation of a number of new      
+  procedures concerning the transformation between the celestial and            
+  terrestrial reference systems.  Information regarding these procedures        
+  is available in Chapter 5 of the IERS Conventions 2000 available at           
+  http://maia.usno.navy.mil/conv2000.html.                                      
+                                                                                
+  With these resolutions, beginning on 1 January 2003, IERS Bulletins A and     
+  B will publish new products in addition to those already published.  These    
+  products will include the celestial pole offsets X_obs-X_IAU2000A             
+  and Y_obs-Y_IAU2000A where                                                    
+                                                                                
+  X_obs and Y_obs are the observed X, Y coordinates of the Celestial            
+  Intermediate Pole in the Geocentric Celestial Reference System, and           
+                                                                                
+  X_IAU2000A and Y_IAU2000A are the celestial pole coordinates provided by      
+  using the IAU2000A Precession-Nutation theory.                                
+                                                                                
+  It is expected that Celestial Pole offsets related to the IAU1980 Nutation    
+  Theory will continue to be published through 31 December 2004.  The new       
+  celestial pole offsets related to the new IAU2000A Precession-Nutation        
+  theory will be produced in a separate data file as well as appended to        
+  to this file.                                                                 
+                                                                                
+  **************************************************************************    
+       Important Notice - IAU 2000 Resolution Compliancy Information            
+  **************************************************************************    
+                                                                                
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2a.coeff
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2a.coeff	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2a.coeff	(revision 22322)
@@ -0,0 +1,11 @@
+# Expression for the X coordinate of the CIP in the GCRS based on the IAU2000A 
+# precession-nutation model 
+# 
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# X = polynomial part + non-polynomial part
+# 
+# --------------------------------------------------------------------------------------------------------------
+# Polynomial part (unit microarcsecond)
+-16616.99 +2004191742.88 -427219.05 -198620.54 -46.05 +5.98
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2a.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2a.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2a.dat	(revision 22322)
@@ -0,0 +1,1630 @@
+# Expression for the X coordinate of the CIP in the GCRS based on the IAU2000A 
+# precession-nutation model 
+# 
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# X = polynomial part + non-polynomial part
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# Non-polynomial part (unit microarcsecond)
+# (ARG being for various combination of the fundamental arguments of the nutation theory)
+# 
+#   Sum_i[a_{s,0})_i * sin(ARG) + a_{c,0})_i * cos(ARG)] 
+# 
+# + Sum_i)j=1,4 [a_{s,j})_i * t^j * sin(ARG) + a_{c,j})_i * cos(ARG)] * t^j]
+# 
+# The Table below provides the values for a_{s,j})_i and a_{c,j})_i
+# 
+# The expressions for the fundamental arguments appearing in columns 4 to 8 (luni-solar part) 
+# and in columns 6 to 17 (planetary part) are those of the IERS Conventions 2000
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+#     i    a_{s,j})_i      a_{c,j})_i    l    l'   F    D   Om L_Me L_Ve  L_E L_Ma  L_J L_Sa  L_U L_Ne  p_A
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+0    -6844318.44        1328.67    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+0     -523908.04        -544.76    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0      -90552.22         111.23    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+0       82168.76         -27.64    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+0       58707.02         470.05    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+0       28288.28         -34.69    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+0      -20557.78         -20.84    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+0      -15406.85          15.12    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+0      -11991.74          32.46    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+0       -8584.95           4.42    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+0       -6245.02          -6.68    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+0        5095.50           7.19    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0       -4910.93           0.76    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+0        2521.07          -5.97    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+0        2511.85           1.07    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+0        2372.58           5.93    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0        2307.58          -7.52    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+0       -2053.16           5.13    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+0        1898.27          -0.72    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+0       -1825.49           1.23    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+0       -1534.09           6.29    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+0       -1292.02           0.00    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+0       -1234.96           5.21    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+0        1163.22          -2.94    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+0        1137.48          -0.04    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0        1029.70          -2.63    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+0        -866.48           0.52    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+0        -813.13           0.40    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+0         664.57          -0.40    0    2    0    0    0    0    0    0    0    0    0    0    0    0
+0        -628.24          -0.64    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+0        -603.52           0.44    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+0        -556.26           3.16    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+0        -512.37          -1.47    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+0         506.65           2.54    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+0         438.51          -0.56    2    0   -2    0    0    0    0    0    0    0    0    0    0    0
+0         405.91           0.99    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0        -122.67         203.78    0    0    1   -1    1    0    0   -1    0   -2    5    0    0    0
+0        -305.78           1.75    1    0    2    2    2    0    0    0    0    0    0    0    0    0
+0         300.99          -0.44    0    1    2    0    2    0    0    0    0    0    0    0    0    0
+0        -292.37          -0.32    1    1    0   -2    0    0    0    0    0    0    0    0    0    0
+0         284.09           0.32    0    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+0        -264.02           0.99    0    0    2    2    1    0    0    0    0    0    0    0    0    0
+0         261.54          -0.95    1    0    0    2    0    0    0    0    0    0    0    0    0    0
+0         256.30          -0.28    2    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0        -250.54           0.08    0    0    0    2    1    0    0    0    0    0    0    0    0    0
+0         230.72           0.08    1    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0         229.78          -0.60    2    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+0        -212.82           0.84    2    0    2    0    1    0    0    0    0    0    0    0    0    0
+0         196.64          -0.84    0    0    0    2   -1    0    0    0    0    0    0    0    0    0
+0         188.95          -0.12    0    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+0         187.95          -0.24    1   -1    0    0    0    0    0    0    0    0    0    0    0    0
+0        -160.15         -14.04    1    0    0   -1    0    0    0    0    0    0    0    0    0    0
+0        -172.95          -0.40    0    1    0   -2    0    0    0    0    0    0    0    0    0    0
+0        -168.26           0.20    0    0    0    1    0    0    0    0    0    0    0    0    0    0
+0         161.79           0.24    2    0    0   -2    1    0    0    0    0    0    0    0    0    0
+0         161.34           0.20    1    0   -2    0    0    0    0    0    0    0    0    0    0    0
+0          57.44          95.82    0    0    0    0    0    0    0    0    0    2   -5    0    0   -1
+0         142.16           0.20    0    1    2   -2    1    0    0    0    0    0    0    0    0    0
+0        -134.81           0.20    1    1    0    0    0    0    0    0    0    0    0    0    0    0
+0         132.81          -0.52    1    0    2    0    0    0    0    0    0    0    0    0    0    0
+0        -130.31           0.04    1   -1    0   -1    0    0    0    0    0    0    0    0    0    0
+0         121.98          -0.08    2    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+0        -115.40           0.60    3    0    2    0    2    0    0    0    0    0    0    0    0    0
+0        -114.49           0.32    1   -1    2    0    2    0    0    0    0    0    0    0    0    0
+0         112.14           0.28    1    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0         105.29           0.44    0    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0          98.69          -0.28    1    1    2    0    2    0    0    0    0    0    0    0    0    0
+0          91.31          -0.40    2    0    0    0   -1    0    0    0    0    0    0    0    0    0
+0          86.74          -0.08    2    0    0    0    1    0    0    0    0    0    0    0    0    0
+0         -18.38          63.80    0    0    0    0    0    0    0    4   -8    3    0    0    0    0
+0          82.14           0.00    0    0    0    0    0    0    3   -5    0    0    0    0    0   -2
+0          79.03          -0.24    1    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00         -79.08    0    1   -1    1   -1    0    0    0    0    0    0    0    0    0
+0         -78.56           0.00    1    0    0    0    2    0    0    0    0    0    0    0    0    0
+0          47.73          23.79    0    0    1   -1    1    0   -8   12    0    0    0    0    0    0
+0          66.03          -0.20    0    0    2    1    2    0    0    0    0    0    0    0    0    0
+0          62.65          -0.24    3    0    0    0    0    0    0    0    0    0    0    0    0    0
+0          60.50           0.36    1    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0          59.07           0.00    0    0    0    0    0    0    1   -1    0    0    0    0    0    0
+0          57.28           0.00    0    0    0    0    0    0    0    8  -16    4    5    0    0    0
+0         -55.66           0.16    1    0    0    0   -2    0    0    0    0    0    0    0    0    0
+0         -54.81          -0.08    2    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0         -53.22          -0.20    1    0    0   -4    0    0    0    0    0    0    0    0    0    0
+0         -52.95           0.32    1    0    2    2    1    0    0    0    0    0    0    0    0    0
+0         -52.27           0.00    1   -1    0   -1   -1    0    0    0    0    0    0    0    0    0
+0          51.32           0.00    1    1    2   -2    2    0    0    0    0    0    0    0    0    0
+0         -51.00          -0.12    2    0    0   -4    0    0    0    0    0    0    0    0    0    0
+0          51.02           0.00    0    2   -2    2   -1    0    0    0    0    0    0    0    0    0
+0         -48.65          -1.15    0    0    0    0    0    0    0    1    0   -1    0    0    0    0
+0          48.29           0.20    2    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0         -46.38           0.00    0    0    0    0    0    0    0    0    0    2    0    0    0    2
+0         -45.59          -0.12    1    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+0         -43.76           0.36    2    0    2    2    2    0    0    0    0    0    0    0    0    0
+0         -40.58          -1.00    1    0    0   -1   -1    0    0    0    0    0    0    0    0    0
+0           0.00         -41.53    1    0   -1    0   -1    0    0    0    0    0    0    0    0    0
+0          40.54          -0.04    2    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0          40.33          -0.04    2    1    0   -2    0    0    0    0    0    0    0    0    0    0
+0         -38.57           0.08    1    0    0    2    1    0    0    0    0    0    0    0    0    0
+0          37.75           0.04    1   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+0          37.15          -0.12    3    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0          36.68          -0.04    0    0    4   -2    2    0    0    0    0    0    0    0    0    0
+0         -18.30         -17.30    0    0    0    0    1    0    0   -1    2    0    0    0    0    0
+0         -17.86          17.10    0    0    0    0    0    0    0    1   -2    0    0    0    0    0
+0         -34.81           0.04    0    1   -2    2    0    0    0    0    0    0    0    0    0    0
+0         -33.22           0.08    0    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+0          32.43          -0.04    0    1    2    0    1    0    0    0    0    0    0    0    0    0
+0         -30.47           0.04    1    0    2   -2    0    0    0    0    0    0    0    0    0    0
+0         -29.53           0.04    1    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          28.50          -0.08    2    0   -2    0    1    0    0    0    0    0    0    0    0    0
+0          28.35          -0.16    0    1    0    0    2    0    0    0    0    0    0    0    0    0
+0         -28.00           0.00    0    0    2   -1    2    0    0    0    0    0    0    0    0    0
+0         -27.61           0.20    0    0    2    4    2    0    0    0    0    0    0    0    0    0
+0         -26.77           0.08    0    1    0    2    0    0    0    0    0    0    0    0    0    0
+0          26.54          -0.12    0    0    2    0   -1    0    0    0    0    0    0    0    0    0
+0          26.54           0.04    0    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+0         -26.17           0.00    0    1    2   -2    0    0    0    0    0    0    0    0    0    0
+0         -25.42          -0.08    1    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+0         -16.91           8.43    0    0    0    0    0    0    8  -13    0    0    0    0    0   -1
+0           0.32          24.42    0    0    0    0    0    0    2   -3    0    0    0    0    0    0
+0         -19.53           5.09    0    0    0    0    0    0    0    0    0    2   -5    0    0    0
+0         -23.79           0.00    0    0    0    0    0    0    2   -2    0    0    0    0    0    0
+0          23.66           0.00    1   -1    0   -1   -2    0    0    0    0    0    0    0    0    0
+0         -23.47           0.16    1   -1    2    2    2    0    0    0    0    0    0    0    0    0
+0          23.39          -0.12    2    0    0    2    0    0    0    0    0    0    0    0    0    0
+0         -23.49           0.00    0    1    0    0   -2    0    0    0    0    0    0    0    0    0
+0         -23.28          -0.08    1    1    0   -2    1    0    0    0    0    0    0    0    0    0
+0         -22.99           0.04    1    0   -2    2    0    0    0    0    0    0    0    0    0    0
+0         -22.67          -0.08    1   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           9.35          13.29    0    0    0    0    0    0    8  -13    0    0    0    0    0    0
+0          22.47          -0.04    0    1    0    1    0    0    0    0    0    0    0    0    0    0
+0           4.89         -16.55    0    0    0    0    0    0    0    2   -8    3    0    0    0   -2
+0           4.89         -16.51    0    0    0    0    0    0    0    6   -8    3    0    0    0    2
+0          21.28          -0.08    0    1    2    2    2    0    0    0    0    0    0    0    0    0
+0          20.57           0.64    0    0    0    0    0    0    0    3    0   -1    0    0    0    2
+0          21.01           0.00    1   -1    0    0    1    0    0    0    0    0    0    0    0    0
+0           1.23         -19.13    0    0    1   -1    1    0    0   -1    0    2   -5    0    0    0
+0         -19.97           0.12    3    0    2    0    1    0    0    0    0    0    0    0    0    0
+0          19.65          -0.08    0    0    0    4    0    0    0    0    0    0    0    0    0    0
+0          19.58          -0.12    1    0    0    2   -1    0    0    0    0    0    0    0    0    0
+0          19.61          -0.08    1   -1    0    2    0    0    0    0    0    0    0    0    0    0
+0         -19.41           0.08    2   -1    2    0    2    0    0    0    0    0    0    0    0    0
+0         -19.49           0.00    0    0    0    0    0    0    4   -6    0    0    0    0    0   -2
+0         -18.64           0.00    0    0    0    2    2    0    0    0    0    0    0    0    0    0
+0          18.58           0.04    1    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0         -18.42           0.00    1   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+0          18.22           0.00    0    0    0    0    0    0    2   -4    0    0    0    0    0   -2
+0          -0.72         -17.34    0    0    2   -2    1    0   -5    6    0    0    0    0    0    0
+0         -18.02          -0.04    1    0    2   -4    1    0    0    0    0    0    0    0    0    0
+0          17.74           0.08    0    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0          17.46           0.00    2    0    0   -2    0    0    0   -2    0    2    0    0    0    0
+0         -17.42           0.00    0    3    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -6.60          10.70    0    0    0    0    0    0    0    1    0   -2    0    0    0    0
+0          16.43           0.52    0    0    0    0    0    0    0    2    0   -2    0    0    0    0
+0         -16.75           0.04    1   -1    2    0    1    0    0    0    0    0    0    0    0    0
+0          16.55          -0.08    0    0    2    2    0    0    0    0    0    0    0    0    0    0
+0          16.39          -0.08    2    1    2    0    2    0    0    0    0    0    0    0    0    0
+0          13.88          -2.47    2    0    0   -2    0    0    0   -2    0    3    0    0    0    0
+0          15.69           0.00    1    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+0         -15.52           0.00    0    0    0    1    1    0    0    0    0    0    0    0    0    0
+0           3.34          11.86    0    0    0    0    1    0    0   -4    8   -3    0    0    0    0
+0          14.72          -0.32    0    0    0    0    0    0    2    0    0    0    0    0    0    2
+0          14.92          -0.04    2   -1    0    0    0    0    0    0    0    0    0    0    0    0
+0          -3.26          11.62    0    0    0    0    1    0    0    4   -8    3    0    0    0    0
+0         -14.64           0.00    0    0    0    0    0    0    0    2   -2    0    0    0    0    0
+0           0.00          14.47    1    0   -1    0   -2    0    0    0    0    0    0    0    0    0
+0         -14.37           0.00    1    1    0    0    1    0    0    0    0    0    0    0    0    0
+0          14.32          -0.04    1    1    2    0    1    0    0    0    0    0    0    0    0    0
+0         -14.10           0.04    1    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+0          10.86           3.18    0    0    1   -1    1    0    0    0   -2    0    0    0    0    0
+0         -10.58          -3.10    0    0    1   -1    0    0    0    0   -2    0    0    0    0    0
+0          -3.62           9.86    0    0    0    0    0    0    0    0    0    1    0    0    0    0
+0         -13.48           0.00    0    0    0    0    0    0    1    1    0    0    0    0    0    2
+0          13.41          -0.04    1    0    2    1    2    0    0    0    0    0    0    0    0    0
+0          13.32          -0.08    2    0    2    0    0    0    0    0    0    0    0    0    0    0
+0         -13.33          -0.04    0    1    0   -2    1    0    0    0    0    0    0    0    0    0
+0         -13.29           0.00    1    0    2   -1    2    0    0    0    0    0    0    0    0    0
+0          -0.20          13.05    0    0    0    0    0    0    3   -4    0    0    0    0    0    0
+0           0.00          13.13    1    0   -1    0    0    0    0    0    0    0    0    0    0    0
+0          -8.99           4.02    1    0    0    0    0    0  -18   16    0    0    0    0    0    0
+0         -12.93           0.04    1    0    0    1    0    0    0    0    0    0    0    0    0    0
+0           2.03          10.82    0    0    0    0    0    0    0    0    0    2    0    0    0    1
+0         -12.78           0.04    0    0    0    1   -1    0    0    0    0    0    0    0    0    0
+0          12.24           0.04    1    0    0   -2    2    0    0    0    0    0    0    0    0    0
+0           8.71           3.54    1    0    0    0    0    0  -10    3    0    0    0    0    0    0
+0          11.98          -0.04    1   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+0         -11.38           0.04    2    1    0    0    0    0    0    0    0    0    0    0    0    0
+0         -11.30           0.00    2    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+0          11.14          -0.04    0    0    2    1    1    0    0    0    0    0    0    0    0    0
+0          10.98           0.00    0    3    0    0    0    0    0    0    0    0    0    0    0    0
+0         -10.98           0.00    1    2    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.44         -10.38    0    0    0    0    0    0    5   -8    0    0    0    0    0   -2
+0          10.46           0.08    1    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0         -10.42           0.00    0    0    0    0    0    0    0    1    0    1    0    0    0    2
+0         -10.30           0.08    4    0    2    0    2    0    0    0    0    0    0    0    0    0
+0           6.92           3.34    0    0    1   -1    1    0    0   -1    0    0   -1    0    0    0
+0          10.07           0.04    1    0   -2    0    1    0    0    0    0    0    0    0    0    0
+0          10.02           0.00    2    1    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -9.75           0.04    0    1    2    1    2    0    0    0    0    0    0    0    0    0
+0           9.75           0.00    1    1    2   -2    1    0    0    0    0    0    0    0    0    0
+0           9.67          -0.04    1    0    4   -2    2    0    0    0    0    0    0    0    0    0
+0          -1.99           7.72    0    0    1   -1    1    0    0   -1    0   -1    0    0    0    0
+0           0.40           9.27    0    0    2   -2    0    0   -5    6    0    0    0    0    0    0
+0          -3.42           6.09    0    0    0    0    0    0    0    2   -4    0    0    0    0    0
+0           0.56          -8.67    0    0    0    0    0    0    0    0    0    2   -5    0    0    1
+0          -9.19           0.00    2    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           9.11           0.00    1    0   -2    1   -1    0    0    0    0    0    0    0    0    0
+0           9.07           0.00    2   -2    0   -2    0    0    0    0    0    0    0    0    0    0
+0           1.63           6.96    0    0    0    0    0    0    8  -13    0    0    0    0    0   -2
+0          -8.47           0.00    0    2    0   -2    0    0    0    0    0    0    0    0    0    0
+0          -8.28           0.04    1    1    0    0   -1    0    0    0    0    0    0    0    0    0
+0           8.27           0.04    2    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0          -8.04           0.00    0    0    0    0    0    0    5   -7    0    0    0    0    0   -2
+0           7.91           0.00    0    1    0    2    1    0    0    0    0    0    0    0    0    0
+0          -7.84          -0.04    1    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+0          -7.64           0.08    2    0    2    2    1    0    0    0    0    0    0    0    0    0
+0           5.21          -2.51    1    0    2    0    2    0    0    1    0    0    0    0    0    0
+0          -5.77           1.87    0    0    0    0    0    0    3   -5    0    0    0    0    0    0
+0           5.01          -2.51    1    0   -2    0   -2    0    0    4   -8    3    0    0    0    0
+0          -7.48           0.00    0    0    2   -3    2    0    0    0    0    0    0    0    0    0
+0          -7.32          -0.12    0    0    0    0    0    0    0    4    0   -2    0    0    0    2
+0           7.40          -0.04    0    0    4    0    2    0    0    0    0    0    0    0    0    0
+0           7.44           0.00    1    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+0           6.32          -1.11    1    0    0   -1    1    0    0    0    0    0    0    0    0    0
+0          -6.13          -1.19    0    0    0    0    0    0    0    2    0   -1    0    0    0    2
+0           0.20          -6.88    0    0    0    0    0    0    2   -1    0    0    0    0    0    2
+0           6.92           0.04    1    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           6.48          -0.48    0    0    0    0    0    0    0    2    0    1    0    0    0    2
+0          -6.94           0.00    2    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+0           2.47          -4.46    0    0    0    0    0    0    8  -11    0    0    0    0    0   -2
+0          -2.23          -4.65    0    0    0    0    0    0    0    8  -16    4    5    0    0   -2
+0          -1.07          -5.69    0    0    1   -1    1    0    0   -1    0    2    0    0    0    0
+0           4.97          -1.71    0    0    0    0    0    0    0    8  -16    4    5    0    0    2
+0           5.57           1.07    0    0    1   -1    1    0   -5    7    0    0    0    0    0    0
+0          -6.48           0.08    1    0    2    4    2    0    0    0    0    0    0    0    0    0
+0           2.03           4.53    0    0    0    0    0    0    0    0    0    0    2    0    0    1
+0           4.10          -2.39    1    0    0   -2    0    0   19  -21    3    0    0    0    0    0
+0           0.00          -6.44    0    0    1    0    1    0    0    0    0    0    0    0    0    0
+0          -6.40           0.00    3    0    0   -4    0    0    0    0    0    0    0    0    0    0
+0           6.32           0.00    1    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+0           2.67          -3.62    0    0    0    0    0    0    0    3    0   -2    0    0    0    2
+0          -1.91          -4.38    0    0    1   -1    1    0    0   -1    0    0    2    0    0    0
+0          -2.43          -3.82    0    0    0    0    0    0    8  -15    0    0    0    0    0   -2
+0           6.20           0.00    0    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          -3.38          -2.78    0    0    0    0    0    0    0    1    2    0    0    0    0    2
+0          -6.12           0.04    0    0    0    4    1    0    0    0    0    0    0    0    0    0
+0          -6.09          -0.04    0    0    2   -4    1    0    0    0    0    0    0    0    0    0
+0          -6.01          -0.04    1    1    0   -4    0    0    0    0    0    0    0    0    0    0
+0           3.18          -2.82    0    0    0    0    0    0    0    3   -2    0    0    0    0    2
+0          -5.05           0.84    0    0    0    0    0    0    0    0    0    3    0    0    0    2
+0           5.85           0.00    3    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0           5.69          -0.12    0    0    0    0    0    0    0    4   -2    0    0    0    0    2
+0           5.73          -0.04    1    1    2    2    2    0    0    0    0    0    0    0    0    0
+0           5.61           0.00    0    0    4   -2    1    0    0    0    0    0    0    0    0    0
+0           5.49           0.00    2    0    0   -2    0    0   -3    3    0    0    0    0    0    0
+0          -5.33           0.04    3    0    2    2    2    0    0    0    0    0    0    0    0    0
+0          -5.29           0.00    0    0    0    0    0    0    0    0    0    0    2    0    0    2
+0           5.25           0.00    2    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.99           4.22    0    0    0    0    0    0    4   -6    0    0    0    0    0   -1
+0          -0.99           4.22    0    0    0    0    0    0    1   -1    0    0    0    0    0   -1
+0           0.00           5.21    1    0    0   -1    0    0   -3    4    0    0    0    0    0    0
+0           5.13           0.04    0    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0          -4.90           0.00    2    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+0          -3.10           1.79    0    0    0    0    0    0    0    3   -4    0    0    0    0    0
+0          -4.81           0.04    0    0    2    4    1    0    0    0    0    0    0    0    0    0
+0          -4.75           0.00    0    0    0    2   -2    0    0    0    0    0    0    0    0    0
+0           4.70          -0.04    3    0    0    0   -1    0    0    0    0    0    0    0    0    0
+0          -4.69           0.00    0    0    0    0    0    0    0    1    0   -3    0    0    0   -2
+0          -4.65           0.00    0    0    0    0    0    0    6   -8    0    0    0    0    0   -2
+0           4.65           0.00    0    0    2   -2    1    0   -3    3    0    0    0    0    0    0
+0          -4.57           0.00    2    1    0   -4    0    0    0    0    0    0    0    0    0    0
+0           4.49          -0.04    4    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -4.53           0.00    0    0    1   -1    1    0    0    3   -8    3    0    0    0    0
+0           0.00          -4.53    0    0    0    0    0    0    3   -2    0    0    0    0    0    2
+0           0.00          -4.53    0    0    0    0    0    0    2   -5    0    0    0    0    0   -2
+0          -4.53           0.00    2    0    0    2    1    0    0    0    0    0    0    0    0    0
+0           4.50           0.00    1   -1    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          -4.49           0.00    0    0    0    0    0    0    1   -3    0    0    0    0    0   -2
+0           1.83           2.63    0    0    0    0    1    0    8  -13    0    0    0    0    0    0
+0           4.38           0.00    2    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.88          -3.46    0    0    0    0    0    0    1   -2    0    0    0    0    0    0
+0          -2.70           1.55    0    0    0    0    0    0    0    2   -3    0    0    0    0    0
+0          -4.22           0.00    0    0    2   -1    1    0    0    0    0    0    0    0    0    0
+0          -4.10          -0.12    1    1    0    2    0    0    0    0    0    0    0    0    0    0
+0           3.54          -0.64    2    0    0   -2    1    0    0   -2    0    3    0    0    0    0
+0          -3.50           0.68    0    0    0    0    0    0    5   -8    0    0    0    0    0   -1
+0           4.18           0.00    1   -1   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           4.14           0.00    1    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           4.10           0.00    1    0    0   -3    0    0    0    0    0    0    0    0    0    0
+0          -4.06           0.00    2    0    0   -4    1    0    0    0    0    0    0    0    0    0
+0           2.70          -1.35    1    0    0    0   -1    0  -18   16    0    0    0    0    0    0
+0          -4.04           0.00    2    0    0   -2    2    0    0    0    0    0    0    0    0    0
+0          -3.98          -0.04    1    0    0   -4    1    0    0    0    0    0    0    0    0    0
+0          -3.98           0.04    1   -1    2    2    1    0    0    0    0    0    0    0    0    0
+0           4.02           0.00    2    0    2   -4    1    0    0    0    0    0    0    0    0    0
+0           3.94           0.00    0    0    1   -1    1    0    0   -5    8   -3    0    0    0    0
+0           0.84          -3.10    0    0    1   -1    0    0    0   -1    0   -1    0    0    0    0
+0           3.30           0.60    0    0    0    0    0    0    0    2    0   -3    0    0    0    0
+0          -1.59           2.27    0    0    0    0    1    0   -8   13    0    0    0    0    0    0
+0          -3.66          -0.20    2   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+0          -3.10          -0.72    2    0    0   -2    0    0   -6    8    0    0    0    0    0    0
+0          -3.82           0.00    1   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+0          -3.62          -0.16    2    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+0          -3.74           0.00    0    1   -2    1   -2    0    0    0    0    0    0    0    0    0
+0           3.74           0.00    4    0    0    0    0    0    0    0    0    0    0    0    0    0
+0          -3.74           0.00    0    2   -2    2    0    0    0    0    0    0    0    0    0    0
+0          -3.71           0.00    0    2    0    0    1    0    0    0    0    0    0    0    0    0
+0           3.02           0.68    0    0    0    0    1    0    0    0    0   -2    5    0    0    0
+0           3.70           0.00    1    0   -4    2   -2    0    0    0    0    0    0    0    0    0
+0           3.30           0.40    0    2   -2    0   -2    0    0    0    0    0    0    0    0    0
+0          -3.66           0.04    2   -1    2    2    2    0    0    0    0    0    0    0    0    0
+0           3.66           0.04    0    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0          -3.62           0.00    1    0   -2   -3   -2    0    0    0    0    0    0    0    0    0
+0          -3.61           0.00    2    0    0    0    2    0    0    0    0    0    0    0    0    0
+0          -2.90           0.68    0    0    0    0    1    0    0    0    0    2   -5    0    0    0
+0           0.80          -2.78    0    0    0    0    1    0    0    0    0    1    0    0    0    0
+0           3.54           0.00    0    0    0    0    0    0    3   -3    0    0    0    0    0    0
+0          -3.54           0.00    0    0    0    0    0    0    0    2    0    0    0    0    0    2
+0          -3.50           0.00    2    0    2   -2    0    0    0    0    0    0    0    0    0    0
+0           3.45           0.00    0    2    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -3.42    0    0    0    0    0    0    0    6  -16    4    5    0    0   -2
+0           3.38           0.00    1   -2    0    0    0    0    0    0    0    0    0    0    0    0
+0           2.27          -1.11    1    0    0    0    1    0  -18   16    0    0    0    0    0    0
+0          -3.34           0.00    1   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           3.34           0.00    0    1    2    2    1    0    0    0    0    0    0    0    0    0
+0          -3.30           0.01    0    0    2    0    3    0    0    0    0    0    0    0    0    0
+0           3.31           0.00    1    0    2    0   -1    0    0    0    0    0    0    0    0    0
+0           3.30           0.00    3    0    0    0    1    0    0    0    0    0    0    0    0    0
+0          -3.30           0.00    1    0   -2   -1   -2    0    0    0    0    0    0    0    0    0
+0          -1.39          -1.91    0    0    0    0    0    0    0    1    0    2    0    0    0    2
+0           3.30           0.00    0    0    0    0    0    0    4   -4    0    0    0    0    0    0
+0           3.26           0.00    2    1    0   -2    1    0    0    0    0    0    0    0    0    0
+0           3.26           0.00    1    0    0    4    0    0    0    0    0    0    0    0    0    0
+0           3.22          -0.04    1    0    2    2    0    0    0    0    0    0    0    0    0    0
+0          -3.26           0.00    2   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           2.51          -0.64    0    0    0    0    0    0    3   -7    0    0    0    0    0   -2
+0           3.14           0.00    2    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+0          -2.63          -0.48    0    0    1   -1    1    0    0   -1    0    0    1    0    0    0
+0           3.10           0.00    3    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+0          -3.06           0.00    2   -1    2    0    1    0    0    0    0    0    0    0    0    0
+0           2.94          -0.12    1   -2    0   -2    0    0    0    0    0    0    0    0    0    0
+0           3.06           0.00    2    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           2.98    0    0    1    0    0    0    0    0    0    0    0    0    0    0
+0           2.98           0.00    1    0    2   -4    0    0    0    0    0    0    0    0    0    0
+0           2.07           0.91    0    0    0    0    0    0    0    0    0    1    0    0    0    2
+0          -2.98           0.00    0    0    0    0    0    0    0    1    0    0   -1    0    0    0
+0           2.94           0.00    0    2    2    0    2    0    0    0    0    0    0    0    0    0
+0          -2.94           0.00    0    0    0    0    0    0    7   -9    0    0    0    0    0   -2
+0          -2.94           0.00    0    0    0    0    0    0    0    0    2    0    0    0    0    2
+0          -2.90           0.00    1   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.56          -2.35    0    0    0    0    0    0    2   -4    0    0    0    0    0   -1
+0          -1.47           1.39    0    0    0    0    1    0    0    1   -2    0    0    0    0    0
+0           2.80           0.00    1    0    2   -4    2    0    0    0    0    0    0    0    0    0
+0          -2.74           0.00    2    0    2   -1    2    0    0    0    0    0    0    0    0    0
+0          -0.12           2.63    0    0    0    0    0    0    4   -7    0    0    0    0    0   -2
+0           2.15          -0.60    0    0    0    0    0    0    3   -3    0    0    0    0    0    2
+0          -2.70           0.00    1    1   -2    1   -1    0    0    0    0    0    0    0    0    0
+0           1.79          -0.88    0    0    0    0    0    0    0    8  -15    0    0    0    0    0
+0          -0.48           2.19    0    0    0    0    0    0    2   -2    0    0    0    0    0   -1
+0           0.44           2.23    0    0    0    0    0    0    0    0    0    0    1    0    0    0
+0           0.52           2.07    0    0    0    0    0    0    0    0    0    1    0    0    0   -1
+0          -2.59           0.00    0    1    0   -4    0    0    0    0    0    0    0    0    0    0
+0           2.55           0.00    2    1    2    0    1    0    0    0    0    0    0    0    0    0
+0          -1.11           1.43    0    0    0    0    0    0    0    1    0   -3    0    0    0    0
+0          -2.51           0.00    3   -1    2    0    2    0    0    0    0    0    0    0    0    0
+0          -2.51           0.00    2    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           2.51           0.00    1    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -2.50    1    0   -1    0   -3    0    0    0    0    0    0    0    0    0
+0           2.47           0.00    1   -1    0   -2    1    0    0    0    0    0    0    0    0    0
+0           2.11          -0.36    2    0    0   -2   -1    0    0   -2    0    3    0    0    0    0
+0           1.67           0.80    0    0    1   -1    0    0    0   -1    0    0   -1    0    0    0
+0           2.46           0.00    0    2    0    0   -1    0    0    0    0    0    0    0    0    0
+0          -2.43           0.00    1    0   -2    1    0    0    0    0    0    0    0    0    0    0
+0          -2.39           0.00    1    0    2   -3    2    0    0    0    0    0    0    0    0    0
+0          -1.83           0.56    0    0    0    0    0    0    4   -6    0    0    0    0    0    0
+0          -0.44          -1.95    0    0    0    0    0    0    3   -5    0    0    0    0    0   -1
+0           0.24           2.15    0    0    0    0    0    0    0    0    0    1    0    0    0    1
+0           2.39           0.00    2    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+0           2.35           0.00    0    0    0    0    0    0    5   -5    0    0    0    0    0    0
+0           2.27           0.00    1    0    2    1    1    0    0    0    0    0    0    0    0    0
+0          -2.22           0.00    2    0    0    0   -2    0    0    0    0    0    0    0    0    0
+0          -1.03          -1.15    1    0    0   -1    1    0    0   -1    0    2    0    0    0    0
+0           1.87           0.32    0    0    0    0    0    0    0    0    0    0    1    0    0    1
+0          -0.32          -1.87    0    0    1   -1    1    0    0   -2    2    0    0    0    0    0
+0           2.15           0.00    2    0    0   -2    1    0    0   -2    0    2    0    0    0    0
+0          -0.80           1.35    0    0    0    0    0    0    0    3   -5    0    0    0    0    0
+0           2.11           0.00    3    1    2    0    2    0    0    0    0    0    0    0    0    0
+0          -2.11           0.00    1    1    2    1    2    0    0    0    0    0    0    0    0    0
+0          -0.56          -1.55    0    0    0    0    0    0    5   -8    0    0    0    0    0    0
+0           2.11           0.00    1    0    0   -1    0    0    0   -1    0    1    0    0    0    0
+0          -0.84          -1.27    0    0    0    0    0    0    0    0    4    0    0    0    0    2
+0          -1.99           0.12    0    0    0    0    0    0    8  -10    0    0    0    0    0   -2
+0          -0.24           1.87    0    0    0    0    0    0    0    1   -2    0    0    0    0   -1
+0          -0.24          -1.87    0    0    1   -1    1    0    0   -1    0    1    0    0    0    0
+0          -2.03           0.00    1   -2    2    0    2    0    0    0    0    0    0    0    0    0
+0           2.03           0.00    2   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+0           2.03           0.00    2    0    0   -3    0    0    0    0    0    0    0    0    0    0
+0           2.03           0.00    0    0    0    0    0    0    0    2   -4    0    0    0    0   -2
+0          -0.40           1.59    0    0    1   -1    1    0   -3    4    0    0    0    0    0    0
+0           1.99           0.00    0    0    2   -2    1    0    0   -2    0    2    0    0    0    0
+0           1.95           0.00    0    1    0    1    1    0    0    0    0    0    0    0    0    0
+0           1.95           0.00    3    0    0    2    0    0    0    0    0    0    0    0    0    0
+0           1.91           0.00    0    1    0    1   -1    0    0    0    0    0    0    0    0    0
+0           1.19          -0.72    0    0    0    0    0    0    0    5   -4    0    0    0    0    2
+0           1.87           0.00    2    0    2    1    2    0    0    0    0    0    0    0    0    0
+0           1.87           0.00    1    1    0    1    0    0    0    0    0    0    0    0    0    0
+0          -1.27           0.60    0    0    0    0    0    0    0    0    0    0    1    0    0   -1
+0           0.72          -1.15    0    0    0    0    0    0    0    4   -4    0    0    0    0    2
+0          -0.99           0.88    0    0    0    0    0    0    0    0    0    2    0    0    0    0
+0           1.87           0.00    2   -1    0    2    0    0    0    0    0    0    0    0    0    0
+0          -1.87           0.00    1    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+0          -1.83           0.00    2    0   -4   -2   -2    0    0    0    0    0    0    0    0    0
+0          -1.79           0.00    0    1    0    2   -1    0    0    0    0    0    0    0    0    0
+0          -1.79           0.00    4    0    2    0    1    0    0    0    0    0    0    0    0    0
+0           1.79           0.00    1    2    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00          -1.79    0    0    0    0    0    0    6   -9    0    0    0    0    0   -2
+0          -1.79           0.00    1    1    2   -4    1    0    0    0    0    0    0    0    0    0
+0          -1.75           0.00    0    0    4   -4    2    0    0    0    0    0    0    0    0    0
+0          -1.75           0.00    3    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           1.75           0.00    2    1   -2    0    0    0    0    0    0    0    0    0    0    0
+0          -1.47          -0.28    0    0    0    0    0    0    0    4    0   -3    0    0    0    2
+0          -1.71           0.00    1    0    2   -3    1    0    0    0    0    0    0    0    0    0
+0           1.71           0.00    2    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0           0.32           1.39    0    0    0    0    0    0    5   -7    0    0    0    0    0   -1
+0           0.28          -1.43    0    0    0    0    0    0    0    2    0    0    0    0    0    1
+0          -0.52          -1.19    0    0    0    0    0    0    0    1    0    2   -5    0    0    0
+0           1.67           0.00    2    0    0    2   -1    0    0    0    0    0    0    0    0    0
+0          -1.67           0.00    0    1    2    1    1    0    0    0    0    0    0    0    0    0
+0           0.76          -0.91    0    0    0    0    0    0    2    1    0    0    0    0    0    2
+0          -0.32           1.35    0    0    0    0    0    0    3   -3    0    0    0    0    0   -1
+0          -1.39          -0.28    0    0    1   -1    0    0   -5    7    0    0    0    0    0    0
+0           1.63           0.00    2    1    2   -2    1    0    0    0    0    0    0    0    0    0
+0          -1.59           0.00    1    0    2   -1    1    0    0    0    0    0    0    0    0    0
+0           1.03          -0.56    0    0    0    0    0    0    0    4   -3    0    0    0    0    2
+0           1.59           0.00    0    0    0    0    0    0    6   -6    0    0    0    0    0    0
+0           1.55           0.00    1    0    4   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.28          -1.27    0    0    0    0    0    0    1   -1    0    0    0    0    0    1
+0          -0.64           0.91    0    0    0    0    0    0    0    4   -6    0    0    0    0    0
+0          -0.32          -1.23    0    0    0    0    0    0    1   -3    0    0    0    0    0   -1
+0          -1.55           0.00    1   -1    2   -2    1    0    0    0    0    0    0    0    0    0
+0          -1.51           0.00    1    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+0           1.51           0.00    2   -2    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          -1.51           0.00    2   -1    0   -2    1    0    0    0    0    0    0    0    0    0
+0           1.51           0.00    2    0    0   -1    0    0    0    0    0    0    0    0    0    0
+0           1.47           0.00    2    0    4   -2    2    0    0    0    0    0    0    0    0    0
+0           1.47           0.00    0    0    2    3    2    0    0    0    0    0    0    0    0    0
+0           0.95          -0.52    0    0    0    0    0    0    0    3   -6    0    0    0    0   -2
+0           1.23          -0.24    0    0    0    0    0    0    3   -1    0    0    0    0    0    2
+0           0.60           0.88    2    0    0   -2    0    0    0   -6    8    0    0    0    0    0
+0          -1.47           0.00    1   -1    0    2    1    0    0    0    0    0    0    0    0    0
+0          -1.43           0.00    1    0   -2    2    1    0    0    0    0    0    0    0    0    0
+0           1.43           0.00    1   -1    0    2   -1    0    0    0    0    0    0    0    0    0
+0           1.43           0.00    0    1    4   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.68          -0.76    0    0    1   -1    1    0    0   -1    0    0    0    0    2    0
+0           0.95          -0.48    2    0    2    0    2    0    0    2    0   -3    0    0    0    0
+0          -0.95          -0.48    0    0    2    0    2    0    0    1    0    0    0    0    0    0
+0          -1.19          -0.24    0    0    0    0    0    0    0    1    0   -4    0    0    0   -2
+0           0.36          -1.07    0    0    1   -1    1    0    0   -1    0   -4   10    0    0    0
+0           0.95           0.48    2    0    0   -2    0    0    0   -5    6    0    0    0    0    0
+0           1.43           0.00    1    1    0   -1    0    0    0    0    0    0    0    0    0    0
+0           1.39           0.00    0    0    0    4   -1    0    0    0    0    0    0    0    0    0
+0           1.39           0.00    2   -1    0    0    1    0    0    0    0    0    0    0    0    0
+0          -1.39           0.00    1   -2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0          -1.39           0.00    1    2    0    0    0    0    0    0    0    0    0    0    0    0
+0           0.00           1.39    0    0    0    0    0    0    0    2    0   -1    0    0    0    0
+0          -0.12          -1.27    0    0    0    0    0    0    0    4    0   -1    0    0    0    2
+0           0.56           0.84    0    0    0    0    0    0    0    2   -4    0    0    0    0   -1
+0          -0.44          -0.95    0    0    0    0    0    0    0    0    0    2   -5    0    0   -2
+0           0.32          -1.07    0    0    0    0    0    0    0    1    0    0    0    0    0    0
+0           1.03          -0.36    2    0   -1   -1    0    0    0    3   -7    0    0    0    0    0
+0          -0.28           1.11    0    0    1   -1    1    0   -4    5    0    0    0    0    0    0
+0           0.44           0.95    0    0    1   -1    2    0    0   -1    0    0    2    0    0    0
+0          -1.35           0.00    1   -1   -2    2    0    0    0    0    0    0    0    0    0    0
+0           0.88           0.48    0    0    0    0    0    0    0    1   -8    3    0    0    0   -2
+0          -1.35           0.00    0    0    0    0    0    0    9  -11    0    0    0    0    0   -2
+0           1.35           0.00    1    0    0   -2    0    0    0   -2    0    2    0    0    0    0
+0           1.35           0.00    1    0    0   -1   -2    0    0    0    0    0    0    0    0    0
+0          -1.31           0.00    0    1   -2    2    1    0    0    0    0    0    0    0    0    0
+0           1.31           0.00    1    0   -2    1   -2    0    0    0    0    0    0    0    0    0
+0          -1.19          -0.12    0    0    0    0    0    0    2   -3    0    0    0    0    0   -1
+0           1.27           0.00    0    0    4    0    1    0    0    0    0    0    0    0    0    0
+0           0.40          -0.88    0    0    2   -2    1    0    0   -9   13    0    0    0    0    0
+0           1.27           0.00    1    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0           1.27           0.00    3    1    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.16          -1.11    0    0    0    0    0    0    1    0    0    0    0    0    0    0
+0          -0.84           0.44    2    0    0    0    0    0    0   -2    0    3    0    0    0    0
+0           0.84          -0.44    1    0    2    0    1    0    0   -2    0    3    0    0    0    0
+0           0.84          -0.44    1    0   -2    0   -1    0    0   -1    0    0    0    0    0    0
+0          -1.27           0.00    1    0   -4   -2   -2    0    0    0    0    0    0    0    0    0
+0          -1.27           0.00    1    0    0    4    1    0    0    0    0    0    0    0    0    0
+0           1.27           0.00    1    1    0    2    1    0    0    0    0    0    0    0    0    0
+0          -0.44          -0.84    0    0    0    0    1    0    0   -2    4    0    0    0    0    0
+0           0.00          -1.27    0    0    0    0    1    0    2   -3    0    0    0    0    0    0
+0          -1.27           0.00    2    0    2   -4    2    0    0    0    0    0    0    0    0    0
+0          -1.23           0.00    1   -1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0          -1.23           0.00    1   -2    2    2    2    0    0    0    0    0    0    0    0    0
+0           1.23           0.00    0    0    2   -1    0    0    0    0    0    0    0    0    0    0
+0           0.00           1.23    0    0    0    0    0    0    0    3   -6    0    0    0    0    0
+0          -0.12           1.11    0    0    1    1    1    0    0    1    0    0    0    0    0    0
+0           1.22           0.00    0    0    2   -4    2    0    0    0    0    0    0    0    0    0
+0           1.19           0.00    1    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0          -0.24           0.95    0    0    0    0    0    0    4   -4    0    0    0    0    0   -1
+0          -0.76          -0.44    0    0    0    0    0    0    0    3   -8    3    0    0    0    0
+0           0.91           0.28    0    0    0    0    0    0    0    1   -2    0    0    0    0    1
+0           1.19           0.00    1    1   -2    1    0    0    0    0    0    0    0    0    0    0
+0           1.19           0.00    3    0    2    0    0    0    0    0    0    0    0    0    0    0
+0           0.00           1.19    0    0    1    0    2    0    0    0    0    0    0    0    0    0
+0           1.15           0.00    2   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00           1.15    1    0    0   -1    1    0   -3    4    0    0    0    0    0    0
+0          -1.15           0.00    1    0    2    4    1    0    0    0    0    0    0    0    0    0
+0           1.15           0.00    2   -1   -2    0    0    0    0    0    0    0    0    0    0    0
+0          -1.15           0.00    2    0   -2    2    0    0    0    0    0    0    0    0    0    0
+0           1.15           0.00    3   -1    0    0    0    0    0    0    0    0    0    0    0    0
+0           1.15           0.00    0    0    0    0    0    0    0    3    0    0   -1    0    0    2
+0          -0.95           0.20    0    0    0    0    0    0    6  -10    0    0    0    0    0   -2
+0           0.24           0.91    0    0    0    0    0    0    0    0    0    3    0    0    0    1
+0          -1.15           0.00    1    1    2   -2    0    0    0    0    0    0    0    0    0    0
+0          -1.12           0.00    0    1    2   -2   -1    0    0    0    0    0    0    0    0    0
+0          -1.11           0.00    1    0    0    1   -1    0    0    0    0    0    0    0    0    0
+0          -1.11           0.00    1    2    0   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.16           0.95    0    0    0    0    1    0    0    0    0    0    1    0    0    0
+0          -1.11           0.00    2    0    2   -4    0    0    0    0    0    0    0    0    0    0
+0           1.11           0.00    0    0    0    0    0    0    7   -7    0    0    0    0    0    0
+0           0.20          -0.91    0    0    0    0    0    0    1    1    0    0    0    0    0    1
+0          -0.72          -0.40    0    0    0    0    0    0    0    0    3    0    0    0    0    2
+0          -1.11           0.00    1    1   -2   -2    0    0    0    0    0    0    0    0    0    0
+0          -1.11           0.00    0    0    0    0    1    0    1   -1    0    0    0    0    0    0
+0           1.07           0.00    2   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0          -1.07           0.00    2    0    2    4    2    0    0    0    0    0    0    0    0    0
+0           0.76          -0.32    0    0    0    0    0    0    0    1   -4    0    0    0    0   -2
+0           0.00          -1.07    0    0    0    0    0    0    3   -6    0    0    0    0    0   -2
+0           1.07           0.00    0    0    0    0    0    0    0    3   -3    0    0    0    0    0
+0           1.07           0.00    0    0    2   -2    1    0   -4    4    0    0    0    0    0    0
+0          -1.07           0.00    0    1   -2   -2    0    0    0    0    0    0    0    0    0    0
+0           1.07           0.00    0    0    2   -3    1    0    0    0    0    0    0    0    0    0
+0          -0.84          -0.24    0    0    0    0    1    0   -3    5    0    0    0    0    0    0
+0           0.00          -1.03    0    0    3    0    3    0    0    0    0    0    0    0    0    0
+0           1.03           0.00    2    1    2    2    2    0    0    0    0    0    0    0    0    0
+0          -1.03           0.00    3    0    2   -4    2    0    0    0    0    0    0    0    0    0
+0          -0.24           0.80    0    0    0    0    0    0    0    1    0    0   -2    0    0    0
+0           0.20           0.84    0    0    1   -1    1    0   -1    0    0    0    0    0    0    0
+0          -1.03           0.00    3    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0          -1.03           0.00    0    0    0    0    3    0    0    0    0    0    0    0    0    0
+0          -0.99           0.00    2    0    0    1    0    0    0    0    0    0    0    0    0    0
+0           0.24           0.76    0    0    0    0    0    0    2   -1    0    0    0    0    0    0
+0          -0.99           0.00    0    0    0    0    0    0    0    7   -8    3    0    0    0    2
+0          -0.16           0.84    0    0    0    0    0    0    0    2    0   -2    0    0    0    1
+0          -0.99           0.00    0    0    0    0    0    0    0    1   -1    0    0    0    0    0
+0          -0.64           0.36    0    0    0    0    0    0    0    4   -7    0    0    0    0   -2
+0           0.99           0.00    1   -2    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.36          -0.64    0    0    1   -1    0    0    0   -1    0   -2    5    0    0    0
+0          -0.95           0.00    3    0    2    2    1    0    0    0    0    0    0    0    0    0
+0          -0.95           0.00    1   -1    2    4    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.95    0    0    1   -1    0    0    0   -1    0   -1    1    0    0    0
+0           0.64           0.32    1    0    0   -1    0    0    0   -3    4    0    0    0    0    0
+0           0.00          -0.95    0    0    0    0    0    0    7  -10    0    0    0    0    0   -2
+0           0.84           0.12    0    0    0    0    0    0    0    5   -8    3    0    0    0    0
+0           0.20           0.76    0    0    0    0    0    0    6   -8    0    0    0    0    0   -1
+0          -0.95           0.00    3    0   -2    0    0    0    0    0    0    0    0    0    0    0
+0           0.95           0.00    1    0    4    0    2    0    0    0    0    0    0    0    0    0
+0          -0.95           0.00    1   -1   -2    2   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.92    1    0    0   -1   -1    0   -3    4    0    0    0    0    0    0
+0           0.91           0.00    1    1    2    2    1    0    0    0    0    0    0    0    0    0
+0           0.91           0.00    3    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0           0.40           0.52    1   -2    2   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.91           0.00    5    0    2    0    2    0    0    0    0    0    0    0    0    0
+0          -0.56           0.36    0    0    0    0    0    0    3   -9    4    0    0    0    0   -2
+0           0.44          -0.48    0    0    1   -1    1    0    8  -14    0    0    0    0    0    0
+0          -0.91           0.00    2    0    0   -6    0    0    0    0    0    0    0    0    0    0
+0          -0.91           0.00    3    1    0    0    0    0    0    0    0    0    0    0    0    0
+0          -0.36          -0.56    0    0    1   -1    1    0    3   -6    0    0    0    0    0    0
+0           0.91           0.00    1    0    0    2    2    0    0    0    0    0    0    0    0    0
+0          -0.88           0.00    1    2    0   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.88           0.00    2    1    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.60          -0.28    0    0    0    0    1    0    0    8  -15    0    0    0    0    0
+0           0.88           0.00    0    2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.36          -0.52    0    0    1   -1    1    0    0   -3    4    0    0    0    0    0
+0          -0.52           0.36    0    0    0    0    0    0    3   -5    4    0    0    0    0    2
+0           0.52           0.36    0    0    0    0    0    0    0    6    0    0    0    0    0    2
+0           0.00           0.88    0    0    0    0    0    0    0    4   -7    0    0    0    0    0
+0           0.56           0.32    0    0    0    0    0    0    0    8  -15    0    0    0    0   -2
+0           0.64          -0.24    0    0    0    0    0    0    0    4   -5    0    0    0    0    0
+0           0.88           0.00    1   -1    0   -4    0    0    0    0    0    0    0    0    0    0
+0           0.88           0.00    1   -1    2    0    0    0    0    0    0    0    0    0    0    0
+0           0.88           0.00    1   -1    2   -1    2    0    0    0    0    0    0    0    0    0
+0           0.84           0.00    0    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+0          -0.68          -0.16    2    0    0   -2    1    0   -6    8    0    0    0    0    0    0
+0           0.84           0.00    1    2    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.56           0.28    0    0    1   -1    0    0   -8   12    0    0    0    0    0    0
+0          -0.16           0.68    0    0    0    0    0    0    5   -5    0    0    0    0    0   -1
+0           0.64          -0.20    0    0    0    0    0    0    5   -7    0    0    0    0    0    0
+0           0.16           0.68    0    0    0    0    0    0    1    2    0    0    0    0    0    2
+0           0.72          -0.12    0    0    0    0    0    0    0    3    0   -3    0    0    0    0
+0          -0.83           0.00    2    0   -2    0    2    0    0    0    0    0    0    0    0    0
+0          -0.80           0.00    2    1    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.80           0.00    2    2    0   -2    0    0    0    0    0    0    0    0    0    0
+0          -0.80           0.00    4    0    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.28           0.52    0    0    0    0    0    0    0    5   -9    0    0    0    0    0
+0           0.68          -0.12    0    0    0    0    0    0    3    0    0    0    0    0    0    2
+0           0.00          -0.80    0    0    0    0    0    0    0    2    0    0   -1    0    0    2
+0          -0.32           0.48    0    0    0    0    0    0    0    5   -9    0    0    0    0   -2
+0           0.36          -0.44    0    0    0    0    0    0    0    3    0   -3    0    0    0    2
+0          -0.36          -0.44    0    0    0    0    0    0    0    0    0    0    0    0    2    1
+0          -0.80           0.00    0    1    2    0    0    0    0    0    0    0    0    0    0    0
+0           0.79           0.00    1    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.74          -0.04    0    0    2   -2    3    0    0    0    0    0    0    0    0    0
+0          -0.76           0.00    0    0    0    0    1    0   -1    1    0    0    0    0    0    0
+0           0.00           0.76    0    0    0    0    1    0   -2    3    0    0    0    0    0    0
+0           0.16           0.60    0    0    0    0    1    0    0    0    0   -1    0    0    0    0
+0          -0.76           0.00    1    0    0    1    1    0    0    0    0    0    0    0    0    0
+0          -0.76           0.00    3    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.76           0.00    2    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0          -0.76           0.00    2   -1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.76           0.00    1    0   -4    2   -1    0    0    0    0    0    0    0    0    0
+0           0.12           0.64    0    0    1   -1    0    0    0   -1    0    2    0    0    0    0
+0           0.76           0.00    1    0    0   -2    0    0   -3    3    0    0    0    0    0    0
+0           0.00           0.76    0    0    1   -1    1    0    0   -9   15    0    0    0    0    0
+0           0.76           0.00    0    0    0    0    0    0    8   -8    0    0    0    0    0    0
+0           0.64          -0.12    0    0    0    0    0    0    7  -11    0    0    0    0    0   -2
+0           0.16          -0.60    0    0    0    0    0    0    2    0    0    0    0    0    0    1
+0           0.76           0.00    0    0    0    0    0    0    0    3   -1    0    0    0    0    2
+0           0.00          -0.76    0    0    0    0    0    0    0    2    0    0    1    0    0    2
+0           0.28          -0.48    0    0    0    0    0    0    0    5   -5    0    0    0    0    2
+0           0.32           0.44    0    0    0    0    0    0    0    1    0    1    0    0    0    0
+0          -0.76           0.00    1    1    2    0    0    0    0    0    0    0    0    0    0    0
+0           0.72           0.00    4    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.72           0.00    1   -1   -2    0    0    0    0    0    0    0    0    0    0    0
+0           0.48          -0.24    0    0    0    0    0    0    0    2    0   -2    5    0    0    2
+0          -0.72           0.00    0    1    2   -4    1    0    0    0    0    0    0    0    0    0
+0           0.72           0.00    0    0    1   -1    1    0   -2    2    0    0    0    0    0    0
+0          -0.72           0.00    0    0    2    1    0    0    0    0    0    0    0    0    0    0
+0          -0.72           0.00    3    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+0          -0.71           0.00    1   -1    0    0    2    0    0    0    0    0    0    0    0    0
+0          -0.68           0.00    1    0   -2   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.68           0.00    0    2    0   -2    1    0    0    0    0    0    0    0    0    0
+0           0.68           0.00    0    1   -4    2   -2    0    0    0    0    0    0    0    0    0
+0           0.68           0.00    0    1    2    4    2    0    0    0    0    0    0    0    0    0
+0           0.68           0.00    1    0   -2    4   -1    0    0    0    0    0    0    0    0    0
+0          -0.68           0.00    0    2    2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.56          -0.12    0    0    0    0    0    0    5   -3    0    0    0    0    0    2
+0          -0.68           0.00    0    0    0    0    0    0    0    3   -5    0    0    0    0   -2
+0          -0.68           0.00    0    0    0    0    0    0    0    2    0   -4    0    0    0   -2
+0           0.20           0.48    0    0    0    0    0    0    0    3    0    2   -5    0    0    2
+0          -0.44          -0.24    0    0    0    0    0    0    0    2    0    2   -5    0    0    2
+0          -0.68           0.00    1    1   -2    0    0    0    0    0    0    0    0    0    0    0
+0           0.64           0.00    2    0    0   -2    1    0   -3    3    0    0    0    0    0    0
+0           0.64           0.00    3    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          -0.64           0.00    2   -1    2    2    1    0    0    0    0    0    0    0    0    0
+0           0.64           0.00    0    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0          -0.64           0.00    1    0   -2   -3   -1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.52    0    0    0    0    0    0    6   -6    0    0    0    0    0   -1
+0          -0.12          -0.52    0    0    0    0    0    0    2   -2    0    0    0    0    0    1
+0          -0.16          -0.48    0    0    0    0    0    0    0    3    0   -2    0    0    0    0
+0          -0.20          -0.44    0    0    0    0    0    0    2   -3    0    0    0    0    0   -2
+0          -0.44           0.20    0    0    0    0    0    0    0   11    0    0    0    0    0    2
+0          -0.44           0.20    0    0    0    0    0    0    0    6  -15    0    0    0    0   -2
+0           0.24          -0.40    0    0    0    0    0    0    0    4   -8    0    0    0    0   -2
+0          -0.20          -0.44    0    0    0    0    0    0    0    3    0    1    0    0    0    2
+0          -0.64           0.00    1    0    0   -6    0    0    0    0    0    0    0    0    0    0
+0           0.40          -0.24    0    0    0    0    1    0    3   -7    4    0    0    0    0    0
+0          -0.64           0.00    1    0   -2   -4    0    0    0    0    0    0    0    0    0    0
+0           0.64           0.00    0    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+0          -0.63           0.00    2    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+0          -0.60           0.00    0    1   -2    1   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.60    0    0    2   -2    2    0   -8   11    0    0    0    0    0    0
+0          -0.60           0.00    2    0   -2   -3   -2    0    0    0    0    0    0    0    0    0
+0          -0.60           0.00    4    0    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.60           0.00    0    0    0    0    0    0    0    2    0    0   -2    0    0    0
+0           0.00           0.60    0    0    0    0    0    0    0    0    0    0    3    0    0    2
+0           0.24          -0.36    2    0    0   -2    0    0    0   -2    0    4   -3    0    0    0
+0           0.12           0.48    0    0    0    0    0    0    7   -9    0    0    0    0    0   -1
+0           0.48          -0.12    0    0    0    0    0    0    4   -7    0    0    0    0    0   -1
+0           0.12           0.48    0    0    0    0    0    0    3   -3    0    0    0    0    0    1
+0           0.24          -0.36    0    0    0    0    0    0    0    6   -6    0    0    0    0    2
+0           0.36           0.24    0    0    0    0    0    0    0    6  -11    0    0    0    0    0
+0           0.12           0.48    0    0    0    0    0    0    0    5    0   -2    0    0    0    2
+0           0.44           0.16    0    0    0    0    0    0    0    2    0   -4    0    0    0    0
+0          -0.60           0.00    0    0    0    3    0    0    0    0    0    0    0    0    0    0
+0          -0.60           0.00    2    0    0   -2    0    0    0   -2    0    3   -1    0    0    0
+0           0.60           0.00    2   -1    0   -4    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.60    0    0    1   -1    2    0    0   -2    2    0    0    0    0    0
+0           0.59           0.00    0    0    0    0    1    0    0    1    0   -1    0    0    0    0
+0          -0.56           0.00    3    0    0   -4    1    0    0    0    0    0    0    0    0    0
+0          -0.44          -0.12    2    0    0   -2   -1    0   -6    8    0    0    0    0    0    0
+0           0.56           0.00    1    2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.56    0    0    1   -1    1    0    0   -1    0   -1    1    0    0    0
+0          -0.56           0.00    3    1    0   -4    0    0    0    0    0    0    0    0    0    0
+0          -0.56           0.00    3    0    0   -6    0    0    0    0    0    0    0    0    0    0
+0           0.56           0.00    0    0    0    0    0    0    0    6    0    0    0    0    0    0
+0          -0.56           0.00    0    0    0    0    0    0    0    1    0    0    1    0    0    2
+0           0.16           0.40    0    0    0    0    0    1    0   -4    0    0    0    0    0   -2
+0           0.44          -0.12    0    0    0    0    0    0    4   -4    0    0    0    0    0    2
+0           0.20          -0.36    0    0    0    0    0    0    0    1   -5    0    0    0    0   -2
+0          -0.36          -0.20    0    0    0    0    1    0   -3    7   -4    0    0    0    0    0
+0          -0.56           0.00    0    0    4   -4    1    0    0    0    0    0    0    0    0    0
+0           0.55           0.00    1    1    0   -2    2    0    0    0    0    0    0    0    0    0
+0           0.52           0.00    1    0    0   -4   -2    0    0    0    0    0    0    0    0    0
+0          -0.52           0.00    1    1    0   -4    1    0    0    0    0    0    0    0    0    0
+0           0.52           0.00    0    0    0    0    1    0    3   -5    0    2    0    0    0    0
+0           0.52           0.00    0    1    0   -1    0    0    0    0    0    0    0    0    0    0
+0           0.16           0.36    0    0    1   -1    0    0    0   -1    0    0    2    0    0    0
+0          -0.52           0.00    0    0    2   -2    0    0   -3    3    0    0    0    0    0    0
+0          -0.52           0.00    1    0    0   -2    0    0    0   -2    0    3    0    0    0    0
+0          -0.52           0.00    1    0   -2   -2   -2    0    0   -2    0    2    0    0    0    0
+0          -0.52           0.00    0    0    2    6    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.52    0    0    1   -1    1    0    2   -4    0   -3    0    0    0    0
+0           0.52           0.00    0    0    0    0    0    0    9   -9    0    0    0    0    0    0
+0          -0.52           0.00    0    0    0    0    0    0    0    1    1    0    0    0    0    2
+0           0.12           0.40    0    0    0    0    0    0    5   -6    0    0    0    0    0    2
+0           0.52           0.00    1    0   -4    0    0    0    0    0    0    0    0    0    0    0
+0          -0.52           0.00    2    0   -2   -4    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.52    1    0    1    0    1    0    0    0    0    0    0    0    0    0
+0           0.52           0.00    1    1    0    0    2    0    0    0    0    0    0    0    0    0
+0           0.52           0.00    0    0    0    1    2    0    0    0    0    0    0    0    0    0
+0          -0.51           0.00    1    0    0    2   -2    0    0    0    0    0    0    0    0    0
+0          -0.51           0.00    1    0    2    0    3    0    0    0    0    0    0    0    0    0
+0           0.48           0.00    0    1    0    2    2    0    0    0    0    0    0    0    0    0
+0           0.48           0.00    0    0    2    2   -1    0    0    0    0    0    0    0    0    0
+0          -0.16           0.32    0    0    0    0    1    0    0    2   -4    0    0    0    0    0
+0          -0.48           0.00    1   -2    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.48           0.00    0    1   -2   -1   -2    0    0    0    0    0    0    0    0    0
+0           0.48           0.00    3   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.48           0.00    4    0    0   -4    0    0    0    0    0    0    0    0    0    0
+0          -0.48           0.00    3   -1    2    2    2    0    0    0    0    0    0    0    0    0
+0          -0.12          -0.36    0    0    1   -1    1    0    0   -1    0    3    0    0    0    0
+0          -0.32           0.16    0    0    1   -1    1    0    0   -1    0    0    3    0    0    0
+0           0.32          -0.16    0    0    0    0    0    0    4   -2    0    0    0    0    0    2
+0          -0.12          -0.36    0    0    0    0    0    0    3   -5    0    0    0    0    0    1
+0           0.16           0.32    0    0    0    0    0    0    0    7  -13    0    0    0    0   -2
+0           0.20          -0.28    0    0    0    0    0    0    0    5   -7    0    0    0    0    0
+0          -0.20          -0.28    0    0    0    0    0    0    0    1    0    3    0    0    0    2
+0          -0.36           0.12    0    0    0    0    0    0    0    0    0    4    0    0    0    2
+0          -0.48           0.00    0    0    0    0    0    0    0    0    0    2   -5    0    0    2
+0           0.32          -0.16    0    0    0    0    0    0    0    0    0    0    3    0    0    1
+0           0.48           0.00    1   -1    0    4    0    0    0    0    0    0    0    0    0    0
+0          -0.48           0.00    2    1    0    2    0    0    0    0    0    0    0    0    0    0
+0          -0.48           0.00    2    0    0   -2   -1    0    0   -2    0    0    5    0    0    0
+0          -0.48           0.00    3    0    0    2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.48    1    0    1   -2    1    0    0    0    0    0    0    0    0    0
+0           0.48           0.00    1    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+0          -0.48           0.00    0    0    2   -2    2    0   -3    3    0    0    0    0    0    0
+0          -0.48           0.00    1    0    0   -1    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.48    0    0    2   -2    2    0   -5    6    0    0    0    0    0    0
+0           0.44           0.00    0    0    0    0    1    0    0   -1    0    1    0    0    0    0
+0          -0.32          -0.12    0    0    1   -1   -1    0    0    0   -2    0    0    0    0    0
+0          -0.44           0.00    0    1   -2    0    0    0    0    0    0    0    0    0    0    0
+0           0.20          -0.24    0    0    0    0    0    0    0    9  -17    0    0    0    0    0
+0           0.44           0.00    3   -1   -2   -1   -2    0    0    0    0    0    0    0    0    0
+0          -0.44           0.00    1   -1    0   -3    0    0    0    0    0    0    0    0    0    0
+0           0.44           0.00    0    0    2   -2    1    0   -2    2    0    0    0    0    0    0
+0           0.20          -0.24    0    0    0    0    0    0    8  -13    0    0    0    0    0    1
+0           0.12           0.32    0    0    0    0    0    0    5  -10    0    0    0    0    0   -2
+0          -0.20           0.24    0    0    0    0    0    0    1    0    0    0    0    0    0    2
+0           0.32          -0.12    0    0    0    0    0    0    1   -3    0    0    0    0    0    0
+0           0.00           0.44    0    0    0    0    0    0    0    6  -11    0    0    0    0   -2
+0           0.00           0.44    0    0    0    0    0    0    0    5   -8    0    0    0    0    0
+0           0.44           0.00    0    0    0    0    0    0    0    1   -3    0    0    0    0   -2
+0          -0.44           0.00    1    2    0   -4    0    0    0    0    0    0    0    0    0    0
+0          -0.44           0.00    0    1    0    4    0    0    0    0    0    0    0    0    0    0
+0          -0.44           0.00    3    0    2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.44           0.00    1   -1    2   -4    1    0    0    0    0    0    0    0    0    0
+0           0.44           0.00    2    0    2    2    0    0    0    0    0    0    0    0    0    0
+0           0.40           0.00    2    0    2    0   -1    0    0    0    0    0    0    0    0    0
+0          -0.40           0.00    2    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+0          -0.40           0.00    2    1    0   -4    1    0    0    0    0    0    0    0    0    0
+0          -0.40           0.00    1    0    4   -4    2    0    0    0    0    0    0    0    0    0
+0           0.40           0.00    2    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+0           0.24           0.16    2    0    0   -2   -1    0    0   -5    6    0    0    0    0    0
+0           0.00          -0.40    0    0    3    0    2    0    0    0    0    0    0    0    0    0
+0           0.12           0.28    2    0   -1   -1   -1    0    0   -1    0    3    0    0    0    0
+0           0.40           0.00    1    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0          -0.40           0.00    0    0    4   -1    2    0    0    0    0    0    0    0    0    0
+0           0.40           0.00    0    0    0    0    0    0    0    7  -13    0    0    0    0    0
+0           0.40           0.00    5    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    0    0    0    0    0    0    9  -12    0    0    0    0    0   -2
+0          -0.40           0.00    0    0    0    0    0    0    5   -9    0    0    0    0    0   -2
+0           0.00          -0.40    0    0    0    0    0    0    4   -4    0    0    0    0    0    1
+0           0.00          -0.40    0    0    0    0    0    0    3   -1    0    0    0    0    0    1
+0           0.20          -0.20    0    0    0    0    0    0    2   -4    0    0    0    0    0    0
+0          -0.40           0.00    0    0    0    0    0    0    1   -2    0    0    0    0    0    1
+0          -0.40           0.00    0    0    0    0    0    0    0    5   -3    0    0    0    0    2
+0          -0.12          -0.28    0    0    0    0    0    0    0    4   -8    1    5    0    0   -2
+0           0.40           0.00    0    0    0    0    0    0    3   -5    0    2    0    0    0    0
+0           0.40           0.00    1    1   -2    2    0    0    0    0    0    0    0    0    0    0
+0           0.40           0.00    2    0    0    4    0    0    0    0    0    0    0    0    0    0
+0           0.40           0.00    0    0    0    3    1    0    0    0    0    0    0    0    0    0
+0           0.40           0.00    4    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    0    0    1   -1    2    0    0   -1    0    2    0    0    0    0
+0          -0.20          -0.16    0    0    0    0    2    0    0   -1    2    0    0    0    0    0
+0           0.36           0.00    1    0    0   -3    1    0    0    0    0    0    0    0    0    0
+0           0.36           0.00    1   -2    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.24          -0.12    0    2   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.20          -0.16    2    0   -1   -1   -1    0    0    3   -7    0    0    0    0    0
+0           0.00           0.36    0    0    2    0    2    0    0   -4    8   -3    0    0    0    0
+0           0.00           0.36    0    0    2    0    2    0    0    4   -8    3    0    0    0    0
+0          -0.36           0.00    2    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.12           0.24    1    0    1    0    0    0    0    0    0    0    0    0    0    0
+0          -0.36           0.00    2   -2    2    0    2    0    0    0    0    0    0    0    0    0
+0          -0.36           0.00    0    3    0   -2    0    0    0    0    0    0    0    0    0    0
+0          -0.36           0.00    2   -1    0   -1    0    0    0    0    0    0    0    0    0    0
+0          -0.36           0.00    1    3    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.36           0.00    1   -1   -2   -3   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.36    0    0    1   -1    1    0   -2    3    0    0    0    0    0    0
+0           0.00           0.36    0    0    0    0    0    0    7   -7    0    0    0    0    0   -1
+0           0.00           0.36    0    0    0    0    0    0    6   -7    0    0    0    0    0    0
+0          -0.36           0.00    0    0    0    0    0    0    6   -9    0    0    0    0    0   -1
+0           0.00           0.36    0    0    0    0    0    0    4   -3    0    0    0    0    0    2
+0           0.12          -0.24    0    0    0    0    0    0    1   -2    0    0    0    0    0   -2
+0          -0.24           0.12    0    0    0    0    0    0    0    6   -5    0    0    0    0    2
+0          -0.36           0.00    0    0    0    0    0    0    0    5    0   -3    0    0    0    2
+0           0.00           0.36    0    0    0    0    0    0    0    3   -2    0    0    0    0    0
+0           0.36           0.00    0    0    0    0    0    0    0    3   -6    0    0    0    0   -1
+0           0.24          -0.12    0    0    0    0    0    0    0    2   -5    0    0    0    0   -2
+0           0.00          -0.36    0    0    1   -1    1    0    0    1    0    0    0    0    0    0
+0          -0.36           0.00    1   -2   -2    0   -2    0    0    0    0    0    0    0    0    0
+0           0.36           0.00    2    0    0    2    2    0    0    0    0    0    0    0    0    0
+0           0.36           0.00    0    1    0   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.36           0.00    0    0    2    0   -2    0    0    0    0    0    0    0    0    0
+0           0.36           0.00    0    0    2   -2   -2    0    0    0    0    0    0    0    0    0
+0          -0.13           0.22    0    0    1   -1    2    0    0   -1    0   -2    5    0    0    0
+0          -0.32           0.00    0    0    0    0    1    0    3   -5    0    0    0    0    0    0
+0          -0.32           0.00    1    1    0    2   -1    0    0    0    0    0    0    0    0    0
+0           0.32           0.00    4    0    0    0   -1    0    0    0    0    0    0    0    0    0
+0          -0.20          -0.12    0    0    0    0    1    0    0   -8   15    0    0    0    0    0
+0           0.32           0.00    0    2    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.12           0.20    2    0    0   -2    1    0    0   -6    8    0    0    0    0    0
+0          -0.32           0.00    3   -1    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.32           0.00    0    0    2    0    2    0    1   -1    0    0    0    0    0    0
+0          -0.32           0.00    0    0    2    0    2    0   -1    1    0    0    0    0    0    0
+0          -0.32           0.00    2    0    2   -1    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.32    2    0   -1   -1   -2    0    0   -1    0    2    0    0    0    0
+0           0.32           0.00    1    2    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.32           0.00    2    2    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.12          -0.20    0    0    2   -2    0    0    0   -9   13    0    0    0    0    0
+0          -0.32           0.00    3    0    2   -1    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.32    1    0    0   -2    0    0   20  -21    0    0    0    0    0    0
+0           0.32           0.00    0    0    2   -2    1    0    0   -2    0    0    2    0    0    0
+0           0.00           0.32    0    0    2   -2    1    0    0   -8   11    0    0    0    0    0
+0           0.00          -0.32    0    0    1   -1    1    0    0   -1    0    0    0    2    0    0
+0           0.00          -0.32    0    0    1   -1    1    0    0   -1    0   -1    2    0    0    0
+0           0.32           0.00    0    0    0    0    0    0    8  -12    0    0    0    0    0    0
+0          -0.32           0.00    0    0    0    0    0    0    5   -5    0    0    0    0    0    2
+0           0.00           0.32    0    0    0    0    0    0    5   -6    0    0    0    0    0    0
+0           0.32           0.00    0    0    0    0    0    0    2   -6    0    0    0    0    0   -2
+0           0.00           0.32    0    0    0    0    0    0    0    8  -15    0    0    0    0   -1
+0           0.00          -0.32    0    0    0    0    0    0    0    5   -2    0    0    0    0    2
+0           0.32           0.00    0    0    0    0    0    0    0    4   -4    0    0    0    0    0
+0          -0.16           0.16    0    0    0    0    0    0    0    2    2    0    0    0    0    2
+0          -0.16           0.16    0    0    0    0    0    0    0    1    0   -4    0    0    0    0
+0           0.00           0.32    0    0    0    0    0    0    0    0    0    0    1    0    0    2
+0           0.20           0.12    0    0    1   -1    1    0    0   -1    0   -2    4    0    0    0
+0           0.20           0.12    0    0    0    0    0    1    0   -4    0    0    0    0    0    0
+0          -0.20           0.12    0    0    0    0    0    0    0    5   -8    0    0    0    0   -2
+0           0.12           0.20    0    0    0    0    0    0    0    4   -8    0    0    0    0    0
+0           0.12          -0.20    0    0    0    0    0    0    0    2   -6    0    0    0    0   -2
+0           0.00           0.32    0    0    2   -2    1   -1    0    2    0    0    0    0    0    0
+0          -0.32           0.00    0    0    4   -2    0    0    0    0    0    0    0    0    0    0
+0           0.32           0.00    0    0    0    1   -2    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    2    0   -4    2   -2    0    0    0    0    0    0    0    0    0
+0          -0.28           0.00    0    0    0    4    2    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    1    0    0    4   -1    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    1   -2    0   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    1    1    0   -1    1    0    0    0    0    0    0    0    0    0
+0           0.16           0.12    1    0    0   -1    1    0    0   -3    4    0    0    0    0    0
+0           0.28           0.00    3    1    2    0    1    0    0    0    0    0    0    0    0    0
+0          -0.28           0.00    1    1    2    1    1    0    0    0    0    0    0    0    0    0
+0          -0.12          -0.16    0    0    0    0    1    0    0   -9   17    0    0    0    0    0
+0           0.28           0.00    1    1    4   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.28           0.00    4    0    2   -4    2    0    0    0    0    0    0    0    0    0
+0          -0.28           0.00    0    2    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    1    0    2   -3    0    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    3    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    2   -2    0    0    0    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    1    1    0   -3    0    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    1    0    2    3    2    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    1    0    0   -2    0    0    0    1    0   -1    0    0    0    0
+0          -0.28           0.00    1    0   -2   -2   -2    0   -3    3    0    0    0    0    0    0
+0           0.28           0.00    0    0    2   -2    1    0    0   -2    0    3    0    0    0    0
+0           0.00           0.28    0    0    0    0    0    0    8   -8    0    0    0    0    0   -1
+0           0.00           0.28    0    0    0    0    0    0    8  -10    0    0    0    0    0   -1
+0           0.00          -0.28    0    0    0    0    0    0    4   -2    0    0    0    0    0    1
+0          -0.28           0.00    0    0    0    0    0    0    3   -4    0    0    0    0    0   -1
+0           0.28           0.00    0    0    0    0    0    0    3   -6    0    0    0    0    0   -1
+0          -0.12          -0.16    0    0    0    0    0    0    1   -4    0    0    0    0    0   -2
+0           0.00           0.28    0    0    0    0    0    0    0    6    0    0    0    0    0    1
+0           0.00          -0.28    0    0    0    0    0    0    0    6   -7    0    0    0    0    2
+0           0.12          -0.16    0    0    0    0    0    0    0    4    0    0    0    0    0    0
+0          -0.28           0.00    0    0    0    0    0    0    0    4    0    0   -2    0    0    2
+0           0.00          -0.28    0    0    0    0    0    0    0    3    0    0   -2    0    0    2
+0           0.00           0.28    0    0    0    0    0    0    0    1    0   -1    0    0    0    1
+0           0.00          -0.28    0    0    0    0    0    0    0    1   -6    0    0    0    0   -2
+0           0.28           0.00    0    0    0    0    0    0    0    0    0    4   -5    0    0    2
+0          -0.28           0.00    0    0    0    0    0    0    0    0    0    0    0    2    0    2
+0          -0.28           0.00    0    0    0    0    0    0    3   -7    4    0    0    0    0    0
+0           0.28           0.00    1   -1    2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    1    1    2   -4    0    0    0    0    0    0    0    0    0    0
+0           0.12          -0.16    0    0    1   -1    1    0    0   -1    0    0   -2    0    0    0
+0           0.28           0.00    0    0    2    4    0    0    0    0    0    0    0    0    0    0
+0           0.28           0.00    2   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+0          -0.28           0.00    0    0    1   -1    2    0    0    0   -2    0    0    0    0    0
+0           0.00          -0.28    0    0    0    0    2    0    0    4   -8    3    0    0    0    0
+0           0.00          -0.28    0    0    0    0    2    0    0   -4    8   -3    0    0    0    0
+0           0.28           0.00    1    1    2   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.24    0    0    2   -2   -1    0   -5    6    0    0    0    0    0    0
+0           0.24           0.00    1   -2    0   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.24           0.00    0    1    0   -4    1    0    0    0    0    0    0    0    0    0
+0          -0.24           0.00    1   -2    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.24           0.00    0    0    2    0    2    0    0   -1    0    1    0    0    0    0
+0          -0.24           0.00    0    0    2    0    2    0    0    1    0   -1    0    0    0    0
+0          -0.24           0.00    2    0   -4   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.24           0.00    2    0    2    1    1    0    0    0    0    0    0    0    0    0
+0           0.24           0.00    2    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.24    2    0   -1   -1   -1    0    0   -1    0    2    0    0    0    0
+0          -0.24           0.00    4   -1    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.24           0.00    5    0    0    0    0    0    0    0    0    0    0    0    0    0
+0          -0.24           0.00    3    0    0   -3    0    0    0    0    0    0    0    0    0    0
+0          -0.24           0.00    2    2    0   -4    0    0    0    0    0    0    0    0    0    0
+0           0.24           0.00    2    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0          -0.24           0.00    2    1    2    1    2    0    0    0    0    0    0    0    0    0
+0          -0.24           0.00    1    0    2   -2    2    0    0   -2    0    2    0    0    0    0
+0          -0.24           0.00    1    0    0    0    0    0    0   -2    0    2    0    0    0    0
+0           0.24           0.00    1    0    0   -2    0    0    2   -2    0    0    0    0    0    0
+0           0.00          -0.24    1    0   -1    1   -1    0  -18   17    0    0    0    0    0    0
+0           0.24           0.00    0    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0          -0.24           0.00    0    1    2    3    2    0    0    0    0    0    0    0    0    0
+0           0.24           0.00    0    0    0    0    0    0    8  -12    0    0    0    0    0   -2
+0           0.24           0.00    0    0    0    0    0    0    8  -16    0    0    0    0    0   -2
+0           0.00           0.24    0    0    0    0    0    0    7   -8    0    0    0    0    0    0
+0           0.12          -0.12    0    0    0    0    0    0    2   -3    0    0    0    0    0    1
+0           0.00          -0.24    0    0    0    0    0    0    0    5   -6    0    0    0    0    2
+0          -0.24           0.00    0    0    0    0    0    0    0    4   -6    0    0    0    0   -2
+0           0.00           0.24    0    0    0    0    0    0    0    4   -8    1    5    0    0    2
+0          -0.24           0.00    0    0    0    0    0    0    0    2    0   -2    0    0    0    2
+0           0.00          -0.24    0    0    0    0    0    0    0    2   -7    0    0    0    0   -2
+0           0.24           0.00    0    0    0    0    0    0    0    0    2    0    0    0    0    0
+0          -0.24           0.00    0    0    0    0    0    0    0    0    0    0    5    0    0    2
+0          -0.24           0.00    0    0    0    0    0    0    0    0    0    0    0    0    2    2
+0           0.24           0.00    0    1    0    3    0    0    0    0    0    0    0    0    0    0
+0           0.24           0.00    0    2    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.24           0.00    2   -1    2   -1    2    0    0    0    0    0    0    0    0    0
+0           0.24           0.00    1   -1    2    2    0    0    0    0    0    0    0    0    0    0
+0           0.24           0.00    2    1    2   -4    1    0    0    0    0    0    0    0    0    0
+0          -0.24           0.00    2    0    0   -2   -2    0   -3    3    0    0    0    0    0    0
+0          -0.24           0.00    1   -1    0   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.24    0    0    1   -1    2    0    0   -1    0    1    0    0    0    0
+0           0.00          -0.24    0    0    1   -1    2    0    0   -1    0   -1    0    0    0    0
+0           0.24           0.00    0    0    0    0    1    0    2   -2    0    0    0    0    0    0
+0           0.00           0.20    1    0   -1    0    1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    2   -2    0   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    0    0    1   -1    2    0    0   -1    0    0    1    0    0    0
+0           0.20           0.00    2    0    0   -2   -1    0   -3    3    0    0    0    0    0    0
+0           0.20           0.00    1   -2    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    0    0    0    1    0    0    0    0    0   -1    0    0    0
+0          -0.20           0.00    1    0   -2    1    1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    2    0    0   -3   -1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    2    1   -2    0    1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    2    0    0   -1    1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    2   -1    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    1   -1   -4    2   -2    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    1   -2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    2    0    4   -2    1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    0    0    2    3    1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    0    1    4   -2    1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    1    0   -2    0   -2    0  -10    3    0    0    0    0    0    0
+0           0.00           0.20    1    0    1   -2    0    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    1   -1    2    1    2    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    4    1    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    0    0    2   -2    1    0    0   -1    0    1    0    0    0    0
+0           0.20           0.00    1    0    0   -1    0    0    0   -2    2    0    0    0    0    0
+0          -0.20           0.00    0    0    2   -2    0    0    0   -2    0    2    0    0    0    0
+0           0.00          -0.20    1    0    0    0    0    0    0    4   -8    3    0    0    0    0
+0          -0.20           0.00    4   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    2    1   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    2    0    0   -2    0    0    0   -4    8   -3    0    0    0    0
+0           0.20           0.00    2    0    0   -2    0    0   -2    2    0    0    0    0    0    0
+0           0.20           0.00    2    0    0   -2   -1    0    0   -2    0    4   -5    0    0    0
+0           0.00          -0.20    2    0   -1   -1   -1    0    0   -1    0    0    0    0    0    0
+0          -0.20           0.00    1    1   -2   -3   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1    0    3    0    3    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    0    1   -1    1    0    0   -1    0    0    0    0    0    0
+0           0.00          -0.20    1    0    0    0    0    0    0   -4    8   -3    0    0    0    0
+0           0.20           0.00    1    0    0    0    0    0   -1    1    0    0    0    0    0    0
+0           0.00           0.20    1    0    0   -2    0    0   17  -16    0   -2    0    0    0    0
+0          -0.20           0.00    1    0   -1   -1   -1    0   20  -20    0    0    0    0    0    0
+0           0.20           0.00    1    0   -2   -2   -2    0    0   -2    0    3    0    0    0    0
+0           0.20           0.00    0    3   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    0    1   -1    1    0    1   -2    0    0    0    0    0    0
+0           0.00           0.20    0    0    1   -1    1    0   -2    1    0    0    0    0    0    0
+0          -0.20           0.00    0    0    1   -1    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    0    0    0    0    0    9   -9    0    0    0    0    0   -1
+0           0.00           0.20    0    0    0    0    0    0    9  -11    0    0    0    0    0   -1
+0           0.00           0.20    0    0    0    0    0    0    6  -10    0    0    0    0    0   -1
+0           0.00          -0.20    0    0    0    0    0    0    5   -3    0    0    0    0    0    1
+0          -0.20           0.00    0    0    0    0    0    0    4   -5    0    0    0    0    0   -1
+0           0.00          -0.20    0    0    0    0    0    0    3   -4    0    0    0    0    0   -2
+0           0.00          -0.20    0    0    0    0    0    0    0    6   -9    0    0    0    0    0
+0           0.00          -0.20    0    0    0    0    0    0    0    5  -10    0    0    0    0   -2
+0          -0.20           0.00    0    0    0    0    0    0    0    4    0   -4    0    0    0    2
+0           0.20           0.00    0    0    0    0    0    0    0    3    0   -4    0    0    0    0
+0          -0.20           0.00    0    0    0    0    0    0    0    2    0    0    0    0    0    0
+0          -0.20           0.00    0    0    0    0    0    0    0    2    0   -5    0    0    0   -2
+0           0.00           0.20    0    0    0    0    0    0    0    1    0   -2    5    0    0    2
+0           0.20           0.00    0    0    0    0    0    0    0    1    0   -2    0    0    0   -2
+0           0.00          -0.20    0    0    0    0    0    0    0    1    0   -3    0    0    0   -1
+0          -0.20           0.00    0    0    0    0    0    0    0    1    0   -5    0    0    0   -2
+0           0.00          -0.20    0    0    0    0    0    0    0    0    0    0    0    2    0    1
+0           0.00           0.20    0    0    0    0    0    0    0    0    0    0    0    1   -2   -2
+0          -0.20           0.00    0    2    0   -4    0    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    0    0    0    6    0    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    3    0    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    2   -1   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1    0   -1    1   -1    0    0    1    0    0    0    0    0    0
+0          -0.20           0.00    0    0    2   -4    0    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    1    0    0   -1   -1    0    0   -2    2    0    0    0    0    0
+0           0.20           0.00    0    1    0    4    1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    1    0    0   -6   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    0    0    0    1    0    5   -8    0    0    0    0    0    0
+0           0.20           0.00    0    0    4    2    2    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    3    0    0   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    0    0    2   -2    2    0    0   -2    0    2    0    0    0    0
+0          -0.20           0.00    1   -1    0   -2   -2    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    0    0    1   -1    2    0   -5    7    0    0    0    0    0    0
+0          -0.20           0.00    2    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    0    0    0    0    1    0    0    2   -2    0    0    0    0    0
+0          -0.20           0.00    1    0   -2    0    2    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    1   -1    0    0   -2    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    1    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    0    0    0    0    1    0   -2    2    0    0    0    0    0    0
+0           0.16           0.00    1    0    0   -3   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.16    0    0    1   -1   -1    0    0   -1    0   -1    0    0    0    0
+0           0.16           0.00    2    0   -2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    1    0    0   -1    1    0    0   -1    0    1    0    0    0    0
+0           0.16           0.00    2    0    0   -3    1    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    3    0    0    2   -1    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    1    1    0    1    1    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    0    2    0    0   -2    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    2   -1    0    2   -1    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    1    1    0    1   -1    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    1    0   -4   -2   -1    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    1   -1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    1   -2    2    2    1    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    3    1    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    1    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    2    0    4   -4    2    0    0    0    0    0    0    0    0    0
+0          -0.04           0.12    2   -2    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    3    0    4   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.16    0    0    1   -1    0    0    3   -6    0    0    0    0    0    0
+0           0.16           0.00    1    1    2    4    2    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    0    0    2   -3    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.16    0    0    1    1    0    0    0    1    0    0    0    0    0    0
+0           0.00          -0.16    0    0    1   -1    0    0   -4    5    0    0    0    0    0    0
+0           0.00           0.16    0    0    1   -1    0    0    0   -2    2    0    0    0    0    0
+0           0.16           0.00    0    0    1   -1    0    0    0   -1    0    0    1    0    0    0
+0           0.00           0.16    0    0    1   -1    0    0    0   -1    0    1    0    0    0    0
+0          -0.16           0.00    0    1   -2    4   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.16    1    0    0   -2    0    0    0    4   -8    3    0    0    0    0
+0           0.16           0.00    3    0    2    1    2    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    3    0    0   -1    0    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    2    1    0    1    0    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    2    0    2    0    1    0    0    1    0    0    0    0    0    0
+0          -0.16           0.00    2    0    0   -2    0    0    0   -2    0    2    2    0    0    0
+0           0.16           0.00    2    0    0   -2    0    0    0   -4    4    0    0    0    0    0
+0          -0.16           0.00    2   -2    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    1    1   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    1    0    0    0    0    0    1   -1    0    0    0    0    0    0
+0          -0.16           0.00    1    0    0    0    0    0    0   -1    0    1    0    0    0    0
+0          -0.16           0.00    1    0    0    0    0    0   -3    3    0    0    0    0    0    0
+0          -0.16           0.00    1    0    0   -2    0    0    1   -1    0    0    0    0    0    0
+0           0.00          -0.16    1    0    0   -2    0    0    0   -4    8   -3    0    0    0    0
+0          -0.16           0.00    1    0    0   -2    0    0   -2    2    0    0    0    0    0    0
+0          -0.16           0.00    0    0    2   -2    1    0    0    1    0   -1    0    0    0    0
+0           0.16           0.00    0    0    2   -2    1    0    0   -3    0    3    0    0    0    0
+0           0.16           0.00    0    0    2   -2    1    0   -5    5    0    0    0    0    0    0
+0          -0.16           0.00    0    0    1   -1    1    0    1   -3    0    0    0    0    0    0
+0           0.00           0.16    0    0    1   -1    1    0    0   -1    0    0    0   -1    0    0
+0           0.16           0.00    0    0    1   -1    1    0    0   -4    6    0    0    0    0    0
+0           0.00           0.16    0    0    1   -1    1    0   -5    6    0    0    0    0    0    0
+0          -0.16           0.00    0    0    0    2    0    0    0   -1    0    1    0    0    0    0
+0           0.00           0.16    0    0    0    0    0    0    8   -9    0    0    0    0    0    0
+0          -0.16           0.00    0    0    0    0    0    0    7  -10    0    0    0    0    0   -1
+0           0.00          -0.16    0    0    0    0    0    0    5   -5    0    0    0    0    0    1
+0           0.00          -0.16    0    0    0    0    0    0    4   -5    0    0    0    0    0   -2
+0           0.00          -0.16    0    0    0    0    0    0    3   -6    0    0    0    0    0    0
+0           0.00           0.16    0    0    0    0    0    0    3   -8    0    0    0    0    0   -2
+0           0.16           0.00    0    0    0    0    0    0    2   -5    0    0    0    0    0   -1
+0          -0.16           0.00    0    0    0    0    0    0    1    1    0    0    0    0    0    0
+0           0.16           0.00    0    0    0    0    0    0    1   -2    0    0    0    0    0   -1
+0           0.00          -0.16    0    0    0    0    0    0    0    7   -8    0    0    0    0    2
+0           0.00          -0.16    0    0    0    0    0    0    0    7   -9    0    0    0    0    2
+0           0.00           0.16    0    0    0    0    0    0    0    6  -10    0    0    0    0   -2
+0           0.00           0.16    0    0    0    0    0    0    0    3    0    0    0    0    0    2
+0           0.16           0.00    0    0    0    0    0    0    0    3   -8    3    0    0    0   -2
+0           0.00           0.16    0    0    0    0    0    0    0    2    0    0   -2    0    0    1
+0           0.16           0.00    0    0    0    0    0    0    0    2   -4    0    0    0    0    1
+0           0.16           0.00    0    0    0    0    0    0    0    1    0    0    0    0    0   -1
+0           0.00          -0.16    0    0    0    0    0    0    0    1    0   -1    0    0    0   -1
+0           0.00          -0.16    0    0    0    0    0    0    0    0    0    3   -5    0    0    0
+0          -0.16           0.00    0    0    0    0    0    0    0    0    0    2   -2    0    0    0
+0          -0.16           0.00    2    1    0   -6    0    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    1   -1    0    1    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.16    0    0    1   -1    0    0   -3    4    0    0    0    0    0    0
+0           0.16           0.00    3   -1    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    4    0    0    2    0    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    0    0    2   -2    0    0   -4    4    0    0    0    0    0    0
+0           0.16           0.00    1    0    2   -1    0    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    1   -2   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    1   -1   -2   -2    0    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    2    0    2   -3    2    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    0    1    2    2    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.16    0    0    2   -2    1    0    0   -4    8   -3    0    0    0    0
+0           0.00           0.16    0    0    2   -2    1    0    0    4   -8    3    0    0    0    0
+0           0.16           0.00    3    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    2   -1    2    0    0    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    2    0    0    4    1    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    2    0   -4    2   -1    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    2   -1    0    2    1    0    0    0    0    0    0    0    0    0
+0          -0.16           0.00    2   -2    0   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.16    0    0    0    0    1    0    0    1    0   -2    0    0    0    0
+0          -0.16           0.00    3    0    0    0    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.16    0    0    0    0    1    0    3   -4    0    0    0    0    0    0
+0           0.16           0.00    0    2    0    0    2    0    0    0    0    0    0    0    0    0
+0           0.16           0.00    0    1    2   -4    2    0    0    0    0    0    0    0    0    0
+0          -0.15           0.00    0    0    1   -1    2    0   -8   12    0    0    0    0    0    0
+0           0.00           0.12    0    0    0    0    1    0    0   -1    0    2    0    0    0    0
+0           0.12           0.00    0    0    0    0    1    0    0   -2    2    0    0    0    0    0
+0           0.00           0.12    0    0    0    0    1    0   -3    4    0    0    0    0    0    0
+0           0.12           0.00    1    0    0    0   -1    0  -10    3    0    0    0    0    0    0
+0           0.12           0.00    1    0    0    0    1    0  -10    3    0    0    0    0    0    0
+0           0.00          -0.12    0    0    0    0    2    0    0    0    0    1    0    0    0    0
+0          -0.12           0.00    0    1    4   -4    2    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    2    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    1    0    2   -4   -1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    1    2    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.12    2    0    0   -2   -1    0    0   -6    8    0    0    0    0    0
+0           0.12           0.00    2   -1   -2    0    1    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    0    0   -2    1    0    0   -5    6    0    0    0    0    0
+0           0.12           0.00    1    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+0           0.12           0.00    2    0   -1   -1    1    0    0    3   -7    0    0    0    0    0
+0           0.12           0.00    1    0    0   -2    1    0    0   -2    0    2    0    0    0    0
+0           0.12           0.00    1    0    0   -1   -1    0    0   -3    4    0    0    0    0    0
+0           0.12           0.00    0    0    0    3    2    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    2    0   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    1   -1    2   -3    1    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    1    0    4    0    1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    2    0    0   -2   -1    0    0   -2    0    3   -1    0    0    0
+0           0.12           0.00    2   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    1    2    2    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.12    0    0    2    0    2    0    2   -3    0    0    0    0    0    0
+0           0.00           0.12    0    0    2    0    2    0   -2    3    0    0    0    0    0    0
+0          -0.12           0.00    0    0    2    0    2    0    2   -2    0    0    0    0    0    0
+0           0.12           0.00    0    0    2    0    2    0   -2    2    0    0    0    0    0    0
+0          -0.12           0.00    5    0    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    3    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    1    2    2    0    1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    1   -1    2    4    1    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    0   -2   -2   -2    0    0   -2    0    2    0    0    0    0
+0          -0.12           0.00    2    0    2    4    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.12    0    0    2   -2    1    0   -8   11    0    0    0    0    0    0
+0           0.00           0.12    1    0    2    0    2    0    0   -4    8   -3    0    0    0    0
+0           0.00           0.12    1    0    2    0    2    0    0    4   -8    3    0    0    0    0
+0          -0.12           0.00    1   -2    0   -1    0    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    0    2   -2    2    0    0   -2    0    3    0    0    0    0
+0           0.12           0.00    1    0    2    0    2    0    1   -1    0    0    0    0    0    0
+0          -0.12           0.00    1    0    2    0    2    0   -1    1    0    0    0    0    0    0
+0           0.00          -0.12    0    0    2   -2    0   -1    0    2    0    0    0    0    0    0
+0          -0.12           0.00    4    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    1    0    2   -6    1    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    0    1   -4    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.12    0    0    1   -1    0    0    0   -1    0    0   -2    0    0    0
+0           0.12           0.00    1   -1   -2    1   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.12    0    0    1   -1    0    0    0    1    0    0    0    0    0    0
+0           0.00           0.12    2    0   -1   -1    0    0    0   -1    0    3    0    0    0    0
+0           0.00          -0.12    0    0    1   -1    0    0   -1    0    0    0    0    0    0    0
+0           0.08           0.04    0    0    0    0    0    0    0    0    1    0    0    0    0    0
+0           0.12           0.00    4    1    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    3    1    2    2    2    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    3    1    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    3    1   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    3   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    3   -1    0   -3    0    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    1    0   -3    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.12    2    0    1   -3    1    0   -6    7    0    0    0    0    0    0
+0           0.00          -0.12    2    0    0   -2    0    0    2   -5    0    0    0    0    0    0
+0           0.00           0.12    2    0    0   -2    0    0    0   -2    0    5   -5    0    0    0
+0           0.12           0.00    2    0    0   -2    0    0    0   -2    0    1    5    0    0    0
+0           0.12           0.00    2    0    0   -2    0    0    0   -2    0    0    2    0    0    0
+0           0.12           0.00    2    0    0   -2    0    0   -4    4    0    0    0    0    0    0
+0           0.00           0.12    2    0   -2    0   -2    0    0    5   -9    0    0    0    0    0
+0          -0.12           0.00    2    0   -2   -5   -2    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    2   -1    2    4    2    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    1    3   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    1    1   -2   -1   -2    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    1    0    2   -2    2    0   -3    3    0    0    0    0    0    0
+0           0.00           0.12    1    0    1    1    1    0    0    1    0    0    0    0    0    0
+0           0.12           0.00    1    0    0    0    0    0    0    1    0   -1    0    0    0    0
+0           0.12           0.00    1    0    0    0    0    0    0   -2    0    3    0    0    0    0
+0          -0.12           0.00    1    0    0   -2    0    0    0    2    0   -2    0    0    0    0
+0           0.12           0.00    1    0   -1    0   -1    0   -3    5    0    0    0    0    0    0
+0          -0.12           0.00    1    0   -1   -1    0    0    0    8  -15    0    0    0    0    0
+0           0.00          -0.12    1    0   -1   -2   -1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    1    0   -2   -2   -2    0    0    1    0   -1    0    0    0    0
+0           0.12           0.00    0    0    2    2    2    0    0    2    0   -2    0    0    0    0
+0          -0.12           0.00    0    0    2   -2    1    0    0   -2    0    1    0    0    0    0
+0          -0.12           0.00    0    0    2   -2    1    0    0   -2    0    0    0    0    0    0
+0           0.12           0.00    0    0    2   -2    1    0    0   -4    4    0    0    0    0    0
+0           0.00           0.12    0    0    2   -2    1    0    0   -7    9    0    0    0    0    0
+0          -0.12           0.00    0    0    2   -2    1    0    0  -10   15    0    0    0    0    0
+0           0.12           0.00    0    0    1   -1    1    0    0    1   -4    0    0    0    0    0
+0           0.00           0.12    0    0    1   -1    1    0    0   -1    0    1   -3    0    0    0
+0           0.00           0.12    0    0    1   -1    1    0   -1    2    0    0    0    0    0    0
+0           0.12           0.00    0    0    1   -1    1    0   -4    6    0    0    0    0    0    0
+0          -0.12           0.00    0    0    0    2    0    0    0    2    0   -2    0    0    0    0
+0          -0.12           0.00    0    0    0    2    0    0   -2    2    0    0    0    0    0    0
+0           0.12           0.00    0    0    0    0    0    0    9  -13    0    0    0    0    0   -2
+0          -0.12           0.00    0    0    0    0    0    0    8  -11    0    0    0    0    0   -1
+0           0.12           0.00    0    0    0    0    0    0    8  -14    0    0    0    0    0   -2
+0           0.00          -0.12    0    0    0    0    0    0    7  -11    0    0    0    0    0   -1
+0           0.00          -0.12    0    0    0    0    0    0    6   -4    0    0    0    0    0    1
+0           0.00          -0.12    0    0    0    0    0    0    6   -6    0    0    0    0    0    1
+0          -0.12           0.00    0    0    0    0    0    0    6   -7    0    0    0    0    0   -1
+0          -0.12           0.00    0    0    0    0    0    0    6   -8    0    0    0    0    0    0
+0           0.00          -0.12    0    0    0    0    0    0    6   -9    0    0    0    0    0    0
+0           0.00           0.12    0    0    0    0    0    0    5   -4    0    0    0    0    0    2
+0          -0.12           0.00    0    0    0    0    0    0    5   -6    0    0    0    0    0   -1
+0           0.00          -0.12    0    0    0    0    0    0    5   -6    0    0    0    0    0   -2
+0          -0.12           0.00    0    0    0    0    0    0    5   -6   -4    0    0    0    0   -2
+0           0.00           0.12    0    0    0    0    0    0    4   -2    0    0    0    0    0    0
+0           0.00          -0.12    0    0    0    0    0    0    4   -5    0    0    0    0    0    0
+0           0.12           0.00    0    0    0    0    0    0    4   -8    0    0    0    0    0   -2
+0          -0.12           0.00    0    0    0    0    0    0    3   -1    0    0    0    0    0    0
+0          -0.12           0.00    0    0    0    0    0    0    3   -3    0    2    0    0    0    2
+0           0.12           0.00    0    0    0    0    0    0    3   -4    0    0    0    0    0    1
+0           0.00           0.12    0    0    0    0    0    0    2    1    0    0    0    0    0    1
+0          -0.12           0.00    0    0    0    0    0    0    2    0    0    0    0    0    0    0
+0           0.12           0.00    0    0    0    0    0    0    1   -1    0    0    0    0    0   -2
+0           0.12           0.00    0    0    0    0    0    0    1   -4    0    0    0    0    0   -1
+0          -0.12           0.00    0    0    0    0    0    0    0    9  -17    0    0    0    0   -2
+0           0.00           0.12    0    0    0    0    0    0    0    7   -7    0    0    0    0    2
+0           0.00           0.12    0    0    0    0    0    0    0    7  -12    0    0    0    0   -2
+0          -0.12           0.00    0    0    0    0    0    0    0    6   -4    0    0    0    0    2
+0           0.00          -0.12    0    0    0    0    0    0    0    6   -8    1    5    0    0    2
+0          -0.12           0.00    0    0    0    0    0    0    0    6   -9    0    0    0    0   -2
+0           0.00           0.12    0    0    0    0    0    0    0    6  -10    0    0    0    0    0
+0          -0.12           0.00    0    0    0    0    0    0    0    5    0   -4    0    0    0    2
+0           0.12           0.00    0    0    0    0    0    0    0    5   -6    0    0    0    0    0
+0          -0.12           0.00    0    0    0    0    0    0    0    5   -7    0    0    0    0   -2
+0          -0.12           0.00    0    0    0    0    0    0    0    5   -8    3    0    0    0    2
+0          -0.12           0.00    0    0    0    0    0    0    0    5   -9    0    0    0    0   -1
+0          -0.12           0.00    0    0    0    0    0    0    0    5  -13    0    0    0    0   -2
+0           0.12           0.00    0    0    0    0    0    0    0    5  -16    4    5    0    0   -2
+0          -0.12           0.00    0    0    0    0    0    0    0    4   -7    0    0    0    0   -1
+0           0.12           0.00    0    0    0    0    0    0    0    4   -8    3    0    0    0    1
+0           0.12           0.00    0    0    0    0    0    0    0    4   -8    3    0    0    0   -1
+0          -0.12           0.00    0    0    0    0    0    0    0    3    0   -5    0    0    0   -2
+0           0.00          -0.12    0    0    0    0    0    0    0    3   -5    0    0    0    0   -1
+0           0.00          -0.12    0    0    0    0    0    0    0    3   -7    0    0    0    0   -2
+0           0.00          -0.12    0    0    0    0    0    0    0    3   -9    0    0    0    0   -2
+0           0.12           0.00    0    0    0    0    0    0    0    2    1    0    0    0    0    2
+0           0.12           0.00    0    0    0    0    0    0    0    2    0    2    0    0    0    2
+0           0.00           0.12    0    0    0    0    0    0    0    2    0    0   -3    0    0    0
+0           0.00          -0.12    0    0    0    0    0    0    0    2   -8    1    5    0    0   -2
+0           0.00           0.12    0    0    0    0    0    0    0    1    0    1    0    0    0    1
+0          -0.12           0.00    0    0    0    0    0    0    0    1    0    1   -5    0    0    0
+0           0.00          -0.12    0    0    0    0    0    0    0    1    0    0    2    0    0    2
+0          -0.12           0.00    0    0    0    0    0    0    0    1    0    0   -3    0    0    0
+0           0.12           0.00    0    0    0    0    0    0    0    1    0   -3    5    0    0    0
+0           0.00           0.12    0    0    0    0    0    0    0    1   -3    0    0    0    0    0
+0           0.12           0.00    0    0    0    0    0    0    0    0    0    2   -6    3    0   -2
+0           0.00           0.12    0    0    0    0    0    0    0    0    0    1   -2    0    0    0
+0           0.00           0.12    0    0    0    0    0    0    0    0    0    0    0    1    0    0
+0           0.12           0.00    0    0    0    0    0    0    0    0    0    0    0    0    0    2
+0          -0.12           0.00    1    1    0   -6    0    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    1    0    0    3    0    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    0    0   -2    0    0    0   -2    0    0    5    0    0    0
+0          -0.12           0.00    3    0   -2   -1   -1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    1    2    2   -4    1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    0    0    1   -1    0    0   -2    2    0    0    0    0    0    0
+0           0.00           0.12    1    0   -1    1    0    0    0    1    0    0    0    0    0    0
+0           0.12           0.00    0    0    2   -2    1    0    1   -1    0    0    0    0    0    0
+0          -0.12           0.00    2    0    2   -6    1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    2    1    2   -2    0    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    2    1    2    0    0    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    1    0    2    1    0    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    4    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    0    0    0    0    1    0    0    7  -13    0    0    0    0    0
+0           0.12           0.00    2    1    0    2    1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    1   -1    0    4    1    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    3    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    0    0   -6   -1    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    0    0    1   -1   -1    0   -5    7    0    0    0    0    0    0
+0           0.12           0.00    0    0    0    0    2    0   -3    5    0    0    0    0    0    0
+0           0.00          -0.12    0    0    1    1    2    0    0    1    0    0    0    0    0    0
+0           0.00          -0.12    0    0    1   -1    2    0   -3    4    0    0    0    0    0    0
+0           0.00           0.12    0    0    0    0    1    0   -1    2    0    0    0    0    0    0
+0           0.12           0.00    2   -1    0   -2    2    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    1    1   -2    1   -2    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    0    0    1   -1    2    0    0   -1    0    0   -1    0    0    0
+0          -0.12           0.00    2    1    2   -4    2    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    0    0   -4    2    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    3    0    0    0   -2    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    1    0    0   -4    2    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    2    0    0   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.12           0.00    1    1    0    0   -2    0    0    0    0    0    0    0    0    0
+0          -0.12           0.00    0    0    0    0    1    0    0    2    0   -2    0    0    0    0
+0          -0.12           0.00    1    0   -2    4   -2    0    0    0    0    0    0    0    0    0
+0           0.11           0.00    0    0    4   -4    4    0    0    0    0    0    0    0    0    0
+1       -3328.48      205833.15    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+1         197.53       12814.01    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+1          41.19        2187.91    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+1         -35.85       -2004.36    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+1          59.15         501.82    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+1          -5.82         448.76    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+1        -179.56         164.33    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+1           5.67         288.49    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+1          23.85        -214.50    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+1           2.87        -154.91    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+1           2.14        -119.21    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+1           1.17         -74.33    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+1           1.47          70.31    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.42          58.94    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+1          -0.95          57.12    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1          -1.08         -54.19    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+1           0.92          36.78    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+1           0.68         -31.01    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+1           0.74          29.60    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+1          -0.60         -27.59    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+1         -11.11         -15.07    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+1          -0.40         -24.05    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.81          19.06    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+1           3.18          15.32    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+1          -0.08         -17.90    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+1          -0.16          15.55    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+1          -0.77          14.40    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.25          11.67    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+1           6.18           3.58    0    0    1   -1    1    0    0   -1    0   -2    5    0    0    0
+1          -1.00          -7.27    0    1    2    0    2    0    0    0    0    0    0    0    0    0
+1          -0.99           6.87    0    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+1          -0.27           7.49    0    0    2    2    1    0    0    0    0    0    0    0    0    0
+1          -0.30           7.31    0    0    0    2    1    0    0    0    0    0    0    0    0    0
+1           0.20           7.30    1    0    2    2    2    0    0    0    0    0    0    0    0    0
+1           0.33           6.80    2    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+1           0.27          -6.81    1    0    2   -2    1    0    0    0    0    0    0    0    0    0
+1           0.35           6.08    0    0    0    2   -1    0    0    0    0    0    0    0    0    0
+1           0.35           6.09    0    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+1          -0.14          -6.19    2    0    2   -2    2    0    0    0    0    0    0    0    0    0
+1           0.14           6.02    2    0    2    0    1    0    0    0    0    0    0    0    0    0
+1           2.69          -2.76    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+1          -0.08          -4.93    2    0    0   -2    1    0    0    0    0    0    0    0    0    0
+1           2.85          -1.77    0    0    0    0    0    0    0    0    0    2   -5    0    0   -1
+1          -0.07          -4.27    0    1    2   -2    1    0    0    0    0    0    0    0    0    0
+1          -3.71           0.38    0    2    0    0    0    0    0    0    0    0    0    0    0    0
+1           3.75           0.04    0    1   -1    1   -1    0    0    0    0    0    0    0    0    0
+1          -0.82          -2.73    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+1          -0.06           2.93    2    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+1          -0.04           2.83    2    0    0    0   -1    0    0    0    0    0    0    0    0    0
+1           0.08           2.75    3    0    2    0    2    0    0    0    0    0    0    0    0    0
+1           0.07           2.75    1   -1    2    0    2    0    0    0    0    0    0    0    0    0
+1          -0.07           2.70    1    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1          -0.07           2.52    0    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1          -0.05          -2.53    2    0    0    0    1    0    0    0    0    0    0    0    0    0
+1          -0.04           2.40    1    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+1          -0.06          -2.37    1    1    2    0    2    0    0    0    0    0    0    0    0    0
+1           0.69          -1.45    0    0    1   -1    1    0   -8   12    0    0    0    0    0    0
+1          -0.04           2.00    0    0    0    0    0    0    3   -5    0    0    0    0    0   -2
+1           1.99           0.02    1    0   -1    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.94           1.07    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+1           0.04           1.91    1    0    0    0    2    0    0    0    0    0    0    0    0    0
+1          -0.58          -1.36    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+1          -0.51          -1.25    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+1          -0.04          -1.59    0    0    2    1    2    0    0    0    0    0    0    0    0    0
+1           0.39          -1.23    1    0    0   -1    0    0    0    0    0    0    0    0    0    0
+1           0.03          -1.57    1   -1    0   -1   -1    0    0    0    0    0    0    0    0    0
+1          -0.03           1.50    0    2   -2    2   -1    0    0    0    0    0    0    0    0    0
+1           0.04           1.48    1    0    2    2    1    0    0    0    0    0    0    0    0    0
+1          -0.04           1.45    1    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+1           0.02          -1.36    1    0    0    0   -2    0    0    0    0    0    0    0    0    0
+1           0.03          -1.32    2    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1          -0.03          -1.24    1    1    2   -2    2    0    0    0    0    0    0    0    0    0
+1          -0.02          -1.18    2    0    2   -2    1    0    0    0    0    0    0    0    0    0
+1          -0.03           1.16    2    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+1           0.02           1.13    0    0    0    0    0    0    0    0    0    2    0    0    0    2
+1           0.04          -1.11    1    0    0   -1   -1    0    0    0    0    0    0    0    0    0
+1           0.02           1.11    1    0    0    2    1    0    0    0    0    0    0    0    0    0
+1           0.03          -1.10    1    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+1           0.03           1.04    2    0    2    2    2    0    0    0    0    0    0    0    0    0
+1          -0.51           0.56    0    0    0    0    1    0    0   -1    2    0    0    0    0    0
+1           0.02          -0.98    0    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+1          -0.02          -0.94    0    1    2    0    1    0    0    0    0    0    0    0    0    0
+1          -0.02          -0.89    3    0    2   -2    2    0    0    0    0    0    0    0    0    0
+1          -0.02          -0.88    0    0    4   -2    2    0    0    0    0    0    0    0    0    0
+1           0.31           0.60    0    0    0    0    0    0    8  -13    0    0    0    0    0   -1
+1           0.02          -0.87    1    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+1          -0.02          -0.87    2    0   -2    0    1    0    0    0    0    0    0    0    0    0
+1          -0.01           0.83    0    0    2    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.02           0.77    0    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+1           0.42          -0.36    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.73    0    1    0    0    2    0    0    0    0    0    0    0    0    0
+1           0.01           0.71    1    1    0   -2    1    0    0    0    0    0    0    0    0    0
+1           0.01           0.68    0    0    2   -1    2    0    0    0    0    0    0    0    0    0
+1           0.02           0.66    0    0    2    4    2    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.62    1   -1    0    0    1    0    0    0    0    0    0    0    0    0
+1          -0.01           0.62    1    0    0    2   -1    0    0    0    0    0    0    0    0    0
+1          -0.58          -0.03    0    0    1   -1    1    0    0   -1    0    2   -5    0    0    0
+1          -0.01           0.58    1   -1    0   -1   -2    0    0    0    0    0    0    0    0    0
+1           0.44           0.14    0    0    0    0    0    0    0    2   -8    3    0    0    0   -2
+1           0.02           0.56    1   -1    2    2    2    0    0    0    0    0    0    0    0    0
+1           0.01          -0.57    0    1    0    0   -2    0    0    0    0    0    0    0    0    0
+1          -0.13          -0.45    1    0    0    2    0    0    0    0    0    0    0    0    0    0
+1           0.01           0.56    3    0    2    0    1    0    0    0    0    0    0    0    0    0
+1           0.01          -0.55    1   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1           0.01           0.55    1    0    2   -4    1    0    0    0    0    0    0    0    0    0
+1          -0.52           0.03    0    0    2   -2    1    0   -5    6    0    0    0    0    0    0
+1          -0.01           0.54    1    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.51    0    1    2    2    2    0    0    0    0    0    0    0    0    0
+1          -0.41          -0.11    0    0    0    0    0    0    0    6   -8    3    0    0    0    2
+1          -0.01           0.50    0    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+1           0.01           0.48    1   -1    2    0    1    0    0    0    0    0    0    0    0    0
+1           0.45          -0.04    1    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+1           0.01          -0.48    0    0    0    0    0    0    4   -6    0    0    0    0    0   -2
+1           0.01           0.46    2   -1    2    0    2    0    0    0    0    0    0    0    0    0
+1          -0.23           0.24    2    0   -2    0    0    0    0    0    0    0    0    0    0    0
+1           0.01           0.46    0    0    0    1    1    0    0    0    0    0    0    0    0    0
+1           0.35          -0.11    0    0    0    0    1    0    0   -4    8   -3    0    0    0    0
+1           0.01           0.45    0    0    0    2    2    0    0    0    0    0    0    0    0    0
+1           0.01          -0.45    1   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+1           0.00          -0.45    0    0    0    0    0    0    0    3    0   -1    0    0    0    2
+1          -0.01           0.44    0    0    0    0    0    0    2   -4    0    0    0    0    0   -2
+1           0.35           0.09    0    0    0    0    1    0    0    4   -8    3    0    0    0    0
+1           0.01           0.42    1    1    0    0    1    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.41    1    1    2    0    1    0    0    0    0    0    0    0    0    0
+1           0.09          -0.33    0    0    1   -1    1    0    0    0   -2    0    0    0    0    0
+1           0.00           0.41    0    1    0   -2    1    0    0    0    0    0    0    0    0    0
+1           0.01           0.40    0    3    2   -2    2    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.39    2    1    2    0    2    0    0    0    0    0    0    0    0    0
+1          -0.39          -0.01    1    0   -1    0   -2    0    0    0    0    0    0    0    0    0
+1           0.01          -0.39    0    0    0    1   -1    0    0    0    0    0    0    0    0    0
+1          -0.01           0.38    1    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+1           0.32          -0.07    0    0    0    0    0    0    0    0    0    2    0    0    0    1
+1          -0.01           0.36    1   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.36    0    0    0    0    0    0    2    0    0    0    0    0    0    2
+1           0.01          -0.34    1    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+1           0.01          -0.34    2    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+1           0.01           0.33    0    0    0    0    0    0    1    1    0    0    0    0    0    2
+1          -0.01          -0.32    1    0    2    1    2    0    0    0    0    0    0    0    0    0
+1           0.01           0.32    1    0    2   -1    2    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.32    0    0    2    1    1    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.31    1    0   -2    0    1    0    0    0    0    0    0    0    0    0
+1          -0.31           0.00    0    0    1    0    1    0    0    0    0    0    0    0    0    0
+1          -0.07          -0.24    1    0    2    0    0    0    0    0    0    0    0    0    0    0
+1           0.10          -0.21    0    0    1   -1    1    0    0   -1    0    0   -1    0    0    0
+1          -0.01          -0.30    1    0    0   -2    2    0    0    0    0    0    0    0    0    0
+1          -0.01           0.29    1    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.29    1    1    2   -2    1    0    0    0    0    0    0    0    0    0
+1           0.00           0.29    1    0   -2    1   -1    0    0    0    0    0    0    0    0    0
+1           0.23           0.06    0    0    1   -1    1    0    0   -1    0   -1    0    0    0    0
+1           0.26           0.02    0    0    0    0    0    0    0    0    0    2   -5    0    0    1
+1           0.00          -0.27    2    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+1           0.25           0.02    0    0    0    0    0    0    5   -8    0    0    0    0    0   -2
+1           0.09          -0.18    0    1    0   -2    0    0    0    0    0    0    0    0    0    0
+1           0.01           0.25    0    0    0    0    0    0    0    1    0    1    0    0    0    2
+1           0.14          -0.11    1    1    0   -2    0    0    0    0    0    0    0    0    0    0
+1           0.00          -0.25    1    1    0    0   -1    0    0    0    0    0    0    0    0    0
+1           0.01           0.24    4    0    2    0    2    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.24    2    1    2   -2    2    0    0    0    0    0    0    0    0    0
+1           0.00           0.23    2    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+1           0.01           0.23    0    1    2    1    2    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.23    1    0    4   -2    2    0    0    0    0    0    0    0    0    0
+1           0.00          -0.23    0    1    0    2    1    0    0    0    0    0    0    0    0    0
+1           0.00          -0.22    1    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+1           0.00           0.21    1    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+1           0.01           0.21    2    0    2    2    1    0    0    0    0    0    0    0    0    0
+1          -0.17           0.03    0    0    1   -1    1    0    0   -1    0    2    0    0    0    0
+1          -0.17           0.03    0    0    0    0    0    0    8  -13    0    0    0    0    0   -2
+1           0.00          -0.19    0    0    0    0    0    0    5   -7    0    0    0    0    0   -2
+1           0.14          -0.06    0    0    0    0    0    0    0    0    0    0    2    0    0    1
+1           0.03          -0.17    0    0    1   -1    1    0   -5    7    0    0    0    0    0    0
+1          -0.13           0.06    0    0    1   -1    1    0    0   -1    0    0    2    0    0    0
+1           0.00           0.19    0    0    2   -4    1    0    0    0    0    0    0    0    0    0
+1           0.00           0.19    0    0    2   -3    2    0    0    0    0    0    0    0    0    0
+1          -0.06          -0.13    1    0    2    0    2    0    0    1    0    0    0    0    0    0
+1           0.00           0.18    0    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+1          -0.09          -0.09    1   -1    0    0    0    0    0    0    0    0    0    0    0    0
+1           0.10          -0.09    0    0    0    0    0    0    0    8  -16    4    5    0    0   -2
+1           0.06           0.12    1    0   -2    0   -2    0    0    4   -8    3    0    0    0    0
+1           0.00           0.18    0    0    0    0    0    0    0    4    0   -2    0    0    0    2
+1           0.00          -0.18    0    0    4    0    2    0    0    0    0    0    0    0    0    0
+1           0.00           0.17    0    0    0    4    1    0    0    0    0    0    0    0    0    0
+1          -0.03           0.15    0    0    0    0    0    0    0    2    0   -1    0    0    0    2
+1          -0.01          -0.16    0    0    0    0    0    0    0    2    0    1    0    0    0    2
+1           0.00           0.17    1    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+1           0.00          -0.17    2    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+1           0.11           0.06    0    0    0    0    0    0    8  -11    0    0    0    0    0   -2
+1           0.00          -0.17    3    0    2   -2    1    0    0    0    0    0    0    0    0    0
+1          -0.08           0.09    1    0   -2    0    0    0    0    0    0    0    0    0    0    0
+1          -0.17           0.00    0    0    0    0    0    0    2   -1    0    0    0    0    0    2
+1           0.00          -0.16    0    0    4   -2    1    0    0    0    0    0    0    0    0    0
+1           0.01           0.15    1    0    2    4    2    0    0    0    0    0    0    0    0    0
+1          -0.13          -0.03    0    0    0    0    0    0    1   -1    0    0    0    0    0   -1
+1           0.00           0.15    1    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+1           0.00           0.15    2    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+1          -0.13           0.03    0    0    0    0    0    0    4   -6    0    0    0    0    0   -1
+1           0.10          -0.06    0    0    0    0    0    0    8  -15    0    0    0    0    0   -2
+1          -0.07           0.08    0    0    0    0    0    0    0    1    2    0    0    0    0    2
+1          -0.09          -0.06    0    0    0    0    0    0    0    3    0   -2    0    0    0    2
+1           0.00           0.15    3    0    0    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.07          -0.08    0    0    0    0    0    0    0    3   -2    0    0    0    0    2
+1           0.00          -0.14    2    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+1           0.02           0.12    0    0    0    0    0    0    0    0    0    3    0    0    0    2
+1           0.07           0.08    1    1    0    0    0    0    0    0    0    0    0    0    0    0
+1          -0.03          -0.11    3    0    0    0    0    0    0    0    0    0    0    0    0    0
+1          -0.01          -0.14    0    0    0    0    0    0    0    4   -2    0    0    0    0    2
+1           0.00          -0.14    0    0    2   -2    1    0   -3    3    0    0    0    0    0    0
+1           0.02          -0.12    1    0    0   -1    1    0    0    0    0    0    0    0    0    0
+1           0.00          -0.14    1    1    2    2    2    0    0    0    0    0    0    0    0    0
+1           0.00           0.14    0    0    1   -1    1    0    0    3   -8    3    0    0    0    0
+1           0.00           0.14    0    0    2   -1    1    0    0    0    0    0    0    0    0    0
+1           0.00           0.13    0    0    2    4    1    0    0    0    0    0    0    0    0    0
+1           0.08          -0.06    0    0    0    0    1    0    8  -13    0    0    0    0    0    0
+1           0.00           0.13    1   -1    0   -2   -1    0    0    0    0    0    0    0    0    0
+1           0.00           0.13    0    0    0    0    0    0    0    0    0    0    2    0    0    2
+1           0.01           0.13    3    0    2    2    2    0    0    0    0    0    0    0    0    0
+1           0.00           0.13    2    0    0    2    1    0    0    0    0    0    0    0    0    0
+1           0.00           0.13    1   -1   -2    2   -1    0    0    0    0    0    0    0    0    0
+1          -0.02          -0.11    2    0    0   -2    1    0    0   -2    0    3    0    0    0    0
+1           0.08          -0.04    0    0    0    1    0    0    0    0    0    0    0    0    0    0
+1           0.00           0.13    2    0    0   -4    1    0    0    0    0    0    0    0    0    0
+1           0.00           0.13    1    0    0   -4    1    0    0    0    0    0    0    0    0    0
+1           0.01          -0.12    2    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+1           0.00           0.12    0    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1          -0.02          -0.11    0    0    0    0    0    0    5   -8    0    0    0    0    0   -1
+1           0.00          -0.12    2    0    2   -4    1    0    0    0    0    0    0    0    0    0
+1           0.00          -0.12    0    0    0    0    0    0    0    8  -16    4    5    0    0    2
+1           0.00          -0.12    0    0    1   -1    1    0    0   -5    8   -3    0    0    0    0
+1           0.04           0.08    1    0    0    0   -1    0  -18   16    0    0    0    0    0    0
+1           0.00          -0.12    0    0    0    0    0    0    0    1    0   -3    0    0    0   -2
+1           0.00           0.12    1   -1    0   -2   -2    0    0    0    0    0    0    0    0    0
+1           0.00          -0.12    0    0    0    2   -2    0    0    0    0    0    0    0    0    0
+1           0.00          -0.11    0    0    0    0    0    0    6   -8    0    0    0    0    0   -2
+1           0.03          -0.09    1    0    0   -4    0    0    0    0    0    0    0    0    0    0
+1           0.00           0.11    0    2    0    0    1    0    0    0    0    0    0    0    0    0
+1          -0.11           0.00    0    0    0    0    0    0    3   -2    0    0    0    0    0    2
+1           0.00           0.11    1   -1    2    2    1    0    0    0    0    0    0    0    0    0
+1           0.00          -0.11    1   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+1           0.07           0.05    0    0    0    0    1    0   -8   13    0    0    0    0    0    0
+1           0.11           0.00    0    0    0    0    0    0    2   -5    0    0    0    0    0   -2
+1           0.00          -0.11    0    0    0    0    0    0    1   -3    0    0    0    0    0   -2
+1           0.00          -0.11    4    0    2   -2    2    0    0    0    0    0    0    0    0    0
+1           0.02          -0.09    0    0    0    0    1    0    0    0    0   -2    5    0    0    0
+1           0.00           0.11    2    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+1           0.02           0.09    0    0    0    0    1    0    0    0    0    2   -5    0    0    0
+1           0.00          -0.11    0    2    2   -2    1    0    0    0    0    0    0    0    0    0
+1           0.00           0.11    1    0    2    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.08          -0.02    0    0    0    0    1    0    0    0    0    1    0    0    0    0
+1           0.00          -0.10    2    1    0   -2    1    0    0    0    0    0    0    0    0    0
+1           0.00          -0.10    2   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.03          -0.07    1    0    0    0    1    0  -18   16    0    0    0    0    0    0
+1           0.00           0.10    1    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1           0.00           0.10    2    0    0   -2    2    0    0    0    0    0    0    0    0    0
+1           0.00          -0.10    1   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+2        2038.00          82.26    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+2         155.75          -2.70    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+2          26.92          -0.45    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+2         -24.43           0.46    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+2         -17.36          -0.50    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+2          -8.41           0.01    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+2           6.08          -1.36    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+2           4.59           0.17    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+2           3.57          -0.06    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+2           2.54           0.60    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+2           1.86           0.00    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+2          -1.52          -0.07    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+2           1.46           0.04    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+2          -0.75          -0.02    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+2          -0.75           0.00    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+2          -0.71          -0.01    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+2          -0.69           0.02    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+2           0.61           0.02    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+2           0.54          -0.04    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+2          -0.56           0.00    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+2           0.46          -0.02    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+2           0.38          -0.01    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+2           0.37          -0.02    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+2          -0.34           0.01    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+2          -0.35           0.00    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+2          -0.31           0.00    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+2           0.19          -0.09    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+2           0.26           0.00    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+2           0.24          -0.01    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+2          -0.20           0.00    0    2    0    0    0    0    0    0    0    0    0    0    0    0
+2           0.18          -0.01    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+2           0.17           0.00    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+2           0.15           0.01    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+2          -0.15           0.00    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+2          -0.13           0.00    2    0   -2    0    0    0    0    0    0    0    0    0    0    0
+2          -0.12           0.00    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+3           1.76         -20.39    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+3           0.00          -1.27    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+3           0.00          -0.22    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+3           0.00           0.20    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+4          -0.10          -0.02    0    0    0    0    1    0    0    0    0    0    0    0    0    0
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2a.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2a.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2a.txt	(revision 22322)
@@ -0,0 +1,1651 @@
+Expression for the X coordinate of the CIP in the GCRS based on the IAU2000A 
+precession-nutation model 
+
+
+--------------------------------------------------------------------------------------------------------------
+
+X = polynomial part + non-polynomial part
+
+--------------------------------------------------------------------------------------------------------------
+
+Polynomial part (unit microarcsecond)
+
+  -16616.99 + 2004191742.88 t - 427219.05 t^2 - 198620.54 t^3 - 46.05 t^4 + 5.98 t^5
+
+--------------------------------------------------------------------------------------------------------------
+
+Non-polynomial part (unit microarcsecond)
+(ARG being for various combination of the fundamental arguments of the nutation theory)
+
+  Sum_i[a_{s,0})_i * sin(ARG) + a_{c,0})_i * cos(ARG)] 
+
++ Sum_i)j=1,4 [a_{s,j})_i * t^j * sin(ARG) + a_{c,j})_i * cos(ARG)] * t^j]
+
+The Table below provides the values for a_{s,j})_i and a_{c,j})_i
+
+The expressions for the fundamental arguments appearing in columns 4 to 8 (luni-solar part) 
+and in columns 6 to 17 (planetary part) are those of the IERS Conventions 2000
+
+--------------------------------------------------------------------------------------------------------------
+
+    i    a_{s,j})_i      a_{c,j})_i    l    l'   F    D   Om L_Me L_Ve  L_E L_Ma  L_J L_Sa  L_U L_Ne  p_A
+
+--------------------------------------------------------------------------------------------------------------
+
+j = 0  Nb of terms = 1306        
+
+    1    -6844318.44        1328.67    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+    2     -523908.04        -544.76    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+    3      -90552.22         111.23    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+    4       82168.76         -27.64    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+    5       58707.02         470.05    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+    6       28288.28         -34.69    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+    7      -20557.78         -20.84    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+    8      -15406.85          15.12    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+    9      -11991.74          32.46    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+   10       -8584.95           4.42    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+   11       -6245.02          -6.68    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+   12        5095.50           7.19    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+   13       -4910.93           0.76    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+   14        2521.07          -5.97    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+   15        2511.85           1.07    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+   16        2372.58           5.93    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+   17        2307.58          -7.52    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+   18       -2053.16           5.13    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+   19        1898.27          -0.72    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+   20       -1825.49           1.23    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+   21       -1534.09           6.29    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+   22       -1292.02           0.00    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+   23       -1234.96           5.21    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+   24        1163.22          -2.94    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+   25        1137.48          -0.04    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+   26        1029.70          -2.63    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+   27        -866.48           0.52    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+   28        -813.13           0.40    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+   29         664.57          -0.40    0    2    0    0    0    0    0    0    0    0    0    0    0    0
+   30        -628.24          -0.64    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+   31        -603.52           0.44    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+   32        -556.26           3.16    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+   33        -512.37          -1.47    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+   34         506.65           2.54    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+   35         438.51          -0.56    2    0   -2    0    0    0    0    0    0    0    0    0    0    0
+   36         405.91           0.99    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+   37        -122.67         203.78    0    0    1   -1    1    0    0   -1    0   -2    5    0    0    0
+   38        -305.78           1.75    1    0    2    2    2    0    0    0    0    0    0    0    0    0
+   39         300.99          -0.44    0    1    2    0    2    0    0    0    0    0    0    0    0    0
+   40        -292.37          -0.32    1    1    0   -2    0    0    0    0    0    0    0    0    0    0
+   41         284.09           0.32    0    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+   42        -264.02           0.99    0    0    2    2    1    0    0    0    0    0    0    0    0    0
+   43         261.54          -0.95    1    0    0    2    0    0    0    0    0    0    0    0    0    0
+   44         256.30          -0.28    2    0    2   -2    2    0    0    0    0    0    0    0    0    0
+   45        -250.54           0.08    0    0    0    2    1    0    0    0    0    0    0    0    0    0
+   46         230.72           0.08    1    0    2   -2    1    0    0    0    0    0    0    0    0    0
+   47         229.78          -0.60    2    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+   48        -212.82           0.84    2    0    2    0    1    0    0    0    0    0    0    0    0    0
+   49         196.64          -0.84    0    0    0    2   -1    0    0    0    0    0    0    0    0    0
+   50         188.95          -0.12    0    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+   51         187.95          -0.24    1   -1    0    0    0    0    0    0    0    0    0    0    0    0
+   52        -160.15         -14.04    1    0    0   -1    0    0    0    0    0    0    0    0    0    0
+   53        -172.95          -0.40    0    1    0   -2    0    0    0    0    0    0    0    0    0    0
+   54        -168.26           0.20    0    0    0    1    0    0    0    0    0    0    0    0    0    0
+   55         161.79           0.24    2    0    0   -2    1    0    0    0    0    0    0    0    0    0
+   56         161.34           0.20    1    0   -2    0    0    0    0    0    0    0    0    0    0    0
+   57          57.44          95.82    0    0    0    0    0    0    0    0    0    2   -5    0    0   -1
+   58         142.16           0.20    0    1    2   -2    1    0    0    0    0    0    0    0    0    0
+   59        -134.81           0.20    1    1    0    0    0    0    0    0    0    0    0    0    0    0
+   60         132.81          -0.52    1    0    2    0    0    0    0    0    0    0    0    0    0    0
+   61        -130.31           0.04    1   -1    0   -1    0    0    0    0    0    0    0    0    0    0
+   62         121.98          -0.08    2    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+   63        -115.40           0.60    3    0    2    0    2    0    0    0    0    0    0    0    0    0
+   64        -114.49           0.32    1   -1    2    0    2    0    0    0    0    0    0    0    0    0
+   65         112.14           0.28    1    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+   66         105.29           0.44    0    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+   67          98.69          -0.28    1    1    2    0    2    0    0    0    0    0    0    0    0    0
+   68          91.31          -0.40    2    0    0    0   -1    0    0    0    0    0    0    0    0    0
+   69          86.74          -0.08    2    0    0    0    1    0    0    0    0    0    0    0    0    0
+   70         -18.38          63.80    0    0    0    0    0    0    0    4   -8    3    0    0    0    0
+   71          82.14           0.00    0    0    0    0    0    0    3   -5    0    0    0    0    0   -2
+   72          79.03          -0.24    1    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+   73           0.00         -79.08    0    1   -1    1   -1    0    0    0    0    0    0    0    0    0
+   74         -78.56           0.00    1    0    0    0    2    0    0    0    0    0    0    0    0    0
+   75          47.73          23.79    0    0    1   -1    1    0   -8   12    0    0    0    0    0    0
+   76          66.03          -0.20    0    0    2    1    2    0    0    0    0    0    0    0    0    0
+   77          62.65          -0.24    3    0    0    0    0    0    0    0    0    0    0    0    0    0
+   78          60.50           0.36    1    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+   79          59.07           0.00    0    0    0    0    0    0    1   -1    0    0    0    0    0    0
+   80          57.28           0.00    0    0    0    0    0    0    0    8  -16    4    5    0    0    0
+   81         -55.66           0.16    1    0    0    0   -2    0    0    0    0    0    0    0    0    0
+   82         -54.81          -0.08    2    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+   83         -53.22          -0.20    1    0    0   -4    0    0    0    0    0    0    0    0    0    0
+   84         -52.95           0.32    1    0    2    2    1    0    0    0    0    0    0    0    0    0
+   85         -52.27           0.00    1   -1    0   -1   -1    0    0    0    0    0    0    0    0    0
+   86          51.32           0.00    1    1    2   -2    2    0    0    0    0    0    0    0    0    0
+   87         -51.00          -0.12    2    0    0   -4    0    0    0    0    0    0    0    0    0    0
+   88          51.02           0.00    0    2   -2    2   -1    0    0    0    0    0    0    0    0    0
+   89         -48.65          -1.15    0    0    0    0    0    0    0    1    0   -1    0    0    0    0
+   90          48.29           0.20    2    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+   91         -46.38           0.00    0    0    0    0    0    0    0    0    0    2    0    0    0    2
+   92         -45.59          -0.12    1    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+   93         -43.76           0.36    2    0    2    2    2    0    0    0    0    0    0    0    0    0
+   94         -40.58          -1.00    1    0    0   -1   -1    0    0    0    0    0    0    0    0    0
+   95           0.00         -41.53    1    0   -1    0   -1    0    0    0    0    0    0    0    0    0
+   96          40.54          -0.04    2    0    2   -2    1    0    0    0    0    0    0    0    0    0
+   97          40.33          -0.04    2    1    0   -2    0    0    0    0    0    0    0    0    0    0
+   98         -38.57           0.08    1    0    0    2    1    0    0    0    0    0    0    0    0    0
+   99          37.75           0.04    1   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+  100          37.15          -0.12    3    0    2   -2    2    0    0    0    0    0    0    0    0    0
+  101          36.68          -0.04    0    0    4   -2    2    0    0    0    0    0    0    0    0    0
+  102         -18.30         -17.30    0    0    0    0    1    0    0   -1    2    0    0    0    0    0
+  103         -17.86          17.10    0    0    0    0    0    0    0    1   -2    0    0    0    0    0
+  104         -34.81           0.04    0    1   -2    2    0    0    0    0    0    0    0    0    0    0
+  105         -33.22           0.08    0    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+  106          32.43          -0.04    0    1    2    0    1    0    0    0    0    0    0    0    0    0
+  107         -30.47           0.04    1    0    2   -2    0    0    0    0    0    0    0    0    0    0
+  108         -29.53           0.04    1    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+  109          28.50          -0.08    2    0   -2    0    1    0    0    0    0    0    0    0    0    0
+  110          28.35          -0.16    0    1    0    0    2    0    0    0    0    0    0    0    0    0
+  111         -28.00           0.00    0    0    2   -1    2    0    0    0    0    0    0    0    0    0
+  112         -27.61           0.20    0    0    2    4    2    0    0    0    0    0    0    0    0    0
+  113         -26.77           0.08    0    1    0    2    0    0    0    0    0    0    0    0    0    0
+  114          26.54          -0.12    0    0    2    0   -1    0    0    0    0    0    0    0    0    0
+  115          26.54           0.04    0    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+  116         -26.17           0.00    0    1    2   -2    0    0    0    0    0    0    0    0    0    0
+  117         -25.42          -0.08    1    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+  118         -16.91           8.43    0    0    0    0    0    0    8  -13    0    0    0    0    0   -1
+  119           0.32          24.42    0    0    0    0    0    0    2   -3    0    0    0    0    0    0
+  120         -19.53           5.09    0    0    0    0    0    0    0    0    0    2   -5    0    0    0
+  121         -23.79           0.00    0    0    0    0    0    0    2   -2    0    0    0    0    0    0
+  122          23.66           0.00    1   -1    0   -1   -2    0    0    0    0    0    0    0    0    0
+  123         -23.47           0.16    1   -1    2    2    2    0    0    0    0    0    0    0    0    0
+  124          23.39          -0.12    2    0    0    2    0    0    0    0    0    0    0    0    0    0
+  125         -23.49           0.00    0    1    0    0   -2    0    0    0    0    0    0    0    0    0
+  126         -23.28          -0.08    1    1    0   -2    1    0    0    0    0    0    0    0    0    0
+  127         -22.99           0.04    1    0   -2    2    0    0    0    0    0    0    0    0    0    0
+  128         -22.67          -0.08    1   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  129           9.35          13.29    0    0    0    0    0    0    8  -13    0    0    0    0    0    0
+  130          22.47          -0.04    0    1    0    1    0    0    0    0    0    0    0    0    0    0
+  131           4.89         -16.55    0    0    0    0    0    0    0    2   -8    3    0    0    0   -2
+  132           4.89         -16.51    0    0    0    0    0    0    0    6   -8    3    0    0    0    2
+  133          21.28          -0.08    0    1    2    2    2    0    0    0    0    0    0    0    0    0
+  134          20.57           0.64    0    0    0    0    0    0    0    3    0   -1    0    0    0    2
+  135          21.01           0.00    1   -1    0    0    1    0    0    0    0    0    0    0    0    0
+  136           1.23         -19.13    0    0    1   -1    1    0    0   -1    0    2   -5    0    0    0
+  137         -19.97           0.12    3    0    2    0    1    0    0    0    0    0    0    0    0    0
+  138          19.65          -0.08    0    0    0    4    0    0    0    0    0    0    0    0    0    0
+  139          19.58          -0.12    1    0    0    2   -1    0    0    0    0    0    0    0    0    0
+  140          19.61          -0.08    1   -1    0    2    0    0    0    0    0    0    0    0    0    0
+  141         -19.41           0.08    2   -1    2    0    2    0    0    0    0    0    0    0    0    0
+  142         -19.49           0.00    0    0    0    0    0    0    4   -6    0    0    0    0    0   -2
+  143         -18.64           0.00    0    0    0    2    2    0    0    0    0    0    0    0    0    0
+  144          18.58           0.04    1    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  145         -18.42           0.00    1   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+  146          18.22           0.00    0    0    0    0    0    0    2   -4    0    0    0    0    0   -2
+  147          -0.72         -17.34    0    0    2   -2    1    0   -5    6    0    0    0    0    0    0
+  148         -18.02          -0.04    1    0    2   -4    1    0    0    0    0    0    0    0    0    0
+  149          17.74           0.08    0    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  150          17.46           0.00    2    0    0   -2    0    0    0   -2    0    2    0    0    0    0
+  151         -17.42           0.00    0    3    2   -2    2    0    0    0    0    0    0    0    0    0
+  152          -6.60          10.70    0    0    0    0    0    0    0    1    0   -2    0    0    0    0
+  153          16.43           0.52    0    0    0    0    0    0    0    2    0   -2    0    0    0    0
+  154         -16.75           0.04    1   -1    2    0    1    0    0    0    0    0    0    0    0    0
+  155          16.55          -0.08    0    0    2    2    0    0    0    0    0    0    0    0    0    0
+  156          16.39          -0.08    2    1    2    0    2    0    0    0    0    0    0    0    0    0
+  157          13.88          -2.47    2    0    0   -2    0    0    0   -2    0    3    0    0    0    0
+  158          15.69           0.00    1    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+  159         -15.52           0.00    0    0    0    1    1    0    0    0    0    0    0    0    0    0
+  160           3.34          11.86    0    0    0    0    1    0    0   -4    8   -3    0    0    0    0
+  161          14.72          -0.32    0    0    0    0    0    0    2    0    0    0    0    0    0    2
+  162          14.92          -0.04    2   -1    0    0    0    0    0    0    0    0    0    0    0    0
+  163          -3.26          11.62    0    0    0    0    1    0    0    4   -8    3    0    0    0    0
+  164         -14.64           0.00    0    0    0    0    0    0    0    2   -2    0    0    0    0    0
+  165           0.00          14.47    1    0   -1    0   -2    0    0    0    0    0    0    0    0    0
+  166         -14.37           0.00    1    1    0    0    1    0    0    0    0    0    0    0    0    0
+  167          14.32          -0.04    1    1    2    0    1    0    0    0    0    0    0    0    0    0
+  168         -14.10           0.04    1    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+  169          10.86           3.18    0    0    1   -1    1    0    0    0   -2    0    0    0    0    0
+  170         -10.58          -3.10    0    0    1   -1    0    0    0    0   -2    0    0    0    0    0
+  171          -3.62           9.86    0    0    0    0    0    0    0    0    0    1    0    0    0    0
+  172         -13.48           0.00    0    0    0    0    0    0    1    1    0    0    0    0    0    2
+  173          13.41          -0.04    1    0    2    1    2    0    0    0    0    0    0    0    0    0
+  174          13.32          -0.08    2    0    2    0    0    0    0    0    0    0    0    0    0    0
+  175         -13.33          -0.04    0    1    0   -2    1    0    0    0    0    0    0    0    0    0
+  176         -13.29           0.00    1    0    2   -1    2    0    0    0    0    0    0    0    0    0
+  177          -0.20          13.05    0    0    0    0    0    0    3   -4    0    0    0    0    0    0
+  178           0.00          13.13    1    0   -1    0    0    0    0    0    0    0    0    0    0    0
+  179          -8.99           4.02    1    0    0    0    0    0  -18   16    0    0    0    0    0    0
+  180         -12.93           0.04    1    0    0    1    0    0    0    0    0    0    0    0    0    0
+  181           2.03          10.82    0    0    0    0    0    0    0    0    0    2    0    0    0    1
+  182         -12.78           0.04    0    0    0    1   -1    0    0    0    0    0    0    0    0    0
+  183          12.24           0.04    1    0    0   -2    2    0    0    0    0    0    0    0    0    0
+  184           8.71           3.54    1    0    0    0    0    0  -10    3    0    0    0    0    0    0
+  185          11.98          -0.04    1   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+  186         -11.38           0.04    2    1    0    0    0    0    0    0    0    0    0    0    0    0
+  187         -11.30           0.00    2    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+  188          11.14          -0.04    0    0    2    1    1    0    0    0    0    0    0    0    0    0
+  189          10.98           0.00    0    3    0    0    0    0    0    0    0    0    0    0    0    0
+  190         -10.98           0.00    1    2    0   -2    0    0    0    0    0    0    0    0    0    0
+  191           0.44         -10.38    0    0    0    0    0    0    5   -8    0    0    0    0    0   -2
+  192          10.46           0.08    1    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  193         -10.42           0.00    0    0    0    0    0    0    0    1    0    1    0    0    0    2
+  194         -10.30           0.08    4    0    2    0    2    0    0    0    0    0    0    0    0    0
+  195           6.92           3.34    0    0    1   -1    1    0    0   -1    0    0   -1    0    0    0
+  196          10.07           0.04    1    0   -2    0    1    0    0    0    0    0    0    0    0    0
+  197          10.02           0.00    2    1    2   -2    2    0    0    0    0    0    0    0    0    0
+  198          -9.75           0.04    0    1    2    1    2    0    0    0    0    0    0    0    0    0
+  199           9.75           0.00    1    1    2   -2    1    0    0    0    0    0    0    0    0    0
+  200           9.67          -0.04    1    0    4   -2    2    0    0    0    0    0    0    0    0    0
+  201          -1.99           7.72    0    0    1   -1    1    0    0   -1    0   -1    0    0    0    0
+  202           0.40           9.27    0    0    2   -2    0    0   -5    6    0    0    0    0    0    0
+  203          -3.42           6.09    0    0    0    0    0    0    0    2   -4    0    0    0    0    0
+  204           0.56          -8.67    0    0    0    0    0    0    0    0    0    2   -5    0    0    1
+  205          -9.19           0.00    2    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  206           9.11           0.00    1    0   -2    1   -1    0    0    0    0    0    0    0    0    0
+  207           9.07           0.00    2   -2    0   -2    0    0    0    0    0    0    0    0    0    0
+  208           1.63           6.96    0    0    0    0    0    0    8  -13    0    0    0    0    0   -2
+  209          -8.47           0.00    0    2    0   -2    0    0    0    0    0    0    0    0    0    0
+  210          -8.28           0.04    1    1    0    0   -1    0    0    0    0    0    0    0    0    0
+  211           8.27           0.04    2    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  212          -8.04           0.00    0    0    0    0    0    0    5   -7    0    0    0    0    0   -2
+  213           7.91           0.00    0    1    0    2    1    0    0    0    0    0    0    0    0    0
+  214          -7.84          -0.04    1    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+  215          -7.64           0.08    2    0    2    2    1    0    0    0    0    0    0    0    0    0
+  216           5.21          -2.51    1    0    2    0    2    0    0    1    0    0    0    0    0    0
+  217          -5.77           1.87    0    0    0    0    0    0    3   -5    0    0    0    0    0    0
+  218           5.01          -2.51    1    0   -2    0   -2    0    0    4   -8    3    0    0    0    0
+  219          -7.48           0.00    0    0    2   -3    2    0    0    0    0    0    0    0    0    0
+  220          -7.32          -0.12    0    0    0    0    0    0    0    4    0   -2    0    0    0    2
+  221           7.40          -0.04    0    0    4    0    2    0    0    0    0    0    0    0    0    0
+  222           7.44           0.00    1    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+  223           6.32          -1.11    1    0    0   -1    1    0    0    0    0    0    0    0    0    0
+  224          -6.13          -1.19    0    0    0    0    0    0    0    2    0   -1    0    0    0    2
+  225           0.20          -6.88    0    0    0    0    0    0    2   -1    0    0    0    0    0    2
+  226           6.92           0.04    1    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  227           6.48          -0.48    0    0    0    0    0    0    0    2    0    1    0    0    0    2
+  228          -6.94           0.00    2    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+  229           2.47          -4.46    0    0    0    0    0    0    8  -11    0    0    0    0    0   -2
+  230          -2.23          -4.65    0    0    0    0    0    0    0    8  -16    4    5    0    0   -2
+  231          -1.07          -5.69    0    0    1   -1    1    0    0   -1    0    2    0    0    0    0
+  232           4.97          -1.71    0    0    0    0    0    0    0    8  -16    4    5    0    0    2
+  233           5.57           1.07    0    0    1   -1    1    0   -5    7    0    0    0    0    0    0
+  234          -6.48           0.08    1    0    2    4    2    0    0    0    0    0    0    0    0    0
+  235           2.03           4.53    0    0    0    0    0    0    0    0    0    0    2    0    0    1
+  236           4.10          -2.39    1    0    0   -2    0    0   19  -21    3    0    0    0    0    0
+  237           0.00          -6.44    0    0    1    0    1    0    0    0    0    0    0    0    0    0
+  238          -6.40           0.00    3    0    0   -4    0    0    0    0    0    0    0    0    0    0
+  239           6.32           0.00    1    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+  240           2.67          -3.62    0    0    0    0    0    0    0    3    0   -2    0    0    0    2
+  241          -1.91          -4.38    0    0    1   -1    1    0    0   -1    0    0    2    0    0    0
+  242          -2.43          -3.82    0    0    0    0    0    0    8  -15    0    0    0    0    0   -2
+  243           6.20           0.00    0    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+  244          -3.38          -2.78    0    0    0    0    0    0    0    1    2    0    0    0    0    2
+  245          -6.12           0.04    0    0    0    4    1    0    0    0    0    0    0    0    0    0
+  246          -6.09          -0.04    0    0    2   -4    1    0    0    0    0    0    0    0    0    0
+  247          -6.01          -0.04    1    1    0   -4    0    0    0    0    0    0    0    0    0    0
+  248           3.18          -2.82    0    0    0    0    0    0    0    3   -2    0    0    0    0    2
+  249          -5.05           0.84    0    0    0    0    0    0    0    0    0    3    0    0    0    2
+  250           5.85           0.00    3    0    2   -2    1    0    0    0    0    0    0    0    0    0
+  251           5.69          -0.12    0    0    0    0    0    0    0    4   -2    0    0    0    0    2
+  252           5.73          -0.04    1    1    2    2    2    0    0    0    0    0    0    0    0    0
+  253           5.61           0.00    0    0    4   -2    1    0    0    0    0    0    0    0    0    0
+  254           5.49           0.00    2    0    0   -2    0    0   -3    3    0    0    0    0    0    0
+  255          -5.33           0.04    3    0    2    2    2    0    0    0    0    0    0    0    0    0
+  256          -5.29           0.00    0    0    0    0    0    0    0    0    0    0    2    0    0    2
+  257           5.25           0.00    2    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+  258           0.99           4.22    0    0    0    0    0    0    4   -6    0    0    0    0    0   -1
+  259          -0.99           4.22    0    0    0    0    0    0    1   -1    0    0    0    0    0   -1
+  260           0.00           5.21    1    0    0   -1    0    0   -3    4    0    0    0    0    0    0
+  261           5.13           0.04    0    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  262          -4.90           0.00    2    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+  263          -3.10           1.79    0    0    0    0    0    0    0    3   -4    0    0    0    0    0
+  264          -4.81           0.04    0    0    2    4    1    0    0    0    0    0    0    0    0    0
+  265          -4.75           0.00    0    0    0    2   -2    0    0    0    0    0    0    0    0    0
+  266           4.70          -0.04    3    0    0    0   -1    0    0    0    0    0    0    0    0    0
+  267          -4.69           0.00    0    0    0    0    0    0    0    1    0   -3    0    0    0   -2
+  268          -4.65           0.00    0    0    0    0    0    0    6   -8    0    0    0    0    0   -2
+  269           4.65           0.00    0    0    2   -2    1    0   -3    3    0    0    0    0    0    0
+  270          -4.57           0.00    2    1    0   -4    0    0    0    0    0    0    0    0    0    0
+  271           4.49          -0.04    4    0    2   -2    2    0    0    0    0    0    0    0    0    0
+  272          -4.53           0.00    0    0    1   -1    1    0    0    3   -8    3    0    0    0    0
+  273           0.00          -4.53    0    0    0    0    0    0    3   -2    0    0    0    0    0    2
+  274           0.00          -4.53    0    0    0    0    0    0    2   -5    0    0    0    0    0   -2
+  275          -4.53           0.00    2    0    0    2    1    0    0    0    0    0    0    0    0    0
+  276           4.50           0.00    1   -1    0   -2   -1    0    0    0    0    0    0    0    0    0
+  277          -4.49           0.00    0    0    0    0    0    0    1   -3    0    0    0    0    0   -2
+  278           1.83           2.63    0    0    0    0    1    0    8  -13    0    0    0    0    0    0
+  279           4.38           0.00    2    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  280           0.88          -3.46    0    0    0    0    0    0    1   -2    0    0    0    0    0    0
+  281          -2.70           1.55    0    0    0    0    0    0    0    2   -3    0    0    0    0    0
+  282          -4.22           0.00    0    0    2   -1    1    0    0    0    0    0    0    0    0    0
+  283          -4.10          -0.12    1    1    0    2    0    0    0    0    0    0    0    0    0    0
+  284           3.54          -0.64    2    0    0   -2    1    0    0   -2    0    3    0    0    0    0
+  285          -3.50           0.68    0    0    0    0    0    0    5   -8    0    0    0    0    0   -1
+  286           4.18           0.00    1   -1   -2    2   -1    0    0    0    0    0    0    0    0    0
+  287           4.14           0.00    1    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  288           4.10           0.00    1    0    0   -3    0    0    0    0    0    0    0    0    0    0
+  289          -4.06           0.00    2    0    0   -4    1    0    0    0    0    0    0    0    0    0
+  290           2.70          -1.35    1    0    0    0   -1    0  -18   16    0    0    0    0    0    0
+  291          -4.04           0.00    2    0    0   -2    2    0    0    0    0    0    0    0    0    0
+  292          -3.98          -0.04    1    0    0   -4    1    0    0    0    0    0    0    0    0    0
+  293          -3.98           0.04    1   -1    2    2    1    0    0    0    0    0    0    0    0    0
+  294           4.02           0.00    2    0    2   -4    1    0    0    0    0    0    0    0    0    0
+  295           3.94           0.00    0    0    1   -1    1    0    0   -5    8   -3    0    0    0    0
+  296           0.84          -3.10    0    0    1   -1    0    0    0   -1    0   -1    0    0    0    0
+  297           3.30           0.60    0    0    0    0    0    0    0    2    0   -3    0    0    0    0
+  298          -1.59           2.27    0    0    0    0    1    0   -8   13    0    0    0    0    0    0
+  299          -3.66          -0.20    2   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+  300          -3.10          -0.72    2    0    0   -2    0    0   -6    8    0    0    0    0    0    0
+  301          -3.82           0.00    1   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+  302          -3.62          -0.16    2    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+  303          -3.74           0.00    0    1   -2    1   -2    0    0    0    0    0    0    0    0    0
+  304           3.74           0.00    4    0    0    0    0    0    0    0    0    0    0    0    0    0
+  305          -3.74           0.00    0    2   -2    2    0    0    0    0    0    0    0    0    0    0
+  306          -3.71           0.00    0    2    0    0    1    0    0    0    0    0    0    0    0    0
+  307           3.02           0.68    0    0    0    0    1    0    0    0    0   -2    5    0    0    0
+  308           3.70           0.00    1    0   -4    2   -2    0    0    0    0    0    0    0    0    0
+  309           3.30           0.40    0    2   -2    0   -2    0    0    0    0    0    0    0    0    0
+  310          -3.66           0.04    2   -1    2    2    2    0    0    0    0    0    0    0    0    0
+  311           3.66           0.04    0    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  312          -3.62           0.00    1    0   -2   -3   -2    0    0    0    0    0    0    0    0    0
+  313          -3.61           0.00    2    0    0    0    2    0    0    0    0    0    0    0    0    0
+  314          -2.90           0.68    0    0    0    0    1    0    0    0    0    2   -5    0    0    0
+  315           0.80          -2.78    0    0    0    0    1    0    0    0    0    1    0    0    0    0
+  316           3.54           0.00    0    0    0    0    0    0    3   -3    0    0    0    0    0    0
+  317          -3.54           0.00    0    0    0    0    0    0    0    2    0    0    0    0    0    2
+  318          -3.50           0.00    2    0    2   -2    0    0    0    0    0    0    0    0    0    0
+  319           3.45           0.00    0    2    2   -2    1    0    0    0    0    0    0    0    0    0
+  320           0.00          -3.42    0    0    0    0    0    0    0    6  -16    4    5    0    0   -2
+  321           3.38           0.00    1   -2    0    0    0    0    0    0    0    0    0    0    0    0
+  322           2.27          -1.11    1    0    0    0    1    0  -18   16    0    0    0    0    0    0
+  323          -3.34           0.00    1   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  324           3.34           0.00    0    1    2    2    1    0    0    0    0    0    0    0    0    0
+  325          -3.30           0.01    0    0    2    0    3    0    0    0    0    0    0    0    0    0
+  326           3.31           0.00    1    0    2    0   -1    0    0    0    0    0    0    0    0    0
+  327           3.30           0.00    3    0    0    0    1    0    0    0    0    0    0    0    0    0
+  328          -3.30           0.00    1    0   -2   -1   -2    0    0    0    0    0    0    0    0    0
+  329          -1.39          -1.91    0    0    0    0    0    0    0    1    0    2    0    0    0    2
+  330           3.30           0.00    0    0    0    0    0    0    4   -4    0    0    0    0    0    0
+  331           3.26           0.00    2    1    0   -2    1    0    0    0    0    0    0    0    0    0
+  332           3.26           0.00    1    0    0    4    0    0    0    0    0    0    0    0    0    0
+  333           3.22          -0.04    1    0    2    2    0    0    0    0    0    0    0    0    0    0
+  334          -3.26           0.00    2   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+  335           2.51          -0.64    0    0    0    0    0    0    3   -7    0    0    0    0    0   -2
+  336           3.14           0.00    2    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+  337          -2.63          -0.48    0    0    1   -1    1    0    0   -1    0    0    1    0    0    0
+  338           3.10           0.00    3    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+  339          -3.06           0.00    2   -1    2    0    1    0    0    0    0    0    0    0    0    0
+  340           2.94          -0.12    1   -2    0   -2    0    0    0    0    0    0    0    0    0    0
+  341           3.06           0.00    2    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+  342           0.00           2.98    0    0    1    0    0    0    0    0    0    0    0    0    0    0
+  343           2.98           0.00    1    0    2   -4    0    0    0    0    0    0    0    0    0    0
+  344           2.07           0.91    0    0    0    0    0    0    0    0    0    1    0    0    0    2
+  345          -2.98           0.00    0    0    0    0    0    0    0    1    0    0   -1    0    0    0
+  346           2.94           0.00    0    2    2    0    2    0    0    0    0    0    0    0    0    0
+  347          -2.94           0.00    0    0    0    0    0    0    7   -9    0    0    0    0    0   -2
+  348          -2.94           0.00    0    0    0    0    0    0    0    0    2    0    0    0    0    2
+  349          -2.90           0.00    1   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+  350          -0.56          -2.35    0    0    0    0    0    0    2   -4    0    0    0    0    0   -1
+  351          -1.47           1.39    0    0    0    0    1    0    0    1   -2    0    0    0    0    0
+  352           2.80           0.00    1    0    2   -4    2    0    0    0    0    0    0    0    0    0
+  353          -2.74           0.00    2    0    2   -1    2    0    0    0    0    0    0    0    0    0
+  354          -0.12           2.63    0    0    0    0    0    0    4   -7    0    0    0    0    0   -2
+  355           2.15          -0.60    0    0    0    0    0    0    3   -3    0    0    0    0    0    2
+  356          -2.70           0.00    1    1   -2    1   -1    0    0    0    0    0    0    0    0    0
+  357           1.79          -0.88    0    0    0    0    0    0    0    8  -15    0    0    0    0    0
+  358          -0.48           2.19    0    0    0    0    0    0    2   -2    0    0    0    0    0   -1
+  359           0.44           2.23    0    0    0    0    0    0    0    0    0    0    1    0    0    0
+  360           0.52           2.07    0    0    0    0    0    0    0    0    0    1    0    0    0   -1
+  361          -2.59           0.00    0    1    0   -4    0    0    0    0    0    0    0    0    0    0
+  362           2.55           0.00    2    1    2    0    1    0    0    0    0    0    0    0    0    0
+  363          -1.11           1.43    0    0    0    0    0    0    0    1    0   -3    0    0    0    0
+  364          -2.51           0.00    3   -1    2    0    2    0    0    0    0    0    0    0    0    0
+  365          -2.51           0.00    2    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  366           2.51           0.00    1    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+  367           0.00          -2.50    1    0   -1    0   -3    0    0    0    0    0    0    0    0    0
+  368           2.47           0.00    1   -1    0   -2    1    0    0    0    0    0    0    0    0    0
+  369           2.11          -0.36    2    0    0   -2   -1    0    0   -2    0    3    0    0    0    0
+  370           1.67           0.80    0    0    1   -1    0    0    0   -1    0    0   -1    0    0    0
+  371           2.46           0.00    0    2    0    0   -1    0    0    0    0    0    0    0    0    0
+  372          -2.43           0.00    1    0   -2    1    0    0    0    0    0    0    0    0    0    0
+  373          -2.39           0.00    1    0    2   -3    2    0    0    0    0    0    0    0    0    0
+  374          -1.83           0.56    0    0    0    0    0    0    4   -6    0    0    0    0    0    0
+  375          -0.44          -1.95    0    0    0    0    0    0    3   -5    0    0    0    0    0   -1
+  376           0.24           2.15    0    0    0    0    0    0    0    0    0    1    0    0    0    1
+  377           2.39           0.00    2    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+  378           2.35           0.00    0    0    0    0    0    0    5   -5    0    0    0    0    0    0
+  379           2.27           0.00    1    0    2    1    1    0    0    0    0    0    0    0    0    0
+  380          -2.22           0.00    2    0    0    0   -2    0    0    0    0    0    0    0    0    0
+  381          -1.03          -1.15    1    0    0   -1    1    0    0   -1    0    2    0    0    0    0
+  382           1.87           0.32    0    0    0    0    0    0    0    0    0    0    1    0    0    1
+  383          -0.32          -1.87    0    0    1   -1    1    0    0   -2    2    0    0    0    0    0
+  384           2.15           0.00    2    0    0   -2    1    0    0   -2    0    2    0    0    0    0
+  385          -0.80           1.35    0    0    0    0    0    0    0    3   -5    0    0    0    0    0
+  386           2.11           0.00    3    1    2    0    2    0    0    0    0    0    0    0    0    0
+  387          -2.11           0.00    1    1    2    1    2    0    0    0    0    0    0    0    0    0
+  388          -0.56          -1.55    0    0    0    0    0    0    5   -8    0    0    0    0    0    0
+  389           2.11           0.00    1    0    0   -1    0    0    0   -1    0    1    0    0    0    0
+  390          -0.84          -1.27    0    0    0    0    0    0    0    0    4    0    0    0    0    2
+  391          -1.99           0.12    0    0    0    0    0    0    8  -10    0    0    0    0    0   -2
+  392          -0.24           1.87    0    0    0    0    0    0    0    1   -2    0    0    0    0   -1
+  393          -0.24          -1.87    0    0    1   -1    1    0    0   -1    0    1    0    0    0    0
+  394          -2.03           0.00    1   -2    2    0    2    0    0    0    0    0    0    0    0    0
+  395           2.03           0.00    2   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+  396           2.03           0.00    2    0    0   -3    0    0    0    0    0    0    0    0    0    0
+  397           2.03           0.00    0    0    0    0    0    0    0    2   -4    0    0    0    0   -2
+  398          -0.40           1.59    0    0    1   -1    1    0   -3    4    0    0    0    0    0    0
+  399           1.99           0.00    0    0    2   -2    1    0    0   -2    0    2    0    0    0    0
+  400           1.95           0.00    0    1    0    1    1    0    0    0    0    0    0    0    0    0
+  401           1.95           0.00    3    0    0    2    0    0    0    0    0    0    0    0    0    0
+  402           1.91           0.00    0    1    0    1   -1    0    0    0    0    0    0    0    0    0
+  403           1.19          -0.72    0    0    0    0    0    0    0    5   -4    0    0    0    0    2
+  404           1.87           0.00    2    0    2    1    2    0    0    0    0    0    0    0    0    0
+  405           1.87           0.00    1    1    0    1    0    0    0    0    0    0    0    0    0    0
+  406          -1.27           0.60    0    0    0    0    0    0    0    0    0    0    1    0    0   -1
+  407           0.72          -1.15    0    0    0    0    0    0    0    4   -4    0    0    0    0    2
+  408          -0.99           0.88    0    0    0    0    0    0    0    0    0    2    0    0    0    0
+  409           1.87           0.00    2   -1    0    2    0    0    0    0    0    0    0    0    0    0
+  410          -1.87           0.00    1    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+  411          -1.83           0.00    2    0   -4   -2   -2    0    0    0    0    0    0    0    0    0
+  412          -1.79           0.00    0    1    0    2   -1    0    0    0    0    0    0    0    0    0
+  413          -1.79           0.00    4    0    2    0    1    0    0    0    0    0    0    0    0    0
+  414           1.79           0.00    1    2    2   -2    2    0    0    0    0    0    0    0    0    0
+  415           0.00          -1.79    0    0    0    0    0    0    6   -9    0    0    0    0    0   -2
+  416          -1.79           0.00    1    1    2   -4    1    0    0    0    0    0    0    0    0    0
+  417          -1.75           0.00    0    0    4   -4    2    0    0    0    0    0    0    0    0    0
+  418          -1.75           0.00    3    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  419           1.75           0.00    2    1   -2    0    0    0    0    0    0    0    0    0    0    0
+  420          -1.47          -0.28    0    0    0    0    0    0    0    4    0   -3    0    0    0    2
+  421          -1.71           0.00    1    0    2   -3    1    0    0    0    0    0    0    0    0    0
+  422           1.71           0.00    2    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+  423           0.32           1.39    0    0    0    0    0    0    5   -7    0    0    0    0    0   -1
+  424           0.28          -1.43    0    0    0    0    0    0    0    2    0    0    0    0    0    1
+  425          -0.52          -1.19    0    0    0    0    0    0    0    1    0    2   -5    0    0    0
+  426           1.67           0.00    2    0    0    2   -1    0    0    0    0    0    0    0    0    0
+  427          -1.67           0.00    0    1    2    1    1    0    0    0    0    0    0    0    0    0
+  428           0.76          -0.91    0    0    0    0    0    0    2    1    0    0    0    0    0    2
+  429          -0.32           1.35    0    0    0    0    0    0    3   -3    0    0    0    0    0   -1
+  430          -1.39          -0.28    0    0    1   -1    0    0   -5    7    0    0    0    0    0    0
+  431           1.63           0.00    2    1    2   -2    1    0    0    0    0    0    0    0    0    0
+  432          -1.59           0.00    1    0    2   -1    1    0    0    0    0    0    0    0    0    0
+  433           1.03          -0.56    0    0    0    0    0    0    0    4   -3    0    0    0    0    2
+  434           1.59           0.00    0    0    0    0    0    0    6   -6    0    0    0    0    0    0
+  435           1.55           0.00    1    0    4   -2    1    0    0    0    0    0    0    0    0    0
+  436          -0.28          -1.27    0    0    0    0    0    0    1   -1    0    0    0    0    0    1
+  437          -0.64           0.91    0    0    0    0    0    0    0    4   -6    0    0    0    0    0
+  438          -0.32          -1.23    0    0    0    0    0    0    1   -3    0    0    0    0    0   -1
+  439          -1.55           0.00    1   -1    2   -2    1    0    0    0    0    0    0    0    0    0
+  440          -1.51           0.00    1    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+  441           1.51           0.00    2   -2    0   -2   -1    0    0    0    0    0    0    0    0    0
+  442          -1.51           0.00    2   -1    0   -2    1    0    0    0    0    0    0    0    0    0
+  443           1.51           0.00    2    0    0   -1    0    0    0    0    0    0    0    0    0    0
+  444           1.47           0.00    2    0    4   -2    2    0    0    0    0    0    0    0    0    0
+  445           1.47           0.00    0    0    2    3    2    0    0    0    0    0    0    0    0    0
+  446           0.95          -0.52    0    0    0    0    0    0    0    3   -6    0    0    0    0   -2
+  447           1.23          -0.24    0    0    0    0    0    0    3   -1    0    0    0    0    0    2
+  448           0.60           0.88    2    0    0   -2    0    0    0   -6    8    0    0    0    0    0
+  449          -1.47           0.00    1   -1    0    2    1    0    0    0    0    0    0    0    0    0
+  450          -1.43           0.00    1    0   -2    2    1    0    0    0    0    0    0    0    0    0
+  451           1.43           0.00    1   -1    0    2   -1    0    0    0    0    0    0    0    0    0
+  452           1.43           0.00    0    1    4   -2    2    0    0    0    0    0    0    0    0    0
+  453          -0.68          -0.76    0    0    1   -1    1    0    0   -1    0    0    0    0    2    0
+  454           0.95          -0.48    2    0    2    0    2    0    0    2    0   -3    0    0    0    0
+  455          -0.95          -0.48    0    0    2    0    2    0    0    1    0    0    0    0    0    0
+  456          -1.19          -0.24    0    0    0    0    0    0    0    1    0   -4    0    0    0   -2
+  457           0.36          -1.07    0    0    1   -1    1    0    0   -1    0   -4   10    0    0    0
+  458           0.95           0.48    2    0    0   -2    0    0    0   -5    6    0    0    0    0    0
+  459           1.43           0.00    1    1    0   -1    0    0    0    0    0    0    0    0    0    0
+  460           1.39           0.00    0    0    0    4   -1    0    0    0    0    0    0    0    0    0
+  461           1.39           0.00    2   -1    0    0    1    0    0    0    0    0    0    0    0    0
+  462          -1.39           0.00    1   -2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  463          -1.39           0.00    1    2    0    0    0    0    0    0    0    0    0    0    0    0
+  464           0.00           1.39    0    0    0    0    0    0    0    2    0   -1    0    0    0    0
+  465          -0.12          -1.27    0    0    0    0    0    0    0    4    0   -1    0    0    0    2
+  466           0.56           0.84    0    0    0    0    0    0    0    2   -4    0    0    0    0   -1
+  467          -0.44          -0.95    0    0    0    0    0    0    0    0    0    2   -5    0    0   -2
+  468           0.32          -1.07    0    0    0    0    0    0    0    1    0    0    0    0    0    0
+  469           1.03          -0.36    2    0   -1   -1    0    0    0    3   -7    0    0    0    0    0
+  470          -0.28           1.11    0    0    1   -1    1    0   -4    5    0    0    0    0    0    0
+  471           0.44           0.95    0    0    1   -1    2    0    0   -1    0    0    2    0    0    0
+  472          -1.35           0.00    1   -1   -2    2    0    0    0    0    0    0    0    0    0    0
+  473           0.88           0.48    0    0    0    0    0    0    0    1   -8    3    0    0    0   -2
+  474          -1.35           0.00    0    0    0    0    0    0    9  -11    0    0    0    0    0   -2
+  475           1.35           0.00    1    0    0   -2    0    0    0   -2    0    2    0    0    0    0
+  476           1.35           0.00    1    0    0   -1   -2    0    0    0    0    0    0    0    0    0
+  477          -1.31           0.00    0    1   -2    2    1    0    0    0    0    0    0    0    0    0
+  478           1.31           0.00    1    0   -2    1   -2    0    0    0    0    0    0    0    0    0
+  479          -1.19          -0.12    0    0    0    0    0    0    2   -3    0    0    0    0    0   -1
+  480           1.27           0.00    0    0    4    0    1    0    0    0    0    0    0    0    0    0
+  481           0.40          -0.88    0    0    2   -2    1    0    0   -9   13    0    0    0    0    0
+  482           1.27           0.00    1    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+  483           1.27           0.00    3    1    2   -2    2    0    0    0    0    0    0    0    0    0
+  484          -0.16          -1.11    0    0    0    0    0    0    1    0    0    0    0    0    0    0
+  485          -0.84           0.44    2    0    0    0    0    0    0   -2    0    3    0    0    0    0
+  486           0.84          -0.44    1    0    2    0    1    0    0   -2    0    3    0    0    0    0
+  487           0.84          -0.44    1    0   -2    0   -1    0    0   -1    0    0    0    0    0    0
+  488          -1.27           0.00    1    0   -4   -2   -2    0    0    0    0    0    0    0    0    0
+  489          -1.27           0.00    1    0    0    4    1    0    0    0    0    0    0    0    0    0
+  490           1.27           0.00    1    1    0    2    1    0    0    0    0    0    0    0    0    0
+  491          -0.44          -0.84    0    0    0    0    1    0    0   -2    4    0    0    0    0    0
+  492           0.00          -1.27    0    0    0    0    1    0    2   -3    0    0    0    0    0    0
+  493          -1.27           0.00    2    0    2   -4    2    0    0    0    0    0    0    0    0    0
+  494          -1.23           0.00    1   -1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  495          -1.23           0.00    1   -2    2    2    2    0    0    0    0    0    0    0    0    0
+  496           1.23           0.00    0    0    2   -1    0    0    0    0    0    0    0    0    0    0
+  497           0.00           1.23    0    0    0    0    0    0    0    3   -6    0    0    0    0    0
+  498          -0.12           1.11    0    0    1    1    1    0    0    1    0    0    0    0    0    0
+  499           1.22           0.00    0    0    2   -4    2    0    0    0    0    0    0    0    0    0
+  500           1.19           0.00    1    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  501          -0.24           0.95    0    0    0    0    0    0    4   -4    0    0    0    0    0   -1
+  502          -0.76          -0.44    0    0    0    0    0    0    0    3   -8    3    0    0    0    0
+  503           0.91           0.28    0    0    0    0    0    0    0    1   -2    0    0    0    0    1
+  504           1.19           0.00    1    1   -2    1    0    0    0    0    0    0    0    0    0    0
+  505           1.19           0.00    3    0    2    0    0    0    0    0    0    0    0    0    0    0
+  506           0.00           1.19    0    0    1    0    2    0    0    0    0    0    0    0    0    0
+  507           1.15           0.00    2   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+  508           0.00           1.15    1    0    0   -1    1    0   -3    4    0    0    0    0    0    0
+  509          -1.15           0.00    1    0    2    4    1    0    0    0    0    0    0    0    0    0
+  510           1.15           0.00    2   -1   -2    0    0    0    0    0    0    0    0    0    0    0
+  511          -1.15           0.00    2    0   -2    2    0    0    0    0    0    0    0    0    0    0
+  512           1.15           0.00    3   -1    0    0    0    0    0    0    0    0    0    0    0    0
+  513           1.15           0.00    0    0    0    0    0    0    0    3    0    0   -1    0    0    2
+  514          -0.95           0.20    0    0    0    0    0    0    6  -10    0    0    0    0    0   -2
+  515           0.24           0.91    0    0    0    0    0    0    0    0    0    3    0    0    0    1
+  516          -1.15           0.00    1    1    2   -2    0    0    0    0    0    0    0    0    0    0
+  517          -1.12           0.00    0    1    2   -2   -1    0    0    0    0    0    0    0    0    0
+  518          -1.11           0.00    1    0    0    1   -1    0    0    0    0    0    0    0    0    0
+  519          -1.11           0.00    1    2    0   -2   -1    0    0    0    0    0    0    0    0    0
+  520           0.16           0.95    0    0    0    0    1    0    0    0    0    0    1    0    0    0
+  521          -1.11           0.00    2    0    2   -4    0    0    0    0    0    0    0    0    0    0
+  522           1.11           0.00    0    0    0    0    0    0    7   -7    0    0    0    0    0    0
+  523           0.20          -0.91    0    0    0    0    0    0    1    1    0    0    0    0    0    1
+  524          -0.72          -0.40    0    0    0    0    0    0    0    0    3    0    0    0    0    2
+  525          -1.11           0.00    1    1   -2   -2    0    0    0    0    0    0    0    0    0    0
+  526          -1.11           0.00    0    0    0    0    1    0    1   -1    0    0    0    0    0    0
+  527           1.07           0.00    2   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  528          -1.07           0.00    2    0    2    4    2    0    0    0    0    0    0    0    0    0
+  529           0.76          -0.32    0    0    0    0    0    0    0    1   -4    0    0    0    0   -2
+  530           0.00          -1.07    0    0    0    0    0    0    3   -6    0    0    0    0    0   -2
+  531           1.07           0.00    0    0    0    0    0    0    0    3   -3    0    0    0    0    0
+  532           1.07           0.00    0    0    2   -2    1    0   -4    4    0    0    0    0    0    0
+  533          -1.07           0.00    0    1   -2   -2    0    0    0    0    0    0    0    0    0    0
+  534           1.07           0.00    0    0    2   -3    1    0    0    0    0    0    0    0    0    0
+  535          -0.84          -0.24    0    0    0    0    1    0   -3    5    0    0    0    0    0    0
+  536           0.00          -1.03    0    0    3    0    3    0    0    0    0    0    0    0    0    0
+  537           1.03           0.00    2    1    2    2    2    0    0    0    0    0    0    0    0    0
+  538          -1.03           0.00    3    0    2   -4    2    0    0    0    0    0    0    0    0    0
+  539          -0.24           0.80    0    0    0    0    0    0    0    1    0    0   -2    0    0    0
+  540           0.20           0.84    0    0    1   -1    1    0   -1    0    0    0    0    0    0    0
+  541          -1.03           0.00    3    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  542          -1.03           0.00    0    0    0    0    3    0    0    0    0    0    0    0    0    0
+  543          -0.99           0.00    2    0    0    1    0    0    0    0    0    0    0    0    0    0
+  544           0.24           0.76    0    0    0    0    0    0    2   -1    0    0    0    0    0    0
+  545          -0.99           0.00    0    0    0    0    0    0    0    7   -8    3    0    0    0    2
+  546          -0.16           0.84    0    0    0    0    0    0    0    2    0   -2    0    0    0    1
+  547          -0.99           0.00    0    0    0    0    0    0    0    1   -1    0    0    0    0    0
+  548          -0.64           0.36    0    0    0    0    0    0    0    4   -7    0    0    0    0   -2
+  549           0.99           0.00    1   -2    0    2    0    0    0    0    0    0    0    0    0    0
+  550           0.36          -0.64    0    0    1   -1    0    0    0   -1    0   -2    5    0    0    0
+  551          -0.95           0.00    3    0    2    2    1    0    0    0    0    0    0    0    0    0
+  552          -0.95           0.00    1   -1    2    4    2    0    0    0    0    0    0    0    0    0
+  553           0.00           0.95    0    0    1   -1    0    0    0   -1    0   -1    1    0    0    0
+  554           0.64           0.32    1    0    0   -1    0    0    0   -3    4    0    0    0    0    0
+  555           0.00          -0.95    0    0    0    0    0    0    7  -10    0    0    0    0    0   -2
+  556           0.84           0.12    0    0    0    0    0    0    0    5   -8    3    0    0    0    0
+  557           0.20           0.76    0    0    0    0    0    0    6   -8    0    0    0    0    0   -1
+  558          -0.95           0.00    3    0   -2    0    0    0    0    0    0    0    0    0    0    0
+  559           0.95           0.00    1    0    4    0    2    0    0    0    0    0    0    0    0    0
+  560          -0.95           0.00    1   -1   -2    2   -2    0    0    0    0    0    0    0    0    0
+  561           0.00           0.92    1    0    0   -1   -1    0   -3    4    0    0    0    0    0    0
+  562           0.91           0.00    1    1    2    2    1    0    0    0    0    0    0    0    0    0
+  563           0.91           0.00    3    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+  564           0.40           0.52    1   -2    2   -2    1    0    0    0    0    0    0    0    0    0
+  565          -0.91           0.00    5    0    2    0    2    0    0    0    0    0    0    0    0    0
+  566          -0.56           0.36    0    0    0    0    0    0    3   -9    4    0    0    0    0   -2
+  567           0.44          -0.48    0    0    1   -1    1    0    8  -14    0    0    0    0    0    0
+  568          -0.91           0.00    2    0    0   -6    0    0    0    0    0    0    0    0    0    0
+  569          -0.91           0.00    3    1    0    0    0    0    0    0    0    0    0    0    0    0
+  570          -0.36          -0.56    0    0    1   -1    1    0    3   -6    0    0    0    0    0    0
+  571           0.91           0.00    1    0    0    2    2    0    0    0    0    0    0    0    0    0
+  572          -0.88           0.00    1    2    0   -2    1    0    0    0    0    0    0    0    0    0
+  573          -0.88           0.00    2    1    0    0   -1    0    0    0    0    0    0    0    0    0
+  574           0.60          -0.28    0    0    0    0    1    0    0    8  -15    0    0    0    0    0
+  575           0.88           0.00    0    2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  576           0.36          -0.52    0    0    1   -1    1    0    0   -3    4    0    0    0    0    0
+  577          -0.52           0.36    0    0    0    0    0    0    3   -5    4    0    0    0    0    2
+  578           0.52           0.36    0    0    0    0    0    0    0    6    0    0    0    0    0    2
+  579           0.00           0.88    0    0    0    0    0    0    0    4   -7    0    0    0    0    0
+  580           0.56           0.32    0    0    0    0    0    0    0    8  -15    0    0    0    0   -2
+  581           0.64          -0.24    0    0    0    0    0    0    0    4   -5    0    0    0    0    0
+  582           0.88           0.00    1   -1    0   -4    0    0    0    0    0    0    0    0    0    0
+  583           0.88           0.00    1   -1    2    0    0    0    0    0    0    0    0    0    0    0
+  584           0.88           0.00    1   -1    2   -1    2    0    0    0    0    0    0    0    0    0
+  585           0.84           0.00    0    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+  586          -0.68          -0.16    2    0    0   -2    1    0   -6    8    0    0    0    0    0    0
+  587           0.84           0.00    1    2    2    0    2    0    0    0    0    0    0    0    0    0
+  588           0.56           0.28    0    0    1   -1    0    0   -8   12    0    0    0    0    0    0
+  589          -0.16           0.68    0    0    0    0    0    0    5   -5    0    0    0    0    0   -1
+  590           0.64          -0.20    0    0    0    0    0    0    5   -7    0    0    0    0    0    0
+  591           0.16           0.68    0    0    0    0    0    0    1    2    0    0    0    0    0    2
+  592           0.72          -0.12    0    0    0    0    0    0    0    3    0   -3    0    0    0    0
+  593          -0.83           0.00    2    0   -2    0    2    0    0    0    0    0    0    0    0    0
+  594          -0.80           0.00    2    1    0    0    1    0    0    0    0    0    0    0    0    0
+  595           0.80           0.00    2    2    0   -2    0    0    0    0    0    0    0    0    0    0
+  596          -0.80           0.00    4    0    0   -2    0    0    0    0    0    0    0    0    0    0
+  597           0.28           0.52    0    0    0    0    0    0    0    5   -9    0    0    0    0    0
+  598           0.68          -0.12    0    0    0    0    0    0    3    0    0    0    0    0    0    2
+  599           0.00          -0.80    0    0    0    0    0    0    0    2    0    0   -1    0    0    2
+  600          -0.32           0.48    0    0    0    0    0    0    0    5   -9    0    0    0    0   -2
+  601           0.36          -0.44    0    0    0    0    0    0    0    3    0   -3    0    0    0    2
+  602          -0.36          -0.44    0    0    0    0    0    0    0    0    0    0    0    0    2    1
+  603          -0.80           0.00    0    1    2    0    0    0    0    0    0    0    0    0    0    0
+  604           0.79           0.00    1    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+  605           0.74          -0.04    0    0    2   -2    3    0    0    0    0    0    0    0    0    0
+  606          -0.76           0.00    0    0    0    0    1    0   -1    1    0    0    0    0    0    0
+  607           0.00           0.76    0    0    0    0    1    0   -2    3    0    0    0    0    0    0
+  608           0.16           0.60    0    0    0    0    1    0    0    0    0   -1    0    0    0    0
+  609          -0.76           0.00    1    0    0    1    1    0    0    0    0    0    0    0    0    0
+  610          -0.76           0.00    3    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+  611           0.76           0.00    2    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  612          -0.76           0.00    2   -1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  613           0.76           0.00    1    0   -4    2   -1    0    0    0    0    0    0    0    0    0
+  614           0.12           0.64    0    0    1   -1    0    0    0   -1    0    2    0    0    0    0
+  615           0.76           0.00    1    0    0   -2    0    0   -3    3    0    0    0    0    0    0
+  616           0.00           0.76    0    0    1   -1    1    0    0   -9   15    0    0    0    0    0
+  617           0.76           0.00    0    0    0    0    0    0    8   -8    0    0    0    0    0    0
+  618           0.64          -0.12    0    0    0    0    0    0    7  -11    0    0    0    0    0   -2
+  619           0.16          -0.60    0    0    0    0    0    0    2    0    0    0    0    0    0    1
+  620           0.76           0.00    0    0    0    0    0    0    0    3   -1    0    0    0    0    2
+  621           0.00          -0.76    0    0    0    0    0    0    0    2    0    0    1    0    0    2
+  622           0.28          -0.48    0    0    0    0    0    0    0    5   -5    0    0    0    0    2
+  623           0.32           0.44    0    0    0    0    0    0    0    1    0    1    0    0    0    0
+  624          -0.76           0.00    1    1    2    0    0    0    0    0    0    0    0    0    0    0
+  625           0.72           0.00    4    0    2   -2    1    0    0    0    0    0    0    0    0    0
+  626           0.72           0.00    1   -1   -2    0    0    0    0    0    0    0    0    0    0    0
+  627           0.48          -0.24    0    0    0    0    0    0    0    2    0   -2    5    0    0    2
+  628          -0.72           0.00    0    1    2   -4    1    0    0    0    0    0    0    0    0    0
+  629           0.72           0.00    0    0    1   -1    1    0   -2    2    0    0    0    0    0    0
+  630          -0.72           0.00    0    0    2    1    0    0    0    0    0    0    0    0    0    0
+  631          -0.72           0.00    3    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+  632          -0.71           0.00    1   -1    0    0    2    0    0    0    0    0    0    0    0    0
+  633          -0.68           0.00    1    0   -2   -2    1    0    0    0    0    0    0    0    0    0
+  634          -0.68           0.00    0    2    0   -2    1    0    0    0    0    0    0    0    0    0
+  635           0.68           0.00    0    1   -4    2   -2    0    0    0    0    0    0    0    0    0
+  636           0.68           0.00    0    1    2    4    2    0    0    0    0    0    0    0    0    0
+  637           0.68           0.00    1    0   -2    4   -1    0    0    0    0    0    0    0    0    0
+  638          -0.68           0.00    0    2    2   -2    0    0    0    0    0    0    0    0    0    0
+  639           0.56          -0.12    0    0    0    0    0    0    5   -3    0    0    0    0    0    2
+  640          -0.68           0.00    0    0    0    0    0    0    0    3   -5    0    0    0    0   -2
+  641          -0.68           0.00    0    0    0    0    0    0    0    2    0   -4    0    0    0   -2
+  642           0.20           0.48    0    0    0    0    0    0    0    3    0    2   -5    0    0    2
+  643          -0.44          -0.24    0    0    0    0    0    0    0    2    0    2   -5    0    0    2
+  644          -0.68           0.00    1    1   -2    0    0    0    0    0    0    0    0    0    0    0
+  645           0.64           0.00    2    0    0   -2    1    0   -3    3    0    0    0    0    0    0
+  646           0.64           0.00    3    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+  647          -0.64           0.00    2   -1    2    2    1    0    0    0    0    0    0    0    0    0
+  648           0.64           0.00    0    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  649          -0.64           0.00    1    0   -2   -3   -1    0    0    0    0    0    0    0    0    0
+  650          -0.12           0.52    0    0    0    0    0    0    6   -6    0    0    0    0    0   -1
+  651          -0.12          -0.52    0    0    0    0    0    0    2   -2    0    0    0    0    0    1
+  652          -0.16          -0.48    0    0    0    0    0    0    0    3    0   -2    0    0    0    0
+  653          -0.20          -0.44    0    0    0    0    0    0    2   -3    0    0    0    0    0   -2
+  654          -0.44           0.20    0    0    0    0    0    0    0   11    0    0    0    0    0    2
+  655          -0.44           0.20    0    0    0    0    0    0    0    6  -15    0    0    0    0   -2
+  656           0.24          -0.40    0    0    0    0    0    0    0    4   -8    0    0    0    0   -2
+  657          -0.20          -0.44    0    0    0    0    0    0    0    3    0    1    0    0    0    2
+  658          -0.64           0.00    1    0    0   -6    0    0    0    0    0    0    0    0    0    0
+  659           0.40          -0.24    0    0    0    0    1    0    3   -7    4    0    0    0    0    0
+  660          -0.64           0.00    1    0   -2   -4    0    0    0    0    0    0    0    0    0    0
+  661           0.64           0.00    0    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+  662          -0.63           0.00    2    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+  663          -0.60           0.00    0    1   -2    1   -1    0    0    0    0    0    0    0    0    0
+  664           0.00           0.60    0    0    2   -2    2    0   -8   11    0    0    0    0    0    0
+  665          -0.60           0.00    2    0   -2   -3   -2    0    0    0    0    0    0    0    0    0
+  666          -0.60           0.00    4    0    2    2    2    0    0    0    0    0    0    0    0    0
+  667           0.60           0.00    0    0    0    0    0    0    0    2    0    0   -2    0    0    0
+  668           0.00           0.60    0    0    0    0    0    0    0    0    0    0    3    0    0    2
+  669           0.24          -0.36    2    0    0   -2    0    0    0   -2    0    4   -3    0    0    0
+  670           0.12           0.48    0    0    0    0    0    0    7   -9    0    0    0    0    0   -1
+  671           0.48          -0.12    0    0    0    0    0    0    4   -7    0    0    0    0    0   -1
+  672           0.12           0.48    0    0    0    0    0    0    3   -3    0    0    0    0    0    1
+  673           0.24          -0.36    0    0    0    0    0    0    0    6   -6    0    0    0    0    2
+  674           0.36           0.24    0    0    0    0    0    0    0    6  -11    0    0    0    0    0
+  675           0.12           0.48    0    0    0    0    0    0    0    5    0   -2    0    0    0    2
+  676           0.44           0.16    0    0    0    0    0    0    0    2    0   -4    0    0    0    0
+  677          -0.60           0.00    0    0    0    3    0    0    0    0    0    0    0    0    0    0
+  678          -0.60           0.00    2    0    0   -2    0    0    0   -2    0    3   -1    0    0    0
+  679           0.60           0.00    2   -1    0   -4    0    0    0    0    0    0    0    0    0    0
+  680           0.00           0.60    0    0    1   -1    2    0    0   -2    2    0    0    0    0    0
+  681           0.59           0.00    0    0    0    0    1    0    0    1    0   -1    0    0    0    0
+  682          -0.56           0.00    3    0    0   -4    1    0    0    0    0    0    0    0    0    0
+  683          -0.44          -0.12    2    0    0   -2   -1    0   -6    8    0    0    0    0    0    0
+  684           0.56           0.00    1    2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  685           0.00           0.56    0    0    1   -1    1    0    0   -1    0   -1    1    0    0    0
+  686          -0.56           0.00    3    1    0   -4    0    0    0    0    0    0    0    0    0    0
+  687          -0.56           0.00    3    0    0   -6    0    0    0    0    0    0    0    0    0    0
+  688           0.56           0.00    0    0    0    0    0    0    0    6    0    0    0    0    0    0
+  689          -0.56           0.00    0    0    0    0    0    0    0    1    0    0    1    0    0    2
+  690           0.16           0.40    0    0    0    0    0    1    0   -4    0    0    0    0    0   -2
+  691           0.44          -0.12    0    0    0    0    0    0    4   -4    0    0    0    0    0    2
+  692           0.20          -0.36    0    0    0    0    0    0    0    1   -5    0    0    0    0   -2
+  693          -0.36          -0.20    0    0    0    0    1    0   -3    7   -4    0    0    0    0    0
+  694          -0.56           0.00    0    0    4   -4    1    0    0    0    0    0    0    0    0    0
+  695           0.55           0.00    1    1    0   -2    2    0    0    0    0    0    0    0    0    0
+  696           0.52           0.00    1    0    0   -4   -2    0    0    0    0    0    0    0    0    0
+  697          -0.52           0.00    1    1    0   -4    1    0    0    0    0    0    0    0    0    0
+  698           0.52           0.00    0    0    0    0    1    0    3   -5    0    2    0    0    0    0
+  699           0.52           0.00    0    1    0   -1    0    0    0    0    0    0    0    0    0    0
+  700           0.16           0.36    0    0    1   -1    0    0    0   -1    0    0    2    0    0    0
+  701          -0.52           0.00    0    0    2   -2    0    0   -3    3    0    0    0    0    0    0
+  702          -0.52           0.00    1    0    0   -2    0    0    0   -2    0    3    0    0    0    0
+  703          -0.52           0.00    1    0   -2   -2   -2    0    0   -2    0    2    0    0    0    0
+  704          -0.52           0.00    0    0    2    6    2    0    0    0    0    0    0    0    0    0
+  705           0.00          -0.52    0    0    1   -1    1    0    2   -4    0   -3    0    0    0    0
+  706           0.52           0.00    0    0    0    0    0    0    9   -9    0    0    0    0    0    0
+  707          -0.52           0.00    0    0    0    0    0    0    0    1    1    0    0    0    0    2
+  708           0.12           0.40    0    0    0    0    0    0    5   -6    0    0    0    0    0    2
+  709           0.52           0.00    1    0   -4    0    0    0    0    0    0    0    0    0    0    0
+  710          -0.52           0.00    2    0   -2   -4    0    0    0    0    0    0    0    0    0    0
+  711           0.00          -0.52    1    0    1    0    1    0    0    0    0    0    0    0    0    0
+  712           0.52           0.00    1    1    0    0    2    0    0    0    0    0    0    0    0    0
+  713           0.52           0.00    0    0    0    1    2    0    0    0    0    0    0    0    0    0
+  714          -0.51           0.00    1    0    0    2   -2    0    0    0    0    0    0    0    0    0
+  715          -0.51           0.00    1    0    2    0    3    0    0    0    0    0    0    0    0    0
+  716           0.48           0.00    0    1    0    2    2    0    0    0    0    0    0    0    0    0
+  717           0.48           0.00    0    0    2    2   -1    0    0    0    0    0    0    0    0    0
+  718          -0.16           0.32    0    0    0    0    1    0    0    2   -4    0    0    0    0    0
+  719          -0.48           0.00    1   -2    2   -2    2    0    0    0    0    0    0    0    0    0
+  720          -0.48           0.00    0    1   -2   -1   -2    0    0    0    0    0    0    0    0    0
+  721           0.48           0.00    3   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+  722           0.48           0.00    4    0    0   -4    0    0    0    0    0    0    0    0    0    0
+  723          -0.48           0.00    3   -1    2    2    2    0    0    0    0    0    0    0    0    0
+  724          -0.12          -0.36    0    0    1   -1    1    0    0   -1    0    3    0    0    0    0
+  725          -0.32           0.16    0    0    1   -1    1    0    0   -1    0    0    3    0    0    0
+  726           0.32          -0.16    0    0    0    0    0    0    4   -2    0    0    0    0    0    2
+  727          -0.12          -0.36    0    0    0    0    0    0    3   -5    0    0    0    0    0    1
+  728           0.16           0.32    0    0    0    0    0    0    0    7  -13    0    0    0    0   -2
+  729           0.20          -0.28    0    0    0    0    0    0    0    5   -7    0    0    0    0    0
+  730          -0.20          -0.28    0    0    0    0    0    0    0    1    0    3    0    0    0    2
+  731          -0.36           0.12    0    0    0    0    0    0    0    0    0    4    0    0    0    2
+  732          -0.48           0.00    0    0    0    0    0    0    0    0    0    2   -5    0    0    2
+  733           0.32          -0.16    0    0    0    0    0    0    0    0    0    0    3    0    0    1
+  734           0.48           0.00    1   -1    0    4    0    0    0    0    0    0    0    0    0    0
+  735          -0.48           0.00    2    1    0    2    0    0    0    0    0    0    0    0    0    0
+  736          -0.48           0.00    2    0    0   -2   -1    0    0   -2    0    0    5    0    0    0
+  737          -0.48           0.00    3    0    0    2    1    0    0    0    0    0    0    0    0    0
+  738           0.00          -0.48    1    0    1   -2    1    0    0    0    0    0    0    0    0    0
+  739           0.48           0.00    1    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+  740          -0.48           0.00    0    0    2   -2    2    0   -3    3    0    0    0    0    0    0
+  741          -0.48           0.00    1    0    0   -1    2    0    0    0    0    0    0    0    0    0
+  742           0.00           0.48    0    0    2   -2    2    0   -5    6    0    0    0    0    0    0
+  743           0.44           0.00    0    0    0    0    1    0    0   -1    0    1    0    0    0    0
+  744          -0.32          -0.12    0    0    1   -1   -1    0    0    0   -2    0    0    0    0    0
+  745          -0.44           0.00    0    1   -2    0    0    0    0    0    0    0    0    0    0    0
+  746           0.20          -0.24    0    0    0    0    0    0    0    9  -17    0    0    0    0    0
+  747           0.44           0.00    3   -1   -2   -1   -2    0    0    0    0    0    0    0    0    0
+  748          -0.44           0.00    1   -1    0   -3    0    0    0    0    0    0    0    0    0    0
+  749           0.44           0.00    0    0    2   -2    1    0   -2    2    0    0    0    0    0    0
+  750           0.20          -0.24    0    0    0    0    0    0    8  -13    0    0    0    0    0    1
+  751           0.12           0.32    0    0    0    0    0    0    5  -10    0    0    0    0    0   -2
+  752          -0.20           0.24    0    0    0    0    0    0    1    0    0    0    0    0    0    2
+  753           0.32          -0.12    0    0    0    0    0    0    1   -3    0    0    0    0    0    0
+  754           0.00           0.44    0    0    0    0    0    0    0    6  -11    0    0    0    0   -2
+  755           0.00           0.44    0    0    0    0    0    0    0    5   -8    0    0    0    0    0
+  756           0.44           0.00    0    0    0    0    0    0    0    1   -3    0    0    0    0   -2
+  757          -0.44           0.00    1    2    0   -4    0    0    0    0    0    0    0    0    0    0
+  758          -0.44           0.00    0    1    0    4    0    0    0    0    0    0    0    0    0    0
+  759          -0.44           0.00    3    0    2   -2    0    0    0    0    0    0    0    0    0    0
+  760           0.44           0.00    1   -1    2   -4    1    0    0    0    0    0    0    0    0    0
+  761           0.44           0.00    2    0    2    2    0    0    0    0    0    0    0    0    0    0
+  762           0.40           0.00    2    0    2    0   -1    0    0    0    0    0    0    0    0    0
+  763          -0.40           0.00    2    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+  764          -0.40           0.00    2    1    0   -4    1    0    0    0    0    0    0    0    0    0
+  765          -0.40           0.00    1    0    4   -4    2    0    0    0    0    0    0    0    0    0
+  766           0.40           0.00    2    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+  767           0.24           0.16    2    0    0   -2   -1    0    0   -5    6    0    0    0    0    0
+  768           0.00          -0.40    0    0    3    0    2    0    0    0    0    0    0    0    0    0
+  769           0.12           0.28    2    0   -1   -1   -1    0    0   -1    0    3    0    0    0    0
+  770           0.40           0.00    1    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  771          -0.40           0.00    0    0    4   -1    2    0    0    0    0    0    0    0    0    0
+  772           0.40           0.00    0    0    0    0    0    0    0    7  -13    0    0    0    0    0
+  773           0.40           0.00    5    0    2   -2    2    0    0    0    0    0    0    0    0    0
+  774           0.00          -0.40    0    0    0    0    0    0    9  -12    0    0    0    0    0   -2
+  775          -0.40           0.00    0    0    0    0    0    0    5   -9    0    0    0    0    0   -2
+  776           0.00          -0.40    0    0    0    0    0    0    4   -4    0    0    0    0    0    1
+  777           0.00          -0.40    0    0    0    0    0    0    3   -1    0    0    0    0    0    1
+  778           0.20          -0.20    0    0    0    0    0    0    2   -4    0    0    0    0    0    0
+  779          -0.40           0.00    0    0    0    0    0    0    1   -2    0    0    0    0    0    1
+  780          -0.40           0.00    0    0    0    0    0    0    0    5   -3    0    0    0    0    2
+  781          -0.12          -0.28    0    0    0    0    0    0    0    4   -8    1    5    0    0   -2
+  782           0.40           0.00    0    0    0    0    0    0    3   -5    0    2    0    0    0    0
+  783           0.40           0.00    1    1   -2    2    0    0    0    0    0    0    0    0    0    0
+  784           0.40           0.00    2    0    0    4    0    0    0    0    0    0    0    0    0    0
+  785           0.40           0.00    0    0    0    3    1    0    0    0    0    0    0    0    0    0
+  786           0.40           0.00    4    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  787           0.00           0.40    0    0    1   -1    2    0    0   -1    0    2    0    0    0    0
+  788          -0.20          -0.16    0    0    0    0    2    0    0   -1    2    0    0    0    0    0
+  789           0.36           0.00    1    0    0   -3    1    0    0    0    0    0    0    0    0    0
+  790           0.36           0.00    1   -2    0    0    1    0    0    0    0    0    0    0    0    0
+  791           0.24          -0.12    0    2   -2    0   -1    0    0    0    0    0    0    0    0    0
+  792           0.20          -0.16    2    0   -1   -1   -1    0    0    3   -7    0    0    0    0    0
+  793           0.00           0.36    0    0    2    0    2    0    0   -4    8   -3    0    0    0    0
+  794           0.00           0.36    0    0    2    0    2    0    0    4   -8    3    0    0    0    0
+  795          -0.36           0.00    2    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  796           0.12           0.24    1    0    1    0    0    0    0    0    0    0    0    0    0    0
+  797          -0.36           0.00    2   -2    2    0    2    0    0    0    0    0    0    0    0    0
+  798          -0.36           0.00    0    3    0   -2    0    0    0    0    0    0    0    0    0    0
+  799          -0.36           0.00    2   -1    0   -1    0    0    0    0    0    0    0    0    0    0
+  800          -0.36           0.00    1    3    0   -2    0    0    0    0    0    0    0    0    0    0
+  801           0.36           0.00    1   -1   -2   -3   -2    0    0    0    0    0    0    0    0    0
+  802           0.00           0.36    0    0    1   -1    1    0   -2    3    0    0    0    0    0    0
+  803           0.00           0.36    0    0    0    0    0    0    7   -7    0    0    0    0    0   -1
+  804           0.00           0.36    0    0    0    0    0    0    6   -7    0    0    0    0    0    0
+  805          -0.36           0.00    0    0    0    0    0    0    6   -9    0    0    0    0    0   -1
+  806           0.00           0.36    0    0    0    0    0    0    4   -3    0    0    0    0    0    2
+  807           0.12          -0.24    0    0    0    0    0    0    1   -2    0    0    0    0    0   -2
+  808          -0.24           0.12    0    0    0    0    0    0    0    6   -5    0    0    0    0    2
+  809          -0.36           0.00    0    0    0    0    0    0    0    5    0   -3    0    0    0    2
+  810           0.00           0.36    0    0    0    0    0    0    0    3   -2    0    0    0    0    0
+  811           0.36           0.00    0    0    0    0    0    0    0    3   -6    0    0    0    0   -1
+  812           0.24          -0.12    0    0    0    0    0    0    0    2   -5    0    0    0    0   -2
+  813           0.00          -0.36    0    0    1   -1    1    0    0    1    0    0    0    0    0    0
+  814          -0.36           0.00    1   -2   -2    0   -2    0    0    0    0    0    0    0    0    0
+  815           0.36           0.00    2    0    0    2    2    0    0    0    0    0    0    0    0    0
+  816           0.36           0.00    0    1    0   -2    2    0    0    0    0    0    0    0    0    0
+  817          -0.36           0.00    0    0    2    0   -2    0    0    0    0    0    0    0    0    0
+  818           0.36           0.00    0    0    2   -2   -2    0    0    0    0    0    0    0    0    0
+  819          -0.13           0.22    0    0    1   -1    2    0    0   -1    0   -2    5    0    0    0
+  820          -0.32           0.00    0    0    0    0    1    0    3   -5    0    0    0    0    0    0
+  821          -0.32           0.00    1    1    0    2   -1    0    0    0    0    0    0    0    0    0
+  822           0.32           0.00    4    0    0    0   -1    0    0    0    0    0    0    0    0    0
+  823          -0.20          -0.12    0    0    0    0    1    0    0   -8   15    0    0    0    0    0
+  824           0.32           0.00    0    2    2    0    1    0    0    0    0    0    0    0    0    0
+  825           0.12           0.20    2    0    0   -2    1    0    0   -6    8    0    0    0    0    0
+  826          -0.32           0.00    3   -1    2    0    1    0    0    0    0    0    0    0    0    0
+  827           0.32           0.00    0    0    2    0    2    0    1   -1    0    0    0    0    0    0
+  828          -0.32           0.00    0    0    2    0    2    0   -1    1    0    0    0    0    0    0
+  829          -0.32           0.00    2    0    2   -1    1    0    0    0    0    0    0    0    0    0
+  830           0.00          -0.32    2    0   -1   -1   -2    0    0   -1    0    2    0    0    0    0
+  831           0.32           0.00    1    2    2   -2    1    0    0    0    0    0    0    0    0    0
+  832           0.32           0.00    2    2    2   -2    2    0    0    0    0    0    0    0    0    0
+  833           0.12          -0.20    0    0    2   -2    0    0    0   -9   13    0    0    0    0    0
+  834          -0.32           0.00    3    0    2   -1    2    0    0    0    0    0    0    0    0    0
+  835           0.00          -0.32    1    0    0   -2    0    0   20  -21    0    0    0    0    0    0
+  836           0.32           0.00    0    0    2   -2    1    0    0   -2    0    0    2    0    0    0
+  837           0.00           0.32    0    0    2   -2    1    0    0   -8   11    0    0    0    0    0
+  838           0.00          -0.32    0    0    1   -1    1    0    0   -1    0    0    0    2    0    0
+  839           0.00          -0.32    0    0    1   -1    1    0    0   -1    0   -1    2    0    0    0
+  840           0.32           0.00    0    0    0    0    0    0    8  -12    0    0    0    0    0    0
+  841          -0.32           0.00    0    0    0    0    0    0    5   -5    0    0    0    0    0    2
+  842           0.00           0.32    0    0    0    0    0    0    5   -6    0    0    0    0    0    0
+  843           0.32           0.00    0    0    0    0    0    0    2   -6    0    0    0    0    0   -2
+  844           0.00           0.32    0    0    0    0    0    0    0    8  -15    0    0    0    0   -1
+  845           0.00          -0.32    0    0    0    0    0    0    0    5   -2    0    0    0    0    2
+  846           0.32           0.00    0    0    0    0    0    0    0    4   -4    0    0    0    0    0
+  847          -0.16           0.16    0    0    0    0    0    0    0    2    2    0    0    0    0    2
+  848          -0.16           0.16    0    0    0    0    0    0    0    1    0   -4    0    0    0    0
+  849           0.00           0.32    0    0    0    0    0    0    0    0    0    0    1    0    0    2
+  850           0.20           0.12    0    0    1   -1    1    0    0   -1    0   -2    4    0    0    0
+  851           0.20           0.12    0    0    0    0    0    1    0   -4    0    0    0    0    0    0
+  852          -0.20           0.12    0    0    0    0    0    0    0    5   -8    0    0    0    0   -2
+  853           0.12           0.20    0    0    0    0    0    0    0    4   -8    0    0    0    0    0
+  854           0.12          -0.20    0    0    0    0    0    0    0    2   -6    0    0    0    0   -2
+  855           0.00           0.32    0    0    2   -2    1   -1    0    2    0    0    0    0    0    0
+  856          -0.32           0.00    0    0    4   -2    0    0    0    0    0    0    0    0    0    0
+  857           0.32           0.00    0    0    0    1   -2    0    0    0    0    0    0    0    0    0
+  858           0.28           0.00    2    0   -4    2   -2    0    0    0    0    0    0    0    0    0
+  859          -0.28           0.00    0    0    0    4    2    0    0    0    0    0    0    0    0    0
+  860           0.28           0.00    1    0    0    4   -1    0    0    0    0    0    0    0    0    0
+  861           0.28           0.00    1   -2    0   -2   -1    0    0    0    0    0    0    0    0    0
+  862           0.28           0.00    1    1    0   -1    1    0    0    0    0    0    0    0    0    0
+  863           0.16           0.12    1    0    0   -1    1    0    0   -3    4    0    0    0    0    0
+  864           0.28           0.00    3    1    2    0    1    0    0    0    0    0    0    0    0    0
+  865          -0.28           0.00    1    1    2    1    1    0    0    0    0    0    0    0    0    0
+  866          -0.12          -0.16    0    0    0    0    1    0    0   -9   17    0    0    0    0    0
+  867           0.28           0.00    1    1    4   -2    2    0    0    0    0    0    0    0    0    0
+  868          -0.28           0.00    4    0    2   -4    2    0    0    0    0    0    0    0    0    0
+  869          -0.28           0.00    0    2    0    2    0    0    0    0    0    0    0    0    0    0
+  870           0.28           0.00    1    0    2   -3    0    0    0    0    0    0    0    0    0    0
+  871           0.28           0.00    3    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+  872           0.28           0.00    2   -2    0    0    0    0    0    0    0    0    0    0    0    0
+  873           0.28           0.00    1    1    0   -3    0    0    0    0    0    0    0    0    0    0
+  874           0.28           0.00    1    0    2    3    2    0    0    0    0    0    0    0    0    0
+  875           0.28           0.00    1    0    0   -2    0    0    0    1    0   -1    0    0    0    0
+  876          -0.28           0.00    1    0   -2   -2   -2    0   -3    3    0    0    0    0    0    0
+  877           0.28           0.00    0    0    2   -2    1    0    0   -2    0    3    0    0    0    0
+  878           0.00           0.28    0    0    0    0    0    0    8   -8    0    0    0    0    0   -1
+  879           0.00           0.28    0    0    0    0    0    0    8  -10    0    0    0    0    0   -1
+  880           0.00          -0.28    0    0    0    0    0    0    4   -2    0    0    0    0    0    1
+  881          -0.28           0.00    0    0    0    0    0    0    3   -4    0    0    0    0    0   -1
+  882           0.28           0.00    0    0    0    0    0    0    3   -6    0    0    0    0    0   -1
+  883          -0.12          -0.16    0    0    0    0    0    0    1   -4    0    0    0    0    0   -2
+  884           0.00           0.28    0    0    0    0    0    0    0    6    0    0    0    0    0    1
+  885           0.00          -0.28    0    0    0    0    0    0    0    6   -7    0    0    0    0    2
+  886           0.12          -0.16    0    0    0    0    0    0    0    4    0    0    0    0    0    0
+  887          -0.28           0.00    0    0    0    0    0    0    0    4    0    0   -2    0    0    2
+  888           0.00          -0.28    0    0    0    0    0    0    0    3    0    0   -2    0    0    2
+  889           0.00           0.28    0    0    0    0    0    0    0    1    0   -1    0    0    0    1
+  890           0.00          -0.28    0    0    0    0    0    0    0    1   -6    0    0    0    0   -2
+  891           0.28           0.00    0    0    0    0    0    0    0    0    0    4   -5    0    0    2
+  892          -0.28           0.00    0    0    0    0    0    0    0    0    0    0    0    2    0    2
+  893          -0.28           0.00    0    0    0    0    0    0    3   -7    4    0    0    0    0    0
+  894           0.28           0.00    1   -1    2   -2    0    0    0    0    0    0    0    0    0    0
+  895           0.28           0.00    1    1    2   -4    0    0    0    0    0    0    0    0    0    0
+  896           0.12          -0.16    0    0    1   -1    1    0    0   -1    0    0   -2    0    0    0
+  897           0.28           0.00    0    0    2    4    0    0    0    0    0    0    0    0    0    0
+  898           0.28           0.00    2   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+  899          -0.28           0.00    0    0    1   -1    2    0    0    0   -2    0    0    0    0    0
+  900           0.00          -0.28    0    0    0    0    2    0    0    4   -8    3    0    0    0    0
+  901           0.00          -0.28    0    0    0    0    2    0    0   -4    8   -3    0    0    0    0
+  902           0.28           0.00    1    1    2   -4    2    0    0    0    0    0    0    0    0    0
+  903           0.00           0.24    0    0    2   -2   -1    0   -5    6    0    0    0    0    0    0
+  904           0.24           0.00    1   -2    0   -2    1    0    0    0    0    0    0    0    0    0
+  905          -0.24           0.00    0    1    0   -4    1    0    0    0    0    0    0    0    0    0
+  906          -0.24           0.00    1   -2    2    0    1    0    0    0    0    0    0    0    0    0
+  907           0.24           0.00    0    0    2    0    2    0    0   -1    0    1    0    0    0    0
+  908          -0.24           0.00    0    0    2    0    2    0    0    1    0   -1    0    0    0    0
+  909          -0.24           0.00    2    0   -4   -2   -1    0    0    0    0    0    0    0    0    0
+  910           0.24           0.00    2    0    2    1    1    0    0    0    0    0    0    0    0    0
+  911           0.24           0.00    2    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+  912           0.00          -0.24    2    0   -1   -1   -1    0    0   -1    0    2    0    0    0    0
+  913          -0.24           0.00    4   -1    2    0    2    0    0    0    0    0    0    0    0    0
+  914           0.24           0.00    5    0    0    0    0    0    0    0    0    0    0    0    0    0
+  915          -0.24           0.00    3    0    0   -3    0    0    0    0    0    0    0    0    0    0
+  916          -0.24           0.00    2    2    0   -4    0    0    0    0    0    0    0    0    0    0
+  917           0.24           0.00    2    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  918          -0.24           0.00    2    1    2    1    2    0    0    0    0    0    0    0    0    0
+  919          -0.24           0.00    1    0    2   -2    2    0    0   -2    0    2    0    0    0    0
+  920          -0.24           0.00    1    0    0    0    0    0    0   -2    0    2    0    0    0    0
+  921           0.24           0.00    1    0    0   -2    0    0    2   -2    0    0    0    0    0    0
+  922           0.00          -0.24    1    0   -1    1   -1    0  -18   17    0    0    0    0    0    0
+  923           0.24           0.00    0    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  924          -0.24           0.00    0    1    2    3    2    0    0    0    0    0    0    0    0    0
+  925           0.24           0.00    0    0    0    0    0    0    8  -12    0    0    0    0    0   -2
+  926           0.24           0.00    0    0    0    0    0    0    8  -16    0    0    0    0    0   -2
+  927           0.00           0.24    0    0    0    0    0    0    7   -8    0    0    0    0    0    0
+  928           0.12          -0.12    0    0    0    0    0    0    2   -3    0    0    0    0    0    1
+  929           0.00          -0.24    0    0    0    0    0    0    0    5   -6    0    0    0    0    2
+  930          -0.24           0.00    0    0    0    0    0    0    0    4   -6    0    0    0    0   -2
+  931           0.00           0.24    0    0    0    0    0    0    0    4   -8    1    5    0    0    2
+  932          -0.24           0.00    0    0    0    0    0    0    0    2    0   -2    0    0    0    2
+  933           0.00          -0.24    0    0    0    0    0    0    0    2   -7    0    0    0    0   -2
+  934           0.24           0.00    0    0    0    0    0    0    0    0    2    0    0    0    0    0
+  935          -0.24           0.00    0    0    0    0    0    0    0    0    0    0    5    0    0    2
+  936          -0.24           0.00    0    0    0    0    0    0    0    0    0    0    0    0    2    2
+  937           0.24           0.00    0    1    0    3    0    0    0    0    0    0    0    0    0    0
+  938           0.24           0.00    0    2    2    2    2    0    0    0    0    0    0    0    0    0
+  939           0.24           0.00    2   -1    2   -1    2    0    0    0    0    0    0    0    0    0
+  940           0.24           0.00    1   -1    2    2    0    0    0    0    0    0    0    0    0    0
+  941           0.24           0.00    2    1    2   -4    1    0    0    0    0    0    0    0    0    0
+  942          -0.24           0.00    2    0    0   -2   -2    0   -3    3    0    0    0    0    0    0
+  943          -0.24           0.00    1   -1    0   -4   -1    0    0    0    0    0    0    0    0    0
+  944           0.00           0.24    0    0    1   -1    2    0    0   -1    0    1    0    0    0    0
+  945           0.00          -0.24    0    0    1   -1    2    0    0   -1    0   -1    0    0    0    0
+  946           0.24           0.00    0    0    0    0    1    0    2   -2    0    0    0    0    0    0
+  947           0.00           0.20    1    0   -1    0    1    0    0    0    0    0    0    0    0    0
+  948           0.20           0.00    2   -2    0   -2    1    0    0    0    0    0    0    0    0    0
+  949          -0.20           0.00    0    0    1   -1    2    0    0   -1    0    0    1    0    0    0
+  950           0.20           0.00    2    0    0   -2   -1    0   -3    3    0    0    0    0    0    0
+  951           0.20           0.00    1   -2    0    0   -1    0    0    0    0    0    0    0    0    0
+  952           0.00           0.20    0    0    0    0    1    0    0    0    0    0   -1    0    0    0
+  953          -0.20           0.00    1    0   -2    1    1    0    0    0    0    0    0    0    0    0
+  954           0.20           0.00    2    0    0   -3   -1    0    0    0    0    0    0    0    0    0
+  955           0.20           0.00    2    1   -2    0    1    0    0    0    0    0    0    0    0    0
+  956           0.20           0.00    2    0    0   -1    1    0    0    0    0    0    0    0    0    0
+  957           0.20           0.00    2   -1    2   -2    1    0    0    0    0    0    0    0    0    0
+  958           0.20           0.00    1   -1   -4    2   -2    0    0    0    0    0    0    0    0    0
+  959          -0.20           0.00    1   -2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  960           0.20           0.00    2    0    4   -2    1    0    0    0    0    0    0    0    0    0
+  961           0.20           0.00    0    0    2    3    1    0    0    0    0    0    0    0    0    0
+  962           0.20           0.00    0    1    4   -2    1    0    0    0    0    0    0    0    0    0
+  963           0.20           0.00    1    0   -2    0   -2    0  -10    3    0    0    0    0    0    0
+  964           0.00           0.20    1    0    1   -2    0    0    0    0    0    0    0    0    0    0
+  965           0.20           0.00    1   -1    2    1    2    0    0    0    0    0    0    0    0    0
+  966           0.20           0.00    4    1    2    0    2    0    0    0    0    0    0    0    0    0
+  967           0.20           0.00    0    0    2   -2    1    0    0   -1    0    1    0    0    0    0
+  968           0.20           0.00    1    0    0   -1    0    0    0   -2    2    0    0    0    0    0
+  969          -0.20           0.00    0    0    2   -2    0    0    0   -2    0    2    0    0    0    0
+  970           0.00          -0.20    1    0    0    0    0    0    0    4   -8    3    0    0    0    0
+  971          -0.20           0.00    4   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  972           0.20           0.00    2    1   -2   -6   -2    0    0    0    0    0    0    0    0    0
+  973          -0.20           0.00    2    0    0   -2    0    0    0   -4    8   -3    0    0    0    0
+  974           0.20           0.00    2    0    0   -2    0    0   -2    2    0    0    0    0    0    0
+  975           0.20           0.00    2    0    0   -2   -1    0    0   -2    0    4   -5    0    0    0
+  976           0.00          -0.20    2    0   -1   -1   -1    0    0   -1    0    0    0    0    0    0
+  977          -0.20           0.00    1    1   -2   -3   -2    0    0    0    0    0    0    0    0    0
+  978           0.00          -0.20    1    0    3    0    3    0    0    0    0    0    0    0    0    0
+  979           0.00           0.20    1    0    1   -1    1    0    0   -1    0    0    0    0    0    0
+  980           0.00          -0.20    1    0    0    0    0    0    0   -4    8   -3    0    0    0    0
+  981           0.20           0.00    1    0    0    0    0    0   -1    1    0    0    0    0    0    0
+  982           0.00           0.20    1    0    0   -2    0    0   17  -16    0   -2    0    0    0    0
+  983          -0.20           0.00    1    0   -1   -1   -1    0   20  -20    0    0    0    0    0    0
+  984           0.20           0.00    1    0   -2   -2   -2    0    0   -2    0    3    0    0    0    0
+  985           0.20           0.00    0    3   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  986           0.00           0.20    0    0    1   -1    1    0    1   -2    0    0    0    0    0    0
+  987           0.00           0.20    0    0    1   -1    1    0   -2    1    0    0    0    0    0    0
+  988          -0.20           0.00    0    0    1   -1    0    0    0    0    0    0    0    0    0    0
+  989           0.00           0.20    0    0    0    0    0    0    9   -9    0    0    0    0    0   -1
+  990           0.00           0.20    0    0    0    0    0    0    9  -11    0    0    0    0    0   -1
+  991           0.00           0.20    0    0    0    0    0    0    6  -10    0    0    0    0    0   -1
+  992           0.00          -0.20    0    0    0    0    0    0    5   -3    0    0    0    0    0    1
+  993          -0.20           0.00    0    0    0    0    0    0    4   -5    0    0    0    0    0   -1
+  994           0.00          -0.20    0    0    0    0    0    0    3   -4    0    0    0    0    0   -2
+  995           0.00          -0.20    0    0    0    0    0    0    0    6   -9    0    0    0    0    0
+  996           0.00          -0.20    0    0    0    0    0    0    0    5  -10    0    0    0    0   -2
+  997          -0.20           0.00    0    0    0    0    0    0    0    4    0   -4    0    0    0    2
+  998           0.20           0.00    0    0    0    0    0    0    0    3    0   -4    0    0    0    0
+  999          -0.20           0.00    0    0    0    0    0    0    0    2    0    0    0    0    0    0
+ 1000          -0.20           0.00    0    0    0    0    0    0    0    2    0   -5    0    0    0   -2
+ 1001           0.00           0.20    0    0    0    0    0    0    0    1    0   -2    5    0    0    2
+ 1002           0.20           0.00    0    0    0    0    0    0    0    1    0   -2    0    0    0   -2
+ 1003           0.00          -0.20    0    0    0    0    0    0    0    1    0   -3    0    0    0   -1
+ 1004          -0.20           0.00    0    0    0    0    0    0    0    1    0   -5    0    0    0   -2
+ 1005           0.00          -0.20    0    0    0    0    0    0    0    0    0    0    0    2    0    1
+ 1006           0.00           0.20    0    0    0    0    0    0    0    0    0    0    0    1   -2   -2
+ 1007          -0.20           0.00    0    2    0   -4    0    0    0    0    0    0    0    0    0    0
+ 1008           0.20           0.00    0    0    0    6    0    0    0    0    0    0    0    0    0    0
+ 1009          -0.20           0.00    3    0    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1010           0.20           0.00    2   -1   -2    2   -1    0    0    0    0    0    0    0    0    0
+ 1011           0.00          -0.20    1    0   -1    1   -1    0    0    1    0    0    0    0    0    0
+ 1012          -0.20           0.00    0    0    2   -4    0    0    0    0    0    0    0    0    0    0
+ 1013          -0.20           0.00    1    0    0   -1   -1    0    0   -2    2    0    0    0    0    0
+ 1014           0.20           0.00    0    1    0    4    1    0    0    0    0    0    0    0    0    0
+ 1015           0.20           0.00    1    0    0   -6   -1    0    0    0    0    0    0    0    0    0
+ 1016           0.00           0.20    0    0    0    0    1    0    5   -8    0    0    0    0    0    0
+ 1017           0.20           0.00    0    0    4    2    2    0    0    0    0    0    0    0    0    0
+ 1018           0.20           0.00    3    0    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1019          -0.20           0.00    0    0    2   -2    2    0    0   -2    0    2    0    0    0    0
+ 1020          -0.20           0.00    1   -1    0   -2   -2    0    0    0    0    0    0    0    0    0
+ 1021          -0.20           0.00    0    0    1   -1    2    0   -5    7    0    0    0    0    0    0
+ 1022          -0.20           0.00    2    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+ 1023           0.20           0.00    0    0    0    0    1    0    0    2   -2    0    0    0    0    0
+ 1024          -0.20           0.00    1    0   -2    0    2    0    0    0    0    0    0    0    0    0
+ 1025          -0.20           0.00    1   -1    0    0   -2    0    0    0    0    0    0    0    0    0
+ 1026           0.20           0.00    1    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+ 1027           0.16           0.00    0    0    0    0    1    0   -2    2    0    0    0    0    0    0
+ 1028           0.16           0.00    1    0    0   -3   -1    0    0    0    0    0    0    0    0    0
+ 1029           0.00          -0.16    0    0    1   -1   -1    0    0   -1    0   -1    0    0    0    0
+ 1030           0.16           0.00    2    0   -2   -2    1    0    0    0    0    0    0    0    0    0
+ 1031           0.16           0.00    1    0    0   -1    1    0    0   -1    0    1    0    0    0    0
+ 1032           0.16           0.00    2    0    0   -3    1    0    0    0    0    0    0    0    0    0
+ 1033           0.16           0.00    3    0    0    2   -1    0    0    0    0    0    0    0    0    0
+ 1034           0.16           0.00    1    1    0    1    1    0    0    0    0    0    0    0    0    0
+ 1035          -0.16           0.00    0    2    0    0   -2    0    0    0    0    0    0    0    0    0
+ 1036           0.16           0.00    2   -1    0    2   -1    0    0    0    0    0    0    0    0    0
+ 1037           0.16           0.00    1    1    0    1   -1    0    0    0    0    0    0    0    0    0
+ 1038          -0.16           0.00    1    0   -4   -2   -1    0    0    0    0    0    0    0    0    0
+ 1039          -0.16           0.00    1   -1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+ 1040          -0.16           0.00    1   -2    2    2    1    0    0    0    0    0    0    0    0    0
+ 1041           0.16           0.00    3    1    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1042           0.16           0.00    1    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+ 1043          -0.16           0.00    2    0    4   -4    2    0    0    0    0    0    0    0    0    0
+ 1044          -0.04           0.12    2   -2    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1045           0.16           0.00    3    0    4   -2    2    0    0    0    0    0    0    0    0    0
+ 1046           0.00           0.16    0    0    1   -1    0    0    3   -6    0    0    0    0    0    0
+ 1047           0.16           0.00    1    1    2    4    2    0    0    0    0    0    0    0    0    0
+ 1048           0.16           0.00    0    0    2   -3    0    0    0    0    0    0    0    0    0    0
+ 1049           0.00          -0.16    0    0    1    1    0    0    0    1    0    0    0    0    0    0
+ 1050           0.00          -0.16    0    0    1   -1    0    0   -4    5    0    0    0    0    0    0
+ 1051           0.00           0.16    0    0    1   -1    0    0    0   -2    2    0    0    0    0    0
+ 1052           0.16           0.00    0    0    1   -1    0    0    0   -1    0    0    1    0    0    0
+ 1053           0.00           0.16    0    0    1   -1    0    0    0   -1    0    1    0    0    0    0
+ 1054          -0.16           0.00    0    1   -2    4   -1    0    0    0    0    0    0    0    0    0
+ 1055           0.00          -0.16    1    0    0   -2    0    0    0    4   -8    3    0    0    0    0
+ 1056           0.16           0.00    3    0    2    1    2    0    0    0    0    0    0    0    0    0
+ 1057           0.16           0.00    3    0    0   -1    0    0    0    0    0    0    0    0    0    0
+ 1058           0.16           0.00    2    1    0    1    0    0    0    0    0    0    0    0    0    0
+ 1059           0.16           0.00    2    0    2    0    1    0    0    1    0    0    0    0    0    0
+ 1060          -0.16           0.00    2    0    0   -2    0    0    0   -2    0    2    2    0    0    0
+ 1061           0.16           0.00    2    0    0   -2    0    0    0   -4    4    0    0    0    0    0
+ 1062          -0.16           0.00    2   -2    2    2    2    0    0    0    0    0    0    0    0    0
+ 1063           0.16           0.00    1    1   -2   -6   -2    0    0    0    0    0    0    0    0    0
+ 1064          -0.16           0.00    1    0    0    0    0    0    1   -1    0    0    0    0    0    0
+ 1065          -0.16           0.00    1    0    0    0    0    0    0   -1    0    1    0    0    0    0
+ 1066          -0.16           0.00    1    0    0    0    0    0   -3    3    0    0    0    0    0    0
+ 1067          -0.16           0.00    1    0    0   -2    0    0    1   -1    0    0    0    0    0    0
+ 1068           0.00          -0.16    1    0    0   -2    0    0    0   -4    8   -3    0    0    0    0
+ 1069          -0.16           0.00    1    0    0   -2    0    0   -2    2    0    0    0    0    0    0
+ 1070          -0.16           0.00    0    0    2   -2    1    0    0    1    0   -1    0    0    0    0
+ 1071           0.16           0.00    0    0    2   -2    1    0    0   -3    0    3    0    0    0    0
+ 1072           0.16           0.00    0    0    2   -2    1    0   -5    5    0    0    0    0    0    0
+ 1073          -0.16           0.00    0    0    1   -1    1    0    1   -3    0    0    0    0    0    0
+ 1074           0.00           0.16    0    0    1   -1    1    0    0   -1    0    0    0   -1    0    0
+ 1075           0.16           0.00    0    0    1   -1    1    0    0   -4    6    0    0    0    0    0
+ 1076           0.00           0.16    0    0    1   -1    1    0   -5    6    0    0    0    0    0    0
+ 1077          -0.16           0.00    0    0    0    2    0    0    0   -1    0    1    0    0    0    0
+ 1078           0.00           0.16    0    0    0    0    0    0    8   -9    0    0    0    0    0    0
+ 1079          -0.16           0.00    0    0    0    0    0    0    7  -10    0    0    0    0    0   -1
+ 1080           0.00          -0.16    0    0    0    0    0    0    5   -5    0    0    0    0    0    1
+ 1081           0.00          -0.16    0    0    0    0    0    0    4   -5    0    0    0    0    0   -2
+ 1082           0.00          -0.16    0    0    0    0    0    0    3   -6    0    0    0    0    0    0
+ 1083           0.00           0.16    0    0    0    0    0    0    3   -8    0    0    0    0    0   -2
+ 1084           0.16           0.00    0    0    0    0    0    0    2   -5    0    0    0    0    0   -1
+ 1085          -0.16           0.00    0    0    0    0    0    0    1    1    0    0    0    0    0    0
+ 1086           0.16           0.00    0    0    0    0    0    0    1   -2    0    0    0    0    0   -1
+ 1087           0.00          -0.16    0    0    0    0    0    0    0    7   -8    0    0    0    0    2
+ 1088           0.00          -0.16    0    0    0    0    0    0    0    7   -9    0    0    0    0    2
+ 1089           0.00           0.16    0    0    0    0    0    0    0    6  -10    0    0    0    0   -2
+ 1090           0.00           0.16    0    0    0    0    0    0    0    3    0    0    0    0    0    2
+ 1091           0.16           0.00    0    0    0    0    0    0    0    3   -8    3    0    0    0   -2
+ 1092           0.00           0.16    0    0    0    0    0    0    0    2    0    0   -2    0    0    1
+ 1093           0.16           0.00    0    0    0    0    0    0    0    2   -4    0    0    0    0    1
+ 1094           0.16           0.00    0    0    0    0    0    0    0    1    0    0    0    0    0   -1
+ 1095           0.00          -0.16    0    0    0    0    0    0    0    1    0   -1    0    0    0   -1
+ 1096           0.00          -0.16    0    0    0    0    0    0    0    0    0    3   -5    0    0    0
+ 1097          -0.16           0.00    0    0    0    0    0    0    0    0    0    2   -2    0    0    0
+ 1098          -0.16           0.00    2    1    0   -6    0    0    0    0    0    0    0    0    0    0
+ 1099          -0.16           0.00    1   -1    0    1    0    0    0    0    0    0    0    0    0    0
+ 1100           0.00          -0.16    0    0    1   -1    0    0   -3    4    0    0    0    0    0    0
+ 1101           0.16           0.00    3   -1    0    2    0    0    0    0    0    0    0    0    0    0
+ 1102           0.16           0.00    4    0    0    2    0    0    0    0    0    0    0    0    0    0
+ 1103          -0.16           0.00    0    0    2   -2    0    0   -4    4    0    0    0    0    0    0
+ 1104           0.16           0.00    1    0    2   -1    0    0    0    0    0    0    0    0    0    0
+ 1105           0.16           0.00    1   -2   -2    2   -1    0    0    0    0    0    0    0    0    0
+ 1106           0.16           0.00    1   -1   -2   -2    0    0    0    0    0    0    0    0    0    0
+ 1107          -0.16           0.00    2    0    2   -3    2    0    0    0    0    0    0    0    0    0
+ 1108          -0.16           0.00    0    1    2    2    0    0    0    0    0    0    0    0    0    0
+ 1109           0.00           0.16    0    0    2   -2    1    0    0   -4    8   -3    0    0    0    0
+ 1110           0.00           0.16    0    0    2   -2    1    0    0    4   -8    3    0    0    0    0
+ 1111           0.16           0.00    3    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+ 1112           0.16           0.00    2   -1    2    0    0    0    0    0    0    0    0    0    0    0
+ 1113          -0.16           0.00    2    0    0    4    1    0    0    0    0    0    0    0    0    0
+ 1114           0.16           0.00    2    0   -4    2   -1    0    0    0    0    0    0    0    0    0
+ 1115          -0.16           0.00    2   -1    0    2    1    0    0    0    0    0    0    0    0    0
+ 1116          -0.16           0.00    2   -2    0   -2   -2    0    0    0    0    0    0    0    0    0
+ 1117           0.00          -0.16    0    0    0    0    1    0    0    1    0   -2    0    0    0    0
+ 1118          -0.16           0.00    3    0    0    0    2    0    0    0    0    0    0    0    0    0
+ 1119           0.00          -0.16    0    0    0    0    1    0    3   -4    0    0    0    0    0    0
+ 1120           0.16           0.00    0    2    0    0    2    0    0    0    0    0    0    0    0    0
+ 1121           0.16           0.00    0    1    2   -4    2    0    0    0    0    0    0    0    0    0
+ 1122          -0.15           0.00    0    0    1   -1    2    0   -8   12    0    0    0    0    0    0
+ 1123           0.00           0.12    0    0    0    0    1    0    0   -1    0    2    0    0    0    0
+ 1124           0.12           0.00    0    0    0    0    1    0    0   -2    2    0    0    0    0    0
+ 1125           0.00           0.12    0    0    0    0    1    0   -3    4    0    0    0    0    0    0
+ 1126           0.12           0.00    1    0    0    0   -1    0  -10    3    0    0    0    0    0    0
+ 1127           0.12           0.00    1    0    0    0    1    0  -10    3    0    0    0    0    0    0
+ 1128           0.00          -0.12    0    0    0    0    2    0    0    0    0    1    0    0    0    0
+ 1129          -0.12           0.00    0    1    4   -4    2    0    0    0    0    0    0    0    0    0
+ 1130          -0.12           0.00    2    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1131           0.12           0.00    1    0    2   -4   -1    0    0    0    0    0    0    0    0    0
+ 1132          -0.12           0.00    1    2    0    0    1    0    0    0    0    0    0    0    0    0
+ 1133           0.00           0.12    2    0    0   -2   -1    0    0   -6    8    0    0    0    0    0
+ 1134           0.12           0.00    2   -1   -2    0    1    0    0    0    0    0    0    0    0    0
+ 1135           0.12           0.00    2    0    0   -2    1    0    0   -5    6    0    0    0    0    0
+ 1136           0.12           0.00    1    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+ 1137           0.12           0.00    2    0   -1   -1    1    0    0    3   -7    0    0    0    0    0
+ 1138           0.12           0.00    1    0    0   -2    1    0    0   -2    0    2    0    0    0    0
+ 1139           0.12           0.00    1    0    0   -1   -1    0    0   -3    4    0    0    0    0    0
+ 1140           0.12           0.00    0    0    0    3    2    0    0    0    0    0    0    0    0    0
+ 1141           0.12           0.00    2    2    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1142           0.12           0.00    1   -1    2   -3    1    0    0    0    0    0    0    0    0    0
+ 1143           0.12           0.00    1    0    4    0    1    0    0    0    0    0    0    0    0    0
+ 1144          -0.12           0.00    2    0    0   -2   -1    0    0   -2    0    3   -1    0    0    0
+ 1145           0.12           0.00    2   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1146           0.12           0.00    2    1    2    2    1    0    0    0    0    0    0    0    0    0
+ 1147           0.00           0.12    0    0    2    0    2    0    2   -3    0    0    0    0    0    0
+ 1148           0.00           0.12    0    0    2    0    2    0   -2    3    0    0    0    0    0    0
+ 1149          -0.12           0.00    0    0    2    0    2    0    2   -2    0    0    0    0    0    0
+ 1150           0.12           0.00    0    0    2    0    2    0   -2    2    0    0    0    0    0    0
+ 1151          -0.12           0.00    5    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1152           0.12           0.00    3    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+ 1153           0.12           0.00    1    2    2    0    1    0    0    0    0    0    0    0    0    0
+ 1154          -0.12           0.00    1   -1    2    4    1    0    0    0    0    0    0    0    0    0
+ 1155           0.12           0.00    2    0   -2   -2   -2    0    0   -2    0    2    0    0    0    0
+ 1156          -0.12           0.00    2    0    2    4    1    0    0    0    0    0    0    0    0    0
+ 1157           0.00           0.12    0    0    2   -2    1    0   -8   11    0    0    0    0    0    0
+ 1158           0.00           0.12    1    0    2    0    2    0    0   -4    8   -3    0    0    0    0
+ 1159           0.00           0.12    1    0    2    0    2    0    0    4   -8    3    0    0    0    0
+ 1160          -0.12           0.00    1   -2    0   -1    0    0    0    0    0    0    0    0    0    0
+ 1161           0.12           0.00    2    0    2   -2    2    0    0   -2    0    3    0    0    0    0
+ 1162           0.12           0.00    1    0    2    0    2    0    1   -1    0    0    0    0    0    0
+ 1163          -0.12           0.00    1    0    2    0    2    0   -1    1    0    0    0    0    0    0
+ 1164           0.00          -0.12    0    0    2   -2    0   -1    0    2    0    0    0    0    0    0
+ 1165          -0.12           0.00    4    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+ 1166          -0.12           0.00    1    0    2   -6    1    0    0    0    0    0    0    0    0    0
+ 1167           0.12           0.00    0    1   -4    2   -1    0    0    0    0    0    0    0    0    0
+ 1168           0.00           0.12    0    0    1   -1    0    0    0   -1    0    0   -2    0    0    0
+ 1169           0.12           0.00    1   -1   -2    1   -1    0    0    0    0    0    0    0    0    0
+ 1170           0.00           0.12    0    0    1   -1    0    0    0    1    0    0    0    0    0    0
+ 1171           0.00           0.12    2    0   -1   -1    0    0    0   -1    0    3    0    0    0    0
+ 1172           0.00          -0.12    0    0    1   -1    0    0   -1    0    0    0    0    0    0    0
+ 1173           0.08           0.04    0    0    0    0    0    0    0    0    1    0    0    0    0    0
+ 1174           0.12           0.00    4    1    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1175           0.12           0.00    3    1    2    2    2    0    0    0    0    0    0    0    0    0
+ 1176          -0.12           0.00    3    1    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1177           0.12           0.00    3    1   -2   -6   -2    0    0    0    0    0    0    0    0    0
+ 1178          -0.12           0.00    3   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1179          -0.12           0.00    3   -1    0   -3    0    0    0    0    0    0    0    0    0    0
+ 1180           0.12           0.00    2    1    0   -3    0    0    0    0    0    0    0    0    0    0
+ 1181           0.00           0.12    2    0    1   -3    1    0   -6    7    0    0    0    0    0    0
+ 1182           0.00          -0.12    2    0    0   -2    0    0    2   -5    0    0    0    0    0    0
+ 1183           0.00           0.12    2    0    0   -2    0    0    0   -2    0    5   -5    0    0    0
+ 1184           0.12           0.00    2    0    0   -2    0    0    0   -2    0    1    5    0    0    0
+ 1185           0.12           0.00    2    0    0   -2    0    0    0   -2    0    0    2    0    0    0
+ 1186           0.12           0.00    2    0    0   -2    0    0   -4    4    0    0    0    0    0    0
+ 1187           0.00           0.12    2    0   -2    0   -2    0    0    5   -9    0    0    0    0    0
+ 1188          -0.12           0.00    2    0   -2   -5   -2    0    0    0    0    0    0    0    0    0
+ 1189          -0.12           0.00    2   -1    2    4    2    0    0    0    0    0    0    0    0    0
+ 1190           0.12           0.00    1    3   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1191          -0.12           0.00    1    1   -2   -1   -2    0    0    0    0    0    0    0    0    0
+ 1192          -0.12           0.00    1    0    2   -2    2    0   -3    3    0    0    0    0    0    0
+ 1193           0.00           0.12    1    0    1    1    1    0    0    1    0    0    0    0    0    0
+ 1194           0.12           0.00    1    0    0    0    0    0    0    1    0   -1    0    0    0    0
+ 1195           0.12           0.00    1    0    0    0    0    0    0   -2    0    3    0    0    0    0
+ 1196          -0.12           0.00    1    0    0   -2    0    0    0    2    0   -2    0    0    0    0
+ 1197           0.12           0.00    1    0   -1    0   -1    0   -3    5    0    0    0    0    0    0
+ 1198          -0.12           0.00    1    0   -1   -1    0    0    0    8  -15    0    0    0    0    0
+ 1199           0.00          -0.12    1    0   -1   -2   -1    0    0    0    0    0    0    0    0    0
+ 1200          -0.12           0.00    1    0   -2   -2   -2    0    0    1    0   -1    0    0    0    0
+ 1201           0.12           0.00    0    0    2    2    2    0    0    2    0   -2    0    0    0    0
+ 1202          -0.12           0.00    0    0    2   -2    1    0    0   -2    0    1    0    0    0    0
+ 1203          -0.12           0.00    0    0    2   -2    1    0    0   -2    0    0    0    0    0    0
+ 1204           0.12           0.00    0    0    2   -2    1    0    0   -4    4    0    0    0    0    0
+ 1205           0.00           0.12    0    0    2   -2    1    0    0   -7    9    0    0    0    0    0
+ 1206          -0.12           0.00    0    0    2   -2    1    0    0  -10   15    0    0    0    0    0
+ 1207           0.12           0.00    0    0    1   -1    1    0    0    1   -4    0    0    0    0    0
+ 1208           0.00           0.12    0    0    1   -1    1    0    0   -1    0    1   -3    0    0    0
+ 1209           0.00           0.12    0    0    1   -1    1    0   -1    2    0    0    0    0    0    0
+ 1210           0.12           0.00    0    0    1   -1    1    0   -4    6    0    0    0    0    0    0
+ 1211          -0.12           0.00    0    0    0    2    0    0    0    2    0   -2    0    0    0    0
+ 1212          -0.12           0.00    0    0    0    2    0    0   -2    2    0    0    0    0    0    0
+ 1213           0.12           0.00    0    0    0    0    0    0    9  -13    0    0    0    0    0   -2
+ 1214          -0.12           0.00    0    0    0    0    0    0    8  -11    0    0    0    0    0   -1
+ 1215           0.12           0.00    0    0    0    0    0    0    8  -14    0    0    0    0    0   -2
+ 1216           0.00          -0.12    0    0    0    0    0    0    7  -11    0    0    0    0    0   -1
+ 1217           0.00          -0.12    0    0    0    0    0    0    6   -4    0    0    0    0    0    1
+ 1218           0.00          -0.12    0    0    0    0    0    0    6   -6    0    0    0    0    0    1
+ 1219          -0.12           0.00    0    0    0    0    0    0    6   -7    0    0    0    0    0   -1
+ 1220          -0.12           0.00    0    0    0    0    0    0    6   -8    0    0    0    0    0    0
+ 1221           0.00          -0.12    0    0    0    0    0    0    6   -9    0    0    0    0    0    0
+ 1222           0.00           0.12    0    0    0    0    0    0    5   -4    0    0    0    0    0    2
+ 1223          -0.12           0.00    0    0    0    0    0    0    5   -6    0    0    0    0    0   -1
+ 1224           0.00          -0.12    0    0    0    0    0    0    5   -6    0    0    0    0    0   -2
+ 1225          -0.12           0.00    0    0    0    0    0    0    5   -6   -4    0    0    0    0   -2
+ 1226           0.00           0.12    0    0    0    0    0    0    4   -2    0    0    0    0    0    0
+ 1227           0.00          -0.12    0    0    0    0    0    0    4   -5    0    0    0    0    0    0
+ 1228           0.12           0.00    0    0    0    0    0    0    4   -8    0    0    0    0    0   -2
+ 1229          -0.12           0.00    0    0    0    0    0    0    3   -1    0    0    0    0    0    0
+ 1230          -0.12           0.00    0    0    0    0    0    0    3   -3    0    2    0    0    0    2
+ 1231           0.12           0.00    0    0    0    0    0    0    3   -4    0    0    0    0    0    1
+ 1232           0.00           0.12    0    0    0    0    0    0    2    1    0    0    0    0    0    1
+ 1233          -0.12           0.00    0    0    0    0    0    0    2    0    0    0    0    0    0    0
+ 1234           0.12           0.00    0    0    0    0    0    0    1   -1    0    0    0    0    0   -2
+ 1235           0.12           0.00    0    0    0    0    0    0    1   -4    0    0    0    0    0   -1
+ 1236          -0.12           0.00    0    0    0    0    0    0    0    9  -17    0    0    0    0   -2
+ 1237           0.00           0.12    0    0    0    0    0    0    0    7   -7    0    0    0    0    2
+ 1238           0.00           0.12    0    0    0    0    0    0    0    7  -12    0    0    0    0   -2
+ 1239          -0.12           0.00    0    0    0    0    0    0    0    6   -4    0    0    0    0    2
+ 1240           0.00          -0.12    0    0    0    0    0    0    0    6   -8    1    5    0    0    2
+ 1241          -0.12           0.00    0    0    0    0    0    0    0    6   -9    0    0    0    0   -2
+ 1242           0.00           0.12    0    0    0    0    0    0    0    6  -10    0    0    0    0    0
+ 1243          -0.12           0.00    0    0    0    0    0    0    0    5    0   -4    0    0    0    2
+ 1244           0.12           0.00    0    0    0    0    0    0    0    5   -6    0    0    0    0    0
+ 1245          -0.12           0.00    0    0    0    0    0    0    0    5   -7    0    0    0    0   -2
+ 1246          -0.12           0.00    0    0    0    0    0    0    0    5   -8    3    0    0    0    2
+ 1247          -0.12           0.00    0    0    0    0    0    0    0    5   -9    0    0    0    0   -1
+ 1248          -0.12           0.00    0    0    0    0    0    0    0    5  -13    0    0    0    0   -2
+ 1249           0.12           0.00    0    0    0    0    0    0    0    5  -16    4    5    0    0   -2
+ 1250          -0.12           0.00    0    0    0    0    0    0    0    4   -7    0    0    0    0   -1
+ 1251           0.12           0.00    0    0    0    0    0    0    0    4   -8    3    0    0    0    1
+ 1252           0.12           0.00    0    0    0    0    0    0    0    4   -8    3    0    0    0   -1
+ 1253          -0.12           0.00    0    0    0    0    0    0    0    3    0   -5    0    0    0   -2
+ 1254           0.00          -0.12    0    0    0    0    0    0    0    3   -5    0    0    0    0   -1
+ 1255           0.00          -0.12    0    0    0    0    0    0    0    3   -7    0    0    0    0   -2
+ 1256           0.00          -0.12    0    0    0    0    0    0    0    3   -9    0    0    0    0   -2
+ 1257           0.12           0.00    0    0    0    0    0    0    0    2    1    0    0    0    0    2
+ 1258           0.12           0.00    0    0    0    0    0    0    0    2    0    2    0    0    0    2
+ 1259           0.00           0.12    0    0    0    0    0    0    0    2    0    0   -3    0    0    0
+ 1260           0.00          -0.12    0    0    0    0    0    0    0    2   -8    1    5    0    0   -2
+ 1261           0.00           0.12    0    0    0    0    0    0    0    1    0    1    0    0    0    1
+ 1262          -0.12           0.00    0    0    0    0    0    0    0    1    0    1   -5    0    0    0
+ 1263           0.00          -0.12    0    0    0    0    0    0    0    1    0    0    2    0    0    2
+ 1264          -0.12           0.00    0    0    0    0    0    0    0    1    0    0   -3    0    0    0
+ 1265           0.12           0.00    0    0    0    0    0    0    0    1    0   -3    5    0    0    0
+ 1266           0.00           0.12    0    0    0    0    0    0    0    1   -3    0    0    0    0    0
+ 1267           0.12           0.00    0    0    0    0    0    0    0    0    0    2   -6    3    0   -2
+ 1268           0.00           0.12    0    0    0    0    0    0    0    0    0    1   -2    0    0    0
+ 1269           0.00           0.12    0    0    0    0    0    0    0    0    0    0    0    1    0    0
+ 1270           0.12           0.00    0    0    0    0    0    0    0    0    0    0    0    0    0    2
+ 1271          -0.12           0.00    1    1    0   -6    0    0    0    0    0    0    0    0    0    0
+ 1272          -0.12           0.00    1    0    0    3    0    0    0    0    0    0    0    0    0    0
+ 1273           0.12           0.00    2    0    0   -2    0    0    0   -2    0    0    5    0    0    0
+ 1274          -0.12           0.00    3    0   -2   -1   -1    0    0    0    0    0    0    0    0    0
+ 1275          -0.12           0.00    1    2    2   -4    1    0    0    0    0    0    0    0    0    0
+ 1276          -0.12           0.00    0    0    1   -1    0    0   -2    2    0    0    0    0    0    0
+ 1277           0.00           0.12    1    0   -1    1    0    0    0    1    0    0    0    0    0    0
+ 1278           0.12           0.00    0    0    2   -2    1    0    1   -1    0    0    0    0    0    0
+ 1279          -0.12           0.00    2    0    2   -6    1    0    0    0    0    0    0    0    0    0
+ 1280          -0.12           0.00    2    1    2   -2    0    0    0    0    0    0    0    0    0    0
+ 1281          -0.12           0.00    2    1    2    0    0    0    0    0    0    0    0    0    0    0
+ 1282          -0.12           0.00    1    0    2    1    0    0    0    0    0    0    0    0    0    0
+ 1283           0.12           0.00    4    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1284          -0.12           0.00    0    0    0    0    1    0    0    7  -13    0    0    0    0    0
+ 1285           0.12           0.00    2    1    0    2    1    0    0    0    0    0    0    0    0    0
+ 1286          -0.12           0.00    1   -1    0    4    1    0    0    0    0    0    0    0    0    0
+ 1287          -0.12           0.00    3    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+ 1288           0.12           0.00    2    0    0   -6   -1    0    0    0    0    0    0    0    0    0
+ 1289           0.12           0.00    0    0    1   -1   -1    0   -5    7    0    0    0    0    0    0
+ 1290           0.12           0.00    0    0    0    0    2    0   -3    5    0    0    0    0    0    0
+ 1291           0.00          -0.12    0    0    1    1    2    0    0    1    0    0    0    0    0    0
+ 1292           0.00          -0.12    0    0    1   -1    2    0   -3    4    0    0    0    0    0    0
+ 1293           0.00           0.12    0    0    0    0    1    0   -1    2    0    0    0    0    0    0
+ 1294           0.12           0.00    2   -1    0   -2    2    0    0    0    0    0    0    0    0    0
+ 1295           0.12           0.00    1    1   -2    1   -2    0    0    0    0    0    0    0    0    0
+ 1296           0.12           0.00    2    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1297          -0.12           0.00    0    0    1   -1    2    0    0   -1    0    0   -1    0    0    0
+ 1298          -0.12           0.00    2    1    2   -4    2    0    0    0    0    0    0    0    0    0
+ 1299           0.12           0.00    2    0    0   -4    2    0    0    0    0    0    0    0    0    0
+ 1300          -0.12           0.00    3    0    0    0   -2    0    0    0    0    0    0    0    0    0
+ 1301           0.12           0.00    1    0    0   -4    2    0    0    0    0    0    0    0    0    0
+ 1302           0.12           0.00    2    0    0   -4   -2    0    0    0    0    0    0    0    0    0
+ 1303           0.12           0.00    1    1    0    0   -2    0    0    0    0    0    0    0    0    0
+ 1304          -0.12           0.00    0    0    0    0    1    0    0    2    0   -2    0    0    0    0
+ 1305          -0.12           0.00    1    0   -2    4   -2    0    0    0    0    0    0    0    0    0
+ 1306           0.11           0.00    0    0    4   -4    4    0    0    0    0    0    0    0    0    0
+ 
+j = 1  Nb of terms = 253
+
+ 1307       -3328.48      205833.15    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+ 1308         197.53       12814.01    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1309          41.19        2187.91    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1310         -35.85       -2004.36    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+ 1311          59.15         501.82    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1312          -5.82         448.76    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1313        -179.56         164.33    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+ 1314           5.67         288.49    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1315          23.85        -214.50    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+ 1316           2.87        -154.91    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1317           2.14        -119.21    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1318           1.17         -74.33    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+ 1319           1.47          70.31    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1320          -0.42          58.94    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1321          -0.95          57.12    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1322          -1.08         -54.19    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1323           0.92          36.78    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+ 1324           0.68         -31.01    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+ 1325           0.74          29.60    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1326          -0.60         -27.59    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1327         -11.11         -15.07    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+ 1328          -0.40         -24.05    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1329          -0.81          19.06    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+ 1330           3.18          15.32    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1331          -0.08         -17.90    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1332          -0.16          15.55    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1333          -0.77          14.40    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1334          -0.25          11.67    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1335           6.18           3.58    0    0    1   -1    1    0    0   -1    0   -2    5    0    0    0
+ 1336          -1.00          -7.27    0    1    2    0    2    0    0    0    0    0    0    0    0    0
+ 1337          -0.99           6.87    0    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1338          -0.27           7.49    0    0    2    2    1    0    0    0    0    0    0    0    0    0
+ 1339          -0.30           7.31    0    0    0    2    1    0    0    0    0    0    0    0    0    0
+ 1340           0.20           7.30    1    0    2    2    2    0    0    0    0    0    0    0    0    0
+ 1341           0.33           6.80    2    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1342           0.27          -6.81    1    0    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1343           0.35           6.08    0    0    0    2   -1    0    0    0    0    0    0    0    0    0
+ 1344           0.35           6.09    0    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+ 1345          -0.14          -6.19    2    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1346           0.14           6.02    2    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1347           2.69          -2.76    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1348          -0.08          -4.93    2    0    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1349           2.85          -1.77    0    0    0    0    0    0    0    0    0    2   -5    0    0   -1
+ 1350          -0.07          -4.27    0    1    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1351          -3.71           0.38    0    2    0    0    0    0    0    0    0    0    0    0    0    0
+ 1352           3.75           0.04    0    1   -1    1   -1    0    0    0    0    0    0    0    0    0
+ 1353          -0.82          -2.73    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+ 1354          -0.06           2.93    2    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1355          -0.04           2.83    2    0    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1356           0.08           2.75    3    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1357           0.07           2.75    1   -1    2    0    2    0    0    0    0    0    0    0    0    0
+ 1358          -0.07           2.70    1    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1359          -0.07           2.52    0    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1360          -0.05          -2.53    2    0    0    0    1    0    0    0    0    0    0    0    0    0
+ 1361          -0.04           2.40    1    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+ 1362          -0.06          -2.37    1    1    2    0    2    0    0    0    0    0    0    0    0    0
+ 1363           0.69          -1.45    0    0    1   -1    1    0   -8   12    0    0    0    0    0    0
+ 1364          -0.04           2.00    0    0    0    0    0    0    3   -5    0    0    0    0    0   -2
+ 1365           1.99           0.02    1    0   -1    0   -1    0    0    0    0    0    0    0    0    0
+ 1366          -0.94           1.07    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1367           0.04           1.91    1    0    0    0    2    0    0    0    0    0    0    0    0    0
+ 1368          -0.58          -1.36    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+ 1369          -0.51          -1.25    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+ 1370          -0.04          -1.59    0    0    2    1    2    0    0    0    0    0    0    0    0    0
+ 1371           0.39          -1.23    1    0    0   -1    0    0    0    0    0    0    0    0    0    0
+ 1372           0.03          -1.57    1   -1    0   -1   -1    0    0    0    0    0    0    0    0    0
+ 1373          -0.03           1.50    0    2   -2    2   -1    0    0    0    0    0    0    0    0    0
+ 1374           0.04           1.48    1    0    2    2    1    0    0    0    0    0    0    0    0    0
+ 1375          -0.04           1.45    1    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+ 1376           0.02          -1.36    1    0    0    0   -2    0    0    0    0    0    0    0    0    0
+ 1377           0.03          -1.32    2    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1378          -0.03          -1.24    1    1    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1379          -0.02          -1.18    2    0    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1380          -0.03           1.16    2    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+ 1381           0.02           1.13    0    0    0    0    0    0    0    0    0    2    0    0    0    2
+ 1382           0.04          -1.11    1    0    0   -1   -1    0    0    0    0    0    0    0    0    0
+ 1383           0.02           1.11    1    0    0    2    1    0    0    0    0    0    0    0    0    0
+ 1384           0.03          -1.10    1    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+ 1385           0.03           1.04    2    0    2    2    2    0    0    0    0    0    0    0    0    0
+ 1386          -0.51           0.56    0    0    0    0    1    0    0   -1    2    0    0    0    0    0
+ 1387           0.02          -0.98    0    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1388          -0.02          -0.94    0    1    2    0    1    0    0    0    0    0    0    0    0    0
+ 1389          -0.02          -0.89    3    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1390          -0.02          -0.88    0    0    4   -2    2    0    0    0    0    0    0    0    0    0
+ 1391           0.31           0.60    0    0    0    0    0    0    8  -13    0    0    0    0    0   -1
+ 1392           0.02          -0.87    1    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1393          -0.02          -0.87    2    0   -2    0    1    0    0    0    0    0    0    0    0    0
+ 1394          -0.01           0.83    0    0    2    0   -1    0    0    0    0    0    0    0    0    0
+ 1395          -0.02           0.77    0    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1396           0.42          -0.36    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+ 1397          -0.01          -0.73    0    1    0    0    2    0    0    0    0    0    0    0    0    0
+ 1398           0.01           0.71    1    1    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1399           0.01           0.68    0    0    2   -1    2    0    0    0    0    0    0    0    0    0
+ 1400           0.02           0.66    0    0    2    4    2    0    0    0    0    0    0    0    0    0
+ 1401          -0.01          -0.62    1   -1    0    0    1    0    0    0    0    0    0    0    0    0
+ 1402          -0.01           0.62    1    0    0    2   -1    0    0    0    0    0    0    0    0    0
+ 1403          -0.58          -0.03    0    0    1   -1    1    0    0   -1    0    2   -5    0    0    0
+ 1404          -0.01           0.58    1   -1    0   -1   -2    0    0    0    0    0    0    0    0    0
+ 1405           0.44           0.14    0    0    0    0    0    0    0    2   -8    3    0    0    0   -2
+ 1406           0.02           0.56    1   -1    2    2    2    0    0    0    0    0    0    0    0    0
+ 1407           0.01          -0.57    0    1    0    0   -2    0    0    0    0    0    0    0    0    0
+ 1408          -0.13          -0.45    1    0    0    2    0    0    0    0    0    0    0    0    0    0
+ 1409           0.01           0.56    3    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1410           0.01          -0.55    1   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1411           0.01           0.55    1    0    2   -4    1    0    0    0    0    0    0    0    0    0
+ 1412          -0.52           0.03    0    0    2   -2    1    0   -5    6    0    0    0    0    0    0
+ 1413          -0.01           0.54    1    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1414          -0.01          -0.51    0    1    2    2    2    0    0    0    0    0    0    0    0    0
+ 1415          -0.41          -0.11    0    0    0    0    0    0    0    6   -8    3    0    0    0    2
+ 1416          -0.01           0.50    0    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1417           0.01           0.48    1   -1    2    0    1    0    0    0    0    0    0    0    0    0
+ 1418           0.45          -0.04    1    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+ 1419           0.01          -0.48    0    0    0    0    0    0    4   -6    0    0    0    0    0   -2
+ 1420           0.01           0.46    2   -1    2    0    2    0    0    0    0    0    0    0    0    0
+ 1421          -0.23           0.24    2    0   -2    0    0    0    0    0    0    0    0    0    0    0
+ 1422           0.01           0.46    0    0    0    1    1    0    0    0    0    0    0    0    0    0
+ 1423           0.35          -0.11    0    0    0    0    1    0    0   -4    8   -3    0    0    0    0
+ 1424           0.01           0.45    0    0    0    2    2    0    0    0    0    0    0    0    0    0
+ 1425           0.01          -0.45    1   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1426           0.00          -0.45    0    0    0    0    0    0    0    3    0   -1    0    0    0    2
+ 1427          -0.01           0.44    0    0    0    0    0    0    2   -4    0    0    0    0    0   -2
+ 1428           0.35           0.09    0    0    0    0    1    0    0    4   -8    3    0    0    0    0
+ 1429           0.01           0.42    1    1    0    0    1    0    0    0    0    0    0    0    0    0
+ 1430          -0.01          -0.41    1    1    2    0    1    0    0    0    0    0    0    0    0    0
+ 1431           0.09          -0.33    0    0    1   -1    1    0    0    0   -2    0    0    0    0    0
+ 1432           0.00           0.41    0    1    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1433           0.01           0.40    0    3    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1434          -0.01          -0.39    2    1    2    0    2    0    0    0    0    0    0    0    0    0
+ 1435          -0.39          -0.01    1    0   -1    0   -2    0    0    0    0    0    0    0    0    0
+ 1436           0.01          -0.39    0    0    0    1   -1    0    0    0    0    0    0    0    0    0
+ 1437          -0.01           0.38    1    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+ 1438           0.32          -0.07    0    0    0    0    0    0    0    0    0    2    0    0    0    1
+ 1439          -0.01           0.36    1   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1440          -0.01          -0.36    0    0    0    0    0    0    2    0    0    0    0    0    0    2
+ 1441           0.01          -0.34    1    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+ 1442           0.01          -0.34    2    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+ 1443           0.01           0.33    0    0    0    0    0    0    1    1    0    0    0    0    0    2
+ 1444          -0.01          -0.32    1    0    2    1    2    0    0    0    0    0    0    0    0    0
+ 1445           0.01           0.32    1    0    2   -1    2    0    0    0    0    0    0    0    0    0
+ 1446          -0.01          -0.32    0    0    2    1    1    0    0    0    0    0    0    0    0    0
+ 1447          -0.01          -0.31    1    0   -2    0    1    0    0    0    0    0    0    0    0    0
+ 1448          -0.31           0.00    0    0    1    0    1    0    0    0    0    0    0    0    0    0
+ 1449          -0.07          -0.24    1    0    2    0    0    0    0    0    0    0    0    0    0    0
+ 1450           0.10          -0.21    0    0    1   -1    1    0    0   -1    0    0   -1    0    0    0
+ 1451          -0.01          -0.30    1    0    0   -2    2    0    0    0    0    0    0    0    0    0
+ 1452          -0.01           0.29    1    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+ 1453          -0.01          -0.29    1    1    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1454           0.00           0.29    1    0   -2    1   -1    0    0    0    0    0    0    0    0    0
+ 1455           0.23           0.06    0    0    1   -1    1    0    0   -1    0   -1    0    0    0    0
+ 1456           0.26           0.02    0    0    0    0    0    0    0    0    0    2   -5    0    0    1
+ 1457           0.00          -0.27    2    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1458           0.25           0.02    0    0    0    0    0    0    5   -8    0    0    0    0    0   -2
+ 1459           0.09          -0.18    0    1    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1460           0.01           0.25    0    0    0    0    0    0    0    1    0    1    0    0    0    2
+ 1461           0.14          -0.11    1    1    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1462           0.00          -0.25    1    1    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1463           0.01           0.24    4    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1464          -0.01          -0.24    2    1    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1465           0.00           0.23    2    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+ 1466           0.01           0.23    0    1    2    1    2    0    0    0    0    0    0    0    0    0
+ 1467          -0.01          -0.23    1    0    4   -2    2    0    0    0    0    0    0    0    0    0
+ 1468           0.00          -0.23    0    1    0    2    1    0    0    0    0    0    0    0    0    0
+ 1469           0.00          -0.22    1    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+ 1470           0.00           0.21    1    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+ 1471           0.01           0.21    2    0    2    2    1    0    0    0    0    0    0    0    0    0
+ 1472          -0.17           0.03    0    0    1   -1    1    0    0   -1    0    2    0    0    0    0
+ 1473          -0.17           0.03    0    0    0    0    0    0    8  -13    0    0    0    0    0   -2
+ 1474           0.00          -0.19    0    0    0    0    0    0    5   -7    0    0    0    0    0   -2
+ 1475           0.14          -0.06    0    0    0    0    0    0    0    0    0    0    2    0    0    1
+ 1476           0.03          -0.17    0    0    1   -1    1    0   -5    7    0    0    0    0    0    0
+ 1477          -0.13           0.06    0    0    1   -1    1    0    0   -1    0    0    2    0    0    0
+ 1478           0.00           0.19    0    0    2   -4    1    0    0    0    0    0    0    0    0    0
+ 1479           0.00           0.19    0    0    2   -3    2    0    0    0    0    0    0    0    0    0
+ 1480          -0.06          -0.13    1    0    2    0    2    0    0    1    0    0    0    0    0    0
+ 1481           0.00           0.18    0    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1482          -0.09          -0.09    1   -1    0    0    0    0    0    0    0    0    0    0    0    0
+ 1483           0.10          -0.09    0    0    0    0    0    0    0    8  -16    4    5    0    0   -2
+ 1484           0.06           0.12    1    0   -2    0   -2    0    0    4   -8    3    0    0    0    0
+ 1485           0.00           0.18    0    0    0    0    0    0    0    4    0   -2    0    0    0    2
+ 1486           0.00          -0.18    0    0    4    0    2    0    0    0    0    0    0    0    0    0
+ 1487           0.00           0.17    0    0    0    4    1    0    0    0    0    0    0    0    0    0
+ 1488          -0.03           0.15    0    0    0    0    0    0    0    2    0   -1    0    0    0    2
+ 1489          -0.01          -0.16    0    0    0    0    0    0    0    2    0    1    0    0    0    2
+ 1490           0.00           0.17    1    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+ 1491           0.00          -0.17    2    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+ 1492           0.11           0.06    0    0    0    0    0    0    8  -11    0    0    0    0    0   -2
+ 1493           0.00          -0.17    3    0    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1494          -0.08           0.09    1    0   -2    0    0    0    0    0    0    0    0    0    0    0
+ 1495          -0.17           0.00    0    0    0    0    0    0    2   -1    0    0    0    0    0    2
+ 1496           0.00          -0.16    0    0    4   -2    1    0    0    0    0    0    0    0    0    0
+ 1497           0.01           0.15    1    0    2    4    2    0    0    0    0    0    0    0    0    0
+ 1498          -0.13          -0.03    0    0    0    0    0    0    1   -1    0    0    0    0    0   -1
+ 1499           0.00           0.15    1    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1500           0.00           0.15    2    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1501          -0.13           0.03    0    0    0    0    0    0    4   -6    0    0    0    0    0   -1
+ 1502           0.10          -0.06    0    0    0    0    0    0    8  -15    0    0    0    0    0   -2
+ 1503          -0.07           0.08    0    0    0    0    0    0    0    1    2    0    0    0    0    2
+ 1504          -0.09          -0.06    0    0    0    0    0    0    0    3    0   -2    0    0    0    2
+ 1505           0.00           0.15    3    0    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1506          -0.07          -0.08    0    0    0    0    0    0    0    3   -2    0    0    0    0    2
+ 1507           0.00          -0.14    2    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+ 1508           0.02           0.12    0    0    0    0    0    0    0    0    0    3    0    0    0    2
+ 1509           0.07           0.08    1    1    0    0    0    0    0    0    0    0    0    0    0    0
+ 1510          -0.03          -0.11    3    0    0    0    0    0    0    0    0    0    0    0    0    0
+ 1511          -0.01          -0.14    0    0    0    0    0    0    0    4   -2    0    0    0    0    2
+ 1512           0.00          -0.14    0    0    2   -2    1    0   -3    3    0    0    0    0    0    0
+ 1513           0.02          -0.12    1    0    0   -1    1    0    0    0    0    0    0    0    0    0
+ 1514           0.00          -0.14    1    1    2    2    2    0    0    0    0    0    0    0    0    0
+ 1515           0.00           0.14    0    0    1   -1    1    0    0    3   -8    3    0    0    0    0
+ 1516           0.00           0.14    0    0    2   -1    1    0    0    0    0    0    0    0    0    0
+ 1517           0.00           0.13    0    0    2    4    1    0    0    0    0    0    0    0    0    0
+ 1518           0.08          -0.06    0    0    0    0    1    0    8  -13    0    0    0    0    0    0
+ 1519           0.00           0.13    1   -1    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1520           0.00           0.13    0    0    0    0    0    0    0    0    0    0    2    0    0    2
+ 1521           0.01           0.13    3    0    2    2    2    0    0    0    0    0    0    0    0    0
+ 1522           0.00           0.13    2    0    0    2    1    0    0    0    0    0    0    0    0    0
+ 1523           0.00           0.13    1   -1   -2    2   -1    0    0    0    0    0    0    0    0    0
+ 1524          -0.02          -0.11    2    0    0   -2    1    0    0   -2    0    3    0    0    0    0
+ 1525           0.08          -0.04    0    0    0    1    0    0    0    0    0    0    0    0    0    0
+ 1526           0.00           0.13    2    0    0   -4    1    0    0    0    0    0    0    0    0    0
+ 1527           0.00           0.13    1    0    0   -4    1    0    0    0    0    0    0    0    0    0
+ 1528           0.01          -0.12    2    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1529           0.00           0.12    0    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1530          -0.02          -0.11    0    0    0    0    0    0    5   -8    0    0    0    0    0   -1
+ 1531           0.00          -0.12    2    0    2   -4    1    0    0    0    0    0    0    0    0    0
+ 1532           0.00          -0.12    0    0    0    0    0    0    0    8  -16    4    5    0    0    2
+ 1533           0.00          -0.12    0    0    1   -1    1    0    0   -5    8   -3    0    0    0    0
+ 1534           0.04           0.08    1    0    0    0   -1    0  -18   16    0    0    0    0    0    0
+ 1535           0.00          -0.12    0    0    0    0    0    0    0    1    0   -3    0    0    0   -2
+ 1536           0.00           0.12    1   -1    0   -2   -2    0    0    0    0    0    0    0    0    0
+ 1537           0.00          -0.12    0    0    0    2   -2    0    0    0    0    0    0    0    0    0
+ 1538           0.00          -0.11    0    0    0    0    0    0    6   -8    0    0    0    0    0   -2
+ 1539           0.03          -0.09    1    0    0   -4    0    0    0    0    0    0    0    0    0    0
+ 1540           0.00           0.11    0    2    0    0    1    0    0    0    0    0    0    0    0    0
+ 1541          -0.11           0.00    0    0    0    0    0    0    3   -2    0    0    0    0    0    2
+ 1542           0.00           0.11    1   -1    2    2    1    0    0    0    0    0    0    0    0    0
+ 1543           0.00          -0.11    1   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1544           0.07           0.05    0    0    0    0    1    0   -8   13    0    0    0    0    0    0
+ 1545           0.11           0.00    0    0    0    0    0    0    2   -5    0    0    0    0    0   -2
+ 1546           0.00          -0.11    0    0    0    0    0    0    1   -3    0    0    0    0    0   -2
+ 1547           0.00          -0.11    4    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1548           0.02          -0.09    0    0    0    0    1    0    0    0    0   -2    5    0    0    0
+ 1549           0.00           0.11    2    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+ 1550           0.02           0.09    0    0    0    0    1    0    0    0    0    2   -5    0    0    0
+ 1551           0.00          -0.11    0    2    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1552           0.00           0.11    1    0    2    0   -1    0    0    0    0    0    0    0    0    0
+ 1553          -0.08          -0.02    0    0    0    0    1    0    0    0    0    1    0    0    0    0
+ 1554           0.00          -0.10    2    1    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1555           0.00          -0.10    2   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1556          -0.03          -0.07    1    0    0    0    1    0  -18   16    0    0    0    0    0    0
+ 1557           0.00           0.10    1    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1558           0.00           0.10    2    0    0   -2    2    0    0    0    0    0    0    0    0    0
+ 1559           0.00          -0.10    1   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+
+j = 2  Nb of terms = 36
+
+ 1560        2038.00          82.26    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+ 1561         155.75          -2.70    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1562          26.92          -0.45    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1563         -24.43           0.46    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+ 1564         -17.36          -0.50    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+ 1565          -8.41           0.01    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+ 1566           6.08          -1.36    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1567           4.59           0.17    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1568           3.57          -0.06    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1569           2.54           0.60    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+ 1570           1.86           0.00    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1571          -1.52          -0.07    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1572           1.46           0.04    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1573          -0.75          -0.02    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+ 1574          -0.75           0.00    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+ 1575          -0.71          -0.01    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1576          -0.69           0.02    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1577           0.61           0.02    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1578           0.54          -0.04    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1579          -0.56           0.00    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1580           0.46          -0.02    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+ 1581           0.38          -0.01    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+ 1582           0.37          -0.02    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1583          -0.34           0.01    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1584          -0.35           0.00    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+ 1585          -0.31           0.00    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+ 1586           0.19          -0.09    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1587           0.26           0.00    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+ 1588           0.24          -0.01    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1589          -0.20           0.00    0    2    0    0    0    0    0    0    0    0    0    0    0    0
+ 1590           0.18          -0.01    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1591           0.17           0.00    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+ 1592           0.15           0.01    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1593          -0.15           0.00    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1594          -0.13           0.00    2    0   -2    0    0    0    0    0    0    0    0    0    0    0
+ 1595          -0.12           0.00    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+ 
+j = 3  Nb of terms = 4
+
+ 1596           1.76         -20.39    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+ 1597           0.00          -1.27    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1598           0.00          -0.22    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1599           0.00           0.20    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+
+j = 4  Nb of terms = 1
+      
+ 1600          -0.10          -0.02    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2b.coeff
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2b.coeff	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2b.coeff	(revision 22322)
@@ -0,0 +1,10 @@
+# Expression for the Y coordinate of the CIP in the GCRS based on the IAU2000A 
+# precession-nutation model 
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# Y = polynomial part + non-polynomial part
+# 
+# --------------------------------------------------------------------------------------------------------------
+# Polynomial part (unit microarcsecond)
+-6950.78 -25381.99 -22407250.99 +1842.28 +1113.06 +0.99
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2b.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2b.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2b.dat	(revision 22322)
@@ -0,0 +1,1304 @@
+# Expression for the Y coordinate of the CIP in the GCRS based on the IAU2000A 
+# precession-nutation model 
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# Y = polynomial part + non-polynomial part
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# Non-polynomial part (unit microarcsecond)
+# (ARG being for various combination of the fundamental arguments of the nutation theory)
+# 
+#   Sum_i[b_{c,0})_i * cos(ARG) + b_{s,0})_i * sin(ARG)] 
+# 
+# + Sum_i)j=1,4 [b_{c,j})_i * t^j * cos(ARG) + b_{s,j})_i * sin(ARG)] * t^j]
+# 
+# The Table below provides the values for b_{s,j})_i and b_{c,j})_i
+# 
+# The expressions for the fundamental arguments appearing in columns 4 to 8 (luni-solar part) 
+# and in columns 6 to 17 (planetary part) are those of the IERS Conventions 2000
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+#     i    b_{s,j})_i      b_{c,j})_i    l    l'   F    D   Om L_Me L_Ve  L_E L_Ma  L_J L_Sa  L_U L_Ne  p_A
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+0        1538.18     9205236.26    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+0        -458.66      573033.42    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0         137.41       97846.69    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+0         -29.05      -89618.24    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+0         -17.40       22438.42    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+0          31.80       20069.50    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+0          36.70       12902.66    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+0         -13.20       -9592.72    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+0        -192.40        7387.02    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+0           3.92       -6918.22    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.40       -5331.13    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+0          -0.90       -3323.89    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+0           7.50        3143.98    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+0           7.80        2636.13    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+0          -6.60        2554.51    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0          -2.00       -2423.59    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           6.80        1645.01    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.00       -1387.00    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+0           5.90        1323.81    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+0          -0.30       -1233.89    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.30       -1075.60    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+0          -4.48         852.85    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.10        -800.34    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          35.80        -674.99    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+0          -1.40         695.54    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.50         684.99    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -2.62         643.75    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+0          -1.50         522.11    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0         273.50         164.70    0    0    1   -1    1    0    0   -1    0   -2    5    0    0    0
+0           1.40         335.24    0    0    2    2    1    0    0    0    0    0    0    0    0    0
+0           1.90         326.60    1    0    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.40         327.11    0    0    0    2    1    0    0    0    0    0    0    0    0    0
+0          -0.50        -325.03    0    1    2    0    2    0    0    0    0    0    0    0    0    0
+0          -0.40         307.03    0    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+0           0.50         304.17    2    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          -0.10        -304.46    1    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.40        -276.81    2    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.90         272.05    0    0    0    2   -1    0    0    0    0    0    0    0    0    0
+0           0.30         272.22    0    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           1.20         269.45    2    0    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.10        -220.67    2    0    0   -2    1    0    0    0    0    0    0    0    0    0
+0         128.60         -77.10    0    0    0    0    0    0    0    0    0    2   -5    0    0   -1
+0           0.10        -190.79    0    1    2   -2    1    0    0    0    0    0    0    0    0    0
+0         167.90           0.00    0    1   -1    1   -1    0    0    0    0    0    0    0    0    0
+0          -8.20        -123.48    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.10         131.04    2    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+0           0.40         126.64    2    0    0    0   -1    0    0    0    0    0    0    0    0    0
+0           2.90        -122.28    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.70         123.20    3    0    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.40         123.20    1   -1    2    0    2    0    0    0    0    0    0    0    0    0
+0          -0.30         120.70    1    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0          -0.50         112.90    0    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0          -0.20        -112.94    2    0    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.20         107.31    1    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+0          -0.30        -106.20    1    1    2    0    2    0    0    0    0    0    0    0    0    0
+0          31.90         -64.10    0    0    1   -1    1    0   -8   12    0    0    0    0    0    0
+0           0.00          89.50    0    0    0    0    0    0    3   -5    0    0    0    0    0   -2
+0          89.10           0.00    1    0   -1    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00          85.32    1    0    0    0    2    0    0    0    0    0    0    0    0    0
+0          -0.20         -71.00    0    0    2    1    2    0    0    0    0    0    0    0    0    0
+0           0.00         -70.01    1   -1    0   -1   -1    0    0    0    0    0    0    0    0    0
+0          13.90         -55.30    1    0    0   -1    0    0    0    0    0    0    0    0    0    0
+0           0.00          67.25    0    2   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.40          66.29    1    0    2    2    1    0    0    0    0    0    0    0    0    0
+0          -0.40          64.70    1    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           1.30         -60.90    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+0          -0.20         -60.92    1    0    0    0   -2    0    0    0    0    0    0    0    0    0
+0           0.20         -59.20    2    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           1.10         -55.55    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+0           0.00         -55.60    1    1    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.10         -52.69    2    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.20          51.80    2    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           1.00         -49.51    1    0    0   -1   -1    0    0    0    0    0    0    0    0    0
+0           0.00          50.50    0    0    0    0    0    0    0    0    0    2    0    0    0    2
+0           2.50          47.70    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.10          49.59    1    0    0    2    1    0    0    0    0    0    0    0    0    0
+0           0.10         -49.00    1    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+0         -23.20          24.60    0    0    0    0    1    0    0   -1    2    0    0    0    0    0
+0           0.40          46.50    2    0    2    2    2    0    0    0    0    0    0    0    0    0
+0          -0.10         -44.04    0    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+0          -0.10         -42.19    0    1    2    0    1    0    0    0    0    0    0    0    0    0
+0          13.30          26.90    0    0    0    0    0    0    8  -13    0    0    0    0    0   -1
+0          -0.10         -39.90    3    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.10         -39.50    0    0    4   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00         -39.11    1    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          -0.10         -38.92    2    0   -2    0    1    0    0    0    0    0    0    0    0    0
+0           0.10          36.95    0    0    2    0   -1    0    0    0    0    0    0    0    0    0
+0          -0.10          34.59    0    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.20         -32.55    0    1    0    0    2    0    0    0    0    0    0    0    0    0
+0          -0.10          31.61    1    1    0   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          30.40    0    0    2   -1    2    0    0    0    0    0    0    0    0    0
+0           0.20          29.40    0    0    2    4    2    0    0    0    0    0    0    0    0    0
+0           0.00         -27.91    1   -1    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.10          27.50    1    0    0    2   -1    0    0    0    0    0    0    0    0    0
+0         -25.70          -1.70    0    0    1   -1    1    0    0   -1    0    2   -5    0    0    0
+0          19.90           5.90    0    0    0    0    0    0    0    2   -8    3    0    0    0   -2
+0           0.00          25.80    1   -1    0   -1   -2    0    0    0    0    0    0    0    0    0
+0           0.20          25.20    1   -1    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.00         -25.31    0    1    0    0   -2    0    0    0    0    0    0    0    0    0
+0           0.20          25.00    3    0    2    0    1    0    0    0    0    0    0    0    0    0
+0          -0.10          24.40    1    0    2   -4    1    0    0    0    0    0    0    0    0    0
+0           0.10         -24.40    1   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0         -23.30           0.90    0    0    2   -2    1    0   -5    6    0    0    0    0    0    0
+0          -0.10          24.00    1    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0         -18.00          -5.30    0    0    0    0    0    0    0    6   -8    3    0    0    0    2
+0          -0.10         -22.80    0    1    2    2    2    0    0    0    0    0    0    0    0    0
+0          -0.10          22.50    0    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.10          21.60    1   -1    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.00         -21.30    0    0    0    0    0    0    4   -6    0    0    0    0    0   -2
+0           0.10          20.70    2   -1    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.70         -20.10    0    0    0    0    0    0    0    3    0   -1    0    0    0    2
+0           0.00          20.51    0    0    0    1    1    0    0    0    0    0    0    0    0    0
+0          15.90          -4.50    0    0    0    0    1    0    0   -4    8   -3    0    0    0    0
+0           0.20         -19.94    1    0    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.00          20.11    0    0    0    2    2    0    0    0    0    0    0    0    0    0
+0          15.60           4.40    0    0    0    0    1    0    0    4   -8    3    0    0    0    0
+0           0.00         -20.00    1   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+0           0.00          19.80    0    0    0    0    0    0    2   -4    0    0    0    0    0   -2
+0           0.00          18.91    1    1    0    0    1    0    0    0    0    0    0    0    0    0
+0           4.30         -14.60    0    0    1   -1    1    0    0    0   -2    0    0    0    0    0
+0          -0.10         -18.50    1    1    2    0    1    0    0    0    0    0    0    0    0    0
+0          -0.10          18.40    0    1    0   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          18.10    0    3    2   -2    2    0    0    0    0    0    0    0    0    0
+0           1.00          16.81    0    2    0    0    0    0    0    0    0    0    0    0    0    0
+0          -0.10         -17.60    2    1    2    0    2    0    0    0    0    0    0    0    0    0
+0         -17.60           0.00    1    0   -1    0   -2    0    0    0    0    0    0    0    0    0
+0          -1.30         -16.26    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00         -17.41    0    0    0    1   -1    0    0    0    0    0    0    0    0    0
+0          14.50          -2.70    0    0    0    0    0    0    0    0    0    2    0    0    0    1
+0           0.00          17.08    1    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          16.21    1   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00         -16.00    0    0    0    0    0    0    2    0    0    0    0    0    0    2
+0           0.00         -15.31    1    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+0           0.00         -15.10    2    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+0           0.00          14.70    0    0    0    0    0    0    1    1    0    0    0    0    0    2
+0           0.00          14.40    1    0    2   -1    2    0    0    0    0    0    0    0    0    0
+0          -0.10         -14.30    1    0    2    1    2    0    0    0    0    0    0    0    0    0
+0           0.00         -14.40    0    0    2    1    1    0    0    0    0    0    0    0    0    0
+0           0.00         -13.81    1    0   -2    0    1    0    0    0    0    0    0    0    0    0
+0           4.50          -9.30    0    0    1   -1    1    0    0   -1    0    0   -1    0    0    0
+0         -13.80           0.00    0    0    1    0    1    0    0    0    0    0    0    0    0    0
+0           0.00         -13.38    1    0    0   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.10          13.10    1    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0          10.30           2.70    0    0    1   -1    1    0    0   -1    0   -1    0    0    0    0
+0           0.00          12.80    1    0   -2    1   -1    0    0    0    0    0    0    0    0    0
+0           0.00         -12.80    1    1    2   -2    1    0    0    0    0    0    0    0    0    0
+0          11.70           0.80    0    0    0    0    0    0    0    0    0    2   -5    0    0    1
+0           0.00         -12.00    2    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0          11.30           0.50    0    0    0    0    0    0    5   -8    0    0    0    0    0   -2
+0           0.00          11.40    0    0    0    0    0    0    0    1    0    1    0    0    0    2
+0           0.00         -11.20    1    1    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.10          10.90    4    0    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.10         -10.77    1    0    2    0    0    0    0    0    0    0    0    0    0    0
+0           0.00         -10.80    2    1    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.20          10.47    2    0   -2    0    0    0    0    0    0    0    0    0    0    0
+0           0.00          10.50    2    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00         -10.40    1    0    4   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00          10.40    0    1    2    1    2    0    0    0    0    0    0    0    0    0
+0           0.00         -10.20    0    1    0    2    1    0    0    0    0    0    0    0    0    0
+0           0.00         -10.00    1    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00           9.60    1    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.10           9.40    2    0    2    2    1    0    0    0    0    0    0    0    0    0
+0          -7.60           1.70    0    0    0    0    0    0    8  -13    0    0    0    0    0   -2
+0          -7.70           1.40    0    0    1   -1    1    0    0   -1    0    2    0    0    0    0
+0           1.40          -7.50    0    0    1   -1    1    0   -5    7    0    0    0    0    0    0
+0           6.10          -2.70    0    0    0    0    0    0    0    0    0    0    2    0    0    1
+0           0.00          -8.70    0    0    0    0    0    0    5   -7    0    0    0    0    0   -2
+0          -5.90           2.60    0    0    1   -1    1    0    0   -1    0    0    2    0    0    0
+0           0.00           8.40    0    0    2   -4    1    0    0    0    0    0    0    0    0    0
+0          -0.20          -8.11    0    1    0   -2    0    0    0    0    0    0    0    0    0    0
+0          -2.60          -5.70    1    0    2    0    2    0    0    1    0    0    0    0    0    0
+0           0.00           8.30    0    0    2   -3    2    0    0    0    0    0    0    0    0    0
+0           2.70           5.50    1    0   -2    0   -2    0    0    4   -8    3    0    0    0    0
+0           4.20          -4.00    0    0    0    0    0    0    0    8  -16    4    5    0    0   -2
+0          -0.10           8.00    0    0    0    0    0    0    0    4    0   -2    0    0    0    2
+0           0.00           8.09    0    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          -1.30           6.70    0    0    0    0    0    0    0    2    0   -1    0    0    0    2
+0           0.00          -7.90    0    0    4    0    2    0    0    0    0    0    0    0    0    0
+0           0.00           7.80    0    0    0    4    1    0    0    0    0    0    0    0    0    0
+0          -7.50          -0.20    0    0    0    0    0    0    2   -1    0    0    0    0    0    2
+0          -0.50          -7.20    0    0    0    0    0    0    0    2    0    1    0    0    0    2
+0           4.90           2.70    0    0    0    0    0    0    8  -11    0    0    0    0    0   -2
+0           0.00           7.50    1    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -7.50    3    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -7.49    2    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -7.20    0    0    4   -2    1    0    0    0    0    0    0    0    0    0
+0           0.10           6.90    1    0    2    4    2    0    0    0    0    0    0    0    0    0
+0          -5.60           1.40    0    0    0    0    0    0    4   -6    0    0    0    0    0   -1
+0          -5.70          -1.30    0    0    0    0    0    0    1   -1    0    0    0    0    0   -1
+0           0.00           6.90    2    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+0           4.20          -2.70    0    0    0    0    0    0    8  -15    0    0    0    0    0   -2
+0           0.00           6.90    1    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+0          -3.10           3.70    0    0    0    0    0    0    0    1    2    0    0    0    0    2
+0          -3.90          -2.90    0    0    0    0    0    0    0    3    0   -2    0    0    0    2
+0           0.00           6.60    3    0    0    0   -1    0    0    0    0    0    0    0    0    0
+0          -3.10          -3.50    0    0    0    0    0    0    0    3   -2    0    0    0    0    2
+0           1.10          -5.39    1    0    0   -1    1    0    0    0    0    0    0    0    0    0
+0           0.00          -6.40    2    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.90           5.50    0    0    0    0    0    0    0    0    0    3    0    0    0    2
+0           0.00          -6.30    0    0    2   -2    1    0   -3    3    0    0    0    0    0    0
+0          -0.10          -6.20    0    0    0    0    0    0    0    4   -2    0    0    0    0    2
+0           0.00          -6.10    1    1    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.00           6.10    0    0    1   -1    1    0    0    3   -8    3    0    0    0    0
+0           0.00           6.10    0    0    2   -1    1    0    0    0    0    0    0    0    0    0
+0           3.50          -2.50    0    0    0    0    1    0    8  -13    0    0    0    0    0    0
+0           0.00           6.00    0    0    2    4    1    0    0    0    0    0    0    0    0    0
+0           0.00           5.90    1   -1    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          -0.90          -4.80    2    0    0   -2    1    0    0   -2    0    3    0    0    0    0
+0           0.00           5.70    1   -1   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.10           5.60    3    0    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.00           5.70    0    0    0    0    0    0    0    0    0    0    2    0    0    2
+0           0.00           5.70    2    0    0    2    1    0    0    0    0    0    0    0    0    0
+0           0.00           5.60    2    0    0   -4    1    0    0    0    0    0    0    0    0    0
+0           0.00           5.60    1    0    0   -4    1    0    0    0    0    0    0    0    0    0
+0           0.20          -5.40    2    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+0          -0.90          -4.70    0    0    0    0    0    0    5   -8    0    0    0    0    0   -1
+0          -0.40          -5.10    1    1    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00           5.50    0    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -5.40    2    0    2   -4    1    0    0    0    0    0    0    0    0    0
+0           0.00          -5.40    0    0    0    0    0    0    0    8  -16    4    5    0    0    2
+0           1.80           3.60    1    0    0    0   -1    0  -18   16    0    0    0    0    0    0
+0           0.00           5.30    1   -1    0   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -5.30    0    0    1   -1    1    0    0   -5    8   -3    0    0    0    0
+0           0.00          -5.20    0    0    0    0    0    0    0    1    0   -3    0    0    0   -2
+0           0.00          -5.19    0    0    0    2   -2    0    0    0    0    0    0    0    0    0
+0           3.00           2.10    0    0    0    0    1    0   -8   13    0    0    0    0    0    0
+0           0.00          -5.10    0    0    0    0    0    0    6   -8    0    0    0    0    0   -2
+0           0.00           5.07    0    2    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.90          -4.10    0    0    0    0    1    0    0    0    0   -2    5    0    0    0
+0          -5.00           0.00    0    0    0    0    0    0    3   -2    0    0    0    0    0    2
+0           0.00          -5.00    3    0    0    0    0    0    0    0    0    0    0    0    0    0
+0           0.00           5.00    1   -1    2    2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -5.00    1   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -4.90    4    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0           4.90           0.00    0    0    0    0    0    0    2   -5    0    0    0    0    0   -2
+0           0.00          -4.90    0    0    0    0    0    0    1   -3    0    0    0    0    0   -2
+0           0.90           3.90    0    0    0    0    1    0    0    0    0    2   -5    0    0    0
+0           0.00           4.80    2    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0          -3.70          -1.10    0    0    0    0    1    0    0    0    0    1    0    0    0    0
+0           0.00          -4.72    0    2    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00           4.71    1    0    2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -4.50    2    1    0   -2    1    0    0    0    0    0    0    0    0    0
+0          -1.50          -3.00    1    0    0    0    1    0  -18   16    0    0    0    0    0    0
+0           0.00          -4.50    2   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.30          -4.11    1   -1    0    0    0    0    0    0    0    0    0    0    0    0
+0           0.00           4.40    1    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -4.40    1   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           4.39    2    0    0   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00          -4.30    3    0    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.00           4.30    2    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -4.30    0    1    2    2    1    0    0    0    0    0    0    0    0    0
+0           0.20           4.03    1    0   -2    0    0    0    0    0    0    0    0    0    0    0
+0           0.20           4.00    0    2   -2    0   -2    0    0    0    0    0    0    0    0    0
+0          -0.60           3.50    0    0    1   -1    1    0    0   -1    0    0    1    0    0    0
+0           0.00           4.10    3    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00           4.00    1    0   -4    2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -4.00    0    1   -2    1   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -3.91    1    0    0   -4    0    0    0    0    0    0    0    0    0    0
+0           1.90           2.00    0    0    0    0    1    0    0    1   -2    0    0    0    0    0
+0           0.00           3.90    2   -1    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.00           3.90    0    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -3.90    1    0   -2   -3   -2    0    0    0    0    0    0    0    0    0
+0           3.10          -0.80    0    0    0    0    0    0    2   -4    0    0    0    0    0   -1
+0           0.00           3.90    2   -1    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.00           3.90    2    0    0    0    2    0    0    0    0    0    0    0    0    0
+0           0.00           3.80    0    0    0    0    0    0    0    2    0    0    0    0    0    2
+0          -0.20           3.51    1    1    0    0    0    0    0    0    0    0    0    0    0    0
+0           0.00          -3.60    1    0   -2   -1   -2    0    0    0    0    0    0    0    0    0
+0          -2.10           1.50    0    0    0    0    0    0    0    1    0    2    0    0    0    2
+0           0.00          -3.60    1    1   -2    1   -1    0    0    0    0    0    0    0    0    0
+0           0.70           2.80    0    0    0    0    0    0    3   -7    0    0    0    0    0   -2
+0          -2.80           0.70    0    0    0    0    0    0    0    0    0    1    0    0    0   -1
+0           0.00          -3.50    0    0    0    0    0    0    0    2    0    0    0    0    0    0
+0          -2.90          -0.60    0    0    0    0    0    0    2   -2    0    0    0    0    0   -1
+0           0.00          -3.40    1   -1    0   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00           3.40    2    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+0           0.00           3.36    0    0    2    0    3    0    0    0    0    0    0    0    0    0
+0           0.50           2.80    2    0    0   -2   -1    0    0   -2    0    3    0    0    0    0
+0           2.60          -0.70    0    0    0    0    0    0    3   -5    0    0    0    0    0   -1
+0           1.00          -2.30    0    0    0    0    0    0    0    0    0    1    0    0    0    2
+0           0.00          -3.30    2    1    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.00           3.30    1    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00           3.23    0    2    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00           3.20    1   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00          -3.20    0    2    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.00          -3.20    0    0    0    0    0    0    7   -9    0    0    0    0    0   -2
+0           0.00           3.20    0    0    0    0    0    0    0    0    2    0    0    0    0    2
+0           2.90          -0.30    0    0    0    0    0    0    0    0    0    1    0    0    0    1
+0           0.08           3.05    0    0    2   -2    3    0    0    0    0    0    0    0    0    0
+0          -0.70          -2.40    0    0    0    0    0    0    3   -3    0    0    0    0    0    2
+0           0.00          -3.08    1    0    2   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00           3.00    2    0    2   -1    2    0    0    0    0    0    0    0    0    0
+0          -1.60           1.40    1    0    0   -1    1    0    0   -1    0    2    0    0    0    0
+0          -2.90          -0.10    0    0    0    0    0    0    4   -7    0    0    0    0    0   -2
+0           0.00          -2.90    2    0    0   -2    1    0    0   -2    0    2    0    0    0    0
+0          -2.50           0.40    0    0    1   -1    1    0    0   -2    2    0    0    0    0    0
+0           0.40          -2.50    0    0    0    0    0    0    0    0    0    0    1    0    0    1
+0           0.00          -2.90    1    0    2    1    1    0    0    0    0    0    0    0    0    0
+0           0.00           2.89    0    1   -2    2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -2.80    2    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0          -2.50           0.30    0    0    1   -1    1    0    0   -1    0    1    0    0    0    0
+0          -2.50          -0.30    0    0    0    0    0    0    0    1   -2    0    0    0    0   -1
+0           0.00          -2.70    0    0    2   -2    1    0    0   -2    0    2    0    0    0    0
+0           2.70           0.00    1    0   -1    0   -3    0    0    0    0    0    0    0    0    0
+0           0.00          -2.60    0    1    0    1    1    0    0    0    0    0    0    0    0    0
+0           0.00          -2.60    0    1    0    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           2.60    3   -1    2    0    2    0    0    0    0    0    0    0    0    0
+0           2.10           0.50    0    0    1   -1    1    0   -3    4    0    0    0    0    0    0
+0           0.00           2.50    0    1    0    1   -1    0    0    0    0    0    0    0    0    0
+0           0.80           1.70    0    0    0    0    0    0    0    0    0    0    1    0    0   -1
+0           1.90          -0.60    0    0    0    0    0    0    0    6  -16    4    5    0    0   -2
+0           0.00          -2.50    2    0    0    0   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -2.40    2    0    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.00           2.40    2    0    0    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -2.40    1    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           2.40    1    0    2   -3    1    0    0    0    0    0    0    0    0    0
+0          -1.90           0.50    0    0    0    0    0    0    5   -7    0    0    0    0    0   -1
+0          -0.10          -2.30    2    0    0   -4    0    0    0    0    0    0    0    0    0    0
+0           0.00           2.30    1    1    2   -4    1    0    0    0    0    0    0    0    0    0
+0           0.00          -2.30    3    1    2    0    2    0    0    0    0    0    0    0    0    0
+0          -1.40           0.90    0    0    0    0    0    0    0    0    4    0    0    0    0    2
+0          -0.10          -2.20    0    0    0    0    0    0    8  -10    0    0    0    0    0   -2
+0          -0.20          -2.00    0    0    0    1    0    0    0    0    0    0    0    0    0    0
+0           0.00           2.20    1   -2    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.00          -2.20    2   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00           2.20    1    1    2    1    2    0    0    0    0    0    0    0    0    0
+0           0.00           2.20    0    0    0    0    0    0    0    2   -4    0    0    0    0   -2
+0          -1.80          -0.40    0    0    0    0    0    0    3   -3    0    0    0    0    0   -1
+0           0.00           2.20    0    1    2    1    1    0    0    0    0    0    0    0    0    0
+0           0.00           2.20    4    0    2    0    1    0    0    0    0    0    0    0    0    0
+0          -1.70           0.40    0    0    0    0    0    0    1   -1    0    0    0    0    0    1
+0          -0.80          -1.30    0    0    0    0    0    0    0    5   -4    0    0    0    0    2
+0          -1.30          -0.80    0    0    0    0    0    0    0    4   -4    0    0    0    0    2
+0           0.00           2.10    1    0   -2    1   -2    0    0    0    0    0    0    0    0    0
+0           0.00           2.10    1   -1    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -2.10    1    0    4   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -2.10    2    1    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00           2.10    1    0    2   -1    1    0    0    0    0    0    0    0    0    0
+0           0.00          -2.00    1    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           2.00    1    0   -2    2    1    0    0    0    0    0    0    0    0    0
+0           0.00           2.00    1   -1    0    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           2.00    2   -2    0   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -2.00    1    2    2   -2    2    0    0    0    0    0    0    0    0    0
+0           2.00           0.00    0    0    0    0    0    0    6   -9    0    0    0    0    0   -2
+0           1.10          -0.90    0    0    0    0    0    0    0    0    0    2   -5    0    0   -2
+0           1.60          -0.40    0    0    0    0    0    0    1   -3    0    0    0    0    0   -1
+0           0.00          -1.91    1    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -1.90    0    0    0    4    0    0    0    0    0    0    0    0    0    0
+0           0.00           1.90    0    0    4   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00          -1.90    3    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00           1.90    2   -1    0   -2    1    0    0    0    0    0    0    0    0    0
+0           1.50           0.40    0    0    1   -1    1    0   -4    5    0    0    0    0    0    0
+0          -1.50          -0.40    0    0    0    0    0    0    0    2    0    0    0    0    0    1
+0          -1.40          -0.50    0    0    1   -1    1    0    0   -1    0   -4   10    0    0    0
+0          -1.00           0.90    0    0    1   -1    1    0    0   -1    0    0    0    0    2    0
+0           0.00          -1.90    2    0    2    1    2    0    0    0    0    0    0    0    0    0
+0          -0.30           1.60    0    0    0    0    0    0    0    4    0   -3    0    0    0    2
+0           0.00           1.90    0    0    0    4   -1    0    0    0    0    0    0    0    0    0
+0           0.00           1.90    1   -1    0    2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.80    2   -1    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.80    2    0   -4   -2   -2    0    0    0    0    0    0    0    0    0
+0          -1.10           0.70    0    0    0    0    0    0    0    2   -4    0    0    0    0   -1
+0           0.20          -1.60    0    0    0    0    0    0    2   -3    0    0    0    0    0   -1
+0           0.00           1.80    2    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -1.71    0    0    2    2    0    0    0    0    0    0    0    0    0    0
+0          -1.20          -0.50    0    0    2   -2    1    0    0   -9   13    0    0    0    0    0
+0           1.50           0.20    0    0    1    1    1    0    0    1    0    0    0    0    0    0
+0          -0.60          -1.10    1    0    2    0    1    0    0   -2    0    3    0    0    0    0
+0           0.60           1.10    1    0   -2    0   -1    0    0   -1    0    0    0    0    0    0
+0          -0.60          -1.10    0    0    0    0    0    0    0    4   -3    0    0    0    0    2
+0          -1.10           0.60    0    0    0    0    1    0    0   -2    4    0    0    0    0    0
+0          -1.70           0.00    0    0    0    0    1    0    2   -3    0    0    0    0    0    0
+0           0.00           1.60    0    1   -2    2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.60    2    0    4   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00          -1.60    0    0    2    3    2    0    0    0    0    0    0    0    0    0
+0           1.20          -0.40    0    0    0    0    0    0    0    0    0    3    0    0    0    1
+0          -0.50          -1.10    2    0    2    0    2    0    0    2    0   -3    0    0    0    0
+0           0.60           1.00    0    0    0    0    0    0    0    3   -6    0    0    0    0   -2
+0          -1.30          -0.30    0    0    0    0    0    0    4   -4    0    0    0    0    0   -1
+0           0.30          -1.30    0    0    0    0    0    0    0    1   -2    0    0    0    0    1
+0           0.00           1.60    1    0    0    4    1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.60    1    1    0    2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.60    0    0    4    0    1    0    0    0    0    0    0    0    0    0
+0           1.10          -0.50    0    0    1   -1    2    0    0   -1    0    0    2    0    0    0
+0           0.00          -1.50    1   -1    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -1.50    0    1    2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           1.50    2   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.50    1    0    0    1   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.50    1    2    0   -2   -1    0    0    0    0    0    0    0    0    0
+0           1.50           0.00    1    0    0   -1    1    0   -3    4    0    0    0    0    0    0
+0           0.00          -1.50    0    1    4   -2    2    0    0    0    0    0    0    0    0    0
+0           1.30          -0.20    0    0    0    0    1    0    0    0    0    0    1    0    0    0
+0           0.00          -1.50    0    0    0    0    0    0    9  -11    0    0    0    0    0   -2
+0          -1.20          -0.30    0    0    0    0    0    0    1    1    0    0    0    0    0    1
+0          -1.40           0.10    0    0    0    0    0    0    0    4    0   -1    0    0    0    2
+0          -0.50           1.00    0    0    2    0    2    0    0    1    0    0    0    0    0    0
+0          -0.50           1.00    0    0    0    0    0    0    0    1   -8    3    0    0    0   -2
+0           0.20          -1.30    0    0    0    0    0    0    0    1    0   -4    0    0    0   -2
+0           0.00           1.50    1    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00           1.50    1    0    0   -1   -2    0    0    0    0    0    0    0    0    0
+0           0.00           1.50    0    0    0    0    1    0    1   -1    0    0    0    0    0    0
+0           0.00           1.49    2    0    2   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00          -1.41    2    0    2    0    0    0    0    0    0    0    0    0    0    0
+0           0.00           1.41    0    1    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -1.40    0    0    2   -3    1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.40    1   -2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00           1.40    1    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -1.40    0    0    2   -2    1    0   -4    4    0    0    0    0    0    0
+0           1.10          -0.30    0    0    1   -1    1    0   -1    0    0    0    0    0    0    0
+0           0.00          -1.40    3    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           1.40    1    0    2    4    1    0    0    0    0    0    0    0    0    0
+0           1.40           0.00    0    0    1    0    2    0    0    0    0    0    0    0    0    0
+0          -0.30           1.10    0    0    0    0    1    0   -3    5    0    0    0    0    0    0
+0           0.20           1.20    2   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+0          -1.30           0.00    1    0    0   -1   -1    0   -3    4    0    0    0    0    0    0
+0           0.00          -1.30    1   -1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.00           1.30    1   -2    2    2    2    0    0    0    0    0    0    0    0    0
+0          -0.70          -0.60    0    0    1   -1    1    0    8  -14    0    0    0    0    0    0
+0          -0.80           0.50    0    0    1   -1    1    0    3   -6    0    0    0    0    0    0
+0          -0.20          -1.10    0    0    0    0    0    0    6  -10    0    0    0    0    0   -2
+0           1.10           0.20    0    0    0    0    0    0    0    2    0   -2    0    0    0    1
+0           0.00          -1.30    3    1    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00          -1.30    1    0   -4   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -1.30    0    0    0    0    0    0    3   -1    0    0    0    0    0    2
+0           0.00          -1.30    0    0    0    0    0    0    0    3    0    0   -1    0    0    2
+0           0.00          -1.29    0    0    2   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00           1.20    1    2    0   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.20    2    1    0    0   -1    0    0    0    0    0    0    0    0    0
+0          -0.40          -0.80    0    0    0    0    1    0    0    8  -15    0    0    0    0    0
+0           0.00           1.20    2    0    2    4    2    0    0    0    0    0    0    0    0    0
+0           1.20           0.00    0    0    0    0    0    0    3   -6    0    0    0    0    0   -2
+0          -0.70          -0.50    0    0    1   -1    1    0    0   -3    4    0    0    0    0    0
+0          -1.00           0.20    0    0    0    0    0    0    6   -8    0    0    0    0    0   -1
+0          -1.00           0.20    0    0    0    0    0    0    2    1    0    0    0    0    0    2
+0           0.20          -1.00    0    0    0    0    0    0    2    1    0    0    0    0    0    1
+0           0.40           0.80    0    0    0    0    0    0    0    1   -4    0    0    0    0   -2
+0          -0.40           0.80    0    0    0    0    0    0    0    0    3    0    0    0    0    2
+0           0.00          -1.20    1    1    2    2    1    0    0    0    0    0    0    0    0    0
+0           0.00           1.15    0    0    0    0    3    0    0    0    0    0    0    0    0    0
+0           0.00           1.10    2    1    0    0    1    0    0    0    0    0    0    0    0    0
+0          -0.20           0.90    2    0    0   -2    1    0   -6    8    0    0    0    0    0    0
+0          -1.10           0.00    0    0    3    0    3    0    0    0    0    0    0    0    0    0
+0           0.00          -1.10    2    1    2    2    2    0    0    0    0    0    0    0    0    0
+0          -1.10           0.00    1    0    1    0    1    0    0    0    0    0    0    0    0    0
+0           0.00           1.10    2   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00           1.10    3    0    2   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00           1.10    0    0    0    0    0    0    0    7   -8    3    0    0    0    2
+0           0.60          -0.50    0    0    0    0    0    0    0    0    0    0    0    0    2    1
+0          -0.90          -0.20    0    0    0    0    0    0    5   -5    0    0    0    0    0   -1
+0          -0.40          -0.70    0    0    0    0    0    0    0    4   -7    0    0    0    0   -2
+0          -0.50           0.60    1   -2    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00           1.10    3    0    2    2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.10    1   -1   -2    2   -2    0    0    0    0    0    0    0    0    0
+0           0.00           1.00    0    0    0    0    1    0   -1    1    0    0    0    0    0    0
+0           1.00           0.00    0    0    0    0    1    0   -2    3    0    0    0    0    0    0
+0           0.80          -0.20    0    0    0    0    1    0    0    0    0   -1    0    0    0    0
+0           0.00           1.00    1    0    0    1    1    0    0    0    0    0    0    0    0    0
+0           0.00           1.00    0    2    0   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -1.00    3    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+0          -1.00           0.00    1    0    1   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00           1.00    1   -1    2    4    2    0    0    0    0    0    0    0    0    0
+0           1.00           0.00    0    0    1   -1    1    0    0   -9   15    0    0    0    0    0
+0           1.00           0.00    0    0    0    0    0    0    7  -10    0    0    0    0    0   -2
+0          -0.80          -0.20    0    0    0    0    0    0    2    0    0    0    0    0    0    1
+0           0.40           0.60    0    0    0    0    0    0    3   -5    4    0    0    0    0    2
+0          -0.40          -0.60    0    0    0    0    0    0    3   -9    4    0    0    0    0   -2
+0           0.00          -1.00    1    0    4    0    2    0    0    0    0    0    0    0    0    0
+0           0.00           1.00    0    1    2   -4    1    0    0    0    0    0    0    0    0    0
+0           0.00           1.00    1    0   -4    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           1.00    0    2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           1.00    2    0   -2    0    2    0    0    0    0    0    0    0    0    0
+0           0.00          -1.00    1    0    0    2    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.91    1    0    2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.10           0.80    1   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.90    1    0   -2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.90    0    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.90    2    0    0   -2    1    0   -3    3    0    0    0    0    0    0
+0           0.00          -0.90    1    2    2    0    2    0    0    0    0    0    0    0    0    0
+0          -0.70          -0.20    0    0    0    0    0    0    6   -6    0    0    0    0    0   -1
+0           0.70          -0.20    0    0    0    0    0    0    1    2    0    0    0    0    0    2
+0          -0.30           0.60    0    0    0    0    0    0    0    8  -15    0    0    0    0   -2
+0           0.00           0.90    5    0    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.90    3    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.90    0    0    1   -1    1    0   -2    2    0    0    0    0    0    0
+0          -0.50          -0.40    0    0    0    0    0    0    0    3    0   -3    0    0    0    2
+0          -0.90           0.00    0    0    0    0    0    0    0    2    0    0   -1    0    0    2
+0           0.00          -0.90    1   -1    2   -1    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.90    1    0   -2    4   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.90    2    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.90    4    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.90    1   -1    0   -1    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.80    2   -1    0    0    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.80    3    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.80    2   -1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.10           0.70    0    0    0    0    0    0    7  -11    0    0    0    0    0   -2
+0          -0.70           0.10    0    0    0    0    0    0    2   -2    0    0    0    0    0    1
+0          -0.60           0.20    0    0    0    0    0    0    7   -9    0    0    0    0    0   -1
+0           0.20           0.60    0    0    0    0    0    0    4   -7    0    0    0    0    0   -1
+0           0.00           0.80    0    0    4   -4    1    0    0    0    0    0    0    0    0    0
+0          -0.50           0.30    0    0    0    0    0    0    3   -5    0    0    0    0    0    1
+0          -0.50          -0.30    0    0    0    0    0    0    0    5   -5    0    0    0    0    2
+0          -0.50          -0.30    0    0    0    0    0    0    0    5   -9    0    0    0    0   -2
+0           0.00          -0.80    0    0    0    0    0    0    0    3   -1    0    0    0    0    2
+0          -0.30           0.50    0    0    0    0    0    0    0    2    0    2   -5    0    0    2
+0          -0.80           0.00    0    0    0    0    0    0    0    2    0    0    1    0    0    2
+0          -0.30          -0.50    0    0    0    0    0    0    0    2    0   -2    5    0    0    2
+0          -0.30           0.50    0    0    0    0    1    0   -3    7   -4    0    0    0    0    0
+0          -0.30          -0.50    0    0    0    0    1    0    3   -7    4    0    0    0    0    0
+0           0.00           0.80    0    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.80    3    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.80    1    0   -2   -3   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.80    0    1   -2    1   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.80    1    2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.80    1    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.80    0    0    0    0    1    0    0    1    0   -1    0    0    0    0
+0           0.00           0.76    1    0    2    0    3    0    0    0    0    0    0    0    0    0
+0           0.00           0.70    3    0    0   -4    1    0    0    0    0    0    0    0    0    0
+0           0.10          -0.60    2    0    0   -2   -1    0   -6    8    0    0    0    0    0    0
+0           0.00           0.70    0    1   -4    2   -2    0    0    0    0    0    0    0    0    0
+0           0.70           0.00    0    0    1   -1    1    0    0   -1    0   -1    1    0    0    0
+0           0.00          -0.70    0    0    0    0    1    0    3   -5    0    2    0    0    0    0
+0           0.00          -0.70    0    1    2    4    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.70    4    0    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.70    2    0   -2   -3   -2    0    0    0    0    0    0    0    0    0
+0          -0.70           0.00    0    0    1   -1    1    0    2   -4    0   -3    0    0    0    0
+0          -0.50           0.20    0    0    1   -1    1    0    0   -1    0    3    0    0    0    0
+0          -0.20          -0.50    0    0    0    0    0    0    9   -9    0    0    0    0    0   -1
+0           0.50          -0.20    0    0    0    0    0    0    2   -3    0    0    0    0    0   -2
+0           0.20           0.50    0    0    0    0    0    0    0   11    0    0    0    0    0    2
+0          -0.20          -0.50    0    0    0    0    0    0    0    6  -15    0    0    0    0   -2
+0           0.50          -0.20    0    0    0    0    0    0    0    3    0    2   -5    0    0    2
+0          -0.50           0.20    0    0    0    0    0    0    0    3    0    1    0    0    0    2
+0           0.00          -0.70    0    0    0    0    0    0    0    3   -5    0    0    0    0   -2
+0           0.00          -0.70    0    0    0    0    0    0    0    2    0   -4    0    0    0   -2
+0           0.70           0.00    0    0    0    0    0    0    0    0    0    0    3    0    0    2
+0          -0.60          -0.10    0    0    0    0    0    0    3   -1    0    0    0    0    0    1
+0           0.60          -0.10    0    0    0    0    0    0    3   -3    0    0    0    0    0    1
+0           0.40           0.30    0    0    0    0    0    0    0    4   -8    0    0    0    0   -2
+0           0.00           0.70    1    0    0    1    0    0    0    0    0    0    0    0    0    0
+0           0.70           0.00    0    0    1   -1    2    0    0   -2    2    0    0    0    0    0
+0           0.00           0.70    2   -1    2    2    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.70    0    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.70    1   -1    0    0    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.60    0    0    0    0    1    0    0   -1    0    1    0    0    0    0
+0           0.00           0.60    0    0    2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.10          -0.50    0    0    1   -1   -1    0    0    0   -2    0    0    0    0    0
+0           0.00           0.60    2    1    0    0    0    0    0    0    0    0    0    0    0    0
+0           0.40           0.20    0    0    0    0    1    0    0    2   -4    0    0    0    0    0
+0           0.00           0.60    1    0    0   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.60    2    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.60    1    1    0   -4    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.60    0    1    0    1    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.60    1   -1    2   -4    1    0    0    0    0    0    0    0    0    0
+0           0.50           0.10    0    0    1   -1    1    0   -2    3    0    0    0    0    0    0
+0          -0.50          -0.10    0    0    0    0    0    0    7   -7    0    0    0    0    0   -1
+0          -0.10          -0.50    0    0    0    0    0    0    4   -4    0    0    0    0    0    2
+0           0.10           0.50    0    0    0    0    0    0    1   -2    0    0    0    0    0    1
+0           0.50          -0.10    0    0    0    0    0    0    0    5    0   -2    0    0    0    2
+0          -0.10           0.50    0    0    0    0    0    0    0    3   -6    0    0    0    0   -1
+0           0.00          -0.60    2    0    0   -2   -1    0    0   -2    0    0    5    0    0    0
+0          -0.40           0.20    2    0   -1   -1   -1    0    0   -1    0    3    0    0    0    0
+0           0.00          -0.60    1    0   -2   -2   -2    0    0   -2    0    2    0    0    0    0
+0           0.60           0.00    0    0    2   -2    2    0   -8   11    0    0    0    0    0    0
+0           0.00          -0.60    0    0    2   -2    1    0   -2    2    0    0    0    0    0    0
+0           0.20           0.40    0    0    1   -1    1    0    0   -1    0    0    3    0    0    0
+0          -0.40           0.20    0    0    0    0    0    1    0   -4    0    0    0    0    0   -2
+0           0.30           0.30    0    0    0    0    0    0    8  -13    0    0    0    0    0    1
+0           0.40          -0.20    0    0    0    0    0    0    0    6    0    0    0    0    0    2
+0          -0.40          -0.20    0    0    0    0    0    0    0    6   -6    0    0    0    0    2
+0           0.00           0.60    0    0    0    0    0    0    0    1    1    0    0    0    0    2
+0           0.00           0.60    0    0    0    0    0    0    0    1    0    0    1    0    0    2
+0           0.40           0.20    0    0    0    0    0    0    0    1   -5    0    0    0    0   -2
+0          -0.20          -0.40    0    0    0    0    0    0    0    0    0    0    3    0    0    1
+0           0.00           0.60    4    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.60    0    0    0    3    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.60    3    0    0    2    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.60    1    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.60    0    0    0    1    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.60    1    1    0   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.60    2    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.50    1    1    0   -4    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.50    2    0    2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.10           0.40    2    1    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.50    0    1    0    2    2    0    0    0    0    0    0    0    0    0
+0           0.10           0.40    0    0    0    0    1    0    3   -5    0    0    0    0    0    0
+0           0.00           0.50    1   -2    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.50    2    1    0   -4    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.50    1   -2    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.30          -0.20    2    0    0   -2    1    0    0   -6    8    0    0    0    0    0
+0          -0.20           0.30    2    0    0   -2   -1    0    0   -5    6    0    0    0    0    0
+0           0.20           0.30    2    0   -1   -1   -1    0    0    3   -7    0    0    0    0    0
+0           0.40          -0.10    0    0    2    0    2    0    0   -4    8   -3    0    0    0    0
+0           0.40           0.10    0    0    2    0    2    0    0    4   -8    3    0    0    0    0
+0           0.00          -0.50    0    1   -2   -1   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.50    3   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.30           0.20    0    0    0    0    0    0    1    0    0    0    0    0    0    2
+0          -0.30           0.20    0    0    0    0    0    0    0    7  -13    0    0    0    0   -2
+0           0.20           0.30    0    0    0    0    0    0    0    2   -5    0    0    0    0   -2
+0          -0.30           0.20    0    0    0    0    0    0    0    1    0    3    0    0    0    2
+0           0.00           0.50    3   -1    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.50    3   -1   -2   -1   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.50    0    0    2    6    2    0    0    0    0    0    0    0    0    0
+0          -0.50           0.00    0    0    1   -1    1    0    0    1    0    0    0    0    0    0
+0           0.50           0.00    0    0    0    0    0    0    9  -12    0    0    0    0    0   -2
+0           0.00          -0.50    0    0    0    0    0    0    6   -9    0    0    0    0    0   -1
+0          -0.50           0.00    0    0    0    0    0    0    4   -4    0    0    0    0    0    1
+0          -0.50           0.00    0    0    0    0    0    0    0    6  -11    0    0    0    0   -2
+0           0.00           0.50    0    0    0    0    0    0    0    1   -3    0    0    0    0   -2
+0           0.40           0.10    0    0    2   -2    1   -1    0    2    0    0    0    0    0    0
+0          -0.40          -0.10    0    0    1   -1    1    0    0   -1    0    0    0    2    0    0
+0           0.40          -0.10    0    0    0    0    0    0    5   -6    0    0    0    0    0    2
+0          -0.40           0.10    0    0    0    0    0    0    5  -10    0    0    0    0    0   -2
+0           0.10           0.40    0    0    0    0    0    0    0    5    0   -3    0    0    0    2
+0           0.10           0.40    0    0    0    0    0    0    0    0    0    4    0    0    0    2
+0          -0.50           0.00    0    0    3    0    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.50    2    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.50    2    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.50    0    0    2   -2    2    0   -3    3    0    0    0    0    0    0
+0           0.00           0.50    1    0    0   -1    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.50    1    1    0    0    2    0    0    0    0    0    0    0    0    0
+0           0.50           0.00    0    0    2   -2    2    0   -5    6    0    0    0    0    0    0
+0           0.00          -0.50    1    0    0    2   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.50    1    0   -2    2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    1    0    2    2    0    0    0    0    0    0    0    0    0    0
+0          -0.20           0.20    0    0    0    0    2    0    0   -1    2    0    0    0    0    0
+0          -0.10           0.30    1    1    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    1    0    0    4    0    0    0    0    0    0    0    0    0    0
+0           0.40           0.00    1    0   -1    0    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    0    2    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    1    0    0   -3    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    4    0    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    1   -2    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          -0.10           0.30    0    0    0    0    1    0    0   -8   15    0    0    0    0    0
+0           0.00           0.40    1    0    4   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    1    1    0   -1    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    0    0    2    0    2    0    1   -1    0    0    0    0    0    0
+0           0.00          -0.40    1    1    0    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    2   -2    2    0    2    0    0    0    0    0    0    0    0    0
+0          -0.20          -0.20    0    0    1   -1    1    0    0   -1    0    0   -2    0    0    0
+0           0.20          -0.20    0    0    1   -1    1    0    0   -1    0   -2    4    0    0    0
+0           0.20           0.20    0    0    0    0    0    0    0    2    2    0    0    0    0    2
+0          -0.10           0.30    2   -2    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.30           0.10    0    0    0    0    0    0    6  -10    0    0    0    0    0   -1
+0           0.10           0.30    0    0    0    0    0    0    0    6   -5    0    0    0    0    2
+0          -0.10           0.30    0    0    0    0    0    0    0    2    0   -2    0    0    0    2
+0           0.00          -0.40    5    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    3    0    2   -1    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    1    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    1   -1   -2   -3   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    0    0    4   -1    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    0    0    2   -2    1    0    0   -2    0    3    0    0    0    0
+0           0.00          -0.40    0    0    2   -2    1    0    0   -2    0    0    2    0    0    0
+0           0.40           0.00    0    0    2   -2    1    0    0   -8   11    0    0    0    0    0
+0          -0.40           0.00    0    0    1   -1    1    0    0   -1    0   -1    2    0    0    0
+0          -0.40           0.00    0    0    0    0    0    0    8   -8    0    0    0    0    0   -1
+0          -0.40           0.00    0    0    0    0    0    0    8  -10    0    0    0    0    0   -1
+0           0.00           0.40    0    0    0    0    0    0    5   -5    0    0    0    0    0    2
+0           0.00          -0.40    0    0    0    0    0    0    5   -9    0    0    0    0    0   -2
+0           0.00          -0.40    0    0    0    0    0    0    4   -2    0    0    0    0    0    2
+0          -0.40           0.00    0    0    0    0    0    0    4   -2    0    0    0    0    0    1
+0           0.40           0.00    0    0    0    0    0    0    4   -3    0    0    0    0    0    2
+0           0.00          -0.40    0    0    0    0    0    0    3   -4    0    0    0    0    0   -1
+0           0.00           0.40    0    0    0    0    0    0    3   -6    0    0    0    0    0   -1
+0           0.00           0.40    0    0    0    0    0    0    2   -6    0    0    0    0    0   -2
+0           0.40           0.00    0    0    0    0    0    0    0    8  -15    0    0    0    0   -1
+0           0.00          -0.40    0    0    0    0    0    0    0    6    0    0    0    0    0    0
+0          -0.40           0.00    0    0    0    0    0    0    0    5   -2    0    0    0    0    2
+0           0.00           0.40    0    0    0    0    0    0    0    5   -3    0    0    0    0    2
+0           0.40           0.00    0    0    0    0    0    0    0    0    0    0    1    0    0    2
+0           0.00          -0.40    4    0    0    0    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    1   -2   -2    0   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    1    2    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    0    2    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    3    1    2    0    1    0    0    0    0    0    0    0    0    0
+0          -0.10           0.30    0    2   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    1    1    2    1    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    3   -1    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.40    2    0    2   -1    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.40    2    0    0    2    2    0    0    0    0    0    0    0    0    0
+0           0.40           0.00    0    0    1   -1    2    0    0   -1    0    2    0    0    0    0
+0           0.00           0.40    0    0    2   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.21           0.10    0    0    1   -1    2    0    0   -1    0   -2    5    0    0    0
+0           0.00           0.30    2    0   -4    2   -2    0    0    0    0    0    0    0    0    0
+0          -0.30           0.00    0    0    2   -2   -1    0   -5    6    0    0    0    0    0    0
+0           0.00          -0.30    2   -2    0   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    0    0    0    4    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    2    0    0   -2   -1    0   -3    3    0    0    0    0    0    0
+0           0.20           0.10    0    0    1   -1   -1    0    0   -1    0   -1    0    0    0    0
+0           0.00          -0.30    1   -2    0   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    1   -2    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.30           0.00    0    0    0    0    1    0    0    0    0    0   -1    0    0    0
+0           0.00           0.30    2    0    0   -3   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    2    1   -2    0    1    0    0    0    0    0    0    0    0    0
+0          -0.20           0.10    2    0    0   -2   -1    0    0   -6    8    0    0    0    0    0
+0          -0.10          -0.20    2    0   -1   -1    1    0    0    3   -7    0    0    0    0    0
+0           0.00          -0.30    2    1    2   -4    1    0    0    0    0    0    0    0    0    0
+0           0.10          -0.20    1    0    0   -1    1    0    0   -3    4    0    0    0    0    0
+0           0.00           0.30    2   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    0    0    2    0    2    0   -1    1    0    0    0    0    0    0
+0           0.00          -0.30    0    0    2    0    2    0    0   -1    0    1    0    0    0    0
+0           0.00           0.30    0    0    2    0    2    0    0    1    0   -1    0    0    0    0
+0           0.00           0.30    0    1    0   -4    1    0    0    0    0    0    0    0    0    0
+0          -0.20           0.10    0    0    0    0    1    0    0   -9   17    0    0    0    0    0
+0           0.00          -0.30    1    1    4   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.10          -0.20    2    0    2    0    1    0    0    1    0    0    0    0    0    0
+0          -0.10           0.20    1    0   -2    0   -2    0  -10    3    0    0    0    0    0    0
+0           0.20          -0.10    0    0    2   -2    1    0    0   -7    9    0    0    0    0    0
+0          -0.10          -0.20    0    0    0    0    0    0    2   -3    0    0    0    0    0    1
+0           0.20           0.10    0    0    0    0    0    0    1   -2    0    0    0    0    0   -2
+0           0.20          -0.10    0    0    0    0    0    0    1   -4    0    0    0    0    0   -2
+0          -0.20          -0.10    0    0    0    0    0    0    0    6  -10    0    0    0    0   -2
+0          -0.10          -0.20    0    0    0    0    0    0    0    5   -8    0    0    0    0   -2
+0           0.20          -0.10    0    0    0    0    0    0    0    3   -5    0    0    0    0   -1
+0           0.20           0.10    0    0    0    0    0    0    0    2   -6    0    0    0    0   -2
+0           0.00           0.30    4    0    2   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    2    2    2   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    2    1    2    1    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    2    0    0   -2   -1    0    0   -2    0    4   -5    0    0    0
+0           0.00          -0.30    2    0    0   -2   -2    0   -3    3    0    0    0    0    0    0
+0           0.30           0.00    2    0   -1   -1   -1    0    0   -1    0    0    0    0    0    0
+0           0.00          -0.30    1    0    2    3    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    1    0    2   -2    2    0    0   -2    0    2    0    0    0    0
+0           0.30           0.00    1    0    1   -1    1    0    0   -1    0    0    0    0    0    0
+0           0.30           0.00    1    0   -1    1   -1    0  -18   17    0    0    0    0    0    0
+0           0.00          -0.30    1    0   -1   -1   -1    0   20  -20    0    0    0    0    0    0
+0           0.00          -0.30    1    0   -2   -2   -2    0   -3    3    0    0    0    0    0    0
+0           0.00          -0.30    0    2    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    0    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    0    1    2    3    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    0    0    2   -2    1    0    0   -1    0    1    0    0    0    0
+0           0.30           0.00    0    0    1   -1    1    0    1   -2    0    0    0    0    0    0
+0           0.30           0.00    0    0    1   -1    1    0   -2    1    0    0    0    0    0    0
+0          -0.30           0.00    0    0    0    0    0    0    9  -11    0    0    0    0    0   -1
+0           0.00           0.30    0    0    0    0    0    0    8  -16    0    0    0    0    0   -2
+0          -0.30           0.00    0    0    0    0    0    0    5   -3    0    0    0    0    0    1
+0           0.30           0.00    0    0    0    0    0    0    0    6    0    0    0    0    0    1
+0          -0.30           0.00    0    0    0    0    0    0    0    6   -7    0    0    0    0    2
+0           0.00           0.30    0    0    0    0    0    0    0    4    0    0   -2    0    0    2
+0           0.30           0.00    0    0    0    0    0    0    0    4   -8    1    5    0    0   -2
+0          -0.30           0.00    0    0    0    0    0    0    0    3    0    0   -2    0    0    2
+0           0.30           0.00    0    0    0    0    0    0    0    1    0   -1    0    0    0    1
+0           0.30           0.00    0    0    0    0    0    0    0    1    0   -3    0    0    0   -1
+0           0.30           0.00    0    0    0    0    0    0    0    1   -6    0    0    0    0   -2
+0           0.00          -0.30    0    0    0    0    0    0    0    0    0    4   -5    0    0    2
+0           0.00           0.30    0    0    0    0    0    0    0    0    0    0    5    0    0    2
+0           0.00           0.30    0    0    0    0    0    0    0    0    0    0    0    2    0    2
+0           0.30           0.00    0    0    0    0    0    0    0    0    0    0    0    2    0    1
+0           0.00           0.30    0    0    0    0    0    0    0    0    0    0    0    0    2    2
+0           0.00          -0.30    1    0    0   -1   -1    0    0   -2    2    0    0    0    0    0
+0           0.00          -0.30    2   -1    2   -1    2    0    0    0    0    0    0    0    0    0
+0           0.30           0.00    2    0   -1   -1   -1    0    0   -1    0    2    0    0    0    0
+0           0.00           0.30    1    0    0   -6   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    1   -1    0   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    1    0    0    4   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    3    0    0   -2    1    0    0    0    0    0    0    0    0    0
+0           0.30           0.00    0    0    0    0    1    0    5   -8    0    0    0    0    0    0
+0           0.00          -0.30    1   -2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    0    1    4   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    2    0    4   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    0    0    2    3    1    0    0    0    0    0    0    0    0    0
+0           0.30           0.00    0    0    1   -1    2    0    0   -1    0    1    0    0    0    0
+0           0.00           0.30    1   -2    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    2   -1    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    2    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    2    0   -4   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    2    0    2    1    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.30    0    0    1   -1    2    0    0    0   -2    0    0    0    0    0
+0          -0.30           0.00    0    0    0    0    2    0    0    4   -8    3    0    0    0    0
+0          -0.30           0.00    0    0    0    0    2    0    0   -4    8   -3    0    0    0    0
+0           0.00           0.30    0    0    0    1   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    0    0    2    0   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.30    0    1    0   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.20          -0.10    0    0    0    0    1    0    0    1    0   -2    0    0    0    0
+0           0.00          -0.30    0    0    0    0    1    0    2   -2    0    0    0    0    0    0
+0           0.00          -0.30    1    1    2   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.21    0    1    2   -2    3    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2    0    2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    0    0    0    0    1    0   -2    2    0    0    0    0    0    0
+0           0.00          -0.20    3    0    2    0    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    0    0    0    0    1    0    0   -2    2    0    0    0    0    0
+0           0.00          -0.20    0    1    0   -4    0    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    0    0    0    0    1    0   -3    4    0    0    0    0    0    0
+0           0.00          -0.20    3    0    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    3    0    0    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    2    0    2   -4    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    0    0   -3   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    2    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    0    2   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    0    1   -1    2    0    0   -1    0    0    1    0    0    0
+0           0.00           0.20    1    0   -2    1    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1    2    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    2    0   -2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1    0    0   -1    1    0    0   -1    0    1    0    0    0    0
+0           0.00          -0.20    2    0    0   -3    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1    1    0    1    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    1    0    1   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    2    0    0   -1    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    2    0    0    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    2    0    0   -2    1    0    0   -5    6    0    0    0    0    0
+0           0.00           0.20    1    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+0           0.00          -0.20    1    0    0   -2    1    0    0   -2    0    2    0    0    0    0
+0           0.00           0.20    2    0   -4    2   -1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    0    0    2   -2    1    0    0    4   -8    3    0    0    0    0
+0           0.20           0.00    0    0    2   -2    1    0    0   -4    8   -3    0    0    0    0
+0           0.00           0.20    2   -1    0    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    0    0   -1   -1    0    0   -3    4    0    0    0    0    0
+0           0.10          -0.10    1   -2    0   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    2    0    0   -2   -1    0    0   -2    0    3   -1    0    0    0
+0           0.00           0.20    1   -1   -4    2   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    3    0    0    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    0    0   -3    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2    0    2   -6    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2   -1   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1   -2   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2    0    4   -4    2    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    2    0   -1   -1   -2    0    0   -1    0    2    0    0    0    0
+0           0.00          -0.20    4    1    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    4   -1    2    0    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    4   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    3    0    4   -2    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    3    0    2    1    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    3    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    3    0   -2   -1   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    2    1    0   -4    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2    1   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2    0    2   -3    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2   -2    2    2    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1    1    2    4    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1    1   -2   -3   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    1   -2   -6   -2    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    1    0    3    0    3    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    1    0    1    1    1    0    0    1    0    0    0    0    0    0
+0           0.20           0.00    1    0   -1    1   -1    0    0    1    0    0    0    0    0    0
+0           0.00           0.20    1    0   -1    0   -1    0   -3    5    0    0    0    0    0    0
+0           0.20           0.00    1    0   -1   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    0   -2   -2   -2    0    0   -2    0    3    0    0    0    0
+0           0.00          -0.20    1   -1    2    1    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1   -1   -2    1   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    3   -2   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    0    0    4    2    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    0    2   -2    1    0    0    1    0   -1    0    0    0    0
+0           0.00           0.20    0    0    2   -2    1    0    0   -2    0    0    0    0    0    0
+0           0.00          -0.20    0    0    2   -2    1    0    0   -3    0    3    0    0    0    0
+0           0.00          -0.20    0    0    2   -2    1    0    0   -4    4    0    0    0    0    0
+0           0.00          -0.20    0    0    2   -2    1    0   -5    5    0    0    0    0    0    0
+0           0.00           0.20    0    0    1   -1    1    0    1   -3    0    0    0    0    0    0
+0           0.00          -0.20    0    0    1   -1    1    0    0    1   -4    0    0    0    0    0
+0           0.20           0.00    0    0    1   -1    1    0    0   -1    0    1   -3    0    0    0
+0           0.20           0.00    0    0    1   -1    1    0    0   -1    0    0    0   -1    0    0
+0           0.00          -0.20    0    0    1   -1    1    0    0   -4    6    0    0    0    0    0
+0           0.20           0.00    0    0    1   -1    1    0   -5    6    0    0    0    0    0    0
+0           0.00           0.20    0    0    0    0    0    0    8  -12    0    0    0    0    0   -2
+0           0.00          -0.20    0    0    0    0    0    0    7  -10    0    0    0    0    0   -1
+0           0.20           0.00    0    0    0    0    0    0    7  -11    0    0    0    0    0   -1
+0          -0.20           0.00    0    0    0    0    0    0    6   -4    0    0    0    0    0    1
+0          -0.20           0.00    0    0    0    0    0    0    6   -6    0    0    0    0    0    1
+0          -0.20           0.00    0    0    0    0    0    0    5   -5    0    0    0    0    0    1
+0           0.00          -0.20    0    0    0    0    0    0    5   -6    0    0    0    0    0   -1
+0          -0.20           0.00    0    0    0    0    0    0    4   -2    0    0    0    0    0    0
+0           0.00          -0.20    0    0    0    0    0    0    4   -5    0    0    0    0    0   -1
+0           0.20           0.00    0    0    0    0    0    0    4   -5    0    0    0    0    0   -2
+0           0.00          -0.20    0    0    0    0    0    0    3   -4    0    0    0    0    0    1
+0           0.20           0.00    0    0    0    0    0    0    3   -4    0    0    0    0    0   -2
+0          -0.20           0.00    0    0    0    0    0    0    3   -8    0    0    0    0    0   -2
+0           0.00           0.20    0    0    0    0    0    0    2   -5    0    0    0    0    0   -1
+0           0.00           0.20    0    0    0    0    0    0    1   -2    0    0    0    0    0   -1
+0           0.00           0.20    0    0    0    0    0    0    1   -4    0    0    0    0    0   -1
+0           0.00          -0.20    0    0    0    0    0    0    0    9  -17    0    0    0    0   -2
+0          -0.20           0.00    0    0    0    0    0    0    0    7   -7    0    0    0    0    2
+0          -0.20           0.00    0    0    0    0    0    0    0    7   -8    0    0    0    0    2
+0          -0.20           0.00    0    0    0    0    0    0    0    7   -9    0    0    0    0    2
+0          -0.20           0.00    0    0    0    0    0    0    0    5   -6    0    0    0    0    2
+0           0.20           0.00    0    0    0    0    0    0    0    5  -10    0    0    0    0   -2
+0           0.00           0.20    0    0    0    0    0    0    0    4    0   -4    0    0    0    2
+0           0.00          -0.20    0    0    0    0    0    0    0    4   -6    0    0    0    0   -2
+0           0.00          -0.20    0    0    0    0    0    0    0    4   -7    0    0    0    0   -1
+0           0.00          -0.20    0    0    0    0    0    0    0    4   -8    3    0    0    0    1
+0           0.00           0.20    0    0    0    0    0    0    0    4   -8    3    0    0    0   -1
+0           0.20           0.00    0    0    0    0    0    0    0    4   -8    1    5    0    0    2
+0           0.20           0.00    0    0    0    0    0    0    0    3    0    0    0    0    0    2
+0           0.00           0.20    0    0    0    0    0    0    0    3   -8    3    0    0    0   -2
+0           0.20           0.00    0    0    0    0    0    0    0    2    0    0   -2    0    0    1
+0           0.00          -0.20    0    0    0    0    0    0    0    2    0   -5    0    0    0   -2
+0           0.00          -0.20    0    0    0    0    0    0    0    2   -4    0    0    0    0    1
+0           0.20           0.00    0    0    0    0    0    0    0    2   -7    0    0    0    0   -2
+0           0.20           0.00    0    0    0    0    0    0    0    1    0    1    0    0    0    1
+0           0.00           0.20    0    0    0    0    0    0    0    1    0    0    0    0    0   -1
+0           0.20           0.00    0    0    0    0    0    0    0    1    0   -1    0    0    0   -1
+0           0.20           0.00    0    0    0    0    0    0    0    1    0   -2    5    0    0    2
+0           0.00           0.20    0    0    0    0    0    0    0    1    0   -2    0    0    0   -2
+0           0.00          -0.20    0    0    0    0    0    0    0    1    0   -5    0    0    0   -2
+0           0.10          -0.10    0    0    0    0    0    0    0    2    1    0    0    0    0    2
+0           0.00          -0.20    1    0    4    0    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    4    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    0    1    0    4    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2    0    0    4    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    0    0    0    1    0    0    7  -13    0    0    0    0    0
+0           0.00          -0.20    2    1    0    2    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1   -1    0    4    1    0    0    0    0    0    0    0    0    0
+0           0.20           0.00    0    0    2   -2    1    0   -8   11    0    0    0    0    0    0
+0           0.00           0.20    1    0    2   -6    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    0    2   -2    2    0    0   -2    0    2    0    0    0    0
+0           0.00          -0.20    1    2    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    3    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2    0    0   -6   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1   -1    2    4    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    2    1    2    2    1    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    0    0    1    1    2    0    0    1    0    0    0    0    0    0
+0           0.00           0.20    2   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    0    1   -2    4   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2    0    2    4    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1   -1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1    0   -4   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1   -2    2    2    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    3    1    2   -2    1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    2   -2    0   -2   -2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    2   -1    0    2    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    3    0    0    0    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1   -1    2   -3    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    0    1   -1    2    0   -5    7    0    0    0    0    0    0
+0           0.00          -0.20    2    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    0    0    1   -1    2    0    0   -1    0   -1    0    0    0    0
+0           0.00          -0.20    2    1   -2    0    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    1    0   -2    0    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    1   -1    0    0   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.20    0    2    0    0    2    0    0    0    0    0    0    0    0    0
+0          -0.20           0.00    0    0    0    0    1    0    3   -4    0    0    0    0    0    0
+0           0.00          -0.20    0    0    0    0    1    0    0    2   -2    0    0    0    0    0
+0           0.00           0.20    0    0    0    0    1    0    0    2    0   -2    0    0    0    0
+0           0.00          -0.20    0    1    2   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00           0.20    0    0    1   -1    2    0   -8   12    0    0    0    0    0    0
+0           0.00           0.20    1    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.19    0    1    2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.17    0    1   -2    2   -3    0    0    0    0    0    0    0    0    0
+0           0.00           0.11    1    0   -2    0   -3    0    0    0    0    0    0    0    0    0
+0           0.00          -0.10    1    1   -2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.10           0.00    0    0    0    0    1    0    0   -1    0    2    0    0    0    0
+0           0.00          -0.10    0    1   -2   -2    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.10    1    0    0    0   -1    0  -10    3    0    0    0    0    0    0
+0           0.00          -0.10    1    0    0    0    1    0  -10    3    0    0    0    0    0    0
+0           0.00          -0.10    1    0   -2   -4    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.10    2   -1    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.10    0    2    0    0   -2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.10    1    0    0   -6    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.10    0    1    4   -4    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.10    2    0    0   -6    0    0    0    0    0    0    0    0    0    0
+0          -0.10           0.00    0    0    0    0    2    0    0    0    0    1    0    0    0    0
+0           0.00          -0.10    3    0    0   -4    0    0    0    0    0    0    0    0    0    0
+0           0.00          -0.10    2   -1   -2    0    1    0    0    0    0    0    0    0    0    0
+0           0.00           0.10    2    2    0   -2   -1    0    0    0    0    0    0    0    0    0
+0           0.00          -0.10    0    0    0    3    2    0    0    0    0    0    0    0    0    0
+0           0.00          -0.10    3   -1    0    0    0    0    0    0    0    0    0    0    0    0
+0           0.10           0.00    0    0    2    0    2    0    2   -3    0    0    0    0    0    0
+0           0.10           0.00    0    0    2    0    2    0   -2    3    0    0    0    0    0    0
+0           0.00           0.10    0    0    2    0    2    0    2   -2    0    0    0    0    0    0
+0           0.00          -0.10    0    0    2    0    2    0   -2    2    0    0    0    0    0    0
+0           0.00          -0.10    2   -1   -2    0    0    0    0    0    0    0    0    0    0    0
+0           0.00           0.10    2    0   -2   -2   -2    0    0   -2    0    2    0    0    0    0
+0           0.00           0.10    1    2    2   -4    1    0    0    0    0    0    0    0    0    0
+1      153041.82         878.89    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+1       11714.49        -289.32    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+1        2024.68         -50.99    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+1       -1837.33          47.75    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+1       -1312.21         -28.91    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+1        -632.54           0.78    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+1         459.68         -67.23    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+1         344.50           1.46    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+1         268.14          -7.03    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+1         192.06          29.80    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+1         139.64           0.15    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+1        -113.94          -1.06    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+1         109.81           3.18    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+1         -56.37           0.13    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+1         -56.17          -0.02    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+1         -53.05          -1.23    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1         -51.60           0.17    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+1          45.91          -0.11    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+1         -42.45           0.02    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+1          40.82          -1.03    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+1          34.30          -1.24    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+1          28.89           0.00    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+1          27.61          -1.22    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+1         -25.43           1.00    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+1         -26.01           0.07    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+1         -23.02           0.06    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+1          19.37          -0.01    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+1          14.05          -4.19    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+1          18.18          -0.01    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+1         -14.86          -0.09    0    2    0    0    0    0    0    0    0    0    0    0    0    0
+1          13.49          -0.01    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+1          12.44          -0.27    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+1          11.46           0.03    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+1         -11.33          -0.06    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+1          -9.81           0.01    2    0   -2    0    0    0    0    0    0    0    0    0    0    0
+1          -9.08          -0.02    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+1           2.74          -4.56    0    0    1   -1    1    0    0   -1    0   -2    5    0    0    0
+1           6.84          -0.04    1    0    2    2    2    0    0    0    0    0    0    0    0    0
+1          -6.73           0.01    0    1    2    0    2    0    0    0    0    0    0    0    0    0
+1           6.54           0.01    1    1    0   -2    0    0    0    0    0    0    0    0    0    0
+1          -6.35          -0.01    0    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+1           5.90          -0.02    0    0    2    2    1    0    0    0    0    0    0    0    0    0
+1          -5.85           0.02    1    0    0    2    0    0    0    0    0    0    0    0    0    0
+1          -5.73           0.01    2    0    2   -2    2    0    0    0    0    0    0    0    0    0
+1           5.60           0.00    0    0    0    2    1    0    0    0    0    0    0    0    0    0
+1          -5.16           0.00    1    0    2   -2    1    0    0    0    0    0    0    0    0    0
+1          -5.14           0.01    2    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+1           4.76          -0.02    2    0    2    0    1    0    0    0    0    0    0    0    0    0
+1          -4.40           0.02    0    0    0    2   -1    0    0    0    0    0    0    0    0    0
+1          -4.22           0.00    0    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+1          -4.20           0.01    1   -1    0    0    0    0    0    0    0    0    0    0    0    0
+1           3.58           0.31    1    0    0   -1    0    0    0    0    0    0    0    0    0    0
+1           3.87           0.01    0    1    0   -2    0    0    0    0    0    0    0    0    0    0
+1           3.76           0.00    0    0    0    1    0    0    0    0    0    0    0    0    0    0
+1          -3.62          -0.01    2    0    0   -2    1    0    0    0    0    0    0    0    0    0
+1          -3.61           0.00    1    0   -2    0    0    0    0    0    0    0    0    0    0    0
+1          -1.28          -2.14    0    0    0    0    0    0    0    0    0    2   -5    0    0   -1
+1          -3.18           0.00    0    1    2   -2    1    0    0    0    0    0    0    0    0    0
+1           3.01           0.00    1    1    0    0    0    0    0    0    0    0    0    0    0    0
+1          -2.97           0.01    1    0    2    0    0    0    0    0    0    0    0    0    0    0
+1           2.91           0.00    1   -1    0   -1    0    0    0    0    0    0    0    0    0    0
+1          -2.73           0.00    2    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+1           2.58          -0.01    3    0    2    0    2    0    0    0    0    0    0    0    0    0
+1           2.56          -0.01    1   -1    2    0    2    0    0    0    0    0    0    0    0    0
+1          -2.51          -0.01    1    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1          -2.35          -0.01    0    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1          -2.21           0.01    1    1    2    0    2    0    0    0    0    0    0    0    0    0
+1          -2.04           0.01    2    0    0    0   -1    0    0    0    0    0    0    0    0    0
+1          -1.94           0.00    2    0    0    0    1    0    0    0    0    0    0    0    0    0
+1           0.41          -1.43    0    0    0    0    0    0    0    4   -8    3    0    0    0    0
+1          -1.84           0.00    0    0    0    0    0    0    3   -5    0    0    0    0    0   -2
+1          -1.77           0.01    1    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+1           0.00           1.77    0    1   -1    1   -1    0    0    0    0    0    0    0    0    0
+1           1.76           0.00    1    0    0    0    2    0    0    0    0    0    0    0    0    0
+1          -1.07          -0.53    0    0    1   -1    1    0   -8   12    0    0    0    0    0    0
+1          -1.48           0.00    0    0    2    1    2    0    0    0    0    0    0    0    0    0
+1          -1.40           0.01    3    0    0    0    0    0    0    0    0    0    0    0    0    0
+1          -1.35          -0.01    1    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+1          -1.32           0.00    0    0    0    0    0    0    1   -1    0    0    0    0    0    0
+1          -1.28           0.00    0    0    0    0    0    0    0    8  -16    4    5    0    0    0
+1           1.24           0.00    1    0    0    0   -2    0    0    0    0    0    0    0    0    0
+1           1.23           0.00    2    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1           1.19           0.00    1    0    0   -4    0    0    0    0    0    0    0    0    0    0
+1           1.18          -0.01    1    0    2    2    1    0    0    0    0    0    0    0    0    0
+1           1.17           0.00    1   -1    0   -1   -1    0    0    0    0    0    0    0    0    0
+1          -1.15           0.00    1    1    2   -2    2    0    0    0    0    0    0    0    0    0
+1           1.14           0.00    2    0    0   -4    0    0    0    0    0    0    0    0    0    0
+1          -1.14           0.00    0    2   -2    2   -1    0    0    0    0    0    0    0    0    0
+1           1.09           0.03    0    0    0    0    0    0    0    1    0   -1    0    0    0    0
+1          -1.08           0.00    2    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+1           1.04           0.00    0    0    0    0    0    0    0    0    0    2    0    0    0    2
+1           1.02           0.00    1    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+1           0.98          -0.01    2    0    2    2    2    0    0    0    0    0    0    0    0    0
+1           0.91           0.02    1    0    0   -1   -1    0    0    0    0    0    0    0    0    0
+1           0.00           0.93    1    0   -1    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.91           0.00    2    0    2   -2    1    0    0    0    0    0    0    0    0    0
+1          -0.90           0.00    2    1    0   -2    0    0    0    0    0    0    0    0    0    0
+1           0.86           0.00    1    0    0    2    1    0    0    0    0    0    0    0    0    0
+1          -0.84           0.00    1   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+1          -0.83           0.00    3    0    2   -2    2    0    0    0    0    0    0    0    0    0
+1          -0.82           0.00    0    0    4   -2    2    0    0    0    0    0    0    0    0    0
+1           0.41           0.39    0    0    0    0    1    0    0   -1    2    0    0    0    0    0
+1           0.40          -0.38    0    0    0    0    0    0    0    1   -2    0    0    0    0    0
+1           0.78           0.00    0    1   -2    2    0    0    0    0    0    0    0    0    0    0
+1           0.74           0.00    0    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+1          -0.73           0.00    0    1    2    0    1    0    0    0    0    0    0    0    0    0
+1           0.68           0.00    1    0    2   -2    0    0    0    0    0    0    0    0    0    0
+1           0.66           0.00    1    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+1          -0.64           0.00    2    0   -2    0    1    0    0    0    0    0    0    0    0    0
+1          -0.63           0.00    0    1    0    0    2    0    0    0    0    0    0    0    0    0
+1           0.63           0.00    0    0    2   -1    2    0    0    0    0    0    0    0    0    0
+1           0.62           0.00    0    0    2    4    2    0    0    0    0    0    0    0    0    0
+1           0.60           0.00    0    1    0    2    0    0    0    0    0    0    0    0    0    0
+1          -0.59           0.00    0    0    2    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.59           0.00    0    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+1           0.59           0.00    0    1    2   -2    0    0    0    0    0    0    0    0    0    0
+1           0.57           0.00    1    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+1           0.38          -0.19    0    0    0    0    0    0    8  -13    0    0    0    0    0   -1
+1          -0.01          -0.55    0    0    0    0    0    0    2   -3    0    0    0    0    0    0
+1           0.44          -0.11    0    0    0    0    0    0    0    0    0    2   -5    0    0    0
+1           0.53           0.00    0    0    0    0    0    0    2   -2    0    0    0    0    0    0
+1          -0.53           0.00    1   -1    0   -1   -2    0    0    0    0    0    0    0    0    0
+1           0.52           0.00    1   -1    2    2    2    0    0    0    0    0    0    0    0    0
+1          -0.52           0.00    2    0    0    2    0    0    0    0    0    0    0    0    0    0
+1           0.53           0.00    0    1    0    0   -2    0    0    0    0    0    0    0    0    0
+1           0.52           0.00    1    1    0   -2    1    0    0    0    0    0    0    0    0    0
+1           0.51           0.00    1    0   -2    2    0    0    0    0    0    0    0    0    0    0
+1           0.51           0.00    1   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1          -0.21          -0.30    0    0    0    0    0    0    8  -13    0    0    0    0    0    0
+1          -0.50           0.00    0    1    0    1    0    0    0    0    0    0    0    0    0    0
+1          -0.11           0.37    0    0    0    0    0    0    0    2   -8    3    0    0    0   -2
+1          -0.11           0.37    0    0    0    0    0    0    0    6   -8    3    0    0    0    2
+1          -0.48           0.00    0    1    2    2    2    0    0    0    0    0    0    0    0    0
+1          -0.46          -0.01    0    0    0    0    0    0    0    3    0   -1    0    0    0    2
+1          -0.47           0.00    1   -1    0    0    1    0    0    0    0    0    0    0    0    0
+1          -0.03           0.43    0    0    1   -1    1    0    0   -1    0    2   -5    0    0    0
+1           0.45           0.00    3    0    2    0    1    0    0    0    0    0    0    0    0    0
+1          -0.44           0.00    0    0    0    4    0    0    0    0    0    0    0    0    0    0
+1          -0.44           0.00    1    0    0    2   -1    0    0    0    0    0    0    0    0    0
+1          -0.44           0.00    1   -1    0    2    0    0    0    0    0    0    0    0    0    0
+1           0.43           0.00    2   -1    2    0    2    0    0    0    0    0    0    0    0    0
+1           0.44           0.00    0    0    0    0    0    0    4   -6    0    0    0    0    0   -2
+1           0.42           0.00    0    0    0    2    2    0    0    0    0    0    0    0    0    0
+1          -0.42           0.00    1    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+1           0.41           0.00    1   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+1          -0.41           0.00    0    0    0    0    0    0    2   -4    0    0    0    0    0   -2
+1           0.02           0.39    0    0    2   -2    1    0   -5    6    0    0    0    0    0    0
+1           0.40           0.00    1    0    2   -4    1    0    0    0    0    0    0    0    0    0
+1          -0.40           0.00    0    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+1          -0.39           0.00    2    0    0   -2    0    0    0   -2    0    2    0    0    0    0
+1           0.39           0.00    0    3    2   -2    2    0    0    0    0    0    0    0    0    0
+1           0.15          -0.24    0    0    0    0    0    0    0    1    0   -2    0    0    0    0
+1          -0.37          -0.01    0    0    0    0    0    0    0    2    0   -2    0    0    0    0
+1           0.37           0.00    1   -1    2    0    1    0    0    0    0    0    0    0    0    0
+1          -0.37           0.00    0    0    2    2    0    0    0    0    0    0    0    0    0    0
+1          -0.37           0.00    2    1    2    0    2    0    0    0    0    0    0    0    0    0
+1          -0.31           0.06    2    0    0   -2    0    0    0   -2    0    3    0    0    0    0
+1          -0.35           0.00    1    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+1           0.35           0.00    0    0    0    1    1    0    0    0    0    0    0    0    0    0
+1          -0.07          -0.27    0    0    0    0    1    0    0   -4    8   -3    0    0    0    0
+1          -0.33           0.01    0    0    0    0    0    0    2    0    0    0    0    0    0    2
+1          -0.33           0.00    2   -1    0    0    0    0    0    0    0    0    0    0    0    0
+1           0.07          -0.26    0    0    0    0    1    0    0    4   -8    3    0    0    0    0
+1           0.33           0.00    0    0    0    0    0    0    0    2   -2    0    0    0    0    0
+1           0.00          -0.32    1    0   -1    0   -2    0    0    0    0    0    0    0    0    0
+1           0.32           0.00    1    1    0    0    1    0    0    0    0    0    0    0    0    0
+1          -0.32           0.00    1    1    2    0    1    0    0    0    0    0    0    0    0    0
+1           0.32           0.00    1    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+1          -0.24          -0.07    0    0    1   -1    1    0    0    0   -2    0    0    0    0    0
+1           0.24           0.07    0    0    1   -1    0    0    0    0   -2    0    0    0    0    0
+1           0.30           0.00    0    0    0    0    0    0    1    1    0    0    0    0    0    2
+1           0.08          -0.22    0    0    0    0    0    0    0    0    0    1    0    0    0    0
+1          -0.30           0.00    1    0    2    1    2    0    0    0    0    0    0    0    0    0
+1          -0.30           0.00    2    0    2    0    0    0    0    0    0    0    0    0    0    0
+1           0.30           0.00    0    1    0   -2    1    0    0    0    0    0    0    0    0    0
+1           0.30           0.00    1    0    2   -1    2    0    0    0    0    0    0    0    0    0
+1           0.00          -0.29    0    0    0    0    0    0    3   -4    0    0    0    0    0    0
+1           0.00          -0.29    1    0   -1    0    0    0    0    0    0    0    0    0    0    0
+1           0.20          -0.09    1    0    0    0    0    0  -18   16    0    0    0    0    0    0
+1           0.29           0.00    1    0    0    1    0    0    0    0    0    0    0    0    0    0
+1          -0.05          -0.24    0    0    0    0    0    0    0    0    0    2    0    0    0    1
+1           0.29           0.00    0    0    0    1   -1    0    0    0    0    0    0    0    0    0
+1          -0.27           0.00    1    0    0   -2    2    0    0    0    0    0    0    0    0    0
+1          -0.19          -0.08    1    0    0    0    0    0  -10    3    0    0    0    0    0    0
+1          -0.27           0.00    1   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+1           0.25           0.00    2    1    0    0    0    0    0    0    0    0    0    0    0    0
+1           0.25           0.00    2    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+1          -0.25           0.00    0    0    2    1    1    0    0    0    0    0    0    0    0    0
+1           0.25           0.00    1    2    0   -2    0    0    0    0    0    0    0    0    0    0
+1          -0.25           0.00    0    3    0    0    0    0    0    0    0    0    0    0    0    0
+1          -0.01           0.23    0    0    0    0    0    0    5   -8    0    0    0    0    0   -2
+1          -0.23           0.00    1    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+1           0.23           0.00    0    0    0    0    0    0    0    1    0    1    0    0    0    2
+1           0.23           0.00    4    0    2    0    2    0    0    0    0    0    0    0    0    0
+1          -0.15          -0.07    0    0    1   -1    1    0    0   -1    0    0   -1    0    0    0
+1          -0.23           0.00    1    0   -2    0    1    0    0    0    0    0    0    0    0    0
+1          -0.22           0.00    2    1    2   -2    2    0    0    0    0    0    0    0    0    0
+1           0.22           0.00    0    1    2    1    2    0    0    0    0    0    0    0    0    0
+1          -0.22           0.00    1    1    2   -2    1    0    0    0    0    0    0    0    0    0
+1          -0.22           0.00    1    0    4   -2    2    0    0    0    0    0    0    0    0    0
+1           0.04          -0.17    0    0    1   -1    1    0    0   -1    0   -1    0    0    0    0
+1          -0.01          -0.21    0    0    2   -2    0    0   -5    6    0    0    0    0    0    0
+1           0.08          -0.14    0    0    0    0    0    0    0    2   -4    0    0    0    0    0
+1          -0.01           0.19    0    0    0    0    0    0    0    0    0    2   -5    0    0    1
+1           0.21           0.00    2    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+1          -0.20           0.00    1    0   -2    1   -1    0    0    0    0    0    0    0    0    0
+1          -0.20           0.00    2   -2    0   -2    0    0    0    0    0    0    0    0    0    0
+1          -0.04          -0.16    0    0    0    0    0    0    8  -13    0    0    0    0    0   -2
+1           0.19           0.00    0    2    0   -2    0    0    0    0    0    0    0    0    0    0
+1           0.19           0.00    1    1    0    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.19           0.00    2    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+1           0.18           0.00    0    0    0    0    0    0    5   -7    0    0    0    0    0   -2
+1          -0.18           0.00    0    1    0    2    1    0    0    0    0    0    0    0    0    0
+1           0.18           0.00    1    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+1           0.17           0.00    2    0    2    2    1    0    0    0    0    0    0    0    0    0
+1          -0.12           0.06    1    0    2    0    2    0    0    1    0    0    0    0    0    0
+1           0.13          -0.04    0    0    0    0    0    0    3   -5    0    0    0    0    0    0
+1          -0.11           0.06    1    0   -2    0   -2    0    0    4   -8    3    0    0    0    0
+1           0.17           0.00    0    0    2   -3    2    0    0    0    0    0    0    0    0    0
+1           0.16           0.00    0    0    0    0    0    0    0    4    0   -2    0    0    0    2
+1          -0.17           0.00    0    0    4    0    2    0    0    0    0    0    0    0    0    0
+1          -0.17           0.00    1    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+1          -0.14           0.02    1    0    0   -1    1    0    0    0    0    0    0    0    0    0
+1           0.14           0.03    0    0    0    0    0    0    0    2    0   -1    0    0    0    2
+1           0.00           0.15    0    0    0    0    0    0    2   -1    0    0    0    0    0    2
+1          -0.15           0.00    1    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+1          -0.14           0.01    0    0    0    0    0    0    0    2    0    1    0    0    0    2
+1           0.16           0.00    2    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+1          -0.06           0.10    0    0    0    0    0    0    8  -11    0    0    0    0    0   -2
+1           0.05           0.10    0    0    0    0    0    0    0    8  -16    4    5    0    0   -2
+1           0.02           0.13    0    0    1   -1    1    0    0   -1    0    2    0    0    0    0
+1          -0.11           0.04    0    0    0    0    0    0    0    8  -16    4    5    0    0    2
+1          -0.12          -0.02    0    0    1   -1    1    0   -5    7    0    0    0    0    0    0
+1          -0.05          -0.10    0    0    0    0    0    0    0    0    0    0    2    0    0    1
+1           0.14           0.00    1    0    2    4    2    0    0    0    0    0    0    0    0    0
+1          -0.09           0.05    1    0    0   -2    0    0   19  -21    3    0    0    0    0    0
+1           0.00           0.14    0    0    1    0    1    0    0    0    0    0    0    0    0    0
+1           0.14           0.00    3    0    0   -4    0    0    0    0    0    0    0    0    0    0
+1          -0.14           0.00    1    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+1           0.04           0.10    0    0    1   -1    1    0    0   -1    0    0    2    0    0    0
+1          -0.06           0.08    0    0    0    0    0    0    0    3    0   -2    0    0    0    2
+1           0.05           0.09    0    0    0    0    0    0    8  -15    0    0    0    0    0   -2
+1          -0.14           0.00    0    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+1           0.08           0.06    0    0    0    0    0    0    0    1    2    0    0    0    0    2
+1           0.14           0.00    0    0    0    4    1    0    0    0    0    0    0    0    0    0
+1           0.14           0.00    0    0    2   -4    1    0    0    0    0    0    0    0    0    0
+1           0.13           0.00    1    1    0   -4    0    0    0    0    0    0    0    0    0    0
+1          -0.07           0.06    0    0    0    0    0    0    0    3   -2    0    0    0    0    2
+1           0.11          -0.02    0    0    0    0    0    0    0    0    0    3    0    0    0    2
+1          -0.13           0.00    3    0    2   -2    1    0    0    0    0    0    0    0    0    0
+1          -0.13           0.00    0    0    0    0    0    0    0    4   -2    0    0    0    0    2
+1          -0.13           0.00    1    1    2    2    2    0    0    0    0    0    0    0    0    0
+1          -0.13           0.00    0    0    4   -2    1    0    0    0    0    0    0    0    0    0
+1          -0.12           0.00    2    0    0   -2    0    0   -3    3    0    0    0    0    0    0
+1           0.12           0.00    3    0    2    2    2    0    0    0    0    0    0    0    0    0
+1           0.12           0.00    0    0    0    0    0    0    0    0    0    0    2    0    0    2
+1          -0.12           0.00    2    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+1           0.00          -0.12    1    0    0   -1    0    0   -3    4    0    0    0    0    0    0
+1          -0.02          -0.09    0    0    0    0    0    0    4   -6    0    0    0    0    0   -1
+1           0.02          -0.09    0    0    0    0    0    0    1   -1    0    0    0    0    0   -1
+1          -0.11           0.00    0    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+1           0.11           0.00    2    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+1           0.07          -0.04    0    0    0    0    0    0    0    3   -4    0    0    0    0    0
+1           0.11           0.00    0    0    2    4    1    0    0    0    0    0    0    0    0    0
+1           0.11           0.00    0    0    0    2   -2    0    0    0    0    0    0    0    0    0
+1          -0.11           0.00    3    0    0    0   -1    0    0    0    0    0    0    0    0    0
+1           0.10           0.00    0    0    0    0    0    0    0    1    0   -3    0    0    0   -2
+1          -0.10           0.00    0    0    2   -2    1    0   -3    3    0    0    0    0    0    0
+1           0.10           0.00    0    0    0    0    0    0    6   -8    0    0    0    0    0   -2
+1           0.10           0.00    2    1    0   -4    0    0    0    0    0    0    0    0    0    0
+1           0.10           0.00    0    0    1   -1    1    0    0    3   -8    3    0    0    0    0
+1           0.00           0.10    0    0    0    0    0    0    3   -2    0    0    0    0    0    2
+1           0.00           0.10    0    0    0    0    0    0    2   -5    0    0    0    0    0   -2
+1          -0.10           0.00    4    0    2   -2    2    0    0    0    0    0    0    0    0    0
+1           0.10           0.00    2    0    0    2    1    0    0    0    0    0    0    0    0    0
+1          -0.10           0.00    1   -1    0   -2   -1    0    0    0    0    0    0    0    0    0
+1           0.10           0.00    0    0    0    0    0    0    1   -3    0    0    0    0    0   -2
+2         121.15       -2301.27    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+2          -0.98        -143.27    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+2          -0.27         -24.46    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+2           0.24          22.41    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+2          -1.19          -5.61    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+2           3.57          -1.83    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+2           0.24          -5.02    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+2          -0.04          -3.23    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+2          -0.48           2.40    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+2          -0.10           1.73    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+2          -0.01           1.33    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+2          -0.04           0.83    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+2          -0.05          -0.79    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+2           0.03          -0.66    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+2           0.00          -0.64    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+2           0.04           0.61    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+2          -0.01          -0.41    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+2          -0.01           0.35    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+2          -0.01          -0.33    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+2           0.01           0.31    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+2           0.01           0.27    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+2          -0.07          -0.17    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+2           0.07           0.17    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+2           0.02          -0.21    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+2           0.01           0.20    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+2           0.01          -0.17    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+2           0.01          -0.16    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+2           0.00          -0.13    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+2          -0.07          -0.04    0    0    1   -1    1    0    0   -1    0   -2    5    0    0    0
+2           0.02           0.08    0    1    2    0    2    0    0    0    0    0    0    0    0    0
+3         -15.23          -1.62    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+3          -1.16          -0.01    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+3          -0.20           0.00    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+3           0.18           0.00    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+3           0.13           0.00    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+4          -0.01           0.11    0    0    0    0    1    0    0    0    0    0    0    0    0    0
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2b.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2b.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2b.txt	(revision 22322)
@@ -0,0 +1,1325 @@
+Expression for the Y coordinate of the CIP in the GCRS based on the IAU2000A 
+precession-nutation model 
+
+--------------------------------------------------------------------------------------------------------------
+
+Y = polynomial part + non-polynomial part
+
+--------------------------------------------------------------------------------------------------------------
+
+Polynomial part (unit microarcsecond)
+
+  -6950.78 - 25381.99 t - 22407250.99 t^2 + 1842.28 t^3 + 1113.06 t^4 + 0.99 t^5
+
+--------------------------------------------------------------------------------------------------------------
+
+Non-polynomial part (unit microarcsecond)
+(ARG being for various combination of the fundamental arguments of the nutation theory)
+
+  Sum_i[b_{c,0})_i * cos(ARG) + b_{s,0})_i * sin(ARG)] 
+
++ Sum_i)j=1,4 [b_{c,j})_i * t^j * cos(ARG) + b_{s,j})_i * sin(ARG)] * t^j]
+
+The Table below provides the values for b_{s,j})_i and b_{c,j})_i
+
+The expressions for the fundamental arguments appearing in columns 4 to 8 (luni-solar part) 
+and in columns 6 to 17 (planetary part) are those of the IERS Conventions 2000
+
+--------------------------------------------------------------------------------------------------------------
+
+    i    b_{s,j})_i      b_{c,j})_i    l    l'   F    D   Om L_Me L_Ve  L_E L_Ma  L_J L_Sa  L_U L_Ne  p_A
+
+--------------------------------------------------------------------------------------------------------------
+
+j = 0  Nb of terms = 962
+
+    1        1538.18     9205236.26    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+    2        -458.66      573033.42    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+    3         137.41       97846.69    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+    4         -29.05      -89618.24    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+    5         -17.40       22438.42    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+    6          31.80       20069.50    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+    7          36.70       12902.66    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+    8         -13.20       -9592.72    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+    9        -192.40        7387.02    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+   10           3.92       -6918.22    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+   11           0.40       -5331.13    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+   12          -0.90       -3323.89    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+   13           7.50        3143.98    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+   14           7.80        2636.13    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+   15          -6.60        2554.51    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+   16          -2.00       -2423.59    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+   17           6.80        1645.01    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+   18           0.00       -1387.00    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+   19           5.90        1323.81    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+   20          -0.30       -1233.89    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+   21           0.30       -1075.60    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+   22          -4.48         852.85    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+   23           0.10        -800.34    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+   24          35.80        -674.99    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+   25          -1.40         695.54    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+   26          -0.50         684.99    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+   27          -2.62         643.75    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+   28          -1.50         522.11    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+   29         273.50         164.70    0    0    1   -1    1    0    0   -1    0   -2    5    0    0    0
+   30           1.40         335.24    0    0    2    2    1    0    0    0    0    0    0    0    0    0
+   31           1.90         326.60    1    0    2    2    2    0    0    0    0    0    0    0    0    0
+   32           0.40         327.11    0    0    0    2    1    0    0    0    0    0    0    0    0    0
+   33          -0.50        -325.03    0    1    2    0    2    0    0    0    0    0    0    0    0    0
+   34          -0.40         307.03    0    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+   35           0.50         304.17    2    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+   36          -0.10        -304.46    1    0    2   -2    1    0    0    0    0    0    0    0    0    0
+   37          -0.40        -276.81    2    0    2   -2    2    0    0    0    0    0    0    0    0    0
+   38           0.90         272.05    0    0    0    2   -1    0    0    0    0    0    0    0    0    0
+   39           0.30         272.22    0    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+   40           1.20         269.45    2    0    2    0    1    0    0    0    0    0    0    0    0    0
+   41           0.10        -220.67    2    0    0   -2    1    0    0    0    0    0    0    0    0    0
+   42         128.60         -77.10    0    0    0    0    0    0    0    0    0    2   -5    0    0   -1
+   43           0.10        -190.79    0    1    2   -2    1    0    0    0    0    0    0    0    0    0
+   44         167.90           0.00    0    1   -1    1   -1    0    0    0    0    0    0    0    0    0
+   45          -8.20        -123.48    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+   46           0.10         131.04    2    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+   47           0.40         126.64    2    0    0    0   -1    0    0    0    0    0    0    0    0    0
+   48           2.90        -122.28    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+   49           0.70         123.20    3    0    2    0    2    0    0    0    0    0    0    0    0    0
+   50           0.40         123.20    1   -1    2    0    2    0    0    0    0    0    0    0    0    0
+   51          -0.30         120.70    1    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+   52          -0.50         112.90    0    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+   53          -0.20        -112.94    2    0    0    0    1    0    0    0    0    0    0    0    0    0
+   54           0.20         107.31    1    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+   55          -0.30        -106.20    1    1    2    0    2    0    0    0    0    0    0    0    0    0
+   56          31.90         -64.10    0    0    1   -1    1    0   -8   12    0    0    0    0    0    0
+   57           0.00          89.50    0    0    0    0    0    0    3   -5    0    0    0    0    0   -2
+   58          89.10           0.00    1    0   -1    0   -1    0    0    0    0    0    0    0    0    0
+   59           0.00          85.32    1    0    0    0    2    0    0    0    0    0    0    0    0    0
+   60          -0.20         -71.00    0    0    2    1    2    0    0    0    0    0    0    0    0    0
+   61           0.00         -70.01    1   -1    0   -1   -1    0    0    0    0    0    0    0    0    0
+   62          13.90         -55.30    1    0    0   -1    0    0    0    0    0    0    0    0    0    0
+   63           0.00          67.25    0    2   -2    2   -1    0    0    0    0    0    0    0    0    0
+   64           0.40          66.29    1    0    2    2    1    0    0    0    0    0    0    0    0    0
+   65          -0.40          64.70    1    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+   66           1.30         -60.90    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+   67          -0.20         -60.92    1    0    0    0   -2    0    0    0    0    0    0    0    0    0
+   68           0.20         -59.20    2    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+   69           1.10         -55.55    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+   70           0.00         -55.60    1    1    2   -2    2    0    0    0    0    0    0    0    0    0
+   71          -0.10         -52.69    2    0    2   -2    1    0    0    0    0    0    0    0    0    0
+   72          -0.20          51.80    2    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+   73           1.00         -49.51    1    0    0   -1   -1    0    0    0    0    0    0    0    0    0
+   74           0.00          50.50    0    0    0    0    0    0    0    0    0    2    0    0    0    2
+   75           2.50          47.70    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+   76           0.10          49.59    1    0    0    2    1    0    0    0    0    0    0    0    0    0
+   77           0.10         -49.00    1    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+   78         -23.20          24.60    0    0    0    0    1    0    0   -1    2    0    0    0    0    0
+   79           0.40          46.50    2    0    2    2    2    0    0    0    0    0    0    0    0    0
+   80          -0.10         -44.04    0    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+   81          -0.10         -42.19    0    1    2    0    1    0    0    0    0    0    0    0    0    0
+   82          13.30          26.90    0    0    0    0    0    0    8  -13    0    0    0    0    0   -1
+   83          -0.10         -39.90    3    0    2   -2    2    0    0    0    0    0    0    0    0    0
+   84          -0.10         -39.50    0    0    4   -2    2    0    0    0    0    0    0    0    0    0
+   85           0.00         -39.11    1    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+   86          -0.10         -38.92    2    0   -2    0    1    0    0    0    0    0    0    0    0    0
+   87           0.10          36.95    0    0    2    0   -1    0    0    0    0    0    0    0    0    0
+   88          -0.10          34.59    0    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+   89           0.20         -32.55    0    1    0    0    2    0    0    0    0    0    0    0    0    0
+   90          -0.10          31.61    1    1    0   -2    1    0    0    0    0    0    0    0    0    0
+   91           0.00          30.40    0    0    2   -1    2    0    0    0    0    0    0    0    0    0
+   92           0.20          29.40    0    0    2    4    2    0    0    0    0    0    0    0    0    0
+   93           0.00         -27.91    1   -1    0    0    1    0    0    0    0    0    0    0    0    0
+   94           0.10          27.50    1    0    0    2   -1    0    0    0    0    0    0    0    0    0
+   95         -25.70          -1.70    0    0    1   -1    1    0    0   -1    0    2   -5    0    0    0
+   96          19.90           5.90    0    0    0    0    0    0    0    2   -8    3    0    0    0   -2
+   97           0.00          25.80    1   -1    0   -1   -2    0    0    0    0    0    0    0    0    0
+   98           0.20          25.20    1   -1    2    2    2    0    0    0    0    0    0    0    0    0
+   99           0.00         -25.31    0    1    0    0   -2    0    0    0    0    0    0    0    0    0
+  100           0.20          25.00    3    0    2    0    1    0    0    0    0    0    0    0    0    0
+  101          -0.10          24.40    1    0    2   -4    1    0    0    0    0    0    0    0    0    0
+  102           0.10         -24.40    1   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  103         -23.30           0.90    0    0    2   -2    1    0   -5    6    0    0    0    0    0    0
+  104          -0.10          24.00    1    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  105         -18.00          -5.30    0    0    0    0    0    0    0    6   -8    3    0    0    0    2
+  106          -0.10         -22.80    0    1    2    2    2    0    0    0    0    0    0    0    0    0
+  107          -0.10          22.50    0    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  108           0.10          21.60    1   -1    2    0    1    0    0    0    0    0    0    0    0    0
+  109           0.00         -21.30    0    0    0    0    0    0    4   -6    0    0    0    0    0   -2
+  110           0.10          20.70    2   -1    2    0    2    0    0    0    0    0    0    0    0    0
+  111           0.70         -20.10    0    0    0    0    0    0    0    3    0   -1    0    0    0    2
+  112           0.00          20.51    0    0    0    1    1    0    0    0    0    0    0    0    0    0
+  113          15.90          -4.50    0    0    0    0    1    0    0   -4    8   -3    0    0    0    0
+  114           0.20         -19.94    1    0    0    2    0    0    0    0    0    0    0    0    0    0
+  115           0.00          20.11    0    0    0    2    2    0    0    0    0    0    0    0    0    0
+  116          15.60           4.40    0    0    0    0    1    0    0    4   -8    3    0    0    0    0
+  117           0.00         -20.00    1   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+  118           0.00          19.80    0    0    0    0    0    0    2   -4    0    0    0    0    0   -2
+  119           0.00          18.91    1    1    0    0    1    0    0    0    0    0    0    0    0    0
+  120           4.30         -14.60    0    0    1   -1    1    0    0    0   -2    0    0    0    0    0
+  121          -0.10         -18.50    1    1    2    0    1    0    0    0    0    0    0    0    0    0
+  122          -0.10          18.40    0    1    0   -2    1    0    0    0    0    0    0    0    0    0
+  123           0.00          18.10    0    3    2   -2    2    0    0    0    0    0    0    0    0    0
+  124           1.00          16.81    0    2    0    0    0    0    0    0    0    0    0    0    0    0
+  125          -0.10         -17.60    2    1    2    0    2    0    0    0    0    0    0    0    0    0
+  126         -17.60           0.00    1    0   -1    0   -2    0    0    0    0    0    0    0    0    0
+  127          -1.30         -16.26    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+  128           0.00         -17.41    0    0    0    1   -1    0    0    0    0    0    0    0    0    0
+  129          14.50          -2.70    0    0    0    0    0    0    0    0    0    2    0    0    0    1
+  130           0.00          17.08    1    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+  131           0.00          16.21    1   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+  132           0.00         -16.00    0    0    0    0    0    0    2    0    0    0    0    0    0    2
+  133           0.00         -15.31    1    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+  134           0.00         -15.10    2    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+  135           0.00          14.70    0    0    0    0    0    0    1    1    0    0    0    0    0    2
+  136           0.00          14.40    1    0    2   -1    2    0    0    0    0    0    0    0    0    0
+  137          -0.10         -14.30    1    0    2    1    2    0    0    0    0    0    0    0    0    0
+  138           0.00         -14.40    0    0    2    1    1    0    0    0    0    0    0    0    0    0
+  139           0.00         -13.81    1    0   -2    0    1    0    0    0    0    0    0    0    0    0
+  140           4.50          -9.30    0    0    1   -1    1    0    0   -1    0    0   -1    0    0    0
+  141         -13.80           0.00    0    0    1    0    1    0    0    0    0    0    0    0    0    0
+  142           0.00         -13.38    1    0    0   -2    2    0    0    0    0    0    0    0    0    0
+  143          -0.10          13.10    1    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  144          10.30           2.70    0    0    1   -1    1    0    0   -1    0   -1    0    0    0    0
+  145           0.00          12.80    1    0   -2    1   -1    0    0    0    0    0    0    0    0    0
+  146           0.00         -12.80    1    1    2   -2    1    0    0    0    0    0    0    0    0    0
+  147          11.70           0.80    0    0    0    0    0    0    0    0    0    2   -5    0    0    1
+  148           0.00         -12.00    2    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  149          11.30           0.50    0    0    0    0    0    0    5   -8    0    0    0    0    0   -2
+  150           0.00          11.40    0    0    0    0    0    0    0    1    0    1    0    0    0    2
+  151           0.00         -11.20    1    1    0    0   -1    0    0    0    0    0    0    0    0    0
+  152           0.10          10.90    4    0    2    0    2    0    0    0    0    0    0    0    0    0
+  153           0.10         -10.77    1    0    2    0    0    0    0    0    0    0    0    0    0    0
+  154           0.00         -10.80    2    1    2   -2    2    0    0    0    0    0    0    0    0    0
+  155          -0.20          10.47    2    0   -2    0    0    0    0    0    0    0    0    0    0    0
+  156           0.00          10.50    2    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  157           0.00         -10.40    1    0    4   -2    2    0    0    0    0    0    0    0    0    0
+  158           0.00          10.40    0    1    2    1    2    0    0    0    0    0    0    0    0    0
+  159           0.00         -10.20    0    1    0    2    1    0    0    0    0    0    0    0    0    0
+  160           0.00         -10.00    1    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+  161           0.00           9.60    1    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+  162           0.10           9.40    2    0    2    2    1    0    0    0    0    0    0    0    0    0
+  163          -7.60           1.70    0    0    0    0    0    0    8  -13    0    0    0    0    0   -2
+  164          -7.70           1.40    0    0    1   -1    1    0    0   -1    0    2    0    0    0    0
+  165           1.40          -7.50    0    0    1   -1    1    0   -5    7    0    0    0    0    0    0
+  166           6.10          -2.70    0    0    0    0    0    0    0    0    0    0    2    0    0    1
+  167           0.00          -8.70    0    0    0    0    0    0    5   -7    0    0    0    0    0   -2
+  168          -5.90           2.60    0    0    1   -1    1    0    0   -1    0    0    2    0    0    0
+  169           0.00           8.40    0    0    2   -4    1    0    0    0    0    0    0    0    0    0
+  170          -0.20          -8.11    0    1    0   -2    0    0    0    0    0    0    0    0    0    0
+  171          -2.60          -5.70    1    0    2    0    2    0    0    1    0    0    0    0    0    0
+  172           0.00           8.30    0    0    2   -3    2    0    0    0    0    0    0    0    0    0
+  173           2.70           5.50    1    0   -2    0   -2    0    0    4   -8    3    0    0    0    0
+  174           4.20          -4.00    0    0    0    0    0    0    0    8  -16    4    5    0    0   -2
+  175          -0.10           8.00    0    0    0    0    0    0    0    4    0   -2    0    0    0    2
+  176           0.00           8.09    0    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+  177          -1.30           6.70    0    0    0    0    0    0    0    2    0   -1    0    0    0    2
+  178           0.00          -7.90    0    0    4    0    2    0    0    0    0    0    0    0    0    0
+  179           0.00           7.80    0    0    0    4    1    0    0    0    0    0    0    0    0    0
+  180          -7.50          -0.20    0    0    0    0    0    0    2   -1    0    0    0    0    0    2
+  181          -0.50          -7.20    0    0    0    0    0    0    0    2    0    1    0    0    0    2
+  182           4.90           2.70    0    0    0    0    0    0    8  -11    0    0    0    0    0   -2
+  183           0.00           7.50    1    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  184           0.00          -7.50    3    0    2   -2    1    0    0    0    0    0    0    0    0    0
+  185           0.00          -7.49    2    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+  186           0.00          -7.20    0    0    4   -2    1    0    0    0    0    0    0    0    0    0
+  187           0.10           6.90    1    0    2    4    2    0    0    0    0    0    0    0    0    0
+  188          -5.60           1.40    0    0    0    0    0    0    4   -6    0    0    0    0    0   -1
+  189          -5.70          -1.30    0    0    0    0    0    0    1   -1    0    0    0    0    0   -1
+  190           0.00           6.90    2    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+  191           4.20          -2.70    0    0    0    0    0    0    8  -15    0    0    0    0    0   -2
+  192           0.00           6.90    1    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+  193          -3.10           3.70    0    0    0    0    0    0    0    1    2    0    0    0    0    2
+  194          -3.90          -2.90    0    0    0    0    0    0    0    3    0   -2    0    0    0    2
+  195           0.00           6.60    3    0    0    0   -1    0    0    0    0    0    0    0    0    0
+  196          -3.10          -3.50    0    0    0    0    0    0    0    3   -2    0    0    0    0    2
+  197           1.10          -5.39    1    0    0   -1    1    0    0    0    0    0    0    0    0    0
+  198           0.00          -6.40    2    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+  199           0.90           5.50    0    0    0    0    0    0    0    0    0    3    0    0    0    2
+  200           0.00          -6.30    0    0    2   -2    1    0   -3    3    0    0    0    0    0    0
+  201          -0.10          -6.20    0    0    0    0    0    0    0    4   -2    0    0    0    0    2
+  202           0.00          -6.10    1    1    2    2    2    0    0    0    0    0    0    0    0    0
+  203           0.00           6.10    0    0    1   -1    1    0    0    3   -8    3    0    0    0    0
+  204           0.00           6.10    0    0    2   -1    1    0    0    0    0    0    0    0    0    0
+  205           3.50          -2.50    0    0    0    0    1    0    8  -13    0    0    0    0    0    0
+  206           0.00           6.00    0    0    2    4    1    0    0    0    0    0    0    0    0    0
+  207           0.00           5.90    1   -1    0   -2   -1    0    0    0    0    0    0    0    0    0
+  208          -0.90          -4.80    2    0    0   -2    1    0    0   -2    0    3    0    0    0    0
+  209           0.00           5.70    1   -1   -2    2   -1    0    0    0    0    0    0    0    0    0
+  210           0.10           5.60    3    0    2    2    2    0    0    0    0    0    0    0    0    0
+  211           0.00           5.70    0    0    0    0    0    0    0    0    0    0    2    0    0    2
+  212           0.00           5.70    2    0    0    2    1    0    0    0    0    0    0    0    0    0
+  213           0.00           5.60    2    0    0   -4    1    0    0    0    0    0    0    0    0    0
+  214           0.00           5.60    1    0    0   -4    1    0    0    0    0    0    0    0    0    0
+  215           0.20          -5.40    2    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+  216          -0.90          -4.70    0    0    0    0    0    0    5   -8    0    0    0    0    0   -1
+  217          -0.40          -5.10    1    1    0   -2    0    0    0    0    0    0    0    0    0    0
+  218           0.00           5.50    0    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  219           0.00          -5.40    2    0    2   -4    1    0    0    0    0    0    0    0    0    0
+  220           0.00          -5.40    0    0    0    0    0    0    0    8  -16    4    5    0    0    2
+  221           1.80           3.60    1    0    0    0   -1    0  -18   16    0    0    0    0    0    0
+  222           0.00           5.30    1   -1    0   -2   -2    0    0    0    0    0    0    0    0    0
+  223           0.00          -5.30    0    0    1   -1    1    0    0   -5    8   -3    0    0    0    0
+  224           0.00          -5.20    0    0    0    0    0    0    0    1    0   -3    0    0    0   -2
+  225           0.00          -5.19    0    0    0    2   -2    0    0    0    0    0    0    0    0    0
+  226           3.00           2.10    0    0    0    0    1    0   -8   13    0    0    0    0    0    0
+  227           0.00          -5.10    0    0    0    0    0    0    6   -8    0    0    0    0    0   -2
+  228           0.00           5.07    0    2    0    0    1    0    0    0    0    0    0    0    0    0
+  229           0.90          -4.10    0    0    0    0    1    0    0    0    0   -2    5    0    0    0
+  230          -5.00           0.00    0    0    0    0    0    0    3   -2    0    0    0    0    0    2
+  231           0.00          -5.00    3    0    0    0    0    0    0    0    0    0    0    0    0    0
+  232           0.00           5.00    1   -1    2    2    1    0    0    0    0    0    0    0    0    0
+  233           0.00          -5.00    1   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+  234           0.00          -4.90    4    0    2   -2    2    0    0    0    0    0    0    0    0    0
+  235           4.90           0.00    0    0    0    0    0    0    2   -5    0    0    0    0    0   -2
+  236           0.00          -4.90    0    0    0    0    0    0    1   -3    0    0    0    0    0   -2
+  237           0.90           3.90    0    0    0    0    1    0    0    0    0    2   -5    0    0    0
+  238           0.00           4.80    2    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  239          -3.70          -1.10    0    0    0    0    1    0    0    0    0    1    0    0    0    0
+  240           0.00          -4.72    0    2    2   -2    1    0    0    0    0    0    0    0    0    0
+  241           0.00           4.71    1    0    2    0   -1    0    0    0    0    0    0    0    0    0
+  242           0.00          -4.50    2    1    0   -2    1    0    0    0    0    0    0    0    0    0
+  243          -1.50          -3.00    1    0    0    0    1    0  -18   16    0    0    0    0    0    0
+  244           0.00          -4.50    2   -1   -2    0   -1    0    0    0    0    0    0    0    0    0
+  245           0.30          -4.11    1   -1    0    0    0    0    0    0    0    0    0    0    0    0
+  246           0.00           4.40    1    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  247           0.00          -4.40    1   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  248           0.00           4.39    2    0    0   -2    2    0    0    0    0    0    0    0    0    0
+  249           0.00          -4.30    3    0    0    0    1    0    0    0    0    0    0    0    0    0
+  250           0.00           4.30    2    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+  251           0.00          -4.30    0    1    2    2    1    0    0    0    0    0    0    0    0    0
+  252           0.20           4.03    1    0   -2    0    0    0    0    0    0    0    0    0    0    0
+  253           0.20           4.00    0    2   -2    0   -2    0    0    0    0    0    0    0    0    0
+  254          -0.60           3.50    0    0    1   -1    1    0    0   -1    0    0    1    0    0    0
+  255           0.00           4.10    3    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+  256           0.00           4.00    1    0   -4    2   -2    0    0    0    0    0    0    0    0    0
+  257           0.00          -4.00    0    1   -2    1   -2    0    0    0    0    0    0    0    0    0
+  258           0.00          -3.91    1    0    0   -4    0    0    0    0    0    0    0    0    0    0
+  259           1.90           2.00    0    0    0    0    1    0    0    1   -2    0    0    0    0    0
+  260           0.00           3.90    2   -1    2    2    2    0    0    0    0    0    0    0    0    0
+  261           0.00           3.90    0    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  262           0.00          -3.90    1    0   -2   -3   -2    0    0    0    0    0    0    0    0    0
+  263           3.10          -0.80    0    0    0    0    0    0    2   -4    0    0    0    0    0   -1
+  264           0.00           3.90    2   -1    2    0    1    0    0    0    0    0    0    0    0    0
+  265           0.00           3.90    2    0    0    0    2    0    0    0    0    0    0    0    0    0
+  266           0.00           3.80    0    0    0    0    0    0    0    2    0    0    0    0    0    2
+  267          -0.20           3.51    1    1    0    0    0    0    0    0    0    0    0    0    0    0
+  268           0.00          -3.60    1    0   -2   -1   -2    0    0    0    0    0    0    0    0    0
+  269          -2.10           1.50    0    0    0    0    0    0    0    1    0    2    0    0    0    2
+  270           0.00          -3.60    1    1   -2    1   -1    0    0    0    0    0    0    0    0    0
+  271           0.70           2.80    0    0    0    0    0    0    3   -7    0    0    0    0    0   -2
+  272          -2.80           0.70    0    0    0    0    0    0    0    0    0    1    0    0    0   -1
+  273           0.00          -3.50    0    0    0    0    0    0    0    2    0    0    0    0    0    0
+  274          -2.90          -0.60    0    0    0    0    0    0    2   -2    0    0    0    0    0   -1
+  275           0.00          -3.40    1   -1    0   -2    1    0    0    0    0    0    0    0    0    0
+  276           0.00           3.40    2    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+  277           0.00           3.36    0    0    2    0    3    0    0    0    0    0    0    0    0    0
+  278           0.50           2.80    2    0    0   -2   -1    0    0   -2    0    3    0    0    0    0
+  279           2.60          -0.70    0    0    0    0    0    0    3   -5    0    0    0    0    0   -1
+  280           1.00          -2.30    0    0    0    0    0    0    0    0    0    1    0    0    0    2
+  281           0.00          -3.30    2    1    2    0    1    0    0    0    0    0    0    0    0    0
+  282           0.00           3.30    1    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+  283           0.00           3.23    0    2    0    0   -1    0    0    0    0    0    0    0    0    0
+  284           0.00           3.20    1   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+  285           0.00          -3.20    0    2    2    0    2    0    0    0    0    0    0    0    0    0
+  286           0.00          -3.20    0    0    0    0    0    0    7   -9    0    0    0    0    0   -2
+  287           0.00           3.20    0    0    0    0    0    0    0    0    2    0    0    0    0    2
+  288           2.90          -0.30    0    0    0    0    0    0    0    0    0    1    0    0    0    1
+  289           0.08           3.05    0    0    2   -2    3    0    0    0    0    0    0    0    0    0
+  290          -0.70          -2.40    0    0    0    0    0    0    3   -3    0    0    0    0    0    2
+  291           0.00          -3.08    1    0    2   -4    2    0    0    0    0    0    0    0    0    0
+  292           0.00           3.00    2    0    2   -1    2    0    0    0    0    0    0    0    0    0
+  293          -1.60           1.40    1    0    0   -1    1    0    0   -1    0    2    0    0    0    0
+  294          -2.90          -0.10    0    0    0    0    0    0    4   -7    0    0    0    0    0   -2
+  295           0.00          -2.90    2    0    0   -2    1    0    0   -2    0    2    0    0    0    0
+  296          -2.50           0.40    0    0    1   -1    1    0    0   -2    2    0    0    0    0    0
+  297           0.40          -2.50    0    0    0    0    0    0    0    0    0    0    1    0    0    1
+  298           0.00          -2.90    1    0    2    1    1    0    0    0    0    0    0    0    0    0
+  299           0.00           2.89    0    1   -2    2    0    0    0    0    0    0    0    0    0    0
+  300           0.00          -2.80    2    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  301          -2.50           0.30    0    0    1   -1    1    0    0   -1    0    1    0    0    0    0
+  302          -2.50          -0.30    0    0    0    0    0    0    0    1   -2    0    0    0    0   -1
+  303           0.00          -2.70    0    0    2   -2    1    0    0   -2    0    2    0    0    0    0
+  304           2.70           0.00    1    0   -1    0   -3    0    0    0    0    0    0    0    0    0
+  305           0.00          -2.60    0    1    0    1    1    0    0    0    0    0    0    0    0    0
+  306           0.00          -2.60    0    1    0    2   -1    0    0    0    0    0    0    0    0    0
+  307           0.00           2.60    3   -1    2    0    2    0    0    0    0    0    0    0    0    0
+  308           2.10           0.50    0    0    1   -1    1    0   -3    4    0    0    0    0    0    0
+  309           0.00           2.50    0    1    0    1   -1    0    0    0    0    0    0    0    0    0
+  310           0.80           1.70    0    0    0    0    0    0    0    0    0    0    1    0    0   -1
+  311           1.90          -0.60    0    0    0    0    0    0    0    6  -16    4    5    0    0   -2
+  312           0.00          -2.50    2    0    0    0   -2    0    0    0    0    0    0    0    0    0
+  313           0.00          -2.40    2    0    0    2    0    0    0    0    0    0    0    0    0    0
+  314           0.00           2.40    2    0    0    2   -1    0    0    0    0    0    0    0    0    0
+  315           0.00          -2.40    1    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+  316           0.00           2.40    1    0    2   -3    1    0    0    0    0    0    0    0    0    0
+  317          -1.90           0.50    0    0    0    0    0    0    5   -7    0    0    0    0    0   -1
+  318          -0.10          -2.30    2    0    0   -4    0    0    0    0    0    0    0    0    0    0
+  319           0.00           2.30    1    1    2   -4    1    0    0    0    0    0    0    0    0    0
+  320           0.00          -2.30    3    1    2    0    2    0    0    0    0    0    0    0    0    0
+  321          -1.40           0.90    0    0    0    0    0    0    0    0    4    0    0    0    0    2
+  322          -0.10          -2.20    0    0    0    0    0    0    8  -10    0    0    0    0    0   -2
+  323          -0.20          -2.00    0    0    0    1    0    0    0    0    0    0    0    0    0    0
+  324           0.00           2.20    1   -2    2    0    2    0    0    0    0    0    0    0    0    0
+  325           0.00          -2.20    2   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+  326           0.00           2.20    1    1    2    1    2    0    0    0    0    0    0    0    0    0
+  327           0.00           2.20    0    0    0    0    0    0    0    2   -4    0    0    0    0   -2
+  328          -1.80          -0.40    0    0    0    0    0    0    3   -3    0    0    0    0    0   -1
+  329           0.00           2.20    0    1    2    1    1    0    0    0    0    0    0    0    0    0
+  330           0.00           2.20    4    0    2    0    1    0    0    0    0    0    0    0    0    0
+  331          -1.70           0.40    0    0    0    0    0    0    1   -1    0    0    0    0    0    1
+  332          -0.80          -1.30    0    0    0    0    0    0    0    5   -4    0    0    0    0    2
+  333          -1.30          -0.80    0    0    0    0    0    0    0    4   -4    0    0    0    0    2
+  334           0.00           2.10    1    0   -2    1   -2    0    0    0    0    0    0    0    0    0
+  335           0.00           2.10    1   -1    2   -2    1    0    0    0    0    0    0    0    0    0
+  336           0.00          -2.10    1    0    4   -2    1    0    0    0    0    0    0    0    0    0
+  337           0.00          -2.10    2    1    2   -2    1    0    0    0    0    0    0    0    0    0
+  338           0.00           2.10    1    0    2   -1    1    0    0    0    0    0    0    0    0    0
+  339           0.00          -2.00    1    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+  340           0.00           2.00    1    0   -2    2    1    0    0    0    0    0    0    0    0    0
+  341           0.00           2.00    1   -1    0    2   -1    0    0    0    0    0    0    0    0    0
+  342           0.00           2.00    2   -2    0   -2   -1    0    0    0    0    0    0    0    0    0
+  343           0.00          -2.00    1    2    2   -2    2    0    0    0    0    0    0    0    0    0
+  344           2.00           0.00    0    0    0    0    0    0    6   -9    0    0    0    0    0   -2
+  345           1.10          -0.90    0    0    0    0    0    0    0    0    0    2   -5    0    0   -2
+  346           1.60          -0.40    0    0    0    0    0    0    1   -3    0    0    0    0    0   -1
+  347           0.00          -1.91    1    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+  348           0.00          -1.90    0    0    0    4    0    0    0    0    0    0    0    0    0    0
+  349           0.00           1.90    0    0    4   -4    2    0    0    0    0    0    0    0    0    0
+  350           0.00          -1.90    3    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  351           0.00           1.90    2   -1    0   -2    1    0    0    0    0    0    0    0    0    0
+  352           1.50           0.40    0    0    1   -1    1    0   -4    5    0    0    0    0    0    0
+  353          -1.50          -0.40    0    0    0    0    0    0    0    2    0    0    0    0    0    1
+  354          -1.40          -0.50    0    0    1   -1    1    0    0   -1    0   -4   10    0    0    0
+  355          -1.00           0.90    0    0    1   -1    1    0    0   -1    0    0    0    0    2    0
+  356           0.00          -1.90    2    0    2    1    2    0    0    0    0    0    0    0    0    0
+  357          -0.30           1.60    0    0    0    0    0    0    0    4    0   -3    0    0    0    2
+  358           0.00           1.90    0    0    0    4   -1    0    0    0    0    0    0    0    0    0
+  359           0.00           1.90    1   -1    0    2    1    0    0    0    0    0    0    0    0    0
+  360           0.00          -1.80    2   -1    0    0    1    0    0    0    0    0    0    0    0    0
+  361           0.00          -1.80    2    0   -4   -2   -2    0    0    0    0    0    0    0    0    0
+  362          -1.10           0.70    0    0    0    0    0    0    0    2   -4    0    0    0    0   -1
+  363           0.20          -1.60    0    0    0    0    0    0    2   -3    0    0    0    0    0   -1
+  364           0.00           1.80    2    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+  365           0.00          -1.71    0    0    2    2    0    0    0    0    0    0    0    0    0    0
+  366          -1.20          -0.50    0    0    2   -2    1    0    0   -9   13    0    0    0    0    0
+  367           1.50           0.20    0    0    1    1    1    0    0    1    0    0    0    0    0    0
+  368          -0.60          -1.10    1    0    2    0    1    0    0   -2    0    3    0    0    0    0
+  369           0.60           1.10    1    0   -2    0   -1    0    0   -1    0    0    0    0    0    0
+  370          -0.60          -1.10    0    0    0    0    0    0    0    4   -3    0    0    0    0    2
+  371          -1.10           0.60    0    0    0    0    1    0    0   -2    4    0    0    0    0    0
+  372          -1.70           0.00    0    0    0    0    1    0    2   -3    0    0    0    0    0    0
+  373           0.00           1.60    0    1   -2    2    1    0    0    0    0    0    0    0    0    0
+  374           0.00          -1.60    2    0    4   -2    2    0    0    0    0    0    0    0    0    0
+  375           0.00          -1.60    0    0    2    3    2    0    0    0    0    0    0    0    0    0
+  376           1.20          -0.40    0    0    0    0    0    0    0    0    0    3    0    0    0    1
+  377          -0.50          -1.10    2    0    2    0    2    0    0    2    0   -3    0    0    0    0
+  378           0.60           1.00    0    0    0    0    0    0    0    3   -6    0    0    0    0   -2
+  379          -1.30          -0.30    0    0    0    0    0    0    4   -4    0    0    0    0    0   -1
+  380           0.30          -1.30    0    0    0    0    0    0    0    1   -2    0    0    0    0    1
+  381           0.00           1.60    1    0    0    4    1    0    0    0    0    0    0    0    0    0
+  382           0.00          -1.60    1    1    0    2    1    0    0    0    0    0    0    0    0    0
+  383           0.00          -1.60    0    0    4    0    1    0    0    0    0    0    0    0    0    0
+  384           1.10          -0.50    0    0    1   -1    2    0    0   -1    0    0    2    0    0    0
+  385           0.00          -1.50    1   -1    0    2    0    0    0    0    0    0    0    0    0    0
+  386           0.00          -1.50    0    1    2   -2   -1    0    0    0    0    0    0    0    0    0
+  387           0.00           1.50    2   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+  388           0.00          -1.50    1    0    0    1   -1    0    0    0    0    0    0    0    0    0
+  389           0.00          -1.50    1    2    0   -2   -1    0    0    0    0    0    0    0    0    0
+  390           1.50           0.00    1    0    0   -1    1    0   -3    4    0    0    0    0    0    0
+  391           0.00          -1.50    0    1    4   -2    2    0    0    0    0    0    0    0    0    0
+  392           1.30          -0.20    0    0    0    0    1    0    0    0    0    0    1    0    0    0
+  393           0.00          -1.50    0    0    0    0    0    0    9  -11    0    0    0    0    0   -2
+  394          -1.20          -0.30    0    0    0    0    0    0    1    1    0    0    0    0    0    1
+  395          -1.40           0.10    0    0    0    0    0    0    0    4    0   -1    0    0    0    2
+  396          -0.50           1.00    0    0    2    0    2    0    0    1    0    0    0    0    0    0
+  397          -0.50           1.00    0    0    0    0    0    0    0    1   -8    3    0    0    0   -2
+  398           0.20          -1.30    0    0    0    0    0    0    0    1    0   -4    0    0    0   -2
+  399           0.00           1.50    1    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  400           0.00           1.50    1    0    0   -1   -2    0    0    0    0    0    0    0    0    0
+  401           0.00           1.50    0    0    0    0    1    0    1   -1    0    0    0    0    0    0
+  402           0.00           1.49    2    0    2   -4    2    0    0    0    0    0    0    0    0    0
+  403           0.00          -1.41    2    0    2    0    0    0    0    0    0    0    0    0    0    0
+  404           0.00           1.41    0    1    0    2    0    0    0    0    0    0    0    0    0    0
+  405           0.00          -1.40    0    0    2   -3    1    0    0    0    0    0    0    0    0    0
+  406           0.00          -1.40    1   -2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  407           0.00           1.40    1    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+  408           0.00          -1.40    0    0    2   -2    1    0   -4    4    0    0    0    0    0    0
+  409           1.10          -0.30    0    0    1   -1    1    0   -1    0    0    0    0    0    0    0
+  410           0.00          -1.40    3    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  411           0.00           1.40    1    0    2    4    1    0    0    0    0    0    0    0    0    0
+  412           1.40           0.00    0    0    1    0    2    0    0    0    0    0    0    0    0    0
+  413          -0.30           1.10    0    0    0    0    1    0   -3    5    0    0    0    0    0    0
+  414           0.20           1.20    2   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+  415          -1.30           0.00    1    0    0   -1   -1    0   -3    4    0    0    0    0    0    0
+  416           0.00          -1.30    1   -1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  417           0.00           1.30    1   -2    2    2    2    0    0    0    0    0    0    0    0    0
+  418          -0.70          -0.60    0    0    1   -1    1    0    8  -14    0    0    0    0    0    0
+  419          -0.80           0.50    0    0    1   -1    1    0    3   -6    0    0    0    0    0    0
+  420          -0.20          -1.10    0    0    0    0    0    0    6  -10    0    0    0    0    0   -2
+  421           1.10           0.20    0    0    0    0    0    0    0    2    0   -2    0    0    0    1
+  422           0.00          -1.30    3    1    2   -2    2    0    0    0    0    0    0    0    0    0
+  423           0.00          -1.30    1    0   -4   -2   -2    0    0    0    0    0    0    0    0    0
+  424           0.00          -1.30    0    0    0    0    0    0    3   -1    0    0    0    0    0    2
+  425           0.00          -1.30    0    0    0    0    0    0    0    3    0    0   -1    0    0    2
+  426           0.00          -1.29    0    0    2   -4    2    0    0    0    0    0    0    0    0    0
+  427           0.00           1.20    1    2    0   -2    1    0    0    0    0    0    0    0    0    0
+  428           0.00          -1.20    2    1    0    0   -1    0    0    0    0    0    0    0    0    0
+  429          -0.40          -0.80    0    0    0    0    1    0    0    8  -15    0    0    0    0    0
+  430           0.00           1.20    2    0    2    4    2    0    0    0    0    0    0    0    0    0
+  431           1.20           0.00    0    0    0    0    0    0    3   -6    0    0    0    0    0   -2
+  432          -0.70          -0.50    0    0    1   -1    1    0    0   -3    4    0    0    0    0    0
+  433          -1.00           0.20    0    0    0    0    0    0    6   -8    0    0    0    0    0   -1
+  434          -1.00           0.20    0    0    0    0    0    0    2    1    0    0    0    0    0    2
+  435           0.20          -1.00    0    0    0    0    0    0    2    1    0    0    0    0    0    1
+  436           0.40           0.80    0    0    0    0    0    0    0    1   -4    0    0    0    0   -2
+  437          -0.40           0.80    0    0    0    0    0    0    0    0    3    0    0    0    0    2
+  438           0.00          -1.20    1    1    2    2    1    0    0    0    0    0    0    0    0    0
+  439           0.00           1.15    0    0    0    0    3    0    0    0    0    0    0    0    0    0
+  440           0.00           1.10    2    1    0    0    1    0    0    0    0    0    0    0    0    0
+  441          -0.20           0.90    2    0    0   -2    1    0   -6    8    0    0    0    0    0    0
+  442          -1.10           0.00    0    0    3    0    3    0    0    0    0    0    0    0    0    0
+  443           0.00          -1.10    2    1    2    2    2    0    0    0    0    0    0    0    0    0
+  444          -1.10           0.00    1    0    1    0    1    0    0    0    0    0    0    0    0    0
+  445           0.00           1.10    2   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  446           0.00           1.10    3    0    2   -4    2    0    0    0    0    0    0    0    0    0
+  447           0.00           1.10    0    0    0    0    0    0    0    7   -8    3    0    0    0    2
+  448           0.60          -0.50    0    0    0    0    0    0    0    0    0    0    0    0    2    1
+  449          -0.90          -0.20    0    0    0    0    0    0    5   -5    0    0    0    0    0   -1
+  450          -0.40          -0.70    0    0    0    0    0    0    0    4   -7    0    0    0    0   -2
+  451          -0.50           0.60    1   -2    2   -2    1    0    0    0    0    0    0    0    0    0
+  452           0.00           1.10    3    0    2    2    1    0    0    0    0    0    0    0    0    0
+  453           0.00          -1.10    1   -1   -2    2   -2    0    0    0    0    0    0    0    0    0
+  454           0.00           1.00    0    0    0    0    1    0   -1    1    0    0    0    0    0    0
+  455           1.00           0.00    0    0    0    0    1    0   -2    3    0    0    0    0    0    0
+  456           0.80          -0.20    0    0    0    0    1    0    0    0    0   -1    0    0    0    0
+  457           0.00           1.00    1    0    0    1    1    0    0    0    0    0    0    0    0    0
+  458           0.00           1.00    0    2    0   -2    1    0    0    0    0    0    0    0    0    0
+  459           0.00          -1.00    3    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+  460          -1.00           0.00    1    0    1   -2    1    0    0    0    0    0    0    0    0    0
+  461           0.00           1.00    1   -1    2    4    2    0    0    0    0    0    0    0    0    0
+  462           1.00           0.00    0    0    1   -1    1    0    0   -9   15    0    0    0    0    0
+  463           1.00           0.00    0    0    0    0    0    0    7  -10    0    0    0    0    0   -2
+  464          -0.80          -0.20    0    0    0    0    0    0    2    0    0    0    0    0    0    1
+  465           0.40           0.60    0    0    0    0    0    0    3   -5    4    0    0    0    0    2
+  466          -0.40          -0.60    0    0    0    0    0    0    3   -9    4    0    0    0    0   -2
+  467           0.00          -1.00    1    0    4    0    2    0    0    0    0    0    0    0    0    0
+  468           0.00           1.00    0    1    2   -4    1    0    0    0    0    0    0    0    0    0
+  469           0.00           1.00    1    0   -4    2   -1    0    0    0    0    0    0    0    0    0
+  470           0.00           1.00    0    2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  471           0.00           1.00    2    0   -2    0    2    0    0    0    0    0    0    0    0    0
+  472           0.00          -1.00    1    0    0    2    2    0    0    0    0    0    0    0    0    0
+  473           0.00           0.91    1    0    2   -2    0    0    0    0    0    0    0    0    0    0
+  474           0.10           0.80    1   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+  475           0.00           0.90    1    0   -2   -2    1    0    0    0    0    0    0    0    0    0
+  476           0.00           0.90    0    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+  477           0.00          -0.90    2    0    0   -2    1    0   -3    3    0    0    0    0    0    0
+  478           0.00          -0.90    1    2    2    0    2    0    0    0    0    0    0    0    0    0
+  479          -0.70          -0.20    0    0    0    0    0    0    6   -6    0    0    0    0    0   -1
+  480           0.70          -0.20    0    0    0    0    0    0    1    2    0    0    0    0    0    2
+  481          -0.30           0.60    0    0    0    0    0    0    0    8  -15    0    0    0    0   -2
+  482           0.00           0.90    5    0    2    0    2    0    0    0    0    0    0    0    0    0
+  483           0.00           0.90    3    0   -2   -6   -2    0    0    0    0    0    0    0    0    0
+  484           0.00          -0.90    0    0    1   -1    1    0   -2    2    0    0    0    0    0    0
+  485          -0.50          -0.40    0    0    0    0    0    0    0    3    0   -3    0    0    0    2
+  486          -0.90           0.00    0    0    0    0    0    0    0    2    0    0   -1    0    0    2
+  487           0.00          -0.90    1   -1    2   -1    2    0    0    0    0    0    0    0    0    0
+  488           0.00           0.90    1    0   -2    4   -1    0    0    0    0    0    0    0    0    0
+  489           0.00           0.90    2    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  490           0.00          -0.90    4    0    2   -2    1    0    0    0    0    0    0    0    0    0
+  491           0.00          -0.90    1   -1    0   -1    0    0    0    0    0    0    0    0    0    0
+  492           0.00          -0.80    2   -1    0    0    0    0    0    0    0    0    0    0    0    0
+  493           0.00           0.80    3    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+  494           0.00          -0.80    2   -1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  495           0.10           0.70    0    0    0    0    0    0    7  -11    0    0    0    0    0   -2
+  496          -0.70           0.10    0    0    0    0    0    0    2   -2    0    0    0    0    0    1
+  497          -0.60           0.20    0    0    0    0    0    0    7   -9    0    0    0    0    0   -1
+  498           0.20           0.60    0    0    0    0    0    0    4   -7    0    0    0    0    0   -1
+  499           0.00           0.80    0    0    4   -4    1    0    0    0    0    0    0    0    0    0
+  500          -0.50           0.30    0    0    0    0    0    0    3   -5    0    0    0    0    0    1
+  501          -0.50          -0.30    0    0    0    0    0    0    0    5   -5    0    0    0    0    2
+  502          -0.50          -0.30    0    0    0    0    0    0    0    5   -9    0    0    0    0   -2
+  503           0.00          -0.80    0    0    0    0    0    0    0    3   -1    0    0    0    0    2
+  504          -0.30           0.50    0    0    0    0    0    0    0    2    0    2   -5    0    0    2
+  505          -0.80           0.00    0    0    0    0    0    0    0    2    0    0    1    0    0    2
+  506          -0.30          -0.50    0    0    0    0    0    0    0    2    0   -2    5    0    0    2
+  507          -0.30           0.50    0    0    0    0    1    0   -3    7   -4    0    0    0    0    0
+  508          -0.30          -0.50    0    0    0    0    1    0    3   -7    4    0    0    0    0    0
+  509           0.00           0.80    0    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+  510           0.00          -0.80    3    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+  511           0.00          -0.80    1    0   -2   -3   -1    0    0    0    0    0    0    0    0    0
+  512           0.00          -0.80    0    1   -2    1   -1    0    0    0    0    0    0    0    0    0
+  513           0.00           0.80    1    2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  514           0.00           0.80    1    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+  515           0.00          -0.80    0    0    0    0    1    0    0    1    0   -1    0    0    0    0
+  516           0.00           0.76    1    0    2    0    3    0    0    0    0    0    0    0    0    0
+  517           0.00           0.70    3    0    0   -4    1    0    0    0    0    0    0    0    0    0
+  518           0.10          -0.60    2    0    0   -2   -1    0   -6    8    0    0    0    0    0    0
+  519           0.00           0.70    0    1   -4    2   -2    0    0    0    0    0    0    0    0    0
+  520           0.70           0.00    0    0    1   -1    1    0    0   -1    0   -1    1    0    0    0
+  521           0.00          -0.70    0    0    0    0    1    0    3   -5    0    2    0    0    0    0
+  522           0.00          -0.70    0    1    2    4    2    0    0    0    0    0    0    0    0    0
+  523           0.00           0.70    4    0    2    2    2    0    0    0    0    0    0    0    0    0
+  524           0.00          -0.70    2    0   -2   -3   -2    0    0    0    0    0    0    0    0    0
+  525          -0.70           0.00    0    0    1   -1    1    0    2   -4    0   -3    0    0    0    0
+  526          -0.50           0.20    0    0    1   -1    1    0    0   -1    0    3    0    0    0    0
+  527          -0.20          -0.50    0    0    0    0    0    0    9   -9    0    0    0    0    0   -1
+  528           0.50          -0.20    0    0    0    0    0    0    2   -3    0    0    0    0    0   -2
+  529           0.20           0.50    0    0    0    0    0    0    0   11    0    0    0    0    0    2
+  530          -0.20          -0.50    0    0    0    0    0    0    0    6  -15    0    0    0    0   -2
+  531           0.50          -0.20    0    0    0    0    0    0    0    3    0    2   -5    0    0    2
+  532          -0.50           0.20    0    0    0    0    0    0    0    3    0    1    0    0    0    2
+  533           0.00          -0.70    0    0    0    0    0    0    0    3   -5    0    0    0    0   -2
+  534           0.00          -0.70    0    0    0    0    0    0    0    2    0   -4    0    0    0   -2
+  535           0.70           0.00    0    0    0    0    0    0    0    0    0    0    3    0    0    2
+  536          -0.60          -0.10    0    0    0    0    0    0    3   -1    0    0    0    0    0    1
+  537           0.60          -0.10    0    0    0    0    0    0    3   -3    0    0    0    0    0    1
+  538           0.40           0.30    0    0    0    0    0    0    0    4   -8    0    0    0    0   -2
+  539           0.00           0.70    1    0    0    1    0    0    0    0    0    0    0    0    0    0
+  540           0.70           0.00    0    0    1   -1    2    0    0   -2    2    0    0    0    0    0
+  541           0.00           0.70    2   -1    2    2    1    0    0    0    0    0    0    0    0    0
+  542           0.00           0.70    0    1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  543           0.00           0.70    1   -1    0    0    2    0    0    0    0    0    0    0    0    0
+  544           0.00          -0.60    0    0    0    0    1    0    0   -1    0    1    0    0    0    0
+  545           0.00           0.60    0    0    2    2   -1    0    0    0    0    0    0    0    0    0
+  546           0.10          -0.50    0    0    1   -1   -1    0    0    0   -2    0    0    0    0    0
+  547           0.00           0.60    2    1    0    0    0    0    0    0    0    0    0    0    0    0
+  548           0.40           0.20    0    0    0    0    1    0    0    2   -4    0    0    0    0    0
+  549           0.00           0.60    1    0    0   -4   -2    0    0    0    0    0    0    0    0    0
+  550           0.00          -0.60    2    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+  551           0.00           0.60    1    1    0   -4    1    0    0    0    0    0    0    0    0    0
+  552           0.00          -0.60    0    1    0    1    0    0    0    0    0    0    0    0    0    0
+  553           0.00          -0.60    1   -1    2   -4    1    0    0    0    0    0    0    0    0    0
+  554           0.50           0.10    0    0    1   -1    1    0   -2    3    0    0    0    0    0    0
+  555          -0.50          -0.10    0    0    0    0    0    0    7   -7    0    0    0    0    0   -1
+  556          -0.10          -0.50    0    0    0    0    0    0    4   -4    0    0    0    0    0    2
+  557           0.10           0.50    0    0    0    0    0    0    1   -2    0    0    0    0    0    1
+  558           0.50          -0.10    0    0    0    0    0    0    0    5    0   -2    0    0    0    2
+  559          -0.10           0.50    0    0    0    0    0    0    0    3   -6    0    0    0    0   -1
+  560           0.00          -0.60    2    0    0   -2   -1    0    0   -2    0    0    5    0    0    0
+  561          -0.40           0.20    2    0   -1   -1   -1    0    0   -1    0    3    0    0    0    0
+  562           0.00          -0.60    1    0   -2   -2   -2    0    0   -2    0    2    0    0    0    0
+  563           0.60           0.00    0    0    2   -2    2    0   -8   11    0    0    0    0    0    0
+  564           0.00          -0.60    0    0    2   -2    1    0   -2    2    0    0    0    0    0    0
+  565           0.20           0.40    0    0    1   -1    1    0    0   -1    0    0    3    0    0    0
+  566          -0.40           0.20    0    0    0    0    0    1    0   -4    0    0    0    0    0   -2
+  567           0.30           0.30    0    0    0    0    0    0    8  -13    0    0    0    0    0    1
+  568           0.40          -0.20    0    0    0    0    0    0    0    6    0    0    0    0    0    2
+  569          -0.40          -0.20    0    0    0    0    0    0    0    6   -6    0    0    0    0    2
+  570           0.00           0.60    0    0    0    0    0    0    0    1    1    0    0    0    0    2
+  571           0.00           0.60    0    0    0    0    0    0    0    1    0    0    1    0    0    2
+  572           0.40           0.20    0    0    0    0    0    0    0    1   -5    0    0    0    0   -2
+  573          -0.20          -0.40    0    0    0    0    0    0    0    0    0    0    3    0    0    1
+  574           0.00           0.60    4    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  575           0.00          -0.60    0    0    0    3    1    0    0    0    0    0    0    0    0    0
+  576           0.00           0.60    3    0    0    2    1    0    0    0    0    0    0    0    0    0
+  577           0.00           0.60    1    1    0   -4   -1    0    0    0    0    0    0    0    0    0
+  578           0.00          -0.60    0    0    0    1    2    0    0    0    0    0    0    0    0    0
+  579           0.00          -0.60    1    1    0   -2    2    0    0    0    0    0    0    0    0    0
+  580           0.00          -0.60    2    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+  581           0.00          -0.50    1    1    0   -4    0    0    0    0    0    0    0    0    0    0
+  582           0.00           0.50    2    0    2    0   -1    0    0    0    0    0    0    0    0    0
+  583           0.10           0.40    2    1    0   -2    0    0    0    0    0    0    0    0    0    0
+  584           0.00          -0.50    0    1    0    2    2    0    0    0    0    0    0    0    0    0
+  585           0.10           0.40    0    0    0    0    1    0    3   -5    0    0    0    0    0    0
+  586           0.00           0.50    1   -2    2   -2    2    0    0    0    0    0    0    0    0    0
+  587           0.00           0.50    2    1    0   -4    1    0    0    0    0    0    0    0    0    0
+  588           0.00          -0.50    1   -2    0    0    1    0    0    0    0    0    0    0    0    0
+  589           0.30          -0.20    2    0    0   -2    1    0    0   -6    8    0    0    0    0    0
+  590          -0.20           0.30    2    0    0   -2   -1    0    0   -5    6    0    0    0    0    0
+  591           0.20           0.30    2    0   -1   -1   -1    0    0    3   -7    0    0    0    0    0
+  592           0.40          -0.10    0    0    2    0    2    0    0   -4    8   -3    0    0    0    0
+  593           0.40           0.10    0    0    2    0    2    0    0    4   -8    3    0    0    0    0
+  594           0.00          -0.50    0    1   -2   -1   -2    0    0    0    0    0    0    0    0    0
+  595           0.00          -0.50    3   -1    2   -2    2    0    0    0    0    0    0    0    0    0
+  596           0.30           0.20    0    0    0    0    0    0    1    0    0    0    0    0    0    2
+  597          -0.30           0.20    0    0    0    0    0    0    0    7  -13    0    0    0    0   -2
+  598           0.20           0.30    0    0    0    0    0    0    0    2   -5    0    0    0    0   -2
+  599          -0.30           0.20    0    0    0    0    0    0    0    1    0    3    0    0    0    2
+  600           0.00           0.50    3   -1    2    2    2    0    0    0    0    0    0    0    0    0
+  601           0.00           0.50    3   -1   -2   -1   -2    0    0    0    0    0    0    0    0    0
+  602           0.00           0.50    0    0    2    6    2    0    0    0    0    0    0    0    0    0
+  603          -0.50           0.00    0    0    1   -1    1    0    0    1    0    0    0    0    0    0
+  604           0.50           0.00    0    0    0    0    0    0    9  -12    0    0    0    0    0   -2
+  605           0.00          -0.50    0    0    0    0    0    0    6   -9    0    0    0    0    0   -1
+  606          -0.50           0.00    0    0    0    0    0    0    4   -4    0    0    0    0    0    1
+  607          -0.50           0.00    0    0    0    0    0    0    0    6  -11    0    0    0    0   -2
+  608           0.00           0.50    0    0    0    0    0    0    0    1   -3    0    0    0    0   -2
+  609           0.40           0.10    0    0    2   -2    1   -1    0    2    0    0    0    0    0    0
+  610          -0.40          -0.10    0    0    1   -1    1    0    0   -1    0    0    0    2    0    0
+  611           0.40          -0.10    0    0    0    0    0    0    5   -6    0    0    0    0    0    2
+  612          -0.40           0.10    0    0    0    0    0    0    5  -10    0    0    0    0    0   -2
+  613           0.10           0.40    0    0    0    0    0    0    0    5    0   -3    0    0    0    2
+  614           0.10           0.40    0    0    0    0    0    0    0    0    0    4    0    0    0    2
+  615          -0.50           0.00    0    0    3    0    2    0    0    0    0    0    0    0    0    0
+  616           0.00           0.50    2    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+  617           0.00          -0.50    2    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  618           0.00           0.50    0    0    2   -2    2    0   -3    3    0    0    0    0    0    0
+  619           0.00           0.50    1    0    0   -1    2    0    0    0    0    0    0    0    0    0
+  620           0.00          -0.50    1    1    0    0    2    0    0    0    0    0    0    0    0    0
+  621           0.50           0.00    0    0    2   -2    2    0   -5    6    0    0    0    0    0    0
+  622           0.00          -0.50    1    0    0    2   -2    0    0    0    0    0    0    0    0    0
+  623           0.00           0.50    1    0   -2    2    0    0    0    0    0    0    0    0    0    0
+  624           0.00          -0.40    1    0    2    2    0    0    0    0    0    0    0    0    0    0
+  625          -0.20           0.20    0    0    0    0    2    0    0   -1    2    0    0    0    0    0
+  626          -0.10           0.30    1    1    0    2    0    0    0    0    0    0    0    0    0    0
+  627           0.00          -0.40    1    0    0    4    0    0    0    0    0    0    0    0    0    0
+  628           0.40           0.00    1    0   -1    0    1    0    0    0    0    0    0    0    0    0
+  629           0.00          -0.40    0    2    0   -2    0    0    0    0    0    0    0    0    0    0
+  630           0.00          -0.40    1    0    0   -3    1    0    0    0    0    0    0    0    0    0
+  631           0.00           0.40    4    0    0    0   -1    0    0    0    0    0    0    0    0    0
+  632           0.00           0.40    1   -2    0   -2   -1    0    0    0    0    0    0    0    0    0
+  633          -0.10           0.30    0    0    0    0    1    0    0   -8   15    0    0    0    0    0
+  634           0.00           0.40    1    0    4   -4    2    0    0    0    0    0    0    0    0    0
+  635           0.00          -0.40    1    1    0   -1    1    0    0    0    0    0    0    0    0    0
+  636           0.00          -0.40    0    0    2    0    2    0    1   -1    0    0    0    0    0    0
+  637           0.00          -0.40    1    1    0    2   -1    0    0    0    0    0    0    0    0    0
+  638           0.00           0.40    2   -2    2    0    2    0    0    0    0    0    0    0    0    0
+  639          -0.20          -0.20    0    0    1   -1    1    0    0   -1    0    0   -2    0    0    0
+  640           0.20          -0.20    0    0    1   -1    1    0    0   -1    0   -2    4    0    0    0
+  641           0.20           0.20    0    0    0    0    0    0    0    2    2    0    0    0    0    2
+  642          -0.10           0.30    2   -2    2   -2    2    0    0    0    0    0    0    0    0    0
+  643          -0.30           0.10    0    0    0    0    0    0    6  -10    0    0    0    0    0   -1
+  644           0.10           0.30    0    0    0    0    0    0    0    6   -5    0    0    0    0    2
+  645          -0.10           0.30    0    0    0    0    0    0    0    2    0   -2    0    0    0    2
+  646           0.00          -0.40    5    0    2   -2    2    0    0    0    0    0    0    0    0    0
+  647           0.00           0.40    3    0    2   -1    2    0    0    0    0    0    0    0    0    0
+  648           0.00           0.40    1    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  649           0.00           0.40    1   -1   -2   -3   -2    0    0    0    0    0    0    0    0    0
+  650           0.00           0.40    0    0    4   -1    2    0    0    0    0    0    0    0    0    0
+  651           0.00          -0.40    0    0    2   -2    1    0    0   -2    0    3    0    0    0    0
+  652           0.00          -0.40    0    0    2   -2    1    0    0   -2    0    0    2    0    0    0
+  653           0.40           0.00    0    0    2   -2    1    0    0   -8   11    0    0    0    0    0
+  654          -0.40           0.00    0    0    1   -1    1    0    0   -1    0   -1    2    0    0    0
+  655          -0.40           0.00    0    0    0    0    0    0    8   -8    0    0    0    0    0   -1
+  656          -0.40           0.00    0    0    0    0    0    0    8  -10    0    0    0    0    0   -1
+  657           0.00           0.40    0    0    0    0    0    0    5   -5    0    0    0    0    0    2
+  658           0.00          -0.40    0    0    0    0    0    0    5   -9    0    0    0    0    0   -2
+  659           0.00          -0.40    0    0    0    0    0    0    4   -2    0    0    0    0    0    2
+  660          -0.40           0.00    0    0    0    0    0    0    4   -2    0    0    0    0    0    1
+  661           0.40           0.00    0    0    0    0    0    0    4   -3    0    0    0    0    0    2
+  662           0.00          -0.40    0    0    0    0    0    0    3   -4    0    0    0    0    0   -1
+  663           0.00           0.40    0    0    0    0    0    0    3   -6    0    0    0    0    0   -1
+  664           0.00           0.40    0    0    0    0    0    0    2   -6    0    0    0    0    0   -2
+  665           0.40           0.00    0    0    0    0    0    0    0    8  -15    0    0    0    0   -1
+  666           0.00          -0.40    0    0    0    0    0    0    0    6    0    0    0    0    0    0
+  667          -0.40           0.00    0    0    0    0    0    0    0    5   -2    0    0    0    0    2
+  668           0.00           0.40    0    0    0    0    0    0    0    5   -3    0    0    0    0    2
+  669           0.40           0.00    0    0    0    0    0    0    0    0    0    0    1    0    0    2
+  670           0.00          -0.40    4    0    0    0    0    0    0    0    0    0    0    0    0    0
+  671           0.00          -0.40    1   -2   -2    0   -2    0    0    0    0    0    0    0    0    0
+  672           0.00          -0.40    1    2    2   -2    1    0    0    0    0    0    0    0    0    0
+  673           0.00          -0.40    0    2    2    0    1    0    0    0    0    0    0    0    0    0
+  674           0.00          -0.40    3    1    2    0    1    0    0    0    0    0    0    0    0    0
+  675          -0.10           0.30    0    2   -2    0   -1    0    0    0    0    0    0    0    0    0
+  676           0.00           0.40    1    1    2    1    1    0    0    0    0    0    0    0    0    0
+  677           0.00           0.40    3   -1    2    0    1    0    0    0    0    0    0    0    0    0
+  678           0.00           0.40    2    0    2   -1    1    0    0    0    0    0    0    0    0    0
+  679           0.00          -0.40    2    0    0    2    2    0    0    0    0    0    0    0    0    0
+  680           0.40           0.00    0    0    1   -1    2    0    0   -1    0    2    0    0    0    0
+  681           0.00           0.40    0    0    2   -2   -2    0    0    0    0    0    0    0    0    0
+  682           0.21           0.10    0    0    1   -1    2    0    0   -1    0   -2    5    0    0    0
+  683           0.00           0.30    2    0   -4    2   -2    0    0    0    0    0    0    0    0    0
+  684          -0.30           0.00    0    0    2   -2   -1    0   -5    6    0    0    0    0    0    0
+  685           0.00          -0.30    2   -2    0   -2    1    0    0    0    0    0    0    0    0    0
+  686           0.00           0.30    0    0    0    4    2    0    0    0    0    0    0    0    0    0
+  687           0.00           0.30    2    0    0   -2   -1    0   -3    3    0    0    0    0    0    0
+  688           0.20           0.10    0    0    1   -1   -1    0    0   -1    0   -1    0    0    0    0
+  689           0.00          -0.30    1   -2    0   -2    1    0    0    0    0    0    0    0    0    0
+  690           0.00           0.30    1   -2    0    0   -1    0    0    0    0    0    0    0    0    0
+  691           0.30           0.00    0    0    0    0    1    0    0    0    0    0   -1    0    0    0
+  692           0.00           0.30    2    0    0   -3   -1    0    0    0    0    0    0    0    0    0
+  693           0.00          -0.30    2    1   -2    0    1    0    0    0    0    0    0    0    0    0
+  694          -0.20           0.10    2    0    0   -2   -1    0    0   -6    8    0    0    0    0    0
+  695          -0.10          -0.20    2    0   -1   -1    1    0    0    3   -7    0    0    0    0    0
+  696           0.00          -0.30    2    1    2   -4    1    0    0    0    0    0    0    0    0    0
+  697           0.10          -0.20    1    0    0   -1    1    0    0   -3    4    0    0    0    0    0
+  698           0.00           0.30    2   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+  699           0.00           0.30    0    0    2    0    2    0   -1    1    0    0    0    0    0    0
+  700           0.00          -0.30    0    0    2    0    2    0    0   -1    0    1    0    0    0    0
+  701           0.00           0.30    0    0    2    0    2    0    0    1    0   -1    0    0    0    0
+  702           0.00           0.30    0    1    0   -4    1    0    0    0    0    0    0    0    0    0
+  703          -0.20           0.10    0    0    0    0    1    0    0   -9   17    0    0    0    0    0
+  704           0.00          -0.30    1    1    4   -2    2    0    0    0    0    0    0    0    0    0
+  705          -0.10          -0.20    2    0    2    0    1    0    0    1    0    0    0    0    0    0
+  706          -0.10           0.20    1    0   -2    0   -2    0  -10    3    0    0    0    0    0    0
+  707           0.20          -0.10    0    0    2   -2    1    0    0   -7    9    0    0    0    0    0
+  708          -0.10          -0.20    0    0    0    0    0    0    2   -3    0    0    0    0    0    1
+  709           0.20           0.10    0    0    0    0    0    0    1   -2    0    0    0    0    0   -2
+  710           0.20          -0.10    0    0    0    0    0    0    1   -4    0    0    0    0    0   -2
+  711          -0.20          -0.10    0    0    0    0    0    0    0    6  -10    0    0    0    0   -2
+  712          -0.10          -0.20    0    0    0    0    0    0    0    5   -8    0    0    0    0   -2
+  713           0.20          -0.10    0    0    0    0    0    0    0    3   -5    0    0    0    0   -1
+  714           0.20           0.10    0    0    0    0    0    0    0    2   -6    0    0    0    0   -2
+  715           0.00           0.30    4    0    2   -4    2    0    0    0    0    0    0    0    0    0
+  716           0.00          -0.30    2    2    2   -2    2    0    0    0    0    0    0    0    0    0
+  717           0.00           0.30    2    1    2    1    2    0    0    0    0    0    0    0    0    0
+  718           0.00           0.30    2    0    0   -2   -1    0    0   -2    0    4   -5    0    0    0
+  719           0.00          -0.30    2    0    0   -2   -2    0   -3    3    0    0    0    0    0    0
+  720           0.30           0.00    2    0   -1   -1   -1    0    0   -1    0    0    0    0    0    0
+  721           0.00          -0.30    1    0    2    3    2    0    0    0    0    0    0    0    0    0
+  722           0.00           0.30    1    0    2   -2    2    0    0   -2    0    2    0    0    0    0
+  723           0.30           0.00    1    0    1   -1    1    0    0   -1    0    0    0    0    0    0
+  724           0.30           0.00    1    0   -1    1   -1    0  -18   17    0    0    0    0    0    0
+  725           0.00          -0.30    1    0   -1   -1   -1    0   20  -20    0    0    0    0    0    0
+  726           0.00          -0.30    1    0   -2   -2   -2    0   -3    3    0    0    0    0    0    0
+  727           0.00          -0.30    0    2    2    2    2    0    0    0    0    0    0    0    0    0
+  728           0.00           0.30    0    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  729           0.00           0.30    0    1    2    3    2    0    0    0    0    0    0    0    0    0
+  730           0.00          -0.30    0    0    2   -2    1    0    0   -1    0    1    0    0    0    0
+  731           0.30           0.00    0    0    1   -1    1    0    1   -2    0    0    0    0    0    0
+  732           0.30           0.00    0    0    1   -1    1    0   -2    1    0    0    0    0    0    0
+  733          -0.30           0.00    0    0    0    0    0    0    9  -11    0    0    0    0    0   -1
+  734           0.00           0.30    0    0    0    0    0    0    8  -16    0    0    0    0    0   -2
+  735          -0.30           0.00    0    0    0    0    0    0    5   -3    0    0    0    0    0    1
+  736           0.30           0.00    0    0    0    0    0    0    0    6    0    0    0    0    0    1
+  737          -0.30           0.00    0    0    0    0    0    0    0    6   -7    0    0    0    0    2
+  738           0.00           0.30    0    0    0    0    0    0    0    4    0    0   -2    0    0    2
+  739           0.30           0.00    0    0    0    0    0    0    0    4   -8    1    5    0    0   -2
+  740          -0.30           0.00    0    0    0    0    0    0    0    3    0    0   -2    0    0    2
+  741           0.30           0.00    0    0    0    0    0    0    0    1    0   -1    0    0    0    1
+  742           0.30           0.00    0    0    0    0    0    0    0    1    0   -3    0    0    0   -1
+  743           0.30           0.00    0    0    0    0    0    0    0    1   -6    0    0    0    0   -2
+  744           0.00          -0.30    0    0    0    0    0    0    0    0    0    4   -5    0    0    2
+  745           0.00           0.30    0    0    0    0    0    0    0    0    0    0    5    0    0    2
+  746           0.00           0.30    0    0    0    0    0    0    0    0    0    0    0    2    0    2
+  747           0.30           0.00    0    0    0    0    0    0    0    0    0    0    0    2    0    1
+  748           0.00           0.30    0    0    0    0    0    0    0    0    0    0    0    0    2    2
+  749           0.00          -0.30    1    0    0   -1   -1    0    0   -2    2    0    0    0    0    0
+  750           0.00          -0.30    2   -1    2   -1    2    0    0    0    0    0    0    0    0    0
+  751           0.30           0.00    2    0   -1   -1   -1    0    0   -1    0    2    0    0    0    0
+  752           0.00           0.30    1    0    0   -6   -1    0    0    0    0    0    0    0    0    0
+  753           0.00          -0.30    1   -1    0   -4   -1    0    0    0    0    0    0    0    0    0
+  754           0.00           0.30    1    0    0    4   -1    0    0    0    0    0    0    0    0    0
+  755           0.00          -0.30    3    0    0   -2    1    0    0    0    0    0    0    0    0    0
+  756           0.30           0.00    0    0    0    0    1    0    5   -8    0    0    0    0    0    0
+  757           0.00          -0.30    1   -2   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  758           0.00          -0.30    0    1    4   -2    1    0    0    0    0    0    0    0    0    0
+  759           0.00          -0.30    2    0    4   -2    1    0    0    0    0    0    0    0    0    0
+  760           0.00          -0.30    0    0    2    3    1    0    0    0    0    0    0    0    0    0
+  761           0.30           0.00    0    0    1   -1    2    0    0   -1    0    1    0    0    0    0
+  762           0.00           0.30    1   -2    2    0    1    0    0    0    0    0    0    0    0    0
+  763           0.00          -0.30    2   -1    2   -2    1    0    0    0    0    0    0    0    0    0
+  764           0.00           0.30    2    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+  765           0.00          -0.30    2    0   -4   -2   -1    0    0    0    0    0    0    0    0    0
+  766           0.00          -0.30    2    0    2    1    1    0    0    0    0    0    0    0    0    0
+  767           0.00           0.30    0    0    1   -1    2    0    0    0   -2    0    0    0    0    0
+  768          -0.30           0.00    0    0    0    0    2    0    0    4   -8    3    0    0    0    0
+  769          -0.30           0.00    0    0    0    0    2    0    0   -4    8   -3    0    0    0    0
+  770           0.00           0.30    0    0    0    1   -2    0    0    0    0    0    0    0    0    0
+  771           0.00          -0.30    0    0    2    0   -2    0    0    0    0    0    0    0    0    0
+  772           0.00          -0.30    0    1    0   -2    2    0    0    0    0    0    0    0    0    0
+  773          -0.20          -0.10    0    0    0    0    1    0    0    1    0   -2    0    0    0    0
+  774           0.00          -0.30    0    0    0    0    1    0    2   -2    0    0    0    0    0    0
+  775           0.00          -0.30    1    1    2   -4    2    0    0    0    0    0    0    0    0    0
+  776           0.00          -0.21    0    1    2   -2    3    0    0    0    0    0    0    0    0    0
+  777           0.00           0.20    2    0    2   -2    0    0    0    0    0    0    0    0    0    0
+  778           0.00          -0.20    0    0    0    0    1    0   -2    2    0    0    0    0    0    0
+  779           0.00          -0.20    3    0    2    0    0    0    0    0    0    0    0    0    0    0
+  780           0.00          -0.20    0    0    0    0    1    0    0   -2    2    0    0    0    0    0
+  781           0.00          -0.20    0    1    0   -4    0    0    0    0    0    0    0    0    0    0
+  782           0.20           0.00    0    0    0    0    1    0   -3    4    0    0    0    0    0    0
+  783           0.00          -0.20    3    0    0    2    0    0    0    0    0    0    0    0    0    0
+  784           0.00           0.20    0    3    0    0    0    0    0    0    0    0    0    0    0    0
+  785           0.00           0.20    2    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+  786           0.20           0.00    2    0    2   -4    0    0    0    0    0    0    0    0    0    0
+  787           0.00           0.20    1    0    0   -3   -1    0    0    0    0    0    0    0    0    0
+  788           0.00          -0.20    2    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+  789           0.00           0.20    1    0    2   -4   -1    0    0    0    0    0    0    0    0    0
+  790           0.00           0.20    0    0    1   -1    2    0    0   -1    0    0    1    0    0    0
+  791           0.00           0.20    1    0   -2    1    1    0    0    0    0    0    0    0    0    0
+  792           0.00          -0.20    1    2    0   -2    0    0    0    0    0    0    0    0    0    0
+  793           0.00          -0.20    2    0   -2   -2    1    0    0    0    0    0    0    0    0    0
+  794           0.00          -0.20    1    0    0   -1    1    0    0   -1    0    1    0    0    0    0
+  795           0.00          -0.20    2    0    0   -3    1    0    0    0    0    0    0    0    0    0
+  796           0.00          -0.20    1    1    0    1    1    0    0    0    0    0    0    0    0    0
+  797           0.00           0.20    1    1    0    1   -1    0    0    0    0    0    0    0    0    0
+  798           0.00          -0.20    2    0    0   -1    1    0    0    0    0    0    0    0    0    0
+  799           0.00           0.20    1    2    0    0    1    0    0    0    0    0    0    0    0    0
+  800           0.00          -0.20    2    0    0   -2    1    0    0   -5    6    0    0    0    0    0
+  801           0.00           0.20    1    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+  802           0.00          -0.20    1    0    0   -2    1    0    0   -2    0    2    0    0    0    0
+  803           0.00           0.20    2    0   -4    2   -1    0    0    0    0    0    0    0    0    0
+  804           0.20           0.00    0    0    2   -2    1    0    0    4   -8    3    0    0    0    0
+  805           0.20           0.00    0    0    2   -2    1    0    0   -4    8   -3    0    0    0    0
+  806           0.00           0.20    2   -1    0    2   -1    0    0    0    0    0    0    0    0    0
+  807           0.00           0.20    1    0    0   -1   -1    0    0   -3    4    0    0    0    0    0
+  808           0.10          -0.10    1   -2    0   -2    0    0    0    0    0    0    0    0    0    0
+  809           0.00          -0.20    2    0    0   -2   -1    0    0   -2    0    3   -1    0    0    0
+  810           0.00           0.20    1   -1   -4    2   -2    0    0    0    0    0    0    0    0    0
+  811           0.00           0.20    3    0    0    2   -1    0    0    0    0    0    0    0    0    0
+  812           0.00           0.20    1    0    0   -3    0    0    0    0    0    0    0    0    0    0
+  813           0.00           0.20    2    0    2   -6    1    0    0    0    0    0    0    0    0    0
+  814           0.00           0.20    2   -1   -2    2   -1    0    0    0    0    0    0    0    0    0
+  815           0.00           0.20    1   -2   -2    2   -1    0    0    0    0    0    0    0    0    0
+  816           0.00           0.20    2    0    4   -4    2    0    0    0    0    0    0    0    0    0
+  817           0.20           0.00    2    0   -1   -1   -2    0    0   -1    0    2    0    0    0    0
+  818           0.00          -0.20    4    1    2    0    2    0    0    0    0    0    0    0    0    0
+  819           0.00           0.20    4   -1    2    0    2    0    0    0    0    0    0    0    0    0
+  820           0.00          -0.20    4   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  821           0.00          -0.20    3    0    4   -2    2    0    0    0    0    0    0    0    0    0
+  822           0.00          -0.20    3    0    2    1    2    0    0    0    0    0    0    0    0    0
+  823           0.00           0.20    3    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+  824           0.00          -0.20    3    0   -2   -1   -1    0    0    0    0    0    0    0    0    0
+  825           0.00           0.20    2    2   -2   -4   -2    0    0    0    0    0    0    0    0    0
+  826           0.00          -0.20    2    1    0   -4    0    0    0    0    0    0    0    0    0    0
+  827           0.00           0.20    2    1   -2   -6   -2    0    0    0    0    0    0    0    0    0
+  828           0.00           0.20    2    0    2   -3    2    0    0    0    0    0    0    0    0    0
+  829           0.00           0.20    2   -2    2    2    2    0    0    0    0    0    0    0    0    0
+  830           0.00          -0.20    1    1    2    4    2    0    0    0    0    0    0    0    0    0
+  831           0.00          -0.20    1    1   -2   -3   -2    0    0    0    0    0    0    0    0    0
+  832           0.00           0.20    1    1   -2   -6   -2    0    0    0    0    0    0    0    0    0
+  833          -0.20           0.00    1    0    3    0    3    0    0    0    0    0    0    0    0    0
+  834           0.20           0.00    1    0    1    1    1    0    0    1    0    0    0    0    0    0
+  835           0.20           0.00    1    0   -1    1   -1    0    0    1    0    0    0    0    0    0
+  836           0.00           0.20    1    0   -1    0   -1    0   -3    5    0    0    0    0    0    0
+  837           0.20           0.00    1    0   -1   -2   -1    0    0    0    0    0    0    0    0    0
+  838           0.00           0.20    1    0   -2   -2   -2    0    0   -2    0    3    0    0    0    0
+  839           0.00          -0.20    1   -1    2    1    2    0    0    0    0    0    0    0    0    0
+  840           0.00           0.20    1   -1   -2    1   -1    0    0    0    0    0    0    0    0    0
+  841           0.00           0.20    0    3   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  842           0.00          -0.20    0    0    4    2    2    0    0    0    0    0    0    0    0    0
+  843           0.00           0.20    0    0    2   -2    1    0    0    1    0   -1    0    0    0    0
+  844           0.00           0.20    0    0    2   -2    1    0    0   -2    0    0    0    0    0    0
+  845           0.00          -0.20    0    0    2   -2    1    0    0   -3    0    3    0    0    0    0
+  846           0.00          -0.20    0    0    2   -2    1    0    0   -4    4    0    0    0    0    0
+  847           0.00          -0.20    0    0    2   -2    1    0   -5    5    0    0    0    0    0    0
+  848           0.00           0.20    0    0    1   -1    1    0    1   -3    0    0    0    0    0    0
+  849           0.00          -0.20    0    0    1   -1    1    0    0    1   -4    0    0    0    0    0
+  850           0.20           0.00    0    0    1   -1    1    0    0   -1    0    1   -3    0    0    0
+  851           0.20           0.00    0    0    1   -1    1    0    0   -1    0    0    0   -1    0    0
+  852           0.00          -0.20    0    0    1   -1    1    0    0   -4    6    0    0    0    0    0
+  853           0.20           0.00    0    0    1   -1    1    0   -5    6    0    0    0    0    0    0
+  854           0.00           0.20    0    0    0    0    0    0    8  -12    0    0    0    0    0   -2
+  855           0.00          -0.20    0    0    0    0    0    0    7  -10    0    0    0    0    0   -1
+  856           0.20           0.00    0    0    0    0    0    0    7  -11    0    0    0    0    0   -1
+  857          -0.20           0.00    0    0    0    0    0    0    6   -4    0    0    0    0    0    1
+  858          -0.20           0.00    0    0    0    0    0    0    6   -6    0    0    0    0    0    1
+  859          -0.20           0.00    0    0    0    0    0    0    5   -5    0    0    0    0    0    1
+  860           0.00          -0.20    0    0    0    0    0    0    5   -6    0    0    0    0    0   -1
+  861          -0.20           0.00    0    0    0    0    0    0    4   -2    0    0    0    0    0    0
+  862           0.00          -0.20    0    0    0    0    0    0    4   -5    0    0    0    0    0   -1
+  863           0.20           0.00    0    0    0    0    0    0    4   -5    0    0    0    0    0   -2
+  864           0.00          -0.20    0    0    0    0    0    0    3   -4    0    0    0    0    0    1
+  865           0.20           0.00    0    0    0    0    0    0    3   -4    0    0    0    0    0   -2
+  866          -0.20           0.00    0    0    0    0    0    0    3   -8    0    0    0    0    0   -2
+  867           0.00           0.20    0    0    0    0    0    0    2   -5    0    0    0    0    0   -1
+  868           0.00           0.20    0    0    0    0    0    0    1   -2    0    0    0    0    0   -1
+  869           0.00           0.20    0    0    0    0    0    0    1   -4    0    0    0    0    0   -1
+  870           0.00          -0.20    0    0    0    0    0    0    0    9  -17    0    0    0    0   -2
+  871          -0.20           0.00    0    0    0    0    0    0    0    7   -7    0    0    0    0    2
+  872          -0.20           0.00    0    0    0    0    0    0    0    7   -8    0    0    0    0    2
+  873          -0.20           0.00    0    0    0    0    0    0    0    7   -9    0    0    0    0    2
+  874          -0.20           0.00    0    0    0    0    0    0    0    5   -6    0    0    0    0    2
+  875           0.20           0.00    0    0    0    0    0    0    0    5  -10    0    0    0    0   -2
+  876           0.00           0.20    0    0    0    0    0    0    0    4    0   -4    0    0    0    2
+  877           0.00          -0.20    0    0    0    0    0    0    0    4   -6    0    0    0    0   -2
+  878           0.00          -0.20    0    0    0    0    0    0    0    4   -7    0    0    0    0   -1
+  879           0.00          -0.20    0    0    0    0    0    0    0    4   -8    3    0    0    0    1
+  880           0.00           0.20    0    0    0    0    0    0    0    4   -8    3    0    0    0   -1
+  881           0.20           0.00    0    0    0    0    0    0    0    4   -8    1    5    0    0    2
+  882           0.20           0.00    0    0    0    0    0    0    0    3    0    0    0    0    0    2
+  883           0.00           0.20    0    0    0    0    0    0    0    3   -8    3    0    0    0   -2
+  884           0.20           0.00    0    0    0    0    0    0    0    2    0    0   -2    0    0    1
+  885           0.00          -0.20    0    0    0    0    0    0    0    2    0   -5    0    0    0   -2
+  886           0.00          -0.20    0    0    0    0    0    0    0    2   -4    0    0    0    0    1
+  887           0.20           0.00    0    0    0    0    0    0    0    2   -7    0    0    0    0   -2
+  888           0.20           0.00    0    0    0    0    0    0    0    1    0    1    0    0    0    1
+  889           0.00           0.20    0    0    0    0    0    0    0    1    0    0    0    0    0   -1
+  890           0.20           0.00    0    0    0    0    0    0    0    1    0   -1    0    0    0   -1
+  891           0.20           0.00    0    0    0    0    0    0    0    1    0   -2    5    0    0    2
+  892           0.00           0.20    0    0    0    0    0    0    0    1    0   -2    0    0    0   -2
+  893           0.00          -0.20    0    0    0    0    0    0    0    1    0   -5    0    0    0   -2
+  894           0.10          -0.10    0    0    0    0    0    0    0    2    1    0    0    0    0    2
+  895           0.00          -0.20    1    0    4    0    1    0    0    0    0    0    0    0    0    0
+  896           0.00           0.20    4    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+  897           0.00          -0.20    0    1    0    4    1    0    0    0    0    0    0    0    0    0
+  898           0.00           0.20    2    0    0    4    1    0    0    0    0    0    0    0    0    0
+  899           0.00           0.20    0    0    0    0    1    0    0    7  -13    0    0    0    0    0
+  900           0.00          -0.20    2    1    0    2    1    0    0    0    0    0    0    0    0    0
+  901           0.00           0.20    1   -1    0    4    1    0    0    0    0    0    0    0    0    0
+  902           0.20           0.00    0    0    2   -2    1    0   -8   11    0    0    0    0    0    0
+  903           0.00           0.20    1    0    2   -6    1    0    0    0    0    0    0    0    0    0
+  904           0.00           0.20    0    0    2   -2    2    0    0   -2    0    2    0    0    0    0
+  905           0.00          -0.20    1    2    2    0    1    0    0    0    0    0    0    0    0    0
+  906           0.00           0.20    3    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+  907           0.00           0.20    2    0    0   -6   -1    0    0    0    0    0    0    0    0    0
+  908           0.00           0.20    1   -1    2    4    1    0    0    0    0    0    0    0    0    0
+  909           0.00          -0.20    2    1    2    2    1    0    0    0    0    0    0    0    0    0
+  910          -0.20           0.00    0    0    1    1    2    0    0    1    0    0    0    0    0    0
+  911           0.00           0.20    2   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  912           0.00          -0.20    0    1   -2    4   -1    0    0    0    0    0    0    0    0    0
+  913           0.00           0.20    2    0    2    4    1    0    0    0    0    0    0    0    0    0
+  914           0.00          -0.20    1   -1   -2   -4   -1    0    0    0    0    0    0    0    0    0
+  915           0.00          -0.20    1    0   -4   -2   -1    0    0    0    0    0    0    0    0    0
+  916           0.00           0.20    1   -2    2    2    1    0    0    0    0    0    0    0    0    0
+  917           0.00           0.20    1    0   -2   -6   -1    0    0    0    0    0    0    0    0    0
+  918           0.00          -0.20    3    1    2   -2    1    0    0    0    0    0    0    0    0    0
+  919           0.00          -0.20    2   -2    0   -2   -2    0    0    0    0    0    0    0    0    0
+  920           0.00           0.20    2   -1    0    2    1    0    0    0    0    0    0    0    0    0
+  921           0.00           0.20    3    0    0    0    2    0    0    0    0    0    0    0    0    0
+  922           0.00          -0.20    1   -1    2   -3    1    0    0    0    0    0    0    0    0    0
+  923           0.00           0.20    0    0    1   -1    2    0   -5    7    0    0    0    0    0    0
+  924           0.00          -0.20    2    1    0   -2   -2    0    0    0    0    0    0    0    0    0
+  925          -0.20           0.00    0    0    1   -1    2    0    0   -1    0   -1    0    0    0    0
+  926           0.00          -0.20    2    1   -2    0    0    0    0    0    0    0    0    0    0    0
+  927           0.00           0.20    1    0   -2    0    2    0    0    0    0    0    0    0    0    0
+  928           0.00          -0.20    1   -1    0    0   -2    0    0    0    0    0    0    0    0    0
+  929           0.00          -0.20    0    2    0    0    2    0    0    0    0    0    0    0    0    0
+  930          -0.20           0.00    0    0    0    0    1    0    3   -4    0    0    0    0    0    0
+  931           0.00          -0.20    0    0    0    0    1    0    0    2   -2    0    0    0    0    0
+  932           0.00           0.20    0    0    0    0    1    0    0    2    0   -2    0    0    0    0
+  933           0.00          -0.20    0    1    2   -4    2    0    0    0    0    0    0    0    0    0
+  934           0.00           0.20    0    0    1   -1    2    0   -8   12    0    0    0    0    0    0
+  935           0.00           0.20    1    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+  936           0.00          -0.19    0    1    2   -2    0    0    0    0    0    0    0    0    0    0
+  937           0.00           0.17    0    1   -2    2   -3    0    0    0    0    0    0    0    0    0
+  938           0.00           0.11    1    0   -2    0   -3    0    0    0    0    0    0    0    0    0
+  939           0.00          -0.10    1    1   -2   -2    0    0    0    0    0    0    0    0    0    0
+  940           0.10           0.00    0    0    0    0    1    0    0   -1    0    2    0    0    0    0
+  941           0.00          -0.10    0    1   -2   -2    0    0    0    0    0    0    0    0    0    0
+  942           0.00           0.10    1    0    0    0   -1    0  -10    3    0    0    0    0    0    0
+  943           0.00          -0.10    1    0    0    0    1    0  -10    3    0    0    0    0    0    0
+  944           0.00          -0.10    1    0   -2   -4    0    0    0    0    0    0    0    0    0    0
+  945           0.00          -0.10    2   -1    0    2    0    0    0    0    0    0    0    0    0    0
+  946           0.00          -0.10    0    2    0    0   -2    0    0    0    0    0    0    0    0    0
+  947           0.00          -0.10    1    0    0   -6    0    0    0    0    0    0    0    0    0    0
+  948           0.00           0.10    0    1    4   -4    2    0    0    0    0    0    0    0    0    0
+  949           0.00          -0.10    2    0    0   -6    0    0    0    0    0    0    0    0    0    0
+  950          -0.10           0.00    0    0    0    0    2    0    0    0    0    1    0    0    0    0
+  951           0.00          -0.10    3    0    0   -4    0    0    0    0    0    0    0    0    0    0
+  952           0.00          -0.10    2   -1   -2    0    1    0    0    0    0    0    0    0    0    0
+  953           0.00           0.10    2    2    0   -2   -1    0    0    0    0    0    0    0    0    0
+  954           0.00          -0.10    0    0    0    3    2    0    0    0    0    0    0    0    0    0
+  955           0.00          -0.10    3   -1    0    0    0    0    0    0    0    0    0    0    0    0
+  956           0.10           0.00    0    0    2    0    2    0    2   -3    0    0    0    0    0    0
+  957           0.10           0.00    0    0    2    0    2    0   -2    3    0    0    0    0    0    0
+  958           0.00           0.10    0    0    2    0    2    0    2   -2    0    0    0    0    0    0
+  959           0.00          -0.10    0    0    2    0    2    0   -2    2    0    0    0    0    0    0
+  960           0.00          -0.10    2   -1   -2    0    0    0    0    0    0    0    0    0    0    0
+  961           0.00           0.10    2    0   -2   -2   -2    0    0   -2    0    2    0    0    0    0
+  962           0.00           0.10    1    2    2   -4    1    0    0    0    0    0    0    0    0    0
+
+j = 1  Nb of terms = 277
+
+  963      153041.82         878.89    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+  964       11714.49        -289.32    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+  965        2024.68         -50.99    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+  966       -1837.33          47.75    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+  967       -1312.21         -28.91    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+  968        -632.54           0.78    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+  969         459.68         -67.23    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+  970         344.50           1.46    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+  971         268.14          -7.03    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+  972         192.06          29.80    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+  973         139.64           0.15    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+  974        -113.94          -1.06    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+  975         109.81           3.18    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+  976         -56.37           0.13    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+  977         -56.17          -0.02    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+  978         -53.05          -1.23    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+  979         -51.60           0.17    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+  980          45.91          -0.11    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+  981         -42.45           0.02    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+  982          40.82          -1.03    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+  983          34.30          -1.24    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+  984          28.89           0.00    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+  985          27.61          -1.22    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+  986         -25.43           1.00    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+  987         -26.01           0.07    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+  988         -23.02           0.06    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+  989          19.37          -0.01    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+  990          14.05          -4.19    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+  991          18.18          -0.01    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+  992         -14.86          -0.09    0    2    0    0    0    0    0    0    0    0    0    0    0    0
+  993          13.49          -0.01    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+  994          12.44          -0.27    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+  995          11.46           0.03    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+  996         -11.33          -0.06    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+  997          -9.81           0.01    2    0   -2    0    0    0    0    0    0    0    0    0    0    0
+  998          -9.08          -0.02    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+  999           2.74          -4.56    0    0    1   -1    1    0    0   -1    0   -2    5    0    0    0
+ 1000           6.84          -0.04    1    0    2    2    2    0    0    0    0    0    0    0    0    0
+ 1001          -6.73           0.01    0    1    2    0    2    0    0    0    0    0    0    0    0    0
+ 1002           6.54           0.01    1    1    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1003          -6.35          -0.01    0    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1004           5.90          -0.02    0    0    2    2    1    0    0    0    0    0    0    0    0    0
+ 1005          -5.85           0.02    1    0    0    2    0    0    0    0    0    0    0    0    0    0
+ 1006          -5.73           0.01    2    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1007           5.60           0.00    0    0    0    2    1    0    0    0    0    0    0    0    0    0
+ 1008          -5.16           0.00    1    0    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1009          -5.14           0.01    2    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1010           4.76          -0.02    2    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1011          -4.40           0.02    0    0    0    2   -1    0    0    0    0    0    0    0    0    0
+ 1012          -4.22           0.00    0    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+ 1013          -4.20           0.01    1   -1    0    0    0    0    0    0    0    0    0    0    0    0
+ 1014           3.58           0.31    1    0    0   -1    0    0    0    0    0    0    0    0    0    0
+ 1015           3.87           0.01    0    1    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1016           3.76           0.00    0    0    0    1    0    0    0    0    0    0    0    0    0    0
+ 1017          -3.62          -0.01    2    0    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1018          -3.61           0.00    1    0   -2    0    0    0    0    0    0    0    0    0    0    0
+ 1019          -1.28          -2.14    0    0    0    0    0    0    0    0    0    2   -5    0    0   -1
+ 1020          -3.18           0.00    0    1    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1021           3.01           0.00    1    1    0    0    0    0    0    0    0    0    0    0    0    0
+ 1022          -2.97           0.01    1    0    2    0    0    0    0    0    0    0    0    0    0    0
+ 1023           2.91           0.00    1   -1    0   -1    0    0    0    0    0    0    0    0    0    0
+ 1024          -2.73           0.00    2    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1025           2.58          -0.01    3    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1026           2.56          -0.01    1   -1    2    0    2    0    0    0    0    0    0    0    0    0
+ 1027          -2.51          -0.01    1    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1028          -2.35          -0.01    0    1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1029          -2.21           0.01    1    1    2    0    2    0    0    0    0    0    0    0    0    0
+ 1030          -2.04           0.01    2    0    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1031          -1.94           0.00    2    0    0    0    1    0    0    0    0    0    0    0    0    0
+ 1032           0.41          -1.43    0    0    0    0    0    0    0    4   -8    3    0    0    0    0
+ 1033          -1.84           0.00    0    0    0    0    0    0    3   -5    0    0    0    0    0   -2
+ 1034          -1.77           0.01    1    0   -2    2   -1    0    0    0    0    0    0    0    0    0
+ 1035           0.00           1.77    0    1   -1    1   -1    0    0    0    0    0    0    0    0    0
+ 1036           1.76           0.00    1    0    0    0    2    0    0    0    0    0    0    0    0    0
+ 1037          -1.07          -0.53    0    0    1   -1    1    0   -8   12    0    0    0    0    0    0
+ 1038          -1.48           0.00    0    0    2    1    2    0    0    0    0    0    0    0    0    0
+ 1039          -1.40           0.01    3    0    0    0    0    0    0    0    0    0    0    0    0    0
+ 1040          -1.35          -0.01    1    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+ 1041          -1.32           0.00    0    0    0    0    0    0    1   -1    0    0    0    0    0    0
+ 1042          -1.28           0.00    0    0    0    0    0    0    0    8  -16    4    5    0    0    0
+ 1043           1.24           0.00    1    0    0    0   -2    0    0    0    0    0    0    0    0    0
+ 1044           1.23           0.00    2    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1045           1.19           0.00    1    0    0   -4    0    0    0    0    0    0    0    0    0    0
+ 1046           1.18          -0.01    1    0    2    2    1    0    0    0    0    0    0    0    0    0
+ 1047           1.17           0.00    1   -1    0   -1   -1    0    0    0    0    0    0    0    0    0
+ 1048          -1.15           0.00    1    1    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1049           1.14           0.00    2    0    0   -4    0    0    0    0    0    0    0    0    0    0
+ 1050          -1.14           0.00    0    2   -2    2   -1    0    0    0    0    0    0    0    0    0
+ 1051           1.09           0.03    0    0    0    0    0    0    0    1    0   -1    0    0    0    0
+ 1052          -1.08           0.00    2    0   -2   -4   -2    0    0    0    0    0    0    0    0    0
+ 1053           1.04           0.00    0    0    0    0    0    0    0    0    0    2    0    0    0    2
+ 1054           1.02           0.00    1    0   -4    0   -2    0    0    0    0    0    0    0    0    0
+ 1055           0.98          -0.01    2    0    2    2    2    0    0    0    0    0    0    0    0    0
+ 1056           0.91           0.02    1    0    0   -1   -1    0    0    0    0    0    0    0    0    0
+ 1057           0.00           0.93    1    0   -1    0   -1    0    0    0    0    0    0    0    0    0
+ 1058          -0.91           0.00    2    0    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1059          -0.90           0.00    2    1    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1060           0.86           0.00    1    0    0    2    1    0    0    0    0    0    0    0    0    0
+ 1061          -0.84           0.00    1   -1    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1062          -0.83           0.00    3    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1063          -0.82           0.00    0    0    4   -2    2    0    0    0    0    0    0    0    0    0
+ 1064           0.41           0.39    0    0    0    0    1    0    0   -1    2    0    0    0    0    0
+ 1065           0.40          -0.38    0    0    0    0    0    0    0    1   -2    0    0    0    0    0
+ 1066           0.78           0.00    0    1   -2    2    0    0    0    0    0    0    0    0    0    0
+ 1067           0.74           0.00    0    0    2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1068          -0.73           0.00    0    1    2    0    1    0    0    0    0    0    0    0    0    0
+ 1069           0.68           0.00    1    0    2   -2    0    0    0    0    0    0    0    0    0    0
+ 1070           0.66           0.00    1    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1071          -0.64           0.00    2    0   -2    0    1    0    0    0    0    0    0    0    0    0
+ 1072          -0.63           0.00    0    1    0    0    2    0    0    0    0    0    0    0    0    0
+ 1073           0.63           0.00    0    0    2   -1    2    0    0    0    0    0    0    0    0    0
+ 1074           0.62           0.00    0    0    2    4    2    0    0    0    0    0    0    0    0    0
+ 1075           0.60           0.00    0    1    0    2    0    0    0    0    0    0    0    0    0    0
+ 1076          -0.59           0.00    0    0    2    0   -1    0    0    0    0    0    0    0    0    0
+ 1077          -0.59           0.00    0    1   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1078           0.59           0.00    0    1    2   -2    0    0    0    0    0    0    0    0    0    0
+ 1079           0.57           0.00    1    0   -2   -2    0    0    0    0    0    0    0    0    0    0
+ 1080           0.38          -0.19    0    0    0    0    0    0    8  -13    0    0    0    0    0   -1
+ 1081          -0.01          -0.55    0    0    0    0    0    0    2   -3    0    0    0    0    0    0
+ 1082           0.44          -0.11    0    0    0    0    0    0    0    0    0    2   -5    0    0    0
+ 1083           0.53           0.00    0    0    0    0    0    0    2   -2    0    0    0    0    0    0
+ 1084          -0.53           0.00    1   -1    0   -1   -2    0    0    0    0    0    0    0    0    0
+ 1085           0.52           0.00    1   -1    2    2    2    0    0    0    0    0    0    0    0    0
+ 1086          -0.52           0.00    2    0    0    2    0    0    0    0    0    0    0    0    0    0
+ 1087           0.53           0.00    0    1    0    0   -2    0    0    0    0    0    0    0    0    0
+ 1088           0.52           0.00    1    1    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1089           0.51           0.00    1    0   -2    2    0    0    0    0    0    0    0    0    0    0
+ 1090           0.51           0.00    1   -1   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1091          -0.21          -0.30    0    0    0    0    0    0    8  -13    0    0    0    0    0    0
+ 1092          -0.50           0.00    0    1    0    1    0    0    0    0    0    0    0    0    0    0
+ 1093          -0.11           0.37    0    0    0    0    0    0    0    2   -8    3    0    0    0   -2
+ 1094          -0.11           0.37    0    0    0    0    0    0    0    6   -8    3    0    0    0    2
+ 1095          -0.48           0.00    0    1    2    2    2    0    0    0    0    0    0    0    0    0
+ 1096          -0.46          -0.01    0    0    0    0    0    0    0    3    0   -1    0    0    0    2
+ 1097          -0.47           0.00    1   -1    0    0    1    0    0    0    0    0    0    0    0    0
+ 1098          -0.03           0.43    0    0    1   -1    1    0    0   -1    0    2   -5    0    0    0
+ 1099           0.45           0.00    3    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1100          -0.44           0.00    0    0    0    4    0    0    0    0    0    0    0    0    0    0
+ 1101          -0.44           0.00    1    0    0    2   -1    0    0    0    0    0    0    0    0    0
+ 1102          -0.44           0.00    1   -1    0    2    0    0    0    0    0    0    0    0    0    0
+ 1103           0.43           0.00    2   -1    2    0    2    0    0    0    0    0    0    0    0    0
+ 1104           0.44           0.00    0    0    0    0    0    0    4   -6    0    0    0    0    0   -2
+ 1105           0.42           0.00    0    0    0    2    2    0    0    0    0    0    0    0    0    0
+ 1106          -0.42           0.00    1    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1107           0.41           0.00    1   -1   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1108          -0.41           0.00    0    0    0    0    0    0    2   -4    0    0    0    0    0   -2
+ 1109           0.02           0.39    0    0    2   -2    1    0   -5    6    0    0    0    0    0    0
+ 1110           0.40           0.00    1    0    2   -4    1    0    0    0    0    0    0    0    0    0
+ 1111          -0.40           0.00    0    1   -2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1112          -0.39           0.00    2    0    0   -2    0    0    0   -2    0    2    0    0    0    0
+ 1113           0.39           0.00    0    3    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1114           0.15          -0.24    0    0    0    0    0    0    0    1    0   -2    0    0    0    0
+ 1115          -0.37          -0.01    0    0    0    0    0    0    0    2    0   -2    0    0    0    0
+ 1116           0.37           0.00    1   -1    2    0    1    0    0    0    0    0    0    0    0    0
+ 1117          -0.37           0.00    0    0    2    2    0    0    0    0    0    0    0    0    0    0
+ 1118          -0.37           0.00    2    1    2    0    2    0    0    0    0    0    0    0    0    0
+ 1119          -0.31           0.06    2    0    0   -2    0    0    0   -2    0    3    0    0    0    0
+ 1120          -0.35           0.00    1    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+ 1121           0.35           0.00    0    0    0    1    1    0    0    0    0    0    0    0    0    0
+ 1122          -0.07          -0.27    0    0    0    0    1    0    0   -4    8   -3    0    0    0    0
+ 1123          -0.33           0.01    0    0    0    0    0    0    2    0    0    0    0    0    0    2
+ 1124          -0.33           0.00    2   -1    0    0    0    0    0    0    0    0    0    0    0    0
+ 1125           0.07          -0.26    0    0    0    0    1    0    0    4   -8    3    0    0    0    0
+ 1126           0.33           0.00    0    0    0    0    0    0    0    2   -2    0    0    0    0    0
+ 1127           0.00          -0.32    1    0   -1    0   -2    0    0    0    0    0    0    0    0    0
+ 1128           0.32           0.00    1    1    0    0    1    0    0    0    0    0    0    0    0    0
+ 1129          -0.32           0.00    1    1    2    0    1    0    0    0    0    0    0    0    0    0
+ 1130           0.32           0.00    1    0   -2    2   -2    0    0    0    0    0    0    0    0    0
+ 1131          -0.24          -0.07    0    0    1   -1    1    0    0    0   -2    0    0    0    0    0
+ 1132           0.24           0.07    0    0    1   -1    0    0    0    0   -2    0    0    0    0    0
+ 1133           0.30           0.00    0    0    0    0    0    0    1    1    0    0    0    0    0    2
+ 1134           0.08          -0.22    0    0    0    0    0    0    0    0    0    1    0    0    0    0
+ 1135          -0.30           0.00    1    0    2    1    2    0    0    0    0    0    0    0    0    0
+ 1136          -0.30           0.00    2    0    2    0    0    0    0    0    0    0    0    0    0    0
+ 1137           0.30           0.00    0    1    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1138           0.30           0.00    1    0    2   -1    2    0    0    0    0    0    0    0    0    0
+ 1139           0.00          -0.29    0    0    0    0    0    0    3   -4    0    0    0    0    0    0
+ 1140           0.00          -0.29    1    0   -1    0    0    0    0    0    0    0    0    0    0    0
+ 1141           0.20          -0.09    1    0    0    0    0    0  -18   16    0    0    0    0    0    0
+ 1142           0.29           0.00    1    0    0    1    0    0    0    0    0    0    0    0    0    0
+ 1143          -0.05          -0.24    0    0    0    0    0    0    0    0    0    2    0    0    0    1
+ 1144           0.29           0.00    0    0    0    1   -1    0    0    0    0    0    0    0    0    0
+ 1145          -0.27           0.00    1    0    0   -2    2    0    0    0    0    0    0    0    0    0
+ 1146          -0.19          -0.08    1    0    0    0    0    0  -10    3    0    0    0    0    0    0
+ 1147          -0.27           0.00    1   -1    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1148           0.25           0.00    2    1    0    0    0    0    0    0    0    0    0    0    0    0
+ 1149           0.25           0.00    2    0    0   -2   -1    0    0   -2    0    2    0    0    0    0
+ 1150          -0.25           0.00    0    0    2    1    1    0    0    0    0    0    0    0    0    0
+ 1151           0.25           0.00    1    2    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1152          -0.25           0.00    0    3    0    0    0    0    0    0    0    0    0    0    0    0
+ 1153          -0.01           0.23    0    0    0    0    0    0    5   -8    0    0    0    0    0   -2
+ 1154          -0.23           0.00    1    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+ 1155           0.23           0.00    0    0    0    0    0    0    0    1    0    1    0    0    0    2
+ 1156           0.23           0.00    4    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1157          -0.15          -0.07    0    0    1   -1    1    0    0   -1    0    0   -1    0    0    0
+ 1158          -0.23           0.00    1    0   -2    0    1    0    0    0    0    0    0    0    0    0
+ 1159          -0.22           0.00    2    1    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1160           0.22           0.00    0    1    2    1    2    0    0    0    0    0    0    0    0    0
+ 1161          -0.22           0.00    1    1    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1162          -0.22           0.00    1    0    4   -2    2    0    0    0    0    0    0    0    0    0
+ 1163           0.04          -0.17    0    0    1   -1    1    0    0   -1    0   -1    0    0    0    0
+ 1164          -0.01          -0.21    0    0    2   -2    0    0   -5    6    0    0    0    0    0    0
+ 1165           0.08          -0.14    0    0    0    0    0    0    0    2   -4    0    0    0    0    0
+ 1166          -0.01           0.19    0    0    0    0    0    0    0    0    0    2   -5    0    0    1
+ 1167           0.21           0.00    2    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1168          -0.20           0.00    1    0   -2    1   -1    0    0    0    0    0    0    0    0    0
+ 1169          -0.20           0.00    2   -2    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1170          -0.04          -0.16    0    0    0    0    0    0    8  -13    0    0    0    0    0   -2
+ 1171           0.19           0.00    0    2    0   -2    0    0    0    0    0    0    0    0    0    0
+ 1172           0.19           0.00    1    1    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1173          -0.19           0.00    2    0   -2   -4   -1    0    0    0    0    0    0    0    0    0
+ 1174           0.18           0.00    0    0    0    0    0    0    5   -7    0    0    0    0    0   -2
+ 1175          -0.18           0.00    0    1    0    2    1    0    0    0    0    0    0    0    0    0
+ 1176           0.18           0.00    1    0   -4    0   -1    0    0    0    0    0    0    0    0    0
+ 1177           0.17           0.00    2    0    2    2    1    0    0    0    0    0    0    0    0    0
+ 1178          -0.12           0.06    1    0    2    0    2    0    0    1    0    0    0    0    0    0
+ 1179           0.13          -0.04    0    0    0    0    0    0    3   -5    0    0    0    0    0    0
+ 1180          -0.11           0.06    1    0   -2    0   -2    0    0    4   -8    3    0    0    0    0
+ 1181           0.17           0.00    0    0    2   -3    2    0    0    0    0    0    0    0    0    0
+ 1182           0.16           0.00    0    0    0    0    0    0    0    4    0   -2    0    0    0    2
+ 1183          -0.17           0.00    0    0    4    0    2    0    0    0    0    0    0    0    0    0
+ 1184          -0.17           0.00    1    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+ 1185          -0.14           0.02    1    0    0   -1    1    0    0    0    0    0    0    0    0    0
+ 1186           0.14           0.03    0    0    0    0    0    0    0    2    0   -1    0    0    0    2
+ 1187           0.00           0.15    0    0    0    0    0    0    2   -1    0    0    0    0    0    2
+ 1188          -0.15           0.00    1    1   -2   -4   -2    0    0    0    0    0    0    0    0    0
+ 1189          -0.14           0.01    0    0    0    0    0    0    0    2    0    1    0    0    0    2
+ 1190           0.16           0.00    2    0    0   -2   -2    0    0    0    0    0    0    0    0    0
+ 1191          -0.06           0.10    0    0    0    0    0    0    8  -11    0    0    0    0    0   -2
+ 1192           0.05           0.10    0    0    0    0    0    0    0    8  -16    4    5    0    0   -2
+ 1193           0.02           0.13    0    0    1   -1    1    0    0   -1    0    2    0    0    0    0
+ 1194          -0.11           0.04    0    0    0    0    0    0    0    8  -16    4    5    0    0    2
+ 1195          -0.12          -0.02    0    0    1   -1    1    0   -5    7    0    0    0    0    0    0
+ 1196          -0.05          -0.10    0    0    0    0    0    0    0    0    0    0    2    0    0    1
+ 1197           0.14           0.00    1    0    2    4    2    0    0    0    0    0    0    0    0    0
+ 1198          -0.09           0.05    1    0    0   -2    0    0   19  -21    3    0    0    0    0    0
+ 1199           0.00           0.14    0    0    1    0    1    0    0    0    0    0    0    0    0    0
+ 1200           0.14           0.00    3    0    0   -4    0    0    0    0    0    0    0    0    0    0
+ 1201          -0.14           0.00    1    1   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1202           0.04           0.10    0    0    1   -1    1    0    0   -1    0    0    2    0    0    0
+ 1203          -0.06           0.08    0    0    0    0    0    0    0    3    0   -2    0    0    0    2
+ 1204           0.05           0.09    0    0    0    0    0    0    8  -15    0    0    0    0    0   -2
+ 1205          -0.14           0.00    0    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1206           0.08           0.06    0    0    0    0    0    0    0    1    2    0    0    0    0    2
+ 1207           0.14           0.00    0    0    0    4    1    0    0    0    0    0    0    0    0    0
+ 1208           0.14           0.00    0    0    2   -4    1    0    0    0    0    0    0    0    0    0
+ 1209           0.13           0.00    1    1    0   -4    0    0    0    0    0    0    0    0    0    0
+ 1210          -0.07           0.06    0    0    0    0    0    0    0    3   -2    0    0    0    0    2
+ 1211           0.11          -0.02    0    0    0    0    0    0    0    0    0    3    0    0    0    2
+ 1212          -0.13           0.00    3    0    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1213          -0.13           0.00    0    0    0    0    0    0    0    4   -2    0    0    0    0    2
+ 1214          -0.13           0.00    1    1    2    2    2    0    0    0    0    0    0    0    0    0
+ 1215          -0.13           0.00    0    0    4   -2    1    0    0    0    0    0    0    0    0    0
+ 1216          -0.12           0.00    2    0    0   -2    0    0   -3    3    0    0    0    0    0    0
+ 1217           0.12           0.00    3    0    2    2    2    0    0    0    0    0    0    0    0    0
+ 1218           0.12           0.00    0    0    0    0    0    0    0    0    0    0    2    0    0    2
+ 1219          -0.12           0.00    2    1    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1220           0.00          -0.12    1    0    0   -1    0    0   -3    4    0    0    0    0    0    0
+ 1221          -0.02          -0.09    0    0    0    0    0    0    4   -6    0    0    0    0    0   -1
+ 1222           0.02          -0.09    0    0    0    0    0    0    1   -1    0    0    0    0    0   -1
+ 1223          -0.11           0.00    0    2   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1224           0.11           0.00    2    0    0   -4   -1    0    0    0    0    0    0    0    0    0
+ 1225           0.07          -0.04    0    0    0    0    0    0    0    3   -4    0    0    0    0    0
+ 1226           0.11           0.00    0    0    2    4    1    0    0    0    0    0    0    0    0    0
+ 1227           0.11           0.00    0    0    0    2   -2    0    0    0    0    0    0    0    0    0
+ 1228          -0.11           0.00    3    0    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1229           0.10           0.00    0    0    0    0    0    0    0    1    0   -3    0    0    0   -2
+ 1230          -0.10           0.00    0    0    2   -2    1    0   -3    3    0    0    0    0    0    0
+ 1231           0.10           0.00    0    0    0    0    0    0    6   -8    0    0    0    0    0   -2
+ 1232           0.10           0.00    2    1    0   -4    0    0    0    0    0    0    0    0    0    0
+ 1233           0.10           0.00    0    0    1   -1    1    0    0    3   -8    3    0    0    0    0
+ 1234           0.00           0.10    0    0    0    0    0    0    3   -2    0    0    0    0    0    2
+ 1235           0.00           0.10    0    0    0    0    0    0    2   -5    0    0    0    0    0   -2
+ 1236          -0.10           0.00    4    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1237           0.10           0.00    2    0    0    2    1    0    0    0    0    0    0    0    0    0
+ 1238          -0.10           0.00    1   -1    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1239           0.10           0.00    0    0    0    0    0    0    1   -3    0    0    0    0    0   -2
+
+j = 2  Nb of terms = 30
+
+ 1240         121.15       -2301.27    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+ 1241          -0.98        -143.27    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1242          -0.27         -24.46    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1243           0.24          22.41    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+ 1244          -1.19          -5.61    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1245           3.57          -1.83    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+ 1246           0.24          -5.02    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1247          -0.04          -3.23    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1248          -0.48           2.40    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+ 1249          -0.10           1.73    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+ 1250          -0.01           1.33    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+ 1251          -0.04           0.83    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+ 1252          -0.05          -0.79    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1253           0.03          -0.66    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+ 1254           0.00          -0.64    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+ 1255           0.04           0.61    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1256          -0.01          -0.41    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+ 1257          -0.01           0.35    0    2   -2    2   -2    0    0    0    0    0    0    0    0    0
+ 1258          -0.01          -0.33    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1259           0.01           0.31    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1260           0.01           0.27    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 1261          -0.07          -0.17    0    2    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1262           0.07           0.17    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+ 1263           0.02          -0.21    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+ 1264           0.01           0.20    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+ 1265           0.01          -0.17    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+ 1266           0.01          -0.16    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+ 1267           0.00          -0.13    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0
+ 1268          -0.07          -0.04    0    0    1   -1    1    0    0   -1    0   -2    5    0    0    0
+ 1269           0.02           0.08    0    1    2    0    2    0    0    0    0    0    0    0    0    0
+ 
+j = 3  Nb of terms = 5
+
+ 1270         -15.23          -1.62    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+ 1271          -1.16          -0.01    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+ 1272          -0.20           0.00    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+ 1273           0.18           0.00    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+ 1274           0.13           0.00    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+ 
+j = 4  Nb of terms = 1
+
+ 1275          -0.01           0.11    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2c.coeff
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2c.coeff	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2c.coeff	(revision 22322)
@@ -0,0 +1,13 @@
+# Expression for the quantity s(t)+XY/2 based on the IAU2000A precession-nutation 
+# model 
+# 
+# 
+# Updated Table (12/11/2003) for ensuring continuity of UT1 on 1st January 2003
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# s + XY/2 = polynomial part + non-polynomial part
+# 
+# --------------------------------------------------------------------------------------------------------------
+# Polynomial part (unit microarcsecond)
+94.0 +3808.35 -119.94 -72574.09 +27.70 +15.61
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2c.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2c.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2c.dat	(revision 22322)
@@ -0,0 +1,100 @@
+# Expression for the quantity s(t)+XY/2 based on the IAU2000A precession-nutation 
+# model 
+# 
+# 
+# Updated Table (12/11/2003) for ensuring continuity of UT1 on 1st January 2003
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# s + XY/2 = polynomial part + non-polynomial part
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+# Non-polynomial part (unit microarcsecond)
+# (ARG being for various combination of the fundamental arguments of the nutation theory)
+# 
+#   Sum_i[C_{s,0})_i * sin(ARG) + C_{c,0})_i * cos(ARG)] 
+# 
+# + Sum_i)j=1,4 [C_{s,j})_i * t^j * sin(ARG) + C_{c,j})_i * cos(ARG)] * t^j]
+# 
+# The Table below provides the values for C_{s,j})_i and C_{c,j})_i
+# 
+# Cutoff (0.1 microarcsecond and periods less than 500 years)
+# 
+# The expressions for the fundamental arguments appearing in columns 4 to 8 (luni-solar part)
+# and in columns 6 to 17 (planetary part) are those of the IERS Conventions 2000
+# 
+# --------------------------------------------------------------------------------------------------------------
+# 
+#     i    C_{s,j})_i      C_{c,j})_i    l    l'   F    D   Om L_Me L_Ve  L_E L_Ma  L_J L_Sa  L_U L_Ne  p_A
+# 
+# --------------------------------------------------------------------------------------------------------------
+#  
+0       -2640.73           0.39    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+0         -63.53           0.02    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+0         -11.75          -0.01    0    0    2   -2    3    0    0    0    0    0    0    0    0    0
+0         -11.21          -0.01    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+0           4.57           0.00    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -2.02           0.00    0    0    2    0    3    0    0    0    0    0    0    0    0    0
+0          -1.98           0.00    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+0           1.72           0.00    0    0    0    0    3    0    0    0    0    0    0    0    0    0
+0           1.41           0.01    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+0           1.26           0.01    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.63           0.00    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+0           0.63           0.00    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+0          -0.46           0.00    0    1    2   -2    3    0    0    0    0    0    0    0    0    0
+0          -0.45           0.00    0    1    2   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.36           0.00    0    0    4   -4    4    0    0    0    0    0    0    0    0    0
+0           0.24           0.12    0    0    1   -1    1    0   -8   12    0    0    0    0    0    0
+0          -0.32           0.00    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+0          -0.28           0.00    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+0          -0.27           0.00    1    0    2    0    3    0    0    0    0    0    0    0    0    0
+0          -0.26           0.00    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+0           0.21           0.00    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+0          -0.19           0.00    0    1   -2    2   -3    0    0    0    0    0    0    0    0    0
+0          -0.18           0.00    0    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+0           0.10          -0.05    0    0    0    0    0    0    8  -13    0    0    0    0    0   -1
+0          -0.15           0.00    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+0           0.14           0.00    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+0           0.14           0.00    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+0          -0.14           0.00    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+0          -0.14           0.00    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+0          -0.13           0.00    0    0    4   -2    4    0    0    0    0    0    0    0    0    0
+0           0.11           0.00    0    0    2   -2    4    0    0    0    0    0    0    0    0    0
+0          -0.11           0.00    1    0   -2    0   -3    0    0    0    0    0    0    0    0    0
+0          -0.11           0.00    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+1          -0.07           3.57    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+1           1.71          -0.03    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+1           0.00           0.48    0    0    2   -2    3    0    0    0    0    0    0    0    0    0
+2         743.53          -0.17    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+2          56.91           0.06    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+2           9.84          -0.01    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+2          -8.85           0.01    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+2          -6.38          -0.05    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+2          -3.07           0.00    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+2           2.23           0.00    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+2           1.67           0.00    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+2           1.30           0.00    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+2           0.93           0.00    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+2           0.68           0.00    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+2          -0.55           0.00    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+2           0.53           0.00    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+2          -0.27           0.00    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+2          -0.27           0.00    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+2          -0.26           0.00    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+2          -0.25           0.00    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+2           0.22           0.00    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+2          -0.21           0.00    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+2           0.20           0.00    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+2           0.17           0.00    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+2           0.13           0.00    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+2          -0.13           0.00    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+2          -0.12           0.00    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+2          -0.11           0.00    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+3           0.30         -23.51    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+3          -0.03          -1.39    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+3          -0.01          -0.24    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+3           0.00           0.22    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+4          -0.26          -0.01    0    0    0    0    1    0    0    0    0    0    0    0    0    0
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2c.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2c.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tab5.2c.txt	(revision 22322)
@@ -0,0 +1,121 @@
+Expression for the quantity s(t)+XY/2 based on the IAU2000A precession-nutation 
+model 
+
+
+Updated Table (12/11/2003) for ensuring continuity of UT1 on 1st January 2003
+
+--------------------------------------------------------------------------------------------------------------
+
+s + XY/2 = polynomial part + non-polynomial part
+
+--------------------------------------------------------------------------------------------------------------
+
+Polynomial part (unit microarcsecond)
+
+  94.0 + 3808.35 t - 119.94 t^2 - 72574.09 t^3 + 27.70 t^4 + 15.61 t^5
+
+--------------------------------------------------------------------------------------------------------------
+
+Non-polynomial part (unit microarcsecond)
+(ARG being for various combination of the fundamental arguments of the nutation theory)
+
+  Sum_i[C_{s,0})_i * sin(ARG) + C_{c,0})_i * cos(ARG)] 
+
++ Sum_i)j=1,4 [C_{s,j})_i * t^j * sin(ARG) + C_{c,j})_i * cos(ARG)] * t^j]
+
+The Table below provides the values for C_{s,j})_i and C_{c,j})_i
+
+Cutoff (0.1 microarcsecond and periods less than 500 years)
+
+The expressions for the fundamental arguments appearing in columns 4 to 8 (luni-solar part)
+and in columns 6 to 17 (planetary part) are those of the IERS Conventions 2000
+
+--------------------------------------------------------------------------------------------------------------
+
+    i    C_{s,j})_i      C_{c,j})_i    l    l'   F    D   Om L_Me L_Ve  L_E L_Ma  L_J L_Sa  L_U L_Ne  p_A
+
+--------------------------------------------------------------------------------------------------------------
+ 
+j = 0  Nb of terms = 33
+
+    1       -2640.73           0.39    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+    2         -63.53           0.02    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+    3         -11.75          -0.01    0    0    2   -2    3    0    0    0    0    0    0    0    0    0
+    4         -11.21          -0.01    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+    5           4.57           0.00    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+    6          -2.02           0.00    0    0    2    0    3    0    0    0    0    0    0    0    0    0
+    7          -1.98           0.00    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+    8           1.72           0.00    0    0    0    0    3    0    0    0    0    0    0    0    0    0
+    9           1.41           0.01    0    1    0    0    1    0    0    0    0    0    0    0    0    0
+   10           1.26           0.01    0    1    0    0   -1    0    0    0    0    0    0    0    0    0
+   11           0.63           0.00    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+   12           0.63           0.00    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+   13          -0.46           0.00    0    1    2   -2    3    0    0    0    0    0    0    0    0    0
+   14          -0.45           0.00    0    1    2   -2    1    0    0    0    0    0    0    0    0    0
+   15          -0.36           0.00    0    0    4   -4    4    0    0    0    0    0    0    0    0    0
+   16           0.24           0.12    0    0    1   -1    1    0   -8   12    0    0    0    0    0    0
+   17          -0.32           0.00    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+   18          -0.28           0.00    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+   19          -0.27           0.00    1    0    2    0    3    0    0    0    0    0    0    0    0    0
+   20          -0.26           0.00    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+   21           0.21           0.00    0    0    2   -2    0    0    0    0    0    0    0    0    0    0
+   22          -0.19           0.00    0    1   -2    2   -3    0    0    0    0    0    0    0    0    0
+   23          -0.18           0.00    0    1   -2    2   -1    0    0    0    0    0    0    0    0    0
+   24           0.10          -0.05    0    0    0    0    0    0    8  -13    0    0    0    0    0   -1
+   25          -0.15           0.00    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+   26           0.14           0.00    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+   27           0.14           0.00    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+   28          -0.14           0.00    1    0    0   -2    1    0    0    0    0    0    0    0    0    0
+   29          -0.14           0.00    1    0    0   -2   -1    0    0    0    0    0    0    0    0    0
+   30          -0.13           0.00    0    0    4   -2    4    0    0    0    0    0    0    0    0    0
+   31           0.11           0.00    0    0    2   -2    4    0    0    0    0    0    0    0    0    0
+   32          -0.11           0.00    1    0   -2    0   -3    0    0    0    0    0    0    0    0    0
+   33          -0.11           0.00    1    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+ 
+j = 1  Nb of terms = 3
+
+   34          -0.07           3.57    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+   35           1.71          -0.03    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+   36           0.00           0.48    0    0    2   -2    3    0    0    0    0    0    0    0    0    0
+
+j = 2  Nb of terms = 25
+      
+   37         743.53          -0.17    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+   38          56.91           0.06    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+   39           9.84          -0.01    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+   40          -8.85           0.01    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+   41          -6.38          -0.05    0    1    0    0    0    0    0    0    0    0    0    0    0    0
+   42          -3.07           0.00    1    0    0    0    0    0    0    0    0    0    0    0    0    0
+   43           2.23           0.00    0    1    2   -2    2    0    0    0    0    0    0    0    0    0
+   44           1.67           0.00    0    0    2    0    1    0    0    0    0    0    0    0    0    0
+   45           1.30           0.00    1    0    2    0    2    0    0    0    0    0    0    0    0    0
+   46           0.93           0.00    0    1   -2    2   -2    0    0    0    0    0    0    0    0    0
+   47           0.68           0.00    1    0    0   -2    0    0    0    0    0    0    0    0    0    0
+   48          -0.55           0.00    0    0    2   -2    1    0    0    0    0    0    0    0    0    0
+   49           0.53           0.00    1    0   -2    0   -2    0    0    0    0    0    0    0    0    0
+   50          -0.27           0.00    0    0    0    2    0    0    0    0    0    0    0    0    0    0
+   51          -0.27           0.00    1    0    0    0    1    0    0    0    0    0    0    0    0    0
+   52          -0.26           0.00    1    0   -2   -2   -2    0    0    0    0    0    0    0    0    0
+   53          -0.25           0.00    1    0    0    0   -1    0    0    0    0    0    0    0    0    0
+   54           0.22           0.00    1    0    2    0    1    0    0    0    0    0    0    0    0    0
+   55          -0.21           0.00    2    0    0   -2    0    0    0    0    0    0    0    0    0    0
+   56           0.20           0.00    2    0   -2    0   -1    0    0    0    0    0    0    0    0    0
+   57           0.17           0.00    0    0    2    2    2    0    0    0    0    0    0    0    0    0
+   58           0.13           0.00    2    0    2    0    2    0    0    0    0    0    0    0    0    0
+   59          -0.13           0.00    2    0    0    0    0    0    0    0    0    0    0    0    0    0
+   60          -0.12           0.00    1    0    2   -2    2    0    0    0    0    0    0    0    0    0
+   61          -0.11           0.00    0    0    2    0    0    0    0    0    0    0    0    0    0    0
+ 
+j = 3  Nb of terms = 4
+
+   62           0.30         -23.51    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+   63          -0.03          -1.39    0    0    2   -2    2    0    0    0    0    0    0    0    0    0
+   64          -0.01          -0.24    0    0    2    0    2    0    0    0    0    0    0    0    0    0
+   65           0.00           0.22    0    0    0    0    2    0    0    0    0    0    0    0    0    0
+
+j = 4  Nb of terms = 1
+  
+   66          -0.26          -0.01    0    0    0    0    1    0    0    0    0    0    0    0    0    0
+
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tai_utc.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tai_utc.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tai_utc.dat	(revision 22322)
@@ -0,0 +1,50 @@
+#  tai-utc.dat
+#
+#  This file comes from http://maia.usno.navy.mil (Click on table of leap seconds since 1961). This file may
+#  also be directly downloaded from ftp://maia.usno.navy.mil/ser7/tai-utc.dat. See readme.tai-utc (located in
+#  the psLib/data directory) for details.
+#
+#  @author Ross Harman, MHPCC
+#
+#  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+#  @date $Date: 2006-09-23 02:20:18 $
+#
+#          JD           TAI-UTC    Const 1       Const 2
+#        (days)          (sec)      (none)       (none)
+#          psF64	         psF64      psF64         psF64
+        2437300.5      1.4228180    37300.0     0.0012960
+        2437512.5      1.3728180    37300.0     0.0012960
+        2437665.5      1.8458580    37665.0     0.0011232
+        2438334.5      1.9458580    37665.0     0.0011232
+        2438395.5      3.2401300    38761.0     0.0012960
+        2438486.5      3.3401300    38761.0     0.0012960
+        2438639.5      3.4401300    38761.0     0.0012960
+        2438761.5      3.5401300    38761.0     0.0012960
+        2438820.5      3.6401300    38761.0     0.0012960
+        2438942.5      3.7401300    38761.0     0.0012960
+        2439004.5      3.8401300    38761.0     0.0012960
+        2439126.5      4.3131700    39126.0     0.0025920
+        2439887.5      4.2131700    39126.0     0.0025920
+        2441317.5     10.0000000    41317.0     0.0000000
+        2441499.5     11.0000000    41317.0     0.0000000
+        2441683.5     12.0000000    41317.0     0.0000000
+        2442048.5     13.0000000    41317.0     0.0000000
+        2442413.5     14.0000000    41317.0     0.0000000
+        2442778.5     15.0000000    41317.0     0.0000000
+        2443144.5     16.0000000    41317.0     0.0000000
+        2443509.5     17.0000000    41317.0     0.0000000
+        2443874.5     18.0000000    41317.0     0.0000000
+        2444239.5     19.0000000    41317.0     0.0000000
+        2444786.5     20.0000000    41317.0     0.0000000
+        2445151.5     21.0000000    41317.0     0.0000000
+        2445516.5     22.0000000    41317.0     0.0000000
+        2446247.5     23.0000000    41317.0     0.0000000
+        2447161.5     24.0000000    41317.0     0.0000000
+        2447892.5     25.0000000    41317.0     0.0000000
+        2448257.5     26.0000000    41317.0     0.0000000
+        2448804.5     27.0000000    41317.0     0.0000000
+        2449169.5     28.0000000    41317.0     0.0000000
+        2449534.5     29.0000000    41317.0     0.0000000
+        2450083.5     30.0000000    41317.0     0.0000000
+        2450630.5     31.0000000    41317.0     0.0000000
+        2451179.5     32.0000000    41317.0     0.0000000
Index: /tags/sj_tags/sj_root_20080929/psLib/share/tai_utc.raw
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/share/tai_utc.raw	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/share/tai_utc.raw	(revision 22322)
@@ -0,0 +1,36 @@
+ 1961 JAN  1 =JD 2437300.5  TAI-UTC=   1.4228180 S + (MJD - 37300.) X 0.001296 S
+ 1961 AUG  1 =JD 2437512.5  TAI-UTC=   1.3728180 S + (MJD - 37300.) X 0.001296 S
+ 1962 JAN  1 =JD 2437665.5  TAI-UTC=   1.8458580 S + (MJD - 37665.) X 0.0011232S
+ 1963 NOV  1 =JD 2438334.5  TAI-UTC=   1.9458580 S + (MJD - 37665.) X 0.0011232S
+ 1964 JAN  1 =JD 2438395.5  TAI-UTC=   3.2401300 S + (MJD - 38761.) X 0.001296 S
+ 1964 APR  1 =JD 2438486.5  TAI-UTC=   3.3401300 S + (MJD - 38761.) X 0.001296 S
+ 1964 SEP  1 =JD 2438639.5  TAI-UTC=   3.4401300 S + (MJD - 38761.) X 0.001296 S
+ 1965 JAN  1 =JD 2438761.5  TAI-UTC=   3.5401300 S + (MJD - 38761.) X 0.001296 S
+ 1965 MAR  1 =JD 2438820.5  TAI-UTC=   3.6401300 S + (MJD - 38761.) X 0.001296 S
+ 1965 JUL  1 =JD 2438942.5  TAI-UTC=   3.7401300 S + (MJD - 38761.) X 0.001296 S
+ 1965 SEP  1 =JD 2439004.5  TAI-UTC=   3.8401300 S + (MJD - 38761.) X 0.001296 S
+ 1966 JAN  1 =JD 2439126.5  TAI-UTC=   4.3131700 S + (MJD - 39126.) X 0.002592 S
+ 1968 FEB  1 =JD 2439887.5  TAI-UTC=   4.2131700 S + (MJD - 39126.) X 0.002592 S
+ 1972 JAN  1 =JD 2441317.5  TAI-UTC=  10.0       S + (MJD - 41317.) X 0.0      S
+ 1972 JUL  1 =JD 2441499.5  TAI-UTC=  11.0       S + (MJD - 41317.) X 0.0      S
+ 1973 JAN  1 =JD 2441683.5  TAI-UTC=  12.0       S + (MJD - 41317.) X 0.0      S
+ 1974 JAN  1 =JD 2442048.5  TAI-UTC=  13.0       S + (MJD - 41317.) X 0.0      S
+ 1975 JAN  1 =JD 2442413.5  TAI-UTC=  14.0       S + (MJD - 41317.) X 0.0      S
+ 1976 JAN  1 =JD 2442778.5  TAI-UTC=  15.0       S + (MJD - 41317.) X 0.0      S
+ 1977 JAN  1 =JD 2443144.5  TAI-UTC=  16.0       S + (MJD - 41317.) X 0.0      S
+ 1978 JAN  1 =JD 2443509.5  TAI-UTC=  17.0       S + (MJD - 41317.) X 0.0      S
+ 1979 JAN  1 =JD 2443874.5  TAI-UTC=  18.0       S + (MJD - 41317.) X 0.0      S
+ 1980 JAN  1 =JD 2444239.5  TAI-UTC=  19.0       S + (MJD - 41317.) X 0.0      S
+ 1981 JUL  1 =JD 2444786.5  TAI-UTC=  20.0       S + (MJD - 41317.) X 0.0      S
+ 1982 JUL  1 =JD 2445151.5  TAI-UTC=  21.0       S + (MJD - 41317.) X 0.0      S
+ 1983 JUL  1 =JD 2445516.5  TAI-UTC=  22.0       S + (MJD - 41317.) X 0.0      S
+ 1985 JUL  1 =JD 2446247.5  TAI-UTC=  23.0       S + (MJD - 41317.) X 0.0      S
+ 1988 JAN  1 =JD 2447161.5  TAI-UTC=  24.0       S + (MJD - 41317.) X 0.0      S
+ 1990 JAN  1 =JD 2447892.5  TAI-UTC=  25.0       S + (MJD - 41317.) X 0.0      S
+ 1991 JAN  1 =JD 2448257.5  TAI-UTC=  26.0       S + (MJD - 41317.) X 0.0      S
+ 1992 JUL  1 =JD 2448804.5  TAI-UTC=  27.0       S + (MJD - 41317.) X 0.0      S
+ 1993 JUL  1 =JD 2449169.5  TAI-UTC=  28.0       S + (MJD - 41317.) X 0.0      S
+ 1994 JUL  1 =JD 2449534.5  TAI-UTC=  29.0       S + (MJD - 41317.) X 0.0      S
+ 1996 JAN  1 =JD 2450083.5  TAI-UTC=  30.0       S + (MJD - 41317.) X 0.0      S
+ 1997 JUL  1 =JD 2450630.5  TAI-UTC=  31.0       S + (MJD - 41317.) X 0.0      S
+ 1999 JAN  1 =JD 2451179.5  TAI-UTC=  32.0       S + (MJD - 41317.) X 0.0      S
Index: /tags/sj_tags/sj_root_20080929/psLib/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/.cvsignore	(revision 22322)
@@ -0,0 +1,14 @@
+DoxygenLog
+Makefile.in
+.deps
+.libs
+Makefile
+config.h
+*.la
+*.lo
+stamp-h1
+libpslib.la.temp
+config.h.in
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/Makefile.am	(revision 22322)
@@ -0,0 +1,23 @@
+SUBDIRS = $(SRCDIRS)
+
+lib_LTLIBRARIES = libpslib.la
+
+libpslib_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) 
+libpslib_la_LIBADD = $(SRCSUBLIBS) $(PSLIB_LIBS)
+libpslib_la_DEPENDENCIES = $(SRCSUBLIBS)
+libpslib_la_SOURCES =
+libpslib_la_LDFLAGS = -version-info $(PSLIB_LT_VERSION)
+
+
+EXTRA_DIST = mainpage.dox psErrorCodes_en.dat
+
+pkginclude_HEADERS = \
+	pslib.h \
+	pslib_strict.h
+
+install-exec-local: libpslib.la
+	$(PERL) -pe "s|(^dependency_libs=.*)|dependency_libs=\'$(PSLIB_LIBS)\'|" libpslib.la > libpslib.la.temp
+	cp libpslib.la.temp libpslib.la
+	rm -f libpslib.la.temp
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/TestCoverage
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/TestCoverage	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/TestCoverage	(revision 22322)
@@ -0,0 +1,236 @@
+#!/usr/bin/perl
+#
+#  This is a perl script
+#
+#  SYNOPSIS:  SlocCount options topLevelDirectory
+#
+#       where  options =
+#             --verbose     Display extra information to user
+#             --noverbose   Don't display extra information to user
+#             --recursiv e  Recursively run tests in directory tree
+#             --norecursive Test only the specified or current directory
+#             --silent      Don't display any information to user
+#             --nosilent    Display progress of script to user
+#
+#              arguements = directory(ies) to perform software line count
+#
+#  RETURN : zero upon successful parsing of directories
+#
+#  $Revision: 1.4 $  $Name: not supported by cvs2svn $
+#  $Date: 2005-09-23 19:03:12 $
+#
+#  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+#
+##############################################################################
+
+# Provide functions for determining the pathname of current working directory
+use Cwd;
+
+# Provides functions for handling long command line options
+use Getopt::Long;
+
+# Assign variables based on the presence of command line options to the script
+# The ! option allows for --nooption to be set to zero
+# (e.g. --noverbose --recursive causes $verbose=0 and $recursive=1)
+GetOptions("verbose!"=>\$verbose,
+           "recursive!"=>\$recursive,
+           "silent!"=>\$silent);
+
+# Check if both silent and verbose options are set and if so stop the script
+die "Can't specify both verbose and silent options." if ($verbose && $silent);
+
+# Initialize variables for counting the total lines of code, files
+$cFileCount = 0;
+
+# Initialize variable indicating how many arguements were passed
+$args = 0;
+
+open(COVFILE, "> coverageFileResult.txt");
+
+# Loop through all the arguements passed to the script
+foreach (@ARGV) {
+    # Remove newline if there is one
+    chomp;
+    # Increment number of arguements found
+    $args++;
+    # Set variable to current working directory
+    $cwd = cwd;
+    # Change directory to the directory of the arguement
+    chdir($_);
+    # Check for the recursive option
+    if ($recursive) {
+        # Invoke subroutine to go to the lowest directory in tree
+        # starting at the specified directory
+#        &worm($_);
+    }
+    # Change directory back to the directory where FullUnitTest was invoked
+    chdir($cwd);
+}
+
+# Check if there were no arguements specified
+if ($args == 0){
+    # Display message to user that all directories under the current will be
+    # tested
+    print("Recursively testing current directory tree.\n");
+    # Invoke subroutine to go to recursively test each directory in tree
+    &worm($ENV{"PWD"});
+}
+
+# Display summary of line counts
+print("\nSUMMARY STATISTICS\n");
+print("Files");
+print("$cFileCount");
+$totalFiles = $cFileCount;
+print("\nTotal\t$totalFiles\n\n");
+
+close(COVFILE);
+
+# Exit
+exit(0);
+
+
+################################################################################
+#
+# SUBROUTINE: worm
+#
+#     Description:  This subroutine will perform the necessary unit tests be
+#                   calling makeTestDrivers and executeTestDrivers  subroutines
+#                   and then check each subdirectory below the base directory
+#                   recursively.
+#
+#     Parameter(s):  base directory to counting slocs
+#
+################################################################################
+
+sub worm {
+    # Assign local variable to input parameter
+    local($base_dir) = @_;
+    local(@files, $i);
+
+    # Create array of entries found in directory
+    @files = <*>;
+
+    # Loop through the file list looking for another directory that is not
+    # labelled CVS and recursively invoke subroutine worm
+    $i = 0;
+    while ($files[$i]) {
+        # Check for directory and directory not labelled CVS
+        if (-d $files[$i] && ($files[$i] ne "CVS") && ($files[$i] ne "temp")
+                          && ($files[$i] ne "verified") && ($files[$i] ne ".libs")) {
+            # Change current directory to directory found
+            chdir($files[$i]);
+            # Invoke subroutine worm again
+            &worm("$base_dir/$files[$i]");
+            # Change current directory back to parent
+            chdir("..");
+        }
+        # Increment file list index
+        $i++;
+    }
+
+    # Count the number of files and test coverage in this directory
+    &getTestCoverage($base_dir);
+}
+
+################################################################################
+#
+#  SUBROUTINE: getSlocNumber
+#
+#      Description:  This subroutine will parse the output file for the sloc
+#                    count.
+#
+#      Parameter(s):  base directory where make and test drivers are located
+#
+################################################################################
+
+sub getFileCoverage{
+    local($base_dir) = @_;
+    local($retVal) = 0;
+
+    $fname = 0;
+
+    # Open the output file for reading if it exists
+    if ( -e "$base_dir/coverage" ) {
+        open(OUTFILE, "< $base_dir/coverage");
+        while($retVal = <OUTFILE>) {
+            @result = split(/ /,$retVal);
+
+            if( $result[6] !~ /file/) {
+               if( $result[0] !~ /Creat/) {
+                  print COVFILE ("$result[0]\t$result[2]\t$result[6]\t$result[7]");
+               }
+            }
+            else {
+               print COVFILE ("$result[0]\t$result[2]\t$result[6]\t$result[7]");
+            }
+       }
+
+        $retVal = s/\D//;
+        close(OUTFILE);
+    }
+
+    return($result[0]);
+}
+
+################################################################################
+#
+#  SUBROUTINE: getTestCoverage
+#
+#      Description:  This subroutine will
+#
+#      Parameter(s):  base directory where make and test drivers are located
+#
+################################################################################
+
+sub getTestCoverage{
+    local($base_dir) = @_;
+    local($pwd);
+
+    my($cfiles);
+
+    $cfiles = 0;
+
+    # Set variable to pwd to current test directory
+    # Display total for this directory
+    print("\n$base_dir\n");
+
+    $pwd = cwd;
+
+    # Create a list of all elements in base directory
+    @files = <*>;
+
+    # Loop through all the items in the files array
+    $initialTest = 0;
+    $j = 0;
+    while ( $files[$j]) {
+        # Check that the item is not a directory
+        if ( ! (-d $files[$j]) ) {
+
+            if ( ( $files[$j] =~ m@\.c$@ ) ) {
+
+                $objfile = $files[$j];
+                $objfile =~ s/\.c$/\.o/;
+                $objfile = `find $base_dir/.libs -name "*$objfile"`;
+                chop $objfile;
+
+                if (length($objfile) > 0) {
+                    # Increment total number of c files
+                    $cfiles++;
+
+                    # Call C source sloc count
+                    system("gcov -f -o $objfile $base_dir/$files[$j] > $base_dir/coverage");
+                    if ( $? == 0 ) {
+                        getFileCoverage($base_dir);
+                    }
+                    if ( -e "$base_dir/coverage" ) {
+                        system("rm $base_dir/coverage");
+                    }
+                }
+            }
+        }
+        $j++;
+    }
+    $cFileCount += $cfiles;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/Makefile.am	(revision 22322)
@@ -0,0 +1,22 @@
+#Makefile for astronomy functions of psLib
+#
+AM_CFLAGS=$(CFLAGS) -DPS_CONFIG_FILE_DEFAULT=\"$(sysconfdir)/pslib/pslib.config\"
+
+noinst_LTLIBRARIES = libpslibastro.la
+
+libpslibastro_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) 
+libpslibastro_la_SOURCES = \
+	psTime.c \
+	psCoord.c \
+	psSphereOps.c \
+        psEarthOrientation.c
+
+EXTRA_DIST = astro.i
+
+pkginclude_HEADERS = \
+	psTime.h \
+	psCoord.h \
+	psSphereOps.h \
+	psEarthOrientation.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/astro.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/astro.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/astro.i	(revision 22322)
@@ -0,0 +1,3 @@
+/* astro headers */
+%include "psCoord.h"
+%include "psTime.h"
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/parse_gcov-out.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/parse_gcov-out.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/parse_gcov-out.c	(revision 22322)
@@ -0,0 +1,114 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAXSTR 10000
+
+static char outStr[MAXSTR];
+static float average;
+static int sum;
+
+void parseFile(FILE *in, const char *filename);
+
+int main(void)
+{
+
+    FILE *output = NULL;
+    FILE *input = NULL;
+    average = 0.0;
+    sum = 0;
+    int N = 0;
+
+    input = fopen("sph_gcov.out", "r");
+    parseFile(input, "psSphereOps.c");
+    fclose(input);
+    N++;
+
+    input = fopen("eoc_gcov.out", "r");
+    parseFile(input, "psEarthOrientation.c");
+    fclose(input);
+    N++;
+
+    input = fopen("time_gcov.out", "r");
+    parseFile(input, "psTime.c");
+    fclose(input);
+    N++;
+
+    input = fopen("crd_gcov.out", "r");
+    parseFile(input, "psCoord.c");
+    fclose(input);
+    N++;
+
+    average = average / sum;
+
+    output = fopen("Coverage-Report.txt", "w");
+    fprintf(output, "\nTOTAL COVERAGE IN THE ../src/astro/ DIRECTORY\n");
+    fprintf(output, "\n%s", outStr);
+    fprintf(output, "  -------------------------------"
+            "-------------------------------------\n");
+    fprintf(output, "  ---> Total                        = Lines executed:");
+    fprintf(output, "%3.2f%%  of %d\n\n", average, sum);
+    fclose(output);
+    return 0;
+}
+
+void parseINT(const char *nums, float percent)
+{
+    sum += atoi(nums);
+    average += (float)((int)(percent * atoi(nums)));
+}
+
+float parseDBL(const char *nums)
+{
+    char temp[7];
+    int i = 0;
+    int j = 9;
+    float out = 0.0;
+    while (nums[j] != '%') {
+        temp[i] = nums[j];
+        i++;
+        j++;
+    }
+    //    average += (float)atof(temp);
+    out = (float)atof(temp);
+    return out;
+}
+
+void parseFile(FILE *in, const char *filename)
+{
+    char currentStr[100];
+    char searchStr[100];
+    char out[250];
+    sprintf(out, "  >><< %-25s    =", filename);
+    sprintf(searchStr, "'%s'", filename);
+    //    printf("\n searchStr = %s\n", searchStr);
+    strcat(outStr, out);
+    int i;
+    float numLines = 0.0;
+    while ( fscanf(in, "%s", currentStr) != EOF) {
+        if ( strncmp(currentStr, searchStr, 99) == 0 ) {
+            for (i = 0; i < 4; i++) {
+                if (i == 1) {
+                    fscanf(in, "%s", out);
+                    numLines = parseDBL(out);
+                    sprintf(currentStr, "%-16s", out);
+                } else if ( i == 3) {
+                    fscanf(in, "%s", out);
+                    parseINT(out, numLines);
+                    sprintf(currentStr, "%s", out);
+                } else {
+                    fscanf(in, "%s", currentStr);
+                }
+                strcat(outStr, " ");
+                strcat(outStr, currentStr);
+            }
+            continue;
+        }
+    }
+    strcat(outStr, "\n\n");
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/profile_tap
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/profile_tap	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/profile_tap	(revision 22322)
@@ -0,0 +1,9 @@
+
+gcov -f --object-file ./.libs/libpslibastro_la-psSphereOps.o psSphereOps.c > sph_gcov.out
+gcov -f --object-file ./.libs/libpslibastro_la-psEarthOrientation.o psEarthOrientation.c > eoc_gcov.out
+gcov -f --object-file ./.libs/libpslibastro_la-psTime.o psTime.c > time_gcov.out
+gcov -f --object-file ./.libs/libpslibastro_la-psCoord.o psCoord.c > crd_gcov.out
+gcc parse_gcov-out.c -o parse_gcov
+./parse_gcov
+rm *.out
+more Coverage-Report.txt
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/psCoord.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/psCoord.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/psCoord.c	(revision 22322)
@@ -0,0 +1,1252 @@
+/** @file  psCoord.c
+ *
+ *  @brief Contains basic coordinate transformation definitions and operations
+ *
+ *  This file defines the basic types for astronomical coordinate
+ *  transformation
+ *
+ *  @ingroup CoordinateTransform
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.142 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-12 00:59:00 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+
+#include "psType.h"
+#include "psCoord.h"
+#include "psMemory.h"
+#include "psTime.h"
+#include "psAssert.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psTrace.h"
+#include "psAbort.h"
+
+#include "psMatrix.h"
+#include "psMinimizePolyFit.h"
+
+# define ELIXIR_CODE 1
+
+static void planeFree(psPlane *p)
+{
+    // There are non dynamic allocated items
+}
+
+static void sphereFree(psSphere* s)
+{
+    // There are non dynamic allocated items
+}
+
+static void cubeFree(psCube* c)
+{
+    // There are non dynamic allocated items
+}
+
+static void planeTransformFree(psPlaneTransform *pt)
+{
+    psFree(pt->x);
+    psFree(pt->y);
+}
+
+/*****************************************************************************
+p_psPlaneTransformLinearInvert(transform): : this is a private function which
+simply inverts the supplied psPlaneTransform transform.  It assumes that
+"transform" is linear.
+
+XXX: This code no longer makes sense.  The merge must be reviewed.
+
+XXX: below is the code using the standard matrix representation.  note that
+this inversion requires x->nX == 1, y->nY == 1 and x->nY <= 1, y->nX <= 1
+*****************************************************************************/
+psPlaneTransform *p_psPlaneTransformLinearInvert(psPlaneTransform *transform)
+{
+    PS_ASSERT_PTR_NON_NULL(transform, NULL);
+    PS_ASSERT_PTR_NON_NULL(transform->x, NULL);
+    PS_ASSERT_PTR_NON_NULL(transform->y, NULL);
+    if ((transform->x->nX < 1) ||
+	(transform->x->nY < 1) ||
+	(transform->y->nX < 1) ||
+	(transform->y->nY < 1)) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: input transform is not invertible.");
+        return(NULL);
+    }
+
+    psPlaneTransform *out = psPlaneTransformAlloc(1, 1);
+
+    psF64 r12 = 0.0;
+    if (transform->x->nY == 1) {
+        r12 = transform->x->coeff[0][1];
+    }
+    psF64 r21 = 0.0;
+    if (transform->y->nX == 1) {
+        r21 = transform->y->coeff[1][0];
+    }
+    psF64 r11 = transform->x->coeff[1][0];
+    psF64 r22 = transform->y->coeff[0][1];
+    psF64 xo  = transform->x->coeff[0][0];
+    psF64 yo  = transform->y->coeff[0][0];
+
+    psF64 invDet = 1.0 / (r11 * r22 - r12 * r21);
+
+    // apply the results back to the polynomials
+    out->x->coeff[0][0] = -invDet * (r22 * xo - r12 * yo);
+    out->y->coeff[0][0] = -invDet * (r11 * yo - r21 * xo);
+    out->x->coeff[1][0] = +invDet * r22;
+    out->y->coeff[0][1] = +invDet * r11;
+    if (transform->x->nY == 1) {
+        out->x->coeff[0][1] = -invDet * r12;
+    }
+    if (transform->y->nX == 1) {
+        out->y->coeff[1][0] = -invDet * r21;
+    }
+    return(out);
+}
+
+/*****************************************************************************
+p_psIsProjectionLinear(): this is a private function which simply determines
+if the supplied psPlaneTransform transform is linear: if any of the
+cooefficients of order 2 are higher are non-zero, then it is not linear.
+
+Returns:
+    true: if linear
+    false: otherwise
+
+Why isn't this called p_psIsPlaneTransformLinear()?
+*****************************************************************************/
+bool p_psIsProjectionLinear(psPlaneTransform *transform)
+{
+    PS_ASSERT_PTR_NON_NULL(transform, false);
+    PS_ASSERT_PTR_NON_NULL(transform->x, false);
+    PS_ASSERT_PTR_NON_NULL(transform->y, false);
+
+    for (psS32 i=0;i<(1 + transform->x->nX);i++) {
+        for (psS32 j=0;j<(1 + transform->x->nY);j++) {
+            if (transform->x->coeff[i][j] != 0.0) {
+                if (!(((i == 0) && (j == 0)) ||
+		      ((i == 0) && (j == 1)) ||
+		      ((i == 1) && (j == 0)))) {
+                    return(false);
+                }
+            }
+        }
+    }
+
+    for (psS32 i=0;i<(1 + transform->y->nX);i++) {
+        for (psS32 j=0;j<(1 + transform->y->nY);j++) {
+            if (transform->y->coeff[i][j] != 0.0) {
+                if (!(((i == 0) && (j == 0)) ||
+		      ((i == 0) && (j == 1)) ||
+		      ((i == 1) && (j == 0)))) {
+                    return(false);
+                }
+            }
+        }
+    }
+
+    return(true);
+}
+
+psPlane* psPlaneAlloc(void)
+{
+    psPlane *p = psAlloc(sizeof(psPlane));
+    psMemSetDeallocator(p, (psFreeFunc) planeFree);
+
+    p->x = NAN;
+    p->y = NAN;
+    p->xErr = NAN;
+    p->yErr = NAN;
+
+    return(p);
+}
+
+
+psSphere* psSphereAlloc(void)
+{
+    psSphere *s = psAlloc(sizeof(psSphere));
+    psMemSetDeallocator(s, (psFreeFunc) sphereFree);
+
+    return(s);
+}
+
+psCube* psCubeAlloc(void)
+{
+    psCube *c = psAlloc(sizeof(psCube));
+
+    psMemSetDeallocator(c, (psFreeFunc) cubeFree);
+    return(c);
+}
+
+psPlaneTransform* psPlaneTransformAlloc(int order1, int order2)
+{
+    PS_ASSERT_INT_NONNEGATIVE(order1, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(order2, NULL);
+
+    psPlaneTransform *pt = psAlloc(sizeof(psPlaneTransform));
+    pt->x = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, order1, order2);
+    pt->y = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, order1, order2);
+
+    psMemSetDeallocator(pt, (psFreeFunc) planeTransformFree);
+    return(pt);
+}
+
+
+bool psMemCheckPlane(psPtr ptr)
+{
+    PS_ASSERT_PTR_NON_NULL(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)planeFree );
+}
+
+bool psMemCheckSphere(psPtr ptr)
+{
+    PS_ASSERT_PTR_NON_NULL(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)sphereFree );
+}
+
+bool psMemCheckCube(psPtr ptr)
+{
+    PS_ASSERT_PTR_NON_NULL(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)cubeFree );
+}
+
+bool psMemCheckPlaneTransform(psPtr ptr)
+{
+    PS_ASSERT_PTR_NON_NULL(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)planeTransformFree );
+}
+
+psPlane* psPlaneTransformApply(
+    psPlane* out,
+    const psPlaneTransform* transform,
+    const psPlane* coords)
+{
+    PS_ASSERT_PTR_NON_NULL(transform, NULL);
+    PS_ASSERT_PTR_NON_NULL(transform->x, NULL);
+    PS_ASSERT_PTR_NON_NULL(transform->y, NULL);
+    PS_ASSERT_PTR_NON_NULL(coords, NULL);
+
+    if (out == NULL) {
+        out = psPlaneAlloc();
+    }
+
+    out->x = psPolynomial2DEval(transform->x, coords->x, coords->y);
+    out->y = psPolynomial2DEval(transform->y, coords->x, coords->y);
+
+    return (out);
+}
+
+static void planeDistortFree(psPlaneDistort *pt)
+{
+    psFree(pt->x);
+    psFree(pt->y);
+}
+
+psPlaneDistort* psPlaneDistortAlloc(int order1, int order2, int order3, int order4)
+{
+    PS_ASSERT_INT_NONNEGATIVE(order1, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(order2, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(order3, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(order4, NULL);
+
+    psPlaneDistort *pt = psAlloc(sizeof(psPlaneDistort));
+    pt->x = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, order1, order2, order3, order4);
+    pt->y = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, order1, order2, order3, order4);
+
+    psMemSetDeallocator(pt, (psFreeFunc) planeDistortFree);
+    return(pt);
+}
+
+bool psMemCheckPlaneDistort(psPtr ptr)
+{
+    PS_ASSERT_PTR_NON_NULL(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)planeDistortFree );
+}
+
+
+
+/******************************************************************************
+This transformation takes into account parameters beyond an objects spatial
+coordinates: term3 and term4 (magnitude and color).
+*****************************************************************************/
+psPlane* psPlaneDistortApply(
+    psPlane* out,
+    const psPlaneDistort* distort,
+    const psPlane* coords,
+    float mag,
+    float color)
+{
+    PS_ASSERT_PTR_NON_NULL(distort, NULL);
+    PS_ASSERT_PTR_NON_NULL(distort->x, NULL);
+    PS_ASSERT_PTR_NON_NULL(distort->y, NULL);
+    PS_ASSERT_PTR_NON_NULL(coords, NULL);
+
+    if (out == NULL) {
+        out = psPlaneAlloc();
+    }
+    out->x = psPolynomial4DEval(distort->x, coords->x, coords->y, mag, color);
+    out->y = psPolynomial4DEval(distort->y, coords->x, coords->y, mag, color);
+    return (out);
+}
+
+
+void projectionFree(psProjection *p)
+{
+    // There are no dynamically allocated items
+}
+
+psProjection* psProjectionAlloc(
+    double R,
+    double D,
+    double Xs,
+    double Ys,
+    psProjectionType type)
+{
+    psProjection *p = psAlloc(sizeof(psProjection));
+    p->D = D;
+    p->R = R;
+    p->Xs = Xs;
+    p->Ys = Ys;
+    p->type = type;
+
+    psMemSetDeallocator(p, (psFreeFunc) projectionFree);
+    return(p);
+}
+
+bool psMemCheckProjection(psPtr ptr)
+{
+    PS_ASSERT_PTR_NON_NULL(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)projectionFree );
+}
+
+typedef enum {
+    PS_PROJECTION_CLASS_NONE,
+    PS_PROJECTION_CLASS_CARTESIAN,
+    PS_PROJECTION_CLASS_ZENITHAL,
+    PS_PROJECTION_CLASS_PSEUDOCYL,
+}psProjectionClass;
+
+psPlane* psProject(
+    psPlane *outPlane,
+    const psSphere* coord,
+    const psProjection* projection)
+{
+    PS_ASSERT_PTR_NON_NULL(coord, NULL);
+    PS_ASSERT_PTR_NON_NULL(projection, NULL);
+
+    psF64 phi = 0;
+    psF64 theta = 0;
+    psF64 sinTheta = 0;
+    psF64 cosPhiCT = 0;
+    psF64 sinPhiCT = 0;
+    psF64 sinDp, cosDp, sinAlpha, cosAlpha, sinDelta, cosDelta, zeta;
+
+    psProjectionClass class = PS_PROJECTION_CLASS_NONE;
+    switch (projection->type) {
+      case PS_PROJ_LIN:
+      case PS_PROJ_PLY:
+      case PS_PROJ_WRP:
+        class = PS_PROJECTION_CLASS_CARTESIAN;
+        break;
+      case PS_PROJ_TAN:
+      case PS_PROJ_TNX:
+      case PS_PROJ_DIS:
+      case PS_PROJ_SIN:
+      case PS_PROJ_STG:
+      case PS_PROJ_ZEA:
+      case PS_PROJ_ZPL:
+        class = PS_PROJECTION_CLASS_ZENITHAL;
+        break;
+      case PS_PROJ_AIT:
+      case PS_PROJ_PAR:
+      case PS_PROJ_GLS:
+      case PS_PROJ_MER:
+      case PS_PROJ_CAR:
+        class = PS_PROJECTION_CLASS_PSEUDOCYL;
+        break;
+
+      default:
+        psAbort("invalid projection");
+        break;
+    }
+
+    // Allocate return value
+    psPlane* out = NULL;
+    if (outPlane == NULL) {
+        out = psPlaneAlloc();
+    } else {
+        out = outPlane;
+    }
+
+    switch (class) {
+      case PS_PROJECTION_CLASS_CARTESIAN:
+        out->x = (coord->r - projection->R);
+        out->y = (coord->d - projection->D);
+        break;
+
+      case PS_PROJECTION_CLASS_ZENITHAL:
+        sinDp = sin(projection->D);
+        cosDp = cos(projection->D);
+        sinAlpha = sin(coord->r-projection->R);
+        cosAlpha = cos(coord->r-projection->R);
+        sinDelta = sin(coord->d);
+        cosDelta = cos(coord->d);
+
+# if ELIXIR_CODE
+
+        sinTheta =  cosDelta*cosAlpha*cosDp + sinDelta*sinDp;
+	sinPhiCT =  cosDelta*sinAlpha;
+	cosPhiCT =  cosDelta*cosAlpha*sinDp - sinDelta*cosDp;
+# else
+
+        sinTheta =  cosDelta*cosAlpha*cosDp + sinDelta*sinDp;
+	sinPhiCT = -cosDelta*sinAlpha;
+	cosPhiCT = -cosDelta*cosAlpha*sinDp + sinDelta*cosDp;
+# endif
+	// Perform the specified projection
+	switch (projection->type) {
+	  case PS_PROJ_TAN:
+	  case PS_PROJ_TNX: // XXX this is not quite right
+	  case PS_PROJ_DIS:
+	    // Gnomonic projection
+	    out->x = +sinPhiCT / sinTheta;
+	    out->y = -cosPhiCT / sinTheta;
+	    break;
+	  case PS_PROJ_SIN:
+	    // Othrographic projection
+	    out->x = +sinPhiCT;
+	    out->y = -cosPhiCT;
+	    break;
+	  case PS_PROJ_STG:
+	    // Othrographic projection
+	    out->x = +2*sinPhiCT / (1 + sinTheta);
+	    out->y = -2*cosPhiCT / (1 + sinTheta);
+	    break;
+	  case PS_PROJ_ZEA:
+	  case PS_PROJ_ZPL:
+	    // Zenithal Equal-Area
+	    zeta = M_SQRT2 / sqrt (1 + sinTheta);
+	    out->x = +zeta * sinPhiCT;
+	    out->y = -zeta * cosPhiCT;
+	    break;
+	  default:
+	    psAbort("invalid projection");
+	    break;
+	}
+	break;
+
+      case PS_PROJECTION_CLASS_PSEUDOCYL:
+        phi = coord->r - projection->R;
+        theta = coord->d - projection->D;
+        switch (projection->type)
+        {
+	  case PS_PROJ_AIT:
+            // Hammer-Aitoff projection
+            zeta = 1.0/sqrt(0.5*(1.0+cos(theta)*cos(phi/2.0)));
+            out->x = 2.0*zeta*cos(theta)*sin(phi/2.0);
+            out->y = zeta*sin(theta);
+            break;
+	  case PS_PROJ_GLS:
+            // projection name?
+            out->x = phi * cos(theta);
+            out->x = theta;
+            break;
+	  case PS_PROJ_PAR:
+            // Parabolic projection
+            out->x = phi*(2.0*cos(2.0*theta/3.0) - 1.0);
+            out->y = M_PI*sin(theta/3.0);
+            break;
+	  case PS_PROJ_CAR:
+	  case PS_PROJ_MER:
+            psAbort("projection not yet implemented");
+            break;
+	  default:
+            psAbort("invalid projection");
+            break;
+        }
+        break;
+
+      default:
+        psAbort("invalid projection");
+        break;
+    }
+
+    // Apply plate scales
+    out->x /= projection->Xs;
+    out->y /= projection->Ys;
+
+    // Return output
+    return out;
+}
+
+psSphere* psDeproject(
+    psSphere *outSphere,
+    const psPlane* coord,
+    const psProjection* projection)
+{
+    PS_ASSERT_PTR_NON_NULL(coord, NULL);
+    PS_ASSERT_PTR_NON_NULL(projection, NULL);
+
+    psF64 rho      = 0.0;
+    psF64 rho2      = 0.0;
+    psF64 sinTheta = 0.0;
+    psF64 cosTheta = 0.0;
+    psF64 sinPhi   = 0.0;
+    psF64 cosPhi   = 0.0;
+
+    psF64  theta = 0.0;
+    psF64  phi   = 0.0;
+    psF64  x = 0, y = 0, R = 0;
+
+    psProjectionClass class = PS_PROJECTION_CLASS_NONE;
+    switch (projection->type) {
+      case PS_PROJ_LIN:
+      case PS_PROJ_PLY:
+      case PS_PROJ_WRP:
+        class = PS_PROJECTION_CLASS_CARTESIAN;
+        break;
+      case PS_PROJ_TAN:
+      case PS_PROJ_TNX:
+      case PS_PROJ_DIS:
+      case PS_PROJ_SIN:
+      case PS_PROJ_STG:
+      case PS_PROJ_ZEA:
+      case PS_PROJ_ZPL:
+        class = PS_PROJECTION_CLASS_ZENITHAL;
+        break;
+      case PS_PROJ_AIT:
+      case PS_PROJ_PAR:
+      case PS_PROJ_GLS:
+      case PS_PROJ_CAR:
+      case PS_PROJ_MER:
+        class = PS_PROJECTION_CLASS_PSEUDOCYL;
+        break;
+
+      default:
+        psAbort("invalid projection");
+        break;
+    }
+
+    // Allocate return sphere structure
+    psSphere *out = NULL;
+    if (outSphere == NULL) {
+        out = psSphereAlloc();
+    } else {
+        out = outSphere;
+    }
+
+    // Perform inverse projection
+    switch (class) {
+      case PS_PROJECTION_CLASS_CARTESIAN:
+        out->r = coord->x*projection->Xs + projection->R;
+        out->d = coord->y*projection->Ys + projection->D;
+        break;
+
+      case PS_PROJECTION_CLASS_ZENITHAL:
+        // Remove plate scales
+        x = coord->x*projection->Xs;
+        y = coord->y*projection->Ys;
+        R = sqrt(x*x + y*y);
+        sinPhi   = (R == 0) ? 0.0 : +x / R;
+        cosPhi   = (R == 0) ? 1.0 : -y / R;
+
+        switch (projection->type) {
+	  case PS_PROJ_TAN:
+	  case PS_PROJ_TNX:// XXX this is not quite right
+	  case PS_PROJ_DIS:
+            // Gnonomic deprojection
+            rho      = sqrt (1 + R*R);
+            sinTheta = 1 / rho;
+            cosTheta = R / rho;
+            break;
+	  case PS_PROJ_SIN:
+            // Orhtographic deprojection
+            sinTheta = sqrt (1 - R*R);
+            cosTheta = R;
+            break;
+	  case PS_PROJ_STG:
+            psAbort("STG not defined");
+            break;
+	  case PS_PROJ_ZEA:
+	  case PS_PROJ_ZPL:
+            if (R > 2)
+                return NULL;
+            sinTheta = 1 - 0.5*PS_SQR(R);
+            cosTheta = sqrt (1 - PS_SQR(sinTheta));
+            break;
+	  default:
+            psAbort("invalid projection");
+            break;
+        }
+
+        psF64 sinDp = sin(projection->D);
+        psF64 cosDp = cos(projection->D);
+
+        // Convert from projection spherical coordinates
+        // psLib versions:
+# if ELIXIR_CODE
+        // XXX the elixir version : does the ADD have a sign error?
+        psF64 delta    = asin(sinTheta*sinDp - cosTheta*cosPhi*cosDp);
+        psF64 sinAlpha = +cosTheta*sinPhi;
+        psF64 cosAlpha = +cosTheta*cosPhi*sinDp + sinTheta*cosDp;
+# else
+
+	psF64 delta    = asin(sinTheta*sinDp + cosTheta*cosPhi*cosDp);
+        psF64 sinAlpha = -cosTheta*sinPhi;
+        psF64 cosAlpha = -cosTheta*cosPhi*sinDp + sinTheta*cosDp;
+# endif
+
+        out->d = delta;
+        out->r = atan2(sinAlpha, cosAlpha) + projection->R;
+        break;
+
+      case PS_PROJECTION_CLASS_PSEUDOCYL:
+        switch (projection->type)
+        {
+	  case PS_PROJ_AIT:
+            // Hammer-Aitoff deprojection
+            // XXX EAM : need range check on z^2 : must be > 0
+            // XXX EAM : old code, ADD, and elixir code are discrepant re x/4, y/2
+            rho2 = 1.0 - PS_SQR(0.25*x) - PS_SQR(0.5*y);
+            if (rho2 < 0)
+                return (NULL);
+            rho = sqrt(rho2);
+            phi = 2.0*atan2(0.5*x*rho, 2.0*rho2 - 1.0);
+            theta = asin(y*rho);
+            break;
+	  case PS_PROJ_PAR:
+            // Parabolic deprojection
+            rho = y/M_PI;
+            phi = x/(1.0 - 4.0*rho*rho);
+            theta = 3.0*asin(rho);
+            break;
+	  case PS_PROJ_GLS:
+            phi = x/cos(y);
+            theta = y;
+            break;
+	  default:
+            psAbort("invalid projection");
+            break;
+        }
+        out->r = phi   + projection->R;
+        out->d = theta + projection->D;
+        break;
+      default:
+        psAbort("invalid projection");
+        break;
+    }
+
+    // Return sphere coordinate
+    return out;
+}
+
+/*****************************************************************************
+multiplyDPoly2D(trans1, trans2): Takes two 2-D polynomials as input and
+multiplies them.  Basically, for each non-zero coeff in the trans1 coeff[][]
+array, you must multiply by all non-zero coeffs in trans2.
+*****************************************************************************/
+static psPolynomial2D *multiplyDPoly2D(
+    psPolynomial2D *trans1,
+    psPolynomial2D *trans2)
+{
+    psTrace("psLib.astro", 4, "---- %s() begin ----\n", __func__);
+    psTrace("psLib.astro", 5, "multiplyDPoly2D(%d %d: %d %d)\n", trans1->nX, trans1->nY, trans2->nX, trans2->nY);
+    psS32 orderX = trans1->nX + trans2->nX;
+    psS32 orderY = trans1->nY + trans2->nY;
+    psTrace("psLib.astro", 5, "out poly (nX, nY) is (%d, %d)\n", orderX, orderY);
+
+    psPolynomial2D *out = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, orderX, orderY);
+    psTrace("psLib.astro", 5, "Creating poly (%d, %d)\n", orderX, orderY);
+
+    for (psS32 t1x = 0 ; t1x < (1 + trans1->nX) ; t1x++) {
+        for (psS32 t1y = 0 ; t1y < (1 + trans1->nY) ; t1y++) {
+            if (0.0 != trans1->coeff[t1x][t1y]) {
+                for (psS32 t2x = 0 ; t2x < (1 + trans2->nX) ; t2x++) {
+                    for (psS32 t2y = 0 ; t2y < (1 + trans2->nY) ; t2y++) {
+                        out->coeff[t1x+t2x][t1y+t2y]+= (trans1->coeff[t1x][t1y] * trans2->coeff[t2x][t2y]);
+                    }
+                }
+            }
+        }
+    }
+    psTrace("psLib.astro", 4, "---- %s() end ----\n", __func__);
+    return(out);
+}
+
+/*****************************************************************************
+psPlaneTransformCombine(out, trans1, trans2)
+*****************************************************************************/
+psPlaneTransform *psPlaneTransformCombine(
+    psPlaneTransform *out,
+    const psPlaneTransform *trans1,
+    const psPlaneTransform *trans2,
+    psRegion region,
+    int nSamples)
+{
+    psTrace("psLib.astro", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psLib.astro", 3, "tracelevel(%s) is %d\n", __func__, psTraceGetLevel("psLib.astro"));
+
+    PS_ASSERT_PTR_NON_NULL(trans1, NULL);
+    PS_ASSERT_PTR_NON_NULL(trans2, NULL);
+    psTrace("psLib.astro", 5, "trans1->x is (%d, %d) order.\n", trans1->x->nX, trans1->x->nY);
+    psTrace("psLib.astro", 5, "trans1->y is (%d, %d) order.\n", trans1->y->nX, trans1->y->nY);
+    psTrace("psLib.astro", 5, "trans2->x is (%d, %d) order.\n", trans2->x->nX, trans2->x->nY);
+    psTrace("psLib.astro", 5, "trans2->y is (%d, %d) order.\n", trans2->y->nX, trans2->y->nY);
+    if (psTraceGetLevel("psLib.astro") >= 6) {
+        PS_POLY_PRINT_2D(trans1->x);
+        PS_POLY_PRINT_2D(trans1->y);
+        PS_POLY_PRINT_2D(trans2->x);
+        PS_POLY_PRINT_2D(trans2->y);
+    }
+
+    //
+    // Determine the size of the new psPlaneTransform.
+    //
+    psS32 orderXnX = (trans2->x->nX * trans1->x->nX) + (trans2->x->nY * trans1->y->nX);
+    psS32 orderXnY = (trans2->x->nX * trans1->x->nY) + (trans2->x->nY * trans1->y->nY);
+    psS32 orderYnX = (trans2->y->nX * trans1->x->nX) + (trans2->y->nY * trans1->y->nX);
+    psS32 orderYnY = (trans2->y->nX * trans1->x->nY) + (trans2->y->nY * trans1->y->nY);
+    psS32 orderX = PS_MAX(orderXnX, orderYnX);
+    psS32 orderY = PS_MAX(orderXnY, orderYnY);
+    psTrace("psLib.astro", 5, "The new (orderX, orderY) is (%d, %d)\n", orderX, orderY);
+
+    //
+    // Allocate the new psPlaneTransform, if necessary.
+    //
+
+    psPlaneTransform *myPT = NULL;
+    if (out == NULL) {
+        myPT = psPlaneTransformAlloc(orderX, orderY);
+    } else {
+        if ((out->x->nX == orderX) &&
+	    (out->x->nY == orderY) &&
+	    (out->y->nX == orderX) &&
+	    (out->y->nY == orderY)) {
+            myPT = out;
+            //
+            // Initialize the new psPlaneTransform, if necessary.
+            //
+            for (psS32 i = 0 ; i < orderX+1 ; i++) {
+                for (psS32 j = 0 ; j < orderY+1 ; j++) {
+                    myPT->x->coeff[i][j] = 0.0;
+                    myPT->y->coeff[i][j] = 0.0;
+                    myPT->x->coeffMask[i][j] = PS_POLY_MASK_NONE;
+                    myPT->y->coeffMask[i][j] = PS_POLY_MASK_NONE;
+                }
+            }
+        } else {
+            psFree(out);
+            myPT = psPlaneTransformAlloc(orderX, orderY);
+        }
+    }
+    psTrace("psLib.astro", 5, "New polynomial ranks are (%d %d %d %d)\n", myPT->x->nX, myPT->x->nY, myPT->y->nX, myPT->y->nY);
+
+    //
+    // For each term (a * x^i * y^j) in trans2, we substitute the appropriate
+    // equation from trans1, and raise it to the appropriate power.  This is
+    // done via the multiplyDPoly2D().  The result is a polynomial (currPoly)
+    // and its coefficients are added into the myPT coeff matrix.
+    //
+    // trans1XPolys[i]: contains a polynomial corresponding to trans1->x raised to the i-th power.
+    //
+
+    psS32 order = PS_MAX(PS_MAX(PS_MAX(trans2->x->nX, trans2->x->nY), trans2->y->nX), trans2->y->nY);
+    psPolynomial2D **trans1XPolys = (psPolynomial2D **) psAlloc((order + 1) * sizeof(psPolynomial2D *));
+    psPolynomial2D **trans1YPolys = (psPolynomial2D **) psAlloc((order + 1) * sizeof(psPolynomial2D *));
+
+    //
+    // Raise the trans1 polynomials to whatever power is need in the trans2 polynomials.
+    //
+    trans1XPolys[0] = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 0, 0);
+    trans1XPolys[0]->coeff[0][0] = 1.0;
+    trans1YPolys[0] = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 0, 0);
+    trans1YPolys[0]->coeff[0][0] = 1.0;
+
+    for (psS32 c = 1 ; c < (order + 1) ; c++) {
+        trans1XPolys[c] = multiplyDPoly2D(trans1XPolys[c-1], trans1->x);
+        trans1YPolys[c] = multiplyDPoly2D(trans1YPolys[c-1], trans1->y);
+    }
+
+    psTrace("psLib.astro", 5, "Determine the new x-polynomial\n");
+    for (psS32 t2x = 0 ; t2x < (trans2->x->nX + 1) ; t2x++) {
+        for (psS32 t2y = 0 ; t2y < (trans2->x->nY + 1) ; t2y++) {
+            psTrace("psLib.astro", 6, "X: -------------------- (t2x, t2y) (%d, %d) --------------------\n", t2x, t2y);
+            if (trans2->x->coeffMask[t2x][t2y] & PS_POLY_MASK_SET) {
+		continue;
+	    }
+
+	    psTrace("psLib.astro", 6, "In this iteration, we raise trans1->x to the %d power and trans1->y to the %d-power.\n", t2x, t2y);
+	    psPolynomial2D *newPoly = multiplyDPoly2D(trans1XPolys[t2x], trans1YPolys[t2y]);
+
+	    if (psTraceGetLevel("psLib.astro") >= 6) {
+		PS_POLY_PRINT_2D(newPoly);
+	    }
+
+	    // Set the appropriate coeffs in myPT->x
+	    for (psS32 i = 0 ; i < (1 + newPoly->nX) ; i++) {
+		for (psS32 j = 0 ; j < (1 + newPoly->nY) ; j++) {
+		    myPT->x->coeff[i][j]+= newPoly->coeff[i][j] * trans2->x->coeff[t2x][t2y];
+		}
+	    }
+
+	    if (psTraceGetLevel("psLib.astro") >= 6) {
+		PS_POLY_PRINT_2D(myPT->x);
+	    }
+	    psFree(newPoly);
+        }
+    }
+    if (psTraceGetLevel("psLib.astro") >= 6) {
+        psTrace("psLib.astro", 6, "The final x-polynomial\n");
+        PS_POLY_PRINT_2D(myPT->x);
+    }
+
+    //
+    // Determine the new y-polynomial
+    //
+    psTrace("psLib.astro", 5, "Determine the new y-polynomial\n");
+    for (psS32 t2x = 0 ; t2x < (trans2->y->nX + 1) ; t2x++) {
+        for (psS32 t2y = 0 ; t2y < (trans2->y->nY + 1) ; t2y++) {
+            psTrace("psLib.astro", 5, "Y: -------------------- (t2x, t2y) (%d, %d) --------------------\n", t2x, t2y);
+            if (trans2->y->coeffMask[t2x][t2y] & PS_POLY_MASK_SET) {
+		continue;
+	    }
+	    psTrace("psLib.astro", 5, "In this iteration, we raise trans1->x to the %d power and trans1->y to the %d-power.\n", t2x, t2y);
+	    psPolynomial2D *newPoly = multiplyDPoly2D(trans1XPolys[t2x], trans1YPolys[t2y]);
+
+	    if (psTraceGetLevel("psLib.astro") >= 6) {
+		PS_POLY_PRINT_2D(newPoly);
+	    }
+
+	    // Set the appropriate coeffs in myPT->x
+	    for (psS32 i = 0 ; i < (1 + newPoly->nX) ; i++) {
+		for (psS32 j = 0 ; j < (1 + newPoly->nY) ; j++) {
+		    myPT->y->coeff[i][j]+= newPoly->coeff[i][j] * trans2->y->coeff[t2x][t2y];
+		}
+	    }
+	    if (psTraceGetLevel("psLib.astro") >= 6) {
+		PS_POLY_PRINT_2D(myPT->x);
+	    }
+	    psFree(newPoly);
+        }
+    }
+    if (psTraceGetLevel("psLib.astro") >= 6) {
+        psTrace("psLib.astro", 6, "The final y-polynomial\n");
+        PS_POLY_PRINT_2D(myPT->y);
+    }
+
+    for (psS32 c = 0 ; c < (order + 1) ; c++) {
+        psFree(trans1XPolys[c]);
+        psFree(trans1YPolys[c]);
+    }
+    psFree(trans1XPolys);
+    psFree(trans1YPolys);
+
+    psTrace("psLib.astro", 3, "---- %s() end ----\n", __func__);
+    return(myPT);
+}
+
+/*****************************************************************************
+psPlaneTransformFit(trans, source, dest, nRejIter, sigmaClip)
+
+XXX: This code ignores nRejIter and sigmaClip.  We must call the ClipFit
+routines instead.
+*****************************************************************************/
+bool psPlaneTransformFit(
+    psPlaneTransform *trans,
+    const psArray *source,
+    const psArray *dest,
+    int nRejIter,
+    float sigmaClip)
+{
+    PS_ASSERT_PTR_NON_NULL(trans, NULL);
+    PS_ASSERT_PTR_NON_NULL(source, NULL);
+    PS_ASSERT_PTR_NON_NULL(dest, NULL);
+
+    //
+    // Create the x and y vectors for the psVectorFitPolynomial2D() function.
+    //
+    psS32 numCoords = PS_MIN(source->n, dest->n);
+    psVector *xIn = psVectorAlloc(numCoords, PS_TYPE_F64);
+    psVector *yIn = psVectorAlloc(numCoords, PS_TYPE_F64);
+    psVector *xOut = psVectorAlloc(numCoords, PS_TYPE_F64);
+    psVector *yOut = psVectorAlloc(numCoords, PS_TYPE_F64);
+    for (int g = 0; g < numCoords; g++) {
+        xIn->data.F64[g] = ((psPlane *) source->data[g])->x;
+        yIn->data.F64[g] = ((psPlane *) source->data[g])->y;
+        xOut->data.F64[g] = ((psPlane *) dest->data[g])->x;
+        yOut->data.F64[g] = ((psPlane *) dest->data[g])->y;
+    }
+
+    bool result = true;
+    result &= psVectorFitPolynomial2D(trans->x, NULL, 0, xOut, NULL, xIn, yIn);
+    result &= psVectorFitPolynomial2D(trans->y, NULL, 0, yOut, NULL, xIn, yIn);
+    psFree(xIn);
+    psFree(yIn);
+    psFree(xOut);
+    psFree(yOut);
+
+    if (!result) {
+        psError( PS_ERR_UNKNOWN, true, "psVectorFitPolynomial2D() returned NULL: could not fit a 2-D polynomial to the data.\n");
+        return(false);
+    }
+
+    return(true);
+}
+
+
+/*****************************************************************************
+psPlaneTransformInvert(out, in, region, nSamples)
+
+*****************************************************************************/
+psPlaneTransform *psPlaneTransformInvert(
+    psPlaneTransform *out,
+    const psPlaneTransform *in,
+    psRegion region,
+    int nSamples)
+{
+    PS_ASSERT_PTR_NON_NULL(in, NULL);
+    PS_ASSERT_PTR_NON_NULL(in->x, NULL);
+    PS_ASSERT_PTR_NON_NULL(in->y, NULL);
+    PS_ASSERT_INT_LARGER_THAN(nSamples, 0, NULL);
+
+    // Reject a trivially non-invertible case.
+    if ((in->x->nX < 1) || (in->x->nY < 1) || (in->y->nX < 1) || (in->y->nY < 1)) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: input transform is not invertible.");
+        return(NULL);
+    }
+
+    // Ensure that the input transformation is symmetrical.
+    if ((in->x->nX != in->x->nY) || (in->y->nX != in->y->nY) || (in->x->nX != in->y->nX)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Input transformation must have same nX==nY.");
+    }
+
+    //
+    // If the transform is linear, then invert it exactly and return.
+    //
+    if (p_psIsProjectionLinear((psPlaneTransform *) in)) {
+        return(p_psPlaneTransformLinearInvert((psPlaneTransform *) in));
+    }
+    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(nSamples, 1, NULL);
+
+    //
+    // Allocate a new psPlaneTransform if "out" is NULL, or has the wrong size.
+    //
+    psS32 order = in->x->nX;
+    psPlaneTransform *myPT = NULL;
+    if (out == NULL) {
+        myPT = psPlaneTransformAlloc(order, order);
+    } else {
+        if ((out->x->nX == order) && (out->x->nY == order) &&
+	    (out->y->nX == order) && (out->y->nX == order)) {
+            myPT = out;
+        } else {
+            psFree(out);
+            myPT = psPlaneTransformAlloc(order, order);
+        }
+    }
+
+    //
+    // Initialize the grid.  Since we want the inverse of the transformation, the
+    // inCoords are written to the outData vector, and the outCoords are written
+    // to the inData vector.
+    //
+    psVector *xIn = psVectorAlloc(nSamples*nSamples, PS_TYPE_F64);
+    psVector *yIn = psVectorAlloc(nSamples*nSamples, PS_TYPE_F64);
+    psVector *xOut = psVectorAlloc(nSamples*nSamples, PS_TYPE_F64);
+    psVector *yOut = psVectorAlloc(nSamples*nSamples, PS_TYPE_F64);
+    psPlane *inCoord = psPlaneAlloc();
+    psPlane *outCoord = psPlaneAlloc();
+    psS32 cnt = 0;
+    for (int yint = 0; yint < nSamples; yint++) {
+        inCoord->y = region.y0 + ((psF32) yint) * ((region.y1 - region.y0) / ((psF32) nSamples));
+        for (int xint = 0; xint < nSamples; xint++) {
+            inCoord->x = region.x0 + ((psF32) xint) * ((region.x1 - region.x0) / ((psF32) nSamples));
+            (void)psPlaneTransformApply(outCoord, in, inCoord);
+            xOut->data.F64[cnt] = inCoord->x;
+            yOut->data.F64[cnt] = inCoord->y;
+            xIn->data.F64[cnt] = outCoord->x;
+            yIn->data.F64[cnt] = outCoord->y;
+            cnt++;
+        }
+    }
+
+    bool result = true;
+    result &= psVectorFitPolynomial2D(myPT->x, NULL, 0, xOut, NULL, xIn, yIn);
+    result &= psVectorFitPolynomial2D(myPT->y, NULL, 0, yOut, NULL, xIn, yIn);
+
+    psFree(inCoord);
+    psFree(outCoord);
+    psFree(xIn);
+    psFree(yIn);
+    psFree(xOut);
+    psFree(yOut);
+
+    if (!result) {
+        psError( PS_ERR_UNKNOWN, true, "psVectorFitPolynomial2D() returned NULL: could not fit a 2-D polynomial to the data.\n");
+        psFree(myPT);
+        return(NULL);
+    }
+
+    return(myPT);
+}
+
+
+psPlane *psPlaneTransformDeriv(
+    psPlane *out,
+    const psPlaneTransform *transformation,
+    const psPlane *coord
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(transformation, NULL);
+    PS_ASSERT_POLY_NON_NULL(transformation->x, NULL);
+    PS_ASSERT_POLY_NON_NULL(transformation->y, NULL);
+    PS_ASSERT_PTR_NON_NULL(coord, NULL);
+
+    if (out == NULL) {
+        out = psPlaneAlloc();
+    }
+
+    out->x = 0.0;
+    out->y = 0.0;
+    out->xErr = 0.0;
+    out->yErr = 0.0;
+
+    psPolynomial2D *xPoly = transformation->x;
+    psPolynomial2D *yPoly = transformation->y;
+
+    //
+    // Calculate the derivative with respect to x.
+    //
+    psF32 xSum = 1.0;
+    psF32 ySum = 1.0;
+
+    // This loop starts at loop_x=1 since the derivative of the loop_x=0 terms are all 0.0
+    for (psS32 loop_x = 1; loop_x < (1 + xPoly->nX); loop_x++) {
+        ySum = 1.0;
+        for (psS32 loop_y = 0; loop_y < (1 + xPoly->nY); loop_y++) {
+            //
+            // For each iteration of the loop, we multiply the (x, y) coefficient
+            // by (x^(loop_x-1) * y^loop_y) * loop_x
+            //
+
+            out->x+= xPoly->coeff[loop_x][loop_y] * xSum * ySum * ((psF32) loop_x);
+            psTrace("psLib.astro", 6, "out->x+= (%.2f * %.2f * %.2f * %.2f)\n", xPoly->coeff[loop_x][loop_y], xSum, ySum, ((psF32) loop_x));
+            ySum*= coord->y;
+        }
+        xSum*= coord->x;
+    }
+
+    //
+    // Calculate the derivative with respect to x.
+    //
+    xSum = 1.0;
+
+    // This loop starts at loop_y=1 since the derivative of the loop_y=0 terms are all 0.0
+    for (psS32 loop_x = 0; loop_x < (1 + yPoly->nX); loop_x++) {
+        ySum = 1.0;
+        for (psS32 loop_y = 1; loop_y < (1 + yPoly->nY); loop_y++) {
+            //
+            // For each iteration of the loop, we multiply the (x, y) coefficient
+            // by (x^(loop_x-1) * y^loop_y) * loop_y
+            // by (x^(loop_x) * y^(loop_y-1))
+            //
+
+            out->y+= yPoly->coeff[loop_x][loop_y] * xSum * ySum * ((psF32) loop_y);
+            psTrace("psLib.astro", 6, "out->y+= (%.2f * %.2f * %.2f * %.2f)\n", yPoly->coeff[loop_x][loop_y], xSum, ySum, ((psF32) loop_y));
+            ySum*= coord->y;
+        }
+        xSum*= coord->x;
+    }
+
+    return(out);
+}
+
+psPixels *psPixelsTransform(
+    psPixels *out,
+    const psPixels *input,
+    const psPlaneTransform *inToOut)
+{
+    PS_ASSERT_PTR_NON_NULL(input, NULL);
+    PS_ASSERT_PTR_NON_NULL(inToOut, NULL);
+    if (out == NULL) {
+        //XXX: Should the length (nalloc) be 1 and append be used everytime a pixel is added?
+        //        out = psPixelsAlloc(input->nalloc);
+        out = psPixelsAlloc(0);
+    }
+    psPlane *coord = psPlaneAlloc();
+    psPlane *deriv = psPlaneAlloc();
+    psPlane *fxnVal = psPlaneAlloc();
+
+    int i = 0;
+    //    int m = 0;
+    //    while (input->data.x[i] != 0.0 && input->data.y[i] != 0.0) {
+    for ( i = 0; i < input->n; i++) {
+        coord->x = input->data[i].x;
+        coord->y = input->data[i].y;
+        deriv = psPlaneTransformDeriv(deriv, inToOut, coord);
+        fxnVal = psPlaneTransformApply(fxnVal, inToOut, coord);
+        if (fabs(fxnVal->x - coord->x) <= fabs(deriv->x) &&
+	    fabs(fxnVal->y - coord->y) <= fabs(deriv->y)) {
+            int x = (int)(ceil(fabs(deriv->x)));
+            int y = (int)(ceil(fabs(deriv->y)));
+            for (int j = -x; j <= x; j++) {
+                for (int k = -y; k <= y; k++) {
+                    //                    out->data[m].x = fxnVal->x + x;
+                    //                    out->data[m].y = fxnVal->y + y;
+                    //                    m++;
+                    out = psPixelsAdd(out, 1, (float)(fxnVal->x+j),
+                                      (float)(fxnVal->y+k) );
+                }
+            }
+        }
+    }
+
+    psFree(coord);
+    psFree(deriv);
+    psFree(fxnVal);
+    return out;
+}
+
+psCube *psSphereToCube(const psSphere *sphere)
+{
+    if(sphere == NULL) {
+        psError( PS_ERR_UNKNOWN, true, "psSphere argument is NULL.  Returning NULL.\n");
+        return NULL;
+    }
+
+    psCube *cube = NULL;
+
+    cube = psCubeAlloc();
+    cube->x = cos(sphere->d) * cos(sphere->r);
+    cube->y = cos(sphere->d) * sin(sphere->r);
+    cube->z = sin(sphere->d);
+    cube->xErr = cos(sphere->dErr) * cos(sphere->rErr);
+    cube->yErr = cos(sphere->dErr) * sin(sphere->rErr);
+    cube->zErr = sin(sphere->dErr);
+
+    return(cube);
+}
+
+psSphere *psCubeToSphere(const psCube *cube)
+{
+    if(cube == NULL) {
+        psError( PS_ERR_UNKNOWN, true, "psCube argument is NULL.  Returning NULL.\n");
+        return NULL;
+    }
+
+    psSphere *sphere = NULL;
+    sphere = psSphereAlloc();
+    //    sphere->r = arctan(cube->x/cube->y);
+    //    sphere->d = arctan(sqrt(cube->x*cube->x + cube->y*cube->y)/cube->z);
+    //    sphere->rErr = arctan(cube->xErr/cube->yErr);
+    //    sphere->dErr = arctan(sqrt(cube->xErr*cube->xErr + cube->yErr*cube->yErr)/cube->zErr);
+    //    sphere->r = 1 / (atan(cube->x/cube->y));
+    //    sphere->d = 1 / (atan(sqrt(cube->x*cube->x + cube->y*cube->y)/cube->z));
+    //    sphere->rErr = 1 / (atan(cube->xErr/cube->yErr));
+    //    sphere->dErr = 1 / (atan(sqrt(cube->xErr*cube->xErr + cube->yErr*cube->yErr)/cube->zErr));
+    psCube *cube2 = psCubeAlloc();
+    *cube2 = *cube;
+    double mag = sqrt(cube->x*cube->x + cube->y*cube->y + cube->z*cube->z);
+    if (mag > 1.0) {
+        cube2->x = cube2->x/mag;
+        cube2->y = cube2->y/mag;
+        cube2->z = cube2->z/mag;
+    }
+
+    sphere->r = atan2(cube2->y, cube2->x);
+    sphere->d = asin(cube2->z);
+    //    sphere->d = atan2((cube->x*cube->x + cube->y*cube->y), cube->z);
+    sphere->rErr = atan2(cube2->yErr, cube2->xErr);
+    sphere->dErr = asin(cube2->zErr);
+    psFree(cube2);
+
+    return(sphere);
+}
+
+psString psProjectTypeToString(psProjectionType type, const char *prefix)
+{
+    PS_ASSERT_STRING_NON_EMPTY(prefix, NULL);
+
+    char *name = NULL;
+
+    switch (type) {
+      case PS_PROJ_LIN:
+        psStringAppend (&name, "%s-LIN", prefix);
+        return name;
+      case PS_PROJ_PLY:
+        psStringAppend (&name, "%s-PLY", prefix);
+        return name;
+      case PS_PROJ_WRP:
+        psStringAppend (&name, "%s-WRP", prefix);
+        return name;
+      case PS_PROJ_TAN:
+        psStringAppend (&name, "%s-TAN", prefix);
+        return name;
+      case PS_PROJ_TNX:
+        psStringAppend (&name, "%s-TNX", prefix);
+        return name;
+      case PS_PROJ_DIS:
+        psStringAppend (&name, "%s-DIS", prefix);
+        return name;
+      case PS_PROJ_SIN:
+        psStringAppend (&name, "%s-SIN", prefix);
+        return name;
+      case PS_PROJ_STG:
+        psStringAppend (&name, "%s-STG", prefix);
+        return name;
+      case PS_PROJ_AIT:
+        psStringAppend (&name, "%s-AIT", prefix);
+        return name;
+      case PS_PROJ_PAR:
+        psStringAppend (&name, "%s-PAR", prefix);
+        return name;
+      case PS_PROJ_GLS:
+        psStringAppend (&name, "%s-GLS", prefix);
+        return name;
+      case PS_PROJ_CAR:
+        psStringAppend (&name, "%s-CAR", prefix);
+        return name;
+      case PS_PROJ_MER:
+        psStringAppend (&name, "%s-MER", prefix);
+        return name;
+
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unknown projection type: %d\n", type);
+        return NULL;
+    }
+    return NULL;
+}
+
+psProjectionType psProjectTypeFromString(const char *name)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, PS_PROJ_NONE);
+
+    if (!strcmp (&name[4], "-LIN")) return PS_PROJ_LIN;
+    if (!strcmp (&name[4], "-PLY")) return PS_PROJ_PLY;
+    if (!strcmp (&name[4], "-WRP")) return PS_PROJ_WRP;
+    if (!strcmp (&name[4], "-TAN")) return PS_PROJ_TAN;
+    if (!strcmp (&name[4], "-TNX")) return PS_PROJ_TNX;
+    if (!strcmp (&name[4], "-DIS")) return PS_PROJ_DIS;
+    if (!strcmp (&name[4], "-SIN")) return PS_PROJ_SIN;
+    if (!strcmp (&name[4], "-STG")) return PS_PROJ_STG;
+    if (!strcmp (&name[4], "-AIT")) return PS_PROJ_AIT;
+    if (!strcmp (&name[4], "-PAR")) return PS_PROJ_PAR;
+    if (!strcmp (&name[4], "-GLS")) return PS_PROJ_GLS;
+    if (!strcmp (&name[4], "-CAR")) return PS_PROJ_CAR;
+    if (!strcmp (&name[4], "-MER")) return PS_PROJ_MER;
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised projection type: %s", name);
+    return PS_PROJ_NONE;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/psCoord.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/psCoord.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/psCoord.h	(revision 22322)
@@ -0,0 +1,460 @@
+/** @file  psCoord.h
+ *
+ *  @brief Basic coordinate transformations
+ *
+ *  This file defines the basic types for astronomical coordinate
+ *  transformation
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.61 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-08-09 01:40:07 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_COORD_H
+#define PS_COORD_H
+
+/// @addtogroup Astro Astronomy
+/// @{
+
+#include "psType.h"
+#include "psImage.h"
+#include "psArray.h"
+#include "psList.h"
+#include "psPolynomial.h"
+#include "psPixels.h"
+#include "psRegion.h"
+//#include "psTime.h"
+
+/** Euclidiean Coordinate System.
+ *
+ *  Both detector and sky positions will be used extensively in the IPP. One
+ *  coordinate system to be used is linear coordinates which conform to
+ *  Euclidean geometry.
+ *
+ */
+typedef struct
+{
+    double x;                          ///< x position
+    double y;                          ///< y position
+    double xErr;                       ///< Error in x position
+    double yErr;                       ///< Error in y position
+}
+psPlane;
+
+/** Angular Coordinate System
+ *
+ *  Both detector and sky positions will be used extensively in the IPP. One
+ *  coordinate system to be used is angular coordinates for which additional
+ *  care must often be taken in comparison to a euclidiean coordinate system.
+ *
+ */
+typedef struct
+{
+    double r;                          ///< RA
+    double d;                          ///< Dec
+    double rErr;                       ///< Error in RA
+    double dErr;                       ///< Error in Dec
+}
+psSphere;
+
+/** 3-Dimensional Euclidean Coordinate System
+ *
+ *  Both detector and sky positions will be used extensively in the IPP. One
+ *  coordinate system to be used is cubic coordinates for which additional
+ *  care must often be taken in comparison to an angular coordinate system.
+ *
+ */
+typedef struct
+{
+    double x;                          ///< cos (DEC) cos (RA)
+    double y;                          ///< cos (DEC) sin (RA)
+    double z;                          ///< sin (DEC)
+    double xErr;                       ///< Error in x
+    double yErr;                       ///< Error in y
+    double zErr;                       ///< Error in z
+}
+psCube;
+
+/** 2D Polynomial Transform
+ *
+ *  A transform between coordinate systems that consists simply of two 2D
+ *  polynomials to transform both components - the output coordinates depend
+ *  only on the input coordinates and no other quantities of objects at those
+ *  coordinates.
+ *
+ */
+typedef struct
+{
+    psPolynomial2D* x;                 ///< 2D polynomial transform of X coordinates
+    psPolynomial2D* y;                 ///< 2D polynomial transform of Y coordinates
+}
+psPlaneTransform;
+
+/** 4D Polynomial Transform
+ *
+ *  A transform between coordinate systems that consists of two 4D polynomials
+ *  in which the output coordinates are also specified to be a function of the
+ *  magnitude and color of the object with the given coordinates. This type of
+ *  coordinate transformation is necessary to represent the (color-dependent)
+ *  optical distortions caused by the atmosphere and camera optics, and the
+ *  possibly effects of charge transfer inefficiency.
+ *
+ *  The lowest two terms are the x and y axis of the target system.  The higher
+ *  two terms may represent magnitude and color terms.
+ */
+typedef struct
+{
+    psPolynomial4D* x;                 ///< 4D polynomial transform of X coordinates
+    psPolynomial4D* y;                 ///< 4D polynomial transform of Y coordinates
+}
+psPlaneDistort;
+
+/** Projection type for projection/deprojection
+ *
+ *  @see psProject, psDeproject
+ *
+ */
+typedef enum {
+    PS_PROJ_NONE,                       ///< No projection
+    PS_PROJ_LIN,                        ///< Linear projection
+    PS_PROJ_PLY,                        ///< Linear polynomial projection
+    PS_PROJ_WRP,                        ///< Linear polynomial projection
+    PS_PROJ_TAN,                        ///< Tangent projection
+    PS_PROJ_DIS,                        ///< Sine projection
+    PS_PROJ_SIN,                        ///< Sine projection
+    PS_PROJ_STG,                        ///< Sine projection
+    PS_PROJ_TNX,                        ///< Sine projection
+    PS_PROJ_ZEA,                        ///< Sine projection
+    PS_PROJ_ZPL,                        ///< Sine projection
+    PS_PROJ_AIT,                        ///< Aitoff projection
+    PS_PROJ_PAR,                        ///< Par projection
+    PS_PROJ_GLS,                        ///< GLS projection
+    PS_PROJ_CAR,                        ///< CAR projection
+    PS_PROJ_MER,                        ///< MER projection
+    PS_PROJ_NTYPE                       ///< Number of types; must be last.
+} psProjectionType;
+
+/** Parameter set for projection/deprojection
+ *
+ *  @see psProject, psDeproject
+ *
+ */
+typedef struct
+{
+    double R;                          ///< Coordinates of projection center
+    double D;                          ///< Coordinates of projection center
+    double Xs;                         ///< plate-scale in X direction
+    double Ys;                         ///< plate-scale in Y direction
+    psProjectionType type;             ///< Projection type
+}
+psProjection;
+
+/** Allocates a psPlane
+ *
+ *  @return psPlane*     resulting plane structure.
+ */
+psPlane* psPlaneAlloc(void) PS_ATTR_MALLOC;
+
+/** Allocates a psSphere
+ *
+ *  @return psSphere*     resulting sphere structure.
+ */
+psSphere* psSphereAlloc(void) PS_ATTR_MALLOC;
+
+/** Allocates a psCube
+ *
+ *  @return psCube*     resulting cubic structure.
+ */
+psCube* psCubeAlloc(void) PS_ATTR_MALLOC;
+
+/** Allocates a psPlaneTransform transform.
+ *
+ *  @return psPlaneTransform*     resulting plane transform
+ */
+
+psPlaneTransform* psPlaneTransformAlloc(
+    int order1,                        ///< The order of the x term in the transform.
+    int order2                         ///< The order of the y term in the transform.
+) PS_ATTR_MALLOC;
+
+/** Applies the psPlaneTransform transform to a specified coordinate
+ *
+ *  @return psPlane*     resulting coordinate based on transform
+ */
+psPlane* psPlaneTransformApply(
+    psPlane* out,                      ///< a psPlane to recycle.  If NULL, a new one is generated.
+    const psPlaneTransform* transform, ///< the transform to apply
+    const psPlane* coords              ///< the coordinate to apply the transform above.
+);
+
+/** Allocates a psPlaneDistort transform.
+ *
+ *  @return psPlaneDistort*     resulting plane distort transform
+ */
+
+psPlaneDistort* psPlaneDistortAlloc(
+    int order1,                        ///< The order of the w term in the transform.
+    int order2,                        ///< The order of the x term in the transform.
+    int order3,                        ///< The order of the y term in the transform.
+    int order4                         ///< The order of the z term in the transform.
+) PS_ATTR_MALLOC;
+
+
+/** Applies the psPlaneDistort transform to a specified coordinate
+ *
+ *  @return psPlane*     resulting coordinate based on transform
+ */
+psPlane* psPlaneDistortApply(
+    psPlane* out,                      ///< a psPlane to recycle.  If NULL, a new one is generated.
+    const psPlaneDistort* distort,     ///< the transform to apply
+    const psPlane* coords,             ///< the coordinate to apply the transform above.
+    float mag,                         ///< third term -- maybe magnitude
+    float color                        ///< forth term -- maybe color
+);
+
+/** Allocates memory for a psProjection structure
+ *
+ *  @return psProjection*    psProjection structure
+ */
+psProjection* psProjectionAlloc(
+    double R,                          ///< Right-ascension of projection center.
+    double D,                          ///< Declination of projection center.
+    double Xs,                         ///< Scale in x-dimension
+    double Ys,                         ///< Scale in y-dimension
+    psProjectionType type
+) PS_ATTR_MALLOC;
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psPlane structure, false otherwise.
+ */
+bool psMemCheckPlane(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psSphere structure, false otherwise.
+ */
+bool psMemCheckSphere(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psCube structure, false otherwise.
+ */
+bool psMemCheckCube(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psPlaneTransform structure, false otherwise.
+ */
+bool psMemCheckPlaneTransform(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psPlaneDistort structure, false otherwise.
+ */
+bool psMemCheckPlaneDistort(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psProjection structure, false otherwise.
+ */
+bool psMemCheckProjection(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Projects a spherical coordinate to a linear coordinate system
+ *
+ *  @return psPlane*    projected coordinate
+ */
+psPlane* psProject(
+    psPlane *out,
+    const psSphere* coord,             ///< coordinate to project
+    const psProjection* projection     ///< parameters of the projection
+);
+
+/** Reverse projection of a linear coordinate to a spherical coordinate system
+ *
+ *  @return psPlane*    projected coordinate
+ */
+psSphere* psDeproject(
+    psSphere *outSphere,
+    const psPlane* coord,              ///< coordinate to project
+    const psProjection* projection     ///< parameters of the projection
+);
+
+/** Takes a given transform and inverts it linearly if possible.
+ *
+ *  @return psPlaneTransform
+ *  the linearly inverted transform
+*/
+psPlaneTransform *p_psPlaneTransformLinearInvert(
+    psPlaneTransform *transform        ///< transform to invert
+);
+
+/** Takes a transform and tests whether or not it is a linear projection.
+ *
+ *  @return psS32
+ *  the order of the projection
+*/
+bool p_psIsProjectionLinear(
+    psPlaneTransform *transform        ///< transform to test for linearity
+);
+
+
+/** inverts a given transformation.
+ *
+ *  It may assume that the input transformation is one-to-one, and that the
+ *  inverse transformation may be specified through using polynomials of the
+ *  same type and order as the forward transformation. In the event that the
+ *  input transformation is linear, an exact solution may be calculated;
+ *  otherwise nSamples samples in each axis, covering the region specified by
+ *  region shall be used as a grid to fit the best inverse transformation. The
+ *  function shall return NULL if it was unable to generate the inverse
+ *  transformation; otherwise it shall return the inverse transformation. In
+ *  the event that out is NULL, a new psPlaneTransform shall be allocated and
+ *  returned.
+ *
+ *  @return psPlaneTransform*  the resulting inverted transform
+ */
+psPlaneTransform* psPlaneTransformInvert(
+    psPlaneTransform *out,             ///< a transform to recycle, or NULL if one is to be created.
+    const psPlaneTransform *in,        ///< transform to invert
+    psRegion region,                   ///< region to fit for non-linear transform inversion
+    int nSamples                       ///< number of samples in each axis for fit
+);
+
+/** Creates a single transformation that has the effect of performing trans1
+ *  followed by trans2.
+ *
+ *  psPlaneTransformCombine takes two transformations (trans1 and trans2) and
+ *  returns a single transformation that has the effect of performing trans1
+ *  followed by trans2. In the event that the input transformation is linear,
+ *  an exact solution may be calculated; otherwise nSamples samples in each
+ *  axis, covering the region specified by region shall be used as a grid to
+ *  fit the best inverse transformation. The function shall return NULL if it
+ *  was unable to generate the transformation; otherwise it shall return the
+ *  transformation.
+ *
+ *  @return psPlaneTransform*    resulting transformation
+ */
+psPlaneTransform* psPlaneTransformCombine(
+    psPlaneTransform *out,             ///< a transform to recycle, or NULL if one is to be created.
+    const psPlaneTransform *trans1,    ///< first transform to combine
+    const psPlaneTransform *trans2,    ///< first transform to combine
+    psRegion region,                   ///< region to cover (for non-linear transforms)
+    int nSamples                       ///< number of samples on each axis (for non-linear transforms)
+);
+
+
+/** takes two arrays containing matched coordinates and returns the
+ *  best-fitting transformation.
+ *
+ *  psPlaneTransformFit takes two arrays containing matched coordinates (i.e.,
+ *  coordinates in the source array correspond to the coordinates in the dest
+ *  array) and returns the best-fitting transformation. The source and dest
+ *  will contain psCoords. In the event that the number of coordinates in each
+ *  is not identical, the function shall generate a warning, and extra
+ *  coordinates in the longer of the two shall be ignored. The trans transform
+ *  may not be NULL, since it specifies the desired order, polynomial type and
+ *  any polynomial terms to mask. nRejIter rejection iterations shall be
+ *  performed, wherein coordinates lying more than sigmaClip standard
+ *  deviations from the fit shall be rejected.
+ *
+ *  @return bool        TRUE if successful, otherwise FALSE.
+ */
+bool psPlaneTransformFit(
+    psPlaneTransform *trans,           ///< desired order, polynomial type, & polynomial mask terms
+    const psArray *source,             ///< coordinates matching those in dest
+    const psArray *dest,               ///< coordinates matching those in source
+    int nRejIter,                      ///< number of rejection iterations to be performed
+    float sigmaClip                    ///< coordinates above this number of standard deviations will be rejected
+);
+//XXX: need to add doxygen comments on the parameters above. -rdd
+
+/** Converts from a 3-dimensional coordinate system to an angular coordinate system.
+ *
+ *  @return psSphere*       The former psCube in terms of a psSphere.
+ */
+psSphere *psCubeToSphere(
+    const psCube *cube                 ///< psCube to convert
+);
+
+/** Converts from an angular coordinate system to a 3-dimensional coordinate system.
+ *
+ *  @return psCube*         The former psSphere in terms of a psCube.
+ */
+psCube *psSphereToCube(
+    const psSphere *sphere             ///< psSphere to convert
+);
+
+
+/** Calculates the derivative of the specified psPlaneTransform with respect to x and y.
+ *
+ *  @return psPlane*         The derivative.
+ */
+psPlane *psPlaneTransformDeriv(
+    psPlane *out,
+    const psPlaneTransform *transformation,
+    const psPlane *coord
+);
+
+/** Generates a list of pixels in the output coordinate frame that overlap the input
+ *  pixels in the input coordinate frame through the specified transformation, inToOut.
+ *
+ *  psPixelsTransform is more complicated than simply transforming the input pixels,
+ *  but requires the evaluation of the derivatives of the transformation in order to
+ *  obtain the list of all pixels in the output coordinate frame that possibly overlap
+ *  the pixel in the input coordinate frame.  In the event that input or inToOut are
+ *  NULL, the function shall generate an error and return NULL.  If out is non-NULL it
+ *  shall be modified and returned;  otherwise a new psPixels shall be allocated and
+ *  returned.
+ *
+ *  @return psPixels*:      the list of overlapping pixels.
+ */
+psPixels *psPixelsTransform(
+    psPixels *out,                     ///< output list of overlapping pixels
+    const psPixels *input,             ///< input list of pixels
+    const psPlaneTransform *inToOut    ///< specified transformation
+);
+
+psString psProjectTypeToString(psProjectionType type, const char *prefix);
+psProjectionType psProjectTypeFromString(const char *name);
+
+
+#define PS_PRINT_PLANE_TRANSFORM(NAME) \
+{ \
+    printf("---------------------- Plane Transform ----------------------\n"); \
+    printf("x:\n"); \
+    PS_POLY_PRINT_2D(NAME->x); \
+    printf("y:\n"); \
+    PS_POLY_PRINT_2D(NAME->y); \
+} \
+
+/// @}
+#endif // #ifndef PS_COORD_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/psEarthOrientation.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/psEarthOrientation.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/psEarthOrientation.c	(revision 22322)
@@ -0,0 +1,1390 @@
+/** @file  psEarthOrientation.c
+ *
+ *  @brief Function implementations for earth orientation calculations
+ *
+ *  @ingroup EarthOrientation
+ *
+ *  @author Dave Robbins, MHPCC
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.46 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-06-22 02:28:48 $
+ *
+ *  Copyright 2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include "psEarthOrientation.h"
+#include "psArray.h"
+#include "psPolynomial.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psMetadataConfig.h"
+#include "psError.h"
+
+#include "psMemory.h"
+#include "psCoord.h"
+#include "psAssert.h"
+
+// Sun's Mass (src: Google's Calculator Service)
+#define PS_M   1.98892e30 /* kilograms */
+// Newton's Gravitational Constant (src:NIST)
+#define PS_G   6.6742e-11 /* m^3/kg/s^2 */
+// Speed of light in vacuum (src:NIST)
+#define PS_C0  299792458.0 /* m/s */
+// Average distance from earth to sun
+#define PS_AU 149597890000.0 /* meters */
+// Modified Julian Day 1/1/2000 00:00:00
+#define MJD_2000  51544.5
+// Days in Julian century
+#define JULIAN_CENTURY 36525.0
+
+//The arrays used for storage of x,y,&s-values extracted from the precession data tables.
+// Created in p_psEOCInit from finalsTable and used in psPrecessionModel.
+static psArray* xTable = NULL;
+static psArray* yTable = NULL;
+static psArray* sTable = NULL;
+
+//used for storage of the IERS precession table.  Should contain MJD date followed by the
+// corresponding dX & dY values from the finals2000A table.  (Used in psPrecessionCorr.)
+static psArray* iersTable = NULL;
+//used for precession data.  Should contain MJD date followed by the corresponding X, Y, &
+// UT1-UTC values from the finals2000A table.  (Used in psPrecessionModel, psGetPolarMotion).
+static psArray* finalsTable = NULL;
+
+//1D Polynomials created from the coefficients given by the tab5.2?.txt files.  Used
+// by psPrecessionModel to determine psEarthPole components.
+static psPolynomial1D* xPoly = NULL;
+static psPolynomial1D* yPoly = NULL;
+static psPolynomial1D* sPoly = NULL;
+
+//Boolean variable to tell whether the above EOC data has been initialized.
+static bool eocInitialized = false;
+
+//Internal function used for conversion from a 3x3 rotation matrix to a quaternion (psSphereRot).
+static psSphereRot *rotMatrix_To_Quat(double A[3][3]);
+
+static void earthPoleFree(psEarthPole *pole)
+{
+    // There are non dynamic allocated items
+}
+
+psEarthPole *psEarthPoleAlloc(void)
+{
+    psEarthPole* pole = psAlloc(sizeof(psEarthPole));
+    psMemSetDeallocator(pole, (psFreeFunc) earthPoleFree);
+    pole->x = 0.0;
+    pole->y = 0.0;
+    pole->s = 0.0;
+    return pole;
+}
+
+bool p_psEOCInit()
+{
+    unsigned int nFail = 0;
+
+    // Read config file
+    psMetadata* eocMetadata = psMetadataConfigRead(NULL,
+                              &nFail,
+                              p_psTimeConfigFilename(NULL),
+                              true);
+    //Make sure reading of config file worked correctly
+    if(eocMetadata == NULL) {
+        return false;
+    } else if(nFail != 0) {
+        return false;
+    }
+
+    bool success = false;
+    // Get table formats & error if lookups fail.
+    char* tableFormat = psMetadataLookupStr(&success, eocMetadata,
+                                            "psLib.eoc.precession.table.format");
+    if(! success || tableFormat == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.table.format");
+        return false;
+    }
+
+    char* xTableName = psMetadataLookupStr(&success, eocMetadata,
+                                           "psLib.eoc.precession.table.file.x");
+    if(! success || xTableName == NULL) {
+        psFree(tableFormat);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.x.file");
+        return false;
+    }
+
+    char* yTableName = psMetadataLookupStr(&success, eocMetadata,
+                                           "psLib.eoc.precession.table.file.y");
+    if(! success || yTableName == NULL) {
+        psFree(tableFormat);
+        psFree(xTableName);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.y.file");
+        return false;
+    }
+
+    char* sTableName = psMetadataLookupStr(&success, eocMetadata,
+                                           "psLib.eoc.precession.table.file.s");
+    if(! success || sTableName == NULL) {
+        psFree(tableFormat);
+        psFree(xTableName);
+        psFree(yTableName);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.s.file");
+        return false;
+    }
+
+    char* iersTableName = psMetadataLookupStr(&success, eocMetadata,
+                          "psLib.eoc.precession.table.file.iers");
+    if(! success || sTableName == NULL) {
+        psFree(tableFormat);
+        psFree(xTableName);
+        psFree(yTableName);
+        psFree(sTableName);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.iers.file");
+        return false;
+    }
+
+    char* finalsTableName = psMetadataLookupStr(&success, eocMetadata,
+                            "psLib.eoc.precession.table.file.final");
+    if(! success || sTableName == NULL) {
+        psFree(tableFormat);
+        psFree(xTableName);
+        psFree(yTableName);
+        psFree(sTableName);
+        psFree(iersTableName);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.finals.file");
+        return false;
+    }
+
+    char* iersTableFormat = psMetadataLookupStr(&success, eocMetadata,
+                            "psLib.eoc.precession.iers.table.format");
+    if(! success || iersTableFormat == NULL) {
+        psFree(tableFormat);
+        psFree(xTableName);
+        psFree(yTableName);
+        psFree(sTableName);
+        psFree(iersTableName);
+        psFree(finalsTableName);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.iers.table.format");
+        return false;
+    }
+
+    char* finalsTableFormat = psMetadataLookupStr(&success, eocMetadata,
+                              "psLib.eoc.precession.finals.table.format");
+    if(! success || iersTableFormat == NULL) {
+        psFree(tableFormat);
+        psFree(xTableName);
+        psFree(yTableName);
+        psFree(sTableName);
+        psFree(iersTableName);
+        psFree(finalsTableName);
+        psFree(iersTableFormat);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.iers.table.format");
+        return false;
+    }
+
+    //Extract the necessary data to setup the tables
+    xTable = psVectorsReadFromFile(xTableName, tableFormat);
+    yTable = psVectorsReadFromFile(yTableName, tableFormat);
+    sTable = psVectorsReadFromFile(sTableName, tableFormat);
+    iersTable = psVectorsReadFromFile(iersTableName, iersTableFormat);
+    finalsTable = psVectorsReadFromFile(finalsTableName, finalsTableFormat);
+
+    //Check that the data extraction was performed successfully.
+    if(xTable == NULL || yTable == NULL || sTable == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Failed to read the precession-nutation model tables.");
+        return false;
+    }
+    if(iersTable == NULL || finalsTable == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Failed to read the IERS/finals tables.");
+        return false;
+    }
+
+    //Load the coefficient data to setup the polynomials.
+    psVector* xCoeff = psMetadataLookupPtr(&success, eocMetadata, "psLib.eoc.precession.poly.x");
+    if(! success || xCoeff == NULL || xCoeff->type.type != PS_TYPE_F64) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.poly.x");
+        return false;
+    }
+    psVector* yCoeff = psMetadataLookupPtr(&success, eocMetadata, "psLib.eoc.precession.poly.y");
+    if(! success || yCoeff == NULL || yCoeff->type.type != PS_TYPE_F64) {
+        psFree(xCoeff);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.poly.y");
+        return false;
+    }
+    psVector* sCoeff = psMetadataLookupPtr(&success, eocMetadata, "psLib.eoc.precession.poly.s");
+    if(! success || sCoeff == NULL || sCoeff->type.type != PS_TYPE_F64) {
+        psFree(xCoeff);
+        psFree(yCoeff);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.eoc.precession.poly.s");
+        return false;
+    }
+    //Allocate the X,Y,&S polynomials to be used for earthpole calculations
+    xPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, xCoeff->n);
+    memcpy(xPoly->coeff, xCoeff->data.F64,PSELEMTYPE_SIZEOF(PS_TYPE_F64)*xCoeff->n);
+    yPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, yCoeff->n);
+    memcpy(yPoly->coeff, yCoeff->data.F64,PSELEMTYPE_SIZEOF(PS_TYPE_F64)*yCoeff->n);
+    sPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, sCoeff->n);
+    memcpy(sPoly->coeff, sCoeff->data.F64,PSELEMTYPE_SIZEOF(PS_TYPE_F64)*sCoeff->n);
+
+    psFree(eocMetadata);
+
+    return true;
+}
+
+bool p_psEOCFinalize(void)
+{
+    psFree(xTable);
+    psFree(yTable);
+    psFree(sTable);    // There are non dynamic allocated items
+    psFree(iersTable);
+    psFree(finalsTable);
+
+    xTable = NULL;
+    yTable = NULL;
+    sTable = NULL;
+    iersTable = NULL;
+    finalsTable = NULL;
+
+    psFree(xPoly);
+    psFree(yPoly);
+    psFree(sPoly);
+
+    xPoly = NULL;
+    yPoly = NULL;
+    sPoly = NULL;
+
+    eocInitialized = false;
+
+    return true;
+}
+
+psSphere *psAberration(psSphere *apparent,
+                       const psSphere *actual,
+                       const psSphere *direction,
+                       double speed)
+{
+    //Check for valid inputs
+    PS_ASSERT_PTR_NON_NULL(actual, NULL);
+    PS_ASSERT_PTR_NON_NULL(direction, NULL);
+    if (fabs(speed) < DBL_EPSILON) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Aberration speed should not be equal to 0.\n");
+        return NULL;
+    }
+    if (fabs(speed) > 1.0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Aberration speed should not greater than the speed of light!.\n");
+        return NULL;
+    }
+
+    psCube *rp = psCubeAlloc();
+    psCube *r_p = psCubeAlloc();
+    double mu = 0.0;
+    double mu_p = 0.0;
+    double a = 0.0;
+
+    //Convert the spherical input coords direction and actual to cubic coords for computations.
+    psCube* directionVector = NULL;
+    directionVector = psSphereToCube(direction);
+    if (directionVector == NULL) {
+        PS_ASSERT_PTR_NON_NULL(directionVector, NULL);
+    }
+    psCube* actualVector = NULL;
+    actualVector = psSphereToCube(actual);
+    if (actualVector == NULL) {
+        PS_ASSERT_PTR_NON_NULL(actualVector, NULL);
+    }
+
+    //Calculate mu as the dot-product of direction and actual (from ADD)
+    mu = (directionVector->x*actualVector->x +
+          directionVector->y*actualVector->y +
+          directionVector->z*actualVector->z);
+    //Calculate r-perpendicular (as in sec 3.5.3.2 of ADD). actual = r-hat, direction = beta-hat.
+    rp->x = actualVector->x - mu * directionVector->x;
+    rp->y = actualVector->y - mu * directionVector->y;
+    rp->z = actualVector->z - mu * directionVector->z;
+    //Calculate mu-prime.  (Eqn. 129 of ADD).
+    mu_p = mu - speed * ((mu * mu - 1.0) / (1.0 - speed * mu));
+    //Calculate a-value.  (a = sqrt[ (1 - mu-prime^2) / modulus(r-perpendicular) ] )
+    a = (1.0 - mu_p * mu_p) / (rp->x * rp->x + rp->y * rp->y + rp->z * rp->z);
+    if (a < 0.0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Invalid parameter.  a-value cannot be negative.\n");
+        psFree(rp);
+        psFree(r_p);
+        psFree(directionVector);
+        psFree(actualVector);
+        psFree(apparent);
+        return NULL;
+    } else {
+        a = sqrt(a);
+    }
+    //Calculate r-prime values.  r-prime = mu-prime * beta-hat  +  a * r-perpendicular
+    r_p->x = mu_p * directionVector->x + a * rp->x;
+    r_p->y = mu_p * directionVector->y + a * rp->y;
+    r_p->z = mu_p * directionVector->z + a * rp->z;
+    //If apparent is non-NULL, deallocate it for use with psCubeToSphere
+    if (apparent != NULL) {
+        psFree(apparent);
+    }
+    //Convert r-prime to spherical coordinates for output.
+    apparent = psCubeToSphere(r_p);
+    if (apparent == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "psCubeToSphere returned a NULL sphere in psAberration.\n");
+        return NULL;
+    }
+    psFree(rp);
+    psFree(r_p);
+    psFree(directionVector);
+    psFree(actualVector);
+
+    return apparent;
+}
+
+psSphere *psGravityDeflection(psSphere *apparent,
+                              psSphere *actual,
+                              psSphere *sun)
+{
+    //Check for valid inputs
+    PS_ASSERT_PTR_NON_NULL(actual, NULL);
+    PS_ASSERT_PTR_NON_NULL(sun, NULL);
+
+    psSphere *temp = psSphereAlloc();
+    psCube* sunVector = psSphereToCube(sun);
+    psCube* actualVector = psSphereToCube(actual);
+
+    // use dot product to calculate the angle of separation
+    double dotProd = (sunVector->x*actualVector->x +
+                      sunVector->y*actualVector->y + sunVector->z*actualVector->z);
+    double theta, sunMag, actMag;
+    sunMag = sqrt(sunVector->x*sunVector->x + sunVector->y*sunVector->y +
+                  sunVector->z*sunVector->z);
+    actMag = sqrt(actualVector->x*actualVector->x + actualVector->y*actualVector->y +
+                  actualVector->z*actualVector->z);
+    dotProd = dotProd / (sunMag * actMag);
+    theta = acos(dotProd);
+
+    printf(" Theta = %.13g\n", theta);
+    double r0 = PS_AU * tan(theta);
+    printf(" r0 = %.19e\n", r0);
+    double deflection = 4.0*PS_G*PS_M/(PS_C0*PS_C0*r0);
+
+    // make sure the deflection is not greater than 1.75 arcsec
+    double limit = SEC_TO_RAD(1.75);
+    if (deflection > limit) {
+        //if deflection is greater than limit, the light rays will hit the sun
+        psWarning("Invalid positions.  Light ray will not be seen on earth.\n");
+        psFree(actualVector);
+        psFree(sunVector);
+        return apparent;
+    }
+
+
+    if (apparent != NULL) {
+        psFree(apparent);
+    }
+    // bend the actual vector away from the sun vector by deflection angle.
+    theta = 0.0;
+    double phi = 0.0;
+    //    deflection = SEC_TO_RAD(deflection) * 1e6;
+    //    deflection *= M_PI * 1e-2;
+    theta = atan(r0/PS_AU) * tan(deflection);
+    //    phi = sqrt( deflection*deflection - theta*theta );
+    //    phi = deflection * cos(asin(theta/deflection));
+
+    //    phi = sqrt(theta*theta - deflection*deflection);
+    //    phi = deflection * cos(asin(theta/deflection)) * 3e-2;
+    //    phi = cos(asin(theta/deflection));
+    //    phi = asin(theta/deflection);
+
+    //    apparent->r = theta;
+    //    apparent->d = phi;
+    /*
+        actualVector->x += actualVector->x*deflection;
+        actualVector->y += actualVector->y*deflection;
+        actualVector->z += actualVector->z*deflection;
+        apparent = psCubeToSphere(actualVector);
+    */
+    theta = tan(sun->r - actual->r) * deflection;
+    phi = tan(sun->d - actual->d) * deflection;
+
+    printf(" Theta = %.13g\n", theta);
+    printf(" phi = %.13g\n", phi);
+
+    temp->r = theta;
+    temp->d = phi;
+    apparent = psSphereSetOffset(actual, temp, PS_SPHERICAL, PS_RADIAN);
+
+    psFree(actualVector);
+    psFree(sunVector);
+    psFree(temp);
+
+    return apparent;
+}
+
+psEarthPole *psEOC_PrecessionModel(const psTime *time)
+{
+    // Check for null parameter
+    PS_ASSERT_PTR_NON_NULL(time, NULL);
+    if (time->type == PS_TIME_UT1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Invalid time input.  Time cannot be of type UT1.\n");
+        return NULL;
+    }
+
+    // Convert psTime to MJD
+    double MJD = psTimeToMJD(time);
+    if (isnan(MJD)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Time conversion to MJD failed.  Invalid input time.\n");
+        return NULL;
+    }
+
+    // Calculate number of Julian centuries since 2000
+    double t = ( MJD - MJD_2000 ) / JULIAN_CENTURY;
+    double t2 = t*t;
+    double t3 = t*t*t;
+    double t4 = t*t*t*t;
+
+    // The following formulae are from the ADD (& s5.8 of IERS Technical Note 32):
+    double F[14];
+    // Mean Anomaly of the Moon (l)
+    F[0] = DEG_TO_RAD(134.96340251) +
+           SEC_TO_RAD(1717915923.2178)*t +
+           SEC_TO_RAD(31.8792)*t2 +
+           SEC_TO_RAD(0.051635)*t3 -
+           SEC_TO_RAD(0.00024470)*t4;
+    // Mean Anomaly of the Sun (l')
+    F[1] = DEG_TO_RAD(357.52910918) +
+           SEC_TO_RAD(129596581.0481)*t -
+           SEC_TO_RAD(0.5532)*t2 +
+           SEC_TO_RAD(0.000136)*t3 -
+           SEC_TO_RAD(0.00001149)*t4;
+    // L - Omega    (L = Mean Longitude of the Moon, Omega = F4)
+    F[2] = DEG_TO_RAD(93.27209062) +
+           SEC_TO_RAD(1739527262.8478)*t -
+           SEC_TO_RAD(12.7512)*t2 -
+           SEC_TO_RAD(0.001037)*t3 +
+           SEC_TO_RAD(0.00000417)*t4;
+    // Mean Elongation of the Moon from the Sun (D)
+    F[3] = DEG_TO_RAD(297.85019547) +
+           SEC_TO_RAD(1602961601.2090)*t -
+           SEC_TO_RAD(6.3706)*t2 +
+           SEC_TO_RAD(0.006593)*t3 -
+           SEC_TO_RAD(0.00003169)*t4;
+    // Mean Longitude of the Ascending Node of the Moon (Omega)
+    F[4] = DEG_TO_RAD(125.04455501) -
+           SEC_TO_RAD(6962890.5431)*t +
+           SEC_TO_RAD(7.4722)*t2 +
+           SEC_TO_RAD(0.007702)*t3 -
+           SEC_TO_RAD(0.0000593)*t4;
+    //F5-F13 are the mean longitudes of the planets
+    F[5] = 4.402608842 + 2608.7903141574*t;
+    F[6] = 3.176146697 + 1021.3285546211*t;
+    F[7] = 1.753470314 + 628.3075849991*t;
+    F[8] = 6.203480913 + 334.0612426700*t;
+    F[9] = 0.599546497 + 52.9690962641*t;
+    F[10] = 0.874016757 + 21.3299104960*t;
+    F[11] = 5.481293872 + 7.4781598567*t;
+    F[12] = 5.311886287 + 3.8133035638*t;
+    F[13] = 0.024381750*t + 0.00000538691*t2;
+
+    // Check if EOC data loaded
+    if(! eocInitialized) {
+        eocInitialized = p_psEOCInit();
+        if(!eocInitialized) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Could not initialize EOC tables -- check data files.");
+            return NULL;
+        }
+    }
+    // calculate the polynomial portion first
+    double X = psPolynomial1DEval(xPoly,t);
+    double Y = psPolynomial1DEval(yPoly,t);
+    double S = psPolynomial1DEval(sPoly,t);
+    //Units from the table & coefficients are in micro-arcseconds so convert to radians.
+    X = SEC_TO_RAD(X * 1e-6);
+    Y = SEC_TO_RAD(Y * 1e-6);
+    S = SEC_TO_RAD(S * 1e-6);
+
+    // now calculate the non-poly portion from the tables
+    psF64* cols[17];  //Used to store all of the table information from tab5.2?.dat
+    for (int lcv = 0; lcv < 17; lcv++) {
+        cols[lcv] = ((psVector*)(xTable->data[lcv]))->data.F64;
+    }
+    //Get the number of rows in the table and loop through the non-poly contributions.
+    int numRows = ((psVector*)(xTable->data[0]))->n;
+    for (int lcv = 0; lcv < numRows; lcv++) {
+        double arg = 0.0;
+        //Get the argument- from the table and corresponding F-value.  Convert to radians.
+        for (int k = 0; k < 14; k++) {
+            arg += cols[k+3][lcv]*F[k];
+        }
+        //The order of t for each part is specified by a column in the tab.dat files.
+        double tj = pow(t,cols[0][lcv]);
+        double as = cols[1][lcv];
+        double ac = cols[2][lcv];
+        as = SEC_TO_RAD(as) * 1e-6;
+        ac = SEC_TO_RAD(ac) * 1e-6;
+        X += (as*tj*sin(arg) + ac*cos(arg)) * tj;
+    }
+    //Do the same procedure from the previous 15 lines for Y.
+    for (int lcv = 0; lcv < 17; lcv++) {
+        cols[lcv] = ((psVector*)(yTable->data[lcv]))->data.F64;
+    }
+    numRows = ((psVector*)(yTable->data[0]))->n;
+    for (int lcv = 0; lcv < numRows; lcv++) {
+        double arg = 0.0;
+        for (int k = 0; k < 14; k++) {
+            arg += cols[k+3][lcv]*F[k];
+        }
+        double tj = pow(t,cols[0][lcv]);
+        double as = cols[1][lcv];
+        double ac = cols[2][lcv];
+        as = SEC_TO_RAD(as) * 1e-6;
+        ac = SEC_TO_RAD(ac) * 1e-6;
+        Y += (as*tj*sin(arg) + ac*cos(arg)) * tj;
+    }
+    //Again for S.
+    for (int lcv = 0; lcv < 17; lcv++) {
+        cols[lcv] = ((psVector*)(sTable->data[lcv]))->data.F64;
+    }
+    numRows = ((psVector*)(sTable->data[0]))->n;
+    for (int lcv = 0; lcv < numRows; lcv++) {
+        double arg = 0.0;
+        for (int k = 0; k < 14; k++) {
+            arg += cols[k+3][lcv]*F[k];
+        }
+        double tj = pow(t,cols[0][lcv]);
+        double as = cols[1][lcv];
+        double ac = cols[2][lcv];
+        as = SEC_TO_RAD(as) * 1e-6;
+        ac = SEC_TO_RAD(ac) * 1e-6;
+        S += (as*tj*sin(arg) + ac*cos(arg)) * tj;
+    }
+
+    //the tables for S actually gives S + XY/2, so let's get the real S now. (from ADD)
+    S -= X*Y/2.0;
+
+    //Create the output psEarthPole and set the corresponding component values.
+    psEarthPole* pole = psEarthPoleAlloc();
+    pole->x = X;
+    pole->y = Y;
+    pole->s = S;
+
+    return pole;
+}
+
+psEarthPole *psEOC_PrecessionCorr(const psTime *time,
+                                  psTimeBulletin bulletin)
+{
+    // Check for null parameter or invalid bulletin
+    PS_ASSERT_PTR_NON_NULL(time,NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(bulletin, PS_IERS_A, PS_IERS_B, NULL);
+    //Convert the input time to MJD.  If NAN is returned, return NULL for the function.
+    double MJD = psTimeToMJD(time);
+    if (isnan(MJD)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Time conversion to MJD failed.  Invalid input time.\n");
+        return NULL;
+    }
+    //Allocate space for the psEarthPole output.
+    psEarthPole *out = psEarthPoleAlloc();
+
+    // Check if EOC data loaded
+    if(!eocInitialized) {
+        eocInitialized = p_psEOCInit();
+        if(!eocInitialized) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Could not initialize EOC tables -- check data files.");
+            return NULL;
+        }
+    }
+
+    //Load the data table and store in the corresponding, newly-allocated vectors.
+    psF64* cols[5];
+    for (int colNum = 0; colNum < 5; colNum++) {
+        cols[colNum] = ((psVector*)(iersTable->data[colNum]))->data.F64;
+    }
+    int numRows = ((psVector*)(iersTable->data[0]))->n;
+    psVector *X = psVectorAlloc(numRows, PS_TYPE_F64);
+    psVector *Y = psVectorAlloc(numRows, PS_TYPE_F64);
+    psVector *T = psVectorAlloc(numRows, PS_TYPE_F64);
+    if (bulletin == PS_IERS_A) {
+        for (int rowNum = 0; rowNum < numRows; rowNum++) {
+            T->data.F64[rowNum] = cols[0][rowNum];
+            X->data.F64[rowNum] = cols[1][rowNum];
+            Y->data.F64[rowNum] = cols[2][rowNum];
+        }
+    } else {
+        for (int rowNum = 0; rowNum < numRows; rowNum++) {
+            T->data.F64[rowNum] = cols[0][rowNum];
+            X->data.F64[rowNum] = cols[3][rowNum];
+            Y->data.F64[rowNum] = cols[4][rowNum];
+        }
+    }
+
+    //The following uses lagrange interpolation to calculate the corrections to X & Y.
+    double xOut = 0.0;
+    double yOut = 0.0;
+    double xTerm = 0.0;
+    double yTerm = 0.0;
+    int k = 0;
+    for (int i = 0; i < (numRows-1); i++) {
+        if (MJD >= T->data.F64[i] && MJD < T->data.F64[i+1]) {
+            k = i;
+            if (k < 2) {
+                k = 2;
+            }
+            if (k > (numRows-2)) {
+                k = numRows-2;
+            }
+            for (int m = k-1; m <= k+2; m++) {
+                xTerm = X->data.F64[m];
+                yTerm = Y->data.F64[m];
+                for (int j = k-1; j <= k+2; j++) {
+                    if ( m != j) {
+                        double term = (MJD - T->data.F64[j])/(T->data.F64[m] - T->data.F64[j]);
+                        xTerm *= term;
+                        yTerm *= term;
+                    }
+                }
+                xOut += xTerm;
+                yOut += yTerm;
+            }
+            i = numRows-1;
+        }
+    }
+    //Convert the values from milli-arcsecond to radian.
+    out->x = SEC_TO_RAD(xOut) * 1e-3;
+    out->y = SEC_TO_RAD(yOut) * 1e-3;
+    psFree(X);
+    psFree(Y);
+    psFree(T);
+
+    return out;
+}
+
+static psSphereRot *rotMatrix_To_Quat(double A[3][3])
+{
+    int i;
+    psSphereRot *new = psSphereRotAlloc(0.0, 0.0, 0.0);
+    new->q3 = 0.0;
+
+    //Convert rotation matrix to quaternions.  Formula directly from ADD.
+    double diag_sum[3];
+    int maxi;
+    double recip;
+    diag_sum[0] = 1.0 + A[0][0] - A[1][1] - A[2][2];
+    diag_sum[1] = 1.0 - A[0][0] + A[1][1] - A[2][2];
+    diag_sum[2] = 1.0 - A[0][0] - A[1][1] + A[2][2];
+    diag_sum[3] = 1.0 + A[0][0] + A[1][1] + A[2][2];
+
+    maxi = 0;
+    for (i = 1; i < 4; ++i) {
+        if (diag_sum[i] > diag_sum[maxi]) {
+            maxi = i;
+        }
+    }
+
+    double p = 0.5 * sqrt(diag_sum[maxi]);
+    recip = 1.0 / (4.0 * p);
+
+    if (maxi == 0) {
+        new->q0 = p;
+        new->q1 = recip * (A[0][1] + A[1][0]);
+        new->q2 = recip * (A[2][0] + A[0][2]);
+        new->q3 = recip * (A[1][2] - A[2][1]);
+    } else if (maxi == 1) {
+        new->q0 = recip * (A[0][1] + A[1][0]);
+        new->q1 = p;
+        new->q2 = recip * (A[1][2] + A[2][1]);
+        new->q3 = recip * (A[2][0] - A[0][2]);
+    } else if (maxi == 2) {
+        new->q0 = recip * (A[2][0] + A[0][2]);
+        new->q1 = recip * (A[1][2] + A[2][1]);
+        new->q2 = p;
+        new->q3 = recip * (A[0][1] - A[1][0]);
+    } else if (maxi == 3) {
+        new->q0 = recip * (A[1][2] - A[2][1]);
+        new->q1 = recip * (A[2][0] - A[0][2]);
+        new->q2 = recip * (A[0][1] - A[1][0]);
+        new->q3 = p;
+    }
+    return new;
+}
+
+psSphereRot* psSphereRot_CEOtoGCRS(const psEarthPole *pole)
+{
+    // Check for null parameter
+    PS_ASSERT_PTR_NON_NULL(pole,NULL);
+    double A[3][3];
+    psSphereRot *out = NULL;
+
+    //Setup the rotation matrix and scalar value, a, as outlined by the ADD
+    double a =  1.0 / (1.0 + sqrt(1.0 - (pole->x*pole->x + pole->y*pole->y) ) );
+    A[0][0] = (1.0 - a*pole->x*pole->x)*cos(pole->s) - a*pole->x*pole->y*sin(pole->s);
+    A[1][0] = -a*pole->x*pole->y*cos(pole->s) + (1.0 - a*pole->y*pole->y)*sin(pole->s);
+    A[2][0] = pole->x*cos(pole->s) + pole->y*sin(pole->s);
+    A[0][1] = -(1.0 - a*pole->x*pole->x)*sin(pole->s) - a*pole->x*pole->y*cos(pole->s);
+    A[1][1] = a*pole->x*pole->y*sin(pole->s) + (1.0 - a*pole->y*pole->y)*cos(pole->s);
+    A[2][1] = -pole->x*sin(pole->s) + pole->y*cos(pole->s);
+    A[0][2] = -pole->x;
+    A[1][2] = -pole->y;
+    A[2][2] = 1.0 - a*(pole->x*pole->x + pole->y*pole->y);
+
+    //Convert the rotation matrix to a psSphereRot (quaternions)
+    out = rotMatrix_To_Quat(A);
+
+    return out;
+}
+
+psSphereRot* psSphereRot_TEOtoCEO(const psTime *time,
+                                  psEarthPole *tidalCorr)
+{
+    // Check for null parameter
+    PS_ASSERT_PTR_NON_NULL(time,NULL);
+    //Create a copy of the input time that can be manipulated/changed (input time is const).
+    psTime *in = psTimeCopy(time);
+    if (in->type != PS_TIME_UT1) {
+        in = psTimeConvert(in, PS_TIME_UT1);
+    }
+    //Check if tidal corrections should be included.
+    //If so, make sure values are positive and in the correct range.
+    if (tidalCorr != NULL && fabs(tidalCorr->s) > FLT_EPSILON) {
+        int nsec = in->nsec + (int)(tidalCorr->s * 1e9);
+        if (nsec > 1e9) {
+            nsec += -1e9;
+            in->sec += 1;
+        }
+        if (nsec < 0) {
+            in->sec += -1;
+            in->nsec = (int)(1e9) + nsec;
+        } else {
+            in->nsec = nsec;
+        }
+    }
+    //Calculate the Julian Date from the input time in UT1 format.
+    double T = psTimeToJD(in);
+    if (isnan(T)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Time conversion to JD failed.  Invalid input time.\n");
+        return NULL;
+    }
+    T += -2451545.0;
+    //Formula for theta comes directly from the ADD.  Create output psSphereRot from theta.
+    double theta = 2.0 * M_PI * (0.7790572732640 + 1.00273781191135448 * T);
+    psSphereRot *out = psSphereRotAlloc(theta, 0.0, 0.0);
+
+    psFree(in);
+    return out;
+}
+
+psEarthPole* psEOC_GetPolarMotion(const psTime *time,
+                                  psTimeBulletin bulletin)
+{
+    PS_ASSERT_PTR_NON_NULL(time, NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(bulletin, PS_IERS_A, PS_IERS_B, NULL);
+
+    psEarthPole *out = psEarthPoleAlloc();
+    out->x = 0.0;
+    out->y = 0.0;
+    out->s = 0.0;
+
+    double MJD = psTimeToMJD(time);
+    if ( isnan(MJD) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Time conversion to MJD failed.  Invalid input time.\n");
+        return NULL;
+    }
+
+    // Check if EOC data loaded
+    if(! eocInitialized) {
+        eocInitialized = p_psEOCInit();
+        if(!eocInitialized) {
+            // XXX: Move error message.
+            psError(PS_ERR_UNKNOWN, false,
+                    "Could not initialize EOC tables -- check data files.");
+            return NULL;
+        }
+    }
+
+    psF64* cols[7];
+    for (int colNum = 0; colNum < 7; colNum++) {
+        cols[colNum] = ((psVector*)(finalsTable->data[colNum]))->data.F64;
+    }
+    int numRows = ((psVector*)(finalsTable->data[0]))->n;
+    psVector *X = psVectorAlloc(numRows, PS_TYPE_F64);
+    psVector *Y = psVectorAlloc(numRows, PS_TYPE_F64);
+    //    psVector *S = psVectorAlloc(numRows, PS_TYPE_F64);
+    psVector *T = psVectorAlloc(numRows, PS_TYPE_F64);
+    if (bulletin == PS_IERS_A) {
+        for (int rowNum = 0; rowNum < numRows; rowNum++) {
+            T->data.F64[rowNum] = cols[0][rowNum];
+            X->data.F64[rowNum] = cols[1][rowNum];
+            Y->data.F64[rowNum] = cols[2][rowNum];
+            //            S->data.F64[rowNum] = cols[3][rowNum];
+        }
+    } else {
+        for (int rowNum = 0; rowNum < numRows; rowNum++) {
+            T->data.F64[rowNum] = cols[0][rowNum];
+            X->data.F64[rowNum] = cols[4][rowNum];
+            Y->data.F64[rowNum] = cols[5][rowNum];
+            //            S->data.F64[rowNum] = cols[6][rowNum];
+        }
+    }
+
+    double xOut = 0.0;
+    double yOut = 0.0;
+    //    double sOut = 0.0;
+    double xTerm = 0.0;
+    double yTerm = 0.0;
+    //    double sTerm = 0.0;
+    int k = 0;
+    for (int i = 0; i < (numRows-1); i++) {
+        if (MJD >= T->data.F64[i] && MJD < T->data.F64[i+1]) {
+            k = i;
+            if (k < 2) {
+                k = 2;
+            }
+            //            if (k > (numRows-2)) {
+            //                k = numRows-2;
+            //            }
+            for (int m = k-1; m <= k+2; m++) {
+                xTerm = X->data.F64[m];
+                yTerm = Y->data.F64[m];
+                //                sTerm = S->data.F64[m];
+                for (int j = k-1; j <= k+2; j++) {
+                    if ( m != j) {
+                        double term = (MJD - T->data.F64[j])/(T->data.F64[m] - T->data.F64[j]);
+                        xTerm *= term;
+                        yTerm *= term;
+                        //                        sTerm *= term;
+                    }
+                }
+                xOut += xTerm;
+                yOut += yTerm;
+                //                sOut += sTerm;
+            }
+            i = numRows-1;
+        }
+    }
+    out->x = SEC_TO_RAD(xOut);
+    out->y = SEC_TO_RAD(yOut);
+    //    psEarthPole *polarTideCorr = psEOC_PolarTideCorr(time);
+    //    out->x += polarTideCorr->x;
+    //    out->y += polarTideCorr->y;
+    //    psFree(polarTideCorr);
+
+    //    out->s = SEC_TO_RAD(sOut);
+
+
+    /*
+        for (int rowNum = 0; rowNum < numRows; rowNum++) {
+            if ( (MJD - cols[0][rowNum]) < 1.0 ) {
+                if (bulletin == PS_IERS_A) {
+                    out->x = cols[1][rowNum];
+                    out->y = cols[2][rowNum];
+                    out->s = cols[3][rowNum];
+                    out->x = SEC_TO_RAD(out->x);
+                    out->y = SEC_TO_RAD(out->y);
+                    out->s = SEC_TO_RAD(out->s);
+                    rowNum = numRows;
+                } else {
+                    out->x = cols[4][rowNum];
+                    out->y = cols[5][rowNum];
+                    out->s = cols[6][rowNum];
+                    out->x = SEC_TO_RAD(out->x);
+                    out->y = SEC_TO_RAD(out->y);
+                    out->s = SEC_TO_RAD(out->s);
+                    rowNum = numRows;
+                }
+            }
+        }
+    */
+    psFree(X);
+    psFree(Y);
+    //    psFree(S);
+    psFree(T);
+    return out;
+}
+
+static double DMOD(double x, double y)
+{
+    //Internal function for calculating the remainder of a double quotient.
+    //used often in the algorithm for psEOC_PolarTideCorr which is the Ray model of
+    //Simon et. al. from its fortran implementation.
+    double value = x - y * trunc(x/y);
+    return value;
+}
+
+psEarthPole* psEOC_PolarTideCorr(const psTime *time)
+{
+    // Check for null parameter
+    PS_ASSERT_PTR_NON_NULL(time, NULL);
+    // Convert psTime to MJD
+    double MJD = psTimeToMJD(time);
+    if (isnan(MJD)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Time conversion to MJD failed.  Invalid input time.\n");
+        return NULL;
+    }
+    psEarthPole *out = psEarthPoleAlloc();
+
+    //Formula comes from fortran reference of the Ray model of Simon et. al.
+    double T, L, LPRIME, CAPF, CAPD, OMEGA, THETA, CORX, CORY, CORZ;
+    double ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8;
+    double T2, T3, T4;
+    // Calculate number of Julian centuries since 2000
+    T = (MJD - 51544.5) / 36525.0;
+    T2 = T*T;
+    T3 = T*T*T;
+    T4 = T*T*T*T;
+    L = -0.0002447 * T4 + 0.051635 * T3 + 31.8792 * T2 + 1717915923.2178 * T + 485868.249036;
+    L = DMOD(L, 1296000.0);
+    LPRIME = -0.00001149 * T4 - 0.000136 * T3 - 0.5532 * T2 + 129596581.0481 * T + 1287104.79305;
+    LPRIME = DMOD(LPRIME, 1296000.0);
+    CAPF = 0.00000417 * T4 - 0.001037 * T3 - 12.7512 * T2 + 1739527262.8478 * T + 335779.526232;
+    CAPF = DMOD(CAPF, 1296000.0);
+    CAPD = -0.00003169 * T4 + 0.006593 * T3 - 6.3706 * T2 + 1602961601.209 * T + 1072260.70369;
+    CAPD = DMOD(CAPD, 1296000.0);
+    OMEGA = -0.00005939 * T4 + 0.007702 * T3 + 7.4722 * T2 - 6962890.2665 * T + 450160.398036;
+    OMEGA = DMOD(OMEGA, 1296000.0);
+    THETA = (67310.54841 + (876600.0 * 3600.0 + 8640184.812866) * T + 0.093104 * T2 -
+             6.2e-6 * T3) * 15.0 + 648000.0;
+    ARG7 = DMOD((-L - 2.0 * CAPF - 2.0 * OMEGA + THETA) * M_PI / 648000.0, 2.0 * M_PI)
+           - M_PI / 2.0;
+    ARG1 = DMOD((-2.0 * CAPF - 2.0 * OMEGA + THETA) * M_PI / 648000.0, 2.0 * M_PI) - M_PI / 2.0;
+    ARG2 = DMOD((-2.0 * CAPF + 2.0 * CAPD - 2.0 * OMEGA + THETA) * M_PI / 648000.0, 2.0 * M_PI)
+           - M_PI / 2.0;
+    ARG3 = DMOD(THETA * M_PI / 648000.0, 2.0 * M_PI) - M_PI / 2.0;
+    ARG4 = DMOD((-L - 2.0 * CAPF - 2.0 * OMEGA + 2.0 * THETA) * M_PI / 648000.0, 2.0 * M_PI);
+    ARG5 = DMOD((-2.0 * CAPF - 2.0 * OMEGA + 2.0 * THETA) * M_PI / 648000.0, 2.0 * M_PI);
+    ARG6 = DMOD((-2.0 * CAPF + 2.0 * CAPD - 2.0 * OMEGA + 2.0 * THETA) * M_PI / 648000.0,
+                2.0 * M_PI);
+    ARG8 = DMOD((2.0 * THETA) * M_PI / 648000.0, 2.0 * M_PI);
+    CORX = -0.026 * sin(ARG7) + 0.006 * cos(ARG7)
+           -0.133 * sin(ARG1) + 0.049 * cos(ARG1)
+           -0.050 * sin(ARG2) + 0.025 * cos(ARG2)
+           -0.152 * sin(ARG3) + 0.078 * cos(ARG3)
+           -0.057 * sin(ARG4) - 0.013 * cos(ARG4)
+           -0.330 * sin(ARG5) - 0.028 * cos(ARG5)
+           -0.145 * sin(ARG6) + 0.064 * cos(ARG6)
+           -0.036 * sin(ARG8) + 0.017 * cos(ARG8);
+    CORY = -0.006 * sin(ARG7) - 0.026 * cos(ARG7)
+           -0.049 * sin(ARG1) - 0.133 * cos(ARG1)
+           -0.025 * sin(ARG2) - 0.050 * cos(ARG2)
+           -0.078 * sin(ARG3) - 0.152 * cos(ARG3)
+           +0.011 * sin(ARG4) + 0.033 * cos(ARG4)
+           +0.037 * sin(ARG5) + 0.196 * cos(ARG5)
+           +0.059 * sin(ARG6) + 0.087 * cos(ARG6)
+           +0.018 * sin(ARG8) + 0.022 * cos(ARG8);
+    CORZ =  0.0245 * sin(ARG7) + 0.0503 * cos(ARG7)
+            +0.1210 * sin(ARG1) + 0.1605 * cos(ARG1)
+            +0.0286 * sin(ARG2) + 0.0516 * cos(ARG2)
+            +0.0864 * sin(ARG3) + 0.1771 * cos(ARG3)
+            -0.0380 * sin(ARG4) - 0.0154 * cos(ARG4)
+            -0.1617 * sin(ARG5) - 0.0720 * cos(ARG5)
+            -0.0759 * sin(ARG6) - 0.0004 * cos(ARG6)
+            -0.0196 * sin(ARG8) - 0.0038 * cos(ARG8);
+    CORX = CORX * 1.0e-3;
+    CORY = CORY * 1.0e-3;
+    CORZ = CORZ * 0.1e-3;
+
+    CORX = SEC_TO_RAD(CORX);
+    CORY = SEC_TO_RAD(CORY);
+
+    out->x = CORX;
+    out->y = CORY;
+    out->s = CORZ;
+
+    return out;
+}
+
+psEarthPole* psEOC_NutationCorr(psTime *time)
+{
+    // Check for null parameter
+    PS_ASSERT_PTR_NON_NULL(time, NULL);
+    // Convert psTime to MJD
+    double MJD = psTimeToMJD(time);
+    //    printf("\nMJD check = %.13g\n", MJD);
+    if (isnan(MJD)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Time conversion to MJD failed.  Invalid input time.\n");
+        return NULL;
+    }
+
+    // Calculate number of Julian centuries since 2000
+    double t = ( MJD - MJD_2000 ) / JULIAN_CENTURY;
+    double t2 = t*t;
+    double t3 = t*t*t;
+    double t4 = t*t*t*t;
+
+    // The following formulae are from the ADD (& s5.8 of IERS Technical Note 32):
+    double F[5];
+    // Mean Anomaly of the Moon
+    F[0] = DEG_TO_RAD(134.96340251) +
+           SEC_TO_RAD(1717915923.2178)*t +
+           SEC_TO_RAD(31.8792)*t2 +
+           SEC_TO_RAD(0.051635)*t3 -
+           SEC_TO_RAD(0.00024470)*t4;
+
+    // Mean Anomaly of the Sun
+    F[1] = DEG_TO_RAD(357.52910918) +
+           SEC_TO_RAD(129596581.0481)*t -
+           SEC_TO_RAD(0.5532)*t2 +
+           SEC_TO_RAD(0.000136)*t3 -
+           SEC_TO_RAD(0.00001149)*t4;
+
+    // L â Omega    (L = Mean Longitude of the Moon, Omega = F4)
+    F[2] = DEG_TO_RAD(93.27209062) +
+           SEC_TO_RAD(1739527262.8478)*t -
+           SEC_TO_RAD(12.7512)*t2 -
+           SEC_TO_RAD(0.001037)*t3 +
+           SEC_TO_RAD(0.00000417)*t4;
+
+    // Mean Elongation of the Moon from the Sun
+    F[3] = DEG_TO_RAD(297.85019547) +
+           SEC_TO_RAD(1602961601.2090)*t -
+           SEC_TO_RAD(6.3706)*t2 +
+           SEC_TO_RAD(0.006593)*t3 -
+           SEC_TO_RAD(0.00003169)*t4;
+
+    // Mean Longitude of the Ascending Node of the Moon
+    F[4] = DEG_TO_RAD(125.04455501) -
+           SEC_TO_RAD(6962890.5431)*t +
+           SEC_TO_RAD(7.4722)*t2 +
+           SEC_TO_RAD(0.007702)*t3 -
+           SEC_TO_RAD(0.00005939)*t4;
+
+    //argument values taken from table 5.1 in IERS techical note No.32
+    //http://maia.usno.navy.mil/conv2000/chapter5/tn32_c5.pdf, p38
+    //Units are in micro-arcseconds here and must be converted to radians before using
+    double w_l[10] = {SEC_TO_RAD(-1.0),
+                      SEC_TO_RAD(-1.0),
+                      SEC_TO_RAD(1.0),
+                      0.0,
+                      0.0,
+                      SEC_TO_RAD(-1.0),
+                      0.0,
+                      0.0,
+                      0.0,
+                      SEC_TO_RAD(1.0)};
+    double w_l_p[10] = {0.0,
+                        0.0,
+                        0.0,
+                        0.0,
+                        0.0,
+                        0.0,
+                        0.0,
+                        0.0,
+                        0.0,
+                        0.0};
+    double w_F[10] = {SEC_TO_RAD(-2.0),
+                      SEC_TO_RAD(-2.0),
+                      SEC_TO_RAD(-2.0),
+                      SEC_TO_RAD(-2.0),
+                      SEC_TO_RAD(-2.0),
+                      0.0,
+                      SEC_TO_RAD(-2.0),
+                      0.0,
+                      0.0,
+                      0.0};
+    double w_D[10] = {0.0,
+                      0.0,
+                      SEC_TO_RAD(-2.0),
+                      0.0,
+                      0.0,
+                      0.0,
+                      SEC_TO_RAD(2.0),
+                      0.0,
+                      0.0,
+                      0.0};
+    double w_Omega[10] = {SEC_TO_RAD(-1.0),
+                          SEC_TO_RAD(-2.0),
+                          SEC_TO_RAD(-2.0),
+                          SEC_TO_RAD(-1.0),
+                          SEC_TO_RAD(-2.0),
+                          0.0,
+                          SEC_TO_RAD(-2.0),
+                          0.0,
+                          SEC_TO_RAD(-1.0),
+                          0.0};
+    double xp_sin[10] = {SEC_TO_RAD(-0.44),
+                         SEC_TO_RAD(-2.31),
+                         SEC_TO_RAD(-0.44),
+                         SEC_TO_RAD(-2.14),
+                         SEC_TO_RAD(-11.36),
+                         SEC_TO_RAD(0.84),
+                         SEC_TO_RAD(-4.76),
+                         SEC_TO_RAD(14.27),
+                         SEC_TO_RAD(1.93),
+                         SEC_TO_RAD(0.76)};
+    double xp_cos[10] = {SEC_TO_RAD(0.25),
+                         SEC_TO_RAD(1.32),
+                         SEC_TO_RAD(0.25),
+                         SEC_TO_RAD(1.23),
+                         SEC_TO_RAD(6.52),
+                         SEC_TO_RAD(-0.48),
+                         SEC_TO_RAD(2.73),
+                         SEC_TO_RAD(-8.19),
+                         SEC_TO_RAD(-1.11),
+                         SEC_TO_RAD(-0.43)};
+    double yp_sin[10] = {SEC_TO_RAD(-0.25),
+                         SEC_TO_RAD(-1.32),
+                         SEC_TO_RAD(-0.25),
+                         SEC_TO_RAD(-1.23),
+                         SEC_TO_RAD(-6.52),
+                         SEC_TO_RAD(0.48),
+                         SEC_TO_RAD(-2.73),
+                         SEC_TO_RAD(8.19),
+                         SEC_TO_RAD(1.11),
+                         SEC_TO_RAD(0.43)};
+    double yp_cos[10] = {SEC_TO_RAD(-0.44),
+                         SEC_TO_RAD(-2.31),
+                         SEC_TO_RAD(-0.44),
+                         SEC_TO_RAD(-2.14),
+                         SEC_TO_RAD(-11.36),
+                         SEC_TO_RAD(0.84),
+                         SEC_TO_RAD(-4.76),
+                         SEC_TO_RAD(14.27),
+                         SEC_TO_RAD(1.93),
+                         SEC_TO_RAD(0.76)};
+
+    double X = 0.0;
+    double Y = 0.0;
+
+    //Implementation adapted from PM_GRAVI in interp.f from hpiers.obspm.fr/eop-pc/models/interp.f
+    double arg[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
+    arg[0] = (67310.54841 +
+              (876600.0*3600.0 + 8640184.812866)*t
+              + 0.093104*t2 - 6.2e-6*t3) * 15.0 + 648000.0;
+    arg[0] = DMOD(arg[1], 1296000.0);
+    arg[0] = SEC_TO_RAD(arg[0]);
+    arg[1] = RAD_TO_SEC(F[0]);
+    arg[1] = DMOD(arg[1], 1296000.0);
+    arg[1] = SEC_TO_RAD(arg[1]);
+    arg[2] = RAD_TO_SEC(F[1]);
+    arg[2] = DMOD(arg[2], 1296000.0);
+    arg[2] = SEC_TO_RAD(arg[2]);
+    arg[3] = RAD_TO_SEC(F[2]);
+    arg[3] = DMOD(arg[3], 1296000.0);
+    arg[3] = SEC_TO_RAD(arg[3]);
+    arg[4] = RAD_TO_SEC(F[3]);
+    arg[4] = DMOD(arg[4], 1296000.0);
+    arg[4] = SEC_TO_RAD(arg[4]);
+    arg[5] = RAD_TO_SEC(F[4]);
+    arg[5] = DMOD(arg[5], 1296000.0);
+    arg[5] = SEC_TO_RAD(arg[5]);
+
+    for (int j = 0; j < 10; j++) {
+        double ag = 0.0;
+        ag = SEC_TO_RAD(1.0)*arg[0] + w_l[j]*arg[1] + w_l_p[j]*arg[2] + w_F[j]*arg[3]
+             + w_D[j]*arg[4] + w_Omega[j]*arg[5];
+        ag = RAD_TO_SEC(ag);
+        ag = DMOD(ag, 2.0*M_PI);
+        X += xp_sin[j] * SEC_TO_RAD(sin(ag)) + xp_cos[j] * SEC_TO_RAD(cos(ag));
+        Y += yp_sin[j] * SEC_TO_RAD(sin(ag)) + yp_cos[j] * SEC_TO_RAD(cos(ag));
+    }
+
+    psEarthPole *pole = psEarthPoleAlloc();
+    pole->x = X;
+    pole->y = Y;
+    //The value of s is simply: s = -4.7e-5 * t as specified by the ADD and IERS 32.
+    pole->s = -SEC_TO_RAD(4.7e-5) * t;
+
+    return pole;
+}
+
+psSphereRot* psSphereRot_ITRStoTEO(const psEarthPole* motion)
+{
+    // Check for null parameter
+    PS_ASSERT_PTR_NON_NULL(motion,NULL);
+    double A[3][3];
+    psSphereRot *out = NULL;
+
+    double x,y,s;
+    x = motion->x;
+    y = motion->y;
+    s = motion->s;
+    s = -s;
+
+    //Setup Rotation matrix.  Rotation is constructed by rotation about the X-axis by y,
+    // about the Y-axis by x, and about the Z-axis by s' (where s' = -s).
+    A[0][0] = cos(x)*cos(s);
+    A[1][0] = cos(x)*sin(s);
+    A[2][0] = -sin(x);
+    A[0][1] = sin(x)*sin(y)*cos(s) - cos(y)*sin(s);
+    A[1][1] = sin(x)*sin(y)*sin(s) + cos(y)*cos(s);
+    A[2][1] = cos(x)*sin(y);
+    A[0][2] = sin(x)*cos(y)*cos(s) + sin(y)*sin(s);
+    A[1][2] = sin(x)*cos(y)*sin(s) - sin(y)*cos(s);
+    A[2][2] = cos(x)*cos(y);
+
+    //Convert rotation matrix to quaternions
+    out = rotMatrix_To_Quat(A);
+
+    return out;
+}
+
+psSphereRot *psSpherePrecess(const psTime *fromTime,
+                             const psTime *toTime,
+                             psPrecessMethod mode)
+{
+    // Check input for NULL pointers
+    if (fromTime == NULL && toTime == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Invalid time inputs.  fromTime & toTime cannot both be NULL.\n");
+        return NULL;
+    }
+    PS_ASSERT_INT_WITHIN_RANGE(mode, PS_PRECESS_ROUGH, PS_PRECESS_IAU2000A, NULL);
+    psF64 fromMJD, toMJD;
+    // Calculate Julian centuries
+    //If either input time is NULL, assume it to be J2000 -> from SDRS as of rev 18
+    psTime *from = NULL;
+    psTime *to = NULL;
+    if (fromTime == NULL) {
+        fromMJD = MJD_2000;
+        from = psTimeFromMJD(fromMJD);
+    } else {
+        fromMJD = psTimeToMJD(fromTime);
+        from = psTimeCopy(fromTime);
+    }
+    if (toTime == NULL) {
+        toMJD = MJD_2000;
+        to = psTimeFromMJD(toMJD);
+    } else {
+        toMJD = psTimeToMJD(toTime);
+        to = psTimeCopy(toTime);
+    }
+    if (fromMJD > toMJD) {
+        psWarning("From time is later than to time in psSpherePrecess.\n");
+    }
+
+    if (mode == PS_PRECESS_ROUGH) {
+        //For PS_PRECESS_ROUGH, no time/earthpole corrections are used.  This is the
+        //lowest level of detail mode.
+        psF64 T = (toMJD - fromMJD) / JULIAN_CENTURY;
+
+        // Calculate conversion constants
+        //    psF64 alphaP = DEG_TO_RAD(90.0) - ((DEG_TO_RAD(0.6406161) * T) +
+        psF64 alphaP = DEG_TO_RAD(180.0) + ((DEG_TO_RAD(0.6406161) * T) +
+                                            (DEG_TO_RAD(0.0000839) * T * T) +
+                                            (DEG_TO_RAD(0.000005) * T * T * T));
+
+        psF64 deltaP = (DEG_TO_RAD(0.5567530) * T) -
+                       (DEG_TO_RAD(0.0001185) * T * T) -
+                       (DEG_TO_RAD(0.0000116) * T * T * T);
+
+        //    psF64 phiP = DEG_TO_RAD(90.0) + ((DEG_TO_RAD(0.6406161) * T) +
+        psF64 phiP = DEG_TO_RAD(180.0) + ((DEG_TO_RAD(0.6406161) * T) +
+                                          (DEG_TO_RAD(0.0003041) * T * T) +
+                                          (DEG_TO_RAD(0.0000051) * T * T * T));
+
+        // Create transform with proper constants
+        psSphereRot* tmpST = psSphereRotAlloc(alphaP, deltaP, phiP);
+        psFree(from);
+        psFree(to);
+        return tmpST;
+    } else if (mode == PS_PRECESS_IAU2000A) {
+        //For IAU2000A mode, run psEOC_PrecessionModel and then psSphereRot_CEOtoGCRS for
+        //each time.  Then difference the resulting rotations by adding the inverse
+        //rotation corresponding to fromTime to the toTime rotation.
+
+        //Calculate the earthpoles and quaternions corresponding to each time (from, to).
+        //Combine the quaternions to produce the output psSphereRot.
+        psEarthPole *fromEP = psEOC_PrecessionModel(from);
+        psSphereRot *fromRot = psSphereRot_CEOtoGCRS(fromEP);
+        psEarthPole *toEP = psEOC_PrecessionModel(to);
+        psSphereRot *toRot = psSphereRot_CEOtoGCRS(toEP);
+        psSphereRot *fromConj = psSphereRotConjugate(NULL, fromRot);
+        psSphereRot *out = psSphereRotCombine(NULL, toRot, fromConj);
+        psFree(from);
+        psFree(to);
+        psFree(fromEP);
+        psFree(fromRot);
+        psFree(toEP);
+        psFree(toRot);
+        psFree(fromConj);
+        return out;
+    } else if (mode == PS_PRECESS_COMPLETE_A) {
+        //For PS_PRECESS_COMPLETE_A the same procedure as IAU2000A is used but with
+        //additional earthpole corrections from psEOC_PrecessionCorr.  The corrections
+        //for COMPLETE_A come from the IERS Bulletin A.
+
+        //Calculate the earthpoles and quaternions corresponding to each time (from, to).
+        //Add in the precession corrections from IERS bulletin A.
+        //Combine the quaternions to produce the output psSphereRot.
+        psEarthPole *fromEP = psEOC_PrecessionModel(from);
+        psEarthPole *fromCorr = psEOC_PrecessionCorr(from, PS_IERS_A);
+        fromEP->x += fromCorr->x;
+        fromEP->y += fromCorr->y;
+        psSphereRot *fromRot = psSphereRot_CEOtoGCRS(fromEP);
+        psEarthPole *toEP = psEOC_PrecessionModel(to);
+        psEarthPole *toCorr = psEOC_PrecessionCorr(to, PS_IERS_A);
+        toEP->x += toCorr->x;
+        toEP->y += toCorr->y;
+        psSphereRot *toRot = psSphereRot_CEOtoGCRS(toEP);
+        psSphereRot *fromConj = psSphereRotConjugate(NULL, fromRot);
+        psSphereRot *out = psSphereRotCombine(NULL, toRot, fromConj);
+        psFree(from);
+        psFree(to);
+        psFree(fromEP);
+        psFree(fromCorr);
+        psFree(fromRot);
+        psFree(toEP);
+        psFree(toCorr);
+        psFree(toRot);
+        psFree(fromConj);
+        return out;
+    } else {  //mode == PS_PRECESS_COMPLETE_B
+        //For PS_PRECESS_COMPLETE_B the same procedure as IAU2000A is used but with
+        //additional earthpole corrections from psEOC_PrecessionCorr.  The corrections
+        //for COMPLETE_B come from the IERS Bulletin B.
+
+        //Calculate the earthpoles and quaternions corresponding to each time (from, to).
+        //Add in the precession corrections from IERS bulletin B.
+        //Combine the quaternions to produce the output psSphereRot.
+        psEarthPole *fromEP = psEOC_PrecessionModel(from);
+        psEarthPole *fromCorr = psEOC_PrecessionCorr(from, PS_IERS_B);
+        fromEP->x += fromCorr->x;
+        fromEP->y += fromCorr->y;
+        psSphereRot *fromRot = psSphereRot_CEOtoGCRS(fromEP);
+        psEarthPole *toEP = psEOC_PrecessionModel(to);
+        psEarthPole *toCorr = psEOC_PrecessionCorr(to, PS_IERS_B);
+        toEP->x += toCorr->x;
+        toEP->y += toCorr->y;
+        psSphereRot *toRot = psSphereRot_CEOtoGCRS(toEP);
+        psSphereRot *fromConj = psSphereRotConjugate(NULL, fromRot);
+        psSphereRot *out = psSphereRotCombine(NULL, toRot, fromConj);
+        psFree(from);
+        psFree(to);
+        psFree(fromEP);
+        psFree(fromCorr);
+        psFree(fromRot);
+        psFree(toEP);
+        psFree(toCorr);
+        psFree(toRot);
+        psFree(fromConj);
+        return out;
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/psEarthOrientation.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/psEarthOrientation.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/psEarthOrientation.h	(revision 22322)
@@ -0,0 +1,201 @@
+/** @file  psEarthOrientation.h
+*
+*  @brief earth orientation calculations and transformation
+*
+*  @author Josh Hoblitt, IfA
+*  @author Ed Pier, IfA
+*  @author Eugene Magnier, IfA
+*  @author Robert Daniel DeSonia, MHPCC
+*
+*  @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-08-09 01:40:07 $
+*
+*  Copyright 2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_EARTH_ORIENTATION
+#define PS_EARTH_ORIENTATION
+
+/// @addtogroup Astro Astronomy
+/// @{
+
+#include "psCoord.h"
+#include "psTime.h"
+#include "psSphereOps.h"
+
+/** Structure for respresenting the Earth's pole at any moment or determine velocity
+ *  at the pole.  This structure carries the polar coordinate information.       */
+typedef struct
+{
+    double x;                          ///< X component of the earth's pole
+    double y;                          ///< Y component of the earth's pole
+    double s;                          ///< s component of the earth's pole
+}
+psEarthPole;
+
+/** Method for spherical precession used to specify the level of detail used in
+ *  calculation.
+ *
+ *  @see psSpherePrecess
+ *
+ */
+typedef enum {
+    PS_PRECESS_ROUGH,                  ///< roughest, lowest level of detail
+    PS_PRECESS_COMPLETE_A,             ///< complete level of detail using IERS A
+    PS_PRECESS_COMPLETE_B,             ///< complete level of detail using IERS B
+    PS_PRECESS_IAU2000A                ///< highest level of detail
+}
+psPrecessMethod;
+
+/** Initialize EOC.
+ *
+ *  Parses IERS table data and creates the polynomials used by certain EOC functions.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool p_psEOCInit(void);
+
+/** Finalize EOC after using functions that make calls to eocInit for time table data
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool p_psEOCFinalize(void);
+
+/** Allocates a new psEarthPole structure.  */
+psEarthPole *psEarthPoleAlloc(void) PS_ATTR_MALLOC;
+
+/** Calculates the apparent position of a star, given its actual position and the
+ *  velocity vector of the observer.
+ *
+ *  The actual and apparent positions are represented as psSphere entries, as is the
+ *  direction of motion.  The speed in that direction is given in units of the speed
+ *  of light.  If the value of apparent is NULL, a new psSphere is allocated, otherwise
+ *  the point to apparent is used for the result.
+ *
+ *  @return psSphere*:      the actual position of a star.
+ */
+psSphere *psAberration(
+    psSphere *apparent,                ///< apparent position of star
+    const psSphere *actual,            ///< actual position of star
+    const psSphere *direction,         ///< direction of motion of observer
+    double speed                       ///< speed of motion of observer
+);
+
+/** Calculates the apparent position of a star, given its actual position and the
+ *  position of the sun.
+ *
+ *  The actual and apparent positions are represented as psSphere entries, as is
+ *  position of the sun.  If the value of apparent is NULL, a new psSphere is allocated,
+ *  otherwise the point to apparent is used for the result.
+ *
+ *  @return psSphere*:      the apparent position of a star.
+ */
+psSphere *psGravityDeflection(
+    psSphere *apparent,                ///< apparent position of star
+    psSphere *actual,                  ///< actual position of star
+    psSphere *sunPos                      ///< position of the sun
+);
+
+/** Calculates the components of the rotation between the CEO and GCRS frames, X, Y,
+ *  and s, using the IAU2000A precession & nutation model.
+ *
+ *  The input time may be represented in any format other than UT1.  This routine must
+ *  give results identical to the IERS XYS2000A subroutine.
+ *
+ *  @return psEarthPole*:       the calculated components of the rotation.
+ */
+psEarthPole *psEOC_PrecessionModel(
+    const psTime *time                 ///< specified time
+);
+
+/** Provides interpolated corrections to the X and Y components of the polar
+ *  coordinates from the tables provided by the IERS, just as it does for UT1 and
+ *  polar motion.
+ *
+ *  @return psEarthPole*:       interpolated corrections to the precession components.
+ */
+psEarthPole *psEOC_PrecessionCorr(
+    const psTime *time,                ///< specified time
+    psTimeBulletin bulletin            ///< IERS tables for polar coordinate components.
+);
+
+/** Constructs the spherical rotation for transforming from CEO to GCRS coordinates.
+ *
+ *  The resulting psSphereRot may be used to determine the rotation from CIP/CEO to
+ *  GCRS.  This function must give results identical to the IERS BPN2000.
+ *
+ *  @return psSphereRot*:       spherical rotation for CEO to GCRS transformation.
+ */
+psSphereRot *psSphereRot_CEOtoGCRS(
+    const psEarthPole *pole            ///< input coordinates to transform
+);
+
+/** Calculates the rotation of the Earth about the CIP.
+ *
+ *  If tidalCorr is non-NULL, use the S-component to provide tidal corrections to the
+ *  UT1 time.  If tidalCorr is NULL, no corrections are made & this step is skipped.
+ *
+ *  @return psSphereRot*:       spherical rotation of the Earth about the CIP.
+ */
+psSphereRot *psSphereRot_TEOtoCEO(
+    const psTime *time,                ///< specified time
+    psEarthPole *tidalCorr             ///< UT1 polar tide correction or NULL
+);
+
+/** Provides interpolated values of the polar motion components extracted from the
+ *  IERS tables.
+ *
+ *  @return psEarthPole*:       interpolated polar motion components.
+ */
+psEarthPole *psEOC_GetPolarMotion(
+    const psTime *time,                ///< specified time
+    psTimeBulletin bulletin            ///< IERS tables for polar coordinate components.
+);
+
+/** Provides tidal corrections to the polar motion components using the Ray model
+ *  of Simon et al.
+ *
+ *  @return psEarthPole*:       corrected polar motion components.
+ */
+psEarthPole *psEOC_PolarTideCorr(
+    const psTime *time                 ///< specified time
+);
+
+/** Provides the additional corrections due to nutation terms with periods less than
+ *  or equal to two days, as well as the correction to the s-prime component of polar
+ *  motion.
+ *
+ *  @return psEarthPole*:       corrected polar motion components.
+ */
+psEarthPole *psEOC_NutationCorr(
+    psTime *time                       ///< specified time
+);
+
+/** Converts the polar motion corrections to a spherical rotation.
+ *
+ *  This function should give identical results to the IERS POM2000 subroutine.
+ *
+ *  @return psSphereRot*:       ITRS to TEO sphere rotation.
+ */
+psSphereRot *psSphereRot_ITRStoTEO(
+    const psEarthPole *motion          ///< corrected polar motion components
+);
+
+/** Generates the complete spherical rotation to account for precession
+ *  between two times.  The equinoxes shall be Julian equinoxes.
+ *
+ *  If NULL is provided for either time, it is assumed to have the reference
+ *  equinox value of J2000.  The mode argument is used to specify the level of
+ *  detail used in the calculation.
+ *
+ *  @return psSphere*:       the resulting spherical rotation
+ */
+psSphereRot* psSpherePrecess(
+    const psTime *fromTime,            ///< equinox of coords input
+    const psTime *toTime,              ///< equinox of coords output
+    psPrecessMethod mode               ///< level of detail to use
+);
+
+
+/// @}
+#endif // #ifndef PS_EARTH_ORIENTATION
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/psSphereOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/psSphereOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/psSphereOps.c	(revision 22322)
@@ -0,0 +1,473 @@
+/** @file  psSphereOps.c
+ *
+ *  @brief Contains spherical rotation and offset operations
+ *
+ *  @ingroup CoordinateTransform
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Dave Robbins, MHPCC
+ *
+ *  @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-01-09 22:38:52 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>
+#include <float.h>
+
+#include "psSphereOps.h"
+#include "psType.h"
+#include "psCoord.h"
+#include "psMemory.h"
+#include "psTime.h"
+#include "psAssert.h"
+#include "psError.h"
+#include "psLogMsg.h"
+
+
+
+// Modified Julian Day 01/01/1900 00:00:00
+#define MJD_1900 15021.0
+
+// Days in Julian century
+#define JULIAN_CENTURY 36525.0
+
+static void sphereRotFree(psSphereRot *result)
+{
+    // There are non dynamic allocated items
+}
+
+static psSphereRot* sphereRotAlloc(void)
+{
+    psSphereRot *sphereRot = (psSphereRot* ) psAlloc(sizeof(psSphereRot));
+    psMemSetDeallocator(sphereRot, (psFreeFunc)sphereRotFree);
+    return (sphereRot);
+}
+
+psSphereRot* psSphereRotAlloc(double alphaP,
+                              double deltaP,
+                              double phiP)
+{
+    if (isnan(alphaP) || isnan(deltaP) || isnan(phiP) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Input angles cannot be NaN in psSphereRotAlloc.");
+        return NULL;
+    }
+    psSphereRot *r = sphereRotAlloc();
+    psSphereRot *s = sphereRotAlloc();
+    psSphereRot *t = sphereRotAlloc();
+
+    //The following represents: r =a rotation about the z-axis by alphaP, s =a rotation about
+    // the y-axis by deltaP, and t =a rotation about the z-axis by phiP.
+    r->q0=0;
+    r->q1=0;
+    r->q2=sin(alphaP/2.0);
+    r->q3=cos(alphaP/2.0);
+
+    s->q0=0;
+    s->q1=sin(deltaP/2.0);
+    s->q2=0;
+    s->q3=cos(deltaP/2.0);
+
+    t->q0=0;
+    t->q1=0;
+    t->q2=sin(phiP/2.0);
+    t->q3=cos(phiP/2.0);
+
+    // calculate t*s*r.
+    psSphereRot* temp = psSphereRotCombine(NULL,t,s);
+    psSphereRot* result = psSphereRotCombine(NULL, temp, r);
+    psFree(temp);
+    psFree(r);
+    psFree(s);
+    psFree(t);
+    return result;
+}
+
+bool psMemCheckSphereRot(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    //See if the ptr corresponds to a psSphereRot*
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)sphereRotFree );
+}
+
+//This function is really a second allocate function for psSphereRot's that uses quaternion
+// component inputs instead of angle inputs as in psSphereRotAlloc.
+psSphereRot* psSphereRotQuat(double q0,
+                             double q1,
+                             double q2,
+                             double q3)
+{
+    if (isnan(q0) || isnan(q1) || isnan(q2) || isnan(q3) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Input quaternions cannot be NaN in psSphereRotQuat.");
+        return NULL;
+    }
+    //allocate space for a new sphere rotation and set deallocator
+    psSphereRot* rot = sphereRotAlloc();
+
+    //The magnitude of a rotation quaternion should = 1 so we normalize here in case the
+    // inputs have not been.
+    double len = sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
+    rot->q0 = q0 / len;
+    rot->q1 = q1 / len;
+    rot->q2 = q2 / len;
+    rot->q3 = q3 / len;
+
+    return rot;
+}
+
+psSphereRot* psSphereRotConjugate(psSphereRot *out,
+                                  const psSphereRot *in)
+{
+    //if input sphere rotation is NULL, return NULL
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "psSphereRot input cannot be NULL.\n");
+        return NULL;
+    }
+    //if output sphere rotation is NULL, allocate a new sphere rotation to return
+    if (out == NULL) {
+        out = sphereRotAlloc();
+    }
+    //Since q3 is the magnitude and q0,q1,q2 are direction, the conjugate rotation
+    // is formed by -q0,-q1,-q2,+q3.  Note that this is the same as q0,q1,q2,-q3.
+    out->q0 = -in->q0;
+    out->q1 = -in->q1;
+    out->q2 = -in->q2;
+    out->q3 = in->q3;
+
+    return out;
+}
+
+psSphere* psSphereRotApply(psSphere* out,
+                           const psSphereRot* transform,
+                           const psSphere* coord)
+{
+    //Make sure that input coordinates and rotation are not NULL
+    PS_ASSERT_PTR_NON_NULL(transform, NULL);
+    PS_ASSERT_PTR_NON_NULL(coord, NULL);
+    //If output coordinates is NULL allocate a new psSphere
+    if (out == NULL) {
+        out = psSphereAlloc();
+    }
+    //apply the transform by creating a new psSphereRot from the input coord
+    // and combining it with the input transform (see ADD)
+    double cosD = cos(coord->d);
+    psSphereRot* coordQuat = psSphereRotQuat(
+                                 cosD*cos(coord->r),
+                                 cosD*sin(coord->r),
+                                 sin(coord->d),
+                                 0.0);
+    //Get the conjugate of the input rotation.  Need to calculate r*p*R to apply rotation
+    // where R = r-conjugate.
+    psSphereRot *conjugate = psSphereRotConjugate(NULL, transform);
+    psSphereRot *temp = psSphereRotCombine(NULL, transform, coordQuat);
+    psSphereRot *result = psSphereRotCombine(NULL, temp, conjugate);
+    //From ADD we can find the new sphere coordinates,
+    // r is calculated as tan^-1 (q1/q0), d is sin^-1 (q2)
+    out->r = atan2(result->q1, result->q0);
+    out->d = asin(result->q2);
+    out->rErr = 0.0;
+    out->dErr = 0.0;
+    //Simply for convention, we make sure all output r-parameters are positive in the range
+    // of 0 to 2pi
+    if (out->r < -0.000001) {
+        out->r += 2.0 * M_PI;
+    }
+
+    psFree(conjugate);
+    psFree(temp);
+    psFree(result);
+    psFree(coordQuat);
+    return out;
+}
+
+psSphereRot* psSphereRotCombine(psSphereRot* out,
+                                const psSphereRot* rot1,
+                                const psSphereRot* rot2)
+{
+    //Make sure that input rotations are not NULL
+    PS_ASSERT_PTR_NON_NULL(rot1, NULL);
+    PS_ASSERT_PTR_NON_NULL(rot2, NULL);
+    //If output rotation is NULL, allocate a new psSphereRot to return
+    if (out == NULL) {
+        out = sphereRotAlloc();
+    }
+
+    double a0 = rot1->q0;
+    double a1 = rot1->q1;
+    double a2 = rot1->q2;
+    double a3 = rot1->q3;
+    double b0 = rot2->q0;
+    double b1 = rot2->q1;
+    double b2 = rot2->q2;
+    double b3 = rot2->q3;
+
+    //Combine rot1 & rot2.  Formulas here came from ADD.
+    out->q0 = a3*b0 + a0*b3 + a1*b2 - a2*b1;
+    out->q1 = a3*b1 - a0*b2 + a1*b3 + a2*b0;
+    out->q2 = a3*b2 + a0*b1 - a1*b0 + a2*b3;
+    out->q3 = b3*a3 - b2*a2 - b1*a1 - b0*a0;
+
+    return out;
+}
+
+psSphereRot *psSphereRotInvert(double alphaP,
+                               double deltaP,
+                               double phiP)
+{
+    //This function should produce identical results to psSphereRotConjugate in most
+    // or possibly all situations.  Creates the inverse rotation from the input angles.
+    return (psSphereRotAlloc(-phiP, -deltaP, -alphaP));
+}
+
+psSphereRot* psSphereRotEclipticToICRS(const psTime *time)
+{
+    psF64 T;
+    // Check for null parameter
+    PS_ASSERT_PTR_NON_NULL(time, NULL);
+    // Convert psTime to MJD
+    psF64 MJD = psTimeToMJD(time);
+    // Check the specified MJD is greater than 1900
+    if ( MJD < MJD_1900 ) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE,true,_("Specified time is less than 1900."));
+        return NULL;
+    }
+    // Calculate number of Julian centuries since 1900
+    T = ( MJD - MJD_1900 ) / JULIAN_CENTURY;
+    //Formulas for phiP, deltaP, alphaP came from ADD.
+    psF64 phiP = - DEG_TO_RAD(270.0);
+    psF64 deltaP = - (DEG_TO_RAD(23.0) +
+                      MIN_TO_RAD(27.0) +
+                      SEC_TO_RAD(8.26) -
+                      (SEC_TO_RAD(46.845) * T) -
+                      (SEC_TO_RAD(0.0059) * T * T) +
+                      (SEC_TO_RAD(0.00181) * T * T * T));
+    psF64 alphaP = - DEG_TO_RAD(90.0);
+
+    return (psSphereRotAlloc(alphaP, deltaP, phiP));
+}
+
+psSphereRot* psSphereRotICRSToEcliptic(const psTime *time)
+{
+    psF64 T;
+    // Check for null parameter
+    PS_ASSERT_PTR_NON_NULL(time, NULL);
+    // Convert psTime to MJD
+    psF64 MJD = psTimeToMJD(time);
+    // Check the specified MJD is greater than 1900
+    if ( MJD < MJD_1900 ) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE,true,_("Specified time is less than 1900."));
+        return NULL;
+    }
+    // Calculate number of Julian centuries since 1900
+    T = ( MJD - MJD_1900 ) / JULIAN_CENTURY;
+
+    //Formulas for phiP, deltaP, alphaP came from ADD.  Notice that the formulas are the
+    // same as for EclipticToICRS except that alpha=-phiP, deltaP=-deltaP, phiP=-alphaP to
+    // produce the inverse rotation as in psSphereRotInverse.
+    psF64 alphaP = DEG_TO_RAD(270.0);
+    psF64 deltaP = DEG_TO_RAD(23.0) +
+                   MIN_TO_RAD(27.0) +
+                   SEC_TO_RAD(8.26) -
+                   (SEC_TO_RAD(46.845) * T) -
+                   (SEC_TO_RAD(0.0059) * T * T) +
+                   (SEC_TO_RAD(0.00181) * T * T * T);
+    psF64 phiP = DEG_TO_RAD(90.0);
+
+    return (psSphereRotAlloc(alphaP, deltaP, phiP));
+}
+
+psSphereRot* psSphereRotGalacticToICRS(void)
+{
+    //Formulas for alphaP, deltaP, phiP came from the ADD for ICRSToGalactic.  Notice that
+    // this is the reason the inverse gets allocated and returned here.
+    psF64 alphaP = DEG_TO_RAD(180.0-192.85948);
+    psF64 deltaP = DEG_TO_RAD(90.0 - 27.12825);
+    psF64 phiP = DEG_TO_RAD(90.0+32.93192);
+    return (psSphereRotAlloc(-phiP,-deltaP,-alphaP));
+}
+
+psSphereRot* psSphereRotICRSToGalactic(void)
+{
+    //Formulas for alphaP, deltaP, phiP came from ADD.
+    psF64 alphaP = DEG_TO_RAD(180.0-192.85948);
+    psF64 deltaP = DEG_TO_RAD(90.0 - 27.12825);
+    psF64 phiP = DEG_TO_RAD(90.0+32.93192);
+
+    return (psSphereRotAlloc(alphaP, deltaP, phiP));
+}
+
+//Calculates the difference between coordinates in position1 & 2 and returns this offset.
+psSphere* psSphereGetOffset(const psSphere* position1,
+                            const psSphere* position2,
+                            psSphereOffsetMode mode,
+                            psSphereOffsetUnit unit)
+{
+    //Make sure that input coordinates are not NULL & that mode & unit are valid
+    PS_ASSERT_PTR_NON_NULL(position1, NULL);
+    PS_ASSERT_PTR_NON_NULL(position2, NULL);
+    // Check positions near 90 degree and issue warnings if necessary
+    if (position1->d >= DEG_TO_RAD(90.0)) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: psDeproject(): position1->d is larger than 90 degrees.  Returning NULL.");
+        return NULL;
+    }
+    if (position2->d >= DEG_TO_RAD(90.0)) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: psDeproject(): position2->d is larger than 90 degrees.  Returning NULL.");
+        return NULL;
+    }
+    // Allocate return structure
+    psSphere* tmp = psSphereAlloc();
+
+    // Mode is LINEAR - Use first position as projection center and project second point
+    // onto tangent plane, set point projected into psSphere structure x->r y->d
+    if (mode == PS_LINEAR) {
+        //The basic idea is to project both positions onto the linear plane, with position1
+        // at the center, then calculate the linear offset between those projections.
+        psProjection* proj = psProjectionAlloc(position1->r,
+                                               position1->d,
+                                               1.0,
+                                               1.0,
+                                               PS_PROJ_TAN);
+        // Perform projection onto tangent plane
+        psPlane* lin = psProject(NULL, position2, proj);
+        // Set return values
+        tmp->r = lin->x;
+        tmp->d = lin->y;
+        // Free data structures allocated
+        psFree(proj);
+        psFree(lin);
+
+        // Mode is SPHERICAL - Get difference between positiion 1 and position 2 and convert
+        // offset value from radians to desired units and return
+    } else if (mode == PS_SPHERICAL) {
+        tmp->r = position2->r - position1->r;
+        tmp->d = position2->d - position1->d;
+
+        // Wrap these to an acceptable range.  This assumes that all
+        // angles are in radians.
+        tmp->r = fmod(tmp->r, 2*M_PI);
+        tmp->d = fmod(tmp->d, 2*M_PI);
+        tmp->rErr = 0.0;
+        tmp->dErr = 0.0;
+
+        // Convert output to desired units
+        if (unit == PS_ARCSEC) {
+            tmp->r = RAD_TO_SEC(tmp->r);
+            tmp->d = RAD_TO_SEC(tmp->d);
+        } else if (unit == PS_ARCMIN) {
+            tmp->r = RAD_TO_MIN(tmp->r);
+            tmp->d = RAD_TO_MIN(tmp->d);
+        } else if (unit == PS_DEGREE) {
+            tmp->r = RAD_TO_DEG(tmp->r);
+            tmp->d = RAD_TO_DEG(tmp->d);
+        } else if (unit == PS_RADIAN) {}
+        else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Specified units, 0x%x, is not supported."),
+                    unit);
+            psFree(tmp);
+            return NULL;
+        }
+
+        // Invalid mode
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified offset mode, 0x%x, is not supported."),
+                mode);
+        psFree(tmp);
+        return NULL;
+    }
+
+    // Return value
+    return tmp;
+}
+
+//Applies the specified offset to the input position coordinates and returns the new position.
+psSphere* psSphereSetOffset(const psSphere* position,
+                            const psSphere* offset,
+                            psSphereOffsetMode mode,
+                            psSphereOffsetUnit unit)
+{
+    //Make sure that input coordinates are not NULL & that mode & unit are valid
+    PS_ASSERT_PTR_NON_NULL(position, NULL);
+    PS_ASSERT_PTR_NON_NULL(offset, NULL);
+
+    psSphere* tmp;
+    psF64 tmpR = 0.0;
+    psF64 tmpD = 0.0;
+
+    // If mode is linear then set position to projection center
+    // and offset to linear coordinate then deproject to obtain
+    // new sphere coordinate
+    if (mode == PS_LINEAR) {
+        // Allocate plane coordinate and set coordinate
+        psPlane*  lin = psPlaneAlloc();
+        lin->x = offset->r;
+        lin->y = offset->d;
+        // Allocate and set projection structure
+        psProjection* proj = psProjectionAlloc(position->r,
+                                               position->d,
+                                               1.0,
+                                               1.0,
+                                               PS_PROJ_TAN);
+        // Project tangent plane coord to spherical coord
+        tmp = psDeproject(NULL, lin, proj);
+        // Free data structures used
+        psFree(proj);
+        psFree(lin);
+
+        // If mode is spherical then convert offset to radians, add the offset
+        // to the position and wrap to 0 to 2pi
+    } else if (mode == PS_SPHERICAL) {
+        // Convert offset unit to radians
+        if (unit == PS_ARCSEC) {
+            tmpR = SEC_TO_RAD(offset->r);
+            tmpD = SEC_TO_RAD(offset->d);
+        } else if (unit == PS_ARCMIN) {
+            tmpR = MIN_TO_RAD(offset->r);
+            tmpD = MIN_TO_RAD(offset->d);
+        } else if (unit == PS_DEGREE) {
+            tmpR = DEG_TO_RAD(offset->r);
+            tmpD = DEG_TO_RAD(offset->d);
+        } else if (unit == PS_RADIAN) {
+            tmpR = offset->r;
+            tmpD = offset->d;
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Specified units, 0x%x, is not supported."),
+                    unit);
+            return NULL;
+        }
+
+        // Allocate sphere structure to return
+        tmp = psSphereAlloc();
+
+        // Add offset and wrap to 0 to 2PI if necessary
+        tmp->r = position->r + tmpR;
+        tmp->r = fmod(tmp->r, 2.0*M_PI);
+        tmp->d = position->d + tmpD;
+        tmp->d = fmod(tmp->d, 2.0*M_PI);
+        tmp->rErr = 0.0;
+        tmp->dErr = 0.0;
+
+        // Invalid mode report error
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified offset mode, 0x%x, is not supported."),
+                mode);
+        return NULL;
+    }
+
+    return tmp;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/psSphereOps.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/psSphereOps.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/psSphereOps.h	(revision 22322)
@@ -0,0 +1,217 @@
+/** @file  psSphereOps.h
+ *
+ *  @brief Contains spherical rotation and offset operations
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author David Robbins, MHPCC
+ *
+ *  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-08-09 01:40:07 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_SPHERE_H
+#define PS_SPHERE_H
+
+/// @addtogroup Astro Astronomy
+/// @{
+
+# include "psTime.h"
+
+/** Spherical Rotation Definition
+ *
+ *  We need to be able to convert between ICRS, Galactic and Ecliptic
+ *  coordinates, and potentially between arbitrary spherical coordinate
+ *  systems. All of these basic spherical transformations represent rotations
+ *  of the spherical coordinate reference.
+ *
+ */
+typedef struct
+{
+    double q0;
+    double q1;
+    double q2;
+    double q3;
+}
+psSphereRot;
+
+/** Mode for Offset calculation between two sky positions
+ *
+ *  @see  psSphereGetOffset, psSphereSetOffset
+ *
+ */
+typedef enum {
+    PS_SPHERICAL,                      ///< offset corresponds to an angular offset
+    PS_LINEAR                          ///< offset corresponds to a linear offset
+} psSphereOffsetMode;
+
+/** The units of the offset
+ *
+ *  @see  psSphereGetOffset, psSphereSetOffset
+ *
+ */
+typedef enum {
+    PS_ARCSEC,                         ///< Arcseconds
+    PS_ARCMIN,                         ///< Arcminutes
+    PS_DEGREE,                         ///< Degrees
+    PS_RADIAN                          ///< Radians
+} psSphereOffsetUnit;
+
+
+/** Allocator for psSphereRot
+ *
+ *  @return psSphereRot*         newly allocated psSphereRot
+ */
+
+psSphereRot* psSphereRotAlloc(
+    double alphaP,                     ///< north pole latitude
+    double deltaP,                     ///< north pole longitude
+    double phiP                        ///< defines the longitude in the input system of the equatorial intersection between the two systems (e.g, the first point of Ares).
+) PS_ATTR_MALLOC;
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psSphereRot structure, false otherwise.
+ */
+bool psMemCheckSphereRot(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Allocator for psSphereRot given quaternions.
+ *
+ *  Normalizes a given quaternion and returns the cooresponding newly allocated
+ *  psSphereRot object.
+ *
+ *  @return psSphereRot*         newly allocated psSphereRot
+ */
+psSphereRot* psSphereRotQuat(
+    double q0,                          ///< q0
+    double q1,                          ///< q1
+    double q2,                          ///< q2
+    double q3                           ///< q3
+);
+
+/** Applies the psSphereTransform transform for a specified coordinate
+ *
+ *  @return psSphere*      resulting coordinate based on transform
+ */
+psSphere* psSphereRotApply(
+    psSphere* out,                     ///< a psSphere to recycle.  If NULL, a new one is generated.
+    const psSphereRot* transform,      ///< the transform to apply
+    const psSphere* coord              ///< the coordinate to apply the transform above.x
+);
+
+/** Combines two rotations.
+ *
+ *  Combines two rotations to produce a single rotation which is the
+ *  equivalent of applying the ï¬rst rotation and then the second. The output
+ *  rotation may be supplied, or will be allocated if NULL.
+ *
+ *  @return psSphereRot*    New psSphereRot that is the combination of the input psSphereRots
+ */
+psSphereRot* psSphereRotCombine(
+    psSphereRot* out,                  ///< a psSphereRot to recycle or NULL
+    const psSphereRot* rot1,           ///< first rotation to combine
+    const psSphereRot* rot2            ///< second rotation to combine
+);
+
+/** Returns the conjugate of a specified rotation.
+ *
+ *  Stores the conjugate of a specified rotation in an existing psSphereRot* or creates a
+ *  new psSphereRot* if NULL.  The conjugate of a rotation given in quaternions is -q0,
+ *  -q1, -q2, q3.
+ *
+ *  @return psSphereRot*    the Conjugate of the specified psSphereRot, in.
+ */
+psSphereRot* psSphereRotConjugate(
+    psSphereRot *out,                  ///< a psSphereRot to recycle or NULL
+    const psSphereRot *in              ///< the psSphereRot from which to obtain the conjugate
+);
+
+/** Inverts the rotation.
+ *
+ *  A given rotation is inverted by creating a psSphereRot using -phiP, -deltaP, -alphaP.
+ *
+ *  @return psSphereRot*    Inverted input psSphereRot
+ */
+psSphereRot* psSphereRotInvert(
+    double alphaP,                      ///< north pole latitude
+    double deltaP,                      ///< north pole longitude
+    double phiP                         ///< defines the longitude in the input system of the equatorial intersection between the two systems (e.g, the first point of Ares).
+);
+
+/** Determines the offset (RA,Dec) on the sky between two positions.
+ *
+ *  Both an offset mode and an offset unit may be defined. The mode may be
+ *  either PS_SPHERICAL, in which case the specified offset corresponds to an
+ *  offset in angles, or it may be PS_LINEAR, in which case the offset
+ *  corresponds to a linear offset in a local projection. The offset unit may
+ *  be in one of PS_ARCSEC, PS_ARCMIN, PS_DEGREE, and PS_RADIAN, which
+ *  specifies the units of the offset only.
+ *
+ *  @return psSphere*        the offset between position1 and position2
+ */
+psSphere* psSphereGetOffset(
+    const psSphere* position1,         ///< first position for calculating offset
+    const psSphere* position2,         ///< second position for calculating offset
+    psSphereOffsetMode mode,           ///< type of offset can be PS_SPHERICAL or PS_LINEAR
+    psSphereOffsetUnit unit            ///< specifies the units of offset only
+);
+
+/** Applies the given offset to a coordinate.
+ *
+ *  Both an offset mode and an offset unit may be defined. The mode may be
+ *  either PS_SPHERICAL, in which case the specified offset corresponds to an
+ *  offset in angles, or it may be PS_LINEAR, in which case the offset
+ *  corresponds to a linear offset in a local projection. The offset unit may
+ *  be in one of PS_ARCSEC, PS_ARCMIN, PS_DEGREE, and PS_RADIAN, which
+ *  specifies the units of the offset only.
+ *
+ *  @return psSphere*              the original position with the given offset applied.
+ */
+psSphere* psSphereSetOffset(
+    const psSphere* position,          ///< coordinate of origin
+    const psSphere* offset,            ///< coordinate of offset to apply
+    psSphereOffsetMode mode,           ///< corresponds to an offset in angles or local projection
+    psSphereOffsetUnit unit            ///< specifies the units of offset only
+);
+
+/** Creates the appropriate transform for converting from ICRS to Ecliptic
+ *  coordinate systems.
+ *
+ *  @return psSphereRot*     transform for ICRS->Ecliptic coordinate systems
+ */
+psSphereRot* psSphereRotICRSToEcliptic(
+    const psTime *time                 ///< the time for which the resulting transform will be valid
+);
+
+/** Creates the appropriate transform for converting from Ecliptic to ICRS
+ *  coordinate systems.
+ *
+ *  @return psSphereRot*     transform for Ecliptic->ICRS coordinate systems
+ */
+psSphereRot* psSphereRotEclipticToICRS(
+    const psTime *time                 ///< the time for which the resulting transform will be valid
+);
+
+/** Creates the appropriate transform for converting from ICRS to Galactic
+ *  coordinate systems.
+ *
+ *  @return psSphereRot*        new sphere rotation for ICRS to Galactic transformations.
+ */
+psSphereRot* psSphereRotICRSToGalactic(void);
+
+/** Creates the appropriate transform for converting from Galactic to ICRS
+ *  coordinate systems.
+ *
+ *  @return psSphereRot*        new sphere rotation for Galactic to ICRS transformations.
+ */
+psSphereRot* psSphereRotGalacticToICRS(void);
+
+/// @}
+
+#endif // #ifndef PS_SPHERE_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/psTime.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/psTime.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/psTime.c	(revision 22322)
@@ -0,0 +1,1987 @@
+/** @file  psTime.c
+ *
+ *  @brief Definitions for time, time utilities, and conversion functions for use with psLib astronomy
+ *  functions.
+ *
+ *  A collection of functions are required by psLib to manipulate time data. These functions primarily consist
+ *  of conversions between specific time formats.  They use the UNIX timeval time system as the
+ *  base upon which International Atomic Time (TAI) and Universal Time Coordinated (UTC) are calculated.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.116 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-24 20:04:32 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "psTime.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psTrace.h"
+#include "psMemory.h"
+#include "psAbort.h"
+#include "psImage.h"
+#include "psString.h"
+#include "psMetadata.h"
+#include "psMetadataConfig.h"
+#include "psLookupTable.h"
+#include "psAssert.h"
+
+#define MAX_STRING_LENGTH 256
+
+/** Sidereal angular conversion from seconds to radians for GMST in seconds (i.e. pi/(180*240)) */
+#define S2R (7.272205216643039903848711535369e-5)
+
+/** Two times pi with double precision accuracy */
+#define TWOPI (2.0*M_PI)
+
+/** Conversion from radians to degrees */
+#define R2DEG = (180.0/M_PI)
+
+/** Maximum length of time string */
+#define MAX_TIME_STRING_LENGTH 256
+
+/** Seconds per minute */
+#define  SEC_PER_MINUTE 60.0
+
+/** Seconds per hour */
+#define  SEC_PER_HOUR (60.0*SEC_PER_MINUTE)
+
+/** Seconds per day */
+#define  SEC_PER_DAY (24.0*SEC_PER_HOUR)
+
+/** Seconds per year */
+#define  SEC_PER_YEAR (365.0*SEC_PER_DAY)
+
+/** Microseconds per day */
+#define NSEC_PER_DAY 86400000000000.0
+
+// Time config file path
+static char *timeConfig = NULL;
+
+/** Time metadata read from config file */
+static psMetadata *timeMetadata = NULL;
+
+// Offset to convert terrestrial time(TT) to international atomic time(TAI)
+#define  TAI_TT_OFFSET_SECONDS        32
+#define  TAI_TT_OFFSET_NANOSECONDS    184000000
+
+// Offset from converting to MJD
+#define  MJD_EPOCH_OFFSET             40587.0
+
+// Offset for converting to JD
+#define  JD_EPOCH_OFFSET              2440587.5
+
+// Offset of year 0000 from epoch
+#define YEAR_0000_SEC                 -62125920000
+
+// Offset of year 9999 from epoch
+#define YEAR_9999_SEC                 253202544000
+
+/** Static function prototypes */
+static char *cleanString(char *inString, int sLen);
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status);
+static psTime* convertTimeTAIUTC(psTime* time);
+static psTime* convertTimeUTCTAI(psTime* time);
+static psTime* convertTimeTAITT(psTime* time);
+static psTime* convertTimeTTTAI(psTime* time);
+static psTime* convertTimeUTCUT1(psTime* time);
+
+static bool p_psTimeInit(const char *fileName);
+
+/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+ *  terminated copy of the original input string. */
+static char *cleanString(char *inString,
+                         int sLen)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+    ptrB = inString;
+
+    // Skip over leading # or whitespace
+    while (isspace(*ptrB) || *ptrB=='#') {
+        ptrB++;
+    }
+
+    // Skip over trailing whitespace, null terminators, and # characters
+    ptrE = inString + sLen - 1;
+    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+        ptrE--;
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    cleaned = psStringNCopy(ptrB, sLen);
+
+    return cleaned;
+}
+
+/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+ * the beginning of the string. Tokens are newly allocated null terminated strings. */
+static char* getToken(char **inString,
+                      char *delimiter,
+                      psParseErrorType *status)
+{
+    char *cleanToken = NULL;
+    int sLen = 0;
+
+    // Skip over leading whitespace
+    while(isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+
+    if(sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen);
+
+        // Move to end of token
+        //        (*inString) += (sLen+1);
+        (*inString) += sLen;
+        if (**inString != '\0' ) {
+            (*inString)++;
+        }
+
+    } else if(**inString!='\0' && sLen==0) {
+        *status = PS_PARSE_ERROR_GENERAL;
+    }
+
+    return cleanToken;
+}
+
+// get the pslib.config filename by checking environment variable first, then
+// the possiblity set config file name, then the original installation area.
+const char *p_psTimeConfigFilename(const char *filename)
+{
+    // if filename is provided, set timeConfig to this value
+    if (filename) {
+        psFree(timeConfig);
+        timeConfig = psStringCopy(filename);
+        psMemSetPersistent(timeConfig, true);
+        return timeConfig;
+    }
+
+    // check the env var first
+    const char *PS_CONFIG_FILE = getenv("PS_CONFIG_FILE");
+    if (PS_CONFIG_FILE) {
+        return PS_CONFIG_FILE;
+    }
+
+    // check timeConfig var 2nd
+    if (timeConfig) {
+        return timeConfig;
+    }
+
+    // fall back to the default, this should come from configure.ac
+    return PS_CONFIG_FILE_DEFAULT;
+}
+
+
+// Searches time tables in priority order and performs interpolation if input index value is within a table.
+// If the index value is out of range, the status is set accordingly.
+psF64 p_psTimeSearchTables(psF64 index,
+                           psU64 column,
+                           char *metadataTableNames[],
+                           psU32 nTables,
+                           psLookupStatusType* status)
+{
+    char*            tableName          = NULL;
+    psF64            result             = NAN;
+    psLookupTable*   table              = NULL;
+    psMetadataItem*  tableMetadataItem  = NULL;
+
+    // Check if psTime tables are already loaded
+    if(!p_psTimeInit(p_psTimeConfigFilename(NULL))) {
+        *status = PS_LOOKUP_ERROR;
+        return NAN;
+    }
+
+    // Search each table in priority order: daily, eopc,finals
+    for(psS32 i = 0; i < nTables; i++) {
+
+        // Get table name from list of tables to search
+        tableName = metadataTableNames[i];
+
+        // Lookup table name in time metadata
+        tableMetadataItem = psMetadataLookup(timeMetadata, tableName);
+
+        // Check if table not a metadata item
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                    tableName);
+            *status = PS_LOOKUP_ERROR;
+            return NAN;
+        }
+
+        // Get table from metadata
+        table = (psLookupTable*)tableMetadataItem->data.V;
+
+        // Check that table is not NULL
+        PS_ASSERT_PTR_NON_NULL(table,NAN);
+
+        // Check if index within to/from range
+        if(index >= table->validFrom ) {
+            if(index <= table->validTo) {
+                // Attempt to interpolate table
+                result = psLookupTableInterpolate(table, index, column);
+                *status = PS_LOOKUP_SUCCESS;
+                if(!isnan(result)) {
+                    break;
+                }
+            } else {
+                *status = PS_LOOKUP_PAST_BOTTOM;
+            }
+        } else {
+            *status = PS_LOOKUP_PAST_TOP;
+        }
+    }
+
+    return result;
+}
+
+bool p_psTimeInit(const char *fileName)
+{
+    psS32 numLines = 0;
+    bool foundTable = false;
+    char *tableDir = NULL;
+    char *tableNames = NULL;
+    char *tableFormats = NULL;
+    char *namesPtr = NULL;
+    char *formatPtr = NULL;
+    char *metadataNamesPtr = NULL;
+    char *tableName = NULL;
+    char *tableFormat = NULL;
+    char *fullTableName = NULL;
+    psS32 i = 0;
+    psS32 j = 0;
+    psS32 numTables = 0;
+    psU32 nFail = 0;
+    psVector *tablesFrom = NULL;
+    psVector *tablesTo = NULL;
+    psVector *tablesIndex = NULL;
+    psMetadataItem *metadataItem = NULL;
+    psLookupTable *table = NULL;
+    psParseErrorType status = PS_PARSE_SUCCESS;
+    char metadataTableNames[4][MAX_STRING_LENGTH] = {"daily", "eopc",  "finals", "tai"};
+
+    // Check if the p_psTimeInit has already been ran
+    if (timeMetadata != NULL) {
+        return true;
+    }
+
+    // XXX this is not thread safe as the persistence setting is global
+    const bool initialPersistence =
+        p_psMemAllocatePersistent(true); // All memory allocated below is "persistent"
+
+    // Read config file
+    timeMetadata = psMetadataConfigRead(timeMetadata, &nFail, fileName, true);
+    if(timeMetadata == NULL) {
+        return false;
+    } else if(nFail != 0) {
+        return false;
+    }
+
+    // Get number of tables
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.n");
+    if(metadataItem == NULL) {
+        p_psMemAllocatePersistent(initialPersistence);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.time.tables.n");
+        return false;
+    }
+    numTables = (psS32)metadataItem->data.S32;
+
+    // Get lower range of tables
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.from");
+    if(metadataItem == NULL) {
+        p_psMemAllocatePersistent(initialPersistence);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.time.tables.from");
+        return false;
+    }
+    tablesFrom = psVectorCopy(tablesFrom, metadataItem->data.V, PS_TYPE_F64);
+    if(tablesFrom->n != numTables) {
+        p_psMemAllocatePersistent(initialPersistence);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Incorrect vector size. Size: %ld, Expected %d."), tablesFrom->n, numTables);
+        psFree(tablesFrom);
+        return false;
+    }
+
+    // Get upper range of tables
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.to");
+    if(metadataItem == NULL) {
+        p_psMemAllocatePersistent(initialPersistence);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.time.tables.to");
+        psFree(tablesFrom);
+        return false;
+    }
+    tablesTo = psVectorCopy(tablesTo, metadataItem->data.V, PS_TYPE_F64);
+    if(tablesTo->n != numTables) {
+        p_psMemAllocatePersistent(initialPersistence);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Incorrect vector size. Size: %ld, Expected %d."), tablesTo->n, numTables);
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        return false;
+    }
+
+    // Get index columns for the tables
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.index");
+    if(metadataItem == NULL) {
+        p_psMemAllocatePersistent(initialPersistence);
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,_("Failed find '%s' in time metadata."),
+                "psLib.time.tables.index");
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        return false;
+    }
+    tablesIndex = psVectorCopy(tablesIndex, metadataItem->data.V, PS_TYPE_S32);
+    if(tablesIndex->n != numTables) {
+        p_psMemAllocatePersistent(initialPersistence);
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,_("Incorrect vector size. Size: %ld, Expected %d."),tablesIndex->n,numTables);
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        psFree(tablesIndex);
+        return false;
+    }
+
+    // Get path to time data files
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.dir");
+    if(metadataItem == NULL) {
+        p_psMemAllocatePersistent(initialPersistence);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.time.tables.dir");
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        psFree(tablesIndex);
+        return false;
+    }
+    tableDir = psStringCopy(metadataItem->data.V);
+
+    // Table file names
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.files");
+    if(metadataItem == NULL) {
+        p_psMemAllocatePersistent(initialPersistence);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                "psLib.time.tables.files");
+
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        psFree(tablesIndex);
+        psFree(tableDir);
+        return false;
+    }
+    tableNames = psStringCopy(metadataItem->data.V);
+
+    // Get table format strings
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.format");
+    if(metadataItem == NULL) {
+        p_psMemAllocatePersistent(initialPersistence);
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true, _("Failed find '%s' in time metadata."),
+                "psLib.time.tables.format");
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        psFree(tablesIndex);
+        psFree(tableDir);
+        psFree(tableNames);
+        return false;
+    }
+    tableFormats = psStringCopy(metadataItem->data.V);
+    formatPtr = tableFormats;
+
+    // Read time tables
+    bool no_problem = true;  // True if we've detected no errors
+    namesPtr = tableNames;
+    while((tableName=getToken(&namesPtr, " ", &status)) != NULL) {
+
+        // Form path with table name, adding one to length for last '/' that may not occur
+        // in string in cong file
+        fullTableName = (char*)psAlloc(strlen(tableDir)+strlen(tableName)+1+1);
+
+        // Old strings may come back from psAlloc(), so set initial position to EOL
+        fullTableName[0]='\0';
+        strcat(fullTableName, tableDir);
+        strcat(fullTableName, "/");
+        strcat(fullTableName, tableName);
+
+        // Get table format
+        tableFormat = getToken(&formatPtr,",",&status);
+        if(tableFormat == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE,no_problem,_("Failed find '%s' in time metadata."),
+                    "psLib.time.tables.format");
+            no_problem = false;
+        }
+
+        // Create and read table
+        if(i < numTables) {
+            table = psLookupTableAlloc(fullTableName, (const char*)tableFormat, tablesIndex->data.S32[i]);
+            numLines = psLookupTableRead(table);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, no_problem,
+                    _("Incorrect number of table files entered. Found: %d. Expected: %d."), i+1, numTables);
+            no_problem = false;
+        }
+
+        // Place tables into metadata slightly altered names as keys to create consistent naming conventions
+        foundTable = false;
+        for(j=0; j<numTables; j++) {
+            metadataNamesPtr = strstr(tableName, metadataTableNames[j]);
+            if(metadataNamesPtr != NULL) {
+                psMetadataAdd(timeMetadata, PS_LIST_TAIL, strcat(metadataTableNames[j], "Table"),
+                              PS_DATA_LOOKUPTABLE, NULL, table);
+                foundTable = true;
+            } else if(foundTable==false && j==numTables-1) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, no_problem,
+                        _("Incorrect number of table files entered. Found: %d. Expected: %d."), j, numTables);
+                no_problem = false;
+            }
+        }
+
+        psFree(fullTableName);
+        psFree(tableName);
+        psFree(tableFormat);
+        psFree(table);
+        i++;
+    }
+
+    p_psMemAllocatePersistent(initialPersistence);
+
+    if(numTables != i) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, no_problem, _("Incorrect number of table files entered. Found: %d. Expected: %d."), i, numTables);
+    }
+
+    psFree(tableDir);
+    psFree(tableNames);
+    psFree(tablesFrom);
+    psFree(tablesTo);
+    psFree(tablesIndex);
+    psFree(tableFormats);
+
+    return no_problem;
+}
+
+bool p_psTimeFinalize(void)
+{
+    if(timeMetadata != NULL) {
+        psFree(timeMetadata);
+        timeMetadata = NULL;
+    }
+
+    return true;
+}
+
+static void timeFree(psTime *outTime)
+{
+    // There are non dynamic allocated items
+}
+
+psTime* psTimeAlloc(psTimeType type)
+{
+    psTime *outTime = NULL;
+
+    // Error checks
+    if(type!=PS_TIME_TAI && type!=PS_TIME_UTC && type!=PS_TIME_UT1 &&
+            type!=PS_TIME_TT) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified type, %d, is not supported."),
+                type);
+        return NULL;
+    }
+
+    // Allocate memory for structure
+    outTime = (psTime*)psAlloc(sizeof(psTime));
+    psMemSetDeallocator(outTime, (psFreeFunc)timeFree);
+    // Initialize members
+    outTime->sec = 0;
+    outTime->nsec = 0;
+    outTime->type = type;
+    outTime->leapsecond = false;
+
+    return outTime;
+}
+
+
+bool psMemCheckTime(psPtr ptr)
+{
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)timeFree );
+}
+
+
+psTime* psTimeGetNow(psTimeType type)
+{
+    struct timeval now;
+    psTime *time = NULL;
+
+    // Allocate psTime struct
+    time = psTimeAlloc(type);
+
+    // Verify time structure allocated
+    if(time == NULL) {
+        return NULL;
+    }
+
+    // Get the system time
+    //    if (gettimeofday(&now, (struct timezone *)0) == -1) {
+    if (gettimeofday(&now, 0) == -1) {
+        psError(PS_ERR_OS_CALL_FAILED, true,
+                _("Failed to determine the current time from gettimeofday function."));
+        return NULL;
+    }
+
+    // Convert timeval time to psTime
+    time->sec = now.tv_sec;
+    time->nsec = now.tv_usec*1000;
+
+    // Add most leapseconds to UTC time to get TAI time if necessary
+    if(type == PS_TIME_TAI) {
+        time->sec += p_psTimeGetTAIDelta(time);
+    }
+
+    return time;
+}
+
+static psTime* convertTimeTAIUTC(psTime* time)
+{
+    psF64  deltaTAI     = 0.0;
+    psS64  deltaSec     = 0;
+    psU32  deltaNsec    = 0;
+    psF64  deltaUTC     = 0.0;
+
+    // Determine delta to convert between UTC and TAI
+    deltaTAI = p_psTimeGetTAIDelta(time);
+    deltaSec = (psS64)(deltaTAI);
+    deltaNsec = (psU32)((deltaTAI - (psF64)deltaSec) * 1e9);
+
+    // Determine seconds
+    time->sec -= deltaSec;
+
+    // Check for underflow in nsec
+    if(deltaNsec > time->nsec) {
+        // Borrow second
+        time->nsec += 1e9;
+        time->sec--;
+    }
+
+    // Determine nsec
+    time->nsec -= deltaNsec;
+
+    // Check for overflow in nsec
+    if(time->nsec >= 1e9) {
+        time->nsec -= 1e9;
+        time->sec++;
+    }
+
+    // Set new type
+    time->type = PS_TIME_UTC;
+
+    // Check if leapsecond present in delta
+    deltaUTC = p_psTimeGetTAIDelta(time);
+    if(fabs(deltaTAI-deltaUTC) >= 1.0) {
+        time->sec++;
+    }
+
+    return time;
+}
+
+static psTime* convertTimeUTCTAI(psTime* time)
+{
+    psF64  delta     = 0.0;
+    psS64  deltaSec  = 0;
+    psU32  deltaNsec = 0;
+
+    // Determine delta to convert between UTC and TAI
+    delta = p_psTimeGetTAIDelta(time);
+
+    deltaSec = (psS64)(delta);
+    deltaNsec = (psU32)((delta - (psF64)deltaSec) * 1e9);
+
+    // Determine seconds
+    time->sec += deltaSec;
+
+    // Determine nsec
+    time->nsec += deltaNsec;
+
+    // Check for overflow in nsec
+    if(time->nsec >= 1e9) {
+        time->nsec -= 1e9;
+        time->sec++;
+    }
+
+    // Set new type
+    time->type = PS_TIME_TAI;
+
+    //XXX: Set leapseconds to TRUE
+    //    time->leapsecond = true;
+    return time;
+}
+
+static psTime* convertTimeTAITT(psTime* time)
+{
+    // Add TT offset
+    time->sec += TAI_TT_OFFSET_SECONDS;
+    time->nsec += TAI_TT_OFFSET_NANOSECONDS;
+
+    // Check for overflow in nsec
+    if(time->nsec >= 1e9) {
+        time->nsec -= 1e9;
+        time->sec++;
+    }
+
+    // Set new type
+    time->type = PS_TIME_TT;
+
+    return time;
+}
+
+static psTime* convertTimeTTTAI(psTime* time)
+{
+    // Subtract TT offset
+    time->sec -= TAI_TT_OFFSET_SECONDS;
+
+    // Check for nsec underflow
+    if(TAI_TT_OFFSET_NANOSECONDS > time->nsec) {
+        // Borrow second
+        time->sec--;
+        time->nsec += 1e9;
+    }
+    time->nsec -= TAI_TT_OFFSET_NANOSECONDS;
+
+    // Check for overflow in nsec
+    if(time->nsec >= 1e9) {
+        time->nsec -= 1e9;
+        time->sec++;
+    }
+
+    // Set new type
+    time->type = PS_TIME_TAI;
+
+    return time;
+}
+
+static psTime* convertTimeUTCUT1(psTime* time)
+{
+    psS64   ut1utc  = 0;
+
+    // Get UT1-UTC value
+    ut1utc = (psS64)(psTimeGetUT1Delta(time,PS_IERS_A) * 1e9);
+
+    // Since UTC is within 0.9 sec of UT1 then nsec member is the member affected
+    if((ut1utc < 0) && (abs(ut1utc) > time->nsec)) {
+        // Borrow from sec
+        time->sec--;
+        if(time->leapsecond) {
+            time->leapsecond = false;
+        } else {
+            time->leapsecond = psTimeIsLeapSecond(time);
+        }
+        // Add to nsec
+        time->nsec += 1e9;
+    }
+    time->nsec += ut1utc;
+
+    // Check for overflow in nsec
+    if(time->nsec >= 1e9) {
+        time->nsec -= 1e9;
+        time->sec++;
+        if(time->leapsecond) {
+            time->leapsecond = false;
+            time->sec--;
+        } else {
+            time->leapsecond = psTimeIsLeapSecond(time);
+        }
+    }
+
+    // Set new type
+    time->type = PS_TIME_UT1;
+
+    return time;
+}
+
+psTime* psTimeConvert(psTime *time,
+                      psTimeType type)
+{
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time,NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(time->nsec,0,(psU32)((1e9)-1), NULL);
+
+    // If the input type is UT1 then return time and generate error message
+    if(time->type == PS_TIME_UT1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,"Cannot convert from UT1 time type");
+        return NULL;
+    }
+
+    // If the time to convert to is the same as psTime the return time
+    if (time->type == type) {
+        return time;
+    }
+
+    // Convert from TAI to UTC, TT, UT1
+    if(time->type == PS_TIME_TAI) {
+        // Convert from TAI to UTC
+        if(type == PS_TIME_UTC) {
+            time = convertTimeTAIUTC(time);
+            // Convert from TAI to TT
+        } else if(type == PS_TIME_TT) {
+            time = convertTimeTAITT(time);
+            // Convert from TAI to UT1
+        } else if(type == PS_TIME_UT1) {
+            // Convert to UTC first
+            time = convertTimeTAIUTC(time);
+            // Convert UTC to UT1
+            time = convertTimeUTCUT1(time);
+            // Convert from TAI to unknown time type
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Specified type, %d, is not supported."), type);
+            return NULL;
+        }
+        // Convert from TT to TAI, UTC, UT1
+    } else if(time->type == PS_TIME_TT) {
+        // Convert from TT to UTC
+        if(type == PS_TIME_UTC) {
+            // Convert to TAI time first
+            time = convertTimeTTTAI(time);
+            // Convert from TAI to UTC
+            time = convertTimeTAIUTC(time);
+            // Convert from TT to TAI
+        } else if(type == PS_TIME_TAI) {
+            time = convertTimeTTTAI(time);
+            // Convert from TT to UT1
+        } else if(type == PS_TIME_UT1) {
+            // Convert to UTC first
+            // Convert to TAI time first
+            time = convertTimeTTTAI(time);
+            // Convert from TAI to UTC
+            time = convertTimeTAIUTC(time);
+            // Convert from UTC to UT1
+            time = convertTimeUTCUT1(time);
+            // Convert from TT to unknown time type
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Specified type, %d, is not supported."), type);
+            return NULL;
+        }
+        // Convert from UTC to TAI, TT, UT1
+    } else if(time->type == PS_TIME_UTC) {
+        // Convert UTC to TAI
+        if(type == PS_TIME_TAI) {
+            time = convertTimeUTCTAI(time);
+            // Convert UTC to TT
+        } else if(type == PS_TIME_TT) {
+            // Convert to TAI time first
+            time = convertTimeUTCTAI(time);
+            // Convert TAI to TT
+            time = convertTimeTAITT(time);
+            // Convert UTC to UT1
+        } else if(type == PS_TIME_UT1) {
+            time = convertTimeUTCUT1(time);
+            // Convert UTC to unknown time type
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Specified type, %d, is not supported."), type);
+            return NULL;
+        }
+        // Convert unknown time type
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Specified type, %d, is not supported."), time->type);
+        return NULL;
+    }
+
+    return time;
+}
+
+double psTimeToLMST(psTime *time,
+                    double longitude)
+{
+    psF64  jdTdtDays    =  0.0;
+    psF64  jdUt1Days    =  0.0;
+    psF64  mjdUt1Days   =  0.0;
+    psF64  lmstRad      =  0.0;
+    psF64  fracDays     =  0.0;
+    psF64  gmstRad      =  0.0;
+    psF64  t            =  0.0;
+    psF64  tu           =  0.0;
+    psF64  const1       =  24110.5493771;
+    psF64  const2       =  8639877.3173760;
+    psF64  const3       =  307.4771600;
+    psF64  const4       =  0.0931118;
+    psF64  const5       = -0.0000062;
+    psF64  const6       =  0.0000013;
+    psTime *tdtTime     = NULL;
+    psTime *ut1Time     = NULL;
+
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time,NAN);
+    PS_ASSERT_INT_WITHIN_RANGE(time->nsec,0,(psU32)((1e9)-1),NAN);
+
+    // Verify input time is not in UT1 seconds
+    if(time->type == PS_TIME_UT1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,_("Specified type, %d, is incorrect."),time->type);
+        return NAN;
+    }
+
+    // Determine time reference to TT
+    tdtTime = psTimeAlloc(time->type);
+    tdtTime->sec = time->sec;
+    tdtTime->nsec = time->nsec;
+    tdtTime->leapsecond = time->leapsecond;
+    tdtTime = psTimeConvert(tdtTime,PS_TIME_TT);
+
+    // Determine time reference to UT1
+    ut1Time = psTimeAlloc(time->type);
+    ut1Time->sec = time->sec;
+    ut1Time->nsec = time->nsec;
+    ut1Time->leapsecond = time->leapsecond;
+    ut1Time = psTimeConvert(ut1Time,PS_TIME_UT1);
+
+    // Calculate UT1 as Julian Centuries since J2000.0
+    jdUt1Days = psTimeToJD(ut1Time);
+    mjdUt1Days = psTimeToMJD(ut1Time);
+    t = (jdUt1Days - 2451545.0)/36525.0;
+
+    // Calculate TDT as Julian centuries since J2000.0
+    jdTdtDays = psTimeToJD(tdtTime);
+    tu = (jdTdtDays - 2451545.0)/36525.0;
+
+    // Calculate fractional part of MJD
+    fracDays = fmod(mjdUt1Days, 1.0);
+
+    // Calculate Greenwich Mean Sidereal Time (GMST) in radians.
+    // Equation set up to minimize multiplications.
+    gmstRad = fracDays*TWOPI
+              + (const1+const2*tu+t*(const3+t*(const4+t*(const5+const6*t))))*S2R;
+
+    // Place GMST between 0 and 2*pi
+    gmstRad = fmod(gmstRad, TWOPI);
+
+    // Calculate Local Mean Sidereal Time (LMST) in radians
+    lmstRad = gmstRad + longitude;
+
+    // Free temporary structs
+    psFree(ut1Time);
+    psFree(tdtTime);
+
+    return lmstRad;
+}
+
+double psTimeGetUT1Delta(const psTime *time,
+                         psTimeBulletin bulletin)
+{
+    psU32              nTables               = 2;
+    psF64              mjd                   = 0.0;
+    psF64              result                = 0.0;
+    psU64              tableColumn           = 0;
+    psF64              dut2ut1               = 0.0;
+    psF64              t                     = 0.0;
+    psVector*          dut                   = NULL;
+    psMetadataItem*    tableMetadataItem     = NULL;
+    psLookupStatusType status                = PS_LOOKUP_SUCCESS;
+    char*              metadataTableNames[2] = {"dailyTable",  "finalsTable"};
+
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time,NAN);
+    PS_ASSERT_INT_WITHIN_RANGE(time->nsec,0,(psU32)((1e9)-1),NAN);
+
+    // Check for invalid bulletin specified
+    if((bulletin != PS_IERS_A) && (bulletin != PS_IERS_B)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,"Invalid bulletin specified %d",bulletin);
+        return NAN;
+    }
+
+    // Check if psTime tables are already loaded
+    if(!p_psTimeInit(p_psTimeConfigFilename(NULL))) {
+        psError(PS_ERR_UNKNOWN, true, "failed to init time tables.");
+        return NAN;
+    }
+
+    // Set lookup table column based on Bullentin
+    if(bulletin == PS_IERS_A) {
+        tableColumn = 3;
+    } else {
+        tableColumn = 6;
+    }
+
+    // Attempt to find value through table lookup and interpolation
+    mjd = psTimeToMJD(time);
+    result = p_psTimeSearchTables(mjd,tableColumn,metadataTableNames,nTables,&status);
+
+    // Value could not be found through table lookup and interpolation
+    if(status == PS_LOOKUP_PAST_TOP) {
+
+        // Date too early for tables. Get default time delta value from metadata, and issue warning.
+        psLogMsg("psLib.astro", PS_LOG_WARN,_("Specified psTime predates (%g) all tables of %s information."),mjd,"UT1-UTC");
+
+        // Lookup value from time metadata loaded from pslib.config
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.before.dut");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                    "psLib.time.before.dut");
+            return NAN;
+        }
+        result = tableMetadataItem->data.F64;
+
+    } else if(status == PS_LOOKUP_PAST_BOTTOM) {
+        /* Date too late for tables. Issue warning and use following formulae for predicting
+           ahead of the most recent available table entry.
+             ut1-utc = [0] + [1]*(MJD - [2]) - (ut2-ut1)
+             [0, 1, 2] = @psLib.time.predict.dut
+             ut2-ut1 = 0.022 sin(2*pi*t) - 0.012 cos(2*pi*t) - 0.006 sin(4*pi*t) + 0.007 cos(4*pi*t)
+             t = 2000.0 + (MJD - 51544.03)/365.2422
+        */
+        // Generate warning of postdate information
+        psLogMsg("psLib.astro", PS_LOG_WARN,_("Specified psTime postdates (%g) all tables of %s information."), mjd, "UT1-UTC");
+
+        // Lookup values to calculate prediction
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.dut");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                    "psLib.time.predict.dut");
+            return NAN;
+        }
+        dut = (psVector*)tableMetadataItem->data.V;
+        PS_ASSERT_PTR_NON_NULL(dut,NAN);
+
+        // Calculate predication of future UT1-UTC
+        t = 2000.0 + (mjd - 51544.03)/365.2422;
+        dut2ut1 = 0.022*sin(TWOPI*t) - 0.012*cos(TWOPI*t) - 0.006*sin(4.0*M_PI*t) + 0.007*cos(4.0*M_PI*t);
+        result = dut->data.F64[0] + dut->data.F64[1]*(mjd - dut->data.F64[2]) - dut2ut1;
+
+    } else if(status != PS_LOOKUP_SUCCESS) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed time table interpolation."));
+        return NAN;
+    }
+
+    return result;
+}
+
+static double DMOD(double x, double y)
+{
+    double value = x - y * trunc(x/y);
+    return value;
+}
+
+psTime *psTime_TideUT1Corr(const psTime *time)
+{
+    PS_ASSERT_PTR_NON_NULL(time, NULL);
+    psTime *out = NULL;
+    //Also see psEOC_PolarTideCorr for more info.
+
+    // Convert psTime to MJD
+    double MJD = psTimeToMJD(time);
+    if (isnan(MJD)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Time conversion to MJD failed.  Invalid input time.\n");
+        return NULL;
+    }
+
+    // Calculate number of Julian centuries since 2000
+    double RJD = MJD;
+
+    //Formula comes from fortran reference
+    //DMOD in fortran ref. = double remainder -> x - y * trunc(x/y)
+    double T, L, LPRIME, CAPF, CAPD, OMEGA, THETA, CORZ;
+    double ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8;
+    double T2, T3, T4;
+    T = (RJD - 51544.5) / 36525.0;
+    T2 = T*T;
+    T3 = T*T*T;
+    T4 = T*T*T*T;
+    L = -0.0002447 * T4 + 0.051635 * T3 + 31.8792 * T2 + 1717915923.2178 * T + 485868.249036;
+    L = DMOD(L, 1296000.0);
+    LPRIME = -0.00001149 * T4 - 0.000136 * T3 - 0.5532 * T2 + 129596581.0481 * T + 1287104.79305;
+    LPRIME = DMOD(LPRIME, 1296000.0);
+    CAPF = 0.00000417 * T4 - 0.001037 * T3 - 12.7512 * T2 + 1739527262.8478 * T + 335779.526232;
+    CAPF = DMOD(CAPF, 1296000.0);
+    CAPD = -0.00003169 * T4 + 0.006593 * T3 - 6.3706 * T2 + 1602961601.209 * T + 1072260.70369;
+    CAPD = DMOD(CAPD, 1296000.0);
+    OMEGA = -0.00005939 * T4 + 0.007702 * T3 + 7.4722 * T2 - 6962890.2665 * T + 450160.398036;
+    OMEGA = DMOD(OMEGA, 1296000.0);
+    THETA = (67310.54841 + (876600.0 * 3600.0 + 8640184.812866) * T + 0.093104 * T2 -
+             6.2e-6 * T3) * 15.0 + 648000.0;
+    ARG7 = DMOD((-L - 2.0 * CAPF - 2.0 * OMEGA + THETA) * M_PI / 648000.0, 2.0 * M_PI)
+           - M_PI / 2.0;
+    ARG1 = DMOD((-2.0 * CAPF - 2.0 * OMEGA + THETA) * M_PI / 648000.0, 2.0 * M_PI) - M_PI / 2.0;
+    ARG2 = DMOD((-2.0 * CAPF + 2.0 * CAPD - 2.0 * OMEGA + THETA) * M_PI / 648000.0, 2.0 * M_PI)
+           - M_PI / 2.0;
+    ARG3 = DMOD(THETA * M_PI / 648000.0, 2.0 * M_PI) - M_PI / 2.0;
+    ARG4 = DMOD((-L - 2.0 * CAPF - 2.0 * OMEGA + 2.0 * THETA) * M_PI / 648000.0, 2.0 * M_PI);
+    ARG5 = DMOD((-2.0 * CAPF - 2.0 * OMEGA + 2.0 * THETA) * M_PI / 648000.0, 2.0 * M_PI);
+    ARG6 = DMOD((-2.0 * CAPF + 2.0 * CAPD - 2.0 * OMEGA + 2.0 * THETA) * M_PI / 648000.0,
+                2.0 * M_PI);
+    ARG8 = DMOD((2.0 * THETA) * M_PI / 648000.0, 2.0 * M_PI);
+    CORZ =  0.0245 * sin(ARG7) + 0.0503 * cos(ARG7)
+            +0.1210 * sin(ARG1) + 0.1605 * cos(ARG1)
+            +0.0286 * sin(ARG2) + 0.0516 * cos(ARG2)
+            +0.0864 * sin(ARG3) + 0.1771 * cos(ARG3)
+            -0.0380 * sin(ARG4) - 0.0154 * cos(ARG4)
+            -0.1617 * sin(ARG5) - 0.0720 * cos(ARG5)
+            -0.0759 * sin(ARG6) - 0.0004 * cos(ARG6)
+            -0.0196 * sin(ARG8) - 0.0038 * cos(ARG8);
+    CORZ = CORZ * 0.1e-3;
+
+    double timeCheck = (double)(time->sec) + (double)(1e-9*time->nsec);
+    if ( (timeCheck + CORZ) < 0.0 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Invalid time for Tide Correction.\n");
+        return NULL;
+    }
+    out = psTimeAlloc(time->type);
+    *out = *time;
+    if (out->type != PS_TIME_UT1) {
+        out = psTimeConvert(out, PS_TIME_UT1);
+    }
+    //see if corrections include seconds or just nano-seconds
+    //nano-seconds must be converted (scaled) to integer values & total cannot be negative
+    if (fabs(CORZ) > 1.0) {
+        int sec = (int)CORZ;
+        out->sec += sec;
+        int nsec = (int)((CORZ - sec)*1e9);
+        out->nsec += nsec;
+    } else {
+        int nsec = out->nsec + (int)(CORZ * 1e9);
+        if (nsec < 0) {
+            out->sec += -1;
+            out->nsec = (int)(1e9) + nsec;
+        } else {
+            out->nsec = nsec;
+        }
+    }
+    return out;
+}
+
+psSphere* p_psTimeGetPoleCoords(const psTime* time)
+{
+    psU32 nTables = 3;
+    psF64 x = 0.0;
+    psF64 y = 0.0;
+    psF64 mjd = 0.0;
+    psF64 a = 0.0;
+    psF64 c = 0.0;
+    psF64 mjdPred = 0.0;
+    psSphere* output = NULL;
+    psLookupStatusType xStatus = PS_LOOKUP_SUCCESS;
+    psLookupStatusType yStatus = PS_LOOKUP_SUCCESS;
+    psMetadataItem *tableMetadataItem = NULL;
+    char *metadataTableNames[3] = {"dailyTable", "eopcTable",  "finalsTable"};
+    psVector *xp = NULL;
+    psVector *yp = NULL;
+
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time,NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(time->nsec,0,(psU32)((1e9)-1),NULL);
+
+    if(time->type != PS_TIME_TAI) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Specified type, %d, is incorrect."), time->type);
+        return NULL;
+    }
+
+    // Check if psTime tables are already loaded
+    if(!p_psTimeInit(p_psTimeConfigFilename(NULL))) {
+        psError(PS_ERR_UNKNOWN, true, "failed to init time tables.");
+        return NULL;
+    }
+
+    // Attempt to find value through table lookup and interpolation
+    mjd = psTimeToMJD(time);
+    //    x = p_psTimeSearchTables(mjd, 0, &xStatus, metadataTableNames, nTables);
+    x = p_psTimeSearchTables(mjd, 0, metadataTableNames, nTables,&xStatus);
+    //    y = p_psTimeSearchTables(mjd, 0, &yStatus, metadataTableNames, nTables);
+    y = p_psTimeSearchTables(mjd, 0, metadataTableNames, nTables,&yStatus);
+
+    // Value could not be found through table lookup and interpolation
+    if(xStatus==PS_LOOKUP_PAST_TOP && yStatus==PS_LOOKUP_PAST_TOP) {
+
+        // Date too earlier for tables. Get default polar coodinate values from metadata, and issue warning.
+        #if 0
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Specified psTime predates (%g) all tables of %s information."), mjd, "polar motion");
+        return NULL;
+        #else
+
+        psLogMsg("psLib.astro", PS_LOG_ERROR, _("Specified psTime predates (%g) all tables of %s information."), mjd, "polar motion");
+        #endif
+
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.before.xp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Failed find '%s' in time metadata."), "psLib.time.before.xp");
+            return NULL;
+        }
+        x = tableMetadataItem->data.F64;
+
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.before.yp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."), "psLib.time.before.yp");
+            return NULL;
+        }
+        y = tableMetadataItem->data.F64;
+
+    } else if(xStatus==PS_LOOKUP_PAST_BOTTOM && yStatus==PS_LOOKUP_PAST_BOTTOM) {
+
+        /* Date too late for tables. Issue warning and use following formulae for predicting
+           ahead of the most recent available table entry.
+              x = [0] + [1]*cos a + [2]*sin a + [3]*cos c + [4]*sin c
+              [0], [1], [2], [3] = @psLib.time.predict.xp
+              y = [0] + [1]*cos a + [2]*sin a + [3]*cos c + [4]*sin c
+              [0], [1], [2], [3] = @psLib.time.predict.yp
+              a = 2*pi*(mjd - pslib.time.predict.mjd)/365.25
+              c = 2*pi*(mjd - pslib.time.predict.mjd)/435.0
+        */
+        #if 0
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified psTime postdates (%g) all tables of %s information."), mjd, "polar motion");
+        return NULL;
+        #else
+
+        psLogMsg("psLib.astro", PS_LOG_ERROR,
+                 _("Specified psTime postdates (%g) all tables of %s information."), mjd, "polar motion");
+        #endif
+
+        // Get predicted MJD
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.mjd");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."),
+                    "psLib.time.predict.mjd");
+            return NULL;
+        }
+        mjdPred = tableMetadataItem->data.F64;
+
+        // Get xp
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.xp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."), "psLib.time.predict.xp");
+            return NULL;
+        }
+        xp = (psVector*)tableMetadataItem->data.V;
+        PS_ASSERT_PTR_NON_NULL(xp,NULL);
+
+        // Get yp
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.yp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."), "psLib.time.predict.yp");
+            return NULL;
+        }
+        yp = (psVector*)tableMetadataItem->data.V;
+        PS_ASSERT_PTR_NON_NULL(yp,NULL);
+
+        // Calculate "a" and "c" constants
+        a = TWOPI*(mjd - mjdPred)/365.25;
+        c = TWOPI*(mjd - mjdPred)/435.0;
+
+        // Calculate x and y polar coordinates
+        x = xp->data.F64[0] +
+            xp->data.F64[1]*cos(a) +
+            xp->data.F64[2]*sin(a) +
+            xp->data.F64[3]*cos(c) +
+            xp->data.F64[4]*sin(c);
+
+        y = yp->data.F64[0] +
+            yp->data.F64[1]*cos(a) +
+            yp->data.F64[2]*sin(a) +
+            yp->data.F64[3]*cos(c) +
+            yp->data.F64[4]*sin(c);
+
+    } else if(xStatus!=PS_LOOKUP_SUCCESS || yStatus!=PS_LOOKUP_SUCCESS) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed time table interpolation."));
+        return NULL;
+    }
+
+    // Create output sphere and convert arcsec to radians (i.e. x/60/60*PS_PI/180)
+    output = psAlloc(sizeof(psSphere));
+    output->r = x * M_PI / 648000.0;
+    output->d = y * M_PI / 648000.0;
+
+    return output;
+}
+
+psF64 p_psTimeGetTAIDelta(const psTime *time)
+{
+    psF64 jd = 0.0;
+    psF64 mjd = 0.0;
+    psF64 out = 0.0;
+    psF64 const1 = 0.0;
+    psF64 const2 = 0.0;
+    psF64 const3 = 0.0;
+    psLookupTable* table = NULL;
+    psMetadataItem *tableMetadataItem = NULL;
+    psVector *results = NULL;
+
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time,NAN);
+    PS_ASSERT_INT_WITHIN_RANGE(time->nsec,0,(psU32)((1e9)-1),NAN);
+
+    // Check if psTime tables are loaded/loadable
+    if (!p_psTimeInit(p_psTimeConfigFilename(NULL))) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed to open file %s."),
+                p_psTimeConfigFilename(NULL));
+        return NAN;
+    }
+
+    // Get table from metadata
+    tableMetadataItem = psMetadataLookup(timeMetadata, "taiTable");
+    if(tableMetadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed find '%s' in time metadata."), "taiTable");
+        return NAN;
+    }
+    table = (psLookupTable*)tableMetadataItem->data.V;
+    PS_ASSERT_PTR_NON_NULL(table,0);
+
+    // Determine Julian and modified Julian dates used in table lookup and time delta calculation
+    if(time->sec < 0) {
+        // psTime earlier than epoch
+        jd = time->sec / SEC_PER_DAY - time->nsec / NSEC_PER_DAY + JD_EPOCH_OFFSET;
+        mjd = time->sec / SEC_PER_DAY - time->nsec / NSEC_PER_DAY + MJD_EPOCH_OFFSET;
+    } else {
+        // psTime greater than epoch
+        jd = time->sec / SEC_PER_DAY + time->nsec / NSEC_PER_DAY + JD_EPOCH_OFFSET;
+        mjd = time->sec / SEC_PER_DAY + time->nsec / NSEC_PER_DAY + MJD_EPOCH_OFFSET;
+    }
+
+    // Set ceiling of the julian date to the last entry in the lookup table
+    if(table->validTo < jd) {
+        jd = table->validTo;
+    }
+
+    // Interpolation of look up table
+    results = psLookupTableInterpolateAll(table, jd);
+
+    // Check for successful interpolation
+    if(results == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed time table interpolation."));
+        return NAN;
+    }
+
+    // Set constants from table
+    const1 = results->data.F64[1];
+    const2 = results->data.F64[2];
+    const3 = results->data.F64[3];
+
+    // If const3 not equal to zero solve for difference else floor of const1
+    if(fabs(const3-0.0) > FLT_EPSILON) {
+        out = const1 + (mjd - const2) * const3;
+    } else {
+        out = floor(const1);
+    }
+
+    psFree(results);
+
+    return out;
+}
+
+long psTimeLeapSecondDelta(const psTime *time1,
+                           const psTime *time2)
+{
+    psS64 diff = 0;
+
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time1,0);
+    PS_ASSERT_PTR_NON_NULL(time2,0);
+    PS_ASSERT_INT_WITHIN_RANGE(time1->nsec,0,(psU32)((1e9)-1),0);
+    PS_ASSERT_INT_WITHIN_RANGE(time2->nsec,0,(psU32)((1e9)-1),0);
+    diff = abs((psS64)p_psTimeGetTAIDelta((psTime*)time1)-(psS64)p_psTimeGetTAIDelta((psTime*)time2));
+    return diff;
+}
+
+bool psTimeIsLeapSecond(const psTime* utc)
+{
+    psTime*    prevUtc     = NULL;
+    bool     returnValue = false;
+
+    // Check for valid time
+    PS_ASSERT_PTR_NON_NULL(utc,false);
+
+    // Verify time is UTC type
+    if(utc->type != PS_TIME_UTC) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,_("Specified type, %d, is incorrect."),utc->type);
+        return false;
+    }
+
+    // Allocate time to hold utc - 1 second
+    prevUtc = psTimeAlloc(PS_TIME_UTC);
+    prevUtc->sec = utc->sec - 1;
+
+    // Check the absolute difference between the two times for leapsecond
+    if(psTimeLeapSecondDelta(utc,prevUtc) >= 1.0) {
+        returnValue = true;
+    } else {
+        returnValue = false;
+    }
+
+    // Free prevUtc
+    psFree(prevUtc);
+
+    return returnValue;
+}
+
+double psTimeToJD(const psTime *time)
+{
+    psF64 jd = NAN;
+
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time,NAN);
+    PS_ASSERT_INT_WITHIN_RANGE(time->nsec,0,(psU32)((1e9)-1),NAN);
+
+    psTime *time2 = psTimeCopy(time);
+    //XXX: ADD says that this formula works only for PS_TIME_TAI, so adding the following:
+    if (time->type == PS_TIME_UTC || time->type == PS_TIME_TT) {
+        time2 = psTimeConvert(time2, PS_TIME_TAI);
+    }
+
+    // Julian date conversion
+    if(time2->sec < 0) {
+        // psTime earlier than epoch
+        jd = time2->sec / SEC_PER_DAY - time2->nsec / NSEC_PER_DAY + JD_EPOCH_OFFSET;
+    } else {
+        // psTime greater than epoch
+        jd = time2->sec / SEC_PER_DAY + time2->nsec / NSEC_PER_DAY + JD_EPOCH_OFFSET;
+    }
+
+    psFree(time2);
+    return jd;
+}
+
+double psTimeToMJD(const psTime *time)
+{
+    psF64 mjd = NAN;
+
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time,NAN);
+    PS_ASSERT_INT_WITHIN_RANGE(time->nsec,0,(psU32)((1e9)-1),NAN);
+    psTime *time2 = psTimeCopy(time);
+    //XXX: ADD says that this formula works only for PS_TIME_TAI, so adding the following:
+    if (time->type == PS_TIME_UTC || time->type == PS_TIME_TT) {
+        time2 = psTimeConvert(time2, PS_TIME_TAI);
+    }
+
+    // Modified Julian date conversion
+    if(time2->sec < 0) {
+        // psTime earlier than epoch
+        mjd = time2->sec / SEC_PER_DAY - time2->nsec / NSEC_PER_DAY + MJD_EPOCH_OFFSET;
+    } else {
+        // psTime greater than epoch
+        mjd = time2->sec / SEC_PER_DAY + time2->nsec / NSEC_PER_DAY + MJD_EPOCH_OFFSET;
+    }
+    psFree(time2);
+    return mjd;
+}
+
+psString psTimeToISO(const psTime *time)
+{
+    psS32 ds = 0;
+    char *timeString = NULL;
+    char *tempString = NULL;
+
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time,NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(time->nsec,0,(psU32)((1e9)-1),NULL);
+
+    // Check valid year range
+    PS_ASSERT_S64_WITHIN_RANGE(time->sec, (psS64)YEAR_0000_SEC, (psS64)YEAR_9999_SEC, NULL);
+
+    // Allocate temp strings
+    tempString = psAlloc(MAX_TIME_STRING_LENGTH);
+    timeString = psAlloc(MAX_TIME_STRING_LENGTH);
+
+    // Convert nanoseconds to decaseconds
+    ds = time->nsec / 100000000;
+
+    // tmTime variable is statically allocated, no need to free
+    struct tm *tmTime = psTimeToTM(time);
+
+    // Converts psTime to YYYY-MM-DDThh:mm:ss.sss in string form
+    if (!strftime(tempString, MAX_TIME_STRING_LENGTH, "%Y-%m-%dT%H:%M:%S", tmTime)) {
+        psError(PS_ERR_OS_CALL_FAILED, true, _("Failed to convert a time via strftime function."));
+        return NULL;
+    }
+    psFree(tmTime);
+
+    // Check if time is UTC and leapsecond
+    if(((time->type==PS_TIME_UTC)||(time->type==PS_TIME_UT1)) && (time->leapsecond)) {
+        // Modify second to be 60
+        tempString[17] = '6';
+        tempString[18] = '0';
+    }
+
+    // Create string with milliseconds
+    if (snprintf(timeString, MAX_TIME_STRING_LENGTH, "%s.%1d", tempString, ds) < 0) {
+        psError(PS_ERR_OS_CALL_FAILED, true, _("Failed to append millisecond to time string with snprintf function."));
+        return NULL;
+    }
+    psFree(tempString);
+
+    return timeString;
+}
+
+struct tm *psTimeToTM(const psTime *time)
+{
+    PS_ASSERT_PTR_NON_NULL(time,NULL);
+
+    // XXX is it safe to assume that time_t is always an integer value?
+    time_t sec = time->sec;
+
+    // if this is NOT a UTC time then we want to make sure tm.tm_sec does not
+    // end up being set to 60
+    if (!time->type == PS_TIME_UTC)
+    {
+        // If leapsecond use previous day
+        if (time->leapsecond) {
+            sec--;
+        }
+    }
+
+    // struct tm can handle leapseconds
+    struct tm *tmTime = psAlloc(sizeof(struct tm));
+    gmtime_r(&sec, tmTime);
+
+    return tmTime;
+}
+
+struct timeval* psTimeToTimeval(const psTime *time)
+{
+    struct timeval  *timevalTime = NULL;
+
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time,NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(time->sec,0,INT32_MAX,NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(time->nsec,0,(psU32)((1e9)-1),NULL);
+
+    // Allocate structure timeval
+    timevalTime = (struct timeval*)psAlloc(sizeof(struct timeval));
+
+    // Set structure members
+    timevalTime->tv_sec = time->sec;
+    timevalTime->tv_usec = time->nsec / 1000;
+
+    return timevalTime;
+}
+
+psTime* psTimeFromJD(double jd)
+{
+    psF64 days = 0.0;
+    psF64 seconds = 0.0;
+    psTime *outTime = NULL;
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Julian date conversion courtesy of Eugene Magnier
+    days = jd - 2440587.5;
+    seconds = days * SEC_PER_DAY;
+    if(seconds < 0.0) {
+        outTime->nsec = (seconds - (psS64)seconds) * -1000000000.0;  // psTime earlier than epoch
+    } else {
+        outTime->nsec = (seconds - (psS64)seconds) * 1000000000.0;   // psTime greater than epoch
+    }
+    outTime->sec = seconds;
+
+    // Error check
+    PS_ASSERT_INT_WITHIN_RANGE(outTime->nsec,0,(psU32)((1e9)-1),outTime);
+
+    return outTime;
+}
+
+psTime* psTimeFromMJD(double mjd)
+{
+    psF64 days = 0.0;
+    psF64 seconds = 0.0;
+    psTime *outTime = NULL;
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Modified Julian date conversion courtesy of Eugene Magnier
+    days = mjd - 40587.0;
+    seconds = days * SEC_PER_DAY;
+
+    if(seconds < 0.0) {
+        outTime->nsec = (seconds - (psS64)seconds) * -1000000000.0;  // psTime earlier than epoch
+    } else {
+        outTime->nsec = (seconds - (psS64)seconds) * 1000000000.0;   // psTime greater than epoch
+    }
+    outTime->sec = seconds;
+
+    // Error check
+    PS_ASSERT_INT_WITHIN_RANGE(outTime->nsec,0,(psU32)((1e9)-1),NULL);
+
+    return outTime;
+}
+
+psTime* psTimeFromISO(const char *input,
+                      psTimeType type)
+{
+
+    // Check for NULL string
+    PS_ASSERT_PTR_NON_NULL(input,NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(type, PS_TIME_TAI, PS_TIME_TT, NULL);
+
+    // Convert YYYY-MM-DDThh:mm:ss.sss[Z] in string form to tm time
+    psTime *outTime = psTimeStrptime(input, "%Y-%m-%dT%H:%M:%S");
+    if (!outTime) {
+        // Try without the middle 'T'
+        outTime = psTimeStrptime(input, "%Y-%m-%d %H:%M:%S");
+    }
+    if (!outTime) {
+        // try for date with assumed 00:00:00 time
+        outTime = psTimeStrptime(input, "%Y-%m-%d");
+    }
+    if (!outTime) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified ISO Time string, '%s', is malformed.  "
+                  "Must be in 'YYYY-MM-DDThh:mm:ss.sss' format."), input);
+        return NULL;
+    }
+
+    outTime->type = type;
+
+    return outTime;
+}
+
+// accepts a range of human readable times:
+// ISO (YYYY-MM-MMTHH:MM:SS[.sss][Z])
+// YYYY/MM/DD,HH:MM:DD
+psTime* psTimeFromString(const char *input,
+                      psTimeType type)
+{
+
+    // Check for NULL string
+    PS_ASSERT_PTR_NON_NULL(input,NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(type, PS_TIME_TAI, PS_TIME_TT, NULL);
+
+    psTime *outTime = NULL;
+
+    // Convert YYYY-MM-DDThh:mm:ss.sss[Z] in string form to tm time
+    outTime = psTimeStrptime(input, "%Y-%m-%dT%H:%M:%S");
+    if (outTime) {
+      outTime->type = type;
+      return outTime;
+    }
+    psErrorClear ();
+
+    // Convert YYYY/MM/DD,hh:mm:ss.sss[Z] in string form to tm time
+    outTime = psTimeStrptime(input, "%Y/%m/%d,%H:%M:%S");
+    if (outTime) {
+      outTime->type = type;
+      return outTime;
+    }
+    psErrorClear ();
+
+    // Convert YYYY/MM/DD@hh:mm:ss.sss[Z] in string form to tm time
+    outTime = psTimeStrptime(input, "%Y/%m/%d@%H:%M:%S");
+    if (outTime) {
+      outTime->type = type;
+      return outTime;
+    }
+    psErrorClear ();
+
+    // try for date with assumed 00:00:00 time
+    outTime = psTimeStrptime(input, "%Y-%m-%d");
+    if (outTime) {
+      outTime->type = type;
+      return outTime;
+    }
+    psErrorClear ();
+
+    // try for date with assumed 00:00:00 time
+    outTime = psTimeStrptime(input, "%Y/%m/%d");
+    if (outTime) {
+      outTime->type = type;
+      return outTime;
+    }
+    psErrorClear ();
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Specified Time string, '%s', is malformed.  Must be in 'YYYY-MM-DDThh:mm:ss.sss' format."), input);
+    return NULL;
+}
+
+psTime* psTimeFromTT(psS64 sec,
+                     psU32 nsec)
+{
+    psTime*      outTime  = NULL;
+
+    // Verify nsec within range
+    PS_ASSERT_INT_WITHIN_RANGE(nsec,0,(psU32)((1e9)-1),NULL);
+
+    // Allocate psTime data
+    outTime = psTimeAlloc(PS_TIME_TT);
+
+    // Set data members
+    outTime->sec = sec;
+    outTime->nsec = nsec;
+
+    // Return data structure
+    return outTime;
+}
+
+psTime* psTimeFromUTC(psS64 sec,
+                      psU32 nsec,
+                      bool leapsecond)
+{
+    psTime*   outTime   = NULL;
+
+    // Verify nsec within range
+    PS_ASSERT_INT_WITHIN_RANGE(nsec,0,(psU32)((1e9)-1),NULL);
+
+    // Allocate psTime data
+    outTime = psTimeAlloc(PS_TIME_UTC);
+
+    // Set data members
+    outTime->sec = sec;
+    outTime->nsec = nsec;
+
+    // Set leapsecond flag if necessary
+    outTime->leapsecond = psTimeIsLeapSecond(outTime);
+
+    return outTime;
+}
+
+psTime* psTimeFromTimeval(const struct timeval *input)
+{
+    psTime *outTime = NULL;
+
+
+    // Error check
+    PS_ASSERT_PTR_NON_NULL(input,NULL);
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Convert to psTime
+    outTime->sec = input->tv_sec;
+    outTime->nsec = input->tv_usec * 1000;
+
+    // Error check
+    PS_ASSERT_INT_WITHIN_RANGE(outTime->nsec,0,(psU32)((1e9)-1),outTime);
+
+    return outTime;
+}
+
+psTime* psTimeFromTM(const struct tm* time)
+{
+    psS64 year;
+    psS64 month;
+    psS64 day;
+    psS64 hour;
+    psS64 minute;
+    psS64 seconds;
+    psS64 temp;
+    psTime *outTime = NULL;
+
+    // Error check
+    PS_ASSERT_PTR_NON_NULL(time,NULL);
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Extract data from TM struct
+    year = time->tm_year + 1900;
+    month = time->tm_mon + 1;
+    day = time->tm_mday;
+    hour = time->tm_hour;
+    minute = time->tm_min;
+    seconds = time->tm_sec;
+
+    // Make month in range 3..14 (treat Jan & Feb as months 13..14 of prev year)
+    if( month <= 2 )
+    {
+        temp = (14 - month) / 12;
+        //        year -= (temp = (14 - month) / 12);
+        year -= temp;
+        month += 12 * temp;
+    } else if(month > 14)
+    {
+        temp = (month - 3) / 12;
+        //        year += (temp = (month - 3) / 12);
+        year += temp;
+        month -= 12 * temp;
+    }
+
+    // Make year positive
+    if (year < 0 )
+    {
+        day -= 146097 * (temp = (399 - year) / 400);
+        year += 400 * temp;
+    }
+
+    // Add day of month, days of previous 0-11 month period that began w/March, days of previous 0-399 year
+    // period that began w/March of a 400-multiple year), days of any 400-year periods before that, and 306
+    // days to adjust from Mar 1, year 0-relative to Jan 1, year 1-relative. Add hours, minutes, and seconds.
+    day += (month * 367 - 1094) / 12 + year % 100 * 1461 / 4 + (year/100 * 36524 + year/400) - 306;
+
+    outTime->sec = ((day - 1) * SEC_PER_DAY - 62135596800)
+                   + (hour * SEC_PER_HOUR)
+                   + (minute * SEC_PER_MINUTE)
+                   + seconds;
+
+    // C's TM does not define a microsecond field. Microseconds must be manipulated by calling function.
+    outTime->nsec = 0;
+
+    // Error check
+    PS_ASSERT_INT_WITHIN_RANGE(outTime->nsec,0,(psU32)((1e9)-1),outTime);
+
+    return outTime;
+}
+
+psTime* psTimeStrptime(const char *s, const char *format)
+{
+    PS_ASSERT_PTR_NON_NULL(s, NULL);
+    PS_ASSERT_PTR_NON_NULL(format, NULL);
+
+    double fractionalSeconds = 0.0;
+
+    struct tm tmTime;
+    tmTime.tm_year = tmTime.tm_mon = tmTime.tm_mday = 0;
+    tmTime.tm_hour = tmTime.tm_min = tmTime.tm_sec = 0;
+    char *lastChar = strptime(s, format, &tmTime);
+    if (!lastChar) {
+        // No error because this function is called repeatedly as a test.
+        // Returning NULL without an error is fine, since we're just saying that it's not readable.
+        return NULL;
+    }
+
+    // strptime cannot handle fractional seconds, so we do that ourselves
+    if (*lastChar != '\0') {
+        if (*lastChar == '.') {
+            char *reallyLast;           // The real last part of the string
+            fractionalSeconds = strtod(lastChar, &reallyLast);
+            if (!reallyLast) {
+                psWarning("Time string was not completely consumed");
+            }
+        }
+    }
+
+    psTime *time = psTimeFromTM(&tmTime);
+    if (!time) {
+        psError(PS_ERR_UNKNOWN, false, "failed to generate a psTime");
+        return NULL;
+    }
+
+    time->nsec += fractionalSeconds * 1e9;
+    return time;
+}
+
+psString psTimeStrftime(const psTime *time, const char *format)
+{
+    PS_ASSERT_PTR_NON_NULL(time, NULL);
+    PS_ASSERT_PTR_NON_NULL(format, NULL);
+
+    struct tm *tmTime = psTimeToTM(time);
+
+    psString s = psAlloc(MAX_TIME_STRING_LENGTH);
+    size_t size = strftime(s, MAX_TIME_STRING_LENGTH, format, tmTime);
+    psFree(tmTime);
+    // it's worth nothing that strftime() can zero without an error having
+    // occured don't believe it's worth supporting that edge case.  See
+    // strftime(3) for further details.
+    if (size == 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to stringify a psTime");
+        return NULL;
+    }
+
+    return s;
+}
+
+psTime* psTimeMath(const psTime *time,
+                   double delta)
+{
+    psF64 sec = 0.0;
+    psTime *outTime = NULL;
+    psTime *tempTime = NULL;
+
+    // Error checks
+    PS_ASSERT_PTR_NON_NULL(time,NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(time->nsec, (psU32)0, (psU32)((1e9)-1), NULL);
+
+    // Convert time to TAI if necessary, but without changing input arguments
+    if(time->type == PS_TIME_UTC) {
+        tempTime = psTimeAlloc(PS_TIME_UTC);
+        tempTime->sec = time->sec;
+        tempTime->nsec = time->nsec;
+        tempTime = psTimeConvert(tempTime, PS_TIME_TAI);
+        outTime = psTimeAlloc(PS_TIME_TAI);
+    } else {
+        tempTime = psMemIncrRefCounter((psTime*)time);
+        outTime = psTimeAlloc(time->type);
+    }
+
+    // Create output time
+    sec = delta + (psF64)tempTime->sec + (psF64)tempTime->nsec/1e9;
+    PS_ASSERT_S64_WITHIN_RANGE((psS64)sec, (psS64)0, PS_MAX_S64, NULL);
+    outTime->sec = (psS64)sec;
+    outTime->nsec = (psU32)((sec - (psF64)outTime->sec)*1e9);
+
+    // Error check
+    PS_ASSERT_INT_WITHIN_RANGE(outTime->nsec,0,(psU32)((1e9)-1), NULL);
+
+    // Convert result to same time type as input
+    if(time->type == PS_TIME_UTC) {
+        outTime = psTimeConvert(outTime, PS_TIME_UTC);
+    }
+
+    psFree(tempTime);
+
+    return outTime;
+}
+
+double psTimeDelta(const psTime *time1,
+                   const psTime *time2)
+{
+    psF64 uSec1 = 0.0;
+    psF64 uSec2 = 0.0;
+    psTime *tempTime1 = NULL;
+    psTime *tempTime2 = NULL;
+
+    // Error checks
+    // XXX nsec is U32, use an integer for comparison
+    PS_ASSERT_PTR_NON_NULL(time1,0.0);
+    PS_ASSERT_INT_WITHIN_RANGE(time1->nsec,0,(psU32)(999999999),0.0);
+    PS_ASSERT_PTR_NON_NULL(time2,0.0);
+    PS_ASSERT_INT_WITHIN_RANGE(time2->nsec,0,(psU32)(999999999),0.0);
+
+    // Verify both times of the same type
+    if(time1->type != time2->type) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,_("Specified type, %d, is incorrect."),time1->type);
+        return NAN;
+    }
+
+    // Convert time to TAI if necessary, but without changing input arguments
+    if(time1->type == PS_TIME_UTC) {
+        tempTime1 = psTimeAlloc(PS_TIME_UTC);
+        tempTime1->sec = time1->sec;
+        tempTime1->nsec = time1->nsec;
+        tempTime1 = psTimeConvert(tempTime1, PS_TIME_TAI);
+    } else {
+        tempTime1 = psMemIncrRefCounter((psTime*)time1);
+    }
+    if(time2->type == PS_TIME_UTC) {
+        tempTime2 = psTimeAlloc(PS_TIME_UTC);
+        tempTime2->sec = time2->sec;
+        tempTime2->nsec = time2->nsec;
+        tempTime2 = psTimeConvert(tempTime2, PS_TIME_TAI);
+    } else {
+        tempTime2 = psMemIncrRefCounter((psTime*)time2);
+    }
+
+    uSec1 = tempTime1->sec >= 0 ? 1.0 : -1.0;
+    uSec1 = uSec1*tempTime1->nsec/1e9;
+    uSec2 = tempTime2->sec >= 0 ? 1.0 : -1.0;
+    uSec2 = uSec2*tempTime2->nsec/1e9;
+    psF64 out = (tempTime1->sec-tempTime2->sec) + (uSec1-uSec2);
+
+    psFree(tempTime1);
+    psFree(tempTime2);
+
+    return out;
+}
+
+// delay actual init until a timefunction is used
+bool psTimeInit(const char *filename)
+{
+    // at present, this function can not fail
+    p_psTimeConfigFilename(filename);
+
+    return true;
+}
+
+void psTimeFinalize(void)
+{
+    p_psTimeFinalize();
+}
+
+psTime *psTimeCopy(const psTime *inTime)
+{
+    // Pass through NULL values!
+    if (!inTime) {
+        psTrace("psLib.astro", 6, "passing through NULL value");
+        return NULL;
+    }
+
+    psTime *outTime = psTimeAlloc(inTime->type);
+    if (outTime == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Invalid type specified in psTimeCopy.  %x", inTime->type);
+        return NULL;
+    }
+    //    *outTime = *inTime;
+    outTime->sec = inTime->sec;
+    outTime->nsec = inTime->nsec;
+    outTime->leapsecond = inTime->leapsecond;
+    return outTime;
+}
+
+// XXX EAM : I've changed the timers to report TAI
+// this makes more sense because it has monotonically increasing seconds
+// would be even better if we could get a TAI value without doing a big lookup...
+
+static psHash *timers = NULL;
+
+// free all timers
+static void psTimerFree ()
+{
+    p_psTimeFinalize();
+    psFree(timers);
+    timers = NULL;
+    return;
+}
+
+// start/restart a named timer
+bool psTimerStart (char *name)
+{
+    if (name == NULL)
+        return false;
+    if (timers == NULL) {
+        timers = psHashAlloc (16);
+    }
+    psTime *start = psTimeGetNow (PS_TIME_TAI);
+    psHashAdd (timers, name, start);
+    psFree (start);
+    return true;
+}
+
+// clear the timer, return elapsed time to date, or NAN if not previously defined
+psF64 psTimerClear (char *name)
+{
+    if (name == NULL)
+        return NAN;
+    psF64 delta = psTimerMark(name);
+
+    psTime *start = psTimeGetNow (PS_TIME_TAI);
+    psHashAdd (timers, name, start);
+    psFree (start);
+    return delta;
+}
+
+// get current elapsed time on named timer (NAN if not defined)
+psF64 psTimerMark (char *name)
+{
+    if (timers == NULL)
+        return (NAN);
+    psTime *start = psHashLookup (timers, name);
+    if (start == NULL)
+        return (NAN);
+
+    psTime *mark = psTimeGetNow (PS_TIME_TAI);
+    psF64  delta = psTimeDelta (mark, start);
+    psFree (mark);
+    return delta;
+}
+
+bool psTimerStop(void)
+{
+    psTimerFree();
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/astro/psTime.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/astro/psTime.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/astro/psTime.h	(revision 22322)
@@ -0,0 +1,481 @@
+/** @file  psTime.h
+ *
+ *  @brief Definitions for time, time utilities, and conversion functions for use
+ *  with psLib astronomy functions.
+ *
+ *  A collection of functions are required by psLib to manipulate time data. These
+ *  functions primarily consist of conversions between specific time formats.  They
+ *  use the UNIX timeval time system as the base upon which International Atomic
+ *  Time (TAI) and Universal Time Coordinated (UTC) are calculated.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.58 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-03-17 23:53:59 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSTIME_H
+#define PSTIME_H
+
+/// @addtogroup Astro Astronomy
+/// @{
+
+#include <time.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "psType.h"
+#include "psImage.h"
+#include "psLookupTable.h"
+#include "psCoord.h"
+
+#if defined(__APPLE__)   // incorrectly missing in time.h
+struct tm *gmtime_r(const time_t *, struct tm *);
+#endif
+
+struct psSphere;
+
+/** Time type.
+ *
+ * Enumeration for psTime types, TAI or UTC time.
+ */
+typedef enum {
+    PS_TIME_TAI,                       ///< Temps Atomique International (TAI) time (time with leapseconds)
+    PS_TIME_UTC,                       ///< Universal Time Coordinated (UTC) time (time without leapseconds)
+    PS_TIME_UT1,                       ///< Universal Time corrected for polar motion
+    PS_TIME_TT,                        ///< Terrestrial Time
+} psTimeType;
+
+/** Time Bulletin type
+ *
+ * Enumeration for psTimeBulletin type, A or B.
+ */
+typedef enum {
+    PS_IERS_A,                         ///< IERS Bulletin A
+    PS_IERS_B,                         ///< IERS Bulletin B
+} psTimeBulletin;
+
+/** Definition of psTime.
+ *
+ *  The psTime struct is used by psLib to represent time values critical to
+ *  astronomical calculations.  This structure represents a time which is
+ *  equivalent to TAI (International Atomic Time) and is measured in both
+ *  seconds and microseconds.
+ */
+typedef struct
+{
+    psS64 sec;                         ///< Seconds since epoch, Jan 1, 1970.
+    psU32 nsec;                        ///< Nanoseconds since last second.
+    bool leapsecond;                   ///< if time falls on UTC leapsecond
+    psTimeType type;                   ///< Type of time.
+}
+psTime;
+
+
+/** Initialize time data.
+ *
+ *  Sets the configuration file and sets up the appropriate psTimeTables and predictions.
+ */
+bool psTimeInit(
+    const char *filename                ///< psTime configuration file
+);
+
+
+/** Frees memory that was allocated by psTime functions.
+ *
+ *  Allows a subsequent search for leaked memory.
+ *
+ * @return true on sucess.
+*/
+bool p_psTimeFinalize(void);
+
+/** Frees memory that was allocated by psTime functions.
+ *
+ *  Allows a subsequent search for leaked memory.
+ */
+void psTimeFinalize(void);
+
+/** Allocate time struct.
+ *
+ * Allocates an empty time struct. User must specify the psTimeType
+ * (PS_TIME_TAI or PS_TIME_UTC) in the argument. The seconds and microseconds members
+ * of the struct are set to zero.
+ *
+ * @return psTime*:     Struct with empty time.
+ */
+psTime* psTimeAlloc(
+    psTimeType type                    ///< Type of time to create (UTC or TAI).
+) PS_ATTR_MALLOC;
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psTime structure, false otherwise.
+ */
+bool psMemCheckTime(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Get current time.
+ *
+ * Gets current time from the system clock. User must specify the psTimeType
+ * (PS_TIME_TAI or PS_TIME_UTC) in the argument.
+ *
+ *  @return psTime*:    Struct with current time.
+ */
+psTime* psTimeGetNow(
+    psTimeType type                    ///< Type of time to get (UTC or TAI).
+);
+
+/** Convert psTime to UTC, TAI, UT1, or TT time.
+ *
+ *  Converts psTime to UTC, TAI, UT1, or TT time based on the psTimeType argument.
+ *
+ *  @return psTime*:    Pointer to psTime.
+ */
+psTime* psTimeConvert(
+    psTime *time,                      ///< Time to be converted.
+    psTimeType type                    ///< Type to be converted to.
+);
+
+/** Convert psTime to Local Mean Sidereal Time (LMST).
+ *
+ *  Converts psTime at the given longitude to LMST time. If the input time is not
+ *  in UTC format, then it is converted.
+ *
+ *  @return double:     LST Time.
+ */
+double psTimeToLMST(
+    psTime *time,                      ///< psTime to be converted.
+    double longitude                   ///< Longitude.
+);
+
+/** Determine UT1 - UTC from table lookup.
+ *
+ *  This function is necessary to for various SLALIB functions.
+ *
+ *  @return double:     Time difference.
+ */
+double psTimeGetUT1Delta(
+    const psTime *time,                ///< psTime to be looked up.
+    psTimeBulletin bulletin            ///< IERS bulletin to use
+);
+
+/** Provides tidal corrections to UT1-UTC.
+ *
+ *  Uses the Ray model of Simon et al.
+ *
+ *  @return psTime*:    The corrected time in UT1.
+ */
+psTime *psTime_TideUT1Corr(
+    const psTime *time                 ///< psTime to be corrected.
+);
+
+/** Determine TAI - UTC from table lookup.
+ *
+ *  This function is necessary to for various psTime functions.
+ *
+ *  @return psF64:      Time difference.
+ */
+psF64 p_psTimeGetTAIDelta(
+    const psTime *time                 ///< psTime to be looked up.
+);
+
+/** Determine polar coordinates at a given time.
+ *
+ *  Determines the orientation of the polar axis at the given time.
+ *
+ *  @return psSphere*:      Spherical coordinates of Earth's polar axias.
+ */
+psSphere* p_psTimeGetPoleCoords(
+    const psTime *time      ///< psTime determine polar orientation.
+);
+
+/** Calculate the number of leapseconds between two times.
+ *
+ *  Calculates the number of leapseconds between two times.
+ *
+ *  @return long:   leapseconds added between given times
+ */
+long psTimeLeapSecondDelta(
+    const psTime* time1,               ///< First input time.
+    const psTime* time2                ///< Second input time.
+);
+
+/** Determine if UTC time is a leapsecond.
+ *
+ *  Determines if the specified UTC time is a valid leapsecond.
+ *
+ *  @return bool:   valid leap second
+ */
+bool psTimeIsLeapSecond(
+    const psTime* utc                  ///< UTC to verify if leap second
+);
+
+/** Convert psTime to Julian date time.
+ *
+ *  Converts psTime to Julian date (JD) time. This function does not add or
+ *  subtract leapseconds.
+ *
+ *  @return double:     Julian Date (JD) time.
+ */
+double psTimeToJD(
+    const psTime* time                 ///< Input time to be converted.
+);
+/** Convert psTime to modified Julian date time.
+ *
+ *  Converts psTime to modified Julian date (MJD) time. This function does not
+ *  add or subtract leapseconds.
+ *
+ *  @return double:     Modified Julian Days (MJD) time.
+ */
+double psTimeToMJD(
+    const psTime* time                  ///< Input time to be converted.
+);
+
+/** Convert psTime to ISO8601 formatted string.
+ *
+ *  Converts psTime to a null terminated string in the form of YYYY-MM-DDThh:mm:ss.sss.
+ *  This function does not add or subtract leapseconds.
+ *
+ *  @return psString:     Pointer null terminated array of chars in ISO time.
+ */
+psString psTimeToISO(
+    const psTime* time                  ///< Input time to be converted.
+);
+
+/** Convert psTime to struct tm time.
+ *
+ *  Converts psTime to struct tm time.  This function should handle
+ *  UTC leapseconds correctly.
+ *
+ *  @return tm*:   tm struct.
+ */
+struct tm *psTimeToTM(
+                const psTime* time     ///< Input time to be converted.
+            );
+
+/** Convert psTime to timeval time.
+ *
+ *  Converts psTime to timeval time. This function does not add or subtract leapseconds.
+ *
+ *  @return timeval*:   timeval struct time.
+ */
+struct timeval* psTimeToTimeval(
+                const psTime* time     ///< Input time to be converted.
+            );
+
+/*
+ * Convert psTime to tm time.
+ *
+ * Converts psTime to tm time. This function is based on a Perl algorithm availble
+ * in the Pan-STARRS Image processing Algorithm Design Description (ADD). This function
+ * does not add or subtract leapseconds.
+ *
+ *  @return  tm: tm struct time.
+ *
+struct tm* p_psTimeToTM(
+                const psTime *time     ///< Input time to be converted.
+            );
+*/
+/** Convert JD to psTime.
+ *
+ *  Converts JD time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime: time.
+ */
+psTime* psTimeFromJD(
+    double jd                          ///< Input time to be converted.
+);
+
+/** Convert MJD to psTime.
+ *
+ *  Converts MJD time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime: time.
+ */
+psTime* psTimeFromMJD(
+    double mjd                         ///< Input time to be converted.
+);
+
+/** Convert ISO to psTime.
+ *
+ *  Converts ISO time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime*: time
+ */
+psTime* psTimeFromISO(
+    const char* input,                 ///< Input time to be converted.
+    psTimeType type                    ///< Time type.
+);
+
+/** Convert various human-readable formats to psTime.
+ *
+ *  Converts human readable time formats to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime*: time
+ */
+psTime* psTimeFromString(
+    const char* input,                 ///< Input time to be converted.
+    psTimeType type                    ///< Time type.
+);
+
+/** Convert timeval to psTime.
+ *
+ *  Converts timeval time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime*: time.
+ */
+psTime* psTimeFromTimeval(
+    const struct timeval *input        ///< Input time to be converted.
+);
+
+/** Convert Terrestrial Time to psTime
+ *
+ *  Converts Terrestial Time to psTime.  This function assumes resultant time is of type TT.
+ *
+ *  @return psTime*: time (TT)
+ */
+psTime* psTimeFromTT(
+    psS64 sec,                         ///< Input terrestrial time in seconds
+    psU32 nsec                         ///< Input terrestrial time fraction of seconds (nanoseconds)
+);
+
+/** Convert UTC time to psTime
+ *
+ *  Converts UTC time to psTime.  It will verify if time specified is a leapsecond.
+ *
+ *  @return psTime*: time (UTC)time
+ */
+psTime* psTimeFromUTC(
+    psS64  sec,                        ///< Input time in seconds
+    psU32  nsec,                       ///< Input time fraction of seconds (nanoseconds)
+    bool leapsecond                    ///< Input time is a leapsecond
+);
+
+/** Convert tm time to psTime.
+ *
+ *  Converts tm time to psTime. This function is based on a Perl algorithm availble
+ *  in the Pan-STARRS Image processing Algorithm Design Description (ADD). This function
+ *  does not add or subtract leapseconds.
+ *
+ *  @return  psTime*: time.
+ */
+psTime* psTimeFromTM(
+    const struct tm *time              ///< Input time to be converted.
+);
+
+/** Convert an arbitrary string into a psTime.
+ *
+ *  Converts a string, using a strptime(3) format, into a psTime.  See
+ *  strptime(3) for documentation on this format.
+ *
+ *  @return  psTime*: time.
+ */
+
+psTime* psTimeStrptime(
+    const char *s,                      ///< string to be converted
+    const char *format                  ///< strptime(3) format
+);
+
+/** Convert a psTime into a formated string.
+ *
+ *  Converts a psTime, using a strftime(3) format, into a formatted string.
+ *  See strftime(3) for documentation on this format.
+ *
+ *  @return  psString: string.
+ */
+
+psString psTimeStrftime(
+    const psTime *time,                 ///< Time to be formatted.
+    const char *format                  ///< strftime(3) format
+);
+
+/** Adds delta to time. Result is in TAI time.
+ *
+ *  Adds delta to time. Input time is converted to TAI format if necessary.
+ *
+ *  @return  psTime*: time.
+ */
+psTime* psTimeMath(
+    const psTime *time,                ///< Time.
+    double delta                       ///< Time delta.
+);
+
+/** Determine difference between two times. Result is in TAI time.
+ *
+ *  Determine difference between two times. Input times are converted to TAI format if necessary.
+ *
+ *  @return double: Time difference.
+ */
+double psTimeDelta(
+    const psTime *time1,               ///< First time.
+    const psTime *time2                ///< Second time.
+);
+
+/** Searches the IERS time tables for a specified entry location.
+ *
+ *  Returns the interpolated double precision (arcsec) value at the specified entry
+ *  location.  Inputs to specify are the time index in mjd, the column number
+ *  corresponding to Xp, Yp, or Sp (UT1-UTC) in IERS A or B, the time table names,
+ *  and the number of time tables.
+ *
+ *  @return psF64:          Resulting table entry for specified parameters.
+ */
+psF64 p_psTimeSearchTables(
+    psF64 index,                       ///< time index for which to search
+    psU64 column,                      ///< column number of specified index
+    char *metadataTableNames[],        ///< names of IERS tables to search
+    psU32 nTables,                     ///< number of IERS tables to search
+    psLookupStatusType* status         ///< status of table search
+);
+
+/** Stores the current time in a psHash of timers, under the supplied name.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psTimerStart(
+    char *name                         ///< timer name to start
+);
+
+/** Resets the named timer.
+ *
+ *  @return psF64:      The time elapsed since start.
+ */
+psF64 psTimerClear(
+    char *name                         ///< timer name to clear
+);
+
+/** Returns the elapsed time, in seconds, for the timer specified by name.
+ *
+ *  @return psF64:      The elapsed time in seconds since timer start.
+ */
+psF64 psTimerMark(
+    char *name                        ///< timer name to mark
+);
+
+/** Frees all memory associated with all timers and returns the expended time.
+ *
+ *  @return psF64:      The maximum time expended.
+ */
+bool psTimerStop(void);
+
+/** Copy a psTime.
+ *
+ *  @return psTime*:        New copy of existing psTime.
+ */
+psTime *psTimeCopy(
+    const psTime *inTime               ///< input time to copy.
+);
+
+// used by p_psEOCInit()
+const char *p_psTimeConfigFilename(const char *filename);
+
+/// @}
+
+#endif // #ifndef PSTIME_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/db/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/db/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/db/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/db/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/db/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/db/Makefile.am	(revision 22322)
@@ -0,0 +1,14 @@
+#Makefile for db functions of psLib
+#
+noinst_LTLIBRARIES = libpslibdb.la
+
+libpslibdb_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(MYSQL_CFLAGS)
+libpslibdb_la_SOURCES = \
+	psDB.c
+
+EXTRA_DIST = db.i
+
+pkginclude_HEADERS = \
+	psDB.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/db/db.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/db/db.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/db/db.i	(revision 22322)
@@ -0,0 +1,2 @@
+/* db headers */
+%include "psDB.h"
Index: /tags/sj_tags/sj_root_20080929/psLib/src/db/psDB.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/db/psDB.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/db/psDB.c	(revision 22322)
@@ -0,0 +1,2769 @@
+/** @file psDB.c
+ *
+ * Copyright (C) 2005-2007  Joshua Hoblitt, University of Hawaii
+ * Copyright (C) 2005  Aaron Culliney
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * @brief database functions
+ *
+ * This file contains functions that perform basic database operations.  MySQL
+ * 4.1.2 or newer is required.
+ *
+ * $Id: psDB.c,v 1.168 2008-07-18 22:09:19 price Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_PSDB
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+#undef __STRICT_ANSI__
+#include <stdlib.h>
+#define __STRICT_ANSI__
+#include <math.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <mysql.h>
+#include <mysql_com.h> // enum_field_types
+
+#include "psDB.h"
+#include "psMemory.h"
+#include "psAssert.h"
+#include "psAbort.h"
+#include "psError.h"
+#include "psString.h"
+#include "psTrace.h"
+#include "psMetadataConfig.h"
+
+
+// set the pointer to NULL if we are actually freeing the memory
+#define PSDB_NULL_FREE(ptr) \
+if (psMemGetRefCounter(ptr) == 1) { \
+    psFree(ptr); \
+    ptr = NULL; \
+} else { \
+    psFree(ptr); \
+}
+
+typedef struct
+{
+    enum enum_field_types type;
+    bool            isUnsigned;
+}
+mysqlType;
+
+typedef struct
+{
+    MYSQL_BIND      *bind;
+    psU32           n;
+}
+psDBMysqlRow;
+
+// cache of prepared query statements
+static pthread_mutex_t preparedQueryMutex = PTHREAD_MUTEX_INITIALIZER;
+static psHash *preparedQuery = NULL;
+
+static pthread_mutex_t lookupTableMutex = PTHREAD_MUTEX_INITIALIZER;
+static psHash   *pTypeToSQLlookupTable = NULL;
+static psHash   *sqlToPTypeLookupTable = NULL;
+static psHash   *mysqlToSqlLookupTable = NULL;
+static psHash   *pTypeToMysqlLookupTable = NULL;
+
+// free func
+static void psDBFree(psDB *dbh);
+
+// database utility functions
+static inline bool psDBPackMySQLRow(psDBMysqlRow *mysqlRow, const psMetadata *values);
+static psDBMysqlRow *psDBMysqlRowAlloc(psU32 paramCount);
+static void psDBMysqlRowFree(psDBMysqlRow *row);
+static void psDBMysqlRowRecycle(psDBMysqlRow *mysqlRow);
+
+// SQL generation functions
+static psString psDBGenerateCreateTableSQL(const char *tableName, const psMetadata *where);
+static psString psDBGenerateSelectRowSQL(const char *tableName, const char *col,
+        const psMetadata *where, psU64 limit);
+static psString psDBGenerateInsertRowSQL(const char *tableName, const psMetadata *row);
+static psString psDBGenerateUpdateRowSQL(const char *tableName, const psMetadata *where,
+        const psMetadata *values);
+static psString psDBGenerateDeleteRowSQL(const char *tableName, const psMetadata *where,
+        unsigned long long limit);
+static psString psDBGenerateSetSQL(const psMetadata *set
+                                  );
+static char *psDBGenerateConditionalSQL(const psMetadataItem *item, const char *tableName);
+
+// lookup table functions
+static psElemType psDBMySQLToPType(enum enum_field_types type, unsigned int flags);
+static psString psDBPTypeToSQL(psElemType pType);
+static mysqlType *psDBPTypeToMySQL(psElemType pType);
+
+static psHash  *psDBPTypeToSQLTableSetup(void);
+static psHash  *psDBPTypeToSQLTableGet(void);
+static void     psDBPTypeToSQLTableCleanup(void);
+
+static psHash  *psDBSQLToPTypeTableSetup(void);
+static psHash  *psDBSQLToPTypeTableGet(void);
+static void     psDBSQLToPTypeTableCleanup(void);
+
+static psHash  *psDBMySQLToSQLTableSetup(void);
+static psHash  *psDBMySQLToSQLTableGet(void);
+static void     psDBMySQLToSQLTableCleanup(void);
+
+static psHash  *psDBPTypeToMySQLTableSetup(void);
+static psHash  *psDBPTypeToMySQLTableGet(void);
+static void     psDBPTypeToMySQLTableCleanup(void);
+
+static psPtr    psDBMySQLTypeAlloc(enum enum_field_types type, bool isUnsigned);
+static void     psDBAddToLookupTable(psHash *lookupTable, psU32 type, const char *string);
+static void     psDBAddVoidToLookupTable(psHash *lookupTable, psU32 type, psPtr value);
+static psErrorCode mysqlTopsErr(MYSQL *mysql);
+
+// pType utility functions
+static psPtr    psDBGetPTypeNaN(psElemType pType);
+static bool     psDBIsPTypeNaN(psElemType pType, psPtr data);
+
+// public functions
+/*****************************************************************************/
+
+psDB *psDBAlloc(const char *host,
+                const char *user,
+                const char *passwd,
+                const char *dbname,
+                unsigned int port)
+{
+    MYSQL           *mysql;
+    psDB            *dbh;
+
+    mysql = mysql_init(NULL);
+    if (!mysql) {
+        psAbort("mysql_init(), out of memory.");
+    }
+
+    // without this call we won't pick up anything from my.cnf
+    mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "client");
+
+    // Connect to host and mySql server with specified database
+    if (!mysql_real_connect(mysql, host, user, passwd, dbname, port, NULL, 0)) {
+        psError(mysqlTopsErr(mysql), true,
+                _("Failed to connect to database.  Error: %s"),mysql_error(mysql));
+
+        mysql_close(mysql);
+
+        return NULL;
+    }
+
+    dbh = psAlloc(sizeof(psDB));
+
+    dbh->mysql = mysql;
+
+    // explicit transactions default to false
+    if (!psDBExplicitTrans(dbh, false)) {
+        psError(mysqlTopsErr(dbh->mysql), true,
+                "failed to set transaction type. Error: %s", mysql_error(mysql));
+
+        mysql_close(mysql);
+        psFree(dbh);
+
+        return NULL;
+    }
+
+    if (psMemGetThreadSafety()) {
+        pthread_mutex_lock(&lookupTableMutex);
+
+        // psDBPTypeToSQLTableSetu must be run first
+        psDBPTypeToSQLTableSetup();
+        psDBSQLToPTypeTableSetup();
+        psDBMySQLToSQLTableSetup();
+        psDBPTypeToMySQLTableSetup();
+
+        pthread_mutex_unlock(&lookupTableMutex);
+    } else {
+        // psDBPTypeToSQLTableSetu must be run first
+        psDBPTypeToSQLTableSetup();
+        psDBSQLToPTypeTableSetup();
+        psDBMySQLToSQLTableSetup();
+        psDBPTypeToMySQLTableSetup();
+    }
+
+    // don't set the deallocator func until after we've setup the lookup tables
+    // so an alloc error doesn't try to free potentionally uncreated tables
+    psMemSetDeallocator(dbh, (psFreeFunc) psDBFree);
+
+    psTrace("psLib.db", PS_LOG_INFO, "connected to database %s", dbname);
+
+    return dbh;
+}
+
+static void psDBFree(psDB *dbh)
+{
+    // quietly handle NULLs
+    if (!dbh) {
+        return;
+    }
+
+    // Attempt to close specified database connection
+    mysql_close(dbh->mysql);
+
+    // ASC WARNING NOTE: the psDBSQLToPTypeTableCleanup cleanup routine
+    // needs to be called first because it refers to
+    // psDBPTypeToSQLTableGet ...
+    //
+    // The *cleanup functions should be thread safe as they just call psFree()
+    // but we don't want to be cleaning up & setting up at the same time
+    if (psMemGetThreadSafety()) {
+        pthread_mutex_lock(&lookupTableMutex);
+
+        psDBSQLToPTypeTableCleanup();
+        psDBMySQLToSQLTableCleanup();
+        psDBPTypeToSQLTableCleanup();
+        psDBPTypeToMySQLTableCleanup();
+
+        pthread_mutex_unlock(&lookupTableMutex);
+    } else {
+        psDBSQLToPTypeTableCleanup();
+        psDBMySQLToSQLTableCleanup();
+        psDBPTypeToSQLTableCleanup();
+        psDBPTypeToMySQLTableCleanup();
+    }
+
+    psTrace("psLib.db", PS_LOG_INFO, "disconnected");
+}
+
+bool psDBCreate(psDB *dbh,
+                const char *dbname)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(dbname, false);
+
+    psString query = NULL;
+    psStringAppend(&query, "CREATE DATABASE %s", dbname);
+
+    // the MySQL C API notes that mysql_create_db() is deprecated
+    bool status = p_psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to create new database.");
+    }
+
+    psFree(query);
+
+    psTrace("psLib.db", PS_LOG_INFO, "created a database named %s", dbname);
+
+    return status;
+}
+
+bool psDBChange(psDB *dbh,
+                const char *dbname)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(dbname, false);
+
+    // Attempt to select new database
+    if (mysql_select_db(dbh->mysql, dbname) != 0) {
+        psError(mysqlTopsErr(dbh->mysql), true, _("Failed to change database.  Error: %s"),
+                mysql_error(dbh->mysql));
+
+        return false;
+    }
+
+    psTrace("psLib.db", PS_LOG_INFO, "changed to using database %s", dbname);
+
+    return true;
+}
+
+bool psDBDrop(psDB *dbh,
+              const char *dbname)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(dbname, false);
+
+    char            *query = NULL;
+    psStringAppend(&query, "DROP DATABASE %s", dbname);
+
+    // the MySQL C API notes that mysql_drop_db() is deprecated
+    bool status = p_psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to drop database.");
+    }
+
+    psFree(query);
+
+    psTrace("psLib.db", PS_LOG_INFO, "dropped database %s", dbname);
+
+    return status;
+}
+
+bool psDBCreateTable(psDB *dbh,
+                     const char *tableName,
+                     const psMetadata *md)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(tableName, false);
+    PS_ASSERT_PTR_NON_NULL(md, false);
+
+    // Generate SQL query string
+    psString query = psDBGenerateCreateTableSQL(tableName, md);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, _("Query generation failed."));
+        return false;
+    }
+
+    // Run SQL query to create table
+    bool status = p_psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, _("Failed to create table."));
+    }
+
+    psFree(query);
+
+    psTrace("psLib.db", PS_LOG_INFO, "created table %s", tableName);
+
+    return status;
+}
+
+bool psDBDropTable(psDB *dbh,
+                   const char *tableName)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(tableName, false);
+
+    // Create SQL command string to drop table
+    psString query = NULL;
+    psStringAppend(&query, "DROP TABLE %s", tableName);
+
+    // Execute query
+    bool status = p_psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, _("Failed to drop table."));
+    }
+
+    psFree(query);
+
+    psTrace("psLib.db", PS_LOG_INFO, "dropped table %s", tableName);
+
+    return status;
+}
+
+psArray *psDBSelectColumn(psDB *dbh,
+                          const char *tableName,
+                          const char *col,
+                          unsigned long long limit)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, NULL);
+    PS_ASSERT_PTR_NON_NULL(tableName, NULL);
+    PS_ASSERT_PTR_NON_NULL(col, NULL);
+
+    MYSQL_RES       *result;            // complete db result set
+    MYSQL_ROW       row;                // single row of db result set
+    my_ulonglong    rowCount;           // number of rows in db result set
+    unsigned long   dataSize;           // size of field
+    unsigned int    fieldCount;         // number of fields in db result set
+    psArray         *column = NULL;     // return array
+    psPtr           data;               // copy of result field
+
+    // Generate SQL query string
+    psString query = psDBGenerateSelectRowSQL(tableName, col, NULL, limit);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, _("Query generation failed."));
+        return NULL;
+    }
+
+    // Execut SQL query string
+    if (!p_psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, _("Failed to select column."));
+        psFree(query);
+        return NULL;
+    }
+    psFree(query);
+
+    // Obtain query result and check for no data condition
+    result = mysql_store_result(dbh->mysql);
+
+    if (!result) {
+        // no result set
+        fieldCount = mysql_field_count(dbh->mysql);
+
+        // if field count is zero the query returned no data.  If it's non-zero
+        // then something bad has happened.
+        if (fieldCount != 0) {
+            psError(mysqlTopsErr(dbh->mysql), true, _("Query returned no data.  Error: %s"),
+                    mysql_error(dbh->mysql));
+            return NULL;
+        }
+    }
+
+    // Get number of rows returned in result
+    rowCount = mysql_num_rows(result);
+    // XXX has mysql's semantics changed?  If this is zero then why was a
+    // result set returned?
+    if (!rowCount) {
+        mysql_free_result(result);
+        return NULL;
+    }
+
+    // pre-allocate enough elements to hold the complete result set
+    // then reset n to 0 so elements are added from the beginning of
+    // the array
+    column = psArrayAllocEmpty(rowCount);
+
+    // Fetch each result
+    while ((row = mysql_fetch_row(result))) {
+        // get the first element of lengths array that is part of the
+        // result set
+        dataSize = *(mysql_fetch_lengths(result));
+
+        // represent NULL as a NULL string
+        if (row[0] == NULL) {
+            psArrayAdd(column, 0, NULL);
+            continue;
+        }
+
+        // make a copy of the data
+        data = psAlloc(dataSize+1);
+        memcpy(data, row[0], dataSize);
+        ((char*)data)[dataSize] = '\0';
+
+        // add field to return array
+        psArrayAdd(column, 0, data);
+        psFree(data);
+    }
+
+    // Clean up mysql memory
+    mysql_free_result(result);
+
+    return column;
+}
+
+// dest = assign to, source = source string psArray, conv = conversion function,
+// type = type to cast to, pType = psElemType
+#define PS_STR_ARRAY_TO_PTYPE(dest, source, conv, type, pType) \
+{ \
+    psPtr           myNaN; \
+    \
+    for (long i = 0; i < source->n; i++) { \
+        if (source->data[i]) { \
+            dest[i] = (type)conv(source->data[i]); \
+        } else { \
+            myNaN = psDBGetPTypeNaN(pType); \
+            dest[i] = *(type *)myNaN; \
+            psFree(myNaN); \
+        } \
+    } \
+}
+
+psVector *psDBSelectColumnNum(psDB *dbh,
+                              const char *tableName,
+                              const char *col,
+                              psElemType type,
+                              unsigned long long limit)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, NULL);
+    PS_ASSERT_PTR_NON_NULL(tableName, NULL);
+    PS_ASSERT_PTR_NON_NULL(col, NULL);
+
+    psArray *stringColumn = psDBSelectColumn(dbh, tableName, col, limit);
+    if (!stringColumn) {
+        // could be an error or the result set was just empty
+        return NULL;
+    }
+
+    psVector *column = psVectorAlloc(stringColumn->n, type);
+
+    // conversion functions are a portability issue
+    switch (type) {
+    case PS_DATA_S8:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S8, stringColumn, atoi, psS8, PS_DATA_S8);
+        break;
+    case PS_DATA_S16:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S16, stringColumn, atoi, psS16, PS_DATA_S16);
+        break;
+    case PS_DATA_S32:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S32, stringColumn, atoi, psS32, PS_DATA_S32);
+        break;
+    case PS_DATA_S64:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S64, stringColumn, atoll, psS64, PS_DATA_S64);
+        break;
+    case PS_DATA_U8:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U8, stringColumn, atoi, psU8, PS_DATA_U8);
+        break;
+    case PS_DATA_U16:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U16, stringColumn, atoi, psU16, PS_DATA_U16);
+        break;
+    case PS_DATA_U32:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U32, stringColumn, atoi, psU32, PS_DATA_U32);
+        break;
+    case PS_DATA_U64:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U64, stringColumn, atoll, psU64, PS_DATA_U64);
+        break;
+    case PS_DATA_F32:
+        PS_STR_ARRAY_TO_PTYPE(column->data.F32, stringColumn, atof, psF32, PS_DATA_F32);
+        break;
+    case PS_DATA_F64:
+        PS_STR_ARRAY_TO_PTYPE(column->data.F64, stringColumn, atof, psF64, PS_DATA_F64);
+        break;
+    case PS_DATA_BOOL:
+        // valid for psVector?
+        PS_STR_ARRAY_TO_PTYPE(column->data.U8, stringColumn, atoi, psU8, PS_DATA_U8);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Invalid type specified in psDBSelectColumnNum.\n");
+        psFree(stringColumn);
+        psFree(column);
+        return NULL;
+        break;
+    }
+
+    psFree(stringColumn);
+
+    return column;
+}
+
+psArray *psDBSelectRows(psDB *dbh,
+                        const char *tableName,
+                        const psMetadata *where,
+                        unsigned long long limit)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, NULL);
+    PS_ASSERT_PTR_NON_NULL(tableName, NULL);
+
+    // Create select row query
+    psString query = psDBGenerateSelectRowSQL(tableName, NULL, where, limit);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+        return NULL;
+    }
+
+    // Run SQL query
+    if (!p_psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Query execution failed.");
+        psFree(query);
+        return NULL;
+    }
+    psFree(query);
+
+    // return the result
+    return p_psDBFetchResult(dbh);
+}
+
+bool psDBInsertOneRow(psDB *dbh,
+                      const char *tableName,
+                      const psMetadata *row)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(tableName, false);
+    PS_ASSERT_PTR_NON_NULL(row, false);
+
+    // Create array to store single row
+    psArray *rowSet = psArrayAllocEmpty(1);
+    psArrayAdd(rowSet, 0, (psPtr)row);
+
+    // Execute function to insert rows
+    if (!psDBInsertRows(dbh, tableName, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, _("Failed to insert row."));
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool psDBInsertRows(psDB *dbh,
+                    const char *tableName,
+                    const psArray *rowSet)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(tableName, false);
+    PS_ASSERT_PTR_NON_NULL(rowSet, false);
+
+    // we are assuming that all rows in the set have an identical with reguard
+    // to field count and type
+    psMetadata *row = rowSet->data[0];
+
+    // Generate SQL query string
+    psString query = psDBGenerateInsertRowSQL(tableName, row);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, _("Query generation failed."));
+        return false;
+    }
+
+    if (p_psDBRunQueryPrepared(dbh, rowSet, query) < 0) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "insert failed");
+        psFree(query);
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+psArray *psDBDumpRows(psDB *dbh,
+                      const char *tableName)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(tableName, false);
+
+    return psDBSelectRows(dbh, tableName, NULL, 0);
+}
+
+psMetadata *psDBDumpCols(psDB *dbh,
+                         const char *tableName)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(tableName, false);
+
+    MYSQL_RES       *result;
+    MYSQL_FIELD     *field;
+    unsigned int    fieldCount;
+    psU32           pType;
+    unsigned int    i;
+    psPtr           column;
+
+    // find column types
+    result = mysql_list_fields(dbh->mysql, tableName, NULL);
+    if (!result) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Failed to retrieve column types.");
+    }
+
+    field = mysql_fetch_fields(result);
+    fieldCount = mysql_num_fields(result);
+
+    // this metadata is returned
+    psMetadata *table = psMetadataAlloc();
+
+    // fetch each column and load into psMetadata
+    for (i =0; i < fieldCount; i++) {
+        // find ptype of column
+        pType = psDBMySQLToPType(field[i].type, field[i].flags);
+        if (!pType) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to lookup type.");
+            psFree(table);
+            mysql_free_result(result);
+            return NULL;
+        }
+        //psLogMsg( __func__, PS_LOG_INFO, "pType=[%ld]\n", pType );
+
+        // if the ptype is PS_DATA_PTR assume that it's a string and fetch the
+        // column as an psArray of strings; otherwise fetch the column as a
+        // psVector.
+        if (pType == PS_DATA_STRING) {
+            // PS_DATA_UNKNOWN -> PS_DATA_ARRAY ?
+            column = psDBSelectColumn(dbh, tableName, field[i].name, 0);
+            psMetadataAddArray(table, PS_LIST_TAIL, field[i].name, 0, "", column);
+            //            psMetadataAddStr(table, PS_LIST_TAIL, field[i].name, "", column);
+            psFree(column);
+        } else {
+            column = psDBSelectColumnNum(dbh, tableName, field[i].name, pType, 0);
+            psMetadataAddVector(table, PS_LIST_TAIL, field[i].name, 0, "", column);
+            psFree(column);
+        }
+    }
+
+    // Clean up mysql memory
+    mysql_free_result(result);
+
+    return table;
+}
+
+long psDBUpdateRows(psDB *dbh,
+                    const char *tableName,
+                    const psMetadata *where,
+                    psMetadata *values)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, -1);
+    PS_ASSERT_PTR_NON_NULL(tableName, -1);
+    PS_ASSERT_PTR_NON_NULL(values, -1);
+
+    // Generate SQL query to update row
+    psString query = psDBGenerateUpdateRowSQL(tableName, where, values);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, _("Query generation failed."));
+        return -1;
+    }
+
+    psArray *rowData = psArrayAllocEmpty(1);
+    psArrayAdd(rowData, 0, values);
+    long rowsAffected = p_psDBRunQueryPrepared(dbh, rowData, query);
+    psFree(rowData);
+    psFree(query);
+    if (rowsAffected < 0) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "update failed");
+        return -1;
+    }
+
+    return rowsAffected;
+}
+
+long psDBDeleteRows(psDB *dbh,
+                    const char *tableName,
+                    const psMetadata *where,
+                    unsigned long long limit)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, -1);
+    PS_ASSERT_PTR_NON_NULL(tableName, -1);
+
+    // Create SQL statement string
+    psString query = psDBGenerateDeleteRowSQL(tableName, where,limit);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+        return -1;
+    }
+
+    // Run SQL query
+    if (!p_psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Delete failed.");
+        mysql_rollback(dbh->mysql);
+        psFree(query);
+        return -1;
+    }
+    psFree(query);
+
+    // Get the number of affected row for the delete command
+    psS64 rows = (psS64)mysql_affected_rows(dbh->mysql);
+
+    return rows;
+}
+
+long long psDBLastInsertID(psDB *dbh)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, -1);
+
+    psTrace("psLib.db", PS_LOG_INFO, "calling myql_insert_id()");
+
+    // XXX return type is actually my_ulonglong - should the return be psU64?
+    long long id = (long long)mysql_insert_id(dbh->mysql);
+
+    psTrace("psLib.db", PS_LOG_INFO, "LAST_INSERT_ID == %lld", id);
+
+    return id;
+}
+
+bool psDBExplicitTrans(psDB *dbh, bool mode)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, -1);
+
+    psTrace("psLib.db", PS_LOG_INFO, "calling mysql_autocommit(): %u", !mode);
+
+    // mode needs to be inverted as autocommits are the opposide of explicit
+    // transactions.
+    // the return value also needs to be inverted for the same reason.
+    // is it safe to assume my_bool always safely casts to bool?
+    return !(bool)mysql_autocommit(dbh->mysql, !mode);
+}
+
+bool psDBTransaction(psDB *dbh)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, -1);
+
+    bool status = p_psDBRunQuery(dbh, "START TRANSACTION");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to start a new transaction.");
+    }
+
+    return status;
+}
+
+bool psDBCommit(psDB *dbh)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, -1);
+
+    psTrace("psLib.db", PS_LOG_INFO, "calling myql_commit()");
+
+    // is it safe to assume my_bool always safely casts to bool?
+    // mysql_commit - Zero if successful. Non-zero if an error occurred.
+    return !(bool)mysql_commit(dbh->mysql);
+}
+
+bool psDBRollback(psDB *dbh)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, -1);
+
+    psTrace("psLib.db", PS_LOG_INFO, "calling myql_rollback()");
+
+    // is it safe to assume my_bool always safely casts to bool?
+    // mysql_rollback - Zero if successful. Non-zero if an error occurred.
+    return !(bool)mysql_rollback(dbh->mysql);
+}
+
+psU64 psDBAffectedRows(psDB *dbh)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, (psU64)-1);
+
+    // mysql_affected_rows() returns (my_ulonglong)-1 on error
+    return (psU64)mysql_affected_rows(dbh->mysql);
+}
+
+// database utility functions
+/*****************************************************************************/
+
+bool p_psDBRunQuery(psDB *dbh,
+                    const char *format,
+                    ...)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, false);
+    PS_ASSERT_PTR_NON_NULL(format, false);
+
+    psString query = NULL;
+
+    // Run query
+    va_list ap;
+    va_start(ap, format);
+    psStringAppendV(&query, format, ap);
+    va_end(ap);
+
+    psTrace("psLib.db", PS_LOG_INFO, "Executing SQL:\n%s", query);
+
+    if (mysql_real_query(dbh->mysql, query, (unsigned long)strlen(query)) !=0) {
+        psError(mysqlTopsErr(dbh->mysql), true, _("Failed to execute SQL query.  Error: %s"), mysql_error(dbh->mysql));
+        psFree(query);
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+long p_psDBRunQueryPrepared(psDB *dbh,
+                            const psArray *rowSet,
+                            const char *format,
+                            ...)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, -1);
+    PS_ASSERT_PTR_NON_NULL(rowSet, -1);
+    PS_ASSERT_PTR_NON_NULL(format, -1);
+
+    psString query = NULL;
+
+    // start lock on query cache
+    if (psMemGetThreadSafety()) {
+        pthread_mutex_lock(&preparedQueryMutex);
+    }
+
+    // initalize the prepared query cache
+    if (!preparedQuery) {
+        preparedQuery = psHashAlloc(10);
+        psMemSetPersistent(preparedQuery, true);
+    }
+
+    // generate query string
+    va_list ap;
+    va_start(ap, format);
+    psStringAppendV(&query, format, ap);
+    va_end(ap);
+
+    psTrace("psLib.db", PS_LOG_INFO, "Preparing SQL:\n%s", query);
+
+    // check the query cache
+    MYSQL_STMT **stmt = psHashLookup(preparedQuery, query);
+    if (!stmt) {
+        psTrace("psLib.db", PS_LOG_INFO, "statment is not in query cache");
+
+        // Prepare SQL statement
+        stmt = psAlloc(sizeof(MYSQL_STMT *));
+        *stmt = mysql_stmt_init(dbh->mysql);
+        if (!stmt) {
+            psAbort("mysql_stmt_init(), out of memory.");
+        }
+        if (mysql_stmt_prepare(*stmt, query, (unsigned long)strlen(query))) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to prepare query.  Error: %s", mysql_stmt_error(*stmt));
+            mysql_stmt_close(*stmt);
+            psFree(query);
+
+            // end lock on query cache
+            if (psMemGetThreadSafety()) {
+                pthread_mutex_unlock(&preparedQueryMutex);
+            }
+
+            return -1;
+        }
+
+        // add this statement to the cache
+        psHashAdd(preparedQuery, query, stmt);
+    } else {
+        psTrace("psLib.db", PS_LOG_INFO, "found statment in query cache");
+    }
+
+    // end lock on query cache
+    if (psMemGetThreadSafety()) {
+        pthread_mutex_unlock(&preparedQueryMutex);
+    }
+
+    psFree(query);
+
+    // how many place holders are in our query
+    psS32 paramCount = mysql_stmt_param_count(*stmt);
+
+    // structure large enough to hold one field of data per place holder
+    psDBMysqlRow *mysqlRow = psDBMysqlRowAlloc(paramCount);
+
+    // loop over rows
+    for (long j = 0; j < rowSet->n; j++) {
+        psMetadata *rowData = rowSet->data[j];
+
+        if (!psDBPackMySQLRow(mysqlRow, rowData)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to pack params into bind structure.");
+
+            psFree(mysqlRow);
+//            mysql_stmt_close(stmt);
+
+            return -1;
+        }
+
+        if (psTraceGetLevel("psLib.db") >= PS_LOG_INFO) {
+            psString binding = psMetadataConfigFormat(rowData);
+            psTrace("psLib.db", PS_LOG_INFO, "Binding:\n%s", binding);
+            psFree(binding);
+        }
+
+        if (mysql_stmt_bind_param(*stmt, mysqlRow->bind)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to bind params.  Error: %s", mysql_stmt_error(*stmt));
+
+            psFree(mysqlRow);
+//           mysql_stmt_close(stmt);
+
+            return -1;
+        }
+
+        if (mysql_stmt_execute(*stmt)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to execute prepared statement.  Error: %s", mysql_stmt_error(*stmt));
+
+            psFree(mysqlRow);
+//            mysql_stmt_close(stmt);
+
+            return -1;
+        }
+
+        // free temporary buffers
+        psDBMysqlRowRecycle(mysqlRow);
+
+    } // end loop over rows
+
+    psFree(mysqlRow);
+
+    // FYI mysql_stmt_affected_rows() must be called before a commit
+    long rowsAffected = mysql_stmt_affected_rows(*stmt);
+
+//    mysql_stmt_close(stmt);
+
+    return rowsAffected;
+}
+
+psArray *p_psDBFetchResult(psDB *dbh)
+{
+    PS_ASSERT_PTR_NON_NULL(dbh, NULL);
+
+    MYSQL_RES       *result;            // complete db result set
+    MYSQL_ROW       row;                // single row of db result set
+    MYSQL_FIELD     *field;             // field type info
+    my_ulonglong    rowCount;           // number of rows in db result set
+    unsigned int    fieldCount;         // number of fields in db result set
+    unsigned long   *fieldLength;       // field sizes
+    long            len;                // field length
+    psArray         *resultSet;         // return array
+    int             i;                  // field index
+    psMetadata      *md;                // a row
+    psU32           pType;              // psElemType of a field
+    psPtr           data;               // copy of result field
+
+    result = mysql_store_result(dbh->mysql);
+    if (!result) {
+        // no result set
+        fieldCount = mysql_field_count(dbh->mysql);
+
+        // we're going to exit no matter what after this point
+        mysql_free_result(result);
+
+        // if field count is zero the query should have returned no data.  If
+        // it's non-zero then something bad has happened.
+        if (fieldCount != 0) {
+            psError(mysqlTopsErr(dbh->mysql), true, "Query returned no data.  Error: %s", mysql_error(dbh->mysql));
+
+            return NULL;
+        }
+
+        return psArrayAlloc(0);
+    }
+
+    rowCount = mysql_num_rows(result);
+    // XXX has mysql's semantics changed?  If this is zero then why was a
+    // result set returned?
+    if (rowCount == 0) {
+        mysql_free_result(result);
+        // occording to popular opinion a query that succeeds but returns no
+        // data should return an empty array -- this is somewhat inefficent -JH
+        return psArrayAlloc(0);
+    }
+
+    // pre-allocate enough elements to hold the complete result set
+    // then reset n to 0 so elements are added from the beginning of
+    // the array
+    resultSet = psArrayAllocEmpty(rowCount);
+
+    field = mysql_fetch_fields(result);
+    fieldCount = mysql_num_fields(result);
+
+    psTrace("psLib.db", PS_LOG_INFO, "query returned %lld rows with %d fields",             rowCount, fieldCount);
+
+    while ((row = mysql_fetch_row(result))) {
+        // allocate new psMetadata to represent a row
+        md = psMetadataAlloc();
+
+        fieldLength = mysql_fetch_lengths(result);
+
+        for (i = 0; i < fieldCount; i++) {
+            // lookup MySQL column type
+            pType = psDBMySQLToPType(field[i].type, field[i].flags);
+            if (!pType) {
+                psError(PS_ERR_UNKNOWN, false, "Failed to lookup type.");
+                psFree(md);
+                mysql_free_result(result);
+                psFree(resultSet);
+                return NULL;
+            }
+
+            len = fieldLength[i];
+            if (len) {
+                // copy the data out of the result set struct
+                data = psAlloc(len + 1);
+                memcpy(data, row[i], len);
+                ((char*)data)[len] = '\0';
+            } else {
+                // if len is zero then the value is NULL
+                psTrace("psLib.db", PS_LOG_INFO, "database field name %s contains a NULL", field[i].name);
+                data = psDBGetPTypeNaN(pType);
+                if (!psMetadataAdd(md, PS_LIST_TAIL, field[i].name, pType, "", data)) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                psFree(data);
+                continue;
+            }
+
+            switch (pType) {
+            case PS_DATA_STRING:
+                if (!psMetadataAddStr(md, PS_LIST_TAIL, field[i].name, 0, "", data)) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_U8:
+                if (!psMetadataAddU8(md, PS_LIST_TAIL, field[i].name, 0, "",(psU8)atoll(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_U16:
+                if (!psMetadataAddU16(md, PS_LIST_TAIL, field[i].name, 0, "",(psU16)atoll(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_U32:
+                if (!psMetadataAddU32(md, PS_LIST_TAIL, field[i].name, 0, "",(psU32)atoll(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_U64:
+                if (!psMetadataAddU64(md, PS_LIST_TAIL, field[i].name, 0, "",(psU64)atoll(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_S8:
+                if (!psMetadataAddS8(md, PS_LIST_TAIL, field[i].name, 0, "", (psS8)atoll(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_S16:
+                if (!psMetadataAddS16(md, PS_LIST_TAIL, field[i].name, 0, "", (psS16)atoll(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_S32:
+                if (!psMetadataAddS32(md, PS_LIST_TAIL, field[i].name, 0, "", (psS32)atoll(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_S64:
+                if (!psMetadataAddS64(md, PS_LIST_TAIL, field[i].name, 0, "", (psS64)atoll(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_F32:
+                if (!psMetadataAddF32(md, PS_LIST_TAIL, field[i].name, 0, "", atof(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_F64:
+                if (!psMetadataAddF64(md, PS_LIST_TAIL, field[i].name, 0, "", atof(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_BOOL:
+                if (!psMetadataAddBool(md, PS_LIST_TAIL, field[i].name, 0, "", (bool)atoi(data))) {
+                    psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                    psFree(data);
+                    psFree(md);
+                    mysql_free_result(result);
+                    psFree(resultSet);
+                    return NULL;
+                }
+                break;
+            case PS_DATA_TIME:
+                // just pass NULL values through as there is currently no
+                // concept of a psTime with a NULL value
+                if (!(char *)data) {
+                    if (!psMetadataAddTime(md, PS_LIST_TAIL, field[i].name, 0, "", NULL)) {
+                        psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                        psFree(data);
+                        psFree(md);
+                        mysql_free_result(result);
+                        psFree(resultSet);
+                        return NULL;
+                    }
+                } else if (psStrcasestr(data, "0000-00-00 00:00:00")) {
+                    // look for 0000-00-00 00:00:00, which can't be parsed by
+                    // psTimeStrptime as the month/day are bogus
+                    if (!psMetadataAddTime(md, PS_LIST_TAIL, field[i].name, 0, "", NULL)) {
+                        psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                        psFree(data);
+                        psFree(md);
+                        mysql_free_result(result);
+                        psFree(resultSet);
+                        return NULL;
+                    }
+                } else {
+                    psTime *time = psTimeStrptime((char *)data, "%EY-%m-%d%t%T");
+                    if (!time) {
+                        psError(PS_ERR_UNKNOWN, true, "error parsing MySQL DateTime string");
+                        psFree(md);
+                        mysql_free_result(result);
+                        psFree(resultSet);
+                        return NULL;
+                    }
+                    if (!psMetadataAddTime(md, PS_LIST_TAIL, field[i].name, 0, "", time)) {
+                        psError(PS_ERR_UNKNOWN, false, "Failed to add item %s", field[i].name);
+                        psFree(data);
+                        psFree(md);
+                        mysql_free_result(result);
+                        psFree(resultSet);
+                        return NULL;
+                    }
+                    psFree(time);
+                }
+                break;
+                #if 0
+                // this is the procedure needed for stmt
+                MYSQL_TIME *myTime = (MYSQL_TIME *)&data;
+                // convert MYSQL_TIME to struct tm
+                struct tm tmTime;
+                tmTime.tm_year  = (int)myTime->year - 1900;
+                tmTime.tm_mon   = (int)myTime->month - 1;
+                tmTime.tm_mday  = (int)myTime->day;
+                tmTime.tm_hour  = (int)myTime->hour;
+                tmTime.tm_min   = (int)myTime->minute;
+                tmTime.tm_sec   = (int)myTime->second;
+                // assume for the time being that we don't have negative time
+                //(bool)myTime->neg
+                // currently unused by mysql nor does struct tm support it
+                // (unsigned long)myTime->second_part;
+                psTime *time = psTimeFromTM(&tmTime);
+                psMetadataAddTime(md, PS_LIST_TAIL, field[i].name, 0, "",
+                                  time);
+                psFree(time);
+                #endif
+
+            default:
+                psError(PS_ERR_BAD_PARAMETER_TYPE , true,
+                        "field name: %s FIXME: Only type of "
+                        "PS_DATA_U8 (PS_DATA_U8), "
+                        "PS_DATA_U16 (PS_DATA_U16), "
+                        "PS_DATA_U32 (PS_DATA_U32), "
+                        "PS_DATA_U64 (PS_DATA_U64), "
+                        "PS_DATA_S8 (PS_DATA_S8), "
+                        "PS_DATA_S16 (PS_DATA_S16), "
+                        "PS_DATA_S32 (PS_DATA_S32), "
+                        "PS_DATA_S64 (PS_DATA_S64), "
+                        "PS_DATA_F32 (PS_DATA_F32), "
+                        "PS_DATA_F64 (PS_DATA_F64), "
+                        "PS_DATA_BOOL (PS_DATA_BOOL), "
+                        "PS_DATA_STRING "
+                        "and PS_DATA_TIME are supported.", field[i].name);
+                psFree(data);
+                psFree(md);
+                mysql_free_result(result);
+                psFree(resultSet);
+                return NULL;
+            }
+
+            psFree(data);
+        }
+
+        if (psTraceGetLevel("psLib.db") >= PS_LOG_INFO) {
+            psString rowString = psMetadataConfigFormat(md);
+            psTrace("psLib.db", PS_LOG_INFO, "adding row to result set:\n %s", rowString);
+            psFree(rowString);
+        }
+
+        // add row to result set
+        psArrayAdd(resultSet, 0, md);
+        psFree(md);
+    }
+
+    mysql_free_result(result);
+
+
+    return resultSet;
+}
+
+static inline bool psDBPackMySQLRow(psDBMysqlRow *mysqlRow,
+                                    const psMetadata *values)
+{
+    PS_ASSERT_PTR_NON_NULL(mysqlRow, false);
+    PS_ASSERT_PTR_NON_NULL(values, false);
+
+    mysqlType       *mType;             // type tmp variable
+    static bool     isNull = true;      // used in a MYSQL_BIND to indicate NULL
+
+    MYSQL_BIND *bind = mysqlRow->bind;
+
+    // row iterator
+    // check size of values == paramCount ?
+    psListIterator *cursor = psListIteratorAlloc(values->list, 0, false);
+
+    // loop over fields
+    psMetadataItem *item = NULL;              // field in row
+    for (psU32 i = 0; (item = psListGetAndIncrement(cursor)); i++) {
+        // lookup pType -> mysql type
+        mType = psDBPTypeToMySQL(item->type);
+
+        bind[i].buffer_type = mType->type;
+        bind[i].is_unsigned = mType->isUnsigned;
+
+        psFree(mType);
+
+        // input data length is determined by the MYSQL_TYPE_* unless it's a
+        // string
+        switch (item->type) {
+        case PS_DATA_U8: {
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.U8;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U8)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_U16: {
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.U16;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U16)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_U32: {
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.U32;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U32)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_U64: {
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.U64;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U64)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_S8: {
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.S8;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S8)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_S16: {
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.S16;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S16)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_S32: {
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.S32;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S32)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_S64: {
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.S64;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S64)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_F32: {
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.F32;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F32)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_F64: {
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.F64;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F64)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_BOOL: {
+                // XXX: ASC HACK NOTE (2005/06/03): set extreme bytes to the
+                // boolean character value.  sizeof(bool)==4 which triggers
+                // an endianess issue in the MySQL conversion (reading only 1
+                // byte), on Macintosh hardware (and maybe others?)
+                unsigned int c  = (unsigned int)item->data.B;
+                item->data.S32  = (unsigned int)((c<<24) | c);
+                bind[i].length  = 0;
+                bind[i].buffer  = &item->data.B;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.B)
+                                  ? (my_bool *)&isNull
+                                  : NULL;
+                break;
+            }
+        case PS_DATA_STRING: {
+                // convert NaNs to NULL and set the buffer_length for strings
+
+                if (item->data.str) {
+                    // will handle the case of "" as a NULL database value
+                    bind[i].buffer_length = (unsigned long)strlen(item->data.str);
+                    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;
+                } else {
+                    // handles the case of NULL as a NULL database value
+                    bind[i].buffer_length = 0;
+                    bind[i].length  = &bind[i].buffer_length;
+                    bind[i].buffer  = NULL;
+                    bind[i].is_null = (my_bool *)&isNull;
+                }
+                break;
+            }
+        case PS_DATA_TIME: {
+                // XXX we're abusing the comment field of the metadata item
+                // here as we need to have a buffer that exists outside this
+                // functions scope without leaking memory make a copy of the
+                // psTime so we don't modify user data when we try to do the
+                // conversion
+                if (item->data.V) {
+                    psTime *time = (psTime *)item->data.V;
+                    struct tm *tmTime = psTimeToTM(time);
+                    if (!tmTime) {
+                        psError(PS_ERR_UNKNOWN, false, _("failed to convert psTime to struct tm"));
+                        psFree(cursor);
+
+                        return false;
+                    }
+
+                    // XXX it wouldn't hurt to make this conversion it's own
+                    // function myTime is used as the 'buffer' so it doesn't
+                    // have to be free'd
+                    MYSQL_TIME *myTime = psAlloc(sizeof(MYSQL_TIME));
+                    myTime->year    = (unsigned int)tmTime->tm_year + 1900;
+                    myTime->month   = (unsigned int)tmTime->tm_mon + 1;
+                    myTime->day     = (unsigned int)tmTime->tm_mday;
+                    myTime->hour    = (unsigned int)tmTime->tm_hour;
+                    myTime->minute  = (unsigned int)tmTime->tm_min;
+                    myTime->second  = (unsigned int)tmTime->tm_sec;
+                    // assume for the time being that we don't have negative
+                    // time as ISO8601 doesn't support dates prior to 0
+                    myTime->neg     = (my_bool)false;
+                    // currently unused by mysql
+                    myTime->second_part  = (unsigned long)time->nsec;
+                    psFree(tmTime);
+
+                    bind[i].buffer  = myTime;
+                    bind[i].buffer_length = sizeof(MYSQL_TIME);
+                    bind[i].length  = &bind[i].buffer_length;
+                    bind[i].is_null = NULL;
+                } else {
+                    // handles the case of NULL as a NULL database value
+                    bind[i].buffer_length = 0;
+                    bind[i].length  = &bind[i].buffer_length;
+                    bind[i].buffer  = NULL;
+                    bind[i].is_null = (my_bool *)&isNull;
+                }
+                break;
+            }
+        default: {
+                psError(PS_ERR_BAD_PARAMETER_TYPE , true,
+                        "FIXME: Unsupported data type");
+                psFree(cursor);
+
+                return false;
+                break;                  // unreachable
+            }
+        }
+    }
+
+    psFree(cursor);
+
+    return true;
+}
+
+static psDBMysqlRow *psDBMysqlRowAlloc(psU32 paramCount)
+{
+    psDBMysqlRow *row = psAlloc(sizeof(psDBMysqlRow));
+    psMemSetDeallocator(row, (psFreeFunc)psDBMysqlRowFree);
+
+    row->bind = psAlloc(sizeof(MYSQL_BIND) * paramCount);
+    memset(row->bind, 0, sizeof(MYSQL_BIND) * paramCount);
+    row->n = paramCount;
+
+    return row;
+}
+
+static void psDBMysqlRowFree(psDBMysqlRow *mysqlRow)
+{
+    PS_ASSERT_PTR_NON_NULL(mysqlRow, );
+
+    MYSQL_BIND *bind = mysqlRow->bind;
+
+    psDBMysqlRowRecycle(mysqlRow);
+
+    psFree(bind);
+}
+
+static void psDBMysqlRowRecycle(psDBMysqlRow *mysqlRow)
+{
+    PS_ASSERT_PTR_NON_NULL(mysqlRow, );
+
+    MYSQL_BIND *bind = mysqlRow->bind;
+
+    for (psU32 i = 0; i < mysqlRow->n; i++) {
+        // buffer_length is only defined for pointers to character buffers
+        // primitive types will have this value set to zero.
+        if (bind[i].buffer_length) {
+            psFree(bind[i].buffer);
+        }
+    }
+
+    memset(bind, '\0', sizeof(MYSQL_BIND) * mysqlRow->n);
+}
+
+
+// SQL generation functions
+/*****************************************************************************/
+
+static psString psDBGenerateCreateTableSQL(const char *tableName,
+        const psMetadata *table)
+{
+    PS_ASSERT_PTR_NON_NULL(tableName, NULL);
+    PS_ASSERT_PTR_NON_NULL(table, NULL);
+
+    char            *query = NULL;      // complete query
+    psMetadataItem  *item;              // column description
+    psListIterator  *cursor;            // column iterator
+    char            *colType;           // type lookup table
+
+    // Begin to create SQL string to create table
+    psStringAppend(&query, "CREATE TABLE %s (", tableName);
+
+    // Set list iterator at head of list
+    cursor = psListIteratorAlloc(table->list, 0, false);
+
+    // find column name and type
+    while ((item = psListGetAndIncrement(cursor))) {
+        switch (item->type) {
+        case PS_DATA_U8:
+        case PS_DATA_U16:
+        case PS_DATA_U32:
+        case PS_DATA_U64:
+        case PS_DATA_S8:
+        case PS_DATA_S16:
+        case PS_DATA_S32:
+        case PS_DATA_S64:
+        case PS_DATA_F32:
+        case PS_DATA_F64:
+        case PS_DATA_BOOL:
+        case PS_DATA_TIME: {
+                // + column name + _ + column type
+                colType = psDBPTypeToSQL(item->type);
+                psStringAppend(&query, "%s %s", item->name, colType);
+                psFree(colType);
+                break;
+            }
+        case PS_DATA_STRING: {
+                // + column name + _ + varchar( + length + )
+                psStringAppend(&query, "%s VARCHAR(%s)", item->name, item->data.str);
+                break;
+            }
+        default: {
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        "FIXME: Unsupported data type %d", item->type);
+
+                psFree(query);
+                psFree(cursor);
+
+                return NULL;
+                break;                  // unreachable
+            }
+        }
+
+        if (psStrcasestr(item->comment, "AUTO_INCREMENT")) {
+            psStringAppend(&query, " %s", "AUTO_INCREMENT");
+        }
+        if (psStrcasestr(item->comment, "Unique")) {
+            psStringAppend(&query, " %s", "UNIQUE");
+        }
+        if (psStrcasestr(item->comment, "NOT NULL")) {
+            psStringAppend(&query, " %s", "NOT NULL");
+        }
+
+        // add a , after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+
+    // find database indexes
+    // check for Primary Keys first as they have to be part of the same Primary
+    // Key stmt
+    // Reset iterator to head of list
+    psListIteratorSet(cursor, 0);
+    psArray *pKeys = psArrayAllocEmpty(1);
+    while ((item = psListGetAndIncrement(cursor))) {
+        if (psStrcasestr(item->comment, "Primary Key")) {
+            psArrayAdd(pKeys, 0, item->name);
+        }
+    }
+    if (psArrayLength(pKeys)) {
+        psStringAppend(&query, ", PRIMARY KEY(");
+        for (int i = 0; i < psArrayLength(pKeys); i++) {
+            if (i < 1) {
+                psStringAppend(&query, "%s", (char *)pKeys->data[i]);
+            } else {
+                psStringAppend(&query, ", %s", (char *)pKeys->data[i]);
+            }
+        }
+        psStringAppend(&query, ")");
+    }
+    psFree(pKeys);
+
+    // Reset iterator to head of list
+    psListIteratorSet(cursor, 0);
+    // look for regular keys
+    while ((item = psListGetAndIncrement(cursor))) {
+        // it's just a regular key if it matchs "Key" but not "Primary Key"
+        if (psStrcasestr(item->comment, "Key")
+                && (psStrcasestr(item->comment, "Primary Key") == NULL)) {
+            psStringAppend(&query, ", KEY(%s)", item->name);
+        } else if (psStrcasestr(item->comment, "AUTO_INCREMENT")) {
+            // this needs to be recognized as a key if it wasn't already
+            psStringAppend(&query, ", KEY(%s)", item->name);
+        }
+    }
+
+
+    // Reset iterator to head of list
+    psListIteratorSet(cursor, 0);
+    // look for indexes
+    while ((item = psListGetAndIncrement(cursor))) {
+        // don't compile a regex unless we have too
+
+        if (psStrcasestr(item->comment, "UINDEX")) {
+            // look for UINDEXs before INDEXs
+            regex_t         myregex;
+            regmatch_t      mymatch[2];     // match + 1 sub strings
+            int             errbuf_size = 1024;
+            char            errbuf[errbuf_size];
+            char            *pattern = "UINDEX[:space:]*[(]([^)]*)";
+
+            int status = regcomp(&myregex, pattern, REG_EXTENDED|REG_ICASE);
+            if (status != 0) {
+                regerror(status, &myregex, errbuf, errbuf_size);
+                psError(PS_ERR_UNKNOWN, true, "regcomp() failed: %s", errbuf);
+                psFree(query);
+                psFree(cursor);
+                return NULL;
+            }
+
+            psTrace("psLib.db", 10, "trying regex pattern: %s against string: %s", pattern, item->comment);
+            status = regexec(&myregex, item->comment, 2, mymatch, 0);
+            if (status != 0) {
+                regerror(status, &myregex, errbuf, errbuf_size);
+                psError(PS_ERR_UNKNOWN, true, "regexec() failed: %s", errbuf);
+                psFree(query);
+                psFree(cursor);
+                return NULL;
+            }
+
+            regfree(&myregex);
+
+            // sub string 1: uindex(.*)
+            size_t matchStart = (size_t)mymatch[1].rm_so;
+            size_t matchEnd   = (size_t)mymatch[1].rm_eo;
+            size_t matchLength = matchEnd - matchStart;
+
+            if (matchStart == -1) {
+                psError(PS_ERR_UNKNOWN, true, "substring 1 failed to match");
+                psFree(query);
+                psFree(cursor);
+                return NULL;
+            }
+
+            psString index = psStringNCopy(item->comment + matchStart, matchLength);
+            psTrace("psLib.db", 10, "regex $1 matched: %s", index);
+            psStringAppend(&query, ", UNIQUE KEY(%s)", index);
+            psFree(index);
+        } else if (psStrcasestr(item->comment, "INDEX")) {
+            regex_t         myregex;
+            regmatch_t      mymatch[2];     // match + 1 sub strings
+            int             errbuf_size = 1024;
+            char            errbuf[errbuf_size];
+            // don't accidentally match uindex
+            char            *pattern = "[:space:]*INDEX[:space:]*[(]([^)]*)";
+
+            int status = regcomp(&myregex, pattern, REG_EXTENDED|REG_ICASE);
+            if (status != 0) {
+                regerror(status, &myregex, errbuf, errbuf_size);
+                psError(PS_ERR_UNKNOWN, true, "regcomp() failed: %s", errbuf);
+                psFree(query);
+                psFree(cursor);
+                return NULL;
+            }
+
+            psTrace("psLib.db", 10, "trying regex pattern: %s against string: %s", pattern, item->comment);
+            status = regexec(&myregex, item->comment, 2, mymatch, 0);
+            if (status != 0) {
+                regerror(status, &myregex, errbuf, errbuf_size);
+                psError(PS_ERR_UNKNOWN, true, "regexec() failed: %s", errbuf);
+                psFree(query);
+                psFree(cursor);
+                return NULL;
+            }
+
+            regfree(&myregex);
+
+            // sub string 1: index(.*)
+            size_t matchStart = (size_t)mymatch[1].rm_so;
+            size_t matchEnd   = (size_t)mymatch[1].rm_eo;
+            size_t matchLength = matchEnd - matchStart;
+
+            if (matchStart == -1) {
+                psError(PS_ERR_UNKNOWN, true, "substring 1 failed to match");
+                psFree(query);
+                psFree(cursor);
+                return NULL;
+            }
+
+            psString index = psStringNCopy(item->comment + matchStart, matchLength);
+            psTrace("psLib.db", 10, "regex $1 matched: %s", index);
+            psStringAppend(&query, ", INDEX(%s)", index);
+            psFree(index);
+        }
+    }
+
+    // Reset iterator to head of list
+    psListIteratorSet(cursor, 0);
+    // look for foreign keys after all other key types
+    while ((item = psListGetAndIncrement(cursor))) {
+        // don't compile a regex unless we have too
+        if (psStrcasestr(item->comment, "FKEY") == NULL) {
+            continue;
+        }
+        // find foreign key and references
+        regex_t         myregex;
+        regmatch_t      mymatch[3];     // match + 2 sub strings
+        int             errbuf_size = 1024;
+        char            errbuf[errbuf_size];
+        char            *pattern = "FKEY(.*)[:space:]*REF(.*)";
+
+        int status = regcomp(&myregex, pattern, REG_EXTENDED|REG_ICASE);
+        if (status != 0) {
+            regerror(status, &myregex, errbuf, errbuf_size);
+            psError(PS_ERR_UNKNOWN, true, "regcomp() failed: %s", errbuf);
+            psFree(query);
+            psFree(cursor);
+            return NULL;
+        }
+
+        psTrace("psLib.db", 10, "trying regex pattern: %s against string: %s", pattern, item->comment);
+        status = regexec(&myregex, item->comment, 3, mymatch, 0);
+        if (status != 0) {
+            regerror(status, &myregex, errbuf, errbuf_size);
+            psError(PS_ERR_UNKNOWN, true, "regexec() failed: %s", errbuf);
+            psFree(query);
+            psFree(cursor);
+            return NULL;
+        }
+
+        regfree(&myregex);
+
+        // sub string 1: foreign(.*)
+        size_t matchStart = (size_t)mymatch[1].rm_so;
+        size_t matchEnd   = (size_t)mymatch[1].rm_eo;
+        size_t matchLength = matchEnd - matchStart;
+
+        if (matchStart == -1) {
+            psError(PS_ERR_UNKNOWN, true, "substring 1 failed to match");
+            psFree(query);
+            psFree(cursor);
+            return NULL;
+        }
+
+        psString fkey = psStringNCopy(item->comment + matchStart, matchLength);
+        psTrace("psLib.db", 10, "regex $1 matched: %s", fkey);
+        psStringAppend(&query, ", FOREIGN KEY %s", fkey);
+        psFree(fkey);
+
+        // sub string 2: references(.*)
+        matchStart = (size_t)mymatch[2].rm_so;
+        matchEnd   = (size_t)mymatch[2].rm_eo;
+        matchLength = matchEnd - matchStart;
+
+        if (matchStart == -1) {
+            psError(PS_ERR_UNKNOWN, true, "substring 2 failed to match");
+            psFree(query);
+            psFree(cursor);
+            return NULL;
+        }
+
+        psString refs = psStringNCopy(item->comment + matchStart, matchLength);
+        psTrace("psLib.db", 10, "regex $2 matched: %s", refs);
+        psStringAppend(&query, " REFERENCES %s", refs);
+        psFree(refs);
+    }
+
+    psFree(cursor);
+
+    // end column types + table type
+    psStringAppend(&query, ") ENGINE=innodb");
+
+    return query;
+}
+
+static psString psDBGenerateSelectRowSQL(const char *tableName,
+        const char *col,
+        const psMetadata *where,
+        psU64 limit)
+{
+    PS_ASSERT_PTR_NON_NULL(tableName, NULL);
+
+    char            *query = NULL;
+    char            *whereSQL;
+    char            *limitString;
+
+    // select all columns if col is NULL
+    if (col) {
+        psStringAppend(&query, "SELECT %s FROM %s", col, tableName);
+    } else {
+        psStringAppend(&query, "SELECT * FROM %s", tableName);
+    }
+
+    // select all rows if where is NULL
+    if (where) {
+        whereSQL = psDBGenerateWhereSQL(where, tableName);
+        if (!whereSQL) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+            psFree(query);
+
+            return NULL;
+        }
+        psStringAppend(&query, " %s", whereSQL);
+        psFree(whereSQL);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    return query;
+}
+
+psString psDBGenerateLimitSQL(psU64 limit)
+{
+    psString query = NULL;
+
+    psString limitString = psDBIntToString(limit);
+    psStringAppend(&query, " LIMIT %s", limitString);
+    psFree(limitString);
+
+    return query;
+}
+
+static psString psDBGenerateInsertRowSQL(const char *tableName,
+        const psMetadata *row)
+{
+    PS_ASSERT_PTR_NON_NULL(tableName, NULL);
+    PS_ASSERT_PTR_NON_NULL(row, NULL);
+
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    // Start building query string
+    psStringAppend(&query, "INSERT INTO %s (", tableName);
+
+    cursor = psListIteratorAlloc(row->list, 0, false);
+
+    // get field names
+    while ((item = psListGetAndIncrement(cursor))) {
+        psStringAppend(&query, item->name);
+
+        // + , + _ between every field name
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    // end of field names
+    psStringAppend(&query, ") VALUES (");
+
+    psListIteratorSet(cursor, 0);
+
+    // create value place holders
+    while ((item = psListGetAndIncrement(cursor))) {
+        psStringAppend(&query, "?");
+
+        // + ", " between every place holder
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    psFree(cursor);
+
+    // end of values
+    psStringAppend(&query, ")");
+
+    return query;
+}
+
+static psString psDBGenerateUpdateRowSQL(const char *tableName,
+        const psMetadata *where,
+        const psMetadata *values)
+{
+    PS_ASSERT_PTR_NON_NULL(tableName, NULL);
+    PS_ASSERT_PTR_NON_NULL(values, NULL);
+
+    char            *query = NULL;
+    char            *setSQL;
+    char            *whereSQL;
+
+    // Create set SQL substring
+    setSQL = psDBGenerateSetSQL(values);
+    if (!setSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, _("SQL substring generation failed."));
+        return NULL;
+    }
+
+    // Create where SQL substring
+    whereSQL = psDBGenerateWhereSQL(where, tableName);
+    if (!whereSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, _("SQL substring generation failed."));
+        return NULL;
+    }
+
+    // Append substring to SQL update string
+    psStringAppend(&query, "UPDATE %s %s %s", tableName, setSQL, whereSQL);
+    psFree(setSQL);
+    psFree(whereSQL);
+
+    return query;
+}
+
+static psString psDBGenerateDeleteRowSQL(const char *tableName,
+        const psMetadata *where,
+        unsigned long long limit)
+{
+    PS_ASSERT_PTR_NON_NULL(tableName, NULL);
+
+    char            *query = NULL;
+    char            *whereSQL;
+    char            *limitString;
+
+    // delete all rows if where is NULL
+    if (!where) {
+        psStringAppend(&query, "TRUNCATE TABLE %s", tableName);
+        return query;
+    }
+
+    // Generate where SQL substring
+    whereSQL = psDBGenerateWhereSQL(where, tableName);
+    if (!whereSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, _("SQL substring generation failed."));
+        return NULL;
+    }
+
+    psStringAppend(&query, "DELETE FROM %s %s", tableName, whereSQL);
+
+    // Complete delete SQL command string
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        limitString = psDBIntToString(limit);
+        psStringAppend(&query, " LIMIT %s", limitString);
+        psFree(limitString);
+    }
+    psFree(whereSQL);
+
+    return query;
+}
+
+psString psDBGenerateWhereSQL(const psMetadata *where, const char *tableName)
+{
+    PS_ASSERT_PTR_NON_NULL(where, NULL);
+
+    psString search = psDBGenerateWhereConditionSQL(where, tableName);
+    if (search) {
+        psStringPrepend(&search, "WHERE ");
+    }
+
+    return search;
+}
+
+psString psDBGenerateWhereConditionSQL(const psMetadata *where, const char *tableName)
+{
+    PS_ASSERT_PTR_NON_NULL(where, NULL);
+
+    psString query = NULL;
+
+    // we need to know if an item is 'MULTI' so we have to march through the
+    // list by key name and not by items
+    psList *keys = psHashKeyList(where->hash);
+    psListIterator *cursor = psListIteratorAlloc(keys, 0, false);
+
+    // find column name and match pattern
+    psString itemName = NULL;
+    while ((itemName = psListGetAndIncrement(cursor))) {
+        psMetadataItem *item = psMetadataLookup(where, itemName);
+
+        if (item->type == PS_DATA_METADATA_MULTI) {
+            char *logicalOp = "AND";
+
+            // scan through the list and change the logicalOp joining
+            // conditionals together to "OR" if a comment of "==" is found
+            psListIterator *mCursor = psListIteratorAlloc(item->data.list, 0,
+                                      false);
+            psMetadataItem *mItem = NULL;
+            while ((mItem = psListGetAndIncrement(mCursor))) {
+                if (mItem->comment && psStrcasestr(mItem->comment, "==")) {
+                    logicalOp = "OR";
+                    break;
+                }
+            }
+
+            // "(" + conditional [ + " AND/OR " + conditional ] + ")"
+            // reset the iterator
+            psListIteratorSet(mCursor, 0);
+            psStringAppend(&query, "(");
+            while ((mItem = psListGetAndIncrement(mCursor))) {
+                psString conditional = psDBGenerateConditionalSQL(mItem, tableName);
+                if (!conditional) {
+                    psError(PS_ERR_UNKNOWN, false,
+                            "SQL conditional generation failed.");
+                    psFree(mCursor);
+                    psFree(cursor);
+                    psFree(keys);
+                    psFree(query);
+                    return NULL;
+                }
+
+                psStringAppend(&query, "%s", conditional);
+                psFree(conditional);
+
+                // + " AND/OR "
+                if (!mCursor->offEnd) {
+                    psStringAppend(&query, " %s ", logicalOp);
+                }
+
+            }
+            psFree(mCursor);
+
+            psStringAppend(&query, ")");
+        } else {
+            psString conditional = psDBGenerateConditionalSQL(item, tableName);
+            if (!conditional) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "SQL conditional generation failed.");
+                psFree(cursor);
+                psFree(keys);
+                psFree(query);
+                return NULL;
+            }
+
+            psStringAppend(&query, "%s", conditional);
+            psFree(conditional);
+        }
+
+        // + " and " after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, " AND ");
+        }
+    }
+
+    //    psFree(cursor);
+    psFree(keys);
+
+    return query;
+}
+
+static psString psDBGenerateSetSQL(const psMetadata *set
+                                  )
+{
+    PS_ASSERT_PTR_NON_NULL(set
+                           , NULL);
+
+    psString query = psStringCopy("SET ");
+
+    psListIterator *cursor = psListIteratorAlloc(set
+                             ->list, 0, false);
+
+    // find column name
+    psMetadataItem *item = NULL;
+    while ((item = psListGetAndIncrement(cursor))) {
+        // + column name + _ + = + _ + ?
+        psStringAppend(&query, "%s = ?", item->name);
+
+        // + ", " after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ",  ");
+        }
+    }
+
+    psFree(cursor);
+
+    return query;
+}
+
+typedef enum {
+    PS_DB_OP_EQ,
+    PS_DB_OP_LT,
+    PS_DB_OP_GT,
+    PS_DB_OP_LE,
+    PS_DB_OP_GE,
+} psDBOpValue;
+
+# define PS_DB_FLT_PAD (FLT_EPSILON * 10)
+# define PS_DB_DBL_PAD (DBL_EPSILON * 10)
+
+static psString psDBGenerateConditionalSQL(const psMetadataItem *item, const char *tableName)
+{
+    PS_ASSERT_PTR_NON_NULL(item, NULL);
+
+    psString query = NULL;
+
+    // stringify the psMetadataItem into a SQL search specification
+    // XXX we're making a big assumption here that the MySQL server handles
+    // floating point in the exact same way as the client.  This is a bit scary
+    // as MySQL uses native types on the server end.  In theory all IEEE754
+    // math is the same but know that isn't always the case.  At least forcing
+    // the comparison to be done on the server provides some consistency
+    // between clients on different archs.
+
+    // if tableName is specified prepend it to the item name
+    psString itemName = NULL;
+    if (tableName) {
+        psStringAppend(&itemName, "%s.%s", tableName, item->name);
+    } else {
+        psStringAppend(&itemName, "%s", item->name);
+    }
+
+    // select the by of comparasion to use.  currently the comparision
+    // op value is only used by PS_DATA_S32 & PS_DATA_TIME
+
+    // default to exact match ('=')
+    char *opStr = "=";
+    psDBOpValue op = PS_DB_OP_EQ;
+    if (item->comment) {
+        // arbitrary semantic, precedence is: >=, <=, >, <, = (default)
+        if (strstr(item->comment, ">=")) {
+            opStr = ">=";
+            op = PS_DB_OP_GE;
+        } else if (strstr(item->comment, "<=")) {
+            opStr = "<=";
+            op = PS_DB_OP_LE;
+        } else if (strstr(item->comment, ">")) {
+            opStr = ">";
+            op = PS_DB_OP_GT;
+        } else if (strstr(item->comment, "<")) {
+            opStr = "<";
+            op = PS_DB_OP_LT;
+        }
+    }
+
+    // XXX why are >, < searches not supported here????
+    // fix this immediately!!
+    // for datetime comparisons, we need to use single-quotes around the string value:
+    // where dateobs < '2002-09-06' and dateobs > '2002-09-05,15:40:22'
+
+    switch (item->type) {
+    case PS_DATA_S8:
+    case PS_DATA_S16:
+    case PS_DATA_S32:
+        // the raw opStr is good enough in the query
+        psStringAppend(&query, "%s %s %d", itemName, opStr, (int)(item->data.S32));
+        break;
+    case PS_DATA_S64:
+        // the raw opStr is good enough in the query
+        psStringAppend(&query, "%s %s %" PRId64, itemName, opStr, item->data.S64);
+        break;
+    case PS_DATA_U8:
+    case PS_DATA_U16:
+    case PS_DATA_U32:
+        psStringAppend(&query, "%s %s %u", itemName, opStr, (unsigned int)(item->data.U32));
+        break;
+    case PS_DATA_U64:
+        // the raw opStr is good enough in the query
+        psStringAppend(&query, "%s %s %" PRIu64, itemName, opStr, item->data.U64);
+        break;
+    case PS_DATA_F32:
+        // need to handle floating-point round-off issues (use a padding of 10*FLT_EPSILON)
+        switch (op) {
+        case PS_DB_OP_EQ:
+            psStringAppend(&query, "(ABS(%s - %.8f) < %.8f)", itemName, (float)(item->data.F32), PS_DB_FLT_PAD);
+            break;
+        case PS_DB_OP_LE:
+        case PS_DB_OP_LT:
+            // A < B becomes A < B + epsilon
+            psStringAppend(&query, "(%s < %.8f + %.8f)", itemName, (float)(item->data.F32), PS_DB_FLT_PAD);
+            break;
+        case PS_DB_OP_GE:
+        case PS_DB_OP_GT:
+            // A > B becomes A > B - epsilon
+            psStringAppend(&query, "(%s > %.8f - %.8f)", itemName, (float)(item->data.F32), PS_DB_FLT_PAD);
+            break;
+        }
+        break;
+    case PS_DATA_F64:
+        // need to handle floating-point round-off issues (use a padding of 10*FLT_EPSILON)
+        switch (op) {
+        case PS_DB_OP_EQ:
+            psStringAppend(&query, "(ABS(%s - %.17f) < %.17f)", itemName, (float)(item->data.F64), PS_DB_DBL_PAD);
+            break;
+        case PS_DB_OP_LE:
+        case PS_DB_OP_LT:
+            // A < B becomes A < B + epsilon
+            psStringAppend(&query, "(%s < %.17f + %.17f)", itemName, (float)(item->data.F64), PS_DB_DBL_PAD);
+            break;
+        case PS_DB_OP_GE:
+        case PS_DB_OP_GT:
+            // A > B becomes A > B - epsilon
+            psStringAppend(&query, "(%s > %.17f - %.17f)", itemName, (float)(item->data.F64), PS_DB_DBL_PAD);
+            break;
+        }
+        break;
+    case PS_DATA_BOOL:
+        switch (op) {
+        case PS_DB_OP_EQ:
+            psStringAppend(&query, "%s = %d", itemName, (int)(item->data.B));
+            break;
+        default:
+            psError(PS_ERR_UNKNOWN, true, "NULL bool can't be compared with any operator other than '=='");
+            psFree(itemName);
+            return NULL;
+        }
+        break;
+    case PS_DATA_STRING:
+        // XXX EAM : probably need to add some regex-like operations here
+        // + column name + _ + like + _ + ' + value + '
+        // check for NULL and empty ("") strings
+        if (item->data.V == NULL || *item->data.str == '\0') {
+            psStringAppend(&query, "%s IS NULL", itemName);
+        } else {
+            if (item->comment && psStrcasestr(item->comment, "LIKE")) {
+                // XXX ASC NOTE: we should have a better match for
+                // char & varchar columns than this.  LIKE is OK for
+                // very large TEXT columns that really shouldn't be
+                // used in a where clause...
+                psStringAppend(&query, "%s LIKE '%s'", itemName, item->data.str);
+            } else {
+                psStringAppend(&query, "%s = '%s'", itemName, item->data.str);
+            }
+        }
+        break;
+    case PS_DATA_TIME: {
+            if (item->data.V) {
+                psString timeStr = psTimeToISO(item->data.V);
+                psStringAppend(&query, "%s %s '%s'", itemName, opStr, timeStr);
+                psFree(timeStr);
+            } else if (strstr(opStr, "=")) {
+                psStringAppend(&query, "%s IS NULL", itemName);
+            } else {
+                psError(PS_ERR_UNKNOWN, true, "psTime comparison value is NULL: A NULL psTime value can't be compared with any operator other than '=='");
+                psFree(itemName);
+                return NULL;
+            }
+
+            break;
+        }
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Fixme: Unsupported data type");
+        psFree(itemName);
+
+        return NULL;
+    }
+
+    psFree(itemName);
+
+    return query;
+}
+
+
+// lookup table functions
+/*****************************************************************************/
+
+static psElemType psDBMySQLToPType(enum enum_field_types type,
+                                   unsigned int flags)
+{
+    psHash          *mysqlToSQLTable;   // type lookup table
+    psHash          *sqlToPSTable;      // type lookup table
+    char            *key;               // hash tmp value
+    char            *value;             // hash tmp value
+    psString        sqlType;            // copy of lookup table result
+    psU32           pType;              // psElemType of a field
+
+    mysqlToSQLTable = psDBMySQLToSQLTableGet();
+
+    // lookup MySQL column type
+    key     = psDBIntToString((psU64)type);
+    sqlType = psMemIncrRefCounter(psHashLookup(mysqlToSQLTable, key));
+    psFree(key);
+
+    if (!sqlType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+        return 0;
+    }
+
+    // MySQL column types can not be directly translated to PS
+    // types as the the mysql types do not tell you if the value is
+    // signed or unsigned.  The result is this ugly conversion from
+    // mysql -> ascii -> ptype
+    // XXX it appears that TIMESTAMP fields get marked as being unsigned...
+    if ((flags & UNSIGNED_FLAG) && strncmp(sqlType, "DATETIME", 9)) {
+        psString new = NULL;
+        psStringAppend(&new, "%s UNSIGNED", sqlType);
+        psFree(sqlType);
+        sqlType = new;
+    }
+
+    //psTrace("psLib.db", 9, "sqlType=[%s]\n", sqlType );
+
+    // convert MySQL type to PS type
+    sqlToPSTable = psDBSQLToPTypeTableGet();
+    value = psHashLookup(sqlToPSTable, sqlType);
+
+    if (!value) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup of %s failed.", sqlType);
+        psFree(sqlType);
+        return 0;
+    }
+    psFree(sqlType);
+
+    pType = (psU32)atol(value);
+
+    return pType;
+}
+
+static psString psDBPTypeToSQL(psElemType pType)
+{
+    psHash          *pTypeToSQLTable;   // type lookup table
+    char            *key;               // hash tmp value
+    char            *sqlType;             // hash tmp value
+
+    pTypeToSQLTable = psDBPTypeToSQLTableGet();
+
+    key = psDBIntToString((psU64)pType);
+    sqlType = psHashLookup(pTypeToSQLTable, key);
+    psFree(key);
+
+    if (!sqlType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return NULL;
+    }
+
+    psMemIncrRefCounter(sqlType);
+
+    return sqlType;
+}
+
+static mysqlType *psDBPTypeToMySQL(psElemType pType)
+{
+    psHash          *pTypeToMySQLTable; // type lookup table
+    char            *key;               // hash tmp value
+    mysqlType       *mType;             // mysqlType struct to return
+
+    pTypeToMySQLTable = psDBPTypeToMySQLTableGet();
+
+    key = psDBIntToString((psU64)pType);
+    mType = psHashLookup(pTypeToMySQLTable, key);
+    psFree(key);
+
+    if (!mType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return NULL;
+    }
+
+    psMemIncrRefCounter(mType);
+
+    return mType;
+}
+
+// PTypeToSQLTable
+
+static psHash *psDBPTypeToSQLTableSetup(void)
+{
+    if (!pTypeToSQLlookupTable) {
+        pTypeToSQLlookupTable = psHashAlloc(14);
+
+        // no support for CHAR, TEXT or GLOB
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_S8,  "TINYINT");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_S16, "SMALLINT");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_S32, "INT");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_S64, "BIGINT");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_U8,  "TINYINT UNSIGNED");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_U16, "SMALLINT UNSIGNED");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_U32, "INT UNSIGNED" );
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_U64, "BIGINT UNSIGNED");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_F32, "FLOAT");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_F64, "DOUBLE");
+        // XXX Since BOOL is added after S8 all "TINYINT" data will appear in
+        // the database as boolean data.  There does not seem to be any way to
+        // work around this with MySQL < 5.0.3.
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_BOOL,"TINYINT");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_STRING, "VARCHAR");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_TIME, "DATETIME");
+        psDBAddToLookupTable(pTypeToSQLlookupTable, PS_DATA_UNKNOWN, "NULL");
+    } else {
+        // increment the ref count by one for every psDB
+        psMemIncrRefCounter(pTypeToSQLlookupTable);
+    }
+
+    return pTypeToSQLlookupTable;
+}
+
+static psHash *psDBPTypeToSQLTableGet(void)
+{
+    return pTypeToSQLlookupTable;
+}
+
+static void psDBPTypeToSQLTableCleanup(void)
+{
+    PSDB_NULL_FREE(pTypeToSQLlookupTable);
+}
+
+
+// SQLToPTypeTable
+
+static psHash *psDBSQLToPTypeTableSetup(void)
+{
+    psHash          *psToSQLTable;
+    psList          *list;
+    psListIterator  *cursor;
+    char            *key;
+    char            *value;
+
+    if (!sqlToPTypeLookupTable) {
+        // invert the PSToSQL table
+        psToSQLTable = psDBPTypeToSQLTableGet();
+        sqlToPTypeLookupTable = psHashAlloc(psToSQLTable->n);
+
+        list = psHashKeyList(psToSQLTable);
+        cursor = psListIteratorAlloc(list, 0, false);
+
+        while ((key = psListGetAndIncrement(cursor))) {
+            value = psHashLookup(psToSQLTable, key);
+            // switch key and value
+            psHashAdd(sqlToPTypeLookupTable, value, key);
+        }
+
+        // Add BLOB & TEXT reverse mappings
+        value = psDBIntToString((psU64)PS_DATA_STRING);
+        psHashAdd(sqlToPTypeLookupTable, "BLOB",    value);
+        psHashAdd(sqlToPTypeLookupTable, "TEXT",    value);
+        psFree(value);
+
+        // DECIMAL does not exist in the pType to SQL table
+        value = psDBIntToString(0);
+        psHashAdd(sqlToPTypeLookupTable, "DECIMAL", value);
+        psFree(value);
+
+        psFree(cursor);
+        psFree(list);
+    } else {
+        // increment the ref count by one for every psDB
+        psMemIncrRefCounter(sqlToPTypeLookupTable);
+    }
+
+    return sqlToPTypeLookupTable;
+}
+
+static psHash *psDBSQLToPTypeTableGet(void)
+{
+    return sqlToPTypeLookupTable;
+}
+
+static void psDBSQLToPTypeTableCleanup(void)
+{
+    PSDB_NULL_FREE(sqlToPTypeLookupTable);
+}
+
+
+// MySQLToSQLTable
+
+static psHash *psDBMySQLToSQLTableSetup(void)
+{
+    if (!mysqlToSqlLookupTable) {
+        mysqlToSqlLookupTable = psHashAlloc(20);
+
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_TINY,      "TINYINT");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_SHORT,     "SMALLINT");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_LONG,      "INT");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_INT24,     "MEDIUMINT");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_LONGLONG,  "BIGINT");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_DECIMAL,   "DECIMAL");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_FLOAT,     "FLOAT");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_DOUBLE,    "DOUBLE");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_TIMESTAMP, "DATETIME");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_DATE,      "DATE");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_TIME,      "TIME");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_DATETIME,  "DATETIME");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_YEAR,      "YEAR");
+        // strictly speaking this should CHAR but there is no equivilent ps type
+        // for char as psString is already mapped to varchar
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_STRING,    "VARCHAR");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_VAR_STRING,"VARCHAR");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_BLOB,      "BLOB");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_SET,       "SET");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_ENUM,      "ENUM");
+        psDBAddToLookupTable(mysqlToSqlLookupTable, MYSQL_TYPE_NULL,      "NULL");
+    } else {
+        // increment the ref count by one for every psDB
+        psMemIncrRefCounter(mysqlToSqlLookupTable);
+    }
+
+    return mysqlToSqlLookupTable;
+}
+
+static psHash *psDBMySQLToSQLTableGet(void)
+{
+    return mysqlToSqlLookupTable;
+}
+
+static void psDBMySQLToSQLTableCleanup(void)
+{
+    PSDB_NULL_FREE(mysqlToSqlLookupTable);
+}
+
+
+// PTypeToMySQLTable
+
+static psHash *psDBPTypeToMySQLTableSetup(void)
+{
+    if (!pTypeToMysqlLookupTable) {
+        pTypeToMysqlLookupTable = psHashAlloc(14);
+
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_S8,     psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_S16,    psDBMySQLTypeAlloc(MYSQL_TYPE_SHORT,      false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_S32,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONG,       false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_S64,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONGLONG,   false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_U8,     psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       true));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_U16,    psDBMySQLTypeAlloc(MYSQL_TYPE_SHORT,      true));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_U32,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONG,       true));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_U64,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONGLONG,   true));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_F32,    psDBMySQLTypeAlloc(MYSQL_TYPE_FLOAT,      false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_F64,    psDBMySQLTypeAlloc(MYSQL_TYPE_DOUBLE,     false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_BOOL,   psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       true));
+        // XXX: removed PS_DATA_PTR, can this be removed too?
+        // psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_PTR,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_STRING,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_TIME,      psDBMySQLTypeAlloc(MYSQL_TYPE_DATETIME, false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_VECTOR,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_IMAGE,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_HASH,   psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_LOOKUPTABLE,
+                                 psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        //        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_JPEG,   psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        //        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_PNG,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        //        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_ASTROM, psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(pTypeToMysqlLookupTable, PS_DATA_UNKNOWN,psDBMySQLTypeAlloc(MYSQL_TYPE_NULL, false));
+    } else {
+        // increment the ref count by one for every psDB
+        psMemIncrRefCounter(pTypeToMysqlLookupTable);
+    }
+
+    return pTypeToMysqlLookupTable;
+}
+
+static psHash *psDBPTypeToMySQLTableGet(void)
+{
+    return pTypeToMysqlLookupTable;
+}
+
+static void psDBPTypeToMySQLTableCleanup(void)
+{
+    PSDB_NULL_FREE(pTypeToMysqlLookupTable);
+}
+
+static psPtr psDBMySQLTypeAlloc(enum enum_field_types type,
+                                bool isUnsigned)
+{
+    mysqlType *mType = psAlloc(sizeof(mysqlType));
+    mType->type       = type;
+    mType->isUnsigned = isUnsigned;
+
+    return mType;
+}
+
+static void psDBAddToLookupTable(psHash *lookupTable,
+                                 psU32 type,
+                                 const char *string)
+{
+    PS_ASSERT_PTR_NON_NULL(lookupTable, );
+
+    psString key = psDBIntToString((psU64)type);
+    psString value = psStringCopy(string);
+
+    psHashAdd(lookupTable, key, value);
+
+    psFree(key);
+    psFree(value);
+}
+
+static void psDBAddVoidToLookupTable(psHash *lookupTable,
+                                     psU32 type,
+                                     psPtr value)
+{
+    PS_ASSERT_PTR_NON_NULL(lookupTable, );
+
+    psString key = psDBIntToString((psU64)type);
+
+    psHashAdd(lookupTable, key, value);
+
+    // destructive of value parameter
+    psFree(value);
+    psFree(key);
+}
+
+static psErrorCode mysqlTopsErr(MYSQL *mysql)
+{
+    unsigned int myerrno = mysql_errno(mysql);
+    if ((myerrno >= 1000) && (myerrno < 2000)) {
+        return PS_ERR_DB_SERVER;
+    } else if ((myerrno >= 2000) && (myerrno < 3000)) {
+        return PS_ERR_DB_CLIENT;
+    }
+
+    return PS_ERR_UNKNOWN;
+}
+
+
+// pType utility functions
+/*****************************************************************************/
+
+#define PS_NAN_ALLOC(dest, type, nan) \
+dest = psAlloc(sizeof(type)); \
+*(type *)dest = nan;
+
+static psPtr psDBGetPTypeNaN(psElemType pType)
+{
+    psPtr           myNaN = NULL;
+
+    switch (pType) {
+    case PS_DATA_S8:
+        PS_NAN_ALLOC(myNaN, psS8, PS_MAX_S8);
+        break;
+    case PS_DATA_S16:
+        PS_NAN_ALLOC(myNaN, psS16, PS_MAX_S16);
+        break;
+    case PS_DATA_S32:
+        PS_NAN_ALLOC(myNaN, psS32, PS_MAX_S32);
+        break;
+    case PS_DATA_S64:
+        PS_NAN_ALLOC(myNaN, psS64, PS_MAX_S64);
+        break;
+    case PS_DATA_U8:
+        PS_NAN_ALLOC(myNaN, psU8, PS_MAX_U8);
+        break;
+    case PS_DATA_U16:
+        PS_NAN_ALLOC(myNaN, psU16, PS_MAX_U16);
+        break;
+    case PS_DATA_U32:
+        PS_NAN_ALLOC(myNaN, psU32, PS_MAX_U32);
+        break;
+    case PS_DATA_U64:
+        PS_NAN_ALLOC(myNaN, psU64, PS_MAX_U64);
+        break;
+    case PS_DATA_F32:
+        PS_NAN_ALLOC(myNaN, psF32, NAN);
+        break;
+    case PS_DATA_F64:
+        PS_NAN_ALLOC(myNaN, psF64, NAN);
+        break;
+    case PS_DATA_BOOL:
+        // XXX: what is NaN for a bool?
+        PS_NAN_ALLOC(myNaN, psU8, PS_MAX_U8);
+        break;
+    }
+
+    return myNaN;
+}
+
+#define PS_IS_NAN(type, data, nan) *(type *)data == nan
+
+static bool psDBIsPTypeNaN(psElemType pType,
+                           psPtr data)
+{
+    bool    isNaN = NULL;
+
+    switch (pType) {
+    case PS_DATA_S8:
+        isNaN = PS_IS_NAN(psS8, data, PS_MAX_S8);
+        break;
+    case PS_DATA_S16:
+        isNaN = PS_IS_NAN(psS16, data, PS_MAX_S16);
+        break;
+    case PS_DATA_S32:
+        isNaN = PS_IS_NAN(psS32, data, PS_MAX_S32);
+        break;
+    case PS_DATA_S64:
+        isNaN = PS_IS_NAN(psS64, data, PS_MAX_S64);
+        break;
+    case PS_DATA_U8:
+        isNaN = PS_IS_NAN(psU8, data, PS_MAX_U8);
+        break;
+    case PS_DATA_U16:
+        isNaN = PS_IS_NAN(psU16, data, PS_MAX_U16);
+        break;
+    case PS_DATA_U32:
+        isNaN = PS_IS_NAN(psU32, data, PS_MAX_U32);
+        break;
+    case PS_DATA_U64:
+        isNaN = PS_IS_NAN(psU64, data, PS_MAX_U64);
+        break;
+    case PS_DATA_F32:
+        isNaN = PS_IS_NAN(psF32, data, NAN);
+        break;
+    case PS_DATA_F64:
+        isNaN = PS_IS_NAN(psF64, data, NAN);
+        break;
+    case PS_DATA_BOOL:
+        // XXX: what is NaN for a bool?
+        isNaN = PS_IS_NAN(psU8, data, PS_MAX_U8);
+        break;
+    }
+
+    return isNaN;
+}
+
+// string utility functions
+/*****************************************************************************/
+
+psString psDBIntToString(psU64 value)
+{
+    // length of string (log10 + 1) + \0
+    // if value is 0, length is 1 char + \0
+    size_t length = value ? (size_t)log10((double)value) + 1 + 1
+                    : 2;
+    psString string = psStringAlloc(length);
+    snprintf(string, length, "%li", (long int)value);
+
+    return string;
+}
+
+#endif // HAVE_PSDB
Index: /tags/sj_tags/sj_root_20080929/psLib/src/db/psDB.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/db/psDB.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/db/psDB.h	(revision 22322)
@@ -0,0 +1,453 @@
+/** @file psDB.h
+ *
+ * Copyright (C) 2007  Joshua Hoblitt, University of Hawaii
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * @brief database types and functions
+ *
+ * This file defines the abstract database type and functions that
+ * perform basic database operations.
+ *
+ * $Id: psDB.h,v 1.37 2007-09-05 23:20:04 jhoblitt Exp $
+ */
+
+#ifndef PS_DB_H
+#define PS_DB_H 1
+#ifdef HAVE_PSDB
+
+/// @addtogroup FileIO Input/Output
+/// @{
+
+#include "psType.h"
+#include "psMetadata.h"
+
+/** Database handle
+ *
+ *  An opaque object representing a database connection.
+ *
+ */
+typedef struct
+{
+    void* mysql;                       ///< MySQL database handle
+}
+psDB;
+
+/** Opens a new database connection
+ *
+ *  @return psDB*:      A new psDB object if the database connection is
+ *  successful or NULL on failure.
+ */
+psDB *psDBAlloc(
+    const char *host,                  ///< Database server hostname
+    const char *user,                  ///< Database username
+    const char *passwd,                ///< Database password
+    const char *dbname,                ///< Database namespace
+    unsigned int port                  ///< Database port number
+) PS_ATTR_MALLOC;
+
+/** Opens a new database connection
+ *
+ *  This function is deprecated favor of psDBAlloc()
+ *
+ *  @return psDB*:      A new psDB object if the database connection is
+ *  successful or NULL on failure.
+ */
+#ifdef DOXYGEN
+psDB *psDBInit(
+    const char *host,                  ///< Database server hostname
+    const char *user,                  ///< Database username
+    const char *passwd,                ///< Database password
+    const char *dbname,                ///< Database namespace
+    unsigned int port                  ///< Database port number
+);
+#else // DOXYGEN
+#define psDBInit(host, user, passwd, dbname, port) \
+psDBAlloc(host, user, passwd, dbname, port)
+#endif
+
+#ifdef DOXYGEN
+/** Closes a database connection
+ *
+ *  This function is deprecated favor of psFree()
+ */
+void psDBCleanup(
+    psDB *dbh                          ///< Database handle
+);
+#else // DOXYGEN
+#define psDBCleanup(dbh) \
+psFree(dbh)
+#endif
+
+/** Creates a new database namespace
+ *
+ * @return bool:    true on success
+ */
+bool psDBCreate(
+    psDB *dbh,                         ///< Database handle
+    const char *dbname                 ///< New database namespace
+);
+
+/** Changes the current database namespace
+ *
+ * @return bool:    true on success
+ */
+bool psDBChange(
+    psDB *dbh,                         ///< Database handle
+    const char *dbname                 ///< Database namespace
+);
+
+/** Drops a database namespace
+ *
+ * @return bool:    true on success
+ */
+bool psDBDrop(
+    psDB *dbh,                         ///< Database handle
+    const char *dbname                 ///< Database namespace
+);
+
+/** Executes a SQL query
+ *
+ * This function will execute a string as a raw SQL query.  No additional
+ * processing of the string or abstraction of the underlying database's SQL
+ * dialect is provided.  Caveat emptor.
+ *
+ * @return bool:    true on success
+ */
+bool p_psDBRunQuery(
+    psDB *dbh,                         ///< Database handle
+    const char *format,                ///< SQL string to execute
+    ...                                ///< Arguments for name formatting and metadata item data.
+) PS_ATTR_FORMAT(printf, 2, 3);
+
+/** Executes a SQL query as a prepared statement
+ *
+ * This function will execute a string as a raw SQL query.  No additional
+ * processing of the string or abstraction of the underlying database's SQL
+ * dialect is provided.  Caveat emptor.
+ *
+ * @return long:    the number of database rows affected
+ */
+long p_psDBRunQueryPrepared(
+    psDB *dbh,                          ///< Database handle
+    const psArray *rowSet,              ///< row data as psArray of psMetadata
+    const char *format,                 ///< SQL string to execute
+    ...
+) PS_ATTR_FORMAT(printf, 3, 4);
+
+/** Fetches the result of a SQL query
+ *
+ * This function returns the result of the most recent SQL query as a psArray
+ * of psMetadata.  Caveat emptor.
+ *
+ * @return psArray*:    A psArray of psMetadata or NULL on failure
+ */
+psArray *p_psDBFetchResult(
+    psDB *dbh                          ///< Database handle
+);
+
+/** Creates a new database table
+ *
+ * This function generates and executes the SQL needed to create a table named
+ * "tableName", with the column names and data types as described in "md".  Each
+ * data item in the psMetadata collection represents a single table field.  The
+ * name of the field is given by the name of the psMetadataItem and the data
+ * type is give by the psMetadataItem.type and psMetadataItem.ptype entries.  A
+ * lookup table should be used to convert from PSLib types into MySQL
+ * compatible SQL data types.  For example, a PS_DATA_STRING would map to an SQL99
+ * varchar.  If the value of type is PS_DATA_STRING then the psMetadataItem.data
+ * element is set to a string with the length for the field written as a text
+ * string.  The value of the psMetadataItem.data element is unused for the
+ * PS_META_PRIMITIVE types.  Other psMetadata types beyond PS_DATA_STRING and
+ * PS_META_PRIMITIVE are not allowed in a table definition.
+ *
+ * Database indexes can be specified setting the "comment" field to "Primary
+ * Key" or "Key".  Comments are otherwise ignored.
+ *
+ * @return bool:    true on success
+ */
+bool psDBCreateTable(
+    psDB *dbh,                         ///< Database handle
+    const char *tableName,             ///< Table name
+    const psMetadata *md               ///< Column names, types, and indexes
+);
+
+/** Deletes a database table
+ *
+ * @return bool:    true on success
+*/
+bool psDBDropTable(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName               ///< Table name
+);
+
+/** Selects a column from a table
+ *
+ * This function generates and executes the SQL needed to select an entire
+ * column from a table or up to "limit" rows from it.  If "limit" is 0, the
+ * entire range is returned.
+ *
+ * @return psArray*:    A psArray of strings or NULL on failure
+ */
+psArray *psDBSelectColumn(
+    psDB *dbh,                         ///< Database handle
+    const char *tableName,             ///< Table name
+    const char *col,                   ///< Column name
+    unsigned long long limit           ///< Maximum number of elements to return
+);
+
+/** Selects a column from a table and casts it to a given type
+ *
+ * This function generates and executes the SQL needed to select an entire
+ * column from a table or up to "limit" rows from it.  If "limit" is 0, the
+ * entire range is returned.  The data in the column is cast to to "pType".
+ *
+ * @return psVector*:   A psVector or NULL on failure
+ */
+psVector *psDBSelectColumnNum(
+    psDB *dbh,                         ///< Database handle
+    const char *tableName,             ///< Table name
+    const char *col,                   ///< Column name
+    psElemType type,                   ///< Resulting psVector type
+    unsigned long long limit           ///< Maximum number of elements to return
+);
+
+/** Selects a set of rows from a table
+ *
+ * This function returns rows from the specified table which match the
+ * restrictions given by "where".  The restrictions are specified as field /
+ * value pairs.  The psMetadata collection "where" must consist of valid
+ * database fields.  The selected rows are returned as a psArray of psMetadata
+ * values, one per row.
+ *
+ * Currently, the "where" specification only supports the PS_DATA_STRING type.
+ * The string value can be a SQL match pattern, e.g. "%foo%", or an empty
+ * string, e.g. "", to match NULL field values.
+ *
+ * @return psArray*:    A psArray of psMetadata or NULL on failure
+ */
+psArray *psDBSelectRows(
+    psDB *dbh,                         ///< Database handle
+    const char *tableName,             ///< Table name
+    const psMetadata *where,           ///< Row match criteria
+    unsigned long long limit           ///< Maximum number of elements to return
+);
+
+/** Insert a single row into a table
+ *
+ * This function inserts the data from "row" into "tableName".
+ *
+ * The "row" specification uses the psMetadataItem name as the column name.
+ * The field values may be specified in any order.  psMetadata types beyond
+ * PS_DATA_STRING and PS_META_PRIMITIVE are not supported.  If fields are
+ * specified in "row" that do not exist in "tableName", the insert will fail.
+ *
+ * @return bool:    true on success
+ */
+bool psDBInsertOneRow(
+    psDB *dbh,                         ///< Database handle
+    const char *tableName,             ///< Table name
+    const psMetadata *row              ///< Row description
+);
+
+/** Insert a set of rows into a table
+ *
+ * This function inserts the data from "rowSet" into "tableName".
+ *
+ * "rowSet" is a psArray of psMetadata containing row specifications identical to
+ * those used in psDBInsertOneRow().
+ *
+ * @return bool:    true on success
+ */
+bool psDBInsertRows(
+    psDB *dbh,                         ///< Database handle
+    const char *tableName,             ///< Table name
+    const psArray *rowSet              ///< Set of rows to insert
+);
+
+/** Retrieves all rows from a table
+ *
+ * This function fetches all rows as an psArray of psMetadata.  The rows are in
+ * the same psMetadata format as used in psDBInsertOneRow() & psDBInsertRows().
+ *
+ * @return psArray*:    A psArray of psMetadata or NULL on failure
+ */
+psArray *psDBDumpRows(
+    psDB *dbh,                         ///< Database handle
+    const char *tableName              ///< Table name
+);
+
+/** Retrieves all columns from a table
+ *
+ * This function fetches all columns, as either a psVector or a psArray
+ * depending on whether or not the column is numeric, and return them in a
+ * psMetadata structure where psMetadataItem.name contains the column's name.
+ *
+ * @return psMetadata*:     A psMetadata containing either a psArrays or psVector per column
+ */
+psMetadata *psDBDumpCols(
+    psDB *dbh,                         ///< Database handle
+    const char *tableName              ///< Table name
+);
+
+/** Updates the field values, as specified, in a table
+ *
+ * This function updates the fields contained in "values" in the row(s) that
+ * have a field with the value indicated by "where".  Where "where" is in the
+ * same format as used in psDBSelectRows().
+ *
+ * The "values" specification uses the same format as the row specification
+ * used in psDBInsertOneRow(), etc.
+ *
+ * @return long:    The number of rows modified or a negative value on error
+ */
+long psDBUpdateRows(
+    psDB *dbh,                         ///< Database handle
+    const char *tableName,             ///< Table name
+    const psMetadata *where,           ///< Row match criteria
+    psMetadata *values                 ///< new field values
+);
+
+/** Deletes rows, as specified, in a table
+ *
+ * Delete the rows that are matched by "where" using the same semantics for
+ * "where" as in psDBUpdateRow().
+ *
+ * If "where" is NULL, all rows in the table will be removed and regardless of
+ * the number of rows that were dropped, only 1 will be returned on success.
+ *
+ * @return long:    The number of rows removed or a negative value on error
+ */
+long psDBDeleteRows(
+    psDB *dbh,                         ///< Database handle
+    const char *tableName,             ///< Table name
+    const psMetadata *where,           ///< Row match criteria
+    unsigned long long limit           ///< Maximum number of rows to delete
+);
+
+/** Get the last insert ID
+ *
+ * Returns the value of MySQLs 'LAST_INSERT_ID()' function
+ *
+ * @return long:    The last insert ID
+ */
+long long psDBLastInsertID(
+    psDB *dbh                          ///< Database handle
+);
+
+/** Enable/Disable explicit database transactions
+ *
+ * This function is used to enable explicit transaction support.  It is off by
+ * default.
+ *
+ * @return bool:    true if transactions are enabled
+ */
+bool psDBExplicitTrans(
+    psDB *dbh,                          ///< Database handle
+    bool mode                           ///< transactions enable/disable
+);
+
+/** Start a new transaction set.
+ *
+ * This is only a meaningful action if explict transactions are disabled.
+ *
+ * @return bool:    true on success
+ */
+bool psDBTransaction(
+    psDB *dbh                           ///< Database handle
+);
+
+/** Commits the current transaction
+ *
+ * This function will commit the current transaction set (a rollback is not
+ * possible after this function is successfully executed).  A commit also
+ * effectively starts a new transaction if explict transactions are enabled.
+ *
+ * @return bool:    true on success
+ */
+bool psDBCommit(
+    psDB *dbh                           ///< Database handle
+);
+
+/** Rollback the current transaction
+ *
+ * This function will rollback the current transaction set.
+ *
+ * @return bool:    true on success
+ */
+bool psDBRollback(
+    psDB *dbh                           ///< Database handle
+);
+
+/** Generates an SQL "Where" fragment
+ *
+ * This function generates an SQL fragment (not a whole usable query) based on
+ * the standard "where" metadata format.
+ *
+ * @return psString:   A psString or NULL on failure
+ */
+psString psDBGenerateWhereSQL(
+    const psMetadata *where,           ///< Row match criteria
+    const char *tableName              ///< Table name
+);
+
+/** Generates an SQL "where conditon" statement
+ *
+ * This function generates a "Where" fragment but omits the "Where" keyword.
+ * This function generates an SQL fragment (not a whole usable query) based on
+ * the standard "where" metadata format.
+ *
+ * @return psString:   A psString or NULL on failure
+ */
+psString psDBGenerateWhereConditionSQL(
+    const psMetadata *where,           ///< Row match criteria
+    const char *tableName              ///< Table name
+);
+
+/** Generates an SQL "limit" statement
+ *
+ * This function generates an SQL fragment (not a whole usable query).
+ *
+ * @return psString:   A psString or NULL on failure
+ */
+psString psDBGenerateLimitSQL(
+    psU64 limit                         ///< result set row limit
+);
+
+/** converts an integer into a psString
+ *
+ * Note that this function takes an unsigned value.
+ *
+ * @return psString:   A psString or NULL on failure
+ */
+psString psDBIntToString(
+    psU64 value                         // integer value to convert
+);
+
+/** The number of rows modified or inserted by the last query
+ *
+ *  This function returns ((psU64) - 1) on error
+ *
+ * @return psU64
+ */
+psU64 psDBAffectedRows(
+    psDB *dbh                           ///< Database handle
+);
+
+/// @}
+#else
+typedef void psDB;
+#endif // HAVE_PSDB
+#endif // PS_DB_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fft/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fft/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fft/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fft/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fft/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fft/Makefile.am	(revision 22322)
@@ -0,0 +1,18 @@
+#Makefile for fft functions of psLib
+#
+noinst_LTLIBRARIES = libpslibfft.la
+
+libpslibfft_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(FFTW3_CFLAGS)
+libpslibfft_la_SOURCES = \
+	psFFT.c \
+	psImageFFT.c \
+	psVectorFFT.c
+
+EXTRA_DIST = fft.i
+
+pkginclude_HEADERS = \
+	psFFT.h \
+	psVectorFFT.h \
+	psImageFFT.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fft/fft.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fft/fft.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fft/fft.i	(revision 22322)
@@ -0,0 +1,3 @@
+/* db headers */
+%include "psImageFFT.h"
+%include "psVectorFFT.h"
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fft/psFFT.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fft/psFFT.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fft/psFFT.c	(revision 22322)
@@ -0,0 +1,46 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fftw3.h>
+#include <pthread.h>
+
+#include "psAssert.h"
+#include "psLogMsg.h"
+#include "psFFT.h"
+
+static pthread_mutex_t fftwLock = PTHREAD_MUTEX_INITIALIZER; // Lock for FFTW
+
+void psFFTLock(void)
+{
+    pthread_mutex_lock(&fftwLock);
+}
+
+void psFFTUnlock(void)
+{
+    pthread_mutex_unlock(&fftwLock);
+}
+
+bool psFFTThreads(int threads)
+{
+    PS_ASSERT_INT_NONNEGATIVE(threads, false);
+#if HAVE_FFTW_THREADS
+    static int numThreads = 0;          // Number of threads to use with FFTW
+    if (threads > 0) {
+        if (numThreads == 0) {
+            fftwf_init_threads();
+        }
+        fftwf_plan_with_nthreads(threads);
+    } else {
+        fftwf_cleanup_threads();
+    }
+    numThreads = threads;
+    return true;
+#else
+    if (threads > 0) {
+        psWarning("No thread support for FFTW.");
+        return false;
+    }
+    return true;
+#endif
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fft/psFFT.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fft/psFFT.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fft/psFFT.h	(revision 22322)
@@ -0,0 +1,19 @@
+#ifndef PS_FFT_H
+#define PS_FFT_H
+
+// Only fftw_execute is thread-safe: all other calls have to be wrapped in a mutex
+
+/// Lock FFT mutex
+void psFFTLock(void);
+
+/// Unlock FFT mutex
+void psFFTUnlock(void);
+
+/// Set number of threads for FFTW to use
+///
+/// Pass zero to turn off threading.  Note that the FFTW threads are *independent* of any other threads set up
+/// throuhg, e.g., psThread.
+bool psFFTThreads(int threads           // Number of threads
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fft/psImageFFT.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fft/psImageFFT.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fft/psImageFFT.c	(revision 22322)
@@ -0,0 +1,512 @@
+/// @file  psImageFFT.c
+///
+/// @brief Contains FFT transform related functions for psImage.
+///
+/// @author Paul Price, IfA
+/// @author Robert DeSonia, MHPCC
+///
+/// @version $Revision: 1.27 $ $Name: not supported by cvs2svn $
+/// @date $Date: 2008-08-14 03:23:13 $
+///
+/// Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+///
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <complex.h>
+#include <fftw3.h>
+#include <pthread.h>
+
+#include "psAssert.h"
+#include "psError.h"
+#include "psMemory.h"
+#include "psLogMsg.h"
+#include "psConstants.h"
+#include "psImageStructManip.h"
+#include "psImageConvolve.h"
+#include "psThread.h"
+#include "psFFT.h"
+#include "psImageFFT.h"
+
+// Lock FFTW access
+#define FFTW_LOCK \
+if (threaded) { \
+    psFFTLock(); \
+}
+// Unlock FFTW access
+#define FFTW_UNLOCK \
+if (threaded) { \
+    psFFTUnlock(); \
+}
+
+#define FFTW_PLAN_RIGOR FFTW_ESTIMATE   // How rigorous the FFTW planning is
+
+static psBool fftwWisdomImported = false; // Has the FFTW wisdom been imported yet?
+
+bool psImageForwardFFT(psImage **real, psImage **imag, const psImage *in)
+{
+    PS_ASSERT_IMAGE_NON_NULL(in, false);
+    PS_ASSERT_IMAGE_TYPE(in, PS_TYPE_F32, false);
+    PS_ASSERT_PTR_NON_NULL(real, false);
+    PS_ASSERT_PTR_NON_NULL(imag, false);
+
+    bool threaded = psThreadPoolSize(); // Are we running threaded?
+
+    // Make sure the system-level wisdom information is imported.
+    FFTW_LOCK;
+    if (!fftwWisdomImported) {
+        fftwf_import_system_wisdom();
+        fftwWisdomImported = true;
+    }
+    FFTW_UNLOCK;
+
+    int numCols = in->numCols;          // Number of columns
+    int numRows = in->numRows;          // Number of rows
+
+    psF32 *input;                       // Input data, for FFTW
+    if (!in->parent) {
+        // No parent --- can just use the data buffer
+        input = psMemIncrRefCounter(in->p_rawDataBuffer);
+    } else {
+        // Need to copy the data
+        input = psAlloc(numCols * numRows);
+        for (int y = 0; y < numRows; y++) {
+            memcpy(&input[y * numRows], in->data.F32[y], numCols);
+        }
+    }
+
+    // Do the FFT
+
+    FFTW_LOCK;
+    fftwf_complex *out = fftwf_malloc((numCols/2 + 1) * numRows * sizeof(fftwf_complex)); // Output data
+    fftwf_plan plan = fftwf_plan_dft_r2c_2d(numRows, numCols, input, out, FFTW_PLAN_RIGOR);
+    FFTW_UNLOCK;
+
+    fftwf_execute(plan);
+
+    FFTW_LOCK;
+    fftwf_destroy_plan(plan);
+    FFTW_UNLOCK;
+
+    psFree(input);
+
+    // Pull the real and imaginary parts out
+    numCols = numCols/2 + 1;            // x dimension is halved by FFTW
+    *real = psImageRecycle(*real, numCols, numRows, PS_TYPE_F32);
+    *imag = psImageRecycle(*imag, numCols, numRows, PS_TYPE_F32);
+    for (int y = 0, index = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; index++, x++) {
+#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
+            // C99 complex support
+            (*real)->data.F32[y][x] = creal(out[index]);
+            (*imag)->data.F32[y][x] = cimag(out[index]);
+#else
+            // FFTW's backup complex support
+            (*real)->data.F32[y][x] = out[index][0];
+            (*imag)->data.F32[y][x] = out[index][1];
+#endif
+        }
+    }
+
+    FFTW_LOCK;
+    fftwf_free(out);
+    FFTW_UNLOCK;
+
+    return true;
+}
+
+bool psImageBackwardFFT(psImage **out, const psImage *real, const psImage *imag, int origCols)
+{
+    PS_ASSERT_IMAGE_NON_NULL(real, false);
+    PS_ASSERT_IMAGE_TYPE(real, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGE_NON_NULL(imag, false);
+    PS_ASSERT_IMAGE_TYPE(imag, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(real, imag, false);
+    PS_ASSERT_PTR_NON_NULL(out, false);
+
+    bool threaded = psThreadPoolSize(); // Are we running threaded?
+
+    int numCols = real->numCols;        // Number of columns
+    int numRows = real->numRows;        // Number of rows
+
+    // Because of the way FFT r2c and c2r work, need the number of columns in the target to be:
+    // 2 * numCols = 2*(numCols/2 + 1) = origCols % 2 ? origCols + 1 : origCols + 2
+    if (2 * numCols != ((origCols % 2) ? origCols + 1 : origCols + 2)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Number of columns in FFT-ed images (%d) should be origCols (%d) / 2 + 1 = %d",
+                numCols, origCols, origCols/2 + 1);
+        return false;
+    }
+
+    if (*out && (*out)->parent) {
+        // It has a parent, so we can't write directly into the buffer.
+        // It had better be the correct size and type, because we don't want to resize a child image
+        PS_ASSERT_IMAGE_SIZE(*out, origCols, numRows, false);
+        PS_ASSERT_IMAGE_TYPE(*out, PS_TYPE_F32, false);
+    }
+
+    // Make sure the system-level wisdom information is imported.
+    FFTW_LOCK;
+    if (!fftwWisdomImported) {
+        fftwf_import_system_wisdom();
+        fftwWisdomImported = true;
+    }
+    FFTW_UNLOCK;
+
+    // Stuff the real and imaginary parts in
+    psF32 *target = psAlloc(2 * numCols * numRows * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); // Target for FFTW
+    FFTW_LOCK;
+    fftwf_complex *in = fftwf_malloc(numCols * numRows * sizeof(fftwf_complex)); // Input data
+    FFTW_UNLOCK;
+
+    for (int y = 0, index = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; index++, x++) {
+#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
+            // C99 complex support
+            in[index] = real->data.F32[y][x] + imag->data.F32[y][x] * I;
+#else
+            // FFTW's backup complex support
+            in[index][0] = real->data.F32[y][x];
+            in[index][1] = imag->data.F32[y][x];
+#endif
+        }
+    }
+
+    // Do the FFT
+    FFTW_LOCK;
+    fftwf_plan plan = fftwf_plan_dft_c2r_2d(numRows, origCols, in, target, FFTW_PLAN_RIGOR);
+    FFTW_UNLOCK;
+
+    fftwf_execute(plan);
+
+    FFTW_LOCK;
+    fftwf_destroy_plan(plan);
+    fftwf_free(in);
+    FFTW_UNLOCK;
+
+    // Copy the target pixels into the output
+    if (!(*out) || !(*out)->parent) {
+        *out = psImageRecycle(*out, origCols, numRows, PS_TYPE_F32);
+        memcpy((*out)->p_rawDataBuffer, target, numRows * origCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
+    } else {
+        for (int y = 0, index = 0; y < numRows; y++, index += origCols) {
+            memcpy(&(*out)->data.F32[y][0], &target[index], origCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
+        }
+    }
+
+    psFree(target);
+
+    return true;
+}
+
+psImage* psImagePowerSpectrum(psImage *out, const psImage *in)
+{
+    PS_ASSERT_IMAGE_NON_NULL(in, NULL);
+    PS_ASSERT_IMAGE_TYPE(in, PS_TYPE_F32, NULL);
+
+    psImage *real = NULL;              // Real component of FFT
+    psImage *imag = NULL;              // Imaginary component of FFT
+
+    if (!psImageForwardFFT(&real, &imag, in)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to perform forward FFT.");
+        return NULL;
+    }
+
+    int numCols = real->numCols;        // Number of columns
+    int numRows = real->numRows;        // Number of rows
+
+    float norm = 1.0 / PS_SQR(in->numCols) / PS_SQR(in->numRows);
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            // Power spectrum is the square of the complex modulus
+            real->data.F32[y][x] = norm * (PS_SQR(real->data.F32[y][x]) + PS_SQR(imag->data.F32[y][x]));
+        }
+    }
+    psFree(imag);
+
+    return real;
+}
+
+
+bool psImageComplexMultiply(psImage **outReal, psImage **outImag,
+                            const psImage *in1Real, const psImage *in1Imag,
+                            const psImage *in2Real, const psImage *in2Imag)
+{
+    PS_ASSERT_IMAGE_NON_NULL(in1Real, false);
+    PS_ASSERT_IMAGE_NON_NULL(in1Imag, false);
+    PS_ASSERT_IMAGE_NON_NULL(in2Real, false);
+    PS_ASSERT_IMAGE_NON_NULL(in2Imag, false);
+    PS_ASSERT_IMAGE_TYPE(in1Real, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGE_TYPE(in1Imag, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGE_TYPE(in2Real, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGE_TYPE(in2Imag, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(in1Imag, in1Real, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(in2Real, in1Real, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(in2Imag, in1Real, false);
+    PS_ASSERT_PTR_NON_NULL(outReal, false);
+    PS_ASSERT_PTR_NON_NULL(outImag, false);
+    if (*outReal) {
+        PS_ASSERT_IMAGE_NON_NULL(*outReal, false);
+        PS_ASSERT_IMAGE_NON_NULL(*outImag, false);
+    } else if (*outImag) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "If the output real part is provided, the output imaginary part must also be provided.");
+        return false;
+    }
+
+    int numRows = in1Real->numRows;     // Number of rows
+    int numCols = in1Real->numCols;     // Number of columns
+
+    // Need to worry if the outputs are children
+    psImage *targetReal, *targetImag;   // Target real and imaginary parts
+
+    if ((*outReal)->parent) {
+        // It had better be the correct size and type, because we don't want to resize a child image
+        PS_ASSERT_IMAGE_TYPE(*outReal, PS_TYPE_F32, false);
+        PS_ASSERT_IMAGE_SIZE(*outReal, numCols, numRows, false);
+        targetReal = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    } else {
+        *outReal = psImageRecycle(*outReal, numCols, numRows, PS_TYPE_F32);
+        targetReal = psMemIncrRefCounter(*outReal);
+    }
+    if ((*outImag)->parent) {
+        // It had better be the correct size and type, because we don't want to resize a child image
+        if ((*outReal)->numCols != numCols || (*outReal)->numRows != numRows ||
+            (*outImag)->type.type != PS_TYPE_F32) {
+            // Plug potential memory leak --- need to free targetReal if there's a problem
+            psFree(targetReal);
+        }
+        PS_ASSERT_IMAGE_TYPE(*outReal, PS_TYPE_F32, false);
+        PS_ASSERT_IMAGE_SIZE(*outReal, numCols, numRows, false);
+        targetImag = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    } else {
+        *outImag = psImageRecycle(*outImag, numCols, numRows, PS_TYPE_F32);
+        targetImag = psMemIncrRefCounter(*outImag);
+    }
+
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            // (a + bi) * (c + di) = (ac - bd) + (bc + ad)i
+            float real = in1Real->data.F32[y][x] * in2Real->data.F32[y][x] -
+                in1Imag->data.F32[y][x] * in2Imag->data.F32[y][x];
+            float imag = in1Imag->data.F32[y][x] * in2Real->data.F32[y][x] +
+                in1Real->data.F32[y][x] * in2Imag->data.F32[y][x];
+            targetReal->data.F32[y][x] = real;
+            targetImag->data.F32[y][x] = imag;
+        }
+    }
+
+    if ((*outReal)->parent) {
+        *outReal = psImageCopy(*outReal, targetReal, PS_TYPE_F32);
+    }
+    if ((*outImag)->parent) {
+        *outImag = psImageCopy(*outImag, targetImag, PS_TYPE_F32);
+    }
+
+    psFree(targetReal);
+    psFree(targetImag);
+
+    return true;
+}
+
+
+psImage *psImageConvolveFFT(psImage *out, const psImage *in, const psImage *mask, psMaskType maskVal,
+                            const psKernel *kernel)
+{
+    PS_ASSERT_IMAGE_NON_NULL(in, NULL);
+    PS_ASSERT_IMAGE_TYPE(in, PS_TYPE_F32, NULL);
+    PS_ASSERT_KERNEL_NON_NULL(kernel, NULL);
+    if (mask) {
+        PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, NULL);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(mask, in, NULL);
+    }
+
+    bool threaded = psThreadPoolSize(); // Are we running threaded?
+
+    int numCols = in->numCols, numRows = in->numRows; // Size of image
+    int xMin = kernel->xMin, xMax = kernel->xMax, yMin = kernel->yMin, yMax = kernel->yMax; // Kernel sizes
+
+    // Need to pad the input image to protect from wrap-around effects
+    if (xMax - xMin > numCols || yMax - yMin > numRows) {
+        // Cannot pad the image if the kernel is larger.
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                _("Kernel cannot extend further than input image size (%dx%d vs %dx%d)."),
+                xMax, yMax, numCols, numRows);
+        return NULL;
+    }
+    int paddedCols = numCols + PS_MAX(-xMin, xMax); // Number of columns in padded image
+    int paddedRows = numRows + PS_MAX(-yMin, yMax); // Number of rows in padded image
+    int numPadded = paddedCols * paddedRows; // Number of pixels in padded image
+
+    // Create data array containing the padded image and padded kernel
+    FFTW_LOCK;
+    psF32 *data = fftwf_malloc(2 * numPadded * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); // Data for FFTW
+    FFTW_UNLOCK;
+    psF32 *dataPtr = data;              // Pointer into FFTW data
+    psF32 **imageData = in->data.F32;   // Pointer into image data
+
+    // Image part of data array
+    size_t goodBytes = numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes per image row
+    size_t padBytes = (paddedCols - numCols) * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes to pad
+    for (int y = 0; y < numRows; y++, dataPtr += paddedCols, imageData++) {
+        memcpy(dataPtr, *imageData, goodBytes);
+        memset(dataPtr + numCols, 0, padBytes);
+    }
+    memset(dataPtr, 0, (paddedRows - numRows) * paddedCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
+
+#if 0
+    {
+        // Use this for inspecting the result of copying the image
+        psImage *test = psImageAlloc(paddedCols, paddedRows, PS_TYPE_F32);
+        psFree(test->p_rawDataBuffer);
+        test->p_rawDataBuffer = data;
+        test->data.V[0] = test->p_rawDataBuffer;
+        for (int y = 1; y < paddedRows; y++) {
+            test->data.V[y] = (psPtr)((int8_t *)test->data.V[y - 1] +
+                                      paddedCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
+        }
+        // View image here
+        test->p_rawDataBuffer = NULL;
+        psFree(test);
+    }
+#endif
+
+    // Kernel part of data array
+    dataPtr = data + numPadded;         // Reset to kernel image location
+    float norm = 1.0 / (float)(paddedRows * paddedCols); // Normalisation to correct for FFT
+    // We could generate the padded kernel image using memcpy, but by going pixel by pixel we can apply the
+    // normalisation that corrects for the FFT renormalisation.  By applying it to the kernel here, we save
+    // applying it to the entire output image.
+    int xNegMin = PS_MIN(-1, xMin), xNegMax = PS_MIN(-1, xMax); // Min and max for x when negative
+    int xPosMin = PS_MAX(0, xMin), xPosMax = PS_MAX(0, xMax); // Min and max for x when positive
+    int yNegMin = PS_MIN(-1, yMin), yNegMax = PS_MIN(-1, yMax); // Min and max for x when negative
+    int yPosMin = PS_MAX(0, yMin), yPosMax = PS_MAX(0, yMax); // Min and max for x when positive
+    int blankCols = xNegMin + paddedCols - xPosMax - 1; // Number of columns between kernel extrema
+    int blankRows = (yNegMin + paddedRows - yPosMax - 1) * paddedCols; // Rows between kernel extrema
+    size_t blankColBytes = blankCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes in blankCols
+    for (int y = yPosMin; y <= yPosMax; y++) {
+        // y is positive
+        for (int x = xPosMin; x <= xPosMax; x++, dataPtr++) {
+            // x is positive
+            *dataPtr = kernel->kernel[y][x] * norm;
+        }
+        // Columns between kernel extrema
+        memset(dataPtr, 0, blankColBytes);
+        dataPtr += blankCols;
+        for (int x = xNegMin; x <= xNegMax; x++, dataPtr++) {
+            // x is negative
+            *dataPtr = kernel->kernel[y][x] * norm;
+        }
+    }
+    // Rows between kernel extrema
+    memset(dataPtr, 0, blankRows * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
+    dataPtr += blankRows;
+    for (int y = yNegMin; y <= yNegMax; y++) {
+        // y is negative
+        for (int x = xPosMin; x <= xPosMax; x++, dataPtr++) {
+            // x is positive
+            *dataPtr = kernel->kernel[y][x] * norm;
+        }
+        // Columns between kernel extrema
+        memset(dataPtr, 0, blankColBytes);
+        dataPtr += blankCols;
+        for (int x = xNegMin; x <= xNegMax; x++, dataPtr++) {
+            // x is negative
+            *dataPtr = kernel->kernel[y][x] * norm;
+        }
+    }
+
+#if 0
+    {
+        // Use this for inspecting the result of copying the kernel
+        psImage *test = psImageAlloc(paddedCols, paddedRows, PS_TYPE_F32);
+        psFree(test->p_rawDataBuffer);
+        test->p_rawDataBuffer = &data[numPadded];
+        test->data.V[0] = test->p_rawDataBuffer;
+        for (int y = 1; y < paddedRows; y++) {
+            test->data.V[y] = (psPtr)((int8_t *)test->data.V[y - 1] +
+                                      paddedCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
+        }
+        // View image here
+        test->p_rawDataBuffer = NULL;
+        psFree(test);
+    }
+#endif
+
+    // Mask bad pixels (which may be NANs), lest they infect everything
+    if (mask && maskVal) {
+        for (int y = 0; y < numRows; y++) {
+            for (int x = 0; x < numCols; x++) {
+                if (mask->data.PS_TYPE_MASK_DATA[y][x] & maskVal) {
+                    data[x + paddedCols * y] = 0;
+                }
+            }
+        }
+    }
+
+    // Do the forward FFT
+    // Note that the FFT images have different size from the input
+    FFTW_LOCK;
+    fftwf_complex *fft = fftwf_malloc(2 * (paddedCols/2 + 1) * paddedRows * sizeof(fftwf_complex)); // FFT
+    FFTW_UNLOCK;
+    int size[] = { paddedRows, paddedCols }; // Size of transforms
+    int fftCols = paddedCols/2 + 1, fftRows = paddedRows; // Size of FFT images
+    int fftPixels = fftCols * fftRows;  // Number of pixels in FFT image
+
+    FFTW_LOCK;
+    fftwf_plan forward = fftwf_plan_many_dft_r2c(2, size, 2, data, NULL, 1, paddedCols * paddedRows,
+                                                 fft, NULL, 1, fftPixels, FFTW_PLAN_RIGOR);
+    FFTW_UNLOCK;
+
+    fftwf_execute(forward);
+
+    FFTW_LOCK;
+    fftwf_destroy_plan(forward);
+    FFTW_UNLOCK;
+
+    // Multiply the two transforms
+    for (int i = 0, j = fftPixels; i < fftPixels; i++, j++) {
+        // (a + bi) * (c + di) = (ac - bd) + (bc + ad)i
+#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
+        // C99 complex support
+        fft[i] *= fft[j];
+#else
+        // FFTW's backup complex support
+        float imageReal = fft[i][0], imageImag = fft[i][1];
+        float kernelReal = fft[j][0], kernelImag = fft[j][1];
+        fft[i][0] = imageReal * kernelReal - imageImag * kernelImag;
+        fft[i][1] = imageImag * kernelReal + imageReal * kernelImag;
+#endif
+    }
+
+    // Do the backward FFT
+    FFTW_LOCK;
+    fftwf_plan backward = fftwf_plan_dft_c2r_2d(paddedRows, paddedCols, fft, data, FFTW_PLAN_RIGOR);
+    FFTW_UNLOCK;
+
+    fftwf_execute(backward);
+
+    FFTW_LOCK;
+    fftwf_destroy_plan(backward);
+    fftwf_free(fft);
+    FFTW_UNLOCK;
+
+    // Copy into the target, without the padding
+    out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32);
+    psF32 **outData = out->data.F32;    // Pointer into output
+    dataPtr = data;                     // Reset to start
+    for (int y = 0; y < numRows; y++, outData++, dataPtr += paddedCols) {
+        memcpy(*outData, dataPtr, goodBytes);
+    }
+
+    FFTW_LOCK;
+    fftwf_free(data);
+    FFTW_UNLOCK;
+
+    return out;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fft/psImageFFT.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fft/psImageFFT.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fft/psImageFFT.h	(revision 22322)
@@ -0,0 +1,77 @@
+/// @file  psImageFFT.h
+/// @brief Contains FFT transform related functions for psImage
+///
+/// @author Paul Price, IfA
+/// @author Robert DeSonia, MHPCC
+///
+/// @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+/// @date $Date: 2008-04-04 22:44:56 $
+/// Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+///
+
+#ifndef PS_IMAGE_FFT_H
+#define PS_IMAGE_FFT_H
+
+#include "psImage.h"
+#include "psImageConvolve.h"
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/// Forward FFT of an image
+///
+/// Applies a forward FFT (exponent -1), with the result returned in both real and imaginary parts.  The FFT
+/// is not normalised (a forward followed by a reverse is the original scaled by the total number of pixels).
+/// The FFT takes advantage of the fact that the input is purely real; hence the output number of columns is
+/// numCols/2 + 1 (with division rounding down).  Only implemented for F32 input.
+bool psImageForwardFFT(psImage **real,  ///< Real part of FFT
+                       psImage **imag,  ///< Imaginary part of FFT
+                       const psImage *in///< Input image (F32)
+    );
+
+/// Backward FFT of an image
+///
+/// Applies a backward FFT (exponent +1) from the real and imaginary parts with the (purely) real result
+/// returned.  The FFT is not normalised (a forward followed by a reverse is the original scaled by the total
+/// number of pixels).  The FFT takes advantage of the fact that the output will be purely real; hence the
+/// input number of columns is numCols/2 + 1 (with division rounding down); to manage the redundancy (is the
+/// original number of columns even or odd?), we need the original number of columns (the number of columns of
+/// the image that was input to psImageForwardFFT) to be provided.  Only implemented for F32 input.
+bool psImageBackwardFFT(psImage **out,///< Output image
+                        const psImage *real, ///< Real input (F32)
+                        const psImage *imag, ///< Imaginary input (F32)
+                        int origCols    ///< Original number of columns
+    );
+
+
+/// Power spectrum of an image
+///
+/// Generates the power spectrum of an image.  Only implemented for F32 input.
+psImage* psImagePowerSpectrum(psImage *out, const psImage *in);
+
+/// Multiply complex images
+///
+/// The input images are the real and imaginary parts of each of two images.  The real and imaginary parts
+/// of the output image are returned.  Only implemented for F32 input.
+bool psImageComplexMultiply(psImage **outReal, ///< Real part of output
+                            psImage **outImag, ///< Imaginary part of output
+                            const psImage *in1Real, ///< Real part of input 1
+                            const psImage *in1Imag, ///< Imaginary part of input 1
+                            const psImage *in2Real, ///< Real part of input 2
+                            const psImage *in2Imag ///< Imaginary part of input 2
+    );
+
+/// Convolve an image with a kernel, using the FFT
+///
+/// This is appropriate for larger kernels, where the direct convolution is slow.  The input image and kernel
+/// are suitably padded to avoid wrap-around effects.
+psImage *psImageConvolveFFT(
+    psImage *out,                       ///< Output image, or NULL
+    const psImage *in,                  ///< Image to convolve
+    const psImage *mask,                ///< Corresponding mask
+    psMaskType maskVal,                 ///< Value to mask
+    const psKernel *kernel              ///< kernel to colvolve with
+);
+
+/// @}
+#endif // #ifndef PS_IMAGE_FFT_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fft/psVectorFFT.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fft/psVectorFFT.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fft/psVectorFFT.c	(revision 22322)
@@ -0,0 +1,223 @@
+/** @file  psVectorFFT.c
+ *
+ *  @brief Contains FFT transform related functions for psVector
+ *
+ *  @author Paul Price, IfA
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.40 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-14 03:23:13 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <stdbool.h>
+#include <string.h>
+#include <complex.h>
+#include <fftw3.h>
+
+#include "psAssert.h"
+#include "psError.h"
+#include "psMemory.h"
+#include "psLogMsg.h"
+#include "psConstants.h"
+#include "psThread.h"
+#include "psFFT.h"
+#include "psVectorFFT.h"
+
+// Lock FFTW access
+#define FFTW_LOCK \
+if (threaded) { \
+    psFFTLock(); \
+}
+// Unlock FFTW access
+#define FFTW_UNLOCK \
+if (threaded) { \
+    psFFTUnlock(); \
+}
+
+#define FFTW_PLAN_RIGOR FFTW_ESTIMATE   // How rigorous the FFTW planning is
+
+static psBool fftwWisdomImported = false; // Has the system wisdom been imported?
+
+bool psVectorForwardFFT(psVector **real, psVector **imag, const psVector *in)
+{
+    PS_ASSERT_VECTOR_NON_NULL(in, false);
+    PS_ASSERT_VECTOR_TYPE(in, PS_TYPE_F32, false);
+    PS_ASSERT_PTR_NON_NULL(real, false);
+    PS_ASSERT_PTR_NON_NULL(imag, false);
+
+    bool threaded = psThreadPoolSize(); // Are we running threaded?
+
+    // Make sure the system-level wisdom information is imported.
+    FFTW_LOCK;
+    if (!fftwWisdomImported) {
+        fftwf_import_system_wisdom();
+        fftwWisdomImported = true;
+    }
+    FFTW_UNLOCK;
+
+    long num = in->n;                   // Number of elements
+
+    // Do the FFT
+    FFTW_LOCK;
+    fftwf_complex *out = fftwf_malloc((num/2 + 1) * sizeof(fftwf_complex)); // Output data
+    fftwf_plan plan = fftwf_plan_dft_r2c_1d(num, in->data.F32, out, FFTW_PLAN_RIGOR);
+    FFTW_UNLOCK;
+
+    fftwf_execute(plan);
+
+    FFTW_LOCK;
+    fftwf_destroy_plan(plan);
+    FFTW_UNLOCK;
+
+    // Pull the real and imaginary parts out
+    num = num/2 + 1;
+    *real = psVectorRecycle(*real, num, PS_TYPE_F32);
+    *imag = psVectorRecycle(*imag, num, PS_TYPE_F32);
+    for (int i = 0; i < num; i++) {
+#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
+        // C99 complex support
+        (*real)->data.F32[i] = creal(out[i]);
+        (*imag)->data.F32[i] = cimag(out[i]);
+#else
+        // FFTW's backup complex support
+        (*real)->data.F32[i] = out[i][0];
+        (*imag)->data.F32[i] = out[i][1];
+#endif
+    }
+
+    FFTW_LOCK;
+    fftwf_free(out);
+    FFTW_UNLOCK;
+
+    return true;
+}
+
+bool psVectorBackwardFFT(psVector **out, const psVector *real, const psVector *imag, long origNum)
+{
+    PS_ASSERT_VECTOR_NON_NULL(real, false);
+    PS_ASSERT_VECTOR_TYPE(real, PS_TYPE_F32, false);
+    PS_ASSERT_VECTOR_NON_NULL(imag, false);
+    PS_ASSERT_VECTOR_TYPE(imag, PS_TYPE_F32, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(real, imag, false);
+    PS_ASSERT_PTR_NON_NULL(out, false);
+
+    bool threaded = psThreadPoolSize(); // Are we running threaded?
+
+    // Make sure the system-level wisdom information is imported.
+    FFTW_LOCK;
+    if (!fftwWisdomImported) {
+        fftwf_import_system_wisdom();
+        fftwWisdomImported = true;
+    }
+    FFTW_UNLOCK;
+
+    long num = real->n;                 // Number of elements
+
+    // Stuff the real and imaginary parts in
+    FFTW_LOCK;
+    fftwf_complex *in = fftwf_malloc(num * sizeof(fftwf_complex)); // Input data
+    FFTW_UNLOCK;
+    for (int i = 0; i < num; i++) {
+#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
+        // C99 complex support
+        in[i] = real->data.F32[i] + imag->data.F32[i] * I;
+#else
+        // FFTW's backup complex support
+        in[i][0] = real->data.F32[i];
+        in[i][1] = imag->data.F32[i];
+#endif
+    }
+
+
+    // Do the FFT
+    *out = psVectorRecycle(*out, origNum, PS_TYPE_F32);
+    FFTW_LOCK;
+    fftwf_plan plan = fftwf_plan_dft_c2r_1d(origNum, in, (*out)->data.F32, FFTW_PLAN_RIGOR);
+    FFTW_UNLOCK;
+
+    fftwf_execute(plan);
+
+    FFTW_LOCK;
+    fftwf_destroy_plan(plan);
+    fftwf_free(in);
+    FFTW_UNLOCK;
+
+    return true;
+}
+
+psVector *psVectorPowerSpectrum(psVector* out, const psVector* in)
+{
+    PS_ASSERT_VECTOR_NON_NULL(in, NULL);
+    PS_ASSERT_VECTOR_TYPE(in, PS_TYPE_F32, NULL);
+
+    psVector *real = NULL;              // Real component of FFT
+    psVector *imag = NULL;              // Imaginary component of FFT
+
+    if (!psVectorForwardFFT(&real, &imag, in)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to perform forward FFT.");
+        return NULL;
+    }
+
+    int num = real->n;                  // Number of elements
+
+    float norm = 1.0 / PS_SQR(in->n);
+    for (int i = 0; i < num; i++) {
+        // Power spectrum is the square of the complex modulus
+        real->data.F32[i] = norm * (PS_SQR(real->data.F32[i]) + PS_SQR(imag->data.F32[i]));
+    }
+    psFree(imag);
+
+    return real;
+}
+
+bool psVectorComplexMultiply(psVector **outReal, psVector **outImag,
+                             const psVector *in1Real, const psVector *in1Imag,
+                             const psVector *in2Real, const psVector *in2Imag)
+{
+    PS_ASSERT_VECTOR_NON_NULL(in1Real, false);
+    PS_ASSERT_VECTOR_NON_NULL(in1Imag, false);
+    PS_ASSERT_VECTOR_NON_NULL(in2Real, false);
+    PS_ASSERT_VECTOR_NON_NULL(in2Imag, false);
+    PS_ASSERT_VECTOR_TYPE(in1Real, PS_TYPE_F32, false);
+    PS_ASSERT_VECTOR_TYPE(in1Imag, PS_TYPE_F32, false);
+    PS_ASSERT_VECTOR_TYPE(in2Real, PS_TYPE_F32, false);
+    PS_ASSERT_VECTOR_TYPE(in2Imag, PS_TYPE_F32, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(in1Imag, in1Real, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(in2Real, in1Real, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(in2Imag, in1Real, false);
+    PS_ASSERT_PTR_NON_NULL(outReal, false);
+    PS_ASSERT_PTR_NON_NULL(outImag, false);
+    if (*outReal) {
+        PS_ASSERT_VECTOR_NON_NULL(*outReal, false);
+        PS_ASSERT_VECTOR_NON_NULL(*outImag, false);
+    } else if (*outImag) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "If the output real part is provided, the output imaginary part must also be provided.");
+        return false;
+    }
+
+    int num = in1Real->n;               // Number of elements
+
+    *outReal = psVectorRecycle(*outReal, num, PS_TYPE_F32);
+    *outImag = psVectorRecycle(*outImag, num, PS_TYPE_F32);
+
+    for (int i = 0; i < num; i++) {
+        // (a + bi) * (c + di) = (ac - bd) + (bc + ad)i
+        float real = in1Real->data.F32[i] * in2Real->data.F32[i] -
+            in1Imag->data.F32[i] * in2Imag->data.F32[i];
+        float imag = in1Imag->data.F32[i] * in2Real->data.F32[i] +
+            in1Real->data.F32[i] * in2Imag->data.F32[i];
+
+        (*outReal)->data.F32[i] = real;
+        (*outImag)->data.F32[i] = imag;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fft/psVectorFFT.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fft/psVectorFFT.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fft/psVectorFFT.h	(revision 22322)
@@ -0,0 +1,65 @@
+/// @file  psVectorFFT.h
+/// @brief Contains FFT transform related functions for psVector
+///
+/// @author Paul Price, IfA
+/// @author Robert DeSonia, MHPCC
+///
+/// @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
+/// @date $Date: 2007-02-08 04:23:57 $
+/// Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+///
+
+#ifndef PS_VECTOR_FFT_H
+#define PS_VECTOR_FFT_H
+
+#include "psVector.h"
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/// Forward FFT of a vector
+///
+/// Applies a forward FFT (exponent -1), with the result returned in both real and imaginary parts.  The FFT
+/// is not normalised (a forward followed by a reverse is the original scaled by the number of elements).  The
+/// FFT takes advantage of the fact that the input is purely real; hence the output size is N/2 + 1 (with
+/// division rounding down).  Only implemented for F32 input.
+bool psVectorForwardFFT(psVector **real,///< Real part of FFT
+                        psVector **imag,///< Imaginary part of FFT
+                        const psVector *in ///< Input vector (F32)
+    );
+
+/// Backward FFT of a vector
+///
+/// Applies a backward FFT (exponent +1) from the real and imaginary parts with the (purely) real result
+/// returned.  The FFT is not normalised (a forward followed by a reverse is the original scaled by the number
+/// of elements).  The FFT takes advantage of the fact that the output will be purely real; hence the input
+/// size is N/2 + 1 (with division rounding down); to manage the redundancy (is the original size even or
+/// odd?), we need the original size (the size of the array that was input to psVectorForwardFFT) to be
+/// provided.  Only implemented for F32 input.
+bool psVectorBackwardFFT(psVector **out,///< Output vector
+                         const psVector *real, ///< Real input (F32)
+                         const psVector *imag, ///< Imaginary input (F32)
+                         long origNum    ///< Original number of elements
+    );
+
+/// Power spectrum of a vector
+///
+/// Generates the power spectrum of a vector.  Only implemented for F32 input.
+psVector *psVectorPowerSpectrum(psVector *out, ///< Output power spectrum, or NULL
+                                const psVector* in ///< Input vector (F32)
+    );
+
+/// Multiply complex vectors
+///
+/// The input vectors are the real and imaginary parts of each of two vectors.  The real and imaginary parts
+/// of the output vector are returned.  Only implemented for F32 input.
+bool psVectorComplexMultiply(psVector **outReal, ///< Real part of output
+                             psVector **outImag, ///< Imaginary part of output
+                             const psVector *in1Real, ///< Real part of input 1
+                             const psVector *in1Imag, ///< Imaginary part of input 1
+                             const psVector *in2Real, ///< Real part of input 2
+                             const psVector *in2Imag ///< Imaginary part of input 2
+    );
+
+/// @}
+#endif // #ifndef PS_VECTOR_FFT_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/Makefile.am	(revision 22322)
@@ -0,0 +1,26 @@
+#Makefile for fits functions of psLib
+#
+noinst_LTLIBRARIES = libpslibfits.la
+
+libpslibfits_la_CFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(CFITSIO_CFLAGS) $(AM_CFLAGS)
+libpslibfits_la_SOURCES = \
+	psFits.c \
+	psFitsHeader.c \
+	psFitsImage.c \
+	psFitsTable.c \
+	psFitsFloat.c \
+	psFitsFloatFile.c \
+	psFitsScale.c
+
+EXTRA_DIST = fits.i
+
+pkginclude_HEADERS = \
+	psFits.h \
+	psFitsHeader.h \
+	psFitsImage.h \
+	psFitsTable.h \
+	psFitsFloat.h \
+	psFitsFloatFile.h \
+	psFitsScale.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/fits.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/fits.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/fits.i	(revision 22322)
@@ -0,0 +1,5 @@
+/* fits headers */
+%include "psFits.h"
+%include "psFitsHeader.h"
+%include "psFitsImage.h"
+%include "psFitsTable.h"
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFits.c	(revision 22322)
@@ -0,0 +1,893 @@
+/** @file  psFits.c
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.81 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-12 22:54:53 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "psFits.h"
+#include "psFitsHeader.h"
+#include "psError.h"
+#include "psAssert.h"
+#include "psImageStructManip.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psLogMsg.h"
+#include "psTrace.h"
+#include "psVector.h"
+#include "psAbort.h"
+#include "psFitsFloat.h"
+
+#define MAX_STRING_LENGTH 256  // maximum length string for FITS routines
+static char *defaultExtword = "EXTNAME";
+
+bool p_psFitsDumpErrors (const char* filename, unsigned int lineno, const char* func, 
+			 psErrorCode code, const char *message, ...) {
+
+    char fitsErr[MAX_STRING_LENGTH];
+
+    va_list ap;
+    va_start(ap, message);
+    p_psErrorV (filename, lineno, func, PS_ERR_BAD_FITS, true, message, ap);
+    va_end(ap);
+
+    while (fits_read_errmsg(fitsErr)) {
+	p_psError(filename, lineno, func, PS_ERR_BAD_FITS, false, "CFITSIO error: %s", fitsErr);
+    }
+
+    return true;
+}
+
+psErrorCode p_psFitsError(const char* filename, unsigned int lineno, const char* func, 
+			  int status, bool new, const char *errorMsg, ...)
+{
+    char fitsErr[MAX_STRING_LENGTH];
+
+    if (status == 0) return PS_ERR_NONE;
+
+    va_list ap;                         // Variable arguments
+    va_start(ap, errorMsg);
+    psErrorV (PS_ERR_IO, new, errorMsg, ap);
+    va_end(ap);
+
+    while (fits_read_errmsg(fitsErr)) {
+	psError(PS_ERR_IO, false, "[CFITSIO error: %s]", fitsErr);
+    }
+    return PS_ERR_IO;
+}
+
+static bool isHDUEmpty(const psFits* fits)
+{
+    /* check for keys - no keys means this is really an empty HDU */
+    int keysexist = -1;
+    int morekeys;
+    int status = 0;
+
+    fits_get_hdrspace(fits->fd, &keysexist, &morekeys, &status);
+
+    // if no keys exist and not primary HDU, this really is an empty HDU
+    if (keysexist == 0) {
+        return true;
+    }
+
+    return false;
+
+}
+
+static bool fitsClose(psFits* fits)
+{
+    int status = 0;
+
+    if (fits != NULL) {
+        if (fits_close_file(fits->fd, &status)) {
+	    psFitsDumpErrors (PS_ERR_IO, "Error while closing psFits object");
+            return false;
+        }
+        fits->fd = NULL;
+    }
+    return true;
+}
+
+static void fitsFree(psFits* fits)
+{
+    if (!fits) return;
+    if (fits->fd) {
+        fitsClose(fits);
+    }
+    psFree(fits->options);
+}
+
+bool psFitsClose(psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    bool status = fitsClose(fits);
+    psFree(fits);
+
+    return status;
+}
+
+psFits* psFitsOpen(const char* name, const char* mode)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    int status = 0;
+    fitsfile *fptr = NULL;      /* Pointer to the FITS file */
+
+    /* check the mode to determine how to open/create file */
+    int iomode;
+    bool newFile;
+    if (strcmp(mode,"r") == 0) {
+        iomode = READONLY;
+        newFile = false;
+    } else if (strcmp(mode,"rw") == 0 || strcmp(mode,"r+") == 0) {
+        iomode = READWRITE;
+        newFile = false;
+    } else if (strcmp(mode,"w") == 0 || strcmp(mode,"w+") == 0) {
+        iomode = READWRITE;
+        newFile = true;
+    } else if (strcmp(mode,"a") == 0|| strcmp(mode,"a+") == 0) {
+        iomode = READWRITE;
+        newFile = (access(name, F_OK) != 0);
+    } else {
+        // mode is not valid
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified mode, '%s', is invalid.  Supported modes are r, r+, rw, w, w+, a, or a+.",
+                mode);
+        return NULL;
+    }
+
+    if (newFile) {
+        /* Check if an existing file is in the way before creating file*/
+        if (access(name, F_OK) == 0) {
+            // file exists, delete old one first
+            if (remove(name)) {
+		int thisErrno = errno;
+		char errorBuf[64], *errorMsg;
+# if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE)
+		errorMsg = strerror_r (errno, errorBuf, 64);
+# else
+		strerror_r (errno, errorBuf, 64);
+		errorMsg = errorBuf;		
+# endif
+		psError (PS_ERR_IO, true, "Failed to delete a previously-existing file: %s", errorMsg);
+		fprintf (stderr, "errno: %d, %s, %s : %lx, %lx\n", thisErrno, errorMsg, errorBuf, (long int) errorMsg, (long int) errorBuf);
+		return NULL;
+	    }
+        }
+        if (!access(name, F_OK)) {
+	    psError (PS_ERR_IO, true, "deleted file still exists!");
+	    return NULL;
+	}
+
+        #if ( CFITSIO_DISKFILE == 1 )
+        (void)fits_create_diskfile
+        #else
+        (void)fits_create_file
+        #endif
+        (&fptr, name, &status);
+        if (fptr == NULL || status != 0) {
+	    psFitsDumpErrors (PS_ERR_IO, _("Could not create file,'%s'"), name);
+            return NULL;
+        }
+    } else {
+        #if ( CFITSIO_DISKFILE == 1 )
+        (void)fits_open_diskfile
+        #else
+        (void)fits_open_file
+        #endif
+        (&fptr, name, iomode, &status);
+        if (fptr == NULL || status != 0) {
+	    psFitsDumpErrors(PS_ERR_IO, _("Could not open file,'%s'"), name);
+            return NULL;
+        }
+    }
+
+    psFits* fits = psAlloc(sizeof(psFits));
+    psMemSetDeallocator(fits, (psFreeFunc)fitsFree);
+
+    fits->fd = fptr;
+    fits->writable = (iomode == READWRITE);
+
+    fits->options = NULL;
+
+    return fits;
+}
+
+
+static void fitsOptionsFree(psFitsOptions *options)
+{
+    psFree(options->extword);
+}
+
+
+psFitsOptions *psFitsOptionsAlloc(void)
+{
+    psFitsOptions *options = psAlloc(sizeof(psFitsOptions)); // Options, to return
+    psMemSetDeallocator(options, (psFreeFunc)fitsOptionsFree);
+
+    options->extword = NULL;
+
+    options->conventions.compression = true;
+    options->conventions.psBitpix = true;
+
+    options->floatType = PS_FITS_FLOAT_NONE;
+
+    options->bitpix = 0;
+
+    options->scaling = PS_FITS_SCALE_NONE;
+    options->fuzz = true;
+    options->bscale = 1.0;
+    options->bzero = 0.0;
+    options->mean = NAN;
+    options->stdev = NAN;
+    options->stdevBits = 4;
+    options->stdevNum = 5.0;
+
+    return options;
+}
+
+static void psFitsCompressionFree(psFitsCompression *comp)
+{
+    PS_ASSERT_PTR_NON_NULL(comp,);
+    psFree(comp->tilesize);
+}
+
+psFitsCompression* psFitsCompressionAlloc(
+    psFitsCompressionType type,         ///< type of compression
+    psVector *tilesize,                 ///< vector defining compression tile size
+    int noisebits,                      ///< noise bits
+    int scale,                          ///< hcompress scale
+    int smooth                          ///< hcompress smothing
+)
+{
+    psFitsCompression *comp = psAlloc(sizeof(psFitsCompression));
+    psMemSetDeallocator(comp, (psFreeFunc) psFitsCompressionFree);
+
+    comp->type = type;
+    comp->tilesize = psVectorCopy(NULL, tilesize, PS_DATA_S64);
+    comp->noisebits = noisebits;
+    comp->scale = scale;
+    comp->smooth = smooth;
+
+    return comp;
+}
+
+bool psMemCheckFits(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)fitsFree );
+}
+
+bool psFitsSetExtnameWord(psFits *fits, const char *extword)
+{
+    PS_ASSERT_PTR_NON_NULL(fits,    false);
+    PS_ASSERT_PTR_NON_NULL(extword, false);
+
+    if (!fits->options) {
+        fits->options = psFitsOptionsAlloc();
+    }
+
+    psFree(fits->options->extword);
+    fits->options->extword = psStringCopy(extword);
+    return true;
+}
+
+// Files compressed with cfitsio's "imcopy" program may have multiple EXTNAME keywords, with the first set to
+// COMPRESSED_IMAGE.  However, fits_movnam_hdu won't find the second (proper) value of EXTNAME, and so can
+// fail to find a perfectly legitimate extension, simply because imcopy does something silly.  However, we
+// really want to be able to read these files (MegaCam data are shipped as imcopy-compressed images).
+// Therefore, we implement our own version of moving to an extension specified by name.  The pure cfitsio
+// version is used if "conventions.compression" handling is turned off in the psFits structure.
+bool psFitsMoveExtName(const psFits* fits,
+                       const char* extname)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_STRING_NON_EMPTY(extname, false);
+
+    int status = 0;
+
+    psFitsOptions *options = fits->options; // FITS options
+    if (options && !options->conventions.compression && !options->extword) {
+        // User wants to use cfitsio.  Good luck to them!
+        if (fits_movnam_hdu(fits->fd, ANY_HDU, (char*)extname, 0, &status) != 0) {
+            psFitsError(status, true, _("Could not find HDU '%s'"), extname);
+            return false;
+        }
+        return true;
+    }
+
+    // Ignore EXTNAME with value COMPRESSED_IMAGE?
+    bool ignoreCI = (!options || (options->conventions.compression && (strcmp(extname, "COMPRESSED_IMAGE") != 0)));
+
+    char *extword = (options && options->extword) ? options->extword : "EXTNAME"; // Word for extension name
+
+#if 0
+    // XXX Future optimisation: loop through from the current HDU to the end, then from the start to the
+    // current position.  This will save seeking through the file multiple times.
+    int currentExt = psFitsGetExtNum(fits); // Current extension number
+    int numExt = psFitsGetSize(fits);   // Total number of extensions
+#endif
+
+    for (int i = 1; true; i++) {
+        int hdutype = 0;
+        if (fits_movabs_hdu(fits->fd, i, &hdutype, &status)) {
+            // We've run off the end
+            psFitsError(status, true, _("Could not find HDU with %s = '%s'"), extword, extname);
+            return false;
+        }
+        // Is there a keyword called 'extword'? (read as string regardless of type)
+        char name[MAX_STRING_LENGTH];  // Name of extension
+        if (fits_read_keyword(fits->fd, extword, name, NULL, &status)) {
+            // It doesn't exist in the header.
+            // This isn't the extension you're looking for.  Move along.
+            status = 0;
+            continue;
+        }
+        char *fixed = p_psFitsHeaderParseString(name); // Parsed version (removing quotes and spaces)
+
+        if (ignoreCI && strcmp(fixed, "COMPRESSED_IMAGE") == 0) {
+            // Read it again, Sam
+            if (fits_read_keyword(fits->fd, extword, name, NULL, &status)) {
+                status = 0;
+                continue;
+            }
+            fixed = p_psFitsHeaderParseString(name);
+        }
+
+        if (strcmp(fixed, extname) == 0) {
+            // We've arrived
+            return true;
+        }
+    }
+    psAbort("Should never reach here.");
+}
+
+bool psFitsMoveExtNum(const psFits* fits,
+                      int extnum,
+                      bool relative)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    int status = 0;
+    int hdutype = 0;
+
+    if (relative) {
+        fits_movrel_hdu(fits->fd, extnum, &hdutype, &status);
+        if (status != 0) {
+	    psFitsDumpErrors (PS_ERR_LOCATION_INVALID, _("Could not move %d HDUs from current position"), extnum);
+            return false;
+        }
+    } else {
+        fits_movabs_hdu(fits->fd, extnum+1, &hdutype, &status);
+        if (status != 0) {
+	    psFitsDumpErrors (PS_ERR_LOCATION_INVALID, _("Could not move to specified HDU #%d."), extnum);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool psFitsMoveLast(psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    int size = psFitsGetSize(fits);
+    if (size == 0) { // empty file -- no action needed
+        return true;
+    } else {
+        return psFitsMoveExtNum(fits,size-1,false);
+    }
+}
+
+int psFitsGetExtNum(const psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    int hdunum;
+    return fits_get_hdu_num(fits->fd,&hdunum) - 1;
+}
+
+psString psFitsGetExtName(const psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    int status = 0;
+    char name[MAX_STRING_LENGTH];
+
+    psFitsOptions *options = fits->options; // FITS options
+    char *extword = (!options || !options->extword) ? defaultExtword : options->extword;
+
+    if (fits_read_key_str(fits->fd, extword, name, NULL, &status) != 0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("Header keyword %s is not found"), extword);
+        return NULL;
+    }
+    return psStringCopy(name);
+}
+
+bool psFitsSetExtName(psFits* fits, const char* name)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    int status = 0;
+
+    psFitsOptions *options = fits->options; // FITS options
+    char *extword = (!options || !options->extword) ? defaultExtword : options->extword;
+
+    if (fits_update_key_str(fits->fd, extword, (char*)name, NULL, &status) != 0) {
+	psFitsDumpErrors (PS_ERR_IO, _("Could not write data to file %s"), name);
+        return false;
+    }
+
+    return true;
+}
+
+bool psFitsDeleteExtNum(psFits* fits,
+                        int extnum,
+                        bool relative)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+
+    // move to the specified HDU
+    if (!psFitsMoveExtNum(fits, extnum, relative) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Failed to delete HDU #%d", extnum);
+        return false;
+    }
+
+    int status = 0;
+
+    // OK, now let's delete the HDU
+    if (fits_delete_hdu(fits->fd, NULL, &status) != 0) {
+        psFitsDumpErrors(PS_ERR_IO, _("Could not write data to file extnum %d"), extnum);
+        return false;
+    }
+
+    return true;
+}
+
+bool psFitsDeleteExtName(psFits* fits,
+                         const char* extname)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_STRING_NON_EMPTY(extname, false);
+
+    // move to the specified HDU
+    if (! psFitsMoveExtName(fits,extname) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Failed to delete HDU with the name '%s'",
+                extname);
+        return false;
+    }
+
+
+    int status = 0;
+
+    // OK, now let's delete the HDU
+    if (fits_delete_hdu(fits->fd, NULL, &status) != 0) {
+        psFitsDumpErrors(PS_ERR_IO, _("Could not write data to file extname %s"), extname);
+        return false;
+    }
+
+    return true;
+}
+
+int psFitsGetSize(const psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
+
+    int num = 0;
+    int status = 0;
+
+    if (fits_get_num_hdus(fits->fd, &num, &status) != 0) {
+        psFitsDumpErrors(PS_ERR_LOCATION_INVALID, _("Failed to determine the number of HDUs"));
+        return 0;
+    }
+
+    return num;
+}
+
+psFitsType psFitsGetExtType(const psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, PS_FITS_TYPE_NONE);
+
+    int status = 0;
+    int hdutype = PS_FITS_TYPE_NONE;
+
+    if (fits_get_hdu_type(fits->fd, &hdutype, &status) != 0) {
+        psFitsDumpErrors(PS_ERR_LOCATION_INVALID, _("Failed to determine an HDU type"));
+        return PS_FITS_TYPE_NONE;
+    }
+
+    if (hdutype == PS_FITS_TYPE_IMAGE &&
+            psFitsGetExtNum(fits) > 0 &&
+            isHDUEmpty(fits)) {
+        return PS_FITS_TYPE_ANY;
+    }
+
+    return hdutype;
+}
+
+bool psFitsTruncate(psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+
+    int newEnd = psFitsGetExtNum(fits);
+
+    psFitsMoveLast(fits);
+    int end = psFitsGetExtNum(fits);
+
+    // delete HDUs from end to beginning position + 1;
+    for (int lcv=end;lcv > newEnd; lcv--) {
+        if (! psFitsDeleteExtNum(fits,lcv,false)) {
+            // failed to delete an HDU!?
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to truncate file.  HDU #%d out of %d could not be deleted.",
+                    lcv,end);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+bool psFitsSetCompression(
+    psFits* fits,                       ///< psFits object to close
+    psFitsCompressionType type,         ///< type of compression
+    psVector *tilesize,
+    int noisebits,                      ///< noise bits
+    int scale,                          ///< hcompress scale
+    int smooth                          ///< hcompress smothing
+)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+
+    // convert psFitsCompressionType to cfitsio compression types
+    int comptype;
+    switch (type) {
+        case PS_FITS_COMPRESS_NONE:
+            comptype = 0x0;
+            break;
+        case PS_FITS_COMPRESS_GZIP:
+            comptype = GZIP_1;
+            break;
+        case PS_FITS_COMPRESS_RICE:
+            comptype = RICE_1;
+            break;
+        case PS_FITS_COMPRESS_HCOMPRESS:
+            comptype = HCOMPRESS_1;
+            break;
+        case PS_FITS_COMPRESS_PLIO:
+            comptype = PLIO_1;
+            break;
+        default:
+            psError(PS_ERR_UNKNOWN, true, "invalid psFitsCompressionType");
+            return false;
+    }
+
+    int status = 0;
+    if (fits_set_compression_type(fits->fd, comptype, &status)) {
+        psFitsDumpErrors(PS_ERR_BAD_FITS, "Error while configuring compression");
+        return false;
+    }
+
+    // if we are setting a trivial compression (NONE), don't bother with the other parameters
+    if (type == PS_FITS_COMPRESS_NONE) {
+        return true;
+    }
+
+    PS_ASSERT_VECTOR_NON_NULL(tilesize, false);
+
+    // convert a psVector into the (long *) array that cfitsio requires
+    psVector *dim = NULL;
+    if (sizeof(long) == sizeof(psS64)) {
+        dim = psVectorCopy(NULL, tilesize, PS_DATA_S64);
+        fits_set_tile_dim(fits->fd, psVectorLength(dim), (long *)dim->data.S64, &status);
+    } else if (sizeof(long) == sizeof(psS32)) {
+        dim = psVectorCopy(NULL, tilesize, PS_DATA_S32);
+        fits_set_tile_dim(fits->fd, psVectorLength(dim), (long *)dim->data.S32, &status);
+    } else {
+        psAbort("can't map (long) type to a psLib type");
+    }
+    psFree(dim);
+    // status check belongs to fits_set_tile_dim() call
+    if (status) {
+        fits_set_compression_type(fits->fd, 0x0, &status);
+        psFitsDumpErrors(PS_ERR_BAD_FITS, "Error while configuring compression");
+        return false;
+    }
+
+    // noise bits are irrelevant (not allowed) for PLIO.  XXX actually, it is the data type
+    // that is the restriction; data must be 32 or 64 bit for noise bits to be valid.
+    if (type != PS_FITS_COMPRESS_PLIO) {
+        if (fits_set_noise_bits(fits->fd, noisebits, &status)) {
+            fits_set_compression_type(fits->fd, 0x0, &status);
+            psFitsDumpErrors(PS_ERR_BAD_FITS, "Error while configuring compression");
+            return false;
+        }
+    }
+
+#if FITS_HCOMP
+    if (fits_set_hcomp_scale(fits->fd, scale, &status)) {
+        fits_set_compression_type(fits->fd, 0x0, &status);
+        psError(PS_ERR_BAD_FITS, status, "Error while configuring compression");
+        return false;
+    }
+    if (fits_set_hcomp_smooth(fits->fd, smooth, &status)) {
+        fits_set_compression_type(fits->fd, 0x0, &status);
+        psError(PS_ERR_BAD_FITS, status, "Error while configuring compression");
+        return false;
+    }
+#endif // FITS_HCOMP
+
+    return true;
+}
+
+psFitsCompression *psFitsCompressionGet(psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    int status = 0;                     // cfitsio status
+
+    psFitsCompressionType type = psFitsCompressionGetType(fits);
+    if (type < 0) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to get compression type.");
+        return NULL;
+    }
+
+    psElemType tileType;                // Type corresponding to "long"
+    if (sizeof(long) == sizeof(psS64)) {
+        tileType = PS_TYPE_S64;
+    } else if (sizeof(long) == sizeof(psS32)) {
+        tileType = PS_TYPE_S32;
+    } else {
+        psAbort("can't map (long) type to a psLib type");
+    }
+
+    psVector *tiles = psVectorAlloc(3, tileType); // Tile sizes
+    if (fits_get_tile_dim(fits->fd, 3, (long*)tiles->data.U8, &status)) {
+        psFitsError(status, true, "Unable to get compression tile sizes.");
+        psFree(tiles);
+        return NULL;
+    }
+
+    int noisebits;                      // Noise bits for compression
+    if (fits_get_noise_bits(fits->fd, &noisebits, &status)) {
+        psFitsError(status, true, "Unable to get compression noise bits.");
+        psFree(tiles);
+        return NULL;
+    }
+
+    int hscale = 0, hsmooth = 0;        // Scaling and smoothing for HCOMPRESS
+
+#if FITS_HCOMP
+    if (fits_get_hcomp_scale(fits->fd, &hscale, &status)) {
+        psFitsError(status, true, "Unable to get HCOMPRESS scaling.");
+        psFree(tiles);
+        return NULL;
+    }
+    if (fits_get_hcomp_smooth(fits->fd, &hsmooth, &status)) {
+        psFitsError(status, true, "Unable to get HCOMPRESS smoothing.");
+        psFree(tiles);
+        return NULL;
+    }
+#endif // FITS_HCOMP
+
+    psFitsCompression *compress = psFitsCompressionAlloc(type, tiles, noisebits, hscale, hsmooth);
+    psFree(tiles);                      // Drop reference
+
+    return compress;
+}
+
+psFitsCompressionType psFitsCompressionGetType(psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, -1);
+
+    int status = 0;                     // cfitsio status
+    int comptype = 0;                   // cfitsio compression type
+    if (fits_get_compression_type(fits->fd, &comptype, &status)) {
+        psFitsError(status, true, "Unable to get compression type.");
+        return -1;
+    }
+
+    psFitsCompressionType type;
+    switch (comptype) {
+      case 0:
+        type = PS_FITS_COMPRESS_NONE;
+        break;
+      case GZIP_1:
+        type = PS_FITS_COMPRESS_GZIP;
+        break;
+      case RICE_1:
+        type = PS_FITS_COMPRESS_RICE;
+        break;
+      case HCOMPRESS_1:
+        type = PS_FITS_COMPRESS_HCOMPRESS;
+        break;
+      case PLIO_1:
+        type = PS_FITS_COMPRESS_PLIO;
+        break;
+      default:
+        psError(PS_ERR_UNKNOWN, true, "cfitsio reports unknown compression type.");
+        return -1;
+    }
+
+    return type;
+}
+
+
+bool psFitsCompressionApply(
+    psFits* fits,                       ///< psFits object to close
+    psFitsCompression *comp             ///< options object
+)
+{
+    return psFitsSetCompression(fits, comp->type, comp->tilesize, comp->noisebits, comp->scale, comp->smooth);
+}
+
+
+psDataType p_psFitsTypeFromCfitsio(int datatype)
+{
+    switch (datatype) {
+    case TBYTE:
+        return PS_TYPE_U8;
+    case TSBYTE:
+        return PS_TYPE_S8;
+    case TSHORT:
+        return PS_TYPE_S16;
+    case TUSHORT:
+        return PS_TYPE_U16;
+    case TLONG:
+        if (sizeof(long) == 8) {
+            return PS_TYPE_S64;
+        }
+        // no break
+    case TINT:
+        return PS_TYPE_S32;
+    case TULONG:
+        if (sizeof(unsigned long) == 8) {
+            return PS_TYPE_U64;
+        }
+        // no break
+    case TUINT:
+        return PS_TYPE_U32;
+    case TLONGLONG:
+        return PS_TYPE_S64;
+    case TFLOAT:
+        return PS_TYPE_F32;
+    case TDOUBLE:
+        return PS_TYPE_F64;
+    case TLOGICAL:
+        return PS_TYPE_BOOL;
+    default:
+        psError(PS_ERR_IO, true,
+                "Unknown FITS datatype, %d.",
+                datatype);
+        return 0;
+    }
+}
+
+bool p_psFitsTypeToCfitsio(psDataType type, int* bitPix, double* bZero, int* dataType)
+{
+    int bitpix;
+    int datatype;
+    double bzero = 0.0;
+
+    switch (type) {
+
+    case PS_TYPE_U8:
+        bitpix = BYTE_IMG;
+        // bzero = -1.0 * INT8_MIN;
+        datatype = TBYTE;
+        break;
+
+    case PS_TYPE_S8:
+        bitpix = BYTE_IMG;
+        bzero = +1.0 * INT8_MIN;
+        datatype = TSBYTE;
+        break;
+
+    case PS_TYPE_U16:
+        bitpix = SHORT_IMG;
+        bzero = -1.0 * INT16_MIN;
+        datatype = TUSHORT;
+        break;
+
+    case PS_TYPE_S16:
+        bitpix = SHORT_IMG;
+        datatype = TSHORT;
+        break;
+
+    case PS_TYPE_U32:
+        bitpix = LONG_IMG;
+        bzero = -1.0 * INT32_MIN;
+        datatype = TUINT;
+        break;
+
+    case PS_TYPE_S32:
+        bitpix = LONG_IMG;
+        datatype = TINT;
+        break;
+
+    case PS_TYPE_F32:
+        bitpix = FLOAT_IMG;
+        datatype = TFLOAT;
+        break;
+
+    case PS_TYPE_F64:
+        bitpix = DOUBLE_IMG;
+        datatype = TDOUBLE;
+        break;
+
+    case PS_DATA_STRING:
+        bitpix = BYTE_IMG;
+        datatype = TSTRING;
+        break;
+
+    case PS_DATA_BOOL:
+        bitpix = BYTE_IMG;
+        datatype = TLOGICAL;
+        break;
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified type, %s, is not supported."),
+                    typeStr);
+            return false;
+        }
+    }
+
+    // pass back the requested parameters  (NULL parameters are not set, of course).
+    if (bitPix != NULL) {
+        *bitPix = bitpix;
+    }
+
+    if (dataType != NULL) {
+        *dataType = datatype;
+    }
+
+    if (bZero != NULL) {
+        *bZero = bzero;
+    }
+
+    return true;
+}
+
+
+psFitsCompressionType psFitsCompressionTypeFromString(const char *string)
+{
+    if (!string || strlen(string) == 0) {
+        psWarning("Unable to identify compression type --- none set.");
+        return PS_FITS_COMPRESS_NONE;
+    }
+
+    if (strcmp(string, "NONE") == 0) return PS_FITS_COMPRESS_NONE;
+    if (strcmp(string, "GZIP") == 0) return PS_FITS_COMPRESS_GZIP;
+    if (strcmp(string, "RICE") == 0) return PS_FITS_COMPRESS_RICE;
+    if (strcmp(string, "HCOMPRESS") == 0) return PS_FITS_COMPRESS_HCOMPRESS;
+    if (strcmp(string, "PLIO") == 0) return PS_FITS_COMPRESS_PLIO;
+
+    psWarning("Unable to identify compression type (%s) --- none set.", string);
+    return PS_FITS_COMPRESS_NONE;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFits.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFits.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFits.h	(revision 22322)
@@ -0,0 +1,362 @@
+/* @file  psFits.h
+ * @brief Contains Fits I/O routines
+ *
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.37 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-12 22:54:53 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FITS_H
+#define PS_FITS_H
+
+/// @addtogroup FileIO Input/Output
+/// @{
+
+#include <fitsio.h>
+#include "psType.h"
+#include "psArray.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psImage.h"
+#include "psFitsFloat.h"
+
+#include "psErrorCodes.h"
+
+/// FITS HDU type.
+typedef enum {
+    PS_FITS_TYPE_NONE = -1,            ///< Unknown HDU type
+    PS_FITS_TYPE_IMAGE = IMAGE_HDU,    ///< Image HDU type
+    PS_FITS_TYPE_BINARY_TABLE = BINARY_TBL, ///< Binary table HDU type
+    PS_FITS_TYPE_ASCII_TABLE = ASCII_TBL,   ///< ASCII table HDU type
+    PS_FITS_TYPE_ANY = ANY_HDU         ///< Any HDU type
+} psFitsType;
+
+/// FITS compression type
+typedef enum {
+    PS_FITS_COMPRESS_NONE = 0,          ///< No compression
+    PS_FITS_COMPRESS_GZIP,              ///< GZIP compression (of the pixels only)
+    PS_FITS_COMPRESS_RICE,              ///< RICE compression (of the pixels only)
+    PS_FITS_COMPRESS_HCOMPRESS,         ///< HCOMPRESS compression (of the pixels only)
+    PS_FITS_COMPRESS_PLIO               ///< PLIO compression (of the pixels only; appropriate for masks)
+} psFitsCompressionType;
+
+/// FITS scaling method: how to set BSCALE and BZERO
+typedef enum {
+    PS_FITS_SCALE_NONE,                 ///< No auto-scaling to be applied (BSCALE = 1, BZERO = 0)
+    PS_FITS_SCALE_RANGE,                ///< Auto-scale to preserve dynamic range
+    PS_FITS_SCALE_STDEV_POSITIVE,       ///< Auto-scale to sample stdev, place mean at lower limit
+    PS_FITS_SCALE_STDEV_NEGATIVE,       ///< Auto-scale to sample stdev, place mean at upper limit
+    PS_FITS_SCALE_STDEV_BOTH,           ///< Auto-scale to sample stdev, place mean at middle
+    PS_FITS_SCALE_MANUAL                ///< Manual scaling (use specified BSCALE and BZERO)
+} psFitsScaling;
+
+/// Options for FITS I/O
+typedef struct {
+    char *extword;                      ///< user-specified word to name extensions (NULL implies EXTNAME)
+    struct {
+        bool compression;               ///< Compression convention: handling of compressed images
+        bool psBitpix;                  ///< Custom floating-point image
+    } conventions;                      ///< Conventions to honour
+    // The following options are particular to writing images; they needn't be set for anything else.
+    psFitsFloat floatType;              ///< Desired custom floating-point for output images
+    int bitpix;                         ///< Desired BITPIX for output images; 0 to use as provided
+    psFitsScaling scaling;              ///< Scaling scheme to use when quantising floating-point values
+    bool fuzz;                          ///< Fuzz the values when quantising floating-point values?
+    double bscale, bzero;               ///< Manually specified BSCALE and BZERO (for SCALE_MANUAL)
+    double mean, stdev;                 ///< Mean and standard deviation of image
+    int stdevBits;                      ///< Number of bits to sample a standard deviation (for SCALE_STDEV_*)
+    float stdevNum;                     ///< Number of standard deviations to pad off the edge
+} psFitsOptions;
+
+
+/// FITS file
+typedef struct {
+    fitsfile* fd;                       ///< the CFITSIO fits files handle.
+    bool writable;                      ///< Is the file writable?
+    psFitsOptions *options;             ///< Options for FITS I/O, or NULL
+} psFits;
+
+
+/** FITS compression settings. */
+typedef struct {
+    psFitsCompressionType type;         ///< type of compression
+    psVector *tilesize;                 ///< vector defining compression tile size
+    int noisebits;                      ///< noise bits
+    int scale;                          ///< hcompress scale
+    int smooth;                         ///< hcompress smothing
+} psFitsCompression;
+
+/** Opens a FITS file and allocates the associated psFits object.
+ *
+ *  @return psFits*    new psFits object for the FITS files specified or
+ *                     NULL if the open of the FITS file failed
+ */
+psFits* psFitsOpen(
+    const char* filename,              ///< the FITS file name
+    const char* mode
+    /**< File open mode. Could be one of the following:
+     *       'r' (read only),
+     *       'r+' (read & write),
+     *       'rw' (same as 'r+'), or
+     *       'w' (create new file for writing)
+     */
+);
+
+
+/// Generate an error including the cfitsio error string
+#ifdef DOXYGEN
+psErrorCode psFitsError(
+    int status,                         ///< cfitsio status value
+    bool new,                           ///< new error?
+    const char *errorMsg,               ///< printf-style format of header line
+    ...                                 ///< any parameters required in format
+    );
+#else // ifdef DOXYGEN
+psErrorCode p_psFitsError(
+    const char* filename,               ///< file name
+    unsigned int lineno,                ///< line number in file
+    const char* func,                   ///< function name
+    int status,                         ///< cfitsio status value
+    bool new,                           ///< new error?
+    const char *errorMsg,               ///< printf-style format of header line
+    ...                                 ///< any parameters required in format
+    ) PS_ATTR_FORMAT(printf, 6, 7);
+#ifndef SWIG
+#define psFitsError(status,new,...) \
+      p_psFitsError(__FILE__,__LINE__,__func__,status,new,__VA_ARGS__)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+/// dump the full CFITSIO error stack
+#ifdef DOXYGEN
+bool psFitsDumpErrors(
+    psErrorCode code,                   ///< psLib code to use
+    const char *message,                ///< printf-style format of header line
+    ...                                 ///< any parameters required in format
+    );
+#else // ifdef DOXYGEN
+bool p_psFitsDumpErrors(
+    const char* filename,               ///< file name
+    unsigned int lineno,                ///< line number in file
+    const char* func,                   ///< function name
+    psErrorCode code,                   ///< psLib code to use
+    const char *message,                ///< printf-style format of header line
+    ...                                 ///< any parameters required in format
+    ) PS_ATTR_FORMAT(printf, 5, 6);
+#ifndef SWIG
+#define psFitsDumpErrors(CODE,...) \
+        p_psFitsDumpErrors(__FILE__,__LINE__,__func__,CODE,__VA_ARGS__)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+/** Creates a new FITS options struct
+ *
+ *  @return psFitsOptions or NULL on failure
+ */
+psFitsCompression* psFitsCompressionAlloc(
+    psFitsCompressionType type,         ///< type of compression
+    psVector *tilesize,                 ///< vector defining compression tile size
+    int noisebits,                      ///< noise bits
+    int scale,                          ///< hcompress scale
+    int smooth                          ///< hcompress smothing
+);
+
+/// Return the FITS compression type specified by a string
+psFitsCompressionType psFitsCompressionTypeFromString(
+    const char *string                  ///< String with compression type
+    );
+
+/** Closes a FITS file.
+ *
+ *  @return bool      TRUE if FITS file was successfully closed, otherwise FALSE
+ */
+bool psFitsClose(
+    psFits* fits                       ///< psFits object to close
+);
+
+/// Allocator for options
+psFitsOptions *psFitsOptionsAlloc(void);
+
+/** Enables/configures FITS compression.
+ *
+ * Note that HCOMPRESS compression is not presently supported.
+ *
+ *  @return bool      TRUE if successfully configured, otherwise FALSE
+ */
+bool psFitsSetCompression(
+    psFits* fits,                       ///< psFits object for which to set compression
+    psFitsCompressionType type,         ///< type of compression
+    psVector *tilesize,                 ///< vector defining compression tile size
+    int noisebits,                      ///< noise bits
+    int scale,                          ///< hcompress scale
+    int smooth                          ///< hcompress smothing
+);
+
+/// Get the compression options for a file handle
+psFitsCompression *psFitsCompressionGet(
+    psFits* fits                        ///< psFits object for which to get compression
+);
+
+/// Get the compression type for a file handle
+psFitsCompressionType psFitsCompressionGetType(
+    psFits* fits                        ///< psFits object for which to get compression type
+    );
+
+/** Sets FITS write options
+ *
+ *  @return bool      TRUE if successfully configured, otherwise FALSE
+ */
+bool psFitsCompressionApply(
+    psFits* fits,                       ///< psFits object for which to set compression
+    psFitsCompression *compress         ///< options object
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr
+ *  datatype.
+ *
+ *  @return bool:       True if the pointer matches a psFits structure, false
+ *                      otherwise.
+ */
+bool psMemCheckFits(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+// set the user-defined extension name;
+bool psFitsSetExtnameWord (psFits *fits, const char *extword);
+
+// move to the first HDU where extword == extname.  this is equivalent to fits_movnam_hdu() for
+// a user-defined word in place of EXTNAME
+bool p_psFitsMoveExtName_UserKey(const psFits *fits,
+                                 const char *extname,
+                                 const char *extword);
+
+/** Moves the FITS HDU to the specified extension name.
+ *
+ *  @return bool        TRUE if the extension name was found and move was
+ *                      successful, otherwise FALSE
+ */
+bool psFitsMoveExtName(
+    const psFits* fits,                ///< the psFits object to move
+    const char* extname                ///< the extension name
+);
+
+/** Moves the FITS HDU to the specified extension number
+ *
+ *  @return bool        TRUE if the extension number was found and move was
+ *                      successful, otherwise FALSE
+ */
+bool psFitsMoveExtNum(
+    const psFits* fits,                ///< the psFits object to move
+    int extnum,                        ///< the extension number to move to (zero is primary HDU)
+    bool relative                      ///< if true, extnum is a relative number to the current position
+);
+
+/** Moves the FITS HDU to the end of the file
+ *
+ *  @return bool        TRUE if the move was successful, otherwise FALSE
+*/
+bool psFitsMoveLast(
+    psFits* fits                       ///< the psFits object to move
+);
+
+/** Get the current extension number, where 0 is the primary HDU.
+ *
+ *  @return int        Current HDU number of the psFits file or < 0 if an error
+ *                     occurred.
+ */
+int psFitsGetExtNum(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Get the current extension name.
+ *
+ *  @return int        Current HDU name of the psFits file or NULL if an
+ *                     error occurred.
+ */
+psString psFitsGetExtName(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Set the current extension's name
+ *
+ *  @return bool       TRUE if the extension was successfully set, otherwise FALSE.
+ */
+bool psFitsSetExtName(
+    psFits* fits,                      ///< the psFits object
+    const char* name                   ///< the extension name
+);
+
+/** Get the total number of HDUs in the FITS file.
+ *
+ *  @return int        The total number of HDUs in the FITS file or < 0 if an
+ *                     error occurred.
+ */
+int psFitsGetSize(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Remove the an HDU as specified by number
+ *
+ *  @return bool        TRUE if the specified HDU was removed, otherwise FALSE
+ */
+bool psFitsDeleteExtNum(
+    psFits* fits,                      ///< the psFits object
+    int extnum,                        ///< the extension number to delete (zero is primary HDU)
+    bool relative                      ///< if true, extnum is a relative number to the current position
+);
+
+/** Remove the an HDU as specified by extension name
+ *
+ *  @return bool        TRUE if the specified HDU was removed, otherwise FALSE
+ */
+bool psFitsDeleteExtName(
+    psFits* fits,                      ///< the psFits object
+    const char* extname                ///< the extension name to delete
+);
+
+/** Get the extension type of the current HDU.
+ *
+ *  @return psFitsType The type of the current HDU.  If PS_FITS_TYPE_UNKNOWN,
+ *                     the type could not be determined.
+ */
+psFitsType psFitsGetExtType(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Delete all extensions after the current position
+ *
+ *  @return bool        TRUE if the operation was successful, otherwise FALSE
+ */
+bool psFitsTruncate(
+    psFits* fits                       ///< the psFits object
+);
+
+// Return the psLib type, given a cfitsio data type
+psDataType p_psFitsTypeFromCfitsio(int datatype // cfitsio data type
+                                  );
+
+// Return the cfitsio data type, given a psLib type
+bool p_psFitsTypeToCfitsio(psDataType type, // psLib data type
+                           int* bitPix, // The corresponding BITPIX (returned)
+                           double* bZero, // The corresponding BZERO (returned)
+                           int* dataType// The corresponding cfitsio data type (returned)
+                          );
+
+#define PS_ASSERT_FITS_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->fd) { \
+    psError(PS_ERR_UNEXPECTED_NULL, true, "Error: FITS file pointer %s is NULL", #NAME); \
+    return RVAL; \
+}
+
+#define PS_ASSERT_FITS_WRITABLE(NAME, RVAL) \
+if (!(NAME)->writable) { \
+    psError(PS_ERR_UNEXPECTED_NULL, true, "Error: FITS file %s not open for writing.", #NAME); \
+    return RVAL; \
+}
+
+/// @}
+#endif // #ifndef PS_FITS_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloat.c	(revision 22322)
@@ -0,0 +1,192 @@
+/*
+** Copyright (C) 2008 Institute for Astronomy, University of Hawaii
+**
+** This is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Lesser General Public
+** License as published by the Free Software Foundation; either
+** version 2.1 of the License, or (at your option) any later version.
+**
+** The GNU C Library is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+** Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public
+** License along with the GNU C Library; if not, write to the Free
+** Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+** 02111-1307 USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include "psAbort.h"
+#include "psType.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psFits.h"
+#include "psTrace.h"
+#include "psMemory.h"
+
+#include "psFitsFloat.h"
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// This definition of IEEE754 floating-point, with some modifications, is from the GNU C Library, ieee754.h
+// Copyright (C) 1992, 1995, 1996, 1999 Free Software Foundation, Inc.
+union float_ieee754 {
+    float f;                            // Floating point
+    struct {                            // IEEE 754 single-precision format
+#ifdef WORDS_BIGENDIAN
+        unsigned int negative:1;
+        unsigned int exponent:8;
+        unsigned int mantissa:23;
+#else
+        unsigned int mantissa:23;
+        unsigned int exponent:8;
+        unsigned int negative:1;
+#endif
+    } ieee;
+};
+#define BIAS_FLOAT_IEEE754         0x7f // Exponent bias for IEEE754
+// End definition from ieee754.h
+
+
+// 16-bit floating point
+union float_16_0 {
+    psS16 s16;                          // 16-bit integer version
+        struct {                        // Floating-point version
+        unsigned int negative:1;        // Sign bit
+        unsigned int exponent:5;        // Exponent bits
+        unsigned int mantissa:10;       // Mantissa bits
+    } f16;
+};
+#define BIAS_FLOAT_16_0              10 // Exponent bias for FLOAT_16_0
+
+
+static inline psS16 convertF32toFloat16_0(psF32 value)
+{
+    union float_ieee754 in;             // Input value
+    union float_16_0 out;               // Output value
+
+    // XXX What happens to NAN and INF?
+    in.f = value;
+    out.f16.negative = in.ieee.negative;
+    out.f16.exponent = in.ieee.exponent - BIAS_FLOAT_IEEE754 + BIAS_FLOAT_16_0;
+    out.f16.mantissa = in.ieee.mantissa >> 13;
+
+    return out.s16;
+}
+
+static inline psF32 convertF32fromFloat16_0(psS16 value)
+{
+    union float_16_0 in;                // Input value
+    union float_ieee754 out;            // Output value
+
+    in.s16 = value;
+    out.ieee.negative = in.f16.negative;
+    out.ieee.exponent = in.f16.exponent + BIAS_FLOAT_IEEE754 - BIAS_FLOAT_16_0;
+    out.ieee.mantissa = in.f16.mantissa << 13;
+
+    return out.f;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+psImage *psFitsFloatImageToDisk(const psImage *image, psFitsFloat type)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+
+    psImage *output = NULL;             // Output image, to return
+
+    switch (type) {
+      case PS_FITS_FLOAT_NONE:
+        // No conversion to be performed
+        return psMemIncrRefCounter((psImage*)image); // Casting away "const"
+      case PS_FITS_FLOAT_16_0:
+        output = psImageAlloc(image->numCols, image->numRows, PS_TYPE_S16); // Output image
+        for (int y = 0; y < image->numRows; y++) {
+            for (int x = 0; x < image->numCols; x++) {
+                output->data.S16[y][x] = convertF32toFloat16_0(image->data.F32[y][x]);
+            }
+        }
+        break;
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised custom floating-point type: %x", type);
+        return NULL;
+    }
+
+    return output;
+}
+
+
+psImage *psFitsFloatImageFromDisk(psImage *out, const psImage *in, psFitsFloat type)
+{
+    PS_ASSERT_IMAGE_NON_NULL(in, NULL);
+
+    psElemType elem = psFitsFloatImageType(type); // Type for elements
+    if (elem == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to recognise convention: %x", type);
+        return NULL;
+    }
+
+    int numCols = in->numCols, numRows = in->numRows; // Size of image
+
+    out = psImageRecycle(out, numCols, numRows, elem);
+
+    switch (type) {
+      case PS_FITS_FLOAT_16_0:
+        if (in->type.type != PS_TYPE_S16) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Convention claims to be FLOAT_16_0, but image type is not S16.");
+            return NULL;
+        }
+        psAssert(out->type.type == PS_TYPE_F32, "impossible");
+        for (int y = 0; y < numRows; y++) {
+            for (int x = 0; x < numCols; x++) {
+                out->data.F32[y][x] = convertF32fromFloat16_0(in->data.S16[y][x]);
+            }
+        }
+        return out;
+      case PS_FITS_FLOAT_NONE:
+      default:
+        psAbort("Should be unreachable");
+    }
+    return NULL;
+}
+
+psElemType psFitsFloatImageType(psFitsFloat type)
+{
+    switch (type) {
+      case PS_FITS_FLOAT_16_0:
+        return PS_TYPE_F32;
+      case PS_FITS_FLOAT_NONE:
+      default:
+        return 0;                       // Doesn't correspond to ANY type --- should flag a real error
+    }
+}
+
+
+psFitsFloat psFitsFloatTypeFromString(const char *string)
+{
+    PS_ASSERT_STRING_NON_EMPTY(string, PS_FITS_FLOAT_NONE);
+
+    if (strcmp(string, "FLOAT_16_0") == 0) {
+        return PS_FITS_FLOAT_16_0;
+    }
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+            "Unable to recognise custom floating-point convention name: %s", string);
+    return PS_FITS_FLOAT_NONE;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloat.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloat.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloat.h	(revision 22322)
@@ -0,0 +1,33 @@
+#ifndef PS_FITS_FLOAT_H
+#define PS_FITS_FLOAT_H
+
+#include <psFits.h>
+#include <psType.h>
+#include <psImage.h>
+
+/// Type of custom floating point
+typedef enum {
+    PS_FITS_FLOAT_NONE,                 ///< No conversion to be performed
+    PS_FITS_FLOAT_16_0,                 ///< Original F16 proposal: 1S 5E 10M
+} psFitsFloat;
+
+/// Convert an image to custom floating-point (the disk representation) in preparation for writing as FITS
+psImage *psFitsFloatImageToDisk(const psImage *image, ///< Image to convert
+                               psFitsFloat type ///< Custom floating point type
+    );
+
+/// Convert the custom floating-point image (the disk representation) to a normal floating-point image
+psImage *psFitsFloatImageFromDisk(psImage *out, ///< Output image, or NULL
+                                  const psImage *in, ///< Image to convert
+                                  psFitsFloat type ///< Custom floating point type
+    );
+
+/// Return the appropriate element type for a custom floating-point
+psElemType psFitsFloatImageType(psFitsFloat type ///< Custom floating-point type
+    );
+
+/// Return the custom floating-point type from a string description
+psFitsFloat psFitsFloatTypeFromString(const char *string ///< String with name of type
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloatFile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloatFile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloatFile.c	(revision 22322)
@@ -0,0 +1,65 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "psError.h"
+#include "psFits.h"
+#include "psFitsFloat.h"
+#include "psMemory.h"
+
+#include "psFitsFloatFile.h"
+
+bool psFitsFloatImageSet(const psFits *fits, psFitsFloat type)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+
+    char *convName;                     // Convention name
+
+    switch (type) {
+      case PS_FITS_FLOAT_NONE:
+        return true;
+      case PS_FITS_FLOAT_16_0:
+        convName = "FLOAT_16_0";
+        break;
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to recognise convention: %x", type);
+        return false;
+    }
+
+    int status = 0;                     // Status from cfitsio
+    fits_write_key_str(fits->fd, "PSBITPIX", convName, "Custom floating point convention name", &status);
+    if (psFitsError(status, true, "Could not write PSBITPIX header to file.")) {
+        return false;
+    }
+    return true;
+}
+
+
+psFitsFloat psFitsFloatImageCheck(const psFits *fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, PS_FITS_FLOAT_NONE);
+
+    psFitsOptions *options = fits->options; // FITS I/O options
+
+    if (!options || !options->conventions.psBitpix) {
+        return PS_FITS_FLOAT_NONE;
+    }
+
+    int status = 0;                     // Status of CFITSIO calls
+    char convName[FLEN_CARD];           // Convention name for custom floating-point
+    if (fits_read_key_str(fits->fd, "PSBITPIX", convName, NULL, &status) && status != KEY_NO_EXIST) {
+        psFitsError(status, true, "Unable to read header.");
+        return PS_FITS_FLOAT_NONE;
+    }
+
+    // XXX convName is static if (!convName || status == KEY_NO_EXIST) {
+    if (status == KEY_NO_EXIST) {
+        return PS_FITS_FLOAT_NONE;
+    }
+
+    return psFitsFloatTypeFromString(convName);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloatFile.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloatFile.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsFloatFile.h	(revision 22322)
@@ -0,0 +1,20 @@
+// This file exists to resolve the co-dependency problem of placing these function definitions in
+// psFitsFloat.h --- because they refer to psFits, and psFits refers to psFitsFloat, we would end up with a
+// co-dependency problem if these function declarations were to be placed in psFitsFloat.h.
+#ifndef PS_FITS_FLOAT_FILE_H
+#define PS_FITS_FLOAT_FILE_H
+
+#include <psFits.h>
+#include <psFitsFloat.h>
+
+/// Set a flag in the FITS header of the current extension that the image is a custom floating-point
+bool psFitsFloatImageSet(const psFits *fits,  ///< FITS file
+                         psFitsFloat type ///< Custom floating-point type
+    );
+
+/// Check if the current extension contains a custom floating-point, returning the appropriate type
+psFitsFloat psFitsFloatImageCheck(const psFits *fits ///< FITS file
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsHeader.c	(revision 22322)
@@ -0,0 +1,754 @@
+/** @file  psFitsHeader.c
+ *
+ *  @brief Contains Fits header I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.47 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-02 02:50:41 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+
+#include "psAbort.h"
+#include "psAssert.h"
+#include "psFits.h"
+#include "psError.h"
+
+#include "psImageStructManip.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psLogMsg.h"
+#include "psTrace.h"
+#include "psVector.h"
+
+#define MAX_STRING_LENGTH 256           // maximum length string for FITS routines
+#define NUM_EMPTY_KEYS 8                // Number of keywords before header is considered practically empty
+
+// list of FITS header keys to ignore; NULL-terminated
+static const char* ignoreFitsKeys[] = { "", NULL };
+
+// List of FITS header keys that may be duplicated; NULL-terminated
+static const char *duplicateFitsKeys[] = { "COMMENT", "HIERARCH", "HISTORY", NULL };
+
+// List of FITS header keys that are put in the comment by cfitsio; but we want them in the value.
+static const char *commentFitsKeys[] = { "COMMENT", "HISTORY", NULL };
+
+// List of FITS header keys not to write (handled by cfitsio); NULL-terminated
+static const char *noWriteFitsKeys[] = { "SIMPLE", "XTENSION", "BITPIX", "NAXIS", "EXTNAME", "BSCALE",
+                                         "BZERO", "TFIELDS", "PCOUNT", "GCOUNT", "PSBITPIX", "BLANK", NULL };
+
+// List of the start of FITS header keys not to write (handled by cfitsio); NULL-terminated
+static const char *noWriteFitsKeyStarts[] = { "NAXIS", "TTYPE", "TFORM", NULL };
+
+// List of compressed FITS header keys not to write (handled by cfitsio); NULL-terminated
+static const char *noWriteCompressedKeys[] = { "ZBITPIX", "ZIMAGE", "ZBITPIX", "ZCMPTYPE", "ZSIMPLE",
+                                               "ZEXTEND", "ZBLANK", NULL };
+
+// List of the start of FITS header keys not to write (handled by cfitsio); NULL-terminated
+static const char *noWriteCompressedKeyStarts[] = { "ZNAXIS", "ZTILE", "ZNAME", "ZVAL", NULL };
+
+// List of FITS header keys that may be present if the header is considered "empty"; NULL-terminated
+static const char *emptyKeys[] = { "SIMPLE", "BITPIX", "NAXIS", "EXTEND", "COMMENT", "CHECKSUM", "DATASUM",
+                                   NULL  };
+
+// How to translate between keywords
+typedef struct {
+    const char *from;                   // Translate from this keyword
+    const char *to;                     // Translate to this keyword
+} keywordTranslation;
+
+// Translation for compressed image headers           FROM        TO
+// The "From" and "To" are appropriate for reading.
+static keywordTranslation compressTranslation[] = { { "ZSIMPLE",  "SIMPLE" },
+                                                    { "ZTENSION", "XTENSION" },
+                                                    { "ZBITPIX",  "BITPIX" },
+                                                    { "ZNAXIS",   "NAXIS"  },
+                                                    { "ZNAXIS1",  "NAXIS1" },
+                                                    { "ZNAXIS2",  "NAXIS2" },
+                                                    { "ZNAXIS3",  "NAXIS3" },
+                                                    { "ZEXTEND",  "EXTEND" },
+                                                    { "ZBLOCKED", "BLOCKED"},
+                                                    //                                                    { "ZPCOUNT",  "PCOUNT" },
+                                                    //                                                    { "ZGCOUNT",  "GCOUNT" },
+                                                    { "ZHECKSUM", "CHECKSUM"},
+                                                    { "ZDATASUM", "DATASUM" },
+                                                    { NULL,       NULL } };
+
+
+// Compare a keyword with a list of keywords; return true if it's in the list
+static bool keywordInList(const char *keyword, // Keyword to check
+                          const char *list[] // List of keywords
+    )
+{
+    for (const char **check = list; *check; ++check) {
+        if (strcmp(keyword, *check) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// Compare a keyword with a list of keyword beginnings; return true if the keyword starts with one of these
+static bool keywordStartsWith(const char *keyword, // Keyword to check
+                              const char *list[] // List of keyword beginnings
+                              )
+{
+    bool writeKey = true;   // Write this keyword?
+    for (int i = 0; list[i] && writeKey; i++) {
+        if (strncmp(keyword, list[i], strlen(list[i])) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+
+// Translate one keyword to another.
+// This is appropriate for reading
+static const char *keywordTranslate(const char *keyword, // Keyword to check
+                                    keywordTranslation translation[] // Translation list
+                                    )
+{
+    for (keywordTranslation *trans = translation; (*trans).from; ++trans) {
+        if (strcmp(keyword, (*trans).from) == 0) {
+            // Translate it
+            return (*trans).to;
+        } else if (strcmp(keyword, (*trans).to) == 0) {
+            // Ignore it completely --- something else will translate to it
+            return NULL;
+        }
+    }
+    // It translates to itself
+    return keyword;
+}
+
+// Translate back the other way.
+// This is appropriate for writing.
+static const char *keywordUntranslate(const char *keyword, // Keyword to check
+                                      keywordTranslation translation[] // Translation list
+    )
+{
+    for (keywordTranslation *trans = translation; (*trans).to; ++trans) {
+        if (strcmp(keyword, (*trans).to) == 0) {
+            // Translate it
+            return (*trans).from;
+        }
+    }
+    // It translates to itself
+    return keyword;
+}
+
+
+bool psFitsCheckSingleCompressedImagePHU(const psFits *fits, psMetadata *header)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    if (fits->options && !fits->options->conventions.compression) {
+        // User has turned off compression conventions; doesn't want any nasty surprises
+        return false;
+    }
+
+    if (psFitsGetExtNum(fits) != 0) {
+        // It's not the PHU, so it can't be the PHU for a single compressed image!
+        return false;
+    }
+
+    if (psFitsGetSize(fits) != 2) {
+        // No second extension, or multiple extensions
+        return false;
+    }
+
+    int numKeys;                        // Number of keywords in the header
+    int status = 0;                     // CFITSIO status
+    fits_get_hdrspace(fits->fd, &numKeys, 0, &status);
+    if (numKeys > NUM_EMPTY_KEYS) {
+        return false;
+    }
+
+    int bitpix, naxis;                  // Bits per pixel and number of axes
+    long naxes[MAX_COMPRESS_DIM];       // Dimensions
+    fits_get_img_param(fits->fd, MAX_COMPRESS_DIM, &bitpix, &naxis, naxes, &status);
+    if (naxis != 0) {
+        return false;
+    }
+
+    if (!header) {
+        for (int i = 1; i <= numKeys; i++) {
+            // Just want to read the keyword names, without parsing the values and stuffing into a metadata
+            char keyName[MAX_STRING_LENGTH];// Keyword name
+            char keyValue[MAX_STRING_LENGTH]; // Corresponding value
+            char keyComment[MAX_STRING_LENGTH]; // Corresponding comment
+            fits_read_keyn(fits->fd, i, keyName, keyValue, keyComment, &status);
+            if (!keywordInList(keyName, emptyKeys)) {
+                return false;
+            }
+        }
+    } else {
+        psMetadataIterator *iter = psMetadataIteratorAlloc(header, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *item;           // Item from iteration
+        while ((item = psMetadataGetAndIncrement(iter))) {
+            if (!keywordInList(item->name, emptyKeys)) {
+                psFree(iter);
+                return false;
+            }
+        }
+        psFree(iter);
+    }
+
+    if (!psFitsMoveExtNum(fits, 1, false)) {
+        psWarning("Unable to examine first extension as suspect compressed image.");
+        return false;
+    }
+
+    if (fits_is_compressed_image(fits->fd, &status)) {
+        return true;
+    }
+
+    // It's not a single compressed image PHU --- move back to the PHU for the user
+    if (!psFitsMoveExtNum(fits, 0, false)) {
+        psWarning("Unable to examine first extension as suspect compressed image.");
+        return false;
+    }
+
+    return false;
+}
+
+char *p_psFitsHeaderParseString(char *string)
+{
+    if (!string || strlen(string) == 0) {
+        return string;
+    }
+
+    char *fixed = string;       // Fixed version of the string
+    // remove the single-quotes at front/end
+    if (fixed[0] == '\'' && fixed[strlen(string)-1] == '\'') {
+        string[strlen(string)-1] = '\0'; // Remove the trailing quote
+        fixed += 1; // Advance past the leading quote
+    }
+    // Remove trailing spaces, which are not significant, according to the FITS standard
+    // http://archive.stsci.edu/fits/fits_standard/node31.html
+    char *lastSpace = NULL; // The last space in the string
+    while (strlen(fixed) > 0 && (lastSpace = strrchr(fixed, ' ')) && lastSpace[1] == '\0') {
+        // This is a trailing space, not a leading space.
+        lastSpace[0] = '\0'; // Truncate the string here
+    }
+
+    return fixed;
+}
+
+// Read the header
+static psMetadata *readHeader(const psFits *fits // FITS file from which to read header
+                              )
+{
+    psAssert(fits, "impossible");
+
+    psMetadata *header = psMetadataAlloc(); // Header, to return
+
+    // Get number of key names
+    int numKeys = 0;                    // Number of keywords
+    int keyNum = 0;                     // Current key number
+    int status = 0;                     // Status of cfitsio calls
+    fits_get_hdrpos(fits->fd, &numKeys, &keyNum, &status);
+
+    bool compressed = false;            // Is this a compressed image?
+    if ((!fits->options || fits->options->conventions.compression) &&
+        fits_is_compressed_image(fits->fd, &status)) {
+        compressed = true;
+    }
+
+    // Get each key name. Keywords start at one.
+    for (int i = 1; i <= numKeys; i++) {
+        char keyName[MAX_STRING_LENGTH];// Keyword name
+        char keyValue[MAX_STRING_LENGTH]; // Corresponding value
+        char keyComment[MAX_STRING_LENGTH]; // Corresponding comment
+        fits_read_keyn(fits->fd, i, keyName, keyValue, keyComment, &status);
+
+        // Check to see if the keyword should be ignored
+        if (keywordInList(keyName, ignoreFitsKeys)) {
+            // We're done here; skip to the next key
+            continue;
+        }
+
+        const char *keyNameTrans = keyName;   // Translated name of keyword
+        if (compressed) {
+            keyNameTrans = keywordTranslate(keyName, compressTranslation);
+            if (!keyNameTrans) {
+                // It's to be ignored (it will be replaced by something else)
+                continue;
+            }
+        }
+
+        // Check to see if the keyword should be duplicated
+        int dupFlag = 0;                // Duplicate flag
+        if (keywordInList(keyName, duplicateFitsKeys)) {
+            dupFlag = PS_META_DUPLICATE_OK;
+        }
+
+        bool success;                   // Was the add to the metadata successful?
+
+        // Certain keywords (COMMENT and HISTORY) are put in the comment rather than value by cfitsio.
+        if (keywordInList(keyName, commentFitsKeys)) {
+            success = psMetadataAddStr(header, PS_LIST_TAIL, keyNameTrans, dupFlag, NULL, keyComment);
+        } else {
+            char keyType;                   // Type of key; from cfitsio
+            if (keyValue[0] != 0) { // blank values are not handled by fits_get_keytype
+                fits_get_keytype(keyValue, &keyType, &status);
+            } else {
+                keyType = 'C';
+            }
+            if (status != 0) {
+                break;
+            }
+
+            switch (keyType) {
+              case 'X': // bit
+              case 'B': // byte
+                success = psMetadataAddS8(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                          atoi(keyValue));
+                break;
+              case 'I': // short int.
+                // This is the default type that cfitsio reports whenever it doesn't know what it is.
+                // Trap NAN, INF and -INF, which cfitsio doesn't handle.
+                if (strncasecmp(keyValue, "NAN", 3) == 0) {
+                    success = psMetadataAddF32(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment, NAN);
+                } else if (strncasecmp(keyValue, "INF", 3) == 0) {
+                    success = psMetadataAddF32(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                               INFINITY);
+                } else if (strncasecmp(keyValue, "-INF", 4) == 0) {
+                    success = psMetadataAddF32(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                               -INFINITY);
+                } else {
+                    success = psMetadataAddS32(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                               atoi(keyValue));
+                }
+                break;
+              case 'J': // int.
+                success = psMetadataAddS32(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                           atoi(keyValue));
+                break;
+              case 'U': // unsigned int.
+                success = psMetadataAddU32(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                           atol(keyValue));
+                break;
+
+              case 'K': // long int. can't all fit in a psS32, put in psF64
+              case 'F':
+                success = psMetadataAddF64(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                           atof(keyValue));
+                break;
+              case 'C': {
+                  char *keyValueFixed = p_psFitsHeaderParseString(keyValue); // Fixed version of the string
+
+                  // Need to trap NAN, INF and -INF written by psFitsWriteHeader: cfitsio won't write these,
+                  // so we write them as strings, and then have to trap them on read.
+                  if (strcasecmp(keyValueFixed, "NAN") == 0) {
+                      success = psMetadataAddF32(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                                 NAN);
+                  } else if (strcasecmp(keyValueFixed, "INF") == 0) {
+                      success = psMetadataAddF32(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                                 INFINITY);
+                  } else if (strcasecmp(keyValueFixed, "-INF") == 0) {
+                      success = psMetadataAddF32(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                                 -INFINITY);
+                  } else if (compressed && strcmp(keyName, "EXTNAME") == 0 &&
+                             strcmp(keyValueFixed, "COMPRESSED_IMAGE") == 0) {
+                      // Ignore EXTNAME=COMPRESSED_IMAGE if compression convention is to be respected
+                      success = true;
+                  } else {
+                      success = psMetadataAddStr(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment,
+                                                 keyValueFixed);
+                  }
+                  break;
+              }
+              case 'L': {
+                  bool temp = (keyValue[0] == 'T') ? 1 : 0;
+                  success = psMetadataAddBool(header, PS_LIST_TAIL, keyNameTrans, dupFlag, keyComment, temp);
+                  break;
+              }
+              default:
+                psError(PS_ERR_IO, true, _("Specified FITS metadata type, %c, is not supported."), keyType);
+                psFree(header);
+                return NULL;
+            }
+        }
+
+        if (!success) {
+            psError(PS_ERR_UNKNOWN, false, _("Failed to add metadata item, %s --> %s."),
+                    keyName, keyNameTrans);
+            psFree(header);
+            return NULL;
+        }
+
+    }
+
+    if (status != 0) {
+        psFitsError(status, true, _("Failed to add metadata item."));
+        psFree(header);
+        return false;
+    }
+
+    return header;
+}
+
+
+psMetadata* psFitsReadHeader(psMetadata* out,
+                             const psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    psMetadata *header = readHeader(fits); // Header
+    if (!header) {
+        return NULL;
+    }
+
+    // Explore the potential case that this is an empty PHU, and the first extension contains the sole image,
+    // which is compressed.
+    if (psFitsCheckSingleCompressedImagePHU(fits, header)) {
+        // This is really what we want, not the empty PHU
+        psTrace("psLib.fits", 1,
+                "This PHU should really be a compressed image --- getting that header instead.");
+        psFree(header);
+        header = readHeader(fits);
+        if (!header) {
+            return NULL;
+        }
+    }
+
+    if (!out) {
+        return header;
+    }
+
+    // Need to move header onto the nominated metadata
+    psMetadataIterator *iter = psMetadataIteratorAlloc(header, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;           // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        // Need to look for MULTI, which won't be picked up using the iterator.
+        psMetadataItem *multiCheckItem = psMetadataLookup(header, item->name);
+        psAssert(multiCheckItem, "impossible");
+        unsigned int flag = 0;      // Flag to indicate MULTI; otherwise default action
+        if (multiCheckItem->type == PS_DATA_METADATA_MULTI) {
+            flag = PS_META_DUPLICATE_OK;
+        }
+        if (!psMetadataAddItem(out, item, PS_LIST_TAIL, flag)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to add header item %s to extant metadata.",
+                    item->name);
+            psFree(iter);
+            psFree(header);
+            return NULL;
+        }
+    }
+    psFree(iter);
+    psFree(header);
+    return out;
+}
+
+psMetadata* psFitsReadHeaderSet(psMetadata* out, const psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    if (!out) {
+        out = psMetadataAlloc();
+    }
+
+    int size = psFitsGetSize(fits);
+
+    int origPosition = psFitsGetExtNum(fits);
+
+    for (int lcv=0; lcv < size; lcv++) {
+        psFitsMoveExtNum(fits, lcv, false);
+
+        char* name = NULL;
+        if (lcv == 0) {
+            name = psStringCopy("PHU");
+        } else {
+            name = psFitsGetExtName(fits);
+        }
+
+        psMetadata* header = psFitsReadHeader(NULL, fits);
+        if (name != NULL && header != NULL) {
+            psMetadataAddMetadata(out, PS_LIST_TAIL, name, 0, "FITS Header", header);
+        } else {
+            psWarning("Failed to read HDU#%d header data.",
+                      lcv);
+        }
+
+        psFree(name);
+        psFree(header);
+    }
+
+    // reposition to the original position
+    psFitsMoveExtNum(fits, origPosition, false);
+
+    return out;
+}
+
+static bool fitsWriteHeader(psFits *fits, // The FITS file handle
+                            const psMetadata *output, // Metadata that is to be output into the FITS file
+                            bool keyStarts // Write out the key starts?
+                           )
+{
+    int status = 0;                     // Status of cfitsio calls
+    bool simple = true;                 // If SIMPLE is T, then the file should conform to the FITS standard
+    psFitsCompressionType compress = psFitsCompressionGetType(fits); // Compression type
+    if (psFitsGetExtNum(fits) == 0) {
+        // We allow the user to write SIMPLE, but it must be boolean
+        psMetadataItem *simpleItem = psMetadataLookup(output, "SIMPLE"); // SIMPLE in the header
+        if (simpleItem) {
+            if (simpleItem->type != PS_DATA_BOOL) {
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, "SIMPLE in a FITS header must be of boolean type: "
+                        "not %x --- assuming FALSE.\n", simpleItem->type);
+                int value = false;          // Temporary holder for boolean
+                fits_update_key(fits->fd, TLOGICAL, "SIMPLE", &value,
+                                "File does not conform to FITS standard", &status);
+                simple = false;
+            } else if (!simpleItem->data.B) {
+                simple = false;
+                int value = false;      // Temporary holder for boolean
+                fits_update_key(fits->fd, TLOGICAL, "SIMPLE", &value,
+                                "File does not conform to FITS standard", &status);
+            }
+            // Uncompressed SIMPLE = T is taken care of by cfitsio.
+        }
+    }
+    if ((!fits->options || fits->options->conventions.compression) && compress != PS_FITS_COMPRESS_NONE) {
+        psMetadataItem *simpleItem = psMetadataLookup(output, "SIMPLE"); // SIMPLE in the header
+        if (simpleItem) {
+            if (simpleItem->type != PS_DATA_BOOL) {
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, "SIMPLE in a FITS header must be of boolean type: "
+                        "not %x --- assuming FALSE.\n", simpleItem->type);
+                simple = false;
+            } else {
+                simple = simpleItem->data.B;
+            }
+        }
+        //        int value = simple;             // Temporary holder for boolean
+        //        fits_update_key(fits->fd, TLOGICAL, "ZSIMPLE", &value,
+        //                        "Uncompressed file's conformance to FITS standard", &status);
+    }
+
+    // Traverse the metadata list and add each key.
+    psListIterator* iter = psListIteratorAlloc(output->list, PS_LIST_HEAD, true); // Iterator
+    psMetadataItem* item;               // Item from iteration
+    while ((item = psListGetAndIncrement(iter))) {
+        char *name = item->name;        // Keyword name to use when writing out
+        // Check to see if the item should be ignored
+        if (simple) {
+            // We ignore particular (required) keywords, because these are written by CFITSIO
+            // Furthermore, users tend to supply FITS headers that are wrong (e.g., after binning down the
+            // image, the NAXISn haven't been changed; or after converting to F32, the BITPIX hasn't been
+            // changed) so we'll take care of that for them.
+
+            // Also block out TTYPEn, NAXISn, etc --- keywords that start with a certain sequence.
+            // We want to do this when writing an image or table, since it guarantees that the NAXISn etc
+            // that go in are correct.  However, when we're writing a "blank" HDU (header only), we want
+            // to preserve NAXISn etc for reference, so we don't do this.
+
+            // Options for compression
+            if (!fits->options || fits->options->conventions.compression) {
+                // Check to see if the keyword should be translated
+                name = (char*)keywordUntranslate(name, compressTranslation); // Casting away const for cfitsio
+
+                if (keywordInList(name, noWriteCompressedKeys) ||
+                    (keyStarts && keywordStartsWith(name, noWriteCompressedKeyStarts))) {
+                    continue;
+                }
+            }
+
+            if (keywordInList(name, noWriteFitsKeys) ||
+                (keyStarts && keywordStartsWith(name, noWriteFitsKeyStarts))) {
+                continue;
+            }
+        }
+
+        if (strcmp(name, "COMMENT") == 0) {
+            if (item->type != PS_DATA_STRING) {
+                psWarning("COMMENT header is not of type STRING (%x) --- ignored.", item->type);
+            } else {
+                fits_write_comment(fits->fd, item->data.str, &status);
+            }
+        } else if (strcmp(name,  "HISTORY") == 0) {
+            if (item->type != PS_DATA_STRING) {
+                psWarning("COMMENT header is not of type STRING (%x) --- ignored.", item->type);
+            } else {
+                fits_write_history(fits->fd, item->data.str, &status);
+            }
+        } else {
+            // A regular FITS header
+            switch (item->type) {
+            case PS_DATA_BOOL: {
+                    int value = item->data.B;
+                    fits_update_key(fits->fd, TLOGICAL, name, &value, item->comment, &status);
+                    break;
+                }
+            case PS_DATA_S8:
+                fits_update_key(fits->fd, TBYTE, name, &item->data.S8, item->comment, &status);
+                break;
+            case PS_DATA_S16:
+                fits_update_key(fits->fd, TSHORT, name, &item->data.S16, item->comment, &status);
+                break;
+            case PS_DATA_S32:
+                fits_update_key(fits->fd, TINT, name, &item->data.S32, item->comment, &status);
+                break;
+            case PS_DATA_U8: {
+                    unsigned short int temp = item->data.U8;
+                    fits_update_key(fits->fd, TUSHORT, name, &temp, item->comment, &status);
+                }
+                break;
+            case PS_DATA_U16:
+                fits_update_key(fits->fd, TUSHORT, name, &item->data.U16, item->comment, &status);
+                break;
+            case PS_DATA_U32:
+                fits_update_key(fits->fd, TUINT, name, &item->data.U32, item->comment, &status);
+                break;
+            case PS_DATA_F32: {
+                    int infCheck = 0;         // Result of isinf()
+                    if (isnan(item->data.F32)) {
+                        fits_update_key(fits->fd, TSTRING, name, "NaN", item->comment, &status);
+                    } else if ((infCheck = isinf(item->data.F32)) != 0) {
+                        if (infCheck == 1) {
+                            fits_update_key(fits->fd, TSTRING, name, "Inf", item->comment, &status);
+                        } else {
+                            fits_update_key(fits->fd, TSTRING, name, "-Inf", item->comment, &status);
+                        }
+                    } else {
+                        fits_update_key(fits->fd, TFLOAT, name, &item->data.F32, item->comment,
+                                        &status);
+                    }
+                    break;
+                }
+            case PS_DATA_F64: {
+                    int infCheck = 0;         // Result of isinf()
+                    if (isnan(item->data.F64)) {
+                        fits_update_key(fits->fd, TSTRING, name, "NaN", item->comment, &status);
+                    } else if ((infCheck = isinf(item->data.F64)) != 0) {
+                        if (infCheck == 1) {
+                            fits_update_key(fits->fd, TSTRING, name, "Inf", item->comment, &status);
+                        } else {
+                            fits_update_key(fits->fd, TSTRING, name, "-Inf", item->comment, &status);
+                        }
+                    } else {
+                        fits_update_key(fits->fd, TDOUBLE, name, &item->data.F64, item->comment,
+                                        &status);
+                    }
+                    break;
+                }
+            case PS_DATA_STRING:
+                fits_update_key(fits->fd, TSTRING, name, item->data.V, item->comment, &status);
+                break;
+            default:  // all other META types are ignored
+                psLogMsg(__func__, PS_LOG_WARN, "Attempt to write metadata type %x to FITS header ignored.\n",
+                         item->type);
+                break;
+            }
+        }
+
+        if (status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            (void)fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true, _("Could not write data to file. CFITSIO Error: %s"), fitsErr);
+            return false;
+        }
+    }
+
+    psFree(iter);
+
+    return true;
+}
+
+bool psFitsWriteHeader(psFits *fits,
+                       const psMetadata *output
+                      )
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_METADATA_NON_NULL(output, false);
+
+    return fitsWriteHeader(fits, output, true);
+}
+
+bool psFitsWriteBlank(psFits* fits,
+                      const psMetadata* output,
+                      const char *extname
+                     )
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+
+    // We allow output == NULL in order to write a minimal header.
+
+    // Create a dummy image HDU for the primary HDU
+    int status = 0;                 // Status of cfitsio
+
+    psFitsMoveLast(fits);
+
+    int hdus = psFitsGetSize(fits);     // Number of HDUs in file
+    if (hdus == 0) {
+        // We're creating the first image
+        fits_create_img(fits->fd, 16, 0, NULL, &status);
+    } else {
+        // Insert after the current position
+        fits_insert_img(fits->fd, 16, 0, NULL, &status);
+    }
+
+    if (status) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true, "Unable to create blank header.\n%s\n", fitsErr);
+        return false;
+    }
+
+    if (output && !fitsWriteHeader(fits, output, false)) {
+        psError(PS_ERR_IO, false, "Unable to write FITS header.\n");
+        return false;
+    }
+
+    if (extname && strlen(extname)) {
+        psFitsSetExtName(fits, extname);
+    }
+
+    char buffer[10];
+    fits_write_img(fits->fd, TSHORT, 1, 0, buffer, &status);
+
+    return true;
+}
+
+bool psFitsHeaderValidate(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    // Traverse the metadata list and inspect at each key
+    psListIterator* iter = psListIteratorAlloc(header->list, PS_LIST_HEAD, true); // Iterator
+    psMetadataItem* item;               // Item from iteration
+    bool valid = true;                  // Are all items valid?
+    while ((item = psListGetAndIncrement(iter))) {
+        if (item->type > PS_DATA_STRING) { // i.e., a non-primitive type
+            valid = false;
+        }
+
+        if (strlen(item->name) > 8) {
+            item->name[8] = '\0'; // truncate to 8 characters
+        }
+
+        fits_uppercase(item->name); // make uppercase
+
+        // now, let's see if CFITSIO thinks this is a good keyword...
+        int status = 0;                 // Status from cfitsio calls
+        if (fits_test_keyword(item->name,&status) != 0) {
+            valid = false;
+        }
+    }
+    psFree(iter);
+
+    return valid;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsHeader.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsHeader.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsHeader.h	(revision 22322)
@@ -0,0 +1,87 @@
+/* @file  psFitsHeader.h
+ * @brief Contains Fits header I/O routines
+ *
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-10-03 21:27:21 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FITSHEADER_H
+#define PS_FITSHEADER_H
+
+/// @addtogroup FileIO Input/Output
+/// @{
+
+#include "psFits.h"
+#include "psMetadata.h"
+
+
+/// Determine whether the current HDU is an empty PHU with a single compressed image following.
+///
+/// In that case, what should be treated as an image PHU is technically an empty PHU with a binary table
+/// extension.  We test the current position, number of extensions, the FITS headers and presence of a
+/// following compressed image to determine if this is the case.  If so, the FITS file pointer is left
+/// pointing at the compressed image.
+bool psFitsCheckSingleCompressedImagePHU(const psFits *fits, ///< FITS file pointer
+                                         const psMetadata *header ///< Header, or NULL
+    );
+
+/// Parse a string read from the FITS header.
+
+/// Removes the quotes, and any trailing spaces.  NOTE: the returned string is NOT on the psLib memory system
+/// --- it's simply a hacked version of the input string.  Note also that the input string is MODIFIED.
+char *p_psFitsHeaderParseString(char *string ///< String to parse
+    );
+
+/** Reads the header of the current HDU.
+ *
+ *  @return psMetadata*   the header data
+ */
+psMetadata* psFitsReadHeader(
+    psMetadata* out,
+    ///< The psMetadata to add the header data.  If null, a new psMetadata is created.
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Reads the header of all HDUs.  The current HDU is not changed.
+ *
+ *  @return psMetadata*      the header data set as a number of metadata entries
+ */
+psMetadata* psFitsReadHeaderSet(
+    psMetadata* out,                         ///< output metadata or NULL if new psMetadata is to be created.
+    const psFits* fits                       ///< the psFits object
+);
+
+/** Writes the values of the metadata to the current HDU header.
+ *  Doesn't check if the header has to be created.
+ *
+ * @return bool         if TRUE, the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteHeader(
+    psFits* fits,                       ///< the psFits object
+    const psMetadata* output            ///< the psMetadata data in which to write
+);
+
+/** Writes a "blank" --- a header only, with no image or table.
+ *
+ *  @return bool        if TRUE, the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteBlank(
+    psFits* fits,                       ///< the psFits object
+    const psMetadata* output,           ///< the psMetadata data in which to write
+    const char *extname
+);
+
+/** psFitsHeaderValidate validates the supplied header so that it is in
+ *  compliance to the FITS standard for header keyword names and types.
+ *
+ *  @return bool        TRUE if the resulting header conforms to the FITS
+ *                      standard, otherwise FALSE
+ */
+bool psFitsHeaderValidate(psMetadata *header);
+
+/// @}
+#endif // #ifndef PS_FITS_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsImage.c	(revision 22322)
@@ -0,0 +1,999 @@
+/** @file  psFitsImage.c
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.40 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-05 08:08:33 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+
+#include "psAbort.h"
+#include "psType.h"
+#include "psAssert.h"
+#include "psError.h"
+#include "psString.h"
+#include "psLogMsg.h"
+#include "psTrace.h"
+#include "psRandom.h"
+#include "psImage.h"
+#include "psImageStructManip.h"
+
+#include "psFits.h"
+#include "psFitsFloat.h"
+#include "psFitsFloatFile.h"
+#include "psFitsHeader.h"
+#include "psFitsScale.h"
+
+#include "psMemory.h"
+
+#include "psFitsImage.h"
+
+#define MAX_STRING_LENGTH 256           // maximum length string for FITS routines
+
+// Information required to read a FITS file
+typedef struct {
+    int nAxis;                          // Number of axes
+    int bitPix;                         // Bits per pixel
+    long nAxes[3];                      // Length of each axis
+    long firstPixel[3];                 // lower-left corner of image subset
+    long lastPixel[3];                  // upper-right corner of image subset
+    long increment[3];                  // increment for image subset
+    int fitsDatatype;                   // cfitsio data type
+    int psDatatype;                     // psLib data type
+} p_psFitsReadInfo;
+
+// Read the vital statistics of this FITS image, in preparation for reading the image
+static p_psFitsReadInfo *p_psFitsReadInfoAlloc(
+    const psFits *fits, // The FITS file handle
+    psRegion region, // Region to read
+    int z // z-plane to read in cube
+    )
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(z, NULL);
+
+    p_psFitsReadInfo *info = psAlloc(sizeof(p_psFitsReadInfo));
+    memset(info, 0, sizeof(p_psFitsReadInfo));
+
+    int status = 0;                     // CFITSIO status
+
+    // check to see if we even are positioned on an image HDU
+    int hduType;                        // Type of HDU
+    if (fits_get_hdu_type(fits->fd, &hduType, &status) != 0) {
+        psFitsError(status, true, "Could not determine the HDU type.");
+        goto bad;
+    }
+    if (hduType != IMAGE_HDU) {
+        psError(PS_ERR_IO, true, _("Current FITS HDU type must be an image."));
+        goto bad;
+    }
+
+    // Get the data type 'bitPix' from the FITS image
+    if (fits_get_img_equivtype(fits->fd, &info->bitPix, &status) != 0) {
+        psFitsError(status, true, "Could not determine image data type.");
+        goto bad;
+    }
+
+    /* Get the dimensions 'nAxis' from the FITS image */
+    if (fits_get_img_dim(fits->fd, &info->nAxis, &status) != 0) {
+        psFitsError(status, true, "Could not determine image dimensions.");
+        goto bad;
+    }
+
+    /* Validate the number of axis */
+    if ((info->nAxis < 2) || (info->nAxis > 3)) {
+        psError(PS_ERR_IO, true,
+                _("Image number of dimensions, %d, is not supported."), info->nAxis);
+        goto bad;
+    }
+
+    /* Get the Image size from the FITS file */
+    if (fits_get_img_size(fits->fd, info->nAxis, info->nAxes, &status) != 0) {
+        psFitsError(status, true, "Could not determine image size.");
+        goto bad;
+    }
+
+    info->firstPixel[0] = region.x0 + 1;
+    info->firstPixel[1] = region.y0 + 1;
+    info->firstPixel[2] = z + 1;
+
+    if (region.x1 > 0) {
+        info->lastPixel[0] = region.x1;
+    } else {
+        info->lastPixel[0] = info->nAxes[0] + region.x1; // n.b., region.x1 < 0
+    }
+    if (region.y1 > 0) {
+        info->lastPixel[1] = region.y1;
+    } else {
+        info->lastPixel[1] = info->nAxes[1] + region.y1; // n.b., region.y1 < 0
+    }
+    info->lastPixel[2] = z + 1;
+
+    info->increment[0] = 1;
+    info->increment[1] = 1;
+    info->increment[2] = 1;
+
+    // Check scale and zero
+    double bscale = 0.0, bzero = 0.0;    // Scale and zero point
+    if (fits_read_key_dbl(fits->fd, "BSCALE", &bscale, NULL, &status) && status != KEY_NO_EXIST) {
+        psFitsError(status, true, "Unable to read header.");
+        goto bad;
+    }
+    status = 0;
+    if (fits_read_key_dbl(fits->fd, "BZERO", &bzero, NULL, &status) && status != KEY_NO_EXIST) {
+        psFitsError(status, true, "Unable to read header.");
+        goto bad;
+    }
+    status = 0;
+
+    if ((bscale != 0.0 && bscale != 1.0) || bzero != (int)bzero) {
+        // It's a floating-point image that's been quantised
+        // cfitsio will apply the scale and zero point for us if we choose the correct data type
+        switch (info->bitPix) {
+          case BYTE_IMG:
+          case SBYTE_IMG:
+          case USHORT_IMG:
+          case SHORT_IMG:
+          case ULONG_IMG:
+          case LONG_IMG:
+          case FLOAT_IMG:
+            info->psDatatype = PS_TYPE_F32;
+            info->fitsDatatype = TFLOAT;
+            break;
+          case LONGLONG_IMG:
+          case DOUBLE_IMG:
+            info->psDatatype = PS_TYPE_F64;
+            info->fitsDatatype = TDOUBLE;
+            break;
+          default:
+            psError(PS_ERR_IO, true, _("FITS image type, BITPIX=%d, is not supported."), info->bitPix);
+            goto bad;
+        }
+    } else {
+        switch (info->bitPix) {
+          case BYTE_IMG:
+            info->psDatatype = PS_TYPE_U8;
+            info->fitsDatatype = TBYTE;
+            break;
+          case SBYTE_IMG:
+            info->psDatatype = PS_TYPE_S8;
+            info->fitsDatatype = TSBYTE;
+            break;
+          case USHORT_IMG:
+            info->psDatatype = PS_TYPE_U16;
+            info->fitsDatatype = TUSHORT;
+            break;
+          case SHORT_IMG:
+            info->psDatatype = PS_TYPE_S16;
+            info->fitsDatatype = TSHORT;
+            break;
+          case ULONG_IMG:
+            info->psDatatype = PS_TYPE_U32;
+            info->fitsDatatype = TUINT;
+            break;
+          case LONG_IMG:
+            info->psDatatype = PS_TYPE_S32;
+            info->fitsDatatype = TINT;
+            break;
+          case LONGLONG_IMG:
+            info->psDatatype = PS_TYPE_S64;
+            info->fitsDatatype = TLONGLONG;
+            break;
+          case FLOAT_IMG:
+            info->psDatatype = PS_TYPE_F32;
+            info->fitsDatatype = TFLOAT;
+            break;
+          case DOUBLE_IMG:
+            info->psDatatype = PS_TYPE_F64;
+            info->fitsDatatype = TDOUBLE;
+            break;
+          default:
+            psError(PS_ERR_IO, true, _("FITS image type, BITPIX=%d, is not supported."), info->bitPix);
+            goto bad;
+        }
+    }
+    return info;
+
+    // Common error cleanup
+bad:
+    psFree(info);
+    return NULL;
+}
+
+
+bool psFitsImageSize(int *numCols, int *numRows, psElemType *type, const psFits *fits, psRegion region)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+        // This is really what we want, not the empty PHU
+        psTrace("psLib.fits", 1,
+                "This PHU should really be a compressed image --- reading that image instead.");
+    }
+
+    p_psFitsReadInfo *info = p_psFitsReadInfoAlloc(fits, region, 0); // How big the region to read is
+
+    if (numCols) {
+        *numCols = info->lastPixel[0] - info->firstPixel[0] + 1;
+    }
+    if (numRows) {
+        *numRows = info->lastPixel[1] - info->firstPixel[1] + 1;
+    }
+    if (type) {
+        *type = info->psDatatype;
+    }
+
+    psFree(info);
+
+    return true;
+}
+
+
+// Convert an image to the desired BITPIX, i.e., the desired disk representation
+static psImage *imageToDiskRepresentation(double *bscale, // Scaling applied
+                                          double *bzero, // Zero point applied
+                                          long *blank, // Blank value (integer data)
+                                          psFitsFloat *floatType, // Type of custom floating-point
+                                          psFits *fits, // FITS file pointer
+                                          const psImage *image, // Image to convert
+                                          const psImage *mask, // Mask image, or NULL
+                                          psMaskType maskVal, // Value to mask
+                                          psRandom *rng, // Random number generator
+                                          bool newScaleZero // Determine a new BSCALE and BZERO?
+                                          )
+{
+    psAssert(bscale, "impossible");
+    psAssert(bzero, "impossible");
+    psAssert(floatType, "impossible");
+    psAssert(fits, "impossible");
+    psAssert(image, "impossible");
+
+    *bscale = 1.0;
+    *bzero = 0.0;
+    *floatType = PS_FITS_FLOAT_NONE;
+
+    psFitsOptions *options = fits->options;
+    if (!options) {
+        return psMemIncrRefCounter((psImage*)image); // Casting away const
+    }
+
+    // Custom floating-point
+    if (PS_IS_PSELEMTYPE_REAL(image->type.type) && options->conventions.psBitpix &&
+        options->floatType != PS_FITS_FLOAT_NONE) {
+        *floatType = options->floatType;
+        return psFitsFloatImageToDisk(image, options->floatType);
+    }
+
+    // Automatically select what we're given
+    if (options->bitpix == 0) {
+        return psMemIncrRefCounter((psImage*)image); // Casting away const
+    }
+
+    // Quantise floating-point images
+    if (PS_IS_PSELEMTYPE_REAL(image->type.type) && options->bitpix > 0) {
+        if (newScaleZero) {
+            // Choose an appropriate BSCALE and BZERO
+            if (!psFitsScaleDetermine(bscale, bzero, blank, image, mask, maskVal, fits)) {
+                // We can't have the write dying for this reason --- try to save it somehow!
+                psWarning("Unable to determine BSCALE and BZERO for image --- refusing to quantise.");
+                psErrorClear();
+                return psMemIncrRefCounter((psImage*)image);
+            }
+        } else {
+            // Don't want to muck with the current BSCALE and BZERO.  Get the current values and use those.
+            int status = 0;                 // Status of cfitsio
+            if (fits_read_key_dbl(fits->fd, "BSCALE", bscale, NULL, &status) && status != KEY_NO_EXIST) {
+                psFitsError(status, true, "Unable to read header.");
+                return NULL;
+            }
+            status = 0;
+            if (fits_read_key_dbl(fits->fd, "BZERO", bzero, NULL, &status) && status != KEY_NO_EXIST) {
+                psFitsError(status, true, "Unable to read header.");
+                return NULL;
+            }
+            status = 0;
+            if (*bscale == 0.0) {
+                psError(PS_ERR_IO, true,
+                        "Supposed to use old values of BSCALE and BZERO, but they don't exist.");
+                return NULL;
+            }
+        }
+
+        return psFitsScaleForDisk(image, fits, *bscale, *bzero, rng);
+    }
+
+    // Choose the appropriate output type, given the input type and desired bits per pixel
+#define CONVERT_TYPE_INT_CASE(OUTTYPE, INTYPE, BITPIX) \
+  case BITPIX: \
+    OUTTYPE = PS_IS_PSELEMTYPE_UNSIGNED(INTYPE) ? PS_TYPE_U##BITPIX : PS_TYPE_S##BITPIX; \
+    break;
+#define CONVERT_TYPE_FLOAT_CASE(OUTTYPE, BITPIX) \
+  case -BITPIX: /* Note the use of the negative sign */ \
+    OUTTYPE = PS_TYPE_F##BITPIX; \
+    break;
+
+    *bscale = 1.0;
+    *bzero = 0.0;
+    psElemType inType = image->type.type; // Type for input image
+    psElemType outType;                 // Type for output image
+    switch (options->bitpix) {
+        CONVERT_TYPE_INT_CASE(outType, inType, 8);
+        CONVERT_TYPE_INT_CASE(outType, inType, 16);
+        CONVERT_TYPE_INT_CASE(outType, inType, 32);
+        CONVERT_TYPE_INT_CASE(outType, inType, 64);
+        CONVERT_TYPE_FLOAT_CASE(outType, 32);
+        CONVERT_TYPE_FLOAT_CASE(outType, 64);
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Target bitpix (%d) is not one of 8,16,32,64",
+                options->bitpix);
+        return NULL;
+    }
+
+    if (outType == inType) {
+        return psMemIncrRefCounter((psImage*)image);
+    }
+
+    if (PSELEMTYPE_SIZEOF(inType) > PSELEMTYPE_SIZEOF(outType)) {
+        psWarning("Truncating image pixels to write to disk.");
+    }
+
+    return psImageCopy(NULL, image, outType);
+}
+
+
+// Read into an extant image of just the right size
+static bool fitsReadImage(psImage *output,   // Output image
+                          const psFits *fits, // FITS file handle
+                          p_psFitsReadInfo *info // Info on how to read
+                         )
+{
+    // n.b., this assumes contiguous image buffer
+    psAssert(output, "impossible");
+    psAssert(fits, "impossible");
+    psAssert(info, "impossible");
+    psAssert(output->numCols == info->lastPixel[0] - info->firstPixel[0] + 1, "impossible");
+    psAssert(output->numRows == info->lastPixel[1] - info->firstPixel[1] + 1, "impossible"); // Right size
+    psAssert(!output->parent, "impossible");            // No parents means the buffer is contiguous
+
+    void *nullValue = NULL;             // Null value for data
+    float nullFloat = NAN;              // Null value for floating point
+    double nullDouble = NAN;            // Null value for double
+    switch (info->psDatatype) {
+      case PS_TYPE_F32:
+        nullValue = &nullFloat;
+        break;
+      case PS_TYPE_F64:
+        nullValue = &nullDouble;
+        break;
+      case PS_TYPE_U8:
+      case PS_TYPE_U16:
+      case PS_TYPE_U32:
+      case PS_TYPE_U64:
+      case PS_TYPE_S8:
+      case PS_TYPE_S16:
+      case PS_TYPE_S32:
+      case PS_TYPE_S64:
+        // Can't mark bad pixels any further than what is in the FITS image
+        break;
+      default:
+        psAbort("Unknown type: %x", info->psDatatype);
+    }
+
+    int anynull = 0;                    // Are there any NULLs in the data?
+    int status = 0;                     // cfitsio status
+    if (fits_read_subset(fits->fd, info->fitsDatatype, info->firstPixel, info->lastPixel,
+                         info->increment, nullValue, output->data.V[0], &anynull, &status) != 0) {
+        psFitsError(status, true, "Reading FITS file failed.");
+        return false;
+    }
+
+    // No need to apply the BSCALE, BZERO because cfitsio does this for us
+
+    return true;
+}
+
+psImage *psFitsReadImage(const psFits *fits, // the psFits object
+                         psRegion region, // the region in the FITS image to read
+                         int z          // the z-plane in the FITS image cube to read
+                        )
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(z, NULL);
+
+    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+        // This is really what we want, not the empty PHU
+        psTrace("psLib.fits", 1,
+                "This PHU should really be a compressed image --- reading that image instead.");
+    }
+
+    p_psFitsReadInfo *info = p_psFitsReadInfoAlloc(fits, region, z);
+
+    // Size of image
+    int numCols = info->lastPixel[0] - info->firstPixel[0] + 1;
+    int numRows = info->lastPixel[1] - info->firstPixel[1] + 1;
+
+    psImage *inImage = psImageAlloc(numCols, numRows, info->psDatatype); // Image to read in
+
+    psFitsFloat floatType = psFitsFloatImageCheck(fits); // Type of custom floating-point
+    psImage *outImage = (floatType == PS_FITS_FLOAT_NONE ? psMemIncrRefCounter(inImage) :
+                         psImageAlloc(numCols, numRows, psFitsFloatImageType(floatType))); // Output image
+
+    if (!fitsReadImage(inImage, fits, info)) {
+        psFree(info);
+        psFree(inImage);
+        return NULL;
+    }
+    psFree(info);
+
+    if (floatType != PS_FITS_FLOAT_NONE) {
+        outImage = psFitsFloatImageFromDisk(outImage, inImage, floatType);
+    }
+    psFree(inImage);
+
+    return outImage;
+}
+
+psImage *psFitsReadImageBuffer(psImage *outImage, // Output image buffer
+                               const psFits *fits, // the psFits object
+                               psRegion region, // the region in the FITS image to read
+                               int z           // the z-plane in the FITS image cube to read
+                              )
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(z, NULL);
+
+    if (outImage && outImage->parent) {
+        psError(PS_ERR_IO, true, "Unable to read into a buffer for a child image.\n");
+        return NULL;
+    }
+
+    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+        // This is really what we want, not the empty PHU
+        psTrace("psLib.fits", 1,
+                "This PHU should really be a compressed image --- reading that image instead.");
+    }
+
+    p_psFitsReadInfo *info = p_psFitsReadInfoAlloc(fits, region, z);
+
+    // Size of image
+    int numCols = info->lastPixel[0] - info->firstPixel[0] + 1;
+    int numRows = info->lastPixel[1] - info->firstPixel[1] + 1;
+
+    psFitsFloat floatType = psFitsFloatImageCheck(fits); // Type of custom floating-point
+    psImage *inImage;                   // Image to read in
+    if (floatType == PS_FITS_FLOAT_NONE) {
+        inImage = psImageRecycle(outImage, numCols, numRows, info->psDatatype);
+        outImage = psMemIncrRefCounter(inImage);
+    } else {
+        inImage = psImageAlloc(numCols, numRows, info->psDatatype);
+        outImage = psImageRecycle(outImage, numCols, numRows, psFitsFloatImageType(floatType));
+    }
+
+    if (!fitsReadImage(inImage, fits, info)) {
+        psFree(info);
+        psFree(inImage);
+        psFree(outImage);
+        return NULL;
+    }
+    psFree(info);
+
+    if (floatType != PS_FITS_FLOAT_NONE) {
+        outImage = psFitsFloatImageFromDisk(outImage, inImage, floatType);
+    }
+    psFree(inImage);
+
+    return outImage;
+}
+
+bool psFitsWriteImage(psFits *fits, psMetadata *header, const psImage *input,
+                      int numZPlanes, const char *extname)
+{
+    return psFitsWriteImageWithMask(fits, header, input, NULL, 0, numZPlanes, extname);
+}
+
+bool psFitsWriteImageWithMask(psFits *fits, psMetadata *header, const psImage *input,
+                              const psImage *mask, psMaskType maskVal, int numZPlanes,
+                              const char *extname)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_IMAGE_NON_NULL(input, false);
+    // this is equivalent to insert after the last HDU
+
+    psFitsMoveLast(fits);
+    return psFitsInsertImageWithMask(fits, header, input, mask, maskVal, numZPlanes, extname, true);
+}
+
+bool psFitsInsertImage(psFits *fits, psMetadata *header, const psImage *image, int numZPlanes,
+                       const char *extname, bool after)
+{
+    return psFitsInsertImageWithMask(fits, header, image, NULL, 0, numZPlanes, extname, after);
+}
+
+bool psFitsInsertImageWithMask(psFits *fits, psMetadata *header, const psImage *image,
+                               const psImage *mask, psMaskType maskVal, int numZPlanes,
+                               const char *extname, bool after)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    if (mask) {
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(mask, image, false);
+    }
+
+    int numCols = image->numCols;       // Number of columns for image
+    int numRows = image->numRows;       // Number of rows for image
+    int status = 0;                     // Status from cfitsio
+    bool success = true;
+    psFitsCompression *compress = NULL; // FITS compression parameters; to save state
+
+    double bscale = NAN, bzero = NAN;   // Scale and zero point to put in header
+    long blank = 0;                     // Blank (undefined) value for image
+    psFitsFloat floatType;              // Custom floating-point convention type
+    psImage *diskImage = imageToDiskRepresentation(&bscale, &bzero, &blank, &floatType, fits, image,
+                                                   mask, maskVal, NULL, true); // Image to write out
+    if (!diskImage) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to convert image to desired disk format.");
+        success = false;
+        goto INSERT_DONE;
+    }
+
+    bool useRequestedScale = true;
+    if (!isfinite(bscale) || !isfinite(bzero)) {
+        // Couldn't scale, so don't compress.  Save compression parameters for later
+        useRequestedScale = false;
+        compress = psFitsCompressionGet(fits);
+        if (!psFitsSetCompression(fits, PS_FITS_COMPRESS_NONE, NULL, 0, 0, 0)) {
+            psError(PS_ERR_IO, false, "Unable to unset compression.");
+            success = false;
+            goto INSERT_DONE;
+        }
+    }
+
+    // determine the FITS-equivalent parameters
+    int bitPix;                         // Bits per pixel
+    double cfitsioBzero = 0.0;          // Zero point for cfitsio to apply
+    int dataType;                       // cfitsio data type
+    if (!p_psFitsTypeToCfitsio(diskImage->type.type, &bitPix, &cfitsioBzero, &dataType)) {
+        success = false;
+        goto INSERT_DONE;
+    }
+    if (cfitsioBzero != 0.0) {
+        psAssert(bzero == 0.0 && bscale == 1.0,
+                 "p_psFitsTypeToCfitsio and imageToDiskRepresentation must not clash!");
+        bscale = 1.0;
+        bzero = cfitsioBzero;
+    }
+
+    psFitsOptions *options = fits->options; // FITS I/O options
+    psAssert(!useRequestedScale || !options || bitPix == options->bitpix || options->bitpix == 0,
+             "Something's not consistent");
+
+    int naxis = 3;                      // Number of axes
+    long naxes[3];                      // Length of each axis
+    naxes[0] = numCols;
+    naxes[1] = numRows;
+    naxes[2] = numZPlanes;
+
+    if (numZPlanes < 2) {
+        naxis = 2;
+    }
+
+    // Create the image HDU
+    int hdus = psFitsGetSize(fits);     // Number of HDUs in file
+    if (hdus == 0) {
+        // We're creating the first image
+        fits_create_img(fits->fd, bitPix, naxis, naxes, &status);
+    } else {
+        if (!after) {
+            if (psFitsGetExtNum(fits) == 0) {
+                // We're creating a replacement primary HDU.
+                // Set status to signal fits_insert_img to insert a new primary HDU
+                status = PREPEND_PRIMARY;
+            } else {
+                // Move back one to perform an insert after the previous HDU
+                psFitsMoveExtNum(fits, -1, true);
+            }
+        }
+        // Insert after the current position
+        fits_insert_img(fits->fd, bitPix, naxis, naxes, &status);
+    }
+    if (psFitsError(status, true, "Could not create image HDU.")) {
+        success = false;
+        goto INSERT_DONE;
+    }
+
+    // write the header, if any.
+    if (header && !psFitsWriteHeader(fits, header)) {
+        psError(PS_ERR_IO, false, "Unable to write FITS header.\n");
+        success = false;
+        goto INSERT_DONE;
+    }
+
+    // We only want cfitsio to do the scale and zero if the type conversion requires it (e.g., input type is
+    // an unsigned integer type).  In all other cases, we have already converted the image to use the
+    // appropriate scale and zero (because we want to apply a randomiser to the quantisation).
+    fits_set_bscale(fits->fd, 1.0, cfitsioBzero, &status);
+
+    if (isfinite(bzero) && isfinite(bscale) && (bscale != 0.0)) {
+        fits_write_key_dbl(fits->fd, "BZERO", bzero, 12,
+                           "Scaling: TRUE = BZERO + BSCALE * DISK", &status);
+        fits_write_key_dbl(fits->fd, "BSCALE", bscale, 12,
+                           "Scaling: TRUE = BZERO + BSCALE * DISK", &status);
+        if (psFitsError(status, true, "Could not write BSCALE/BZERO headers to file.")) {
+            success = false;
+            goto INSERT_DONE;
+        }
+    }
+
+    if (blank != 0 && bitPix > 0) {
+        // Some quantisation has taken place --- record the blank ("magic") pixel value.
+        //
+        // According to http://heasarc.gsfc.nasa.gov/docs/heasarc/fits/compress/compress_image.html,
+        // the keyword is "BLANK" when BITPIX > 0, and "ZBLANK" when BITPIX < 0.  Since we do our
+        // own quantisation, the correct keyword is always "BLANK" instead of "ZBLANK".
+        fits_write_key_lng(fits->fd, "BLANK", blank, "Value for undefined pixels", &status);
+        // But it seems that cfitsio uses ZBLANK
+        fits_write_key_lng(fits->fd, "ZBLANK", blank, "Value for undefined pixels", &status);
+        fits_set_imgnull(fits->fd, blank, &status);
+        if (psFitsError(status, true, "Could not write BLANK header to file.")) {
+            success = false;
+            goto INSERT_DONE;
+        }
+    }
+
+    if (floatType != PS_FITS_FLOAT_NONE) {
+        psFitsFloatImageSet(fits, floatType);
+    }
+
+    if (extname && strlen(extname) > 0) {
+        psFitsSetExtName(fits, extname);
+    }
+
+    if (image->parent == NULL) {
+        // if no parent, assume that the image data is contiguous
+        fits_write_img(fits->fd, dataType, 1, numCols*numRows, diskImage->data.V[0], &status);
+    } else {
+        // image data may not be contiguous; write one row at a time
+        int firstPixel = 1;
+        for (int row = 0; row < numRows; row++) {
+            fits_write_img(fits->fd, dataType, firstPixel, numCols, diskImage->data.V[row], &status);
+            firstPixel += numCols;
+        }
+    }
+    if (psFitsError(status, true, "Could not write image to file.")) {
+        success = false;
+        goto INSERT_DONE;
+    }
+
+    // This forces a re-scan of the header to ensure everything's kosher.
+    // Without this, compressed HDUs have been written out with PCOUNT=0 and TFORM1 not correctly set.
+    ffrdef(fits->fd, &status);
+    if (psFitsError(status, true, "Could not re-scan HDU.")) {
+        success = false;
+        goto INSERT_DONE;
+    }
+
+
+ INSERT_DONE:
+    psFree(diskImage);
+    if (compress) {
+        // Restore compression state
+        psFitsCompressionApply(fits, compress);
+        psFree(compress);
+    }
+
+    return success;
+}
+
+bool psFitsUpdateImage(psFits *fits, const psImage *input, int x0, int y0, int z)
+{
+    return psFitsUpdateImageWithMask(fits, input, NULL, 0, x0, y0, z);
+}
+
+bool psFitsUpdateImageWithMask(psFits *fits, const psImage *input, const psImage *mask, psMaskType maskVal,
+                               int x0, int y0, int z)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_IMAGE_NON_NULL(input, false);
+    if (mask) {
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(mask, input, false);
+    }
+
+    int status = 0;
+
+    // check to see if we are positioned on an image HDU
+    int hduType;
+    if (fits_get_hdu_type(fits->fd, &hduType, &status) != 0) {
+        psFitsError(status, true, "Could not determine the HDU type.");
+        return NULL;
+    }
+    if (hduType != IMAGE_HDU) {
+        psError(PS_ERR_IO, true, _("Current FITS HDU type must be an image."));
+        return NULL;
+    }
+
+    int numCols = input->numCols;
+    int numRows = input->numRows;
+    psFitsCompression *compress = NULL; // FITS compression parameters; to save state
+
+    bool success = true;                // Successful update?
+    double bscale = NAN, bzero = NAN;   // Scale and zero point to put in header
+    long blank = 0;                     // Blank (undefined) value for image
+    psFitsFloat floatType;              // Custom floating-point convention type
+    psImage *diskImage = imageToDiskRepresentation(&bscale, &bzero, &blank, &floatType, fits, input,
+                                                   mask, maskVal, NULL, false); // Image to write out
+    if (!diskImage) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to convert image to desired disk format.");
+        success = false;
+        goto UPDATE_DONE;
+    }
+
+    bool useRequestedScale = true;
+    if (!isfinite(bscale) || !isfinite(bzero)) {
+        // Couldn't scale, so don't compress.  Save compression parameters for later
+        useRequestedScale = false;
+        compress = psFitsCompressionGet(fits);
+        if (!psFitsSetCompression(fits, PS_FITS_COMPRESS_NONE, NULL, 0, 0, 0)) {
+            psError(PS_ERR_IO, false, "Unable to unset compression.");
+            success = false;
+            goto UPDATE_DONE;
+        }
+    }
+
+    // determine the FITS-equivalent parameters
+    int bitPix;                         // Bits per pixel
+    double cfitsioBzero = 0.0;          // Zero point for cfitsio to apply
+    int dataType;                       // cfitsio data type
+    if (!p_psFitsTypeToCfitsio(diskImage->type.type, &bitPix, &cfitsioBzero, &dataType)) {
+        success = false;
+        goto UPDATE_DONE;
+    }
+    if (cfitsioBzero != 0.0) {
+        psAssert(bzero == 0.0 && bscale == 1.0,
+                 "p_psFitsTypeToCfitsio and imageToDiskRepresentation must not clash!");
+        bscale = 1.0;
+        bzero = cfitsioBzero;
+    }
+    psFitsOptions *options = fits->options; // FITS I/O options
+    psAssert(!useRequestedScale || !options || bitPix == options->bitpix || options->bitpix == 0,
+             "Something's not consistent");
+
+    // Check to see if the HDU has the same datatype
+    int fileBitpix;
+    int naxis;
+    long nAxes[3];
+    nAxes[2] = 1;
+    fits_get_img_param(fits->fd, 3, &fileBitpix, &naxis, nAxes, &status);
+
+    //check if the HDU has the z-plane requested
+    if (z >= nAxes[2]) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                _("Current FITS HDU has %ld z-planes, but z-plane %d was specified."), nAxes[2], z);
+        success = false;
+        goto UPDATE_DONE;
+    }
+
+    // determine the region in the FITS file domain
+    long firstPixel[3];
+    long lastPixel[3];
+
+    firstPixel[0] = x0 + 1;
+    firstPixel[1] = y0 + 1;
+    firstPixel[2] = z + 1;
+
+    lastPixel[0] = x0 + numCols;
+    lastPixel[1] = y0 + numRows;
+    lastPixel[2] = z + 1;
+
+    if (firstPixel[0] < 1 || firstPixel[0] > nAxes[0] ||
+        firstPixel[1] < 1 || firstPixel[1] > nAxes[1] ||
+        lastPixel[0] < 1 || lastPixel[0] > nAxes[0] ||
+        lastPixel[1] < 1 || lastPixel[1] > nAxes[1]) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Input image [size of %ix%i] at position (%i,%i) does not all lay in the %lix%li FITS image.",
+                numCols, numRows, x0, y0, nAxes[0], nAxes[1]);
+        success = false;
+        goto UPDATE_DONE;
+    }
+
+    // We only want cfitsio to do the scale and zero if the type conversion requires it (e.g., input type is
+    // an unsigned integer type).  In all other cases, we have already converted the image to use the
+    // appropriate scale and zero (because we want to apply a randomiser to the quantisation).
+    fits_set_bscale(fits->fd, 1.0, cfitsioBzero, &status);
+    fits_write_subset(fits->fd, dataType, firstPixel, lastPixel, diskImage->data.V[0], &status);
+    if (psFitsError(status, true, "Could not write data to file.")) {
+        success = false;
+        goto UPDATE_DONE;
+    }
+
+    // This forces a re-scan of the header to ensure everything's kosher.  We found this occassionally
+    // necessary for compressed images, which are tables, so perhaps it helps here too.  I guess it can't
+    // hurt.
+    ffrdef(fits->fd, &status);
+    if (psFitsError(status, true, "Could not re-scan HDU.")) {
+        success = false;
+        goto UPDATE_DONE;
+    }
+
+UPDATE_DONE:
+    psFree(diskImage);
+    if (compress) {
+        // Restore compression state
+        psFitsCompressionApply(fits, compress);
+        psFree(compress);
+    }
+
+    return success;
+}
+
+psArray *psFitsReadImageCube(const psFits *fits, psRegion region)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    int nAxis = 0;                      // Number of axes
+    long nAxes[3];                      // Number of pixels on each axis
+    int status = 0;                     // cfitsio status value
+
+    // Some of this replicates what is in psFitsReadImage, so it's a little inefficient.  But it saves
+    // code replication, and should be sufficient for our needs.
+
+    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+        // This is really what we want, not the empty PHU
+        psTrace("psLib.fits", 1,
+                "This PHU should really be a compressed image --- reading that image instead.");
+    }
+
+    if (fits_get_img_dim(fits->fd, &nAxis, &status) != 0) {
+        psFitsError(status, true, "Could not determine image dimensions.");
+        return NULL;
+    }
+
+    if (nAxis == 2) {
+        psArray *images = psArrayAlloc(1); // Single image plane
+        images->data[0] = psFitsReadImage(fits, region, 0);
+        return images;
+    }
+    if (nAxis == 3) {
+        if (fits_get_img_size(fits->fd, nAxis, nAxes, &status) != 0) {
+            psFitsError(status, true, "Could not determine image size.");
+            return NULL;
+        }
+
+        psArray *images = psArrayAlloc(nAxes[2]); // Array of image planes
+        for (int i = 0; i < nAxes[2]; i++) {
+            images->data[i] = psFitsReadImage(fits, region, i);
+        }
+
+        return images;
+    }
+
+    // Bad dimensionality
+    psError(PS_ERR_IO, true, _("Image number of dimensions, %d, is not supported."), nAxis);
+    return NULL;
+}
+
+bool psFitsWriteImageCube(psFits *fits, psMetadata *header, const psArray *input, const char *extname)
+{
+    return psFitsWriteImageCubeWithMask(fits, header, input, NULL, 0, extname);
+}
+
+bool psFitsWriteImageCubeWithMask(psFits *fits, psMetadata *header, const psArray *input,
+                                  const psArray *masks, psMaskType maskVal, const char *extname)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_ARRAY_NON_NULL(input, false);
+    if (masks) {
+        PS_ASSERT_ARRAY_NON_NULL(masks, false);
+        PS_ASSERT_ARRAYS_SIZE_EQUAL(masks, input, false);
+    }
+
+    if (input->n == 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("The input array was empty."));
+        return false;
+    }
+
+    if (input->n == 1) {
+        // The problem reduces to one already solved
+        return psFitsWriteImageWithMask(fits, header, input->data[0],
+                                        masks ? masks->data[0] : NULL, maskVal, 1, extname);
+    }
+
+    // Check that all images are of the same size
+    psImage *testImage = input->data[0];// First image off the array
+    int numCols = testImage->numCols;   // Number of columns
+    int numRows = testImage->numRows;   // Number of rows
+    for (int i = 1; i < input->n; i++) {
+        testImage = input->data[i];
+        if (testImage->numCols != numCols || testImage->numRows != numRows) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("The sizes of images in the array differ."));
+            return false;
+        }
+    }
+
+    // Need to check the header to make sure NAXIS and NAXIS[1-3] are correct
+    psMetadata *headerCopy = NULL;      // Copy of header
+    if (header) {
+        headerCopy = psMemIncrRefCounter(header);
+    } else {
+        headerCopy = psMetadataAlloc();
+    }
+    bool update = psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS", PS_META_REPLACE, "Dimensionality", 3) &&
+        psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS1", PS_META_REPLACE, "Number of columns", numCols) &&
+        psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS2", PS_META_REPLACE, "Number of rows", numRows) &&
+        psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS3", PS_META_REPLACE, "Number of image planes",
+                         input->n);
+    if (!update) {
+        psError(PS_ERR_UNKNOWN, false, _("Failed to add metadata item, %s."),
+                "NAXIS, NAXIS1, NAXIS2, NAXIS3");
+        psFree(headerCopy);
+        return false;
+    }
+
+    // Now we can safely write the images out.
+    // The first is an psFitsImageWrite to create the extension.
+    // The next are psFitsImageUpdate to write into the extension.
+    if (!psFitsWriteImageWithMask(fits, headerCopy, input->data[0],
+                                  masks ? masks->data[0] : NULL, maskVal, input->n, extname)) {
+        psError(PS_ERR_UNKNOWN, false, _("Could not write image plane %d."), 0);
+        psFree(headerCopy);
+        return false;
+    }
+    psFree(headerCopy);                 // Free, or drop reference
+
+    for (int i = 1; i < input->n; i++) {
+        if (!psFitsUpdateImageWithMask(fits, input->data[i],
+                                       masks ? masks->data[i] : NULL, maskVal, 0, 0, i)) {
+            psError(PS_ERR_UNKNOWN, false, _("Could not write image plane %d."), i);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool psFitsUpdateImageCube(psFits *fits, const psArray *input, int x0, int y0)
+{
+    return psFitsUpdateImageCubeWithMask(fits, input, NULL, 0, x0, y0);
+}
+
+bool psFitsUpdateImageCubeWithMask(psFits *fits, const psArray *input,
+                                   const psArray *masks, psMaskType maskVal, int x0, int y0)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_ARRAY_NON_NULL(input, false);
+
+    if (input->n == 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("The input array was empty."));
+        return false;
+    }
+
+    for (int i = 0; i < input->n; i++) {
+        if (!psFitsUpdateImageWithMask(fits, input->data[i],
+                                       masks ? masks->data[i] : NULL, maskVal, x0, y0, i)) {
+            psError(PS_ERR_UNKNOWN, false, _("Could not update image plane %d."), i);
+            return false;
+        }
+    }
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsImage.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsImage.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsImage.h	(revision 22322)
@@ -0,0 +1,183 @@
+/* @file  psFitsImage.h
+ * @brief Contains Fits I/O routines
+ *
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-09-05 08:08:33 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FITSIMAGE_H
+#define PS_FITSIMAGE_H
+
+/// @addtogroup FileIO Input/Output
+/// @{
+
+#include "psFits.h"
+#include "psType.h"
+#include "psArray.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psImage.h"
+
+/// Return the dimensions and type of the FITS image
+bool psFitsImageSize(
+    int *numCols, int *numRows,         ///< Size of image
+    psElemType *type,                   ///< Type of image
+    const psFits *fits,                 ///< FITS file pointer
+    psRegion region                     ///< Region in the FITS image to read
+    );
+
+/** Reads an image, given the desired region and z-plane.
+ *
+ *  @return psImage*     the read image or NULL if there was an error.
+ */
+psImage *psFitsReadImage(
+    const psFits *fits,                 ///< the psFits object
+    psRegion region,                    ///< the region in the FITS image to read
+    int z                               ///< the z-plane in the FITS image cube to read
+);
+
+/** Read an image into an extant buffer
+ */
+psImage *psFitsReadImageBuffer(
+    psImage *output,                    ///< Output image buffer
+    const psFits *fits,                 ///< the psFits object
+    psRegion region,                    ///< the region in the FITS image to read
+    int z                               ///< the z-plane in the FITS image cube to read
+    );
+
+/** Writes an image to a FITS file
+ *
+ * A new IMAGE HDU is appended to the end of the FITS file.
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteImage(
+    psFits *fits,                       ///< the psFits object
+    psMetadata *header,                 ///< header items for the new HDU.  Can be NULL.
+    const psImage *input,               ///< the image to output
+    int depth,                          ///< the number of z-planes of the FITS image data cube
+    const char *extname                 ///< FITS extension name
+);
+
+/** Writes an image to a FITS file, optionally using the supplied mask image to do statistics when compressing
+ *
+ * A new IMAGE HDU is appended to the end of the FITS file.
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteImageWithMask(
+    psFits *fits,                       ///< the psFits object
+    psMetadata *header,                 ///< header items for the new HDU.  Can be NULL.
+    const psImage *input,               ///< the image to output
+    const psImage *mask,                ///< the mask image
+    psMaskType maskVal,                 ///< value to mask
+    int depth,                          ///< the number of z-planes of the FITS image data cube
+    const char *extname                 ///< FITS extension name
+);
+
+/** Insert an image in a FITS file
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsInsertImage(
+    psFits *fits,                       ///< the psFits object
+    psMetadata *header,                 ///< header items for the new HDU.  Can be NULL.
+    const psImage *input,               ///< the image to output
+    int depth,                          ///< the number of z-planes of the FITS image data cube
+    const char *extname,                ///< FITS extension name
+    bool after                          ///< if TRUE, inserts HDU after current HDU, otherwise before
+);
+
+/** Insert an image in a FITS file, optionally using the supplied mask image to do statistics when compressing
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsInsertImageWithMask(
+    psFits *fits,                       ///< the psFits object
+    psMetadata *header,                 ///< header items for the new HDU.  Can be NULL.
+    const psImage *input,               ///< the image to output
+    const psImage *mask,                ///< the mask image
+    psMaskType maskVal,                 ///< value to mask
+    int depth,                          ///< the number of z-planes of the FITS image data cube
+    const char *extname,                ///< FITS extension name
+    bool after                          ///< if TRUE, inserts HDU after current HDU, otherwise before
+);
+
+/** Updates an existing FITS file image
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsUpdateImage(
+    psFits *fits,                       ///< the psFits object
+    const psImage *input,               ///< the image to output
+    int x0,                             ///< psImage's x-axis origin in FITS image coordinates
+    int y0,                             ///< psImage's y-axis origin in FITS image coordinates
+    int z                               ///< the z-planes of the FITS image data cube to write
+);
+
+/** Updates an existing FITS file image, optionally using the supplied mask image to do statistics when
+ ** compressing
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsUpdateImageWithMask(
+    psFits *fits,                       ///< the psFits object
+    const psImage *input,               ///< the image to output
+    const psImage *mask,                ///< the mask image
+    psMaskType maskVal,                 ///< value to mask
+    int x0,                             ///< psImage's x-axis origin in FITS image coordinates
+    int y0,                             ///< psImage's y-axis origin in FITS image coordinates
+    int z                               ///< the z-planes of the FITS image data cube to write
+);
+
+/// Read an image cube (3D image with each plane a separate image)
+///
+/// Images are returned in an array of psImage
+psArray *psFitsReadImageCube(
+    const psFits *fits,                 ///< FITS file to read
+    psRegion region                     ///< Region to read
+    );
+
+/// Write an image cube (3D image from an array of images)
+bool psFitsWriteImageCube(
+    psFits *fits,                       ///< FITS file to write
+    psMetadata *header,                 ///< Header to write
+    const psArray *input,               ///< Array of images
+    const char *extname                 ///< Name of extension
+    );
+
+/// Write an image cube (3D image from an array of images), optionally using the supplied mask images to do
+/// statistics when compressing
+bool psFitsWriteImageCubeWithMask(
+    psFits *fits,                       ///< FITS file to write
+    psMetadata *header,                 ///< Header to write
+    const psArray *input,               ///< Array of images
+    const psArray *masks,               ///< Array of masks
+    psMaskType maskVal,                 ///< Value to mask
+    const char *extname                 ///< Name of extension
+    );
+
+/// Update an image cube (3D image from an array of images)
+bool psFitsUpdateImageCube(
+    psFits *fits,                       ///< FITS file to update
+    const psArray *input,               ///< Array of images
+    int x0,                             ///< x origin of images in FITS image coordinates
+    int y0                              ///< y origin of images in FITS image coordinates
+    );
+
+/// Update an image cube (3D image from an array of images), optionally using the supplied mask images to do
+/// statistics when compressing
+bool psFitsUpdateImageCubeWithMask(
+    psFits *fits,                       ///< FITS file to update
+    const psArray *input,               ///< Array of images
+    const psArray *masks,               ///< Array of masks
+    psMaskType maskVal,                 ///< Value to mask
+    int x0,                             ///< x origin of images in FITS image coordinates
+    int y0                              ///< y origin of images in FITS image coordinates
+    );
+
+/// @}
+#endif // #ifndef PS_FITS_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsScale.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsScale.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsScale.c	(revision 22322)
@@ -0,0 +1,451 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <strings.h>
+
+#include "psAbort.h"
+#include "psAssert.h"
+#include "psError.h"
+#include "psTrace.h"
+
+#include "psImage.h"
+#include "psFits.h"
+#include "psImageBackground.h"
+#include "psStats.h"
+#include "psImageStructManip.h"
+
+#include "psMemory.h"
+
+#include "psFitsScale.h"
+
+// Remember:
+// TRUE(i.e., value in memory) = BZERO + BSCALE * FITS(i.e., value on disk)
+
+
+#define MEAN_STAT PS_STAT_ROBUST_MEDIAN // Statistic to use for mean
+#define STDEV_STAT PS_STAT_ROBUST_STDEV // Statistic to use for stdev
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Private (file-static) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Determine appropriate BSCALE and BZERO for an image, preserving dynamic range
+static bool scaleRange(double *bscale, // Scaling, to return
+                       double *bzero, // Zero point, to return
+                       const psImage *image, // Image to scale
+                       const psFitsOptions *options // FITS options
+    )
+{
+    psAssert(bscale, "impossible");
+    psAssert(bzero, "impossible");
+    psAssert(image, "impossible");
+    psAssert(options, "impossible");
+
+    double range = pow(2.0, options->bitpix) - 1.0; // Range of values for target BITPIX, reduced by the BLANK
+    double min = INFINITY, max = -INFINITY; // Minimum and maximum values
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+
+    // Determine the minimum and maximum values
+#define SCALE_DETERMINE_CASE(IN, INTYPE) \
+    case PS_TYPE_##INTYPE: { \
+        for (int y = 0; y < numRows; y++) { \
+            for (int x = 0; x < numCols; x++) { \
+                ps##INTYPE value = (IN)->data.INTYPE[y][x]; /* Value of interest */ \
+                if (isfinite(value)) { \
+                    if (value < min) { \
+                        min = value; \
+                    } \
+                    if (value > max) { \
+                        max = value; \
+                    } \
+                } \
+            } \
+        } \
+        break; \
+    }
+
+    switch (image->type.type) {
+        SCALE_DETERMINE_CASE(image, F32);
+        SCALE_DETERMINE_CASE(image, F64);
+      default:
+        psAbort("Should be unreachable.");
+    }
+
+    if (!isfinite(min) || !isfinite(max)) {
+        psWarning("No valid values in image to derive BSCALE,BZERO");
+        *bscale = 1.0;
+        *bzero = 0.0;
+        return false;
+    }
+    if (min == max) {
+        *bscale = 1.0;
+        *bzero = min;
+    } else {
+        *bscale = (max - min) / range ;
+        *bzero = min + 0.5 * range * (*bscale);
+    }
+
+    return true;
+}
+
+// Determine appropriate BSCALE and BZERO for an image, mapping the standard deviation to the nominated number
+// of bits
+static bool scaleStdev(double *bscale, // Scaling, to return
+                       double *bzero, // Zero point, to return
+                       const psImage *image, // Image to scale
+                       const psImage *mask, // Mask image
+                       psMaskType maskVal, // Value to mask
+                       const psFitsOptions *options // FITS options
+    )
+{
+    psAssert(bscale, "impossible");
+    psAssert(bzero, "impossible");
+    psAssert(image, "impossible");
+    psAssert(options, "impossible");
+
+    // Measure the mean and stdev
+    // psImageBackground automatically excludes pixels that are non-finite, so we don't need to bother about a
+    // mask.
+    psU64 seed = p_psRandomGetSystemSeed(false);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, seed);
+    psStats *stats = psStatsAlloc(MEAN_STAT | STDEV_STAT); // Statistics object
+    if (!psImageBackground(stats, NULL, image, mask, maskVal, rng)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to perform statistics on image");
+        psFree(rng);
+        psFree(stats);
+        return false;
+    }
+    psFree(rng);
+
+    double mean = psStatsGetValue(stats, MEAN_STAT); // Mean
+    double stdev = psStatsGetValue(stats, STDEV_STAT); // Standard deviation
+    psFree(stats);
+    if (!isfinite(mean) || !isfinite(stdev)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Mean (%f) or stdev (%f) is non-finite.", mean, stdev);
+        return false;
+    }
+
+    long range = 1 << options->stdevBits;  // Range of values to carry standard deviation
+    *bscale = stdev / (double) range;
+
+    double imageVal;                    // Value on image
+    long diskVal;                       // Corresponding value on disk
+    switch (options->scaling) {
+      case PS_FITS_SCALE_STDEV_POSITIVE:
+        // Put (mean - N sigma) at the lowest possible value: predominantly positive images
+        imageVal = mean - options->stdevNum * stdev;
+        diskVal = - (1 << (options->bitpix - 1));
+        break;
+      case PS_FITS_SCALE_STDEV_NEGATIVE:
+        // Put (mean + N sigma) at the highest possible value: predominantly negative images
+        imageVal = mean + options->stdevNum * stdev;
+        diskVal = (1 << (options->bitpix - 1)) - 1;
+        break;
+      case PS_FITS_SCALE_STDEV_BOTH:
+        // Put mean right in the middle: images with an equal abundance of positive and negative values
+        imageVal = mean;
+        diskVal = 0;
+        break;
+      default:
+        psAbort("Should never get here.");
+    }
+
+    *bzero = imageVal - *bscale * diskVal;
+
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool psFitsScaleDetermine(double *bscale, double *bzero, long *blank, const psImage *image,
+                          const psImage *mask, psMaskType maskVal, const psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(bscale, false);
+    PS_ASSERT_PTR_NON_NULL(bzero, false);
+    PS_ASSERT_PTR_NON_NULL(blank, false);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    if (mask) {
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(mask, image, false);
+    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    *bscale = NAN;
+    *bzero = NAN;
+    *blank = 0;
+
+    psFitsOptions *options = fits->options; // FITS options
+    if (!PS_IS_PSELEMTYPE_REAL(image->type.type) || !options) {
+        return true;
+    }
+
+    switch (options->bitpix) {
+      case 0:
+        // No scaling applied
+        return true;
+      case 8:
+      case 16:
+      case 32:
+      case 64:
+        // Nothing to do; just allowing these values to pass through
+        break;
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Target bitpix (%d) is not one of 0,8,16,32,64",
+                options->bitpix);
+        return false;
+    }
+
+    *blank = (1 << (options->bitpix - 1)) - 1;
+
+    switch (options->scaling) {
+      case PS_FITS_SCALE_NONE:
+        // No scaling applied
+        break;
+      case PS_FITS_SCALE_RANGE:
+        if (!scaleRange(bscale, bzero, image, options)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to set BSCALE and BZERO from range");
+            return false;
+        }
+        break;
+      case PS_FITS_SCALE_STDEV_POSITIVE:
+      case PS_FITS_SCALE_STDEV_NEGATIVE:
+      case PS_FITS_SCALE_STDEV_BOTH:
+        if (!scaleStdev(bscale, bzero, image, mask, maskVal, options)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to set BSCALE and BZERO from stdev");
+            return false;
+        }
+        break;
+      case PS_FITS_SCALE_MANUAL:
+        *bscale = options->bscale;
+        *bzero = options->bzero;
+        break;
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised FITS scaling method: %x",
+                options->scaling);
+        return false;
+    }
+
+    if (options->bitpix == 8) {
+        // FITS standard wants unsigned for BITPIX=8, two's-complement for BITPIX=16,32,64.
+        *bzero -= *bscale * 128;
+        *blank = 255;
+    }
+
+
+    psTrace("psLib.fits", 3, "BSCALE = %.10lf, BZERO = %.10lf, BLANK = %ld\n", *bscale, *bzero, *blank);
+    return true;
+}
+
+
+psImage *psFitsScaleForDisk(const psImage *image, const psFits *fits, double bscale, double bzero,
+                            psRandom *rng)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    psFitsOptions *options = fits->options; // FITS options
+    if (!PS_IS_PSELEMTYPE_REAL(image->type.type) || !options || options->bitpix == 0) {
+        // No scaling desired
+        return psMemIncrRefCounter((psImage*)image); // Casting away "const"
+    }
+
+    int bitpix = options->bitpix;       // Bits per pixel
+    psElemType outType;                 // Type for output image
+    // Choosing to use signed types because those don't require BSCALE,BZERO to represent them in the FITS
+    // file
+    switch (bitpix) {
+      case 8:
+        // Note: Use unsigned integer for BITPIX=8
+        outType = PS_TYPE_U8;
+        break;
+      case 16:
+        outType = PS_TYPE_S16;
+        break;
+      case 32:
+        outType = PS_TYPE_S32;
+        break;
+      case 64:
+        outType = PS_TYPE_S64;
+        break;
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Target bitpix (%d) is not one of 8,16,32,64", bitpix);
+        return NULL;
+    }
+
+    if (bscale == 1.0 && bzero == 0.0 && !options->fuzz) {
+        return psImageCopy(NULL, image, outType);
+    }
+
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+    psImage *out = psImageAlloc(numCols, numRows, outType); // Output image
+
+    if (!psMemIncrRefCounter(rng) && options->fuzz) {
+        // Don't blab about which seed we're going to get --- it's not necessary for this purpose
+        psU64 seed = p_psRandomGetSystemSeed(false);
+        rng = psRandomAlloc(PS_RANDOM_TAUS, seed);
+    }
+
+#define SCALE_WRITE_OUT_CASE(IN, INTYPE, OUT, OUTTYPE) \
+    case PS_TYPE_##OUTTYPE: { \
+        double scale = 1.0 / bscale; \
+        double zero = bzero; \
+        /* Note: BITPIX=8 treated differently, since it uses unsigned values; the rest use signed */ \
+        double min = bitpix == 8 ? 0 : -pow(2.0, options->bitpix - 1); \
+        double max = bitpix == 8 ? 255 : (pow(2.0, options->bitpix - 1) - 1.0); \
+        for (int y = 0; y < numRows; y++) { \
+            for (int x = 0; x < numCols; x++) { \
+                ps##INTYPE value = (IN)->data.INTYPE[y][x]; \
+                if (!isfinite(value)) { \
+                    /* This choice of "max" for non-finite pixels is mainly cosmetic --- it has to be */ \
+                    /* something, and "min" would produce holes in the cores of bright stars. */ \
+                    (OUT)->data.OUTTYPE[y][x] = max; \
+                } else { \
+                    value = (value - zero) * scale; \
+                    if (options->fuzz) { \
+                       /* Add random factor [0,1): adds a variance of 1/12, */ \
+                       /* but preserves the expectation value */ \
+                        value += psRandomUniform(rng); \
+                    } \
+                    /* Check for underflow and overflow; set either to max */ \
+                    (OUT)->data.OUTTYPE[y][x] = (value < min || value > max ? max : value); \
+                } \
+            } \
+        } \
+        break; \
+    }
+
+#define SCALE_WRITE_IN_CASE(IN, INTYPE, OUT) \
+    case PS_TYPE_##INTYPE: { \
+        switch (outType) { \
+            SCALE_WRITE_OUT_CASE(IN, INTYPE, OUT, U8); \
+            SCALE_WRITE_OUT_CASE(IN, INTYPE, OUT, S16); \
+            SCALE_WRITE_OUT_CASE(IN, INTYPE, OUT, S32); \
+            SCALE_WRITE_OUT_CASE(IN, INTYPE, OUT, S64); \
+          default: \
+            psAbort("Should be unreachable."); \
+        } \
+        break; \
+    }
+
+    switch (image->type.type) {
+        SCALE_WRITE_IN_CASE(image, F32, out);
+        SCALE_WRITE_IN_CASE(image, F64, out);
+      default:
+        psAbort("Should be unreachable.");
+    }
+
+    psFree(rng);
+
+    return out;
+}
+
+
+#if 0
+// This function to apply BSCALE and BZERO to an image read immediately from disk should not be necessary at
+// the present time, since cfitsio should apply the scaling itself in the process of reading.  However, we may
+// later desire it (e.g., if we ever make our own FITS implementation).
+psImage *psFitsScaleFromDisk(psFits *fits, psImage *image)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+
+    if (bscale == 0.0) {
+        // BSCALE = 0 means don't apply anything
+        return psMemIncrRefCounter(image);
+    }
+
+    psElemType inType = image->type.type; // Type for input image
+    psElemType outType;                 // Type for output image
+    switch (inType) {
+      case PS_TYPE_S8:
+      case PS_TYPE_S16:
+      case PS_TYPE_S32:
+      case PS_TYPE_U8:
+      case PS_TYPE_U16:
+        outType = PS_TYPE_F32;
+        break;
+      case PS_TYPE_S64:
+      case PS_TYPE_U32:
+      case PS_TYPE_U64:
+        outType = PS_TYPE_F64;
+        break;
+        // Including floating-point types just in case someone wants to apply a BSCALE and BZERO to them.
+      case PS_TYPE_F32:
+        outType = PS_TYPE_F32;
+        break;
+      case PS_TYPE_F64:
+        outType = PS_TYPE_F64;
+        break;
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unsupported image type: %x", inType);
+        return NULL;
+    }
+
+    int numCols = image->numCols, numRows = image->numRows;
+    psImage *out = psImageAlloc(numCols, numRows, outType); // Output scaled image
+
+
+#define SCALE_READ_OUT_CASE(INTYPE, OUTTYPE) \
+  case PS_TYPE_##OUTTYPE: { \
+      for (int y = 0; y < numRows; y++) { \
+          for (int x = 0; x < numCols; x++) { \
+              out->data.OUTTYPE[y][x] = image->data.INTYPE[y][x] * bscale + bzero; \
+          } \
+      } \
+      break; \
+  }
+
+#define SCALE_READ_IN_CASE(INTYPE) \
+  case PS_TYPE_##INTYPE: { \
+      switch (outType) { \
+          SCALE_READ_OUT_CASE(INTYPE, F32); \
+          SCALE_READ_OUT_CASE(INTYPE, F64); \
+        default: \
+          psAbort("Should never get here: type %x should be F32 or F64", outType); \
+      } \
+      break; \
+  }
+
+    switch (inType) {
+        SCALE_READ_IN_CASE(S8);
+        SCALE_READ_IN_CASE(S16);
+        SCALE_READ_IN_CASE(S32);
+        SCALE_READ_IN_CASE(S64);
+        SCALE_READ_IN_CASE(U8);
+        SCALE_READ_IN_CASE(U16);
+        SCALE_READ_IN_CASE(U32);
+        SCALE_READ_IN_CASE(U64);
+        SCALE_READ_IN_CASE(F32);
+        SCALE_READ_IN_CASE(F64);
+      default:
+          psAbort("Should never get here: type %x should be integer", inType);
+    }
+
+    return out;
+}
+#endif
+
+
+
+psFitsScaling psFitsScalingFromString(const char *string)
+{
+    PS_ASSERT_STRING_NON_EMPTY(string, PS_FITS_SCALE_NONE);
+
+    if (strcasecmp(string, "RANGE") == 0)          return PS_FITS_SCALE_RANGE;
+    if (strcasecmp(string, "STDEV_POSITIVE") == 0) return PS_FITS_SCALE_STDEV_POSITIVE;
+    if (strcasecmp(string, "STDEV_NEGATIVE") == 0) return PS_FITS_SCALE_STDEV_NEGATIVE;
+    if (strcasecmp(string, "STDEV_BOTH") == 0)     return PS_FITS_SCALE_STDEV_BOTH;
+    if (strcasecmp(string, "MANUAL") == 0)         return PS_FITS_SCALE_MANUAL;
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to interpret FITS scaling: %s", string);
+    return PS_FITS_SCALE_NONE;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsScale.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsScale.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsScale.h	(revision 22322)
@@ -0,0 +1,36 @@
+#ifndef PS_FITS_SCALE_H
+#define PS_FITS_SCALE_H
+
+#include <psFits.h>
+#include <psImage.h>
+#include <psType.h>
+#include <psRandom.h>
+
+/// Determine BSCALE and BZERO for an image
+bool psFitsScaleDetermine(double *bscale, ///< Scaling, to return
+                          double *bzero, ///< Zero point, to return
+                          long *blank,  ///< Blank value, to return
+                          const psImage *image, ///< Image to scale
+                          const psImage *mask, ///< Mask image
+                          psMaskType maskVal, ///< Value to mask
+                          const psFits *fits ///< FITS options
+    );
+
+/// Apply the BSCALE and BZERO for an image, so that we get the image as it should be written to disk.
+///
+/// "Fuzz" may be optionally added.  The idea is that the "fuzz" (adding a random number between 0 and 1)
+/// preserves the expectation value of the image (e.g., a value of 0.1 will get translated to zero 90% of the
+/// time, and unity 10% of the time), though at the cost of adding an additional variance of 1/12 (a standard
+/// deviation of ~0.29).
+psImage *psFitsScaleForDisk(const psImage *image, ///< Image to which to apply BSCALE and BZERO
+                            const psFits *fits, ///< FITS file
+                            double bscale, ///< Scaling
+                            double bzero, ///< Zero point
+                            psRandom *rng ///< Random number generator (for the "fuzz"), or NULL
+    );
+
+/// Interpret a string as a scaling method
+psFitsScaling psFitsScalingFromString(const char *string ///< String to interpret
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsTable.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsTable.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsTable.c	(revision 22322)
@@ -0,0 +1,704 @@
+/** @file  psFitsTable.c
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.33 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-07-04 03:18:06 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "psFits.h"
+#include "string.h"
+#include "psError.h"
+
+#include "psImageStructManip.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psLogMsg.h"
+#include "psTrace.h"
+#include "psVector.h"
+#include "psFitsTable.h"
+#include "psFitsHeader.h"
+#include "psAbort.h"
+#include "psAssert.h"
+
+#define MAX_STRING_LENGTH 256  // maximum length string for FITS routines
+
+long psFitsTableSize(const psFits *fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
+
+    int status = 0;                     // CFITSIO status
+    long numRows;                       // Number of rows
+    if (fits_get_num_rows(fits->fd, &numRows, &status)) {
+        psFitsError(status, true, "Unable to determine number of rows in table.");
+        return -1;
+    }
+
+    return numRows;
+}
+
+// Check the FITS file in preparation for reading a table
+static bool readTableCheck(const psFits *fits // FITS file
+                           )
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    if (psFitsGetExtNum(fits) == 0 && !psFitsMoveExtNum(fits, 1, false)) {
+        psError(PS_ERR_IO, false, "Unable to move to first extension to read table.");
+        return false;
+    }
+
+
+    // check that we are positioned on a table HDU
+    int status = 0;                     // CFITSIO status
+    int hdutype;                        // Type of HDU
+    fits_get_hdu_type(fits->fd, &hdutype, &status);
+    if (psFitsError(status, true, "Could not determine the HDU type.")) {
+        return false;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true, _("Current FITS HDU is not a table."));
+        return false;
+    }
+    return true;
+}
+
+
+psMetadata* psFitsReadTableRow(const psFits* fits,
+                               int row)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(row, NULL);
+
+    if (!readTableCheck(fits)) {
+        return NULL;
+    }
+
+    // get the size of the FITS table
+    long numRows;
+    int numCols;
+    int status = 0;
+    fits_get_num_rows(fits->fd, &numRows, &status);
+    fits_get_num_cols(fits->fd, &numCols, &status);
+    if (status != 0) {
+        psFitsError(status, true, "Failed to determine the size of the current HDU table.");
+        return NULL;
+    }
+
+    psTrace("psLib.fits",5,"Table size is %ix%li\n",numCols, numRows);
+    // the row parameter in the proper range?
+    if (row < 0 || row >= numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified row, %d, is not valid for current table of %ld rows."),
+                row, numRows);
+        return NULL;
+    }
+
+    psMetadata* data = psMetadataAlloc();
+
+    int hdutype;                        // Type of HDU: need to distinguish ASCII and binary tables
+    fits_get_hdu_type(fits->fd, &hdutype, &status);
+    if (psFitsError(status, true, "Could not determine the HDU type.")) {
+        return false;
+    }
+
+    int typecode;
+    long repeat;
+    long width;
+    char name[60];
+    for (int col = 1; col <= numCols; col++) {
+        // get the column name
+        if (hdutype == BINARY_TBL) {
+            fits_get_bcolparms(fits->fd, col, name,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, &status);
+        } else {
+            fits_get_acolparms(fits->fd, col, name,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, &status);
+        }
+        // get the column type
+        fits_get_coltype(fits->fd, col, &typecode, &repeat, &width, &status);
+        if (psFitsError(status, true, "Unable to get column type for column %d", col)) {
+            psFree(data);
+            return NULL;
+        }
+
+#define READ_TABLE_ROW_CASE(FITSTYPE, NATIVETYPE, TYPE, VECTYPE) \
+        case FITSTYPE: { \
+                if (repeat == 1) { \
+                    NATIVETYPE value; \
+                    int anynul = 0; \
+                    fits_read_col(fits->fd, FITSTYPE, col,row+1, \
+                                  1, 1, NULL, &value, &anynul, &status); \
+                    psTrace("psLib.fits",5,"Column #%i, '%s', is type %i, repeat %li, Value = %g\n", \
+                            col, name, typecode, repeat, (double)value); \
+                    psMetadataAdd(data,PS_LIST_TAIL, name, \
+                                  PS_DATA_##TYPE, \
+                                  "", (ps##TYPE)value); \
+                } else { \
+                    NATIVETYPE* value = psAlloc(sizeof(NATIVETYPE)*repeat); \
+                    psVector* vec = psVectorAlloc(repeat,PS_TYPE_##VECTYPE); \
+                    int anynul = 0; \
+                    fits_read_col(fits->fd, FITSTYPE, col,row+1, \
+                                  1, repeat, NULL, value, &anynul, &status); \
+                    for (int lcv = 0; lcv < repeat; lcv++) { \
+                        vec->data.VECTYPE[lcv] = value[lcv]; \
+                    } \
+                    psMetadataAdd(data,PS_LIST_TAIL, name, PS_DATA_VECTOR, "", vec); \
+                    psFree(value); \
+                    psFree(vec); \
+                } \
+                break; \
+            }
+
+        switch (typecode) {
+          case TBYTE:
+          case TSHORT:
+          case TLONGLONG:
+            READ_TABLE_ROW_CASE(TLONG, long, S32, S32);
+            READ_TABLE_ROW_CASE(TFLOAT, float, F32, F32);
+            READ_TABLE_ROW_CASE(TDOUBLE, double, F64, F64);
+            READ_TABLE_ROW_CASE(TLOGICAL, bool, BOOL, S8);
+          case TSTRING: {
+              psString value = psStringAlloc(repeat);
+              int anynul = 0;
+              fits_read_col(fits->fd, TSTRING, col,row+1, 1, 1, NULL, &value, &anynul, &status);
+              psTrace("psLib.fits", 5, "Column #%i, '%s', is type %i, repeat %li, value = %s\n",
+                      col, name, typecode, repeat, value);
+              if (anynul == 0) {
+                  psMetadataAdd(data,PS_LIST_TAIL, name, PS_DATA_STRING, NULL, value);
+              }
+              psFree(value);
+              break;
+          }
+          default:
+            psWarning("Data type (%d) not supportted for column %d", typecode, col);
+            psTrace("psLib.fits", 2, "Column %d or row %d was of a non primitive type, %d",
+                    col, row, typecode);
+        }
+
+        if (psFitsError(status, true, "Failed to retrieve table element (%d,%d)", col, row)) {
+            psFree(data);
+            return NULL;
+        }
+    }
+
+    return data;
+}
+
+psArray* psFitsReadTableColumn(const psFits* fits,
+                               const char* colname)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(colname, NULL);
+
+    if (!readTableCheck(fits)) {
+        return NULL;
+    }
+
+    int colnum = 0;
+    int status = 0;
+
+    // find the column by name
+    if (fits_get_colnum(fits->fd, CASESEN, (char*)colname, &colnum, &status) != 0) {
+        psFitsError(status, true, "Specified column, %s, was not found.", colname);
+        return NULL;
+    }
+
+    // get the number of rows
+    long numRows = psFitsTableSize(fits);
+    if (numRows == -1) {
+        return NULL;
+    }
+
+    // get the column length.
+    int width;
+    if (fits_get_col_display_width(fits->fd, colnum, &width, &status) != 0) {
+        psFitsError(status, true, "Could not determine the datatype of the table column.");
+        return NULL;
+    }
+
+    // allocate the buffers
+    psArray* result = psArrayAlloc(numRows);
+    for (int row = 0; row < numRows; row++) {
+        result->data[row] = psStringAlloc(width);
+    }
+
+    fits_read_col_str(fits->fd, colnum, 1, 1, numRows, "", (char**)result->data, NULL, &status);
+    if (psFitsError(status, true, "Failed to read table column.")) {
+        psFree(result);
+        return NULL;
+    }
+
+    return result;
+}
+
+psVector* psFitsReadTableColumnNum(const psFits* fits,
+                                   const char* colname)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(colname, NULL);
+
+    if (!readTableCheck(fits)) {
+        return NULL;
+    }
+
+    int status = 0;
+    int colnum = 0;
+
+    // find the column by name
+    if (fits_get_colnum(fits->fd, CASESEN, (char*)colname, &colnum, &status) != 0) {
+        psFitsError(status, true, "Specified column, %s, was not found.", colname);
+        return NULL;
+    }
+
+    // get the number of rows
+    long numRows = psFitsTableSize(fits);
+    if (numRows == -1) {
+        return NULL;
+    }
+
+    // get the column datatype.
+    int typecode;
+    long repeat;
+    long width;
+    if (fits_get_eqcoltype(fits->fd, colnum, &typecode, &repeat, &width, &status) != 0) {
+        psFitsError(status, true, "Could not determine the datatype of the table column.");
+        return NULL;
+    }
+
+    psVector* result = psVectorAlloc(numRows, p_psFitsTypeFromCfitsio(typecode));
+
+    fits_read_col(fits->fd, typecode, colnum, 1, 1, numRows, NULL, (psPtr)(result->data.U8), NULL, &status);
+    if (psFitsError(status, true, "Failed to read table column.")) {
+        psFree(result);
+        return NULL;
+    }
+
+    return result;
+}
+
+
+psArray* psFitsReadTable(const psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    if (!readTableCheck(fits)) {
+        return NULL;
+    }
+
+    // get the number of rows
+    long numRows = psFitsTableSize(fits);
+    if (numRows == -1) {
+        return NULL;
+    }
+
+    psArray* table = psArrayAlloc(numRows);
+
+    for (int row = 0; row < numRows; row++) {
+        psTrace("psLib.fits",5,"Reading row %i of %li\n", row, numRows);
+        table->data[row] = psFitsReadTableRow(fits,row);
+    }
+
+    return table;
+}
+
+bool psFitsWriteTable(psFits* fits,
+                      const psMetadata* header,
+                      const psArray* table,
+                      const char *extname)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    if (!psFitsMoveLast(fits)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to move to last extension to write table");
+        return false;
+    }
+    return psFitsInsertTable(fits, header, table, extname, true);
+}
+
+
+// Return the size of the column
+static inline size_t columnSize(const psMetadataItem *item // Item for which to get the size
+                               )
+{
+    if (PS_DATA_IS_PRIMITIVE(item->type)) {
+        return 1;
+    }
+    switch (item->type) {
+    case PS_DATA_STRING:
+        return strlen(item->data.V);
+    case PS_DATA_VECTOR: {
+            psVector *vector = item->data.V;
+            return vector ? vector->n : 0;
+        }
+    default:
+        psAbort("Shouldn't ever get here.");
+    }
+    return 0;
+}
+
+// Get the TFORM character, given a PS type
+static inline char getTForm(psDataType type)
+{
+    switch (type) {
+    case PS_TYPE_U8:
+    case PS_TYPE_S8:
+        return 'B';
+    case PS_TYPE_S16:
+        return 'I';
+    case PS_TYPE_S32:
+        return 'J';
+    case PS_TYPE_U64:
+    case PS_TYPE_S64:
+        return 'K';
+    case PS_TYPE_U16:
+        return 'U';
+    case PS_TYPE_U32:
+        return 'V';
+    case PS_TYPE_F32:
+        return 'E';
+    case PS_TYPE_F64:
+        return 'D';
+    case PS_TYPE_BOOL:
+        return 'L';
+    case PS_DATA_STRING:
+        return 'A';
+    default:
+        psError(PS_ERR_UNKNOWN, true, "Unknown type: %x\n", type);
+        return '?';
+    }
+}
+
+
+// Column specification
+// Included here, because there's no need for the user to have access to it
+typedef struct {
+    psDataType type;                    // psLib type (e.g., PS_DATA_STRING or PS_TYPE_F32)
+    size_t size;                        // Size (number of repeats)
+    psElemType vectorType;              // psLib type of vectors
+} colSpec;
+
+
+bool psFitsInsertTable(psFits* fits,
+                       const psMetadata* header,
+                       const psArray* table,
+                       const char *extname,
+                       bool after)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_ARRAY_NON_NULL(table, false);
+
+    int status = 0;
+
+    long numRows = table->n;
+    if (numRows < 1) {
+        // no table data, what can I do?
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                _("Can't create a table without any rows."));
+        return false;
+    }
+
+    // Find the unique items in the array of metadata 'rows', and their sizes
+    psMetadata *colSpecs = psMetadataAlloc(); // Column specifications
+    size_t rowSize = 0;                 // Size (in bytes) of each row
+    for (long i = 0; i < numRows; i++) {
+        psMetadata* row = table->data[i];
+        if (!row) {
+            continue;
+        }
+        psMetadataIterator *rowIter = psMetadataIteratorAlloc(row, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *colItem;        // Column item, from iteration
+        while ((colItem = psMetadataGetAndIncrement(rowIter))) {
+            if (!(PS_DATA_IS_PRIMITIVE(colItem->type) || colItem->type == PS_DATA_STRING ||
+                    colItem->type == PS_DATA_VECTOR)) {
+                // unsupported type -- treating as an error
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        "Unsupported data type (%d) for Metadata Item '%s' in row %ld.",
+                        colItem->type, colItem->name, i);
+                psFree(rowIter);
+                psFree(colSpecs);
+                return false;
+            }
+
+            size_t size = columnSize(colItem); // Size for this column
+
+            // Check to see if we know about this one; or update the size if required
+            psMetadataItem *colSpecItem = psMetadataLookup(colSpecs, colItem->name);
+            if (!colSpecItem) {
+                // A new one!
+                colSpec *spec = psAlloc(sizeof(colSpec)); // Specification for this column
+                // BOOL type is not a valid vector type, so we translate it to U8
+                spec->type = colItem->type == PS_TYPE_BOOL ? PS_TYPE_U8 : colItem->type;
+                spec->size = size;
+                if (colItem->type == PS_DATA_VECTOR) {
+                    psVector *vector = colItem->data.V; // The vector
+                    spec->vectorType = vector->type.type;
+                }
+                psMetadataAddPtr(colSpecs, PS_LIST_TAIL, colItem->name, PS_DATA_UNKNOWN, "", spec);
+                psFree(spec);           // Drop reference
+                rowSize += PSELEMTYPE_SIZEOF(spec->type);
+            } else {
+                colSpec *spec = colSpecItem->data.V; // The specification
+                if (size > spec->size) {
+                    spec->size = size;
+                }
+                if (colItem->type != spec->type &&
+                    colItem->type != PS_TYPE_BOOL && spec->type != PS_TYPE_U8) {
+                    psWarning("Differing type found for column %s: %x vs %x --- using the first found.\n",
+                              colSpecItem->name, colItem->type, spec->type);
+                }
+                if (colItem->type == PS_DATA_VECTOR) {
+                    psVector *vector = colItem->data.V; // The vector
+                    if (vector->type.type != spec->vectorType) {
+                        psWarning("Differing vector type found for column %s: %x vs %x "
+                                 "--- using the first found.\n", colSpecItem->name, vector->type.type,
+                                 spec->vectorType);
+                    }
+                }
+            }
+        }
+        psFree(rowIter);
+    }
+
+    long numColumns = colSpecs->list->n;// Number of columns
+    if (numColumns == 0) {
+        // No table columns found
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Did not find any column data to write to a table.");
+        psFree(colSpecs);
+        return false;
+    }
+
+    // Create array of column names and types.
+    psArray *columnNames = psArrayAlloc(numColumns); // Array of column names, for cfitsio
+    psArray *columnTypes = psArrayAlloc(numColumns); // Array of column types, for cfitsio
+    psMetadataIterator *colSpecsIter = psMetadataIteratorAlloc(colSpecs, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *colSpecItem;        // Column specification item, from iteration
+    for (long i = 0; (colSpecItem = psMetadataGetAndIncrement(colSpecsIter)); i++) {
+        colSpec *spec = colSpecItem->data.V; // The specification
+        columnNames->data[i] = psMemIncrRefCounter(colSpecItem->name);
+        psString colType = NULL;        // The column type
+        if (spec->type == PS_DATA_VECTOR) {
+            psStringAppend(&colType, "%zd%c", spec->size, getTForm(spec->vectorType));
+        } else {
+            psStringAppend(&colType, "%zd%c", spec->size, getTForm(spec->type));
+        }
+        columnTypes->data[i] = colType;
+    }
+
+    // Create the table HDU
+    int numHDUs = psFitsGetSize(fits);  // Number of HDUs in file
+    if (numHDUs == 0) {
+        // We're creating the first extension
+        fits_create_tbl(fits->fd,
+                        BINARY_TBL,
+                        table->n, // number of rows in table
+                        numColumns, // number of columns in table
+                        (char**)columnNames->data, // names of the columns
+                        (char**)columnTypes->data, // format of the columns
+                        NULL, // physical unit of columns
+                        NULL, // skip extension name: we set the by hand below
+                        &status);
+    } else {
+        if (!after) {
+            if (psFitsGetExtNum(fits) == 0) {
+                // We're creating a replacement primary HDU.
+                // Set status to signal fits_insert_img to insert a new primary HDU
+                status = PREPEND_PRIMARY;
+            } else {
+                // Move back one to perform an insert after the previous HDU
+                psFitsMoveExtNum(fits, -1, true);
+            }
+        }
+        // Insert the table
+        fits_insert_btbl(fits->fd,
+                         table->n, // number of rows in table
+                         numColumns, // number of columns in table
+                         (char**)columnNames->data, // names of the columns
+                         (char**)columnTypes->data, // format of the columns
+                         NULL, // physical unit of columns
+                         NULL, // skip extension name: we set this by hand below
+                         0, &status);
+    }
+    psFree(columnNames);
+    psFree(columnTypes);
+
+    if (status != 0) {
+        psFitsError(status, true, "Unable to create FITS table with %ld columns and %ld rows",
+                    numColumns, table->n);
+        psFree(colSpecsIter);
+        psFree(colSpecs);
+        return false;
+    }
+
+    // Write header
+    if (header && !psFitsWriteHeader(fits, header)) {
+        psError(PS_ERR_IO, false, "Unable to write FITS header.\n");
+        psFree(colSpecsIter);
+        psFree(colSpecs);
+        return false;
+    }
+
+    // write the header, if any.
+    if (extname && strlen(extname) > 0) {
+        if (!psFitsSetExtName(fits, extname)) {
+            psError(PS_ERR_IO, false, "Unable to write FITS header extension name.\n");
+            psFree(colSpecsIter);
+            psFree(colSpecs);
+            return false;
+        }
+    }
+
+    // cfitsio requires that we write the data by columns --- urgh!
+    psMetadataIteratorSet(colSpecsIter, PS_LIST_HEAD);
+    for (long colNum = 1; (colSpecItem = psMetadataGetAndIncrement(colSpecsIter)); colNum++) {
+        // Note: colNum is unit-indexed, because it's for cfitsio
+        colSpec *spec = colSpecItem->data.V; // The specification
+        if (PS_DATA_IS_PRIMITIVE(spec->type)) {
+            size_t dataSize = PSELEMTYPE_SIZEOF(spec->type); // Size (in bytes) of this type
+            psVector *columnData = psVectorAlloc(table->n, spec->type); // The raw row data, to be written
+            psVectorInit(columnData, 0);
+            for (long i = 0; i < table->n; i++) {
+                psMetadata *row = table->data[i]; // The row of interest
+                psMetadataItem *dataItem = psMetadataLookup(row, colSpecItem->name); // The value of interest
+                memcpy(&columnData->data.U8[i * dataSize], &dataItem->data, dataSize);
+            }
+
+            int fitsDataType;           // Data type for cfitsio
+            p_psFitsTypeToCfitsio(spec->type, NULL, NULL, &fitsDataType);
+            fits_write_col(fits->fd,
+                           fitsDataType,
+                           colNum, // column number
+                           1, // first row
+                           1, // first element
+                           table->n, // number of rows
+                           columnData->data.U8, // the data
+                           &status);
+            psFree(columnData);
+        } else {
+            switch (spec->type) {
+            case PS_DATA_STRING: {
+                    psArray *strings = psArrayAlloc(table->n); // Array of strings
+                    for (long i = 0; i < table->n; i++) {
+                        psMetadata *row = table->data[i]; // The row of interest
+                        strings->data[i] = psMemIncrRefCounter(psMetadataLookupStr(NULL, row,
+                                                               colSpecItem->name));
+                    }
+                    fits_write_col_str(fits->fd, colNum, 1, 1, table->n, (char**)strings->data, &status);
+                    psFree(strings);
+                    break;
+                }
+            case PS_DATA_VECTOR: {
+                    size_t dataSize = PSELEMTYPE_SIZEOF(spec->vectorType); // Size of data, in bytes
+                    psVector *columnData = psVectorAlloc(spec->size * table->n * dataSize, PS_TYPE_U8);
+                    psVectorInit(columnData, 0);
+                    for (long i = 0; i < table->n; i++) {
+                        psMetadata *row = table->data[i]; // The row of interest
+                        psMetadataItem* dataItem = psMetadataLookup(row, colSpecItem->name);
+                        if (dataItem->type != PS_DATA_VECTOR) {
+                            // Just in case --- get a zero instead of some weird result
+                            continue;
+                        }
+                        psVector *vector = dataItem->data.V;
+                        memcpy(&columnData->data.U8[i * dataSize * spec->size], vector->data.U8,
+                               vector->n * dataSize);
+                    }
+
+                    int fitsDataType;           // Data type for cfitsio
+                    p_psFitsTypeToCfitsio(spec->vectorType, NULL, NULL, &fitsDataType);
+                    fits_write_col(fits->fd, fitsDataType, colNum, 1, 1, table->n * spec->size,
+                                   columnData->data.U8, &status);
+                    psFree(columnData);
+                    break;
+                }
+            default:
+                psAbort("Should never get here.\n");
+            }
+        }
+
+        // Check error status from writing column
+        if (status != 0) {
+            psFitsError(status, true, "Unable to write column %ld of FITS table", colNum);
+            psFree(colSpecsIter);
+            psFree(colSpecs);
+            return false;
+        }
+    }
+
+    psFree(colSpecsIter);
+    psFree(colSpecs);
+
+    // This forces a re-scan of the header to ensure everything's kosher.  We found this occassionally
+    // necessary for compressed images, which are tables, so perhaps it helps here too.  I guess it can't
+    // hurt.
+    ffrdef(fits->fd, &status);
+    if (psFitsError(status, true, "Could not re-scan HDU.")) {
+        return false;
+    }
+
+    return true;
+}
+
+bool psFitsUpdateTable(psFits* fits,
+                       const psMetadata* data,
+                       int row)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_METADATA_NON_NULL(data, false);
+    PS_ASSERT_INT_NONNEGATIVE(row, false);
+
+    if (!readTableCheck(fits)) {
+        return NULL;
+    }
+
+    int status = 0;
+    psMetadataIterator* iter = psMetadataIteratorAlloc((psPtr)data, PS_LIST_HEAD, NULL);
+    psMetadataItem* item;
+    while ( (item=psMetadataGetAndIncrement(iter)) != NULL) {
+        if (PS_DATA_IS_PRIMITIVE(item->type) ||
+                item->type == PS_DATA_BOOL ||
+                item->type == PS_DATA_STRING) {
+            // operating on primitive data type or string, i.e., not a complex object
+            int colnum = 0;
+
+            if (fits_get_colnum(fits->fd, CASESEN, item->name, &colnum, &status) == 0) {
+                // cooresponding column found in table
+                int dataType;
+                p_psFitsTypeToCfitsio(item->type, NULL, NULL, &dataType);
+
+                if (fits_write_col(fits->fd, dataType, colnum, row+1, 1, 1, &item->data, &status) != 0) {
+                    psFitsError(status, true, "Could not write data to file.");
+                    psFree(iter);
+                    return false;
+                }
+            } else {
+                // the column was not found.
+                psWarning("No column with the name '%s' exists in the table.", item->name);
+            }
+        }
+    }
+
+    psFree(iter);
+
+    // This forces a re-scan of the header to ensure everything's kosher.  We found this occassionally
+    // necessary for compressed images, which are tables, so perhaps it helps here too.  I guess it can't
+    // hurt.
+    ffrdef(fits->fd, &status);
+    if (psFitsError(status, true, "Could not re-scan HDU.")) {
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsTable.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsTable.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/fits/psFitsTable.h	(revision 22322)
@@ -0,0 +1,122 @@
+/* @file  psFitsTable.h
+ * @brief Contains Fits I/O routines
+ *
+ * @author EAM, PAP, JH
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-10-09 02:56:23 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FITSTABLE_H
+#define PS_FITSTABLE_H
+
+/// @addtogroup FileIO Input/Output
+/// @{
+
+#include "psFits.h"
+
+#include "psType.h"
+#include "psArray.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psImage.h"
+
+/// Return the number of rows in the current table
+///
+/// The current HDU type must be either PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+long psFitsTableSize(const psFits *fits ///< FITS file
+                     );
+
+/** Reads a table row.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psMetadata*    The table row's data.  The keys are the column names.
+ */
+psMetadata* psFitsReadTableRow(
+    const psFits* fits,                ///< the psFits object
+    int row                            ///< row number to read
+);
+
+/** Reads a table column.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*    Array of data items for the specified column or NULL
+ *                      if an error occurred.
+ */
+psArray* psFitsReadTableColumn(
+    const psFits* fits,                ///< the psFits object
+    const char* colname                ///< the column name
+);
+
+/** Reads a table column of numbers.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psVector*    Vector of data for the specified column or NULL
+ *                       if an error occurred.
+ */
+psVector* psFitsReadTableColumnNum(
+    const psFits* fits,                ///< the psFits object
+    const char* colname                ///< the column name
+);
+
+
+/** Reads a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*     Array of psMetadata items, which contains the output
+ *                       data items of each row.
+ *
+ *  @see psFitsReadTableRow
+ */
+psArray* psFitsReadTable(
+    const psFits* fits                  ///< the psFits object
+);
+
+/** Writes a whole FITS table. A new HDU of the type BINTABLE is appended
+ *  to the file.
+ *
+ *  @return bool        TRUE if the write was successful, otherwise FALSE
+ *
+ *  @see psFitsReadTable
+ *  @see psFitsInsertTable
+ */
+bool psFitsWriteTable(
+    psFits* fits,                      ///< the psFits object
+    const psMetadata* header,          ///< header items for the new HDU.  Can be NULL.
+    const psArray* table, ///< Array of psMetadata items, which contains the output data items of each row.
+    const char *extname                 ///< Extension name
+);
+
+/** Inserts a whole FITS table. A new HDU of the type BINTABLE is inserted either
+ *  before or after, depending on the AFTER parameter, the current HDU.
+ *
+ *  @return bool        TRUE if the insert/write was successful, otherwise FALSE
+ *
+ *  @see psFitsWriteTable
+ */
+bool psFitsInsertTable(
+    psFits* fits,                  ///< the psFits object
+    const psMetadata* header,      ///< header items for the new HDU.  Can be NULL.
+    const psArray* table, ///< Array of psMetadata items, which contains the output data items of each row.
+    const char *extname,                ///< Extension name
+    bool after    ///< TRUE if insert is done after CHDU, otherwise table is inserted before CHDU
+);
+
+/** Updates a FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return bool        TRUE if the write was successful, otherwise FALSE
+ *
+ *  @see psFitsWriteTable
+ */
+bool psFitsUpdateTable(
+    psFits* fits,                      ///< the psFits object
+    const psMetadata* data,
+    ///< Array of psMetadata items, which contains the output data items of each row.
+    int row                            ///< the row number to update.
+);
+
+/// @}
+#endif // #ifndef PS_FITS_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/Makefile.am	(revision 22322)
@@ -0,0 +1,40 @@
+#Makefile for image operation functions of psLib
+#
+noinst_LTLIBRARIES = libpslibimageops.la
+
+libpslibimageops_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(GSL_CFLAGS)
+libpslibimageops_la_SOURCES = \
+	psImageBackground.c \
+	psImageConvolve.c \
+	psImageGeomManip.c \
+	psImageInterpolate.c \
+	psImagePixelExtract.c \
+	psImagePixelManip.c \
+	psImageStats.c \
+	psImageStructManip.c \
+	psImageMaskOps.c \
+	psImageBinning.c \
+	psImageMap.c \
+	psImageMapFit.c \
+	psImagePixelInterpolate.c \
+	psImageUnbin.c
+
+EXTRA_DIST = imageops.i
+
+pkginclude_HEADERS = \
+	psImageBackground.h \
+	psImageConvolve.h \
+	psImageGeomManip.h \
+	psImageInterpolate.h \
+	psImagePixelExtract.h \
+	psImagePixelManip.h \
+	psImageStats.h \
+	psImageStructManip.h \
+	psImageMaskOps.h \
+	psImageBinning.h \
+	psImageMap.h \
+	psImageMapFit.h \
+	psImagePixelInterpolate.h \
+	psImageUnbin.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/imageops.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/imageops.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/imageops.i	(revision 22322)
@@ -0,0 +1,8 @@
+/* imageops headers */
+%include "psImageConvolve.h"
+%include "psImageGeomManip.h"
+%include "psImagePixelExtract.h"
+%include "psImagePixelManip.h"
+%include "psImageStats.h"
+%include "psImageStructManip.h"
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBackground.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBackground.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBackground.c	(revision 22322)
@@ -0,0 +1,134 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "psMemory.h"
+#include "psTrace.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psStats.h"
+#include "psType.h"
+#include "psAssert.h"
+#include "psRandom.h"
+#include "psError.h"
+
+// XXX allow the user to choose the stats method?
+// (SAMPLE_MEAN, CLIPPED_MEAN, ROBUST_MEDIAN, FITTED_MEAN)
+bool psImageBackground(psStats *stats, psVector **sample, const psImage *image, const psImage *mask, psMaskType maskValue, psRandom *rng)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    if (mask) {
+        PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(mask, image, NULL);
+    }
+    if (stats->options & PS_STAT_ROBUST_QUARTILE) {
+        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(stats->min, 0.0, NULL);
+        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(stats->max, 0.0, NULL);
+        PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(stats->min, 1.0, NULL);
+        PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(stats->max, 1.0, NULL);
+    }
+    PS_ASSERT_INT_NONNEGATIVE(stats->nSubsample, NULL);
+    PS_ASSERT_PTR_NON_NULL(rng, NULL);
+
+    // Size of image
+    long nx = image->numCols;
+    long ny = image->numRows;
+
+    const int Npixels = nx*ny;                // Total number of pixels
+    const int Nsubset = (stats->nSubsample == 0) ? Npixels : PS_MIN(stats->nSubsample, Npixels); // Number of pixels in subset
+
+    psVector *values;                   // Vector containing subsample
+    if (sample) {
+        *sample = psVectorRecycle(*sample, Nsubset, PS_TYPE_F32);
+        values = psMemIncrRefCounter(*sample);
+        values->n = 0;
+    } else {
+        values = psVectorAllocEmpty(Nsubset, PS_TYPE_F32);
+    }
+
+    // Minimum and maximum values
+    float min = values->data.F32[0];
+    float max = values->data.F32[0];
+
+    // select a subset of the image pixels to measure the stats
+    long n = 0;                         // Number of actual pixels in subset
+    for (long i = 0; i < Nsubset; i++) {
+        double frnd = psRandomUniform(rng);
+        int pixel = Npixels * frnd;
+        int ix = pixel % nx;
+        int iy = pixel / nx;
+
+        if (!isfinite(image->data.F32[iy][ix]) || (mask && mask->data.U8[iy][ix] & maskValue)) {
+            continue;
+        }
+
+        float value = image->data.F32[iy][ix];
+        min = PS_MIN(value, min);
+        max = PS_MIN(value, max);
+        values->data.F32[n] = value;
+        n++;
+    }
+    if (n < 0.01*Nsubset) {
+        psLogMsg("psLib.psImageBackground", PS_LOG_INFO,
+                 "Unable to measure image background: too few data points (%ld)", n);
+        psFree(values);
+        return false;
+    }
+
+    values->n = n;
+
+    if (stats->options & PS_STAT_ROBUST_QUARTILE) {
+        // use simple stats code (old verions)
+        // XXX this hack is just for testing, drop when I am happy with the psStats version of the values
+
+        int imin = stats->min * n;
+        int imax = stats->max * n;
+        int npts = imax - imin + 1;
+
+        if (!psVectorSort(values, values)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to sort values.\n");
+            psFree(values);
+            return false;
+        }
+
+        // Subtract the median when we add the numbers, so we don't get numerical problems
+        float median = npts % 2 ? 0.5 * (values->data.F32[npts/2 - 1] + values->data.F32[npts/2]) :
+                       values->data.F32[npts/2];
+        double value = 0;
+        for (long i = imin; (i <= imax) && (i < n); i++) {
+            value += values->data.F32[i] - median;
+        }
+        value = value / npts + median;
+        stats->robustMedian = value;
+        stats->robustUQ = values->data.F32[imax];
+        stats->robustLQ = values->data.F32[imin];
+    } else {
+        if (!psVectorStats (stats, values, NULL, NULL, 0)) {
+            if (psTraceGetLevel("psLib.imageops") >= 5) {
+                FILE *f = fopen ("vector.dat", "w");
+                int fd = fileno(f);
+                p_psVectorPrint (fd, values, "values");
+                fclose (f);
+            }
+            psError(PS_ERR_UNKNOWN, false, "Unable to measure statistics for image background "
+                    "(%dx%d, (row0,col0) = (%d,%d)",
+                    image->numRows, image->numCols, image->row0, image->col0);
+            psFree(values);
+            return false;
+        }
+        if (psTraceGetLevel("psLib.imageops") >= 6) {
+            FILE *f = fopen ("vector.dat", "w");
+            int fd = fileno(f);
+            p_psVectorPrint (fd, values, "values");
+            fclose (f);
+        }
+    }
+
+    psFree(values);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBackground.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBackground.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBackground.h	(revision 22322)
@@ -0,0 +1,37 @@
+/** @file  psImageUnbin.h
+ *
+ *  @brief Functions to determine the image background
+ *
+ *  @author EAM, IfA
+ *
+ *  $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ *  $Date: 2008-01-17 22:07:33 $
+ *  Copyright 2004-2005 IfA, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_BACKGROUND_H
+#define PS_IMAGE_BACKGROUND_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+#include <psStats.h>
+#include <psImage.h>
+#include <psVector.h>
+#include <psType.h>
+#include <psRandom.h>
+
+// Get the background for an image
+bool psImageBackground(psStats *stats, // desired measurement and options
+                       psVector **sample, // Vector of data used for analysis (buffer), or NULL
+                       const psImage *image, // Image for which to get the background
+                       const psImage *mask, // Mask image
+                       psMaskType maskValue, // Mask pixels which this mask value
+                       psRandom *rng // Random number generator (for pixel selection)
+                      );
+
+/// @}
+#endif // #ifndef PS_IMAGE_BACKGROUND_H
+
+/* the user may supply a psVector ** or NULL to sample.  if a vector is supplied,
+   the user must free the resulting vector */
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBinning.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBinning.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBinning.c	(revision 22322)
@@ -0,0 +1,206 @@
+/** @file  psImageBinning.c
+ *
+ *  @brief Functions to define the binning strategy and to perform image binning / unbinning
+ *  (resampling).
+ *
+ *  @ingroup Image
+ *
+ *  @author Eugene Magnier, IfA
+ *
+ *  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-08 03:10:30 $
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psAssert.h"
+#include "psRegion.h"
+#include "psImage.h"
+#include "psImageBinning.h"
+
+static void psImageBinningFree(psImageBinning *binning) {
+    return;
+}
+
+psImageBinning *psImageBinningAlloc() {
+    psImageBinning *binning = (psImageBinning*)psAlloc(sizeof(psImageBinning));
+    psMemSetDeallocator(binning, (psFreeFunc)psImageBinningFree);
+
+    return binning;
+}
+
+bool psMemCheckBinning(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) psImageBinningFree);
+}
+
+void psImageBinningSetRuffSize(psImageBinning *binning, psImageBinningAlign align) {
+
+    assert (binning->nXfine > 0);
+    assert (binning->nYfine > 0);
+    assert (binning->nXbin > 0);
+    assert (binning->nYbin > 0);
+
+    // force nXruff*nXbin > nXfine
+    binning->nXruff = binning->nXfine / binning->nXbin;
+    if (binning->nXfine % binning->nXbin) binning->nXruff ++;
+
+    // force nYruff*nYbin > nYfine
+    binning->nYruff = binning->nYfine / binning->nYbin;
+    if (binning->nYfine % binning->nYbin) binning->nYruff ++;
+
+    switch (align) {
+      case PS_IMAGE_BINNING_LEFT:
+        binning->nXoff = 0;
+        binning->nYoff = 0;
+        break;
+      case PS_IMAGE_BINNING_CENTER:
+        binning->nXoff = (binning->nXruff * binning->nXbin - binning->nXfine) / 2;
+        binning->nYoff = (binning->nYruff * binning->nYbin - binning->nYfine) / 2;
+        break;
+      case PS_IMAGE_BINNING_RIGHT:
+        binning->nXoff = (binning->nXruff * binning->nXbin - binning->nXfine);
+        binning->nYoff = (binning->nYruff * binning->nYbin - binning->nYfine);
+        break;
+      default:
+        psAbort ("programming error in %s: impossible case\n", __func__);
+    }
+    return;
+}
+
+void psImageBinningSetFineSize(psImageBinning *binning, psImageBinningAlign align) {
+
+    binning->nXfine = binning->nXruff * binning->nXbin;
+    binning->nYfine = binning->nYruff * binning->nYbin;
+    return;
+}
+
+void psImageBinningSetSkip(psImageBinning *binning, psImage *image)
+{
+    int col0, row0;                     // Offset for image
+    if (image) {
+        col0 = image->col0;
+        row0 = image->row0;
+    } else {
+        col0 = row0 = 0;
+    }
+    psImageBinningSetSkipByOffset(binning, col0, row0);
+    return;
+}
+
+void psImageBinningSetSkipByOffset(psImageBinning *binning, int col0, int row0) {
+
+    binning->nXskip = col0 - binning->nXoff;
+    binning->nYskip = row0 - binning->nYoff;
+    return;
+}
+
+void psImageBinningSetScale(psImageBinning *binning, psImageBinningAlign align) {
+
+    assert (binning->nXfine > 0);
+    assert (binning->nYfine > 0);
+    assert (binning->nXruff > 0);
+    assert (binning->nYruff > 0);
+
+    // force nXruff*nXbin > nXfine
+    binning->nXbin = binning->nXfine / binning->nXruff;
+    if (binning->nXfine % binning->nXruff) binning->nXbin ++;
+
+    // force nYruff*nYbin > nYfine
+    binning->nYbin = binning->nYfine / binning->nYruff;
+    if (binning->nYfine % binning->nYruff) binning->nYbin ++;
+
+    switch (align) {
+      case PS_IMAGE_BINNING_LEFT:
+        binning->nXoff = 0;
+        binning->nYoff = 0;
+        break;
+      case PS_IMAGE_BINNING_CENTER:
+        binning->nXoff = (binning->nXruff * binning->nXbin - binning->nXfine) / 2;
+        binning->nYoff = (binning->nYruff * binning->nYbin - binning->nYfine) / 2;
+        break;
+      case PS_IMAGE_BINNING_RIGHT:
+        binning->nXoff = (binning->nXruff * binning->nXbin - binning->nXfine);
+        binning->nYoff = (binning->nYruff * binning->nYbin - binning->nYfine);
+        break;
+      default:
+        psAbort ("programming error in %s: impossible case\n", __func__);
+    }
+    return;
+}
+
+psRegion psImageBinningSetFineRegion (psImageBinning *binning, psRegion ruffRegion) {
+
+    psRegion fineRegion;
+
+    fineRegion.x0 = ruffRegion.x0 * binning->nXbin;
+    fineRegion.x1 = ruffRegion.x1 * binning->nXbin;
+    fineRegion.y0 = ruffRegion.y0 * binning->nYbin;
+    fineRegion.y1 = ruffRegion.y1 * binning->nYbin;
+    return fineRegion;
+}
+
+psRegion psImageBinningSetRuffRegion (psImageBinning *binning, psRegion fineRegion) {
+
+    psRegion ruffRegion;
+
+    ruffRegion.x0 = fineRegion.x0 / binning->nXbin;
+    ruffRegion.x1 = fineRegion.x1 / binning->nXbin;
+    ruffRegion.y0 = fineRegion.y0 / binning->nYbin;
+    ruffRegion.y1 = fineRegion.y1 / binning->nYbin;
+    return ruffRegion;
+}
+
+
+// convert the fine coordinate to the ruff coordinate
+double psImageBinningGetRuffX (const psImageBinning *binning, const double xFine) {
+
+    PS_ASSERT_INT_POSITIVE(binning->nXbin, NAN);
+    PS_ASSERT_INT_POSITIVE(binning->nYbin, NAN);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(binning->nXskip, binning->nXbin, NAN);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(binning->nYskip, binning->nYbin, NAN);
+
+    double xRuff = (xFine - binning->nXskip)/binning->nXbin;
+    return xRuff;
+}
+double psImageBinningGetRuffY (const psImageBinning *binning, const double yFine) {
+
+    PS_ASSERT_INT_POSITIVE(binning->nXbin, NAN);
+    PS_ASSERT_INT_POSITIVE(binning->nYbin, NAN);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(binning->nXskip, binning->nXbin, NAN);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(binning->nYskip, binning->nYbin, NAN);
+
+    double yRuff = (yFine - binning->nYskip)/binning->nYbin;
+    return yRuff;
+}
+
+// convert the ruff coordinate to the fine coordinate
+double psImageBinningGetFineX (const psImageBinning *binning, const double xRuff) {
+
+    PS_ASSERT_INT_POSITIVE(binning->nXbin, NAN);
+    PS_ASSERT_INT_POSITIVE(binning->nYbin, NAN);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(binning->nXskip, binning->nXbin, NAN);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(binning->nYskip, binning->nYbin, NAN);
+
+    double xFine = xRuff * binning->nXbin + binning->nXskip;
+    return xFine;
+}
+double psImageBinningGetFineY (const psImageBinning *binning, const double yRuff) {
+
+    PS_ASSERT_INT_POSITIVE(binning->nXbin, NAN);
+    PS_ASSERT_INT_POSITIVE(binning->nYbin, NAN);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(binning->nXskip, binning->nXbin, NAN);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(binning->nYskip, binning->nYbin, NAN);
+
+    double yFine = yRuff * binning->nYbin + binning->nYskip;
+    return yFine;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBinning.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBinning.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageBinning.h	(revision 22322)
@@ -0,0 +1,62 @@
+/** @file  psImageBinning.c
+ *
+ *  @brief Functions to define the binning strategy and to perform image binning / unbinning
+ *  (resampling).
+ *
+ *  @ingroup Image
+ *
+ *  @author Eugene Magnier, IfA
+ *
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-10-25 20:35:50 $
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_BINNING_H
+#define PS_IMAGE_BINNING_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+// description of the image binning relationship between a binned and an unbinned
+// image. binning is defined for a specific input size and output image size.
+typedef struct {
+    int nXfine; 			// width of the hi-res image
+    int nYfine;				// height of the hi-res image
+    int nXruff; 			// width of the lo-res image 
+    int nYruff;				// height of the lo-res image
+    int nXbin;				// X binning factor (~ nXfine/nXruff)
+    int nYbin;				// Y binning factor (~ nYfine/nYruff)
+    int nXoff;				// offset in fine pixels to start of ruff 0 pixel (x_fine - nXoff) / nXbin = x_ruff
+    int nYoff;				// offset in fine pixels to start of ruff 0 pixel (x_fine - nXoff) / nXbin = x_ruff
+    int nXskip;				// offset in fine pixels from start of fine parent 0 pixel to ruff 0 pixel (nXskip = col0 - nXoff)
+    int nYskip;				// offset in fine pixels from start of fine parent 0 pixel to ruff 0 pixel (nYskip = row0 - nYoff)
+} psImageBinning;
+
+typedef enum {
+    PS_IMAGE_BINNING_LEFT,
+    PS_IMAGE_BINNING_CENTER,
+    PS_IMAGE_BINNING_RIGHT,
+} psImageBinningAlign;
+
+
+psImageBinning *psImageBinningAlloc() PS_ATTR_MALLOC;
+bool psMemCheckBinning(psPtr ptr);
+
+void psImageBinningSetRuffSize(psImageBinning *binning, psImageBinningAlign align);
+void psImageBinningSetFineSize(psImageBinning *binning, psImageBinningAlign align);
+void psImageBinningSetScale(psImageBinning *binning, psImageBinningAlign align);
+void psImageBinningSetSkip(psImageBinning *binning, psImage *image);
+void psImageBinningSetSkipByOffset(psImageBinning *binning, int col0, int row0);
+
+psRegion psImageBinningSetFineRegion (psImageBinning *binning, psRegion ruffRegion);
+psRegion psImageBinningSetRuffRegion (psImageBinning *binning, psRegion fineRegion);
+
+double psImageBinningGetRuffX (const psImageBinning *binning, const double xFine);
+double psImageBinningGetRuffY (const psImageBinning *binning, const double yFine);
+double psImageBinningGetFineX (const psImageBinning *binning, const double xRuff);
+double psImageBinningGetFineY (const psImageBinning *binning, const double yRuff);
+
+/// @}
+#endif // #ifndef PS_IMAGE_GEOM_MANIP_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageConvolve.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageConvolve.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageConvolve.c	(revision 22322)
@@ -0,0 +1,1084 @@
+/// @file  psImageConvolve.c
+///
+/// @brief Contains FFT transform related functions for psImage.
+///
+/// @author Robert DeSonia, MHPCC
+/// @author Paul Price, IfA
+/// @author Eugene Magnier, IfA
+///
+/// @version $Revision: 1.76 $ $Name: not supported by cvs2svn $
+/// @date $Date: 2008-08-30 02:24:21 $
+///
+/// Copyright 2004-2007 Institute for Astronomy, University of Hawaii
+///
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <math.h>
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psAssert.h"
+#include "psScalar.h"
+#include "psBinaryOp.h"
+#include "psImageFFT.h"
+#include "psImageStructManip.h"
+#include "psImagePixelManip.h"
+#include "psTrace.h"
+#include "psThread.h"
+
+#include "psImageConvolve.h"
+
+static bool threaded = false;           // Run image convolution threaded?
+
+
+
+
+static void kernelFree(psKernel *kernel)
+{
+    if (kernel) {
+        psFree(kernel->image);
+        psFree(kernel->p_kernelRows);
+    }
+    return;
+}
+
+// Set up indirections, so we can refer to kernel->kernel[-1][-3] for the (-1,-1) element, instead of
+// kernel->image[kernel->yMax - kernel->yMin + 1][kernel->yMax - kernel->yMin + 3] (yuk!).
+static void kernelRedirects(psKernel *kernel, // Kernel for which to set up the redirects
+                            int numRows // Number of rows
+    )
+{
+    int xMin = kernel->xMin, yMin = kernel->yMin; // Minimum values
+
+    kernel->p_kernelRows = psAlloc(sizeof(float*)*numRows);
+    for (int i = 0; i < numRows; i++) {
+        kernel->p_kernelRows[i] = kernel->image->data.PS_TYPE_KERNEL_DATA[i] - xMin;
+    }
+    kernel->kernel = kernel->p_kernelRows - yMin;
+    return;
+}
+
+psKernel *psKernelAlloc(int xMin, int xMax, int yMin, int yMax)
+{
+    // Check the inputs to make sure max > min; if not, switch.
+    // following is explicitly spelled out in the SDRS as a requirement
+    if (yMin > yMax) {
+        psWarning("Specified yMin, %d, was greater than yMax, %d.  Values swapped.",
+                 yMin, yMax);
+
+        int temp = yMin;
+        yMin = yMax;
+        yMax = temp;
+    }
+    if (xMin > xMax) {
+        psWarning("Specified xMin, %d, was greater than xMax, %d.  Values swapped.",
+                 xMin, xMax);
+
+        int temp = xMin;
+        xMin = xMax;
+        xMax = temp;
+    }
+
+    int numRows = yMax - yMin + 1;      // Number of rows for kernel image
+    int numCols = xMax - xMin + 1;      // Number of columns for kernel image
+
+    psKernel *kernel = psAlloc(sizeof(psKernel)); // The kernel, to be returned
+    psMemSetDeallocator(kernel,(psFreeFunc)kernelFree);
+
+    kernel->xMin = xMin;
+    kernel->xMax = xMax;
+    kernel->yMin = yMin;
+    kernel->yMax = yMax;
+    kernel->image = psImageAlloc(numCols, numRows, PS_TYPE_KERNEL);
+    psImageInit(kernel->image, 0.0);
+
+    kernelRedirects(kernel, numRows);
+
+    return kernel;
+}
+
+psKernel *psKernelAllocFromImage(psImage *image, int x0, int y0)
+{
+    psKernel *kernel = psAlloc(sizeof(psKernel)); // The kernel, to be returned
+    psMemSetDeallocator(kernel,(psFreeFunc)kernelFree);
+
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+
+    kernel->xMin = - x0;
+    kernel->xMax = numCols - 1 - x0;
+    kernel->yMin = - y0;
+    kernel->yMax = numRows - 1 - y0;
+    kernel->image = psMemIncrRefCounter(image);
+
+    kernelRedirects(kernel, numRows);
+
+    return kernel;
+}
+
+bool psMemCheckKernel(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)kernelFree );
+}
+
+
+psKernel *psKernelGenerate(const psVector *tShifts,
+                           const psVector *xShifts,
+                           const psVector *yShifts,
+                           float totalTime,
+                           bool xyRelative)
+{
+    PS_ASSERT_VECTOR_NON_NULL(tShifts, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(xShifts, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(yShifts, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(tShifts, xShifts, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(tShifts, yShifts, NULL);
+    PS_ASSERT_VECTOR_TYPE(tShifts, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_TYPE(xShifts, PS_TYPE_S32, NULL);
+    PS_ASSERT_VECTOR_TYPE(yShifts, PS_TYPE_S32, NULL);
+
+    if (isnan(totalTime)) {
+        // It's more expensive to check for NAN than 0.0
+        totalTime = 0.0;
+    }
+
+    // If there are no shifts, the kernel is just a 1 at 0,0
+    long num = tShifts->n;              // Number of shifts
+    if (num == 0) {
+        psKernel *kernel = psKernelAlloc(0,0,0,0);
+        kernel->kernel[0][0] = 1;
+        return kernel;
+    }
+
+    // Get dimensions and scaling
+    int xMin, xMax, yMin, yMax;         // Range of values for kernel
+    int xLast, yLast;                   // Last location, for relative shifts
+    float tSum = tShifts->data.F32[0];   // Sum of the times
+    xLast = xMin = xMax = xShifts->data.S32[0];
+    yLast = yMin = yMax = yShifts->data.S32[0];
+    int x0, y0;                         // Final location; everything is relative to this
+    x0 = xShifts->data.S32[num - 1];
+    y0 = yShifts->data.S32[num - 1];
+    for (long i = 1; i < num; i++) {
+        int x = xShifts->data.S32[i] - x0; // x position in kernel
+        int y = yShifts->data.S32[i] - y0; // y position in kernel
+        if (xyRelative) {
+            x += xLast;
+            y += yLast;
+            xLast = x;
+            yLast = y;
+        }
+        if (x < xMin) {
+            xMin = x;
+        }
+        if (x > xMax) {
+            xMax = x;
+        }
+        if (y < yMin) {
+            yMin = y;
+        }
+        if (y > yMax) {
+            yMax = y;
+        }
+
+        if (totalTime <= 0) {
+            tSum += tShifts->data.F32[i];
+        }
+    }
+
+    psTrace("psLib.imageops", 5, "Kernel range: %d:%d,%d:%d\n", xMin, xMax, yMin, yMax);
+
+    if (totalTime > 0) {
+        // Then the total time is simply the final value
+        // NB: We assume the counter starts at zero!
+        tSum = totalTime;
+    }
+
+    // One more pass through to set the kernel
+    psKernel *kernel = psKernelAlloc(xMin, xMax, yMin, yMax); // The kernel
+    xLast = xShifts->data.S32[0];
+    yLast = yShifts->data.S32[0];
+    float tLast = 0.0;                  // Last value for t
+    for (int i = 0; i < num; i++) {
+        int x = xShifts->data.S32[i] - x0; // x position in kernel
+        int y = yShifts->data.S32[i] - y0; // y position in kernel
+        if (xyRelative) {
+            x += xLast;
+            y += yLast;
+            xLast = x;
+            yLast = y;
+        }
+        float t = tShifts->data.F32[i];
+        if (totalTime > 0) {
+            t -= tLast;
+            tLast = tShifts->data.F32[i];
+        }
+
+        kernel->kernel[y][x] += t;
+    }
+
+    // Normalise the kernel by the total time (kernel sum should be unity)
+    psBinaryOp(kernel->image, kernel->image, "*", psScalarAlloc(1.0 / tSum, PS_TYPE_F32));
+
+    return kernel;
+}
+
+psImage *psImageConvolveDirect(psImage *out,
+                               const psImage *in,
+                               const psKernel *kernel)
+{
+    PS_ASSERT_IMAGE_NON_NULL(in, NULL);
+    if (in->type.type != PS_TYPE_S32 && in->type.type != PS_TYPE_F32 && in->type.type != PS_TYPE_F64) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Unallowable operation: psImage %s is not of type S32 or F{32,64}.", "in");
+        return(NULL);
+    }
+    PS_ASSERT_PTR_NON_NULL(kernel, NULL);
+    PS_ASSERT_PTR_NON_NULL(kernel->kernel, NULL);
+
+    // Pull out kernel parameters, for convenience
+    int xMin = kernel->xMin;
+    int xMax = kernel->xMax;
+    int yMin = kernel->yMin;
+    int yMax = kernel->yMax;
+    float **kernelData = kernel->kernel;
+
+    int numRows = in->numRows;          // Number of rows
+    int numCols = in->numCols;          // Number of columns
+
+#if 1
+
+    // This is the usual way of doing the convolution, but it wastes time if there's a large number of zero
+    // pixels in the kernel.
+#define SPATIAL_CONVOLVE_CASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+        ps##TYPE **inData = in->data.TYPE; /* Dereference input data */ \
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_##TYPE); \
+        for (int row = 0; row < numRows; row++) { \
+            ps##TYPE *outRow = out->data.TYPE[row]; \
+            int kRowMin = PS_MAX(yMin, row + 1 - numRows); \
+            int kRowMax = PS_MIN(yMax, row); \
+            for (int col = 0; col < numCols; col++) { \
+                int kColMin = PS_MAX(xMin, col + 1 - numCols); \
+                int kColMax = PS_MIN(xMax, col); \
+                ps##TYPE pixel = 0.0; \
+                for (int kRow = kRowMin; kRow <= kRowMax; kRow++) { \
+                    for (int kCol = kColMin; kCol <= kColMax; kCol++) { \
+                        pixel += kernelData[kRow][kCol] * inData[row - kRow][col - kCol]; \
+                    } \
+                } \
+                outRow[col] = pixel; \
+            } \
+        } \
+    } \
+    break;
+
+#else
+
+    // This turns the convolution inside-out, allowing us to skip kernel pixels that have no contribution.
+#define SPATIAL_CONVOLVE_CASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+        ps##TYPE **inData = in->data.TYPE; /* Dereference input data */ \
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_##TYPE); \
+        psImageInit(out, 0.0); \
+        for (int ky = yMin; ky <= yMax; ky++) { \
+            for (int kx = xMin; kx <= xMax; kx++) { \
+                float kValue = kernelData[ky][kx]; /* Kernel value */ \
+                if (kValue == 0.0) { \
+                    continue; \
+                } \
+                for (int y = PS_MAX(ky, 0); y < PS_MIN(numRows, numRows + ky); y++) { \
+                    for (int x = PS_MAX(kx, 0); x < PS_MIN(numCols, numCols + kx); x++) { \
+                        out->data.TYPE[y][x] += kValue * inData[y - ky][x - kx]; \
+                    } \
+                } \
+            } \
+        } \
+    } \
+    break;
+
+#endif
+
+    switch (in->type.type) {
+        SPATIAL_CONVOLVE_CASE(S32);
+        SPATIAL_CONVOLVE_CASE(F32);
+        SPATIAL_CONVOLVE_CASE(F64);
+      default:
+        psAbort("Should never get here: bad type that was asserted on previously.");
+    }
+
+    return out;
+}
+
+psImage *psImageConvolveMaskDirect(psImage *out, const psImage *mask, psMaskType maskVal,
+                                   psMaskType setVal, int xMin, int xMax, int yMin, int yMax)
+{
+    PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
+    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, NULL);
+    if (out == mask && ((maskVal & setVal) || !setVal)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't convolve mask in-place if values to set contains values to convolve.");
+        return NULL;
+    }
+
+    if (yMin > yMax) {
+        psWarning("Specified yMin, %d, was greater than yMax, %d.  Values swapped.",
+                 yMin, yMax);
+
+        int temp = yMin;
+        yMin = yMax;
+        yMax = temp;
+    }
+    if (xMin > xMax) {
+        psWarning("Specified xMin, %d, was greater than xMax, %d.  Values swapped.",
+                 xMin, xMax);
+
+        int temp = xMin;
+        xMin = xMax;
+        xMax = temp;
+    }
+
+    int numRows = mask->numRows;        // Number of rows
+    int numCols = mask->numCols;        // Number of columns
+
+    if (!out) {
+        // Propagate the non-masked values
+        out = (psImage*)psBinaryOp(NULL, (const psPtr)mask, "&", psScalarAlloc(~maskVal, PS_TYPE_MASK));
+    }
+
+    // Dereference mask images
+    psMaskType **maskData = mask->data.PS_TYPE_MASK_DATA;
+    psMaskType **outData = out->data.PS_TYPE_MASK_DATA;
+
+    if (setVal) {
+        // Grow any pixels matching maskVal, setting setVal
+        for (int row = 0; row < numRows; row++) {
+            for (int col = 0; col < numCols; col++) {
+                if (maskData[row][col] & maskVal) {
+                    for (int kRow = PS_MAX(yMin, -row); kRow <= PS_MIN(yMax, numRows - row - 1); kRow++) {
+                        for (int kCol = PS_MAX(xMin, -col); kCol <= PS_MIN(xMax, numCols - col - 1); kCol++) {
+                            outData[row + kRow][col + kCol] |= setVal;
+                        }
+                    }
+                }
+            }
+        }
+    } else {
+        // Each pixel receives any maskVal bits set within the convolution window
+        for (int row = 0; row < numRows; row++) {
+            for (int col = 0; col < numCols; col++) {
+                psMaskType pixel = outData[row][col]; // Pixel value to set
+                if (pixel & maskVal) {
+                    // Already done this one
+                    continue;
+                }
+                for (int kRow = PS_MAX(yMin, -row); kRow <= PS_MIN(yMax, numRows - row - 1); kRow++) {
+                    for (int kCol = PS_MAX(xMin, -col); kCol <= PS_MIN(xMax, numCols - col - 1); kCol++) {
+                        pixel |= maskData[row][col] & maskVal;
+                    }
+                }
+                outData[row][col] = pixel;
+            }
+        }
+    }
+
+    return out;
+}
+
+
+psImage *psImageConvolveMaskFFT(psImage *out, const psImage *mask, psMaskType maskVal,
+                                psMaskType setVal, int xMin, int xMax, int yMin, int yMax, float thresh)
+{
+    PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
+    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN(thresh, 0.0, NULL);
+    PS_ASSERT_FLOAT_LESS_THAN(thresh, 1.0, NULL);
+    if (out == mask && (maskVal & setVal)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't convolve mask in-place if values to set contains values to convolve.");
+        return NULL;
+    }
+
+    if (yMin > yMax) {
+        psWarning("Specified yMin, %d, was greater than yMax, %d.  Values swapped.",
+                 yMin, yMax);
+
+        int temp = yMin;
+        yMin = yMax;
+        yMax = temp;
+    }
+    if (xMin > xMax) {
+        psWarning("Specified xMin, %d, was greater than xMax, %d.  Values swapped.",
+                 xMin, xMax);
+
+        int temp = xMin;
+        xMin = xMax;
+        xMax = temp;
+    }
+
+    int numRows = mask->numRows, numCols = mask->numCols; // Size of image
+
+    psImage *onoff = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Pixels on or off
+    psImageInit(onoff, 0);
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            if (mask->data.PS_TYPE_MASK_DATA[y][x] & maskVal) {
+                onoff->data.F32[y][x] = 1.0;
+            }
+        }
+    }
+
+    psKernel *kernel = psKernelAlloc(xMin, xMax, yMin, yMax);
+    psImageInit(kernel->image, 1.0);
+    psImage *convolved = psImageConvolveFFT(NULL, onoff, NULL, 0, kernel);
+    psFree(onoff);
+    psFree(kernel);
+    if (!convolved) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to convolve mask.");
+        return NULL;
+    }
+
+    if (!setVal) {
+        setVal = maskVal;
+    }
+
+    if (!out) {
+        out = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+    }
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            out->data.PS_TYPE_MASK_DATA[y][x] = (convolved->data.F32[y][x] >= thresh) ?
+                (mask->data.PS_TYPE_MASK_DATA[y][x] | setVal) : mask->data.PS_TYPE_MASK_DATA[y][x];
+        }
+    }
+
+    psFree(convolved);
+
+    return out;
+}
+
+bool psImageSmooth (psImage *image,
+                    double  sigma,
+                    double  Nsigma)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+
+    // relevant terms
+    int Nrange = sigma*Nsigma + 0.5;    // Number of pixels either side for convolution kernel
+    int Npixel = 2*Nrange + 1;          // Total number of pixels in convolution kernel
+    int Nx = image->numCols;            // Number of columns
+    int Ny = image->numRows;            // Number of rows
+
+    #define IMAGESMOOTH_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        /* generate normalized gaussian */ \
+        psVector *gaussnorm = psVectorAlloc(Npixel, PS_TYPE_##TYPE); \
+        { \
+            double sum = 0.0; \
+            double factor = -0.5/(sigma*sigma); \
+            for (int i = -Nrange; i < Nrange + 1; i++) { \
+                gaussnorm->data.TYPE[i+Nrange] = exp(factor*i*i); \
+                sum += gaussnorm->data.TYPE[i+Nrange]; \
+            } \
+            for (int i = -Nrange; i < Nrange + 1; i++) { \
+                gaussnorm->data.TYPE[i+Nrange] /= sum; \
+            } \
+        } \
+        ps##TYPE *gauss = &gaussnorm->data.TYPE[Nrange]; \
+        \
+        /* Smooth in X direction */ \
+        { \
+            psVector *calculation = psVectorAlloc(Nx, PS_TYPE_##TYPE); \
+            for (int j = 0; j < Ny; j++) { \
+                ps##TYPE *vi = image->data.TYPE[j]; \
+                ps##TYPE *vo = calculation->data.TYPE; \
+                int xMax = PS_MIN(Nrange, Nx); \
+                int convRange = PS_MIN(Nrange + 1, Nx); \
+                /* Smooth first Nrange pixels, with renorm */ \
+                for (int i = 0; i < xMax; i++, vi++, vo++) { \
+                    ps##TYPE *vr = vi - i; \
+                    ps##TYPE *vg = gauss - i; \
+                    double g = 0.0; \
+                    double s = 0.0; \
+                    for (int n = -i; n < convRange; n++, vr++, vg++) { \
+                        s += *vg * *vr; \
+                        g += *vg; \
+                    } \
+                    *vo = s / g; \
+                } \
+                /* If that's all the pixels we have, then we're done already */ \
+                if (Nx > Nrange) { \
+                    /* Smooth middle pixels */ \
+                    for (int i = Nrange; i < Nx - Nrange; i++, vi++, vo++) { \
+                        ps##TYPE *vr = vi - Nrange; \
+                        ps##TYPE *vg = gauss - Nrange; \
+                        double s = 0; \
+                        for (int n = -Nrange; n < Nrange + 1; n++, vr++, vg++) { \
+                            s += *vg * *vr; \
+                        } \
+                        *vo = s; \
+                    } \
+                    /* Smooth last Nrange pixels, with renorm */ \
+                    /* XXX does this miss the last column? */ \
+                    for (int i = Nx - Nrange; i < Nx; i++, vi++, vo++) { \
+                        ps##TYPE *vr = vi - Nrange; \
+                        ps##TYPE *vg = gauss - Nrange; \
+                        double g = 0.0; \
+                        double s = 0.0; \
+                        for (int n = -Nrange; n < Nx - i; n++, vr++, vg++) { \
+                            s += *vg * *vr; \
+                            g += *vg; \
+                        } \
+                        *vo = s / g; \
+                    } \
+                } \
+                memcpy(image->data.TYPE[j], calculation->data.TYPE, Nx*sizeof(ps##TYPE)); \
+            } \
+            psFree(calculation); \
+        } \
+        \
+        /* Smooth in Y direction */ \
+        psArray *rows = psArrayAlloc(Nrange); \
+        /* Smooth the first Nrange pixels, with renorm */ \
+        int yMax = PS_MIN(Nrange, Ny); \
+        int convRange = PS_MIN(Nrange + 1, Ny); \
+        for (int j = 0; j < yMax; j++) { \
+            psVector *calculation = psVectorAlloc(Nx, PS_TYPE_##TYPE); \
+            /* Zero the output row */ \
+            memset(calculation->data.TYPE, 0, Nx*sizeof(ps##TYPE)); \
+            double sum = 0.0; \
+            for (int n = -j; n < convRange; n++) { sum += gauss[n]; } \
+            for (int n = -j; n < convRange; n++) { \
+                ps##TYPE *vi = image->data.TYPE[j+n]; \
+                ps##TYPE *vo = calculation->data.TYPE; \
+                double g = gauss[n] / sum; \
+                for (int i = 0; i < Nx; i++, vi++, vo++) { \
+                    *vo += *vi * g; \
+                } \
+            } \
+            /* Save output rows on temp array of rows */ \
+            rows->data[j] = calculation; \
+        } \
+        if (Ny < Nrange) { \
+            /* Need to save the first bit, then we're done */ \
+            for (int j = 0; j < Ny; j++) {  \
+                psVector *save = rows->data[j]; \
+                memcpy(image->data.TYPE[j], save->data.TYPE, Nx*sizeof(ps##TYPE)); \
+            } \
+        } else { \
+            /* Smooth middle pixels */ \
+            psVector *calculation = psVectorAlloc(Nx, PS_TYPE_##TYPE); \
+            for (int j = Nrange; j < Ny - Nrange; j++) { \
+                memset(calculation->data.TYPE, 0, Nx*sizeof(ps##TYPE)); \
+                for (int n = -Nrange; n < Nrange + 1; n++) { \
+                    ps##TYPE *vi = image->data.TYPE[j+n]; \
+                    ps##TYPE *vo = calculation->data.TYPE; \
+                    double g = gauss[n]; \
+                    for (int i = 0; i < Nx; i++, vi++, vo++) { \
+                        *vo += *vi * g; \
+                    } \
+                } \
+                /* Write the output row */ \
+                int Nr = j % Nrange; \
+                psVector *save = rows->data[Nr]; \
+                memcpy(image->data.TYPE[j-Nrange], save->data.TYPE, Nx*sizeof(ps##TYPE)); \
+                /* Juggle the pointers, so that next run we use the one we just wrote */ \
+                rows->data[Nr] = calculation; \
+                calculation = save; \
+            } \
+            /* Smooth last Nrange pixels, with renorm */ \
+            for (int j = Ny - Nrange; j < Ny; j++) { \
+                /* save the Nrange-offset output row, then zero */ \
+                memset(calculation->data.TYPE, 0, Nx*sizeof(ps##TYPE)); \
+                double sum = 0.0; \
+                for (int n = -Nrange; n < Ny - j; n++) { sum += gauss[n]; } \
+                for (int n = -Nrange; n < Ny - j; n++) { \
+                    ps##TYPE *vi = image->data.TYPE[j+n]; \
+                    ps##TYPE *vo = calculation->data.TYPE; \
+                    double g = gauss[n] / sum; \
+                    for (int i = 0; i < Nx; i++, vi++, vo++) { \
+                        *vo += *vi * g; \
+                    } \
+                } \
+                /* Write the output row */ \
+                int Nr = j % Nrange; \
+                psVector *save = rows->data[Nr]; \
+                memcpy(image->data.TYPE[j-Nrange], save->data.TYPE, Nx*sizeof(ps##TYPE)); \
+                /* Juggle the pointers, so that next run we use the one we just wrote */ \
+                rows->data[Nr] = calculation; \
+                calculation = save; \
+            } \
+            psFree(calculation); \
+            /* Write the remaining rows */ \
+            for (int j = Ny; j < Ny + Nrange; j++) {  \
+                int Nr = j % Nrange; \
+                psVector *save = rows->data[Nr]; \
+                memcpy(image->data.TYPE[j-Nrange], save->data.TYPE, Nx*sizeof(ps##TYPE)); \
+            } \
+        } \
+        psFree(rows); \
+        psFree(gaussnorm); \
+        break; \
+    }
+
+    switch (image->type.type) {
+        IMAGESMOOTH_CASE(F32);
+        IMAGESMOOTH_CASE(F64);
+    default: {
+            char *typeStr;
+            PS_TYPE_NAME(typeStr,image->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified psImage type, %s, is not supported."),
+                    typeStr);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool psImageSmoothMaskF32 (psImage *image,
+                           psImage *mask,
+                           psMaskType maskVal,
+                           double  sigma,
+                           double  Nsigma)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
+
+    // relevant terms
+    int Nrange = sigma*Nsigma + 0.5;    // Number of pixels either side for convolution kernel
+    int Npixel = 2*Nrange + 1;          // Total number of pixels in convolution kernel
+    int Nx = image->numCols;            // Number of columns
+    int Ny = image->numRows;            // Number of rows
+
+    /* generate normalized gaussian */
+    psVector *gaussnorm = psVectorAlloc(Npixel, PS_TYPE_F32);
+    {
+        double sum = 0.0;
+        double factor = -0.5/(sigma*sigma);
+        for (int i = -Nrange; i < Nrange + 1; i++) {
+            gaussnorm->data.F32[i+Nrange] = exp(factor*i*i);
+            sum += gaussnorm->data.F32[i+Nrange];
+        }
+        for (int i = -Nrange; i < Nrange + 1; i++) {
+            gaussnorm->data.F32[i+Nrange] /= sum;
+        }
+    }
+    psF32 *gauss = &gaussnorm->data.F32[Nrange];
+
+    /** Smooth in X direction **/
+    psVector *calculation = psVectorAlloc(Nx, PS_TYPE_F32);
+    for (int j = 0; j < Ny; j++) {
+        psU8  *vm = mask->data.U8[j];
+        psF32 *vi = image->data.F32[j];
+        psF32 *vo = calculation->data.F32;
+        // loop over all pixels in the row
+        for (int i = 0; i < Nx; i++, vi++, vo++, vm++) {
+            int offset = PS_MIN (i, Nrange);
+            psU8  *sm = vm - offset;
+            psF32 *si = vi - offset;
+            psF32 *sg = gauss - offset;
+            double g = 0.0;
+            double s = 0.0;
+            // loop over all valid pixels in the smoothing kernel
+            int xMin = PS_MAX (i - Nrange, 0);
+            int xMax = PS_MIN (i + Nrange + 1, Nx);
+            for (int n = xMin; n < xMax; n++, sm++, si++, sg++) {
+                if (*sm & maskVal)
+                    continue;
+                s += *sg * *si;
+                g += *sg;
+            }
+            *vo = (g > 0.25) ? s / g : NAN;
+        }
+        memcpy(image->data.F32[j], calculation->data.F32, Nx*sizeof(psF32));
+    }
+    psFree(calculation);
+
+    // XXX test
+    if (0) {
+        psFree(gaussnorm);
+        return true;
+    }
+
+    /** Smooth in Y direction  **/
+    // allocate and save Nrange extra row vectors for storage. these will be
+    // written over the output rows only after we are Nrange rows beyond them
+    int Nsave = Nrange + 1;
+    psArray *rows = psArrayAlloc(Nsave);
+    for (int i = 0; i < Nsave; i++) {
+        rows->data[i] = psVectorAlloc(Nx, PS_TYPE_F32);
+    }
+
+    psVector *outsum = psVectorAlloc(Nx, PS_TYPE_F32);
+    for (int j = 0; j < Ny; j++) {
+        psVector *output = rows->data[j % Nsave];
+        memset (output->data.F32, 0, Nx*sizeof(psF32));
+        memset (outsum->data.F32, 0, Nx*sizeof(psF32));
+        int yMin = PS_MAX (j - Nrange, 0);
+        int yMax = PS_MIN (j + Nrange + 1, Ny);
+        for (int n = yMin; n < yMax; n++) {
+            psU8  *vm = mask->data.U8[n];
+            psF32 *vi = image->data.F32[n];
+            psF32 *vo = output->data.F32;
+            psF32 *vs = outsum->data.F32;
+            double g = gauss[n - j];
+            for (int i = 0; i < Nx; i++, vi++, vo++, vm++, vs++) {
+                if (*vm & maskVal) continue;
+                if (!isfinite(*vi)) continue;
+                *vo += *vi * g;
+                *vs += g;
+            }
+        }
+        // renormalize the row
+        psF32 *vo = output->data.F32;
+        psF32 *vs = outsum->data.F32;
+        for (int i = 0; i < Nx; i++, vo++, vs++) {
+            *vo = (*vs > 0.25) ? *vo / *vs : NAN;
+        }
+
+        // Write the output row
+        if (j - Nrange >= 0) {
+            int Nout = (j - Nrange) % Nsave;
+            psVector *save = rows->data[Nout];
+            memcpy(image->data.F32[j-Nrange], save->data.F32, Nx*sizeof(psF32));
+        }
+    }
+
+    // Write the remaining output rows
+    for (int j = PS_MAX(0, Ny - Nrange); j < Ny; j++) {
+        psVector *save = rows->data[j % Nsave];
+        memcpy(image->data.F32[j], save->data.F32, Nx*sizeof(psF32));
+    }
+    psFree(rows);
+    psFree(outsum);
+    psFree(gaussnorm);
+    return true;
+}
+
+
+// Convolve mask columns
+static bool imageConvolveMaskColumns(psImage *target, // Output, convolved image
+                                     const psImage *input, // Input image
+                                     int start, int stop, // Range of rows
+                                     psMaskType maskVal, // Value to mask; NOTE subtle difference!
+                                     int xMin, int xMax // Range in x for kernel
+                                     )
+{
+    // Dereference mask images
+    psMaskType **inputData = input->data.PS_TYPE_MASK_DATA;
+    psMaskType **targetData = target->data.PS_TYPE_MASK_DATA;
+
+    int numCols = input->numCols;       // Number of columns
+
+    for (int y = start; y < stop; y++) {
+        int min = 0, max = 0;           // Minimum and maximum points to mask
+        bool masking = false;           // Currently masking?
+        for (int x = 0; x < numCols; x++) {
+            if (inputData[y][x] & maskVal) {
+                if (!masking) {
+                    masking = true;
+                    min = x + xMin;
+                    max = x + xMax;
+                } else {
+                    max++;
+                }
+            } else if (masking) {
+                // Do the masking
+                masking = false;
+                min = PS_MAX(0, min);
+                max = PS_MIN(numCols - 1, max);
+                memset(&targetData[y][min], 0xff, (max - min + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_MASK));
+            }
+        }
+        if (masking) {
+            // Mask from the minimum to the end of the row
+            min = PS_MAX(0, min);
+            memset(&targetData[y][min], 0xff, (numCols - min) * PSELEMTYPE_SIZEOF(PS_TYPE_MASK));
+        }
+    }
+    return true;
+}
+
+static bool imageConvolveMaskRows(psImage *target, // Output, convolved image
+                                     const psImage *input, // Input image
+                                     int start, int stop, // Range of rows
+                                     psMaskType setVal, // Value to set; NOTE subtle difference!
+                                     int yMin, int yMax // Range in y for kernel
+                                     )
+{
+    // Dereference mask images
+    psMaskType **inputData = input->data.PS_TYPE_MASK_DATA;
+    psMaskType **targetData = target->data.PS_TYPE_MASK_DATA;
+
+    int numRows = input->numRows;       // Number of rows
+
+    for (int x = start; x < stop; x++) {
+        int min = 0, max = 0;           // Minimum and maximum points to mask
+        bool masking = false;           // Currently masking?
+        for (int y = 0; y < numRows; y++) {
+            if (inputData[y][x]) {
+                if (!masking) {
+                    masking = true;
+                    min = y + yMin;
+                    max = y + yMax;
+                } else {
+                    max++;
+                }
+            } else if (masking) {
+                // Do the masking
+                masking = false;
+                min = PS_MAX(0, min);
+                max = PS_MIN(numRows - 1, max);
+                for (int i = min; i <= max; i++) {
+                    targetData[i][x] |= setVal;
+                }
+            }
+        }
+        if (masking) {
+            // Mask from the minimum to the end of the column
+            for (int i = PS_MAX(0, min); i < numRows; i++) {
+                targetData[i][x] |= setVal;
+            }
+        }
+    }
+    return true;
+}
+
+static bool imageConvolveMaskThread(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    psArray *args = job->args;          // Arguments for job
+
+    psImage *target = args->data[0];       // Output mask image
+    const psImage *input = args->data[1];// Input mask image
+    int start = PS_SCALAR_VALUE(args->data[2], S32); // Row/col to start at
+    int stop = PS_SCALAR_VALUE(args->data[3], S32); // Row/col to stop at
+    psMaskType maskVal = PS_SCALAR_VALUE(args->data[4], U8); // Value to mask/set
+    int kernelMin = PS_SCALAR_VALUE(args->data[5], S32); // Minimum range for kernel
+    int kernelMax = PS_SCALAR_VALUE(args->data[6], S32); // Maximum range for kernel
+    bool row = PS_SCALAR_VALUE(args->data[7], U8); // Do row (true) or column (false)?
+
+    return row ? imageConvolveMaskRows(target, input, start, stop, maskVal, kernelMin, kernelMax) :
+        imageConvolveMaskColumns(target, input, start, stop, maskVal, kernelMin, kernelMax);
+}
+
+psImage *psImageConvolveMask(psImage *out, const psImage *mask, psMaskType maskVal,
+                             psMaskType setVal, int xMin, int xMax, int yMin, int yMax)
+{
+    PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
+    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, NULL);
+    if (out) {
+        PS_ASSERT_IMAGE_NON_NULL(out, NULL);
+        PS_ASSERT_IMAGE_TYPE(out, PS_TYPE_MASK, NULL);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(out, mask, NULL);
+        if (out == mask && ((maskVal & setVal) || !setVal)) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Can't convolve mask in-place if values to set contains values to convolve.");
+            return NULL;
+        }
+    }
+
+    if (yMin > yMax) {
+        psWarning("Specified yMin, %d, was greater than yMax, %d.  Values swapped.",
+                 yMin, yMax);
+
+        int temp = yMin;
+        yMin = yMax;
+        yMax = temp;
+    }
+    if (xMin > xMax) {
+        psWarning("Specified xMin, %d, was greater than xMax, %d.  Values swapped.",
+                 xMin, xMax);
+
+        int temp = xMin;
+        xMin = xMax;
+        xMax = temp;
+    }
+
+    int numRows = mask->numRows;        // Number of rows
+    int numCols = mask->numCols;        // Number of columns
+
+    // Propagate the non-masked values
+    out = (psImage*)psBinaryOp(out, (const psPtr)mask, "&", psScalarAlloc(~setVal, PS_TYPE_MASK));
+
+    if (!setVal) {
+        setVal = maskVal;
+    }
+
+    psImage *conv = psImageAlloc(numCols, numRows, PS_TYPE_MASK); // Temporary convolved image
+    psImageInit(conv, 0);
+
+    // Since we're just masking everything inside a square, it's separable
+
+    // Rows
+    if (threaded) {
+        int numThreads = psThreadPoolSize(); // Number of threads
+        float cols = (float)numCols / (float)numThreads; // Number of cols to do at once
+        for (int i = 0; i < numThreads; i++) {
+            int start = i * cols;       // Starting colunms
+            int stop = (i + 1) * cols;  // Stopping columns
+
+            psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_CONVOLVE_MASK");
+            psArrayAdd(job->args, 1, conv);
+            psArrayAdd(job->args, 1, (psImage*)mask); // Casting away const to put on arguments
+            PS_ARRAY_ADD_SCALAR(job->args, start, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, stop, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, maskVal, PS_TYPE_MASK);
+            PS_ARRAY_ADD_SCALAR(job->args, xMin, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, xMax, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, 0x00, PS_TYPE_U8);
+            if (!psThreadJobAddPending(job)) {
+                psFree(job);
+                psFree(conv);
+                psFree(out);
+                return NULL;
+            }
+            psFree(job);
+        }
+    } else if (!imageConvolveMaskColumns(conv, mask, 0, numRows, maskVal, xMin, xMax)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to convolve mask columns.");
+        psFree(conv);
+        psFree(out);
+        return NULL;
+    }
+
+    if (threaded && !psThreadPoolWait(true)) {
+        psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
+        psFree(conv);
+        psFree(out);
+        return NULL;
+    }
+
+    // Columns
+    if (threaded) {
+        int numThreads = psThreadPoolSize(); // Number of threads
+        float cols = (float)numCols / (float)numThreads; // Number of columns to do at once
+        for (int i = 0; i < numThreads; i++) {
+            int start = i * cols;       // Starting column
+            int stop = (i + 1) * cols;  // Stopping column
+
+            psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_CONVOLVE_MASK");
+            psArrayAdd(job->args, 1, conv);
+            psArrayAdd(job->args, 1, out);
+            PS_ARRAY_ADD_SCALAR(job->args, start, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, stop, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, setVal, PS_TYPE_MASK);
+            PS_ARRAY_ADD_SCALAR(job->args, xMin, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, xMax, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, 0xff, PS_TYPE_U8);
+            if (!psThreadJobAddPending(job)) {
+                psFree(job);
+                psFree(conv);
+                psFree(out);
+                return NULL;
+            }
+            psFree(job);
+        }
+    } else if (!imageConvolveMaskRows(out, conv, 0, numCols, setVal, yMin, yMax)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to convolve mask columns.");
+        psFree(conv);
+        psFree(out);
+        return NULL;
+    }
+
+    if (threaded && !psThreadPoolWait(true)) {
+        psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
+        psFree(conv);
+        psFree(out);
+        return NULL;
+    }
+
+
+#if 0
+    for (int y = 0; y < numRows; y++) {
+        int min = 0, max = 0;           // Minimum and maximum points to mask
+        bool masking = false;           // Currently masking?
+        for (int x = 0; x < numCols; x++) {
+            if (maskData[y][x] & maskVal) {
+                if (!masking) {
+                    masking = true;
+                    min = x + xMin;
+                    max = x + xMax;
+                } else {
+                    max++;
+                }
+            } else if (masking) {
+                // Do the masking
+                masking = false;
+                min = PS_MAX(0, min);
+                max = PS_MIN(numCols - 1, max);
+                memset(&convData[y][min], 0xff, (max - min + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_MASK));
+            }
+        }
+        if (masking) {
+            // Mask from the minimum to the end of the row
+            min = PS_MAX(0, min);
+            memset(&convData[y][min], 0xff, (numCols - min) * PSELEMTYPE_SIZEOF(PS_TYPE_MASK));
+        }
+    }
+    for (int x = 0; x < numCols; x++) {
+        int min = 0, max = 0;           // Minimum and maximum points to mask
+        bool masking = false;           // Currently masking?
+        for (int y = 0; y < numRows; y++) {
+            if (convData[y][x]) {
+                if (!masking) {
+                    masking = true;
+                    min = y + yMin;
+                    max = y + yMax;
+                } else {
+                    max++;
+                }
+            } else if (masking) {
+                // Do the masking
+                masking = false;
+                min = PS_MAX(0, min);
+                max = PS_MIN(numRows - 1, max);
+                for (int i = min; i <= max; i++) {
+                    outData[i][x] |= setVal;
+                }
+            }
+        }
+        if (masking) {
+            // Mask from the minimum to the end of the column
+            for (int i = PS_MAX(0, min); i < numRows; i++) {
+                outData[i][x] |= setVal;
+            }
+        }
+    }
+#endif
+
+    psFree(conv);
+
+    return out;
+}
+
+
+void psImageConvolveSetThreads(bool set)
+{
+    if (set && !threaded) {
+        {
+            psThreadTask *task = psThreadTaskAlloc("PSLIB_IMAGE_CONVOLVE_MASK", 8);
+            task->function = &imageConvolveMaskThread;
+            psThreadTaskAdd(task);
+            psFree(task);
+        }
+    } else if (!set && threaded) {
+        psThreadTaskRemove("PSLIB_IMAGE_CONVOLVE_MASK");
+    }
+
+    threaded = set;
+
+}
+
+bool psImageConvolveGetThreads(void)
+{
+    return threaded;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageConvolve.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageConvolve.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageConvolve.h	(revision 22322)
@@ -0,0 +1,211 @@
+/* @file  psImageConvolve.h
+ *
+ * @brief image convolution functionality
+ *
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.34 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-12 03:32:56 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_CONVOLVE_H
+#define PS_IMAGE_CONVOLVE_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+#include "psImage.h"
+#include "psVector.h"
+#include "psType.h"
+
+#define PS_TYPE_KERNEL PS_TYPE_F32     ///< the data member to use for kernel image */
+#define PS_TYPE_KERNEL_DATA F32        ///< the data member to use for kernel image */
+#define PS_TYPE_KERNEL_NAME "psF32"    ///< the data type for kernel as a string */
+
+/// A convolution kernel
+typedef struct {
+    psImage *image;                    ///< Kernel data, in the form of an image
+    int xMin;                          ///< Most negative x index
+    int yMin;                          ///< Most negative y index
+    int xMax;                          ///< Most positive x index
+    int yMax;                          ///< Most positive y index
+    float **kernel;                    ///< Pointer to the kernel data
+    float **p_kernelRows;              ///< Pointer to the rows of the kernel data; not intended for user use.
+} psKernel;
+
+#define PS_ASSERT_KERNEL_NON_NULL(KERNEL, RETURNVALUE) \
+    if ((KERNEL) == NULL || (KERNEL)->kernel == NULL) { \
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+                "Unallowable operation: psKernel %s or its data is NULL.", \
+                #KERNEL); \
+        return RETURNVALUE; \
+    } \
+    PS_ASSERT_IMAGE_NON_NULL((KERNEL)->image, RETURNVALUE);
+
+#define PS_ASSERT_KERNELS_SIZE_EQUAL(KERNEL1, KERNEL2, RETURNVALUE) \
+    if ((KERNEL1)->xMin != (KERNEL2)->xMin || \
+        (KERNEL1)->xMax != (KERNEL2)->xMax || \
+        (KERNEL1)->yMin != (KERNEL2)->yMin || \
+        (KERNEL1)->yMax != (KERNEL2)->yMax) { \
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+                "Unallowable operation: Kernels %s and %s are not the same size.", \
+                #KERNEL1, #KERNEL2); \
+        return RETURNVALUE; \
+    } \
+    PS_ASSERT_IMAGES_SIZE_EQUAL((KERNEL1)->image, (KERNEL2)->image, RETURNVALUE);
+
+/// Allocates a convolution kernel of the given range
+///
+/// In order to perform a convolution, we need to define the convolution
+/// kernel. We need a more general object than a psImage so that we can
+/// incorporate the offset from the (0, 0) pixel to the (0, 0) value of the
+/// kernel. It might be convenient to allow both positive and negative
+/// indices to convey the positive and negative shifts. One might consider
+/// setting the x0 and y0 members of a psImage to the appropriate offsets,
+/// but this is not the purpose of these members, and doing so may affect the
+/// behavior of other psImage operations.
+///
+/// This construction allows the kernel member to use negative indices, while
+/// preserving the location of psMemBlocks relative to allocated memory.
+///
+/// The maximum extent of the kernel shifts shall be defined by the xMin,
+/// xMax, yMin and yMax members. Note that xMin and yMin, under normal
+/// circumstances, should be negative numbers. That is,
+/// myKernel->kernel[-3][-2] may be defined if yMin and xMin are equal to or
+/// more negative than -3 and -2, respectively.
+///
+/// In the event that one of the minimum values is greater than the
+/// corresponding maximum value, the function shall generate a warning, and
+/// the offending values shall be exchanged.
+///
+/// @return psKernel*          A new kernel object
+///
+psKernel *psKernelAlloc(
+    int xMin,                          ///< Most negative x index
+    int xMax,                          ///< Most positive x index
+    int yMin,                          ///< Most negative y index
+    int yMax                           ///< Most positive y index
+) PS_ATTR_MALLOC;
+
+/// Allocate a convolution kernel from a provided image
+psKernel *psKernelAllocFromImage(psImage *image, ///< Image from which to define kernel
+                                 int x0, int y0 ///< Coordinates of kernel centre
+    );
+
+/// Checks the type of a particular pointer.
+///
+/// Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+///
+/// @return bool:       True if the pointer matches a psKernel structure, false otherwise.
+///
+bool psMemCheckKernel(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/// Generates a kernel given a list of shift values
+///
+/// Given a list of values (e.g., shifts made in the course of OT guiding),
+/// psKernelGenerate shall return the appropriate kernel.  The vectors xShifts
+/// and yShifts, which are a list of shifts relative to some starting point,
+/// will be supplied by the user. The elements of the vectors should be of an
+/// integer type; otherwise the values shall be truncated to integers. The
+/// output kernel shall be normalized such that the sum over the kernel is
+/// unity.
+///
+/// If the vectors are not of the same number of elements, then the function
+/// shall generate a warning shall be generated, following which, the longer
+/// vector trimmed to the length of the shorter, and the function shall continue.
+///
+/// @return psKernel*    new Kernel object
+///
+psKernel *psKernelGenerate(
+    const psVector *tShifts,            ///< list of time shifts (F32)
+    const psVector *xShifts,            ///< list of x-axis shifts (S32)
+    const psVector *yShifts,            ///< list of y-axis shifts (S32)
+    float totalTime,                    ///< Total time (relative times if negative or zero)
+    bool xyRelative                     ///< Are x,y positions relative (shifts) or absolute?
+);
+
+/// Convolve an image with a kernel, using a direct convolution
+///
+/// This is appropriate for small kernels, where there is no time saving to use FFT method.
+///
+/// @return psImage*  resulting image
+///
+psImage *psImageConvolveDirect(
+    psImage *out,                       ///< Output image, or NULL
+    const psImage *in,                  ///< Image to convolve
+    const psKernel *kernel              ///< kernel to colvolve with
+);
+
+/// Convolve a mask image with a kernel
+///
+/// Returns a mask, grown by the supplied convolution bounds.  Only those pixels specified by the maskVal are
+/// grown, being ORed with setVal; the rest are simply propagated.  If setVal is zero, uses maskVal; note that
+/// the mode of growing individual bits in maskVal is NOT supported because this algorithm does not enable it.
+psImage *psImageConvolveMask(psImage *out, ///< Output image, or NULL
+                             const psImage *mask, ///< Mask to convolve
+                             psMaskType maskVal, ///< Mask value to convolve
+                             psMaskType setVal, ///< Mask value to set; 0 to propagate maskVal
+                             int xMin, int xMax, int yMin, int yMax ///< Convolution bounds
+    );
+
+/// Convolve a mask image with a kernel, using direct convolution
+///
+/// Returns a mask, grown by the supplied convolution bounds.  Only those pixels specified by the maskVal are
+/// grown, being ORed with setVal; the rest are simply propagated.  If setVal is zero, then individual bits
+/// matching maskVal are grown.
+psImage *psImageConvolveMaskDirect(psImage *out, ///< Output image, or NULL
+                                   const psImage *mask, ///< Mask to convolve
+                                   psMaskType maskVal, ///< Mask value to convolve
+                                   psMaskType setVal, ///< Mask value to set; 0 to propagate maskVal
+                                   int xMin, int xMax, int yMin, int yMax ///< Convolution bounds
+    );
+
+/// Convolve a mask image with a kernel, using the FFT
+///
+/// Returns a mask, grown by the supplied convolution bounds.  Only those pixels specified by the maskVal are
+/// grown, being ORed with setVal; the rest are simply propagated.  If setVal is zero, uses maskVal; note that
+/// the mode of growing individual bits in maskVal is NOT supported because this algorithm does not enable it.
+/// Uses psImageConvolveFFT to convolve those pixels which are masked, and then thresholds at the specified
+/// level.
+psImage *psImageConvolveMaskFFT(psImage *out, ///< Output image, or NULL
+                                const psImage *mask, ///< Mask to convolve
+                                psMaskType maskVal, ///< Mask value to convolve
+                                psMaskType setVal, ///< Mask value to set; 0 to use maskVal
+                                int xMin, int xMax, int yMin, int yMax, ///< Convolution bounds
+                                float thresh ///< Threshold (0..1) for convolved floating-point image
+    );
+
+/// Smooths an image by parts using 1D Gaussian independently in x and y.
+///
+/// Applies a circularly symmetric Gaussian smoothing first in x and then in y
+/// directions with just a vector.  This process is 2N faster than 2D convolutions (in general).
+///
+/// @return bool        TRUE if successful, otherwise FALSE
+///
+bool psImageSmooth(
+    psImage *image,                    ///< the image to be smoothed
+    double  sigma,                     ///< the width of the smoothing kernel in pixels
+    double  Nsigma                     ///< the size of the smoothing box in sigmas
+);
+
+bool psImageSmoothMaskF32(
+    psImage *image,                    ///< the image to be smoothed
+    psImage *mask,                     ///< optional mask
+    psMaskType maskVal,
+    double  sigma,                     ///< the width of the smoothing kernel in pixels
+    double  Nsigma                     ///< the size of the smoothing box in sigmas
+);
+
+/// Control threading for image convolution functions
+void psImageConvolveSetThreads(bool threaded ///< Run image convolution threaded?
+    );
+
+/// Return whether image convolution functions are threaded
+bool psImageConvolveGetThreads(void);
+
+/// @}
+#endif // #ifndef PS_IMAGE_CONVOLVE_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageGeomManip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageGeomManip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageGeomManip.c	(revision 22322)
@@ -0,0 +1,998 @@
+/** @file  psImageGeomManip.c
+ *
+ *  @brief Contains basic image pixel and geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Pixel Manipulations" and
+ *         "Image Geometry Manipulations".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.43 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-21 02:56:11 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>                          // for isfinite(), etc.
+#include <stdlib.h>
+#include <string.h>                        // for memcpy, etc.
+
+#include "psImageGeomManip.h"
+
+#include "psAbort.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psImageStructManip.h"
+#include "psStats.h"
+#include "psMemory.h"
+#include "psAssert.h"
+#include "psImageInterpolate.h"
+#include "psCoord.h"
+
+psImage* psImageRebin(psImage* out,
+                      const psImage* in,
+                      const psImage* mask,
+                      psMaskType maskVal,
+                      int scale,
+                      const psStats* stats)
+{
+    psS32 inRows;
+    psS32 inCols;
+    psS32 outRows;
+    psS32 outCols;
+    psVector* vec;                     // vector to hold the values of a single bin.
+    psVector* maskVec = NULL;          // vector to hold the mask of a single bin.
+    psMaskType* maskData = NULL;
+    psStats* myStats;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(out);
+        return NULL;
+    }
+
+    if (scale < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified scale value, %d, must be a positive value."),
+                scale);
+        psFree(out);
+        return NULL;
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified statistic can not be NULL."));
+        psFree(out);
+        return NULL;
+    }
+
+    psStatsOptions statistic = psStatsSingleOption(stats->options); // Statistics option to use
+    if (statistic == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified statistic option, %d, is not valid.  Must specify one and only one statistic type."),
+                stats->options);
+        psFree(out);
+        return NULL;
+    }
+
+    vec = psVectorAllocEmpty(scale * scale, in->type.type);
+
+    if (mask != NULL) {
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Input psImage mask type, %s, is not the supported mask datatype of %s."),
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            psFree(vec);
+            return NULL;
+        }
+        maskVec = psVectorAllocEmpty(scale * scale, PS_TYPE_MASK);
+        maskData = maskVec->data.PS_TYPE_MASK_DATA;
+    }
+
+    myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    // create output image.
+    inRows = in->numRows;
+    inCols = in->numCols;
+    outRows = (inRows + scale - 1) / scale;     // round-up for remainders
+    outCols = (inCols + scale - 1) / scale;     // round-up for remainders
+    out = psImageRecycle(out, outCols, outRows, in->type.type);
+
+    #define PS_IMAGE_REBIN_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE *outRowData; \
+        ps##TYPE *vecData = vec->data.TYPE; \
+        psMaskType *inRowMask = NULL; \
+        for (psS32 row = 0; row < outRows; row++) { \
+            outRowData = out->data.TYPE[row]; \
+            psS32 inCurrentRow = row * scale; \
+            psS32 inNextRow = (row + 1) * scale; \
+            for (psS32 col = 0; col < outCols; col++) { \
+                psS32 inCurrentCol = col * scale; \
+                psS32 inNextCol = (col + 1) * scale; \
+                psS32 n = 0; \
+                for (psS32 inRow = inCurrentRow; inRow < inNextRow && inRow < inRows; inRow++) { \
+                    ps##TYPE* inRowData = in->data.TYPE[inRow]; \
+                    if (mask != NULL) { \
+                        inRowMask = mask->data.PS_TYPE_MASK_DATA[inRow]; \
+                    } \
+                    for (psS32 inCol = inCurrentCol; inCol < inNextCol && inCol < inCols; inCol++) { \
+                        if (maskData != NULL) { \
+                            maskData[n] = inRowMask[inCol]; \
+                        } \
+                        vecData[n++] = inRowData[inCol]; \
+                    } \
+                } \
+                vec->n = n; \
+                if (maskVec) { \
+                    maskVec->n = n; \
+                } \
+                psVectorStats(myStats, vec, NULL, maskVec, maskVal); \
+                outRowData[col] = (ps##TYPE)psStatsGetValue(myStats, statistic); \
+            } \
+        } \
+    } \
+    break;
+
+    switch (in->type.type) {
+        //        PS_IMAGE_REBIN_CASE(U8);       Not valid since psVectorStats doesn't allow
+        PS_IMAGE_REBIN_CASE(U16);
+        PS_IMAGE_REBIN_CASE(U32);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(U64);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(S8);
+        //        PS_IMAGE_REBIN_CASE(S16);      Not valid since psVectorStats doesn't allow
+        PS_IMAGE_REBIN_CASE(S32);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(S64);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(F32);
+        PS_IMAGE_REBIN_CASE(F64);
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified psImage type, %s, is not supported."),
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    psFree(vec);
+    psFree(maskVec);
+    psFree(myStats);
+
+    return out;
+}
+
+psImage* psImageResample(psImage* out,
+                         const psImage* in,
+                         int scale,
+                         psImageInterpolateMode mode)
+{
+    psS32 outRows;
+    psS32 outCols;
+    float invScale;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(out);
+        return NULL;
+    }
+
+    if (scale < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified scale value, %d, must be a positive value."),
+                scale);
+        psFree(out);
+        return NULL;
+    }
+
+    // create an output image of the same size
+    // and type
+    outRows = in->numRows * scale;
+    outCols = in->numCols * scale;
+    invScale = 1.0f / (float)scale;
+
+    psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(mode, in, NULL, NULL, 0,
+                                                                       NAN, NAN, 0, 0, 0);
+
+    #define PSIMAGE_RESAMPLE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        out = psImageRecycle(out,outCols, outRows, PS_TYPE_##TYPE); \
+        for (psS32 row=0;row<outRows;row++) { \
+            ps##TYPE* rowData = out->data.TYPE[row]; \
+            float inRow = (float)row * invScale; \
+            for (psS32 col=0;col<outCols;col++) { \
+                double value; \
+                if (!psImageInterpolate(&value, NULL, NULL, (float)col*invScale, inRow, interp)) { \
+                    psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image."); \
+                    psFree(interp); \
+                    psFree(out); \
+                    return NULL; \
+                } \
+                rowData[col] = value; \
+            } \
+        }  \
+        break; \
+    }
+
+    switch (in->type.type) {
+        PSIMAGE_RESAMPLE_CASE(U8)
+        PSIMAGE_RESAMPLE_CASE(U16)
+        PSIMAGE_RESAMPLE_CASE(U32)
+        PSIMAGE_RESAMPLE_CASE(U64)
+        PSIMAGE_RESAMPLE_CASE(S8)
+        PSIMAGE_RESAMPLE_CASE(S16)
+        PSIMAGE_RESAMPLE_CASE(S32)
+        PSIMAGE_RESAMPLE_CASE(S64)
+        PSIMAGE_RESAMPLE_CASE(F32)
+        PSIMAGE_RESAMPLE_CASE(F64)
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified psImage type, %s, is not supported."),
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    psFree(interp);
+
+    return out;
+}
+
+psImage* psImageRoll(psImage* out,
+                     const psImage* input,
+                     int dx,
+                     int dy)
+{
+    psS32 outRows;
+    psS32 outCols;
+    psS32 elementSize;
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(out);
+        return NULL;
+    }
+    // create an output image of the same size
+    // and type
+    outRows = input->numRows;
+    outCols = input->numCols;
+    elementSize = PSELEMTYPE_SIZEOF(input->type.type);
+    out = psImageRecycle(out, outCols, outRows, input->type.type);
+
+    // make dx and dy between 0 and outCols or
+    // outRows, respectively
+    dx = dx % outCols;
+    dy = dy % outRows;
+    if (dx < 0) {
+        dx += outCols;
+    }
+    if (dy < 0) {
+        dy += outRows;
+    }
+
+    psS32 segment1Size = elementSize * (outCols - dx);
+    psS32 segment2Size = elementSize * dx;
+
+    for (psS32 row = 0; row < outRows; row++) {
+        psS32 inRowNumber = row + dy;
+
+        if (inRowNumber >= outRows) {
+            inRowNumber -= outRows;
+        }
+        psU8* inRow = input->data.U8[inRowNumber]; // use byte arithmetic for all types
+        psU8* outRow = out->data.U8[row];
+
+        memcpy(outRow, inRow + segment2Size, segment1Size);
+        memcpy(outRow + segment1Size, inRow, segment2Size);
+    }
+
+    return out;
+}
+
+psImage* psImageRotate(psImage* out,
+                       const psImage* input,
+                       float angle,
+                       double exposed,
+                       psImageInterpolateMode mode)
+{
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(out);
+        return NULL;
+    }
+    // put the angle in the range of 0...2PI.
+    angle = (float)((double)angle - (2.0*M_PI) * floor(angle / (2.0*M_PI)));
+
+    if (fabsf(angle - M_PI_2) < FLT_EPSILON) {
+        // perform 1/4 rotate counter-clockwise
+        psS32 numRows = input->numCols;
+        psS32 numCols = input->numRows;
+        psS32 lastCol = numCols - 1;
+        psElemType type = input->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_LEFT_90(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            ps##TYPE** inData = input->data.TYPE; \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inData[lastCol-col][row]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_LEFT_90(U8);
+            PSIMAGE_ROTATE_LEFT_90(U16);
+            PSIMAGE_ROTATE_LEFT_90(U32);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(U64);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(S8);
+            PSIMAGE_ROTATE_LEFT_90(S16);
+            PSIMAGE_ROTATE_LEFT_90(S32);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(S64);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(F32);
+            PSIMAGE_ROTATE_LEFT_90(F64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        _("Specified psImage type, %s, is not supported."),
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle - M_PI) < FLT_EPSILON) {
+        // perform 1/2 rotate
+        psS32 numRows = input->numRows;
+        psS32 lastRow = numRows - 1;
+        psS32 numCols = input->numCols;
+        psS32 lastCol = numCols - 1;
+        psElemType type = input->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_180_CASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                ps##TYPE* inRow = input->data.TYPE[lastRow-row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inRow[lastCol - col]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_180_CASE(U8);
+            PSIMAGE_ROTATE_180_CASE(U16);
+            PSIMAGE_ROTATE_180_CASE(U32);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(U64);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(S8);
+            PSIMAGE_ROTATE_180_CASE(S16);
+            PSIMAGE_ROTATE_180_CASE(S32);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(S64);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(F32);
+            PSIMAGE_ROTATE_180_CASE(F64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        _("Specified psImage type, %s, is not supported."),
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle - (M_PI+M_PI_2)) < FLT_EPSILON) {
+        // perform 1/4 rotate clockwise
+        psS32 numRows = input->numCols;
+        psS32 lastRow = numRows - 1;
+        psS32 numCols = input->numRows;
+        psElemType type = input->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_RIGHT_90(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            ps##TYPE** inData = input->data.TYPE; \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inData[col][lastRow-row]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_RIGHT_90(U8);
+            PSIMAGE_ROTATE_RIGHT_90(U16);
+            PSIMAGE_ROTATE_RIGHT_90(U32);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(U64);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(S8);
+            PSIMAGE_ROTATE_RIGHT_90(S16);
+            PSIMAGE_ROTATE_RIGHT_90(S32);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(S64);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(F32);
+            PSIMAGE_ROTATE_RIGHT_90(F64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        _("Specified psImage type, %s, is not supported."),
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle) < FLT_EPSILON) {
+        out = psImageCopy(out, input, input->type.type);
+    } else {
+        psElemType type = input->type.type;
+        psS32 numRows = input->numRows;
+        psS32 numCols = input->numCols;
+        float centerX = (float)(numCols) / 2.0f;
+        float centerY = (float)(numRows) / 2.0f;
+        double cosT = cosf(angle);
+        double sinT = sinf(angle);
+
+        // calculate the corners of the rotated image so we know the proper output image size.
+        // x' = x cos(t) + y sin(t); i.e, x' = (x-centerX)*cosT + (y-centerY)*sinT;
+        // y' = y cos(t) - x sin(t); i.e. y' = (y-centerY)*cosT - (x-centerX)*sinT;
+
+        psS32 outCols = ceil(abs(numCols * cosT) + abs(numRows * sinT)) + 1;
+        psS32 outRows = ceil(abs(numCols * sinT) + abs(numRows * cosT)) + 1;
+        float minX = (float)outCols / -2.0f;
+        psS32 intMinY = outRows / -2;
+
+        out = psImageRecycle(out, outCols, outRows, type);
+
+        /* optimized public domain rotation routine by Karl Lager
+         *
+         * float cosT,sinT;
+         * cosT = cos(t);
+         * sinT = sin(t);
+         * for (y = min_y; y <= max_y; y++) {
+         *     x' = min_x * cosT + y * sinT + x1';
+         *     y' = y * cosT - min_x * sinT + y1';
+         *     for (x = min_x; x <= max_x; x++) {
+         *         if (x', y') is in the bounds of the bitmap, get pixel
+         *            (x', y') and plot the pixel to (x, y) on screen.
+         *         x' += cosT;
+         *         y' -= sinT;
+         *     }
+         * }
+         */
+
+        // precalculate some figures that are used within loop
+        float minXTimesCosTPlusCenterX = minX * cosT + centerX;
+        float CenterYMinusminXTimesSinT = centerY - minX * sinT;
+
+        psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(mode, input, NULL, NULL, 0,
+                                                                           exposed, NAN, 0, 0, 0.0);
+
+        #define PSIMAGE_ROTATE_ARBITRARY_LOOP(TYPE)  \
+          case PS_TYPE_##TYPE: { \
+            if (exposed < PS_MIN_##TYPE || \
+                    exposed > PS_MAX_##TYPE || \
+                    exposed < PS_MIN_##TYPE || \
+                    exposed > PS_MAX_##TYPE) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        _("Specified %s value, %g%+gi, is not the the range of input psImage's valid pixel values (%s), i.e. [%g:%g]."), \
+                        "exposed", \
+                        exposed, exposed, \
+                        PS_TYPE_##TYPE##_NAME,  \
+                        (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+                psFree(out); \
+                out = NULL; \
+                break; \
+            } \
+            float inX; \
+            float inY; \
+            ps##TYPE* outRow; \
+            for (psS32 y = 0; y < outRows; y++) { \
+                inX = minXTimesCosTPlusCenterX + (y+intMinY) * sinT; \
+                inY = CenterYMinusminXTimesSinT + (y+intMinY) * cosT; \
+                outRow = out->data.TYPE[y]; \
+                for (psS32 x = 0; x < outCols; x++) { \
+                    double value; \
+                    if (!psImageInterpolate(&value, NULL, NULL, inX, inY, interp)) { \
+                        psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image."); \
+                        psFree(out); \
+                        psFree(interp); \
+                        return NULL; \
+                    } \
+                    outRow[x] = value; \
+                    inX += cosT; \
+                    inY -= sinT; \
+                } \
+            } \
+            break; \
+        }
+
+        switch (type) {
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U8);
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U16);
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U32);
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U64);
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S8);
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S16);
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S32);
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S64);
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(F32);
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(F64);
+          default: {
+              char* typeStr;
+              PS_TYPE_NAME(typeStr,type);
+              psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                      _("Specified psImage type, %s, is not supported."),
+                      typeStr);
+              psFree(out);
+              psFree(interp);
+              out = NULL;
+          }
+        }
+
+        psFree(interp);
+
+    }
+
+    return out;
+}
+
+psImage* psImageShift(psImage* out,
+                      const psImage* input,
+                      float dx,
+                      float dy,
+                      double exposed,
+                      psImageInterpolateMode mode)
+{
+    psS32 outRows;
+    psS32 outCols;
+    psS32 elementSize;
+    psElemType type;
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(out);
+        return NULL;
+    }
+    // create an output image of the same size
+    // and type
+    outRows = input->numRows;
+    outCols = input->numCols;
+    type = input->type.type;
+    elementSize = PSELEMTYPE_SIZEOF(type);
+    out = psImageRecycle(out, outCols, outRows, type);
+
+    psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(mode, input, NULL, NULL, 0,
+                                                                       exposed, NAN, 0, 0, 0.0);
+
+    #define PSIMAGE_SHIFT_CASE(TYPE) \
+case PS_TYPE_##TYPE: \
+    if (exposed < PS_MIN_##TYPE || \
+            exposed > PS_MAX_##TYPE || \
+            exposed < PS_MIN_##TYPE || \
+            exposed > PS_MAX_##TYPE) { \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                _("Specified %s value, %g%+gi, is not the the range of input psImage's valid pixel values (%s), i.e. [%g:%g]."), \
+                "exposed", \
+                exposed,exposed, \
+                PS_TYPE_##TYPE##_NAME,  \
+                (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+        psFree(out); \
+        out = NULL; \
+        break; \
+    } \
+    /* note: output(i,j) = input(i+0.5-dx,j+0.5-dy) */ \
+    /* positive dx,dy moves pixel i-dx,j-dy to i,y */ \
+    /* also: pixel center is 0.5,0.5 */ \
+    for (psS32 row=0;row<outRows;row++) { \
+        ps##TYPE* outRow = out->data.TYPE[row]; \
+        float y = row + 0.5 - dy; \
+        for (psS32 col=0;col<outCols;col++) { \
+            float x = col + 0.5 - dx; \
+            double value; \
+            if (!psImageInterpolate(&value, NULL, NULL, x, y, interp)) { \
+                psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image."); \
+                psFree(interp); \
+                psFree(out); \
+                return NULL; \
+            } \
+            outRow[col] = value; \
+        } \
+    } \
+    break;
+
+    switch (input->type.type) {
+        PSIMAGE_SHIFT_CASE(U8);
+        PSIMAGE_SHIFT_CASE(U16);
+        PSIMAGE_SHIFT_CASE(U32);
+        PSIMAGE_SHIFT_CASE(U64);
+        PSIMAGE_SHIFT_CASE(S8);
+        PSIMAGE_SHIFT_CASE(S16);
+        PSIMAGE_SHIFT_CASE(S32);
+        PSIMAGE_SHIFT_CASE(S64);
+        PSIMAGE_SHIFT_CASE(F32);
+        PSIMAGE_SHIFT_CASE(F64);
+      default: {
+          char* typeStr;
+          PS_TYPE_NAME(typeStr,type);
+          psError(PS_ERR_BAD_PARAMETER_TYPE, true, _("Specified psImage type, %s, is not supported."),
+                  typeStr);
+          psFree(out);
+          psFree(interp);
+          return NULL;
+        }
+    }
+
+    psFree(interp);
+    return out;
+}
+
+bool psImageShiftMask(psImage **out, psImage **outMask, const psImage* in, const psImage *inMask,
+                      psMaskType maskVal, float dx, float dy, double exposed, psMaskType blank,
+                      psImageInterpolateMode mode)
+{
+    PS_ASSERT(out, false);
+    PS_ASSERT_IMAGE_NON_NULL(in, false);
+    if (inMask) {
+        PS_ASSERT_IMAGE_NON_NULL(inMask, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(in, inMask, false);
+        PS_ASSERT_IMAGE_TYPE(inMask, PS_TYPE_MASK, false);
+    }
+
+    int numRows = in->numRows, numCols = in->numCols; // Size of image
+    psElemType type = in->type.type;    // Type of image
+
+    *out = psImageRecycle(*out, numCols, numRows, type);
+    if (outMask) {
+        *outMask = psImageRecycle(*outMask, numCols, numRows, PS_TYPE_MASK);
+    }
+
+    psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(PS_INTERPOLATE_BICUBE, in, NULL,
+                                                                       inMask, maskVal, exposed, NAN, blank,
+                                                                       blank, 0.0);
+
+    #define PSIMAGE_SHIFT_MASK_CASE(TYPE) \
+case PS_TYPE_##TYPE: \
+    if (exposed < PS_MIN_##TYPE || \
+            exposed > PS_MAX_##TYPE || \
+            exposed < PS_MIN_##TYPE || \
+            exposed > PS_MAX_##TYPE) { \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                _("Specified exposed value, %g%+gi, is not the the range of input image's " \
+                  "valid pixel values (%s), i.e. [%g:%g]."), \
+                exposed, exposed, \
+                PS_TYPE_##TYPE##_NAME,  \
+                (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+        psFree(out); \
+        out = NULL; \
+        break; \
+    } \
+    /* note: output(i,j) = input(i+0.5-dx,j+0.5-dy) */ \
+    /* positive dx,dy moves pixel i-dx,j-dy to i,y */ \
+    /* also: pixel center is 0.5,0.5 */ \
+    for (int row = 0; row < numRows; row++) { \
+        ps##TYPE* outRow = (*out)->data.TYPE[row]; \
+        psMaskType *outMaskRow = (outMask ? (*outMask)->data.PS_TYPE_MASK_DATA[row] : NULL); \
+        float y = row + 0.5 - dy; \
+        for (int col = 0; col < numCols; col++) { \
+            float x = col + 0.5 - dx; \
+            double value; \
+            psMaskType valueMask = 0; \
+            if (!psImageInterpolate(&value, NULL, &valueMask, x, y, interp)) { \
+                psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image."); \
+                psFree(interp); \
+                return false; \
+            } \
+            outRow[col] = value; \
+            if (outMask) { \
+                outMaskRow[col] = valueMask; \
+            } \
+        } \
+    } \
+    break;
+
+    switch (type) {
+        PSIMAGE_SHIFT_MASK_CASE(U8);
+        PSIMAGE_SHIFT_MASK_CASE(U16);
+        PSIMAGE_SHIFT_MASK_CASE(U32);
+        PSIMAGE_SHIFT_MASK_CASE(U64);
+        PSIMAGE_SHIFT_MASK_CASE(S8);
+        PSIMAGE_SHIFT_MASK_CASE(S16);
+        PSIMAGE_SHIFT_MASK_CASE(S32);
+        PSIMAGE_SHIFT_MASK_CASE(S64);
+        PSIMAGE_SHIFT_MASK_CASE(F32);
+        PSIMAGE_SHIFT_MASK_CASE(F64);
+      default: {
+          char* typeStr;
+          PS_TYPE_NAME(typeStr,type);
+          psError(PS_ERR_BAD_PARAMETER_TYPE, true, _("Specified psImage type, %s, is not supported."),
+                  typeStr);
+          psFree(interp);
+          return false;
+        }
+    }
+
+    psFree(interp);
+    return true;
+}
+
+psImage* psImageTransform(psImage *output,
+                          psPixels** blankPixels,
+                          const psImage *input,
+                          const psImage *inputMask,
+                          psMaskType inputMaskVal,
+                          const psPlaneTransform *outToIn,
+                          psRegion region,
+                          const psPixels* pixels,
+                          psImageInterpolateMode mode,
+                          double exposedValue)
+{
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(output);
+        return NULL;
+    }
+    psElemType type = input->type.type;
+
+    if (blankPixels != NULL && *blankPixels == NULL) {
+        *blankPixels = psPixelsAlloc(0);
+    }
+
+    if (inputMask != NULL) {
+        if (input->numRows != inputMask->numRows || input->numCols != inputMask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    _("Input psImage mask size, %dx%d, does not match psImage input size, %dx%d."),
+                    input->numCols, input->numRows,
+                    inputMask->numCols, inputMask->numRows );
+            psFree(output);
+            return NULL;
+        }
+        if (inputMask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,inputMask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Input psImage mask type, %s, is not the supported mask datatype of %s."),
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(output);
+            return NULL;
+        }
+    }
+
+    if (outToIn == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified input transform can not be NULL."));
+        return NULL;
+    }
+
+    int row0;
+    int row1;
+    int col0;
+    int col1;
+    int numRows;
+    int numCols;
+    if (output == NULL) { // output image size is determined by psRegion
+        row0 = region.y0;
+        row1 = region.y1;
+        col0 = region.x0;
+        col1 = region.x1;
+        //If [0,0,0,0] specified, the whole image is to be included
+        if (row0 == 0 && col0 == 0 && row1 == 0 && col1 == 0) {
+            row0 = input->row0;
+            col0 = input->col0;
+            row1 = input->row0 + input->numRows - 1;
+            col1 = input->col0 + input->numCols - 1;
+        }
+        if (col1 < 1) {
+            col1 += input->col0 + input->numCols;
+        }
+
+        if (row1 < 1) {
+            row1 += input->row0 + input->numRows;
+        }
+
+        numRows = row1 - row0;
+        numCols = col1 - col0;
+
+        if (numRows < 1 || numCols < 1) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "The specified region is invalid.");
+            psFree(output);
+            return NULL;
+        }
+        // create the output image.
+        output = psImageRecycle(output, numCols, numRows, input->type.type);
+        P_PSIMAGE_SET_COL0(output, region.x0);
+        P_PSIMAGE_SET_ROW0(output, region.y0);
+    } else { // size of output is determined by output parameter
+        numRows = output->numRows;
+        numCols = output->numCols;
+        row0 = output->row0;
+        col0 = output->col0;
+        row1 = row0+numRows -1;
+        col1 = col0+numCols -1;
+    }
+
+    // loop through the output image using the domain above and transform
+    // each output pixel to input coordinates and use psImageInterpolate
+    // to determine the pixel value.
+    psPlane outPosition;
+    psPlane* inPosition = NULL;
+
+    psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(mode, input, NULL, inputMask,
+                                                                       inputMaskVal, NAN, NAN, 0, 0, 0.0);
+
+
+    #define PSIMAGE_TRANSFORM_DOTRANSFORM(TYPE) \
+    /* apply the transform to get the position in the input image */ \
+    inPosition = psPlaneTransformApply(inPosition, outToIn, &outPosition); \
+    \
+    if (inPosition == NULL) { \
+        psError(PS_ERR_UNKNOWN, false, \
+                "Failed to apply the transform"); \
+        psFree(output); \
+        return NULL; \
+    } \
+    /* interpolate the cooresponding input pixel to get the output pixel value. */ \
+    double value; \
+    if (!psImageInterpolate(&value, NULL, NULL, inPosition->x, inPosition->y, interp)) { \
+        psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image."); \
+        psFree(output); \
+        psFree(interp); \
+        return NULL; \
+    } \
+    /*    psFree(inPosition); */\
+    if (isnan(value)) { \
+        if (blankPixels != NULL) { \
+            psPixelsAdd(*blankPixels, (*blankPixels)->nalloc, outPosition.x, outPosition.y); \
+        } \
+        value = exposedValue; \
+    } \
+
+    #define PSIMAGE_TRANSFORM_CASE(TYPE) \
+      case PS_TYPE_##TYPE: { \
+        for (int row = 0; row < numRows; row++) { \
+            outPosition.y = row+row0; \
+            ps##TYPE* outputData=output->data.TYPE[row]; \
+            for (int col = 0; col < numCols; col++) { \
+                outPosition.x = col+col0; \
+                PSIMAGE_TRANSFORM_DOTRANSFORM(TYPE) \
+                outputData[col] = value; \
+            } \
+        } \
+        break; \
+    }
+
+    switch (type) {
+        PSIMAGE_TRANSFORM_CASE(F32);
+        PSIMAGE_TRANSFORM_CASE(F64);
+      default: {
+          char* typeStr;
+          PS_TYPE_NAME(typeStr,type);
+          psError(PS_ERR_BAD_PARAMETER_TYPE, true, _("Specified psImage type, %s, is not supported."),
+                  typeStr);
+          psFree(output);
+          psFree(inPosition);
+          return NULL;
+      }
+    }
+
+    psFree(inPosition);
+
+    return output;
+}
+
+
+#define FLIP_X_CASE(TYPENAME,TYPE) \
+case TYPENAME: { \
+    long numRows = input->numRows; \
+    long numCols = input->numCols; \
+    for (long i = 0; i < numRows; i++) { \
+        for (long j = 0; j < numCols; j++) { \
+            output->data.TYPE[i][j] = input->data.TYPE[i][numCols - j - 1]; \
+        } \
+    } \
+    break; \
+}
+
+#define FLIP_Y_CASE(TYPENAME,TYPE) \
+case TYPENAME: { \
+    long numRows = input->numRows; \
+    long numCols = input->numCols; \
+    for (long i = 0; i < numRows; i++) { \
+        for (long j = 0; j < numCols; j++) { \
+            output->data.TYPE[i][j] = input->data.TYPE[numRows - i - 1][j]; \
+        } \
+    } \
+    break; \
+}
+
+psImage *psImageFlip(psImage *output, const psImage *input, bool xFlip, bool yFlip)
+{
+    PS_ASSERT_IMAGE_NON_NULL(input, NULL);
+
+    if (xFlip && yFlip) {
+        // This is equivalent to a 180 degree rotation;
+        return psImageRotate(output, input, M_PI, NAN, PS_INTERPOLATE_BILINEAR);
+    }
+
+    if (!xFlip && !yFlip) {
+        // They want something, so let's give it to them
+        return psImageCopy(output, input, input->type.type);
+    }
+
+    output = psImageRecycle(output, input->numCols, input->numRows, input->type.type);
+
+    if (xFlip) {
+        switch (input->type.type) {
+            FLIP_X_CASE(PS_TYPE_U8,  U8);
+            FLIP_X_CASE(PS_TYPE_U16, U16);
+            FLIP_X_CASE(PS_TYPE_U32, U32);
+            FLIP_X_CASE(PS_TYPE_U64, U64);
+            FLIP_X_CASE(PS_TYPE_S8,  S8);
+            FLIP_X_CASE(PS_TYPE_S16, S16);
+            FLIP_X_CASE(PS_TYPE_S32, S32);
+            FLIP_X_CASE(PS_TYPE_S64, S64);
+            FLIP_X_CASE(PS_TYPE_F32, F32);
+            FLIP_X_CASE(PS_TYPE_F64, F64);
+        default:
+            psFree(output);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unknown type for input image: %x\n", input->type.type);
+            return NULL;
+        }
+        return output;
+    }
+    if (yFlip) {
+        switch (input->type.type) {
+            FLIP_Y_CASE(PS_TYPE_U8,  U8);
+            FLIP_Y_CASE(PS_TYPE_U16, U16);
+            FLIP_Y_CASE(PS_TYPE_U32, U32);
+            FLIP_Y_CASE(PS_TYPE_U64, U64);
+            FLIP_Y_CASE(PS_TYPE_S8,  S8);
+            FLIP_Y_CASE(PS_TYPE_S16, S16);
+            FLIP_Y_CASE(PS_TYPE_S32, S32);
+            FLIP_Y_CASE(PS_TYPE_S64, S64);
+            FLIP_Y_CASE(PS_TYPE_F32, F32);
+            FLIP_Y_CASE(PS_TYPE_F64, F64);
+        default:
+            psFree(output);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unknown type for input image: %x\n", input->type.type);
+            return NULL;
+        }
+        return output;
+    }
+
+    psAbort("Should never get here.\n");
+    return NULL;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageGeomManip.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageGeomManip.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageGeomManip.h	(revision 22322)
@@ -0,0 +1,179 @@
+/* @file  psImageGeomManip.h
+ *
+ * @brief Contains basic image geometry manipulation operations, as
+ *        specified in the PSLIB SDRS sections "Image Geometry Manipulations".
+ *
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-03-11 00:41:30 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_IMAGE_GEOM_MANIP_H
+#define PS_IMAGE_GEOM_MANIP_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+#include "psImage.h"
+#include "psImageInterpolate.h"
+#include "psCoord.h"
+#include "psStats.h"
+#include "psPixels.h"
+
+/** Rebin image to new scale.
+ *
+ *  A new image is constructed in which the dimensions are reduced by a factor of
+ *  1/scale.  The scale, always a positive number, is equal in each dimension and
+ *  specified the number of pixels used to define a new pixel in the output image.
+ *  The output image is generated from all input image pixels. This function is
+ *  defined for psU8, psS8, psS16, psF32, psF64.
+ *
+ *  @return psImage    new image formed by rebinning input image.
+ */
+psImage* psImageRebin(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    const psImage* mask,               ///< mask for input image.  If NULL, no masking is done.
+    psMaskType maskVal,                ///< the bits to check in mask.
+    int scale,                         ///< the scale to rebin for each dimension
+    const psStats* stats
+    ///< the statistic to perform when rebinning.  Only one method should be set.
+);
+
+/** Resample image to new scale.
+ *
+ *  A new image is constructed in which the dimensions are increased by a
+ *  factor of scale. The scale, always a positive number, is equal in each
+ *  dimension. The output image is generated from all input image pixels.
+ *  Each pixel in the output image is derived by interpolating between
+ *  neighboring pixels using the specified interpolation method (mode).
+ *
+ *  @return psImage*    resampled image result
+ */
+psImage* psImageResample(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    int scale,                       ///< resample scaling factor
+    psImageInterpolateMode mode        ///< the interpolation mode used in resampling
+);
+
+/** Rotate the input image by given angle, specified in degrees.
+ *
+ *  The output image must contain all of the pixels from the input image in
+ *  their new frame. Pixels in the output image which do not map to input
+ *  pixels should be set to exposed. The center of rotation is always the
+ *  center pixel of the image. The rotation is specified in the sense that a
+ *  positive angle is an anti-clockwise rotation. This function must be
+ *  defined for the following types: psU8, psU16, psS8, psS16, psF32, psF64.
+ *
+ *  @return psImage*     the rotated image result.
+ */
+psImage* psImageRotate(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* input,              ///< input image
+    float angle,                       ///< the rotation angle in radians.
+    double exposed,                    ///< the output image pixel values for non-imagery areas
+    psImageInterpolateMode mode        ///< the interpolation mode used
+);
+
+/** Shift image by an arbitrary number of pixels (dx,dy) in either direction.
+ *
+ *  If the shift values are fractional, the output pixel values should
+ *  interpolate between the input pixel values. The output image has the same
+ *  dimensions as the input image. Pixels which fall off the edge of the
+ *  output image are lost. Newly exposed pixels are set to the value given by
+ *  exposed. This function must be defined for the following types: psU8,
+ *  psU16, psS8, psS16, psF32, psF64.
+ *
+ *  @return psImage*     the shifted image result.
+ */
+psImage* psImageShift(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* input,              ///< input image
+    float dx,                          ///< the shift in x direction.
+    float dy,                          ///< the shift in y direction.
+    double exposed,                    ///< the output image pixel values for non-imagery areas
+    psImageInterpolateMode mode        ///< the interpolation mode to use
+);
+
+/// Apply a translation to an image
+///
+/// This function is very much like psImageShift, except that it applies the same shifts to the mask
+bool psImageShiftMask(
+    psImage **out,                      ///< Output shifted image
+    psImage **outMask,                  ///< Output shifted mask, or NULL
+    const psImage* in,                  ///< Input image
+    const psImage *inMask,              ///< Input mask, or NULL
+    psMaskType maskVal,                 ///< Value to mask
+    float dx, float dy,                 ///< Shift to apply
+    double exposed,                     ///< Value to give exposed pixels
+    psMaskType blank,                   ///< Mask value for exposed pixels
+    psImageInterpolateMode mode         ///< Interpolation mode
+    );
+
+/** Roll image by an integer number of pixels in either direction.
+ *
+ *  The output image is the same dimensions as the input image.  Edge pixels
+ *  wrap to the other side (no values are lost).  This function is
+ *  defined for psU8, psS8, psS16, psF32, psF64.
+ *
+ *  @return psImage* the rolled version of the input image.
+ */
+psImage* psImageRoll(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* input,              ///< input image
+    int dx,                            ///< number of pixels to roll in the x-dimension
+    int dy                             ///< number of pixels to roll in the y-dimension
+);
+
+/** Transform the input image according the supplied transformation.
+ *
+ *  Transform the input image according the supplied transformation. The size
+ *  of the transformed image is defined by the supplied output image, if
+ *  non-NULL, or the region otherwise (size region.x1 - region.x0 by region.y1
+ *  region.y0, with out->x0 = region.x0 and out->y0 = region.y0). If the
+ *  inputMask is non-NULL, those pixels in the inputMask matching inputMaskVal
+ *  are to be ignored in the transformation. The inputMask must be of type
+ *  psU8, and of the same size as the input, otherwise the function shall
+ *  generate an error and return NULL. The transformation outToIn speciï¬es the
+ *  coordinates in the input image of a pixel in the output image â note that
+ *  this is the reverse of what might be naively expected, but it is what is
+ *  required in order to use psImageInterpolate. If the pixels array is
+ *  non-NULL, it shall consist of psPixelCoords, and only those pixels in the
+ *  output image shall be transformed; otherwise, the entire image is
+ *  generated. The interpolation is performed using the speciï¬ed interpolation
+ *  mode. Where a pixel in the output image does not correspond to a pixel in
+ *  the input image (or all appropriate pixels in the input image are
+ *  masked), the value shall be set to exposed, and the pixel added to the
+ *  appropriate list of pixels (psPixels) in the array of blankPixels for
+ *  return to the user. This function must be capable of handling the following
+ *  types for the input (with corresponding types for the output): psF32, psF64.
+
+ *
+ *  @return psImage*    The transformed image.
+ */
+psImage* psImageTransform(
+    psImage *output,                   ///< psImage to recycle, or NULL
+    psPixels** blankPixels,            ///< list of pixels in output image not set, or NULL if no list is desired.
+    const psImage *input,              ///< psImage to apply transform to
+    const psImage *inputMask,          ///< if not NULL, mask of input psImage
+    psMaskType inputMaskVal,           ///< masking value for inputMask
+    const psPlaneTransform *outToIn,   ///< the transform to apply
+    psRegion region,                   ///< the size of the transformed image
+    const psPixels* pixels,            /**< if not NULL, consists of psPixelCoords and specifies
+                                                        * which pixels in output image shall be transformed;
+                                                        * otherwise, entire image generated*/
+    psImageInterpolateMode mode,       ///< the interpolation scheme to be used
+    double exposedValue                ///< Exposed value to which non-corresponding pixels are set
+);
+
+// Flip the input image
+psImage *psImageFlip(psImage *output,   // Output image, or NULL
+                     const psImage *input, // Input image
+                     bool xFlip,        // Flip x axis?
+                     bool yFlip         // Flip y axis?
+                    );
+
+/// @}
+#endif // #ifndef PS_IMAGE_GEOM_MANIP_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageInterpolate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageInterpolate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageInterpolate.c	(revision 22322)
@@ -0,0 +1,989 @@
+/** @file  psImageInterpolate.c
+ *
+ *  @brief Contains functions for interpolating an image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *  @author Paul Price, IfA
+ *
+ *  @version $Revision: 1.20 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-04 20:11:00 $
+ *
+ *  Copyright 2004-2007 Institute for Astronomy, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psAssert.h"
+#include "psString.h"
+#include "psImage.h"
+#include "psImageInterpolate.h"
+
+static void imageInterpolateOptionsFree(psImageInterpolateOptions *options)
+{
+    // Casting away const
+    psFree((psImage*)options->image);
+    psFree((psImage*)options->mask);
+    psFree((psImage*)options->variance);
+}
+
+psImageInterpolateOptions *psImageInterpolateOptionsAlloc(psImageInterpolateMode mode,
+                                                          const psImage *image, const psImage *variance,
+                                                          const psImage *mask, psMaskType maskVal,
+                                                          double badImage, double badVariance,
+                                                          psMaskType badMask, psMaskType poorMask,
+                                                          float poorFrac)
+{
+    psImageInterpolateOptions *options = psAlloc(sizeof(psImageInterpolateOptions)); // Options, to return
+    psMemSetDeallocator(options, (psFreeFunc)imageInterpolateOptionsFree);
+
+    options->mode = mode;
+    // Casting away const to add to options
+    options->image = psMemIncrRefCounter((psImage*)image);
+    options->variance = psMemIncrRefCounter((psImage*)variance);
+    options->mask = psMemIncrRefCounter((psImage*)mask);
+    options->maskVal = maskVal;
+    options->badImage = badImage;
+    options->badVariance = badVariance;
+    options->badMask = badMask;
+    options->poorMask = poorMask;
+    options->poorFrac = poorFrac;
+    options->shifting = true;
+
+    return options;
+}
+
+// Interpolation engine for flat mode (nearest pixel)
+static inline psImageInterpolateStatus interpolateFlat(double *imageValue, double *varianceValue,
+                                                       psMaskType *maskValue, float x, float y,
+                                                       const psImageInterpolateOptions *options)
+{
+    // Parameters have been checked by psImageInterpolate()
+
+    const psImage *image = options->image; // Image of interest
+    int xInt = round(x - 0.5 + FLT_EPSILON); // Pixel closest to point of interest in x
+    int yInt = round(y - 0.5 + FLT_EPSILON); // Pixel closest to point of interest in y
+    int xLast = image->numCols - 1;     // Last pixel in x
+    int yLast = image->numRows - 1;     // Last pixel in y
+
+    if (xInt < 0 || xInt > xLast || yInt < 0 || yInt > yLast) {
+        // At least one pixel of the interpolation kernel is off the image
+        if (imageValue) {
+            *imageValue = options->badImage;
+        }
+        if (varianceValue) {
+            *varianceValue = options->badVariance;
+        }
+        if (maskValue) {
+            *maskValue = options->badMask;
+        }
+
+        return PS_INTERPOLATE_STATUS_OFF;
+    } else {
+
+        // Image and variance 'interpolation' according to image type
+        #define FLAT_CASE(TYPE) \
+          case PS_TYPE_##TYPE: { \
+            if (imageValue) { \
+                *imageValue = options->image->data.TYPE[yInt][xInt]; \
+            } \
+            if (varianceValue && options->variance) { \
+                *varianceValue = options->variance->data.TYPE[yInt][xInt]; \
+            } \
+            break; \
+        }
+
+        switch (options->image->type.type) {
+            FLAT_CASE(U8);
+            FLAT_CASE(U16);
+            FLAT_CASE(U32);
+            FLAT_CASE(U64);
+            FLAT_CASE(S8);
+            FLAT_CASE(S16);
+            FLAT_CASE(S32);
+            FLAT_CASE(S64);
+            FLAT_CASE(F32);
+            FLAT_CASE(F64);
+          default:
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unrecognised type for image: %x",
+                    options->image->type.type);
+            return PS_INTERPOLATE_STATUS_ERROR;
+        }
+
+        if (maskValue) {
+            if (options->mask) {
+                *maskValue = options->mask->data.PS_TYPE_MASK_DATA[yInt][xInt];
+            } else {
+                *maskValue = 0;
+            }
+        }
+    }
+    return PS_INTERPOLATE_STATUS_GOOD;
+}
+
+// Setup for interpolation by kernel
+static inline void interpolateKernelSetup(int *xNum, int *yNum, // Size of interpolation kernel, returned
+                                          int *xCentral, int *yCentral, // Central pixel of convolution
+                                          float x, float y, // Coordinates of interest
+                                          psImageInterpolateMode mode // Mode for interpolation
+                                          )
+{
+    switch (mode) {
+      case PS_INTERPOLATE_BILINEAR:
+        *xNum = *yNum = 2;
+        // Central pixel is the pixel below the point of interest
+        *xCentral = floor(x - 0.5 + FLT_EPSILON);
+        *yCentral = floor(y - 0.5 + FLT_EPSILON);
+        break;
+      case PS_INTERPOLATE_BICUBE:
+      case PS_INTERPOLATE_GAUSS:
+        *xNum = *yNum = 3;
+        // Central pixel is the closest pixel to the point of interest
+        *xCentral = x;
+        *yCentral = y;
+        break;
+      case PS_INTERPOLATE_FLAT:
+      case PS_INTERPOLATE_LANCZOS2:
+      case PS_INTERPOLATE_LANCZOS3:
+      case PS_INTERPOLATE_LANCZOS4:
+      default:
+        psAbort("Invalid interpolation mode.");
+    }
+}
+
+// Generate the interpolation kernel
+// No need to normalise to unity
+static inline void interpolateKernelGenerate(int xNum, int yNum, // Size of interpolation kernel
+                                             double kernel[xNum][yNum], // Kernel, to be set
+                                             bool *xExact, bool *yExact, // Exact shift?
+                                             int xCentral, int yCentral, // Central pixel of convolution
+                                             float x, float y, // Coordinates of interest
+                                             psImageInterpolateMode mode, // Mode for interpolation
+                                             bool allowExact // Allow exact shifts?
+                                             )
+{
+    double xFrac = x - 0.5 - xCentral; // Fraction of pixel in x
+    double yFrac = y - 0.5 - yCentral; // Fraction of pixel in y
+    *xExact = (allowExact && fabs(xFrac) < DBL_EPSILON);
+    *yExact = (allowExact && fabs(yFrac) < DBL_EPSILON);
+
+    switch (mode) {
+      case PS_INTERPOLATE_BILINEAR: {
+          kernel[0][0] = (1.0 - xFrac) * (1.0 - yFrac);
+          kernel[0][1] = xFrac * (1.0 - yFrac);
+          kernel[1][0] = yFrac * (1.0 - xFrac);
+          kernel[1][1] = xFrac * yFrac;
+          break;
+      }
+      case PS_INTERPOLATE_BICUBE: {
+          double xFrac = x - 0.5 - xCentral; // Fraction of pixel in x
+          double yFrac = y - 0.5 - yCentral; // Fraction of pixel in y
+          // Calculation variables
+          double xxFrac = xFrac * xFrac / 6.0;
+          double yyFrac = yFrac * yFrac / 6.0;
+          double xyFrac = 0.25 * xFrac * yFrac;
+          xFrac /= 6.0;
+          yFrac /= 6.0;
+          kernel[0][0] = - 1.0/9.0 - xFrac - yFrac + xxFrac + yyFrac + xyFrac;
+          kernel[0][1] = 2.0/9.0 - yFrac - 2.0 * xxFrac + yyFrac;
+          kernel[0][2] = - 1.0/9.0 + xFrac - yFrac + xxFrac + yyFrac - xyFrac;
+          kernel[1][0] = 2.0/9.0 - xFrac + xxFrac - 2.0 * yyFrac;
+          kernel[1][1] = 5.0/9.0 - 2.0 * xxFrac - 2.0 * yyFrac;
+          kernel[1][2] = 2.0/9.0 + xFrac + xxFrac - 2.0 * yyFrac;
+          kernel[2][0] = - 1.0/9.0 - xFrac + yFrac + xxFrac + yyFrac - xyFrac;
+          kernel[2][1] = 2.0/9.0 + yFrac - 2.0 * xxFrac + yyFrac;
+          kernel[2][2] = - 1.0/9.0 + xFrac + yFrac + xxFrac + yyFrac + xyFrac;
+          break;
+      }
+      case PS_INTERPOLATE_GAUSS: {
+          double xFrac = x - xCentral - 0.5; // Fraction of pixel in x
+          double yFrac = y - yCentral - 0.5; // Fraction of pixel in y
+          double sigma = 0.5;           // Gaussian sigma
+          for (int j = 0, yPos = - (yNum - 1) / 2; j < yNum; j++, yPos++) {
+              for (int i = 0, xPos = - (xNum - 1) / 2; i < xNum; i++, xPos++) {
+                  kernel[j][i] = exp(-0.5 / PS_SQR(sigma) * (PS_SQR(xPos - xFrac) +
+                                                             PS_SQR(yPos - yFrac)));
+              }
+          }
+          break;
+      }
+      case PS_INTERPOLATE_FLAT:
+      case PS_INTERPOLATE_LANCZOS2:
+      case PS_INTERPOLATE_LANCZOS3:
+      case PS_INTERPOLATE_LANCZOS4:
+      default:
+        psAbort("Invalid interpolation mode.");
+    }
+    return;
+}
+
+// Can't interpolate: kernel is off the image
+#define INTERPOLATE_OFF() { \
+        *imageValue = options->badImage; \
+        if (varianceValue) { \
+            *varianceValue = options->badVariance; \
+        } \
+        if (maskValue) { \
+            *maskValue = options->badMask; \
+        } \
+        return PS_INTERPOLATE_STATUS_OFF; \
+    }
+
+// Set up and check interpolation bounds
+// This macro defines many useful values
+#define INTERPOLATE_BOUNDS() \
+    int xLast = image->numCols - 1, yLast = image->numRows - 1; /* Last pixels of image */ \
+    /* Start and stop of kernel on image */ \
+    int xStart = xCentral - (xNum - 1) / 2, xStop = xCentral + xNum / 2; \
+    int yStart = yCentral - (yNum - 1) / 2, yStop = yCentral + yNum / 2; \
+    if (xStart > xLast || xStop < 0 || yStart > yLast || yStop < 0) { \
+        /* Interpolation kernel is entirely off the image */ \
+        INTERPOLATE_OFF(); \
+    } \
+    int xMin, xMax, yMin, yMax; /* Minimum and maximum valid pixels on image */ \
+    int iMin, iMax, jMin, jMax; /* Minimum and maximum valid pixels on kernel */ \
+    bool offImage = false; /* At least one pixel of the kernel is off the image */ \
+    if (xStart < 0) { \
+        xMin = 0; \
+        iMin = -xStart; \
+        offImage = true; \
+    } else { \
+        xMin = xStart; \
+        iMin = 0; \
+    } \
+    if (xStop > xLast) { \
+        xMax = xLast; \
+        iMax = xLast - xStart + 1; \
+        offImage = true; \
+    } else { \
+        xMax = xStop; \
+        iMax = xNum; \
+    } \
+    if (yStart < 0) { \
+        yMin = 0; \
+        jMin = -yStart; \
+        offImage = true; \
+    } else { \
+        yMin = yStart; \
+        jMin = 0; \
+    } \
+    if (yStop > yLast) { \
+        yMax = yLast; \
+        jMax = yLast - yStart + 1; \
+        offImage = true; \
+    } else { \
+        yMax = yStop; \
+        jMax = yNum; \
+    }
+
+// Refine limits if the interpolation is exact
+#define INTERPOLATE_EXACT() { \
+    if (xExact) { \
+        if (iMin > 0 || iMax < 1) { \
+            INTERPOLATE_OFF(); /* Returns */ \
+        } \
+        iMin = (xNum - 1) / 2; \
+        iMax = iMin + 1; \
+    } \
+    if (yExact) { \
+        if (jMin > 0 || jMax < 1) { \
+            INTERPOLATE_OFF(); /* Returns */ \
+        } \
+        jMin = (yNum - 1) / 2; \
+        jMax = jMin + 1; \
+    } \
+    if (xExact && yExact) { \
+        return interpolateFlat(imageValue, varianceValue, maskValue, x, y, options); \
+    } \
+}
+
+// Interpolation engine using interpolation kernel
+static psImageInterpolateStatus interpolateKernel(double *imageValue, double *varianceValue,
+                                                  psMaskType *maskValue, float x, float y,
+                                                  const psImageInterpolateOptions *options)
+{
+    // Parameters have been checked by psImageInterpolate()
+
+    int xNum, yNum;                     // Number of interpolation kernel pixels
+    int xCentral, yCentral;             // Central pixel of the convolution
+    interpolateKernelSetup(&xNum, &yNum, &xCentral, &yCentral, x, y, options->mode);
+
+    const psImage *image = options->image; // Image of interest
+    const psImage *mask = options->mask; // Image mask
+    const psImage *variance = options->variance; // Image variance
+
+    INTERPOLATE_BOUNDS();
+
+    double kernel[yNum][xNum];          // Interpolation kernel for straight interpolation
+    bool xExact, yExact;                // Exact shifts?
+    interpolateKernelGenerate(xNum, yNum, kernel, &xExact, &yExact, xCentral, yCentral,
+                              x, y, options->mode, options->shifting);
+
+    float sumKernel = 0.0;              // Sum of the kernel
+    double sumKernel2 = 0.0;            // Sum of the kernel-squared
+    float sumBad = 0.0;                 // Sum of bad kernel-squared contributions
+    psMaskType maskVal = options->maskVal; // Value to mask
+    double sumImage = 0.0;              // Sum of image multiplied by kernel
+    double sumVariance = 0.0;           // Sum of variance multiplied by kernel-squared
+
+    bool wantVariance = variance && varianceValue; // Does the user want the variance value?
+    bool haveMask = mask && maskVal; // Does the user want the variance value?
+
+    INTERPOLATE_EXACT();
+
+    // Add contributions in an area outside the image
+#define INTERPOLATE_KERNEL_ADD_OFFIMAGE() { \
+        float kernelValue = kernel[j][i]; /* Value of kernel */ \
+        double kernelValue2 = PS_SQR(kernelValue); /* Square of kernel */ \
+        sumBad += kernelValue2; \
+        sumKernel2 += kernelValue2; \
+    }
+
+    // Measure kernel contribution from outside image
+    if (offImage) {
+        if (!yExact) {
+            // Bottom rows
+            for (int j = 0; j < jMin; j++) {
+                for (int i = 0; i < xNum; i++) {
+                    INTERPOLATE_KERNEL_ADD_OFFIMAGE();
+                }
+            }
+        }
+        if (!xExact) {
+            // Two sides
+            for (int j = jMin; j < jMax; j++) {
+                for (int i = 0; i < iMin; i++) {
+                    INTERPOLATE_KERNEL_ADD_OFFIMAGE();
+                }
+                for (int i = iMax + 1; i < xNum; i++) {
+                    INTERPOLATE_KERNEL_ADD_OFFIMAGE();
+                }
+            }
+        }
+        if (!yExact) {
+            // Top rows
+            for (int j = jMax + 1; j < yNum; j++) {
+                for (int i = 0; i < xNum; i++) {
+                    INTERPOLATE_KERNEL_ADD_OFFIMAGE();
+                }
+            }
+        }
+    }
+
+#define INTERPOLATE_KERNEL_CASE(TYPE) \
+  case PS_TYPE_##TYPE: { \
+      if (wantVariance) { \
+          if (haveMask) { \
+              /* Variance and mask */ \
+              for (int j = jMin, yPix = yMin; j < jMax; j++, yPix++) { \
+                  for (int i = iMin, xPix = xMin; i < iMax; i++, xPix++) { \
+                      float kernelValue = kernel[j][i]; /* Value of kernel */ \
+                      double kernelValue2 = PS_SQR(kernelValue); /* Square of kernel */ \
+                      if (mask->data.PS_TYPE_MASK_DATA[yPix][xPix] & maskVal) { \
+                          sumBad += kernelValue2; \
+                      } else { \
+                          sumImage += kernelValue * image->data.TYPE[yPix][xPix]; \
+                          sumVariance += kernelValue2 * variance->data.TYPE[yPix][xPix]; \
+                          sumKernel += kernelValue; \
+                      } \
+                      sumKernel2 += PS_SQR(kernelValue); \
+                  } \
+              } \
+              \
+          } else { \
+              /* Variance, no mask */ \
+              for (int j = jMin, yPix = yMin; j < jMax; j++, yPix++) { \
+                  for (int i = iMin, xPix = xMin; i < iMax; i++, xPix++) { \
+                      float kernelValue = kernel[j][i]; /* Value of kernel */ \
+                      double kernelValue2 = PS_SQR(kernelValue); /* Square of kernel */ \
+                      sumImage += kernelValue * image->data.TYPE[yPix][xPix]; \
+                      sumVariance += kernelValue2 * variance->data.TYPE[yPix][xPix]; \
+                      sumKernel += kernelValue; \
+                      sumKernel2 += kernelValue2; \
+                  } \
+              } \
+          } \
+      } else if (haveMask) { \
+          /* Mask, no variance */ \
+          for (int j = jMin, yPix = yMin; j < jMax; j++, yPix++) { \
+              for (int i = iMin, xPix = xMin; i < iMax; i++, xPix++) { \
+                  float kernelValue = kernel[j][i]; /* Value of kernel */ \
+                  if (mask->data.PS_TYPE_MASK_DATA[yPix][xPix] & maskVal) { \
+                      sumBad += PS_SQR(kernelValue); \
+                  } else { \
+                      sumImage += kernelValue * image->data.TYPE[yPix][xPix]; \
+                      sumKernel += kernelValue; \
+                  } \
+                  sumKernel2 += PS_SQR(kernelValue); \
+              } \
+          } \
+      } else { \
+          /* Neither variance nor mask */ \
+          for (int j = jMin, yPix = yMin; j < jMax; j++, yPix++) { \
+              for (int i = iMin, xPix = xMin; i < iMax; i++, xPix++) { \
+                  float kernelValue = kernel[j][i]; /* Value of kernel */ \
+                  sumImage += kernelValue * image->data.TYPE[yPix][xPix]; \
+                  sumKernel += kernelValue; \
+              } \
+          } \
+      } \
+    } \
+    break;
+
+
+    switch (image->type.type) {
+        INTERPOLATE_KERNEL_CASE(U8);
+        INTERPOLATE_KERNEL_CASE(U16);
+        INTERPOLATE_KERNEL_CASE(U32);
+        INTERPOLATE_KERNEL_CASE(U64);
+        INTERPOLATE_KERNEL_CASE(S8);
+        INTERPOLATE_KERNEL_CASE(S16);
+        INTERPOLATE_KERNEL_CASE(S32);
+        INTERPOLATE_KERNEL_CASE(S64);
+        INTERPOLATE_KERNEL_CASE(F32);
+        INTERPOLATE_KERNEL_CASE(F64);
+      default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unrecognised type for image: %x",
+                image->type.type);
+        return PS_INTERPOLATE_STATUS_ERROR;
+    }
+
+    psImageInterpolateStatus status = PS_INTERPOLATE_STATUS_ERROR; // Status of interpolation
+    *imageValue = sumImage / sumKernel;
+    if (wantVariance) {
+        *varianceValue = sumVariance * PS_SQR(sumKernel) / sumKernel2;
+    }
+    if (sumBad == 0) {
+        // Completely good pixel
+        status = PS_INTERPOLATE_STATUS_GOOD;
+    } else if (sumBad / sumKernel2 < PS_SQR(options->poorFrac)) {
+        // Some pixels masked: poor pixel
+        if (haveMask && maskValue) {
+            *maskValue |= options->poorMask;
+        }
+        status = PS_INTERPOLATE_STATUS_POOR;
+    } else {
+        // Many pixels (or a few important pixels) masked: bad pixel
+        if (haveMask && maskValue) {
+            *maskValue |= options->badMask;
+        }
+        status = PS_INTERPOLATE_STATUS_BAD;
+    }
+
+    return status;
+}
+
+
+// Generate Lanczos interpolation kernels
+static void lanczos(double values[],    // Interpolation kernel to generate
+                    bool *exact,        // Exact shift?
+                    int num,            // Number of values in the kernel
+                    float frac,         // Sub-pixel position
+                    bool allowExact     // Allow exact shift?
+    )
+{
+    if (allowExact && fabs(frac) < DBL_EPSILON) {
+        *exact = true;
+        // No real shift
+        for (int i = 0; i < (num - 1) / 2; i++) {
+            values[i] = 0.0;
+        }
+        values[(num - 1) / 2] = 1.0;
+        for (int i = (num - 1) / 2 + 1; i < num; i++) {
+            values[i] = 0.0;
+        }
+    } else {
+        *exact = false;
+        double norm1 = 2.0 / PS_SQR(M_PI); // Normalisation for laczos
+        double norm2 = M_PI * 4.0 / (float)num; // Normalisation for sinc function 1
+        double norm3 =M_PI_2 * 4.0 / (float)num; // Normalisation for sinc function 2
+        double pos = - (num - 1)/2 - frac;  // Position of interest
+        for (int i = 0; i < num; i++, pos += 1.0) {
+            if (fabs(pos) < DBL_EPSILON) {
+                values[i] = 1.0;
+            } else {
+                values[i] = norm1 * sin(pos * norm2) * sin(pos * norm3) / PS_SQR(pos);
+            }
+        }
+    }
+
+    return;
+}
+
+// Setup for interpolation by separable kernels
+static inline void interpolateSeparateSetup(int *xNum, int *yNum, // Size of interpolation kernel, returned
+                                            int *xCentral, int *yCentral, // Central pixel of convolution
+                                            float x, float y, // Coordinates of interest
+                                            psImageInterpolateMode mode // Mode for interpolation
+                                            )
+{
+    // Central pixel is the pixel below the point of interest
+    *xCentral = floor(x - 0.5);
+    *yCentral = floor(y - 0.5);
+    switch (mode) {
+      case PS_INTERPOLATE_LANCZOS2:
+        *xNum = *yNum = 4;
+        break;
+      case PS_INTERPOLATE_LANCZOS3:
+        *xNum = *yNum = 6;
+        break;
+      case PS_INTERPOLATE_LANCZOS4:
+        *xNum = *yNum = 8;
+        break;
+      case PS_INTERPOLATE_FLAT:
+      case PS_INTERPOLATE_BILINEAR:
+      case PS_INTERPOLATE_BICUBE:
+      case PS_INTERPOLATE_GAUSS:
+      default:
+        psAbort("Invalid interpolation mode.");
+    }
+}
+
+// Generate the interpolation kernels for separable case; they should be normalised to unity
+static inline void interpolateSeparateGenerate(int xNum, int yNum, // Size of interpolation kernel
+                                               double xKernel[xNum], double yKernel[yNum], // Kernels
+                                               bool *xExact, bool *yExact, // Exact shifts?
+                                               int xCentral, int yCentral, // Central pixel of convolution
+                                               float x, float y, // Coordinates of interest
+                                               psImageInterpolateMode mode, // Mode for interpolation
+                                               bool allowExact // Allow an exact shift?
+                                               )
+{
+    // XXX Could put in an "exact shift" (i.e., xFrac = 0.0) version
+    switch (mode) {
+      case PS_INTERPOLATE_LANCZOS2:
+      case PS_INTERPOLATE_LANCZOS3:
+      case PS_INTERPOLATE_LANCZOS4: {
+          double xFrac = x - xCentral - 0.5; // Fraction of pixel in x
+          lanczos(xKernel, xExact, xNum, xFrac, allowExact);
+          double yFrac = y - yCentral - 0.5; // Fraction of pixel in y
+          lanczos(yKernel, yExact, yNum, yFrac, allowExact);
+          break;
+      }
+      case PS_INTERPOLATE_FLAT:
+      case PS_INTERPOLATE_BILINEAR:
+      case PS_INTERPOLATE_BICUBE:
+      case PS_INTERPOLATE_GAUSS:
+      default:
+        psAbort("Invalid interpolation mode.");
+    }
+}
+
+// Interpolation engine for separable interpolation kernels (either for good reasons or for practical reasons)
+static psImageInterpolateStatus interpolateSeparate(double *imageValue, double *varianceValue,
+                                                    psMaskType *maskValue, float x, float y,
+                                                    const psImageInterpolateOptions *options)
+{
+    // Parameters have been checked by psImageInterpolate()
+
+    int xNum, yNum;                     // Number of interpolation kernel pixels
+    int xCentral, yCentral; // Central pixel of the convolution
+    interpolateSeparateSetup(&xNum, &yNum, &xCentral, &yCentral, x, y, options->mode);
+
+    const psImage *image = options->image; // Image of interest
+    const psImage *mask = options->mask; // Image mask
+    const psImage *variance = options->variance; // Image variance
+
+    INTERPOLATE_BOUNDS();
+
+    double xKernel[xNum], yKernel[yNum]; // Interpolation kernels
+    bool xExact, yExact;                // Exact shift?
+    interpolateSeparateGenerate(xNum, yNum, xKernel, yKernel, &xExact, &yExact,
+                                xCentral, yCentral, x, y, options->mode, options->shifting);
+
+    float sumKernel = 0.0;              // Sum of the kernel
+    double sumKernel2 = 0.0;            // Sum of the kernel-squared
+    float sumBad = 0.0;                 // Sum of bad kernel-squared contributions
+    psMaskType maskVal = options->maskVal; // Value to mask
+    double sumImage = 0.0;              // Sum of image multiplied by kernel
+    double sumVariance = 0.0;           // Sum of variance multiplied by kernel-squared
+
+    bool wantVariance = variance && varianceValue; // Does the user want the variance value?
+    bool haveMask = mask && maskVal; // Does the user want the variance value?
+
+    INTERPOLATE_EXACT();
+
+    // Add contributions in an area outside the image
+#define INTERPOLATE_SEPARATE_SETUP_OFFIMAGE_COL() \
+    float xSumBad = 0.0; \
+    double xSumKernel2 = 0.0;
+#define INTERPOLATE_SEPARATE_ADD_OFFIMAGE_COL() { \
+        float kernelValue = xKernel[i]; /* Value of kernel */ \
+        double kernelValue2 = PS_SQR(kernelValue); /* Square of kernel */ \
+        xSumBad += kernelValue2; \
+        xSumKernel2 += kernelValue2; \
+    }
+#define INTERPOLATE_SEPARATE_ADD_OFFIMAGE_ROW() { \
+        float kernelValue = yKernel[j]; /* Value of kernel */ \
+        double kernelValue2 = PS_SQR(kernelValue); /* Square of kernel */ \
+        sumBad += kernelValue2 * xSumBad; \
+        sumKernel2 += kernelValue2 * xSumKernel2; \
+    }
+
+    // Measure kernel contribution from outside image
+    if (offImage) {
+        // Bottom rows
+        if (!yExact) {
+            for (int j = 0; j < jMin; j++) {
+                INTERPOLATE_SEPARATE_SETUP_OFFIMAGE_COL();
+                for (int i = 0; i < xNum; i++) {
+                    INTERPOLATE_SEPARATE_ADD_OFFIMAGE_COL();
+                }
+                INTERPOLATE_SEPARATE_ADD_OFFIMAGE_ROW();
+            }
+        }
+        // Two sides
+        if (!xExact) {
+            for (int j = jMin; j < jMax; j++) {
+                INTERPOLATE_SEPARATE_SETUP_OFFIMAGE_COL();
+                for (int i = 0; i < iMin; i++) {
+                    INTERPOLATE_SEPARATE_ADD_OFFIMAGE_COL();
+                }
+                for (int i = iMax + 1; i < xNum; i++) {
+                    INTERPOLATE_SEPARATE_ADD_OFFIMAGE_COL();
+                }
+                INTERPOLATE_SEPARATE_ADD_OFFIMAGE_ROW();
+            }
+        }
+        // Top rows
+        if (!yExact) {
+            for (int j = jMax + 1; j < yNum; j++) {
+                if (!xExact) {
+                    INTERPOLATE_SEPARATE_SETUP_OFFIMAGE_COL();
+                    for (int i = 0; i < xNum; i++) {
+                        INTERPOLATE_SEPARATE_ADD_OFFIMAGE_COL();
+                    }
+                    INTERPOLATE_SEPARATE_ADD_OFFIMAGE_ROW();
+                }
+            }
+        }
+    }
+
+#define INTERPOLATE_SEPARATE_CASE(TYPE) \
+  case PS_TYPE_##TYPE: { \
+      if (wantVariance) { \
+          if (haveMask) { \
+              /* Variance and mask */ \
+              for (int j = jMin, yPix = yMin; j < jMax; j++, yPix++) { \
+                  double xSumImage = 0.0; /* Sum of image multiplied by kernel in x */ \
+                  double xSumVariance = 0.0; /* Sum of image multiplied by kernel-squared in x */ \
+                  float xSumKernel = 0.0; /* Sum of kernel in x */ \
+                  double xSumKernel2 = 0.0; /* Sum of kernel-squared in x */ \
+                  float xSumBad = 0.0; /* Sum of bad kernel-squared in x */ \
+                  for (int i = iMin, xPix = xMin; i < iMax; i++, xPix++) { \
+                      float kernelValue = xKernel[i]; /* Value of kernel in x */ \
+                      double kernelValue2 = PS_SQR(kernelValue); /* Square of kernel in x */ \
+                      if (mask->data.PS_TYPE_MASK_DATA[yPix][xPix] & maskVal) { \
+                          xSumBad += kernelValue2; \
+                      } else { \
+                          xSumImage += kernelValue * image->data.TYPE[yPix][xPix]; \
+                          xSumVariance += kernelValue2 * variance->data.TYPE[yPix][xPix]; \
+                          xSumKernel += kernelValue; \
+                      } \
+                      xSumKernel2 += kernelValue2; \
+                  } \
+                  float kernelValue = yKernel[j]; /* Value of kernel in y */ \
+                  double kernelValue2 = PS_SQR(kernelValue); /* Value of kernel-squared in y */ \
+                  sumImage += kernelValue * xSumImage; \
+                  sumVariance += kernelValue2 * xSumVariance; \
+                  sumBad += kernelValue2 * xSumBad; \
+                  sumKernel += kernelValue * xSumKernel; \
+                  sumKernel2 += kernelValue2 * xSumKernel2; \
+              } \
+          } else { \
+              /* Variance, no mask */ \
+              for (int j = jMin, yPix = yMin; j < jMax; j++, yPix++) { \
+                  double xSumImage = 0.0; /* Sum of image multiplied by kernel in x */ \
+                  double xSumVariance = 0.0; /* Sum of image multiplied by kernel-squared in x */ \
+                  float xSumKernel = 0.0; /* Sum of kernel in x */ \
+                  double xSumKernel2 = 0.0; /* Sum of kernel-squared in x */ \
+                  for (int i = iMin, xPix = xMin; i < iMax; i++, xPix++) { \
+                      float kernelValue = xKernel[i]; /* Value of kernel */ \
+                      double kernelValue2 = PS_SQR(kernelValue); /* Square of kernel */ \
+                      xSumImage += kernelValue * image->data.TYPE[yPix][xPix]; \
+                      xSumVariance += kernelValue2 * variance->data.TYPE[yPix][xPix]; \
+                      xSumKernel += kernelValue; \
+                      xSumKernel2 += kernelValue2; \
+                  } \
+                  float kernelValue = yKernel[j]; /* Value of kernel in y */ \
+                  double kernelValue2 = PS_SQR(kernelValue); /* Value of kernel-squared in y */ \
+                  sumImage += kernelValue * xSumImage; \
+                  sumVariance += kernelValue2 * xSumVariance; \
+                  sumKernel += kernelValue * xSumKernel; \
+                  sumKernel2 += kernelValue2 * xSumKernel2; \
+              } \
+          } \
+      } else if (haveMask) { \
+          /* Mask, no variance */ \
+          for (int j = jMin, yPix = yMin; j < jMax; j++, yPix++) { \
+              double xSumImage = 0.0; /* Sum of image multiplied by kernel in x */ \
+              float xSumKernel = 0.0; /* Sum of kernel in x */ \
+              double xSumKernel2 = 0.0; /* Sum of kernel-squared in x */ \
+              float xSumBad = 0.0; /* Sum of bad kernel-squared in x */ \
+              for (int i = iMin, xPix = xMin; i < iMax; i++, xPix++) { \
+                  float kernelValue = xKernel[i]; /* Value of kernel */ \
+                  double kernelValue2 = PS_SQR(kernelValue); /* Value of kernel-squared */ \
+                  if (mask->data.PS_TYPE_MASK_DATA[yPix][xPix] & maskVal) { \
+                      xSumBad += kernelValue2; \
+                  } else { \
+                      xSumImage += kernelValue * image->data.TYPE[yPix][xPix]; \
+                      xSumKernel += kernelValue; \
+                  } \
+                  xSumKernel2 += kernelValue2; \
+              } \
+              float kernelValue = yKernel[j]; /* Value of kernel in y */ \
+              double kernelValue2 = PS_SQR(kernelValue); /* Value of kernel-squared in y */ \
+              sumImage += kernelValue * xSumImage; \
+              sumBad += kernelValue2 * xSumBad; \
+              sumKernel += kernelValue * xSumKernel; \
+              sumKernel2 += kernelValue2 * xSumKernel2; \
+          } \
+      } else {\
+          /* Neither variance nor mask */ \
+          for (int j = jMin, yPix = yMin; j < jMax; j++, yPix++) { \
+              double xSumImage = 0.0; /* Sum of image multiplied by kernel in x */ \
+              float xSumKernel = 0.0; /* Sum of kernel in x */ \
+              for (int i = iMin, xPix = xMin; i < iMax; i++, xPix++) { \
+                  float kernelValue = xKernel[i]; /* Value of kernel */ \
+                  xSumImage += kernelValue * image->data.TYPE[yPix][xPix]; \
+                  xSumKernel += kernelValue; \
+              } \
+              float kernelValue = yKernel[j]; /* Value of kernel in y */ \
+              sumImage += kernelValue * xSumImage; \
+              sumKernel += kernelValue * xSumKernel; \
+          } \
+      } \
+    } \
+    break;
+
+
+    switch (image->type.type) {
+        INTERPOLATE_SEPARATE_CASE(U8);
+        INTERPOLATE_SEPARATE_CASE(U16);
+        INTERPOLATE_SEPARATE_CASE(U32);
+        INTERPOLATE_SEPARATE_CASE(U64);
+        INTERPOLATE_SEPARATE_CASE(S8);
+        INTERPOLATE_SEPARATE_CASE(S16);
+        INTERPOLATE_SEPARATE_CASE(S32);
+        INTERPOLATE_SEPARATE_CASE(S64);
+        INTERPOLATE_SEPARATE_CASE(F32);
+        INTERPOLATE_SEPARATE_CASE(F64);
+      default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unrecognised type for image: %x",
+                image->type.type);
+        return PS_INTERPOLATE_STATUS_ERROR;
+    }
+
+    psImageInterpolateStatus status = PS_INTERPOLATE_STATUS_ERROR; // Status of interpolation
+    *imageValue = sumImage / sumKernel;
+    if (wantVariance) {
+        *varianceValue = sumVariance * PS_SQR(sumKernel) / sumKernel2;
+    }
+    if (sumBad == 0) {
+        // Completely good pixel
+        status = PS_INTERPOLATE_STATUS_GOOD;
+    } else if (sumBad / sumKernel2 < PS_SQR(options->poorFrac)) {
+        // Some pixels masked: poor pixel
+        if (haveMask && maskValue) {
+            *maskValue |= options->poorMask;
+        }
+        status = PS_INTERPOLATE_STATUS_POOR;
+    } else {
+        // Many pixels (or a few important pixels) masked: bad pixel
+        if (haveMask && maskValue) {
+            *maskValue |= options->badMask;
+        }
+        status = PS_INTERPOLATE_STATUS_BAD;
+    }
+
+    return status;
+}
+
+
+
+psImageInterpolateStatus psImageInterpolate(double *imageValue, double *varianceValue, psMaskType *maskValue,
+                                            float x, float y, const psImageInterpolateOptions *options)
+{
+    PS_ASSERT_PTR_NON_NULL(imageValue, PS_INTERPOLATE_STATUS_ERROR);
+    PS_ASSERT_PTR_NON_NULL(options, PS_INTERPOLATE_STATUS_ERROR);
+
+    const psImage *image = options->image; // Image to interpolate
+    const psImage *mask = options->mask; // Mask to interpolate
+    const psImage *variance = options->variance; // Variance to interpolate
+
+    PS_ASSERT_IMAGE_NON_NULL(image, PS_INTERPOLATE_STATUS_ERROR);
+    if (varianceValue && variance) {
+        PS_ASSERT_IMAGE_NON_NULL(variance, PS_INTERPOLATE_STATUS_ERROR);
+        PS_ASSERT_IMAGE_TYPE(variance, image->type.type, PS_INTERPOLATE_STATUS_ERROR);
+        psAssert(image->numCols == variance->numCols && image->numRows == variance->numRows,
+                 "Image and variance sizes");
+    }
+    if (maskValue && mask) {
+        PS_ASSERT_IMAGE_NON_NULL(mask, PS_INTERPOLATE_STATUS_ERROR);
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, PS_INTERPOLATE_STATUS_ERROR);
+        psAssert(image->numCols == mask->numCols && image->numRows == mask->numRows, "Image and mask sizes");
+    }
+
+    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(options->poorFrac, 0.0, PS_INTERPOLATE_STATUS_ERROR);
+
+    switch (options->mode) {
+      case PS_INTERPOLATE_FLAT:
+        return interpolateFlat(imageValue, varianceValue, maskValue, x, y, options);
+      case PS_INTERPOLATE_BILINEAR:
+      case PS_INTERPOLATE_BICUBE:
+      case PS_INTERPOLATE_GAUSS:
+        return interpolateKernel(imageValue, varianceValue, maskValue, x, y, options);
+      case PS_INTERPOLATE_LANCZOS2:
+      case PS_INTERPOLATE_LANCZOS3:
+      case PS_INTERPOLATE_LANCZOS4:
+        return interpolateSeparate(imageValue, varianceValue, maskValue, x, y, options);
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified interpolation method (%d) is not supported."),
+                options->mode);
+        return PS_INTERPOLATE_STATUS_ERROR;
+    }
+
+    psAbort("Should never reach here.");
+    return PS_INTERPOLATE_STATUS_ERROR;
+}
+
+
+static float varianceFactorFlat(float x, float y, const psImageInterpolateOptions *options)
+{
+    // There's no smearing
+    return 1.0;
+}
+
+static float varianceFactorKernel(float x, float y, const psImageInterpolateOptions *options)
+{
+    int xNum, yNum;                     // Number of interpolation kernel pixels
+    int xCentral, yCentral;             // Central pixel of the convolution
+    interpolateKernelSetup(&xNum, &yNum, &xCentral, &yCentral, x, y, options->mode);
+
+    const psImage *image = options->image; // Image of interest
+    int xLast = image->numCols - 1;     // Last pixel in x
+    int yLast = image->numRows - 1;     // Last pixel in y
+
+    if (xCentral - (xNum - 1) / 2 < 0 || xCentral + xNum / 2 > xLast ||
+        yCentral - (yNum - 1) / 2 < 0 || yCentral + yNum / 2 > yLast) {
+        // At least one pixel of the interpolation kernel is off the image
+        return NAN;
+    }
+
+    double kernel[yNum][xNum];          // Interpolation kernel for straight interpolation
+    bool xExact, yExact;                // Exact shift?
+    // Pretend we're not shifting pixels (i.e., we don't care about any exact shifting) because on the off
+    // chance that a shift is integral, we don't want to get a trivial kernel back, but something that sort of
+    // reflects what's going on.
+    interpolateKernelGenerate(xNum, yNum, kernel, &xExact, &yExact, xCentral, yCentral,
+                              x, y, options->mode, false);
+
+    double sumKernel = 0.0;             // Sum of kernel
+    double sumKernel2 = 0.0;            // Sum of kernel squares
+    for (int j = 0; j < yNum; j++) {
+        for (int i = 0; i < xNum; i++) {
+            float value = kernel[j][i]; // Value of kernel
+            sumKernel += value;
+            sumKernel2 += PS_SQR(value);
+        }
+    }
+
+    return sumKernel2 / PS_SQR(sumKernel);
+}
+
+static float varianceFactorSeparate(float x, float y, const psImageInterpolateOptions *options)
+{
+    int xNum, yNum;                     // Number of interpolation kernel pixels
+    int xCentral, yCentral;             // Central pixel of the convolution
+    interpolateSeparateSetup(&xNum, &yNum, &xCentral, &yCentral, x, y, options->mode);
+
+    const psImage *image = options->image; // Image of interest
+    int xLast = image->numCols - 1;     // Last pixel in x
+    int yLast = image->numRows - 1;     // Last pixel in y
+
+    if (xCentral - (xNum - 1) / 2 < 0 || xCentral + xNum / 2 > xLast ||
+        yCentral - (yNum - 1) / 2 < 0 || yCentral + yNum / 2 > yLast) {
+        // At least one pixel of the interpolation kernel is off the image
+        return NAN;
+    }
+
+    double xKernel[xNum], yKernel[yNum]; // Interpolation kernels for separable interpolation
+    bool xExact, yExact;                // Exact shift?
+    // Pretend we're not shifting pixels (i.e., we don't care about any exact shifting) because on the off
+    // chance that a shift is integral, we don't want to get a trivial kernel back, but something that sort of
+    // reflects what's going on.
+    interpolateSeparateGenerate(xNum, yNum, xKernel, yKernel, &xExact, &yExact,
+                                xCentral, yCentral, x, y, options->mode, false);
+
+    double ySumKernel = 0.0;            // Sum of kernel in y
+    double ySumKernel2 = 0.0;           // Sum of kernel squared in y
+    for (int j = 0; j < yNum; j++) {
+        double xSumKernel = 0.0;        // Sum of kernel in x
+        double xSumKernel2 = 0.0;       // Sum of kernel squared in x
+        for (int i = 0; i < xNum; i++) {
+            float value = xKernel[i];   // Value of kernel
+            xSumKernel += value;
+            xSumKernel2 += PS_SQR(value);
+        }
+        float value = yKernel[j];       // Value of kernel
+        ySumKernel += xSumKernel * value;
+        ySumKernel2 += xSumKernel2 * PS_SQR(value);
+    }
+
+    return ySumKernel2 / PS_SQR(ySumKernel);
+}
+
+float psImageInterpolateVarianceFactor(float x, float y, const psImageInterpolateOptions *options)
+{
+    PS_ASSERT_PTR_NON_NULL(options, PS_INTERPOLATE_STATUS_ERROR);
+
+    switch (options->mode) {
+      case PS_INTERPOLATE_FLAT:
+        return varianceFactorFlat(x, y, options);
+      case PS_INTERPOLATE_BILINEAR:
+      case PS_INTERPOLATE_BICUBE:
+      case PS_INTERPOLATE_GAUSS:
+        return varianceFactorKernel(x, y, options);
+      case PS_INTERPOLATE_LANCZOS2:
+      case PS_INTERPOLATE_LANCZOS3:
+      case PS_INTERPOLATE_LANCZOS4:
+        return varianceFactorSeparate(x, y, options);
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified interpolation method (%d) is not supported."),
+                options->mode);
+        return NAN;
+    }
+
+    psAbort("Should never reach here.");
+    return NAN;
+}
+
+
+psImageInterpolateMode psImageInterpolateModeFromString(const char *name)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, PS_INTERPOLATE_NONE);
+
+    if (!strcasecmp(name, "FLAT"))     return PS_INTERPOLATE_FLAT;
+    if (!strcasecmp(name, "BILINEAR")) return PS_INTERPOLATE_BILINEAR;
+    if (!strcasecmp(name, "BICUBE"))   return PS_INTERPOLATE_BICUBE;
+    if (!strcasecmp(name, "GAUSS"))    return PS_INTERPOLATE_GAUSS;
+    if (!strcasecmp(name, "LANCZOS2")) return PS_INTERPOLATE_LANCZOS2;
+    if (!strcasecmp(name, "LANCZOS3")) return PS_INTERPOLATE_LANCZOS3;
+    if (!strcasecmp(name, "LANCZOS4")) return PS_INTERPOLATE_LANCZOS4;
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Unknown interpolation type %s"), name);
+    return PS_INTERPOLATE_NONE;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageInterpolate.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageInterpolate.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageInterpolate.h	(revision 22322)
@@ -0,0 +1,96 @@
+/* @file  psImageInterpolate.h
+ * @brief Functions for interpolating an image
+ *
+ * @author Robert DeSonia, MHPCC
+ * @author Ross Harman, MHPCC
+ * @author Joshua Hoblitt, University of Hawaii
+ * @author Paul Price, Institute for Astronomy
+ *
+ * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-21 01:12:44 $
+ * Copyright 2004-2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_INTERPOLATE_H
+#define PS_IMAGE_INTERPOLATE_H
+
+
+/// Enumeration of options in interpolation
+typedef enum {
+    PS_INTERPOLATE_NONE,               ///< no interpolate defined (error state)
+    PS_INTERPOLATE_FLAT,               ///< Flat interpolation (nearest pixel)
+    PS_INTERPOLATE_BILINEAR,           ///< Bilinear interpolation
+    PS_INTERPOLATE_BICUBE,             ///< Bicubic interpolation with 3x3 region
+    PS_INTERPOLATE_GAUSS,              ///< Gaussian inteprolation with 3x3 region
+    PS_INTERPOLATE_LANCZOS2,           ///< Sinc interpolation with 4x4 pixel kernel
+    PS_INTERPOLATE_LANCZOS3,           ///< Sinc interpolation with 6x6 pixel kernel
+    PS_INTERPOLATE_LANCZOS4,           ///< Sinc interpolation with 8x8 pixel kernel
+} psImageInterpolateMode;
+
+/// Status of interpolation
+typedef enum {
+    PS_INTERPOLATE_STATUS_ERROR = 0,    ///< There was an error
+    PS_INTERPOLATE_STATUS_OFF,          ///< The pixel fell completely off the image or in the border
+    PS_INTERPOLATE_STATUS_BAD,          ///< The pixel is bad
+    PS_INTERPOLATE_STATUS_POOR,         ///< The pixel is poor
+    PS_INTERPOLATE_STATUS_GOOD,         ///< The pixel is good
+} psImageInterpolateStatus;
+
+/// Options for general interpolation.
+///
+/// We stuff in here all the constant values when doing interpolation, so that not all of it has to be pushed
+/// onto the stack in the middle of a tight loop.  For this reason, even the image, mask and variance map are
+/// included.
+typedef struct {
+    psImageInterpolateMode mode;        ///< Interpolation mode
+    const psImage *image;               ///< Input image for interpolation
+    const psImage *variance;            ///< Variance image for interpolation
+    const psImage *mask;                ///< Mask image for interpolation
+    psMaskType maskVal;                 ///< Value to mask
+    double badImage;                    ///< Image value if x,y location is not good
+    double badVariance;                 ///< Variance value if x,y location is not good
+    psMaskType badMask;                 ///< Mask value to give bad pixels
+    psMaskType poorMask;                ///< Mask value to give poor pixels
+    float poorFrac;                     ///< Fraction of flux in bad pixels before output is marked bad
+    bool shifting;                      ///< Shifting images? Don't interpolate if the shift is exact.
+} psImageInterpolateOptions;
+
+
+/// Allocator
+psImageInterpolateOptions *psImageInterpolateOptionsAlloc(psImageInterpolateMode mode, // Interpolation mode
+                                                          const psImage *image, // Input image
+                                                          const psImage *variance,  // Variance image
+                                                          const psImage *mask, // Mask image
+                                                          psMaskType maskVal, // Value to mask
+                                                          double badImage, // Value for image if bad
+                                                          double badVariance, // Value for variance if bad
+                                                          psMaskType badMask, // Mask value for bad pixels
+                                                          psMaskType poorMask, // Mask value for poor pixels
+                                                          float poorFrac // Fraction of flux for question
+    ) PS_ATTR_MALLOC;
+
+
+
+/// Interpolate image pixel value given floating point coordinates.
+psImageInterpolateStatus psImageInterpolate(double *imageValue, ///< Return value for image
+                                            double *varianceValue, ///< Return value for variance
+                                            psMaskType *maskValue, ///< Return value for mask
+                                            float x, float y, ///< Location to which to interpolate
+                                            const psImageInterpolateOptions *options ///< Options
+    );
+
+
+// Return the appropriate interpolation mode given a char string name for that mode
+psImageInterpolateMode psImageInterpolateModeFromString(const char *name // Mode name
+    );
+
+/// Return the variance factor for the appropriate position
+///
+/// psImageInterpolate sets the variance appropriate for extended regions (on the scale of the interpolation
+/// kernel), but this is not appropriate for pixel-to-pixel statistics.  This function returns the conversion
+/// factor.
+float psImageInterpolateVarianceFactor(float x, float y, ///< Position of interest
+                                       const psImageInterpolateOptions *options ///< Interpolation options
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMap.c	(revision 22322)
@@ -0,0 +1,329 @@
+/** @file  psImageMap.c
+ *
+ *  @brief Functions define a 2d coarse representation of a finer 2D field
+ *
+ *  @ingroup Image
+ *
+ *  @author Eugene Magnier, IfA
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-12-15 01:20:03 $
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include "psError.h"
+#include "psAbort.h"
+
+#include "psFits.h"
+#include "psAssert.h"
+#include "psRegion.h"
+#include "psFitsImage.h"
+
+#include "psMemory.h"
+#include "psVector.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psImageBinning.h"
+#include "psImagePixelInterpolate.h"
+#include "psImageUnbin.h"
+
+#include "psImageMap.h"
+
+static void psImageMapFree(psImageMap *map) {
+
+    if (!map) return;
+
+    psFree(map->map);
+    psFree(map->error);
+    psFree(map->stats);
+    psFree(map->binning);
+
+    return;
+}
+
+psImageMap *psImageMapAlloc(const psImage *field, psImageBinning *binning, psStats *stats) {
+    PS_ASSERT_IMAGE_NON_NULL(field, NULL);
+    assert (binning);
+    assert (stats);
+
+    psImageMap *map = (psImageMap*)psAlloc(sizeof(psImageMap));
+    psMemSetDeallocator(map, (psFreeFunc)psImageMapFree);
+
+    map->binning = psMemIncrRefCounter (binning);
+    map->stats   = psMemIncrRefCounter (stats);
+
+    map->col0 = field->col0;
+    map->row0 = field->row0;
+    map->numCols = field->numCols;
+    map->numRows = field->numRows;
+
+    map->map     = psImageAlloc (binning->nXruff, binning->nYruff, PS_TYPE_F32);
+    psImageInit (map->map, 0.0);
+
+    map->error   = psImageAlloc (binning->nXruff, binning->nYruff, PS_TYPE_F32);
+    psImageInit (map->error, 0.0);
+
+    psImageBinningSetScale (map->binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkipByOffset (map->binning, map->col0, map->row0);
+
+    return map;
+}
+
+bool psMemCheckImageMap(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) psImageMapFree);
+}
+
+// allocate the image map using the psImageBinning supplied
+psImageMap *psImageMapNoImageAlloc(psImageBinning *binning, psStats *stats) {
+
+    assert (binning);
+    assert (stats);
+
+    psImageMap *map = (psImageMap*)psAlloc(sizeof(psImageMap));
+    psMemSetDeallocator(map, (psFreeFunc)psImageMapFree);
+
+    map->stats   = psMemIncrRefCounter (stats);
+    map->binning = psMemIncrRefCounter (binning);
+
+    map->map     = psImageAlloc (binning->nXruff, binning->nYruff, PS_TYPE_F32);
+    psImageInit (map->map, 0.0);
+
+    map->error   = psImageAlloc (binning->nXruff, binning->nYruff, PS_TYPE_F32);
+    psImageInit (map->error, 0.0);
+
+    return map;
+}
+
+bool psImageMapModifyScale(psImageMap *map, int nXruff, int nYruff)
+{
+    assert (map);
+
+    map->binning->nXruff = nXruff;
+    map->binning->nYruff = nYruff;
+
+    psImageRecycle (map->map, nXruff, nYruff, PS_TYPE_F32);
+    psImageRecycle (map->error, nXruff, nYruff, PS_TYPE_F32);
+
+    psImageBinningSetScale (map->binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkipByOffset (map->binning, map->col0, map->row0);
+
+    return true;
+}
+
+// generate a psImageMap (or NULL) with the given number of superpixels in X and Y
+// this function returns an error if the output map has impossible holes
+bool psImageMapGenerate(psImageMap *map, const psVector *x, const psVector *y,
+                        const psVector *f, const psVector *df, float badFrac)
+{
+    // XXX asserts
+
+    psImage *mask = psImageAlloc (map->map->numCols, map->map->numRows, PS_TYPE_MASK);
+    psImage *xCoord = psImageAlloc (map->map->numCols, map->map->numRows, PS_TYPE_F32);
+    psImage *yCoord = psImageAlloc (map->map->numCols, map->map->numRows, PS_TYPE_F32);
+
+    // accumulate the values for each map pixel
+
+    // we can do this by accumulating a vector of pixel indexes for each cell
+    psArray *pixelSets = psArrayAlloc (map->map->numCols*map->map->numRows);
+    for (int i = 0; i < pixelSets->n; i++) {
+        pixelSets->data[i] = psVectorAllocEmpty (4, PS_TYPE_S32);
+    }
+    // associate each value with a cell
+    for (int i = 0; i < x->n; i++) {
+        int xRuff = psImageBinningGetRuffX (map->binning, x->data.F32[i]);
+        int yRuff = psImageBinningGetRuffY (map->binning, y->data.F32[i]);
+
+        int bin = xRuff + yRuff*map->map->numCols;
+        assert (bin >= 0);
+        assert (bin < pixelSets->n);
+
+        psVector *pixels = pixelSets->data[bin];
+        pixels->data.S32[pixels->n] = i;
+        psVectorExtend (pixels, 4, 1);
+    }
+
+    // stats structure for getting the position centers
+    psStats *meanStat = psStatsAlloc (PS_STAT_SAMPLE_MEAN);
+
+    // accumulate the x,y coords for each point to calculate the mean position.
+    int Nx = map->map->numCols;
+    int Ny = map->map->numRows;
+    for (int iy = 0; iy < Ny; iy++) {
+        for (int ix = 0; ix < Nx; ix++) {
+
+            // pixel index for this cell
+            psVector *pixels = pixelSets->data[ix + iy*Nx];
+
+            // storage vectors
+            psVector *xCell = psVectorAlloc (pixels->n, PS_TYPE_F32);
+            psVector *yCell = psVectorAlloc (pixels->n, PS_TYPE_F32);
+            psVector *fCell = psVectorAlloc (pixels->n, PS_TYPE_F32);
+
+            // error vector, if needed
+            psVector *dfCell = NULL;
+            if (df) {
+                dfCell = psVectorAlloc (pixels->n, PS_TYPE_F32);
+            }
+
+            // collect data for this cell
+            for (int i = 0; i < pixels->n; i++) {
+                int bin = pixels->data.S32[i];
+                // convert x,y in the fine image to the ruff image
+                xCell->data.F32[i]  = psImageBinningGetRuffX (map->binning, x->data.F32[bin]);
+                yCell->data.F32[i]  = psImageBinningGetRuffY (map->binning, y->data.F32[bin]);
+                fCell->data.F32[i]  = f->data.F32[bin];
+                if (df) {
+                    dfCell->data.F32[i] = df->data.F32[bin];
+                }
+            }
+
+            // reset the stats to avoid contamination from the previous loop
+            psStatsInit (map->stats);
+
+            // get the value
+            // XXX need to supply a mask and skip the masked pixels when calculating the centroid
+            // this will not in general be properly weighted...
+            if (psVectorStats (map->stats, fCell, dfCell, NULL, 0)) {
+                mask->data.U8[iy][ix] = 0;
+                // XXX ensure only one option is selected, or save both position and width
+                map->map->data.F32[iy][ix] = psStatsGetValue (map->stats, map->stats->options);
+
+                // calculate the mean position and save:
+                psStatsInit (meanStat);
+                psVectorStats (meanStat, xCell, NULL, NULL, 0);
+                xCoord->data.F32[iy][ix] = psStatsGetValue (meanStat, meanStat->options);
+                psStatsInit (meanStat);
+                psVectorStats (meanStat, yCell, NULL, NULL, 0);
+                yCoord->data.F32[iy][ix] = psStatsGetValue (meanStat, meanStat->options);
+            } else {
+                mask->data.U8[iy][ix] = 1;
+            }
+
+            psFree (xCell);
+            psFree (yCell);
+            psFree (fCell);
+            psFree (dfCell);
+        }
+    }
+    psFree (pixelSets);
+    psFree (meanStat);
+   // at this point, for each map pixel, we have (f,x,y), or the pixel is masked.
+
+    psFits *fits = NULL;
+
+    fits = psFitsOpen ("imageMap.raw.fits", "w");
+    psFitsWriteImage (fits, NULL, map->map, 0, NULL);
+    psFitsClose (fits);
+
+    // did this analysis succeed?  (enough good or OK pixels?)
+    psImage *state = psImagePixelInterpolateState (&map->nBad, &map->nPoor, mask, 0xff);
+    map->nGood = mask->numCols * mask->numRows - map->nBad - map->nPoor;
+    if (map->nBad > badFrac * mask->numCols * mask->numRows) {
+        psFree (xCoord);
+        psFree (yCoord);
+        psFree (mask);
+        return false;
+    }
+
+    // fit the valid pixels to (0,1,2) order polynomials, interpolate values to the pixel center
+    // XXX I need to be careful about the pixel coordinates: center is 0,0 or 0.5, 0.5?
+    psImagePixelInterpolateCenter (map->map, xCoord, yCoord, state, mask, 0xff);
+    psFree (xCoord);
+    psFree (yCoord);
+
+    fits = psFitsOpen ("imageMap.ref.fits", "w");
+    psFitsWriteImage (fits, NULL, map->map, 0, NULL);
+    psFitsClose (fits);
+
+    psImagePixelInterpolatePoor (map->map, state, mask, 0xff);
+
+    fits = psFitsOpen ("imageMap.fix.fits", "w");
+    psFitsWriteImage (fits, NULL, map->map, 0, NULL);
+    psFitsClose (fits);
+
+    psFree (state);
+    psFree (mask);
+    return true;
+}
+
+// using the points given, generate a map with maximum resolution that yields only good and ok pixels
+bool psImageMapGenerateScale(psImageMap *map, const psVector *x, const psVector *y,
+                             const psVector *f, const psVector *df, float badFrac)
+{
+    // XXX Asserts
+
+    int nXruff, nYruff;
+
+    while (!psImageMapGenerate (map, x, y, f, df, badFrac)) {
+        // if we failed to build an acceptable map, decrease nXruff, nYruff as appropriate, and
+        // try again...  try to keep the aspect ratio.
+
+        // if both axes are at 1, give up
+        if ((map->binning->nXruff == 1) && (map->binning->nYruff == 1)) {
+            return false;
+        }
+
+        // if one axis is at 1, decrement the other
+        if (map->binning->nXruff == 1) {
+            nXruff = map->binning->nXruff;
+            nYruff = map->binning->nYruff - 1;
+            psImageMapModifyScale (map, nXruff, nYruff);
+            continue;
+        }
+        if (map->binning->nYruff == 1) {
+            nYruff = map->binning->nYruff;
+            nXruff = map->binning->nXruff - 1;
+            psImageMapModifyScale (map, nXruff, nYruff);
+            continue;
+        }
+
+        // otherwise, decrement the larger axis, and set the smaller based
+        // on the aspect ratio
+        float aRatio = map->binning->nXruff / map->binning->nYruff;
+        if (map->binning->nXruff > map->binning->nYruff) {
+            nXruff = map->binning->nXruff - 1;
+            nYruff = (int)(0.5 + (nXruff / aRatio));
+        } else {
+            nYruff = map->binning->nYruff - 1;
+            nXruff = (int)(0.5 + (nYruff * aRatio));
+        }
+
+        psImageMapModifyScale (map, nXruff, nYruff);
+    }
+    return true;
+}
+
+// x,y are in fractional pixel coords of the fine image (pixel center: 0.5)
+double psImageMapEval(const psImageMap *map, float x, float y) {
+
+    double result;
+
+    result = psImageUnbinPixel_V2(x, y, map->map, map->binning);
+
+    return result;
+}
+
+psVector *psImageMapEvalVector(const psImageMap *map, const psVector *x, const psVector *y) {
+
+    assert (x);
+    assert (y);
+    assert (x->n == y->n);
+    assert (map);
+
+    psVector *result = psVectorAlloc (x->n, PS_TYPE_F32);
+
+    for (int i = 0; i < x->n; i++) {
+        result->data.F32[i] = psImageUnbinPixel_V2(x->data.F32[i], y->data.F32[i], map->map, map->binning);
+    }
+
+    return result;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMap.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMap.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMap.h	(revision 22322)
@@ -0,0 +1,62 @@
+/** @file  psImageMap.c
+ *
+ *  @brief Functions define a 2d coarse representation of a finer 2D field
+ *
+ *  @ingroup Image
+ *
+ *  @author Eugene Magnier, IfA
+ *
+ *  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-12-15 01:20:03 $
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_MAP_H
+#define PS_IMAGE_MAP_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+#include <psStats.h>
+#include <psImage.h>
+#include <psImageBinning.h>
+#include <psVector.h>
+
+// a structure to describe the 2D variations of some quantity as a function of position the
+// variation is represented as a psImage which covers the field also represented as a psImage.
+// the map image pixels are superpixels of the field image.  values in the field are determined
+// by interpolating the map image.
+typedef struct {
+    psStats *stats;
+    psImage *map;
+    psImage *error;
+    int col0, row0;                     // Column and row offsets from the original image
+    int numCols, numRows;               // Size of original image
+    psImageBinning *binning;
+    int nBad;
+    int nPoor;
+    int nGood;
+    psStatsOptions singleMean, singleStdev;  // Statistics for mean and stdev when there's a single pixel
+} psImageMap;
+
+psImageMap *psImageMapAlloc(const psImage *field, psImageBinning *binning, psStats *stats) PS_ATTR_MALLOC;
+bool psMemCheckImageMap(psPtr ptr);
+
+psImageMap *psImageMapNoImageAlloc(psImageBinning *binning, psStats *stats) PS_ATTR_MALLOC;
+
+bool psImageMapModifyScale(psImageMap *map, int nXruff, int nYruff);
+
+// generate a psImageMap (or NULL) with the given number of superpixels in X and Y
+bool psImageMapGenerate (psImageMap *map, const psVector *x, const psVector *y, const psVector *f, const psVector *df, float badFrac);
+
+bool psImageMapGenerateScale (psImageMap *map, const psVector *x, const psVector *y, const psVector *f, const psVector *df, float badFrac);
+
+// apply the psImageMap to the given coordinate (fine image pixels)
+double psImageMapEval (const psImageMap *map, float x, float y);
+
+// apply the psImageMap to the given coordinate vectors (fine image pixels)
+psVector *psImageMapEvalVector (const psImageMap *map, const psVector *x, const psVector *y);
+
+/// @}
+#endif // #ifndef PS_IMAGE_MAP_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMapFit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMapFit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMapFit.c	(revision 22322)
@@ -0,0 +1,746 @@
+/** @file  psImageMapFit.c
+ *
+ *  @brief Functions define a 2d coarse representation of a finer 2D field
+ *
+ *  @ingroup Image
+ *
+ *  @author Eugene Magnier, IfA
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-05-10 01:33:26 $
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include "psError.h"
+#include "psAbort.h"
+#include "psTrace.h"
+
+// #include "psFits.h"
+#include "psAssert.h"
+#include "psRegion.h"
+// #include "psFitsImage.h"
+
+#include "psMemory.h"
+#include "psVector.h"
+#include "psImage.h"
+#include "psMatrix.h"
+#include "psStats.h"
+#include "psImageBinning.h"
+#include "psImageMap.h"
+// #include "psImagePixelInterpolate.h"
+// #include "psImageUnbin.h"
+
+#include "psImageMapFit.h"
+
+
+// given a randomly-sampled field of values & weights at points: (f, df) @ (x, y), find the
+// best fit image from which Bilinear interpolation yields the input field.  The fitted image
+// consists of a grid of values g(n,m) at coordinates (n,m).
+
+// relationship between x,y and n,m coordinates:
+
+// map defines the output image dimensions and scaling.
+bool psImageMapFit(psImageMap *map, const psVector *mask, psMaskType maskValue,
+                   const psVector *x, const psVector *y, const psVector *f, const psVector *df)
+{
+    // XXX Add Asserts
+
+    // dimensions of the output map image
+    int Nx = map->binning->nXruff;
+    int Ny = map->binning->nYruff;
+
+    // no spatial information, just calculate mean & stdev
+    if ((Nx == 1) && (Ny == 1)) {
+        psStatsInit(map->stats);
+
+        // the user has supplied one of various stats option pairs,
+        psStatsOptions mean = psStatsMeanOption(map->stats->options);
+        psStatsOptions stdev = psStatsStdevOption(map->stats->options);
+        if (!psStatsSingleOption(mean)) {
+            psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
+            return false;
+        }
+        if (!psStatsSingleOption(stdev)) {
+            psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
+            return false;
+        }
+
+        // XXX does ROBUST_MEDIAN work with weight?
+        psVectorStats(map->stats, f, NULL, mask, maskValue);
+
+        map->map->data.F32[0][0]   = psStatsGetValue(map->stats, mean);
+        map->error->data.F32[0][0] = psStatsGetValue(map->stats, stdev);
+        return true;
+    }
+
+    if (Nx == 1) {
+        bool status;
+        status = psImageMapFit1DinY (map, mask, maskValue, x, y, f, df);
+        return status;
+    }
+    if (Ny == 1) {
+        bool status;
+        status = psImageMapFit1DinX (map, mask, maskValue, x, y, f, df);
+        return status;
+    }
+
+    // set up the redirection table so we can use sA[-1][-1], etc
+    // XXX psKernel does this for you --- PAP.
+    float SAm[3][3], *SAv[3], **sA;
+    // float TAm[3][3], *TAv[3], **tA;
+
+    for (int i = 0; i < 3; i++) {
+        SAv[i] = SAm[i] + 1;
+        // TAv[i] = TAm[i] + 1;
+    }
+    sA = SAv + 1;
+    // tA = TAv + 1;
+
+    // elements of the matrix equation Ax = B; we are solving for the vector x
+    psImage *A = psImageAlloc (Nx*Ny, Nx*Ny, PS_TYPE_F32);
+    psVector *B = psVectorAlloc (Nx*Ny, PS_TYPE_F32);
+
+    psImageInit (A, 0.0);
+    psVectorInit (B, 0.0);
+
+    // we are looping over the Nx,Ny image map elements;
+    // the matrix equation contains Nx*Ny rows and columns
+    // for (int n = 1; n < Nx - 1; n++) {
+    // for (int m = 1; m < Ny - 1; m++) {
+
+    // float Total = 0.0;
+    for (int n = 0; n < Nx; n++) {
+        for (int m = 0; m < Ny; m++) {
+            // define & init summing variables
+            float rx_rx_ry_ry = 0;
+            float rx_rx_dy_ry = 0;
+            float dx_rx_ry_ry = 0;
+            float dx_rx_dy_ry = 0;
+            float fi_rx_ry    = 0;
+            float rx_rx_py_py = 0;
+            float rx_rx_qy_py = 0;
+            float dx_rx_py_py = 0;
+            float dx_rx_qy_py = 0;
+            float fi_rx_py    = 0;
+            float px_px_ry_ry = 0;
+            float px_px_dy_ry = 0;
+            float qx_px_ry_ry = 0;
+            float qx_px_dy_ry = 0;
+            float fi_px_ry    = 0;
+            float px_px_py_py = 0;
+            float px_px_qy_py = 0;
+            float qx_px_py_py = 0;
+            float qx_px_qy_py = 0;
+            float fi_px_py    = 0;
+
+            // generate the sums for the fitting matrix element I,J
+            // I = n + nX*m
+            // J = (n + jn) + nX*(m + jm)
+            for (int i = 0; i < x->n; i++) {
+
+                if (mask && (mask->data.U8[i] & maskValue)) continue;
+
+                // base coordinate offset for this point (x,y) relative to this map element (n,m)
+                // float dx = x->data.F32[i] - psImageBinningGetFineX (map->binning, n + 0.5);
+                // float dy = y->data.F32[i] - psImageBinningGetFineY (map->binning, m + 0.5);
+
+                float dx = psImageBinningGetRuffX (map->binning, x->data.F32[i]) - (n + 0.5);
+                float dy = psImageBinningGetRuffY (map->binning, y->data.F32[i]) - (m + 0.5);
+
+                // edge cases to include:
+                bool edgeX = false;
+                edgeX |= ((n == 1) && (dx < -1.0));
+                edgeX |= ((n == Nx - 2) && (dx > +1.0));
+
+                bool edgeY = false;
+                edgeY |= ((m == 1) && (dy < -1.0));
+                edgeY |= ((m == Ny - 2) && (dy > +1.0));
+
+                // skip points outside of 2x2 grid centered on n,m:
+                if (!edgeX && (fabs(dx) > 1.0)) continue;
+                if (!edgeY && (fabs(dy) > 1.0)) continue;
+
+                // related offset values
+                float rx = 1.0 - dx;
+                float ry = 1.0 - dy;
+                float px = 1.0 + dx;
+                float py = 1.0 + dy;
+                float qx = -dx;
+                float qy = -dy;
+
+                // data value & weight for this point
+                float fi = f->data.F32[i];
+                float wt = 1.0;
+                if (df != NULL) {
+                    if (df->data.F32[i] == 0.0) {
+                        wt = 0.0;
+                    } else {
+                        wt = 1.0 / PS_SQR(df->data.F32[i]); // XXX test for dz == NULL or dz_i = 0
+                    }
+                }
+
+                // sum the appropriate elements for the different quadrants
+
+                int Qx = (dx >= 0) ? 1 : 0;
+                if (n ==      0) Qx = 1;
+                if (n == Nx - 1) Qx = 0;
+
+                int Qy = (dy >= 0) ? 1 : 0;
+                if (m ==      0) Qy = 1;
+                if (m == Ny - 1) Qy = 0;
+
+                assert (isfinite(fi));
+                assert (isfinite(wt));
+                assert (isfinite(rx));
+                assert (isfinite(ry));
+
+                // points at offset 1,1
+                if ((Qx == 1) && (Qy == 1)) {
+                    rx_rx_ry_ry += rx*rx*ry*ry*wt;
+                    rx_rx_dy_ry += rx*rx*dy*ry*wt;
+                    dx_rx_ry_ry += dx*rx*ry*ry*wt;
+                    dx_rx_dy_ry += dx*rx*dy*ry*wt;
+                    fi_rx_ry    += fi*rx*ry*wt;
+                }
+                // points at offset 1,0
+                if ((Qx == 1) && (Qy == 0)) {
+                    rx_rx_py_py += rx*rx*py*py*wt;
+                    rx_rx_qy_py += rx*rx*qy*py*wt;
+                    dx_rx_py_py += dx*rx*py*py*wt;
+                    dx_rx_qy_py += dx*rx*qy*py*wt;
+                    fi_rx_py    += fi*rx*py*wt;
+                }
+                // points at offset 0,1
+                if ((Qx == 0) && (Qy == 1)) {
+                    px_px_ry_ry += px*px*ry*ry*wt;
+                    px_px_dy_ry += px*px*dy*ry*wt;
+                    qx_px_ry_ry += qx*px*ry*ry*wt;
+                    qx_px_dy_ry += qx*px*dy*ry*wt;
+                    fi_px_ry    += fi*px*ry*wt;
+                }
+                // points at offset 0,0
+                if ((Qx == 0) && (Qy == 0)) {
+                    px_px_py_py += px*px*py*py*wt;
+                    px_px_qy_py += px*px*qy*py*wt;
+                    qx_px_py_py += qx*px*py*py*wt;
+                    qx_px_qy_py += qx*px*qy*py*wt;
+                    fi_px_py    += fi*px*py*wt;
+                }
+            }
+
+            // the chi-square derivatives have elements of the form g(n+jn,m+jm)*A(jn,jm),
+            // jn,jm = -1 to +1. Convert the sums above into the correct coefficients
+            sA[-1][-1] = qx_px_qy_py;
+            sA[-1][ 0] = qx_px_ry_ry + qx_px_py_py;
+            sA[-1][+1] = qx_px_dy_ry;
+            sA[ 0][-1] = rx_rx_qy_py + px_px_qy_py;
+            sA[ 0][ 0] = rx_rx_ry_ry + px_px_ry_ry + rx_rx_py_py + px_px_py_py;
+            sA[ 0][+1] = rx_rx_dy_ry + px_px_dy_ry;
+            sA[+1][-1] = dx_rx_qy_py;
+            sA[+1][ 0] = dx_rx_ry_ry + dx_rx_py_py;
+            sA[+1][+1] = dx_rx_dy_ry;
+
+            // I[ 0][ 0] = index for this n,m element:
+            int I = n + Nx * m;
+            B->data.F32[I] = fi_rx_ry + fi_rx_py + fi_px_ry + fi_px_py;
+
+            // insert these values into their corresponding locations in A, B
+            // float Sum = 0.0;
+            for (int jn = -1; jn <= +1; jn++) {
+                if (n + jn <   0) continue;
+                if (n + jn >= Nx) continue;
+                for (int jm = -1; jm <= +1; jm++) {
+                    if (m + jm <   0) continue;
+                    if (m + jm >= Ny) continue;
+                    int J = (n + jn) + Nx * (m + jm);
+                    A->data.F32[J][I] = sA[jn][jm];
+                    // fprintf (stderr, "A %d %d (%d %d : %d %d): %f\n", I, J, n, m, n + jn, m + jm, sA[jn][jm]);
+                    // Sum += sA[jn][jm];
+                }
+            }
+            // fprintf (stderr, "B %d (%d %d) : %f  :  %f\n", I, n, m, B->data.F32[I], Sum);
+            // Total += Sum;
+        }
+    }
+    // fprintf (stderr, "Total: %f\n", Total);
+
+    // test for empty diagonal elements (unconstained cells), mark, and set pivots to 1.0
+    psVector *Empty = psVectorAlloc (Nx*Ny, PS_TYPE_S8);
+    psVectorInit (Empty, 0);
+    for (int i = 0; i < Nx*Ny; i++) {
+        if (A->data.F32[i][i] == 0.0) {
+            Empty->data.S8[i] = 1;
+            for (int j = 0; j < Nx*Ny; j++) {
+                A->data.F32[i][j] = 0.0;
+                A->data.F32[j][i] = 0.0;
+            }
+            A->data.F32[i][i] = 1.0;
+            B->data.F32[i] = 0.0;
+        }
+    }
+
+# if (0)
+    psFits *fits = psFitsOpen ("Agj.fits", "w");
+    psFitsWriteImage (fits, NULL, A, 0, NULL);
+    psFitsClose (fits);
+
+    psImage *vector = psImageAlloc (1, B->n, PS_TYPE_F32);
+    for (int n = 0; n < B->n; n++) {
+        vector->data.F32[0][n] = B->data.F32[n];
+    }
+
+    fits = psFitsOpen ("Bgj.fits", "w");
+    psFitsWriteImage (fits, NULL, vector, 0, NULL);
+    psFitsClose (fits);
+    psFree (vector);
+# endif
+
+    if (!psMatrixGJSolveF32(A, B)) {
+        psAbort ("failed on linear equations");
+        psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.  Returning NULL.\n");
+        psFree (A);
+        psFree (B);
+        return false;
+    }
+
+    // set bad values to NaN
+    for (int i = 0; i < Nx*Ny; i++) {
+        if (Empty->data.S8[i]) {
+            B->data.F32[i] = NAN;
+        }
+    }
+
+
+    for (int n = 0; n < Nx; n++) {
+        for (int m = 0; m < Ny; m++) {
+            int I = n + Nx * m;
+            map->map->data.F32[m][n] = B->data.F32[I];
+            map->error->data.F32[m][n] = sqrt(A->data.F32[I][I]);
+        }
+    }
+
+    psFree (A);
+    psFree (B);
+    psFree (Empty);
+
+    return true;
+}
+
+// measure residuals on each pass and clip outliers based on stats
+bool psImageMapClipFit(psImageMap *map, psStats *stats, psVector *inMask, psMaskType maskValue,
+                       const psVector *x, const psVector *y, const psVector *f, const psVector *df)
+{
+    // XXX add in full PS_ASSERTS
+    psAssert(map, "impossible");
+    psAssert(stats, "impossible");
+    psAssert(x, "impossible");
+    psAssert(y, "impossible");
+    psAssert(f, "impossible");
+
+    // the user supplies one of various stats option pairs,
+    // determine the desired mean and stdev STATS options:
+    psStatsOptions meanOption = psStatsMeanOption(stats->options);
+    psStatsOptions stdevOption = psStatsStdevOption(stats->options);
+    if (!psStatsSingleOption(meanOption)) {
+        psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
+        return false;
+    }
+    if (!psStatsSingleOption(stdevOption)) {
+        psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
+        return false;
+    }
+
+    // clipping range defined by min and max and/or clipSigma
+    psF32 minClipSigma;
+    psF32 maxClipSigma;
+    if (isfinite(stats->max)) {
+        maxClipSigma = fabs(stats->max);
+    } else {
+        maxClipSigma = fabs(stats->clipSigma);
+    }
+    if (isfinite(stats->min)) {
+        minClipSigma = fabs(stats->min);
+    } else {
+        minClipSigma = fabs(stats->clipSigma);
+    }
+
+    psVector *mask = inMask;
+    if (!inMask) {
+        mask = psVectorAlloc (x->n, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+    }
+
+    // vector to store residuals
+    psVector *resid = psVectorAlloc(f->n, PS_TYPE_F32);
+
+    psTrace("psLib.imageops", 4, "stats->clipIter is %d\n", stats->clipIter);
+    psTrace("psLib.imageops", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
+
+    for (psS32 N = 0; N < stats->clipIter; N++) {
+        psTrace("psLib.imageops", 6, "Loop iteration %d.  Calling psImageMapFit()\n", N);
+        psS32 Nkeep = 0;
+        if (!psImageMapFit(map, mask, maskValue, x, y, f, df)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not fit image map.\n");
+            psFree(resid);
+            if (!inMask) psFree (mask);
+            return false;
+        }
+
+        psVector *fit = psImageMapEvalVector(map, x, y);
+        if (fit == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Failure in psImageMapEvalVector().\n");
+            psFree(resid);
+            if (!inMask) psFree (mask);
+            return false;
+        }
+        for (int i = 0 ; i < f->n ; i++) {
+            resid->data.F32[i] = (f->data.F32[i] - fit->data.F32[i]);
+        }
+
+        if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
+            psError(PS_ERR_UNKNOWN, false, "Failure to compute statistics on the resid vector.\n");
+            psFree(resid);
+            psFree(fit);
+            if (!inMask) psFree (mask);
+            return false;
+        }
+
+        double meanValue = psStatsGetValue (stats, meanOption);
+        double stdevValue = psStatsGetValue (stats, stdevOption);
+
+        psTrace("psLib.imageops", 5, "Mean is %f\n", meanValue);
+        psTrace("psLib.imageops", 5, "Stdev is %f\n", stdevValue);
+        psF32 minClipValue = -minClipSigma*stdevValue;
+        psF32 maxClipValue = +maxClipSigma*stdevValue;
+
+        // set mask if pts are not valid
+        // we are masking out any point which is out of range
+        // recovery is not allowed with this scheme
+        for (psS32 i = 0; i < resid->n; i++) {
+            // XXX this prevents recovery of previously masked values
+            if (mask->data.U8[i] & maskValue) {
+                continue;
+            }
+
+            if ((resid->data.F32[i] - meanValue > maxClipValue) || (resid->data.F32[i] - meanValue < minClipValue)) {
+                psTrace("psLib.imageops", 6, "Masking element %d  : %f vs %f : resid is %f\n", i, f->data.F32[i], fit->data.F32[i], resid->data.F32[i]);
+                mask->data.U8[i] |= 0x01;
+                continue;
+            }
+            Nkeep++;
+        }
+
+        // We should probably exit this loop if no new elements were masked since the fit won't
+        // change.
+        psTrace("psLib.imageops", 6, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
+        stats->clippedNvalues = Nkeep;
+        psFree(fit);
+    }
+
+    // Free local temporary variables
+    psFree(resid);
+    if (!inMask) psFree (mask);
+    return true;
+}
+
+// map defines the output image dimensions and scaling.
+bool psImageMapFit1DinY(psImageMap *map, const psVector *mask, psMaskType maskValue,
+                        const psVector *x, const psVector *y, const psVector *f, const psVector *df)
+{
+    // XXX Add Asserts
+    assert (map->binning->nXruff == 1);
+
+    // dimensions of the output map image
+    int Ny = map->binning->nYruff;
+
+    // set up the redirection table so we can use sA[-1][-1], etc
+    float SAv[3], *sA;
+
+    sA = SAv + 1;
+
+    // elements of the matrix equation Ax = B; we are solving for the vector x
+    psImage *A = psImageAlloc (Ny, Ny, PS_TYPE_F32);
+    psVector *B = psVectorAlloc (Ny, PS_TYPE_F32);
+
+    psImageInit (A, 0.0);
+    psVectorInit (B, 0.0);
+
+    for (int m = 0; m < Ny; m++) {
+        // define & init summing variables
+        float ry_ry = 0;
+        float dy_ry = 0;
+        float fi_ry = 0;
+        float py_py = 0;
+        float qy_py = 0;
+        float fi_py = 0;
+
+        // generate the sums for the fitting matrix element I,J
+        // I = m
+        // J = m + jm
+        for (int i = 0; i < y->n; i++) {
+
+            if (mask && (mask->data.U8[i] & maskValue)) continue;
+
+            float dy = psImageBinningGetRuffY (map->binning, y->data.F32[i]) - (m + 0.5);
+
+            bool edgeY = false;
+            edgeY |= ((m == 1) && (dy < -1.0));
+            edgeY |= ((m == Ny - 2) && (dy > +1.0));
+
+            // skip points outside of 2x2 grid centered on n,m:
+            if (!edgeY && (fabs(dy) > 1.0)) continue;
+
+            // related offset values
+            float ry = 1.0 - dy;
+            float py = 1.0 + dy;
+            float qy = -dy;
+
+            // data value & weight for this point
+            float fi = f->data.F32[i];
+            float wt = 1.0;
+            if (df != NULL) {
+                if (df->data.F32[i] == 0.0) {
+                    wt = 0.0;
+                } else {
+                    wt = 1.0 / PS_SQR(df->data.F32[i]); // XXX test for dz == NULL or dz_i = 0
+                }
+            }
+
+            // sum the appropriate elements for the different quadrants
+            int Qy = (dy >= 0) ? 1 : 0;
+            if (m ==      0) Qy = 1;
+            if (m == Ny - 1) Qy = 0;
+
+            assert (isfinite(fi));
+            assert (isfinite(wt));
+            assert (isfinite(ry));
+
+            // points at offset 1,1
+            if (Qy == 1) {
+                ry_ry += ry*ry*wt;
+                dy_ry += dy*ry*wt;
+                fi_ry += fi*ry*wt;
+            }
+            // points at offset 1,0
+            if (Qy == 0) {
+                py_py += py*py*wt;
+                qy_py += qy*py*wt;
+                fi_py += fi*py*wt;
+            }
+        }
+
+        // the chi-square derivatives have elements of the form g(n+jn,m+jm)*A(jn,jm),
+        // jn,jm = -1 to +1. Convert the sums above into the correct coefficients
+        sA[-1] = qy_py;
+        sA[ 0] = ry_ry + py_py;
+        sA[+1] = dy_ry;
+
+        // I[ 0][ 0] = index for this n,m element:
+        int I = m;
+        B->data.F32[I] = fi_ry + fi_py;
+
+        // insert these values into their corresponding locations in A, B
+        for (int jm = -1; jm <= +1; jm++) {
+            if (m + jm <   0) continue;
+            if (m + jm >= Ny) continue;
+            int J = (m + jm);
+            A->data.F32[J][I] = sA[jm];
+        }
+    }
+
+    // test for empty diagonal elements (unconstained cells), mark, and set pivots to 1.0
+    psVector *Empty = psVectorAlloc (Ny, PS_TYPE_S8);
+    psVectorInit (Empty, 0);
+    for (int i = 0; i < Ny; i++) {
+        if (A->data.F32[i][i] == 0.0) {
+            Empty->data.S8[i] = 1;
+            for (int j = 0; j < Ny; j++) {
+                A->data.F32[i][j] = 0.0;
+                A->data.F32[j][i] = 0.0;
+            }
+            A->data.F32[i][i] = 1.0;
+            B->data.F32[i] = 0.0;
+        }
+    }
+
+    if (!psMatrixGJSolveF32(A, B)) {
+        psAbort ("failed on linear equations");
+        psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.  Returning NULL.\n");
+        psFree (A);
+        psFree (B);
+        return false;
+    }
+
+    // set bad values to NaN
+    for (int i = 0; i < Ny; i++) {
+        if (Empty->data.S8[i]) {
+            B->data.F32[i] = NAN;
+        }
+    }
+
+    for (int m = 0; m < Ny; m++) {
+        map->map->data.F32[m][0] = B->data.F32[m];
+        map->error->data.F32[m][0] = sqrt(A->data.F32[m][m]);
+    }
+
+    psFree (A);
+    psFree (B);
+    psFree (Empty);
+
+    return true;
+}
+
+// map defines the output image dimensions and scaling.
+bool psImageMapFit1DinX(psImageMap *map, const psVector *mask, psMaskType maskValue,
+                        const psVector *x, const psVector *y, const psVector *f, const psVector *df)
+{
+    // XXX Add Asserts
+    assert (map->binning->nYruff == 1);
+
+    // dimensions of the output map image
+    int Nx = map->binning->nXruff;
+
+    // set up the redirection table so we can use sA[-1][-1], etc
+    float SAv[3], *sA;
+
+    sA = SAv + 1;
+
+    // elements of the matrix equation Ax = B; we are solving for the vector x
+    psImage *A = psImageAlloc (Nx, Nx, PS_TYPE_F32);
+    psVector *B = psVectorAlloc (Nx, PS_TYPE_F32);
+
+    psImageInit (A, 0.0);
+    psVectorInit (B, 0.0);
+
+    for (int m = 0; m < Nx; m++) {
+        // define & init summing variables
+        float rx_rx = 0;
+        float dx_rx = 0;
+        float fi_rx = 0;
+        float px_px = 0;
+        float qx_px = 0;
+        float fi_px = 0;
+
+        // generate the sums for the fitting matrix element I,J
+        // I = m
+        // J = m + jm
+        for (int i = 0; i < x->n; i++) {
+
+            if (mask && (mask->data.U8[i] & maskValue)) continue;
+
+            float dx = psImageBinningGetRuffX (map->binning, x->data.F32[i]) - (m + 0.5);
+
+            bool edgeX = false;
+            edgeX |= ((m == 1) && (dx < -1.0));
+            edgeX |= ((m == Nx - 2) && (dx > +1.0));
+
+            // skip points outside of 2x2 grid centered on n,m:
+            if (!edgeX && (fabs(dx) > 1.0)) continue;
+
+            // related offset values
+            float rx = 1.0 - dx;
+            float px = 1.0 + dx;
+            float qx = -dx;
+
+            // data value & weight for this point
+            float fi = f->data.F32[i];
+            float wt = 1.0;
+            if (df != NULL) {
+                if (df->data.F32[i] == 0.0) {
+                    wt = 0.0;
+                } else {
+                    wt = 1.0 / PS_SQR(df->data.F32[i]); // XXX test for dz == NULL or dz_i = 0
+                }
+            }
+
+            // sum the appropriate elements for the different quadrants
+            int Qx = (dx >= 0) ? 1 : 0;
+            if (m ==      0) Qx = 1;
+            if (m == Nx - 1) Qx = 0;
+
+            assert (isfinite(fi));
+            assert (isfinite(wt));
+            assert (isfinite(rx));
+
+            // points at offset 1,1
+            if (Qx == 1) {
+                rx_rx += rx*rx*wt;
+                dx_rx += dx*rx*wt;
+                fi_rx += fi*rx*wt;
+            }
+            // points at offset 1,0
+            if (Qx == 0) {
+                px_px += px*px*wt;
+                qx_px += qx*px*wt;
+                fi_px += fi*px*wt;
+            }
+        }
+
+        // the chi-square derivatives have elements of the form g(n+jn,m+jm)*A(jn,jm),
+        // jn,jm = -1 to +1. Convert the sums above into the correct coefficients
+        sA[-1] = qx_px;
+        sA[ 0] = rx_rx + px_px;
+        sA[+1] = dx_rx;
+
+        // I[ 0][ 0] = index for this n,m element:
+        int I = m;
+        B->data.F32[I] = fi_rx + fi_px;
+
+        // insert these values into their corresponding locations in A, B
+        for (int jm = -1; jm <= +1; jm++) {
+            if (m + jm <   0) continue;
+            if (m + jm >= Nx) continue;
+            int J = (m + jm);
+            A->data.F32[J][I] = sA[jm];
+        }
+    }
+
+    // test for empty diagonal elements (unconstained cells), mark, and set pivots to 1.0
+    psVector *Empty = psVectorAlloc (Nx, PS_TYPE_S8);
+    psVectorInit (Empty, 0);
+    for (int i = 0; i < Nx; i++) {
+        if (A->data.F32[i][i] == 0.0) {
+            Empty->data.S8[i] = 1;
+            for (int j = 0; j < Nx; j++) {
+                A->data.F32[i][j] = 0.0;
+                A->data.F32[j][i] = 0.0;
+            }
+            A->data.F32[i][i] = 1.0;
+            B->data.F32[i] = 0.0;
+        }
+    }
+
+    if (!psMatrixGJSolveF32(A, B)) {
+        psAbort ("failed on linear equations");
+        psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.  Returning NULL.\n");
+        psFree (A);
+        psFree (B);
+        return false;
+    }
+
+    // set bad values to NaN
+    for (int i = 0; i < Nx; i++) {
+        if (Empty->data.S8[i]) {
+            B->data.F32[i] = NAN;
+        }
+    }
+
+    for (int m = 0; m < Nx; m++) {
+        int I = m; // XXX I'm not entirely sure about this; it wasn't set for this scope --- PAP.
+        map->map->data.F32[0][m] = B->data.F32[I];
+        map->error->data.F32[0][m] = sqrt(A->data.F32[I][I]);
+    }
+
+    psFree (A);
+    psFree (B);
+    psFree (Empty);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMapFit.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMapFit.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMapFit.h	(revision 22322)
@@ -0,0 +1,48 @@
+#ifndef PS_IMAGE_MAP_FIT_H
+#define PS_IMAGE_MAP_FIT_H
+
+#include <psType.h>
+#include <psVector.h>
+#include <psImageMap.h>
+
+
+// fit the image map to a set of points
+bool psImageMapFit(psImageMap *map,
+                   const psVector *mask,
+                   psMaskType maskValue,
+                   const psVector *x,
+                   const psVector *y,
+                   const psVector *f,
+                   const psVector *df
+    );
+
+// fit the image map to a set of points
+bool psImageMapClipFit(psImageMap *map,
+                       psStats *stats,
+                       psVector *mask,  // WARNING: Mask is modified!
+                       psMaskType maskValue,
+                       const psVector *x,
+                       const psVector *y,
+                       const psVector *f,
+                       const psVector *df
+    );
+
+bool psImageMapFit1DinY(psImageMap *map,
+                        const psVector *mask,
+                        psMaskType maskValue,
+                        const psVector *x,
+                        const psVector *y,
+                        const psVector *f,
+                        const psVector *df
+    );
+
+bool psImageMapFit1DinX(psImageMap *map,
+                        const psVector *mask,
+                        psMaskType maskValue,
+                        const psVector *x,
+                        const psVector *y,
+                        const psVector *f,
+                        const psVector *df
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMaskOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMaskOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMaskOps.c	(revision 22322)
@@ -0,0 +1,296 @@
+/** @file  psImageMaskOps.c
+ *
+ *  @brief Contains basic image pixel and geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Mask Operations"
+ *
+ *  @ingroup Image
+ *
+ *  @author David Robbins, MHPCC
+ *
+ *  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-07-12 21:08:33 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>                          // for isfinite(), etc.
+#include <stdlib.h>
+#include <string.h>                        // for memcpy, etc.
+
+#include "psImageMaskOps.h"
+
+#include "psError.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psMemory.h"
+#include "psAssert.h"
+
+#include "psCoord.h"
+
+// mask the area contained by the region
+// the region is defined wrt the parent image
+void psImageMaskRegion(psImage *image,
+                       psRegion region,
+                       const char *op,
+                       psMaskType maskValue)
+{
+    if (image == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Invalid image input.  Image is NULL.\n");
+        return;
+    }
+
+
+# define MASK_IT(OP) \
+      for (int j = 0; j < image->numRows; j++) { \
+	if ((j + image->row0) < region.y0) continue; \
+	if ((j + image->row0) > region.y1) continue; /* is this correct (not >= ?) */ \
+	for (int i = 0; i < image->numCols; i++) { \
+	  if ((i + image->col0) < region.x0) continue; \
+	  if ((i + image->col0) > region.x1) continue; /* is this correct (not >= ?) */ \
+	  image->data.PS_TYPE_MASK_DATA[j][i] OP maskValue; \
+	} \
+      }
+
+    if ( !strncmp(op, "&", 2) || !strncmp(op, "AND", 5) ) {
+      MASK_IT (&=);
+      return;
+    }
+    if ( !strncmp(op, "|", 2) || !strncmp(op, "OR", 5) ) {
+      MASK_IT (|=);
+      return;
+    }
+    if ( !strncmp(op, "=", 2) || !strncmp(op, "EQUAL", 5) ) {
+      MASK_IT (=);
+      return;
+    }
+    if ( !strncmp(op, "^", 2) || !strncmp(op, "XOR", 5) ) {
+      MASK_IT (^=);
+      return;
+    }
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+	    "The logical operation specified is incorrect\n");
+    return;
+}
+
+// mask the area not contained by the region
+// the region is defined wrt the parent image
+void psImageKeepRegion(psImage *image,
+                       psRegion region,
+                       const char *op,
+                       psMaskType maskValue)
+{
+    if (image == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Invalid image input.  Image is NULL.\n");
+        return;
+    }
+
+
+# define KEEP_IT(OP) \
+    for (int j = 0; j < image->numRows; j++) { \
+      for (int i = 0; i < image->numCols; i++) { \
+	if ((j + image->row0) < region.y0 || \
+	    (j + image->row0) > region.y1 || \
+	    (i + image->col0) < region.x0 || \
+	    (i + image->col0) > region.x1 ) { \
+	  image->data.PS_TYPE_MASK_DATA[j][i] OP maskValue; \
+	} } }
+
+    if ( !strncmp(op, "&", 2) || !strncmp(op, "AND", 5) ) {
+      KEEP_IT(&=);
+      return;
+    }
+    if ( !strncmp(op, "|", 2) || !strncmp(op, "OR", 5) ) {
+      KEEP_IT(|=);
+      return;
+    }
+    if ( !strncmp(op, "=", 2) || !strncmp(op, "EQUAL", 5) ) {
+      KEEP_IT(=);
+      return;
+    }
+    if ( !strncmp(op, "^", 2) || !strncmp(op, "XOR", 5) ) {
+      KEEP_IT(^=);
+      return;
+    }
+    psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+	    "The logical operation specified is incorrect\n");
+    return;
+}
+
+// mask the area contained by the region
+// the region is defined wrt the parent image
+void psImageMaskCircle(psImage *image,
+                       double x,
+                       double y,
+                       double radius,
+                       const char *op,
+                       psMaskType maskValue)
+{
+    if (image == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Invalid image input.  Image is NULL.\n");
+        return;
+    }
+
+    double dx, dy, r2, R2;
+
+    R2 = PS_SQR(radius);
+
+# define MASK_IT_CIRCLE(OP) \
+    for (int iy = 0; iy < image->numRows; iy++) { \
+        for (int ix = 0; ix < image->numCols; ix++) { \
+            dx = ix + image->col0 - x; \
+            dy = iy + image->row0 - y; \
+            r2 = PS_SQR(dx) + PS_SQR(dy); \
+            if (r2 <= R2) { \
+	      image->data.PS_TYPE_MASK_DATA[iy][ix] OP maskValue; \
+            } } }
+
+    if ( !strncmp(op, "&", 2) || !strncmp(op, "AND", 5) ) {
+      MASK_IT_CIRCLE (&=);
+      return;
+    } 
+    if ( !strncmp(op, "|", 2) || !strncmp(op, "OR", 5) ) {
+      MASK_IT_CIRCLE (|=);
+      return;
+    } 
+    if ( !strncmp(op, "=", 2) || !strncmp(op, "EQUAL", 5) ) {
+      MASK_IT_CIRCLE (=);
+      return;
+    } 
+    if ( !strncmp(op, "^", 2) || !strncmp(op, "XOR", 5) ) {
+      MASK_IT_CIRCLE (^=);
+      return;
+    } 
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+	    "The logical operation specified is incorrect\n");
+    return;
+}
+
+// mask the area contained by the region
+// the region is defined wrt the parent image
+void psImageKeepCircle(psImage *image,
+                       double x,
+                       double y,
+                       double radius,
+                       const char *op,
+                       psMaskType maskValue)
+{
+
+    if (image == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Invalid image input.  Image is NULL.\n");
+        return;
+    }
+    double dx, dy, r2, R2;
+
+    R2 = PS_SQR(radius);
+
+# define KEEP_IT_CIRCLE(OP) \
+    for (int iy = 0; iy < image->numRows; iy++) { \
+        for (int ix = 0; ix < image->numCols; ix++) { \
+            dx = ix + image->col0 - x; \
+            dy = iy + image->row0 - y; \
+            r2 = PS_SQR(dx) + PS_SQR(dy); \
+            if (r2 > R2) { \
+	      image->data.PS_TYPE_MASK_DATA[iy][ix] OP maskValue; \
+            } } }
+
+    if ( !strncmp(op, "&", 2) || !strncmp(op, "AND", 5) ) {
+      KEEP_IT_CIRCLE (&=);
+      return;
+    } 
+    if ( !strncmp(op, "|", 2) || !strncmp(op, "OR", 5) ) {
+      KEEP_IT_CIRCLE (|=);
+      return;
+    } 
+    if ( !strncmp(op, "=", 2) || !strncmp(op, "EQUAL", 5) ) {
+      KEEP_IT_CIRCLE (=);
+      return;
+    } 
+    if ( !strncmp(op, "^", 2) || !strncmp(op, "XOR", 5) ) {
+      KEEP_IT_CIRCLE (^=);
+      return;
+    } 
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+	    "The logical operation specified is incorrect\n");
+    return;
+}
+
+psImage *psImageGrowMask(psImage *out,
+                         const psImage *in,
+                         psMaskType maskVal,
+                         unsigned int growSize,
+                         psMaskType growVal)
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Invalid input image.  Input image cannot be NULL.\n");
+        return NULL;
+    }
+    if (in->type.type != PS_TYPE_MASK) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Invalid input image.  Input image type must match psMaskType.\n");
+        return NULL;
+    }
+    if (out != NULL) {
+        if (out->numCols != in->numCols || out->numRows != in->numRows) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Invalid out image.  Size of out does not match size of in.\n");
+            return NULL;
+        }
+        if (out->type.type != in->type.type) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Invalid out image.  Type of out does not match type of in.\n");
+            return NULL;
+        }
+    }
+    if (out == NULL) {
+        out = psImageAlloc(in->numCols, in->numRows, in->type.type);
+    }
+    psImage *changed = psImageAlloc(in->numCols, in->numRows, in->type.type);
+    int k,l,m,n;
+    for (k = 0; k < in->numRows; k++) {
+        for (l = 0; l < in->numCols; l++) {
+            out->data.PS_TYPE_MASK_DATA[k][l] = in->data.PS_TYPE_MASK_DATA[k][l];
+            changed->data.PS_TYPE_MASK_DATA[k][l] = 0;
+        }
+    }
+
+    for (int i = 0; i < in->numRows; i++) {
+        for (int j = 0; j < in->numCols; j++) {
+            if ( (in->data.PS_TYPE_MASK_DATA[i][j] & maskVal) != 0 &&
+                    changed->data.PS_TYPE_MASK_DATA[i][j] == 0) {
+                m = i - growSize;
+                if (m < 0) {
+                    m = 0;
+                }
+                for (k = m; k <= (i + growSize) && k < in->numRows; k++) {
+                    n = j - growSize;
+                    if (n < 0) {
+                        n = 0;
+                    }
+                    for (l = n; l <= (j + growSize) && l < in->numCols; l++) {
+                        if (((k-i)*(k-i) + (l-j)*(l-j)) <= (growSize*growSize)) {
+                            out->data.PS_TYPE_MASK_DATA[k][l] |= growVal;
+                            if ( (in->data.PS_TYPE_MASK_DATA[i][j] & maskVal) == 0 ) {
+                                changed->data.PS_TYPE_MASK_DATA[k][l] = 1;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    psFree(changed);
+    return out;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMaskOps.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMaskOps.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageMaskOps.h	(revision 22322)
@@ -0,0 +1,97 @@
+/* @file  psImageMaskOps.h
+ *
+ * @brief Contains basic image pixel manipulation operations, as
+ *        specified in the PSLIB SDRS sections "Mask Operations"
+ *
+ * @author David Robbins, MHPCC
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-23 22:47:23 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_MASK_OPS_H
+#define PS_IMAGE_MASK_OPS_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+#include "psImage.h"
+#include "psCoord.h"
+#include "psStats.h"
+#include "psPixels.h"
+
+/** Sets the bits inside the region, ignoring pixels outside.
+ *
+ *  The pixels are set by combining the existing pixel value and the given maskValue
+ *  with a logical operation.  The allowed operations are =, AND, OR, and XOR.
+ */
+void psImageMaskRegion(
+    psImage *image,                    ///< the image to set
+    psRegion region,                   ///< the specified region
+    const char *op,                    ///< the logical operation
+    psMaskType maskValue               ///< the specified bits
+);
+
+/** Sets the bits outside the region, ignoring pixels inside.
+ *
+ *  The pixels are set by combining the existing pixel value and the given maskValue
+ *  with a logical operation.  The allowed operations are =, AND, OR, and XOR.
+ */
+void psImageKeepRegion(
+    psImage *image,                    ///< the image to set
+    psRegion region,                   ///< the specified region
+    const char *op,                    ///< the logical operation
+    psMaskType maskValue               ///< the specified bits
+);
+
+/** Sets the bits inside the circle, ignoring the pixels outside.
+ *
+ *  The pixel values are set by combining the existing pixel value and the given maskValue
+ *  with a logical operation.  The allowed operations are =, AND, OR, and XOR.
+ */
+void psImageMaskCircle(
+    psImage *image,                    ///< the image to set
+    double x,                          ///< the x coordinate of the circle's center
+    double y,                          ///< the y coordinate of the circle's center
+    double radius,                     ///< the radius of the specified circle
+    const char *op,                    ///< the logical operation
+    psMaskType maskValue               ///< the specified bits
+);
+
+/** Sets the bits outside the circle, ignoring the pixels inside.
+ *
+ *  The pixel values are set by combining the existing pixel value and the given maskValue
+ *  with a logical operation.  The allowed operations are =, AND, OR, and XOR.
+ */
+void psImageKeepCircle(
+    psImage *image,                    ///< the image to set
+    double x,                          ///< the x coordinate of the circle's center
+    double y,                          ///< the y coordinate of the circle's center
+    double radius,                     ///< the radius of the specified circle
+    const char *op,                    ///< the logical operation
+    psMaskType maskValue               ///< the specified bits
+);
+
+/** Grows the specified values on the imput mask image, in, returning the result.
+ *
+ *  If out is NULL, then a new image of the same type and dimension as in shall
+ *  be allocated and returned; otherwise out shall be modified.  If out is non-
+ *  NULL and does not have the same size and type as in, the function shall
+ *  generate an error and return NULL.  Pixels in the in image within growSize
+ *  pixels (either horizontal or vertical) of a pixel which matches the maskVal
+ *  shall have the corresponding pixel in the out image set to the growVal.
+ *
+ *  @return psImage*:
+ */
+psImage *psImageGrowMask(
+    psImage *out,                      ///< the image to set and return
+    const psImage *in,                 ///< the input to image
+    psMaskType maskVal,                ///< the specified mask value
+    unsigned int growSize,             ///< the range of values from maskVal
+    psMaskType growVal                 ///< the output value to set
+);
+
+/// @}
+#endif // #ifndef PS_MASK_OPS_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelExtract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelExtract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelExtract.c	(revision 22322)
@@ -0,0 +1,929 @@
+/** @file  psImagePixelExtract.c
+ *
+ *  @brief Contains basic image extraction operations, as specified in the
+ *         PSLIB SDRS sections "Image Pixel Extractions".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.32 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-04-04 22:42:02 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include "psMemory.h"
+#include "psVector.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psImageInterpolate.h"
+#include "psImagePixelExtract.h"
+
+#define VECTOR_STORE_ROW_CASE(TYPE) \
+case PS_TYPE_##TYPE: \
+memcpy(out->data.TYPE, input->data.TYPE[row], input->numCols*sizeof(ps##TYPE)); \
+break;
+
+psVector *psImageRow(psVector *out,
+                     const psImage *input,
+                     int row)
+{
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(out);
+        return NULL;
+    }
+    if (input->col0 < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  col0 cannot be negative.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->row0 < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  row0 cannot be negative.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->numCols < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  numCols must be greater than 0.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->numRows < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  numRows must be greater than 0.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (row >= (input->numRows + input->row0) ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Specified row number is out of range for specified image.\n");
+        psFree(out);
+        return NULL;
+    } else if ( row < input->row0 && row >= 0 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified row number is out of range for specified image.\n");
+        psFree(out);
+        return NULL;
+    } else if ( row < 0 ) {
+        row += input->numRows;
+        if ( row < 0 ) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Specified row number is out of range for specified image.\n");
+            psFree(out);
+            return NULL;
+        }
+    } else {
+        row -= input->row0;
+    }
+
+    out = psVectorRecycle(out, input->numCols, input->type.type);
+    out->n = input->numCols;
+
+    switch (input->type.type) {
+        VECTOR_STORE_ROW_CASE(S8);
+        VECTOR_STORE_ROW_CASE(S16);
+        VECTOR_STORE_ROW_CASE(S32);
+        VECTOR_STORE_ROW_CASE(S64);
+        VECTOR_STORE_ROW_CASE(U8);
+        VECTOR_STORE_ROW_CASE(U16);
+        VECTOR_STORE_ROW_CASE(U32);
+        VECTOR_STORE_ROW_CASE(U64);
+        VECTOR_STORE_ROW_CASE(F32);
+        VECTOR_STORE_ROW_CASE(F64);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Specified psImage has invalid type for this function.\n");
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+
+#define VECTOR_STORE_COL_CASE(TYPE) \
+case PS_TYPE_##TYPE: \
+for (int i = 0; i < input->numRows; i++) { \
+    out->data.TYPE[i] = input->data.TYPE[i][column]; \
+} \
+break;
+
+psVector *psImageCol(psVector *out,
+                     const psImage *input,
+                     int column)
+{
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(out);
+        return NULL;
+    }
+    if (input->col0 < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  col0 cannot be negative.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->row0 < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  row0 cannot be negative.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->numCols < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  numCols must be greater than 0.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->numRows < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  numRows must be greater than 0.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (column >= (input->numCols + input->col0) ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Specified column number is out of range for specified image.\n");
+        psFree(out);
+        return NULL;
+    } else if ( column < input->col0 && column >= 0 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified column number is out of range for specified image.\n");
+        psFree(out);
+        return NULL;
+    } else if ( column < 0 ) {
+        column += input->numCols;
+        if ( column < 0 ) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Specified column number is out of range for specified image.\n");
+            psFree(out);
+            return NULL;
+        }
+    } else {
+        column -= input->col0;
+    }
+
+    out = psVectorRecycle(out, input->numRows, input->type.type);
+    out->n = input->numRows;
+
+    switch (input->type.type) {
+        VECTOR_STORE_COL_CASE(S8);
+        VECTOR_STORE_COL_CASE(S16);
+        VECTOR_STORE_COL_CASE(S32);
+        VECTOR_STORE_COL_CASE(S64);
+        VECTOR_STORE_COL_CASE(U8);
+        VECTOR_STORE_COL_CASE(U16);
+        VECTOR_STORE_COL_CASE(U32);
+        VECTOR_STORE_COL_CASE(U64);
+        VECTOR_STORE_COL_CASE(F32);
+        VECTOR_STORE_COL_CASE(F64);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Specified psImage has invalid type for this function.\n");
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+
+}
+
+psVector* psImageSlice(psVector* out,
+                       psPixels* coords,
+                       const psImage* input,
+                       const psImage* mask,
+                       psMaskType maskVal,
+                       psRegion region,
+                       psImageCutDirection direction,
+                       const psStats* stats)
+{
+    psStats* myStats;
+    psElemType type;
+    psS32 inRows;
+    psS32 inCols;
+    psS32 delta = 1;
+    psF64* outData;
+    psS32 row0 = region.y0;
+    psS32 row1 = region.y1;
+    psS32 col0 = region.x0;
+    psS32 col1 = region.x1;
+
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(out);
+        return NULL;
+    }
+    if (input->col0 < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  col0 cannot be negative.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->row0 < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  row0 cannot be negative.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->numCols < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  numCols must be greater than 0.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->numRows < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  numRows must be greater than 0.\n");
+        psFree(out);
+        return NULL;
+    }
+
+    //If [0,0,0,0] specified, the whole image is to be included
+    if (row0 == 0 && col0 == 0 && row1 == 0 && col1 == 0) {
+        row0 = input->row0;
+        col0 = input->col0;
+        row1 = input->row0 + input->numRows - 1;
+        col1 = input->col0 + input->numCols - 1;
+    }
+
+    //Make sure x0 of region is inside image.  If so, set col0 to corresponding index number.
+    if (col0 >= input->col0 && col0 < (input->col0 + input->numCols) ) {
+        col0 -= input->col0;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified psRegion parameter, x0=%f, is out of range [%d,%d].\n",
+                region.x0, input->col0, input->col0+input->numCols-1);
+        psFree(out);
+        return NULL;
+    }
+    //Make sure y0 of region is inside image.  If so, set row0 to corresponding index number.
+    if (row0 >= input->row0 && row0 < (input->row0 + input->numRows) ) {
+        row0 -= input->row0;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified psRegion parameter, y0=%f, is out of range [%d,%d].\n",
+                region.y0, input->row0, input->row0+input->numRows-1);
+        psFree(out);
+        return NULL;
+    }
+
+    //Make sure x1 of region is valid.  If negative, index from tail (if valid).
+    if (col1 < 0) {
+        col1 += input->numCols;
+        if (col1 < 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Specified psRegion parameter, x1=%f=%d, is out of range [%d,%d].\n",
+                    region.x1, col1+input->col0, input->col0, input->col0+input->numCols-1);
+            psFree(out);
+            return NULL;
+        }
+    } else if (col1 >= input->col0 && col1 < (input->col0 + input->numCols) ) {
+        col1 -= input->col0;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified psRegion parameter, x1=%f=%d, is out of range [%d,%d].\n",
+                region.x1, col1, input->col0, input->col0+input->numCols-1);
+        psFree(out);
+        return NULL;
+    }
+    //Make sure y1 of region is valid.  If negative, index from tail (if valid).
+    if (row1 < 0) {
+        row1 += input->numRows;
+        if (row1 < 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Specified psRegion parameter, y1=%f=%d, is out of range [%d,%d].\n",
+                    region.y1, row1+input->row0, input->row0, input->row0+input->numRows-1);
+            psFree(out);
+            return NULL;
+        }
+    } else if (row1 >= input->row0 && row1 < (input->row0 + input->numRows) ) {
+        row1 -= input->row0;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified psRegion parameter, y1=%f=%d, is out of range [%d,%d].\n",
+                region.y1, row1, input->row0, input->row0+input->numRows-1);
+        psFree(out);
+        return NULL;
+    }
+    //Now make sure that the region makes sense.
+    if (col0 > col1 || row0 > row1) {
+        if (col0 > col1) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Invalid psRegion specified.  x0=%f=%d is greater than x1=%f=%d.\n",
+                    region.x0, col0, region.x1, col1);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Invalid psRegion specified.  y0=%f=%d is greater than y1=%f=%d.\n",
+                    region.y0, row0, region.y1, row1);
+        }
+        psFree(out);
+        return NULL;
+    } else if (col0 == col1 && row0 == row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Invalid psRegion specified.  Region contains only 1 pixel.\n");
+        psFree(out);
+        return NULL;
+    }
+
+    type = input->type.type;
+    inRows = input->numRows;
+    inCols = input->numCols;
+
+    if (direction == PS_CUT_X_NEG || direction == PS_CUT_Y_NEG) {
+        delta = -1;
+    }
+
+    if (mask != NULL) {
+        if (inRows != mask->numRows || inCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Input psImage mask size, %dx%d, does not match psImage input size, %dx%d."),
+                    mask->numCols,mask->numRows,
+                    inCols, inRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Input psImage mask type, %s, is not the supported mask datatype of %s."),
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified statistic can not be NULL."));
+        psFree(out);
+        return NULL;
+    }
+
+    // verify that the stats struct specifies a single stats operation
+    psStatsOptions statistic = psStatsSingleOption(stats->options);
+    if (statistic == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                _("Specified statistic option, %d, is not valid.  Must specify one and only one statistic type."),stats->options);
+        psFree(out);
+        return NULL;
+    }
+    // since stats input is const, I need to
+    // create a 'scratch' stats struct
+    myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    psS32 numCols = col1-col0;
+    psS32 numRows = row1-row0;
+
+    if (direction == PS_CUT_X_POS || direction == PS_CUT_X_NEG) {
+        psVector* imgVec = psVectorAlloc(numRows, type);
+        psVector* maskVec = NULL;
+        psMaskType* maskData = NULL;
+        psPixelCoord* outPosition = NULL;
+
+        // recycle output to make a proper sized/type output structure
+        // n.b. type is double as that is the type given for all stats is
+        // psStats.
+        out = psVectorRecycle(out, numCols, PS_TYPE_F64);
+        out->n = numCols;
+        if (coords != NULL) {
+            coords = psPixelsRealloc(coords, numCols);
+            coords->n = numCols;
+            outPosition = coords->data;
+        }
+        outData = out->data.F64;
+        if (delta < 0) {
+            outData += numCols - 1;
+            if (outPosition != NULL) {
+                outPosition += numCols - 1;
+            }
+        }
+
+        if (mask != NULL) {
+            maskVec = psVectorAlloc(numRows, mask->type.type);
+        }
+        #define PSIMAGE_CUT_VERTICAL(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            psMaskType* maskVecData = NULL; \
+            for (psS32 c=col0;c<col1;c++) { \
+                ps##TYPE *imgData = input->data.TYPE[row0] + c; \
+                ps##TYPE *imgVecData = imgVec->data.TYPE; \
+                if (maskVec != NULL) { \
+                    maskVecData = maskVec->data.U8; \
+                    maskData = (psMaskType* )(mask->data.U8[row0]) + c; \
+                } \
+                for (psS32 r=row0;r<row1;r++) { \
+                    *(imgVecData++) = *imgData; \
+                    imgData += inCols; \
+                    if (maskVecData != NULL) { \
+                        *(maskVecData++) = *maskData; \
+                        maskData += inCols; \
+                    } \
+                } \
+                psVectorStats(myStats,imgVec,NULL,maskVec,maskVal); \
+                *outData = psStatsGetValue(myStats, statistic); \
+                if (outPosition != NULL) { \
+                    outPosition->x = c; \
+                    outPosition->y = row0; \
+                    outPosition += delta; \
+                } \
+                outData += delta; \
+            } \
+            break; \
+        }
+
+        switch (type) {
+            PSIMAGE_CUT_VERTICAL(U8);  // Not a requirement
+            PSIMAGE_CUT_VERTICAL(U16);
+            PSIMAGE_CUT_VERTICAL(U32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(U64); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S8);
+            PSIMAGE_CUT_VERTICAL(S16); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S64); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(F32);
+            PSIMAGE_CUT_VERTICAL(F64);
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        _("Specified psImage type, %s, is not supported."),
+                        typeStr);
+                psFree(out);
+                out = NULL;
+            }
+        }
+        psFree(imgVec);
+        psFree(maskVec);
+    } else if (direction == PS_CUT_Y_POS || direction == PS_CUT_Y_NEG) {
+        // Cut in Y direction
+        psVector* imgVec = NULL;
+        psVector* maskVec = NULL;
+        psS32 elementSize = PSELEMTYPE_SIZEOF(type);
+        psPixelCoord* outPosition = NULL;
+
+        // fill in psVector to fake out the statistics functions.
+        imgVec = psAlloc(sizeof(psVector));
+        imgVec->type = input->type;
+        P_PSVECTOR_SET_NALLOC(imgVec,numCols);
+        if (mask != NULL) {
+            maskVec = psAlloc(sizeof(psVector));
+            maskVec->type = mask->type;
+            P_PSVECTOR_SET_NALLOC(maskVec,numCols);
+        }
+        // recycle output to make a proper sized/type output structure
+        // n.b. type is double as that is the type given for all stats in
+        // psStats.
+        out = psVectorRecycle(out, numRows, PS_TYPE_F64);
+        out->n = numRows;
+        imgVec->n = imgVec->nalloc;
+        maskVec->n = maskVec->nalloc;
+        if (coords != NULL) {
+            coords = psPixelsRealloc(coords, numRows);
+            coords->n = numRows;
+            outPosition = coords->data;
+        }
+        outData = out->data.F64;
+        if (delta < 0) {
+            outData += numRows-1;
+            if (outPosition != NULL) {
+                outPosition += numRows-1;
+            }
+        }
+
+        for (psS32 r = row0; r < row1; r++) {
+            // point the vector struct to the
+            // data to calculate the stats
+            imgVec->data.U8 = (psPtr )(input->data.U8[r] + col0 * elementSize);
+            if (maskVec != NULL) {
+                maskVec->data.U8 = (psPtr )(mask->data.U8[r] + col0 * sizeof(psMaskType));
+            }
+            psVectorStats(myStats, imgVec, NULL, maskVec, maskVal);
+            *outData = psStatsGetValue(myStats, statistic);
+            if (outPosition != NULL) {
+                outPosition->y = r;
+                outPosition->x = col0;
+                outPosition += delta;
+
+            }
+            outData += delta;
+        }
+        psFree(imgVec);
+        psFree(maskVec);
+    } else { // don't know what the direction flag is
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified slice direction, %d, is invalid."),
+                direction);
+        psFree(out);
+        out = NULL;
+    }
+
+    psFree(myStats);
+
+    return out;
+}
+
+psVector* psImageCut(psVector* out,
+                     psVector* cutCols,
+                     psVector* cutRows,
+                     const psImage* input,
+                     const psImage* mask,
+                     psMaskType maskVal,
+                     psRegion region,
+                     unsigned int nSamples,
+                     psImageInterpolateMode mode)
+{
+
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(out);
+        return NULL;
+    }
+    if (input->col0 < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  col0 cannot be negative.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->row0 < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  row0 cannot be negative.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->numCols < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  numCols must be greater than 0.\n");
+        psFree(out);
+        return NULL;
+    }
+    if (input->numRows < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImage input is invalid.  numRows must be greater than 0.\n");
+        psFree(out);
+        return NULL;
+    }
+    psS32 numCols = input->numCols;
+    psS32 numRows = input->numRows;
+
+    if (nSamples < 2) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified number of samples, %d, must be greater than 1 to make a line."),
+                nSamples);
+        psFree(out);
+        return NULL;
+    }
+
+    float col0 = region.x0;
+    float row0 = region.y0;
+    float col1 = region.x1;
+    float row1 = region.y1;
+
+    //If [0,0,0,0] specified, the whole image is to be included
+    if (row0 == 0 && col0 == 0 && row1 == 0 && col1 == 0) {
+        row0 = input->row0;
+        col0 = input->col0;
+        row1 = input->row0 + input->numRows - 1;
+        col1 = input->col0 + input->numCols - 1;
+    }
+
+    //Make sure x0 of region is inside image.  If so, set col0 to corresponding index number.
+    if (col0 >= input->col0 && col0 < (input->col0 + input->numCols) ) {
+        col0 -= input->col0;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified psRegion parameter, x0=%f, is out of range [%d,%d].\n",
+                region.x0, input->col0, input->col0+input->numCols);
+        psFree(out);
+        return NULL;
+    }
+    //Make sure y0 of region is inside image.  If so, set row0 to corresponding index number.
+    if (row0 >= input->row0 && row0 < (input->row0 + input->numRows) ) {
+        row0 -= input->row0;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified psRegion parameter, y0=%f, is out of range [%d,%d].\n",
+                region.y0, input->row0, input->row0+input->numRows);
+        psFree(out);
+        return NULL;
+    }
+    if (col1 < 0 || row1 < 0 || col0 < 0 || row0 < 0 || col0 >= numCols || col1 >= numCols ||
+            row0 >= numRows || row1 >= numRows) {
+        psFree(out);
+        return NULL;
+    }
+    float startCol = col0;
+    float startRow = row0;
+    float endCol = col1;
+    float endRow = row1;
+
+    if (mode < PS_INTERPOLATE_FLAT ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified interpolation mode, %d, is unsupported."),
+                mode);
+        psFree(out);
+        return NULL;
+    }
+
+    if (mask != NULL) {
+        if (numRows != mask->numRows || numCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Input psImage mask size, %dx%d, does not match psImage input size, %dx%d."),
+                    mask->numCols,mask->numRows,
+                    numCols-1, numRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Input psImage mask type, %s, is not the supported mask datatype of %s."),
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    //resize the vectors for the coordinate output
+    psF32* cutColsData = NULL;
+    psF32* cutRowsData = NULL;
+    if (cutCols != NULL) {
+        cutCols = psVectorRecycle(cutCols, nSamples, PS_TYPE_F32);
+        cutColsData = cutCols->data.F32;
+    }
+    if (cutRows != NULL) {
+        cutRows = psVectorRecycle(cutRows, nSamples, PS_TYPE_F32);
+        cutRowsData = cutRows->data.F32;
+    }
+
+    out = psVectorRecycle(out, nSamples, input->type.type);
+
+    float dX = (endCol - startCol) / (float)(nSamples-1);
+    float dY = (endRow - startRow) / (float)(nSamples-1);
+
+    psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(mode, input, NULL, mask, maskVal,
+                                                                       0, 0, 0, 0, 0);
+
+    #define LINEAR_CUT_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE* outData = out->data.TYPE; \
+        for (psS32 i = 0; i < nSamples; i++) { \
+            float x = startCol + (float)i*dX; \
+            float y = startRow + (float)i*dY; \
+            /* store off the location of the sample. */ \
+            if (cutColsData != NULL) { \
+                cutColsData[i] = x; \
+            } \
+            if (cutRowsData != NULL) { \
+                cutRowsData[i] = y; \
+            } \
+            double value; \
+            if (!psImageInterpolate(&value, NULL, NULL, x, y, interp)) { \
+                psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image."); \
+                psFree(interp); \
+                psFree(out); \
+                return NULL; \
+            } \
+            outData[i] = value; \
+        } \
+    } \
+    break;
+
+
+    switch (input->type.type) {
+        LINEAR_CUT_CASE(U8);
+        LINEAR_CUT_CASE(U16);
+        LINEAR_CUT_CASE(U32);
+        LINEAR_CUT_CASE(U64);
+        LINEAR_CUT_CASE(S8);
+        LINEAR_CUT_CASE(S16);
+        LINEAR_CUT_CASE(S32);
+        LINEAR_CUT_CASE(S64);
+        LINEAR_CUT_CASE(F32);
+        LINEAR_CUT_CASE(F64);
+      default: {
+          char* typeStr;
+          PS_TYPE_NAME(typeStr,input->type.type);
+          psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                  _("Specified psImage type, %s, is not supported."),
+                  typeStr);
+          psFree(interp);
+          psFree(out);
+          out = NULL;
+      }
+    }
+
+    psFree(interp);
+
+    return out;
+}
+
+psVector* psImageRadialCut(psVector* out,
+                           const psImage* input,
+                           const psImage* mask,
+                           psMaskType maskVal,
+                           float x,
+                           float y,
+                           const psVector* radii,
+                           const psStats* stats)
+{
+    /* check the parameters */
+
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(out);
+        return NULL;
+    }
+    psS32 numCols = input->numCols;
+    psS32 numRows = input->numRows;
+
+    if (mask != NULL) {
+        if (numRows != mask->numRows || numCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    _("Input psImage mask size, %dx%d, does not match psImage input size, %dx%d."),
+                    mask->numCols,mask->numRows,
+                    numCols, numRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Input psImage mask type, %s, is not the supported mask datatype of %s."),
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    //    if (x < 0 || x >= numCols ||
+    //            y < 0 || y >= numRows) {
+    if (x < input->col0 || x >= (input->col0 + numCols) ||
+            y < input->row0 || y >= (input->row0 + numRows) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified center, (%g,%g), is outside of the psImage boundaries, [0:%d,0:%d]."),
+                x, y,
+                numCols-1, numRows-1);
+        psFree(out);
+        return NULL;
+    }
+
+    if (radii == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified radii vector can not be NULL."));
+        psFree(out);
+        return NULL;
+    }
+
+    if (radii->n < 2) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                _("Input radii vector size, %ld, can not be less than 2."),
+                radii->n);
+        psFree(out);
+        return NULL;
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified statistic can not be NULL."));
+        psFree(out);
+        return NULL;
+    }
+
+    // verify that the stats struct specifies a single stats operation
+    psStatsOptions statistic = psStatsSingleOption(stats->options);
+    if (statistic == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                _("Specified statistic option, %d, is not valid.  Must specify one and only one statistic type."),
+                stats->options);
+        psFree(out);
+        return NULL;
+    }
+
+    /* completed checking the parameters */
+
+    // size the output vector to proper size.
+    psS32 numOut = radii->n - 1;
+    out = psVectorRecycle(out, numOut, PS_TYPE_F64);
+    psF64* outData = out->data.F64;
+
+    psVector* rSqVec = psVectorCopy(NULL, radii, PS_TYPE_F32);
+    psF32* rSq = rSqVec->data.F32;
+
+    psS32 startRow = y - rSq[numOut];
+    psS32 endRow = y + rSq[numOut];
+    psS32 startCol = x - rSq[numOut];
+    psS32 endCol = x + rSq[numOut];
+
+    if (startRow < 0) {
+        startRow = 0;
+    }
+
+    if (startCol < 0) {
+        startCol = 0;
+    }
+
+    if (endRow >= numRows) {
+        endRow = numRows - 1;
+    }
+
+    if (endCol >= numCols) {
+        endCol = numCols - 1;
+    }
+
+    // Square the radii data
+    for (psS32 d = 0; d <= numOut; d++) {
+        rSq[d] *= rSq[d];
+    }
+
+    // create temporary vectors for the data binning step
+    psVector** buffer = psAlloc(sizeof(psVector*)*numOut);
+    psVector** bufferMask = psAlloc(sizeof(psVector*)*numOut);
+    for (psS32 lcv = 0; lcv < numOut; lcv++) {
+        // n.b. alloc enough for the data by making the vectors slightly larger
+        // than the area of the region of interest.
+        buffer[lcv] = psVectorAllocEmpty(1+4*(rSq[lcv+1]-rSq[lcv]),
+                                         input->type.type);
+
+        bufferMask[lcv] = NULL;
+        if (mask != NULL) {
+            bufferMask[lcv] = psVectorAllocEmpty(1+4*(rSq[lcv+1]-rSq[lcv]),
+                                                 PS_TYPE_MASK);
+        }
+    }
+
+    float dX;
+    float dY;
+    float dist;
+    for (psS32 row=startRow; row <= endRow; row++) {
+        psF32* inRow = input->data.F32[row];
+        psMaskType* maskRow = NULL;
+        if (mask != NULL) {
+            maskRow = mask->data.PS_TYPE_MASK_DATA[row];
+        }
+        for (psS32 col=startCol; col <= endCol; col++) {
+            dX = x - (float)col - 0.5f;
+            dY = y - (float)row - 0.5f;
+            dist = dX*dX+dY*dY;
+            for (psS32 r = 0; r < numOut; r++) {
+                if (rSq[r] < dist && dist < rSq[r+1]) {
+                    psS32 n = buffer[r]->n;
+                    if (n == buffer[r]->nalloc) { // in case buffers already full, expand
+                        buffer[r] = psVectorRealloc(buffer[r], n*2);
+                        if (bufferMask[r] != NULL) {
+                            bufferMask[r] = psVectorRealloc(bufferMask[r], n*2);
+                        }
+                    }
+
+                    buffer[r]->data.F32[n] = inRow[col];
+                    buffer[r]->n = n+1;
+
+                    if (maskRow != NULL) {
+                        bufferMask[r]->data.PS_TYPE_MASK_DATA[n] = maskRow[col];
+                        bufferMask[r]->n = n+1;
+                    }
+
+                    break;
+                }
+            }
+        }
+    }
+
+    psStats* myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    for (psS32 r = 0; r < numOut; r++) {
+        psVectorStats(myStats,buffer[r], NULL, bufferMask[r],maskVal);
+        outData[r] = psStatsGetValue(myStats, statistic);
+    }
+
+    psFree(myStats);
+
+    for (psS32 lcv = 0; lcv < numOut; lcv++) {
+        psFree(buffer[lcv]);
+        psFree(bufferMask[lcv]);
+    }
+    psFree(buffer);
+    psFree(bufferMask);
+    psFree(rSqVec);
+    return out;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelExtract.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelExtract.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelExtract.h	(revision 22322)
@@ -0,0 +1,145 @@
+/* @file  psImagePixelExtract.h
+ *
+ * @brief Basic image extraction operations
+ *
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-23 22:47:23 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSIMAGE_PIXEL_EXTRACT_H
+#define PSIMAGE_PIXEL_EXTRACT_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+#include "psImage.h"
+#include "psVector.h"
+#include "psStats.h"
+#include "psPixels.h"
+
+/* Cut direction flag.  Used with psImageCut function.
+ */
+typedef enum {
+    PS_CUT_X_POS,                      ///< Cut in the x dimension from left to right
+    PS_CUT_X_NEG,                      ///< Cut in the x dimension from rigth to left
+    PS_CUT_Y_POS,                      ///< Cut in the y dimension from bottom up
+    PS_CUT_Y_NEG                       ///< Cut in the y dimension from top down.
+} psImageCutDirection;
+
+/** Extracts a single complete row from the image and returns it to the
+ *  provided vector, allocating it if it is NULL.
+ *
+ *  @return psVector*:      The row data extracted from psImage input
+ */
+psVector *psImageRow(
+    psVector *out,                     ///< specified vector to return
+    const psImage *input,              ///< input image
+    int row                            ///< row number to extract
+);
+
+/** Extracts a single complete column from the image and returns it to the
+ *  provided vector, allocating it if it is NULL.
+ *
+ *  @return psVector*:      The column data extracted from psImage input
+ */
+psVector *psImageCol(
+    psVector *out,                     ///< specified vector to return
+    const psImage *input,              ///< input image
+    int column                         ///< column number to extract
+);
+
+/** Extract pixels from rectlinear region to a vector (array of floats).
+ *
+ *  The output vector contains either col1-col0 or row1-row0 elements, based
+ *  on the value of the direction: e.g., if direction is PS_CUT_X_POS, there
+ *  are col1-col0 elements. The region to be  sliced  is defined by the
+ *  lower-left corner, (col0,row0), and the upper-right corner, (col1,row1).
+ *  Note that the row and column of the  upper right-hand corner  are NOT
+ *  included in the region. In the event that col1 or row1 are negative, they
+ *  shall be interpreted as being relative to the size of the parent image in
+ *  that dimension. The input region is collapsed in the direction perpendicular
+ *  to that specified by direction, and each element of the output vectors is
+ *  derived from the statistics of the pixels at that direction coordinate. The
+ *  statistic used to derive the output vector value is specified by stats.
+ *  If mask is non-NULL, pixels for which the corresponding mask pixel
+ *  matches maskVal are excluded from operations. If coords is not NULL, the
+ *  calculated coordinates along the slice are returned in this vector. Only
+ *  one of the statistics choices may be specified, otherwise the function
+ *  must return an error.
+ *
+ *  This function is defined for the following types: psS8, psU16, psF32, psF64.
+ *
+ * @return psVector    the resulting vector
+ */
+psVector* psImageSlice(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    psPixels* coords,
+    ///< If not NULL, it is populated with the coordinate in the slice dimension
+    ///< coorsponding to the output vector's value of the same position in the
+    ///< vector.  This vector maybe resized and retyped as appropriate.
+    const psImage* input,              ///< the input image in which to perform the slice
+    const psImage* mask,               ///< the mask for the input image.
+    psMaskType maskVal,                ///< the mask value to apply to the mask
+    psRegion region,                   ///< the slice region
+    psImageCutDirection direction,     ///< the slice dimension and direction
+    const psStats* stats               ///< the statistic to perform in slice operation
+);
+
+/** Extract pixels from an image along a line to a vector (array of floats).
+ *
+ *  The vector (xs,ys) - (xe,ye) forms the basis of the output vector. Pixels
+ *  are considered in a rectangular region of width dw about this vector. The
+ *  input region is collapsed in the perpendicular direction, and each element
+ *  of the output vector represents pixel-sized boxes, where the value is
+ *  derived from the statistics of the pixels interpolated along the
+ *  perpendicular direction. The specific algorithm which must be used is
+ *  described in the PSLib ADD (PSDC-430-006). The statistic used to derive
+ *  the output vector value is specified by stats. Only one of the statistics
+ *  choices may be specified, otherwise the function must return an error.
+ *  This function must be defined for the following types: psS8, psU16, psF32,
+ *  psF64.
+ *
+ *  @return psVector*    resulting vector
+ */
+psVector* psImageCut(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    psVector* cutCols,                 ///< if not NULL, the calculated column values along the slice (output)
+    psVector* cutRows,                 ///< if not NULL, the calculated row values along the slice (output)
+    const psImage* input,              ///< the input image in which to perform the cut
+    const psImage* mask,               ///< the mask for the input image.
+    psMaskType maskVal,                ///< the mask value to apply to the mask
+    psRegion region,                   ///< the start and end points to cut along
+    unsigned int nSamples,             ///< the number of samples along the cut
+    psImageInterpolateMode mode        ///< the interpolation method to use
+);
+
+/** Extract radial region data to a vector. A vector is constructed where each
+ *  vector elements is derived from the statistics of the pixels which land
+ *  within one of a sequence of radii. The radii are centered on the image
+ *  pixel coordinate x,y, and are defined by the sequence of values in the
+ *  vector radii. The specific algorithm which must be used is described in
+ *  the PSLib ADD (PSDC-430-006). The statistic used to derive the output
+ *  vector value is specified by stats. Only one of the statistics choices
+ *  may be specified, otherwise the function must return an error. This
+ *  function must be defined for the following types: psS8, psU16, psF32,
+ *  psF64.
+ *
+ *  @return psVector    resulting vector
+ */
+psVector* psImageRadialCut(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    const psImage* input,              ///< the input image in which to perform the cut
+    const psImage* mask,               ///< the mask for the input image.
+    psMaskType maskVal,                ///< the mask value to apply to the mask
+    float x,                           ///< the column of the center of the cut circle
+    float y,                           ///< the row of the center of the cut circle
+    const psVector* radii,             ///< the radii of the cut circle
+    const psStats* stats               ///< the statistic to perform in operation
+);
+
+/// @}
+#endif // #ifndef PSIMAGE_PIXEL_EXTRACT_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelInterpolate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelInterpolate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelInterpolate.c	(revision 22322)
@@ -0,0 +1,379 @@
+/** @file  psImagePixelInterpolate.c
+ *
+ *  @brief Functions for interpolating bad pixels in images
+ *
+ *  these functions test and set masked pixels in an image.  These functions are complementary
+ *  to the psImageInterpolate functions, which perform sub-pixel interpolation.  Those
+ *  functions require all pixels surrounding the sub-pixel interpolation to have values which
+ *  are valid.  These functions enable interpolation of complete missing pixels, potentially
+ *  across large spans.
+ *
+ *  @author Eugene Magnier, IfA
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-10-09 19:25:44 $
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psAssert.h"
+#include "psString.h"
+#include "psPolynomial.h"
+#include "psPolynomialUtils.h"
+#include "psMinimizePolyFit.h"
+#include "psImage.h"
+#include "psImageInterpolate.h"
+#include "psImagePixelInterpolate.h"
+
+#include "psFits.h"
+#include "psFitsImage.h"
+
+# define PS_IMAGE_ITER_STATE(XS,XE,YS,YE,N_MIN,TYPE) \
+	    nGood = 0; \
+	    for (int jy = YS; jy <= YE; jy++) { \
+		/* stick to pixels in image grid */ \
+		if (jy + iy < 0) { continue; } \
+		if (jy + iy >= mask->numRows) { continue; } \
+		for (int jx = XS; jx <= XE; jx++) { \
+		    /* stick to pixels in image grid */ \
+		    if (jx + ix < 0) { continue; } \
+		    if (jx + ix >= mask->numCols) { continue; } \
+		    /* do not test self */ \
+		    if (!jx && !jy) { continue; } \
+		    if (mask->data.PS_TYPE_MASK_DATA[iy+jy][ix+jx] & maskVal) { continue; } \
+		    nGood ++; \
+		} \
+	    } \
+	    if (nGood >= N_MIN) {  \
+		nPoor ++; \
+		result->data.S32[iy][ix] = TYPE; \
+		continue; \
+	    }
+
+// count and mark pixels based on their potential for being interpolated.  the input image is
+// just the mask, the output image contains enum values which define the type of interpolation which
+// can be performed
+psImage *psImagePixelInterpolateState (int *nBad, int *nPoor, psImage *mask, psMaskType maskVal) {
+
+    psImage *result = psImageAlloc (mask->numCols, mask->numRows, PS_TYPE_S32);
+    psImageInit (result, 0);
+    
+    *nPoor = 0;
+    *nBad = 0;
+
+    for (int iy = 0; iy < mask->numRows; iy++) {
+	for (int ix = 0; ix < mask->numCols; ix++) {
+
+	    // state of the good pixels (unmasked)
+	    if (!(mask->data.PS_TYPE_MASK_DATA[iy][ix] & maskVal)) { 
+		// count good neighbor pixels (+ self)
+		int nGood = 0;
+		int minX = +1;
+		int maxX = -1;
+		int minY = +1;
+		int maxY = -1;
+		for (int jy = -1; jy <= +1; jy++) {
+		    /* stick to pixels in image grid */
+		    if (jy + iy < 0) { continue; }
+		    if (jy + iy >= mask->numRows) { continue; }
+		    for (int jx = -1; jx <= +1; jx++) {
+			/* stick to pixels in image grid */
+			if (jx + ix < 0) { continue; }
+			if (jx + ix >= mask->numCols) { continue; }
+			if (mask->data.PS_TYPE_MASK_DATA[iy+jy][ix+jx] & maskVal) { continue; }
+			nGood ++;
+			minX = PS_MIN (minX, jx);
+			maxX = PS_MAX (maxX, jx);
+			minY = PS_MIN (minY, jy);
+			maxY = PS_MAX (maxY, jy);
+		    }
+		}
+		int dX = maxX - minX;
+		int dY = maxY - minY;
+		// what type of local interpolation can we use?
+		if ((nGood >= 6) && (dX == 2) && (dY == 2)) {
+		    result->data.S32[iy][ix] = PS_IMAGE_INTERPOLATE_GOOD2;
+		    continue; 
+		}
+		if (nGood >= 3) {
+		    result->data.S32[iy][ix] = PS_IMAGE_INTERPOLATE_GOOD1;
+		    continue; 
+		}
+		result->data.S32[iy][ix] = PS_IMAGE_INTERPOLATE_GOOD0;
+		continue; 
+	    }
+
+	    // examine the neighbors.  If at least 6 of the 8 surrounding, or 3 of the 4 corner
+	    // neighbors are valid, this is a pixel which can be interpolated.
+
+	    int nGood;
+
+	    // check for poor pixels
+	    PS_IMAGE_ITER_STATE (-1,+1,-1,+1,6, PS_IMAGE_INTERPOLATE_CENTER);
+	    PS_IMAGE_ITER_STATE (-1,+0,-1,+0,3, PS_IMAGE_INTERPOLATE_UR);
+	    PS_IMAGE_ITER_STATE (-1,+0,+0,+1,3, PS_IMAGE_INTERPOLATE_LR);
+	    PS_IMAGE_ITER_STATE (+0,+1,-1,+0,3, PS_IMAGE_INTERPOLATE_UL);
+	    PS_IMAGE_ITER_STATE (+0,+1,+0,+1,3, PS_IMAGE_INTERPOLATE_LL);
+
+	    nBad ++;
+	    result->data.S32[iy][ix] = PS_IMAGE_INTERPOLATE_BAD;
+	}	    
+    }
+    return result;
+}
+
+// interpolate the poor pixels using the available options
+bool psImagePixelInterpolatePoor (psImage *image, psImage *state, psImage *mask, psMaskType maskVal) {
+
+    assert (image->numCols == state->numCols);
+    assert (image->numRows == state->numRows);
+    assert (image->numCols == mask->numCols);
+    assert (image->numRows == mask->numRows);
+
+    // allocate the vectors for the 2nd order fit below
+    psVector *f  = psVectorAlloc (9, PS_TYPE_F32);
+    // XXX if we add the weight above, include df
+    // psVector *df = psVectorAlloc (9, PS_TYPE_F32); 
+    psVector *x  = psVectorAlloc (9, PS_TYPE_F32);
+    psVector *y  = psVectorAlloc (9, PS_TYPE_F32);
+
+    // allocate a 2D polynomial to fit a quadratic to the valid neighbor pixels.
+    psPolynomial2D *poly = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, 2, 2);
+    poly->coeffMask[2][2] = PS_POLY_MASK_SET;
+    poly->coeffMask[2][1] = PS_POLY_MASK_SET;
+    poly->coeffMask[1][2] = PS_POLY_MASK_SET;
+
+    for (int iy = 0; iy < state->numRows; iy++) {
+	for (int ix = 0; ix < state->numCols; ix++) {
+
+	    switch (state->data.S32[iy][ix]) {
+	      case PS_IMAGE_INTERPOLATE_GOOD0: 
+	      case PS_IMAGE_INTERPOLATE_GOOD1: 
+	      case PS_IMAGE_INTERPOLATE_GOOD2: 
+		// skip the good pixels
+		break; 
+
+	      case PS_IMAGE_INTERPOLATE_BAD:
+		// skip the bad pixels
+		break; 
+
+	      case PS_IMAGE_INTERPOLATE_CENTER: {
+		  // XXX is there a fit-image-region function?
+		  int n = 0;
+		  for (int jy = -1; jy <= +1; jy++) {
+		      // skip invalid pixels 
+		      if (jy + iy < 0) { continue; }
+		      if (jy + iy >= image->numRows) { continue; }
+		      for (int jx = -1; jx <= +1; jx++) {
+			  // skip invalid pixels 
+			  if (jx + ix < 0) { continue; } 
+			  if (jx + ix >= image->numCols) { continue; } 
+			  // skip self 
+			  if (!jx && !jy) { continue; } 
+			  // skip masked pixels
+			  if (mask->data.PS_TYPE_MASK_DATA[iy+jy][ix+jx] & maskVal) { continue; }
+			  x->data.F32[n] = jx;
+			  y->data.F32[n] = jy;
+			  f->data.F32[n] = image->data.F32[iy+jy][ix+jx];
+			  // df->data.F32[n] = weight->data.F32[iy+jy][ix+jx];
+			  n++;
+		      }
+		  }
+		  // set vector lengths here
+		  x->n = n;
+		  y->n = n;
+		  f->n = n;
+		  // df->n = n;
+		  // psVectorFitPolynomial2D (poly, NULL, 0xff, f, df, x, y);
+		  psVectorFitPolynomial2D (poly, NULL, 0xff, f, NULL, x, y);
+		  // apply the fitted quadratic to get the poor pixel value
+		  image->data.F32[iy][ix] = poly->coeff[0][0];
+		  break; }
+
+		// XXX should I use 1 1D polynomial fitting all unmasked pixels in the 3x3 grid?
+		// XXX that would automatically extend to regions where only 2 pixels are valid...
+	      case PS_IMAGE_INTERPOLATE_LL: {
+		  // fit a plane to the 3 pixels at (0,1),(1,0),(1,1), extend to pixel at (0,0)
+		  image->data.F32[iy][ix] = image->data.F32[iy+1][ix+1] - image->data.F32[iy+0][ix+1] - image->data.F32[iy+1][ix+0];
+		  break; }
+
+	      case PS_IMAGE_INTERPOLATE_LR: {
+		  // fit a plane to the 3 pixels at (0,1),(-1,0),(-1,1), extend to pixel at (0,0)
+		  image->data.F32[iy][ix] = image->data.F32[iy+1][ix-1] - image->data.F32[iy+0][ix-1] - image->data.F32[iy+1][ix+0];
+		  break; }
+
+	      case PS_IMAGE_INTERPOLATE_UL: {
+		  // fit a plane to the 3 pixels at (0,-1),(1,0),(1,-1), extend to pixel at (0,0)
+		  image->data.F32[iy][ix] = image->data.F32[iy-1][ix+1] - image->data.F32[iy+0][ix+1] - image->data.F32[iy-1][ix+0];
+		  break; }
+
+	      case PS_IMAGE_INTERPOLATE_UR: {
+		  // fit a plane to the 3 pixels at (0,-1),(-1,0),(-1,-1), extend to pixel at (0,0)
+		  image->data.F32[iy][ix] = image->data.F32[iy-1][ix-1] - image->data.F32[iy+0][ix-1] - image->data.F32[iy-1][ix+0];
+		  break; }
+
+	      default:
+		psAbort("impossible case in __func__");
+	    }
+	}	    
+    }
+
+    psFree (x);
+    psFree (y);
+    psFree (f);
+
+    psFree (poly);
+    return true;
+}
+    
+// interpolate the good pixels to their true centers
+bool psImagePixelInterpolateCenter (psImage *value, psImage *xCoord, psImage *yCoord, psImage *state, psImage *mask, psMaskType maskVal) {
+
+    assert (value->numCols == state->numCols);
+    assert (value->numRows == state->numRows);
+    assert (value->numCols == mask->numCols);
+    assert (value->numRows == mask->numRows);
+
+# if (0)
+    psFits *fits = NULL;
+
+    fits = psFitsOpen ("xcoords.fits", "w");
+    psFitsWriteImage (fits, NULL, xCoord, 0, NULL);
+    psFitsClose (fits);
+
+    fits = psFitsOpen ("ycoords.fits", "w");
+    psFitsWriteImage (fits, NULL, yCoord, 0, NULL);
+    psFitsClose (fits);
+
+    fits = psFitsOpen ("value.fits", "w");
+    psFitsWriteImage (fits, NULL, value, 0, NULL);
+    psFitsClose (fits);
+# endif
+
+    psImage *output = psImageAlloc (value->numCols, value->numRows, PS_TYPE_F32);
+
+    // allocate the vectors for the 2nd order fit below
+    psVector *f  = psVectorAlloc (9, PS_TYPE_F32);
+    // XXX if we add the weight above, include df
+    // psVector *df = psVectorAlloc (9, PS_TYPE_F32); 
+    psVector *x  = psVectorAlloc (9, PS_TYPE_F32);
+    psVector *y  = psVectorAlloc (9, PS_TYPE_F32);
+
+    // allocate a 2D polynomial to fit a quadratic to the valid neighbor pixels.
+    psPolynomial2D *poly2o = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, 2, 2);
+    poly2o->coeffMask[2][2] = PS_POLY_MASK_SET;
+    poly2o->coeffMask[2][1] = PS_POLY_MASK_SET;
+    poly2o->coeffMask[1][2] = PS_POLY_MASK_SET;
+
+    // allocate a 2D polynomial to fit a plane to the valid neighbor pixels.
+    psPolynomial2D *poly1o = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, 1, 1);
+    poly2o->coeffMask[1][1] = PS_POLY_MASK_SET;
+
+    for (int iy = 0; iy < state->numRows; iy++) {
+	for (int ix = 0; ix < state->numCols; ix++) {
+
+	    switch (state->data.S32[iy][ix]) {
+	      case PS_IMAGE_INTERPOLATE_GOOD2: {
+		  // XXX is there a fit-image-region function?
+		  int n = 0;
+		  for (int jy = -1; jy <= +1; jy++) {
+		      // skip invalid pixels 
+		      if (jy + iy < 0) { continue; }
+		      if (jy + iy >= value->numRows) { continue; }
+		      for (int jx = -1; jx <= +1; jx++) {
+			  // skip invalid pixels 
+			  if (jx + ix < 0) { continue; } 
+			  if (jx + ix >= value->numCols) { continue; } 
+			  // skip masked pixels
+			  if (mask->data.PS_TYPE_MASK_DATA[iy+jy][ix+jx] & maskVal) { continue; }
+			  x->data.F32[n] = xCoord->data.F32[iy+jy][ix+jx];
+			  y->data.F32[n] = yCoord->data.F32[iy+jy][ix+jx];
+			  f->data.F32[n] = value->data.F32[iy+jy][ix+jx];
+			  // df->data.F32[n] = weight->data.F32[iy+jy][ix+jx];
+			  n++;
+		      }
+		  }
+		  // set vector lengths here
+		  x->n = n;
+		  y->n = n;
+		  f->n = n;
+		  // df->n = n;
+		  // psVectorFitPolynomial2D (poly, NULL, 0xff, f, df, x, y);
+		  psVectorFitPolynomial2D (poly2o, NULL, 0xff, f, NULL, x, y);
+		  // apply the fitted quadratic to get the poor pixel value
+		  // center of pixel is 0.5,0.5
+		  output->data.F32[iy][ix] = psPolynomial2DEval (poly2o, ix + 0.5, iy + 0.5);
+		  break; }
+
+	      case PS_IMAGE_INTERPOLATE_GOOD1: {
+		  // XXX is there a fit-image-region function?
+		  int n = 0;
+		  for (int jy = -1; jy <= +1; jy++) {
+		      // skip invalid pixels 
+		      if (jy + iy < 0) { continue; }
+		      if (jy + iy >= value->numRows) { continue; }
+		      for (int jx = -1; jx <= +1; jx++) {
+			  // skip invalid pixels 
+			  if (jx + ix < 0) { continue; } 
+			  if (jx + ix >= value->numCols) { continue; } 
+			  // skip masked pixels
+			  if (mask->data.PS_TYPE_MASK_DATA[iy+jy][ix+jx] & maskVal) { continue; }
+			  x->data.F32[n] = xCoord->data.F32[iy+jy][ix+jx];
+			  y->data.F32[n] = yCoord->data.F32[iy+jy][ix+jx];
+			  f->data.F32[n] = value->data.F32[iy+jy][ix+jx];
+			  // df->data.F32[n] = weight->data.F32[iy+jy][ix+jx];
+			  n++;
+		      }
+		  }
+		  // set vector lengths here
+		  x->n = n;
+		  y->n = n;
+		  f->n = n;
+		  // df->n = n;
+		  // psVectorFitPolynomial2D (poly1o, NULL, 0xff, f, df, x, y);
+		  psVectorFitPolynomial2D (poly1o, NULL, 0xff, f, NULL, x, y);
+		  // apply the fitted quadratic to get the poor pixel value
+		  output->data.F32[iy][ix] = psPolynomial2DEval (poly1o, ix + 0.5, iy + 0.5);
+		  break; }
+
+	      case PS_IMAGE_INTERPOLATE_GOOD0: {
+		  output->data.F32[iy][ix] = value->data.F32[iy][ix];
+		  break; }
+
+	      default:
+		// skip poor or bad pixels (interpolate later)
+		break;
+	    }
+	}	    
+    }
+
+    for (int iy = 0; iy < value->numRows; iy++) {
+	for (int ix = 0; ix < value->numCols; ix++) {
+	  if (mask->data.PS_TYPE_MASK_DATA[iy][ix] & maskVal) { continue; }
+	  value->data.F32[iy][ix] = output->data.F32[iy][ix];
+	}
+    }
+
+    psFree (x);
+    psFree (y);
+    psFree (f);
+
+    psFree (poly2o);
+    psFree (poly1o);
+
+    psFree (output);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelInterpolate.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelInterpolate.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelInterpolate.h	(revision 22322)
@@ -0,0 +1,41 @@
+/** @file  psImagePixelInterpolate.c
+ *
+ *  @brief Functions for interpolating bad pixels in images
+ *
+ *  @ingroup Image
+ *
+ *  @author Eugene Magnier, IfA
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-09-20 23:54:25 $
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_PIXEL_INTERPOLATE_H
+#define PS_IMAGE_PIXEL_INTERPOLATE_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+// XXX make these all bit values?
+typedef enum {
+    PS_IMAGE_INTERPOLATE_GOOD   = 0x10,
+    PS_IMAGE_INTERPOLATE_GOOD0  = 0x11,
+    PS_IMAGE_INTERPOLATE_GOOD1  = 0x12,
+    PS_IMAGE_INTERPOLATE_GOOD2  = 0x13,
+    PS_IMAGE_INTERPOLATE_BAD    = 0x01,
+    PS_IMAGE_INTERPOLATE_CENTER = 0x02,
+    PS_IMAGE_INTERPOLATE_CORNER = 0x04,
+    PS_IMAGE_INTERPOLATE_UR     = 0x04,
+    PS_IMAGE_INTERPOLATE_UL     = 0x05,
+    PS_IMAGE_INTERPOLATE_LR     = 0x06,
+    PS_IMAGE_INTERPOLATE_LL     = 0x07,
+} psImagePixelInterpolateType;
+
+psImage *psImagePixelInterpolateState (int *nBad, int *nPoor, psImage *mask, psMaskType maskVal);
+bool psImagePixelInterpolatePoor (psImage *image, psImage *state, psImage *mask, psMaskType maskVal);
+bool psImagePixelInterpolateCenter (psImage *value, psImage *xCoord, psImage *yCoord, psImage *state, psImage *mask, psMaskType maskVal);
+
+/// @}
+#endif // #ifndef PS_IMAGE_MAP_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelManip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelManip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelManip.c	(revision 22322)
@@ -0,0 +1,313 @@
+/** @file  psImagePixelManip.c
+ *
+ *  @brief Contains basic image pixel and geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Pixel Manipulations" and
+ *         "Image Geometry Manipulations".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.24 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-04-22 22:22:09 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>                          // for isfinite(), etc.
+#include <stdlib.h>
+#include <string.h>                        // for memcpy, etc.
+
+#include "psImagePixelManip.h"
+
+#include "psError.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psMemory.h"
+#include "psAssert.h"
+
+#include "psCoord.h"
+
+int psImageClip(psImage* input,
+                double min,
+                double vmin,
+                double max,
+                double vmax)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+
+    if (input == NULL) {
+        return 0;
+    }
+
+    if (max < min) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified min value, %g, can not be greater than the specified max value, %g."),
+                (double)min,(double)max);
+        return 0;
+    }
+
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    switch (input->type.type) {
+
+        #define psImageClipCase(type) \
+    case PS_TYPE_##type: { \
+            if (vmin < PS_MIN_##type || vmin > PS_MAX_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        _("Specified %s value, %g, is outside of psImage type's range (%s: %g to %g)."), \
+                        "vmin",vmin, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            if (vmax > PS_MAX_##type || vmax < PS_MIN_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        _("Specified %s value, %g, is outside of psImage type's range (%s: %g to %g)."), \
+                        "vmax",vmax, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            for (psU32 row = 0;row<numRows;row++) { \
+                ps##type* inputRow = input->data.type[row]; \
+                for (psU32 col = 0; col < numCols; col++) { \
+                    if ((psF64)inputRow[col] < min) { \
+                        inputRow[col] = (ps##type)vmin; \
+                        numClipped++; \
+                    } else if ((psF64)inputRow[col] > max) { \
+                        inputRow[col] = (ps##type)vmax; \
+                        numClipped++; \
+                    } \
+                } \
+            } \
+        } \
+        break;
+
+        psImageClipCase(S8)
+        psImageClipCase(S16)
+        psImageClipCase(S32)            // Not a requirement
+        psImageClipCase(S64)            // Not a requirement
+        psImageClipCase(U8)
+        psImageClipCase(U16)
+        psImageClipCase(U32)            // Not a requirement
+        psImageClipCase(U64)            // Not a requirement
+        psImageClipCase(F32)
+        psImageClipCase(F64)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified psImage type, %s, is not supported."),
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
+int psImageClipNaN(psImage* input,
+                   float value)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+
+    if (input == NULL) {
+        return 0;
+    }
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    switch (input->type.type) {
+
+        #define psImageClipNaNCase(type) \
+    case PS_TYPE_##type: \
+        for (psU32 row = 0;row<numRows;row++) { \
+            ps##type* inputRow = input->data.type[row]; \
+            for (psU32 col = 0; col < numCols; col++) { \
+                if (! isfinite(inputRow[col])) { \
+                    inputRow[col] = (ps##type)value; \
+                    numClipped++; \
+                } \
+            } \
+        } \
+        break;
+
+        psImageClipNaNCase(F32)
+        psImageClipNaNCase(F64)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified psImage type, %s, is not supported."),
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
+// XXX why does this have an x0,y0?  does it respect col0,row0 (in either image?)
+int psImageOverlaySection(psImage* image,
+                          const psImage* overlay,
+                          int x0,
+                          int y0,
+                          const char *op)
+{
+    psU32 imageNumRows;
+    psU32 imageNumCols;
+    psU32 overlayNumRows;
+    psU32 overlayNumCols;
+    psU32 imageRowLimit;
+    psU32 imageColLimit;
+    psElemType type;
+    psU32 pixelsOverlaid = 0;
+
+    if (image == NULL || overlay == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        return pixelsOverlaid;
+    }
+
+    if (op == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Operation can not be NULL."));
+        return pixelsOverlaid;
+    }
+
+    type = image->type.type;
+
+    if (type != overlay->type.type) {
+        char* typeStr;
+        char* typeStrOverlay;
+        PS_TYPE_NAME(typeStr,type);
+        PS_TYPE_NAME(typeStrOverlay,overlay->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Input overlay psImage type, %s, must match input psImage type, %s."),
+                typeStrOverlay, typeStr);
+        return pixelsOverlaid;
+    }
+
+    imageNumRows = image->numRows;
+    imageNumCols = image->numCols;
+    overlayNumRows = overlay->numRows;
+    overlayNumCols = overlay->numCols;
+    imageRowLimit = y0 + overlayNumRows;
+    imageColLimit = x0 + overlayNumCols;
+
+    /* check to see if overlay is within the input image */
+    if ( y0 < 0 ||
+            x0 < 0 ||
+            imageRowLimit > imageNumRows ||
+            imageColLimit > imageNumCols) {
+
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified subset range, [%d:%d,%d:%d], is invalid or outside input psImage's boundaries, [0:%d,0:%d]."),
+                x0, imageColLimit, y0, imageRowLimit,
+                imageNumCols, imageNumRows);
+        return pixelsOverlaid;
+    }
+
+
+    #define psImageOverlayLoop(DATATYPE,OP) { \
+        for (int row=y0;row<imageRowLimit;row++) { \
+            ps##DATATYPE* imageRow = image->data.DATATYPE[row]; \
+            ps##DATATYPE* overlayRow = overlay->data.DATATYPE[row-y0]; \
+            for (int col=x0;col<imageColLimit;col++) { \
+                imageRow[col] OP overlayRow[col-x0]; \
+            } \
+        } \
+        pixelsOverlaid += (imageRowLimit - y0) * (imageColLimit - x0); \
+    }
+
+    #define psImageOverlayLoopDivide(DATATYPE,BADVALUE) { \
+        for (int row=y0;row<imageRowLimit;row++) { \
+            ps##DATATYPE* imageRow = image->data.DATATYPE[row]; \
+            ps##DATATYPE* overlayRow = overlay->data.DATATYPE[row-y0]; \
+            for (int col=x0;col<imageColLimit;col++) { \
+                if (overlayRow[col-x0] == 0) { \
+                    imageRow[col] = BADVALUE; \
+                    continue; \
+                } \
+                imageRow[col] /= overlayRow[col-x0]; \
+            } \
+        } \
+        pixelsOverlaid += (imageRowLimit - y0) * (imageColLimit - x0); \
+    }
+
+    // Use memcpy to perform the '=' operation.  Depending on the particular application, it can be about 20%
+    // faster than using a 'for' loop.  Josh Hoblitt says it has an additional advantage that it doesn't blow
+    // away the L2 cache.  Of course, if you want to use the result immediately afterwards, perhaps this is
+    // a drawback?  We fall back on the loop if we have to change types.
+    #define psImageOverlaySetLoop(DATATYPE) { \
+        if (image->type.type == overlay->type.type) { \
+            int numBytes = (imageColLimit - x0) * sizeof(ps##DATATYPE); \
+            for (int row = y0; row < imageRowLimit; row++) { \
+                ps##DATATYPE *imageRow = image->data.DATATYPE[row]; \
+                ps##DATATYPE *overlayRow = overlay->data.DATATYPE[row - y0]; \
+                memcpy(&imageRow[x0], overlayRow, numBytes); \
+            } \
+            pixelsOverlaid += (imageRowLimit - y0) * (imageColLimit - x0); \
+        } else { \
+            psImageOverlayLoop(DATATYPE,=); \
+        } \
+    }
+
+    #define psImageOverlayCase(DATATYPE,BADVALUE) \
+case PS_TYPE_##DATATYPE: \
+    switch (*op) { \
+    case '+': \
+        psImageOverlayLoop(DATATYPE,+=); \
+        break; \
+    case '-': \
+        psImageOverlayLoop(DATATYPE,-=); \
+        break; \
+    case '*': \
+        psImageOverlayLoop(DATATYPE,*=); \
+        break; \
+    case '/': \
+        psImageOverlayLoopDivide(DATATYPE,BADVALUE); \
+        break; \
+    case '=': \
+        psImageOverlaySetLoop(DATATYPE); \
+        break; \
+    default: \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                _("Specified operation, '%s', is not supported."), \
+                op); \
+        return pixelsOverlaid; \
+    } \
+    break;
+
+    switch (type) {
+        psImageOverlayCase(U8, 0);
+        psImageOverlayCase(U16,0);
+        psImageOverlayCase(U32,0);       // Not a requirement
+        psImageOverlayCase(U64,0);       // Not a requirement
+        psImageOverlayCase(S8, 0);
+        psImageOverlayCase(S16,0);
+        psImageOverlayCase(S32,0);       // Not a requirement
+        psImageOverlayCase(S64,0);       // Not a requirement
+        psImageOverlayCase(F32,NAN);
+        psImageOverlayCase(F64,NAN);
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified psImage type, %s, is not supported."),
+                    typeStr);
+            return pixelsOverlaid;
+        }
+    }
+
+    return pixelsOverlaid;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelManip.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelManip.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImagePixelManip.h	(revision 22322)
@@ -0,0 +1,72 @@
+/* @file  psImagePixelManip.h
+ *
+ * @brief Basic image pixel manipulation operations
+ *
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-14 00:39:50 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_PIXEL_MANIP_H
+#define PS_IMAGE_PIXEL_MANIP_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+#include "psImage.h"
+#include "psCoord.h"
+#include "psStats.h"
+#include "psPixels.h"
+
+/** Clip image values outside of range to given values
+ *
+ *  All pixels with values less than min are set to the value vmin.  all pixels
+ *  with values greater than max are set to the value vmax. This function is
+ *  defined for psU8, psU16, psS8, psS16, psF32, psF64.
+ *
+ *  @return int     The number of clipped pixels
+ */
+int psImageClip(
+    psImage* input,                    ///< the image to clip
+    double min,                        ///< the minimum image value allowed
+    double vmin,                       ///< the value pixels < min are set to
+    double max,                        ///< the maximum image value allowed
+    double vmax                        ///< the value pixels > max are set to
+);
+
+/** Clip NaN image pixels to given value.
+ *
+ *  Pixels with NaN, +Inf, or -Inf values are set to the specified value. This
+ *  function is defined for psF32, psF64.
+ *
+ *  @return int     The number of clipped pixels
+ */
+int psImageClipNaN(
+    psImage* input,                    ///< the image to clip
+    float value                        ///< the value to set all NaN/Inf values to
+);
+
+/** Overlay subregion of image with another image
+ *
+ *  Replace the pixels in the image which correspond to the pixels in OVERLAY
+ *  with values derived from the IMAGE and OVERLAY based on the given operator
+ *  OP.  Valid operators are "=" (set image value to OVERLAY value), "+" (add
+ *  OVERLAY value to image value), "-" (subtract OVERLAY from image), "*"
+ *  (multiply OVERLAY times image), "/" (divide image by OVERLAY).  This
+ *  function is defined for psU8, psS8, psS16, psF32, psF64.
+ *
+ *  @return int         0 if success, non-zero if failed.
+ */
+int psImageOverlaySection(
+    psImage* image,                    ///< target image
+    const psImage* overlay,            ///< the overlay image
+    int x0,                            ///< the column to start overlay
+    int y0,                            ///< the row to start overlay
+    const char *op                     ///< the operation to perform for overlay
+);
+
+/// @}
+#endif // #ifndef PS_IMAGE_PIXEL_MANIP_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStats.c	(revision 22322)
@@ -0,0 +1,647 @@
+/** @file psImageStats.c
+ *  \brief Routines for calculating statistics on images.
+ *  @ingroup ImageStats
+ *
+ *  This file will hold the prototypes for procedures which calculate
+ *  statistic on images, histograms on images, and fit/evaluate Chebyshev
+ *  polynomials to images.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.106 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-03-14 00:39:50 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <float.h>
+#include <math.h>
+#include "psMemory.h"
+#include "psVector.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psStats.h"
+#include "psImage.h"
+#include "psPolynomial.h"
+#include "psImageStats.h"
+#include "psAssert.h"
+
+#include "psRegion.h"
+#include "psRegionForImage.h"
+
+/// This routine must determine the various statistics for the image.
+/*****************************************************************************
+psImageStats(stats, in, mask, maskVal): this routine simply calls the
+psVectorStats() routine, which does the actual statistical calculation.  In
+order to do so, we create dummy psVectors and set their "data" pointer to that
+of the input psImages.
+
+XXX: use static psVectors
+
+XXX: optimize this.  2k vs 4k, sample mean, takes8 seconds on Gene's machine.
+Should take .2.
+ *****************************************************************************/
+bool psImageStats(psStats* stats,
+                      const psImage* in,
+                      const psImage* mask,
+                      psMaskType maskVal)
+{
+    psVector *junkData = NULL;
+    psVector *junkMask = NULL;
+
+    PS_ASSERT_PTR_NON_NULL(stats, false);
+    PS_ASSERT_INT_NONZERO(stats->options, false);
+    PS_ASSERT_IMAGE_NON_NULL(in, false)
+    if (mask != NULL) {
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(in, mask, false);
+    }
+
+    if (in->parent == NULL) {
+        // stuff the image data into a psVector struct.
+        junkData = (psVector *) psAlloc(sizeof(psVector));
+        junkData->type = in->type;
+        P_PSVECTOR_SET_NALLOC(junkData,in->numRows * in->numCols);
+        junkData->n = junkData->nalloc;
+        junkData->data.U8 = in->data.V[0];      // since psImage data is contiguous...
+    } else {
+        // image not necessarily contiguous
+        int numRows = in->numRows;
+        int numCols = in->numCols;
+        int rowSize = numCols * (PSELEMTYPE_SIZEOF(in->type.type));
+
+        junkData = psVectorAlloc(numRows*numCols, in->type.type);
+
+        psU8* data = junkData->data.U8;
+        for (int row = 0; row < numRows; row++) {
+            memcpy(data, in->data.V[row], rowSize);
+            data += rowSize;
+        }
+    }
+
+    if (mask != NULL) {
+        if (mask->parent == NULL) {
+            // stuff the mask data into a psVector struct.
+            junkMask = psAlloc(sizeof(psVector));
+            junkMask->type = mask->type;
+            P_PSVECTOR_SET_NALLOC(junkMask,mask->numRows * mask->numCols);
+            junkMask->n = junkMask->nalloc;
+            junkMask->data.U8 = mask->data.V[0];
+        } else {
+            // image not necessarily contiguous
+            int numRows = mask->numRows;
+            int numCols = mask->numCols;
+            int rowSize = numCols * (PSELEMTYPE_SIZEOF(mask->type.type));
+
+            junkMask = psVectorAlloc(numRows*numCols, mask->type.type);
+
+            psU8* data = junkMask->data.U8;
+            for (int row = 0; row < numRows; row++) {
+                memcpy(data, mask->data.V[row], rowSize);
+                data += rowSize;
+            }
+        }
+    }
+
+    psVectorStats(stats, junkData, NULL, junkMask, maskVal);
+
+    psFree(junkMask);
+    psFree(junkData);
+    return true;
+}
+
+/*****************************************************************************
+NOTE: We assume that the psHistogram structure out has already been allocated
+and initialized.
+ *****************************************************************************/
+bool psImageHistogram(psHistogram* out,
+                              const psImage* in,
+                              const psImage* mask,
+                              psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(out, false);
+    PS_ASSERT_PTR_NON_NULL(in, false);
+    if (mask != NULL) {
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(in, mask, false);
+    }
+    psVector* junkData = NULL;
+    psVector* junkMask = NULL;
+
+    if (in->parent == NULL) {
+        // stuff the image data into a psVector struct.
+        junkData = (psVector *) psAlloc(sizeof(psVector));
+        junkData->type = in->type;
+        P_PSVECTOR_SET_NALLOC(junkData,in->numRows * in->numCols);
+        junkData->n = junkData->nalloc;
+        junkData->data.U8 = in->data.V[0];      // since psImage data is contiguous...
+    } else {
+        // image not necessarily contiguous
+        int numRows = in->numRows;
+        int numCols = in->numCols;
+        int rowSize = numCols * (PSELEMTYPE_SIZEOF(in->type.type));
+
+        junkData = psVectorAlloc(numRows*numCols, in->type.type);
+
+        psU8* data = junkData->data.U8;
+        for (int row = 0; row < numRows; row++) {
+            memcpy(data, in->data.V[row], rowSize);
+            data += rowSize;
+        }
+    }
+
+    if (mask != NULL) {
+        if (mask->parent == NULL) {
+            // stuff the mask data into a psVector struct.
+            junkMask = psAlloc(sizeof(psVector));
+            junkMask->type = mask->type;
+            P_PSVECTOR_SET_NALLOC(junkMask,mask->numRows * mask->numCols);
+            junkMask->n = junkMask->nalloc;
+            junkMask->data.U8 = mask->data.V[0];
+        } else {
+            // image not necessarily contiguous
+            int numRows = mask->numRows;
+            int numCols = mask->numCols;
+            int rowSize = numCols * (PSELEMTYPE_SIZEOF(mask->type.type));
+
+            junkMask = psVectorAlloc(numRows*numCols, mask->type.type);
+
+            psU8* data = junkMask->data.U8;
+            for (int row = 0; row < numRows; row++) {
+                memcpy(data, mask->data.V[row], rowSize);
+                data += rowSize;
+            }
+        }
+    }
+
+    if (!psVectorHistogram(out, junkData, NULL, junkMask, maskVal)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate image histogram.\n");
+        psFree(junkMask);
+        psFree(junkData);
+        return false;
+    }
+
+    psFree(junkMask);
+    psFree(junkData);
+
+    return true;
+}
+
+/*****************************************************************************
+calcScaleFactorsEval(n): The Chebyshev polynomials are defined over the
+interval [-1.0 : 1.0].  Images typically have sizes of 512x512 or more.  In
+order to use Chebyshev polynomials, we must scale the coordinates from
+0:512 to -1:1.  This routine takes as input an integer N and produces as
+output a vector of evenly spaced floating point values between -1.0:1.0.
+
+XXX: Use the p_psNormalizeVector here?
+ *****************************************************************************/
+static double* calcScaleFactors(psS32 n)
+{
+    PS_ASSERT_INT_NONNEGATIVE(n, NULL);
+    psS32 i = 0;
+    double tmp = 0.0;
+    double *scalingFactors = (double *)psAlloc(n * sizeof(double));
+
+    for (i = 0; i < n; i++) {
+        tmp = (double)(n - i);
+        tmp = (M_PI * (tmp - 0.5)) / ((double)n);
+        scalingFactors[i] = cos(tmp);
+    }
+
+    return (scalingFactors);
+}
+
+/*****************************************************************************
+psImageFitPolynomial(): This routine takes as input a 2-D image and produces
+as output the coefficients of the Chebyshev polynomials which match that
+input image.
+  Input:
+  Output:
+  Internal Data Structures:
+    chebPolys[i][j]
+    sums[i][j]: This will contain the sum of
+                input->data.F32[y][x] *
+                psPolynomial1DEval(
+chebPolys[i],
+(float) x) *
+                psPolynomial1DEval(
+chebPolys[j],
+(float) y,
+);
+        over all pixels (x,y) in the image.
+  *****************************************************************************/
+bool psImageFitPolynomial(psPolynomial2D* coeffs,
+                                     const psImage* input)
+{
+    PS_ASSERT_IMAGE_NON_NULL(input, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(input, false);
+    if ((input->type.type != PS_TYPE_S8) &&
+            (input->type.type != PS_TYPE_U16) &&
+            (input->type.type != PS_TYPE_F32) &&
+            (input->type.type != PS_TYPE_F64)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unallowable image type.\n");
+        return false;
+    }
+    PS_ASSERT_POLY_NON_NULL(coeffs, false);
+    PS_ASSERT_POLY_TYPE(coeffs, PS_POLYNOMIAL_CHEB, false);
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 i = 0;
+    psS32 j = 0;
+    double **sums = NULL;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+    double *cScalingFactors = NULL;
+    double *rScalingFactors = NULL;
+
+    // Create the sums[][] data structure.  This
+    // will hold the LHS of
+    // equation
+    // 29 in the ADD: sums[k][l] = SUM {
+    // image(x,y) * Tk(x) * Tl(y) }
+    sums = (double **)psAlloc((1 + coeffs->nX) * sizeof(double *));
+    for (i = 0; i < (1 + coeffs->nX); i++) {
+        sums[i] = (double *)psAlloc((1 + coeffs->nY) * sizeof(double));
+    }
+    // We scale the pixel positions to values
+    // between -1.0 and 1.0
+    //    rScalingFactors = calcScaleFactors(input->numRows);
+    //    cScalingFactors = calcScaleFactors(input->numCols);
+    rScalingFactors = calcScaleFactors(input->row0 + input->numRows);
+    cScalingFactors = calcScaleFactors(input->col0 + input->numCols);
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    // XXX: record or verify the poly order/nterm change
+    maxChebyPoly = coeffs->nX;
+    if (coeffs->nY > coeffs->nX) {
+        maxChebyPoly = coeffs->nY;
+    }
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly + 1);
+
+    // Compute the sums[][] data structure.
+    for (i = 0; i < (1 + coeffs->nX); i++) {
+        for (j = 0; j < (1 + coeffs->nY); j++) {
+            sums[i][j] = 0.0;
+            //            for (x = 0; x < input->numRows; x++) {
+            //                for (y = 0; y < input->numCols; y++) {
+            for (y = input->row0; y < (input->row0 + input->numRows); y++) {
+                for (x = input->col0; x < (input->col0 + input->numCols); x++) {
+                    double pixel = 0.0;
+                    if (input->type.type == PS_TYPE_S8) {
+                        pixel = (double) input->data.S8[y][x];
+                    } else if (input->type.type == PS_TYPE_U16) {
+                        pixel = (double) input->data.U16[y][x];
+                    } else if (input->type.type == PS_TYPE_F32) {
+                        pixel = (double) input->data.F32[y][x];
+                    } else if (input->type.type == PS_TYPE_F64) {
+                        pixel = input->data.F64[y][x];
+                    }
+                    sums[i][j] += pixel * psPolynomial1DEval(chebPolys[i],rScalingFactors[y]) *
+                                  psPolynomial1DEval(chebPolys[j], cScalingFactors[x]);
+                }
+            }
+        }
+    }
+
+    for (i = 0; i < (1 + coeffs->nX); i++) {
+        for (j = 0; j < (1 + coeffs->nY); j++) {
+            coeffs->coeff[i][j] = sums[i][j];
+            coeffs->coeff[i][j] /= (double)(input->numRows * input->numCols);
+
+            if ((i != 0) && (j != 0)) {
+                coeffs->coeff[i][j] *= 4.0;
+            } else if ((i == 0) && (j == 0)) {
+                coeffs->coeff[i][j] *= 1.0;
+            } else {
+                coeffs->coeff[i][j] *= 2.0;
+            }
+        }
+    }
+
+    // Free the Chebyshev polynomials that were
+    // created in this routine.
+    for (i = 0; i < maxChebyPoly + 1; i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+
+    // Free some data
+    for (i = 0; i < (1 + coeffs->nX); i++) {
+        psFree(sums[i]);
+    }
+    psFree(sums);
+    psFree(cScalingFactors);
+    psFree(rScalingFactors);
+
+    return true;
+}
+
+/*****************************************************************************
+XXX: Use static variables for Chebyshev polynomials and scaling factors.
+ *****************************************************************************/
+psImage* p_psImageEvalPolynomialCheb(psImage* input,
+                                     const psPolynomial2D* coeffs)
+{
+    PS_ASSERT_POLY_TYPE(coeffs, PS_POLYNOMIAL_CHEB, NULL);
+
+    psPolynomial1D* *chebPolys = NULL;
+    long maxChebyPoly = 0;
+    double *cScalingFactors = NULL;
+    double *rScalingFactors = NULL;
+
+    // We scale the pixel positions to values between -1.0 and 1.0
+    // Use static data structures here.
+    //    rScalingFactors = calcScaleFactors(input->numRows);
+    //    cScalingFactors = calcScaleFactors(input->numCols);
+    rScalingFactors = calcScaleFactors(input->numRows+input->row0);
+    cScalingFactors = calcScaleFactors(input->numCols+input->col0);
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = coeffs->nX;
+    if (coeffs->nY > coeffs->nX) {
+        maxChebyPoly = coeffs->nY;
+    }
+
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly + 1);
+
+    for (long y = input->row0; y < (input->row0 + input->numRows); y++) {
+        for (long x = input->col0; x < (input->col0 + input->numCols); x++) {
+            double polySum = 0.0;
+            for (long i = 0; i < (1 + coeffs->nX); i++) {
+                for (long j = 0; j < (1 + coeffs->nY); j++) {
+                    polySum +=
+                        psPolynomial1DEval(chebPolys[i], rScalingFactors[y]) *
+                        psPolynomial1DEval(chebPolys[j], cScalingFactors[x]) *
+                        coeffs->coeff[i][j];
+                }
+            }
+
+            if (input->type.type == PS_TYPE_S8) {
+                input->data.S8[y][x] = (char) polySum;
+            } else if (input->type.type == PS_TYPE_U16) {
+                input->data.U16[y][x] = (short int) polySum;
+            } else if (input->type.type == PS_TYPE_F32) {
+                input->data.F32[y][x] = (float) polySum;
+            } else if (input->type.type == PS_TYPE_F64) {
+                input->data.F64[y][x] = polySum;
+            }
+        }
+    }
+
+    // Free the Chebyshev polynomials that were
+    // created in this routine.
+    // XXX: Use static data structures here.
+    for (long i = 0; i < maxChebyPoly + 1; i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+
+    psFree(cScalingFactors);
+    psFree(rScalingFactors);
+
+    return input;
+}
+
+psImage* p_psImageEvalPolynomialOrd(psImage* input,
+                                    const psPolynomial2D* coeffs)
+{
+    PS_ASSERT_POLY_TYPE(coeffs, PS_POLYNOMIAL_ORD, NULL);
+
+    for (int row = 0; row < input->numRows ; row++) {
+        for (int col = 0; col < input->numCols ; col++) {
+            if (input->type.type == PS_TYPE_S8) {
+                input->data.S8[row][col] = (psS8) psPolynomial2DEval(coeffs,
+                                           (psF32) (row + input->row0), (psF32) (col + input->col0));
+            } else if (input->type.type == PS_TYPE_U16) {
+                input->data.U16[row][col] = (psS16) psPolynomial2DEval(coeffs,
+                                            (psF32) (row + input->row0), (psF32) (col + input->col0));
+            } else if (input->type.type == PS_TYPE_F32) {
+                input->data.F32[row][col] = psPolynomial2DEval(coeffs,
+                                            (psF32) (row + input->row0), (psF32) (col + input->col0));
+            } else if (input->type.type == PS_TYPE_F64) {
+                input->data.F64[row][col] = (psF64) psPolynomial2DEval(coeffs,
+                                            (psF32) (row + input->row0), (psF32) (col + input->col0));
+            }
+        }
+    }
+
+    return(input);
+}
+
+
+/*****************************************************************************
+XXX: I added normal polynomials to this routine.  Let IfA know, put it in the
+psLib SDR.
+ *****************************************************************************/
+psImage* psImageEvalPolynomial(psImage* input,
+                               const psPolynomial2D* coeffs)
+{
+    PS_ASSERT_IMAGE_NON_NULL(input, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(input, NULL);
+    if ((input->type.type != PS_TYPE_S8) &&
+            (input->type.type != PS_TYPE_U16) &&
+            (input->type.type != PS_TYPE_F32) &&
+            (input->type.type != PS_TYPE_F64)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Unallowable image type.\n");
+    }
+    PS_ASSERT_POLY_NON_NULL(coeffs, NULL);
+
+    if (coeffs->type == PS_POLYNOMIAL_ORD) {
+        return(p_psImageEvalPolynomialOrd(input, coeffs));
+    } else if (coeffs->type == PS_POLYNOMIAL_CHEB) {
+        return(p_psImageEvalPolynomialCheb(input, coeffs));
+    } else {
+        psError(PS_ERR_UNKNOWN, false, "Incorrect Polynomial Type.\n");
+    }
+    return(NULL);
+}
+
+// count number of pixels with given mask value
+long psImageCountPixelMask (psImage *mask,
+                            psRegion region,
+                            psMaskType value)
+{
+    long Npixels = 0;
+    int x0 = 0;
+    int y0 = 0;
+    int x1 = 0;
+    int y1 = 0;
+    psElemType type;
+
+    // this is not a valid error: a psRegion with ranges outside the valid pixels
+    // should saturate on the valid pixels, not result in an error (per SDRS)
+    //    if (region.x1 > mask->numCols || region.y1 > mask->numRows) {
+    //        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+    //                "psRegion input is outside of image boundary\n");
+    //        return -1;
+    //    }
+    //    if (region.x0 <= 0 || region.x1 <= 0 || region.y0 <= 0 || region.y1 <= 0) {
+    //        region = psRegionForImage(mask, region);
+    //    }
+    // not a valid error: if region coordinates are out of order, they should be flipped
+    //    if (region.x0 > region.x1 || region.y0 > region.y1) {
+    //        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+    //               "Invalid region.  Lower boundary greater than upper boundary.\n");
+    //        return -1;
+    //    }
+
+    // rationalize the region
+
+    /*
+        if (mask == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    _("Can not operate on a NULL psImage."));
+            return -1;
+        }
+        region = psRegionForImage(mask, region);
+
+        if (region.x0 == region.x1 || region.y0 == region.y1) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "psRegion input contains 0 pixels\n");
+            return -1;
+        }
+    */
+    PS_ASSERT_IMAGE_NON_NULL(mask, -1);
+    PS_ASSERT_INT_NONNEGATIVE(mask->col0, -1);
+    PS_ASSERT_INT_NONNEGATIVE(mask->row0, -1);
+    PS_ASSERT_INT_POSITIVE(mask->numCols, -1);
+    PS_ASSERT_INT_POSITIVE(mask->numRows, -1);
+
+    int col0 = (int)(roundf(region.x0));
+    int col1 = (int)(roundf(region.x1));
+    int row0 = (int)(roundf(region.y0));
+    int row1 = (int)(roundf(region.y1));
+    //If (0,0,0,0) specified, the whole image is to be used.
+    if (col0 == 0 && col1 == 0 && row0 == 0 && row1 == 0) {
+        col0 = mask->col0;
+        col1 = mask->col0 + mask->numCols;
+        row0 = mask->row0;
+        row1 = mask->row0 + mask->numRows;
+    }
+
+    //Make sure x0 of region is inside image.  If so, set col0 to corresponding index number.
+    if (col0 >= mask->col0 && col0 <= (mask->col0 + mask->numCols) ) {
+        col0 -= mask->col0;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified psRegion parameter, x0=%f, is out of range [%d,%d].\n",
+                region.x0, mask->col0, mask->col0+mask->numCols);
+        return -1;
+    }
+    //Make sure y0 of region is inside image.  If so, set row0 to corresponding index number.
+    if (row0 >= mask->row0 && row0 <= (mask->row0 + mask->numRows) ) {
+        row0 -= mask->row0;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified psRegion parameter, y0=%f, is out of range [%d,%d].\n",
+                region.y0, mask->row0, mask->row0+mask->numRows);
+        return -1;
+    }
+
+    //Make sure x1 of region is valid.  If negative, index from tail (if valid).
+    if (col1 < 0) {
+        col1 += mask->numCols;
+        if (col1 < 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Specified psRegion parameter, x1=%f=%d, is out of range [%d,%d].\n",
+                    region.x1, col1+mask->col0, mask->col0, mask->col0+mask->numCols);
+            return -1;
+        }
+    } else if (col1 >= mask->col0 && col1 <= (mask->col0 + mask->numCols) ) {
+        col1 -= mask->col0;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified psRegion parameter, x1=%f=%d, is out of range [%d,%d].\n",
+                region.x1, col1, mask->col0, mask->col0+mask->numCols);
+        return -1;
+    }
+    //Make sure y1 of region is valid.  If negative, index from tail (if valid).
+    if (row1 < 0) {
+        row1 += mask->numRows;
+        if (row1 < 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Specified psRegion parameter, y1=%f=%d, is out of range [%d,%d].\n",
+                    region.y1, row1+mask->row0, mask->row0, mask->row0+mask->numRows);
+            return -1;
+        }
+    } else if (row1 >= mask->row0 && row1 <= (mask->row0 + mask->numRows) ) {
+        row1 -= mask->row0;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified psRegion parameter, y1=%f=%d, is out of range [%d,%d].\n",
+                region.y1, row1, mask->row0, mask->row0+mask->numRows);
+        return -1;
+    }
+    //Now make sure that the region makes sense.
+    if (col0 > col1 || row0 > row1) {
+        if (col0 > col1) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Invalid psRegion specified.  x0=%f=%d is greater than x1=%f=%d.\n",
+                    region.x0, col0, region.x1, col1);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Invalid psRegion specified.  y0=%f=%d is greater than y1=%f=%d.\n",
+                    region.y0, row0, region.y1, row1);
+        }
+        return -1;
+    }/* else if (col0 == col1 && row0 == row1) {
+                                                            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                                                                    "Invalid psRegion specified.  Region contains only 1 pixel.\n");
+                                                            return -1;
+                                                        }
+                                                    */
+    x0 = col0;
+    x1 = col1;
+    y0 = row0;
+    y1 = row1;
+
+    type = mask->type.type;
+    if (type != PS_TYPE_MASK) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "psImage type does not match the specified psMaskType!\n");
+        return -1;
+    }
+
+    switch (type) {
+    case PS_TYPE_U8:
+    case PS_TYPE_U16:
+        for (long j = y0; j < y1; j++) {
+            for (long i = x0; i < x1; i++) {
+                if (mask->data.PS_TYPE_MASK_DATA[j][i] & value) {
+                    Npixels ++;
+                }
+            }
+        }
+        break;
+    case PS_TYPE_S8:
+    case PS_TYPE_S16:
+    case PS_TYPE_S32:
+    case PS_TYPE_S64:
+    case PS_TYPE_U32:
+    case PS_TYPE_U64:
+    case PS_TYPE_F32:
+    case PS_TYPE_F64:
+    default:
+        // XXX this should include the mask type (as a string)
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Input psImage mask type is not a supported mask datatype"));
+
+        return -1;
+    }
+    return (Npixels);
+
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStats.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStats.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStats.h	(revision 22322)
@@ -0,0 +1,103 @@
+/* @file psImageStats.h
+ *
+ * @brief Routines for calculating statistics on images.
+ *
+ * This file will hold the prototypes for procedures which calculate
+ * statistic on images, histograms on images, and fit/evaluate Chebyshev
+ * polynomials to images.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.31 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-02-13 03:01:23 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_STATS_H
+#define PS_IMAGE_STATS_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+#include "psType.h"
+#include "psVector.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psHistogram.h"
+#include "psPolynomial.h"
+#include "psRegion.h"
+
+/** This routine must determine the various statistics for the image.
+ *
+ *  Determine statistics for image (or subimage). The statistics to be
+ *  determined are specified by stats. The mask allows pixels to be excluded
+ *  if their corresponding mask pixel value matches the value of maskVal.
+ *  This function must be defined for the following types: psS8, psU16, psF32,
+ *  psF64.
+ *
+ *  @return bool   Successful operation?
+ */
+bool psImageStats(
+    psStats* stats,                    ///< defines statistics to be calculated
+    const psImage* in,                 ///< image (or subimage) to calculate stats
+    const psImage* mask,               ///< mask data for image (NULL ok)
+    psMaskType maskVal                 ///< mask value for mask
+);
+
+/** Construct a histogram from an image (or subimage).
+ *
+ *  The histogram to generate is specified by psHistogram hist (see section
+ *  4.3.2 in SDRS). This function must be defined for the following types:
+ *  psS8, psU16, psF32, psF64.
+ *
+ *  @return bool   Successful operation?
+ */
+bool psImageHistogram(
+    psHistogram* out,                  ///< input histogram description & target
+    const psImage* in,                 ///< Image data to be histogramed.
+    const psImage* mask,               ///< mask data for image (NULL ok)
+    psMaskType maskVal                 ///< mask Mask for mask
+);
+
+/** Fit a 2-D polynomial surface to an image.
+ *
+ *  The input structure coeffs contains the desired order and terms of
+ *  interest. This function must be defined for the following types: psS8,
+ *  psU16, psF32, psF64.
+ *
+ *  @return bool   Successful operation?
+ *
+ */
+bool psImageFitPolynomial(
+    psPolynomial2D* coeffs,            ///< coefficient structure carries in desired terms & target
+    const psImage* input               ///< input image
+);
+
+/** Evaluate a 2-D polynomial surface for the image pixels.
+ *
+ *  Given the input polynomial coefficients, set the image pixel values on the
+ *  basis of the polynomial function. This function must be defined for the
+ *  following types: psS8, psU16, psF32, psF64.
+ *
+ *  @return psImage*    the resulting image
+ */
+psImage* psImageEvalPolynomial(
+    psImage* input,                    ///< input image
+    const psPolynomial2D* coeffs       ///< coefficient structure carries in desired terms
+);
+
+/** Returns the number of pixels in the image region which satisfy any of the mask bits.
+ *
+ *  An error (eg, invalid image, invalid region) results in a return value of -1.
+ *  The vector must be U8.
+ *
+ *  @return long:       the number of pixels counted
+ */
+long psImageCountPixelMask(
+    psImage *mask,                     ///< input image to count
+    psRegion region,                   ///< input region of image
+    psMaskType value                   ///< the mask value to satisfy
+);
+
+/// @}
+#endif // #ifndef PS_IMAGE_STATS_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStructManip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStructManip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStructManip.c	(revision 22322)
@@ -0,0 +1,382 @@
+/** @file  psImageStructManip.c
+ *
+ *  @brief Contains basic image structure manipulations, as specified in the
+ *         PSLIB SDRS sections "Image Structure Manipulation".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.20 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-08 01:09:48 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+
+#include "psImageStructManip.h"
+
+// col0,row0 are the starting pixel in the input image coordinate frame
+// col1,row1 are the ending pixel in the input image coordinate frame
+// note that these are relative to the input col0,row0
+// also note that col0,row0 may not be less than input->col0,row0
+static psImage* imageSubset(const char *file, // File name of caller
+                            unsigned int lineno, // Line number of caller
+                            const char *func, // Function name of caller
+                            psImage* out,
+                            psImage* image,
+                            psS32 col0,
+                            psS32 row0,
+                            psS32 col1,
+                            psS32 row1)
+{
+    psU32 elementSize;          // size of image element in bytes
+    psS32 inputColOffset;       // offset in **bytes** to first subset pixel in input row
+    psS32 inputRowOffset;       // offset in **rows*** to first input row
+
+    if (image == NULL || image->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        return NULL;
+    }
+
+    if ( col0 < image->col0 || row0 < image->row0 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified subset range, [%d:%d,%d:%d], is invalid or outside input psImage's boundaries, [%d:%d,%d:%d]."),
+                col0, col1-1, row0, row1-1, image->col0, image->col0 + image->numCols-1, image->row0,
+                image->row0 + image->numRows-1);
+        return NULL;
+    }
+
+    if (image->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("The input psImage must have a PS_DIMEN_IMAGE dimension type."));
+        return NULL;
+    }
+
+    if (col1 < 1) {
+        col1 = image->col0 + image->numCols + col1;
+    }
+    if (row1 < 1) {
+        row1 = image->row0 + image->numRows + row1;
+    }
+
+    if (col1 <= col0 ||
+        row1 <= row0 ||
+        col0 >= image->col0 + image->numCols ||
+        row0 >= image->row0 + image->numRows ||
+        col1 > image->col0 + image->numCols ||
+        row1 > image->row0 + image->numRows ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified subset range, [%d:%d,%d:%d], is invalid or outside input psImage's boundaries, [%d:%d,%d:%d]."),
+                col0, col1-1, row0, row1-1,
+                image->col0, image->col0 + image->numCols-1, image->row0, image->row0 + image->numRows-1);
+        return NULL;
+    }
+
+    psS32 numRows = row1-row0;
+    psS32 numCols = col1-col0;
+
+    elementSize = PSELEMTYPE_SIZEOF(image->type.type);
+
+    // if this is a child, we need to start working with parent pixels
+    // the subset region (col0,row0 - col1,row1) is in the parent frame
+    if (image->parent != NULL) {
+        image = (psImage*)image->parent;
+    }
+
+    // increment the raw data buffer before freeing anything in the 'out'
+    psPtr rawData = psMemIncrRefCounter(image->p_rawDataBuffer);
+
+    if (out != NULL) {
+        // if a child, need to orphan (disassociate from parent) first
+        psImage *parent = (psImage *) out->parent;
+        if (parent != NULL) {
+            // break the back-pointer first so we don't loop
+            out->parent = NULL;
+
+            // drop my entry on my parent's array of children
+            psArrayRemoveDataNoFree (out->parent->children, out);
+
+            // drop my reference to my old parent
+            psFree (parent);
+        }
+
+        // we recycle out->data.V
+        psFree(out->p_rawDataBuffer); // free the previous data reference
+    } else {
+        out = p_psAlloc(file, lineno, func, sizeof(psImage));
+        out->data.V = NULL;
+    }
+
+    out->data.V = p_psRealloc(file, lineno, func, out->data.V,
+                              sizeof(psPtr)*numRows); // resize row pointer array
+    P_PSIMAGE_SET_TYPE(out, image->type);
+    P_PSIMAGE_SET_NUMCOLS(out, numCols);
+    P_PSIMAGE_SET_NUMROWS(out, numRows);
+    P_PSIMAGE_SET_COL0(out, col0);
+    P_PSIMAGE_SET_ROW0(out, row0);
+
+    out->parent = psMemIncrRefCounter(image); // track references to parents
+    out->children = NULL;
+    out->p_rawDataBuffer = rawData;
+
+    // set the new psImage's deallocator to the same as the input image
+    psMemSetDeallocator(out,psMemGetDeallocator(image));
+
+    inputRowOffset = (row0 - image->row0);
+    inputColOffset = (col0 - image->col0)*elementSize;
+    assert (inputRowOffset >= 0);
+    assert (inputColOffset >= 0);
+    for (psS32 row = 0; row < numRows; row++) {
+        out->data.V[row] = image->data.U8[row + inputRowOffset] + inputColOffset;
+    }
+
+    // add output image as a child of the input image.
+    image->children = p_psArrayAdd(file, lineno, func, image->children, 16, out);
+    psFree (out); // the image->children array is an array of views only
+
+    return (out);
+}
+
+psImage* p_psImageSubset(const char *file, unsigned int lineno, const char *func,
+                       psImage* image, psRegion region)
+{
+    return imageSubset(file, lineno, func, NULL, image, region.x0, region.y0, region.x1, region.y1);
+}
+
+psImage* psImageCopyView(psImage *output, psImage *input)
+{
+    psRegion region = {0, 0, 0, 0};
+    region = psRegionForImage (input, region);
+    psImage *result = imageSubset(__FILE__, __LINE__, __func__, output, input,
+                                  region.x0, region.y0, region.x1, region.y1);
+    return result;
+}
+
+psImage* p_psImageCopy(const char *file, unsigned int lineno, const char *func,
+                       psImage* output, const psImage* input, psElemType type)
+{
+    psElemType inDatatype;
+    psS32 elementSize;
+    psS32 elements;
+    psS32 numRows;
+    psS32 numCols;
+
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        psFree(output);
+        return NULL;
+    }
+
+    if (input == output) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified input and output psImage can not reference the same psImage."));
+        psFree(output);
+        return NULL;
+    }
+
+    if (input->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("The input psImage must have a PS_DIMEN_IMAGE dimension type."));
+        psFree(output);
+        return NULL;
+    }
+
+    inDatatype = input->type.type;
+    numRows = input->numRows;
+    numCols = input->numCols;
+    elements = numRows * numCols;
+    elementSize = PSELEMTYPE_SIZEOF(inDatatype);
+
+    output = p_psImageRecycle(file, lineno, func, output, numCols, numRows, type);
+    P_PSIMAGE_SET_COL0(output, input->col0);
+    P_PSIMAGE_SET_ROW0(output, input->row0);
+
+    // cover the trival case of copy of the same
+    // datatype.
+    if (type == inDatatype) {
+        for (psS32 row=0;row<numRows;row++) {
+            memcpy(output->data.V[row], input->data.V[row], elementSize * numCols);
+        }
+        return output;
+    }
+
+    #define PSIMAGE_ELEMENT_COPY(IN,INTYPE,OUT,OUTTYPE,ELEMENTS) { \
+        ps##INTYPE *in; \
+        ps##OUTTYPE *out; \
+        for(psS32 row=0;row<numRows;row++) { \
+            in = IN->data.INTYPE[row]; \
+            out = OUT->data.OUTTYPE[row]; \
+            for (psS32 col=0;col<numCols;col++) { \
+                *(out++) = *(in++); \
+            } \
+        } \
+    }
+
+    #define PSIMAGE_COPY_CASE(OUT,OUTTYPE) { \
+        switch (inDatatype) { \
+        case PS_TYPE_S8: \
+            PSIMAGE_ELEMENT_COPY(input,S8,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_S16: \
+            PSIMAGE_ELEMENT_COPY(input,S16,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_S32: \
+            PSIMAGE_ELEMENT_COPY(input,S32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_S64: \
+            PSIMAGE_ELEMENT_COPY(input,S64,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U8: \
+            PSIMAGE_ELEMENT_COPY(input,U8,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U16: \
+            PSIMAGE_ELEMENT_COPY(input,U16,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U32: \
+            PSIMAGE_ELEMENT_COPY(input,U32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U64: \
+            PSIMAGE_ELEMENT_COPY(input,U64,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_F32: \
+            PSIMAGE_ELEMENT_COPY(input,F32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_F64: \
+            PSIMAGE_ELEMENT_COPY(input,F64,OUT,OUTTYPE,elements); \
+            break; \
+        default: \
+            break; \
+        } \
+    }
+
+    switch (type) {
+    case PS_TYPE_S8:
+        PSIMAGE_COPY_CASE(output, S8);
+        break;
+    case PS_TYPE_S16:
+        PSIMAGE_COPY_CASE(output, S16);
+        break;
+    case PS_TYPE_S32:
+        PSIMAGE_COPY_CASE(output, S32);
+        break;
+    case PS_TYPE_S64:
+        PSIMAGE_COPY_CASE(output, S64);
+        break;
+    case PS_TYPE_U8:
+        PSIMAGE_COPY_CASE(output, U8);
+        break;
+    case PS_TYPE_U16:
+        PSIMAGE_COPY_CASE(output, U16);
+        break;
+    case PS_TYPE_U32:
+        PSIMAGE_COPY_CASE(output, U32);
+        break;
+    case PS_TYPE_U64:
+        PSIMAGE_COPY_CASE(output, U64);
+        break;
+    case PS_TYPE_F32:
+        PSIMAGE_COPY_CASE(output, F32);
+        break;
+    case PS_TYPE_F64:
+        PSIMAGE_COPY_CASE(output, F64);
+        break;
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified psImage type, %s, is not supported."),
+                    typeStr);
+            psFree(output);
+
+            break;
+        }
+    }
+    return output;
+}
+
+psImage* psImageTrim(psImage* image,
+                     psRegion region)
+{
+    if (image == NULL || image->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        return NULL;
+    }
+
+    if ((image->children != NULL) && (image->children->n > 0)) {
+        psAbort ("cannot trim an image with outstanding children");
+    }
+
+    if (image->parent != NULL) {
+        return imageSubset(__FILE__, __LINE__, __func__, image, (psImage*)image->parent,
+                           region.x0+image->col0, region.y0+image->row0,
+                           region.x1+image->col0, region.y1+image->row0);
+    }
+
+    int col0 = region.x0;
+    int row0 = region.y0;
+    int col1 = region.x1;
+    int row1 = region.y1;
+
+    if (col1 < 1) {
+        col1 += image->numCols;
+    }
+
+    if (row1 < 1) {
+        row1 += image->numRows;
+    }
+
+    if (    col0 < 0 ||
+            row0 < 0 ||
+            col1 > image->numCols ||
+            row1 > image->numRows ||
+            col0 >= col1 ||
+            row0 >= row1 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified subset range, [%d:%d,%d:%d], is invalid or outside input psImage's boundaries, [0:%d,0:%d]."),
+                col0, col1-1, row0, row1-1,
+                image->numCols-1, image->numRows-1);
+        psFree(image);
+        return NULL;
+    }
+
+    psU32 elementSize = PSELEMTYPE_SIZEOF(image->type.type);
+    psU32 numCols = col1-col0;
+    psU32 numRows = row1-row0;
+    psU32 rowSize = elementSize*numCols;
+    psU32 colOffset = elementSize * col0;
+    psU8* imageData = image->p_rawDataBuffer;
+    for (psS32 row = row0; row < row1; row++) {
+        memmove(imageData,image->data.U8[row] + colOffset,rowSize);
+        imageData += rowSize;
+    }
+
+    P_PSIMAGE_SET_NUMCOLS(image, numCols);
+    P_PSIMAGE_SET_NUMROWS(image, numRows);
+
+    // XXX: should I really resize the buffers?
+    image->data.V = psRealloc(image->data.V,sizeof(psPtr)*numRows);
+    image->p_rawDataBuffer = psRealloc(image->p_rawDataBuffer,rowSize*numRows);
+
+    image->data.V[0] = image->p_rawDataBuffer;
+    for (psS32 r = 1; r < numRows; r++) {
+        image->data.U8[r] = image->data.U8[r-1] + rowSize;
+    }
+
+    return (image);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStructManip.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStructManip.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageStructManip.h	(revision 22322)
@@ -0,0 +1,108 @@
+/** @file  psImageStructManip.h
+*
+*  @brief basic image structure manipulation operations
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-11-08 01:10:15 $
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PSIMAGE_STRUCT_MANIP_H
+#define PSIMAGE_STRUCT_MANIP_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+#include "psImage.h"
+#include "psRegion.h"
+#include "psRegionForImage.h"
+
+/** Create a subimage of the specified area.
+ *
+ *  Define a subimage of the specified area of the given image. This function must raise an
+ *  error if the requested subset area lies outside of the parent image and return NULL. The
+ *  argument image is the parent image, region.x0, region.y0 specify the starting pixel of the
+ *  subraster, and region.x1,region.y1 specify the extent of the desired subraster. Note that
+ *  the row and column of this upper right-hand corner NOT included in the region. In the event
+ *  that x1 or y1 are negative, they shall be interpreted as being relative to the size of the
+ *  parent image in that dimension. The entire subraster must be contained within the raster of
+ *  the parent image. Note that the refCounter for the parent should be incremented.  This
+ *  function must be defined for the following types: psU8, psU16, psS8, psS16, psF32, psF64.
+ *
+ *  @return psImage* : Pointer to psImage.
+ *
+ */
+#ifdef DOXYGEN
+psImage* psImageSubset(
+    psImage* image,                    ///< Parent image.
+    psRegion region                    ///< region of subimage
+);
+#else // ifdef DOXYGEN
+psImage* p_psImageSubset(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psImage* image,                    ///< Parent image.
+    psRegion region                    ///< region of subimage
+) PS_ATTR_MALLOC;
+#define psImageSubset(image, region) \
+      p_psImageSubset(__FILE__, __LINE__, __func__, image, region)
+#endif // ifdef DOXYGEN
+
+/** Makes a copy of the image view on the parent:
+ *  if this is a child, returns a child pointing at the same pixels
+ *  if this is a parent, returns a child pointing at the full array
+ */
+psImage* psImageCopyView(psImage *output, psImage *input);
+
+/** Makes a copy of a psImage
+ *
+ * @return psImage* Copy of the input psImage.  This may not be equal to the
+ * output parameter
+ *
+ */
+#ifdef DOXYGEN
+psImage* psImageCopy(
+    psImage* output,                   ///< if not NULL, a psImage that could be recycled.
+    const psImage* input,              ///< the psImage to copy
+    psElemType type                    ///< the desired datatype of the returned copy
+);
+#else // ifdef DOXYGEN
+psImage* p_psImageCopy(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psImage* output,                   ///< if not NULL, a psImage that could be recycled.
+    const psImage* input,              ///< the psImage to copy
+    psElemType type                    ///< the desired datatype of the returned copy
+) PS_ATTR_MALLOC;
+#define psImageCopy(output, input, type) \
+      p_psImageCopy(__FILE__, __LINE__, __func__, output, input, type)
+#endif // ifdef DOXYGEN
+
+/** Trim an image
+ *
+ *  Trim the specified image in-place, which involves shuffling the pixels around in memory.
+ *  The pixels in the region [col0:col1,row0:row1] shall consist the output image.  The column
+ *  col1 and row row1 are NOT included in the range.  In the event that x1 or y1 are
+ *  non-positive, they shall be interpreted as being relative to the size of the parent image
+ *  in that dimension.
+ *
+ *  If the entire specified subimage is not contained within the parent image, an error results
+ *  and the return value will be NULL.
+ *
+ *  N.B. If the input psImage is a child of another psImage, no pixel data will be trimmed,
+ *  rather it equivalent to calling psImageSubset.  If the input psImage is, however, a parent
+ *  psImage, any children will be obliterated, i.e., freed from memory.
+ *
+ *  @return psImage*  trimmed image result
+ */
+psImage* psImageTrim(
+    psImage* image,                    ///< image to trim
+    psRegion region                    ///< trim region
+);
+
+/// @}
+#endif // #ifndef PSIMAGE_STRUCT_MANIP_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageUnbin.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageUnbin.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageUnbin.c	(revision 22322)
@@ -0,0 +1,343 @@
+/** @file  psImageUnbin.c
+ *
+ *  @brief Functions to perform unbinning (resampling) of images.
+ *
+ *  @ingroup Image
+ *
+ *  @author Eugene Magnier, IfA
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-09-20 23:55:50 $
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include "psError.h"
+#include "psAssert.h"
+#include "psRegion.h"
+#include "psImage.h"
+#include "psImageBinning.h"
+#include "psImageUnbin.h"
+
+// interpolate from model to background (~bresenham linear interpolation)
+// DX, DY are the binning factor
+// dx, dy is the distance is high-res pixels to the 0,0 corner of the first
+// binned pixel. 
+// XXX check that this is still consistent with psphotImageMedian...
+psImage *psImageUnbin(psImage *out, const psImage *in, const psImageBinning *binning)
+{
+    PS_ASSERT_IMAGE_NON_NULL(in, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(out, NULL);
+
+    int DX = binning->nXbin;
+    int DY = binning->nYbin;
+    int dx = binning->nXskip;
+    int dy = binning->nYskip;
+
+    PS_ASSERT_INT_POSITIVE(DX, NULL);
+    PS_ASSERT_INT_POSITIVE(DY, NULL);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(dx, DX, NULL);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(dy, DY, NULL);
+
+    long nx = in->numCols;
+    long ny = in->numRows;
+    long Nx = out->numCols;
+    long Ny = out->numRows;
+
+    // XXX validate the binning structure vs the input and output images?
+
+    psF32 **vIn = in->data.F32;
+    psF32 **vOut = out->data.F32;
+
+    // loop over all input pixels excluding the last
+    for (int Iy = 0; Iy < ny-1; Iy ++) {
+        for (int Ix = 0; Ix < nx-1; Ix ++) {
+
+            float V00 = vIn[Iy+0][Ix+0];
+            float V01 = vIn[Iy+0][Ix+1];
+            float V10 = vIn[Iy+1][Ix+0];
+            float V11 = vIn[Iy+1][Ix+1];
+
+            // a single binned pixel quad
+            // (Xs,Ys) : (Xe,Ye) : binned pixel centers in unbinned coords
+            // corresponding to (Ix,Iy), (Ix+1,Iy+1)
+            int Xs = PS_MAX (0, PS_MIN (Nx, (Ix + 0.5)*DX - dx));
+            int Ys = PS_MAX (0, PS_MIN (Ny, (Iy + 0.5)*DY - dy));
+            int Xe = PS_MAX (0, PS_MIN (Nx, Xs + DX));
+            int Ye = PS_MAX (0, PS_MIN (Ny, Ys + DY));
+
+            for (int iy = Ys; (iy < Ye) && (iy < Ny); iy++) {
+                float Vxs = (V10 - V00)*(iy - Ys) / DY + V00;
+                float Vxe = (V11 - V01)*(iy - Ys) / DY + V01;
+                float dV = (Vxe - Vxs) / DX;
+                float V  = Vxs;
+                for (int ix = Xs; (ix < Xe) && (ix < Nx); ix++) {
+                    vOut[iy][ix] = V;
+                    //assert (fabs(V - psImageUnbinPixel(ix, iy, in, DX, DY, dx, dy)) < 1e-5*fabs(V));
+                    V += dV;
+                }
+            }
+        }
+    }
+
+    // side pixels
+    int Xs = PS_MAX (0, PS_MIN (Nx, ( 0 + 0.5)*DX - dx));
+    int Xe = PS_MAX (0, PS_MIN (Nx, (nx - 0.5)*DX - dx));
+    for (int Iy = 0; Iy < ny - 1; Iy++) {
+
+        int Ys = PS_MAX (0, PS_MIN (Ny, (Iy + 0.5)*DY - dy));
+        int Ye = PS_MAX (0, PS_MIN (Ny, Ys + DY));
+
+        // leading edge
+        float V0 = vIn[Iy+0][0];
+        float V1 = vIn[Iy+1][0];
+        float dV = (V1 - V0) / DY;
+        float V = V0;
+        for (int iy = Ys; (iy < Ye) && (iy < Ny); iy++) {
+            for (int ix = 0; ix < Xs; ix++) {
+                vOut[iy][ix] = V;
+            }
+            V += dV;
+        }
+
+        // trailing edge
+        V0 = vIn[Iy+0][nx-1];
+        V1 = vIn[Iy+1][nx-1];
+        dV = (V1 - V0) / DY;
+        V = V0;
+        for (int iy = Ys; (iy < Ye) && (iy < Ny); iy++) {
+            for (int ix = Xe; ix < Nx; ix++) {
+                vOut[iy][ix] = V;
+            }
+            V += dV;
+        }
+    }
+
+    // top and bottom pixels
+    int Ys = PS_MAX (0, PS_MIN (Ny, ( 0 + 0.5)*DY - dy));
+    int Ye = PS_MAX (0, PS_MIN (Ny, (ny - 0.5)*DY - dy));
+    for (int Ix = 0; Ix < nx - 1; Ix++) {
+
+        int Xs = PS_MAX (0, PS_MIN (Nx, (Ix + 0.5)*DX - dx));
+        int Xe = PS_MAX (0, PS_MIN (Nx, Xs + DX));
+
+        // top edge
+        float V0 = vIn[0][Ix+0];
+        float V1 = vIn[0][Ix+1];
+        float dV = (V1 - V0) / DX;
+        for (int iy = 0; iy < Ys; iy++) {
+            float V = V0;
+            for (int ix = Xs; (ix < Xe) && (ix < Nx); ix++) {
+                vOut[iy][ix] = V;
+                V += dV;
+            }
+        }
+
+        // bottom edge
+        V0 = vIn[ny-1][Ix+0];
+        V1 = vIn[ny-1][Ix+1];
+        dV = (V1 - V0) / DX;
+        for (int iy = Ye; iy < Ny; iy++) {
+            float V = V0;
+            for (int ix = Xs; (ix < Xe) && (ix < Nx); ix++) {
+                vOut[iy][ix] = V;
+                V += dV;
+            }
+        }
+    }
+    // return out;
+
+    // the four corners
+    {
+        float V;
+	// center of last pixel 
+	int Xs = PS_MAX (0, PS_MIN (Nx, ( 0 + 0.5)*DX - dx));
+	int Xe = PS_MAX (0, PS_MIN (Nx, (nx - 0.5)*DX - dx));
+	int Ys = PS_MAX (0, PS_MIN (Ny, ( 0 + 0.5)*DY - dy));
+	int Ye = PS_MAX (0, PS_MIN (Ny, (ny - 0.5)*DY - dy));
+
+        // 0,0
+        V = vIn[0][0];
+        for (int iy = 0; iy < Ys; iy++)
+        {
+            for (int ix = 0; ix < Xs; ix++) {
+                vOut[iy][ix] = V;
+            }
+        }
+        // Nx,0
+        V = vIn[0][nx-1];
+        for (int iy = 0; iy < Ys; iy++)
+        {
+            for (int ix = Xe; ix < Nx; ix++) {
+                vOut[iy][ix] = V;
+            }
+        }
+        // 0,Ny
+        V = vIn[ny-1][0];
+        for (int iy = Ye; iy < Ny; iy++)
+        {
+            for (int ix = 0; ix < Xs; ix++) {
+                vOut[iy][ix] = V;
+            }
+        }
+        // Nx,Ny
+        V = vIn[ny-1][nx-1];
+        for (int iy = Ye; iy < Ny; iy++)
+        {
+            for (int ix = Xe; ix < Nx; ix++) {
+                vOut[iy][ix] = V;
+            }
+        }
+    }
+
+    return out;
+}
+
+/************************************************************************************************************/
+/*
+ * Get the value of a single unbinned pixel from the binned representation
+ *
+ * N.b. This code only works for the central part of the image; the edge
+ * cases should be added
+
+ * XXXX this is bilinear interpolation, but written sub-optimally
+ * XXXX this function should be taking float input coordinates!!!
+ */
+double psImageUnbinPixel(const int ix, const int iy, // desired Unbinned point (parent coords)
+                         const psImage *in, // binned image
+                         const psImageBinning *binning)   //!< Overhang
+{
+
+    int DX = binning->nXbin;
+    int DY = binning->nYbin;
+    int dx = binning->nXskip;
+    int dy = binning->nYskip;
+
+    PS_ASSERT_IMAGE_NON_NULL(in, NAN);
+    assert (in->type.type == PS_TYPE_F32);
+
+    PS_ASSERT_INT_POSITIVE(DX, NAN);
+    PS_ASSERT_INT_POSITIVE(DY, NAN);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(dx, DX, NAN);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(dy, DY, NAN);
+    /*
+     * Find which binned pixel we're in
+     */
+
+    const int Xs = (ix - dx)/DX; // index of binned pixel
+    const int Ys = (iy - dy)/DY;
+    if (Xs < 0 || Xs >= in->numCols || Ys < 0 || Ys >= in->numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Point (%d,%d) lies outside binned image", ix, iy);
+        return NAN;
+    }
+    const int Xe = PS_MIN (Xs + 1, in->numCols - 1);
+    const int Ye = PS_MIN (Ys + 1, in->numRows - 1);
+
+    double V00 = in->data.F32[Ys][Xs];
+    double V01 = in->data.F32[Ys][Xe];
+    double V10 = in->data.F32[Ye][Xs];
+    double V11 = in->data.F32[Ye][Xe];
+
+    const int xs = Xs*DX - dx; // centre of bottom left corner of binned pixel
+    const int ys = Ys*DY - dy; // (i.e. [Xs][Ys]) in unbinned coordinates
+
+    const double Vxs = (V10 - V00)*(iy - ys)/DY + V00;
+    const double Vxe = (V11 - V01)*(iy - ys)/DY + V01;
+
+    return Vxs + (Vxe - Vxs)*(ix - xs)/DX; // value at [iy][ix]
+}
+
+double psImageUnbinPixel_V2(const double xFine, const double yFine, // desired Unbinned point (parent coords)
+			    const psImage *in, // binned image
+			    const psImageBinning *binning)   //!< Overhang
+{
+    PS_ASSERT_IMAGE_NON_NULL(in, NAN);
+    assert (in->type.type == PS_TYPE_F32);
+
+    const float xRuff = psImageBinningGetRuffX (binning, xFine);
+    const float yRuff = psImageBinningGetRuffY (binning, yFine);
+
+    const double value = psImageInterpolatePixelBilinear (xRuff, yRuff, in);
+
+    return value;
+}
+
+// fast & simple API to interpolate to a subpixel position using bilinear interpolation
+// x,y in parent image coordinates (pixel centers at 0.5, 0.5)
+double psImageInterpolatePixelBilinear (const double xIn, const double yIn, const psImage *in) {
+
+    PS_ASSERT_PTR_NON_NULL(in, PS_ERR_BAD_PARAMETER_VALUE);
+    assert (in->type.type == PS_TYPE_F32);
+
+    const double x = xIn - in->col0;
+    const double y = yIn - in->row0;
+
+    // allow extrapolation to edge of valid pixels, but not beyond
+    if ((x < 0) || (x >= in->numCols) || (y < 0) || (y >= in->numRows)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Point (%f,%f) lies outside binned image", x, y);
+        return NAN;
+    }
+
+    // limiting cases: Nx == 1 and/or Ny == 1
+
+    // if we have a single pixel, there is no spatial information
+    if ((in->numCols == 1) && (in->numRows == 1)) {
+	const double value = in->data.F32[0][0];
+	return value;
+    }
+
+    // handle edge cases with extrapolation
+
+    const int ix = x - 0.5; // index of reference pixel
+    const int iy = y - 0.5; // index of reference pixel
+
+    // do numCols,Rows first so we are never < 0
+    const int Xs = PS_MAX (PS_MIN (ix, in->numCols - 2), 0);
+    const int Ys = PS_MAX (PS_MIN (iy, in->numRows - 2), 0);
+
+    const int Xe = Xs + 1;
+    const int Ye = Ys + 1;
+
+    // dx,dy range from 0.0 to 1.0 for interpolated pixels, and -0.5 to 1.5 for extrapolation
+    const double dx = x - 0.5 - Xs;
+    const double dy = y - 0.5 - Ys;
+
+    const double rx = 1.0 - dx;
+    const double ry = 1.0 - dy;
+
+    // if Nx == 1, we have no x-dir spatial information
+    if (in->numCols == 1) {
+	double V0 = in->data.F32[Ys][Xs];
+	double V1 = in->data.F32[Ye][Xs];
+
+	const double value = V0*ry + V1*dy;
+	return value;
+    }	
+
+    // if Ny == 1, we have no y-dir spatial information
+    if (in->numRows == 1) {
+	double V0 = in->data.F32[Ys][Xs];
+	double V1 = in->data.F32[Ys][Xe];
+
+	const double value = V0*rx + V1*dx;
+	return value;
+    }	
+
+    // Vxy 
+    double V00 = in->data.F32[Ys][Xs];
+    double V10 = in->data.F32[Ys][Xe];
+    double V01 = in->data.F32[Ye][Xs];
+    double V11 = in->data.F32[Ye][Xe];
+
+    // bilinear interpolation
+    const double value = V00*rx*ry + V10*dx*ry + V01*rx*dy + V11*dx*dy;
+
+    return value;
+}
+
+    
Index: /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageUnbin.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageUnbin.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/imageops/psImageUnbin.h	(revision 22322)
@@ -0,0 +1,38 @@
+/** @file  psImageUnbin.h
+ *
+ *  @brief Functions to unbin images
+ *
+ *  @author EAM, IfA
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  $Date: 2007-09-20 23:55:52 $
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_UNBIN_H
+#define PS_IMAGE_UNBIN_H
+
+/// @addtogroup ImageOps Image Operations
+/// @{
+
+// This needs to be considered more carefully
+psImage *psImageUnbin (psImage *out,    //!< Output image
+                       const psImage *in, //!< Input image
+                       const psImageBinning *binning ///< binning definition
+    );
+
+double psImageUnbinPixel(const int ix, const int iy, //!< desired Unbinned point
+                         const psImage *in, //!< binned image
+			 const psImageBinning *binning ///< binning definition
+                        );
+
+double psImageUnbinPixel_V2(const double xFine, const double yFine, // desired Unbinned point (parent coords)
+			    const psImage *in, // binned image
+			    const psImageBinning *binning   //!< Overhang
+    );
+
+double psImageInterpolatePixelBilinear (const double xIn, const double yIn, const psImage *in);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/Makefile.am	(revision 22322)
@@ -0,0 +1,12 @@
+#Makefile for image jpeg functions of psLib
+#
+noinst_LTLIBRARIES = libpslibjpeg.la
+
+libpslibjpeg_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(JPEG_CFLAGS)
+libpslibjpeg_la_SOURCES = \
+	psImageJpeg.c
+
+pkginclude_HEADERS = \
+	psImageJpeg.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/psImageJpeg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/psImageJpeg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/psImageJpeg.c	(revision 22322)
@@ -0,0 +1,220 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <strings.h>  // for strcasecmp
+#include <string.h>
+
+#include "psMemory.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psError.h"
+#include "psAssert.h"
+#include "psImageJpeg.h"
+
+#ifdef HAVE_STDLIB_H
+// jpeglib.h includes jconfig.h which is full of autoconf generated HAVE_*
+// defines. This is a hack to work around CPP redefinition errors.  Arrrrrgh!!!
+// -JH
+# undef HAVE_STDLIB_H
+# include <jpeglib.h>
+#endif // ifdef HAVE_STDLIB_H
+
+static void imageJpegColormapFree(psImageJpegColormap *map)
+{
+
+    if (!map) {
+        return;
+    }
+
+    psFree(map->red);
+    psFree(map->green);
+    psFree(map->blue);
+    return;
+}
+
+psImageJpegColormap *psImageJpegColormapAlloc ()
+{
+    psImageJpegColormap *map = psAlloc(sizeof(psImageJpegColormap));
+    psMemSetDeallocator(map, (psFreeFunc)imageJpegColormapFree);
+
+    map->red   = psVectorAlloc(256, PS_TYPE_U8);
+    map->blue  = psVectorAlloc(256, PS_TYPE_U8);
+    map->green = psVectorAlloc(256, PS_TYPE_U8);
+
+    return map;
+}
+
+psImageJpegColormap *psImageJpegColormapSet(psImageJpegColormap *map, const char *name)
+{
+
+    if (!map) {
+        map = psImageJpegColormapAlloc ();
+    }
+
+    /* grayscale */
+    if ((!strcasecmp (name, "grayscale")) || (!strcasecmp (name, "greyscale"))) {
+        for (int i = 0; i < map->red->n; i++) {
+            map->red->data.U8[i]   = PS_JPEG_RANGELIM(i);
+            map->green->data.U8[i] = PS_JPEG_RANGELIM(i);
+            map->blue->data.U8[i]  = PS_JPEG_RANGELIM(i);
+        }
+        return map;
+    }
+
+    /* -grayscale */
+    if ((!strcasecmp (name, "-grayscale")) || (!strcasecmp (name, "-greyscale"))) {
+        for (int i = 0; i < map->red->n; i++) {
+            map->red->data.U8[i]   = PS_JPEG_RANGELIM(256 - i);
+            map->green->data.U8[i] = PS_JPEG_RANGELIM(256 - i);
+            map->blue->data.U8[i]  = PS_JPEG_RANGELIM(256 - i);
+        }
+        return map;
+    }
+
+    /* rainbow */
+    if (!strcasecmp (name, "rainbow")) {
+        int I1 = 0.25*map->red->n;
+        int I2 = 0.50*map->red->n;
+        int I3 = 0.75*map->red->n;
+        for (int i = 0; i < I1; i++) {
+            map->red->data.U8[i]   = 0;
+            map->green->data.U8[i] = 0;
+            map->blue->data.U8[i]  = PS_JPEG_RANGELIM(4*i);
+        }
+        for (int i = I1; i < I2; i++) {
+            map->red->data.U8[i]   = PS_JPEG_RANGELIM(4*(i - I1));
+            map->green->data.U8[i] = 0;
+            map->blue->data.U8[i]  = PS_JPEG_RANGELIM(4*(I2 - i));
+        }
+        for (int i = I2; i < I3; i++) {
+            map->red->data.U8[i]   = 255;
+            map->green->data.U8[i] = 4*(i - I2);
+            map->blue->data.U8[i]  = 0;
+        }
+        for (int i = I3; i < map->red->n; i++) {
+            map->red->data.U8[i]   = 255;
+            map->green->data.U8[i] = 255;
+            map->blue->data.U8[i]  = PS_JPEG_RANGELIM(4*(i - I3));
+        }
+        return map;
+    }
+
+    /* heat */
+    if (!strcasecmp (name, "heat")) {
+        int I1 = 0.25*map->red->n;
+        int I2 = 0.50*map->red->n;
+        int I3 = 0.75*map->red->n;
+        for (int i = 0; i < I1; i++) {
+            map->red->data.U8[i]   = PS_JPEG_RANGELIM(2*i);
+            map->green->data.U8[i] = 0;
+            map->blue->data.U8[i]  = 0;
+        }
+        for (int i = I1; i < I2; i++) {
+            map->red->data.U8[i]   = PS_JPEG_RANGELIM(2*i);
+            map->green->data.U8[i] = PS_JPEG_RANGELIM(2*(i - I1));
+            map->blue->data.U8[i]  = 0;
+        }
+        for (int i = I2; i < I3; i++) {
+            map->red->data.U8[i]   = 255;
+            map->green->data.U8[i] = PS_JPEG_RANGELIM(2*(i - I1));
+            map->blue->data.U8[i]  = PS_JPEG_RANGELIM(2*(i - I2));
+        }
+        for (int i = I3; i < map->red->n; i++) {
+            map->red->data.U8[i]   = 255;
+            map->green->data.U8[i] = 255;
+            map->blue->data.U8[i]  = PS_JPEG_RANGELIM(2*(i - I2));
+        }
+        return map;
+    }
+
+    // invalid colormap: warn user
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Invalid colormap name: %s --- using greyscale\n", name);
+    return psImageJpegColormapSet (map, "greyscale");
+}
+
+bool psImageJpeg(const psImageJpegColormap *map, const psImage *image, const char *filename,
+                 float min, float max)
+{
+    PS_ASSERT_PTR_NON_NULL(map, false);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_VECTOR_NON_NULL(map->red, false);
+    PS_ASSERT_VECTOR_NON_NULL(map->green, false);
+    PS_ASSERT_VECTOR_NON_NULL(map->blue, false);
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+    PS_ASSERT_INT_POSITIVE(strlen(filename), false);
+    PS_ASSERT_FLOAT_REAL(min, false);
+    PS_ASSERT_FLOAT_REAL(max, false);
+
+    float zero, scale;
+    struct jpeg_compress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+
+    long pixel;
+    JSAMPLE *jpegLine;   // Points to data for current line
+    JSAMPROW jpegLineList[1];  // pointer to JSAMPLE row[s]
+    JSAMPLE *outPix;
+
+    /* JPEG init calls */
+    cinfo.err = jpeg_std_error (&jerr);
+    jpeg_create_compress (&cinfo);
+
+    /* open file, prep for jpeg */
+    FILE *f = fopen(filename, "w");
+    if (!f) {
+        psError(PS_ERR_IO, true, "failed to open %s for output\n", filename);
+        return false;
+    }
+    jpeg_stdio_dest(&cinfo, f);
+
+    /* set up color jpeg buffers */
+    int quality = 75;
+    cinfo.image_width = image->numCols; // image width and height, in pixels
+    cinfo.image_height = image->numRows;
+    cinfo.input_components = 3;
+    cinfo.in_color_space = JCS_RGB;
+    jpeg_set_defaults (&cinfo);
+    jpeg_set_quality (&cinfo, quality, true); // limit to baseline-JPEG values
+    jpeg_start_compress (&cinfo, true);
+
+    jpegLine = psAlloc (3*image->numCols*sizeof(JSAMPLE));
+    jpegLineList[0] = jpegLine;
+
+    psU8 *Rpix = map->red->data.U8;
+    psU8 *Gpix = map->green->data.U8;
+    psU8 *Bpix = map->blue->data.U8;
+
+    if (max == min) {
+        zero = min - 0.1;
+        scale = 256.0/0.2;
+    } else {
+        zero = min;
+        scale = 256.0/(max - min);
+    }
+
+    for (int j = 0; j < image->numRows; j++) {
+        psF32 *row = image->data.F32[j];
+
+        outPix = jpegLine;
+        for (int i = 0; i < image->numCols; i++, outPix += 3) {
+            if (isfinite(row[i])) {
+                pixel = PS_JPEG_SCALEVALUE(row[i],zero,scale);
+            } else {
+                pixel = 0;
+            }
+            outPix[0] = Rpix[pixel];
+            outPix[1] = Gpix[pixel];
+            outPix[2] = Bpix[pixel];
+        }
+        jpeg_write_scanlines(&cinfo, jpegLineList, 1);
+    }
+
+    jpeg_finish_compress(&cinfo);
+    fclose(f);
+    jpeg_destroy_compress(&cinfo);
+
+    psFree(jpegLine);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/psImageJpeg.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/psImageJpeg.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/jpeg/psImageJpeg.h	(revision 22322)
@@ -0,0 +1,46 @@
+/* @file  psImageJpeg.h
+ * @brief functions to generate JPEG images from psImage
+ * 
+ * @author EAM
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-08-09 01:40:07 $
+ */
+
+#ifndef PS_IMAGE_JPEG_H
+#define PS_IMAGE_JPEG_H
+
+/// @addtogroup FileIO Input/Output
+/// @{
+
+#include "psImage.h"
+
+typedef struct
+{
+    psVector *red;                      // Red colormap
+    psVector *green;                    // Green colormap
+    psVector *blue;                     // Blue colormap
+}
+psImageJpegColormap;
+
+#define PS_JPEG_RANGELIM(A)(PS_MAX(0,PS_MIN(255,(A))))
+
+#define PS_JPEG_SCALEVALUE(VALUE,ZERO,SCALE)(PS_MAX(0,PS_MIN(255,(SCALE*(VALUE-ZERO)))))
+
+// allocate a colormap (does not define the map values)
+psImageJpegColormap *psImageJpegColormapAlloc() PS_ATTR_MALLOC;
+
+// set the colormap values using the supplied name
+psImageJpegColormap *psImageJpegColormapSet(psImageJpegColormap *map, // Colormap to set
+        const char *name // Name of colormap
+                                           );
+
+// write out a JPEG file using the supplied image and colormap
+// output goes to the specified filename
+bool psImageJpeg(const psImageJpegColormap *map, // Color map
+                 const psImage *image,  // Image to write
+                 const char *filename,  // Filename of JPEG
+                 float min, float max   // Minimum and maximum values
+                );
+
+/// @}
+#endif /* PS_IMAGE_JPEG_H */
Index: /tags/sj_tags/sj_root_20080929/psLib/src/mainpage.dox
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/mainpage.dox	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/mainpage.dox	(revision 22322)
@@ -0,0 +1,197 @@
+/** @file mainpage.dox
+ *  @brief Main page for the Doxygen documentation
+ *
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-12-06 18:46:08 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/** @mainpage psLib Image Processing Library
+
+@section intro Introduction
+
+This library contains the Pan-STARRS Image Processing Pipeline (IPP) common library,
+psLib. The intended use is to provide a set of basic software functions in which
+can be used throughout the Pan-STARRS project to simplify programming tasks. Among
+the benefits are:
+ - Reuse of code
+ - Simplification of testing
+ - Streamlining of code
+ - Isolation of functionality that may be subject to change
+
+The capabilities provided by psLib are grouped into the following areas which are
+also reflected in the file system directory structure:
+ - System Utilities
+ - Data Collections
+ - Image Operations
+ - Data Manipulation
+ - Astronomy
+
+The capabilities provided by psModuke are grouped into the following areas which are
+also reflected in the file system directory structure:
+ - Configuration
+ - Astrometry, Focal Plane
+ - Photometry
+ - Basic Image Detrending
+ - Object Detection and Classification
+ - Image Combination
+ - Image Subtraction
+
+This list is sorted in hierarchical order: The later entries depend on the functions and
+data types of the earlier entries.
+
+The installed code for psLib consists of header files and binary libraries in
+the form of static and/or shared library, depending on the configuration options.
+To assist in using the library, a script "pslib-config" is supplied that
+provides the compiler and linker options.
+
+
+@section extinstall 3rd Party Libraries
+
+Before building psLib from source, several 3rd party software libraries must
+be installed.  These include:
+ - GNU Scientific Library (GSL)
+     - Available at http://www.gnu.org/software/gsl
+     - Compatibility tested with version 1.4
+ - Fastest Fourier Transform in the West (FFTW), version 3, with single-precison float support
+     - Available at http://www.fftw.org
+     - Compatibility tested with version is 3.0.1
+     - The '--enable-float' option is required in the configure step to enable single-precision float support.  Use 'configure --help' for more information of the configuration options for this library.
+ - C Flexible Image Transport System Input output (CFITSIO), version 2.480 or later
+     - Available at http://heasarc.gsfc.nasa.gov/docs/software/fitsio
+     - Compatibility tested with version 2.490, requires at least 2.480
+ - Gnome XML C parser and toolkit (libxml2)
+     - Available at http://www.xmlsoft.org
+     - Compatibility tested with version 2.6.12 and 2.6.16
+
+Though not required to build the base library, several 3rd party software
+libraries are needed for particular functionality.
+ - Doxygen Documentation System
+     - Required for creation of documentation
+     - Available at http://www.doxygen.org
+     - Compatibility tested with version 1.3.2 and 1.3.8
+     - The optional companion package, GraphViz (a.k.a., the 'dot' command), is recommended to enable header-file dependency diagrams
+ - MySQL
+     - Required for database functionality.  If not desired, the '--disable-mysql' configure option should be used.
+     - Available at http://dev.mysql.com
+     - Compatibility tested with version 4.1.10a, requires at least 4.1.2
+     - A server running locally is required for tests.
+ - Autoconf/Automake
+     - Required only if making from CVS
+     - Available at http://www.gnu.org/software/autoconf and http://www.gnu.org/software/automake
+     - Compatibility tested with version 2.5.9 (autoconf) and 1.9.2 (automake)
+
+We recommend using the particular versions listed as compatibility tested, as
+that is the only versions tested to work well with psLib. Though it is quite 
+possible that later versions of the libraries listed will also work as well, 
+care must be taken when upgrading these libraries to verify that its
+functionality is compatible with the tested version.
+
+
+@section obtain How to Obtain the Source
+
+Tested versions of psLib are put into a tar file and can be downloaded from:
+
+https://mhpcc.pan-starrs.org/code/releases
+
+If one has a login account on mhpcc.pan-starrs.org, direct CVS access is also
+possible.  Example of the commands required for direct CVS retrieval are
+as follows:
+<pre>
+$ cvs -d:ext:USERNAME@mhpcc.pan-starrs.org:/data/panstarrs/cvsroot co -r RELEASEBRANCH psLib
+</pre>
+where:
+  - USERNAME is your login name on the server
+  - RELEASEBRANCH is the desired release branch, e.g. rel7.
+
+
+@section build How to Build and Test
+
+The source uses autoconf/automake to build control the build process.  If, 
+and only if, the source came from CVS, the configuration script will have
+to be made first via:
+<pre>
+$ cd psLib
+$ ./autogen.sh
+</pre>
+<i>This creates the required configure script and Makefile templates.</i>
+
+The recommended steps to build the library are:
+<pre>
+$ cd psLib
+$ ./configure
+$ make
+$ make check
+$ make install
+</pre>
+<i>This builds, tests, and installs the library.</i>
+
+The configure step allows users to specify specific locations for the third
+party libraries (see "3rd Party Libraries" for a list), disabling of select
+functionality (e.g., database and perl module), installation location, etc.
+An enumeration of the options for configure, and the default values, can be
+obtained via "./configure --help".
+
+Though 'make check' compiles and runs the test suite, one can also use custom
+scripts to run all the tests and just a particular test.  To run the unit test
+suite, do the following:
+<pre>
+$ cd psLib/test
+$ ./FullUnitTest
+</pre>
+
+This has the advantage over 'make check' in that it does not stop when a test
+fails and it collects some very basic statistics on the results.  To run a
+particular test, do the following:
+<pre>
+$ runTest testfilename
+</pre>
+
+@section install How to Install
+
+To install the library using the prefix given in the configure step, execute in
+the top build directory:
+<pre>
+$ make install
+</pre>
+
+
+@section usage Building and Linking your code to the psLib library
+
+To assist the use of the library with your own code, a configuration tool is part
+of the psLib library package.  This tool, pslib-config, is installed in the BIN
+directory, according to the options given to the configure script.
+
+The required CFLAG options for the compiler stage of code that uses psLib can be
+obtained via 'pslib-config --cflags'.  This outputs the cc options that supplies
+include path(s) required to find the psLib headers.
+
+The required linking options, can be obtained via 'pslib-config --libs'.  This
+outputs the ld options that supplies the library paths and files required to
+link to the pslib library.
+
+Note: pslib-config usage above refers to the install locations of the library.  
+To link to the library directly in the build area, you need to use
+'pslib-config --build-cflags' and 'pslib-config --build-libs' instead.  These
+options will modify the output to point to the non-installed (build) locations
+of the include files and libraries. (this feature is experimental)
+
+@section doc How to Create Code Documentation
+
+Both HTML and man page documentation may be generated from the inline
+documentation embedded in the code using the following commands:
+<pre>
+$ cd psLib
+$ make docs
+</pre>
+<i>This places documentation in PREFIX/docs/psLib, where PREFIX is set using
+the configure script.</i>
+
+Also, a prebuilt set of code documentation for releases and can be found at:
+
+https://mhpcc.pan-starrs.org/docs/
+
+*/
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/Makefile.am	(revision 22322)
@@ -0,0 +1,64 @@
+#Makefile for math functions of psLib
+#
+noinst_LTLIBRARIES = libpslibmath.la
+
+libpslibmath_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(GSL_CFLAGS)
+libpslibmath_la_SOURCES = \
+	psUnaryOp.c \
+	psBinaryOp.c \
+	psClip.c \
+	psCompare.c \
+	psEllipse.c \
+	psMathUtils.c \
+	psMatrix.c \
+	psMD5.c \
+	psMinimizeLMM.c \
+	psMinimizePowell.c \
+	psMinimizePolyFit.c \
+	psPolynomial.c \
+	psPolynomialMetadata.c \
+	psPolynomialUtils.c \
+	psPolynomialMD.c \
+	psRandom.c \
+	psRegion.c \
+	psRegionForImage.c \
+	psSparse.c \
+	psSpline.c \
+	psStats.c \
+	psHistogram.c \
+	psVectorSmooth.c \
+	md5.c
+
+EXTRA_DIST = math.i
+
+pkginclude_HEADERS = \
+	psUnaryOp.h \
+	psBinaryOp.h \
+	psClip.h \
+	psCompare.h \
+	psConstants.h \
+	psEllipse.h \
+	psMathUtils.h \
+	psMatrix.h \
+	psMD5.h \
+	psMinimizeLMM.h \
+	psMinimizePowell.h \
+	psMinimizePolyFit.h \
+	psPolynomial.h \
+	psPolynomialMetadata.h \
+	psPolynomialUtils.h \
+	psPolynomialMD.h \
+	psRandom.h \
+	psRegion.h \
+	psRegionForImage.h \
+	psSort.h \
+	psSparse.h \
+	psSpline.h \
+	psStats.h \
+	psHistogram.h \
+	psVectorSmooth.h
+
+noinst_HEADERS = \
+	md5.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/math.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/math.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/math.i	(revision 22322)
@@ -0,0 +1,12 @@
+/* math headers */
+
+%include "psBinaryOp.h"
+%include "psCompare.h"
+%include "psConstants.h"
+%include "psMatrix.h"
+%include "psMinimize.h"
+%include "psPolynomial.h"
+%include "psSpline.h"
+%include "psRandom.h"
+%include "psStats.h"
+%include "psUnaryOp.h"
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/md5.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/md5.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/md5.c	(revision 22322)
@@ -0,0 +1,386 @@
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.1 2007-03-20 01:13:54 price Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+        http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2007-03-19 pap Added support for autoconf; incorporating into psLib.
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+        either statically or dynamically; added missing #include <string.h>
+        in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+        type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+        unsigned in ANSI C, signed in traditional"; made test program
+        self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER       /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef WORDS_BIGENDIAN                  // Comes from autoconf
+#  define BYTE_ORDER (WORDS_BIGENDIAN ? 1 : -1)
+#else
+#  define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3    0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6    0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9    0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13    0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16    0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19    0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22    0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25    0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28    0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31    0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35    0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38    0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41    0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44    0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47    0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50    0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53    0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57    0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60    0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63    0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+    md5_word_t
+        a = pms->abcd[0], b = pms->abcd[1],
+        c = pms->abcd[2], d = pms->abcd[3];
+    md5_word_t t;
+#if BYTE_ORDER > 0
+    /* Define storage only for big-endian CPUs. */
+    md5_word_t X[16];
+#else
+    /* Define storage for little-endian or both types of CPUs. */
+    md5_word_t xbuf[16];
+    const md5_word_t *X;
+#endif
+
+    {
+#if BYTE_ORDER == 0
+        /*
+         * Determine dynamically whether this is a big-endian or
+         * little-endian machine, since we can use a more efficient
+         * algorithm on the latter.
+         */
+        static const int w = 1;
+
+        if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0             /* little-endian */
+        {
+            /*
+             * On little-endian machines, we can process properly aligned
+             * data without copying it.
+             */
+            if (!((data - (const md5_byte_t *)0) & 3)) {
+                /* data are properly aligned */
+                X = (const md5_word_t *)data;
+            } else {
+                /* not aligned */
+                memcpy(xbuf, data, 64);
+                X = xbuf;
+            }
+        }
+#endif
+#if BYTE_ORDER == 0
+        else                    /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0             /* big-endian */
+        {
+            /*
+             * On big-endian machines, we must arrange the bytes in the
+             * right order.
+             */
+            const md5_byte_t *xp = data;
+            int i;
+
+#  if BYTE_ORDER == 0
+            X = xbuf;           /* (dynamic only) */
+#  else
+#    define xbuf X              /* (static only) */
+#  endif
+            for (i = 0; i < 16; ++i, xp += 4)
+                xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+        }
+#endif
+    }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+        return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+        pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+        int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+        memcpy(pms->buf + offset, p, copy);
+        if (offset + copy < 64)
+            return;
+        p += copy;
+        left -= copy;
+        md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+        md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+        memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+        data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+        digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/md5.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/md5.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/md5.h	(revision 22322)
@@ -0,0 +1,94 @@
+/*
+  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.1 2007-03-20 01:13:54 price Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+        http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2007-03-19 pap Fixing types for wider acceptance; incorporating into psLib.
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+        references to Ghostscript; clarified derivation from RFC 1321;
+        now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+        added conditionalization for C++ compilation from Martin
+        Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+#include <inttypes.h>
+
+typedef uint8_t md5_byte_t; /* 8-bit byte */
+typedef uint32_t md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];        /* message length in bits, lsw first */
+    md5_word_t abcd[4];         /* digest buffer */
+    md5_byte_t buf[64];         /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psBinaryOp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psBinaryOp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psBinaryOp.c	(revision 22322)
@@ -0,0 +1,538 @@
+/** @file  psBinaryOp.c
+ *
+ *  @brief Provides binary functions for simple matrix and vector element operations. Functions
+ *  include:
+ *
+ *      Addition (+)
+ *      Subtraction (-)
+ *      Multiplication (*)
+ *      Division (/)
+ *      Power (^)
+ *      Minimum (min)
+ *      Maximum (max)
+ *      Absolute value (abs)
+ *      Exponent (exp)
+ *      Natural Log (ln)
+ *      Power of 10 (ten)
+ *      Log (log)
+ *      Sine (sin or dsin)
+ *      Cosine (cos or dcos)
+ *      Tangent (tan or dtan)
+ *      Arcsine (asin or dasin)
+ *      Arccosine (acos or dacos)
+ *      Arctan (atan or datan)
+ *
+ *  Currently only vector-vector and image-image binary operations are supported.
+ *
+ *  @ingroup MatrixArithmetic
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-03-19 00:52:35 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/******************************************************************************
+ *  INCLUDE FILES                                                             *
+ ******************************************************************************/
+#include <string.h>
+#include <strings.h>
+#include <math.h>
+#include <stdint.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psLogMsg.h"
+#include "psAssert.h"
+
+
+/*****************************************************************************
+ *  FUNCTION IMPLEMENTATION - LOCAL                                          *
+ *****************************************************************************/
+
+// Binary SCALAR_XXXX operations
+#define SCALAR_SCALAR(OUT,IN1,OP,IN2,TYPE) \
+{ \
+    ps##TYPE *o  = &((psScalar*)OUT)->data.TYPE; \
+    ps##TYPE *i1 = &((psScalar*)IN1)->data.TYPE; \
+    ps##TYPE *i2 = &((psScalar*)IN2)->data.TYPE; \
+    *o = OP; \
+}
+
+#define SCALAR_VECTOR(OUT,IN1,OP,IN2,TYPE) \
+{ \
+    long npt = ((psVector*)IN2)->n; \
+    ps##TYPE *o  = ((psVector*)OUT)->data.TYPE; \
+    ps##TYPE *i1 = &((psScalar*)IN1)->data.TYPE; \
+    ps##TYPE *i2 = ((psVector*)IN2)->data.TYPE; \
+    for (long i = 0; i < npt; i++, o++, i2++) { \
+        *o = OP; \
+    } \
+}
+
+#define SCALAR_IMAGE(OUT,IN1,OP,IN2,TYPE) \
+{ \
+    long numRows = ((psImage*)IN2)->numRows; \
+    long numCols = ((psImage*)IN2)->numCols; \
+    for (long j = 0; j < numCols; j++) { \
+        ps##TYPE *o  = ((psImage*)OUT)->data.TYPE[j]; \
+        ps##TYPE *i1 = &((psScalar*)IN1)->data.TYPE; \
+        ps##TYPE *i2 = ((psImage*)IN2)->data.TYPE[j]; \
+        for (long i = 0; i < numRows; i++, o++, i2++) { \
+            *o = OP; \
+        } \
+    } \
+}
+
+// Binary VECTOR_XXXX operations
+#define VECTOR_SCALAR(OUT,IN1,OP,IN2,TYPE) \
+{ \
+    long n1  = ((psVector*)IN1)->n; \
+    ps##TYPE *o  = ((psVector*)OUT)->data.TYPE; \
+    ps##TYPE *i1 = ((psVector*)IN1)->data.TYPE; \
+    ps##TYPE *i2 = &((psScalar*)IN2)->data.TYPE; \
+    for (long i = 0; i < n1; i++, o++, i1++) { \
+        *o = OP; \
+    } \
+}
+
+#define VECTOR_VECTOR(OUT,IN1,OP,IN2,TYPE) \
+{ \
+    long n1 = ((psVector*)IN1)->n; \
+    long n2 = ((psVector*)IN2)->n; \
+    if (n1 != n2) { \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Number of elements inconsistent, %ld vs %ld.  Number of elements must match."), n1, n2); \
+        if (OUT != IN1 && OUT != IN2) { \
+            psFree(OUT); \
+        } \
+        return NULL; \
+    } \
+    ps##TYPE *o  = ((psVector*)OUT)->data.TYPE; \
+    ps##TYPE *i1 = ((psVector*)IN1)->data.TYPE; \
+    ps##TYPE *i2 = ((psVector*)IN2)->data.TYPE; \
+    for (long i = 0; i < n1; i++, o++, i1++, i2++) { \
+        *o = OP; \
+    } \
+}
+
+#define VECTOR_IMAGE(OUT,IN1,OP,IN2,TYPE) \
+{ \
+    long n1 = ((psVector*)IN1)->n; \
+    long numRows2 = ((psImage*)IN2)->numRows; \
+    long numCols2 = ((psImage*)IN2)->numCols; \
+    \
+    if (((psVector*)IN1)->type.dimen == PS_DIMEN_VECTOR) { /* Regular vectors */ \
+        if (n1 != numRows2) { \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Number of elements inconsistent, %ld vs %ld.  Number of elements must match."), n1, numRows2); \
+            if (OUT != IN1 && OUT != IN2) { \
+                psFree(OUT); \
+            } \
+            return NULL; \
+        } \
+        \
+        ps##TYPE *i1 = ((psVector*)IN1)->data.TYPE; \
+        for (long j = 0; j < numRows2; j++, i1++) { \
+            ps##TYPE *o  = ((psImage*)OUT)->data.TYPE[j]; \
+            ps##TYPE *i2 = ((psImage*)IN2)->data.TYPE[j]; \
+            for (long i = 0; i < numCols2; i++, o++, i2++) { \
+                *o = OP; \
+            } \
+        } \
+    } else {  /* Transposed vectors */ \
+        if (n1 != numCols2) { \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Number of elements inconsistent, %ld vs %ld.  Number of elements must match."), n1, numCols2); \
+            if (OUT != IN1 && OUT != IN2) { \
+                psFree(OUT); \
+            } \
+            return NULL; \
+        } \
+        \
+        for (long j = 0; j < numRows2; j++) { \
+            ps##TYPE *o  = ((psImage*)OUT)->data.TYPE[j]; \
+            ps##TYPE *i1 = ((psVector*)IN1)->data.TYPE; \
+            ps##TYPE *i2 = ((psImage*)IN2)->data.TYPE[j]; \
+            for (long i = 0; i < numCols2; i++, o++, i1++, i2++) { \
+                *o = OP; \
+            } \
+        } \
+    } \
+}
+
+// Binary IMAGE_XXXX operations
+#define IMAGE_SCALAR(OUT,IN1,OP,IN2,TYPE) \
+{ \
+    long numRows = ((psImage*)IN1)->numRows; \
+    long numCols = ((psImage*)IN1)->numCols; \
+    for (long j = 0; j < numRows; j++) { \
+        ps##TYPE *o  = ((psImage*)OUT)->data.TYPE[j]; \
+        ps##TYPE *i1 = ((psImage*)IN1)->data.TYPE[j]; \
+        ps##TYPE *i2 = &((psScalar*)IN2)->data.TYPE; \
+        for (long i = 0; i < numCols; i++, o++, i1++) { \
+            *o = OP; \
+        } \
+    } \
+}
+
+#define IMAGE_VECTOR(OUT,IN1,OP,IN2,TYPE) \
+{ \
+    long n2 = ((psVector*)IN2)->n; \
+    long numRows1 = ((psImage*)IN1)->numRows; \
+    long numCols1 = ((psImage*)IN1)->numCols; \
+    \
+    if (((psVector*)IN2)->type.dimen == PS_DIMEN_VECTOR) { /* Regular vectors */ \
+        if (n2 != numRows1) { \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Number of elements inconsistent, %ld vs %ld.  Number of elements must match."), n2, numRows1); \
+            if (OUT != IN1 && OUT != IN2) { \
+                psFree(OUT); \
+            } \
+            return NULL; \
+        } \
+        \
+        ps##TYPE *i2 = ((psVector* )IN2)->data.TYPE; \
+        for (long j = 0; j < numRows1; j++, i2++) { \
+            ps##TYPE *o  = ((psImage*)OUT)->data.TYPE[j]; \
+            ps##TYPE *i1 = ((psImage*)IN1)->data.TYPE[j]; \
+            for (long i = 0; i < numCols1; i++, o++, i1++) { \
+                *o = OP; \
+            } \
+        } \
+    } else {  /* Transposed vectors */ \
+        if (n2 != numCols1) { \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Number of elements inconsistent, %ld vs %ld.  Number of elements must match."), n2, numCols1); \
+            if (OUT != IN1) { \
+                psFree(OUT); \
+            } \
+            return NULL; \
+        } \
+        \
+        for (long j = 0; j < numRows1; j++) { \
+            ps##TYPE *o  = ((psImage*)OUT)->data.TYPE[j]; \
+            ps##TYPE *i1 = ((psVector*)IN2)->data.TYPE; \
+            ps##TYPE *i2 = ((psImage*)IN1)->data.TYPE[j]; \
+            for (long i = 0; i < numCols1; i++, o++, i2++, i1++) { \
+                *o = OP; \
+            } \
+        } \
+    } \
+}
+
+#define IMAGE_IMAGE(OUT,IN1,OP,IN2,TYPE) \
+{ \
+    long numRows1 = ((psImage*)IN1)->numRows; \
+    long numCols1 = ((psImage*)IN1)->numCols; \
+    long numRows2 = ((psImage*)IN2)->numRows; \
+    long numCols2 = ((psImage*)IN2)->numCols; \
+    if (numRows1 != numRows2 || numCols1 != numCols2) { \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Specified psImage dimensions differed, %ldx%ld vs %ldx%ld."), \
+                numCols1, numRows1, numCols2, numRows2); \
+        if (OUT != IN1 && OUT != IN2) { \
+            psFree(OUT); \
+        } \
+        return NULL; \
+    } \
+    for (long j = 0; j < numRows1; j++) { \
+        ps##TYPE *o  = ((psImage*)OUT)->data.TYPE[j]; \
+        ps##TYPE *i1 = ((psImage*)IN1)->data.TYPE[j]; \
+        ps##TYPE *i2 = ((psImage*)IN2)->data.TYPE[j]; \
+        for (long i = 0; i < numCols1; i++, o++, i1++, i2++) { \
+            *o = OP; \
+        } \
+    } \
+}
+
+
+// Preprocessor macro function to create arithmetic function based on input type --- for integers only
+#define BINARY_TYPE_INTEGER(DIM1,DIM2,OUT,IN1,OP,IN2)                                                        \
+switch (IN1->type) {                                                                                         \
+case PS_TYPE_U8:                                                                                             \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U8);                                                                        \
+    break;                                                                                                   \
+case PS_TYPE_U16:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U16);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_U32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_U64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U64);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S8:                                                                                             \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S8);                                                                        \
+    break;                                                                                                   \
+case PS_TYPE_S16:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S16);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S64);                                                                       \
+    break;                                                                                                   \
+default:                                                                                                     \
+    /* char* strType;                                                                                        \
+    PS_TYPE_NAME(strType,IN1->type);                                                                         \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                                 \
+            _("Specified data type, %s, is not supported."),                                                             \
+            strType);  */                                                                                    \
+    if (OUT != IN1 && OUT != IN2) {                                                                          \
+        psFree(OUT);                                                                                         \
+    }                                                                                                        \
+    return NULL;                                                                                             \
+}
+
+// Preprocessor macro function to create arithmetic function based on input type
+#define BINARY_TYPE(DIM1,DIM2,OUT,IN1,OP,IN2)                                                                \
+switch (IN1->type) {                                                                                         \
+case PS_TYPE_U8:                                                                                             \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U8);                                                                        \
+    break;                                                                                                   \
+case PS_TYPE_U16:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U16);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_U32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_U64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U64);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S8:                                                                                             \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S8);                                                                        \
+    break;                                                                                                   \
+case PS_TYPE_S16:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S16);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S64);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_F32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,F32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_F64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,F64);                                                                       \
+    break;                                                                                                   \
+default: {                                                                                                   \
+        char* strType;                                                                                       \
+        PS_TYPE_NAME(strType,IN1->type);                                                                     \
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                             \
+                _("Specified data type, %s, is not supported."),                                             \
+                strType);                                                                                    \
+        if (OUT != IN1 && OUT != IN2) {                                                                      \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+}
+
+// Preprocessor macro function to create arithmetic function operation name
+#define BINARY_OP(DIM1,DIM2,OUT,IN1,OP,IN2)                                                                  \
+if (!strncmp(OP, "+", 1)) {                                                                                  \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 + *i2,IN2);                                                            \
+} else if (!strncmp(OP, "-", 1)) {                                                                           \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 - *i2,IN2);                                                            \
+} else if (!strncmp(OP, "*", 1)) {                                                                           \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 * *i2,IN2);                                                            \
+} else if (!strncmp(OP, "/", 1)) {                                                                           \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 / *i2,IN2);                                                            \
+} else if (!strncmp(OP, "&", 1)) {                                                                           \
+    if (PS_IS_PSELEMTYPE_INT(IN1->type) && PS_IS_PSELEMTYPE_INT(IN2->type)) {                                \
+        BINARY_TYPE_INTEGER(DIM1,DIM2,OUT,IN1,(*i1) & (*i2),IN2);                                            \
+    } else {                                                                                                 \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                            \
+                "Types (%x,%x) are not appropriate for logical AND.\n", IN1->type, IN2->type);               \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+} else if (!strncmp(OP, "|", 1)) {                                                                           \
+    if (PS_IS_PSELEMTYPE_INT(IN1->type) && PS_IS_PSELEMTYPE_INT(IN2->type)) {                                \
+        BINARY_TYPE_INTEGER(DIM1,DIM2,OUT,IN1,(*i1) | (*i2),IN2);                                            \
+    } else {                                                                                                 \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                            \
+                "Types (%x,%x) are not appropriate for logical OR.\n", IN1->type, IN2->type);                \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+} else if (!strncmp(OP, "^", 1)) {                                                                           \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,pow(*i1,*i2),IN2);                                                         \
+} else if (!strncasecmp(OP, "min", 3)) {                                                                     \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,fmin(*i1,*i2),IN2);                                                        \
+} else if (!strncasecmp(OP, "max", 3)) {                                                                     \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,fmax(*i1,*i2),IN2);                                                        \
+} else {                                                                                                     \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                                \
+            _("Specified operation, %s, is not supported."),                                                 \
+            OP);                                                                                             \
+    if (OUT != IN1 && OUT != IN2) {                                                                          \
+        psFree(OUT);                                                                                         \
+    }                                                                                                        \
+    return NULL;                                                                                             \
+}
+
+psMathType* psBinaryOp(psPtr out, const psPtr in1, const char *op, const psPtr in2)
+{
+
+    psVector* input1 = (psVector* ) in1;
+    psVector* input2 = (psVector* ) in2;
+
+    #define psBinaryOp_EXIT { \
+                              if (out != in1 && out != in2) { \
+                              psFree(out); \
+                              } \
+                              return NULL; \
+                            }
+
+    PS_ASSERT_GENERAL_PTR_NON_NULL(input1, psBinaryOp_EXIT);
+    PS_ASSERT_GENERAL_PTR_NON_NULL(input2, psBinaryOp_EXIT);
+    PS_ASSERT_GENERAL_PTR_NON_NULL(op, psBinaryOp_EXIT);
+
+    PS_ASSERT_GENERAL_PTR_TYPE_EQUAL(input1,input2, psBinaryOp_EXIT);
+
+    PS_ASSERT_PTR_DIMEN_GENERAL_NOT(input1, PS_DIMEN_OTHER, psBinaryOp_EXIT);
+    PS_ASSERT_PTR_DIMEN_GENERAL_NOT(input2, PS_DIMEN_OTHER, psBinaryOp_EXIT);
+
+    psMathType* psType1 = (psMathType*)in1;
+    psMathType* psType2 = (psMathType*)in2;
+    psDimen dim1 = psType1->dimen;
+    psDimen dim2 = psType2->dimen;
+    psElemType elType1 = psType1->type;
+    psElemType elType2 = psType2->type;
+
+    if (dim1 == PS_DIMEN_VECTOR || dim1 == PS_DIMEN_TRANSV) {
+        if (((psVector* ) in1)->n == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Vector contains zero elements");
+        }
+    } else if (dim1 == PS_DIMEN_IMAGE) {
+        if (((psImage* ) in1)->numCols == 0 || ((psImage* ) in1)->numRows == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Image contains zero length row or cols");
+        }
+    }
+
+    if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+        if (((psVector* ) in2)->n == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Vector contains zero elements");
+        }
+    } else if (dim2 == PS_DIMEN_IMAGE) {
+        if (((psImage* ) in2)->numCols == 0 || ((psImage* ) in2)->numRows == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Image contains zero length row or cols");
+        }
+    }
+
+    if (dim1 == PS_DIMEN_SCALAR) {
+        if ( out != NULL && ((psMathType*)out)->dimen != dim2) {
+            if (out != in1 && out != in2) {
+                psFree(out);
+            }
+            out = NULL;
+        }
+        if (dim2 == PS_DIMEN_SCALAR) {
+            if (out == NULL || ((psScalar*)out)->type.type != elType1) {
+                if (out != in1 && out != in2) {
+                    psFree(out);
+                }
+                out = psScalarAlloc(0.0,elType1);
+            }
+            BINARY_OP(SCALAR, SCALAR, out, psType1, op, psType2);       // scalar op scalar
+        } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+            out = psVectorRecycle(out,((psVector*)in2)->n,elType1);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        _("Couldn't create a proper output psVector."));
+                return NULL;
+            }
+            ((psVector*)out)->n = ((psVector*)in2)->n;
+            BINARY_OP(SCALAR, VECTOR, out, psType1, op, psType2);       // scalar op vector
+        } else if (dim2 == PS_DIMEN_IMAGE) {
+            out = psImageRecycle(out, ((psImage* ) in2)->numCols, ((psImage* ) in2)->numRows,elType1);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        _("Couldn't create a proper output psImage."));
+                return NULL;
+            }
+            BINARY_OP(SCALAR, IMAGE, out, psType1, op, psType2);        // scalar op image
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified parameter, %s, has invalid dimensionality, %d."),
+                    "in2",dim2);
+            psBinaryOp_EXIT;
+        }
+    } else if (dim1 == PS_DIMEN_VECTOR || dim1 == PS_DIMEN_TRANSV) {
+        if (dim2 == PS_DIMEN_SCALAR) {
+            out = psVectorRecycle(out,((psVector*)in1)->n,elType1);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        _("Couldn't create a proper output psVector."));
+                return NULL;
+            }
+            ((psVector*)out)->n = ((psVector*)in1)->n;
+            BINARY_OP(VECTOR, SCALAR, out, psType1, op, psType2);       // vector op scalar
+        } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+            out = psVectorRecycle(out,((psVector*)in2)->n,elType2);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        _("Couldn't create a proper output psVector."));
+                return NULL;
+            }
+            ((psVector*)out)->n = ((psVector*)in2)->n;
+            BINARY_OP(VECTOR, VECTOR, out, psType1, op, psType2);       // vector op vector
+        } else if (dim2 == PS_DIMEN_IMAGE) {
+            out = psImageRecycle(out, ((psImage* ) in2)->numCols, ((psImage* ) in2)->numRows, elType2);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        _("Couldn't create a proper output psImage."));
+                return NULL;
+            }
+            BINARY_OP(VECTOR, IMAGE, out, psType1, op, psType2);        // vector op image
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified parameter, %s, has invalid dimensionality, %d."),
+                    "in2",dim2);
+            psBinaryOp_EXIT;
+        }
+    } else if (dim1 == PS_DIMEN_IMAGE) {
+        out = psImageRecycle(out, ((psImage*)in1)->numCols, ((psImage*)in1)->numRows, elType1);
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    _("Couldn't create a proper output psImage."));
+            return NULL;
+        }
+        if (dim2 == PS_DIMEN_SCALAR) {
+            BINARY_OP(IMAGE, SCALAR, out, psType1, op, psType2);        // image op scalar
+        } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+            BINARY_OP(IMAGE, VECTOR, out, psType1, op, psType2);        // image op vector
+        } else if (dim2 == PS_DIMEN_IMAGE) {
+            BINARY_OP(IMAGE, IMAGE, out, psType1, op, psType2); // image op image
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified parameter, %s, has invalid dimensionality, %d."),
+                    "in2",dim2);
+            psBinaryOp_EXIT;
+        }
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Specified parameter, %s, has invalid dimensionality, %d."),
+                "in1",dim1);
+        psBinaryOp_EXIT;
+    }
+
+    // Automtically free psScalar types, since they are usually allocated in the argument list when this
+    // function is called, provided that the input is not the output.
+    if (psType1->dimen==PS_DIMEN_SCALAR && in1!=out) {
+        psFree(in1);
+    }
+
+    if (psType2->dimen==PS_DIMEN_SCALAR && in2!=out) {
+        psFree(in2);
+    }
+
+    return out;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psBinaryOp.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psBinaryOp.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psBinaryOp.h	(revision 22322)
@@ -0,0 +1,63 @@
+/* @file  psBinaryOp.h
+ *
+ * @brief Provides binary functions for simple matrix and vector element operations. 
+ * 
+ * Functions include:
+ *     Addition (+)
+ *     Subtraction (-)
+ *     Multiplication (*)
+ *     Division (/)
+ *     Power (^)
+ *     Minimum (min)
+ *     Maximum (max)
+ *     Absolute value (abs)
+ *     Exponent (exp)
+ *     Natural Log (ln)
+ *     Power of 10 (ten)
+ *     Log (log)
+ *     Sine (sin or dsin)
+ *     Cosine (cos or dcos)
+ *     Tangent (tan or dtan)
+ *     Arcsine (asin or dasin)
+ *     Arccosine (acos or dacos)
+ *     Arctan (atan or datan)
+ *
+ * Currently only vector-vector and image-image binary operations are supported.
+ *
+ * @author Ross Harman, MHPCC
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-23 22:47:23 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSBINARY_OP_H
+#define PSBINARY_OP_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/** Perform simple binary arithmetic with images or vectors
+ *
+ *  Performs addition, subtraction, multiplication, division, power, minumum, and maximum arithmetic
+ *  operations with images and vectors. Uses the form:
+ *
+ *      out = in1 op in2,
+ *
+ *      Where op is: "=", "+", "-", "*", "/", "^", "min", or "max"
+ *
+ *  This function only supports vector-vector or image-image operations.
+ *
+ *  @return  psType* : Pointer to either psImage or psVector.
+ */
+psMathType* psBinaryOp(
+    psPtr out,                         ///< Output type, either psImage or psVector.
+    const psPtr in1,                   ///< First input, either psImage or psVector.
+    const char *op,                    ///< Operator.
+    const psPtr in2                    ///< Second input, either psImage or psVector.
+);
+
+/// @}
+#endif // #ifndef PSBINARY_OP_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psClip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psClip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psClip.c	(revision 22322)
@@ -0,0 +1,197 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <math.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psErrorCodes.h"
+#include "psAssert.h"
+#include "psTrace.h"
+#include "psVector.h"
+#include "psStats.h"
+#include "psClip.h"
+
+// No-op; purpose of function is merely to identify the type
+static void clipParamsFree(psClipParams *params)
+{
+    return;
+}
+
+psClipParams *psClipParamsAlloc(psStatsOptions meanStat, psStatsOptions stdevStat,
+                                psMaskType masked, psMaskType clipped)
+{
+    psClipParams *params = psAlloc(sizeof(psClipParams)); // Clip parameters
+    psMemSetDeallocator(params, (psFreeFunc)clipParamsFree);
+
+    params->meanStat = meanStat;
+    params->stdevStat = stdevStat;
+    params->fracHigh = 0.0;
+    params->fracLow = 0.0;
+    params->numKeep = 0;
+    params->iter = 1;
+    params->rej = 0.0;
+    params->masked = masked;
+    params->clipped = clipped;
+
+    params->mean = NAN;
+    params->stdev = NAN;
+
+    return params;
+}
+
+
+long psClipMinMax(const psClipParams *params, const psVector *values, psVector *mask)
+{
+    PS_ASSERT_VECTOR_NON_NULL(values, -1);
+    PS_ASSERT_VECTOR_NON_NULL(mask, -1);
+    PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_MASK, -1);
+    PS_ASSERT_PTR(params, -1);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(values, mask, -1);
+    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(params->fracLow, 0.0, -1);
+    PS_ASSERT_FLOAT_LESS_THAN(params->fracLow, 1.0, -1);
+    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(params->fracHigh, 0.0, -1);
+    PS_ASSERT_FLOAT_LESS_THAN(params->fracHigh, 1.0, -1);
+    PS_ASSERT_INT_NONZERO(params->clipped, -1);
+
+    if (params->fracLow == 0.0 && params->fracHigh == 0.0) {
+        // No min-max rejection desired
+        return 0;
+    }
+
+    psMaskType masked = params->masked; // Indicates masked values
+    psMaskType clipped = params->clipped; // Indicates clipped values
+    masked |= clipped;                  // Make sure we're also masking clipped values
+    psMaskType *maskData = mask->data.PS_TYPE_MASK_DATA; // Dereference mask
+    long totalMasked = 0;               // Total number of pixels masked
+
+    // Apply fracLow,fracHigh if there are enough values
+
+    // Run through to get number of operational values
+    long numValid = 0;                  // Number of valid values
+    for (long i = 0; i < mask->n; i++) {
+        if (!(maskData[i] & masked)) {
+            numValid++;
+        }
+    }
+    psTrace("psLib.math", 2, "%ld valid values.\n", numValid);
+
+    // XXX: Not sure if sorting provides the fastest implementation.  It might be quicker to do a linear pass
+    // through the data, pulling out the highest M and lowest N values,
+
+    float keepFrac = 1.0 - params->fracLow - params->fracHigh; // Fraction of values to keep
+    if (numValid * keepFrac >= params->numKeep) {
+        psTrace("psLib.math", 1, "Applying min/max clipping.\n");
+        psVector *index = psVectorSortIndex(NULL, values);
+        int numLow = numValid * params->fracLow; // Number of low values to clip
+        int numHigh = numValid * params->fracHigh; // Number of high values to clip
+        // Low values
+        psS32 *indexData = index->data.S32; // Dereference index
+        long numMasked = 0;             // Number masked
+        for (long i = 0; i < index->n && numMasked < numLow; i++) {
+            // Don't count the ones that are already masked
+            if (! (maskData[indexData[i]] & masked)) {
+                maskData[indexData[i]] |= clipped;
+                numMasked++;
+            }
+        }
+        totalMasked += numMasked;
+        numMasked = 0;
+        // High values
+        for (long i = values->n - 1;  i >= 0 && numMasked < numHigh; i--) {
+            // Don't count the ones that are already masked
+            if (! (maskData[indexData[i]] & masked)) {
+                maskData[indexData[i]] |= clipped;
+                numMasked++;
+            }
+        }
+        totalMasked += numMasked;
+        psFree(index);
+    }
+
+    return totalMasked;
+}
+
+
+long psClipReject(psClipParams *params, const psVector *values, psVector *mask, const psVector *errors)
+{
+    PS_ASSERT_VECTOR_NON_NULL(values, -1);
+    PS_ASSERT_VECTOR_NON_NULL(mask, -1);
+    PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_MASK, -1);
+    PS_ASSERT_PTR(params, -1);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(values, mask, -1);
+    if (errors) {
+        PS_ASSERT_VECTOR_NON_NULL(errors, -1);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(values, errors, -1);
+        PS_ASSERT_VECTOR_TYPE_F32_OR_F64(values, -1);
+        PS_ASSERT_VECTOR_TYPE_EQUAL(values, errors, -1);
+    }
+    PS_ASSERT_INT_NONZERO(params->meanStat, -1);
+    PS_ASSERT_INT_NONZERO(params->stdevStat, -1);
+    if (!isfinite(params->rej) || params->rej < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Rejection limit (%f) is not valid.\n", params->rej);
+        return -1;
+    }
+
+    if (params->rej == 0.0) {
+        // No clipping desired
+        return 0;
+    }
+
+    psMaskType masked = params->masked; // Indicates masked values
+    psMaskType clipped = params->clipped; // Indicates clipped values
+    masked |= clipped;                  // Make sure we're also masking clipped values
+
+    // Get statistics
+    psStats *stats = psStatsAlloc(params->meanStat | params->stdevStat);
+    if (!psVectorStats(stats, values, errors, mask, masked)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to perform statistics on values.\n");
+        psFree(stats);
+        return -1;
+    }
+    params->mean = psStatsGetValue(stats, params->meanStat);
+    params->stdev = psStatsGetValue(stats, params->stdevStat);
+
+    #define REJECT_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE *valuesData = values->data.TYPE; /* Dereference for speed */ \
+        psMaskType *maskData = mask->data.PS_TYPE_MASK_DATA; /* Dereference mask for speed */ \
+        if (errors) { \
+            ps##TYPE *errorsData = errors->data.TYPE; \
+            for (int j = 0; j < values->n; j++) { \
+                if (!(maskData[j] & masked) && \
+                        fabs(valuesData[j] - params->mean) > params->rej * errorsData[j]) { \
+                    maskData[j] |= clipped; \
+                    totalMasked++; \
+                } \
+            } \
+        } else { \
+            ps##TYPE limit = params->rej * params->stdev; /* Limit on deviation */ \
+            for (int j = 0; j < values->n; j++) { \
+                if (!(maskData[j] & masked) && fabs(valuesData[j] - params->mean) > limit) { \
+                    maskData[j] |= clipped; \
+                    totalMasked++; \
+                } \
+            } \
+        } \
+    }
+
+    long totalMasked = 0;               // Total number of pixels masked
+    switch (values->type.type) {
+        REJECT_CASE(S8);
+        REJECT_CASE(S16);
+        REJECT_CASE(S32);
+        REJECT_CASE(S64);
+        REJECT_CASE(F32);
+        REJECT_CASE(F64);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unsupported vector type: %x\n", values->type.type);
+        psFree(stats);
+        return -1;
+    }
+
+    psFree(stats);
+    return totalMasked;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psClip.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psClip.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psClip.h	(revision 22322)
@@ -0,0 +1,64 @@
+/* @file  psClip.h
+ * @brief vector clipping functions
+ *
+ * @author Paul Price, IfA.
+ *
+ * $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ * $Date: 2007-08-09 01:40:07 $
+ * Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PS_CLIP_H
+#define PS_CLIP_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/// Parameters for clipping
+typedef struct
+{
+    psStatsOptions meanStat;            ///< Stats option to use for mean
+    psStatsOptions stdevStat;           ///< Stats option to use for standard deviation
+    float fracHigh;                     ///< Fraction of high values to clip
+    float fracLow;                      ///< Fraction of low values to clip
+    int numKeep;                        ///< Minimum number of values to keep from clipping
+    int iter;                           ///< Number of rejection iterations; unused by psClip functions
+    float rej;                          ///< Rejection limit (standard deviations)
+    psMaskType masked;                  ///< Mask value for entries already masked
+    psMaskType clipped;                 ///< Mask value to give to clipped entries
+    double mean;                        ///< Resultant mean
+    double stdev;                       ///< Resultant stdev
+}
+psClipParams;
+
+/// Allocator
+psClipParams *psClipParamsAlloc(psStatsOptions meanStat, ///< Stats option to use for mean
+                                psStatsOptions stdevStat, ///< Stats option to use for standard deviation
+                                psMaskType masked, ///< Mask value for entries already masked
+                                psMaskType clipped ///< Mask value to give to clipped entries
+    ) PS_ATTR_MALLOC;
+
+
+/// Apply min-max clipping to a list of values
+///
+/// The specified fraction of high and low values are identified as clipped in the mask.  Errors are not used
+/// in this step.
+long psClipMinMax(const psClipParams *params, ///< Clip parameters
+                  const psVector *values, ///< Values to inspect and clip
+                  psVector *mask        ///< Mask for values
+    );
+
+
+
+/// Apply a rejection iteration to a list of values
+///
+/// The specified rejection limit is applied to the values and errors; discrepant values are identified as
+/// clipped in the mask.  This function only applies a single rejection iteration.
+long psClipReject(psClipParams *params, ///< Clip parameters
+                  const psVector *values, ///< Values to inspect and clip
+                  psVector *mask,       ///< Mask for values
+                  const psVector *errors ///< Errors for values, or NULL
+    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psCompare.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psCompare.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psCompare.c	(revision 22322)
@@ -0,0 +1,123 @@
+
+/** @file psCompare.c
+ *  @brief Comparison functions for sorting routines
+ *  @ingroup Compare
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-01-09 22:38:52 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "psCompare.h"
+
+#define COMPARE_NUMERIC_PTR(TYPE) \
+int psCompare##TYPE##Ptr(const void** a, const void** b) { \
+    return **((ps##TYPE**)a) - **((ps##TYPE**)b); \
+}
+
+#define COMPARE_NUMERIC_PTR_DESCENDING(TYPE) \
+int psCompareDescending##TYPE##Ptr(const void** a, const void** b) { \
+    return **((ps##TYPE**)b) - **((ps##TYPE**)a); \
+}
+
+#define COMPARE_NUMERIC(TYPE) \
+int psCompare##TYPE(const void* a, const void* b) { \
+    return *((ps##TYPE*)a) - *((ps##TYPE*)b); \
+}
+
+#define COMPARE_NUMERIC_DESCENDING(TYPE) \
+int psCompareDescending##TYPE(const void* a, const void* b) { \
+    return *((ps##TYPE*)b) - *((ps##TYPE*)a); \
+}
+
+COMPARE_NUMERIC_PTR(S8)
+COMPARE_NUMERIC_PTR(S16)
+COMPARE_NUMERIC_PTR(S32)
+COMPARE_NUMERIC_PTR(S64)
+COMPARE_NUMERIC_PTR(U8)
+COMPARE_NUMERIC_PTR(U16)
+COMPARE_NUMERIC_PTR(U32)
+COMPARE_NUMERIC_PTR(U64)
+
+int psCompareF32Ptr(const void** a, const void** b)
+{
+    psF32 diff = **((psF32**)a) - **((psF32**)b);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareF64Ptr(const void** a, const void** b)
+{
+    psF64 diff = **((psF64**)a) - **((psF64**)b);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
+
+COMPARE_NUMERIC_PTR_DESCENDING(S8)
+COMPARE_NUMERIC_PTR_DESCENDING(S16)
+COMPARE_NUMERIC_PTR_DESCENDING(S32)
+COMPARE_NUMERIC_PTR_DESCENDING(S64)
+COMPARE_NUMERIC_PTR_DESCENDING(U8)
+COMPARE_NUMERIC_PTR_DESCENDING(U16)
+COMPARE_NUMERIC_PTR_DESCENDING(U32)
+COMPARE_NUMERIC_PTR_DESCENDING(U64)
+
+int psCompareDescendingF32Ptr(const void** a, const void** b)
+{
+    psF32 diff = **((psF32**)b) - **((psF32**)a);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareDescendingF64Ptr(const void** a, const void** b)
+{
+    psF64 diff = **((psF64**)b) - **((psF64**)a);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
+
+COMPARE_NUMERIC(S8)
+COMPARE_NUMERIC(S16)
+COMPARE_NUMERIC(S32)
+COMPARE_NUMERIC(S64)
+COMPARE_NUMERIC(U8)
+COMPARE_NUMERIC(U16)
+COMPARE_NUMERIC(U32)
+COMPARE_NUMERIC(U64)
+
+int psCompareF32(const void* a, const void* b)
+{
+    psF32 diff = *((psF32*)a) - *((psF32*)b);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareF64(const void* a, const void* b)
+{
+    psF64 diff = *((psF64*)a) - *((psF64*)b);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
+
+COMPARE_NUMERIC_DESCENDING(S8)
+COMPARE_NUMERIC_DESCENDING(S16)
+COMPARE_NUMERIC_DESCENDING(S32)
+COMPARE_NUMERIC_DESCENDING(S64)
+COMPARE_NUMERIC_DESCENDING(U8)
+COMPARE_NUMERIC_DESCENDING(U16)
+COMPARE_NUMERIC_DESCENDING(U32)
+COMPARE_NUMERIC_DESCENDING(U64)
+
+int psCompareDescendingF32(const void* a, const void* b)
+{
+    psF32 diff = *((psF32*)b) - *((psF32*)a);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareDescendingF64(const void* a, const void* b)
+{
+    psF64 diff = *((psF64*)b) - *((psF64*)a);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psCompare.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psCompare.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psCompare.h	(revision 22322)
@@ -0,0 +1,502 @@
+/* @file psCompare.h
+ * @brief Comparison functions for sorting routines
+ *
+ * @author Robert Daniel DeSonia, MHPCC
+ *
+ * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-09-28 21:02:23 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_COMPARE_H
+#define PS_COMPARE_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include "psType.h"
+
+/** A comparison function for sorting elements that are pointers to data,
+ *  e.g., for psList of pointers to numeric values.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+typedef int (*psComparePtrFunc) (
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** A comparison function for sorting.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+typedef int (*psCompareFunc) (
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+/** Compare function of psS8 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareS8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS16 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareS16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS32 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareS32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS64 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareS64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU8 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareU8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU16 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareU16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU32 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareU32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU64 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareU64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF32 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareF32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF64 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareF64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS8 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingS8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS16 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingS16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS32 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingS32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS64 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingS64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU8 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingU8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU16 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingU16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU32 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or lessg than the second.
+ */
+int psCompareDescendingU32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU64 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or lessg than the second.
+ */
+int psCompareDescendingU64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF32 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or lessg than the second.
+ */
+int psCompareDescendingF32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF64 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or lessg than the second.
+ */
+int psCompareDescendingF64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareS8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareS16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareS32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareS64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareU8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareU16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareU32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareU64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareF32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively less
+ *                   than, equal to, or greater than the second.
+ */
+int psCompareF64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingS8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingS16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingS32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingS64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingU8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingU16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingU32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingU64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingF32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if
+ *                   the first argument is considered to be respectively greater
+ *                   than, equal to, or less than the second.
+ */
+int psCompareDescendingF64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+/// @}
+#endif  // #ifndef PS_COMPARE_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psConstants.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psConstants.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psConstants.h	(revision 22322)
@@ -0,0 +1,73 @@
+/* @file  psConstants.h
+ *
+ * @brief Definitions of various constants and common macros
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.96 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-03-18 18:25:00 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ * XXX: Add parenthesis around all arguments so that these macros can be
+ *      called with complex expressions.
+ *
+ * XXX: All functions which use the PS_ASSERT macros must be scrutinized so
+ * that we ensure that an argument which is expected to be output is
+ * psFree'ed before reurning NULL.
+ *
+ * XXX: The macros have a name similar to PS_CHECK_CONDITION() and generally
+ * throw a psError if the CONDITION is true.  However, some throw the error
+ * if the CONDITION is false.  This should be consistant.
+ *
+ */
+
+#ifndef PS_CONSTANTS_H
+#define PS_CONSTANTS_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include <math.h> // for M_PI
+
+/*****************************************************************************
+These are common mathimatical constants used by various functions in the psLib.
+ *****************************************************************************/
+#ifndef M_PI
+#define M_PI   3.1415926535897932384626433832795029  /* pi */
+#define M_PI_2 1.5707963267948966192313216916397514  /* pi/2 */
+#define M_PI_4 0.7853981633974483096156608458198757  /* pi/4 */
+#define M_1_PI 0.3183098861837906715377675267450287  /* 1/pi */
+#define M_2_PI 0.6366197723675813430755350534900574  /* 2/pi */
+#endif // #ifndef M_PI
+
+#define DEG_TO_RAD(DEGREES) ((DEGREES) * M_PI / 180.0)
+#define MIN_TO_RAD(MINUTES) ((MINUTES) * M_PI / (180.0 * 60.0))
+#define SEC_TO_RAD(SECONDS) ((SECONDS) * M_PI / (180.0 * 60.0 * 60.0))
+#define RAD_TO_DEG(RADIANS) ((RADIANS) * 180.0 / M_PI)
+#define RAD_TO_MIN(RADIANS) ((RADIANS) * 180.0 * 60.0 / M_PI)
+#define RAD_TO_SEC(RADIANS) ((RADIANS) * 180.0 * 60.0 * 60.0 / M_PI)
+
+# define PS_DEG_RAD 57.295779513082322
+# define PS_RAD_DEG  0.017453292519943
+
+/*****************************************************************************
+    Misc. macros:
+*****************************************************************************/
+#define PS_MAX(A, B) \
+(((A) > (B)) ? (A) : (B))
+
+#define PS_MIN(A, B) \
+(((A) < (B)) ? (A) : (B))
+
+#define PS_SQR(A) \
+((A) * (A))
+
+#define PS_SWAP(X,Y) {double tmp=(X); (X) = (Y); (Y) = tmp;}
+
+// These defines for bitwise opertaions are necessary to yield results of the proper size (use instead of ~)
+#define PS_NOT_U8(A)(UCHAR_MAX-(A)) // Perform bitwise NOT on A which is of type U8
+#define PS_NOT_U16(A)(USHORT_MAX-(A)) // Perform bitwaise NOT on A which is of type U16
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psEllipse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psEllipse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psEllipse.c	(revision 22322)
@@ -0,0 +1,212 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "psConstants.h"
+#include "psEllipse.h"
+
+// ellipse rotation (major, minor, theta) -> (x2, y2, xy)
+psEllipsePol psEllipseAxesToPol(psEllipseAxes axes)
+{
+    psEllipsePol pol;
+
+    pol.e0 = PS_SQR(axes.major) + PS_SQR(axes.minor);
+    double ds = PS_SQR(axes.major) - PS_SQR(axes.minor);
+
+    pol.e1 = ds*cos(2*axes.theta);
+    pol.e2 = ds*sin(2*axes.theta);
+
+    assert (isfinite(pol.e0));
+    assert (isfinite(pol.e1));
+    assert (isfinite(pol.e2));
+
+    return pol;
+}
+
+// ellipse rotation (major, minor, theta) -> (x2, y2, xy)
+psEllipsePol psEllipseShapeToPol(psEllipseShape shape)
+{
+    psEllipsePol pol;
+
+    double r = 1.0 / (1.0 - PS_SQR(shape.sx)*PS_SQR(shape.sy)*PS_SQR(shape.sxy));
+
+    pol.e0 = r*(PS_SQR(shape.sx) + PS_SQR(shape.sy));
+    pol.e1 = r*(PS_SQR(shape.sx) - PS_SQR(shape.sy));
+    // XXX I do not understand this negative sign
+    pol.e2 = -r*(2.0*PS_SQR(shape.sx)*PS_SQR(shape.sy)*shape.sxy);
+
+    assert (isfinite(pol.e0));
+    assert (isfinite(pol.e1));
+    assert (isfinite(pol.e2));
+
+    return pol;
+}
+
+// ellipse rotation (major, minor, theta) -> (x2, y2, xy)
+// XXXX handle case where e0 < LIMIT
+psErrorCode psEllipsePolToAxes(const psEllipsePol pol,
+			       const float minMinorAxis,
+			       psEllipseAxes *axes)
+{
+    axes->theta = +0.5 * atan2 (pol.e2, pol.e1); // theta in radians
+
+    double cs = cos(2*axes->theta);
+    double sn = sin(2*axes->theta);
+    double ds = 0;
+
+    if ((cs > 0.707) || (cs < -0.707)) {
+	ds = pol.e1 / cs;
+    } else {
+	ds = pol.e2 / sn;
+    }
+
+    float LIMIT = PS_SQR(minMinorAxis);
+    if (pol.e0 < LIMIT) {
+	// if e0 is too small, we are really out of luck
+	axes->major = minMinorAxis;
+	axes->minor = minMinorAxis;
+    } else {
+	if (2.0*(pol.e0 - ds) < LIMIT) {
+	    // if e0 - ds is too small, saturate the minor axis at minMinorAxis
+	    axes->major = sqrt(pol.e0 - LIMIT);
+	    axes->minor = sqrt(LIMIT);
+	} else {
+	    // normal values for e0 & ds
+	    axes->major = sqrt(0.5*(pol.e0 + ds));
+	    axes->minor = sqrt(0.5*(pol.e0 - ds));
+	}
+    }
+
+    if (!isfinite(axes->major) || !isfinite(axes->minor) || !isfinite(axes->theta)) {
+	return psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Shape of object is NaN");
+    }
+
+    return PS_ERR_NONE;
+}
+
+// ellipse rotation (major, minor, theta) -> (x2, y2, xy)
+psEllipseMoments psEllipseAxesToMoments(psEllipseAxes axes)
+{
+    psEllipseMoments moments;
+
+    double f1 = PS_SQR(axes.major) + PS_SQR(axes.minor);
+    double f2 = PS_SQR(axes.major) - PS_SQR(axes.minor);
+
+    moments.x2 = +0.5*f1 + 0.5*f2*cos(2*axes.theta);
+    moments.y2 = +0.5*f1 - 0.5*f2*cos(2*axes.theta);
+    moments.xy = +0.5*f2*sin(2*axes.theta);
+
+    assert (isfinite(moments.x2));
+    assert (isfinite(moments.y2));
+    assert (isfinite(moments.xy));
+
+    return moments;
+}
+
+// ellipse rotation (x2, y2, xy) -> (major, minor, theta)
+psEllipseAxes psEllipseMomentsToAxes(psEllipseMoments moments, double maxAR)
+{
+    psEllipseAxes axes;
+    psEllipseAxes badValue = {NAN, NAN, NAN};
+
+    if (!isfinite(moments.x2)) return badValue;
+    if (!isfinite(moments.y2)) return badValue;
+    if (!isfinite(moments.xy)) return badValue;
+
+    if (moments.x2 < 0) return badValue;
+    if (moments.y2 < 0) return badValue;
+
+    double g1 = moments.x2 + moments.y2;
+    double g2 = moments.x2 - moments.y2;
+    double g3 = sqrt(PS_SQR(g2) + 4*PS_SQR(moments.xy));
+
+    axes.major = sqrt (0.5*(g1 + g3));
+    axes.theta = +0.5 * atan2 (+2.0*moments.xy, g2); // theta in radians
+
+    // long, thin objects are likely to have a poorly measured minor axis
+    // the angle and major axis are likely to be ok.
+    // restrict the axis ratio
+    double rAR2 = (g1 - g3) / (g1 + g3);
+    if (rAR2 < 1.0/PS_SQR(maxAR)) {
+        axes.minor = axes.major / maxAR;
+    } else {
+        axes.minor = sqrt (0.5*(g1 - g3));
+    }
+
+    assert (isfinite(axes.major));
+    assert (isfinite(axes.minor));
+    assert (isfinite(axes.theta));
+
+    return axes;
+}
+
+// ellipse rotation (major, minor, theta) -> (sx, sy, sxy)
+psEllipseShape psEllipseAxesToShape(psEllipseAxes axes)
+{
+    psEllipseShape shape;
+    psEllipseShape badValue = {NAN, NAN, NAN};
+
+    if (!isfinite(axes.minor)) return badValue;
+    if (!isfinite(axes.major)) return badValue;
+    if (!isfinite(axes.theta)) return badValue;
+
+    if (axes.minor <= 0) return badValue;
+    if (axes.major <= 0) return badValue;
+
+    double f1 = 1.0 / PS_SQR(axes.minor) + 1.0 / PS_SQR(axes.major);
+    double f2 = 1.0 / PS_SQR(axes.minor) - 1.0 / PS_SQR(axes.major);
+
+    double sxr = 0.5*f1 - 0.5*f2*cos(2*axes.theta);
+    double syr = 0.5*f1 + 0.5*f2*cos(2*axes.theta);
+
+    // sxr, syr cannot be < 0 (f1 >= f2)
+
+    shape.sx  = +1.0 / sqrt(sxr);
+    shape.sy  = +1.0 / sqrt(syr);
+    shape.sxy = -0.5*f2*sin(2*axes.theta);
+
+    assert (isfinite(shape.sx));
+    assert (isfinite(shape.sy));
+    assert (isfinite(shape.sxy));
+
+    return shape;
+}
+
+// ellipse derotation (sx, sy, sxy) -> (major, minor, theta)
+psEllipseAxes psEllipseShapeToAxes(psEllipseShape shape, double maxAR)
+{
+    psEllipseAxes axes;
+
+    double f1 = 1.0 / PS_SQR(shape.sy) + 1.0 / PS_SQR(shape.sx);
+    double f2 = 1.0 / PS_SQR(shape.sy) - 1.0 / PS_SQR(shape.sx);
+    double f3 = sqrt(PS_SQR(f2) + 4*PS_SQR(shape.sxy));
+
+    axes.minor = sqrt (2.0 / (f1 + f3));
+    axes.theta = -0.5 * atan2 (+2.0*shape.sxy, f2);
+
+    // long, thin objects are likely to have a poorly measured major axis
+    // the angle and minor axis are likely to be ok.
+    // restrict the axis ratio
+    double rAR2 = (f1 - f3) / (f1 + f3);
+    if (rAR2 < 1.0/PS_SQR(maxAR)) {
+        axes.major = axes.minor * maxAR;
+    } else {
+        axes.major = sqrt (2.0 / (f1 - f3));
+    }
+
+    assert (isfinite(axes.theta));
+    assert (isfinite(axes.major));
+    assert (isfinite(axes.minor));
+
+    return axes;
+}
+
+// XXX keep this construction?
+// force the axis ratio to be less than 10
+// double r1 = 0.5*0.95*sqrt (PS_SQR(f1) - PS_SQR(f2));
+
+//    double f = sqrt (0.25*PS_SQR(moments.x2 - moments.y2) + PS_SQR(moments.xy));
+//    if (f > (moments.x2 + moments.y2) / 2.0) {
+//        f = 0.98*(moments.x2 + moments.y2) / 2.0;
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psEllipse.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psEllipse.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psEllipse.h	(revision 22322)
@@ -0,0 +1,84 @@
+/* @file  psEllipse.h
+ * @brief functions to manipulate sparse matrices equations
+ *
+ * $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ * $Date: 2007-12-12 21:01:32 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ELLIPSE_H
+#define PS_ELLIPSE_H
+
+#include "psError.h"
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+// Different representations of an ellipse
+
+/// Ellipse defined in terms of axes
+typedef struct {
+    double major;                       ///< Major axis
+    double minor;                       ///< Minor axis
+    double theta;                       ///< Position angle
+} psEllipseAxes;
+
+/// Ellipse defined in terms of moments
+typedef struct {
+    double x2;                          ///< Moment of xx (Mxx)
+    double y2;                          ///< Moment of yy (Myy)
+    double xy;                          ///< Moment of xy (Mxy)
+} psEllipseMoments;
+
+/// Ellipse defined in terms of Gaussian shape parameters
+typedef struct {
+    double sx;                          ///< Shape parameter in x
+    double sy;                          ///< Shape parameter in y
+    double sxy;                         ///< Shape parameter in xy
+} psEllipseShape;
+
+/// Ellipse defined in terms of polarisations
+typedef struct {
+    double e0;                          ///< Scale (Mxx + Myy)
+    double e1;                          ///< Polarization 1 (Mxx - Myy)
+    double e2;                          ///< Polarization 1 (2Mxy)
+} psEllipsePol;
+
+// Conversions between elliptical shape representations
+
+/// Convert axes to moments representation
+psEllipseMoments psEllipseAxesToMoments(psEllipseAxes axes ///< Axes of ellipse
+                                        );
+
+/// Convert moments to axes representation
+psEllipseAxes psEllipseMomentsToAxes(psEllipseMoments moments, ///< Moments of ellipse
+                                     double maxAR ///< Maximum allowed axis ratio
+                                     );
+
+/// Convert axes to shape representation
+psEllipseShape psEllipseAxesToShape(psEllipseAxes axes ///< Axes of ellipse
+                                    );
+
+/// Convert shape to axes representation
+psEllipseAxes psEllipseShapeToAxes(psEllipseShape shape, ///< Shape of ellipse
+                                   double maxAR ///< Maximum allowed axis ratio
+                                   );
+
+/// Convert axes to polarisation representation
+psEllipsePol psEllipseAxesToPol(psEllipseAxes axes ///< Axes of ellipse
+                                );
+
+/// Convert shape to polarisation representation
+psEllipsePol psEllipseShapeToPol(psEllipseShape shape ///< Shape of ellipse
+                                 );
+
+/// Convert polarisation to axes representation
+///
+/// XXX This API goes against the PS convention of outputs being first.
+psErrorCode psEllipsePolToAxes(const psEllipsePol pol, ///< Polarisation of ellipse
+                               const float minMinorAxis, ///< Minimum allowed minor axis
+                               psEllipseAxes *axes ///< Output ellipse axes
+                               );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psHistogram.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psHistogram.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psHistogram.c	(revision 22322)
@@ -0,0 +1,352 @@
+/** @file  psHistogram.h
+ *  \brief basic histogram functions
+ *  @ingroup Math
+ *
+ *  @author GLG (MHPCC), EAM (IfA)
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-05-07 23:11:30 $
+ *
+ *  Copyright 2006 IfA, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <float.h>
+#include <math.h>
+#include <limits.h>
+#include <strings.h>
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+#include "psMemory.h"
+#include "psAbort.h"
+//#include "psImage.h"
+#include "psVector.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psStats.h"
+#include "psHistogram.h"
+#include "psAssert.h"
+#include "psMathUtils.h"
+//#include "psList.h"
+//#include "psString.h"
+
+static void histogramFree(psHistogram* myHist);
+
+/******************************************************************************
+psHistogramAlloc(lower, upper, n): allocate a uniform histogram structure
+with the specifed upper and lower limits, and the specifed number of bins.
+This routine will also set the bounds for each of the bins.
+
+Input:
+    lower
+    upper
+    n
+Returns:
+    The histogram structure
+ *****************************************************************************/
+psHistogram* psHistogramAlloc(float lower, float upper, int n)
+{
+    psTrace("psLib.math", 3, "---- %s() begin  ----\n", __func__);
+    psTrace("psLib.math", 5, "(lower, upper, n) is (%f, %f, %d)\n", lower, upper, n);
+    PS_ASSERT_INT_POSITIVE(n, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(upper, lower, NULL);
+
+    // Allocate memory for the new histogram structure.  If there are N bins, then there are N+1 bounds to
+    // those bins.
+    psHistogram *newHist = (psHistogram* ) psAlloc(sizeof(psHistogram)); // The new histogram structure
+    psMemSetDeallocator(newHist, (psFreeFunc) histogramFree);
+    psVector* newBounds = psVectorAlloc(n + 1, PS_TYPE_F32);
+    newHist->bounds = newBounds;
+
+    // Calculate the bounds for each bin.
+    psF32 binSize = (upper - lower) / (psF32)n; // The histogram bin size
+    // XXX: Is the following necessary? It prevents the max data point from being in a non-existant bin.
+    binSize += FLT_EPSILON;
+    for (long i = 0; i < n + 1; i++) {
+        newBounds->data.F32[i] = lower + (binSize * (psF32)i);
+    }
+
+    // Allocate the bins, and initialize them to zero.
+    newHist->nums = psVectorAlloc(n, PS_TYPE_F32);
+    psVectorInit(newHist->nums, 0.0);
+
+    // Initialize the other members.
+    newHist->minNum = 0;
+    newHist->maxNum = 0;
+    newHist->uniform = true;
+
+    psTrace("psLib.math", 3, "---- %s() end  ----\n", __func__);
+    return newHist;
+}
+
+/******************************************************************************
+psHistogramAllocGeneric(bounds): allocate a non-uniform histogram structure
+with the specifed bounds.
+
+Input:
+    bounds
+Returns:
+    The histogram structure
+ *****************************************************************************/
+psHistogram* psHistogramAllocGeneric(const psVector* bounds)
+{
+    psTrace("psLib.math", 3, "---- %s() begin  ----\n", __func__);
+    PS_ASSERT_VECTOR_NON_NULL(bounds, NULL);
+    PS_ASSERT_VECTOR_TYPE(bounds, PS_TYPE_F32, NULL);
+    PS_ASSERT_LONG_LARGER_THAN_OR_EQUAL(bounds->n, (long)2, NULL);
+
+    // Allocate memory for the new histogram structure.
+    psHistogram *newHist = (psHistogram* ) psAlloc(sizeof(psHistogram)); // The new histogram structure
+    psMemSetDeallocator(newHist, (psFreeFunc) histogramFree);
+    psVector* newBounds = psVectorCopy(NULL, bounds, PS_TYPE_F32);
+    newHist->bounds = newBounds;
+
+    // Allocate the bins, and initialize them to zero.  If there are N bounds,
+    // then there are N-1 bins.
+    newHist->nums = psVectorAlloc((bounds->n) - 1, PS_TYPE_F32);
+    psVectorInit(newHist->nums, 0.0);
+
+    // Initialize the other members.
+    newHist->minNum = 0;
+    newHist->maxNum = 0;
+    newHist->uniform = false;
+
+    psTrace("psLib.math", 3, "---- %s() end  ----\n", __func__);
+    return (newHist);
+}
+
+static void histogramFree(psHistogram* myHist)
+{
+    psFree((void *)myHist->bounds);
+    psFree(myHist->nums);
+}
+
+
+bool psMemCheckHistogram(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)histogramFree );
+}
+
+/*****************************************************************************
+UpdateHistogramBins(binNum, out, data, error): This routine is to be used when
+updating the histogram in the presence of errors in the input data.  We treat
+the data point as a boxcar PDF and update a range of points surrounding the
+histogram bin which contains the point.  The width of that boxcar is defined
+as 2.35 * error.
+
+XXX: Must test this.
+ *****************************************************************************/
+static bool UpdateHistogramBins(long binNum, // Bin number of the data point
+                                psHistogram* out, // The histogram to be updated
+                                psF32 data, // The data point value
+                                psF32 error // The error in the data point
+                               )
+{
+    psTrace("psLib.math", 3, "---- %s() begin  ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(out, false);
+    PS_ASSERT_PTR_NON_NULL(out->bounds, false);
+    PS_ASSERT_PTR_NON_NULL(out->nums, false);
+    PS_ASSERT_LONG_WITHIN_RANGE(binNum, (long)0, (long)((out->nums->n)-1), false);
+    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(error, 0.0, false);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(data, out->bounds->data.F32[0],
+                                 out->bounds->data.F32[(out->bounds->n)-1], false);
+
+    psF32 boxcarWidth = 2.35 * error;   // Width of the boxcar
+    psF32 boxcarCenter = (out->bounds->data.F32[binNum] +
+                          out->bounds->data.F32[binNum+1]) / 2.0; // Centre of the boxcar
+    psF32 boxcarLeft = boxcarCenter - (boxcarWidth / 2.0); // Left endpoint of the boxcar for the PDF
+    psF32 boxcarRight = boxcarCenter + (boxcarWidth / 2.0); // Right endpoint of the boxcar for the PDF
+    psS32 boxcarLeftBinNum = 0;         // Bin number for left endpoint
+    psS32 boxcarRightBinNum = 0;        // Bin number for right endpoint
+
+    // Determine the left endpoint of the boxcar for the PDF.
+    for (long bin = binNum; bin >= 0; bin--) {
+        if (out->nums->data.F32[bin] <= boxcarLeft) {
+            boxcarLeftBinNum = bin;
+            break;
+        }
+    }
+
+    // Determine the right endpoint of the boxcar for the PDF.
+    for (long bin = binNum; bin < out->nums->n; bin++) {
+        if (out->nums->data.F32[bin] >= boxcarRight) {
+            boxcarRightBinNum = bin;
+            break;
+        }
+    }
+
+    // If the boxcar fits entirely inside this bin, then simply add 1.0 to the
+    // bin and return.
+    if (boxcarLeftBinNum == boxcarRightBinNum) {
+        out->nums->data.F32[binNum] += 1.0;
+        psTrace("psLib.math", 3, "---- %s(true) end  ----\n", __func__);
+        return true;
+    }
+
+    // If we get here, multiple bins must be updated.  We handle the left-most endpoint, and right-most
+    // endpoints differently.
+    out->nums->data.F32[boxcarLeftBinNum] +=
+        (out->bounds->data.F32[boxcarLeftBinNum+1] - boxcarLeft) / boxcarWidth;
+
+    // Loop through the center bins, if any.
+    for (long bin = boxcarLeftBinNum + 1; bin < (boxcarRightBinNum - 1); bin++) {
+        out->nums->data.F32[bin] +=
+            (out->bounds->data.F32[bin+1] - out->bounds->data.F32[bin]) / boxcarWidth;
+    }
+
+    // Handle the right endpoint differently.
+    out->nums->data.F32[boxcarRightBinNum]+=
+        (boxcarRight - out->bounds->data.F32[boxcarRightBinNum]) / boxcarWidth;
+
+    psTrace("psLib.math", 3, "---- %s(true) end  ----\n", __func__);
+    return true;
+}
+
+/*****************************************************************************
+psVectorHistogram(out, in, errors, mask, maskVal): this procedure takes as
+input a preallocated and initialized histogram structure.  It fills the bins
+in that histogram structure in accordance with the input data "in" and the,
+possibly NULL, mask vector.
+
+Inputs:
+    out
+    in
+    mask
+    maskVal
+Returns:
+    The histogram structure "out".
+ *****************************************************************************/
+bool psVectorHistogram(psHistogram* out,
+                               const psVector* values,
+                               const psVector* errors,
+                               const psVector* mask,
+                               psMaskType maskVal)
+{
+    psTrace("psLib.math", 3, "---- %s() begin  ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(out, false);
+    PS_ASSERT_VECTOR_NON_NULL(out->bounds, false);
+    PS_ASSERT_VECTOR_TYPE(out->bounds, PS_TYPE_F32, false);
+    PS_ASSERT_INT_NONNEGATIVE(out->bounds->n, false);
+    PS_ASSERT_VECTOR_NON_NULL(out->nums, false);
+    PS_ASSERT_VECTOR_TYPE(out->nums, PS_TYPE_F32, false);
+    PS_ASSERT_INT_NONNEGATIVE(out->nums->n, false);
+    PS_ASSERT_VECTOR_NON_NULL(values, out);
+    if (mask) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(values, mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+    }
+    if (errors) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(values, errors, false);
+        PS_ASSERT_VECTOR_TYPE(errors, values->type.type, false);
+    }
+
+    long binNum = 0;                    // A temporary bin number
+    long numBins = out->nums->n;        // The total number of bins
+    psScalar tmpScalar;
+    tmpScalar.type.type = PS_TYPE_F32;
+
+    // Convert input and errors vectors to F32 if necessary.
+    psVector* inF32 = NULL;             // F32 version of input vector
+    if (values->type.type == PS_TYPE_F32) {
+        inF32 = psMemIncrRefCounter((psPtr)values);
+    } else {
+        inF32 = psVectorCopy(NULL, values, PS_TYPE_F32);
+    }
+    psVector* errorsF32 = NULL;         // F32 version of errors vector
+    if (errors) {
+        if (errors->type.type == PS_TYPE_F32) {
+            errorsF32 = psMemIncrRefCounter((psPtr)errors);
+        } else {
+            errorsF32 = psVectorCopy(NULL, errors, PS_TYPE_F32);
+        }
+    }
+
+    for (long i = 0; i < inF32->n; i++) {
+        // Check if this pixel is masked, and if so, skip it.
+        if (!mask || (mask && (!(mask->data.U8[i] & maskVal)))) {
+            if (inF32->data.F32[i] < out->bounds->data.F32[0]) {
+                // If this pixel is below minimum value, count it, then skip.
+                out->minNum++;
+            } else if (inF32->data.F32[i] > out->bounds->data.F32[numBins]) {
+                // If this pixel is above maximum value, count it, then skip.
+                out->maxNum++;
+            } else {
+                // If this is a uniform histogram, determining the correct bin
+                // number is almost trivial.  start with a guess from the uniform scale.
+                if (out->uniform == true) {
+                    double binSize = (out->bounds->data.F32[out->nums->n] - out->bounds->data.F32[0]) / (float) out->nums->n; // Histogram bin size
+                    binNum = (inF32->data.F32[i] - out->bounds->data.F32[0]) / binSize;
+		    binNum = PS_MAX (binNum, 0);
+		    binNum = PS_MIN (binNum, numBins - 1);
+
+		    // value is in bin 'i' if bound[i] <= value < bound[i]
+
+		    // we may slightly overshoot or undershoot.  creep up or down on the true bin
+		    if (inF32->data.F32[i] < out->bounds->data.F32[binNum]) {
+			psTrace("psLib.math", 6, "missed target bin, adjusting: %f vs %f to %f\n", inF32->data.F32[i], out->bounds->data.F32[binNum], out->bounds->data.F32[binNum+1]);
+			while ((inF32->data.F32[i] < out->bounds->data.F32[binNum]) && (binNum > 0)) {
+			    binNum --;
+			}
+			
+		    }
+		    if (inF32->data.F32[i] >= out->bounds->data.F32[binNum+1]) {
+			psTrace("psLib.math", 6, "missed target bin, adjusting: %f vs %f to %f\n", inF32->data.F32[i], out->bounds->data.F32[binNum], out->bounds->data.F32[binNum+1]);
+			while ((inF32->data.F32[i] >= out->bounds->data.F32[binNum+1]) && (binNum < numBins - 1)) {
+			    binNum ++;
+			}
+		    }
+
+                    if (errorsF32) {
+                        if (!UpdateHistogramBins(binNum, out, inF32->data.F32[i], errorsF32->data.F32[i])) {
+                            psLogMsg(__func__, PS_LOG_WARN, "WARNING: Failed to update the histogram "
+                                     "bins with the errors vector.\n");
+                        }
+                    } else {
+                        // This if-statement really shouldn't be necessary.
+                        // However, due to numerical lack of precision, we
+                        // occasionally produce a binNum outside the range.
+                        if (binNum >= out->nums->n) {
+                            binNum = out->nums->n - 1;
+                        }
+                        (out->nums->data.F32[binNum])+= 1.0;
+                    }
+
+                } else {
+                    // If this is a non-uniform histogram, determining the
+                    // correct bin number requires a bit more work.
+                    tmpScalar.data.F32 = inF32->data.F32[i];
+		    psVectorBinaryDisectResult result;
+                    binNum = psVectorBinaryDisect(&result, out->bounds, &tmpScalar);
+		    if (result != PS_BINARY_DISECT_PASS) {
+			continue;
+		    }
+		    if (errorsF32 != NULL) {
+			if (!UpdateHistogramBins(binNum, out, inF32->data.F32[i], errors->data.F32[i])) {
+			    psLogMsg(__func__, PS_LOG_WARN, "WARNING: Failed to update the histogram "
+				     "bins with the errors vector.\n");
+			}
+		    } else {
+			out->nums->data.F32[binNum] += 1.0;
+		    }
+                }
+            }
+        }
+    }
+
+    psFree(inF32);
+    psFree(errorsF32);
+
+    psTrace("psLib.math", 3, "---- %s() end  ----\n", __func__);
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psHistogram.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psHistogram.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psHistogram.h	(revision 22322)
@@ -0,0 +1,94 @@
+/* @file  psHistogram.h
+ * @brief basic histogram functions
+ *
+ * This file holds the definition of the histogram data structures.  It also contains
+ * prototypes for procedures which operate on those data structures.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-08-09 01:40:07 $
+ *
+ * Copyright 2004-2005 IfA, University of Hawaii
+ */
+
+#ifndef PS_HISTOGRAM_H
+#define PS_HISTOGRAM_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/******************************************************************************
+    Histogram functions and data structures.
+ *****************************************************************************/
+
+/** The basic histogram structure which contains bounds and bins.
+ *
+ *  In this structure, the vector bounds specifies the boundaries of the
+ *  histogram bins, and must of type psF32, while nums specifies the number
+ *  of entries in the bin, and must of type psU32. The value of bounds.n must
+ *  therefore be 1 greater than than nums.n. The two values minNum and maxNum
+ *  are the number of data values which fell below the lower limit bound or
+ *  above the upper limit bound, respectively.
+ */
+typedef struct
+{
+    const psVector* bounds;            ///< Bounds for the bins (type F32)
+    psVector* nums;                    ///< Number in each of the bins (INT)
+    int minNum;                        ///< Number below the minimum
+    int maxNum;                        ///< Number above the maximum
+    bool uniform;                      ///< Is it a uniform distribution?
+}
+psHistogram;
+
+/** Allocator for psHistogram where the bounds of the bins are implicitly
+ *  specified through simply specifying an upper and lower limit along with
+ *  the size of the bins.
+ *
+ *  @return psHistogram*    Newly allocated psHistogram
+ */
+psHistogram* psHistogramAlloc(
+    float lower,                       ///< Lower limit for the bins
+    float upper,                       ///< Upper limit for the bins
+    int n                              ///< Number of bins
+) PS_ATTR_MALLOC;
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psHistogram structure, false otherwise.
+ */
+bool psMemCheckHistogram(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Allocator for psHistogram where the bounds of the bins are explicitly
+ *  specified.
+ *
+ *  @return psHistogram*    Newly allocated psHistogram
+ */
+psHistogram* psHistogramAllocGeneric(
+    const psVector* bounds             ///< Bounds for the bins
+);
+
+/** Calculate a histogram
+ *
+ *  The following function populates the histogram bins from the specified
+ *  vector (in). It alters and returns the histogram out structure. The input
+ *  vector may be of types psU8, psU16, psF32, psF64.
+ *
+ *  @return bool   Successful operation?
+ */
+bool psVectorHistogram(
+    psHistogram* out,                  ///< Histogram data
+    const psVector* values,            ///< Vector to analyse
+    const psVector* errors,            ///< Errors
+    const psVector* mask,              ///< Mask dat for input vector
+    psMaskType maskVal                 ///< Mask value
+);
+
+/// @}
+#endif // #ifndef PS_HISTOGRAM_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMD5.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMD5.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMD5.c	(revision 22322)
@@ -0,0 +1,78 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "md5.h"
+
+#include "psAssert.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psVector.h"
+#include "psImage.h"
+#include "psError.h"
+#include "psMD5.h"
+
+
+#define MD5_DIGEST_LENGTH 16            // Length of an MD5 digest, in bytes
+
+psVector *psStringMD5(const char *string)
+{
+    psVector *hash = psVectorAlloc(MD5_DIGEST_LENGTH, PS_TYPE_U8); // The resultant MD5 hash
+    md5_state_t buffer;                 // Calculation buffer
+    md5_init(&buffer);
+    md5_append(&buffer, (psU8*)string, strlen(string));
+    md5_finish(&buffer, &hash->data.U8[0]);
+    return hash;
+}
+
+psVector *psVectorMD5(const psVector *vector)
+{
+    PS_ASSERT_VECTOR_NON_NULL(vector, NULL);
+
+    psVector *hash = psVectorAlloc(MD5_DIGEST_LENGTH, PS_TYPE_U8); // The resultant MD5 hash
+    md5_state_t buffer;                 // Calculation buffer
+    md5_init(&buffer);
+    md5_append(&buffer, vector->data.U8, vector->n * PSELEMTYPE_SIZEOF(vector->type.type));
+    md5_finish(&buffer, &hash->data.U8[0]);
+    return hash;
+}
+
+
+psVector *psImageMD5(const psImage *image)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+
+    psVector *hash = psVectorAlloc(MD5_DIGEST_LENGTH, PS_TYPE_U8); // The resultant MD5 hash
+    md5_state_t buffer;                 // Calculation buffer
+    md5_init(&buffer);
+    if (!image->parent) {
+        // No parent means image is contiguous
+        md5_append(&buffer, image->data.U8[0],
+                   image->numCols * image->numRows * PSELEMTYPE_SIZEOF(image->type.type));
+    } else {
+        for (int row = 0; row < image->numRows; row++) {
+            md5_append(&buffer, image->data.U8[row],
+                       image->numCols * PSELEMTYPE_SIZEOF(image->type.type));
+        }
+    }
+    md5_finish(&buffer, &hash->data.U8[0]);
+
+    return hash;
+}
+
+
+psString psMD5toString(const psVector *hash)
+{
+    PS_ASSERT_VECTOR_NON_NULL(hash, NULL);
+    PS_ASSERT_VECTOR_SIZE(hash, (long int)MD5_DIGEST_LENGTH, NULL);
+    PS_ASSERT_VECTOR_TYPE(hash, PS_TYPE_U8, NULL);
+
+    psString string = psStringAlloc(MD5_DIGEST_LENGTH * 2 + 1); // String to return
+    for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
+        sprintf(string + i * 2, "%02x", hash->data.U8[i]);
+    }
+    return string;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMD5.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMD5.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMD5.h	(revision 22322)
@@ -0,0 +1,42 @@
+/* @file  psMD5.h
+ * @brief support for MD5 hashes
+ *
+ * $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * $Date: 2007-01-23 22:47:23 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_MD5_H
+#define PS_MD5_h
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include "psVector.h"
+#include "psString.h"
+#include "psImage.h"
+
+/// Return an MD5 hash of the supplied string.
+///
+/// The MD5 hash is returned in a U8 vector of size 16.
+psVector *psStringMD5(const char *string   ///< String to hash
+                     );
+
+/// Return an MD5 hash of the supplied vector.
+///
+/// The MD5 hash is returned in a U8 vector of size 16.
+psVector *psVectorMD5(const psVector *vector ///< Vector to hash
+                     );
+
+/// Return an MD5 hash of the supplied image.
+///
+/// The MD5 hash is returned in a U8 vector of size 16.
+psVector *psImageMD5(const psImage *image ///< Image to hash
+                    );
+
+/// Convert an MD5 hash into a string, for printing.
+psString psMD5toString(const psVector *hash ///< Hash to stringify
+                      );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMathUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMathUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMathUtils.c	(revision 22322)
@@ -0,0 +1,316 @@
+/** @file psMathUtils.c
+ *
+ *  This file contains standard math routines.
+ *
+ *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-05-07 23:11:58 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/*****************************************************************************/
+/*  INCLUDE FILES                                                            */
+/*****************************************************************************/
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+#include "psMemory.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psPolynomial.h"
+#include "psMathUtils.h"
+#include "psAssert.h"
+#include "psConstants.h"
+#include "psAbort.h"
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+/*****************************************************************************
+This is a macro covering the various types for the below function.
+ *****************************************************************************/
+#define VECTOR_BINARY_DISECT_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+    ps##TYPE *bounds = bins->data.TYPE; \
+    ps##TYPE value = x->data.TYPE; \
+    long min; \
+    long max; \
+    long mid; \
+    psTrace("psLib.math", 5, "---- () begin ----\n"); \
+    psTrace("psLib.math", 4, "Determining the bin for: %f\n", (float) value); \
+    if (value < bounds[0]) { \
+        psTrace("psLib.math", 3, \
+                 "psVectorBinaryDisect : ordinate %f is outside vector range (%f - %f).", \
+                 (double)value, (double)bounds[0], (double)bounds[numBins-1]); \
+        *status = PS_BINARY_DISECT_OUTSIDE_RANGE; \
+        return (0); \
+    } \
+    if (value > bounds[numBins-1]) { \
+        psTrace("psLib.math", 3, \
+                 "psVectorBinaryDisect : ordinate %f is outside vector range (%f - %f).", \
+                 (double)value, (double)bounds[0], (double)bounds[numBins-1]); \
+        *status = PS_BINARY_DISECT_OUTSIDE_RANGE; \
+        return (numBins-1); \
+    } \
+    \
+    min = 0; \
+    max = numBins-2; \
+    mid = ((max+1)-min)/2; \
+    while (min != max) { \
+        \
+        if (value == bounds[mid]) { \
+            psTrace("psLib.math", 4, "found %ld\n", mid); \
+            psTrace("psLib.math", 5, "---- %s(%ld) end (1) ----\n", __func__, mid); \
+            return(mid); \
+        } else if (value < bounds[mid]) { \
+            max = mid-1; \
+        } else { \
+            min = mid; \
+        } \
+        mid = ((max+1)+min)/2; \
+    } \
+    psTrace("psLib.math", 5, "---- %s(%ld) end (2) ----\n", __func__, min); \
+    return(min); \
+}
+
+/*****************************************************************************
+psVectorBinDisect(): This function takes as input an array of data as well as a single value for that data.
+The input vector values are assumed to be non-decreasing (v[i-1] <= v[i] for all i).  This routine does a
+binary disection of the vector and returns "i" such that (v[i] <= x < v[i+1).  If x lies outside the range of
+v[], then this routine prints a warning message and returns (-2 or -1).
+  *****************************************************************************/
+psS32 psVectorBinaryDisect(
+    psVectorBinaryDisectResult *status,
+    const psVector *bins,
+    const psScalar *x)
+{
+    assert (status);
+    PS_ASSERT_GENERAL_VECTOR_NON_NULL (bins, *status = PS_BINARY_DISECT_INVALID_INPUT; return 0);
+    PS_ASSERT_GENERAL_VECTOR_NON_EMPTY(bins, *status = PS_BINARY_DISECT_INVALID_INPUT; return 0);
+    PS_ASSERT_GENERAL_PTR_NON_NULL(x, *status = PS_BINARY_DISECT_INVALID_INPUT; return 0);
+    PS_ASSERT_GENERAL_PTR_TYPE_EQUAL(x, bins, *status = PS_BINARY_DISECT_INVALID_INPUT; return 0);
+    long numBins = bins->n;             // Number of bins
+
+    *status = PS_BINARY_DISECT_PASS;
+
+    switch (x->type.type) {
+        VECTOR_BINARY_DISECT_CASE(S8);
+        VECTOR_BINARY_DISECT_CASE(S16);
+        VECTOR_BINARY_DISECT_CASE(S32);
+        VECTOR_BINARY_DISECT_CASE(S64);
+        VECTOR_BINARY_DISECT_CASE(U8);
+        VECTOR_BINARY_DISECT_CASE(U16);
+        VECTOR_BINARY_DISECT_CASE(U32);
+        VECTOR_BINARY_DISECT_CASE(U64);
+	VECTOR_BINARY_DISECT_CASE(F32);
+        VECTOR_BINARY_DISECT_CASE(F64);
+    default: {
+            char* strType;
+            PS_TYPE_NAME(strType, x->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, "psLib type %s is not supported.", strType);
+	    *status = PS_BINARY_DISECT_INVALID_TYPE;
+            return 0;
+        }
+    }
+    psAbort ("programming error");
+    return (0);
+}
+
+
+/*************************************************************************************************************
+Helper macro for p_psVectorInterpolate: handles each of the types
+*************************************************************************************************************/
+#define VECTOR_INTERPOLATE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+    psTrace("psLib.math", 4, "---- %s() begin %u-order.) (%d data points) ----\n", __func__, order, order+1); \
+    if (x->data.TYPE < domain->data.TYPE[0]) { \
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: x is outside the domain of input data.\n"); \
+        out->data.TYPE = range->data.TYPE[0]; \
+        return(out); \
+    } \
+    if (x->data.TYPE > domain->data.TYPE[domain->n-1]) { \
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: x is outside the domain of input data.\n"); \
+        out->data.TYPE = range->data.TYPE[domain->n-1]; \
+        return(out); \
+    } \
+    psVector *p = psVectorCopy(NULL, range, PS_TYPE_##TYPE); \
+    psVectorBinaryDisectResult result; \
+    psS32 binNum = psVectorBinaryDisect(&result, domain, x); \
+    \
+    psS32 numIntPoints = order+1; \
+    psS32 origin; \
+    if (0 == numIntPoints%2) { \
+        origin = binNum - ((numIntPoints/2) - 1); \
+    } else { \
+        origin = binNum - (numIntPoints/2); \
+        if ((x->data.TYPE-domain->data.TYPE[binNum]) > (domain->data.TYPE[binNum+1]-x->data.TYPE)) { \
+            /* x is closer to binNum+1. */\
+            origin = 1 + (binNum - (numIntPoints/2)); \
+        } \
+    } \
+    origin = PS_MAX(origin, 0); \
+    origin = PS_MIN(origin, (domain->n - numIntPoints)); \
+    \
+    /* From NR, during each iteration of the m loop, we are computing the p_{i ... i+m} terms. */ \
+    for (psU32 m = 1 ; m < numIntPoints ; m++) { \
+        for (psU32 i = origin ; i < (numIntPoints+origin-m) ; i++) { \
+            p->data.TYPE[i] = (((x->data.TYPE - domain->data.TYPE[i+m]) * p->data.TYPE[i]) + \
+                               ((domain->data.TYPE[i] - x->data.TYPE) * p->data.TYPE[i+1])) / \
+                              (domain->data.TYPE[i] - domain->data.TYPE[i+m]); \
+        } \
+    } \
+    out->data.TYPE = p->data.TYPE[origin]; \
+    psFree(p); \
+    psTrace("psLib.math", 4, "---- %s(....) end ----\n", __func__); \
+    return(out); \
+}
+
+/*****************************************************************************
+p_psVectorInterpolate(): This routine will take as input psVectors domain and
+range, and the x value, assumed to lie with the domain vector.  It produces
+as output the LaGrange interpolated value of a polynomial of the specified
+order around the point x.
+ 
+XXX: This stuff does not currently work with a mask.
+ *****************************************************************************/
+psScalar *p_psVectorInterpolate(
+    psScalar *out,
+    const psVector *domain,
+    const psVector *range,
+    psS32 order,
+    const psScalar *x)
+{
+    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_VECTOR_NON_NULL(domain, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(range, NULL);
+    PS_ASSERT_PTR_NON_NULL(x, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(order, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(domain, range, NULL);
+    PS_ASSERT_PTR_TYPE_EQUAL(domain, range, NULL);
+    PS_ASSERT_PTR_TYPE_EQUAL(domain, x, NULL);
+    if (!out) {
+        out = psScalarAlloc(0, x->type.type);
+    } else {
+        PS_ASSERT_PTR_TYPE_EQUAL(domain, out, NULL);
+    }
+
+    switch (x->type.type) {
+        VECTOR_INTERPOLATE_CASE(U8);
+        VECTOR_INTERPOLATE_CASE(U16);
+        VECTOR_INTERPOLATE_CASE(U32);
+        VECTOR_INTERPOLATE_CASE(U64);
+        VECTOR_INTERPOLATE_CASE(S8);
+        VECTOR_INTERPOLATE_CASE(S16);
+        VECTOR_INTERPOLATE_CASE(S32);
+        VECTOR_INTERPOLATE_CASE(S64);
+        VECTOR_INTERPOLATE_CASE(F32);
+        VECTOR_INTERPOLATE_CASE(F64);
+    default: {
+            char* strType;
+            PS_TYPE_NAME(strType, x->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, "psLib type %s is not supported.", strType);
+            return NULL;
+        }
+    }
+
+    return NULL;
+}
+
+/*****************************************************************************
+Helper macro for p_psNormalizeVectorRange: handles each of the types
+ *****************************************************************************/
+#define NORMALIZE_VECTOR_RANGE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+    ps##TYPE low = outLow; \
+    ps##TYPE high = outHigh; \
+    ps##TYPE min = (ps##TYPE)PS_MAX_##TYPE; \
+    ps##TYPE max = (ps##TYPE)-PS_MAX_##TYPE; \
+    \
+    for (long i = 0; i < myData->n; i++) { \
+        if (myData->data.TYPE[i] < min) { \
+            min = myData->data.TYPE[i]; \
+        } \
+        if (myData->data.TYPE[i] > max) { \
+            max = myData->data.TYPE[i]; \
+        } \
+    } \
+    \
+    /* Ensure that max!=min before we divide by (max-min) */ \
+    if (max != min) { \
+        for (long i = 0; i < myData->n; i++) { \
+            myData->data.TYPE[i] = (low + (myData->data.TYPE[i] - min) * \
+                                    (high - low) / (max - min)); \
+        } \
+    } else { \
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: (max==min).  Setting all elements to min.\n"); \
+        for (long i = 0; i < myData->n; i++) \
+        { \
+            \
+            myData->data.TYPE[i] = low; \
+            \
+        } \
+    } \
+    break; \
+} \
+
+/*************************************************************************************************************
+p_psNormalizeVectorRange(myData, low, high): this function normalises the vector (myData) to the range
+low:
+high.
+*************************************************************************************************************/
+bool p_psNormalizeVectorRange(psVector* myData,
+                              psF64 outLow,
+                              psF64 outHigh)
+{
+    PS_ASSERT_VECTOR_NON_NULL(myData, false);
+    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
+
+    switch (myData->type.type) {
+        NORMALIZE_VECTOR_RANGE_CASE(U8);
+        NORMALIZE_VECTOR_RANGE_CASE(U16);
+        NORMALIZE_VECTOR_RANGE_CASE(U32);
+        NORMALIZE_VECTOR_RANGE_CASE(U64);
+        NORMALIZE_VECTOR_RANGE_CASE(S8);
+        NORMALIZE_VECTOR_RANGE_CASE(S16);
+        NORMALIZE_VECTOR_RANGE_CASE(S32);
+        NORMALIZE_VECTOR_RANGE_CASE(S64);
+        NORMALIZE_VECTOR_RANGE_CASE(F32);
+        NORMALIZE_VECTOR_RANGE_CASE(F64);
+    default: {
+            char* strType;
+            PS_TYPE_NAME(strType, myData->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, "psLib type %s is not supported.", strType);
+            break;
+        }
+    }
+    psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
+    return true;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMathUtils.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMathUtils.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMathUtils.h	(revision 22322)
@@ -0,0 +1,63 @@
+/* @file psMathUtils.h
+ * @brief Standard Mathematical Functions.
+ *
+ * This file contains standard math rotines.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-14 02:36:28 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_MATHUTILS_H
+#define PS_MATHUTILS_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+#include "psVector.h"
+#include "psScalar.h"
+#include "psPolynomial.h"
+
+typedef enum {
+    PS_BINARY_DISECT_PASS,
+    PS_BINARY_DISECT_OUTSIDE_RANGE,
+    PS_BINARY_DISECT_INVALID_INPUT,
+    PS_BINARY_DISECT_INVALID_TYPE,
+} psVectorBinaryDisectResult;
+
+/** Performs a binary disection on a monotonically non-decreasing vector.
+ *  Searches through an array of data for a specified value.
+ *
+ *  @return psS32    corresponding index number of specified value
+ */
+psS32 psVectorBinaryDisect(
+    psVectorBinaryDisectResult *status,
+    const psVector *bins,               ///< Array of non-decreasing values
+    const psScalar *x                   ///< Target value to find
+);
+
+/** Interpolates a series of data points for evaluation at a specific coordinate.  Uses a
+ *  Lagrange interpolation method.
+ *
+ *  @return psScalar*    Lagrange interpolation value at given location
+ */
+psScalar *p_psVectorInterpolate(
+    psScalar *out,                      ///< Output scalar, or NULL
+    const psVector *domain,             ///< Domain (x coords) for interpolation
+    const psVector *range,              ///< Range (y coords) for interpolation
+    psS32 order,                        ///< Order of interpolation function
+    const psScalar *x                   ///< Location at which to evaluate
+);
+
+bool p_psNormalizeVectorRange(psVector* myData,
+                              psF64 outLow,
+                              psF64 outHigh);
+
+/// @}
+#endif // #ifndef PS_MATHUTILS_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMatrix.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMatrix.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMatrix.c	(revision 22322)
@@ -0,0 +1,785 @@
+/** @file  psMatrix.c
+ *
+ *  @brief Provides functions for linear algebra operations on psImages and psVectors.
+ *
+ *  Functions are provided to:
+ *      Transpose a psImage
+ *      Compute LUD
+ *      Solve LUD
+ *      Matrix inversion
+ *      Calculate determinant
+ *      Matrix addition
+ *      Matrix subtraction
+ *      Matrix multiplication
+ *      Calculate Eigenvectors
+ *      Convert matrix to vector
+ *      Convert vector to matrix
+ *
+ *  These functions treat psImages as if they were matrices, therefore there is no psMatrix.
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *  @author Andy Becker, University of Washington (SVD).
+ *
+ *  @version $Revision: 1.57 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-05-07 23:10:46 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+// XXX Future optimisation: use GSL type appropriate to the psLib type, and use memcpy instead of copying each
+// value individually.
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <string.h>
+#include <gsl/gsl_matrix.h>
+#include <gsl/gsl_linalg.h>
+#include <gsl/gsl_blas.h>
+#include <gsl/gsl_permutation.h>
+#include <gsl/gsl_eigen.h>
+
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psMatrix.h"
+#include "psAssert.h"
+
+#include "psTrace.h"
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/** Preprocessor macro to generate error for image dimensionality not set to PS_DIMEN_IMAGE */
+#define PS_CHECK_DIMEN_AND_TYPE(NAME, PS_DIMEN, CLEANUP)                                             \
+if (NAME->type.dimen != PS_DIMEN) {                                                                 \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                        \
+            "Invalid operation. %s has incorrect dimensionality %d.", #NAME, PS_DIMEN);             \
+    CLEANUP;                                                                                  \
+} else if(NAME->type.type!=PS_TYPE_F64 && NAME->type.type!=PS_TYPE_F32) {                           \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                        \
+            "Invalid operation. %s not PS_TYPE_F64.", #NAME);                                       \
+    CLEANUP;                                                                                  \
+}
+
+/** Preprocessor macro to check that input is not equal to output */
+#define PS_CHECK_POINTERS(NAME1, NAME2, CLEANUP)                                                     \
+if (NAME1 == NAME2) {                                                                               \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                       \
+            "Invalid operation: Pointer to %s is same as %s.", #NAME1, #NAME2);                     \
+    CLEANUP;                                                                                  \
+}
+
+/** Preprocessor macro to check that an image is square */
+#define PS_CHECK_SQUARE(NAME, CLEANUP)                                                               \
+if (NAME->numCols != NAME->numRows) {                                                               \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid operation: %s not square array.", #NAME);     \
+    CLEANUP;                                                                                  \
+}
+
+/** Preprocessor macro to initalize a GSL matrix. */
+#define PS_GSL_MATRIX_INITIALIZE(LHS_NAME, RHS_NAME)                                                \
+LHS_NAME.size1 = numRows;                                                                           \
+LHS_NAME.size2 = numCols;                                                                           \
+LHS_NAME.tda   = numCols;                                                                           \
+LHS_NAME.data  = RHS_NAME;
+
+
+/*****************************************************************************/
+/* FILE STATIC FUNCTIONS                                                     */
+/*****************************************************************************/
+
+static void  psVectorToGslVector(gsl_vector *outGslVector, const psVector *inVector);
+static void gslVectorToPsVector(psVector *outVector, gsl_vector *inGslVector);
+static void  psImageToGslMatrix(gsl_matrix *outGslMatrix, const psImage *inImage);
+static void gslMatrixToPsImage(psImage *outImage, gsl_matrix *inGslMatrix);
+
+/** Static function to copy psF32 or psF64 vector data to a GSL vector */
+static void  psVectorToGslVector(gsl_vector *outGslVector,
+                                 const psVector *inVector)
+{
+    psU32 i = 0;
+    psU32 n = 0;
+
+
+    n = inVector->n;
+    for(i=0; i<n; i++) {
+        if(inVector->type.type == PS_TYPE_F32) {
+            outGslVector->data[i] = (psF64)inVector->data.F32[i];
+        } else {
+            outGslVector->data[i] = inVector->data.F64[i];
+        }
+    }
+}
+
+/** Static function to copy GSL vector data to a psF32 or psF64 vector */
+static void gslVectorToPsVector(psVector *outVector,
+                                gsl_vector *inGslVector)
+{
+    psU32 i = 0;
+    psU32 n = 0;
+
+
+    n = outVector->n;
+    for(i=0; i<n; i++) {
+        if(outVector->type.type == PS_TYPE_F32) {
+            outVector->data.F32[i] = (psF32)inGslVector->data[i];
+        } else {
+            outVector->data.F64[i] = inGslVector->data[i];
+        }
+    }
+}
+
+/** Static function to copy psF32 or psF64 image data to a GSL matrix */
+static void  psImageToGslMatrix(gsl_matrix *outGslMatrix,
+                                const psImage *inImage)
+{
+    psU32 i = 0;
+    psU32 j = 0;
+    psU32 numRows = 0;
+    psU32 numCols = 0;
+
+
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+    if(inImage->type.type == PS_TYPE_F32) {
+        for(i=0; i<numRows; i++) {
+            for(j=0; j<numCols; j++) {
+                outGslMatrix->data[i*numCols+j] = inImage->data.F32[i][j];
+            }
+        }
+    } else {
+        for(i=0; i<numRows; i++) {
+            for(j=0; j<numCols; j++) {
+                outGslMatrix->data[i*numCols+j] = inImage->data.F64[i][j];
+            }
+        }
+    }
+}
+
+/** Static function to copy GSL matrix data to a psF32 or psF64 image */
+static void gslMatrixToPsImage(psImage *outImage,
+                               gsl_matrix *inGslMatrix)
+{
+    psU32 i = 0;
+    psU32 j = 0;
+    psU32 numRows = 0;
+    psU32 numCols = 0;
+
+
+    numRows = outImage->numRows;
+    numCols = outImage->numCols;
+    if(outImage->type.type == PS_TYPE_F32) {
+        for(i=0; i<numRows; i++) {
+            for(j=0; j<numCols; j++) {
+                outImage->data.F32[i][j] = inGslMatrix->data[i*numCols+j];
+            }
+        }
+    } else {
+        for(i=0; i<numRows; i++) {
+            for(j=0; j<numCols; j++) {
+                outImage->data.F64[i][j] = inGslMatrix->data[i*numCols+j];
+            }
+        }
+    }
+}
+
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+psImage* psMatrixLUD(psImage* out,
+                     psVector** perm,
+                     const psImage* in)
+{
+    psS32 signum = 0;
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_matrix *lu = NULL;
+    gsl_permutation permGSL;
+
+
+    #define psMatrixLUD_EXIT {psFree(out); return NULL;}
+
+    // Error checks
+    PS_ASSERT_GENERAL_IMAGE_NON_NULL(in, psMatrixLUD_EXIT);
+    PS_CHECK_POINTERS(in, out, psMatrixLUD_EXIT);
+    PS_CHECK_DIMEN_AND_TYPE(in, PS_DIMEN_IMAGE, psMatrixLUD_EXIT);
+    PS_ASSERT_GENERAL_PTR_NON_NULL(perm, psMatrixLUD_EXIT);
+
+    out = psImageRecycle(out, in->numCols, in->numRows, in->type.type);
+
+    PS_CHECK_SQUARE(in, psMatrixLUD_EXIT); // gsl_linalg_LU_decomp would fail on non-square input.
+    PS_CHECK_SQUARE(out, psMatrixLUD_EXIT);
+
+    // Initialize data
+    numRows = in->numRows;
+    numCols = in->numCols;
+
+    // Initialize GSL data
+    permGSL.size = numCols;
+    if (sizeof(size_t) == 4) {
+        *perm = psVectorRecycle(*perm, numCols, PS_TYPE_S32);
+    } else if (sizeof(size_t) == 8) {
+        *perm = psVectorRecycle(*perm, numCols, PS_TYPE_S64);
+    } else {
+        psError(PS_ERR_UNKNOWN, true,
+                "Failed to allocate the permutation vector; "
+                "could not determine the cooresponding data type.");
+        psMatrixLUD_EXIT;
+    }
+
+    (*perm)->n = numCols;
+    permGSL.data = (psPtr)((*perm)->data.U8);
+    lu = gsl_matrix_alloc(numRows, numCols);
+
+    // Copy psImage data into GSL matrix data
+    psImageToGslMatrix(lu, in);
+
+    // Calculate LU decomposition
+    gsl_linalg_LU_decomp(lu, &permGSL, &signum); // N.B., uses Gaussian Elimination with partial pivoting.
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(out, lu);
+
+    // Free GSL data
+    gsl_matrix_free(lu);
+
+    return out;
+}
+
+psVector* psMatrixLUSolve(psVector* out,
+                          const psImage* LU,
+                          const psVector* RHS,
+                          const psVector* perm)
+{
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_matrix *lu;
+    gsl_permutation permGSL;
+    gsl_vector *b = NULL;
+    gsl_vector *x = NULL;
+
+    #define LUSOLVE_CLEANUP {psFree(out); return NULL;}
+
+    // Error checks
+    PS_ASSERT_GENERAL_IMAGE_NON_NULL(LU, LUSOLVE_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(LU, PS_DIMEN_IMAGE, LUSOLVE_CLEANUP);
+    PS_ASSERT_GENERAL_IMAGE_NON_EMPTY(LU, LUSOLVE_CLEANUP);
+    PS_ASSERT_GENERAL_VECTOR_NON_NULL(RHS, LUSOLVE_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(RHS, PS_DIMEN_VECTOR, LUSOLVE_CLEANUP);
+    PS_ASSERT_GENERAL_VECTOR_NON_NULL(perm, LUSOLVE_CLEANUP);
+
+    out = psVectorRecycle(out, LU->numRows, LU->type.type);
+
+    PS_CHECK_POINTERS(out, RHS, LUSOLVE_CLEANUP);
+    PS_CHECK_POINTERS(RHS, perm, LUSOLVE_CLEANUP);
+    PS_CHECK_POINTERS(out, perm, LUSOLVE_CLEANUP);
+
+    // Initialize data
+    numRows = LU->numRows;
+    numCols = LU->numCols;
+
+    // Initialize GSL data
+    lu = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(lu, LU);
+    b = gsl_vector_alloc(RHS->n);
+    psVectorToGslVector(b, RHS);
+    x = gsl_vector_alloc(RHS->n);
+
+    out->n = numCols;
+    permGSL.size = perm->n;
+    permGSL.data = (psPtr)(perm->data.U8);
+
+    // Solve for {x} in equation: {b} = [A]{x}
+    gsl_linalg_LU_solve(lu, &permGSL, b, x);
+
+    // Copy GSL vector data to psVector data
+    gslVectorToPsVector(out, x);
+
+    // Free GSL data
+    gsl_vector_free(b);
+    gsl_vector_free(x);
+    gsl_matrix_free(lu);
+
+    return out;
+}
+
+// This used to be "a temporary gauss-jordan solver based on gene's version based on the Numerical Recipes
+// version".  However, it's been removed due to copyright, and replaced with LU Decomposition solving.
+bool psMatrixGJSolve(psImage *a,
+                     psVector *b
+                    )
+{
+    PS_ASSERT_IMAGE_NON_NULL(a, false);
+    PS_ASSERT_VECTOR_NON_NULL(b, false);
+    PS_ASSERT_IMAGE_TYPE_F32_OR_F64(a, false);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(b, false);
+    PS_ASSERT_INT_EQUAL(a->numCols, a->numRows, false);
+    PS_ASSERT_VECTOR_SIZE(b, (long int)a->numCols, false);
+
+    // Check for non-finite entries
+#define MATRIX_CHECK_NONFINITE_CASE(TYPE,MATRIX) \
+  case PS_TYPE_##TYPE: { \
+      ps##TYPE **values = MATRIX->data.TYPE; /* Dereference */ \
+      int numCols = (MATRIX)->numCols, numRows = (MATRIX)->numRows; /* Size of matrix */ \
+      for (int i = 0; i < numRows; i++) { \
+          for (int j = 0; j < numCols; j++) { \
+              if (!isfinite(values[i][j])) { \
+                  psError(PS_ERR_BAD_PARAMETER_VALUE, 3, \
+                          "Input matrix contains non-finite elements: matrix[%d][%d] is %.2f\n", \
+                          i, j, values[i][j]); \
+                  return false; \
+              } \
+          } \
+      } \
+      break; \
+  }
+
+    // Check for non-finite entries in matrix
+    switch (a->type.type) {
+        MATRIX_CHECK_NONFINITE_CASE(F32, a);
+        MATRIX_CHECK_NONFINITE_CASE(F64, a);
+      default:
+        psAbort("Should never get here.");
+    }
+
+    // Decompose the matrix and solve
+    psVector *perm = NULL;              // Permutation vector
+    psImage *lu = psMatrixLUD(NULL, &perm, a); // LU decomposed matrix
+    if (!lu) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate LU decomposed matrix");
+        psFree(perm);
+        return false;
+    }
+    psVector *ans = psMatrixLUSolve(NULL, lu, b, perm); // Answer
+    psFree(lu);
+    psFree(perm);
+    if (!ans) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to solve matrix equation.");
+        return false;
+    }
+
+    memcpy(b->data.U8, ans->data.U8, b->n * PSELEMTYPE_SIZEOF(ans->type.type));
+    psFree(ans);
+
+    return true;
+}
+
+
+psImage* psMatrixInvert(psImage* out,
+                        const psImage* in,
+                        float *determinant)
+{
+    psS32 signum = 0;
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_matrix *inv = NULL;
+    gsl_matrix *lu = NULL;
+    gsl_permutation *perm = NULL;
+
+    #define INVERT_CLEANUP { psFree(out); return NULL; }
+    // Error checks
+    PS_ASSERT_GENERAL_IMAGE_NON_NULL(in, INVERT_CLEANUP);
+    PS_CHECK_POINTERS(in, out, INVERT_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(in, PS_DIMEN_IMAGE, INVERT_CLEANUP);
+    PS_ASSERT_GENERAL_IMAGE_NON_EMPTY(in, INVERT_CLEANUP);
+
+    out = psImageRecycle(out, in->numCols, in->numRows, in->type.type);
+
+    PS_CHECK_SQUARE(in, INVERT_CLEANUP);
+    PS_CHECK_SQUARE(out, INVERT_CLEANUP);
+
+    // Initialize data
+    numRows = in->numRows;
+    numCols = in->numCols;
+
+    // Initialize GSL data
+    perm = gsl_permutation_alloc(numRows);
+    lu = gsl_matrix_alloc(numRows, numCols);
+    inv = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(lu, in);
+
+    // Invert data and calculate determinant
+    gsl_linalg_LU_decomp(lu, perm, &signum);
+    gsl_linalg_LU_invert(lu, perm, inv);
+    if (determinant) {
+      // XXX this is getting the wrong value: is it the wrong calculation?
+      // it disagrees with the results of 
+      // det = (psF32)gsl_linalg_LU_det(lu, signum);
+      // used in psMatrixDeterminatn
+      // *determinant = (float)gsl_linalg_LU_lndet(lu);
+      *determinant = (psF32)gsl_linalg_LU_det(lu, signum);
+    }
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(out, inv);
+
+    // Free GSL structs
+    gsl_permutation_free(perm);
+    gsl_matrix_free(lu);
+    gsl_matrix_free(inv);
+
+    return out;
+}
+
+float psMatrixDeterminant(const psImage* in)
+{
+    psS32 signum = 0;
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    psF32 det = 0;
+    gsl_matrix *lu = NULL;
+    gsl_permutation *perm = NULL;
+
+    #define DETERMINANT_EXIT { return 0; }
+    // Error checks
+    PS_ASSERT_GENERAL_IMAGE_NON_NULL(in, DETERMINANT_EXIT);
+    PS_CHECK_DIMEN_AND_TYPE(in, PS_DIMEN_IMAGE, DETERMINANT_EXIT);
+    PS_ASSERT_GENERAL_IMAGE_NON_EMPTY(in, DETERMINANT_EXIT);
+    PS_CHECK_SQUARE(in, DETERMINANT_EXIT);
+
+    // Initialize data
+    numRows = in->numRows;
+    numCols = in->numCols;
+
+    // Allocate GSL structs
+    perm = gsl_permutation_alloc(numRows);
+    lu = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(lu, in);
+
+    // Calculate determinant
+    gsl_linalg_LU_decomp(lu, perm, &signum);
+    det = (psF32)gsl_linalg_LU_det(lu, signum);
+
+    // Free GSL structs
+    gsl_permutation_free(perm);
+    gsl_matrix_free(lu);
+
+    return det;
+}
+
+psImage* psMatrixMultiply(psImage* out,
+                          const psImage* in1,
+                          const psImage* in2)
+{
+    #define MULTIPLY_CLEANUP { psFree(out); return NULL; }
+
+    // Error checks
+    PS_ASSERT_GENERAL_IMAGE_NON_NULL(in1, MULTIPLY_CLEANUP);
+    PS_ASSERT_GENERAL_IMAGE_NON_NULL(in2, MULTIPLY_CLEANUP);
+    PS_ASSERT_GENERAL_IMAGE_NON_EMPTY(in1, MULTIPLY_CLEANUP);
+    PS_ASSERT_GENERAL_IMAGE_NON_EMPTY(in2, MULTIPLY_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(in1, PS_DIMEN_IMAGE, MULTIPLY_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(in2, PS_DIMEN_IMAGE, MULTIPLY_CLEANUP);
+    PS_CHECK_POINTERS(in1, out, MULTIPLY_CLEANUP);
+    PS_CHECK_POINTERS(in1, in2, MULTIPLY_CLEANUP);
+
+    int rows1 = in1->numRows, cols1 = in1->numCols; // Size of input 1
+    int rows2 = in2->numRows, cols2 = in2->numCols; // Size of input 2
+    if (cols1 != rows2) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Incompatible dimensions for matrix multiplication: %dx%d * %dx%d (row x col)",
+                rows1, cols1, rows2, cols2);
+        MULTIPLY_CLEANUP;
+    }
+    int common = cols1;                 // Common dimension
+
+    if (in1->type.type != in2->type.type) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid operation: data types of in1 and in2 must match.");
+        MULTIPLY_CLEANUP;
+    }
+
+    psElemType type = in1->type.type;   // Data type
+    int outRows = rows1, outCols = cols2; // Size of output
+    out = psImageRecycle(out, outCols, outRows, type);
+
+#define MATRIX_MULTIPLY_CASE(TYPE) \
+  case PS_TYPE_##TYPE: \
+    for (int i = 0; i < outRows; i++) { \
+        for (int j = 0; j < outCols; j++) { \
+            ps##TYPE value = 0.0; \
+            for (int k = 0; k < common; k++) { \
+                value += in1->data.TYPE[i][k] * in2->data.TYPE[k][j]; \
+            } \
+            out->data.TYPE[i][j] = value; \
+        } \
+    } \
+    break;
+
+    switch (type) {
+        MATRIX_MULTIPLY_CASE(F32);
+        MATRIX_MULTIPLY_CASE(F64);
+      default:
+        psAbort("Should never get here.  Unsupported type: %x", type);
+    }
+
+    return out;
+}
+
+psImage* psMatrixTranspose(psImage* out, const psImage* in)
+{
+    // Error checks
+    PS_ASSERT_IMAGE_NON_NULL(in, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(in, NULL);
+    PS_CHECK_DIMEN_AND_TYPE(in, PS_DIMEN_IMAGE, return NULL);
+    PS_ASSERT(in != out, NULL);
+
+    int numCols = in->numRows, numRows = in->numCols; // Size of transposed image
+    psElemType type = in->type.type;    // Data type
+
+    out = psImageRecycle(out, numCols, numRows, type);
+
+#define TRANSPOSE_CASE(TYPE) \
+  case PS_TYPE_##TYPE: { \
+      for (int i = 0; i < numRows; i++) { \
+          for (int j = 0; j < numCols; j++) { \
+              out->data.TYPE[i][j] = in->data.TYPE[j][i]; \
+          } \
+      } \
+      break; \
+  }
+
+    switch (type) {
+        TRANSPOSE_CASE(F32);
+        TRANSPOSE_CASE(F64);
+      default:
+        psAbort("Unsupported type: %x", type);
+    }
+
+    return out;
+}
+
+psImage* psMatrixEigenvectors(psImage* out,
+                              const psImage* in)
+{
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_vector *eVals = NULL;
+    gsl_eigen_symmv_workspace *w = NULL;
+    gsl_matrix *outGSL = NULL;
+    gsl_matrix *inGSL = NULL;
+
+    #define EIGENVECTORS_CLEANUP { psFree(out); return NULL; }
+    // Error checks
+    PS_ASSERT_GENERAL_IMAGE_NON_NULL(in, EIGENVECTORS_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(in, PS_DIMEN_IMAGE, EIGENVECTORS_CLEANUP);
+    PS_ASSERT_GENERAL_IMAGE_NON_EMPTY(in, EIGENVECTORS_CLEANUP);
+    PS_CHECK_POINTERS(in, out, EIGENVECTORS_CLEANUP);
+
+    out = psImageRecycle(out, in->numCols, in->numRows, in->type.type);
+
+    // Initialize data
+    numRows = in->numRows;
+    numCols = in->numCols;
+
+    inGSL = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(inGSL, in);
+    outGSL = gsl_matrix_alloc(numRows, numCols);
+
+    // Allocate GSL structs
+    eVals = gsl_vector_alloc(numRows);
+    w = gsl_eigen_symmv_alloc(numRows);
+
+    // Non-square matrices not allowed
+    PS_CHECK_SQUARE(in, EIGENVECTORS_CLEANUP);
+    PS_CHECK_SQUARE(out, EIGENVECTORS_CLEANUP);
+
+    // Calculate Eigenvalues and Eigenvectors...Eigenvalues not currently used
+    gsl_eigen_symmv(inGSL, eVals, outGSL, w);
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(out, outGSL);
+
+    // Free GSL structs
+    gsl_matrix_free(inGSL);
+    gsl_matrix_free(outGSL);
+    gsl_eigen_symmv_free(w);
+    gsl_vector_free(eVals);
+
+    return out;
+}
+
+psVector* psMatrixToVector(psVector* outVector,
+                           const psImage* inImage)
+{
+    psS32 size = 0;
+
+    #define psMatrixToVector_EXIT {psFree(outVector); return NULL;}
+
+    // Error checks
+    PS_ASSERT_GENERAL_IMAGE_NON_NULL(inImage, psMatrixToVector_EXIT);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, psMatrixToVector_EXIT);
+    PS_ASSERT_GENERAL_IMAGE_NON_EMPTY(inImage, psMatrixToVector_EXIT);
+
+    if (inImage->numRows == 1) {
+        // Create transposed row vector
+        outVector = psVectorRecycle(outVector, inImage->numCols, inImage->type.type);
+        outVector->type.dimen = PS_DIMEN_TRANSV;
+    } else if (inImage->numCols == 1) {
+        // Create non-transposed column vector
+        outVector = psVectorRecycle(outVector, inImage->numRows, inImage->type.type);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Image does not have dim with 1 col or 1 row: (%d x %d).",
+                inImage->numRows, inImage->numCols);
+        psMatrixToVector_EXIT;
+    }
+
+    // More checks
+    if (outVector->type.dimen == PS_DIMEN_VECTOR) {
+        PS_CHECK_DIMEN_AND_TYPE(outVector, PS_DIMEN_VECTOR, psMatrixToVector_EXIT);
+
+        if (outVector->n == 0) {
+            outVector->n = inImage->numRows;
+        }
+
+        if (outVector->n != inImage->numRows) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %ld).",
+                    inImage->numRows, outVector->n);
+            psMatrixToVector_EXIT;
+        }
+
+        size = PSELEMTYPE_SIZEOF(inImage->type.type) * inImage->numRows;
+
+    } else if (outVector->type.dimen == PS_DIMEN_TRANSV) {
+        PS_CHECK_DIMEN_AND_TYPE(outVector, PS_DIMEN_TRANSV, psMatrixToVector_EXIT);
+
+        if (outVector->n == 0) {
+            outVector->n = inImage->numCols;
+        }
+
+        if (outVector->n != inImage->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %ld).",
+                    inImage->numCols, outVector->n);
+            psMatrixToVector_EXIT;
+        }
+
+        size = PSELEMTYPE_SIZEOF(inImage->type.type) * inImage->numCols;
+    }
+
+    memcpy(outVector->data.U8, inImage->data.U8[0], size);
+
+    return outVector;
+}
+
+psImage* psVectorToMatrix(psImage* outImage,
+                          const psVector* inVector)
+{
+    psS32 size = 0;
+
+    #define VECTORTOMATRIX_CLEANUP {psFree(outImage); return NULL; }
+    // Error checks
+    PS_ASSERT_GENERAL_VECTOR_NON_NULL(inVector, VECTORTOMATRIX_CLEANUP);
+
+    if (inVector->type.dimen == PS_DIMEN_VECTOR) {
+        PS_CHECK_DIMEN_AND_TYPE(inVector, PS_DIMEN_VECTOR, VECTORTOMATRIX_CLEANUP);
+        PS_ASSERT_GENERAL_VECTOR_NON_EMPTY(inVector, VECTORTOMATRIX_CLEANUP);
+
+        outImage = psImageRecycle(outImage, 1, inVector->n, inVector->type.type);
+
+        // More checks for PS_DIMEN_VECTOR
+        if (outImage->numCols > 1) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image has more than 1 column: numCols = %d.",
+                    outImage->numCols);
+            VECTORTOMATRIX_CLEANUP;
+        } else if (outImage->numRows != inVector->n) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %ld).",
+                    outImage->numRows, inVector->n);
+            VECTORTOMATRIX_CLEANUP;
+        }
+
+        size = PSELEMTYPE_SIZEOF(outImage->type.type) * outImage->numRows;
+
+    } else if (inVector->type.dimen == PS_DIMEN_TRANSV) {
+        PS_CHECK_DIMEN_AND_TYPE(inVector, PS_DIMEN_TRANSV, VECTORTOMATRIX_CLEANUP);
+        PS_ASSERT_GENERAL_VECTOR_NON_EMPTY(inVector, VECTORTOMATRIX_CLEANUP);
+        outImage = psImageRecycle(outImage, inVector->n, 1, inVector->type.type);
+        // More checks for PS_DIMEN_TRANSV
+        if (outImage->numRows > 1) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image has more than 1 row: numRows = %d.",
+                    outImage->numRows);
+            VECTORTOMATRIX_CLEANUP;
+        } else if (outImage->numCols != inVector->n) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %ld).",
+                    outImage->numCols, inVector->n);
+            VECTORTOMATRIX_CLEANUP;
+        }
+
+        size = PSELEMTYPE_SIZEOF(outImage->type.type) * outImage->numCols;
+    }
+
+    PS_ASSERT_GENERAL_IMAGE_NON_NULL(outImage, VECTORTOMATRIX_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(outImage, PS_DIMEN_IMAGE, VECTORTOMATRIX_CLEANUP);
+
+    memcpy(outImage->data.U8[0], inVector->data.U8, size);
+
+    return outImage;
+}
+
+// This code supplied by Andy Becker (becker@astro.washington.edu)
+psImage *psMatrixSVD(psImage* evec, psVector* eval, const psImage* in)
+{
+    #define psMatrixSVD_EXIT {psFree(evec); psFree(eval); return NULL;}
+
+    // Error checks  Missing one for eval
+    PS_ASSERT_GENERAL_IMAGE_NON_NULL(in, psMatrixSVD_EXIT);
+    PS_CHECK_POINTERS(in, evec, psMatrixSVD_EXIT);
+    PS_CHECK_DIMEN_AND_TYPE(in, PS_DIMEN_IMAGE, psMatrixSVD_EXIT);
+
+    // evec ends up : numCols x numCols
+    evec = psImageRecycle(evec,  in->numCols, in->numCols, in->type.type);
+    eval = psVectorRecycle(eval, in->numCols, in->type.type);
+
+    // Initialize data
+    int numRows = in->numRows;
+    int numCols = in->numCols;
+
+    gsl_matrix *A = gsl_matrix_alloc(numRows, numCols);
+    gsl_matrix *V = gsl_matrix_alloc(numCols, numCols);
+    gsl_vector *S = gsl_vector_alloc(numCols);
+    gsl_vector *work = gsl_vector_alloc(numCols);
+
+    // Copy psImage data into GSL matrix data
+    psImageToGslMatrix(A, in);
+
+    // Calculate SVD decomposition
+    gsl_linalg_SV_decomp(A, V, S, work);
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(evec, V);
+    gslVectorToPsVector(eval, S);
+
+    // Take the square root of eval
+    for (int i = 0; i < eval->n; i++) {
+        eval->data.F64[i] = sqrt(eval->data.F64[i]);
+        /* make sure that these things are sorted! */
+        if (i > 0) {
+            psAssert(eval->data.F64[i] <= eval->data.F64[i-1], "impossible");
+        }
+    }
+
+    // Free GSL data
+    gsl_matrix_free(A);
+    gsl_matrix_free(V);
+    gsl_vector_free(S);
+    gsl_vector_free(work);
+
+    return evec;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMatrix.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMatrix.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMatrix.h	(revision 22322)
@@ -0,0 +1,177 @@
+/* @file  psMatrix.h
+ *
+ * @brief Provides functions for linear algebra operations on psImages and psVectors.
+ *
+ * Functions are provided to:
+ *     Transpose a psImage
+ *     Compute LUD
+ *     Solve LUD
+ *     Matrix inversion
+ *     Calculate determinant
+ *     Matrix multiplication
+ *     Calculate Eigenvectors
+ *     Convert matrix to vector
+ *     Convert vector to matrix
+ *
+ * These functions treat psImages as if they were matrices, therefore there is no psMatrix. These functions
+ * operate only with the psF64 data type.
+ *
+ * @author Ross Harman, MHPCC
+ *
+ * @version $Revision: 1.28 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-12-14 00:41:17 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSMATRIX_H
+#define PSMATRIX_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/** LU Decomposition of psImage matrix.
+ *
+ *  Performs a LU decomposition on a psImage matrix and returns the LU matrix. If the user specifies NULL for
+ *  the outImage or outPerm arguments, then they will be automatically created. The input image must
+ *  be square. This function operates only with the psF64 data type. Input and output arguments should not be
+ *  the same. GSL indexes the top row as the zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to LU decomposed psImage.
+ */
+psImage* psMatrixLUD(
+    psImage* out,                      ///< Image to return, or NULL.
+    psVector** perm,                   ///< Output permutation vector used by psMatrixLUSolve.
+    const psImage* in                  ///< Image to decompose.
+);
+
+/** LU Solution of psImage matrix.
+ *
+ *  Solves for and returns the psVector, {x} in the equation [A]{x} = {b}. If the user specifies NULL as the
+ *  outVector argument, then it will automatically be created. The input image must be square. This function
+ *  operates only with the psF64 data type. Input and output arguments should not be the same. GSL indexes
+ *  the top row as the zero row, not the bottom.
+ *
+ *  @return  psVector* : Pointer to psVector solution of matrix equation.
+ */
+psVector* psMatrixLUSolve(
+    psVector* out,                     ///< Vector to return, or NULL.
+    const psImage* LU,                 ///< LU-decomposed matrix.
+    const psVector* RHS,               ///< Vector right-hand-side of equation.
+    const psVector* perm               ///< Permutation vector resulting from psMatrixLUD function.
+);
+
+/** Used to be a Gauss-Jordan numerical solver, but now uses LU decomposition.
+ *
+ *  @return bool:   True if successful.
+ */
+bool psMatrixGJSolve(
+    psImage *A,                   ///< Matrix to be solved
+    psVector *b                   ///< Vector of values
+);
+
+/** Gauss-Jordan numerical solver for F32 input data */
+#define psMatrixGJSolveF32(A,B) psMatrixGJSolve(A,B)
+
+
+/** Invert psImage matrix.
+ *
+ *  Inverts a psImage matrix and returns the determinant as an option through the argument list. If the user
+ *  specifies NULL as the outImage argument, then it will automatically be created. The input image must be
+ *  square. This function operates only with the psF64 data type. Input and output arguments should not be
+ *  the same. GSL indexes the top row as the zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to inverted psImage.
+ */
+psImage* psMatrixInvert(
+    psImage* out,                      ///< Image to return, or NULL for in-place substitution.
+    const psImage* in,                 ///< Image to be inverted
+    float *determinant                 ///< Determinant to return, or NULL
+);
+
+/** Calculate psImage matrix determinant.
+ *
+ *  Calculates the determinant of a psImage matrix and returns the single precision floating point result. The
+ *  input image must be square. This function operates only with the psF64 data type. GSL indexes the top row
+ *  as the zero row, not the bottom.
+ *
+ *  @return  float: Determinant from psImage.
+ */
+float psMatrixDeterminant(
+    const psImage* in                  ///< Image used to calculate determinant.
+);
+
+/** Performs psImage matrix multiplication.
+ *
+ *  Performs a classical matrix multiplication involving row and column operations. Input images must be square
+ *  and the same size. If the user specifies NULL as the outImage argument, then it will automatically be
+ *  created. This function operates only with the psF64 data type. GSL indexes the top row as the
+ *  zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to resulting psImage.
+ */
+psImage* psMatrixMultiply(
+    psImage* out,                      ///< Matrix to return, or NULL.
+    const psImage* in1,                ///< First input image.
+    const psImage* in2                 ///< Second input image.
+);
+
+/** Transpose matrix.
+ *
+ *  Performs psImage matrix transpose by substituting existing rows for columns. The input image must be
+ *  square. If the user specifies NULL as the outImage argument, then it will automaticallty be created.
+ *  This function operates only with the psF64 data type. GSL indexes the top row as the zero
+ *  row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to transposed psImage.
+ */
+psImage* psMatrixTranspose(
+    psImage* out,                      ///< Image to return, or NULL
+    const psImage* in                  ///< Image to transpose
+);
+
+/** Calculate matrix eigenvectors.
+ *
+ *  Calculates the eigenvectors for a matrix. The input image must be symmetric and square. If the user
+ *  specifies NULL as the outImage argument, then it will automatically be created. This function operates
+ *  only with the psF64 data type. GSL indexes the top row as the zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to matrix of Eigenvectors.
+ */
+psImage* psMatrixEigenvectors(
+    psImage* out,                      ///< Eigenvectors to return, or NULL.
+    const psImage* in                  ///< Input image.
+);
+
+/** Convert matrix to vector.
+ *
+ *  Converts a 1-d psImage matrix into a vector. If the user specifies NULL as the outVector argument, then it
+ *  will automatically be created based on the input image (PS_DIMEN_VECTOR for an input image with 1 col or
+ *  PS_DIMENT_TRANSV for an input image with 1 row). Either the number of rows or the number of colums of the
+ *  input matrix must be 1. This function operates only  with the psF64 data type.
+ *
+ *  @return  psVector* : Pointer to psVector.
+ */
+psVector* psMatrixToVector(
+    psVector* outVector,               ///< Vector to return, or NULL.
+    const psImage* inImage             ///< Image to convert.
+);
+
+/** Convert vector to matrix.
+ *
+ *  Converts a vector into a psImage matrix. If the dimensionality of the vector is PS_DIMEN_VECTOR, then the
+ *  resulting psImage is a 1d column. If the dimensionality of the vector is PS_DIMEN_TRANSV, then the
+ *  resulting psImage is a 1d row. If the user specifies NULL as the outImage argument,  then it will
+ *  automatically be created. This function operates only with the psF64 data type.
+ *
+ *  @return  psVector* : Pointer to psIamge.
+ */
+psImage* psVectorToMatrix(
+    psImage* outImage,                 ///< Matrix to return, or NULL.
+    const psVector* inVector           ///< Vector to convert.
+);
+
+/// Single value decomposition, provided by Andy Becker
+psImage *psMatrixSVD(psImage* evec, psVector* eval, const psImage* in);
+
+/// @}
+#endif // #ifndef PSMATRIX_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM.c	(revision 22322)
@@ -0,0 +1,589 @@
+/** @file  psMinimize.c
+ *  \brief basic minimization functions
+ *  @ingroup Math
+ *
+ *  This file will contain functions to minimize an arbitrary function at
+ *  a data point, fit an arbitrary function to a set of data points, and
+ *  fit a 1-D polynomial to a set of data points.
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.34 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-04-07 00:45:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+
+#include "psAbort.h"
+#include "psAssert.h"
+#include "psMinimizeLMM.h"
+#include "psImage.h"
+#include "psImageStructManip.h"
+#include "psLogMsg.h"
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+// Alpha & Beta only represent unmasked values
+bool psMinLM_GuessABP(
+    psImage  *Alpha,
+    psVector *Beta,
+    psVector *Params,
+    const psImage  *alpha,
+    const psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    psMinimizeLMLimitFunc checkLimits,
+    psF32 lambda,
+    psF32 *dLinear)
+{
+    PS_ASSERT_VECTOR_TYPE(Alpha,     PS_TYPE_F32,  false);
+    PS_ASSERT_VECTOR_TYPE(Beta,      PS_TYPE_F32,  false);
+    PS_ASSERT_VECTOR_TYPE(Params,    PS_TYPE_F32,  false);
+    PS_ASSERT_VECTOR_TYPE(alpha,     PS_TYPE_F32,  false);
+    PS_ASSERT_VECTOR_TYPE(beta,      PS_TYPE_F32,  false);
+    PS_ASSERT_VECTOR_TYPE(params,    PS_TYPE_F32,  false);
+    if (paramMask) {
+        PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_MASK, false);
+    }
+
+    assert (alpha->numCols == beta->n);
+    assert (alpha->numCols == alpha->numRows);
+
+    // set new guess values, applying (1+lambda) scaling to pivots
+    Beta = psVectorCopy(Beta, beta, PS_TYPE_F32);
+    Alpha = psImageCopy(Alpha, alpha, PS_TYPE_F32);
+    for (int j = 0; j < Alpha->numCols; j++) {
+        Alpha->data.F32[j][j] = alpha->data.F32[j][j] * (1.0 + lambda);
+    }
+
+    // error and clear above if kept?
+    if (false == psMatrixGJSolveF32(Alpha, Beta)) {
+        psTrace ("psLib.math", 4, "singular matrix in Guess ABP\n");
+        return(false);
+    }
+
+    // measure linear model prediction
+    // (we must do this before truncating Beta below)
+    if (dLinear) {
+	*dLinear = psMinLM_dLinear(Beta, beta, lambda);
+    }
+
+    // full-length Beta for checkLimits functions 
+    psVector *tmpBeta = psVectorAlloc(params->n, PS_TYPE_F32);
+    psVectorInit (tmpBeta, 0.0);
+
+    // set tmpBeta values which are not masked
+    for (int j = 0, n = 0; j < params->n; j++) {
+	if (paramMask && (paramMask->data.U8[j])) continue;
+	tmpBeta->data.F32[j] = Beta->data.F32[n];
+	n++;
+    }
+
+    // apply Beta to get new Params values
+    for (int j = 0; j < params->n; j++) {
+        if (paramMask && (paramMask->data.U8[j])) {
+            Params->data.F32[j] = params->data.F32[j];
+            continue;
+        }
+        // apply beta limits
+        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];
+
+        // compare new params to param limits
+        if (checkLimits) {
+            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.U8[j])) continue;
+	Beta->data.F32[n] = tmpBeta->data.F32[j];
+	n++;
+    }
+
+    psFree (tmpBeta);
+    return(true);
+}
+
+bool psMinimizeGaussNewtonDelta(
+    psVector *delta,
+    const psVector *params,
+    const psVector *paramMask,
+    const psArray  *x,
+    const psVector *y,
+    const psVector *yWt,
+    psMinimizeLMChi2Func func)
+{
+    psTrace("psLib.math", 3, "---- begin ----\n");
+
+    // allocate internal arrays (current vs Guess)
+    psImage *Alpha = NULL;
+    psVector *Beta = NULL;
+
+    psVectorInit (delta, 0.0);
+
+    // Alpha & Beta only contain elements to represent the unmasked parameters
+    // if none are available, return false
+    if (!psMinLM_AllocAB (&Alpha, &Beta, params, paramMask)) {
+	return false;
+    }
+
+    psImage *alpha   = psImageAlloc(Alpha->numCols, Alpha->numRows, PS_TYPE_F32);
+    psVector *Params = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    psVector *dy     = NULL;
+    bool retValue = true;
+
+    // the user provides the error or NULL.  we need to convert
+    // to appropriate weights
+    if (yWt != NULL) {
+        dy = (psVector *) yWt;
+    } else {
+        dy = psVectorAlloc(y->n, PS_TYPE_F32);
+        psVectorInit(dy, 1.0);
+    }
+
+    // XXX should we give up if chisq is nan?
+    psF32 chisq = psMinLM_SetABX(alpha, Beta, params, paramMask, x, y, dy, func);
+    if (isnan(chisq)) {
+        psTrace ("psLib.math", 5, "psMinLM_SetABX() returned a NAN chisq.\n");
+        retValue = false;
+    }
+
+    psTrace("psLib.math", 5, "psMinLM_SetABX() was succesful\n");
+    // dump some useful info if trace is defined
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        p_psImagePrint(psTraceGetDestination(), alpha, "alpha guess (0)");
+        p_psVectorPrint(psTraceGetDestination(), Beta, "beta guess (0)");
+        p_psVectorPrint(psTraceGetDestination(), params, "params guess (0)");
+    }
+
+    bool status = psMinLM_GuessABP(Alpha, delta, Params, alpha, Beta, params, paramMask, NULL, 0.0, NULL);
+    if (!status) {
+        psTrace ("psLib.math", 5, "psMinLM_GuessABP() returned FALSE.\n");
+        retValue = false;
+    }
+    psTrace("psLib.math", 5, "psMinLM_GuessABP() was succesful\n");
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        p_psImagePrint(psTraceGetDestination(), Alpha, "alpha guess (1)");
+        p_psVectorPrint(psTraceGetDestination(), delta, "delta guess (1)");
+        p_psVectorPrint(psTraceGetDestination(), Params, "params guess (1)");
+    }
+
+    psFree(alpha);
+    psFree(Alpha);
+    psFree(Beta);
+    psFree(Params);
+    if (yWt == NULL) {
+        psFree(dy);
+    }
+    psTrace("psLib.math", 3, "---- end ----\n");
+    return(retValue);
+}
+
+// measure linear model prediction
+psF32 psMinLM_dLinear(
+    const psVector *Beta,
+    const psVector *beta,
+    psF32 lambda)
+{
+
+    /* get linear model prediction */
+    psF32 dLinear = 0;
+    psF32 *B = Beta->data.F32;
+    psF32 *b = beta->data.F32;
+
+    float dh = 0.0, sh = 0.0, Sh = 0.0;
+
+    // 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;
+        dLinear += lambda*PS_SQR(B[i]) + B[i]*b[i];
+    }
+    return(0.5*dLinear);
+}
+
+// alpha, beta, params are already allocated
+psF32 psMinLM_SetABX(
+    psImage  *alpha,
+    psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    const psArray  *x,
+    const psVector *y,
+    const psVector *dy,
+    psMinimizeLMChi2Func func)
+{
+    PS_ASSERT_IMAGE_NON_NULL(alpha, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(beta, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_PTR_NON_NULL(x, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(y, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(dy, NAN);
+
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
+    if (paramMask) {
+        PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_MASK, false);
+    }
+
+    psF32 chisq;
+    psF32 delta;
+    psF32 weight;
+    psF32 ymodel;
+    psVector *deriv = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    // zero alpha, beta, and chisq for summing below
+    psImageInit (alpha, 0.0);
+    psVectorInit (beta, 0.0);
+    chisq = 0.0;
+
+    // calculate chisq, alpha, beta. alpha & beta only represent unmasked parameters; skip
+    // masked ones
+    for (psS32 i = 0; i < y->n; i++) {
+        ymodel = func(deriv, params, (psVector *) x->data[i]);
+
+        delta = ymodel - y->data.F32[i];
+        chisq += PS_SQR(delta) * dy->data.F32[i];
+
+        assert (!isnan(dy->data.F32[i]));
+        assert (!isnan(delta));
+        assert (!isnan(chisq));
+
+	// we track alpha,beta and params,deriv separately
+        for (int j = 0, J = 0; j < params->n; j++) {
+            if (paramMask && (paramMask->data.U8[j])) continue;
+
+            weight = deriv->data.F32[j] * dy->data.F32[i];
+
+            for (int k = 0, K = 0; k <= j; k++) {
+                if (paramMask && (paramMask->data.U8[k])) continue;
+                alpha->data.F32[J][K] += weight * deriv->data.F32[k];
+		K++;
+            }
+            beta->data.F32[J] += weight * delta;
+	    J++;
+        }
+    }
+
+    // calculate lower-left half of alpha
+    for (int j = 1; j < alpha->numCols; j++) {
+        for (int k = 0; k < j; k++) {
+            alpha->data.F32[k][j] = alpha->data.F32[j][k];
+        }
+    }
+
+    psFree(deriv);
+    return(chisq);
+}
+
+
+/******************************************************************************
+psMinimizeLMChi2():  This routine will take an procedure which calculates an
+arbitrary function and it's derivative and minimize the chi-squared match
+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?
+  *****************************************************************************/
+bool psMinimizeLMChi2(
+    psMinimization *min,
+    psImage *covar,
+    psVector *params,
+    psMinConstraint *constraint,
+    const psArray *x,
+    const psVector *y,
+    const psVector *yWt,
+    psMinimizeLMChi2Func func)
+{
+    psTrace("psLib.math", 3, "---- begin ----\n");
+    PS_ASSERT_PTR_NON_NULL(min, false);
+    PS_ASSERT_VECTOR_NON_NULL(params, false);
+    PS_ASSERT_VECTOR_NON_EMPTY(params, false);
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
+    psVector *paramMask = NULL;
+    if (constraint != NULL) {
+        paramMask = constraint->paramMask;
+        if (paramMask != NULL) {
+            PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_U8, false);
+            PS_ASSERT_VECTORS_SIZE_EQUAL(params, paramMask, false);
+        }
+    }
+    PS_ASSERT_PTR_NON_NULL(x, false);
+    for (psS32 i = 0 ; i < x->n ; i++) {
+        psVector *coord = (psVector *) (x->data[i]);
+        PS_ASSERT_VECTOR_NON_NULL(coord, false);
+        PS_ASSERT_VECTOR_TYPE(coord, PS_TYPE_F32, false);
+    }
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTOR_NON_EMPTY(y, false);
+    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F32, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(x, y, false);
+    if (yWt != NULL) {
+        PS_ASSERT_VECTOR_TYPE(yWt, PS_TYPE_F32, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(y, yWt, false);
+    }
+    PS_ASSERT_PTR_NON_NULL(func, false);
+
+    psMinimizeLMLimitFunc checkLimits = NULL;
+    if (constraint) {
+        checkLimits = constraint->checkLimits;
+    }
+
+    // this function has test and current values for several things
+    // the current best value is in lower case
+    // the next guess value is in upper case
+
+    // allocate internal arrays (current vs Guess)
+    psImage *Alpha = NULL;
+    psVector *Beta = NULL;
+
+    // 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");
+    }
+    
+    psImage *alpha   = psImageAlloc(Alpha->numCols, Alpha->numRows, PS_TYPE_F32);
+    psVector *beta   = psVectorAlloc(Beta->n, PS_TYPE_F32);
+    psVector *Params = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    psVector *dy     = NULL;
+    psF32 Chisq = 0.0;
+    psF32 lambda = 0.001;
+    psF32 dLinear = 0.0;
+
+    // the user provides the error or NULL.  we need to convert
+    // to appropriate weights
+    if (yWt != NULL) {
+        dy = (psVector *) yWt;
+    } else {
+        dy = psVectorAlloc(y->n, PS_TYPE_F32);
+        psVectorInit(dy, 1.0);
+    }
+
+    // calculate initial alpha and beta, set chisq (min->value)
+    min->value = psMinLM_SetABX(alpha, beta, params, paramMask, x, y, dy, func);
+    if (isnan(min->value)) {
+        min->iter = min->maxIter;
+        return(false);
+    }
+    // dump some useful info if trace is defined
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        p_psImagePrint(psTraceGetDestination(), alpha, "alpha guess (0)");
+        p_psVectorPrint(psTraceGetDestination(), beta, "beta guess (0)");
+    }
+    if (psTraceGetLevel("psLib.math") >= 5) {
+        p_psVectorPrint(psTraceGetDestination(), params, "params guess (0)");
+    }
+
+    // iterate until the tolerance is reached, or give up
+    while ((min->iter < min->maxIter) && ((min->lastDelta > min->tol) || !isfinite(min->lastDelta))) {
+        psTrace("psLib.math", 5, "Iteration number %d.  (max iterations is %d).\n", min->iter, min->maxIter);
+        psTrace("psLib.math", 5, "Last delta is %f.  Min->tol is %f.\n", min->lastDelta, min->tol);
+
+        // set a new guess for Alpha, Beta, Params
+        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda, &dLinear)) {
+            min->iter ++;
+            lambda *= 10.0;
+            continue;
+        }
+
+        // dump some useful info if trace is defined
+        if (psTraceGetLevel("psLib.math") >= 6) {
+            p_psImagePrint(psTraceGetDestination(), Alpha, "Alpha guess (1)");
+            p_psVectorPrint(psTraceGetDestination(), Beta, "Beta guess (1)");
+            p_psVectorPrint(psTraceGetDestination(), beta, "beta current (1)");
+        }
+        if (psTraceGetLevel("psLib.math") >= 5) {
+            p_psVectorPrint(psTraceGetDestination(), Params, "params guess (1)");
+        }
+
+        // calculate Chisq for new guess, update Alpha & Beta
+        Chisq = psMinLM_SetABX(Alpha, Beta, Params, paramMask, x, y, dy, func);
+        if (isnan(Chisq)) {
+            min->iter ++;
+            lambda *= 10.0;
+            continue;
+        }
+
+        // convergence criterion:
+        // compare the delta (min->value - Chisq) with the
+        // expected delta from the linear model (dLinear)
+        // accept new guess if it is an improvement (rho > 0), or else increase lambda
+        psF32 rho = (min->value - Chisq) / dLinear;
+
+        psTrace("psLib.math", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %f\n", min->value,
+                Chisq, min->lastDelta, dLinear, rho, lambda);
+
+        // dump some useful info if trace is defined
+        if (psTraceGetLevel("psLib.math") >= 6) {
+            p_psImagePrint(psTraceGetDestination(), Alpha, "alpha guess (2)");
+            p_psVectorPrint(psTraceGetDestination(), Beta, "beta guess (2)");
+        }
+
+        /* if (Chisq < min->value) {  */
+        if (rho > 0.0) {
+            min->lastDelta = (min->value - Chisq) / (dy->n - params->n);
+            min->value = Chisq;
+            alpha  = psImageCopy(alpha, Alpha, PS_TYPE_F32);
+            beta   = psVectorCopy(beta, Beta, PS_TYPE_F32);
+            params = psVectorCopy(params, Params, PS_TYPE_F32);
+            lambda *= 0.25;
+        } else {
+            lambda *= 10.0;
+        }
+        min->iter++;
+    }
+    psTrace("psLib.math", 5, "chisq: %f, last delta: %f, Niter: %d\n", min->value, min->lastDelta, min->iter);
+
+    // construct & return the covariance matrix (if requested)
+    if (covar != NULL) {
+        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, NULL, 0.0, NULL)) {
+            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.U8[j])) {
+		covar->data.F32[j][j] = 1.0;
+		continue;
+	    }
+	    for (int k = 0, K = 0; k < params->n; k++) {
+		if (paramMask && (paramMask->data.U8[k])) continue;
+		covar->data.F32[j][k] = Alpha->data.F32[J][K];
+		K++;
+	    }
+	    J++;
+	}
+    }
+
+    // free the internal temporary data
+    psFree(alpha);
+    psFree(Alpha);
+    psFree(beta);
+    psFree(Beta);
+    psFree(Params);
+    if (yWt == NULL) {
+        psFree(dy);
+    }
+    if (min->iter == min->maxIter) {
+        psTrace("psLib.math", 3, "---- end (false) ----\n");
+        return(false);
+    }
+    psTrace("psLib.math", 3, "---- end (true) ----\n");
+    return(true);
+}
+
+bool psMinLM_AllocAB (psImage **Alpha, psVector **Beta, const psVector *params, const psVector *paramMask) {
+
+    assert (Alpha);
+    assert (Beta);
+    assert (params);
+
+    int nParams = params->n;
+
+    // count unmasked parameters
+    if (paramMask) {
+	nParams = 0;
+	for (int i = 0; i < paramMask->n; i++) {
+	    if (paramMask->data.U8[i]) continue;
+	    nParams ++;
+	}
+    }
+
+    if (nParams == 0) { 
+	return false;
+    }
+
+    *Alpha = psImageAlloc(nParams, nParams, PS_TYPE_F32);
+    *Beta  = psVectorAlloc(nParams, PS_TYPE_F32);
+    return true;
+}
+
+static void minimizationFree(psMinimization *min)
+{
+    // There are no dynamically allocated items
+}
+
+psMinimization *psMinimizationAlloc(int maxIter,
+                                    float tol)
+{
+    PS_ASSERT_INT_NONNEGATIVE(maxIter, NULL);
+
+    psMinimization *min = psAlloc(sizeof(psMinimization));
+    psMemSetDeallocator(min, (psFreeFunc)minimizationFree);
+    P_PSMINIMIZATION_SET_MAXITER(min,maxIter);
+    P_PSMINIMIZATION_SET_TOL(min,tol);
+    min->value = 0.0;
+    min->iter = 0;
+    min->lastDelta = NAN;
+
+    return(min);
+}
+
+bool psMemCheckMinimization(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return( psMemGetDeallocator(ptr) == (psFreeFunc)minimizationFree );
+}
+
+
+static void constraintFree(psMinConstraint *tmp)
+{
+    if (tmp == NULL)
+        return;
+
+    psFree (tmp->paramMask);
+}
+
+psMinConstraint* psMinConstraintAlloc()
+{
+    psMinConstraint *tmp = psAlloc(sizeof(psMinConstraint));
+    psMemSetDeallocator(tmp, (psFreeFunc)constraintFree);
+    tmp->paramMask = NULL;
+    tmp->checkLimits = NULL;
+
+    return(tmp);
+}
+
+bool psMemCheckConstraint(psPtr tmp)
+{
+    return(psMemGetDeallocator(tmp) == (psFreeFunc) constraintFree);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM.h	(revision 22322)
@@ -0,0 +1,180 @@
+/* @file  psMinimizeLMM.c
+ * @brief basic minimization functions
+ *
+ * This file will contain function prototypes for various Levenberg-Marquadt
+ * minimization routines.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-09-28 00:35:20 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_MINIMIZE_LMM_H
+#define PS_MINIMIZE_LMM_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include "psVector.h"
+#include "psMemory.h"
+#include "psArray.h"
+#include "psImage.h"
+#include "psMatrix.h"
+#include "psPolynomial.h"
+#include "psSpline.h"
+#include "psStats.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psConstants.h"
+
+#define PS_DETERMINE_BRACKET_STEP_SIZE 0.10
+#define PS_MAX_LMM_ITERATIONS 100
+#define PS_MAX_MINIMIZE_ITERATIONS 100
+#define P_PSMINIMIZATION_SET_MAXITER(m,val) *(int*)&m->maxIter = val
+        #define P_PSMINIMIZATION_SET_TOL(m,val) *(float*)&m->tol = val
+
+                typedef enum {
+                    PS_MINIMIZE_BETA_LIMIT,
+                    PS_MINIMIZE_PARAM_MIN,
+                    PS_MINIMIZE_PARAM_MAX
+                } psMinConstraintMode;
+
+/** Specifies the format of a user-defined function that the general Levenberg-
+ *  Marquardt minimizer routine will accept.
+ *
+ *  @return float:   the single float value of the function given the parameters,
+ *       positions, and derivatives.
+ */
+typedef
+float (*psMinimizeLMChi2Func)(
+    psVector *deriv,                   ///< derivatives of the function
+    const psVector *params,            ///< the parameters used to evaluate the function
+    const psVector *x                  ///< positions for evaluation
+);
+
+/** Specifies the format of a user-defined function which check the parameters
+ *  against the allowed limits.  used by the general Levenberg-Marquardt minimizer.
+ *
+ *  @return float:   the single float value of the function given the parameters,
+ *       positions, and derivatives.
+ */
+typedef
+bool (*psMinimizeLMLimitFunc)(
+    psMinConstraintMode mode,   ///< which limit to check
+    int nParam,    ///< which param to check
+    float *params,   ///< current param value set
+    float *beta       ///< current beta value, if needed
+);
+
+/** A data structure for minimization routines.
+ *
+ *  Contains numerical analysis parameters/values
+ */
+typedef struct
+{
+    const int maxIter;                 ///< Convergence limit
+    const float tol;                   ///< Error Tolerance
+    float value;                       ///< Value of function at minimum
+    int iter;                          ///< Number of iterations to date
+    float lastDelta;                   ///< The last difference for the fit
+}
+psMinimization;
+
+/** A data structure for minimization routines.
+ *
+ *
+ */
+typedef struct
+{
+    psVector *paramMask;                ///< valid / invalid parameters
+    psMinimizeLMLimitFunc checkLimits; ///< user-supplied function to test the parameter limits
+}
+psMinConstraint;
+
+psMinConstraint *psMinConstraintAlloc() PS_ATTR_MALLOC;
+
+/** Allocates a psMinimization structure.
+ *
+ *  @return psMinimization* :   a new psMinimization struct
+ */
+psMinimization *psMinimizationAlloc(
+    int maxIter,                       ///< Number of minimization iterations to perform.
+    float tol                          ///< Requested error tolerance
+) PS_ATTR_MALLOC;
+
+/*  Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psMinimization structure, false otherwise.
+ */
+bool psMemCheckMinimization(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Minimizes a specified function based on the Levenberg-Marquardt method.
+ *
+ *  @return bool:   True if successful.
+ */
+bool psMinimizeLMChi2(
+    psMinimization *min,               ///< Minimization specification
+    psImage *covar,                    ///< Covariance matrix
+    psVector *params,                  ///< "Best Guess" for the parameters that minimize func
+    psMinConstraint *constraint, ///< Constraints on the parameters
+    const psArray *x,                  ///< Measurement ordinates of multiple vectors
+    const psVector *y,                 ///< Measurement coordinates
+    const psVector *yWt,               ///< Errors in the measurement coordinates
+    psMinimizeLMChi2Func func          ///< Specified function
+);
+
+bool psMinimizeGaussNewtonDelta (
+    psVector *delta,
+    const psVector *params,
+    const psVector *paramMask,
+    const psArray  *x,
+    const psVector *y,
+    const psVector *yErr,
+    psMinimizeLMChi2Func func
+);
+
+/** Function used to set parameters for generating "best guess" in minimizing Chi-Squared value.
+ *
+ *  @return psF32:    Chi-squared value for new guess
+ */
+psF32 psMinLM_SetABX (
+    psImage  *alpha,                   ///< alpha guess
+    psVector *beta,                    ///< beta guess
+    const psVector *params,            ///< params guess
+    const psVector *paramMask,         ///< param mask
+    const psArray  *x,                 ///< Measurement ordinates
+    const psVector *y,                 ///< Measurement coordinates
+    const psVector *dy,                ///< Weights calculated from y-errors
+    psMinimizeLMChi2Func func          ///< Specified function
+);
+
+
+bool psMinLM_GuessABP(
+    psImage  *Alpha,
+    psVector *Beta,
+    psVector *Params,
+    const psImage  *alpha,
+    const psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    psMinimizeLMLimitFunc checkLimits,
+    psF32 lambda,
+    psF32 *dLinear
+);
+
+psF32 psMinLM_dLinear(
+    const psVector *Beta,
+    const psVector *beta,
+    psF32 lambda);
+
+// allocate alpha and beta for unmasked parameters only 
+bool psMinLM_AllocAB (psImage **Alpha, psVector **Beta, const psVector *params, const psVector *paramMask);
+
+/// @}
+#endif // #ifndef PS_MINIMIZE_LMM_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM_ND.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM_ND.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM_ND.c	(revision 22322)
@@ -0,0 +1,577 @@
+/** @file  psMinimizeLMM_ND.c
+ *  \brief Levenberg-Marqardt minimization of N-D functions of N-D variables.  
+ *  @ingroup Math
+ *
+ *  Levenberg-Marqardt minimization of an N-dimensional function of N-diminsional independent
+ *  variables.  This code is based on the 1-D function version of N-D variables in psMinimizeLMM.c 
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-04-11 08:01:29 $
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+#include "psAbort.h"
+#include "psAssert.h"
+#include "psVector.h"
+#include "psMemory.h"
+#include "psArray.h"
+#include "psImage.h"
+#include "psMatrix.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psConstants.h"
+#include "psImage.h"
+#include "psLogMsg.h"
+
+// the main user API: minimize the chisq of the N-D function
+bool psMinimizeLMChi2(
+    psMinimization *min,
+    psImage *covar,
+    psVector *params,
+    psMinConstraint *constraint,
+    const psArray *x,
+    const psArray *y,
+    const psArray *yWt,
+    psMinimizeLMNDChi2Func func)
+{
+    psTrace("psLib.math", 3, "---- begin ----\n");
+    PS_ASSERT_PTR_NON_NULL(min, false);
+    PS_ASSERT_VECTOR_NON_NULL(params, false);
+    PS_ASSERT_VECTOR_NON_EMPTY(params, false);
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
+    psVector *paramMask = NULL;
+    if (constraint != NULL) {
+        paramMask = constraint->paramMask;
+        if (paramMask != NULL) {
+            PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_U8, false);
+            PS_ASSERT_VECTORS_SIZE_EQUAL(params, paramMask, false);
+        }
+    }
+    PS_ASSERT_PTR_NON_NULL(x, false);
+    for (psS32 i = 0 ; i < x->n ; i++) {
+        psVector *coord = (psVector *) (x->data[i]);
+        PS_ASSERT_VECTOR_NON_NULL(coord, false);
+        PS_ASSERT_VECTOR_TYPE(coord, PS_TYPE_F32, false);
+    }
+    PS_ASSERT_PTR_NON_NULL(y, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(x, y, false);
+    for (psS32 i = 0 ; i < y->n ; i++) {
+        psVector *value = (psVector *) (y->data[i]);
+        PS_ASSERT_VECTOR_NON_NULL(value, false);
+        PS_ASSERT_VECTOR_TYPE(value, PS_TYPE_F32, false);
+    }
+    if (yWt != NULL) {
+	PS_ASSERT_VECTORS_SIZE_EQUAL(x, yWt, false);
+	for (psS32 i = 0 ; i < yWt->n ; i++) {
+	    psVector *value = (psVector *) (yWt->data[i]);
+	    PS_ASSERT_VECTOR_NON_NULL(value, false);
+	    PS_ASSERT_VECTOR_TYPE(value, PS_TYPE_F32, false);
+	}
+    }
+    PS_ASSERT_PTR_NON_NULL(func, false);
+
+    psMinimizeLMLimitFunc checkLimits = NULL;
+    if (constraint) {
+        checkLimits = constraint->checkLimits;
+    }
+
+    // This function has 'test' and 'current' values for several things (alpha, beta,
+    // etc).  The current best value is in lower case; the next guess value is in upper
+    // case
+
+    // allocate internal arrays (current vs Guess)
+    psImage *Alpha = NULL;
+    psVector *Beta = NULL;
+
+    // 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");
+    }
+    
+    psImage *alpha   = psImageAlloc(Alpha->numCols, Alpha->numRows, PS_TYPE_F32);
+    psVector *beta   = psVectorAlloc(Beta->n, PS_TYPE_F32);
+    psVector *Params = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    psVector *dy     = NULL;
+    psF32 Chisq = 0.0;
+    psF32 lambda = 0.001;
+    psF32 dLinear = 0.0;
+
+    // the user provides the error or NULL.  we need to convert
+    // to appropriate weights
+    if (yWt != NULL) {
+        dy = (psVector *) yWt;
+    } else {
+        dy = psVectorAlloc(y->n, PS_TYPE_F32);
+        psVectorInit(dy, 1.0);
+    }
+
+    // calculate initial alpha and beta, set chisq (min->value)
+    min->value = psMinLM_SetABX(alpha, beta, params, paramMask, x, y, dy, func);
+    if (isnan(min->value)) {
+        min->iter = min->maxIter;
+        return(false);
+    }
+    // dump some useful info if trace is defined
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        p_psImagePrint(psTraceGetDestination(), alpha, "alpha guess (0)");
+        p_psVectorPrint(psTraceGetDestination(), beta, "beta guess (0)");
+    }
+    if (psTraceGetLevel("psLib.math") >= 5) {
+        p_psVectorPrint(psTraceGetDestination(), params, "params guess (0)");
+    }
+
+    // iterate until the tolerance is reached, or give up
+    while ((min->iter < min->maxIter) && ((min->lastDelta > min->tol) || !isfinite(min->lastDelta))) {
+        psTrace("psLib.math", 5, "Iteration number %d.  (max iterations is %d).\n", min->iter, min->maxIter);
+        psTrace("psLib.math", 5, "Last delta is %f.  Min->tol is %f.\n", min->lastDelta, min->tol);
+
+        // set a new guess for Alpha, Beta, Params
+        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda, &dLinear)) {
+            min->iter ++;
+            lambda *= 10.0;
+            continue;
+        }
+
+        // dump some useful info if trace is defined
+        if (psTraceGetLevel("psLib.math") >= 6) {
+            p_psImagePrint(psTraceGetDestination(), Alpha, "Alpha guess (1)");
+            p_psVectorPrint(psTraceGetDestination(), Beta, "Beta guess (1)");
+            p_psVectorPrint(psTraceGetDestination(), beta, "beta current (1)");
+        }
+        if (psTraceGetLevel("psLib.math") >= 5) {
+            p_psVectorPrint(psTraceGetDestination(), Params, "params guess (1)");
+        }
+
+        // calculate Chisq for new guess, update Alpha & Beta
+        Chisq = psMinLM_SetABX(Alpha, Beta, Params, paramMask, x, y, dy, func);
+        if (isnan(Chisq)) {
+            min->iter ++;
+            lambda *= 10.0;
+            continue;
+        }
+
+        // convergence criterion:
+        // compare the delta (min->value - Chisq) with the
+        // expected delta from the linear model (dLinear)
+        // accept new guess if it is an improvement (rho > 0), or else increase lambda
+        psF32 rho = (min->value - Chisq) / dLinear;
+
+        psTrace("psLib.math", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %f\n", min->value,
+                Chisq, min->lastDelta, dLinear, rho, lambda);
+
+        // dump some useful info if trace is defined
+        if (psTraceGetLevel("psLib.math") >= 6) {
+            p_psImagePrint(psTraceGetDestination(), Alpha, "alpha guess (2)");
+            p_psVectorPrint(psTraceGetDestination(), Beta, "beta guess (2)");
+        }
+
+        /* if (Chisq < min->value) {  */
+        if (rho > 0.0) {
+            min->lastDelta = (min->value - Chisq) / (dy->n - params->n);
+            min->value = Chisq;
+            alpha  = psImageCopy(alpha, Alpha, PS_TYPE_F32);
+            beta   = psVectorCopy(beta, Beta, PS_TYPE_F32);
+            params = psVectorCopy(params, Params, PS_TYPE_F32);
+            lambda *= 0.25;
+        } else {
+            lambda *= 10.0;
+        }
+        min->iter++;
+    }
+    psTrace("psLib.math", 5, "chisq: %f, last delta: %f, Niter: %d\n", min->value, min->lastDelta, min->iter);
+
+    // construct & return the covariance matrix (if requested)
+    if (covar != NULL) {
+        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, NULL, 0.0, NULL)) {
+            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.U8[j])) {
+		covar->data.F32[j][j] = 1.0;
+		continue;
+	    }
+	    for (int k = 0, K = 0; k < params->n; k++) {
+		if (paramMask && (paramMask->data.U8[k])) continue;
+		covar->data.F32[j][k] = Alpha->data.F32[J][K];
+		K++;
+	    }
+	    J++;
+	}
+    }
+
+    // free the internal temporary data
+    psFree(alpha);
+    psFree(Alpha);
+    psFree(beta);
+    psFree(Beta);
+    psFree(Params);
+    if (yWt == NULL) {
+        psFree(dy);
+    }
+    if (min->iter == min->maxIter) {
+        psTrace("psLib.math", 3, "---- end (false) ----\n");
+        return(false);
+    }
+    psTrace("psLib.math", 3, "---- end (true) ----\n");
+    return(true);
+}
+
+// Alpha & Beta only represent unmasked values
+bool psMinLM_GuessABP(
+    psImage  *Alpha,
+    psVector *Beta,
+    psVector *Params,
+    const psImage  *alpha,
+    const psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    psMinimizeLMLimitFunc checkLimits,
+    psF32 lambda,
+    psF32 *dLinear)
+{
+    PS_ASSERT_VECTOR_TYPE(Alpha,     PS_TYPE_F32,  false);
+    PS_ASSERT_VECTOR_TYPE(Beta,      PS_TYPE_F32,  false);
+    PS_ASSERT_VECTOR_TYPE(Params,    PS_TYPE_F32,  false);
+    PS_ASSERT_VECTOR_TYPE(alpha,     PS_TYPE_F32,  false);
+    PS_ASSERT_VECTOR_TYPE(beta,      PS_TYPE_F32,  false);
+    PS_ASSERT_VECTOR_TYPE(params,    PS_TYPE_F32,  false);
+    if (paramMask) {
+        PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_MASK, false);
+    }
+
+    assert (alpha->numCols == beta->n);
+    assert (alpha->numCols == alpha->numRows);
+
+    // set new guess values, applying (1+lambda) scaling to pivots
+    Beta = psVectorCopy(Beta, beta, PS_TYPE_F32);
+    Alpha = psImageCopy(Alpha, alpha, PS_TYPE_F32);
+    for (int j = 0; j < Alpha->numCols; j++) {
+        Alpha->data.F32[j][j] = alpha->data.F32[j][j] * (1.0 + lambda);
+    }
+
+    // error and clear above if kept?
+    if (false == psMatrixGJSolveF32(Alpha, Beta)) {
+        psTrace ("psLib.math", 4, "singular matrix in Guess ABP\n");
+        return(false);
+    }
+
+    // measure linear model prediction
+    // (we must do this before truncating Beta below)
+    if (dLinear) {
+	*dLinear = psMinLM_dLinear(Beta, beta, lambda);
+    }
+
+    // full-length Beta for checkLimits functions 
+    psVector *tmpBeta = psVectorAlloc(params->n, PS_TYPE_F32);
+    psVectorInit (tmpBeta, 0.0);
+
+    // set tmpBeta values which are not masked
+    for (int j = 0, n = 0; j < params->n; j++) {
+	if (paramMask && (paramMask->data.U8[j])) continue;
+	tmpBeta->data.F32[j] = Beta->data.F32[n];
+	n++;
+    }
+
+    // apply Beta to get new Params values
+    for (int j = 0; j < params->n; j++) {
+        if (paramMask && (paramMask->data.U8[j])) {
+            Params->data.F32[j] = params->data.F32[j];
+            continue;
+        }
+        // apply beta limits
+        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];
+
+        // compare new params to param limits
+        if (checkLimits) {
+            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.U8[j])) continue;
+	Beta->data.F32[n] = tmpBeta->data.F32[j];
+	n++;
+    }
+
+    psFree (tmpBeta);
+    return(true);
+}
+
+bool psMinimizeGaussNewtonDelta(
+    psVector *delta,
+    const psVector *params,
+    const psVector *paramMask,
+    const psArray  *x,
+    const psVector *y,
+    const psVector *yWt,
+    psMinimizeLMChi2Func func)
+{
+    psTrace("psLib.math", 3, "---- begin ----\n");
+
+    // allocate internal arrays (current vs Guess)
+    psImage *Alpha = NULL;
+    psVector *Beta = NULL;
+
+    psVectorInit (delta, 0.0);
+
+    // Alpha & Beta only contain elements to represent the unmasked parameters
+    // if none are available, return false
+    if (!psMinLM_AllocAB (&Alpha, &Beta, params, paramMask)) {
+	return false;
+    }
+
+    psImage *alpha   = psImageAlloc(Alpha->numCols, Alpha->numRows, PS_TYPE_F32);
+    psVector *Params = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    psVector *dy     = NULL;
+    bool retValue = true;
+
+    // the user provides the error or NULL.  we need to convert
+    // to appropriate weights
+    if (yWt != NULL) {
+        dy = (psVector *) yWt;
+    } else {
+        dy = psVectorAlloc(y->n, PS_TYPE_F32);
+        psVectorInit(dy, 1.0);
+    }
+
+    // XXX should we give up if chisq is nan?
+    psF32 chisq = psMinLM_SetABX(alpha, Beta, params, paramMask, x, y, dy, func);
+    if (isnan(chisq)) {
+        psTrace ("psLib.math", 5, "psMinLM_SetABX() returned a NAN chisq.\n");
+        retValue = false;
+    }
+
+    psTrace("psLib.math", 5, "psMinLM_SetABX() was succesful\n");
+    // dump some useful info if trace is defined
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        p_psImagePrint(psTraceGetDestination(), alpha, "alpha guess (0)");
+        p_psVectorPrint(psTraceGetDestination(), Beta, "beta guess (0)");
+        p_psVectorPrint(psTraceGetDestination(), params, "params guess (0)");
+    }
+
+    bool status = psMinLM_GuessABP(Alpha, delta, Params, alpha, Beta, params, paramMask, NULL, 0.0, NULL);
+    if (!status) {
+        psTrace ("psLib.math", 5, "psMinLM_GuessABP() returned FALSE.\n");
+        retValue = false;
+    }
+    psTrace("psLib.math", 5, "psMinLM_GuessABP() was succesful\n");
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        p_psImagePrint(psTraceGetDestination(), Alpha, "alpha guess (1)");
+        p_psVectorPrint(psTraceGetDestination(), delta, "delta guess (1)");
+        p_psVectorPrint(psTraceGetDestination(), Params, "params guess (1)");
+    }
+
+    psFree(alpha);
+    psFree(Alpha);
+    psFree(Beta);
+    psFree(Params);
+    if (yWt == NULL) {
+        psFree(dy);
+    }
+    psTrace("psLib.math", 3, "---- end ----\n");
+    return(retValue);
+}
+
+// measure linear model prediction
+psF32 psMinLM_dLinear(
+    const psVector *Beta,
+    const psVector *beta,
+    psF32 lambda)
+{
+
+    /* get linear model prediction */
+    psF32 dLinear = 0;
+    psF32 *B = Beta->data.F32;
+    psF32 *b = beta->data.F32;
+
+    float dh = 0.0, sh = 0.0, Sh = 0.0;
+
+    // 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;
+        dLinear += lambda*PS_SQR(B[i]) + B[i]*b[i];
+    }
+    return(0.5*dLinear);
+}
+
+// alpha, beta, params are already allocated
+psF32 psMinLM_SetABX(
+    psImage  *alpha,
+    psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    const psArray  *x,
+    const psArray  *y,
+    const psArray *dy,
+    psMinimizeLMNDChi2Func func)
+{
+    PS_ASSERT_IMAGE_NON_NULL(alpha, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(beta, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_PTR_NON_NULL(x, NAN);
+    PS_ASSERT_PTR_NON_NULL(y, NAN);
+    PS_ASSERT_PTR_NON_NULL(dy, NAN);
+
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
+    if (paramMask) {
+        PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_MASK, false);
+    }
+
+    psF32 chisq;
+    psF32 delta;
+    psF32 weight;
+
+    int nValue = ((psVector *)(y->data[0]))->n;
+
+    psVector *deriv = psVectorAlloc(params->n, PS_TYPE_F32);
+    psVector *ymodel = psVectorAlloc(nValue, PS_TYPE_F32);
+
+    // zero alpha, beta, and chisq for summing below
+    psImageInit (alpha, 0.0);
+    psVectorInit (beta, 0.0);
+    chisq = 0.0;
+
+    // calculate chisq, alpha, beta. alpha & beta only represent unmasked parameters; skip
+    // masked ones
+    for (psS32 i = 0; i < y->n; i++) {
+        func (ymodel, deriv, params, (psVector *) x->data[i]);
+
+	psVector *yvalue = y->data[i];
+	psVector *dyvalue = dy->data[i];
+
+	for (int k = 0; k < nValue; k++) {
+	    delta = ymodel->data.F32[k] - yvalue->data.F32[k];
+	    chisq += PS_SQR(delta) * dyvalue->data.F32[k];
+
+	    assert (!isnan(dyvalue->data.F32[k]));
+	    assert (!isnan(delta));
+	    assert (!isnan(chisq));
+	}
+
+	// we track alpha,beta and params,deriv separately
+        for (int j = 0, J = 0; j < params->n; j++) {
+            if (paramMask && (paramMask->data.U8[j])) continue;
+
+            weight = deriv->data.F32[j] * dy->data.F32[i];
+
+            for (int k = 0, K = 0; k <= j; k++) {
+                if (paramMask && (paramMask->data.U8[k])) continue;
+                alpha->data.F32[J][K] += weight * deriv->data.F32[k];
+		K++;
+            }
+            beta->data.F32[J] += weight * delta;
+	    J++;
+        }
+    }
+
+    // calculate lower-left half of alpha
+    for (int j = 1; j < alpha->numCols; j++) {
+        for (int k = 0; k < j; k++) {
+            alpha->data.F32[k][j] = alpha->data.F32[j][k];
+        }
+    }
+
+    psFree(deriv);
+    return(chisq);
+}
+
+
+bool psMinLM_AllocAB (psImage **Alpha, psVector **Beta, const psVector *params, const psVector *paramMask) {
+
+    assert (Alpha);
+    assert (Beta);
+    assert (params);
+
+    int nParams = params->n;
+
+    // count unmasked parameters
+    if (paramMask) {
+	nParams = 0;
+	for (int i = 0; i < paramMask->n; i++) {
+	    if (paramMask->data.U8[i]) continue;
+	    nParams ++;
+	}
+    }
+
+    if (nParams == 0) { 
+	return false;
+    }
+
+    *Alpha = psImageAlloc(nParams, nParams, PS_TYPE_F32);
+    *Beta  = psVectorAlloc(nParams, PS_TYPE_F32);
+    return true;
+}
+
+static void minimizationFree(psMinimization *min)
+{
+    // There are no dynamically allocated items
+}
+
+psMinimization *psMinimizationAlloc(int maxIter,
+                                    float tol)
+{
+    PS_ASSERT_INT_NONNEGATIVE(maxIter, NULL);
+
+    psMinimization *min = psAlloc(sizeof(psMinimization));
+    psMemSetDeallocator(min, (psFreeFunc)minimizationFree);
+    P_PSMINIMIZATION_SET_MAXITER(min,maxIter);
+    P_PSMINIMIZATION_SET_TOL(min,tol);
+    min->value = 0.0;
+    min->iter = 0;
+    min->lastDelta = NAN;
+
+    return(min);
+}
+
+bool psMemCheckMinimization(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return( psMemGetDeallocator(ptr) == (psFreeFunc)minimizationFree );
+}
+
+
+static void constraintFree(psMinConstraint *tmp)
+{
+    if (tmp == NULL)
+        return;
+
+    psFree (tmp->paramMask);
+}
+
+psMinConstraint* psMinConstraintAlloc()
+{
+    psMinConstraint *tmp = psAlloc(sizeof(psMinConstraint));
+    psMemSetDeallocator(tmp, (psFreeFunc)constraintFree);
+    tmp->paramMask = NULL;
+    tmp->checkLimits = NULL;
+
+    return(tmp);
+}
+
+bool psMemCheckConstraint(psPtr tmp)
+{
+    return(psMemGetDeallocator(tmp) == (psFreeFunc) constraintFree);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM_ND.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM_ND.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizeLMM_ND.h	(revision 22322)
@@ -0,0 +1,106 @@
+/* @file  psMinimizeLMM.h
+ * @brief Levenberg-Marqardt minimization of N-D functions of N-D variables.  
+ * @ingroup Math
+ *
+ *  Levenberg-Marqardt minimization of an N-dimensional function of N-diminsional independent
+ *  variables.  This code is based on the 1-D function version of N-D variables in psMinimizeLMM.c 
+ *
+ *  @author EAM, IfA
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PS_MINIMIZE_LMM_ND_H
+#define PS_MINIMIZE_LMM_ND_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/* Format of a user-defined function that the general Levenberg-Marquardt minimizer
+ * routine will accept.
+ *
+ * @return bool: success / failure status.  the N-D function value is returned to the
+ * pre-allocated vector 'value' and the derivatives of the parameters are returned to the
+ * pre-allocated vector 'deriv', iff defined
+ * 
+ */
+typedef bool (*psMinimizeLMNDChi2Func)(
+    psVector *value,                   ///< values of the function
+    psVector *deriv,                   ///< derivatives of the function
+    const psVector *params,            ///< the parameters used to evaluate the function
+    const psVector *x                  ///< positions for evaluation
+    );
+
+/*  Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psMinimization structure, false otherwise.
+ */
+bool psMemCheckMinimization(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Minimizes a specified function based on the Levenberg-Marquardt method.
+ *
+ *  @return bool:   True if successful.
+ */
+bool psMinimizeLMChi2(
+    psMinimization *min,               ///< Minimization specification
+    psImage *covar,                    ///< Covariance matrix
+    psVector *params,                  ///< "Best Guess" for the parameters that minimize func
+    psMinConstraint *constraint, ///< Constraints on the parameters
+    const psArray *x,                  ///< Measurement ordinates of multiple vectors
+    const psVector *y,                 ///< Measurement coordinates
+    const psVector *yWt,               ///< Errors in the measurement coordinates
+    psMinimizeLMChi2Func func          ///< Specified function
+);
+
+bool psMinimizeGaussNewtonDelta (
+    psVector *delta,
+    const psVector *params,
+    const psVector *paramMask,
+    const psArray  *x,
+    const psVector *y,
+    const psVector *yErr,
+    psMinimizeLMChi2Func func
+);
+
+/** Function used to set parameters for generating "best guess" in minimizing Chi-Squared value.
+ *
+ *  @return psF32:    Chi-squared value for new guess
+ */
+psF32 psMinLM_SetABX (
+    psImage  *alpha,                   ///< alpha guess
+    psVector *beta,                    ///< beta guess
+    const psVector *params,            ///< params guess
+    const psVector *paramMask,         ///< param mask
+    const psArray  *x,                 ///< Measurement ordinates
+    const psVector *y,                 ///< Measurement coordinates
+    const psVector *dy,                ///< Weights calculated from y-errors
+    psMinimizeLMChi2Func func          ///< Specified function
+);
+
+
+bool psMinLM_GuessABP(
+    psImage  *Alpha,
+    psVector *Beta,
+    psVector *Params,
+    const psImage  *alpha,
+    const psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    psMinimizeLMLimitFunc checkLimits,
+    psF32 lambda,
+    psF32 *dLinear
+);
+
+psF32 psMinLM_dLinear(
+    const psVector *Beta,
+    const psVector *beta,
+    psF32 lambda);
+
+// allocate alpha and beta for unmasked parameters only 
+bool psMinLM_AllocAB (psImage **Alpha, psVector **Beta, const psVector *params, const psVector *paramMask);
+
+/// @}
+#endif // #ifndef PS_MINIMIZE_LMM_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePolyFit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePolyFit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePolyFit.c	(revision 22322)
@@ -0,0 +1,2313 @@
+/** @file  psMinimize.c
+ *  \brief basic minimization functions
+ *  @ingroup Math
+ *
+ *  This file will contain functions to minimize an arbitrary function at
+ *  a data point, fit an arbitrary function to a set of data points, and
+ *  fit a 1-D polynomial to a set of data points.
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.33 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-02 19:03:38 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: psMatrixLUSolve() does not return error codes when the results are NANs.
+ *
+ *  XXX: For clip-fit functions, what should we do if the mask is NULL?
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <float.h>
+#include <math.h>
+
+#include "psMinimizePolyFit.h"
+#include "psAssert.h"
+#include "psMinimizeLMM.h"  // For Gauss-Jordan routines
+#include "psStats.h"
+#include "psImage.h"
+#include "psImageStructManip.h"
+#include "psBinaryOp.h"
+#include "psLogMsg.h"
+#include "psMathUtils.h"
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+# define USE_GAUSS_JORDAN 1
+# define USE_ROBUST_STATS_FOR_CLIPPING 1
+
+#define PS_VECTOR_GEN_CHEBY_INDEX(VEC, SIZE, TYPE) \
+VEC = psVectorAlloc(SIZE, TYPE); \
+if (TYPE == PS_TYPE_F64) { \
+    for (psS32 i = 0 ; i < SIZE ; i++) { \
+        VEC->data.F64[i] = ((2.0 / ((psF64) (SIZE - 1))) * ((psF64) i)) - 1.0; \
+    }\
+} else if (TYPE == PS_TYPE_F32){ \
+    for (psS32 i = 0 ; i < SIZE ; i++) { \
+        VEC->data.F32[i] = ((2.0 / ((psF32) (SIZE - 1))) * ((psF32) i)) - 1.0; \
+    }\
+}\
+
+// free a local temporary F64 vector (TEMP) which is a copy of a non-F64 vector (ORIG)
+# define PS_FREE_TEMP_F64_VECTOR(ORIG, TEMP) \
+if ((ORIG != NULL) && (ORIG->type.type != PS_TYPE_F64)) { psFree(TEMP); }
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+/******************************************************************************
+BuildSums1D(sums, x, polyOrder, sums): this routine calculates the powers of
+input parameter "x" between 0 and input parameter nTerms*2.  The result is
+returned as a psVector sums.
+*****************************************************************************/
+static psVector *BuildSums1D(
+    psVector* sums,
+    psF64 x,
+    psS32 nTerm)
+{
+    psS32 nSum = 0;
+    psF64 xSum = 0.0;
+
+    //
+    // XXX: Why do we multiply by 2 here?  It's better to do it outside and
+    // have the definition of this function remain sensible.
+    //
+    nSum = 2*nTerm;
+    if (sums == NULL) {
+        sums = psVectorAlloc(nSum, PS_TYPE_F64);
+    } else if (nSum > sums->n) {
+        sums = psVectorRealloc(sums, nSum);
+        sums->n = nSum;
+    }
+
+    xSum = 1.0;
+    for (psS32 i = 0; i < nSum; i++) {
+        sums->data.F64[i] = xSum;
+        xSum *= x;
+    }
+    return (sums);
+}
+
+/******************************************************************************
+BuildSums2D(sums, x, y, nXterm, nYterm): this routine calculates the powers of
+input parameter "x" and "y" between 0 and input parameter nXterms*2 and
+nYterm*2.  The result is returned as a psImage sums.
+ *****************************************************************************/
+static psImage *BuildSums2D(
+    psImage *sums,
+    psF64 x,
+    psF64 y,
+    psS32 nXterm,
+    psS32 nYterm)
+{
+    psS32 nXsum = 0;
+    psS32 nYsum = 0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+
+    // note that we are using the X and Y elements of the image reversed to be
+    // consistent with the other BuildSumsND functions in terms of the definition
+    // of the sums array
+    nXsum = 2*nXterm;
+    nYsum = 2*nYterm;
+    if (sums == NULL) {
+        sums = psImageAlloc(nYsum, nXsum, PS_TYPE_F64);
+    }
+    if ((nYsum != sums->numCols) || (nXsum != sums->numRows)) {
+        psFree (sums);
+        sums = psImageAlloc(nYsum, nXsum, PS_TYPE_F64);
+    }
+
+    xSum = 1.0;
+    for (psS32 i = 0; i < nXsum; i++) {
+        ySum = xSum;
+        for (psS32 j = 0; j < nYsum; j++) {
+            sums->data.F64[i][j] = ySum;
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return (sums);
+}
+
+/******************************************************************************
+BuildSums3D(sums, x, y, z, nXterm, nYterm, nZterm): this routine calculates
+the powers of input parameter "x", "y", and "z" between 0 and input parameter
+nXterms*2, nYterm*2, and nZterm*2.  The result is returned as a 3-D array sums.
+ *****************************************************************************/
+static psF64 ***BuildSums3D(
+    psF64 ***sums,
+    psF64 x,
+    psF64 y,
+    psF64 z,
+    psS32 nXterm,
+    psS32 nYterm,
+    psS32 nZterm)
+{
+    psS32 nXsum = 0;
+    psS32 nYsum = 0;
+    psS32 nZsum = 0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    psF64 zSum = 1.0;
+
+    nXsum = 2*nXterm;
+    nYsum = 2*nYterm;
+    nZsum = 2*nZterm;
+    if (sums == NULL) {
+        sums = (psF64 ***) psAlloc (nXsum*sizeof(psF64));
+        for (psS32 i = 0; i < nXsum; i++) {
+            sums[i] = (psF64 **) psAlloc (nYsum*sizeof(psF64));
+            for (psS32 j = 0; j < nYsum; j++) {
+                sums[i][j] = (psF64 *) psAlloc (nZsum*sizeof(psF64));
+            }
+        }
+    }
+    // careful with this function: there is no size checking and realloc for reuse
+
+    if (1) {
+        zSum = 1.0;
+        for (psS32 k = 0; k < nZsum; k++) {
+            ySum = zSum;
+            for (psS32 j = 0; j < nYsum; j++) {
+                xSum = ySum;
+                for (psS32 i = 0; i < nXsum; i++) {
+                    sums[i][j][k] = xSum;
+                    xSum *= x;
+                }
+                ySum *= y;
+            }
+            zSum *= z;
+        }
+    } else {
+        xSum = 1.0;
+        for (psS32 i = 0; i < nXsum; i++) {
+            ySum = xSum;
+            for (psS32 j = 0; j < nYsum; j++) {
+                zSum = ySum;
+                for (psS32 k = 0; k < nZsum; k++) {
+                    sums[i][j][k] = zSum;
+                    zSum *= z;
+                }
+                ySum *= y;
+            }
+            xSum *= x;
+        }
+    }
+
+    return (sums);
+}
+
+/******************************************************************************
+    BuildSums4D(sums, x, y, z, t, nXterm, nYterm, nZterm, nTterm). equiv to
+    BuildSums2D(). The result is returned as a psF64 ****
+*****************************************************************************/
+static psF64 ****BuildSums4D(
+    psF64 ****sums,
+    psF64 x,
+    psF64 y,
+    psF64 z,
+    psF64 t,
+    psS32 nXterm,
+    psS32 nYterm,
+    psS32 nZterm,
+    psS32 nTterm)
+{
+    psS32 nXsum = 0;
+    psS32 nYsum = 0;
+    psS32 nZsum = 0;
+    psS32 nTsum = 0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    psF64 zSum = 1.0;
+    psF64 tSum = 1.0;
+
+    nXsum = 2*nXterm;
+    nYsum = 2*nYterm;
+    nZsum = 2*nZterm;
+    nTsum = 2*nTterm;
+    if (sums == NULL) {
+        sums = (psF64 ****) psAlloc (nXsum*sizeof(psF64));
+        for (psS32 i = 0; i < nXsum; i++) {
+            sums[i] = (psF64 ***) psAlloc (nYsum*sizeof(psF64));
+            for (psS32 j = 0; j < nYsum; j++) {
+                sums[i][j] = (psF64 **) psAlloc (nZsum*sizeof(psF64));
+                for (psS32 k = 0; k < nZsum; k++) {
+                    sums[i][j][k] = (psF64 *) psAlloc (nTsum*sizeof(psF64));
+                }
+            }
+        }
+    }
+    // careful with this function: there is no size checking and realloc for reuse
+
+    tSum = 1.0;
+    for (psS32 m = 0; m < nTsum; m++) {
+        zSum = tSum;
+        for (psS32 k = 0; k < nZsum; k++) {
+            ySum = zSum;
+            for (psS32 j = 0; j < nYsum; j++) {
+                xSum = ySum;
+                for (psS32 i = 0; i < nXsum; i++) {
+                    sums[i][j][k][m] = xSum;
+                    xSum *= x;
+                }
+                ySum *= y;
+            }
+            zSum *= z;
+        }
+        tSum *= t;
+    }
+    return (sums);
+}
+
+/******************************************************************************
+ ******************************************************************************
+ Analytical 1-D fitting routines.
+ ******************************************************************************
+ *****************************************************************************/
+
+/******************************************************************************
+ ******************************************************************************
+ 1-D Vector Poly Fitting Code.
+ ******************************************************************************
+ *****************************************************************************/
+
+/******************************************************************************
+vectorFitPolynomial1DCheb():  This routine will fit a Chebyshev
+polynomial of degree myPoly->nX to the data points (x, y) and return the
+coefficients of that polynomial.
+*****************************************************************************/
+static bool vectorFitPolynomial1DCheb(
+    psPolynomial1D* myPoly,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector* y,
+    const psVector* yErr,
+    const psVector* x)
+{
+    PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(myPoly->nX, 0, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(y, NULL);
+    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, NULL);
+    if (yErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(y, yErr, NULL);
+        PS_ASSERT_VECTOR_TYPE(yErr, PS_TYPE_F64, NULL);
+    }
+    if (x != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(y, x, NULL);
+        PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, NULL);
+    }
+    if (mask != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(y, mask, NULL);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, NULL);
+    }
+
+    int numTerms = myPoly->nX + 1;      // Number of polynomial terms
+    int numData = x->n;                 // Number of data elements
+
+    psImage *A = psImageAlloc(numTerms, numTerms, PS_TYPE_F64); // Least-squares matrix
+    psVector *B = psVectorAlloc(numTerms, PS_TYPE_F64); // Least-squares vector
+    psImageInit(A, 0.0);
+    psVectorInit(B, 0.0);
+
+    psPolynomial1D **chebPolys = p_psCreateChebyshevPolys(numTerms); // The chebyshev polynomials
+    psImage *sums = psImageAlloc(numData, numTerms, PS_TYPE_F64);
+    for (int i = 0; i < numTerms; i++) {
+        if (myPoly->coeffMask[i] & PS_POLY_MASK_BOTH) {
+            continue;
+        }
+        psPolynomial1D *cheb = chebPolys[i];
+        psVector *sum = psPolynomial1DEvalVector(cheb, x);
+        memcpy(sums->data.F64[i], sum->data.F64, numData*sizeof(psF64));
+        psFree(sum);
+        psFree(cheb);
+    }
+    psFree(chebPolys);
+
+    // Dereference pointers, for speed in the loop
+    psF64 **matrix = A->data.F64;       // Least-squares matrix
+    psF64 *vector = B->data.F64;        // Least-squares vector
+    psU8 *dataMask = NULL;              // Mask for data
+    if (mask) {
+        dataMask = mask->data.U8;
+    }
+    psU8 *coeffMask = myPoly->coeffMask;      // Mask for polynomial terms
+    psF64 *yData = y->data.F64;         // Coordinate data
+    psF64 *yErrData = NULL;             // Errors in the coordinate
+    if (yErr) {
+        yErrData = yErr->data.F64;
+    }
+    psF64 **sumsData = sums->data.F64;  // Sums
+
+    for (int k = 0; k < numData; k++) {
+        if (dataMask && dataMask[k]) {
+            continue;
+        }
+
+        double wt;
+        if (!yErr) {
+            wt = 1.0;
+        } else {
+            // this filters fErr == 0 values
+            wt = (yErrData[k] == 0) ? 0.0 : 1.0 / PS_SQR(yErrData[k]);
+        }
+
+        for (int i = 0; i < numTerms; i++) {
+            if (coeffMask[i] & PS_POLY_MASK_BOTH) {
+                matrix[i][i] = 1.0;
+                continue;
+            }
+            vector[i] += yData[k] * sumsData[i][k] * wt;
+            matrix[i][i] += sumsData[i][k] * sumsData[i][k] * wt; // The diagonal entry
+            for (int j = i + 1; j < numTerms; j++) { // The upper diagonal only: we will use symmetry
+                if (coeffMask[j] & PS_POLY_MASK_BOTH) {
+                    continue;
+                }
+                double value = sumsData[i][k] * sumsData[j][k] * wt; // The value to add to the matrix
+                matrix[i][j] += value;
+                matrix[j][i] += value;  // Taking advantage of the symmetry
+            }
+        }
+    }
+    psFree(sums);
+
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        PS_IMAGE_PRINT_F64(A);
+        PS_VECTOR_PRINT_F64(B);
+    }
+
+    if (USE_GAUSS_JORDAN) {
+        // GaussJordan version
+        if (!psMatrixGJSolve(A, B)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.  Returning NULL.\n");
+            goto escape_GJ;
+        } else {
+            // the first nTerm entries in B correspond directly to the desired
+            // polynomial coefficients.  this is only true for the 1D case
+            for (psS32 k = 0; k < numTerms; k++) {
+                myPoly->coeff[k] = B->data.F64[k];
+                myPoly->coeffErr[k] = sqrt(A->data.F64[k][k]);
+            }
+            // The constant needs to be multiplied by 2, because it's half the a_0.
+            myPoly->coeff[0] *= 2.0;
+            myPoly->coeffErr[0] *= 2.0;
+        }
+    } else {
+        // LUD version of the fit
+        psImage *ALUD = NULL;
+        psVector* outPerm = NULL;
+        psVector* coeffs = NULL;
+
+        ALUD = psImageAlloc(numTerms, numTerms, PS_TYPE_F64);
+        ALUD = psMatrixLUD(ALUD, &outPerm, A);
+        if (ALUD == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not do LUD decomposition on matrix.  Returning NULL.\n");
+            goto escape_LUD;
+        } else {
+            coeffs = psMatrixLUSolve(coeffs, ALUD, B, outPerm);
+            if (coeffs == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "Could not solve LUD matrix.  Returning NULL.\n");
+                goto escape_LUD;
+            } else {
+                for (psS32 k = 0; k < numTerms; k++) {
+                    myPoly->coeff[k] = coeffs->data.F64[k];
+                }
+            }
+        }
+        psFree(ALUD);
+        psFree(coeffs);
+        psFree(outPerm);
+    }
+    psFree(A);
+    psFree(B);
+    return true;
+
+escape_LUD:
+    // XXX drop the LUD version!
+    psFree(A);
+    psFree(B);
+    return false;
+
+escape_GJ:
+    psFree(A);
+    psFree(B);
+    return false;
+}
+
+/******************************************************************************
+VectorFitPolynomial1DOrd(myPoly, *mask, maskValue, *y, *yErr, *x): This is a
+private routine which will fit a 1-D polynomial to a set of (x, f) pairs.  The
+x and fErr vectors may be NULL.  All non-NULL vectors must be of type
+PS_TYPE_F64.
+ 
+XXX EAM : since this is a private function, can we drop the ASSERTS?
+XXX EAM : can we drop the LUD version? it does not calculate coeffErr values!!
+*****************************************************************************/
+static bool VectorFitPolynomial1DOrd(
+    psPolynomial1D* myPoly,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x)
+{
+    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_POLY_NON_NULL(myPoly, false);
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
+    if (mask) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+    }
+    if (x) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+        PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
+    }
+    if (fErr) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
+        PS_ASSERT_VECTOR_TYPE(fErr, PS_TYPE_F64, false);
+    }
+
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        psTrace("psLib.math", 6, "VectorFitPolynomial1D()\n");
+        for (psS32 i = 0; i < f->n; i++) {
+            psTrace("psLib.math", 6, "(x, f, fErr) is (");
+            if (x != NULL) {
+                psTrace("psLib.math", 6, "%f, %f, ", x->data.F64[i], f->data.F64[i]);
+            } else {
+                psTrace("psLib.math", 6, "%f, %f, ", (psF64) i, f->data.F64[i]);
+            }
+            if (fErr != NULL) {
+                psTrace("psLib.math", 6, "%f)\n", fErr->data.F64[i]);
+            } else {
+                psTrace("psLib.math", 6, "NULL)\n");
+            }
+        }
+    }
+
+    int nTerm = myPoly->nX + 1;         // Number of terms in the equation
+    int nData = f->n;                   // Number of data points
+    psImage *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
+    psVector *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
+
+    // Initialize data structures.
+    if (!psImageInit(A, 0.0) || !psVectorInit(B, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "Could initialize data structures A, B.  Returning NULL.\n");
+        psFree(A);
+        psFree(B);
+        psTrace("psLib.math", 4, "---- %s() End ----\n", __func__);
+        return false;
+    }
+
+    // Build the least squares matrix and vector
+
+    // Dereference some pointers for speed in the loop
+    psU8 *dataMask = NULL;              // Dereferenced version of mask for data points
+    if (mask) {
+        dataMask = mask->data.U8;
+    }
+    psU8 *coeffMask = myPoly->coeffMask;      // Dereferenced version of mask for polynomial terms
+    psF64 *ordinates = NULL;            // Dereferenced version of ordinate data
+    if (x) {
+        ordinates = x->data.F64;
+    }
+    psF64 *coordinates = f->data.F64;   // Dereferenced version of coordinate data
+    psF64 *coordErr = NULL;             // Dereferenced version of coordinate errors
+    if (fErr) {
+        coordErr = fErr->data.F64;
+    }
+    psF64 *vector = B->data.F64;        // Dereferenced version of least-squares vector
+    psF64 **matrix = A->data.F64;       // Dereferenced version of least-squares matrix
+
+    psVector* xSums = NULL;             // Contains 1, x, x^2, x^3, etc, for ease of calculation
+    for (int k = 0; k < nData; k++) {
+        if (dataMask && dataMask[k] & maskValue) {
+            continue;
+        }
+        if (ordinates) {
+            xSums = BuildSums1D(xSums, ordinates[k], nTerm);
+        } else {
+            xSums = BuildSums1D(xSums, (psF64)k, nTerm);
+        }
+        psF64 *sums = xSums->data.F64;  // Dereferenced version of sums
+
+        double wt;
+        if (!fErr) {
+            wt = 1.0;
+        } else {
+            // this filters fErr == 0 values
+            wt = (coordErr[k] == 0) ? 0.0 : 1.0 / PS_SQR(coordErr[k]);
+        }
+
+        for (int i = 0; i < nTerm; i++) {
+            if (coeffMask[i] & PS_POLY_MASK_SET) {
+                matrix[i][i] = 1.0;
+                continue;
+            }
+            vector[i] += coordinates[k] * sums[i] * wt;
+            matrix[i][i] += sums[2 * i] * wt; // The diagonal entry
+            for (int j = i + 1; j < nTerm; j++) { // The upper diagonal only: we will use symmetry
+                if (coeffMask[j] & PS_POLY_MASK_SET) {
+                    continue;
+                }
+                double value = sums[i + j] * wt; // The value to add to the matrix
+                matrix[i][j] += value;
+                matrix[j][i] += value;  // Taking advantage of the symmetry
+            }
+        }
+    }
+    psFree(xSums);
+
+    // elements which are masked for fitting need to be subtracted from the vector
+    for (int i = 0; i < nTerm; i++) {
+	if (coeffMask[i] & PS_POLY_MASK_BOTH) {
+	    continue;
+	}
+	for (int j = 0; j < nTerm; j++) { // The upper diagonal only: we will use symmetry
+	    if (coeffMask[j] & PS_POLY_MASK_SET) {
+		continue;
+	    }
+	    if (!(coeffMask[j] & PS_POLY_MASK_FIT)) {
+		continue;
+	    }
+	    vector[i] -= matrix[i][j]*myPoly->coeff[j];
+	}
+    }
+    
+    // set the un-fitted and un-set elements to 0 or 1 for pivots
+    for (int i = 0; i < nTerm; i++) {
+	if (coeffMask[i] & PS_POLY_MASK_BOTH) {
+	    for (int j = 0; j < nTerm; j++) { // The upper diagonal only: we will use symmetry
+		matrix[i][j] = 0.0;
+		matrix[j][i] = 0.0;
+	    }
+	    matrix[i][i] = 1.0;
+	    continue;
+	}
+    }
+
+    if (psTraceGetLevel("psLib.math") >= 4) {
+        printf("Least-squares vector:\n");
+        for (int i = 0; i < nTerm; i++) {
+            printf("%f ", B->data.F64[i]);
+        }
+        printf("\n");
+        printf("Least-squares matrix:\n");
+        for (int i = 0; i < nTerm; i++) {
+            for (int j = 0; j < nTerm; j++) {
+                printf("%f ", A->data.F64[i][j]);
+            }
+            printf("\n");
+        }
+    }
+
+    // XXX: rel10_ifa used psMatrixGJSolve().  However, this failed tests.  So, I'm using psMatrixLUD().
+    if (USE_GAUSS_JORDAN) {
+        // GaussJordan version
+        if (!psMatrixGJSolve(A, B)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.  Returning NULL.\n");
+            goto escape_GJ;
+        } else {
+            // the first nTerm entries in B correspond directly to the desired
+            // polynomial coefficients.  this is only true for the 1D case
+            for (psS32 k = 0; k < nTerm; k++) {
+		if (coeffMask[k] & PS_POLY_MASK_FIT) continue;
+		myPoly->coeff[k] = B->data.F64[k];
+		myPoly->coeffErr[k] = sqrt(A->data.F64[k][k]);
+            }
+        }
+    } else {
+        // LUD version of the fit
+        psImage *ALUD = NULL;
+        psVector* outPerm = NULL;
+        psVector* coeffs = NULL;
+
+        ALUD = psImageAlloc(nTerm, nTerm, PS_TYPE_F64);
+        ALUD = psMatrixLUD(ALUD, &outPerm, A);
+        if (ALUD == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not do LUD decomposition on matrix.  Returning NULL.\n");
+            goto escape_LUD;
+        } else {
+            coeffs = psMatrixLUSolve(coeffs, ALUD, B, outPerm);
+            if (coeffs == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "Could not solve LUD matrix.  Returning NULL.\n");
+                goto escape_LUD;
+            } else {
+                for (psS32 k = 0; k < nTerm; k++) {
+		    if (coeffMask[k] & PS_POLY_MASK_FIT) continue;
+                    myPoly->coeff[k] = coeffs->data.F64[k];
+                    // XXX LUD does not give inverse of A
+                }
+            }
+        }
+        psFree(ALUD);
+        psFree(coeffs);
+        psFree(outPerm);
+    }
+    psFree(A);
+    psFree(B);
+
+    psTrace("psLib.math", 4, "---- %s() End ----\n", __func__);
+    return true;
+
+escape_LUD:
+    psFree(A);
+    psFree(B);
+    return false;
+
+escape_GJ:
+    psFree(A);
+    psFree(B);
+    return false;
+}
+
+
+/******************************************************************************
+psVectorFitPolynomial1D():  This routine fits a polynomial of arbitrary degree
+(specified in poly) to the data points (x, y) and return that polynomial.
+Types F32 and F64 are supported, however, type F32 is done via vector
+conversion only.
+ *****************************************************************************/
+bool psVectorFitPolynomial1D(
+    psPolynomial1D *poly,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, false);
+    PS_ASSERT_INT_NONNEGATIVE(poly->nX, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_NON_EMPTY(f, false);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
+    if (mask != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+    }
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
+        PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, false);
+    }
+    if (x != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+        PS_ASSERT_VECTOR_TYPE_F32_OR_F64(x, false);
+    }
+
+    // Convert input vectors to F64 if necessary.
+    psVector *f64 = (f->type.type == PS_TYPE_F64) ? (psVector *) f : psVectorCopy (NULL, f, PS_TYPE_F64);
+    psVector *x64 = NULL;
+    if (x != NULL) {
+        x64 = (x->type.type == PS_TYPE_F64) ? (psVector *) x : psVectorCopy (NULL, x, PS_TYPE_F64);
+    }
+    psVector *fErr64 = NULL;
+    if (fErr != NULL) {
+        fErr64 = (fErr->type.type == PS_TYPE_F64) ? (psVector *) fErr : psVectorCopy (NULL, fErr, PS_TYPE_F64);
+    }
+
+    bool result = true;
+
+    switch (poly->type) {
+    case PS_POLYNOMIAL_ORD:
+        result = VectorFitPolynomial1DOrd(poly, mask, maskValue, f64, fErr64, x64);
+        if (!result) {
+            psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning NULL.\n");
+        }
+        break;
+    case PS_POLYNOMIAL_CHEB:
+        if (mask != NULL) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring mask and maskValue with Chebyshev polynomials.\n");
+        }
+        if (fErr != NULL) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring error vector with Chebyshev polynomials.\n");
+        }
+        if (x == NULL) {
+            // If x==NULL, create an x64 vector with x values set to (-1:1).
+            PS_VECTOR_GEN_CHEBY_INDEX(x64, f64->n, PS_TYPE_F64);
+        }
+
+        result = vectorFitPolynomial1DCheb(poly, mask, maskValue, f64, fErr64, x64);
+        if (!result) {
+            psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning NULL.\n");
+        }
+
+        if (x == NULL) {
+            psFree(x64);
+        }
+        break;
+    default:
+        psError(PS_ERR_UNKNOWN, true, "Incorrect polynomial type (%d).  Returning NULL.\n", poly->type);
+        result = false;
+        break;
+    }
+
+    // Free psVectors that were created for NULL arguments.
+    PS_FREE_TEMP_F64_VECTOR (f, f64);
+    PS_FREE_TEMP_F64_VECTOR (x, x64);
+    PS_FREE_TEMP_F64_VECTOR (fErr, fErr64);
+
+    return result;
+}
+
+// This function accepts F32 and F64 input vectors.
+bool psVectorClipFitPolynomial1D(
+    psPolynomial1D *poly,
+    psStats *stats,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *xIn)
+{
+    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_POLY_NON_NULL(poly, false);
+    PS_ASSERT_PTR_NON_NULL(stats, false);
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
+    PS_ASSERT_VECTOR_NON_NULL(mask, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(mask, f, false);
+    PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(fErr, f, false);
+        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
+    }
+
+    // Internal pointers for possibly NULL vectors.
+    psVector *x = NULL;
+    if (xIn != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(xIn, f, false);
+        PS_ASSERT_VECTOR_TYPE(xIn, f->type.type, false);
+        x = (psVector *) xIn;
+    } else {
+        if (poly->type == PS_POLYNOMIAL_ORD) {
+            x = psVectorCreate(NULL, 0, f->n, 1, f->type.type);
+        } else if (poly->type == PS_POLYNOMIAL_CHEB) {
+            if (f->type.type == PS_TYPE_F32) {
+                PS_VECTOR_GEN_CHEBY_INDEX(x, f->n, PS_TYPE_F32);
+            } else if (f->type.type == PS_TYPE_F64) {
+                PS_VECTOR_GEN_CHEBY_INDEX(x, f->n, PS_TYPE_F64);
+            }
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "Error, bad poly type.\n");
+            return false;
+        }
+    }
+
+    // the user supplies one of various stats option pairs,
+    // determine the desired mean and stdev STATS options:
+    // XXX enforce consistency?
+    // XXX psStatsGetValue() probably has inverted precedence
+    psStatsOptions meanOption = stats->options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN | PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN_V2);
+    psStatsOptions stdevOption = stats->options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV | PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV_V2);
+    if (!meanOption) {
+        psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
+        return false;
+    }
+    if (!stdevOption) {
+        psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
+        return false;
+    }
+
+    // clipping range defined by min and max and/or clipSigma
+    psF32 minClipSigma;
+    psF32 maxClipSigma;
+    if (isfinite(stats->max)) {
+        maxClipSigma = fabs(stats->max);
+    } else {
+        maxClipSigma = fabs(stats->clipSigma);
+    }
+    if (isfinite(stats->min)) {
+        minClipSigma = fabs(stats->min);
+    } else {
+        minClipSigma = fabs(stats->clipSigma);
+    }
+    psVector *resid = psVectorAlloc(f->n, PS_TYPE_F64);
+
+    psTrace("psLib.math", 4, "stats->clipIter is %d\n", stats->clipIter);
+    psTrace("psLib.math", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
+
+    //
+    for (psS32 N = 0; N < stats->clipIter; N++) {
+        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial1D()\n", N);
+        psS32 Nkeep = 0;
+        if (psTraceGetLevel("psLib.math") >= 6) {
+            if (mask != NULL) {
+                for (psS32 i = 0 ; i < mask->n ; i++) {
+                    psTrace("psLib.math", 6,  "mask[%d] is %d\n", i, mask->data.U8[i]);
+                }
+            }
+        }
+        if (!psVectorFitPolynomial1D(poly, mask, maskValue, f, fErr, x)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
+            if (xIn == NULL) {
+                psFree(x);
+            }
+	    psFree(resid);
+	    
+            return false;
+        }
+
+        psVector *fit = psPolynomial1DEvalVector(poly, x);
+        if (fit == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial3DEvalVector().  Returning false.\n");
+            if (xIn == NULL) {
+                psFree(x);
+            }
+            psFree(resid);
+            return false;
+        }
+        for (psS32 i = 0 ; i < f->n ; i++) {
+            if (f->type.type == PS_TYPE_F64) {
+                resid->data.F64[i] = f->data.F64[i] - fit->data.F64[i];
+            } else {
+                resid->data.F64[i] = (psF64) (f->data.F32[i] - fit->data.F32[i]);
+            }
+        }
+        if (psTraceGetLevel("psLib.math") >= 6) {
+            if (mask != NULL) {
+                for (psS32 i = 0 ; i < mask->n ; i++) {
+                    if (!((mask != NULL) && (mask->data.U8[i] & maskValue))) {
+                        psTrace("psLib.math", 6,  "(f, fit)[%d] is (%f, %f).  resid is (%f)\n",
+                                i, f->data.F32[i], fit->data.F32[i], resid->data.F64[i]);
+                    }
+                }
+            }
+        }
+
+        if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not compute statistics on the resid vector.  Returning false.\n");
+            psFree(resid);
+            psFree(fit);
+            return false;
+        }
+
+        double meanValue = psStatsGetValue (stats, meanOption);
+        double stdevValue = psStatsGetValue (stats, stdevOption);
+
+        psTrace("psLib.math", 5, "Mean is %f\n", meanValue);
+        psTrace("psLib.math", 5, "Stdev is %f\n", stdevValue);
+        psF32 minClipValue = -minClipSigma*stdevValue;
+        psF32 maxClipValue = +maxClipSigma*stdevValue;
+
+        // set mask if pts are not valid
+        // we are masking out any point which is out of range
+        // recovery is not allowed with this scheme
+        for (psS32 i = 0; i < resid->n; i++) {
+            if ((mask != NULL) && (mask->data.U8[i] & maskValue)) {
+                continue;
+            }
+
+            if ((resid->data.F64[i] - meanValue > maxClipValue) || (resid->data.F64[i] - meanValue < minClipValue)) {
+                if (f->type.type == PS_TYPE_F64) {
+                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
+                            i, fit->data.F64[i], i, resid->data.F64[i]);
+                } else {
+                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
+                            i, fit->data.F32[i], i, resid->data.F64[i]);
+                }
+
+                if (mask != NULL) {
+                    mask->data.U8[i] |= 0x01;
+                }
+                continue;
+            }
+            Nkeep++;
+        }
+
+        //
+        // We should probably exit this loop if no new elements were masked
+        // since the polynomial fit won't change.
+        //
+        psTrace("psLib.math", 6, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
+        stats->clippedNvalues = Nkeep;
+        psFree(fit);
+    }
+
+    // Free psVectors that were created for NULL arguments.
+    if (xIn == NULL) {
+        psFree(x);
+    }
+    // Free other local temporary variables
+    psFree(resid);
+
+    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
+    return true;
+}
+
+
+/******************************************************************************
+ ******************************************************************************
+ 2-D Vector Code.
+ ******************************************************************************
+ *****************************************************************************/
+
+/******************************************************************************
+VectorFitPolynomial2DOrd(myPoly, *mask, maskValue, *f, *fErr, *x, *y): This is
+a private routine which will fit a 2-D polynomial to a set of (x, y)-(f)
+pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
+ 
+ *****************************************************************************/
+static bool VectorFitPolynomial2DOrd(
+    psPolynomial2D* myPoly,
+    const psVector* mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y)
+{
+    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_POLY_NON_NULL(myPoly, false);
+    PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
+    PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(y, fErr, false);
+        PS_ASSERT_VECTOR_TYPE(fErr, PS_TYPE_F64, false);
+    }
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
+    if (mask != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(y, mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+    }
+
+    // Number of polynomial terms
+    int nXterm = 1 + myPoly->nX;      // Number of terms in x
+    int nYterm = 1 + myPoly->nY;      // Number of terms in y
+    int nTerm = nXterm * nYterm;            // Total number of terms
+
+    psImage *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
+    psVector *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
+
+    // Initialize data structures.
+    if (!psImageInit(A, 0.0) || !psVectorInit(B, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "Could initialize data structures A, B.  Returning NULL.\n");
+        psFree(A);
+        psFree(B);
+        psTrace("psLib.math", 6, "---- %s() End ----\n", __func__);
+        return false;
+    }
+
+    // Dereference stuff, to make the loop go faster
+    psF64 **matrix = A->data.F64;       // Dereference the least-squares matrix
+    psF64 *vector = B->data.F64;        // Dereference the least-squares vector
+    psU8 **coeffMask = myPoly->coeffMask;     // Dereference mask for polynomial terms
+    psU8 *dataMask = NULL;              // Dereference mask for data
+    if (mask) {
+        dataMask = mask->data.U8;
+    }
+    psF64 *xData = x->data.F64;         // Dereference x
+    psF64 *yData = y->data.F64;         // Dereference y
+    psF64 *fData = f->data.F64;         // Dereference f
+    psF64 *fErrData = NULL;             // Dereference fErr
+    if (fErr) {
+        fErrData = fErr->data.F64;
+    }
+
+    // Build the least-squares matrix and vector
+    psImage *xySums = NULL;               // The sums: 1, x, x^2, ... x^(2n+1), y, xy, x^2y, ... x^(2n+1)
+    for (int k = 0; k < x->n; k++) {
+        if (dataMask && dataMask[k] & maskValue) {
+            continue;
+        }
+        xySums = BuildSums2D(xySums, xData[k], yData[k], nXterm, nYterm);
+        psF64 **sums = xySums->data.F64;// Dereference sums
+
+        double wt;                      // Weight
+        if (!fErrData) {
+            wt = 1.0;
+        } else {
+            // this filters fErr == 0 values
+            wt = (fErrData[k] == 0.0) ? 0.0 : 1.0 / PS_SQR(fErrData[k]);
+        }
+
+        // Iterating over the matrix
+        for (int i = 0; i < nTerm; i++) {
+            int l = i / nYterm;         // x index
+            int m = i % nYterm;         // y index
+            if (coeffMask[l][m] & PS_POLY_MASK_SET) {
+                matrix[i][i] = 1.0;
+                continue;
+            }
+            vector[i] += fData[k] * sums[l][m] * wt;
+            matrix[i][i] += sums[2*l][2*m] * wt; // The diagonal entry
+            for (int j = i + 1; j < nTerm; j++) { // Doing the upper diagonal only: we will use symmetry
+                int p = j / nYterm;     // x index
+                int q = j % nYterm;     // y index
+                if (coeffMask[p][q] & PS_POLY_MASK_SET) {
+                    continue;
+                }
+                double value = sums[l+p][m+q] * wt; // Value to add in
+                matrix[i][j] += value;
+                matrix[j][i] += value;  // Taking advantage of the symmetry
+            }
+        }
+    }
+    psFree(xySums);
+
+    // elements which are masked for fitting need to be subtracted from the vector
+    for (int i = 0; i < nTerm; i++) {
+	int ix = i / nYterm;         // x index
+	int iy = i % nYterm;         // y index
+	if (coeffMask[ix][iy] & PS_POLY_MASK_BOTH) {
+	    continue;
+	}
+	for (int j = 0; j < nTerm; j++) { // The upper diagonal only: we will use symmetry
+	    int jx = j / nYterm;         // x index
+	    int jy = j % nYterm;         // y index
+	    if (coeffMask[jx][jy] & PS_POLY_MASK_SET) {
+		continue;
+	    }
+	    if (!(coeffMask[jx][jy] & PS_POLY_MASK_FIT)) {
+		continue;
+	    }
+	    vector[i] -= matrix[i][j]*myPoly->coeff[jx][jy];
+	}
+    }
+    
+    // set the un-fitted and un-set elements to 0 or 1 for pivots
+    for (int i = 0; i < nTerm; i++) {
+	int ix = i / nYterm;         // x index
+	int iy = i % nYterm;         // y index
+	if (coeffMask[ix][iy] & PS_POLY_MASK_BOTH) {
+	    for (int j = 0; j < nTerm; j++) { // The upper diagonal only: we will use symmetry
+		matrix[i][j] = 0.0;
+		matrix[j][i] = 0.0;
+	    }
+	    matrix[i][i] = 1.0;
+	    continue;
+	}
+    }
+
+    if (psTraceGetLevel("psLib.math") >= 4) {
+        printf("Least-squares vector:\n");
+        for (int i = 0; i < nTerm; i++) {
+            printf("%f ", B->data.F64[i]);
+        }
+        printf("\n");
+        printf("Least-squares matrix:\n");
+        for (int i = 0; i < nTerm; i++) {
+            for (int j = 0; j < nTerm; j++) {
+                printf("%f ", A->data.F64[i][j]);
+            }
+            printf("\n");
+        }
+    }
+
+    if (!psMatrixGJSolve(A, B)) {
+        psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.  Returning NULL.\n");
+        psFree(A);
+        psFree(B);
+        return false;
+    }
+
+    // select the appropriate solution entries
+    for (int i = 0; i < nTerm; i++) {
+        int l = i / nYterm;         // x index
+        int m = i % nYterm;         // y index
+
+	// retain the incoming values if masked on the fit
+	if (coeffMask[l][m] & PS_POLY_MASK_FIT) continue;
+	myPoly->coeff[l][m] = B->data.F64[i];
+	myPoly->coeffErr[l][m] = sqrt(A->data.F64[i][i]);
+    }
+    psFree(A);
+    psFree(B);
+
+    psTrace("psLib.math", 6, "---- %s() end ----\n", __func__);
+    return true;
+}
+
+/******************************************************************************
+psVectorFitPolynomial2D():  This routine fits a 2D polynomial of arbitrary
+degree (specified in poly) to the data points (x, y)-(f) and returns that
+polynomial.  Types F32 and F64 are supported, however, type F32 is done via
+vector conversion only.
+ *****************************************************************************/
+bool psVectorFitPolynomial2D(
+    psPolynomial2D *poly,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, false);
+    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
+    if (mask != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+    }
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
+        PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, false);
+    }
+
+    // Convert input vectors to F64 if necessary.
+    psVector *f64 = (f->type.type == PS_TYPE_F64) ? (psVector *) f : psVectorCopy(NULL, f, PS_TYPE_F64);
+    psVector *x64 = (x->type.type == PS_TYPE_F64) ? (psVector *) x : psVectorCopy(NULL, x, PS_TYPE_F64);
+    psVector *y64 = (y->type.type == PS_TYPE_F64) ? (psVector *) y : psVectorCopy(NULL, y, PS_TYPE_F64);
+
+    psVector *fErr64 = NULL;
+    if (fErr != NULL) {
+        fErr64 = (fErr->type.type == PS_TYPE_F64) ? (psVector *) fErr : psVectorCopy(NULL, fErr, PS_TYPE_F64);
+    }
+
+    bool result = true;
+
+    switch (poly->type) {
+    case PS_POLYNOMIAL_ORD:
+        result = VectorFitPolynomial2DOrd(poly, mask, maskValue, f64, fErr64, x64, y64);
+        if (!result) {
+            psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
+        }
+        break;
+    case PS_POLYNOMIAL_CHEB:
+        if (mask != NULL) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring mask and maskValue with Chebyshev polynomials.\n");
+        }
+        psError(PS_ERR_UNKNOWN, true, "2-D Chebyshev polynomial vector fitting has not been implemented.  Returning NULL.\n");
+        result = false;
+        break;
+    default:
+        psError(PS_ERR_UNKNOWN, true, "Incorrect polynomial type.  Returning NULL.\n");
+        result = false;
+        break;
+    }
+
+    // Free psVectors that were created for NULL arguments.
+    PS_FREE_TEMP_F64_VECTOR (f, f64);
+    PS_FREE_TEMP_F64_VECTOR (x, x64);
+    PS_FREE_TEMP_F64_VECTOR (y, y64);
+    PS_FREE_TEMP_F64_VECTOR (fErr, fErr64);
+
+    return result;
+}
+
+bool psVectorClipFitPolynomial2D(
+    psPolynomial2D *poly,
+    psStats *stats,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y)
+{
+    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_POLY_NON_NULL(poly, false);
+    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
+    PS_ASSERT_PTR_NON_NULL(stats, false);
+    PS_ASSERT_VECTOR_NON_NULL(mask, false);
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+    PS_ASSERT_VECTOR_TYPE(x, f->type.type, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
+    PS_ASSERT_VECTOR_TYPE(y, f->type.type, false);
+
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
+    PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
+        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
+    }
+
+    // the user supplies one of various stats option pairs,
+    // determine the desired mean and stdev STATS options:
+    // XXX enforce consistency?
+    // XXX psStatsGetValue() probably has inverted precedence
+    psStatsOptions meanOption = stats->options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN | PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN_V2);
+    psStatsOptions stdevOption = stats->options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV | PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV_V2);
+    if (!meanOption) {
+        psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
+        return false;
+    }
+    if (!stdevOption) {
+        psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
+        return false;
+    }
+
+    // clipping range defined by min and max and/or clipSigma
+    psF32 minClipSigma;
+    psF32 maxClipSigma;
+    if (isfinite(stats->max)) {
+        maxClipSigma = fabs(stats->max);
+    } else {
+        maxClipSigma = fabs(stats->clipSigma);
+    }
+    if (isfinite(stats->min)) {
+        minClipSigma = fabs(stats->min);
+    } else {
+        minClipSigma = fabs(stats->clipSigma);
+    }
+    psVector *resid = psVectorAlloc(f->n, PS_TYPE_F64);
+
+    psTrace("psLib.math", 4, "stats->clipIter is %d\n", stats->clipIter);
+    psTrace("psLib.math", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
+
+    for (psS32 N = 0; N < stats->clipIter; N++) {
+        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial1D()\n", N);
+        psS32 Nkeep = 0;
+        if (psTraceGetLevel("psLib.math") >= 7) {
+            if (mask != NULL) {
+                for (psS32 i = 0 ; i < mask->n ; i++) {
+                    psTrace("psLib.math", 7,  "mask[%d] is %d\n", i, mask->data.U8[i]);
+                }
+            }
+        }
+
+        if (!psVectorFitPolynomial2D(poly, mask, maskValue, f, fErr, x, y)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the data.  Returning false.\n");
+            psFree(resid);
+            return false;
+        }
+
+        psVector *fit = psPolynomial2DEvalVector(poly, x, y);
+        if (fit == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial3DEvalVector().  Returning NULL.\n");
+            psFree(resid);
+            return false;
+        }
+
+        for (psS32 i = 0 ; i < f->n ; i++) {
+            if (f->type.type == PS_TYPE_F64) {
+                resid->data.F64[i] = f->data.F64[i] - fit->data.F64[i];
+            } else {
+                resid->data.F64[i] = (psF64) (f->data.F32[i] - fit->data.F32[i]);
+            }
+        }
+
+        if (psTraceGetLevel("psLib.math") >= 7) {
+            if (mask != NULL) {
+                for (psS32 i = 0 ; i < mask->n ; i++) {
+                    if (!((mask != NULL) && (mask->data.U8[i] & maskValue))) {
+                        psTrace("psLib.math", 7,  "point %d at %f %f : value, fit : %f  %f resid: %f\n",
+                                i, x->data.F32[i], y->data.F32[i], f->data.F32[i], fit->data.F32[i], resid->data.F64[i]);
+                    }
+                }
+            }
+        }
+
+        if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not compute statistics on the resid vector.  Returning NULL.\n");
+            psFree(resid);
+            psFree(fit);
+            return false;
+        }
+
+        double meanValue = psStatsGetValue (stats, meanOption);
+        double stdevValue = psStatsGetValue (stats, stdevOption);
+
+        psTrace("psLib.math", 5, "Mean is %f\n", meanValue);
+        psTrace("psLib.math", 5, "Stdev is %f\n", stdevValue);
+        psF32 minClipValue = -minClipSigma*stdevValue;
+        psF32 maxClipValue = +maxClipSigma*stdevValue;
+
+        // set mask if pts are not valid
+        // we are masking out any point which is out of range
+        // recovery is not allowed with this scheme
+        for (psS32 i = 0; i < resid->n; i++) {
+            if ((mask != NULL) && (mask->data.U8[i] & maskValue)) {
+                continue;
+            }
+
+            if ((resid->data.F64[i] - meanValue > maxClipValue) || (resid->data.F64[i] - meanValue < minClipValue)) {
+                if (fit->type.type == PS_TYPE_F64) {
+                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
+                            i, fit->data.F64[i], i, resid->data.F64[i]);
+                } else {
+                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
+                            i, fit->data.F32[i], i, resid->data.F64[i]);
+                }
+
+                if (mask != NULL) {
+                    mask->data.U8[i] |= 0x01;
+                }
+                continue;
+            }
+            Nkeep++;
+        }
+        psTrace("psLib.math", 4, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
+        stats->clippedNvalues = Nkeep;
+        psFree(fit);
+    }
+    // Free local temporary variables
+    psFree(resid);
+
+    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
+    return true;
+}
+
+
+/******************************************************************************
+ ******************************************************************************
+ 3-D Vector Code.
+ ******************************************************************************
+ *****************************************************************************/
+
+/******************************************************************************
+VectorFitPolynomial3DOrd(myPoly, *mask, maskValue, *f, *fErr, *x, *y, *z):
+This is a private routine which will fit a 3-D polynomial to a set of (x,
+y, z)-(f) pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
+ 
+ *****************************************************************************/
+static bool VectorFitPolynomial3DOrd(
+    psPolynomial3D* myPoly,
+    const psVector* mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z)
+{
+    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_POLY_NON_NULL(myPoly, false);
+    PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
+    PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
+    PS_ASSERT_INT_NONNEGATIVE(myPoly->nZ, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(y, fErr, false);
+        PS_ASSERT_VECTOR_TYPE(fErr, PS_TYPE_F64, false);
+    }
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
+    PS_ASSERT_VECTOR_NON_NULL(z, false);
+    PS_ASSERT_VECTOR_TYPE(z, PS_TYPE_F64, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
+    if (mask != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+    }
+
+    int nXterm = 1 + myPoly->nX;        // Number of x terms
+    int nYterm = 1 + myPoly->nY;        // Number of y terms
+    int nZterm = 1 + myPoly->nZ;        // Number of z terms
+    int nTerm = nXterm * nYterm * nZterm; // Total number of terms
+    int nData = x->n;                   // Number of data points
+    psImage    *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
+    psVector   *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
+
+    // Initialize data structures.
+    if (!psImageInit(A, 0.0) || !psVectorInit(B, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "Could initialize data structures A, B.  Returning NULL.\n");
+        psFree(A);
+        psFree(B);
+        psTrace("psLib.math", 4, "---- %s() End ----\n", __func__);
+        return false;
+    }
+
+    // Dereference points for speed in the loop
+    psF64 **matrix = A->data.F64;       // Least-squares matrix
+    psF64 *vector = B->data.F64;        // Least-squares vector
+    psF64 *xData = x->data.F64;         // x
+    psF64 *yData = y->data.F64;         // y
+    psF64 *zData = z->data.F64;         // z
+    psF64 *fData = f->data.F64;         // f
+    psF64 *fErrData = NULL;             // Error in f
+    if (fErr) {
+        fErrData = fErr->data.F64;
+    }
+    psU8 *dataMask = NULL;              // Mask for data
+    if (mask) {
+        dataMask = mask->data.U8;
+    }
+    psU8 ***coeffMask = myPoly->coeffMask;    // Mask for polynomial terms
+    int nYZterm = nYterm * nZterm;      // Multiplication of the numbers, to calculate the index
+
+    // Build the B and A data structs.
+    psF64 ***Sums = NULL;         // Sums look like: 1, x, x^2, ... x^(2n+1), y, xy, x^2y, ... x^(2n+1)*y, ...
+    for (int k = 0; k < nData; k++) {
+        if (dataMask && dataMask[k] & maskValue) {
+            continue;
+        }
+
+        Sums = BuildSums3D(Sums, xData[k], yData[k], zData[k], nXterm, nYterm, nZterm);
+
+        double wt;
+        if (fErr == NULL) {
+            wt = 1.0;
+        } else {
+            // this filters fErr == 0 values
+            wt = (fErr->data.F64[k] == 0.0) ? 0.0 : 1.0 / PS_SQR(fErrData[k]);
+        }
+
+        for (int i = 0; i < nTerm; i++) {
+            int ix = i / nYZterm; // x index
+            int iy = (i % nYZterm) / nZterm; // y index
+            int iz = (i % nYZterm) % nZterm; // z index
+            if (coeffMask[ix][iy][iz] & PS_POLY_MASK_BOTH) {
+                matrix[i][i] = 1.0;
+                continue;
+            }
+
+            vector[i] += fData[k] * Sums[ix][iy][iz] * wt;
+            matrix[i][i] += Sums[2*ix][2*iy][2*iz] * wt;
+            for (int j = i + 1; j < nTerm; j++) {
+                int jx = j / (nYZterm); // x index
+                int jy = (j % nYZterm) / nZterm; // y index
+                int jz = (j % nYZterm) % nZterm; // z index
+                if (coeffMask[jx][jy][jz] & PS_POLY_MASK_BOTH) {
+                    continue;
+                }
+                double value = Sums[ix+jx][iy+jy][iz+jz] * wt;
+                matrix[i][j] += value;
+                matrix[j][i] += value;
+            }
+        }
+    }
+
+    // Free the sums
+    for (psS32 ix = 0; ix < 2*nXterm; ix++) {
+        for (psS32 iy = 0; iy < 2*nYterm; iy++) {
+            psFree(Sums[ix][iy]);
+        }
+        psFree(Sums[ix]);
+    }
+    psFree(Sums);
+
+
+    // XXX: rel10_ifa used psMatrixGJSolve().  However, this failed tests.  So, I'm using psMatrixLUD().
+    if (USE_GAUSS_JORDAN) {
+        // does the solution in place
+        // The matrices were overflowing, so I switched to LUD.
+        if (!psMatrixGJSolve(A, B)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to perform GaussJordan elimination.\n");
+            goto escape_GJ;
+        }
+        // select the appropriate solution entries
+        for (int i = 0; i < nTerm; i++) {
+            int ix = i / nYZterm; // x index
+            int iy = (i % nYZterm) / nZterm; // y index
+            int iz = (i % nYZterm) % nZterm; // z index
+	    if (coeffMask[ix][iy][iz] & PS_POLY_MASK_FIT) continue;
+            myPoly->coeff[ix][iy][iz] = B->data.F64[i];
+            myPoly->coeffErr[ix][iy][iz] = sqrt(A->data.F64[i][i]);
+        }
+    } else {
+        // LUD version of the fit
+        psImage *ALUD = NULL;
+        psVector* outPerm = NULL;
+        psVector* coeffs = NULL;
+
+        ALUD = psImageAlloc(nTerm, nTerm, PS_TYPE_F64);
+        ALUD = psMatrixLUD(ALUD, &outPerm, A);
+        if (ALUD == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not do LUD decomposition on matrix.  Returning NULL.\n");
+            goto escape_LUD;
+        } else {
+            coeffs = psMatrixLUSolve(coeffs, ALUD, B, outPerm);
+            if (coeffs == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "Could not solve LUD matrix.  Returning NULL.\n");
+                goto escape_LUD;
+            } else {
+                // select the appropriate solution entries
+                for (psS32 ix = 0; ix < nXterm; ix++) {
+                    for (psS32 iy = 0; iy < nYterm; iy++) {
+                        for (psS32 iz = 0; iz < nZterm; iz++) {
+                            psS32 nx = ix+iy*nXterm+iz*nXterm*nYterm;
+			    if (coeffMask[ix][iy][iz] & PS_POLY_MASK_FIT) continue;
+                            myPoly->coeff[ix][iy][iz] = coeffs->data.F64[nx];
+                            // XXX myPoly->coeffErr[ix][iy][iz] = sqrt(A->data.F64[nx][nx]);
+                            // XXX this is wrong: A is not replaced with inverse(A), as for GaussJordan
+                        }
+                    }
+                }
+            }
+        }
+
+        psFree(ALUD);
+        psFree(coeffs);
+        psFree(outPerm);
+    }
+    psFree(A);
+    psFree(B);
+
+    psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
+    return true;
+
+escape_LUD:
+    psFree(A);
+    psFree(B);
+    return false;
+
+escape_GJ:
+
+    psFree(A);
+    psFree(B);
+    return false;
+}
+
+/******************************************************************************
+psVectorFitPolynomial3D():  This routine fits a 3D polynomial of arbitrary
+degree (specified in poly) to the data points (x, y, z)-(f) and returns that
+polynomial.  Types F32 and F64 are supported, however, type F32 is done via
+vector conversion only.
+ *****************************************************************************/
+bool psVectorFitPolynomial3D(
+    psPolynomial3D *poly,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, false);
+    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
+    PS_ASSERT_VECTOR_NON_NULL(z, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
+    if (mask != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+    }
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
+        PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, false);
+    }
+
+    // Convert input vectors to F64 if necessary.
+    psVector *f64 = (f->type.type == PS_TYPE_F64) ? (psVector *) f : psVectorCopy(NULL, f, PS_TYPE_F64);
+    psVector *x64 = (x->type.type == PS_TYPE_F64) ? (psVector *) x : psVectorCopy(NULL, x, PS_TYPE_F64);
+    psVector *y64 = (y->type.type == PS_TYPE_F64) ? (psVector *) y : psVectorCopy(NULL, y, PS_TYPE_F64);
+    psVector *z64 = (z->type.type == PS_TYPE_F64) ? (psVector *) z : psVectorCopy(NULL, z, PS_TYPE_F64);
+
+    psVector *fErr64 = NULL;
+    if (fErr != NULL) {
+        fErr64 = (fErr->type.type == PS_TYPE_F64) ? (psVector *) fErr : psVectorCopy(NULL, fErr, PS_TYPE_F64);
+    }
+
+    bool result = true;
+
+    switch (poly->type) {
+    case PS_POLYNOMIAL_ORD:
+        result = VectorFitPolynomial3DOrd(poly, mask, maskValue, f64, fErr64, x64, y64, z64);
+        if (!result) {
+            psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
+        }
+        break;
+    case PS_POLYNOMIAL_CHEB:
+        if (mask != NULL) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring mask and maskValue with Chebyshev polynomials.\n");
+        }
+        psError(PS_ERR_UNKNOWN, true, "3-D Chebyshev polynomial vector fitting has not been implemented.  Returning NULL.\n");
+        result = false;
+        break;
+    default:
+        psError(PS_ERR_UNKNOWN, true, "Incorrect polynomial type.  Returning NULL.\n");
+        result = false;
+        break;
+    }
+
+    // Free psVectors that were created for NULL arguments.
+    PS_FREE_TEMP_F64_VECTOR (f, f64);
+    PS_FREE_TEMP_F64_VECTOR (x, x64);
+    PS_FREE_TEMP_F64_VECTOR (y, y64);
+    PS_FREE_TEMP_F64_VECTOR (z, z64);
+    PS_FREE_TEMP_F64_VECTOR (fErr, fErr64);
+
+    return result;
+}
+
+bool psVectorClipFitPolynomial3D(
+    psPolynomial3D *poly,
+    psStats *stats,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z)
+{
+    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_POLY_NON_NULL(poly, false);
+    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
+    PS_ASSERT_PTR_NON_NULL(stats, false);
+    PS_ASSERT_VECTOR_NON_NULL(mask, false);
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+    PS_ASSERT_VECTOR_TYPE(x, f->type.type, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
+    PS_ASSERT_VECTOR_TYPE(y, f->type.type, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(z, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
+    PS_ASSERT_VECTOR_TYPE(z, f->type.type, false);
+
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
+    PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
+        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
+    }
+
+    // the user supplies one of various stats option pairs,
+    // determine the desired mean and stdev STATS options:
+    // XXX enforce consistency?
+    // XXX psStatsGetValue() probably has inverted precedence
+    psStatsOptions meanOption = stats->options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN | PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN_V2);
+    psStatsOptions stdevOption = stats->options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV | PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV_V2);
+    if (!meanOption) {
+        psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
+        return false;
+    }
+    if (!stdevOption) {
+        psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
+        return false;
+    }
+
+    // clipping range defined by min and max and/or clipSigma
+    psF32 minClipSigma;
+    psF32 maxClipSigma;
+    if (isfinite(stats->max)) {
+        maxClipSigma = fabs(stats->max);
+    } else {
+        maxClipSigma = fabs(stats->clipSigma);
+    }
+    if (isfinite(stats->min)) {
+        minClipSigma = fabs(stats->min);
+    } else {
+        minClipSigma = fabs(stats->clipSigma);
+    }
+    psVector *resid = psVectorAlloc(f->n, PS_TYPE_F64);
+
+    psTrace("psLib.math", 4, "stats->clipIter is %d\n", stats->clipIter);
+    psTrace("psLib.math", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
+
+    for (psS32 N = 0; N < stats->clipIter; N++) {
+        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial1D()\n", N);
+        psS32 Nkeep = 0;
+        if (psTraceGetLevel("psLib.math") >= 6) {
+            if (mask != NULL) {
+                for (psS32 i = 0 ; i < mask->n ; i++) {
+                    psTrace("psLib.math", 6,  "mask[%d] is %d\n", i, mask->data.U8[i]);
+                }
+            }
+        }
+
+        if (!psVectorFitPolynomial3D(poly, mask, maskValue, f, fErr, x, y, z)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the data.  Returning NULL.\n");
+            psFree(resid);
+            return false;
+        }
+        psVector *fit = psPolynomial3DEvalVector(poly, x, y, z);
+        if (fit == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial3DEvalVector().  Returning NULL.\n");
+            psFree(resid);
+            return false;
+        }
+        for (psS32 i = 0 ; i < f->n ; i++) {
+            if (f->type.type == PS_TYPE_F64) {
+                resid->data.F64[i] = f->data.F64[i] - fit->data.F64[i];
+            } else {
+                resid->data.F64[i] = ((psF64) f->data.F32[i]) - fit->data.F64[i];
+            }
+        }
+
+        if (psTraceGetLevel("psLib.math") >= 6) {
+            if (mask != NULL) {
+                for (psS32 i = 0 ; i < mask->n ; i++) {
+                    if (!((mask != NULL) && (mask->data.U8[i] & maskValue))) {
+                        psTrace("psLib.math", 6,  "(f, fit)[%d] is (%f, %f).  resid is (%f)\n",
+                                i, f->data.F32[i], fit->data.F32[i], resid->data.F64[i]);
+                    }
+                }
+            }
+        }
+
+        if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not compute statistics on the resid vector.  Returning NULL.\n");
+            psFree(resid);
+            psFree(fit);
+            return false;
+        }
+
+        double meanValue = psStatsGetValue (stats, meanOption);
+        double stdevValue = psStatsGetValue (stats, stdevOption);
+
+        psTrace("psLib.math", 5, "Mean is %f\n", meanValue);
+        psTrace("psLib.math", 5, "Stdev is %f\n", stdevValue);
+        psF32 minClipValue = -minClipSigma*stdevValue;
+        psF32 maxClipValue = +maxClipSigma*stdevValue;
+
+        // set mask if pts are not valid
+        // we are masking out any point which is out of range
+        // recovery is not allowed with this scheme
+        for (psS32 i = 0; i < resid->n; i++) {
+            if ((mask != NULL) && (mask->data.U8[i] & maskValue)) {
+                continue;
+            }
+
+            if ((resid->data.F64[i] - meanValue > maxClipValue) || (resid->data.F64[i] - meanValue < minClipValue))  {
+                if (f->type.type == PS_TYPE_F64) {
+                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
+                            i, fit->data.F64[i], i, resid->data.F64[i]);
+                } else {
+                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
+                            i, fit->data.F32[i], i, resid->data.F64[i]);
+                }
+
+                if (mask != NULL) {
+                    mask->data.U8[i] |= 0x01;
+                }
+                continue;
+            }
+            Nkeep++;
+        }
+        psTrace("psLib.math", 6, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
+        stats->clippedNvalues = Nkeep;
+        psFree(fit);
+    }
+    // Free local temporary variables
+    psFree(resid);
+
+    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
+    return true;
+}
+
+/******************************************************************************
+ ******************************************************************************
+ 4-D Vector Code.
+ ******************************************************************************
+ *****************************************************************************/
+/******************************************************************************
+VectorFitPolynomial4DOrd(myPoly, *mask, maskValue, *f, *fErr, *x, *y, *z, *t):
+This is a private routine which will fit a 4-D polynomial to a set of (x,
+y, z, t)-(f) pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
+ 
+ *****************************************************************************/
+static bool VectorFitPolynomial4DOrd(
+    psPolynomial4D* myPoly,
+    const psVector* mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z,
+    const psVector *t)
+{
+    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_POLY_NON_NULL(myPoly, false);
+    PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
+    PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
+    PS_ASSERT_INT_NONNEGATIVE(myPoly->nZ, false);
+    PS_ASSERT_INT_NONNEGATIVE(myPoly->nT, false);
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(y, fErr, false);
+        PS_ASSERT_VECTOR_TYPE(fErr, PS_TYPE_F64, false);
+    }
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
+    PS_ASSERT_VECTOR_NON_NULL(z, false);
+    PS_ASSERT_VECTOR_TYPE(z, PS_TYPE_F64, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
+    PS_ASSERT_VECTOR_NON_NULL(t, false);
+    PS_ASSERT_VECTOR_TYPE(t, PS_TYPE_F64, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, t, false);
+    if (mask) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(y, mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+    }
+
+
+    int nXterm = 1 + myPoly->nX;        // Number of x terms
+    int nYterm = 1 + myPoly->nY;        // Number of y terms
+    int nZterm = 1 + myPoly->nZ;        // Number of z terms
+    int nTterm = 1 + myPoly->nT;        // Number of t terms
+    int nTerm = nXterm * nYterm * nZterm * nTterm; // Total number of terms
+    int nData = x->n;                   // Number of data points
+    psImage    *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
+    psVector   *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
+
+    // Initialize data structures.
+    if (!psImageInit(A, 0.0) || !psVectorInit(B, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "Could initialize data structures A, B.  Returning NULL.\n");
+        psFree(A);
+        psFree(B);
+        psTrace("psLib.math", 4, "---- %s() End ----\n", __func__);
+        return false;
+    }
+
+    // Dereference points for speed in the loop
+    psF64 **matrix = A->data.F64;       // Least-squares matrix
+    psF64 *vector = B->data.F64;        // Least-squares vector
+    psF64 *xData = x->data.F64;         // x
+    psF64 *yData = y->data.F64;         // y
+    psF64 *zData = z->data.F64;         // z
+    psF64 *tData = t->data.F64;         // t
+    psF64 *fData = f->data.F64;         // f
+    psF64 *fErrData = NULL;             // Error in f
+    if (fErr) {
+        fErrData = fErr->data.F64;
+    }
+    psU8 *dataMask = NULL;              // Mask for data
+    if (mask) {
+        dataMask = mask->data.U8;
+    }
+    psU8 ****coeffMask = myPoly->coeffMask;    // Mask for polynomial terms
+    int nYZTterm = nYterm * nZterm * nTterm; // Multiplication of the numbers, for calculating the index
+    int nZTterm = nZterm * nTterm;      // Multiplication of the numbers, for calculating the index
+
+    // Build the B and A data structs.
+    psF64 ****Sums = NULL;        // Sums look like: 1, x, x^2, ... x^(2n+1), y, xy, x^2y, ... x^(2n+1)*y, ...
+    for (int k = 0; k < nData; k++) {
+        if (dataMask && dataMask[k] & maskValue) {
+            continue;
+        }
+
+        Sums = BuildSums4D(Sums, xData[k], yData[k], zData[k], tData[k], nXterm, nYterm, nZterm, nTterm);
+
+        double wt;
+        if (fErr == NULL) {
+            wt = 1.0;
+        } else {
+            // this filters fErr == 0 values
+            wt = (fErr->data.F64[k] == 0.0) ? 0.0 : 1.0 / PS_SQR(fErrData[k]);
+        }
+
+        for (int i = 0; i < nTerm; i++) {
+            int ix = i / (nYZTterm); // x index
+            int iy = (i % (nYZTterm)) / (nZTterm); // y index
+            int iz = ((i % (nYZTterm)) % (nZTterm)) / nTterm; // z index
+            int it = ((i % (nYZTterm)) % (nZTterm)) % nTterm; // t index
+            if (coeffMask[ix][iy][iz][it] & PS_POLY_MASK_BOTH) {
+                matrix[i][i] = 1.0;
+                continue;
+            }
+
+            vector[i] += fData[k] * Sums[ix][iy][iz][it] * wt;
+            matrix[i][i] += Sums[2*ix][2*iy][2*iz][2*it] * wt;
+            for (int j = i + 1; j < nTerm; j++) {
+                int jx = j / nYZTterm; // x index
+                int jy = (j % nYZTterm) / nZTterm; // y index
+                int jz = ((j % nYZTterm) % nZTterm) / nTterm; // z index
+                int jt = ((j % nYZTterm) % nZTterm) % nTterm; // t index
+                if (coeffMask[jx][jy][jz][jt] & PS_POLY_MASK_BOTH) {
+                    continue;
+                }
+                double value = Sums[ix+jx][iy+jy][iz+jz][it+jt] * wt;
+                matrix[i][j] += value;
+                matrix[j][i] += value;
+            }
+        }
+    }
+
+    // Free the sums
+    if (Sums == NULL) {
+        assert (nData == 0);
+    } else {
+        for (int ix = 0; ix < 2*nXterm; ix++) {
+            for (int iy = 0; iy < 2*nYterm; iy++) {
+                for (int iz = 0; iz < 2*nZterm; iz++) {
+                    psFree(Sums[ix][iy][iz]);
+                }
+                psFree(Sums[ix][iy]);
+            }
+            psFree(Sums[ix]);
+        }
+        psFree(Sums);
+    }
+
+    // XXX: rel10_ifa used psMatrixGJSolve().  However, this failed tests.  So, I'm using psMatrixLUD().
+    if (USE_GAUSS_JORDAN) {
+        // does the solution in place
+        // The GaussJordan version was overflowing, so I'm using LUD.
+        if (!psMatrixGJSolve(A, B)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to perform GaussJordan elimination.\n");
+            goto escape_GJ;
+        }
+
+        // select the appropriate solution entries
+        for (int i = 0; i < nTerm; i++) {
+            int ix = i / nYZTterm; // x index
+            int iy = (i % nYZTterm) / nZTterm; // y index
+            int iz = ((i % nYZTterm) % nZTterm) / nTterm; // z index
+            int it = ((i % nYZTterm) % nZTterm) % nTterm; // t index
+	    if (coeffMask[ix][iy][iz][it] & PS_POLY_MASK_FIT) continue;
+            myPoly->coeff[ix][iy][iz][it] = B->data.F64[i];
+            myPoly->coeffErr[ix][iy][iz][it] = sqrt(A->data.F64[i][i]);
+        }
+    } else {
+        // LUD version of the fit
+        psImage *ALUD = NULL;
+        psVector* outPerm = NULL;
+        psVector* coeffs = NULL;
+
+        ALUD = psImageAlloc(nTerm, nTerm, PS_TYPE_F64);
+        ALUD = psMatrixLUD(ALUD, &outPerm, A);
+        if (ALUD == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not do LUD decomposition on matrix.  Returning NULL.\n");
+            goto escape_LUD;
+        } else {
+            coeffs = psMatrixLUSolve(coeffs, ALUD, B, outPerm);
+            if (coeffs == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "Could not solve LUD matrix.  Returning NULL.\n");
+                goto escape_LUD;
+            } else {
+                // select the appropriate solution entries
+                for (psS32 ix = 0; ix < nXterm; ix++) {
+                    for (psS32 iy = 0; iy < nYterm; iy++) {
+                        for (psS32 iz = 0; iz < nZterm; iz++) {
+                            for (psS32 it = 0; it < nTterm; it++) {
+                                psS32 nx = ix+iy*nXterm+iz*nXterm*nYterm+it*nXterm*nYterm*nZterm;
+				if (coeffMask[ix][iy][iz][it] & PS_POLY_MASK_FIT) continue;
+                                myPoly->coeff[ix][iy][iz][it] = coeffs->data.F64[nx];
+                                // myPoly->coeffErr[ix][iy][iz][it] = sqrt(A->data.F64[nx][nx]);
+                                // XXX this is wrong: LUD does not supply inverse(A)
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        psFree(ALUD);
+        psFree(coeffs);
+        psFree(outPerm);
+    }
+    psFree(A);
+    psFree(B);
+
+    psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
+    return true;
+
+escape_LUD:
+    psFree(A);
+    psFree(B);
+    return false;
+
+escape_GJ:
+    psFree(A);
+    psFree(B);
+    return false;
+}
+
+/******************************************************************************
+psVectorFitPolynomial4D():  This routine fits a 4D polynomial of arbitrary
+degree (specified in poly) to the data points (x, y, z, t)-(f) and returns
+that polynomial.  Types F32 and F64 are supported, however, type F32 is done
+via vector conversion only.
+ *****************************************************************************/
+bool psVectorFitPolynomial4D(
+    psPolynomial4D *poly,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z,
+    const psVector *t)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, false);
+    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
+    PS_ASSERT_VECTOR_NON_NULL(z, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
+    PS_ASSERT_VECTOR_NON_NULL(t, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, t, false);
+    if (mask) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+    }
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
+        PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, false);
+    }
+
+    // Convert input vectors to F64 if necessary.
+    psVector *f64 = (f->type.type == PS_TYPE_F64) ? (psVector *) f : psVectorCopy(NULL, f, PS_TYPE_F64);
+    psVector *x64 = (x->type.type == PS_TYPE_F64) ? (psVector *) x : psVectorCopy(NULL, x, PS_TYPE_F64);
+    psVector *y64 = (y->type.type == PS_TYPE_F64) ? (psVector *) y : psVectorCopy(NULL, y, PS_TYPE_F64);
+    psVector *z64 = (z->type.type == PS_TYPE_F64) ? (psVector *) z : psVectorCopy(NULL, z, PS_TYPE_F64);
+    psVector *t64 = (t->type.type == PS_TYPE_F64) ? (psVector *) t : psVectorCopy(NULL, t, PS_TYPE_F64);
+
+    psVector *fErr64 = NULL;
+    if (fErr != NULL) {
+        fErr64 = (fErr->type.type == PS_TYPE_F64) ? (psVector *) fErr : psVectorCopy(NULL, fErr, PS_TYPE_F64);
+    }
+
+    bool result = true;
+
+    switch (poly->type) {
+    case PS_POLYNOMIAL_ORD:
+        result = VectorFitPolynomial4DOrd(poly, mask, maskValue, f64, fErr64, x64, y64, z64, t64);
+        if (!result) {
+            psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
+        }
+        break;
+    case PS_POLYNOMIAL_CHEB:
+        if (mask != NULL) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring mask and maskValue with Chebyshev polynomials.\n");
+        }
+        psError(PS_ERR_UNKNOWN, true, "4-D Chebyshev polynomial vector fitting has not been implemented.  Returning NULL.\n");
+        result = false;
+        break;
+    default:
+        psError(PS_ERR_UNKNOWN, true, "Incorrect polynomial type.  Returning NULL.\n");
+        result = false;
+        break;
+    }
+
+    // Free psVectors that were created for NULL arguments.
+    PS_FREE_TEMP_F64_VECTOR (f, f64);
+    PS_FREE_TEMP_F64_VECTOR (x, x64);
+    PS_FREE_TEMP_F64_VECTOR (y, y64);
+    PS_FREE_TEMP_F64_VECTOR (z, z64);
+    PS_FREE_TEMP_F64_VECTOR (t, t64);
+    PS_FREE_TEMP_F64_VECTOR (fErr, fErr64);
+
+    return result;
+}
+
+
+bool psVectorClipFitPolynomial4D(
+    psPolynomial4D *poly,
+    psStats *stats,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z,
+    const psVector *t)
+{
+    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_POLY_NON_NULL(poly, false);
+    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
+    PS_ASSERT_PTR_NON_NULL(stats, false);
+    PS_ASSERT_VECTOR_NON_NULL(mask, false);
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
+    PS_ASSERT_VECTOR_TYPE(x, f->type.type, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
+    PS_ASSERT_VECTOR_TYPE(y, f->type.type, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(z, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
+    PS_ASSERT_VECTOR_TYPE(z, f->type.type, false);
+
+    PS_ASSERT_VECTOR_NON_NULL(t, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, t, false);
+    PS_ASSERT_VECTOR_TYPE(t, f->type.type, false);
+
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
+    PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+
+    if (fErr != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
+        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
+    }
+
+    // the user supplies one of various stats option pairs,
+    // determine the desired mean and stdev STATS options:
+    // XXX enforce consistency?
+    // XXX psStatsGetValue() probably has inverted precedence
+    psStatsOptions meanOption = stats->options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN | PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN_V2);
+    psStatsOptions stdevOption = stats->options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV | PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV_V2);
+    if (!meanOption) {
+        psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
+        return false;
+    }
+    if (!stdevOption) {
+        psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
+        return false;
+    }
+
+    // clipping range defined by min and max and/or clipSigma
+    psF32 minClipSigma;
+    psF32 maxClipSigma;
+    if (isfinite(stats->max)) {
+        maxClipSigma = fabs(stats->max);
+    } else {
+        maxClipSigma = fabs(stats->clipSigma);
+    }
+    if (isfinite(stats->min)) {
+        minClipSigma = fabs(stats->min);
+    } else {
+        minClipSigma = fabs(stats->clipSigma);
+    }
+    psVector *resid = psVectorAlloc(f->n, PS_TYPE_F64);
+
+    psTrace("psLib.math", 4, "stats->clipIter is %d\n", stats->clipIter);
+    psTrace("psLib.math", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
+
+    for (psS32 N = 0; N < stats->clipIter; N++) {
+        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial4D()\n", N);
+        psS32 Nkeep = 0;
+        if (psTraceGetLevel("psLib.math") >= 6) {
+            if (mask != NULL) {
+                for (psS32 i = 0 ; i < mask->n ; i++) {
+                    psTrace("psLib.math", 6,  "mask[%d] is %d\n", i, mask->data.U8[i]);
+                }
+            }
+        }
+
+        if (!psVectorFitPolynomial4D (poly, mask, maskValue, f, fErr, x, y, z, t)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the data.  Returning NULL.\n");
+            psFree(resid);
+            return false;
+        }
+
+        psVector *fit = psPolynomial4DEvalVector (poly, x, y, z, t);
+        if (fit == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial4DEvalVector().  Returning NULL.\n");
+            psFree(resid);
+            return false;
+        }
+        for (psS32 i = 0 ; i < f->n ; i++) {
+            if (f->type.type == PS_TYPE_F64) {
+                resid->data.F64[i] = f->data.F64[i] - fit->data.F64[i];
+            } else {
+                resid->data.F64[i] = ((psF64) f->data.F32[i]) - fit->data.F64[i];
+            }
+        }
+
+        if (psTraceGetLevel("psLib.math") >= 6) {
+            if (mask != NULL) {
+                for (psS32 i = 0 ; i < mask->n ; i++) {
+                    if (!((mask != NULL) && (mask->data.U8[i] & maskValue))) {
+                        psTrace("psLib.math", 6,  "(f, fit)[%d] is (%f, %f).  resid is (%f)\n",
+                                i, f->data.F32[i], fit->data.F32[i], resid->data.F64[i]);
+                    }
+                }
+            }
+        }
+
+        if (!psVectorStats (stats, resid, NULL, mask, maskValue)) {
+            psError(PS_ERR_UNKNOWN, false, "Could not compute statistics on the resid vector.  Returning NULL.\n");
+            psFree(resid);
+            psFree(fit);
+            return false;
+        }
+
+        double meanValue = psStatsGetValue (stats, meanOption);
+        double stdevValue = psStatsGetValue (stats, stdevOption);
+
+        psTrace("psLib.math", 5, "Mean is %f\n", meanValue);
+        psTrace("psLib.math", 5, "Stdev is %f\n", stdevValue);
+        psF32 minClipValue = -minClipSigma*stdevValue;
+        psF32 maxClipValue = +maxClipSigma*stdevValue;
+
+        // set mask if pts are not valid
+        // we are masking out any point which is out of range
+        // recovery is not allowed with this scheme
+        for (psS32 i = 0; i < resid->n; i++) {
+            if ((mask != NULL) && (mask->data.U8[i] & maskValue)) {
+                continue;
+            }
+
+            if ((resid->data.F64[i] - meanValue > maxClipValue) || (resid->data.F64[i] - meanValue < minClipValue)) {
+                if (f->type.type == PS_TYPE_F64) {
+                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
+                            i, fit->data.F64[i], i, resid->data.F64[i]);
+                } else {
+                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
+                            i, fit->data.F32[i], i, resid->data.F64[i]);
+                }
+
+                if (mask != NULL) {
+                    mask->data.U8[i] |= 0x01;
+                }
+                continue;
+            }
+            Nkeep++;
+        }
+        psTrace("psLib.math", 6, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
+        stats->clippedNvalues = Nkeep;
+        psFree (fit);
+    }
+    // Free local temporary variables
+    psFree (resid);
+
+    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePolyFit.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePolyFit.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePolyFit.h	(revision 22322)
@@ -0,0 +1,137 @@
+/* @file  psMinimizePolyFit.c
+ * @brief basic minimization functions
+ *
+ * This file will contain function prototypes for various
+ * 1-D polynomial fitting routines.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-23 22:47:23 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifndef PS_MINIMIZE_POLYFIT_H
+#define PS_MINIMIZE_POLYFIT_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include "psVector.h"
+#include "psMemory.h"
+#include "psArray.h"
+#include "psImage.h"
+#include "psMatrix.h"
+#include "psPolynomial.h"
+#include "psSpline.h"
+#include "psStats.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psConstants.h"
+
+/** Derive a polynomial fit.
+ *
+ *  psVectorFitPolynomial1d returns the polynomial that best fits the
+ *  observations. The input parameters are a polynomial that specifies the
+ *  fit order, myPoly, which will be altered and returned with the best-fit
+ *  coefficients; and the observations, x, y and yErr. The independent
+ *  variable list, x may be NULL, in which case the vector index is used.
+ *  The dependent variable error, yErr may be null, in which case the solution
+ *  is determined in the assumption that all data errors are equal. This
+ *  function must be valid only for types psF32, psF64.
+ *
+ *  @return psPolynomial1D*    polynomial fit
+ */
+
+bool psVectorFitPolynomial1D(
+    psPolynomial1D *poly,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x
+);
+
+bool psVectorFitPolynomial2D(
+    psPolynomial2D *poly,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y
+);
+
+bool psVectorFitPolynomial3D(
+    psPolynomial3D *poly,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z
+);
+
+bool psVectorFitPolynomial4D(
+    psPolynomial4D *poly,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z,
+    const psVector *t
+);
+
+
+bool psVectorClipFitPolynomial1D(
+    psPolynomial1D *poly,
+    psStats *stats,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x
+);
+
+bool psVectorClipFitPolynomial2D(
+    psPolynomial2D *poly,
+    psStats *stats,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y
+);
+
+bool psVectorClipFitPolynomial3D(
+    psPolynomial3D *poly,
+    psStats *stats,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z
+);
+
+bool psVectorClipFitPolynomial4D(
+    psPolynomial4D *poly,
+    psStats *stats,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z,
+    const psVector *t
+);
+
+/// @}
+#endif // #ifndef PS_MINIMIZE_POLYFIT_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePowell.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePowell.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePowell.c	(revision 22322)
@@ -0,0 +1,797 @@
+/** @file  psMinimize.c
+ *  \brief basic minimization functions
+ *  @ingroup Math
+ *
+ *  This file will contain functions to minimize an arbitrary function at
+ *  a data point, fit an arbitrary function to a set of data points, and
+ *  fit a 1-D polynomial to a set of data points.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  NOTE: XXX: The SDR is silent about data types.  F32 is implemented here.
+ *
+ *  @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-02-06 21:36:09 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+
+#include "psMinimizePowell.h"
+#include "psAssert.h"
+#include "psStats.h"
+#include "psImage.h"
+#include "psImageStructManip.h"
+#include "psLogMsg.h"
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+// This macro takes as input the vector BASE and adds a multiple of the vector
+// LINE to it.  We assume BASEMASK is non-null.
+#define PS_VECTOR_ADD_MULTIPLE(BASE, BASEMASK, LINE, OUT, MUL) \
+for (psS32 i=0;i<BASE->n;i++) { \
+    if (BASEMASK->data.U8[i] == 0) { \
+        OUT->data.F32[i] = BASE->data.F32[i] + (MUL * LINE->data.F32[i]); \
+    } else { \
+        OUT->data.F32[i] = BASE->data.F32[i]; \
+    } \
+} \
+
+#define PS_VECTOR_F32_CHECK_ZERO_VECTOR(IN, BOOL_VAR) \
+BOOL_VAR = true; \
+for (psS32 i=0;i<IN->n;i++) { \
+    if (fabs(IN->data.F32[i]) >= FLT_EPSILON) { \
+        BOOL_VAR = false; \
+        break; \
+    } \
+} \
+
+#define PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(IN, INMASK, BOOL_VAR) \
+BOOL_VAR = true; \
+for (psS32 i=0;i<IN->n;i++) { \
+    if ((INMASK->data.U8[i] == 0) && (fabs(IN->data.F32[i]) >= FLT_EPSILON)) { \
+        BOOL_VAR = false; \
+        break; \
+    } \
+} \
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+/******************************************************************************
+p_psDetermineBracket():  This routine takes as input an arbitrary function,
+and the parameter to vary, and the line along which it must vary.  This
+function produces as output a bracket [a, b, c] such that
+f(param + b * line) < f(param + a * line)
+f(param + b * line) < f(param + c * line)
+a < b < c
+ 
+Algorithm:
+ 
+XXX completely ad hoc:
+start with the user-supplied starting parameter and
+call that b.  Calculate a/c as a fractional amount smaller/larger than b.
+Repeat this process until a local minimum is found.
+ 
+XXX: new algorithm:
+start at x=0, expand in one direction until the function
+decreases.  Then you have two points in the bracket.  Keep going until it
+increases, or x is too large.  If thst does not work, expand in the other
+direction.
+ 
+XXX: output bracket vector should be an input as well.
+*****************************************************************************/
+psVector *p_psDetermineBracket(
+    psVector *params,
+    psVector *line,
+    const psVector *paramMask,
+    const psArray *coords,
+    psMinimizePowellFunc func)
+{
+    psF32 a = 0.0;
+    psF32 b = 0.0;
+    psF32 c = 0.0;
+    psF32 fa = 0.0;
+    psF32 fb = 0.0;
+    psF32 fc = 0.0;
+    psS32 iter = 100;
+    psF32 aDir = 0.0;
+    psF32 cDir = 0.0;
+    psF32 new_aDir = 0.0;
+    psF32 new_cDir = 0.0;
+    psVector *bracket = psVectorAlloc(3, PS_TYPE_F32);
+    psF32 stepSize = PS_DETERMINE_BRACKET_STEP_SIZE;
+    psVector *tmp = NULL;
+    bool boolLineIsNull = true;
+
+    psTrace("psLib.math", 4, "---- p_psDetermineBracket() begin ----\n");
+
+    // If the line vector is zero, then return NULL.
+    PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(params, paramMask, boolLineIsNull);
+    if (boolLineIsNull == true) {
+        psTrace("psLib.math", 2, "p_psDetermineBracket() called with zero line vector.\n");
+        psTrace("psLib.math", 4, "---- p_psDetermineBracket() end (NULL) ----\n");
+        psFree(bracket);
+        return(NULL);
+    }
+
+    tmp = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    b = 0;
+    a = -stepSize;
+    c = stepSize;
+
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, a);
+    fa = func(tmp, coords);
+
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, b);
+    fb = func(tmp, coords);
+
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
+    fc = func(tmp, coords);
+
+    if (fa < fb) {
+        aDir = -1;
+    } else {
+        aDir = 1;
+    }
+
+    if (fc < fb) {
+        cDir = -1;
+    } else {
+        cDir = 1;
+    }
+
+    psTrace("psLib.math", 6, "(a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", a, b, c, fa, fb, fc);
+
+    while (iter > 0) {
+        psTrace("psLib.math", 6, "psDetermineBracket(): iteration %d\n", iter);
+        if ((fb < fa) && (fb < fc)) {
+            bracket->data.F32[0] = a;
+            bracket->data.F32[1] = b;
+            bracket->data.F32[2] = c;
+            psFree(tmp);
+            psTrace("psLib.math", 6, "---- p_psDetermineBracket() end ----\n");
+            return(bracket);
+        }
+        stepSize*= (1.0 + stepSize);
+        a =- stepSize;
+        c =+ stepSize;
+
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, a);
+        fa = func(tmp, coords);
+
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
+        fc = func(tmp, coords);
+
+        psTrace("psLib.math", 6, "Iter(%d): (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", iter, a, b, c, fa, fb, fc);
+
+        if (fa < fb) {
+            new_aDir = -1;
+        } else {
+            new_aDir = 1;
+        }
+
+        if (fc < fb) {
+            new_cDir = -1;
+        } else {
+            new_cDir = 1;
+        }
+        if ((new_aDir == 1) && (aDir == -1)) {
+            bracket->data.F32[0] = a;
+            bracket->data.F32[1] = b;
+            bracket->data.F32[2] = c;
+            psFree(tmp);
+            psTrace("psLib.math", 4, "---- p_psDetermineBracket() end ----\n");
+            return(bracket);
+        }
+
+        if ((new_cDir == 1) && (cDir == -1)) {
+            bracket->data.F32[0] = a;
+            bracket->data.F32[1] = b;
+            bracket->data.F32[2] = c;
+            psFree(tmp);
+            psTrace("psLib.math", 4, "---- p_psDetermineBracket() end ----\n");
+            return(bracket);
+        }
+        aDir = new_aDir;
+        cDir = new_cDir;
+        iter--;
+    }
+    psFree(tmp);
+    psFree(bracket);
+    psTrace("psLib.math", 4, "---- p_psDetermineBracket() end (NULL) ----\n");
+    return(NULL);
+}
+
+
+#define RETURN_FINAL_BRACKET(d) \
+if (a < c) { \
+    bracket->data.F32[0] = a; \
+    bracket->data.F32[1] = b; \
+    bracket->data.F32[2] = c; \
+} else { \
+    bracket->data.F32[0] = c; \
+    bracket->data.F32[1] = b; \
+    bracket->data.F32[2] = a; \
+} \
+psTrace("psLib.math", 4, "Final bracket (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", a, b, c, fa, fb, fc); \
+psTrace("psLib.math", 4, "---- p_psDetermineBracket() end ----\n"); \
+psFree(tmp); \
+return(bracket); \
+
+#define PS_DETERMINE_BRACKET_MAX_ITERATIONS 100
+psVector *p_psDetermineBracket2(
+    psVector *params,
+    psVector *line,
+    const psVector *paramMask,
+    const psArray *coords,
+    psMinimizePowellFunc func)
+{
+    psF32 a = 0.0;
+    psF32 b = 0.0;
+    psF32 c = 0.0;
+    psF32 fa = 0.0;
+    psF32 fb = 0.0;
+    psF32 fc = 0.0;
+    psS32 iter = 0;
+    psVector *tmp = psVectorAlloc(params->n, PS_TYPE_F32);
+    bool boolLineIsNull = true;
+    psF32 prevMin = 0.0;
+    psS32 countMin = 0;
+
+    psTrace("psLib.math", 4, "---- p_psDetermineBracket() begin ----\n");
+
+    // If the line vector is zero, then return NULL.
+    PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(params, paramMask, boolLineIsNull);
+    if (boolLineIsNull == true) {
+        psTrace("psLib.math", 2, "p_psDetermineBracket() called with zero line vector.\n");
+        psTrace("psLib.math", 4, "---- p_psDetermineBracket() end (NULL) ----\n");
+        psFree(tmp);
+        return(NULL);
+    }
+
+    // We determine in what x-direction does the function decrease.
+    a = 0.0;
+    fa = func(params, coords);
+    b = 0.5;
+    iter = 0;
+    do {
+        b*= (1.0 + PS_DETERMINE_BRACKET_STEP_SIZE);
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, b);
+        fb = func(tmp, coords);
+    } while ((fabs(fb - fa) < FLT_EPSILON) && (iter++ < 100));
+
+    if (fb > fa) {
+        a = b;
+        fa = fb;
+        b = 0.0;
+        fb = func(params, coords);
+    }
+    c = b;
+
+    // At this point we have (a, b) and we know that (fa >= fb).  Initially, c=b;
+    // We keep stretching b out further from "a" until (fc > previous fc).  If
+    // that happens, then we have our bracket.
+    psVector *bracket = psVectorAlloc(3, PS_TYPE_F32);
+    iter = 0;
+    while (iter < PS_DETERMINE_BRACKET_MAX_ITERATIONS) {
+        psTrace("psLib.math", 6, "psDetermineBracket(): iterationA %d\n", iter);
+        c+= (1.0 + PS_DETERMINE_BRACKET_STEP_SIZE) * (c - a);
+
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
+        fc = func(tmp, coords);
+
+        psTrace("psLib.math", 6, "Iteration(%d) (bracket): (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", iter, a, b, c, fa, fb, fc);
+
+        if ((fb < fa) && (fb < fc)) {
+            RETURN_FINAL_BRACKET();
+        } else {
+            b = c;
+            fb = fc;
+        }
+
+        // This code maintains a count of how many times the minimum fc has
+        // stayed the same.  If it gets too high, we exit this loop.
+        if (fc == prevMin) {
+            countMin++;
+        } else {
+            countMin = 0;
+        }
+        prevMin = fc;
+        if (countMin == 10) {
+            RETURN_FINAL_BRACKET();
+        }
+
+        iter++;
+    }
+
+    psFree(bracket);
+    psTrace("psLib.math", 4, "---- p_psDetermineBracket() end (NULL) (BAD) ----\n");
+    return(NULL);
+}
+
+/******************************************************************************
+This routine takes as input a possibly multi-dimensional function, along
+with an initial guess at the parameters of that function and vector "line"
+of the same size as the parameter vector.  It will minimize the function
+along that vector and returns the offset along that vector at which the
+minimum is determined.
+ 
+XXX: This routine is not very efficient in terms of total evaluations of the
+function.
+XXX: Since this is an internal function, many of the parameter checks are
+     redundant.
+ *****************************************************************************/
+#define PS_LINEMIN_MAX_ITERATIONS 30
+static psF32 LineMin(
+    psMinimization *min,
+    psVector *params,
+    psVector *line,
+    const psVector *paramMask,
+    const psArray *coords,
+    psMinimizePowellFunc func)
+{
+    PS_ASSERT_PTR_NON_NULL(min, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_VECTOR_NON_EMPTY(params, NAN);
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(line, NAN);
+    PS_ASSERT_VECTOR_NON_EMPTY(line, NAN);
+    PS_ASSERT_VECTOR_TYPE(line, PS_TYPE_F32, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(paramMask, NAN);
+    PS_ASSERT_VECTOR_NON_EMPTY(paramMask, NAN);
+    PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_U8, NAN);
+    PS_ASSERT_PTR_NON_NULL(coords, NAN);
+    PS_ASSERT_PTR_NON_NULL(func, NAN);
+    psVector *bracket;
+    psF32 a = 0.0;
+    psF32 b = 0.0;
+    psF32 c = 0.0;
+    psF32 n = 0.0;
+    psF32 fa = 0.0;
+    psF32 fb = 0.0;
+    psF32 fc = 0.0;
+    psF32 fn = 0.0;
+    psF32 mul = 0.0;
+    psS32 i = 0;
+    psS32 boolLineIsNull = true;
+    psS32 numIterations = 0;
+
+    psTrace("psLib.math", 4, "---- LineMin() begin ----\n");
+    PS_VECTOR_F32_CHECK_ZERO_VECTOR(line, boolLineIsNull);
+
+    if (boolLineIsNull == true) {
+        min->value = func(params, coords);
+        psTrace("psLib.math", 2, "LineMin() called with zero line vector.  Return 0.0.  Function value is %f\n", min->value);
+        return(0.0);
+    }
+
+    if (6 <= psTraceGetLevel("psLib.math")) {
+        for (i=0;i<params->n;i++) {
+            psTrace("psLib.math", 6, "(params, paramMask, line)[%d] is (%f %d %f)\n", i,
+                    params->data.F32[i], paramMask->data.U8[i], line->data.F32[i]);
+        }
+    }
+
+    bracket = p_psDetermineBracket2(params, line, paramMask, coords, func);
+    if (bracket == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Could not bracket minimum.  Returning NAN.\n");
+        return(NAN);
+    }
+    numIterations = 0;
+
+    psVector *tmpa = psVectorAlloc(params->n, PS_TYPE_F32);
+    psVector *tmpb = psVectorAlloc(params->n, PS_TYPE_F32);
+    psVector *tmpc = psVectorAlloc(params->n, PS_TYPE_F32);
+    psVector *tmpn = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    while (numIterations < PS_LINEMIN_MAX_ITERATIONS) {
+        numIterations++;
+        psTrace("psLib.math", 6, "LineMin(): iteration %d\n", numIterations);
+
+        a = bracket->data.F32[0];
+        b = bracket->data.F32[1];
+        c = bracket->data.F32[2];
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpa, a);
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpb, b);
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpc, c);
+        fa = func(tmpa, coords);
+        fb = func(tmpb, coords);
+        fc = func(tmpc, coords);
+        psTrace("psLib.math", 6, "LineMin: f(%f %f %f) is (%f %f %f)\n", a, b, c, fa, fb, fc);
+
+        // We determine which is the biggest segment in [a,b,c] then split
+        // that with the point n.
+        if ((b-a) > (c-b)) {
+            // This is the golden section formula
+            n = a + (0.69 * (b-a));
+            for (i=0;i<params->n;i++) {
+                tmpn->data.F32[i] = params->data.F32[i] + (n * line->data.F32[i]);
+            }
+            fn = func(tmpn, coords);
+
+            if (fn > fb) {
+                // a = n, b = b, c = c
+                bracket->data.F32[0] = n;
+            } else {
+                // a = a, b = n, c = b
+                bracket->data.F32[1] = n;
+                bracket->data.F32[2] = b;
+            }
+        } else {
+            n = b + (0.69 * (c-b));
+            for (i=0;i<params->n;i++) {
+                tmpn->data.F32[i] = params->data.F32[i] + (n * line->data.F32[i]);
+            }
+            fn = func(tmpn, coords);
+
+            if (fn > fb) {
+                // a = a, b = b, c = n
+                bracket->data.F32[2] = n;
+            } else {
+                // a = b, b = n, c = c
+                bracket->data.F32[0] = b;
+                bracket->data.F32[1] = n;
+            }
+        }
+        psTrace("psLib.math", 6, "LineMin: new bracket is (%f %f %f)\n", bracket->data.F32[0], bracket->data.F32[1], bracket->data.F32[2]);
+
+        mul = bracket->data.F32[1];
+        if ((fabs(a-b) < min->tol) && (fabs(b-c) < min->tol)) {
+            PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, params, mul);
+            min->value = func(params, coords);
+            psFree(bracket);
+            psTrace("psLib.math", 4, "---- LineMin() end.a (%f) (%f) ----\n", mul, min->value);
+            psFree(tmpa);
+            psFree(tmpb);
+            psFree(tmpc);
+            psFree(tmpn);
+            return(mul);
+        }
+    }
+
+    mul = bracket->data.F32[1];
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, params, mul);
+    min->value = func(params, coords);
+    psTrace("psLib.math", 4, "---- LineMin() end.b (%f) %f ----\n", mul, min->value);
+
+    psFree(bracket);
+    psFree(tmpa);
+    psFree(tmpb);
+    psFree(tmpc);
+    psFree(tmpn);
+    return(mul);
+}
+
+
+/******************************************************************************
+This routine must minimize a possibly multi-dimensional function.  The
+function to be minimized "func" is:
+    psF32 func(psVector *params, psArray *coords)
+The "params" are the parameters of the function which are varied.  The data
+points at which the function is varied are in the argument "coords" which is
+a psArray of psVectors: each vector represents a different coordinate.
+ 
+XXX: We do not use Brent's method.
+ *****************************************************************************/
+#define PS_MINIMIZE_POWELL_LINEMIN_MAX_ITERATIONS 20
+#define PS_MINIMIZE_POWELL_LINEMIN_ERROR_TOLERANCE 0.01
+
+bool psMinimizePowell(
+    psMinimization *min,
+    psVector *params,
+    const psVector *paramMask,
+    const psArray *coords,
+    psMinimizePowellFunc func)
+{
+    PS_ASSERT_PTR_NON_NULL(min, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(params, NULL);
+    PS_ASSERT_VECTOR_NON_EMPTY(params, NULL);
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, NULL);
+    PS_ASSERT_PTR_NON_NULL(coords, NULL);
+    PS_ASSERT_PTR_NON_NULL(func, NULL);
+    psS32 numDims = params->n;
+    psS32 i = 0;
+    psS32 j = 0;
+    psVector *myParamMask = NULL;
+    psMinimization dummyMin;
+    psF32 mul = 0.0;
+    psF32 baseFuncVal = 0.0;
+    psF32 currFuncVal = 0.0;
+    psS32 biggestIter = 0;
+    psF32 biggestDiff = 0.0;
+    psS32 iterationNumber = 0;
+
+    psTrace("psLib.math", 4, "---- psMinimizePowell() begin ----\n");
+    psTrace("psLib.math", 6, "min->maxIter is %d\n", min->maxIter);
+    psTrace("psLib.math", 6, "min->tol is %f\n", min->tol);
+
+    if (paramMask == NULL) {
+        myParamMask = psVectorAlloc(params->n, PS_TYPE_U8);
+        psVectorInit(myParamMask, 0);
+    } else {
+        myParamMask = (psVector *) paramMask;
+    }
+    PS_ASSERT_VECTORS_SIZE_EQUAL(params, myParamMask, NULL);
+
+
+    psVector *pQP = psVectorAlloc(numDims, PS_TYPE_F32);
+    psVector *u   = psVectorAlloc(numDims, PS_TYPE_F32);
+    psVector *Q   = psVectorAlloc(numDims, PS_TYPE_F32);
+
+    // 1: Set v[i] to be the unit vectors for each dimension in params
+    psArray *v = psArrayAlloc(numDims);
+    for (i=0;i<numDims;i++) {
+        (v->data[i]) = (psVector *) psVectorAlloc(numDims, PS_TYPE_F32);
+        for (j=0;j<numDims;j++) {
+            if (i == j) {
+                ((psVector *) (v->data[i]))->data.F32[j] = 1.0;
+            } else {
+                ((psVector *) (v->data[i]))->data.F32[j] = 0.0;
+            }
+        }
+    }
+
+    // 2: Set Q to be the initial params (P in the ADD)
+    for (i=0;i<numDims;i++) {
+        Q->data.F32[i] = params->data.F32[i];
+        Q->n++;
+    }
+
+    while (iterationNumber < min->maxIter) {
+        iterationNumber++;
+        psTrace("psLib.math", 6, "psMinimizePowell() iteration %d\n", iterationNumber);
+
+        // 3: For each dimension in params, move Q only in the vector v[i] to
+        //    minimize the function.
+
+        baseFuncVal = func(Q, coords);
+        currFuncVal = baseFuncVal;
+        psTrace("psLib.math", 6, "Current function value is %f\n", currFuncVal);
+
+        biggestDiff = 0;
+        biggestIter = 0;
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                P_PSMINIMIZATION_SET_MAXITER((&dummyMin),PS_MINIMIZE_POWELL_LINEMIN_MAX_ITERATIONS);
+                *(float*)&dummyMin.tol = PS_MINIMIZE_POWELL_LINEMIN_ERROR_TOLERANCE;
+                mul = LineMin(&dummyMin, Q, ((psVector *) v->data[i]),
+                              myParamMask, coords, func);
+                if (isnan(mul)) {
+                    psError(PS_ERR_UNKNOWN, false,
+                            "Could not perform line minimization.  Returning FALSE.\n");
+                    psFree(v);
+                    psFree(pQP);
+                    psFree(u);
+                    psFree(Q);
+                    psFree(myParamMask);
+                    return(false);
+                }
+                psTrace("psLib.math", 6, "LineMin along dimension %d has multiple %f\n", i, mul);
+
+                if (fabs(dummyMin.value - currFuncVal) > biggestDiff) {
+                    biggestDiff = fabs(dummyMin.value - currFuncVal);
+                    biggestIter = i;
+                }
+                currFuncVal = dummyMin.value;
+            }
+            // XXX: how can it be that we are not saving mul anywhere?
+        }
+        psTrace("psLib.math", 6, "New function value is %f\n", currFuncVal);
+        // XXX: There must be a bug here.  How can currFuncVal be the current function value?
+        // It is simply the minimum along one of the parameter dimensions.
+
+        // 4: Set the vector u = Q - P
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                u->data.F32[i] = Q->data.F32[i] - params->data.F32[i];
+                u->n++;
+
+                psTrace("psLib.math", 6, "u[i]=Q[i]-P[i] (%f = %f - %f)\n", u->data.F32[i],
+                        Q->data.F32[i],
+                        params->data.F32[i]);
+
+            } else {
+                u->data.F32[i] = 0.0;
+                u->n++;
+            }
+        }
+
+        // 5: Move Q only in the direction u, and minimize the function.
+        for (i=0;i<numDims;i++) {
+            psTrace("psLib.math", 6, "u[i] is %f\n", u->data.F32[i]);
+        }
+
+        mul = LineMin(&dummyMin, params, u, myParamMask, coords, func);
+        if (isnan(mul)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Could not perform line minimization.  Returning FALSE.\n");
+            psFree(v);
+            psFree(pQP);
+            psFree(u);
+            psFree(Q);
+            psFree(myParamMask);
+            return(false);
+        }
+
+        // 6:
+        if (dummyMin.value > currFuncVal) {
+            psFree(v);
+            psFree(pQP);
+            psFree(u);
+            psFree(Q);
+            min->iter = iterationNumber;
+            min->value = currFuncVal;
+            min->lastDelta = 0.0;
+            psTrace("psLib.math", 4, "---- psMinimizePowell() end (1)(true) ----\n");
+            psFree(myParamMask);
+            return(true);
+        }
+
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                pQP->data.F32[i] = (2 * Q->data.F32[i]) - params->data.F32[i];
+            } else {
+                pQP->data.F32[i] = params->data.F32[i];
+            }
+        }
+        psF32 fqp = func(pQP, coords);
+        psF32 term1 = (baseFuncVal - currFuncVal) - biggestDiff;
+        term1*= term1;
+        term1*= 2.0 * (baseFuncVal - (2.0 * currFuncVal) + fqp);
+        psF32 term2 = baseFuncVal - fqp;
+        term2*= term2 * biggestDiff;
+        if (term1 < term2) {
+            for (i=0;i<numDims;i++) {
+                if (myParamMask->data.U8[i] == 0) {
+                    ((psVector *) v->data[biggestIter])->data.F32[i] = u->data.F32[i];
+                }
+            }
+        }
+
+        // 7: Set P to Q
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                params->data.F32[i] = Q->data.F32[i];
+            }
+        }
+
+        // 8: Go to step 3 until the change is less than some tolerance.
+        if (fabs(baseFuncVal - currFuncVal) <= min->tol) {
+            psFree(v);
+            psFree(pQP);
+            psFree(u);
+            psFree(Q);
+            // XXX: Ensure that currFuncVal is the correct value to use here.
+            min->value = currFuncVal;
+            min->iter = iterationNumber;
+            min->lastDelta = currFuncVal - baseFuncVal;
+            psTrace("psLib.math", 4, "---- psMinimizePowell() end (2) (true) ----\n");
+            psFree(myParamMask);
+            return(true);
+        }
+    }
+
+    psFree(v);
+    psFree(pQP);
+    psFree(u);
+    psFree(Q);
+    min->iter = iterationNumber;
+    psTrace("psLib.math", 4, "---- psMinimizePowell() end (0) (false) ----\n");
+
+    psFree(myParamMask);
+    return(false);
+}
+
+
+/******************************************************************************
+This routine is to be used with the psMinimizeChi2Powell() function below.
+and the psMinimizePowell() function above.
+ 
+The basic idea is calculate chi-squared for a set of params/coords/errors.
+This functions uses global variables to receive the function pointer, the
+data values, and the data errors.
+ *****************************************************************************/
+static psF32 myPowellChi2Func(
+    const psVector *params,
+    const psArray *coords)
+{
+    psTrace("psLib.math", 4, "---- myPowellChi2Func() begin ----\n");
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_VECTOR_NON_EMPTY(params, NAN);
+    PS_ASSERT_PTR_NON_NULL(coords, NAN);
+
+    psF32 chi2 = 0.0;
+    psF32 d;
+    psS32 i;
+    psVector *tmp;
+
+    psVector *values = coords->data[coords->n];
+    psVector *errors = coords->data[coords->n + 1];
+    psMinimizeChi2PowellFunc *func = coords->data[coords->n + 2];
+
+    PS_ASSERT_VECTOR_NON_NULL(values, NAN);
+    PS_ASSERT_VECTOR_NON_EMPTY(values, NAN);
+    PS_ASSERT_VECTOR_TYPE(values, PS_TYPE_F32, NAN);
+    if (errors) {
+        PS_ASSERT_VECTOR_NON_NULL(errors, NAN);
+        PS_ASSERT_VECTOR_NON_EMPTY(errors, NAN);
+        PS_ASSERT_VECTOR_TYPE(errors, PS_TYPE_F32, NAN);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(values, errors, NAN);
+    }
+
+    tmp = (*func)(params, coords);
+
+    if (errors == NULL) {
+        for (i=0;i<coords->n;i++) {
+            d = (tmp->data.F32[i] - values->data.F32[i]);
+            chi2+= d * d;
+        }
+    } else {
+        for (i=0;i<coords->n;i++) {
+            d = (tmp->data.F32[i] - values->data.F32[i]) / errors->data.F32[i];
+            chi2+= d * d;
+        }
+    }
+    psFree(tmp);
+    psTrace("psLib.math", 4, "---- myPowellChi2Func() end (chi2 is %f) ----\n", chi2);
+    return(chi2);
+}
+
+
+/******************************************************************************
+This routine must minimize the chi-squared match of a set of data points and
+values for a possibly multi-dimensional function.
+ 
+The basic idea is to use the psMinimizePowell() function defined above.  In
+order to do so, we defined above a function myPowellChi2Func() which takes
+the "func" function and returns chi-squared over the params/coords/values.
+We then use that function myPowellChi2Func() in the call to
+psMinimizePowell().
+ *****************************************************************************/
+bool psMinimizeChi2Powell(
+    psMinimization *min,
+    psVector *params,
+    psMinConstraint *constraint,
+    const psArray *coords,
+    const psVector *value,
+    const psVector *error,
+    psMinimizeChi2PowellFunc model)
+{
+    // Generate extended version of coords array, so we can pass in extra data to the chi^2 function
+    psArray *newCoords = psArrayAlloc(coords->n + 3);
+    for (long i = 0; i < coords->n; i++) {
+        newCoords->data[i] = psMemIncrRefCounter(coords->data[i]);
+    }
+    newCoords->n = coords->n;           // We deceive everyone else as to the length
+    // Casting away const: I'm not going to hurt you, just want to increment your reference counter is all
+    newCoords->data[coords->n] = psMemIncrRefCounter((psVector*)value);
+    newCoords->data[coords->n + 1] = psMemIncrRefCounter((psVector*)error);
+    newCoords->data[coords->n + 2] = &model;
+
+    bool success = psMinimizePowell(min, params, constraint->paramMask, newCoords, myPowellChi2Func);
+
+    newCoords->data[coords->n - 1] = NULL; // We can't free the array with a function pointer on it
+    psFree(newCoords);
+    return success;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePowell.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePowell.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psMinimizePowell.h	(revision 22322)
@@ -0,0 +1,85 @@
+/* @file  psMinimizePowell.c
+ * @brief basic minimization functions
+ *
+ * This file will contain function prototypes for various Powell
+ * chi-squared minimization routines.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-23 22:47:23 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifndef PS_MINIMIZE_POWELL_H
+#define PS_MINIMIZE_POWELL_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include "psMinimizeLMM.h"
+#include "psVector.h"
+#include "psMemory.h"
+#include "psArray.h"
+#include "psImage.h"
+#include "psMatrix.h"
+#include "psPolynomial.h"
+#include "psSpline.h"
+#include "psStats.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psConstants.h"
+
+/** Specifies the format of a user-defined function that the general Powell
+ *  minimizer routine will accept.
+ *
+ *  @return float:   the single float value of the function given the parameters
+ *      and coordinate vectors.
+*/
+typedef
+float (*psMinimizePowellFunc)(
+    const psVector *params,            ///< Parameters used to evaluate the function
+    const psArray *coords              ///< Coordinates at which to evaluate
+);
+
+/** Minimizes a specified function based on the Powell method.
+ *
+ *  @return bool:   True if successful.
+ */
+bool psMinimizePowell(
+    psMinimization *min,               ///< Minimization specification
+    psVector *params,                  ///< "Best guess" for parameters that minimize func
+    const psVector *paramMask,         ///< Parameters to be held fixed by minimizer
+    const psArray *coords,             ///< Measurement coordinates
+    psMinimizePowellFunc func          ///< Specified function
+);
+
+/** Specifies the format of a user-defined function that the general Powell chi-
+ *  squared minimizer routine will accept.
+ *
+ *  @return psVector*:    Calculated values given the parameters and coordinates.
+*/
+typedef
+psVector *(*psMinimizeChi2PowellFunc)(
+    const psVector *params,            ///< Parameters used to evaluate the function
+    const psArray *coords              ///< Coordinates at which to evaluate
+);
+
+/** Minimizes a specified function based on the Powell chi-squared method.
+ *
+ *  @return bool:   True is successful.
+ */
+bool psMinimizeChi2Powell(
+    psMinimization *min,               ///< Minimization specification
+    psVector *params,                  ///< "Best guess" for parameters that minimize func
+    psMinConstraint *constraint,
+    const psArray *coords,             ///< Measurement coordinates
+    const psVector *value,             ///< Measured values at the coordinates
+    const psVector *error,             ///< Errors in the measure values (or NULL)
+    psMinimizeChi2PowellFunc model     ///< Specified function
+);
+
+/// @}
+#endif // #ifndef PS_MINIMIZE_POWELL_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomial.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomial.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomial.c	(revision 22322)
@@ -0,0 +1,1223 @@
+/** @file  psPolynomial.c
+*
+*  @brief Contains basic function allocation, deallocation, and evaluation
+*         routines.
+*
+*  This file will hold the routiness for allocating, freeing, and evaluating
+*  polynomials.  It also contains a Gaussian functions.
+*
+*  @version $Revision: 1.158 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-10-09 19:24:46 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*  XXX: Should the "coeffErr[]" be used as well?  Bug ???.  Ignore coeffErr
+*
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/*****************************************************************************/
+/*  INCLUDE FILES                                                            */
+/*****************************************************************************/
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psRandom.h"
+#include "psMemory.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psPolynomial.h"
+#include "psAssert.h"
+
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+static void polynomial1DFree(psPolynomial1D* poly);
+static void polynomial2DFree(psPolynomial2D* poly);
+static void polynomial3DFree(psPolynomial3D* poly);
+static void polynomial4DFree(psPolynomial4D* poly);
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+bool psMemCheckPolynomial1D(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)polynomial1DFree );
+}
+
+bool psMemCheckPolynomial2D(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)polynomial2DFree );
+}
+
+bool psMemCheckPolynomial3D(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)polynomial3DFree );
+}
+
+bool psMemCheckPolynomial4D(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)polynomial4DFree );
+}
+
+static void polynomial1DFree(psPolynomial1D* poly)
+{
+    psFree(poly->coeff);
+    psFree(poly->coeffErr);
+    psFree(poly->coeffMask);
+}
+
+static void polynomial2DFree(psPolynomial2D* poly)
+{
+    unsigned int x = 0;
+
+    for (x = 0; x < (1 + poly->nX); x++) {
+        psFree(poly->coeff[x]);
+        psFree(poly->coeffErr[x]);
+        psFree(poly->coeffMask[x]);
+    }
+    psFree(poly->coeff);
+    psFree(poly->coeffErr);
+    psFree(poly->coeffMask);
+}
+
+static void polynomial3DFree(psPolynomial3D* poly)
+{
+    unsigned int x = 0;
+    unsigned int y = 0;
+
+    for (x = 0; x < (1 + poly->nX); x++) {
+        for (y = 0; y < (1 + poly->nY); y++) {
+            psFree(poly->coeff[x][y]);
+            psFree(poly->coeffErr[x][y]);
+            psFree(poly->coeffMask[x][y]);
+        }
+        psFree(poly->coeff[x]);
+        psFree(poly->coeffErr[x]);
+        psFree(poly->coeffMask[x]);
+    }
+
+    psFree(poly->coeff);
+    psFree(poly->coeffErr);
+    psFree(poly->coeffMask);
+}
+
+static void polynomial4DFree(psPolynomial4D* poly)
+{
+    unsigned int x = 0;
+    unsigned int y = 0;
+    unsigned int z = 0;
+
+    for (x = 0; x < (1 + poly->nX); x++) {
+        for (y = 0; y < (1 + poly->nY); y++) {
+            for (z = 0; z < (1 + poly->nZ); z++) {
+                psFree(poly->coeff[x][y][z]);
+                psFree(poly->coeffErr[x][y][z]);
+                psFree(poly->coeffMask[x][y][z]);
+            }
+            psFree(poly->coeff[x][y]);
+            psFree(poly->coeffErr[x][y]);
+            psFree(poly->coeffMask[x][y]);
+        }
+        psFree(poly->coeff[x]);
+        psFree(poly->coeffErr[x]);
+        psFree(poly->coeffMask[x]);
+    }
+
+    psFree(poly->coeff);
+    psFree(poly->coeffErr);
+    psFree(poly->coeffMask);
+}
+
+/*****************************************************************************
+p_psCreateChebyshevPolys(n): this routine takes as input the required order n,
+and returns as output as a pointer to an array of n psPolynomial1D
+structures, corresponding to the first n Chebyshev polynomials.
+ 
+XXX: The output should be static since the Chebyshev polynomials might be
+used frequently and the data structure created here does not contain the
+outer coefficients of the Chebyshev polynomials.
+ *****************************************************************************/
+psPolynomial1D **p_psCreateChebyshevPolys(psS32 numPolys)
+{
+    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(numPolys, 1, NULL);
+
+    psPolynomial1D **chebPolys = (psPolynomial1D **) psAlloc(numPolys * sizeof(psPolynomial1D *));
+    for (psS32 i = 0; i < numPolys; i++) {
+        chebPolys[i] = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, i);
+    }
+
+    // Create the Chebyshev polynomials.
+    // Polynomial i has i-th order.
+    chebPolys[0]->coeff[0] = 1.0;
+    if (numPolys >= 2) {
+        chebPolys[1]->coeff[1] = 1.0;
+
+        for (psS32 i = 2; i < numPolys; i++) {
+            for (psS32 j = 0; j < chebPolys[i - 1]->nX+1; j++) {
+                chebPolys[i]->coeff[j + 1] = 2.0 * chebPolys[i - 1]->coeff[j];
+            }
+            for (psS32 j = 0; j < chebPolys[i - 2]->nX+1; j++) {
+                chebPolys[i]->coeff[j] -= chebPolys[i - 2]->coeff[j];
+            }
+        }
+    }
+
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        for (psS32 j = 0; j < numPolys; j++) {
+            PS_POLY_PRINT_1D(chebPolys[j]);
+        }
+    }
+    return (chebPolys);
+}
+
+
+/*****************************************************************************
+    Polynomial coefficients will be accessed in [w][x][y][z] fashion.
+ *****************************************************************************/
+static psF64 ordPolynomial1DEval(
+    psF64 x,
+    const psPolynomial1D* poly)
+{
+    unsigned int loop_x = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+
+    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
+    psTrace("psLib.math", 4, "Polynomial order is %u\n", poly->nX);
+    for (loop_x = 0; loop_x < poly->nX+1; loop_x++) {
+        psTrace("psLib.math", 4, "Polynomial coeff[%u] is %lf\n", loop_x, poly->coeff[loop_x]);
+    }
+
+    for (loop_x = 0; loop_x < poly->nX+1; loop_x++) {
+        if (!(poly->coeffMask[loop_x] & PS_POLY_MASK_SET)) {
+            psTrace("psLib.math", 8,
+                    "polysum+= sum*coeff [%lf+= (%lf * %lf)\n", polySum, xSum, poly->coeff[loop_x]);
+            polySum += xSum * poly->coeff[loop_x];
+        }
+        xSum *= x;
+    }
+
+    psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
+    return(polySum);
+}
+
+// XXX: You can do this without having to psAlloc() vector d.
+// XXX: How does the mask vector effect Crenshaw's formula?
+// NOTE: We assume that x is scaled between -1.0 and 1.0;
+// XXX: Create a faster version for low-order Chebyshevs.
+static psF64 chebPolynomial1DEval(
+    psF64 x,
+    const psPolynomial1D* poly)
+{
+    PS_ASSERT_DOUBLE_WITHIN_RANGE(x, -1.0, 1.0, NAN);
+    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(poly->nX, 0, NAN);
+    psVector *d;
+
+    unsigned int nTerms = 1 + poly->nX;
+    unsigned int i;
+    psF64 tmp = 0.0;
+
+    // Special case where the Chebyshev poly is constant.
+    if (nTerms == 1) {
+        if (!(poly->coeffMask[0] & PS_POLY_MASK_SET)) {
+            tmp += poly->coeff[0];
+        }
+        return(tmp);
+    }
+
+    // Special case where the Chebyshev poly is linear.
+    if (nTerms == 2) {
+        if (!(poly->coeffMask[0] & PS_POLY_MASK_SET)) {
+            tmp+= poly->coeff[0];
+        }
+        if (!(poly->coeffMask[1] & PS_POLY_MASK_SET)) {
+            tmp+= poly->coeff[1] * x;
+        }
+        return(tmp);
+    }
+
+    if (1) {
+        // General case where the Chebyshev poly has 2 or more terms.
+        d = psVectorAlloc(nTerms, PS_TYPE_F64);
+        if (!(poly->coeffMask[nTerms-1] & PS_POLY_MASK_SET)) {
+            d->data.F64[nTerms-1] = poly->coeff[nTerms-1];
+        } else {
+            d->data.F64[nTerms-1] = 0.0;
+        }
+
+        d->data.F64[nTerms-2] = (2.0 * x * d->data.F64[nTerms-1]);
+        if (!(poly->coeffMask[nTerms-2] & PS_POLY_MASK_SET)) {
+            d->data.F64[nTerms-2] += poly->coeff[nTerms-2];
+        }
+
+        for (i=nTerms-3;i>=1;i--) {
+            d->data.F64[i] = (2.0 * x * d->data.F64[i+1]) - (d->data.F64[i+2]);
+            if (!(poly->coeffMask[i] & PS_POLY_MASK_SET)) {
+                d->data.F64[i] += poly->coeff[i];
+            }
+        }
+
+        tmp = (x * d->data.F64[1]) - (d->data.F64[2]);
+        if (!(poly->coeffMask[0] & PS_POLY_MASK_SET)) {
+            tmp += (0.5 * poly->coeff[0]);
+        }
+        psFree(d);
+    } else {
+        // XXX: This is old code that does not use Clenshaw's formula.  Get rid of it.
+        psPolynomial1D **chebPolys = p_psCreateChebyshevPolys(1 + poly->nX);
+
+        tmp = 0.0;
+        for (psS32 i=0;i<(1 + poly->nX);i++) {
+            tmp+= (poly->coeff[i] * psPolynomial1DEval(chebPolys[i], x));
+        }
+        tmp-= (poly->coeff[0]/2.0);
+
+        for (psS32 i=0;i<(1 + poly->nX);i++) {
+            psFree(chebPolys[i]);
+        }
+        psFree(chebPolys);
+    }
+
+    return(tmp);
+}
+
+static psF64 ordPolynomial2DEval(psF64 x,
+                                 psF64 y,
+                                 const psPolynomial2D* poly)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, NAN);
+
+    unsigned int loop_x = 0;
+    unsigned int loop_y = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+
+    for (loop_x = 0; loop_x < (1 + poly->nX); loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < (1 + poly->nY); loop_y++) {
+            if (!(poly->coeffMask[loop_x][loop_y] & PS_POLY_MASK_SET)) {
+                polySum += ySum * poly->coeff[loop_x][loop_y];
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF64 chebPolynomial2DEval(psF64 x,
+                                  psF64 y,
+                                  const psPolynomial2D* poly)
+{
+    PS_ASSERT_DOUBLE_WITHIN_RANGE(x, -1.0, 1.0, 0.0);
+    PS_ASSERT_DOUBLE_WITHIN_RANGE(y, -1.0, 1.0, 0.0);
+    PS_ASSERT_POLY_NON_NULL(poly, NAN);
+
+    unsigned int loop_x = 0;
+    unsigned int loop_y = 0;
+    unsigned int i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    unsigned int maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = poly->nX;
+    if (poly->nY > maxChebyPoly) {
+        maxChebyPoly = poly->nY;
+    }
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly + 1);
+
+    for (loop_x = 0; loop_x < (1 + poly->nX); loop_x++) {
+        for (loop_y = 0; loop_y < (1 + poly->nY); loop_y++) {
+            if (!(poly->coeffMask[loop_x][loop_y] & PS_POLY_MASK_SET)) {
+                polySum += poly->coeff[loop_x][loop_y] *
+                           psPolynomial1DEval(chebPolys[loop_x], x) *
+                           psPolynomial1DEval(chebPolys[loop_y], y);
+            }
+        }
+    }
+    for (i=0;i<maxChebyPoly+1;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF64 ordPolynomial3DEval(psF64 x,
+                                 psF64 y,
+                                 psF64 z,
+                                 const psPolynomial3D* poly)
+{
+    unsigned int loop_x = 0;
+    unsigned int loop_y = 0;
+    unsigned int loop_z = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    psF64 zSum = 1.0;
+
+    for (loop_x = 0; loop_x < (1 + poly->nX); loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < (1 + poly->nY); loop_y++) {
+            zSum = ySum;
+            for (loop_z = 0; loop_z < (1 + poly->nZ); loop_z++) {
+                if (!(poly->coeffMask[loop_x][loop_y][loop_z] & PS_POLY_MASK_SET)) {
+                    polySum += zSum * poly->coeff[loop_x][loop_y][loop_z];
+                }
+                zSum *= z;
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF64 chebPolynomial3DEval(psF64 x,
+                                  psF64 y,
+                                  psF64 z,
+                                  const psPolynomial3D* poly)
+{
+    PS_ASSERT_DOUBLE_WITHIN_RANGE(x, -1.0, 1.0, 0.0);
+    PS_ASSERT_DOUBLE_WITHIN_RANGE(y, -1.0, 1.0, 0.0);
+    PS_ASSERT_DOUBLE_WITHIN_RANGE(z, -1.0, 1.0, 0.0);
+    unsigned int loop_x = 0;
+    unsigned int loop_y = 0;
+    unsigned int loop_z = 0;
+    unsigned int i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    unsigned int maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = poly->nX;
+    if (poly->nY > maxChebyPoly) {
+        maxChebyPoly = poly->nY;
+    }
+    if (poly->nZ > maxChebyPoly) {
+        maxChebyPoly = poly->nZ;
+    }
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly + 1);
+
+    for (loop_x = 0; loop_x < (1 + poly->nX); loop_x++) {
+        for (loop_y = 0; loop_y < (1 + poly->nY); loop_y++) {
+            for (loop_z = 0; loop_z < (1 + poly->nZ); loop_z++) {
+                if (!(poly->coeffMask[loop_x][loop_y][loop_z] & PS_POLY_MASK_SET)) {
+                    polySum += poly->coeff[loop_x][loop_y][loop_z] *
+                               psPolynomial1DEval(chebPolys[loop_x], x) *
+                               psPolynomial1DEval(chebPolys[loop_y], y) *
+                               psPolynomial1DEval(chebPolys[loop_z], z);
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly+1;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF64 ordPolynomial4DEval(psF64 x,
+                                 psF64 y,
+                                 psF64 z,
+                                 psF64 t,
+                                 const psPolynomial4D* poly)
+{
+    unsigned int loop_x = 0;
+    unsigned int loop_y = 0;
+    unsigned int loop_z = 0;
+    unsigned int loop_t = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    psF64 zSum = 1.0;
+    psF64 tSum = 1.0;
+
+    for (loop_x = 0; loop_x < (1 + poly->nX); loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < (1 + poly->nY); loop_y++) {
+            zSum = ySum;
+            for (loop_z = 0; loop_z < (1 + poly->nZ); loop_z++) {
+                tSum = zSum;
+                for (loop_t = 0; loop_t < (1 + poly->nT); loop_t++) {
+                    if (!(poly->coeffMask[loop_x][loop_y][loop_z][loop_t] & PS_POLY_MASK_SET)) {
+                        polySum += tSum * poly->coeff[loop_x][loop_y][loop_z][loop_t];
+                    }
+                    tSum *= t;
+                }
+                zSum *= z;
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF64 chebPolynomial4DEval(psF64 x,
+                                  psF64 y,
+                                  psF64 z,
+                                  psF64 t,
+                                  const psPolynomial4D* poly)
+{
+    PS_ASSERT_DOUBLE_WITHIN_RANGE(x, -1.0, 1.0, 0.0);
+    PS_ASSERT_DOUBLE_WITHIN_RANGE(y, -1.0, 1.0, 0.0);
+    PS_ASSERT_DOUBLE_WITHIN_RANGE(z, -1.0, 1.0, 0.0);
+    PS_ASSERT_DOUBLE_WITHIN_RANGE(t, -1.0, 1.0, 0.0);
+    unsigned int loop_x = 0;
+    unsigned int loop_y = 0;
+    unsigned int loop_z = 0;
+    unsigned int loop_t = 0;
+    unsigned int i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    unsigned int maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = poly->nX;
+    if (poly->nY > maxChebyPoly) {
+        maxChebyPoly = poly->nY;
+    }
+    if (poly->nZ > maxChebyPoly) {
+        maxChebyPoly = poly->nZ;
+    }
+    if (poly->nT > maxChebyPoly) {
+        maxChebyPoly = poly->nT;
+    }
+    // Add 1 since p_psCreateChebyshevPolys() takes nTerms, not nOrder.
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly + 1);
+
+    for (loop_x = 0; loop_x < (1 + poly->nX); loop_x++) {
+        for (loop_y = 0; loop_y < (1 + poly->nY); loop_y++) {
+            for (loop_z = 0; loop_z < (1 + poly->nZ); loop_z++) {
+                for (loop_t = 0; loop_t < (1 + poly->nT); loop_t++) {
+                    if (!(poly->coeffMask[loop_x][loop_y][loop_z][loop_t] & PS_POLY_MASK_SET)) {
+                        polySum += poly->coeff[loop_x][loop_y][loop_z][loop_t] *
+                                   psPolynomial1DEval(chebPolys[loop_x], x) *
+                                   psPolynomial1DEval(chebPolys[loop_y], y) *
+                                   psPolynomial1DEval(chebPolys[loop_z], z) *
+                                   psPolynomial1DEval(chebPolys[loop_t], t);
+                    }
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly+1;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - PUBLIC                                         */
+/*****************************************************************************/
+
+/*****************************************************************************
+    Evaluate a non-normalized Gaussian with the given mean and sigma at the
+    given coordianate.  Note that this is not a Gaussian deviate.  The
+    evaluated Gaussian is: \f[ exp(-\frac{(x-mean)^2}{2\sigma^2}) \f]
+ *****************************************************************************/
+float psGaussian(float x,
+                 float mean,
+                 float sigma,
+                 bool normal)
+{
+    psF32 tmp = 1.0;
+
+    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
+
+    if (normal == true) {
+        tmp = 1.0 / sqrtf(2.0 * M_PI * (sigma * sigma));
+    }
+
+    psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
+    return(tmp * exp(-((x - mean) * (x - mean)) / (2.0 * sigma * sigma)));
+}
+
+/*****************************************************************************
+    This routine must allocate memory for the polynomial structures.
+ 
+    XXX: How do we check for an appropriate value for n?
+ *****************************************************************************/
+psPolynomial1D* psPolynomial1DAlloc(
+    psPolynomialType type,
+    unsigned int nX)
+{
+    PS_ASSERT_POLY_VALID_TYPE(type, NULL);
+
+    psU32 nOrder = nX;
+    psPolynomial1D *newPoly = (psPolynomial1D* ) psAlloc(sizeof(psPolynomial1D));
+    psMemSetDeallocator(newPoly, (psFreeFunc) polynomial1DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nOrder;
+    newPoly->coeff = psAlloc((nOrder + 1) * sizeof(psF64));
+    newPoly->coeffErr = psAlloc((nOrder + 1) * sizeof(psF64));
+    newPoly->coeffMask = (psMaskType *)psAlloc((nOrder + 1) * sizeof(psMaskType));
+    for (psU32 i = 0; i < (nOrder + 1); i++) {
+        newPoly->coeff[i] = 0.0;
+        newPoly->coeffErr[i] = 0.0;
+        newPoly->coeffMask[i] = PS_POLY_MASK_NONE;
+    }
+
+    return(newPoly);
+}
+
+psPolynomial2D* psPolynomial2DAlloc(
+    psPolynomialType type,
+    unsigned int nX,
+    unsigned int nY)
+{
+    PS_ASSERT_POLY_VALID_TYPE(type, NULL);
+
+    unsigned int x = 0;
+    unsigned int y = 0;
+    psPolynomial2D* newPoly = NULL;
+
+    newPoly = (psPolynomial2D* ) psAlloc(sizeof(psPolynomial2D));
+    psMemSetDeallocator(newPoly, (psFreeFunc) polynomial2DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+
+    newPoly->coeff = psAlloc((1 + nX) * sizeof(psF64 *));
+    newPoly->coeffErr = psAlloc((1 + nX) * sizeof(psF64 *));
+    newPoly->coeffMask = (psMaskType **)psAlloc((1 + nX) * sizeof(psMaskType *));
+    for (x = 0; x < (1 + nX); x++) {
+        newPoly->coeff[x] = psAlloc((1 + nY) * sizeof(psF64));
+        newPoly->coeffErr[x] = psAlloc((1 + nY) * sizeof(psF64));
+        newPoly->coeffMask[x] = (psMaskType *)psAlloc((1 + nY) * sizeof(psMaskType));
+    }
+    for (x = 0; x < (1 + nX); x++) {
+        for (y = 0; y < (1 + nY); y++) {
+            newPoly->coeff[x][y] = 0.0;
+            newPoly->coeffErr[x][y] = 0.0;
+            newPoly->coeffMask[x][y] = PS_POLY_MASK_NONE;
+        }
+    }
+
+    return(newPoly);
+}
+
+// XXX add 1D, 3D, 4D versions
+bool psPolynomial2DRecycle(psPolynomial2D *poly,
+                           psPolynomialType type,
+                           unsigned int nX,
+                           unsigned int nY)
+{
+    PS_ASSERT_INT_NONNEGATIVE(nX, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(nY, NULL);
+
+    bool match = true;
+    match &= (poly->type == type);
+    match &= (poly->nX == type);
+    match &= (poly->nY == type);
+
+    if (!match) {
+        for (int i = 0; i < poly->nX + 1; i++) {
+            psFree (poly->coeff[i]);
+            psFree (poly->coeffErr[i]);
+            psFree (poly->coeffMask[i]);
+        }
+        psFree (poly->coeff);
+        psFree (poly->coeffErr);
+        psFree (poly->coeffMask);
+
+        poly->type = type;
+        poly->nX = nX;
+        poly->nY = nY;
+
+        poly->coeff = psAlloc((1 + nX) * sizeof(psF64 *));
+        poly->coeffErr = psAlloc((1 + nX) * sizeof(psF64 *));
+        poly->coeffMask = (psMaskType **)psAlloc((1 + nX) * sizeof(psMaskType *));
+        for (int i = 0; i < (1 + nX); i++) {
+            poly->coeff[i] = psAlloc((1 + nY) * sizeof(psF64));
+            poly->coeffErr[i] = psAlloc((1 + nY) * sizeof(psF64));
+            poly->coeffMask[i] = (psMaskType *)psAlloc((1 + nY) * sizeof(psMaskType));
+        }
+    }
+    for (int i = 0; i < (1 + nX); i++) {
+        for (int j = 0; j < (1 + nY); j++) {
+            poly->coeff[i][j] = 0.0;
+            poly->coeffErr[i][j] = 0.0;
+            poly->coeffMask[i][j] = PS_POLY_MASK_NONE;
+        }
+    }
+    return(true);
+}
+
+// XXX add 1D, 3D, 4D versions
+psPolynomial2D *psPolynomial2DCopy(psPolynomial2D *out,
+                                   psPolynomial2D *poly)
+{
+    if (out == NULL) {
+        out = psPolynomial2DAlloc (poly->type, poly->nX, poly->nY);
+    } else {
+        psPolynomial2DRecycle (out, poly->type, poly->nX, poly->nY);
+    }
+
+    for (int i = 0; i < (1 + poly->nX); i++) {
+        for (int j = 0; j < (1 + poly->nY); j++) {
+            out->coeff[i][j] = poly->coeff[i][j];
+            out->coeffErr[i][j] = poly->coeffErr[i][j];
+            out->coeffMask[i][j] = poly->coeffMask[i][j];
+        }
+    }
+    return(out);
+}
+
+psPolynomial3D* psPolynomial3DAlloc(
+    psPolynomialType type,
+    unsigned int nX,
+    unsigned int nY,
+    unsigned int nZ)
+{
+    PS_ASSERT_POLY_VALID_TYPE(type, NULL);
+
+    //PS_ASSERT_INT_NONNEGATIVE(nX, NULL);
+    //PS_ASSERT_INT_NONNEGATIVE(nY, NULL);
+    //PS_ASSERT_INT_NONNEGATIVE(nZ, NULL);
+
+    unsigned int x = 0;
+    unsigned int y = 0;
+    unsigned int z = 0;
+    psPolynomial3D* newPoly = NULL;
+
+    newPoly = (psPolynomial3D* ) psAlloc(sizeof(psPolynomial3D));
+    psMemSetDeallocator(newPoly, (psFreeFunc) polynomial3DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = psAlloc((nX + 1) * sizeof(psF64 **));
+    newPoly->coeffErr = psAlloc((nX + 1) * sizeof(psF64 **));
+    newPoly->coeffMask = (psMaskType ***)psAlloc((nX + 1) * sizeof(psMaskType **));
+    for (x = 0; x < (1 + nX); x++) {
+        newPoly->coeff[x] = psAlloc((nY + 1) * sizeof(psF64 *));
+        newPoly->coeffErr[x] = psAlloc((nY + 1) * sizeof(psF64 *));
+        newPoly->coeffMask[x] = (psMaskType **)psAlloc((nY + 1) * sizeof(psMaskType *));
+        for (y = 0; y < (nY + 1); y++) {
+            newPoly->coeff[x][y] = psAlloc((nZ + 1) * sizeof(psF64));
+            newPoly->coeffErr[x][y] = psAlloc((nZ + 1) * sizeof(psF64));
+            newPoly->coeffMask[x][y] = (psMaskType *)psAlloc((nZ + 1) * sizeof(psMaskType));
+        }
+    }
+    for (x = 0; x < (nX + 1); x++) {
+        for (y = 0; y < (nY + 1); y++) {
+            for (z = 0; z < (nZ + 1); z++) {
+                newPoly->coeff[x][y][z] = 0.0;
+                newPoly->coeffErr[x][y][z] = 0.0;
+                newPoly->coeffMask[x][y][z] = PS_POLY_MASK_NONE;
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psPolynomial4D* psPolynomial4DAlloc(
+    psPolynomialType type,
+    unsigned int nX,
+    unsigned int nY,
+    unsigned int nZ,
+    unsigned int nT)
+{
+    PS_ASSERT_POLY_VALID_TYPE(type, NULL);
+
+    //PS_ASSERT_INT_NONNEGATIVE(nX, NULL);
+    //PS_ASSERT_INT_NONNEGATIVE(nY, NULL);
+    //PS_ASSERT_INT_NONNEGATIVE(nZ, NULL);
+    //PS_ASSERT_INT_NONNEGATIVE(nT, NULL);
+
+    unsigned int x = 0;
+    unsigned int y = 0;
+    unsigned int z = 0;
+    unsigned int t = 0;
+    psPolynomial4D* newPoly = NULL;
+
+    newPoly = (psPolynomial4D* ) psAlloc(sizeof(psPolynomial4D));
+    psMemSetDeallocator(newPoly, (psFreeFunc) polynomial4DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+    newPoly->nT = nT;
+
+    newPoly->coeff = psAlloc((nX + 1) * sizeof(psF64 ***));
+    newPoly->coeffErr = psAlloc((nX + 1) * sizeof(psF64 ***));
+    newPoly->coeffMask = (psMaskType ****)psAlloc((nX + 1) * sizeof(psMaskType ***));
+    for (x = 0; x < (nX + 1); x++) {
+        newPoly->coeff[x] = psAlloc((nY + 1) * sizeof(psF64 **));
+        newPoly->coeffErr[x] = psAlloc((nY + 1) * sizeof(psF64 **));
+        newPoly->coeffMask[x] = (psMaskType ***)psAlloc((nY + 1) * sizeof(psMaskType **));
+        for (y = 0; y < (nY + 1); y++) {
+            newPoly->coeff[x][y] = psAlloc((nZ + 1) * sizeof(psF64 *));
+            newPoly->coeffErr[x][y] = psAlloc((nZ + 1) * sizeof(psF64 *));
+            newPoly->coeffMask[x][y] = (psMaskType **)psAlloc((nZ + 1) * sizeof(psMaskType *));
+            for (z = 0; z < (nZ + 1); z++) {
+                newPoly->coeff[x][y][z] = psAlloc((nT + 1) * sizeof(psF64));
+                newPoly->coeffErr[x][y][z] = psAlloc((nT + 1) * sizeof(psF64));
+                newPoly->coeffMask[x][y][z] = (psMaskType *)psAlloc((nT + 1) * sizeof(psMaskType));
+            }
+        }
+    }
+    for (x = 0; x < (nX + 1); x++) {
+        for (y = 0; y < (nY + 1); y++) {
+            for (z = 0; z < (nZ + 1); z++) {
+                for (t = 0; t < (nT + 1); t++) {
+                    newPoly->coeff[x][y][z][t] = 0.0;
+                    newPoly->coeffErr[x][y][z][t] = 0.0;
+                    newPoly->coeffMask[x][y][z][t] = PS_POLY_MASK_NONE;
+                }
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psF64 psPolynomial1DEval(const psPolynomial1D* poly,
+                         psF64 x)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, NAN);
+
+    if (poly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial1DEval(x, poly));
+    } else if (poly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial1DEval(x, poly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Unknown polynomial type 0x%x found.  Evaluation failed."),
+                poly->type);
+    }
+    return(NAN);
+}
+
+// this function must accept F32 and F64 input x vectors
+psVector *psPolynomial1DEvalVector(const psPolynomial1D *poly,
+                                   const psVector *x)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(x, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(x, NULL);
+
+    psVector *tmp;
+
+    switch (x->type.type) {
+    case PS_TYPE_F64:
+        tmp = psVectorAlloc(x->n, PS_TYPE_F64);
+        for (unsigned int i=0;i<x->n;i++) {
+            tmp->data.F64[i] = psPolynomial1DEval(poly, x->data.F64[i]);
+        }
+        break;
+    case PS_TYPE_F32:
+        tmp = psVectorAlloc(x->n, PS_TYPE_F32);
+        for (unsigned int i=0;i<x->n;i++) {
+            tmp->data.F32[i] = psPolynomial1DEval(poly, x->data.F32[i]);
+        }
+        break;
+    default:
+        psError(PS_ERR_UNKNOWN, false, "invalid input data type.\n");
+        return (NULL);
+    }
+    return(tmp);
+}
+
+psF64 psPolynomial2DEval(const psPolynomial2D* poly,
+                         psF64 x,
+                         psF64 y)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, NAN);
+
+    if (poly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial2DEval(x, y, poly));
+    } else if (poly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial2DEval(x, y, poly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Unknown polynomial type 0x%x found.  Evaluation failed."),
+                poly->type);
+    }
+    return(NAN);
+}
+
+// this function must support input data types of F32 and F64
+// all input vectors data types must match (all F32 or all F64)
+psVector *psPolynomial2DEvalVector(const psPolynomial2D *poly,
+                                   const psVector *x,
+                                   const psVector *y)
+
+{
+    PS_ASSERT_POLY_NON_NULL(poly, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(x, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(x, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(y, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(y, NULL);
+
+    psVector *tmp;
+    unsigned int vecLen=x->n;
+
+    // Determine the length of the output vector to by the minimum of the x,y vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+
+    switch (x->type.type) {
+    case PS_TYPE_F32:
+        if (y->type.type != x->type.type) {
+            psError(PS_ERR_UNKNOWN, true, "type mismatch in data vectors");
+            return (NULL);
+        }
+
+        // Create output vector to return
+        tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
+
+        // Evaluate the polynomial at the specified points
+        for (unsigned int i=0; i<vecLen; i++) {
+            tmp->data.F32[i] = psPolynomial2DEval(poly,x->data.F32[i],y->data.F32[i]);
+        }
+        break;
+    case PS_TYPE_F64:
+        if (y->type.type != x->type.type) {
+            psError(PS_ERR_UNKNOWN, true, "type mismatch in data vectors");
+            return (NULL);
+        }
+
+        // Create output vector to return
+        tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+        // Evaluate the polynomial at the specified points
+        for (unsigned int i=0; i<vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial2DEval(poly,x->data.F64[i],y->data.F64[i]);
+        }
+        break;
+    default:
+        psError(PS_ERR_UNKNOWN, false, "invalid input data type.\n");
+        return (NULL);
+    }
+    // Return output vector
+    return(tmp);
+}
+
+psF64 psPolynomial3DEval(const psPolynomial3D* poly,
+                         psF64 x,
+                         psF64 y,
+                         psF64 z)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, NAN);
+
+    if (poly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial3DEval(x, y, z, poly));
+    } else if (poly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial3DEval(x, y, z, poly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Unknown polynomial type 0x%x found.  Evaluation failed."),
+                poly->type);
+    }
+    return(NAN);
+}
+
+// XXX: The output of this routine is always psF64 while 1D and 2D are
+// dependent on the input vectors.
+psVector *psPolynomial3DEvalVector(
+    const psPolynomial3D *poly,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(x, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(x, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(y, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(y, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(z, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(z, NULL);
+
+    psVector *tmp;
+    unsigned int vecLen=x->n;
+
+    // Determine the length of output vector from min of the input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate polynomial
+    // XXX: Consult with IfA: is this how they want to handle multiple data types?
+    if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F32)
+            && (z->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial3DEval(poly, x->data.F32[i],
+                                                  y->data.F32[i], z->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F32)
+               && (z->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial3DEval(poly, x->data.F32[i],
+                                                  y->data.F32[i], z->data.F64[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial3DEval(poly, x->data.F32[i],
+                                                  y->data.F64[i], z->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial3DEval(poly, x->data.F32[i],
+                                                  y->data.F64[i], z->data.F64[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F32)
+               && (z->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial3DEval(poly, x->data.F64[i],
+                                                  y->data.F32[i], z->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F32)
+               && (z->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial3DEval(poly, x->data.F64[i],
+                                                  y->data.F32[i], z->data.F64[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial3DEval(poly, x->data.F64[i],
+                                                  y->data.F64[i], z->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial3DEval(poly, x->data.F64[i],
+                                                  y->data.F64[i], z->data.F64[i]);
+        }
+    }
+
+
+    // Return output vector
+    return(tmp);
+}
+
+psF64 psPolynomial4DEval(
+    const psPolynomial4D* poly,
+    psF64 x,
+    psF64 y,
+    psF64 z,
+    psF64 t)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, NAN);
+
+    if (poly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial4DEval(x,y,z,t, poly));
+    } else if (poly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial4DEval(x,y,z,t, poly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Unknown polynomial type 0x%x found.  Evaluation failed."),
+                poly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial4DEvalVector(
+    const psPolynomial4D *poly,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z,
+    const psVector *t)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(x, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(x, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(y, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(y, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(z, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(z, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(t, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(t, NULL);
+
+    psVector *tmp;
+    unsigned int vecLen=x->n;
+
+    // Determine output vector size from min of input vectors
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (t->n < vecLen) {
+        vecLen = t->n;
+    }
+
+    // Allocoutput vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate polynomial
+    // XXX: Consult with IfA: is this how they want to handle multiple data types?
+    if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F32)
+            && (z->type.type == PS_TYPE_F32) && (t->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F32[i],
+                                                  y->data.F32[i], z->data.F32[i], t->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F32)
+               && (z->type.type == PS_TYPE_F32) && (t->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F32[i],
+                                                  y->data.F32[i], z->data.F32[i], t->data.F64[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F32)
+               && (z->type.type == PS_TYPE_F64) && (t->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F32[i],
+                                                  y->data.F32[i], z->data.F64[i], t->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F32)
+               && (z->type.type == PS_TYPE_F64) && (t->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F32[i],
+                                                  y->data.F32[i], z->data.F64[i], t->data.F64[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F32) && (t->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F32[i],
+                                                  y->data.F64[i], z->data.F32[i], t->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F32) && (t->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F32[i],
+                                                  y->data.F64[i], z->data.F32[i], t->data.F64[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F64) && (t->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F32[i],
+                                                  y->data.F64[i], z->data.F64[i], t->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F32) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F64) && (t->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F32[i],
+                                                  y->data.F64[i], z->data.F64[i], t->data.F64[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F32)
+               && (z->type.type == PS_TYPE_F32) && (t->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F64[i],
+                                                  y->data.F32[i], z->data.F32[i], t->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F32)
+               && (z->type.type == PS_TYPE_F32) && (t->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F64[i],
+                                                  y->data.F32[i], z->data.F32[i], t->data.F64[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F32)
+               && (z->type.type == PS_TYPE_F64) && (t->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F64[i],
+                                                  y->data.F32[i], z->data.F64[i], t->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F32)
+               && (z->type.type == PS_TYPE_F64) && (t->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F64[i],
+                                                  y->data.F32[i], z->data.F64[i], t->data.F64[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F32) && (t->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F64[i],
+                                                  y->data.F64[i], z->data.F32[i], t->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F32) && (t->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F64[i],
+                                                  y->data.F64[i], z->data.F32[i], t->data.F64[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F64) && (t->type.type == PS_TYPE_F32)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F64[i],
+                                                  y->data.F64[i], z->data.F64[i], t->data.F32[i]);
+        }
+    } else if ((x->type.type == PS_TYPE_F64) && (y->type.type == PS_TYPE_F64)
+               && (z->type.type == PS_TYPE_F64) && (t->type.type == PS_TYPE_F64)) {
+        for (unsigned int i = 0; i < vecLen; i++) {
+            tmp->data.F64[i] = psPolynomial4DEval(poly, x->data.F64[i],
+                                                  y->data.F64[i], z->data.F64[i],
+                                                  t->data.F64[i]);
+        }
+    }
+
+
+    // Evaluate polynomial
+    for (unsigned int i = 0; i < vecLen; i++) {
+        tmp->data.F64[i] = psPolynomial4DEval(poly,
+                                              x->data.F64[i],
+                                              y->data.F64[i],
+                                              z->data.F64[i],
+                                              t->data.F64[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomial.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomial.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomial.h	(revision 22322)
@@ -0,0 +1,357 @@
+/* @file  psPolynomial.h
+ * @brief Standard Mathematical Functions.
+ *
+ * This file will hold the prototypes for procedures which allocate, free,
+ * and evaluate various polynomials.  Those polynomial structures are also
+ * defined here.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.69 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-10-09 19:24:46 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_POLYNOMIAL_H
+#define PS_POLYNOMIAL_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psVector.h"
+#include "psScalar.h"
+
+typedef enum {
+    PS_POLY_MASK_NONE = 0,
+    PS_POLY_MASK_SET  = 1, // the coefficient should not be used (implies MASK_FIT)
+    PS_POLY_MASK_FIT  = 2, // the coefficient should not be fitted 
+    PS_POLY_MASK_BOTH = 3, // the coefficient should not be fitted 
+} psPolynomialMaskValues;
+
+/** Evaluate a non-normalized Gaussian with the given mean and sigma at the
+ *  given coordianate.
+ *
+ *  Note that this is not a Gaussian deviate.  The evaluated Gaussian is:
+ *        \f[ exp(-\frac{(x-mean)^2}{2\sigma^2}) \f]
+ *
+ *  @return float      value on the gaussian curve given the input parameters
+ */
+float psGaussian(
+    float x,                           ///< Value at which to evaluate
+    float mean,                        ///< Mean for the Gaussian
+    float sigma,                       ///< Standard deviation for the Gaussian
+    bool normal                        ///< Indicates whether result should be normalized
+);
+
+/** Polynomial Type.
+ *
+ *  Enumeration for Polynomial types.
+ */
+typedef enum {
+    PS_POLYNOMIAL_ORD,                  ///< Ordinary Polynomial
+    PS_POLYNOMIAL_CHEB                  ///< Chebyshev Polynomial
+}
+psPolynomialType;
+
+/** One-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;              ///< Polynomial type
+    unsigned int nX;                    ///< Polynomial order
+    psF64 *coeff;                       ///< Coefficients
+    psF64 *coeffErr;                    ///< Error in coefficients
+    psMaskType *coeffMask;		///< Coefficient mask
+}
+psPolynomial1D;
+
+/** Two-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;              ///< Polynomial type
+    unsigned int nX;            ///< Polynomial order in x
+    unsigned int nY;            ///< Polynomial order in y
+    psF64 **coeff;                      ///< Coefficients
+    psF64 **coeffErr;                   ///< Error in coefficients
+    psMaskType **coeffMask;                  ///< Coefficients mask
+}
+psPolynomial2D;
+
+/** Three-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;              ///< Polynomial type
+    unsigned int nX;            ///< Polynomial order in x
+    unsigned int nY;            ///< Polynomial order in y
+    unsigned int nZ;            ///< Polynomial order in z
+    psF64 ***coeff;                     ///< Coefficients
+    psF64 ***coeffErr;                  ///< Error in coefficients
+    psMaskType ***coeffMask;                 ///< Coefficients mask
+}
+psPolynomial3D;
+
+/** Four-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;              ///< Polynomial type
+    unsigned int nX;			///< Polynomial order in x
+    unsigned int nY;			///< Polynomial order in y
+    unsigned int nZ;			///< Polynomial order in z
+    unsigned int nT;			///< Polynomial order in t
+    psF64 ****coeff;                    ///< Coefficients
+    psF64 ****coeffErr;                 ///< Error in coefficients
+    psMaskType ****coeffMask;		///< Coefficients mask
+}
+psPolynomial4D;
+
+
+/** Allocates a psPolynomial1D structure with n terms
+ *
+ *  @return  psPolynomial1D*    new 1-D polynomial struct
+ */
+psPolynomial1D* psPolynomial1DAlloc(
+    psPolynomialType type,             ///< Polynomial Type
+    unsigned int nX                    ///< Number of terms
+) PS_ATTR_MALLOC;
+
+/** Allocates a 2-D polynomial structure
+ *
+ *  @return  psPolynomial2D*    new 2-D polynomial struct
+ */
+psPolynomial2D* psPolynomial2DAlloc(
+    psPolynomialType type,             ///< Polynomial Type
+    unsigned int nX,                   ///< Number of terms in x
+    unsigned int nY                    ///< Number of terms in y
+) PS_ATTR_MALLOC;
+
+/** Allocates a 3-D polynomial structure
+ *
+ *  @return  psPolynomial3D*    new 3-D polynomial struct
+ */
+psPolynomial3D* psPolynomial3DAlloc(
+    psPolynomialType type,             ///< Polynomial Type
+    unsigned int nX,                   ///< Number of terms in x
+    unsigned int nY,                   ///< Number of terms in y
+    unsigned int nZ                    ///< Number of terms in z
+) PS_ATTR_MALLOC;
+
+/** Allocates a 4-D polynomial structure
+ *
+ *  @return  psPolynomial4D*    new 4-D polynomial struct
+ */
+psPolynomial4D* psPolynomial4DAlloc(
+    psPolynomialType type,             ///< Polynomial Type
+    unsigned int nX,                   ///< Number of terms in x
+    unsigned int nY,                   ///< Number of terms in y
+    unsigned int nZ,                   ///< Number of terms in z
+    unsigned int nT                    ///< Number of terms in t
+) PS_ATTR_MALLOC;
+
+bool psPolynomial2DRecycle(psPolynomial2D *poly,
+                           psPolynomialType type,
+                           unsigned int nX,
+                           unsigned int nY);
+
+psPolynomial2D *psPolynomial2DCopy(psPolynomial2D *out,
+                                   psPolynomial2D *poly);
+
+/** Evaluates a 1-D polynomial at specific coordinates.
+ *
+ *  @return psF64    result of polynomial at given location
+ */
+psF64 psPolynomial1DEval(
+    const psPolynomial1D* poly,        ///< Coefficients for the polynomial
+    psF64 x                            ///< location at which to evaluate
+);
+
+/** Evaluates a 2-D polynomial at specific coordinates.
+ *
+ *  @return psF64    result of polynomial at given location
+ */
+psF64 psPolynomial2DEval(
+    const psPolynomial2D* poly,        ///< Coefficients for the polynomial
+    psF64 x,                           ///< x location at which to evaluate
+    psF64 y                            ///< y location at which to evaluate
+);
+
+/** Evaluates a 3-D polynomial at specific coordinates.
+ *
+ *  @return psF64    result of polynomial at given location
+ */
+psF64 psPolynomial3DEval(
+    const psPolynomial3D* poly,        ///< Coefficients for the polynomial
+    psF64 x,                           ///< x location at which to evaluate
+    psF64 y,                           ///< y location at which to evaluate
+    psF64 z                            ///< z location at which to evaluate
+);
+
+/** Evaluates a 4-D polynomial at specific coordinates.
+ *
+ *  @return psF64    result of polynomial at given location
+ */
+psF64 psPolynomial4DEval(
+    const psPolynomial4D* poly,        ///< Coefficients for the polynomial
+    psF64 x,                           ///< x location at which to evaluate
+    psF64 y,                           ///< y location at which to evaluate
+    psF64 z,                           ///< z location at which to evaluate
+    psF64 t                            ///< t location at which to evaluate
+);
+
+/** Evaluates a 1-D polynomial at specific sets of coordinates
+ *
+ *  @return psVector*    results of polynomials at given locations
+ */
+psVector *psPolynomial1DEvalVector(
+    const psPolynomial1D *poly,        ///< Coefficients for the polynomial
+    const psVector *x                  ///< x locations at which to evaluate
+);
+
+/** Evaluates a 2-D polynomial at specific sets of coordinates
+ *
+ *  @return psVector*    results of polynomial at given locations
+ */
+psVector *psPolynomial2DEvalVector(
+    const psPolynomial2D *poly,        ///< Coefficients for the polynomial
+    const psVector *x,                 ///< x locations at which to evaluate
+    const psVector *y                  ///< y locations at which to evaluate
+);
+
+/** Evaluates a 3-D polynomial at specific sets of coordinates
+ *
+ *  @return psVector*    results of polynomial at given locations
+ */
+psVector *psPolynomial3DEvalVector(
+    const psPolynomial3D *poly,        ///< Coefficients for the polynomial
+    const psVector *x,                 ///< x locations at which to evaluate
+    const psVector *y,                 ///< y locations at which to evaluate
+    const psVector *z                  ///< z locations at which to evaluate
+);
+
+/** Evaluates a 4-D polynomial at specific sets of coordinates
+ *
+ *  @return psVector*    results of polynomial at given locations
+ */
+psVector *psPolynomial4DEvalVector(
+    const psPolynomial4D *poly,        ///< Coefficients for the polynomial
+    const psVector *x,                 ///< x locations at which to evaluate
+    const psVector *y,                 ///< y locations at which to evaluate
+    const psVector *z,                 ///< z locations at which to evaluate
+    const psVector *t                  ///< t locations at which to evaluate
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psPolynomial1D structure, false otherwise.
+ */
+bool psMemCheckPolynomial1D(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psPolynomial2D structure, false otherwise.
+ */
+bool psMemCheckPolynomial2D(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psPolynomial3D structure, false otherwise.
+ */
+bool psMemCheckPolynomial3D(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psPolynomial4D structure, false otherwise.
+ */
+bool psMemCheckPolynomial4D(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+
+
+/** Creates the specified number of chebyshev polys.
+ *
+ *  @return psPolynomial1D** The chebyshev polys.
+ *
+ */
+psPolynomial1D **p_psCreateChebyshevPolys(
+    psS32 numPolys
+);
+
+typedef struct
+{
+    int n;                              ///< The number of Chebyshev polys.
+    psPolynomial1D **chebyPolys;        ///< THe chebyshev polys
+
+}
+p_chebyPolys;
+
+
+/*****************************************************************************
+    PS_POLY macros:
+*****************************************************************************/
+#define PS_ASSERT_POLY1D(NAME, RVAL) \
+if (false == psMemCheckPolynomial1D(NAME)) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: argument %s is not a psPolynomial1D struct.\n",\
+            #NAME); \
+    return(RVAL); \
+} \
+
+#define PS_ASSERT_POLY_NON_NULL(NAME, RVAL) \
+if ((NAME) == NULL || (NAME)->coeff == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: polynomial %s or its coeffs is NULL.", \
+            #NAME); \
+    return(RVAL); \
+} \
+
+#define PS_ASSERT_POLY_TYPE(NAME, TYPE, RVAL) \
+if ((NAME)->type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: polynomial %s has wrong type.", #NAME); \
+    return(RVAL); \
+} \
+
+#define PS_ASSERT_POLY_VALID_TYPE(TYPE, RVAL) \
+if ((TYPE != PS_POLYNOMIAL_ORD) && \
+        (TYPE != PS_POLYNOMIAL_CHEB)) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: invalid type %d for polynomial", TYPE); \
+    return(RVAL); \
+} \
+
+#define PS_POLY_PRINT_1D(NAME) \
+printf("Poly %s: (nX) is (%d)\n", #NAME, NAME->nX);\
+for (psS32 i = 0 ; i < NAME->nX+1 ; i++) {\
+    printf("%s->coeff[%d] is %f\n", #NAME, i, NAME->coeff[i]); \
+}\
+
+#define PS_POLY_PRINT_2D(NAME) \
+printf("Poly %s: (nX, nY) is (%d, %d)\n", #NAME, NAME->nX, NAME->nY);\
+for (psS32 i = 0 ; i < NAME->nX+1 ; i++) {\
+    for (psS32 j = 0 ; j < NAME->nY+1 ; j++) {\
+        printf("%s->coeff[%d][%d] is %f\n", #NAME, i, j, NAME->coeff[i][j]); \
+    }\
+}\
+
+/// @}
+#endif // #ifndef PS_POLYNOMIAL_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMD.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMD.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMD.c	(revision 22322)
@@ -0,0 +1,595 @@
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "psMemory.h"
+#include "psVector.h"
+#include "psImage.h"
+#include "psPolynomial.h"
+#include "psArray.h"
+#include "psMatrix.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psAssert.h"
+#include "psTrace.h"
+
+#include "psPolynomialMD.h"
+
+
+//#define DEBUG
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Private (file-static) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Destructor
+static void polynomialMDFree(psPolynomialMD *poly // Polynomial
+    )
+{
+    if (!poly) return;
+    psFree(poly->orders);
+    psFree(poly->coeff);
+
+    psFree(poly->fitMatrix);
+    psFree(poly->fitVector);
+    psFree(poly->tmpMatrix);
+    psFree(poly->tmpVector);
+
+    psFree(poly->fitBuffer);
+
+    psFree(poly->ownMask);
+    psFree(poly->deviations);
+
+    psFree(poly->LUperm);
+    psFree(poly->LU);
+    
+    return;
+}
+
+// Generate a least-squares matrix and vector
+// Note: only generates the diagonal and lower triangle of the matrix
+static void polynomialMDLeastSquares(const psPolynomialMD *poly, // Polynomial
+                                     psImage *matrix, // Least-squares matrix to fill out
+                                     psVector *vector, // Least-squares vector to fill out
+                                     const psVector *coords, // Coordinates
+                                     float value, // Value
+                                     float error, // Error
+                                     psVector *buffer // Buffer for evaluations
+    )
+{
+    int numTerms = vector->n;           // Number of terms
+    psAssert(matrix->numCols == numTerms && matrix->numRows == numTerms, "impossible");
+    psAssert(buffer && buffer->n == numTerms && buffer->type.type == PS_TYPE_F64, "impossible");
+
+#ifdef DEBUG
+    psImageInit(matrix, NAN);
+    psVectorInit(vector, NAN);
+#endif
+
+    buffer->data.F64[0] = 1.0;
+    for (int i = 0, index = 1; i < poly->dim; i++) {
+        int order = poly->orders->data.U8[i]; // Order of polynomial
+        float coord = coords->data.F32[i]; // Coordinate of interest
+        double value = coord;           // Value of polynomial stages
+        buffer->data.F64[index++] = value;
+        for (int j = 2; j <= order; j++, index++) {
+            value *= coord;
+            buffer->data.F64[index] = value;
+        }
+    }
+
+    double invSigma2 = (error == 0.0) ? 1.0 : 1.0 / PS_SQR(error); // 1/sigma^2
+    for (int i = 0; i < numTerms; i++) {
+        for (int j = 0; j < i; j++) {
+            matrix->data.F64[i][j] = buffer->data.F64[i] * buffer->data.F64[j] * invSigma2;
+        }
+        matrix->data.F64[i][i] = PS_SQR(buffer->data.F64[i]) * invSigma2;
+        vector->data.F64[i] = value * buffer->data.F64[i] * invSigma2;
+    }
+
+    return;
+}
+
+// Accumulate the lower triangle of the matrix
+static void polynomialMDAccumulate(psImage *targetMatrix, // Final least-squares matrix
+                                   psVector *targetVector, // Final least-squares vector
+                                   const psImage *sourceMatrix, // Input least-squares matrix
+                                   const psVector *sourceVector // Input least-squares vector
+    )
+{
+    int numTerms = targetVector->n;     // Number of terms in polynomial
+
+    for (int j = 0; j < numTerms; j++) {
+        for (int k = 0; k < j; k++) {
+            targetMatrix->data.F64[j][k] += sourceMatrix->data.F64[j][k];
+        }
+        targetMatrix->data.F64[j][j] += sourceMatrix->data.F64[j][j];
+        targetVector->data.F64[j] += sourceVector->data.F64[j];
+    }
+
+    return;
+}
+
+// Fill in the upper triangle of the matrix
+static void polynomialMDFill(psImage *matrix // Final least-squares matrix
+    )
+{
+    int numTerms = matrix->numCols;     // Number of terms in polynomial
+
+    for (int j = 0; j < numTerms; j++) {
+        for (int k = j + 1; k < numTerms; k++) {
+            matrix->data.F64[j][k] = matrix->data.F64[k][j];
+        }
+    }
+
+    return;
+}
+
+// Calculate the standard deviation of the fit
+static void polynomialMDStdev(psPolynomialMD *poly, // Polynomial for which to measure stdev
+                              psVector *deviations, // Deviations, or NULL
+                              const psArray *coords, // Array of coordinates
+                              const psVector *values, // Measured values
+                              const psVector *mask, // Mask for values
+			      psMaskType maskVal 
+                              )
+{
+    psAssert(poly, "impossible");
+    psAssert(coords, "impossible");
+    psAssert(values, "impossible");
+
+    double rms = 0.0;                   // Root mean square deviation
+    int numValues = values->n;          // Number of values
+    int numGood = numValues;            // Number of good values
+    for (int i = 0; i < numValues; i++) {
+        if (mask && (mask->data.U8[i] & maskVal)) {
+            numGood--;
+            continue;
+        }
+        float diff = values->data.F32[i] - psPolynomialMDEval(poly, coords->data[i]);
+        if (deviations) {
+            deviations->data.F32[i] = diff;
+        }
+        rms += PS_SQR(diff);
+    }
+    poly->stdevFit = sqrt(rms / (double)numGood);
+
+    return;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+psPolynomialMD *psPolynomialMDAlloc(const psVector *orders)
+{
+    psPolynomialMD *poly = psAlloc(sizeof(psPolynomialMD));
+    psMemSetDeallocator(poly, (psFreeFunc)polynomialMDFree);
+
+    int dim = orders->n;                // Number of dimensions
+
+        #define CHECK_ORDER_CASE_SIGNED(TYPE) \
+          case PS_TYPE_##TYPE: { \
+              for (int i = 0; i < dim; i++) { \
+                  if (orders->data.TYPE[i] <= 0) { \
+                      psAbort("Order %d is non-positive.", i); \
+                  } \
+              } \
+              break; \
+          }
+
+    switch (orders->type.type) {
+        CHECK_ORDER_CASE_SIGNED(S8);
+        CHECK_ORDER_CASE_SIGNED(S16);
+        CHECK_ORDER_CASE_SIGNED(S32);
+        CHECK_ORDER_CASE_SIGNED(S64);
+      case PS_TYPE_F32:
+      case PS_TYPE_F64:
+        psAbort("Floating-point order vector is not supported.");
+      case PS_TYPE_U8:
+      case PS_TYPE_U16:
+      case PS_TYPE_U32:
+      case PS_TYPE_U64:
+        // These can pass unchecked
+        break;
+      default:
+        psAbort("Unknown type: %x", orders->type.type);
+    }
+
+    poly->dim = dim;
+    poly->orders = psVectorCopy(NULL, orders, PS_TYPE_U8);
+    poly->numFit = 0;
+    poly->stdevFit = NAN;
+
+    int numTerms = 1;                   // Number of terms in polynomial
+    for (int i = 0; i < dim; i++) {
+        numTerms += poly->orders->data.U8[i];
+    }
+    poly->coeff = psVectorAlloc(numTerms, PS_TYPE_F64);
+
+    // internal temporary variables so we can use this in a tight, threaded loop
+    poly->fitMatrix = NULL;
+    poly->fitVector = NULL;
+    poly->tmpMatrix = NULL;
+    poly->tmpVector = NULL;
+
+    poly->fitBuffer = NULL;
+
+    poly->ownMask = NULL;
+    poly->deviations = NULL;
+
+    poly->LUperm = NULL;
+    poly->LU = NULL;
+
+    return poly;
+}
+
+double psPolynomialMDEval(const psPolynomialMD *poly, ///< Polynomial
+                          const psVector *coords ///< Coordinates
+    )
+{
+    PS_ASSERT_POLYNOMIALMD_NON_NULL(poly, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(coords, NAN);
+    PS_ASSERT_VECTOR_SIZE(coords, (long)poly->dim, NAN);
+    PS_ASSERT_VECTOR_TYPE(coords, PS_TYPE_F32, NAN);
+
+    psVector *orders = poly->orders; // Orders for each polynomial
+    psF64 *coeff = poly->coeff->data.F64; // Coefficients
+
+    double sum = coeff[0];              // Sum of all polynomials
+    for (int i = 0, index = 1; i < poly->dim; i++) {
+        int order = orders->data.U8[i]; // Order of polynomial
+        float coord = coords->data.F32[i]; // Coordinate of interest
+        float value = coord; // Value of the polynomial stage
+        sum += coeff[index++] * value;
+        for (int j = 2; j <= order; j++, index++) {
+            value *= coord;
+            sum += coeff[index] * value;
+        }
+    }
+
+    return sum;
+}
+
+bool psPolynomialMDFit(psPolynomialMD *poly, const psVector *values, const psVector *errors,
+                       const psVector *mask, psMaskType maskVal, const psArray *coordsArray)
+{
+    PS_ASSERT_POLYNOMIALMD_NON_NULL(poly, false);
+    PS_ASSERT_VECTOR_NON_NULL(values, false);
+    PS_ASSERT_VECTOR_TYPE(values, PS_TYPE_F32, false);
+    if (errors) {
+        PS_ASSERT_VECTOR_NON_NULL(errors, false);
+        PS_ASSERT_VECTOR_TYPE(errors, PS_TYPE_F32, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(values, errors, false);
+    }
+    if (maskVal == 0) {
+        mask = NULL;
+    }
+    if (mask) {
+        PS_ASSERT_VECTOR_NON_NULL(mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_MASK, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(values, mask, false);
+    }
+    PS_ASSERT_ARRAY_NON_NULL(coordsArray, false);
+    PS_ASSERT_ARRAY_SIZE(coordsArray, values->n, false);
+
+    int numValues = values->n;          // Number of values
+    int numTerms = poly->coeff->n;      // Number of terms
+
+    // we recycle matrices and vectors carried by poly to avoid alloc / threading hits
+    poly->fitMatrix = psImageRecycle(poly->fitMatrix, numTerms, numTerms, PS_TYPE_F64); // Least-squares matrix
+    poly->fitVector = psVectorRecycle(poly->fitVector, numTerms, PS_TYPE_F64); // Least-squares vector
+
+    psImageInit(poly->fitMatrix, 0.0);
+    psVectorInit(poly->fitVector, 0.0);
+
+    poly->tmpMatrix = psImageRecycle (poly->tmpMatrix, numTerms, numTerms, PS_TYPE_F64); // Least-squares matrix for each term
+    poly->tmpVector = psVectorRecycle(poly->tmpVector, numTerms, PS_TYPE_F64); // Least-squares vector for each term
+    poly->fitBuffer = psVectorRecycle(poly->fitBuffer, numTerms, PS_TYPE_F64); // Buffer for evaluations of polynomial stages
+
+    for (int i = 0; i < numValues; i++) {
+        psVector *coords = coordsArray->data[i];
+        PS_ASSERT_VECTOR_NON_NULL(coords, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(coords, poly->orders, false);
+        PS_ASSERT_VECTOR_TYPE(coords, PS_TYPE_F32, false);
+
+        if (mask && (mask->data.PS_TYPE_MASK_DATA[i] & maskVal)) {
+            continue;
+        }
+
+	float err = errors ? errors->data.F32[i] : 0.0;
+        polynomialMDLeastSquares(poly, poly->tmpMatrix, poly->tmpVector, coords, values->data.F32[i], err, poly->fitBuffer);
+        polynomialMDAccumulate(poly->fitMatrix, poly->fitVector, poly->tmpMatrix, poly->tmpVector);
+    }
+    polynomialMDFill(poly->fitMatrix);
+
+    poly->LU = psMatrixLUD(poly->LU, &poly->LUperm, poly->fitMatrix); // LU-decomposed matrix
+    if (!poly->LU) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to LU-Decompose least-squares matrix.");
+        return false;
+    }
+
+    poly->coeff = psMatrixLUSolve(poly->coeff, poly->LU, poly->fitVector, poly->LUperm);
+    if (!poly->coeff) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to solve least-squares equation.");
+        return false;
+    }
+
+    polynomialMDStdev(poly, poly->deviations, coordsArray, values, mask, maskVal);
+
+    return true;
+}
+
+// XXX this function should take a (psMaskType markVal) argument
+bool psPolynomialMDClipFit(psPolynomialMD *poly, const psVector *values, const psVector *errors,
+                           const psVector *mask, psMaskType maskVal, const psArray *coordsArray,
+                           int numIter, float rej) 
+{
+
+    PS_ASSERT_POLYNOMIALMD_NON_NULL(poly, false);
+    PS_ASSERT_VECTOR_NON_NULL(values, false);
+    PS_ASSERT_VECTOR_TYPE(values, PS_TYPE_F32, false);
+    if (errors) {
+        PS_ASSERT_VECTOR_NON_NULL(errors, false);
+        PS_ASSERT_VECTOR_TYPE(errors, PS_TYPE_F32, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(values, errors, false);
+    }
+    if (maskVal == 0) {
+        mask = NULL;
+    }
+    if (mask) {
+        PS_ASSERT_VECTOR_NON_NULL(mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_MASK, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(values, mask, false);
+    }
+    PS_ASSERT_ARRAY_NON_NULL(coordsArray, false);
+    PS_ASSERT_ARRAY_SIZE(coordsArray, (long)values->n, false);
+    PS_ASSERT_INT_NONNEGATIVE(numIter, false);
+    PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
+
+    int numValues = values->n;          // Number of values
+    int numTerms = poly->coeff->n;      // Number of terms
+
+    int numClipped = INT_MAX;           // Number of values clipped int an interation
+    int numGood = numValues;            // Number of good values
+
+    // copy the input mask to a local temporary mask 
+    poly->ownMask = psVectorRecycle(poly->ownMask, numValues, PS_TYPE_U8); // Our own mask for input values
+    psVectorInit(poly->ownMask, 0);
+    for (int i = 0; mask && (i < numValues); i++) {
+	if (mask->data.PS_TYPE_MASK_DATA[i] & maskVal) {
+	    poly->ownMask->data.U8[i] = 0xff;
+	    numGood--;
+	}
+    }
+
+    // make sure we have storage for the residuals
+    poly->deviations = psVectorRecycle (poly->deviations, numValues, PS_TYPE_F32);
+
+    for (int iter = 0; (iter < numIter) && (numClipped > 0) && (numGood > numTerms); iter++) {
+        numClipped = 0;
+
+	// XXX raise error?
+        if (!psPolynomialMDFit(poly, values, errors, poly->ownMask, maskVal, coordsArray)) {
+            return false;
+        }
+
+        psTrace("psLib.math", 7, "RMS from %d points is %lf\n", numGood, poly->stdevFit);
+
+        // Reject
+        float limit = rej * poly->stdevFit; // Rejection limit
+        for (int i = 0; i < numValues; i++) {
+
+# if (0)
+	    fprintf (stderr, "coords: ");
+	    psVector *coords = coordsArray->data[i];
+	    for (int j = 0; j < coords->n; j++) {
+		fprintf (stderr, "%f ", coords->data.F32[j]);
+	    }
+# endif
+	    
+	    psTrace("psLib.math", 10, "point %d (%f,%f: %f > %f)\n",
+		    i, values->data.F32[i], values->data.F32[i] - poly->deviations->data.F32[i], poly->deviations->data.F32[i], limit);
+
+            if (poly->ownMask->data.U8[i]) continue;
+
+            if (fabs(poly->deviations->data.F32[i]) > limit) {
+                psTrace("psLib.math", 9, "Rejected point %d (%f,%f: %f > %f)\n",
+                        i, values->data.F32[i], values->data.F32[i] + poly->deviations->data.F32[i], 
+                        poly->deviations->data.F32[i], limit);
+                poly->ownMask->data.U8[i] = 0xff;
+                numClipped++;
+                numGood--;
+            }
+        }
+        psTrace("psLib.math", 7, "Rejected %d points (%d remaining)\n", numClipped, numGood);
+    }
+
+    if (numClipped > 0) {
+        // Need to do a final re-evaluation of the fit
+        if (!psPolynomialMDFit(poly, values, errors, poly->ownMask, maskVal, coordsArray)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// XXX EAM : This function was trying to save calculation time at the expense of a LOT of allocs.
+# if (0)
+// Accumulate and solve the least-squares equation
+static bool polynomialMDClipFit(psPolynomialMD *poly, // Polynomial
+                                psImage *matrix, // Least-squares matrix
+                                psVector *vector, // Least-squares vector
+                                psImage **lu, // LU-decomposed matrix
+                                psVector **perm, // Permutations vector
+                                const psArray *matrices, // Individual least-squares matrices
+                                const psArray *vectors, // Individual least-squares vectors
+                                const psVector *mask, // Mask for values
+    )
+{
+    int numValues = vectors->n;         // Number of values
+
+    // Accumulate the least-squares matrix and vector
+    psImageInit(matrix, 0.0);
+    psVectorInit(vector, 0.0);
+    for (int i = 0; i < numValues; i++) {
+        if (mask->data.U8[i]) {
+            continue;
+        }
+
+        psImage *newMatrix = matrices->data[i]; // Matrix to accumulate
+        psVector *newVector = vectors->data[i]; // Vector to accumulate
+
+        polynomialMDAccumulate(matrix, vector, newMatrix, newVector);
+    }
+    polynomialMDFill(matrix);
+
+    // Solve least-squares equation
+    *lu = psMatrixLUD(*lu, perm, matrix);
+    if (!*lu) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to LU-Decompose least-squares matrix.");
+        return false;
+    }
+    poly->coeff = psMatrixLUSolve(poly->coeff, *lu, vector, *perm);
+    if (!poly->coeff) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to solve least-squares equation.");
+        return false;
+    }
+    return true;
+}
+
+
+bool psPolynomialMDClipFit(psPolynomialMD *poly, const psVector *values, const psVector *errors,
+                           const psVector *mask, psMaskType maskVal, const psArray *coordsArray,
+                           int numIter, float rej)
+{
+    PS_ASSERT_POLYNOMIALMD_NON_NULL(poly, false);
+    PS_ASSERT_VECTOR_NON_NULL(values, false);
+    PS_ASSERT_VECTOR_TYPE(values, PS_TYPE_F32, false);
+    if (errors) {
+        PS_ASSERT_VECTOR_NON_NULL(errors, false);
+        PS_ASSERT_VECTOR_TYPE(errors, PS_TYPE_F32, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(values, errors, false);
+    }
+    if (maskVal == 0) {
+        mask = NULL;
+    }
+    if (mask) {
+        PS_ASSERT_VECTOR_NON_NULL(mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_MASK, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(values, mask, false);
+    }
+    PS_ASSERT_ARRAY_NON_NULL(coordsArray, false);
+    PS_ASSERT_ARRAY_SIZE(coordsArray, (long)values->n, false);
+    PS_ASSERT_INT_NONNEGATIVE(numIter, false);
+    PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
+
+    int numValues = values->n;          // Number of values
+    int numTerms = poly->coeff->n;      // Number of terms
+
+    psArray *matrices = psArrayAlloc(numValues); // Least-squares matrix for each input value
+    psArray *vectors = psArrayAlloc(numValues); // Least-squares vector for each input value
+
+    // Generate least-squares matrices and vectors
+    psVector *buffer = psVectorAlloc(numTerms, PS_TYPE_F64); // Buffer for evaluations of polynomial stages
+    for (int i = 0; i < numValues; i++) {
+        psVector *coords = coordsArray->data[i];
+        PS_ASSERT_VECTOR_NON_NULL(coords, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(coords, poly->orders, false);
+        PS_ASSERT_VECTOR_TYPE(coords, PS_TYPE_F32, false);
+
+        if (mask && (mask->data.PS_TYPE_MASK_DATA[i] & maskVal)) {
+            continue;
+        }
+
+        matrices->data[i] = psImageAlloc(numTerms, numTerms, PS_TYPE_F64);
+        vectors->data[i] = psVectorAlloc(numTerms, PS_TYPE_F64);
+
+        polynomialMDLeastSquares(poly, matrices->data[i], vectors->data[i], coords, values->data.F32[i],
+                                 errors ? errors->data.F32[i] : 0.0, buffer);
+    }
+    psFree(buffer);
+
+    // Iterate over the solution
+    int numGood = numValues;            // Number of good values
+    psImage *matrix = psImageAlloc(numTerms, numTerms, PS_TYPE_F64); // Least-squares matrix
+    psVector *vector = psVectorAlloc(numTerms, PS_TYPE_F64); // Least-squares vector
+    psVector *ownMask = psVectorAlloc(numValues, PS_TYPE_U8); // Our own mask for input values
+    psVectorInit(ownMask, 0);
+    if (mask) {
+        for (int i = 0; i < numValues; i++) {
+            if (mask->data.PS_TYPE_MASK_DATA[i] & maskVal) {
+                ownMask->data.U8[i] = 0xff;
+                numGood--;
+            }
+        }
+    }
+    psImage *lu = NULL;                 // LU-decomposed matrix
+    psVector *perm = NULL;              // Permutation vector
+    psVector *deviations = psVectorAlloc(numValues, PS_TYPE_F32); // Deviations from fit
+    int numClipped = INT_MAX;           // Number of values clipped int an interation
+    for (int iter = 0; iter < numIter && numClipped > 0 && numGood > numTerms; iter++) {
+        numClipped = 0;
+
+        if (!polynomialMDClipFit(poly, matrix, vector, &lu, &perm, matrices, vectors, ownMask)) {
+            psFree(matrix);
+            psFree(vector);
+            psFree(lu);
+            psFree(perm);
+            psFree(matrices);
+            psFree(vectors);
+            psFree(ownMask);
+            psFree(deviations);
+            return false;
+        }
+
+        polynomialMDStdev(poly, deviations, coordsArray, values, ownMask);
+        psTrace("psLib.math", 7, "RMS from %d points is %lf\n", numGood, poly->stdevFit);
+
+        // Reject
+        float limit = rej * poly->stdevFit; // Rejection limit
+        for (int i = 0; i < numValues; i++) {
+            if (ownMask->data.U8[i]) {
+                continue;
+            }
+            if (fabs(deviations->data.F32[i]) > limit) {
+                psTrace("psLib.math", 9, "Rejected point %d (%f,%f: %f > %f)\n",
+                        i, values->data.F32[i], psPolynomialMDEval(poly, coordsArray->data[i]),
+                        deviations->data.F32[i], limit);
+                ownMask->data.U8[i] = 0xff;
+                numClipped++;
+                numGood--;
+            }
+        }
+        psTrace("psLib.math", 7, "Rejected %d points (%d remaining)\n", numClipped, numGood);
+    }
+
+    if (numClipped > 0) {
+        // Need to do a final re-evaluation of the fit
+        if (!polynomialMDClipFit(poly, matrix, vector, &lu, &perm, matrices, vectors, ownMask)) {
+            psFree(matrix);
+            psFree(vector);
+            psFree(lu);
+            psFree(perm);
+            psFree(matrices);
+            psFree(vectors);
+            psFree(ownMask);
+            psFree(deviations);
+            return false;
+        }
+        polynomialMDStdev(poly, NULL, coordsArray, values, ownMask);
+    }
+
+    psFree(matrix);
+    psFree(vector);
+    psFree(lu);
+    psFree(perm);
+    psFree(matrices);
+    psFree(vectors);
+    psFree(ownMask);
+    psFree(deviations);
+
+    return true;
+}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMD.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMD.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMD.h	(revision 22322)
@@ -0,0 +1,69 @@
+#ifndef PS_POLYNOMIAL_MD_H
+#define PS_POLYNOMIAL_MD_H
+
+#include <psVector.h>
+#include <psPolynomial.h>
+#include <psArray.h>
+
+/// Multi-dimensional polynomial
+typedef struct {
+    int dim;                            ///< Dimensions
+    psVector *orders;                   ///< Polynomial orders for each dimension
+    psVector *coeff;                    ///< Coefficients
+    int numFit;                         ///< Number of values in fit
+    float stdevFit;                     ///< Standard deviation of fit
+
+    psImage  *fitMatrix;
+    psVector *fitVector;
+
+    psImage  *tmpMatrix;
+    psVector *tmpVector;
+
+    psVector *fitBuffer;
+
+    psVector *ownMask;
+    psVector *deviations;
+
+    psVector *LUperm;
+    psImage  *LU;
+
+} psPolynomialMD;
+
+/// Assertion for valid polynomial
+#define PS_ASSERT_POLYNOMIALMD_NON_NULL(POLY, RETURN) \
+    if (!(POLY) || (POLY)->dim < 0 || !(POLY)->orders || (POLY)->orders->n != (POLY)->dim || \
+        !(POLY)->coeff) { \
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Invalid polynomial."); \
+        return RETURN; \
+    }
+
+/// Constructor
+psPolynomialMD *psPolynomialMDAlloc(const psVector *orders ///< Orders for each dimension
+    ) PS_ATTR_MALLOC;
+
+/// Evaluate a polynomial
+double psPolynomialMDEval(const psPolynomialMD *poly, ///< Polynomial
+                          const psVector *coords ///< Coordinates
+    );
+
+/// Fit a polynomial
+bool psPolynomialMDFit(psPolynomialMD *poly, ///< Polynomial to fit
+                       const psVector *values, ///< Values
+                       const psVector *errors, ///< Errors
+                       const psVector *mask, ///< Mask
+                       psMaskType maskVal, ///< Value to mask
+                       const psArray *coordsArray ///< Array of coordinates
+    );
+
+/// Fit a polynomial, with clipping
+bool psPolynomialMDClipFit(psPolynomialMD *poly, ///< Polynomial to fit
+                           const psVector *values, ///< Values
+                           const psVector *errors, ///< Errors
+                           const psVector *mask, ///< Mask
+                           psMaskType maskVal, ///< Value to mask
+                           const psArray *coordsArray, ///< Array of coordinates
+                           int numIter,    ///< Number of rejection iterations
+                           float rej    ///< Rejection limit, standard deviations
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMetadata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMetadata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMetadata.c	(revision 22322)
@@ -0,0 +1,545 @@
+/** @file  psPolyMetadata.c
+ *
+ *
+ *  @brief Contains metadata structures, enumerations and functions prototypes.
+ *
+ *  This file defines metadata item, metadata type, metadata flags, metadata containers, and function
+ *  prototypes necessary creating psLib metadata APIs
+ *
+ *  @ingroup Metadata
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-05-05 00:09:04 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "psType.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psMetadata.h"
+#include "psString.h"
+
+#include "psAssert.h"
+
+psPolynomial1D *psPolynomial1DfromMetadata(const psMetadata *folder)
+{
+    PS_ASSERT_PTR_NON_NULL(folder, NULL);
+    bool status;
+    char keyword[80];
+
+    // get polynomial orders
+    int nXorder = psMetadataLookupS32 (&status, folder, "NORDER_X");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial1D in metadata is missing NORDER_X");
+        return NULL;
+    }
+    // how many polynomial coeffs are expected?
+    int nElementsExpected = psMetadataLookupS32 (&status, folder, "NELEMENTS");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial1D in metadata is missing NELEMENTS");
+        return NULL;
+    }
+
+    psPolynomial1D *poly = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, nXorder);
+
+    int nElements = 0;
+    for (int nx = 0; nx < poly->nX + 1; nx++) {
+        sprintf (keyword, "VAL_X%02d", nx);
+        poly->coeff[nx] = psMetadataLookupF64 (&status, folder, keyword);
+        if (!status) {
+            // an undefined component implies the component was masked
+            // this is symmetrical with the 1DtoMD function
+            poly->coeff[nx] = 0;
+            poly->coeffErr[nx] = 0;
+            poly->coeffMask[nx] = PS_POLY_MASK_SET;
+        } else {
+            poly->coeffMask[nx] = PS_POLY_MASK_NONE;
+            nElements ++;
+        }
+        sprintf (keyword, "ERR_X%02d", nx);
+        poly->coeffErr[nx] = psMetadataLookupF64 (&status, folder, keyword);
+    }
+    if (nElements != nElementsExpected) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psPolynomial1D in metadata does not have the correct number of coefficients: "
+                "%d found vs %d expected", nElements, nElementsExpected);
+        psFree(poly);
+        return NULL;
+    }
+    return (poly);
+}
+
+// XXX : these may need F64, or %g format for output
+bool psPolynomial1DtoMetadata(psMetadata *md,
+                              const psPolynomial1D *poly,
+                              const char *format,
+                              ...)
+{
+    PS_ASSERT_PTR_NON_NULL(md, false);
+    PS_ASSERT_PTR_NON_NULL(poly, false);
+    PS_ASSERT_PTR_NON_NULL(format, false);
+
+    //XXX:  Current implementation only supports ordinary polynomials.
+    if (poly->type != PS_POLYNOMIAL_ORD)
+        return false;
+
+    int Nbyte;
+    char tmp;
+    char *root;
+    va_list argp;
+
+    // skip past whitespace (output name should not include whitespace)
+    char *fmt = (char *) format;
+    while (isspace(*fmt)) fmt++;
+
+    va_start (argp, format);
+    Nbyte = vsnprintf (&tmp, 0, fmt, argp);
+    va_end (argp);
+
+    if (Nbyte <= 0) return false;
+
+    va_start (argp, format);
+    root = (char *) psAlloc (Nbyte + 1);
+    memset (root, 0, Nbyte + 1);
+    vsnprintf (root, Nbyte + 1, fmt, argp);
+    va_end (argp);
+
+    psMetadata *folder = psMetadataAlloc ();
+
+    // specify the polynomial orders
+    psMetadataAdd (folder, PS_LIST_TAIL, "NORDER_X", PS_DATA_S32, "number of x orders", poly->nX);
+
+    char namespace[80];
+    char namespace_err[80];
+    int nElements = 0;   // count the number of unmasked elements
+
+    // place polynomial entries on folder
+    for (int nx = 0; nx < poly->nX + 1; nx++) {
+        if (!(poly->coeffMask[nx] & PS_POLY_MASK_SET)) {
+            sprintf(namespace, "VAL_X%02d", nx);
+            sprintf(namespace_err, "ERR_X%02d", nx);
+            psMetadataAdd (folder, PS_LIST_TAIL, namespace, PS_DATA_F64,
+                           "polynomial coefficient", poly->coeff[nx]);
+            psMetadataAdd (folder, PS_LIST_TAIL, namespace_err, PS_DATA_F64,
+                           "polynomial coefficient error", poly->coeffErr[nx]);
+            nElements ++;
+        }
+    }
+    psMetadataAdd (folder, PS_LIST_TAIL, "NELEMENTS", PS_DATA_S32, "number of unmasked coeffs", nElements);
+    psMetadataAdd (md, PS_LIST_TAIL, root, PS_DATA_METADATA, "folder for 1D polynomial", folder);
+    psFree (root);
+    psFree(folder);
+    return true;
+}
+
+psPolynomial2D *psPolynomial2DfromMetadata(const psMetadata *folder)
+{
+    PS_ASSERT_PTR_NON_NULL(folder, NULL);
+    bool status;
+    char keyword[80];
+
+    // get polynomial orders
+    int nXorder = psMetadataLookupS32 (&status, folder, "NORDER_X");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial2D in metadata is missing NORDER_X");
+        return NULL;
+    }
+    int nYorder = psMetadataLookupS32 (&status, folder, "NORDER_Y");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial2D in metadata is missing NORDER_Y");
+        return NULL;
+    }
+    // how many polynomial coeffs are expected?
+    int nElementsExpected = psMetadataLookupS32 (&status, folder, "NELEMENTS");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial2D in metadata is missing NELEMENTS");
+        return NULL;
+    }
+
+    psPolynomial2D *poly = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, nXorder, nYorder);
+
+    int nElements = 0;
+    for (int nx = 0; nx < poly->nX + 1; nx++) {
+        for (int ny = 0; ny < poly->nY + 1; ny++) {
+            sprintf (keyword, "VAL_X%02d_Y%02d", nx, ny);
+            poly->coeff[nx][ny] = psMetadataLookupF64 (&status, folder, keyword);
+            if (!status) {
+                // an undefined component implies the component was masked
+                // this is symmetrical with the 2DtoMD function
+                poly->coeff[nx][ny] = 0;
+                poly->coeffErr[nx][ny] = 0;
+                poly->coeffMask[nx][ny] = PS_POLY_MASK_SET;
+            } else {
+                poly->coeffMask[nx][ny] = PS_POLY_MASK_NONE;
+                nElements ++;
+            }
+            sprintf (keyword, "ERR_X%02d_Y%02d", nx, ny);
+            poly->coeffErr[nx][ny] = psMetadataLookupF64 (&status, folder, keyword);
+        }
+    }
+    if (nElements != nElementsExpected) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psPolynomial2D in metadata does not have the correct number of coefficients: "
+                "%d found vs %d expected", nElements, nElementsExpected);
+        psFree(poly);
+        return NULL;
+    }
+    return (poly);
+}
+
+// XXX : these may need F64, or %g format for output
+bool psPolynomial2DtoMetadata (psMetadata *md,
+                               const psPolynomial2D *poly,
+                               const char *format,
+                               ...)
+{
+    PS_ASSERT_PTR_NON_NULL(md, false);
+    PS_ASSERT_PTR_NON_NULL(poly, false);
+    PS_ASSERT_PTR_NON_NULL(format, false);
+
+    // XXX Current implementation only supports ordinary polynomials.
+    if (poly->type != PS_POLYNOMIAL_ORD)
+        return false;
+
+    int Nbyte;
+    char tmp;
+    char *root;
+    va_list argp;
+
+    // skip past whitespace (output name should not include whitespace)
+    char *fmt = (char *) format;
+    while (isspace(*fmt)) fmt++;
+
+    va_start (argp, format);
+    Nbyte = vsnprintf (&tmp, 0, fmt, argp);
+    va_end (argp);
+
+    if (Nbyte <= 0)
+        return false;
+
+    va_start (argp, format);
+    root = (char *) psAlloc (Nbyte + 1);
+    memset (root, 0, Nbyte + 1);
+    vsnprintf (root, Nbyte + 1, fmt, argp);
+    va_end (argp);
+
+    psMetadata *folder = psMetadataAlloc ();
+
+    // specify the polynomial orders
+    psMetadataAdd (folder, PS_LIST_TAIL, "NORDER_X", PS_DATA_S32, "number of x orders", poly->nX);
+    psMetadataAdd (folder, PS_LIST_TAIL, "NORDER_Y", PS_DATA_S32, "number of y orders", poly->nY);
+
+    char namespace[80];
+    char namespace_err[80];
+    int nElements = 0;   // count the number of unmasked elements
+
+    // place polynomial entries on folder
+    for (int nx = 0; nx < poly->nX + 1; nx++) {
+        for (int ny = 0; ny < poly->nY + 1; ny++) {
+            if (!(poly->coeffMask[nx][ny] & PS_POLY_MASK_SET)) {
+                sprintf(namespace, "VAL_X%02d_Y%02d", nx, ny);
+                sprintf(namespace_err, "ERR_X%02d_Y%02d", nx, ny);
+                psMetadataAdd (folder, PS_LIST_TAIL, namespace, PS_DATA_F64,
+                               "polynomial coefficient", poly->coeff[nx][ny]);
+                psMetadataAdd (folder, PS_LIST_TAIL, namespace_err, PS_DATA_F64,
+                               "polynomial coefficient error", poly->coeffErr[nx][ny]);
+                nElements ++;
+            }
+        }
+    }
+    psMetadataAdd (folder, PS_LIST_TAIL, "NELEMENTS", PS_DATA_S32, "number of unmasked coeffs", nElements);
+    psMetadataAdd (md, PS_LIST_TAIL, root, PS_DATA_METADATA, "folder for 2D polynomial", folder);
+    psFree (root);
+    psFree(folder);
+    return true;
+}
+
+psPolynomial3D *psPolynomial3DfromMetadata (const psMetadata *folder)
+{
+    PS_ASSERT_PTR_NON_NULL(folder, NULL);
+
+    bool status;
+    char keyword[80];
+
+    // get polynomial orders
+    int nXorder = psMetadataLookupS32 (&status, folder, "NORDER_X");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial3D in metadata is missing NORDER_X");
+        return NULL;
+    }
+    int nYorder = psMetadataLookupS32 (&status, folder, "NORDER_Y");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial3D in metadata is missing NORDER_Y");
+        return NULL;
+    }
+    int nZorder = psMetadataLookupS32 (&status, folder, "NORDER_Z");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial3D in metadata is missing NORDER_Z");
+        return NULL;
+    }
+    // how many polynomial coeffs are expected?
+    int nElementsExpected = psMetadataLookupS32 (&status, folder, "NELEMENTS");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial3D in metadata is missing NELEMENTS");
+        return NULL;
+    }
+
+    psPolynomial3D *poly = psPolynomial3DAlloc (PS_POLYNOMIAL_ORD, nXorder, nYorder, nZorder);
+
+    int nElements = 0;
+    for (int nx = 0; nx < poly->nX + 1; nx++) {
+        for (int ny = 0; ny < poly->nY + 1; ny++) {
+            for (int nz = 0; nz < poly->nZ + 1; nz++) {
+                sprintf (keyword, "VAL_X%02d_Y%02d_Z%02d", nx, ny, nz);
+                poly->coeff[nx][ny][nz] = psMetadataLookupF64 (&status, folder, keyword);
+                if (!status) {
+                    // an undefined component implies the component was masked
+                    // this is symmetrical with the 3DtoMD function
+                    poly->coeff[nx][ny][nz] = 0;
+                    poly->coeffErr[nx][ny][nz] = 0;
+                    poly->coeffMask[nx][ny][nz] = PS_POLY_MASK_SET;
+                } else {
+                    poly->coeffMask[nx][ny][nz] = PS_POLY_MASK_NONE;
+                    nElements ++;
+                }
+                sprintf (keyword, "ERR_X%02d_Y%02d_Z%02d", nx, ny, nz);
+                poly->coeffErr[nx][ny][nz] = psMetadataLookupF64 (&status, folder, keyword);
+            }
+        }
+    }
+    if (nElements != nElementsExpected) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psPolynomial3D in metadata does not have the correct number of coefficients: "
+                "%d found vs %d expected", nElements, nElementsExpected);
+        psFree(poly);
+        return NULL;
+    }
+    return (poly);
+}
+
+bool psPolynomial3DtoMetadata (psMetadata *md,
+                               const psPolynomial3D *poly,
+                               const char *format,
+                               ...)
+{
+    PS_ASSERT_PTR_NON_NULL(md, false);
+    PS_ASSERT_PTR_NON_NULL(poly, false);
+    PS_ASSERT_PTR_NON_NULL(format, false);
+
+    //XXX:  Current implementation only supports ordinary polynomials.
+    if (poly->type != PS_POLYNOMIAL_ORD)
+        return false;
+
+    int Nbyte;
+    char tmp;
+    char *root;
+    va_list argp;
+
+    // skip past whitespace (output name should not include whitespace)
+    char *fmt = (char *) format;
+    while (isspace(*fmt)) fmt++;
+
+    va_start (argp, format);
+    Nbyte = vsnprintf (&tmp, 0, fmt, argp);
+    va_end (argp);
+
+    if (Nbyte <= 0)
+        return false;
+
+    va_start (argp, format);
+    root = (char *) psAlloc (Nbyte + 1);
+    memset (root, 0, Nbyte + 1);
+    vsnprintf (root, Nbyte + 1, fmt, argp);
+    va_end (argp);
+
+    psMetadata *folder = psMetadataAlloc ();
+
+    // specify the polynomial orders
+    psMetadataAdd (folder, PS_LIST_TAIL, "NORDER_X", PS_DATA_S32, "number of x orders", poly->nX);
+    psMetadataAdd (folder, PS_LIST_TAIL, "NORDER_Y", PS_DATA_S32, "number of y orders", poly->nY);
+    psMetadataAdd (folder, PS_LIST_TAIL, "NORDER_Z", PS_DATA_S32, "number of z orders", poly->nZ);
+
+    char namespace[80];
+    char namespace_err[80];
+    int nElements = 0;   // count the number of unmasked elements
+
+    // place polynomial entries on folder
+    for (int nx = 0; nx < poly->nX + 1; nx++) {
+        for (int ny = 0; ny < poly->nY + 1; ny++) {
+            for (int nz = 0; nz < poly->nZ + 1; nz++) {
+                if (!(poly->coeffMask[nx][ny][nz] & PS_POLY_MASK_SET)) {
+                    sprintf(namespace, "VAL_X%02d_Y%02d_Z%02d", nx, ny, nz);
+                    sprintf(namespace_err, "ERR_X%02d_Y%02d_Z%02d", nx, ny, nz);
+                    psMetadataAdd (folder, PS_LIST_TAIL, namespace,
+                                   PS_DATA_F64, "polynomial coefficient",
+                                   poly->coeff[nx][ny][nz], nx, ny, nz);
+                    psMetadataAdd (folder, PS_LIST_TAIL, namespace_err,
+                                   PS_DATA_F64, "polynomial coeffficient error",
+                                   poly->coeffErr[nx][ny][nz], nx, ny, nz);
+                    nElements ++;
+                }
+            }
+        }
+    }
+    psMetadataAdd (folder, PS_LIST_TAIL, "NELEMENTS", PS_DATA_S32, "number of unmasked coeffs", nElements);
+    psMetadataAdd (md, PS_LIST_TAIL, root, PS_DATA_METADATA, "folder for 3D polynomial", folder);
+    psFree(root);
+    psFree(folder);
+    return true;
+}
+
+psPolynomial4D *psPolynomial4DfromMetadata(const psMetadata *folder)
+{
+    PS_ASSERT_PTR_NON_NULL(folder, NULL);
+
+    bool status;
+    char keyword[80];
+
+    int nXorder = psMetadataLookupS32 (&status, folder, "NORDER_X");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial4D in metadata is missing NORDER_X");
+        return NULL;
+    }
+    int nYorder = psMetadataLookupS32 (&status, folder, "NORDER_Y");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial4D in metadata is missing NORDER_Y");
+        return NULL;
+    }
+    int nZorder = psMetadataLookupS32 (&status, folder, "NORDER_Z");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial4D in metadata is missing NORDER_Z");
+        return NULL;
+    }
+    int nTorder = psMetadataLookupS32 (&status, folder, "NORDER_T");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial4D in metadata is missing NORDER_T");
+        return NULL;
+    }
+    // how many polynomial coeffs are expected?
+    int nElementsExpected = psMetadataLookupS32 (&status, folder, "NELEMENTS");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPolynomial4D in metadata is missing NELEMENTS");
+        return NULL;
+    }
+
+    psPolynomial4D *poly = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, nXorder, nYorder, nZorder, nTorder);
+
+    int nElements = 0;
+    for (int nx = 0; nx < poly->nX + 1; nx++) {
+        for (int ny = 0; ny < poly->nY + 1; ny++) {
+            for (int nz = 0; nz < poly->nZ + 1; nz++) {
+                for (int nt = 0; nt < poly->nT + 1; nt++) {
+                    sprintf (keyword, "VAL_X%02d_Y%02d_Z%02d_T%02d", nx, ny, nz, nt);
+                    poly->coeff[nx][ny][nz][nt] = psMetadataLookupF64 (&status, folder, keyword);
+                    if (!status) {
+                        // an undefined component implies the component was masked
+                        // this is symmetrical with the 4DtoMD function
+                        poly->coeff[nx][ny][nz][nt] = 0;
+                        poly->coeffErr[nx][ny][nz][nt] = 0;
+                        poly->coeffMask[nx][ny][nz][nt] = PS_POLY_MASK_SET;
+                    } else {
+                        poly->coeffMask[nx][ny][nz][nt] = PS_POLY_MASK_NONE;
+                        nElements ++;
+                    }
+                    sprintf (keyword, "ERR_X%02d_Y%02d_Z%02d_T%02d", nx, ny, nz, nt);
+                    poly->coeffErr[nx][ny][nz][nt] = psMetadataLookupF64 (&status, folder, keyword);
+                }
+            }
+        }
+    }
+    if (nElements != nElementsExpected) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psPolynomial4D in metadata does not have the correct number of coefficients: "
+                "%d found vs %d expected", nElements, nElementsExpected);
+        psFree(poly);
+        return NULL;
+    }
+    return (poly);
+}
+
+bool psPolynomial4DtoMetadata (psMetadata *md,
+                               const psPolynomial4D *poly,
+                               const char *format,
+                               ...)
+{
+    PS_ASSERT_PTR_NON_NULL(md, false);
+    PS_ASSERT_PTR_NON_NULL(poly, false);
+    PS_ASSERT_PTR_NON_NULL(format, false);
+
+    //XXX:  Current implementation only supports ordinary polynomials.
+    if (poly->type != PS_POLYNOMIAL_ORD)
+        return false;
+
+    int Nbyte;
+    char tmp;
+    char *root;
+    va_list argp;
+
+    // skip past whitespace (output name should not include whitespace)
+    char *fmt = (char *) format;
+    while (isspace(*fmt)) fmt++;
+
+    va_start (argp, format);
+    Nbyte = vsnprintf (&tmp, 0, fmt, argp);
+    va_end (argp);
+
+    if (Nbyte <= 0)
+        return false;
+
+    va_start (argp, format);
+    root = (char *) psAlloc (Nbyte + 1);
+    memset (root, 0, Nbyte + 1);
+    vsnprintf (root, Nbyte + 1, fmt, argp);
+    va_end (argp);
+
+    psMetadata *folder = psMetadataAlloc ();
+
+    // specify the polynomial orders
+    psMetadataAdd (folder, PS_LIST_TAIL, "NORDER_X", PS_DATA_S32, "number of x orders", poly->nX);
+    psMetadataAdd (folder, PS_LIST_TAIL, "NORDER_Y", PS_DATA_S32, "number of y orders", poly->nY);
+    psMetadataAdd (folder, PS_LIST_TAIL, "NORDER_Z", PS_DATA_S32, "number of z orders", poly->nZ);
+    psMetadataAdd (folder, PS_LIST_TAIL, "NORDER_T", PS_DATA_S32, "number of t orders", poly->nT);
+
+    char namespace[80];
+    char namespace_err[80];
+    int nElements = 0;   // count the number of unmasked elements
+
+    // place polynomial entries on folder
+    for (int nx = 0; nx < poly->nX + 1; nx++) {
+        for (int ny = 0; ny < poly->nY + 1; ny++) {
+            for (int nz = 0; nz < poly->nZ + 1; nz++) {
+                for (int nt = 0; nt < poly->nT + 1; nt++) {
+                    if (!(poly->coeffMask[nx][ny][nz][nt] & PS_POLY_MASK_SET)) {
+                        sprintf(namespace, "VAL_X%02d_Y%02d_Z%02d_T%02d", nx, ny, nz, nt);
+                        sprintf(namespace_err, "ERR_X%02d_Y%02d_Z%02d_T%02d", nx, ny, nz, nt);
+                        psMetadataAdd (folder, PS_LIST_TAIL, namespace,
+                                       PS_DATA_F64, "polynomial coefficient",
+                                       poly->coeff[nx][ny][nz][nt], nx, ny, nz, nt);
+                        psMetadataAdd (folder, PS_LIST_TAIL, namespace_err,
+                                       PS_DATA_F64, "polynomial coeffficient error",
+                                       poly->coeffErr[nx][ny][nz][nt], nx, ny, nz, nt);
+                        nElements ++;
+                    }
+                }
+            }
+        }
+    }
+    psMetadataAdd (folder, PS_LIST_TAIL, "NELEMENTS", PS_DATA_S32, "number of unmasked coeffs", nElements);
+    psMetadataAdd (md, PS_LIST_TAIL, root, PS_DATA_METADATA, "folder for 4D polynomial", folder);
+    psFree(root);
+    psFree(folder);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMetadata.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMetadata.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialMetadata.h	(revision 22322)
@@ -0,0 +1,157 @@
+/* @file  psPolyMetdata.h
+ * @brief Standard Mathematical Functions.
+ *
+ * This file will hold the prototypes for procedures which allocate, free,
+ * and evaluate various polynomials.  Those polynomial structures are also
+ * defined here.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-08-09 01:40:07 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_POLYMETADATA_H
+#define PS_POLYMETADATA_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/** Allocates a new psPolynomial1D structure with information from a psMetadata.
+ *
+ *  Parses a psMetadata container with psPolynomial1D information.  The first two
+ *  elements of the metadata folder specify the order of the x & y variables.  (ie,
+ *  NORDER_X, NORDER_Y).  The following elements are the values of the coefficients
+ *  and the coefficient errors.  (ie, VAL_X00_Y00, ERR_X00_Y00, etc.).  If the orders
+ *  or any coefficients are missing or have incorrect syntax, NULL is returned.
+ *
+ *  @return psPolynomial1D*:        Newly allocated psPolynomial1D from metadata.
+ */
+psPolynomial1D *psPolynomial1DfromMetadata(
+    const psMetadata *folder                 ///< folder containing the polynomial info.
+);
+
+/** Stores the information from a psPolynomial1D structure in a psMetadata container.
+ *
+ *  Creates a psMetadata folder with psPolynomial1D information.  The first two
+ *  elements of the metadata folder specify the order of the x & y variables.  (ie,
+ *  NORDER_X, NORDER_Y).  The following elements are the values of the coefficients
+ *  and the coefficient errors.  (ie, VAL_X00_Y00, ERR_X00_Y00, etc.).  The input
+ *  polynomial must be of ordinary type and have a valid name format.  False is also
+ *  returned if any inputs are NULL.  *If a particular mask element is non-zero, that
+ *  polynomial coefficient (and error) are skipped.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psPolynomial1DtoMetadata(
+    psMetadata *md,                    ///< Metadata container for polynomial storage.
+    const psPolynomial1D *poly,        ///< Polynomial information to be stored.
+    const char *format,                ///< Name of polynomial folder.
+    ...                                ///< Arguments for name formatting.
+) PS_ATTR_FORMAT(printf,3,4);
+
+/** Allocates a new psPolynomial2D structure with information from a psMetadata.
+ *
+ *  Parses a psMetadata container with psPolynomial2D information.  The first two
+ *  elements of the metadata folder specify the order of the x & y variables.  (ie,
+ *  NORDER_X, NORDER_Y).  The following elements are the values of the coefficients
+ *  and the coefficient errors.  (ie, VAL_X00_Y00, ERR_X00_Y00, etc.).  If the orders
+ *  or any coefficients are missing or have incorrect syntax, NULL is returned.
+ *
+ *  @return psPolynomial2D*:        Newly allocated psPolynomial2D from metadata.
+ */
+psPolynomial2D *psPolynomial2DfromMetadata(
+    const psMetadata *folder                 ///< folder containing the polynomial info.
+);
+
+/** Stores the information from a psPolynomial2D structure in a psMetadata container.
+ *
+ *  Creates a psMetadata folder with psPolynomial2D information.  The first two
+ *  elements of the metadata folder specify the order of the x & y variables.  (ie,
+ *  NORDER_X, NORDER_Y).  The following elements are the values of the coefficients
+ *  and the coefficient errors.  (ie, VAL_X00_Y00, ERR_X00_Y00, etc.).  The input
+ *  polynomial must be of ordinary type and have a valid name format.  False is also
+ *  returned if any inputs are NULL.  *If a particular mask element is non-zero, that
+ *  polynomial coefficient (and error) are skipped.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psPolynomial2DtoMetadata(
+    psMetadata *md,                    ///< Metadata container for polynomial storage.
+    const psPolynomial2D *poly,              ///< Polynomial information to be stored.
+    const char *format,                      ///< Name of polynomial folder.
+    ...                                ///< Arguments for name formatting.
+) PS_ATTR_FORMAT(printf,3,4);
+
+/** Allocates a new psPolynomial3D structure with information from a psMetadata.
+ *
+ *  Parses a psMetadata container with psPolynomial3D information.  The first three
+ *  elements of the metadata folder specify the order of the x, y, & z variables.  (ie,
+ *  NORDER_X, NORDER_Y, NORDER_Z).  The following elements are the values of the
+ *  coefficients and the coefficient errors.  (ie, VAL_X00_Y00_Z00, ERR_X00_Y00_Z00,
+ *  etc.).  If the orders or any coefficients are missing or have incorrect syntax,
+ *  NULL is returned.
+ *
+ *  @return psPolynomial3D*:        Newly allocated psPolynomial3D from metadata.
+ */
+psPolynomial3D *psPolynomial3DfromMetadata(
+    const psMetadata *folder                 ///< folder containing the polynomial info.
+);
+
+/** Stores the information from a psPolynomial3D structure in a psMetadata container.
+ *
+ *  Creates a psMetadata folder with psPolynomial3D information.  The first three
+ *  elements of the metadata folder specify the order of the x, y, & z variables.  (ie,
+ *  NORDER_X, NORDER_Y, NORDER_Z).  The following elements are the values of the
+ *  coefficients and the coefficient errors.  (ie, VAL_X00_Y00_Z00, ERR_X00_Y00_Z00,
+ *  etc.).  The input polynomial must be of ordinary type and have a valid name format.
+ *  False is also returned if any inputs are NULL.  *If a particular mask element is
+ *  non-zero, that polynomial coefficient (and error) are skipped.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psPolynomial3DtoMetadata(
+    psMetadata *md,                    ///< Metadata container for polynomial storage.
+    const psPolynomial3D *poly,              ///< Polynomial information to be stored.
+    const char *format,                      ///< Name of polynomial folder.
+    ...                                ///< Arguments for name formatting.
+) PS_ATTR_FORMAT(printf, 3, 4);
+
+/** Allocates a new psPolynomial4D structure with information from a psMetadata.
+ *
+ *  Parses a psMetadata container with psPolynomial4D information.  The first four
+ *  elements of the metadata folder specify the order of the x, y, z, & t variables.
+ *  (ie, NORDER_X, NORDER_Y, NORDER_Z, NORDER_T).  The following elements are the
+ *  values of the coefficients and the coefficient errors.  (ie, VAL_X00_Y00_Z00_T00,
+ *  ERR_X00_Y00_Z00_T00, etc.).  If the orders or any coefficients are missing or
+ *  have incorrect syntax, NULL is returned.
+ *
+ *  @return psPolynomial4D*:        Newly allocated psPolynomial4D from metadata.
+ */
+psPolynomial4D *psPolynomial4DfromMetadata(
+    const psMetadata *folder                 ///< folder containing the polynomial info.
+);
+
+/** Stores the information from a psPolynomial4D structure in a psMetadata container.
+ *
+ *  Creates a psMetadata folder with psPolynomial4D information.  The first four
+ *  elements of the metadata folder specify the order of the x, y, z, & t variables.
+ *  (ie, NORDER_X, NORDER_Y, NORDER_Z, NORDER_T).  The following elements are the values
+ *  of the coefficients and the coefficient errors.  (ie, VAL_X00_Y00_Z00_T00,
+ *  ERR_X00_Y00_Z00_T00, etc.).  The input polynomial must be of ordinary type and have
+ *  a valid name format.  False is also returned if any inputs are NULL.  *If a particular
+ *  mask element is non-zero, that polynomial coefficient (and error) are skipped.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psPolynomial4DtoMetadata(
+    psMetadata *md,                    ///< Metadata container for polynomial storage.
+    const psPolynomial4D *poly,              ///< Polynomial information to be stored.
+    const char *format,                      ///< Name of polynomial folder.
+    ...                                ///< Arguments for name formatting.
+) PS_ATTR_FORMAT(printf, 3, 4);
+
+/// @}
+#endif // #ifndef PS_POLYMETADATA_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialUtils.c	(revision 22322)
@@ -0,0 +1,247 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include "psMemory.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psVector.h"
+#include "psImage.h"
+#include "psBinaryOp.h"
+#include "psStats.h"
+#include "psAssert.h"
+#include "psPolynomial.h"
+#include "psMinimizePolyFit.h"
+#include "psCoord.h"
+#include "psPolynomialUtils.h"
+
+bool psVectorChiClipFitPolynomial4D(
+    psPolynomial4D *poly,
+    psStats *stats,
+    const psVector *mask,
+    psMaskType maskValue,
+    const psVector *f,
+    const psVector *fErr,
+    const psVector *x,
+    const psVector *y,
+    const psVector *z,
+    const psVector *t)
+{
+    PS_ASSERT_POLY_NON_NULL(poly, NULL);
+    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, NULL);
+    PS_ASSERT_PTR_NON_NULL(stats, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(f, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, NULL);
+    if (mask != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, NULL);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, NULL);
+    }
+    PS_ASSERT_VECTOR_NON_NULL(x, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(x, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(y, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(y, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(z, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(z, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(t, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(f, t, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(t, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(fErr, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(fErr, mask, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, NULL);
+
+    // clipping range defined by min and max and/or clipSigma
+    float minClipSigma;
+    float maxClipSigma;
+    if (isfinite(stats->max)) {
+        maxClipSigma = +fabs(stats->max);
+    } else {
+        maxClipSigma = +fabs(stats->clipSigma);
+    }
+    if (isfinite(stats->min)) {
+        minClipSigma = -fabs(stats->min);
+    } else {
+        minClipSigma = -fabs(stats->clipSigma);
+    }
+    psVector *fit   = NULL;
+    psVector *resid = psVectorAlloc (x->n, PS_TYPE_F64);
+
+    // eventual expansion: user supplies one of various stats option pairs,
+    // eg (SAMPLE_MEAN | SAMPLE_STDEV) and the correct pair is used to
+    // evaluate the clipping sigma
+    // for now, for the SAMPLE_MEDIAN and SAMPLE_STDEV to be used
+    stats->options |= (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+
+    for (int N = 0; N < stats->clipIter; N++) {
+        int Nkeep = 0;
+
+        if (!psVectorFitPolynomial4D (poly, mask, maskValue, f, fErr, x, y, z, t)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not fit a polynomial to the data.  Returning NULL.\n");
+            psFree (resid);
+            return false;
+        }
+
+        fit = psPolynomial4DEvalVector (poly, x, y, z, t);
+        if (fit == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial4DEvalVector().  Returning NULL.\n");
+            psFree(resid);
+            return false;
+        }
+
+        resid = (psVector *) psBinaryOp (resid, (void *) f, "-", (void *) fit);
+
+        if (!psVectorStats (stats, resid, NULL, mask, maskValue)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to measure vector stats");
+            psFree (fit);
+            psFree (resid);
+            return false;
+        }
+        psTrace (__func__, 5, "resid stats: %f +/- %f\n", stats->sampleMedian, stats->sampleStdev);
+
+        // set mask if pts are not valid
+        // we are masking out any point which is out of range
+        // recovery is not allowed with this scheme
+        for (int i = 0; i < resid->n; i++) {
+            if ((mask != NULL) && (mask->data.U8[i] & maskValue)) {
+                continue;
+            }
+            float sigma = hypot (psVectorGet (fErr, i), stats->sampleStdev);
+            if (resid->data.F64[i] - stats->sampleMedian > sigma*maxClipSigma) {
+                if (mask != NULL) {
+                    mask->data.U8[i] |= 0x01;
+                }
+                continue;
+            }
+            if (resid->data.F64[i] - stats->sampleMedian < sigma*minClipSigma) {
+                if (mask != NULL) {
+                    mask->data.U8[i] |= 0x01;
+                }
+                continue;
+            }
+            Nkeep ++;
+        }
+
+        psTrace (__func__, 4, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
+        stats->clippedNvalues = Nkeep;
+        psFree (fit);
+    }
+    // Free local temporary variables
+    psFree (resid);
+
+    return true;
+}
+
+// this function expects x,y in parent coords
+// XXX add a mask, fit only the valid pixels
+// XXX determine the errors, and propagate to the output of psImageBicubeMin
+psPolynomial2D *psImageBicubeFit(const psImage *image, int x, int y)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+
+    int ix = x - image->col0;
+    int iy = y - image->row0;
+
+    PS_ASSERT_INT_WITHIN_RANGE(ix, 1, image->numCols - 1, NULL);
+    PS_ASSERT_INT_WITHIN_RANGE(iy, 1, image->numRows - 1, NULL);
+
+    psF32 *Fm = &image->data.F32[iy - 1][ix];
+    psF32 *Fo = &image->data.F32[iy + 0][ix];
+    psF32 *Fp = &image->data.F32[iy + 1][ix];
+
+    double Fxm = Fm[-1] + Fo[-1] + Fp[-1];
+    double Fxp = Fm[+1] + Fo[+1] + Fp[+1];
+    double Fym = Fm[-1] + Fm[+0] + Fm[+1];
+    double Fyp = Fp[-1] + Fp[+0] + Fp[+1];
+    double Foo = Fym + Fyp + Fo[-1] + Fo[+0] + Fo[+1];
+
+    psPolynomial2D *poly = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, 2, 2);
+    poly->coeffMask[2][2] = PS_POLY_MASK_SET;
+    poly->coeffMask[1][2] = PS_POLY_MASK_SET;
+    poly->coeffMask[2][1] = PS_POLY_MASK_SET;
+
+    poly->coeff[0][0] = Foo*(5.0/9.0) - (Fxp + Fxm)/3.0 - (Fyp + Fym)/3.0 ;
+
+    poly->coeff[1][0] = (Fxp - Fxm)/6.0;
+    poly->coeff[0][1] = (Fyp - Fym)/6.0;
+
+    poly->coeff[2][0] = (Fxp + Fxm)/2.0 - Foo/3.0;
+    poly->coeff[0][2] = (Fyp + Fym)/2.0 - Foo/3.0;
+
+    poly->coeff[1][1] = (Fp[+1] + Fm[-1] - Fm[+1] - Fp[-1])/4.0;
+
+    return poly;
+}
+
+psPlane psImageBicubeMin(const psPolynomial2D *poly)
+{
+    psPlane min = { NAN, NAN, 0, 0 };   // Minimum value to return
+
+    PS_ASSERT_PTR_NON_NULL(poly, min);
+    PS_ASSERT_INT_EQUAL(poly->nX, 2, min);
+    PS_ASSERT_INT_EQUAL(poly->nY, 2, min);
+
+    double det = 4*poly->coeff[2][0]*poly->coeff[0][2] - PS_SQR(poly->coeff[1][1]); // Determinant
+    min.x = (poly->coeff[1][1]*poly->coeff[0][1] - 2*poly->coeff[0][2]*poly->coeff[1][0]) / det;
+    min.y = (poly->coeff[1][1]*poly->coeff[1][0] - 2*poly->coeff[2][0]*poly->coeff[0][1]) / det;
+
+    return min;
+}
+
+psPolynomial2D *psPolynomial2D_dX (psPolynomial2D *out, psPolynomial2D *poly)
+{
+    int nXout = poly->nX - 1;
+    int nYout = poly->nY;
+
+    if (out == poly) {
+        psError(PS_ERR_UNKNOWN, false, "cannot assign output to input polynomial");
+        return NULL;
+    }
+
+    if (poly->nX == 0)
+        return NULL;
+
+    if (out == NULL) {
+        out = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, nXout, nYout);
+    } else {
+        psPolynomial2DRecycle (out, poly->type, nXout, nYout);
+    }
+
+    for (int i = 0; i < nXout + 1; i++) {
+        for (int j = 0; j < nYout + 1; j++) {
+            out->coeffMask[i][j] = poly->coeffMask[i+1][j];
+            out->coeff[i][j] = poly->coeff[i+1][j] * (i+1);
+        }
+    }
+    return out;
+}
+
+psPolynomial2D *psPolynomial2D_dY (psPolynomial2D *out, psPolynomial2D *poly)
+{
+    int nXout = poly->nX;
+    int nYout = poly->nY - 1;
+
+    if (out == poly) {
+        psError(PS_ERR_UNKNOWN, false, "cannot assign output to input polynomial");
+        return NULL;
+    }
+
+    if (poly->nY == 0)
+        return NULL;
+
+    if (out == NULL) {
+        out = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, nXout, nYout);
+    } else {
+        psPolynomial2DRecycle (out, poly->type, nXout, nYout);
+    }
+
+    for (int i = 0; i < nXout + 1; i++) {
+        for (int j = 0; j < nYout + 1; j++) {
+            out->coeffMask[i][j] = poly->coeffMask[i][j+1];
+            out->coeff[i][j] = poly->coeff[i][j+1] * (j+1);
+        }
+    }
+    return out;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialUtils.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialUtils.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psPolynomialUtils.h	(revision 22322)
@@ -0,0 +1,47 @@
+/* @file  psPolynomialUtils.h
+ * @brief extra psPolynomial-related functions
+ *
+ * $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ * $Date: 2007-01-23 22:47:23 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_POLYNOMIAL_UTILS_H
+#define PS_POLYNOMIAL_UTILS_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include "psVector.h"
+#include "psImage.h"
+#include "psCoord.h"
+#include "psStats.h"
+#include "psPolynomial.h"
+
+// perform vector clip-fit based on significance of deviations
+bool psVectorChiClipFitPolynomial4D(
+    psPolynomial4D *poly,               // Polynomial to fit
+    psStats *stats,                     // Statistics to use in clipping
+    const psVector *mask,               // Mask for input values
+    psMaskType maskValue,               // Mask value
+    const psVector *f,                  // Value of the function, f(x,y,z,t)
+    const psVector *fErr,               // Error in the value
+    const psVector *x,                  // x ordinate
+    const psVector *y,                  // y ordinate
+    const psVector *z,                  // z ordinate
+    const psVector *t                   // t ordinate
+);
+
+// fit a 2D 2nd order polynomial to the 9 pixels centered on (x,y)
+psPolynomial2D *psImageBicubeFit(const psImage *image, // Image to fit
+                                 int x, int y // Pixels of centre of fit
+                                );
+
+// detemine the min(max) of the special 2D 2nd order polynomial
+psPlane psImageBicubeMin(const psPolynomial2D *poly // The polynomial
+                        );
+psPolynomial2D *psPolynomial2D_dX (psPolynomial2D *out, psPolynomial2D *poly);
+psPolynomial2D *psPolynomial2D_dY (psPolynomial2D *out, psPolynomial2D *poly);
+
+/// @}
+#endif /* PS_POLY_UTILS_H */
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psRandom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psRandom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psRandom.c	(revision 22322)
@@ -0,0 +1,162 @@
+/** @file psRandom.c
+*  \brief Random Number Generators
+*  \ingroup Math
+*
+*  This file will hold the functions which allocate, free,
+*  and evaluate random number Generators.
+*
+*  @ingroup Math
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-11-16 00:41:07 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+#include <time.h>
+#include <gsl/gsl_rng.h>
+#include <gsl/gsl_randist.h>
+#include <inttypes.h>
+
+#include "psMemory.h"
+#include "psRandom.h"
+#include "psScalar.h"
+#include "psError.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+#include "psAssert.h"
+
+
+psU64 p_psRandomGetSystemSeed(bool log)
+{
+    FILE*  fd;
+    psU64  seedVal = 0;
+    time_t timeVal;
+
+    fd = fopen("/dev/urandom","r");
+    if(fd == NULL) {
+        // Read system clock to get seed
+        seedVal = (psU64)time(&timeVal);
+    } else {
+        // Read urandom to get seed
+        fread(&seedVal, sizeof(psU64),1,fd);
+        // Close file
+        fclose(fd);
+    }
+
+    // Send log message of the system seed value used
+    if (log) {
+        psLogMsg(__func__,PS_LOG_INFO,"System random seed value used = %" PRIx64, seedVal);
+    }
+
+    return seedVal;
+}
+
+psRandom *psRandomAlloc(psRandomType type,
+                        unsigned long seed)
+{
+    gsl_rng   *r      = NULL;
+    psRandom  *myRNG  = NULL;
+
+    switch (type) {
+    case PS_RANDOM_TAUS:
+        myRNG = (psRandom*)psAlloc(sizeof(psRandom));
+        r = gsl_rng_alloc(gsl_rng_taus);
+        myRNG->gsl = r;
+        if(seed == 0) {
+            gsl_rng_set(myRNG->gsl, p_psRandomGetSystemSeed(true));
+        } else {
+            gsl_rng_set(myRNG->gsl, seed);
+        }
+        myRNG->type = type;
+        break;
+
+    default:
+        psError(PS_ERR_UNEXPECTED_NULL, true, _("Unknown Random Number Generator Type"));
+        break;
+    }
+
+    return(myRNG);
+}
+
+void psRandomReset(psRandom *rand,
+                   unsigned long seed)
+{
+    // Check null psRandom
+    if(rand==NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                _("Random variable is NULL."));
+    } else {
+        // Check seed value to see if system seed should be used
+        if(seed == 0) {
+            gsl_rng_set(rand->gsl,p_psRandomGetSystemSeed(true));
+        } else {
+            gsl_rng_set(rand->gsl, seed);
+        }
+    }
+}
+
+double psRandomUniform(const psRandom *r)
+{
+    // Check null psRandom variable
+    if(r == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                _("Random variable is NULL."));
+        return(0);
+    } else {
+        return(gsl_rng_uniform(r->gsl));
+    }
+}
+
+double psRandomGaussian(const psRandom *r)
+{
+    // Check null psRandom variable
+    if(r == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                _("Random variable is NULL."));
+        return(0);
+    } else {
+        // XXX: What should sigma be?
+        return(gsl_ran_gaussian(r->gsl, 1.0));
+    }
+}
+
+double p_psRandomGaussian(const psRandom *r, double sigma)
+{
+    // Check null psRandom variable
+    if(r == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                _("Random variable is NULL."));
+        return(0);
+    } else {
+        return(gsl_ran_gaussian(r->gsl, sigma));
+    }
+}
+
+double psRandomPoisson(const psRandom *r, double mean)
+{
+    // Check null psRandom variable
+    if(r == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                _("Random variable is NULL."));
+        return(0);
+    } else {
+        return((psF64) gsl_ran_poisson(r->gsl, mean));
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psRandom.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psRandom.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psRandom.h	(revision 22322)
@@ -0,0 +1,116 @@
+/* @file psRandom.h
+ * @brief Random Number Generators
+ *
+ * This file will hold the prototypes for procedures which allocate, free,
+ * and evaluate random number Generators.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-16 00:41:07 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_RANDOM_H
+#define PS_RANDOM_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psVector.h"
+#include "psScalar.h"
+#include <gsl/gsl_rng.h>
+#include <gsl/gsl_randist.h>
+
+/** Enumeration containing a flag for psRandom types.  */
+typedef enum {
+    PS_RANDOM_TAUS                     ///< A maximally equidistributed combined Tausworthe generator.
+} psRandomType;
+
+/** Data structure for psRandom.
+ *  Contains information on the psRandom type and GNU Scientific Library random number generator.
+ */
+typedef struct {
+    psRandomType type;                 ///< The type of RNG
+    gsl_rng *gsl;                      ///< The RNG itself
+} psRandom;
+
+/// Get a seed from the system.
+///
+/// Tries /dev/random first, and then the system clock
+psU64 p_psRandomGetSystemSeed(bool log  ///< Print a log message about the choice of seed?
+    );
+
+/** Allocates a psRandom struct.
+ *
+ *  @return psRandom*:    A new psRandom structure.
+ */
+psRandom *psRandomAlloc(
+    psRandomType type,                 ///< The type of RNG
+    unsigned long seed                 ///< Known value with which to seed the RNG
+) PS_ATTR_MALLOC;
+
+/** Resets an existing psRandom struct.
+ *
+ *  @return void
+ */
+void psRandomReset(
+    psRandom *rand,                    ///< Existing psRandom struct to reset
+    unsigned long seed                 ///< Known value with which to seed the RNG
+);
+
+/** Random number generator based on a uniform distribution on [0,1).
+ *  Uses gsl_rng_uniform.
+ *
+ *  @return double:     Random number.
+ */
+double psRandomUniform(
+    const psRandom *r                  ///< psRandom struct for RNG
+);
+
+/** Random number generator based on a Gaussian deviate, N(0,1).
+ *  Uses gsl_ran_gaussian.
+ *
+ *  @return double:     Random number.
+ */
+double psRandomGaussian(
+    const psRandom *r                  ///< psRandom struct for RNG
+);
+
+/** Random number generator based on a Gaussian deviate, N(0,1).
+ *  Uses gsl_ran_gaussian.
+ *
+ *  XXX: I created this since the above psLib spec for p_psRandomGaussian
+ *  had no argument for sigma.  Verify that with IfA.
+ *
+ *  @return double:     Random number.
+ */
+double p_psRandomGaussian(
+    const psRandom *r,                  ///< psRandom struct for RNG
+    double sigma
+);
+
+/** Random number generator based on a Poisson distribution with the given mean.
+ *  Uses gsl_ran_poisson.
+ *
+ *  @return double:     Random number.
+ */
+double psRandomPoisson(
+    const psRandom *r,                 ///< psRandom struct for RNG
+    double mean                         ///< Mean value
+);
+
+#define PS_ASSERT_RANDOM_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->gsl) { \
+    psError(PS_ERR_UNEXPECTED_NULL, true, "Error: Random number generator %s is NULL", #NAME); \
+    return RVAL; \
+}
+
+/// @}
+#endif // #ifndef PS_RANDOM_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegion.c	(revision 22322)
@@ -0,0 +1,173 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include "psMemory.h"
+#include "psError.h"
+#include "psAssert.h"
+#include "psConstants.h"
+#include "psRegion.h"
+
+static void regionFree(psRegion *region)
+{
+    // There are non dynamic allocated items
+}
+
+psRegion *psRegionAlloc(float x0,
+                        float x1,
+                        float y0,
+                        float y1)
+{
+    psRegion *region = psAlloc(sizeof(psRegion)); // New region, to be returned
+    psMemSetDeallocator(region, (psFreeFunc)regionFree);
+    // No complex structures, so no special deallocator
+    *region = psRegionSet(x0, x1, y0, y1);
+    return region;
+}
+
+psRegion psRegionSet(float x0,
+                     float x1,
+                     float y0,
+                     float y1)
+{
+    psRegion out;
+
+    out.x0 = x0;
+    out.y0 = y0;
+    out.x1 = x1;
+    out.y1 = y1;
+
+    return out;
+}
+
+psRegion psRegionFromString(const char* region)
+{
+    psS32 col0;
+    psS32 col1;
+    psS32 row0;
+    psS32 row1;
+
+    // section should be of the form '[col0:col1,row0:row1]'
+    if (region == NULL) {
+        return psRegionSet(0,0,0,0);
+    }
+
+    if (sscanf(region,"[%d:%d,%d:%d]",&col0,&col1,&row0,&row1) < 4) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified subsection string, '%s', can not be parsed.  Must be in the form '[x1:x2,y1:y2]'."),
+                region);
+        return psRegionSet(NAN,NAN,NAN,NAN);
+    }
+
+    // [0:0,0:0] is complete image region
+    if ((col0 == 0) && (col1 == 0) && (row0 == 0) && (row1 == 0)) {
+        return psRegionSet(0,0,0,0);
+    }
+
+    if ((col1 > 0) && (col0 > col1)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified subset range, [%d:%d,%d:%d], is invalid.  Ranges must be incremental."),
+                col0,col1,row0,row1);
+        return psRegionSet(NAN,NAN,NAN,NAN);
+    }
+
+    if ((row1 > 0) && (row0 > row1)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified subset range, [%d:%d,%d:%d], is invalid.  Ranges must be incremental."),
+                col0,col1,row0,row1);
+        return psRegionSet(NAN,NAN,NAN,NAN);
+    }
+
+    return psRegionSet(col0-1,col1,row0-1,row1);
+}
+
+psRegion psRegionAndParityFromString(int *xParity, int *yParity, const char* region)
+{
+    PS_ASSERT_PTR_NON_NULL (xParity, psRegionSet(NAN,NAN,NAN,NAN));
+    PS_ASSERT_PTR_NON_NULL (yParity, psRegionSet(NAN,NAN,NAN,NAN));
+
+    psS32 col0;
+    psS32 col1;
+    psS32 row0;
+    psS32 row1;
+
+    // unless otherwise detected
+    *xParity = +1;
+    *yParity = +1;
+
+    // section should be of the form '[col0:col1,row0:row1]'
+    if (region == NULL) {
+        return psRegionSet(0,0,0,0);
+    }
+
+    // XXX this is perhaps excessive: if the string is empty, assume 0,0,0,0
+    if (*region == 0) {
+        return psRegionSet(0,0,0,0);
+    }
+
+    if (sscanf(region,"[%d:%d,%d:%d]",&col0,&col1,&row0,&row1) < 4) {
+	// psError(PS_ERR_BAD_PARAMETER_NULL, true,
+	// ("Specified subsection string, '%s', can not be parsed.  Must be in the form '[x1:x2,y1:y2]'."),
+	// region);
+        // return psRegionSet(NAN,NAN,NAN,NAN);
+        return psRegionSet(0,0,0,0);
+    }
+
+    // [0:0,0:0] is complete image region
+    if ((col0 == 0) && (col1 == 0) && (row0 == 0) && (row1 == 0)) {
+        return psRegionSet(0,0,0,0);
+    }
+
+    if ((col1 > 0) && (col0 > col1)) {
+	*xParity = -1;
+	PS_SWAP (col0, col1);
+    }
+
+    if ((row1 > 0) && (row0 > row1)) {
+	*yParity = -1;
+	PS_SWAP (row0, row1);
+    }
+
+    return psRegionSet(col0-1,col1,row0-1,row1);
+}
+
+psString psRegionToString(const psRegion region)
+{
+    char *result = NULL;
+
+    // [0:0,0:0] is complete image region
+    if ((region.x0 == 0) && (region.x1 == 0) && (region.y0 == 0) && (region.y1 == 0)) {
+        psStringAppend(&result, "[0:0,0:0]");
+    } else {
+        psStringAppend(&result, "[%g:%g,%g:%g]",
+                       region.x0+1, region.x1,
+                       region.y0+1, region.y1);
+    }
+    return result;
+}
+
+// define a square region centered on the given coordinate
+psRegion psRegionForSquare(double x,
+                           double y,
+                           double radius)
+{
+    psRegion region;
+    region = psRegionSet (x - radius, x + radius + 1,
+                          y - radius, y + radius + 1);
+    return (region);
+}
+
+bool psRegionIsNaN(psRegion region)
+{
+    return isnan(region.x0) || isnan(region.x1) || isnan(region.y0) || isnan(region.y1);
+}
+
+bool psMemCheckRegion(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)regionFree );
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegion.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegion.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegion.h	(revision 22322)
@@ -0,0 +1,117 @@
+/* @file  psRegion.h
+ * @brief image regions and related functions
+ *
+ * $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ * $Date: 2007-06-10 17:54:05 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_REGION_H
+#define PS_REGION_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include "psString.h"
+
+/** Basic image region structure.
+ *
+ * Struct for specifying a rectangular area in an image.
+ *
+ */
+typedef struct
+{
+    float x0;                          ///< the first column of the region.
+    float x1;                          ///< the last column of the region.
+    float y0;                          ///< the first row of the region.
+    float y1;                          ///< the last row of the region.
+}
+psRegion;
+
+/** Create a pointer to a psRegion, with associated psMemBlock.
+ *
+ * @return psRegion* : a new psRegion.
+ */
+psRegion *psRegionAlloc(
+    float x0,                          ///< the first column of the region.
+    float x1,                          ///< the last column of the region + 1.
+    float y0,                          ///< the first row of the region.
+    float y1                           ///< the last row of the region + 1.
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psRegion structure, false otherwise.
+ */
+bool psMemCheckRegion(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Create a psRegion with the specified attributes.
+ *
+ *  @return psRegion :      a corresponding psRegion.
+ */
+psRegion psRegionSet(
+    float x0,                          ///< the first column of the region.
+    float x1,                          ///< the last column of the region + 1.
+    float y0,                          ///< the first row of the region.
+    float y1                           ///< the last row of the region + 1.
+);
+
+/** Create a psRegion with the attribute values given as a string.
+ *
+ *  Create a psRegion with the attribute values given as a string.  The format
+ *  shall be of the standard IRAF form '[x0:x1,y0:y1]'
+ *
+ *  @return psRegion:       A new psRegion struct, or NULL is not successful.
+ */
+psRegion psRegionFromString(
+    const char* region                 ///< image rectangular region in the form '[x0:x1,y0:y1]'
+);
+
+/** Create a psRegion from a string in IRAF form '[x0:x1,y0:y1]', returning range parities
+ *
+ *  Create a psRegion using the range defined by a string in the standard IRAF form
+ *  '[x0:x1,y0:y1]'.  Unlike psRegionFromString, the ranges may have x0 > x1 or y0 > y1, in
+ *  which case the xParity or yParity terms will be set to -1 (instead of the default +1).
+ *
+ *  @return psRegion:       A new psRegion struct, or NULL is not successful.
+ */
+psRegion psRegionAndParityFromString(
+    int *xParity,		      ///< +1 if x0 <= x1, -1 otherwise
+    int *yParity,		      ///< +1 if y0 <= y1, -1 otherwise
+    const char* region		      ///< image rectangular region in the form '[x0:x1,y0:y1]'
+);
+
+/** Create a string of the standard IRAF form '[x0:x1,y0:y1]' from a psRegion.
+ *
+ *  @return psString:  A new string representing the psRegion as text, or NULL
+ *                  is not successful.
+ */
+psString psRegionToString(
+    const psRegion region              ///< the psRegion to convert to a string
+);
+
+/** Defines a region corresponding to the square with center at coordinate x,y
+ *  and with coderadius.  The width of the square is 2radius + 1.
+ *
+ *  @return psRegion:       the newly defined psRegion.
+ */
+psRegion psRegionForSquare(
+    double x,                          ///< x coordinate at square-center
+    double y,                          ///< y coordinate at square-center
+    double radius                      ///< radius of square
+);
+
+/** Test if any element of the region is NaN
+ *
+ * @return bool:        True if an element is NaN, otherwise false.
+ */
+bool psRegionIsNaN(
+    psRegion region                    ///< Region to check
+);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegionForImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegionForImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegionForImage.c	(revision 22322)
@@ -0,0 +1,65 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include "psError.h"
+#include "psRegion.h"
+#include "psImage.h"
+#include "psRegionForImage.h"
+
+// set actual region based on image parameters:
+// - compensate for negative upper limits
+// - force range to be on this image
+// - saturate on upper and lower limits of image
+// - flip x0,x1 if x0>x1
+// - flip y0,y1 if y0>y1
+// psRegion in refers to coordinates in the *parent* image
+psRegion psRegionForImage(const psImage *image,
+                          psRegion in)
+{
+
+    //    if (image == NULL) {
+    //        return in;
+    //    }
+    PS_ASSERT_IMAGE_NON_NULL(image, in);
+    //if the region is [0,0,0,0], the whole image (or subimage) is to be included.
+    if (in.x0 == 0 && in.x1 == 0 && in.y0 == 0 && in.y1 == 0) {
+        in.x0 = image->col0;
+        in.x1 = image->col0 + image->numCols;
+        in.y0 = image->row0;
+        in.y1 = image->row0 + image->numRows;
+        return (in);
+    }
+
+    // convert non-positive upper-limits
+    // XXX note that the upper limit in these cases is defined relative to the subimage
+    // also note that truncation limits to the valid subimage pixels
+    in.x1 = (in.x1 <= 0) ? (image->col0 + image->numCols + in.x1) : in.x1;
+    in.y1 = (in.y1 <= 0) ? (image->row0 + image->numRows + in.y1) : in.y1;
+
+    // force the upper-limits to be on the image
+    in.x1 = PS_MIN(image->col0 + image->numCols, in.x1);
+    in.y1 = PS_MIN(image->row0 + image->numRows, in.y1);
+
+    // force the lower-limits to be on the image
+    in.x0 = PS_MAX(image->col0, in.x0);
+    in.y0 = PS_MAX(image->row0, in.y0);
+    in.x0 = PS_MIN(image->col0 + image->numCols, in.x0);
+    in.y0 = PS_MIN(image->row0 + image->numRows, in.y0);
+
+    // flip start and end if out of order
+    if (in.x0 > in.x1) {
+        psError (PS_ERR_BAD_PARAMETER_VALUE, true,
+                 "Invalid region in psRegionForImage.  x0 > x1.  Values have been swapped.\n");
+        PS_SWAP (in.x0, in.x1);
+    }
+    if (in.y0 > in.y1) {
+        psError (PS_ERR_BAD_PARAMETER_VALUE, true,
+                 "Invalid region in psRegionForImage.  y0 > y1.  Values have been swapped.\n");
+        PS_SWAP (in.y0, in.y1);
+    }
+
+    return (in);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegionForImage.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegionForImage.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psRegionForImage.h	(revision 22322)
@@ -0,0 +1,35 @@
+/* @file  psRegionForImage.h
+ * @brief regions definitions based on images
+ *
+ * $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * $Date: 2008-08-21 21:56:55 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_REGION_FOR_IMAGE_H
+#define PS_REGION_FOR_IMAGE_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/** Sets an actual region based on image parameters.
+ *
+ *  An image region defined with negative upper limits may be rationalized for the bounds of a
+ *  specific image with psRegionForImage.  The output of this function is a region with negative
+ *  upper limits replaced by their corrected value appropriate to the given image.  In addition,
+ *  the lower and upper limits are foced to lie within the bounds of the image.  If the lower-
+ *  limit coordinates are lewss than the lower bound of the image, they are limited to the lower
+ *  bound of the image.  Conversely, if the upper-limit coordinates are greater than the upper
+ *  bound of the image, they are truncated to define only valid pixels.  If the lower-limit
+ *  coordinates are greater than the upper bounds of the image, or the upper-limit coordinates
+ *  are less than the lower bounds of the image, the coordinates should saturate on those limits.
+ *
+ *  @return psRegion:       A region with negative upper limits replaced by the corrected
+ */
+psRegion psRegionForImage(
+    const psImage *image,               ///< the image for which the region is to be set
+    psRegion in                         ///< the image region limits
+);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psSort.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psSort.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psSort.h	(revision 22322)
@@ -0,0 +1,137 @@
+// Heap sort of the array
+// Based on descriptions in Sedgewick "Algorithms in C"
+//
+// Copyright (C) 1999  Thomas Walter
+// Copyright (C) 2007  Paul Price, Institute for Astronomy, University of Hawaii
+//
+// 18 February 2000: Modified for GSL by Brian Gough
+// 29 November 2007: Modified for psLib by Paul Price
+//
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option) any
+// later version.
+//
+// This source is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+
+#ifndef PS_SORT_H
+#define PS_SORT_H
+
+// XXX since the key size is fixed (same number of bits) a Radix sort could be
+// a big win here -- someone should benchmark this -JH
+
+// The following sort code is based on gsl_heapsort from GSL-1.8 (www.gnu.org/software/gsl), file
+// gsl-1.8/sort/sort.c, which is distributed under the GNU General Public License, version 2.
+
+
+// Use of the PSSORT() and PSSELECT() macros require additional macros defined:
+// COMPAREEXPR(A,B): expression to compare element A with element B; true if element A is smaller than B.
+// SWAPFUNC(TYPE,A,B): swap element A with element B; the type is provided so that a temporary variable can
+// be defined.
+
+// Sort the heap
+#define PSSORT_DOWNHEAP(COMPAREFUNC, SWAPFUNC, SWAPTYPE, K) { \
+    unsigned long k = K; /* Local version of k --- so the higher-level version isn't modified */ \
+    while (k <= N / 2) { \
+        unsigned long j = 2 * k; \
+        if (j < N && COMPAREFUNC(j, j + 1)) { \
+            j++; \
+        } \
+        if (COMPAREFUNC(k, j)) { \
+            SWAPFUNC(SWAPTYPE, j, k); \
+        } else { \
+            break; \
+        } \
+        k = j; \
+    } \
+}
+
+// Driver for heap sort
+// SIZE: Size of the array
+// COMPAREEXPR: Macro with expression for comparison
+// SWAPFUNC: Macro to swap elements
+// SWAPTYPE: Type for swapping, passed to SWAPFUNC
+#define PSSORT(SIZE, COMPAREEXPR, SWAPFUNC, SWAPTYPE) { \
+    unsigned long N = (SIZE) - 1; /* Index of last element */ \
+    unsigned long i = N / 2 + 1; /* Adding one to compensate for i-- below */ \
+    do { \
+        i--; \
+        PSSORT_DOWNHEAP(COMPAREEXPR, SWAPFUNC, SWAPTYPE, i); \
+    } while (i > 0); \
+    while (N > 0) { \
+        SWAPFUNC(SWAPTYPE, 0, N); /* Swap elements */ \
+        /* Process the heap */ \
+        N--; \
+        PSSORT_DOWNHEAP(COMPAREEXPR, SWAPFUNC, SWAPTYPE, 0); \
+    } \
+}
+// END of heap sort code from GSL
+
+
+
+// The following algorithm for selection was provided by http://en.wikipedia.org/wiki/Selection_algorithm
+// (version as of 21 May 2008, at 17:38), and implemented below as PSSELECT():
+//
+// function partition(list, left, right, pivotIndex)
+//     pivotValue := list[pivotIndex]
+//     swap list[pivotIndex] and list[right]  // Move pivot to end
+//     storeIndex := left
+//     for i from left to right-1
+//         if list[i] < pivotValue
+//             swap list[storeIndex] and list[i]
+//             storeIndex := storeIndex + 1
+//     swap list[right] and list[storeIndex]  // Move pivot to its final place
+//     return storeIndex
+//
+// function select(list, k, left, right)
+//     loop
+//         select a pivot value list[pivotIndex]
+//         pivotNewIndex := partition(list, left, right, pivotIndex)
+//         if k = pivotNewIndex
+//             return list[k]
+//         else if k < pivotNewIndex
+//             right := pivotNewIndex-1
+//         else
+//             left := pivotNewIndex+1
+
+
+// Select the RANK-th element
+// This macro reorders the array so that the RANK-th element is in the correct position
+// SIZE: Size of the array
+// RANK: RANK to move into the correct position
+// COMPAREEXPR: Macro with expression for comparison
+// SWAPFUNC: Macro to swap elements
+// SWAPTYPE: Type for swapping, passed to SWAPFUNC
+#define PSSELECT(SIZE, RANK, COMPAREEXPR, SWAPFUNC, SWAPTYPE) { \
+    bool selectContinue = true;         /* Continue swapping? */ \
+    long selectMax = SIZE - 1;           /* Maximum index */ \
+    long selectMin = 0;                  /* Minimum index */ \
+    while (selectContinue) { \
+        long selectPivot = (selectMin + selectMax) >> 1; /* Pivot index */ \
+        SWAPFUNC(SWAPTYPE, selectPivot, selectMax); /* Move pivot to end */ \
+        long selectStore = selectMin;    /* Index of interest */ \
+        for (long i = selectMin; i < selectMax; i++) { \
+            if (COMPAREEXPR(i, selectMax)) { /* Note: comparing with the original pivot */ \
+                SWAPFUNC(SWAPTYPE, selectStore, i); \
+                selectStore++; \
+            } \
+        } \
+        SWAPFUNC(SWAPTYPE, selectMax, selectStore); /* Move pivot to its final place */ \
+        if (selectStore == RANK) { \
+            selectContinue = false;     /* Done */ \
+        } else if (selectStore > RANK) { \
+            selectMax = selectStore - 1; \
+        } else { \
+            selectMin = selectStore + 1; \
+        } \
+    } \
+}
+
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psSparse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psSparse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psSparse.c	(revision 22322)
@@ -0,0 +1,478 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <unistd.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psVector.h"
+#include "psAssert.h"
+#include "psConstants.h"
+#include "psImageStructManip.h"
+#include "psImage.h"
+#include "psMatrix.h"
+#include "psSparse.h"
+#include "psAbort.h"
+
+#define BUFFER 100                      // Size to increment at each go
+
+static void sparseFree(psSparse *sparse)
+{
+    if (!sparse) {
+        return;
+    }
+    psFree(sparse->Aij);
+    psFree(sparse->Bfj);
+    psFree(sparse->Qii);
+    psFree(sparse->Si);
+    psFree(sparse->Sj);
+    return;
+}
+
+// allocate a sparse matrix container for Nrows, with Nelem slots allocated
+psSparse *psSparseAlloc(int Nrows, int Nelem)
+{
+    psSparse *sparse = (psSparse *)psAlloc(sizeof(psSparse));
+    psMemSetDeallocator(sparse, (psFreeFunc)sparseFree);
+
+    sparse->Aij = psVectorAllocEmpty(Nelem, PS_DATA_F32);
+    sparse->Si  = psVectorAllocEmpty(Nelem, PS_DATA_S32);
+    sparse->Sj  = psVectorAllocEmpty(Nelem, PS_DATA_S32);
+
+    sparse->Nelem = 0;
+
+    sparse->Bfj = psVectorAlloc(Nrows, PS_DATA_F32);
+    sparse->Qii = psVectorAlloc(Nrows, PS_DATA_F32);
+
+    sparse->Nrows = Nrows;
+
+    return sparse;
+}
+
+// user should only add elements above the diagonal, but we don't check this
+bool psSparseMatrixElement(psSparse *sparse, int i, int j, float value)
+{
+    PS_ASSERT_PTR_NON_NULL(sparse, false);
+    PS_ASSERT_INT_NONNEGATIVE(i, false);
+    PS_ASSERT_INT_NONNEGATIVE(j, false);
+
+    // EAM : this is now a fatal error
+    if (i < j) {
+        // psError(PS_ERR_UNKNOWN, true, "i=%d, j=%d refers to a sub-diagonal element. not allowed!");
+        psAbort("i=%d, j=%d refers to a sub-diagonal element. not allowed!", i, j);
+        return false;
+    }
+
+    if (i == j) {
+        // add to the diagonal
+        sparse->Qii->data.F32[i] = value;
+
+        // check vectors lengths and extend if needed
+        if (sparse->Nelem >= sparse->Aij->nalloc) {
+            psVectorRealloc(sparse->Aij, sparse->Aij->nalloc + BUFFER);
+            psVectorRealloc(sparse->Si,  sparse->Si->nalloc + BUFFER);
+            psVectorRealloc(sparse->Sj,  sparse->Sj->nalloc + BUFFER);
+        }
+
+        int k = sparse->Nelem;         // Index at which to add
+        sparse->Aij->data.F32[k] = value;
+        sparse->Si->data.S32[k]  = i;
+        sparse->Sj->data.S32[k]  = j;
+
+        sparse->Nelem ++;
+        sparse->Aij->n ++;
+        sparse->Si->n ++;
+        sparse->Sj->n ++;
+    } else {
+        // check vectors lengths and extend if needed
+        if (sparse->Nelem >= sparse->Aij->nalloc - 1) {
+            psVectorRealloc(sparse->Aij, sparse->Aij->nalloc + BUFFER);
+            psVectorRealloc(sparse->Si,  sparse->Si->nalloc + BUFFER);
+            psVectorRealloc(sparse->Sj,  sparse->Sj->nalloc + BUFFER);
+        }
+
+        int k = sparse->Nelem;         // Index at which to add
+        sparse->Aij->data.F32[k] = value;
+        sparse->Si->data.S32[k]  = i;
+        sparse->Sj->data.S32[k]  = j;
+        k++;
+
+        sparse->Aij->data.F32[k] = value;
+        sparse->Si->data.S32[k]  = j;
+        sparse->Sj->data.S32[k]  = i;
+
+        sparse->Nelem  += 2;
+        sparse->Aij->n += 2;
+        sparse->Si->n  += 2;
+        sparse->Sj->n  += 2;
+    }
+
+    return true;
+}
+
+void psSparseVectorElement(psSparse *sparse, int i, float value)
+{
+
+    sparse->Bfj->data.F32[i] = value;
+    return;
+}
+
+// multiply A * x
+psVector *psSparseMatrixTimesVector(psVector *output, const psSparse *matrix, const psVector *vector)
+{
+    PS_ASSERT_PTR_NON_NULL(matrix, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(vector, NULL);
+
+    output = psVectorRecycle(output, vector->n, PS_TYPE_F32);
+
+    int Nelem = 0;                     // Number of elements
+    for (int j = 0; j < vector->n; j++) {
+        double F = 0;                    // Running total
+        while (matrix->Sj->data.S32[Nelem] == j) {
+            int i = matrix->Si->data.S32[Nelem];
+            F += vector->data.F32[i] * matrix->Aij->data.F32[Nelem];
+            Nelem++;
+        }
+        output->data.F32[j] = F;
+    }
+    return output;
+}
+
+// solve Ax = B
+psVector *psSparseSolve(psVector *output, psSparseConstraint constraint, const psSparse *sparse, int Niter)
+{
+    PS_ASSERT_PTR_NON_NULL(sparse, NULL);
+    PS_ASSERT_INT_POSITIVE(Niter, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN(constraint.paramDelta, 0.0, NULL);
+
+    // Dereference some vectors
+    psVector *Qii = sparse->Qii;
+    psVector *Bfj = sparse->Bfj;
+
+    // initial guess is B / Qii
+    output = psVectorCopy(output, Bfj, PS_DATA_F32);
+    for (int i = 0; i < output->n; i++) {
+        output->data.F32[i] /= Qii->data.F32[i];
+    }
+
+    // temporary storage for intermediate results
+    psVector *dQ = psVectorAlloc(output->n, PS_DATA_F32);
+
+    for (int j = 0; j < Niter; j++) {
+        dQ = psSparseMatrixTimesVector(dQ, sparse, output);
+        for (int i = 0; i < dQ->n; i++) {
+            psF32 dG = (dQ->data.F32[i] - Bfj->data.F32[i]) / Qii->data.F32[i];
+            if (fabs (dG) > constraint.paramDelta) {
+                if (dG > 0) {
+                    dG = +constraint.paramDelta;
+                } else {
+                    dG = -constraint.paramDelta;
+                }
+            }
+            output->data.F32[i] -= dG;
+            output->data.F32[i] = PS_MAX(output->data.F32[i], constraint.paramMin);
+            output->data.F32[i] = PS_MIN(output->data.F32[i], constraint.paramMax);
+        }
+    }
+    psFree(dQ);
+
+    return output;
+}
+
+bool psSparseResort(psSparse *sparse)
+{
+    int Nelem = sparse->Nelem;
+
+    psVector *index = psVectorSortIndex(NULL, sparse->Sj); // Index key for sorting
+    if (!index) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to sort sparse matrix.\n");
+        return false;
+    }
+    psVector *Aij = sparse->Aij;
+    psVector *Si = sparse->Si;
+    psVector *Sj = sparse->Sj;
+
+    // allocate new temporary vectors
+    psVector *tAij = psVectorAlloc(Nelem, PS_DATA_F32);
+    psVector *tSi  = psVectorAlloc(Nelem, PS_DATA_S32);
+    psVector *tSj  = psVectorAlloc(Nelem, PS_DATA_S32);
+
+    for (int i = 0; i < Nelem; i++) {
+        int j = index->data.U32[i];
+        tAij->data.F32[i] = Aij->data.F32[j];
+        tSi->data.S32[i]  = Si->data.S32[j];
+        tSj->data.S32[i]  = Sj->data.S32[j];
+    }
+    psFree(index);
+    psFree(Aij);
+    psFree(Si);
+    psFree(Sj);
+
+    sparse->Aij = tAij;
+    sparse->Si = tSi;
+    sparse->Sj = tSj;
+
+    return true;
+}
+
+/*** psSparseBorder Functions : these are used to solve a matrix equation of the form:
+     A x = f  where A is partitioned into:
+     A = |S B| where Q is a low-rank square matrix (N<20)
+     |B Q| and B is a rectangular band (technically this is B and B^T)
+     and S is a sparse matrix.
+*/
+
+static void psSparseBorderFree(psSparseBorder *border)
+{
+    if (!border) {
+        return;
+    }
+    psFree(border->sparse);
+    psFree(border->Bij);
+    psFree(border->Tjj);
+    psFree(border->Gj);
+    return;
+}
+
+// allocate a sparse matrix border container for a system with Nrows + Nborder elements
+// the supplied psSparse has a size of Nrows
+psSparseBorder *psSparseBorderAlloc(psSparse *sparse, int Nborder)
+{
+    psSparseBorder *border = (psSparseBorder *)psAlloc(sizeof(psSparseBorder));
+    psMemSetDeallocator(border, (psFreeFunc) psSparseBorderFree);
+
+    border->sparse = psMemIncrRefCounter (sparse); // XXX increment ref counter or not?
+
+    int Nrows = sparse->Nrows;
+    border->Nrows = Nrows;
+    border->Nborder = Nborder;
+
+    border->Bij = psImageAlloc(Nrows, Nborder, PS_DATA_F32);
+    psImageInit (border->Bij, 0.0);
+
+    border->Tjj = psImageAlloc(Nborder, Nborder, PS_DATA_F32);
+    psImageInit (border->Tjj, 0.0);
+
+    border->Gj = psVectorAlloc(Nborder, PS_DATA_F32);
+    psVectorInit (border->Gj, 0.0);
+
+    return border;
+}
+
+// add elements to border->Tjj
+bool psSparseBorderElementT(psSparseBorder *border, int i, int j, float value)
+{
+    PS_ASSERT_PTR_NON_NULL(border, false);
+    PS_ASSERT_PTR_NON_NULL(border->Tjj, false);
+    PS_ASSERT_INT_NONNEGATIVE(i, false);
+    PS_ASSERT_INT_NONNEGATIVE(j, false);
+
+    // check i,j against border->Tjj->nX,nY
+    border->Tjj->data.F32[j][i] = value;
+    return true;
+}
+
+// add elements to border->Bij
+bool psSparseBorderElementB(psSparseBorder *border, int i, int j, float value)
+{
+    PS_ASSERT_PTR_NON_NULL(border, false);
+    PS_ASSERT_PTR_NON_NULL(border->Bij, false);
+    PS_ASSERT_INT_NONNEGATIVE(i, false);
+    PS_ASSERT_INT_NONNEGATIVE(j, false);
+
+    border->Bij->data.F32[j][i] = value;
+    return true;
+}
+
+// add elements to border->Gj
+bool psSparseBorderElementG(psSparseBorder *border, int i, float value)
+{
+    PS_ASSERT_PTR_NON_NULL(border, false);
+    PS_ASSERT_PTR_NON_NULL(border->Gj, false);
+    PS_ASSERT_INT_NONNEGATIVE(i, false);
+
+    border->Gj->data.F32[i] = value;
+    return true;
+}
+
+// perform the operation dG = B*x
+psVector *psSparseBorderLowerProduct (psVector *dG, psSparseBorder *border, psVector *xVec)
+{
+    // XXX assert xVec->n == border->Nrows
+
+    int Nborder = border->Nborder;
+    int Nrows = border->Nrows;
+
+    dG = psVectorRecycle (dG, Nborder, PS_TYPE_F32);
+    psVectorInit (dG, 0.0);
+
+    for (int j = 0; j < Nborder; j++) {
+        double value = 0;
+        for (int i = 0; i < Nrows; i++) {
+            value += border->Bij->data.F32[j][i] * xVec->data.F32[i];
+        }
+        dG->data.F32[j] = value;
+    }
+
+    return dG;
+}
+
+// perform the operation dF = B^T*y
+psVector *psSparseBorderUpperProduct (psVector *dF, psSparseBorder *border, psVector *yVec)
+{
+    // XXX assert yVec->n == border->Nborder
+
+    int Nborder = border->Nborder;
+    int Nrows = border->Nrows;
+
+    dF = psVectorRecycle (dF, Nrows, PS_TYPE_F32);
+    psVectorInit (dF, 0.0);
+
+    for (int i = 0; i < Nrows; i++) {
+        double value = 0;
+        for (int j = 0; j < Nborder; j++) {
+            value += border->Bij->data.F32[j][i] * yVec->data.F32[j];
+        }
+        dF->data.F32[i] = value;
+    }
+
+    return dF;
+}
+
+// perform the operation dG = T*y
+psVector *psSparseBorderSquareProduct (psVector *dG, psSparseBorder *border, psVector *yVec)
+{
+    // XXX assert yVec->n == border->Nborder
+
+    int Nborder = border->Nborder;
+
+    dG = psVectorRecycle (dG, Nborder, PS_TYPE_F32);
+    psVectorInit (dG, 0.0);
+
+    for (int i = 0; i < Nborder; i++) {
+        double value = 0;
+        for (int j = 0; j < Nborder; j++) {
+            value += border->Tjj->data.F32[i][j] * yVec->data.F32[j];
+        }
+        dG->data.F32[i] = value;
+    }
+
+    return dG;
+}
+
+// perform the operation dF = B^T*y
+bool psSparseBorderUpperDelta (psSparseBorder *border, psVector *dF)
+{
+
+    int Nrows = border->Nrows;
+
+    for (int i = 0; i < Nrows; i++) {
+        border->sparse->Bfj->data.F32[i] -= dF->data.F32[i];
+    }
+
+    return true;
+}
+
+// perform the operation dF = B^T*y
+psVector *psSparseBorderLowerDelta (psVector *Go, psSparseBorder *border, psVector *dG)
+{
+    int Nborder = border->Nborder;
+
+    Go = psVectorRecycle (Go, Nborder, PS_TYPE_F64);
+    psVectorInit (Go, 0.0);
+
+    for (int i = 0; i < Nborder; i++) {
+        Go->data.F64[i] = border->Gj->data.F32[i] - dG->data.F32[i];
+    }
+    return Go;
+}
+
+// multiply A*x = b (where x is (x,y) and b is (f,g))
+bool psSparseBorderMultiply (psVector **fIn, psVector **gIn, psSparseBorder *border, psVector *xVec, psVector *yVec)
+{
+    psVector *fVec = *fIn;
+    fVec = psSparseMatrixTimesVector (fVec, border->sparse, xVec);
+    psVector *dF = psSparseBorderUpperProduct (NULL, border, yVec);
+    for (int i = 0; i < border->Nrows; i++) {
+        fVec->data.F32[i] += dF->data.F32[i];
+    }
+    psFree (dF);
+    *fIn = fVec;
+
+    psVector *gVec = *gIn;
+    gVec = psSparseBorderSquareProduct (gVec, border, yVec);
+    psVector *dG = psSparseBorderLowerProduct (NULL, border, xVec);
+    for (int i = 0; i < border->Nborder; i++) {
+        gVec->data.F32[i] += dG->data.F32[i];
+    }
+    psFree (dG);
+    *gIn = gVec;
+
+    return true;
+}
+
+bool psSparseBorderSolve(psVector **xFit, psVector **yFit, psSparseConstraint constraint, psSparseBorder *border, int Niter)
+{
+    // PS_ASSERT_PTR_NON_NULL(sparse, NULL);
+    // PS_ASSERT_INT_POSITIVE(Niter, NULL);
+    // PS_ASSERT_FLOAT_LARGER_THAN(constraint.paramDelta, 0.0, NULL);
+
+    psVector *xVec = *xFit;
+    psVector *yVec = *yFit;
+
+    // save the original value of Bfj, alloc other temp vectors
+    // XXX be careful about TYPE requirements of support functions...
+    psVector *Fo = psVectorCopy (NULL, border->sparse->Bfj, PS_TYPE_F32);
+    psVector *dF = psVectorAlloc (border->Nrows, PS_TYPE_F32);
+    psVector *Go = psVectorAlloc (border->Nborder, PS_TYPE_F64);
+    psVector *dG = psVectorAlloc (border->Nborder, PS_TYPE_F32);
+    psImage *square = psImageAlloc (border->Nborder, border->Nborder, PS_TYPE_F64);
+
+    for (int j = 0; j < Niter; j++) {
+
+        // solve Sx = f
+        xVec = psSparseSolve(xVec, constraint, border->sparse, Niter);
+
+        dG = psSparseBorderLowerProduct (dG, border, xVec);
+
+        // XXX i have an error here: i'm not
+        // I need to reset to the original value of g before subtracting
+        // XXX this function returns an psVector:F64 so we can us it in GJ
+        Go = psSparseBorderLowerDelta (Go, border, dG);
+
+        // use gauss-jordan to solve the lower square
+        // square is modified by GJSolve, so re-copy each time
+        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;
+	}
+        yVec = psVectorCopy (yVec, Go, PS_TYPE_F32);
+
+        // calculate the delta relative to the original Bfj:
+        border->sparse->Bfj = psVectorCopy (border->sparse->Bfj, Fo, PS_TYPE_F32);
+        dF = psSparseBorderUpperProduct (dF, border, yVec);
+        psSparseBorderUpperDelta (border, dF);
+    }
+
+    psFree (dG);
+    psFree (Go);
+    psFree (dF);
+    psFree (Fo);
+    psFree (square);
+
+    *xFit = xVec;
+    *yFit = yVec;
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psSparse.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psSparse.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psSparse.h	(revision 22322)
@@ -0,0 +1,110 @@
+/* @file  psSparse.h
+ * @brief functions to manipulate sparse matrices equations
+ *
+ * $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ * $Date: 2007-08-09 01:40:07 $
+ * Copyright 2004-2005 IfA, University of Hawaii
+ */
+
+#ifndef PS_SPARSE_H
+#define PS_SPARSE_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+// constraints to limit the range of the matrix equation solution
+typedef struct
+{
+    double paramDelta;
+    double paramMin;
+    double paramMax;
+}
+psSparseConstraint;
+
+// A sparse matrix equation: A x = Bf
+typedef struct
+{
+    psVector *Aij;                      // Aij contains the populated elements of the matrix
+    psVector *Bfj;                      // Bfj contains the elements of the vector Bf
+    psVector *Qii;                      // Qii contains the diagonal elements of Aij
+    psVector *Si;                       // Si contains the i-index values of Aij
+    psVector *Sj;                       // Sj contains the j-index values of Aij
+    int Nelem;                         // Number of elements
+    int Nrows;                         // Number of rows
+}
+psSparse;
+
+// The border elements of a sparse matrix equation:
+// A = |S B'| where T is a low-rank square matrix (N<20)
+//     |B T | and B is a rectangular band (B' is B transpose)
+typedef struct
+{
+    psSparse *sparse;   // corresponding sparse matrix equation
+    psImage *Bij;   // Bij contains the border band (Nrow x Nborder)
+    psImage *Tjj;   // Tjj contains the square border matrix (Nborder x Nborder)
+    psVector *Gj;   // XXX lower dependent var drop??
+    int Nrows;    // Number of rows (long dimension of Bij, 0-j)
+    int Nborder;   // Number of border elements (size of Qii)
+}
+psSparseBorder;
+
+// allocate a sparse matrix structure
+psSparse *psSparseAlloc(int Nrows, int Nelem) PS_ATTR_MALLOC;
+
+// add a new matrix element
+// user should only add elements above the diagonal
+bool psSparseMatrixElement(psSparse *sparse, // Matrix to which to add
+                           int i, int j, // Matrix indices at which to add
+                           float value  // Value to add
+                          );
+
+// define a new sparse matrix equation vector element
+void psSparseVectorElement(psSparse *sparse, // Matrix to which to add
+                           int i,      // Index to add
+                           float value  // Value to add
+                          );
+
+// perform the operation matrix * vector on a sparse matrix and a vector
+psVector *psSparseMatrixTimesVector(psVector *output, // Output vector, or NULL
+                                    const psSparse *matrix, // Sparse matrix
+                                    const psVector *vector // Corresponding vector
+                                   );
+
+// re-sort a sparse matrix to have all elements in index order rather than insertion order
+// call this before solving, but after populating matrix and vector
+bool psSparseResort(psSparse *sparse    // Matrix to re-sort
+                   );
+
+// solve the equation A x = Bf for the value of x
+// a good starting guess is the vector Bf
+psVector *psSparseSolve(psVector *output,// The output vector, or NULL
+                        psSparseConstraint constraint, // Constraint to limit the range of the solution
+                        const psSparse *sparse, // Sparse matrix
+                        int Niter       // Number of iterations
+                       );
+
+// allocate a sparse matrix structure
+psSparseBorder *psSparseBorderAlloc(psSparse *sparse, int Nborder) PS_ATTR_MALLOC;
+
+bool psSparseBorderElementT(psSparseBorder *border, int i, int j, float value);
+
+bool psSparseBorderElementB(psSparseBorder *border, int i, int j, float value);
+
+bool psSparseBorderElementG(psSparseBorder *border, int i, float value);
+
+psVector *psSparseBorderLowerProduct (psVector *dG, psSparseBorder *border, psVector *xVec);
+
+psVector *psSparseBorderUpperProduct (psVector *dF, psSparseBorder *border, psVector *yVec);
+
+psVector *psSparseBorderSquareProduct (psVector *dG, psSparseBorder *border, psVector *yVec);
+
+bool psSparseBorderUpperDelta (psSparseBorder *border, psVector *dF);
+
+psVector *psSparseBorderLowerDelta (psVector *Go, psSparseBorder *border, psVector *dG);
+
+bool psSparseBorderMultiply (psVector **fIn, psVector **gIn, psSparseBorder *border, psVector *xVec, psVector *yVec);
+
+bool psSparseBorderSolve(psVector **xFit, psVector **yFit, psSparseConstraint constraint, psSparseBorder *border, int Niter);
+
+/// @}
+#endif /* PS_SPARSE_H */
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psSpline.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psSpline.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psSpline.c	(revision 22322)
@@ -0,0 +1,429 @@
+/** @file psSpline.c
+*
+*  @brief Contains basic spline allocation, deallocation, fitting,
+*         and evaluation routines.
+*
+*  This file contains the routines that allocate, free, and evaluate splines.
+*
+*  @version $Revision: 1.158 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-03-14 02:36:28 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/*****************************************************************************/
+/*  INCLUDE FILES                                                            */
+/*****************************************************************************/
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psMemory.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psPolynomial.h"
+#include "psSpline.h"
+#include "psAssert.h"
+
+#include "psMathUtils.h"
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+static void spline1DFree(psSpline1D *tmpSpline)
+{
+    if (tmpSpline == NULL) {
+        return;
+    }
+
+    if (tmpSpline->spline != NULL) {
+        for (psS32 i=0;i<tmpSpline->n;i++) {
+            psFree((tmpSpline->spline)[i]);
+        }
+        psFree(tmpSpline->spline);
+    }
+
+    if (tmpSpline->p_psDeriv2 != NULL) {
+        psFree(tmpSpline->p_psDeriv2);
+    }
+
+    psFree(tmpSpline->knots);
+
+    return;
+}
+
+
+static void PS_POLY1D_PRINT(
+    psPolynomial1D *poly)
+{
+    printf("-------------- PS_POLY1D_PRINT() --------------\n");
+    printf("poly->nX is %d\n", poly->nX);
+    for (psS32 i = 0 ; i < (1 + poly->nX) ; i++) {
+        printf("poly->coeff[%d] is %f\n", i, poly->coeff[i]);
+    }
+}
+
+static void PS_PRINT_SPLINE2(psSpline1D *mySpline)
+{
+    printf("-------------- PS_PRINT_SPLINE2() --------------\n");
+    printf("mySpline->n is %d\n", mySpline->n);
+    for (psS32 i = 0 ; i < mySpline->n ; i++) {
+        PS_POLY1D_PRINT(mySpline->spline[i]);
+    }
+    PS_VECTOR_PRINT_F32(mySpline->knots);
+}
+
+/*****************************************************************************
+CalculateSecondDerivs(): Given a set of x/y vectors corresponding to a
+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?
+ *****************************************************************************/
+#define PS_LEFT_SPLINE_DERIV 0.0
+#define PS_RIGHT_SPLINE_DERIV 0.0
+static psF32 *calculateSecondDerivs(
+    const psVector* x,                  ///< Ordinates
+    const psVector* y)                  ///< Coordinates
+{
+    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        p_psVectorPrint(1, (psVector *) x, "x");
+        p_psVectorPrint(1, (psVector *) y, "y");
+    }
+    psS32 n = y->n;
+    psF32 *u = (psF32 *) psAlloc(n * sizeof(psF32));
+    psF32 *derivs2 = (psF32 *) psAlloc(n * sizeof(psF32));
+    psF32 *X = (psF32 *) & (x->data.F32[0]);
+    psF32 *Y = (psF32 *) & (y->data.F32[0]);
+    //
+    // The second derivatives at the endpoints, undefined in the SDR,
+    // are set in psAssert.h: PS_LEFT_SPLINE_DERIV, PS_RIGHT_SPLINE_DERIV.
+    //
+    derivs2[0] = -0.5;
+    u[0]= (3.0/(X[1]-X[0])) * ((Y[1]-Y[0])/(X[1]-X[0]) - PS_LEFT_SPLINE_DERIV);
+
+    for (psS32 i=1;i<=(n-2);i++) {
+        psF32 sig = (X[i] - X[i-1]) / (X[i+1] - X[i-1]);
+        psF32 p = sig * derivs2[i-1] + 2.0;
+        derivs2[i] = (sig - 1.0) / p;
+        u[i] = ((Y[i+1] - Y[i])/(X[i+1]-X[i])) - ((Y[i]-Y[i-1])/(X[i]-X[i-1]));
+        u[i] = ((6.0 * u[i] / (X[i+1] - X[i-1])) - (sig * u[i-1])) / p;
+
+        psTrace("psLib.math", 6, "X[%d] is %f\n", i, X[i]);
+        psTrace("psLib.math", 6, "Y[%d] is %f\n", i, Y[i]);
+        psTrace("psLib.math", 6, "u[%d] is %f\n", i, u[i]);
+    }
+
+    psF32 qn = 0.5;
+    u[n-1] = (3.0/(X[n-1]-X[n-2])) * (PS_RIGHT_SPLINE_DERIV - (Y[n-1]-Y[n-2])/(X[n-1]-X[n-2]));
+    derivs2[n-1] = (u[n-1] - (qn * u[n-2])) / ((qn * derivs2[n-2]) + 1.0);
+
+    for (psS32 k=(n-2);k>=0;k--) {
+        derivs2[k] = derivs2[k] * derivs2[k+1] + u[k];
+        psTrace("psLib.math", 6, "derivs2[%d] is %f\n", k, derivs2[k]);
+    }
+    psFree(u);
+    psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
+    return(derivs2);
+}
+
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+bool psMemCheckSpline1D(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)spline1DFree );
+}
+
+psSpline1D *psSpline1DAlloc()
+{
+    psSpline1D *tmpSpline = (psSpline1D *) psAlloc(sizeof(psSpline1D));
+    tmpSpline->n = 0;
+    tmpSpline->spline = NULL;
+    tmpSpline->knots = NULL;
+    tmpSpline->p_psDeriv2 = NULL;
+    psMemSetDeallocator(tmpSpline, (psFreeFunc) spline1DFree);
+
+    return(tmpSpline);
+}
+
+/*****************************************************************************
+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
+ (1)     y = (A * y[0]) +
+ (2)         (B * y[1]) +
+ (3)         ((((A*A*A)-A) * mySpline->p_psDeriv2[0]) * H^2)/6.0 +
+ (4)         ((((B*B*B)-B) * mySpline->p_psDeriv2[1]) * H^2)/6.0
+Where:
+ H = x[1]-x[0]
+ A = (x[1]-x)/H
+ B = (x-x[0])/H
+The bulk of the code in this routine is the expansion of the above equation
+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?
+ *****************************************************************************/
+psSpline1D *psVectorFitSpline1D(
+    const psVector* x,                  ///< Ordinates.
+    const psVector* y)                  ///< Coordinates.
+{
+    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_VECTOR_NON_NULL(y, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(y, NULL);
+    PS_ASSERT_LONG_LARGER_THAN_OR_EQUAL(y->n, (long)2, NULL);
+    psS32 numSplines = (y->n)-1;
+    psTrace("psLib.math", 5, "numSplines is %d\n", numSplines);
+
+    //
+    // Create the psSpline1D struct.
+    //
+    psSpline1D *spline = psSpline1DAlloc();
+    spline->n = numSplines;
+    spline->spline = (psPolynomial1D **) psAlloc(numSplines * sizeof(psPolynomial1D *));
+    for (psS32 i=0;i<numSplines;i++) {
+        spline->spline[i] = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 3);
+    }
+
+    //
+    // The following code ensures that xPtr and yPtr points to a psF32 psVector.
+    //
+    // XXX: Use the vector copy and create routines here:
+    //
+
+    spline->knots = psVectorAlloc(y->n, PS_TYPE_F32);
+    if (x != NULL) {
+        PS_ASSERT_VECTOR_NON_NULL(x, NULL);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(x, y, NULL);
+        PS_ASSERT_VECTOR_TYPE_F32_OR_F64(x, NULL);
+        if (x->type.type == PS_TYPE_F32) {
+            for (psS32 i = 0 ; i < x->n ; i++) {
+                spline->knots->data.F32[i] = x->data.F32[i];
+            }
+        } else if (x->type.type == PS_TYPE_F64) {
+            for (psS32 i = 0 ; i < x->n ; i++) {
+                spline->knots->data.F32[i] = (psF32) x->data.F64[i];
+            }
+        }
+    } else {
+        for (psS32 i = 0 ; i < y->n ; i++) {
+            spline->knots->data.F32[i] = (psF32) i;
+        }
+    }
+    psVector *xPtr = spline->knots;
+
+    psVector *yPtr = NULL;
+    // Convert y to F32 if necessary.
+    if (PS_TYPE_F64 == y->type.type) {
+        yPtr = psVectorCopy(NULL, y, PS_TYPE_F32);
+    } else {
+        yPtr = (psVector *) y;
+    }
+
+    //
+    // Generate the second derivatives at each data point.
+    //
+    spline->p_psDeriv2 = calculateSecondDerivs(xPtr, yPtr);
+
+    //
+    // We generate the coefficients of the spline polynomials.  I can't
+    // concisely explain how this code works.  See above function comments
+    // and Numerical Recipes in C.
+    //
+    for (psS32 i=0 ; i < numSplines ; i++) {
+        psF32 H = xPtr->data.F32[i+1] - xPtr->data.F32[i];
+        if (fabs(H) <= FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, false, "x data points are not distinct (%d %d) (%f %f).\n",
+                    i, i+1, xPtr->data.F32[i], xPtr->data.F32[i+1]);
+        }
+        psTrace("psLib.math", 6, "x data (%f - %f) (%f)\n", xPtr->data.F32[i], xPtr->data.F32[i+1], H);
+        //
+        // ******** Calculate 0-order term ********
+        //
+        // From (1)
+        spline->spline[i]->coeff[0] = yPtr->data.F32[i] * xPtr->data.F32[i+1]/H;
+        // From (2)
+        spline->spline[i]->coeff[0]-= (yPtr->data.F32[i+1] * xPtr->data.F32[i])/H;
+        // From (3)
+        psF32 tmp = (xPtr->data.F32[i+1] * xPtr->data.F32[i+1] * xPtr->data.F32[i+1]) / (H * H * H);
+        tmp-= xPtr->data.F32[i+1] / H;
+        tmp*= spline->p_psDeriv2[i] * H * H / 6.0;
+        spline->spline[i]->coeff[0]+= tmp;
+        // From (4)
+        tmp = -(xPtr->data.F32[i] * xPtr->data.F32[i] * xPtr->data.F32[i]) / (H * H * H);
+        tmp+= xPtr->data.F32[i] / H;
+        tmp*= spline->p_psDeriv2[i+1] * H * H / 6.0;
+        spline->spline[i]->coeff[0]+= tmp;
+
+        //
+        // ******** Calculate 1-order term ********
+        //
+        // From (1)
+        spline->spline[i]->coeff[1] = -(yPtr->data.F32[i]) / H;
+        // From (2)
+        spline->spline[i]->coeff[1]+= yPtr->data.F32[i+1] / H;
+        // From (3)
+        tmp = -3.0 * xPtr->data.F32[i+1] * xPtr->data.F32[i+1] / (H * H * H);
+        tmp+= (1.0 / H);
+        tmp*= spline->p_psDeriv2[i] * H * H / 6.0;
+        spline->spline[i]->coeff[1]+= tmp;
+        // From (4)
+        tmp = 3.0 * xPtr->data.F32[i] * xPtr->data.F32[i] / (H * H * H);
+        tmp-= 1.0 / H;
+        tmp*= spline->p_psDeriv2[i+1] * H * H / 6.0;
+        spline->spline[i]->coeff[1]+= tmp;
+
+        //
+        // ******** Calculate 2-order term ********
+        //
+        // From (3)
+        spline->spline[i]->coeff[2] = spline->p_psDeriv2[i] * 3.0 * xPtr->data.F32[i+1] / (6.0 * H);
+        // From (4)
+        spline->spline[i]->coeff[2]-= spline->p_psDeriv2[i+1] * 3.0 * xPtr->data.F32[i] / (6.0 * H);
+
+        //
+        // ******** Calculate 3-order term ********
+        //
+        // From (3)
+        spline->spline[i]->coeff[3] = -spline->p_psDeriv2[i] / (6.0 * H);
+        // From (4)
+        spline->spline[i]->coeff[3]+=  spline->p_psDeriv2[i+1] / (6.0 * H);
+
+        psTrace("psLib.math", 6, "(spline->spline[%u])->coeff[0] is %f\n", i, spline->spline[i]->coeff[0]);
+        psTrace("psLib.math", 6, "(spline->spline[%u])->coeff[1] is %f\n", i, spline->spline[i]->coeff[1]);
+        psTrace("psLib.math", 6, "(spline->spline[%u])->coeff[2] is %f\n", i, spline->spline[i]->coeff[2]);
+        psTrace("psLib.math", 6, "(spline->spline[%u])->coeff[3] is %f\n", i, spline->spline[i]->coeff[3]);
+    }
+
+    if (PS_TYPE_F64 == y->type.type) {
+        psFree(yPtr);
+    }
+    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
+    return(spline);
+}
+
+
+/*****************************************************************************
+psSpline1DEval(): this routine takes an existing spline of arbitrary order
+and an independent x value.  It determines which spline that x corresponds
+to by doing a bracket disection on the knots of the spline data structure
+(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?
+ *****************************************************************************/
+float psSpline1DEval(
+    const psSpline1D *spline,
+    float x)
+{
+    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(spline, NAN);
+    PS_ASSERT_INT_NONNEGATIVE(spline->n, NAN);
+    PS_ASSERT_VECTOR_TYPE(spline->knots, PS_TYPE_F32, NAN);
+
+    psS32 n = spline->n;
+    if ((x < spline->knots->data.F32[0]) || (x > spline->knots->data.F32[spline->knots->n-1])) {
+        // If x is outside the range of spline->knots, generate a warning
+        // message, then return the left, or right, endpoint.
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "psSpline1DEval(): x ordinate (%f) is outside the spline range (%f - %f) (%d).",
+                 x, spline->knots->data.F32[0], spline->knots->data.F32[n-1], n);
+
+        psS32 binNum = (x < spline->knots->data.F32[0]) ? 0 : n-1;
+        psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
+        return(psPolynomial1DEval(spline->spline[binNum], x));
+    }
+
+    psScalar tmpScalar;
+    tmpScalar.type.type = PS_TYPE_F32;
+    tmpScalar.data.F32 = x;
+    psVectorBinaryDisectResult result;
+    psS32 binNum = psVectorBinaryDisect(&result, spline->knots, &tmpScalar);
+    if (result != PS_BINARY_DISECT_PASS) {
+        psError(PS_ERR_UNKNOWN, false, "Could not perform bin dissection on spline->knots.\n");
+        return(NAN);
+    }
+
+    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
+    return(psPolynomial1DEval(spline->spline[binNum], x));
+}
+
+
+/*****************************************************************************
+ *****************************************************************************/
+psVector *psSpline1DEvalVector(
+    const psSpline1D *spline,
+    const psVector *x)
+{
+    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(spline, NULL);
+    PS_ASSERT_VECTOR_TYPE(spline->knots, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(x, NULL);
+    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(x, NULL);
+    if (psTraceGetLevel("psLib.math") >= 6) {
+        PS_VECTOR_PRINT_F32(x);
+        PS_PRINT_SPLINE2((psSpline1D *) spline);
+    }
+
+    psVector *tmpVector = psVectorAlloc(x->n, PS_TYPE_F32);
+    if (x->type.type == PS_TYPE_F32) {
+        for (psS32 i=0;i<x->n;i++) {
+            tmpVector->data.F32[i] = psSpline1DEval(spline, x->data.F32[i]);
+        }
+    } else if (x->type.type == PS_TYPE_F64) {
+        for (psS32 i=0;i<x->n;i++) {
+            tmpVector->data.F32[i] = psSpline1DEval(spline, (psF32) x->data.F64[i]);
+        }
+    }
+
+    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
+    return(tmpVector);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psSpline.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psSpline.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psSpline.h	(revision 22322)
@@ -0,0 +1,111 @@
+/* @file psSpline.h
+ * @brief Standard Mathematical Functions.
+ *
+ * This file will hold the prototypes for procedures which allocate, free,
+ * and evaluate splines.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.62 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-08-09 01:40:07 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_SPLINE_H
+#define PS_SPLINE_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psVector.h"
+#include "psScalar.h"
+#include "psPolynomial.h"
+
+#define PS_LEFT_SPLINE_DERIV 0.0
+#define PS_RIGHT_SPLINE_DERIV 0.0
+
+/** One-Dimensional Spline */
+typedef struct
+{
+    unsigned int n;                    ///< The number of spline pieces
+    psPolynomial1D **spline;           ///< An array of n pointers to the spline polynomials
+    psVector *knots;                   ///< The boundaries between each spline piece.  Size is n+1.
+    psF32 *p_psDeriv2;                 ///< For cubic splines, the second derivative at each domain point.  Size is n+1.
+}
+psSpline1D;
+
+/** Allocates a psSpline1D structure
+ *
+ *  Allocator for psSpline1D.
+ *
+ *  @return psSpline1D*    new 1-D spline struct
+ */
+psSpline1D *psSpline1DAlloc() PS_ATTR_MALLOC;
+
+/** Evaluates 1-D spline polynomials at a specific coordinate.
+ *
+ *  @return float    result of spline polynomials evaluated at given location
+ */
+float psSpline1DEval(
+    const psSpline1D *spline,          ///< Coefficients for spline polynomials
+    float x                            ///< location at which to evaluate
+);
+
+/** Evaluates 1-D spline polynomials at a set of specific coordinates.
+ *
+ *  @return psVector*    results of spline polynomials evaluated at given locations
+ */
+psVector *psSpline1DEvalVector(
+    const psSpline1D *spline,          ///< Coefficients of spline polynomials
+    const psVector *x                  ///< locations at which to evaluate
+);
+
+/** Derive a one-dimensional spline fit.
+ *
+ *  Given a psSpline1D data structure and a set of x,y vectors, this routine
+ *  generates the linear splines which satisfy those data points.
+ *
+ *  @return psSpline1D*:  the calculated one-dimensional splines
+ */
+psSpline1D *psVectorFitSpline1D(
+    const psVector* x,                 ///< Ordinates (or NULL to just use the indices)
+    const psVector* y                  ///< Coordinates
+);
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psSpline1D structure, false otherwise.
+ */
+bool psMemCheckSpline1D(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/*****************************************************************************
+    PS_SPLINE macros:
+*****************************************************************************/
+#define PS_ASSERT_SPLINE(NAME, RVAL) \
+if (false == psMemCheckSpline1D(NAME)) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: argument %s is not a psSpline1D struct.\n",\
+            #NAME); \
+    return(RVAL); \
+} \
+
+#define PS_ASSERT_SPLINE_NON_NULL(NAME, RVAL) \
+if ((NAME) == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: psSpline1D %s is NULL.", \
+            #NAME); \
+    return(RVAL); \
+} \
+
+
+/// @}
+#endif // #ifndef PS_SPLINE_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psStats.c	(revision 22322)
@@ -0,0 +1,2902 @@
+/** @file  psStats.c
+ *  \brief basic statistical operations
+ *  @ingroup Stat
+ *
+ *  This file will hold the definitions of the histogram and stats data
+ *  structures.  It also contains prototypes for procedures which operate
+ *  on those data structures.
+ *
+ *  @author GLG (MHPCC), EAM (IfA)
+ *
+ * XXX: Must do
+ * nSubsample points
+ * use ->min and ->max (PS_STAT_USE_RANGE)
+ *
+ *  @version $Revision: 1.228 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-03 20:54:43 $
+ *
+ *  Copyright 2006 IfA, University of Hawaii
+ */
+
+
+// Note: choice of return values in many places is quite grey.  For example, calculating the sample standard
+// deviation: here it's an error to get an array of size zero, but it's not an error to get an array of size
+// unity, even though the standard deviation is not defined in that case (NAN).
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <float.h>
+#include <math.h>
+#include <limits.h>
+#include <strings.h>
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+#include "psMemory.h"
+#include "psAbort.h"
+//#include "psImage.h"
+#include "psVector.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psHistogram.h"
+#include "psStats.h"
+#include "psMinimizeLMM.h"
+#include "psMinimizePolyFit.h"
+#include "psPolynomial.h"
+#include "psAssert.h"
+#include "psMathUtils.h"
+#include "psList.h"
+#include "psString.h"
+
+
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+#define PS_GAUSS_WIDTH 3                // The width of the Gaussian smoothing.
+#define PS_CLIPPED_NUM_ITER_LB 1 // This corresponds to N in the ADD.
+#define PS_CLIPPED_NUM_ITER_UB 10
+#define PS_CLIPPED_SIGMA_LB 1.0
+#define PS_CLIPPED_SIGMA_UB 10.0
+#define PS_POLY_MEDIAN_MAX_ITERATIONS 30
+#define MASK_MARK 0x80   // bit to use internally to mark data as bad
+#define PS_ROBUST_MAX_ITERATIONS 20     // Maximum number of iterations for robust statistics
+
+#define PS_BIN_MIDPOINT(HISTOGRAM, BIN_NUM) \
+(0.5 * (HISTOGRAM->bounds->data.F32[(BIN_NUM)] + HISTOGRAM->bounds->data.F32[(BIN_NUM)+1]))
+
+// set the bin closest to the corresponding value.  if USE_END is +/- 1,
+// out-of-range saturates on lower/upper bin REGARDLESS of actual value
+#define PS_BIN_FOR_VALUE(RESULT, VECTOR, VALUE, USE_END) { \
+        psVectorBinaryDisectResult result; \
+        psScalar tmpScalar; \
+        tmpScalar.type.type = PS_TYPE_F32; \
+        tmpScalar.data.F32 = (VALUE); \
+        RESULT = psVectorBinaryDisect (&result, VECTOR, &tmpScalar); \
+        switch (result) { \
+          case PS_BINARY_DISECT_PASS: \
+            break; \
+          case PS_BINARY_DISECT_OUTSIDE_RANGE: \
+            psTrace ("psLib.math", 6, "selected bin outside range"); \
+            if (USE_END == -1) { RESULT = 0; } \
+            if (USE_END == +1) { RESULT = VECTOR->n - 1; } \
+            break; \
+          case PS_BINARY_DISECT_INVALID_INPUT: \
+          case PS_BINARY_DISECT_INVALID_TYPE: \
+            psAbort ("programming error"); \
+            break; \
+        } }
+
+# define PS_BIN_INTERPOLATE(RESULT, VECTOR, BOUNDS, BIN, VALUE) { \
+        float dX, dY, Xo, Yo, Xt; \
+        if (BIN == BOUNDS->n - 1) { \
+            dX = 0.5*(BOUNDS->data.F32[BIN+1] - BOUNDS->data.F32[BIN-1]); \
+            dY = VECTOR->data.F32[BIN] - VECTOR->data.F32[BIN-1]; \
+            Xo = 0.5*(BOUNDS->data.F32[BIN+1] + BOUNDS->data.F32[BIN]); \
+            Yo = VECTOR->data.F32[BIN]; \
+        } else { \
+            dX = 0.5*(BOUNDS->data.F32[BIN+2] - BOUNDS->data.F32[BIN]); \
+            dY = VECTOR->data.F32[BIN+1] - VECTOR->data.F32[BIN]; \
+            Xo = 0.5*(BOUNDS->data.F32[BIN+1] + BOUNDS->data.F32[BIN]); \
+            Yo = VECTOR->data.F32[BIN]; \
+        } \
+        if (dY != 0) { \
+            Xt = (VALUE - Yo)*dX/dY + Xo; \
+        } else { \
+            Xt = Xo; \
+        } \
+        Xt = PS_MIN (BOUNDS->data.F32[BIN+1], PS_MAX(BOUNDS->data.F32[BIN], Xt)); \
+        psTrace("psLib.math", 6, "(Xo, Yo, dX, dY, Xt, Yt) is (%.2f %.2f %.2f %.2f %.2f %.2f)\n", \
+                Xo, Yo, dX, dY, Xt, VALUE); \
+        RESULT = Xt; }
+
+#define TRACE "psLib.math"
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+// None
+
+/******************************************************************************
+MISC PRIVATE STATISTICAL FUNCTIONS
+
+NOTE: it is assumed that any call to these statistical functions will have
+been preceded by a call to the psVectorStats() function.  Various sanity tests
+will only be performed in psVectorStats().
+
+For many of these private stats routines, it is possible that there are no acceptable elements
+in the input vector (if no elements lie within range, or there are no unmasked elements, or the
+input vector is NULL).  In such cases, we set the desired value to NAN and return an error.
+The calling function may clear the error.
+*****************************************************************************/
+
+// static prototypes:
+static psF32 minimizeLMChi2Gauss1D(psVector *deriv, const psVector *params, const psVector *coords);
+// static psF32 fitQuadraticSearchForYThenReturnX(const psVector *xVec, psVector *yVec, psS32 binNum, psF32 yVal);
+static psF32 fitQuadraticSearchForYThenReturnXusingValues(const psVector *xVec, psVector *yVec, psS32 binNum, psF32 yVal);
+
+/******************************************************************************
+vectorSampleMean(myVector, maskVector, maskVal, stats): calculates the
+mean of the input vector.  If there was a problem with the mean calculation,
+this routine sets stats->sampleMean to NAN.
+
+using the method below, with a single loop for various options costs only a small amount and is
+much easier to debug. running 10000 tests of 1000 point vectors for the two methods gives:
+
+                     separate    single          w/errors
+(mask: 0, range: 0): 0.067 sec  0.073 sec (10%)  0.072 sec
+(mask: 1, range: 0): 0.098 sec  0.102 sec ( 4%)  0.119 sec
+(mask: 0, range: 0): 0.136 sec  0.162 sec (20%)  0.170 sec
+(mask: 1, range: 0): 0.177 sec  0.181 sec ( 2%)  0.198 sec
+
+(effect of errors not tested)
+
+To optmize this, use a macro and ifdef in or out the three states (errors, mask, range)
+*****************************************************************************/
+    static bool vectorSampleMean(const psVector* myVector,
+                                 const psVector* errors,
+                                 const psVector* maskVector,
+                                 psMaskType maskVal,
+                                 psStats* stats)
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+
+    long count = 0;                     // Number of points contributing to this mean
+    psF32 mean = 0.0;                   // The mean
+    psF32 weight;
+
+    psF32 *data = myVector->data.F32;   // Dereference
+    int numData = myVector->n;          // Number of data points
+
+    psU8 *maskData = (maskVector == NULL) ? NULL : maskVector->data.U8;
+    bool useRange = stats->options & PS_STAT_USE_RANGE;
+
+    psF32 sumWeights = 0.0;  // The sum of the weights
+    psF32 *errorsData = (errors == NULL) ? NULL : errors->data.F32;
+
+    for (long i = 0; i < numData; i++) {
+        // Check if the data is with the specified range
+        if (useRange && (data[i] < stats->min))
+            continue;
+        if (useRange && (data[i] > stats->max))
+            continue;
+        if (maskData && (maskData[i] & maskVal))
+            continue;
+        if (errors) {
+            weight = (errorsData[i] == 0) ? 0.0 : PS_SQR(errorsData[i]);
+            mean += data[i] * weight;
+            sumWeights += weight;
+        } else {
+            mean += data[i];
+        }
+        count++;
+    }
+    if (errors) {
+        mean = (count > 0) ? mean / sumWeights : NAN;
+    } else {
+        mean = (count > 0) ? mean / count : NAN;
+    }
+    stats->sampleMean = mean;
+    if (isnan(mean)) {
+        // XXX raise an error here?
+        psTrace(TRACE, 4, "---- %s(false) end ----\n", __func__);
+        return true;
+    }
+
+    stats->results |= PS_STAT_SAMPLE_MEAN;
+    psTrace(TRACE, 4, "---- %s(true) end ----\n", __func__);
+    return true;
+}
+
+/******************************************************************************
+vectorMinMax(myVector, maskVector, maskVal, stats): calculates the minimum and maximum of the input vector.
+If there was a problem with the calculation, this routine sets stats->max and stats->min to NAN.  Return the
+number of valid values in the vector (those not masked or outside the specified range).
+XXX : using the method below, with a single loop for various options
+      costs only a small amount and is much easier to debug. running 10000 tests
+      of 1000 point vectors for the two methods gives:
+                     separate    single
+(mask: 0, range: 0):  0.101 sec  0.149 sec
+(mask: 1, range: 0):  0.125 sec  0.160 sec
+(mask: 0, range: 0):  0.179 sec  0.208 sec
+(mask: 1, range: 0):  0.200 sec  0.244 sec
+*****************************************************************************/
+    static long vectorMinMax(const psVector* myVector,
+                             const psVector* maskVector,
+                             psMaskType maskVal,
+                             psStats* stats
+        )
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+    psF32 max = -PS_MAX_F32;            // The calculated maximum
+    psF32 min = PS_MAX_F32;             // The calculated minimum
+    psF32 *vector = myVector->data.F32; // Dereference the vector
+
+    int num = myVector->n;              // Number of values
+    int numValid = 0;                   // Number of valid values
+
+    psU8 *maskData = (maskVector == NULL) ? NULL : maskVector->data.U8;
+    bool useRange = stats->options & PS_STAT_USE_RANGE;
+
+    for (long i = 0; i < num; i++) {
+        // Check if the data is with the specified range
+        if (maskData && (maskData[i] & maskVal)) {
+            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;
+        }
+
+        numValid++;
+        max = PS_MAX (vector[i], max);
+        min = PS_MIN (vector[i], min);
+    }
+
+    // XXX save numValid in psStats?
+    if (numValid == 0) {
+        stats->max = NAN;
+        stats->min = NAN;
+    } else {
+        stats->max = max;
+        stats->min = min;
+        stats->results |= PS_STAT_MIN;
+        stats->results |= PS_STAT_MAX;
+    }
+    psTrace(TRACE, 4, "---- %s(%d) end ----\n", __func__, numValid);
+    return numValid;
+}
+
+/******************************************************************************
+vectorSampleMedian(myVector, maskVector, maskVal, stats): calculates the median
+and quartiles of the input vector.  Returns true on success (including if there
+were no valid input vector elements). Expects F32 vector for input.
+*****************************************************************************/
+static bool vectorSampleMedian(const psVector* inVector,
+                               const psVector* maskVector,
+                               psMaskType maskVal,
+                               psStats* stats)
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+
+    bool useRange = stats->options & PS_STAT_USE_RANGE;
+    psU8 *maskData = (maskVector == NULL) ? NULL : maskVector->data.U8; // Dereference the vector
+    psF32 *input = inVector->data.F32; // Dereference the vector
+
+    // use the temporary vector for the sorted output
+    stats->tmpData = psVectorRecycle (stats->tmpData, inVector->n, PS_TYPE_F32);
+    psVector *outVector = stats->tmpData;
+    psF32 *output = outVector->data.F32; // Dereference the vector
+
+    long count = 0;                     // Number of valid entries
+
+    // Store all non-masked data points within the min/max range
+    // into the temporary vectors.
+    for (long i = 0; i < inVector->n; i++) {
+        if (useRange && (input[i] < stats->min))
+            continue;
+        if (useRange && (input[i] > stats->max))
+            continue;
+        if (maskData && (maskData[i] & maskVal))
+            continue;
+
+        output[count] = input[i];
+        count++;
+    }
+    outVector->n = count;
+
+    if (count == 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "No valid data in input vector.\n");
+        stats->sampleUQ = NAN;
+        stats->sampleLQ = NAN;
+        stats->sampleMedian = NAN;
+        return true;
+    }
+
+    // Sort the temporary vector.
+    if (!psVectorSort(outVector, outVector)) { // Sort in-place (since it's a copy, it's OK)
+        psError(PS_ERR_UNEXPECTED_NULL, false, _("Failed to sort input data."));
+        stats->sampleUQ = NAN;
+        stats->sampleLQ = NAN;
+        stats->sampleMedian = NAN;
+        return false;
+    }
+
+    // Calculate the median.  Use the average if the number of samples if even.
+    if (count % 2 == 0) {
+        stats->sampleMedian = 0.5 * (output[(count/2) - 1] + output[count/2]);
+    } else {
+        stats->sampleMedian = output[count/2];
+    }
+
+    // Calculate the quartile points exactly.
+    stats->sampleUQ = output[(int)(0.75*count)];
+    stats->sampleLQ = output[(int)(0.25*count)];
+
+    stats->results |= PS_STAT_SAMPLE_MEDIAN;
+    stats->results |= PS_STAT_SAMPLE_QUARTILE;
+
+    // Return "true" on success.
+    psTrace(TRACE, 4, "---- %s(true) end ----\n", __func__);
+    return true;
+}
+
+/******************************************************************************
+vectorSampleStdev(myVector, maskVector, maskVal, stats): calculates the
+stdev of the input vector.
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    NULL
+
+using the method below, with a single loop for various options costs only a small amount and is
+much easier to debug.
+*****************************************************************************/
+
+static bool vectorSampleStdev(const psVector* myVector,
+                              const psVector* errors,
+                              const psVector* maskVector,
+                              psMaskType maskVal,
+                              psStats* stats)
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+
+    // This procedure requires the mean.  If it has not been already
+    // calculated, then call vectorSampleMean()
+    if (!(stats->results & PS_STAT_SAMPLE_MEAN)) {
+        vectorSampleMean(myVector, errors, maskVector, maskVal, stats);
+    }
+
+    // If the mean is NAN, then generate a warning and set the stdev to NAN.
+    if (isnan(stats->sampleMean)) {
+        psTrace(TRACE, 5, "WARNING: vectorSampleStdev(): sample mean is NAN.  "
+                "Setting stats->sampleStdev = NAN.\n");
+        stats->sampleStdev = NAN;
+        return true;
+    }
+
+    psF32 *data = myVector->data.F32;   // Dereference
+    psU8 *maskData = (maskVector == NULL) ? NULL : maskVector->data.U8;
+    bool useRange = stats->options & PS_STAT_USE_RANGE;
+    psF32 *errorsData = (errors == NULL) ? NULL : errors->data.F32;
+
+    // Accumulate the sums
+    double mean = stats->sampleMean;    // The mean
+    double sumSquares = 0.0;            // Sum of the squares
+    double sumDiffs = 0.0;              // Sum of the differences
+    double errorDivisor = 0.0;          // Division for errors
+    long count = 0;                     // Number of data points being used
+    for (long i = 0; i < myVector->n; i++) {
+        // Check if the data is with the specified range
+        if (useRange && (data[i] < stats->min)) {
+            continue;
+        }
+        if (useRange && (data[i] > stats->max)) {
+            continue;
+        }
+        if (maskData && (maskData[i] & maskVal)) {
+            continue;
+        }
+
+        double diff = data[i] - mean;
+        sumSquares += PS_SQR(diff);
+        sumDiffs += diff;
+        count++;
+        if (errors) {
+            errorDivisor += 1.0 / PS_SQR(errorsData[i]);
+        }
+    }
+
+    if (count == 0) {
+        // This is an ambiguous case: error or not?
+        // It's not an empty array (that's been asserted on previously), but everything's been masked out.
+        // Assume that the user knows what he's doing when he masks out everything --> no error.
+        stats->sampleStdev = NAN;
+        psTrace(TRACE, 5, "WARNING: vectorSampleStdev(): no valid psVector elements (%ld).  "
+                "Setting stats->sampleStdev = NAN.\n", count);
+        return true;
+    }
+    if (count == 1) {
+        stats->sampleStdev = 0.0;
+        psTrace(TRACE, 5, "WARNING: vectorSampleStdev(): only one valid psVector elements (%ld).  "
+                "Setting stats->sampleStdev = 0.0.\n", count);
+        return true;
+    }
+
+    if (errors) {
+        stats->sampleStdev = (1.0 / sqrtf(errorDivisor));
+    } else {
+        stats->sampleStdev = sqrt((sumSquares - (sumDiffs * sumDiffs / (float)count)) / (float)(count - 1));
+    }
+    stats->results |= PS_STAT_SAMPLE_STDEV;
+
+    psTrace(TRACE, 4, "---- %s() end ----\n", __func__);
+
+    return true;
+}
+
+static bool vectorSampleMoments(const psVector* myVector,
+                                const psVector* maskVector,
+                                psMaskType maskVal,
+                                psStats* stats)
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+
+    // This procedure requires the mean and standard deviation
+    if (!(stats->results & PS_STAT_SAMPLE_MEAN)) {
+        vectorSampleMean(myVector, NULL, maskVector, maskVal, stats);
+    }
+    if (isnan(stats->sampleMean)) {
+        psTrace(TRACE, 5, "WARNING: vectorSampleMoments(): sample mean is NAN.\n");
+        goto SAMPLE_MOMENTS_BAD;
+    }
+    if (!(stats->results & PS_STAT_SAMPLE_STDEV)) {
+        vectorSampleStdev(myVector, NULL, maskVector, maskVal, stats);
+    }
+    if (isnan(stats->sampleStdev) || stats->sampleStdev == 0.0) {
+        psTrace(TRACE, 5, "WARNING: vectorSampleMoments(): sample stdev is NAN or 0.\n");
+        goto SAMPLE_MOMENTS_BAD;
+    }
+
+    psF32 *data = myVector->data.F32;   // Dereference
+    psU8 *maskData = (maskVector == NULL) ? NULL : maskVector->data.U8;
+    bool useRange = stats->options & PS_STAT_USE_RANGE;
+
+    // Accumulate the sums
+    double mean = stats->sampleMean;    // The mean
+    double sum3 = 0.0;                  // Sum of the cubes of the differences
+    double sum4 = 0.0;                  // Sum of the fourth powers of the differences
+    long count = 0;                     // Number of data points being used
+    for (long i = 0; i < myVector->n; i++) {
+        // Check if the data is with the specified range
+        if (useRange && (data[i] < stats->min)) {
+            continue;
+        }
+        if (useRange && (data[i] > stats->max)) {
+            continue;
+        }
+        if (maskData && (maskData[i] & maskVal)) {
+            continue;
+        }
+
+        double diff = data[i] - mean;   // Difference from the mean
+        double temp;                    // Temporary variable for accumulating
+
+        sum3 += temp = PS_SQR(diff);
+        sum4 += temp * diff;
+
+        count++;
+    }
+
+    psAssert(count > 1, "impossible");                  // It should be, because we have a mean and standard deviation
+
+    double stdev = stats->sampleStdev;  // Standard deviation
+    double variance = PS_SQR(stdev);    // Variance
+
+    // Formula for skewness and kurtosis from Numerical Recipes in C, p 613.
+    // Note that we are defining the kurtosis relative to a normal distribution (hence the -3.0).
+    stats->sampleSkewness = sum3 / (count * variance * stdev);
+    stats->sampleKurtosis = sum4 / (count * PS_SQR(variance)) - 3.0;
+    stats->results |= PS_STAT_SAMPLE_SKEWNESS | PS_STAT_SAMPLE_KURTOSIS;
+
+    psTrace(TRACE, 4, "---- %s() end ----\n", __func__);
+
+    return true;
+
+ SAMPLE_MOMENTS_BAD:
+    // stats->sampleStdev has already been set
+    stats->sampleSkewness = NAN;
+    stats->sampleKurtosis = NAN;
+    stats->results |= PS_STAT_SAMPLE_SKEWNESS | PS_STAT_SAMPLE_KURTOSIS;
+    return true;
+}
+
+/******************************************************************************
+vectorClippedStats(myVector, errors, maskInput, maskValInput, stats): calculates the
+clipped stats (mean or stdev) of the input vector.
+
+Inputs
+    myVector
+    maskInput
+    maskValInput
+    stats
+Returns
+    true for success; false otherwise
+*****************************************************************************/
+static bool vectorClippedStats(const psVector* myVector,
+                               const psVector* errors,
+                               psVector* maskInput,
+                               psMaskType maskValInput,
+                               psStats* stats
+    )
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+    psTrace(TRACE, 4, "Trace level is %d\n", psTraceGetLevel("psLib.math"));
+
+    // Ensure that stats->clipIter is within the proper range.
+    PS_ASSERT_INT_WITHIN_RANGE(stats->clipIter,
+                               PS_CLIPPED_NUM_ITER_LB,
+                               PS_CLIPPED_NUM_ITER_UB, -1);
+
+    // Ensure that stats->clipSigma is within the proper range.
+    PS_ASSERT_FLOAT_WITHIN_RANGE(stats->clipSigma,
+                                 PS_CLIPPED_SIGMA_LB,
+                                 PS_CLIPPED_SIGMA_UB, -1);
+
+    // unless we succeed, these will have NAN values
+    stats->clippedMean = NAN;
+    stats->clippedStdev = NAN;
+    stats->clippedNvalues = 0;
+
+    // We copy the mask vector, to preserve the original
+    psMaskType maskVal = MASK_MARK | maskValInput;
+
+    // use the temporary vector for local temporary mask
+    stats->tmpMask = psVectorRecycle (stats->tmpMask, myVector->n, PS_TYPE_U8);
+    psVector *tmpMask = stats->tmpMask;
+    psVectorInit(tmpMask, 0);
+    if (maskInput) {
+        for (long i = 0; i < myVector->n; i++) {
+            if (maskInput->data.U8[i] & maskValInput) {
+                tmpMask->data.U8[i] = maskVal;
+            }
+        }
+    }
+
+    // 1. Compute the sample median, which we save for output
+    vectorSampleMedian(myVector, tmpMask, maskVal, stats);
+    if (isnan(stats->sampleMedian)) {
+        psTrace(TRACE, 5, "Call to vectorSampleMedian returned NAN\n");
+        psTrace(TRACE, 4, "---- %s(false) end ----\n", __func__);
+        return false;
+    }
+    psTrace(TRACE, 6, "The initial sample median is %f\n", stats->sampleMedian);
+
+    // 2. Compute the sample standard deviation, which we save for output
+    vectorSampleStdev(myVector, errors, tmpMask, maskVal, stats);
+    if (isnan(stats->sampleStdev)) {
+        psTrace(TRACE, 5, "Call to vectorSampleStdev returned NAN\n");
+        psTrace(TRACE, 4, "---- %s(false) end ----\n", __func__);
+        return false;
+    }
+    psTrace(TRACE, 6, "The initial sample stdev is %f\n", stats->sampleStdev);
+
+    // 3. Use the sample median as the first estimator of the mean X.
+    psF32 clippedMean = stats->sampleMedian;
+
+    // 4. Use the sample stdev as the first estimator of the mean stdev.
+    psF32 clippedStdev = stats->sampleStdev;
+
+    // 5. Repeat N (stats->clipIter) times:
+    long numClipped = 0;                // Number of values clipped
+    bool clipped = true;                // Have we clipped anything in this iteration
+    for (int iter = 0; iter < stats->clipIter && clipped; iter++) {
+        clipped = false;
+        psTrace(TRACE, 6, "------------ Iteration %d ------------\n", iter);
+        // a) Exclude all values x_i for which |x_i - x| > K * stdev
+        if (errors) {
+            // XXXX if we convert errors to variance, this should square the other terms (A*A faster than
+            // sqrt(A))
+            for (long j = 0; j < myVector->n; j++) {
+                if (!tmpMask->data.U8[j] &&
+                    fabsf(myVector->data.F32[j] - clippedMean) > stats->clipSigma * errors->data.F32[j]) {
+                    tmpMask->data.U8[j] = 0xff;
+                    psTrace(TRACE, 10, "Clipped %ld: %f +/- %f\n", j,
+                            myVector->data.F32[j], errors->data.F32[j]);
+                    numClipped++;
+                    clipped = true;
+                }
+            }
+        } else {
+            for (long j = 0; j < myVector->n; j++) {
+                if (!tmpMask->data.U8[j] &&
+                    fabsf(myVector->data.F32[j] - clippedMean) > (stats->clipSigma * clippedStdev)) {
+                    tmpMask->data.U8[j] = 0xff;
+                    psTrace(TRACE, 10, "Clipped %ld: %f\n", j, myVector->data.F32[j]);
+                    numClipped++;
+                    clipped = true;
+                }
+            }
+        }
+
+        // b) compute new mean and stdev
+        // Allocate a psStats structure for calculating the mean and stdev.
+	// XXX Can we just use this psStats structure to calculate the SAMPLE MEAN and STDEV?
+        // psStats *statsTmp = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        // vectorSampleMean(myVector, errors, tmpMask, maskVal, statsTmp);
+        // vectorSampleStdev(myVector, errors, tmpMask, maskVal, statsTmp);
+        vectorSampleMean(myVector, errors, tmpMask, maskVal, stats);
+        vectorSampleStdev(myVector, errors, tmpMask, maskVal, stats);
+        psTrace(TRACE, 6, "The new sample mean is %f\n", stats->sampleMean);
+        psTrace(TRACE, 6, "The new sample stdev is %f\n", stats->sampleStdev);
+
+        // If the new mean and stdev are NAN, we must exit the loop.
+        // Otherwise, use the new results and continue.
+        if (isnan(stats->sampleMean) || isnan(stats->sampleStdev)) {
+            iter = stats->clipIter;
+            psError(PS_ERR_UNKNOWN, true, "vectorSampleMean() or vectorSampleStdev() returned a NAN.\n");
+            return false;
+        } else {
+            clippedMean = stats->sampleMean;
+            clippedStdev = stats->sampleStdev;
+        }
+        // psFree(statsTmp);
+    }
+
+    // Number of values used in calculation is the total number of data values, minus those we clipped
+    stats->clippedNvalues = myVector->n - numClipped;
+
+    // 7. The last calcuated value of x is the clipped mean.
+    // 8. The last calcuated value of stdev is the clipped stdev.
+    // we always return both stats even if only one was requested
+    stats->clippedMean = clippedMean;
+    stats->clippedStdev = clippedStdev;
+
+    stats->results |= PS_STAT_CLIPPED_MEAN;
+    stats->results |= PS_STAT_CLIPPED_STDEV;
+
+    psTrace(TRACE, 6, "The final clipped mean is %f\n", clippedMean);
+    psTrace(TRACE, 6, "The final clipped stdev is %f\n", clippedStdev);
+
+    psTrace(TRACE, 4, "---- %s(true) end ----\n", __func__);
+    return true;
+}
+
+/******************************************************************************
+vectorRobustStats(myVector, maskInput, maskValInput, stats): This is the new
+version of the robust stats routine.
+
+XXX: MUST DO: If the errors in the input values are known, then the same
+approach is used, except that the histograms become probability density
+functions (PDFs). In this case, the input values are spread out, so that they
+do not simply contribute a single unit to the histogram, but rather contribute
+a fraction of a value, equivalent to the weight. In the interests of speed, a
+boxcar PDF may be used to represent each input value (as opposed to a
+Gaussian), where the boxcar width is equal to 2p2 ln 2 times the error and
+each input value contributes constant area. Then the robust median and
+standard deviation are estimated in the same manner as above.
+
+XXX: Check for errors in psLib routines that we call.
+
+XXX: Review and ensure that all memory is free'ed at premature exits.
+*****************************************************************************/
+#define INITIAL_NUM_BINS 1000.0
+static bool vectorRobustStats(const psVector* myVector,
+                              const psVector* errors,
+                              psVector* maskInput,
+                              psMaskType maskValInput,
+                              psStats* stats)
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+    if (psTraceGetLevel("psLib.math") >= 8) {
+        PS_VECTOR_PRINT_F32(myVector);
+    }
+
+    // we need to generate an internal mask and maskVal which ensures a bit is set
+    // and tested even if there is no supplied mask (and/or the maskVal is 0)
+    // XXX this would be better if we had globally defined mask values
+    psMaskType maskVal = MASK_MARK | maskValInput;
+    psVector *mask = psVectorAlloc(myVector->n, PS_TYPE_MASK); // The actual mask we will use
+    psVectorInit(mask, 0);
+    if (maskInput) {
+        for (long i = 0; i < myVector->n; i++) {
+            if (maskInput->data.U8[i] & maskValInput) {
+                mask->data.U8[i] = maskVal;
+            }
+        }
+    }
+
+    // statsMinMax is only applied to a subset of the data points
+    psStats *statsMinMax = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
+    psHistogram *histogram = NULL;      // Histogram of the data
+    psHistogram *cumulative = NULL;     // Cumulative histogram of the data
+    float min = NAN, max = NAN;         // Mimimum and maximum values
+    float sigma = NAN;                  // The robust standard deviation
+    long totalDataPoints = 0;           // Total number of (unmasked) data points
+
+    float binSize = 0.0;            // Size of bins for histogram
+    long binLo, binHi;
+    long binL2, binH2;
+    long binL4, binH4;
+    long binMedian;
+
+    // Iterate to get the best bin size; an iteration limit is enforced at the bottom of the loop.
+    for (int iterate = 1; iterate > 0; iterate++) {
+        psTrace(TRACE, 6,
+                "-------------------- Iterating on Bin size.  Iteration number %d --------------------\n",
+                iterate);
+
+        // Get the minimum and maximum values
+        int numValid = vectorMinMax(myVector, mask, maskVal, statsMinMax); // Number of valid values
+        min = statsMinMax->min;
+        max = statsMinMax->max;
+        if (numValid == 0 || isnan(min) || isnan(max)) {
+            // Data range calculation failed
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
+            goto escape;
+        }
+        psTrace(TRACE, 6, "Data min/max is (%.2f, %.2f)\n", min, max);
+
+        // If all data points have the same value, then we set the appropriate members of stats and return.
+        if (fabs(max - min) <= FLT_EPSILON) {
+            stats->robustMedian = min;
+            stats->robustStdev = 0.0;
+            stats->robustUQ = min;
+            stats->robustLQ = min;
+            stats->robustN50 = numValid;
+            stats->results |= PS_STAT_ROBUST_MEDIAN;
+            stats->results |= PS_STAT_ROBUST_STDEV;
+            stats->results |= PS_STAT_ROBUST_QUARTILE;
+            psTrace(TRACE, 5, "All data points have the same value: %f.\n", min);
+            psTrace(TRACE, 4, "---- %s(0) end  ----\n", __func__);
+            psFree(mask);
+            psFree(statsMinMax);
+            return true;
+        }
+
+        if ((iterate == 1) && (stats->options & PS_STAT_USE_BINSIZE)) {
+            // Set initial bin size to the specified value.
+            binSize = stats->binsize;
+            psTrace(TRACE, 6, "Setting initial robust bin size to %.2f\n", binSize);
+        } else {
+            // Determine the bin size of the robust histogram, using the pre-defined number of bins
+            binSize = (max - min) / INITIAL_NUM_BINS;
+        }
+        psTrace(TRACE, 6, "Initial robust bin size is %.2f\n", binSize);
+
+        // ADD step 0: Construct the histogram with the specified bin size.  NOTE: we can not specify the bin
+        // size precisely since the argument to psHistogramAlloc() is the number of bins, not the binSize.  If
+        // we get here, we know that binSize != 0.0.
+        long numBins = (max - min) / binSize; // Number of bins
+        psTrace(TRACE, 6, "Numbins is %ld\n", numBins);
+        psTrace(TRACE, 6, "Creating a robust histogram from data range (%.2f - %.2f)\n", min, max);
+        // Generate the histogram
+        histogram = psHistogramAlloc(min, max, numBins);
+        // XXXXX we need to consider this step if errors -> variance
+        if (!psVectorHistogram(histogram, myVector, errors, mask, maskVal)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate histogram for robust statistics.\n");
+            goto escape;
+        }
+        if (psTraceGetLevel("psLib.math") >= 8) {
+            PS_VECTOR_PRINT_F32(histogram->bounds);
+            PS_VECTOR_PRINT_F32(histogram->nums);
+        }
+
+        // ADD step 1: Convert the specific histogram to a cumulative histogram
+        // The cumulative histogram data points correspond to the UPPER bound value (N < Bin[i+1])
+        cumulative = psHistogramAlloc(min, max, numBins);
+        cumulative->nums->data.F32[0] = histogram->nums->data.F32[0];
+        for (long i = 1; i < histogram->nums->n; i++) {
+            cumulative->nums->data.F32[i] = cumulative->nums->data.F32[i-1] + histogram->nums->data.F32[i];
+            cumulative->bounds->data.F32[i-1] = histogram->bounds->data.F32[i];
+        }
+        if (psTraceGetLevel("psLib.math") >= 8) {
+            PS_VECTOR_PRINT_F32(cumulative->bounds);
+            PS_VECTOR_PRINT_F32(cumulative->nums);
+        }
+
+        // ADD step 2: Find the bin which contains the 50% data point.
+        totalDataPoints = cumulative->nums->data.F32[numBins - 1];
+        psTrace(TRACE, 6, "Total data points is %ld\n", totalDataPoints);
+
+        // find bin which is the lower bound of median (value[binMedian] < median < value[binMedian+1]
+        PS_BIN_FOR_VALUE(binMedian, cumulative->nums, totalDataPoints/2.0, 0);
+
+        psTrace(TRACE, 6, "The median bin is %ld (%.2f to %.2f)\n", binMedian,
+                cumulative->bounds->data.F32[binMedian], cumulative->bounds->data.F32[binMedian+1]);
+
+        // ADD step 3: Interpolate to the exact 50% position: this is the robust histogram median.
+        stats->robustMedian = fitQuadraticSearchForYThenReturnXusingValues(cumulative->bounds, cumulative->nums, binMedian, totalDataPoints/2.0);
+        if (isnan(stats->robustMedian)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to fit a quadratic and calculate the 50-percent position.\n");
+            goto escape;
+        }
+        psTrace(TRACE, 6, "Current robust median is %f\n", stats->robustMedian);
+
+        // ADD step 4: Find the bins which contains the 15.8655% (-1 sigma) and 84.1345% (+1 sigma) data
+        // points also find the 30.8538% (-0.5 sigma) and 69.1462% (+0.5 sigma) points
+        PS_BIN_FOR_VALUE(binLo, cumulative->nums, totalDataPoints * 0.158655f, 0);
+        PS_BIN_FOR_VALUE(binHi, cumulative->nums, totalDataPoints * 0.841345f, 0);
+        PS_BIN_FOR_VALUE(binL2, cumulative->nums, totalDataPoints * 0.308538f, 0);
+        PS_BIN_FOR_VALUE(binH2, cumulative->nums, totalDataPoints * 0.691462f, 0);
+        PS_BIN_FOR_VALUE(binL4, cumulative->nums, totalDataPoints * 0.022481f, 0);
+        PS_BIN_FOR_VALUE(binH4, cumulative->nums, totalDataPoints * 0.977519f, 0);
+
+        psTrace(TRACE, 6, "The 15.8655%% and 84.1345%% data point bins are (%ld, %ld).\n",
+                binLo, binHi);
+        psTrace(TRACE, 6, "binLo midpoint is %f\n", PS_BIN_MIDPOINT(cumulative, binLo));
+        psTrace(TRACE, 6, "binHi midpoint is %f\n", PS_BIN_MIDPOINT(cumulative, binHi));
+        psTrace(TRACE, 6, "binL2 midpoint is %f\n", PS_BIN_MIDPOINT(cumulative, binL2));
+        psTrace(TRACE, 6, "binH2 midpoint is %f\n", PS_BIN_MIDPOINT(cumulative, binH2));
+
+        if ((binLo < 0) || (binHi < 0)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the 15.8655%% and 84.1345%% data points.\n");
+            goto escape;
+        }
+
+        // ADD step 4b: Interpolate Sigma (linearly) to find these two positions exactly: these are the 1sigma
+        // positions.
+        psTrace(TRACE, 6, "binLo is %ld.  Nums at that bin and the next are (%.2f, %.2f)\n",
+                binLo, cumulative->nums->data.F32[binLo], cumulative->nums->data.F32[binLo+1]);
+        psTrace(TRACE, 6, "binHi is %ld.  Nums at that bin and the next are (%.2f, %.2f)\n",
+                binHi, cumulative->nums->data.F32[binHi], cumulative->nums->data.F32[binHi+1]);
+
+        // find the +0.5 and -0.5 sigma points with linear interpolation.  binLo and binHi are the bins
+        // containing the value of interest.  constrain the answer to be within the current bin.
+        // (extrapolation should not be needed and will result in errors)
+        float binLoF32, binHiF32, binL2F32, binH2F32, binL4F32, binH4F32;
+        PS_BIN_INTERPOLATE (binLoF32, cumulative->nums, cumulative->bounds, binLo,
+                            totalDataPoints * 0.158655f);
+        PS_BIN_INTERPOLATE (binHiF32, cumulative->nums, cumulative->bounds, binHi,
+                            totalDataPoints * 0.841345f);
+        PS_BIN_INTERPOLATE (binL2F32, cumulative->nums, cumulative->bounds, binL2,
+                            totalDataPoints * 0.308538f);
+        PS_BIN_INTERPOLATE (binH2F32, cumulative->nums, cumulative->bounds, binH2,
+                            totalDataPoints * 0.691462f);
+        PS_BIN_INTERPOLATE (binL4F32, cumulative->nums, cumulative->bounds, binL4,
+                            totalDataPoints * 0.022481f);
+        PS_BIN_INTERPOLATE (binH4F32, cumulative->nums, cumulative->bounds, binH4,
+                            totalDataPoints * 0.977519f);
+
+        // report +/- 1 sigma points
+        psTrace(TRACE, 5,
+                "The exact 15.8655 and 84.1345 percent data point positions are: (%f, %f)\n",
+                binLoF32, binHiF32);
+        psTrace(TRACE, 5,
+                "The exact 30.8538 and 69.1462 percent data point positions are: (%f, %f)\n",
+                binL2F32, binH2F32);
+        psTrace(TRACE, 5,
+                "The exact 02.22481 and 97.7519 percent data point positions are: (%f, %f)\n",
+                binL4F32, binH4F32);
+
+        // ADD step 5: Determine SIGMA as the distance between binL2 and binH2 (+/- 0.5 sigma)
+        float sigma1 = (binH2F32 - binL2F32);
+        float sigma2 = (binHiF32 - binLoF32) / 2.0;
+        float sigma4 = (binH4F32 - binL4F32) / 4.0;
+
+        // take the smallest of the three: if we have a clump with wide outliers, sigma2 and
+        // sigma4 will be biased high; if we have a bi-modal distribution, sigma1 and sigma2
+        // will be biased high.
+        sigma = PS_MIN (sigma1, PS_MIN (sigma2, sigma4));
+
+        psTrace(TRACE, 6, "The 1x sigma is %f.\n", sigma1);
+        psTrace(TRACE, 6, "The 2x sigma is %f.\n", sigma2);
+        psTrace(TRACE, 6, "The 4x sigma is %f.\n", sigma4);
+
+        psTrace(TRACE, 6, "The current sigma is %f.\n", sigma);
+        stats->robustStdev = sigma;
+
+        // ADD step 6: If the measured SIGMA is less than 2 times the bin size, exclude points which are more
+        // than 25 bins from the median, recalculate the bin size, and perform the algorithm again.
+        if (sigma < (3.0 * binSize)) {
+            psTrace(TRACE, 6, "*************: Do another iteration (%f %f).\n", sigma, binSize);
+            long maskLo = PS_MAX(0, (binMedian - 25)); // Low index for masking region
+            long maskHi = PS_MIN(histogram->bounds->n - 1, (binMedian + 25)); // High index for masking
+            psF32 medianLo = histogram->bounds->data.F32[maskLo]; // Value at low index
+            psF32 medianHi = histogram->bounds->data.F32[maskHi]; // Value at high index
+            psTrace(TRACE, 6, "Masking data more than 25 bins from the median\n");
+            psTrace(TRACE, 6,
+                    "The median is at bin number %ld.  We mask bins outside the bin range (%ld:%ld)\n",
+                    binMedian, maskLo, maskHi);
+            psTrace(TRACE, 6, "Masking data outside (%f %f)\n", medianLo, medianHi);
+            for (long i = 0 ; i < myVector->n ; i++) {
+                if ((myVector->data.F32[i] < medianLo) || (myVector->data.F32[i] > medianHi)) {
+		    // XXXX is this correct?  is MASK_MARK safe?
+                    mask->data.U8[i] |= MASK_MARK;
+                    psTrace(TRACE, 6, "Masking element %ld is %f\n", i, myVector->data.F32[i]);
+                }
+            }
+
+            // Free the histograms; they will be recreated on the next iteration, with new bounds
+            psFree(histogram);
+            histogram = NULL;
+
+            psFree(cumulative);
+            cumulative = NULL;
+
+            if (iterate >= PS_ROBUST_MAX_ITERATIONS) {
+                // This occurs when a large number of the values are identical --- a bin size cannot be found
+                // that will spread out the distribution.  Therefore, set what we can, and fall over
+                // gracefully.
+                // stats->robustMedian has already been set
+                stats->robustStdev = sigma;
+                stats->robustUQ = stats->robustMedian;
+                stats->robustLQ = stats->robustMedian;
+                stats->robustN50 = numValid;
+                stats->results |= PS_STAT_ROBUST_MEDIAN;
+                stats->results |= PS_STAT_ROBUST_STDEV;
+                stats->results |= PS_STAT_ROBUST_QUARTILE;
+                psTrace(TRACE, 5, "Maximum number of iterations (%d) exceeded.", PS_ROBUST_MAX_ITERATIONS);
+                psTrace(TRACE, 4, "---- %s(0) end  ----\n", __func__);
+                psFree(mask);
+                psFree(statsMinMax);
+                return false;
+            }
+        } else {
+            // We've got the bin size correct now
+            psTrace(TRACE, 6, "*************: No more iteration.  sigma is %f\n", sigma);
+            iterate = -1;
+        }
+    }
+
+    // XXX test lines while studying algorithm errors
+    // fprintf (stderr, "robust stats test %7.1f +/- %7.1f : %4ld %4ld %4ld %4ld %4ld  : %f %f %f\n",
+    // stats->robustMedian, stats->robustStdev,
+    // binLo, binL2, binMedian, binH2, binHi, binSize, max, min);
+
+    // ADD step 7: Find the bins which contains the 25% and 75% data points.
+    long binLo25, binHi25;
+    PS_BIN_FOR_VALUE (binLo25, cumulative->nums, totalDataPoints * 0.25f, 0);
+    PS_BIN_FOR_VALUE (binHi25, cumulative->nums, totalDataPoints * 0.75f, 0);
+    psTrace(TRACE, 6, "The 25-percent and 75-precent data point bins are (%ld, %ld).\n", binLo25, binHi25);
+
+    // ADD step 8: Interpolate to find these two positions exactly: these are the upper and lower quartile
+    // positions.
+    psF32 binLo25F32 = fitQuadraticSearchForYThenReturnXusingValues(cumulative->bounds, cumulative->nums, binLo25, totalDataPoints * 0.25f);
+    psF32 binHi25F32 = fitQuadraticSearchForYThenReturnXusingValues(cumulative->bounds, cumulative->nums, binHi25, totalDataPoints * 0.75f);
+    if (isnan(binLo25F32) || isnan(binHi25F32)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "could not determine the robustUQ: fitQuadraticSearchForYThenReturnX() returned a NAN.\n");
+        goto escape;
+    }
+
+    stats->robustLQ = binLo25F32;
+    stats->robustUQ = binHi25F32;
+    psTrace(TRACE, 6, "The 25 and 75 percent data point exact positions are (%f, %f).\n",
+            binLo25F32, binHi25F32);
+    long N50 = 0;
+    for (long i = 0 ; i < myVector->n ; i++) {
+        if (!mask->data.U8[i] &&
+            (binLo25F32 <= myVector->data.F32[i]) && (binHi25F32 >= myVector->data.F32[i])) {
+            N50++;
+        }
+    }
+    stats->robustN50 = N50;
+    psTrace(TRACE, 6, "The robustN50 is %ld.\n", N50);
+
+    // Clean up
+    psFree(histogram);
+    psFree(cumulative);
+    psFree(statsMinMax);
+    psFree(mask);
+
+    stats->results |= PS_STAT_ROBUST_MEDIAN;
+    stats->results |= PS_STAT_ROBUST_STDEV;
+    stats->results |= PS_STAT_ROBUST_QUARTILE;
+
+    psTrace(TRACE, 4, "---- %s(0) end  ----\n", __func__);
+    return true;
+
+escape:
+    stats->robustMedian = NAN;
+    stats->robustStdev = NAN;
+    stats->robustUQ = NAN;
+    stats->robustLQ = NAN;
+    stats->robustN50 = 0;
+    stats->results |= PS_STAT_ROBUST_MEDIAN;
+    stats->results |= PS_STAT_ROBUST_STDEV;
+    stats->results |= PS_STAT_ROBUST_QUARTILE;
+
+    psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+
+    psFree(histogram);
+    psFree(cumulative);
+    psFree(statsMinMax);
+    psFree(mask);
+
+    return false;
+}
+
+/*
+ * vectorFittedStats requires guess for fittedMean and fittedStdev
+ * robustN50 should also be set
+ */
+static bool vectorFittedStats (const psVector* myVector,
+                               const psVector* errors,
+                               psVector* mask,
+                               psMaskType maskVal,
+                               psStats* stats)
+{
+
+    // This procedure requires the mean.  If it has not been already
+    // calculated, then call vectorSampleMean()
+    if (!(stats->results & PS_STAT_ROBUST_MEDIAN)) {
+        vectorRobustStats(myVector, errors, mask, maskVal, stats);
+    }
+
+    // If the mean is NAN, then generate a warning and set the stdev to NAN.
+    if (isnan(stats->robustMedian)) {
+        stats->fittedStdev = NAN;
+        stats->fittedStdev = NAN;
+        psTrace(TRACE, 4, "---- %s() end ----\n", __func__);
+        return false;
+    }
+
+    float guessStdev = stats->robustStdev;  // pass the guess sigma
+    float guessMean = stats->robustMedian;  // pass the guess mean
+
+    psTrace(TRACE, 6, "The guess mean  is %f.\n", guessMean);
+    psTrace(TRACE, 6, "The guess stdev is %f.\n", guessStdev);
+
+    bool done = false;
+    for (int iteration = 0; !done && (iteration < 2); iteration ++) {
+        psStats *statsMinMax = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
+
+        psF32 binSize = 1;
+        if (stats->options & PS_STAT_USE_BINSIZE) {
+            // Set initial bin size to the specified value.
+            binSize = stats->binsize;
+            psTrace(TRACE, 6, "Setting initial robust bin size to %.2f\n", binSize);
+        } else {
+            // construct a histogram with (sigma/10 < binsize < sigma)
+            // set roughly so that the lowest bins have about 2 cnts
+            // Nsmallest ~ N50 / (4*dN))
+            psF32 dN = PS_MAX (1, PS_MIN (10, stats->robustN50 / 8));
+            binSize = guessStdev / dN;
+        }
+
+        // Determine the min/max of the vector (which prior outliers masked out)
+        int numValid = vectorMinMax(myVector, mask, maskVal, statsMinMax); // Number of values
+        float min = statsMinMax->min;
+        float max = statsMinMax->max;
+        if (numValid == 0 || isnan(min) || isnan(max)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
+            psFree(statsMinMax);
+            psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+            return false;
+        }
+
+        // Calculate the number of bins.
+        // XXX can we calculate the binMin, binMax **before** building this histogram?
+        long numBins = (max - min) / binSize;
+        psTrace(TRACE, 6, "The new min/max values are (%f, %f).\n", min, max);
+        psTrace(TRACE, 6, "The new bin size is %f.\n", binSize);
+        psTrace(TRACE, 6, "The numBins is %ld\n", numBins);
+
+        psHistogram *histogram = psHistogramAlloc(min, max, numBins); // A new histogram (without outliers)
+        if (!psVectorHistogram(histogram, myVector, errors, mask, maskVal)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate histogram for fitted statistics.\n");
+            psFree(histogram);
+            psFree(statsMinMax);
+            return false;
+        }
+        if (psTraceGetLevel("psLib.math") >= 8) {
+            PS_VECTOR_PRINT_F32(histogram->nums);
+        }
+
+        // Fit a Gaussian to the bins in the requested range about the guess mean
+        // min,max overrides clipSigma
+        psF32 maxFitSigma = 2.0;
+        if (isfinite(stats->clipSigma)) {
+            maxFitSigma = fabs(stats->clipSigma);
+        }
+        if (isfinite(stats->max)) {
+            maxFitSigma = fabs(stats->max);
+        }
+
+        psF32 minFitSigma = 2.0;
+        if (isfinite(stats->clipSigma)) {
+            minFitSigma = fabs(stats->clipSigma);
+        }
+        if (isfinite(stats->min)) {
+            minFitSigma = fabs(stats->min);
+        }
+
+        // select the min and max bins, saturating on the lower and upper end-points
+        long binMin, binMax;
+        PS_BIN_FOR_VALUE (binMin, histogram->bounds, guessMean - minFitSigma*guessStdev, 0);
+        PS_BIN_FOR_VALUE (binMax, histogram->bounds, guessMean + maxFitSigma*guessStdev, 0);
+
+        // Generate the variables that will be used in the Gaussian fitting
+        // XXX EAM : we should test / guarantee that there are at least 3 (right?) bins
+        psVector *y = psVectorAlloc((1 + (binMax - binMin)), PS_TYPE_F32); // Vector of coordinates
+        psArray *x = psArrayAlloc((1 + (binMax - binMin))); // Array of ordinates
+        for (long i = binMin, j = 0; i <= binMax ; i++, j++) {
+            y->data.F32[j] = histogram->nums->data.F32[i];
+            psVector *ordinate = psVectorAlloc(1, PS_TYPE_F32); // The ordinate value
+            ordinate->data.F32[0] = PS_BIN_MIDPOINT(histogram, i);
+            x->data[j] = ordinate;
+        }
+        if (psTraceGetLevel("psLib.math") >= 8) {
+            // XXX: Print the x array somehow.
+            PS_VECTOR_PRINT_F32(y);
+        }
+        psTrace(TRACE, 6, "The clipped numBins is %ld\n", y->n);
+        psTrace(TRACE, 6, "The clipped min is %f (%ld)\n", PS_BIN_MIDPOINT(histogram, binMin), binMin);
+        psTrace(TRACE, 6, "The clipped max is %f (%ld)\n", PS_BIN_MIDPOINT(histogram, binMax), binMax);
+
+        // Normalize y to [0.0:1.0] (since the psMinimizeLMChi2Gauss1D() functions is [0.0:1.0])
+        // XXX EAM : why not just fit the normalization as well??
+        p_psNormalizeVectorRange(y, 0.0, 1.0);
+
+        // Fit a Gaussian to the data.
+        psMinimization *minimizer = psMinimizationAlloc(100, 0.01); // The minimizer information
+        psVector *params = psVectorAlloc(2, PS_TYPE_F32); // Parameters for the Gaussian
+        // Initial guess for the mean (index 0) and var (index 1).
+        params->data.F32[0] = guessMean;
+        params->data.F32[1] = PS_SQR(guessStdev);
+        if (psTraceGetLevel("psLib.math") >= 8) {
+            PS_VECTOR_PRINT_F32(params);
+            PS_VECTOR_PRINT_F32(y);
+        }
+        if (!psMinimizeLMChi2(minimizer, NULL, params, NULL, x, y, NULL, minimizeLMChi2Gauss1D)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
+            psFree(params);
+            psFree(minimizer);
+            psFree(x);
+            psFree(y);
+            psFree(histogram);
+            psFree(statsMinMax);
+            psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+            return false;
+        }
+        if (psTraceGetLevel("psLib.math") >= 8) {
+            PS_VECTOR_PRINT_F32(params);
+        }
+
+        guessMean = params->data.F32[0];
+        guessStdev = sqrt(params->data.F32[1]);
+        if (guessStdev > 0.75*stats->robustStdev) {
+            done = true;
+        }
+
+        // Clean up after fitting
+        psFree (params);
+        psFree (minimizer);
+        psFree (x);
+        psFree (y);
+        psFree (histogram);
+        psFree (statsMinMax);
+    }
+
+    // The fitted mean is the Gaussian mean.
+    stats->fittedMean = guessMean;
+    psTrace(TRACE, 6, "The fitted mean is %f.\n", stats->fittedMean);
+
+    // The fitted standard deviation
+    stats->fittedStdev = guessStdev;
+    psTrace(TRACE, 6, "The fitted stdev is %f.\n", stats->fittedStdev);
+
+    stats->results |= PS_STAT_FITTED_MEAN;
+    stats->results |= PS_STAT_FITTED_STDEV;
+
+    return true;
+}
+
+
+/********************
+ * vectorFittedStats_v2 requires guess for fittedMean and fittedStdev
+ * robustN50 should also be set
+ * gaussian fit is performed using 2D polynomial to ln(y)
+ ********************/
+static bool vectorFittedStats_v2 (const psVector* myVector,
+                                  const psVector* errors,
+                                  psVector* mask,
+                                  psMaskType maskVal,
+                                  psStats* stats)
+{
+
+    // This procedure requires the mean.  If it has not been already
+    // calculated, then call vectorSampleMean()
+    if (!(stats->results & PS_STAT_ROBUST_MEDIAN)) {
+        vectorRobustStats(myVector, errors, mask, maskVal, stats);
+    }
+
+    // If the mean is NAN, then generate a warning and set the stdev to NAN.
+    if (isnan(stats->robustMedian)) {
+        stats->fittedStdev = NAN;
+        stats->fittedStdev = NAN;
+        psTrace(TRACE, 4, "---- %s() end ----\n", __func__);
+        return false;
+    }
+
+    float guessStdev = stats->robustStdev;  // pass the guess sigma
+    float guessMean = stats->robustMedian;  // pass the guess mean
+
+    psTrace(TRACE, 6, "The ** starting ** guess mean  is %f.\n", guessMean);
+    psTrace(TRACE, 6, "The ** starting ** guess stdev is %f.\n", guessStdev);
+
+    bool done = false;
+    for (int iteration = 0; !done && (iteration < 2); iteration ++) {
+        psStats *statsMinMax = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
+
+        psF32 binSize = 1;
+        if (stats->options & PS_STAT_USE_BINSIZE) {
+            // Set initial bin size to the specified value.
+            binSize = stats->binsize;
+            psTrace(TRACE, 6, "Setting initial robust bin size to %.2f\n", binSize);
+        } else {
+            // construct a histogram with (sigma/10 < binsize < sigma)
+            // set roughly so that the lowest bins have about 2 cnts
+            // Nsmallest ~ N50 / (4*dN))
+            psF32 dN = PS_MAX (1, PS_MIN (10, stats->robustN50 / 8));
+            binSize = guessStdev / dN;
+        }
+
+        // Determine the min/max of the vector (which prior outliers masked out)
+        int numValid = vectorMinMax(myVector, mask, maskVal, statsMinMax); // Number of values
+        float min = statsMinMax->min;
+        float max = statsMinMax->max;
+        if (numValid == 0 || isnan(min) || isnan(max)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
+            psFree(statsMinMax);
+            psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+            return false;
+        }
+
+        // Calculate the number of bins.
+        // XXX can we calculate the binMin, binMax **before** building this histogram?
+        long numBins = (max - min) / binSize;
+        psTrace(TRACE, 6, "The new min/max values are (%f, %f).\n", min, max);
+        psTrace(TRACE, 6, "The new bin size is %f.\n", binSize);
+        psTrace(TRACE, 6, "The numBins is %ld\n", numBins);
+
+        psHistogram *histogram = psHistogramAlloc(min, max, numBins); // A new histogram (without outliers)
+        if (!psVectorHistogram(histogram, myVector, errors, mask, maskVal)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate histogram for fitted statistcs v2.\n");
+            psFree(histogram);
+            psFree(statsMinMax);
+            return false;
+        }
+        if (psTraceGetLevel("psLib.math") >= 8) {
+            PS_VECTOR_PRINT_F32(histogram->nums);
+        }
+
+        // Fit a Gaussian to the bins in the requested range about the guess mean
+        // min,max overrides clipSigma
+        psF32 maxFitSigma = 2.0;
+        if (isfinite(stats->clipSigma)) {
+            maxFitSigma = fabs(stats->clipSigma);
+        }
+        if (isfinite(stats->max)) {
+            maxFitSigma = fabs(stats->max);
+        }
+
+        psF32 minFitSigma = 2.0;
+        if (isfinite(stats->clipSigma)) {
+            minFitSigma = fabs(stats->clipSigma);
+        }
+        if (isfinite(stats->min)) {
+            minFitSigma = fabs(stats->min);
+        }
+
+        // select the min and max bins, saturating on the lower and upper end-points
+        long binMin, binMax;
+        PS_BIN_FOR_VALUE (binMin, histogram->bounds, guessMean - minFitSigma*guessStdev, 0);
+        PS_BIN_FOR_VALUE (binMax, histogram->bounds, guessMean + maxFitSigma*guessStdev, 0);
+
+        // Generate the variables that will be used in the Gaussian fitting
+        // XXX EAM : we should test / guarantee that there are at least 3 (right?) bins
+        psVector *y = psVectorAllocEmpty((1 + (binMax - binMin)), PS_TYPE_F32); // Vector of coordinates
+        psVector *x = psVectorAllocEmpty((1 + (binMax - binMin)), PS_TYPE_F32); // Vector of ordinates
+        long j = 0;
+        for (long i = binMin; i <= binMax ; i++) {
+            if (histogram->nums->data.F32[i] <= 0.0)
+                continue;
+            x->data.F32[j] = PS_BIN_MIDPOINT(histogram, i);
+            y->data.F32[j] = log(histogram->nums->data.F32[i]);
+            // XXX note this is the natural log: expected distribution is A exp(-(x-xo)^2/2sigma^2)
+            j++;
+        }
+        y->n = x->n = j;
+        if (psTraceGetLevel("psLib.math") >= 8) {
+            // XXX: Print the x array somehow.
+            PS_VECTOR_PRINT_F32(y);
+        }
+        psTrace(TRACE, 6, "The clipped numBins is %ld\n", y->n);
+        psTrace(TRACE, 6, "The clipped min is %f (%ld)\n", PS_BIN_MIDPOINT(histogram, binMin), binMin);
+        psTrace(TRACE, 6, "The clipped max is %f (%ld)\n", PS_BIN_MIDPOINT(histogram, binMax), binMax);
+
+        // fit 2nd order polynomial to ln(y) = -(x-xo)^2/2sigma^2
+        psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+
+        // XXX how can we protect against some extreme outliers?  the robust histogram
+        // should have already dealt with those, but we are again sensitive to them...
+        // psStats *fitStats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+        // fitStats->clipIter = 3.0;
+        // fitStats->clipSigma = 3.0;
+        // psVector *fitMask = psVectorAlloc(y->n, PS_TYPE_U8);
+        // psVectorInit (fitMask, 0);
+
+        if (!psVectorFitPolynomial1D (poly, NULL, 0, y, NULL, x)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
+            psFree(x);
+            psFree(y);
+            psFree(poly);
+            psFree(histogram);
+            psFree(statsMinMax);
+            psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+            return false;
+        }
+
+        if (poly->coeff[2] >= 0.0) {
+            psTrace(TRACE, 6, "Parabolic fit results: %f + %f x + %f x^2\n",
+                    poly->coeff[0], poly->coeff[1], poly->coeff[2]);
+            psError(PS_ERR_UNKNOWN, false, "fit did not converge\n");
+            psFree(x);
+            psFree(y);
+            psFree(poly);
+            psFree(histogram);
+            psFree(statsMinMax);
+            psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+            return false;
+        }
+
+        guessStdev = sqrt(-0.5/poly->coeff[2]);
+        guessMean = poly->coeff[1]*PS_SQR(guessStdev);
+        if (guessStdev > 0.75*stats->robustStdev) {
+            done = true;
+        } else {
+            psTrace(TRACE, 6, "Parabolic fit results: %f + %f x + %f x^2\n",
+                    poly->coeff[0], poly->coeff[1], poly->coeff[2]);
+            psTrace(TRACE, 6, "The new guess mean  is %f.\n", guessMean);
+            psTrace(TRACE, 6, "The new guess stdev is %f.\n", guessStdev);
+        }
+
+        // Clean up after fitting
+        psFree (x);
+        psFree (y);
+        psFree (poly);
+        // psFree (fitStats);
+        // psFree (fitMask);
+        psFree (histogram);
+        psFree (statsMinMax);
+    }
+
+    // The fitted mean is the Gaussian mean.
+    stats->fittedMean = guessMean;
+    psTrace(TRACE, 6, "The fitted mean is %f.\n", stats->fittedMean);
+
+    // The fitted standard deviation
+    stats->fittedStdev = guessStdev;
+    psTrace(TRACE, 6, "The fitted stdev is %f.\n", stats->fittedStdev);
+
+    stats->results |= PS_STAT_FITTED_MEAN_V2;
+    stats->results |= PS_STAT_FITTED_STDEV_V2;
+
+    return true;
+}
+
+/********************
+ * perform an asymmetric fit to the population
+ * vectorFittedStats_v3 requires guess for fittedMean and fittedStdev
+ * robustN50 should also be set
+ * gaussian fit is performed using 2D polynomial to ln(y)
+ ********************/
+static bool vectorFittedStats_v3 (const psVector* myVector,
+                                  const psVector* errors,
+                                  psVector* mask,
+                                  psMaskType maskVal,
+                                  psStats* stats)
+{
+
+    // This procedure requires the mean.  If it has not been already
+    // calculated, then call vectorSampleMean()
+    if (!(stats->results & PS_STAT_ROBUST_MEDIAN)) {
+        vectorRobustStats(myVector, errors, mask, maskVal, stats);
+    }
+
+    // If the mean is NAN, then generate a warning and set the stdev to NAN.
+    if (isnan(stats->robustMedian)) {
+        stats->fittedStdev = NAN;
+        stats->fittedStdev = NAN;
+        psTrace(TRACE, 4, "---- %s() end ----\n", __func__);
+        return false;
+    }
+
+    float guessStdev = stats->robustStdev;  // pass the guess sigma
+    float guessMean = stats->robustMedian;  // pass the guess mean
+
+    psTrace(TRACE, 6, "The ** starting ** guess mean  is %f.\n", guessMean);
+    psTrace(TRACE, 6, "The ** starting ** guess stdev is %f.\n", guessStdev);
+
+    bool done = false;
+    for (int iteration = 0; !done && (iteration < 2); iteration ++) {
+        psStats *statsMinMax = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
+
+        psF32 binSize = 1;
+        if (stats->options & PS_STAT_USE_BINSIZE) {
+            // Set initial bin size to the specified value.
+            binSize = stats->binsize;
+            psTrace(TRACE, 6, "Setting initial robust bin size to %.2f\n", binSize);
+        } else {
+            // construct a histogram with (sigma/2 < binsize < sigma)
+            // set roughly so that the lowest bins have about 2 cnts
+            // Nsmallest ~ N50 / (4*dN))
+            psF32 dN = PS_MAX (1, PS_MIN (4, stats->robustN50 / 8));
+            binSize = guessStdev / dN;
+        }
+
+        // Determine the min/max of the vector (which prior outliers masked out)
+        int numValid = vectorMinMax(myVector, mask, maskVal, statsMinMax); // Number of values
+        float min = statsMinMax->min;
+        float max = statsMinMax->max;
+        if (numValid == 0 || isnan(min) || isnan(max)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
+            psFree(statsMinMax);
+            psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+            return false;
+        }
+
+        // Calculate the number of bins.
+        // XXX can we calculate the binMin, binMax **before** building this histogram?
+        long numBins = (max - min) / binSize;
+        psTrace(TRACE, 6, "The new min/max values are (%f, %f).\n", min, max);
+        psTrace(TRACE, 6, "The new bin size is %f.\n", binSize);
+        psTrace(TRACE, 6, "The numBins is %ld\n", numBins);
+
+        psHistogram *histogram = psHistogramAlloc(min, max, numBins); // A new histogram (without outliers)
+        if (!psVectorHistogram(histogram, myVector, errors, mask, maskVal)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate histogram for fitted statistics v3.\n");
+            psFree(histogram);
+            psFree(statsMinMax);
+            return false;
+        }
+        if (psTraceGetLevel("psLib.math") >= 8) {
+            PS_VECTOR_PRINT_F32(histogram->nums);
+        }
+
+        // now fit a Gaussian to the upper and lower halves about the peak independently
+
+        // set the full-range upper and lower limits
+        psF32 maxFitSigma = 2.0;
+        if (isfinite(stats->clipSigma)) {
+            maxFitSigma = fabs(stats->clipSigma);
+        }
+        if (isfinite(stats->max)) {
+            maxFitSigma = fabs(stats->max);
+        }
+
+        psF32 minFitSigma = 2.0;
+        if (isfinite(stats->clipSigma)) {
+            minFitSigma = fabs(stats->clipSigma);
+        }
+        if (isfinite(stats->min)) {
+            minFitSigma = fabs(stats->min);
+        }
+
+        // select the min and max bins, saturating on the lower and upper end-points
+        long binMin, binMax;
+        PS_BIN_FOR_VALUE (binMin, histogram->bounds, guessMean - minFitSigma*guessStdev, 0);
+        PS_BIN_FOR_VALUE (binMax, histogram->bounds, guessMean + maxFitSigma*guessStdev, 0);
+        if (binMin == binMax) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
+            psFree(statsMinMax);
+            psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+            return false;
+        }
+
+        // search for mode (peak of histogram within range mean-2sigma - mean+2sigma
+        long  binPeak = binMin;
+        float valPeak = histogram->nums->data.F32[binPeak];
+        for (int i = binMin; i < binMax; i++) {
+            if (histogram->nums->data.F32[i] > valPeak) {
+                binPeak = i;
+                valPeak = histogram->nums->data.F32[binPeak];
+            }
+            psTrace (TRACE, 6, "(%f = %.0f) ", histogram->bounds->data.F32[i], histogram->nums->data.F32[i]);
+        }
+        psTrace (TRACE, 6, "\n");
+
+        // assume a reasonably well-defined gaussian-like population; run from peak out until val < 0.25*peak
+
+        psTrace(TRACE, 6, "The clipped numBins is %ld\n", binMax - binMin);
+        psTrace(TRACE, 6, "The clipped min is %f (%ld)\n",
+                PS_BIN_MIDPOINT(histogram, binMin), binMin);
+        psTrace(TRACE, 6, "The clipped max is %f (%ld)\n",
+                PS_BIN_MIDPOINT(histogram, binMax - 1), binMax - 1);
+        psTrace(TRACE, 6, "The clipped peak is %f (%ld)\n",
+                PS_BIN_MIDPOINT(histogram, binPeak), binPeak);
+        psTrace(TRACE, 6, "The clipped peak value is %f\n", histogram->nums->data.F32[binPeak]);
+
+        {
+            // fit the lower half of the distribution
+            // run down until we drop below 0.25*valPeak
+            long binS = binMin;
+            long binE = PS_MIN (binPeak + 3, binMax);
+            for (int i = binPeak-3; i >= binMin; i--) {
+                if (histogram->nums->data.F32[i] < 0.25*valPeak) {
+                    binS = i;
+                    break;
+                }
+            }
+            psTrace(TRACE, 6, "Lower bound for lower half: %f (%ld)\n",
+                    PS_BIN_MIDPOINT(histogram, binS), binS);
+            psTrace(TRACE, 6, "Upper bound for lower half: %f (%ld)\n",
+                    PS_BIN_MIDPOINT(histogram, binE), binE);
+
+            psVector *y = psVectorAllocEmpty(binE - binS, PS_TYPE_F32); // Vector of coordinates
+            psVector *x = psVectorAllocEmpty(binE - binS, PS_TYPE_F32); // Vector of ordinates
+            long j = 0;
+            for (long i = binS; i < binE; i++) {
+                if (histogram->nums->data.F32[i] <= 0.0)
+                    continue;
+                x->data.F32[j] = PS_BIN_MIDPOINT(histogram, i);
+                // note this is the natural log: expected distribution is A exp(-(x-xo)^2/2sigma^2)
+                y->data.F32[j] = log(histogram->nums->data.F32[i]);
+                j++;
+            }
+            y->n = x->n = j;
+
+            // fit 2nd order polynomial to ln(y) = -(x-xo)^2/2sigma^2
+            psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+            bool status = psVectorFitPolynomial1D (poly, NULL, 0, y, NULL, x);
+            psFree(x);
+            psFree(y);
+
+            if (!status) {
+                psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
+                psFree(poly);
+                psFree(histogram);
+                psFree(statsMinMax);
+                psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+                return false;
+            }
+
+            if (poly->coeff[2] >= 0.0) {
+                psTrace(TRACE, 6, "Failed parabolic fit: %f + %f x + %f x^2\n",
+                        poly->coeff[0], poly->coeff[1], poly->coeff[2]);
+                psFree(poly);
+                psFree(histogram);
+                psFree(statsMinMax);
+
+                // sometimes, the guessStdev is much too large.  in this case, the entire real population
+                // tends to be found in a single bin.  make one attempt to recover by dropping the guessStdev
+                // down by a jump and trying again
+                if (iteration == 0) {
+                    guessStdev = 0.25*guessStdev;
+                    psTrace(TRACE, 6, "*** retry, new stdev is %f.\n", guessStdev);
+                    continue;
+                }
+
+                psError(PS_ERR_UNKNOWN, false, "fit did not converge\n");
+                psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+                return false;
+            }
+
+            // calculate lower mean & stdev from parabolic fit -- use this as the result
+            guessStdev = sqrt(-0.5/poly->coeff[2]);
+            guessMean = poly->coeff[1]*PS_SQR(guessStdev);
+            if (guessStdev > 0.75*stats->robustStdev) {
+                done = true;
+            }
+            psTrace(TRACE, 6, "Parabolic Lower fit results: %f + %f x + %f x^2\n",
+                    poly->coeff[0], poly->coeff[1], poly->coeff[2]);
+            psTrace(TRACE, 6, "The lower mean  is %f.\n", guessMean);
+            psTrace(TRACE, 6, "The lower stdev is %f.\n", guessStdev);
+
+            psFree(poly);
+        }
+
+        // for test, measure the same result for the upper section
+        {
+            // fit the upper half of the distribution
+            // run up until we drop below 0.25*valPeak
+            long binS = PS_MAX (binPeak - 3, 0);
+            long binE = binMax;
+            for (int i = binPeak+3; i < binMax; i++) {
+                if (histogram->nums->data.F32[i] < 0.25*valPeak) {
+                    binE = i;
+                    break;
+                }
+            }
+            psTrace(TRACE, 6, "Lower bound for upper half: %f (%ld)\n",
+                    PS_BIN_MIDPOINT(histogram, binS), binS);
+            psTrace(TRACE, 6, "Upper bound for upper half: %f (%ld)\n",
+                    PS_BIN_MIDPOINT(histogram, binE), binE);
+
+            psVector *y = psVectorAllocEmpty(binE - binS, PS_TYPE_F32); // Vector of coordinates
+            psVector *x = psVectorAllocEmpty(binE - binS, PS_TYPE_F32); // Vector of ordinates
+            long j = 0;
+            for (long i = binS; i < binE; i++) {
+                if (histogram->nums->data.F32[i] <= 0.0)
+                    continue;
+                x->data.F32[j] = PS_BIN_MIDPOINT(histogram, i);
+                // note this is the natural log: expected distribution is A exp(-(x-xo)^2/2sigma^2)
+                y->data.F32[j] = log(histogram->nums->data.F32[i]);
+                j++;
+            }
+            y->n = x->n = j;
+
+            // fit 2nd order polynomial to ln(y) = -(x-xo)^2/2sigma^2
+            psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+            bool status = psVectorFitPolynomial1D (poly, NULL, 0, y, NULL, x);
+            psFree(x);
+            psFree(y);
+
+            if (!status) {
+                psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
+                psFree(poly);
+                psFree(histogram);
+                psFree(statsMinMax);
+                psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+                return false;
+            }
+
+            // calculate upper mean & stdev from parabolic fit -- ignore this value
+            float upperStdev = sqrt(-0.5/poly->coeff[2]);
+            float upperMean = poly->coeff[1]*PS_SQR(upperStdev);
+#ifndef PS_NO_TRACE
+            psTrace(TRACE, 6, "Parabolic Upper fit results: %f + %f x + %f x^2\n",
+                    poly->coeff[0], poly->coeff[1], poly->coeff[2]);
+            psTrace(TRACE, 6, "The upper mean  is %f.\n", upperMean);
+            psTrace(TRACE, 6, "The upper stdev is %f.\n", upperStdev);
+#endif
+
+            // if the resulting value is outside of the range binMin - binMax, use the upper value
+            if (done && (guessMean > PS_BIN_MIDPOINT(histogram, binMax - 1))) {
+                guessMean = upperMean;
+                guessStdev = upperStdev;
+            }
+
+            psFree (poly);
+        }
+
+        // Clean up after fitting
+        psFree (histogram);
+        psFree (statsMinMax);
+    }
+
+    // The fitted mean is the Gaussian mean.
+    stats->fittedMean = guessMean;
+    psTrace(TRACE, 6, "The fitted mean is %f.\n", stats->fittedMean);
+
+    // The fitted standard deviation
+    stats->fittedStdev = guessStdev;
+    psTrace(TRACE, 6, "The fitted stdev is %f.\n", stats->fittedStdev);
+
+    stats->results |= PS_STAT_FITTED_MEAN_V3;
+    stats->results |= PS_STAT_FITTED_STDEV_V3;
+
+    return true;
+}
+
+/********************
+ * perform an asymmetric fit to the population
+ * vectorFittedStats_v4 requires guess for fittedMean and fittedStdev
+ * robustN50 should also be set
+ * gaussian fit is performed using 2D polynomial to ln(y)
+ * this version follows the upper portion of the distribution until it passes 0.5*peak
+ ********************/
+static bool vectorFittedStats_v4 (const psVector* myVector,
+                                  const psVector* errors,
+                                  psVector* mask,
+                                  psMaskType maskVal,
+                                  psStats* stats)
+{
+
+    // This procedure requires the mean.  If it has not been already
+    // calculated, then call vectorSampleMean()
+    if (!(stats->results & PS_STAT_ROBUST_MEDIAN)) {
+        vectorRobustStats(myVector, errors, mask, maskVal, stats);
+    }
+
+    // If the mean is NAN, then generate a warning and set the stdev to NAN.
+    if (isnan(stats->robustMedian)) {
+        stats->fittedStdev = NAN;
+        stats->fittedStdev = NAN;
+        psTrace(TRACE, 4, "---- %s() end ----\n", __func__);
+        return false;
+    }
+
+    float guessStdev = stats->robustStdev;  // pass the guess sigma
+    float guessMean = stats->robustMedian;  // pass the guess mean
+
+    psTrace(TRACE, 6, "The ** starting ** guess mean  is %f.\n", guessMean);
+    psTrace(TRACE, 6, "The ** starting ** guess stdev is %f.\n", guessStdev);
+
+    bool done = false;
+    for (int iteration = 0; !done && (iteration < 2); iteration ++) {
+        psStats *statsMinMax = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
+
+        psF32 binSize = 1;
+        if (stats->options & PS_STAT_USE_BINSIZE) {
+            // Set initial bin size to the specified value.
+            binSize = stats->binsize;
+            psTrace(TRACE, 6, "Setting initial robust bin size to %.2f\n", binSize);
+        } else {
+            // construct a histogram with (sigma/2 < binsize < sigma)
+            // set roughly so that the lowest bins have about 2 cnts
+            // Nsmallest ~ N50 / (4*dN))
+            psF32 dN = PS_MAX (1, PS_MIN (4, stats->robustN50 / 8));
+            binSize = guessStdev / dN;
+        }
+
+        // Determine the min/max of the vector (which prior outliers masked out)
+        int numValid = vectorMinMax(myVector, mask, maskVal, statsMinMax); // Number of values
+        float min = statsMinMax->min;
+        float max = statsMinMax->max;
+        if (numValid == 0 || isnan(min) || isnan(max)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
+            psFree(statsMinMax);
+            psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+            return false;
+        }
+
+        // Calculate the number of bins.
+        // XXX can we calculate the binMin, binMax **before** building this histogram?
+        // if the range is too absurd, adjust numBins & binSize
+        long numBins = PS_MAX (50, PS_MIN (10000, (max - min) / binSize));
+        binSize = (max - min) / (float) numBins;
+        psTrace(TRACE, 6, "The new min/max values are (%f, %f).\n", min, max);
+        psTrace(TRACE, 6, "The new bin size is %f.\n", binSize);
+        psTrace(TRACE, 6, "The numBins is %ld\n", numBins);
+
+        psHistogram *histogram = psHistogramAlloc(min, max, numBins); // A new histogram (without outliers)
+        if (!psVectorHistogram(histogram, myVector, errors, mask, maskVal)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate histogram for fitted statistics v4.\n");
+            psFree(histogram);
+            psFree(statsMinMax);
+            return false;
+        }
+        if (psTraceGetLevel("psLib.math") >= 8) {
+            PS_VECTOR_PRINT_F32(histogram->nums);
+        }
+
+        // now fit a Gaussian to the upper and lower halves about the peak independently
+
+        // set the full-range upper and lower limits
+        psF32 maxFitSigma = 2.0;
+        if (isfinite(stats->clipSigma)) {
+            maxFitSigma = fabs(stats->clipSigma);
+        }
+        if (isfinite(stats->max)) {
+            maxFitSigma = fabs(stats->max);
+        }
+
+        psF32 minFitSigma = 2.0;
+        if (isfinite(stats->clipSigma)) {
+            minFitSigma = fabs(stats->clipSigma);
+        }
+        if (isfinite(stats->min)) {
+            minFitSigma = fabs(stats->min);
+        }
+
+        // select the min and max bins, saturating on the lower and upper end-points
+        long binMin, binMax;
+        PS_BIN_FOR_VALUE (binMin, histogram->bounds, guessMean - minFitSigma*guessStdev, 0);
+        PS_BIN_FOR_VALUE (binMax, histogram->bounds, guessMean + maxFitSigma*guessStdev, 0);
+        if (binMin == binMax) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
+            psFree(statsMinMax);
+            psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+            return false;
+        }
+
+        // search for mode (peak of histogram within range mean-2sigma - mean+2sigma
+        long  binPeak = binMin;
+        float valPeak = histogram->nums->data.F32[binPeak];
+        for (int i = binMin; i < binMax; i++) {
+            if (histogram->nums->data.F32[i] > valPeak) {
+                binPeak = i;
+                valPeak = histogram->nums->data.F32[binPeak];
+            }
+            psTrace (TRACE, 6, "(%f = %.0f) ", histogram->bounds->data.F32[i], histogram->nums->data.F32[i]);
+        }
+        psTrace (TRACE, 6, "\n");
+
+        // assume a reasonably well-defined gaussian-like population; run from peak out until val < 0.25*peak
+
+        psTrace(TRACE, 6, "The clipped numBins is %ld\n", binMax - binMin);
+        psTrace(TRACE, 6, "The clipped min is %f (%ld)\n", PS_BIN_MIDPOINT(histogram, binMin), binMin);
+        psTrace(TRACE, 6, "The clipped max is %f (%ld)\n",
+                PS_BIN_MIDPOINT(histogram, binMax - 1), binMax - 1);
+        psTrace(TRACE, 6, "The clipped peak is %f (%ld)\n", PS_BIN_MIDPOINT(histogram, binPeak), binPeak);
+        psTrace(TRACE, 6, "The clipped peak value is %f\n", histogram->nums->data.F32[binPeak]);
+
+        {
+            // fit the lower half of the distribution
+            // run down until we drop below 0.25*valPeak
+            // run up until we drop below 0.50*valPeak
+            long binS = binMin;
+            long binE = PS_MIN (binPeak + 3, binMax);
+            for (int i = binPeak - 3; i >= binMin; i--) {
+                if (histogram->nums->data.F32[i] < 0.25*valPeak) {
+                    binS = i;
+                    break;
+                }
+            }
+            for (int i = binPeak + 3; i < binMax; i++) {
+                if (histogram->nums->data.F32[i] < 0.50*valPeak) {
+                    binE = i;
+                    break;
+                }
+            }
+            psTrace(TRACE, 6, "Lower bound for lower half: %f (%ld)\n",
+                    PS_BIN_MIDPOINT(histogram, binS), binS);
+            psTrace(TRACE, 6, "Upper bound for lower half: %f (%ld)\n",
+                    PS_BIN_MIDPOINT(histogram, binE), binE);
+
+            psVector *y = psVectorAllocEmpty(binE - binS, PS_TYPE_F32); // Vector of coordinates
+            psVector *x = psVectorAllocEmpty(binE - binS, PS_TYPE_F32); // Vector of ordinates
+            long j = 0;
+            for (long i = binS; i < binE; i++) {
+                if (histogram->nums->data.F32[i] <= 0.0)
+                    continue;
+                x->data.F32[j] = PS_BIN_MIDPOINT(histogram, i);
+                // note this is the natural log: expected distribution is A exp(-(x-xo)^2/2sigma^2)
+                y->data.F32[j] = log(histogram->nums->data.F32[i]);
+                j++;
+            }
+            y->n = x->n = j;
+
+            // fit 2nd order polynomial to ln(y) = -(x-xo)^2/2sigma^2
+            psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+            bool status = psVectorFitPolynomial1D (poly, NULL, 0, y, NULL, x);
+            psFree(x);
+            psFree(y);
+
+            if (!status) {
+                psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
+                psFree(poly);
+                psFree(histogram);
+                psFree(statsMinMax);
+                psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+                return false;
+            }
+
+            if (poly->coeff[2] >= 0.0) {
+                psTrace(TRACE, 6, "Failed parabolic fit: %f + %f x + %f x^2\n",
+                        poly->coeff[0], poly->coeff[1], poly->coeff[2]);
+                psFree(poly);
+                psFree(histogram);
+                psFree(statsMinMax);
+
+                // sometimes, the guessStdev is much too large.  in this case, the entire real population
+                // tends to be found in a single bin.  make one attempt to recover by dropping the guessStdev
+                // down by a jump and trying again
+                if (iteration == 0) {
+                    guessStdev = 0.25*guessStdev;
+                    psTrace(TRACE, 6, "*** retry, new stdev is %f.\n", guessStdev);
+                    continue;
+                }
+
+                psError(PS_ERR_UNKNOWN, false, "fit did not converge\n");
+                psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+                return false;
+            }
+
+            // calculate lower mean & stdev from parabolic fit -- use this as the result
+            guessStdev = sqrt(-0.5/poly->coeff[2]);
+            guessMean = poly->coeff[1]*PS_SQR(guessStdev);
+            if (guessStdev > 0.75*stats->robustStdev) {
+                done = true;
+            }
+            psTrace(TRACE, 6, "Parabolic Lower fit results: %f + %f x + %f x^2\n",
+                    poly->coeff[0], poly->coeff[1], poly->coeff[2]);
+            psTrace(TRACE, 6, "The lower mean  is %f.\n", guessMean);
+            psTrace(TRACE, 6, "The lower stdev is %f.\n", guessStdev);
+
+            psFree(poly);
+        }
+
+        // if we converge on a solution outside the range binMin - binMax, use a more conservative range
+        float minValue = PS_BIN_MIDPOINT(histogram, binMin);
+        float maxValue = PS_BIN_MIDPOINT(histogram, binMax - 1);
+
+        if (done && ((guessMean < minValue) || (guessMean > maxValue))) {
+            psTrace(TRACE, 6, "Inconsistent result, re-trying the fit\n");
+
+            // fit a symmetric distribution
+            // run up until we drop below 0.15*valPeak
+            // run up until we drop below 0.15*valPeak
+            long binS = binMin;
+            long binE = binMax;
+            for (int i = binPeak - 3; i >= binMin; i--) {
+                if (histogram->nums->data.F32[i] < 0.15*valPeak) {
+                    binS = i;
+                    break;
+                }
+            }
+            for (int i = binPeak + 3; i < binMax; i++) {
+                if (histogram->nums->data.F32[i] < 0.15*valPeak) {
+                    binE = i;
+                    break;
+                }
+            }
+            psTrace(TRACE, 6, "Lower bound for symmetric range: %f (%ld)\n",
+                    PS_BIN_MIDPOINT(histogram, binS), binS);
+            psTrace(TRACE, 6, "Upper bound for symmetric range: %f (%ld)\n",
+                    PS_BIN_MIDPOINT(histogram, binE), binE);
+
+            psVector *y = psVectorAllocEmpty(binE - binS, PS_TYPE_F32); // Vector of coordinates
+            psVector *x = psVectorAllocEmpty(binE - binS, PS_TYPE_F32); // Vector of ordinates
+            long j = 0;
+            for (long i = binS; i < binE; i++) {
+                if (histogram->nums->data.F32[i] <= 0.0)
+                    continue;
+                x->data.F32[j] = PS_BIN_MIDPOINT(histogram, i);
+                // note this is the natural log: expected distribution is A exp(-(x-xo)^2/2sigma^2)
+                y->data.F32[j] = log(histogram->nums->data.F32[i]);
+                j++;
+            }
+            y->n = x->n = j;
+
+            // fit 2nd order polynomial to ln(y) = -(x-xo)^2/2sigma^2
+            psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+            bool status = psVectorFitPolynomial1D (poly, NULL, 0, y, NULL, x);
+            psFree(x);
+            psFree(y);
+
+            if (!status) {
+                psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
+                psFree(poly);
+                psFree(histogram);
+                psFree(statsMinMax);
+                psTrace(TRACE, 4, "---- %s(false) end  ----\n", __func__);
+                return false;
+            }
+
+            // calculate upper mean & stdev from parabolic fit -- ignore this value
+            guessStdev = sqrt(-0.5/poly->coeff[2]);
+            guessMean = poly->coeff[1]*PS_SQR(guessStdev);
+#ifndef PS_NO_TRACE
+            psTrace(TRACE, 6, "Parabolic Symmetric fit results: %f + %f x + %f x^2\n",
+                    poly->coeff[0], poly->coeff[1], poly->coeff[2]);
+            psTrace(TRACE, 6, "The symmetric mean  is %f.\n", guessMean);
+            psTrace(TRACE, 6, "The symmetric stdev is %f.\n", guessStdev);
+#endif
+
+            // if we converge on a solution outside the range binMin - binMax, use a more conservative range
+            float minValueSym = PS_BIN_MIDPOINT(histogram, binS);
+            float maxValueSym = PS_BIN_MIDPOINT(histogram, binE - 1);
+
+            // saturate on min or max value
+            if (guessMean < minValueSym) {
+                guessMean = minValueSym;
+                psTrace(TRACE, 6, "The symmetric mean is out of bounds, saturating to %f.\n", guessMean);
+            }
+
+            // saturate on min or max value
+            if (guessMean > maxValueSym) {
+                guessMean = maxValueSym;
+                psTrace(TRACE, 6, "The symmetric mean is out of bounds, saturating to %f.\n", guessMean);
+            }
+
+            psFree (poly);
+        }
+
+        // Clean up after fitting
+        psFree (histogram);
+        psFree (statsMinMax);
+    }
+
+    // The fitted mean is the Gaussian mean.
+    stats->fittedMean = guessMean;
+    psTrace(TRACE, 6, "The fitted mean is %f.\n", stats->fittedMean);
+
+    // The fitted standard deviation
+    stats->fittedStdev = guessStdev;
+    psTrace(TRACE, 6, "The fitted stdev is %f.\n", stats->fittedStdev);
+
+    stats->results |= PS_STAT_FITTED_MEAN_V4;
+    stats->results |= PS_STAT_FITTED_STDEV_V4;
+
+    return true;
+}
+
+
+/******************************************************************************
+vectorSmoothHistGaussian(): This routine smoothes the data in the input
+robustHistogram with a Gaussian of width sigma.  It returns a psVector of the
+smoothed data.
+
+XXX this function is unused
+*****************************************************************************/
+psVector *vectorSmoothHistGaussian(psHistogram *histogram,
+                                   psF32 sigma)
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+    psTrace(TRACE, 5, "(histogram->nums->n, sigma) is (%d, %.2f\n", (int) histogram->nums->n, sigma);
+    PS_ASSERT_PTR_NON_NULL(histogram, NULL);
+    PS_ASSERT_PTR_NON_NULL(histogram->bounds, NULL);
+    PS_ASSERT_PTR_NON_NULL(histogram->nums, NULL);
+    if (psTraceGetLevel("psLib.math") >= 8) {
+        PS_VECTOR_PRINT_F32(histogram->nums);
+    }
+
+    long numBins = histogram->nums->n;  // Number of histogram bins
+    psVector *smooth = psVectorAlloc(numBins, PS_TYPE_F32); // Smoothed version of histogram bins
+    const psVector *bounds = histogram->bounds; // The bounds for the histogram bins
+
+    if (!histogram->uniform) {
+        //
+        // We get here if the histogram is non-uniform.  This code is not tested.
+        // However, it is also not used anywhere, yet.
+        //
+        psWarning("WARNING: vectorSmoothHistGaussian() on non-uniform "
+                  "histograms has not been tested or used.\n");
+
+        for (long i = 0; i < numBins; i++) {
+            // Determine the midpoint of bin i.
+            float iMid = PS_BIN_MIDPOINT(histogram, i);
+
+            //
+            // We determine the bin numbers (jMin:jMax) corresponding to a
+            // range of data values surrounding iMid.  The range is of size:
+            // 2*PS_GAUSS_WIDTH*sigma
+            long jMin, jMax;
+            psF32 x = iMid - (PS_GAUSS_WIDTH * sigma);
+            for (jMin = i; jMin > 0 && bounds->data.F32[jMin] > x; jMin--)
+                ;
+            x = iMid + (PS_GAUSS_WIDTH * sigma);
+            for (jMax = i; jMax < bounds->n - 1 && bounds->data.F32[jMax + 1] > x; jMax++)
+                ;
+
+            //
+            // Loop from jMin to jMax, computing the gaussian of data i.
+            //
+            smooth->data.F32[i] = 0.0;
+            for (long j = jMin ; j <= jMax ; j++) {
+                float jMid = PS_BIN_MIDPOINT(histogram, j);
+                smooth->data.F32[i] +=
+                    histogram->nums->data.F32[j] * psGaussian(jMid, iMid, sigma, true);
+            }
+        }
+    } else {
+        //
+        // We get here if the histogram is uniform.
+        //
+        for (long i = 0; i < numBins; i++) {
+            psF32 binSize = bounds->data.F32[1] - bounds->data.F32[0];
+            psS32 gaussWidth = ((PS_GAUSS_WIDTH * sigma) / binSize);
+
+            //
+            // XXX: The following is wrong.  However, in practice, the sigma was too
+            // large compared to the binSize.  This meant that the smoothing was done
+            // over 500 bins in the robust stats algorithm.  This mean that the smoothing
+            // took way too long.
+            //
+#if 0
+
+            gaussWidth = 10;
+#endif
+            //
+            // We determine the bin numbers (jMin:jMax) corresponding to a
+            // range of data values surrounding iMid.  The range is of size:
+            // 2*PS_GAUSS_WIDTH*sigma
+            //
+            psS32 jMin = PS_MAX(i - gaussWidth, 0);
+            psS32 jMax = PS_MIN(i + gaussWidth, bounds->n - 1);
+
+            //
+            // Loop from jMin to jMax, computing the gaussian of data i.
+            //
+            smooth->data.F32[i] = 0.0;
+            float iMid = PS_BIN_MIDPOINT(histogram, i);
+            for (long j = jMin ; j <= jMax ; j++) {
+                float jMid = PS_BIN_MIDPOINT(histogram, j);
+                smooth->data.F32[i] +=
+                    histogram->nums->data.F32[j] * psGaussian(jMid, iMid, sigma, true);
+            }
+        }
+    }
+
+    if (psTraceGetLevel("psLib.math") >= 8) {
+        PS_VECTOR_PRINT_F32(smooth);
+    }
+    psTrace(TRACE, 4, "---- %s() end ----\n", __func__);
+    return(smooth);
+}
+
+/*****************************************************************************/
+
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+
+/*****************************************************************************/
+
+// free function
+static void statsFree(psStats *stats)
+{
+    if (!stats) return;
+    psFree (stats->tmpData);
+    psFree (stats->tmpMask);
+    return;
+}
+
+/******************************************************************************
+    psStatsAlloc(): This routine must create a new psStats data structure.
+*****************************************************************************/
+psStats* p_psStatsAlloc(const char *file, unsigned int lineno, const char *func, psStatsOptions options)
+{
+    psTrace(TRACE, 3,"---- %s() begin  ----\n", __func__);
+
+    psStats *stats = p_psAlloc(file, lineno, func, sizeof(psStats));
+    psMemSetDeallocator(stats, (psFreeFunc)statsFree);
+
+    // set initial, default values
+    psStatsInit (stats);
+
+    // these values are can be set as desired by the user.  they are not affected by
+    // psStatsInit
+    stats->clipSigma = 3.0;
+    stats->clipIter = 3;
+    stats->nSubsample = 100000;
+    stats->options = options;
+    stats->tmpData = NULL;
+    stats->tmpMask = NULL;
+
+    psTrace(TRACE, 3, "---- %s() end  ----\n", __func__);
+    return stats;
+}
+
+// reset the values which are output, and which may be used from one psStats stage to the next
+void psStatsInit(psStats *stats)
+{
+    stats->sampleMean = NAN;
+    stats->sampleMedian = NAN;
+    stats->sampleStdev = NAN;
+    stats->sampleUQ = NAN;
+    stats->sampleLQ = NAN;
+    stats->sampleSkewness = NAN;
+    stats->sampleKurtosis = NAN;
+    stats->robustMedian = NAN;
+    stats->robustStdev = NAN;
+    stats->robustUQ = NAN;
+    stats->robustLQ = NAN;
+    stats->robustN50 = -1;
+    stats->fittedMean = NAN;
+    stats->fittedStdev = NAN;
+    stats->fittedNfit = -1;
+    stats->clippedMean = NAN;
+    stats->clippedStdev = NAN;
+    stats->clippedNvalues = -1;     // XXX: This is never used
+    stats->min = NAN;
+    stats->max = NAN;
+    stats->binsize = NAN;
+    return;
+}
+
+
+bool psMemCheckStats(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)statsFree );
+}
+
+/******************************************************************************
+psVectorStats(in, mask, maskVal, stats): this is the public API
+function which calls the above private stats functions based on what bits
+were set in stats->options.
+
+Inputs
+    in
+    mask
+    maskVal
+    stats
+Returns
+    The stats structure.
+
+XXX: Should we free stats if the asserts fail? NO; we don't own it (RHL).
+*****************************************************************************/
+bool psVectorStats(psStats* stats,
+                   const psVector* in,
+                   const psVector* errors,
+                   const psVector* mask,
+                   psMaskType maskVal)
+{
+    psTrace(TRACE, 3,"---- %s() begin  ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(stats, false);
+    PS_ASSERT_VECTOR_NON_NULL(in, false);
+    PS_ASSERT_VECTOR_NON_EMPTY(in, false);
+    if (mask) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(mask, in, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+    }
+    if (errors) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(errors, in, false);
+        PS_ASSERT_VECTOR_TYPE(errors, in->type.type, false);
+    }
+
+    // Convert types, as necessary
+    psVector *inF32 = NULL;             // Input vector of values, F32 version
+    if (in->type.type == PS_TYPE_F32) {
+        inF32 = psMemIncrRefCounter((psPtr)in);
+    } else {
+        inF32 = psVectorCopy(NULL, in, PS_TYPE_F32);
+    }
+    psVector *errorsF32 = NULL;         // Input vector of errors, F32 version
+    if (errors) {
+        if (errors->type.type == PS_TYPE_F32) {
+            errorsF32 = psMemIncrRefCounter((psPtr)errors);
+        } else {
+            errorsF32 = psVectorCopy(NULL, errors, PS_TYPE_F32);
+        }
+    }
+    psVector *maskU8 = NULL;            // Input mask vector, U8 version
+    if (mask) {
+        if (mask->type.type == PS_TYPE_MASK) {
+            maskU8 = psMemIncrRefCounter((psPtr)mask);
+        } else {
+            maskU8 = psVectorCopy(NULL, mask, PS_TYPE_MASK);
+        }
+    }
+
+    if ((stats->options & PS_STAT_USE_RANGE) && (stats->min >= stats->max)) {
+        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(stats->max, stats->min, false);
+    }
+
+    if ((stats->options & PS_STAT_USE_BINSIZE) && (stats->min >= stats->max)) {
+        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(stats->binsize, 0.0, false);
+    }
+
+    // init the value of stats->results: this is used internally to check if
+    // prior functions have been called
+    stats->results = PS_STAT_NONE;
+    bool status = true;
+
+    // ************************************************************************
+    if (stats->options & PS_STAT_SAMPLE_MEAN) {
+        if (!vectorSampleMean(inF32, errorsF32, maskU8, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate vector sample mean");
+            status &= false;
+        }
+    }
+
+    // ************************************************************************
+    if (stats->options & (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_QUARTILE)) {
+        if (!vectorSampleMedian(inF32, maskU8, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate sample median");
+            status &= false;
+        }
+    }
+
+    // ************************************************************************
+    if (stats->options & PS_STAT_SAMPLE_STDEV) {
+        if (!vectorSampleStdev(inF32, errorsF32, maskU8, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate sample stdev");
+            status &= false;
+        }
+    }
+
+    if (stats->options & (PS_STAT_SAMPLE_SKEWNESS | PS_STAT_SAMPLE_KURTOSIS)) {
+        if (!vectorSampleMoments(inF32, maskU8, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate sample moments");
+            status &= false;
+        }
+    }
+
+    // ************************************************************************
+    if (stats->options & (PS_STAT_MAX | PS_STAT_MIN)) {
+        if (vectorMinMax(inF32, maskU8, maskVal, stats) == 0) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate vector minimum and maximum");
+            status &= false;
+        }
+    }
+
+    // ************************************************************************
+    if (stats->options & (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE)) {
+        if (!vectorRobustStats(inF32, errorsF32, maskU8, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false, _("Failed to calculate robust statistics"));
+            status &= false;
+        }
+    }
+
+    // ************************************************************************
+    if (stats->options & (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV)) {
+        if (!vectorFittedStats(inF32, errorsF32, maskU8, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false, _("Failed to calculate fitted statistics"));
+            status &= false;
+        }
+    }
+
+    // ************************************************************************
+    if (stats->options & (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2)) {
+        if (stats->options & (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV)) {
+            psAbort("you may not specify both FITTED_MEAN and FITTED_MEAN_V2");
+        }
+        if (!vectorFittedStats_v2(inF32, errorsF32, maskU8, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false, _("Failed to calculate fitted statistics"));
+            status &= false;
+        }
+    }
+
+    // ************************************************************************
+    if (stats->options & (PS_STAT_FITTED_MEAN_V3 | PS_STAT_FITTED_STDEV_V3)) {
+        if (stats->options & (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV)) {
+            psAbort("you may not specify both FITTED_MEAN and FITTED_MEAN_V3");
+        }
+        if (!vectorFittedStats_v3(inF32, errorsF32, maskU8, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false, _("Failed to calculate fitted statistics"));
+            status &= false;
+        }
+    }
+
+    // ************************************************************************
+    if (stats->options & (PS_STAT_FITTED_MEAN_V4 | PS_STAT_FITTED_STDEV_V4)) {
+        if (stats->options & (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV)) {
+            psAbort("you may not specify both FITTED_MEAN and FITTED_MEAN_V4");
+        }
+        if (!vectorFittedStats_v4(inF32, errorsF32, maskU8, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false, _("Failed to calculate fitted statistics"));
+            status &= false;
+        }
+    }
+
+    // ************************************************************************
+    if ((stats->options & PS_STAT_CLIPPED_MEAN) || (stats->options & PS_STAT_CLIPPED_STDEV)) {
+        if (!vectorClippedStats(inF32, errorsF32, maskU8, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to calculate clipped statistics\n");
+            status &= false;
+        }
+    }
+
+    psFree(inF32);
+    psFree(errorsF32);
+    psFree(maskU8);
+    psTrace(TRACE, 3,"---- %s() end  ----\n", __func__);
+    return status;
+}
+
+psStatsOptions psStatsOptionFromString(const char *string)
+{
+#define READ_STAT(NAME, SYMBOL) \
+    if (strcasecmp(string, NAME) == 0) { \
+        return SYMBOL; \
+    }
+
+    READ_STAT("MEAN",       PS_STAT_SAMPLE_MEAN);
+    READ_STAT("STDEV",      PS_STAT_SAMPLE_STDEV);
+    READ_STAT("SKEWNESS",   PS_STAT_SAMPLE_SKEWNESS);
+    READ_STAT("KURTOSIS",   PS_STAT_SAMPLE_KURTOSIS);
+    READ_STAT("MEDIAN",     PS_STAT_SAMPLE_MEDIAN);
+    READ_STAT("QUARTILE",   PS_STAT_SAMPLE_QUARTILE);
+    READ_STAT("SAMPLE_MEAN",     PS_STAT_SAMPLE_MEAN);
+    READ_STAT("SAMPLE_STDEV",    PS_STAT_SAMPLE_STDEV);
+    READ_STAT("SAMPLE_MEDIAN",   PS_STAT_SAMPLE_MEDIAN);
+    READ_STAT("SAMPLE_QUARTILE", PS_STAT_SAMPLE_QUARTILE);
+    READ_STAT("SAMPLE_SKEWNESS", PS_STAT_SAMPLE_SKEWNESS);
+    READ_STAT("SAMPLE_KURTOSIS", PS_STAT_SAMPLE_KURTOSIS);
+    READ_STAT("ROBUST",          PS_STAT_ROBUST_MEDIAN);
+    READ_STAT("ROBUST_MEDIAN",   PS_STAT_ROBUST_MEDIAN);
+    READ_STAT("ROBUST_STDEV",    PS_STAT_ROBUST_STDEV);
+    READ_STAT("ROBUST_QUARTILE", PS_STAT_ROBUST_QUARTILE);
+    READ_STAT("FITTED",         PS_STAT_FITTED_MEAN);
+    READ_STAT("FITTED_MEAN",    PS_STAT_FITTED_MEAN);
+    READ_STAT("FITTED_STDEV",   PS_STAT_FITTED_STDEV);
+    READ_STAT("FITTED_V2",       PS_STAT_FITTED_MEAN_V2);
+    READ_STAT("FITTED_MEAN_V2",  PS_STAT_FITTED_MEAN_V2);
+    READ_STAT("FITTED_STDEV_V2", PS_STAT_FITTED_STDEV_V2);
+    READ_STAT("FITTED_V3",       PS_STAT_FITTED_MEAN_V3);
+    READ_STAT("FITTED_MEAN_V3",  PS_STAT_FITTED_MEAN_V3);
+    READ_STAT("FITTED_STDEV_V3", PS_STAT_FITTED_STDEV_V3);
+    READ_STAT("FITTED_V4",       PS_STAT_FITTED_MEAN_V4);
+    READ_STAT("FITTED_MEAN_V4",  PS_STAT_FITTED_MEAN_V4);
+    READ_STAT("FITTED_STDEV_V4", PS_STAT_FITTED_STDEV_V4);
+    READ_STAT("CLIPPED",         PS_STAT_CLIPPED_MEAN);
+    READ_STAT("CLIPPED_MEAN",    PS_STAT_CLIPPED_MEAN);
+    READ_STAT("CLIPPED_STDEV",   PS_STAT_CLIPPED_STDEV);
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to parse statistic: %s\n", string);
+    return 0;
+}
+
+psString psStatsOptionToString(psStatsOptions option)
+{
+    psString string = NULL;             // String to return
+
+#define WRITE_STAT(NAME, SYMBOL) \
+    if (option & SYMBOL) { \
+        psStringAppend(&string, "%s ", NAME); \
+    }
+
+    // Same list as above (for psStatsFromString), but with repeat symbols removed
+    WRITE_STAT("SAMPLE_MEAN",     PS_STAT_SAMPLE_MEAN);
+    WRITE_STAT("SAMPLE_STDEV",    PS_STAT_SAMPLE_STDEV);
+    WRITE_STAT("SAMPLE_MEDIAN",   PS_STAT_SAMPLE_MEDIAN);
+    WRITE_STAT("SAMPLE_QUARTILE", PS_STAT_SAMPLE_QUARTILE);
+    WRITE_STAT("SAMPLE_SKEWNESS", PS_STAT_SAMPLE_SKEWNESS);
+    WRITE_STAT("SAMPLE_KURTOSIS", PS_STAT_SAMPLE_KURTOSIS);
+    WRITE_STAT("ROBUST_MEDIAN",   PS_STAT_ROBUST_MEDIAN);
+    WRITE_STAT("ROBUST_STDEV",    PS_STAT_ROBUST_STDEV);
+    WRITE_STAT("ROBUST_QUARTILE", PS_STAT_ROBUST_QUARTILE);
+    WRITE_STAT("FITTED_MEAN",     PS_STAT_FITTED_MEAN);
+    WRITE_STAT("FITTED_STDEV",    PS_STAT_FITTED_STDEV);
+    WRITE_STAT("FITTED_MEAN_V2",  PS_STAT_FITTED_MEAN_V2);
+    WRITE_STAT("FITTED_STDEV_V2", PS_STAT_FITTED_STDEV_V2);
+    WRITE_STAT("FITTED_MEAN_V3",  PS_STAT_FITTED_MEAN_V3);
+    WRITE_STAT("FITTED_STDEV_V3", PS_STAT_FITTED_STDEV_V3);
+    WRITE_STAT("FITTED_MEAN_V4",  PS_STAT_FITTED_MEAN_V4);
+    WRITE_STAT("FITTED_STDEV_V4", PS_STAT_FITTED_STDEV_V4);
+    WRITE_STAT("CLIPPED_MEAN",    PS_STAT_CLIPPED_MEAN);
+    WRITE_STAT("CLIPPED_STDEV",   PS_STAT_CLIPPED_STDEV);
+
+    return string;
+}
+
+psStats *psStatsFromString(const char *string)
+{
+    psList *subStrings = psStringSplit(string, " ,;", false); // List of sub-strings
+    if (!subStrings || psListLength(subStrings) == 0) {
+        // Nothing here
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "No string to parse for statistics: %s\n", string);
+        psFree(subStrings);
+        return NULL;
+    }
+    psStats *stats = psStatsAlloc(0);   // Generate empty stats structure
+    psListIterator *iterator = psListIteratorAlloc(subStrings, PS_LIST_HEAD, false); // Iterator
+    psString statString;                // Statistic string, from iteration
+    while ((statString = psListGetAndIncrement(iterator))) {
+        psStatsOptions option = psStatsOptionFromString(statString);
+        if (option == 0) {
+            psWarning("Unable to interpret statistic option: %s --- ignored.\n", statString);
+            continue;
+        }
+        stats->options |= option;
+    }
+    psFree(iterator);
+    psFree(subStrings);
+    return stats;
+}
+
+psString psStatsToString(const psStats *stats)
+{
+    return psStatsOptionToString(stats->options);
+}
+
+psStatsOptions psStatsSingleOption(psStatsOptions option)
+{
+    switch (option & ~(PS_STAT_USE_RANGE | PS_STAT_USE_BINSIZE)) {
+      case PS_STAT_SAMPLE_MEAN:
+      case PS_STAT_SAMPLE_MEDIAN:
+      case PS_STAT_SAMPLE_STDEV:
+      case PS_STAT_SAMPLE_QUARTILE:
+      case PS_STAT_SAMPLE_SKEWNESS:
+      case PS_STAT_SAMPLE_KURTOSIS:
+      case PS_STAT_ROBUST_MEDIAN:
+      case PS_STAT_ROBUST_STDEV:
+      case PS_STAT_ROBUST_QUARTILE:
+      case PS_STAT_FITTED_MEAN:
+      case PS_STAT_FITTED_STDEV:
+      case PS_STAT_FITTED_MEAN_V2:
+      case PS_STAT_FITTED_STDEV_V2:
+      case PS_STAT_FITTED_MEAN_V3:
+      case PS_STAT_FITTED_STDEV_V3:
+      case PS_STAT_FITTED_MEAN_V4:
+      case PS_STAT_FITTED_STDEV_V4:
+      case PS_STAT_CLIPPED_MEAN:
+      case PS_STAT_CLIPPED_STDEV:
+      case PS_STAT_MAX:
+      case PS_STAT_MIN:
+        return option & ~(PS_STAT_USE_RANGE | PS_STAT_USE_BINSIZE);
+      default:
+        return 0;
+    }
+    psAbort("Should never get here.\n");
+    return 0;
+}
+
+psStatsOptions psStatsMeanOption(psStatsOptions options)
+{
+    return options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN |
+                      PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN_V2 |
+                      PS_STAT_FITTED_MEAN_V3 | PS_STAT_FITTED_MEAN_V4);
+}
+
+psStatsOptions psStatsStdevOption(psStatsOptions options)
+{
+    return options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV |
+                      PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV_V2 | PS_STAT_FITTED_STDEV_V3 |
+                      PS_STAT_FITTED_STDEV_V4);
+}
+
+
+double psStatsGetValue(const psStats *stats, psStatsOptions option)
+{
+    // We could call psStatsSingle to check, but it would be a waste since we effectively do it anyway
+    switch (option & ~(PS_STAT_USE_RANGE | PS_STAT_USE_BINSIZE)) {
+      case PS_STAT_SAMPLE_MEAN:
+        return stats->sampleMean;
+      case PS_STAT_SAMPLE_MEDIAN:
+        return stats->sampleMedian;
+      case PS_STAT_SAMPLE_STDEV:
+        return stats->sampleStdev;
+      case PS_STAT_SAMPLE_SKEWNESS:
+        return stats->sampleSkewness;
+      case PS_STAT_SAMPLE_KURTOSIS:
+        return stats->sampleKurtosis;
+      case PS_STAT_ROBUST_MEDIAN:
+        return stats->robustMedian;
+      case PS_STAT_ROBUST_STDEV:
+        return stats->robustStdev;
+      case PS_STAT_FITTED_MEAN:
+        return stats->fittedMean;
+      case PS_STAT_FITTED_STDEV:
+        return stats->fittedStdev;
+      case PS_STAT_FITTED_MEAN_V2:
+        return stats->fittedMean;
+      case PS_STAT_FITTED_STDEV_V2:
+        return stats->fittedStdev;
+      case PS_STAT_FITTED_MEAN_V3:
+        return stats->fittedMean;
+      case PS_STAT_FITTED_STDEV_V3:
+        return stats->fittedStdev;
+      case PS_STAT_FITTED_MEAN_V4:
+        return stats->fittedMean;
+      case PS_STAT_FITTED_STDEV_V4:
+        return stats->fittedStdev;
+      case PS_STAT_CLIPPED_MEAN:
+        return stats->clippedMean;
+      case PS_STAT_CLIPPED_STDEV:
+        return stats->clippedStdev;
+      case PS_STAT_MAX:
+        return stats->max;
+      case PS_STAT_MIN:
+        return stats->min;
+      case PS_STAT_SAMPLE_QUARTILE:
+      case PS_STAT_ROBUST_QUARTILE:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot return a single quartile value; "
+                "get them yourself.\n");
+        return NAN;
+      default:
+        return NAN;
+    }
+    psAbort("Should never get here.\n");
+    return NAN;
+}
+
+// other private functions used above
+
+static psF32 QuadraticInverse(psF32 a,
+                              psF32 b,
+                              psF32 c,
+                              psF32 y,
+                              psF32 xLo,
+                              psF32 xHi
+    )
+{
+    psF64 tmp = sqrt((y - c)/a + (b*b)/(4.0 * a * a));
+
+    psF64 x1 = -b/(2.0*a) + tmp;
+    psF64 x2 = -b/(2.0*a) - tmp;
+
+    if (xLo <= x1 && x1 <= xHi) {
+        return x1;
+    }
+    if (xLo <= x2 && x2 <= xHi) {
+        return x2;
+    }
+    return 0.5 * (xLo + xHi);
+}
+
+# if (0)
+/******************************************************************************
+fitQuadraticSearchForYThenReturnX(*xVec, *yVec, binNum, yVal): A general
+routine which fits a quadratic to three points and returns the x-value
+corresponding to the input y-value.  This routine takes psVectors of x/y pairs
+as input, and fits a quadratic to the 3 points surrounding element binNum in
+the vectors (the midpoint between element i and i+1 is used for x[i]).  It
+then determines for what value x does that quadratic f(x) = yVal (the input
+parameter).
+
+*****************************************************************************/
+static psF32 fitQuadraticSearchForYThenReturnX(const psVector *xVec,
+                                               psVector *yVec,
+                                               psS32 binNum,
+                                               psF32 yVal
+    )
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+    psTrace(TRACE, 5, "binNum, yVal is (%d, %f)\n", binNum, yVal);
+    if (psTraceGetLevel("psLib.math") >= 8) {
+        PS_VECTOR_PRINT_F32(xVec);
+        PS_VECTOR_PRINT_F32(yVec);
+    }
+
+    PS_ASSERT_VECTOR_NON_NULL(xVec, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(yVec, NAN);
+    PS_ASSERT_VECTOR_TYPE(xVec, PS_TYPE_F32, NAN);
+    PS_ASSERT_VECTOR_TYPE(yVec, PS_TYPE_F32, NAN);
+    //    PS_ASSERT_VECTORS_SIZE_EQUAL(xVec, yVec, NAN);
+    PS_ASSERT_INT_WITHIN_RANGE(binNum, 0, (int)(xVec->n - 1), NAN);
+    PS_ASSERT_INT_WITHIN_RANGE(binNum, 0, (int)(yVec->n - 1), NAN);
+
+    psVector *x = psVectorAlloc(3, PS_TYPE_F64);
+    psVector *y = psVectorAlloc(3, PS_TYPE_F64);
+    psF32 tmpFloat = 0.0f;
+
+    if ((binNum >= 1) && (binNum < (yVec->n - 2)) && (binNum < (xVec->n - 2))) {
+        // The general case.  We have all three points.
+        x->data.F64[0] = (psF64) (0.5 * (xVec->data.F32[binNum - 1] + xVec->data.F32[binNum]));
+        x->data.F64[1] = (psF64) (0.5 * (xVec->data.F32[binNum] + xVec->data.F32[binNum+1]));
+        x->data.F64[2] = (psF64) (0.5 * (xVec->data.F32[binNum+1] + xVec->data.F32[binNum+2]));
+        y->data.F64[0] = yVec->data.F32[binNum - 1];
+        y->data.F64[1] = yVec->data.F32[binNum];
+        y->data.F64[2] = yVec->data.F32[binNum + 1];
+        psTrace(TRACE, 6, "x vec (orig) is (%f %f %f %f)\n", xVec->data.F32[binNum - 1],
+                xVec->data.F32[binNum], xVec->data.F32[binNum+1], xVec->data.F32[binNum+2]);
+        psTrace(TRACE, 6, "x data is (%f %f %f)\n", x->data.F64[0], x->data.F64[1], x->data.F64[2]);
+        psTrace(TRACE, 6, "y data is (%f %f %f)\n", y->data.F64[0], y->data.F64[1], y->data.F64[2]);
+
+        //
+        // Ensure that the y value lies within range of the y values.
+        //
+        if (! (((y->data.F64[0] <= yVal) && (yVal <= y->data.F64[2])) ||
+               ((y->data.F64[2] <= yVal) && (yVal <= y->data.F64[0]))) ) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Specified yVal, %g, is not within y-range, %g to %g."),
+                    (psF64)yVal, y->data.F64[0], y->data.F64[2]);
+        }
+
+        //
+        // Ensure that the y values are monotonic.
+        //
+        if (((y->data.F64[0] < y->data.F64[1]) && !(y->data.F64[1] <= y->data.F64[2])) ||
+            ((y->data.F64[0] > y->data.F64[1]) && !(y->data.F64[1] >= y->data.F64[2]))) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "This routine must be called with monotonically increasing or decreasing data points.\n");
+            psFree(x);
+            psFree(y);
+            psTrace(TRACE, 5, "---- %s() end ----\n", __func__);
+            return NAN;
+        }
+
+        // Determine the coefficients of the polynomial.
+        psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+        if (!psVectorFitPolynomial1D(myPoly, NULL, 0, y, NULL, x)) {
+            psError(PS_ERR_UNEXPECTED_NULL, false,
+                    _("Failed to fit a 1-dimensional polynomial to the three specified data points.  "
+                      "Returning NAN."));
+            psFree(x);
+            psFree(y);
+            psTrace(TRACE, 5, "---- %s(NAN) end ----\n", __func__);
+            return NAN;
+        }
+        psTrace(TRACE, 6, "myPoly->coeff[0] is %f\n", myPoly->coeff[0]);
+        psTrace(TRACE, 6, "myPoly->coeff[1] is %f\n", myPoly->coeff[1]);
+        psTrace(TRACE, 6, "myPoly->coeff[2] is %f\n", myPoly->coeff[2]);
+        psTrace(TRACE, 6, "Fitted y vec is (%f %f %f)\n",
+                (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[0]),
+                (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[1]),
+                (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[2]));
+
+        psTrace(TRACE, 6, "We fit the polynomial, now find x such that f(x) equals %f\n", yVal);
+        tmpFloat = QuadraticInverse(myPoly->coeff[2], myPoly->coeff[1], myPoly->coeff[0], yVal,
+                                    x->data.F64[0], x->data.F64[2]);
+        psFree(myPoly);
+
+        if (isnan(tmpFloat)) {
+            psError(PS_ERR_UNEXPECTED_NULL,
+                    false, _("Failed to determine the median of the fitted polynomial.  Returning NAN."));
+            psFree(x);
+            psFree(y);
+            psTrace(TRACE, 5, "---- %s(NAN) end ----\n", __func__);
+            return(NAN);
+        }
+    } else {
+        // These are special cases where the bin is at the beginning or end of the vector.
+        if (binNum == 0) {
+            // We have two points only at the beginning of the vectors x and y.
+            tmpFloat = 0.5 * (xVec->data.F32[binNum] +
+                              xVec->data.F32[binNum + 1]);
+        } else if (binNum == (xVec->n - 1)) {
+            // The special case where we have two points only at the end of
+            // the vectors x and y.
+            // XXX: Is this right?
+            tmpFloat = xVec->data.F32[binNum];
+        } else if (binNum == (xVec->n - 2)) {
+            // XXX: Is this right?
+            tmpFloat = 0.5 * (xVec->data.F32[binNum] + xVec->data.F32[binNum + 1]);
+        }
+    }
+
+    psTrace(TRACE, 6, "FIT: return %f\n", tmpFloat);
+    psFree(x);
+    psFree(y);
+
+    psTrace(TRACE, 5, "---- %s(%f) end ----\n", __func__, tmpFloat);
+    return tmpFloat;
+}
+# endif
+
+/******************************************************************************
+fitQuadraticSearchForYThenReturnXusingValues(*xVec, *yVec, binNum, yVal): A general routine
+which fits a quadratic to three points and returns the x-value corresponding to the input
+y-value.  This routine takes psVectors of x/y pairs as input, and fits a quadratic to the 3
+points surrounding element binNum in the vectors.  This version uses the values of x[i] for the
+x coordinates (not the midpoints).  This is appropriate for a cumulative histogram.  It then
+determines for what value x does that quadratic f(x) = yVal (the input parameter).
+
+*****************************************************************************/
+static psF32 fitQuadraticSearchForYThenReturnXusingValues(const psVector *xVec,
+                                                          psVector *yVec,
+                                                          psS32 binNum,
+                                                          psF32 yVal
+    )
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+    psTrace(TRACE, 5, "binNum, yVal is (%d, %f)\n", binNum, yVal);
+    if (psTraceGetLevel("psLib.math") >= 8) {
+        PS_VECTOR_PRINT_F32(xVec);
+        PS_VECTOR_PRINT_F32(yVec);
+    }
+
+    PS_ASSERT_VECTOR_NON_NULL(xVec, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(yVec, NAN);
+    PS_ASSERT_VECTOR_TYPE(xVec, PS_TYPE_F32, NAN);
+    PS_ASSERT_VECTOR_TYPE(yVec, PS_TYPE_F32, NAN);
+    PS_ASSERT_INT_WITHIN_RANGE(binNum, 0, (int)(xVec->n - 1), NAN);
+    PS_ASSERT_INT_WITHIN_RANGE(binNum, 0, (int)(yVec->n - 1), NAN);
+
+    psVector *x = psVectorAlloc(3, PS_TYPE_F64);
+    psVector *y = psVectorAlloc(3, PS_TYPE_F64);
+    psF32 tmpFloat = 0.0f;
+
+    if ((binNum >= 1) && (binNum < (yVec->n - 2)) && (binNum < (xVec->n - 2))) {
+        // The general case.  We have all three points.
+        x->data.F64[0] = xVec->data.F32[binNum - 1];
+        x->data.F64[1] = xVec->data.F32[binNum];
+        x->data.F64[2] = xVec->data.F32[binNum+1];
+        y->data.F64[0] = yVec->data.F32[binNum - 1];
+        y->data.F64[1] = yVec->data.F32[binNum];
+        y->data.F64[2] = yVec->data.F32[binNum + 1];
+        psTrace(TRACE, 6, "x vec (orig) is (%f %f %f %f)\n", xVec->data.F32[binNum - 1],
+                xVec->data.F32[binNum], xVec->data.F32[binNum+1], xVec->data.F32[binNum+2]);
+        psTrace(TRACE, 6, "x data is (%f %f %f)\n", x->data.F64[0], x->data.F64[1], x->data.F64[2]);
+        psTrace(TRACE, 6, "y data is (%f %f %f)\n", y->data.F64[0], y->data.F64[1], y->data.F64[2]);
+
+        //
+        // Ensure that the y value lies within range of the y values.
+        //
+        if (! (((y->data.F64[0] <= yVal) && (yVal <= y->data.F64[2])) ||
+               ((y->data.F64[2] <= yVal) && (yVal <= y->data.F64[0]))) ) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Specified yVal, %g, is not within y-range, %g to %g."),
+                    (psF64)yVal, y->data.F64[0], y->data.F64[2]);
+        }
+
+        //
+        // Ensure that the y values are monotonic.
+        //
+        if (((y->data.F64[0] < y->data.F64[1]) && !(y->data.F64[1] <= y->data.F64[2])) ||
+            ((y->data.F64[0] > y->data.F64[1]) && !(y->data.F64[1] >= y->data.F64[2]))) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "This routine must be called with monotonically increasing or decreasing data points.\n");
+            psFree(x);
+            psFree(y);
+            psTrace(TRACE, 5, "---- %s() end ----\n", __func__);
+            return NAN;
+        }
+
+        // Determine the coefficients of the polynomial.
+        psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+        if (!psVectorFitPolynomial1D(myPoly, NULL, 0, y, NULL, x)) {
+            psError(PS_ERR_UNEXPECTED_NULL, false,
+                    _("Failed to fit a 1-dimensional polynomial to the three specified data points.  "
+                      "Returning NAN."));
+            psFree(x);
+            psFree(y);
+            psTrace(TRACE, 5, "---- %s(NAN) end ----\n", __func__);
+            return NAN;
+        }
+        psTrace(TRACE, 6, "myPoly->coeff[0] is %f\n", myPoly->coeff[0]);
+        psTrace(TRACE, 6, "myPoly->coeff[1] is %f\n", myPoly->coeff[1]);
+        psTrace(TRACE, 6, "myPoly->coeff[2] is %f\n", myPoly->coeff[2]);
+        psTrace(TRACE, 6, "Fitted y vec is (%f %f %f)\n",
+                (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[0]),
+                (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[1]),
+                (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[2]));
+
+        psTrace(TRACE, 6, "We fit the polynomial, now find x such that f(x) equals %f\n", yVal);
+        tmpFloat = QuadraticInverse(myPoly->coeff[2], myPoly->coeff[1], myPoly->coeff[0], yVal,
+                                    x->data.F64[0], x->data.F64[2]);
+        psFree(myPoly);
+
+        if (isnan(tmpFloat)) {
+            psError(PS_ERR_UNEXPECTED_NULL,
+                    false, _("Failed to determine the median of the fitted polynomial.  Returning NAN."));
+            psFree(x);
+            psFree(y);
+            psTrace(TRACE, 5, "---- %s(NAN) end ----\n", __func__);
+            return(NAN);
+        }
+    } else {
+        // These are special cases where the bin is at the beginning or end of the vector.
+        if (binNum == 0) {
+            // We have two points only at the beginning of the vectors x and y.
+            // XXX this does not seem to be doing the linear interpolation / extrapolation
+            tmpFloat = 0.5 * (xVec->data.F32[binNum] +
+                              xVec->data.F32[binNum + 1]);
+        } else if (binNum == (xVec->n - 1)) {
+            // The special case where we have two points only at the end of
+            // the vectors x and y.
+            // XXX: Is this right?
+            tmpFloat = xVec->data.F32[binNum];
+        } else if (binNum == (xVec->n - 2)) {
+            // XXX: Is this right?
+            tmpFloat = 0.5 * (xVec->data.F32[binNum] + xVec->data.F32[binNum + 1]);
+        }
+    }
+
+    psTrace(TRACE, 6, "FIT: return %f\n", tmpFloat);
+    psFree(x);
+    psFree(y);
+
+    psTrace(TRACE, 5, "---- %s(%f) end ----\n", __func__, tmpFloat);
+    return tmpFloat;
+}
+
+/******************************************************************************
+NOTE: We assume unnormalized gaussians.
+*****************************************************************************/
+static psF32 minimizeLMChi2Gauss1D(psVector *deriv,
+                                   const psVector *params,
+                                   const psVector *coords
+    )
+{
+    psTrace(TRACE, 4, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_VECTOR_SIZE(params, (long)2, NAN);
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(coords, NAN);
+    PS_ASSERT_VECTOR_SIZE(coords, (long)1, NAN);
+    PS_ASSERT_VECTOR_TYPE(coords, PS_TYPE_F32, NAN);
+
+    psF32 x = coords->data.F32[0];
+    psF32 mean = params->data.F32[0];
+    psF32 var = params->data.F32[1];
+    psF32 dx = (x - mean);
+
+    psF32 gauss = exp (-0.5*PS_SQR(dx)/var);
+    if (deriv) {
+        PS_ASSERT_VECTOR_SIZE(deriv, (long)2, NAN);
+        PS_ASSERT_VECTOR_TYPE(deriv, PS_TYPE_F32, NAN);
+        psF32 tmp = dx * gauss;
+        deriv->data.F32[0] = tmp / var;
+        deriv->data.F32[1] = tmp * dx / (var * var);
+    }
+
+
+    psTrace(TRACE, 4, "---- %s() end ----\n", __func__);
+    return gauss;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psStats.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psStats.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psStats.h	(revision 22322)
@@ -0,0 +1,185 @@
+/* @file  psStats.h
+ * @brief basic statistical operations
+ *
+ * This file will hold the definition of the histogram and stats data
+ * structures.  It also contains prototypes for procedures which operate
+ * on those data structures.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.65 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-03 20:55:09 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_STATS_H
+#define PS_STATS_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/******************************************************************************
+    Statistical functions and data structures.
+ *****************************************************************************/
+
+/** enumeration of statistical calculation options
+ *
+ *  @see psStats, psVectorStats, psImageStats
+ */
+typedef enum {
+    PS_STAT_NONE            = 0x000000, ///< Empty set
+    PS_STAT_MIN             = 0x000001, ///< Maximum
+    PS_STAT_MAX             = 0x000002, ///< Minumum
+    PS_STAT_SAMPLE_MEAN     = 0x000004, ///< Sample Mean
+    PS_STAT_SAMPLE_MEDIAN   = 0x000008, ///< Sample Median
+    PS_STAT_SAMPLE_STDEV    = 0x000010, ///< Sample Standard Deviation
+    PS_STAT_SAMPLE_QUARTILE = 0x000020, ///< Sample Quartile
+    PS_STAT_SAMPLE_SKEWNESS = 0x000040, ///< Sample Skewness (third moment)
+    PS_STAT_SAMPLE_KURTOSIS = 0x000080, ///< Sample Kurtosis (fourth moment)
+    PS_STAT_ROBUST_MEDIAN   = 0x000100, ///< Robust Median
+    PS_STAT_ROBUST_STDEV    = 0x000200, ///< Robust Standarad Deviation
+    PS_STAT_ROBUST_QUARTILE = 0x000400, ///< Robust Quartile
+    PS_STAT_ROBUST_SPARE1   = 0x000800, ///< Spare 1
+    PS_STAT_FITTED_MEAN     = 0x001000, ///< Fitted Mean
+    PS_STAT_FITTED_STDEV    = 0x002000, ///< Fitted Standard Deviation
+    PS_STAT_FITTED_MEAN_V2  = 0x004000, ///< Fitted Mean
+    PS_STAT_FITTED_STDEV_V2 = 0x008000, ///< Fitted Standard Deviation
+    PS_STAT_FITTED_MEAN_V3  = 0x010000, ///< Fitted Mean
+    PS_STAT_FITTED_STDEV_V3 = 0x020000, ///< Fitted Standard Deviation
+    PS_STAT_CLIPPED_MEAN    = 0x040000, ///< Clipped Mean
+    PS_STAT_CLIPPED_STDEV   = 0x080000, ///< Clipped Standard Deviation
+    PS_STAT_USE_RANGE       = 0x100000, ///< Range
+    PS_STAT_USE_BINSIZE     = 0x200000, ///< Binsize
+    PS_STAT_FITTED_MEAN_V4  = 0x400000, ///< Fitted Mean
+    PS_STAT_FITTED_STDEV_V4 = 0x800000, ///< Fitted Standard Deviation
+} psStatsOptions;
+
+/** This is the generic statistics structure.  It contails the data members
+    for the various statistic values.  It also contains the options member to
+    specifiy which statistics should be calculated. */
+typedef struct
+{
+    double sampleMean;                 ///< formal mean of sample
+    double sampleMedian;               ///< formal median of sample
+    double sampleStdev;                ///< standard deviation of sample
+    double sampleUQ;                   ///< upper quartile of sample
+    double sampleLQ;                   ///< lower quartile of sample
+    double sampleSkewness;             ///< skewness (third moment) of sample
+    double sampleKurtosis;             ///< kurtosis (fourth moment) of sample
+    double robustMedian;               ///< robust median of array
+    double robustStdev;                ///< robust standard deviation of array
+    double robustUQ;                   ///< robust upper quartile
+    double robustLQ;                   ///< robust lower quartile
+    long robustN50;                    ///< Number of points in Gaussian fit; XXX: This is currently unused.
+    double fittedMean;                 ///< robust mean of data
+    double fittedStdev;                ///< robust standard deviation of data
+    long fittedNfit;                   ///< Number of points in Gaussian fit; XXX: This is currently unused
+    double clippedMean;                ///< Nsigma clipped mean
+    double clippedStdev;               ///< standard deviation after clipping
+    long clippedNvalues;               ///< Number of data points used for clipped mean.
+    double clipSigma;                  ///< Nsigma used for clipping; user input
+    int clipIter;                      ///< Number of clipping iterations; user input
+    double min;                        ///< minimum data value in array
+    double max;                        ///< maximum data value in array
+    double binsize;                    ///< binsize for robust fit (input/ouput)
+    long nSubsample;                   ///< maxinum number of measurements (input)
+    psStatsOptions options;            ///< bitmask of values requested
+    psStatsOptions results;            ///< bitmask of values calculated
+    psVector *tmpData;		       ///< temporary vector so repeated calls do not have to realloc
+    psVector *tmpMask;		       ///< temporary vector so repeated calls do not have to realloc
+}
+psStats;
+
+/** Performs statistical calculations on a vector.
+ *
+ *  @return psStats*    the statistical results as specified by stats->options
+ */
+bool psVectorStats(
+    psStats* stats,                    ///< stats structure defines stats to be calculated and how
+    const psVector* in,                ///< Vector to be analysed.
+    const psVector* errors,            ///< Errors.
+    const psVector* mask,              ///< Ignore elements where (maskVector & maskVal) != 0: must be INT or NULL
+    psMaskType maskVal                 ///< Only mask elements with one of these bits set in maskVector
+);
+
+/** Allocator of the psStats structure.
+ *
+ *  @return psStats*    A new psStats struct with the options member set to the
+ *                      value given.
+ */
+#ifdef DOXYGEN
+psStats* psStatsAlloc(
+    psStatsOptions options              ///< Statistics to calculate
+);
+#else // ifdef DOXYGEN
+psStats* p_psStatsAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psStatsOptions options              ///< Statistics to calculate
+) PS_ATTR_MALLOC;
+#define psStatsAlloc(options) \
+      p_psStatsAlloc(__FILE__, __LINE__, __func__, options)
+#endif // ifdef DOXYGEN
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psStats structure, false otherwise.
+ */
+bool psMemCheckStats(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+// reset the values which are output, and which may be used from one psStats stage to the next
+void psStatsInit(psStats *stats);
+
+// Get the statistics option from a string
+psStatsOptions psStatsOptionFromString(const char *string);
+// Write a string from the statistics options
+psString psStatsOptionToString(psStatsOptions option);
+// Generate a psStats from a string of statistics options
+psStats *psStatsFromString(const char *string);
+// Generate a string of statistics options from a psStats
+psString psStatsToString(const psStats *stats);
+// Is only a single statistics option set?
+psStatsOptions psStatsSingleOption(psStatsOptions option);
+// Return a particular stats value
+double psStatsGetValue(const psStats *stats, psStatsOptions option);
+// Return the statistics option(s) for the mean/median
+psStatsOptions psStatsMeanOption(psStatsOptions options);
+// Return the statistics option(s) for the stdev
+psStatsOptions psStatsStdevOption(psStatsOptions options);
+
+/// @}
+#endif // #ifndef PS_STATS_H
+
+/*
+ * private stats functions used in psStats.c:
+ *
+ * vectorSampleMean
+   (none)
+ * vectorMinMax
+   (none)
+ * vectorSampleMedian (also yields SAMPLE_QUARTILE)
+   (none)
+ * vectorSampleStdev
+   (vectorSampleMean)
+ * vectorClippedStats
+   (vectorSampleMedian)
+   (vectorSampleMean (*also subset))
+   (vectorSampleStdev (*also subset))
+ * vectorRobustStats
+   (vectorMinMax (*only subset))
+ * vectorFittedStats
+   (vectorRobustStats)
+
+ * private stats functions called by other private stats functions are automatically called by
+ * those functions.  since they set the stats->results flags, they are not called multiple
+ * times.
+
+ * the private stats functions do not test for their corresponding stats flags: it is not
+ * necessary to request them if they are called within this function.
+
+*/
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psUnaryOp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psUnaryOp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psUnaryOp.c	(revision 22322)
@@ -0,0 +1,282 @@
+/** @file  psUnary.c
+ *
+ *  @brief Provides unary functions for simple matrix and vector element operations. Functions
+ *  include:
+ *
+ *      Addition (+)
+ *      Subtraction (-)
+ *      Multiplication (*)
+ *      Division (/)
+ *      Power (^)
+ *      Minimum (min)
+ *      Maximum (max)
+ *      Absolute value (abs)
+ *      Exponent (exp)
+ *      Natural Log (ln)
+ *      Power of 10 (ten)
+ *      Log (log)
+ *      Square root (sqrt)
+ *      Sine (sin or dsin)
+ *      Cosine (cos or dcos)
+ *      Tangent (tan or dtan)
+ *      Arcsine (asin or dasin)
+ *      Arccosine (acos or dacos)
+ *      Arctan (atan or datan)
+ *
+ *  Currently only vector-vector and image-image binary operations are supported.
+ *
+ *  @ingroup MatrixArithmetic
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-03-19 00:52:35 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/******************************************************************************
+ *  INCLUDE FILES                                                             *
+ ******************************************************************************/
+#include <string.h>
+#include <strings.h>
+#include <math.h>
+#include <stdint.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psLogMsg.h"
+#include "psAssert.h"
+
+
+/*****************************************************************************
+ *  FUNCTION IMPLEMENTATION - LOCAL                                          *
+ *****************************************************************************/
+
+// Conversion for degrees to radians
+#define D2R 0.01745329252111111  /* PI/180 */
+
+// Conversion for radians to degrees
+#define R2D 57.29577950924861   /* 180.0/PI */
+
+
+// Unary SCALAR operations
+#define SCALAR(OUT,IN,OP,TYPE) \
+{ \
+    ps##TYPE *o  = &((psScalar*)OUT)->data.TYPE; \
+    ps##TYPE *i1 = &((psScalar*)IN)->data.TYPE; \
+    *o = OP; \
+}
+
+// Unary IMAGE operations
+#define VECTOR(OUT,IN,OP,TYPE) \
+{ \
+    long nIn = ((psVector*)IN)->n; \
+    long nOut = ((psVector*)OUT)->n; \
+    if (nIn != nOut) { \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Number of elements inconsistent, %ld vs %ld.  Number of elements must match."), nIn, nOut); \
+        if (OUT != IN) { \
+            psFree(OUT); \
+        } \
+        return NULL; \
+    } \
+    ps##TYPE *o  = ((psVector*)OUT)->data.TYPE; \
+    ps##TYPE *i1 = ((psVector*)IN)->data.TYPE; \
+    for (long i = 0; i < nIn; i++, o++, i1++) { \
+        *o = OP; \
+    } \
+}
+
+// Unary IMAGE operations
+#define IMAGE(OUT,IN,OP,TYPE) \
+{ \
+    long numRowsIn = ((psImage*)IN)->numRows; \
+    long numColsIn = ((psImage*)IN)->numCols; \
+    long numRowsOut = ((psImage*)OUT)->numRows; \
+    long numColsOut = ((psImage*)OUT)->numCols; \
+    if(numRowsIn!=numRowsOut || numColsIn!=numColsOut) { \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Specified psImage dimensions differed, %ldx%ld vs %ldx%ld."), \
+                numColsIn, numRowsIn, numColsOut, numRowsOut); \
+        if (OUT != IN) { \
+            psFree(OUT); \
+        } \
+        return NULL; \
+    } \
+    for (long j = 0; j < numRowsIn; j++) { \
+        ps##TYPE *o  = ((psImage* )OUT)->data.TYPE[j]; \
+        ps##TYPE *i1 = ((psImage* )IN)->data.TYPE[j]; \
+        for (long i = 0; i < numColsIn; i++, o++, i1++) { \
+            *o = OP; \
+        } \
+    }\
+}
+
+// Preprocessor macro function to create arithmetic function based on input type
+#define UNARY_TYPE(DIM,OUT,IN,OP) \
+switch (IN->type) { \
+case PS_TYPE_U8: \
+    DIM(OUT,IN,OP,U8); \
+    break; \
+case PS_TYPE_U16: \
+    DIM(OUT,IN,OP,U16); \
+    break; \
+case PS_TYPE_U32: \
+    DIM(OUT,IN,OP,U32); \
+    break; \
+case PS_TYPE_U64: \
+    DIM(OUT,IN,OP,U64); \
+    break; \
+case PS_TYPE_S8: \
+    DIM(OUT,IN,OP,S8); \
+    break; \
+case PS_TYPE_S16: \
+    DIM(OUT,IN,OP,S16); \
+    break; \
+case PS_TYPE_S32: \
+    DIM(OUT,IN,OP,S32); \
+    break; \
+case PS_TYPE_S64: \
+    DIM(OUT,IN,OP,S64); \
+    break; \
+case PS_TYPE_F32: \
+    DIM(OUT,IN,OP,F32); \
+    break; \
+case PS_TYPE_F64: \
+    DIM(OUT,IN,OP,F64); \
+    break; \
+default: { \
+        char* strType; \
+        PS_TYPE_NAME(strType, IN->type); \
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, _("Specified data type, %s, is not supported."), strType); \
+        if (OUT != IN) { \
+            psFree(OUT); \
+        } \
+        return NULL; \
+    } \
+}
+
+// Preprocessor macro function to create arithmetic function operation name. Functions below that add
+// FLT_EPSILON are done so to align results with a 64 bit computing architecture
+#define UNARY_OP(DIM,OUT,IN,OP) \
+if(!strncasecmp(OP, "abs", 3)) { \
+    UNARY_TYPE(DIM,OUT,IN,fabs((double)*i1)); \
+} else if(!strncasecmp(OP, "sqrt", 4)) { \
+    UNARY_TYPE(DIM,OUT,IN,sqrt((double)*i1)); \
+} else if(!strncasecmp(OP, "exp", 3)) { \
+    UNARY_TYPE(DIM,OUT,IN,exp((double)*i1)); \
+} else if(!strncasecmp(OP, "ln", 2)) { \
+    UNARY_TYPE(DIM,OUT,IN,log((double)*i1)); \
+} else if(!strncasecmp(OP, "ten", 3)) { \
+    UNARY_TYPE(DIM,OUT,IN,pow(10.0,(double)*i1)); \
+} else if(!strncasecmp(OP, "log", 3)) { \
+    UNARY_TYPE(DIM,OUT,IN,log10((double)*i1)); \
+} else if(!strncasecmp(OP, "sin", 3)) { \
+    UNARY_TYPE(DIM,OUT,IN,sin((double)*i1)); \
+} else if(!strncasecmp(OP, "dsin", 4)) { \
+    UNARY_TYPE(DIM,OUT,IN,sin((double)*i1*D2R)); \
+} else if(!strncasecmp(OP, "cos", 3)) { \
+    UNARY_TYPE(DIM,OUT,IN,cos((double)*i1)); \
+} else if(!strncasecmp(OP, "dcos", 4)) { \
+    UNARY_TYPE(DIM,OUT,IN,cos((double)*i1*D2R)); \
+} else if(!strncasecmp(OP, "tan", 3)) { \
+    UNARY_TYPE(DIM,OUT,IN,tan((double)*i1)); \
+} else if(!strncasecmp(OP, "dtan", 4)) { \
+    UNARY_TYPE(DIM,OUT,IN,tan((double)*i1*D2R)); \
+} else if(!strncasecmp(OP, "asin", 4)) { \
+    UNARY_TYPE(DIM,OUT,IN,asin((double)*i1)); \
+} else if(!strncasecmp(OP, "dasin", 5)) { \
+    UNARY_TYPE(DIM,OUT,IN,(R2D*asin((double)*i1))); \
+} else if(!strncasecmp(OP, "acos", 4)) { \
+    UNARY_TYPE(DIM,OUT,IN,acos((double)*i1)); \
+} else if(!strncasecmp(OP, "dacos", 5)) { \
+    UNARY_TYPE(DIM,OUT,IN,R2D*acos((double)*i1)); \
+} else if(!strncasecmp(OP, "atan", 4)) { \
+    UNARY_TYPE(DIM,OUT,IN,atan((double)*i1)); \
+} else if(!strncasecmp(OP, "datan", 5)) { \
+    UNARY_TYPE(DIM,OUT,IN,R2D*atan((double)*i1)); \
+} else { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Specified operation, %s, is not supported."), OP); \
+    if (OUT != IN) { \
+        psFree(OUT); \
+    } \
+    return NULL; \
+}
+
+psMathType* psUnaryOp(psPtr out, const psPtr in, const char *op)
+{
+    #define psUnaryOp_EXIT { \
+                             if (out != in) { \
+                             psFree(out); \
+                             } \
+                             return NULL; \
+                           }
+
+    psMathType* psTypeIn = (psMathType* ) in;
+
+    PS_ASSERT_GENERAL_PTR_NON_NULL(in, psUnaryOp_EXIT);
+    PS_ASSERT_GENERAL_PTR_NON_NULL(op, psUnaryOp_EXIT);
+
+    switch (psTypeIn->dimen) {
+    case PS_DIMEN_SCALAR:
+        if (out == NULL ||
+                ((psMathType*)out)->dimen != PS_DIMEN_SCALAR ||
+                ((psScalar*)out)->type.type != psTypeIn->type) {
+            psFree(out);
+            out = psScalarAlloc(0.0, psTypeIn->type);
+        }
+        UNARY_OP(SCALAR, out, psTypeIn, op);    // scalar
+        break;
+    case PS_DIMEN_VECTOR:
+    case PS_DIMEN_TRANSV:
+        if (((psVector*)in)->n == 0) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Input psVector contains no elements.  No data to perform operation with."));
+            psUnaryOp_EXIT;
+        }
+
+        out = psVectorRecycle(out, ((psVector*)in)->n, psTypeIn->type);
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false, _("Couldn't create a proper output psVector."));
+            psUnaryOp_EXIT;
+        }
+        ((psVector*)out)->n = ((psVector*)in)->n;
+
+        UNARY_OP(VECTOR, out, psTypeIn, op);    // vector
+        break;
+    case PS_DIMEN_IMAGE:
+        if (((psImage* ) in)->numCols == 0 || ((psImage* ) in)->numRows == 0) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Input psImage contains no pixels.  No data to perform operation with."));
+            psUnaryOp_EXIT;
+        }
+
+        out = psImageRecycle(out, ((psImage*)in)->numCols, ((psImage*)in)->numRows, psTypeIn->type);
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false, _("Couldn't create a proper output psImage."));
+            psUnaryOp_EXIT;
+        }
+
+        UNARY_OP(IMAGE, out, psTypeIn, op);     // image
+        break;
+    default:
+        if (out != in) {
+            psFree(out);
+        }
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Specified parameter, %s, has invalid dimensionality, %d."), "in", psTypeIn->dimen);
+        psUnaryOp_EXIT;
+    }
+
+    // Automtically free psScalar types, since they are usually allocated in the argument list when this
+    // function is called, provided that the input is not the output.
+    if (psTypeIn->dimen == PS_DIMEN_SCALAR && in!=out) {
+        psFree(in);
+    }
+
+    return out;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psUnaryOp.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psUnaryOp.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psUnaryOp.h	(revision 22322)
@@ -0,0 +1,66 @@
+/* @file  psUnaryOp.h
+ *
+ * @brief Provides unary functions for simple matrix and vector element operations
+ *
+ * Defined unary functions include:
+ *
+ *     Addition (+)
+ *     Subtraction (-)
+ *     Multiplication (*)
+ *     Division (/)
+ *     Power (^)
+ *     Minimum (min)
+ *     Maximum (max)
+ *     Absolute value (abs)
+ *     Exponent (exp)
+ *     Natural Log (ln)
+ *     Power of 10 (ten)
+ *     Log (log)
+ *     Sine (sin or dsin)
+ *     Cosine (cos or dcos)
+ *     Tangent (tan or dtan)
+ *     Arcsine (asin or dasin)
+ *     Arccosine (acos or dacos)
+ *     Arctan (atan or datan)
+ *
+ * Currently only vector-vector and image-image binary operations are supported.
+ *
+ * @author EAM, IfA
+ * @author Ross Harman, MHPCC
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-23 22:47:23 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSUNARY_OP_H
+#define PSUNARY_OP_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/** Perform simple unary arithmetic with images or vectors
+ *
+ *  Performs absolute value, exponent, natural log, power of 10, log, sine, cosine, tangent, arcsine,
+ *  arccosine, or arctan. operations with images and vectors. Uses the form:
+ *
+ *     out = op(in),
+ *
+ *     Where op is: "abs", "exp", "ln", "ten", "log", "sin", "cos", "tan" "asin", "acos", "atan", "dsin",
+ *                  "dcos", dtan", "dasin", "dacos", or "datan".
+ *
+ *  Trigometric Operations with "d" prefix use units of degrees. Those without are in radians.
+ *
+ *  This function only supports vector-vector or image-image opertions.
+ *
+ *  @return  psType* : Pointer to either psImage or psVector.
+ */
+psMathType* psUnaryOp(
+    psPtr out,                         ///< Output type, either psImage or psVector.
+    const psPtr in,                    ///< Input, either psImage or psVector.
+    const char *op                     ///< Operator.
+);
+
+/// @}
+#endif // #ifndef PSUNARY_OP_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psVectorSmooth.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psVectorSmooth.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psVectorSmooth.c	(revision 22322)
@@ -0,0 +1,171 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <unistd.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psVector.h"
+#include "psAssert.h"
+#include "psConstants.h"
+
+#include "psVectorSmooth.h"
+
+psVector *psVectorSmooth(psVector *output,
+                         const psVector *input,
+                         double sigma,
+                         double Nsigma
+                        )
+{
+    PS_ASSERT_VECTOR_NON_NULL(input, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN(sigma, 0.0, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN(Nsigma, 0.0, NULL);
+
+    if (output == input) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot smooth vector in-place.");
+        return false;
+    }
+
+    // relevant terms
+    long Nrange = sigma*Nsigma + 0.5;   // Extent of smoothing
+    long Npixel = 2*Nrange + 1;         // Total size of smoothing kernel
+    long Nbin = input->n;               // Number of elements
+    double factor = -0.5/(sigma*sigma); // Factor for Gaussian
+
+    #define VECTOR_SMOOTH_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        output = psVectorRecycle(output, Nbin, PS_TYPE_##TYPE); \
+        /* generate normalized gaussian */ \
+        psVector *gaussnorm = psVectorAlloc(Npixel, PS_TYPE_##TYPE); \
+        double sum = 0.0; \
+        for (long i = -Nrange; i < Nrange + 1; i++) { \
+            gaussnorm->data.TYPE[i+Nrange] = exp(factor*i*i); \
+            sum += gaussnorm->data.TYPE[i+Nrange]; \
+        } \
+        for (long i = -Nrange; i < Nrange + 1; i++) { \
+            gaussnorm->data.TYPE[i+Nrange] /= sum; \
+        } \
+        ps##TYPE *gauss = &gaussnorm->data.TYPE[Nrange]; \
+        \
+        /* smooth vector */ \
+        psVector *temp = psVectorAlloc(Nbin, PS_TYPE_##TYPE); \
+        ps##TYPE *vi = input->data.TYPE; \
+        ps##TYPE *vo = temp->data.TYPE; \
+        /* smooth first Nrange pixels, with renorm */ \
+        /* XXX need to check that this does not run over end for narrow vectors */ \
+        for (long i = 0; i < Nrange; i++, vi++, vo++) { \
+            ps##TYPE *vr = vi - i; \
+            ps##TYPE *vg = gauss - i; \
+            double g = 0; \
+            double s = 0; \
+            for (int n = -i; n < Nrange + 1; n++, vr++, vg++) { \
+                s += *vg * *vr; \
+                g += *vg; \
+            } \
+            *vo = s / g; \
+        } \
+        /* smooth middle pixels */ \
+        for (long i = Nrange; i < Nbin - Nrange; i++, vi++, vo++) { \
+            ps##TYPE *vr = vi - Nrange; \
+            ps##TYPE *vg = gauss - Nrange; \
+            double s = 0; \
+            for (int n = -Nrange; n < Nrange + 1; n++, vr++, vg++) { \
+                s += *vg * *vr; \
+            } \
+            *vo = s; \
+        } \
+        /* smooth last Nrange pixels, with renorm */ \
+        /* XXX does this miss the last column? */ \
+        for (long i = Nbin - Nrange; i < Nbin; i++, vi++, vo++) { \
+            ps##TYPE *vr = vi - Nrange; \
+            ps##TYPE *vg = gauss - Nrange; \
+            double g = 0; \
+            double s = 0; \
+            for (int n = -Nrange; n < Nbin - i - 1; n++, vr++, vg++) { \
+                s += *vg * *vr; \
+                g += *vg; \
+            } \
+            *vo = s / g; \
+        } \
+        memcpy(output->data.TYPE, temp->data.TYPE, Nbin*sizeof(ps##TYPE)); \
+        psFree(temp); \
+        psFree(gaussnorm); \
+        break; \
+    }
+
+    switch (input->type.type) {
+        VECTOR_SMOOTH_CASE(F32);
+        VECTOR_SMOOTH_CASE(F64);
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr, input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type %s is not valid.", typeStr);
+            return NULL;
+        }
+    }
+    return output;
+}
+
+
+
+psVector *psVectorBoxcar(psVector *output,
+                         const psVector *input,
+                         int size
+                        )
+{
+    PS_ASSERT_VECTOR_NON_NULL(input, NULL);
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+
+    if (output == input) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot smooth vector in-place.");
+        return false;
+    }
+
+    long num = input->n;                // Number of elements
+    output = psVectorRecycle(output, num, input->type.type);
+    psVector *nums = psVectorAlloc(num, PS_TYPE_U32); // Number of elements in each bin
+    psU32 *numsData = nums->data.U32;   // Dereferenced version
+
+    psVectorInit(output, 0.0);
+    psVectorInit(nums, 0);
+
+
+    #define VECTOR_BOXCAR_CASE(TYPE) \
+  case PS_TYPE_##TYPE: { \
+      /* Dereference data */ \
+      ps##TYPE *outputData = output->data.TYPE; \
+      ps##TYPE *inputData = input->data.TYPE; \
+      /* Smooth the vector */ \
+      for (long i = 0; i < num; i++) { \
+          for (long j = PS_MAX(0, i - size); j < PS_MIN(num, i + size + 1); j++) { \
+              outputData[j] += inputData[i]; \
+              numsData[j]++; \
+          } \
+      } \
+      /* Normalisation */ \
+      for (long i = 0; i < num; i++) { \
+          outputData[i] /= numsData[i]; \
+      } \
+      break; \
+  }
+
+    switch (input->type.type) {
+        VECTOR_BOXCAR_CASE(F32);
+        VECTOR_BOXCAR_CASE(F64);
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr, input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type %s is not valid.", typeStr);
+            psFree(nums);
+            return NULL;
+        }
+    }
+    psFree(nums);
+    return output;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/math/psVectorSmooth.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/math/psVectorSmooth.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/math/psVectorSmooth.h	(revision 22322)
@@ -0,0 +1,29 @@
+/* @file psVectorSmooth.h
+ * @brief smooth the input vector
+ *
+ * $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * $Date: 2007-06-20 20:21:30 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_VECTOR_SMOOTH_H
+#define PS_VECTOR_SMOOTH_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+/// Smooth a vector with a Gaussian
+psVector *psVectorSmooth(psVector *output, ///< Output vector, or NULL
+                         const psVector *input, ///< Input vector (F32 or F64 only)
+                         double sigma,  ///< Gausian width (standard deviations)
+                         double Nsigma  ///< Number of standard deviations for Gaussian to extend
+                        );
+
+/// Smooth a vector with a boxcar
+psVector *psVectorBoxcar(psVector *output, ///< Output vector, or NULL
+                         const psVector *input, ///< Input vector (F32 or F64 only)
+                         int size       ///< Boxcar size (one-sided size)
+    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/Makefile.am	(revision 22322)
@@ -0,0 +1,18 @@
+#Makefile for mathtypes functions of psLib
+#
+noinst_LTLIBRARIES = libpslibmathtypes.la
+
+libpslibmathtypes_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) 
+libpslibmathtypes_la_SOURCES = \
+	psImage.c \
+	psScalar.c \
+	psVector.c
+
+EXTRA_DIST = mathtypes.i
+
+pkginclude_HEADERS = \
+	psImage.h \
+	psScalar.h \
+	psVector.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/mathtypes.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/mathtypes.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/mathtypes.i	(revision 22322)
@@ -0,0 +1,5 @@
+/* mathtypes headers */
+
+%include "psImage.h"
+%include "psScalar.h"
+%include "psVector.h"
Index: /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psImage.c	(revision 22322)
@@ -0,0 +1,613 @@
+/** @file  psImage.c
+ *
+ *  @brief Contains basic image definitions and operations.
+ *
+ *  This file defines the basic type for an image struct and functions useful
+ *  in manupulating images.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.131 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-05 23:42:46 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  That is the routine used to generate matrices.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psAssert.h"
+#include "psAbort.h"
+
+#include "psImage.h"
+#include "psString.h"
+
+
+static void imageFree(psImage* image)
+{
+    if (image == NULL) {
+        return;
+    }
+
+    psImage *parent = (psImage *) image->parent;
+
+    // if I am a child, remove me from my parent's array of children
+    if (parent != NULL) {
+        // sanity check : a child cannot also be a parent
+        if ((image->children != NULL) && (image->children->n > 0)) {
+            psAbort ("psImage cannot be both child and parent!");
+        }
+
+        // break the back-pointer first so we don't loop
+        image->parent = NULL;
+
+        // drop my entry on my parent's array of children
+        psArrayRemoveDataNoFree (parent->children, image);
+
+        // drop my reference to my parent
+        psFree (parent);
+    }
+
+    // sanity check: this function should never be reached if an image still has live children;
+    // they should each be holding a pointer to the image, forcing the number of references to
+    // be > 1.
+    if (image->children && (image->children->n > 0)) {
+        psAbort ("psImage memory management programming error : imageFree called on image with live children");
+    }
+
+    psFree(image->children);
+    psFree(image->p_rawDataBuffer);
+    psFree(image->data.V);
+}
+
+psImage* p_psImageAlloc(const char *file,
+                        unsigned int lineno,
+                        const char *func,
+                        int numCols,
+                        int numRows,
+                        psElemType type)
+{
+    int elementSize = PSELEMTYPE_SIZEOF(type);  // element size in bytes
+    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;
+    }
+
+    long numBytes = numRows * numCols * elementSize;
+
+    psImage* image = (psImage* ) p_psAlloc(file, lineno, func, sizeof(psImage));
+
+    psMemSetDeallocator(image, (psFreeFunc) imageFree);
+
+    image->data.V = p_psAlloc(file, lineno, func, sizeof(psPtr ) * numRows);
+
+    image->p_rawDataBuffer = p_psAlloc(file, lineno, func, numBytes);
+
+    // set the row pointers.
+    image->data.V[0] = image->p_rawDataBuffer;
+    for (psS32 i = 1; i < numRows; i++) {
+        image->data.V[i] = (psPtr )((int8_t *) image->data.V[i - 1] + rowSize);
+    }
+
+    P_PSIMAGE_SET_COL0 (image, 0);
+    P_PSIMAGE_SET_ROW0 (image, 0);
+    P_PSIMAGE_SET_NUMCOLS (image, numCols);
+    P_PSIMAGE_SET_NUMROWS (image, numRows);
+
+    psMathType imageType;
+    imageType.dimen = PS_DIMEN_IMAGE;
+    imageType.type = type;
+    P_PSIMAGE_SET_TYPE (image, imageType);
+
+    image->parent = NULL;
+    image->children = NULL;
+
+    return image;
+}
+
+
+bool psMemCheckImage(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)imageFree );
+}
+
+
+
+// Image initialisation for integer types
+#define IMAGEINIT_INTCASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            if (value == 0.0) { \
+                size_t numBytes = image->numCols * sizeof(ps##TYPE); \
+                for (int y = 0; y < image->numRows; y++) { \
+                    memset(image->data.TYPE[y], 0, numBytes); \
+                } \
+            } else { \
+                if (value < (double)PS_MIN_##TYPE || value > (double)PS_MAX_##TYPE) { \
+                    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Error: Value %f out of range for type %s.\n", \
+                            value, #TYPE); \
+                    return false; \
+                } \
+                ps##TYPE castValue = (ps##TYPE)value; \
+                for (int iy = 0; iy < image->numRows; iy++) { \
+                    ps##TYPE *row = image->data.TYPE[iy]; \
+                    for (int ix = 0; ix < image->numCols; ix++) { \
+                        row[ix] = castValue; \
+                    } \
+                } \
+            } \
+            return true; \
+        }
+
+// Image initialisation for char-size integer types
+#define IMAGEINIT_CHARCASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            if (value < (double)PS_MIN_##TYPE || value > (double)PS_MAX_##TYPE) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Error: Value %f out of range for type %s.\n", \
+                        value, #TYPE); \
+                return false; \
+            } \
+            size_t numBytes = image->numCols * sizeof(ps##TYPE); \
+            ps##TYPE castValue = (ps##TYPE)value; \
+            for (int y = 0; y < image->numRows; y++) { \
+                memset(image->data.TYPE[y], castValue, numBytes); \
+            } \
+            return true; \
+        }
+// Image initialisation for floating point types
+#define IMAGEINIT_FLOATCASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            if (value == 0.0) { \
+                size_t numBytes = image->numCols * sizeof(ps##TYPE); \
+                for (int y = 0; y < image->numRows; y++) { \
+                    memset(image->data.TYPE[y], 0, numBytes); \
+                } \
+            } else { \
+                ps##TYPE castValue = (ps##TYPE)value; \
+                for (int iy = 0; iy < image->numRows; iy++) { \
+                    ps##TYPE *row = image->data.TYPE[iy]; \
+                    for (int ix = 0; ix < image->numCols; ix++) { \
+                        row[ix] = castValue; \
+                    } \
+                } \
+            } \
+            return true; \
+        }
+
+
+bool psImageInit (psImage *image, double value)
+{
+    PS_ASSERT_PTR(image, false);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+
+    switch (image->type.type) {
+        IMAGEINIT_CHARCASE(U8)
+        IMAGEINIT_INTCASE(U16)
+        IMAGEINIT_INTCASE(U32)
+        IMAGEINIT_CHARCASE(S8)
+        IMAGEINIT_INTCASE(S16)
+        IMAGEINIT_INTCASE(S32)
+        IMAGEINIT_INTCASE(U64)
+        IMAGEINIT_INTCASE(S64)
+        IMAGEINIT_FLOATCASE(F32)
+        IMAGEINIT_FLOATCASE(F64)
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type %x not supported", image->type.type);
+    }
+    return (false);
+}
+
+bool psImageSet(psImage *image,
+                int x,
+                int y,
+                double value)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_INT_NONNEGATIVE(image->col0, false);
+    PS_ASSERT_INT_NONNEGATIVE(image->row0, false);
+    PS_ASSERT_INT_POSITIVE(image->numCols, false);
+    PS_ASSERT_INT_POSITIVE(image->numRows, false);
+    if ( x >= (image->col0 + image->numCols) ) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Invalid x-position %d.  Position out of range (%d-%d)\n",
+                x, image->col0, image->numCols+image->col0-1 );
+        return false;
+    } else if ( y >= (image->row0 + image->numRows) ) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Invalid y-position %d.  Position out of range (%d-%d)\n",
+                y, image->row0, image->numRows+image->row0-1 );
+        return false;
+    } else if (x < image->col0 && x >= 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Invalid x-position %d.  Position out of range (%d-%d)\n",
+                x, image->col0, image->numCols+image->col0-1 );
+        return false;
+    } else if (y < image->row0 && y >= 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Invalid y-position %d.  Position out of range (%d-%d)\n",
+                y, image->row0, image->numRows+image->row0-1 );
+        return false;
+    } else if (x < 0 || y < 0) {
+        if (x < 0) {
+            x += image->numCols;
+        }
+        if (x < 0) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Invalid x-position %d.  Position out of range (%d-%d)\n",
+                    (x+image->col0), image->col0, image->numCols+image->col0-1 );
+            return false;
+        }
+        if (y < 0) {
+            y += image->numRows;
+        }
+        if (y < 0) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Invalid y-position %d.  Position out of range (%d-%d)\n",
+                    (y+image->row0), image->row0, image->numRows+image->row0-1 );
+            return false;
+        }
+    } else {
+        x -= image->col0;
+        y -= image->row0;
+    }
+
+    #define IMAGE_SET_CASE(TYPE) \
+case PS_TYPE_##TYPE: \
+    image->data.TYPE[y][x] = (ps##TYPE)value; \
+    break;
+
+    switch (image->type.type) {
+        IMAGE_SET_CASE(U8);
+        IMAGE_SET_CASE(U16);
+        IMAGE_SET_CASE(U32);
+        IMAGE_SET_CASE(U64);
+        IMAGE_SET_CASE(S8);
+        IMAGE_SET_CASE(S16);
+        IMAGE_SET_CASE(S32);
+        IMAGE_SET_CASE(S64);
+        IMAGE_SET_CASE(F32);
+        IMAGE_SET_CASE(F64);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Invalid psImage Data Type\n");
+        return false;
+    }
+
+    return true;
+}
+
+double psImageGet(const psImage *image,
+                          int x,
+                          int y)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NAN);
+    PS_ASSERT_INT_NONNEGATIVE(image->col0, NAN);
+    PS_ASSERT_INT_NONNEGATIVE(image->row0, NAN);
+    PS_ASSERT_INT_POSITIVE(image->numCols, NAN);
+    PS_ASSERT_INT_POSITIVE(image->numRows, NAN);
+    if ( x >= (image->col0 + image->numCols) ) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Invalid x-position %d.  Position out of range (%d-%d)\n",
+                x, image->col0, image->numCols+image->col0-1 );
+        return NAN;
+    } else if ( y >= (image->row0 + image->numRows) ) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Invalid y-position %d.  Position out of range (%d-%d)\n",
+                y, image->row0, image->numRows+image->row0-1 );
+        return NAN;
+    } else if (x < image->col0 && x >= 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Invalid x-position %d.  Position out of range (%d-%d)\n",
+                x, image->col0, image->numCols+image->col0-1 );
+        return NAN;
+    } else if (y < image->row0 && y >= 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Invalid y-position %d.  Position out of range (%d-%d)\n",
+                y, image->row0, image->numRows+image->row0-1 );
+        return NAN;
+    } else if (x < 0 || y < 0) {
+        if (x < 0) {
+            x += image->numCols;
+        }
+        if (x < 0) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Invalid x-position %d.  Position out of range (%d-%d)\n",
+                    (x+image->col0), image->col0, image->numCols+image->col0-1 );
+            return NAN;
+        }
+        if (y < 0) {
+            y += image->numRows;
+        }
+        if (y < 0) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Invalid y-position %d.  Position out of range (%d-%d)\n",
+                    (y+image->row0), image->row0, image->numRows+image->row0-1 );
+            return NAN;
+        }
+    } else {
+        x -= image->col0;
+        y -= image->row0;
+    }
+
+    #define IMAGE_GET_CASE(TYPE) \
+case PS_TYPE_##TYPE: \
+    return image->data.TYPE[y][x];
+
+    switch (image->type.type) {
+        IMAGE_GET_CASE(U8);
+        IMAGE_GET_CASE(U16);
+        IMAGE_GET_CASE(U32);
+        IMAGE_GET_CASE(U64);
+        IMAGE_GET_CASE(S8);
+        IMAGE_GET_CASE(S16);
+        IMAGE_GET_CASE(S32);
+        IMAGE_GET_CASE(S64);
+        IMAGE_GET_CASE(F32);
+        IMAGE_GET_CASE(F64);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Invalid psImage Data Type\n");
+        return NAN;
+    }
+}
+
+psImage* p_psImageRecycle(const char *file,
+                        unsigned int lineno,
+                        const char *func,
+                        psImage* old,
+                        int numCols,
+                        int numRows,
+                        const psElemType type)
+{
+    psS32 elementSize = PSELEMTYPE_SIZEOF(type);  // element size in bytes
+    psS32 rowSize = numCols * elementSize;        // row size in bytes.
+
+    if (old == NULL) {
+        old = p_psImageAlloc(file, lineno, func, numCols, numRows, type);
+        return old;
+    }
+
+    if (old->type.dimen != PS_DIMEN_IMAGE) {
+        psFree(old);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("The input psImage must have a PS_DIMEN_IMAGE dimension type."));
+        return NULL;
+    }
+
+    /* image already the right size/type? */
+    if (numCols == old->numCols && numRows == old->numRows &&
+            type == old->type.type) {
+        return old;
+    }
+    // Resize the image buffer
+    old->p_rawDataBuffer = psRealloc(old->data.V[0],
+                                     numCols * numRows * elementSize);
+    old->data.V = (psPtr *)psRealloc(old->data.V, numRows * sizeof(psPtr ));
+
+    // recreate the row pointers
+    old->data.V[0] = old->p_rawDataBuffer;
+    for (psS32 i = 1; i < numRows; i++) {
+        old->data.V[i] = (psPtr )((int8_t *) old->data.V[i - 1] + rowSize);
+    }
+
+    *(psU32 *)&old->numCols = numCols;
+    *(psU32 *)&old->numRows = numRows;
+    *(psElemType* ) & old->type.type = type;
+
+    return old;
+}
+
+bool p_psImageCopyToRawBuffer(void* buffer,
+                              const psImage* input,
+                              psElemType type)
+{
+    psElemType inDatatype;
+    psS32 numRows;
+    psS32 numCols;
+
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psImage."));
+        return false;
+    }
+
+    if (input->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("The input psImage must have a PS_DIMEN_IMAGE dimension type."));
+        return false;
+    }
+
+    inDatatype = input->type.type;
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    // cover the trival case of copy of the same
+    // datatype.
+    if (type == inDatatype) {
+        int rowSize = PSELEMTYPE_SIZEOF(inDatatype)*numCols;
+        for (psS32 row=0;row<numRows;row++) {
+            memcpy(&((psS8*)buffer)[row*rowSize], input->data.V[row], rowSize);
+        }
+        return true;
+    }
+
+    #define PSIMAGE_BUFFER_COPY(INTYPE,OUTTYPE) { \
+        ps##INTYPE *in; \
+        ps##OUTTYPE *out = buffer; \
+        for(psS32 row=0;row<numRows;row++) { \
+            in = input->data.INTYPE[row]; \
+            for (psS32 col=0;col<numCols;col++) { \
+                *(out++) = *(in++); \
+            } \
+        } \
+    }
+
+    #define PSIMAGE_BUFFER_COPY_CASE(OUT,OUTTYPE) { \
+        switch (inDatatype) { \
+        case PS_TYPE_S8: \
+            PSIMAGE_BUFFER_COPY(S8,OUTTYPE); \
+            break; \
+        case PS_TYPE_S16: \
+            PSIMAGE_BUFFER_COPY(S16,OUTTYPE); \
+            break; \
+        case PS_TYPE_S32: \
+            PSIMAGE_BUFFER_COPY(S32,OUTTYPE); \
+            break; \
+        case PS_TYPE_S64: \
+            PSIMAGE_BUFFER_COPY(S64,OUTTYPE); \
+            break; \
+        case PS_TYPE_U8: \
+            PSIMAGE_BUFFER_COPY(U8,OUTTYPE); \
+            break; \
+        case PS_TYPE_U16: \
+            PSIMAGE_BUFFER_COPY(U16,OUTTYPE); \
+            break; \
+        case PS_TYPE_U32: \
+            PSIMAGE_BUFFER_COPY(U32,OUTTYPE); \
+            break; \
+        case PS_TYPE_U64: \
+            PSIMAGE_BUFFER_COPY(U64,OUTTYPE); \
+            break; \
+        case PS_TYPE_F32: \
+            PSIMAGE_BUFFER_COPY(F32,OUTTYPE); \
+            break; \
+        case PS_TYPE_F64: \
+            PSIMAGE_BUFFER_COPY(F64,OUTTYPE); \
+            break; \
+        default: \
+            break; \
+        } \
+    }
+
+    switch (type) {
+    case PS_TYPE_S8:
+        PSIMAGE_BUFFER_COPY_CASE(output, S8);
+        break;
+    case PS_TYPE_S16:
+        PSIMAGE_BUFFER_COPY_CASE(output, S16);
+        break;
+    case PS_TYPE_S32:
+        PSIMAGE_BUFFER_COPY_CASE(output, S32);
+        break;
+    case PS_TYPE_S64:
+        PSIMAGE_BUFFER_COPY_CASE(output, S64);
+        break;
+    case PS_TYPE_U8:
+        PSIMAGE_BUFFER_COPY_CASE(output, U8);
+        break;
+    case PS_TYPE_U16:
+        PSIMAGE_BUFFER_COPY_CASE(output, U16);
+        break;
+    case PS_TYPE_U32:
+        PSIMAGE_BUFFER_COPY_CASE(output, U32);
+        break;
+    case PS_TYPE_U64:
+        PSIMAGE_BUFFER_COPY_CASE(output, U64);
+        break;
+    case PS_TYPE_F32:
+        PSIMAGE_BUFFER_COPY_CASE(output, F32);
+        break;
+    case PS_TYPE_F64:
+        PSIMAGE_BUFFER_COPY_CASE(output, F64);
+        break;
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Specified psImage type, %s, is not supported."),
+                    typeStr);
+            break;
+        }
+    }
+    return true;
+}
+
+bool p_psImagePrint (int fd,
+                     psImage *a,
+                     char *name)
+{
+    write(fd,"matrix: ",8);
+    write(fd,name,strlen(name));
+    write(fd,"\n",1);
+
+    char buffer[20];
+    //    for (int j = 0; j < a[0].numRows; j++) {
+    //        for (int i = 0; i < a[0].numCols; i++) {
+    for (int j = 0; j < a->numRows; j++) {
+        for (int i = 0; i < a->numCols; i++) {
+            snprintf (buffer,20, "%f  ", p_psImageGetElementF64(a, i, j));
+            write(fd,buffer,strlen(buffer));
+        }
+        write(fd,"\n",1);
+    }
+    write(fd,"\n",1);
+    return (true);
+}
+
+
+psF64 p_psImageGetElementF64(psImage* image,
+                             int col,
+                             int row)
+{
+    if (image == NULL) {
+        return NAN;
+    }
+    if (col < 0 || col >= image->numCols) {
+        return NAN;
+    }
+    if (row < 0 || row >= image->numRows) {
+        return NAN;
+    }
+
+    switch (image->type.type) {
+    case PS_TYPE_U8:
+        return image->data.U8[row][col];
+        break;
+    case PS_TYPE_U16:
+        return image->data.U16[row][col];
+        break;
+    case PS_TYPE_U32:
+        return image->data.U32[row][col];
+        break;
+    case PS_TYPE_U64:
+        return image->data.U64[row][col];
+        break;
+    case PS_TYPE_S8:
+        return image->data.S8[row][col];
+        break;
+    case PS_TYPE_S16:
+        return image->data.S16[row][col];
+        break;
+    case PS_TYPE_S32:
+        return image->data.S32[row][col];
+        break;
+    case PS_TYPE_S64:
+        return image->data.S64[row][col];
+        break;
+    case PS_TYPE_F32:
+        return image->data.F32[row][col];
+        break;
+    case PS_TYPE_F64:
+        return image->data.F64[row][col];
+    default:
+        return NAN;
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psImage.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psImage.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psImage.h	(revision 22322)
@@ -0,0 +1,292 @@
+/* @file  psImage.h
+ * @brief Basic image definitions and operations
+ *
+ * This file defines the basic type for an image struct and functions useful
+ * in manupulating images.
+ *
+ * @author Robert DeSonia, MHPCC
+ * @author Ross Harman, MHPCC
+ * @author Joshua Hoblitt, University of Hawaii
+ *
+ * @version $Revision: 1.97 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-14 03:18:41 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_H
+#define PS_IMAGE_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include <stdio.h>
+#include "psType.h"
+#include "psArray.h"
+#include "psConstants.h"
+#include "psMutex.h"
+
+/** Basic image data structure.
+ *
+ * Struct for maintaining image data of varying types. It also contains
+ * information about image size, parent images and children images.
+ *
+ */
+typedef struct psImage
+{
+    const psMathType type;             ///< Image data type and dimension.
+    const int numCols;                 ///< Number of columns in image
+    const int numRows;                 ///< Number of rows in image.
+    const int col0;                     ///< Column position relative to parent.
+    const int row0;                     ///< Row position relative to parent.
+
+    union {
+        psS8**  S8;                    ///< Signed 8-bit integer data.
+        psS16** S16;                   ///< Signed 16-bit integer data.
+        psS32** S32;                   ///< Signed 32-bit integer data.
+        psS64** S64;                   ///< Signed 64-bit integer data.
+        psU8**  U8;                    ///< Unsigned 8-bit integer data.
+        psU16** U16;                   ///< Unsigned 16-bit integer data.
+        psU32** U32;                   ///< Unsigned 32-bit integer data.
+        psU64** U64;                   ///< Unsigned 64-bit integer data.
+        psF32** F32;                   ///< Single-precision float data.
+        psF64** F64;                   ///< Double-precision float data.
+        psPtr*  V;                     ///< Pointer to data.
+    } data;                            ///< Union for data types.
+    const struct psImage* parent;      ///< Parent, if a subimage.
+    psPtr p_rawDataBuffer;             ///< Raw data buffer for Allocating/Freeing Images; private
+    psArray* children;                 ///< Children of this region.
+    psMutex lock;                       ///< Optional lock for thread safety
+}
+psImage;
+
+
+#define P_PSIMAGE_SET_NUMCOLS(img,nc) {*(int*)&img->numCols = nc;}
+#define P_PSIMAGE_SET_NUMROWS(img,nr) {*(int*)&img->numRows = nr;}
+#define P_PSIMAGE_SET_COL0(img,c0) {*(int*)&img->col0 = c0;}
+#define P_PSIMAGE_SET_ROW0(img,r0) {*(int*)&img->row0 = r0;}
+#define P_PSIMAGE_SET_TYPE(img,t) {*(psMathType*)&img->type = t;}
+
+/** Create an image of the specified size and type.
+ *
+ * Uses psLib memory allocation functions to create an image struct of the
+ * specified size and type.
+ *
+ * @return psImage* : Pointer to psImage.
+ *
+ */
+#ifdef DOXYGEN
+psImage* psImageAlloc(
+    int numCols,                       ///< Number of columns in image.
+    int numRows,                       ///< Number of rows in image.
+    psElemType type                    ///< Type of data for image.
+);
+#else // ifdef DOXYGEN
+psImage* p_psImageAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    int numCols,                       ///< Number of columns in image.
+    int numRows,                       ///< Number of rows in image.
+    psElemType type                    ///< Type of data for image.
+) PS_ATTR_MALLOC;
+#define psImageAlloc(numCols, numRows, type) \
+      p_psImageAlloc(__FILE__, __LINE__, __func__, numCols, numRows, type)
+#endif // ifdef DOXYGEN
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr * datatype.
+ *  @return bool:       True if the pointer matches a psImage structure, false otherwise.
+ */
+bool psMemCheckImage(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+/** Initializes the image with the given value.
+ *
+ *  The input data is cast to match the image datatype.
+ *
+ *  @return bool:       True on success, otherwise false.
+ */
+bool psImageInit(
+    psImage *image,                    ///< the image to be initialized
+    double value                        ///< Value to which to initialise
+);
+
+
+/** Sets the value of the image at the specified x,y position to value.
+ *
+ *  A negative value for the x or y positions means index from the end.
+ *
+ *  @return bool:       True on success, otherwise false.
+ */
+bool psImageSet(
+    psImage *image,                     ///< the image to set
+    int x,                              ///< x-position
+    int y,                              ///< y-position
+    double value                        ///< specified value to set
+);
+
+/** Returns the value of the image at the specified x,y position.
+ *
+ *  A negative value for the x or y positions means index from the end.
+ *
+ *  @return double: The value at the specified x,y position.
+ */
+double psImageGet(
+    const psImage *image,              ///< the image from which to get
+    int x,                             ///< x-position
+    int y                              ///< y-position
+);
+
+/** Resize a given image to the given size/type.
+ *
+ *  @return psImage* Resized psImage.
+ */
+#ifdef DOXYGEN
+psImage* psImageRecycle(
+    psImage* old,                      ///< the psImage to recycle by resizing image buffer
+    int numCols,                       ///< the desired number of columns in image
+    int numRows,                       ///< the desired number of rows in image
+    const psElemType type              ///< the desired datatype of the image
+);
+#else // ifdef DOXYGEN
+psImage* p_psImageRecycle(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psImage* old,                      ///< the psImage to recycle by resizing image buffer
+    int numCols,                       ///< the desired number of columns in image
+    int numRows,                       ///< the desired number of rows in image
+    const psElemType type              ///< the desired datatype of the image
+);
+#define psImageRecycle(old, numCols, numRows, type) \
+      p_psImageRecycle(__FILE__, __LINE__, __func__, old, numCols, numRows, type)
+#endif // ifdef DOXYGEN
+
+
+/** Copy an image to a new buffer
+ *
+ *  @return True if image copied or false if error
+ */
+bool p_psImageCopyToRawBuffer(
+    void* buffer,                      ///< the buffer used to copy the image
+    const psImage* input,              ///< the input image to be copied
+    psElemType type                    ///< the datatype of the image to be copied
+);
+
+
+/** Frees all children of a psImage.
+ *
+ *  @return int      Number of children freed.
+ */
+int psImageFreeChildren(
+    psImage* image                     ///< psImage in which all children shall be deallocated
+);
+
+
+/** get an element of an image as a psF64.
+ *
+ *  @return psF64   pixel value at specified location
+ */
+psF64 p_psImageGetElementF64(
+    psImage* image,                    ///< input image
+    int col,                           ///< pixel column
+    int row                            ///< pixel row
+);
+
+
+/** print image pixel values.
+ *
+ *  @return bool    TRUE is successful, otherwise FALSE.
+ */
+bool p_psImagePrint(
+    int fd,                            ///< Destination file descriptor
+    psImage *a,                        ///< image to print
+    char *name                         ///< name of the image (for title)
+);
+
+
+
+
+/*****************************************************************************
+    PS_IMAGE macros:
+*****************************************************************************/
+#define PS_ASSERT_IMAGE_NON_NULL(NAME, RVAL) PS_ASSERT_GENERAL_IMAGE_NON_NULL(NAME, return RVAL)
+#define PS_ASSERT_GENERAL_IMAGE_NON_NULL(NAME, CLEANUP) \
+if ((NAME) == NULL || (NAME)->data.V == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: psImage %s or its data is NULL.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+#define PS_ASSERT_IMAGE_NON_EMPTY(NAME, RVAL) PS_ASSERT_GENERAL_IMAGE_NON_EMPTY(NAME, return RVAL)
+#define PS_ASSERT_GENERAL_IMAGE_NON_EMPTY(NAME, CLEANUP) \
+if ((NAME)->numCols < 1 || (NAME)->numRows < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psImage %s has zero rows or columns (%dx%d).", \
+            #NAME, (NAME)->numCols, (NAME)->numRows); \
+    CLEANUP; \
+}
+
+#define PS_ASSERT_IMAGE_TYPE(NAME, TYPE, RVAL) \
+if ((NAME)->type.type != TYPE) { \
+    char *imageType, *desiredType; \
+    PS_TYPE_NAME(imageType, (NAME)->type.type); \
+    PS_TYPE_NAME(desiredType, TYPE); \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: psImage %s has incorrect type: %s instead of %s.", \
+            #NAME, imageType, desiredType); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_IMAGE_TYPE_F32_OR_F64(NAME, RVAL) \
+if ((NAME)->type.type != PS_TYPE_F32 && (NAME)->type.type != PS_TYPE_F64) { \
+    char *imageType; \
+    PS_TYPE_NAME(imageType, (NAME)->type.type); \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: psImage %s is not of type F32 or F64: %s.", \
+            #NAME, imageType); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_IMAGES_SIZE_EQUAL(NAME1, NAME2, RVAL) \
+if (((NAME1)->numCols != (NAME2)->numCols) || \
+        ((NAME1)->numRows != (NAME2)->numRows)) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psImages %s and %s are not the same size: %dx%d vs %dx%d.", \
+            #NAME1, #NAME2, (NAME1)->numCols, (NAME1)->numRows, (NAME2)->numCols, (NAME2)->numRows); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_IMAGE_SIZE(NAME1, NUM_COLS, NUM_ROWS, RVAL) \
+if (((NAME1)->numCols != NUM_COLS) || \
+        ((NAME1)->numRows != NUM_ROWS)) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psImages %s is not the correct size: %dx%d instead of %dx%d.", \
+            #NAME1, (NAME1)->numCols, (NAME1)->numRows, NUM_COLS, NUM_ROWS); \
+    return(RVAL); \
+}
+
+#define PS_IMAGE_PRINT_F32(NAME) \
+printf("======== printing %s ========\n", #NAME); \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        printf("%.2f ", (NAME)->data.F32[i][j]); \
+    } \
+    printf("\n"); \
+}\
+
+#define PS_IMAGE_PRINT_F64(NAME) \
+printf("======== printing %s ========\n", #NAME); \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        printf("%.2f ", (NAME)->data.F64[i][j]); \
+    } \
+    printf("\n"); \
+}\
+
+/// @}
+#endif // PS_IMAGE_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psScalar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psScalar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psScalar.c	(revision 22322)
@@ -0,0 +1,154 @@
+/** @file  psScalar.c
+ *
+ *  @brief Contains basic scalar definitions and operations
+ *
+ *  This file defines the basic type for a scalar struct and functions useful
+ *  in manupulating scalars.
+ * *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.29 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-07-31 23:40:12 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psScalar.h"
+#include "psLogMsg.h"
+#include "psAbort.h"
+#include "psAssert.h"
+
+
+static void scalarFree(psScalar *scalar)
+{
+    // There are non dynamic allocated items
+}
+
+// XXX this function is badly designed for int types.  we should be using a var-arg syntax so
+// we can actually pass the value correctly casted.
+psScalar* p_psScalarAlloc(const char *file,
+                          unsigned int lineno,
+                          const char *func,
+                          double value,
+                          psElemType type)
+{
+    psScalar* scalar = NULL;
+
+    // Create scalar
+    scalar = (psScalar* ) p_psAlloc(file, lineno, func, sizeof(psScalar));
+    psMemSetDeallocator(scalar, (psFreeFunc)scalarFree);
+    scalar->type.dimen = PS_DIMEN_SCALAR;
+    scalar->type.type = type;
+
+    switch (type) {
+    case PS_TYPE_S8:
+        scalar->data.S8 = (psS8) value;
+        break;
+    case PS_TYPE_U8:
+        scalar->data.U8 = (psU8) value;
+        break;
+    case PS_TYPE_S16:
+        scalar->data.S16 = (psS16) value;
+        break;
+    case PS_TYPE_U16:
+        scalar->data.U16 = (psU16) value;
+        break;
+    case PS_TYPE_S32:
+        scalar->data.S32 = (psS32) value;
+        break;
+    case PS_TYPE_U32:
+        scalar->data.U32 = (psU32) value;
+        break;
+    case PS_TYPE_S64:
+        scalar->data.S64 = (psS64) value;
+        break;
+    case PS_TYPE_U64:
+        scalar->data.U64 = (psU64) value;
+        break;
+    case PS_TYPE_F32:
+        scalar->data.F32 = (psF32) value;
+        break;
+    case PS_TYPE_F64:
+        scalar->data.F64 = (psF64) value;
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Specified datatype (%d) is unsupported by psScalar."),
+                type);
+        psFree(scalar);
+        return NULL;
+    }
+
+    return scalar;
+}
+
+
+
+bool psMemCheckScalar(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, NULL);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)scalarFree );
+}
+
+
+psScalar* p_psScalarCopy(const char *file,
+                       unsigned int lineno,
+                       const char *func,
+                       const psScalar *value)
+{
+    psElemType dataType;
+    psScalar *newScalar = NULL;
+
+    if (value == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not copy a NULL psScalar."));
+        return NULL;
+    }
+
+    dataType = value->type.type;
+    switch (dataType) {
+    case PS_TYPE_S8:
+        newScalar =  p_psScalarAlloc(file, lineno, func, value->data.S8, dataType);
+        break;
+    case PS_TYPE_U8:
+        newScalar =  p_psScalarAlloc(file, lineno, func, value->data.U8, dataType);
+        break;
+    case PS_TYPE_S16:
+        newScalar =  p_psScalarAlloc(file, lineno, func, value->data.S16, dataType);
+        break;
+    case PS_TYPE_U16:
+        newScalar =  p_psScalarAlloc(file, lineno, func, value->data.U16, dataType);
+        break;
+    case PS_TYPE_S32:
+        newScalar =  p_psScalarAlloc(file, lineno, func, value->data.S32, dataType);
+        break;
+    case PS_TYPE_U32:
+        newScalar =  p_psScalarAlloc(file, lineno, func, value->data.U32, dataType);
+        break;
+    case PS_TYPE_S64:
+        newScalar =  p_psScalarAlloc(file, lineno, func, value->data.S64, dataType);
+        break;
+    case PS_TYPE_U64:
+        newScalar =  p_psScalarAlloc(file, lineno, func, value->data.U64, dataType);
+        break;
+    case PS_TYPE_F32:
+        newScalar =  p_psScalarAlloc(file, lineno, func, value->data.F32, dataType);
+        break;
+    case PS_TYPE_F64:
+        newScalar =  p_psScalarAlloc(file, lineno, func, value->data.F64, dataType);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Specified datatype (%d) is unsupported by psScalar."),
+                dataType);
+        return NULL;
+    }
+
+    return newScalar;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psScalar.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psScalar.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psScalar.h	(revision 22322)
@@ -0,0 +1,114 @@
+/* @file  psScalar.h
+ * @brief Basic scalar definitions and operations
+ *
+ * This file defines the basic type for a scalar struct and functions useful
+ * in manupulating scalars.
+ *
+ * @author Ross Harman, MHPCC
+ * @author Joshua Hoblitt, University of Hawaii
+ *
+ * @version $Revision: 1.25 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-05 01:23:59 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_SCALAR_H
+#define PS_SCALAR_H
+
+/// @addtogroup MathOps Mathematical Operations
+/// @{
+
+#include "psType.h"
+
+# define PS_SCALAR_VALUE(DATA,TYPE) (((psScalar *)(DATA))->data.TYPE)
+
+/** Basic scalar data structure.
+ *
+ * Struct for maintaining a scalar of frequently used primitive types.
+ *
+ */
+typedef struct
+{
+    psMathType type;            ///< Type of data.
+
+    union {
+        psS8 S8;                ///< Signed 8-bit integer data.
+        psS16 S16;              ///< Signed 16-bit integer data.
+        psS32 S32;              ///< Signed 32-bit integer data.
+        psS64 S64;              ///< Signed 64-bit integer data.
+        psU8 U8;                ///< Unsigned 8-bit integer data.
+        psU16 U16;              ///< Unsigned 16-bit integer data.
+        psU32 U32;              ///< Unsigned 32-bit integer data.
+        psU64 U64;              ///< Unsigned 64-bit integer data.
+        psF32 F32;              ///< Single-precision float data.
+        psF64 F64;              ///< Double-precision float data.
+    } data;                     ///< Union for data types.
+}
+psScalar;
+
+/*****************************************************************************/
+
+/* FUNCTION PROTOTYPES                                                       */
+
+/*****************************************************************************/
+
+/** Allocate a scalar.
+ *
+ * Uses psLib memory allocation functions to create scalar data as defined by the psType type.
+ * Accepts a double for input value, as max size, but resizes according to correct type.
+ *
+ * @return psScalar*   Pointer to a new psScalar.
+ */
+#ifdef DOXYGEN
+psScalar* psScalarAlloc(
+    double value,                       ///< Data to be put into psScalar
+    psElemType type                     ///< Type of data to be held by psScalar
+);
+#else // ifdef DOXYGEN
+psScalar* p_psScalarAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    double value,                       ///< Data to be put into psScalar
+    psElemType type                     ///< Type of data to be held by psScalar
+) PS_ATTR_MALLOC;
+#define psScalarAlloc(value, type) \
+      p_psScalarAlloc(__FILE__, __LINE__, __func__, value, type)
+#endif // ifdef DOXYGEN
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psScalar structure, false otherwise.
+ */
+bool psMemCheckScalar(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Copy a scalar.
+ *
+ * Uses psLib memory allocation functions to copy a scalar.
+ *
+ * @return psScalar*    A copy of the input scalar
+ */
+#ifdef DOXYGEN
+psScalar* psScalarCopy(
+    const psScalar *value               ///< Scalar to copy
+);
+#else // ifdef DOXYGEN
+psScalar* p_psScalarCopy(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const psScalar *value               ///< Scalar to copy.
+);
+#define psScalarCopy(value) \
+      p_psScalarCopy(__FILE__, __LINE__, __func__, value)
+#endif // ifdef DOXYGEN
+
+
+/// @}
+#endif // #ifndef PS_SCALAR_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psVector.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psVector.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psVector.c	(revision 22322)
@@ -0,0 +1,1044 @@
+/** @file  psVector.c
+*
+*  @brief Contains support for basic vector types
+*
+*  This file defines the basic type for a vector struct and functions useful
+*  in manupulating vectors.
+*
+*  @author Ross Harman, MHPCC
+*  @author Robert DeSonia, MHPCC
+*  @author Joshua Hoblitt, University of Hawaii
+*
+*  @version $Revision: 1.103 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2008-09-12 00:22:48 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>                        // for memcpy
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <unistd.h>
+
+#include "psMemory.h"
+#include "psAbort.h"
+#include "psError.h"
+#include "psVector.h"
+#include "psLogMsg.h"
+#include "psCompare.h"
+#include "psAssert.h"
+#include "psString.h"
+#include "psSort.h"
+
+static void vectorFree(psVector* psVec)
+{
+    if (psVec == NULL) {
+        return;
+    }
+
+    psFree(psVec->data.U8);
+}
+
+// FUNCTION IMPLEMENTATION - PUBLIC
+bool psMemCheckVector(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)vectorFree );
+}
+
+
+// Allocate a psVector; does not set vector->n (left to the caller)
+static psVector *vectorAlloc(const char *file,
+                             unsigned int lineno,
+                             const char *func,
+                             long nalloc, // Number of elements to allocate
+                             psElemType type) // Type of elements
+{
+    int elementSize = PSELEMTYPE_SIZEOF(type); // Size, in bytes, of element
+    if (elementSize < 1) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Input psVector is an unsupported type (0x%x)."), type);
+        return NULL;
+    }
+
+    // Create vector struct
+    psVector *vector = (psVector*) p_psAlloc(file, lineno, func, sizeof(psVector));
+    psMemSetDeallocator(vector, (psFreeFunc) vectorFree);
+
+    vector->type.dimen = PS_DIMEN_VECTOR;
+    vector->type.type = type;
+    P_PSVECTOR_SET_NALLOC(vector, nalloc);
+
+    // Create vector data array
+    vector->data.U8 = psAlloc(nalloc * elementSize);
+
+    return vector;
+}
+
+psVector* p_psVectorAlloc(const char *file,
+                          unsigned int lineno,
+                          const char *func,
+                          long nalloc,
+                          psElemType type)
+{
+    psVector *vector = vectorAlloc(file, lineno, func, nalloc, type);
+    if (!vector) {
+        return NULL;
+    }
+    vector->n = nalloc;
+    return vector;
+}
+
+psVector* p_psVectorAllocEmpty(const char *file,
+                               unsigned int lineno,
+                               const char *func,
+                               long nalloc,
+                               psElemType type)
+{
+    psVector *vector = vectorAlloc(file, lineno, func, nalloc, type);
+    if (!vector) {
+        return NULL;
+    }
+    vector->n = 0;
+    return vector;
+}
+
+psVector* psVectorRealloc(psVector* vector,
+                          long nalloc)
+{
+    psS32 elementSize = 0;
+    psElemType elemType;
+
+    if (vector == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("psVectorRealloc must a given a non-NULL psVector to resize.  Desired datatype unknown."));
+        return NULL;
+    } else if (vector->nalloc != nalloc) {     // No need to realloc to same size
+        elemType = vector->type.type;
+        elementSize = PSELEMTYPE_SIZEOF(elemType);
+        if (nalloc < vector->n) {
+            vector->n = nalloc;
+        }
+        // Realloc after decrementation to avoid accessing freed array elements
+        vector->data.U8 = psRealloc(vector->data.U8, nalloc * elementSize);
+        P_PSVECTOR_SET_NALLOC(vector,nalloc);
+    }
+
+    return vector;
+}
+
+psVector* p_psVectorRecycle(const char *file,
+                            unsigned int lineno,
+                            const char *func,
+                            psVector* vector,
+                            long nalloc,
+                            psElemType type)
+{
+    psS32 byteSize;
+
+    if (vector == NULL) {
+        return p_psVectorAlloc(file, lineno, func, nalloc, type);
+    }
+
+    if (vector->type.dimen !=  PS_DIMEN_VECTOR &&
+            vector->type.dimen !=  PS_DIMEN_TRANSV) {
+        psFree(vector);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("The input psVector must have a vector dimension type."));
+        return NULL;
+    }
+
+    byteSize = nalloc * PSELEMTYPE_SIZEOF(type);
+
+    // need to increase data buffer?
+    if (byteSize > vector->nalloc*PSELEMTYPE_SIZEOF(vector->type.type)) {
+        vector->data.U8 = psRealloc(vector->data.U8, byteSize);
+        P_PSVECTOR_SET_NALLOC(vector,nalloc);
+    }
+
+    vector->type.dimen = PS_DIMEN_VECTOR;
+    vector->type.type = type;
+    vector->n = nalloc;
+    return vector;
+}
+
+psVector *psVectorExtend(psVector *vector,
+                         long delta,
+                         long nExtend)
+{
+    // can't handle a NULL vector (don't know the data type to allocate)
+    if (vector == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("The input psVector can not be NULL."));
+        return NULL;
+    }
+
+    // requirement on delta; if < 1, set to 10.
+    if (delta < 1) {
+        delta = 10;
+    }
+
+    // adjust the allocated size, if needed. (if nExtend < 1, this is will never happen)
+    if (nExtend > 0) {
+        unsigned int minAlloc = vector->n + nExtend + nExtend;
+        if (vector->nalloc < minAlloc) {
+            unsigned int nAlloc = delta + vector->nalloc;
+            // make sure the delta is large enough hold twice the extended length.
+            if (nAlloc < minAlloc) {
+                nAlloc = minAlloc;
+            }
+            vector = psVectorRealloc(vector, nAlloc);
+        }
+    } else if (nExtend < -vector->n) {
+        // For the case of a negative nExtend, need to check that we are not decreasing
+        // vector beyond its own length (i.e., creating a negative length).
+        nExtend = -vector->n;
+    }
+
+    // increment the length by the value specified
+    vector->n += nExtend;
+
+    return vector;
+}
+
+bool psVectorAppend(psVector *vector,...) {
+
+    va_list argPtr;
+    
+    int N = vector->n;
+    if (vector->nalloc == vector->n) {
+	psVectorRealloc (vector, vector->nalloc + 100);
+    }
+
+    // Get the variable list parameters to pass to allocation function
+    va_start(argPtr, vector);
+
+    switch(vector->type.type) {
+    case PS_DATA_S8:
+        vector->data.S8[N] = (psS8)va_arg(argPtr, psS32);
+        break;
+    case PS_DATA_S16:
+        vector->data.S16[N] = (psS16)va_arg(argPtr, psS32);
+        break;
+    case PS_DATA_S32:
+        vector->data.S32[N] = (psS32)va_arg(argPtr, psS32);
+        break;
+    case PS_DATA_S64:
+        vector->data.S64[N] = (psS64)va_arg(argPtr, psS64);
+        break;
+    case PS_DATA_U8:
+        vector->data.U8[N] = (psU8)va_arg(argPtr, psU32);
+        break;
+    case PS_DATA_U16:
+        vector->data.U16[N] = (psU16)va_arg(argPtr, psU32);
+        break;
+    case PS_DATA_U32:
+        vector->data.U32[N] = (psU32)va_arg(argPtr, psU32);
+        break;
+    case PS_DATA_U64:
+        vector->data.U64[N] = (psU64)va_arg(argPtr, psU64);
+        break;
+    case PS_DATA_F32:
+        vector->data.F32[N] = (psF32)va_arg(argPtr, psF64);
+        break;
+    case PS_DATA_F64:
+        vector->data.F64[N] = (psF64)va_arg(argPtr, psF64);
+        break;
+      default:
+	psAbort ("invalid data type for vector");
+    }
+
+    vector->n ++;
+    va_end(argPtr);
+
+    return true;
+}
+
+psVector* p_psVectorCopy(const char *file,
+                       unsigned int lineno,
+                       const char *func,
+                       psVector* output,
+                       const psVector* input,
+                       psElemType type)
+{
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("The input psVector can not be NULL."));
+        psFree(output);
+        return NULL;
+    }
+
+    psS32 nElements = input->n;
+
+    output = p_psVectorRecycle(file, lineno, func, output, nElements, type);
+    if (nElements == 0) {
+        //        psWarning("Warning: psVector was copied with 0 elements!\n");
+        return output;
+    }
+    output->n = nElements;
+
+    if (input->type.type == type) {
+        // Can simply copy the bytes if the types are the same
+
+        #define PSVECTOR_COPY_SAME_CASE(NAME) \
+    case PS_TYPE_##NAME: \
+        output->data.NAME = memcpy(output->data.NAME, input->data.NAME, \
+                                   input->n * PSELEMTYPE_SIZEOF(PS_TYPE_##NAME)); \
+        break;
+
+        switch (type) {
+            PSVECTOR_COPY_SAME_CASE(U8);
+            PSVECTOR_COPY_SAME_CASE(U16);
+            PSVECTOR_COPY_SAME_CASE(U32);
+            PSVECTOR_COPY_SAME_CASE(U64);
+            PSVECTOR_COPY_SAME_CASE(S8);
+            PSVECTOR_COPY_SAME_CASE(S16);
+            PSVECTOR_COPY_SAME_CASE(S32);
+            PSVECTOR_COPY_SAME_CASE(S64);
+            PSVECTOR_COPY_SAME_CASE(F32);
+            PSVECTOR_COPY_SAME_CASE(F64);
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        //                        _("Input psVector is an unsupported type (0x%x)."), typeStr);
+                        "Input psVector is an unsupported type.\n");
+                psFree(output);
+                return NULL;
+            }
+        }
+        return output;
+    }
+
+    #define PSVECTOR_COPY(INTYPE,OUTTYPE) { \
+        ps##INTYPE *inVec = input->data.INTYPE; \
+        ps##OUTTYPE *outVec = output->data.OUTTYPE; \
+        for (psS32 col=0;col<nElements;col++) { \
+            *(outVec++) = *(inVec++); \
+        } \
+    }
+
+    #define PSVECTOR_COPY_CASE(OUTTYPE) \
+case PS_TYPE_##OUTTYPE: { \
+        switch (input->type.type) { \
+        case PS_TYPE_S8: \
+            PSVECTOR_COPY(S8,OUTTYPE); \
+            break; \
+        case PS_TYPE_S16: \
+            PSVECTOR_COPY(S16,OUTTYPE); \
+            break; \
+        case PS_TYPE_S32: \
+            PSVECTOR_COPY(S32,OUTTYPE); \
+            break; \
+        case PS_TYPE_S64: \
+            PSVECTOR_COPY(S64,OUTTYPE); \
+            break; \
+        case PS_TYPE_U8: \
+            PSVECTOR_COPY(U8,OUTTYPE); \
+            break; \
+        case PS_TYPE_U16: \
+            PSVECTOR_COPY(U16,OUTTYPE); \
+            break; \
+        case PS_TYPE_U32: \
+            PSVECTOR_COPY(U32,OUTTYPE); \
+            break; \
+        case PS_TYPE_U64: \
+            PSVECTOR_COPY(U64,OUTTYPE); \
+            break; \
+        case PS_TYPE_F32: \
+            PSVECTOR_COPY(F32,OUTTYPE); \
+            break; \
+        case PS_TYPE_F64: \
+            PSVECTOR_COPY(F64,OUTTYPE); \
+            break; \
+        default: { \
+                char* typeStr; \
+                PS_TYPE_NAME(typeStr,type); \
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+                        _("Input psVector is an unsupported type (0x%s)."), \
+                        typeStr); \
+                psFree(output); \
+            } \
+        } \
+        break; \
+    }
+
+    switch (type) {
+        PSVECTOR_COPY_CASE(S8);
+        PSVECTOR_COPY_CASE(S16);
+        PSVECTOR_COPY_CASE(S32);
+        PSVECTOR_COPY_CASE(S64);
+        PSVECTOR_COPY_CASE(U8);
+        PSVECTOR_COPY_CASE(U16);
+        PSVECTOR_COPY_CASE(U32);
+        PSVECTOR_COPY_CASE(U64);
+        PSVECTOR_COPY_CASE(F32);
+        PSVECTOR_COPY_CASE(F64);
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    _("Input psVector is an unsupported type (0x%s)."),
+                    typeStr);
+            psFree(output);
+            output = NULL;
+            break;
+        }
+    }
+    return output;
+
+
+}
+
+
+// Comparison and swap functions for sorting values directly
+#define PSVECTOR_SORT_COMPARE_DIRECT(A,B) (value[A] < value[B])
+#define PSVECTOR_SORT_SWAP_DIRECT(TYPE,A,B) { \
+    if (A != B) { \
+        ps##TYPE temp = value[A]; \
+        value[A] = value[B]; \
+        value[B] = temp; \
+    } \
+}
+
+// Comparison and swap functions for sorting vector indices
+#define PSVECTOR_SORT_COMPARE_INDEX(A,B) (value[index[A]] < value[index[B]])
+#define PSVECTOR_SORT_SWAP_INDEX(TYPE,A,B) { \
+    if (A != B) { \
+        ps##TYPE temp = index[A]; \
+        index[A] = index[B]; \
+        index[B] = temp; \
+    } \
+}
+
+#define PSVECTOR_SORT_CASE(ELEMTYPE, COMPAREEXPR, SWAPFUNC, SWAPTYPE) \
+case PS_TYPE_##ELEMTYPE: { \
+    ps##ELEMTYPE *value = vector->data.ELEMTYPE; \
+    PSSORT(vector->n, COMPAREEXPR, SWAPFUNC, SWAPTYPE); \
+    break; \
+}
+
+bool psVectorSortInPlace(const psVector *vector)
+{
+    PS_ASSERT_VECTOR_NON_NULL(vector, false);
+
+    if (vector->n < 2) {
+        // Already sorted!
+        return true;
+    }
+
+    switch (vector->type.type) {
+        PSVECTOR_SORT_CASE(U8 , PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, U8 );
+        PSVECTOR_SORT_CASE(U16, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, U16);
+        PSVECTOR_SORT_CASE(U32, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, U32);
+        PSVECTOR_SORT_CASE(U64, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, U64);
+        PSVECTOR_SORT_CASE(S8 , PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, S8 );
+        PSVECTOR_SORT_CASE(S16, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, S16);
+        PSVECTOR_SORT_CASE(S32, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, S32);
+        PSVECTOR_SORT_CASE(S64, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, S64);
+        PSVECTOR_SORT_CASE(F32, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, F32);
+        PSVECTOR_SORT_CASE(F64, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, F64);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Input psVector is an unsupported type (0x%x)."),
+                vector->type.type);
+        return false;
+    }
+    return true;
+}
+
+psVector* psVectorSort(psVector* outVector,
+                       const psVector* inVector)
+{
+    if (!inVector) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("psVectorSort can not sort a NULL psVector."));
+        psFree(outVector);
+        return NULL;
+    }
+
+    if (outVector != inVector) {
+        outVector = psVectorCopy(outVector, inVector, inVector->type.type);
+        if (!outVector) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    "Error in psVectorSort:  psVectorCopy returned NULL vector!\n");
+            return NULL;
+        }
+    }
+
+    if (!psVectorSortInPlace(outVector)) {
+        if (outVector != inVector) {
+            // It was allocated here.
+            psFree(outVector);
+        }
+        return NULL;
+    }
+
+    return outVector;
+}
+
+psVector* psVectorSortIndex(psVector *out,
+                            const psVector *vector)
+{
+    PS_ASSERT_VECTOR_NON_NULL(vector, NULL);
+
+    psVector *indexVector = psVectorCreate(out, 0, vector->n, 1, PS_TYPE_S32); // Array of indices
+    psS32 *index = indexVector->data.S32; // Dereference for convenience
+
+    if (vector->n < 2) {
+        // Already sorted
+        return indexVector;
+    }
+    switch (vector->type.type) {
+        PSVECTOR_SORT_CASE(U8 , PSVECTOR_SORT_COMPARE_INDEX, PSVECTOR_SORT_SWAP_INDEX, S32);
+        PSVECTOR_SORT_CASE(U16, PSVECTOR_SORT_COMPARE_INDEX, PSVECTOR_SORT_SWAP_INDEX, S32);
+        PSVECTOR_SORT_CASE(U32, PSVECTOR_SORT_COMPARE_INDEX, PSVECTOR_SORT_SWAP_INDEX, S32);
+        PSVECTOR_SORT_CASE(U64, PSVECTOR_SORT_COMPARE_INDEX, PSVECTOR_SORT_SWAP_INDEX, S32);
+        PSVECTOR_SORT_CASE(S8 , PSVECTOR_SORT_COMPARE_INDEX, PSVECTOR_SORT_SWAP_INDEX, S32);
+        PSVECTOR_SORT_CASE(S16, PSVECTOR_SORT_COMPARE_INDEX, PSVECTOR_SORT_SWAP_INDEX, S32);
+        PSVECTOR_SORT_CASE(S32, PSVECTOR_SORT_COMPARE_INDEX, PSVECTOR_SORT_SWAP_INDEX, S32);
+        PSVECTOR_SORT_CASE(S64, PSVECTOR_SORT_COMPARE_INDEX, PSVECTOR_SORT_SWAP_INDEX, S32);
+        PSVECTOR_SORT_CASE(F32, PSVECTOR_SORT_COMPARE_INDEX, PSVECTOR_SORT_SWAP_INDEX, S32);
+        PSVECTOR_SORT_CASE(F64, PSVECTOR_SORT_COMPARE_INDEX, PSVECTOR_SORT_SWAP_INDEX, S32);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Input psVector is an unsupported type (0x%x)."),
+                vector->type.type);
+        psFree(indexVector);
+        return NULL;
+    }
+
+    return indexVector;
+}
+
+// Selection for different vector types
+#define PSVECTOR_SELECT_CASE(TYPE, COMPAREFUNC, SWAPFUNC, SWAPTYPE) \
+case PS_TYPE_##TYPE: { \
+    ps##TYPE *value = vector->data.TYPE; \
+    PSSELECT(vector->n, rank, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, SWAPTYPE); \
+    break; \
+}
+
+
+bool psVectorSelectInPlace(psVector *vector, long rank)
+{
+    PS_ASSERT_VECTOR_NON_NULL(vector, false);
+    PS_ASSERT_INT_NONNEGATIVE(rank, false);
+    PS_ASSERT_INT_LESS_THAN(rank, vector->n, false);
+
+    if (vector->n < 2) {
+        // Already sorted!
+        return true;
+    }
+
+    switch (vector->type.type) {
+        PSVECTOR_SELECT_CASE(U8 , PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, U8 );
+        PSVECTOR_SELECT_CASE(U16, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, U16);
+        PSVECTOR_SELECT_CASE(U32, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, U32);
+        PSVECTOR_SELECT_CASE(U64, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, U64);
+        PSVECTOR_SELECT_CASE(S8 , PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, S8 );
+        PSVECTOR_SELECT_CASE(S16, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, S16);
+        PSVECTOR_SELECT_CASE(S32, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, S32);
+        PSVECTOR_SELECT_CASE(S64, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, S64);
+        PSVECTOR_SELECT_CASE(F32, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, F32);
+        PSVECTOR_SELECT_CASE(F64, PSVECTOR_SORT_COMPARE_DIRECT, PSVECTOR_SORT_SWAP_DIRECT, F64);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Input psVector is an unsupported type (0x%x)."),
+                vector->type.type);
+        return false;
+    }
+    return true;
+}
+
+psVector *psVectorSelect(psVector *out, const psVector *in, long rank)
+{
+    PS_ASSERT_VECTOR_NON_NULL(in, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(rank, false);
+    PS_ASSERT_INT_LESS_THAN(rank, in->n, false);
+
+    if (out != in) {
+        out = psVectorCopy(out, in, in->type.type);
+        if (!out) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to copy vector to select.");
+            return NULL;
+        }
+    }
+
+    if (!psVectorSelectInPlace(out, rank)) {
+        if (out != in) {
+            // It was allocated here.
+            psFree(out);
+        }
+        return NULL;
+    }
+
+    return out;
+}
+
+psString psVectorToString(const psVector* vector,
+                          int maxLength)
+{
+
+    if (maxLength < 5) {
+        return NULL;
+    }
+
+    psString str = psStringAlloc(maxLength + 1);
+
+    if (vector == NULL) {
+        snprintf(str,maxLength, "NULL");
+        return str;
+    }
+
+    int size = vector->n;
+
+    if (size == 0) {
+        snprintf(str,maxLength, "[]");
+        return str;
+    }
+
+    psString tempStr = psStringAlloc(maxLength+1);
+    *str = '\0';
+    bool full = false;
+
+    #define APPEND_ELEMENTS_CASE(TYPE, NATIVE_TYPE, FORMAT) \
+case PS_TYPE_##TYPE: \
+    for (lcv=0; lcv < size && ! full; lcv++) { \
+        snprintf(tempStr, maxLength, "%s" FORMAT, prefix, (NATIVE_TYPE) (vector->data.TYPE[lcv])); \
+        strncat(str,tempStr,maxLength); \
+        full = (strlen(str) > maxLength-2); \
+        prefix = ","; \
+    } \
+    break;
+
+    int lcv;
+    char* prefix = "[";
+    switch(vector->type.type) {
+        APPEND_ELEMENTS_CASE(S8,char,"%hd")
+        APPEND_ELEMENTS_CASE(S16,short int,"%hd")
+        APPEND_ELEMENTS_CASE(S32,int,"%d")
+        APPEND_ELEMENTS_CASE(S64,long,"%ld")
+        APPEND_ELEMENTS_CASE(U8,unsigned char,"%hu")
+        APPEND_ELEMENTS_CASE(U16,unsigned short,"%hu")
+        APPEND_ELEMENTS_CASE(U32,unsigned int, "%u")
+        APPEND_ELEMENTS_CASE(U64,unsigned long,"%lu")
+        APPEND_ELEMENTS_CASE(F32,double,"%g")
+        APPEND_ELEMENTS_CASE(F64,double,"%g")
+    default:
+        snprintf(str,maxLength,"[...]");
+        break;
+    }
+
+    if (full) {
+        // couldn't all fit in given string length
+
+        // remove elements until there is room for ",...]"
+        while (strlen(str) > maxLength - 5) {
+            char* lastComma = strrchr(str,',');
+            if (lastComma == NULL) { // no comma, must be first number
+                str[1] = '\0';
+            } else {
+                *lastComma = '\0';
+            }
+        }
+        strncat(str,",...]",maxLength);
+    } else {
+        strncat(str,"]",maxLength);
+    }
+
+    psFree(tempStr);
+
+    return str;
+}
+
+psF64 p_psVectorGetElementF64(const psVector* vector,
+                              int position)
+{
+    if (vector == NULL) {
+        return NAN;
+    }
+    if (position < 0 || position >= vector->n) {
+        return NAN;
+    }
+
+    switch (vector->type.type) {
+    case PS_TYPE_U8:
+        return vector->data.U8[position];
+        break;
+    case PS_TYPE_U16:
+        return vector->data.U16[position];
+        break;
+    case PS_TYPE_U32:
+        return vector->data.U32[position];
+        break;
+    case PS_TYPE_U64:
+        return vector->data.U64[position];
+        break;
+    case PS_TYPE_S8:
+        return vector->data.S8[position];
+        break;
+    case PS_TYPE_S16:
+        return vector->data.S16[position];
+        break;
+    case PS_TYPE_S32:
+        return vector->data.S32[position];
+        break;
+    case PS_TYPE_S64:
+        return vector->data.S64[position];
+        break;
+    case PS_TYPE_F32:
+        return vector->data.F32[position];
+        break;
+    case PS_TYPE_F64:
+        return vector->data.F64[position];
+    default:
+        return NAN;
+    }
+}
+
+
+bool p_psVectorPrint (int fd,
+                      const psVector *a,
+                      char *name)
+{
+    char line[1024];
+
+    sprintf (line, "vector: %s\n", name);
+    write (fd, line, strlen(line));
+
+    for (int i = 0; i < a[0].n; i++) {
+        sprintf (line, "%f\n", p_psVectorGetElementF64(a, i));
+        write (fd, line, strlen(line));
+    }
+    sprintf (line, "\n");
+    write (fd, line, strlen(line));
+    return (true);
+}
+
+// Image initialisation for integer types
+#define VECTORINIT_INTCASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            if (value == 0.0) { \
+                memset(vector->data.TYPE, 0, vector->n * sizeof(ps##TYPE)); \
+            } else { \
+                if (value < (double)PS_MIN_##TYPE || value > (double)PS_MAX_##TYPE) { \
+                    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Error: Value %f out of range for type %s.\n", \
+                            value, #TYPE); \
+                    return false; \
+                } \
+                ps##TYPE castValue = (ps##TYPE)value; \
+                ps##TYPE *vectorData = vector->data.TYPE; \
+                for (int i = 0; i < vector->n; i++) { \
+                    vectorData[i] = castValue; \
+                } \
+            } \
+            return true; \
+        }
+
+// Image initialisation for char-size integer types
+#define VECTORINIT_CHARCASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            if (value < (double)PS_MIN_##TYPE || value > (double)PS_MAX_##TYPE) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Error: Value %f out of range for type %s.\n", \
+                        value, #TYPE); \
+                return false; \
+            } \
+            memset(vector->data.TYPE, (ps##TYPE)value, vector->n * sizeof(ps##TYPE)); \
+            return true; \
+        }
+
+// Image initialisation for floating point types
+#define VECTORINIT_FLOATCASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            if (value == 0.0) { \
+                memset(vector->data.TYPE, 0, vector->n * sizeof(ps##TYPE)); \
+            } else { \
+                ps##TYPE castValue = (ps##TYPE)value; \
+                ps##TYPE *vectorData = vector->data.TYPE; \
+                for (int i = 0; i < vector->n; i++) { \
+                    vectorData[i] = castValue; \
+                } \
+            } \
+            return true; \
+        }
+
+
+
+bool psVectorInit(psVector *vector, double value)
+{
+    PS_ASSERT_VECTOR_NON_NULL(vector, false);
+
+    switch (vector->type.type) {
+        VECTORINIT_CHARCASE(U8)
+        VECTORINIT_INTCASE(U16)
+        VECTORINIT_INTCASE(U32)
+        VECTORINIT_CHARCASE(S8)
+        VECTORINIT_INTCASE(S16)
+        VECTORINIT_INTCASE(S32)
+        VECTORINIT_INTCASE(U64)
+        VECTORINIT_INTCASE(S64)
+        VECTORINIT_FLOATCASE(F32)
+        VECTORINIT_FLOATCASE(F64)
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type %x not supported", vector->type.type);
+    }
+    return (false);
+}
+
+#define FUNC_MACRO_VECTOR_CREATE(TYPE) \
+static psVector *vectorCreate##TYPE(const char *file, unsigned int lineno, const char *func, psVector *input, double lower, double upper, double delta) \
+{ \
+    \
+    int nBin = (upper - lower) / delta; \
+    psVector *vec = p_psVectorRecycle(file, lineno, func, input, nBin, PS_TYPE_##TYPE); \
+    vec->n = nBin; \
+    for (int i = 0; i < nBin; i++) \
+    { \
+        vec->data.TYPE[i] = lower + (i * delta); \
+    } \
+    return vec; \
+} \
+
+FUNC_MACRO_VECTOR_CREATE(S8)
+FUNC_MACRO_VECTOR_CREATE(S16)
+FUNC_MACRO_VECTOR_CREATE(S32)
+FUNC_MACRO_VECTOR_CREATE(S64)
+FUNC_MACRO_VECTOR_CREATE(U8)
+FUNC_MACRO_VECTOR_CREATE(U16)
+FUNC_MACRO_VECTOR_CREATE(U32)
+FUNC_MACRO_VECTOR_CREATE(U64)
+FUNC_MACRO_VECTOR_CREATE(F32)
+FUNC_MACRO_VECTOR_CREATE(F64)
+
+psVector *p_psVectorCreate(const char *file,
+                           unsigned int lineno,
+                           const char *func,
+                           psVector *input,
+                           double lower,
+                           double upper,
+                           double delta,
+                           psElemType type)
+{
+    psVector *out = NULL;
+    switch (type) {
+    case PS_TYPE_S8:
+        out = vectorCreateS8(file, lineno, func, input, lower,  upper, delta);
+        break;
+    case PS_TYPE_S16:
+        out = vectorCreateS16(file, lineno, func, input, lower, upper, delta);
+        break;
+    case PS_TYPE_S32:
+        out = vectorCreateS32(file, lineno, func, input, lower, upper, delta);
+        break;
+    case PS_TYPE_S64:
+        out = vectorCreateS64(file, lineno, func, input, lower, upper, delta);
+        break;
+    case PS_TYPE_U8:
+        out = vectorCreateU8(file, lineno, func, input, lower, upper, delta);
+        break;
+    case PS_TYPE_U16:
+        out = vectorCreateU16(file, lineno, func, input, lower, upper, delta);
+        break;
+    case PS_TYPE_U32:
+        out = vectorCreateU32(file, lineno, func, input, lower, upper, delta);
+        break;
+    case PS_TYPE_U64:
+        out = vectorCreateU64(file, lineno, func, input, lower, upper, delta);
+        break;
+    case PS_TYPE_F32:
+        out = vectorCreateF32(file, lineno, func, input, lower, upper, delta);
+        break;
+    case PS_TYPE_F64:
+        out = vectorCreateF64(file, lineno, func, input, lower, upper, delta);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Invalid psType for Vector Create\n");
+    }
+    return (out);
+}
+
+bool psVectorSet(psVector *input,
+                 long position,
+                 double value)
+{
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psVector can not be NULL."));
+        return false;
+    }
+    if (position > input->n) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Number too large\n");
+        return false;
+    }
+    if (position < 0)
+        position += input->n;
+    if (position < 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Negative number too large\n");
+        return false;
+    }
+
+    if (position == input->n) {
+        if (position >= input->nalloc) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    "Specified position, %ld, is greater than n+1 of the vector, %ld.",
+                    position, input->nalloc);
+            return false;
+        }
+        (*(psVector**)&input)->n++;
+    }
+
+    switch (input->type.type) {
+    case PS_TYPE_U8:
+        input->data.U8[position] = (psU8)value;
+        break;
+    case PS_TYPE_U16:
+        input->data.U16[position] = (psU16)value;
+        break;
+    case PS_TYPE_U32:
+        input->data.U32[position] = (psU32)value;
+        break;
+    case PS_TYPE_U64:
+        input->data.U64[position] = (psU64)value;
+        break;
+    case PS_TYPE_S8:
+        input->data.S8[position] = (psS8)value;
+        break;
+    case PS_TYPE_S16:
+        input->data.S16[position] = (psS16)value;
+        break;
+    case PS_TYPE_S32:
+        input->data.S32[position] = (psS32)value;
+        break;
+    case PS_TYPE_S64:
+        input->data.S64[position] = (psS64)value;
+        break;
+    case PS_TYPE_F32:
+        input->data.F32[position] = (psF32)value;
+        break;
+    case PS_TYPE_F64:
+        input->data.F64[position] = (psF64)value;
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Invalid psVector Data Type\n");
+        return false;
+    }
+
+    return true;
+}
+
+double psVectorGet(const psVector *input,
+                           long position)
+{
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psVector can not be NULL."));
+        return NAN;
+    }
+    if (position >= input->n) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Number too large\n");
+        return NAN;
+    }
+    if(position < 0)
+        position += input->n;
+    if (position < 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Negative number too large\n");
+        return NAN;
+    }
+    switch (input->type.type) {
+    case PS_TYPE_U8:
+        return input->data.U8[position];
+        break;
+    case PS_TYPE_U16:
+        return input->data.U16[position];
+        break;
+    case PS_TYPE_U32:
+        return input->data.U32[position];
+        break;
+    case PS_TYPE_U64:
+        return input->data.U64[position];
+        break;
+    case PS_TYPE_S8:
+        return input->data.S8[position];
+        break;
+    case PS_TYPE_S16:
+        return input->data.S16[position];
+        break;
+    case PS_TYPE_S32:
+        return input->data.S32[position];
+        break;
+    case PS_TYPE_S64:
+        return input->data.S64[position];
+        break;
+    case PS_TYPE_F32:
+        return input->data.F32[position];
+        break;
+    case PS_TYPE_F64:
+        return input->data.F64[position];
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Invalid psVector Data Type\n");
+        return NAN;
+    }
+
+}
+
+// count number of pixels with given mask value
+long psVectorCountPixelMask (psVector *mask,
+                             psMaskType value)
+{
+    long Npixels = 0;
+    if (mask == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psVector can not be NULL."));
+        Npixels = -1;
+        return Npixels;
+    }
+
+    psElemType type;
+    type = mask->type.type;
+
+    if (type != PS_TYPE_MASK) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "psVector type does not match the specified psMaskType!\n");
+        return -1;
+    }
+
+    switch (type) {
+    case PS_TYPE_U8:
+    case PS_TYPE_U16:
+        for (long i = 0; i < mask->n; i++) {
+            if (mask->data.PS_TYPE_MASK_DATA[i] & value) {
+                Npixels ++;
+            }
+        }
+        break;
+    case PS_TYPE_S8:
+    case PS_TYPE_S16:
+    case PS_TYPE_S32:
+    case PS_TYPE_S64:
+    case PS_TYPE_U32:
+    case PS_TYPE_U64:
+    case PS_TYPE_F32:
+    case PS_TYPE_F64:
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Input psVector is an unsupported type (0x%x)."), type);
+        return -1;
+    }
+    return (Npixels);
+}
+
+long psVectorLength(const psVector *vector)
+{
+    if ( !psMemCheckVector((psVector*)vector) ) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Error:  Specified vector is not a valid psVector \n");
+        return -1;
+    }
+    return (vector->n);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psVector.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psVector.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/mathtypes/psVector.h	(revision 22322)
@@ -0,0 +1,486 @@
+/* @file  psVector.h
+ *
+ * @brief basic vector definitions and operations
+ *
+ * This file defines the basic type for a vector struct and functions useful
+ * in manupulating vectors.
+ *
+ * @author Robert DeSonia, MHPCC
+ * @author Ross Harman, MHPCC
+ * @author Joshua Hoblitt, University of Hawaii
+ *
+ * @version $Revision: 1.73 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-09-12 00:22:48 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_VECTOR_H
+#define PS_VECTOR_H
+
+/// @addtogroup MathTypes Mathematical Structures
+/// @{
+
+#include <stdio.h>
+#include "psType.h"
+#include "psMutex.h"
+
+/** An vector to support primitive types.
+ *
+ * Struct for maintaining an vector of frequently used primitive types.
+ *
+ */
+typedef struct
+{
+    psMathType type;                   ///< Type of data.
+    long n;                            ///< Number of elements in use.
+    const long nalloc;                 ///< Total number of elements available.
+    union {
+        psS8* S8;                      ///< Signed 8-bit integer data.
+        psS16* S16;                    ///< Signed 16-bit integer data.
+        psS32* S32;                    ///< Signed 32-bit integer data.
+        psS64* S64;                    ///< Signed 64-bit integer data.
+        psU8* U8;                      ///< Unsigned 8-bit integer data.
+        psU16* U16;                    ///< Unsigned 16-bit integer data.
+        psU32* U32;                    ///< Unsigned 32-bit integer data.
+        psU64* U64;                    ///< Unsigned 64-bit integer data.
+        psF32* F32;                    ///< Single-precision float data.
+        psF64* F64;                    ///< Double-precision float data.
+    } data;
+    psMutex lock;                       ///< Optional lock for thread safety.
+}
+psVector;
+
+#define P_PSVECTOR_SET_NALLOC(vec,n) *(long*)&(vec->nalloc) = n
+
+/*****************************************************************************/
+
+/* FUNCTION PROTOTYPES                                                       */
+
+/*****************************************************************************/
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr
+ *  datatype.
+ *
+ *  @return bool:       True if the pointer matches a psVector structure, false
+ *  otherwise.
+ */
+bool psMemCheckVector(
+    psPtr ptr                          ///< the pointer whose type to check
+)
+;
+
+/** Allocate a vector, with length set to number allocated
+ *
+ *  Uses psLib memory allocation functions to create a vector collection of
+ *  data as defined by the psType type.
+ *
+ * @return psVector*    Pointer to psVector.
+ */
+#ifdef DOXYGEN
+psVector* psVectorAlloc(
+    long nalloc,                       ///< Total number of elements to make available.
+    psElemType type                    ///< Type of data to be held by vector.
+);
+#else // ifdef DOXYGEN
+psVector* p_psVectorAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    long nalloc,                        ///< Total number of elements to make available.
+    psElemType type                    ///< Type of data to be held by vector.
+) PS_ATTR_MALLOC;
+#define psVectorAlloc(nalloc, type) \
+      p_psVectorAlloc(__FILE__, __LINE__, __func__, nalloc, type)
+#endif // ifdef DOXYGEN
+
+
+/** Allocate a vector, with length set to zero
+ *
+ *  Uses psLib memory allocation functions to create a vector collection of
+ *  data as defined by the psType type.
+ *
+ * @return psVector*    Pointer to psVector.
+ */
+#ifdef DOXYGEN
+psVector* psVectorAllocEmpty(
+    long nalloc,                       ///< Total number of elements to make available.
+    psElemType type                    ///< Type of data to be held by vector.
+);
+#else // ifdef DOXYGEN
+psVector* p_psVectorAllocEmpty(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    long nalloc,                       ///< Total number of elements to make available.
+    psElemType type                    ///< Type of data to be held by vector.
+) PS_ATTR_MALLOC;
+#define psVectorAllocEmpty(nalloc, type) \
+      p_psVectorAllocEmpty(__FILE__, __LINE__, __func__, nalloc, type)
+#endif // ifdef DOXYGEN
+
+
+/** Reallocate a vector.
+ *
+ *  Uses psLib memory allocation functions to reallocate a vector collection
+ *  of data. The vector is reallocated according to the psType type member
+ *  contained within the vector.
+ *
+ *  @return psVector*      Pointer to psVector.
+ *
+ */
+psVector* psVectorRealloc(
+    psVector* vector,                  ///< Vector to reallocate.
+    long nalloc                        ///< Total number of elements to make available.
+);
+
+
+/** Extend a vector's length.
+ *
+ *  Increments a vector's length, n, by the specified number of elements.
+ *  If the allocated storage is less than the current vector's length plus
+ *  twice the number of elements to be added, it is reallocated larger by
+ *  a given amount.
+ *
+ *  @return psVector*      Pointer to the adjusted psVector
+ */
+psVector *psVectorExtend(
+    psVector *vector,                  ///< Vector to extend
+    long delta,                        ///< Amount to expand allocation, if necessary
+    long nExtend                       ///< Number of elements to add to vector length
+);
+
+
+// add one more element of the vector type, extend as needed
+bool psVectorAppend(psVector *vector,...);
+
+/** Recycle a vector.
+ *
+ *  Uses psLib memory allocation functions to reallocate a vector collection
+ *  of data. The vector is reallocated according to the psElemType type
+ *  parameter.
+ *
+ * @return psVector*       Pointer to psVector.
+ *
+ */
+#ifdef DOXYGEN
+psVector* psVectorRecycle(
+    psVector* vector,
+    ///< Vector to recycle.  If NULL, a new vector is created.  No effort
+    ///< taken to preserve the values.
+
+    long nalloc,                        ///< Total number of elements to make available.
+    psElemType type                     ///< the datatype of the returned vector
+);
+#else // ifdef DOXYGEN
+psVector* p_psVectorRecycle(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psVector* vector,
+    ///< Vector to recycle.  If NULL, a new vector is created.  No effort
+    ///< taken to preserve the values.
+
+    long nalloc,                        ///< Total number of elements to make available.
+    psElemType type                     ///< the datatype of the returned vector
+);
+#define psVectorRecycle(vector, nalloc, type) \
+      p_psVectorRecycle(__FILE__, __LINE__, __func__, vector, nalloc, type)
+#endif // ifdef DOXYGEN
+
+
+/** Copy a vector, converting types.
+ *
+ *  Performs a deep copy of the elements of one psVector to a new psVector,
+ *  converting numeric types to a specified type.
+ *
+ * @return psVector*       Pointer to resulting psVector.
+ *
+ */
+#ifdef DOXYGEN
+psVector* psVectorCopy(
+    psVector* output,                  ///< if non-NULL, a psVector to recycle
+    const psVector* input,             ///< the vector to copy.
+    psElemType type                    ///< the data type of the resulting psVector
+);
+#else // ifdef DOXYGEN
+psVector* p_psVectorCopy(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psVector* output,                  ///< if non-NULL, a psVector to recycle
+    const psVector* input,             ///< the vector to copy.
+    psElemType type                    ///< the data type of the resulting psVector
+);
+#define psVectorCopy(output, input, type) \
+      p_psVectorCopy(__FILE__, __LINE__, __func__, output, input, type)
+#endif // ifdef DOXYGEN
+
+/** Sort a vector in-place
+ *
+ * Sorts in ascending order
+ *
+ * @return  bool           Success or failure
+ */
+bool psVectorSortInPlace(const psVector *vector ///< Vector to sort
+    );
+
+/** Sort an array of floats.
+ *
+ *  Sorts an array of floats in ascending order.
+ *
+ *  @return  psVector*     Pointer to sorted psVector.
+ */
+psVector* psVectorSort(
+    psVector* outVector,               ///< the output vector to recycle, or NULL if new vector desired.
+    const psVector* inVector           ///< the vector to sort.
+);
+
+
+/** Creates an array of indices based on sort ordered of array.
+ *
+ *  Sorts a vector and creates an integer array holding indices of
+ *  sorted float values based on pre-sort index positions.
+ *
+ *  @return  psVector*     vector of the indices of sort.
+ */
+psVector* psVectorSortIndex(
+    psVector* outVector,               ///< vector to recycle
+    const psVector* inVector           ///< vector to sort
+);
+
+
+/// Select a ranked element within a vector; operations are performed in-place
+///
+/// Selection orders the vector so that the element of the nominated rank is in the correct place.  No
+/// guarantee is made about other elements.
+bool psVectorSelectInPlace(psVector *vector, ///< Vector from which to select
+                           long rank    ///< Rank of interest
+    );
+
+/// Select a ranked element within a vector; operations are performed on a copy
+///
+/// Selection orders the vector so that the element of the nominated rank is in the correct place.  No
+/// guarantee is made about other elements.
+psVector *psVectorSelect(psVector *out, ///< Output vector, or NULL
+                         const psVector *in, ///< Vector from which to select
+                         long rank      ///< Rank of interest
+    );
+
+
+/** Creates a string from a psVector's values in the form "[x0,x1,x2]".
+ *
+ *  @return psPtr          a newly allocated string
+ */
+psString psVectorToString(
+    const psVector* vector,             ///< vector to create a string from
+    int maxLength                      ///< the maximum length of the resulting string
+);
+
+
+/** Returns an element in the vector as a psF64 value
+ *
+ *  @return psF64          the value at specified position, or NAN if position is invalid.
+ */
+psF64 p_psVectorGetElementF64(
+    const psVector* vector,                  ///< vector to retrieve element
+    int position                       ///< the vector position to get
+);
+
+
+/** Print a vector to a stream
+ *
+ *  @return bool          TRUE is successful, otherwise FALSE.
+ */
+bool p_psVectorPrint(
+    int fd,                            ///< output file descriptor
+    const psVector *a,                       ///< vector to print
+    char *name                         ///< name of vector (for title)
+);
+
+
+/** Initializes the vector with the given value.
+ *
+ *  The input data is cast to match the vector datatype, allowing for integers
+ *  to be preserved.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psVectorInit(
+    psVector *vector,                  ///< the vector to be initialized
+    double value                        ///< Value to which to initialise
+);
+
+
+/** Creates a new vector, or reallocates the provided vector if input is not NULL.
+ *
+ *  The created vector consists of the data range starting at lower, running to
+ *  upper, in steps of delta.  The upper-end value is exclusive; the sequence
+ *  is equivalent to for (x = lower; x <= upper - 1; x += delta).
+ *
+ *  @return psVector*:       the newly created psVector
+ */
+#ifdef DOXYGEN
+psVector *psVectorCreate(
+    psVector *input,                   ///< Input vector
+    double lower,                      ///< lower bound
+    double upper,                      ///< upper bound
+    double delta,                      ///< size of increment
+    psElemType type                    ///< type of vector to create
+);
+#else // ifdef DOXYGEN
+psVector *p_psVectorCreate(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psVector *input,                   ///< Input vector
+    double lower,                      ///< lower bound
+    double upper,                      ///< upper bound
+    double delta,                      ///< size of increment
+    psElemType type                    ///< type of vector to create
+);
+#define psVectorCreate(input, lower, upper, delta, type) \
+      p_psVectorCreate(__FILE__, __LINE__, __func__, input, lower, upper, delta, type)
+#endif // ifdef DOXYGEN
+
+
+/** Sets the value of the input vector at the specified position to value.
+ *
+ *  A negative position means index from the end.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psVectorSet(
+    psVector *input,                    ///< Input vector to set
+    long position,                      ///< vector position
+    double value                        ///< value to set
+);
+
+
+/** Returns the value of the input vector at the specified position.
+ *
+ *  A negative position means index from the end.
+ *
+ *  @return double:        Value of the input vector at the specified position.
+ */
+double psVectorGet(
+    const psVector *input,             ///< Input vector from which to get value
+    long position                      ///< vector position
+);
+
+
+/** Returns the number of pixels in the vector which satisfy any of the mask bits.
+ *
+ *  An error (eg, invalid vector) results in a return value of -1.  The vector
+ *  must be U8.
+ *
+ *  @return long:       the number of pixels counted
+ */
+long psVectorCountPixelMask(
+    psVector *mask,                    ///< input vector to count
+    psMaskType value                   ///< the mask value to satisfy
+);
+
+
+/** Get the number of elements in use from a specified psVector. (vector.n)
+ *
+ *  @return long:       The number of elements in use.
+ */
+long psVectorLength(
+    const psVector *vector             ///< input psVector
+);
+
+
+/*****************************************************************************
+    PS_VECTOR macros:
+ *****************************************************************************/
+
+#define PS_ASSERT_VECTOR_NON_NULL(NAME, RVAL) PS_ASSERT_GENERAL_VECTOR_NON_NULL(NAME, return RVAL)
+#define PS_ASSERT_GENERAL_VECTOR_NON_NULL(NAME, CLEANUP) \
+if ((NAME) == NULL || (NAME)->data.U8 == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: psVector %s or its data is NULL.", \
+            #NAME); \
+    CLEANUP; \
+} \
+
+#define PS_ASSERT_VECTOR_NON_EMPTY(NAME, RVAL) PS_ASSERT_GENERAL_VECTOR_NON_EMPTY(NAME, return RVAL)
+#define PS_ASSERT_GENERAL_VECTOR_NON_EMPTY(NAME, CLEANUP) \
+if ((NAME)->n < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psVector %s has no elements.", \
+            #NAME); \
+    CLEANUP; \
+} \
+
+#define PS_ASSERT_VECTOR_TYPE_F32_OR_F64(NAME, RVAL) \
+if (((NAME)->type.type != PS_TYPE_F32) && ((NAME)->type.type != PS_TYPE_F64)) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "psVector %s: bad type(%d)", \
+            #NAME, (NAME)->type.type); \
+    return(RVAL); \
+} \
+
+#define PS_ASSERT_VECTOR_TYPE_S16_S32_F32(NAME, RVAL) \
+if (((NAME)->type.type != PS_TYPE_S16) && ((NAME)->type.type != PS_TYPE_S32) && ((NAME)->type.type != PS_TYPE_F32)) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "psVector %s: bad type(%d)", \
+            #NAME, (NAME)->type.type); \
+    return(RVAL); \
+} \
+
+#define PS_ASSERT_VECTOR_TYPE(NAME, TYPE, RVAL) \
+if ((NAME)->type.type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: psVector %s has incorrect type.", \
+            #NAME); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_VECTORS_SIZE_EQUAL(VEC1, VEC2, RVAL) \
+if ((VEC1)->n != (VEC2)->n) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "psVector %s has size %ld, psVector %s has size %ld.", \
+            #VEC1, (VEC1)->n, #VEC2, (VEC2)->n); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_VECTOR_SIZE(VEC, SIZE, RVAL) \
+if ((VEC)->n != (SIZE)) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "psVector %s has size %ld, should be %ld.", \
+            #VEC, (VEC)->n, (SIZE)); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_VECTOR_TYPE_EQUAL(VEC1, VEC2, RVAL) \
+if ((VEC1)->type.type != (VEC2)->type.type) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "psVector %s has size %d, psVector %s has size %d.", \
+            #VEC1, (VEC1)->type.type, #VEC2, (VEC2)->type.type); \
+    return(RVAL); \
+}
+
+#define PS_VECTOR_PRINT_F32(NAME) \
+if ((NAME) != NULL) { \
+    for (int i = 0; i < (NAME)->n; i++) { \
+        printf("%s->data.F32[%d] is %f\n", #NAME, i, (NAME)->data.F32[i]); \
+    } \
+    printf("\n"); \
+} else {\
+    printf("MACRO WARNING: vector %s is NULL.\n", #NAME); \
+}\
+
+#define PS_VECTOR_PRINT_F64(NAME) \
+if ((NAME) != NULL) { \
+    for (int i = 0; i < (NAME)->n; i++) { \
+        printf("%s->data.F64[%d] is %f\n", #NAME, i, (NAME)->data.F64[i]); \
+    } \
+    printf("\n"); \
+} else {\
+    printf("MACRO WARNING: vector %s is NULL.\n", #NAME); \
+}\
+
+/// @}
+#endif // #ifndef PS_VECTOR_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/psErrorCodes_en.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/psErrorCodes_en.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/psErrorCodes_en.dat	(revision 22322)
@@ -0,0 +1,24 @@
+#
+#  This file is used to generate psErrorCode.h content
+#
+# Format:
+#    ERROR_CLASS_NAME    ERROR_CLASS_DESCRIPTION
+#
+# Note: the ERROR_CLASS_NAME will appear in the code with a "PS_ERR_" prefix
+#
+UNKNOWN                        unknown psLib error
+IO                             I/O error
+LOCATION_INVALID               specified location is unknown
+MEMORY_CORRUPTION              memory corruption detected
+MEMORY_DEREF_USAGE             dereferenced memory still used
+BAD_PARAMETER_VALUE            parameter is out-of-range
+BAD_PARAMETER_TYPE             parameter is of unsupported type
+BAD_PARAMETER_NULL             parameter is null
+BAD_PARAMETER_SIZE             size of parameter's data is outside of acceptable range.
+UNEXPECTED_NULL                unexpected NULL found
+OS_CALL_FAILED                 unexpected result from an OS standard library call.
+BAD_FITS		       file doesn't obey FITS standard
+IEEE			       a NaN or Inf was detected
+DB_CLIENT                      Database error originated in the client library
+DB_SERVER                      Database error generated by the server
+PROGRAMMING                    Known programming error
Index: /tags/sj_tags/sj_root_20080929/psLib/src/pslib.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/pslib.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/pslib.h	(revision 22322)
@@ -0,0 +1,21 @@
+/** @file  pslib.h
+*
+*  @brief Contains the complete list of header files for pslib.
+*
+*  This header file includes all the necessary header files for a user to
+*  user all public functions within the pslib library.
+*
+*  @author Eric Van Alst, MHPCC
+*
+*  @version $Revision: 1.36 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-06-08 23:40:45 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_LIB_H
+#define PS_LIB_H
+
+#include "pslib_strict.h"
+
+#endif // #ifndef PS_LIB_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/pslib_strict.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/pslib_strict.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/pslib_strict.h	(revision 22322)
@@ -0,0 +1,122 @@
+/** @file  pslib_strict.h
+*
+*  @brief Contains the complete list of header files for pslib while poisoning
+*         the use of standard memory allocation routines.
+*
+*  This header file includes all the necessary header files for a user to
+*  user all public functions within the pslib library.
+*
+*  @author Eric Van Alst, MHPCC
+*
+*  @version $Revision: 1.39 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2008-08-14 03:18:41 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_LIB_STRICT_H
+#define PS_LIB_STRICT_H
+
+#ifdef PS_LIB_H  /* this is included from pslib.h, so don't poison anything */
+#ifndef PS_ALLOW_MALLOC
+#define PS_ALLOW_MALLOC
+#endif // #ifndef PS_ALLOW_MALLOC
+#else
+#undef PS_ALLOW_MALLOC /* don't allow code to not poison malloc, i.e., strict poisioning */
+#endif // #else
+
+#include "psTime.h"
+#include "psCoord.h"
+#include "psSphereOps.h"
+#include "psEarthOrientation.h"
+
+#include "psDB.h"
+
+#include "psFFT.h"
+#include "psVectorFFT.h"
+#include "psImageFFT.h"
+
+#include "psFits.h"
+#include "psFitsHeader.h"
+#include "psFitsImage.h"
+#include "psFitsTable.h"
+#include "psFitsFloat.h"
+#include "psFitsFloatFile.h"
+#include "psFitsScale.h"
+
+//#include "psXML.h"
+
+#include "psRegion.h"
+#include "psImageInterpolate.h"
+#include "psImageConvolve.h"
+#include "psImageGeomManip.h"
+#include "psImagePixelExtract.h"
+#include "psImagePixelManip.h"
+#include "psImagePixelInterpolate.h"
+#include "psImageStats.h"
+#include "psImageStructManip.h"
+#include "psImageMaskOps.h"
+#include "psImageBinning.h"
+#include "psImageUnbin.h"
+#include "psImageMap.h"
+#include "psImageMapFit.h"
+
+#include "psImageJpeg.h"
+
+#include "psAssert.h"
+#include "psBinaryOp.h"
+#include "psCompare.h"
+#include "psConstants.h"
+#include "psExit.h"
+#include "psMatrix.h"
+#include "psMD5.h"
+#include "psMinimizeLMM.h"
+#include "psMinimizePowell.h"
+#include "psMinimizePolyFit.h"
+#include "psMutex.h"
+#include "psRandom.h"
+#include "psRegionForImage.h"
+#include "psPolynomial.h"
+#include "psPolynomialMetadata.h"
+#include "psPolynomialUtils.h"
+#include "psPolynomialMD.h"
+#include "psSort.h"
+#include "psSpline.h"
+#include "psStats.h"
+#include "psHistogram.h"
+#include "psUnaryOp.h"
+#include "psMathUtils.h"
+#include "psImage.h"
+#include "psScalar.h"
+#include "psVector.h"
+#include "psAbort.h"
+#include "psConfigure.h"
+#include "psError.h"
+#include "psErrorCodes.h"
+#include "psLogMsg.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psLine.h"
+#include "psTrace.h"
+#include "psType.h"
+#include "psArray.h"
+#include "psBitSet.h"
+#include "psHash.h"
+#include "psList.h"
+#include "psLookupTable.h"
+#include "psMetadata.h"
+#include "psMetadataConfig.h"
+#include "psMetadataItemParse.h"
+#include "psMetadataItemCompare.h"
+#include "psPixels.h"
+#include "psArguments.h"
+#include "psVectorSmooth.h"
+#include "psImageBackground.h"
+#include "psEllipse.h"
+#include "psSparse.h"
+#include "psSlurp.h"
+#include "psTree.h"
+
+#include "psThread.h"
+
+#endif // #ifndef PS_LIB_STRICT_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/.cvsignore	(revision 22322)
@@ -0,0 +1,12 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+psErrorCodes.c
+psErrorCodes.h
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/Makefile.am	(revision 22322)
@@ -0,0 +1,50 @@
+#Makefile for sys functions of psLib
+#
+noinst_LTLIBRARIES = libpslibsys.la
+
+libpslibsys_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(CFITSIO_CFLAGS)
+libpslibsys_la_SOURCES = \
+	psAbort.c \
+	psConfigure.c  \
+	psError.c      \
+	psErrorCodes.c \
+	psLine.c       \
+	psLogMsg.c     \
+	psMemory.c     \
+	psSlurp.c      \
+	psString.c     \
+	psThread.c     \
+	psTrace.c      \
+	psType.c       \
+	strcasestr.c
+
+EXTRA_DIST = sys.i psErrorCodes.c.in psErrorCodes.h.in
+
+BUILT_SOURCES = psErrorCodes.c
+
+psErrorCodes.c: ../psErrorCodes_$(PS_LANG).dat psErrorCodes.c.in psErrorCodes.h
+	$(top_srcdir)/utils/psParseErrorCodes --data=../psErrorCodes_$(PS_LANG).dat $@
+
+psError.h: psErrorCodes.h
+
+psErrorCodes.h: ../psErrorCodes_$(PS_LANG).dat psErrorCodes.h.in
+	$(top_srcdir)/utils/psParseErrorCodes --data=../psErrorCodes_$(PS_LANG).dat $@
+
+pkginclude_HEADERS = \
+	psAbort.h \
+	psAssert.h \
+	psConfigure.h  \
+	psError.h      \
+	psErrorCodes.h \
+	psExit.h       \
+	psLine.h       \
+	psLogMsg.h     \
+	psMemory.h     \
+	psMutex.h      \
+	psSlurp.h      \
+	psString.h     \
+	psThread.h     \
+	psTrace.h      \
+	psType.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psAbort.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psAbort.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psAbort.c	(revision 22322)
@@ -0,0 +1,74 @@
+
+/** @file  psAbort.c
+ *
+ *  @brief Contains the definition for abort function
+ *
+ *  The abort logging and handling shall be performed by psAbort function.
+ *  This will allow for consistent handling of other software units
+ *  needing to abort from program execution.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *  @author Joshua Hoblitt, University of Hawaii
+ *   
+ *  @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-04-13 08:18:27 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "psAbort.h"
+#include "psError.h"
+#include "psLogMsg.h"
+
+void p_psAbort(const char *file,
+               unsigned int lineno,
+               const char *func,
+               const char *format,
+               ...)
+{
+    psErrorStackPrint(stderr, "Aborting. Error stack:");
+
+    va_list argPtr;             // variable list arguement pointer
+    // Get the variable list parameters to pass to logging function
+    va_start(argPtr, format);
+
+    // Call logging function with PS_LOG_ABORT level
+    psLogMsgV("psLib.sys", PS_LOG_ABORT, format, argPtr);
+
+    // Clean up stack after variable arguement has been used
+    va_end(argPtr);
+
+    // Call system abort function to terminate program execution
+    abort();
+}
+
+void p_psAssert(const char *file,
+		unsigned int lineno,
+		const char *func,
+		const bool value,
+		const char *format,
+		...)
+{
+    if (value) return;
+    psErrorStackPrint(stderr, "Aborting. Error stack:");
+
+    va_list argPtr;             // variable list arguement pointer
+    // Get the variable list parameters to pass to logging function
+    va_start(argPtr, format);
+
+    // Call logging function with PS_LOG_ABORT level
+    psLogMsgV("psLib.sys", PS_LOG_ABORT, format, argPtr);
+
+    // Clean up stack after variable arguement has been used
+    va_end(argPtr);
+
+    // Call system abort function to terminate program execution
+    abort();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psAbort.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psAbort.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psAbort.h	(revision 22322)
@@ -0,0 +1,88 @@
+/** @file  psAbort.h
+ *
+ *  @brief Contains the declarations for the abort function
+ *
+ *  The abort logging and handling shall be performed by psAbort function.
+ *  This will allow for consistent handling of other software units
+ *  needing to abort from program execution.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *  @author Joshua Hoblitt, University of Hawaii
+ *
+ *  $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ *  $Date: 2008-04-13 08:18:27 $
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ABORT_H
+#define PS_ABORT_H
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+#include <stdarg.h>
+
+#include "psType.h"
+
+/** Reports an abort message to logging facility
+ *
+ *  This function will invoke the psLogMsg function with a level of
+ *  PS_LOG_ABORT and pass the parameters name and fmt to generate a proper
+ *  log message.  After logging, this function will call system abort
+ *  function to abnormally terminate the program.
+ *
+ *  @return  void No return value
+ *
+ */
+#ifdef DOXYGEN
+void psAbort(
+    const char *format,                 ///< A printf style formatting statement
+    ...
+);
+#else // ifdef DOXYGEN
+void p_psAbort(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const char *format,                 ///< A printf style formatting statement
+    ...
+) PS_ATTR_FORMAT(printf, 4, 5) PS_ATTR_NORETURN;
+#ifndef SWIG
+#define psAbort(...) \
+      p_psAbort(__FILE__, __LINE__, __func__, __VA_ARGS__)
+#endif // iddef SWIG
+#endif // ifdef DOXYGEN
+
+/** Reports an abort message to logging facility
+ *
+ *  This function will invoke the psLogMsg function with a level of
+ *  PS_LOG_ABORT and pass the parameters name and fmt to generate a proper
+ *  log message.  After logging, this function will call system abort
+ *  function to abnormally terminate the program.
+ *
+ *  @return  void No return value
+ *
+ */
+#ifdef DOXYGEN
+void psAssert(
+    const bool value,
+    const char *format,                 ///< A printf style formatting statement
+    ...
+);
+#else // ifdef DOXYGEN
+void p_psAssert(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const bool value,
+    const char *format,                 ///< A printf style formatting statement
+    ...
+) PS_ATTR_FORMAT(printf, 5, 6);
+#ifndef SWIG
+#define psAssert(VALUE, ...) \
+      p_psAssert(__FILE__, __LINE__, __func__, (VALUE), __VA_ARGS__)
+#endif // iddef SWIG
+#endif // ifdef DOXYGEN
+
+/// @}
+#endif // #ifndef PS_ABORT_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psAssert.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psAssert.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psAssert.h	(revision 22322)
@@ -0,0 +1,279 @@
+#ifndef PS_ASSERT_H
+#define PS_ASSERT_H
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+#include <assert.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include "psError.h"
+#include "psLogMsg.h"
+
+// these two asserts can be used in the middle of a function to test for programming errors
+#define PS_ASSERT(VAR, RVAL) \
+if (!(VAR)) { \
+    psError(PS_ERR_PROGRAMMING, false, "Error: %s is not true.", #VAR); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_INT_UNEQUAL(NAME1, NAME2, RVAL) \
+if ((NAME1) == (NAME2)) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_INT_UNEQUAL(NAME1, NAME2, RVAL) \
+if ((NAME1) == (NAME2)) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_INT_EQUAL(NAME1, NAME2, RVAL) \
+if ((NAME1) != (NAME2)) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are not equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_INT_NONNEGATIVE(NAME, RVAL) \
+if ((NAME) < 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Error: %s is less than 0.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_INT_POSITIVE(NAME, RVAL) \
+if ((NAME) < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s is 0 or less.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_INT_ZERO(NAME, RVAL) \
+if ((NAME) != 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s is 0.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_INT_NONZERO(NAME, RVAL) \
+if ((NAME) == 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s is 0.", #NAME); \
+    return(RVAL); \
+}
+
+// XXX: Where did these int casts come from?
+#define PS_ASSERT_INT_WITHIN_RANGE(NAME, LOWER, UPPER, RVAL) \
+if ((int)(NAME) < LOWER || (int)(NAME) > UPPER) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s, %ld, is out of range.  Must be between %ld and %ld.", \
+            #NAME,(long)(NAME),(long)(LOWER),(long)(UPPER)); \
+    return RVAL; \
+}
+
+#define PS_ASSERT_INT_LESS_THAN(VAR1, VAR2, RVAL) \
+if (!(VAR1 < VAR2)) { \
+    psError(PS_ERR_UNKNOWN, true, \
+            "Error: %s is not less than %s (%ld, %ld)", #VAR1, #VAR2, (long)(VAR1), (long)(VAR2)); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_INT_LESS_THAN_OR_EQUAL(VAR1, VAR2, RVAL) \
+if (!(VAR1 <= VAR2)) { \
+    psError(PS_ERR_UNKNOWN, true, \
+            "Error: %s is not less than %s (%ld, %ld)", #VAR1, #VAR2, (long)(VAR1), (long)(VAR2)); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_INT_LARGER_THAN(NAME1, NAME2, RVAL) \
+if (!((NAME1) > (NAME2))) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: !(%s > %s) (%ld %ld).", \
+            #NAME1, #NAME2,(long)(NAME1), (long)(NAME2)); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(NAME1, NAME2, RVAL) \
+if (!((NAME1) >= (NAME2))) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: !(%s >= %s) (%ld %ld).", \
+            #NAME1, #NAME2, (long)(NAME1), (long)(NAME2)); \
+    return(RVAL); \
+}
+#define PS_ASSERT_FLOAT_LARGER_THAN(NAME1, NAME2, RVAL) \
+if (!((NAME1) > (NAME2))) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: !(%s > %s) (%lf %lf).", \
+            #NAME1, #NAME2, (double)(NAME1), (double)(NAME2)); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(NAME1, NAME2, RVAL) \
+if (!((NAME1) >= (NAME2))) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: !(%s >= %s) (%lf %lf).", \
+            #NAME1, #NAME2, (double)(NAME1), (double)(NAME2)); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_FLOAT_LESS_THAN(NAME1, NAME2, RVAL) \
+if (!((NAME1) < (NAME2))) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: !(%s < %s) (%lf %lf).", \
+            #NAME1, #NAME2, (double)(NAME1), (double)(NAME2)); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(NAME1, NAME2, RVAL) \
+if (!((NAME1) <= (NAME2))) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: !(%s <= %s) (%lf %lf).", \
+            #NAME1, #NAME2, (double)(NAME1), (double)(NAME2)); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_FLOAT_NON_EQUAL(NAME1, NAME2, RVAL) \
+if (fabs((NAME2) - (NAME1)) < FLT_EPSILON) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_FLOAT_EQUAL(NAME1, NAME2, RVAL) \
+if (fabs((NAME2) - (NAME1)) > FLT_EPSILON) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are not equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+// Return an error if the arg lies outside the supplied range.
+#define PS_ASSERT_FLOAT_WITHIN_RANGE(NAME, LOWER, UPPER, RVAL) \
+if ((NAME) < (LOWER) || (NAME) > (UPPER)) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s, %f, is out of range.  Must be between %lf and %lf.", \
+            #NAME, NAME, (double)(LOWER), (double)(UPPER)); \
+    return RVAL; \
+}
+
+#define PS_ASSERT_FLOAT_REAL(NAME, RVAL) \
+if (!isfinite(NAME)) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s=%lf is not a real value.\n", \
+            #NAME, (double)(NAME)); \
+    return RVAL; \
+}
+
+#define PS_ASSERT_DOUBLE_WITHIN_RANGE(NAME, LOWER, UPPER, RVAL) \
+if ((NAME) < (LOWER) || (NAME) > (UPPER)) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s, %lf, is out of range.  Must be between %lf and %lf.", \
+            #NAME, NAME, LOWER, UPPER); \
+    return RVAL; \
+}
+
+#define PS_ASSERT_LONG_WITHIN_RANGE(NAME, LOWER, UPPER, RVAL) \
+if ((NAME) < (LOWER) || (NAME) > (UPPER)) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s, %ld, is out of range.  Must be between %ld and %ld.", \
+            #NAME, NAME, LOWER, UPPER); \
+    return RVAL; \
+}
+
+#define PS_ASSERT_LONG_LARGER_THAN_OR_EQUAL(NAME1, NAME2, RVAL) \
+if (!((NAME1) >= (NAME2))) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Error: !(%s >= %s) (%ld %ld).",\
+            #NAME1, #NAME2, NAME1, NAME2); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_S64_WITHIN_RANGE(NAME, LOWER, UPPER, RVAL) \
+if ((NAME) < (LOWER) || (NAME) > (UPPER)) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s, %" PRId64 ", is out of range.  Must be between %" PRId64 " and %" PRId64 ".", \
+            #NAME, NAME, LOWER, UPPER); \
+    return RVAL; \
+}
+
+/*****************************************************************************
+Macros which take a generic psLib type and determine if it is NULL, or has
+the wrong type.
+*****************************************************************************/
+#define PS_WARN_PTR_NON_NULL(NAME) \
+if ((NAME) == NULL) { \
+    psLogMsg(__func__, PS_LOG_WARN, "WARNING: %s is NULL.", #NAME); \
+} \
+
+#define PS_ASSERT_PTR(NAME, RVAL)
+
+#define PS_ASSERT_PTR_NON_NULL(NAME, RVAL) PS_ASSERT_GENERAL_PTR_NON_NULL(NAME, return RVAL)
+#define PS_ASSERT_GENERAL_PTR_NON_NULL(NAME, CLEANUP) \
+if ((NAME) == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: %s is NULL.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+#define PS_ASSERT_PTR_NULL(NAME, RVAL) \
+if ((NAME) != NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: %s is not NULL.", \
+            #NAME); \
+    return RVAL; \
+}
+
+#define PS_ASSERT_PTR_TYPE(NAME, TYPE, RVAL) \
+if ((NAME)->type.type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: %s has incorrect type.", \
+            #NAME); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_PTR_DIMEN(NAME, DIMEN, RVAL) PS_ASSERT_GENERAL_PTR_DIMEN(NAME, DIMEN, return RVAL)
+#define PS_ASSERT_GENERAL_PTR_DIMEN(NAME, DIMEN, CLEANUP) \
+if ((NAME)->type.dimen != DIMEN) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: %s has incorrect dimensionality.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+#define PS_ASSERT_PTR_DIMEN_GENERAL_NOT(NAME, DIMEN, CLEANUP) \
+if ((NAME)->type.dimen == DIMEN) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: %s has incorrect dimensionality.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+
+#define PS_ASSERT_PTRS_SIZE_EQUAL(PTR1, PTR2, RVAL) \
+if (PTR1->n != PTR2->n) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "ptr %s has size %d, ptr %s has size %d.", \
+            #PTR1, PTR1->n, #PTR2, PTR2->n); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_PTR_TYPE_EQUAL(PTR1, PTR2, RVAL) PS_ASSERT_GENERAL_PTR_TYPE_EQUAL(PTR1, PTR2, return RVAL)
+#define PS_ASSERT_GENERAL_PTR_TYPE_EQUAL(PTR1, PTR2, CLEANUP) \
+if (PTR1->type.type != PTR2->type.type) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "ptr %s has type %d, ptr %s has type %d.", \
+            #PTR1, PTR1->type.type, #PTR2, PTR2->type.type); \
+    CLEANUP; \
+}
+
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psConfigure.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psConfigure.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psConfigure.c	(revision 22322)
@@ -0,0 +1,149 @@
+/** @file  psConfigure.c
+ *
+ *  @brief Contains the declarations for initialization, memory finalization,
+ *   and configuration.
+ *
+ *  These functions initalize psLib data before the beginning of a run and
+ *  remove (finalize) the same data after the run is complete. A function is
+ *  also provided to return the current psLib version.
+ *
+ *  @ingroup Configure
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.28 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-08 18:05:08 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "psAbort.h"
+#include "psTrace.h"
+#include "psString.h"
+#include "psTime.h"
+#include "psThread.h"
+#include "psEarthOrientation.h"
+#include "psError.h"
+#include "psFFT.h"
+#include "psConfigure.h"
+#include "psMemory.h"
+
+static char *memCheckName = NULL;       // Filename to which to write results of mem check
+static FILE *memCheckFile = NULL;       // File to which to write results of mem check
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $"; // CVS tag name
+
+psString psLibVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString psLibVersionLong(void)
+{
+    psString version = psLibVersion();    // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+
+#ifdef HAVE_PSDB
+    psStringAppend(&version, " (cvs tag %s), %s, %s with psDB", tag, __DATE__, __TIME__);
+#else
+    psStringAppend(&version, " (cvs tag %s), %s, %s without psDB", tag, __DATE__, __TIME__);
+#endif
+    psFree(tag);
+    return version;
+}
+
+// Check the memory; intended for use on exit, but might be used elsewhere
+void p_psMemoryCheck(void)
+{
+    if (!memCheckName || strlen(memCheckName) == 0) {
+        return;
+    }
+
+    memCheckFile = fopen(memCheckName, "w"); // File to write leaks to
+    if (!memCheckFile) {
+        psError(PS_ERR_IO, true, "Unable to open leaks file, %s\n", memCheckName);
+        return;
+    }
+
+    int nLeaks = psMemCheckLeaks(0, NULL, memCheckFile, false); // Number of leaks
+    if (nLeaks > 0) {
+        psWarning("%d memory leaks found; list written to %s.\n", nLeaks, memCheckName);
+    } else {
+        psLogMsg(__func__, PS_LOG_INFO, "No memory leaks found.\n");
+    }
+
+    int nCorrupted;                     // Number of corrupted memory blocks
+    nCorrupted = psMemCheckCorruption(memCheckFile, false);
+    if (nCorrupted > 0) {
+        psWarning("%d memory blocks corrupted; list written to %s.\n", nCorrupted, memCheckName);
+    } else {
+        psLogMsg(__func__, PS_LOG_INFO, "No memory corruption found.\n");
+    }
+
+    fclose(memCheckFile);
+
+    return;
+}
+
+
+bool psLibInit(const char* timeConfig)
+{
+    // XXX: Still needs error codes to be set
+
+    if (timeConfig && strlen(timeConfig) > 0) {
+        if (!psTimeInit(timeConfig)) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Failed to initialize %s."), "psTime");
+            return false;
+        }
+    }
+
+    // Does the user want memory checking at exit?
+    memCheckName = getenv("PS_ALLOC_CHECK"); // The value of PS_ALLOC_CHECK
+    if (memCheckName && strlen(memCheckName) > 0) {
+        atexit(&p_psMemoryCheck);
+    }
+
+    return true;
+}
+
+void psLibFinalize(void)
+{
+    // Users of persistent memory should free them in this function
+
+    // Stop timers
+    psTimerStop();
+
+    // Clean up FFTW threads
+    psFFTThreads(0);
+
+    // Clean up threads
+    psThreadPoolFinalize();
+
+    // Free the time tables
+    if (!p_psTimeFinalize()) {
+        psAbort(_("Failed to finalize psTime."));
+    }
+
+    // Free the precession tables
+    if (!p_psEOCFinalize()) {
+        psAbort(_("Failed to finalize psEOC."));
+    }
+
+    // Free the trace system
+    psTraceReset();
+
+    // Free the error system
+    psErrorClear();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psConfigure.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psConfigure.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psConfigure.h	(revision 22322)
@@ -0,0 +1,74 @@
+/** @file  psConfigure.h
+ *
+ *  @brief Functions to init and cleanup psLib
+ *
+ *  These functions initalize psLib data before the beginning of a run and
+ *  remove (finalize) the same data after the run is complete.  A function is
+ *  also provided to return the current psLib version.
+ *
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-06-22 02:28:48 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_CONFIGURE_H
+#define PS_CONFIGURE_H
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+/** Get current psLib version
+ *
+ *  Returns the current psLib version name as a string.
+ *
+ *  @return psString: String with version name.
+ */
+psString psLibVersion(
+    void
+);
+
+
+/** Get current psLib version (full identification)
+ *
+ *  Returns the current psLib version name and other information identifying the compilation.
+ *
+ *  @return psString: String with identity.
+ */
+psString psLibVersionLong(void);
+
+
+/** Initializes persistent memory.
+ *
+ *  Creates persistant memory items used throughout psLib. Items created
+ *  within this method should be freed with the psLibFinalize function.
+ *  current, a non-NULL psErr is returned with code PS_ERR_NONE.
+ *
+ */
+bool psLibInit(
+    const char* timeConfig           ///< Filename of config file for psTime.
+);
+
+
+/** Removes persistant memory created with the psLibInit function.
+ *
+ *  The memory created but not freed by psLib modules should be freed
+ *  within this function at the end of a psLib execution cycle.
+ *
+ *  @return void: void.
+ */
+void psLibFinalize(
+    void
+);
+
+
+// Check the memory; intended for use on exit, but might be used elsewhere
+void p_psMemoryCheck(void);
+
+
+/// @}
+#endif // #ifndef PS_CONFIGURE_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psError.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psError.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psError.c	(revision 22322)
@@ -0,0 +1,327 @@
+/** @file  psError.c
+ *
+ *  @brief Contains the definitions for the error reporting functions
+ *
+ *  Error reporting functions shall be used to create log entries in the
+ *  event errors are detected.  The messages shall give enough information
+ *  to allow the user to know where the error has occurred and the type
+ *  of error detected.
+ *
+ *  @author Joshua Hoblitt, University of Hawaii
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.48 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-12 22:53:34 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdarg.h>
+#include <pthread.h>
+#include <string.h>
+
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psString.h"
+#include "psTrace.h"
+#include "psAbort.h"
+#include "psMemory.h"
+
+#define MAX_STRING_LENGTH 2048
+#define MAX_ERROR_STACK_SIZE 64
+
+static pthread_mutex_t lockErrorStack = PTHREAD_MUTEX_INITIALIZER;
+static bool errorStackKeyInitialized = false;
+static pthread_key_t errorStack_key;
+
+static void psFreeWrapper(void *ptr);
+static void psErrorStackPush(psErr* err);
+static psArray *psErrorStackGet(void);
+static void psErrFree(psErr* err);
+
+// needed for pthread_key_create() because p_psFree() does not match free()'s
+// prototype
+// XXX does something like this need to be global in psMmemory.h
+static void psFreeWrapper(void *ptr)
+{
+    psFree(ptr);
+}
+
+static void psErrorStackPush(psErr* err)
+{
+    psArray *errorStack = psErrorStackGet();
+
+    // push the item onto the stack and increment the error count
+    if (psArrayLength(errorStack) < MAX_ERROR_STACK_SIZE) {
+        // make the psErr persistent
+        psMemSetPersistent(err, true);
+        psMemSetPersistent(err->msg, true);
+        psMemSetPersistent(err->name, true);
+
+        psArrayAdd(errorStack, 0, err);
+    } else {
+        psAbort("attempt to exceed maximum error stack depth of %ld",
+                (long)MAX_ERROR_STACK_SIZE);
+    }
+}
+
+static psArray *psErrorStackGet(void)
+{
+    // check to see if the error stack key has been initialized
+    pthread_mutex_lock(&lockErrorStack);
+    if (errorStackKeyInitialized == false) {
+        // note that each error stack will automatically be free'd when it's
+        // thread exits
+        if (pthread_key_create(&errorStack_key, psFreeWrapper)) {
+            psAbort("pthread_key_create failed()");
+        }
+        errorStackKeyInitialized = true;
+    }
+    pthread_mutex_unlock(&lockErrorStack);
+
+    // check to see if the error stack for this thread has been allocated
+    psArray *errorStack = NULL;
+    if ((errorStack = pthread_getspecific(errorStack_key)) == NULL) {
+        // allocate the error stack
+        errorStack = psArrayAllocEmpty(MAX_ERROR_STACK_SIZE);
+        psMemSetPersistent(errorStack, true);
+        psMemSetPersistent(errorStack->data, true);
+        // store this threads error stack
+        // note that pthread_setspecifc() does not take a pointer as the first
+        // param
+        if (pthread_setspecific(errorStack_key, errorStack)) {
+            psAbort("pthread_setspecific() failed");
+        }
+    }
+
+    return errorStack;
+}
+
+// This function serves the same purpose of psErrorStackGet() accept it does
+// not alloc the error stack if it DOES NOT already exist.  Unlike
+// psErrorStackGet(), this function is NOT guarenteed to return a valid pointer
+// and it's return status must be checked.
+static psArray *psErrorStackGetNoAlloc(void)
+{
+    // check to see if the error stack key has been initialized
+    pthread_mutex_lock(&lockErrorStack);
+    if (errorStackKeyInitialized == false) {
+        pthread_mutex_unlock(&lockErrorStack);
+        return NULL;
+    }
+    pthread_mutex_unlock(&lockErrorStack);
+
+    // check to see if the error stack for this thread has been allocated
+    psArray *errorStack = NULL;
+    if ((errorStack = pthread_getspecific(errorStack_key)) == NULL) {
+        return NULL;
+    }
+
+    return errorStack;
+}
+
+static void psErrFree(psErr* err)
+{
+    if (err != NULL) {
+        psFree(err->msg);
+        psFree(err->name);
+    }
+}
+
+psErr* psErrAlloc(const char* name, psErrorCode code, const char* msg)
+{
+    psErr* err = psAlloc(sizeof(psErr));
+    err->msg = psStringCopy(msg);
+    err->name = psStringCopy(name);
+    err->code = code;
+
+    psMemSetDeallocator(err, (psFreeFunc)psErrFree);
+
+    return err;
+}
+
+psErrorCode p_psErrorV(const char* filename,
+		       unsigned int lineno,
+		       const char* func,
+		       psErrorCode code,
+		       bool new,
+		       const char* format,
+		       va_list ap)
+{
+  char errMsg[MAX_STRING_LENGTH];
+    char msgName[MAX_STRING_LENGTH];
+
+    // if this the origin of a new error reset the error stack
+    if (new) {
+        psErrorClear();
+    }
+
+    snprintf(msgName, MAX_STRING_LENGTH, "%s (%s:%d)", func, filename, lineno);
+    vsnprintf(errMsg, MAX_STRING_LENGTH, format, ap);
+
+    // Remove a single trailing \n from message -- it interferes with
+    // psErrorStackPrint
+    size_t len = strlen(errMsg);
+    if (len > 0 && errMsg[len - 1] == '\n') {
+        errMsg[len - 1] = '\0';
+    }
+
+    psErr *err = psErrAlloc(msgName, code, errMsg);
+    psErrorStackPush(err);
+
+    #ifndef PS_NO_TRACE
+    // Call tracing function with PS_LOG_ERROR level
+    // p_psTrace() automatically appends the the function name to the facility
+    // for us
+    p_psTrace(__FILE__, __LINE__, func, "err", PS_LOG_ERROR, "%s : %s", err->name, err->msg);
+    #endif
+
+    psFree(err);
+
+    return code;
+}
+
+psErrorCode p_psError(const char* filename,
+                      unsigned int lineno,
+                      const char* func,
+                      psErrorCode code,
+                      bool new,
+                      const char* format,
+                      ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    p_psErrorV(filename, lineno, func, code, new, format, ap);
+    va_end(ap);
+    return code;
+}
+
+void p_psWarning(const char* file,
+                 int lineno,
+                 const char* func,
+                 const char* format,
+                 ...)
+{
+    char msgName[MAX_STRING_LENGTH];
+
+    snprintf(msgName, MAX_STRING_LENGTH, "%s (%s:%d)", func, file, lineno);
+
+    va_list ap;
+    va_start(ap, format);
+
+    psLogMsgV(msgName, PS_LOG_WARN, format, ap);
+
+    va_end(ap);
+
+    return;
+}
+
+psErr* psErrorGet(long which)
+{
+    psErr* result;
+
+    psArray *errorStack = psErrorStackGet();
+
+    // Check for negative reference and if found return PS_ERR_NONE
+    if (which < 0 ) {
+        result = psErrAlloc("", PS_ERR_NONE, "");
+    } else {
+        // the which input is from the end of errorStack
+        which = psArrayLength(errorStack) - 1 - which;
+        if (which < 0 || which >= psArrayLength(errorStack)) {
+            // no error at the given location
+            result = psErrAlloc("", PS_ERR_NONE, "");
+        } else {
+            // a new reference passed back
+            result = psMemIncrRefCounter(errorStack->data[which]);
+        }
+    }
+
+    return result;
+}
+
+long psErrorGetStackSize()
+{
+    psArray *errorStack = psErrorStackGet();
+
+    return psArrayLength(errorStack);
+}
+
+psErr* psErrorLast(void)
+{
+    return psErrorGet(0);
+}
+
+psErrorCode psErrorCodeLast(void)
+{
+    psErr *err = psErrorGet(0);
+    psErrorCode code = err->code;
+    psFree(err);
+
+    return code;
+}
+
+void psErrorClear(void)
+{
+    psArray *errorStack = psErrorStackGet();
+
+    for (long i = 0; i < psArrayLength(errorStack); i++) {
+        psErr *err = errorStack->data[i];
+
+        psMemSetPersistent(err, false);
+        psMemSetPersistent(err->msg, false);
+        psMemSetPersistent(err->name, false);
+    }
+
+    psArrayElementsFree(errorStack);
+}
+
+void psErrorStackPrint(FILE *fd, const char *format, ...)
+{
+    if (fd == NULL) {
+        fd = stdout;
+    }
+
+    va_list ap;             // variable list arguement pointer
+
+    va_start(ap, format);
+
+    psErrorStackPrintV(fd, format, ap);
+
+    va_end(ap);
+}
+
+// This function does not allocate any memory so it is safe to call from inside
+// of psMemory.c.  Do not allocate memory in function (or call any functions
+// that do) without first removing it's use from psMemory.c.
+void psErrorStackPrintV(FILE *fd, const char *format, va_list va)
+{
+    psArray *errorStack = psErrorStackGetNoAlloc();
+    // do nothing if the error stack has not been allocated
+    if (!errorStack) {
+        return;
+    }
+
+    vfprintf(fd, format, va);
+    fprintf (fd, "\n");
+
+    for (long i = 0; i < psArrayLength(errorStack); i++) {
+        psErr *err = errorStack->data[i];
+        if(err->code >= PS_ERR_BASE) {
+            fprintf(fd," -> %s: %s\n     %s\n",
+                    err->name,
+                    psErrorCodeString(err->code),
+                    err->msg);
+        } else {
+            fprintf(fd," -> %s: %s\n     %s\n",
+                    err->name,
+                    strerror(err->code),
+                    err->msg);
+        }
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psError.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psError.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psError.h	(revision 22322)
@@ -0,0 +1,212 @@
+/** @file  psError.h
+ *
+ *  @brief error reporting functions
+ *
+ *  Error reporting functions shall be used to create log entries in the
+ *  event errors are detected.  The messages shall give enough information
+ *  to allow the user to know where the error has occurred and the type
+ *  of error detected.
+ *
+ *  @author RHL, Princeton
+ *  @author Eric Van Alst, MHPCC
+ *  @author Joshua Hoblitt, University of Hawaii
+ *
+ *  @version $Revision: 1.37 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-12 22:53:34 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ERROR_H
+#define PS_ERROR_H
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+#include<stdio.h>
+#include<stdbool.h>
+#include<stdarg.h>
+
+#include "psType.h"
+#include "psErrorCodes.h"
+
+#define _(string) string
+
+/** Error message object */
+typedef struct
+{
+    char* name;                        ///< category of code that caused the error
+    psErrorCode code;                  ///< class of error
+    char* msg;                         ///< the message associated with the error
+}
+psErr;
+
+
+/** Get a error from the error stack
+ *
+ *  Previous errors on the stack are returned by psErrorGet (a value of 0
+ *  passed to psErrorGet is equivalent to a call to psErrorLast).
+ *
+ *  if no error is at the which position, a non-NULL psErr is returned with
+ *  code PS_ERR_NONE.
+ *
+ *  @return    Error message object at 'which'
+ */
+psErr* psErrorGet(
+    long which                          ///< position in the error stack. 0 is last error on stack.
+);
+
+
+/** Get last error put on the error stack
+ *
+ *  The last error reported is available from psErrorLast; if no errors are
+ *  current, a non-NULL psErr is returned with code PS_ERR_NONE.
+ *
+ *  @return psErr*     Reference to last error message on error stack
+ */
+psErr* psErrorLast(void);
+
+
+/** Get errorCode of last error put on the error stack
+ *
+ *  @return psErrorCode     last error code, or PS_ERR_NONE
+ */
+psErrorCode psErrorCodeLast(void);
+
+
+/** Clears the error stack.
+ *
+ *  The error stack may be completely cleared with psErrorClear.
+ *
+ */
+void psErrorClear(void);
+
+
+/** Get the error stack depth
+ *
+ *  @return int The number of items on the error stack
+ */
+long psErrorGetStackSize();
+
+
+/** Prints error stack to specified open file descriptor
+ *
+ *  The entire error stack may be printed to an open file descriptor by
+ *  calling psErrorStackPrint; if and only if there are current errors, the
+ *  printf-style string format is first printed to the file descriptor fd. In
+ *  this printout, error codes are replaced by their string equivalents.
+ *
+ */
+void psErrorStackPrint(
+    FILE* fd,                          ///< destination file descriptor
+    const char* format,                ///< printf-style format of header line
+    ...                                ///< any parameters required in format
+) PS_ATTR_FORMAT(printf, 2, 3);
+
+#ifndef SWIG
+/** Prints error stack to specified open file descriptor
+ *
+ *  The entire error stack may be printed to an open file descriptor by
+ *  calling psErrorStackPrintV; if and only if there are current errors, the
+ *  vprintf-style string format is first printed to the file descriptor fd. In
+ *  this printout, error codes are replaced by their string equivalents.
+ *
+ */
+void psErrorStackPrintV(
+    FILE* fd,                          ///< destination file descriptor
+    const char* format,                   ///< printf-style format of header line
+    va_list va                         ///< any parameters required in format
+);
+#endif // ifndef SWIG
+
+
+/** Reports an error message to the logging facility
+ *
+ *  This function will invoke the psLogMsg function with a level of
+ *  PS_LOG_ERROR and pass the parameters name and format to generate a proper
+ *  log message.
+ *
+ *  This function modifies the error stack.
+ *
+ *  @return psErrorCode    the given error code
+ */
+#ifdef DOXYGEN
+psErrorCode psError(
+    psErrorCode code,                  ///< Error class code
+    bool new,                        ///< true if error originates at this location
+    const char* format,                ///< printf-style format of header line
+    ...                                ///< any parameters required in format
+);
+psErrorCode psErrorV(
+    psErrorCode code,                  ///< Error class code
+    bool new,                          ///< true if error originates at this location
+    const char* format,                ///< printf-style format of header line
+    va_list ap                         ///< any parameters required in format
+);
+#else // ifdef DOXYGEN
+psErrorCode p_psError(
+    const char* filename,              ///< file name
+    unsigned int lineno,               ///< line number in file
+    const char* func,                  ///< function name
+    psErrorCode code,                  ///< Error class code
+    bool new,                          ///< true if error originates at this location
+    const char* format,                ///< printf-style format of header line
+    ...                                ///< any parameters required in format
+) PS_ATTR_FORMAT(printf, 6, 7);
+psErrorCode p_psErrorV(
+    const char* filename,              ///< file name
+    unsigned int lineno,               ///< line number in file
+    const char* func,                  ///< function name
+    psErrorCode code,                  ///< Error class code
+    bool new,                          ///< true if error originates at this location
+    const char* format,                ///< printf-style format of header line
+    va_list ap                         ///< any parameters required in format
+  );
+#ifndef SWIG
+#define psError(code,new,...) p_psError(__FILE__,__LINE__,__func__,code,new,__VA_ARGS__)
+#define psErrorV(code,new,format,ap) p_psErrorV(__FILE__,__LINE__,__func__,code,new,format,ap)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Logs a warning message.
+ *
+ *  This procedure logs a message to the destination set by a prior
+ *  call to psLogSetDestination(), This is equivalent to calling
+ *  psLogMsg with a level of PS_LOG_WARN.
+ *
+ */
+#ifdef DOXYGEN
+void psWarning(
+    const char* format,                ///< printf-style format of header line
+    ...                                ///< any parameters required in format
+);
+#else // #ifdef DOXYGEN
+void p_psWarning(
+    const char* file,                  ///< file name
+    int lineno,                        ///< line number in file
+    const char* func,                  ///< function name
+    const char* format,                ///< printf-style format of header line
+    ...                                ///< any parameters required in format
+) PS_ATTR_FORMAT(printf, 4, 5);
+#ifndef SWIG
+#define psWarning(...) \
+      p_psWarning(__FILE__,__LINE__,__func__,__VA_ARGS__)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Create a new psErr struct
+ *
+ *  Creates a new psErr struct, making a copy of the parameters.
+ *
+ *  @return psErr*     new psErr object
+ */
+psErr* psErrAlloc(
+    const char* name,                  ///< Name of error in the form aaa.bbb.ccc
+    psErrorCode code,                  ///< Error class code
+    const char* msg                    ///< Error message
+) PS_ATTR_MALLOC;
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,165 @@
+/** @file  psErrorCodes.c
+ *
+ *  @brief Contains the error codes for the error classes
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-02-07 01:21:54 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <string.h>
+
+#include "psError.h"
+#include "psErrorCodes.h"
+#include "psList.h"
+#include "psMemory.h"
+
+
+
+static psErrorDescription staticErrorCodes[] = {
+            {PS_ERR_NONE,"not an error"},
+            {PS_ERR_BASE,"base error"},
+            {PS_ERR_${ErrorCode},"${ErrorDescription}"},
+            {PS_ERR_N_ERR_CLASSES,"error classes end marker"}
+        };
+
+static psList* dynamicErrorCodes = NULL;
+static const psErrorDescription* getErrorDescription(psErrorCode code);
+
+
+static const psErrorDescription* getErrorDescription(psErrorCode code)
+{
+    // first, search the static error codes
+
+    psS32 n = 0;
+    while(staticErrorCodes[n].code != PS_ERR_N_ERR_CLASSES &&
+            staticErrorCodes[n].code != code) {
+        n++;
+    }
+
+    if (staticErrorCodes[n].code == code) {
+        return &staticErrorCodes[n];
+    } else {
+        psErrorDescription* desc;
+        // make sure there is a list to search
+        if (dynamicErrorCodes == NULL) {
+            return NULL;
+        }
+
+        // search dynamic list of error descriptions before giving up.
+        psListIterator* iter = psListIteratorAlloc(dynamicErrorCodes,PS_LIST_HEAD,true);
+        while ((desc = (psErrorDescription*)psListGetAndIncrement(iter)) != NULL) {
+            if (desc->code == code) {
+                psFree(iter);
+                return desc;
+            }
+        }
+        psFree(iter);
+    }
+    return NULL;
+}
+
+static void freeErrorDescription(psErrorDescription* err)
+{
+    psFree((void *)err->description);
+}
+
+psErrorDescription* psErrorDescriptionAlloc(psErrorCode code,
+        const char *description)
+{
+    psErrorDescription* err = psAlloc(sizeof(psErrorDescription));
+    err->code = code;
+    if (description == NULL) {
+        err->description = NULL;
+    } else {
+        err->description = psAlloc(sizeof(char)*strlen(description)+1);
+        strcpy((char*)err->description,description);
+    }
+
+    psMemSetDeallocator(err,(psFreeFunc)freeErrorDescription);
+    return err;
+}
+
+
+const char *psErrorCodeString(psErrorCode code)
+{
+    // Check input argument is non-negative
+    if ( code < 0 ) {
+        return NULL;
+    }
+
+    const psErrorDescription* desc = getErrorDescription(code);
+
+    if (desc == NULL) {
+        return NULL;
+    }
+
+    return desc->description;
+}
+
+void psErrorRegister(const psErrorDescription* errors,
+                     int errorCode)
+{
+    if (errorCode < 1) {
+        return;
+    }
+
+    if (errors == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified psErrorDescription pointer can not be NULL."));
+        return;
+    }
+
+    if (dynamicErrorCodes == NULL) {
+        dynamicErrorCodes = psListAlloc(NULL);
+        psMemSetPersistent(dynamicErrorCodes,true);
+
+        psMemSetPersistent(dynamicErrorCodes->iterators,true);
+        psMemSetPersistent(dynamicErrorCodes->iterators->data,true);
+        for (int i = 0; i < dynamicErrorCodes->iterators->n;i++) {
+            psMemSetPersistent(dynamicErrorCodes->iterators->data[i],true);
+        }
+    }
+
+    for (psS32 i = 0; i < errorCode; i++) {
+        psErrorDescription* err = psErrorDescriptionAlloc(
+                                      errors[i].code, errors[i].description);
+        psMemSetPersistent(err,true);
+        psMemSetPersistent((psPtr)err->description,true);
+        if (! psListAdd(dynamicErrorCodes,
+                        PS_LIST_HEAD,
+                        err) ) {
+
+            psError(PS_ERR_UNKNOWN, false,
+                    _("Failed to add input psErrorDescription at array index %d."),
+                    i);
+        }
+        psMemSetPersistent(dynamicErrorCodes->head,true);
+        psFree(err);
+    }
+}
+
+bool p_psErrorUnregister(psErrorCode code)
+{
+    // Check input argument is non-negative
+    if ( code < 0 ) {
+        return false;
+    }
+
+    const psErrorDescription* desc = getErrorDescription(code);
+
+    if (desc == NULL) {
+        return false;
+    }
+
+    if (dynamicErrorCodes == NULL) {
+        return false;
+    }
+
+    return psListRemoveData(dynamicErrorCodes,(psPtr)desc);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,90 @@
+/** @file  psErrorCodes.h
+ *
+ *  @brief Contains the error codes for the error classes
+ *
+ *  @author RHL, Princeton
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-02-08 01:59:28 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ERROR_CODES_H
+#define PS_ERROR_CODES_H
+
+/// @addtogroup SysUtils System Utilities 
+/// @{
+
+#include "psType.h"
+
+/** enumeration of the static error code classes
+ */
+typedef enum {
+    PS_ERR_NONE = 0,                   ///< not an error
+    PS_ERR_BASE = 256,
+    /**< base error.  Any psErrorCode less than this should be taken to be
+     *   valid values of errno
+     */
+    PS_ERR_${ErrorCode},
+    PS_ERR_N_ERR_CLASSES               ///< end marker - should not be used as a true error
+} psErrorCode;
+
+
+/** An error code with description
+ */
+typedef struct
+{
+    psErrorCode code;                  ///< An error code
+    const char *description;           ///< the associated description
+}
+psErrorDescription;
+
+
+/** Allocates a new psErrorDescription
+ *
+ *  @return psErrorDescription*        new psErrorDescription struct.
+ */
+psErrorDescription* psErrorDescriptionAlloc(
+    psErrorCode code,                  ///< An error code
+    const char *description            ///< the associated description
+);
+
+
+/** Retrieves the description of an error code.
+ *
+ *  The routine psErrorCodeString returns the string associated with an error
+ *  code.
+ *
+ *  @return const char*     the description associated with the given code.
+ */
+const char *psErrorCodeString(
+    psErrorCode code                   ///< the associated error code
+);
+
+
+/** Register an error code
+ *
+ *  Any project needed to use psLib must define the necessary error codes and
+ *  associated message strings.  This function registers an array of error
+ *  codes with the error handling subsystem.
+ *
+ */
+void psErrorRegister(
+    const psErrorDescription* errors,  ///< Array of error codes to register
+    int errorCode                      ///< number of errors in input array
+);
+
+
+/** Clears error codes registered via psErrorRegister.
+ *
+ *  @return bool    TRUE if given errorcode was removed, otherwise FALSE.
+ */
+bool p_psErrorUnregister(
+    psErrorCode code                   ///< the error code to find and remove
+);
+
+
+/// @}
+#endif // #ifndef PS_ERROR_CODES_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psExit.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psExit.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psExit.h	(revision 22322)
@@ -0,0 +1,21 @@
+#ifndef PS_EXIT_H
+#define PS_EXIT_H
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+/// Exit status codes
+///
+/// These provide a bit finer granularity compared to success/fail
+typedef enum {
+    PS_EXIT_SUCCESS = 0,                ///< Successful termination; matches EXIT_SUCCESS
+    PS_EXIT_UNKNOWN_ERROR = 1,          ///< Error of unknown nature; matches EXIT_FAILURE
+    PS_EXIT_SYS_ERROR,                  ///< Error with a system call
+    PS_EXIT_CONFIG_ERROR,               ///< Error with configuration
+    PS_EXIT_PROG_ERROR,                 ///< Error in programming (look also for aborts)
+    PS_EXIT_DATA_ERROR,                 ///< Error with data
+    PS_EXIT_TIMEOUT_ERROR,              ///< Error due to timeout
+} psExit;
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLine.c	(revision 22322)
@@ -0,0 +1,75 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "psAssert.h"
+#include "psConstants.h"
+#include "psLine.h"
+#include "psString.h"
+#include "psMemory.h"
+
+static void lineFree(psLine *line)
+{
+
+    if (!line) {
+        return;
+    }
+
+    psFree (line->line);
+    return;
+}
+
+// allocate a psLine structrue
+psLine *psLineAlloc(long Nline)
+{
+    psLine *line = psAlloc(sizeof(psLine));
+    psMemSetDeallocator(line, (psFreeFunc)lineFree);
+    line->Nline = 0;
+    line->NLINE = Nline;
+    //    line->line = psAlloc(Nline);
+    line->line = psStringAlloc(Nline);
+
+    return line;
+}
+
+bool psLineInit(psLine *line)
+{
+    if (!line) {
+        return false;
+    }
+
+    line->Nline = 0;
+    return true;
+}
+
+bool psLineAdd(psLine *line,
+               const char *format,
+               ...)
+{
+    if (!line) {
+        return false;
+    }
+
+    long nMax = line->NLINE - line->Nline;
+
+    va_list ap;
+    va_start(ap, format);
+    long Nchar = vsnprintf(&line->line[line->Nline], nMax, format, ap);
+    line->Nline += PS_MIN(nMax - 1, Nchar);
+    va_end(ap);
+
+    if (Nchar >= nMax) {
+        return false;
+    }
+    return true;
+}
+
+bool psMemCheckLine(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)lineFree );
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLine.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLine.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLine.h	(revision 22322)
@@ -0,0 +1,80 @@
+/** @file  psLine.h
+ *
+ *  @brief charater-string fixed-length line functions
+ *
+ *  The psLine functions allow manipulation of fixed-length lines.
+ *
+ *  @author EAM, IFA
+ *  @author Paul Price, IFA
+ *  @author David Robbins, MHPCC
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-08-09 01:40:07 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_LINE_H
+#define PS_LINE_H
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+/** Structure to carry a dynamic string */
+typedef struct
+{
+    long NLINE;                        ///< allocated length
+    long Nline;                        ///< current length
+    psString line;                     ///< character string data
+}
+psLine;
+
+
+/** Allocates a line object of length Nline.
+ *
+ *  @return psLine*:        the newly allocated line object.
+*/
+psLine *psLineAlloc(
+    long Nline                         ///< length of line object to allocate
+) PS_ATTR_MALLOC;
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psLine structure, false otherwise.
+ */
+bool psMemCheckLine(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Initializes or re-initializes a line.
+ *
+ *  Initializes or re-initializes a line, setting the current length to zero and setting
+ *  the string data values to 0.  If the function is passed NULL, false is returned.
+ *
+ *  @return bool:       True if successful, otherwise false.
+*/
+bool psLineInit(
+    psLine *line                       ///< line to (re-)initialize
+);
+
+
+/** Adds the line segment to the string.
+ *
+ *  Appends a line segment to the string, returning false if the new segment would
+ *  overflow the allocated string length.
+ *
+ *  @return bool:        True if successful, otherwise false.
+*/
+bool psLineAdd(
+    psLine *line,                      ///< the line segment to append
+    const char *format,                ///< printf-style format of line
+    ...                                ///< any parameters required in format
+) PS_ATTR_FORMAT(printf, 2, 3);
+
+
+/// @}
+#endif /* PS_LINE_H */
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLogMsg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLogMsg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLogMsg.c	(revision 22322)
@@ -0,0 +1,372 @@
+/** @file  psLogMsg.c
+ *  @brief Procedures for logging messages.
+ *  \ingroup LogTrace
+ *
+ *  This file contains code for setting message log levels, message log
+ *  formats, message log destinations, and for generating the messages
+ *  themselves.
+ *  @ingroup LogTrace
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.69 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-02-06 20:33:18 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psTrace.h"
+#include "psList.h"
+#include "psString.h"
+#include "psMemory.h"
+
+
+
+#define MIN_LOG_LEVEL 0
+#define MAX_LOG_LEVEL 9
+
+#define MAX_LOG_LINE_LENGTH 256
+
+static int logFD = STDERR_FILENO;       // Log file descriptor
+static psS32 globalLogLevel = PS_LOG_INFO; // log all messages at this or above
+static bool logTime = true;     // Flag to include time info
+static bool logHost = true;     // Flag to include host info
+static bool logLevel = true;    // Flag to include level info
+static bool logName = true;     // Flag to include name info
+static bool logMsg = true;      // Flag to include message info
+
+/*****************************************************************************
+psLogSetLevel(): Set the current log level and return old level.
+Input:
+ level (psS32): the new log level.
+Output:
+ The old log level.
+ *****************************************************************************/
+int psLogSetLevel(int level)
+{
+    // Save old global log level for changing it.
+    psS32 oldLevel = globalLogLevel;
+
+    if ((level < MIN_LOG_LEVEL) || (level > MAX_LOG_LEVEL)) {
+        psLogMsg("logmsg", PS_LOG_WARN, "Attempt to set invalid logMsg level: %d", level);
+        level = (level < MIN_LOG_LEVEL) ? MIN_LOG_LEVEL : MAX_LOG_LEVEL;
+    }
+    // Set new global log level
+    globalLogLevel = level;
+
+    // Return old global log level
+    return oldLevel;
+}
+
+int psLogGetLevel()
+{
+    return globalLogLevel;
+}
+
+/*****************************************************************************
+psLogSetDestination(): sets the log message destination.
+Input:
+ dest (psS32): the new log destination
+Return:
+ An bool: TRUE if successful.
+ *****************************************************************************/
+bool psLogSetDestination(int fd)
+{
+    if (fd < 0) {
+        return false;
+    }
+
+    // Close the current FD if it's not stdout, stderr.
+    if (logFD > STDERR_FILENO) {
+        close(logFD);
+    }
+    logFD = fd;
+
+    return true;
+}
+
+int psLogGetDestination(void)
+{
+    return logFD;
+}
+
+
+/*****************************************************************************
+psLogSetFormat(): Set the format of psLogMsg output.  More precisely,
+    provide a string consisting of the letters {H (host), L (level), M
+    (message), N (name), T (time)}.  The default is "HLMNT".  This string
+    determines whether or not they associated type of information will be
+    included in message logs.  It does not determine the order in which that
+    information will appear (that order is fixed).
+
+Input:
+    fmt: a string specifying the format.
+Return:
+    NULL.
+ *****************************************************************************/
+bool psLogSetFormat(const char *format)
+{
+    // assume none.
+    logHost = false;
+    logLevel = false;
+    logMsg = false;
+    logName = false;
+    logTime = false;
+
+    // if fmt is NULL, no logging is desired.
+    if (format == NULL) {
+        return false;
+    }
+
+    // XXX: What is the purpose of this conditional.
+    if (strlen(format) == 0) {
+        format = "THLNM";
+    }
+    // Step through each character in the format string.  For each letter
+    // in that string, set the appropriate logging.
+
+    for (const char *ptr = format; *ptr != '\0'; ptr++) {
+        switch (*ptr) {
+        case 'H':
+        case 'h':
+            logHost = true;
+            break;
+        case 'L':
+        case 'l':
+            logLevel = true;
+            break;
+        case 'M':
+        case 'm':
+            logMsg = true;
+            break;
+        case 'N':
+        case 'n':
+            logName = true;
+            break;
+        case 'T':
+        case 't':
+            logTime = true;
+            break;
+        default:
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Unknown logging keyword %c."), *ptr);
+            return false;
+        }
+    }
+
+    // XXX: If one must at least log error messages, why don't we set logMsg = true here?
+    if (!logMsg) {
+        psTrace("psLib.sys", 1, "You must at least log error messages (You chose \"%s\")", format);
+
+    }
+    return true;
+}
+
+int psMessageDestination(const char *dest)
+{
+    // No destination
+    if (dest == NULL || strcasecmp(dest, "none") == 0) {
+        return 0;
+    }
+
+    // Special destinations: stdout, stderr
+    if (strcasecmp(dest, "stdout") == 0) {
+        return STDOUT_FILENO;
+    }
+    if (strcasecmp(dest, "stderr") == 0) {
+        return STDERR_FILENO;
+    }
+
+    int fileD = creat(dest, 0666);
+    if (fileD == 0) {
+        psError(PS_ERR_IO, true, _("Could not open file '%s' for output."), dest);
+        return -1;
+    }
+    return fileD;
+}
+
+#ifndef HOST_NAME_MAX                // should be in limits.h
+#define HOST_NAME_MAX 256
+#endif // #ifndef HOST_NAME_MAX
+
+/*****************************************************************************
+psLogMsgV(): This routine sends the message, which is a printf style string
+specified in the "..." argument, to the current message log destination with
+the severity specified by the "level" argument.
+ Input:
+   name
+   level
+   fmt
+   ap
+ *****************************************************************************/
+void psLogMsgV(const char *name, int level, const char *format, va_list ap)
+{
+    static psS32 first = 1;       // Flag for calling gethostname()
+    static char hostname[HOST_NAME_MAX + 1];
+
+    // Buffer for hostname.
+    char clevel = 0;            // letter-name for level
+    char head[MAX_LOG_LINE_LENGTH + 2]; // the added two are for the ending | and \0
+    char *head_ptr = head;      // where we've got to in head
+    psS32 maxLength = MAX_LOG_LINE_LENGTH;
+    time_t clock = time(NULL);  // The current time.
+    struct tm *utc = gmtime(&clock);    // The current gm time.
+
+    // If it's an abort, we always want to see the message
+    if (logFD == 0 && level == PS_LOG_ABORT) {
+        logFD = STDERR_FILENO;
+    }
+    // If logging is off, or if the level is too high, return immediately.
+    if ((level > globalLogLevel) || (logFD == 0)) {
+        return;
+    }
+    // If I have not been here yet, determine my hostname and save it.
+    if (first) {
+        first = 0;
+        gethostname(hostname, HOST_NAME_MAX);
+    }
+
+    switch (level) {
+    case PS_LOG_ABORT:
+        clevel = 'A';
+        break;
+
+    case PS_LOG_ERROR:
+        clevel = 'E';
+        break;
+
+    case PS_LOG_WARN:
+        clevel = 'W';
+        break;
+
+    case PS_LOG_INFO:
+        clevel = 'I';
+        break;
+
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+    case 8:
+    case 9:
+        clevel = level + '0';
+        break;
+
+    default:
+        psTrace("psLib.sys", 2, "Invalid logMsg level: %d (%s)\n", level, format);
+        level = (level < 0) ? 0 : 9;
+        clevel = level + '0';
+        break;
+    }
+
+    // Create the various log fields...
+    if (logTime) {
+        maxLength -= snprintf(head_ptr, maxLength, "%4d-%02d-%02d %02d:%02d:%02dZ",
+                              utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday,
+                              utc->tm_hour, utc->tm_min, utc->tm_sec) - 1;
+        head_ptr += strlen(head_ptr);
+    }
+    // Hostname should be 20 characters.
+    if (logHost) {
+        if (head_ptr > head) {
+            *head_ptr++ = '|';
+        }
+        maxLength -= snprintf(head_ptr, maxLength, "%-20s", hostname);
+        head_ptr += strlen(head_ptr);
+    }
+    if (logLevel) {
+        if (head_ptr > head) {
+            *head_ptr++ = '|';
+        }
+        maxLength -= snprintf(head_ptr, maxLength, "%c", clevel);
+        head_ptr += strlen(head_ptr);
+    }
+    if (logName) {
+        if (head_ptr > head) {
+            *head_ptr++ = '|';
+        }
+        maxLength -= snprintf(head_ptr, maxLength, "%s", name);
+
+        head_ptr += strlen(head_ptr);
+    }
+
+    if (head_ptr > head) {
+        *head_ptr++ = '\n';
+    } else if (!logMsg) {                  // no output desired
+        return;
+    }
+    *head_ptr = '\0';
+
+    write(logFD, head, strlen(head));
+    if (logMsg) {
+        psString msg = NULL;            // Message to print
+        psStringAppendV(&msg, format, ap);
+
+        // detect multiple lines in message and indent each line by 4 spaces.
+        char* msgPtr;
+        char *line = strtok_r(msg, "\n", &msgPtr);
+        while (line) {
+            write(logFD, "    ", 4);
+            write(logFD, line, strlen(line));
+            write(logFD, "\n", 1);
+            line = strtok_r(NULL, "\n", &msgPtr);
+        }
+        psFree(msg);
+    } else {
+        write(logFD, "\n", 1);
+    }
+
+    if (level == PS_LOG_ABORT) {
+        switch (logFD) {
+        case STDOUT_FILENO:
+            fflush(stdout);
+            break;
+        case STDERR_FILENO:
+            fflush(stderr);
+            break;
+            // For the others, write() should send it unbuffered...?
+        }
+
+    }
+}
+
+/*****************************************************************************
+psLogMsg(): This routine sends the message, which is a printf style string
+specified in the "..." argument, to the current message log destination with
+the severity specified by the "level" argument.
+
+Input:
+  name: Indicates the source of this log message.
+  level: The severity of this log message.
+  fmt: The printf-stype formatted string, followed by the arguments
+        to that string.
+  ... The arguments to the above printf-style string.
+
+Return:
+   NULL
+ *****************************************************************************/
+void psLogMsg(const char *name, int level, const char *format, ...)
+{
+    va_list ap;
+
+    va_start(ap, format);
+    psLogMsgV(name, level, format, ap);
+    va_end(ap);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLogMsg.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLogMsg.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psLogMsg.h	(revision 22322)
@@ -0,0 +1,140 @@
+/** @file  psLogMsg.h
+ *
+ *  @brief Procedures for logging messages.
+ *
+ *  This file will hold the prototypes for defining procedure which set
+ *  message log levels, messahe log formats, message log destinations, and
+ *  for generating the messages themselves.
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author GLG, MHPCC
+ *  @author Joshua Hoblitt, University of Hawaii
+ *
+ *  @version $Revision: 1.40 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-08-09 01:40:07 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_LOG_MSG_H
+#define PS_LOG_MSG_H
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+#include <stdarg.h>
+#include "psType.h"
+
+
+///< Status codes for log messages
+enum {
+    PS_LOG_ABORT = 0,                  ///< log message is a critical error, perform an abort after printing
+    PS_LOG_ERROR,                      ///< log message is an error, but don't abort
+    PS_LOG_WARN,                       ///< log message is a warning
+    PS_LOG_INFO,                       ///< log message is informational only
+    PS_LOG_DETAIL,                     ///< log message provides details of minor interest
+    PS_LOG_MINUTIA,                    ///< log message provides very detailed information
+};
+
+
+///< Destinations for log messages
+enum {
+    PS_LOG_TO_NONE = 0,                ///< turn off logging
+    PS_LOG_TO_STDERR = 1,              ///< log to system's stderr
+    PS_LOG_TO_STDOUT = 2               ///< log to system's stdout
+};
+
+/** This procedure sets the destination for future log messages.
+ *
+ *  This procedure will take an integer as an argument
+ *  which can specify general log destinations.
+ *
+ *  @return bool     true if set successfully, otherwise false.
+ */
+bool psLogSetDestination(
+    int fd                             ///< Specifies where to send messages.
+);
+
+
+/** This procedure returns the current log destination file descriptor.  If the
+ * destination has not been defined by the use, the descriptor for stdout is
+ * returned.
+ *
+ *  @return int:        The current file descriptor.
+ */
+int psLogGetDestination();
+
+
+/** This procedure sets the message level for future log messages.  Subsequent
+ *  log messages, with a log level of "mylevel", will only be logged if
+ *  "mylevel" is less than the current log level set by this procedure.
+ *  Ie. higher values set by this procedure will cause more log messages to
+ *  be displayed.  The old log level will be returned.
+ *
+ *  @return int:        old logging level.
+ */
+int psLogSetLevel(
+    int level                          ///< Specifies the system log level
+);
+
+
+/** This procedures returns the current log message level.
+ *
+ *  @return int:        The current logging level.
+ */
+int psLogGetLevel();
+
+/** This procedure sets the log format for future log messages.  The argument
+ *  must be a character string consistsing of the letters H (host), L
+ *  (level), M (message), N (name), and T (time).  The default is "THLNM".
+ *  Deleting a letter from the string will cause the associated information
+ *  to not be logged.  This procedure does not alter the order in which
+ *  the messages are displayed.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psLogSetFormat(
+    const char *format                 ///< Specifies the system log format
+);
+
+
+/** This procedures uses a string to set the destination for which to send
+ *  the corresponding log messages.
+ *
+ *  @return int:        The file descriptor location of the message.
+ */
+int psMessageDestination(
+    const char *dest                   ///< Specifies where to send the message
+);
+
+
+/** This procedure logs a message to the destination set by a prior
+ *  call to psLogSetDestination(), if myLevel is less than the level
+ *  specified by a prior call to psLogSetLevel().  The message is specified
+ *  with a printf-type string and arguments.
+ *
+ */
+void psLogMsg(
+    const char *name,                  ///< name of the log source
+    int level,                         ///< severity level of this log message
+    const char *format,                ///< printf-style format command
+    ...
+) PS_ATTR_FORMAT(printf, 3, 4);
+
+
+/** This procedure is functionally equivalent to psLogMsg(), except that
+ *  it takes a va_list as the message parameter, not a printf-style string.
+ *
+ */
+#ifndef SWIG
+void psLogMsgV(
+    const char *name,                  ///< name of the log source
+    int level,                         ///< severity level of this log message
+    const char *format,                ///< printf-style format command
+    va_list ap                         ///< varargs argument list
+);
+#endif // #ifndef SWIG
+
+
+/// @}
+#endif // #ifndef PS_LOG_MSG_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psMemory.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psMemory.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psMemory.c	(revision 22322)
@@ -0,0 +1,1070 @@
+/** @file  psMemory.c
+*
+*  @brief Contains the definitions for the memory management system
+*
+*  psMemory.h has additional information and documentation of the routines
+*  found in this file.
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Robert Lupton, Princeton University
+*  @author Joshua Hoblitt, University of Hawaii
+*
+*  @version $Revision: 1.100 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2008-08-20 02:00:20 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define PS_ALLOW_MALLOC                    // we're allowed to call malloc()
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#if defined(PS_MEM_BACKTRACE) && defined(HAVE_BACKTRACE)
+# include <execinfo.h>
+#endif
+
+#include "psError.h"    // for psErrorStackPrint() only
+#include "psMemory.h"
+
+// Magic number in psMemBlock header
+#define P_PS_MEMMAGIC (uint32_t)0xdeadbeef
+
+#define MUTEX_LOCK(mutexPtr) \
+if (safeThreads) { \
+    pthread_mutex_lock(mutexPtr); \
+}
+
+#define MUTEX_UNLOCK(mutexPtr) \
+if (safeThreads) { \
+    pthread_mutex_unlock(mutexPtr); \
+}
+
+// psAbort() calls functions that call psAlloc() so it is *UNSAFE* to use it
+// from within the memory subsystem.  Previous implementations tried to do
+// this and would deadlock while trying to allocate memory.
+//
+// Note that psError() is also *UNSAFE* to use from within the memory
+// subsystem.
+#define PS_MEM_ABORT(name, ...) \
+P_PS_MEM_ABORT(__FILE__, __LINE__, __func__, name, __VA_ARGS__)
+
+// psErrorStackPrint() was specifically modified to be safe to call from inside
+// psMemory.c.
+#define P_PS_MEM_ABORT(filename, lineno, func, name, ...) \
+fprintf(stderr, "%s (%s:%d) ", func, filename, lineno); \
+fprintf(stderr, __VA_ARGS__);\
+psErrorStackPrint(stderr, "\nAborting.  Error stack:\n"); \
+fprintf(stderr, "\n");\
+abort();
+
+#define HANDLE_BAD_BLOCK(memBlock, file, lineo, func) \
+if (isBadMemBlock(stderr, memBlock, file, lineo, func)) { \
+    PS_MEM_ABORT(__func__, "Unsafe to Continue\n"); \
+}
+
+static bool isBadMemBlock(FILE *output, const psMemBlock *memBlock, const char *file, unsigned int lineo, const char *func);
+
+// memBlockListMutex protects access to:
+//      safeThreads -- very rarely accessed
+//      memory_is_persistent -- very rarely accessed
+//      memAllocCallback -- very rarely accessed
+//      memFreeCallback -- very rarely accessed
+//      memExhaustedCallback -- very rarely accessed
+//      p_psMemAllocID -- rarely accessed
+//      p_psMemFreeID -- rarely accessed
+//      lastMemBlockAllocated
+//      memid -- rarely accessby
+//      "the linked list of mem blocks"
+//
+// This is a fair ammount of stuff to protect with a single mutex but most of
+// these items are *VERY* low contention items.  The only item that should be
+// performance issue is "the linked list of mem blocks".  If this does become a
+// problem in production use of the list should be disabled as it is really a
+// debugging feature.
+//
+// XXX make the mem block list a build time option
+//
+static pthread_mutex_t memBlockListMutex = PTHREAD_MUTEX_INITIALIZER;
+
+/******** thread safety options management ********/
+
+// private boolean for enabling/disabling thread safety.  Default = enabled.
+static bool safeThreads = true;
+
+// Set the thread-safety state of the memory system: default is true
+bool psMemSetThreadSafety(bool safe)
+{
+    // this function is only called ~once per program, before threads are launched
+    MUTEX_LOCK(&memBlockListMutex);
+
+    bool oldState = safeThreads;
+    safeThreads = safe;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return oldState;
+}
+
+
+// Get the thread-safety state of the memory system
+bool psMemGetThreadSafety(void)
+{
+    // this function is only called ~once per program, probably before threads are launched
+    MUTEX_LOCK(&memBlockListMutex);
+
+    bool oldState = safeThreads;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return oldState;
+}
+
+/******** persistent memory options management ********/
+
+// private boolean for deciding if allocated memory is persistent by default
+static bool memory_is_persistent = false;
+
+/* Set whether allocated memory is persistent
+ */
+bool p_psMemAllocatePersistent(bool is_persistent)
+{
+    // this function is only called ~once per program, before threads are launched
+    MUTEX_LOCK(&memBlockListMutex);
+
+    const bool old = memory_is_persistent;
+    memory_is_persistent = is_persistent;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+/*
+ * And now the I-want-to-be-informed callbacks
+ *
+ * Call the callbacks when these IDs are allocated/freed
+ */
+/******** memory allocation callback management (memAllocCallback) ********/
+
+// Default memFreeCallback function
+static psMemId memAllocCallbackDefault(const psMemBlock *memBlock)
+{
+    static psMemId incr = 0; // "p_psMemAllocID += incr"
+
+    return incr;
+}
+
+static psMemAllocCallback memAllocCallback = memAllocCallbackDefault;
+
+psMemAllocCallback psMemAllocCallbackSet(psMemAllocCallback func)
+{
+    // this function is only called rarely per program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemAllocCallback old = memAllocCallback;
+
+    if (func != NULL) {
+        memAllocCallback = func;
+    } else {
+        memAllocCallback = memAllocCallbackDefault;
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+// notify user when this block is allocated
+// XXX why was this not static, and why was it p_psMemAllocID?
+static psMemId p_psMemAllocID = 0;
+
+// the above callback is only called if a specific psMemId is set with this function
+// this function is rarely called in a given program
+psMemId psMemAllocCallbackSetID(psMemId id)
+{
+    // this function is only called rarely per program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemId old = p_psMemAllocID;
+
+    p_psMemAllocID = id;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+/******** memory free callback management (memFreeCallback) ********/
+
+// Default memFreeCallback function
+static psMemId memFreeCallbackDefault(const psMemBlock *memBlock)
+{
+    static psMemId incr = 0; // "p_psMemFreeID += incr"
+
+    return incr;
+}
+
+static psMemFreeCallback memFreeCallback = memFreeCallbackDefault;
+
+// this function is called only rarely in a program
+psMemFreeCallback psMemFreeCallbackSet(psMemFreeCallback func)
+{
+    // this function is only called rarely per program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemFreeCallback old = memFreeCallback;
+
+    if (func != NULL) {
+        memFreeCallback = func;
+    } else {
+        memFreeCallback = memFreeCallbackDefault;
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+// notify user when this block is freed
+// XXX why was this not static?
+static psMemId p_psMemFreeID = 0;
+
+// the above callback is only called if a specific psMemId is set with this function
+psMemId psMemFreeCallbackSetID(psMemId id)
+{
+    // this function is rarely called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemId old = p_psMemFreeID;
+
+    p_psMemFreeID = id;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+/******** memory exhausted callback management (memExhaustedCallback) ********/
+
+// Default memExhaustedCallback function
+static void *memExhaustedCallbackDefault(size_t size)
+{
+    return NULL;
+}
+
+static psMemExhaustedCallback memExhaustedCallback = memExhaustedCallbackDefault;
+
+psMemExhaustedCallback psMemExhaustedCallbackSet(psMemExhaustedCallback func)
+{
+    // this function is rarely called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemExhaustedCallback old = memExhaustedCallback;
+
+    if (func != NULL) {
+        memExhaustedCallback = func;
+    } else {
+        memExhaustedCallback = memExhaustedCallbackDefault;
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+/* An example callback function to check the state of the memory system; may be registered
+ * with psMem{Alloc,Free}CallbackSet
+ */
+psMemId memAllocCallbackCheckCorruption(const psMemBlock *memBlock)
+{
+    static psMemId incr = 10; // "p_psMemAllocID += incr"
+
+    if (psMemCheckCorruption(stderr, false) > 0) {
+        fprintf(stderr, "Detected memory corruption\n"); // somewhere to set a breakpoint
+    }
+
+    return incr;
+}
+
+/**** Unique ID for allocated blocks and associated accessor functions
+ */
+static psMemId memid = 0;
+
+/* Return memory ID counter for next block to be allocated
+ */
+psMemId psMemGetId(void)
+{
+    // this function is only occasionally called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemId id = memid + 1;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return id;
+}
+
+/* Return memory ID counter for last block allocated
+ */
+psMemId psMemGetLastId(void)
+{
+    // this function is only occasionally called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemId id = memid;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return id;
+}
+
+// pointer to the last mem block that was allocated.
+// This is the root of the entire memory list
+static psMemBlock *lastMemBlockAllocated = NULL;
+
+/* Actually allocate memory
+ */
+void *p_psAlloc(const char *file,
+                unsigned int lineno,
+                const char *func,
+                size_t size)
+{
+
+    psMemBlock *memBlock = malloc(sizeof(psMemBlock) + size + sizeof(void *));
+    if (memBlock == NULL) {
+        // this lock is only occasionally called in a given program (rarely fail malloc)
+        MUTEX_LOCK(&memBlockListMutex);
+        memBlock = memExhaustedCallback(size);
+        MUTEX_UNLOCK(&memBlockListMutex);
+        if (memBlock == NULL) {
+            PS_MEM_ABORT(__func__, "Failed to allocate %zd bytes at %s (%s:%d)", size, func, file, lineno);
+        }
+    }
+
+    // posts
+    *(psU32 *)&memBlock->startblock = P_PS_MEMMAGIC;
+    *(psU32 *)&memBlock->endblock   = P_PS_MEMMAGIC;
+    *(psU32 *)((char *) (memBlock + 1) + size) = P_PS_MEMMAGIC;
+
+    // size of memory allocated
+    memBlock->userMemorySize = size;
+
+    // alloc request by:
+    // thread
+    *(pthread_t *)&memBlock->tid = pthread_self();
+    // file
+    memBlock->file = file;
+    // line number
+    *(unsigned int *)&memBlock->lineno = (unsigned int)lineno;
+    // function
+    memBlock->func = func;
+
+    #if defined(PS_MEM_BACKTRACE) && defined(HAVE_BACKTRACE)
+    #define BACKTRACE_BUFFER_SIZE 32
+    // psMemBlock.func is a 'const char *', so basically we're going to abuse
+    // that and treat it as a void ** to carry around backtrace information.
+    // psMemBlock is not ifdef'd to make sure that psMemBlock is always the
+    // same size & layout reguardless of the pslib .so that's being linked
+    // against
+    void **bt = malloc(BACKTRACE_BUFFER_SIZE * sizeof(void *));
+    if (bt == NULL) {
+        PS_MEM_ABORT(__func__, "Failed to allocate memory for backtrace buffer: %zd bytes at %s (%s:%d)",
+                     32 * sizeof(void *), func, file, lineno);
+    }
+    *(size_t *)&memBlock->backtraceSize = backtrace(bt, BACKTRACE_BUFFER_SIZE);
+    *(void ***)&memBlock->backtrace = bt;
+    #endif // ifdef HAVE_BACKTRACE
+
+    // free function
+    memBlock->freeFunc = NULL;
+
+    // persistent memory flag
+    memBlock->persistent = memory_is_persistent;
+
+    // this block will be add as the last mem block in the list
+    memBlock->previousBlock = NULL;
+
+    // ref count
+    memBlock->refCounter = 1;                   // one user so far
+
+    // need exclusive access of the memory block list now...
+    // this lock is very frequently called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    // increment the memory id only after we've grabbed the memBlockListMutex
+    *(psMemId* )&memBlock->id = ++memid;
+
+    // insert the new block to the front of the memBlock linked-list
+    if (lastMemBlockAllocated) {
+        // exchange forward and backward references with the last allocated block
+        lastMemBlockAllocated->previousBlock = memBlock;
+    }
+    memBlock->nextBlock = lastMemBlockAllocated;
+    lastMemBlockAllocated = memBlock;
+
+    // Did the user ask to be informed about this allocation?
+    if (memBlock->id == p_psMemAllocID) {
+        // p_psMemAllocID can only be changed while the memBlockList mutex is held
+        p_psMemAllocID += memAllocCallback(memBlock);
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    // And return the user the memory that they allocated
+    return memBlock + 1;                        // user memory
+}
+
+/* internal routine to check the consistency of the allocated and/or free memory arena.
+ * this is used by the user functions below (psMem{Set,Get}Deallocator,
+ * N.b. If the block wasn't allocated by psAlloc, it will appear corrupted
+ */
+static bool isBadMemBlock(FILE *output, const psMemBlock *memBlock, const char *file, unsigned int lineno, const char *func)
+{
+    // n.b. since this is called by psMemCheckCorruption while the memblock
+    // list is mutex locked, we shouldn't call such things as
+    // p_psAlloc/p_psFree here.
+
+    bool bad = false;
+    bool blockPrinted = false;
+
+    if (memBlock == NULL) {
+        fprintf(output, _("NULL memory block.\n"
+                          "Caught in %s at (%s:%d.).\n\n"), func, file, lineno);
+        // return now as we can't do anything else
+        return true;
+    }
+
+    #if 0
+    // Currently psAlloc()/psRealloc() will blindly create memBlock's with a
+    // size of 0.  This test is in here to check if this is really
+    // happening/being use as a feature in the wild.
+    if (memBlock->userMemorySize < 1) {
+        psMemBlockPrint(output, memBlock);
+        blockPrinted = true;
+        fprintf(output, _("\n\tMemory block has a size of less than 1.\n"));
+        bad = true;
+    }
+    #endif
+
+    if (memBlock->refCounter < 1) {
+        // using an unreferenced block of memory, are you?
+        psMemBlockPrint(output, memBlock);
+        blockPrinted = true;
+        fprintf(output, _("\n\tMemory block was freed but still being used.\n"));
+        bad = true;
+    }
+
+    if (memBlock->startblock != P_PS_MEMMAGIC || memBlock->endblock != P_PS_MEMMAGIC) {
+        if (!blockPrinted) {
+            psMemBlockPrint(output, memBlock);
+            blockPrinted = true;
+        }
+        fprintf(output, _("\n\tMemory block is corrupted; buffer underflow detected.\n"));
+        bad = true;
+    }
+
+    if (*(psU32 *)((char *)(memBlock + 1) + memBlock->userMemorySize) != P_PS_MEMMAGIC) {
+        if (!blockPrinted) {
+            psMemBlockPrint(output, memBlock);
+            blockPrinted = true;
+        }
+        fprintf(output, _("\n\tMemory block is corrupted; buffer overflow detected.\n"));
+        bad = true;
+    }
+
+    //  XXX ->nextBlock, & -> prevousBlock really should not be looked at by
+    //  this function as they may be changed out from underneath us by new
+    //  memory allocation.  However, the memBlock itself shouldn't go away (end
+    //  users responsiblity) so all we're really risking is a garbage value.
+
+    //  XXX EAM : is this the error I'm catching in multithreaded processing?
+    if (memBlock == memBlock->nextBlock) {
+        if (!blockPrinted) {
+            psMemBlockPrint(output, memBlock);
+            blockPrinted = true;
+        }
+        fprintf(output, _("\n\tMemory block's ->nextBlock pointer refers to itself.\n"));
+        bad = true;
+    }
+
+    if (memBlock == memBlock->previousBlock) {
+        if (!blockPrinted) {
+            psMemBlockPrint(output, memBlock);
+            blockPrinted = true;
+        }
+        fprintf(output, _("\n\tMemory block's ->previousBlock pointer refers to itself.\n"));
+        bad = true;
+    }
+
+    if (bad) {
+        fprintf(output, _("\tCaught in %s at (%s:%d).\n\n"), func, file, lineno);
+    }
+
+    return bad;
+}
+
+void *p_psRealloc(const char *file,
+                  unsigned int lineno,
+                  const char *func,
+                  void *ptr,
+                  size_t size)
+{
+    if (ptr == NULL) {
+        return p_psAlloc(file, lineno, func, size);
+    }
+
+    psMemBlock *memBlock = ((psMemBlock *)ptr) - 1;
+
+    HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
+
+    if (size == memBlock->userMemorySize) {
+        // Nothing to do
+        return ptr;
+    }
+
+    // Reallocate the memory
+
+    // we need to lock this whole section because we need to be able to adjust
+    // lastMemBlockAllocated if needed (ie, this IS lastMemBlockAllocated)
+    // this lock is frequently called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemBlock *nextBlock = memBlock->nextBlock;
+    psMemBlock *previousBlock = memBlock->previousBlock;
+
+    // Is this the last block we allocated?  If it is, we need to keep track of
+    // this fact and update lastMemBlockAllocated *after* the realloc or
+    // lastMemBlockAllocated will be left with a bogus value
+    bool isBlockLast = (memBlock == lastMemBlockAllocated);
+
+    memBlock = (psMemBlock *)realloc(memBlock, sizeof(psMemBlock) + size + sizeof(void *));
+    if (memBlock == NULL) {
+        memBlock = memExhaustedCallback(size);
+        if (memBlock == NULL) {
+            psMemBlockPrint(stderr,  ((psMemBlock *)ptr) - 1);
+            fprintf(stderr, "Problem reallocating block\n");
+            PS_MEM_ABORT(__func__, "Failed to reallocate to %zd bytes at %s (%s:%d)", size, func, file, lineno);
+        }
+    }
+
+    memBlock->userMemorySize = size;
+    *(psU32 *)((char *)(memBlock + 1) + size) = P_PS_MEMMAGIC;
+
+    // update the references on the list:
+
+    // is we are modifying the last mem block, we need to update lastMemBlockAllocated
+    if (isBlockLast) {
+        lastMemBlockAllocated = memBlock;
+    }
+
+    // the block location may have changed, so fix the linked list addresses.
+    if (nextBlock != NULL) {
+        nextBlock->previousBlock = memBlock;
+    }
+    if (previousBlock != NULL) {
+        previousBlock->nextBlock = memBlock;
+    }
+
+    // Did the user ask to be informed about this allocation?
+    if (memBlock->id == p_psMemAllocID) {
+        p_psMemAllocID += memAllocCallback(memBlock);
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return memBlock + 1;                    // usr memory
+}
+
+/*
+ * Check for memory leaks.
+ */
+int p_psMemCheckLeaks(const char *file,
+                      unsigned int lineno,
+                      const char *func,
+                      psMemId id0,
+                      psMemBlock ***array,
+                      FILE * fd,
+                      bool persistence)
+{
+    psS32 nleak = 0;
+    psS32 j = 0;
+    psMemBlock *topBlock = lastMemBlockAllocated;
+
+    // make sure that the memblock list is free of corruption before we crawl
+    // the list
+    p_psMemCheckCorruption(file, lineno, func, fd, true);
+
+    // this lock is rarely called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    // if topBlock is NULL there's nothing to do
+    if (!topBlock) {
+        MUTEX_UNLOCK(&memBlockListMutex);
+        return 0;
+    }
+
+    // find the very first memblock
+    psMemBlock *memBlock = NULL;
+    for (memBlock = topBlock; memBlock->nextBlock != NULL; memBlock = memBlock->nextBlock) { }
+
+    // iterate through the block list starting with the oldest block
+    for (; memBlock != NULL; memBlock = memBlock->previousBlock) {
+        if ( (memBlock->refCounter > 0) &&
+                ( (persistence) || (!persistence && !memBlock->persistent) ) &&
+                (memBlock->id >= id0)) {
+
+            nleak++;
+
+            if (fd != NULL) {
+                if (nleak == 1) {
+                    fprintf(fd, "# func at (file:line)  ID: X  Ref: X\n");
+                }
+
+                fprintf(fd, "%s at (%s:%d)  ID: %lu  Ref: %lu", memBlock->func, memBlock->file, (int)memBlock->lineno, (unsigned long)memBlock->id, memBlock->refCounter);
+                #if defined(PS_MEM_BACKTRACE) && defined(HAVE_BACKTRACE)
+
+                size_t size = memBlock->backtraceSize;
+                char **strings = backtrace_symbols((void *const *)memBlock->backtrace, size);
+
+                fprintf(fd, "  Alloc Call Depth: %zd\n", size);
+
+                for (int i = 0; i < size; i++) {
+                    // always ident
+                    int ident = 4;  // initial indent
+                    ident += 2 * i; // nesting depth
+                    fprintf(fd, "%*s", ident, "");
+
+                    // if the caller was an anon function then strchr won't
+                    // find a '(' in the string and will return NULL
+                    char *caller = caller = strchr(strings[i], '(');
+                    if (caller) {
+                        // skip over the '('
+                        caller++;
+                        // find the end of the symbol name
+                        size_t callerLength = abs(strchr(caller, '+') - caller);
+                        // print just the symbol name
+                        for (int i = 0; i < callerLength; i++) {
+                            fputc(caller[i],fd);
+                        }
+                        fprintf(fd, "\n");
+                    } else {
+                        fprintf(fd, "(unknown)\n");
+                    }
+                }
+
+                free (strings);
+                #else // ifdef HAVE_BACKTRACE
+                // \n after "Memory Block ID"
+                fprintf(fd, "\n");
+                #endif // ifdef HAVE_BACKTRACE
+
+            }
+        }
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    if (nleak == 0 || array == NULL) {
+        return nleak;
+    }
+
+    *array = psAlloc(nleak * sizeof(psMemBlock));
+
+    // this lock is rarely called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    for (psMemBlock *memBlock = topBlock; memBlock != NULL; memBlock = memBlock ->nextBlock) {
+        if ( (memBlock->refCounter > 0) &&
+                ( (persistence) || (!persistence && !memBlock->persistent) ) &&
+                (memBlock->id >= id0)) {
+
+            (*array)[j++] = memBlock;
+            if (j == nleak) {              // found them all
+                break;
+            }
+        }
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return nleak;
+}
+
+
+/*
+ * Reference counting APIs
+ */
+psReferenceCount p_psMemGetRefCounter(const char *file,
+                                      unsigned int lineno,
+                                      const char *func,
+                                      void *ptr)
+{
+    if (ptr == NULL) {
+        return 0;
+    }
+
+    psMemBlock *memBlock = ((psMemBlock *) ptr) - 1;
+
+    HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
+
+    return memBlock->refCounter;
+}
+
+
+// increment and return refCounter
+void *p_psMemIncrRefCounter(const char *file,
+                            unsigned int lineno,
+                            const char *func,
+                            void *ptr)
+{
+    if (ptr == NULL) {
+        return ptr;
+    }
+
+    psMemBlock* memBlock = ((psMemBlock *) ptr) - 1;
+
+    HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
+
+    // XXX we probably need a MUTEX_LOCK here: otherwise, two functions can race on recCounter++ and
+    // refCounter--;
+    memBlock->refCounter++;
+
+    // Did the user ask to be informed about this allocation?
+    // this lock is frequently called in a given program
+    // this lock is probably not needed: if someone changes the callback ID in a different thread,
+    // do we really care about really rarely getting this wrong?
+    if (memBlock->id == p_psMemAllocID) {
+        MUTEX_LOCK(&memBlockListMutex);
+        p_psMemAllocID += memAllocCallback(memBlock);
+        MUTEX_UNLOCK(&memBlockListMutex);
+    }
+
+    return ptr;
+}
+
+
+#if 0
+void * p_psMemSetRefCounter(void * vptr,
+                            psReferenceCount count,
+                            const char *file,
+                            psS32 lineno)
+{
+    psMemBlock* ptr;
+
+    if (vptr == NULL) {
+        return vptr;
+    }
+
+    if (count < 0) {
+        count = 0;
+    }
+
+    ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (isBadMemBlock(ptr, __func__)) {
+        (void)p_psMemDecrRefCounter(vptr, filename, lineno);
+        memProblemCallback(ptr, file, lineno);
+    }
+
+    ptr->refCounter = count;
+
+    if (count < 1) {
+        vptr = p_psMemDecrRefCounter(vptr,file,lineno);
+    }
+
+    return vptr;
+}
+#endif
+
+
+// decrement and return refCounter
+void *p_psMemDecrRefCounter(const char *file,
+                            unsigned int lineno,
+                            const char *func,
+                            void * ptr)
+{
+    if (ptr == NULL) {
+        return NULL;
+    }
+
+    psMemBlock *memBlock = ((psMemBlock *) ptr) - 1;
+
+    HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
+
+    // if we have multiple references, just decrement the count and return.
+    // XXX we probably need a MUTEX_LOCK here: otherwise, two functions can race on refCounter--;
+    if (memBlock->refCounter > 1) {
+        memBlock->refCounter--;
+
+        // Did the user ask to be informed about this deallocation?
+        MUTEX_LOCK(&memBlockListMutex);
+        if (memBlock->id == p_psMemFreeID) {
+            p_psMemFreeID += memFreeCallback(memBlock);
+        }
+        MUTEX_UNLOCK(&memBlockListMutex);
+        return ptr;
+    }
+
+    // we can't invoke freeFunc() while we're holding memBlockListMutex as it
+    // may invoke psFree() itself
+    if (memBlock->freeFunc != NULL) {
+        memBlock->freeFunc(ptr);
+    }
+
+    // this lock is frequently set in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    // Did the user ask to be informed about this deallocation?
+    if (memBlock->id == p_psMemFreeID) {
+        p_psMemFreeID += memFreeCallback(ptr);
+    }
+
+    psMemBlock *nextBlock = memBlock->nextBlock;
+    psMemBlock *previousBlock = memBlock->previousBlock;
+
+    // cut the memBlock out of the memBlock list
+    if (nextBlock != NULL) {
+        nextBlock->previousBlock = previousBlock;
+    }
+    if (previousBlock != NULL) {
+        previousBlock->nextBlock = nextBlock;
+    }
+    if (lastMemBlockAllocated == memBlock) {
+        lastMemBlockAllocated = nextBlock;
+    }
+
+    // NULL out the refs so no one can get confused
+    memBlock->nextBlock = NULL;
+    memBlock->previousBlock = NULL;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    // invoke free only after we've released the block list lock as free()
+    // could take awhile.  We can get away with this as at this point the
+    // memBlock is no longer part of the mem block list.
+    #if defined(PS_MEM_BACKTRACE) && defined(HAVE_BACKTRACE)
+
+    free(memBlock->backtrace);
+    #endif
+
+    free(memBlock);
+
+    // since we freed it, make sure we return NULL.
+    return NULL;
+}
+
+
+/**** user functions to manage memory references ****/
+void p_psMemSetDeallocator(const char *file,
+                           unsigned int lineno,
+                           const char *func,
+                           void *ptr,
+                           psFreeFunc freeFunc)
+{
+    if (ptr == NULL) {
+        return;
+    }
+
+    psMemBlock* memBlock = ((psMemBlock *)ptr) - 1;
+
+    HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
+
+    memBlock->freeFunc = freeFunc;
+}
+
+
+psFreeFunc p_psMemGetDeallocator(const char *file,
+                                 unsigned int lineno,
+                                 const char *func,
+                                 void *ptr)
+{
+    if (ptr == NULL) {
+        return NULL;
+    }
+
+    psMemBlock* memBlock = ((psMemBlock *)ptr) - 1;
+
+    HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
+
+    return memBlock->freeFunc;
+}
+
+
+bool p_psMemGetPersistent(const char *file,
+                          unsigned int lineno,
+                          const char *func,
+                          void *ptr)
+{
+    if (ptr == NULL) {
+        return NULL;
+    }
+
+    psMemBlock* memBlock = ((psMemBlock *) ptr) - 1;
+
+    HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
+
+    return memBlock->persistent;
+}
+
+
+void p_psMemSetPersistent(const char *file,
+                          unsigned int lineno,
+                          const char *func,
+                          void *ptr,
+                          bool value)
+{
+    if (ptr == NULL) {
+        return;
+    }
+
+    psMemBlock* memBlock = ((psMemBlock *) ptr) - 1;
+
+    HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
+
+    memBlock->persistent = value;
+}
+
+int p_psMemCheckCorruption(const char *file,
+                           unsigned int lineno,
+                           const char *func,
+                           FILE *output,
+                           bool abort_on_error)
+{
+    // get exclusive access to the memBlock list to avoid it changing on us while we check it.
+
+    // this lock is rarely called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psS32 nbad = 0;               // number of bad blocks
+    for (psMemBlock *memBlock = lastMemBlockAllocated; memBlock != NULL; memBlock = memBlock->nextBlock) {
+        if (isBadMemBlock(output, memBlock, __FILE__, __LINE__, __func__)) {
+            nbad++;
+
+            if (abort_on_error) {
+                // release the lock on the memblock list
+                MUTEX_UNLOCK(&memBlockListMutex);
+                PS_MEM_ABORT(__func__, "Detected memory corruption");
+            }
+        }
+    }
+
+    // release the lock on the memblock list
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return nbad;
+}
+
+bool p_psMemIsAlloced(const char *file,
+                      unsigned int lineno,
+                      const char *func,
+                      const void *ptr)
+{
+    // if ptr is a psAlloc()'d memory, find the actual address of the memBlock
+    psMemBlock *addr = ((psMemBlock *)ptr) - 1;
+
+    // get exclusive access to the memBlock list to avoid it changing on us
+    // this lock is only occasionally called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    // loop through the linked list of memBlocks looking for a matching pointer
+    for (psMemBlock *memBlock = lastMemBlockAllocated; memBlock != NULL; memBlock = memBlock->nextBlock) {
+        if (memBlock == addr) {
+            // we found the memBlock
+            HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
+
+            MUTEX_UNLOCK(&memBlockListMutex);
+            return true;
+        }
+    }
+
+    // release the lock on the memblock list
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return false;
+}
+
+
+/**** memory usage statistics functions ****/
+
+/*
+ * Return the total amount of memory owned by psLib; if non-NULL also provide a
+ * breakdown into recyclable, allocated, and allocated-and-persistent
+ *
+ * It would be simple enough to fix this code to return an array of structs to
+ * describe the insides of the allocator rather than the printf used here.
+ */
+size_t psMemStats(const bool print, // print details as they're found?
+                  size_t *allocated, // memory that's currently allocated (but not persistent)
+                  size_t *persistent) // persistent memory that's currently allocated
+{
+    const size_t overhead = sizeof(psMemBlock) + sizeof(void *); // overhead on each allocation
+
+    if (print) {
+        printf("Type       %6s  %10s %8s\n", "size", "nByte", "nBlock");
+    }
+
+    // this lock is rarely called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+    /*
+     * All memory that's currently allocated, whether persistent or not
+     */
+    size_t allocated_s, persistent_s;
+    if (allocated == NULL) {
+        allocated = &allocated_s;
+    }
+    if (persistent == NULL) {
+        persistent = &persistent_s;
+    }
+
+    size_t alloc = 0, persist = 0;
+    size_t nalloc = 0, npersist = 0;
+    for (psMemBlock* ptr = lastMemBlockAllocated; ptr != NULL; ptr = ptr->nextBlock) {
+        assert (ptr->refCounter > 0);
+
+        if (ptr->persistent) {
+            npersist++;
+            persist += ptr->userMemorySize + overhead;
+        } else {
+            nalloc++;
+            alloc += ptr->userMemorySize + overhead;
+        }
+    }
+    *allocated = alloc;
+    *persistent = persist;
+
+    if (print) {
+        printf("Allocated  %6s  %10zd %8zd\n", "", alloc, nalloc);
+        printf("Persistent %6s  %10zd %8zd\n", "", persist, npersist);
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return *allocated + *persistent;
+}
+
+int psMemBlockPrint(FILE *output, const psMemBlock *memBlock)
+{
+    return fprintf(output,
+                   "Memory Block ID: %lu @ %p\n"
+                   "\tPrevious Block: %p Next Block: %p\n"
+                   "\tFree function: %p\n"
+                   "\tSize: %zd Reference count: %lu Persistent: %s\n"
+                   "\tPosts: %x %x %x\n"
+                   "\tAllocated in %s at (%s:%d)\n"
+                   "\t\tby Thread ID %lu\n",
+                   memBlock->id, memBlock,
+                   memBlock->previousBlock, memBlock->nextBlock,
+                   memBlock->freeFunc,
+                   memBlock->userMemorySize, memBlock->refCounter, (memBlock->persistent ? "Yes" : "No"),
+                   memBlock->startblock, memBlock->endblock,
+                   *(psU32 *)((char *) (memBlock + 1) + memBlock->userMemorySize),
+                   memBlock->func, memBlock->file, memBlock->lineno, (unsigned long)memBlock->tid);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psMemory.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psMemory.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psMemory.h	(revision 22322)
@@ -0,0 +1,668 @@
+/* @file  psMemory.h
+ * @brief Contains the definitions for the memory management system
+ *
+ *  @brief Contains the definitions for the memory management system
+ *
+ *  This is the generic memory management system put inbetween the user's high
+ *  level code and the OS-level memory allocation routines.  This system adds
+ *  such features as callback routines for memory error events, tracing
+ *  capabilities, and reference counting.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Robert Lupton, Princeton University
+ *  @author Joshua Hoblitt, University of Hawaii
+ *
+ *  @ingroup MemoryManagement
+ *
+ *  @version $Revision: 1.73 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-09-28 00:36:08 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_MEMORY_H
+#define PS_MEMORY_H
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+#include <stdio.h>                      // needed for FILE
+#include <pthread.h>                    // mutexes
+#include <stdint.h>                     // for uint32_t
+#include <stdbool.h>
+
+/** @addtogroup MemoryManagement
+ *  @{
+ */
+
+/// typedef for memory identification numbers.  Guaranteed to be some variety of integer.
+typedef unsigned long psMemId;
+
+/// typedef for a memory block's reference count. Guaranteed to be some variety of integer.
+typedef unsigned long psReferenceCount;
+
+/// typedef for deallocator.
+typedef void (*psFreeFunc) (void *ptr);
+
+/** Book-keeping data for storage allocator.
+ *  N.b. sizeof(psMemBlock) must be chosen such that if ptr is a pointer
+ *  returned by malloc, then ((char *)ptr + sizeof(psMemBlock)) is properly
+ *  aligned for all storage types.
+ */
+
+// The memory overhead of a psMemBlock + the trailing post can be checked in
+// gdb with the following command:
+//      p sizeof(psMemBlock) + sizeof(void*)
+typedef struct psMemBlock
+{
+    const uint32_t startblock;          ///< initialised to p_psMEMMAGIC
+    struct psMemBlock *previousBlock;   ///< previous block in allocation list
+    struct psMemBlock *nextBlock;       ///< next block allocation list
+    psFreeFunc freeFunc;                ///< deallocator.  If NULL, use generic deallocation.
+    size_t userMemorySize;              ///< the size of the user-portion of the memory block
+    const psMemId id;                   ///< a unique ID for this allocation
+    const pthread_t tid;                ///< set from pthread_self();
+    const char *file;                   ///< set from __FILE__ in e.g. p_psAlloc
+    const unsigned int lineno;          ///< set from __LINE__ in e.g. p_psAlloc
+    const char *func;                   ///< set from __func__
+
+#if defined(PS_MEM_BACKTRACE) && defined(HAVE_BACKTRACE)
+    const void **backtrace;             ///< set from backtrace()
+    const size_t backtraceSize;         ///< set from bracktrace()
+#endif // defined(PS_MEM_BACKTRACE) && defined(HAVE_BACKTRACE)
+
+    psReferenceCount refCounter;        ///< how many times pointer is referenced
+    bool persistent;                    ///< marks if this non-user persistent data like error stack, etc.
+    const uint32_t endblock;            ///< initialised to p_psMEMMAGIC
+}
+psMemBlock;
+
+/** prototype of a basic callback used by memory functions
+ *
+ *  @see psMemAllocCallbackSet
+ */
+typedef psMemId(*psMemAllocCallback) (
+    const psMemBlock *ptr              ///< the psMemBlock just allocated
+);
+
+/** prototype of memory free callback used by memory functions
+ *
+ *  @see psMemFreeCallbackSet
+ */
+typedef psMemId(*psMemFreeCallback) (
+    const psMemBlock *ptr              ///< the psMemBlock being freed
+);
+
+/** prototype of a callback used in error conditions
+ *
+ *  This callback should not try to call psAlloc or psFree.
+ *
+ *  @see psMemProblemCallbackSet
+ */
+typedef void (*psMemProblemCallback) (
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psMemBlock *ptr                     ///< the pointer to the problematic memory block.
+);
+
+/** prototype of a callback function used when memory runs out
+ *
+ *  @return void * pointer to requested buffer of the size size_t, or NULL if
+ *  memory could not be found.
+ *
+ *  @see psMemExhaustedCallbackSet
+ */
+typedef void *(*psMemExhaustedCallback) (
+    size_t size                         ///< the size of buffer required
+);
+
+/** Memory allocation.  This operates much like malloc(), but is guaranteed to
+ * return a non-NULL value.
+ *
+ *  @return void * pointer to the allocated buffer. This will not be NULL.
+ *  @see psFree
+ */
+#ifdef DOXYGEN
+void *psAlloc(
+    size_t size                        ///< Size required
+);
+#else // ifdef DOXYGEN
+void *p_psAlloc(
+    const char *file,                  ///< File of caller
+    unsigned int lineno,               ///< Line number of caller
+    const char *func,                  ///< Function name of caller
+    size_t size                        ///< Size required
+#ifdef __GNUC__
+) __attribute__((malloc));
+# else // ifdef __GNUC__
+);
+#endif // ifdef __GNUC__
+#ifndef SWIG
+#define psAlloc(size) \
+p_psAlloc(__FILE__, __LINE__, __func__, size)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Set the deallocator routine
+ *
+ *  A deallocator routine can optionally be assigned to a memory block to
+ *  ensure that associated memory blocks also get freed, e.g., memory buffers
+ *  referenced within a struct.
+ *
+ */
+#ifdef DOXYGEN
+void psMemSetDeallocator(
+    void *ptr,                         ///< the memory block to operate on
+    psFreeFunc freeFunc                ///< the function to be executed at deallocation
+);
+#else // ifdef DOXYGEN
+void p_psMemSetDeallocator(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    void *ptr,                          ///< the memory block to operate on
+    psFreeFunc freeFunc                 ///< the function to be executed at deallocation
+);
+#ifndef SWIG
+#define psMemSetDeallocator(ptr, freeFunc) \
+      p_psMemSetDeallocator(__FILE__, __LINE__, __func__, ptr, freeFunc)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Get the deallocator routine
+ *
+ *  This function returns the deallocator for a memory block.  A deallocator
+ *  routine can optionally be assigned to a memory block to ensure that
+ *  associated memory blocks also get freed, e.g., memory buffers referenced
+ *  within a struct.
+ *
+ *  @return psFreeFunc    the routine to be called at deallocation.
+ */
+#ifdef DOXYGEN
+psFreeFunc psMemGetDeallocator(
+    void *ptr                           ///< the memory block
+);
+#else // ifdef DOXYGEN
+psFreeFunc p_psMemGetDeallocator(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    void *ptr                           ///< the memory block
+);
+#ifndef SWIG
+#define psMemGetDeallocator(ptr) \
+      p_psMemGetDeallocator(__FILE__, __LINE__, __func__, ptr)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Activate or Deactivate thread safety and mutex locking in the memory
+ * management.
+ *
+ *  psMemThreadSafety shall turn on thread safety in the memory management
+ *  functions if safe is true, and deactivate all mutex locking in the memory
+ *  management functions if safe is false.  The function shall return the
+ *  previous value of the thread safety.  Note that the default behaviour of
+ *  the library shall be for the locking to be performed.
+ *
+ *  @return bool:       The previous value of the thread safety.
+ */
+bool psMemSetThreadSafety(
+    bool safe                          ///< boolean for turning on/off thread safety
+);
+
+
+/** Get the current state of thread safety and mutex locking in the memory
+ * management.
+ *
+ * psMemGetThreadSafety shall return the current state of thread safety in the
+ * memory management system.
+ *
+ *  @return bool:       The current state of thread safety.
+ */
+bool psMemGetThreadSafety(void);
+
+
+/** Set the memory as persistent so that it is ignored when detecting memory
+ * leaks.
+ *
+ *  Used to mark a memory block as persistent data within the library,
+ *  i.e., non user-level data used to hold psLib's state or cache data.  Such
+ *  examples of this class of memory is psTrace's trace-levels and dynamic
+ *  error codes.
+ *
+ *  Memory marked as persistent is excluded from memory leak checks.
+ *
+ */
+#ifdef DOXYGEN
+void psMemSetPersistent(
+    void *ptr,                          ///< the memory block to operate on
+    bool value,                         ///< true if memory is persistent, otherwise false
+);
+#else // ifdef DOXYGEN
+void p_psMemSetPersistent(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    void *ptr,                          ///< the memory block to operate on
+    bool value                          ///< true if memory is persistent, otherwise false
+);
+#ifndef SWIG
+#define psMemSetPersistent(ptr, value) \
+      p_psMemSetPersistent(__FILE__, __LINE__, __func__, ptr, value)
+#endif // idndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Set whether allocated memory is persistent
+ *
+ *  Set whether allocated memory is persistent. The defeault is false.
+ *
+ *  @return bool:       The previous value of whether all allocated memory is
+ *  persistent
+ */
+bool p_psMemAllocatePersistent(bool is_persistent); ///< Should all memory allocated be persistent?
+
+
+/** Get the memory's persistent flag.
+ *
+ *  Checks if a memory block has been marked as persistent by
+ *  p_psMemSetPresistent.
+ *
+ *  Memory marked as persistent is excluded from memory leak checks.
+ *
+ *  @return bool    true if memory is marked persistent, otherwise false.
+ */
+#ifdef DOXYGEN
+bool psMemGetPersistent(
+    void *ptr,                          ///< the memory block to check.
+);
+#else // ifdef DOXYGEN
+bool p_psMemGetPersistent(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    void *ptr                           ///< the memory block to check.
+);
+#ifndef SWIG
+#define psMemGetPersistent(ptr) \
+      p_psMemGetPersistent(__FILE__, __LINE__, __func__, ptr)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Memory re-allocation.  This operates much like realloc(), but is guaranteed
+ * to return a non-NULL value.
+ *
+ *  @return void * pointer to resized buffer. This will not be NULL.
+ *  @see psAlloc, psFree
+ */
+#ifdef DOXYGEN
+void *psRealloc(
+    void *ptr,                          ///< Pointer to re-allocate
+    size_t size                         ///< Size required
+);
+#else // ifdef DOXYGEN
+void *p_psRealloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    void *ptr,                          ///< Pointer to re-allocate
+    size_t size                         ///< Size required
+#ifdef __GNUC__
+) __attribute__((malloc));
+# else // ifdef __GNUC__
+);
+#endif // ifdef __GNUC__
+#ifndef SWIG
+#define psRealloc(ptr, size) \
+      p_psRealloc(__FILE__, __LINE__, __func__, ptr, size)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Free memory.  This operates much like free().
+ *
+ *  @see psAlloc, psRealloc
+ */
+#ifdef DOXYGEN
+void psFree(
+    void *ptr                           ///< Pointer to free, if NULL, function returns immediately.
+);
+#else // ifdef DOXYGEN
+#ifndef SWIG
+#define psFree(ptr) \
+        psMemDecrRefCounter(ptr)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Check for memory leaks.  This scans for allocated memory buffers not freed
+ * with an ID not less than id0.  This is used to check for memory leaks by: -#
+ * before a block of code to be checked, store the current ID count via
+ * psGetMemId -# after the block of code to be checked, call this function
+ * using the ID stored above.  If all memory in the block that was allocated
+ * has been freed, this call should output nothing and return 0.
+ *
+ *  If memory leaks are found, the Memory Problem callback will be called as
+ *  well.
+ *
+ *  @return int  number of memory blocks found as 'leaks', i.e., the number of
+ *  currently allocated memory blocks above id0 that have not been freed.  @see
+ *  psAlloc, psFree, psgetMemId, psMemProblemCallbackSet
+ */
+#ifdef DOXYGEN
+int psMemCheckLeaks(
+    psMemId id0,                       ///< don't list blocks with id < id0
+    psMemBlock ***array,               ///< pointer to array of pointers to leaked blocks, or NULL
+    FILE * fd,                         ///< print list of leaks to fd (or NULL)
+    bool persistence                   ///< make check across all object even persistent ones
+);
+#else // ifdef DOXYGEN
+int p_psMemCheckLeaks(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psMemId id0,                        ///< don't list blocks with id < id0
+    psMemBlock ***array,                ///< pointer to array of pointers to leaked blocks, or NULL
+    FILE * fd,                          ///< print list of leaks to fd (or NULL)
+    bool persistence                    ///< make check across all object even persistent ones
+);
+#ifndef SWIG
+#define psMemCheckLeaks(id0, array, fd, persistence) \
+      p_psMemCheckLeaks(__FILE__, __LINE__, __func__, id0, array, fd, persistence)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Check for memory corruption.  Scans all currently allocated memory buffers
+ * and checks for corruptions, i.e., invalid markers that signify a buffer
+ * under/overflow.
+ *
+ *  @return int
+ *
+ */
+#ifdef DOXYGEN
+int psMemCheckCorruption(
+    FILE *output,                       ///< FILE to write corrupted blocks too
+    bool abort_on_error                 ///< Abort on detecting corruption?
+);
+#else // ifdef DOXYGEN
+int p_psMemCheckCorruption(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    FILE *output,                       ///< FILE to write corrupted blocks too
+    bool abort_on_error                 ///< Abort on detecting corruption?
+);
+#ifndef SWIG
+#define psMemCheckCorruption(output, abort_on_error) \
+      p_psMemCheckCorruption(__FILE__, __LINE__, __func__, output, abort_on_error)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+/** Checks to see if a pointer is to a region of memory that was allocated by psAlloc().
+ *  @return bool
+ *
+ */
+#ifdef DOXYGEN
+bool psMemIsAlloced(
+    const void *ptr                           ///< pointer to memory
+);
+#else // ifdef DOXYGEN
+bool p_psMemIsAlloced(
+    const char *file,
+    unsigned int lineno,
+    const char *func,
+    const void *ptr
+);
+#ifndef SWIG
+#define psMemIsAlloced(ptr) \
+      p_psMemIsAlloced(__FILE__, __LINE__, __func__, ptr)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Return reference counter
+ *
+ *  @return psReferenceCount
+ *
+ */
+#ifdef DOXYGEN
+psReferenceCount psMemGetRefCounter(
+    void *ptr                     ///< Pointer to get refCounter for
+);
+
+#else // ifdef DOXYGEN
+psReferenceCount p_psMemGetRefCounter(
+    const char *file,                   ///< File of call
+    unsigned int lineno,                ///< Line number of call
+    const char *func,                   ///< Function name of caller
+    void *ptr                           ///< Pointer to get refCounter for
+);
+#ifndef SWIG
+#define psMemGetRefCounter(ptr) \
+      p_psMemGetRefCounter(__FILE__, __LINE__, __func__, ptr)
+#endif // !SWIG
+#endif // !DOXYGEN
+
+
+/** Increment reference counter and return the pointer
+ *
+ *  @return void *
+ *
+ */
+#ifdef DOXYGEN
+void *psMemIncrRefCounter(
+    void *ptr                           ///< Pointer to increment refCounter, and return
+);
+#else // ifdef DOXYGEN
+void *p_psMemIncrRefCounter(
+    const char *file,                   ///< File of call
+    unsigned int lineno,                ///< Line number of call
+    const char *func,                   ///< Function name of caller
+    void *ptr                           ///< Pointer to increment refCounter, and return
+);
+#ifndef SWIG
+#define psMemIncrRefCounter(ptr) \
+      p_psMemIncrRefCounter(__FILE__, __LINE__, __func__, ptr)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Decrement reference counter and return the pointer
+ *
+ *
+ *  @return void *    the pointer deremented in refCount, or NULL if pointer is
+ *                   fully dereferenced.
+ */
+#ifdef DOXYGEN
+void *psMemDecrRefCounter(
+    void *ptr                           ///< Pointer to decrement refCounter, and return
+);
+#else // DOXYGEN
+void *p_psMemDecrRefCounter(
+    const char *file,                   ///< File of call
+    unsigned int lineno,                ///< Line number of call
+    const char *func,                   ///< Function name of caller
+    void *ptr                           ///< Pointer to decrement refCounter, and return
+);
+#ifndef SWIG
+#define psMemDecrRefCounter(ptr) \
+      p_psMemDecrRefCounter(__FILE__, __LINE__, __func__, ptr)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+#if 0 // psMemSetRefCounter
+/** Set reference counter and return the pointer
+ *
+ *  @return void *    the pointer with refCount set, or NULL if pointer is
+ *                   fully dereferenced.
+ */
+#ifdef DOXYGEN
+void * psMemSetRefCounter(
+    void * ptr,                        ///< Pointer to decrement refCounter, and return
+    psReferenceCount count            ///< New reference count
+);
+#else // DOXYGEN
+void * p_psMemSetRefCounter(
+    void * vptr,                        ///< Pointer to decrement refCounter, and return
+    psReferenceCount count,            ///< New reference count
+    const char *file,                  ///< File of call
+    psS32 lineno                       ///< Line number of call
+);
+
+#ifndef SWIG
+#define psMemSetRefCounter(vptr, count) p_psMemSetRefCounter(vptr, count, __FILE__, __LINE__)
+#endif // !SWIG
+
+#endif // !DOXYGEN
+#endif // psMemSetRefCounter
+
+/** Set callback for out-of-memory.
+ *
+ *  If not enough memory is available to satisfy a request by psAlloc or
+ *  psRealloc, these functions attempt to find an alternative solution by
+ *  calling the psMemExhaustedCallback, a function which may be set by the
+ *  programmer in appropriate circumstances, rather than immediately fail.
+ *  The typical use of such a feature may be when a program needs a large
+ *  chunk of memory to do an operation, but the exact size is not critical.
+ *  This feature gives the programmer the opportunity to make a smaller
+ *  request and try again, limiting the size of the operating buffer.
+ *
+ *  @return psMemExhaustedCallback     old psMemExhaustedCallback function
+ */
+psMemExhaustedCallback psMemExhaustedCallbackSet(
+    psMemExhaustedCallback func        ///< Function to run at memory exhaustion
+);
+
+
+/** Set call back for when a particular memory block is allocated
+ *
+ *  A private variable, p_psMemAllocID, can be used to trace the allocation
+ *  and freeing of specific memory blocks. If p_psMemAllocID is set and a
+ *  memory block with that ID is allocated, psMemAllocCallback is called
+ *  just before memory is returned to the calling function.
+ *
+ *  @return psMemAllocCallback      old psMemAllocCallback function
+ */
+psMemAllocCallback psMemAllocCallbackSet(
+    psMemAllocCallback func            ///< Function to run at memory allocation of specific mem block
+);
+
+
+/** Set call back for when a particular memory block is freed
+ *
+ *  A private variable, p_psMemFreeID, can be used to trace the freeing of
+ *  specific memory blocks. If p_psMemFreeID is set and the memory block with
+ *  the ID is about to be freed, the psMemFreeCallback callback is called just
+ *  before the memory block is freed.
+ *
+ *  @return psMemFreeCallback          old psMemFreeCallback function
+ */
+psMemFreeCallback psMemFreeCallbackSet(
+    psMemFreeCallback func             ///< Function to run at memory free of specific mem block
+);
+
+
+/** get next memory ID
+ *
+ *  @return psMemId                 the next memory ID to be used
+ */
+psMemId psMemGetId(void);
+
+
+/** get the last memory ID used
+ *
+ *  @return psMemId                 the last memory ID used
+ */
+psMemId psMemGetLastId(void);
+
+
+/** set p_psMemAllocID to specific id
+ *
+ *  A private variable, p_psMemAllocID, can be used to trace the allocation
+ *  and freeing of specific memory blocks. If p_psMemAllocID is set and a
+ *  memory block with that ID is allocated, psMemAllocCallback is called
+ *  just before memory is returned to the calling function.
+ *
+ *  @return psMemId
+ *
+ *  @see psMemAllocCallbackSet
+ */
+psMemId psMemAllocCallbackSetID(
+    psMemId id                         ///< ID to set
+);
+
+
+/** set p_psMemFreeID to id
+ *
+ *  A private variable, p_psMemFreeID, can be used to trace the freeing of
+ *  specific memory blocks. If p_psMemFreeID is set and the memory block with
+ *  the ID is about to be freed, the psMemFreeCallback callback is called just
+ *  before the memory block is freed.
+ *
+ *  @return psMemId                 the old p_psMemFreeID
+ *
+ *  @see psMemFreeCallbackSet
+ */
+psMemId psMemFreeCallbackSetID(
+    psMemId id                         ///< ID to set
+);
+
+
+/** return statistics on memory usage
+ *
+ * @return the total amount of memory owned by psLib; if non-NULL also provide
+ * a breakdown into allocated and allocated-and-persistent
+ */
+size_t psMemStats(const bool print, ///< print details as they're found?
+                  size_t *allocated, ///< memory that's currently allocated (but not persistent)
+                  size_t *persistent); ///< persistent memory that's currently allocated
+
+/** print detailed information about a psMemBlock
+ *
+ * This function prints a detailed description of a psMemBlock to output.
+ *
+ * @return the return status of fprintf()
+ */
+int psMemBlockPrint(
+    FILE *output,                       ///< FILE to write information too
+    const psMemBlock *memBlock          ///< psMemBlock to be examined
+);
+
+// Ensure this is a psLib pointer
+#define PS_ASSERT_PTR_HEAVY(PTR, RVAL) \
+{ \
+    if (PTR && (!psMemIsAlloced(PTR))) { \
+        psError(PS_ERR_MEMORY_CORRUPTION, false, \
+            "Error: Pointer %p is corrupted or not on the PS memory system.", \
+            PTR); \
+        return RVAL; \
+    } \
+}
+
+/// @} end of SysUtils
+
+#ifndef DOXYGEN
+
+/*
+ * Ensure that any program using malloc/realloc/free will fail to compile
+ */
+#ifndef PS_ALLOW_MALLOC
+#ifdef __GNUC__
+#pragma GCC poison malloc realloc calloc free
+#else // ifdef __GNUC__
+#define malloc(S)       _Pragma("error Use of malloc is not allowed.  Use psAlloc instead.")
+#define realloc(P,S)    _Pragma("error Use of realloc is not allowed.  Use psRealloc instead.")
+#define calloc(S)       _Pragma("error Use of calloc is not allowed.  Use psAlloc instead.")
+#define free(P)         _Pragma("error Use of free is not allowed.  Use psFree instead.")
+#endif // ifdef __GNUC__
+#endif // ifndef PS_ALLOW_MALLOC
+
+#endif // #ifndef DOXYGEN
+#endif // #ifndef PS_MEMORY_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psMutex.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psMutex.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psMutex.h	(revision 22322)
@@ -0,0 +1,95 @@
+#ifndef PS_MUTEX_H
+#define PS_MUTEX_H
+
+#include <pthread.h>
+#include <assert.h>
+
+// We desire that these functions be fast (they may get called a lot), so we've defined them as macros.
+
+//#define PS_MUTEX_CAREFUL                // Use error-checking mutexes, trace messages, check the results
+
+typedef pthread_mutex_t psMutex;        /// Mutual Exclusion using pthreads
+
+// Set type of mutex to use based on what sort of system we're using.
+// GNU systems define a "fast" mutex that is non-portable (i.e., non-standard) that we'd like to use if we can
+#ifdef __USE_GNU
+#ifdef PS_MUTEX_CAREFUL
+
+// These bells and whistles are only defined if PS_MUTEX_CAREFUL and __USE_GNU are both defined
+#define PS_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK_NP // Error-checking mutex
+#define PS_MUTEX_TRACE(LEVEL, STRING, PTR) psTrace("psLib.sys.mutex", LEVEL, STRING, #PTR); // Trace message
+#define PS_MUTEX_TRACE_THREAD(LEVEL, STRING, PTR) \ // Trace message with thread and pointer
+    psTrace("psLib.sys.mutex", LEVEL, STRING, (void*)pthread_self(), #PTR);
+#define PS_MUTEX_CHECK_DEADLOCK() \     // Check for dead-locked mutex
+    if (pml == EDEADLK) { \
+        psAbort("Deadlocked mutex on %s", #PTR); \
+    }
+#define PS_MUTEX_CHECK_STOLEN(PTR) \       // Check for stolen mutex
+    if (pmu == EPERM) { \
+        psAbort("Unlocking stolen mutex on %s", #PTR); \
+    }
+
+#else  // PS_MUTEX_CAREFUL
+
+#define PS_MUTEX_TYPE PTHREAD_MUTEX_FAST_NP // Fast mutex
+#define PS_MUTEX_TRACE(LEVEL, STRING, PTR) // No action
+#define PS_MUTEX_TRACE_THREAD(LEVEL, STRING, PTR) // No action
+#define PS_MUTEX_CHECK_DEADLOCK(PTR)    // No action
+#define PS_MUTEX_CHECK_STOLEN(PTR)      // No action
+
+#endif // PS_MUTEX_CAREFUL
+#else  // __USE_GNU
+
+#define PS_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT // Default mutex type: portable
+#define PS_MUTEX_TRACE(LEVEL, STRING, PTR) // No action
+#define PS_MUTEX_TRACE_THREAD(LEVEL, STRING, PTR) // No action
+#define PS_MUTEX_CHECK_DEADLOCK(PTR)    // No action
+#define PS_MUTEX_CHECK_STOLEN(PTR)      // No action
+
+#endif
+
+
+/// Initialize a mutex
+#define psMutexInit(PTR) { \
+    psAssert(PTR, "Require non-NULL pointer to initialise mutex"); \
+    PS_MUTEX_TRACE(3, "Initialising mutex on %s...", PTR); \
+    /* Gotta go the long way, since we can't use PTHREAD_MUTEX_INITIALIZER after we declare the variable */ \
+    pthread_mutexattr_t attr; /* Mutex attributes */ \
+    pthread_mutexattr_init(&attr); \
+    pthread_mutexattr_settype(&attr, PS_MUTEX_TYPE); \
+    pthread_mutex_init(&(PTR)->lock, &attr); \
+    pthread_mutexattr_destroy(&attr); \
+}
+
+/// Lock a mutex
+#define psMutexLock(PTR) { \
+    psAssert(PTR, "Require non-NULL pointer to lock mutex"); \
+    PS_MUTEX_TRACE_THREAD(1, "Thread %p waiting for mutex lock on %s...", PTR); \
+    int pml = pthread_mutex_lock(&(PTR)->lock); \
+    PS_MUTEX_TRACE_THREAD(1, "Thread %p locked mutex on %s", PTR); \
+    if (pml == EINVAL) { \
+        psAbort("Cannot lock mutex on %s: not initialised", #PTR); \
+    } \
+    PS_MUTEX_CHECK_DEADLOCK(PTR); \
+}
+
+/// Unlock a mutex
+#define psMutexUnlock(PTR) { \
+    psAssert(PTR, "Require non-NULL pointer to unlock mutex"); \
+    PS_MUTEX_TRACE_THREAD(1, "Thread %p unlocking mutex on %s", PTR); \
+    int pmu = pthread_mutex_unlock(&(PTR)->lock); \
+    if (pmu == EINVAL) { \
+        psAbort("Cannot unlock mutex on %s: not initialised", #PTR); \
+    } \
+    PS_MUTEX_CHECK_STOLEN(PTR); \
+}
+
+/// Destroy a mutex
+#define psMutexDestroy(PTR) { \
+    psAssert(PTR, "Require non-NULL pointer to destroy mutex"); \
+    if (pthread_mutex_destroy(&(PTR)->lock) == EBUSY) { \
+        psAbort("Cannot destroy mutex on %s: is busy", #PTR); \
+    } \
+}
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psSlurp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psSlurp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psSlurp.c	(revision 22322)
@@ -0,0 +1,88 @@
+/** @file  psSlurp.c
+ *
+ *  @brief Contains functions for slurping files
+ *
+ *  @author Joshua Hoblitt, University of Hawaii
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-02-27 03:31:56 $
+ *
+ *  Copyright 2006 University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "psType.h"
+#include "psAssert.h"
+#include "psError.h"
+#include "psSlurp.h"
+#include "psMemory.h"
+
+#define SLURP_SIZE 4096
+
+psString psSlurpFD(int fd)
+{
+    psString str = NULL;                // String to which to write
+    size_t size  = 1;                   // bytes allocated -  make sure there is room for '\0'
+    size_t used = 0;                    // bytes actually used
+    ssize_t bytes;                      // Number of bytes read
+    do {
+        // increase the allocated string size
+        size += SLURP_SIZE;
+        str = psRealloc(str, size);
+
+        // read a block from the stream
+        bytes = read(fd, str + used, SLURP_SIZE);
+        if (bytes < 0) {
+            // it's an error
+            psError(PS_ERR_IO, true, "slurp failed on read");
+            psFree(str);
+            return NULL;
+        }
+
+        // Increase the size of the known string
+        used += bytes;
+
+    } while (bytes != 0);
+
+    // append '\0' to the end of the string
+    str[used] = '\0';
+
+    return str;
+}
+
+
+psString psSlurpFile(FILE *stream)
+{
+    return psSlurpFD(fileno(stream));
+}
+
+
+psString psSlurpFilename(const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(filename, NULL);
+
+    int fd = open(filename, O_RDONLY);
+    if (fd < 0) {
+        psError(PS_ERR_IO, true, "Failed to open specified file, %s\n", filename);
+        return NULL;
+    }
+
+    psString text = psSlurpFD(fd);
+
+    if (close(fd) != 0) {
+        psError(PS_ERR_IO, true, "Failed to close specified file, %s\n", filename);
+        psFree(text);
+        return NULL;
+    }
+
+    return text;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psSlurp.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psSlurp.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psSlurp.h	(revision 22322)
@@ -0,0 +1,34 @@
+/* @file  psSlurp.h
+ * @brief read complete files into strings
+ *
+ * @author Joshua Hoblitt, University of Hawaii
+ *
+ * @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-10-29 21:30:03 $
+ */
+
+#ifndef PS_SLURP_H
+#define PS_SLURP_H
+
+#include <psString.h>
+
+/// @addtogroup FileIO Input/Output
+/// @{
+
+// Read ("slurp") a file (specified by file descriptor)
+// and return a string containing the entire file.
+psString psSlurpFD(int fd               // File descriptor to read
+                  );
+
+// Read ("slurp") a file (specified by file stream)
+// and return a string containing the entire file.
+psString psSlurpFile(FILE *stream       // File stream to read
+                    );
+
+// Read ("slurp") a file (specified by filename)
+// and return a string containing the entire file.
+psString psSlurpFilename(const char *filename // Filename
+                    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psString.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psString.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psString.c	(revision 22322)
@@ -0,0 +1,467 @@
+
+/** @file  psString.c
+ *
+ * -*- mode: C; c-basic-indent: 4; tab-width: 8; indent-tabs-mode: nil -*-
+ * vim: set cindent ts=8 sw=4 expandtab:
+ *
+ *  @brief Contains the definition of string utility functions
+ *
+ *  String utility functions defined shall provide basic string copying
+ *  capabilities while using the preferred memory management utilities.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *  @author David Robbins, MHPCC
+ *
+ *  @version $Revision: 1.61 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-15 00:45:18 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "psString.h"
+#include "psError.h"
+#include "psAssert.h"
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psConstants.h"
+
+static void stringFree(psString string)
+{
+    // this function is only necessary so psMemCheckString has a function
+    // pointer address to test against
+}
+
+
+psString p_psStringAlloc(const char *file,
+                         unsigned int lineno,
+                         const char *func,
+                         size_t nChar)
+{
+    psString string = p_psAlloc(file, lineno, func, nChar + 1);
+    psMemSetDeallocator(string, (psFreeFunc)stringFree);
+
+    return string;
+}
+
+
+bool psMemCheckString(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return (psMemGetDeallocator(ptr) == (psFreeFunc)stringFree);
+}
+
+
+psString p_psStringCopy(const char *file,
+                        unsigned int lineno,
+                        const char *func,
+                        const char *string)
+{
+    // Pass through NULL values!
+    if (!string) {
+        return NULL;
+    }
+
+    // Output string
+    psString output = p_psStringAlloc(file, lineno, func, strlen(string));
+
+    // Copy input string to memory just allocated
+    return strcpy(output, string);
+}
+
+
+psString p_psStringNCopy(const char *file,
+                         unsigned int lineno,
+                         const char *func,
+                         const char *string,
+                         size_t nChar)
+{
+    // Pass through NULL values
+    if (!string) {
+        return NULL;
+    }
+
+    // Check the number of characters to copy is non-negative
+    if (nChar < 0) {
+        // Log error message and return NULL
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Can not copy a negative number of characters (%zd)."),
+                nChar);
+        return NULL;
+    }
+
+    // Copy input string to memory allocated up to nChar characters
+    psString output = p_psStringAlloc(file, lineno, func, nChar);
+    output = strncpy(output, string, nChar);
+
+    // Ensure the last byte is NULL character
+    output[nChar] = '\0';
+
+    // Return the string pointer
+    return output;
+}
+
+
+ssize_t p_psStringAppend(const char *file,
+                         unsigned int lineno,
+                         const char *func,
+                         psString *dest,
+                         const char *format,
+                         ...)
+{
+    PS_ASSERT_PTR_NON_NULL(dest, 0);
+    PS_ASSERT_PTR(*dest, 0);
+    PS_ASSERT_STRING_NON_EMPTY(format, 0);
+
+    va_list ap;
+    va_start(ap, format);
+    ssize_t length = p_psStringAppendV(file, lineno, func, dest, format, ap);
+    va_end(ap);
+
+    return length;
+}
+
+ssize_t p_psStringAppendV(const char *file,
+                          unsigned int lineno,
+                          const char *func,
+                          psString *dest,
+                          const char *format,
+                          va_list ap)
+{
+    PS_ASSERT_PTR_NON_NULL(dest, 0);
+    PS_ASSERT_PTR(*dest, 0);
+    PS_ASSERT_STRING_NON_EMPTY(format, 0);
+
+    size_t          length = 0;             // complete string length (sans \0)
+    size_t          oldLength = 0;          // original string length (sans \0)
+    ssize_t         tailLength = 0;         // length of string to append
+
+    if (*dest && psMemGetRefCounter(*dest) > 1) {
+        psWarning("Appending to a string with multiple reference counts may corrupt memory!");
+    }
+
+    if (*dest) {
+        oldLength = strlen(*dest);
+    } else {
+        oldLength = 0;
+    }
+
+    va_list         apCopy;
+
+    // find the size of the string to append
+    // C99 guarentees vsnprintf() to work as expected with size = 0
+    // BUT: the output cannot be a NULL string
+    va_copy(apCopy, ap);
+    char tmp = 0;
+    tailLength = vsnprintf(&tmp, 0, format, apCopy);
+    va_end(apCopy);
+
+    // if the new tail is zero length, return the length of the old string.  if
+    // it's a format error, return the error code.
+    if (tailLength < 1) {
+        return tailLength == 0 ? oldLength : tailLength;
+    }
+
+    // realloc string to string + tail + \0
+    if (*dest) {
+        *dest = p_psRealloc(file, lineno, func, *dest, oldLength + tailLength + 1);
+    } else {
+        *dest = p_psStringAlloc(file, lineno, func, oldLength + tailLength + 1);
+    }
+
+    // append tail + \0
+    // XXX this second call to va_copy() isn't strictly nessicary as the
+    // calling function can't assume we won't modify the va_list.  However, we
+    // have decided to error on the side of caution.
+    va_copy(apCopy, ap);
+    vsnprintf(*dest + oldLength, tailLength + 1, format, apCopy);
+    va_end(apCopy);
+
+    return length;
+}
+
+
+ssize_t p_psStringPrepend(const char *file,
+                          unsigned int lineno,
+                          const char *func,
+                          psString *dest,
+                          const char *format,
+                          ...)
+{
+    PS_ASSERT_PTR_NON_NULL(dest, 0);
+    PS_ASSERT_PTR(*dest, 0);
+    PS_ASSERT_STRING_NON_EMPTY(format, 0);
+
+    va_list ap;
+    va_start(ap, format);
+    ssize_t length = p_psStringPrependV(file, lineno, func, dest, format, ap);
+    va_end(ap);
+
+    return length;
+}
+
+
+ssize_t p_psStringPrependV(const char *file,
+                           unsigned int lineno,
+                           const char *func,
+                           psString *dest,
+                           const char *format,
+                           va_list ap)
+{
+    PS_ASSERT_PTR_NON_NULL(dest, 0);
+    PS_ASSERT_PTR(*dest, 0);
+    PS_ASSERT_STRING_NON_EMPTY(format, 0);
+
+    size_t          length;             // complete string length (sans \0)
+    ssize_t         headLength;         // length of string to prepend
+    char            *oldDest;           // copy of original string
+
+    if (*dest && psMemGetRefCounter(*dest) > 1) {
+        psWarning("Appending to a string with multiple reference counts may corrupt memory!");
+    }
+
+    if (!*dest) {
+        // makes the string backup and concatination pointless
+        *dest = p_psStringCopy(file, lineno, func, "");
+        length = 0;
+    } else {
+        // size of existing string
+        length = strlen(*dest);
+    }
+
+    va_list apCopy;
+
+    // find the size of the string to prepend
+    // C99 guarentees vsnprintf() to work as expected with size = 0
+    va_copy(apCopy, ap);
+    char tmp = 0;
+    headLength = vsnprintf(&tmp, 0, format, apCopy);
+    va_end(apCopy);
+
+    // if the new head is zero length, return the length of the old string.  if
+    // it's a format error, return the error code.
+    if (headLength < 1) {
+        return headLength == 0 ? length : headLength;
+    }
+
+    // backup original string
+    oldDest = p_psStringCopy(file, lineno, func, *dest);
+
+    // new string length (sans \0)
+    length += headLength;
+
+    // realloc string to head + string + \0
+    *dest = p_psRealloc(file, lineno, func, *dest, length + 1);
+
+    // copy the new head to the beginning of string
+    // XXX this second call to va_copy() isn't strictly nessicary as the
+    // calling function can't assume we won't modify the va_list.  However, we
+    // have decided to error on the side of caution.
+    va_copy(apCopy, ap);
+    vsnprintf(*dest, length + 1, format, apCopy);
+    va_end(apCopy);
+
+    // append the original string
+    strncat(*dest, oldDest, length + 1);
+
+    psFree(oldDest);
+
+    return length;
+}
+
+
+// split the string by the given splitters
+// NULL input string returns empty (not NULL) list
+// NULL splitters is an error
+psList *p_psStringSplit(const char *file,
+                        unsigned int lineno,
+                        const char *func,
+                        const char *string,
+                        const char *splitters,
+                        bool multipleAreSignificant)
+{
+    PS_ASSERT_STRING_NON_EMPTY(splitters, NULL);
+
+    psList *values = p_psListAlloc(file, lineno, func, NULL); // The list of values to return
+    // An input NULL string should not generate an error: it is a valid case
+    if (string == NULL) {
+        return values;
+    }
+
+    char *next = NULL;
+    char *current = (char *) string;
+    while ((next = strpbrk (current, splitters)) != NULL) {
+
+        // are multiple splitters in-a-row significant?
+        if ((next == current) && !multipleAreSignificant) {
+            current ++;
+            continue;
+        }
+
+        // Copy the current word
+        psString word = p_psStringNCopy(file, lineno, func, current, next - current);
+        psListAdd(values, PS_LIST_TAIL, word);
+        psFree(word);
+
+        current = next + 1;
+    }
+
+    if (strlen(current) > 0) {
+        // Copy the last word
+        psString word = p_psStringCopy(file, lineno, func, current);
+        (void)psListAdd(values, PS_LIST_TAIL, word);
+        psFree(word);
+    }
+
+    return values;
+}
+
+
+// given the input string, search for all copies of the key, and replace with the replacement value
+// the input string may be freed if not needed
+ssize_t psStringSubstitute(psString *input,
+                           const char *replace,
+                           const char *key)
+{
+    PS_ASSERT_PTR_NON_NULL(input, 0);
+
+    // Empty input gives empty output
+    if (!*input || strlen(*input) == 0) {
+        return 0;
+    }
+    // No key gives the same as the input
+    if (!key || strlen(key) == 0) {
+        return strlen(*input);
+    }
+    if (!psMemCheckString(*input)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "This function requires a psString as input.\n");
+        return 0;
+    }
+
+
+    // replace == NULL is valid: it just means that we strip out the key
+    // without putting anything else in its place
+    size_t replaceLength;               // Size of "replace" string
+    if (replace) {
+        replaceLength = strlen(replace);
+    } else {
+        replaceLength = 0;
+    }
+
+    char *search = *input;              // Search this string
+    while (true) {
+        char *found = strstr(search, key); // Found occurence of the key
+        if (!found) {
+            return strlen(*input);
+        }
+
+        // we have input = xxxkeyxxx
+        // we want output = xxxreplacexxx
+
+        // this is safe since we will subtract strlen(key) elements from input
+        psString output = psStringAlloc(strlen(*input) + replaceLength + 1); // Output string
+        int numChar = found - *input;    // Number of characters to copy
+
+        // copy the first segement into 'output'
+        strncpy(output, *input, numChar);
+
+        // copy the key replacement to the start of the key
+        if (replace && replaceLength > 0) {
+            strcpy(output + numChar, replace);
+            numChar += replaceLength;
+        }
+
+        // copy the remainder to the end of the replacement
+        strcpy(output + numChar, found + strlen(key));
+
+        psFree(*input);
+        *input = output;
+        search = output + numChar;
+    }
+
+    psAbort("Should never get here.\n");
+    return 0;
+}
+
+
+psArray *psStringSplitArray(const char *string, const char *splitters, bool multi)
+{
+
+    psList *list = psStringSplit(string, splitters, multi);
+    psArray *array = psListToArray(list);
+    psFree (list);
+    return array;
+}
+
+
+#ifndef whitespace
+#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
+#endif
+
+/* Strip whitespace from the start and end of STRING. */
+size_t psStringStrip(char *string)
+{
+    long i;
+
+    if (!string || strlen(string) == 0) {
+        return 0;
+    }
+
+    for (i = 0; i < strlen(string) && whitespace(string[i]); i++)
+        ; // No action
+    if (i) {
+        memmove (string, string + i, strlen(string+i)+1);
+    }
+    for (i = strlen (string) - 1; (i > 0) && whitespace(string[i]); i--)
+        ; // No action
+    string[++i] = 0;
+
+    return i;
+}
+
+
+/*
+ * Used by PS_FILE_LINE
+ */
+const char *p_psFileLine(const char *file, int line)
+{
+    static char msg[100];
+    sprintf(msg, "%s:%d", file, line);
+    return msg;
+}
+
+
+psString psStringStripCVS(const char *string, const char *tagName)
+{
+    PS_ASSERT_STRING_NON_EMPTY(string, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(tagName, NULL);
+
+    psString tagString = NULL;          // Tag string, e.g., "$Name:" from "Name"
+    psStringAppend(&tagString, "$%s:", tagName);
+    char *p = strstr(string, tagString); // The start of the real tag
+    psFree(tagString);
+    if (!p) {
+        return psStringCopy("UNKNOWN");
+    }
+
+    psString tag = psStringCopy(string + 6); // The tag, without the leading tagString
+    p = strrchr(tag, '$');              // The closing dollar sign
+    if (p) {
+        *p = 0;
+    }
+    psStringStrip(tag);
+    if (*tag == 0) {
+        psFree(tag);
+        return psStringCopy("UNKNOWN");
+    }
+
+    return tag;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psString.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psString.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psString.h	(revision 22322)
@@ -0,0 +1,319 @@
+/* @file  psString.h
+ *
+ * @brief string utility functions
+ *
+ * String utility functions defined shall provide basic string copying
+ * capabilities.
+ *
+ * @author EAM, IfA
+ * @author Eric Van Alst, MHPCC
+ * @author David Robbins, MHPCC
+ * @author Joshua Hoblitt, University of Hawaii
+ *
+ * @version $Revision: 1.44 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-09 00:47:41 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_STRING_H
+#define PS_STRING_H
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+#include <sys/types.h>
+#include <stdarg.h>
+
+#include "psType.h"
+#include "psList.h"
+
+/** This macro will convert the argument to a quoted string */
+#define PS_STRING(S)  #S
+
+/** This macro returns a (static buffer containing) "file:line" */
+const char *p_psFileLine(const char *file, int line)
+#ifdef __GNUC__
+__attribute__((deprecated))
+#endif // ifdef __GNUC__
+;
+#define PS_FILE_LINE p_psFileLine(__FILE__,__LINE__)
+
+
+/** Allocates a new psString.
+ *
+ *  @return psString:       Newly allocated string of length n.
+ */
+#ifdef DOXYGEN
+psString psStringAlloc(
+    size_t nChar                        ///< Size of psString to allocate.
+);
+#else // ifdef DOXYGEN
+psString p_psStringAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    size_t nChar                        ///< Size of psString to allocate.
+) PS_ATTR_MALLOC;
+#define psStringAlloc(nChar) \
+      p_psStringAlloc(__FILE__, __LINE__, __func__, nChar)
+#endif // ifdef DOXYGEN
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr
+ *  datatype.
+ *
+ *  @return bool:       True if the pointer matches a psString structure, false otherwise.
+ */
+bool psMemCheckString(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Copies the input string
+ *
+ *  This function shall allocate memory to the length of the input string plus
+ *  one and copy the input string to the newly allocated memory.  If 'string'
+ *  is 'NULL' then 'NULL' is returned.
+ *
+ *  @return psString:      Copy of input string
+ */
+#ifdef DOXYGEN
+psString psStringCopy(
+    const char *string                  ///< Input string of characters to copy
+);
+#else // ifdef DOXYGEN
+psString p_psStringCopy(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const char *string                  ///< Input string of characters to copy
+);
+#define psStringCopy(string) \
+      p_psStringCopy(__FILE__, __LINE__, __func__, string)
+#endif // ifdef DOXYGEN
+
+
+/** Copies the input string up to the specified number of characters
+ *
+ *  This function shall allocate memory to the length specified by nChar
+ *  plus one and copy the input string to the newly allocated memory.
+ *  This function will only copy nChar bytes from the input to new string,
+ *  so if the input string is larger than nChar characters the copied
+ *  string will be a substring of the input string.  If the input string
+ *  is smaller than nChar bytes then the remaining bytes allocated will
+ *  be set to NULL.  If 'string' is 'NULL' then 'NULL' is returned.
+ *
+ *  @return  psString:   Copy of input string
+ */
+#ifdef DOXYGEN
+psString psStringNCopy(
+    const char *string,                 ///< Input string of characters to copy
+    size_t nChar                        ///< Number of bytes to allocate for string copy
+);
+#else // ifdef DOXYGEN
+psString p_psStringNCopy(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const char *string,                 ///< Input string of characters to copy
+    size_t nChar                        ///< Number of bytes to allocate for string copy
+);
+#define psStringNCopy(string, nChar) \
+      p_psStringNCopy(__FILE__, __LINE__, __func__, string, nChar)
+#endif // ifdef DOXYGEN
+
+
+/** Appends a format onto a string
+ *
+ * This function shall allocate a new string if dest is NULL.  dest shall be
+ * automatically extended to the size of the new string.
+ *
+ * @return ssize_t:     The length of the new string (excluding '\0')
+ */
+#ifdef DOXYGEN
+ssize_t psStringAppend(
+    psString *dest,                     ///< existing string
+    const char *format,                 ///< format to append
+    ...                                 ///< format arguments
+);
+#else // ifdef DOXYGEN
+ssize_t p_psStringAppend(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psString *dest,                     ///< existing string
+    const char *format,                 ///< format to append
+    ...                                 ///< format arguments
+) PS_ATTR_FORMAT(printf, 5, 6);
+#define psStringAppend(dest, ...) \
+      p_psStringAppend(__FILE__, __LINE__, __func__, dest, __VA_ARGS__)
+#endif // ifdef DOXYGEN
+
+
+/** Appends a format onto a string
+ *
+ * This function shall allocate a new string if dest is NULL.  dest shall be
+ * automatically extended to the size of the new string.
+ *
+ * @return ssize_t:     The length of the new string (excluding '\0')
+ */
+#ifdef DOXYGEN
+ssize_t psStringAppendV(
+    psString *dest,                     ///< existing string
+    const char *format,                 ///< format to append
+    va_list ap                          ///< va_list of format arguments
+);
+#else // ifdef DOXYGEN
+ssize_t p_psStringAppendV(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psString *dest,                     ///< existing string
+    const char *format,                 ///< format to append
+    va_list ap                          ///< va_list of format arguments
+);
+#define psStringAppendV(dest, format, ap) \
+      p_psStringAppendV(__FILE__, __LINE__, __func__, dest, format, ap)
+#endif // ifdef DOXYGEN
+
+
+/** Prepends a format onto a string
+ *
+ * This function shall allocate a new string if dest is NULL.  dest shall be
+ * automatically extended to the size of the new string.
+ *
+ * @return ssize_t:     The length of the new string (excluding '\0')
+ */
+#ifdef DOXYGEN
+ssize_t psStringPrepend(
+    psString *dest,                     ///< existing string
+    const char *format,                 ///< format to append
+    ...                                 ///< format arguments
+);
+#else // ifdef DOXYGEN
+ssize_t p_psStringPrepend(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psString *dest,                     ///< existing string
+    const char *format,                 ///< format to append
+    ...                                 ///< format arguments
+) PS_ATTR_FORMAT(printf, 5, 6);
+#define psStringPrepend(dest, ...) \
+      p_psStringPrepend(__FILE__, __LINE__, __func__, dest, __VA_ARGS__)
+#endif // ifdef DOXYGEN
+
+
+/** Prepends a format onto a string
+ *
+ * This function shall allocate a new string if dest is NULL.  dest shall be
+ * automatically extended to the size of the new string.
+ *
+ * @return ssize_t:     The length of the new string (excluding '\0')
+ */
+#ifdef DOXYGEN
+ssize_t psStringPrependV(
+    psString *dest,                     ///< existing string
+    const char *format,                 ///< format to append
+    va_list ap                          ///< va_list of format arguments
+);
+#else // ifdef DOXYGEN
+ssize_t p_psStringPrependV(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psString *dest,                     ///< existing string
+    const char *format,                 ///< format to append
+    va_list ap                          ///< va_list of format arguments
+);
+#define psStringPrependV(dest, format, ap) \
+      p_psStringPrependV(__FILE__, __LINE__, __func__, dest, format, ap)
+#endif // ifdef DOXYGEN
+
+
+/** Procedure to split the input string into a psList of psStrings.
+ *
+ *  The string is split at any one of the characters in splitters.  Split
+ *  strings of zero length should not be included in the output list.
+ *
+ *  @return psList*:    The list of (split) psStrings.
+ */
+#ifdef DOXYGEN
+psList *psStringSplit(
+    const char *string,                ///< String to split
+    const char *splitters,             ///< Characters on which to split
+    bool multipleAreSignificant        ///< Are multiple occurences significant?
+);
+#else // ifdef DOXYGEN
+psList *p_psStringSplit(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const char *string,                ///< String to split
+    const char *splitters,             ///< Characters on which to split
+    bool multipleAreSignificant        ///< Are multiple occurences significant?
+    );
+#define psStringSplit(string, splitters, multiple) \
+      p_psStringSplit(__FILE__, __LINE__, __func__, string, splitters, multiple)
+#endif // ifdef DOXYGEN
+
+/** Procedure to split the input string into a psArray of psStrings.
+ *
+ *  The string is split at any one of the characters in splitters.  Split
+ *  strings of zero length should not be included in the output list.
+ *
+ *  @return psArray*:       The array of (split) psStrings.
+ */
+psArray *psStringSplitArray(
+    const char *string,                ///< String to split
+    const char *splitters,             ///< Characters on which to split
+    bool multipleAreSignificant        ///< Are multiple occurences significant?
+);
+
+// Given the input string, search for all copies of the key, and replace with
+// the replacement value the input string may be freed if not needed
+/** Procedure to search an input string and substitute strings where desired.
+ *
+ *  The input string is searched for all instances of the key, which is then
+ *  replaced with the replacement value wherever found.  The input string may
+ *  be freed if not needed.
+ *
+ *  @return ssize_t:      the length of the new string (excluding '\0')
+ */
+ssize_t psStringSubstitute (
+    psString *input,                    ///< ptr to input string to be modified
+    const char *replace,                ///< replacement value
+    const char *key                     ///< string to be replaced in input
+);
+
+
+// strip whitespace from head and tail of string
+/** Procedure to strip the whitespace from the head and tail of a string.
+ *
+ *  @return size_t:         Number of whitespaces removed.
+ */
+size_t psStringStrip(
+    char *string                       ///< input string to be stripped.
+);
+
+
+/// Given a CVS keyword string, strip off the CVS-specific keyword to get the value
+psString psStringStripCVS(const char *string, ///< The string, something like "$CVSKEYWORD: Value$"
+                          const char *tagName ///< The name of the tag to remove, something like "CVSKEYWORD"
+                         );
+
+#define PS_ASSERT_STRING_NON_EMPTY(NAME, RVAL) \
+if (!(NAME) || strlen(NAME) == 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: String %s is NULL or empty.", \
+            #NAME); \
+    return RVAL; \
+}
+
+char *psStrcasestr (const char *haystack, const char *needle);
+
+/// @}
+#endif // #ifndef PS_STRING_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psThread.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psThread.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psThread.c	(revision 22322)
@@ -0,0 +1,387 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "psAssert.h"
+#include "psConstants.h"
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psList.h"
+#include "psArray.h"
+#include "psHash.h"
+#include "psString.h"
+#include "psThread.h"
+
+#define THREAD_WAIT 10000               // Microseconds to wait for threads
+#define TASK_BUCKETS 8                  // Number of hash buckets for task list
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Mutex for locking threads
+static psList *pending = NULL;          // queue of pending jobs
+static psList *done = NULL;             // queue of done jobs
+static psArray *pool = NULL;            // array of defined threads
+static psHash *tasks = NULL;            // List of defined tasks
+static psArray *tsd = NULL;             // Thread-specific data
+
+
+/***** basic thread functions *****/
+
+void psThreadLock(void)
+{
+    pthread_mutex_lock(&mutex);
+    return;
+}
+
+void psThreadUnlock(void)
+{
+    pthread_mutex_unlock(&mutex);
+    return;
+}
+
+static void threadFree(psThread *thread)
+{
+    // Nothing to free; this function is merely provided for identification purposes
+    return;
+}
+
+// allocate a psThread
+psThread *psThreadAlloc(void)
+{
+    psThread *thread = (psThread*)psAlloc(sizeof(psThread));
+    psMemSetDeallocator(thread, (psFreeFunc)threadFree);
+
+    thread->busy  = false;
+    thread->fault = false;
+    return thread;
+}
+
+/***** thread job functions *****/
+
+static void threadJobFree(psThreadJob *job)
+{
+    psFree(job->type);
+    psFree(job->args);
+    psFree(job->results);
+    return;
+}
+
+// allocate a psThreadJob of the given type
+psThreadJob *psThreadJobAlloc(const char *type)
+{
+    psThreadJob *job = (psThreadJob *)psAlloc(sizeof(psThreadJob));
+    psMemSetDeallocator(job, (psFreeFunc)threadJobFree);
+
+    job->type = psStringCopy(type);
+    job->args = psArrayAlloc(0);
+    job->results = NULL;
+    return job;
+}
+
+// add a job to the queue of pending jobs
+bool psThreadJobAddPending(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    // if we failed to call psThreadPoolInit, or we called it with nThreads == 0,
+    // find the matching function and just run it.
+    if (!pool || !pool->n) {
+
+        // in non-threaded operation, the job is placed on the done list and immediately run
+        if (!done) {
+            done = psListAlloc(NULL);
+        }
+        psListAdd(done, PS_LIST_TAIL, job);
+
+        // find the corresponding task and run it
+        psThreadTask *task = psHashLookup(tasks, job->type); // Task to execute job
+        psAssert(task, "Unable to find task %s", job->type);
+        psAssert(job->args->n == task->nArgs, "invalid number of arguments to %s", task->type);
+        return task->function(job);
+    }
+
+    psThreadLock();
+    if (!pending) {
+        pending = psListAlloc(NULL);
+    }
+    psListAdd(pending, PS_LIST_TAIL, job);
+    psThreadUnlock();
+
+    return true;
+}
+
+// this function is not locked -- see thread launder for example
+psThreadJob *psThreadJobGetPending(void)
+{
+    if (!pending) {
+        return NULL;
+    }
+
+    psThreadJob *job = psListGetAndRemove(pending, PS_LIST_HEAD);
+    return job;
+}
+
+// this function is not locked -- see thread launcher for example
+psThreadJob *psThreadJobGetDone(void)
+{
+    if (!done) {
+        return NULL;
+    }
+
+    psThreadJob *job = psListGetAndRemove(done, PS_LIST_HEAD);
+    return job;
+}
+
+/***** thread task functions *****/
+
+static void threadTaskFree(psThreadTask *task)
+{
+    psFree(task->type);
+    return;
+}
+
+// allocate a psThreadTask with nArgs arguments
+psThreadTask *psThreadTaskAlloc(const char *type, int nArgs)
+{
+    psThreadTask *task = (psThreadTask *)psAlloc(sizeof(psThreadTask));
+    psMemSetDeallocator(task, (psFreeFunc)threadTaskFree);
+
+    task->type = psStringCopy(type);
+    task->nArgs = nArgs;
+    task->function = NULL;
+    return task;
+}
+
+// add a task to the collection of tasks
+bool psThreadTaskAdd(psThreadTask *task)
+{
+    PS_ASSERT_THREAD_TASK_NON_NULL(task, false);
+
+    if (!tasks) {
+        tasks = psHashAlloc(TASK_BUCKETS);
+    }
+
+    return psHashAdd(tasks, task->type, task);
+}
+
+bool psThreadTaskRemove(const char *type)
+{
+    PS_ASSERT_STRING_NON_EMPTY(type, false);
+
+    return psHashRemove(tasks, type);
+}
+
+// each thread runs this function to choose the task functions
+void *psThreadLauncher(void *thread)
+{
+    psThread *self = thread;            // Thread that's running
+
+    while (1) {
+        // if we get an error, just wait until we are cleared or killed
+        while (self->fault) {
+            usleep(THREAD_WAIT);
+        }
+
+        // if no tasks are assigned, just wait until they are
+        while (!tasks) {
+            usleep(THREAD_WAIT);
+        }
+
+        // request a new job, if there are none available, sleep a bit
+        // we have to lock here so the job queue cannot be empty yet no threads busy
+        psThreadLock();
+        psThreadJob *job = NULL;        // Job to process
+        while ((job = psThreadJobGetPending()) == NULL) {
+            psThreadUnlock();
+            usleep(THREAD_WAIT);
+            psThreadLock(); // XXX ???
+        }
+        self->busy = true;
+
+        psThreadTask *task = psHashLookup(tasks, job->type); // Task to execute job
+        psThreadUnlock();
+
+        psAssert(task, "Couldn't find thread task %s", job->type);
+        psAssert(job->args->n == task->nArgs, "invalid number of arguments to %s", task->type);
+        bool status = task->function(job); // Status of executing task
+
+        // Put the completed job on the 'done' queue
+        psThreadLock();
+        if (!done) {
+            done = psListAlloc(NULL);
+        }
+        psListAdd(done, PS_LIST_TAIL, job);
+        psFree(job);
+
+        if (!status) {
+            self->fault = true;
+        }
+        self->busy = false;
+        psThreadUnlock();
+    }
+}
+
+/***** thread pool functions *****/
+
+// create a pool of Nthreads, each running the user's job-launcher function
+bool psThreadPoolInit(int nThreads)
+{
+    if (pool) {
+        psAbort("psThreadsInit already called");
+    }
+
+    PS_ASSERT_INT_NONNEGATIVE(nThreads, false);
+    if (nThreads == 0) {
+        // No threading
+        return true;
+    }
+
+    pool = psArrayAlloc(nThreads);
+    for (int i = 0; i < nThreads; i++) {
+        psThread *thread = psThreadAlloc(); // Thread for pool
+        pthread_create(&thread->pt, NULL, psThreadLauncher, thread);
+        pool->data[i] = thread;
+    }
+    return true;
+}
+
+int psThreadPoolSize(void)
+{
+    return pool ? pool->n : 0;
+}
+
+// call this function after you have added jobs to the queue and
+bool psThreadPoolWait(bool harvest)
+{
+    if (!pool || pool->n == 0) {
+        // No threads initialised, so everything's done
+        return true;
+    }
+
+    while (1) {
+        // check for an error
+        for (int i = 0; i < pool->n; i++) {
+            psThread *thread = pool->data[i];
+            if (thread->fault) {
+                return false;
+            }
+        }
+
+        // Harvest jobs, if requested
+        if (harvest) {
+            psThreadLock();
+            psThreadJob *job;           // Job from done queue
+            while ((job = psThreadJobGetDone())) {
+                psFree(job);
+            }
+            psThreadUnlock();
+        }
+
+        // are all threads idle?
+        psThreadLock();
+        for (int i = 0; i < pool->n; i++) {
+            psThread *thread = pool->data[i];
+            if (thread->busy) {
+                // At least one thread is busy: sleep.
+                goto SLEEP;
+            }
+        }
+
+        if (!pending || !pending->head) {
+            // Nothing in the queue
+            psThreadUnlock();
+            return true;
+        }
+
+    SLEEP:
+        psThreadUnlock();
+        usleep(THREAD_WAIT);
+    }
+
+    return false;
+}
+
+bool psThreadPoolFinalize(void)
+{
+    psFree(pending);
+    pending = NULL;
+
+    psFree(done);
+    done = NULL;
+
+    psFree(pool);
+    pool = NULL;
+
+    psFree(tasks);
+    tasks = NULL;
+
+    psFree(tsd);
+    tsd = NULL;
+
+    return true;
+}
+
+
+#if 0
+// This doesn't work like I thought it would: pthread_self can return anything.
+bool psThreadDataAdd(const char *name, psPtr ptr)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+    PS_ASSERT_PTR_NON_NULL(ptr, false);
+
+    pthread_key_t *key = psAlloc(sizeof(pthread_key_t)); // Key for data
+    pthread_key_create(key, psFree);
+
+    if (!tsd) {
+        tsd = psArrayAlloc(numThreads);
+    }
+
+    psHashAdd(
+
+    pthread_t tid = pthread_self();     // Thread identifier
+    int numThreads = psThreadPoolSize();// Number of threads
+    psAssert(tid < numThreads, "Thread identifier (%d) exceeds number of threads (%d)", (int)tid, numThreads);
+
+    if (!tsd) {
+        tsd = psArrayAlloc(numThreads);
+    }
+
+    psHash *hash = tsd->data[tid];      // Thread-specific hash of data
+    return psHashAdd(hash, name, ptr);
+}
+
+void *psThreadDataLookup(const char *name)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    if (!tsd) {
+        return NULL;
+    }
+
+    pthread_t tid = pthread_self();     // Thread identifier
+    int numThreads = psThreadPoolSize();// Number of threads
+    psAssert(tid < numThreads, "Thread identifier (%d) exceeds number of threads (%d)", (int)tid, numThreads);
+
+    psHash *hash = tsd->data[tid];      // Thread-specific hash of data
+    return psHashLookup(hash, name);
+}
+
+bool psThreadDataRemove(const char *name)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    if (!tsd) {
+        return false;
+    }
+
+    pthread_t tid = pthread_self();     // Thread identifier
+    int numThreads = psThreadPoolSize();// Number of threads
+    psAssert(tid < numThreads, "Thread identifier (%d) exceeds number of threads (%d)", (int)tid, numThreads);
+
+    psHash *hash = tsd->data[tid];      // Thread-specific hash of data
+    return psHashRemove(hash, name);
+}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psThread.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psThread.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psThread.h	(revision 22322)
@@ -0,0 +1,140 @@
+/** @file  psThread.h
+ *
+ *  @brief tools to manage a pool of threads
+ *
+ *  @author EAM, IFA
+ *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-30 02:23:09 $
+ *
+ *  Copyright 2004-2005 Insitute for Astronomy, University of Hawaii
+ */
+
+#ifndef PS_THREAD_H
+#define PS_THREAD_H
+
+#include <pthread.h>
+#include "psString.h"
+#include "psArray.h"
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+/// Job to be executed on a thread
+///
+/// This job is passed to the function that executes it
+typedef struct {
+    psString type;                      // Type of thread
+    psArray *args;                      // Arguments to job
+    psArray *results;                   // Results of job
+} psThreadJob;
+
+#define PS_ASSERT_THREAD_JOB_NON_NULL(JOB, RVAL) \
+if (!(JOB) || !(JOB)->type || !(JOB)->args) { \
+    psError(PS_ERR_UNEXPECTED_NULL, true, "Thread job %s or one of its components is NULL.", #JOB); \
+    return RVAL; \
+}
+
+/// A thread, which executes a job
+///
+/// Wraps pthread with a few extra conveniences
+typedef struct {
+    bool busy;                          // Is the thread busy?
+    bool fault;                         // Has the thread faulted?
+    pthread_t pt;                       // The thread itself
+} psThread;
+
+/// Function to execute a thread job
+typedef bool (*psThreadTaskFunction)(psThreadJob *job);
+
+/// Task that is executed on a thread
+typedef struct {
+    psString type;                      // Type of task
+    int nArgs;                          // Number of arguments that function takes
+    psThreadTaskFunction function;      // Function to execute
+} psThreadTask;
+
+#define PS_ASSERT_THREAD_TASK_NON_NULL(TASK, RVAL) \
+if (!(TASK) || !(TASK)->type || (TASK)->nArgs < 0 || !(TASK)->function) { \
+    psError(PS_ERR_UNEXPECTED_NULL, true, "Thread task %s or one of its components is NULL.", #TASK); \
+    return RVAL; \
+}
+
+
+/// Lock the thread mutex
+void psThreadLock(void);
+
+/// Unlock the thread mutex
+void psThreadUnlock(void);
+
+/// Allocate a thread
+psThread *psThreadAlloc(void);
+
+/// Allocate a thread job
+psThreadJob *psThreadJobAlloc(const char *type);
+
+/// Add a pending job to the queue
+bool psThreadJobAddPending(psThreadJob *job);
+
+/// Get a job off the queue of pending jobs
+psThreadJob *psThreadJobGetPending(void);
+
+/// Get a job off the queue of done jobs
+psThreadJob *psThreadJobGetDone(void);
+
+/// Allocate a thread task
+psThreadTask *psThreadTaskAlloc(const char *type, // Type of task
+                                int nArgs // Number of arguments
+    );
+
+/// Add a task to the list
+bool psThreadTaskAdd(psThreadTask *task // Task to add
+    );
+
+/// Remove a task from the list
+bool psThreadTaskRemove(const char *type // Task type to remove
+    );
+
+/// Launch jobs on a thread
+void *psThreadLauncher(void *thread     // Thread (of type psThread)
+    );
+
+/// Initialise a pool of threads
+bool psThreadPoolInit(int nThreads      // Number of threads
+    );
+
+/// Return size of thread pool
+int psThreadPoolSize(void);
+
+/// Wait for the thread pool to finish
+///
+/// This function blocks (waits in usleep) until either an error is detected on one of the threads or until ll
+/// threads are idle and no jobs are left on the queue
+bool psThreadPoolWait(bool harvest      // Harvest the jobs from the queue?
+    );
+
+/// Clean up the thread pool
+bool psThreadPoolFinalize(void);
+
+
+/// Add thread-specific data
+///
+/// The provided pointer is added to a thread-specific hash under the provided name.
+bool psThreadDataAdd(const char *name,  // Name of data
+                     psPtr ptr          // Data to add
+    );
+
+/// Lookup thread-specific data
+///
+/// The thread-specific hash is interrogated using the provided name.
+void *psThreadDataLookup(const char *name // Name of data
+    );
+
+/// Remove thread-specific data
+///
+/// The thread-specific hash has the data associated with the provided name deleted.
+bool psThreadDataRemove(const char *name // Name of data
+    );
+
+
+/// @}
+#endif /* PS_THREAD_H */
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psTrace.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psTrace.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psTrace.c	(revision 22322)
@@ -0,0 +1,810 @@
+/** @file psTrace.c
+ *  \brief basic run-time trace facilities
+ *  \ingroup LogTrace
+ *
+ *  This file will hold the code for procedures to insert
+ *  trace messages into the code.
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.87 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-04-17 23:43:02 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/*****************************************************************************
+    NOTES:
+ In the SRD, higher trace levels correspond to a numerically lower trace
+ value in the code.  This is a bit confusing.  For example, a high-level
+ message might be something like "Begin Processing".  The module programmer
+ might give that a numerically low trace level, such as 1, so then any
+ non-zero trace level in that code component will display thatmessage.
+
+ We build a tree of trace components.  Every node in the tree has a
+ depth, which is it's distance from the root.  However, this is not
+ not the same thing as a node's "level", which corresponds to the
+ trace level of that node.
+
+I think the following is the correct behavior, but not sure:
+    PS_UNKNOWN_TRACE_LEVEL: We never set the level of a component to this
+    value.  This value is only used when psTraceGetLevel is called with
+    a bad component name, or if the component root is undefined, I think.
+
+    PS_DEFAULT_TRACE_LEVEL: This should only be used when adding the
+    intermediate components of a psS64 name.  Ie. the "B" in .A.B.C
+
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+// Even if psLib is being built without tracing (PS_NO_TRACE is set) we still
+// want to build the symbols for programs linked against psLib
+#undef PS_NO_TRACE
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "psAbort.h"
+#include "psAssert.h"
+#include "psTrace.h"
+#include "psString.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psMetadata.h"
+#include "psMemory.h"
+
+#define MAX_HOSTNAME_LENGTH 256
+#define MAX_HEADER_LENGTH 256
+#define MAX_TRACE_LENGTH 1024
+#define MAX_COMPONENT_LENGTH 1024
+
+
+static p_psComponent* cRoot = NULL; // The root of the trace component
+static bool traceTime = false;     // Flag to include time info
+static bool traceHost = false;     // Flag to include host info
+static bool traceLevel = false;    // Flag to include level info
+static bool traceName = false;     // Flag to include name info
+static bool traceMsg = true;      // Flag to include message info
+static int traceFD = STDOUT_FILENO; // default value
+
+static void componentFree(p_psComponent* comp);
+static p_psComponent* componentAlloc(const char *name, int level);
+static int getLevel(const char *facil);
+
+/*****************************************************************************
+componentAlloc(): allocate memory for a new node, and initialize members.
+ *****************************************************************************/
+static p_psComponent* componentAlloc(const char *name, int level)
+{
+    psAssert(name, "impossible");
+
+    p_psComponent* comp = psAlloc(sizeof(p_psComponent));
+
+    psMemSetPersistent(comp,true);
+    psMemSetDeallocator(comp, (psFreeFunc) componentFree);
+    comp->name = psStringCopy(name);
+    psMemSetPersistent((psPtr)comp->name,true);
+    comp->level = level;
+    comp->n = 0;
+    comp->p_psSpecified = false;
+    comp->subcomp = NULL;
+    return comp;
+}
+
+/*****************************************************************************
+componentFree(): free the current node in the root tree, and all children
+nodes as well.
+ *****************************************************************************/
+static void componentFree(p_psComponent* comp)
+{
+    if (comp == NULL) {
+        return;
+    }
+
+    if (comp->subcomp != NULL) {
+        for (psS32 i = 0; i < comp->n; i++) {
+            psMemSetPersistent(comp->subcomp[i],false);
+            psFree(comp->subcomp[i]);
+        }
+        psMemSetPersistent(comp->subcomp,false);
+        psFree(comp->subcomp);
+    }
+
+    psMemSetPersistent((psPtr)comp->name,false);
+    psFree((void *)comp->name);
+}
+
+/*****************************************************************************
+initTrace(): simply initialize the component root tree.
+*****************************************************************************/
+static void initTrace(void)
+{
+    if (cRoot == NULL) {
+        cRoot = componentAlloc(".", PS_DEFAULT_TRACE_LEVEL);
+    }
+}
+
+// Append the function name to the facility
+// NB: declares TARGET!
+#define FACILITY(TARGET, FUNC, FACIL) \
+    size_t _facilLength = strlen(FACIL); /* Length of facility name */ \
+    size_t _funcLength = strlen(FUNC);   /* Length of function name */ \
+    char TARGET[_facilLength + _funcLength + 2]; /* facility + the function name */ \
+    strcpy(&TARGET[0], FACIL); \
+    TARGET[_facilLength] = '.'; \
+    strcpy(&TARGET[_facilLength + 1], FUNC);
+
+
+/*****************************************************************************
+Set all trace levels to zero.
+
+XXX: Currently, no function calls this routine.
+ *****************************************************************************/
+void p_psTraceReset(p_psComponent* currentNode)
+{
+    psAssert(currentNode, "impossible");
+
+    psS32 i = 0;
+
+    if (NULL == currentNode) {
+        return;
+    }
+
+    currentNode->level = 0;
+    for (i = 0; i < currentNode->n; i++) {
+        if (NULL == currentNode->subcomp[i]) {
+            psLogMsg("p_psTraceReset", PS_LOG_WARN,
+                     _("Sub-component %d of node %s in trace tree is NULL."),
+                     i, currentNode->name);
+        } else {
+            p_psTraceReset(currentNode->subcomp[i]);
+        }
+    }
+    return;
+}
+
+/*****************************************************************************
+Set all trace levels to zero.
+ *****************************************************************************/
+void psTraceReset()
+{
+    psFree(cRoot);
+    cRoot = NULL;
+}
+
+/*****************************************************************************
+componentAdd(): Adds the component named "addNodeName" to the root tree.
+ *****************************************************************************/
+static bool componentAdd(const char *addNodeName, psS32 level)
+{
+    psAssert(addNodeName, "impossible");
+
+    psS32 i = 0;                        // Loop index variable.
+    char name[MAX_COMPONENT_LENGTH]; // buffer for writeable copy.
+    char *firstComponent = NULL;        // first component of name
+    p_psComponent* currentNode = cRoot;
+    psS32 nodeExists = 0;
+
+    // XXX: Verify that this is the correct behavior.
+    if (strcmp("", addNodeName) == 0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL,true,
+                _("Failed to add null component to trace tree."));
+        return false;
+    }
+
+    // Is this the root node? If so, simply set level and return.
+    if (strcmp(".", addNodeName) == 0) {
+        cRoot->level = level;
+        return true;
+    }
+
+    if (addNodeName[0] != '.') {
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+                _("Failed to add '%s' to the root component tree; component must start with '.'."),
+                addNodeName);
+        return false;
+    }
+
+    strncpy(name, addNodeName, MAX_COMPONENT_LENGTH);
+    char *pname = name+1;               // Take off the period
+    // Iterate through the components of addNodeName.  Strip off the first
+    // component of the name, find that in the root tree, or add it if it
+    // does not exist, then move to the next component in the name.
+
+    while (pname != NULL) {
+        firstComponent = pname;
+        pname = strchr(firstComponent, '.');
+        if (pname != NULL) {
+            *pname = '\0';
+            pname++;
+        }
+        nodeExists = 0;
+        for (i = 0; i < currentNode->n; i++) {
+            if (strcmp(currentNode->subcomp[i]->name, firstComponent) == 0) {
+                currentNode = currentNode->subcomp[i];
+                nodeExists = 1;
+                if (pname == NULL) {
+                    currentNode->level = level;
+                }
+            }
+        }
+
+        if (nodeExists == 0) {
+            currentNode->subcomp = psRealloc(currentNode->subcomp,
+                                             (currentNode->n + 1) * sizeof(p_psComponent* ));
+            psMemSetPersistent(currentNode->subcomp,true);
+
+            currentNode->n = (currentNode->n) + 1;
+
+            if (pname == NULL) {
+                // This is the final component to add.
+                currentNode->subcomp[(currentNode->n) - 1] =
+                    componentAlloc(firstComponent, level);
+            } else {
+                // We are adding an intermediate component.  The trace level
+                // is not defined.  An undefined trace level inherits the
+                // trace level of it's parent.  However, we do not set that
+                // specifically here since that would inheritance to be a
+                // static, one-time, type of behavior.
+
+                currentNode->subcomp[(currentNode->n) - 1] =
+                    componentAlloc(firstComponent, PS_DEFAULT_TRACE_LEVEL);
+            }
+            currentNode = currentNode->subcomp[(currentNode->n) - 1];
+        }
+    }
+
+    return true;
+}
+
+/*****************************************************************************
+    psSetTraceLevel(): add the component named "comp" to the component tree,
+ if it is not already there, and set it's trace level to "level".
+
+    NOTE: We modified this so that the user may omit the leading "," in a
+    component name.  Since the code was already implemented assuming the "."
+    was required, rather than change all that code, in this function, I
+    simply add a leading "." to the component name if there is none.
+
+    Input:
+ comp
+ level
+    Output:
+ none
+    Returns:
+ zero
+*****************************************************************************/
+int psTraceSetLevel(const char *comp,   // component of interest
+                    int level)  // desired trace level
+{
+    PS_ASSERT_STRING_NON_EMPTY(comp, 0);
+
+    char *compName = NULL;
+    int prevLevel = -1;
+
+    // If the root component tree does not exist, then initialize it.
+    if (cRoot == NULL) {
+        initTrace();
+    }
+
+    // If the component name has no leading dot, then supply it.
+    if (comp[0] != '.') {
+        compName = (char *) psAlloc(10 + strlen(comp));
+        strcpy(compName, ".");
+        compName = strcat(compName, comp);
+    } else {
+        compName = (char *) comp;
+    }
+    prevLevel = getLevel(compName);
+    // Add the new component to the component tree.
+    if ( !componentAdd(compName, level) ) {
+        psError(PS_ERR_UNKNOWN, false,
+                _("Failed to set trace level (%d) to '%s'."),
+                level,
+                compName);
+
+        if (comp[0] != '.') {
+            psFree(compName);
+        }
+        //        return false;
+        return -1;
+    }
+
+    if (comp[0] != '.') {
+        psFree(compName);
+    }
+
+    //    return true;
+    return prevLevel;
+}
+
+/*****************************************************************************
+    doGetTraceLevel()
+ This function recursively searches the root component tree for the
+ component named "name", which is supplied by a parameter.  If it
+ finds that component, it returns the level of that component.
+ Otherwise, it returns ???.
+
+    NOTE: We modified this so that the user may omit the leading "," in a
+    component name.  Since the code was already implemented assuming the "."
+    was required, rather than change all that code, in this function, I
+    simply add a leading "." to the component name if there is none.
+
+    Inputs:
+ name:
+    Outputs:
+ none
+    Returns:
+ The trace level of the "name" component.
+ *****************************************************************************/
+static psS32 doGetTraceLevel(const char *aname)
+{
+    psAssert(aname, "impossible");
+    char name[strlen(aname) + 1];       // need a writeable copy: for strsep()
+    char *pname = name;
+    char *firstComponent = NULL;        // first component of name
+    p_psComponent* currentNode = cRoot;
+    psS32 i = 0;
+    psS32 defaultLevel = 0;
+
+    if (NULL == currentNode) {
+        return (PS_UNKNOWN_TRACE_LEVEL);
+    }
+
+    if (strcmp(".", aname) == 0) {
+        return (cRoot->level);
+    }
+
+    if (aname[0] != '.') {
+        return (PS_UNKNOWN_TRACE_LEVEL);
+    }
+
+    defaultLevel = cRoot->level;
+    strcpy(name, aname);
+    pname = name+1;
+    while (pname != NULL) {
+        firstComponent = pname;
+        pname = strchr(firstComponent, '.');
+        if (pname != NULL) {
+            *pname = '\0';
+            pname++;
+        }
+        for (i = 0; i < currentNode->n; i++) {
+            if (NULL == currentNode->subcomp[i]) {
+                psLogMsg("p_psTraceReset", PS_LOG_WARN,
+                         _("Sub-component %d of node %s in trace tree is NULL."),
+                         i, currentNode->name);
+            }
+
+            if (strcmp(currentNode->subcomp[i]->name, firstComponent) == 0) {
+                currentNode = currentNode->subcomp[i];
+                // For level inheritance purpose, we save the level of this
+                // component if it is not DEFAULT.
+                if (currentNode->level != PS_DEFAULT_TRACE_LEVEL) {
+                    defaultLevel = currentNode->level;
+                }
+                // Determine if this is the last component:
+                if (pname == NULL) {
+                    if (currentNode->level != PS_DEFAULT_TRACE_LEVEL) {
+                        return (currentNode->level);
+                    } else {
+                        return(defaultLevel);
+                    }
+                }
+            }
+        }
+    }
+    return(defaultLevel);
+}
+
+
+/*****************************************************************************
+    getLevel()
+ Return a trace level of "name" in the root component tree.  If the
+ exact string of components in "name" does not exist in the root
+ tree, we return the deepest level of the match.
+ *****************************************************************************/
+static int getLevel(const char *name)
+{
+    if (cRoot == NULL) {
+        return (PS_UNKNOWN_TRACE_LEVEL);
+    }
+
+    psS32 traceLevel;
+
+    // If the component name has no leading dot, then supply it.
+    if (name[0] != '.') {
+        char compName[strlen(name) + 2];
+        compName[0] = '.';
+        strcpy(&compName[1], name);
+
+        traceLevel = doGetTraceLevel(compName);
+    } else {
+        // Search the component root tree, determine the trace level.
+        traceLevel = doGetTraceLevel(name);
+    }
+
+    // XXX: The default trace level is currently set at -1, which is not a
+    // valid trace level.  This is convenient in determining whether or not
+    // a component should inherit the trace level from parent nodes.  However,
+    // it's not clear that -1 should ever be returned by this function.
+    // The SDR is unclear on this point and we should probably request IfA
+    // comment.
+    if (traceLevel == PS_DEFAULT_TRACE_LEVEL) {
+        traceLevel = PS_THE_OTHER_DEFAULT_TRACE_LEVEL;
+    }
+
+    return(traceLevel);
+}
+
+int p_psTraceGetLevel(const char *file,
+                      int lineno,
+                      const char *func,
+                      const char *name)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, 0);
+    PS_ASSERT_STRING_NON_EMPTY(func, 0);
+
+    FACILITY(facility, func, name);
+    return getLevel(facility);
+}
+
+/*****************************************************************************
+    doPrintTraceLevels()
+ This function recursively searches the component tree supplied by the
+ parameter "comp" and prints the name and level of each component.
+    Inputs:
+ comp: a node in the component tree.
+ level: the level of that node
+    Outputs:
+ none
+    Returns:
+ null
+ *****************************************************************************/
+static void doPrintTraceLevels(const p_psComponent* comp,
+                               psS32 depth,
+                               psS32 defLevel)
+{
+    psAssert(comp, "impossible");
+
+    char line[1024];
+    psS32 i = 0;
+
+    // XXX EAM : probably should just return if traceFD < 1
+    if (traceFD < 1)
+        return;
+
+    if (comp->name[0] == '\0') {
+        return;
+    } else {
+        if (comp->level == PS_DEFAULT_TRACE_LEVEL) {
+            sprintf(line,"%*s%-*s %d\n", depth, "", 20 - depth, comp->name, defLevel);
+            write (traceFD, line, strlen(line));
+        } else {
+            sprintf(line, "%*s%-*s %d\n", depth, "", 20 - depth, comp->name, comp->level);
+            write (traceFD, line, strlen(line));
+        }
+    }
+
+    for (i = 0; i < comp->n; i++) {
+        if (comp->level == PS_DEFAULT_TRACE_LEVEL) {
+            doPrintTraceLevels(comp->subcomp[i], depth + 1, defLevel);
+        } else {
+            doPrintTraceLevels(comp->subcomp[i], depth + 1, comp->level);
+        }
+    }
+}
+
+
+/*****************************************************************************
+psPrintTraceLevels(): Simply print all the trace levels in the trace level
+component tree.
+Inputs:
+ none
+Outputs:
+ none
+Returns:
+ null
+*****************************************************************************/
+void psTracePrintLevels(void)
+{
+    if (cRoot == NULL) {
+        return;
+    }
+
+    doPrintTraceLevels(cRoot, 0, PS_THE_OTHER_DEFAULT_TRACE_LEVEL);
+}
+
+void psTraceV(const char *comp,
+              int level,
+              const char *format,
+              va_list ap)
+{
+    PS_ASSERT_STRING_NON_EMPTY(comp, );
+    PS_ASSERT_STRING_NON_EMPTY(format, );
+
+    // XXX EAM : fd < 1 does not print messages
+    if (traceFD < 1) {
+        return;
+    }
+
+    static bool first = true;           // First time we've been called?
+    static char hostname[MAX_HOSTNAME_LENGTH + 1]; // Host name
+    if (first) {
+        first = false;
+        gethostname(hostname, MAX_HOSTNAME_LENGTH);
+    }
+
+    // Only display this message if it's trace level is less than the level
+    // of it's associatedcomponent.
+    if (level <= getLevel(comp)) {
+
+        char clevel = 0;                    // letter-name for level
+        switch (level) {
+        case PS_LOG_ABORT:
+            clevel = 'A';
+            break;
+        case PS_LOG_ERROR:
+            clevel = 'E';
+            break;
+        case PS_LOG_WARN:
+            clevel = 'W';
+            break;
+        case PS_LOG_INFO:
+            clevel = 'I';
+            break;
+        default:
+            if (level >= 4) {
+                clevel = level + '0';
+            } else {
+                psTrace("psLib.sys", 2, "Invalid logMsg level: %d (%s)\n", level, format);
+                level = (level < 0) ? 0 : 9;
+                clevel = level + '0';
+            }
+        }
+
+        int maxLength = MAX_HEADER_LENGTH; // Maximum length of header string
+        char head[maxLength + 2];       // the added two are for the ending | and \0
+        char *head_ptr = head;          // where we've got to in head
+
+        // Create the various log fields...
+        if (traceTime) {
+            time_t clock = time(NULL);  // The current time.
+            struct tm *utc = gmtime(&clock);    // The current gm time.
+            maxLength -= snprintf(head_ptr, maxLength, "%4d:%02d:%02d %02d:%02d:%02dZ",
+                                  utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday,
+                                  utc->tm_hour, utc->tm_min, utc->tm_sec) - 1;
+            head_ptr += strlen(head_ptr);
+        }
+        // Hostname should be 20 characters.
+        if (traceHost) {
+            if (head_ptr > head) {
+                *head_ptr++ = '|';
+            }
+            maxLength -= snprintf(head_ptr, maxLength, "%-20s", hostname);
+            head_ptr += strlen(head_ptr);
+        }
+        if (traceLevel) {
+            if (head_ptr > head) {
+                *head_ptr++ = '|';
+            }
+            maxLength -= snprintf(head_ptr, maxLength, "%c", clevel);
+            head_ptr += strlen(head_ptr);
+        }
+        if (traceName) {
+            if (head_ptr > head) {
+                *head_ptr++ = '|';
+            }
+            maxLength -= snprintf(head_ptr, maxLength, "%s", comp);
+
+            head_ptr += strlen(head_ptr);
+        }
+
+        if (head_ptr > head) {
+            *head_ptr++ = '\n';
+        } else if (!traceMsg) {                  // no output desired
+            return;
+        }
+        *head_ptr = '\0';
+
+        write(traceFD, head, strlen(head));
+
+        if (traceMsg) {
+            // We indent each message one space for each level of the message.
+            for (int i = 0; i < level; i++) {
+                write (traceFD, " ", 1);
+            }
+            psString line = NULL;       // Line to print
+            psStringAppendV(&line, format, ap);
+            write (traceFD, line, strlen(line));
+            if (line[strlen(line) - 1] != '\n') {
+                write(traceFD, "\n", 1);
+            }
+            psFree(line);
+        } else {
+            write(traceFD, "\n", 1);
+        }
+
+        // XXX: what is fd-appropriate equivalent of fflush?
+    }
+}
+
+/*****************************************************************************
+p_psTrace(): we display the trace message to standard output if the trace
+level of that message, supplied by the parameter "level" is higher than the
+trace level that is currently associated with the component named by the
+parameter "comp".
+Input:
+ comp
+ level
+ ...  a printf-style output string.
+Output:
+ none
+Return:
+ null
+ *****************************************************************************/
+void p_psTrace(
+    const char* file,                  ///< file name
+    int lineno,                        ///< line number in file
+    const char* func,                  ///< function name
+    const char *facil,                 ///< facilty of interest
+    psS32 level,                       ///< desired trace level
+    const char *format,                ///< printf-style format command
+    ...                                ///< trace message arguments
+)
+{
+    PS_ASSERT_STRING_NON_EMPTY(file, );
+    PS_ASSERT_STRING_NON_EMPTY(func, );
+    PS_ASSERT_STRING_NON_EMPTY(facil, );
+    PS_ASSERT_STRING_NON_EMPTY(format, );
+
+    FACILITY(name, func, facil);
+
+    va_list ap;
+    va_start(ap, format);
+    psTraceV(name, level, format, ap);
+    va_end(ap);
+}
+
+bool psTraceSetDestination(int fd)
+{
+    if (fd < 0) {
+        return false;
+    }
+
+    if (traceFD > STDERR_FILENO) {
+        close(traceFD);
+    }
+    traceFD = fd;
+    return true;
+}
+
+int psTraceGetDestination()
+{
+    return traceFD;
+}
+
+/*****************************************************************************
+psTraceSetFormat(): Set the format of psTrace output.  More precisely,
+    provide a string consisting of the letters {H (host), L (level), M
+    (message), N (name), T (time)}.  The default is "HLMNT".  This string
+    determines whether or not they associated type of information will be
+    included in message traces.  It does not determine the order in which that
+    information will appear (that order is fixed).
+
+Input:
+    fmt: a string specifying the format.
+Return:
+    NULL.
+ *****************************************************************************/
+bool psTraceSetFormat(const char *format)
+{
+    // assume none.
+    traceHost = false;
+    traceLevel = false;
+    traceMsg = false;
+    traceName = false;
+    traceTime = false;
+
+    // if fmt is NULL, no logging is desired.
+    if (format == NULL) {
+        return false;
+    }
+
+    // XXX: What is the purpose of this conditional.
+    if (strlen(format) == 0) {
+        format = "THLNM";
+    }
+    // Step through each character in the format string.  For each letter
+    // in that string, set the appropriate logging.
+
+    for (const char *ptr = format; *ptr != '\0'; ptr++) {
+        switch (*ptr) {
+        case 'H':
+        case 'h':
+            traceHost = true;
+            break;
+        case 'L':
+        case 'l':
+            traceLevel = true;
+            break;
+        case 'M':
+        case 'm':
+            traceMsg = true;
+            break;
+        case 'N':
+        case 'n':
+            traceName = true;
+            break;
+        case 'T':
+        case 't':
+            traceTime = true;
+            break;
+        default:
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Unknown logging keyword %c."), *ptr);
+            return false;
+        }
+    }
+
+    // XXX: If one must at least log error messages, why don't we set logMsg = true here?
+    if (!traceMsg) {
+        psTrace("psLib.sys", 1,
+                "You must at least trace error messages (You chose \"%s\")", format);
+
+    }
+    return true;
+}
+
+
+static void doGetTraceLevels(psMetadata *out, // Output metadata with the trace levels
+                             const p_psComponent* comp, // Component to add
+                             psString parent, // Name of parent level
+                             int defLevel // Default level
+                            )
+{
+    if (comp->name[0] == '\0') {
+        return;
+    }
+
+    psString name = psStringCopy(parent); // Name of this level
+    if (comp->name[0] == '.') {
+        psStringAppend(&name, "%s", comp->name + 1);
+    } else if (!parent) {
+        psStringAppend(&name, "%s", comp->name);
+    } else {
+        psStringAppend(&name, ".%s", comp->name);
+    }
+
+    int level = (comp->level == PS_DEFAULT_TRACE_LEVEL) ? defLevel : comp->level; // Level for component
+    if (name) {
+        psMetadataAddS32(out, PS_LIST_TAIL, name, 0, NULL, level);
+    }
+    for (int i = 0; i < comp->n; i++) {
+        doGetTraceLevels(out, comp->subcomp[i], name, level);
+    }
+
+    psFree(name);
+
+    return;
+}
+
+
+psMetadata *psTraceLevels(void)
+{
+    if (cRoot == NULL) {
+        return psMetadataAlloc();
+    }
+
+    psMetadata *out = psMetadataAlloc();// Output metadata with the levels
+    doGetTraceLevels(out, cRoot, NULL, PS_THE_OTHER_DEFAULT_TRACE_LEVEL);
+
+    return out;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psTrace.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psTrace.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psTrace.h	(revision 22322)
@@ -0,0 +1,178 @@
+/** @file psTrace.h
+ *
+ *  @brief basic run-time trace facilities
+ *
+ *  This file will hold the prototypes for defining procedures to insert
+ *  trace messages into the code.
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.59 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-08-09 01:40:07 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_TRACE_H
+#define PS_TRACE_H 1
+
+/// @addtogroup SysUtils System Utilities
+/// @{
+
+#include <stdarg.h>
+#include "psMetadata.h"
+
+#define PS_UNKNOWN_TRACE_LEVEL -9999   // we don't know this name's level
+#define PS_DEFAULT_TRACE_LEVEL -1
+#define PS_THE_OTHER_DEFAULT_TRACE_LEVEL 0
+
+enum {
+    PS_TRACE_TO_NONE = 0,             ///< turn off all traces
+    PS_TRACE_TO_STDOUT = 1,           ///< trace to system's stdout
+    PS_TRACE_TO_STDERR = 2,           ///< trace to system's stderr
+};
+
+/** Functions **************************************************************/
+
+//#define PS_NO_TRACE 1   ///< to turn off all tracing
+
+// XXX EAM : the old 'empty' values of (void) 0 are dangerous
+# if defined(PS_NO_TRACE)
+#   define psTraceSetFormat(format)     true    /* success */
+#   define psTrace(facil, level, ...)           /* do nothing */
+#   define psTraceGetLevel(facil)       0       /* trace level is always 0 */
+#   define psTraceV(facil, level, format, __VA_LIST)    /* do nothing */
+#   define psTraceSetLevel(facil,level) 0       /* previous level was 0 */
+#   define psTraceReset()                       /* do nothing */
+#   define psTracePrintLevels()                 /* do nothing */
+#   define psTraceSetDestination(fp)    true    /* success */
+#   define psTraceGetDestination()      2       /* destination is stderr */
+#   define psTraceLevels()              psMetadataAlloc() /* empty metadata */
+#   define PS_TRACE_ON                  0
+
+# else /* PS_NO_TRACE */
+#   define PS_TRACE_ON 1
+
+/** Basic structure for the component tree.  A component is a string of the
+    form aaa.bbb.ccc, and may itself contain further subcomponents.  The
+    Component structure doesn't in fact contain it's full name, but only the
+    last part. */
+typedef struct p_psComponent
+{
+    const char *name;                  ///< last part of name of component
+    psS32 level;                       ///< trace level for this component
+    bool p_psSpecified;                ///< whether the component is specified
+    psS32 n;                           ///< number of subcomponents
+    struct p_psComponent* *subcomp;    ///< next level of subcomponents
+}
+p_psComponent;
+
+
+/** This procedure sets the trace format for future trace messages.  The argument
+ *  must be a character string consistsing of the letters H (host), L
+ *  (level), M (message), N (name), and T (time).  The default is "THLNM".
+ *  Deleting a letter from the string will cause the associated information
+ *  to not be logged.  This procedure does not alter the order in which
+ *  the messages are displayed.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psTraceSetFormat(
+    const char *format                 ///< Specifies the system trace format
+);
+
+/** Sends a trace message. */
+#ifdef DOXYGEN
+void psTrace(
+    const char *facil,                 ///< facilty of interest
+    int level,                         ///< desired trace level
+    const char *format,                ///< printf-style format command
+    ...                                ///< trace message arguments
+);
+#else // ifdef DOXYGEN
+void p_psTrace(
+    const char* file,                  ///< file name
+    int lineno,                        ///< line number in file
+    const char* func,                  ///< function name
+    const char *facil,                 ///< facilty of interest
+    psS32 level,                       ///< desired trace level
+    const char *format,                ///< printf-style format command
+    ...                                ///< trace message arguments
+) PS_ATTR_FORMAT(printf, 6, 7);
+#ifndef SWIG
+#define psTrace(facil, level, ...) \
+      p_psTrace(__FILE__,__LINE__,__func__,facil, level, __VA_ARGS__)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Get the trace level
+ *
+ *  @return int:    Trace Level
+ */
+#ifdef DOXYGEN
+int psTraceGetLevel(
+    const char *facil                  ///< facilty of interest
+);
+#else // ifdef DOXYGEN
+int p_psTraceGetLevel(
+    const char* file,                  ///< file name
+    int lineno,                        ///< line number in file
+    const char *func,                  ///< function name
+    const char *facil                  ///< facilty of interest
+);
+#ifndef SWIG
+#define psTraceGetLevel(facil) \
+      p_psTraceGetLevel(__FILE__,__LINE__,__func__,facil)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+
+/** Sends a trace message. */
+void psTraceV(
+    const char *facil,                 ///< facilty of interest
+    int level,                         ///< desired trace level
+    const char *format,                ///< printf-style format command
+    va_list ap                         ///< varargs argument list
+);
+
+
+/** Set trace level
+ *
+ *  @return int:       The previous level.
+ */
+int psTraceSetLevel(
+    const char *facil,                 ///< facilty of interest
+    int level                          ///< desired trace level
+);
+
+/// Set all trace levels to zero (do not free nodes in the facility tree).
+void psTraceReset(void);
+
+
+/// print trace levels
+void psTracePrintLevels(void);
+
+
+/// Set the destination of future trace messages.
+bool psTraceSetDestination(
+    int fd                             ///< File descriptor
+);
+
+
+/** Get the current destination for trace messages.
+ *
+ *  @return FILE*:      File Destination
+ */
+int psTraceGetDestination(void);
+
+
+/// Return a psMetadata summarising the trace levels
+psMetadata *psTraceLevels(void);
+
+
+/// @}
+#endif /* PS_NO_TRACE */
+#endif /* PS_TRACE_H */
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psType.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psType.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psType.c	(revision 22322)
@@ -0,0 +1,208 @@
+/** @file  psType.c
+*
+*  @brief Contains psType checking functions
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Robert Lupton, Princeton University
+*  @author Joshua Hoblitt, University of Hawaii
+*
+*  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-02-03 05:54:08 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdbool.h>
+
+#include "psType.h"
+#include "psBitSet.h"
+#include "psFits.h"
+#include "psPixels.h"
+#include "psSphereOps.h"
+#include "psMinimizeLMM.h"
+#include "psImageConvolve.h"
+#include "psTime.h"
+#include "psLine.h"
+#include "psRegion.h"
+#include "psHistogram.h"
+#include "psMemory.h"
+
+bool psMemCheckType(psDataType type,
+                    psPtr ptr)
+{
+    if (!ptr) {
+        return false;
+    }
+
+    switch(type) {
+    case PS_DATA_ARRAY:
+        if (psMemCheckArray(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_BITSET:
+        if (psMemCheckBitSet(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_CUBE:
+        if (psMemCheckCube(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_FITS:
+        if (psMemCheckFits(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_HASH:
+        if (psMemCheckHash(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_HISTOGRAM:
+        if (psMemCheckHistogram(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_IMAGE:
+        if (psMemCheckImage(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_KERNEL:
+        if (psMemCheckKernel(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_LINE:
+        if (psMemCheckLine(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_LIST:
+        if (psMemCheckList(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_LOOKUPTABLE:
+        if (psMemCheckLookupTable(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_METADATA:
+        if (psMemCheckMetadata(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_METADATAITEM:
+        if (psMemCheckMetadataItem(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_MINIMIZATION:
+        if (psMemCheckMinimization(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_PIXELS:
+        if (psMemCheckPixels(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_PLANE:
+        if (psMemCheckPlane(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_PLANEDISTORT:
+        if (psMemCheckPlaneDistort(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_PLANETRANSFORM:
+        if (psMemCheckPlaneTransform(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_POLYNOMIAL1D:
+        if (psMemCheckPolynomial1D(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_POLYNOMIAL2D:
+        if (psMemCheckPolynomial2D(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_POLYNOMIAL3D:
+        if (psMemCheckPolynomial3D(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_POLYNOMIAL4D:
+        if (psMemCheckPolynomial4D(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_PROJECTION:
+        if (psMemCheckProjection(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_REGION:
+        if (psMemCheckRegion(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_SCALAR:
+        if (psMemCheckScalar(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_SPHERE:
+        if (psMemCheckSphere(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_SPHEREROT:
+        if (psMemCheckSphereRot(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_SPLINE1D:
+        if (psMemCheckSpline1D(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_STATS:
+        if (psMemCheckStats(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_STRING:
+        if (psMemCheckString(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_TIME:
+        if (psMemCheckTime(ptr)) {
+            return true;
+        }
+        break;
+    case PS_DATA_VECTOR:
+        if (psMemCheckVector(ptr)) {
+            return true;
+        }
+        break;
+    default:
+        // error state -- fall through to returning false
+        break;
+    }
+
+    return false;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/psType.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/psType.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/psType.h	(revision 22322)
@@ -0,0 +1,317 @@
+/** @file  psType.h
+*
+*  @brief Contains support for basic types
+*
+*  This file defines common datatypes used throughout psLib.
+*
+*  @ingroup DataContainer
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.62 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-08-09 03:30:47 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_TYPE_H
+#define PS_TYPE_H
+
+/// @addtogroup DataContainer Data Containers
+/// @{
+
+#include <inttypes.h> // According to C99, this includes stdint.h
+#include <float.h>
+#include <stdbool.h>
+
+// Make sure we have INFINITY and NAN
+// Use GSL --- they solved the problem so we don't have to
+#ifndef INFINITY
+#include <gsl/gsl_nan.h>
+#define INFINITY GSL_POSINF
+#endif
+#ifndef NAN
+#include <gsl/gsl_sys.h>
+#include <gsl/gsl_nan.h>
+#define NAN GSL_NAN
+#endif
+
+/******************************************************************************/
+
+/*  TYPE DEFINITIONS                                                          */
+
+/******************************************************************************/
+
+/** Basic data types used by the containers.
+ *
+ * The basic types of the primitives used by psLib are defined within this enum. This enum is in turn used by
+ * the psType struct.
+ *
+ */
+
+typedef uint8_t psU8;                  ///< 8-bit unsigned int
+typedef uint16_t psU16;                ///< 16-bit unsigned int
+typedef uint32_t psU32;                ///< 32-bit unsigned int
+typedef uint64_t psU64;                ///< 64-bit unsigned int
+typedef int8_t psS8;                   ///< 8-bit signed int
+typedef int16_t psS16;                 ///< 16-bit signed int
+typedef int32_t psS32;                 ///< 32-bit signed int
+typedef int64_t psS64;                 ///< 64-bit signed int
+typedef float psF32;                   ///< 32-bit floating point
+typedef double psF64;                  ///< 64-bit floating point
+typedef char* psString;                ///< string value
+typedef void* psPtr;                   ///< void pointer
+
+// XXX psBool can't be removed until all macros that 'generate' this type are
+// fixed -JH
+// #ifdef __GNUC__
+// typedef bool psBool __attribute__ ((deprecated)); ///< boolean value
+//#else // ifdef __GNUC__
+typedef bool psBool;                   ///< boolean value
+// #endif // fdef __GNUC__
+typedef bool psBOOL;                 ///< allow psBOOL to be used instead of psBool (for macros)
+
+/** Enumeration of data types for function elements.
+ *  Contains replacements for native types.
+ */
+typedef enum {
+    PS_TYPE_S8   = 0x0101,             ///< Character.
+    PS_TYPE_S16  = 0x0102,             ///< Short integer.
+    PS_TYPE_S32  = 0x0104,             ///< Integer.
+    PS_TYPE_S64  = 0x0108,             ///< Long integer.
+    PS_TYPE_U8   = 0x0301,             ///< Unsigned character.
+    PS_TYPE_U16  = 0x0302,             ///< Unsigned psS16 integer.
+    PS_TYPE_U32  = 0x0304,             ///< Unsigned integer.
+    PS_TYPE_U64  = 0x0308,             ///< Unsigned psS64 integer.
+    PS_TYPE_F32  = 0x0404,             ///< Single-precision Floating point.
+    PS_TYPE_F64  = 0x0408,             ///< Double-precision floating point.
+    PS_TYPE_BOOL = 0x1301              ///< Boolean.
+} psElemType;
+
+/** Enumeration primarily used with metadata which defines a data structure
+ *  e.g., list, array, FITS file, etc.
+*/
+typedef enum {
+    PS_DATA_S8   = PS_TYPE_S8,         ///< psS8
+    PS_DATA_S16  = PS_TYPE_S16,        ///< psS16
+    PS_DATA_S32  = PS_TYPE_S32,        ///< psS32
+    PS_DATA_S64  = PS_TYPE_S64,        ///< psS64
+    PS_DATA_U8   = PS_TYPE_U8,         ///< psU8
+    PS_DATA_U16  = PS_TYPE_U16,        ///< psU16
+    PS_DATA_U32  = PS_TYPE_U32,        ///< psU32
+    PS_DATA_U64  = PS_TYPE_U64,        ///< psU64
+    PS_DATA_F32  = PS_TYPE_F32,        ///< psF32
+    PS_DATA_F64  = PS_TYPE_F64,        ///< psF64
+    PS_DATA_BOOL = PS_TYPE_BOOL,       ///< psBool
+    PS_DATA_STRING = 0x10000,          ///< psString (char *)
+    PS_DATA_ARRAY,                     ///< psArray
+    PS_DATA_BITSET,                    ///< psBitSet
+    PS_DATA_CUBE,                      ///< psCube
+    PS_DATA_FITS,                      ///< psFits
+    PS_DATA_HASH,                      ///< psHash
+    PS_DATA_HISTOGRAM,                 ///< psHistogram
+    PS_DATA_IMAGE,                     ///< psImage
+    PS_DATA_KERNEL,                    ///< psKernel
+    PS_DATA_LINE,                      ///< psLine
+    PS_DATA_LIST,                      ///< psList
+    PS_DATA_LOOKUPTABLE,               ///< psLookupTable
+    PS_DATA_METADATA,                  ///< psMetadata
+    PS_DATA_METADATAITEM,              ///< psMetadataItem
+    PS_DATA_MINIMIZATION,              ///< psMinimization
+    PS_DATA_PIXELS,                    ///< psPixels
+    PS_DATA_PLANE,                     ///< psPlane
+    PS_DATA_PLANEDISTORT,              ///< psPlaneDistort
+    PS_DATA_PLANETRANSFORM,            ///< psPlaneTransform
+    PS_DATA_POLYNOMIAL1D,              ///< psPolynomial1D
+    PS_DATA_POLYNOMIAL2D,              ///< psPolynomial2D
+    PS_DATA_POLYNOMIAL3D,              ///< psPolynomial3D
+    PS_DATA_POLYNOMIAL4D,              ///< psPolynomial4D
+    PS_DATA_PROJECTION,                ///< psProjection
+    PS_DATA_REGION,                    ///< psRegion
+    PS_DATA_SCALAR,                    ///< psScalar
+    PS_DATA_SPHERE,                    ///< psSphere
+    PS_DATA_SPHEREROT,                 ///< psSphereTransform
+    PS_DATA_SPLINE1D,                  ///< psSpline1D
+    PS_DATA_STATS,                     ///< psStats
+    PS_DATA_TIME,                      ///< psTime
+    PS_DATA_VECTOR,                    ///< psVector
+    PS_DATA_UNKNOWN,                   ///< Other data of an unknown type
+    PS_DATA_METADATA_MULTI             ///< Used internally for metadata; not a 'real' type
+} psDataType;
+
+#define PS_TYPE_MASK PS_TYPE_U8        /**< the psElemType to use for mask image */
+#define PS_TYPE_MASK_DATA U8           /**< the data member to use for mask image */
+#define PS_TYPE_MASK_NAME "psU8"       /**< the data type for mask as a string */
+
+typedef psU8 psMaskType;               ///< the C datatype for a mask image
+
+#define PS_MIN_S8        INT8_MIN      /**< minimum valid psS8 value */
+#define PS_MIN_S16       INT16_MIN     /**< minimum valid psS16 value */
+#define PS_MIN_S32       INT32_MIN     /**< minimum valid psS32 value */
+#define PS_MIN_S64       INT64_MIN     /**< minimum valid psS64 value */
+#define PS_MIN_U8        0             /**< minimum valid psU8 value */
+#define PS_MIN_U16       0             /**< minimum valid psU16 value */
+#define PS_MIN_U32       0             /**< minimum valid psU32 value */
+#define PS_MIN_U64       0             /**< minimum valid psU64 value */
+#define PS_MIN_F32       -FLT_MAX      /**< minimum valid psF32 value */
+#define PS_MIN_F64       -DBL_MAX      /**< minimum valid psF64 value */
+
+#define PS_MAX_S8        INT8_MAX      /**< maximum valid psS8 value */
+#define PS_MAX_S16       INT16_MAX     /**< maximum valid psS16 value */
+#define PS_MAX_S32       INT32_MAX     /**< maximum valid psS32 value */
+#define PS_MAX_S64       INT64_MAX     /**< maximum valid psS64 value */
+#define PS_MAX_U8        UINT8_MAX     /**< maximum valid psU8 value */
+#define PS_MAX_U16       UINT16_MAX    /**< maximum valid psU16 value */
+#define PS_MAX_U32       UINT32_MAX    /**< maximum valid psU32 value */
+#define PS_MAX_U64       UINT64_MAX    /**< maximum valid psU64 value */
+#define PS_MAX_F32       FLT_MAX       /**< maximum valid psF32 value */
+#define PS_MAX_F64       DBL_MAX       /**< maximum valid psF64 value */
+
+#define PS_TYPE_BOOL_NAME "psBool"
+#define PS_TYPE_S8_NAME   "psS8"
+#define PS_TYPE_S16_NAME  "psS16"
+#define PS_TYPE_S32_NAME  "psS32"
+#define PS_TYPE_S64_NAME  "psS64"
+#define PS_TYPE_U8_NAME   "psU8"
+#define PS_TYPE_U16_NAME  "psU16"
+#define PS_TYPE_U32_NAME  "psU32"
+#define PS_TYPE_U64_NAME  "psU64"
+#define PS_TYPE_F32_NAME  "psF32"
+#define PS_TYPE_F64_NAME  "psF64"
+
+#define PS_TYPE_NAME(value,type) \
+switch(type) { \
+case PS_TYPE_BOOL: \
+    value = PS_TYPE_BOOL_NAME; \
+    break; \
+case PS_TYPE_S8: \
+    value = PS_TYPE_S8_NAME; \
+    break; \
+case PS_TYPE_S16: \
+    value = PS_TYPE_S16_NAME; \
+    break; \
+case PS_TYPE_S32: \
+    value = PS_TYPE_S32_NAME; \
+    break; \
+case PS_TYPE_S64: \
+    value = PS_TYPE_S64_NAME; \
+    break; \
+case PS_TYPE_U8: \
+    value = PS_TYPE_U8_NAME; \
+    break; \
+case PS_TYPE_U16: \
+    value = PS_TYPE_U16_NAME; \
+    break; \
+case PS_TYPE_U32: \
+    value = PS_TYPE_U32_NAME; \
+    break; \
+case PS_TYPE_U64: \
+    value = PS_TYPE_U64_NAME; \
+    break; \
+case PS_TYPE_F32: \
+    value = PS_TYPE_F32_NAME; \
+    break; \
+case PS_TYPE_F64: \
+    value = PS_TYPE_F64_NAME; \
+    break; \
+default: \
+    value = "unknown"; \
+};
+
+/// Macro to get the bad pixel reason code (stored as part of mask value)
+#define PS_BADPIXEL_BITMASK 0x0f
+#define PS_GET_BADPIXEL(maskValue) (maskValue & PS_BADPIXEL_BITMASK)
+
+#define PS_IS_BADPIXEL(maskValue) (PS_GET_BADPIXEL(maskValue) != 0)
+
+/// Macro to apply a bad pixel reason code to mask image
+#define PS_SET_BADPIXEL(maskValue, reasonCode) \
+{ \
+    maskValue = (psMaskType)((reasonCode & PS_BADPIXEL_BITMASK) | (maskValue & ~PS_BADPIXEL_BITMASK)); \
+}
+
+/// Macro to determine if the psElemType is an integer.
+#define PS_IS_PSELEMTYPE_INT(x) ((x & 0x100) == 0x100)
+/// Macro to determine if the psElemType is unsigned.
+#define PS_IS_PSELEMTYPE_UNSIGNED(x) ((x & 0x200) == 0x200)
+/// Macro to determine if the psElemType is a real floating-point type.
+#define PS_IS_PSELEMTYPE_REAL(x) ((x & 0x400) == 0x400)
+/// Macro to determine if the psElemType is boolean type.
+#define PS_IS_PSELEMTYPE_BOOL(x) ((x & 0x1000) == 0x1000)
+/// Macro to determine the storage size, in bytes, of the psElemType.
+#define PSELEMTYPE_SIZEOF(x) (x & 0xFF)
+
+#ifdef __GNUC__
+#define PS_ATTR_MALLOC __attribute__((__malloc__))
+#else // __GNUC__
+#define PS_ATTR_MALLOC
+#endif // __GNUC__
+
+#ifdef __GNUC__
+#define PS_ATTR_NORETURN __attribute__((noreturn))
+#else // __GNUC__
+#define PS_ATTR_NORETURN
+#endif // __GNUC__
+
+#ifdef __GNUC__
+#define PS_ATTR_FORMAT(style, start, end) __attribute__((format(style, start, end)))
+#else // __GNUC__
+#define PS_ATTR_FORMAT(style, start, end)
+#endif // __GNUC__
+
+#ifdef __GNUC__
+#define PS_ATTR_PURE __attribute__((pure))
+#else // __GNUC__
+#define PS_ATTR_PURE
+#endif // __GNUC__
+
+/** Dimensions of a data type.
+ *
+ * The dimensions of containers used by psLib are defined within this enum. This enum is used by the psType
+struct. *
+ */
+typedef enum {
+    PS_DIMEN_SCALAR,            ///< Scalar.
+    PS_DIMEN_VECTOR,            ///< Vector.
+    PS_DIMEN_TRANSV,            ///< Transposed vector.
+    PS_DIMEN_IMAGE,             ///< Image.
+    PS_DIMEN_OTHER              ///< Something else that's not supported for arithmetic.
+} psDimen;
+
+/** The type of a data type.
+ *
+ * All psLib complex types consist of primitive components. This struct provides the description of those
+ * primitives.
+ *
+ */
+typedef struct
+{
+    psElemType type;                   ///< The type
+    psDimen dimen;                     ///< The dimensionality.
+}
+psMathType;
+
+/** The type of a basic data type
+ *
+ *  All psLib complex types consist of primitive components.  This structure provides the ability to cast
+ *  an unknown data structure to safely test the underlining data type.
+ *
+ */
+typedef struct
+{
+    psMathType type;              ///< Data type information
+}
+psMath;
+
+/** Checks the deallocator to see if the pointer matches the desired datatype.
+ *
+ *  @return bool:       True if type matches, otherwise false.
+ */
+bool psMemCheckType(
+    psDataType type,                   ///< The desired psDataType to match
+    psPtr ptr                          ///< The desired pointer to match
+);
+
+/// @}
+#endif // #ifndef PS_TYPE_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/strcasestr.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/strcasestr.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/strcasestr.c	(revision 22322)
@@ -0,0 +1,133 @@
+/* Return the offset of one string within another.
+   Copyright (C) 1994, 1996-2000, 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+ 
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+ 
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+ 
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/*
+ * My personal strstr() implementation that beats most other algorithms.
+ * Until someone tells me otherwise, I assume that this is the
+ * fastest implementation of strstr() in C.
+ * I deliberately chose not to comment it.  You should have at least
+ * as much fun trying to understand it, as I had to write it :-).
+ *
+ * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+
+#if defined _LIBC || defined HAVE_STRING_H
+# include <string.h>
+#endif
+
+#ifdef _LIBC
+# include <locale/localeinfo.h>
+# define TOLOWER(c) __tolower_l ((unsigned char) c, loc)
+#else
+# define TOLOWER(c) _tolower (c)
+#endif
+
+typedef unsigned chartype;
+
+#undef strcasestr
+#undef __strcasestr
+
+char *
+psStrcasestr(const char *phaystack,
+	     const char *pneedle)
+{
+    register const unsigned char *haystack, *needle;
+    register chartype b, c;
+    #ifdef _LIBC
+
+    __locale_t loc = _NL_CURRENT_LOCALE;
+    #endif
+
+    haystack = (const unsigned char *) phaystack;
+    needle = (const unsigned char *) pneedle;
+
+    b = TOLOWER (*needle);
+    if (b != '\0') {
+        haystack--;    /* possible ANSI violation */
+        do {
+            c = *++haystack;
+            if (c == '\0')
+                goto ret0;
+        } while (TOLOWER (c) != (int) b);
+
+        c = TOLOWER (*++needle);
+        if (c == '\0')
+            goto foundneedle;
+        ++needle;
+        goto jin;
+
+        for (;;) {
+            register chartype a;
+            register const unsigned char *rhaystack, *rneedle;
+
+            do {
+                a = *++haystack;
+                if (a == '\0')
+                    goto ret0;
+                if (TOLOWER (a) == (int) b)
+                    break;
+                a = *++haystack;
+                if (a == '\0')
+                    goto ret0;
+shloop:
+                ;
+            } while (TOLOWER (a) != (int) b);
+
+jin:
+            a = *++haystack;
+            if (a == '\0')
+                goto ret0;
+
+            if (TOLOWER (a) != (int) c)
+                goto shloop;
+
+            rhaystack = haystack-- + 1;
+            rneedle = needle;
+            a = TOLOWER (*rneedle);
+
+            if (TOLOWER (*rhaystack) == (int) a)
+                do {
+                    if (a == '\0')
+                        goto foundneedle;
+                    ++rhaystack;
+                    a = TOLOWER (*++needle);
+                    if (TOLOWER (*rhaystack) != (int) a)
+                        break;
+                    if (a == '\0')
+                        goto foundneedle;
+                    ++rhaystack;
+                    a = TOLOWER (*++needle);
+                } while (TOLOWER (*rhaystack) == (int) a);
+
+            needle = rneedle;  /* took the register-poor approach */
+
+            if (a == '\0')
+                break;
+        }
+    }
+foundneedle:
+    return (char*) haystack;
+ret0:
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/sys/sys.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/sys/sys.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/sys/sys.i	(revision 22322)
@@ -0,0 +1,11 @@
+/* sys headers */
+
+%include "psAbort.h"
+%include "psConfigure.h"
+%include "psErrorCodes.h"
+%include "psError.h"
+%include "psLogMsg.h"
+%include "psMemory.h"
+%include "psString.h"
+%include "psTrace.h"
+%include "psType.h"
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/Makefile.am	(revision 22322)
@@ -0,0 +1,36 @@
+#Makefile for types functions of psLib
+#
+noinst_LTLIBRARIES = libpslibtypes.la
+
+libpslibtypes_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(CFITSIO_CFLAGS)
+libpslibtypes_la_SOURCES = \
+	psArray.c \
+	psBitSet.c \
+	psHash.c \
+	psList.c \
+	psLookupTable.c \
+	psMetadata.c \
+	psMetadataConfig.c \
+	psMetadataItemParse.c \
+	psMetadataItemCompare.c \
+	psPixels.c \
+	psArguments.c	\
+	psTree.c
+
+EXTRA_DIST = types.i
+
+pkginclude_HEADERS = \
+	psArray.h \
+	psBitSet.h \
+	psHash.h \
+	psList.h \
+	psLookupTable.h \
+	psMetadata.h \
+	psMetadataConfig.h \
+	psMetadataItemParse.h \
+	psMetadataItemCompare.h \
+	psPixels.h \
+	psArguments.h	\
+	psTree.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/parse_gcov-out.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/parse_gcov-out.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/parse_gcov-out.c	(revision 22322)
@@ -0,0 +1,149 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAXSTR 10000
+
+static char outStr[MAXSTR];
+static float average;
+static int sum;
+
+void parseFile(FILE *in, const char *filename);
+
+int main(void)
+{
+
+    FILE *output = NULL;
+    FILE *input = NULL;
+    average = 0.0;
+    sum = 0;
+    int N = 0;
+
+    input = fopen("arg_gcov.out", "r");
+    parseFile(input, "psArguments.c");
+    fclose(input);
+    N++;
+
+    input = fopen("arr_gcov.out", "r");
+    parseFile(input, "psArray.c");
+    fclose(input);
+    N++;
+
+    input = fopen("bit_gcov.out", "r");
+    parseFile(input, "psBitSet.c");
+    fclose(input);
+    N++;
+
+    input = fopen("hash_gcov.out", "r");
+    parseFile(input, "psHash.c");
+    fclose(input);
+    N++;
+
+    input = fopen("list_gcov.out", "r");
+    parseFile(input, "psList.c");
+    fclose(input);
+    N++;
+
+    input = fopen("look_gcov.out", "r");
+    parseFile(input, "psLookupTable.c");
+    fclose(input);
+    N++;
+
+    input = fopen("md_gcov.out", "r");
+    parseFile(input, "psMetadata.c");
+    fclose(input);
+    N++;
+
+    input = fopen("mdic_gcov.out", "r");
+    parseFile(input, "psMetadataItemCompare.c");
+    fclose(input);
+    N++;
+
+    input = fopen("mdip_gcov.out", "r");
+    parseFile(input, "psMetadataItemParse.c");
+    fclose(input);
+    N++;
+
+    input = fopen("mdc_gcov.out", "r");
+    parseFile(input, "psMetadataConfig.c");
+    fclose(input);
+    N++;
+
+    input = fopen("pix_gcov.out", "r");
+    parseFile(input, "psPixels.c");
+    fclose(input);
+    N++;
+
+    average = average / sum;
+
+    output = fopen("Coverage-Report.txt", "w");
+    fprintf(output, "\nTOTAL COVERAGE IN THE ../src/types/ DIRECTORY\n");
+    fprintf(output, "\n%s", outStr);
+    fprintf(output, "  -------------------------------"
+            "-------------------------------------\n");
+    fprintf(output, "  ---> Total                        = Lines executed:");
+    fprintf(output, "%3.2f%%  of %d\n\n", average, sum);
+    fclose(output);
+    return 0;
+}
+
+void parseINT(const char *nums, float percent)
+{
+    sum += atoi(nums);
+    average += (float)((int)(percent * atoi(nums)));
+}
+
+float parseDBL(const char *nums)
+{
+    char temp[7];
+    int i = 0;
+    int j = 9;
+    float out = 0.0;
+    while (nums[j] != '%') {
+        temp[i] = nums[j];
+        i++;
+        j++;
+    }
+    //    average += (float)atof(temp);
+    out = (float)atof(temp);
+    return out;
+}
+
+void parseFile(FILE *in, const char *filename)
+{
+    char currentStr[100];
+    char searchStr[100];
+    char out[250];
+    sprintf(out, "  >><< %-25s    =", filename);
+    sprintf(searchStr, "'%s'", filename);
+    //    printf("\n searchStr = %s\n", searchStr);
+    strcat(outStr, out);
+    int i;
+    float numLines = 0.0;
+    while ( fscanf(in, "%s", currentStr) != EOF) {
+        if ( strncmp(currentStr, searchStr, 99) == 0 ) {
+            for (i = 0; i < 4; i++) {
+                if (i == 1) {
+                    fscanf(in, "%s", out);
+                    numLines = parseDBL(out);
+                    sprintf(currentStr, "%-16s", out);
+                } else if ( i == 3) {
+                    fscanf(in, "%s", out);
+                    parseINT(out, numLines);
+                    sprintf(currentStr, "%s", out);
+                } else {
+                    fscanf(in, "%s", currentStr);
+                }
+                strcat(outStr, " ");
+                strcat(outStr, currentStr);
+            }
+            continue;
+        }
+    }
+    strcat(outStr, "\n\n");
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/profile_tap
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/profile_tap	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/profile_tap	(revision 22322)
@@ -0,0 +1,16 @@
+
+gcov -f --object-file ./.libs/libpslibtypes_la-psArguments.o psArguments.c > arg_gcov.out
+gcov -f --object-file ./.libs/libpslibtypes_la-psArray.o psArray.c > arr_gcov.out
+gcov -f --object-file ./.libs/libpslibtypes_la-psBitSet.o psBitSet.c > bit_gcov.out
+gcov -f --object-file ./.libs/libpslibtypes_la-psHash.o psHash.c > hash_gcov.out
+gcov -f --object-file ./.libs/libpslibtypes_la-psList.o psList.c > list_gcov.out
+gcov -f --object-file ./.libs/libpslibtypes_la-psLookupTable.o psLookupTable.c > look_gcov.out
+gcov -f --object-file ./.libs/libpslibtypes_la-psMetadataConfig.o psMetadataConfig.c > mdc_gcov.out
+gcov -f --object-file ./.libs/libpslibtypes_la-psMetadataItemCompare.o psMetadataItemCompare.c > mdic_gcov.out
+gcov -f --object-file ./.libs/libpslibtypes_la-psMetadataItemParse.o psMetadataItemParse.c > mdip_gcov.out
+gcov -f --object-file ./.libs/libpslibtypes_la-psMetadata.o psMetadata.c > md_gcov.out
+gcov -f --object-file ./.libs/libpslibtypes_la-psPixels.o psPixels.c > pix_gcov.out
+gcc parse_gcov-out.c -o parse_gcov
+./parse_gcov
+rm *.out
+more Coverage-Report.txt
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psArguments.c	(revision 22322)
@@ -0,0 +1,564 @@
+/** @file  psArguments.h
+ *
+ *  @brief Contains operations for parsing command line input arguments.
+ *
+ *  @ingroup Arguments
+ *
+ *  @author David Robbins, MHPCC
+ *
+ *  @version $Revision: 1.35 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-05-05 00:09:04 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "psArguments.h"
+#include "fitsio.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+
+#include "psLogMsg.h"
+#include "psTrace.h"
+#include "psAssert.h"
+
+#define NUM_SPACES 2   // Number of spaces between
+
+// Set verbosity level
+int psArgumentVerbosity(int *argc,
+                        char **argv)
+{
+    int logLevel = psLogGetLevel();     // Current logging level
+    PS_ASSERT_PTR_NON_NULL(argv, 2);
+    PS_ASSERT_PTR_NON_NULL(argc, 2);
+    PS_ASSERT_INT_POSITIVE(*argc, 2);
+    int argnum = 0;   // Argument number
+
+    // set in order, so that -vvv overrides -vv overrides -v
+    if ( (argnum = psArgumentGet(*argc, argv, "-v")) ) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 3;
+        psLogSetLevel(logLevel);
+    }
+    if ( (argnum = psArgumentGet(*argc, argv, "-vv")) ) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 4;
+        psLogSetLevel(logLevel);
+    }
+    if ( (argnum = psArgumentGet(*argc, argv, "-vvv")) ) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 5;
+        psLogSetLevel(logLevel);
+    }
+
+    if ( (argnum = psArgumentGet(*argc, argv, "-logfmt")) ) {
+        if (*argc < argnum + 2) {
+            psError(PS_ERR_IO, true, "-logfmt switch specified without a format.");
+        } else {
+            psArgumentRemove(argnum, argc, argv);
+            psLogSetFormat(argv[argnum]); // XXX EAM : this function should return an error if the log format is invalid
+            psArgumentRemove(argnum, argc, argv);
+        }
+    }
+
+    // Now the trace stuff
+    // argument format is: -trace (facil) (level)
+    while ( (argnum = psArgumentGet(*argc, argv, "-trace")) ) {
+        if ( (*argc < argnum + 3) ) {
+            psError(PS_ERR_IO, true, "-trace switch specified without facility and level.");
+            return logLevel;
+        }
+        psArgumentRemove(argnum, argc, argv);
+        // psTraceSetLevel is cast to void to avoid a warning in the case where
+        // PS_NO_TRACE is set and psTraceSetLevel is a macro returning an
+        // untyped value
+        (void)psTraceSetLevel(argv[argnum], atoi(argv[argnum+1])); // XXX: This function should return an error if the trace level is invalid
+        psArgumentRemove(argnum, argc, argv);
+        psArgumentRemove(argnum, argc, argv);
+    }
+    if ((argnum = psArgumentGet(*argc, argv, "-trace-levels"))) {
+        psTracePrintLevels();
+        return logLevel;
+    }
+
+    return logLevel;
+}
+
+// Find the location of the specified argument
+int psArgumentGet(int argc,
+                  char **argv,
+                  const char *arg)
+{
+    PS_ASSERT_INT_POSITIVE(argc, 0);
+    PS_ASSERT_PTR_NON_NULL(argv, 0);
+    PS_ASSERT_STRING_NON_EMPTY(arg, 0);
+
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], arg))
+            return i;
+    }
+
+    return 0;
+}
+
+// Remove the specified argument (by location)
+bool psArgumentRemove(int argnum,
+                      int *argc,
+                      char **argv)
+{
+    PS_ASSERT_INT_POSITIVE(argnum, false);
+    PS_ASSERT_PTR_NON_NULL(argc, false);
+    PS_ASSERT_INT_LESS_THAN(argnum, *argc, false);
+    PS_ASSERT_PTR_NON_NULL(argv, false);
+
+    (*argc)--;
+    for (int i = argnum; i < *argc; i++) {
+        argv[i] = argv[i+1];
+    }
+    argv[*argc] = NULL;
+
+    return true;
+}
+
+
+#define ARG_READ_CASE_INT(TYPE,FUNC) \
+case PS_TYPE_##TYPE: { \
+    char *end; \
+    item->data.TYPE = FUNC(argv[argnum], &end, 0); \
+    if (end == argv[argnum]) { \
+        psError(PS_ERR_IO, true, "Unable to read argument value for %s", item->name); \
+        return false; \
+    } \
+    return psArgumentRemove(argnum, argc, argv); \
+}
+
+#define ARG_READ_CASE_FLOAT(TYPE,FUNC) \
+case PS_TYPE_##TYPE: { \
+    char *end; \
+    item->data.TYPE = FUNC(argv[argnum], &end); \
+    if (end == argv[argnum]) { \
+        psError(PS_ERR_IO, true, "Unable to read argument value for %s", item->name); \
+        return false; \
+    } \
+    return psArgumentRemove(argnum, argc, argv); \
+}
+
+static bool argumentRead(psMetadataItem *item, // Item to read into
+                         int argnum, // Argument number
+                         int *argc, // Number of arguments in total
+                         char **argv) // The arguments
+
+{
+    switch(item->type)
+    {
+        ARG_READ_CASE_INT(U8,strtol);
+        ARG_READ_CASE_INT(U16,strtol);
+        ARG_READ_CASE_INT(U32,strtol);
+        ARG_READ_CASE_INT(U64,strtoll);
+        ARG_READ_CASE_INT(S8,strtol);
+        ARG_READ_CASE_INT(S16,strtol);
+        ARG_READ_CASE_INT(S32,strtol);
+        ARG_READ_CASE_INT(S64,strtoll);
+        ARG_READ_CASE_FLOAT(F32,strtof);
+        ARG_READ_CASE_FLOAT(F64,strtod);
+    case PS_DATA_BOOL:
+        // Turn option on; no optional argument to remove
+        item->data.B = true;
+        return true;
+    case PS_DATA_STRING:
+        psFree(item->data.V);
+        item->data.V = psStringCopy(argv[argnum]);
+        return psArgumentRemove(argnum, argc, argv);
+    case PS_DATA_TIME:
+    {
+        psTime *time = psTimeFromString(argv[argnum], PS_TIME_UTC);
+        if (!time) {
+            psError(PS_ERR_IO, true, "Unable to read argument value for %s", item->name);
+            return false;
+        }
+        item->data.V = time; 
+        return psArgumentRemove(argnum, argc, argv);
+    }
+    default:
+        psError(PS_ERR_IO, true, "Argument type (%x) is not supported --- argument %s (%s) ignored\n",
+                item->type, item->name, item->comment);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool psArgumentParse(psMetadata *arguments,
+                     int *argc,
+                     char **argv)
+{
+    PS_ASSERT_METADATA_NON_NULL(arguments, false);
+    PS_ASSERT_PTR_NON_NULL(argc, false);
+    PS_ASSERT_INT_POSITIVE(*argc, false);
+    PS_ASSERT_PTR_NON_NULL(argv, false);
+
+    // We need to do a bit of mucking around in order to preserve the arguments metadata until the last
+    // minute --- if there is a bad argument, we need to return the old "arguments", since they contain
+    // the default values, which we probably want to output in a "help" message (we don't want to print
+    // the changed values and have the user think that they are default values).
+    psMetadata *oldArgs = psMetadataCopy(NULL, arguments); // Copy of old arguments, in event of an error
+    psMetadata *multiFlag = psMetadataAlloc(); // Flag for which MULTI values have been read
+
+    for (int i = 1; i < *argc; i++) {
+        psTrace("psLib.types", 7, "Looking at %s\n", argv[i]);
+        psMetadataItem *argItem = psMetadataLookup(arguments, argv[i]);
+        if (argItem) {
+	    psStringAppend (&argItem->comment, " (found)");
+            psArgumentRemove(i, argc, argv); // Remove the switch
+
+            switch (argItem->type) {
+            case PS_DATA_METADATA: {
+                    // -arg 1 2 3
+                    psMetadata *params = argItem->data.V; // The list of parameters
+                    if (*argc < i + params->list->n) {
+                        psError(PS_ERR_IO, true, "Not enough arguments for %s.\n", argItem->name);
+                        // Remove the arguments --- they will be ignored
+                        for (int j = i; j < *argc; j++) {
+                            psArgumentRemove(i, argc, argv);
+                        }
+                        goto failed;
+                    }
+
+                    psMetadataIterator *paramsIter = psMetadataIteratorAlloc(params,
+                                                     PS_LIST_HEAD, NULL);// Iter
+                    psMetadataItem *param = NULL; // Parameter from iteration
+                    while ((param = psMetadataGetAndIncrement(paramsIter))) {
+			if (!argumentRead(param, i, argc, argv)) {
+			    psFree(paramsIter);
+			    psError(PS_ERR_IO, false, "error parsing argument %s\n", argItem->name);
+			    goto failed;
+			}
+                    }
+                    psFree(paramsIter);
+                    break;
+                }
+            case PS_DATA_METADATA_MULTI: {
+                    // -arg 1 -arg 2 -arg 3 ....
+                    psList *multi = argItem->data.V; // The MULTI list
+                    psMetadataItem *template = psListGet(multi, PS_LIST_HEAD)
+                                               ; // The template item
+                    psMetadataItem *newItem = psMetadataItemCopy(template)
+                                              ; // New item to add
+                    if (!argumentRead(newItem, i, argc, argv)) {
+			psFree(newItem);
+			psError(PS_ERR_IO, false, "error parsing argument %s\n", argItem->name);
+			goto failed;
+		    }
+
+                    psMetadataAddItem(arguments, newItem, PS_LIST_TAIL, PS_META_DUPLICATE_OK);
+                    psFree(newItem);      // Drop reference
+
+                    // Remove the template
+                    bool search = false;  // Result of search
+                    if (!psMetadataLookupBool(&search, multiFlag, argItem->name) || !search) {
+                        // Chop the template off
+                        psListRemove(multi, PS_LIST_HEAD); // Remove from MULTI (hash side)
+                        psListRemoveData(arguments->list, template)
+                        ; // Remove from list side
+                        psMetadataAddBool(multiFlag, PS_LIST_HEAD, argItem->name, PS_META_REPLACE,
+                                          NULL, true);
+                    }
+                    break;
+                }
+            default:
+                // -arg 1
+                if (argItem->type != PS_DATA_BOOL && *argc < i + 1) {
+                    psError(PS_ERR_IO, true, "Required argument for %s is missing.\n", argItem->name);
+                    goto failed;
+                }
+                if (!argumentRead(argItem, i, argc, argv)) {
+		    psError(PS_ERR_IO, false, "error parsing argument %s\n", argItem->name);
+		    goto failed;
+		}
+                break;
+            }
+            i--;                        // We removed stuff
+        } else if (strncmp(argv[i], "-", 1) == 0 || strncmp(argv[i], "+", 1) == 0) {
+            // Someone's specified a bad option
+            psError(PS_ERR_IO, true, "Unknown option: %s\n", argv[i]);
+            goto failed;
+        }
+    }
+
+    psFree(multiFlag);
+    psFree(oldArgs);                    // Didn't need these
+    return true;
+
+failed:
+    // We need to copy everything back
+    while (psListLength(arguments->list) > 0) {
+        psMetadataRemoveIndex(arguments, PS_LIST_TAIL);
+    }
+    psMetadataCopy(arguments, oldArgs);
+    psFree(multiFlag);
+    psFree(oldArgs);
+    return false;
+}
+
+
+#define LENGTH_CASE(TYPE) \
+case PS_TYPE_##TYPE: \
+return arg->data.TYPE == 0 ? 1 : \
+       (arg->data.TYPE > 0 ? (int)log10f((float)arg->data.TYPE) + 1 : \
+        (int)log10f(-(float)arg->data.TYPE) + 2);
+
+static int argLength(psMetadataItem *arg)
+{
+    switch (arg->type) {
+        // Only doing a representative set of types
+        LENGTH_CASE(U8);
+        LENGTH_CASE(U16);
+        LENGTH_CASE(U32);
+        LENGTH_CASE(U64);
+        LENGTH_CASE(S8);
+        LENGTH_CASE(S16);
+        LENGTH_CASE(S32);
+        LENGTH_CASE(S64);
+    case PS_DATA_F32:
+        return isnan(arg->data.F32) ? 3 : (arg->data.F32 >= 0 ? 12 : 13); // -d.dddddde?dd
+    case PS_DATA_F64:
+        return isnan(arg->data.F64) ? 3 : (arg->data.F64 >= 0 ? 12 : 13); // -d.dddddde?dd
+    case PS_DATA_BOOL:
+        return arg->data.B ? 4 : 5;
+    case PS_DATA_STRING:
+        return arg->data.V ? strlen(arg->data.V) : 0;
+    case PS_DATA_TIME:
+    {
+        if (arg->data.V) {
+            psString str = psTimeToISO(arg->data.V);
+            int len = strlen(str);
+            psFree(str);
+            return len;
+        }
+        return 0;
+    }
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Argument type (%x) is not supported.\n", arg->type);
+        return 0;
+        //        psAbort("Argument type (%x) is not supported.\n", arg->type);
+    }
+
+    return 0;
+}
+
+
+#define PRINT_CASE(TYPE,FORMAT) \
+case PS_TYPE_##TYPE: \
+printf(FORMAT, item->data.TYPE); \
+break;
+
+// Print the value and comment for an argument item
+static void printValueComment(psMetadataItem *item, // Argument item for which to print
+                              const char *comment, // Comment to print
+                              unsigned int numSpaces) // Number of spaces
+
+{
+    int valueLength = 0;                // Length of the value portion
+    printf("(");
+    if (item) {
+        // Print the value
+        switch (item->type) {
+            PRINT_CASE(U8, "%u");
+            PRINT_CASE(U16, "%u");
+            PRINT_CASE(U32, "%u");
+            PRINT_CASE(U64, "%" PRIu64);
+            PRINT_CASE(S8, "%d");
+            PRINT_CASE(S16, "%d");
+            PRINT_CASE(S32, "%d");
+            PRINT_CASE(S64, "%" PRId64);
+            PRINT_CASE(F32, "%.6e");
+            PRINT_CASE(F64, "%.6e");
+        case PS_DATA_BOOL:
+            if (item->data.B) {
+                printf("TRUE");
+            } else {
+                printf("FALSE");
+            }
+            break;
+        case PS_DATA_STRING:
+            if (item->data.V) {
+                printf("%s", item->data.str);
+            }
+            break;
+        default:
+            //            psAbort("Argument type (%x) for %s is not supported.\n",
+            //                    item->type, item->name);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    "Argument type (%x) for %s is not supported.\n", item->type, item->name);
+        }
+        valueLength = argLength(item);
+    }
+    printf(")");
+
+    // Spaces for formatting
+    for (int i = valueLength; i < numSpaces; i++) {
+        printf(" ");
+    }
+
+    // Print the comment
+    if (comment) {
+        printf("%s", comment);
+    }
+    printf("\n");
+
+    return;
+}
+
+// Return the maximum length of the values
+static void maxLength(int *maxName,     // Maximum length of the name
+                      int *maxValue,    // Maximum length of the value
+                      psMetadata *md)    // Metadata for which to check the length
+
+{
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL;        // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter)))
+    {
+        if (item->type == PS_DATA_METADATA) {
+            maxLength(maxName, maxValue, item->data.V);
+        } else {
+            int nameLength = strlen(item->name); // Length of the name
+            if (nameLength > *maxName) {
+                *maxName = nameLength;
+            }
+            int valueLength = argLength(item); // Length of the value
+            if (valueLength > *maxValue) {
+                *maxValue = valueLength;
+            }
+        }
+    }
+    psFree(iter);
+    return;
+}
+
+
+void psArgumentHelp(psMetadata *arguments)
+{
+    if (arguments == NULL)
+        return;
+    printf("Optional arguments, with default values:\n");
+    int maxName = 4;   // Maximum length of a name
+    int maxValue = 4;   // Maximum length of a value
+
+    // First pass to get the sizes
+    maxLength(&maxName, &maxValue, arguments);
+
+    // Second pass to print
+    psMetadataIterator *argIter = psMetadataIteratorAlloc(arguments, PS_LIST_HEAD, NULL);
+    psMetadataItem *argItem = NULL; // Item from iterator
+    while ((argItem = psMetadataGetAndIncrement(argIter))) {
+        // Initial indent
+        for (int i = 0; i < NUM_SPACES; i++) {
+            printf(" ");
+        }
+
+        // Print the name if required
+        printf("%s", argItem->name);
+        int position = strlen(argItem->name); // Number of spaces in
+        for (int i = position; i < maxName + NUM_SPACES; i++) {
+            printf(" ");
+        }
+
+        // Check to see if it's a MULTI --- there are no default values for a MULTI
+        // -arg 1 -arg 2 -arg 3 ...
+        psMetadataItem *multiCheck = psMetadataLookup(arguments, argItem->name); // Item to check for MULTI
+        if (multiCheck->type == PS_DATA_METADATA_MULTI) {
+            bool first = true;          // Is this the first one?
+            psListIterator* iter = psListIteratorAlloc(multiCheck->data.list,PS_LIST_HEAD,true);
+            psMetadataItem* listItem;
+            int count = 0;
+            //            while ((listItem=(psMetadataItem*)psListGetAndIncrement(iter)) != NULL) {
+            while ((listItem=(psMetadataItem*)psListGetAndIncrement(iter)) != NULL) {
+                //                for (int i = 0; i < NUM_SPACES; i++) {
+                //                    printf(" ");
+                //                }
+                //                printf("%s", listItem->name);
+                //                int position = strlen(listItem->name); // Number of spaces in
+                //                for (int i = position; i < maxName + NUM_SPACES; i++) {
+                //                    printf(" ");
+                //                }
+                if (!first) {
+                    for (int i = 0; i < maxName + 2*NUM_SPACES; i++) {
+                        printf(" ");
+                    }
+                }
+                printValueComment(listItem, listItem->comment, maxValue + NUM_SPACES);
+                first = false;
+                count++;
+                //                continue;
+            }
+            psFree(iter);
+            while (count > 1) {
+                argItem = psMetadataGetAndIncrement(argIter);
+                count--;
+            }
+            continue;
+        }
+
+        // -arg 1 2 3
+        if (argItem->type == PS_DATA_METADATA) {
+            psMetadata *params = argItem->data.V; // The list of parameters
+            psMetadataIterator *paramsIter = psMetadataIteratorAlloc(params, PS_LIST_HEAD, NULL); // Iterator
+            psMetadataItem *paramItem = NULL; // Parameter, from iteration
+            bool first = true;          // Is this the first one?
+            while ((paramItem = psMetadataGetAndIncrement(paramsIter))) {
+                if (!first) {
+                    for (int i = 0; i < maxName + 2*NUM_SPACES; i++) {
+                        printf(" ");
+                    }
+                }
+                printValueComment(paramItem, paramItem->comment, maxValue + NUM_SPACES);
+                first = false;
+            }
+            psFree(paramsIter);
+            continue;
+        }
+
+        // -arg 1
+        printValueComment(argItem, argItem->comment, maxValue + NUM_SPACES);
+    }
+
+    psFree(argIter);
+}
+
+void psArgumentHelpSimple(FILE *stream, psMetadata *arguments)
+{
+    PS_ASSERT_PTR_NON_NULL(arguments, );
+
+    int maxName = 4;   // Maximum length of a name
+    int maxValue = 4;   // Maximum length of a value
+
+    // First pass to get the sizes
+    maxLength(&maxName, &maxValue, arguments);
+
+    // Second pass to print
+    psMetadataIterator *iter = psMetadataIteratorAlloc(arguments, PS_LIST_HEAD, NULL);
+    psMetadataItem *item = NULL; // Item from iterator
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        // Initial indent + name + indent + comment
+        fprintf(stream, "%*s" "%-*s" "%*s" "%s\n",
+                NUM_SPACES, " ",
+                maxName, item->name,
+                NUM_SPACES, " ",
+                item->comment);
+    }
+
+    psFree(iter);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psArguments.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psArguments.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psArguments.h	(revision 22322)
@@ -0,0 +1,81 @@
+/** @file  psArguments.h
+ *
+ *  @brief Contains operations for parsing command line input arguments.
+ *
+ *  @author David Robbins, MHPCC
+ *
+ *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-06-01 03:37:19 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ARGUMENTS_H
+#define PS_ARGUMENTS_H
+
+/// @addtogroup SysUtils
+/// @{
+
+#include "psMetadata.h"
+
+/** Implements the various verbosity controls.
+ *
+ *  Arguments shall be removed from the argument list as they are processed.
+ *
+ *  @return int:        The resultant logging level.
+ */
+int psArgumentVerbosity(
+    int *argc,                         ///< number of arguments
+    char **argv                        ///< the argument list
+);
+
+/** Checks for an argument and returns its index position if found.
+ *
+ *  @return int:        The index of the element in the argument list, otherwise 0.
+ */
+int psArgumentGet(
+    int argc,                          ///< number of arguments
+    char **argv,                       ///< the argument list
+    const char *arg                    ///< the specified argument to match
+);
+
+/** Removes from the argument list the argument whose index is argnum.
+ *
+ *  The number of entries in the argument list shall be decremented.
+ *
+ *  @return bool:       True if the argnum is in the argument list, otherwise false.
+ */
+bool psArgumentRemove(
+    int argnum,                        ///< the argument to remove
+    int *argc,                         ///< number of arguments
+    char **argv                        ///< the argument list
+);
+
+/** Parses the command line arguments into a metadata container of arguments.
+ *
+ *  The input arguments shall contain the list of possible arguments as the keywords providing
+ *  the default values.  As matching arguments are found on the command line, the values shall
+ *  be read into the arguments metadata, with the appropriate type.  The arguments and their
+ *  values shall be removed from the list of command line arguments as they are processed.
+ *
+ *  @return bool:       False if any argument was encountered that is not present in arguments.
+ */
+bool psArgumentParse(
+    psMetadata *arguments,             ///< metadata container for arguments
+    int *argc,                         ///< number of arguments
+    char **argv                        ///< the argument list
+);
+
+/** Prints to stdout a guide to the command-line arguments.      */
+void psArgumentHelp(
+    psMetadata *arguments              ///< metadata container for arguments
+);
+
+/** Prints to stdout a simplifed guide to the command-line arguments.      */
+void psArgumentHelpSimple(
+    FILE *stream,                      ///< FILE* to write too
+    psMetadata *arguments              ///< metadata container for arguments
+);
+
+/// @}
+#endif // #ifndef PS_ARGUMENTS_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psArray.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psArray.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psArray.c	(revision 22322)
@@ -0,0 +1,342 @@
+
+/** @file  psArray.c
+ *
+ *  @brief Contains support for basic vector types
+ *
+ *  This file defines the basic type for a vector struct and functions useful
+ *  in manupulating vectors.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.67 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-29 21:48:53 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/******************************************************************************/
+
+/*  INCLUDE FILES                                                             */
+
+/******************************************************************************/
+#include<stdlib.h>                         // for qsort, etc.
+#include<string.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psArray.h"
+#include "psLogMsg.h"
+#include "psAbort.h"
+#include "psAssert.h"
+#include "psSort.h"
+
+#define DEFAULT_ARRAY_ADD 10            // Default number to add to an array when not specified
+
+/*****************************************************************************
+  FUNCTION IMPLEMENTATION - LOCAL
+ *****************************************************************************/
+static void arrayFree(psArray* psArr);
+
+static void arrayFree(psArray* psArr)
+{
+    psArrayElementsFree(psArr);
+
+    psFree(psArr->data);
+}
+
+bool psMemCheckArray(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)arrayFree );
+}
+
+// Allocate an array, deliberately leave size unset.
+static psArray *arrayAlloc(const char *file,
+                           unsigned int lineno,
+                           const char *func,
+                           long nalloc)
+{
+    if (nalloc < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't allocate a psArray of negative size.");
+        return NULL;
+    }
+
+    // Create vector struct
+    psArray *array = (psArray*)p_psAlloc(file, lineno, func, sizeof(psArray));
+    psMemSetDeallocator(array, (psFreeFunc)arrayFree);
+
+    P_PSARRAY_SET_NALLOC(array, nalloc);
+    // Create vector data array
+    array->data = psAlloc(nalloc * sizeof(psPtr));
+    memset(array->data, 0, sizeof(void*) * nalloc);  //set the initial values of data to NULL
+
+    return array;
+}
+
+/*****************************************************************************
+  FUNCTION IMPLEMENTATION - PUBLIC
+ *****************************************************************************/
+psArray* p_psArrayAlloc(const char *file,
+                        unsigned int lineno,
+                        const char *func,
+                        long nalloc)
+{
+    psArray *array = arrayAlloc(file, lineno, func, nalloc);
+    if (!array) {
+        return NULL;
+    }
+    array->n = nalloc;
+    return array;
+}
+
+psArray* p_psArrayAllocEmpty(const char *file,
+                             unsigned int lineno,
+                             const char *func,
+                             long nalloc)
+{
+    psArray *array = arrayAlloc(file, lineno, func, nalloc);
+    if (!array) {
+        return NULL;
+    }
+    array->n = 0;
+    return array;
+}
+
+psArray* p_psArrayRealloc(const char *file,
+                          unsigned int lineno,
+                          const char *func,
+                          psArray* in,
+                          long nalloc)
+{
+    PS_ASSERT_ARRAY_NON_NULL(in, NULL);
+    if (nalloc < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't reallocate a psArray to negative size.");
+        return in;
+    }
+
+    if (in->nalloc != nalloc) {     // No need to realloc to same size
+        if (nalloc < in->n) {
+            for (long i = nalloc; i < in->n; i++) {      // For reduction in vector size
+                psFree(in->data[i]);
+            }
+            in->n = nalloc;
+        }
+        // Realloc after decrementation to avoid accessing freed array elements
+        long n = in->n;
+        in->data = p_psRealloc(file, lineno, func, in->data, nalloc * sizeof(psPtr));
+        P_PSARRAY_SET_NALLOC(in,nalloc);
+        for (long m = n; m < nalloc; m++) { //if array is grown, set grown data to NULL
+            in->data[m] = NULL;
+        }
+    }
+
+    return in;
+}
+
+psArray* p_psArrayAdd(const char *file, unsigned int lineno, const char *func,
+                      psArray* array, long delta, psPtr data)
+{
+    if (array == NULL) {
+        long d = (delta > 0) ? delta : DEFAULT_ARRAY_ADD;
+        array = p_psArrayAlloc(file, lineno, func, d);
+        array->n = 0;
+    }
+
+    int n = array->n;
+
+    if (n >= array->nalloc) {
+        // array needs to be expanded to make room for more elements
+        long d = (delta > 0) ? delta : DEFAULT_ARRAY_ADD;
+        array = p_psArrayRealloc(file, lineno, func, array, n+d);
+    }
+
+    // add the element to the end of the array.
+    array->data[n] = psMemIncrRefCounter(data);
+    array->n = n+1;
+
+    return array;
+}
+
+// drop an item from the array and free it
+bool psArrayRemoveData(psArray* array,
+                       const psPtr data)
+{
+    PS_ASSERT_ARRAY_NON_NULL(array, false);
+    PS_ASSERT_PTR_NON_NULL(data, false);
+
+    bool success = false;
+    long n = array->n;
+    psPtr *arrayData = array->data;
+    for (long i = n-1; i >= 0; i--) {
+        if (arrayData[i] == data) {
+            memmove(&arrayData[i],&arrayData[i+1],(n-i-1)*sizeof(psPtr));
+            psFree(data); // Free the removed item
+            n--;
+            success = true;
+        }
+    }
+    array->n = n; // reset the array size to indicate the removed item(s)
+
+    return success;
+}
+
+// drop an item from the array and do not free it: this
+// can be useful in the free function of a data type
+// with a reference on another structure
+bool psArrayRemoveDataNoFree(psArray* array,
+                             const psPtr data)
+{
+    PS_ASSERT_ARRAY_NON_NULL(array, false);
+    PS_ASSERT_PTR_NON_NULL(data, false);
+
+    bool success = false;
+    long n = array->n;
+    psPtr *arrayData = array->data;
+    for (long i = n-1; i >= 0; i--) {
+        if (arrayData[i] == data) {
+            memmove(&arrayData[i],&arrayData[i+1],(n-i-1)*sizeof(psPtr));
+            n--;
+            success = true;
+        }
+    }
+    array->n = n; // reset the array size to indicate the removed item(s)
+
+    return success;
+}
+
+bool psArrayRemoveIndex(psArray* array,
+                        long index)
+{
+    PS_ASSERT_ARRAY_NON_NULL(array, false);
+    if (index < 0 || index >= array->n) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified index outside the range of elements in the array."));
+        return false;
+    }
+
+    long i = index;
+    long n = array->n;
+    psFree(array->data[i]);
+    memmove(&array->data[i], &array->data[i + 1], (n - i - 1) * sizeof(psPtr));
+    array->n--;    // reset the array size to indicate the removed item
+
+    return true;
+}
+
+void psArrayElementsFree(psArray* array)
+{
+    if (array == NULL) {
+        return;
+    }
+
+    for (long i = 0; i < array->n; i++) {
+        psFree(array->data[i]);
+        array->data[i] = NULL;
+    }
+
+    array->n = 0;
+}
+
+// Comparison and swap functions for sorting index into array
+#define PSARRAY_SORT_COMPARE_INDEX(A,B) (func(array[index[A]], array[index[B]]) < 0)
+#define PSARRAY_SORT_SWAP_INDEX(TYPE,A,B) { \
+    if (A != B) { \
+        ps##TYPE temp = index[A]; \
+        index[A] = index[B]; \
+        index[B] = temp; \
+    } \
+}
+
+// Heap sort of the index array
+psVector *psArraySortIndex (psVector *out, psArray *in, psCompareFunc func) {
+
+    if (in == NULL) {
+        return NULL;
+    }
+
+    out = psVectorCreate(out, 0, in->n, 1, PS_TYPE_S32);
+    psS32 *index = out->data.S32;       // Dereference index vector
+    psPtr *array = in->data;            // Dereference input array
+    PSSORT(out->n, PSARRAY_SORT_COMPARE_INDEX, PSARRAY_SORT_SWAP_INDEX, S32);
+    return out;
+}
+
+psArray* psArraySort(psArray* array,
+                     psComparePtrFunc func)
+{
+    PS_ASSERT_ARRAY_NON_NULL(array, NULL);
+    qsort(array->data, array->n, sizeof(psPtr), (int (*)(const void* , const void*))func);
+    return array;
+}
+
+// Set an element in the array.
+bool psArraySet(psArray* array,                      ///< input array to set element in
+                long position,                      ///< the element position to set
+                psPtr data)                        ///< the value to set it to
+{
+    PS_ASSERT_ARRAY_NON_NULL(array, false);
+
+    if (position > array->n)
+    {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Specified position, %ld, is greater than n+1 of the array, %ld.",
+                position, array->n);
+        return false;
+    }
+
+    if (position < 0) {
+        position += array->n;
+    }
+    if (position < 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Negative number too large\n");
+        return false;
+    }
+
+    if (position == array->n) {
+        if (position >= array->nalloc) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    _("Specified position, %ld, is greater than the allocated size of the array, %ld."),
+                    position, array->nalloc);
+            return false;
+        }
+        array->n++;
+    }
+    psFree(array->data[position]);
+    array->data[position] = psMemIncrRefCounter(data);
+
+    return true;
+}
+
+// Get an element in the array.
+psPtr psArrayGet(const psArray* array,
+                 long position )
+{
+    PS_ASSERT_ARRAY_NON_NULL(array, NULL);
+
+    if (position >= array->n) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Specified position, %ld, is greater than n+1 of the array, %ld.",
+                position, array->n);
+        return NULL;
+    }
+    if (position < 0)
+        position += array->n;
+    if (position < 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Negative number too large\n");
+        return NULL;
+    }
+    return (array->data[position]);
+}
+
+long psArrayLength(const psArray *array)
+{
+    PS_ASSERT_ARRAY_NON_NULL(array, -1);
+    return (array->n);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psArray.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psArray.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psArray.h	(revision 22322)
@@ -0,0 +1,322 @@
+/** @file  psArray.h
+ *
+ *  @brief Contains basic array definitions and operations
+ *
+ *  This file defines the basic type for a array struct and functions useful
+ *  in manupulating arrays.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *  @author Joshua Hoblitt, University of Hawaii
+ *
+ *  @version $Revision: 1.54 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-14 03:18:41 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ARRAY_H
+#define PS_ARRAY_H
+
+#include "psType.h"
+#include "psCompare.h"
+#include "psVector.h"
+#include "psMutex.h"
+
+/// @addtogroup DataContainer Data Containers
+/// @{
+
+/** An array to support primitive types.
+ *
+ * Struct for maintaining an array of frequently used primitive types.
+ *
+ */
+typedef struct
+{
+    long n;                            ///< Number of elements in use.
+    const long nalloc;                 ///< Total number of elements available.
+    psPtr* data;                       ///< An Array of pointer elements
+    psMutex lock;                       ///< Optional lock for thread safety
+}
+psArray;
+
+#define P_PSARRAY_SET_NALLOC(vec,n) *(long*)&vec->nalloc = n
+
+/*****************************************************************************/
+
+/* FUNCTION PROTOTYPES                                                       */
+
+/*****************************************************************************/
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr
+ *  datatype.
+ *
+ *  @return bool:       True if the pointer matches a psArray structure, false
+ *  otherwise.
+ */
+bool psMemCheckArray(
+    psPtr ptr                          ///< the pointer whose type to check
+)
+;
+
+
+/** Allocate an array, set the length to the number of allocated elements
+ *
+ * Uses psLib memory allocation functions to create an array collection of
+ * data
+ *
+ * @return psArray* : Pointer to psArray.
+ *
+ */
+#ifdef DOXYGEN
+psArray* psArrayAlloc(
+    long nalloc                         ///< Total number of elements to make available.
+);
+#else // ifdef DOXYGEN
+psArray* p_psArrayAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    long nalloc                         ///< Total number of elements to make available.
+) PS_ATTR_MALLOC;
+#define psArrayAlloc(nalloc) \
+      p_psArrayAlloc(__FILE__, __LINE__, __func__, nalloc)
+#endif // ifdef DOXYGEN
+
+
+/** Allocate an array, set the length to zero.
+ *
+ * Uses psLib memory allocation functions to create an array collection of
+ * data
+ *
+ * @return psArray* : Pointer to psArray.
+ *
+ */
+#ifdef DOXYGEN
+psArray* psArrayAllocEmpty(
+    long nalloc                         ///< Total number of elements to make available.
+);
+#else // ifdef DOXYGEN
+psArray* p_psArrayAllocEmpty(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    long nalloc                         ///< Total number of elements to make available.
+);
+#define psArrayAllocEmpty(nalloc) \
+      p_psArrayAllocEmpty(__FILE__, __LINE__, __func__, nalloc)
+#endif // ifdef DOXYGEN
+
+
+/** Reallocate an array.
+ *
+ * Uses psLib memory allocation functions to reallocate an array collection
+ * of data.
+ *
+ * @return psArray* : Pointer to psArray.
+ *
+ */
+#ifdef DOXYGEN
+psArray* psArrayRealloc(
+    psArray* array,                    ///< array to reallocate.
+    long nalloc                        ///< Total number of elements to make available.
+);
+#else // ifdef DOXYGEN
+psArray* p_psArrayRealloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psArray* array,                    ///< array to reallocate.
+    long nalloc                        ///< Total number of elements to make available.
+);
+#define psArrayRealloc(array, nalloc) \
+      p_psArrayRealloc(__FILE__, __LINE__, __func__, array, nalloc)
+#endif // ifdef DOXYGEN
+
+
+/** Add an element to the end the array, expanding the array storage if
+ *  necessary.
+ *
+ *  If delta < 1, then 10 is used.
+ *
+ *  @return psArray*        The array with the element added
+ */
+psArray* psArrayAdd(
+    psArray* array,                    ///< array to operate on
+    long delta,                        ///< the amount to expand array, if necessary.
+    psPtr data                         ///< the data pointer to add to psArray
+);
+#ifdef DOXYGEN
+psArray* psArrayAdd(
+    psArray* array,                    ///< array to operate on
+    long delta,                        ///< the amount to expand array, if necessary.
+    psPtr data                         ///< the data pointer to add to psArray
+);
+#else // ifdef DOXYGEN
+psArray* p_psArrayAdd(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psArray* array,                     ///< array to operate on
+    long delta,                         ///< the amount to expand array, if necessary.
+    psPtr data                          ///< the data pointer to add to psArray
+) PS_ATTR_MALLOC;
+#define psArrayAdd(array, delta, data) \
+      p_psArrayAdd(__FILE__, __LINE__, __func__, array, delta, data)
+#endif // ifdef DOXYGEN
+
+/// Add a scalar value to an array using psScalar
+#define PS_ARRAY_ADD_SCALAR(ARRAY, VALUE, TYPE) { \
+      psScalar *scalar = psScalarAlloc(VALUE, TYPE); \
+      psArrayAdd(ARRAY, 1, scalar); \
+      psFree(scalar); \
+}
+
+
+/** Remove an element from the array by it's pointer
+ *
+ *  Finds and removes the specified data pointer from the list.
+ *
+ * @return bool:  TRUE if the specified data pointer was found and removed,
+ *                otherwise FALSE.
+ *
+ */
+bool psArrayRemoveData(
+    psArray* array,                    ///< array to operate on
+    const psPtr data                   ///< the data pointer to remove from psArray
+);
+
+
+/** Remove an element from the array by it's pointer WITHOUT freeing
+ *
+ *  Finds and removes the specified data pointer from the list, but does not free it
+ *
+ * @return bool:  TRUE if the specified data pointer was found and removed,
+ *                otherwise FALSE.
+ *
+ */
+bool psArrayRemoveDataNoFree(
+    psArray* array,                    ///< array to operate on
+    const psPtr data                   ///< the data pointer to remove from psArray
+);
+
+
+/** Remove an element from the array
+ *
+ *  Finds and removes the elements as the specified position
+ *
+ * @return bool:  TRUE if the specified data pointer was found and removed,
+ *                otherwise FALSE.
+ *
+ */
+bool psArrayRemoveIndex(
+    psArray* array,                    ///< array to operate on
+    long index                      ///< the element to remove
+);
+
+
+/** Deallocate/Dereference elements of an array.
+ *
+ * Uses psLib memory allocation functions to deallocate/dereference elements
+ * of a array of void pointers.  The array psArr is not freed, and its elements
+ * will all be set to NULL.  Additionaly, the array size (n) is set to zero.
+ *
+ */
+void psArrayElementsFree(
+    psArray* array                     ///< Void pointer array to destroy.
+);
+
+
+/** Sort the array according to an external compare function.
+ *
+ *  Sorts an array via the specification of a comparison function
+ *  to specify how the objects on the array should be sorted.
+ *
+ *  The comparison function must return an integer less than, equal to, or
+ *  greater than zero if the first argument is considered to be respectively
+ *  less than, equal to, or greater than the second.
+ *
+ *  If two members compare as equal, their order in the sorted array is
+ *  undefined.
+ *
+ *  @return psArray* The sorted array.
+ */
+psArray* psArraySort(
+    psArray* array,                       ///< input array to sort.
+    psComparePtrFunc func                 ///< the compare function
+);
+
+// return the index which sorts the array
+psVector *psArraySortIndex (psVector *outIndex, psArray *in, psCompareFunc func);
+
+/** Set an element in the array.  If the current element is non-NULL, the old
+ *  element is freed.
+ *
+ *  @return bool  TRUE if the element was set successfully, otherwise FALSE
+ */
+bool psArraySet(
+    psArray* array,                    ///< input array to set element in
+    long position,                     ///< the element position to set
+    psPtr data                         ///< the value to set it to
+);
+
+
+/** Get an element from the array.
+ *
+ *  @return void*   the element at given position.
+ */
+psPtr psArrayGet(
+    const psArray* array,              ///< input array to get element from
+    long position                      ///< the element position to get
+);
+
+
+/** Get the number of elements in use from a specified psArray. (array.n)
+ *
+ *  @return long:       The number of elements in use.
+ */
+long psArrayLength(
+    const psArray *array               ///< input psArray
+);
+
+
+// Some assertions
+
+#define PS_ASSERT_ARRAY_NON_NULL(NAME, RETURNVAL) PS_ASSERT_GENERAL_ARRAY_NON_NULL(NAME, return RETURNVAL)
+#define PS_ASSERT_GENERAL_ARRAY_NON_NULL(NAME, CLEANUP) \
+if ((NAME) == NULL || (NAME)->data == NULL || (NAME)->n < 0) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: psArray %s or its data is NULL.", \
+            #NAME); \
+    CLEANUP; \
+} \
+
+#define PS_ASSERT_ARRAY_NON_EMPTY(NAME, RETURNVAL) PS_ASSERT_GENERAL_ARRAY_NON_EMPTY(NAME, return RETURNVAL)
+#define PS_ASSERT_GENERAL_ARRAY_NON_EMPTY(NAME, CLEANUP) \
+if ((NAME)->n < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psArray %s has no elements.", \
+            #NAME); \
+    CLEANUP; \
+} \
+
+#define PS_ASSERT_ARRAYS_SIZE_EQUAL(ARRAY1, ARRAY2, RVAL) \
+if ((ARRAY1)->n != (ARRAY2)->n) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "psArray %s has size %ld, psArray %s has size %ld.", \
+            #ARRAY1, (ARRAY1)->n, #ARRAY2, (ARRAY2)->n); \
+    return(RVAL); \
+}
+
+#define PS_ASSERT_ARRAY_SIZE(ARRAY, SIZE, RVAL) \
+if ((ARRAY)->n != (SIZE)) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "psArray %s has size %ld instead of expected size %ld.", \
+            #ARRAY, (ARRAY)->n, SIZE); \
+    return RVAL; \
+}
+
+/// @}
+#endif // #ifndef PS_ARRAY_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psBitSet.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psBitSet.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psBitSet.c	(revision 22322)
@@ -0,0 +1,303 @@
+/** @file  psBitSet.c
+ *
+ *  @brief Creates an array of bytes of arbitrary length for storing individual bits.
+ *
+ *  Bit masks are useful tools for toggling various flags and options. This set of functions module provides
+ *  a mechanism to create an array of bits of arbitrary length and manipulate them with basic binary
+ *  operations. A print function is also provided to display the entire set of bits in binary format as a
+ *  string.
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.42 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-08-09 03:30:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "psBitSet.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psString.h"
+#include "psAssert.h"
+
+
+
+enum {
+    UNKNOWN_OP,
+    AND_OP,
+    OR_OP,
+    XOR_OP,
+    NOT_OP
+};
+
+static void bitSetFree(psBitSet* inBitSet);
+
+/** Private function to create a mask.
+ *
+ *  Creates an eight bit mask with the given bit set. All other bits in the byte are zero. The input bit uses
+ *  zero-based indexing, and is the cumulitive index within the array, not the localized byte's bit position.
+ *
+ *  @return  char*: Pointer to byte in which bit is contained.
+ */
+PS_ATTR_PURE static char mask(psS32 bit)
+{
+    char mask = (char)0x01;
+
+    // Ignore splint warning about negative bit shifts
+    /* @i@ */
+    mask = mask << (bit % 8);
+
+    return mask;
+}
+
+static void bitSetFree(psBitSet* inBitSet)
+{
+    psFree(inBitSet->bits);
+}
+
+bool psMemCheckBitSet(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)bitSetFree );
+}
+
+
+psBitSet* p_psBitSetAlloc(const char *file,
+                          unsigned int lineno,
+                          const char *func,
+                          long nalloc)
+{
+    if (nalloc < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("The number of bit in a psBitSet (%ld) must be greater than zero."),
+                nalloc);
+        return NULL;
+    }
+
+    psS32 numBytes = 0;
+    psBitSet* newObj = NULL;
+
+    numBytes = ceil(nalloc / 8.0);
+    newObj = p_psAlloc(file, lineno, func, sizeof(psBitSet));
+    psMemSetDeallocator(newObj, (psFreeFunc) bitSetFree);
+    newObj->n = numBytes;
+
+    // Ignore splint warning about releasing pointer members, since they've not been allocated yet
+    /* @i@ */
+    newObj->bits = psAlloc(sizeof(char) * numBytes);
+
+    memset(newObj->bits, 0, numBytes);
+
+    return newObj;
+}
+
+
+bool psBitSetSet(psBitSet* bitSet,
+                      long bit)
+{
+    unsigned char *byte = NULL;
+
+    if (bitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psBitSet."));
+        return false;
+    } else if ( (bit < 0) ||
+                (bit > bitSet->n * 8 - 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("The specified bit position (%ld) is invalid.  Position must be between 0 and %ld."),
+                bit, bitSet->n * 8 - 1);
+        return false;
+    }
+    // Variable byte is the byte in the array that contains the bit to be set
+    byte = bitSet->bits + bit / 8;
+    *byte |= mask(bit);
+
+    return true;
+}
+
+bool psBitSetClear(psBitSet* bitSet,
+                        long bit)
+{
+    unsigned char *byte = NULL;
+
+    if (bitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psBitSet."));
+        return false;
+    } else if ( (bit < 0) ||
+                (bit > bitSet->n * 8 - 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("The specified bit position (%ld) is invalid.  Position must be between 0 and %ld."),
+                bit, bitSet->n * 8 - 1);
+        return false;
+    }
+    // Variable byte is the byte in the array that contains the bit to be set
+    byte = bitSet->bits + bit / 8;
+    *byte &= ! mask(bit);
+
+    return true;
+}
+
+bool psBitSetTest(const psBitSet* bitSet,
+                  long bit)
+{
+    unsigned char *byte = NULL;
+
+    if (bitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Can not operate on a NULL psBitSet."));
+        return false;
+    } else if ( (bit < 0) ||
+                (bit > bitSet->n * 8 - 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("The specified bit position (%ld) is invalid.  Position must be between 0 and %ld."),
+                bit,bitSet->n * 8 - 1);
+        return false;
+    }
+
+    // Variable byte is the byte in the array that contains the bit to be tested
+    byte = bitSet->bits + bit / 8;
+    return ((*byte & mask(bit)) != 0);
+}
+
+psBitSet* psBitSetOp(psBitSet* outBitSet,
+                     const psBitSet* inBitSet1,
+                     const char *operator,
+                     const psBitSet* inBitSet2)
+{
+    if (inBitSet1 == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("First psBitSet operand can not be NULL."));
+        psFree(outBitSet);
+        return NULL;
+    }
+    if (operator == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified operator is NULL.  Must specify desired operator."));
+        psFree(outBitSet);
+        return NULL;
+    }
+    psS32 i = 0;
+    psS32 n = 0;
+    unsigned char* outBits = NULL;
+    unsigned char* inBits1 = NULL;
+    unsigned char* inBits2 = NULL;
+    psS32 op = UNKNOWN_OP;
+
+    inBits1 = inBitSet1->bits;
+
+
+    // parse the operator
+    if (strcmp(operator,"AND")==0) {
+        op = AND_OP;
+    } else if (strcmp(operator,"OR")==0) {
+        op = OR_OP;
+    } else if (strcmp(operator,"XOR")==0) {
+        op = XOR_OP;
+    } else if (strcmp(operator,"NOT")==0) {
+        op = NOT_OP;
+    } else {
+        psFree(outBitSet);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified operator, %s, is invalid.  Valid operators are AND, OR, and XOR."),
+                operator);
+        return NULL;
+    }
+
+    if (op != NOT_OP) {
+        if (inBitSet2 == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    _("Second psBitSet operand can not be NULL."));
+            psFree(outBitSet);
+            return NULL;
+        }
+
+        if (inBitSet1->n != inBitSet2->n) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    _("The psBitSet operand must be the same size."));
+            psFree(outBitSet);
+            return NULL;
+        }
+        inBits2 = inBitSet2->bits;
+    }
+
+    if (outBitSet == NULL) {
+        outBitSet = psBitSetAlloc(inBitSet1->n*8);
+    } else if (outBitSet->n != inBitSet1->n) {
+        outBitSet->n = inBitSet1->n;
+        outBitSet->bits = psRealloc(outBitSet->bits, inBitSet1->n);
+    }
+
+    n = outBitSet->n;
+    outBits = outBitSet->bits;
+
+    switch (op) {
+    case AND_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = inBits1[i] & inBits2[i];
+        }
+        break;
+    case OR_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = inBits1[i] | inBits2[i];
+        }
+        break;
+    case XOR_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = inBits1[i] ^ inBits2[i];
+        }
+        break;
+    case NOT_OP:
+    default:
+        for (i = 0; i < n; i++) {
+            outBits[i] = ~inBits1[i];
+        }
+        break;
+    }
+
+    return outBitSet;
+}
+
+psBitSet* psBitSetNot(psBitSet* outBitSet,
+                      const psBitSet* inBitSet)
+{
+    if (inBitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Operand can not be NULL."));
+        psFree(outBitSet);
+        return NULL;
+    }
+
+    outBitSet = psBitSetOp(outBitSet,inBitSet,"NOT",NULL);
+
+    return outBitSet;
+}
+
+psString psBitSetToString(const psBitSet* bitSet)
+{
+    PS_ASSERT_PTR_NON_NULL(bitSet, NULL);
+    psS32 i = 0;
+    psS32 numBits = bitSet->n * 8;
+    //    char *outString = psAlloc((size_t) numBits + 1);
+    psString outString = psStringAlloc(numBits + 1);
+
+    for (i = 0; i < numBits; i++) {
+        outString[numBits - i - 1] = psBitSetTest(bitSet, i) ? '1' : '0';
+    }
+
+    outString[numBits] = 0;
+
+    return outString;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psBitSet.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psBitSet.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psBitSet.h	(revision 22322)
@@ -0,0 +1,175 @@
+/** @file  psBitSet.h
+ *
+ *  @brief Creates an array of bytes of arbitrary length for storing individual bits.
+ *
+ *  Bit masks are useful tools for toggling various flags and options. This set of functions module provides
+ *  a mechanism to create an array of bits of arbitrary length and manipulate them with basic binary
+ *  operations. A print function is also provided to display the entire set of bits in binary format as a
+ *  string.
+ *
+ *  @author PAP, EAM, IfA
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.32 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-14 03:18:41 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSBITSET_H
+#define PSBITSET_H
+
+#include "psType.h"
+#include "psMutex.h"
+
+/// @addtogroup DataContainer Data Containers
+/// @{
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+/** Struct containing array of bytes to hold bit data and corresponding array length.
+ *
+ *  The bits in the struct are assembled in as an array of bytes with eight bits per
+ *  byte. The bits are arranged with the LSB in first (right most) position of the
+ *  first array element.
+ */
+typedef struct
+{
+    long n;                            ///< Number of bytes in the array
+    psU8 *bits;                        ///< Aray of bytes holding bits
+    psMutex lock;                       ///< Optional lock for thread safety
+}
+psBitSet;
+
+/*****************************************************************************/
+/* FUNCTION PROTOTYPES                                                       */
+/*****************************************************************************/
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr
+ *  datatype.
+ *
+ *  @return bool:     True if the pointer matches a psBitSet structure, false otherwise.
+ */
+bool psMemCheckBitSet(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Allocate a psBitSet.
+ *
+ *  Create a psBitSet with the number of bits specified by the user. All bits
+ *  are set to zero upon allocation.
+ *
+ *  @return  psBitSet* : Pointer to struct containing array of bits and size of array.
+ */
+#ifdef DOXYGEN
+psBitSet* psBitSetAlloc(
+    long nalloc                        ///< Number of bits in psBitSet array
+);
+#else // ifdef DOXYGEN
+psBitSet* p_psBitSetAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    long nalloc                        ///< Number of bits in psBitSet array
+) PS_ATTR_MALLOC;
+#define psBitSetAlloc(nalloc) \
+      p_psBitSetAlloc(__FILE__, __LINE__, __func__, nalloc)
+#endif // ifdef DOXYGEN
+
+
+
+
+/** Set a bit.
+ *
+ *  Sets a bit at a given bit location. The bit is set based on a zero index
+ *  with the first bit set in the zero bit slot of the zero element of the byte
+ *  array. As an example, setting bit 3 in an array with two elements would
+ *  result in an psBitSet that looks like 00000000 00001000.
+ *
+ *  @return  bool : Successful operation?
+ */
+bool psBitSetSet(
+    psBitSet* bitSet,                  ///< Pointer to psBitSet to be set.
+    long bit                           ///< Bit to be set.
+);
+
+
+/** Clear a bit.
+ *
+ *  Clear a bit at a given bit location. The bit is cleared based on a zero
+ *  index with the first bit set in the zero bit slot of the zero element of
+ *  the byte array.
+ *
+ *  @return  bool : Successful operation?
+ */
+bool psBitSetClear(
+    psBitSet* bitSet,                  ///< Pointer to psBitSet to be cleared.
+    long bit                           ///< Bit to be cleared.
+);
+
+
+/** Test the value of a bit.
+ *
+ *  Prints the value of a bit at a given bit location, either one or zero. The
+ *  resulting bit is based on a zero index format with the first bit set in the
+ *  zero bit slot of the zero element of the byte array.  As an example,
+ *  testing bit 3 in a psBitSet with two bytes that looks like 00000000
+ *  00001000 would return a value of one, since that is the value that was set.
+ *
+ *  @return  bool:      True if successful, otherwise false
+ */
+bool psBitSetTest(
+    const psBitSet* bitSet,            ///< Pointer psBitSet to be tested.
+    long bit                           ///< Bit to be tested.
+);
+
+
+/** Perform a binary operation on two psBitSets
+ *
+ *  Perform an AND, OR, or XOR on two psBitSets. If the BitMasks are not the
+ *  same size, the operation will not be performed and an error message will be
+ *  logged.
+ *
+ *  @return  psBitSet* : Pointer to struct containing result of binary operation.
+ */
+psBitSet* psBitSetOp(
+    psBitSet* outBitSet,               ///< Resulting psBitSet from binary operation
+    const psBitSet* inBitSet1,         ///< First psBitSet on which to operate
+    const char *operator,              ///< Bit operation
+    const psBitSet* inBitSet2          ///< Second psBitSet on which to operate
+);
+
+
+/** Perform a not operation on a psBitSet
+ *
+ *  Toggles bits in a psBitset. All zero bits are set to one and all one bits
+ *  are set to zero.
+ *
+ *  @return  psBitSet* : Pointer to struct containing result of operation.
+ */
+psBitSet* psBitSetNot(
+    psBitSet* outBitSet,               ///< Resulting psBitSet from operation
+    const psBitSet* inBitSet           ///< Input psBitSet
+);
+
+
+/** Convert the psBitSet to a string of ones and zeros.
+ *
+ *  Converts the contents of a psBitSet to a string representation of its
+ *  binary form of ones and zeros. The LSB is the right-most chracter. Each set
+ *  of eight characters represents one byte.
+ *
+ *  @return  psString:      Pointer to character array containing string data.
+ */
+psString psBitSetToString(
+    const psBitSet* bitSet             ///< psBitSet to convert
+);
+
+
+/// @}
+#endif // #ifndef PSBITSET_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psHash.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psHash.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psHash.c	(revision 22322)
@@ -0,0 +1,428 @@
+
+/** @file  psHash.c
+*
+*  @brief Contains support for basic hashing functions.
+*
+*  This file will hold the functions for defining a hash table with arbitrary
+*  data types, allocating/deallocating that hash table, adding and removing
+*  data from that hash table, and listing all keys defined in the hash table.
+*
+*  @author Robert Lupton, Princeton University
+*  @author Robert DeSonia, MHPCC
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.43 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2008-04-17 23:43:03 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "psAbort.h"
+#include "psHash.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psAssert.h"
+
+
+
+static psHashBucket* hashBucketAlloc(const char *key, psPtr data, psHashBucket* next);
+static void hashBucketFree(psHashBucket* bucket);
+static psPtr doHashWork(psHash* table, const char *key, psPtr data, bool remove);
+static void hashFree(psHash* table);
+
+/******************************************************************************
+psHashKeyList(table): this function creates a linked list with an entry in
+that list for every key in the hash table.
+Inputs:
+    table: a hash table
+Return;
+    The linked list
+ *****************************************************************************/
+psList* psHashKeyList(const psHash* hash)
+{
+    PS_ASSERT_HASH_NON_NULL(hash, NULL);
+
+    psList* myLinkList = NULL;  // The output data structure
+    psHashBucket* ptr = NULL;   // Used to step thru linked list.
+
+    // Create the linked list
+    myLinkList = psListAlloc(NULL);
+
+    // Loop through every bucket in the hash table.  If that bucket is not
+    // NULL, then add the bucket's key to the linked list.
+    for (long i = 0; i < hash->n; i++) {
+        if (hash->buckets[i] != NULL) {
+            // Since a bucket contains a linked list of keys/data, we must
+            // step trough each key in that linked list:
+
+            ptr = hash->buckets[i];
+            while (ptr != NULL) {
+                psListAdd(myLinkList, PS_LIST_HEAD, ptr->key);
+                ptr = ptr->next;
+            }
+        }
+    }
+
+    // Return the linked list
+    return (myLinkList);
+}
+
+/******************************************************************************
+hashBucketAlloc(key, data, next): This procedure creates a new hash bucket
+with the specified key, data, and next.
+Inputs:
+    key:  the new bucket's key pointer
+    data: the new bucket's data pointer
+    next: the new bucket's key pointer
+Return:
+    the new hash bucket.
+ *****************************************************************************/
+static psHashBucket* hashBucketAlloc(const char *key,
+                                     psPtr data,
+                                     psHashBucket* next)
+{
+    psAssert(key && strlen(key) > 0, "impossible");
+
+    // Allocate memory for the new hash bucket.
+    psHashBucket* bucket = psAlloc(sizeof(psHashBucket));
+
+    psMemSetDeallocator(bucket, (psFreeFunc) hashBucketFree);
+
+    // Initialize the bucket.
+    bucket->key = psStringCopy(key);
+
+    //XXX:  Since this function is static and only called by doHashWork,
+    //data can never be NULL here.
+    //    if (data == NULL) {
+    // NOTE: Should we flag a warning message?
+    //        bucket->data = NULL;
+    //    } else {
+    bucket->data = psMemIncrRefCounter(data);
+    //    }
+
+    bucket->next = next;
+
+    return bucket;
+}
+
+/******************************************************************************
+hashBucketFree(bucket): This procedure deallocates the specified
+hash bucket.
+Inputs:
+    bucket: the hash bucket to be freed.
+Return:
+    NONE
+ *****************************************************************************/
+static void hashBucketFree(psHashBucket* bucket)
+{
+    psFree(bucket->key);
+    psFree(bucket->data);
+}
+
+/******************************************************************************
+psHashAlloc(n): this procedure creates a new hash table with the
+specified number of buckets.
+Inputs:
+    n: initial number of buckets
+Return:
+    The new hash table.
+ *****************************************************************************/
+psHash* p_psHashAlloc(const char *file,
+                      unsigned int lineno,
+                      const char *func,
+                      long nalloc)        // initial number of buckets
+{
+    if (nalloc < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't allocate a psHash of negative size.");
+        return NULL;
+    }
+
+    // Create the new hash table.
+    psHash* table = p_psAlloc(file, lineno, func, sizeof(psHash));
+
+    psMemSetDeallocator(table, (psFreeFunc) hashFree);
+
+    // Allocate memory for the buckets.
+    table->buckets = psAlloc(nalloc * sizeof(psHashBucket* ));
+    table->n = nalloc;
+
+    psTrace("psLib.types", 1, "Creating %ld-element hash table\n", nalloc);
+
+    // Initialize all buckets to NULL.
+    for (long i = 0; i < nalloc; i++) {
+        table->buckets[i] = NULL;
+    }
+
+    // Return the new hash table.
+    return table;
+}
+
+
+bool psMemCheckHash(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)hashFree );
+}
+
+
+/******************************************************************************
+hashFree(table): This procedure deallocates the specified hash
+table.  It loops through each bucket, and calls hashBucketFree() on that
+bucket.
+
+Inputs:
+    table: a hash table
+Return:
+    NONE
+ *****************************************************************************/
+static void hashFree(psHash* table)
+{
+    // Loop through each bucket in the hash table.  If that bucket is not
+    // NULL, then free the bucket via a function call to hashBucketFree();
+    for (long i = 0; i < table->n; i++) {
+        // A bucket is composed of a linked list of buckets.
+        while (table->buckets[i] != NULL) {
+            psHashBucket* bucket = table->buckets[i];
+            table->buckets[i] = bucket->next;
+            psFree(bucket);
+        }
+    }
+
+    // Free the bucket structure, then the hash table.
+    psFree(table->buckets);
+}
+
+/******************************************************************************
+doHashWork(table, key, data, remove): This is an internal
+procedure which does the bulk of the work in using the hash table.  Depending
+upon the input parameters, it will either insert a new key/data into the hash
+table, retrieve the data for a specified key, or remove a key/data item.  If
+we try to insert a key that already exists in the hash table, then we deallocate
+the existing data/key item.
+Inputs:
+    table: a hash table
+    key: the key to insert, retrieve, or remove.  Must not be NULL.
+    data: the data to insert, if not NULL
+    remove: set to non-zero if the key/data should be removed from the table.
+Return:
+    NONE
+
+NOTE: consider removing this private function and simply putting the code
+into the psHashInsert(), psHashLookup(), and psHashRemove().  Why?  Because
+there is little common code between those functions.
+  *****************************************************************************/
+static psPtr doHashWork(psHash* table,
+                        const char *key,
+                        psPtr data,
+                        bool remove
+                           )
+{
+    psAssert(table, "impossible");
+    psAssert(table->n >= 0, "impossible");
+    psAssert(key && strlen(key) > 0, "impossible");
+
+    if (table->n == 0) {
+        return NULL;
+    }
+
+    // This hash algorithm is from Sedgewick.  NOTE: must reread to ensure that
+    // the size of the hash table is not required to be a prime number.
+    char *tmpchar = (char *)key;        // Used in computing the hash function.
+    long hash;                          // The hash value
+    for (hash = 0; *tmpchar != '\0'; tmpchar++) {
+        hash = (64 * hash + *tmpchar) % (table->n);
+    }
+    psAssert(hash >= 0 && hash < table->n, "impossible");
+
+    // ptr will have the correct hash bucket.
+    psHashBucket *ptr = table->buckets[hash];   // Used to retrieve the hash bucket.
+    psHashBucket* optr = NULL;          // "original pointer": used to step thru the linked list for a bucket.
+
+    // We know the correct hash bucket, now we need to know what to do.
+    // If the data parameter is NULL, then, by definition, this is a retrieve
+    // or a remove operation on the hash table.
+
+    if (!data) {
+        if (remove) {
+            // We search through the linked list for this bucket in
+            // the hash table and look for an entry for this key.
+
+            optr = ptr;
+            while (ptr != NULL) {
+                // Determine if this entry holds the correct key.
+                if (strcmp(key, ptr->key) == 0) {
+                    // The following lines of code are fairly standard ways
+                    // of removing an item from a single-linked list.
+
+                    psPtr data = ptr->data;
+
+                    optr->next = ptr->next;
+                    if (ptr == table->buckets[hash]) {
+                        table->buckets[hash] = ptr->next;
+                    }
+                    psFree(ptr);
+
+                    // By definition, the data associated with that key
+                    // must be returned, not freed.
+                    return data;
+                }
+                optr = ptr;
+                ptr = ptr->next;
+            }
+            return NULL;                   // not in hash
+        }
+        else {
+            // If we get here, then a retrieve operation is requested.  So,
+            // we step trough the linked list at this bucket, and return the
+            // data once we find it, or return NULL if we don't.
+            while (ptr != NULL) {
+                if (strcmp(key, ptr->key) == 0) {
+                    return ptr->data;
+                }
+                ptr = ptr->next;
+            }
+            return NULL;                   // not in hash
+        }
+    } else {
+        // We get here if this procedure was called with non-NULL data.
+        // Therefore, we should insert that data into the hash table.
+        // First, we search through the linked list for this bucket in
+        // the hash table and look for a duplicate entry for this key.
+
+        while (ptr != NULL) {
+            if (strcmp(key, ptr->key) == 0) {
+                // We have found this key in the hash table.
+
+                psTrace("psLib.types", 3, "Replacing data for %s\n", key);
+
+                // NOTE: I have changed this behavior from the originally
+                // supplied code.  Formerly, if itemFree was NULL, then
+                // the new data was not inserted into the hash table.
+
+                psFree(ptr->data);
+
+                ptr->data = psMemIncrRefCounter(data);
+                return data;
+            }
+            ptr = ptr->next;
+        }
+        // We did not found key in the linked list for this bucket of the hash
+        // table.  So, we insert this data at the head of that linked list.
+
+        table->buckets[hash] = hashBucketAlloc(key, data, table->buckets[hash]);
+        return data;
+    }
+}
+
+/******************************************************************************
+psHashAdd(table, key, data): this procedure, which is part of
+the public API, inserts a new key/data pair into the hash table.
+Inputs:
+    table: a hash table
+    key: the key to use
+    data: the data to insert.
+Return:
+    boolean value defining success or failure
+ *****************************************************************************/
+bool psHashAdd(psHash* hash,
+               const char *key,
+               psPtr data)
+{
+    PS_ASSERT_HASH_NON_NULL(hash, false);
+    PS_ASSERT_STRING_NON_EMPTY(key, false);
+    PS_ASSERT_PTR_NON_NULL(data, false);
+
+    return (doHashWork(hash, key, data, false) != NULL);
+}
+
+/******************************************************************************
+psHashLookup(table, key): this procedure, which is part of the public API,
+looks up the specified key in the hash table and returns the data associated
+with that key.
+
+Inputs:
+    table: a hash table
+    key: the key to use
+Return:
+    The data associated with that key.
+ *****************************************************************************/
+psPtr psHashLookup(const psHash* hash,      // hash to lookup key in
+                   const char *key)     // key to lookup
+{
+    PS_ASSERT_HASH_NON_NULL(hash, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(key, NULL);
+
+    return doHashWork((psPtr)hash, key, NULL, false);
+}
+
+/******************************************************************************
+psHashRemove(table, key): this procedure, which is part of the
+public API, removes the specified key from the hash table.
+Inputs:
+    table: a hash table
+    key: the key to remove
+Return:
+    boolean value defining success or failure
+ *****************************************************************************/
+bool psHashRemove(psHash* hash,
+                  const char *key)
+{
+    PS_ASSERT_HASH_NON_NULL(hash, false);
+    PS_ASSERT_STRING_NON_EMPTY(key, false);
+
+    psPtr data = NULL;
+    bool retVal = false;
+
+    data = doHashWork(hash, key, NULL, true);
+    if (data != NULL) {
+        retVal = true;
+    } else {
+        retVal = false;
+    }
+
+    return retVal;
+}
+
+psArray* psHashToArray(const psHash* hash)
+{
+    PS_ASSERT_HASH_NON_NULL(hash, NULL);
+
+    // first, let's just count the number of data elements to know what size psArray we need to allocate.
+    int nElements = 0;
+    int nbucket = hash->n;
+    // XXX:  If we do psArrayAlloc(0) here and use psArrayAdd(result, 1, tmpBucket->data)
+    // we can eliminate the 2nd for loop below.
+    for (int i = 0; i < nbucket; i++) {
+        psHashBucket* tmpBucket = hash->buckets[i];
+        while (tmpBucket != NULL) {
+            nElements++;
+            tmpBucket = tmpBucket->next;
+        }
+    }
+
+    psArray* result = psArrayAlloc(nElements);
+
+    // now fill in the array with the hash hash's data
+    psPtr* data = result->data;
+    for (int i = 0; i < nbucket; i++) {
+        psHashBucket* tmpBucket = hash->buckets[i];
+        while (tmpBucket != NULL) {
+            *(data++) = psMemIncrRefCounter(tmpBucket->data);
+            tmpBucket = tmpBucket->next;
+        }
+    }
+
+    return result;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psHash.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psHash.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psHash.h	(revision 22322)
@@ -0,0 +1,125 @@
+/** @file  psHash.h
+ *
+ *  @brief Contains support for basic hashing functions.
+ *
+ *  This file will hold the prototypes for defining a hash table with arbitrary
+ *  data types, allocating/deallocating that has table, adding and removing
+ *  data from that hash table, and listing all keys defined in the hash table.
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author Robert DeSonia, MHPCC
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.25 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-14 03:18:41 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_HASH_H
+#define PS_HASH_H
+
+/// @addtogroup DataContainer Data Containers
+/// @{
+
+#include "psList.h"
+#include "psMutex.h"
+
+/** A bucket that holds an item of data. */
+typedef struct psHashBucket
+{
+    char *key;                         ///< The key for this item of data.
+    psPtr data;                        ///< The data itself.
+    struct psHashBucket* next;         ///< The list of other possible keys.
+}
+psHashBucket;
+
+
+//typedef struct HashTable psHash; ///< Opaque type for a hash table
+
+/** The hash-table itself. */
+typedef struct
+{
+    long n;                            ///< Number of buckets in hash table.
+    psHashBucket* *buckets;            ///< The bucket data.
+    psMutex lock;                       ///< Optional lock for thread safety.
+}
+psHash;
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr
+ *  datatype.
+ *
+ *  @return bool:       True if the pointer matches a psHash structure, false otherwise.
+ */
+bool psMemCheckHash(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/// Allocate hash buckets in table.
+#ifdef DOXYGEN
+psHash* psHashAlloc(
+    long nalloc                        ///< The number of buckets to allocate.
+);
+#else // ifdef DOXYGEN
+psHash* p_psHashAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    long nalloc                        ///< The number of buckets to allocate.
+) PS_ATTR_MALLOC;
+#define psHashAlloc(nalloc) \
+      p_psHashAlloc(__FILE__, __LINE__, __func__, nalloc)
+#endif // ifdef DOXYGEN
+
+
+
+/// Insert entry into table.
+bool psHashAdd(
+    psHash* hash,                      ///< The table to insert in.
+    const char *key,                   ///< The key to use.
+    psPtr data                         ///< The data to insert.
+);
+
+
+/// Lookup key in table.
+psPtr psHashLookup(
+    const psHash* hash,                ///< The table to lookup key in.
+    const char *key                    ///< The key to lookup.
+);
+
+
+/// Remove key from table.
+bool psHashRemove(
+    psHash* hash,                      ///< The table to lookup key in.
+    const char *key                    ///< The key to lookup.
+);
+
+
+/// List all keys in table.
+psList* psHashKeyList(
+    const psHash* hash                 ///< The table to list keys from..
+);
+
+
+/** Create a psArray from a psHash contents.
+ *
+ *  @return psArray*       A new psArray with duplicate contents of the input psHash
+ */
+psArray* psHashToArray(
+    const psHash* hash                 ///< The table to convert to psArray.
+);
+
+
+#define PS_ASSERT_HASH_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->buckets || (NAME)->n < 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: Hash %s or one of its components is NULL.", \
+            #NAME); \
+    return RVAL; \
+}
+
+/// @} End of DataContainer Functions
+#endif // #ifndef PS_HASH_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psList.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psList.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psList.c	(revision 22322)
@@ -0,0 +1,592 @@
+/** @file psList.c
+ *  @brief Support for doubly linked lists
+ *  @ingroup LinkedList
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author Robert Daniel DeSonia, MHPCC
+ *  @author Joshua Hoblitt, University of Hawaii
+ *
+ *  @version $Revision: 1.71 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-08 18:06:27 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "psError.h"
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psList.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+#include "psAssert.h"
+
+
+#define ITER_INIT_HEAD ((psPtr )1)         // next iteration should return head
+#define ITER_INIT_TAIL ((psPtr )2)         // next iteration should return tail
+
+#define INITIAL_NUM_ITERATORS 16        // Initial number of iterators to create
+
+// private functions.
+static void listFree(psList* list);
+static void listIteratorFree(psListIterator* iter);
+static bool listIteratorRemove(psListIterator* iterator);
+
+static void listFree(psList* list)
+{
+    if (list->iterators != NULL) {
+
+        // remove the associated iterators -- any references are invalid once psList is freed.
+        psArray* iterators = list->iterators;
+        // ONLY orphan the iterators if the list iterators are about to be destroyed
+        // a case where this is not the case is in psListSort.
+        if (psMemGetRefCounter(iterators)  < 2) {
+            for (int i = 0; i < iterators->n; i++) {
+                // orphan iterators first to avoid any callbacks to dying list
+                ((psListIterator*)iterators->data[i])->list = NULL;
+            }
+        }
+        psFree(iterators);
+    }
+
+    for (psListElem* ptr = list->head; ptr != NULL;) {
+        psListElem* next = ptr->next;
+
+        psFree(ptr->data);
+        psFree(ptr);
+
+        ptr = next;
+    }
+}
+
+static void listIteratorFree(psListIterator* iter)
+{
+    // remove this iterator from the parent list
+    if (iter->list != NULL) {
+        psArray* iters = iter->list->iterators;
+        for (int lcv = 0; lcv < iters->n; lcv++) {
+            if (iters->data[lcv] == iter) {
+                // following is done to match SDRS.
+                iters->data[lcv] = iters->data[iters->n-1];
+                iters->n--;
+                break;
+            }
+        }
+    }
+}
+
+static bool listIteratorRemove(psListIterator* iterator)
+{
+    psAssert(iterator, "impossible");
+    if (iterator->cursor == NULL) {
+        return false;
+    }
+
+    psListElem* elem = iterator->cursor;
+    psList* list = iterator->list;
+    int index = iterator->index;
+
+    if (elem == list->head) {        // head of list?
+        list->head = elem->next;
+    } else {
+        elem->prev->next = elem->next;
+    }
+
+    if (elem == list->tail) {        // tail of list?
+        list->tail = elem->prev;
+    } else {
+        elem->next->prev = elem->prev;
+    }
+
+    psArray* iterators = list->iterators;
+    for (int i = 0; i < iterators->n; i++) {
+        psListIterator* iter = (psListIterator*) iterators->data[i];
+        if (iter->cursor == elem) {
+            iter->cursor = NULL;
+        } else if (iter->index > index && iter->index > 0) {
+            iter->index--;
+        }
+    }
+
+    list->n--;
+
+    // OK, delete orphaned list element and its data
+    psFree(elem->data);
+    psFree(elem);
+
+    return true;
+}
+
+psList* p_psListAlloc(const char *file,
+                    unsigned int lineno,
+                    const char *func,
+                    psPtr data)
+{
+    psList* list = p_psAlloc(file, lineno, func, sizeof(psList));
+
+    psMemSetDeallocator(list, (psFreeFunc) listFree);
+
+    list->n = 0;
+    list->head = list->tail = NULL;
+    list->iterators = psArrayAllocEmpty(INITIAL_NUM_ITERATORS);
+
+    // create a default iterator
+    psListIteratorAlloc(list,PS_LIST_HEAD,true);
+
+    if (data != NULL) {
+        psListAdd(list, PS_LIST_TAIL, data);
+    }
+
+    return list;
+}
+
+bool psMemCheckList(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)listFree );
+}
+
+psListIterator* p_psListIteratorAlloc(const char *file,
+                                    unsigned int lineno,
+                                    const char *func,
+                                    psList* list,
+                                    long location,
+                                    bool mutable)
+{
+    PS_ASSERT_LIST_NON_NULL(list, NULL);
+
+    psListIterator* iter = p_psAlloc(file, lineno, func, sizeof(psListIterator));
+    psMemSetDeallocator(iter, (psFreeFunc) listIteratorFree);
+
+    // initialize the attributes
+    iter->list = list;
+    iter->cursor = NULL;
+    iter->index = 0;
+    iter->offEnd = false;
+    iter->mutable = mutable;
+
+    // associate the iterator with the list
+    list->iterators = psArrayAdd(list->iterators,0,iter);
+    // don't want the list's array of iterators to hold a true reference
+    psMemDecrRefCounter(iter);
+
+    if (!psListIteratorSet(iter,location)) {
+        psFree(iter);
+        return NULL;
+    }
+
+    return iter;
+}
+
+bool psListIteratorSet(psListIterator* iterator,
+                       long location)
+{
+    PS_ASSERT_LIST_ITERATOR_NON_NULL(iterator, false);
+
+    psList* list = iterator->list;
+
+    if (location == PS_LIST_TAIL) {
+        iterator->cursor = list->tail;
+        iterator->index = list->n - 1;
+        iterator->offEnd = false;
+        return true;
+    }
+
+    if (location == PS_LIST_HEAD) {
+        iterator->cursor = list->head;
+        iterator->index = 0;
+        iterator->offEnd = false;
+        return true;
+    }
+
+    if (location < 0) {
+        location = list->n + location;
+    }
+
+    if (location < 0 || location >= (int)list->n) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified location, %ld, is invalid."),
+                location);
+        return false;
+    }
+
+    psListElem* cursor = iterator->cursor;
+    int index = iterator->index;
+    if (cursor == NULL) {      // set the cursor to the head if it is NULL
+        //XXX: if location can't be >= n, it def. can't be greater than n/2.
+        /*        if (location > list->n/2) { // closer to tail or head?
+                    cursor = list->tail;
+                    index = list->n - 1;
+                } else {
+        */
+        cursor = list->head;
+        index = 0;
+        //        }
+    }
+
+    if (location < index) {
+        psS32 diff = index - location;
+
+        for (psS32 count = 0; count < diff; count++) {
+            cursor = cursor->prev; // shouldn't need to check for NULL
+        }
+    } else {
+        psS32 diff = location - index;
+
+        for (psS32 count = 0; count < diff; count++) {
+            cursor = cursor->next; // shouldn't need to check for NULL
+        }
+    }
+    iterator->cursor = cursor;
+    iterator->index = location;
+    iterator->offEnd = false;
+
+    return true;
+}
+
+bool psListAdd(psList* list,
+               long location,
+               psPtr data)
+{
+    PS_ASSERT_LIST_NON_NULL(list, false);
+    PS_ASSERT_PTR_NON_NULL(data, false);
+
+    if (location != PS_LIST_HEAD && location >= list->n) {
+        psLogMsg(__func__,PS_LOG_DETAIL,
+                 "Specified location, %ld, is beyond the end of the list.  "
+                 "Adding data item to tail.",
+                 location);
+        location = PS_LIST_TAIL;
+    }
+
+    // move ourselves to the given position
+    if (! psListIteratorSet(list->iterators->data[0],location)) {
+        return false;
+    }
+
+    if (location == PS_LIST_TAIL) {
+        // insert the element at the end of the list
+        return psListAddAfter(list->iterators->data[0],data);
+    } else {
+        return psListAddBefore(list->iterators->data[0],data);
+    }
+}
+
+bool psListAddAfter(psListIterator* iterator,
+                    void* data)
+{
+    PS_ASSERT_LIST_ITERATOR_NON_NULL(iterator, false);
+    PS_ASSERT_PTR_NON_NULL(data, false);
+
+    // Check if the list pointed by the iterator can be changed
+    if (!iterator->mutable) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified iterator indicates list is non-mutable."));
+        return false;
+    }
+
+    psListElem* cursor = iterator->cursor;
+    psList* list = iterator->list;
+
+    if (cursor == NULL && list->head != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified iterator is not valid."));
+        return false;
+    }
+
+    psListElem* elem = psAlloc(sizeof(psListElem));
+
+    // set the new list element's attributes
+    if (cursor == NULL) { // must be an empty list
+        elem->prev = NULL;
+        elem->next = NULL;
+        list->head = elem;
+        list->tail = elem;
+    } else {
+        elem->prev = cursor;
+        elem->next = cursor->next;
+        cursor->next = elem;
+        if (elem->next == NULL) {
+            list->tail = elem;
+        } else {
+            elem->next->prev = elem;
+        }
+    }
+
+    elem->data = psMemIncrRefCounter(data);
+
+    list->n++;
+
+    psArray* iterators = list->iterators;
+    int index = iterator->index;
+    for (int i = 0; i < iterators->n; i++) {
+        psListIterator* iter = (psListIterator*) iterators->data[i];
+        if (iter->index > index) {
+            iter->index++;
+        }
+    }
+
+    return true;
+}
+
+bool psListAddBefore(psListIterator* iterator,
+                     void* data)
+{
+    PS_ASSERT_LIST_ITERATOR_NON_NULL(iterator, false);
+    PS_ASSERT_PTR_NON_NULL(data, false);
+
+    // Check if the list pointed by the iterator can be changed
+    if (!iterator->mutable) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified iterator indicates list is non-mutable."));
+        return false;
+    }
+
+    psListElem* cursor = iterator->cursor;
+    psList* list = iterator->list;
+
+    if (cursor == NULL && list->head != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified iterator is not valid."));
+        return false;
+    }
+
+    psListElem* elem = psAlloc(sizeof(psListElem));
+
+    // set the new list element's attributes
+    if (cursor == NULL) { // empty list.
+        elem->prev = NULL;
+        elem->next = NULL;
+        list->head = elem;
+        list->tail = elem;
+    } else {
+        elem->prev = cursor->prev;
+        elem->next = cursor;
+        cursor->prev = elem;
+        if (elem->prev == NULL) {
+            list->head = elem;
+        } else {
+            elem->prev->next = elem;
+        }
+    }
+
+    elem->data = psMemIncrRefCounter(data);
+
+    list->n++;
+
+    psArray* iterators = list->iterators;
+    int index = iterator->index;
+    for (int i = 0; i < iterators->n; i++) {
+        psListIterator* iter = (psListIterator*) iterators->data[i];
+        if (iter->index >= index) {
+            iter->index++;
+        }
+    }
+
+    return true;
+}
+
+bool psListRemove(psList* list,
+                  long location)
+{
+    PS_ASSERT_LIST_NON_NULL(list, false);
+
+    // move ourselves to the given position
+    psListIterator* defaultIterator = list->iterators->data[0];
+    if (! psListIteratorSet(defaultIterator,location)) {
+        return false;
+    }
+
+    return listIteratorRemove(defaultIterator);
+}
+
+bool psListRemoveData(psList* list,
+                      psPtr data)
+{
+    PS_ASSERT_LIST_NON_NULL(list, false);
+    PS_ASSERT_PTR_NON_NULL(data, false);
+
+    psListElem* elem = list->head;
+    int index = 0;
+    while (elem != NULL && elem->data != data) {
+        elem = elem->next;
+        index++;
+    }
+    if (elem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified data item is not found in the psList."));
+        return false;
+    }
+
+    psListIterator* iterator = (psListIterator*)list->iterators->data[0];
+    iterator->index = index;
+    iterator->cursor = elem;
+
+    return listIteratorRemove(iterator);
+}
+
+psPtr psListGet(psList* list,
+                long location)
+{
+    PS_ASSERT_LIST_NON_NULL(list, NULL);
+
+    // XXX this should not be an error, right?
+    if (list->head == NULL) { // list empty?
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                _("Specified psList reference is empty."));
+        return NULL;
+    }
+
+    psListIterator* iterator = list->iterators->data[0];
+
+    if (! psListIteratorSet(iterator,location)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified location, %ld, is invalid."),
+                location);
+        return NULL;
+    }
+
+    return iterator->cursor->data;
+}
+
+// simultaneous get and remove (ie, 'pop')
+psPtr psListGetAndRemove(psList *list, long location) {
+
+    PS_ASSERT_LIST_NON_NULL(list, NULL);
+
+    // empty list :
+    // XXX handle this explicitly since psListGet raises an error in this case
+    if (list->head == NULL) {
+        return NULL;
+    }
+
+    psPtr *item = psListGet (list, location); // Item of interest
+    if (psMemIncrRefCounter(item)) {    // To prevent psListRemove from killing the item before it gets out
+        psListRemove(list, location);
+    }
+
+    return item;
+}
+
+/*
+ * and now return the previous/next element of the list
+ */
+psPtr psListGetAndIncrement(psListIterator* iterator)
+{
+    PS_ASSERT_LIST_ITERATOR_NON_NULL(iterator, NULL);
+
+    if (( iterator->cursor == NULL) && (iterator->offEnd)) {
+        return NULL;
+    }
+    if ( (iterator->cursor == NULL) && (!iterator->offEnd)) {
+        iterator->cursor = iterator->list->head;
+        iterator->index = 0;
+        return NULL;
+    }
+
+    psPtr data = iterator->cursor->data;
+
+    iterator->cursor = iterator->cursor->next;
+    iterator->index++;
+    if (iterator->cursor == NULL) {
+        iterator->offEnd = true;
+    }
+
+    return data;
+}
+
+psPtr psListGetAndDecrement(psListIterator* iterator)
+{
+    PS_ASSERT_LIST_ITERATOR_NON_NULL(iterator, NULL);
+
+    if ((iterator->cursor == NULL) && (!iterator->offEnd))  {
+        return NULL;
+    }
+    if ( (iterator->cursor == NULL) && (iterator->offEnd) ) {
+        iterator->cursor = iterator->list->tail;
+        iterator->index = iterator->list->n-1;
+        iterator->offEnd = false;
+        return NULL;
+    }
+
+    psPtr data = iterator->cursor->data;
+
+    iterator->cursor = iterator->cursor->prev;
+    iterator->index--;
+
+    return data;
+}
+
+/*
+ * Convert a psList to/from a psVoidPtrArray
+ */
+psArray* psListToArray(const psList* list)
+{
+    PS_ASSERT_LIST_NON_NULL(list, NULL);
+
+    long n = list->n;
+    psArray *arr = psArrayAlloc(n);
+
+    psListElem *ptr = list->head;
+    for (long i = 0; i < n; i++) {
+        arr->data[i] = psMemIncrRefCounter(ptr->data);
+        ptr = ptr->next;
+    }
+
+    return arr;
+}
+
+psList* psArrayToList(const psArray* array)
+{
+    PS_ASSERT_ARRAY_NON_NULL(array, NULL);
+
+    psList *list = psListAlloc(NULL);   // list of elements
+    for (long i = 0; i < array->n; i++) {
+        psListAdd(list, PS_LIST_TAIL, array->data[i]);
+    }
+    return list;
+}
+
+psList* psListSort(psList* list,
+                   psComparePtrFunc func)
+{
+    PS_ASSERT_LIST_NON_NULL(list, NULL);
+    PS_ASSERT_PTR_NON_NULL(func, NULL);
+
+    // convert to indexable vector for use by qsort.
+    psArray *arr = psListToArray(list);
+    psArray *iterators = psMemIncrRefCounter(list->iterators);
+    psFree(list);
+
+    arr = psArraySort(arr, func);
+
+    // convert back to linked list
+    list = psArrayToList(arr);
+    psFree(list->iterators);
+    list->iterators = iterators;
+    psFree(arr);
+
+    // Invalidate all iterator positions.
+    // Also need to point them to the new list.
+    for (int i = 0; i < iterators->n; i++) {
+        psListIterator *iterator = iterators->data[i]; // Iterator of interest
+        iterator->list = list;
+        iterator->cursor = NULL;
+    }
+
+    return list;
+}
+
+long psListLength(const psList *list)
+{
+    PS_ASSERT_LIST_NON_NULL(list, -1);
+    return list->n;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psList.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psList.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psList.h	(revision 22322)
@@ -0,0 +1,307 @@
+/** @file psList.h
+ *  @brief Support for doubly linked lists
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.49 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-14 03:18:41 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_LIST_H
+#define PS_LIST_H
+
+#include "psCompare.h"
+#include "psArray.h"
+#include "psMutex.h"
+
+/// @addtogroup DataContainer Data Containers
+///  @{
+
+/** Special values of index into list
+ *
+ *  This list of possible list position values should be contiguous
+ *  non-positive values ending with PS_LIST_UNKNOWN.  Any value
+ *  less-than-or-equal-to PS_LIST_UNKNOWN is considered a undefined position.
+ *
+ */
+enum {
+    PS_LIST_HEAD = 0,                  ///< at head
+    PS_LIST_TAIL = -1,                 ///< at tail
+};
+
+
+/** Doubly-linked list element */
+typedef struct psListElem {
+    struct psListElem* prev;           ///< previous link in list
+    struct psListElem* next;           ///< next link in list
+    psPtr data;                        ///< real data item
+} psListElem;
+
+
+/** The psList Linked list structure.  User should not allocate this struct
+ *  directly; rather the psListAlloc should be used.
+ *
+ *  @see psListAlloc
+ */
+typedef struct {
+    long n;                            ///< number of elements on list
+    psListElem* head;                  ///< first element on list (may be NULL)
+    psListElem* tail;                  ///< last element on list (may be NULL)
+    psArray* iterators;
+    ///< array of all iterators associated with this list.  First iterator is
+    ///< used internally to improve performance when using indexed access, all
+    ///< others are user-level iterators created by psListIteratorAlloc.
+    psMutex lock;                       ///< Optional lock for thread safety
+} psList;
+
+
+/** The psList iterator structure.  This should be allocated via
+ *  psListIteratorAlloc and not directly.
+ *
+ *  The life span of a psListIterator object is ended by either a psFree
+ *  of this structure OR psFree of the psList in which it operates on.
+ *
+ *  @see psListIteratorAlloc, psListIteratorSet, psListGetAndIncrement, psListGetAndDecrement
+ */
+typedef struct
+{
+    psList* list;                      ///< List iterator to works on
+    psListElem* cursor;                ///< current cursor position
+    bool offEnd;                       ///< Iterator off the end?
+    long index;                         ///< the index number in the list
+    bool mutable;                      ///< Is it permissible to modify the list?
+}
+psListIterator;
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr
+ *  datatype.
+ *
+ *  @return bool:       True if the pointer matches a psList structure, false otherwise.
+ */
+bool psMemCheckList(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Creates a psList linked list object.
+ *
+ *  @return psList* A new psList object.
+ */
+#ifdef DOXYGEN
+psList* psListAlloc(
+    psPtr data          ///< initial data item; may be NULL if an empty psList is desired
+);
+#else // ifdef DOXYGEN
+psList* p_psListAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psPtr data                          ///< initial data item; may be NULL if an empty psList is desired
+) PS_ATTR_MALLOC;
+#define psListAlloc(data) \
+      p_psListAlloc(__FILE__, __LINE__, __func__, data)
+#endif // ifdef DOXYGEN
+
+
+/** Creates a psListIterator object and associates it with a psList.
+ *
+ *  @return psListIterator* A new psListIterator object.
+ */
+#ifdef DOXYGEN
+psListIterator* psListIteratorAlloc(
+    psList* list,                      ///< the psList to iterate with
+    long location,                     ///< the initial starting point.
+    ///<  This can be a numeric index, PS_LIST_HEAD, or PS_LIST_TAIL.
+    bool mutable                       ///< Is it permissible to modify list?
+);
+#else // ifdef DOXYGEN
+psListIterator* p_psListIteratorAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psList* list,                      ///< the psList to iterate with
+    long location,                     ///< the initial starting point.
+    ///<  This can be a numeric index, PS_LIST_HEAD, or PS_LIST_TAIL.
+    bool mutable                       ///< Is it permissible to modify list?
+) PS_ATTR_MALLOC;
+#define psListIteratorAlloc(list, location, mutable) \
+      p_psListIteratorAlloc(__FILE__, __LINE__, __func__, list, location, mutable)
+#endif // ifdef DOXYGEN
+
+
+/** Set the iterator of the list to a given position.  If location is invalid the
+ *  iterator position is not changed.
+ *
+ *  @return bool        TRUE if iterator successfully set, otherwise FALSE.
+ */
+bool psListIteratorSet(
+    psListIterator* iterator,          ///< list iterator
+    long location                      ///< index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+
+/** Adds an element to a psList at position given.
+ *
+ *  @return bool        TRUE if item was successfully added, otherwise FALSE.
+ */
+bool psListAdd(
+    psList* list,                      ///< list to add item to
+    long location,                     ///< index, PS_LIST_HEAD, PS_LIST_TAIL, or numbered location.
+    psPtr data                         ///< data item to add.  If NULL, list is not modified.
+);
+
+
+/** Adds an data item to a psList at position just after the list position given
+ *
+ *  @return bool        TRUE if item was successfully added, otherwise FALSE.
+ */
+bool psListAddAfter(
+    psListIterator* iterator,          ///< list position to add item to
+    psPtr data                         ///< data item to add.  If NULL, list is not modified.
+);
+
+
+/** Adds a data item to a psList at position just before the list position given
+ *
+ *  @return bool        TRUE if item was successfully added, otherwise FALSE.
+ */
+bool psListAddBefore(
+    psListIterator* iterator,          ///< list position to add item to
+    psPtr data                         ///< data item to add.  If NULL, list is not modified.
+);
+
+/** Remove an item at the specified location from a list.
+ *
+ *  @return bool        TRUE if element is successfully removed, otherwise FALSE.
+ */
+bool psListRemove(
+    psList* list,                      ///< list to remove element from
+    long location                      ///< index of item
+);
+
+
+/** Remove an item from a list.
+ *
+ *  @return bool        TRUE if element is successfully removed, otherwise FALSE.
+ */
+bool psListRemoveData(
+    psList* list,                      ///< list to remove element from
+    psPtr data                         ///< data item to find and remove
+);
+
+
+/** Retrieve an item from a list.
+ *
+ *  @return psPtr       the item corresponding to the location parameter.  If
+ *                      location is invalid (e.g., a numbered index greater
+ *                      than the list size or if the list is empty), a
+ *                      NULL is returned.
+ */
+psPtr psListGet(
+    psList* list,                      ///< list to retrieve element from
+    long location                      ///< index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+/** Retrieve an item from a list.
+ *
+ *  @return psPtr       the item corresponding to the location parameter.  If
+ *                      location is invalid (e.g., a numbered index greater
+ *                      than the list size or if the list is empty), a
+ *                      NULL is returned.
+ */
+psPtr psListGetAndRemove(
+    psList *list,                       ///< list from which to get and remove the element
+    long location                       ///< index of item
+);
+
+/** Position the specified iterator to the next item in list.
+ *
+ *  @return psPtr       the data item at the original iterator position or NULL if the
+ *                      iterator went past the end of the list.
+ */
+psPtr psListGetAndIncrement(
+    psListIterator* iterator           ///< iterator to move
+);
+
+
+/** Position the specified iterator to the previous item in list.
+ *
+ *  @return psPtr       the data item at the original iterator position or NULL if the
+ *                      iterator went past the beginning of the list.
+ */
+psPtr psListGetAndDecrement(
+    psListIterator* iterator           ///< iterator to move
+);
+
+
+/** Convert a linked list to an array
+ *
+ *  @return psArray* A new psArray populated with elements from the list,
+ *                      or NULL if the given dlist parameter is NULL.
+ */
+psArray* psListToArray(
+    const psList* list                 ///< List to convert
+);
+
+
+/** Convert array to a doubly-linked list
+ *
+ *  @return psList* A new psList populated with elements formt the psArray,
+ *                      or NULL is the given arr parameter is NULL.
+ */
+psList* psArrayToList(
+    const psArray* array               ///< vector to convert
+);
+
+
+/** Sort a list via a comparison function.
+ *
+ *  The comparison function must return an integer less than, equal to, or
+ *  greater than zero if the first argument is considered to be respectively
+ *  less than, equal to, or greater than the second.
+ *
+ *  If two members compare as equal, their order in the sorted array is
+ *  undefined.
+ *
+ *  @return psList*     Sorted list.
+ */
+psList* psListSort(
+    psList* list,                      ///< the list to sort
+    psComparePtrFunc func              ///< the comparison function
+);
+
+
+/** Get the number of elements in use from a specified psList. (list.n)
+ *
+ *  @return long:       The number of elements in use.
+ */
+long psListLength(
+    const psList *list                 ///< input psList
+);
+
+
+#define PS_ASSERT_LIST_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->iterators || (NAME)->n < 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: List %s or one of its components is NULL.", \
+            #NAME); \
+    return RVAL; \
+}
+
+#define PS_ASSERT_LIST_ITERATOR_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->list || !(NAME)->list->iterators || (NAME)->list->n < 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: List iterator %s or one of its components is NULL.", \
+            #NAME); \
+    return RVAL; \
+}
+
+
+/// @} End of DataContainer Functions
+#endif // #ifndef PS_LIST_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psLookupTable.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psLookupTable.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psLookupTable.c	(revision 22322)
@@ -0,0 +1,737 @@
+/** @file  psLookupTable.c
+*
+*  @brief This file defines the structure and functions for table lookups.
+*
+*  @ingroup types
+*
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.51 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2008-04-17 23:43:03 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#undef __STRICT_ANSI__
+#include <stdlib.h>
+#define __STRICT_ANSI__
+#include <limits.h>
+#include <math.h>
+#include <inttypes.h>
+
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psError.h"
+#include "psLookupTable.h"
+
+#include "psAssert.h"
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Maximum size of a string */
+#define MAX_STRING_LENGTH   256
+#define ARRAY_STRIDE        16
+#define INITIAL_NUM         10          // Initial number of elements
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+static bool ignoreLine(char *inString);
+static char *cleanString(char *inString, int sLen);
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status);
+static void parseValue(psVector *vec, psU64 index, char* strValue, psParseErrorType *status);
+static void lookupTableFree(psLookupTable* table);
+
+/* Determines if a line is blank (whitespace only) or a commentline. It returns true if so.
+   The input string must be null terminated. */
+static bool ignoreLine(char *inString)
+{
+    while (*inString!='\0' && *inString!='#') {
+        if (!isspace(*inString)) {
+            return false;
+        }
+        inString++;
+    }
+    return true;
+}
+
+
+/* Removes leading and trailing whitespace and # characters from a string. The
+   cleaned string is a new null terminated copy of the original input string. */
+static char *cleanString(char *inString,
+                         int sLen)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+    ptrB = inString;
+
+    // Skip over leading # or whitespace
+    //    while (isspace(*ptrB) || *ptrB=='#') {
+    //        ptrB++;
+    //    }
+
+    // Skip over trailing whitespace, null terminators, and # characters
+    ptrE = inString + sLen;
+    while (isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+        ptrE--;
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    cleaned = psStringNCopy(ptrB, sLen);
+
+    return cleaned;
+}
+
+
+/* Returns cleaned token based on delimiter, but not including delimiter. Also changes
+   the pointer location the beginning of the string. Tokens are newly allocated null
+    terminated strings. */
+static char* getToken(char **inString,
+                      char *delimiter,
+                      psParseErrorType *status)
+{
+    char *cleanToken = NULL;
+    int sLen = 0;
+
+    // Skip over leading whitespace
+    while (isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+    if (sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen);
+
+        // Move to end of token
+        (*inString) += sLen;
+    }
+    /*    else if (**inString!='\0' && sLen==0) {
+            *status = PS_PARSE_ERROR_GENERAL;
+        }
+    */
+    return cleanToken;
+}
+
+#define PARSE_VALUE_INT_CASE(TYPE, FUNC) \
+    case PS_TYPE_##TYPE: { \
+        char *end = NULL; \
+        ps##TYPE value = FUNC(strValue, &end, 0); \
+        if (*end != '\0' && !isspace(*end)) { \
+            *status = PS_PARSE_ERROR_VALUE; \
+        } \
+        vec->data.TYPE[index] = value; \
+        return; \
+    }
+
+#define PARSE_VALUE_FLOAT_CASE(TYPE, FUNC) \
+    case PS_TYPE_##TYPE: { \
+        char *end = NULL; \
+        ps##TYPE value = FUNC(strValue, &end); \
+        if (*end != '\0' && !isspace(*end)) { \
+            *status = PS_PARSE_ERROR_VALUE; \
+        } \
+        vec->data.TYPE[index] = value; \
+        return; \
+    }
+
+
+/* Returns single parsed value as a double precision number. The input string must be
+   cleaned and null terminated. */
+static void parseValue(psVector *vec,
+                       psU64 index,
+                       char* strValue,
+                       psParseErrorType *status)
+{
+    switch (vec->type.type) {
+        PARSE_VALUE_INT_CASE(S32, strtol);
+        PARSE_VALUE_INT_CASE(S64, strtoll);
+        PARSE_VALUE_FLOAT_CASE(F32, strtof);
+        PARSE_VALUE_FLOAT_CASE(F64, strtod);
+      default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Bad type for vector: %x.\n", vec->type.type);
+        *status = PS_PARSE_ERROR_TYPE;
+    }
+    return;
+}
+
+static void lookupTableFree(psLookupTable* table)
+{
+    psAssert(table, "impossible");
+    psFree(table->values);
+    psFree(table->filename);
+    psFree(table->format);
+    psFree(table->index);
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+bool psMemCheckLookupTable(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)lookupTableFree );
+}
+
+
+psLookupTable* p_psLookupTableAlloc(const char *file,
+                                    unsigned int lineno,
+                                    const char *func,
+                                    const char *filename,
+                                    const char *format,
+                                    long indexCol)
+{
+    PS_ASSERT_STRING_NON_EMPTY(filename,NULL);
+    PS_ASSERT_STRING_NON_EMPTY(format,NULL);
+
+    psLookupTable *outTable = p_psAlloc(file, lineno, func, sizeof(psLookupTable));
+
+    // Set deallocator
+    psMemSetDeallocator(outTable, (psFreeFunc)lookupTableFree);
+
+    // Allocate and set file name and format strings
+    outTable->filename = psStringCopy(filename);
+    outTable->format = psStringCopy(format);
+
+    // Valid ranges. Automatically set by table read if both zero.
+    *(double *)&outTable->validFrom = 0;
+    *(double *)&outTable->validTo = 0;
+    outTable->indexCol = indexCol;
+
+    // Vector of independent index values. Filled by table read.
+    outTable->index = NULL;
+
+    // Array of dependent table values corresponding to index values. Filled by table read.
+    outTable->values = NULL;
+
+    return outTable;
+}
+
+psArray *psVectorsReadFromFile(const char *filename,
+                               const char *format)
+{
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(format, NULL);
+
+    psArray*          outputArray = NULL;
+    psVector*         colVector   = NULL;
+    char*             strValue    = NULL;
+    char*             strNum      = NULL;
+    char*             line        = NULL;
+    char*             linePtr     = NULL;
+    int               numCols     = 0;
+    int               numRows     = 0;
+    FILE*             fp          = NULL;
+    const char*       tempFormat  = NULL;
+    psParseErrorType  parseStatus = PS_PARSE_SUCCESS;
+
+    // Create temp pointer which can then be used several times
+    tempFormat = format;
+
+    // Create output array and set array elements to zero
+    outputArray = psArrayAllocEmpty(INITIAL_NUM);
+
+    // Parse the format string to determine how many vectors
+    // and whether the format string is valid
+    while ((strValue = getToken((char**)&tempFormat, " \t", &parseStatus))) {
+
+        // Check for %d format sub string
+        if (strcmp(strValue,"\%d") == 0 ) {
+            numCols++;
+            colVector = psVectorAlloc(1,PS_TYPE_S32);
+            outputArray = psArrayAdd(outputArray, ARRAY_STRIDE, colVector);
+            psFree(colVector);
+        } else if (strcmp(strValue,"\%ld") == 0) {
+            numCols++;
+            colVector = psVectorAlloc(1,PS_TYPE_S64);
+            outputArray = psArrayAdd(outputArray, ARRAY_STRIDE, colVector);
+            psFree(colVector);
+        } else if (strcmp(strValue,"\%f") == 0) {
+            numCols++;
+            colVector = psVectorAlloc(1,PS_TYPE_F32);
+            outputArray = psArrayAdd(outputArray, ARRAY_STRIDE, colVector);
+            psFree(colVector);
+        } else if (strcmp(strValue,"\%lf") == 0) {
+            numCols++;
+            colVector = psVectorAlloc(1,PS_TYPE_F64);
+            outputArray = psArrayAdd(outputArray, ARRAY_STRIDE, colVector);
+            psFree(colVector);
+        } else if (strstr(strValue,"\%*") != 0) {
+            // Don't increase number of columns
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Invalid format specifier");
+            psFree(strValue);
+            numCols = 0;
+            break;
+        }
+        psFree(strValue);
+    }
+
+    // If the format string was parsed successfully and return numCols the
+    // prepare to open file and read values
+    if (numCols > 0) {
+
+        // Open specified file
+        if ((fp=fopen(filename, "r")) == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Failed to open file %s."),
+                    filename);
+            psFree(outputArray);
+            return NULL;
+        } else {
+            // Initialize array index
+            int arrayIndex = 0;
+
+            // Create reusable line for continuous read
+            line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
+
+            // Loop through file to get numRows, numCols, and column data types
+            while ((fgets(line, MAX_STRING_LENGTH, fp) != NULL) &&
+                    (parseStatus == PS_PARSE_SUCCESS))   {
+
+                // Copy pointer to line for parsing
+                linePtr = line;
+
+                // If line is not a comment or blank, then extract data
+                if (!ignoreLine(linePtr)) {
+                    numRows++;
+
+                    // Copy format pointer for parsing
+                    tempFormat = format;
+                    arrayIndex = 0;
+                    parseStatus = PS_PARSE_SUCCESS;
+
+                    // Loop through format and line strings to get values in text table file
+                    while ((strValue=getToken((char**)&tempFormat," \t",&parseStatus))
+                            && (strNum=getToken((char**)&linePtr," \t",&parseStatus)) ) {
+                        // Set column vector
+                        colVector = outputArray->data[arrayIndex];
+
+                        // Set column entries based on format string defining the type
+                        if (strcmp(strValue,"\%d") == 0 ) {
+                            colVector = psVectorRecycle(colVector, numRows,
+                                                        colVector->type.type);
+                            parseValue(colVector,numRows-1,strNum,&parseStatus);
+                            arrayIndex++;
+                        } else if (strcmp(strValue,"\%ld") == 0) {
+                            colVector = psVectorRecycle(colVector, numRows,
+                                                        colVector->type.type);
+                            parseValue(colVector,numRows-1,strNum,&parseStatus);
+                            arrayIndex++;
+                        } else if (strcmp(strValue,"\%f") == 0) {
+                            colVector = psVectorRecycle(colVector, numRows,
+                                                        colVector->type.type);
+                            parseValue(colVector,numRows-1,strNum,&parseStatus);
+                            arrayIndex++;
+                        } else if (strcmp(strValue,"\%lf") == 0) {
+                            colVector = psVectorRecycle(colVector, numRows,
+                                                        colVector->type.type);
+                            parseValue(colVector,numRows-1,strNum,&parseStatus);
+                            arrayIndex++;
+                        } else if (strstr(strValue,"\%*") != 0) {
+                            // Don't increase number of columns
+                        }
+                        psFree(strValue);
+                        psFree(strNum);
+
+                        // If the file line was not parsed successful report
+                        // error and return NULL
+                        if (parseStatus != PS_PARSE_SUCCESS) {
+                            psError(PS_ERR_UNKNOWN, true,
+                                    "Parsing text file failed.");
+                            fclose(fp);
+                            psFree(outputArray);
+                            psFree(line);
+                            return NULL;
+                        }
+                    }
+                    if (strValue != NULL && strNum == NULL) {
+                        psError(PS_ERR_UNKNOWN, true,
+                                "Parsing text file failed - missing table value(s).");
+                        fclose(fp);
+                        psFree(outputArray);
+                        psFree(line);
+                        psFree(strValue);
+                        return NULL;
+                    }
+                }  // ignore line
+            }
+
+            //Return NULL for an empty table
+            if (numRows == 0) {
+                psError(PS_ERR_UNKNOWN, true,
+                        "Parsing text file failed - input table is empty.");
+                fclose(fp);
+                psFree(outputArray);
+                psFree(line);
+                return NULL;
+            }
+
+            // Read on the lines in the file - close file pointer
+            fclose(fp);
+            psFree(line);
+        }
+    } else {
+        // Format string parse error detected
+        psError(PS_ERR_UNKNOWN, true,
+                "Format string was not parsed sucessfully");
+        psFree(outputArray);
+        return NULL;
+    }
+
+    // Return populated array
+    return outputArray;
+}
+
+bool p_psLookupTableImport(const char *file,
+                           unsigned int lineno,
+                           const char *func,
+                           psLookupTable *table,
+                           psArray *vectors,
+                           long indexCol)
+{
+    PS_ASSERT_LOOKUPTABLE_NON_NULL(table, false);
+    PS_ASSERT_ARRAY_NON_NULL(vectors, false);
+    PS_ASSERT_INT_NONNEGATIVE(indexCol, false);
+    if (indexCol >= vectors->n) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Index column, %ld, is larger"
+                " than number of columns, %ld.", indexCol, vectors->n);
+        return false;
+    }
+
+    psVector *indexVector = vectors->data[indexCol]; // The vector that provides the index
+    long numRows = indexVector->n;      // Number of rows in table
+    long numCols = vectors->n;          // Number of columns in table
+
+#define CHECK_SORT_CASE(TYPE) \
+    case PS_TYPE_##TYPE: \
+        for (long i = 1; i < numRows; i++) { \
+             if (indexVector->data.TYPE[i] < indexVector->data.TYPE[i - 1]) { \
+                 sortNeeded = true; \
+                 break; \
+             } \
+         } \
+         break;
+
+    // Check if array is sorted on the index column
+    bool sortNeeded  = false;           // Do we need to sort?
+    switch (indexVector->type.type) {
+        CHECK_SORT_CASE(U8);
+        CHECK_SORT_CASE(U16);
+        CHECK_SORT_CASE(U32);
+        CHECK_SORT_CASE(U64);
+        CHECK_SORT_CASE(S8);
+        CHECK_SORT_CASE(S16);
+        CHECK_SORT_CASE(S32);
+        CHECK_SORT_CASE(S64);
+        CHECK_SORT_CASE(F32);
+        CHECK_SORT_CASE(F64);
+      default:
+        psAbort("Should never get here: bad type.");
+    }
+
+    if (sortNeeded) {
+        psVector *sortedIndex = psVectorSortIndex(NULL, indexVector); // Indices for sorting
+        psArray *newValues = psArrayAlloc(numCols);
+        for (long i = 0; i < numCols; i++) {
+            psVector *oldVector = vectors->data[i]; // Vector of interest
+            psVector *newVector = psVectorAlloc(numRows, oldVector->type.type);
+            newValues->data[i] = newVector;
+
+#define REARRANGE_CASE(TYPE) \
+    case PS_TYPE_##TYPE: \
+        for (long j = 0; j < numRows; j++) { \
+            newVector->data.TYPE[j] = oldVector->data.TYPE[sortedIndex->data.S32[j]]; \
+        } \
+        break;
+
+            switch (oldVector->type.type) {
+                REARRANGE_CASE(U8);
+                REARRANGE_CASE(U16);
+                REARRANGE_CASE(U32);
+                REARRANGE_CASE(U64);
+                REARRANGE_CASE(S8);
+                REARRANGE_CASE(S16);
+                REARRANGE_CASE(S32);
+                REARRANGE_CASE(S64);
+                REARRANGE_CASE(F32);
+                REARRANGE_CASE(F64);
+              default:
+                psAbort("Should never get here: bad type.");
+            }
+        }
+
+        psFree(sortedIndex);
+        table->values = newValues;
+    } else {
+        table->values = psMemIncrRefCounter(vectors);
+    }
+
+    table->index = psMemIncrRefCounter(table->values->data[indexCol]);
+    table->indexCol = indexCol;
+
+#define SET_VALID_CASE(TYPE) \
+    case PS_TYPE_##TYPE: \
+        *(double*)&table->validFrom = table->index->data.TYPE[0]; \
+        *(double*)&table->validTo   = table->index->data.TYPE[numRows - 1]; \
+        break;
+
+    switch (table->index->type.type) {
+        SET_VALID_CASE(U8);
+        SET_VALID_CASE(U16);
+        SET_VALID_CASE(U32);
+        SET_VALID_CASE(U64);
+        SET_VALID_CASE(S8);
+        SET_VALID_CASE(S16);
+        SET_VALID_CASE(S32);
+        SET_VALID_CASE(S64);
+        SET_VALID_CASE(F32);
+        SET_VALID_CASE(F64);
+      default:
+        psAbort("Should never get here: bad type.");
+    }
+
+    return true;
+}
+
+long psLookupTableRead(psLookupTable* table)
+{
+    PS_ASSERT_LOOKUPTABLE_NON_NULL(table, 0);
+
+    // Read vectors from file specified in table
+    psArray *vectors = psVectorsReadFromFile(table->filename, table->format); // Vectors in file
+    if (!vectors) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read vectors from %s", table->filename);
+        return 0;
+    }
+
+    // Import vector into table
+    if (!psLookupTableImport(table, vectors, table->indexCol)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to import vectors from %s into lookup table.",
+                table->filename);
+        psFree(vectors);
+        return 0;
+    }
+    psFree(vectors);                // Drop reference
+
+    return table->index->n;
+}
+
+#define CONVERT_VALUE_TO_F64(VECTOR,INDEX,RESULT)     \
+switch(VECTOR->type.type) {                           \
+case PS_TYPE_U8:                                      \
+    RESULT = (psF64)VECTOR->data.U8[INDEX];           \
+    break;                                            \
+case PS_TYPE_U16:                                     \
+    RESULT = (psF64)VECTOR->data.U16[INDEX];          \
+    break;                                            \
+case PS_TYPE_U32:                                     \
+    RESULT = (psF64)VECTOR->data.U32[INDEX];          \
+    break;                                            \
+case PS_TYPE_U64:                                     \
+    RESULT = (psF64)VECTOR->data.U64[INDEX];          \
+    break;                                            \
+case PS_TYPE_S8:                                      \
+    RESULT = (psF64)VECTOR->data.S8[INDEX];           \
+    break;                                            \
+case PS_TYPE_S16:                                     \
+    RESULT = (psF64)VECTOR->data.S16[INDEX];          \
+    break;                                            \
+case PS_TYPE_S32:                                     \
+    RESULT = (psF64)VECTOR->data.S32[INDEX];          \
+    break;                                            \
+case PS_TYPE_S64:                                     \
+    RESULT = (psF64)VECTOR->data.S64[INDEX];          \
+    break;                                            \
+case PS_TYPE_F32:                                     \
+    RESULT = (psF64)VECTOR->data.F32[INDEX];          \
+    break;                                            \
+case PS_TYPE_F64:                                     \
+    RESULT = VECTOR->data.F64[INDEX];                 \
+    break;                                            \
+default:                                              \
+    RESULT = NAN;                                     \
+    break;                                            \
+}
+
+#define CHECK_LOWER_UPPER_BOUND(TABLE,INDEX,COLUMN)                     \
+switch (TABLE->index->type.type) {                                      \
+case PS_TYPE_S32:                                                       \
+    if ( (psS32)index < TABLE->index->data.S32[0] ) {                    \
+        return NAN;                                                     \
+    }                                                                   \
+    if ( (psS32)index > TABLE->index->data.S32[numRows-1] ) {            \
+        return NAN;                                                     \
+    }                                                                   \
+    break;                                                              \
+case PS_TYPE_S64:                                                       \
+    if ( (psS64)index < TABLE->index->data.S64[0] ) {                    \
+        return NAN;                                                     \
+    }                                                                   \
+    if ( (psS64)index > TABLE->index->data.S64[numRows-1] ) {            \
+        return NAN;                                                     \
+    }                                                                   \
+    break;                                                              \
+case PS_TYPE_F32:                                                       \
+    if ( (psF32)index < TABLE->index->data.F32[0] ) {                    \
+        return NAN;                                                     \
+    }                                                                   \
+    if ( (psF32)index > TABLE->index->data.F32[numRows-1] ) {            \
+        return NAN;                                                     \
+    }                                                                   \
+    break;                                                              \
+case PS_TYPE_F64:                                                       \
+    if ( index < TABLE->index->data.F64[0] ) {                           \
+        return NAN;                                                     \
+    }                                                                   \
+    if ( index > TABLE->index->data.F64[numRows-1] ) {                   \
+        return NAN;                                                     \
+    }                                                                   \
+    break;                                                              \
+default:                                                                \
+    return NAN;                                                         \
+    break;                                                              \
+}
+
+double psLookupTableInterpolate(const psLookupTable *table,
+                                double index,
+                                long column)
+{
+    PS_ASSERT_LOOKUPTABLE_NON_NULL(table, NAN);
+    // Ensuring information exists
+    PS_ASSERT_VECTOR_NON_NULL(table->index, NAN);
+    PS_ASSERT_ARRAY_NON_NULL(table->values, NAN);
+    PS_ASSERT_INT_WITHIN_RANGE(column, 0, (int)table->values->n - 1, NAN);
+    PS_ASSERT_VECTOR_NON_NULL((psVector*)table->values->data[column], NAN);
+
+    psU64 hiIdx = 0;
+    psU64 loIdx = 0;
+    long  numRows = 0;
+    long numCols = 0;
+    psF64 out = 0.0;
+    psF64 denom = 0.0;
+    psF64 convertVal = 0.0;
+    psF64 tempVal = 0.0;
+    psVector *indexVec = NULL;
+    psVector *valuesVec = NULL;
+    psArray *values = NULL;
+
+    indexVec = table->index;
+    values = table->values;
+    numRows = table->index->n;
+    numCols = table->values->n;
+    valuesVec = (psVector*)values->data[column];
+
+    // Verify the index is within the bounds of the table
+    CHECK_LOWER_UPPER_BOUND(table,index,column);
+
+    // Find location in table where specified index is between to entries
+    CONVERT_VALUE_TO_F64(indexVec, 0, convertVal)
+    while (index > convertVal ) {
+        hiIdx++;
+        /*  XXX:  following is unreachable.
+                if (hiIdx >= numRows) {
+                    psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                            _("High index too big, %" PRIu64 "."), hiIdx);
+                    return NAN;
+                }
+        */
+        CONVERT_VALUE_TO_F64(indexVec, hiIdx, convertVal)
+    }
+
+    // Check for negative low index and generate error
+    loIdx = hiIdx--;
+    if (loIdx < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Low index too small, %" PRIu64 "."), loIdx);
+        return NAN;
+    }
+
+    // Perform linear interpolation to calculate return value
+    CONVERT_VALUE_TO_F64(indexVec, hiIdx, denom)
+    CONVERT_VALUE_TO_F64(indexVec, loIdx, convertVal);
+    denom -= convertVal;
+    if (fabs(denom) < FLT_EPSILON) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Divide by zero error during interpolation."));
+        return NAN;
+    } else {
+        CONVERT_VALUE_TO_F64(valuesVec,hiIdx,tempVal)
+        CONVERT_VALUE_TO_F64(valuesVec,loIdx,convertVal)
+        tempVal -= convertVal;
+        CONVERT_VALUE_TO_F64(indexVec,loIdx,convertVal)
+        out = tempVal*(index-convertVal)/denom;
+        CONVERT_VALUE_TO_F64(valuesVec,loIdx,convertVal)
+        out += convertVal;
+    }
+
+    return out;
+}
+
+psVector* psLookupTableInterpolateAll(const psLookupTable *table,
+                                      double index)
+{
+    PS_ASSERT_LOOKUPTABLE_NON_NULL(table, NULL);
+    // Ensuring information exists
+    PS_ASSERT_VECTOR_NON_NULL(table->index, NULL);
+    PS_ASSERT_ARRAY_NON_NULL(table->values, NULL);
+    long numCols = table->values->n;
+    if (numCols == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "No columns in lookup table from which to interpolate.");
+        return NULL;
+    }
+
+    psVector *outVector = psVectorAlloc(numCols, PS_TYPE_F64);
+
+    // Fill vectors with results and status of results
+    // XXX: This algorithm must be changed to something more efficient!
+    for (long i = 0; i < numCols; i++) {
+        outVector->data.F64[i] = psLookupTableInterpolate(table, index, i);
+        if (isnan(outVector->data.F64[i])) {
+            // Free allocated vector
+            psFree(outVector);
+            outVector = NULL;
+            // Break out of loop since error detected
+            break;
+        }
+    }
+
+    return outVector;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psLookupTable.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psLookupTable.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psLookupTable.h	(revision 22322)
@@ -0,0 +1,190 @@
+/** @file  psLookupTable.h
+*
+*  @brief This file defines the structure and functions for table lookups.
+*
+*  @author PAP, IfA
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-08-09 01:40:08 $
+*
+*  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_LOOKUPTABLE_H
+#define PS_LOOKUPTABLE_H
+
+/// @addtogroup DataContainer Data Containers
+/// @{
+
+#include "psType.h"
+#include "psVector.h"
+#include "psArray.h"
+#include "psLogMsg.h"
+#include "psString.h"
+
+/** Lookup table structure
+ *
+ *  Holds table data read from external data files.
+ *
+ */
+typedef struct {
+    psString filename;                  ///< Name of file with table
+    psString format;                    ///< scanf-like format string for file
+    long indexCol;                     ///< Column of the index vector (starting at zero)
+    psVector *index;                   ///< Vector of independent index values
+    psArray *values;                   ///< Array of dependent table values corresponding to index values
+    const double validFrom;            ///< Lower bound for rable read
+    const double validTo;              ///< Upper bound for table read
+} psLookupTable;
+
+#define PS_ASSERT_LOOKUPTABLE_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->filename || strlen((NAME)->filename) == 0 || \
+    !(NAME)->format || strlen((NAME)->format) == 0 || (NAME)->index < 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: Lookup table %s or one of its components is NULL.", \
+            #NAME); \
+    return RVAL; \
+}
+
+/** Lookup table lookup status and error conditions
+ *
+ *  Success, failure, and status conditions for table lookups.
+ */
+typedef enum {
+    PS_LOOKUP_SUCCESS             = 0x0000,        ///< Table lookup succeeded
+    PS_LOOKUP_PAST_TOP            = 0x0101,        ///< Lookup off top of table
+    PS_LOOKUP_PAST_BOTTOM         = 0x0102,        ///< Lookup off bottom of table
+    PS_LOOKUP_ERROR               = 0x0104         ///< Any other type of lookup error
+} psLookupStatusType;
+
+
+/** Lookup table parse status and error conditions
+ *
+ *  Success, failure, and status conditions for table parsing.
+ */
+typedef enum {
+    PS_PARSE_SUCCESS              = 0x0000,        ///< Table lookup succeeded
+    PS_PARSE_ERROR_TYPE           = 0x0101,        ///< Error parsing type
+    PS_PARSE_ERROR_VALUE          = 0x0102,        ///< Error parsing numerical value
+    PS_PARSE_ERROR_GENERAL        = 0x0104         ///< Any other type of lookup error
+}psParseErrorType;
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr
+ *  datatype.
+ *
+ *  @return bool:       True if the pointer matches a psLookupTable structure,
+ *  false otherwise.
+ */
+bool psMemCheckLookupTable(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Allocator for psLookupTable struct
+ *
+ *  Allocates a new psLookupTable struct.
+ *
+ *  @return psLookupTable*     New psLookupTable struct.
+ */
+#ifdef DOXYGEN
+psLookupTable* psLookupTableAlloc(
+    const char *filename,              ///< Name of file to read
+    const char *format,                ///< scanf-like format string
+    long indexCol                      ///< Column of the index vector (starting at zero)
+);
+#else // ifdef DOXYGEN
+psLookupTable* p_psLookupTableAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const char *filename,              ///< Name of file to read
+    const char *format,                ///< scanf-like format string
+    long indexCol                      ///< Column of the index vector (starting at zero)
+) PS_ATTR_MALLOC;
+#define psLookupTableAlloc(filename, format, indexCol) \
+      p_psLookupTableAlloc(__FILE__, __LINE__, __func__, filename, format, indexCol)
+#endif // ifdef DOXYGEN
+
+
+/** Read vectors from file
+ *
+ *  Read numeric vectors from ASCII text file
+ *
+ *  @return psArray*  New array of psVectors corresponding to the columns of the table
+ */
+psArray *psVectorsReadFromFile(
+    const char* filename,              ///< File to be read
+    const char* format                 ///< scanf-like format string
+);
+
+
+/** Import arrays of vectors into a table
+ *
+ *  Import array of vectors read from text file into a table structure
+ *
+ *  @return psLookupTable*  Lookup table structure with array vector data
+ */
+#ifdef DOXYGEN
+bool psLookupTableImport(
+    psLookupTable *table,              ///< Lookup table into which to import
+    const psArray *vectors,            ///< Array of vectors
+    long indexCol                      ///< Index of the index vector in the array of vectors
+);
+#else // ifdef DOXYGEN
+bool p_psLookupTableImport(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psLookupTable *table,               ///< Lookup table into which to import
+    psArray *vectors,                   ///< Array of vectors
+    long indexCol                       ///< Index of the index vector in the array of vectors
+);
+#define psLookupTableImport(table, vectors, indexCol) \
+      p_psLookupTableImport(__FILE__, __LINE__, __func__, table, vectors, indexCol)
+#endif // ifdef DOXYGEN
+
+
+/** Read lookup table
+ *
+ *  Reads a lookup table and fills corresponding psLookupTable struct.
+ *
+ *  @return long:     Number of valid lines read
+ */
+long psLookupTableRead(
+    psLookupTable *table               ///< Table to read
+);
+
+
+/** Lookup and interpolate value from table.
+ *
+ *  Interpolates value from table. Sets status bit for success or one of
+ *  several possible failure conditions.
+ *
+ *  @return double     Interpolation value at index
+ */
+double psLookupTableInterpolate(
+    const psLookupTable *table,        ///< Table with data
+    double index,                      ///< Value to be interpolated
+    long column                        ///< Column in table to be interpolated
+);
+
+
+/** Lookup and interpolate all values from table.
+ *
+ *  Interpolates all values from table. Sets status bit for success or one of
+ *  several possible failure conditions.
+ *
+ *  @return psVector*     Interpolation values calculated at index
+ */
+psVector* psLookupTableInterpolateAll(
+    const psLookupTable *table,         ///< Table with data
+    double index                        ///< Value to be interpolated
+);
+
+
+/// @}
+#endif // #ifndef PS_LOOKUPTABLE_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadata.c	(revision 22322)
@@ -0,0 +1,1556 @@
+/** @file  psMetadata.c
+ *
+ *
+ *  @brief Contains metadata structures, enumerations and functions prototypes.
+ *
+ *  This file defines metadata item, metadata type, metadata flags, metadata containers, and function
+ *  prototypes necessary creating psLib metadata APIs
+ *
+ *  @ingroup Metadata
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.172 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-23 19:22:09 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "fitsio.h"
+#include "psType.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psList.h"
+#include "psHash.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psLookupTable.h"
+#include "psString.h"
+
+#include "psAssert.h"
+#include "psLogMsg.h"
+#include "psTrace.h"
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+#define MAX_STRING_SIZE 1024            // Maximum string size for psMetadatItem
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+static psS32 metadataId = 0;
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+static psMetadataItem* makeMetaMulti(psHash* table,
+                                     const char* key,
+                                     psMetadataItem* existing)
+{
+
+    if (existing != NULL && existing->type == PS_DATA_METADATA_MULTI) {
+        return existing;
+    }
+
+    // move any existing entry into a psList
+    psList* newList = psListAlloc(existing);
+
+    psMetadataItem* item = psMetadataItemAlloc(key,
+                           PS_DATA_METADATA_MULTI,
+                           "",
+                           newList);
+
+    if (existing != NULL) {
+        psHashRemove(table,key); // take out the old entry
+    }
+
+    psHashAdd(table, key, item); // put in the new entry
+    psMemDecrRefCounter(item); // get rid of extra reference
+
+    // free local references of newly allocated items.
+    psFree(newList);
+
+    return item;
+}
+
+static void metadataItemFree(psMetadataItem* metadataItem)
+{
+    psDataType type;
+
+    type = metadataItem->type;
+
+
+    psMemDecrRefCounter(metadataItem->name);
+    psMemDecrRefCounter(metadataItem->comment);
+
+    if(!PS_DATA_IS_PRIMITIVE(type)) {
+        psFree(metadataItem->data.V);
+    }
+}
+
+static void metadataIteratorFree(psMetadataIterator* iter)
+{
+    psFree(iter->iter);
+
+    if (iter->regex != NULL) {
+        regfree(iter->regex);
+        psFree(iter->regex);
+    }
+}
+
+static void metadataFree(psMetadata* metadata)
+{
+    psFree(metadata->list);
+    psFree(metadata->hash);
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+psMetadataItem* p_psMetadataItemAlloc(const char *file,
+                                      unsigned int lineno,
+                                      const char *func,
+                                      const char *name,
+                                      psDataType type,
+                                      const char *comment,
+                                      ...)
+{
+    va_list argPtr;
+    psMetadataItem* metadataItem = NULL;
+
+    // Get the variable list parameters to pass to allocation function
+    va_start(argPtr, comment);
+
+    // Call metadata item allocation
+    metadataItem = p_psMetadataItemAllocV(file, lineno, func, name, type, comment, argPtr);
+
+    // Clean up stack after variable arguement has been used
+    va_end(argPtr);
+
+    return metadataItem;
+}
+
+bool psMemCheckMetadataItem(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)metadataItemFree );
+}
+
+
+#define METADATAITEM_ALLOC_TYPE(NAME,TYPE,METATYPE) \
+psMetadataItem* psMetadataItemAlloc##NAME(const char* name, \
+        const char* comment, \
+        TYPE value) \
+{ \
+    return psMetadataItemAlloc(name, METATYPE, comment, value); \
+}
+
+METADATAITEM_ALLOC_TYPE(Str,const char*,PS_DATA_STRING)
+METADATAITEM_ALLOC_TYPE(F32,psF32,PS_DATA_F32)
+METADATAITEM_ALLOC_TYPE(F64,psF64,PS_DATA_F64)
+METADATAITEM_ALLOC_TYPE(S8,psS8,PS_DATA_S8)
+METADATAITEM_ALLOC_TYPE(S16,psS16,PS_DATA_S16)
+METADATAITEM_ALLOC_TYPE(S32,psS32,PS_DATA_S32)
+METADATAITEM_ALLOC_TYPE(S64,psS64,PS_DATA_S64)
+METADATAITEM_ALLOC_TYPE(U8,psU8,PS_DATA_U8)
+METADATAITEM_ALLOC_TYPE(U16,psU16,PS_DATA_U16)
+METADATAITEM_ALLOC_TYPE(U32,psU32,PS_DATA_U32)
+METADATAITEM_ALLOC_TYPE(U64,psU64,PS_DATA_U64)
+METADATAITEM_ALLOC_TYPE(Bool,bool,PS_DATA_BOOL)
+
+psMetadataItem* psMetadataItemAllocPtr(const char *name,
+                                       psDataType type,
+                                       const char *comment,
+                                       psPtr value)
+{
+    return (psMetadataItemAlloc(name, type, comment, value));
+}
+
+
+psMetadataItem* p_psMetadataItemAllocV(const char *file,
+                                     unsigned int lineno,
+                                     const char *func,
+                                     const char *name,
+                                     psDataType type,
+                                     const char *comment,
+                                     va_list argPtr)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+    PS_ASSERT_INT_POSITIVE(type, NULL);
+
+    // Allocate metadata item
+    psMetadataItem *metadataItem = (psMetadataItem*) p_psAlloc(file, lineno, func, sizeof(psMetadataItem));
+    metadataItem->data.V = NULL;
+
+    // Set deallocator
+    psMemSetDeallocator(metadataItem, (psFreeFunc) metadataItemFree);
+
+    // XXX Does it make sense allocate a NULL string here?  It seems like this
+    // is just a waste of memory but something, somewhere, is probably
+    // dependant on this behavior so I'm leaving it in place for the time
+    // being. -JH
+
+    // Allocate and set metadata item comment
+    if (comment == NULL) {
+        // Per SDRS, null isn't allowed, must use "" instead
+        metadataItem->comment = p_psStringCopy(file, lineno, func, "");
+    } else {
+        metadataItem->comment = p_psStringCopy(file, lineno, func, comment);
+    }
+
+    // Set metadata item unique id
+    *(psS32 *)(&metadataItem->id) = ++metadataId;
+
+    // Set metadata item type
+    metadataItem->type = type & PS_METADATA_TYPE_MASK;
+
+    // Allocate and set metadata item name
+    metadataItem->name = p_psStringCopy(file, lineno, func, name);
+
+    // Set metadata item value
+    switch(metadataItem->type) {
+    case PS_DATA_BOOL:
+        metadataItem->data.B = (bool)va_arg(argPtr, psS32);
+        break;
+    case PS_DATA_S8:
+        metadataItem->data.S8 = (psS8)va_arg(argPtr, psS32);
+        break;
+    case PS_DATA_S16:
+        metadataItem->data.S16 = (psS16)va_arg(argPtr, psS32);
+        break;
+    case PS_DATA_S32:
+        metadataItem->data.S32 = (psS32)va_arg(argPtr, psS32);
+        break;
+    case PS_DATA_S64:
+        metadataItem->data.S64 = (psS64)va_arg(argPtr, psS64);
+        break;
+    case PS_DATA_U8:
+        metadataItem->data.U8 = (psU8)va_arg(argPtr, psU32);
+        break;
+    case PS_DATA_U16:
+        metadataItem->data.U16 = (psU16)va_arg(argPtr, psU32);
+        break;
+    case PS_DATA_U32:
+        metadataItem->data.U32 = (psU32)va_arg(argPtr, psU32);
+        break;
+    case PS_DATA_U64:
+        metadataItem->data.U64 = (psU64)va_arg(argPtr, psU64);
+        break;
+    case PS_DATA_F32:
+        metadataItem->data.F32 = (psF32)va_arg(argPtr, psF64);
+        break;
+    case PS_DATA_F64:
+        metadataItem->data.F64 = (psF64)va_arg(argPtr, psF64);
+        break;
+    case PS_DATA_STRING: {
+        // Copy input strings, so they can be messed with
+        char *string = va_arg(argPtr, char*);
+        metadataItem->data.str = (string ? p_psStringCopy(file, lineno, func, string) : NULL);
+        break;
+    }
+    case     PS_DATA_ARRAY:                     // psArray
+    case     PS_DATA_BITSET:                    // psBitSet
+    case     PS_DATA_CUBE:                      // psCube
+    case     PS_DATA_FITS:                      // psFits
+    case     PS_DATA_HASH:                      // psHash
+    case     PS_DATA_HISTOGRAM:                 // psHistogram
+    case     PS_DATA_IMAGE:                     // psImage
+    case     PS_DATA_KERNEL:                    // psKernel
+    case     PS_DATA_LIST:                      // psList
+    case     PS_DATA_LOOKUPTABLE:               // psLookupTable
+    case     PS_DATA_METADATA:                  // psMetadata
+    case     PS_DATA_METADATAITEM:              // psMetadataItem
+    case     PS_DATA_MINIMIZATION:              // psMinimization
+    case     PS_DATA_PIXELS:                    // psPixels
+    case     PS_DATA_PLANE:                     // psPlane
+    case     PS_DATA_PLANEDISTORT:              // psPlaneDistort
+    case     PS_DATA_PLANETRANSFORM:            // psPlaneTransform
+    case     PS_DATA_POLYNOMIAL1D:              // psPolynomial1D
+    case     PS_DATA_POLYNOMIAL2D:              // psPolynomial2D
+    case     PS_DATA_POLYNOMIAL3D:              // psPolynomial3D
+    case     PS_DATA_POLYNOMIAL4D:              // psPolynomial4D
+    case     PS_DATA_PROJECTION:                // psProjection
+    case     PS_DATA_REGION:                  // psRegion
+    case     PS_DATA_SCALAR:                    // psScalar
+    case     PS_DATA_SPHERE:                    // psSphere
+    case     PS_DATA_SPHEREROT:                 // psSphereTransform
+    case     PS_DATA_SPLINE1D:                  // psSpline1D
+    case     PS_DATA_STATS:                     // psStats
+    case     PS_DATA_TIME:                      // psTime
+    case     PS_DATA_VECTOR:                    // psVector
+    case     PS_DATA_UNKNOWN:                   // "other"
+    case     PS_DATA_METADATA_MULTI:
+        // Copy of input data not performed due to variability of data types
+        metadataItem->data.V = p_psMemIncrRefCounter(file, lineno, func, va_arg(argPtr, psPtr));
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Specified psDataType, %d, is not supported."), type);
+        psFree(metadataItem);
+        metadataItem = NULL;
+        break;
+    }
+
+    return metadataItem;
+}
+
+psMetadata* p_psMetadataAlloc(const char *file,
+                              unsigned int lineno,
+                              const char *func)
+{
+    psList* list = NULL;
+    psHash* hash = NULL;
+    psMetadata* metadata = NULL;
+
+    // Allocate metadata
+    metadata = (psMetadata*) p_psAlloc(file, lineno, func, sizeof(psMetadata));
+    // Set deallocator
+    psMemSetDeallocator(metadata, (psFreeFunc) metadataFree);
+
+    // Allocate metadata's internal containers
+    list = (psList*) psListAlloc(NULL);
+    hash = (psHash*) psHashAlloc(10);
+
+    metadata->list = list;
+    metadata->hash = hash;
+
+    return metadata;
+}
+
+
+bool psMemCheckMetadata(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)metadataFree );
+}
+
+
+psMetadataItem *p_psMetadataItemCopy(const char *file,
+                                     unsigned int lineno,
+                                     const char *func,
+                                     const psMetadataItem *in)
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(in,NULL);
+
+    psMetadataItem *newItem = NULL;     // New metadata item, to be returned
+
+    #define PS_METADATA_ITEM_COPY_CASE(NAME,TYPE) \
+case PS_DATA_##NAME: \
+    newItem = p_psMetadataItemAlloc(file, lineno, func, in->name, PS_DATA_##NAME, in->comment, in->data.TYPE); \
+    break; \
+
+    switch (in->type) {
+        // Simple types
+        PS_METADATA_ITEM_COPY_CASE(BOOL,B);
+        PS_METADATA_ITEM_COPY_CASE(S8,S8);
+        PS_METADATA_ITEM_COPY_CASE(S16,S16);
+        PS_METADATA_ITEM_COPY_CASE(S32,S32);
+        PS_METADATA_ITEM_COPY_CASE(S64,S64);
+        PS_METADATA_ITEM_COPY_CASE(U8,U8);
+        PS_METADATA_ITEM_COPY_CASE(U16,U16);
+        PS_METADATA_ITEM_COPY_CASE(U32,U32);
+        PS_METADATA_ITEM_COPY_CASE(U64,U64);
+        PS_METADATA_ITEM_COPY_CASE(F32,F32);
+        PS_METADATA_ITEM_COPY_CASE(F64,F64);
+        PS_METADATA_ITEM_COPY_CASE(STRING,V); // This will copy the string, not point at it.
+    case PS_DATA_VECTOR: {
+            PS_ASSERT_PTR_NON_NULL(in->data.V, NULL);
+            psVector *vecCopy = psVectorCopy(NULL, (psVector*)(in->data.V),
+                                             ((psVector*)(in->data.V))->type.type);
+            newItem = p_psMetadataItemAlloc(file, lineno, func, in->name, PS_DATA_VECTOR, in->comment, vecCopy);
+            psFree(vecCopy);    // Drop reference
+            break;
+        }
+    case PS_DATA_TIME: {
+            psTime *timeCopy = NULL;
+            // pass through NULL values
+            if (in->data.V) {
+                timeCopy = psTimeCopy( (psTime*)(in->data.V) );
+                if (timeCopy == NULL) {
+                    psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                            "Error copying time.  Time skipped.\n");
+                }
+            }
+            newItem = p_psMetadataItemAlloc(file, lineno, func, in->name, PS_DATA_TIME, in->comment, timeCopy);
+            if (timeCopy) {
+                psFree(timeCopy);   // Drop reference
+            }
+            break;
+        }
+    case PS_DATA_METADATA: {
+            // Metadata: copy the next level and stuff that in too
+            psMetadata *metadata = psMetadataCopy(NULL, in->data.md);
+            newItem = p_psMetadataItemAlloc(file, lineno, func, in->name, PS_DATA_METADATA, in->comment, metadata);
+            psFree(metadata);       // Drop reference
+            break;
+        }
+    case PS_DATA_REGION: {
+            psRegion *region = in->data.V; // The region
+            psRegion *new = psRegionAlloc(region->x0, region->x1, region->y0, region->y1); // Copy of the region
+            newItem = p_psMetadataItemAlloc(file, lineno, func, in->name, PS_DATA_REGION, in->comment, new);
+            psFree(new);                  // Drop reference
+            break;
+        }
+    default:
+        // Other kinds of pointers
+      // XXX EAM : why was this a warning??
+      // psWarning("Copying a pointer in the metadata item: %s (%x)\n", in->name, in->type);
+        newItem = p_psMetadataItemAlloc(file, lineno, func, in->name, in->type, in->comment, in->data.V);
+        break;
+    }
+
+    return newItem;
+}
+
+psMetadata *p_psMetadataCopy(const char *file,
+                             unsigned int lineno,
+                             const char *func,
+                             psMetadata *out,
+                             const psMetadata *in)
+{
+    PS_ASSERT_METADATA_NON_NULL(in, NULL);
+
+    bool errorOccurred = false;
+    bool outAlloced = false;
+    if (out ==  NULL) {
+        out = p_psMetadataAlloc(file, lineno, func);
+        outAlloced = true;
+    }
+    psMetadataIterator *iter = psMetadataIteratorAlloc(in, PS_LIST_HEAD, NULL);
+    psMetadataItem *inItem = NULL;
+    while ((inItem = psMetadataGetAndIncrement(iter))) {
+        // Need to look for MULTI, which won't be picked up using the iterator.
+        psMetadataItem *multiCheckItem = psMetadataLookup(in, inItem->name);
+        unsigned int flag = PS_META_REPLACE; // Flag to indicate MULTI; otherwise, replace
+        if (multiCheckItem->type == PS_DATA_METADATA_MULTI) {
+            psTrace("psLib.types", 10, "MULTI: %s (%s)\n", inItem->name, inItem->comment);
+            flag = PS_META_DUPLICATE_OK;
+        }
+        psTrace("psLib.types", 5, "Copying %s (%s)...\n", inItem->name, inItem->comment);
+
+        // Copy the item and add it on
+        psMetadataItem *newItem = psMetadataItemCopy(inItem); // Copied item
+        if (!psMetadataAddItem(out, newItem, PS_LIST_TAIL, flag)) {
+            psError(PS_ERR_UNKNOWN, false, "Error copying %s (%s) in the metadata\n",
+                    inItem->name, inItem->comment);
+            if (outAlloced) {
+                psFree(out);
+                psFree(newItem);
+                psFree(iter);
+                return NULL;
+            } else {
+                errorOccurred = true;
+            }
+        }
+        psFree(newItem);                // Drop reference
+    }
+    psFree(iter);
+
+    if (errorOccurred) {
+        return NULL;
+    }
+
+    return out;
+}
+
+// this function copies the input metadata to the output, raising an error
+// if any entries in 'in' a) do not exist in 'out' or b) have a different type
+bool p_psMetadataUpdate(const char *file,
+                        unsigned int lineno,
+                        const char *func,
+                        psMetadata *out,
+                        const psMetadata *in)
+{
+    PS_ASSERT_METADATA_NON_NULL(in, NULL);
+    PS_ASSERT_METADATA_NON_NULL(out, NULL);
+
+    bool result = true;
+
+    // we loop over the metadata container by item, not by name.  this pushes us directly into
+    // the elements of the MULTI items; we miss the containers.  We need to check the
+    // (possible) MULTI containers to see how to set the flags.  If we encounter a MULTI which
+    // is requesting REPLACE, then the first item on the input list from that MULTI should be
+    // added with the REPLACE flag turned on; the rest should have the REPLACE flag turned off.
+    // save a list of the MULTI entries which has REPLACE turned off?
+
+    psArray *multiItems = psArrayAllocEmpty (128);
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(in, PS_LIST_HEAD, NULL);
+    psMetadataItem *inItem = NULL;
+    while ((inItem = psMetadataGetAndIncrement(iter))) {
+
+        // it is not possible to have RESET && UPDATE
+        // is it possible to have !RESET && !UPDATE?
+        // are these mutually exclusive conditions?
+        // input MULTI & RESET  : replace an existing MULTI or ITEM of same name
+        // input MULTI & UPDATE : supplement an existing MULTI or ITEM of same name
+        // in both cases the name must exist in 'out'
+
+        // Need to look for MULTI, which won't be picked up using the iterator.
+        psMetadataItem *multiCheckItem = psMetadataLookup(in, inItem->name);
+
+        // default operation for the new metadata item (replace existing entry, require existence & type)
+        unsigned int flag = PS_META_REPLACE | PS_META_REQUIRE_ENTRY | PS_META_REQUIRE_TYPE;
+
+        // for MULTI items, the mode is carried on the multi container
+        if (PS_METADATA_ITEM_GET_TYPE(multiCheckItem) == PS_DATA_METADATA_MULTI) {
+            psTrace("psLib.types", 10, "MULTI: %s (%s)\n", inItem->name, inItem->comment);
+            if (multiCheckItem->type & PS_META_UPDATE_FOLDER) {
+                psTrace("psLib.types", 10, "supplement MULTI with new entries\n");
+                flag = PS_META_DUPLICATE_OK | PS_META_REQUIRE_ENTRY | PS_META_REQUIRE_TYPE;
+            } else {
+                multiCheckItem->type |= PS_META_UPDATE_FOLDER;
+                // save this multi so we can reset their flags below
+                psArrayAdd (multiItems, 128, multiCheckItem);
+            }
+        }
+        psTrace("psLib.types", 5, "Copying %s (%s)...\n", inItem->name, inItem->comment);
+
+        // for METADATA folders, do something different?
+        if (PS_METADATA_ITEM_GET_TYPE(inItem) == PS_DATA_METADATA) {
+            if (inItem->type & PS_META_UPDATE_FOLDER) {
+                flag = PS_META_UPDATE_FOLDER | PS_META_REQUIRE_ENTRY | PS_META_REQUIRE_TYPE;
+            }
+        }
+
+        // Copy the item and add it on.  report all errors so we get a listing
+        psMetadataItem *newItem = psMetadataItemCopy(inItem); // Copied item
+        if (!psMetadataAddItem(out, newItem, PS_LIST_TAIL, flag)) {
+            psError(PS_ERR_UNKNOWN, false, "Error copying %s\n", inItem->name);
+            result = false;
+        }
+        psFree(newItem);                // Drop reference
+    }
+    psFree(iter);
+
+    // remove the UPDATE_FOLDER flag from the following MULTI items
+    for (int i = 0; i < multiItems->n; i++) {
+        psMetadataItem *item = multiItems->data[i];
+        item->type &= ~PS_META_UPDATE_FOLDER;
+    }
+    psFree (multiItems);
+
+    if (!result) {
+        psError(PS_ERR_UNKNOWN, false, "failed to update metadata\n");
+    }
+
+    return result;
+}
+
+// this function copies the input metadata to the output, supplementing existing metadata
+// folders with the contents from corresponding folders in the output
+bool p_psMetadataOverlay(const char *file,
+                         unsigned int lineno,
+                         const char *func,
+                         psMetadata *out,
+                         const psMetadata *in)
+{
+    PS_ASSERT_METADATA_NON_NULL(in, NULL);
+    PS_ASSERT_METADATA_NON_NULL(out, NULL);
+
+    bool result = true;
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(in, PS_LIST_HEAD, NULL);
+    psMetadataItem *inItem = NULL;
+    while ((inItem = psMetadataGetAndIncrement(iter))) {
+        // Need to look for MULTI, which won't be picked up using the iterator.
+        psMetadataItem *multiCheckItem = psMetadataLookup(in, inItem->name);
+        unsigned int flag = PS_META_REPLACE; // Flag to indicate MULTI; otherwise, replace
+        if (multiCheckItem->type == PS_DATA_METADATA_MULTI) {
+            psTrace("psLib.types", 10, "MULTI: %s (%s)\n", inItem->name, inItem->comment);
+            flag = PS_META_DUPLICATE_OK;
+        }
+        psTrace("psLib.types", 5, "Copying %s (%s)...\n", inItem->name, inItem->comment);
+
+        // if this is a metadata, and it has a corresponding match, overlay them
+        if (inItem->type == PS_DATA_METADATA) {
+            bool status;
+            psMetadata *outFolder = psMetadataLookupMetadata (&status, out, inItem->name);
+            if (outFolder) {
+                if (!psMetadataOverlay (outFolder, inItem->data.md)) {
+                    fprintf (stderr, "Error overlaying metadata folder\n");
+                    result = false;
+                }
+                continue;
+            }
+        }
+
+        // Copy the item and add it on.  report all errors so we get a listing
+        psMetadataItem *newItem = psMetadataItemCopy(inItem); // Copied item
+        if (!psMetadataAddItem(out, newItem, PS_LIST_TAIL, flag)) {
+            fprintf (stderr, "Error copying %s\n", inItem->name);
+            result = false;
+        }
+        psFree(newItem);                // Drop reference
+    }
+    psFree(iter);
+
+    if (!result) {
+        psError(PS_ERR_UNKNOWN, false, "failed to update metadata\n");
+    }
+    return result;
+}
+
+// may need to extend this to change the keyname in the copy
+bool psMetadataItemSupplement(psMetadata *out,
+                              const psMetadata *in,
+                              const char *key)
+{
+    PS_ASSERT_METADATA_NON_NULL(in, false);
+    PS_ASSERT_METADATA_NON_NULL(out, false);
+    PS_ASSERT_STRING_NON_EMPTY(key, false);
+
+    psMetadataItem *item = psMetadataLookup(in, key);
+    if (!item) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Could not find '%s' in metadata.\n", key);
+        return false;
+    }
+    if (!psMetadataAddItem(out, item, PS_LIST_TAIL, PS_META_REPLACE) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Could not add %s to metadata.\n", key);
+        return false;
+    }
+
+    return true;
+}
+
+// XXX is it sensible that item is a 'const' here?
+bool psMetadataAddItem(psMetadata *md,
+                       const psMetadataItem *item,
+                       int location,
+                       psS32 flags)
+{
+    PS_ASSERT_METADATA_NON_NULL(md,false);
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item,false);
+
+    psHash *mdTable = md->hash;
+    psList *mdList = md->list;
+    char *key = item->name;
+
+    // See if key is already in table
+    psMetadataItem *existingEntry = psHashLookup(mdTable, key);
+
+    // this block handles cases for the MULTI items
+    if (item->type == PS_DATA_METADATA_MULTI) {
+        // the incoming entry is PS_DATA_METADATA_MULTI
+
+        // Shouldn't have a second reference to the same MULTI in a single Metadata!
+        // XXX not sure I understand this case
+        if (item == existingEntry) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot have 2 references to the same MULTI in a single Metadata!");
+            return false;
+        }
+
+        if (flags & PS_META_REPLACE) {
+            // drop the existing entry or entries
+            psMetadataRemoveKey(md, key);
+        } else {
+            // elevate the existing hash entry to be PS_DATA_METADATA_MULTI
+            existingEntry = makeMetaMulti(mdTable,key,existingEntry);
+        }
+
+        // add all the items in the incoming entry to metadata
+        psList* list = item->data.list;
+        if (list != NULL) {
+            psListIterator* iter = psListIteratorAlloc(list,PS_LIST_HEAD,true);
+            psMetadataItem* listItem;
+            while ((listItem=(psMetadataItem*)psListGetAndIncrement(iter)) != NULL) {
+                psMetadataAddItem(md,listItem,location,flags);
+            }
+            psFree(iter);
+        }
+
+        return true; // all done.
+    }
+
+    // special block for items which are METADATA folders
+    if (existingEntry && (item->type == PS_DATA_METADATA)) {
+        // REPLACE and UPDATE must be mutually exclusive
+        psAssert ((!((flags & PS_META_REPLACE) && (flags & PS_META_UPDATE_FOLDER))), "cannot have both REPLACE and UPDATE");
+
+        # if (0)
+        // handle the case of replace below
+        if (flags & PS_META_REPLACE) {
+            // drop the existing entry (skip if we are replacing with same pointer)
+            // XXX what if existingEntry is a MULTI?  drop all?
+            // XXX this segment does not check for matched types
+            if (item != existingEntry) {
+                psMetadataRemoveKey(md, key);
+            }
+            existingEntry = NULL;
+        }
+        # endif
+
+        if (flags & PS_META_UPDATE_FOLDER) {
+            if (existingEntry->type != PS_DATA_METADATA) {
+                psError(PS_ERR_UNKNOWN, false, "invalid to request UPDATE for metadata which matches another type");
+                return false;
+            }
+
+            // merge the existing entry : this completes the insert
+            if (!psMetadataCopy ((psMetadata *)existingEntry->data.V, (psMetadata *)item->data.V)) {
+                psError(PS_ERR_UNKNOWN, false, "failed to copy new metadata on existing");
+                return false;
+            }
+            return true;
+        }
+    }
+
+    // how the item is added to the hash depends on prior existence, flags, etc.
+    // XXX i think these cases are overloaded - are all combinations possible?
+    if (existingEntry) { // prior existence
+
+        // duplicate entries allowed - add another entry.
+        if ((existingEntry->type == PS_DATA_METADATA_MULTI) || (flags & PS_META_DUPLICATE_OK)) {
+
+            // make sure the existing entry is PS_DATA_METADATA_MULTI
+            existingEntry = makeMetaMulti(mdTable,key,existingEntry);
+
+            // add item to the existing hash's list of duplicate entries
+            if (!psListAdd(existingEntry->data.list, PS_LIST_TAIL, (psMetadataItem *) item) ) {
+                psError(PS_ERR_UNKNOWN, false, _("Failed to add metadata item, %s, to metadata collection list."), key);
+                return false;
+            }
+            // add to the metadata list of entries
+            if (!psListAdd(mdList, location, (psMetadataItem *) item)) {
+                psError(PS_ERR_UNKNOWN, false, _("Failed to add metadata item, %s, to metadata collection list."), key);
+                return false;
+            }
+            return true;
+        }
+
+        // replace entry instead of creating a duplicate entry.
+        if (flags & PS_META_REPLACE) {
+
+            if ((flags & PS_META_REQUIRE_TYPE) && (existingEntry->type != item->type)) {
+                psError (PS_ERR_UNKNOWN, true, _("Existing item %s does not match type (%x) for item requiring matching type (%x)"), key, existingEntry->type, item->type);
+                return false;
+            }
+            if (item != existingEntry) {
+                // Replacing an item with itself can lead to memory corruption
+                // when you blow away what you're trying to add
+                psMetadataRemoveKey(md, key);
+                // add to the metadata has of entries
+                if (!psHashAdd(mdTable, key, (psMetadataItem *) item)) {
+                    psError(PS_ERR_UNKNOWN, false, _("Failed to add metadata item, %s, to metadata collection list."), key);
+                    return false;
+                }
+                // add to the metadata list of entries
+                if (!psListAdd(mdList, location, (psMetadataItem *) item)) {
+                    psError(PS_ERR_UNKNOWN, false, _("Failed to add metadata item, %s, to metadata collection list."), key);
+                    return false;
+                }
+                return true;
+            } else {
+              // adding myself back to the list; this is a NOP
+              return true;
+            }
+        }
+
+        // if specified, keep the existing entry
+        if (flags & PS_META_NO_REPLACE) {
+            return true;
+        }
+
+        // default is to error on duplicate entry.
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Duplicate metadata item name: %s is not allowed.  Use a psMetadataFlags option to allow such action."), item->name);
+        return false;
+    }
+
+    // OK, this is a new item.
+    if (flags & PS_META_REQUIRE_ENTRY) {
+        psError (PS_ERR_UNKNOWN, true, _("No matching item found for item requiring existing entry (%s)"), key);
+        return false;
+    }
+
+    // Node doesn't exist - Add new metadata item to metadata collection's hash
+    psHashAdd(mdTable, key, (psMetadataItem *) item);
+
+    // Create a multi, if requested
+    if (flags & PS_META_DUPLICATE_OK) {
+        makeMetaMulti(mdTable, key, (psMetadataItem *) item); // Casting away const!
+    }
+
+    if (!psListAdd(mdList, location, (psPtr)item)) {
+        psError(PS_ERR_UNKNOWN, false, _("Failed to add metadata item, %s, to metadata collection list."), key);
+        return false;
+    }
+
+    return true;
+}
+
+bool psMetadataAdd(psMetadata *md,
+                   long location,
+                   const char *name,
+                   int format,
+                   const char *comment,...)
+{
+    PS_ASSERT_METADATA_NON_NULL(md, false);
+
+    va_list argPtr;
+    va_start(argPtr, comment);
+    bool result = psMetadataAddV(md,location,name,format,comment,argPtr);
+    va_end(argPtr);
+
+    return result;
+}
+
+bool psMetadataAddV(psMetadata *md,
+                    long location,
+                    const char *name,
+                    int format,
+                    const char *comment,
+                    va_list list)
+{
+    PS_ASSERT_METADATA_NON_NULL(md,false);
+
+    psMetadataItem* metadataItem = psMetadataItemAllocV(name, format & PS_METADATA_TYPE_MASK, comment, list);
+
+    if (!psMetadataAddItem(md, metadataItem, location, format & PS_METADATA_FLAGS_MASK)) {
+        psError(PS_ERR_UNKNOWN,false,_("Failed to add metadata item to metadata collection list."));
+        psFree(metadataItem);
+        return false;
+    }
+    // Decrement reference count, since the metadata item is now in metadata collection and no longer needed
+    psFree(metadataItem);
+
+    return true;
+}
+
+#define METADATA_ADD_TYPE(NAME,TYPE,METATYPE) \
+bool psMetadataAdd##NAME(psMetadata* md, long where, const char* name, \
+                         int format, const char* comment, TYPE value) { \
+    return psMetadataAdd(md, where, name, format | METATYPE, comment, value); \
+}
+
+METADATA_ADD_TYPE(Bool,bool,PS_DATA_BOOL)
+METADATA_ADD_TYPE(S8,psS8,PS_DATA_S8)
+METADATA_ADD_TYPE(S16,psS16,PS_DATA_S16)
+METADATA_ADD_TYPE(S32,psS32,PS_DATA_S32)
+METADATA_ADD_TYPE(S64,psS64,PS_DATA_S64)
+METADATA_ADD_TYPE(U8,psU8,PS_DATA_U8)
+METADATA_ADD_TYPE(U16,psU16,PS_DATA_U16)
+METADATA_ADD_TYPE(U32,psU32,PS_DATA_U32)
+METADATA_ADD_TYPE(U64,psU64,PS_DATA_U64)
+METADATA_ADD_TYPE(F32,psF32,PS_DATA_F32)
+METADATA_ADD_TYPE(F64,psF64,PS_DATA_F64)
+METADATA_ADD_TYPE(List,psList*,PS_DATA_LIST)
+METADATA_ADD_TYPE(Str,const char*,PS_DATA_STRING)
+METADATA_ADD_TYPE(Vector,psVector*,PS_DATA_VECTOR)
+METADATA_ADD_TYPE(Image,psImage*,PS_DATA_IMAGE)
+METADATA_ADD_TYPE(Hash,psHash*,PS_DATA_HASH)
+METADATA_ADD_TYPE(LookupTable,psLookupTable*,PS_DATA_LOOKUPTABLE)
+METADATA_ADD_TYPE(Metadata,psMetadata*,PS_DATA_METADATA)
+METADATA_ADD_TYPE(Array,psArray*,PS_DATA_ARRAY)
+METADATA_ADD_TYPE(Time,psTime*,PS_DATA_TIME)
+METADATA_ADD_TYPE(Unknown,psPtr,PS_DATA_UNKNOWN)
+
+bool psMetadataAddPtr(psMetadata* md, long where, const char* name,
+                      psDataType type, const char* comment, psPtr value)
+{
+    return psMetadataAdd(md, where, name, type, comment, value);
+}
+
+
+
+// Remove by key name
+bool psMetadataRemoveKey(psMetadata *md,
+                         const char *key)
+{
+    PS_ASSERT_METADATA_NON_NULL(md, false);
+    PS_ASSERT_STRING_NON_EMPTY(key, false);
+
+    psList* mdList = md->list;
+    psHash* mdTable = md->hash;
+
+    psMetadataItem* entry = psHashLookup(mdTable,key);
+    if (entry == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                _("Failed to remove metadata item, %s, from metadata table."), key);
+        return false;
+    }
+    if (entry->type == PS_DATA_METADATA_MULTI) {
+        psMetadataItem* listItem;
+        psListIterator* iter = psListIteratorAlloc(
+                                   entry->data.list,
+                                   PS_LIST_HEAD,true);
+        while ((listItem=psListGetAndIncrement(iter)) != NULL) {
+            psListRemoveData(mdList, listItem);
+        }
+        psFree(iter);
+        psHashRemove(mdTable,key);
+    } else {
+        psListRemoveData(mdList, entry);
+        psHashRemove(mdTable, key);
+    }
+
+    return true;
+}
+
+
+// Remove by index
+bool psMetadataRemoveIndex(psMetadata *md,
+                           long location)
+{
+    PS_ASSERT_METADATA_NON_NULL(md, false);
+
+    psList* mdList = md->list;
+    psHash* mdTable = md->hash;
+
+    psMetadataItem* entry = psListGet(mdList, location);
+    if (entry == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Could not find metadata item at index %ld."), location);
+        return false;
+    }
+    const char *key = entry->name;
+
+    if (key == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Failed to remove metadata item, at index %ld, from metadata list."),
+                location);
+        return false;
+    }
+
+    psMetadataItem* tableItem = psHashLookup(mdTable, key);
+    if (tableItem == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                _("Failed to remove metadata item, %s, from metadata table."), key);
+        return false;
+    }
+
+    if (tableItem->type == PS_DATA_METADATA_MULTI) {
+        // multiple entries with same key, remove just the specified one
+        psListRemoveData(tableItem->data.list, entry);
+    } else {
+        //Tested below.  psHashRemove can't return false here.
+        psHashRemove(mdTable, key);
+    }
+    psListRemove(mdList, location);
+
+    return true;
+}
+
+psMetadataItem* psMetadataLookup(const psMetadata *md,
+                                 const char *key)
+{
+    PS_ASSERT_METADATA_NON_NULL(md,NULL);
+    PS_ASSERT_STRING_NON_EMPTY(key,NULL);
+
+    return (psMetadataItem*)psHashLookup(md->hash, key);
+}
+
+void* psMetadataLookupPtr(bool *status,
+                          const psMetadata *md,
+                          const char *key)
+{
+    PS_ASSERT_METADATA_NON_NULL(md,NULL);
+
+    if (status) {
+        *status = true;
+    }
+
+    psMetadataItem *metadataItem = psMetadataLookup(md, key);
+    if (metadataItem == NULL) {
+        if (status) {
+            *status = false;
+        }
+        return NULL;
+    }
+    if (metadataItem->type == PS_DATA_METADATA_MULTI) {
+        // if multiple keys found, use the first.
+        //        metadataItem = (psMetadataItem*)((metadataItem->data.list)->head);
+        metadataItem = (psMetadataItem*)(metadataItem->data.list->head->data);
+        if (status) {
+            *status = true;
+        }
+    }
+
+    if(PS_DATA_IS_PRIMITIVE(metadataItem->type)) {
+        if (status) {
+            *status = false;
+        }
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified psDataType, %d, is not supported."),
+                metadataItem->type);
+        return NULL;
+    } else {
+        return metadataItem->data.V;
+    }
+}
+
+#define psMetadataLookupNumTYPE(TYPE) \
+ps##TYPE psMetadataLookup##TYPE(bool *status, const psMetadata *md, const char *key) \
+{ \
+    psMetadataItem *metadataItem = NULL; \
+    ps##TYPE value = 0; \
+    \
+    if (status) {  \
+        *status = true; \
+    } \
+    \
+    metadataItem = psMetadataLookup(md, key); \
+    if(metadataItem == NULL) { \
+        if (status) {  \
+            *status = false; \
+        } \
+        return 0; \
+    } \
+    if (metadataItem->type == PS_DATA_METADATA_MULTI) { \
+        /* if multiple keys found, use the first. */ \
+        metadataItem = (psMetadataItem*)((metadataItem->data.list)->head); \
+    } \
+    \
+    switch (metadataItem->type) { \
+    case PS_DATA_S8: \
+        value = (ps##TYPE)metadataItem->data.S8; \
+        break; \
+    case PS_DATA_S16: \
+        value = (ps##TYPE)metadataItem->data.S16; \
+        break; \
+    case PS_DATA_S32: \
+        value = (ps##TYPE)metadataItem->data.S32; \
+        break; \
+    case PS_DATA_S64: \
+        value = (ps##TYPE)metadataItem->data.S64; \
+        break; \
+    case PS_DATA_U8: \
+        value = (ps##TYPE)metadataItem->data.U8; \
+        break; \
+    case PS_DATA_U16: \
+        value = (ps##TYPE)metadataItem->data.U16; \
+        break; \
+    case PS_DATA_U32: \
+        value = (ps##TYPE)metadataItem->data.U32; \
+        break; \
+    case PS_DATA_U64: \
+        value = (ps##TYPE)metadataItem->data.U64; \
+        break; \
+    case PS_DATA_F32: \
+        value = (ps##TYPE)metadataItem->data.F32; \
+        break; \
+    case PS_DATA_F64: \
+        value = (ps##TYPE)metadataItem->data.F64; \
+        break; \
+    case PS_DATA_BOOL: \
+        if (metadataItem->data.B) { \
+            value = 1; \
+        } \
+        break; \
+    default: \
+        /* if you get to this point, the value is not a number. */ \
+        if (status) {  \
+            *status = false; \
+        }  \
+        break; \
+    } \
+    \
+    /* psFree(metadataItem); currently, the lookup doesn't increment the ref count */ \
+    return value; \
+}
+
+psMetadataLookupNumTYPE(F32)
+psMetadataLookupNumTYPE(F64)
+psMetadataLookupNumTYPE(S8)
+psMetadataLookupNumTYPE(S16)
+psMetadataLookupNumTYPE(S32)
+psMetadataLookupNumTYPE(S64)
+psMetadataLookupNumTYPE(U8)
+psMetadataLookupNumTYPE(U16)
+psMetadataLookupNumTYPE(U32)
+psMetadataLookupNumTYPE(U64)
+psMetadataLookupNumTYPE(Bool)
+
+psMetadataItem* psMetadataGet(const psMetadata *md,
+                              int location)
+{
+    psMetadataItem* entry = NULL;
+
+    PS_ASSERT_METADATA_NON_NULL(md,NULL);
+
+    entry = (psMetadataItem*) psListGet(md->list, location);
+    if (entry == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Could not find metadata item at index %d."), location);
+        return NULL;
+    }
+
+    return entry;
+}
+
+// XXX should md be const?
+psMetadataIterator* p_psMetadataIteratorAlloc(const char *file,
+                                              unsigned int lineno,
+                                              const char *func,
+                                              const psMetadata* md,
+                                              long location,
+                                              const char* regex)
+{
+    PS_ASSERT_METADATA_NON_NULL(md,NULL);
+
+    psMetadataIterator* newIter = p_psAlloc(file, lineno, func, sizeof(psMetadataIterator));
+    psMemSetDeallocator(newIter, (psFreeFunc) metadataIteratorFree);
+
+    newIter->iter = psListIteratorAlloc(md->list, location, false);
+
+    if (regex) {
+        newIter->regex = psAlloc(sizeof(regex_t));
+        int regRtn = regcomp(newIter->regex, regex, 0);
+        if (regRtn != 0) {
+            char errMsg[256];
+            regerror(regRtn, newIter->regex, errMsg, 256);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Specified regular expression is invalid.  %s."),
+                    errMsg);
+            psFree(newIter);
+            return NULL;
+        }
+        if (!psMetadataIteratorSet(newIter, location)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to set location=%ld for metadata iterator.\n", location);
+            psFree(newIter);
+            return NULL;
+        }
+    } else {
+        newIter->regex = NULL;
+    }
+
+    return newIter;
+}
+
+bool psMetadataIteratorSet(psMetadataIterator* iterator,
+                           long location)
+{
+    PS_ASSERT_METADATA_ITERATOR_NON_NULL(iterator, NULL);
+    psListIterator *listIter = iterator->iter;
+
+    regex_t *regex = iterator->regex;
+
+    // handle trivial case where no regex subsetting is required.
+    if (regex == NULL) {
+        return psListIteratorSet(listIter, location);
+    }
+
+    // If there's a regex, then we need to count into the list.
+    // We count ONLY those entries that match the regex
+
+    // Here we count in from the tail
+    if (location < 0) {
+        psListIteratorSet(listIter, PS_LIST_TAIL);
+        psMetadataItem *item;           // Item from iteration
+        int match = 0;                  // Match number
+        while (listIter->cursor && (item = listIter->cursor->data)) {
+            if (regexec(regex, item->name, 0, NULL, 0) == 0) {
+                // this key is a match
+                match--;
+                if (match == location) {
+                    break;
+                }
+            }
+            (void)psListGetAndDecrement(listIter);
+        }
+
+        // We return "true" even if we didn't find the Nth match, because the iterator is set appropriately
+        // (off the front), and subsequent calls to psMetadataGetAnd{In,De}crement will return NULL.
+        return true;
+    }
+
+    // Here we count in from the head
+    psListIteratorSet(listIter, PS_LIST_HEAD);
+    psMetadataItem *item;               // Item from iteration
+    int match = -1;                     // Match number
+    while (listIter->cursor && (item = listIter->cursor->data)) {
+        if (regexec(regex, item->name, 0, NULL, 0) == 0) {
+            // this key is a match
+            match++;
+            if (match == location) {
+                break;
+            }
+        }
+        (void)psListGetAndIncrement(listIter);
+    }
+    // We return "true" even if we didn't find the Nth match, because the iterator is set appropriately
+    // (off the end), and subsequent calls to psMetadataGetAnd{In,De}crement will return NULL.
+    return true;
+}
+
+psMetadataItem* psMetadataGetAndIncrement(psMetadataIterator* iterator)
+{
+    PS_ASSERT_METADATA_ITERATOR_NON_NULL(iterator,NULL);
+    psListIterator* listIter = iterator->iter;
+    regex_t* regex = iterator->regex;
+
+    // handle trivial case where no regex subsetting is required.
+    if (regex == NULL) {
+        return (psMetadataItem*)psListGetAndIncrement(listIter);
+    }
+
+    // Iterate until we find something matching the regex
+    psMetadataItem *newItem;            // New MD item from iteration
+    while ((newItem = psListGetAndIncrement(listIter))) {
+        if (regexec(regex, newItem->name, 0, NULL, 0) == 0) {
+            // this key is a match
+            break;
+        }
+    }
+    return newItem;
+}
+
+psMetadataItem* psMetadataGetAndDecrement(psMetadataIterator* iterator)
+{
+    PS_ASSERT_METADATA_ITERATOR_NON_NULL(iterator, NULL);
+    psListIterator* listIter = iterator->iter;
+    regex_t* regex = iterator->regex;
+
+    // handle trivial case where no regex subsetting is required.
+    if (regex == NULL) {
+        return (psMetadataItem*)psListGetAndDecrement(listIter);
+    }
+
+    // Iterate until we find something matching the regex
+    psMetadataItem *newItem;            // New MD item from iteration
+    while ((newItem = psListGetAndDecrement(listIter))) {
+        if (regexec(regex, newItem->name, 0, NULL, 0) == 0) {
+            // this key is a match
+            break;
+        }
+    }
+    return newItem;
+}
+
+psMetadata *psMetadataLookupMetadata(bool *status,
+                                     const psMetadata *md,
+                                     const char *key)
+{
+    PS_ASSERT_METADATA_NON_NULL(md,NULL);
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    psMetadata *value = NULL;  // The value to return
+    if (!item) {
+        // The given key isn't in the metadata
+        if (status) {
+            *status = false;
+        } else {
+            psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n", key);
+        }
+    } else if (item->type != PS_DATA_METADATA) {
+        // The value at the key isn't metadata
+        if (status) {
+            *status = false;
+        } else {
+            psLogMsg(__func__, PS_LOG_DETAIL, "%s isn't of type PS_DATA_META, as expected.\n", key);
+        }
+        //        value = NULL;
+    } else {
+        // We have the requested metadata
+        if (status) {
+            *status = true;
+        }
+        value = item->data.md; // The requested metadata
+    }
+    return value;
+}
+
+psTime *psMetadataLookupTime(bool *status,
+                             const psMetadata *md,
+                             const char *key)
+{
+    PS_ASSERT_METADATA_NON_NULL(md,NULL);
+
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key);
+    if (!item) {
+        // The given key isn't in the metadata
+        if (status) {
+            *status = false;
+        } else {
+            psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n", key);
+        }
+        return NULL;
+    }
+
+    if (item->type != PS_DATA_TIME) {
+        // The value at the key isn't metadata
+        if (status) {
+            *status = false;
+        } else {
+            psLogMsg(__func__, PS_LOG_DETAIL, "%s isn't of type PS_DATA_TIME, as expected.\n", key);
+        }
+        return NULL;
+    }
+
+    // We have the requested metadata
+    if (status) {
+        *status = true;
+    }
+
+    return item->data.V;
+}
+
+
+psString psMetadataLookupStr(bool *status,
+                             const psMetadata *md,
+                             const char *key)
+{
+    PS_ASSERT_METADATA_NON_NULL(md,NULL);
+
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    //    char *value = NULL;   // The value to return
+    psString value = NULL;
+    if (!item) {
+        // The given key isn't in the metadata
+        if (status) {
+            *status = false;
+        } else {
+            psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n", key);
+        }
+    } else if (item->type != PS_DATA_STRING) {
+        // The value at the key isn't of the desired type
+        if (status) {
+            *status = false;
+        } else {
+            psLogMsg(__func__, PS_LOG_DETAIL, "%s isn't of type PS_DATA_STRING, as expected.\n", key);
+        }
+        //        value = NULL;
+    } else {
+        // We have the requested metadata
+        if (status) {
+            *status = true;
+        }
+        value = item->data.V;
+    }
+    return value;
+}
+
+psList *psMetadataKeys(psMetadata *md)
+{
+    PS_ASSERT_METADATA_NON_NULL(md, NULL);
+    return psHashKeyList(md->hash);
+}
+
+
+bool psMetadataPrint(FILE *fd,
+                     const psMetadata *md,
+                     int level)
+{
+    PS_ASSERT_METADATA_NON_NULL(md,false);
+    if (fd == NULL) {
+        fd = stdout;
+    } else {
+        if ( fprintf(fd, "\n") < 0 ) {
+            psError(PS_ERR_IO, true,
+                    "Invalid file pointer in psMetadataPrint.  Could not write to fd.\n");
+            return false;
+        }
+    }
+
+    psErrorClear();   // we're going to _append_ errors to the stack
+    bool noErrors = true;  // have see seen any errors?
+
+    // Casting away const --- the addition of an iterator should not be considered an invasion of "const".
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL; // Item from metadata
+    while ( (item = psMetadataGetAndIncrement(iter)) ) {
+        for (int i = 0; i < level; i++) {
+            fprintf(fd, "  ");
+        }
+        fprintf(fd, "%s", item->name);
+        if (item->comment && strlen(item->comment) > 0) {
+            fprintf(fd, " (%s)", item->comment);
+        }
+        fprintf(fd, ": ");
+
+        switch (item->type) {
+        case PS_DATA_STRING:
+            fprintf(fd, "%s\n", item->data.str);
+            break;
+        case PS_DATA_BOOL:
+            if (item->data.B) {
+                fprintf(fd, "True\n");
+            } else {
+                fprintf(fd, "False\n");
+            }
+            break;
+        case PS_DATA_S8:
+            fprintf(fd, "%d\n", item->data.S8);
+            break;
+        case PS_DATA_S16:
+            fprintf(fd, "%d\n", item->data.S16);
+            break;
+        case PS_DATA_S32:
+            fprintf(fd, "%d\n", item->data.S32);
+            break;
+        case PS_DATA_S64:
+            fprintf(fd, "%jd\n", item->data.S64);
+            break;
+        case PS_DATA_U8:
+            fprintf(fd, "%u\n", item->data.U8);
+            break;
+        case PS_DATA_U16:
+            fprintf(fd, "%u\n", item->data.U16);
+            break;
+        case PS_DATA_U32:
+            fprintf(fd, "%u\n", item->data.U32);
+            break;
+        case PS_DATA_U64:
+            fprintf(fd, "%ju\n", item->data.U64);
+            break;
+        case PS_DATA_F32:
+            fprintf(fd, "%f\n", item->data.F32);
+            break;
+        case PS_DATA_F64:
+            fprintf(fd, "%f\n", item->data.F64);
+            break;
+        case PS_DATA_METADATA:
+            if (!item->data.V) {
+                fprintf(fd, "(null)\n");
+                break;
+            }
+            psMetadataPrint(fd, item->data.V, level + 1);
+            for (int i = 0; i < level; i++) {
+                fprintf(fd, "  ");
+            }
+            fprintf(fd, "%s  -- END\n", item->name);
+            break;
+        case PS_DATA_REGION:
+            if (!item->data.V) {
+                fprintf(fd, "(null)\n");
+                break;
+            }
+            psString region = psRegionToString(*(psRegion*)item->data.V);
+            fprintf(fd, "%s\n", region);
+            psFree(region);
+            break;
+        case PS_DATA_LIST:
+            if (!item->data.V) {
+                fprintf(fd, "(null)\n");
+                break;
+            }
+            fprintf(fd, "<a list of size %ld>\n", ((psList*)item->data.V)->n);
+            break;
+        case PS_DATA_TIME:
+            if (!item->data.V) {
+                fprintf(fd, "(null)\n");
+                break;
+            }
+            psString time = psTimeToISO(item->data.V);
+            fprintf(fd, "%s\n", time);
+            psFree(time);
+            break;
+        case PS_DATA_VECTOR:
+          if (!item->data.V) {
+              fprintf(fd, "(null)\n");
+              break;
+          }
+          psVector *vector = item->data.V;
+          switch (vector->type.type) {
+            case PS_DATA_U8:
+              fprintf(fd, "U8  ");
+              for (int i = 0; i < vector->n; i++) {
+                  fprintf(fd, "%u ", vector->data.U8[i]);
+              }
+              fprintf(fd, "\n");
+              break;
+            case PS_DATA_U16:
+              fprintf(fd, "U16  ");
+              for (int i = 0; i < vector->n; i++) {
+                  fprintf(fd, "%u ", vector->data.U16[i]);
+              }
+              fprintf(fd, "\n");
+              break;
+            case PS_DATA_U32:
+              fprintf(fd, "U32  ");
+              for (int i = 0; i < vector->n; i++) {
+                  fprintf(fd, "%u ", vector->data.U32[i]);
+              }
+              fprintf(fd, "\n");
+              break;
+            case PS_DATA_U64:
+              fprintf(fd, "U64  ");
+              for (int i = 0; i < vector->n; i++) {
+                  fprintf(fd, "%ju ", vector->data.U64[i]);
+              }
+              fprintf(fd, "\n");
+              break;
+            case PS_DATA_S8:
+              fprintf(fd, "S8  ");
+              for (int i = 0; i < vector->n; i++) {
+                  fprintf(fd, "%d ", vector->data.S8[i]);
+              }
+              fprintf(fd, "\n");
+              break;
+            case PS_DATA_S16:
+              fprintf(fd, "S16  ");
+              for (int i = 0; i < vector->n; i++) {
+                  fprintf(fd, "%d ", vector->data.S16[i]);
+              }
+              fprintf(fd, "\n");
+              break;
+            case PS_DATA_S32:
+              fprintf(fd, "S32  ");
+              for (int i = 0; i < vector->n; i++) {
+                  fprintf(fd, "%d ", vector->data.S32[i]);
+              }
+              fprintf(fd, "\n");
+              break;
+            case PS_DATA_S64:
+              fprintf(fd, "S64  ");
+              for (int i = 0; i < vector->n; i++) {
+                  fprintf(fd, "%jd ", vector->data.S64[i]);
+              }
+              fprintf(fd, "\n");
+              break;
+            case PS_DATA_F32:
+              fprintf(fd, "F32 ");
+              for (int i = 0; i < vector->n; i++) {
+                  fprintf(fd, "%f ", vector->data.F32[i]);
+              }
+              fprintf(fd, "\n");
+              break;
+            case PS_DATA_F64:
+              fprintf(fd, "F64 ");
+              for (int i = 0; i < vector->n; i++) {
+                  fprintf(fd, "%f ", vector->data.F64[i]);
+              }
+              fprintf(fd, "\n");
+              break;
+            case PS_DATA_UNKNOWN:
+            default:
+              fprintf(fd, "<Unsupported type>\n");
+          }
+          break;
+        default:
+          psError(PS_ERR_IO, false, "Non-printable metadata type for \"%s\": %x\n",
+                  item->name, item->type);
+          noErrors = false;
+          break;
+        }
+    }
+    psFree(iter);
+
+    return noErrors;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadata.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadata.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadata.h	(revision 22322)
@@ -0,0 +1,1303 @@
+/** @file  psMetadata.h
+*
+*  @brief Contains metadata struuctures, enumerations and functions prototypes
+*
+*  This file defines metadata item, metadata type, metadata flags, metadata containers, and function
+*  prototypes necessary creating psLib metadata APIs
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.105 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2008-08-14 03:18:41 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#ifndef PS_METADATA_H
+#define PS_METADATA_H
+
+/// @addtogroup DataContainer Data Containers
+/// @{
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#include "psHash.h"
+#include "psList.h"
+#include "psTime.h"
+#include "psLookupTable.h"
+#include "psMutex.h"
+
+#define PS_DATA_IS_PRIMITIVE(TYPE) \
+(TYPE == PS_DATA_S8 || \
+ TYPE == PS_DATA_S16 || \
+ TYPE == PS_DATA_S32 || \
+ TYPE == PS_DATA_S64 || \
+ TYPE == PS_DATA_U8 || \
+ TYPE == PS_DATA_U16 || \
+ TYPE == PS_DATA_U32 || \
+ TYPE == PS_DATA_U64 || \
+ TYPE == PS_DATA_F32 || \
+ TYPE == PS_DATA_F64 || \
+ TYPE == PS_DATA_BOOL)
+
+#define PS_DATA_PRIMITIVE_TYPE(DATATYPE) ( \
+        (DATATYPE==PS_DATA_S8 || DATATYPE==PS_DATA_S16 || \
+         DATATYPE==PS_DATA_S32 || DATATYPE==PS_DATA_S64 || DATATYPE==PS_DATA_U8 || \
+         DATATYPE==PS_DATA_U16 || DATATYPE==PS_DATA_U32 || DATATYPE==PS_DATA_U64 || \
+         DATATYPE==PS_DATA_F32 || DATATYPE==PS_DATA_F64 || DATATYPE==PS_DATA_BOOL) ? DATATYPE : 0)
+
+
+/** Option flags for psMetadata functions
+ *
+ *  Enumeration for the modification of the behaviour in psMetadataAddItem.
+ *
+ *  @see psMetadataAddItem
+ */
+typedef enum {
+    PS_META_DEFAULT       = 0,          ///< default behaviour (duplicate entry is an error)
+    PS_META_REPLACE       = 0x01000000, ///< allow entry to be replaced
+    PS_META_NO_REPLACE    = 0x02000000, ///< duplicate entry is silently skipped
+    PS_META_DUPLICATE_OK  = 0x04000000, ///< allow duplicate entries
+    PS_META_UPDATE_FOLDER = 0x08000000, ///< for a metadata folder, merge contents with existing md
+    PS_META_NULL          = 0x10000000, ///< psMetadataItem.data is a NULL value
+    PS_META_REQUIRE_ENTRY = 0x20000000, ///< require pre-existing entry with same name
+    PS_META_REQUIRE_TYPE  = 0x40000000  ///< require pre-existing entry to have same type
+} psMetadataFlags;
+
+#define PS_METADATA_FLAGS_MASK 0xFF000000
+#define PS_METADATA_TYPE_MASK 0x00FFFFFF
+
+#define PS_METADATA_ITEM_GET_TYPE(MDITEM) (MDITEM->type & PS_METADATA_TYPE_MASK)
+
+/** Metadata data structure.
+ *
+ *  Struct for holding metadata items. Metadata items are held in two
+ *  containers. The first employs a doubly-linked list to preserve the order
+ *  of the metadata. The second container employs a hash table which
+ *  allows fast lookup when given a metadata keyword.
+ */
+typedef struct
+{
+    psList*  list;                     ///< Metadata in linked-list
+    psHash*  hash;                     ///< Metadata in a hash table
+    psMutex lock;                       ///< Optional lock for thread safety
+}
+psMetadata;
+
+
+/** Metadata iterator
+ *
+ *  Iterator for metadata.
+ */
+typedef struct
+{
+    psListIterator* iter;              ///< iterator for the psMetadata's psList
+    regex_t* regex;                    ///< the subsetting regular expression
+}
+psMetadataIterator;
+
+
+/** Metadata item data structure.
+ *
+ * Struct for maintaining metadata items of varying types. It also contains
+ * information about the item name, flags, comments, and other items with the same name.
+ */
+typedef struct
+{
+    const psS32 id;                    ///< Unique ID for metadata item.
+    psString name;                     ///< Name of metadata item.
+    psDataType type;                   ///< Type of metadata item.
+    union {
+        bool B;                      ///< boolean data
+        psS8 S8;                       ///< Signed 8-bit integer data.
+        psS16 S16;                     ///< Signed 16-bit integer data.
+        psS32 S32;                     ///< Signed 32-bit integer data.
+        psS64 S64;                     ///< Signed 64-bit integer data.
+        psU8 U8;                       ///< Unsigned 8-bit integer data.
+        psU16 U16;                     ///< Unsigned 16-bit integer data.
+        psU32 U32;                     ///< Unsigned 32-bit integer data.
+        psU64 U64;                     ///< Unsigned 64-bit integer data.
+        psF32 F32;                     ///< Single-precision float data.
+        psF64 F64;                     ///< Double-precision float data.
+        psList *list;                  ///< List data.
+        psMetadata *md;                ///< Metadata data.
+        psString str;                  ///< string data
+        psPtr V;                       ///< Pointer to other type of data.
+    } data;                            ///< Union for data types.
+    psString comment;                  ///< Optional comment ("", not NULL).
+}
+psMetadataItem;
+
+
+/** Create a metadata item.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct. The name argument specifies the name to use for this item, and
+ *  may include sprintf formatting codes. The format entry specifies both
+ *  the metadata type and optional flags and is created by bit-wise or of the
+ *  appropriate type and flag. The comment argument is a fixed string used to
+ *  comment the metadata item. The arguments to the name formatting codes and
+ *  the metadata itself are passed as arguments following the comment string.
+ *  The data must be a pointer for any of the elements stored in data.void.
+ *  The argument list must be interpreted appropriately by the va_list
+ *  operators in the function specified size and type.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+#ifdef DOXYGEN
+psMetadataItem* psMetadataItemAlloc(
+    const char *name,                  ///< Name of metadata item.
+    psDataType type,                   ///< Type of metadata item.
+    const char *comment,               ///< Comment for metadata item.
+    ...                                ///< Arguments for name formatting and metadata item data.
+);
+#else // ifdef DOXYGEN
+psMetadataItem* p_psMetadataItemAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const char *name,                  ///< Name of metadata item.
+    psDataType type,                   ///< Type of metadata item.
+    const char *comment,               ///< Comment for metadata item.
+    ...                                ///< Arguments for name formatting and metadata item data.
+) PS_ATTR_MALLOC;
+#define psMetadataItemAlloc(name, type, ...) \
+      p_psMetadataItemAlloc(__FILE__, __LINE__, __func__, name, type, __VA_ARGS__)
+#endif // ifdef DOXYGEN
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
+ *
+ *  @return bool:       True if the pointer matches a psMetadataItem structure, false otherwise.
+ */
+bool psMemCheckMetadataItem(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+// XXX Although all of the psMetadataItemAlloc[type] prototypes are allocators,
+// I've decided not to modify them all to pass in file, lineno, func.
+// Hopefully these prototypes will become macros or generated by macros some day
+// so this can be done automatically.  -JH
+
+/** Create a metadata item with specified string data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocStr(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    const char* value                  ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified psF32 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocF32(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psF32 value                        ///< the value of the metadata item.
+);
+
+/** Create a metadata item with specified psF64 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocF64(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psF64 value                        ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified psS8 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocS8(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psS8 value                         ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified psS16 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocS16(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psS16 value                        ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified psS32 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocS32(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psS32 value                        ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified psS64 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocS64(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psS64 value                        ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified psU8 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocU8(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psU8 value                         ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified psU16 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocU16(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psU16 value                        ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified psU32 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocU32(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psU32 value                        ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified psU64 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocU64(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psU64 value                        ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified bool data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocBool(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    bool value                         ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with specified psPtr data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocPtr(
+    const char* name,                  ///< Name of metadata item.
+    psDataType type,                   ///< Data type of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psPtr value                        ///< the value of the metadata item.
+);
+
+
+/** Create a metadata item with va_list.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct. The name argument specifies the name to use for this item, and
+ *  may include sprintf formatting codes. The format entry specifies both
+ *  the metadata type and optional flags and is created by bit-wise or of the
+ *  appropriate type and flag. The comment argument is a fixed string used to
+ *  comment the metadata item. The arguments to the name formatting codes and
+ *  the metadata itself are passed as arguments following the comment string.
+ *  The data must be a pointer for any of the elements stored in data.void.
+ *  The argument list must be interpreted appropriately by the va_list
+ *  operators in the function specified size and type.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+#ifndef SWIG
+#ifdef DOXYGEN
+psMetadataItem* psMetadataItemAllocV(
+    const char *name,                  ///< Name of metadata item.
+    psDataType type,                   ///< Type of metadata item.
+    const char *comment,               ///< Comment for metadata item.
+    va_list list                       ///< Arguments for name formatting and metadata item data.
+);
+#else // ifdef DOXYGEN
+psMetadataItem* p_psMetadataItemAllocV(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const char *name,                  ///< Name of metadata item.
+    psDataType type,                   ///< Type of metadata item.
+    const char *comment,               ///< Comment for metadata item.
+    va_list list                       ///< Arguments for name formatting and metadata item data.
+);
+#define psMetadataItemAllocV(name, type, comment, list) \
+      p_psMetadataItemAllocV(__FILE__, __LINE__, __func__, name, type,comment, list)
+#endif // ifdef DOXYGEN
+#endif // #ifndef SWIG
+
+
+/** Create a metadata collection.
+ *
+ *  Returns an empty metadata container with fully allocated internal metadata
+ *  containers.
+ *
+ *  @return psMetadata* : Pointer metadata.
+ */
+#ifdef DOXYGEN
+psMetadata* psMetadataAlloc(void);
+#else // ifdef DOXYGEN
+psMetadata* p_psMetadataAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func                    ///< Function name of caller
+) PS_ATTR_MALLOC;
+#define psMetadataAlloc() \
+      p_psMetadataAlloc(__FILE__, __LINE__, __func__)
+#endif // ifdef DOXYGEN
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr
+ *  datatype.
+ *
+ *  @return bool:       True if the pointer matches a psMetadata structure, false otherwise.
+ */
+bool psMemCheckMetadata(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** Creates a new copy of a psMetadataItem.
+ *
+ * @return psMetadataItem*: the copy of the psMetadataItem
+ */
+#ifdef DOXYGEN
+psMetadataItem *psMetadataItemCopy(
+    const psMetadataItem *in            ///< metadata item to be copied
+);
+#else // ifdef DOXYGEN
+psMetadataItem *p_psMetadataItemCopy(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const psMetadataItem *in            ///< metadata item to be copied
+);
+#define psMetadataItemCopy(in) \
+      p_psMetadataItemCopy(__FILE__, __LINE__, __func__, in)
+#endif // ifdef DOXYGEN
+
+
+/** Create a copy of an existing psMetadata collection.
+ *
+ *  Creates a new copy of all the psMetadataItems in the psMetadata collection,
+ *  in, and returns them in out, or creates a new container if out is NULL.  If
+ *  an error occurs, NULL is returned but out may still have been updated if it
+ *  is non-NULL.
+ *
+ *  @return psMetadata*:        the copy of the psMetadata container.
+ */
+#ifdef DOXYGEN
+psMetadata *psMetadataCopy(
+    psMetadata *out,                   ///< output Metadata container for copying.
+    const psMetadata *in               ///< Metadata collection to be copied.
+);
+#else // ifdef DOXYGEN
+psMetadata *p_psMetadataCopy(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psMetadata *out,                    ///< output Metadata container for copying.
+    const psMetadata *in                ///< Metadata collection to be copied.
+);
+#define psMetadataCopy(out, in) \
+      p_psMetadataCopy(__FILE__, __LINE__, __func__, out, in)
+#endif // ifdef DOXYGEN
+
+
+/** Updates an existing psMetadata collection with elements from a metadata collection
+ *
+ *  Creates a new copy of all the psMetadataItems in the psMetadata collection 'in' and places
+ *  them in out.  all items in 'in' must already be in 'out' and be of the same type.
+ *
+ *  @return psMetadata*:        the copy of the psMetadata container.
+ */
+#ifdef DOXYGEN
+bool psMetadataUpdate(
+    psMetadata *out,                   ///< output Metadata container for copying.
+    const psMetadata *in               ///< Metadata collection to be copied.
+    );
+#else // ifdef DOXYGEN
+bool p_psMetadataUpdate(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psMetadata *out,                    ///< output Metadata container for copying.
+    const psMetadata *in                ///< Metadata collection to be copied.
+    );
+#define psMetadataUpdate(out, in) \
+      p_psMetadataUpdate(__FILE__, __LINE__, __func__, out, in)
+#endif // ifdef DOXYGEN
+
+
+/** Overlays an existing psMetadata collection with elements from a metadata collection
+ *
+ *  Creates a new copy of all the psMetadataItems in the psMetadata collection 'in' and places
+ *  them in out.  matching metadata structures in 'out' are supplemented with corresponding
+ *  entries from 'in'
+ *
+ *  @return psMetadata*:        the copy of the psMetadata container.
+ */
+#ifdef DOXYGEN
+bool psMetadataOverlay(
+    psMetadata *out,                   ///< output Metadata container for copying.
+    const psMetadata *in               ///< Metadata collection to be copied.
+    );
+#else // ifdef DOXYGEN
+bool p_psMetadataOverlay(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psMetadata *out,                    ///< output Metadata container for copying.
+    const psMetadata *in                ///< Metadata collection to be copied.
+    );
+#define psMetadataOverlay(out, in) \
+      p_psMetadataOverlay(__FILE__, __LINE__, __func__, out, in)
+#endif // ifdef DOXYGEN
+
+
+/** Supplements a metadata with an item from another metadata.
+ *
+ *  Supplements the output metadata with the metadata item of the specified name from the input metadata.  If
+ *  out is NULL, a new container is created.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psMetadataItemSupplement(
+    psMetadata *out,                   ///< output Metadata container for copying.
+    const psMetadata *in,              ///< Metadata collection from which to copy.
+    const char *key                    ///< key to identify the metadata item for copying.
+);
+
+
+/** Add existing metadata item to metadata collection.
+ *
+ *  Add a metadata item that has already been created to the metadata
+ *  collection.
+ *
+ * Note: that this function accepts it's "value" as a stdarg.  This means that
+ * the type of the value is not coerced by the prototype.  You need to be
+ * careful to cast 64-bit integer values as smaller types will not be promoted.
+ * This *includes* constant values as they are typically a 32-bit type.
+ *
+ *  @return bool: True for success, false for failure.
+ */
+bool psMetadataAddItem(
+    psMetadata*  md,                   ///< Metadata collection to insert metadata item.
+    const psMetadataItem* item,        ///< Metadata item to be added.
+    int location,                      ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    psS32 flags                        ///< Options flag mask, see psMetadataFlags enum
+);
+
+
+/** Create and add a metadata item to metadata collection.
+ *
+ * Creates a new metadata item add to the metadata collection.
+ *
+ * Note: that this function accepts it's "value" as a stdarg.  This means that
+ * the type of the value is not coerced by the prototype.  You need to be
+ * careful to cast 64-bit integer values as smaller types will not be promoted.
+ * This *includes* constant values as they are typically a 32-bit type.
+ *
+ * @return bool: True for success, false for failure.
+ */
+bool psMetadataAdd(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item.
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char *name,                  ///< Name of metadata item.
+    int format,                        ///< psDataType of metadata item & options (psMetadataFlags)
+    const char *comment,               ///< Comment for metadata item.
+    ...                                ///< Arguments for name formatting and metadata item data.
+);
+
+
+/** Create and add a metadata item to metadata collection.
+ *
+ * Creates a new metadata item add to the metadata collection.
+ *
+ * @return bool: True for success, false for failure.
+ */
+#ifndef SWIG
+bool psMetadataAddV(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item.
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char *name,                  ///< Name of metadata item.
+    int format,                        ///< psDataType of metadata item & options (psMetadataFlags)
+    const char *comment,               ///< Comment for metadata item.
+    va_list list                       ///< Arguments for name formatting and metadata item data.
+);
+#endif // #ifndef SWIG
+
+
+/** Add a bool value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddBool(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    bool value                         ///< Value for metadata item data
+);
+
+
+/** Add a psS8 value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddS8(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psS8 value                         ///< Value for metadata item data
+);
+
+
+/** Add a psS16 value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddS16(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psS16 value                        ///< Value for metadata item data
+);
+
+
+/** Add a psS32 value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddS32(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psS32 value                        ///< Value for metadata item data
+);
+
+
+/** Add a psS64 value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddS64(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psS64 value                        ///< Value for metadata item data
+);
+
+
+/** Add a psU8 value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddU8(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psU8 value                         ///< Value for metadata item data
+);
+
+
+/** Add a psU16 value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddU16(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psU16 value                        ///< Value for metadata item data
+);
+
+
+/** Add a psU32 value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddU32(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psU32 value                        ///< Value for metadata item data
+);
+
+
+/** Add a psU64 value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddU64(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psU64 value                        ///< Value for metadata item data
+);
+
+
+/** Add a psF32 value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+*/
+bool psMetadataAddF32(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psF32 value                        ///< Value for metadata item data
+);
+
+
+/** Add a psF64 value to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+*/
+bool psMetadataAddF64(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psF64 value                        ///< Value for metadata item data
+);
+
+
+/** Add a psList to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddList(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psList* value                      ///< psList for metadata item data
+);
+
+
+/** Add a string to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddStr(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    const char* value                  ///< String for metadata item data
+);
+
+
+/** Add a vector to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddVector(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psVector* value                    ///< Vector for metadata item data
+);
+
+
+/** Add a array to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddArray(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psArray* value                     ///< Vector for metadata item data
+);
+
+
+/** Add an Image to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddImage(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psImage* value                     ///< Image for metadata item data
+);
+
+
+/** Add a Time to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddTime(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psTime* value                      ///< Time for metadata item data
+);
+
+
+/** Add a Hash to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddHash(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psHash* value                      ///< Hash for metadata item data
+);
+
+
+/** Add a LookupTable to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddLookupTable(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psLookupTable* value               ///< LookupTable for metadata item data
+);
+
+
+/** Add an Unknown (psPtr) to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddUnknown(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psPtr value                        ///< Unknown for metadata item data
+);
+
+
+/** Add a psPtr to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddPtr(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    psDataType type,                   ///< psDataType for metadata item
+    const char* comment,               ///< Comment for metadata item
+    psPtr value                        ///< Unknown for metadata item data
+);
+
+
+/** Add Metadata to metadata collection.
+ *
+ *  @return bool:  True for success, False for failure.
+ */
+bool psMetadataAddMetadata(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item
+    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* name,                  ///< Name of metadata item
+    int format,                        ///< psMetadataFlag options/flags
+    const char* comment,               ///< Comment for metadata item
+    psMetadata* value                  ///< Metadata for metadata item data
+);
+
+
+/** Removes an item from metadata by key name.
+ *
+ *  @return bool:  True for success, false for failure.
+ */
+bool psMetadataRemoveKey(
+    psMetadata *md,                    ///< Metadata collection to remove metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Removes an item from metadata by index number.
+ *
+ *  @return bool:  True for success, false for failure.
+ */
+bool psMetadataRemoveIndex(
+    psMetadata *md,                    ///< Metadata collection to remove metadata item.
+    long location                       ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+
+/** Find an item in the metadata collection based on key name.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the first item is returned. If the item is not found, null is
+ *  returned.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataLookup(
+    const psMetadata * md,             ///< Metadata collection to lookup metadata item.
+    const char * key                   ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its double precision value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return psF64 : Value of metadata item.
+ */
+psF64 psMetadataLookupF64(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its single precision value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return psF32 : Value of metadata item.
+ */
+psF32 psMetadataLookupF32(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return psS8 : Value of metadata item.
+ */
+psS8 psMetadataLookupS8(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return psS16 : Value of metadata item.
+ */
+psS16 psMetadataLookupS16(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return psS32 : Value of metadata item.
+ */
+psS32 psMetadataLookupS32(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return psS64 : Value of metadata item.
+ */
+psS64 psMetadataLookupS64(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return psU8 : Value of metadata item.
+ */
+psU8 psMetadataLookupU8(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return psU16 : Value of metadata item.
+ */
+psU16 psMetadataLookupU16(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return psU32 : Value of metadata item.
+ */
+psU32 psMetadataLookupU32(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return psU64 : Value of metadata item.
+ */
+psU64 psMetadataLookupU64(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its boolean value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return bool : Value of metadata item.
+ */
+bool psMetadataLookupBool(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ * @return void* : Value of metadata item.
+ */
+psPtr psMetadataLookupPtr(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata* md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on list index.
+ *
+ *  Items may be found in the metadata by their entry position in the list
+ *  container.
+ *
+ *  @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataGet(
+    const psMetadata* md,              ///< Metadata collection to retrieve metadata item.
+    int location                       ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+
+/** Creates a psMetadataIterator to iterate over the specified psMetadata.
+ *
+ *  Supports the subsetting of the metadata via keyword using regular
+ *  expression.  If no regular expression is specified, iteration
+ *  over the entire psMetadata is performed.
+ *
+ *  @return psMetadataIterator*        a new psMetadataIterator, of NULL if error occurred
+ */
+#ifdef DOXYGEN
+psMetadataIterator* psMetadataIteratorAlloc(
+    const psMetadata* md,               ///< the psMetadata to iterate with
+    long location,                      ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* regex
+    ///< A regular expression for subsetting the psMetadata.  If NULL, no
+    ///< subsetting is performed.
+);
+#else // ifdef DOXYGEN
+psMetadataIterator* p_psMetadataIteratorAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    const psMetadata* md,               ///< the psMetadata to iterate with
+    long location,                      ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
+    const char* regex
+    ///< A regular expression for subsetting the psMetadata.  If NULL, no
+    ///< subsetting is performed.
+) PS_ATTR_MALLOC;
+#define psMetadataIteratorAlloc(md, location, regex) \
+      p_psMetadataIteratorAlloc(__FILE__, __LINE__, __func__, md, location, regex)
+#endif // ifdef DOXYGEN
+
+
+/** Set the iterator of the psMetadat to a given position.  If location is
+ *  invalid the iterator position is not changed.
+ *
+ *  @return bool        TRUE if iterator successfully set, otherwise FALSE.
+*/
+bool psMetadataIteratorSet(
+    psMetadataIterator* iterator,      ///< psMetadata iterator
+    long location                      ///< index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+
+/** Position the specified iterator to the next matching item in psMetadata,
+ *  given the regular expression of the iterator
+ *
+ *  @return psPtr       the psMetadataItem at the original iterator position
+ *                      or NULL if the iterator went past the end of the list.
+ */
+psMetadataItem* psMetadataGetAndIncrement(
+    psMetadataIterator* iterator       ///< iterator to move
+);
+
+
+/** Position the specified iterator to the previous matching item in psMetadata,
+ *  given the regular expression of the iterator
+ *
+ *  @return psPtr       the psMetadataItem at the original iterator position
+ *                      or NULL if the iterator went past the beginning of the
+ *                      list.
+ */
+psMetadataItem* psMetadataGetAndDecrement(
+    psMetadataIterator* iterator       ///< iterator to move
+);
+
+
+/** Find an item in the metadata collection based on key name and return its metadata value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ *  @return psMetadata*:        Value of metadata item.
+ */
+psMetadata *psMetadataLookupMetadata(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return its string value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ *  @return psString:           Value of metadata item.
+ */
+psString psMetadataLookupStr(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+
+/** Find an item in the metadata collection based on key name and return it as a psTime pointer.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not
+ *  found, zero is returned.
+ *
+ *  @return psTime:           Value of metadata item.
+ */
+
+psTime *psMetadataLookupTime(
+    bool *status,                      ///< Status of lookup.
+    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/// Return a list of keys for a metadata
+psList *psMetadataKeys(psMetadata *md   ///< Metadata for which to return keys
+                       );
+
+/** Prints metadata collection.
+ *
+ *  Metadata contents are printed to a valid file descriptor if one exists.  Otherwise,
+ *  fd should be NULL and the contents are printed to the screen (stdout).
+ *
+ *  @return bool:           True if successful, otherwise false.
+*/
+bool psMetadataPrint(
+    FILE *fd,                          ///< File Descriptor or NULL
+    const psMetadata *md,              ///< Metadata collection to print.
+    int level                          ///< the level of metadata items.
+);
+
+/// Assert on metadata with extant hash and list components
+#define PS_ASSERT_METADATA_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->hash || !(NAME)->list) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: Metadata %s or one of its components is NULL.", \
+            #NAME); \
+    return RVAL; \
+}
+
+/// Assert on metadata item with extant name and type
+///
+/// The data contained within the metadata item is permitted to be NULL.
+#define PS_ASSERT_METADATA_ITEM_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->name || !(NAME)->type) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: Metadata item %s or its name or type is NULL.", \
+            #NAME); \
+    return RVAL; \
+}
+
+#define PS_ASSERT_METADATA_ITERATOR_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->iter) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: Metadata iterator %s or its component is NULL.", \
+            #NAME); \
+    return RVAL; \
+}
+
+/// @}
+#endif // #ifndef PS_METADATA_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataConfig.c	(revision 22322)
@@ -0,0 +1,1706 @@
+/** @file  psMetadataConfig.c
+*
+*  @brief Contains metadata input/output functions.
+*
+*  This file defines functions to read and write metadata to/from an external file.
+*
+*  @ingroup Metadata
+*
+*  @author Ross Harman, MHPCC
+*  @author Eric Van Alst, MHPCC
+*  @author Joshua Hoblitt, University of Hawaii 2006-2007
+*
+*  @version $Revision: 1.143 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2008-05-05 00:09:04 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <fitsio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <strings.h>
+#include "psSlurp.h"
+
+#include "psMemory.h"
+#include "psMetadataConfig.h"
+#include "psAssert.h"
+#include "psTrace.h"
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Maximum size of a string */
+#define MAX_STRING_LENGTH 1024
+#define INITIAL_LENGTH 10               // Initial length for arrays
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+static psMetadata *genTypeTemplate(char *linePtr);
+static psMetadata *parseTypeValues(psMetadata *template, char *linePtr)
+;
+static bool parseLine(psArray *levelArray,
+                      char *linePtr,
+                      bool overwrite,
+                      bool *notBlank);
+static bool parseMetadataItem(char *keyName, psArray *levelArray,
+                              char *linePtr, psMetadataFlags flags);
+static psString formatMetadataItem(psMetadataItem *item);
+static psArray *p_psMetadataKeyArray(psMetadata *md);
+static bool parseGeneric(char *keyName,
+                         psArray *levelArray,
+                         char *linePtr,
+                         psMetadataFlags flags);
+static bool parseType(char *keyName,
+                      psArray *levelArray,
+                      char *linePtr,
+                      psMetadataFlags flags);
+static bool parseMetadataEnd(char *keyName,
+                             psArray *levelArray,
+                             char *linePtr,
+                             psMetadataFlags flags);
+
+
+// A metadata data structure used in parsing arrays.
+// Contains array information and the metadata storage location.
+typedef struct
+{
+    psArray *   nonUniqueKeyArray;      ///< non-unique key names
+    psHash *    typeTemplates;          ///< hash of user type templates
+    psMetadata *metadata;               ///< metadata container
+}
+p_psParseLevelInfo;
+
+static void parseLevelInfoFree(p_psParseLevelInfo *info)
+{
+    psFree(info->nonUniqueKeyArray);
+    psFree(info->typeTemplates);
+    psFree(info->metadata);
+}
+
+static p_psParseLevelInfo *p_psParseLevelInfoAlloc(void)
+{
+    // Allocate memory for parse level info
+    p_psParseLevelInfo *info = (p_psParseLevelInfo*)psAlloc(sizeof(p_psParseLevelInfo));
+    psMemSetDeallocator(info,(psFreeFunc)parseLevelInfoFree);
+
+    info->nonUniqueKeyArray = psArrayAllocEmpty(INITIAL_LENGTH);
+    info->typeTemplates = psHashAlloc(10);
+    info->metadata = NULL;
+
+    return info;
+}
+
+// Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
+// must be null terminated.
+static bool ignoreLine(char *inString)
+{
+    while(*inString!='\0' && *inString!='#') {
+        if(!isspace(*inString)) {
+            return false;
+        }
+        inString++;
+    }
+
+    return true;
+}
+
+
+//  Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+//  terminated copy of the original input string.
+static char *cleanString(char *inString,
+                         psS32 sLen,
+                         bool ignoreComment)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+    // Initialize begining of string pointer
+    ptrB = inString;
+
+    // Skip over leading # or whitespace
+    if(ignoreComment) {
+        while (isspace(*ptrB) || *ptrB=='#') {
+            ptrB++;
+        }
+    } else {
+        while (isspace(*ptrB)) {
+            ptrB++;
+        }
+    }
+
+    // Skip over trailing whitespace, null terminators, and # characters
+    ptrE = inString + sLen;
+    if(ignoreComment) {
+        while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+            ptrE--;
+        }
+    } else {
+        while(isspace(*ptrE) || *ptrE=='\0') {
+            ptrE--;
+        }
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    if(sLen < 0 ) {
+        cleaned = NULL;
+    } else {
+        cleaned = psStringNCopy(ptrB, sLen);
+    }
+
+    return cleaned;
+}
+
+// Count repeat occurances of a single character within a line. The input string must be null terminated.
+static psS32 repeatedChars(char *inString,
+                           char ch)
+{
+    psS32 count = 0;
+
+    while(*inString!='\0') {
+        if(*inString == ch) {
+            count++;
+        }
+        inString++;
+    }
+
+    return count;
+}
+
+// Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+// the beginning of the string. Tokens are newly allocated null terminated strings.
+// XXX EAM : not sure this API is well-thought-out:
+// *status must be set to 0 going in.
+// status is 1 if delimeter is found, but no valid token
+// returned string is NULL if no valid token is found
+static char *getToken(char **inString,
+                      char *delimiter,
+                      psS32 *status,
+                      bool ignoreComment)
+{
+    char *cleanToken = NULL;
+    char *convertChar = NULL;
+    psS32 sLen = 0;
+
+    // Convert tab characters to white space
+    while((convertChar=strchr(*inString,'\t')) != NULL ) {
+        *convertChar = ' ';
+    }
+
+    // Skip over leading whitespace
+    while(isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+    if(sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen,ignoreComment);
+
+        // Move to end of token
+        (*inString) += sLen;
+    } else if(**inString!='\0' && sLen==0) {
+        *status = 1;
+    }
+
+    return cleanToken;
+}
+
+// Returns single parsed value as a double precision number. The input string must be cleaned and null
+// terminated.
+static double parseDouble(char *inString,
+                          psS32 *status)
+{
+    char *end = NULL;
+    double value = 0.0;
+
+    value = strtod(inString, &end);
+    if(*end != '\0') {
+        *status = 1;
+    } else if(inString==end) {
+        *status = 1;
+    }
+
+    return value;
+}
+
+// Returns single parsed value as a long signed int. The input string must be cleaned and null
+// terminated.
+static psS64 parseSignedInt(char *inString,
+                            psS32 *status)
+{
+    char *end = NULL;
+    psS64 value = 0;
+
+    value = strtol(inString, &end, 0);
+    if(*end != '\0') {
+        *status = 1;
+    } else if(inString==end) {
+        *status = 1;
+    }
+
+    return value;
+}
+
+// Returns single parsed value as a double precision number. The input string must be cleaned and null
+// terminated.
+static psU64 parseUnsignedInt(char *inString,
+                              psS32 *status)
+{
+    char *end = NULL;
+    psU64 value = 0;
+
+    value = strtoul(inString, &end, 0);
+    if(*end != '\0') {
+        *status = 1;
+    } else if(inString==end) {
+        *status = 1;
+    }
+
+    return value;
+}
+
+/** Returns true or false. 'T', 't', '1', 'F', 'f', and '0' are acceptable, parsable variations. */
+static bool parseBool(char *inString,
+                      psS32 *status)
+{
+    // if inString is NULL return flalse, status = 0
+    if (!inString) {
+        if (status) {
+            *status = 0;
+        }
+        return false;
+    }
+
+    if (!strncasecmp(inString, "T", 2) ||
+            !strncmp(inString, "1", 2) ||
+            !strncasecmp(inString, "true", 5)) {
+        if (status != NULL) {
+            *status = 0;
+        }
+        return true;
+    }
+    if ( !strncasecmp(inString, "F", 2) ||
+            !strncmp(inString, "0", 2) ||
+            !strncasecmp(inString, "false", 6)) {
+        if (status != NULL) {
+            *status = 0;
+        }
+        return false;
+    }
+
+    if (status != NULL) {
+        *status = 1;
+    }
+    return false;
+}
+
+/** Returns a psTime structure */
+static psTime *parseTime(char *inString,
+                         psTimeType tt,
+                         psS32 *status)
+{
+    PS_ASSERT_PTR_NON_NULL(status, NULL);
+    if (!inString) {
+        *status = 0;
+        return NULL;
+    }
+
+    // handle "NULL" as the time value
+    if (strncmp(inString, "NULL", 5) == 0) {
+        *status = 0;
+        return NULL;
+    }
+
+    psTime *out = psTimeFromISO(inString, tt);
+    if (!out) {
+        *status = 1;
+        return NULL;
+    }
+
+    *status = 0;
+
+    return out;
+}
+
+/** Returns parsed vector filled with with data. The input string must be null terminated. */
+static psVector *parseVector(char *inString,
+                             psElemType elemType,
+                             psS32 *status)
+{
+    PS_ASSERT_PTR_NON_NULL(inString, NULL);
+
+    // split string into multiple values on " " and ","
+    psList *tokens = psStringSplit(inString, ", ", false);
+
+    // allocate a large enough vector to hold all of the tokens
+    psVector *vec = psVectorAlloc(psListLength(tokens), elemType);
+
+    // iterate through the list of tokens and concert them to numeric values
+    // one by one
+    for (long i = 0; i < psListLength(tokens); i ++) {
+        psString tok = psListGet(tokens, i);
+        char *ptr = NULL;
+        double value = strtod(tok, &ptr);
+
+        // ptr will be set to tok if the parse failed
+        if (tok == ptr) {
+            *status = 1;
+            psError(PS_ERR_IO, true,
+                    _("failed to parse %s as a vector element."), tok);
+            psFree(vec);
+            psFree(tokens);
+            return NULL;
+        }
+
+        // XXX this switch statement and cases should be turned inside out but
+        // this optimization isn't a priority
+        switch(elemType) {
+        case PS_TYPE_U8:
+            vec->data.U8[i] = (psU8)value;
+            break;
+        case PS_TYPE_U16:
+            vec->data.U16[i] = (psU16)value;
+            break;
+        case PS_TYPE_U32:
+            vec->data.U32[i] = (psU32)value;
+            break;
+        case PS_TYPE_U64:
+            vec->data.U64[i] = (psU64)value;
+            break;
+        case PS_TYPE_S8:
+            vec->data.S8[i] = (psS8)value;
+            break;
+        case PS_TYPE_S16:
+            vec->data.S16[i] = (psS16)value;
+            break;
+        case PS_TYPE_S32:
+            vec->data.S32[i] = (psS32)value;
+            break;
+        case PS_TYPE_S64:
+            vec->data.S64[i] = (psS64)value;
+            break;
+        case PS_TYPE_F32:
+            vec->data.F32[i] = (psF32)value;
+            break;
+        case PS_TYPE_F64:
+            vec->data.F64[i] = (psF64)value;
+            break;
+        default:
+            *status = 1;
+            psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+                    _("Specified type, %d, is not supported."), elemType);
+            psFree(vec);
+            psFree(tokens);
+            return NULL;
+            break;                          // unreachable
+        }
+    }
+
+    psFree(tokens);
+
+    return vec;
+}
+
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+bool psMetadataItemPrint(FILE  *fd,
+                         const char *format,
+                         const psMetadataItem* item)
+{
+    psDataType type;
+    bool success = true;
+
+    PS_ASSERT_PTR_NON_NULL(fd, false);
+    PS_ASSERT_STRING_NON_EMPTY(format, false);
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, false);
+
+    type = item->type;
+
+    // determining the format type
+    char *fType = strchr(format,'%');
+    if (fType == NULL) {
+        // well, the format contains no reference to the metadataItem's data:
+        // that is truly trival to do!
+        //        fprintf(fd,format);
+        return false;
+    }
+
+    // skip over any format modifiers
+    const char *formatEnd = format+strlen(format);
+    while ( (fType < formatEnd) &&
+        (strchr(" +-01234567890.$#, hlL",*(++fType)) != NULL) ) {}
+
+    #define METADATAITEM_NUMERIC_CAST(FORMAT_TYPE) { \
+        switch(type) { \
+        case PS_DATA_BOOL: \
+            fprintf(fd, format, (FORMAT_TYPE) item->data.B); \
+            break; \
+        case PS_DATA_S8: \
+            fprintf(fd,format,(FORMAT_TYPE)  item->data.S8); \
+            break; \
+        case PS_DATA_S16: \
+            fprintf(fd,format,(FORMAT_TYPE)  item->data.S16); \
+            break; \
+        case PS_DATA_S32: \
+            fprintf(fd,format,(FORMAT_TYPE)  item->data.S32); \
+            break; \
+        case PS_DATA_S64: \
+            fprintf(fd,format,(FORMAT_TYPE)  item->data.S64); \
+            break; \
+        case PS_DATA_U8: \
+            fprintf(fd,format,(FORMAT_TYPE)  item->data.U8); \
+            break; \
+        case PS_DATA_U16: \
+            fprintf(fd,format,(FORMAT_TYPE)  item->data.U16); \
+            break; \
+        case PS_DATA_U32: \
+            fprintf(fd,format,(FORMAT_TYPE)  item->data.U32); \
+            break; \
+        case PS_DATA_U64: \
+            fprintf(fd,format,(FORMAT_TYPE)  item->data.U64); \
+            break; \
+        case PS_DATA_F32: \
+            fprintf(fd, format,(FORMAT_TYPE)  item->data.F32); \
+            break; \
+        case PS_DATA_F64: \
+            fprintf(fd, format,(FORMAT_TYPE) item->data.F64); \
+            break; \
+        default: \
+            psError(PS_ERR_BAD_PARAMETER_TYPE,true, \
+                    _("Specified psDataType, %d, is not supported."), (int)type); \
+            success = false; \
+        } \
+    }
+
+    switch(*fType) {
+    case 'd':
+    case 'i':
+    case 'c':
+        METADATAITEM_NUMERIC_CAST(int)
+        break;
+    case 'o':
+    case 'u':
+    case 'x':
+    case 'X':
+        METADATAITEM_NUMERIC_CAST(unsigned int)
+        break;
+    case 'e':
+    case 'E':
+    case 'f':
+    case 'F':
+    case 'g':
+    case 'G':
+    case 'a':
+    case 'A':
+        METADATAITEM_NUMERIC_CAST(double)
+        break;
+    case 's':
+        if (type == PS_DATA_STRING) {
+            fprintf(fd,format, item->data.str);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE,true,
+                    _("Specified psDataType, %d, is not supported."), (int)type);
+            success = false;
+        }
+        break;
+    case 'p':
+        fprintf(fd,format,item->data.V);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                _("Specified print format, %%%c, is not supported."), *fType);
+        success = false;
+        break;
+    }
+
+    return success;
+}
+
+static psMetadata *genTypeTemplate(char *linePtr)
+{
+    psMetadata*     metadataTemplate = NULL;
+    psS32           status           = 0;
+    char*           token            = NULL;
+    psMetadataItem* tempItem         = NULL;
+
+    // Loop through line and generate metadata items for each token found
+    while ((token = getToken(&linePtr, " ", &status, false)) != NULL) {
+
+        // If not allocated then allocate new metadata
+        if (metadataTemplate == NULL) {
+            metadataTemplate = psMetadataAlloc();
+        }
+
+        // Look for comment indicator #
+        if (strstr(token, "#") != 0) {
+            psFree(metadataTemplate);
+            psFree(token);
+            return NULL;
+        }
+
+        // Create metadata item to represent token read
+        tempItem = psMetadataItemAllocStr(token, "", "");
+        if (tempItem == NULL) {
+            psFree(metadataTemplate);
+            psFree(token);
+            return NULL;
+        }
+        psFree(token);
+
+        // Add item to template
+        if (!psMetadataAddItem(metadataTemplate, tempItem, PS_LIST_TAIL, PS_META_DEFAULT)) {
+            psFree(metadataTemplate);
+            psFree(tempItem);
+            return NULL;
+        }
+        psFree(tempItem);
+    }
+
+    return metadataTemplate;
+}
+
+
+static psMetadata *parseTypeValues(psMetadata *template,
+                                   char *linePtr)
+{
+    psMetadata*      md           = NULL;
+    char*            token        = NULL;
+    psS32            status       = 0;
+    psMetadataItem*  mdItem       = NULL;
+    psMetadataItem*  templateItem = NULL;
+    psListIterator*  iter         = NULL;
+
+    // Allocate metadata to return
+    md = psMetadataAlloc();
+
+    // Determine the number of items in template
+    long items = psListLength(template->list);
+
+    if (items > 0 ) {
+        // Point to first item in template
+        iter = psListIteratorAlloc(template->list, PS_LIST_HEAD, true);
+        // For each item in template parse line string for values
+        for (long i = 0; i < items; i++) {
+            // Get template item
+            templateItem = psListGetAndIncrement(iter);
+            if (templateItem == NULL) {
+                psFree(md);
+                psFree(iter);
+                md = NULL;
+                break;
+            }
+
+            // Get the next token on the line
+            token = getToken(&linePtr, " ", &status, false);
+            if (token != NULL) {
+                // Allocate metadata item
+                mdItem = psMetadataItemAllocStr(templateItem->name,
+                                                templateItem->comment, token);
+                if (mdItem == NULL) {
+                    psFree(md);
+                    md = NULL;
+                    psFree(token);
+                    psFree(iter);
+                    break;
+                }
+                psFree(token);
+
+                // Add item to metadata
+                if (!psMetadataAddItem(md, mdItem, PS_LIST_TAIL, PS_META_DEFAULT)) {
+                    psFree(md);
+                    md = NULL;
+                    psFree(mdItem);
+                    psFree(iter);
+                    break;
+                }
+                psFree(mdItem);
+            } else {
+                // Missing items
+                psFree(md);
+                psFree(iter);
+                md = NULL;
+                break;
+            }
+        }
+    }
+
+    return md;
+}
+
+bool parseMetadataItem(char *keyName,
+                       psArray *levelArray,
+                       char *linePtr,
+                       psMetadataFlags flags)
+{
+    // XXX This function is a monstrous abomination.  I have been slowly
+    // refactoring it as I fix bugs but it really needs to be split up into
+    // managable bits.
+    bool               returnValue   = true;
+    bool               addStatus     = false;
+    psDataType           mdType        = PS_DATA_UNKNOWN;
+    psElemType           vectorType    = PS_TYPE_S8;
+    char*                strValue      = NULL;
+    char*                strComment    = NULL;
+    psS32                status        = 0;
+    psMetadata*          md            = NULL;
+    psArray*             nonUniqueKeys = NULL;
+    psMetadata*          tempMeta      = NULL;
+    p_psParseLevelInfo*  nextLevelInfo = NULL;
+
+    long level = psArrayLength(levelArray) - 1;
+
+    // Get the metadata item type
+    char *strType = getToken(&linePtr, " ", &status,true);
+
+    // Check for no type
+    if (strType == NULL) {
+        psError(PS_ERR_IO, true, _("Failed to read a metadata type."));
+        return false;
+    }
+
+    // Set metadata type based on type token
+    // Check if the keyName specifies a vector and if so use strType token to
+    // find vector type
+    if (*keyName == '@') {
+        mdType = PS_DATA_VECTOR;
+        // Get the type of vector
+        if(!strncmp(strType, "U8", 2)) {
+            vectorType = PS_TYPE_U8;
+        } else if (!strncmp(strType,"U16",3)) {
+            vectorType = PS_TYPE_U16;
+        } else if (!strncmp(strType,"U32",3)) {
+            vectorType = PS_TYPE_U32;
+        } else if (!strncmp(strType,"U64",3)) {
+            vectorType = PS_TYPE_U64;
+        } else if (!strncmp(strType,"S8",2)) {
+            vectorType = PS_TYPE_S8;
+        } else if (!strncmp(strType,"S16",3)) {
+            vectorType = PS_TYPE_S16;
+        } else if (!strncmp(strType,"S32",3)) {
+            vectorType = PS_TYPE_S32;
+        } else if (!strncmp(strType,"S64",3)) {
+            vectorType = PS_TYPE_S64;
+        } else if (!strncmp(strType,"F32",3)) {
+            vectorType = PS_TYPE_F32;
+        } else if (!strncmp(strType,"F64",3)) {
+            vectorType = PS_TYPE_F64;
+        } else {
+            psError(PS_ERR_IO, true,
+                    _("Failed to parse the value '%s' of metadata item %s, type %s, "),
+                      "", keyName,  strType);
+            psFree(strType);
+            return false;
+        }
+    } else if(!strncmp(strType, "STR", 3)) {
+        mdType = PS_DATA_STRING;
+    } else if(!strncmp(strType, "BOOL", 4)) {
+        mdType = PS_DATA_BOOL;
+    } else if(!strncmp(strType, "S8", 2)) {
+        mdType = PS_DATA_S8;
+    } else if(!strncmp(strType, "S16", 3)) {
+        mdType = PS_DATA_S16;
+    } else if(!strncmp(strType, "S32", 3)) {
+        mdType = PS_DATA_S32;
+    } else if(!strncmp(strType, "S64", 3)) {
+        mdType = PS_DATA_S64;
+    } else if(!strncmp(strType, "U8", 2)) {
+        mdType = PS_DATA_U8;
+    } else if(!strncmp(strType, "U16", 3)) {
+        mdType = PS_DATA_U16;
+    } else if(!strncmp(strType, "U32", 3)) {
+        mdType = PS_DATA_U32;
+    } else if(!strncmp(strType, "U64", 3)) {
+        mdType = PS_DATA_U64;
+    } else if(!strncmp(strType, "F32", 3)) {
+        mdType = PS_DATA_F32;
+    } else if(!strncmp(strType, "F64", 3)) {
+        mdType = PS_DATA_F64;
+    } else if(!strncmp(strType, "MULTI", 5)) {
+        mdType = PS_DATA_METADATA_MULTI;
+    } else if(!strncmp(strType, "METADATA", 8)) {
+        mdType = PS_DATA_METADATA;
+    } else if( !strncmp(strType, "UTC", 3) || !strncmp(strType, "TAI", 3)
+               || !strncmp(strType, "UT1", 3) || !strncmp(strType, "TT", 3)) {
+        mdType = PS_DATA_TIME;
+    } else {
+        // Search through user types
+        // Check if type already exists in typeTempaltes
+        psHash *typeTemplates = ((p_psParseLevelInfo*)(levelArray->data[level]))->typeTemplates;
+
+        psMetadata *template = psHashLookup(typeTemplates, strType);
+        if (!template) {
+            psError(PS_ERR_IO, true,
+                    _("Metadata TYPE '%s' is invalid."), strType);
+            psFree(strType);
+            return false;
+        }
+
+        // covert the template, and the rest of the line, into a metadata
+        tempMeta = parseTypeValues(template, linePtr);
+        if (!tempMeta) {
+            // Metadata type read error
+            psError(PS_ERR_IO,true,
+                    _("Failed to read Metadata TYPE %s."), keyName);
+            psFree(strType);
+            return false;
+        }
+
+        // get the current level's metadata
+        md = ((p_psParseLevelInfo*)(levelArray->data[level]))->metadata;
+
+        // add the parsed TYPE to it
+        addStatus = psMetadataAdd(md,PS_LIST_TAIL,keyName,
+                PS_DATA_METADATA | flags,"",tempMeta);
+        psFree(tempMeta);
+
+        // Check for add failure
+        if (! addStatus) {
+            psError(PS_ERR_IO, true,
+                    _("Duplicate Metadata item, %s, found.  "
+                     "Overwrite not allowed."), keyName);
+            psFree(strType);
+            return false;
+        }
+
+        psFree(strComment);
+        psFree(strValue);
+        psFree(strType);
+
+        return true;
+    }
+
+    // If type is MULTI or META then check for the (optional) directives UPDATE or RESET;
+    // otherwise, get the value and comment.
+    // line may have the following forms:
+    // NAME METADATA
+    // NAME METADATA # Comment
+    // NAME METADATA#Comment
+    // NAME METADATA UPDATE # Comment
+    // NAME METADATA RESET # Comment
+    // NAME METADATA UPDATE
+    // NAME METADATA RESET
+    if((mdType == PS_DATA_METADATA_MULTI) || (mdType == PS_DATA_METADATA)) {
+
+        // Get the metadata directive if there is one.
+        status = 0;
+        strValue = getToken (&linePtr, "#", &status, true);
+       
+        if (!status && strValue) {
+	    // found a directive, what does it say?
+	    if (strcasecmp (strValue, "UPDATE") && strcasecmp (strValue, "RESET")) {
+		psError(PS_ERR_IO, true, _("Invalid directive %s for METADATA or MULTI."), strValue);
+		psFree(strType);
+		psFree(strValue);
+		return false;
+	    }
+
+	    // found a directive, what does it say?
+	    if (!strcasecmp (strValue, "UPDATE")) {
+		// this folder or group is merged with an existing one of the same name
+		flags |= PS_META_UPDATE_FOLDER;
+	    }
+	    if (!strcasecmp (strValue, "RESET")) {
+		// this folder or group replaces an existing one of the same name
+		flags |= PS_META_REPLACE;
+	    }
+	    psFree(strValue);
+	    strValue = NULL;
+	}
+
+        // Not all lines will have comments, so NULL is ok.
+        status = 0;
+
+        // XXX this is a very ugly way of finding from the current position to
+        // the end of the line
+        strComment = getToken(&linePtr, "", &status, true);
+
+        if (status) {
+            psError(PS_ERR_IO, true, _("Error reading metadata line"));
+            psFree(strType);
+            psFree(strComment);
+            return false;
+        }
+    } else {
+        // Get the metadata item value if there is one.
+        status = 0;
+        strValue = getToken(&linePtr, "#", &status,true);
+
+        if(status) {
+            psError(PS_ERR_IO, true, _("Failed to read a metadata value."));
+            psFree(strType);
+            psFree(strValue);
+            return false;
+        }
+
+        if(strValue==NULL) {
+            psError(PS_ERR_IO, true, _("Failed to read a metadata value."));
+            psFree(strType);
+            psFree(strValue);
+            return false;
+        }
+        // Not all lines will have comments, so NULL is ok.
+        status = 0;
+        // XXX this is a very ugly way of finding from the current position to
+        // the end of the line
+        strComment = getToken(&linePtr, "", &status, true);
+        if(status) {
+            psError(PS_ERR_IO, true, _("Failed to read a metadata comment"));
+            psFree(strType);
+            psFree(strValue);
+            psFree(strComment);
+            return false;
+        }
+    } 
+
+#define PARSE_ADD_CASE(NAME, TYPE, PARSEFUNC) \
+  case PS_DATA_##NAME: { \
+      ps##TYPE value = PARSEFUNC(strValue, &status); \
+      if (!status) { \
+          addStatus = psMetadataAdd##TYPE(md, PS_LIST_TAIL, keyName, flags, strComment, value); \
+      } else { \
+          psError(PS_ERR_IO, true, \
+                  _("Failed to parse the value '%s' of metadata item %s, type %s."), \
+                  strValue, keyName, strType); \
+          returnValue = false; \
+      } \
+      break; \
+  }
+
+    // Need to add item to metadata so get pointer to metadata
+    status = 0;
+    md = ((p_psParseLevelInfo*)(levelArray->data[level]))->metadata;
+    nonUniqueKeys = ((p_psParseLevelInfo*)(levelArray->data[level]))->nonUniqueKeyArray;
+    switch(mdType) {
+        PARSE_ADD_CASE(BOOL,   Bool,   parseBool);
+        PARSE_ADD_CASE(F32,    F32,    parseDouble);
+        PARSE_ADD_CASE(F64,    F64,    parseDouble);
+        PARSE_ADD_CASE(S8,     S8,     parseSignedInt);
+        PARSE_ADD_CASE(S16,    S16,    parseSignedInt);
+        PARSE_ADD_CASE(S32,    S32,    parseSignedInt);
+        PARSE_ADD_CASE(S64,    S64,    parseSignedInt);
+        PARSE_ADD_CASE(U8,     U8,     parseUnsignedInt);
+        PARSE_ADD_CASE(U16,    U16,    parseUnsignedInt);
+        PARSE_ADD_CASE(U32,    U32,    parseUnsignedInt);
+        PARSE_ADD_CASE(U64,    U64,    parseUnsignedInt);
+      case PS_DATA_STRING:
+        // map "NULL" strings to NULL
+        if (strcasecmp(strValue, "null") == 0) {
+            addStatus = psMetadataAddStr(md, PS_LIST_TAIL, keyName, flags, strComment, NULL);
+        } else {
+            addStatus = psMetadataAddStr(md, PS_LIST_TAIL, keyName, flags, strComment, strValue);
+        }
+        break;
+      case PS_DATA_TIME: {
+          psTimeType timeType = PS_TIME_TAI;
+          if(!strncmp(strType, "UTC", 3)) {
+              timeType = PS_TIME_UTC;
+          } else if(!strncmp(strType, "TAI", 3)) {
+              timeType = PS_TIME_TAI;
+          } else if(!strncmp(strType, "UT1", 3)) {
+              timeType = PS_TIME_UT1;
+          } else if(!strncmp(strType, "TT", 3)) {
+              timeType = PS_TIME_TT;
+          }
+
+          psTime *mTime = parseTime(strValue, timeType, &status);
+          if(!status) {
+              addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                        mdType | flags,
+                                        strComment, mTime);
+          } else {
+              psError(PS_ERR_IO, true,
+                      _("Failed to parse the value '%s' of metadata item %s, type %s."),
+                      strValue, keyName, strType);
+              returnValue = false;
+          }
+          psFree(mTime);
+          break;
+      }
+      case PS_DATA_VECTOR: {
+          psVector *tempVec = parseVector(strValue, vectorType, &status);
+          if(!status) {
+              addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName+1,
+                                        mdType | flags,
+                                        strComment, tempVec);
+          } else {
+              psError(PS_ERR_IO, true,
+                      _("Failed to parse the value '%s' of metadata item %s, type %s."),
+                      strValue, keyName, strType);
+              returnValue = false;
+          }
+          psFree(tempVec);
+          break;
+      }
+    case PS_DATA_METADATA_MULTI:
+        // Add key to non-unique array of keys
+        // Check for duplicate MULTI lines
+
+      // XXX currently, we only place the name of the MULTI on this list.  we thus lose
+      // the associated comment (if any) and the flags (if any) we could probably fix this
+      // behavior by allowing psMetadataAddItem to be passed an empty MULTI, which would
+      // have a null data pointer until an element is added (in other words, treat MULTI
+      // as another type of folder, but without a link of its own on the metadata->list
+
+        addStatus = true;
+        for(psS32 k=0; k < nonUniqueKeys->n; k++) {
+            if(strcmp(keyName,(char*)nonUniqueKeys->data[k]) == 0) {
+                psError(PS_ERR_IO,true,_("Duplicate MULTI specifier."));
+                psFree(strType);
+                return false;
+            }
+        }
+        psString tempStr = psStringCopy(keyName);
+        nonUniqueKeys = psArrayAdd(nonUniqueKeys,0,tempStr);
+        addStatus = true;
+        psFree(tempStr);
+        break;
+    case PS_DATA_METADATA: {
+            // check to see if this keyname already exists and is allowed as a
+            // MULTI.  If we don't do this check first, it's possible that we
+            // can create a new "scope" yet fail to add the new metdata.
+            psMetadataItem *item = psMetadataLookup (md, keyName);
+            if ((item != NULL) &&
+                    ((item->type != PS_DATA_METADATA_MULTI) &&
+                    ((flags & PS_META_REPLACE) == 0))
+            ) {
+                psError(PS_ERR_IO, true, _("Duplicate Metadata declaration: %s is not allowed without 'overwrite' or MULTI specifier."), keyName);
+                break;
+            }
+
+            // create the nested metadata
+            psMetadata *newScope = psMetadataAlloc();
+
+            // Create next level info
+            nextLevelInfo = p_psParseLevelInfoAlloc();
+
+
+            // try to add the new metdata to the current one
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                      mdType | flags,
+                                      strComment, newScope);
+
+            if (addStatus) {
+                // switch to the scope to the new metadata
+                nextLevelInfo->metadata = psMemIncrRefCounter(newScope);
+
+                // Add next level to levelArray
+                levelArray = psArrayAdd(levelArray, 0, nextLevelInfo);
+            }
+
+            psFree(nextLevelInfo);
+            psFree(newScope);
+
+            break;
+        }
+    default:
+        psError(PS_ERR_IO, true,
+                _("Metadata of unknown type found."));
+        break;
+    }
+
+    // Check if the add status was successful
+    if (! addStatus) {
+        returnValue = false;
+    }
+
+    psFree(strComment);
+    psFree(strValue);
+    psFree(strType);
+
+    return returnValue;
+}
+
+static bool parseLine(psArray *levelArray,
+                      char *linePtr,
+                      bool overwrite,
+                      bool *notBlank)
+{
+    psMetadataFlags     flags  = PS_META_DEFAULT;
+
+    // Set flags if overwrite specified
+    if (overwrite) {
+        flags = PS_META_REPLACE;
+    }
+
+    // If line is a comment or blank, then extract data
+    if (ignoreLine(linePtr)) {
+        // do nothing and return
+        if (notBlank) {
+            *notBlank = false;
+        }
+        return true;
+    }
+
+    // even if it's a "bad line" we know it can't be "blank" after this point
+    *notBlank = true;
+
+    // Check for more than one '@' in a line
+    // XXX this logic is wrong -- there's no reason a @ can't appear in a
+    // comment
+    if (repeatedChars(linePtr, '@') > 1) {
+        psError(PS_ERR_IO, true,
+                _("More than one '@' character is not allowed"));
+        return false;
+    }
+
+    // Get metadata item name
+    psS32               status = 0;
+    char *keyName = getToken(&linePtr, " ", &status, true);
+    if (status) {
+        psError(PS_ERR_IO, true, _("Failed to read item key name on line"));
+        psFree(keyName);
+        return false;
+    }
+
+    // Check for special keyName values "TYPE", "END"
+    if (strcmp(keyName, "END") == 0) {
+        if (!parseMetadataEnd(keyName, levelArray, linePtr, flags)) {
+            goto FAIL;
+        }
+    } else if (strcmp(keyName,"TYPE") == 0 ) {
+        if (!parseType(keyName, levelArray, linePtr, flags)) {
+            goto FAIL;
+        }
+    } else {
+        if (!parseGeneric(keyName, levelArray, linePtr, flags)) {
+            goto FAIL;
+        }
+    }
+
+    psFree(keyName);
+
+    return true;
+
+FAIL:
+    psError(PS_ERR_UNKNOWN, false, _("Failed to parse line"));
+    psFree(keyName);
+    return false;
+}
+
+static bool parseGeneric(char *keyName,
+                         psArray *levelArray,
+                         char *linePtr,
+                         psMetadataFlags flags)
+{
+    PS_ASSERT_STRING_NON_EMPTY(keyName, false);
+    PS_ASSERT_ARRAY_NON_NULL(levelArray, false);
+    PS_ASSERT_PTR_NON_NULL(linePtr, false);
+
+    long level = psArrayLength(levelArray) - 1;
+
+    // Check if key name present in array of non-unique key names
+    psS32 limit = ((p_psParseLevelInfo*)(levelArray->data[level]))->nonUniqueKeyArray->n;
+    for (psS32 k = 0; k < limit; k++) {
+        char *name = (char*)((p_psParseLevelInfo*)
+                             (levelArray->data[level]))->nonUniqueKeyArray->data[k];
+        if (strcmp(name, keyName) == 0) {
+            flags = PS_META_DUPLICATE_OK;
+        }
+    }
+
+    // Parse metadataItem
+    if (!parseMetadataItem(keyName, levelArray, linePtr, flags)) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool parseType(char *keyName,
+                      psArray *levelArray,
+                      char *linePtr,
+                      psMetadataFlags flags)
+{
+    PS_ASSERT_STRING_NON_EMPTY(keyName, false);
+    PS_ASSERT_ARRAY_NON_NULL(levelArray, false);
+    PS_ASSERT_PTR_NON_NULL(linePtr, false);
+
+    long level = psArrayLength(levelArray) - 1;
+
+    // Get the type name
+    psS32 status = 0;
+    char *strType = getToken(&linePtr, " ", &status, true);
+    if(!strType) {
+        psError(PS_ERR_IO,true, _("Failed to read item type."));
+        return false;
+    }
+    if (status) {
+        psError(PS_ERR_IO, true, _("Failed to read item type."));
+        psFree(strType);
+        return false;
+    }
+
+    // Access typeTypes for this level/scope
+    psHash *typeTemplates = ((p_psParseLevelInfo*)(levelArray->data[level]))->typeTemplates;
+
+    // Check if type already exists in typeTempaltes
+    if (psHashLookup(typeTemplates, strType)) {
+        psError(PS_ERR_IO, true,
+            _("Specified type %s is already defined."), strType);
+        psFree(strType);
+        return false;
+    }
+
+    // attempt to parse the TYPE line into a template metadata
+    psMetadata *tempTemplate = genTypeTemplate(linePtr);
+    if (!tempTemplate) {
+        psError(PS_ERR_IO, true, _("Metadata type '%s' is invalid."), strType);
+        psFree(strType);
+        return false;
+    }
+
+    // Add key name to array of type
+    // Add template to hash of templates
+    if (!psHashAdd(typeTemplates, strType, tempTemplate)) {
+        psError(PS_ERR_UNKNOWN, false, _("failed to add template for '%s' to hash"), strType);
+        psFree(tempTemplate);
+        psFree(strType);
+        return false;
+    }
+
+    psFree(tempTemplate);
+    psFree(strType);
+
+    return true;
+}
+
+static bool parseMetadataEnd(char *keyName,
+                             psArray *levelArray,
+                             char *linePtr,
+                             psMetadataFlags flags)
+{
+    PS_ASSERT_STRING_NON_EMPTY(keyName, false);
+    PS_ASSERT_ARRAY_NON_NULL(levelArray, false);
+    PS_ASSERT_PTR_NON_NULL(linePtr, false);
+
+    long level = psArrayLength(levelArray) - 1;
+
+    if ((level) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "can not END the top level metadata");
+        return false;
+    }
+
+    // the END just moved us up one nesting level
+    // Remove lower info level
+    if(!psArrayRemoveIndex(levelArray, level)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to remove array item");
+        return false;
+    }
+
+    return true;
+}
+
+psMetadata *psMetadataConfigRead(psMetadata *md,
+                                 unsigned int *nFail,
+                                 const char *filename,
+                                 bool overwrite)
+{
+    // Check for NULL file name
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+
+    psString file = psSlurpFilename(filename);
+
+    md = psMetadataConfigParse(md, nFail, (char *)file, overwrite);
+    if (!md) {
+        psError(PS_ERR_IO, true, _("failed to parse file '%s'"), filename);
+        psFree(md);
+	psFree(file);
+        return NULL;
+    }
+
+    psFree(file);
+    return md;
+}
+
+psMetadata* psMetadataConfigParse(psMetadata* md,
+                                  unsigned int *nFail,
+                                  const char *str,
+                                  bool overwrite)
+{
+    bool allocedMD = false;
+
+    PS_ASSERT_STRING_NON_EMPTY(str, NULL);
+
+    // Initialise nFail, if provided
+    if (nFail) {
+        *nFail = 0;
+    }
+
+    // Allocate metadata if necessary
+    if (md == NULL) {
+        allocedMD = true;
+        md = psMetadataAlloc();
+    }
+
+    // split the input string into an array of lines
+    psList *doc = psStringSplit(str, "\n", true);
+    if (!doc) {
+        psError(PS_ERR_UNKNOWN, false, "failed to split string: %s", str);
+        if (allocedMD) {
+            psFree(md);
+        }
+        return NULL;
+    }
+
+    // accept completely empty strings
+    // nFail == 0 / nPass == 0 - OK
+    if (psListLength(doc) == 0) {
+        psTrace("psLib.types", PS_LOG_INFO, "string contained no lines");
+        psFree(doc);
+        return md;
+    }
+
+    // Allocate array to store parse level information
+    psArray *parseLevelInfoArray = psArrayAllocEmpty(INITIAL_LENGTH);
+
+    // Set parse level info for the top level
+    p_psParseLevelInfo *topLevelInfo = p_psParseLevelInfoAlloc();
+    topLevelInfo->metadata = psMemIncrRefCounter(md);
+    psArrayAdd(parseLevelInfoArray, 0, topLevelInfo);
+    psFree(topLevelInfo);
+
+    // clear the error stack so we can call psError(..., false, ...) and let
+    // errors from the parse loop accumulate
+    psErrorClear();
+
+    // line type counts
+    long nLines = 0;                // all lines
+    long nGood  = 0;                // valid lines excluding blank/comment
+    long nBad   = 0;                // invalid lines
+
+    // While loop to parse the file
+    char *line = NULL;
+    psListIterator *iter = psListIteratorAlloc(doc, 0, false);
+    while ((line = psListGetAndIncrement(iter))) {
+        nLines++; // indexed from 1
+
+        bool notBlank = false;
+        if (!parseLine(parseLevelInfoArray, line, overwrite, &notBlank)) {
+            nBad++;
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Error parsing line #%lu : %s", nLines, line);
+        } else if (notBlank) {
+        // do not count blank/comment lines as "good" lines
+            nGood++;
+        }
+    }
+
+    psFree(iter);
+    psFree(doc);
+
+    // Free parse array and line buffer
+    psFree(parseLevelInfoArray);
+
+    // nFail > 0 / nPass == 0 - NULL
+    if ((nBad > 0) && (nGood == 0)) {
+        psError(PS_ERR_UNKNOWN, false, "string contained no data lines and %ld bad lines", nBad);
+        psFree(md);
+        return NULL;
+    }
+
+    // pass back the number of failed lines
+    if (nFail) {
+        *nFail = nBad;
+    }
+
+    // nFail == 0 / nPass > 0
+    // nFail > 0 / nPass > 0
+    return md;
+}
+
+psString psMetadataConfigFormat(psMetadata *md)
+{
+    PS_ASSERT_METADATA_NON_NULL(md, NULL);
+
+    psString format = psStringCopy("");
+
+    psArray *keys = p_psMetadataKeyArray(md);
+    for (long i = 0; i < psArrayLength(keys); i++) {
+        psMetadataItem *item = psMetadataLookup(md, keys->data[i]);
+        if (!item) {
+            // XXX : this is probably not the right error value
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Specified psDataType, %d, is not supported."), PS_DATA_UNKNOWN);
+            psTrace("psLib.types", 5, "failed to find key %s\n", (char *) keys->data[i]);
+            psFree(keys);
+            psFree(format);
+            return NULL;
+        }
+        psString str = formatMetadataItem(item);
+        if (!str) {
+            psError(PS_ERR_UNKNOWN, false, "failed to format psMetadataItem");
+            psTrace("psLib.types", 5, "failed to format %s\n", (char *) keys->data[i]);
+            psFree(keys);
+            psFree(format);
+            psFree(str);
+            return NULL;
+        }
+        psStringAppend(&format, "%s", str);
+        psFree(str);
+    }
+
+    psFree(keys);
+
+    return format;
+}
+
+
+static psString formatMetadataItem(psMetadataItem *item)
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, NULL);
+
+    psString content = NULL;
+
+    #define FORMAT_PRIMITIVE_METADATAITEM(type, dataformat) \
+    psStringAppend(&content, "%-15s  %-8s  %-15" dataformat, \
+                   item->name, #type, item->data.type); \
+    if (item->comment && strncmp(item->comment, "", 2)) { \
+        psStringAppend(&content, "  # %s", item->comment); \
+    } \
+    psStringAppend(&content, "\n");
+
+    // In this block, the single item is used to build 'content'
+    switch (item->type) {
+    case PS_DATA_METADATA_MULTI: {
+            psStringAppend(&content, "%s MULTI\n", item->name);
+
+            // a MULTI is a list of items so we need to recurse through the
+            // list
+            psListIterator *iter = psListIteratorAlloc(item->data.list, 0, false);
+            psMetadataItem *multiItem = NULL;
+            while ((multiItem = psListGetAndIncrement(iter))) {
+                psString str = formatMetadataItem(multiItem);
+                psStringAppend(&content, str);
+                psFree(str);
+            }
+            psFree(iter);
+        }
+        break;
+    case PS_DATA_BOOL:
+        psStringAppend (&content, "%-15s  %-8s  %-15s",
+                        item->name, "BOOL", item->data.B ? "T" : "F");
+        if (item->comment && strncmp(item->comment, "", 2)) {
+            psStringAppend(&content, "  # %s", item->comment);
+        }
+        psStringAppend(&content, "\n");
+        break;
+    case PS_DATA_S8:
+        FORMAT_PRIMITIVE_METADATAITEM(S8, "d");
+        break;
+    case PS_DATA_S16:
+        FORMAT_PRIMITIVE_METADATAITEM(S16, "d");
+        break;
+    case PS_DATA_S32:
+        FORMAT_PRIMITIVE_METADATAITEM(S32, "d");
+        break;
+    case PS_DATA_S64:
+        FORMAT_PRIMITIVE_METADATAITEM(S64, PRId64);
+        break;
+    case PS_DATA_U8:
+        FORMAT_PRIMITIVE_METADATAITEM(U8, "u");
+        break;
+    case PS_DATA_U16:
+        FORMAT_PRIMITIVE_METADATAITEM(U16, "u");
+        break;
+    case PS_DATA_U32:
+        FORMAT_PRIMITIVE_METADATAITEM(U32, "u");
+        break;
+    case PS_DATA_U64:
+        FORMAT_PRIMITIVE_METADATAITEM(U64, PRIu64);
+        break;
+    case PS_DATA_F32:
+        FORMAT_PRIMITIVE_METADATAITEM(F32, ".7g");
+        break;
+    case PS_DATA_F64:
+        FORMAT_PRIMITIVE_METADATAITEM(F64, ".15g");
+        break;
+      case PS_DATA_STRING: {
+          bool valid = false;
+          if (item->data.str && strlen(item->data.str) > 0) {
+              char *p = item->data.str;
+              while (*p && isblank(*p)) p++;
+              if (*p) valid = true;
+          }
+          if (valid) {
+              psStringAppend(&content, "%-15s  %-8s  %-15s",
+                             item->name, "STR", item->data.str);
+          } else {
+              psStringAppend(&content, "%-15s  %-8s  %-15s",
+                             item->name, "STR", "NULL");
+          }
+          if (item->comment && strncmp(item->comment,"",2)) {
+              psStringAppend(&content, "  # %s", item->comment);
+          }
+          psStringAppend(&content, " \n");
+          break;
+      }
+    case PS_DATA_METADATA: {
+            if (item->comment && strncmp(item->comment,"",2)) {
+                psStringAppend(&content, "\n%s  METADATA  # %s", item->name, item->comment);
+            } else {
+                psStringAppend(&content, "\n%s  METADATA  ", item->name);
+            }
+
+            psString newStr = psMetadataConfigFormat(item->data.md);
+            if (newStr) {
+                // add 3 extra spaces to each metadata folder item
+                char *buf = strtok(newStr, "\n");
+                while (buf != NULL) {
+                    psStringAppend(&content, "\n   %s", buf);
+                    buf = strtok(NULL, "\n");
+                }
+                psFree(newStr);
+            }
+
+            psStringAppend(&content, "\nEND\n");
+            break;
+        }
+    case PS_DATA_TIME:
+        psStringAppend(&content, "%-15s  ", item->name);
+        psTime *time = item->data.V;
+        if (time) {
+            switch (time->type) {
+            case PS_TIME_UTC:
+                psStringAppend(&content, "%-8s  ", "UTC");
+                break;
+            case PS_TIME_TAI:
+                psStringAppend(&content, "%-8s  ", "TAI");
+                break;
+            case PS_TIME_UT1:
+                psStringAppend(&content, "%-8s  ", "UT1");
+                break;
+            case PS_TIME_TT:
+                psStringAppend(&content, "%-8s  ", "TT");
+                break;
+            default:
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                        _("Specified psTime type, %d, is not supported."),
+                        time->type);
+                psFree(content);
+                return NULL;
+            }
+            psString timeStr = psTimeToISO(time);
+            psString timeOutput;
+            timeOutput = psStringNCopy(timeStr, 19);
+            psStringAppend(&content, "%s", timeOutput);
+            psStringAppend(&content, "Z");
+            psFree(timeStr);
+            psFree(timeOutput);
+        } else {
+            // psTime is a NULL pointer
+            psStringAppend(&content, "%-8s  %-15s", "TAI", "NULL");
+        }
+
+        if (item->comment && strncmp(item->comment,"",2)) {
+            psStringAppend(&content, "  # %s", item->comment);
+        }
+        psStringAppend(&content, "\n");
+        break;
+    case PS_DATA_VECTOR:
+        psStringAppend(&content, "@%s ", item->name);
+        psVector *vector = item->data.V;
+
+        switch (vector->type.type) {
+        case PS_DATA_U8:
+            psStringAppend(&content, "U8 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%u ", vector->data.U8[i]);
+            }
+            break;
+        case PS_DATA_U16:
+            psStringAppend(&content, "U16 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%u ", vector->data.U16[i]);
+            }
+            break;
+        case PS_DATA_U32:
+            psStringAppend(&content, "U32 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%u ", vector->data.U32[i]);
+            }
+            break;
+        case PS_DATA_U64:
+            psStringAppend(&content, "U64 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%" PRIu64 " ", vector->data.U64[i]);
+            }
+            break;
+        case PS_DATA_S8:
+            psStringAppend(&content, "S8 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%d ", vector->data.S8[i]);
+            }
+            break;
+        case PS_DATA_S16:
+            psStringAppend(&content, "S16 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%d ", vector->data.S16[i]);
+            }
+            break;
+        case PS_DATA_S32:
+            psStringAppend(&content, "S32 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%d ", vector->data.S32[i]);
+            }
+            break;
+        case PS_DATA_S64:
+            psStringAppend(&content, "S64 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%" PRId64 " ", vector->data.S64[i]);
+            }
+            break;
+        case PS_DATA_F32:
+            psStringAppend(&content, "F32 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%.7g ", vector->data.F32[i]);
+            }
+            break;
+        case PS_DATA_F64:
+            psStringAppend(&content, "F64 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%.15g ", vector->data.F64[i]);
+            }
+            break;
+        case PS_DATA_UNKNOWN:
+        default:
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Specified psDataType, %d, is not supported."), vector->type.type);
+            psFree(content);
+            return NULL;
+        }
+        if (item->comment && strncmp(item->comment, "", 2)) {
+            psStringAppend(&content, "  # %s", item->comment);
+        }
+        psStringAppend(&content, "\n");
+        break;
+    case PS_DATA_UNKNOWN:
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified psDataType, %d, is not supported."), item->type);
+        psFree(content);
+        return NULL;
+    }
+
+    return content;
+}
+
+
+static psArray *p_psMetadataKeyArray(psMetadata *md)
+{
+    PS_ASSERT_METADATA_NON_NULL(md, NULL);
+
+    psArray *keys = psArrayAllocEmpty(psListLength(md->list));
+
+    // since we want to preserve the order of the keys in the metadata we can't
+    // get a list of key names from the metadata's hash table.  Instead we have
+    // to iterate through the metadata's list of items and reject duplicate key
+    // names.
+
+    psMetadataItem *item = NULL;
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
+OUTSIDE:
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        for (long i = 0; i < psArrayLength(keys); i++) {
+            // does this element have a value
+            psString elem = keys->data[i];
+            if (elem) {
+                if(strcmp(elem, item->name) == 0) {
+                    goto OUTSIDE;
+                }
+            }
+        }
+        psString string = psStringCopy(item->name);
+        psArrayAdd(keys, 0, string);
+        psFree(string);
+    }
+    psFree(iter);
+    return keys;
+}
+
+
+bool psMetadataConfigWrite(psMetadata *md,
+                           const char *filename)
+{
+    PS_ASSERT_METADATA_NON_NULL(md, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    FILE *file;
+    if ( !(file = fopen(filename, "w")) ) {
+        psError(PS_ERR_IO, true,
+                "Failed to open specified file, %s\n", filename);
+        return false;
+    }
+    psString fileString = NULL;
+    fileString = psMetadataConfigFormat(md);
+    if (fileString == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psMetadataConfigFormat returned NULL.\n");
+        return false;
+    }
+    fprintf(file, "%s", fileString);
+    psFree(fileString);
+    if (fclose(file) == EOF) {
+        psError(PS_ERR_IO, true,
+                "Failed to close file, %s\n", filename);
+        return false;
+    }
+    return true;
+}
+
+bool psMetadataConfigPrint(FILE *stream,
+                           psMetadata *md)
+{
+    PS_ASSERT_METADATA_NON_NULL(md, false);
+    PS_ASSERT_PTR_NON_NULL(stream, false);
+    if (fprintf(stream, "\n") <= 0) {
+        psError(PS_ERR_IO, false,
+                "Unable to write to specified file.");
+        return false;
+    }
+
+    psString str = psMetadataConfigFormat(md);
+    if (!str) {
+        psError(PS_ERR_UNKNOWN, false,
+                _("failed to format metadata into a string."));
+        return false;
+    }
+
+    fprintf(stream, "%s", str);
+    psFree(str);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataConfig.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataConfig.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataConfig.h	(revision 22322)
@@ -0,0 +1,101 @@
+/** @file  psMetadataConfig.h
+ *
+ *  @brief Contains metadata input/output functions.
+ *
+ *  This file defines functions to read and write metadata to/from an external file.
+ *
+ *  @author EAM, IfA
+ *  @author PAP, IfA
+ *  @author JH, IfA
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.26 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-01-23 22:47:23 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_METADATACONFIG_H
+#define PS_METADATACONFIG_H
+
+#include "psMetadata.h"
+
+/// @addtogroup DataContainer Data Containers
+/// @{
+
+/** Print metadata item to file.
+ *
+ *  Metadata items may be printed to an open file descriptor based on a
+ *  provided format. The format is a sprintf format statement with exactly
+ *  one % formatting command. If the metadata item type is a numeric type,
+ *  this formatting command must also be numeric, and the type conversion
+ *  performed to the value to match the format type. If the metadata type is
+ *  a string, the formatting command must also be for a string. If the
+ *  metadata type is any other data type, printing is not allowed.
+ *
+ * @return psMetadataItem* :    Pointer metadata item.
+ */
+bool psMetadataItemPrint(
+    FILE * fd,                         ///< Pointer to file to write metadata item.
+    const char *format,                ///< Format to print metadata item.
+    const psMetadataItem* item         ///< Metadata item to print.
+);
+
+/** Read metadata configuration file.
+ *
+ *  Loads pre-defined settings by parsing a configuration file into a psMetadata structure.
+ *
+ *  @return psMetadata* : Resulting metadata from read.
+ */
+psMetadata* psMetadataConfigRead(
+    psMetadata* md,                    ///< Resulting metadata from read.
+    unsigned int *nFail,               ///< Number of failed lines.
+    const char *filename,              ///< Name of file to read.
+    bool overwrite                     ///< Allow overwrite of duplicate specifications.
+);
+
+/** Parse metadata configuration string.
+ *
+ *  Loads pre-defined settings by parsing a string into a psMetadata structure.
+ *
+ *  @return psMetadata* : Resulting metadata from parse.
+ */
+psMetadata* psMetadataConfigParse(
+    psMetadata* md,                    ///< Resulting metadata from read.
+    unsigned int *nFail,               ///< Number of failed lines.
+    const char *str,                   ///< String to process.
+    bool overwrite                     ///< Allow overwrite of duplicate specifications.
+);
+
+/** Converts a psMetadata structure (including any nested psMetadata) into a
+ *  configuration file formatted string.
+ *
+ *  A NULL shall be returned on error.
+ *  @return psString:       a Configuration File formatted string.
+ */
+psString psMetadataConfigFormat(
+    psMetadata *md                     ///< The metadata to convert
+);
+
+/** Converts a psMetadata structure (including any nested psMetadata) into a
+ *  configuration file formatted string that is written out to filename.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psMetadataConfigWrite(
+    psMetadata *md,                    ///< The metadata to convert
+    const char *filename               ///< Name of file to write
+);
+
+/** Converts a psMetadata structure (including any nested psMetadata) into a
+ *  configuration file formatted string that is written a file stream.
+ *
+ *  @return bool:       True if successful, otherwise false.
+ */
+bool psMetadataConfigPrint(
+    FILE *stream,                       ///< file stream to write to
+    psMetadata *md                      ///< The metadata to convert
+);
+
+/// @}
+#endif // #ifndef PS_METADATAIO_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemCompare.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemCompare.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemCompare.c	(revision 22322)
@@ -0,0 +1,198 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+#include "psType.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psTrace.h"
+#include "psMetadata.h"
+#include "psMetadataItemCompare.h"
+
+typedef enum {
+    PS_MD_OP_EQ,
+    PS_MD_OP_LT,
+    PS_MD_OP_LE,
+    PS_MD_OP_GT,
+    PS_MD_OP_GE,
+    PS_MD_OP_NE
+} psMetadataItemCompareOp;
+
+/* determine boolean operator specified in comment: @OP: X@ (default is ==) */
+psMetadataItemCompareOp p_psMetadataItemCompareGetOp (const psMetadataItem *item) {
+
+    if (!item->comment) return PS_MD_OP_EQ;
+
+    char *p1 = strstr (item->comment, "@OP:");
+    if (!p1) return PS_MD_OP_EQ;
+    p1 += 4; // point to first char after @OP:
+    while (isblank(p1[0])) p1++;
+
+    char *p2 = strchr (p1, '@');
+    if (!p2) return PS_MD_OP_EQ;
+
+    // the string starting at p1 and p2 must contain one of the following:
+    // == or = PS_MD_OP_EQ,
+    // < PS_MD_OP_LT,
+    // <= PS_MD_OP_LE,
+    // > PS_MD_OP_GT,
+    // >= PS_MD_OP_GE,
+    // ! PS_MD_OP_NE
+    
+    // XXX a bit crude: does not catch the case of invalid chars after boolean op
+    if (!strncmp (p1, "==", 2)) return PS_MD_OP_EQ;
+    if (!strncmp (p1, "=",  1)) return PS_MD_OP_EQ;
+    if (!strncmp (p1, "<=", 2)) return PS_MD_OP_LE;
+    if (!strncmp (p1, "<",  1)) return PS_MD_OP_LT;
+    if (!strncmp (p1, ">=", 2)) return PS_MD_OP_GE;
+    if (!strncmp (p1, ">",  1)) return PS_MD_OP_GT;
+
+    return PS_MD_OP_EQ;
+}
+
+// XXX better value for tolerance?
+# define EQ_TOL 1e-6
+
+# define COMPARE_CASE(TEMPLATENAME, TEMPLATETYPE, COMPARENAME, COMPARETYPENAME) \
+  case PS_TYPE_##COMPARETYPENAME: {					 \
+      TEMPLATETYPE valueC = (TEMPLATETYPE)compare->data.COMPARENAME;	 \
+      TEMPLATETYPE valueT = (TEMPLATETYPE)template->data.TEMPLATENAME;			  \
+      /* XXX check the validiy of the type casting? */                 \
+      if (valueC != compare->data.COMPARENAME) {			 \
+	  return false;						 \
+      }								 \
+      /* does template specify a boolean operation in comment? */ \
+      psMetadataItemCompareOp op = p_psMetadataItemCompareGetOp (template);  \
+      switch (op) { \
+	case PS_MD_OP_EQ: \
+	  if ((template->type == PS_TYPE_F32) || (template->type == PS_TYPE_F64)) { \
+	      result = fabs(valueT - valueC) < EQ_TOL; \
+	  } else { \
+	      result = valueT == valueC; \
+	  } \
+	  break; \
+	case PS_MD_OP_LT: \
+	  result = valueC < valueT; \
+	  break; \
+	case PS_MD_OP_LE: \
+	  result = valueC <= valueT; \
+	  break; \
+	case PS_MD_OP_GT: \
+	  result = valueC > valueT; \
+	  break; \
+	case PS_MD_OP_GE: \
+	  result = valueC >= valueT; \
+	  break; \
+	case PS_MD_OP_NE: \
+	  result = valueC != valueT; \
+	  break; \
+	default: \
+	  psAbort ("all cases should have been handled..."); \
+      } \
+      return (result); \
+    }
+
+#define TEMPLATE_CASE(TYPENAME, NAME, TYPE)				\
+    case PS_TYPE_##TYPENAME:						\
+    switch(compare->type) {						\
+        COMPARE_CASE(NAME, TYPE, B  , BOOL);				\
+        COMPARE_CASE(NAME, TYPE, U8 , U8 );				\
+        COMPARE_CASE(NAME, TYPE, U16, U16);				\
+        COMPARE_CASE(NAME, TYPE, U32, U32);				\
+        COMPARE_CASE(NAME, TYPE, S8 , S8 );				\
+        COMPARE_CASE(NAME, TYPE, S16, S16);				\
+        COMPARE_CASE(NAME, TYPE, S32, S32);				\
+        COMPARE_CASE(NAME, TYPE, F32, F32);				\
+        COMPARE_CASE(NAME, TYPE, F64, F64);				\
+      default:								\
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Don't know how to compare types %x and %x\n", \
+                compare->type, template->type);				\
+        return false;							\
+    }
+
+
+bool psMetadataItemCompare(const psMetadataItem *compare, // Item to compare to the template
+                           const psMetadataItem *template) // The template
+{
+
+    // First order checks:
+
+    // both items must exist
+    if (! compare || ! template) {
+        return false;
+    }
+
+    // the names of both items must match
+    if (strcmp(compare->name, template->name)) {
+        return false;
+    }
+
+    bool result = false;
+
+    switch (template->type) {
+        TEMPLATE_CASE(BOOL, B,   bool) ;
+        TEMPLATE_CASE(U8,   U8,  psU8 );
+        TEMPLATE_CASE(U16,  U16, psU16);
+        TEMPLATE_CASE(U32,  U32, psU32);
+        TEMPLATE_CASE(S8,   S8,  psS8 );
+        TEMPLATE_CASE(S16,  S16, psS16);
+        TEMPLATE_CASE(S32,  S32, psS32);
+        TEMPLATE_CASE(F32,  F32, psF32);
+        TEMPLATE_CASE(F64,  F64, psF64);
+
+    case PS_DATA_STRING:
+
+        // for MULTI, try each one & succeed if any match (valid = true is default state)
+        if (compare->type == PS_DATA_METADATA_MULTI) {
+            for (int j = 0; j < compare->data.list->n; j++) {
+                psMetadataItem *entry = psListGet (compare->data.list, j);
+		if (!entry) continue;
+		if (entry->type != PS_DATA_STRING) {
+		  continue;
+		}
+		if (template->data.V && !entry->data.V) { // expecting valid data, found NULL
+		  continue;
+		}
+		if (!template->data.V && entry->data.V) { // expecting NULL, found valid data
+		  continue;
+		}
+		// XXX should we return true for both NULL?
+		if (!template->data.V && !entry->data.V) { // expecting NULL, found NULL
+		  return true;
+		}
+		if (!strcasecmp(entry->data.V, template->data.V)) {
+		  return true;
+		}
+		// XXX this is a hack : also compare with the comment field (for HISTORY, COMMENT)
+		if (!strcasecmp(entry->comment, template->data.V)) {
+		  return true;
+		}
+            }
+	    return false;
+        }
+
+	// any other (non-MULTI) is a mis-match
+        if (compare->type != PS_DATA_STRING) {
+            return false;
+        }
+        psTrace("psLib.types", 10, "Comparing '%s' with '%s'\n", compare->data.str, template->data.str);
+        if ((!compare->data.V && template->data.V) || (!template->data.V && compare->data.V)) {
+            return false;
+        }
+        return (strcasecmp(compare->data.V, template->data.V) == 0) ? true : false;
+
+    default:
+        // Simply don't know how to compare more complex types.
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Don't know how to compare types %x and %x\n",
+                compare->type, template->type);
+        return false;
+    }
+
+    psAbort("Should never get here.\n");
+    return false;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemCompare.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemCompare.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemCompare.h	(revision 22322)
@@ -0,0 +1,34 @@
+/** @file  psMetadataItemCompare.h
+ *
+ *  @brief Compares Metadata Items
+ *
+ *  This file defines functions to compare psMetadataItem's.
+ *
+ *  @author IFA
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-01-23 22:47:23 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_METADATA_ITEM_COMPARE_H
+#define PS_METADATA_ITEM_COMPARE_H
+
+#include "psMetadata.h"
+
+/// @addtogroup DataContainer Data Containers
+/// @{
+
+/** Compares two psMetadataItems.
+ *
+ *  @return bool:       True if compare matches template, otherwise false.
+ */
+bool psMetadataItemCompare(
+    const psMetadataItem *compare,     ///< Item to compare to the template
+    const psMetadataItem *template     ///< The template
+)
+;
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemParse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemParse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemParse.c	(revision 22322)
@@ -0,0 +1,288 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "psMemory.h"
+#include "psAssert.h"
+#include "psError.h"
+#include "psMetadata.h"
+#include "psMetadataItemParse.h"
+
+# define PS_METADATA_ITEM_PARSE_NUMBER(INTYPE,OUTTYPE) \
+case PS_DATA_##INTYPE: \
+return (ps##OUTTYPE)(item->data.INTYPE); \
+
+
+// NOTE: This function flows through so that errors may be handled by the "default" case.
+#define PS_METADATA_ITEM_PARSE_STRING_FLOAT(OUTTYPE,FUNCTION) \
+case PS_DATA_STRING: { \
+    char *end = NULL; \
+    ps##OUTTYPE number = FUNCTION(item->data.V, &end); \
+    if (end != item->data.V) { \
+        return number; \
+    } \
+}
+
+// NOTE: This function flows through so that errors may be handled by the "default" case.
+#define PS_METADATA_ITEM_PARSE_STRING_INT(OUTTYPE,FUNCTION) \
+case PS_DATA_STRING: { \
+    char *end = NULL; \
+    ps##OUTTYPE number = FUNCTION(item->data.V, &end, 10); \
+    if (end != item->data.V) { \
+        return number; \
+    } \
+}
+
+bool psMetadataItemParseBool(const psMetadataItem *item
+                              )
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, false);
+
+    switch (item->type) {
+        PS_METADATA_ITEM_PARSE_NUMBER(F32, BOOL);
+        PS_METADATA_ITEM_PARSE_NUMBER(F64, BOOL);
+        PS_METADATA_ITEM_PARSE_NUMBER(S8,  BOOL);
+        PS_METADATA_ITEM_PARSE_NUMBER(S16, BOOL);
+        PS_METADATA_ITEM_PARSE_NUMBER(S32, BOOL);
+        PS_METADATA_ITEM_PARSE_NUMBER(U8,  BOOL);
+        PS_METADATA_ITEM_PARSE_NUMBER(U16, BOOL);
+        PS_METADATA_ITEM_PARSE_NUMBER(U32, BOOL);
+    case PS_DATA_BOOL:
+        return item->data.B;
+    case PS_DATA_STRING:
+        if (strcasecmp(item->data.V, "true") == 0) {
+            return true;
+        } else if (strcasecmp(item->data.V, "false") == 0) {
+            return false;
+        }
+        // Flow through
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Item %s (%s) is not of boolean type (%x) --- "
+                "treating as false.\n", item->name, item->comment, item->type);
+        return false;
+    }
+}
+
+psF32 psMetadataItemParseF32(const psMetadataItem *item
+                            )
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, NAN);
+
+    switch (item->type) {
+        PS_METADATA_ITEM_PARSE_NUMBER(F32, F32);
+        PS_METADATA_ITEM_PARSE_NUMBER(F64, F32);
+        PS_METADATA_ITEM_PARSE_NUMBER(S8,  F32);
+        PS_METADATA_ITEM_PARSE_NUMBER(S16, F32);
+        PS_METADATA_ITEM_PARSE_NUMBER(S32, F32);
+        PS_METADATA_ITEM_PARSE_NUMBER(U8,  F32);
+        PS_METADATA_ITEM_PARSE_NUMBER(U16, F32);
+        PS_METADATA_ITEM_PARSE_NUMBER(U32, F32);
+        PS_METADATA_ITEM_PARSE_STRING_FLOAT(F32, strtof);
+        // Flow through
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Item %s (%s) is not of floating point type (%x) --- "
+                "treating as NaN.\n", item->name, item->comment, item->type);
+        return NAN;
+    }
+}
+
+psF64 psMetadataItemParseF64(const psMetadataItem *item
+                            )
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, NAN);
+
+    switch (item->type) {
+        PS_METADATA_ITEM_PARSE_NUMBER(F32, F64);
+        PS_METADATA_ITEM_PARSE_NUMBER(F64, F64);
+        PS_METADATA_ITEM_PARSE_NUMBER(S8,  F64);
+        PS_METADATA_ITEM_PARSE_NUMBER(S16, F64);
+        PS_METADATA_ITEM_PARSE_NUMBER(S32, F64);
+        PS_METADATA_ITEM_PARSE_NUMBER(U8,  F64);
+        PS_METADATA_ITEM_PARSE_NUMBER(U16, F64);
+        PS_METADATA_ITEM_PARSE_NUMBER(U32, F64);
+        PS_METADATA_ITEM_PARSE_STRING_FLOAT(F32, strtod);
+        // Flow through
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Item %s (%s) is not of double-precision floating "
+                "point type (%x) --- treating as NaN.\n", item->name, item->comment, item->type);
+        return NAN;
+    }
+}
+
+psU8 psMetadataItemParseU8(const psMetadataItem *item
+                          )
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, 0);
+
+    switch (item->type) {
+        PS_METADATA_ITEM_PARSE_NUMBER(F32, U8);
+        PS_METADATA_ITEM_PARSE_NUMBER(F64, U8);
+        PS_METADATA_ITEM_PARSE_NUMBER(S8,  U8);
+        PS_METADATA_ITEM_PARSE_NUMBER(S16, U8);
+        PS_METADATA_ITEM_PARSE_NUMBER(S32, U8);
+        PS_METADATA_ITEM_PARSE_NUMBER(U8,  U8);
+        PS_METADATA_ITEM_PARSE_NUMBER(U16, U8);
+        PS_METADATA_ITEM_PARSE_NUMBER(U32, U8);
+        PS_METADATA_ITEM_PARSE_STRING_INT(U8,strtoul);
+        // Flow through
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Item %s (%s) is not of integer type (%x) --- "
+                "treating as zero.\n", item->name, item->comment, item->type);
+        return 0;
+    }
+}
+
+psU16 psMetadataItemParseU16(const psMetadataItem *item
+                            )
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, 0);
+
+    switch (item->type) {
+        PS_METADATA_ITEM_PARSE_NUMBER(F32, U16);
+        PS_METADATA_ITEM_PARSE_NUMBER(F64, U16);
+        PS_METADATA_ITEM_PARSE_NUMBER(S8,  U16);
+        PS_METADATA_ITEM_PARSE_NUMBER(S16, U16);
+        PS_METADATA_ITEM_PARSE_NUMBER(S32, U16);
+        PS_METADATA_ITEM_PARSE_NUMBER(U8,  U16);
+        PS_METADATA_ITEM_PARSE_NUMBER(U16, U16);
+        PS_METADATA_ITEM_PARSE_NUMBER(U32, U16);
+        PS_METADATA_ITEM_PARSE_STRING_INT(U16,strtoul);
+        // Flow through
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Item %s (%s) is not of integer type (%x) --- "
+                "treating as zero.\n", item->name, item->comment, item->type);
+        return 0;
+    }
+}
+
+psU32 psMetadataItemParseU32(const psMetadataItem *item
+                            )
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, 0);
+
+    switch (item->type) {
+        PS_METADATA_ITEM_PARSE_NUMBER(F32, U32);
+        PS_METADATA_ITEM_PARSE_NUMBER(F64, U32);
+        PS_METADATA_ITEM_PARSE_NUMBER(S8,  U32);
+        PS_METADATA_ITEM_PARSE_NUMBER(S16, U32);
+        PS_METADATA_ITEM_PARSE_NUMBER(S32, U32);
+        PS_METADATA_ITEM_PARSE_NUMBER(U8,  U32);
+        PS_METADATA_ITEM_PARSE_NUMBER(U16, U32);
+        PS_METADATA_ITEM_PARSE_NUMBER(U32, U32);
+        PS_METADATA_ITEM_PARSE_STRING_INT(U32,strtoul);
+        // Flow through
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Item %s (%s) is not of integer type (%x) --- "
+                "treating as zero.\n", item->name, item->comment, item->type);
+        return 0;
+    }
+}
+
+psS8 psMetadataItemParseS8(const psMetadataItem *item
+                          )
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, 0);
+
+    switch (item->type) {
+        PS_METADATA_ITEM_PARSE_NUMBER(F32, S8);
+        PS_METADATA_ITEM_PARSE_NUMBER(F64, S8);
+        PS_METADATA_ITEM_PARSE_NUMBER(S8,  S8);
+        PS_METADATA_ITEM_PARSE_NUMBER(S16, S8);
+        PS_METADATA_ITEM_PARSE_NUMBER(S32, S8);
+        PS_METADATA_ITEM_PARSE_NUMBER(U8,  S8);
+        PS_METADATA_ITEM_PARSE_NUMBER(U16, S8);
+        PS_METADATA_ITEM_PARSE_NUMBER(U32, S8);
+        PS_METADATA_ITEM_PARSE_STRING_INT(S8,strtol);
+        // Flow through
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Item %s (%s) is not of integer type (%x) --- "
+                "treating as zero.\n", item->name, item->comment, item->type);
+        return 0;
+    }
+}
+
+psS16 psMetadataItemParseS16(const psMetadataItem *item
+                            )
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, 0);
+
+    switch (item->type) {
+        PS_METADATA_ITEM_PARSE_NUMBER(F32, S16);
+        PS_METADATA_ITEM_PARSE_NUMBER(F64, S16);
+        PS_METADATA_ITEM_PARSE_NUMBER(S8,  S16);
+        PS_METADATA_ITEM_PARSE_NUMBER(S16, S16);
+        PS_METADATA_ITEM_PARSE_NUMBER(S32, S16);
+        PS_METADATA_ITEM_PARSE_NUMBER(U8,  S16);
+        PS_METADATA_ITEM_PARSE_NUMBER(U16, S16);
+        PS_METADATA_ITEM_PARSE_NUMBER(U32, S16);
+        PS_METADATA_ITEM_PARSE_STRING_INT(S16,strtol);
+        // Flow through
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Item %s (%s) is not of integer type (%x) --- "
+                "treating as zero.\n", item->name, item->comment, item->type);
+        return 0;
+    }
+}
+
+psS32 psMetadataItemParseS32(const psMetadataItem *item
+                            )
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, 0);
+
+    switch (item->type) {
+        PS_METADATA_ITEM_PARSE_NUMBER(F32, S32);
+        PS_METADATA_ITEM_PARSE_NUMBER(F64, S32);
+        PS_METADATA_ITEM_PARSE_NUMBER(S8,  S32);
+        PS_METADATA_ITEM_PARSE_NUMBER(S16, S32);
+        PS_METADATA_ITEM_PARSE_NUMBER(S32, S32);
+        PS_METADATA_ITEM_PARSE_NUMBER(U8,  S32);
+        PS_METADATA_ITEM_PARSE_NUMBER(U16, S32);
+        PS_METADATA_ITEM_PARSE_NUMBER(U32, S32);
+        PS_METADATA_ITEM_PARSE_STRING_INT(S32,strtol);
+        // Flow through
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Item %s (%s) is not of integer type (%x) --- "
+                "treating as zero.\n", item->name, item->comment, item->type);
+        return 0;
+    }
+}
+
+# define PS_METADATA_ITEM_PARSE_STRING(TYPE,MODE) \
+case PS_DATA_##TYPE: { \
+    psString value = NULL; \
+    psStringAppend(&value, MODE, item->data.TYPE); \
+    return value; } \
+
+psString psMetadataItemParseString(const psMetadataItem *item
+                                  )
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, NULL);
+
+    switch (item->type) {
+    case PS_DATA_STRING:
+        return psMemIncrRefCounter(item->data.V);
+
+        PS_METADATA_ITEM_PARSE_STRING(F32, "%f");
+        PS_METADATA_ITEM_PARSE_STRING(F64, "%f");
+        PS_METADATA_ITEM_PARSE_STRING(S8,  "%d");
+        PS_METADATA_ITEM_PARSE_STRING(S16, "%d");
+        PS_METADATA_ITEM_PARSE_STRING(S32, "%d");
+        PS_METADATA_ITEM_PARSE_STRING(U8,  "%d");
+        PS_METADATA_ITEM_PARSE_STRING(U16, "%d");
+        PS_METADATA_ITEM_PARSE_STRING(U32, "%d");
+      case PS_DATA_TIME:
+        return psTimeToISO(item->data.V);
+
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Item %s (%s) is not of string type (%x) --- treating as "
+                "undefined.\n", item->name, item->comment, item->type);
+        //        return psStringCopy("");
+        return NULL;
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemParse.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemParse.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psMetadataItemParse.h	(revision 22322)
@@ -0,0 +1,35 @@
+/** @file  psMetadataItemParse.h
+*
+*  @brief Parse metadata items
+*
+*  @author EAM, PAP, JH, RHL, 
+*
+*  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-02-06 21:36:09 $
+*
+*  Copyright 2004-2005 IfA, University of Hawaii
+*/
+
+#ifndef PS_METADATA_ITEM_PARSE_H
+#define PS_METADATA_ITEM_PARSE_H
+
+/// @addtogroup DataContainer Data Containers
+/// @{
+
+#include "psType.h"
+#include "psMetadata.h"
+
+// Parse a psMetadataItem as a particular type
+bool psMetadataItemParseBool(const psMetadataItem *item);
+psF32 psMetadataItemParseF32(const psMetadataItem *item);
+psF64 psMetadataItemParseF64(const psMetadataItem *item);
+psS8  psMetadataItemParseS8(const psMetadataItem *item);
+psS16 psMetadataItemParseS16(const psMetadataItem *item);
+psS32 psMetadataItemParseS32(const psMetadataItem *item);
+psU8  psMetadataItemParseU8(const psMetadataItem *item);
+psU16 psMetadataItemParseU16(const psMetadataItem *item);
+psU32 psMetadataItemParseU32(const psMetadataItem *item);
+psString psMetadataItemParseString(const psMetadataItem *item);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psPixels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psPixels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psPixels.c	(revision 22322)
@@ -0,0 +1,364 @@
+/** @file  psPixels.c
+ *
+ *  @brief Contains psPixel related functions
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.43 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-12 22:36:29 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "psAbort.h"
+#include "psSort.h"
+#include "psMemory.h"
+#include "psAssert.h"
+#include "psError.h"
+
+#include "psPixels.h"
+
+#define PIXELS_DEFAULT_ADD 10           // Default number to add, if not specified
+
+typedef int(*qsortCompareFunc)(const void *, const void *);
+
+static void pixelsFree(psPixels* pixels)
+{
+    psAssert(pixels, "impossible");
+    psFree(pixels->data);
+}
+
+static psPixels *pixelsAlloc(const char *file,
+                          unsigned int lineno,
+                          const char *func,
+                          long nalloc)
+{
+    psPixels* out = p_psAlloc(file, lineno, func, sizeof(psPixels));
+    psMemSetDeallocator(out, (psFreeFunc)pixelsFree);
+
+    out->data = p_psAlloc(file, lineno, func, sizeof(psPixelCoord)*nalloc);
+    P_PSPIXELS_SET_NALLOC(out,nalloc);
+
+    return out;
+}
+
+psPixels* p_psPixelsAlloc(const char *file,
+                          unsigned int lineno,
+                          const char *func,
+                          long nalloc)
+{
+    psPixels *out = pixelsAlloc(file, lineno, func, nalloc);
+    out->n = nalloc;
+    return out;
+}
+
+psPixels* p_psPixelsAllocEmpty(const char *file,
+                             unsigned int lineno,
+                             const char *func,
+                             long nalloc)
+{
+    psPixels *out = pixelsAlloc(file, lineno, func, nalloc);
+    out->n = 0;
+    return out;
+}
+
+
+bool psMemCheckPixels(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)pixelsFree );
+}
+
+
+psPixels* p_psPixelsRealloc(const char *file,
+                            unsigned int lineno,
+                            const char *func,
+                            psPixels* pixels,
+                            long nalloc)
+{
+    if (!pixels) {
+        return p_psPixelsAlloc(file, lineno, func, nalloc);
+    }
+    if (nalloc < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't reallocate a psPixels to negative size.");
+        return pixels;
+    }
+
+    if (pixels->n == nalloc) {
+        return pixels;
+    }
+    if (nalloc < pixels->n) {
+        pixels->n = nalloc;
+    }
+    pixels->data = p_psRealloc(file, lineno, func, pixels->data, sizeof(psPixelCoord)*nalloc);
+    P_PSPIXELS_SET_NALLOC(pixels,nalloc);
+
+    return pixels;
+}
+
+psPixels* psPixelsAdd(psPixels* pixels,
+                      long growth,
+                      float x,
+                      float y)
+{
+    if (growth <= 0) {
+        growth = PIXELS_DEFAULT_ADD;
+    }
+
+    if (!pixels) {
+        pixels = psPixelsAllocEmpty(growth);
+    } else if (pixels->n >= pixels->nalloc) {
+        pixels = psPixelsRealloc(pixels, pixels->nalloc + growth);
+    }
+
+    long n = pixels->n;
+
+    pixels->data[n].x = x;
+    pixels->data[n].y = y;
+
+    pixels->n++;
+
+    return pixels;
+}
+
+psPixels* p_psPixelsCopy(const char *file,
+                         unsigned int lineno,
+                         const char *func,
+                         psPixels* out,
+                         const psPixels* pixels)
+{
+    PS_ASSERT_PIXELS_NON_NULL(pixels, NULL);
+
+    out = p_psPixelsRealloc(file, lineno, func, out, pixels->n);
+    memcpy(out->data,pixels->data, pixels->n*sizeof(psPixelCoord));
+    out->n = pixels->n;
+
+    return out;
+}
+
+psImage *psPixelsToMask(psImage *out,
+                        const psPixels *pixels,
+                        psRegion region,
+                        psMaskType maskVal)
+{
+    PS_ASSERT_PIXELS_NON_NULL(pixels, NULL);
+
+    float x0 = region.x0;
+    float x1 = region.x1;
+    float y0 = region.y0;
+    float y1 = region.y1;
+
+    if (x0 < 0 || x1 < 0 || y0 < 0 || y1 < 0 || x1 < x0 || y1 < y0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Specified psRegion, [%d:%d,%d:%d], does not specify a valid region."),
+                (int)x0,(int)x1,(int)y0,(int)y1);
+        psFree(out);
+        return NULL;
+    }
+
+    int numRows = y1 - y0 + 1, numCols = x1 - x0 + 1; // Size of image
+
+    out = psImageRecycle(out, numCols, numRows, PS_TYPE_MASK);
+    if (!out) {
+        psError(PS_ERR_UNKNOWN, false, _("Failed to create image of size %dx%d."), numCols, numRows);
+        return NULL;
+    }
+//    P_PSIMAGE_SET_COL0(out, (int)x0);
+//    P_PSIMAGE_SET_ROW0(out, (int)y0);
+    psImageInit(out, 0);
+
+
+    // cycle through the vector of pixels and insert pixels into image
+    long length = pixels->n;            // Length of pixel array
+    psMaskType** outData = out->data.PS_TYPE_MASK_DATA;
+    for (int p = 0; p < length; p++) {
+        float x = pixels->data[p].x;
+        float y = pixels->data[p].y;
+        // pixel in region?
+        if (x >= x0 && x <= x1 && y >= y0 && y <= y1) {
+            outData[(int)(y-y0)][(int)(x-x0)] |= maskVal;
+        }
+    }
+
+    return out;
+}
+
+psPixels* psPixelsFromMask(psPixels* out,
+                           const psImage* mask,
+                           psMaskType maskVal)
+{
+    PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
+    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, NULL);
+
+    int numRows = mask->numRows, numCols = mask->numCols; // Size of image
+
+    // assumption: number of masked pixels is relatively small compared to total pixels, so it is best to just
+    // start with a guess and resize if necessary
+    long minPixels = PS_MAX(numRows * numCols / 100, 32); // initial guess: 1% of pixels masked;
+    if (!out || !out->data || out->nalloc < minPixels) {
+        out = psPixelsRealloc(out, minPixels);
+    }
+    out->n = 0;
+
+    // find the mask pixels in the image
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            if (mask->data.PS_TYPE_MASK_DATA[y][x] & maskVal) {
+                psPixelsAdd(out, out->nalloc, x, y);
+            }
+        }
+    }
+
+    return out;
+}
+
+psPixels* psPixelsConcatenate(psPixels *out, const psPixels *pixels)
+{
+    PS_ASSERT_PIXELS_NON_NULL(pixels, NULL);
+    if (!out) {
+        return psPixelsCopy(NULL, pixels);
+    }
+
+    long numIn = pixels->n;             // Number of input pixels
+    long numOut = out->n;               // Number of (original) output pixels
+    out = psPixelsRealloc(out, numIn + numOut);
+    memcpy(&out->data[numOut], pixels->data, numIn * sizeof(psPixelCoord));
+    out->n = numIn + numOut;
+
+    return out;
+}
+
+// Macro functions for sorting a pixel list
+#define PSPIXELS_SORT_COMPARE(A,B) (data[A].y < data[B].y || data[A].x < data[B].x)
+#define PSPIXELS_SORT_SWAP(TYPE,A,B) { \
+    if (A != B) { \
+        TYPE temp = data[A]; \
+        data[A] = data[B]; \
+        data[B] = temp; \
+    } \
+}
+
+psPixels* psPixelsDuplicates(psPixels *out, const psPixels *pixels)
+{
+    PS_ASSERT_PIXELS_NON_NULL(pixels, NULL);
+    if (pixels->n <= 1) {
+        return psPixelsCopy(out, pixels);
+    }
+
+    long numIn = pixels->n;          // Number of input pixels
+    out = psPixelsRealloc(out, numIn);
+
+    psPixelCoord *data = pixels->data;  // Dereference input
+    PSSORT(numIn, PSPIXELS_SORT_COMPARE, PSPIXELS_SORT_SWAP, psPixelCoord);
+
+    long numOut = 0;                     // Number of output pixels
+    for (long i = 0; i < numIn; numOut++) {
+        psPixelCoord pix = data[i];     // Coordinates of interest
+        for (i++; i < numIn && data[i].x == pix.x && data[i].y == pix.y; i++); // No action
+        out->data[numOut] = pix;
+    }
+    out->n = numOut;
+
+    return out;
+}
+
+bool p_psPixelsPrint(FILE *fd,
+                     psPixels* pixels,
+                     const char *name)
+{
+    PS_ASSERT_PIXELS_NON_NULL(pixels, false);
+
+    if (fd == NULL) {
+        fd = stdout;
+    } else {
+        if ( fprintf(fd, "\n") < 0 ) {
+            psError(PS_ERR_IO, true,
+                    "Invalid file pointer in p_psPixelsPrint.  Could not write to fd.\n");
+            return false;
+        }
+    }
+
+    if (name != NULL) {
+        fprintf (fd, "psPixels: %s\n", name);
+    }
+
+    long n = pixels->n;
+    psPixelCoord* data = pixels->data;
+
+    if (data == NULL || n == 0) {
+        fprintf(fd,"EMPTY\n\n");
+        return true;
+    }
+
+    for (long i = 0; i < n; i++) {
+        fprintf (fd, "(%f,%f)\n", data[i].x, data[i].y);
+    }
+    fprintf (fd, "\n");
+    return (true);
+}
+
+bool psPixelsSet(psPixels *pixels,
+                 long position,
+                 psPixelCoord value)
+{
+    PS_ASSERT_PIXELS_NON_NULL(pixels, false);
+
+    if (position < 0) {
+        position += pixels->n;
+    }
+    if (position < 0 || position >= pixels->n) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position: %ld\n", position);
+        return false;
+    }
+    pixels->data[position].x = value.x;
+    pixels->data[position].y = value.y;
+
+    return true;
+}
+
+psPixelCoord psPixelsGet(const psPixels *pixels,
+                         long position)
+{
+    psPixelCoord out;
+    if (pixels == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("Input psPixels can not be NULL."));
+        out.x = NAN;
+        out.y = NAN;
+        return out;
+    }
+    if (position < 0) {
+        position += pixels->n;
+    }
+    if (position >= pixels->n) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Number too large\n");
+        out.x = NAN;
+        out.y = NAN;
+        return out;
+    }
+    if (position < 0) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Negative number too large\n");
+        out.x = NAN;
+        out.y = NAN;
+        return out;
+    }
+    out.x = pixels->data[position].x;
+    out.y = pixels->data[position].y;
+    return out;
+}
+
+long psPixelsLength(const psPixels *pixels)
+{
+    PS_ASSERT_PIXELS_NON_NULL(pixels, -1);
+    return pixels->n;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psPixels.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psPixels.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psPixels.h	(revision 22322)
@@ -0,0 +1,277 @@
+/** @file  psPixels.h
+ *
+ *  @brief Contains psPixel related functions
+ *
+ *  @author Paul Price, IfA
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.30 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-12 22:36:29 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_PIXELS_H
+#define PS_PIXELS_H
+
+#include "psImage.h"
+#include "psVector.h"
+#include "psRegion.h"
+
+/// @addtogroup DataContainer Data Containers
+/// @{
+
+/** Data structure for storing psPixel coordinates  */
+typedef struct
+{
+    float x;                             ///< x coordinate
+    float y;                             ///< y coordinate
+}
+psPixelCoord;
+
+/** list of pixel coordinates
+ *
+ *  Usually an image mask is the best way to carry information about what
+ *  pixels mean what. However, in the case where the number of pixels in which
+ *  we are interested is limited, it is more efficient to simply carry a list
+ *  of pixels. An example of this is in the image combination code, where we
+ *  want to perform an operation on a relatively small fraction of pixels, and
+ *  it is inefficient to go through an entire mask image checking each pixel.
+ *
+ */
+typedef struct
+{
+    long n;                            ///< Number in use
+    const long nalloc;                 ///< Number allocated
+    psPixelCoord* data;                ///< The pixel coordinates
+    psMutex lock;                       ///< Option lock for thread safety
+}
+psPixels;
+
+#define PS_ASSERT_PIXELS_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->data || (NAME)->n < 0 || (NAME)->nalloc < 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: Pixels %s or one of its components is NULL.", \
+            #NAME); \
+    return RVAL; \
+}
+
+#define P_PSPIXELS_SET_NALLOC(pix,n) *(long*)&pix->nalloc = n
+
+
+/** Allocates a new psPixels structure
+ *
+ *  @return psPixels*   new psPixels
+ */
+#ifdef DOXYGEN
+psPixels* psPixelsAlloc(
+    long nalloc                         ///< the size of the coordinate vectors
+);
+#else // ifdef DOXYGEN
+psPixels* p_psPixelsAlloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    long nalloc                         ///< the size of the coordinate vectors
+) PS_ATTR_MALLOC;
+#define psPixelsAlloc(nalloc) \
+      p_psPixelsAlloc(__FILE__, __LINE__, __func__, nalloc)
+#endif // ifdef DOXYGEN
+
+/** Allocates a new empty psPixels structure
+ *
+ *  @return psPixels*   new psPixels
+ */
+#ifdef DOXYGEN
+psPixels* psPixelsAllocEmpty(
+    long nalloc                         ///< the size of the coordinate vectors
+);
+#else // ifdef DOXYGEN
+psPixels* p_psPixelsAllocEmpty(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    long nalloc                         ///< the size of the coordinate vectors
+);
+#define psPixelsAllocEmpty(nalloc) \
+      p_psPixelsAllocEmpty(__FILE__, __LINE__, __func__, nalloc)
+#endif // ifdef DOXYGEN
+
+
+/** Checks the type of a particular pointer.
+ *
+ *  Uses the appropriate deallocation function in psMemBlock to check the ptr
+ *  datatype.
+ *
+ *  @return bool:       True if the pointer matches a psPixels structure, false otherwise.
+ */
+bool psMemCheckPixels(
+    psPtr ptr                          ///< the pointer whose type to check
+);
+
+
+/** resizes a psPixels structure
+ *
+ *  @return psPixels*   resized psPixels
+ */
+#ifdef DOXYGEN
+psPixels* psPixelsRealloc(
+    psPixels* pixels,                  ///< psPixels to resize, or NULL to create new psPixels
+    long nalloc                        ///< the size of the coordinate vectors
+);
+#else // ifdef DOXYGEN
+psPixels* p_psPixelsRealloc(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psPixels* pixels,                  ///< psPixels to resize, or NULL to create new psPixels
+    long nalloc                        ///< the size of the coordinate vectors
+);
+#define psPixelsRealloc(pixels, nalloc) \
+      p_psPixelsRealloc(__FILE__, __LINE__, __func__, pixels, nalloc)
+#endif // ifdef DOXYGEN
+
+
+/** Add a pixel location to a psPixels
+ *
+ *  Grow the psPixels input by growth.  If growth is less that 1, 10 is used.  If a NULL
+ *  psPixels is given, a new one is created.
+ *
+ *  @return psPixels*       psPixels with the value appended.
+ */
+psPixels* psPixelsAdd(
+    psPixels* pixels,                  ///< psPixels to append new coordinate to.
+    long growth,                       ///< Number of elements to grow the pixels list if necessary.
+    float x,                           ///< x coordinate to append
+    float y                            ///< y coordinate to append
+);
+
+
+/** Copies a psPixels object
+ *
+ *  Makes a deep copy of the data in a psPixels object.  Any data in the OUT
+ *  parameter will be destroyed and OUT will be resized, if necessary.
+ *
+ *  @return psPixels*   a new psPixels that is a duplicate to IN
+ */
+#ifdef DOXYGEN
+psPixels* psPixelsCopy(
+    psPixels* out,                     ///< psPixels struct to recycle, or NULL
+    const psPixels* pixels             ///< psPixels struct to copy
+);
+#else // ifdef DOXYGEN
+psPixels* p_psPixelsCopy(
+    const char *file,                   ///< File of caller
+    unsigned int lineno,                ///< Line number of caller
+    const char *func,                   ///< Function name of caller
+    psPixels* out,                     ///< psPixels struct to recycle, or NULL
+    const psPixels* pixels             ///< psPixels struct to copy
+);
+#define psPixelsCopy(out, pixels) \
+      p_psPixelsCopy(__FILE__, __LINE__, __func__, out, pixels)
+#endif // ifdef DOXYGEN
+
+
+/** Generate a psImage from a psPixels
+ *
+ *  psPixelsToMask shall return an image of type U8 with the pixels lying
+ *  within the specified region set to the maskVal. The out image shall be
+ *  modified if supplied, or allocated and returned if NULL. The size of the
+ *  output image shall be region->x1 - region->x0 by region->y1 - region->y0,
+ *  with out->x0 = region->x0 and out->y0 = region->y0. In the event that
+ *  either of pixels or region are NULL, the function shall generate an
+ *  error and return NULL.
+ *
+ *  @return psImage*    generated mask image
+ */
+psImage* psPixelsToMask(
+    psImage* out,                      ///< psImage to recycle, or NULL
+    const psPixels* pixels,            ///< list of pixels to use
+    psRegion region,                   ///< region to define the output mask image
+    psMaskType maskVal                 ///< the mask bit-values to act upon
+);
+
+
+/** Generate a psPixels from a mask psImage
+ *
+ *  psMaskToPixels shall return a psPixels consisting of the coordinates in
+ *  the mask that match the maskVal. The out pixel list shall be modified if
+ *  supplied, or allocated and returned if NULL. In hte event that mask is
+ *  NULL, the function shall generate an error and return NULL.
+ *
+ *  @return psPixels*   generated psPixels pixel list
+ */
+psPixels* psPixelsFromMask(
+    psPixels *out,                     ///< psPixels to recycle, or NULL
+    const psImage *mask,               ///< the input mask psImage
+    psMaskType maskVal                 ///< the mask bit-values to act upon
+);
+
+
+/** Concatenates two psPixels
+ *
+ *  psPixelsConcatenate shall concatenate pixels onto out. In the event that
+ *  out is NULL, a new psPixels shall be allocated, and the contents of
+ *  pixels simply copied in. If pixels is NULL, the function shall generate
+ *  an error and return NULL.
+ *
+ *  @return psPixels         Concatenated psPixel list
+ */
+psPixels* psPixelsConcatenate(
+    psPixels *out,                     ///< psPixels to recycle, or NULL
+    const psPixels *pixels             ///< psPixels to append to OUT
+);
+
+/// Remove duplicates in a list of pixels
+psPixels* psPixelsDuplicates(
+    psPixels *out, ///< Output list with duplicates removed, or NULL
+    const psPixels *pixels ///< Input list of pixels
+    );
+
+/** Prints a psPixels to specified destination.
+ *
+ *  @return bool:    True if successful.
+*/
+bool p_psPixelsPrint(
+    FILE *fd,                          ///< destination file descriptor
+    psPixels* pixels,                  ///< psPixels to print
+    const char *name                   ///< printf-style format of header line
+);
+
+
+/** Sets the value of the the pixels array at the specified position to value.
+ *
+ *  A negative position means index from the end.
+ *
+ *  @return bool:       True if Successful, otherwise false.
+*/
+bool psPixelsSet(
+    psPixels *pixels,                  ///< pixels to set
+    long position,                     ///< position to set
+    psPixelCoord value                 ///< pixels value to be set
+);
+
+
+/** Returns the value of the pixels array at the specified position.
+ *
+ *  A negative position means index from the end.
+ *
+ *  @return psPixelCoord:       The value of the pixels at the specified position.
+*/
+psPixelCoord psPixelsGet(
+    const psPixels *pixels,            ///< input pixels from which to get
+    long position                      ///< position to get
+);
+
+
+/** Get the number of elements in use from a specified psPixels. (pixels.n)
+ *
+ *  @return long:       The number of elements in use.
+ */
+long psPixelsLength(
+    const psPixels *pixels             ///< input psPixels
+);
+
+
+/// @}
+#endif // #ifndef PS_PIXELS_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psTree.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psTree.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psTree.c	(revision 22322)
@@ -0,0 +1,708 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "psAssert.h"
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psType.h"
+#include "psArray.h"
+#include "psVector.h"
+#include "psImage.h"
+#include "psList.h"
+#include "psSort.h"
+
+#include "psTree.h"
+
+
+// XXX Upgrades:
+// * Could allow different types of coordinates (saving memory)
+// * Could make tree "growable" by including a pointer to a new tree within each node.
+// * Rebalancing the tree ("pruning"?) is done by collecting all the points and re-planting a new tree.
+
+static void treeCoordArrayFree(psTreeCoordArray *array)
+{
+    psFree(array->F64);
+    psFree(array->raw);
+}
+
+static void treeNodeFree(psTreeNode *node)
+{
+    // All contents are views only
+    return;
+}
+
+static void treeFree(psTree *tree)
+{
+    for (long i = 0; i < tree->numNodes; i++) {
+        psFree(tree->nodes[i]);
+    }
+    psFree(tree->nodes);
+    psFree(tree->min);
+    psFree(tree->max);
+    psFree(tree->division);
+    // 'root' is a view only
+    psFree(tree->data);
+    psFree(tree->contents);
+}
+
+psTreeCoordArray *psTreeCoordArrayAlloc(long size, int dim)
+{
+    psAssert(size > 0, "Size (%ld) must be positive", size);
+    psAssert(dim > 0, "Dimension (%d) must be positive", dim);
+
+    psTreeCoordArray *array = psAlloc(sizeof(psTreeCoordArray)); // Array to return
+    psMemSetDeallocator(array, (psFreeFunc)treeCoordArrayFree);
+
+    array->raw = psAlloc(dim * size * sizeof(psF64));
+    array->F64 = psAlloc(size * sizeof(psF64*));
+
+    psF64 *ptr = (psF64*)array->raw;    // Pointer into raw vector
+    for (long i = 0; i < size; i++, ptr += dim) {
+        array->F64[i] = ptr;
+    }
+
+    return array;
+}
+
+psTreeNode *psTreeNodeAlloc(long index)
+{
+    psAssert(index >= 0, "Index (%ld) must be non-negative", index);
+
+    psTreeNode *node = psAlloc(sizeof(psTreeNode)); // Node to return
+    psMemSetDeallocator(node, (psFreeFunc)treeNodeFree);
+
+    node->index = index;
+    node->divideDim = -1;
+    node->num = 0;
+    node->contents = NULL;
+    node->tree = NULL;
+    node->up = NULL;
+    node->left = NULL;
+    node->right = NULL;
+
+    return node;
+}
+
+psTree *psTreeAlloc(int dim, int maxLeafContents, long numNodes)
+{
+    psAssert(dim > 0, "Dimensionality (%d) must be positive", dim);
+    psAssert(maxLeafContents > 0, "Maximum number per leaf (%d) must be positive", maxLeafContents);
+    psAssert(numNodes > 0, "Number of nodes (%ld) must be positive", numNodes);
+
+    psTree *tree = psAlloc(sizeof(psTree)); // Tree to return
+    psMemSetDeallocator(tree, (psFreeFunc)treeFree);
+
+    tree->dim = dim;
+    tree->maxLeafContents = maxLeafContents;
+
+    tree->numNodes = numNodes;
+    tree->nodes = psAlloc(numNodes * sizeof(psTreeNode*));
+    for (long i = 0; i < numNodes; i++) {
+        tree->nodes[i] = psTreeNodeAlloc(i);
+    }
+    tree->root = tree->nodes[0];
+
+    tree->min = psTreeCoordArrayAlloc(numNodes, dim);
+    tree->max = psTreeCoordArrayAlloc(numNodes, dim);
+
+    tree->division = psAlloc(numNodes * sizeof(double));
+
+    tree->numData = 0;
+    tree->data = NULL;
+
+    return tree;
+}
+
+
+psTree *psTreePlant(int dim, int maxLeafContents, ...)
+{
+    PS_ASSERT_INT_POSITIVE(dim, NULL);
+    PS_ASSERT_INT_POSITIVE(maxLeafContents, NULL);
+
+    // Parse coordinate list
+    va_list args;                       // Variable argument list
+    va_start(args, maxLeafContents);
+    psArray *coords = psArrayAlloc(dim); // Array of coordinates
+    long numData = 0;                   // Number of data points
+    for (int i = 0; i < dim; i++) {
+        psVector *data = va_arg(args, psVector*);
+        if (i == 0) {
+            numData = data->n;
+        } else if (data->n != numData) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Input vectors have differing sizes: %ld vs %ld",
+                    data->n, numData);
+            psFree(coords);
+            va_end(args);
+            return NULL;
+        }
+        coords->data[i] = psMemIncrRefCounter(data);
+    }
+    va_end(args);
+
+    long numNodes;
+    {
+        // From equation 21.2.2 in Numerical Recipes vol 3, p1102.
+        long pow2;                      // Smallest power of two >= numData
+        for (pow2 = 1; pow2 < numData; pow2 <<= 1);
+        numNodes = PS_MIN(pow2 - 1, 2 * numData - pow2 / 2 - 1);
+    }
+
+    psTree *tree = psTreeAlloc(dim, maxLeafContents, numNodes);
+    tree->data = psTreeCoordArrayAlloc(numData, dim);
+    tree->numData = numData;
+    psF64 **data = tree->data->F64;     // Dereference data
+
+    // Copy data into the tree
+#define PSTREE_COPY_DATA_CASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+        for (long j = 0; j < numData; j++) { \
+            data[j][i] = values->data.TYPE[j]; \
+        } \
+        break; \
+    }
+
+    for (int i = 0; i < dim; i++) {
+        psVector *values = coords->data[i]; // Vector of values for this dimension
+        switch (values->type.type) {
+            PSTREE_COPY_DATA_CASE(U8);
+            PSTREE_COPY_DATA_CASE(U16);
+            PSTREE_COPY_DATA_CASE(U32);
+            PSTREE_COPY_DATA_CASE(U64);
+            PSTREE_COPY_DATA_CASE(S8);
+            PSTREE_COPY_DATA_CASE(S16);
+            PSTREE_COPY_DATA_CASE(S32);
+            PSTREE_COPY_DATA_CASE(S64);
+            PSTREE_COPY_DATA_CASE(F32);
+            PSTREE_COPY_DATA_CASE(F64);
+          default:
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unsupported vector type for dimension %d: %x",
+                    i, values->type.type);
+            psFree(tree);
+            psFree(coords);
+        }
+    }
+    psFree(coords);
+
+    long *contents = tree->contents = psAlloc(numData * sizeof(long)); // Contents of tree
+    for (long i = 0; i < numData; i++) {
+        contents[i] = i;
+    }
+
+    psTreeNode *root = tree->root;      // Root of the tree
+    root->tree = tree;
+    root->contents = contents;
+    root->num = numData;
+    root->divideDim = 0;
+    for (int i = 0; i < dim; i++) {
+        tree->min->F64[0][i] = -INFINITY;
+        tree->max->F64[0][i] = INFINITY;
+    }
+
+    psArray *work = psArrayAlloc(numNodes); // Work queue
+    work->data[0] = root;
+    long workNum = 1;
+
+// Compare points (in a single dimension) by their index
+#define PSTREE_SELECT_COMPARE(A,B) (data[contents[A]][divideDim] < data[contents[B]][divideDim])
+// Swap points by their index
+#define PSTREE_SELECT_SWAP(TYPE,A,B) { \
+    if (A != B) { \
+        TYPE temp = contents[A]; \
+        contents[A] = contents[B]; \
+        contents[B] = temp; \
+    } \
+}
+
+    for (long workIndex = 0, nextIndex = 0; workIndex < workNum; workIndex++) {
+        psTreeNode *node = work->data[workIndex]; // Node to be worked on
+        long num = node->num;           // Number of points in node
+        long *contents = node->contents;// Indices of points in node
+        psF64 **data = tree->data->F64; // Dereference the values
+        long middle = num / 2;          // Index of middle point
+        int divideDim = node->divideDim;// Dimension in which to divide
+        long nodeIndex = node->index;   // Index of node
+
+        PSSELECT(num, middle, PSTREE_SELECT_COMPARE, PSTREE_SELECT_SWAP, long);
+
+        psF64 *nodeMin = tree->min->F64[nodeIndex]; // Minimum bounds for current node
+        psF64 *nodeMax = tree->max->F64[nodeIndex]; // Maximum bounds for current node
+
+        // Divide to the left and right
+        double division = tree->division[nodeIndex] = data[contents[middle]][divideDim];
+
+        long leftIndex = ++nextIndex, rightIndex = ++nextIndex; // Indices for left and right nodes
+        psTreeNode *left = node->left = tree->nodes[leftIndex]; // Node to the left
+        psTreeNode *right = node->right = tree->nodes[rightIndex]; // Node to the right
+        long numLeft = middle, numRight = num - middle; // Number in each node
+
+        for (int i = 0; i < dim; i++) {
+            tree->min->F64[leftIndex][i] = nodeMin[i];
+            tree->min->F64[rightIndex][i] = (i == divideDim ? division : nodeMin[i]);
+            tree->max->F64[leftIndex][i] = (i == divideDim ? division : nodeMax[i]);
+            tree->max->F64[rightIndex][i] = nodeMax[i];
+        }
+
+        left->num = numLeft;
+        right->num = numRight;
+
+        left->contents = contents;
+        right->contents = contents + middle;
+
+#if 0
+        // Check contents
+        for (int i = 0; i < numLeft; i++) {
+            long index = left->contents[i]; // Index of point
+            for (int j = 0; j < dim; j++) {
+                if (data[index][j] < tree->min->F64[leftIndex][j] ||
+                    data[index][j] > tree->max->F64[leftIndex][j]) {
+                    psWarning("Bad division");
+                }
+            }
+        }
+        for (int i = 0; i < numRight; i++) {
+            long index = right->contents[i]; // Index of point
+            for (int j = 0; j < dim; j++) {
+                if (data[index][j] < tree->min->F64[rightIndex][j] ||
+                    data[index][j] > tree->max->F64[rightIndex][j]) {
+                    psWarning("Bad division");
+                }
+            }
+        }
+#endif
+
+        divideDim++;
+        if (divideDim == dim) {
+            divideDim = 0;
+        }
+        left->divideDim = right->divideDim = divideDim;
+
+        left->up = right->up = node;
+        left->tree = right->tree = tree;
+
+        if (numLeft > maxLeafContents) {
+            work->data[workNum++] = left;
+        }
+        if (numRight > maxLeafContents) {
+            work->data[workNum++] = right;
+        }
+
+        work->data[workIndex] = NULL;
+    }
+    psFree(work);
+
+    return tree;
+}
+
+// Print a representation of the node; called recursively
+static void treeNodePrint(FILE *fptr,   // File pointer to which to print
+                          const psTreeNode *node, // Node to print
+                          int level     // Level at which to print
+    )
+{
+    long index = node->index;           // Index of node
+    psTree *tree = node->tree;          // Parent tree
+    int dim = tree->dim;                // Dimensions
+
+    for (int i = 0; i < level; i++) {
+        fprintf(fptr, " ");
+    }
+    fprintf(fptr, "%ld: (", index);
+    for (int i = 0; i < dim; i++) {
+        fprintf(fptr, "%lf", tree->min->F64[index][i]);
+        if (i < dim - 1) {
+            fprintf(fptr, ",");
+        }
+    }
+    fprintf(fptr, ") --> (");
+    for (int i = 0; i < dim; i++) {
+        fprintf(fptr, "%lf", tree->max->F64[index][i]);
+        if (i < dim - 1) {
+            fprintf(fptr, ",");
+        }
+    }
+    fprintf(fptr, ")");
+    level++;
+
+    if (node->left && node->right) {
+        fprintf(fptr, " ==> %lf\n", tree->division[index]);
+        treeNodePrint(fptr, node->left, level);
+        treeNodePrint(fptr, node->right, level);
+        return;
+    }
+    fprintf(fptr, "\n");
+    for (int i = 0; i < level; i++) {
+        fprintf(fptr, " ");
+    }
+    fprintf(fptr, "(");
+    for (int i = 0; i < node->num; i++) {
+        psF64 *coords = tree->data->F64[node->contents[i]]; // Coordinates
+        for (int j = 0; j < dim; j++) {
+            fprintf(fptr, "%lf", coords[j]);
+            if (j < dim - 1) {
+                fprintf(fptr, ",");
+            }
+        }
+        fprintf(fptr, ")");
+        if (i < node->num - 1) {
+            fprintf(fptr, " (");
+        }
+    }
+    fprintf(fptr, "\n");
+
+    return;
+}
+
+
+void psTreePrint(FILE *fptr, const psTree *tree)
+{
+    PS_ASSERT_TREE_NON_NULL(tree, );
+
+    treeNodePrint(fptr, tree->root, 0);
+    return;
+}
+
+// Given an arbitrary point, find the appropriate leaf
+psTreeNode *psTreeLeaf(const psTree *tree, const psVector *coords)
+{
+#if 1 // Might be in a tight loop
+    PS_ASSERT_TREE_NON_NULL(tree, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(coords, NULL);
+    PS_ASSERT_VECTOR_SIZE(coords, (long)tree->dim, NULL);
+#endif
+
+#define TREE_LEAF_CASE(TYPE) \
+  case PS_TYPE_##TYPE: \
+      for (; node->left; node = (coords->data.TYPE[node->divideDim] < tree->division[node->index]) ? \
+               node->left : node->right); \
+      break;
+
+    psTreeNode *node = tree->root;      // Node of interest
+    switch (coords->type.type) {
+        TREE_LEAF_CASE(U8);
+        TREE_LEAF_CASE(U16);
+        TREE_LEAF_CASE(U32);
+        TREE_LEAF_CASE(U64);
+        TREE_LEAF_CASE(S8);
+        TREE_LEAF_CASE(S16);
+        TREE_LEAF_CASE(S32);
+        TREE_LEAF_CASE(S64);
+        TREE_LEAF_CASE(F32);
+        TREE_LEAF_CASE(F64);
+      default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unsupported type: %x", coords->type.type);
+        return NULL;
+    }
+    return node;
+}
+
+// Returns the square of the distance between a point on the tree and an aribtary point
+static inline double treeContentDistance(const psTree *tree, // Tree of interest
+                                         long index, // Index of point on tree
+                                         const psVector *coords // Coordinates of interest
+    )
+{
+    int dim = tree->dim;                // Dimensionality
+    switch (dim) {
+      case 2:
+        return PS_SQR(coords->data.F64[0] - tree->data->F64[index][0]) +
+            PS_SQR(coords->data.F64[1] - tree->data->F64[index][1]);
+      case 3:
+        return PS_SQR(coords->data.F64[0] - tree->data->F64[index][0]) +
+            PS_SQR(coords->data.F64[1] - tree->data->F64[index][1]) +
+            PS_SQR(coords->data.F64[2] - tree->data->F64[index][2]);
+      default: {
+          double distance2 = 0.0;             // Distance of interest
+          for (int i = 0; i < dim; i++) {
+              distance2 += PS_SQR(coords->data.F64[i] - tree->data->F64[index][i]);
+          }
+          return distance2;
+      }
+    }
+    return NAN;
+}
+
+// Returns the square of the distance between a leaf and an arbitrary point
+static inline double treeBranchDistance(const psTree *tree, long index, const psVector *coords)
+{
+    int dim = tree->dim;                // Dimensionality
+    double distance = 0.0;              // Distance to box
+    for (int i = 0; i < dim; i++) {
+        double minDiff = tree->min->F64[index][i] - coords->data.F64[i];
+        if (minDiff > 0) {
+            distance += PS_SQR(minDiff);
+            continue;
+        }
+        double maxDiff = coords->data.F64[i] - tree->max->F64[index][i];
+        if (maxDiff > 0) {
+            distance += PS_SQR(maxDiff);
+            continue;
+        }
+    }
+    return distance;
+}
+
+// Returns whether the designated leaf contains an arbitrary point
+static inline bool treeBranchInside(const psTree *tree, // Tree of interest
+                                    long index, // Index of leaf on tree
+                                    const psVector *coords // Coordinates of interest
+    )
+{
+    int dim = tree->dim;                // Dimensionality
+    for (int i = 0; i < dim; i++) {
+        if (tree->min->F64[index][i] > coords->data.F64[i]) {
+            return false;
+        }
+        if (tree->max->F64[index][i] < coords->data.F64[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// Search a leaf node for a closer point
+static inline void treeLeafSearchNearest(long *bestIndex, // Index of closest point
+                                         double *bestDistance, // Distance to closest point
+                                         const psTree *tree, // Tree of interest
+                                         const psTreeNode *leaf, // Leaf to search
+                                         const psVector *coords // Coordinates of interest
+    )
+{
+    for (int i = 0; i < leaf->num; i++) {
+        long index = leaf->contents[i]; // Index of point
+        double distance = treeContentDistance(tree, index, coords); // Distance to point
+        if (distance < *bestDistance) {
+            *bestIndex = index;
+            *bestDistance = distance;
+        }
+    }
+    return;
+}
+
+// Return the index of the nearest neighbour to given coordinates, within some radius
+// This is the engine for psTreeNearest() and psTreeNearestWithin()
+static inline long treeNearestWithin(const psTree *tree, // Tree
+                                     const psVector *coordinates, // Coordinates of interest
+                                     double bestDistance // Distance (radius-squared) to best point
+    )
+{
+#if 1 // Might be in a tight loop
+    PS_ASSERT_TREE_NON_NULL(tree, -1);
+    PS_ASSERT_VECTOR_NON_NULL(coordinates, -1);
+    PS_ASSERT_VECTOR_SIZE(coordinates, (long)tree->dim, -1);
+#endif
+
+    psVector *coords = (coordinates->type.type == PS_TYPE_F64 ? psMemIncrRefCounter((psVector*)coordinates) :
+                        psVectorCopy(NULL, coordinates, PS_TYPE_F64)); // F64 version of coordinates
+
+    // Find the closest point in the leaf that contains the point of interest
+    psTreeNode *leaf = psTreeLeaf(tree, coords); // Leaf containing the point of interest
+    long bestIndex = -1;                // Index of best point
+    treeLeafSearchNearest(&bestIndex, &bestDistance, tree, leaf, coords);
+
+    // Work up from the leaf containing the point of interest, and for each in turn, search down the other
+    // branch.  Every time we search down a branch, we first check to see if the current best distance rules
+    // out that branch.  By having the check within the work queue loop, we allow the best distance to evolve
+    // (smaller), which means our elimination test goes up against the smallest possible distance.
+    psArray *work = psArrayAlloc(tree->numNodes); // Work queue
+    while (leaf->up) {
+        psTreeNode *up = leaf->up;      // Parent node
+
+        long workIndex = 0;
+        work->data[workIndex] = (leaf == up->left ? up->right : up->left); // The other node
+        while (workIndex >= 0) {
+            psTreeNode *node = work->data[workIndex];
+            if (treeBranchDistance(tree, node->index, coords) > bestDistance) {
+                // No need to investigate
+                work->data[workIndex] = NULL;
+                workIndex--;
+                continue;
+            }
+            if (node->left) {
+                // Branch node
+                work->data[workIndex] = node->right;
+                work->data[++workIndex] = node->left;
+                continue;
+            }
+            // Leaf node
+            treeLeafSearchNearest(&bestIndex, &bestDistance, tree, node, coords);
+            work->data[workIndex] = NULL;
+            workIndex--;
+        }
+
+        leaf = up;
+    }
+    psFree(work);
+    psFree(coords);
+
+    return bestIndex;
+}
+
+// Given an arbitrary point, return the index of the nearest neighbour
+long psTreeNearest(const psTree *tree, const psVector *coords)
+{
+    return treeNearestWithin(tree, coords, INFINITY);
+}
+
+
+long psTreeNearestWithin(const psTree *tree, const psVector *coords, double radius)
+{
+    return treeNearestWithin(tree, coords, PS_SQR(radius));
+}
+
+
+// Search a leaf node for points within radius squared
+static inline long treeLeafSearchWithin(double radius2, // Radius squared to search
+                                        const psTree *tree, // Tree of interest
+                                        const psTreeNode *leaf, // Leaf to search
+                                        const psVector *coords // Coordinates of interest
+    )
+{
+    long num = 0;                       // Number within circle
+    for (int i = 0; i < leaf->num; i++) {
+        long index = leaf->contents[i]; // Index of point
+        double distance = treeContentDistance(tree, index, coords); // Distance to point
+        if (distance < radius2) {
+            num++;
+        }
+    }
+    return num;
+}
+
+// Given an arbitrary point and a matching radius, return the number of points within that radius
+long psTreeWithin(const psTree *tree, const psVector *coordinates, double radius)
+{
+#if 1 // Might be in a tight loop
+    PS_ASSERT_TREE_NON_NULL(tree, -1);
+    PS_ASSERT_VECTOR_NON_NULL(coordinates, -1);
+    PS_ASSERT_VECTOR_SIZE(coordinates, (long)tree->dim, -1);
+#endif
+
+    psVector *coords = (coordinates->type.type == PS_TYPE_F64 ? psMemIncrRefCounter((psVector*)coordinates) :
+                        psVectorCopy(NULL, coordinates, PS_TYPE_F64)); // F64 version of coordinates
+
+    radius *= radius;                   // We work with the radius-squared
+    long num = 0;                       // Number of points in circle
+
+    // This is essentially the same as psTreeNearest, except we're not allowed to prune
+
+    // Find the closest point in the leaf that contains the point of interest
+    psTreeNode *leaf = psTreeLeaf(tree, coords); // Leaf containing the point of interest
+    num += treeLeafSearchWithin(radius, tree, leaf, coords);
+
+    psArray *work = psArrayAlloc(tree->numNodes); // Work queue
+    while (leaf->up) {
+        psTreeNode *up = leaf->up;      // Parent node
+
+        long workIndex = 0;
+        work->data[workIndex] = (leaf == up->left ? up->right : up->left); // The other node
+        while (workIndex >= 0) {
+            psTreeNode *node = work->data[workIndex];
+            if (node->left) {
+                // Branch node
+                work->data[workIndex] = node->right;
+                work->data[++workIndex] = node->left;
+                continue;
+            }
+            // Leaf node
+            num += treeLeafSearchWithin(radius, tree, node, coords);
+            work->data[workIndex] = NULL;
+            workIndex--;
+        }
+
+        leaf = up;
+    }
+    psFree(work);
+    psFree(coords);
+
+    return num;
+}
+
+// Search a leaf node for any points within radius squared
+static inline bool treeLeafSearchWithinAny(double radius2, // Radius squared to search
+                                           const psTree *tree, // Tree of interest
+                                           const psTreeNode *leaf, // Leaf to search
+                                           const psVector *coords // Coordinates of interest
+    )
+{
+    for (int i = 0; i < leaf->num; i++) {
+        long index = leaf->contents[i]; // Index of point
+        double distance = treeContentDistance(tree, index, coords); // Distance to point
+        if (distance < radius2) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// Given an arbitrary point and a matching radius, return whether there are any points within that radius
+bool psTreeWithinAny(const psTree *tree, const psVector *coordinates, double radius)
+{
+#if 1 // Might be in a tight loop
+    PS_ASSERT_TREE_NON_NULL(tree, false);
+    PS_ASSERT_VECTOR_NON_NULL(coordinates, false);
+    PS_ASSERT_VECTOR_SIZE(coordinates, (long)tree->dim, false);
+#endif
+
+    psVector *coords = (coordinates->type.type == PS_TYPE_F64 ? psMemIncrRefCounter((psVector*)coordinates) :
+                        psVectorCopy(NULL, coordinates, PS_TYPE_F64)); // F64 version of coordinates
+
+    radius *= radius;                   // We work with the radius-squared
+
+    // This is essentially the same as psTreeWithin, except we can bail as soon as we find something
+
+    // Find the closest point in the leaf that contains the point of interest
+    psTreeNode *leaf = psTreeLeaf(tree, coords); // Leaf containing the point of interest
+    if (treeLeafSearchWithinAny(radius, tree, leaf, coords)) {
+        psFree(coords);
+        return true;
+    }
+
+    psArray *work = psArrayAlloc(tree->numNodes); // Work queue
+    while (leaf->up) {
+        psTreeNode *up = leaf->up;      // Parent node
+
+        long workIndex = 0;
+        work->data[workIndex] = (leaf == up->left ? up->right : up->left); // The other node
+        while (workIndex >= 0) {
+            psTreeNode *node = work->data[workIndex];
+            if (node->left) {
+                // Branch node
+                work->data[workIndex] = node->right;
+                work->data[++workIndex] = node->left;
+                continue;
+            }
+            // Leaf node
+            if (treeLeafSearchWithinAny(radius, tree, node, coords)) {
+                // Clear out the work queue
+                memset(work->data, 0, (workIndex + 1) * sizeof(void));
+                psFree(work);
+                psFree(coords);
+                return true;
+            }
+            work->data[workIndex] = NULL;
+            workIndex--;
+        }
+
+        leaf = up;
+    }
+    psFree(work);
+    psFree(coords);
+
+    return false;
+}
+
+
+psVector *psTreeCoords(psVector *out, const psTree *tree, long index)
+{
+#if 1 // Might be in a tight loop
+    PS_ASSERT_TREE_NON_NULL(tree, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(index, NULL);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(index, tree->numData, NULL);
+#endif
+
+    int dim = tree->dim;                // Dimensions
+
+    out = psVectorRecycle(out, dim, PS_TYPE_F64);
+    memcpy(out->data.F64, tree->data->F64[index], dim * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
+    return out;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/psTree.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/psTree.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/psTree.h	(revision 22322)
@@ -0,0 +1,121 @@
+#ifndef PS_TREE_H
+#define PS_TREE_H
+
+#include <psError.h>
+#include <psVector.h>
+
+/// An array of coordinates for the tree
+///
+/// The coordinate for point i, dimension j is psTreeCoordArray->F64[i][j].  We have a "raw" vector which
+/// points into the operational vector, as is done for psImage.  This keeps all the memory together, reducing
+/// fragmentation.  Currently supports only F64 type, but could be expanded to support multiple types (to save
+/// memory) by changing the F64 member into a union.
+typedef struct {
+    psF64 **F64;                        ///< Coordinates
+    psU8 *raw;                          // Raw vector
+} psTreeCoordArray;
+
+/// A simple kd-tree implementation
+///
+/// This parent tree structure contains all the main information for the tree.  We choose this instead of
+/// including everything within the nodes to avoid memory fragmentation.
+typedef struct {
+    int dim;                            ///< Dimensionality
+    int maxLeafContents;                ///< Maximum number of points on a leaf
+    long numNodes;                      ///< Number of nodes
+    long numData;                       ///< Number of data points
+    struct psTreeNode **nodes;          ///< Array of nodes
+    psTreeCoordArray *min, *max;        ///< Bounding box: minimum and maximum coordinates
+    double *division;                   ///< Division for each node
+    struct psTreeNode *root;            ///< Convenience pointer to root node of tree
+    psTreeCoordArray *data;             ///< Data: coordinates
+    long *contents;                     ///< Indices into the data
+} psTree;
+
+/// A node of the kd-tree
+///
+/// Most of the information is stored in the parent tree (to reduce memory fragmentation).  All the pointers
+/// are views only; the pointers to the nodes are contained within the parent tree's "nodes" element.
+typedef struct psTreeNode {
+    long index;                         ///< Index of node
+    int divideDim;                      ///< Dimension in which to divide
+    int num;                            ///< Number of points in node
+    long *contents;                     ///< Contents of node; view only
+    psTree *tree;                       ///< Parent tree; view only
+    struct psTreeNode *up;              ///< Node higher up; view only
+    struct psTreeNode *left, *right;    ///< Nodes below, to the left and right; views only
+} psTreeNode;
+
+/// Assertion for psTree
+#define PS_ASSERT_TREE_NON_NULL(TREE, RETVAL) \
+if (!(TREE) || !(TREE)->nodes || !(TREE)->min || !(TREE)->max || !(TREE)->division || !(TREE)->root || \
+    !(TREE)->data || !(TREE)->contents) { \
+    psError(PS_ERR_UNEXPECTED_NULL, true, "Tree %s or one of its components is NULL.", #TREE); \
+    return RETVAL; \
+}
+
+/// Allocator for psTreeCoordArray
+psTreeCoordArray *psTreeCoordArrayAlloc(long size, ///< Size of array
+                                        int dim ///< Dimensionality
+                                        );
+
+/// Allocator for psTreeNode
+psTreeNode *psTreeNodeAlloc(long index  ///< Index of node
+                            );
+
+/// Allocator for psTree
+psTree *psTreeAlloc(int dim,            ///< Dimensionality
+                    int maxLeafContents,///< Maximum number of points on a leaf
+                    long numNodes       ///< Number of nodes in tree
+                    );
+
+/// Plant (create) a tree
+///
+/// This is the main startup function.
+psTree *psTreePlant(int dim,            ///< Dimensionality
+                    int maxLeafContents,///< Maximum number of points on a leaf
+                    ...                 ///< psVector for each coordinate
+                    );
+
+/// Return the leaf node containing given coordinates
+///
+/// Returns a view to the pointer.
+psTreeNode *psTreeLeaf(const psTree *tree, ///< Tree
+                       const psVector *coords ///< Coordinates of interest
+                       );
+
+/// Return the index of the nearest neighbour to given coordinates
+long psTreeNearest(const psTree *tree,  ///< Tree
+                   const psVector *coords ///< Coordinates of interest
+                   );
+
+/// Return the index of the nearest neighbour to given coordinates, but within some radius
+long psTreeNearestWithin(const psTree *tree,  ///< Tree
+                         const psVector *coords, ///< Coordinates of interest
+                         double radius  ///< Radius of interest
+                         );
+
+/// Return the number of points within some radius of given coordinates
+long psTreeWithin(const psTree *tree,   ///< Tree
+                  const psVector *coords, ///< Coordinates of interest
+                  double radius         ///< Radius of interest
+                  );
+
+/// Return whether there are any points within some radius of given coordinates
+bool psTreeWithinAny(const psTree *tree,   ///< Tree
+                     const psVector *coords, ///< Coordinates of interest
+                     double radius         ///< Radius of interest
+                     );
+
+/// Return the coordinates of a point in the tree, specified by its index
+psVector *psTreeCoords(psVector *out,   ///< Output vector, or NULL
+                       const psTree *tree, ///< Tree
+                       long index       ///< Index of point in tree
+                       );
+
+/// Print a representation of the tree
+void psTreePrint(FILE *fptr,            ///< File to which to print
+                 const psTree *tree     ///< Tree
+                 );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psLib/src/types/types.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/types/types.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/types/types.i	(revision 22322)
@@ -0,0 +1,10 @@
+/* types headers */
+
+%include "psArray.h"
+%include "psBitSet.h"
+%include "psHash.h"
+%include "psList.h"
+%include "psLookupTable.h"
+%include "psMetadataConfig.h"
+%include "psMetadata.h"
+%include "psPixels.h"
Index: /tags/sj_tags/sj_root_20080929/psLib/src/xml/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/xml/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/xml/.cvsignore	(revision 22322)
@@ -0,0 +1,10 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+*.loT
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/xml/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/xml/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/xml/Makefile.am	(revision 22322)
@@ -0,0 +1,14 @@
+#Makefile for xml functions of psLib
+#
+noinst_LTLIBRARIES = libpslibxml.la
+
+libpslibxml_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(XML_CFLAGS)
+libpslibxml_la_SOURCES = \
+	psXML.c
+
+EXTRA_DIST = xml.i
+
+pkginclude_HEADERS = \
+	psXML.h
+
+CLEANFILES = *~ *.bb *.bbg *.da
Index: /tags/sj_tags/sj_root_20080929/psLib/src/xml/psXML.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/xml/psXML.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/xml/psXML.c	(revision 22322)
@@ -0,0 +1,634 @@
+/** @file  psXML.c
+*
+*  @brief Contains basic XML definitions and operations
+*
+*  This file defines the basic type for an XML struct and functions useful
+*  in creation and usage of XML documents/files.
+*
+*  @ingroup XML
+*
+*  @author David Robbins, MHPCC
+*
+*  @version $Revision: 1.48 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-10-12 23:43:58 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include "psXML.h"
+#include "psAssert.h"
+#include <unistd.h>
+#include <fcntl.h>
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Maximum size of a string */
+#define MAXSTR 256
+#define MAXVEC 2048
+#define MAXBUF 10000
+
+
+static void XMLFree(psXMLDoc *XML)
+{
+    xmlFreeDoc(*XML);
+}
+
+psXMLDoc *psXMLDocAlloc(void)
+{
+    psXMLDoc *XML;
+    XML = psAlloc(sizeof(psXMLDoc));
+    psMemSetDeallocator(XML, (psFreeFunc) XMLFree);
+    return(XML);
+}
+
+psXMLDoc *psMetadataToXMLDoc(const psMetadata *md)
+{
+    PS_ASSERT_PTR_NON_NULL(md, NULL);
+
+    psXMLDoc *XML = psXMLDocAlloc();
+    xmlNode *cur_node = NULL;
+    xmlNode *root = NULL;
+    xmlAttr *prop = NULL;
+    psDataType type;
+    char content[MAXSTR];
+    char vec[MAXVEC];
+    char timeVal[MAXSTR];
+    int i;
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
+    psMetadataItem *item;
+
+    //setup root element
+    if( root == NULL ) {
+        *XML = xmlNewDoc((const xmlChar*)"1.0");
+        root = xmlNewDocNode(*XML, NULL, (const xmlChar*)"metadata", (const xmlChar*)"\n");
+        xmlDocSetRootElement(*XML, root);
+    }
+    item = psMetadataGetAndIncrement(iter);
+    while ( item != NULL ) {
+        type = item->type;
+        switch (type) {
+        case PS_DATA_BOOL:
+            cur_node = xmlNewChild(root, NULL, (const xmlChar*)"item", (const xmlChar*)"\n");
+            prop = xmlNewProp(cur_node, (const xmlChar*)"name", (const xmlChar*)item->name);
+            prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"BOOL");
+            if ( item->data.B )
+                strncpy(content, "TRUE", MAXSTR);
+            else
+                strncpy(content, "FALSE", MAXSTR);
+            prop = xmlNewProp(cur_node, (const xmlChar*)"value", (const xmlChar*)content);
+            break;
+        case PS_DATA_S32:
+            snprintf(content, MAXSTR, "%d", item->data.S32);
+            cur_node = xmlNewChild(root, NULL, (const xmlChar*)"item", (const xmlChar*)"\n");
+            prop = xmlNewProp(cur_node, (const xmlChar*)"name", (const xmlChar*)item->name);
+            prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"S32");
+            prop = xmlNewProp(cur_node, (const xmlChar*)"value", (const xmlChar*)content);
+            break;
+        case PS_DATA_F32:
+            snprintf(content, MAXSTR, "%f", item->data.F32);
+            cur_node = xmlNewChild(root, NULL, (const xmlChar*)"item", (const xmlChar*)"\n");
+            prop = xmlNewProp(cur_node, (const xmlChar*)"name", (const xmlChar*)item->name);
+            prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"F32");
+            prop = xmlNewProp(cur_node, (const xmlChar*)"value", (const xmlChar*)content);
+            break;
+        case PS_DATA_F64:
+            snprintf(content, MAXSTR, "%lf", item->data.F64);
+            cur_node = xmlNewChild(root, NULL, (const xmlChar*)"item", (const xmlChar*)"\n");
+            prop = xmlNewProp(cur_node, (const xmlChar*)"name", (const xmlChar*)item->name);
+            prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"F64");
+            prop = xmlNewProp(cur_node, (const xmlChar*)"value", (const xmlChar*)content);
+            break;
+        case PS_DATA_STRING:
+            cur_node = xmlNewChild(root, NULL, (const xmlChar*)"item", (const xmlChar*)"\n");
+            prop = xmlNewProp(cur_node, (const xmlChar*)"name", (const xmlChar*)item->name);
+            prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"STR");
+            strncpy(content, item->data.str, MAXSTR);
+            prop = xmlNewProp(cur_node, (const xmlChar*)"value", (const xmlChar*)content);
+            break;
+        case PS_DATA_METADATA:
+            i = 0;
+            psXMLDoc *newDoc;
+            xmlNode *new_root = NULL;
+            xmlNode *new_node = NULL;
+            newDoc = psMetadataToXMLDoc(item->data.md);
+            new_root = xmlDocGetRootElement(*newDoc);
+            prop = xmlNewProp(new_root, (const xmlChar*)"name", (const xmlChar*)item->name);
+            prop = xmlNewProp(new_root, (const xmlChar*)"psType", (const xmlChar*)"METADATA");
+            new_node = xmlAddSibling(cur_node, new_root);
+            psFree(newDoc);
+            break;
+        case PS_DATA_TIME:
+            cur_node = xmlNewChild(root, NULL, (const xmlChar*)"time", (const xmlChar*)"\n");
+            prop = xmlNewProp(cur_node, (const xmlChar*)"name", (const xmlChar*)item->name);
+            if ( ((psTime*)(item->data.V))->type == PS_TIME_UTC )
+                prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"PS_TIME_UTC");
+            else if ( ((psTime*)(item->data.V))->type == PS_TIME_TAI )
+                prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"PS_TIME_TAI");
+            else if ( ((psTime*)(item->data.V))->type == PS_TIME_UT1 )
+                prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"PS_TIME_UT1");
+            else if ( ((psTime*)(item->data.V))->type == PS_TIME_TT )
+                prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"PS_TIME_TT");
+            else {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                        _("Failed to recognize XML content.  Invalid syntax."));
+                return NULL;
+            }
+            snprintf(content, MAXSTR, "%ld, ", (long)((psTime*)(item->data.V))->sec);
+            strncpy(timeVal, content, MAXSTR);
+            snprintf(content, MAXSTR, "%u, ", ((psTime*)(item->data.V))->nsec);
+            strncat(timeVal, content, MAXSTR);
+            if ( ((psTime*)(item->data.V))->leapsecond )
+                strncat(timeVal, "T", MAXSTR);
+            else
+                strncat(timeVal, "F", MAXSTR);
+            prop = xmlNewProp(cur_node, (const xmlChar*)"value", (const xmlChar*)timeVal);
+            break;
+        case PS_DATA_VECTOR:
+            cur_node = xmlNewChild(root, NULL, (const xmlChar*)"vector", (const xmlChar*)"\n");
+            prop = xmlNewProp(cur_node, (const xmlChar*)"name", (const xmlChar*)item->name);
+            type = ((psVector*)(item->data.V))->type.type;
+            strncpy(vec, "", MAXSTR);
+            switch (type) {
+            case PS_DATA_S32:
+                prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"S32");
+                for (i = 0; ((psVector*)(item->data.V))->data.S32[i] != 0; i++) {
+                    snprintf(content, MAXSTR, "%d",
+                             ((psVector*)(item->data.V))->data.S32[i]);
+                    if ( ((psVector*)(item->data.V))->data.S32[i+1] != 0 ) {
+                        strncat(content, ", ", 2);
+                    }
+                    strncat(vec, content, MAXSTR);
+                }
+                break;
+            case PS_DATA_F32:
+                prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"F32");
+                for (i = 0; ((psVector*)(item->data.V))->data.F32[i] != 0; i++) {
+                    snprintf(content, MAXSTR, "%f",
+                             ((psVector*)(item->data.V))->data.F32[i]);
+                    if ( ((psVector*)(item->data.V))->data.F32[i+1] != 0 ) {
+                        strncat(content, ", ", 2);
+                    }
+                    strncat(vec, content, MAXSTR);
+                }
+                break;
+            case PS_DATA_F64:
+                prop = xmlNewProp(cur_node, (const xmlChar*)"psType", (const xmlChar*)"F64");
+                for (i = 0; ((psVector*)(item->data.V))->data.F64[i] != 0; i++) {
+                    snprintf(content, MAXSTR, "%lf",
+                             ((psVector*)(item->data.V))->data.F64[i]);
+                    if ( ((psVector*)(item->data.V))->data.F64[i+1] != 0 ) {
+                        strncat(content, ", ", 2);
+                    }
+                    strncat(vec, content, MAXSTR);
+                }
+                break;
+            case PS_DATA_UNKNOWN:
+            default:
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                        _("Failed to recognize datatype from/for XML file."));
+                psFree(XML);
+                psFree(iter);
+                return NULL;
+            }
+            prop = xmlNewProp(cur_node, (const xmlChar*)"value", (const xmlChar*)vec);
+            break;
+        case PS_DATA_UNKNOWN:
+        default:
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Failed to recognize datatype from/for XML file."));
+            psFree(XML);
+            psFree(iter);
+            return NULL;
+        }
+        item = psMetadataGetAndIncrement(iter);
+    }
+    //    xmlSaveFormatFile("xmlfname", *XML, 1);
+    psFree(iter);
+    return XML;
+}
+
+//compares strings and returns the correct data type//
+static psDataType getType(const char *in)
+{
+    //Returns types S32, F32, F64, STRING, BOOL, or UNKNOWN on ERROR
+    if ( !strncmp(in, "S32", MAXSTR) ) {
+        return(PS_DATA_S32);
+    } else if ( !strncmp(in, "F32", MAXSTR) ) {
+        return(PS_DATA_F32);
+    } else if ( !strncmp(in, "F64", MAXSTR) ) {
+        return(PS_DATA_F64);
+    } else if ( !strncmp(in, "STR", MAXSTR) ) {
+        return(PS_DATA_STRING);
+    } else if ( !strncmp(in, "BOOL", MAXSTR) ) {
+        return(PS_DATA_BOOL);
+    }
+
+    return(PS_DATA_UNKNOWN);
+}
+
+static void storeValues(psVector *inVector, char *in, psDataType type)
+{
+    int i;
+    char *endp;
+
+    switch (type) {
+    case PS_DATA_S32:
+        i = 0;
+        int intValue = 0;
+        while (i < MAXSTR) {
+            intValue = (int)strtol(in, &endp, 10);
+            if ( intValue != 0 ) {
+                inVector->data.S32[i] = intValue;
+                i++;
+                while (!strncmp(endp, ",", 1))
+                    endp++;
+                in = endp;
+            } else
+                i = MAXSTR;
+        }
+        break;
+    case PS_DATA_F32:
+        i = 0;
+        float floatValue = 0.0;
+        while (i < MAXSTR) {
+            floatValue = strtof(in, &endp);
+            if ( floatValue != 0.0 ) {
+                inVector->data.F32[i] = floatValue;
+                i++;
+                while (!strncmp(endp, ",", 1))
+                    endp++;
+                in = endp;
+            } else
+                i = MAXSTR;
+        }
+
+        break;
+    case PS_DATA_F64:
+        i = 0;
+        double doubleValue = 0.0;
+        while (i < MAXSTR) {
+            doubleValue = strtod(in, &endp);
+            if ( doubleValue != 0.0 ) {
+                inVector->data.F64[i] = doubleValue;
+                i++;
+                while (!strncmp(endp, ",", 1))
+                    endp++;
+                in = endp;
+            } else
+                i = MAXSTR;
+        }
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Failed to recognize datatype from/for XML file."));
+    }
+}
+
+static void storeTime(psTime *out, char *in)
+{
+    char *endp;
+    long sec = 0;
+    unsigned int nsec = 0;
+
+    sec = strtol(in, &endp, 10);
+    out->sec = sec;
+    if ( !strncmp(endp, ",", 1) )
+        endp++;
+    else
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Failed to recognize XML content.  Invalid syntax."));
+    in = endp;
+    nsec = (unsigned int)strtol(in, &endp, 10);
+    out->nsec = nsec;
+    if ( !strncmp(endp, ",", 1) )
+        endp++;
+    else
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Failed to recognize XML content.  Invalid syntax."));
+    in = endp;
+    if ( !strncmp(in, "false", 10) || !strncmp(in, "f", 10) ||
+            !strncmp(in, "FALSE", 10) || !strncmp(in, " F", 10) )
+        out->leapsecond = false;
+    else if ( !strncmp(in, "true", 10) || !strncmp(in, "t", 10) ||
+              !strncmp(in, "TRUE", 10) || !strncmp(in, "T", 10) )
+        out->leapsecond = true;
+    else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                _("Failed to recognize XML content.  Invalid syntax."));
+    }
+}
+
+//Check the xml item for errors.  Returns the data type or PS_DATA_UNKNOWN for errors.//
+static psDataType chkType(xmlNode *node)
+{
+    //NEED TO CHECK CONTENTS OF EACH ITEM/NODE //
+    //if item then,
+    if(!strncmp((const char*)node->name, "item", MAXSTR) || !strncmp((const char*)node->name, "ITEM", MAXSTR)) {
+        if (node->properties != NULL && node->properties->name != NULL
+                && node->properties->next != NULL && node->properties->next->name != NULL
+                && node->properties->next->next != NULL
+                && node->properties->next->next->name != NULL) {
+            psDataType type = getType((const char*)node->properties->next->children->content);
+            return type;
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Failed to recognize XML content.  Invalid syntax."));
+            return(PS_DATA_UNKNOWN);
+        }
+    }
+    //if vector then,
+    if(!strncmp((const char*)node->name, "vector", MAXSTR) || !strncmp((const char*)node->name, "VECTOR", MAXSTR)) {
+        if (node->properties != NULL && node->properties->name != NULL
+                && node->properties->next != NULL && node->properties->next->name != NULL
+                && node->properties->next->next != NULL
+                && node->properties->next->next->name != NULL) {
+            return(PS_DATA_VECTOR);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Failed to recognize XML content.  Invalid syntax."));
+            return(PS_DATA_UNKNOWN);
+        }
+
+    }
+    //if metadata then,
+    if(!strncmp((const char*)node->name, "metadata", MAXSTR)) {
+        if (node->properties != NULL && node->properties->name
+                && node->properties->next != NULL && node->properties->next->name != NULL) {
+            return(PS_DATA_METADATA);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Failed to recognize XML content.  Invalid syntax."));
+            return(PS_DATA_UNKNOWN);
+        }
+    }
+    if(!strncmp((const char*)node->name, "time", MAXSTR) || !strncmp((const char*)node->name, "TIME", MAXSTR) ) {
+        if (node->properties != NULL && node->properties->name
+                && node->properties->next != NULL && node->properties->next->name != NULL) {
+            return(PS_DATA_TIME);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    _("Failed to recognize XML content.  Invalid syntax."));
+            return(PS_DATA_UNKNOWN);
+        }
+    }
+    return(PS_DATA_UNKNOWN);
+}
+
+//Sorts the xmlDocPtr data into a usable Metadata structure.  Transverses an xml tree.//
+static psMetadata *xml2metadata(xmlNode *nodePtr, int nodeNum)
+{
+    psMetadata *meta = NULL;
+    xmlNode *cur_node = NULL;
+    char name[MAXSTR];
+    char content[MAXSTR];
+    //    char *name = NULL;
+    //    char *content = NULL;
+    psDataType type = PS_DATA_UNKNOWN;
+    psDataType elemType = PS_DATA_UNKNOWN;
+
+    meta = psMetadataAlloc();
+    cur_node = nodePtr;
+
+    //    for ( cur_node = nodePtr; cur_node != NULL; cur_node = cur_node->next )
+    while (cur_node != NULL) {
+        if ( cur_node->type == XML_ELEMENT_NODE ) {
+            //if the root node isn't a metadata//
+            if ( nodeNum == 0 && strncmp((const char*)cur_node->name, "metadata", MAXSTR) ) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                        _("Failed to recognize XML content.  Invalid syntax."));
+                psFree(meta);
+                return NULL;
+            }
+
+            if ( nodeNum != 0 ) {
+                type = chkType(cur_node);
+                //name = NULL;
+                //content = NULL;
+                switch ( type ) {
+                case PS_DATA_S32:
+                    strncpy(name, (char*)cur_node->properties->children->content, MAXSTR);
+                    strncpy(content,
+                            (char*)cur_node->properties->next->next->children->content, MAXSTR);
+                    psMetadataAddS32(meta, PS_LIST_TAIL, name, 0, "",
+                                     (int)strtol(content, NULL, 10));
+                    break;
+                case PS_DATA_F32:
+                    strncpy(name, (const char*)cur_node->properties->children->content, MAXSTR);
+                    strncpy(content,
+                            (const char*)cur_node->properties->next->next->children->content, MAXSTR);
+                    psMetadataAddF32(meta, PS_LIST_TAIL, name, 0, "", strtof(content, NULL));
+                    break;
+                case PS_DATA_F64:
+                    strncpy(name, (const char*)cur_node->properties->children->content, MAXSTR);
+                    strncpy(content,
+                            (const char*)cur_node->properties->next->next->children->content, MAXSTR);
+                    psMetadataAddF64(meta, PS_LIST_TAIL, name, 0, "", strtod(content, NULL));
+                    break;
+                case PS_DATA_STRING:
+                    strncpy(name, (const char*)cur_node->properties->children->content, MAXSTR);
+                    psMetadataAddStr(meta, PS_LIST_TAIL, name, 0, "",
+                                     (const char*)cur_node->properties->next->next->children->content);
+                    break;
+                case PS_DATA_VECTOR:
+                    strncpy(name, (const char*)cur_node->properties->children->content, MAXSTR);
+                    strncpy(content,
+                            (const char*)cur_node->properties->next->next->children->content, MAXSTR);
+                    elemType = getType((const char*)cur_node->properties->next->children->content);
+                    if (elemType == PS_DATA_UNKNOWN || elemType == PS_DATA_STRING
+                            || elemType == PS_DATA_BOOL) {
+                        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                                _("Failed to recognize datatype from/for XML file."));
+                        psFree(meta);
+                        return NULL;
+                    }
+
+                    psVector *vec = psVectorAlloc(strlen(content), elemType);
+                    storeValues(vec, content, elemType);
+                    psMetadataAddVector(meta, PS_LIST_TAIL, name, 0, "", vec);
+                    psFree(vec);
+                    break;
+                case PS_DATA_TIME:
+                    strncpy(name, (const char*)cur_node->properties->children->content, MAXSTR);
+                    strncpy(content,
+                            (const char*)cur_node->properties->next->next->children->content, MAXSTR);
+                    psTimeType timeType;
+                    char type[MAXSTR];
+                    strncpy(type, (const char*)cur_node->properties->next->children->content, MAXSTR);
+                    if ( !strncmp(type, "PS_TIME_UTC", MAXSTR) )
+                        timeType = PS_TIME_UTC;
+                    else if ( !strncmp(type, "PS_TIME_TAI", MAXSTR) )
+                        timeType = PS_TIME_TAI;
+                    else if ( !strncmp(type, "PS_TIME_UT1", MAXSTR) )
+                        timeType = PS_TIME_UT1;
+                    else if ( !strncmp(type, "PS_TIME_TT", MAXSTR) )
+                        timeType = PS_TIME_TT;
+                    else {
+                        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                                _("Failed to recognize XML content.  Invalid syntax."));
+                        psFree(meta);
+                        return NULL;
+                    }
+
+                    psTime *time;
+                    time = psTimeAlloc(timeType);
+                    storeTime(time, content);
+                    psMetadataAddTime(meta, PS_LIST_TAIL, name, 0, "", time);
+                    psFree(time);
+                    break;
+                case PS_DATA_METADATA:
+                    strncpy(name, (const char*)cur_node->properties->children->content, MAXSTR);
+                    psMetadata *temp = NULL;
+                    temp = xml2metadata(cur_node->children, nodeNum);
+                    psMetadataAddMetadata(meta, PS_LIST_TAIL, name, 0, "", temp);
+                    psFree(temp);
+                    break;
+                case PS_DATA_BOOL:
+                    strncpy(name, (const char*)cur_node->properties->children->content, MAXSTR);
+                    strncpy(content,
+                            (const char*)cur_node->properties->next->next->children->content, MAXSTR);
+                    if ( !strncmp(content, "true", MAXSTR) ||
+                            !strncmp(content, "TRUE", MAXSTR) ||
+                            !strncmp(content, "T", MAXSTR) ||
+                            !strncmp(content, "t", MAXSTR) ) {
+                        psMetadataAddBool(meta, PS_LIST_TAIL, name, 0, "", true);
+                    } else if ( !strncmp(content, "false", MAXSTR) ||
+                                !strncmp(content, "FALSE", MAXSTR) ||
+                                !strncmp(content, "F", MAXSTR) ||
+                                !strncmp(content, "f", MAXSTR) ) {
+                        psMetadataAddBool(meta, PS_LIST_TAIL, name, 0, "", false);
+                    } else {
+                        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                                _("Failed to recognize XML content.  Invalid syntax."));
+                        psFree(meta);
+                        return NULL;
+                    }
+                    break;
+                case PS_DATA_UNKNOWN:
+                default:
+                    psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                            _("Failed to recognize datatype from/for XML file."));
+                    psFree(meta);   //XXX: Do I really need this?
+                    return NULL;
+                }
+                nodeNum++;
+            } else //if root node, then increment nodeNum and go to children//
+            {
+                nodeNum++;
+                cur_node = cur_node->children;
+            }
+        }
+        cur_node = cur_node->next;
+    }
+    psFree(cur_node);
+    return meta;
+}
+
+psMetadata *psXMLDocToMetadata(const psXMLDoc *doc)
+{
+    PS_ASSERT_PTR_NON_NULL(doc, NULL);
+    //    psMetadata *md = psMetadataAlloc();
+    psMetadata *md = NULL;
+    xmlNode *root_element = NULL;
+    //XXX: doc changed to *doc
+    root_element = xmlDocGetRootElement(*doc);
+    md = xml2metadata(root_element, 0);
+    return md;
+}
+
+psXMLDoc *psXMLParseFile(const char *filename)
+{
+
+    PS_ASSERT_PTR_NON_NULL(filename, NULL);
+    psXMLDoc *XML;
+    XML = psXMLDocAlloc();
+    *XML = xmlReadFile(filename, NULL, 0);
+
+    xmlCleanupParser();
+    xmlMemoryDump();
+    return XML;
+}
+
+bool psXMLDocToFile(const psXMLDoc *doc,
+                    const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(doc, 0);
+    PS_ASSERT_PTR_NON_NULL(filename, 0);
+    FILE *file;
+    if( (file = fopen(filename, "w")) == NULL ) {
+        psError(PS_ERR_IO, true, _("Failed to open file '%s'. Check if it exists and it has the proper permissions."), filename);
+        return false;
+    }
+
+    xmlDocDump(file, *doc);
+    fclose(file);
+    return true;
+}
+
+psXMLDoc *psXMLParseMem(const char *buffer,
+                        int size)
+{
+    PS_ASSERT_PTR_NON_NULL(buffer, NULL);
+    PS_ASSERT_INT_NONZERO(size, NULL);
+    char *URL = "new.xml";
+    psXMLDoc *XML;
+    XML = psXMLDocAlloc();
+    *XML = xmlReadMemory(buffer, size, URL, NULL, 0);
+
+    xmlCleanupParser();
+    xmlMemoryDump();
+
+    return XML;
+}
+
+bool psXMLDocToMem(const psXMLDoc *doc,
+                   char *buffer)
+{
+    PS_ASSERT_PTR_NON_NULL(doc, 0);
+    int bufferSize;
+    xmlChar *buff;
+
+    xmlDocDumpMemory(*doc, &buff, &bufferSize);
+    if ( MAXVEC < strlen((char *)buff) ) {
+        psError(PS_ERR_LOCATION_INVALID, true, _("Buffer to small to store XML doc."));
+        //        xmlFree(buff);
+        return false;
+    }
+    strncpy(buffer, (const char*)buff, MAXVEC);
+    xmlFree(buff);
+
+    return true;
+}
+
+psXMLDoc *psXMLParseFD(int fd)
+{
+    PS_ASSERT_INT_NONNEGATIVE(fd, NULL);
+    char *URL = "new.xml";
+    psXMLDoc *XML;
+    XML = psXMLDocAlloc();
+    *XML = xmlReadFd(fd, URL, NULL, 0);
+
+    xmlCleanupParser();
+    xmlMemoryDump();
+
+    return XML;
+}
+
+bool psXMLDocToFD(const psXMLDoc *doc,
+                  int fd)
+{
+    PS_ASSERT_PTR_NON_NULL(doc, 0);
+    PS_ASSERT_INT_NONNEGATIVE(fd, 0);
+
+    char buf[MAXBUF];
+    int n;
+    if ( psXMLDocToMem(doc, buf) ) {
+        n = strlen(buf);
+        write(fd, buf, n);
+    } else {
+        psError(PS_ERR_LOCATION_INVALID, true, _("Buffer to small to store XML doc."));
+        return false;
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/src/xml/psXML.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/xml/psXML.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/xml/psXML.h	(revision 22322)
@@ -0,0 +1,120 @@
+/* @file  psXML.h
+ * @brief Contains basic XML definitions and operations
+ *
+ * This file defines the basic type for an XML struct and functions useful
+ * in creation and usage of XML documents/files.
+ *
+ * @author David Robbins, MHPCC
+ *
+ * @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-23 22:47:23 $
+ * Copyright 2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_XML_H
+#define PS_XML_H
+
+/// @addtogroup FileIO Input/Output
+/// @{
+
+#include <libxml/parser.h>
+//#include <libxml/tree.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "psMetadata.h"
+#include "psMetadataConfig.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psString.h"
+#include "psConstants.h"
+#include "psTime.h"
+
+/** XML wrapper pointing to an XML document in memory */
+typedef xmlDocPtr psXMLDoc;
+
+
+/*****************************************************************************/
+
+/* FUNCTION PROTOTYPES                                                       */
+
+/*****************************************************************************/
+
+/** Allocates a new psXMLDoc
+ *
+ *  @return psXMLDoc*         a new psXMLDoc object
+*/
+psXMLDoc *psXMLDocAlloc(void);
+
+/** Converts a psMetadata data structure to a complete psXMLDoc (in memory)
+ *
+ *  @return psXMLDoc*           Pointer to XML document from read
+*/
+psXMLDoc *psMetadataToXMLDoc(
+    const psMetadata *md               ///< psMetadata structure to read
+);
+
+/** Converts a complete psXMLDoc (in memory) to a psMetadata data structure
+ *
+ *  @return psMetadata*         Pointer to psMetadata data structure from read
+*/
+psMetadata *psXMLDocToMetadata(
+    const psXMLDoc *doc                ///< psXMLDoc to read
+);
+
+/** Loads the data in a named file into a complete psXMLDoc (in memory)
+ *
+ *  @return psXMLDoc*       Pointer to resulting XML document from read
+*/
+psXMLDoc *psXMLParseFile(
+    const char *filename               ///< Filename of file to be read
+);
+
+/** Writes out a complete psXMLDoc (in memory) to a named file
+ *
+ *  @return bool:       True on success or false otherwise.
+*/
+bool psXMLDocToFile(
+    const psXMLDoc *doc,               ///< psXMLDoc to read
+    const char *filename               ///< Filename of file to be written
+);
+
+/** Accepts a block of memory and parses it into a complete psXMLDoc (also in memory)
+ *
+ *  @return psXMLDoc*       Pointer to resulting XML document from read
+*/
+psXMLDoc *psXMLParseMem(
+    const char *buffer,                ///< Buffer to read
+    int size                           ///< Size of buffer
+);
+
+/** Accepts a complete psXMLDoc (in memory) and parses it into a block of memory
+ *
+ *  @return bool:       True on success or false otherwise.
+*/
+bool psXMLDocToMem(
+    const psXMLDoc *doc,               ///< psXMLDoc to read
+    char *buffer                       ///< Buffer to write
+);
+
+/** Reads from a file descriptor and converts the incoming data to a complete
+ *  psXMLDoc (in memory)
+ *
+ *  @return psXMLDoc*      Pointer to resulting XML document from read
+*/
+psXMLDoc *psXMLParseFD(
+    int fd                             ///< File descriptor to read
+);
+
+/** Reads from a complete psXMLDoc (in memory) and writes it to a file descriptor
+ *
+ *  @return bool:       True on success or false otherwise.
+*/
+bool psXMLDocToFD(
+    const psXMLDoc *doc,               ///< psXMLDoc to read
+    int fd                             ///< File descriptor to write
+);
+
+/// @}
+#endif // #ifndef PS_XML_H
Index: /tags/sj_tags/sj_root_20080929/psLib/src/xml/xml.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/src/xml/xml.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/src/xml/xml.i	(revision 22322)
@@ -0,0 +1,2 @@
+/* xml headers */
+%include "psXML.h"
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+psLibModule
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/Makefile.PL
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/Makefile.PL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/Makefile.PL	(revision 22322)
@@ -0,0 +1,35 @@
+# File : Makefile.PL
+use ExtUtils::MakeMaker;
+use FileHandle;
+
+sub ReadSetup {
+  my $filename = shift;
+      #return val
+  my %opts;
+
+  my $fh = FileHandle->new($filename, 'r');
+  while(<$fh>) {
+    /([a-z]+)=(.+)/ && do {
+      $opts{$1} = $2;
+      next;
+    };
+
+    #default:
+    die "Error:  syntaxt error in setup.txt line $_";
+  }
+  return %opts;
+}
+
+###############  Main ##############
+
+my %opts = ReadSetup("setup.txt");
+
+
+WriteMakefile(
+	'NAME' => 'pslib',             # Name of package
+	'INC' => $opts{includes},
+	'LIBS' => ["$opts{ldflags}"],  # Name of custom libraries
+	'OBJECT' => 'pslib_wrap.o',    # Name of object
+	'OPTIMIZE' => "$opts{cflags} -w", # cflags with disabling of warnings (SWIG generated code generates warnings)
+	'LD' => $opts{ld}
+);
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/Makefile.am	(revision 22322)
@@ -0,0 +1,47 @@
+
+EXTRA_DIST = pslib.i Makefile.PL example/phase2.pl example/data/*
+
+clean-generic:
+	rm -rf psLibModule
+
+# this is going into a make file one directory deeper in the tree so the
+# realitive paths need to be adjusted
+SWIGINC=`echo "${SRCINC}" | ${PERL} -pe "s|-I|-I../|g"`
+
+####################################################
+## PsLibModule swig build section.
+
+CLEANFILES = psLibModule
+
+psLibModule:
+	if [ ! -d psLibModule ]; then mkdir psLibModule; fi
+
+
+psLibModule/Makefile.PL: psLibModule
+	cp -f $(srcdir)/Makefile.PL psLibModule/Makefile.PL
+
+psLibModule/setup.txt: psLibModule
+	echo "includes=-I../$(top_srcdir)/src $(SWIGINC) $(PSLIB_CFLAGS)" > psLibModule/setup.txt
+	echo "ldflags=-L$(top_builddir)/src $(PSLIB_LIBS)" >> psLibModule/setup.txt
+	echo "cflags=$(CFLAGS) -std=c99 -Wno-unused -Wno-strict-aliasing" >> psLibModule/setup.txt
+
+psLibModule/pslib_wrap.c: psLibModule $(srcdir)/pslib.i
+	$(SWIG) -perl $(SRCINC) -w451 -o $@ $(srcdir)/pslib.i
+
+psLibModule/Makefile: psLibModule psLibModule/Makefile.PL psLibModule/setup.txt
+	cd psLibModule && $(PERL) Makefile.PL PREFIX=$(PERL_PREFIX)
+
+all: swig
+
+swig: psLibModule/pslib_wrap.c psLibModule/setup.txt psLibModule/Makefile
+	cd psLibModule && make
+
+swig-check: swig
+	export PERLLIB=psLibModule:psLibModule/blib/arch/auto/pslib && perl -Mpslib -e 'print "pslib version = " . pslib::psLibVersion() . "\n";'
+
+swig-install: swig
+	cd psLibModule && make install
+
+install-exec-hook: swig-install
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/badpix.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/badpix.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/badpix.fits	(revision 22322)
@@ -0,0 +1,50 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -64 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   10 / length of data axis 1                          NAXIS2  =                   10 / length of data axis 2                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             p
+
+import os, re, sys
+from math import *
+import pdb
+import rhl
+
+if 0:
+    try:
+        import psLib
+        import ps
+    except: pass
+    
+try:
+    from actUtils import *
+except:
+    print "Failed to import actUtils"
+
+try:
+    from actSim import *
+except:
+    print "Failed to import actSim"
+
+try: import objects
+except: pass
+
+def tb():
+    pdb.pm()
+
+def grep(pattern, list):
+    print filter(lambda x: re.search(pattern, x), list)
+
+try:
+    import psLib
+    mortal = rhl.memCheckLeaks; id0=psLib.psMemGetId()
+except:
+    pass
+
+try:
+    from ds9 import mtv, dot, erase, line, zoom
+except:
+    print "Failed to load ds9"
+    pass
+
+###############################################################################
+
+class foo:
+    def __init__(self, a = 1, b = 2):
+        self.__dict__['a'] = a
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/base.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/base.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/base.config	(revision 22322)
@@ -0,0 +1,3 @@
+#
+SITE    STR     data/site.config
+
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/bias.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/bias.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/bias.fits	(revision 22322)
@@ -0,0 +1,50 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -64 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   10 / length of data axis 1                          NAXIS2  =                   10 / length of data axis 2                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             p
+
+import os, re, sys
+from math import *
+import pdb
+import rhl
+
+if 0:
+    try:
+        import psLib
+        import ps
+    except: pass
+    
+try:
+    from actUtils import *
+except:
+    print "Failed to import actUtils"
+
+try:
+    from actSim import *
+except:
+    print "Failed to import actSim"
+
+try: import objects
+except: pass
+
+def tb():
+    pdb.pm()
+
+def grep(pattern, list):
+    print filter(lambda x: re.search(pattern, x), list)
+
+try:
+    import psLib
+    mortal = rhl.memCheckLeaks; id0=psLib.psMemGetId()
+except:
+    pass
+
+try:
+    from ds9 import mtv, dot, erase, line, zoom
+except:
+    print "Failed to load ds9"
+    pass
+
+###############################################################################
+
+class foo:
+    def __init__(self, a = 1, b = 2):
+        self.__dict__['a'] = a
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/camera-0.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/camera-0.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/camera-0.config	(revision 22322)
@@ -0,0 +1,17 @@
+#
+# Fakes for phase2
+#
+RECIPE.PHASE2   STR  data/recipe.config
+NCHIPS          S32  2
+
+NCELLS          S32  3
+CELL_NROW       S32  20
+CELL_NCOL       S32  20
+
+NREADOUTS.0.0   S32  1
+NREADOUTS.0.1   S32  1
+NREADOUTS.0.2   S32  1
+
+NREADOUTS.1.0   S32  1
+NREADOUTS.1.1   S32  1
+NREADOUTS.1.2   S32  1
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/dark.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/dark.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/dark.fits	(revision 22322)
@@ -0,0 +1,50 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -64 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   10 / length of data axis 1                          NAXIS2  =                   10 / length of data axis 2                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             p
+
+import os, re, sys
+from math import *
+import pdb
+import rhl
+
+if 0:
+    try:
+        import psLib
+        import ps
+    except: pass
+    
+try:
+    from actUtils import *
+except:
+    print "Failed to import actUtils"
+
+try:
+    from actSim import *
+except:
+    print "Failed to import actSim"
+
+try: import objects
+except: pass
+
+def tb():
+    pdb.pm()
+
+def grep(pattern, list):
+    print filter(lambda x: re.search(pattern, x), list)
+
+try:
+    import psLib
+    mortal = rhl.memCheckLeaks; id0=psLib.psMemGetId()
+except:
+    pass
+
+try:
+    from ds9 import mtv, dot, erase, line, zoom
+except:
+    print "Failed to load ds9"
+    pass
+
+###############################################################################
+
+class foo:
+    def __init__(self, a = 1, b = 2):
+        self.__dict__['a'] = a
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/data.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/data.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/data.fits	(revision 22322)
@@ -0,0 +1,50 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -64 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   10 / length of data axis 1                          NAXIS2  =                   10 / length of data axis 2                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             p
+
+import os, re, sys
+from math import *
+import pdb
+import rhl
+
+if 0:
+    try:
+        import psLib
+        import ps
+    except: pass
+    
+try:
+    from actUtils import *
+except:
+    print "Failed to import actUtils"
+
+try:
+    from actSim import *
+except:
+    print "Failed to import actSim"
+
+try: import objects
+except: pass
+
+def tb():
+    pdb.pm()
+
+def grep(pattern, list):
+    print filter(lambda x: re.search(pattern, x), list)
+
+try:
+    import psLib
+    mortal = rhl.memCheckLeaks; id0=psLib.psMemGetId()
+except:
+    pass
+
+try:
+    from ds9 import mtv, dot, erase, line, zoom
+except:
+    print "Failed to load ds9"
+    pass
+
+###############################################################################
+
+class foo:
+    def __init__(self, a = 1, b = 2):
+        self.__dict__['a'] = a
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/flat.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/flat.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/flat.fits	(revision 22322)
@@ -0,0 +1,50 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -64 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   10 / length of data axis 1                          NAXIS2  =                   10 / length of data axis 2                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             p
+
+import os, re, sys
+from math import *
+import pdb
+import rhl
+
+if 0:
+    try:
+        import psLib
+        import ps
+    except: pass
+    
+try:
+    from actUtils import *
+except:
+    print "Failed to import actUtils"
+
+try:
+    from actSim import *
+except:
+    print "Failed to import actSim"
+
+try: import objects
+except: pass
+
+def tb():
+    pdb.pm()
+
+def grep(pattern, list):
+    print filter(lambda x: re.search(pattern, x), list)
+
+try:
+    import psLib
+    mortal = rhl.memCheckLeaks; id0=psLib.psMemGetId()
+except:
+    pass
+
+try:
+    from ds9 import mtv, dot, erase, line, zoom
+except:
+    print "Failed to load ds9"
+    pass
+
+###############################################################################
+
+class foo:
+    def __init__(self, a = 1, b = 2):
+        self.__dict__['a'] = a
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/fringe.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/fringe.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/fringe.fits	(revision 22322)
@@ -0,0 +1,50 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -64 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   10 / length of data axis 1                          NAXIS2  =                   10 / length of data axis 2                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             p
+
+import os, re, sys
+from math import *
+import pdb
+import rhl
+
+if 0:
+    try:
+        import psLib
+        import ps
+    except: pass
+    
+try:
+    from actUtils import *
+except:
+    print "Failed to import actUtils"
+
+try:
+    from actSim import *
+except:
+    print "Failed to import actSim"
+
+try: import objects
+except: pass
+
+def tb():
+    pdb.pm()
+
+def grep(pattern, list):
+    print filter(lambda x: re.search(pattern, x), list)
+
+try:
+    import psLib
+    mortal = rhl.memCheckLeaks; id0=psLib.psMemGetId()
+except:
+    pass
+
+try:
+    from ds9 import mtv, dot, erase, line, zoom
+except:
+    print "Failed to load ds9"
+    pass
+
+###############################################################################
+
+class foo:
+    def __init__(self, a = 1, b = 2):
+        self.__dict__['a'] = a
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/recipe.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/recipe.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/recipe.config	(revision 22322)
@@ -0,0 +1,35 @@
+#
+# Default filenames
+#
+BADPIX.IMAGE    STR     FILE:data/badpix.fits
+BIAS.IMAGE      STR     FILE:data/bias.fits
+DARK.IMAGE      STR     FILE:data/dark.fits
+FLAT.IMAGE      STR     FILE:data/flat.fits
+FRINGE.IMAGE    STR     FILE:data/fringe.fits
+#
+BIAS.OVERSCAN STR  HEADER:BIASSEC
+
+BIAS.OVERSCAN.STATS     STR     XXX
+
+# BIAS.OVERSCAN.FIT: NONE POLYNOMIAL SPLINE 
+BIAS.OVERSCAN.FIT       STR     NONE
+
+BIAS.OVERSCAN.FIT.NBIN  S32     10
+BIAS.OVERSCAN.FIT.NPTS  S32     10
+BIAS.OVERSCAN.FIT.ORDER S32     1
+
+# The dark scaling
+DARK.FACTOR     STR     VALUE:1
+
+# Linearity corrections
+#NONLINEAR.METHOD: LOOKUP NONE POLYNOMIAL
+NONLINEAR.METHOD   STR  NONE
+NONLINEAR.ORDER    S32  3               # Why do we need this? It's implicit in .COEFFS
+@NONLINEAR.COEFFS  F64 1 2 3
+NONLINEAR.IN_FLUX          STR  foo.in.fits
+NONLINEAR.OUT_FLUX         STR  foo.out.fits
+
+#
+# How to trim the file
+#
+TRIM.DATASEC    STR     HEADER:DATASEC
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/site.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/site.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/data/site.config	(revision 22322)
@@ -0,0 +1,1 @@
+CAMERA.0 STR data/camera-0.config # Camera filename
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/example/phase2.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/example/phase2.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/example/phase2.pl	(revision 22322)
@@ -0,0 +1,484 @@
+#!/usr/bin/perl -w
+package phase2;
+
+use strict;
+use pslib;
+#
+# Prototypes
+#
+sub DefineStats($);
+sub DirectionFromOverscan($);
+sub bias_subtract_readout($$$; @);
+sub dark_subtract_readout($$$);
+sub defringe_readout($$$$);
+sub getScalingFactor($$$);
+sub load_image($$$$);
+sub make_kernel();
+sub make_mask($$);
+sub mask_readout($$$$$);
+sub nonlinearity_correct_readout($$$$);
+sub phase2($$; @);
+sub pmCameraFromHeader($);
+sub pmCameraValidateHeaders($$);
+sub pmFPAfromHeader($$);
+sub pmFlatField($$$);
+sub pmRecipeDefineSection($$$);
+sub pmSectiontoRectangle($);
+sub pmSubtractBias($$$$$$$$);
+sub psFITSReadHeaderSet($);
+sub trim_readout($$$);
+
+###############################################################################
+#
+# Do the real work
+#
+sub phase2($$; @)
+{
+   my($input, $outroot, $cameraname, $recipefile, $basefile, $verbose) = @_;
+   $basefile ||= "base.config"; $verbose ||= 0;
+   my($nFail) = 0;
+   $outroot++;
+
+   pslib::psTraceSetLevel("ps.phase2", $verbose);
+
+   my($site);
+   if ($basefile) {
+      pslib::psTrace("ps.phase2", 2, "Basefile: $basefile\n");
+      
+      my($base) = pslib::psMetadataAlloc();
+      pslib::psMetadataParseConfig($base, $basefile, 1);
+      
+      my($sitefile) = pslib::psMetadataLookup($base, "SITE")->get_STR();
+      
+      $site = pslib::psMetadataAlloc();
+      pslib::psMetadataParseConfig($site, $sitefile, 1);
+   } else {
+      $site = undef;
+   }
+   
+   # load only the headers from the given file input a list of headers
+   my($headers) = psFITSReadHeaderSet($input);
+   
+   # identify camera (may be specified on command line)
+   my($camera);
+   if (!$cameraname) {
+      $cameraname = pmCameraFromHeader(pslib::psHashLookup($headers, "PHU"));
+      my($camerafile) = pslib::psMetadataLookup($site, ("CAMERA.$cameraname"))->get_STR();
+
+      # load the camera details for the specific camera
+      $camera = pslib::psMetadataAlloc();
+      pslib::psMetadataParseConfig($camera, $camerafile, 0);
+   }
+
+   # recipefile is defined in the camera metadata, if not specified on command line
+   if (!$recipefile) {
+      if (1) {			# 
+	 $recipefile = pslib::psMetadataLookup($camera, "RECIPE.PHASE2")->get_STR();
+      } else {                           # an alternative make this a database lookup & a function of date
+	 $recipefile = DetrendDatabaseLookup ("recipe", $camera, $headers);
+      }
+   }
+   
+   pslib::psTrace("ps.phase2", 2, "Reading recipe from $recipefile\n");
+   my($recipe) = pslib::psMetadataAlloc();
+   pslib::psMetadataParseConfig($recipe, $recipefile, 0);
+
+   # END CONFIG loading code
+
+   # check the validity of the given set of headers based on expectactions for this camera
+   pslib::psTrace("ps.phase2", 3, "Validating headers\n");
+   pmCameraValidateHeaders($headers, $camera);
+
+   # construct a complete, empty (ie, no pixel data) FPA structure for this camera / header set
+   # we need to construct the output container.  is it big enough to fit in memory?  assume so?
+   my($FPA) = pmFPAfromHeader($headers, $camera);
+   
+   # I\'m pretending the extname value for each cell in part of psCell
+   # Do we need to construct a list of the extname values associated with each readout?
+   # Is EXTNAME invarient?  (probably not; does not always exist)
+   #
+   # XXX Faked psCell->{extname} to exist and be NULL in pslib.i XXX
+   
+   my($i, $j, $k);
+   for ($i = 0; $i < $FPA->{chips}->{n}; $i++) {
+      my($chip) = $FPA->{chips}->get_chip($i);
+
+      pslib::psTrace("ps.phase2", 3, "Processing chip $i\n");
+      for ($j = 0; $j < $chip->{cells}->{n}; $j++) {
+	 my($cell) = $chip->{cells}->get_cell($j);
+	 
+	 pslib::psTrace("ps.phase2", 4, "Processing cell $j\n");
+	 my($bias) = load_image("BIAS", $recipe, $cell->{metadata}, $cell->{extname});
+	 my($dark) = load_image("DARK", $recipe, $cell->{metadata}, $cell->{extname});
+	 my($flat) = load_image("FLAT", $recipe, $cell->{metadata}, $cell->{extname});
+	 my($badpix) = load_image("BADPIX", $recipe, $cell->{metadata}, $cell->{extname});
+	 my($fringe) = load_image("FRINGE", $recipe, $cell->{metadata}, $cell->{extname});
+	 pslib::psTrace("ps.phase2", 5, "Read images for cell $j\n");
+	 
+	 for ($k = 0; $k < $cell->{readouts}->{n}; $k++) {
+	    pslib::psTrace("ps.phase2", 6, "Reading readout $k\n");
+	    
+	    my($readout) = $cell->{readouts}->get_readout($k);
+	    $readout->{image} = pslib::psImageReadSection(undef, 0, 0, 0, 0, 0, $cell->{extname}, $k, $input);
+
+	    if (!defined($readout->{metadata})) {
+	       pslib::psTrace("ps.phase2", 7, "Creating metadata\n");
+	       $readout->{metadata} = pslib::psMetadataAlloc();
+
+	       my($nbias) = 2;
+	       pslib::psMetadataAddStr($readout->{metadata}, $pslib::PS_LIST_TAIL,
+				       "BIASSEC", "Define bias section",
+				       sprintf("[0:%d,0:%d]",
+					       ($readout->{image}->{numRows} - 1, $nbias - 1)));
+	       pslib::psMetadataAddStr($readout->{metadata}, $pslib::PS_LIST_TAIL,
+				       "DATASEC", "Define datasection section",
+				       sprintf("[0:%d,%d:%d]",
+					       ($readout->{image}->{numRows} - 1, $nbias,
+						$readout->{image}->{numCols} - 1)));
+	    }
+	    
+	    my($kernel) = make_kernel();
+	    my($mask) = make_mask($readout->{image}->{numRows}, $readout->{image}->{numCols});
+	    
+	    pslib::psTrace("ps.phase2", 7, "Bias subtraction\n");
+	    bias_subtract_readout($readout, $bias, $recipe);
+	    
+	    pslib::psTrace("ps.phase2", 7, "Dark subtraction\n");
+	    dark_subtract_readout($readout, $dark, $recipe);
+	    nonlinearity_correct_readout($readout, $mask, $recipe, $camera);
+	    # 
+	    pslib::psTrace("ps.phase2", 7, "Trimming\n");
+	    trim_readout($readout, $mask, $recipe);
+	    mask_readout($readout, $mask, $badpix, $kernel, $recipe);
+	    # 
+	    # where do we convolve the flat with the kernel?
+	    #
+	    pmFlatField($readout, $mask, $flat);
+	    # 
+	    pslib::psTrace("ps.phase2", 7, "Defringing\n");
+	    defringe_readout($readout, $mask, $fringe, $recipe);
+
+	 }
+      }
+   }
+}
+
+sub bias_subtract_readout($$$; @)
+{
+   #"bias subtract the given readout according to the recipe (bias image may be None)
+   #no mask needed yet"
+   
+   my($readout, $bias, $recipe, $mask) = (undef, undef, undef, undef);
+   ($readout, $bias, $recipe, $mask) = @_;
+   #determine overscan option
+   my($biassec) = pmRecipeDefineSection($recipe, $readout->{metadata}, "BIAS.OVERSCAN");
+   my($overscan) = pslib::psImageSubsection($readout->{image}, $biassec);
+
+   # the type of statistic is defined by the recipe: BIAS.OVERSCAN.STATS
+   my($statname) = pslib::psMetadataLookup ($recipe, "BIAS.OVERSCAN.STATS")->get_STR();
+   my($stats) = DefineStats($statname);
+   
+   # identify the type of fit requested and relevant parameters
+   # the type of fit is defined by the recipe: BIAS.OVERSCAN.STATS
+   my($fitname) = pslib::psMetadataLookup($recipe, "BIAS.OVERSCAN.FIT")->get_STR();  # string defining overscan stats
+   
+   my($fitType, $fitSpec, $nBin, $nPts, $order);
+   if ($fitname eq "NONE") {
+      $fitType = "PM_FIT_NONE";
+      $fitSpec = undef;
+      $nBin = 0;
+   } elsif ($fitname eq "SPLINE") {
+      my($Npix) = "XXX";
+
+      $fitType = "PM_FIT_SPLINE";
+      $nBin = pslib::psMetadataLookup($recipe, "BIAS.OVERSCAN.FIT.NBIN");
+      $nPts = pslib::psMetadataLookup($recipe, "BIAS.OVERSCAN.FIT.NPTS");
+      $order = pslib::psMetadataLookup($recipe, "BIAS.OVERSCAN.FIT.ORDER");
+      $fitSpec = psSpline1DAlloc($nPts, 3, 0, $Npix);                  # Npix is either NAXIS1 or NAXIS2 (Nx or Ny)
+   } elsif ($fitname eq "POLYNOMIAL") {
+      $fitType = "PM_FIT_POLYNOMIAL";
+      $nBin = pslib::psMetadataLookup($recipe, "BIAS.OVERSCAN.FIT.NBIN");
+      $nPts = pslib::psMetadataLookup($recipe, "BIAS.OVERSCAN.FIT.NPTS");
+      $order = pslib::psMetadataLookup($recipe, "BIAS.OVERSCAN.FIT.ORDER");
+      $fitSpec = psPolynomial1DAlloc($nPts, "PS_POLYNOMIAL_CHEB");
+   } else {
+      #raise pslib::PsUtilsError ("unknown fitname: %s" % fitname)
+   }
+   
+   my($overscanlist) = pslib::psListAlloc($overscan);
+   $readout->{image} = pmSubtractBias($readout->{image}, $fitSpec, $overscanlist,
+				      DirectionFromOverscan($biassec), $stats, $nBin, $fitType, $bias);
+
+   # add these to the readout->{metadata}? 
+   # or to the chip / cell metadata?
+}
+
+sub dark_subtract_readout($$$)
+{
+   #"subtract dark image using the appropriate scaling factor for this exposure time
+   #no mask needed yet"
+
+   my($readout, $dark, $recipe) = @_;
+
+   if (!defined($dark)) {
+      return;
+   }
+
+   # need to get a lookup table from the database, apply exptime for readout and dark
+   my($factor) = getScalingFactor($recipe, $readout, $dark);
+   
+   # $out = in - dark * factor
+   my($tmp) = pslib::psImageCopy(undef, $dark, $dark->{type}->{type});
+   pslib::psBinaryOp($tmp, $dark, "*", $factor);
+   pslib::psBinaryOp($readout->{image}, $readout->{image}, "-", $tmp);
+}
+
+sub nonlinearity_correct_readout($$$$)
+{
+   #"this corrects the non-linear response, but probably should use data determined at the cell level
+   #we need to set a mask value for pixels which have were out of range (TBD)"
+
+   my($readout, $mask, $recipe, $camera) = @_;
+   
+   my($method) = pslib::psMetadataLookup($recipe, "NONLINEAR.METHOD")->get_STR();
+   
+   # use a polynomial; get coeffs from recipe
+   if ($method eq "NONE") {
+      ;
+   } elsif ($method eq "POLYNOMIAL") {
+      my($correction);
+      $correction->{n} = pslib::psMetadataLookup($recipe, "NONLINEAR.ORDER")->{data}->{S32};
+      $correction->{coeffs} = pslib::psMetadataLookup($recipe, "NONLINEAR.COEFFS");
+      pslib::pmNonLinearityPolynomial($readout, $correction);
+   } elsif ($method eq "LOOKUP") {
+      my($time) = "XXX";
+      my($lookup_table) = DetrendDatabaseLookup($time, $camera, "etc?");
+      my($inFlux) = pslib::psFITSTableRead($lookup_table, "NONLINEAR.IN_FLUX");
+      my($outFlux) = pslib::psFITSTableRead($lookup_table, "NONLINEAR.OUT_FLUX");
+      pslib::pmNonLinearityLookup($readout, $inFlux, $outFlux);
+   } else {
+      #raise pslib::PsUtilsError ("unknown method: %s" % method);
+   }
+}
+
+sub trim_readout($$$)
+{
+   #"trim the image and mask based on the requested region"
+   my($readout, $mask, $recipe) = @_;
+   my($datasec) = pmRecipeDefineSection($recipe, $readout->{metadata}, "TRIM.DATASEC");
+   
+   my($r0, $r1, $c0, $c1) = ($datasec =~ /\[([0-9]+):([0-9]+),([0-9]+):([0-9]+)\]/);
+   
+   pslib::psImageTrim($readout->{image}, $c0, $r0, $c1, $r1);
+   $mask = pslib::psImageTrim($mask, $c0, $r0, $c1, $r1);
+}
+
+# apply the bad pixel mask to the image mask
+sub mask_readout($$$$$)
+{
+   my($readout, $mask, $badpix, $kernel, $recipe) = @_;
+}
+
+sub load_image($$$$)
+{
+   #"load in the complete corresponding image of proper type (this should be done once per cell)
+   #we are implying that the bias image is guaranteed to have the corresponding EXTNAME"
+
+   my($type, $recipe, $header, $extname) = @_;
+   my($source) = pslib::psMetadataLookup($recipe, $type . ".IMAGE")->get_STR();
+
+   my($name);
+   if (uc(substr($source, 0, 5)) eq "FILE:") {
+      $name = substr($source, 5);
+   } elsif (uc($source) eq "DB:BEST") {
+      $name = DetrendDatabaseLookup("best", $header);   # these APIs need to be seriously fleshed out!!
+   } elsif (uc($source) eq "DB:CLOSE") {
+      $name = DetrendDatabaseLookup("close", $header);   # these APIs need to be seriously fleshed out!!
+   } else {
+      #raise PsUtilsError ("Unknown source for $type: $source");
+   }
+
+   my($image);
+   if (defined($name)) {
+      $image = pslib::psImageReadSection (undef, 0, 0, 0, 0, 0, $extname, 0, $name);
+   } else {
+      $image = undef;
+   }
+
+   return $image;
+}
+
+sub pmRecipeDefineSection($$$)
+{
+   my($recipe, $header, $name) = @_;
+
+   my($source) = pslib::psMetadataLookup($recipe, $name)->get_STR();
+   
+   pslib::psTrace("ps.phase2.recipe", 10, "pmRecipeDefineSection (recipe, header, $name)\n");
+   
+   if (uc($source) eq "NONE") {
+      return psRectangleAlloc(0, 0, 0, 0); # the None section
+   } elsif (uc(substr($source, 0, 7)) eq "HEADER:") {
+      my($keyword) = substr($source,7);
+      my($section) = pslib::psMetadataLookup($header, $keyword)->get_STR();
+      return pmSectiontoRectangle($section);
+   } elsif (uc(substr($source, 0, 7)) eq "RECIPE:") {
+      my($section) = substr($source, 7);
+      return pmSectiontoRectangle($section);
+   }
+   
+   #raise pslib::PsUtilsError ("invalid section: $section");
+}
+
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+sub DirectionFromOverscan($)
+{
+   #"The direction is implied by noting the long dimension of the biassec"
+   my($secstr) = @_;
+
+   return 1;
+}
+
+sub pmSectiontoRectangle($)
+{
+   #"Parse a specification of the form [col0:col1,row0:row1]
+   #For now, simply return it"
+   my($secstr) = @_;
+
+   return $secstr;
+}
+
+sub psFITSReadHeaderSet($)
+{
+   my($input) = @_;
+   
+   pslib::psTrace("ps.phase2", 2, "Reading headers: $input\n");
+
+   my($meta) = pslib::psMetadataAlloc();
+   pslib::psMetadataAddStr($meta, $pslib::PS_LIST_TAIL, "CAMERA", "Camera ID", "0");
+
+   my($headers) = pslib::psHashAlloc(64);
+   pslib::psHashAdd($headers, "PHU", $meta);
+   
+   return $headers;
+}
+
+sub pmCameraFromHeader($)
+{
+   #"Return a camera given the metadata read from a fits header"
+   my($meta) = @_;
+
+   return "0";
+}
+
+sub pmCameraValidateHeaders($$)
+{
+   my($headers, $camera) = @_;
+
+   if (0) {
+      #raise pslib::PsUtilsError ("Failed to validate $headers:$camera");
+   }
+}
+
+sub pmFPAfromHeader($$)
+{
+   my($headers, $camera) = @_;
+   
+   my($ra) = 1; my($dec) = 2; my($hourAngle) = 3; my($zenith) = 4; my($azimuth) = 5;
+   my($rotAngle) = 8;
+   my($temperature) = 9; my($pressure) = 10; my($humidity) = 11;
+   my($exposureTime) = 12;
+
+   my($nchips) = pslib::psMetadataLookup($camera, "NCHIPS")->{data}->{S32};
+   my($ncells) = pslib::psMetadataLookup($camera, "NCELLS")->{data}->{S32};
+   
+   my($obs) = pslib::psObservatoryAlloc("Haleakala", 21, 150, 3000, 0.1);
+   my($wavelength) = 5500e-10;
+   my($localTime) = pslib::psTimeGetTime($pslib::PS_TIME_TAI);
+
+   my($exposure) = pslib::psExposureAlloc($ra, $dec, $hourAngle, $zenith, $azimuth, $localTime,
+					  $rotAngle, $temperature, $pressure, $humidity, $exposureTime,
+					  $wavelength, $obs);
+   my($FPA) = pslib::psFPAAlloc($nchips, $exposure);
+   my($chips) = pslibc::psFPA_chips_get($FPA);
+   
+   my($i, $j, $k);
+   for ($i = 0; $i <$nchips; $i++) {
+      my($chip) = pslib::psChipAlloc($ncells, $FPA);
+      pslib::psArraySet($chips, $i, $chip);
+      my($cells) = pslibc::psChip_cells_get($chip);
+      for ($j = 0; $j <$ncells; $j++) {
+	 my($nReadouts) = pslib::psMetadataLookup($camera, "NREADOUTS.$i.$j")->{data}->{S32};
+
+	 my($cell) = pslib::psCellAlloc($nReadouts, $chip);
+	 
+	 pslib::psArraySet($cells, $j, $cell);
+	 
+	 my($readouts) = pslibc::psCell_readouts_get($cell);
+	 for ($k = 0; $k < $nReadouts; $k++) {
+	     pslib::psArraySet($readouts, $k, pslib::psReadoutAlloc());
+	 }
+      }
+   }
+
+   return $FPA;
+}
+
+sub make_kernel()
+{
+   my($nrow) = 5; my($ncol) = 5;
+   return pslib::psImageAlloc($ncol, $nrow, $pslib::PS_TYPE_F64);
+}
+
+sub make_mask($$)
+{
+   my($nrow, $ncol) = @_;
+
+   return pslib::psImageAlloc($ncol, $nrow, $pslib::PS_TYPE_F64);
+}
+
+sub DefineStats($)
+{
+   my($str) = @_;
+   return $str;
+}
+
+sub pmSubtractBias($$$$$$$$)
+{
+   #"Subtract the bias. Don't do a very good job..."
+   my($rawImage, $fitSpec, $overscanlist, $direction, $stats, $nBin, $type, $bias) = @_;
+   return pslib::psImageAlloc($rawImage->{numCols}, $rawImage->{numRows}, $pslib::PS_TYPE_F64);
+}
+
+sub getScalingFactor($$$)
+{
+   my($recipe, $readout, $dark) = @_;
+   my($source) = pslib::psMetadataLookup($recipe, "DARK.FACTOR")->get_STR();
+
+   if (uc(substr($source, 0, length("VALUE:"))) eq "VALUE:") {
+      return pslib::psScalarF32Alloc(substr($source, length("VALUE:")), $pslib::PS_TYPE_F64);
+   }
+
+   #raise pslib::PsUtilsError ("Unknown recipe for dark factor: $source");
+}
+
+sub pmFlatField($$$)
+{
+   my($readout, $mask, $flat) = @_;
+}
+
+sub defringe_readout($$$$)
+{
+   my($readout, $mask, $fringe, $recipe) = @_;
+}
+
+#(input, outroot, cameraname = None, recipefile = None, basefile = "base.config", verbose=0)
+if (1) {
+   phase2('data/data.fits', undef, undef, undef, "data/base.config", 4);
+   
+   if (pslib::psMemCheckLeaksToStderr(0,undef,0)) {
+      die "You leaked memory\n";
+   }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/swig/pslib.i
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/swig/pslib.i	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/swig/pslib.i	(revision 22322)
@@ -0,0 +1,69 @@
+%module pslib
+
+%{
+#define PS_ALLOW_MALLOC
+#define SWIG
+#include "pslib.h"
+
+/* SWIG uses malloc/free - make it use the pslib memory functions instead. */
+/*
+#define malloc(S)    psAlloc(S)
+#define realloc(P,S) psRealloc(P,S)
+#define free(P)      psFree(P)
+*/
+
+%}
+
+/* XXX: this is temporary -- not portable, but should work with any current OS supported */
+typedef unsigned char  uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int   uint32_t;
+typedef unsigned long  uint64_t;
+typedef char           int8_t;
+typedef short          int16_t;
+typedef int            int32_t;
+typedef long long      int64_t;
+
+/* grab the typedefs used throughout psLib, e.g. psU8, psU16,... */
+%include "cpointer.i"
+%include "typemaps.i"
+
+/**
+ * Typemap to map FILE* input to the script's native file descriptor wrapper.
+ * Used for such functions as psErrorStackPrint and psTraceSetDestination.
+ */
+%typemap(in) FILE * {
+#if defined(SWIGPERL)
+   if (!SvOK($input)) {
+      $1 = NULL;
+   } else {
+      $1 = PerlIO_findFILE(IoIFP(sv_2io($input)));
+   }
+#elif defined(SWIGPYTHON)
+    if ($input == Py_None) {
+	$1 = NULL;
+    } else if (!PyFile_Check($input)) {
+	PyErr_SetString(PyExc_TypeError, "Need a file!");
+	goto fail;
+    } else {
+	$1 = PyFile_AsFile($input);
+    }
+#endif
+}
+
+%{
+typedef struct { float re, im; } swig_psC32;  ///< 32-bit complex floating point
+typedef struct { double re, im; } swig_psC64; ///< 64-bit complex floating point
+%}
+
+/* the actual including of headers are found in each of the directories. */
+%include "astro.i"
+%include "db.i"
+%include "fft.i"
+%include "fits.i"
+%include "imageops.i"
+%include "math.i"
+%include "mathtypes.i"
+%include "sys.i"
+%include "types.i"
+%include "xml.i"
Index: /tags/sj_tags/sj_root_20080929/psLib/test-coverage
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test-coverage	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test-coverage	(revision 22322)
@@ -0,0 +1,93 @@
+set PWD='pwd'
+./autogen.sh CFLAGS="-lgcov -fprofile-arcs -ftest-coverage" --prefix=$PWD
+make clean
+make
+mkdir Test_Coverage
+cd test
+./FullUnitTest
+cd ../src/astro
+gcov -f --object-file ./.libs/libpslibastro_la-psCoord.o psCoord.c > coverage-astro.coord
+gcov -f --object-file ./.libs/libpslibastro_la-psEarthOrientation.o psEarthOrientation.c > coverage-astro.eoc
+gcov -f --object-file ./.libs/libpslibastro_la-psSphereOps.o psSphereOps.c > coverage-astro.sphereOps
+gcov -f --object-file ./.libs/libpslibastro_la-psTime.o psTime.c > coverage-astro.time
+mv coverage-*.* ../../Test_Coverage/
+cd ../db
+gcov -f --object-file ./.libs/libpslibdb_la-psDB.o psDB.c > coverage-db.db
+mv coverage-*.* ../../Test_Coverage/
+cd ../fft
+gcov -f --object-file ./.libs/libpslibfft_la-psImageFFT.o psImageFFT.c > coverage-fft.imageFFT
+gcov -f --object-file ./.libs/libpslibfft_la-psVectorFFT.o psVectorFFT.c > coverage-fft.vectorFFT
+mv coverage-*.* ../../Test_Coverage/
+cd ../fits
+gcov -f --object-file ./.libs/libpslibfits_la-psFits.o psFits.c > coverage-fits.fits
+gcov -f --object-file ./.libs/libpslibfits_la-psFitsHeader.o psFitsHeader.c > coverage-fits.fitsHeader
+gcov -f --object-file ./.libs/libpslibfits_la-psFitsImage.o psFitsImage.c > coverage-fits.fitsImage
+gcov -f --object-file ./.libs/libpslibfits_la-psFitsTable.o psFitsTable.c > coverage-fits.fitsTable
+mv coverage-*.* ../../Test_Coverage/
+cd ../imageops
+gcov -f --object-file ./.libs/libpslibimageops_la-psImageBackground.o psImageBackground.c > coverage-imageops.imageBackground
+gcov -f --object-file ./.libs/libpslibimageops_la-psImageConvolve.o psImageConvolve.c > coverage-imageops.imageConvolve
+gcov -f --object-file ./.libs/libpslibimageops_la-psImageGeomManip.o psImageGeomManip.c > coverage-imageops.imageGeomManip
+gcov -f --object-file ./.libs/libpslibimageops_la-psImageMaskOps.o psImageMaskOps.c > coverage-imageops.imageMaskOps
+gcov -f --object-file ./.libs/libpslibimageops_la-psImagePixelExtract.o psImagePixelExtract.c > coverage-imageops.imagePixelExtract
+gcov -f --object-file ./.libs/libpslibimageops_la-psImagePixelManip.o psImagePixelManip.c > coverage-imageops.imagePixelManip
+gcov -f --object-file ./.libs/libpslibimageops_la-psImageStats.o psImageStats.c > coverage-imageops.imageStats
+gcov -f --object-file ./.libs/libpslibimageops_la-psImageStructManip.o psImageStructManip.c > coverage-imageops.imageStructManip
+gcov -f --object-file ./.libs/libpslibimageops_la-psImageUnbin.o psImageUnbin.c > coverage-imageops.imageUnbin
+mv coverage-*.* ../../Test_Coverage/
+cd ../jpeg
+gcov -f --object-file ./.libs/libpslibjpeg_la-psImageJpeg.o psImageJpeg.c > coverage-jpeg.imageJpeg
+mv coverage-*.* ../../Test_Coverage/
+cd ../math
+gcov -f --object-file ./.libs/libpslibmath_la-psBinaryOp.o psBinaryOp.c > coverage-math.binaryOp
+gcov -f --object-file ./.libs/libpslibmath_la-psCompare.o psCompare.c > coverage-math.compare
+gcov -f --object-file ./.libs/libpslibmath_la-psEllipse.o psEllipse.c > coverage-math.ellipse
+gcov -f --object-file ./.libs/libpslibmath_la-psMathUtils.o psMathUtils.c > coverage-math.mathUtils
+gcov -f --object-file ./.libs/libpslibmath_la-psMatrix.o psMatrix.c > coverage-math.matrix
+gcov -f --object-file ./.libs/libpslibmath_la-psMinimizeLMM.o psMinimizeLMM.c > coverage-math.minimizeLMM
+gcov -f --object-file ./.libs/libpslibmath_la-psMinimizePolyFit.o psMinimizePolyFit.c > coverage-math.minimizePolyFit
+gcov -f --object-file ./.libs/libpslibmath_la-psMinimizePowell.o psMinimizePowell.c > coverage-math.minimizePowell
+gcov -f --object-file ./.libs/libpslibmath_la-psPolynomial.o psPolynomial.c > coverage-math.polynomial
+gcov -f --object-file ./.libs/libpslibmath_la-psPolynomialUtils.o psPolynomialUtils.c > coverage-math.polynomialUtils
+gcov -f --object-file ./.libs/libpslibmath_la-psRandom.o psRandom.c > coverage-math.random
+gcov -f --object-file ./.libs/libpslibmath_la-psRegion.o psRegion.c > coverage-math.region
+gcov -f --object-file ./.libs/libpslibmath_la-psRegionForImage.o psRegionForImage.c > coverage-math.regionForImage
+gcov -f --object-file ./.libs/libpslibmath_la-psSparse.o psSparse.c > coverage-math.sparse
+gcov -f --object-file ./.libs/libpslibmath_la-psSpline.o psSpline.c > coverage-math.spline
+gcov -f --object-file ./.libs/libpslibmath_la-psStats.o psStats.c > coverage-math.stats
+gcov -f --object-file ./.libs/libpslibmath_la-psUnaryOp.o psUnaryOp.c > coverage-math.unaryOp
+gcov -f --object-file ./.libs/libpslibmath_la-psVectorSmooth.o psVectorSmooth.c > coverage-math.vectorSmooth
+mv coverage-*.* ../../Test_Coverage/
+cd ../mathtypes
+gcov -f --object-file ./.libs/libpslibmathtypes_la-psImage.o psImage.c > coverage-mathtypes.image
+gcov -f --object-file ./.libs/libpslibmathtypes_la-psScalar.o psScalar.c > coverage-mathtypes.scalar
+gcov -f --object-file ./.libs/libpslibmathtypes_la-psVector.o psVector.c > coverage-mathtypes.vector
+mv coverage-*.* ../../Test_Coverage/
+cd ../sys
+gcov -f --object-file ./.libs/libpslibsys_la-psAbort.o psAbort.c > coverage-sys.abort
+gcov -f --object-file ./.libs/libpslibsys_la-psConfigure.o psConfigure.c > coverage-sys.configure
+gcov -f --object-file ./.libs/libpslibsys_la-psError.o psError.c > coverage-sys.error
+gcov -f --object-file ./.libs/libpslibsys_la-psErrorCodes.o psErrorCodes.c > coverage-sys.errorCodes
+gcov -f --object-file ./.libs/libpslibsys_la-psLine.o psLine.c > coverage-sys.line
+gcov -f --object-file ./.libs/libpslibsys_la-psLogMsg.o psLogMsg.c > coverage-sys.logMsg
+gcov -f --object-file ./.libs/libpslibsys_la-psMemory.o psMemory.c > coverage-sys.memory
+gcov -f --object-file ./.libs/libpslibsys_la-psString.o psString.c > coverage-sys.string
+gcov -f --object-file ./.libs/libpslibsys_la-psTrace.o psTrace.c > coverage-sys.trace
+mv coverage-*.* ../../Test_Coverage/
+cd ../types
+gcov -f --object-file ./.libs/libpslibtypes_la-psArguments.o psArguments.c > coverage-types.arguments
+gcov -f --object-file ./.libs/libpslibtypes_la-psArray.o psArray.c > coverage-types.array
+gcov -f --object-file ./.libs/libpslibtypes_la-psBitSet.o psBitSet.c > coverage-types.bitSet
+gcov -f --object-file ./.libs/libpslibtypes_la-psHash.o psHash.c > coverage-types.hash
+gcov -f --object-file ./.libs/libpslibtypes_la-psList.o psList.c > coverage-types.list
+gcov -f --object-file ./.libs/libpslibtypes_la-psLookupTable.o psLookupTable.c > coverage-types.lookupTable
+gcov -f --object-file ./.libs/libpslibtypes_la-psMetadata.o psMetadata.c > coverage-types.metadata
+gcov -f --object-file ./.libs/libpslibtypes_la-psMetadataConfig.o psMetadataConfig.c > coverage-types.metadataConfig
+gcov -f --object-file ./.libs/libpslibtypes_la-psMetadataItemCompare.o psMetadataItemCompare.c > coverage-types.metadataItemCompare
+gcov -f --object-file ./.libs/libpslibtypes_la-psMetadataItemParse.o psMetadataItemParse.c > coverage-types.metadataItemParse
+gcov -f --object-file ./.libs/libpslibtypes_la-psPixels.o psPixels.c > coverage-types.pixels
+mv coverage-*.* ../../Test_Coverage/
+cd ../xml
+gcov -f --object-file ./.libs/libpslibxml_la-psXML.o psXML.c > coverage-xml.xml
+mv coverage-*.* ../../Test_Coverage/
+cd ../../
Index: /tags/sj_tags/sj_root_20080929/psLib/test/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/.cvsignore	(revision 22322)
@@ -0,0 +1,7 @@
+temp
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/Makefile.am	(revision 22322)
@@ -0,0 +1,12 @@
+SUBDIRS = tap pstap $(SRCDIRS)
+
+TESTS = test.pl
+
+EXTRA_DIST = test.pl
+
+# XXX the *.out and *.config files should not be written into this directory in
+# the first place
+CLEANFILES = core core.* *~ *.out *.config *.fits *.md tmpImages/* tst_psTrace02_OUT
+
+test: check
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/TABLE-SDRS-CH06-DataManip
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/TABLE-SDRS-CH06-DataManip	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/TABLE-SDRS-CH06-DataManip	(revision 22322)
@@ -0,0 +1,441 @@
+-------------------------------------------------------------------------------
+			Unallowed	Types	Sizes	Level	Algorithm
+			Params				of
+							Testing
+-------------------------------------------------------------------------------
+psVectorSort()
+			10		10	10	10
+    in mathtypes/tap_psVectorSort.c
+    Many more types are tested successfully than required in the SDRS.
+psVectorSortIndex()
+			10		10	10	10
+    in mathtypes/tap_psVectorSortIndex.c
+    Many more typres are tested successfully than required in the SDRS.
+psVectorStats()		5		10	5	5
+    files: math/tap_psStats00.c - math/tap_psStats09.c
+    Must review these files more.
+    More extensie robust stats?
+    They use preset answers
+    Types should be supported (from SDRS):
+	psS8, psU16, psF32, psF64
+psStatsAlloc()
+			na		na	na	10
+psStatsOptionFromString()
+			10		na	na	10	na
+psStatsOptionToString()
+			10		na	na	10	na
+psStatsFromString()
+			NONE		NONE	NONE	NONE
+psStatsToString()
+			NONE		NONE	NONE	NONE
+psStatsSingleOption()
+			NONE		NONE	NONE	NONE
+psStatsGetValue()
+			NONE		NONE	NONE	NONE
+psVectorCountPixelMask()10		10	10	10	na
+    mathtypes/tst_psVector.c
+psHistogramAlloc()
+			10		na	10	10
+psHistogramAllocGeneric()
+			10		na	10	10
+psVectorHistogram()
+			8		na	10	8
+psPolynomial1DAlloc()
+			10		10	10	10	na
+    math/tap_psPolynomial.c
+psPolynomial2DAlloc()
+			10		10	10	10	na
+    math/tap_psPolynomial.c
+psPolynomial3DAlloc()
+			10		10	10	10	na
+    math/tap_psPolynomial.c
+psPolynomial4DAlloc()
+			10		10	10	10	na
+    math/tap_psPolynomial.c
+psPolynomial1DEval()
+			10		10	1	8
+    We only allocate polynomials with 4 terms
+    We should ensure that asking for negative number of terms fails nicely.
+    The test uses precomputed results.
+psPolynomial2DEval()
+			10		10	1	5
+    We only allocate polynomials with 4 terms
+	Must vary x,y number of terms.
+    The test uses precomputed results.
+psPolynomial3DEval()
+			10		10	1	5
+    We only allocate polynomials with 4 terms
+	Must vary x,y,z number of terms.
+    The test uses precomputed results.
+psPolynomial4DEval()
+			10		10	1	5
+    We only allocate polynomials with 4 terms
+	Must vary w,x,y,z number of terms.
+    The test uses precomputed results.
+psPolynomial1DEvalVector()
+			10		10	5	9
+psPolynomial2DEvalVector()
+			10		10	5	9
+psPolynomial3DEvalVector()
+			10		10	5	9
+psPolynomial4DEvalVector()
+			10		10	5	9
+psPolynomial1DfromMetadata()10		10	10	10	na
+    types/tap_psMetadata_polynomials.c
+psPolynomial2DfromMetadata()10		10	10	10	na
+    types/tap_psMetadata_polynomials.c
+psPolynomial3DfromMetadata()10		10	10	10	na
+    types/tap_psMetadata_polynomials.c
+psPolynomial4DfromMetadata()10		10	10	10	na
+    types/tap_psMetadata_polynomials.c
+psPolynomial1DtoMetadata()10		10	10	10	na
+    types/tap_psMetadata_polynomials.c
+psPolynomial2DtoMetadata()10		10	10	10	na
+    types/tap_psMetadata_polynomials.c
+psPolynomial3DtoMetadata()10		10	10	10	na
+    types/tap_psMetadata_polynomials.c
+psPolynomial4DtoMetadata()10		10	10	10	na
+    types/tap_psMetadata_polynomials.c
+psSpline1DAlloc()
+			na		na	na	10
+psSpline1DEval()
+			8		na	5	8
+    There should be a PS_ASSERT_PTR_NON_NULL(spline->knots) before we
+    assert type:
+        PS_ASSERT_PTR_NON_NULL(spline, NULL);
+        PS_ASSERT_VECTOR_TYPE(spline->knots, PS_TYPE_F32, NULL);
+psSpline1DEvalVector()
+			8		na	5	8
+psGaussian()		na		na	na	5	na
+    math/tap_psFunc01.c
+    This test merely compares psGaussian results agaisnt preset vectors.
+    Add tests to generate a vector of data, then run psVectorStats() on it.
+psMinimizationAlloc()
+			10		10	10	10
+    Completely tested.
+psMinConstrainAlloc()
+			10		10	10	10
+    Completely tested, however the SDRS completely disagrees with the code.
+    What happened to the following members?
+        psVector *paramMax;
+        psVector *paramMin;
+        psVector *paramDelta;
+    And a new member:
+	psMinimizeLMLimitFunc checkLimits;
+psMinimizeLMChi2()
+			5		3	1	1
+    XXX: Add tests for
+        covar arg set to non-NULL
+        constraint set to non-NULL
+	Set x->vectors to NULL, or use wrong types
+	yWt (errors) vector set to incorrect size, type.
+psMinimizeGaussNewtonDelta()0		0	0	0	0
+    NONE
+psMinimizePowell()	0		0	0	0	na
+    The bad param tests are all commented out due to seg faults
+    Basically, everthing is commented out due to seg faults
+psMinimizeChi2Powell()	0		0	0	0	0
+    NONE
+psVectorFitPolynomial1D()
+			10		10	7	9
+    Maybe vary the poly orders.
+    4D has some tests that are failing, but should not be.
+psVectorFitPolynomial2D()
+			10		10	7	9
+    DITTO
+psVectorFitPolynomial3D()
+			10		10	7	9
+    DITTO
+psVectorFitPolynomial4D()
+			10		10	7	9
+    DITTO
+psVectorClipFitPolynomial1D()
+			10		10	7	9
+    DITTO
+psVectorClipFitPolynomial2D()
+			10		10	7	9
+    DITTO
+psVectorClipFitPolynomial3D()
+			10		10	7	9
+    DITTO
+psVectorClipFitPolynomial4D()
+			10		10	7	9
+    DITTO
+psVectorFitSpline1D()	0		2	10	5	5
+psImageSubset()		10		2	1	10	na
+    in imageops/tap_psImageStructManip.c
+    A single 128x256 image of type U32 is tested.
+psImageCopy()		10		10	8	10	na
+    in imageops/tap_psImageStructManip.c
+    A single input 128x256 image of type F32 is tested, however it's
+	converted to all other allowed types.
+psImageTrim()		10		2	4	10	na
+    in imageops/tap_psImageStructManip.c
+    A single 200x300 image of type F32 is tested.
+psImageFlip()		0		0	0	0	0
+    NONE
+psImageRow()		10		10	2	10	na
+    in imageops/tap_psImageStructManip.c
+psImageCol()		10		10	2	10	na
+    in imageops/tap_psImagePixelExtract.c
+    A single 3x3 image is used
+psImageSlice()		10		10	10	10	na
+    in imageops/tap_psImagePixelExtract.c
+    Spend more time determining if all input params are tested, and all combos
+	as well.
+    SDRS seems to be out of date.
+psImageCut()		5		2	2	10	na
+    in imageops/tap_psImagePixelExtract.c
+    SDRS seems to be incomplete and lots of unallowed input param combos are
+	probably not tested.
+    A single 200x300 input image of type F32 is tested.
+psImageRadialCut()	10		2	2	10	na
+    in imageops/tap_psImagePixelExtract.c
+    A single 200x300 input image of type F32 is tested.
+psImageRebin()		10		10	2	10	na
+    In imageops/tap_psImageGeomManip.c
+    Only 16x16 images are rebinned into 4x4 images.  Try NxM.
+psImageResample()	10		2	2	10	na
+    In imageops/tap_psImageGeomManip.c
+    A single 80x60 input image of type F32 is used.
+psImageRotate()		8		5	2	10	na
+    In imageops/tap_psImageGeomManip.c
+    A single 64x64 input image is used
+    Only types F32 and S16 are tested
+    Image values are compared against a stored FITS file.  Where did the file
+	come from?
+    There is a potential autoconf directory problem with reading the FITS file.
+psImageShift()		10		5	5	10	na
+    In imageops/tap_psImageGeomManip.c
+    Only 64x128 images are used.
+    Only types F32 and S16 are tested
+psImageRoll()		10		10	2	10	na
+    In imageops/tap_psImageGeomManip.c
+    Only NxN square images are tested.
+psImageTransform()	0		2	2	5	na
+    In imageops/tap_psImageGeomManip.c
+    A single 16x32 input image of type F32 is used
+    No attempt at unallowed input params, types, or sizes.
+psImageUnbin()		0		0	0	0	0
+    NONE
+psImageStats()		10		2	2	10	na
+    In imageops/tap_psImageStats.c
+    A single 10x10 input image of type F32 is used
+    subRegion tests are skipped because of a seg-fault
+psImageHistogram()	10		2	4	10	na
+    In imageops/tap_psImageStats.c
+    A single 16x15 input image of type F32 is used
+    numBins should be varied
+psImageCountPixelMask()	10		10	2	10	na
+    In imageops/tap_psImageStats.c
+    A single 10x10 input mask images used
+psImageFitPolynomial()	10		2	4	10	5
+    In imageops/tap_psImageStats.c
+    Only 10x10 images are used.
+    Only F32 and F64 are tested.
+psImageEvalPolynomial()	10		2	4	10	5
+    In imageops/tap_psImageStats.c
+    Only 10x10 images are used.
+    Only F32 and F64 are tested.
+psImagePixelInterpolate()
+			2		2	2	8	8
+    There's old code in imageops/tap_psImageInterpolate.c
+    Extensive code in mathtypes/tap_psImageInterpolate.c
+    A single 32x32 input image of type F32 is used
+    Merge param tests from imageops/tap_psImageInterpolate.c
+psImageBackground()	0		0	0	0	0
+    NONE
+psImageClip()		8		10	2	10	na
+    imageops/tap_psImagePixelManip.c
+    Only 256-128 images are tested
+    All types are tested
+    Should block off some individual tests
+psImageClipComplexRegion()0		0	0	0	0
+    NONE in tap files, there is an old imageops/tst_psImagePixelManip.c
+    Is this still in the new SDRS?
+psImageClipNaN()	8		5	2	10	na
+    imageops/tap_psImagePixelManip.c
+    Only 256-128 images are tested
+    Only F32 and F64 are tested
+    Should block off some individual tests
+psImageOverlaySection()	10		10	2	10	na
+    imageops/tap_psImagePixelManip.c
+    Only 128-by-256 images are tested
+    All types are tested
+    Should block off some individual tests
+psImageBicubeFit()	0		0	0	0	0
+    NONE
+psImageBicubeMin()	0		0	0	0	0
+    NONE
+psImageJpegColormapAlloc()
+			0		0	0	0	0
+    NONE
+psImageJpegColormapSet()0		0	0	0	0
+    NONE
+psImageJpeg()		0		0	0	0	0
+    NONE
+psImageGrowMask()	10		10	2	10	na
+    In imageops/tap_psImageMaskOps.c
+    psImageGrowMask() is currently growing a circular mask when the SDRS specs
+	a square mask.
+    All mask images are NxN
+psPixelsToMask()	10		10	10	2	na
+    In types/tap_psPixels_all.c
+    We never test pixels in the output image mask
+psPixelsFromMask()	10		10	2	2	na
+    In types/tap_psPixels_all.c
+    We never test pixels in the output psPixels
+    Should better separate individual tests
+psPixelsConcatenate()	?		?	?	?	na
+    This is not in the SDRS
+    We never test pixels in the output psPixels
+psImageMaskRegion()	5		10	10	10	na
+    In imageops/tap_psImageMaskOps.c
+    Generate tests with NULL input, bad input type, bad psRegion
+psImageKeepRegion()
+    In imageops/tap_psImageMaskOps.c
+    Generate tests with NULL input, bad input type, bad psRegion
+psImageMaskCircle()	5		10	2	5	na
+    In imageops/tap_psImageMaskOps.c
+    Generate tests with NULL input, bad input type, bad radius, etc
+    Seg faults are skipped for Nx1 and 1xN images
+psImageKeepCircle()	5		10	2	5	na
+    In imageops/tap_psImageMaskOps.c
+    Generate tests with NULL input, bad input type, bad radius, etc
+    Seg faults are skipped for Nx1 and 1xN images
+psBinaryOp()		?		?	?	?	?
+    Extensive tests in tap_psMatrixVectorArithmetic:01-04.c
+    Must review further.
+    Some are seg-faulting and failing to compile, so I removed from Makefile.am
+psUnaryOp()		?		?	?	?	?
+    Extensive tests in tap_psMatrixVectorArithmetic:01-04.c
+    Must review further.
+    Some are seg-faulting and failing to compile, so I removed from Makefile.am
+psMatrixLUD()		0		5	2	5	3
+    math/tap_psMatrix03.c
+    The NULL image argument seg-faults
+    Only 3x3 matrices, F32 and F64, are used.
+    Fairly low-level of testing
+psMatrixLUSolve()	0		5	2	5	3
+    math/tap_psMatrix03.c
+    The NULL image argument seg-faults
+    The NULL vector argument seg-faults
+    Only 3x3 matrices, F32 and F64, are used.
+    Fairly low-level of testing
+psMatrixGJSolve()	0		0	0	0	0
+    NONE
+psMatrixInvert()	0		5	2	5	3
+    math/tap_psMatrix04.c
+    Only 3x3 matrices, F32 and F64, are used.
+    The NULL image argument seg-faults
+    The NULL vector argument seg-faults
+    Fairly low-level of testing
+psMatrixDeterminant()	10		10	2	5	na
+    math/tap_psMatrix04.c
+    Only 3x3 matrices, F32 and F64, are used.
+    Fairly low-level of testing
+psMatrixMultiply()	0		5	2	5	na
+    math/tap_psMatrix05.c
+    Only 3x3 matrices, F32 and F64, are used.
+    No tests when args have different types.
+    No tests for bad input params.
+psMatrixTranspose()	10		10	4	8	na
+    math/tap_psMatrix01.c
+    Probably should N-by-M sizes
+    in file tap_psMatrix01.c
+    in file tap_psMatrix02.c
+	A few other tests with bad input parameters
+psMatrixEigenvectors()	0		5	2	5	na
+    math/tap_psMatrix06.c
+    Only 4x4 matrices, F32 and F64, are used.
+    No tests when args have different types.
+    No tests for bad input params.
+psMatrixToVector()	0		5	2	5	na
+    math/tap_psMatrix07.c
+    Only 1x3 and 3x1 matrices are used.
+    Only F32 and F64 types are used.
+    Lots of bad param tests are commented out due to seg faults
+psVectorToMatrix()	0		5	2	5	na
+    math/tap_psMatrix07.c
+    no bad param tests
+    Only 3x1 matrices are used.
+    Only F32 and F64 types are used.
+psSparseAlloc()
+			na		na	10	10	na
+psSparseMatrixElement()	0		na	na	na	na
+    math/tap_psSparse.c
+    This is not tested individually, however it is tested implictly.
+    Must write individual tests for it.
+psSparseVectorElement()	1		1	1	1	na
+    math/tap_psSparse.c
+    This is not tested individually, however it is tested implictly.
+    Must write individual tests for it.
+psSparseVectorElement()	1		1	1	1	na
+    math/tap_psSparse.c
+    This is not in the SDRS
+    This is not tested individually, however it is tested implictly.
+    Must write individual tests for it.
+psSparseMatrixTimesVector()1		1	1	1	na
+    math/tap_psSparse.c
+    This is not tested individually, however it is tested implictly.
+    Must write individual tests for it.
+psSparseResort()	1		1	1	1	na
+    math/tap_psSparse.c
+    This is not tested individually, however it is tested implictly.
+    Must write individual tests for it.
+psSparseSolve()		0		2	2	4	na
+    math/tap_psSparse.c
+    Must add param, types, and size tests.
+psVectorForwardFFT()	10		5	10	10	na
+    fft/tap_psVectorFFT.c
+    Add F64 tests
+psVectorBackwardFFT()	0		2	2	2	na
+    fft/tap_psVectorFFT.c
+    This is only tested implictly, once, along with the forward FFT test.
+psVectorPowerSpectrum()	0		0	0	0	0
+    fft/tst_psVectorFFT.c
+        There do not appear to be any tap tests.  Must look thru old files.
+psVectorComplexMultiply()0		0	0	0	0
+    NONE
+psImageForwardFFT()	10		5	10	10	na
+    fft/tap_psImageFFT.c
+    Need both F32 and F64 tests?
+psImageBackwardFFT()	0		2	2	2	na
+    fft/tap_psImageFFT.c
+    Need both F32 and F64 tests?
+    This is only tested implictly, once, along with the forward FFT test.
+psImagePowerSpectrum()	0		0	0	0	0
+    fft/tst_psImageFFT.c
+        There do not appear to be any tap tests.  Must look thru old files.
+psImageComplexMultiply()0		0	0	0	0
+    NONE
+psImageSmooth()		0		10	10	10	na
+    No bad param tests.
+    Don't you had bad param test lying around somewhere?
+psKernelAlloc()		8		na	na	8	na
+    imageops/tap_psImageConvolve.c
+    Consolidate with tap_psImageConvolve2.c
+psKernelGenerate()	8		5	5	4	na
+    imageops/tap_psImageConvolve.c
+    Some tests are skipped because of seg faults
+psImageConvolveDirect()	0		2	2	2	na
+    imageops/tap_psImageConvolve.c
+    These are completely commented away in tap_psImageConvolve.c and only
+	lightly tested in tap_psImageConvolve2.c
+psImageConvolveFFT()	0		0	0	2
+    imageops/tap_psImageConvolve2.c
+    Only lightly tested in tap_psImageConvolve2.c
+psRandomAlloc()		10		na	na	10	na
+    math/tap_psRandom.c
+psRandomReset()		5		na	na	8	na
+    math/tap_psRandom.c
+    The bad param test is commented out
+psRandomUniform()	8		na	na	10	na
+psRandomGaussian()	8		na	na	10	na
+psRandomPoisson()	8		na	na	10	na
+psEllipseMomentsToAxes()	0	0	0	0	0
+    NONE
+psEllipseAxesToShape()	0	0	0	0	0
+    NONE
+psEllipseShapeToAxes()	0	0	0	0	0
+    NONE
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/.cvsignore	(revision 22322)
@@ -0,0 +1,33 @@
+.deps
+.libs
+Makefile
+Makefile.in
+pslib.config
+temp
+test.psTime.config1
+test.psTime.config2
+test.psTime.config3
+test.psTime.config4
+test.ser7.dat
+tst_psCoord
+tst_psCoord01
+tst_psSphereOps
+tst_psTime_01
+tst_psTime_02
+tst_psTime_03
+tst_psTime_04
+tst_psEarthOrientation
+tst_psCoord02
+*.bb
+*.bbg
+*.da
+gmon.out
+tap_psEarthOrientation_corrections
+tap_psEarthOrientation_motion
+tap_psSphereOps_all
+tap_psCoord
+tap_psCoord02
+tap_psTime_01
+tap_psTime_02
+tap_psTime_03
+tap_psTime_04
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/Makefile.am	(revision 22322)
@@ -0,0 +1,70 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSLIB_CFLAGS)
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpslib.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSLIB_LIBS)
+
+TEST_PROGS = \
+	tap_psSphereOps_all \
+	tap_psEarthOrientation_motion \
+	tap_psEarthOrientation_corrections \
+	tap_psCoord \
+	tap_psCoord02 \
+	tap_psTime_01 \
+	tap_psTime_02 \
+	tap_psTime_03 \
+	tap_psTime_04
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+EXTRA_DIST = \
+	data/psTime.config.template \
+	data/test.psTime.config1.template \
+	data/test.psTime.config2.template \
+	data/test.psTime.config3.template \
+	data/test.psTime.config4.template \
+	data/test.ser7.dat
+
+tmp_files = \
+	pslib.config \
+	test.psTime.config1 \
+	test.psTime.config2 \
+	test.psTime.config3 \
+	test.psTime.config4 \
+	test.ser7.dat
+
+CLEANFILES = $(tmp_files) core core.* *~ *.bb *.bbg *.da gmon.out
+
+tests: $(check_PROGRAMS)
+	$(top_srcdir)/test/test.pl
+
+
+pslib.config: $(top_srcdir)/etc/pslib.config.template
+	$(PERL) -pe 's|DATADIR|$(top_srcdir)/share|' $? > $@
+
+test.psTime.config1: $(srcdir)/data/test.psTime.config1.template
+	cp $? $@
+
+test.psTime.config2: $(srcdir)/data/test.psTime.config2.template
+	$(PERL) -pe 's|PREFIX|$(top_srcdir)|' $? > $@
+
+test.psTime.config3: $(srcdir)/data/test.psTime.config3.template
+	$(PERL) -pe 's|PREFIX|$(top_srcdir)|' $? > $@
+
+test.psTime.config4: $(srcdir)/data/test.psTime.config4.template
+	cp $? $@
+
+test.ser7.dat: $(srcdir)/data/test.ser7.dat
+	cp $? $@
+
+test: check
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/psTime.config.template
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/psTime.config.template	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/psTime.config.template	(revision 22322)
@@ -0,0 +1,23 @@
+# This configuration file specifies values required for time calculations by psLib.
+psLib.time.tables.dir STR PREFIX/share                                                  # Directory for time tables
+psLib.time.tables.n S32 4                                                               # Number of time tables
+psLib.time.tables.files STR ser7.dat eopc01_1900_2004.dat finals_all.dat tai_utc.dat    # These are the table file names
+@psLib.time.tables.from F64 53258.0, 15020.0, 41684.0 2437300.5                         # Valid from these dates
+@psLib.time.tables.to F64 53622.0, 53258.0, 53627.0 2451179.5                           # Valid to these dates
+psLib.time.before.xp F64 0.0                                                            # Value of XP for before the earliest MJD
+psLib.time.before.yp F64 0.0                                                            # Value of YP for before the earliest MJD
+psLib.time.before.dut F64 0.0                                                           # Value of UT1-UTC for before the earliest MJD
+
+# Now follows formulae for predicting ahead of the most recent available table entry.
+# xp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# yp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# A = 2*pi*(MJD - pslib.time.predict.mjd)/365.25
+# C = 2*pi*(MJD - pslib.time.predict.mjd)/435.0
+# dut = ut1-utc = [0] + [1]*(MJD - [2]) - (UT2-UT1)
+# ut2-ut1 = 0.022 sin(2*pi*T) - 0.012 cos(2*pi*T) - 0.006 sin(4*pi*T) + 0.007 cos(4*pi*T)
+# T = 2000.0 + (MJD - 51544.03)/365.2422
+@psLib.time.predict.xp F64 0.0569, 0.0555, -0.0200, 0.0513, 0.1483
+@psLib.time.predict.yp F64 0.3498, -0.0176, -0.0498, 0.1483, -0.0513
+psLib.time.predict.mjd F64 53257.0
+@psLib.time.predict.dut F64 -0.4944, -0.00023, 53262.0
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config1.template
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config1.template	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config1.template	(revision 22322)
@@ -0,0 +1,23 @@
+# This configuration file specifies values required for time calculations by psLib.
+psLib.time.tables.dir STR bogus                                                    # Directory for time tables
+psLib.time.tables.n S32 4                                                               # Number of time tables
+psLib.time.tables.files STR ser7.dat eopc01_1900_2004.dat finals_all.dat tai_utc.dat    # These are the table file names
+@psLib.time.tables.from F64 53258.0, 15020.0, 41684.0 2437300.5                         # Valid from these dates
+@psLib.time.tables.to F64 53622.0, 53258.0, 53627.0 2451179.5                           # Valid to these dates
+psLib.time.before.xp F64 0.0                                                            # Value of XP for before the earliest MJD
+psLib.time.before.yp F64 0.0                                                            # Value of YP for before the earliest MJD
+psLib.time.before.dut F64 0.0                                                           # Value of UT1-UTC for before the earliest MJD
+
+# Now follows formulae for predicting ahead of the most recent available table entry.
+# xp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# yp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# A = 2*pi*(MJD - pslib.time.predict.mjd)/365.25
+# C = 2*pi*(MJD - pslib.time.predict.mjd)/435.0
+# dut = ut1-utc = [0] + [1]*(MJD - [2]) - (UT2-UT1)
+# ut2-ut1 = 0.022 sin(2*pi*T) - 0.012 cos(2*pi*T) - 0.006 sin(4*pi*T) + 0.007 cos(4*pi*T)
+# T = 2000.0 + (MJD - 51544.03)/365.2422
+@psLib.time.predict.xp F64 0.0569, 0.0555, -0.0200, 0.0513, 0.1483
+@psLib.time.predict.yp F64 0.3498, -0.0176, -0.0498, 0.1483, -0.0513
+psLib.time.predict.mjd F64 53257.0
+@psLib.time.predict.dut F64 -0.4944, -0.00023, 53262.0
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config2.template
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config2.template	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config2.template	(revision 22322)
@@ -0,0 +1,23 @@
+# This configuration file specifies values required for time calculations by psLib.
+psLib.time.tables.dir STR PREFIX/share                                                  # Directory for time tables
+psLib.time.tables.n S32 4                                                               # Number of time tables
+psLib.time.tables.files STR eopc01_1900_2004.dat finals_all.dat tai_utc.dat    # These are the table file names
+@psLib.time.tables.from F64 53258.0, 15020.0, 41684.0 2437300.5                         # Valid from these dates
+@psLib.time.tables.to F64 53622.0, 53258.0, 53627.0 2451179.5                           # Valid to these dates
+psLib.time.before.xp F64 0.0                                                            # Value of XP for before the earliest MJD
+psLib.time.before.yp F64 0.0                                                            # Value of YP for before the earliest MJD
+psLib.time.before.dut F64 0.0                                                           # Value of UT1-UTC for before the earliest MJD
+
+# Now follows formulae for predicting ahead of the most recent available table entry.
+# xp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# yp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# A = 2*pi*(MJD - pslib.time.predict.mjd)/365.25
+# C = 2*pi*(MJD - pslib.time.predict.mjd)/435.0
+# dut = ut1-utc = [0] + [1]*(MJD - [2]) - (UT2-UT1)
+# ut2-ut1 = 0.022 sin(2*pi*T) - 0.012 cos(2*pi*T) - 0.006 sin(4*pi*T) + 0.007 cos(4*pi*T)
+# T = 2000.0 + (MJD - 51544.03)/365.2422
+@psLib.time.predict.xp F64 0.0569, 0.0555, -0.0200, 0.0513, 0.1483
+@psLib.time.predict.yp F64 0.3498, -0.0176, -0.0498, 0.1483, -0.0513
+psLib.time.predict.mjd F64 53257.0
+@psLib.time.predict.dut F64 -0.4944, -0.00023, 53262.0
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config3.template
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config3.template	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config3.template	(revision 22322)
@@ -0,0 +1,23 @@
+# This configuration file specifies values required for time calculations by psLib.
+psLib.time.tables.dir STR PREFIX/share                                                  # Directory for time tables
+psLib.time.tables.n S32 4                                                               # Number of time tables
+psLib.time.tables.files STR ser7.dat eopc01_1900_2004.dat finals_all.dat tai_utc.dat    # These are the table file names
+@psLib.time.tables.from F64 15020.0, 41684.0 2437300.5                         # Valid from these dates
+@psLib.time.tables.to F64 53622.0, 53258.0, 53627.0 2451179.5                           # Valid to these dates
+psLib.time.before.xp F64 0.0                                                            # Value of XP for before the earliest MJD
+psLib.time.before.yp F64 0.0                                                            # Value of YP for before the earliest MJD
+psLib.time.before.dut F64 0.0                                                           # Value of UT1-UTC for before the earliest MJD
+
+# Now follows formulae for predicting ahead of the most recent available table entry.
+# xp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# yp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# A = 2*pi*(MJD - pslib.time.predict.mjd)/365.25
+# C = 2*pi*(MJD - pslib.time.predict.mjd)/435.0
+# dut = ut1-utc = [0] + [1]*(MJD - [2]) - (UT2-UT1)
+# ut2-ut1 = 0.022 sin(2*pi*T) - 0.012 cos(2*pi*T) - 0.006 sin(4*pi*T) + 0.007 cos(4*pi*T)
+# T = 2000.0 + (MJD - 51544.03)/365.2422
+@psLib.time.predict.xp F64 0.0569, 0.0555, -0.0200, 0.0513, 0.1483
+@psLib.time.predict.yp F64 0.3498, -0.0176, -0.0498, 0.1483, -0.0513
+psLib.time.predict.mjd F64 53257.0
+@psLib.time.predict.dut F64 -0.4944, -0.00023, 53262.0
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config4.template
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config4.template	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.psTime.config4.template	(revision 22322)
@@ -0,0 +1,23 @@
+# This configuration file specifies values required for time calculations by psLib.
+psLib.time.tables.dir STR .                                                             # Directory for time tables
+psLib.time.tables.n S32 1                                                               # Number of time tables
+psLib.time.tables.files STR test.ser7.dat                                               # This the table file
+@psLib.time.tables.from F64 53258.0                                                     # Valid from this date
+@psLib.time.tables.to F64 53622.0                                                       # Valid to this date
+psLib.time.before.xp F64 0.0                                                            # Value of XP for before the earliest MJD
+psLib.time.before.yp F64 0.0                                                            # Value of YP for before the earliest MJD
+psLib.time.before.dut F64 0.0                                                           # Value of UT1-UTC for before the earliest MJD
+
+# Now follows formulae for predicting ahead of the most recent available table entry.
+# xp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# yp = [0] + [1]*cos A + [2]*sin A + [3]*cos C + [4]*sin C
+# A = 2*pi*(MJD - pslib.time.predict.mjd)/365.25
+# C = 2*pi*(MJD - pslib.time.predict.mjd)/435.0
+# dut = ut1-utc = [0] + [1]*(MJD - [2]) - (UT2-UT1)
+# ut2-ut1 = 0.022 sin(2*pi*T) - 0.012 cos(2*pi*T) - 0.006 sin(4*pi*T) + 0.007 cos(4*pi*T)
+# T = 2000.0 + (MJD - 51544.03)/365.2422
+@psLib.time.predict.xp F64 0.0569
+@psLib.time.predict.yp F64 0.3498
+psLib.time.predict.mjd F64 53257.0
+@psLib.time.predict.dut F64 -0.4944
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.ser7.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.ser7.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/data/test.ser7.dat	(revision 22322)
@@ -0,0 +1,380 @@
+#  ser7.dat
+#
+#  This file comes from http://maia.usno.navy.mil (Click on General Information about Earth Orientation). This
+#  file may also be directly downloaded from ftp://maia.usno.navy.mil/ser7/ser7.dat. See readme.ser7 (located
+#  in the psLib/data directory) for details.
+#
+#  @author Ross Harman, MHPCC
+#
+#  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+#  @date $Date: 2005-07-13 02:46:58 $
+#
+#
+#        MJD         PM-x        PM-y       UT1-UTC
+#      (days)      (arcsec)    (arcsec)      (sec)
+        psF64        psF64       psF64       psF64
+        53yyy244       0.1377      0.5027     -0.45210
+        53245       0.1394      0.5015     -0.45190
+        53246       0.1411      0.5002     -0.45197
+        53247       0.1429      0.4989     -0.45234
+        53248       0.1447      0.4976     -0.45293
+        53249       0.1465      0.4962     -0.45361
+        53250       0.1484      0.4948     -0.45424
+        53251       0.1503      0.4934     -0.45476
+        53252       0.1521      0.4920     -0.45512
+        53253       0.1540      0.4905     -0.45530
+        53254       0.1558      0.4890     -0.45531
+        53255       0.1577      0.4874     -0.45519
+        53256       0.1595      0.4859     -0.45497
+        53257       0.1613      0.4843     -0.45474
+        53258       0.1631      0.4827     -0.45458
+        53259       0.1649      0.4810     -0.45456
+        53260       0.1667      0.4794     -0.45474
+        53261       0.1684      0.4777     -0.45514
+        53262       0.1701      0.4760     -0.45578
+        53263       0.1717      0.4742     -0.45660
+        53264       0.1734      0.4725     -0.45754
+        53265       0.1750      0.4707     -0.45849
+        53266       0.1765      0.4689     -0.45932
+        53267       0.1780      0.4671     -0.45996
+        53268       0.1795      0.4652     -0.46036
+        53269       0.1810      0.4634     -0.46055
+        53270       0.1824      0.4615     -0.46061
+        53271       0.1838      0.4596     -0.46068
+        53272       0.1852      0.4576     -0.46086
+        53273       0.1865      0.4557     -0.46126
+        53274       0.1878      0.4537     -0.46192
+        53275       0.1890      0.4517     -0.46280
+        53276       0.1902      0.4497     -0.46382
+        53277       0.1914      0.4477     -0.46486
+        53278       0.1925      0.4457     -0.46581
+        53279       0.1936      0.4436     -0.46658
+        53280       0.1946      0.4415     -0.46715
+        53281       0.1956      0.4394     -0.46750
+        53282       0.1966      0.4373     -0.46766
+        53283       0.1976      0.4352     -0.46772
+        53284       0.1985      0.4331     -0.46773
+        53285       0.1993      0.4309     -0.46778
+        53286       0.2001      0.4288     -0.46795
+        53287       0.2009      0.4266     -0.46830
+        53288       0.2017      0.4244     -0.46887
+        53289       0.2024      0.4222     -0.46968
+        53290       0.2031      0.4200     -0.47071
+        53291       0.2037      0.4178     -0.47191
+        53292       0.2043      0.4155     -0.47317
+        53293       0.2049      0.4133     -0.47435
+        53294       0.2054      0.4111     -0.47536
+        53295       0.2059      0.4088     -0.47610
+        53296       0.2063      0.4065     -0.47659
+        53297       0.2067      0.4042     -0.47691
+        53298       0.2071      0.4020     -0.47718
+        53299       0.2075      0.3997     -0.47755
+        53300       0.2078      0.3974     -0.47811
+        53301       0.2080      0.3951     -0.47893
+        53302       0.2083      0.3928     -0.47998
+        53303       0.2085      0.3905     -0.48119
+        53304       0.2086      0.3881     -0.48247
+        53305       0.2088      0.3858     -0.48369
+        53306       0.2089      0.3835     -0.48477
+        53307       0.2089      0.3812     -0.48565
+        53308       0.2090      0.3789     -0.48630
+        53309       0.2089      0.3765     -0.48674
+        53310       0.2089      0.3742     -0.48701
+        53311       0.2088      0.3719     -0.48718
+        53312       0.2088      0.3696     -0.48732
+        53313       0.2086      0.3672     -0.48753
+        53314       0.2085      0.3649     -0.48785
+        53315       0.2082      0.3626     -0.48835
+        53316       0.2080      0.3603     -0.48906
+        53317       0.2077      0.3579     -0.48997
+        53318       0.2074      0.3556     -0.49106
+        53319       0.2071      0.3533     -0.49226
+        53320       0.2067      0.3510     -0.49344
+        53321       0.2063      0.3487     -0.49449
+        53322       0.2059      0.3464     -0.49531
+        53323       0.2054      0.3441     -0.49585
+        53324       0.2049      0.3418     -0.49618
+        53325       0.2044      0.3396     -0.49639
+        53326       0.2038      0.3373     -0.49664
+        53327       0.2032      0.3350     -0.49705
+        53328       0.2026      0.3328     -0.49769
+        53329       0.2020      0.3305     -0.49854
+        53330       0.2013      0.3283     -0.49953
+        53331       0.2006      0.3261     -0.50058
+        53332       0.1999      0.3239     -0.50158
+        53333       0.1991      0.3217     -0.50243
+        53334       0.1983      0.3195     -0.50308
+        53335       0.1975      0.3173     -0.50350
+        53336       0.1967      0.3151     -0.50369
+        53337       0.1958      0.3130     -0.50370
+        53338       0.1949      0.3108     -0.50358
+        53339       0.1940      0.3087     -0.50342
+        53340       0.1931      0.3066     -0.50329
+        53341       0.1921      0.3045     -0.50327
+        53342       0.1911      0.3024     -0.50340
+        53343       0.1901      0.3004     -0.50372
+        53344       0.1890      0.2983     -0.50423
+        53345       0.1880      0.2963     -0.50493
+        53346       0.1869      0.2943     -0.50574
+        53347       0.1858      0.2923     -0.50660
+        53348       0.1846      0.2903     -0.50739
+        53349       0.1835      0.2883     -0.50808
+        53350       0.1823      0.2864     -0.50851
+        53351       0.1811      0.2844     -0.50870
+        53352       0.1799      0.2825     -0.50874
+        53353       0.1786      0.2807     -0.50875
+        53354       0.1773      0.2788     -0.50890
+        53355       0.1760      0.2769     -0.50927
+        53356       0.1747      0.2751     -0.50987
+        53357       0.1734      0.2733     -0.51064
+        53358       0.1720      0.2715     -0.51148
+        53359       0.1707      0.2698     -0.51228
+        53360       0.1693      0.2680     -0.51295
+        53361       0.1679      0.2663     -0.51342
+        53362       0.1664      0.2646     -0.51367
+        53363       0.1650      0.2629     -0.51371
+        53364       0.1635      0.2613     -0.51357
+        53365       0.1620      0.2596     -0.51330
+        53366       0.1605      0.2580     -0.51298
+        53367       0.1590      0.2565     -0.51268
+        53368       0.1575      0.2549     -0.51247
+        53369       0.1559      0.2534     -0.51241
+        53370       0.1543      0.2519     -0.51253
+        53371       0.1528      0.2504     -0.51283
+        53372       0.1511      0.2489     -0.51331
+        53373       0.1495      0.2475     -0.51391
+        53374       0.1479      0.2461     -0.51458
+        53375       0.1463      0.2447     -0.51522
+        53376       0.1446      0.2434     -0.51574
+        53377       0.1429      0.2420     -0.51605
+        53378       0.1412      0.2407     -0.51614
+        53379       0.1395      0.2395     -0.51605
+        53380       0.1378      0.2382     -0.51590
+        53381       0.1361      0.2370     -0.51585
+        53382       0.1344      0.2358     -0.51603
+        53383       0.1326      0.2347     -0.51648
+        53384       0.1308      0.2335     -0.51717
+        53385       0.1291      0.2324     -0.51800
+        53386       0.1273      0.2313     -0.51881
+        53387       0.1255      0.2303     -0.51951
+        53388       0.1237      0.2293     -0.52000
+        53389       0.1219      0.2283     -0.52027
+        53390       0.1200      0.2273     -0.52032
+        53391       0.1182      0.2264     -0.52018
+        53392       0.1164      0.2255     -0.51992
+        53393       0.1145      0.2246     -0.51959
+        53394       0.1126      0.2237     -0.51928
+        53395       0.1108      0.2229     -0.51905
+        53396       0.1089      0.2221     -0.51897
+        53397       0.1070      0.2213     -0.51908
+        53398       0.1051      0.2206     -0.51939
+        53399       0.1032      0.2199     -0.51987
+        53400       0.1013      0.2192     -0.52050
+        53401       0.0994      0.2186     -0.52119
+        53402       0.0975      0.2180     -0.52187
+        53403       0.0956      0.2174     -0.52246
+        53404       0.0936      0.2168     -0.52288
+        53405       0.0917      0.2163     -0.52309
+        53406       0.0898      0.2158     -0.52311
+        53407       0.0878      0.2153     -0.52305
+        53408       0.0859      0.2149     -0.52303
+        53409       0.0840      0.2144     -0.52321
+        53410       0.0820      0.2141     -0.52368
+        53411       0.0801      0.2137     -0.52444
+        53412       0.0781      0.2134     -0.52542
+        53413       0.0761      0.2131     -0.52646
+        53414       0.0742      0.2128     -0.52744
+        53415       0.0722      0.2126     -0.52822
+        53416       0.0703      0.2124     -0.52877
+        53417       0.0683      0.2122     -0.52907
+        53418       0.0663      0.2120     -0.52916
+        53419       0.0644      0.2119     -0.52910
+        53420       0.0624      0.2118     -0.52897
+        53421       0.0605      0.2117     -0.52883
+        53422       0.0585      0.2117     -0.52876
+        53423       0.0566      0.2117     -0.52882
+        53424       0.0546      0.2117     -0.52907
+        53425       0.0526      0.2118     -0.52951
+        53426       0.0507      0.2118     -0.53014
+        53427       0.0487      0.2119     -0.53091
+        53428       0.0468      0.2121     -0.53174
+        53429       0.0449      0.2122     -0.53254
+        53430       0.0429      0.2124     -0.53321
+        53431       0.0410      0.2126     -0.53368
+        53432       0.0391      0.2129     -0.53392
+        53433       0.0372      0.2131     -0.53393
+        53434       0.0352      0.2134     -0.53380
+        53435       0.0333      0.2138     -0.53366
+        53436       0.0314      0.2141     -0.53365
+        53437       0.0296      0.2145     -0.53389
+        53438       0.0277      0.2149     -0.53443
+        53439       0.0258      0.2154     -0.53526
+        53440       0.0239      0.2159     -0.53627
+        53441       0.0221      0.2164     -0.53733
+        53442       0.0202      0.2169     -0.53830
+        53443       0.0184      0.2174     -0.53908
+        53444       0.0166      0.2180     -0.53964
+        53445       0.0147      0.2186     -0.53998
+        53446       0.0129      0.2192     -0.54014
+        53447       0.0111      0.2198     -0.54018
+        53448       0.0093      0.2205     -0.54018
+        53449       0.0076      0.2212     -0.54022
+        53450       0.0058      0.2219     -0.54037
+        53451       0.0040      0.2227     -0.54069
+        53452       0.0023      0.2234     -0.54118
+        53453       0.0005      0.2242     -0.54185
+        53454      -0.0012      0.2250     -0.54266
+        53455      -0.0029      0.2258     -0.54356
+        53456      -0.0046      0.2267     -0.54447
+        53457      -0.0063      0.2276     -0.54530
+        53458      -0.0079      0.2285     -0.54598
+        53459      -0.0096      0.2294     -0.54647
+        53460      -0.0112      0.2303     -0.54675
+        53461      -0.0128      0.2313     -0.54688
+        53462      -0.0144      0.2323     -0.54699
+        53463      -0.0160      0.2333     -0.54724
+        53464      -0.0176      0.2343     -0.54775
+        53465      -0.0191      0.2353     -0.54858
+        53466      -0.0207      0.2364     -0.54970
+        53467      -0.0222      0.2375     -0.55097
+        53468      -0.0237      0.2386     -0.55228
+        53469      -0.0252      0.2397     -0.55354
+        53470      -0.0267      0.2408     -0.55465
+        53471      -0.0281      0.2420     -0.55556
+        53472      -0.0295      0.2431     -0.55626
+        53473      -0.0310      0.2443     -0.55677
+        53474      -0.0323      0.2455     -0.55715
+        53475      -0.0337      0.2468     -0.55748
+        53476      -0.0351      0.2480     -0.55785
+        53477      -0.0364      0.2493     -0.55833
+        53478      -0.0377      0.2505     -0.55899
+        53479      -0.0390      0.2518     -0.55988
+        53480      -0.0403      0.2531     -0.56095
+        53481      -0.0415      0.2545     -0.56216
+        53482      -0.0427      0.2558     -0.56347
+        53483      -0.0439      0.2572     -0.56478
+        53484      -0.0451      0.2585     -0.56599
+        53485      -0.0463      0.2599     -0.56699
+        53486      -0.0474      0.2613     -0.56769
+        53487      -0.0485      0.2627     -0.56808
+        53488      -0.0496      0.2641     -0.56824
+        53489      -0.0507      0.2656     -0.56830
+        53490      -0.0517      0.2670     -0.56839
+        53491      -0.0527      0.2685     -0.56860
+        53492      -0.0537      0.2700     -0.56897
+        53493      -0.0547      0.2714     -0.56955
+        53494      -0.0556      0.2729     -0.57032
+        53495      -0.0565      0.2744     -0.57119
+        53496      -0.0574      0.2760     -0.57204
+        53497      -0.0583      0.2775     -0.57277
+        53498      -0.0591      0.2790     -0.57329
+        53499      -0.0600      0.2806     -0.57354
+        53500      -0.0607      0.2821     -0.57350
+        53501      -0.0615      0.2837     -0.57328
+        53502      -0.0622      0.2853     -0.57298
+        53503      -0.0630      0.2869     -0.57261
+        53504      -0.0636      0.2885     -0.57218
+        53505      -0.0643      0.2901     -0.57175
+        53506      -0.0649      0.2917     -0.57143
+        53507      -0.0655      0.2933     -0.57124
+        53508      -0.0661      0.2949     -0.57117
+        53509      -0.0667      0.2966     -0.57124
+        53510      -0.0672      0.2982     -0.57139
+        53511      -0.0677      0.2999     -0.57158
+        53512      -0.0682      0.3015     -0.57166
+        53513      -0.0686      0.3032     -0.57155
+        53514      -0.0690      0.3048     -0.57116
+        53515      -0.0694      0.3065     -0.57057
+        53516      -0.0698      0.3082     -0.56989
+        53517      -0.0701      0.3098     -0.56921
+        53518      -0.0704      0.3115     -0.56860
+        53519      -0.0707      0.3132     -0.56818
+        53520      -0.0710      0.3149     -0.56799
+        53521      -0.0712      0.3166     -0.56800
+        53522      -0.0714      0.3183     -0.56809
+        53523      -0.0716      0.3200     -0.56815
+        53524      -0.0717      0.3217     -0.56808
+        53525      -0.0718      0.3234     -0.56781
+        53526      -0.0719      0.3251     -0.56728
+        53527      -0.0720      0.3268     -0.56651
+        53528      -0.0720      0.3285     -0.56557
+        53529      -0.0720      0.3302     -0.56454
+        53530      -0.0720      0.3319     -0.56347
+        53531      -0.0720      0.3336     -0.56244
+        53532      -0.0719      0.3353     -0.56148
+        53533      -0.0718      0.3370     -0.56060
+        53534      -0.0717      0.3387     -0.55990
+        53535      -0.0715      0.3404     -0.55943
+        53536      -0.0713      0.3421     -0.55916
+        53537      -0.0711      0.3438     -0.55897
+        53538      -0.0709      0.3455     -0.55881
+        53539      -0.0707      0.3472     -0.55859
+        53540      -0.0704      0.3489     -0.55820
+        53541      -0.0701      0.3506     -0.55751
+        53542      -0.0697      0.3523     -0.55650
+        53543      -0.0694      0.3540     -0.55529
+        53544      -0.0690      0.3557     -0.55402
+        53545      -0.0686      0.3573     -0.55279
+        53546      -0.0682      0.3590     -0.55173
+        53547      -0.0677      0.3607     -0.55093
+        53548      -0.0673      0.3623     -0.55037
+        53549      -0.0668      0.3640     -0.54994
+        53550      -0.0662      0.3656     -0.54947
+        53551      -0.0657      0.3673     -0.54884
+        53552      -0.0651      0.3689     -0.54798
+        53553      -0.0645      0.3705     -0.54689
+        53554      -0.0639      0.3721     -0.54560
+        53555      -0.0633      0.3738     -0.54414
+        53556      -0.0626      0.3754     -0.54257
+        53557      -0.0620      0.3770     -0.54101
+        53558      -0.0613      0.3786     -0.53952
+        53559      -0.0605      0.3801     -0.53819
+        53560      -0.0598      0.3817     -0.53705
+        53561      -0.0590      0.3833     -0.53611
+        53562      -0.0583      0.3848     -0.53535
+        53563      -0.0575      0.3864     -0.53475
+        53564      -0.0566      0.3879     -0.53425
+        53565      -0.0558      0.3895     -0.53374
+        53566      -0.0549      0.3910     -0.53315
+        53567      -0.0541      0.3925     -0.53243
+        53568      -0.0532      0.3940     -0.53152
+        53569      -0.0523      0.3955     -0.53042
+        53570      -0.0513      0.3969     -0.52913
+        53571      -0.0504      0.3984     -0.52776
+        53572      -0.0494      0.3998     -0.52644
+        53573      -0.0485      0.4013     -0.52532
+        53574      -0.0475      0.4027     -0.52451
+        53575      -0.0464      0.4041     -0.52400
+        53576      -0.0454      0.4055     -0.52377
+        53577      -0.0444      0.4069     -0.52366
+        53578      -0.0433      0.4083     -0.52344
+        53579      -0.0423      0.4096     -0.52299
+        53580      -0.0412      0.4110     -0.52228
+        53581      -0.0401      0.4123     -0.52130
+        53582      -0.0390      0.4136     -0.52010
+        53583      -0.0378      0.4149     -0.51872
+        53584      -0.0367      0.4162     -0.51725
+        53585      -0.0356      0.4175     -0.51578
+        53586      -0.0344      0.4188     -0.51436
+        53587      -0.0332      0.4200     -0.51310
+        53588      -0.0320      0.4212     -0.51204
+        53589      -0.0309      0.4224     -0.51120
+        53590      -0.0297      0.4236     -0.51055
+        53591      -0.0284      0.4248     -0.51006
+        53592      -0.0272      0.4260     -0.50966
+        53593      -0.0260      0.4271     -0.50922
+        53594      -0.0247      0.4283     -0.50872
+        53595      -0.0235      0.4294     -0.50809
+        53596      -0.0222      0.4305     -0.50729
+        53597      -0.0210      0.4315     -0.50638
+        53598      -0.0197      0.4326     -0.50535
+        53599      -0.0184      0.4336     -0.50431
+        53600      -0.0171      0.4347     -0.50339
+        53601      -0.0158      0.4357     -0.50273
+        53602      -0.0145      0.4367     -0.50244
+        53603      -0.0132      0.4376     -0.50247
+        53604      -0.0119      0.4386     -0.50267
+        53605      -0.0106      0.4395     -0.50286
+        53606      -0.0093      0.4404     -0.50287
+        53607      -0.0079      0.4413     -0.50263
+        53608      -0.0066      0.4422     -0.50217
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/execute_tap
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/execute_tap	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/execute_tap	(revision 22322)
@@ -0,0 +1,4 @@
+make test
+./tap_psSphereOps_all
+./tap_psEarthOrientation_corrections
+./tap_psEarthOrientation_motion
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psCoord.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psCoord.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psCoord.c	(revision 22322)
@@ -0,0 +1,370 @@
+/** @file  tst_psCoord.c
+*
+*  @brief The code will ...
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-06-05 01:10:22 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define ORDER_X 2
+#define ORDER_Y 3
+#define ORDER_Z 4
+#define ORDER_T 5
+#define N 10
+#define COLOR 1.0
+#define MAGNITUDE 1.0
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(57);
+
+    // Test psPlaneAlloc()
+    {
+        psMemId id = psMemGetId();
+        psPlane *myP = psPlaneAlloc();
+        ok(myP != NULL, "psPlaneAlloc() returned non-NULL");
+        skip_start(myP == NULL, 4, "Skipping tests because psPlaneAlloc() returned NULL");
+        ok(isnan(myP->x), "psPlane->x is NAN");
+        ok(isnan(myP->y), "psPlane->y is NAN");
+        ok(isnan(myP->xErr), "psPlane->xErr is NAN");
+        ok(isnan(myP->yErr), "psPlane->yErr is NAN");
+        skip_end();
+        psFree(myP);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test psPlaneTransformAlloc()
+    {
+        psMemId id = psMemGetId();
+        psPlaneTransform *myPT = psPlaneTransformAlloc(ORDER_X, ORDER_Y);
+        ok(myPT != NULL, "psPlaneTransformAlloc() returned non-NULL");
+        skip_start(myPT == NULL, 4, "Skipping tests because psPlaneTransformAlloc() returned NULL");
+        ok(myPT->x->nX == ORDER_X, "psPlaneTransform->x->nX set correctly");
+        ok(myPT->y->nX == ORDER_X, "psPlaneTransform->y->nX set correctly");
+        ok(myPT->x->nY == ORDER_Y, "psPlaneTransform->x->nY set correctly");
+        ok(myPT->y->nY == ORDER_Y, "psPlaneTransform->y->nY set correctly");
+        psFree(myPT);
+
+        // Attempt to specify negative x order and verify NULL returned and
+        // errror message generated
+        myPT = psPlaneTransformAlloc(-1, 1);
+        ok(myPT == NULL, "psPlaneTransformAlloc(-1, 1) returned NULL");
+        psFree(myPT);
+
+        myPT = psPlaneTransformAlloc(1, -1);
+        ok(myPT == NULL, "psPlaneTransformAlloc(1, -1) returned NULL");
+        psFree(myPT);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test psPlaneDistortAlloc()
+    {
+        psMemId id = psMemGetId();
+        psPlaneDistort *myPD = psPlaneDistortAlloc(ORDER_X, ORDER_Y, ORDER_Z, ORDER_T);
+        ok(myPD != NULL, "psPlaneDistortAlloc() returned non-NULL");
+        skip_start(myPD == NULL, 12, "Skipping tests because psPlaneDistortAlloc() returned NULL");
+        ok(myPD->x->nX == ORDER_X, "psPlaneDistort->x->nX set correctly");
+        ok(myPD->x->nY == ORDER_Y, "psPlaneDistort->x->nY set correctly");
+        ok(myPD->x->nZ == ORDER_Z, "psPlaneDistort->x->nZ set correctly");
+        ok(myPD->x->nT == ORDER_T, "psPlaneDistort->x->nT set correctly");
+        ok(myPD->y->nX == ORDER_X, "psPlaneDistort->y->nX set correctly");
+        ok(myPD->y->nY == ORDER_Y, "psPlaneDistort->y->nY set correctly");
+        ok(myPD->y->nZ == ORDER_Z, "psPlaneDistort->y->nZ set correctly");
+        ok(myPD->y->nT == ORDER_T, "psPlaneDistort->y->nT set correctly");
+        psFree(myPD);
+
+        myPD = psPlaneDistortAlloc(-1, 1, 1, 1);
+        ok(myPD == NULL, "psPlaneDistortAlloc(-1, 1, 1, 1) returned NULL");
+        psFree(myPD);
+        myPD = psPlaneDistortAlloc(1, -1, 1, 1);
+        ok(myPD == NULL, "psPlaneDistortAlloc(1, -1, 1, 1) returned NULL");
+        psFree(myPD);
+        myPD = psPlaneDistortAlloc(1, 1, -1, 1);
+        ok(myPD == NULL, "psPlaneDistortAlloc(1, 1, -1, 1) returned NULL");
+        psFree(myPD);
+        myPD = psPlaneDistortAlloc(1, 1, 1, -1);
+        ok(myPD == NULL, "psPlaneDistortAlloc(1, 1, 1, -1) returned NULL");
+        psFree(myPD);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // We test psPlaneTransformApply() on a simple identity transformation for few x,y pairs.
+    {
+        psMemId id = psMemGetId();
+        psPlane* in = psPlaneAlloc();
+        ok(in != NULL, "psPlaneAlloc() returned non-NULL");
+        skip_start(in == NULL, 2, "Skipping tests because psPlaneAlloc() returned NULL");
+        psPlaneTransform* pt = psPlaneTransformAlloc(2,2);
+        ok(pt != NULL, "psPlaneTransformAlloc() returned non-NULL");
+        skip_start(pt == NULL, 1, "Skipping tests because psPlaneTransformAlloc() returned NULL");
+
+        // Set transform coefficients so the x coord input will equal x coord output, same for y
+        pt->x->coeff[1][0] = 1.0;
+        pt->y->coeff[0][1] = 1.0;
+
+        // Apply transform for several points
+        bool errorFlag = false;
+        for (psS32 i = 0; i < N; i++)
+        {
+            in->x = (psF64) i;
+            in->y = (psF64) (i + 5.0);
+            in->xErr = 0.0;
+            in->yErr = 0.0;
+
+            // XXX: psPlane *out = psPlaneTransformApply(out, pt, in); causes a seg-fault.
+            // Why?
+            psPlane *out = psPlaneTransformApply(NULL, pt, in);
+            if(out == NULL) {
+                diag("ERROR: psPlaneTransformApply() returned NULL");
+                errorFlag = true;
+            } else {
+                if (FLT_EPSILON < fabs(out->x - in->x)) {
+                    diag("ERROR: out.x is %lf, should be %lf", out->x, in->x);
+                    errorFlag = true;
+                }
+
+                if (FLT_EPSILON < fabs(out->y - in->y)) {
+                    diag("ERROR: out.y is %lf, should be %lf", out->y, in->y);
+                    errorFlag = true;
+                }
+            }
+            psFree(out);
+        }
+        ok(!errorFlag, "psPlaneTransformApply() successful on several data points");
+        psFree(pt);
+        psFree(in);
+        skip_end();
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psPlaneTransformApply should generate error message for NULL psPlaneTransform
+    {
+        psMemId id = psMemGetId();
+        psPlane* in = psPlaneAlloc();
+        psPlane *tmpPL = psPlaneTransformApply(NULL, NULL, in);
+        ok(tmpPL == NULL, "psPlaneTransformApply(NULL, NULL, in) returned NULL");
+        psFree(in);
+        psFree(tmpPL);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psPlaneTransformApply should generate error message for NULL x coeff psPlaneTransform
+    {
+        psMemId id = psMemGetId();
+        psPlane *in = psPlaneAlloc();
+        psPlaneTransform *pt = psPlaneTransformAlloc(2,2);
+        psFree(pt->x);
+        pt->x = NULL;
+        psPlane *tmpPL = psPlaneTransformApply(NULL, pt, in);
+        ok(tmpPL == NULL, "psPlaneTransformApply(NULL, pt, in) returned NULL with NULL x coeff psPlaneTransform");
+        psFree(in);
+        psFree(pt);
+        psFree(tmpPL);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psPlaneTransformApply Should generate error message for NULL y coeff psPlaneTransform");
+    {
+        psMemId id = psMemGetId();
+        psPlane* in = psPlaneAlloc();
+        psPlaneTransform* pt = psPlaneTransformAlloc(2,2);
+        psFree(pt->y);
+        pt->y = NULL;
+        psPlane *tmpPL = psPlaneTransformApply(NULL, pt, in);
+        ok(tmpPL == NULL, "psPlaneTransformApply(NULL, pt, in) returned NULL with NULL y coeff psPlaneTransform");
+        psFree(tmpPL);
+        psFree(pt);
+        psFree(in);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psPlaneTransformApply() should generate error message for NULL psPlane");
+    {
+        psMemId id = psMemGetId();
+        psPlaneTransform* pt = psPlaneTransformAlloc(2,2);
+        psPlane *tmpPL = psPlaneTransformApply(NULL, pt, NULL);
+        ok(tmpPL == NULL, "psPlaneTransformApply(NULL, pt, NULL) did not return NULL");
+        psFree(tmpPL);
+        psFree(pt);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // We test psPlaneDistortApply() on a simple identity transformation for few x,y pairs.
+    {
+        psMemId id = psMemGetId();
+        psPlane *in = psPlaneAlloc();
+        ok(in != NULL, "psPlaneAlloc() returned non-NULL");
+        skip_start(in == NULL, 1, "Skipping tests because psPlaneAlloc() returned NULL");
+        psPlaneDistort *pt = psPlaneDistortAlloc(2, 2, 2, 2);
+        ok(pt != NULL, "psPlaneDistortAlloc() returned non-NULL");
+        skip_start(pt == NULL, 1, "Skipping tests because psPlaneTransformAlloc() returned NULL");
+
+        pt->x->coeff[1][0][0][0] = 1.0;
+        pt->x->coeff[0][0][1][0] = 1.0;
+        pt->x->coeff[0][0][0][1] = 1.0;
+        pt->y->coeff[0][1][0][0] = 1.0;
+        pt->y->coeff[0][0][1][0] = 1.0;
+        pt->y->coeff[0][0][0][1] = 1.0;
+
+        bool errorFlag = false;
+        for (psS32 i = 0; i < N; i++)
+        {
+            in->x = (psF64) i;
+            in->y = (psF64) (i + 5.0);
+            in->xErr = 0.0;
+            in->yErr = 0.0;
+
+            // XXX: psPlane *out = psPlaneDistortApply(out, pt, in, COLOR, MAGNITUDE); generates a seg-fault.  Why?
+            psPlane *out = psPlaneDistortApply(NULL, pt, in, COLOR, MAGNITUDE);
+            if(out == NULL) {
+                diag("ERROR: psPlaneDistortApply() returned NULL");
+                errorFlag = true;
+            } else {
+                if (FLT_EPSILON < fabs(out->x - COLOR - MAGNITUDE - in->x)) {
+                    diag("ERROR: out->x is %lf, should be %lf",
+                         out->x, in->x + COLOR + MAGNITUDE);
+                    errorFlag = true;
+                }
+                if (FLT_EPSILON < fabs(out->y - COLOR - MAGNITUDE - in->y)) {
+                    diag("ERROR: out->y is %lf, should be %lf",
+                         out->y, in->y + COLOR + MAGNITUDE);
+                    errorFlag = true;
+                }
+            }
+            psFree(out);
+        }
+        ok(!errorFlag, "psPlaneDistortApply() successful on several data points");
+        psFree(in);
+        psFree(pt);
+        skip_end();
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psPlaneDistortApply() should generate an error message for null psPlaneDistort
+    if (1) {
+        psMemId id = psMemGetId();
+        psPlane *in = psPlaneAlloc();
+        psPlane *tmpPL = psPlaneDistortApply(NULL, NULL, in, COLOR, MAGNITUDE);
+        ok(tmpPL == NULL, "psPlaneDistortApply(NULL, NULL, in, COLOR, MAGNITUDE) returned NULL");
+        psFree(tmpPL);
+        psFree(in);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psPlaneDistortApply() should generate an error message for null x member psPlaneDistort
+    if (1) {
+        psMemId id = psMemGetId();
+        psPlane *in = psPlaneAlloc();
+        psPlaneDistort *pt = psPlaneDistortAlloc(2, 2, 2, 2);
+        psFree(pt->x);
+        pt->x = NULL;
+        psPlane *tmpPL = psPlaneDistortApply(NULL, pt, in, COLOR, MAGNITUDE);
+        ok(tmpPL == NULL, "psPlaneDistortApply(NULL, pt, in, COLOR, MAGNITUDE) with NULL pt->x member");
+        psFree(tmpPL);
+        psFree(pt);
+        psFree(in);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psPlaneDistortApply() should generate an error message for null y member psPlaneDistort
+    if (1) {
+        psMemId id = psMemGetId();
+        psPlane *in = psPlaneAlloc();
+        psPlaneDistort *pt = psPlaneDistortAlloc(2, 2, 2, 2);
+        psFree(pt->y);
+        pt->y = NULL;
+        psPlane *tmpPL = psPlaneDistortApply(NULL, pt, in, COLOR, MAGNITUDE);
+        ok(tmpPL == NULL, "psPlaneDistortApply(NULL, pt, in, COLOR, MAGNITUDE) with NULL pt->y member");
+        psFree(tmpPL);
+        psFree(pt);
+        psFree(in);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psPlaneDistortApply() should generate an error message for null input psPlane
+    if (1) {
+        psMemId id = psMemGetId();
+        psPlaneDistort *pt = psPlaneDistortAlloc(2, 2, 2, 2);
+        psPlane *tmpPL = psPlaneDistortApply(NULL, pt, NULL, COLOR, MAGNITUDE);
+        ok(tmpPL == NULL, "psPlaneDistortApply(NULL, pt, NULL, COLOR, MAGNITUDE) returned NULL");
+        psFree(tmpPL);
+        psFree(pt);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test psPixelsTransform()
+    // psPixelsTransform() should generate error message for NULL input pixels
+    {
+        psMemId id = psMemGetId();
+        psPlaneTransform *trans = psPlaneTransformAlloc(1, 3);
+        psPixels *output = psPixelsTransform(NULL, NULL, trans);
+        ok(output == NULL, "psPixelsTransform(NULL, NULL, trans) returned NULL");
+        psFree(trans);
+        psFree(output);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psPixelsTransform() should generate error message for NULL input PlaneTransform
+    {
+        psMemId id = psMemGetId();
+        psPixels *input = psPixelsAlloc(2);
+        psPixels *output = psPixelsTransform(NULL, input, NULL);
+        ok(output == NULL, "psPixelsTransform(NULL, input, NULL) returned NULL");
+        psFree(output);
+        psFree(input);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // We test psPixelsTransform() on several data points
+    {
+        psMemId id = psMemGetId();
+        psPixels *input = psPixelsAlloc(2);
+        input->n = 2;
+        input->data[0].x = 1.0;
+        input->data[0].y = 1.0;
+        input->data[1].x = 1.0;
+        input->data[1].y = 6.0;
+        psPlaneTransform *trans = psPlaneTransformAlloc(1, 3);
+        trans->x->coeff[0][0] = 0;
+        trans->x->coeff[1][0] = 1.0;
+        trans->y->coeff[0][0] = 0;
+        trans->y->coeff[0][0] = 0;
+        trans->y->coeff[0][2] = 0.5;
+
+        // XXX: Fix this
+        psPixels *output = psPixelsTransform(NULL, input, trans);
+        int nExpected = 9;
+        if (output->n != nExpected)
+        {
+            diag("ERROR: psPixelsTransform failed to return the expected number of pixels.\n");
+            printf("\n output returned with %ld pixels\n\n", output->n);
+            for (int i = 0; i < output->n; i++) {
+                printf("  (%6.2lf, %6.2lf) pixel %d\n", output->data[i].x, output->data[i].y, i+1);
+            }
+            return 3;
+        }
+
+        psFree(trans);
+        psFree(input);
+        psFree(output);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psCoord01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psCoord01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psCoord01.c	(revision 22322)
@@ -0,0 +1,1047 @@
+/**  @file  tst_psCoord01.c
+*
+*    @brief  The code will test several functions with PSLib source file
+*            psCoord.c
+*
+*    @author Eric Van Alst, MHPCC
+*
+* XXX: must do (r,d) -> (x, y) -> (r, d) test to ensure correctness.
+* XXX: The (Xs, Ys) scales are not be used properly.
+* XXX: Much work remains to be done.  The original tests defined correct
+*      input/output pairs and compared results.  It's not clear how those
+*      values were obtained, and they nearly all fail now.
+*
+*    @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+*    @date  $Date: 2007-05-01 00:08:52 $
+*    @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+*    @date  $Date: 2007-05-01 00:08:52 $
+*
+*    Copyright 2005 Maui High Performance Computing Center, Univ. of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define ERROR_TOL    0.0001
+#define TESTPOINTS   4
+#define DEG_INC   30.0
+
+#define MY_TINY 0.0001
+#define PS_COMPARE_TINY_THEN_PRINT_ERROR(ACTUAL, EXPECT, TESTSTATUS) \
+if (MY_TINY < fabs(EXPECT - ACTUAL)) { \
+    diag("%s is %lg, should be %lg", #ACTUAL, ACTUAL, EXPECT, TESTSTATUS); \
+    errorFlag = true; \
+}
+
+
+//  alpha, delta, alpha-center, delta-center, scale-x, scale-y
+psF64 projectionTestPoint[TESTPOINTS][6] = {
+            {  0.785398,  0.785398,  0.000000,  0.000000,  1.000000,  1.000000 },
+            {  0.100000,  1.500000,  0.500000,  0.250000,  1.000000,  1.000000 },
+            {  0.628319,  0.448799,  0.000000,  0.000000,  0.250000,  0.750000 },
+            { -1.047196,  0.222222, -0.250000,  0.000000,  1.500000,  1.250000 }
+        };
+
+// Expected values for TAN
+psF64 projectionTanExpected[TESTPOINTS][2] = {
+            { -1.000000, -1.414214 },
+            {  0.088884, -3.066567 },
+            { -0.181636, -0.446444 },
+            {  1.535818, -0.404231 }
+        };
+
+// Expected values for SIN
+psF64 projectionSinExpected[TESTPOINTS][2] = {
+            { -0.500000, -0.707101 },
+            {  0.027546, -0.950366 },
+            { -0.132394, -0.325413 },
+            {  1.046712, -0.275497 }
+        };
+
+// Expected values for AIT
+psF64 projectionAitExpected[TESTPOINTS][2] = {
+            { -0.549175,  0.523375 },
+            {  0.027895,  0.313807 },
+            { -0.162822,  0.607646 },
+            {  1.455312,  0.955388 }
+        };
+
+// Expected values for PAR
+psF64 projectionParExpected[TESTPOINTS][2] = {
+            { -0.541244,  0.545532 },
+            {  0.027703,  0.329366 },
+            { -0.157157,  0.633550 },
+            {  1.432951,  0.971372 }
+        };
+
+// Testpoints, offset and expected values for psSphereSetOffset test
+#define TESTPOINTS_OFFSET  4
+psF64 setOffsetTestpoint[TESTPOINTS_OFFSET][2] = {
+            { 0.50,  0.50 },
+            {-0.50,  0.50 },
+            { 0.00,  0.00 },
+            { 1.50, -0.10 }
+        };
+
+psF64 setOffsetOffset[TESTPOINTS_OFFSET][2] = {
+            { 0.190761, -0.272205 },
+            {-0.553049, -0.460926 },
+            { 0.100335,-14.172222 },
+            {14.172222, -0.100335 }
+        };
+
+psF64 setOffsetResult[TESTPOINTS_OFFSET][2] = {
+            //XXX: Eugene says his values are correct, so i'm changing these values to the output.
+            //            { 0.25,  0.75 },
+            //            { 0.20,  0.80 },
+            //            {-0.10,  1.50 },
+            //            { 0.00,  0.00 }
+            { 0.68702,   0.230294},
+            { -0.966388,  0.0608434 },
+            {0.10,  -1.50 },
+            { 3.00141, -0.0140538  }
+        };
+
+psF64 getOffsetTestpoint1[TESTPOINTS_OFFSET][2] = {
+            { 0.00,  0.00 },
+            {-0.25,  0.50 },
+            { 0.50, -0.25 },
+            { 0.25, -0.10 }
+        };
+
+psF64 getOffsetTestpoint2[TESTPOINTS_OFFSET][2] = {
+            { 0.75,  0.25 },
+            { 0.40, -0.60 },
+            { 1.50,  0.50 },
+            { 0.10,  0.75 }
+        };
+
+psF64 getOffsetResult[TESTPOINTS_OFFSET][2] = {
+            //XXX: Eugene says his values are correct, so i'm changing these values to the output.
+            //            { -0.931596, -0.348976 },
+            //            { -1.632830,  2.649629 },
+            //            { -2.166795, -1.707211 },
+            //            {  0.167752, -1.151351 }
+            { 0.931596, 0.348976 },
+            { 1.632830,  -2.649629 },
+            { 2.166795, 1.707211 },
+            {  -0.167752, 1.151351 }
+        };
+
+
+psS32 main( psS32 argc, char *argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(87);
+
+    // psProjectionAlloc()
+    {
+        psMemId id = psMemGetId();
+        psProjection *myProjection = psProjectionAlloc(1.1, 2.2, 3.3, 4.4, PS_PROJ_AIT);
+        ok(myProjection != NULL, "psProjectionAlloc() returned non-NULL");
+        ok(myProjection->R == 1.1, "psProjection->R set correctly");
+        ok(myProjection->D == 2.2, "psProjection->D set correctly");
+        ok(myProjection->Xs == 3.3, "psProjection->Xs set correctly");
+        ok(myProjection->Ys == 4.4, "psProjection->Ys set correctly");
+        ok(myProjection->type == PS_PROJ_AIT, "psProjection->type set correctly");
+        psFree(myProjection);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testProjectTan()
+    // We do a psProject() then a psDeproject() on several data points and
+    // verify that we produce the original data point.
+    // XXX: This test currently fails.
+    {
+        psMemId id = psMemGetId();
+        psSphere *in = psSphereAlloc();
+        psSphere *inTest = NULL;
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
+
+        // Perform projecton on various test points
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            bool errorFlag = false;
+            in->r = projectionTestPoint[i][0];
+            in->d = projectionTestPoint[i][1];
+            myProjection->R = projectionTestPoint[i][2];
+            myProjection->D = projectionTestPoint[i][3];
+            myProjection->Xs = projectionTestPoint[i][4];
+            myProjection->Ys = projectionTestPoint[i][5];
+
+            psPlane *out = psProject(NULL, in, myProjection);
+            if(out == NULL) {
+                diag("psProject() returned NULL");
+                errorFlag = true;
+            } else {
+                inTest = psDeproject(NULL, out, myProjection);
+                if(fabs(out->x - projectionTanExpected[i][0]) > ERROR_TOL) {
+                    diag("TEST ERROR: Testpoint %d  psPlane->x = %lg  expected %lg",
+                         i,out->x, projectionTanExpected[i][0]);
+                    errorFlag = true;
+                }
+                if(fabs(out->y - projectionTanExpected[i][1]) > ERROR_TOL) {
+                    diag("TEST ERROR: Testpoint % d psPlane->y = %lg  expected %lg",
+                         i,out->y, projectionTanExpected[i][1]);
+                    errorFlag = true;
+                }
+
+                // Verify output is as expected
+                if(fabs(in->r - inTest->r) > ERROR_TOL) {
+                    diag("TEST ERROR: (in->r, inTest->r) (%.2f %.2f)\n", in->r, inTest->r);
+                    errorFlag = true;
+                }
+                if(fabs(in->d - inTest->d) > ERROR_TOL) {
+                    diag("TEST ERROR: (in->d, inTest->d) (%.2f %.2f)\n", in->d, inTest->d);
+                    errorFlag = true;
+                }
+            }
+            ok(!errorFlag, "psProject()/psDeproject() successful for test point %d\n", i);
+            psFree(inTest);
+            psFree(out);
+        }
+
+        psFree(in);
+        psFree(myProjection);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testDeprojectTan()
+    {
+        psMemId id = psMemGetId();
+        psPlane *in = psPlaneAlloc();
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
+
+        // Perform deprojection on various test points
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            bool errorFlag = false;
+            // Initialize input and projection members
+            in->x = projectionTanExpected[i][0];
+            in->y = projectionTanExpected[i][1];
+            myProjection->R = projectionTestPoint[i][2];
+            myProjection->D = projectionTestPoint[i][3];
+            myProjection->Xs = projectionTestPoint[i][4];
+            myProjection->Ys = projectionTestPoint[i][5];
+
+            // Perform deprojection
+            psSphere *out = psDeproject(NULL, in, myProjection);
+
+            // Verify output is not NULL
+            if(out == NULL) {
+                diag("psDeproject() returned NULL");
+                errorFlag = true;
+            } else {
+                // Verify output is as expected
+                if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
+                    diag("Testpoint %d  psSphere->r = %lg  expected %lg",
+                         i,out->r,projectionTestPoint[i][0]);
+                    errorFlag = true;
+                }
+                if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
+                    diag("Testpoint %d  psSphere->d = %lg expected %lg",
+                         i, out->d, projectionTestPoint[i][1]);
+                    errorFlag = true;
+                }
+            }
+            psFree(out);
+            ok(!errorFlag, "psProject()/psDeproject() successful for test point %d\n", i);
+        }
+
+        psFree(in);
+        psFree(myProjection);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testProjectSin()
+    {
+        psMemId id = psMemGetId();
+        psSphere *in = psSphereAlloc();
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_SIN);
+
+        // Perform projecton on various test points
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            bool errorFlag = false;
+            // Initialize input and project members
+            in->r = projectionTestPoint[i][0];
+            in->d = projectionTestPoint[i][1];
+            myProjection->R = projectionTestPoint[i][2];
+            myProjection->D = projectionTestPoint[i][3];
+            myProjection->Xs = projectionTestPoint[i][4];
+            myProjection->Ys = projectionTestPoint[i][5];
+
+            // Perform projection
+            psPlane *out = psProject(NULL, in, myProjection);
+
+            // Verify output not NULL
+            if(out == NULL) {
+                diag("Return null not expected");
+                errorFlag = true;
+            } else {
+                // Verify output is as expected
+                if(fabs(out->x - projectionSinExpected[i][0]) > ERROR_TOL) {
+                    diag("Testpoint %d  psPlane->x = %lg  expected %lg",
+                         i,out->x, projectionSinExpected[i][0]);
+                    errorFlag = true;
+                }
+                if(fabs(out->y - projectionSinExpected[i][1]) > ERROR_TOL) {
+                    diag("Testpoint % d  psPlane->y = %lg  expected %lg",
+                         i,out->y, projectionSinExpected[i][1]);
+                    errorFlag = true;
+                }
+                psFree(out);
+            }
+            ok(!errorFlag, "psProject()/psDeproject() successful for test point %d\n", i);
+        }
+
+        psFree(in);
+        psFree(myProjection);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testDeprojectSin()
+    {
+        psMemId id = psMemGetId();
+        psPlane *in = psPlaneAlloc();
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_SIN);
+
+        // Perform deprojection on various test points
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            bool errorFlag = false;
+            // Initialize input and projection members
+            in->x = projectionSinExpected[i][0];
+            in->y = projectionSinExpected[i][1];
+            myProjection->R = projectionTestPoint[i][2];
+            myProjection->D = projectionTestPoint[i][3];
+            myProjection->Xs = projectionTestPoint[i][4];
+            myProjection->Ys = projectionTestPoint[i][5];
+
+            // Perform deprojection
+            psSphere *out = psDeproject(NULL, in, myProjection);
+
+            // Verify output is not NULL
+            if(out == NULL) {
+                diag("Return null not expected");
+                errorFlag = true;
+            } else {
+                // Verify output is as expected
+                if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
+                    diag("Testpoint %d  psSphere->r = %lg  expected %lg",
+                         i,out->r,projectionTestPoint[i][0]);
+                    errorFlag = true;
+                }
+                if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
+                    diag("Testpoint %d  psSphere->d = %lg expected %lg",
+                         i, out->d, projectionTestPoint[i][1]);
+                    errorFlag = true;
+                }
+                psFree(out);
+            }
+            ok(!errorFlag, "psProject()/psDeproject() successful for test point %d\n", i);
+        }
+
+        psFree(in);
+        psFree(myProjection);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testProjectAit()
+    {
+        psMemId id = psMemGetId();
+        psSphere *in = psSphereAlloc();
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_AIT);
+
+        // Perform projecton on various test points
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            bool errorFlag = false;
+            // Initialize input and project members
+            in->r = projectionTestPoint[i][0];
+            in->d = projectionTestPoint[i][1];
+            myProjection->R = projectionTestPoint[i][2];
+            myProjection->D = projectionTestPoint[i][3];
+            myProjection->Xs = projectionTestPoint[i][4];
+            myProjection->Ys = projectionTestPoint[i][5];
+
+            // Perform projection
+            psPlane *out = psProject(NULL, in, myProjection);
+
+            // Verify output not NULL
+            if(out == NULL) {
+                diag("Return null not expected");
+                errorFlag = true;
+            } else {
+                // Verify output is as expected
+                if(fabs(out->x - projectionAitExpected[i][0]) > ERROR_TOL) {
+                    diag("Testpoint %d  psPlane->x = %lg  expected %lg",
+                         i,out->x, projectionAitExpected[i][0]);
+                    errorFlag = true;
+                }
+                if(fabs(out->y - projectionAitExpected[i][1]) > ERROR_TOL) {
+                    diag("Testpoint % d  psPlane->y = %lg  expected %lg",
+                         i,out->y, projectionAitExpected[i][1]);
+                    errorFlag = true;
+                }
+                psFree(out);
+            }
+            ok(!errorFlag, "psProject()/psDeproject() successful for test point %d\n", i);
+        }
+
+        psFree(in);
+        psFree(myProjection);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testDeprojectAit()
+    {
+        psMemId id = psMemGetId();
+        psPlane *in = psPlaneAlloc();
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_AIT);
+
+        // Perform deprojection on various test points
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            bool errorFlag = false;
+            // Initialize input and projection members
+            in->x = projectionAitExpected[i][0];
+            in->y = projectionAitExpected[i][1];
+            myProjection->R = projectionTestPoint[i][2];
+            myProjection->D = projectionTestPoint[i][3];
+            myProjection->Xs = projectionTestPoint[i][4];
+            myProjection->Ys = projectionTestPoint[i][5];
+
+            // Perform deprojection
+            psSphere *out = psDeproject(NULL, in, myProjection);
+
+            // Verify output is not NULL
+            if(out == NULL) {
+                diag("Return null not expected");
+                errorFlag = true;
+            } else {
+                // Verify output is as expected
+                if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
+                    diag("Testpoint %d  psSphere->r = %lg  expected %lg",
+                         i,out->r,projectionTestPoint[i][0]);
+                    errorFlag = true;
+                }
+                if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
+                    diag("Testpoint %d  psSphere->d = %lg expected %lg",
+                         i, out->d, projectionTestPoint[i][1]);
+                    errorFlag = true;
+                }
+                psFree(out);
+            }
+            ok(!errorFlag, "psProject()/psDeproject() successful for test point %d\n", i);
+        }
+
+        psFree(in);
+        psFree(myProjection);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testProjectPar()
+    {
+        psMemId id = psMemGetId();
+        psSphere *in = psSphereAlloc();
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_PAR);
+
+        // Perform projecton on various test points
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            bool errorFlag = false;
+            // Initialize input and project members
+            in->r = projectionTestPoint[i][0];
+            in->d = projectionTestPoint[i][1];
+            myProjection->R = projectionTestPoint[i][2];
+            myProjection->D = projectionTestPoint[i][3];
+            myProjection->Xs = projectionTestPoint[i][4];
+            myProjection->Ys = projectionTestPoint[i][5];
+
+            // Perform projection
+            psPlane *out = psProject(NULL, in, myProjection);
+
+            // Verify output not NULL
+            if(out == NULL) {
+                diag("Return null not expected");
+                errorFlag = true;
+            } else {
+                // Verify output is as expected
+                if(fabs(out->x - projectionParExpected[i][0]) > ERROR_TOL) {
+                    diag("Testpoint %d  psPlane->x = %lg  expected %lg",
+                         i,out->x, projectionParExpected[i][0]);
+                    errorFlag = true;
+                }
+                if(fabs(out->y - projectionParExpected[i][1]) > ERROR_TOL) {
+                    diag("Testpoint % d  psPlane->y = %lg  expected %lg",
+                         i,out->y, projectionParExpected[i][1]);
+                    errorFlag = true;
+                }
+                psFree(out);
+            }
+            ok(!errorFlag, "psProject()/psDeproject() successful for test point %d\n", i);
+        }
+
+        psFree(in);
+        psFree(myProjection);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testDeprojectPar()
+    {
+        psMemId id = psMemGetId();
+        psPlane *in = psPlaneAlloc();
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_PAR);
+
+        // Perform deprojection on various test points
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            bool errorFlag = false;
+            // Initialize input and projection members
+            in->x = projectionParExpected[i][0];
+            in->y = projectionParExpected[i][1];
+            myProjection->R = projectionTestPoint[i][2];
+            myProjection->D = projectionTestPoint[i][3];
+            myProjection->Xs = projectionTestPoint[i][4];
+            myProjection->Ys = projectionTestPoint[i][5];
+
+            // Perform deprojection
+            psSphere *out = psDeproject(NULL, in, myProjection);
+
+            // Verify output is not NULL
+            if(out == NULL) {
+                diag("Return null not expected");
+                errorFlag = true;
+            } else {
+                // Verify output is as expected
+                if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
+                    diag("Testpoint %d  psSphere->r = %lg  expected %lg",
+                         i,out->r,projectionTestPoint[i][0]);
+                    errorFlag = true;
+                }
+                if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
+                    diag("Testpoint %d  psSphere->d = %lg expected %lg",
+                         i, out->d, projectionTestPoint[i][1]);
+                    errorFlag = true;
+                }
+                psFree(out);
+            }
+            ok(!errorFlag, "psProject()/psDeproject() successful for test point %d\n", i);
+        }
+
+        psFree(in);
+        psFree(myProjection);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psProject(): Invoke function with null coordinate argument
+    // Following should generate an error message for null coord arg
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
+        psPlane *out = psProject(NULL, NULL, myProjection);
+        ok(out == NULL, "psProject(NULL, NULL, xxx) returned NULL");
+        psFree(myProjection);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Invoke function with null projection argument
+    // Following should generate an error message for null projection arg
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *in = psSphereAlloc();
+        psPlane *out = psProject(NULL, in, NULL);
+        ok(out == NULL, "psProject(NULL, in, NULL) returned NULL");
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Invoke function with unallowed projection type
+    // Following should generate an error message for unallowed projection type
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
+        myProjection->type = PS_PROJ_NTYPE;
+        psSphere *in = psSphereAlloc();
+        psPlane *out = psProject(NULL, in,myProjection);
+        ok(out == NULL, "psProject(NULL, in, out) returned NULL with unallowed projection type");
+        psFree(myProjection);
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testDeprojectFail()
+    psMemId id = psMemGetId();
+
+
+    // Invoke function with null coordinate argument
+    // Following should generate an error message for null coord arg
+    // XXX: We do not test the error generation
+    {
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
+        psSphere *out = psDeproject(NULL, NULL, myProjection);
+        ok(out == NULL, "psDeproject(NULL, NULL, myProjection) returned NULL");
+        psFree(myProjection);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke function with null projection argument
+    // Following should generate an error message for null projection arg
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psPlane *in = psPlaneAlloc();
+        psSphere *out = psDeproject(NULL, in, NULL);
+        ok(out == NULL, "psDeproject(NULL, in, NULL) returned NULL");
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke function with unallowed projection type
+    // Following should generate an error message for unallowed projection type
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psProjection *myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
+        psPlane *in = psPlaneAlloc();
+        myProjection->type = PS_PROJ_NTYPE;
+        psSphere *out = psDeproject(NULL, in,myProjection);
+        ok(out == NULL, "psDeproject(NULL, in,myProjection) returned NULL with unallowed projection type");
+        psFree(myProjection);
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // testSetOffsetSphere()
+    {
+        psMemId id = psMemGetId();
+        psSphere *position1 = psSphereAlloc();
+        psSphere *position2 = NULL;
+        psSphere *offset = psSphereAlloc();
+        psSphere *tmpOffset = psSphereAlloc();
+        bool errorFlag = false;
+
+        // Initialize first position
+        position1->r = DEG_TO_RAD(90.0);
+        position1->d = DEG_TO_RAD(45.0);
+        position1->rErr = 0.0;
+        position1->dErr = 0.0;
+
+        //  Using various offset verify spherical offset
+        //  Use all the valid unit types
+        for (psF64 r = 0.0; r < 180.0; r += DEG_INC)
+        {
+            for (psF64 d = 0.0; d < 90.0; d += DEG_INC) {
+
+                offset->r = DEG_TO_RAD(r);
+                offset->d = DEG_TO_RAD(d);
+                offset->rErr = 0.0;
+                offset->dErr = 0.0;
+
+                position2 = psSphereSetOffset(position1, offset, PS_SPHERICAL, PS_RADIAN);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 1);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 2);
+                psFree(position2);
+
+                tmpOffset->r = RAD_TO_DEG(offset->r);
+                tmpOffset->d = RAD_TO_DEG(offset->d);
+                tmpOffset->rErr = 0.0;
+                tmpOffset->dErr = 0.0;
+                position2 = psSphereSetOffset(position1, tmpOffset, PS_SPHERICAL, PS_DEGREE);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 3);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 4);
+                psFree(position2);
+
+                tmpOffset->r = RAD_TO_MIN(offset->r);
+                tmpOffset->d = RAD_TO_MIN(offset->d);
+                tmpOffset->rErr = 0.0;
+                tmpOffset->dErr = 0.0;
+                position2 = psSphereSetOffset(position1, tmpOffset, PS_SPHERICAL, PS_ARCMIN);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 5);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 6);
+                psFree(position2);
+
+                tmpOffset->r = RAD_TO_SEC(offset->r);
+                tmpOffset->d = RAD_TO_SEC(offset->d);
+                tmpOffset->rErr = 0.0;
+                tmpOffset->dErr = 0.0;
+                position2 = psSphereSetOffset(position1, tmpOffset, PS_SPHERICAL, PS_ARCSEC);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 7);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 8);
+                psFree(position2);
+            }
+        }
+        ok(!errorFlag, "psSphereSetOffset() successful on several dat points");
+
+        psFree(position1);
+        psFree(offset);
+        psFree(tmpOffset);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to call function with null input coordinate
+    // Following should generate error message null input coord
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *offset = psSphereAlloc();
+        psSphere *position2 = psSphereSetOffset(NULL, offset, PS_LINEAR, PS_ARCSEC);
+        ok(position2 == NULL, "psSphereSetOffset() returned NULL with NULL input coordinate");
+        psFree(position2);
+        psFree(offset);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to call function with null offset
+    // Following should generate error message null offset
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *position1 = psSphereAlloc();
+        psSphere *position2 = psSphereSetOffset(position1, NULL, PS_LINEAR, PS_ARCSEC);
+        ok(position2 == NULL, "psSphereSetOffset() returned NULL with NULL offset");
+        psFree(position1);
+        psFree(position2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Attempt to call function with unallowed mode
+    // Following should generate error message unallowed mode
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *position1 = psSphereAlloc();
+        psSphere *offset = psSphereAlloc();
+        psSphere *position2 = psSphereSetOffset(position1, offset, 0x54321, 0);
+        ok(position2 == NULL, "psSphereSetOffset() returned NULL with unallowed mode");
+        psFree(position1);
+        psFree(position2);
+        psFree(offset);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to call function with unallowed unit
+    // Following should generate an error message unallowed unit
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *position1 = psSphereAlloc();
+        psSphere *offset = psSphereAlloc();
+        psSphere *position2 = psSphereSetOffset(position1, offset, PS_SPHERICAL, 0x54321);
+        ok(position2 == NULL, "psSphereSetOffset() returned NULL with unallowed unit");
+        psFree(position1);
+        psFree(position2);
+        psFree(offset);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testSetOffsetLinear()
+    {
+        psMemId id = psMemGetId();
+        psSphere *coord = psSphereAlloc();
+        psSphere *offset = psSphereAlloc();
+        psSphere *out = NULL;
+        bool errorFlag = false;
+
+        for(psS32 i = 0; i < TESTPOINTS_OFFSET; i++)
+        {
+            bool errorFlag = false;
+            coord->r = setOffsetTestpoint[i][0];
+            coord->d = setOffsetTestpoint[i][1];
+
+            offset->r = setOffsetOffset[i][0];
+            offset->d = setOffsetOffset[i][1];
+
+            out = psSphereSetOffset(coord, offset, PS_LINEAR, PS_RADIAN);
+
+            if(fabs(out->r - setOffsetResult[i][0]) > ERROR_TOL) {
+                diag("Testpoint %d: Result out->r = %lg not equal to expected %lg",
+                     i, out->r, setOffsetResult[i][0]);
+                errorFlag = true;
+            }
+            if(fabs(out->d - setOffsetResult[i][1]) > ERROR_TOL) {
+                diag("Testpoint %d: Result out->d = %lg not equal to expected %lg",
+                     i,out->d, setOffsetResult[i][1]);
+                errorFlag = true;
+            }
+
+            psFree(out);
+        }
+        psFree(coord);
+        psFree(offset);
+        ok(!errorFlag, "psSphereSetOffset(), linear, successful on several data points");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testGetOffsetSphere()
+    {
+        psMemId id = psMemGetId();
+        psSphere *position1 = psSphereAlloc();
+        psSphere *position2 = psSphereAlloc();
+        psSphere *offset = NULL;
+        bool errorFlag = false;
+        position1->r = DEG_TO_RAD(90.0);
+        position1->d = DEG_TO_RAD(45.0);
+        position1->rErr = 0.0;
+        position1->dErr = 0.0;
+
+        for (psF64 r = 0.0; r < 180.0;r += DEG_INC)
+        {
+            for (psF64 d = 0.0;d < 90.0; d += DEG_INC) {
+                position2->r = DEG_TO_RAD(r);
+                position2->d = DEG_TO_RAD(d);
+                position2->rErr = 0.0;
+                position2->dErr = 0.0;
+
+                offset = psSphereGetOffset( position1,  position2,
+                                            PS_SPHERICAL, PS_RADIAN);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 1);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 1);
+                psFree(offset);
+
+                offset = psSphereGetOffset( position1, position2,
+                                            PS_SPHERICAL, PS_DEGREE);
+                offset->r = DEG_TO_RAD(offset->r);
+                offset->d = DEG_TO_RAD(offset->d);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 2);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 3);
+                psFree(offset);
+
+                offset = psSphereGetOffset( position1,  position2,
+                                            PS_SPHERICAL, PS_ARCMIN);
+                offset->r = MIN_TO_RAD(offset->r);
+                offset->d = MIN_TO_RAD(offset->d);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 2);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 3);
+                psFree(offset);
+
+                offset = psSphereGetOffset( position1,  position2,
+                                            PS_SPHERICAL, PS_ARCSEC);
+                offset->r = SEC_TO_RAD(offset->r);
+                offset->d = SEC_TO_RAD(offset->d);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 2);
+                PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 3);
+                psFree(offset);
+            }
+        }
+        psFree(position1);
+        psFree(position2);
+        ok(!errorFlag, "psSphereSetOffset(), linear, successful on several data points");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to invoke function with null position 1 parameter
+    // Following should generate an error message for null position
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *position2 = psSphereAlloc();
+        psSphere *offset = psSphereGetOffset(NULL, position2, PS_LINEAR, 0);
+        ok(offset == NULL, "psSphereGetOffset() returned NULL with null position 1 parameter");
+        psFree(offset);
+        psFree(position2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to invoke function with null position 2 parameter
+    // Following should generate an error message for null position
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *position1 = psSphereAlloc();
+        psSphere *offset = psSphereGetOffset(position1, NULL, PS_LINEAR, 0);
+        ok(offset == NULL, "psSphereGetOffset() returned NULL with null position 2 parameter");
+        psFree(offset);
+        psFree(position1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to invoke function with unallowed mode
+    // Following should generate an error message for unallowed mode
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *position1 = psSphereAlloc();
+        psSphere *position2 = psSphereAlloc();
+        psSphere *offset = psSphereGetOffset(position1, position2, 0x54321, 0);
+        ok(offset == NULL, "psSphereGetOffset() returned NULL with unallowed mode");
+        psFree(offset);
+        psFree(position1);
+        psFree(position2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to invoke function with unallowed unit type
+    // Following should generate an error message for unallowed unit type
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *position1 = psSphereAlloc();
+        psSphere *position2 = psSphereAlloc();
+        psSphere *offset = psSphereGetOffset(position1, position2, PS_SPHERICAL, 0x54321);
+        ok(offset == NULL, "psSphereGetOffset() returned NULL with unallowed unit type");
+        psFree(offset);
+        psFree(position1);
+        psFree(position2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to invoke function with coordinate 1 declination value 90.0 degree
+    // Following should generate warning message
+    // XXX: We do not test the warning generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *position1 = psSphereAlloc();
+        psSphere *position2 = psSphereAlloc();
+        position1->d = DEG_TO_RAD(90.0);
+        psSphere *offset = psSphereGetOffset(position1, position2, PS_SPHERICAL, PS_RADIAN);
+        ok(offset == NULL, "psSphereGetOffset() returned NULL with coordinate 1 declination value 90.0 degree");
+        psFree(offset);
+        psFree(position1);
+        psFree(position2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to invoke function with coordinate 2 declination value 90.0 degree
+    // Following should generate warning message
+    // XXX: We do not test the warning generation
+    {
+        psMemId id = psMemGetId();
+        psSphere *position1 = psSphereAlloc();
+        psSphere *position2 = psSphereAlloc();
+        position1->d = DEG_TO_RAD(45.0);
+        position2->d = DEG_TO_RAD(90.0);
+        psSphere *offset = psSphereGetOffset(position1, position2, PS_SPHERICAL, PS_RADIAN);
+        ok(offset == NULL, "psSphereGetOffset() returned NULL with coordinate 2 declination value 90.0 degree");
+        psFree(offset);
+        psFree(position1);
+        psFree(position2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testGetOffsetLinear()
+    {
+        psMemId id = psMemGetId();
+        psSphere *coord1 = psSphereAlloc();
+        psSphere *coord2 = psSphereAlloc();
+        bool errorFlag = false;
+
+        for(psS32 i = 0; i < TESTPOINTS_OFFSET; i++)
+        {
+            bool errorFlag = false;
+            coord1->r = getOffsetTestpoint1[i][0];
+            coord1->d = getOffsetTestpoint1[i][1];
+            coord2->r = getOffsetTestpoint2[i][0];
+            coord2->d = getOffsetTestpoint2[i][1];
+            psSphere *out = psSphereGetOffset(coord1, coord2, PS_LINEAR, PS_RADIAN);
+            if (out == NULL) {
+                diag("TEST ERROR: psSphereGetOffset() returned NULL");
+                errorFlag = true;
+            } else {
+                if(fabs(out->r - getOffsetResult[i][0]) > ERROR_TOL) {
+                    diag("Testpoint %d: Result out->r = %lg not equal to expected %lg",
+                         i, out->r, getOffsetResult[i][0]);
+                    errorFlag = true;
+                }
+                if(fabs(out->d - getOffsetResult[i][1]) > ERROR_TOL) {
+                    diag("Testpoint %d: Result out->d = %lg not equal to expected %lg",
+                         i,out->d, getOffsetResult[i][1]);
+                    errorFlag = true;
+                }
+                psFree(out);
+            }
+        }
+        psFree(coord1);
+        psFree(coord2);
+        ok(!errorFlag, "psSphereGetOffset() worked on several data points");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    /* 
+        #define DEG_INC2 15.0
+        #define VERBOSE 0
+        // testProjectTanDeProjectTan()
+        // XXX: Get rid of this?
+        {
+            psMemId id = psMemGetId();
+            bool errorFlag = false;
+            //
+            // I'm not convinced that the p_psProject() and p_psDeproject() functions work
+            // correctly.  If we project a set of coordinates over a wide range of (R, D)
+            // values, then deproject them, the original (R, D) values are only produced
+            // when D is larger than 0.  This code demonstrates that.
+            //
+            psProjection *tmpProj = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+            psPlane planeCoord01;
+            psSphere skyCoord01;
+            psSphere skyCoord02;
+            for (psF32 R = -90.0 ; R <= 90.0 ; R+= DEG_INC2) {
+                for (psF32 D = -90.0 ; D <= 90.0 ; D+= DEG_INC2) {
+                    if ((fabs(R) != 90.0) && (fabs(D) != 90.0)) {
+                        skyCoord01.r = DEG_TO_RAD(R);
+                        skyCoord01.d = DEG_TO_RAD(D);
+                        p_psProject(&planeCoord01, &skyCoord01, tmpProj);
+                        p_psDeproject(&skyCoord02, &planeCoord01, tmpProj);
+                        if ((fabs(skyCoord01.r - skyCoord02.r) < FLT_EPSILON) &&
+                            (fabs(skyCoord01.d - skyCoord02.d) < FLT_EPSILON)) {
+                            if (VERBOSE) {
+                                printf("CORRECT: (%.2fr %.2fd) (%.2fr %.2fd) -> (%.2f %.2f) -> (%.2fr %.2fd)\n", R, D,
+                                       skyCoord01.r, skyCoord01.d,
+                                       planeCoord01.x, planeCoord01.y,
+                                       skyCoord02.r, skyCoord02.d);
+                            }
+                        } else {
+                            diag("TEST ERROR: (%.2fr %.2fd) (%.2fr %.2fd) -> (%.2f %.2f) -> (%.2fr %.2fd)\n", R, D,
+                                  skyCoord01.r, skyCoord01.d,
+                                  planeCoord01.x, planeCoord01.y,
+                                  skyCoord02.r, skyCoord02.d);
+                            errorFlag = true;
+                        }
+                    }
+                }
+            }
+            ok(!errorFlag, "p_psProject()/p_psDeproject() worked on several data points");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+    */
+}
+
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psCoord02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psCoord02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psCoord02.c	(revision 22322)
@@ -0,0 +1,551 @@
+/** @file  tst_psCoord02.c
+*
+*  @brief This file contains test code for:
+*
+*  @author GLG, MHPCC
+*
+*  XXX: These tests should probably be split among several files
+*
+*  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-06-05 01:10:22 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define PS_PERCENT_COMPARE(X, Y, PERCENT_FRACTION) (fabs((Y)-(X))/fabs(X) < (PERCENT_FRACTION))
+#define PS_GEN_RAN_FLOAT (-0.5 + ((psF32)rand()) / ((psF32) RAND_MAX))
+#define RANDOM_SEED 1995
+
+// These macros determine the number of x/y test points which will be x-formed.
+#define TST04_X_MIN 0.0
+#define TST04_X_MAX 10.0
+#define TST04_Y_MIN 0.0
+#define TST04_Y_MAX 10.0
+#define TST04_NUM 5.0
+// These macros define the max sizes of the various input transforms.
+#define TST04_T1_X_X 2
+#define TST04_T1_X_Y 3
+#define TST04_T1_Y_X 3
+#define TST04_T1_Y_Y 3
+#define TST04_T2_X_X 3
+#define TST04_T2_X_Y 3
+#define TST04_T2_Y_X 3
+#define TST04_T2_Y_Y 3
+
+/******************************************************************************
+tstTransforms(t1, t2, t3): this function generates a set of arbitrary x/y
+coordinates, then transforms them into output coordinates via the t3
+transform, as well as the t1 followed by the t2 transform.  It verifies the
+output coordinates are identical and returns TRUE if so.  Otherwise, it prints
+a diag message and returns FALSE.
+ *****************************************************************************/
+bool tstTransforms(psPlaneTransform *t1, psPlaneTransform *t2, psPlaneTransform *t3)
+{
+    bool testStatus = true;
+    psPlane *in = psPlaneAlloc();
+    in->xErr = 0.0;
+    in->yErr = 0.0;
+
+    for (in->x = TST04_X_MIN ; in->x <= TST04_X_MAX ; in->x+= (TST04_X_MAX-TST04_X_MIN) / TST04_NUM) {
+        for (in->y = TST04_Y_MIN ; in->y <= TST04_Y_MAX ; in->y+= (TST04_Y_MAX-TST04_Y_MIN) / TST04_NUM) {
+            // Apply the t1/t2 transforms individually to the in coords.
+            psPlane *mid = psPlaneTransformApply(NULL, t1, in);
+            if (mid == NULL) {
+                diag("TEST ERROR: intermediate psPlane coords are NULL.\n");
+                psFree(in);
+                return(false);
+            }
+
+            psPlane *outT1T2 = psPlaneTransformApply(NULL, t2, mid);
+            if (outT1T2 == NULL) {
+                diag("TEST ERROR: intermediate psPlane coords are NULL.\n");
+                psFree(mid);
+                return(false);
+            }
+
+            // Apply the t3 transforms individually to the in coords.
+            psPlane *outT3 = psPlaneTransformApply(NULL, t3, in);
+            if (outT3 == NULL) {
+                diag("TEST ERROR: intermediate psPlane coords are NULL.\n");
+                psFree(mid);
+                psFree(outT1T2);
+                return(false);
+            }
+
+            // Verify that the results are identical.
+            if (!PS_PERCENT_COMPARE(outT3->x, outT1T2->x, 0.01)) {
+                diag("TEST ERROR: x is %f, should be %f\n", outT3->x, outT1T2->x);
+                testStatus = false;
+            }
+            // Verify that the results are identical.
+            if (!PS_PERCENT_COMPARE(outT3->y, outT1T2->y, 0.01)) {
+                diag("TEST ERROR: y is %f, should be %f\n", outT3->y, outT1T2->y);
+                testStatus = false;
+            }
+            if (0) {
+                if (fabs(outT3->x - outT1T2->x) > FLT_EPSILON) {
+                    printf("TEST ERROR: x is %f, should be %f\n", outT3->x, outT1T2->x);
+                    testStatus = false;
+                    printf("(in->x, in->y) is (%f, %f)\n", in->x, in->y);
+                    printf("(mid->x, mid->y) is (%f, %f)\n", mid->x, mid->y);
+                    printf("(outT1T2->x, outT1T2->y) is (%f, %f)\n", outT1T2->x, outT1T2->y);
+                    printf("(outT3->x, outT3->y) is (%f, %f)\n", outT3->x, outT3->y);
+                    PS_POLY_PRINT_2D(t1->x);
+                    PS_POLY_PRINT_2D(t1->y);
+                    PS_POLY_PRINT_2D(t2->x);
+                    PS_POLY_PRINT_2D(t2->y);
+                    PS_POLY_PRINT_2D(t3->x);
+                    PS_POLY_PRINT_2D(t3->y);
+                }
+                if (fabs(outT3->y - outT1T2->y) > FLT_EPSILON) {
+                    printf("TEST ERROR: y is %f, should be %f\n", outT3->y, outT1T2->y);
+                    testStatus = false;
+                    printf("(in->x, in->y) is (%f, %f)\n", in->x, in->y);
+                    printf("(mid->x, mid->y) is (%f, %f)\n", mid->x, mid->y);
+                    printf("(outT1T2->x, outT1T2->y) is (%f, %f)\n", outT1T2->x, outT1T2->y);
+                    printf("(outT3->x, outT3->y) is (%f, %f)\n", outT3->x, outT3->y);
+                    PS_POLY_PRINT_2D(t1->x);
+                    PS_POLY_PRINT_2D(t1->y);
+                    PS_POLY_PRINT_2D(t2->x);
+                    PS_POLY_PRINT_2D(t2->y);
+                    PS_POLY_PRINT_2D(t3->x);
+                    PS_POLY_PRINT_2D(t3->y);
+                }
+            }
+            psFree(mid);
+            psFree(outT1T2);
+            psFree(outT3);
+        }
+    }
+
+    psFree(in);
+    return(testStatus);
+}
+
+/******************************************************************************
+setCoeffs(t1, t2): this function sets the coefficients of the t1 and t2
+transforms to somewhat arbitrary values.
+ *****************************************************************************/
+int setCoeffs(psPlaneTransform *t1, psPlaneTransform *t2)
+{
+    for (psS32 t1xx = 0 ; t1xx < t1->x->nX+1 ; t1xx++) {
+        for (psS32 t1xy = 0 ; t1xy < t1->x->nY+1 ; t1xy++) {
+            t1->x->coeff[t1xx][t1xy] = PS_GEN_RAN_FLOAT;
+        }
+    }
+
+    for (psS32 t1yx = 0 ; t1yx < t1->y->nX+1 ; t1yx++) {
+        for (psS32 t1yy = 0 ; t1yy < t1->y->nY+1 ; t1yy++) {
+            t1->y->coeff[t1yx][t1yy] = PS_GEN_RAN_FLOAT;
+        }
+    }
+    for (psS32 t2xx = 0 ; t2xx < t2->x->nX+1 ; t2xx++) {
+        for (psS32 t2xy = 0 ; t2xy < t2->x->nY+1 ; t2xy++) {
+            t2->x->coeff[t2xx][t2xy] = PS_GEN_RAN_FLOAT;
+        }
+    }
+    for (psS32 t2yx = 0 ; t2yx < t2->y->nX+1 ; t2yx++) {
+        for (psS32 t2yy = 0 ; t2yy < t2->y->nY+1 ; t2yy++) {
+            t2->y->coeff[t2yx][t2yy] = PS_GEN_RAN_FLOAT;
+        }
+    }
+    return(0);
+}
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(25);
+    srand(RANDOM_SEED);
+
+    // We test psPlaneTransformCombine() with a variety of input transforms
+    // as well as input coordinates
+    //
+    //    The strategy here is to generate a pair of transforms t1 and t2 with a wide
+    //    variety of sizes for the x/y components of the x/y transforms, then set the
+    //    coefficients of t1/t2 to arbitray values, then generate a new transform t3 via
+    //    psPlaneTransformCombine() function.  Then, for several arbitrary input plane
+    //    coordinates, we tarnsform them via the new transform t3, as well as
+    //    individually via t1 then t2, and verify that they produce identical output
+    //    coordinates.
+    //
+    {
+        psMemId id = psMemGetId();
+        bool errorFlag = false;
+        for (psS32 t1xx = 0 ; t1xx < TST04_T1_X_X ; t1xx++)
+        {
+            for (psS32 t1xy = 0 ; t1xy < TST04_T1_X_Y ; t1xy++) {
+                for (psS32 t1yx = 0 ; t1yx < TST04_T1_Y_X ; t1yx++) {
+                    for (psS32 t1yy = 0 ; t1yy < TST04_T1_Y_Y ; t1yy++) {
+                        for (psS32 t2xx = 0 ; t2xx < TST04_T2_X_X ; t2xx++) {
+                            for (psS32 t2xy = 0 ; t2xy < TST04_T2_X_Y ; t2xy++) {
+                                for (psS32 t2yx = 0 ; t2yx < TST04_T2_Y_X ; t2yx++) {
+                                    for (psS32 t2yy = 0 ; t2yy < TST04_T2_Y_Y ; t2yy++) {
+                                        //printf("(%d %d %d %d %d %d %d %d)\n", t1xx, t1xy, t1yx, t1yy, t2xx, t2xy, t2yx, t2yy);
+                                        psPlaneTransform *trans1 = psPlaneTransformAlloc(PS_MAX(t1xx, t1yx), PS_MAX(t1xy, t1yy));
+                                        psPlaneTransform *trans2 = psPlaneTransformAlloc(PS_MAX(t2xx, t2yx), PS_MAX(t2xy, t2yy));
+                                        psPlaneTransform *trans3 = NULL;
+                                        setCoeffs(trans1, trans2);
+                                        trans3 = psPlaneTransformCombine(NULL, trans1,
+                                                                         trans2, psRegionSet(NAN,NAN,NAN,NAN), 0);
+                                        //XXX: the last two parameters are bogus.  Needs to change.  -rdd
+
+                                        if (trans3 == NULL) {
+                                            diag("TEST ERROR: psPlaneTransformCombine() returned NULL");
+                                            errorFlag = true;
+                                        } else {
+                                            bool testStatus = tstTransforms(trans1, trans2, trans3);
+                                            if (!testStatus) {
+                                                diag("TEST ERROR: psPlaneTransformCombine() did not produce correct results");
+                                                errorFlag = true;
+                                            }
+                                        }
+                                        psFree(trans1);
+                                        psFree(trans2);
+                                        psFree(trans3);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        ok(!errorFlag, "psPlaneTransformCombine() successful on a variety of transforms and data points");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling psPlaneTransformCombine with NULL trans1
+    // Should generate error and return NULL
+    // XXX: We do not test error generation
+    {
+        psMemId id = psMemGetId();
+        psPlaneTransform *trans2 = psPlaneTransformAlloc(2, 2);
+        psPlaneTransform *trans3 = psPlaneTransformCombine(NULL, NULL, trans2, psRegionSet(0,0,0,0), 10);
+        ok(trans3 == NULL, "psPlaneTransformCombine() returned NULL with NULL trans1 input");
+        psFree(trans2);
+        psFree(trans3);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Calling psPlaneTransformCombine with NULL trans2
+    // Should generate error and return NULL.\n");
+    // XXX: We do not test error generation
+    {
+        psMemId id = psMemGetId();
+        psPlaneTransform *trans1 = psPlaneTransformAlloc(2, 2);
+        psPlaneTransform *trans3 = psPlaneTransformCombine(NULL, trans1, NULL, psRegionSet(0,0,0,0), 10);
+        ok(trans3 == NULL, "psPlaneTransformCombine() returned NULL with NULL trans2 input");
+        psFree(trans1);
+        psFree(trans3);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // XXX: Clean this, put it elsewhere
+    #define TST05_X_MIN 0.0
+    #define TST05_X_MAX 10.0
+    #define TST05_Y_MIN 0.0
+    #define TST05_Y_MAX 10.0
+    #define TST05_NUM_X 5.0
+    #define TST05_NUM_Y 5.0
+    #define TST05_X_POLY_ORDER 3
+    #define TST05_Y_POLY_ORDER 3
+    #define TST05_NUM_DATA (TST05_NUM_X * TST05_NUM_Y)
+    #define TST05_IGNORE 1
+    #define TST05_VERBOSE 0
+
+    // Calling psPlaneTransformFit with NULL trans
+    // Should generate error and return NULL
+    // XXX: We do not test error generation
+    {
+        psArray *src = psArrayAlloc((int) TST05_NUM_DATA);
+        psArray *dst = psArrayAlloc((int) TST05_NUM_DATA);
+        psMemId id = psMemGetId();
+        ok(!psPlaneTransformFit(NULL, src, dst, 100, 100.0), "psPlaneTransformFit() returned FALSE with NULL trans");
+        psFree(src);
+        psFree(dst);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling psPlaneTransformFit with NULL src psArray
+    // Should generate error and return NULL
+    // XXX: We do not test error generation
+    {
+        psMemId id = psMemGetId();
+        psPlaneTransform *trans = psPlaneTransformAlloc(TST05_X_POLY_ORDER, TST05_Y_POLY_ORDER);
+        psArray *dst = psArrayAlloc((int) TST05_NUM_DATA);
+        ok(!psPlaneTransformFit(trans, NULL, dst, 100, 100.0), "psPlaneTransformFit() returned FALSE with NULL src psArray");
+        psFree(trans);
+        psFree(dst);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling psPlaneTransformFit with NULL dst psArray
+    // Should generate error and return NULL
+    // XXX: We do not test error generation
+    {
+        psMemId id = psMemGetId();
+        psPlaneTransform *trans = psPlaneTransformAlloc(TST05_X_POLY_ORDER, TST05_Y_POLY_ORDER);
+        psArray *src = psArrayAlloc((int) TST05_NUM_DATA);
+        ok(!psPlaneTransformFit(trans, src, NULL, 100, 100.0), "psPlaneTransformFit() returned FALSE with NULL dst psArray");
+        psFree(trans);
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psPlaneTransformFit()
+    // XXX: This is only a rudimentary test of the psPlaneTransformFit() function.
+    //      It tests some simple linear transformations.
+    {
+        psMemId id = psMemGetId();
+        psPlaneTransform *trans = psPlaneTransformAlloc(TST05_X_POLY_ORDER, TST05_Y_POLY_ORDER);
+        psPlaneTransform *transInit = psPlaneTransformAlloc(TST05_X_POLY_ORDER, TST05_Y_POLY_ORDER);
+        psArray *src = psArrayAlloc((int) TST05_NUM_DATA);
+        psArray *dst = psArrayAlloc((int) TST05_NUM_DATA);
+        bool rc;
+
+        // We set an arbitrary non-linear transformation.
+        for (psS32 x = 0 ; x < TST05_X_POLY_ORDER+1 ; x++)
+        {
+            for (psS32 y = 0 ; y < TST05_Y_POLY_ORDER+1 ; y++) {
+                transInit->x->coeff[x][y] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+                transInit->y->coeff[x][y] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+            }
+        }
+        if (TST05_VERBOSE)
+        {
+            PS_POLY_PRINT_2D(transInit->x);
+            PS_POLY_PRINT_2D(transInit->y);
+        }
+
+        // We generate a grid of input data points in the x1,y1 plane, calculate the
+        // corresponding values in the transformed plane, and set these to
+        // the src and dst psVectors.
+        psS32 i = 0;
+        for (psF32 x = TST05_X_MIN ; x < TST05_X_MAX ; x+= (TST05_X_MAX-TST05_X_MIN)/TST05_NUM_X)
+        {
+            for (psF32 y = TST05_Y_MIN ; y < TST05_Y_MAX ; y+= (TST05_Y_MAX-TST05_Y_MIN)/TST05_NUM_Y) {
+                if (i == src->n) {
+                    if (src->n == src->nalloc) {
+                        src = psArrayRealloc(src, src->n+1);
+                    }
+                    src->n++;
+                }
+                if (i == dst->n) {
+                    if (dst->n == dst->nalloc) {
+                        dst = psArrayRealloc(dst, dst->n+1);
+                    }
+                    dst->n++;
+                }
+                src->data[i] = (psPtr *) psPlaneAlloc();
+                ((psPlane *) src->data[i])->x = x;
+                ((psPlane *) src->data[i])->y = y;
+
+                dst->data[i] = psPlaneTransformApply(NULL, transInit, ((psPlane *) src->data[i]));
+                i++;
+            }
+        }
+
+        // Call psPlaneTransformFit with above data arrays
+        rc = psPlaneTransformFit(trans, src, dst, 100, 100.0);
+        ok(rc, "psPlaneTransformFit() returned TRUE");
+        skip_start(!rc, 1, "Skipping tests because psPlaneTransformFit() returned FALSE");
+        if (TST05_VERBOSE)
+        {
+            PS_POLY_PRINT_2D(trans->x);
+            PS_POLY_PRINT_2D(trans->y);
+        }
+
+        // For the initial grid of input points, we transform them to output points with
+        // the derived transformation, and verify that they are within 10%.
+        bool errorFlag = false;
+        for (psS32 i = TST05_IGNORE ; i < src->n-TST05_IGNORE ; i++)
+        {
+            psPlane *inData = (psPlane *) src->data[i];
+            psPlane *outData = (psPlane *) dst->data[i];
+            psPlane *outDataDeriv = psPlaneTransformApply(NULL, trans, inData);
+            if (!PS_PERCENT_COMPARE(outDataDeriv->x, outData->x, 0.20) ||
+                    !PS_PERCENT_COMPARE(outDataDeriv->y, outData->y, 0.20)) {
+                diag("TEST ERROR: the derived output coords (%d) were (%.2f, %.2f) should have been (%.2f, %.2f).\n",
+                     i, outDataDeriv->x, outDataDeriv->y, outData->x, outData->y);
+                errorFlag = true;
+            } else if (TST05_VERBOSE) {
+                diag("GOOD: the derived output coords (%d) were (%.2f, %.2f) should have been (%.2f, %.2f).\n",
+                     i, outDataDeriv->x, outDataDeriv->y, outData->x, outData->y);
+            }
+            psFree(outDataDeriv);
+        }
+        ok(!errorFlag, "The derived transformation was correct");
+        skip_end();
+        psFree(transInit);
+        psFree(trans);
+        psFree(src);
+        psFree(dst);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psPlaneTransformInvert()
+    // Calling psPlaneTransformInvert with NULL trans
+    // Should generate error and return NULL
+    // XXX: We do not test error generation
+    {
+        psMemId id = psMemGetId();
+        psRegion myRegion = psRegionSet(1.0, 5.0, 1.0, 5.0);
+        psPlaneTransform *transInverse = psPlaneTransformInvert(NULL, NULL, myRegion, 10);
+        ok(transInverse == NULL, "psPlaneTransformInvert() returned NULL with NULL trans");
+        psFree(transInverse);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psPlaneTransformInvert()
+    // Calling psPlaneTransformInvert with zero nSamples
+    // Should generate error and return NULL
+    // XXX: We do not test error generation
+    {
+        psMemId id = psMemGetId();
+        psPlaneTransform *trans = psPlaneTransformAlloc(1, 1);
+        psRegion myRegion = psRegionSet(1.0, 5.0, 1.0, 5.0);
+        psPlaneTransform *transInverse = psPlaneTransformInvert(NULL, trans, myRegion, 0);
+        ok(transInverse == NULL, "psPlaneTransformInvert() returned NULL with zero nSamples");
+        psFree(trans);
+        psFree(transInverse);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    #define NUM_TRANSFORMS 100
+    // psPlaneTransformInvert()
+    // This is only a rudimentary test of the psPlaneTransformInvert() function.
+    // It tests: Several random linear transformations.
+    // XXX: Must extensively test non-linear transformations.
+    {
+        psMemId id = psMemGetId();
+        bool errorFlag = false;
+        psPlaneTransform *trans = psPlaneTransformAlloc(1, 1);
+        psPlaneTransform *transInverse = NULL;
+        psRegion myRegion = psRegionSet(1.0, 5.0, 1.0, 5.0);
+
+        trans->x->coeff[0][0] = 0.0;
+        trans->x->coeff[0][1] = 1.0;
+        trans->x->coeff[1][0] = 2.0;
+        trans->x->coeff[1][1] = 3.0;
+        trans->y->coeff[0][0] = 4.0;
+        trans->y->coeff[0][1] = 5.0;
+        trans->y->coeff[1][0] = 6.0;
+        trans->y->coeff[1][1] = 7.0;
+
+        // We calling psPlaneTransformInvert with acceptable linear transformations
+        for (psS32 n = 0 ; n < NUM_TRANSFORMS ; n++)
+        {
+            if (n == 0) {
+                // I ensure that we test the identity transformation since this was
+                // giving us probs before.
+                trans->x->coeff[0][0] = 0.0;
+                trans->x->coeff[0][1] = 0.0;
+                trans->x->coeff[1][0] = 1.0;
+                trans->x->coeff[1][1] = 0.0;
+                trans->y->coeff[0][0] = 0.0;
+                trans->y->coeff[0][1] = 1.0;
+                trans->y->coeff[1][0] = 0.0;
+                trans->y->coeff[1][1] = 0.0;
+            } else {
+                // We create a random linear transformation and hope it is invertible.
+                trans->x->coeff[0][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+                trans->x->coeff[0][1] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+                trans->x->coeff[1][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+                trans->x->coeff[1][1] = 0.0;
+                trans->y->coeff[0][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+                trans->y->coeff[0][1] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+                trans->y->coeff[1][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+                trans->y->coeff[1][1] = 0.0;
+            }
+
+            transInverse = psPlaneTransformInvert(NULL, trans, myRegion, 10);
+            if (transInverse == NULL) {
+                diag("TEST ERROR: psPlaneTransformInvert() returned a NULL psPlaneTransform.\n");
+                errorFlag = true;
+            } else {
+                // Transform the "in" coords to "mid" with psPlaneTransform "trans", then
+                // transform "mid" to "out" with psPlaneTransform "transInverse".
+                // XXX: Must loop on a few data points.
+                psPlane *in = psPlaneAlloc();
+                in->x = 2.0;
+                in->y = 3.0;
+                psPlane *mid = psPlaneTransformApply(NULL, trans, in);
+                psPlane *out = psPlaneTransformApply(NULL, transInverse, mid);
+
+                if (((fabs(in->x - out->x) > FLT_EPSILON)) ||
+                        ((fabs(in->y - out->y) > FLT_EPSILON)) ||
+                        isnan(out->x) ||
+                        isnan(out->y)) {
+                    diag("TEST ERROR: in coords were (%f, %f), out coords were (%f, %f).\n",
+                         in->x, in->y, out->x, out->y);
+                    errorFlag = true;
+                }
+                psFree(in);
+                psFree(mid);
+                psFree(out);
+            }
+            psFree(transInverse);
+        }
+        psFree(trans);
+        ok(!errorFlag, "psPlaneTransformInvert() successful on several transforms");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psPlaneTransformDeriv()
+    // Ensure psPlaneTransformDeriv() returns NULL for NULL input plane
+    // Following should generate error message
+    // XXX: un-NULL the first parameter since we test that above
+    // XXX: We do not test error generation
+    {
+        psMemId id = psMemGetId();
+        psPlaneTransform *trans = psPlaneTransformAlloc(1, 3);
+        psPlane *deriv = psPlaneTransformDeriv(NULL, trans, NULL);
+        ok(deriv == NULL, "psPlaneTransformDeriv(NULL, trans, NULL) returned NULL");
+        psFree(trans);
+        psFree(deriv);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psPlaneTransformDeriv()
+    // We test on a very simple transformation.
+    {
+        psMemId id = psMemGetId();
+        psPlane *coord = psPlaneAlloc();
+        psPlane *deriv = NULL;
+        psPlaneTransform *trans = NULL;
+        coord->x = 3.0;
+        coord->y = 1.0;
+        trans = psPlaneTransformAlloc(1, 3);
+        //Set Polynomials.  f(x) = x, f(y) = 0.5*y^2  -->  f'(x) = 1, f'(y) = y
+        //So for 1,1  -> f'(1) = 1, f'(1) = 1
+        trans->x->coeff[0][0] = 0.0;
+        trans->x->coeff[1][0] = 1.0;
+        trans->y->coeff[0][0] = 0.0;
+        trans->y->coeff[0][1] = 0.0;
+        trans->y->coeff[0][2] = 0.5;
+
+        deriv = psPlaneTransformDeriv(NULL, trans, coord);
+        ok(deriv->x == 1.0 && deriv->y == 1.0, "psPlaneTransformDeriv() returned the correct values");
+        psFree(trans);
+        psFree(deriv);
+        psFree(coord);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psEarthOrientation_corrections.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psEarthOrientation_corrections.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psEarthOrientation_corrections.c	(revision 22322)
@@ -0,0 +1,562 @@
+/**
+ *  C Implementation: tap_psEarthOrientation_corrections
+ *
+ * Description:  Tests for p_psEOCInit, p_psEOCFinalize, psAberration,
+ *                         psGravityDeflection, psEOC_PrecessionCorr,
+ *                         psEOC_PolarTideCorr, psEOC_NutationCorr,
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+static void testEOCInit(void);
+static void testAberration(void);
+static void testGravDef(void);
+static void testEOC_Corrs(void);
+
+#define MJD_1900  15021.0        // Modified Julian Day 1/1/1900 00:00:00
+#define MJD_2100  88069.0        // Modified Julian Day 1/1/2100 00:00:00
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(8);
+
+    // Initialize library internal structures
+    psLibInit("pslib.config");
+
+    testEOCInit();
+    testAberration();
+    testGravDef();
+    testEOC_Corrs();
+    //    testSphereOffsets();
+
+    // Cleanup library
+    psLibFinalize();
+
+    done();
+}
+
+void testEOCInit(void)
+{
+//    diag("  >>>Test 1:  p_psEOCInit ");
+    //Test for psEarthPoleAlloc
+    //Return properly allocated psEarthPole
+    /*    {
+            skip_start(  ep == NULL, 3,
+                         "Skipping 3 tests because psEarthPole is NULL!");
+            ep = psEarthPoleAlloc();
+            ok( ep != NULL,
+                "psEarthPoleAlloc:               return properly allocated psEarthPole.");
+            psFree(ep);
+            ep = NULL;
+        }
+    */
+
+
+    //Check for Memory leaks
+    {
+        checkMem();
+    }
+
+}
+
+void testAberration(void)
+{
+    // psAberration()
+    psSphere *apparent = NULL;
+    psSphere *empty = NULL;
+    psCube *actualCube = psCubeAlloc();
+    //values from After gravity deflection//
+    actualCube->x = -0.35961949760293604;
+    actualCube->y = 0.5555613950298085;
+    actualCube->z = 0.7496835020836093;
+    psSphere *actual = psCubeToSphere(actualCube);
+    psCube *cubeDir = psCubeAlloc();
+    cubeDir->x = 5148.713262821658;
+    cubeDir->y = -26945.04752348012;
+    cubeDir->z = -11682.787302030947;
+    cubeDir->x += -357.6031690489248;
+    cubeDir->y += 248.46429758174693;
+    cubeDir->z += 0.09694774143797581;
+    psSphere *direction = psCubeToSphere(cubeDir);
+    double speed = sqrt(cubeDir->x*cubeDir->x + cubeDir->y*cubeDir->y + cubeDir->z*cubeDir->z);
+    double c = 299792458.0; // Speed of light in vacuum (src:NIST) m/s
+    speed /= c;
+
+    //Tests for psAberration
+    //Return NULL for NULL actual sphere input
+    {
+        empty = psAberration(apparent, NULL, direction, speed);
+        ok( empty == NULL,
+            "psAberration:                   return NULL for NULL actual sphere input.");
+    }
+    //Return NULL for NULL direction sphere input
+    {
+        empty = psAberration(apparent, actual, NULL, speed);
+        ok( empty == NULL,
+            "psAberration:                   return NULL for NULL direction sphere input.");
+    }
+    //Return NULL for speed = 0.0
+    {
+        empty = psAberration(apparent, actual, direction, 0.0);
+        ok( empty == NULL,
+            "psAberration:                   return NULL for zero speed input.");
+    }
+    //Return NULL for speed > 1.0
+    {
+        empty = psAberration(apparent, actual, direction, 1.1);
+        ok( empty == NULL,
+            "psAberration:                   return NULL for impossible speed input (> c).");
+    }
+
+    //Return correct values for valid inputs
+    {
+        double x, y, z;
+        x = -0.35963388069046304;
+        y = 0.5555192509816625;
+        z = 0.7497078321908413;
+        apparent = psAberration(apparent, actual, direction, speed);
+        skip_start(  apparent == NULL, 3,
+                     "Skipping 3 tests because psEarthPole is NULL!");
+        psCube *outCube = psSphereToCube(apparent);
+        is_double_tol( outCube->x, x, 0.001,
+                       "psAberration:                   return correct sphere for valid inputs.");
+        is_double_tol( outCube->y, y, 0.001,
+                       "psAberration:                   return correct sphere for valid inputs.");
+        is_double_tol( outCube->z, z, 0.001,
+                       "psAberration:                   return correct sphere for valid inputs.");
+        psFree(outCube);
+        skip_end();
+    }
+
+    //Check for Memory leaks
+    {
+        psFree(actualCube);
+        psFree(cubeDir);
+        psFree(apparent);
+        psFree(actual);
+        psFree(direction);
+        checkMem();
+    }
+
+}
+
+void testGravDef(void)
+{
+    // psGravityDeflection
+    // Test for psGravityDeflection
+    // Return properly allocated psEarthPole
+    /* XXX: Fix this
+        psSphere *apparent = NULL;
+        psSphere *empty = NULL;
+        psCube *actualCube = psCubeAlloc();
+        actualCube->x = -0.3596195125758298;
+        actualCube->y = 0.5555613903455866;
+        actualCube->z = 0.7496834983724809;
+        psSphere *actual = psCubeToSphere(actualCube);
+        psCube *sunCube = psCubeAlloc();
+        sunCube->x = 1.467797790127511e11;
+        sunCube->y = 2.5880956908748722e10;
+        sunCube->z = 1.1220046291457653e10;
+        double sunLength = sqrt(sunCube->x*sunCube->x + sunCube->y*sunCube->y + sunCube->z*sunCube->z);
+        sunCube->x /= sunLength;
+        sunCube->y /= sunLength;
+        sunCube->z /= sunLength;
+        if (VERBOSE) {
+        printf("sunCube = x,y,z = %.13g, %.13g, %.13g\n",
+        sunCube->x, sunCube->y, sunCube->z);
+    }
+        psSphere *sun = psCubeToSphere(sunCube);
+        if (VERBOSE) {
+        printf("sunSphere  = r, d = %.13g, %.13g\n", sun->r, sun->d);
+    }
+        psCube *outCube = psCubeAlloc();
+     
+        empty = psGravityDeflection(apparent, empty, sun);
+        if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+        "psGravityDeflection Failed to return NULL for NULL actual input sphere.\n");
+        return 1;
+    }
+        empty = psGravityDeflection(apparent, actual, empty);
+        if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+        "psGravityDeflection Failed to return NULL for NULL sun input sphere.\n");
+        return 2;
+    }
+     
+        apparent = psGravityDeflection(NULL, actual, sun);
+        //    apparent->r *= -1.0;
+        //    apparent->d *= -1.0;
+        psSphere *result2;
+        //    psSphere *result2 = psSphereSetOffset(actual, apparent, PS_SPHERICAL, PS_RADIAN);
+        //    psSphere *result = psSphereSetOffset(actual, apparent, PS_SPHERICAL, PS_RADIAN);
+        //    psSphere *result = psSphereGetOffset(apparent, actual, PS_SPHERICAL, PS_RADIAN);
+        if (VERBOSE) {
+        printf(" -- actualCube = x,y,z = %.13g, %.13g, %.13g  -- \n",
+        actualCube->x, actualCube->y, actualCube->z);
+    }
+        //    psCube *outCube = psSphereToCube(result);
+        //    printf(" -- resultCube = x,y,z = %.13g, %.13g, %.13g  -- \n",
+        //           outCube->x, outCube->y, outCube->z);
+        psCube *outCube2 = psSphereToCube(apparent);
+        if (VERBOSE) {
+        printf(" -- resultCube2= x,y,z = %.13g, %.13g, %.13g  -- \n",
+        outCube2->x, outCube2->y, outCube2->z);
+    }
+        double x, y, z;
+        x = -0.35961949760293604;
+        y = 0.5555613950298085;
+        z = 0.7496835020836093;
+     
+        if (VERBOSE) {
+        printf(" -- expectCube = x,y,z = %.13g, %.13g, %.13g  -- \n\n", x, y, z);
+     
+            //    if ( fabs(x - outCube->x) > DBL_EPSILON || fabs(y - outCube->y) > DBL_EPSILON ||
+            //            fabs(z - outCube->z) > DBL_EPSILON ) {
+            //        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+            //                "psGravityDeflection returned incorrect values.\n");
+        printf("expect-actual=  x,y,z  = %.13g, %.13g, %.13g \n",
+        (x - actualCube->x), (y - actualCube->y), (z - actualCube->z) );
+        printf("expect-result=  x,y,z  = %.13g, %.13g, %.13g \n",
+        (x - outCube2->x), (y - outCube2->y), (z - outCube2->z) );
+        printf("result-actual=  x,y,z  = %.13g, %.13g, %.13g \n",
+        (outCube2->x - actualCube->x), (outCube2->y - actualCube->y),
+        (outCube2->z - actualCube->z) );
+    }
+        //        return 1;
+        //    }
+        //    psFree(result2);
+        outCube2->x = x;
+        outCube2->y = y;
+        outCube2->z = z;
+        result2 = psCubeToSphere(outCube2);
+        //    psFree(result);
+        psSphere *result = psSphereGetOffset(actual, result2, PS_SPHERICAL, PS_RADIAN);
+        psFree(result2);
+        result2 = psSphereGetOffset(actual, apparent, PS_SPHERICAL, PS_RADIAN);
+        if (VERBOSE) {
+        printf("The apparent output sphere = r,d = %.13g, %.13g\n", result2->r, result2->d);
+        printf("The expected output sphere = r,d = %.13g, %.13g\n\n", result->r, result->d);
+    }
+        psFree(result2);
+     
+        psFree(outCube);
+        psFree(outCube2);
+        psFree(sunCube);
+        psFree(actualCube);
+        psFree(result);
+        psFree(actual);
+        psFree(apparent);
+        psFree(sun);
+     
+     
+     
+        {
+            skip_start(  ep == NULL, 3,
+                         "Skipping 3 tests because psEarthPole is NULL!");
+            ep = psEarthPoleAlloc();
+            ok( ep != NULL,
+                "psEarthPoleAlloc:               return properly allocated psEarthPole.");
+            psFree(ep);
+            ep = NULL;
+        }
+    */
+
+
+    //Check for Memory leaks
+    {
+        checkMem();
+    }
+
+}
+
+void testEOC_Corrs(void)
+{
+    // psEOC Correction Functions");
+    //Tests for psEOC_PrecessionCorr
+    /* XXX: Fix this
+        psTime *empty = NULL;
+        psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+        time2->sec = timesec;
+        time2->nsec = 0;
+        time2->leapsecond = false;
+        //    time2 = psTimeConvert(time2, PS_TIME_TAI);
+     
+        //Tests for Precession Correction function//
+        //Return NULL for NULL time input
+        psEarthPole *pcorr = NULL;
+        // Following should generate error message
+        pcorr = psEOC_PrecessionCorr(empty, PS_IERS_A);
+        if (pcorr != NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "psEOC_PrecessionCorr failed to return NULL for NULL time input.\n");
+            return 5;
+        }
+     
+        //Return NULL for Invalid IERS table
+        // Following should generate error message
+        pcorr = psEOC_PrecessionCorr(time2, 3);
+        if (pcorr != NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "psEOC_PrecessionCorr failed to return NULL for incorrect IERS table.\n");
+            return 6;
+        }
+        psFree(pcorr);
+     
+        //Check values from IERS table A
+        pcorr = psEOC_PrecessionCorr(time2, PS_IERS_A);
+        if ( pcorr == NULL ) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                    "psEOC_PrecessionCorr returned NULL for valid inputs.\n");
+            return 7;
+        } else {
+            if (VERBOSE) {
+                printf("\nPrecessionCorr output (IERSA) = x,y,s = %.13g,  %.13g, %.13g\n",
+                       pcorr->x, pcorr->y, pcorr->s);
+            }
+        }
+        psFree(pcorr);
+     
+        //Check values from IERS table B
+        pcorr = psEOC_PrecessionCorr(time2, PS_IERS_B);
+        if ( pcorr == NULL ) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                    "psEOC_PrecessionCorr returned NULL for valid inputs.\n");
+            return 9;
+        } else {
+            double xx, yy, ss;
+            xx = 0.06295703125;
+            yy = -0.0287618408203125;
+            ss = 0.0;
+            xx = SEC_TO_RAD(xx) * 1e-3;
+            yy = SEC_TO_RAD(yy) * 1e-3;
+            //        if ( fabs(pcorr->x - xx) > DBL_EPSILON || fabs(pcorr->y - yy) > DBL_EPSILON
+            //                || fabs(pcorr->s - ss) > DBL_EPSILON) {
+            //            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+            //                    "   psEOC_PrecessionCorr return incorrect values.\n");
+            if (VERBOSE) {
+                printf("PrecessionCorr output (IERSB) = x,y,s = %.13g, %.13g, %.13g\n",
+                       pcorr->x, pcorr->y, pcorr->s);
+                printf("Expected output               = x,y,s = %.13g, %.13g, %.13g\n", xx, yy, ss);
+                printf("          A difference of:              %.13g,  %.13g, %.13g\n\n",
+                       (pcorr->x - xx), (pcorr->y - yy), (pcorr->s - ss) );
+            }
+            //            return 10;
+            //        }
+        }
+        //precess is the *actual* output from PrecessionModel + PrecessionCorr
+        psEarthPole *precess = psEOC_PrecessionModel(time2);
+        precess->x += pcorr->x;
+        precess->y += pcorr->y;
+        double xCorr, yCorr;
+        xCorr = 3.05224300720406e-10;
+        yCorr = -1.39441339235822e-10;
+        //pcorr is the *expected* output from PrecessionModel//
+        pcorr->x = 2.857175590089105e-4;
+        pcorr->y = 2.3968739377734732e-5;
+        pcorr->s = -1.3970066457904322e-8;
+        pcorr->x += xCorr;
+        pcorr->y += yCorr;
+        psSphereRot *precessNutInv = psSphereRot_CEOtoGCRS(precess);
+        psSphereRot *precessNut = psSphereRotConjugate(NULL, precessNutInv);
+        double q0, q1, q2;
+        q0 = -1.1984522406756289e-5;
+        q1 = 1.4285893358610674e-4;
+        q2 = 1.2191193518914336e-10;
+        psSphereRot *pni = psSphereRot_CEOtoGCRS(pcorr);
+        if (fabs(pni->q0-q0) > FLT_EPSILON || fabs(pni->q1-q1) > FLT_EPSILON ||
+                fabs(pni->q2-q2) > FLT_EPSILON ) {
+            printf("\n Error at CEOtoGCRS, output psSphereRot doesn't match expected.\n");
+        }
+        //    printf("  Output from CEOtoGCRS only  = %.13g,%.13g,%.13g,%.13g\n",
+        //           pni->q0, pni->q1, pni->q2, pni->q3);
+        if (VERBOSE) {
+            printf("  Expected sphere rotation    = %.13g, %.13g, %.13g\n", q0,q1,q2);
+            printf("  Result sphere rotation      = %.13g, %.13g, %.13g\n",
+                   precessNutInv->q0, precessNutInv->q1, precessNutInv->q2);
+            printf("     Difference         =        %.13g, %.13g, %.13g\n\n",
+                   precessNutInv->q0-q0, precessNutInv->q1-q1, precessNutInv->q2-q2);
+        }
+        psCube *objC = psCubeAlloc();
+        //    objC->x = -3.5963388069046304;
+        //    objC->y = 0.5555192509816625;
+        //    objC->z = 0.7497078321908413;
+        //    objSetup();
+     
+        //This is the sphere rotation for the *expected* precession output//
+        psSphereRot *pn = psSphereRotConjugate(NULL, pni);
+     
+        //    psSphere *sphere = psSphereAlloc();
+        //    *sphere = *obj;
+        //    psFree(obj);
+     
+        //create a psSphere for (from) the start position given in eoc_testing//
+        objC->x = -0.35963388069046304;
+        objC->y = 0.5555192509816625;
+        objC->z = 0.7497078321908413;
+        psSphere *sphere = psCubeToSphere(objC);
+     
+        psSphere *expect = psSphereRotApply(NULL, pn, sphere);
+        //expected results below - stored in:  sphere  //
+        double x,y,z;
+        x = -0.3598480726985338;
+        y = 0.5555012823608123;
+        z = 0.7496183628158023;
+        if (VERBOSE) {
+            printf("\n<<Expected out       = x,y,z = %.13g, %.13g, %.13g\n", x, y, z);
+        }
+        //    psFree(objC);
+        //    objC = psSphereToCube(expect);
+        //printf("<<Expected out (CEO)  = x,y,z = %.13g, %.13g, %.13g\n", objC->x, objC->y, objC->z);
+        //printf("     Difference     =           %.13g, %.13g, %.13g\n", objC->x-x, objC->y-y, objC->z-z);
+        //    x = objC->x;
+        //    y = objC->y;
+        //    z = objC->z;
+        psSphere *result = psSphereRotApply(NULL, precessNut, sphere);
+        psFree(objC);
+        objC = psSphereToCube(result);
+        double xx = greatCircle(result, expect);
+        if (VERBOSE) {
+            printf("<<Resulting out      = x,y,z = %.13g, %.13g, %.13g\n", objC->x, objC->y, objC->z);
+            printf("     Difference         =      %.13g, %.13g, %.13g\n\n",
+                   objC->x-x, objC->y-y, objC->z-z);
+            printf("GREAT CIRCLE DIFFERENCE = %.13g \n", xx);
+        }
+     
+        psFree(precess);
+        psFree(precessNut);
+        psFree(precessNutInv);
+        psFree(expect);
+        psFree(objC);
+     
+        psFree(sphere);
+        psFree(result);
+        psFree(pn);
+        psFree(pni);
+        psFree(pcorr);
+        psFree(time2);
+        if (!p_psEOCFinalize() ) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "EOC failed finalization!\n");
+            return 12;
+        }
+     
+    */
+
+    //Tests for psEOC_PolarTideCorr
+    /*
+        psTime *in = psTimeAlloc(PS_TIME_UTC);
+        in->sec = timesec;
+        in->nsec = 0;
+        in->leapsecond = false;
+        psTime *empty = NULL;
+        psEarthPole *eop = NULL;
+     
+        //Return NULL for NULL input time
+        // Following should generate error message
+        eop = psEOC_PolarTideCorr(empty);
+        if (eop != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+        "psEOC_PolarTideCorr failed to return NULL for NULL input time.\n");
+        return 1;
+    }
+     
+        eop = psEOC_PolarTideCorr(in);
+        if (eop == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+        "psEOC_PolarTideCorr returned NULL for valid input time.\n");
+        return 2;
+    } else {
+        if (VERBOSE) {
+        printf("\nPolarTideCorr output = x,y,s = %.13g, %.13g, %.13g\n",
+        eop->x, eop->y, eop->s);
+    }
+    }
+     
+        psFree(in);
+        psFree(eop);
+     
+    */
+
+
+
+    //Tests for psEOC_NutationCorr
+    /*
+        psTime *in = psTimeAlloc(PS_TIME_UTC);
+        in->sec = timesec;
+        in->nsec = 0;
+        in->leapsecond = false;
+        psTime *empty = NULL;
+        psEarthPole *nute = NULL;
+     
+        //Return NULL for NULL input time.
+        // Following should generate error message
+        nute = psEOC_NutationCorr(empty);
+        if (nute != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+        "psEOC_NutationCorr failed to return NULL for NULL input time.\n");
+        return 1;
+    }
+        //Return NULL for UT1 time input
+        *//*    psTime *UT1time = psTimeAlloc(PS_TIME_UT1);
+        nute = psEOC_NutationCorr(UT1time);
+        if (nute != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+        "psEOC_NutationCorr failed to return NULL for UT1 input time.\n");
+        return 2;
+    }
+        psFree(UT1time);
+        *//*
+        //Check return values from valid nutation time input
+        nute = psEOC_NutationCorr(in);
+        if ( nute == NULL ) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                    "psEOC_NutationCorr returned NULL for valid input.\n");
+            return 3;
+        } else {
+            if (VERBOSE) {
+                printf("Nutation Correction output = x,y,s = %.13g, %.13g, %.13g\n\n",
+                       nute->x, nute->y, nute->s);
+            }
+        }
+        psFree(nute);
+        psFree(in);
+     
+        if (!p_psEOCFinalize() ) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "EOC failed finalization!\n");
+            return 12;
+        }
+     
+    */
+    //Return properly allocated psEarthPole
+    /*
+     
+     
+        {
+            skip_start(  ep == NULL, 3,
+                         "Skipping 3 tests because psEarthPole is NULL!");
+            ep = psEarthPoleAlloc();
+            ok( ep != NULL,
+                "psEarthPoleAlloc:               return properly allocated psEarthPole.");
+            psFree(ep);
+            ep = NULL;
+        }
+    */
+
+
+    //Check for Memory leaks
+    {
+        checkMem();
+    }
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psEarthOrientation_motion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psEarthOrientation_motion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psEarthOrientation_motion.c	(revision 22322)
@@ -0,0 +1,654 @@
+/**
+ *  C Implementation: tap_psEarthOrientation_motion
+ *
+ * Description:  Tests for psEarthPoleAlloc, psEOC_PrecessionModel, psSpherePrecess
+ *                         psSphereRot_CEOtoGCRS, psSphereRot_TEOtoCEO,
+ *                         psEOC_GetPolarMotion, psSphereRot_ITRStoTEO
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+static void testPrecessionModel(void);
+static void testPolarMotion(void);
+static void testSphereRots(void);
+static void testSpherePrecess(void);
+#define MJD_1900  15021.0        // Modified Julian Day 1/1/1900 00:00:00
+#define MJD_2100  88069.0        // Modified Julian Day 1/1/2100 00:00:00
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(78);
+
+    // Tests for psEarthOrientation Motion Functions
+    // Initialize library internal structures
+    psLibInit("pslib.config");
+
+    testPrecessionModel();
+    testPolarMotion();
+    testSphereRots();
+    testSpherePrecess();
+
+    psLibFinalize();
+    done();
+}
+
+void testPrecessionModel(void)
+{
+    // psEOC_PrecessionModel
+
+    psEarthPole *ep = NULL;
+
+
+    //Test for psEarthPoleAlloc
+    //Return properly allocated psEarthPole
+    {
+        psMemId id = psMemGetId();
+        ep = psEarthPoleAlloc();
+        ok( ep != NULL, "psEarthPoleAlloc: return properly allocated psEarthPole.");
+        psFree(ep);
+        ep = NULL;
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Tests for psEOC_PrecessionModel
+    //Return NULL for NULL time input.
+    {
+        psMemId id = psMemGetId();
+        ep = psEOC_PrecessionModel(NULL);
+        ok( ep == NULL, "psEOC_PrecessionModel: return NULL for NULL time input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for UT1 time input.
+    {
+        psMemId id = psMemGetId();
+        psTime *testTime = psTimeAlloc(PS_TIME_UT1);
+        ep = psEOC_PrecessionModel(testTime);
+        ok( ep == NULL, "psEOC_PrecessionModel: return NULL for UT1 time input.");
+        psFree(testTime);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for invalid time input.
+    {
+        psMemId id = psMemGetId();
+        psTime *testTime = psTimeAlloc(PS_TIME_UTC);
+        testTime->nsec = 2e9;
+        ep = psEOC_PrecessionModel(testTime);
+        ok( ep == NULL,
+            "psEOC_PrecessionModel:          return NULL for invalid time input.");
+        testTime->nsec = 0;
+        psFree(testTime);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for failed eoc init - missing file
+    //printf("\n filename = '%s' \n", p_psGetConfigFileName());
+    //XXX: Use the PS_CONFIG_FILE_DEFAULT macro, or PS_CONFIG_FILE environment variable
+    //to define where the real pslib config is.
+    {
+        psMemId id = psMemGetId();
+        psTime *testTime = psTimeAlloc(PS_TIME_UTC);
+        testTime->sec = 1049160600;
+        testTime->nsec = 0;
+        testTime->leapsecond = false;
+        skip_start(rename("../../etc/pslib/pslib.config", "../../etc/pslib/pslib_config.bak"),
+                   1, "Skipping 1 tests because file rename failed!");
+        ep = psEOC_PrecessionModel(testTime);
+        ok( ep == NULL,
+            "psEOC_PrecessionModel:          return NULL for failed eoc init.");
+        rename("../../etc/pslib/pslib_config.bak", "../../etc/pslib/pslib.config");
+        skip_end();
+        psFree(testTime);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return valid EarthPole for valid time input
+    {
+        psMemId id = psMemGetId();
+        psTime *testTime = psTimeAlloc(PS_TIME_UTC);
+        double x, y, s;
+        x = 2.857175590089105e-4;
+        y = 2.3968739377734732e-5;
+        s = -1.3970066457904322e-8;
+        ep = psEOC_PrecessionModel(testTime);
+        skip_start(  ep == NULL, 3,
+                     "Skipping 3 tests because psEarthPole is NULL!");
+        is_double_tol(ep->x, x, 0.1,
+                      "psEOC_PrecessionModel:          return valid EarthPole for valid inputs (x).");
+        is_double_tol(ep->y, y, 0.1,
+                      "psEOC_PrecessionModel:          return valid EarthPole for valid inputs (y).");
+        is_double_tol(ep->s, s, 0.1,
+                      "psEOC_PrecessionModel:          return valid EarthPole for valid inputs (s).");
+        skip_end();
+        psFree(testTime);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    /*
+    //Tests for psMemCheckSphereRot
+    //Make sure psMemCheckSphereRot works correctly - return false
+    {
+    int j = 2;
+    ok( !psMemCheckSphereRot(&j),
+    "psMemCheckSphereRot:            return false for non-SphereRot input.");
+    skip_start(  s1 == NULL, 1,
+    "Skipping 1 tests because psSphereRot is NULL!");
+    skip_end();
+    }
+    */
+    //Check for Memory leaks
+    {
+        psFree(ep);
+        checkMem();
+    }
+
+}
+
+void testPolarMotion(void)
+{
+    // psEOC_GetPolarMotion()
+
+    psTime *in = psTimeAlloc(PS_TIME_UTC);
+    in->sec = 1049160600;
+    in->nsec = 0;
+    in->leapsecond = false;
+    psEarthPole *polarMotion = NULL;
+
+
+    //Tests for psEOC_GetPolarMotion
+    //Return NULL for NULL time input.
+    {
+        psMemId id = psMemGetId();
+        polarMotion = psEOC_GetPolarMotion(NULL, PS_IERS_B);
+        ok( polarMotion == NULL, "psEOC_GetPolarMotion: return NULL for NULL time input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for invalid IERS table input
+    {
+        psMemId id = psMemGetId();
+        polarMotion = psEOC_GetPolarMotion(NULL, PS_IERS_B+1);
+        ok( polarMotion == NULL, "psEOC_GetPolarMotion: return NULL for invalid IERS table input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for invalid time input.
+    {
+        psMemId id = psMemGetId();
+        in->nsec = 2e9;
+        polarMotion = psEOC_GetPolarMotion(in, PS_IERS_B);
+        ok( polarMotion == NULL, "psEOC_GetPolarMotion: return NULL for invalid time input.");
+        in->nsec = 0;
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for failed eoc init - missing file
+    //XXX: Use the PS_CONFIG_FILE_DEFAULT macro, or PS_CONFIG_FILE environment variable
+    //to define where the real pslib config is.
+    {
+        psMemId id = psMemGetId();
+        p_psEOCFinalize();
+        skip_start(rename("../../etc/pslib/pslib.config", "../../etc/pslib/pslib_config.bak"),
+                   1, "Skipping 1 tests because file rename failed!");
+        polarMotion = psEOC_GetPolarMotion(in, PS_IERS_B);
+        ok( polarMotion == NULL, "psEOC_GetPolarMotion: return NULL for failed eoc init.");
+        rename("../../etc/pslib/pslib_config.bak", "../../etc/pslib/pslib.config");
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return valid EarthPole for valid time input - IERS B
+    {
+        psMemId id = psMemGetId();
+        double x, y, s;
+        x = -6.454389659777e-07;
+        y = 2.112606414597e-06;
+        s = 0.0;
+        polarMotion = psEOC_GetPolarMotion(in, PS_IERS_B);
+        skip_start(  polarMotion == NULL, 3,
+                     "Skipping 3 tests because psEarthPole is NULL!");
+        is_double_tol(polarMotion->x, x, 0.1,
+                      "psEOC_GetPolarMotion: return valid EarthPole for valid inputs "
+                      "(x) - IERS B.");
+        is_double_tol(polarMotion->y, y, 0.1,
+                      "psEOC_GetPolarMotion: return valid EarthPole for valid inputs "
+                      "(y) - IERS B.");
+        is_double_tol(polarMotion->s, s, 0.1,
+                      "psEOC_GetPolarMotion: return valid EarthPole for valid inputs "
+                      "(s) - IERS B.");
+        skip_end();
+        psFree(polarMotion);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return valid EarthPole for valid time input - IERS A
+    {
+        psMemId id = psMemGetId();
+        double x, y, s;
+        x = -6.45381397904e-07;
+        y = 2.112819726698e-06;
+        s = 0.0;
+        polarMotion = psEOC_GetPolarMotion(in, PS_IERS_A);
+        skip_start(  polarMotion == NULL, 3,
+                     "Skipping 3 tests because psEarthPole is NULL!");
+        is_double_tol(polarMotion->x, x, 0.1,
+                      "psEOC_GetPolarMotion: return valid EarthPole for valid inputs "
+                      "(x) - IERS A.");
+        is_double_tol(polarMotion->y, y, 0.1,
+                      "psEOC_GetPolarMotion: return valid EarthPole for valid inputs "
+                      "(y) - IERS A.");
+        is_double_tol(polarMotion->s, s, 0.1,
+                      "psEOC_GetPolarMotion: return valid EarthPole for valid inputs "
+                      "(s) - IERS A.");
+        skip_end();
+        psFree(polarMotion);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return valid EarthPole for valid time input - IERS A
+    {
+        psMemId id = psMemGetId();
+        psTime *firstTime = psTimeFromMJD(41684.50);
+        polarMotion = psEOC_GetPolarMotion(firstTime, PS_IERS_B);
+        ok( polarMotion != NULL,
+            "psEOC_GetPolarMotion: return valid EarthPole for valid inputs.");
+        psFree(firstTime);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Check for Memory leaks
+    {
+        psFree(in);
+        psFree(polarMotion);
+        checkMem();
+    }
+
+}
+
+static psSphere *objSetup(void)
+{
+    psSphere *obj = NULL;
+    psCube *tempCube = psCubeAlloc();
+    tempCube->x = -0.3598480726985338;
+    tempCube->y = 0.5555012823608123;
+    tempCube->z = 0.7496183628158023;
+    obj = psCubeToSphere(tempCube);
+    psFree(tempCube);
+    return obj;
+}
+
+void testSphereRots(void)
+{
+    // psSphereRot Functions
+    psSphereRot *out = NULL;
+    psEarthPole *in = NULL;
+    double q0,q1,q2,q3;
+    q0 = -1.1984522406756289e-5;
+    q1 = 1.4285893358610674e-4;
+    q2 = 1.2191193518914336e-10;
+    q3 = -0.9999999897238481;
+
+    //Tests for psSphereRot_CEOtoGCRS
+    //Return NULL for NULL earthPole input
+    {
+        psMemId id = psMemGetId();
+        out = psSphereRot_CEOtoGCRS(in);
+        ok( out == NULL, "psSphereRot_CEOtoGCRS: return NULL for NULL earthPole input.");
+        in = psEarthPoleAlloc();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    in->x = 2.857175590089105e-4;
+    in->y = 2.3968739377734732e-5;
+    in->s = -1.3970066457904322e-8;
+    //Return correct psSphereRot for valid input
+    {
+        psMemId id = psMemGetId();
+        out = psSphereRot_CEOtoGCRS(in);
+        skip_start(  out == NULL, 4,
+                     "Skipping 4 tests because psSphereRot output is NULL!");
+        is_double_tol( out->q0, q0, 0.0001,
+                       "psSphereRot_CEOtoGCRS: return correct psSphereRot for valid input (q0).");
+        is_double_tol( out->q1, q1, 0.0001,
+                       "psSphereRot_CEOtoGCRS: return correct psSphereRot for valid input (q1).");
+        is_double_tol( out->q2, q2, 0.0001,
+                       "psSphereRot_CEOtoGCRS: return correct psSphereRot for valid input (q2).");
+        is_double_tol( out->q3, -q3, 0.0001,
+                       "psSphereRot_CEOtoGCRS: return correct psSphereRot for valid input (q3).");
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Tests for psSphereRot_TEOtoCEO
+    psFree(out);
+    psTime *time = psTimeAlloc(PS_TIME_UT1);
+    psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+    time->sec = 1049160600-1;
+    time->nsec = 657017200;
+    time->leapsecond = false;
+    psEarthPole *temp = psEarthPoleAlloc();
+    temp->s = -2.0;
+    //Return NULL for NULL time input
+    {
+        psMemId id = psMemGetId();
+        out = psSphereRot_TEOtoCEO(NULL, NULL);
+        ok( out == NULL, "psSphereRot_TEOtoCEO: return NULL for NULL time input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for invalid time input - large nsec, UTC time type
+    {
+        psMemId id = psMemGetId();
+        time2->nsec = 3e9;
+        out = psSphereRot_TEOtoCEO(time2, NULL);
+        ok( out == NULL, "psSphereRot_TEOtoCEO: return NULL for invalid time input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for invalid time input - UT1 time type, large nsec
+    {
+        psMemId id = psMemGetId();
+        psFree(time2);
+        time2 = psTimeAlloc(PS_TIME_UT1);
+        time2->nsec = 1e9;
+        temp->s = 1.0;
+        out = psSphereRot_TEOtoCEO(time2, temp);
+        ok( out == NULL, "psSphereRot_TEOtoCEO: return NULL for invalid time input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for invalid earthPole input
+    {
+        psMemId id = psMemGetId();
+        time2->nsec = 0;
+        psEarthPole *temp = psEarthPoleAlloc();
+        temp->s = -2.0;
+        out = psSphereRot_TEOtoCEO(time2, temp);
+        ok( out == NULL, "psSphereRot_TEOtoCEO: return NULL for invalid earthPole input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return correct psSphereRot for valid inputs
+    double x, y, z;
+    x = 0.01698625430807123;
+    y = -0.6616523084626379;
+    z = 0.7496183628158023;
+    {
+        psMemId id = psMemGetId();
+        psEarthPole *polarTideCorr = psEOC_PolarTideCorr(time);
+        out = psSphereRot_TEOtoCEO(time, polarTideCorr);
+        skip_start(  out == NULL, 3,
+                     "Skipping 3 tests because psSphereRot output is NULL!");
+        psSphere *obj = objSetup();
+        psSphereRot *earthRot = psSphereRotConjugate(NULL, out);
+        psSphere *result = psSphereRotApply(NULL, earthRot, obj);
+        psCube *cube = psSphereToCube(result);
+        is_double_tol( cube->x, x, 0.0001,
+                       "psSphereRot_TEOtoCEO: return NULL for NULL time input. (x)");
+        is_double_tol( cube->y, y, 0.0001,
+                       "psSphereRot_TEOtoCEO: return NULL for NULL time input. (y)");
+        is_double_tol( cube->z, z, 0.0001, "psSphereRot_TEOtoCEO: return NULL for NULL time input. (z)");
+        psFree(earthRot);
+        psFree(result);
+        psFree(cube);
+        psFree(obj);
+        skip_end();
+        psFree(polarTideCorr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Tests for psSphereRot_ITRStoTEO
+    //Return NULL for NULL earthPole input
+    {
+        psMemId id = psMemGetId();
+        psFree(out);
+        out = psSphereRot_ITRStoTEO(NULL);
+        ok( out == NULL,
+            "psSphereRot_ITRStoTEO:         return NULL for NULL earthPole input.");
+        in = psEarthPoleAlloc();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return correct psSphereRot for valid input
+    {
+        psMemId id = psMemGetId();
+        q0 = -1.0567571848664005e-6;
+        q1 = 3.218036562931509e-7;
+        q2 = -3.3580195807204483e-12;
+        q3 = -0.9999999999993899;
+        in->x = SEC_TO_RAD(-0.13275353774074533);
+        in->y = SEC_TO_RAD(0.4359436319739848);
+        in->s = SEC_TO_RAD(-4.2376965863576153e-10);
+        out = psSphereRot_ITRStoTEO(in);
+        skip_start(  out == NULL, 4,
+                     "Skipping 4 tests because psSphereRot output is NULL!");
+        is_double_tol( out->q0, q0, 0.0001,
+                       "psSphereRot_ITRStoTEO: return correct psSphereRot for valid input (q0).");
+        is_double_tol( out->q1, q1, 0.0001,
+                       "psSphereRot_ITRStoTEO: return correct psSphereRot for valid input (q1).");
+        is_double_tol( out->q2, q2, 0.0001,
+                       "psSphereRot_ITRStoTEO: return correct psSphereRot for valid input (q2).");
+        is_double_tol( out->q3, q3, 0.0001,
+                       "psSphereRot_ITRStoTEO: return correct psSphereRot for valid input (q3).");
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    //Check for Memory leaks
+    {
+        psFree(temp);
+        psFree(time2);
+        psFree(time);
+        psFree(out);
+        psFree(in);
+        checkMem();
+    }
+}
+
+#define SPHERE_PRECESS_TP1_R            0.0               //    0.0       degrees
+#define SPHERE_PRECESS_TP1_D            0.0               //    0.0       degrees
+#define SPHERE_PRECESS_TP1_EXPECT_R     6.238453          //  357.437     degrees
+#define SPHERE_PRECESS_TP1_EXPECT_D    -0.019426          //   -1.113     degrees
+#define SPHERE_PRECESS_TP2_R            0.0               //    0.0       degrees
+#define SPHERE_PRECESS_TP2_D            1.570796          //   90.0       degrees
+#define SPHERE_PRECESS_TP2_EXPECT_R     6.260828          //  358.719     degrees
+#define SPHERE_PRECESS_TP2_EXPECT_D     1.551353          //   88.886     degrees
+#define SPHERE_PRECESS_TP3_R            3.141593          //  180.0       degrees
+#define SPHERE_PRECESS_TP3_D            0.523599          //   30.0       degrees
+#define SPHERE_PRECESS_TP3_EXPECT_R     3.096616          //  177.423     degrees
+#define SPHERE_PRECESS_TP3_EXPECT_D     0.543024          //   31.113     degrees
+#define ERROR_TOL   0.0001
+
+void testSpherePrecess(void)
+{
+    // psSpherePrecess
+    psSphereRot *rot = NULL;
+    psTime *fromTime = NULL;
+    psTime *toTime = NULL;
+    psSphere* inputCoord  = psSphereAlloc();
+    psSphere* outputCoord = NULL;
+
+
+    //Return NULL for NULL time inputs
+    {
+        psMemId id = psMemGetId();
+        rot = psSpherePrecess(fromTime, toTime, PS_PRECESS_ROUGH);
+        ok( rot == NULL, "psSpherePrecess: return NULL for NULL time inputs.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for invalid mode input
+    {
+        psMemId id = psMemGetId();
+        fromTime    = psTimeFromMJD(MJD_2100);
+        toTime      = psTimeFromMJD(MJD_1900);
+        rot = psSpherePrecess(fromTime, toTime, -1);
+        ok( rot == NULL, "psSpherePrecess: return NULL for invalid mode input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return correct psSphereRot for valid inputs, mode = ROUGH
+    {
+        psMemId id = psMemGetId();
+        inputCoord->r = SPHERE_PRECESS_TP1_R;
+        inputCoord->d = SPHERE_PRECESS_TP1_D;
+        inputCoord->rErr = 0.0;
+        inputCoord->dErr = 0.0;
+
+        rot = psSpherePrecess(fromTime, toTime, PS_PRECESS_ROUGH);
+        outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+        if (outputCoord->r < -0.0001)
+            outputCoord->r += 2.0 * M_PI;
+        skip_start( rot == NULL || outputCoord == NULL, 2,
+                    "Skipping 2 tests because psSphereRot output is NULL!");
+        is_double_tol( outputCoord->r, SPHERE_PRECESS_TP1_EXPECT_R, ERROR_TOL,
+                       "psSpherePrecess: return correct psSphereRot for valid"
+                       " inputs and PS_PRECESS_ROUGH mode. (r)");
+        is_double_tol( outputCoord->d, SPHERE_PRECESS_TP1_EXPECT_D, ERROR_TOL,
+                       "psSpherePrecess: return correct psSphereRot for valid"
+                       " inputs and PS_PRECESS_ROUGH mode. (d)");
+        skip_end();
+        psFree(outputCoord);
+        psFree(rot);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return correct psSphereRot for valid inputs, mode = ROUGH
+    {
+        psMemId id = psMemGetId();
+        inputCoord->r = SPHERE_PRECESS_TP2_R;
+        inputCoord->d = SPHERE_PRECESS_TP2_D;
+        inputCoord->rErr = 0.0;
+        inputCoord->dErr = 0.0;
+
+        rot = psSpherePrecess(fromTime, toTime, PS_PRECESS_ROUGH);
+        outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+        if (outputCoord->r < -0.0001)
+            outputCoord->r += 2.0 * M_PI;
+        skip_start( rot == NULL || outputCoord == NULL, 2,
+                    "Skipping 2 tests because psSphereRot output is NULL!");
+        is_double_tol( outputCoord->r, SPHERE_PRECESS_TP2_EXPECT_R, ERROR_TOL,
+                       "psSpherePrecess: return correct psSphereRot for valid"
+                       " inputs and PS_PRECESS_ROUGH mode. (r)");
+        is_double_tol( outputCoord->d, SPHERE_PRECESS_TP2_EXPECT_D, ERROR_TOL,
+                       "psSpherePrecess: return correct psSphereRot for valid"
+                       " inputs and PS_PRECESS_ROUGH mode. (d)");
+        skip_end();
+        psFree(outputCoord);
+        psFree(rot);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return correct psSphereRot for valid inputs, mode = ROUGH
+    {
+        psMemId id = psMemGetId();
+        inputCoord->r = SPHERE_PRECESS_TP3_R;
+        inputCoord->d = SPHERE_PRECESS_TP3_D;
+        inputCoord->rErr = 0.0;
+        inputCoord->dErr = 0.0;
+
+        rot = psSpherePrecess(fromTime, toTime, PS_PRECESS_ROUGH);
+        outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+        if (outputCoord->r < -0.0001)
+            outputCoord->r += 2.0 * M_PI;
+        skip_start( rot == NULL || outputCoord == NULL, 2,
+                    "Skipping 2 tests because psSphereRot output is NULL!");
+        is_double_tol( outputCoord->r, SPHERE_PRECESS_TP3_EXPECT_R, ERROR_TOL,
+                       "psSpherePrecess: return correct psSphereRot for valid"
+                       " inputs and PS_PRECESS_ROUGH mode. (r)");
+        is_double_tol( outputCoord->d, SPHERE_PRECESS_TP3_EXPECT_D, ERROR_TOL,
+                       "psSpherePrecess: return correct psSphereRot for valid"
+                       " inputs and PS_PRECESS_ROUGH mode. (d)");
+        skip_end();
+        psFree(outputCoord);
+        psFree(rot);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return correct psSphereRot for valid inputs, mode = COMPLETE_A
+    {
+        psMemId id = psMemGetId();
+        rot = psSpherePrecess(NULL, toTime, PS_PRECESS_COMPLETE_A);
+        outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+        ok( outputCoord != NULL && rot != NULL,
+            "psSpherePrecess: return correct psSphereRot for valid"
+            " inputs and PS_PRECESS_COMPLETE_A mode.");
+        psFree(outputCoord);
+        psFree(rot);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return correct psSphereRot for valid inputs, mode = COMPLETE_B
+    {
+        psMemId id = psMemGetId();
+        rot = psSpherePrecess(NULL, toTime, PS_PRECESS_COMPLETE_B);
+        outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+        ok( outputCoord != NULL && rot != NULL,
+            "psSpherePrecess: return correct psSphereRot for valid"
+            " inputs and PS_PRECESS_COMPLETE_B mode.");
+        psFree(outputCoord);
+        psFree(rot);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return correct psSphereRot for valid inputs, mode = IAU2000A
+    {
+        psMemId id = psMemGetId();
+        rot = psSpherePrecess(NULL, toTime, PS_PRECESS_IAU2000A);
+        outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+        ok( outputCoord != NULL && rot != NULL,
+            "psSpherePrecess: return correct psSphereRot for valid"
+            " inputs and PS_PRECESS_IAU2000A mode.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    //Check for Memory leaks
+    {
+        psFree(fromTime);
+        psFree(toTime);
+        psFree(outputCoord);
+        psFree(rot);
+        checkMem();
+    }
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psSphereOps_all.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psSphereOps_all.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psSphereOps_all.c	(revision 22322)
@@ -0,0 +1,567 @@
+/**
+ *  C Implementation: tap_psSphereOps_all
+ *
+ * Description:  Tests for psSphereRotAlloc, psMemCheckSphereRot, psSphereRotQuat,
+ *                         psSphereRotApply, psSphereRotCombine, psSphereRotConjugate,
+ *                         psSphereRotInvert, psSphereGetOffset, psSphereSetOffset,
+ *                         psSphereRotICRSToEcliptic, psSphereRotEclipticToICRS,
+ *                         psSphereRotICRSToGalactic, psSphereRotGalacticToICRS
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define DEG_INC   30.0
+#define MJD_1900  15021.0        // Modified Julian Day 1/1/1900 00:00:00
+#define MJD_2000  51544.0        // Modified Julian Day 1/1/2000 00:00:00
+#define MJD_2100  88069.0        // Modified Julian Day 1/1/2100 00:00:00
+#define ERROR_TOL   0.0001
+#define ALPHA_P 4*M_PI/3
+#define DELTA_P M_PI/4
+#define PHI_P M_PI/3
+
+static void testSphereRotCreate(void);
+static void testSphereRotConvert(void);
+static void testSphereOffsets(void);
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    plan_tests(86);
+    testSphereRotCreate();
+    testSphereRotConvert();
+    testSphereOffsets();
+}
+
+void testSphereRotCreate(void)
+{
+    // ----------------------------------------------------------------
+    //Tests for psSphereRotAlloc
+    //Return NULL for NAN input alpha
+    {
+        psMemId id = psMemGetId();
+        psSphereRot *s1 = psSphereRotAlloc(NAN, DELTA_P, PHI_P);
+        ok( s1 == NULL, "psSphereRotAlloc(): return NULL for NAN input alpha");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for NAN input delta
+    {
+        psMemId id = psMemGetId();
+        psSphereRot *s1 = psSphereRotAlloc(ALPHA_P, NAN, PHI_P);
+        ok( s1 == NULL, "psSphereRotAlloc(): return NULL for NAN input delta");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for NAN input phi
+    {
+        psMemId id = psMemGetId();
+        psSphereRot *s1 = psSphereRotAlloc(ALPHA_P, DELTA_P, NAN);
+        ok( s1 == NULL, "psSphereRotAlloc(): return NULL for NAN input phi");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    double a0 = (ALPHA_P - PHI_P)/2.0;
+    double a1 = (ALPHA_P - PHI_P)/2.0;
+    double a2 = (ALPHA_P + PHI_P)/2.0;
+    double a3 = (ALPHA_P + PHI_P)/2.0;
+    //From Mathworld, this is another way to calculate the quaternions of a rotation
+    double q0 = sin(a0)*sin(DELTA_P/2);
+    double q1 = cos(a1)*sin(DELTA_P/2);
+    double q2 = sin(a2)*cos(DELTA_P/2);
+    double q3 = cos(a3)*cos(DELTA_P/2);
+    // Test psSphereRot() for valid inputs
+    {
+        psMemId id = psMemGetId();
+        psSphereRot* myST = NULL;
+        myST = psSphereRotAlloc(ALPHA_P, DELTA_P, PHI_P);
+        ok(myST != NULL && psMemCheckSphereRot(myST),
+            "psSphereRotAlloc:  return allocated SphereRot for valid inputs.");
+        is_double(q0, myST->q0, "psSphereRotAlloc: return correct q0 value.");
+        is_double(q1, myST->q1, "psSphereRotAlloc: return correct q1 value.");
+        is_double(q2, myST->q2, "psSphereRotAlloc: return correct q2 value.");
+        is_double(q3, myST->q3, "psSphereRotAlloc: return correct q3 value.");
+        psFree(myST);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------
+    //Tests for psMemCheckSphereRot
+    //Make sure psMemCheckSphereRot works correctly - return false
+    //for non-psSphereRot input
+    {
+        psMemId id = psMemGetId();
+        int j = 2;
+        ok( !psMemCheckSphereRot(&j), "psMemCheckSphereRot: return false for non-SphereRot input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------
+    //Tests for psSphereRotQuat
+    //Return NULL for NAN input q0
+    {
+        psMemId id = psMemGetId();
+        psSphereRot *s1 = psSphereRotQuat(NAN, q1, q2, q3);
+        ok( s1 == NULL, "psSphereRotQuat: return NULL for NAN input q0");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for NAN input q1
+    {
+        psMemId id = psMemGetId();
+        psSphereRot *s1 = psSphereRotQuat(q0, NAN, q2, q3);
+        ok( s1 == NULL, "psSphereRotQuat: return NULL for NAN input q1");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for NAN input q2
+    {
+        psMemId id = psMemGetId();
+        psSphereRot *s1 = psSphereRotQuat(q0, q1, NAN, q3);
+        ok( s1 == NULL, "psSphereRotQuat: return NULL for NAN input q2");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for NAN input q3
+    {
+        psMemId id = psMemGetId();
+        psSphereRot *s1 = psSphereRotQuat(q0, q1, q2, NAN);
+        ok( s1 == NULL, "psSphereRotQuat: return NULL for NAN input q3");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return valid psSphereRot (allocated) for valid inputs
+    psSphereRot* myST = psSphereRotQuat(q0*2.0, q1*2.0, q2*2.0, q3*2.0);
+    {
+        psMemId id = psMemGetId();
+        ok( myST != NULL && psMemCheckSphereRot(myST),
+            "psSphereRotQuat: return allocated SphereRot for valid inputs.");
+        is_double(q0, myST->q0, "psSphereRotQuat: return correct q0 value.");
+        is_double(q1, myST->q1, "psSphereRotQuat: return correct q1 value.");
+        is_double(q2, myST->q2, "psSphereRotQuat: return correct q2 value.");
+        is_double(q3, myST->q3, "psSphereRotQuat: return correct q3 value.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
+
+void testSphereRotConvert(void)
+{
+    // Allocate data structure
+    psSphereRot* myST = NULL;
+    psSphereRot* s1 = NULL;
+    psSphereRot *s2 = NULL;
+    myST = psSphereRotAlloc(ALPHA_P, DELTA_P, PHI_P);
+    psSphere *sphere = psSphereAlloc();
+    sphere->r = 0.0;
+    sphere->d = 0.0;
+    psSphere *out = NULL;
+    psSphere *out2 = NULL;
+    psTime *pre1900 = psTimeFromMJD(MJD_1900 - 100.0);
+    psTime *mjd2k = psTimeFromMJD(MJD_2000);
+    psSphere* icrs = psSphereAlloc();
+    psSphere* galactic = NULL;
+    icrs->r = DEG_TO_RAD(0.0);
+    icrs->d = DEG_TO_RAD(90.0);
+
+
+    // ---------------------------------------------------------
+    //Tests for psSphereRotCombine
+    //Return NULL for NULL sphere input
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotCombine(s1, myST, NULL);
+        ok( s1 == NULL, "psSphereRotCombine: return NULL for NULL input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for NULL transform input
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotCombine(s1, NULL, myST);
+        ok( s1 == NULL, "psSphereRotCombine: return NULL for NULL input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    // XXX: Must test with legitimate input data.
+
+
+    // ---------------------------------------------------------
+    //Tests for psSphereRotApply
+    //Return NULL for NULL sphere input
+    {
+        psMemId id = psMemGetId();
+        out = psSphereRotApply(out, myST, NULL);
+        ok( out == NULL, "psSphereRotApply: return NULL for NULL sphere input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for NULL transform input
+    {
+        psMemId id = psMemGetId();
+        out = psSphereRotApply(out, NULL, sphere);
+        ok( out == NULL, "psSphereRotApply: return NULL for NULL transform input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ---------------------------------------------------------
+    //Tests for psSphereRotConjugate
+    //Return NULL for NULL inputs
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotConjugate(s1, NULL);
+        ok(s1 == NULL, "psSphereRotConjugate: return NULL SphereRot for NULL inputs");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    //Return correct conjugate for valid inputs
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotConjugate(s1, myST);
+        skip_start(s1 == NULL, 1, "Skipping 1 tests because psSphereRot is NULL!");
+        out = psSphereRotApply(out, myST, sphere);
+        skip_start(out == NULL || out->r == 0, 2,
+                  "Skipping 1 tests because psSphereRotApply failed!");
+        out2 = psSphereRotApply(out2, s1, out);
+        is_double( out2->r, 0.0, "psSphereRotConjugate: return correct SphereRot values.");
+        is_double( out2->d, 0.0, "psSphereRotConjugate: return correct SphereRot values.");
+        skip_end();
+        psFree(s1);
+        psFree(out);
+        psFree(out2);
+        s1 = NULL;
+        out = NULL;
+        out2 = NULL;
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ---------------------------------------------------------
+    //Tests for psSphereRotInvert
+    //Return correct inversion for valid inputs
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotInvert(-PHI_P, -DELTA_P, -ALPHA_P);
+        is_double(s1->q0, myST->q0, "psSphereRotInvert: return correct q0 value.");
+        is_double(s1->q1, myST->q1, "psSphereRotInvert: return correct q1 value.");
+        is_double(s1->q2, myST->q2, "psSphereRotInvert: return correct q2 value.");
+        is_double(s1->q3, myST->q3, "psSphereRotInvert: return correct q3 value.");
+        psFree(s1);
+        s1 = NULL;
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ---------------------------------------------------------
+    //Tests for psSphereRotICRSToEcliptic
+    //Return NULL for NULL input
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotICRSToEcliptic(NULL);
+        ok( s1 == NULL, "psSphereRotICRSToEcliptic: return NULL SphereRot for NULL inputs.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for Pre-MJD_1900 Date
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotICRSToEcliptic(pre1900);
+        ok( s1 == NULL, "psSphereRotICRSToEcliptic: return NULL SphereRot for pre-1900 MJD date.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return correct SphereRot for MJD_2000
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotICRSToEcliptic(mjd2k);
+        skip_start(  s1 == NULL, 2, "Skipping 2 tests because psSphereRot is NULL!");
+        icrs->r = DEG_TO_RAD(0.0);
+        icrs->d = DEG_TO_RAD(90.0);
+        psSphere* ecliptic = psSphereRotApply(NULL, s1, icrs);
+        is_double_tol(DEG_TO_RAD(90.0), ecliptic->r, 0.00001,
+                      "psSphereRotICRSToEcliptic: return correct SphereRot for MJD_2000 date.");
+        is_double_tol(DEG_TO_RAD(66.560719), ecliptic->d, 0.00001,
+                      "psSphereRotICRSToEcliptic: return correct SphereRot for MJD_2000 date.");
+        psFree(ecliptic);
+        skip_end();
+        psFree(s1);
+        s1 = NULL;
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------
+    //Tests for psSphereRotEclipticToICRS
+    //Return NULL for NULL input
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotEclipticToICRS(NULL);
+        ok( s1 == NULL, "psSphereRotEclipticToICRS: return NULL SphereRot for NULL inputs.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for Pre-MJD_1900 Date
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotEclipticToICRS(pre1900);
+        ok( s1 == NULL, "psSphereRotEclipticToICRS: return NULL SphereRot for pre-1900 MJD date.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return correct SphereRot for MJD_2000
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotEclipticToICRS(mjd2k);
+        skip_start(  s1 == NULL, 2, "Skipping 2 tests because psSphereRot is NULL!");
+        psSphere* ecliptic = psSphereRotApply(NULL, s1, icrs);
+        s2 = psSphereRotICRSToEcliptic(mjd2k);
+        psSphere* fromEcliptic = psSphereRotApply(NULL, s2, ecliptic);
+        //XXX:  Pretty sure the following can be 180 degrees OR 0 degrees. (= the poles)
+        is_double_tol(DEG_TO_RAD(180.0), fromEcliptic->r, 0.00001,
+                      "psSphereRotEclipticToICRS: return correct SphereRot for MJD_2000 date.");
+        is_double_tol(DEG_TO_RAD(90.0), fromEcliptic->d, 0.00001,
+                      "psSphereRotEclipticToICRS: return correct SphereRot for MJD_2000 date.");
+        psFree(ecliptic);
+        psFree(fromEcliptic);
+        skip_end();
+        psFree(s1);
+        s1 = NULL;
+        psFree(s2);
+        s2 = NULL;
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------
+    //Tests for psSphereRotICRSToGalactic
+    //Return correct transformation
+    {
+        psMemId id = psMemGetId();
+        s2 = psSphereRotICRSToGalactic();
+        galactic = psSphereRotApply(NULL, s2, icrs);
+        is_double_tol(DEG_TO_RAD(122.93192), galactic->r, 0.00001,
+                      "psSphereRotICRSToGalactic: return correct SphereRot.");
+        is_double_tol(DEG_TO_RAD(27.12825), galactic->d, 0.00001,
+                      "psSphereRotICRSToGalactic: return correct SphereRot.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------
+    //Tests for psSphereRotGalacticToICRS
+    //Return correct transformation
+    {
+        psMemId id = psMemGetId();
+        s1 = psSphereRotGalacticToICRS();
+        psSphere *test = psSphereRotApply(NULL, s1, galactic);
+        //XXX: Pretty sure the following can be 180 degrees OR 0 degrees. (= the poles)
+        is_double_tol(DEG_TO_RAD(180.0), test->r, 0.00001,
+                      "psSphereRotGalacticToICRS: return correct SphereRot.");
+        is_double_tol(DEG_TO_RAD(90.0), test->d, 0.00001,
+                      "psSphereRotGalacticToICRS: return correct SphereRot.");
+        psFree(test);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psFree(galactic);
+        psFree(s2);
+        psFree(s1);
+        psFree(mjd2k);
+        psFree(pre1900);
+        psFree(icrs);
+        psFree(sphere);
+        psFree(myST);
+    }
+
+}
+
+
+void testSphereOffsets(void)
+{
+    psSphereRot* myST = NULL;
+    myST = psSphereRotAlloc(ALPHA_P, DELTA_P, PHI_P);
+
+    psSphere *origin = psSphereAlloc();
+    psSphere *offset = psSphereAlloc();
+    psSphere *empty = NULL;
+    psSphere *output = NULL;
+
+    // -----------------------------------------------------------
+    //Tests for psSphereGetOffset
+    //Test Get for NULL position1
+    {
+        output = psSphereGetOffset(empty, origin, PS_LINEAR, PS_RADIAN);
+        ok( output == NULL, "psSphereGetOffset: return NULL for NULL position1 input.");
+    }
+
+
+    //Test Get for NULL position2
+    {
+        output = psSphereGetOffset(origin, empty, PS_LINEAR, PS_RADIAN);
+        ok( output == NULL, "psSphereGetOffset: return NULL for NULL position2 input.");
+    }
+
+
+    //Return NULL for position1 declination > 90 degrees
+    {
+        origin->d = 100.0;
+        output = psSphereGetOffset(origin, offset, PS_LINEAR, PS_RADIAN);
+        ok( output == NULL, "psSphereGetOffset: return NULL for position1->d > 90 degrees.");
+    }
+
+
+    //Return NULL for position1 declination > 90 degrees
+    {
+        origin->d = 0.0;
+        offset->d = 100.0;
+        output = psSphereGetOffset(origin, offset, PS_LINEAR, PS_RADIAN);
+        ok( output == NULL, "psSphereGetOffset: return NULL for position2->d > 90 degrees.");
+    }
+
+
+    //Return NULL for invalid mode specification
+    {
+        offset->d = 0.0;
+        output = psSphereGetOffset(origin, offset, -1, PS_RADIAN);
+        ok( output == NULL, "psSphereGetOffset: return NULL for invalid mode input.");
+    }
+
+
+    //Return NULL for invalid unit specification
+    {
+        output = psSphereGetOffset(origin, offset, PS_SPHERICAL, -1);
+        ok( output == NULL, "psSphereGetOffset: return NULL for invalid unit input.");
+    }
+    //Return matching sphere offsets regardless of units
+    {
+        offset->r = (M_PI / 4.0);     //45 deg
+        offset->d = (M_PI / 6.0);     //30 deg
+        output = psSphereGetOffset(origin, offset, PS_SPHERICAL, PS_ARCMIN);
+        empty = psSphereGetOffset(origin, offset, PS_SPHERICAL, PS_DEGREE);
+        is_double_tol(DEG_TO_RAD(empty->r), MIN_TO_RAD(output->r), 0.0001,
+                      "psSphereGetOffset: return correct offset for differing units.");
+        is_double_tol(DEG_TO_RAD(empty->d), MIN_TO_RAD(output->d), 0.0001,
+                      "psSphereGetOffset: return correct offset for differing units.");
+        psFree(output);
+        psFree(empty);
+    }
+
+    //Tests for psSphereSetOffset
+    output = NULL;
+    empty = NULL;
+    //Return NULL for NULL position
+    {
+        output = psSphereSetOffset(empty, offset, PS_SPHERICAL, PS_DEGREE);
+        ok( output == NULL, "psSphereSetOffset: return NULL for NULL position input.");
+    }
+    //Return NULL for NULL offset
+    {
+        output = psSphereSetOffset(offset, empty, PS_SPHERICAL, PS_DEGREE);
+        ok( output == NULL, "psSphereSetOffset: return NULL for NULL offset input.");
+    }
+    //Return NULL for invalid mode specification
+    {
+        output = psSphereSetOffset(origin, offset, -1, PS_RADIAN);
+        ok( output == NULL, "psSphereSetOffset: return NULL for for invalid mode input.");
+    }
+    //Return NULL for invalid unit specification
+    {
+        output = psSphereSetOffset(origin, offset, PS_SPHERICAL, PS_RADIAN+1);
+        ok( output == NULL, "psSphereSetOffset: return NULL for for invalid unit input.");
+    }
+    //Test Set using Spherical mode, Degree units
+    origin->r = 0.0;
+    origin->d = 0.0;
+    offset->r = 45.0;
+    offset->d = 30.0;
+    {
+        output = psSphereSetOffset(origin, offset, PS_SPHERICAL, PS_DEGREE);
+        skip_start(output == NULL, 2, "Skipping 2 tests because Sphere output is NULL!");
+        is_double(output->r, M_PI/4.0, "psSphereSetOffset: return correct spherical offset.");
+        is_double(output->d, M_PI/6.0, "psSphereSetOffset: return correct spherical offset.");
+        skip_end();
+        psFree(output);
+    }
+    //Test Set and Get using Linear mode, Radian units
+    origin->r = 0.0;
+    origin->d = 0.0;
+    offset->r = 1.0;
+    offset->d = 1.0;
+    {
+        output = psSphereSetOffset(origin, offset, PS_LINEAR, PS_RADIAN);
+        empty = psSphereGetOffset(origin, output, PS_LINEAR, PS_RADIAN);
+        skip_start(empty == NULL, 2, "Skipping 2 tests because Sphere output is NULL!");
+        is_double_tol(empty->r, offset->r, 0.00001,
+                      "psSphereGetOffset: return correct spherical offset.");
+        is_double_tol(empty->d, offset->d, 0.00001,
+                      "psSphereGetOffset: return correct spherical offset.");
+        skip_end();
+        psFree(output);
+        psFree(empty);
+    }
+    //Test Set using SPHERICAL mode, Arcmin units
+    origin->r = 0.0;
+    origin->d = 0.0;
+    offset->r = RAD_TO_MIN(M_PI / 4.0);     //45 deg
+    offset->d = RAD_TO_MIN(M_PI / 6.0);     //30 deg
+    {
+        output = psSphereSetOffset(origin, offset, PS_SPHERICAL, PS_ARCMIN);
+        //Test Get using Spherical mode, Arcsec units
+        empty = psSphereGetOffset(origin, output, PS_SPHERICAL, PS_ARCSEC);
+        empty->r = SEC_TO_RAD(empty->r);
+        empty->d = SEC_TO_RAD(empty->d);
+        skip_start(empty == NULL, 2, "Skipping 2 tests because Sphere output is NULL!");
+        is_double_tol(empty->r, (M_PI / 4.0), 0.0001,
+                      "psSphereGetOffset: return correct spherical offset.");
+        is_double_tol(empty->d, (M_PI / 6.0), 0.0001,
+                      "psSphereGetOffset: return correct spherical offset.");
+        skip_end();
+        psFree(output);
+        psFree(empty);
+    }
+    //Return matching sphere offsets regardless of units
+    {
+        offset->r = RAD_TO_SEC(M_PI / 4.0);     //45 deg
+        offset->d = RAD_TO_SEC(M_PI / 6.0);     //30 deg
+        output = psSphereSetOffset(origin, offset, PS_SPHERICAL, PS_ARCSEC);
+        offset->r = (M_PI / 4.0);     //45 deg
+        offset->d = (M_PI / 6.0);     //30 deg
+        empty = psSphereSetOffset(origin, offset, PS_SPHERICAL, PS_RADIAN);
+        is_double_tol(empty->r, offset->r, 0.0001,
+                      "psSphereSetOffset: return correct offset for differing units.");
+        is_double_tol(empty->d, offset->d, 0.0001,
+                      "psSphereSetOffset: return correct offset for differing units.");
+        psFree(output);
+        psFree(empty);
+    }
+
+    //Check for Memory leaks
+    {
+        psFree(origin);
+        psFree(offset);
+        psFree(myST);
+        checkMem();
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_01.c	(revision 22322)
@@ -0,0 +1,1155 @@
+/** @file  tst_psTime_01.c
+ *
+ *  @brief Test driver for psTime functions
+ *
+ *  This test driver contains the following tests for psTime:
+ *     1) Allocate psTime structure
+ *     2) Get current time
+ *     3) Get UT1 UTC delta
+ *     4) Convert psTime to MJD
+ *     5) Convert psTime to JD
+ *     6) Convert psTime to ISO
+ *     7) Convert psTime to timeval
+ *     8) Create psTime from MJD
+ *     9) Create psTime from JD
+ *    10) Create psTime from ISO
+ *    11) Create psTime from timeval
+ *    12) Create psTime from TM
+ *    13) Convert time between different types
+ *
+ *     O) Convert psTime time to LMST
+ *
+ *  @author  Ross Harman, MHPCC
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.7 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-06-05 01:10:22 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define ERROR_TOL    0.0001
+
+// Test Time 1 : July 21, 2004  18:22:24.3
+//               MJD = 53207.765559
+//               JD = 2453208.265559
+// UTC Test Time 1
+const psS64 testTime1SecondsUTC     = 1090434144;
+const psU32 testTime1NanosecondsUTC = 272044000;
+// TAI Test Time 1
+const psS64 testTime1SecondsTAI     = 1090434176;
+const psU32 testTime1NanosecondsTAI = 272044000;
+const psF64 testTime1MJDTAI         = 53207.76592937;
+const psF64 testTime1JDTAI          = 2453208.26592937;
+
+// TT Test Time 1
+const psS64 testTime1SecondsTT      = 1090434208;
+const psU32 testTime1NanosecondsTT  = 456044000;
+// Expected UT1-UTC IERS A & B
+const psF64 testTime1UT1DeltaBullA  = -0.457233186;
+const psF64 testTime1UT1DeltaBullB  = -0.457227;
+// UT1 Test Time 1
+const psS64 testTime1SecondsUT1     = 1090434143;
+//const psU32 testTime1NanosecondsUT1 = 814810814;
+const psU32 testTime1NanosecondsUT1 = 814810861;
+// Expected MJD & JD
+const psF64 testTime1MJD            = 53207.765559;
+const psF64 testTime1JD             = 2453208.265559;
+// Expected ISO string
+const char* testTime1Str     = "2004-07-21T18:22:24.2Z";
+const char* testTime1StrLeap = "2004-07-21T18:22:60.2Z";
+// Expected timeval values
+const psS32 testTime1TimevalSec = 1090434144;
+const psS32 testTime1TimevalUsec = 272044;
+
+// Test Time 2 : Jan. 1, 1973 00:00:00.0000
+//               MJD = 41683.0000
+//               JD = 2441683.5000
+const psS64 testTime2SecondsUTC     = 94694400;
+const psU32 testTime2NanosecondsUTC = 0;
+
+// Expected UT1-UTC IERS A & B
+const psF64 testTime2UT1DeltaBullA  = 0.000000;
+const psF64 testTime2UT1DeltaBullB  = 0.000000;
+
+// Test Time 3 : Sept. 21, 2006 00:00:00.0000
+//               MJD = 53999
+//               JD = 2453999.5
+const psS64 testTime3SecondsUTC     = 1158796800;
+const psU32 testTime3NanosecondsUTC = 0;
+// Expected UT1-UTC IERS A & B
+const psF64 testTime3UT1DeltaBullA  = -0.63574;
+const psF64 testTime3UT1DeltaBullB  = -0.63574;
+
+// Test Time 4 : Jan. 1, 1969 00:00:00.0000
+//               MJD = 40222
+//               JD = 2440222.5
+const psS64 testTime4SecondsUTC     = -31536000;
+const psU32 testTime4NanosecondsUTC = 0;
+// Expected MJD and JD
+const psF64 testTime4MJD            = 40222.0;
+const psF64 testTime4JD             = 2440222.5;
+
+// Test Time 5 : Dec 31, 0001 BC 23:59:59
+//               MJD = -1397755
+//               JD = 1002245.4999
+const psS64 testTime5SecondsUTC     = -62125920001;
+const psU32 testTime5NanosecondsUTC  = 0;
+
+// Test Time 6 : Jan. 1, 10000 AD 00:00:00
+const psS64 testTime6SecondsUTC      = 253202544001;
+const psU32 testTime6NanosecondsUTC  = 0;
+
+// Test Time 7 : Jan. 1, 2004 00:00:00,0
+const psS64 testTime7Seconds         = 1072915200;
+const psU32 testTime7Nanoseconds     = 0;
+const psS32 testTime7TmYear          = 104;
+const psS32 testTime7TmMon           = 0;
+const psS32 testTime7TmDay           = 1;
+const psS32 testTime7TmHour          = 0;
+const psS32 testTime7TmMin           = 0;
+const psS32 testTime7TmSec           = 0;
+
+// Test Time 8 : Dec. 31, 2003 00:00:00,0
+const psS64 testTime8Seconds         = 1072828800;
+const psU32 testTime8Nanoseconds     = 0;
+const psS32 testTime8TmYear          = 103;
+const psS32 testTime8TmMon           = 11;
+const psS32 testTime8TmDay           = 31;
+const psS32 testTime8TmHour          = 0;
+const psS32 testTime8TmMin           = 0;
+const psS32 testTime8TmSec           = 0;
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(198);
+    // Initialize library internal structures
+    psLibInit("pslib.config");
+
+
+    // Allocate new psTime with unallowed time type
+    // Following should generate error message
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(-100);
+        ok(time == NULL, "psTimeAlloc(-100) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeAlloc(TAI)
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        ok(time != NULL, "psTimeAlloc() did not return NULL");
+        skip_start(time == NULL, 4, "Skipping tests because psTimeAlloc() failed");
+        ok(time->type == PS_TIME_TAI, "psTimeAlloc() correctly set psTime->type");
+        ok(time->sec == 0, "psTimeAlloc() correctly set psTime->sec");
+        ok(time->nsec == 0, "psTimeAlloc() correctly set psTime->nsec");
+        ok(time->leapsecond == false, "psTimeAlloc() correctly set psTime->leapsecond");
+        psFree(time);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeAlloc(UTC)
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        ok(time != NULL, "psTimeAlloc() did not return NULL");
+        skip_start(time == NULL, 4, "Skipping tests because psTimeAlloc() failed");
+        ok(time->type == PS_TIME_UTC, "psTimeAlloc() correctly set psTime->type");
+        ok(time->sec == 0, "psTimeAlloc() correctly set psTime->sec");
+        ok(time->nsec == 0, "psTimeAlloc() correctly set psTime->nsec");
+        ok(time->leapsecond == false, "psTimeAlloc() correctly set psTime->leapsecond");
+        psFree(time);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeAlloc(UT1)
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UT1);
+        ok(time != NULL, "psTimeAlloc() did not return NULL");
+        skip_start(time == NULL, 4, "Skipping tests because psTimeAlloc() failed");
+        ok(time->type == PS_TIME_UT1, "psTimeAlloc() correctly set psTime->type");
+        ok(time->sec == 0, "psTimeAlloc() correctly set psTime->sec");
+        ok(time->nsec == 0, "psTimeAlloc() correctly set psTime->nsec");
+        ok(time->leapsecond == false, "psTimeAlloc() correctly set psTime->leapsecond");
+        psFree(time);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeAlloc(TT)
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TT);
+        ok(time != NULL, "psTimeAlloc() did not return NULL");
+        skip_start(time == NULL, 4, "Skipping tests because psTimeAlloc() failed");
+        ok(time->type == PS_TIME_TT, "psTimeAlloc() correctly set psTime->type");
+        ok(time->sec == 0, "psTimeAlloc() correctly set psTime->sec");
+        ok(time->nsec == 0, "psTimeAlloc() correctly set psTime->nsec");
+        ok(time->leapsecond == false, "psTimeAlloc() correctly set psTime->leapsecond");
+        psFree(time);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeGetNow()
+    // Attempt to get time with unallowed type
+    // Following should generate an error message for unallowed time type
+    {
+        psMemId id = psMemGetId();
+        psTime *timeNow = psTimeGetNow(-100);
+        ok(timeNow == NULL, "psTimeGetNow(-100) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeGetNow(TAI)
+    {
+        psMemId id = psMemGetId();
+        psTime *timeNow = psTimeGetNow(PS_TIME_TAI);
+        ok(timeNow != NULL, "psTimeGetNow() returned NULL");
+        skip_start(time == NULL, 4, "Skipping tests because psTimeGetNow() failed");
+        ok(timeNow->type == PS_TIME_TAI, "psTimeGetNow() correctly set psTime->type");
+        ok(timeNow->sec != 0, "psTimeAlloc() set psTime->sec to something");
+        ok(timeNow->nsec != 0, "psTimeAlloc() set psTime->nsec to something");
+        psFree(timeNow);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeGetUT1Delta()
+    // Attempt to convert NULL time
+    // psTimeGetUT1Delta() should generate an error message for NULL time
+    {
+        psMemId id = psMemGetId();
+        psF64 ut1Delta = psTimeGetUT1Delta(NULL, PS_IERS_B);
+        is_double(ut1Delta, NAN, "psTimeGetUT1Delta(NULL, PS_IERS_B) returned NAN");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Attempt to convert unallowed time
+    // Following should generate an error message for incorrect time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec = 1;
+        time->nsec = 2e9;
+        time->leapsecond = false;
+        psF64 ut1Delta = psTimeGetUT1Delta(time, PS_IERS_B);
+        is_double(ut1Delta, NAN, "psTimeGetUT1Delta() returned NAN for incorrect time");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Attempt to convert time with unallowed bulletin
+    // Following should generate an error message for incorrect bulletin
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec = 1;
+        time->nsec = 2;
+        time->leapsecond = false;
+        psF64 ut1Delta = psTimeGetUT1Delta(time, -100);
+        is_double(ut1Delta, NAN, "psTimeGetUT1Delta(time, -100) returned NAN (incorrect bulletin)");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Attempt to get delta with valid time and bulletin A
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec  = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        time->type = PS_TIME_UTC;
+        time->leapsecond = false;
+        psF64 ut1Delta = psTimeGetUT1Delta(time, PS_IERS_A);
+
+        is_double_tol(ut1Delta, testTime1UT1DeltaBullA, ERROR_TOL, "psTimeGetUT1Delta() produced the correct result: bulletin A");
+
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Attempt to get delta with valid time and bulletin B
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec  = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        time->type = PS_TIME_UTC;
+        psF64 ut1Delta = psTimeGetUT1Delta(time, PS_IERS_B);
+
+        is_double_tol(ut1Delta, testTime1UT1DeltaBullB, ERROR_TOL, "psTimeGetUT1Delta() produced the correct result: bulletin B");
+
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to get delta with valid time and bulletin A
+    // Following should generate a warning message predating table
+    // XXX: We don't test whether the warning message is generated
+    if (1) {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec = testTime2SecondsUTC;
+        time->nsec = testTime2NanosecondsUTC;
+        time->type = PS_TIME_UTC;
+        psF64 ut1Delta = psTimeGetUT1Delta(time,PS_IERS_A);
+
+        is_double_tol(ut1Delta, testTime2UT1DeltaBullA, ERROR_TOL, "psTimeGetUT1Delta() produced the correct result: bulletin B");
+
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to get delta with valid time and bulletin B
+    // Following should generate a warning message predating table
+    // XXX: We don't test whether the warning message is generated
+    if (1) {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec = testTime2SecondsUTC;
+        time->nsec = testTime2NanosecondsUTC;
+        time->type = PS_TIME_UTC;
+        psF64 ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
+
+        is_double_tol(ut1Delta, testTime2UT1DeltaBullB, ERROR_TOL, "psTimeGetUT1Delta() produced the correct result: bulletin B");
+
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to get delta with valid time and bulletin A
+    // Following should generate a warning message postdating table
+    // XXX: We don't test whether the warning message is generated
+    if (1) {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec  = testTime3SecondsUTC;
+        time->nsec = testTime3NanosecondsUTC;
+        time->type = PS_TIME_UTC;
+        psF64 ut1Delta = psTimeGetUT1Delta(time,PS_IERS_A);
+
+        is_double_tol(ut1Delta, testTime3UT1DeltaBullA, ERROR_TOL, "psTimeGetUT1Delta() produced the correct result: bulletin B");
+
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Attempt to get delta with valid time and bulletin B
+    // Following should generate a warning message postdating table
+    // XXX: We don't test whether the warning message is generated
+    if (1) {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec  = testTime3SecondsUTC;
+        time->nsec = testTime3NanosecondsUTC;
+        time->type = PS_TIME_UTC;
+        psF64 ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
+
+        is_double_tol(ut1Delta, testTime3UT1DeltaBullB, ERROR_TOL, "psTimeGetUT1Delta() produced the correct result: bulletin B");
+
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeToMJD()
+    // Attempt to convert with time NULL
+    // Following should generate an error message for NULL time
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psF64 mjd = psTimeToMJD(NULL);
+        is_double(mjd, NAN, "psTimeToMJD(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeToMJD()
+    // Attempt to convert incorrect time
+    // Following should generate an error message for incorrect time
+    // Following should generate an error message for NULL time
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = 1;
+        time->nsec = 2e9;
+        time->leapsecond = false;
+        psF64 mjd = psTimeToMJD(time);
+        is_double(mjd, NAN, "psTimeToMJD() returned NAN for incorrect time");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeToMJD()
+    // Check valid time conversion to MJD after 1/1/1970 epoch
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        psF64 mjd = psTimeToMJD(time);
+        is_double_tol(mjd, 53207.765929, ERROR_TOL, "psTimeToMJD() returned correct time after 1/1/1970 epoch");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeToMJD()
+    // Check valid time conversion to MJD before 1/1/1970 epoch
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime4SecondsUTC;
+        time->nsec = testTime4NanosecondsUTC;
+        psF64 mjd = psTimeToMJD(time);
+        is_double_tol(mjd, testTime4MJD, ERROR_TOL, "psTimeToMJD() returned correct time before 1/1/1970 epoch");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeToJD()
+    // Attempt to convert with time NULL
+    // Following should generate an error message for NULL time
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psF64 jd = psTimeToJD(NULL);
+        is_double(jd, NAN, "psTimeToJD(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeToJD()
+    // Attempt to convert incorrect time
+    // Following should generate an error message for incorrect time
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = 1;
+        time->nsec = 2e9;
+        time->leapsecond = false;
+        psF64 jd = psTimeToJD(time);
+        is_double(jd, NAN, "psTimeToJD() returned NAN for incorrect time");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeToJD()
+    // Check valid time conversion to MJD after 1/1/1970 epoch
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        psF64 jd = psTimeToJD(time);
+        is_double_tol(jd, 2453208.265929, ERROR_TOL, "psTimeToJD() returned the correct time after 1/1/1970 epoch");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeToJD()
+    // Check valid time conversion to MJD before 1/1/1970 epoch
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime4SecondsUTC;
+        time->nsec = testTime4NanosecondsUTC;
+        psF64 jd = psTimeToJD(time);
+        is_double_tol(jd, testTime4JD, ERROR_TOL, "psTimeToJD() returned the correct time before 1/1/1970 epoch");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeToISO()
+    // Attempt to convert with NULL time
+    // Following should generate error message for NULL time
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        char *timeStr = psTimeToISO(NULL);
+        ok(timeStr == NULL, "psTimeToISO(NULL) returned NULL");
+        psFree(timeStr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeToISO()
+    // Attempt to convert incorrect time
+    // Following should generate an error message for incorrect time
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->leapsecond = false;
+        time->sec = 1;
+        time->nsec = 2e9;
+        char *timeStr = psTimeToISO(time);
+        ok(timeStr == NULL, "psTimeToISO(time) returned NULL for incorrect time");
+        psFree(time);
+        psFree(timeStr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeToISO()
+    // Verify return NULL for time prior to year 0000
+    // Following should generate error message for time prior year 0000
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime5SecondsUTC;
+        time->nsec = testTime5NanosecondsUTC;
+        char *timeStr = psTimeToISO(time);
+        ok(timeStr == NULL, "psTimeToISO(time) returned NULL for time prior to year 0000");
+        psFree(time);
+        psFree(timeStr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeToISO()
+    // Verify return NULL for time after to year 9999
+    // Following should generate error message for time after year 9999
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->leapsecond = false;
+        time->sec = testTime6SecondsUTC;
+        time->nsec = testTime6NanosecondsUTC;
+        char *timeStr = psTimeToISO(time);
+        ok(timeStr == NULL, "psTimeToISO(time) returned NULL for time after year 9999");
+        psFree(time);
+        psFree(timeStr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeToISO()
+    // Verify return string with valid time
+    // XXX: These tests fail.  They used to succeed in early 2006, I think
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->leapsecond = false;
+        time->sec = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        char *timeStr = psTimeToISO(time);
+        is_str(timeStr, testTime1Str, "psTimeToISO(time) returned correct time (no leapsecond)");
+        psFree(timeStr);
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeToISO()
+    // Verify return string with valid time and leap second set
+    // XXX: These tests fail.  They used to succeed in early 2006, I think
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        time->leapsecond = true;
+        char *timeStr = psTimeToISO(time);
+        is_str(timeStr, testTime1StrLeap, "psTimeToISO(time) returned correct time (with leapsecond)");
+        psFree(timeStr);
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeToTimeval()
+    // Attempt to convert with NULL time
+    // Following should generate error message for NULL time
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        struct timeval *timevalTime = psTimeToTimeval(NULL);
+
+        ok(timevalTime == NULL, "psTimeToTimeval(NULL) returned NULL");
+
+        psFree(timevalTime);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeToTimeval()
+    // Attempt to convert incorrect time
+    // Following should generate an error message for incorrect time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->leapsecond = false;
+        time->sec = 1;
+        time->nsec = 2e9;
+        struct timeval *timevalTime = psTimeToTimeval(time);
+        ok(timevalTime == NULL, "psTimeToTimeval() returned NULL for incorrect time");
+        psFree(time);
+        psFree(timevalTime);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeToTimeval()
+    // Attempt to convert incorrect time
+    // Following should generate an error message for incorrect time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->leapsecond = false;
+        time->sec = -1;
+        time->nsec = 0;
+        struct timeval *timevalTime = psTimeToTimeval(time);
+        ok(timevalTime == NULL, "psTimeToTimeval() returned NULL for incorrect time");
+        psFree(time);
+        psFree(timevalTime);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeToTimeval()
+    // Verify convert to timeval with valid time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->leapsecond = false;
+        time->sec = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        struct timeval *timevalTime = psTimeToTimeval(time);
+        is_long(timevalTime->tv_sec, testTime1TimevalSec, "psTimeToTimeval()->tv_sec");
+        is_long(timevalTime->tv_usec, testTime1TimevalUsec, "psTimeToTimeval()->tv_usec");
+        psFree(time);
+        psFree(timevalTime);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromMJD()
+    // Attempt to convert valid time to psTime
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromMJD(testTime1MJDTAI);
+        ok(time->type == PS_TIME_TAI, "psTimeFromMJD() returned the correct type");
+        is_long(time->sec, testTime1SecondsTAI, "psTimeFromMJD()->sec");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromMJD()
+    // Attempt to convert valid time before 1970 epoch
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromMJD(testTime4MJD);
+        ok(time->type == PS_TIME_TAI, "psTimeFromMJD() returned the correct type");
+        is_long(time->sec, testTime4SecondsUTC, "psTimeFromMJD()->sec");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeFromJD()
+    // Attempt to convert valid time to psTime
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromJD(testTime1JDTAI);
+        ok(time->type == PS_TIME_TAI, "psTimeFromJD() returned the correct type");
+        is_long(time->sec, testTime1SecondsTAI, "psTimeFromJD()->sec");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeFromJD()
+    // Attempt to convert valid time before 1970 epoch
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromJD(testTime4JD);
+
+        ok(time->type == PS_TIME_TAI, "psTimeFromJD() returned the correct type");
+        is_long(time->sec, testTime4SecondsUTC, "psTimeFromJD() returned the correct ->sec");
+
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromISO()
+    // Convert valid ISO string
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromISO(testTime1Str, PS_TIME_TAI);
+        ok(time->type == PS_TIME_TAI, "psTimeFromISO() returned the correct type");
+        is_long(time->sec, testTime1SecondsUTC, "psTimeFromISO()->sec");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromISO()
+    // Attempt to convert NULL string
+    // Following should generate error message for NULL ISO string");
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromISO(NULL, PS_TIME_TAI);
+        ok(time == NULL, "psTimeFromISO(NULL, PS_TIME_TAI) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromISO()
+    // Attempt to convert incorrect ISO string
+    // Following should generate an error for incorrect ISO string");
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromISO("Here I am", PS_TIME_TAI);
+        ok(time == NULL, "psTimeFromISO() returned NULL for incorrect ISO string");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeFromTimeval()
+    // Attempt to create psTime from NULL timeval ptr
+    // Following should generate error message for NULL timeval
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromTimeval(NULL);
+        ok(time == NULL, "psTimeFromTimeval(NULL) returned NULL");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psTimeFromTimeval()
+    // Convert valid timeval structure
+    {
+        psMemId id = psMemGetId();
+        struct timeval *timevalTime = (struct timeval*)psAlloc(sizeof(struct timeval));
+        timevalTime->tv_sec = testTime1SecondsTAI;
+        timevalTime->tv_usec = testTime1NanosecondsTAI / 1000;
+        psTime *time = psTimeFromTimeval(timevalTime);
+        ok(time != NULL, "psTimeFromTimeval() returned non-NULL for correct timeval structure");
+        skip_start(time == NULL, 3, "Skipping tests because psTimeFromTimeval() returned NULL");
+        ok(time->type == PS_TIME_TAI, "psTimeFromTimeval() returned the correct type");
+        is_long(time->sec, testTime1SecondsTAI, "psTimeFromTimeval() returned the correct ->sec");
+        is_long(time->nsec, testTime1NanosecondsTAI, "psTimeFromTimeval() returned the correct ->nsec");
+        skip_end();
+        psFree(timevalTime);
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromTM()
+    // Attempt to convert from NULL tm structure ptr
+    // Following should generate error message for NULL tm ptr
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromTM(NULL);
+        ok(time == NULL, "psTimeFromTM(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromTM()
+    // Verify convert for valid tm structure
+    {
+        psMemId id = psMemGetId();
+        struct tm *tmTime = (struct tm*)psAlloc(sizeof(struct tm));
+        tmTime->tm_year = testTime7TmYear;
+        tmTime->tm_mon  = testTime7TmMon;
+        tmTime->tm_mday = testTime7TmDay;
+        tmTime->tm_hour = testTime7TmHour;
+        tmTime->tm_min  = testTime7TmMin;
+        tmTime->tm_sec  = testTime7TmSec;
+        psTime *time = psTimeFromTM(tmTime);
+        ok(time != NULL, "psTimeFromTM(NULL) returned non-NULL");
+        skip_start(time == NULL, 2, "Skipping tests because psTimeFromTM() returned NULL");
+        is_long(time->sec, testTime7Seconds, "psTimeFromTM() returned the correct ->sec");
+        is_long(time->nsec, testTime7Nanoseconds, "psTimeFromTM() returned the correct ->nsec");
+        skip_end();
+        psFree(tmTime);
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromTM()
+    // Verify convert for valid tm structure
+    {
+        psMemId id = psMemGetId();
+        struct tm *tmTime = (struct tm*)psAlloc(sizeof(struct tm));
+        tmTime->tm_year = testTime8TmYear;
+        tmTime->tm_mon  = testTime8TmMon;
+        tmTime->tm_mday = testTime8TmDay;
+        tmTime->tm_hour = testTime8TmHour;
+        tmTime->tm_min  = testTime8TmMin;
+        tmTime->tm_sec  = testTime8TmSec;
+        psTime *time = psTimeFromTM(tmTime);
+        // XXX should test all fields here
+        ok(time != NULL, "psTimeFromTM(NULL) returned non-NULL");
+        skip_start(time == NULL, 2, "Skipping tests because psTimeFromTM() returned NULL");
+        is_long(time->sec, testTime8Seconds, "psTimeFromTM() returned the correct ->sec");
+        is_long(time->nsec, testTime8Nanoseconds, "psTimeFromTM() returned the correct ->nsec");
+        skip_end();
+        psFree(tmTime);
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert NULL time
+    // Following should generate an error message for NULL time
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time2 = psTimeConvert(NULL, PS_TIME_TAI);
+
+        ok(time2 == NULL, "psTimeConvert(NULL, PS_TIME_TAI) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+
+        psFree(time2);
+    }
+
+
+    // psTimeConvert()
+    // Following should generate an error message for incorrect type output
+    // Input psTime struct is PS_TIME_TAI
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TAI);
+        psTime *time2 = psTimeConvert(time1,-100);
+        ok(time2 == NULL, "psTimeConvert(time1, -100) returned NULL");
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Following should generate an error message for incorrect type output
+    // Input psTime struct is PS_TIME_UTC
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        psTime *time2 = psTimeConvert(time1,-100);
+        ok(time2 == NULL, "psTimeConvert(time1, -100) returned NULL");
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Following should generate an error message for incorrect type
+    // Input psTime struct is PS_TIME_TT
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TT);
+        psTime *time2 = psTimeConvert(time1,-100);
+        ok(time2 == NULL, "psTimeConvert(time1, -100) returned NULL");
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Following should generate an error message for incorrect type input
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TAI);
+        time1->type = -100;
+        psTime *time2 = psTimeConvert(time1,PS_TIME_TAI);
+        ok(time2 == NULL, "psTimeConvert(time1, PS_TIME_TAI) returned NULL");
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert with incorrect time nsec > 1e9
+    // Following should generate an error message for incorrect time
+    // XXX: We don't test whether the error message is generated
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TAI);
+        time1->nsec = 2e9;
+        psTime *time2 = psTimeConvert(time1, PS_TIME_TAI);
+        ok(time2 == NULL, "psTimeConvert(time1, PS_TIME_TAI) returns NULL for incorrect psTime object");
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    //Attempt to convert a time to the same type
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TAI);
+        time1->sec = 1;
+        time1->nsec = 2;
+        time1->type = PS_TIME_TAI;
+        time1->leapsecond = false;
+        psTime *time2 = psTimeConvert(time1, PS_TIME_TAI);
+        ok(time2 == time1, "psTimeConvert(time, ...) returns time for conversion to same type");
+        is_long(time2->sec, 1, "time->sec");
+        is_long(time2->nsec, 2, "time->nsec");
+        ok(time2->type == PS_TIME_TAI, "time->type");
+        is_bool(time2->leapsecond, false, "time->leapsecond");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert a UTC time to a TAI
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        time1->leapsecond = false;
+
+        psTime *time2 = psTimeConvert(time1,PS_TIME_TAI);
+        ok(time2 == time1, "psTimeConvert(time, ...) returns time after conversion to a different type");
+        is_long(time2->sec, testTime1SecondsTAI, "psTimeConvert() returned the correct ->sec");
+        is_long(time2->nsec, testTime1NanosecondsTAI, "psTimeConvert() returned the correct ->nsec");
+        ok(time2->type == PS_TIME_TAI, "psTimeConvert() returned the correct type");
+        is_bool(time2->leapsecond, false, "time->leapsecond");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert a UTC time to a TT
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        time1->leapsecond = false;
+
+        psTime *time2 = psTimeConvert(time1,PS_TIME_TT);
+        ok(time2 == time1, "psTimeConvert(time, ...) returns time after conversion to a different type");
+        is_long(time2->sec, testTime1SecondsTT, "psTimeConvert() returned the correct ->sec");
+        is_long(time2->nsec, testTime1NanosecondsTT, "psTimeConvert() returned the correct ->nsec");
+        ok(time2->type == PS_TIME_TT, "psTimeConvert() returned the correct type");
+        is_bool(time2->leapsecond, false, "time->leapsecond");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert a UTC time to UT1
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        time1->leapsecond = false;
+
+        psTime *time2 = psTimeConvert(time1,PS_TIME_UT1);
+        ok(time2 == time1, "psTimeConvert(time, ...) returns time after conversion to a different type");
+        is_long(time2->sec, testTime1SecondsUT1, "psTimeConvert() returned the correct ->sec");
+        is_long(time2->nsec, testTime1NanosecondsUT1, "psTimeConvert() returned the correct ->nsec");
+        ok(time2->type == PS_TIME_UT1, "psTimeConvert() returned the correct type");
+        is_bool(time2->leapsecond, false, "time->leapsecond");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psTimeConvert()
+    // Attempt to convert a TAI time to UTC
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TAI);
+        time1->sec = testTime1SecondsTAI;
+        time1->nsec = testTime1NanosecondsTAI;
+        time1->leapsecond = false;
+
+        psTime *time2 = psTimeConvert(time1,PS_TIME_UTC);
+        ok(time2 == time1, "psTimeConvert(time, ...) returns time after conversion to a different type");
+        is_long(time2->sec, testTime1SecondsUTC, "psTimeConvert() returned the correct ->sec");
+        is_long(time2->nsec, testTime1NanosecondsUTC, "psTimeConvert() returned the correct ->nsec");
+        ok(time2->type == PS_TIME_UTC, "psTimeConvert() returned the correct type");
+        is_bool(time2->leapsecond, false, "time->leapsecond");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert a TAI time to TT
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TAI);
+        time1->sec = testTime1SecondsTAI;
+        time1->nsec = testTime1NanosecondsTAI;
+        time1->type = PS_TIME_TAI;
+        time1->leapsecond = false;
+
+        psTime *time2 = psTimeConvert(time1,PS_TIME_TT);
+        ok(time2 == time1, "psTimeConvert() returned time for conversion to same type");
+        is_long(time2->sec, testTime1SecondsTT, "psTimeConvert() returned the correct ->sec");
+        is_long(time2->nsec, testTime1NanosecondsTT, "psTimeConvert() returned the correct ->nsec");
+        ok(time2->type == PS_TIME_TT, "psTimeConvert() returned the correct type");
+        is_bool(time2->leapsecond, false, "time->leapsecond");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert a TAI time to UT1
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TAI);
+        time1->sec = testTime1SecondsTAI;
+        time1->nsec = testTime1NanosecondsTAI;
+        time1->leapsecond = false;
+
+        psTime *time2 = psTimeConvert(time1,PS_TIME_UT1);
+        ok(time1 == time2, "psTimeConvert() returned time for conversion to same type");
+        is_long(time2->sec, testTime1SecondsUT1, "psTimeConvert() returned the correct ->sec");
+        is_long(time2->nsec, testTime1NanosecondsUT1, "psTimeConvert() returned the correct ->nsec");
+        ok(time2->type == PS_TIME_UT1, "psTimeConvert() returned the correct type");
+        is_bool(time2->leapsecond, false, "time->leapsecond");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert a TT time to UTC
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TT);
+        time1->sec = testTime1SecondsTT;
+        time1->nsec = testTime1NanosecondsTT;
+        time1->leapsecond = false;
+
+        psTime *time2 = psTimeConvert(time1,PS_TIME_UTC);
+        ok(time2 == time1, "psTimeConvert() returned time for conversion to same type");
+        is_long(time2->sec, testTime1SecondsUTC, "psTimeConvert() returned the correct ->sec");
+        is_long(time2->nsec, testTime1NanosecondsUTC, "psTimeConvert() returned the correct ->nsec");
+        ok(time2->type == PS_TIME_UTC, "psTimeConvert() returned the correct type");
+        is_bool(time2->leapsecond, false, "time->leapsecond");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert a TT time to TAI
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TT);
+        time1->sec = testTime1SecondsTT;
+        time1->nsec = testTime1NanosecondsTT;
+        time1->leapsecond = false;
+
+        psTime *time2 = psTimeConvert(time1,PS_TIME_TAI);
+        ok(time2 == time1, "psTimeConvert() returned time for conversion to same type");
+        is_long(time2->sec, testTime1SecondsTAI, "psTimeConvert() returned the correct ->sec");
+        is_long(time2->nsec, testTime1NanosecondsTAI, "psTimeConvert() returned the correct ->nsec");
+        ok(time2->type == PS_TIME_TAI, "psTimeConvert() returned the correct type");
+        is_bool(time2->leapsecond, false, "time->leapsecond");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert a TT time to UT1
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_TT);
+        time1->sec = testTime1SecondsTT;
+        time1->nsec = testTime1NanosecondsTT;
+        time1->leapsecond = false;
+
+        psTime *time2 = psTimeConvert(time1,PS_TIME_UT1);
+        ok(time1 == time2, "psTimeConvert() returned time for conversion to same type");
+        is_long(time2->sec, testTime1SecondsUT1, "psTimeConvert() returned the correct ->sec");
+        is_long(time2->nsec, testTime1NanosecondsUT1, "psTimeConvert() returned the correct ->nsec");
+        ok(time2->type == PS_TIME_UT1, "psTimeConvert() returned the correct type");
+        is_bool(time2->leapsecond, false, "time->leapsecond");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // Attempt to convert from UT1 to TAI, UTC, TT
+    // Following should generate an error message converting from UT1");
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UT1);
+        time1->sec = 1;
+        time1->nsec = 2;
+        time1->leapsecond = false;
+
+        psTime *time2 = psTimeConvert(time1, PS_TIME_UTC);
+        ok(time2 == NULL, "psTimeConvert() returned NULL for conversion from UT1");
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_02.c	(revision 22322)
@@ -0,0 +1,407 @@
+/** @file  tst_psTime_02.c
+ *
+ *  @brief Test driver for psTime functions
+ *
+ *  This test driver contains the following tests for psTime:
+ *     1) Convert psTime to Local Mean Sidereal Time (LMST)
+ *     2) Calculate leap second delta between times
+ *     3) Creation of psTime of type TT
+ *     4) Creation of psTime of type UTC
+ *
+ *  @author  Ross Harman, MHPCC
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.6 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-06-05 01:10:22 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+#define ERROR_TOL  0.001
+// Test Time 1 : May 9, 2005  00:00:00,0
+//               MJD = 53499.000
+//               JD = 2453499.5
+// UTC Test Time 1
+const psS64 testTime1SecondsUTC     = 1115596900;
+const psU32 testTime1NanosecondsUTC = 0;
+// Expected LMST  15:09:18
+const psF64 testTime1LMST0          = 3.967604;
+
+// Test Time 2 : May 9, 1995 00:00:00,0
+//               MJD = 49846.00
+//               JD = 2449846.5
+// UTC Test Time 2
+const psS64 testTime2SecondsUTC     = 799977600;
+const psU32 testTime2NanosecondsUTC = 0;
+// Expected leap second delta
+const psS64 testTimeLeapSecondDelta1 = 3;
+
+// Test Time 3: Jan 1, 1999 00:00:00,0
+//              MJD = 51179
+//              JD = 2451179.5
+const psS64 testTime3SecondsUTC     = 915148800;
+const psU32 testTime3NanosecondsUTC = 0;
+
+int main(int argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+    psLogSetFormat("HLNM");
+    plan_tests(54);
+    psLibInit("pslib.config");
+
+    // psTimeToLMST()
+    // Attempt to get LMST with NULL time
+    // Following should generate an error message for NULL time
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psF64 lmst = psTimeToLMST(NULL, 0);
+        is_double(lmst, NAN, "psTimeToLMST(NULL, 0) returned NAN");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeToLMST()
+    // Attempt to get LMST with valid test time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        time->leapsecond = false;
+        psF64 lmst = psTimeToLMST(time, 0.0);
+        is_double_tol(lmst, testTime1LMST0, ERROR_TOL, "psTimeToLMST() returned the correct time");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeToLMST()
+    // Attempt to get LMST with unallowed input time UT1
+    // Following should generate error message for incorrect type
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        time->leapsecond = false;
+        time->type = PS_TIME_UT1;
+        psF64 lmst = psTimeToLMST(time,0.0);
+        is_double(lmst, NAN, "psTimeToLMST() generated a NAN for incorrect type");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeLeapSecondDelta()
+    // Attempt to get delta with NULL time1 argument
+    // Following should generate an error message for NULL time
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psS64 delta = psTimeLeapSecondDelta(NULL, NULL);
+        is_long(delta, 0, "psTimeLeapSecondDelta(NULL, NULL) returned 0");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeLeapSecondDelta()
+    // Set test time 1
+    // Attempt to get delta with NULL time2 argument
+    // Following should generate an error message for NULL time
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        time1->leapsecond = false;
+        psS64 delta = psTimeLeapSecondDelta(time1, NULL);
+        is_long(delta, 0, "psTimeLeapSecondDelta(time1, NULL) returned 0");
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeLeapSecondDelta()
+    // Set test time 2 with unallowed time
+    // Attempt to get delta with unallowed time2
+    // Following should generate an error message for unallowed time
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        time1->leapsecond = false;
+        psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+        time2->sec = 0;
+        time2->nsec = 2e9;
+        time2->leapsecond = false;
+        psS64 delta = psTimeLeapSecondDelta(time1, time2);
+        is_long(delta, 0, "psTimeLeapSecondDelta(time1, time2) returned 0 with incorrect time2");
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeLeapSecondDelta()
+    // Set test time 2 valid
+    // Attempt to get delta with unallowed time1
+    // Following should generate an error message for unallowed time
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->leapsecond = false;
+        psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+        time2->leapsecond = false;
+        time2->sec = testTime2SecondsUTC;
+        time2->nsec = testTime2NanosecondsUTC;
+        // Set test time 1 incorrect
+        time1->sec = 0;
+        time1->nsec = 2e9;
+        psS64 delta = psTimeLeapSecondDelta(time1,time2);
+        is_long(delta, 0, "psTimeLeapSecondDelta(time1, time2) returned 0 with incorrect time1");
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeLeapSecondDelta()
+    // Set test time 1 to greater time
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        time1->leapsecond = false;
+        psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+        time2->leapsecond = false;
+        time2->sec = testTime2SecondsUTC;
+        time2->nsec = testTime2NanosecondsUTC;
+        psS64 delta = psTimeLeapSecondDelta(time1,time2);
+        is_long(delta, testTimeLeapSecondDelta1, "psTimeLeapSecondDelta() produced the correct result");
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeLeapSecondDelta()
+    // Set test time 1 to lesser time
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        time1->leapsecond = false;
+        psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+        time2->sec = testTime2SecondsUTC;
+        time2->nsec = testTime2NanosecondsUTC;
+        psS64 delta = psTimeLeapSecondDelta(time2, time1);
+        is_long(delta, testTimeLeapSecondDelta1, "psTimeLeapSecondDelta() produced the correct result");
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeLeapSecondDelta()
+    // Attempt to get delta with times equal
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        time1->leapsecond = false;
+        psS64 delta = psTimeLeapSecondDelta(time1,time1);
+        is_long(delta, 0, "psTimeLeapSecondDelta() produced the correct result");
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeIsLeapSecond()
+    // Attempt to determine if leap second with NULL time
+    // Following should generate an error message for NULL time
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        bool leapsecond = psTimeIsLeapSecond(NULL);
+        is_bool(leapsecond, false, "psTimeIsLeapSecond(NULL) returned correct value");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeIsLeapSecond()
+    // Attempt to determine if leap second with non-UTC time
+    // Following should generate an error message for unallowed type
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        time->leapsecond = false;
+        bool leapsecond = psTimeIsLeapSecond(time);
+        is_bool(leapsecond, false, "psTimeIsLeapSecond() returned false with incorrect type");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeIsLeapSecond()
+    // Set time to UTC
+    // Attempt to determine if leap second with valid time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        time->leapsecond = false;
+        time->type = PS_TIME_UTC;
+        bool leapsecond = psTimeIsLeapSecond(time);
+        is_bool(leapsecond, false, "psTimeIsLeapSecond() returned false");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeIsLeapSecond()
+    // Set time to UTC with leap second
+    // Note: leapseconds are only relevent for UTC
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime3SecondsUTC;
+        time->nsec = testTime3NanosecondsUTC;
+        bool leapsecond = psTimeIsLeapSecond(time);
+        is_bool(leapsecond, true, "psTimeIsLeapSecond() returned true");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeIsLeapSecond()
+    // Set time to 1 second before a known leap second
+    // Note: leapseconds are only relevent for UTC
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime3SecondsUTC;
+        time->nsec = testTime3NanosecondsUTC;
+        time->sec--;
+        bool leapsecond = psTimeIsLeapSecond(time);
+        is_bool(leapsecond, false, "psTimeIsLeapSecond() returned false");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeIsLeapSecond()
+    // Set time to 1 second after a known leap second
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec = testTime3SecondsUTC;
+        time->nsec = testTime3NanosecondsUTC;
+        time->sec++;
+        bool leapsecond = psTimeIsLeapSecond(time);
+        is_bool(leapsecond, false, "psTimeIsLeapSecond() returned false");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromTT()
+    // Attempt to create psTime with unallowed time
+    // Following should generate an error message for unallowed time
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromTT(0,2e9);
+        ok(time == NULL, "psTimeFromTT() returned NULL with incorrect time");
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromTT()
+    // Attempt to create psTime with valid time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromTT(testTime1SecondsUTC,testTime1NanosecondsUTC);
+        ok(time != NULL, "psTimeFromTT() returned non-NULL with valid time");
+        skip_start(time == NULL, 2, "Skipping tests because psTimeFromTT() returned NULL");
+        ok(time->type == PS_TIME_TT, "psTimeFromTT() returned correct type");
+        is_long(time->sec, testTime1SecondsUTC, "psTimeFromTT() returned correct ->sec"); 
+        is_long(time->nsec, testTime1NanosecondsUTC, "psTimeFromTT() returned correct ->nsec");
+        is_bool(time->leapsecond, false, "psTimeFromTT() returned the correct leapsecond flag");
+        skip_end();
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromUTC()
+    // Attempt to create psTime with unallowed time
+    // Following should generate an error message for unallowed time
+    // XXX: We do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromUTC(0, 2e9, true);
+        ok(time == NULL, "psTimeFromUTC() returned NULL with incorrect time input");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromUTC()
+    // Attempt to create psTime with valid leapsecond time but leapsecond
+    // flag false
+    {
+        psMemId id = psMemGetId();
+
+        psTime *time = psTimeFromUTC(testTime3SecondsUTC, testTime3NanosecondsUTC, false);
+        ok(time != NULL, "psTimeFromUTC() returned non-NULL with correct input");
+        skip_start(time == NULL, 5, "Skipping tests because psTimeFromUTC() returned NULL");
+        ok(time->type == PS_TIME_UTC, "psTimeFromUTC() returned the correct type");
+        is_long(time->sec, testTime3SecondsUTC, "psTimeFromUTC() returned the correct ->sec");
+        is_long(time->nsec, testTime3NanosecondsUTC, "psTimeFromUTC() returned the correct ->nsec");
+        is_bool(time->leapsecond, true, "psTimeFromUTC() returned the correct leapsecond flag");
+        psFree(time);
+        skip_end();
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeFromUTC()
+    // Attempt to create psTime with valid non-leapsecond time and
+    // leapsecond flag true
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeFromUTC(testTime1SecondsUTC, testTime1NanosecondsUTC, true);
+        ok(time != NULL, "psTimeFromUTC() returned non-NULL with correct input");
+        skip_start(time == NULL, 3, "Skipping tests because psTimeFromUTC() returned NULL");
+        ok(time->type == PS_TIME_UTC, "psTimeFromUTC() returned the correct type");
+        is_long(time->sec, testTime1SecondsUTC, "psTimeFromUTC() returned the correct ->sec and ->nsec");
+        is_long(time->nsec, testTime1NanosecondsUTC, "psTimeFromUTC() returned the correct ->sec and ->nsec");
+        is_bool(time->leapsecond, false, "psTimeFromUTC() returned the correct leapsecond flag");
+        skip_end();
+
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_03.c	(revision 22322)
@@ -0,0 +1,517 @@
+/** @file  tst_psTime_03.c
+ *
+ *  @brief Test driver for psTime functions
+ *
+ *  This test driver contains the following tests for psTime:
+ *   1) psTimeMath invalid times
+ *   2) psTimeMath valid time of different types
+ *   3) psTimeDelta valid times with different types
+ *
+ *  @author  Ross Harman, MHPCC
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.7 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-06-21 23:53:33 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define ERROR_TOL    0.001
+
+// Test Time 1 : May 9, 2005 00:00:00,0
+//               MJD = 53499.00
+//               JD = 2453499.5
+// UTC Test Time 1
+const psS64 testTime1SecondsUTC         = 1115596900;
+const psU32 testTime1NanosecondsUTC     = 0;
+// TAI Test Time 1
+const psS64 testTime1SecondsTAI         = 1115596932;
+const psU32 testTime1NanosecondsTAI     = 0;
+// TT Test Time 1
+const psS64 testTime1SecondsTT          = 1115596964;
+const psU32 testTime1NanosecondsTT      = 184000000;
+// UT1 Test Time 1
+const psS64 testTime1SecondsUT1         = 1115596900;
+const psU32 testTime1NanosecondsUT1     = 184000000;
+// Delta 1
+const psF64 deltaTime1                  = -15.5;
+// Expected UTC Time 1
+const psS64 newTestTime1SecondsUTC      = 1115596884;
+const psU32 newTestTime1NanosecondsUTC  = 500000000;
+// Expected TAI Time 1
+const psS64 newTestTime1SecondsTAI      = 1115596916;
+const psU32 newTestTime1NanosecondsTAI  = 500000000;
+// Delta 2
+const psF64 deltaTime2                  = 123.066;
+// Expected TT Time 1 w/ delta 2
+const psS64 newTestTime1SecondsTT       = 1115597087;
+const psU32 newTestTime1NanosecondsTT   = 250000000;
+// Expected UT1 Time 1 w/ delta 2
+const psS64 newTestTime1SecondsUT1      = 1115597023;
+const psU32 newTestTime1NanosecondsUT1  = 250000000;
+
+// Test Time 2 : Dec. 31 1998 23:59:45,0
+//               MJD = 51178.99983
+//               JD = 2451179.49983
+// UTC Test Time 1
+const psS64 testTime2SecondsUTC         = 915148785;
+const psU32 testTime2NanosecondsUTC     = 0;
+// Delta 3
+const psF64 deltaTime3                    = 30.0;
+// Expected UTC Time
+const psS64 newTestTime2SecondsUTC      = 915148814;
+const psU32 newTestTime2NanosecondsUTC  = 0;
+
+// Appendix B time conversion tests
+//
+#define APPB_TESTS    8
+const psS64 testTimeBSeconds[APPB_TESTS] =
+    {
+        915148829,
+        915148829,
+        915148830,
+        915148830,
+        915148831,
+        915148831,
+        915148832,
+        915148832
+    };
+const psU32 testTimeBNanoseconds[APPB_TESTS] =
+    {
+        0,
+        500000000,
+        0,
+        500000000,
+        0,
+        500000000,
+        0,
+        500000000
+    };
+const psBool testTimeBLeapsecond[APPB_TESTS] =
+    {
+        false,
+        false,
+        false,
+        false,
+        true,
+        true,
+        false,
+        false
+    };
+// Expected results
+const char* testTimeBStrUTC[APPB_TESTS] =
+    {
+        "1998-12-31T23:59:58.0Z",
+        "1998-12-31T23:59:58.5Z",
+        "1998-12-31T23:59:59.0Z",
+        "1998-12-31T23:59:59.5Z",
+        "1998-12-31T23:59:60.0Z",
+        "1998-12-31T23:59:60.5Z",
+        "1999-01-01T00:00:00.0Z",
+        "1999-01-01T00:00:00.5Z"
+    };
+const char* testTimeBStrTAI[APPB_TESTS] =
+    {
+        "1999-01-01T00:00:29.0Z",
+        "1999-01-01T00:00:29.5Z",
+        "1999-01-01T00:00:30.0Z",
+        "1999-01-01T00:00:30.5Z",
+        "1999-01-01T00:00:31.0Z",
+        "1999-01-01T00:00:31.5Z",
+        "1999-01-01T00:00:32.0Z",
+        "1999-01-01T00:00:32.5Z"
+    };
+const char* testTimeBStrTT[APPB_TESTS] =
+    {
+        "1999-01-01T00:01:01.1Z",
+        "1999-01-01T00:01:01.6Z",
+        "1999-01-01T00:01:02.1Z",
+        "1999-01-01T00:01:02.6Z",
+        "1999-01-01T00:01:03.1Z",
+        "1999-01-01T00:01:03.6Z",
+        "1999-01-01T00:01:04.1Z",
+        "1999-01-01T00:01:04.6Z"
+    };
+const char* testTimeBStrUT1[APPB_TESTS] =
+    {
+        "1998-12-31T23:59:58.7Z",
+        "1998-12-31T23:59:59.2Z",
+        "1998-12-31T23:59:59.7Z",
+        "1998-12-31T23:59:60.2Z",
+        "1998-12-31T23:59:60.7Z",
+        "1999-01-01T00:00:00.2Z",
+        "1999-01-01T00:00:00.7Z",
+        "1999-01-01T00:00:01.2Z"
+    };
+
+// Test Time B1 : Dec 31, 1998 23:59:58,0
+//                MJD = 51178.99998
+//                JD = 2451179.49998
+//const psS64 testTimeB1SecondsUTC        = 915148798;
+//const psU32 testTimeB1NanosecondsUTC    = 0;
+// Expected ISO times
+//const char testTimeB1StrUTC[] = "1998-12-31T23:59:58,0Z";
+//const char testTimeB1StrTAI[] = "1999-01-01T00:00:29,0Z";
+//const char testTimeB1StrTT[]  = "1999-01-01T00:01:01,1Z";
+//const char testTimeB1StrUT1[] = "1998-12-31T23:59:57,7Z";
+//
+// Test Time B2 : Dec 31, 1998 23:59:58,5
+//
+//
+//const psS64 testTimeB2SecondsUTC       = 915148798;
+//const psU32 testTimeB2NanosecondsUTC   = 500000000;
+// Expected ISO times
+//const char testTimeB2StrUTC[] = "1998-12-31T23:59:58,5Z";
+//const char testTimeB2StrTAI[] = "1991-01-01T00:00:29,5Z";
+//const char testTimeB2StrTT[]  = "1999-01-01T00:01:01,6Z";
+//const char testTimeB2StrUT1[] = "1998-12-31T23:59:58,2Z";
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(77);
+    psLibInit("pslib.config");
+
+
+    // psTimeMath()
+    // Attempt to perform math operation on NULL time
+    // Following should generate error message for NULL time
+    // XXX: We do not test error generation here
+    {
+        psMemId id = psMemGetId();
+        ok(psTimeMath(NULL, -1.1) == NULL, "psTimeMath(NULL, -1.1) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeMath()
+    // Set up input time with invalid nanoseconds
+    // Following should generate error message for invalid time
+    // XXX: We do not test error generation here
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = 0;
+        time->nsec = 2e9;
+        ok(psTimeMath(time, -1.1) == NULL, "psTimeMath() returns NULL for unallowable time input");
+        psFree(time);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeMath()
+    // Set up input time with valid time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime1SecondsUTC;
+        time->nsec = testTime1NanosecondsUTC;
+        psTime *newTime = psTimeMath(time, deltaTime1);
+        ok(newTime != NULL, "psTimeMath() returns non-NULL for allowable time input (PS_TIME_UTC)");
+        skip_start(newTime == NULL, 2, "Skipping tests because psTimeMath() returned NULL");
+        ok(newTime->type == PS_TIME_UTC, "psTimeMath() returns the correct type (PS_TIME_UTC)");
+        is_long(newTime->sec, newTestTime1SecondsUTC, "psTimeMath() returns the correct ->sec");
+        is_long(newTime->nsec, newTestTime1NanosecondsUTC, "psTimeMath() returns the correct ->nsec");
+        skip_end();
+        psFree(newTime);
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeMath()
+    // Set up input time with valid TAI time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TAI);
+        time->sec = testTime1SecondsTAI;
+        time->nsec = testTime1NanosecondsTAI;
+
+        psTime *newTime = psTimeMath(time, deltaTime1);
+        ok(newTime != NULL, "psTimeMath() returns non-NULL for allowable time input (PS_TIME_TAI)");
+        skip_start(newTime == NULL, 2, "Skipping tests because psTimeMath() returned NULL");
+        ok(newTime->type == PS_TIME_TAI, "psTimeMath() returns the correct type (PS_TIME_TAI)");
+        is_long(newTime->sec, newTestTime1SecondsTAI, "psTimeMath() returns the correct ->sec");
+        is_long(newTime->nsec, newTestTime1NanosecondsTAI, "psTimeMath() returns the correct ->nsec");
+        skip_end();
+
+        psFree(newTime);
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeMath()
+    // Set up input time with valid TT time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_TT);
+        time->sec = testTime1SecondsTT;
+        time->nsec = testTime1NanosecondsTT;
+
+        psTime *newTime = psTimeMath(time,deltaTime2);
+        ok(newTime != NULL, "psTimeMath() returns non-NULL for allowable time input (PS_TIME_TT)");
+        skip_start(newTime == NULL, 2, "Skipping tests because psTimeMath() returned NULL");
+        ok(newTime->type == PS_TIME_TT, "psTimeMath() returns the correct type (PS_TIME_TT)");
+        is_long(newTime->sec, newTestTime1SecondsTT, "psTimeMath() returns the correct ->sec (PS_TIME_TT)");
+        is_long(newTime->nsec, newTestTime1NanosecondsTT, "psTimeMath() returns the correct ->nsec (PS_TIME_TT)");
+        skip_end();
+
+        psFree(newTime);
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeMath()
+    // Set up input time with valid TT time
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UT1);
+        time->sec = testTime1SecondsUT1;
+        time->nsec = testTime1NanosecondsUT1;
+
+        psTime *newTime = psTimeMath(time,deltaTime2);
+        ok(newTime != NULL, "psTimeMath() returns non-NULL for allowable time input (PS_TIME_UT1)");
+        skip_start(newTime == NULL, 2, "Skipping tests because psTimeMath() returned NULL");
+        ok(newTime->type == PS_TIME_UT1, "psTimeMath() returns the correct type (PS_TIME_UT1)");
+        is_long(newTime->sec, newTestTime1SecondsUT1, "psTimeMath() returns the correct ->sec (PS_TIME_UT1)");
+        is_long(newTime->nsec, newTestTime1NanosecondsUT1, "psTimeMath() returns the correct ->nsec (PS_TIME_UT1)");
+        skip_end();
+
+        psFree(newTime);
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeMath()
+    // Set up input time with valid UTC time and delt which crosses leap second
+    {
+        psMemId id = psMemGetId();
+        psTime *time = psTimeAlloc(PS_TIME_UTC);
+        time->sec = testTime2SecondsUTC;
+        time->nsec = testTime2NanosecondsUTC;
+
+        psTime *newTime = psTimeMath(time, deltaTime3);
+        ok(newTime != NULL, "psTimeMath() returns non-NULL for allowable time input (PS_TIME_UTC)");
+        skip_start(newTime == NULL, 2, "Skipping tests because psTimeMath() returned NULL");
+        ok(newTime->type == PS_TIME_UTC, "psTimeMath() returns the correct type (PS_TIME_UTC)");
+        is_long(newTime->sec, newTestTime2SecondsUTC, "psTimeMath() returns the correct ->sec (PS_TIME_UTC)");
+        is_long(newTime->nsec, newTestTime2NanosecondsUTC, "psTimeMath() returns the correct ->nsec (PS_TIME_UTC)");
+        skip_end();
+
+        psFree(newTime);
+        psFree(time);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeDelta()
+    // Attempt to get delta with time1 NULL
+    // Following should generate error message for NULL time
+    // XXX: We do not test error generation here
+    {
+        psMemId id = psMemGetId();
+
+        psF64 delta = psTimeDelta(NULL, NULL);
+        is_double_tol(delta, 0.0, ERROR_TOL, "psTimeDelta(NULL, NULL) returned 0.0");
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeDelta()
+    // Attempt to get delta with time2 NULL
+    // Following should generate error message for NULL time
+    // XXX: We do not test error generation here
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+
+        psF64 delta = psTimeDelta(time1, NULL);
+        is_double_tol(delta, 0.0, ERROR_TOL, "psTimeDelta(time1, NULL) returned 0.0");
+
+        psFree(time1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeDelta()
+    // Attempt to get delta with time1 invalid
+    // Following should generate error message for invalid time
+    // XXX: We do not test error generation here
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = 0;
+        time1->nsec = 2e9;
+
+        psF64 delta = psTimeDelta(time1, time2);
+        is_double_tol(delta, 0.0, ERROR_TOL, "psTimeDelta(time1, NULL) returned 0.0 for unallowed time1 arg");
+
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeDelta()
+    // Attempt to get delta with time2 invalid
+    // Following should generate error message for invalid time
+    // XXX: We do not test error generation here
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+        time2->sec = 0;
+        time2->nsec = 2e9;
+
+        psF64 delta = psTimeDelta(time1, time2);
+        is_double_tol(delta, 0.0,  ERROR_TOL, "psTimeDelta(time1, NULL) returned 0.0 for unallowed time2 arg");
+
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeDelta()
+    // Set time 2 valid but different type
+    // Attempt to get delta with different time types
+    // Following should generate error message for incorrect type
+    // XXX: We do not test error generation here
+    // XXX: this currently fails; probably because specs have changed
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        psTime *time2 = psTimeAlloc(PS_TIME_TAI);
+        time2->sec = newTestTime1SecondsUTC;
+        time2->nsec = newTestTime1NanosecondsUTC;
+
+        psF64 delta = psTimeDelta(time1, time2);
+        is_double_tol(delta, 0.0, ERROR_TOL, "psTimeDelta(time1, NULL) returned 0.0 for different time types");
+
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeDelta()
+    // Set time 2 to same as time 1
+    // Attempt to get delta with valid times of the same type
+    // XXX: this currently fails; probably because specs have changed
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsUTC;
+        time1->nsec = testTime1NanosecondsUTC;
+        psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+        time2->sec = newTestTime1SecondsUTC;
+        time2->nsec = newTestTime1NanosecondsUTC;
+
+        psF64 delta = psTimeDelta(time2, time1);
+        is_double_tol(delta, deltaTime1, ERROR_TOL, "psTimeDelta(time1, NULL) returned correct delta for times of the same type");
+
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeDelta()
+    // Set time 1 and 2 as different times with same type
+    // Attempt to get delta with valid times of the same type
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime1SecondsTT;
+        time1->nsec = testTime1NanosecondsTT;
+        time1->type = PS_TIME_TT;
+        psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+        time2->sec = newTestTime1SecondsTT;
+        time2->nsec = newTestTime1NanosecondsTT;
+        time2->type = PS_TIME_TT;
+
+        psF64 delta = psTimeDelta(time2,time1);
+        is_double_tol(delta, deltaTime2, ERROR_TOL, "psTimeDelta(time1, NULL) returned correct delta for times of the same type");
+
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeDelta()
+    // Set time 1 and 2 as different times with same type
+    // Attempt to get delta with valid times of the same type
+    {
+        psMemId id = psMemGetId();
+        psTime *time1 = psTimeAlloc(PS_TIME_UTC);
+        time1->sec = testTime2SecondsUTC;
+        time1->nsec = testTime2NanosecondsUTC;
+        time1->type = PS_TIME_UTC;
+        psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+        time2->sec = newTestTime2SecondsUTC;
+        time2->nsec = newTestTime2NanosecondsUTC;
+        time2->type = PS_TIME_UTC;
+        
+        psF64 delta = psTimeDelta(time2,time1);
+        is_double_tol(delta, deltaTime3, ERROR_TOL, "psTimeDelta(time1, NULL) returned correct delta for times of the same type");
+
+        psFree(time1);
+        psFree(time2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psTimeConvert()
+    // XXX: This currently produces lots of errors
+    {
+        for(psS32 i = 0; i < APPB_TESTS; i++)
+        {
+            // Initialize time for test time B1
+            psTime *time = psTimeAlloc(PS_TIME_TAI);
+            time->sec = testTimeBSeconds[i];
+            time->nsec = testTimeBNanoseconds[i];
+
+            // Verify TAI ISO string
+            char *timeStr = psTimeToISO(time);
+            is_str(timeStr, testTimeBStrTAI[i], "TAI ISO string");
+            psFree(timeStr);
+
+            time = psTimeConvert(time, PS_TIME_TT);
+            timeStr = psTimeToISO(time);
+            is_str(timeStr, testTimeBStrTT[i], "TT ISO string");
+            psFree(timeStr);
+
+            // Verify UTC ISO string
+            time = psTimeConvert(time, PS_TIME_UTC);
+            time->leapsecond = testTimeBLeapsecond[i];
+            timeStr = psTimeToISO(time);
+            is_str(timeStr, testTimeBStrUTC[i], "UTC ISO string");
+            psFree(timeStr);
+
+            time = psTimeConvert(time, PS_TIME_UT1);
+            timeStr = psTimeToISO(time);
+            is_str(timeStr, testTimeBStrUT1[i], "UT1 ISO string");
+            psFree(timeStr);
+
+            psFree(time);
+        }
+
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_04.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_04.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tap_psTime_04.c	(revision 22322)
@@ -0,0 +1,146 @@
+/** @file  tst_psTime_04.c
+ *
+ *  @brief Test driver for psTime functions
+ *
+ *  This test driver contains the following tests for psTime:
+ *      Test A - Initialize time
+ *      Test B - Attempt to open non-existant time config file
+ *      Test C - Attempt to open non-existant time data files
+ *      Test D - Attempt to read incorrect number of files
+ *      Test E - Attempt to read incorrect number of from values
+ *      Test F - Attempt to read data file with typo in number
+ *      Test G - Free data
+ *      Test H - Attempt to use all timer functions
+ *      Test I - Tidal Corrections to UT1-UTC
+ *
+ *  @author  Ross Harman, MHPCC
+ *  @author  Eric Van Alst, MHPCC
+ *  @author  David Robbins, MHPCC
+ *
+ *  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-06-04 20:25:32 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(19);
+
+
+    // Test A - Initialize time
+    // XXX: Noting is actually verified here
+    {
+        psMemId id = psMemGetId();
+        psLibInit("pslib.config");
+        psLibFinalize();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test B - Attempt to open non-existant time config file
+    // XXX: Noting is actually verified here
+    {
+        psMemId id = psMemGetId();
+        psLibInit("zzz");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test C - Attempt to open non-existant time data files
+    // XXX: Noting is actually verified here
+    {
+        psMemId id = psMemGetId();
+        psLibInit("test.psTime.config1");
+        psLibFinalize();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test D - Attempt to read incorrect number of files
+    // XXX: Noting is actually verified here
+    {
+        psMemId id = psMemGetId();
+        psLibInit("test.psTime.config2");
+        psLibFinalize();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test E - Attempt to read incorrect number of from values
+    // XXX: Noting is actually verified here
+    {
+        psMemId id = psMemGetId();
+        psLibInit("test.psTime.config3");
+        psLibFinalize();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test F - Attempt to read data file with typo in number
+    // XXX: Noting is actually verified here
+    {
+        psMemId id = psMemGetId();
+        psLibInit("test.psTime.config4");
+        psLibFinalize();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testTimer1()
+    {
+        psMemId id = psMemGetId();
+        psF64 testTime = 0.0;
+        psF64 testTime2 = 0.0;
+        psF64 testTime3 = 0.0;
+
+        ok(psTimerStart("newTime"), "psTimerStart successful");
+        testTime = psTimerMark("newTime");
+        ok(testTime != 0.0, "psTimerMark() successful");
+        testTime2 = psTimerClear("newTime");
+        ok(testTime2 != 0.0, "psTimerClear() successful");
+        psTimerStart("newTime");
+        testTime3 = psTimerStop();
+        ok(testTime3 != 0.0, "psTimerStop() successful");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testTideUT1Corr()
+    // Verify NULL return with NULL input
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psTime *empty = psTime_TideUT1Corr(NULL);
+        ok(empty == NULL, "psTime_TideUT1Corr() returned NULL for NULL input time");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testTideUT1Corr()
+    // 
+    {
+        psMemId id = psMemGetId();
+        psTime *noTide = psTimeAlloc(PS_TIME_UTC);
+        noTide->sec = 1049160600;
+        noTide->nsec = 0;
+        noTide->leapsecond = false;
+        psTime *empty = psTime_TideUT1Corr(noTide);
+        is_long(empty->sec, 1049160599, "psTime_TideUT1Corr() returned correct ->sec");
+        is_long(empty->nsec, 656981971, "psTime_TideUT1Corr() returned correct ->nsec");
+        ok(p_psTimeFinalize(), "p_psTimeFinalize() successful");
+        psFree(empty);
+        psFree(noTide);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psCoord.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psCoord.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psCoord.c	(revision 22322)
@@ -0,0 +1,469 @@
+/** @file  tst_psCoord.c
+*
+*  @brief The code will ...
+*
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-02-28 02:53:03 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include "psTest.h"
+#include "pslib_strict.h"
+static psS32 testPlaneTransformAlloc(void);
+static psS32 testPlaneDistortAlloc(void);
+static psS32 testPlaneAlloc(void);
+static psS32 testPlaneTransformApply(void);
+static psS32 testPlaneDistortApply(void);
+static psS32 testPixelsTransform(void);
+
+testDescription tests[] = {
+                              {testPlaneTransformAlloc, 826, "psPlaneTransformAlloc()", 0, false},
+                              {testPlaneDistortAlloc, 827, "psPlaneDistortAlloc()", 0, false},
+                              {testPlaneAlloc, -1, "psPlaneAlloc()", 0, false},
+                              {testPlaneTransformApply, 831, "psPlaneTransformApply()", 0, false},
+                              {testPlaneDistortApply, 832, "psPlaneDistortApply()", 0, false},
+                              {testPixelsTransform, 833, "psPixelsTransform()", 0, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psCoord", tests, argc, argv ) );
+}
+
+#define ORDER_X 2
+#define ORDER_Y 3
+#define ORDER_Z 4
+#define ORDER_T 5
+
+psS32 testPlaneAlloc( void )
+{
+    // Allocate psPlane.
+    psPlane *myP = psPlaneAlloc();
+
+    // Verify returned value is not NULL
+    if(myP == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "psPlaneAlloc() returned NULL not expected.");
+        return 1;
+    }
+
+    // Verify returned transform has members set properly
+    if (!isnan(myP->x)) {
+        psError(PS_ERR_UNKNOWN,true,"myP->x is %d, should be %d", myP->x, NAN);
+        return 2;
+    }
+
+    if (!isnan(myP->y)) {
+        psError(PS_ERR_UNKNOWN,true,"myP->y is %d, should be %d", myP->y, NAN);
+        return 3;
+    }
+
+    if (!isnan(myP->xErr)) {
+        psError(PS_ERR_UNKNOWN,true,"myP->xErr is %d, should be %d", myP->xErr, NAN);
+        return 4;
+    }
+
+    if (!isnan(myP->yErr)) {
+        psError(PS_ERR_UNKNOWN,true,"myP->yErr is %d, should be %d", myP->yErr, NAN);
+        return 5;
+    }
+
+    // Free psPlane
+    psFree(myP);
+
+    return 0;
+}
+
+psS32 testPlaneTransformAlloc( void )
+{
+    // Allocate psPlaneTransform with known x and y terms
+    psPlaneTransform *myPT = psPlaneTransformAlloc(ORDER_X, ORDER_Y);
+
+    // Verify returned value is not NULL
+    if(myPT == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Function returned NULL not expected.");
+        return 1;
+    }
+
+    // Verify returned transform has members set properly
+    if (myPT->x->nX != ORDER_X) {
+        psError(PS_ERR_UNKNOWN,true,"myPT->x->nX is %d, should be %d",
+                myPT->x->nX, ORDER_X);
+        return 2;
+    }
+    if (myPT->y->nX != ORDER_X) {
+        psError(PS_ERR_UNKNOWN,true,"myPT->y->nX is %d, should be %d",
+                myPT->y->nX, ORDER_X);
+        return 3;
+    }
+    if (myPT->x->nY != ORDER_Y) {
+        psError(PS_ERR_UNKNOWN,true,"myPT->x->nY is %d, should be %d",
+                myPT->x->nY, ORDER_Y);
+        return 4;
+    }
+    if (myPT->y->nY != ORDER_Y) {
+        psError(PS_ERR_UNKNOWN,true,"myPT->y->nY is %d, should be %d",
+                myPT->y->nY, ORDER_Y);
+        return 5;
+    }
+
+    // Free plane transform
+    psFree(myPT);
+
+    // Attempt to specify negative x order and verify NULL returned and errror message generated
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: negative x order");
+    myPT = psPlaneTransformAlloc(-1, 1);
+    if (myPT != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psPlaneTransformAlloc() did not return NULL.");
+        return 6;
+    }
+
+    // Attempt to specify negative y order and verify NULL returned and error message generated
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: negative y order");
+    myPT = psPlaneTransformAlloc(1, -1);
+    if (myPT != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psPlaneTransformAlloc() did not return NULL.");
+        return 7;
+    }
+
+    psFree(myPT);
+
+    return 0;
+}
+
+
+psS32 testPlaneDistortAlloc( void )
+{
+    // Invoke function with known parameters
+    psPlaneDistort *myPD = psPlaneDistortAlloc(ORDER_X, ORDER_Y, ORDER_Z, ORDER_T);
+
+    // Verify NULL is not returned
+    if(myPD == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Return of NULL not expected");
+        return 1;
+    }
+    // Verify the terms are properly set after allocation
+    if (myPD->x->nX != ORDER_X) {
+        psError(PS_ERR_UNKNOWN,true,"myPD->x->nX is %d, should be %d",
+                myPD->x->nX, ORDER_X);
+        return 2;
+    }
+    if (myPD->y->nX != ORDER_X) {
+        psError(PS_ERR_UNKNOWN,true,"myPD->y->nX is %d, should be %d",
+                myPD->y->nX, ORDER_X);
+        return 3;
+    }
+    if (myPD->x->nY != ORDER_Y) {
+        psError(PS_ERR_UNKNOWN,true,"myPD->x->nY is %d, should be %d",
+                myPD->x->nY, ORDER_Y);
+        return 4;
+    }
+    if (myPD->y->nY != ORDER_Y) {
+        psError(PS_ERR_UNKNOWN,true,"myPD->y->nY is %d, should be %d",
+                myPD->y->nY, ORDER_Y);
+        return 5;
+    }
+    if (myPD->x->nZ != ORDER_Z) {
+        psError(PS_ERR_UNKNOWN,true,"myPD->x->nZ is %d, should be %d",
+                myPD->x->nZ, ORDER_Z);
+        return 6;
+    }
+    if (myPD->y->nZ != ORDER_Z) {
+        psError(PS_ERR_UNKNOWN,true,"myPD->y->nZ is %d, should be %d",
+                myPD->y->nZ, ORDER_Z);
+        return 7;
+    }
+    if (myPD->x->nT != ORDER_T) {
+        psError(PS_ERR_UNKNOWN,true,"myPD->x->nT is %d, should be %d",
+                myPD->x->nT, ORDER_T);
+        return 8;
+    }
+    if (myPD->y->nT != ORDER_T) {
+        psError(PS_ERR_UNKNOWN,true,"myPD->y->nT is %d, should be %d",
+                myPD->y->nT, ORDER_T);
+        return 9;
+    }
+    // Free psPlaneTransform
+    psFree(myPD);
+
+    // Invoke function with negative x order parameter
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for negative x order");
+    myPD = psPlaneDistortAlloc(-1, 1, 1, 1);
+    if (myPD != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psPlaneDistortAlloc() did not return NULL.");
+        return 10;
+    }
+
+    // Invoke function with negative y order parameter
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for negative y order");
+    myPD = psPlaneDistortAlloc(1, -1, 1, 1);
+    if (myPD != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psPlaneDistortAlloc() did not return NULL.");
+        return 11;
+    }
+
+    // Invoke function with negative z order parameter
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for negative z order");
+    myPD = psPlaneDistortAlloc(1, 1, -1, 1);
+    if (myPD != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psPlaneDistortAlloc() did not return NULL.");
+        return 12;
+    }
+
+    // Invoke function with negative t order parameter
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for negative w order");
+    myPD = psPlaneDistortAlloc(1, 1, 1, -1);
+    if (myPD != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psPlaneDistortAlloc() did not return NULL.");
+    }
+
+    return 0;
+}
+
+#define N 10
+// We do a simple identity transformation on a few x,y pairs.
+psS32 testPlaneTransformApply( void )
+{
+    psPlane*          out = NULL;
+    psPolynomial2D*  tmp2DPoly = NULL;
+    psPlane*          rc;
+
+    // Create input coordinate
+    psPlane* in = psPlaneAlloc();
+    if(in == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return from psPlaneAlloc");
+        return 1;
+    }
+
+    // Create transform
+    psPlaneTransform* pt = psPlaneTransformAlloc(2,2);
+
+    // Set transform coefficients so the x coord input will equal x coord output, same for y
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+
+    // Apply transform for several points
+    for (psS32 i = 0; i < N; i++) {
+        in->x = (psF64) i;
+        in->y = (psF64) (i + 5.0);
+        in->xErr = 0.0;
+        in->yErr = 0.0;
+
+        // Verify function does not return NULL
+        out = psPlaneTransformApply(out, pt, in);
+        if( out  == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Expected non-NULL return value");
+            return 1;
+        }
+        // Verify the expected values are returned
+        if (FLT_EPSILON < fabs(out->x - in->x)) {
+            psError(PS_ERR_UNKNOWN,true,"out.x is %lf, should be %lf", out->x, in->x);
+            return 3;
+        }
+        if (FLT_EPSILON < fabs(out->y - in->y)) {
+            psError(PS_ERR_UNKNOWN,true,"out.y is %lf, should be %lf", out->y, in->y);
+            return 4;
+        }
+    }
+    psFree(out);
+
+    // Verify return null and error message generater for null specified transform
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL psPlaneTransform");
+    rc = psPlaneTransformApply(NULL, NULL, in);
+    if (rc != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Function did not return NULL as expected for NULL psPlaneTransform.");
+        return 5;
+    }
+
+    // Verify return null and error message generated for null psPlaneTransform x coeff
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL x coeff psPlaneTransform");
+    tmp2DPoly = pt->x;
+    pt->x = NULL;
+    rc = psPlaneTransformApply(NULL, pt, in);
+    if (rc != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Function did not return NULL as expected for NULL x coeff psPlaneTransform");
+        return 6;
+    }
+    pt->x = tmp2DPoly;
+
+    // Verify return null and error message generated for null psPlaneTransform y coeff
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL y coeff psPlaneTransform");
+    tmp2DPoly = pt->y;
+    pt->y = NULL;
+    rc = psPlaneTransformApply(NULL, pt, in);
+    if (rc != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Function did not return NULL as expected for NULL y coeff psPlaneTransform");
+        return 7;
+    }
+    pt->y = tmp2DPoly;
+
+    // Verify return null and error message generated for null psPlane coordinate
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL psPlane");
+    rc = psPlaneTransformApply(NULL, pt, NULL);
+    if (rc != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Function did not return NULL as expected for NULL psPlane");
+        return 8;
+    }
+
+    psFree(pt);
+    psFree(in);
+
+    return 0;
+}
+
+#define COLOR 1.0
+#define MAGNITUDE 1.0
+// We do a simple identity transformation on a few x,y pairs.  For x and y,
+// we add in the COLOR and MAGNITUDE.
+psS32 testPlaneDistortApply( void )
+{
+    psPlane*         out       = NULL;
+    psPlane*         rc        = NULL;
+    psPolynomial4D* tmp4DPoly = NULL;
+
+    // Allocate input coordinate
+    psPlane* in = psPlaneAlloc();
+
+    // Create psPlaneTransform structure
+    psPlaneDistort*  pt = psPlaneDistortAlloc(2, 2, 2, 2);
+
+    pt->x->coeff[1][0][0][0] = 1.0;
+    pt->x->coeff[0][0][1][0] = 1.0;
+    pt->x->coeff[0][0][0][1] = 1.0;
+
+    pt->y->coeff[0][1][0][0] = 1.0;
+    pt->y->coeff[0][0][1][0] = 1.0;
+    pt->y->coeff[0][0][0][1] = 1.0;
+
+    for (psS32 i = 0; i < N; i++) {
+        in->x = (psF64) i;
+        in->y = (psF64) (i + 5.0);
+        in->xErr = 0.0;
+        in->yErr = 0.0;
+        out = psPlaneDistortApply(out, pt, in, COLOR, MAGNITUDE);
+
+        // Verify return value is not NULL
+        if(out == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Expected non-NULL return");
+            return 1;
+        }
+        // Verify return value contains expected values
+        if (FLT_EPSILON < fabs(out->x - COLOR - MAGNITUDE - in->x)) {
+            psError(PS_ERR_UNKNOWN,true,"out->x is %lf, should be %lf",
+                    out->x, in->x + COLOR + MAGNITUDE);
+            return 2;
+        }
+        if (FLT_EPSILON < fabs(out->y - COLOR - MAGNITUDE - in->y)) {
+            psError(PS_ERR_UNKNOWN,true,"out->y is %lf, should be %lf",
+                    out->y, in->y + COLOR + MAGNITUDE);
+            return 3;
+        }
+    }
+    psFree(out);
+
+    // Attempt to invoke psPlaneDistortApply with NULL psPlaneDistort
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null psPlaneDistort");
+    rc = psPlaneDistortApply(NULL, NULL, in, COLOR, MAGNITUDE);
+    if (rc != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL.");
+        return 4;
+    }
+
+    // Attempt to invoke psPlaneDistortApply with NULL psPlaneDistort x member
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null x member psPlaneDistort");
+    tmp4DPoly = pt->x;
+    pt->x = NULL;
+    rc = psPlaneDistortApply(NULL, pt, in, COLOR, MAGNITUDE);
+    if (rc != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL.");
+        return 5;
+    }
+    pt->x = tmp4DPoly;
+
+    // Attempt to invoke psPlaneDistortApply with NULL psPlaneDistort y member
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null y member psPlaneDistort");
+    tmp4DPoly = pt->y;
+    pt->y = NULL;
+    rc = psPlaneDistortApply(NULL, pt, in, COLOR, MAGNITUDE);
+    if (rc != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL.");
+        return 6;
+    }
+    pt->y = tmp4DPoly;
+
+    // Attempt to invoke psPlaneDistortApply with NULL input psPlane
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null input psPlane");
+    rc = psPlaneDistortApply(NULL, pt, NULL, COLOR, MAGNITUDE);
+    if (rc != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL");
+        return 7;
+    }
+
+    psFree(pt);
+    psFree(in);
+
+    return 0;
+}
+
+psS32 testPixelsTransform(void)
+{
+    psPixels *input = NULL;
+    psPixels *output = NULL;
+    psPlaneTransform *trans = psPlaneTransformAlloc(1, 3);
+
+    //Return NULL for NULL input pixels
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    output = psPixelsTransform(output, input, trans);
+    if (output != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psPixelsTransform failed to return NULL for NULL input pixels.\n");
+        return 1;
+    }
+    //Return NULL for NULL input PlaneTransform
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    output = psPixelsTransform(output, input, NULL);
+    if (output != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psPixelsTransform failed to return NULL for NULL psPlaneTransform.\n");
+        return 2;
+    }
+
+    input = psPixelsAlloc(2);
+    input->n = 2;
+    input->data[0].x = 1.0;
+    input->data[0].y = 1.0;
+    input->data[1].x = 1.0;
+    input->data[1].y = 6.0;
+    trans->x->coeff[0][0] = 0;
+    trans->x->coeff[1][0] = 1.0;
+    trans->y->coeff[0][0] = 0;
+    trans->y->coeff[0][0] = 0;
+    trans->y->coeff[0][2] = 0.5;
+
+    //Verify that the output pixels are what we expected
+    output = psPixelsTransform(output, input, trans);
+    int nExpected = 9;
+    if (output->n != nExpected) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, false,
+                "psPixelsTransform failed to return the expected number of pixels.\n");
+        printf("\n output returned with %ld pixels\n\n", output->n);
+        for (int i = 0; i < output->n; i++) {
+            printf("  (%6.2lf, %6.2lf) pixel %d\n", output->data[i].x, output->data[i].y, i+1);
+        }
+        return 3;
+    }
+
+    psFree(trans);
+    psFree(input);
+    psFree(output);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psCoord01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psCoord01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psCoord01.c	(revision 22322)
@@ -0,0 +1,999 @@
+/**  @file  tst_psCoord01.c
+*
+*    @brief  The code will test several functions with PSLib source file
+*            psCoord.c
+*
+*    @author Eric Van Alst, MHPCC
+*
+* XXX: must do (r,d) -> (x, y) -> (r, d) test to ensure correctness.
+* XXX: The (Xs, Ys) scales are not be used properly.
+*
+*
+*    @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+*    @date  $Date: 2006-05-26 01:23:41 $
+*    @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+*    @date  $Date: 2006-05-26 01:23:41 $
+*
+*    Copyright 2005 Maui High Performance Computing Center, Univ. of Hawaii
+*/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testProjectionAlloc(void);
+static psS32 testProjectTan(void);
+static psS32 testDeprojectTan(void);
+static psS32 testProjectSin(void);
+static psS32 testDeprojectSin(void);
+static psS32 testProjectAit(void);
+static psS32 testDeprojectAit(void);
+static psS32 testProjectPar(void);
+static psS32 testDeprojectPar(void);
+static psS32 testProjectFail(void);
+static psS32 testDeprojectFail(void);
+static psS32 testSetOffsetSphere(void);
+static psS32 testSetOffsetLinear(void);
+static psS32 testGetOffsetSphere(void);
+static psS32 testGetOffsetLinear(void);
+static psS32 testProjectTanDeProjectTan(void);
+
+testDescription tests[] = {
+                              {testProjectionAlloc, 833, "psProjectionAlloc",0,false},
+                              {testProjectFail, 834, "psProject",0,false},
+                              {testDeprojectFail, 835, "psProject",0,false},
+                              {testSetOffsetSphere, 0000, "psSphereSetOffset",0,false},
+                              {testSetOffsetLinear, 0000, "psSphereSetOffset",0,false},
+                              {testGetOffsetSphere, 0000, "psSphereGetOffset",0,false},
+                              {testGetOffsetLinear, 0000, "psSphereGetOffset",0,false},
+                              {NULL},
+                              /* XXX: Tests removed pending valuation -- test itself may be faulty. */
+                              {testProjectTan, 834, "psProject(TAN)",0,false},
+                              {testDeprojectTan, 835, "psDeproject(TAN)",0,false},
+                              {testProjectSin, 834, "psProject(SIN)",0,false},
+                              {testDeprojectSin, 835, "psDeproject(SIN)",0,false},
+                              {testProjectAit, 834, "psProject(AIT)",0,false},
+                              {testDeprojectAit, 835, "psDeproject(AIT)",0,false},
+                              {testProjectPar, 834, "psProject(PAR)",0,false},
+                              {testDeprojectPar, 835, "psDeproject(PAR)",0,false},
+                              {testProjectTanDeProjectTan, 0000, "psProject(TAN) -> psDeproject(TAN)",0,false}
+                          };
+
+
+#define ERROR_TOL    0.0001
+#define TESTPOINTS   4
+#define DEG_INC   30.0
+
+#define MY_TINY 0.0001
+#define PS_COMPARE_TINY_THEN_PRINT_ERROR(ACTUAL, EXPECT, TESTSTATUS) \
+if (MY_TINY < fabs(EXPECT - ACTUAL)) { \
+    psError(PS_ERR_UNKNOWN,true,"%s is %lg, should be %lg", #ACTUAL, ACTUAL, EXPECT, TESTSTATUS); \
+    return TESTSTATUS; \
+}
+
+
+//  alpha, delta, alpha-center, delta-center, scale-x, scale-y
+psF64 projectionTestPoint[TESTPOINTS][6] = {
+            {  0.785398,  0.785398,  0.000000,  0.000000,  1.000000,  1.000000 },
+            {  0.100000,  1.500000,  0.500000,  0.250000,  1.000000,  1.000000 },
+            {  0.628319,  0.448799,  0.000000,  0.000000,  0.250000,  0.750000 },
+            { -1.047196,  0.222222, -0.250000,  0.000000,  1.500000,  1.250000 }
+        };
+
+// Expected values for TAN
+psF64 projectionTanExpected[TESTPOINTS][2] = {
+            { -1.000000, -1.414214 },
+            {  0.088884, -3.066567 },
+            { -0.181636, -0.446444 },
+            {  1.535818, -0.404231 }
+        };
+
+// Expected values for SIN
+psF64 projectionSinExpected[TESTPOINTS][2] = {
+            { -0.500000, -0.707101 },
+            {  0.027546, -0.950366 },
+            { -0.132394, -0.325413 },
+            {  1.046712, -0.275497 }
+        };
+
+// Expected values for AIT
+psF64 projectionAitExpected[TESTPOINTS][2] = {
+            { -0.549175,  0.523375 },
+            {  0.027895,  0.313807 },
+            { -0.162822,  0.607646 },
+            {  1.455312,  0.955388 }
+        };
+
+// Expected values for PAR
+psF64 projectionParExpected[TESTPOINTS][2] = {
+            { -0.541244,  0.545532 },
+            {  0.027703,  0.329366 },
+            { -0.157157,  0.633550 },
+            {  1.432951,  0.971372 }
+        };
+
+// Testpoints, offset and expected values for psSphereSetOffset test
+#define TESTPOINTS_OFFSET  4
+psF64 setOffsetTestpoint[TESTPOINTS_OFFSET][2] = {
+            { 0.50,  0.50 },
+            {-0.50,  0.50 },
+            { 0.00,  0.00 },
+            { 1.50, -0.10 }
+        };
+
+psF64 setOffsetOffset[TESTPOINTS_OFFSET][2] = {
+            { 0.190761, -0.272205 },
+            {-0.553049, -0.460926 },
+            { 0.100335,-14.172222 },
+            {14.172222, -0.100335 }
+        };
+
+psF64 setOffsetResult[TESTPOINTS_OFFSET][2] = {
+            //XXX: Eugene says his values are correct, so i'm changing these values to the output.
+            //            { 0.25,  0.75 },
+            //            { 0.20,  0.80 },
+            //            {-0.10,  1.50 },
+            //            { 0.00,  0.00 }
+            { 0.68702,   0.230294},
+            { -0.966388,  0.0608434 },
+            {0.10,  -1.50 },
+            { 3.00141, -0.0140538  }
+        };
+
+psF64 getOffsetTestpoint1[TESTPOINTS_OFFSET][2] = {
+            { 0.00,  0.00 },
+            {-0.25,  0.50 },
+            { 0.50, -0.25 },
+            { 0.25, -0.10 }
+        };
+
+psF64 getOffsetTestpoint2[TESTPOINTS_OFFSET][2] = {
+            { 0.75,  0.25 },
+            { 0.40, -0.60 },
+            { 1.50,  0.50 },
+            { 0.10,  0.75 }
+        };
+
+psF64 getOffsetResult[TESTPOINTS_OFFSET][2] = {
+            //XXX: Eugene says his values are correct, so i'm changing these values to the output.
+            //            { -0.931596, -0.348976 },
+            //            { -1.632830,  2.649629 },
+            //            { -2.166795, -1.707211 },
+            //            {  0.167752, -1.151351 }
+            { 0.931596, 0.348976 },
+            { 1.632830,  -2.649629 },
+            { 2.166795, 1.707211 },
+            {  -0.167752, 1.151351 }
+        };
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( !runTestSuite(stderr,"psCoord",tests,argc,argv) );
+}
+
+psS32 testProjectionAlloc(void)
+{
+    // Allocate new psProjection structure
+    psProjection*  myProjection = psProjectionAlloc(1.1, 2.2, 3.3, 4.4, PS_PROJ_AIT);
+
+    // Verify NULL is not returned
+    if(myProjection == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Unexpected return of NULL");
+        return 1;
+    }
+    // Verify the members are set correctly
+    if(myProjection->R != 1.1) {
+        psError(PS_ERR_UNKNOWN,true,"R member set to %lg, but expected %lg",
+                myProjection->R, 1.1);
+        return 2;
+    }
+    if(myProjection->D != 2.2) {
+        psError(PS_ERR_UNKNOWN,true,"D member set to %lg, but expected %lg",
+                myProjection->D, 2.2);
+        return 3;
+    }
+    if(myProjection->Xs != 3.3) {
+        psError(PS_ERR_UNKNOWN,true,"Xs member set to %lg, but expected %lg",
+                myProjection->Xs, 3.3);
+        return 4;
+    }
+    if(myProjection->Ys != 4.4) {
+        psError(PS_ERR_UNKNOWN,true,"Ys member set to %lg, but expected %lg",
+                myProjection->Ys, 4.4);
+        return 5;
+    }
+    if(myProjection->type != PS_PROJ_AIT) {
+        psError(PS_ERR_UNKNOWN,true,"type member set to %d, but expected %d",
+                myProjection->type, PS_PROJ_AIT);
+        return 6;
+    }
+
+    // Free projection
+    psFree(myProjection);
+
+    return 0;
+}
+
+//HEY
+psS32 testProjectTan(void)
+{
+    psPlane*       out = NULL;
+    psSphere*      in = psSphereAlloc();
+    psSphere*      inTest = NULL;
+    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
+
+    // Perform projecton on various test points
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+
+        // Initialize input and project members
+        in->r = projectionTestPoint[i][0];
+        in->d = projectionTestPoint[i][1];
+        myProjection->R = projectionTestPoint[i][2];
+        myProjection->D = projectionTestPoint[i][3];
+        myProjection->Xs = projectionTestPoint[i][4];
+        myProjection->Ys = projectionTestPoint[i][5];
+
+        // Perform projection
+        out = psProject(in, myProjection);
+        inTest = psDeproject(out, myProjection);
+
+        // Verify output not NULL
+        if(out == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
+            return i*10+3;
+        }
+
+        // Verify output is as expected
+        if(fabs(out->x - projectionTanExpected[i][0]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psPlane->x = %lg  expected %lg",
+                    i,out->x, projectionTanExpected[i][0]);
+            return i*10+1;
+        }
+        if(fabs(out->y - projectionTanExpected[i][1]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint % d  psPlane->y = %lg  expected %lg",
+                    i,out->y, projectionTanExpected[i][1]);
+            return i*10+2;
+        }
+
+        // Verify output is as expected
+        if(fabs(in->r - inTest->r) > ERROR_TOL) {
+            printf("TEST ER (in->r, inTest->r) (%.2f %.2f)\n", in->r, inTest->r);
+            return i*10+4;
+        }
+        if(fabs(in->d - inTest->d) > ERROR_TOL) {
+            printf("TEST ERROR: (in->d, inTest->d) (%.2f %.2f)\n", in->d, inTest->d);
+            return i*10+5;
+        }
+
+        psFree(inTest);
+        psFree(out);
+    }
+
+    psFree(in);
+    psFree(myProjection);
+
+    return 0;
+}
+
+psS32 testDeprojectTan(void)
+{
+    psSphere*       out = NULL;
+    psPlane*        in = psPlaneAlloc();
+    psProjection*   myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
+
+    // Perform deprojection on various test points
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+
+        // Initialize input and projection members
+        in->x = projectionTanExpected[i][0];
+        in->y = projectionTanExpected[i][1];
+        myProjection->R = projectionTestPoint[i][2];
+        myProjection->D = projectionTestPoint[i][3];
+        myProjection->Xs = projectionTestPoint[i][4];
+        myProjection->Ys = projectionTestPoint[i][5];
+
+        // Perform deprojection
+        out = psDeproject(in, myProjection);
+
+        // Verify output is not NULL
+        if(out == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
+            return 20*i;
+        }
+
+        // Verify output is as expected
+        if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->r = %lg  expected %lg",
+                    i,out->r,projectionTestPoint[i][0]);
+            return 20*i + 1;
+        }
+        if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->d = %lg expected %lg",
+                    i, out->d, projectionTestPoint[i][1]);
+            return 20*i + 2;
+        }
+        psFree(out);
+    }
+
+    psFree(in);
+    psFree(myProjection);
+
+    return 0;
+}
+
+psS32 testProjectSin(void)
+{
+    psPlane*       out = NULL;
+    psSphere*      in = psSphereAlloc();
+    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_SIN);
+
+    // Perform projecton on various test points
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+
+        // Initialize input and project members
+        in->r = projectionTestPoint[i][0];
+        in->d = projectionTestPoint[i][1];
+        myProjection->R = projectionTestPoint[i][2];
+        myProjection->D = projectionTestPoint[i][3];
+        myProjection->Xs = projectionTestPoint[i][4];
+        myProjection->Ys = projectionTestPoint[i][5];
+
+        // Perform projection
+        out = psProject(in, myProjection);
+
+        // Verify output not NULL
+        if(out == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
+            return i*10;
+        }
+
+        // Verify output is as expected
+        if(fabs(out->x - projectionSinExpected[i][0]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psPlane->x = %lg  expected %lg",
+                    i,out->x, projectionSinExpected[i][0]);
+            return i*10+1;
+        }
+        if(fabs(out->y - projectionSinExpected[i][1]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint % d  psPlane->y = %lg  expected %lg",
+                    i,out->y, projectionSinExpected[i][1]);
+            return i*10+2;
+        }
+        psFree(out);
+    }
+
+    psFree(in);
+    psFree(myProjection);
+
+    return 0;
+}
+
+psS32 testDeprojectSin(void)
+{
+    psSphere*       out = NULL;
+    psPlane*        in = psPlaneAlloc();
+    psProjection*   myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_SIN);
+
+    // Perform deprojection on various test points
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+
+        // Initialize input and projection members
+        in->x = projectionSinExpected[i][0];
+        in->y = projectionSinExpected[i][1];
+        myProjection->R = projectionTestPoint[i][2];
+        myProjection->D = projectionTestPoint[i][3];
+        myProjection->Xs = projectionTestPoint[i][4];
+        myProjection->Ys = projectionTestPoint[i][5];
+
+        // Perform deprojection
+        out = psDeproject(in, myProjection);
+
+        // Verify output is not NULL
+        if(out == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
+            return 20*i;
+        }
+
+        // Verify output is as expected
+        if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->r = %lg  expected %lg",
+                    i,out->r,projectionTestPoint[i][0]);
+            return 20*i + 1;
+        }
+        if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->d = %lg expected %lg",
+                    i, out->d, projectionTestPoint[i][1]);
+            return 20*i + 2;
+        }
+        psFree(out);
+    }
+
+    psFree(in);
+    psFree(myProjection);
+
+    return 0;
+}
+
+psS32 testProjectAit(void)
+{
+    psPlane*       out = NULL;
+    psSphere*      in = psSphereAlloc();
+    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_AIT);
+
+    // Perform projecton on various test points
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+
+        // Initialize input and project members
+        in->r = projectionTestPoint[i][0];
+        in->d = projectionTestPoint[i][1];
+        myProjection->R = projectionTestPoint[i][2];
+        myProjection->D = projectionTestPoint[i][3];
+        myProjection->Xs = projectionTestPoint[i][4];
+        myProjection->Ys = projectionTestPoint[i][5];
+
+        // Perform projection
+        out = psProject(in, myProjection);
+
+        // Verify output not NULL
+        if(out == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
+            return i*10;
+        }
+
+        // Verify output is as expected
+        if(fabs(out->x - projectionAitExpected[i][0]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psPlane->x = %lg  expected %lg",
+                    i,out->x, projectionAitExpected[i][0]);
+            return i*10+1;
+        }
+        if(fabs(out->y - projectionAitExpected[i][1]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint % d  psPlane->y = %lg  expected %lg",
+                    i,out->y, projectionAitExpected[i][1]);
+            return i*10+2;
+        }
+        psFree(out);
+    }
+
+    psFree(in);
+    psFree(myProjection);
+
+    return 0;
+}
+
+psS32 testDeprojectAit(void)
+{
+    psSphere*       out = NULL;
+    psPlane*        in = psPlaneAlloc();
+    psProjection*   myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_AIT);
+
+    // Perform deprojection on various test points
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+
+        // Initialize input and projection members
+        in->x = projectionAitExpected[i][0];
+        in->y = projectionAitExpected[i][1];
+        myProjection->R = projectionTestPoint[i][2];
+        myProjection->D = projectionTestPoint[i][3];
+        myProjection->Xs = projectionTestPoint[i][4];
+        myProjection->Ys = projectionTestPoint[i][5];
+
+        // Perform deprojection
+        out = psDeproject(in, myProjection);
+
+        // Verify output is not NULL
+        if(out == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
+            return 20*i;
+        }
+
+        // Verify output is as expected
+        if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->r = %lg  expected %lg",
+                    i,out->r,projectionTestPoint[i][0]);
+            return 20*i + 1;
+        }
+        if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->d = %lg expected %lg",
+                    i, out->d, projectionTestPoint[i][1]);
+            return 20*i + 2;
+        }
+        psFree(out);
+    }
+
+    psFree(in);
+    psFree(myProjection);
+
+    return 0;
+}
+
+psS32 testProjectPar(void)
+{
+    psPlane*       out = NULL;
+    psSphere*      in = psSphereAlloc();
+    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_PAR);
+
+    // Perform projecton on various test points
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+
+        // Initialize input and project members
+        in->r = projectionTestPoint[i][0];
+        in->d = projectionTestPoint[i][1];
+        myProjection->R = projectionTestPoint[i][2];
+        myProjection->D = projectionTestPoint[i][3];
+        myProjection->Xs = projectionTestPoint[i][4];
+        myProjection->Ys = projectionTestPoint[i][5];
+
+        // Perform projection
+        out = psProject(in, myProjection);
+
+        // Verify output not NULL
+        if(out == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
+            return i*10;
+        }
+
+        // Verify output is as expected
+        if(fabs(out->x - projectionParExpected[i][0]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psPlane->x = %lg  expected %lg",
+                    i,out->x, projectionParExpected[i][0]);
+            return i*10+1;
+        }
+        if(fabs(out->y - projectionParExpected[i][1]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint % d  psPlane->y = %lg  expected %lg",
+                    i,out->y, projectionParExpected[i][1]);
+            return i*10+2;
+        }
+        psFree(out);
+    }
+
+    psFree(in);
+    psFree(myProjection);
+
+    return 0;
+}
+
+psS32 testDeprojectPar(void)
+{
+    psSphere*       out = NULL;
+    psPlane*        in = psPlaneAlloc();
+    psProjection*   myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_PAR);
+
+    // Perform deprojection on various test points
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+
+        // Initialize input and projection members
+        in->x = projectionParExpected[i][0];
+        in->y = projectionParExpected[i][1];
+        myProjection->R = projectionTestPoint[i][2];
+        myProjection->D = projectionTestPoint[i][3];
+        myProjection->Xs = projectionTestPoint[i][4];
+        myProjection->Ys = projectionTestPoint[i][5];
+
+        // Perform deprojection
+        out = psDeproject(in, myProjection);
+
+        // Verify output is not NULL
+        if(out == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
+            return 20*i;
+        }
+
+        // Verify output is as expected
+        if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->r = %lg  expected %lg",
+                    i,out->r,projectionTestPoint[i][0]);
+            return 20*i + 1;
+        }
+        if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->d = %lg expected %lg",
+                    i, out->d, projectionTestPoint[i][1]);
+            return 20*i + 2;
+        }
+        psFree(out);
+    }
+
+    psFree(in);
+    psFree(myProjection);
+
+    return 0;
+}
+
+psS32 testProjectFail(void)
+{
+    psPlane*       out          = NULL;
+    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
+    psSphere*      in           = psSphereAlloc();
+
+    // Invoke function with null coordinate argument
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null coord arg");
+    out = psProject(NULL, myProjection);
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
+        return 1;
+    }
+
+    // Invoke function with null projection argument
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null projection arg");
+    out = psProject(in, NULL);
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
+        return 2;
+    }
+
+    // Invoke function with invalid projection type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid projection type");
+    myProjection->type = PS_PROJ_NTYPE;
+    out = psProject(in,myProjection);
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
+        return 3;
+    }
+
+    psFree(myProjection);
+    psFree(in);
+
+    return 0;
+}
+
+psS32 testDeprojectFail(void)
+{
+    psSphere*      out          = NULL;
+    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
+    psPlane*       in           = psPlaneAlloc();
+
+    // Invoke function with null coordinate argument
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null coord arg");
+    out = psDeproject(NULL, myProjection);
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
+        return 1;
+    }
+
+    // Invoke function with null projection argument
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null projection arg");
+    out = psDeproject(in, NULL);
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
+        return 2;
+    }
+
+    // Invoke function with invalid projection type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid projection type");
+    myProjection->type = PS_PROJ_NTYPE;
+    out = psDeproject(in,myProjection);
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
+        return 3;
+    }
+
+    psFree(myProjection);
+    psFree(in);
+
+    return 0;
+}
+
+psS32 testSetOffsetSphere( void )
+{
+    psSphere* position1 = psSphereAlloc();
+    psSphere* position2 = NULL;
+    psSphere* offset    = psSphereAlloc();
+    psSphere* tmpOffset = psSphereAlloc();
+
+    // Initialize first position
+    position1->r = DEG_TO_RAD(90.0);
+    position1->d = DEG_TO_RAD(45.0);
+    position1->rErr = 0.0;
+    position1->dErr = 0.0;
+
+    //  Using various offset verify spherical offset
+    //  Use all the valid unit types
+    for (psF64 r = 0.0; r < 180.0; r += DEG_INC) {
+        for (psF64 d = 0.0; d < 90.0; d += DEG_INC) {
+
+            offset->r = DEG_TO_RAD(r);
+            offset->d = DEG_TO_RAD(d);
+            offset->rErr = 0.0;
+            offset->dErr = 0.0;
+
+            position2 = psSphereSetOffset(position1, offset, PS_SPHERICAL, PS_RADIAN);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 1);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 2);
+            psFree(position2);
+
+            tmpOffset->r = RAD_TO_DEG(offset->r);
+            tmpOffset->d = RAD_TO_DEG(offset->d);
+            tmpOffset->rErr = 0.0;
+            tmpOffset->dErr = 0.0;
+            position2 = psSphereSetOffset(position1, tmpOffset, PS_SPHERICAL, PS_DEGREE);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 3);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 4);
+            psFree(position2);
+
+            tmpOffset->r = RAD_TO_MIN(offset->r);
+            tmpOffset->d = RAD_TO_MIN(offset->d);
+            tmpOffset->rErr = 0.0;
+            tmpOffset->dErr = 0.0;
+            position2 = psSphereSetOffset(position1, tmpOffset, PS_SPHERICAL, PS_ARCMIN);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 5);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 6);
+            psFree(position2);
+
+            tmpOffset->r = RAD_TO_SEC(offset->r);
+            tmpOffset->d = RAD_TO_SEC(offset->d);
+            tmpOffset->rErr = 0.0;
+            tmpOffset->dErr = 0.0;
+            position2 = psSphereSetOffset(position1, tmpOffset, PS_SPHERICAL, PS_ARCSEC);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 7);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 8);
+            psFree(position2);
+        }
+    }
+
+    // Attempt to call function with null input coordinate
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message null input coord");
+    position2 = psSphereSetOffset(NULL, offset, PS_LINEAR, PS_ARCSEC);
+    if (position2 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL");
+        return 30;
+    }
+
+    // Attempt to call function with null offset
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message null offset");
+    position2 = psSphereSetOffset(position1, NULL, PS_LINEAR, PS_ARCSEC);
+    if (position2 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL");
+        return 31;
+    }
+
+    // Attempt to call function with invalid mode
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message invalid mode");
+    position2 = psSphereSetOffset(position1, offset, 0x54321, 0);
+    if (position2 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL");
+        return 32;
+    }
+
+    // Attempt to call function with invalid unit
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message invalid unit");
+    position2 = psSphereSetOffset(position1, offset, PS_SPHERICAL, 0x54321);
+    if (position2 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL");
+        return 33;
+    }
+
+    psFree(position1);
+    psFree(offset);
+    psFree(tmpOffset);
+
+    return 0;
+}
+
+psS32 testSetOffsetLinear(void)
+{
+    psSphere*   coord = psSphereAlloc();
+    psSphere*   offset = psSphereAlloc();
+    psSphere*   out = NULL;
+
+    for(psS32 i = 0; i < TESTPOINTS_OFFSET; i++) {
+
+        coord->r = setOffsetTestpoint[i][0];
+        coord->d = setOffsetTestpoint[i][1];
+
+        offset->r = setOffsetOffset[i][0];
+        offset->d = setOffsetOffset[i][1];
+
+        out = psSphereSetOffset(coord, offset, PS_LINEAR, PS_RADIAN);
+
+        if(fabs(out->r - setOffsetResult[i][0]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d: Result out->r = %lg not equal to expected %lg",
+                    i, out->r, setOffsetResult[i][0]);
+            return i*10+1;
+        }
+        if(fabs(out->d - setOffsetResult[i][1]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d: Result out->d = %lg not equal to expected %lg",
+                    i,out->d, setOffsetResult[i][1]);
+            return i*10+2;
+        }
+
+        psFree(out);
+    }
+
+    psFree(coord);
+    psFree(offset);
+
+    return 0;
+}
+
+psS32 testGetOffsetSphere( void )
+{
+    psSphere* position1 = psSphereAlloc();
+    psSphere* position2 = psSphereAlloc();
+    psSphere *offset = NULL;
+
+    position1->r = DEG_TO_RAD(90.0);
+    position1->d = DEG_TO_RAD(45.0);
+    position1->rErr = 0.0;
+    position1->dErr = 0.0;
+
+    for (psF64 r = 0.0; r < 180.0;r += DEG_INC) {
+        for (psF64 d = 0.0;d < 90.0; d += DEG_INC) {
+            position2->r = DEG_TO_RAD(r);
+            position2->d = DEG_TO_RAD(d);
+            position2->rErr = 0.0;
+            position2->dErr = 0.0;
+
+            offset = psSphereGetOffset( position1,  position2,
+                                        PS_SPHERICAL, PS_RADIAN);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 1);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 1);
+            psFree(offset);
+
+            offset = psSphereGetOffset( position1, position2,
+                                        PS_SPHERICAL, PS_DEGREE);
+            offset->r = DEG_TO_RAD(offset->r);
+            offset->d = DEG_TO_RAD(offset->d);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 2);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 3);
+            psFree(offset);
+
+            offset = psSphereGetOffset( position1,  position2,
+                                        PS_SPHERICAL, PS_ARCMIN);
+            offset->r = MIN_TO_RAD(offset->r);
+            offset->d = MIN_TO_RAD(offset->d);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 2);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 3);
+            psFree(offset);
+
+            offset = psSphereGetOffset( position1,  position2,
+                                        PS_SPHERICAL, PS_ARCSEC);
+            offset->r = SEC_TO_RAD(offset->r);
+            offset->d = SEC_TO_RAD(offset->d);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 2);
+            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 3);
+            psFree(offset);
+        }
+    }
+
+    // Attempt to invoke function with null position 1 parameter
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null position");
+    offset = psSphereGetOffset(NULL, position2, PS_LINEAR, 0);
+    if (offset != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
+        return 10;
+    }
+
+    // Attempt to invoke function with null position 2 parameter
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null position");
+    offset = psSphereGetOffset(position1, NULL, PS_LINEAR, 0);
+    if (offset != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
+        return 11;
+    }
+
+    // Attempt to invoke function with invalid mode
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid mode");
+    offset = psSphereGetOffset(position1, position2, 0x54321, 0);
+    if (offset != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
+        return 12;
+    }
+
+    // Attempt to invoke function with invalid unit type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid unit type");
+    offset = psSphereGetOffset(position1, position2, PS_SPHERICAL, 0x54321);
+    if (offset != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
+        return 13;
+    }
+
+    // Attempt to invoke function with coordinate 1 declination value 90.0 degree
+    position1->d = DEG_TO_RAD(90.0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate warning message");
+    offset = psSphereGetOffset(position1, position2, PS_SPHERICAL, PS_RADIAN);
+    if (offset != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
+        return 14;
+    }
+
+    // Attempt to invoke function with coordinate 2 declination value 90.0 degree
+    position1->d = DEG_TO_RAD(45.0);
+    position2->d = DEG_TO_RAD(90.0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate warning message");
+    offset = psSphereGetOffset(position1, position2, PS_SPHERICAL, PS_RADIAN);
+    if (offset != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
+        return 15;
+    }
+
+    psFree(position1);
+    psFree(position2);
+
+    return 0;
+}
+
+psS32 testGetOffsetLinear(void)
+{
+    psSphere*   coord1 = psSphereAlloc();
+    psSphere*   coord2 = psSphereAlloc();
+    psSphere*   out = NULL;
+
+    for(psS32 i = 0; i < TESTPOINTS_OFFSET; i++) {
+
+        coord1->r = getOffsetTestpoint1[i][0];
+        coord1->d = getOffsetTestpoint1[i][1];
+
+        coord2->r = getOffsetTestpoint2[i][0];
+        coord2->d = getOffsetTestpoint2[i][1];
+
+        out = psSphereGetOffset(coord1, coord2, PS_LINEAR, PS_RADIAN);
+
+        if(fabs(out->r - getOffsetResult[i][0]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d: Result out->r = %lg not equal to expected %lg",
+                    i, out->r, getOffsetResult[i][0]);
+            return i*10+1;
+        }
+        if(fabs(out->d - getOffsetResult[i][1]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Testpoint %d: Result out->d = %lg not equal to expected %lg",
+                    i,out->d, getOffsetResult[i][1]);
+            return i*10+2;
+        }
+
+        psFree(out);
+    }
+
+    psFree(coord1);
+    psFree(coord2);
+
+    return 0;
+}
+
+#define DEG_INC2 15.0
+#define VERBOSE 0
+psS32 testProjectTanDeProjectTan()
+{
+    psS32 rc = 0;
+    //
+    // I'm not convinced that the p_psProject() and p_psDeproject() functions work
+    // correctly.  If we project a set of coordinates over a wide range of (R, D)
+    // values, then deproject them, the original (R, D) values are only produced
+    // when D is larger than 0.  This code demonstrates that.
+    //
+    psProjection *tmpProj = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    if (1) { //HEY
+        psPlane planeCoord01;
+        psSphere skyCoord01;
+        psSphere skyCoord02;
+        for (psF32 R = -90.0 ; R <= 90.0 ; R+= DEG_INC2) {
+            for (psF32 D = -90.0 ; D <= 90.0 ; D+= DEG_INC2) {
+                if ((fabs(R) != 90.0) && (fabs(D) != 90.0)) {
+                    skyCoord01.r = DEG_TO_RAD(R);
+                    skyCoord01.d = DEG_TO_RAD(D);
+                    p_psProject(&planeCoord01, &skyCoord01, tmpProj);
+                    p_psDeproject(&skyCoord02, &planeCoord01, tmpProj);
+                    if ((fabs(skyCoord01.r - skyCoord02.r) < FLT_EPSILON) &&
+                            (fabs(skyCoord01.d - skyCoord02.d) < FLT_EPSILON)) {
+                        if (VERBOSE) {
+                            printf("CORRECT: (%.2fr %.2fd) (%.2fr %.2fd) -> (%.2f %.2f) -> (%.2fr %.2fd)\n", R, D,
+                                   skyCoord01.r, skyCoord01.d,
+                                   planeCoord01.x, planeCoord01.y,
+                                   skyCoord02.r, skyCoord02.d);
+                        }
+                        rc = 1;
+                    } else {
+                        printf("TEST ERROR: (%.2fr %.2fd) (%.2fr %.2fd) -> (%.2f %.2f) -> (%.2fr %.2fd)\n", R, D,
+                               skyCoord01.r, skyCoord01.d,
+                               planeCoord01.x, planeCoord01.y,
+                               skyCoord02.r, skyCoord02.d);
+                        rc = 1;
+                    }
+                }
+            }
+        }
+    }
+    return(rc);
+}
+
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psCoord02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psCoord02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psCoord02.c	(revision 22322)
@@ -0,0 +1,572 @@
+/** @file  tst_psCoord02.c
+*
+*  @brief This file contains test code for:
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-02-17 03:24:46 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+static psS32 test04( void );
+static psS32 test05( void );
+static psS32 test06( void );
+static psS32 test07( void );
+
+testDescription tests[] = {
+                              {test04, 660, "psPlaneTransformCombine()", 0, false},
+                              {test05, 662, "psPlaneTransformFit()", 0, false},
+                              {test06, 663, "psPlaneTransformInvert()", 0, false},
+                              {test07, 666, "psPlaneTransformDeriv()", 0, false},
+                              {NULL}
+                          };
+
+#define PS_PERCENT_COMPARE(X, Y, PERCENT_FRACTION) (fabs((Y)-(X))/fabs(X) < (PERCENT_FRACTION))
+
+//#define PS_GEN_RAN_FLOAT (-1.0 + (psF32) (rand() % 2))
+#define PS_GEN_RAN_FLOAT (-0.5 + ((psF32)rand()) / ((psF32) RAND_MAX))
+#define RANDOM_SEED 1995
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+    psLogSetFormat("HLNM");
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("psPlaneTransformCombine", 0);
+    psTraceSetLevel("multiplyDPoly2D", 0);
+    srand(RANDOM_SEED);
+
+    return ( ! runTestSuite( stderr, "psImage", tests, argc, argv ) );
+}
+
+
+/******************************************************************************
+TEST04: The strategy behind test04() is to generate a pair of transforms t1
+and t2 with a wide variety of sizes for the x/y components of the x/y
+transforms, then set the coefficients of t1/t2 to arbitray values, then
+generate a new transform t3 via psPlaneTransformCombine() function.  Then, for
+several arbitrary input plane coordinates, we tarnsform them via the new
+transform t3, as well as individually via t1 then t2, and verify that they
+produce identical output coordinates.
+ *****************************************************************************/
+// These macros determine the number of x/y test points which will be x-formed.
+#define TST04_X_MIN 0.0
+#define TST04_X_MAX 10.0
+#define TST04_Y_MIN 0.0
+#define TST04_Y_MAX 10.0
+#define TST04_NUM 5.0
+// These macros define the max sizes of the various input transforms.
+#define TST04_T1_X_X 2
+#define TST04_T1_X_Y 3
+#define TST04_T1_Y_X 3
+#define TST04_T1_Y_Y 3
+#define TST04_T2_X_X 3
+#define TST04_T2_X_Y 3
+#define TST04_T2_Y_X 3
+#define TST04_T2_Y_Y 3
+/******************************************************************************
+tstTransforms(t1, t2, t3): this function generates a set of arbitrary x/y
+coordinates, then transforms them into output coordinates via the t3
+transform, as well as the t1 followed by the t2 transform.  It verifies the
+output coordinates are identical and returns TRUE if so.  Otherwise, it prints
+an error message and returns FALSE.
+ *****************************************************************************/
+bool tstTransforms(psPlaneTransform *t1, psPlaneTransform *t2, psPlaneTransform *t3)
+{
+    bool testStatus = true;
+    psPlane *in = psPlaneAlloc();
+    in->xErr = 0.0;
+    in->yErr = 0.0;
+
+    for (in->x = TST04_X_MIN ; in->x <= TST04_X_MAX ; in->x+= (TST04_X_MAX-TST04_X_MIN) / TST04_NUM) {
+        for (in->y = TST04_Y_MIN ; in->y <= TST04_Y_MAX ; in->y+= (TST04_Y_MAX-TST04_Y_MIN) / TST04_NUM) {
+            // Apply the t1/t2 transforms individually to the in coords.
+            psPlane *mid = psPlaneTransformApply(NULL, t1, in);
+            if (mid == NULL) {
+                printf("TEST ERROR: intermediate psPlane coords are NULL.\n");
+                psFree(in);
+                return(false);
+            }
+
+            psPlane *outT1T2 = psPlaneTransformApply(NULL, t2, mid);
+            if (outT1T2 == NULL) {
+                printf("TEST ERROR: intermediate psPlane coords are NULL.\n");
+                psFree(mid);
+                return(false);
+            }
+
+            // Apply the t3 transforms individually to the in coords.
+            psPlane *outT3 = psPlaneTransformApply(NULL, t3, in);
+            if (outT3 == NULL) {
+                printf("TEST ERROR: intermediate psPlane coords are NULL.\n");
+                psFree(mid);
+                psFree(outT1T2);
+                return(false);
+            }
+
+            // Verify that the results are identical.
+            if (!PS_PERCENT_COMPARE(outT3->x, outT1T2->x, 0.01)) {
+                printf("TEST ERROR: x is %f, should be %f\n", outT3->x, outT1T2->x);
+                testStatus = false;
+            }
+            // Verify that the results are identical.
+            if (!PS_PERCENT_COMPARE(outT3->y, outT1T2->y, 0.01)) {
+                printf("TEST ERROR: y is %f, should be %f\n", outT3->y, outT1T2->y);
+                testStatus = false;
+            }
+            if (0) {
+                if (fabs(outT3->x - outT1T2->x) > FLT_EPSILON) {
+                    printf("TEST ERROR: x is %f, should be %f\n", outT3->x, outT1T2->x);
+                    testStatus = false;
+                    printf("(in->x, in->y) is (%f, %f)\n", in->x, in->y);
+                    printf("(mid->x, mid->y) is (%f, %f)\n", mid->x, mid->y);
+                    printf("(outT1T2->x, outT1T2->y) is (%f, %f)\n", outT1T2->x, outT1T2->y);
+                    printf("(outT3->x, outT3->y) is (%f, %f)\n", outT3->x, outT3->y);
+                    PS_POLY_PRINT_2D(t1->x);
+                    PS_POLY_PRINT_2D(t1->y);
+                    PS_POLY_PRINT_2D(t2->x);
+                    PS_POLY_PRINT_2D(t2->y);
+                    PS_POLY_PRINT_2D(t3->x);
+                    PS_POLY_PRINT_2D(t3->y);
+                }
+                if (fabs(outT3->y - outT1T2->y) > FLT_EPSILON) {
+                    printf("TEST ERROR: y is %f, should be %f\n", outT3->y, outT1T2->y);
+                    testStatus = false;
+                    printf("(in->x, in->y) is (%f, %f)\n", in->x, in->y);
+                    printf("(mid->x, mid->y) is (%f, %f)\n", mid->x, mid->y);
+                    printf("(outT1T2->x, outT1T2->y) is (%f, %f)\n", outT1T2->x, outT1T2->y);
+                    printf("(outT3->x, outT3->y) is (%f, %f)\n", outT3->x, outT3->y);
+                    PS_POLY_PRINT_2D(t1->x);
+                    PS_POLY_PRINT_2D(t1->y);
+                    PS_POLY_PRINT_2D(t2->x);
+                    PS_POLY_PRINT_2D(t2->y);
+                    PS_POLY_PRINT_2D(t3->x);
+                    PS_POLY_PRINT_2D(t3->y);
+                }
+            }
+            psFree(mid);
+            psFree(outT1T2);
+            psFree(outT3);
+        }
+    }
+
+    psFree(in);
+    return(testStatus);
+}
+
+/******************************************************************************
+setCoeffs(t1, t2): this function sets the coefficients of the t1 and t2
+transforms to somewhat arbitrary values.
+ *****************************************************************************/
+int setCoeffs(psPlaneTransform *t1, psPlaneTransform *t2)
+{
+    for (psS32 t1xx = 0 ; t1xx < t1->x->nX+1 ; t1xx++) {
+        for (psS32 t1xy = 0 ; t1xy < t1->x->nY+1 ; t1xy++) {
+            t1->x->coeff[t1xx][t1xy] = PS_GEN_RAN_FLOAT;
+            //printf("%f\n", t1->x->coeff[t1xx][t1xy]);
+        }
+    }
+
+    for (psS32 t1yx = 0 ; t1yx < t1->y->nX+1 ; t1yx++) {
+        for (psS32 t1yy = 0 ; t1yy < t1->y->nY+1 ; t1yy++) {
+            t1->y->coeff[t1yx][t1yy] = PS_GEN_RAN_FLOAT;
+            //printf("%f\n", t1->y->coeff[t1yx][t1yy]);
+        }
+    }
+    for (psS32 t2xx = 0 ; t2xx < t2->x->nX+1 ; t2xx++) {
+        for (psS32 t2xy = 0 ; t2xy < t2->x->nY+1 ; t2xy++) {
+            t2->x->coeff[t2xx][t2xy] = PS_GEN_RAN_FLOAT;
+            //printf("%f\n", t2->x->coeff[t2xx][t2xy]);
+        }
+    }
+    for (psS32 t2yx = 0 ; t2yx < t2->y->nX+1 ; t2yx++) {
+        for (psS32 t2yy = 0 ; t2yy < t2->y->nY+1 ; t2yy++) {
+            t2->y->coeff[t2yx][t2yy] = PS_GEN_RAN_FLOAT;
+            //printf("%f\n", t2->y->coeff[t2yx][t2yy]);
+        }
+    }
+
+    return(0);
+}
+
+/******************************************************************************
+test04(): We test psPlaneTransformCombine() with a variety of input
+transforms, as well as input coordinates, as well as erroneous input
+parameters.
+ *****************************************************************************/
+psS32 test04( void )
+{
+    bool testStatus = true;
+
+    //HEY
+    // Create a variety of input transforms, then test them.
+    //
+    for (psS32 t1xx = 0 ; t1xx < TST04_T1_X_X ; t1xx++) {
+        for (psS32 t1xy = 0 ; t1xy < TST04_T1_X_Y ; t1xy++) {
+            for (psS32 t1yx = 0 ; t1yx < TST04_T1_Y_X ; t1yx++) {
+                for (psS32 t1yy = 0 ; t1yy < TST04_T1_Y_Y ; t1yy++) {
+                    for (psS32 t2xx = 0 ; t2xx < TST04_T2_X_X ; t2xx++) {
+                        for (psS32 t2xy = 0 ; t2xy < TST04_T2_X_Y ; t2xy++) {
+                            for (psS32 t2yx = 0 ; t2yx < TST04_T2_Y_X ; t2yx++) {
+                                for (psS32 t2yy = 0 ; t2yy < TST04_T2_Y_Y ; t2yy++) {
+                                    //printf("(%d %d %d %d %d %d %d %d)\n", t1xx, t1xy, t1yx, t1yy, t2xx, t2xy, t2yx, t2yy);
+                                    psPlaneTransform *trans1 = psPlaneTransformAlloc(PS_MAX(t1xx, t1yx), PS_MAX(t1xy, t1yy));
+                                    psPlaneTransform *trans2 = psPlaneTransformAlloc(PS_MAX(t2xx, t2yx), PS_MAX(t2xy, t2yy));
+                                    psPlaneTransform *trans3 = NULL;
+                                    setCoeffs(trans1, trans2);
+                                    trans3 = psPlaneTransformCombine(NULL, trans1,
+                                                                     trans2, psRegionSet(NAN,NAN,NAN,NAN), 0);
+                                    //XXX: the last two parameters are bogus.  Needs to change.  -rdd
+
+                                    if (trans3 == NULL) {
+                                        printf("TEST ERROR: psPlaneTransformCombine() returned NULL/\n");
+                                        testStatus = false;
+                                    } else {
+                                        testStatus = tstTransforms(trans1, trans2, trans3);
+                                    }
+                                    psFree(trans1);
+                                    psFree(trans2);
+                                    psFree(trans3);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    //
+    // Test erroneous input parameter combinations
+    //
+    psPlaneTransform *trans1 = psPlaneTransformAlloc(2, 2);
+    psPlaneTransform *trans2 = psPlaneTransformAlloc(2, 2);
+    psPlaneTransform *trans3 = NULL;
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling psPlaneTransformCombine with NULL trans1.  Should generate error and return NULL.\n");
+    trans3 = psPlaneTransformCombine(NULL, NULL, trans2, psRegionSet(0,0,0,0), 10);
+    if (trans3 != NULL) {
+        printf("TEST ERROR: psPlaneTransformCombine() returned a non-NULL psPlaneTransform.\n");
+        testStatus = false;
+        psFree(trans3);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling psPlaneTransformCombine with NULL trans2.  Should generate error and return NULL.\n");
+    trans3 = psPlaneTransformCombine(NULL, trans1, NULL, psRegionSet(0,0,0,0), 10);
+    if (trans3 != NULL) {
+        printf("TEST ERROR: psPlaneTransformCombine() returned a non-NULL psPlaneTransform.\n");
+        testStatus = false;
+        psFree(trans3);
+    }
+
+    psFree(trans1);
+    psFree(trans2);
+    return(!testStatus);
+}
+
+
+#define TST05_X_MIN 0.0
+#define TST05_X_MAX 10.0
+#define TST05_Y_MIN 0.0
+#define TST05_Y_MAX 10.0
+#define TST05_NUM_X 5.0
+#define TST05_NUM_Y 5.0
+#define TST05_X_POLY_ORDER 3
+#define TST05_Y_POLY_ORDER 3
+#define TST05_NUM_DATA (TST05_NUM_X * TST05_NUM_Y)
+#define TST05_IGNORE 1
+#define TST05_VERBOSE 0
+/******************************************************************************
+XXX: This is only a rudimentary test of the psPlaneTransformFit() function.
+It tests a few NULL input parameter conditions, and some simple linear
+transformations.
+ 
+// HEY
+ *****************************************************************************/
+psS32 test05( void )
+{
+    bool testStatus = true;
+    psPlaneTransform *trans = psPlaneTransformAlloc(TST05_X_POLY_ORDER, TST05_Y_POLY_ORDER);
+    psPlaneTransform *transInit = psPlaneTransformAlloc(TST05_X_POLY_ORDER, TST05_Y_POLY_ORDER);
+    psArray *src = psArrayAlloc((int) TST05_NUM_DATA);
+    psArray *dst = psArrayAlloc((int) TST05_NUM_DATA);
+    bool rc;
+
+    //
+    // We set an arbitrary non-linear transformation.
+    //
+    for (psS32 x = 0 ; x < TST05_X_POLY_ORDER+1 ; x++) {
+        for (psS32 y = 0 ; y < TST05_Y_POLY_ORDER+1 ; y++) {
+            transInit->x->coeff[x][y] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+            transInit->y->coeff[x][y] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+        }
+    }
+    if (TST05_VERBOSE) {
+        PS_POLY_PRINT_2D(transInit->x);
+        PS_POLY_PRINT_2D(transInit->y);
+    }
+
+    //
+    // We generate a grid of input data points in the x1,y1 plane, calculate the
+    // corresponding values in the transformed plane, and set these to
+    // the src and dst psVectors.
+    //
+    psS32 i = 0;
+    for (psF32 x = TST05_X_MIN ; x < TST05_X_MAX ; x+= (TST05_X_MAX-TST05_X_MIN)/TST05_NUM_X) {
+        for (psF32 y = TST05_Y_MIN ; y < TST05_Y_MAX ; y+= (TST05_Y_MAX-TST05_Y_MIN)/TST05_NUM_Y) {
+            if (i == src->n) {
+                if (src->n == src->nalloc) {
+                    src = psArrayRealloc(src, src->n+1);
+                }
+                src->n++;
+            }
+            if (i == dst->n) {
+                if (dst->n == dst->nalloc) {
+                    dst = psArrayRealloc(dst, dst->n+1);
+                }
+                dst->n++;
+            }
+            src->data[i] = (psPtr *) psPlaneAlloc();
+            ((psPlane *) src->data[i])->x = x;
+            ((psPlane *) src->data[i])->y = y;
+
+            dst->data[i] = psPlaneTransformApply(NULL, transInit, ((psPlane *) src->data[i]));
+            i++;
+        }
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling psPlaneTransformFit with NULL trans.  Should generate error and return NULL.\n");
+    rc = psPlaneTransformFit(NULL, src, dst, 100, 100.0);
+    if (rc == true) {
+        printf("TEST ERROR1: psPlaneTransformFit() returned TRUE.\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling psPlaneTransformFit with NULL src psArray.  Should generate error and return NULL.\n");
+    rc = psPlaneTransformFit(trans, NULL, dst, 100, 100.0);
+    if (rc == true) {
+        printf("TEST ERROR2: psPlaneTransformFit() returned TRUE.\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling psPlaneTransformFit with NULL dst psArray.  Should generate error and return NULL.\n");
+    rc = psPlaneTransformFit(trans, src, NULL, 100, 100.0);
+    if (rc == true) {
+        printf("TEST ERROR3: psPlaneTransformFit() returned TRUE.\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling psPlaneTransformFit with acceptable data.\n");
+    rc = psPlaneTransformFit(trans, src, dst, 100, 100.0);
+    if (rc != true) {
+        printf("TEST ERROR4: psPlaneTransformFit() returned FALSE.\n");
+        testStatus = false;
+    } else {
+        if (TST05_VERBOSE) {
+            PS_POLY_PRINT_2D(trans->x);
+            PS_POLY_PRINT_2D(trans->y);
+        }
+
+        //
+        // For the initial grid of input points, we transform them to output points with
+        // the derived transformation, and verify that they are within 10%.
+        //
+
+        for (psS32 i = TST05_IGNORE ; i < src->n-TST05_IGNORE ; i++) {
+            psPlane *inData = (psPlane *) src->data[i];
+            psPlane *outData = (psPlane *) dst->data[i];
+            psPlane *outDataDeriv = psPlaneTransformApply(NULL, trans, inData);
+            if (!PS_PERCENT_COMPARE(outDataDeriv->x, outData->x, 0.20) ||
+                    !PS_PERCENT_COMPARE(outDataDeriv->y, outData->y, 0.20)) {
+                printf("TEST ERROR: the derived output coords (%d) were (%.2f, %.2f) should have been (%.2f, %.2f).\n",
+                       i, outDataDeriv->x, outDataDeriv->y, outData->x, outData->y);
+                testStatus = false;
+            } else if (TST05_VERBOSE) {
+                printf("GOOD: the derived output coords (%d) were (%.2f, %.2f) should have been (%.2f, %.2f).\n",
+                       i, outDataDeriv->x, outDataDeriv->y, outData->x, outData->y);
+            }
+            psFree(outDataDeriv);
+        }
+    }
+
+    psFree(transInit);
+    psFree(trans);
+    psFree(src);
+    psFree(dst);
+
+    return(!testStatus);
+}
+
+#define NUM_TRANSFORMS 100
+/******************************************************************************
+This is only a rudimentary test of the psPlaneTransformInvert() function.
+It tests:
+    A few NULL input parameter conditions.
+    Several random linear transformations.
+XXX: Must extensively test:
+    Non-linear transformations.
+  *****************************************************************************/
+psS32 test06( void )
+{
+    bool testStatus = true;
+    psPlaneTransform *trans = psPlaneTransformAlloc(1, 1);
+    psPlaneTransform *transInverse = NULL;
+    psRegion myRegion = psRegionSet(1.0, 5.0, 1.0, 5.0);
+
+    trans->x->coeff[0][0] = 0.0;
+    trans->x->coeff[0][1] = 1.0;
+    trans->x->coeff[1][0] = 2.0;
+    trans->x->coeff[1][1] = 3.0;
+    trans->y->coeff[0][0] = 4.0;
+    trans->y->coeff[0][1] = 5.0;
+    trans->y->coeff[1][0] = 6.0;
+    trans->y->coeff[1][1] = 7.0;
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling psPlaneTransformInvert with NULL trans.  Should generate error and return NULL.\n");
+    transInverse = psPlaneTransformInvert(NULL, NULL, myRegion, 10);
+    if (transInverse != NULL) {
+        printf("TEST ERROR: psPlaneTransformInvert() returned a non-NULL psPlaneTransform.\n");
+        testStatus = false;
+        psFree(transInverse);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling psPlaneTransformInvert with zero nSamples.  Should generate error and return NULL.\n");
+    transInverse = psPlaneTransformInvert(NULL, trans, myRegion, 0);
+    if (transInverse != NULL) {
+        printf("TEST ERROR: psPlaneTransformInvert() returned a non-NULL psPlaneTransform.\n");
+        testStatus = false;
+        psFree(transInverse);
+    }
+
+    printf("Calling psPlaneTransformInvert with acceptable linear transformations.\n");
+    for (psS32 n = 0 ; n < NUM_TRANSFORMS ; n++) {
+        if (n == 0) {
+            // I ensure that we test the identity transformation since this was
+            // giving us probs before.
+            trans->x->coeff[0][0] = 0.0;
+            trans->x->coeff[0][1] = 0.0;
+            trans->x->coeff[1][0] = 1.0;
+            trans->x->coeff[1][1] = 0.0;
+            trans->y->coeff[0][0] = 0.0;
+            trans->y->coeff[0][1] = 1.0;
+            trans->y->coeff[1][0] = 0.0;
+            trans->y->coeff[1][1] = 0.0;
+        } else {
+            // We create a random linear transformation and hope it is invertible.
+            trans->x->coeff[0][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+            trans->x->coeff[0][1] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+            trans->x->coeff[1][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+            trans->x->coeff[1][1] = 0.0;
+            trans->y->coeff[0][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+            trans->y->coeff[0][1] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+            trans->y->coeff[1][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
+            trans->y->coeff[1][1] = 0.0;
+        }
+
+        transInverse = psPlaneTransformInvert(NULL, trans, myRegion, 10);
+        if (transInverse == NULL) {
+            printf("TEST ERROR: psPlaneTransformInvert() returned a NULL psPlaneTransform.\n");
+            testStatus = false;
+        }
+        //
+        // Transform the "in" coords to "mid" with psPlaneTransform "trans", then
+        // transform "mid" to "out" with psPlaneTransform "transInverse".
+        //
+        // XXX: Loop on a few data points.
+        //
+        psPlane *in = psPlaneAlloc();
+        in->x = 2.0;
+        in->y = 3.0;
+        psPlane *mid = psPlaneTransformApply(NULL, trans, in);
+        psPlane *out = psPlaneTransformApply(NULL, transInverse, mid);
+
+        if (((fabs(in->x - out->x) > FLT_EPSILON)) ||
+                ((fabs(in->y - out->y) > FLT_EPSILON)) ||
+                isnan(out->x) ||
+                isnan(out->y)) {
+            printf("TEST ERROR: in coords were (%f, %f), out coords were (%f, %f).\n",
+                   in->x, in->y, out->x, out->y);
+            testStatus = false;
+        }
+
+        psFree(in);
+        psFree(mid);
+        psFree(out);
+        psFree(transInverse);
+    }
+
+    psFree(trans);
+
+    fflush(stdout);
+
+    return(!testStatus);
+}
+
+psS32 test07( void )
+{
+    psPlane *coord = psPlaneAlloc();
+    psPlane *deriv = NULL;
+    psPlaneTransform *trans = NULL;
+
+    //Set fxn values for evaluation
+    coord->x = 3.0;
+    coord->y = 1.0;
+
+    //Return NULL for NULL input plane transform
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    deriv = psPlaneTransformDeriv(NULL, trans, coord);
+    if (deriv != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psPlaneTransformDeriv failed to return NULL for NULL plane transform input.\n");
+        return 1;
+    }
+
+    trans = psPlaneTransformAlloc(1, 3);
+
+    //Set Polynomials.  f(x) = x, f(y) = 0.5*y^2  -->  f'(x) = 1, f'(y) = y
+    //So for 1,1  -> f'(1) = 1, f'(1) = 1
+    trans->x->coeff[0][0] = 0.0;
+    trans->x->coeff[1][0] = 1.0;
+    trans->y->coeff[0][0] = 0.0;
+    trans->y->coeff[0][1] = 0.0;
+    trans->y->coeff[0][2] = 0.5;
+
+    //Return NULL for NULL input plane
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    deriv = psPlaneTransformDeriv(NULL, trans, NULL);
+    if (deriv != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psPlaneTransformDeriv failed to return NULL for NULL plane input.\n");
+        return 2;
+    }
+
+    //Return correct values.  Should have x=1.0, y=1.0.
+    deriv = psPlaneTransformDeriv(NULL, trans, coord);
+    if (deriv->x != 1.0 || deriv->y != 1.0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psPlaneTransformDeriv failed to return the correct values.\n");
+        printf("\n f' values are = %lf, %lf \n", deriv->x, deriv->y);
+        return 3;
+    }
+
+    psFree(trans);
+    psFree(deriv);
+    psFree(coord);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psEarthOrientation.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psEarthOrientation.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psEarthOrientation.c	(revision 22322)
@@ -0,0 +1,1205 @@
+/** @file  tst_psEarthOrientation.c
+*
+*  @brief The code will perform earth orientation calculations and sphere rotations.
+*
+*  @author d-Rob, MHPCC
+*
+*  @version $Revision: 1.35 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-05-26 01:23:41 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+
+static psS32 testAberration(void);
+static psS32 testGravityDeflect(void);
+static psS32 testEOCPrecession(void);
+static psS32 testEOCPrecessionCorr(void);
+static psS32 testEOCPolar(void);
+static psS32 testEOCPolarTide(void);
+static psS32 testEOCNutation(void);
+static psS32 testSphereRot_TEOtoCEO(void);
+static psS32 testSphereRot_CEOtoGCRS(void);
+static psS32 testSphereRot_ITRStoTEO(void);
+static psS32 testSphereRotPrecess(void);
+
+testDescription tests[] = {
+                              {testAberration, 666, "psAberration()", 0, false},
+                              {testGravityDeflect, 667, "psGravityDeflect()", 0, false},
+                              {testEOCPrecession, 669, "psEOCPrecession()", 0, false},
+                              {testEOCPrecessionCorr, 670, "psEOCPrecessionCorr()", 0, false},
+                              {testEOCPolar, 671, "psEOCPolar()", 0, false},
+                              {testEOCPolarTide, 672, "psEOCPolarTide()", 0, false},
+                              {testEOCNutation, 673, "psEOCNutation()", 0, false},
+                              {testSphereRot_TEOtoCEO, 674, "psSphereRot_TEOtoCEO()", 0, false},
+                              {testSphereRot_CEOtoGCRS, 675, "psSphereRot_CEOtoGCRS()", 0, false},
+                              {testSphereRot_ITRStoTEO, 676, "psSphereRRot_ITRStoTEO()", 0, false},
+                              {testSphereRotPrecess, 677, "psSphereRotPrecess()", 0, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    // Initialize library internal structures
+    psLibInit("pslib.config");
+
+    if( !runTestSuite(stderr,"psEarthOrientation",tests,argc,argv)) {
+        return 1;
+    }
+
+    // Cleanup library
+    psLibFinalize();
+
+    return 0;
+
+    //    return ( ! runTestSuite( stderr, "psEarthOrientation", tests, argc, argv ) );
+}
+
+#define timesec 1049160600
+#define objR DEG_TO_RAD(122.9153182445501)
+#define objD DEG_TO_RAD(48.562968978679194)
+#define VERBOSE 0
+static psSphere *obj = NULL;
+static void objSetup(void);
+static double greatCircle(psSphere *in1, psSphere *in2);
+
+static void objSetup(void)
+{
+    if (obj == NULL) {
+        //        obj = psSphereAlloc();
+        //        obj->r = objR;
+        //        obj->d = objD;
+        psCube *tempCube = psCubeAlloc();
+        tempCube->x = -0.3598480726985338;
+        tempCube->y = 0.5555012823608123;
+        tempCube->z = 0.7496183628158023;
+        obj = psCubeToSphere(tempCube);
+        psFree(tempCube);
+    }
+}
+
+//returns the great circle ANGULAR distance between 2 positions (psSphere's)
+static double greatCircle(psSphere *in1, psSphere *in2)
+{
+    double r1, r2, d1, d2;
+    d1 = in1->r;
+    d2 = in2->r;
+    r1 = in1->d;
+    r2 = in2->d;
+    double out = 0.0;
+    double c1, c2, cd, s1, s2, sum, ac;
+    c1 = cos(r1);
+    c2 = cos(r2);
+    cd = cos(d1-d2);
+    s1 = sin(r1);
+    s2 = sin(r2);
+    sum = c1*c2*cd + s1*s2;
+    if (sum > 1.0)
+        sum = 1.0 - (sum - 1.0);
+
+    ac = acos(sum);
+    out = ac;
+    //    printf("\n  c1=%lf, c2=%lf, cd=%lf, s1=%lf, s2=%lf, sum=%.19g, ac=%g\n", c1,c2,cd,s1,s2,sum,ac);
+    //    out = acos(cos(r1)*cos(r2)*cos(d1-d2) + sin(r1)*sin(r2));
+    //    if (out != 0.0) printf("\n   in greatCircle, out = %lf\n", out);
+    return out;
+}
+
+psS32 testAberration(void)
+{
+    psSphere *apparent = NULL;
+    psSphere *empty = NULL;
+    psCube *actualCube = psCubeAlloc();
+    //values from After gravity deflection//
+    actualCube->x = -0.35961949760293604;
+    actualCube->y = 0.5555613950298085;
+    actualCube->z = 0.7496835020836093;
+    psSphere *actual = psCubeToSphere(actualCube);
+    psCube *cubeDir = psCubeAlloc();
+    cubeDir->x = 5148.713262821658;
+    cubeDir->y = -26945.04752348012;
+    cubeDir->z = -11682.787302030947;
+    cubeDir->x += -357.6031690489248;
+    cubeDir->y += 248.46429758174693;
+    cubeDir->z += 0.09694774143797581;
+    psSphere *direction = psCubeToSphere(cubeDir);
+    double speed = sqrt(cubeDir->x*cubeDir->x + cubeDir->y*cubeDir->y + cubeDir->z*cubeDir->z);
+    double c = 299792458.0; // Speed of light in vacuum (src:NIST)   /* m/s */
+    speed /= c;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    empty = psAberration(empty, apparent, direction, speed);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psAberration failed to return NULL for NULL actual input.\n");
+        return 1;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    empty = psAberration(empty, actual, apparent, speed);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psAberration failed to return NULL for NULL direction input.\n");
+        return 2;
+    }
+
+    apparent = psAberration(apparent, actual, direction, speed);
+    psCube *outCube = psSphereToCube(apparent);
+    if (VERBOSE) {
+        printf("\n -- resultCube = x,y,z =     %.13g,      %.13g,    %.13g  -- \n",
+               outCube->x, outCube->y, outCube->z);
+    }
+    //expected cube values
+    double x, y, z;
+    x = -0.35963388069046304;
+    y = 0.5555192509816625;
+    z = 0.7497078321908413;
+
+    if (VERBOSE) {
+        printf(" -- expectedCube = x,y,z =   %.13g,     %.13g,    %.13g  -- \n", x, y, z);
+        printf("Cube Difference  =  x,y,z  = %.13g, %.13g, %.13g \n\n",
+               (x - outCube->x), (y - outCube->y), (z - outCube->z) );
+    }
+    psFree(actual);
+    actualCube->x = x;
+    actualCube->y = y;
+    actualCube->z = z;
+    actual = psCubeToSphere(actualCube);
+    double xxx = greatCircle(actual, apparent);
+    if (VERBOSE) {
+        printf("   The great circle angular distance between expected & result = %.13g\n",
+               xxx);
+    }
+    if ( fabs(x - outCube->x) > FLT_EPSILON || fabs(y - outCube->y) > FLT_EPSILON ||
+            fabs(z - outCube->z) > FLT_EPSILON ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psAberration returned incorrect values.\n");
+        //        printf("\nMagnitude of expected change = x,y,z = %.13g, %.13g, %.13g \n",
+        //               (actualCube->x - x), (actualCube->y - y), (actualCube->z - z) );
+        //        printf("Magnitude of actual change = x,y,z = %.13g, %.13g, %.13g \n",
+        //               (actualCube->x - outCube->x), (actualCube->y - outCube->y),
+        //               (actualCube->z - outCube->z) );
+        psFree(actual);
+        actualCube->x = x;
+        actualCube->y = y;
+        actualCube->z = z;
+        actual = psCubeToSphere(actualCube);
+        x = greatCircle(actual, apparent);
+        if (VERBOSE) {
+            printf("   The great circle angular distance between expected & result = %.13g\n",
+                   x);
+        }
+        return 3;
+    }
+
+    psFree(outCube);
+    psFree(actualCube);
+    psFree(cubeDir);
+    psFree(apparent);
+    psFree(actual);
+    psFree(direction);
+
+    return 0;
+}
+
+psS32 testGravityDeflect(void)
+{
+    psSphere *apparent = NULL;
+    psSphere *empty = NULL;
+    psCube *actualCube = psCubeAlloc();
+    actualCube->x = -0.3596195125758298;
+    actualCube->y = 0.5555613903455866;
+    actualCube->z = 0.7496834983724809;
+    psSphere *actual = psCubeToSphere(actualCube);
+    psCube *sunCube = psCubeAlloc();
+    sunCube->x = 1.467797790127511e11;
+    sunCube->y = 2.5880956908748722e10;
+    sunCube->z = 1.1220046291457653e10;
+    double sunLength = sqrt(sunCube->x*sunCube->x + sunCube->y*sunCube->y + sunCube->z*sunCube->z);
+    sunCube->x /= sunLength;
+    sunCube->y /= sunLength;
+    sunCube->z /= sunLength;
+    if (VERBOSE) {
+        printf("sunCube = x,y,z = %.13g, %.13g, %.13g\n",
+               sunCube->x, sunCube->y, sunCube->z);
+    }
+    psSphere *sun = psCubeToSphere(sunCube);
+    if (VERBOSE) {
+        printf("sunSphere  = r, d = %.13g, %.13g\n", sun->r, sun->d);
+    }
+    psCube *outCube = psCubeAlloc();
+
+    empty = psGravityDeflection(apparent, empty, sun);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "psGravityDeflection Failed to return NULL for NULL actual input sphere.\n");
+        return 1;
+    }
+    empty = psGravityDeflection(apparent, actual, empty);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "psGravityDeflection Failed to return NULL for NULL sun input sphere.\n");
+        return 2;
+    }
+
+    apparent = psGravityDeflection(NULL, actual, sun);
+    //    apparent->r *= -1.0;
+    //    apparent->d *= -1.0;
+    psSphere *result2;
+    //    psSphere *result2 = psSphereSetOffset(actual, apparent, PS_SPHERICAL, PS_RADIAN);
+    //    psSphere *result = psSphereSetOffset(actual, apparent, PS_SPHERICAL, PS_RADIAN);
+    //    psSphere *result = psSphereGetOffset(apparent, actual, PS_SPHERICAL, PS_RADIAN);
+    if (VERBOSE) {
+        printf(" -- actualCube = x,y,z = %.13g, %.13g, %.13g  -- \n",
+               actualCube->x, actualCube->y, actualCube->z);
+    }
+    //    psCube *outCube = psSphereToCube(result);
+    //    printf(" -- resultCube = x,y,z = %.13g, %.13g, %.13g  -- \n",
+    //           outCube->x, outCube->y, outCube->z);
+    psCube *outCube2 = psSphereToCube(apparent);
+    if (VERBOSE) {
+        printf(" -- resultCube2= x,y,z = %.13g, %.13g, %.13g  -- \n",
+               outCube2->x, outCube2->y, outCube2->z);
+    }
+    double x, y, z;
+    x = -0.35961949760293604;
+    y = 0.5555613950298085;
+    z = 0.7496835020836093;
+
+    if (VERBOSE) {
+        printf(" -- expectCube = x,y,z = %.13g, %.13g, %.13g  -- \n\n", x, y, z);
+
+        //    if ( fabs(x - outCube->x) > DBL_EPSILON || fabs(y - outCube->y) > DBL_EPSILON ||
+        //            fabs(z - outCube->z) > DBL_EPSILON ) {
+        //        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+        //                "psGravityDeflection returned incorrect values.\n");
+        printf("expect-actual=  x,y,z  = %.13g, %.13g, %.13g \n",
+               (x - actualCube->x), (y - actualCube->y), (z - actualCube->z) );
+        printf("expect-result=  x,y,z  = %.13g, %.13g, %.13g \n",
+               (x - outCube2->x), (y - outCube2->y), (z - outCube2->z) );
+        printf("result-actual=  x,y,z  = %.13g, %.13g, %.13g \n",
+               (outCube2->x - actualCube->x), (outCube2->y - actualCube->y),
+               (outCube2->z - actualCube->z) );
+    }
+    //        return 1;
+    //    }
+    //    psFree(result2);
+    outCube2->x = x;
+    outCube2->y = y;
+    outCube2->z = z;
+    result2 = psCubeToSphere(outCube2);
+    //    psFree(result);
+    psSphere *result = psSphereGetOffset(actual, result2, PS_SPHERICAL, PS_RADIAN);
+    psFree(result2);
+    result2 = psSphereGetOffset(actual, apparent, PS_SPHERICAL, PS_RADIAN);
+    if (VERBOSE) {
+        printf("The apparent output sphere = r,d = %.13g, %.13g\n", result2->r, result2->d);
+        printf("The expected output sphere = r,d = %.13g, %.13g\n\n", result->r, result->d);
+    }
+    psFree(result2);
+
+    psFree(outCube);
+    psFree(outCube2);
+    psFree(sunCube);
+    psFree(actualCube);
+    psFree(result);
+    psFree(actual);
+    psFree(apparent);
+    psFree(sun);
+
+    return 0;
+}
+
+psS32 testEOCPrecession(void)
+{
+    psTime *empty = NULL;
+    psTime *time = psTimeAlloc(PS_TIME_UTC);
+    time->sec = timesec;
+    time->nsec = 0;
+    time->leapsecond = false;
+
+    //Tests for Precession Model //
+    psEarthPole *pmodel = NULL;
+    //Return NULL for NULL time input
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    pmodel = psEOC_PrecessionModel(empty);
+    if (pmodel != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psEOC_PrecessionModel failed to return NULL for NULL time input.\n");
+        return 1;
+    }
+    //Return NULL for UT1 time input
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    psTime *UT1time = psTimeAlloc(PS_TIME_UT1);
+    pmodel = psEOC_PrecessionModel(UT1time);
+    if (pmodel != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psEOC_PrecessionModel failed to return NULL for UT1 input time.\n");
+        return 2;
+    }
+    psFree(UT1time);
+    //Check return values from valid precession input
+    pmodel = psEOC_PrecessionModel(time);
+    if ( pmodel == NULL ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psEOC_PrecessionModel returned NULL for valid input.\n");
+        return 3;
+    } else {
+        double x, y, s;
+        x = 2.857175590089105e-4;
+        y = 2.3968739377734732e-5;
+        s = -1.3970066457904322e-8;
+        if ( fabs(pmodel->x - x) > FLT_EPSILON || fabs(pmodel->y - y) > FLT_EPSILON
+                || fabs(pmodel->s - s) > FLT_EPSILON) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "   psEOC_PrecessionModel return incorrect values.\n");
+            return 4;
+        }
+        if (VERBOSE) {
+            printf("\n  PrecessionModel output = x,y,s = %.13g,  %.13g,  %.13g\n",
+                   pmodel->x, pmodel->y, pmodel->s);
+            printf("  Expected output        = x,y,s = %.13g, %.13g,  %.13g\n", x, y, s);
+            printf("  A difference of:                 %.13g, %.13g, %.13g\n",
+                   (pmodel->x - x), (pmodel->y - y), (pmodel->s - s) );
+        }
+    }
+    psFree(pmodel);
+
+    psFree(time);
+    if (!p_psEOCFinalize() ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "EOC failed finalization!\n");
+        return 12;
+    }
+
+    return 0;
+}
+
+psS32 testEOCPrecessionCorr(void)
+{
+    psTime *empty = NULL;
+    psTime *time2 = psTimeAlloc(PS_TIME_UTC);
+    time2->sec = timesec;
+    time2->nsec = 0;
+    time2->leapsecond = false;
+    //    time2 = psTimeConvert(time2, PS_TIME_TAI);
+
+    //Tests for Precession Correction function//
+    //Return NULL for NULL time input
+    psEarthPole *pcorr = NULL;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    pcorr = psEOC_PrecessionCorr(empty, PS_IERS_A);
+    if (pcorr != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psEOC_PrecessionCorr failed to return NULL for NULL time input.\n");
+        return 5;
+    }
+
+    //Return NULL for Invalid IERS table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    pcorr = psEOC_PrecessionCorr(time2, 3);
+    if (pcorr != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psEOC_PrecessionCorr failed to return NULL for incorrect IERS table.\n");
+        return 6;
+    }
+    psFree(pcorr);
+
+    //Check values from IERS table A
+    pcorr = psEOC_PrecessionCorr(time2, PS_IERS_A);
+    if ( pcorr == NULL ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psEOC_PrecessionCorr returned NULL for valid inputs.\n");
+        return 7;
+    } else {
+        if (VERBOSE) {
+            printf("\nPrecessionCorr output (IERSA) = x,y,s = %.13g,  %.13g, %.13g\n",
+                   pcorr->x, pcorr->y, pcorr->s);
+        }
+    }
+    psFree(pcorr);
+
+    //Check values from IERS table B
+    pcorr = psEOC_PrecessionCorr(time2, PS_IERS_B);
+    if ( pcorr == NULL ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psEOC_PrecessionCorr returned NULL for valid inputs.\n");
+        return 9;
+    } else {
+        double xx, yy, ss;
+        xx = 0.06295703125;
+        yy = -0.0287618408203125;
+        ss = 0.0;
+        xx = SEC_TO_RAD(xx) * 1e-3;
+        yy = SEC_TO_RAD(yy) * 1e-3;
+        //        if ( fabs(pcorr->x - xx) > DBL_EPSILON || fabs(pcorr->y - yy) > DBL_EPSILON
+        //                || fabs(pcorr->s - ss) > DBL_EPSILON) {
+        //            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+        //                    "   psEOC_PrecessionCorr return incorrect values.\n");
+        if (VERBOSE) {
+            printf("PrecessionCorr output (IERSB) = x,y,s = %.13g, %.13g, %.13g\n",
+                   pcorr->x, pcorr->y, pcorr->s);
+            printf("Expected output               = x,y,s = %.13g, %.13g, %.13g\n", xx, yy, ss);
+            printf("          A difference of:              %.13g,  %.13g, %.13g\n\n",
+                   (pcorr->x - xx), (pcorr->y - yy), (pcorr->s - ss) );
+        }
+        //            return 10;
+        //        }
+    }
+    //precess is the *actual* output from PrecessionModel + PrecessionCorr
+    psEarthPole *precess = psEOC_PrecessionModel(time2);
+    precess->x += pcorr->x;
+    precess->y += pcorr->y;
+    double xCorr, yCorr;
+    xCorr = 3.05224300720406e-10;
+    yCorr = -1.39441339235822e-10;
+    //pcorr is the *expected* output from PrecessionModel//
+    pcorr->x = 2.857175590089105e-4;
+    pcorr->y = 2.3968739377734732e-5;
+    pcorr->s = -1.3970066457904322e-8;
+    pcorr->x += xCorr;
+    pcorr->y += yCorr;
+    psSphereRot *precessNutInv = psSphereRot_CEOtoGCRS(precess);
+    psSphereRot *precessNut = psSphereRotConjugate(NULL, precessNutInv);
+    double q0, q1, q2;
+    q0 = -1.1984522406756289e-5;
+    q1 = 1.4285893358610674e-4;
+    q2 = 1.2191193518914336e-10;
+    psSphereRot *pni = psSphereRot_CEOtoGCRS(pcorr);
+    if (fabs(pni->q0-q0) > FLT_EPSILON || fabs(pni->q1-q1) > FLT_EPSILON ||
+            fabs(pni->q2-q2) > FLT_EPSILON ) {
+        printf("\n Error at CEOtoGCRS, output psSphereRot doesn't match expected.\n");
+    }
+    //    printf("  Output from CEOtoGCRS only  = %.13g,%.13g,%.13g,%.13g\n",
+    //           pni->q0, pni->q1, pni->q2, pni->q3);
+    if (VERBOSE) {
+        printf("  Expected sphere rotation    = %.13g, %.13g, %.13g\n", q0,q1,q2);
+        printf("  Result sphere rotation      = %.13g, %.13g, %.13g\n",
+               precessNutInv->q0, precessNutInv->q1, precessNutInv->q2);
+        printf("     Difference         =        %.13g, %.13g, %.13g\n\n",
+               precessNutInv->q0-q0, precessNutInv->q1-q1, precessNutInv->q2-q2);
+    }
+    psCube *objC = psCubeAlloc();
+    //    objC->x = -3.5963388069046304;
+    //    objC->y = 0.5555192509816625;
+    //    objC->z = 0.7497078321908413;
+    //    objSetup();
+
+    //This is the sphere rotation for the *expected* precession output//
+    psSphereRot *pn = psSphereRotConjugate(NULL, pni);
+
+    //    psSphere *sphere = psSphereAlloc();
+    //    *sphere = *obj;
+    //    psFree(obj);
+
+    //create a psSphere for (from) the start position given in eoc_testing//
+    objC->x = -0.35963388069046304;
+    objC->y = 0.5555192509816625;
+    objC->z = 0.7497078321908413;
+    psSphere *sphere = psCubeToSphere(objC);
+
+    psSphere *expect = psSphereRotApply(NULL, pn, sphere);
+    //expected results below - stored in:  sphere  //
+    double x,y,z;
+    x = -0.3598480726985338;
+    y = 0.5555012823608123;
+    z = 0.7496183628158023;
+    if (VERBOSE) {
+        printf("\n<<Expected out       = x,y,z = %.13g, %.13g, %.13g\n", x, y, z);
+    }
+    //    psFree(objC);
+    //    objC = psSphereToCube(expect);
+    //printf("<<Expected out (CEO)  = x,y,z = %.13g, %.13g, %.13g\n", objC->x, objC->y, objC->z);
+    //printf("     Difference     =           %.13g, %.13g, %.13g\n", objC->x-x, objC->y-y, objC->z-z);
+    //    x = objC->x;
+    //    y = objC->y;
+    //    z = objC->z;
+    psSphere *result = psSphereRotApply(NULL, precessNut, sphere);
+    psFree(objC);
+    objC = psSphereToCube(result);
+    double xx = greatCircle(result, expect);
+    if (VERBOSE) {
+        printf("<<Resulting out      = x,y,z = %.13g, %.13g, %.13g\n", objC->x, objC->y, objC->z);
+        printf("     Difference         =      %.13g, %.13g, %.13g\n\n",
+               objC->x-x, objC->y-y, objC->z-z);
+        printf("GREAT CIRCLE DIFFERENCE = %.13g \n", xx);
+    }
+
+    psFree(precess);
+    psFree(precessNut);
+    psFree(precessNutInv);
+    psFree(expect);
+    psFree(objC);
+
+    psFree(sphere);
+    psFree(result);
+    psFree(pn);
+    psFree(pni);
+    psFree(pcorr);
+    psFree(time2);
+    if (!p_psEOCFinalize() ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "EOC failed finalization!\n");
+        return 12;
+    }
+
+    return 0;
+}
+
+psS32 testEOCPolar(void)
+{
+    psTime *in = psTimeAlloc(PS_TIME_UTC);
+    in->sec = timesec;
+    in->nsec = 0;
+    in->leapsecond = false;
+    psTime *empty = NULL;
+    psEarthPole *polarMotion = NULL;
+
+    //Return NULL for NULL input time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    polarMotion = psEOC_GetPolarMotion(empty, PS_IERS_B);
+    if (polarMotion != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psEOC_GetPolarMotion failed to return NULL for NULL input time.\n");
+        return 1;
+    }
+    //Return NULL for incorrect Bulletin.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    polarMotion = psEOC_GetPolarMotion(empty, 3);
+    if (polarMotion != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psEOC_GetPolarMotion failed to return NULL for NULL input time.\n");
+        return 1;
+    }
+
+    //Test for IERS bulletin A.
+    double x, y, s;
+    x = -6.454389659777e-07;
+    y = 2.112606414597e-06;
+    s = 0.0;
+    polarMotion = psEOC_GetPolarMotion(in, PS_IERS_A);
+    if (polarMotion == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psEOC_GetPolarMotion returned NULL for valid input time.\n");
+        return 4;
+    }
+    //    if ( fabs(polarMotion->x - x) > DBL_EPSILON || fabs(polarMotion->y - y) > DBL_EPSILON
+    //            || fabs(polarMotion->s - s) > DBL_EPSILON) {
+    //        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+    //                "psEOC_GetPolarMotion returned incorrect values.\n");
+    if (VERBOSE) {
+        printf("  <>PolarMotion output (IERSA)   = x,y,s = %.13g, %.13g, %.13g\n",
+               polarMotion->x, polarMotion->y, polarMotion->s);
+    }
+    //        printf("  <>PolarMotion expected (IERSA) = x,y,s = %.13g, %.13g, %.13g\n",
+    //               x, y, s);
+    //        return 5;
+    //    }
+    psFree(polarMotion);
+
+    //Return valid values for correct input time.  Test IERS Bulletin B.
+    polarMotion = psEOC_GetPolarMotion(in, PS_IERS_B);
+    //    x = -6.45381397904e-07;
+    //    y = 2.112819726698e-06;
+    //    s = 0.0;
+    if (polarMotion == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psEOC_GetPolarMotion returned NULL for valid input time.\n");
+        return 2;
+    }
+    //    if ( fabs(polarMotion->x - x) > DBL_EPSILON || fabs(polarMotion->y - y) > DBL_EPSILON
+    //            || fabs(polarMotion->s - s) > DBL_EPSILON) {
+    //        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+    //                "psEOC_GetPolarMotion returned incorrect values.\n");
+    if (VERBOSE) {
+        printf("  <>PolarMotion output (IERSB)   = x,y,s = %.13g,  %.13g, %.13g\n",
+               polarMotion->x, polarMotion->y, polarMotion->s);
+    }
+    //        printf("  <>PolarMotion expected (IERSB) = x,y,s = %.13g, %.13g, %.13g\n",
+    //               x, y, s);
+    //        return 3;
+    //    }
+    psEarthPole *nutationCorr = psEOC_NutationCorr(in);
+    if (nutationCorr == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false, "Nutation Correction returned NULL.\n");
+        return 6;
+    }
+    polarMotion->x += nutationCorr->x;
+    polarMotion->y += nutationCorr->y;
+    polarMotion->s += nutationCorr->s;
+    psEarthPole *polarTide = psEOC_PolarTideCorr(in);
+    polarMotion->x += polarTide->x;
+    polarMotion->y += polarTide->y;
+    psFree(polarTide);
+    x = -6.43607313124045e-7;
+    y = 2.11351436973568e-6;
+    s = -7.39617581324646e-12;
+    //    if ( fabs(polarMotion->x - x) > DBL_EPSILON || fabs(polarMotion->y - y) > DBL_EPSILON
+    //            || fabs(polarMotion->s - s) > DBL_EPSILON) {
+    //        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+    //                "   psEOC_GetPolarMotion returned incorrect values.\n");
+    if (VERBOSE) {
+        printf("\n  PolarMotion + NutationCorr out = x,y,s = %.13g, %.13g, %.13g\n",
+               polarMotion->x, polarMotion->y, polarMotion->s);
+        printf("  Expected output                = x,y,s = %.13g,  %.13g, %.13g\n", x, y, s);
+        printf("                     Difference  = x,y,s = %.13g, %.13g,  %.13g\n",
+               polarMotion->x - x, polarMotion->y - y, polarMotion->s - s);
+    }
+    //    }
+
+    if (!p_psEOCFinalize() ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "EOC failed finalization!\n");
+        return 12;
+    }
+
+    psFree(nutationCorr);
+    psFree(in);
+    psFree(polarMotion);
+    return 0;
+}
+
+psS32 testEOCPolarTide(void)
+{
+    psTime *in = psTimeAlloc(PS_TIME_UTC);
+    in->sec = timesec;
+    in->nsec = 0;
+    in->leapsecond = false;
+    psTime *empty = NULL;
+    psEarthPole *eop = NULL;
+
+    //Return NULL for NULL input time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    eop = psEOC_PolarTideCorr(empty);
+    if (eop != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psEOC_PolarTideCorr failed to return NULL for NULL input time.\n");
+        return 1;
+    }
+
+    eop = psEOC_PolarTideCorr(in);
+    if (eop == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psEOC_PolarTideCorr returned NULL for valid input time.\n");
+        return 2;
+    } else {
+        if (VERBOSE) {
+            printf("\nPolarTideCorr output = x,y,s = %.13g, %.13g, %.13g\n",
+                   eop->x, eop->y, eop->s);
+        }
+    }
+
+    psFree(in);
+    psFree(eop);
+
+    return 0;
+}
+
+psS32 testEOCNutation(void)
+{
+    psTime *in = psTimeAlloc(PS_TIME_UTC);
+    in->sec = timesec;
+    in->nsec = 0;
+    in->leapsecond = false;
+    psTime *empty = NULL;
+    psEarthPole *nute = NULL;
+
+    //Return NULL for NULL input time.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    nute = psEOC_NutationCorr(empty);
+    if (nute != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psEOC_NutationCorr failed to return NULL for NULL input time.\n");
+        return 1;
+    }
+    //Return NULL for UT1 time input
+    /*    psTime *UT1time = psTimeAlloc(PS_TIME_UT1);
+        nute = psEOC_NutationCorr(UT1time);
+        if (nute != NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "psEOC_NutationCorr failed to return NULL for UT1 input time.\n");
+            return 2;
+        }
+        psFree(UT1time);
+    */
+    //Check return values from valid nutation time input
+    nute = psEOC_NutationCorr(in);
+    if ( nute == NULL ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psEOC_NutationCorr returned NULL for valid input.\n");
+        return 3;
+    } else {
+        if (VERBOSE) {
+            printf("Nutation Correction output = x,y,s = %.13g, %.13g, %.13g\n\n",
+                   nute->x, nute->y, nute->s);
+        }
+    }
+    psFree(nute);
+    psFree(in);
+
+    if (!p_psEOCFinalize() ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "EOC failed finalization!\n");
+        return 12;
+    }
+
+    return 0;
+}
+
+psS32 testSphereRot_TEOtoCEO(void)
+{
+    psSphereRot *rot = NULL;
+    psTime *empty = NULL;
+    psTime *time = psTimeAlloc(PS_TIME_UT1);
+    time->sec = timesec-1;
+    time->nsec = 657017200;
+    time->leapsecond = false;
+
+    //return NULL for NULL input time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    rot = psSphereRot_TEOtoCEO(empty, NULL);
+    if (rot != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereRot_TEOtoCEO failed to return NULL for NULL input time.\n");
+        return 1;
+    }
+
+    psEarthPole *polarTideCorr = psEOC_PolarTideCorr(time);
+    psSphereRot *teoceo = psSphereRot_TEOtoCEO(time, polarTideCorr);
+    //Make sure values match for other psTime type
+    empty = psTimeAlloc(PS_TIME_UTC);
+    empty->sec = timesec;
+    empty->nsec = 0;
+    empty->leapsecond = false;
+
+    rot = psSphereRot_TEOtoCEO(empty, polarTideCorr);
+    if (fabs(rot->q0-teoceo->q0) > 0.00001 || fabs(rot->q1-teoceo->q1) > 0.00001 ||
+            fabs(rot->q2-teoceo->q2) > 0.00001 || fabs(rot->q3-teoceo->q3) > 0.00001) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereRot_TEOtoCEO failed to return matching values for different time types.\n");
+        if (VERBOSE) {
+            printf("\n  Output Rotation1 = q0,q1,q2,q3 = %.13g, %.13g, %.13g, %.13g\n",
+                   teoceo->q0, teoceo->q1, teoceo->q2, teoceo->q3 );
+            printf("\n  Output Rotation2 = q0,q1,q2,q3 = %.13g, %.13g, %.13g, %.13g\n",
+                   rot->q0, rot->q1, rot->q2, rot->q3 );
+        }
+        return 2;
+    }
+    if (VERBOSE) {
+        printf("\n  Output Rotation = q0,q1,q2,q3 = %.13g, %.13g, %.13g, %.13g\n",
+               teoceo->q0, teoceo->q1, teoceo->q2, teoceo->q3 );
+    }
+
+    objSetup();
+    psSphereRot *earthRot = psSphereRotConjugate(NULL, teoceo);
+    psSphere *result = psSphereRotApply(NULL, earthRot, obj);
+    //    psSphere *result = psSphereRotApply(NULL, teoceo, obj);
+    psCube *cube = psSphereToCube(result);
+
+    double x, y, z;
+    x = 0.01698625430807123;
+    y = -0.6616523084626379;
+    z = 0.7496183628158023;
+    if ( fabs(x-cube->x) > FLT_EPSILON  || fabs(y-cube->y) > FLT_EPSILON ||
+            fabs(z-cube->z) > FLT_EPSILON) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereRot_TEOtoCEO returned incorrect values.\n");
+        if (VERBOSE) {
+            printf("\nOutput cube = x,y,z = %.13g, %.13g, %.13g\n",
+                   cube->x, cube->y, cube->z);
+            printf("Expected cube = x,y,z = %.13g, %.13g, %.13g\n", x, y, z);
+            printf("A difference of:   %.13g, %.13g, %.13g\n\n",
+                   (x-cube->x), (y-cube->y), (z-cube->z));
+        }
+        return 3;
+    }
+
+    psFree(polarTideCorr);
+    psFree(rot);
+    psFree(empty);
+    psFree(result);
+    psFree(obj);
+    psFree(earthRot);
+    psFree(cube);
+    psFree(time);
+    psFree(teoceo);
+    return 0;
+}
+
+psS32 testSphereRot_CEOtoGCRS(void)
+{
+    psEarthPole *in = psEarthPoleAlloc();
+    psEarthPole *empty = NULL;
+    psSphereRot *rot = NULL;
+    in->x = 2.857175590089105e-4;
+    in->y = 2.3968739377734732e-5;
+    in->s = -1.3970066457904322e-8;
+
+    double q0,q1,q2,q3;
+    q0 = -1.1984522406756289e-5;
+    q1 = 1.4285893358610674e-4;
+    q2 = 1.2191193518914336e-10;
+    q3 = -0.9999999897238481;
+
+    //Return NULL for NULL input earthpole
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    rot = psSphereRot_CEOtoGCRS(empty);
+    if (rot != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereRot_CEOtoGCRS failed to return NULL for NULL input psEarthPole.\n");
+        return 1;
+    }
+
+    rot = psSphereRot_CEOtoGCRS(in);
+    if (rot == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psSphereRot_CEOtoGCRS returned NULL for valid psEarthPole input.\n");
+        return 2;
+    }
+
+    if (VERBOSE) {
+        printf("\n  Output sphere rotation   = %.13g, %.13g, %.13g, %.13g\n",
+               rot->q0, rot->q1, rot->q2, rot->q3);
+        printf("  Expected sphere rotation = %.13g, %.13g, %.13g, %.13g\n", q0,q1,q2,q3);
+        printf("  difference:                 %.13g, %.13g, %.13g \n",
+               (rot->q0-q0), (rot->q1-q1), (rot->q2-q2) );
+    }
+    if (fabs(rot->q0-q0) > FLT_EPSILON || fabs(rot->q1-q1) > FLT_EPSILON ||
+            fabs(rot->q2-q2) > FLT_EPSILON || fabs(rot->q3+q3) > FLT_EPSILON) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereRot_CEOtoGCRS failed to return expected values.\n");
+        return 3;
+    }
+    psCube *tempCube = psCubeAlloc();
+    tempCube->x = -0.35963388069046304;
+    tempCube->y = 0.5555192509816625;
+    tempCube->z = 0.7497078321908413;
+    obj = psCubeToSphere(tempCube);
+    psFree(tempCube);
+    psSphereRot *precessionNutation = psSphereRotConjugate(NULL, rot);
+    psSphere *result = psSphereRotApply(NULL, precessionNutation, obj);
+    psCube *cube = psSphereToCube(result);
+    double x, y, z;
+    x = -0.3598480726985338;
+    y = 0.5555012823608123;
+    z = 0.7496183628158023;
+    if (VERBOSE) {
+        printf("\n  Output cube = x,y,z = %.13g,  %.13g,    %.13g\n", cube->x, cube->y, cube->z);
+        printf("Expected cube = x,y,z = %.13g,  %.13g,   %.13g\n", x, y, z);
+        printf("  A difference of:    %.13g, %.13g, %.13g\n\n",
+               (x-cube->x), (y-cube->y), (z-cube->z));
+    }
+    psCube *expect = psCubeAlloc();
+    expect->x = x;
+    expect->y = y;
+    expect->z = z;
+    psSphere *expected = psCubeToSphere(expect);
+    double d = greatCircle(result, expected);
+    if (VERBOSE) {
+        printf("   The great circle angular distance between expected & result = %.13g\n", d);
+    }
+
+    psFree(expect);
+    psFree(expected);
+    psFree(obj);
+    psFree(precessionNutation);
+    psFree(result);
+    psFree(cube);
+    psFree(rot);
+    psFree(in);
+
+    return 0;
+}
+
+psS32 testSphereRot_ITRStoTEO(void)
+{
+    psEarthPole *in = psEarthPoleAlloc();
+    psEarthPole *empty = NULL;
+    psSphereRot *rot = NULL;
+    in->x = -0.13275353774074533;
+    in->y = 0.4359436319739848;
+    in->s = -4.2376965863576153e-10;
+    in->x = SEC_TO_RAD(in->x);
+    in->y = SEC_TO_RAD(in->y);
+    in->s = SEC_TO_RAD(in->s);
+
+    //Return NULL for NULL input earthpole
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    rot = psSphereRot_ITRStoTEO(empty);
+    if (rot != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereRot_ITRStoTEO failed to return NULL for NULL input psEarthPole.\n");
+        return 1;
+    }
+
+    rot = psSphereRot_ITRStoTEO(in);
+    if (rot == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psSphereRot_ITRStoTEO return NULL for valid psEarthPole input.\n");
+        return 2;
+    }
+
+    double q0,q1,q2,q3;
+    q0 = -1.0567571848664005e-6;
+    q1 = 3.218036562931509e-7;
+    q2 = -3.3580195807204483e-12;
+    q3 = -0.9999999999993899;
+    if (fabs(rot->q0-q0) > FLT_EPSILON || fabs(rot->q1-q1) > FLT_EPSILON ||
+            fabs(rot->q2-q2) > FLT_EPSILON
+            //            || fabs(rot->q3-q3) > DBL_EPSILON
+       ) {
+        if (VERBOSE) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "psSphereRot_ITRStoTEO failed to return expected values.\n");
+            printf("\n  Output sphere rotation   = %.13g, %.13g, %.13g, %.13g\n",
+                   rot->q0, rot->q1, rot->q2, rot->q3);
+            printf("  Expected sphere rotation = %.13g, %.13g, %.13g, %.13g\n", q0,q1,q2,q3);
+            printf("  a difference:   %.13g, %.13g, %.13g, %.13g \n", (rot->q0-q0), (rot->q1-q1),
+                   (rot->q2-q2), (rot->q3-q3) );
+        }
+        //        printf("Error #3\n");
+        //        return 3;
+    }
+    if (VERBOSE) {
+        printf("\n  Output sphere rotation   = %.13g, %.13g, %.13g, %.13g\n",
+               rot->q0, rot->q1, rot->q2, rot->q3);
+        printf("  Expected sphere rotation = %.13g, %.13g, %.13g, %.13g\n", q0,q1,q2,q3);
+    }
+
+    psCube *temp = psCubeAlloc();
+    //    temp->x = -0.3596195125758298;
+    //    temp->y = 0.5555613903455866;
+    //    temp->z = 0.7496834983724809;
+    temp->x = 0.01698625430807123;
+    temp->y = -0.6616523084626379;
+    temp->z = 0.7496183628158023;
+
+    obj = psCubeToSphere(temp);
+    psSphere *test = NULL;
+    psFree(temp);
+    psSphereRot *newRot = psSphereRotConjugate(NULL, rot);
+    test = psSphereRotApply(NULL, newRot, obj);
+    temp = psSphereToCube(test);
+    double x, y, z;
+    x = 0.01698577185310146;
+    y = -0.6616538927902393;
+    z = 0.7496169753347885;
+    if (VERBOSE) {
+        printf("\n  Cube -test- has x,y,z =     %.13g, %.13g, %.13g \n",
+               temp->x, temp->y, temp->z);
+        printf("\n  Cube -expected- has x,y,z = %.13g,  %.13g, %.13g \n", x, y, z );
+    }
+    temp->x = x;
+    temp->y = y;
+    temp->z = z;
+    psSphere *sphere = psCubeToSphere(temp);
+    double d = greatCircle(sphere, test);
+    if (VERBOSE) {
+        printf("Great circle difference of:  %.13g \n", d);
+    }
+
+    psFree(sphere);
+    psFree(newRot);
+    psFree(obj);
+    psFree(temp);
+    psFree(test);
+    psFree(rot);
+    psFree(in);
+
+    return 0;
+}
+
+#define SPHERE_PRECESS_TP1_R            0.0               //    0.0       degrees
+#define SPHERE_PRECESS_TP1_D            0.0               //    0.0       degrees
+#define SPHERE_PRECESS_TP1_EXPECT_R     6.238453          //  357.437     degrees
+#define SPHERE_PRECESS_TP1_EXPECT_D    -0.019426          //   -1.113     degrees
+#define SPHERE_PRECESS_TP2_R            0.0               //    0.0       degrees
+#define SPHERE_PRECESS_TP2_D            1.570796          //   90.0       degrees
+#define SPHERE_PRECESS_TP2_EXPECT_R     6.260828          //  358.719     degrees
+#define SPHERE_PRECESS_TP2_EXPECT_D     1.551353          //   88.886     degrees
+#define SPHERE_PRECESS_TP3_R            3.141593          //  180.0       degrees
+#define SPHERE_PRECESS_TP3_D            0.523599          //   30.0       degrees
+#define SPHERE_PRECESS_TP3_EXPECT_R     3.096616          //  177.423     degrees
+#define SPHERE_PRECESS_TP3_EXPECT_D     0.543024          //   31.113     degrees
+#define ERROR_TOL   0.0001
+#define MJD_1900  15021.0        // Modified Julian Day 1/1/1900 00:00:00
+#define MJD_2100  88069.0        // Modified Julian Day 1/1/2100 00:00:00
+
+psS32 testSphereRotPrecess( void )
+{
+
+    psSphere*     inputCoord  = psSphereAlloc();
+    psSphere*     outputCoord = NULL;
+    //    psTime*       fromTime    = psTimeFromMJD(MJD_1900);
+    //    psTime*       toTime      = psTimeFromMJD(MJD_2100);
+    psTime*       fromTime    = psTimeFromMJD(MJD_2100);
+    psTime*       toTime      = psTimeFromMJD(MJD_1900);
+    psSphereRot *rot = NULL;
+    // Set input coordinate
+    inputCoord->r = SPHERE_PRECESS_TP1_R;
+    inputCoord->d = SPHERE_PRECESS_TP1_D;
+    inputCoord->rErr = 0.0;
+    inputCoord->dErr = 0.0;
+
+    // Calculate precess
+    rot = psSpherePrecess(fromTime, toTime, PS_PRECESS_ROUGH);
+    outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+    if (outputCoord->r < -0.0001) {
+        outputCoord->r += 2.0 * M_PI;
+    }
+    //    outputCoord = psSpherePrecess(inputCoord, fromTime, toTime, PS_PRECESS_ROUGH);
+    // Verify return is not NULL
+    if(outputCoord == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL not expected");
+        return 1;
+    }
+    // Verify return with expected values
+    if( fabs(outputCoord->r - SPHERE_PRECESS_TP1_EXPECT_R) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Precess r = %lg not equal to expected = %lg",
+                outputCoord->r,SPHERE_PRECESS_TP1_EXPECT_R);
+        return 2;
+    }
+    if( fabs(outputCoord->d - SPHERE_PRECESS_TP1_EXPECT_D) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Precess d = %lg not equal to expected = %lg",
+                outputCoord->d,SPHERE_PRECESS_TP1_EXPECT_D);
+        return 3;
+    }
+    psFree(outputCoord);
+    psFree(rot);
+
+    // Set input coordinate
+    inputCoord->r = SPHERE_PRECESS_TP2_R;
+    inputCoord->d = SPHERE_PRECESS_TP2_D;
+    inputCoord->rErr = 0.0;
+    inputCoord->dErr = 0.0;
+
+    // Calculate precess
+    rot = psSpherePrecess(fromTime, toTime, PS_PRECESS_ROUGH);
+    outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+    if (outputCoord->r < -0.0001) {
+        outputCoord->r += 2.0 * M_PI;
+    }
+    //    outputCoord = psSpherePrecess(inputCoord, fromTime, toTime, PS_PRECESS_ROUGH);
+    // Verify return is not NULL
+    if(outputCoord == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL not expected");
+        return 4;
+    }
+    // Verify return with expected values
+    if( fabs(outputCoord->r - SPHERE_PRECESS_TP2_EXPECT_R) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Precess r = %lg not equal to expected = %lg",
+                outputCoord->r,SPHERE_PRECESS_TP2_EXPECT_R);
+        return 5;
+    }
+    if( fabs(outputCoord->d - SPHERE_PRECESS_TP2_EXPECT_D) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Precess d = %lg not equal to expected = %lg",
+                outputCoord->d,SPHERE_PRECESS_TP2_EXPECT_D);
+        return 6;
+    }
+    psFree(outputCoord);
+    psFree(rot);
+
+    // Set input coordinate
+    inputCoord->r = SPHERE_PRECESS_TP3_R;
+    inputCoord->d = SPHERE_PRECESS_TP3_D;
+    inputCoord->rErr = 0.0;
+    inputCoord->dErr = 0.0;
+
+    // Calculate precess
+    rot = psSpherePrecess(fromTime, toTime, PS_PRECESS_ROUGH);
+    outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+    if (outputCoord->r < -0.0001) {
+        outputCoord->r += 2.0 * M_PI;
+    }
+    //    outputCoord = psSpherePrecess(inputCoord, fromTime, toTime, PS_PRECESS_ROUGH);
+    // Verify return is not NULL
+    if(outputCoord == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL not expected");
+        return 7;
+    }
+    // Verify return with expected values
+    if( fabs(outputCoord->r - SPHERE_PRECESS_TP3_EXPECT_R) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Precess r = %lg not equal to expected = %lg",
+                outputCoord->r,SPHERE_PRECESS_TP3_EXPECT_R);
+        return 8;
+    }
+    if( fabs(outputCoord->d - SPHERE_PRECESS_TP3_EXPECT_D) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Precess d = %lg not equal to expected = %lg",
+                outputCoord->d,SPHERE_PRECESS_TP3_EXPECT_D);
+        return 9;
+    }
+    psFree(outputCoord);
+    psFree(rot);
+
+    //Test other modes (IAU2000A, COMPLETE_[A,B])
+    // Set input coordinate
+    inputCoord->r = SPHERE_PRECESS_TP1_R;
+    inputCoord->d = SPHERE_PRECESS_TP1_D;
+    inputCoord->rErr = 0.0;
+    inputCoord->dErr = 0.0;
+    // Calculate precess
+    rot = psSpherePrecess(fromTime, toTime, PS_PRECESS_COMPLETE_A);
+    outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+    if (outputCoord->r < -0.000001) {
+        outputCoord->r += 2.0 * M_PI;
+    }
+    // Verify return is not NULL
+    if(outputCoord == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL not expected");
+        return 10;
+    }
+    psFree(outputCoord);
+    psFree(rot);
+
+    rot = psSpherePrecess(fromTime, toTime, PS_PRECESS_COMPLETE_B);
+    outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+    if (outputCoord->r < -0.000001) {
+        outputCoord->r += 2.0 * M_PI;
+    }
+    // Verify return is not NULL
+    if(outputCoord == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL not expected");
+        return 11;
+    }
+    psFree(outputCoord);
+    psFree(rot);
+
+    rot = psSpherePrecess(fromTime, toTime, PS_PRECESS_IAU2000A);
+    outputCoord = psSphereRotApply(NULL, rot, inputCoord);
+    if (outputCoord->r < -0.000001) {
+        outputCoord->r += 2.0 * M_PI;
+    }
+    // Verify return is not NULL
+    if(outputCoord == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL not expected");
+        return 12;
+    }
+    psFree(outputCoord);
+    psFree(rot);
+
+    // Invoke precess with invalid parameter
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    rot = psSpherePrecess(fromTime, toTime, 10);
+    if(rot != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with invalid input");
+        return 14;
+    }
+    // Return NULL for NULL input times
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    rot = psSpherePrecess(NULL, NULL, PS_PRECESS_ROUGH);
+    if(rot != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with invalid input");
+        return 15;
+    }
+
+    if (!p_psEOCFinalize() ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "EOC failed finalization!\n");
+        return 12;
+    }
+    // Free objects
+    psFree(fromTime);
+    psFree(toTime);
+    psFree(inputCoord);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psSphereOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psSphereOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psSphereOps.c	(revision 22322)
@@ -0,0 +1,390 @@
+/** @file  tst_psSphereOps.c
+*
+*  @brief The code will perform sphere rotations and transformations.
+*
+*  @author d-Rob, MHPCC
+*
+*  @version $Revision: 1.18 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-02-02 23:19:58 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+static psS32 testSphereRotAlloc(void);
+static psS32 testSphereRotQuat(void);
+static psS32 testSphereRotApply1(void);
+static psS32 testSphereRotApplyCelestial(void);
+static psS32 testSphereOffset(void);
+
+testDescription tests[] = {
+                              {testSphereRotAlloc, 819, "psSphereRotAlloc()", 0, false},
+                              {testSphereRotQuat, 820, "psSphereRotQuat()", 0, false},
+                              {testSphereRotApply1, 821, "psSphereRotApply()", 0, false},
+                              {testSphereRotApplyCelestial, 822, "psSphereRotApplyCel()", 0, false},
+                              {testSphereOffset, 825, "testSphereOffset()", 0, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+    psLogSetFormat("HLNM");
+
+    return ( ! runTestSuite( stderr, "psSphereOps", tests, argc, argv ) );
+}
+
+#define DEG_INC   30.0
+
+#define MJD_1900  15021.0        // Modified Julian Day 1/1/1900 00:00:00
+#define MJD_2000  51544.0        // Modified Julian Day 1/1/2000 00:00:00
+#define MJD_2100  88069.0        // Modified Julian Day 1/1/2100 00:00:00
+
+#define ERROR_TOL   0.0001
+
+#define ALPHA_P 4*M_PI/3
+#define DELTA_P M_PI/4
+#define PHI_P M_PI/3
+
+psS32 testSphereRotAlloc( void )
+{
+    // Allocate data structure
+    psSphereRot* myST = psSphereRotAlloc(ALPHA_P, DELTA_P, PHI_P);
+
+    // Verify null not returned
+    if(myST == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL with valid parameters");
+        return 1;
+    }
+
+    double a0 = (ALPHA_P - PHI_P)/2.0;
+    double a1 = (ALPHA_P - PHI_P)/2.0;
+    double a2 = (ALPHA_P + PHI_P)/2.0;
+    double a3 = (ALPHA_P + PHI_P)/2.0;
+    //From Mathworld, this is another way to calculate the quaternions of a rotation
+    double q0 = sin(a0)*sin(DELTA_P/2);
+    double q1 = cos(a1)*sin(DELTA_P/2);
+    double q2 = sin(a2)*cos(DELTA_P/2);
+    double q3 = cos(a3)*cos(DELTA_P/2);
+    //Check that the quaternion components all match
+    if (DBL_EPSILON < fabs(q0 - myST->q0)) {
+        psError(PS_ERR_UNKNOWN,true,"myST->q0 is %lf, should be %lf\n", myST->q0, q0);
+        return 2;
+    }
+    if (DBL_EPSILON < fabs(q1 - myST->q1)) {
+        psError(PS_ERR_UNKNOWN,true,"myST->q1 is %f, should be %f\n", myST->q1, q1);
+        return 3;
+    }
+    if (DBL_EPSILON < fabs(q2 - myST->q2)) {
+        psError(PS_ERR_UNKNOWN,true,"myST->q2 is %f, should be %f\n", myST->q2, q2);
+        return 4;
+    }
+    if (DBL_EPSILON < fabs(q3 - myST->q3)) {
+        psError(PS_ERR_UNKNOWN,true,"myST->q0 is %f, should be %f\n", myST->q3, q3);
+        return 5;
+    }
+
+    // Free data structure
+    psFree(myST);
+
+    return 0;
+}
+
+psS32 testSphereRotQuat(void)
+{
+    double a0 = (ALPHA_P - PHI_P)/2.0;
+    double a1 = (ALPHA_P - PHI_P)/2.0;
+    double a2 = (ALPHA_P + PHI_P)/2.0;
+    double a3 = (ALPHA_P + PHI_P)/2.0;
+    //From Mathworld, this is another way to calculate the quaternions of a rotation
+    double q0 = sin(a0)*sin(DELTA_P/2);
+    double q1 = cos(a1)*sin(DELTA_P/2);
+    double q2 = sin(a2)*cos(DELTA_P/2);
+    double q3 = cos(a3)*cos(DELTA_P/2);
+
+    psSphereRot *myST = psSphereRotQuat(q0*2.0, q1*2.0, q2*2.0, q3*2.0);
+    // Verify null not returned
+    if(myST == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL with valid parameters");
+        return 1;
+    }
+    //Check that the quaternion components all match
+    if (FLT_EPSILON < fabs(q0 - myST->q0)) {
+        psError(PS_ERR_UNKNOWN,true,"myST->q0 is %lf, should be %lf\n", myST->q0, q0);
+        return 2;
+    }
+    if (FLT_EPSILON < fabs(q1 - myST->q1)) {
+        psError(PS_ERR_UNKNOWN,true,"myST->q1 is %f, should be %f\n", myST->q1, q1);
+        return 3;
+    }
+    if (FLT_EPSILON < fabs(q2 - myST->q2)) {
+        psError(PS_ERR_UNKNOWN,true,"myST->q2 is %f, should be %f\n", myST->q2, q2);
+        return 4;
+    }
+    if (FLT_EPSILON < fabs(q3 - myST->q3)) {
+        psError(PS_ERR_UNKNOWN,true,"myST->q0 is %f, should be %f\n", myST->q3, q3);
+        return 5;
+    }
+
+    // Free data structure
+    psFree(myST);
+
+    return 0;
+}
+
+// We do a simple identity transformation on a few RA, DEC pairs.
+psS32 testSphereRotApply1( void )
+{
+    psSphere *in = psSphereAlloc();
+    psSphere *out = psSphereAlloc();
+    psSphere *temp = NULL;
+    psSphere *rc = NULL;
+    psSphere *temp2 = psSphereAlloc();
+    psSphereRot *myST = psSphereRotAlloc(ALPHA_P, DELTA_P, PHI_P);
+    psSphereRot *yourST =  psSphereRotInvert(ALPHA_P, DELTA_P, PHI_P);
+
+    for (float r=0.0;r<180.0;r+=DEG_INC) {
+        for (float d=0.0;d<90.0;d+=DEG_INC) {
+            in->r = DEG_TO_RAD(r);
+            in->d = DEG_TO_RAD(d);
+            in->rErr = 0.0;
+            in->dErr = 0.0;
+            //Here we apply the sphere rotation, then the inverse
+            temp2 = psSphereRotApply(temp2, myST, in);
+            out = psSphereRotApply(out, yourST, temp2);
+            //Check that out matches in
+            if (ERROR_TOL < fabs(out->r - in->r)) {
+                psError(PS_ERR_UNKNOWN,true,"out->r is %f, should be %f\n", out->r, in->r);
+                return 2;
+            }
+            if (ERROR_TOL < fabs(out->d - in->d)) {
+                psError(PS_ERR_UNKNOWN,true,"out->d is %f, should be %f\n", out->d, in->d);
+                return 3;
+            }
+        }
+    }
+    // Verify new sphere object is created if out parameter NULL
+    temp = psSphereRotApply(NULL, myST, in);
+    if ( temp == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL when out parameter was null");
+        return 4;
+    }
+    psFree(temp);
+
+    // Verify NULL returned if transform structure null
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
+    rc = psSphereRotApply(NULL, NULL, in);
+    if (rc != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psSphereRotApply() did not return NULL.");
+        return 5;
+    }
+
+    // Verify NULL returned when input sphere is NULL
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
+    rc = psSphereRotApply(NULL, myST, NULL);
+    if (rc != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psSphereRotApply() did not return NULL");
+        return 6;
+    }
+
+    psFree(myST);
+    psFree(yourST);
+    psFree(temp2);
+    psFree(out);
+    psFree(in);
+
+    return 0;
+}
+
+#define ERROR_PERCENT 0.01
+
+psS32 testSphereRotApplyCelestial( void)
+{
+    //Test cases below were provided in ADD.
+    int numTestPoints = 3;
+    // ICRS coordinates
+    double alpha[] = {  0.0,        0.0,     180.0     };
+    double delta[] = {  0.0,       90.0,      30.0     };
+    //Ecliptic coordinates
+    double lambda[] ={  0.0,       90.0,     167.072470};
+    double beta[] =  {  0.0,       66.560719, 27.308813};
+    // Galactic coordinates
+    double l[] =     { 96.337272, 122.93192, 195.639488};
+    double b[] =     {-60.188553,  27.12825,  78.353806};
+    double t[] =     {  MJD_2000,  MJD_2000,   MJD_2100};
+    double TOLERANCE = 0.001;
+
+    for (int x = 0; x < numTestPoints; x++) {
+        //Setup the appropriate rotations
+        psTime* time = psTimeFromMJD(t[x]);
+        psSphereRot* toEcliptic = psSphereRotICRSToEcliptic(time);
+        psSphereRot* fromEcliptic = psSphereRotEclipticToICRS(time);
+        psSphereRot* toGalactic = psSphereRotICRSToGalactic();
+        psSphereRot* fromGalactic = psSphereRotGalacticToICRS();
+        psFree(time);
+
+        // set the ICRS coordinate
+        psSphere* icrs = psSphereAlloc();
+        icrs->r = DEG_TO_RAD(alpha[x]);
+        icrs->d = DEG_TO_RAD(delta[x]);
+
+        // apply/unapply Ecliptic
+        psSphere* ecliptic = psSphereRotApply(NULL, toEcliptic, icrs);
+        psSphere* icrsFromEcliptic = psSphereRotApply(NULL, fromEcliptic, ecliptic);
+
+        // check ecliptic transforms for correctness
+        if (fabs(RAD_TO_DEG(ecliptic->r) - lambda[x]) > TOLERANCE ||
+                fabs(RAD_TO_DEG(ecliptic->d) - beta[x]) > TOLERANCE) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Ecliptic tranformation incorrect.  Result is (%g,%g), expected (%g,%g)",
+                    RAD_TO_DEG(ecliptic->r),RAD_TO_DEG(ecliptic->d),
+                    lambda[x], beta[x]);
+            return 1;
+        }
+        //The second condition here (d - 90) is used b/c 90 is a pole.
+        if ( (fabs(RAD_TO_DEG(icrsFromEcliptic->r) - alpha[x]) > TOLERANCE &&
+                fabs(RAD_TO_DEG(icrsFromEcliptic->d) - 90.0) > TOLERANCE ) ||
+                fabs(RAD_TO_DEG(icrsFromEcliptic->d) - delta[x]) > TOLERANCE) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "ICRS for Ecliptic tranformation incorrect.  Result is (%g,%g), expected (%g,%g)",
+                    RAD_TO_DEG(icrsFromEcliptic->r),RAD_TO_DEG(icrsFromEcliptic->d),
+                    alpha[x], delta[x]);
+            return 2;
+        }
+        psFree(ecliptic);
+        psFree(icrsFromEcliptic);
+
+        //Setup galactic transformations
+        psSphere* galactic = psSphereRotApply(NULL, toGalactic, icrs);
+        psSphere* icrsFromGalactic = psSphereRotApply(NULL, fromGalactic, galactic);
+
+        // check ecliptic transforms for correctness
+        if (fabs(RAD_TO_DEG(galactic->r) - l[x]) > TOLERANCE ||
+                fabs(RAD_TO_DEG(galactic->d) - b[x]) > TOLERANCE) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Galactic tranformation incorrect.  Result is (%g,%g), expected (%g,%g)",
+                    RAD_TO_DEG(galactic->r),RAD_TO_DEG(galactic->d),
+                    l[x], b[x]);
+            return 3;
+        }
+        //The second condition here (d - 90) is used b/c 90 is a pole.
+        if ( (fabs(RAD_TO_DEG(icrsFromGalactic->r) - alpha[x]) > TOLERANCE &&
+                fabs(RAD_TO_DEG(icrsFromGalactic->d) - 90.0) > TOLERANCE ) ||
+                fabs(RAD_TO_DEG(icrsFromGalactic->d) - delta[x]) > TOLERANCE) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "ICRS for Galactic tranformation incorrect.  Result is (%g,%g), expected (%g,%g)",
+                    RAD_TO_DEG(icrsFromGalactic->r),RAD_TO_DEG(icrsFromGalactic->d),
+                    alpha[x], delta[x]);
+            return 4;
+        }
+        psFree(galactic);
+        psFree(icrsFromGalactic);
+        psFree(icrs);
+        psFree(toEcliptic);
+        psFree(fromEcliptic);
+        psFree(toGalactic);
+        psFree(fromGalactic);
+    }
+    return 0;
+}
+
+psS32 testSphereOffset(void)
+{
+    psSphere *origin = psSphereAlloc();
+    psSphere *offset = psSphereAlloc();
+    psSphere *dest = psSphereAlloc();
+    psSphere *empty = NULL;
+    psSphere *output = NULL;
+
+    //Test Set for NULL position
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
+    output = psSphereSetOffset(empty, offset, PS_SPHERICAL, PS_DEGREE);
+    if (output != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereSetOffset Failed to return NULL for NULL input.\n");
+        return 1;
+    }
+    //Test Set for NULL offset
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
+    output = psSphereSetOffset(offset, empty, PS_SPHERICAL, PS_DEGREE);
+    if (output != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereSetOffset Failed to return NULL for NULL input.\n");
+        return 2;
+    }
+    //Test Get for NULL position1
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
+    output = psSphereGetOffset(empty, origin, PS_LINEAR, PS_RADIAN);
+    if (output != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereGetOffset Failed to return NULL for NULL input.\n");
+        return 3;
+    }
+    //Test Get for NULL position2
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
+    output = psSphereGetOffset(origin, empty, PS_LINEAR, PS_RADIAN);
+    if (output != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereGetOffset Failed to return NULL for NULL input.\n");
+        return 4;
+    }
+
+    //Test Set using Spherical mode, Degree units
+    origin->r = 0.0;
+    origin->d = 0.0;
+    offset->r = 45.0;
+    offset->d = 30.0;
+    output = psSphereSetOffset(origin, offset, PS_SPHERICAL, PS_DEGREE);
+    if ( fabs(output->r - M_PI/4.0) > 0.0001 ||
+            fabs(output->d - M_PI/6.0) > 0.0001 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereSetOffset failed to return correct spherical offset values.\n");
+        return 5;
+    }
+    psFree(output);
+
+    //Test Set and Get using Linear mode, Radian units
+    origin->r = 0.0;
+    origin->d = 0.0;
+    offset->r = 1.0;
+    offset->d = 1.0;
+    output = psSphereSetOffset(origin, offset, PS_LINEAR, PS_RADIAN);
+
+    empty = psSphereGetOffset(origin, output, PS_LINEAR, PS_RADIAN);
+    if ( fabs(offset->r - empty->r) > 0.0001 || fabs(offset->d - empty->d) > 0.0001 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereGetOffset failed to return correct linear offset values.\n");
+        return 7;
+    }
+    psFree(output);
+    psFree(empty);
+
+    //Test Set using Linear mode, Arcmin units
+    origin->r = 0.0;
+    origin->d = 0.0;
+    offset->r = RAD_TO_MIN(M_PI / 4.0);     //45 deg
+    offset->d = RAD_TO_MIN(M_PI / 6.0);     //30 deg
+    output = psSphereSetOffset(origin, offset, PS_SPHERICAL, PS_ARCMIN);
+    //Test Get using Spherical mode, Arcsec units
+    empty = psSphereGetOffset(origin, output, PS_SPHERICAL, PS_ARCSEC);
+    empty->r = SEC_TO_RAD(empty->r);
+    empty->d = SEC_TO_RAD(empty->d);
+    if (fabs(empty->r - (M_PI / 4.0)) > 0.001 || fabs(empty->d - (M_PI / 6.0)) > 0.001) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psSphereGetOffset failed to return correct offset unit values.\n");
+        printf("\n SphereGetOffset should be %lf, %lf and is %lf, %lf\n", output->r, output->d,
+               empty->r, empty->d);
+        return 8;
+    }
+    psFree(output);
+
+    psFree(origin);
+    psFree(dest);
+    psFree(offset);
+    psFree(empty);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_01.c	(revision 22322)
@@ -0,0 +1,1163 @@
+/** @file  tst_psTime_01.c
+ *
+ *  @brief Test driver for psTime functions
+ *
+ *  This test driver contains the following tests for psTime:
+ *     1) Allocate psTime structure
+ *     2) Get current time
+ *     3) Get UT1 UTC delta
+ *     4) Convert psTime to MJD
+ *     5) Convert psTime to JD
+ *     6) Convert psTime to ISO
+ *     7) Convert psTime to timeval
+ *     8) Create psTime from MJD
+ *     9) Create psTime from JD
+ *    10) Create psTime from ISO
+ *    11) Create psTime from timeval
+ *    12) Create psTime from TM
+ *    13) Convert time between different types
+ *
+ *     O) Convert psTime time to LMST
+ *
+ *  @author  Ross Harman, MHPCC
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.7 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-07-21 00:08:15 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+#include <string.h>
+
+#define ERROR_TOL    0.0001
+
+static psS32 testTimeAlloc(void);
+static psS32 testTimeGetNow(void);
+static psS32 testTimeGetUT1Delta(void);
+static psS32 testTimeToMJD(void);
+static psS32 testTimeToJD(void);
+static psS32 testTimeToISO(void);
+static psS32 testTimeToTimeval(void);
+static psS32 testTimeFromMJD(void);
+static psS32 testTimeFromJD(void);
+static psS32 testTimeFromISO(void);
+static psS32 testTimeFromTimeval(void);
+static psS32 testTimeFromTM(void);
+static psS32 testTimeConvert(void);
+
+testDescription tests[] = {
+                              {testTimeAlloc,1,"psTimeAlloc",0,false},
+                              {testTimeGetNow,2,"psTimeGetNow",0,false},
+                              {testTimeGetUT1Delta,3,"psTimeGetUT1Delta",0,false},
+                              {testTimeToMJD,4,"psTimeToMJD",0,false},
+                              {testTimeToJD,5,"psTimeToJD",0,false},
+                              {testTimeToISO,6,"psTimeToISO",0,false},
+                              {testTimeToTimeval,7,"psTimeToTimeval",0,false},
+                              {testTimeFromMJD,8,"psTimeFromMJD",0,false},
+                              {testTimeFromJD,9,"psTimeFromJD",0,false},
+                              {testTimeFromISO,10,"psTimeFromISO",0,false},
+                              {testTimeFromTimeval,11,"psTimeFromTimeval",0,false},
+                              {testTimeFromTM,12,"p_psTimeFromTM",0,false},
+                              {testTimeConvert,666,"psTimeConvert",0,false},
+                              {NULL}
+                          };
+
+// Test Time 1 : July 21, 2004  18:22:24.3
+//               MJD = 53207.765559
+//               JD = 2453208.265559
+// UTC Test Time 1
+const psS64 testTime1SecondsUTC     = 1090434144;
+const psU32 testTime1NanosecondsUTC = 272044000;
+// TAI Test Time 1
+const psS64 testTime1SecondsTAI     = 1090434176;
+const psU32 testTime1NanosecondsTAI = 272044000;
+const psF64 testTime1MJDTAI         = 53207.76592937;
+const psF64 testTime1JDTAI          = 2453208.26592937;
+
+// TT Test Time 1
+const psS64 testTime1SecondsTT      = 1090434208;
+const psU32 testTime1NanosecondsTT  = 456044000;
+// Expected UT1-UTC IERS A & B
+const psF64 testTime1UT1DeltaBullA  = -0.457233186;
+const psF64 testTime1UT1DeltaBullB  = -0.457227;
+// UT1 Test Time 1
+const psS64 testTime1SecondsUT1     = 1090434143;
+//const psU32 testTime1NanosecondsUT1 = 814810814;
+const psU32 testTime1NanosecondsUT1 = 814810861;
+// Expected MJD & JD
+const psF64 testTime1MJD            = 53207.765559;
+const psF64 testTime1JD             = 2453208.265559;
+// Expected ISO string
+const char* testTime1Str     = "2004-07-21T18:22:24.2Z";
+const char* testTime1StrLeap = "2004-07-21T18:22:60.2Z";
+// Expected timeval values
+const psS32 testTime1TimevalSec = 1090434144;
+const psS32 testTime1TimevalUsec = 272044;
+
+// Test Time 2 : Jan. 1, 1973 00:00:00.0000
+//               MJD = 41683.0000
+//               JD = 2441683.5000
+const psS64 testTime2SecondsUTC     = 94694400;
+const psU32 testTime2NanosecondsUTC = 0;
+
+// Expected UT1-UTC IERS A & B
+const psF64 testTime2UT1DeltaBullA  = 0.000000;
+const psF64 testTime2UT1DeltaBullB  = 0.000000;
+
+// Test Time 3 : Sept. 21, 2006 00:00:00.0000
+//               MJD = 53999
+//               JD = 2453999.5
+const psS64 testTime3SecondsUTC     = 1158796800;
+const psU32 testTime3NanosecondsUTC = 0;
+// Expected UT1-UTC IERS A & B
+const psF64 testTime3UT1DeltaBullA  = -0.63574;
+const psF64 testTime3UT1DeltaBullB  = -0.63574;
+
+// Test Time 4 : Jan. 1, 1969 00:00:00.0000
+//               MJD = 40222
+//               JD = 2440222.5
+const psS64 testTime4SecondsUTC     = -31536000;
+const psU32 testTime4NanosecondsUTC = 0;
+// Expected MJD and JD
+const psF64 testTime4MJD            = 40222.0;
+const psF64 testTime4JD             = 2440222.5;
+
+// Test Time 5 : Dec 31, 0001 BC 23:59:59
+//               MJD = -1397755
+//               JD = 1002245.4999
+const psS64 testTime5SecondsUTC     = -62125920001;
+const psU32 testTime5NanosecondsUTC  = 0;
+
+// Test Time 6 : Jan. 1, 10000 AD 00:00:00
+const psS64 testTime6SecondsUTC      = 253202544001;
+const psU32 testTime6NanosecondsUTC  = 0;
+
+// Test Time 7 : Jan. 1, 2004 00:00:00,0
+const psS64 testTime7Seconds         = 1072915200;
+const psU32 testTime7Nanoseconds     = 0;
+const psS32 testTime7TmYear          = 104;
+const psS32 testTime7TmMon           = 0;
+const psS32 testTime7TmDay           = 1;
+const psS32 testTime7TmHour          = 0;
+const psS32 testTime7TmMin           = 0;
+const psS32 testTime7TmSec           = 0;
+
+// Test Time 8 : Dec. 31, 2003 00:00:00,0
+const psS64 testTime8Seconds         = 1072828800;
+const psU32 testTime8Nanoseconds     = 0;
+const psS32 testTime8TmYear          = 103;
+const psS32 testTime8TmMon           = 11;
+const psS32 testTime8TmDay           = 31;
+const psS32 testTime8TmHour          = 0;
+const psS32 testTime8TmMin           = 0;
+const psS32 testTime8TmSec           = 0;
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    // Initialize library internal structures
+    psLibInit("pslib.config");
+
+    if( !runTestSuite(stderr,"psTime",tests,argc,argv)) {
+        return 1;
+    }
+
+    // Cleanup library
+    psLibFinalize();
+
+    return 0;
+}
+
+psS32 testTimeAlloc(void)
+{
+    psTime*  time = NULL;
+
+    // Allocate new psTime with valid time type
+    time = psTimeAlloc(PS_TIME_TAI);
+    if(time == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Return NULL when psTime object ptr expected");
+        return 1;
+    }
+    // Verify members set properly
+    if(time->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"Member type = %d not as expected %d",time->type,PS_TIME_TAI);
+        return 2;
+    }
+    if((time->sec != 0) && (time->nsec != 0) && (time->leapsecond != false)) {
+        psError(PS_ERR_UNKNOWN,true,"Members not set appropriately");
+        return 3;
+    }
+    psFree(time);
+
+    // Allocate new psTime with invalid time type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    time = psTimeAlloc(-100);
+    if(time != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time type");
+        return 4;
+    }
+
+    return 0;
+}
+psS32 testTimeGetNow(void)
+{
+    psTime*  timeNow = NULL;
+
+    // Get current time and verify return is psTime structure
+    timeNow = psTimeGetNow(PS_TIME_TAI);
+    if(timeNow == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return valid psTime struct");
+        return 1;
+    }
+    if(timeNow->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"Time type %d not as expected %d",
+                timeNow->type, PS_TIME_TAI);
+        return 2;
+    }
+    psFree(timeNow);
+
+    // Attempt to get time with invalid type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time type");
+    timeNow = psTimeGetNow(-100);
+    if(timeNow != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time type");
+        return 3;
+    }
+
+    return 0;
+}
+
+psS32 testTimeGetUT1Delta(void)
+{
+    psTime*    time      = NULL;
+    psF64      ut1Delta  = 0.0;
+
+    // Attempt to convert NULL time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
+    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
+    if( !isnan(ut1Delta)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for NULL psTime");
+        return 1;
+    }
+
+    // Attempt to convert invalid time
+    time = psTimeAlloc(PS_TIME_TAI);
+    time->sec = 1;
+    time->nsec = 2e9;
+    time->leapsecond = false;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
+    if( !isnan(ut1Delta)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for invalid time");
+        return 2;
+    }
+
+    // Attempt to convert time with invalid bulletin
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid bulletin");
+    time->nsec = 2;
+    ut1Delta = psTimeGetUT1Delta(time,-100);
+    if( !isnan(ut1Delta)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for invalid bulletin");
+        return 3;
+    }
+
+    // Attempt to get delta with valid time and bulletin A
+    time->sec  = testTime1SecondsUTC;
+    time->nsec = testTime1NanosecondsUTC;
+    time->type = PS_TIME_UTC;
+    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_A);
+    if(fabs(ut1Delta - testTime1UT1DeltaBullA) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 1 bulletin A",
+                ut1Delta,testTime1UT1DeltaBullA);
+        return 4;
+    }
+
+    // Attempt to get delta with valid time and bulletin B
+    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
+    if(fabs(ut1Delta - testTime1UT1DeltaBullB) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 1 bulletin B",
+                ut1Delta,testTime1UT1DeltaBullB);
+        return 5;
+    }
+
+    // Attempt to get delta with valid time and bulletin A
+    time->sec  = testTime2SecondsUTC;
+    time->nsec = testTime2NanosecondsUTC;
+    time->type = PS_TIME_UTC;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning message predating table");
+    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_A);
+    if(fabs(ut1Delta - testTime2UT1DeltaBullA) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 2 bulletin A",
+                ut1Delta,testTime2UT1DeltaBullA);
+        return 6;
+    }
+
+    // Attempt to get delta with valid time and bulletin B
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning message predating table");
+    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
+    if(fabs(ut1Delta - testTime2UT1DeltaBullB) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 2 bulletin B",
+                ut1Delta,testTime2UT1DeltaBullB);
+        return 7;
+    }
+
+    // Attempt to get delta with valid time and bulletin A
+    time->sec  = testTime3SecondsUTC;
+    time->nsec = testTime3NanosecondsUTC;
+    time->type = PS_TIME_UTC;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning message postdating table");
+    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_A);
+    if(fabs(ut1Delta - testTime3UT1DeltaBullA) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 3 bulletin A",
+                ut1Delta,testTime3UT1DeltaBullA);
+        return 8;
+    }
+
+    // Attempt to get delta with valid time and bulletin B
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning message postdating table");
+    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
+    if(fabs(ut1Delta - testTime3UT1DeltaBullB) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 3 bulletin B",
+                ut1Delta,testTime3UT1DeltaBullB);
+        return 9;
+    }
+
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeToMJD(void)
+{
+    psTime*  time = NULL;
+    psF64    mjd  = 0.0;
+
+    // Attempt to convert with time NULL
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
+    mjd = psTimeToMJD(NULL);
+    if(!isnan(mjd)) {
+        psError(PS_ERR_UNKNOWN,true,"Expected NaN for NULL time");
+        return 1;
+    }
+
+    // Attempt to convert invalid time
+    time = psTimeAlloc(PS_TIME_UTC);
+    time->sec = 1;
+    time->nsec = 2e9;
+    time->leapsecond = false;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    mjd = psTimeToMJD(time);
+    if( !isnan(mjd)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for invalid time");
+        return 2;
+    }
+
+    // Check valid time conversion to MJD after 1/1/1970 epoch
+    time->sec = testTime1SecondsUTC;
+    time->nsec = testTime1NanosecondsUTC;
+    mjd = psTimeToMJD(time);
+    //    if(fabs(mjd - testTime1MJD) > ERROR_TOL) {
+    if(fabs(mjd - 53207.765929) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected MJD = %lf not as expected %lf",
+                mjd, testTime1MJD);
+        return 3;
+    }
+
+    // Check valid time conversion to MJD before 1/1/1970 epoch
+    time->sec = testTime4SecondsUTC;
+    time->nsec = testTime4NanosecondsUTC;
+    mjd = psTimeToMJD(time);
+    if(fabs(mjd - testTime4MJD) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected MJD = %lf not as expected %lf",
+                mjd, testTime4MJD);
+        return 4;
+    }
+
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeToJD(void)
+{
+    psTime*  time = NULL;
+    psF64    jd  = 0.0;
+
+    // Attempt to convert with time NULL
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
+    jd = psTimeToJD(NULL);
+    if(!isnan(jd)) {
+        psError(PS_ERR_UNKNOWN,true,"Expected NaN for NULL time");
+        return 1;
+    }
+
+    // Attempt to convert invalid time
+    time = psTimeAlloc(PS_TIME_UTC);
+    time->sec = 1;
+    time->nsec = 2e9;
+    time->leapsecond = false;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    jd = psTimeToJD(time);
+    if( !isnan(jd)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for invalid time");
+        return 2;
+    }
+
+    // Check valid time conversion to MJD after 1/1/1970 epoch
+    time->sec = testTime1SecondsUTC;
+    time->nsec = testTime1NanosecondsUTC;
+    jd = psTimeToJD(time);
+    //    if(fabs(jd - testTime1JD) > ERROR_TOL) {
+    if(fabs(jd - 2453208.265929) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected JD = %lf not as expected %lf",
+                jd, testTime1JD);
+        return 3;
+    }
+
+    // Check valid time conversion to MJD before 1/1/1970 epoch
+    time->sec = testTime4SecondsUTC;
+    time->nsec = testTime4NanosecondsUTC;
+    jd = psTimeToJD(time);
+    if(fabs(jd - testTime4JD) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected JD = %lf not as expected %lf",
+                jd, testTime4JD);
+        return 4;
+    }
+
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeToISO(void)
+{
+    psTime*    time    = NULL;
+    char*      timeStr = NULL;
+
+    // Attempt to convert with NULL time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL time");
+    timeStr = psTimeToISO(time);
+    if(timeStr != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
+        return 1;
+    }
+
+    // Attempt to convert invalid time
+    time = psTimeAlloc(PS_TIME_UTC);
+    time->sec = 1;
+    time->nsec = 2e9;
+    time->leapsecond = false;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    timeStr = psTimeToISO(time);
+    if( timeStr != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time");
+        return 2;
+    }
+
+    // Verify return NULL for time prior to year 0000
+    time->sec = testTime5SecondsUTC;
+    time->nsec = testTime5NanosecondsUTC;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for time prior year 0000");
+    timeStr = psTimeToISO(time);
+    if(timeStr != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for time prior to year 0000");
+        return 3;
+    }
+
+    // Verify return NULL for time after to year 9999
+    time->sec = testTime6SecondsUTC;
+    time->nsec = testTime6NanosecondsUTC;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for time after year 9999");
+    timeStr = psTimeToISO(time);
+    if(timeStr != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for time after to year 9999");
+        return 4;
+    }
+
+    // Verify return string with valid time
+    time->sec = testTime1SecondsUTC;
+    time->nsec = testTime1NanosecondsUTC;
+    timeStr = psTimeToISO(time);
+    if(strcmp(timeStr,testTime1Str) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"String %s not as expected %s",
+                timeStr, testTime1Str);
+        return 5;
+    }
+    psFree(timeStr);
+
+    // Verify return string with valid time and leap second set
+    time->sec = testTime1SecondsUTC;
+    time->nsec = testTime1NanosecondsUTC;
+    time->leapsecond = true;
+    timeStr = psTimeToISO(time);
+    if(strcmp(timeStr,testTime1StrLeap) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"String %s not as expected %s",
+                timeStr, testTime1StrLeap);
+        return 6;
+    }
+    psFree(timeStr);
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeToTimeval(void)
+{
+    psTime*           time         = NULL;
+    struct timeval*   timevalTime  = NULL;
+
+    // Attempt to convert with NULL time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL time");
+    timevalTime = psTimeToTimeval(time);
+    if(timevalTime != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
+        return 1;
+    }
+
+    // Attempt to convert invalid time
+    time = psTimeAlloc(PS_TIME_UTC);
+    time->sec = 1;
+    time->nsec = 2e9;
+    time->leapsecond = false;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    timevalTime = psTimeToTimeval(time);
+    if( timevalTime != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time");
+        return 2;
+    }
+
+    // Attempt to convert invalid time
+    time->sec = -1;
+    time->nsec = 0;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    timevalTime = psTimeToTimeval(time);
+    if( timevalTime != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time");
+        return 2;
+    }
+
+    // Verify convert to timeval with valid time
+    time->sec = testTime1SecondsUTC;
+    time->nsec = testTime1NanosecondsUTC;
+    timevalTime = psTimeToTimeval(time);
+    if(timevalTime->tv_sec != testTime1TimevalSec) {
+        psError(PS_ERR_UNKNOWN,true,"Timeval seconds %ld not as expectd %ld",
+                timevalTime->tv_sec,testTime1TimevalSec);
+        return 4;
+    }
+    if(timevalTime->tv_usec != testTime1TimevalUsec) {
+        psError(PS_ERR_UNKNOWN,true,"Timeval useconds %ld not as expected %ld",
+                timevalTime->tv_usec,testTime1TimevalUsec);
+        return 5;
+    }
+
+    psFree(timevalTime);
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeFromMJD(void)
+{
+    psTime*   time = NULL;
+
+    // Attempt to convert valid time to psTime
+    time = psTimeFromMJD(testTime1MJDTAI);
+    if(time->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
+                time->type,PS_TIME_TAI);
+        return 1;
+    }
+    if(time->sec != testTime1SecondsTAI) {
+        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
+                (long int)time->sec,testTime1SecondsTAI);
+        return 2;
+    }
+    psFree(time);
+
+    // Attempt to convert valid time before 1970 epoch
+    time = psTimeFromMJD(testTime4MJD);
+    if(time->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
+                time->type,PS_TIME_TAI);
+        return 3;
+    }
+    if(time->sec != testTime4SecondsUTC) {
+        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
+                (long int)time->sec,testTime4SecondsUTC);
+        return 4;
+    }
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeFromJD(void)
+{
+    psTime*   time = NULL;
+
+    // Attempt to convert valid time to psTime
+    time = psTimeFromJD(testTime1JDTAI);
+    if(time->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
+                time->type,PS_TIME_TAI);
+        return 1;
+    }
+    if(time->sec != testTime1SecondsTAI) {
+        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
+                (long int)time->sec,(long int)testTime1SecondsTAI);
+        return 2;
+    }
+    psFree(time);
+
+    // Attempt to convert valid time before 1970 epoch
+    time = psTimeFromJD(testTime4JD);
+    if(time->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
+                time->type,PS_TIME_TAI);
+        return 3;
+    }
+    if(time->sec != testTime4SecondsUTC) {
+        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
+                (long int)time->sec,testTime4SecondsUTC);
+        return 4;
+    }
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeFromISO(void)
+{
+    psTime*   time = NULL;
+
+    // Attempt to convert NULL string
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL ISO string");
+    time = psTimeFromISO(NULL, PS_TIME_TAI);
+    if(time != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected for NULL ISO string");
+        return 1;
+    }
+
+    // Convert valid ISO string
+    time = psTimeFromISO(testTime1Str, PS_TIME_TAI);
+    if(time->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
+                time->type, PS_TIME_TAI);
+        return 2;
+    }
+    if(time->sec != testTime1SecondsUTC) {
+        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
+                (long int)time->sec,testTime1SecondsTAI);
+        return 3;
+    }
+    psFree(time);
+
+    // Attempt to convert invalid ISO string
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for invalid ISO string");
+    time = psTimeFromISO("Here I am", PS_TIME_TAI);
+    if(time != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected for invalid ISO string");
+        return 4;
+    }
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeFromTimeval(void)
+{
+    psTime*          time        = NULL;
+    struct timeval*  timevalTime = NULL;
+
+    // Attempt to create psTime from NULL timeval ptr
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL timeval");
+    time = psTimeFromTimeval(NULL);
+    if(time != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for NULL timeval ptr");
+        return 1;
+    }
+
+    // Convert valid timeval structure
+    timevalTime = (struct timeval*)psAlloc(sizeof(struct timeval));
+    timevalTime->tv_sec = testTime1SecondsTAI;
+    timevalTime->tv_usec = testTime1NanosecondsTAI / 1000;
+    time = psTimeFromTimeval(timevalTime);
+    if(time == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return value for valid timeval");
+        return 2;
+    }
+    if(time->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"psTime type %d not as expected %d",
+                time->type, PS_TIME_TAI);
+        return 3;
+    }
+    if(time->sec != testTime1SecondsTAI) {
+        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
+                time->sec, testTime1SecondsTAI);
+        return 4;
+    }
+    if(time->nsec != testTime1NanosecondsTAI) {
+        psError(PS_ERR_UNKNOWN,true,"psTime nanosec %ld not as expected %ld",
+                time->nsec, testTime1NanosecondsTAI);
+        return 5;
+    }
+    psFree(timevalTime);
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeFromTM(void)
+{
+    psTime*     time   = NULL;
+    struct tm*  tmTime = NULL;
+
+    // Attempt to convert from NULL tm structure ptr
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL tm ptr");
+    time = psTimeFromTM(tmTime);
+    if(time != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for NULL tm ptr");
+        return 1;
+    }
+
+    // Verify convert for valid tm structure
+    tmTime = (struct tm*)psAlloc(sizeof(struct tm));
+    tmTime->tm_year = testTime7TmYear;
+    tmTime->tm_mon  = testTime7TmMon;
+    tmTime->tm_mday = testTime7TmDay;
+    tmTime->tm_hour = testTime7TmHour;
+    tmTime->tm_min  = testTime7TmMin;
+    tmTime->tm_sec  = testTime7TmSec;
+    time = psTimeFromTM(tmTime);
+    if(time == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return value for valid tm structure");
+        return 2;
+    }
+    if(time->sec != testTime7Seconds) {
+        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
+                (long int)time->sec, (long int)testTime7Seconds);
+        return 3;
+    }
+    if(time->nsec != testTime7Nanoseconds) {
+        psError(PS_ERR_UNKNOWN,true,"psTime nanoseconds %ld not as expected %ld",
+                time->nsec, testTime7Nanoseconds);
+        return 4;
+    }
+    psFree(tmTime);
+    psFree(time);
+
+    // Verify convert for valid tm structure
+    tmTime = (struct tm*)psAlloc(sizeof(struct tm));
+    tmTime->tm_year = testTime8TmYear;
+    tmTime->tm_mon  = testTime8TmMon;
+    tmTime->tm_mday = testTime8TmDay;
+    tmTime->tm_hour = testTime8TmHour;
+    tmTime->tm_min  = testTime8TmMin;
+    tmTime->tm_sec  = testTime8TmSec;
+    time = psTimeFromTM(tmTime);
+    if(time == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return value for valid tm structure");
+        return 2;
+    }
+    if(time->sec != testTime8Seconds) {
+        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
+                (long int)time->sec, (long int)testTime8Seconds);
+        return 3;
+    }
+    if(time->nsec != testTime8Nanoseconds) {
+        psError(PS_ERR_UNKNOWN,true,"psTime nanoseconds %ld not as expected %ld",
+                time->nsec, testTime8Nanoseconds);
+        return 4;
+    }
+    psFree(tmTime);
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeConvert(void)
+{
+    psTime*  time1 = NULL;
+    psTime*  time2 = NULL;
+
+    // Attempt to convert NULL time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
+    time2 = psTimeConvert(time1,PS_TIME_TAI);
+    // Verify return value is NULL
+    if(time2 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for NULL time specified");
+        return 1;
+    }
+
+    // Attempt to convert to invalid type
+    time1 = psTimeAlloc(PS_TIME_TAI);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid type");
+    time2 = psTimeConvert(time1,-100);
+    // Verify return value is NULL
+    if(time2 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for invalid type to convert to");
+        return 2;
+    }
+    time1->type = PS_TIME_UTC;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid type");
+    time2 = psTimeConvert(time1,-100);
+    // Verify return value is NULL
+    if(time2 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for invalid type to convert to");
+        return 2;
+    }
+    time1->type = PS_TIME_TT;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid type");
+    time2 = psTimeConvert(time1,-100);
+    // Verify return value is NULL
+    if(time2 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for invalid type to convert to");
+        return 2;
+    }
+    time1->type = -100;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid type");
+    time2 = psTimeConvert(time1,PS_TIME_TAI);
+    // Verify return value is NULL
+    if(time2 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for invalid type to convert to");
+        return 2;
+    }
+
+    // Attempt to convert with invalid time nsec > 1e9
+    time1->nsec = 2e9;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    time2 = psTimeConvert(time1,PS_TIME_TAI);
+    if(time2 != time1) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for invalid time specified");
+        return 3;
+    }
+
+    //Attempt to convert a time to the same type
+    time2 = NULL;
+    time1->sec = 1;
+    time1->nsec = 2;
+    time1->type = PS_TIME_TAI;
+    time1->leapsecond = false;
+    time2 = psTimeConvert(time1,PS_TIME_TAI);
+    if(time2 != time1) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion to same type");
+        return 4;
+    }
+    // Verify time not changed
+    if((time2->sec != 1) || (time2->nsec != 2) || (time2->type != PS_TIME_TAI) ||
+            (time2->leapsecond != false) ) {
+        psError(PS_ERR_UNKNOWN,true,"Time member changes when no change expected");
+        return 5;
+    }
+
+    // Attempt to convert a UTC time to a TAI
+    time2 = NULL;
+    time1->sec = testTime1SecondsUTC;
+    time1->nsec = testTime1NanosecondsUTC;
+    time1->type = PS_TIME_UTC;
+    time1->leapsecond = false;
+    time2 = psTimeConvert(time1,PS_TIME_TAI);
+    if(time2 != time1) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from UTC to TAI");
+        return 6;
+    }
+    // Verify time type
+    if(time2->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
+                time2->type, PS_TIME_TAI);
+        return 7;
+    }
+    // Verify time is TAI as expected
+    if(time2->sec != testTime1SecondsTAI) {
+        psError(PS_ERR_UNKNOWN,true,"TAI seconds = %ld not as expected %ld",(long int)time2->sec,
+                (long int)testTime1SecondsTAI);
+        return 7;
+    }
+    if(time2->nsec != testTime1NanosecondsTAI) {
+        psError(PS_ERR_UNKNOWN,true,"TAI nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
+                (long int)testTime1NanosecondsTAI);
+        return 8;
+    }
+
+    // Attempt to convert a UTC time to a TT
+    time2 = NULL;
+    time1->sec = testTime1SecondsUTC;
+    time1->nsec = testTime1NanosecondsUTC;
+    time1->type = PS_TIME_UTC;
+    time1->leapsecond = false;
+    time2 = psTimeConvert(time1,PS_TIME_TT);
+    if(time2 != time1) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from UTC to TT");
+        return 6;
+    }
+    // Verify time type
+    if(time2->type != PS_TIME_TT) {
+        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
+                time2->type, PS_TIME_TT);
+        return 7;
+    }
+    // Verify time is TT as expected
+    if(time2->sec != testTime1SecondsTT) {
+        psError(PS_ERR_UNKNOWN,true,"TT seconds = %ld not as expected %ld",(long int)time2->sec,
+                (long int)testTime1SecondsTT);
+        return 7;
+    }
+    if(time2->nsec != testTime1NanosecondsTT) {
+        psError(PS_ERR_UNKNOWN,true,"TT nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
+                (long int)testTime1NanosecondsTT);
+        return 8;
+    }
+
+    // Attempt to convert a UTC time to UT1
+    time2 = NULL;
+    time1->sec = testTime1SecondsUTC;
+    time1->nsec = testTime1NanosecondsUTC;
+    time1->type = PS_TIME_UTC;
+    time1->leapsecond = false;
+    time2 = psTimeConvert(time1,PS_TIME_UT1);
+    if(time1 != time2) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from UTC to UT1");
+        return 9;
+    }
+    // Verify time type
+    if(time2->type != PS_TIME_UT1) {
+        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
+                time2->type, PS_TIME_UT1);
+        return 7;
+    }
+    // Verify time is UT1 as expected
+    if(time2->sec != testTime1SecondsUT1) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 seconds = %ld not as expected %ld",(long int)time2->sec,
+                (long int)testTime1SecondsUT1);
+        return 9;
+    }
+    if(time2->nsec != testTime1NanosecondsUT1) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 nanoseconds = %d not as expected %d",time2->nsec,
+                testTime1NanosecondsUT1);
+        return 10;
+    }
+
+    // Attempt to convert a TAI time to UTC
+    time2 = NULL;
+    time1->sec = testTime1SecondsTAI;
+    time1->nsec = testTime1NanosecondsTAI;
+    time1->type = PS_TIME_TAI;
+    time1->leapsecond = false;
+    time2 = psTimeConvert(time1,PS_TIME_UTC);
+    if(time2 != time1) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TAI to UTC");
+        return 11;
+    }
+    // Verify time type
+    if(time2->type != PS_TIME_UTC) {
+        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
+                time2->type, PS_TIME_UTC);
+        return 7;
+    }
+    // Verify time is UTC as expected
+    if(time2->sec != testTime1SecondsUTC) {
+        psError(PS_ERR_UNKNOWN,true,"UTC seconds = %ld not as expected %ld",(long int)time2->sec,
+                (long int)testTime1SecondsUTC);
+        return 12;
+    }
+    if(time2->nsec != testTime1NanosecondsUTC) {
+        psError(PS_ERR_UNKNOWN,true,"UTC nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
+                (long int)testTime1NanosecondsUTC);
+        return 13;
+    }
+
+    // Attempt to convert a TAI time to TT
+    time2 = NULL;
+    time1->sec = testTime1SecondsTAI;
+    time1->nsec = testTime1NanosecondsTAI;
+    time1->type = PS_TIME_TAI;
+    time1->leapsecond = false;
+    time2 = psTimeConvert(time1,PS_TIME_TT);
+    if(time2 != time1) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TAI to TT");
+        return 14;
+    }
+    // Verify time type
+    if(time2->type != PS_TIME_TT) {
+        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
+                time2->type, PS_TIME_TT);
+        return 7;
+    }
+    // Verify time is TT as expected
+    if(time2->sec != testTime1SecondsTT) {
+        psError(PS_ERR_UNKNOWN,true,"TT seconds = %ld not as expected %ld",(long int)time2->sec,
+                (long int)testTime1SecondsTT);
+        return 15;
+    }
+    if(time2->nsec != testTime1NanosecondsTT) {
+        psError(PS_ERR_UNKNOWN,true,"TT nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
+                (long int)testTime1NanosecondsTT);
+        return 16;
+    }
+
+    // Attempt to convert a TAI time to UT1
+    time2 = NULL;
+    time1->sec = testTime1SecondsTAI;
+    time1->nsec = testTime1NanosecondsTAI;
+    time1->type = PS_TIME_TAI;
+    time1->leapsecond = false;
+    time2 = psTimeConvert(time1,PS_TIME_UT1);
+    if(time1 != time2) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TAI to UT1");
+        return 9;
+    }
+    // Verify time type
+    if(time2->type != PS_TIME_UT1) {
+        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
+                time2->type, PS_TIME_UT1);
+        return 7;
+    }
+    // Verify time is UT1 as expected
+    if(time2->sec != testTime1SecondsUT1) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 seconds = %ld not as expected %ld",(long int)time2->sec,
+                (long int)testTime1SecondsUT1);
+        return 9;
+    }
+    if(time2->nsec != testTime1NanosecondsUT1) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 nanoseconds = %d not as expected %d",time2->nsec,
+                testTime1NanosecondsUT1);
+        return 10;
+    }
+
+    // Attempt to convert a TT time to UTC
+    time2 = NULL;
+    time1->sec = testTime1SecondsTT;
+    time1->nsec = testTime1NanosecondsTT;
+    time1->type = PS_TIME_TT;
+    time1->leapsecond = false;
+    time2 = psTimeConvert(time1,PS_TIME_UTC);
+    if(time2 != time1) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TT to UTC");
+        return 17;
+    }
+    // Verify time type
+    if(time2->type != PS_TIME_UTC) {
+        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
+                time2->type, PS_TIME_UTC);
+        return 7;
+    }
+    // Verify time is UTC as expected
+    if(time2->sec != testTime1SecondsUTC) {
+        psError(PS_ERR_UNKNOWN,true,"UTC seconds = %ld not as expected %ld",(long int)time2->sec,
+                (long int)testTime1SecondsUTC);
+        return 18;
+    }
+    if(time2->nsec != testTime1NanosecondsUTC) {
+        psError(PS_ERR_UNKNOWN,true,"UTC nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
+                (long int)testTime1NanosecondsUTC);
+        return 19;
+    }
+
+    // Attempt to convert a TT time to TAI
+    time2 = NULL;
+    time1->sec = testTime1SecondsTT;
+    time1->nsec = testTime1NanosecondsTT;
+    time1->type = PS_TIME_TT;
+    time1->leapsecond = false;
+    time2 = psTimeConvert(time1,PS_TIME_TAI);
+    if(time2 != time1) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TT to TAI");
+        return 20;
+    }
+    // Verify time type
+    if(time2->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
+                time2->type, PS_TIME_TAI);
+        return 7;
+    }
+    // Verify time is TAI as expected
+    if(time2->sec != testTime1SecondsTAI) {
+        psError(PS_ERR_UNKNOWN,true,"TAI seconds = %ld not as expected %ld",(long int)time2->sec,
+                (long int)testTime1SecondsTAI);
+        return 21;
+    }
+    if(time2->nsec != testTime1NanosecondsTAI) {
+        psError(PS_ERR_UNKNOWN,true,"TT nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
+                (long int)testTime1NanosecondsTAI);
+        return 22;
+    }
+
+    // Attempt to convert a TT time to UT1
+    time2 = NULL;
+    time1->sec = testTime1SecondsTT;
+    time1->nsec = testTime1NanosecondsTT;
+    time1->type = PS_TIME_TT;
+    time1->leapsecond = false;
+    time2 = psTimeConvert(time1,PS_TIME_UT1);
+    if(time1 != time2) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TT to UT1");
+        return 9;
+    }
+    // Verify time type
+    if(time2->type != PS_TIME_UT1) {
+        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
+                time2->type, PS_TIME_UT1);
+        return 7;
+    }
+    // Verify time is UT1 as expected
+    if(time2->sec != testTime1SecondsUT1) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 seconds = %ld not as expected %ld",(long int)time2->sec,
+                (long int)testTime1SecondsUT1);
+        return 9;
+    }
+    if(time2->nsec != testTime1NanosecondsUT1) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 nanoseconds = %d not as expected %d",time2->nsec,
+                testTime1NanosecondsUT1);
+        return 10;
+    }
+
+    // Attempt to convert from UT1 to TAI, UTC, TT
+    time2 = NULL;
+    time1->sec = 1;
+    time1->nsec = 2;
+    time1->type = PS_TIME_UT1;
+    time1->leapsecond = false;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message converting from UT1");
+    time2 = psTimeConvert(time1,PS_TIME_UTC);
+    if(time2 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from UT1");
+        return 23;
+    }
+
+    psFree(time1);
+
+    return 0;
+}
+
+//psS32 testTimeOther(void)
+//{
+//    psTime *testTime;
+//    char *testString = "2004-07-21T18:22:24.272Z";
+//
+//    psLibInit("pslib.config");
+//
+// Test time was taken at July 21, 2004 at 18:22:24.272044
+//    testTime = (psTime*)psAlloc(sizeof(psTime));
+//    testTime->sec = 1090434144;
+//    testTime->nsec = 272044000;
+//    testTime->type = PS_TIME_TAI;
+
+// Test O - psTime to LMST
+//    printPositiveTestHeader(stdout, "psTime", "Convert psTime time to LST");
+//    char *testString2 = "2004-09-10T1:00:00.00Z";
+//    psTime* testTime2 = NULL;
+//    testTime2 = psTimeFromISO(testString2);
+//    double dblTime = psTimeToLMST(testTime2, 1);
+//    printf("LST (rad): %lf\n", dblTime);
+//    psFree(testTime2);
+//    printFooter(stdout, "psTime", "Convert psTime time to LST", true);
+
+
+//    return 0;
+//}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_02.c	(revision 22322)
@@ -0,0 +1,368 @@
+/** @file  tst_psTime_02.c
+ *
+ *  @brief Test driver for psTime functions
+ *
+ *  This test driver contains the following tests for psTime:
+ *     1) Convert psTime to Local Mean Sidereal Time (LMST)
+ *     2) Calculate leap second delta between times
+ *     3) Creation of psTime of type TT
+ *     4) Creation of psTime of type UTC
+ *
+ *  @author  Ross Harman, MHPCC
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-01-31 23:24:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define ERROR_TOL  0.001
+
+static psS32 testTimeLMST(void);
+static psS32 testTimeLeapSecondDelta(void);
+static psS32 testTimeIsLeapSecond(void);
+static psS32 testTimeFromTT(void);
+static psS32 testTimeFromUTC(void);
+
+// Test Time 1 : May 9, 2005  00:00:00,0
+//               MJD = 53499.000
+//               JD = 2453499.5
+// UTC Test Time 1
+const psS64 testTime1SecondsUTC     = 1115596900;
+const psU32 testTime1NanosecondsUTC = 0;
+// Expected LMST  15:09:18
+const psF64 testTime1LMST0          = 3.967604;
+
+// Test Time 2 : May 9, 1995 00:00:00,0
+//               MJD = 49846.00
+//               JD = 2449846.5
+// UTC Test Time 2
+const psS64 testTime2SecondsUTC     = 799977600;
+const psU32 testTime2NanosecondsUTC = 0;
+// Expected leap second delta
+const psS64 testTimeLeapSecondDelta1 = 3;
+
+// Test Time 3: Jan 1, 1999 00:00:00,0
+//              MJD = 51179
+//              JD = 2451179.5
+const psS64 testTime3SecondsUTC     = 915148800;
+const psU32 testTime3NanosecondsUTC = 0;
+
+testDescription tests[] = {
+                              {testTimeLMST,1,"psTimeToLMST",0,false},
+                              {testTimeLeapSecondDelta,2,"psTimeLeapSecondDelta",0,false},
+                              {testTimeIsLeapSecond,6,"psTimeIsLeapSecond",0,false},
+                              {testTimeFromTT,66,"psTimeFromTT",0,false},
+                              {testTimeFromUTC,666,"psTimeFromUTC",0,false},
+                              {NULL}
+                          };
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    // Initialize library internal structures
+    psLibInit("pslib.config");
+
+    if( !runTestSuite(stderr,"psTime",tests,argc,argv)) {
+        return 1;
+    }
+
+    // Clean up library
+    psLibFinalize();
+
+    return 0;
+}
+
+psS32 testTimeLMST(void)
+{
+    psTime*       time      = NULL;
+    psF64         lmst      = 0.0;
+
+    // Attempt to get LMST with NULL time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
+    lmst = psTimeToLMST(time,0);
+    if(!isnan(lmst)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NaN as expected %lf",lmst);
+        return 1;
+    }
+
+    // Attempt to get LMST with valid test time
+    // Allocate test time
+    time = psTimeAlloc(PS_TIME_UTC);
+    time->sec = testTime1SecondsUTC;
+    time->nsec = testTime1NanosecondsUTC;
+    time->leapsecond = false;
+    lmst = psTimeToLMST(time,0.0);
+    if(fabs(lmst-testTime1LMST0) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"LMST %lf not as expected %lf",lmst,testTime1LMST0);
+        return 2;
+    }
+
+    // Attempt to get LMST with invalid input time UT1
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for incorrect type");
+    time->type = PS_TIME_UT1;
+    lmst = psTimeToLMST(time,0.0);
+    if(!isnan(lmst)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NaN as expected %lf",lmst);
+        return 3;
+    }
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeLeapSecondDelta(void)
+{
+    psTime*       time1   = NULL;
+    psTime*       time2   = NULL;
+    psS64         delta   = 0;
+
+    // Attempt to get delta with NULL time1 argument
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
+    delta = psTimeLeapSecondDelta(time1,time2);
+    if(delta != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Delta of %ld not as expected 0",(long int)delta);
+        return 1;
+    }
+
+    // Set test time 1
+    time1 = psTimeAlloc(PS_TIME_UTC);
+    time1->sec = testTime1SecondsUTC;
+    time1->nsec = testTime1NanosecondsUTC;
+    time1->leapsecond = false;
+
+    // Attempt to get delta with NULL time2 argument
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
+    delta = psTimeLeapSecondDelta(time1,time2);
+    if(delta != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Delta of %ld not as expected 0",(long int)delta);
+        return 2;
+    }
+
+    // Set test time 2 with invalid time
+    time2 = psTimeAlloc(PS_TIME_UTC);
+    time2->sec = 0;
+    time2->nsec = 2e9;
+    time2->leapsecond = false;
+
+    // Attempt to get delta with invalid time2
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    delta = psTimeLeapSecondDelta(time1,time2);
+    if(delta != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Delta of %ld not as expected 0",(long int)delta);
+        return 3;
+    }
+
+    // Set test time 2 valid
+    time2->sec = testTime2SecondsUTC;
+    time2->nsec = testTime2NanosecondsUTC;
+
+    // Set test time 1 invalid
+    time1->sec = 0;
+    time1->nsec = 2e9;
+
+    // Attempt to get delta with invalid time1
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    delta = psTimeLeapSecondDelta(time1,time2);
+    if(delta != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Delta of %ld not as expected 0",(long int)delta);
+        return 4;
+    }
+
+    // Set test time 1 to greater time
+    time1->sec = testTime1SecondsUTC;
+    time1->nsec = testTime1NanosecondsUTC;
+    delta = psTimeLeapSecondDelta(time1,time2);
+    if(delta != testTimeLeapSecondDelta1) {
+        psError(PS_ERR_UNKNOWN,true,"Delta %ld not as expected %ld",
+                (long int)delta,(long int)testTimeLeapSecondDelta1);
+        return 5;
+    }
+
+    // Set test time 1 to lesser time
+    delta = psTimeLeapSecondDelta(time2,time1);
+    if(delta != testTimeLeapSecondDelta1) {
+        psError(PS_ERR_UNKNOWN,true,"Delta %ld not as expected %ld",
+                (long int)delta,(long int)testTimeLeapSecondDelta1);
+        return 6;
+    }
+
+    // Attempt to get delta with times equal
+    delta = psTimeLeapSecondDelta(time1,time1);
+    if(delta != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Delta %ld not as expected 0",(long int)delta);
+        return 7;
+    }
+
+    psFree(time2);
+    psFree(time1);
+
+    return 0;
+}
+
+psS32 testTimeIsLeapSecond(void)
+{
+    psTime*     time        = NULL;
+    psBool      leapsecond  = false;
+
+    // Attempt to determine if leap second with NULL time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
+    leapsecond = psTimeIsLeapSecond(time);
+    if(leapsecond) {
+        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 0",leapsecond);
+        return 1;
+    }
+
+    // Set time
+    time = psTimeAlloc(PS_TIME_TAI);
+    time->sec = testTime1SecondsUTC;
+    time->nsec = testTime1NanosecondsUTC;
+    time->leapsecond = false;
+
+    // Attempt to determine if leap second with non-UTC time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid type");
+    leapsecond = psTimeIsLeapSecond(time);
+    if(leapsecond) {
+        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 0",leapsecond);
+        return 2;
+    }
+
+    // Set time to UTC
+    time->type = PS_TIME_UTC;
+
+    // Attempt to determine if leap second with valid time
+    leapsecond = psTimeIsLeapSecond(time);
+    if(leapsecond) {
+        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not a expected 0",leapsecond);
+        return 3;
+    }
+
+    // Set time to UTC with leap second
+    time->sec = testTime3SecondsUTC;
+    time->nsec = testTime3NanosecondsUTC;
+    leapsecond = psTimeIsLeapSecond(time);
+    if(!leapsecond) {
+        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 1",leapsecond);
+        //        return 4;
+    }
+
+    // Set time to 1 second before a known leap second
+    time->sec--;
+    leapsecond = psTimeIsLeapSecond(time);
+    if(leapsecond) {
+        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 0",leapsecond);
+        return 5;
+    }
+
+    // Set time to 1 second after a known leap second
+    time->sec +=2;
+    leapsecond = psTimeIsLeapSecond(time);
+    if(leapsecond) {
+        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 0",leapsecond);
+        return 6;
+    }
+
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeFromTT(void)
+{
+    psTime*       time = NULL;
+
+    // Attempt to create psTime with invalid time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    time = psTimeFromTT(0,2e9);
+    if(time != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
+        return 1;
+    }
+
+    // Attempt to create psTime with valid time
+    time = psTimeFromTT(testTime1SecondsUTC,testTime1NanosecondsUTC);
+    if(time == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Return NULL when not expected");
+        return 2;
+    }
+    if(time->type != PS_TIME_TT) {
+        psError(PS_ERR_UNKNOWN,true,"Type of time object %d not as expected %d",
+                time->type,PS_TIME_TT);
+        return 3;
+    }
+    if((time->sec != testTime1SecondsUTC) || (time->nsec != testTime1NanosecondsUTC)) {
+        psError(PS_ERR_UNKNOWN,true,"Seconds %ld nanoseconds %d not as expected seconds %ld nanoseconds %d",
+                (long int)time->sec,time->nsec,(long int)testTime1SecondsUTC,testTime1NanosecondsUTC);
+        return 4;
+    }
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeFromUTC(void)
+{
+    psTime*       time = NULL;
+
+    // Attempt to create psTime with invalid time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
+    time = psTimeFromUTC(0,2e9,true);
+    if(time != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
+        return 1;
+    }
+
+    // Attempt to create psTime with valid non-leapsecond time and leapsecond flag true
+    time = psTimeFromUTC(testTime1SecondsUTC,testTime1NanosecondsUTC,true);
+    if(time == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Return NULL when not expected");
+        return 2;
+    }
+    if(time->type != PS_TIME_UTC) {
+        psError(PS_ERR_UNKNOWN,true,"Type of time object %d not as expected %d",
+                time->type,PS_TIME_UTC);
+        return 3;
+    }
+    if((time->sec != testTime1SecondsUTC) || (time->nsec != testTime1NanosecondsUTC)) {
+        psError(PS_ERR_UNKNOWN,true,"Seconds %ld nanoseconds %d not as expected seconds %ld nanoseconds %d",
+                (long int)time->sec,time->nsec,(long int)testTime1SecondsUTC,testTime1NanosecondsUTC);
+        return 4;
+    }
+    if(time->leapsecond) {
+        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 0",time->leapsecond);
+        return 5;
+    }
+    psFree(time);
+
+    // Attempt to create psTime with valid leapsecond time but leapsecond flag false
+    time = psTimeFromUTC(testTime3SecondsUTC,testTime3NanosecondsUTC,false);
+    if(time == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Return NULL when not expected");
+        return 6;
+    }
+    if(time->type != PS_TIME_UTC) {
+        psError(PS_ERR_UNKNOWN,true,"Type of time object %d not as expected %d",
+                time->type,PS_TIME_UTC);
+        return 7;
+    }
+    if((time->sec != testTime3SecondsUTC) || (time->nsec != testTime3NanosecondsUTC)) {
+        psError(PS_ERR_UNKNOWN,true,"Seconds %ld nanoseconds %d not as expected seconds %ld nanoseconds %d",
+                (long int)time->sec,time->nsec,(long int)testTime1SecondsUTC,testTime1NanosecondsUTC);
+        return 8;
+    }
+    if(!time->leapsecond) {
+        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 1",time->leapsecond);
+        return 9;
+    }
+
+
+    psFree(time);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_03.c	(revision 22322)
@@ -0,0 +1,505 @@
+/** @file  tst_psTime_03.c
+ *
+ *  @brief Test driver for psTime functions
+ *
+ *  This test driver contains the following tests for psTime:
+ *   1) psTimeMath invalid times
+ *   2) psTimeMath valid time of different types
+ *   3) psTimeDelta valid times with different types
+ *
+ *  @author  Ross Harman, MHPCC
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.4 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-06-27 20:33:22 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+#include <string.h>
+
+#define ERROR_TOL    0.001
+
+static psS32 testTimeMath(void);
+static psS32 testTimeDelta(void);
+static psS32 testTimeConvert1(void);
+
+// Test Time 1 : May 9, 2005 00:00:00,0
+//               MJD = 53499.00
+//               JD = 2453499.5
+// UTC Test Time 1
+const psS64 testTime1SecondsUTC         = 1115596900;
+const psU32 testTime1NanosecondsUTC     = 0;
+// TAI Test Time 1
+const psS64 testTime1SecondsTAI         = 1115596932;
+const psU32 testTime1NanosecondsTAI     = 0;
+// TT Test Time 1
+const psS64 testTime1SecondsTT          = 1115596964;
+const psU32 testTime1NanosecondsTT      = 184000000;
+// UT1 Test Time 1
+const psS64 testTime1SecondsUT1         = 1115596900;
+const psU32 testTime1NanosecondsUT1     = 184000000;
+// Delta 1
+const psF64 deltaTime1                  = -15.5;
+// Expected UTC Time 1
+const psS64 newTestTime1SecondsUTC      = 1115596884;
+const psU32 newTestTime1NanosecondsUTC  = 500000000;
+// Expected TAI Time 1
+const psS64 newTestTime1SecondsTAI      = 1115596916;
+const psU32 newTestTime1NanosecondsTAI  = 500000000;
+// Delta 2
+const psF64 deltaTime2                  = 123.066;
+// Expected TT Time 1 w/ delta 2
+const psS64 newTestTime1SecondsTT       = 1115597087;
+const psU32 newTestTime1NanosecondsTT   = 250000000;
+// Expected UT1 Time 1 w/ delta 2
+const psS64 newTestTime1SecondsUT1      = 1115597023;
+const psU32 newTestTime1NanosecondsUT1  = 250000000;
+
+// Test Time 2 : Dec. 31 1998 23:59:45,0
+//               MJD = 51178.99983
+//               JD = 2451179.49983
+// UTC Test Time 1
+const psS64 testTime2SecondsUTC         = 915148785;
+const psU32 testTime2NanosecondsUTC     = 0;
+// Delta 3
+const psF64 deltaTime3                    = 30.0;
+// Expected UTC Time
+const psS64 newTestTime2SecondsUTC      = 915148814;
+const psU32 newTestTime2NanosecondsUTC  = 0;
+
+// Appendix B time conversion tests
+//
+#define APPB_TESTS    8
+const psS64 testTimeBSeconds[APPB_TESTS] =
+    {
+        915148829,
+        915148829,
+        915148830,
+        915148830,
+        915148831,
+        915148831,
+        915148832,
+        915148832
+    };
+const psU32 testTimeBNanoseconds[APPB_TESTS] =
+    {
+        0,
+        500000000,
+        0,
+        500000000,
+        0,
+        500000000,
+        0,
+        500000000
+    };
+const psBool testTimeBLeapsecond[APPB_TESTS] =
+    {
+        false,
+        false,
+        false,
+        false,
+        true,
+        true,
+        false,
+        false
+    };
+// Expected results
+const char* testTimeBStrUTC[APPB_TESTS] =
+    {
+        "1998-12-31T23:59:58.0Z",
+        "1998-12-31T23:59:58.5Z",
+        "1998-12-31T23:59:59.0Z",
+        "1998-12-31T23:59:59.5Z",
+        "1998-12-31T23:59:60.0Z",
+        "1998-12-31T23:59:60.5Z",
+        "1999-01-01T00:00:00.0Z",
+        "1999-01-01T00:00:00.5Z"
+    };
+const char* testTimeBStrTAI[APPB_TESTS] =
+    {
+        "1999-01-01T00:00:29.0Z",
+        "1999-01-01T00:00:29.5Z",
+        "1999-01-01T00:00:30.0Z",
+        "1999-01-01T00:00:30.5Z",
+        "1999-01-01T00:00:31.0Z",
+        "1999-01-01T00:00:31.5Z",
+        "1999-01-01T00:00:32.0Z",
+        "1999-01-01T00:00:32.5Z"
+    };
+const char* testTimeBStrTT[APPB_TESTS] =
+    {
+        "1999-01-01T00:01:01.1Z",
+        "1999-01-01T00:01:01.6Z",
+        "1999-01-01T00:01:02.1Z",
+        "1999-01-01T00:01:02.6Z",
+        "1999-01-01T00:01:03.1Z",
+        "1999-01-01T00:01:03.6Z",
+        "1999-01-01T00:01:04.1Z",
+        "1999-01-01T00:01:04.6Z"
+    };
+const char* testTimeBStrUT1[APPB_TESTS] =
+    {
+        "1998-12-31T23:59:58.7Z",
+        "1998-12-31T23:59:59.2Z",
+        "1998-12-31T23:59:59.7Z",
+        "1998-12-31T23:59:60.2Z",
+        "1998-12-31T23:59:60.7Z",
+        "1999-01-01T00:00:00.2Z",
+        "1999-01-01T00:00:00.7Z",
+        "1999-01-01T00:00:01.2Z"
+    };
+
+// Test Time B1 : Dec 31, 1998 23:59:58,0
+//                MJD = 51178.99998
+//                JD = 2451179.49998
+//const psS64 testTimeB1SecondsUTC        = 915148798;
+//const psU32 testTimeB1NanosecondsUTC    = 0;
+// Expected ISO times
+//const char testTimeB1StrUTC[] = "1998-12-31T23:59:58,0Z";
+//const char testTimeB1StrTAI[] = "1999-01-01T00:00:29,0Z";
+//const char testTimeB1StrTT[]  = "1999-01-01T00:01:01,1Z";
+//const char testTimeB1StrUT1[] = "1998-12-31T23:59:57,7Z";
+//
+// Test Time B2 : Dec 31, 1998 23:59:58,5
+//
+//
+//const psS64 testTimeB2SecondsUTC       = 915148798;
+//const psU32 testTimeB2NanosecondsUTC   = 500000000;
+// Expected ISO times
+//const char testTimeB2StrUTC[] = "1998-12-31T23:59:58,5Z";
+//const char testTimeB2StrTAI[] = "1991-01-01T00:00:29,5Z";
+//const char testTimeB2StrTT[]  = "1999-01-01T00:01:01,6Z";
+//const char testTimeB2StrUT1[] = "1998-12-31T23:59:58,2Z";
+
+testDescription tests[] = {
+                              {testTimeMath,1,"psTimeMath",0,false},
+                              {testTimeDelta,2,"psTimeDelta",0,false},
+                              {testTimeConvert1,666,"psTimeConvert",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    // Initialize library internal structures
+    psLibInit("pslib.config");
+
+    if( !runTestSuite(stderr,"psTime",tests,argc,argv)) {
+        return 1;
+    }
+
+    // Clean up library
+    psLibFinalize();
+
+    return 0;
+}
+
+psS32 testTimeMath(void)
+{
+    psTime*     time      = NULL;
+    psTime*     newTime   = NULL;
+
+    // Attempt to perform math operation on NULL time
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL time");
+    if(psTimeMath(time,-1.1) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for NULL time");
+        return 1;
+    }
+
+    // Set up input time with invalid nanoseconds
+    time = psTimeAlloc(PS_TIME_UTC);
+    time->sec = 0;
+    time->nsec = 2e9;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid time");
+    if(psTimeMath(time,-1.1) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time");
+        return 2;
+    }
+
+    // Set up input time with valid time
+    time->sec = testTime1SecondsUTC;
+    time->nsec = testTime1NanosecondsUTC;
+    newTime = psTimeMath(time,deltaTime1);
+    if(newTime == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return");
+        return 3;
+    }
+    if(newTime->type != PS_TIME_UTC) {
+        psError(PS_ERR_UNKNOWN,true,"New time type %d not as expected %d",
+                newTime->type,PS_TIME_UTC);
+        return 4;
+    }
+    if((newTime->sec != newTestTime1SecondsUTC) || (newTime->nsec != newTestTime1NanosecondsUTC)) {
+        psError(PS_ERR_UNKNOWN,true,"UTC sec %ld nsec %d not as expected sec %ld nsec %d",
+                (long int)newTime->sec,newTime->nsec,(long int)newTestTime1SecondsUTC,
+                newTestTime1NanosecondsUTC);
+        return 5;
+    }
+    psFree(newTime);
+
+    // Set up input time with valid TAI time
+    time->sec = testTime1SecondsTAI;
+    time->nsec = testTime1NanosecondsTAI;
+    time->type = PS_TIME_TAI;
+    newTime = psTimeMath(time,deltaTime1);
+    if(newTime == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return");
+        return 3;
+    }
+    if(newTime->type != PS_TIME_TAI) {
+        psError(PS_ERR_UNKNOWN,true,"New time type %d not as expected %d",
+                newTime->type,PS_TIME_TAI);
+        return 4;
+    }
+    if((newTime->sec != newTestTime1SecondsTAI) || (newTime->nsec != newTestTime1NanosecondsTAI)) {
+        psError(PS_ERR_UNKNOWN,true,"TAI sec %ld nsec %d not as expected sec %ld nsec %d",
+                (long int)newTime->sec,newTime->nsec,(long int)newTestTime1SecondsTAI,
+                newTestTime1NanosecondsTAI);
+        return 5;
+    }
+    psFree(newTime);
+
+    // Set up input time with valid TT time
+    time->sec = testTime1SecondsTT;
+    time->nsec = testTime1NanosecondsTT;
+    time->type = PS_TIME_TT;
+    newTime = psTimeMath(time,deltaTime2);
+    if(newTime == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return");
+        return 3;
+    }
+    if(newTime->type != PS_TIME_TT) {
+        psError(PS_ERR_UNKNOWN,true,"New time type %d not as expected %d",
+                newTime->type,PS_TIME_TT);
+        return 4;
+    }
+    if((newTime->sec != newTestTime1SecondsTT) || (newTime->nsec != newTestTime1NanosecondsTT)) {
+        psError(PS_ERR_UNKNOWN,true,"TT sec %ld nsec %d not as expected sec %ld nsec %d",
+                (long int)newTime->sec,newTime->nsec,(long int)newTestTime1SecondsTT,
+                newTestTime1NanosecondsTT);
+        return 5;
+    }
+    psFree(newTime);
+
+    // Set up input time with valid TT time
+    time->sec = testTime1SecondsUT1;
+    time->nsec = testTime1NanosecondsUT1;
+    time->type = PS_TIME_UT1;
+    newTime = psTimeMath(time,deltaTime2);
+    if(newTime == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return");
+        return 3;
+    }
+    if(newTime->type != PS_TIME_UT1) {
+        psError(PS_ERR_UNKNOWN,true,"New time type %d not as expected %d",
+                newTime->type,PS_TIME_UT1);
+        return 4;
+    }
+    if((newTime->sec != newTestTime1SecondsUT1) || (newTime->nsec != newTestTime1NanosecondsUT1)) {
+        psError(PS_ERR_UNKNOWN,true,"UT1 sec %ld nsec %d not as expected sec %ld nsec %d",
+                (long int)newTime->sec,newTime->nsec,(long int)newTestTime1SecondsUT1,
+                newTestTime1NanosecondsUT1);
+        return 5;
+    }
+    psFree(newTime);
+
+    // Set up input time with valid UTC time and delt which crosses leap second
+    time->sec = testTime2SecondsUTC;
+    time->nsec = testTime2NanosecondsUTC;
+    time->type = PS_TIME_UTC;
+    newTime = psTimeMath(time,deltaTime3);
+    if(newTime == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return");
+        return 3;
+    }
+    if(newTime->type != PS_TIME_UTC) {
+        psError(PS_ERR_UNKNOWN,true,"New time type %d not as expected %d",
+                newTime->type,PS_TIME_UTC);
+        return 4;
+    }
+    if((newTime->sec != newTestTime2SecondsUTC) || (newTime->nsec != newTestTime2NanosecondsUTC)) {
+        psError(PS_ERR_UNKNOWN,true,"UTC sec %ld nsec %d not as expected sec %ld nsec %d",
+                (long int)newTime->sec,newTime->nsec,(long int)newTestTime2SecondsUTC,
+                newTestTime2NanosecondsUTC);
+        return 5;
+    }
+    psFree(newTime);
+
+    psFree(time);
+
+    return 0;
+}
+
+psS32 testTimeDelta(void)
+{
+    psTime*   time1   = NULL;
+    psTime*   time2   = NULL;
+    psF64     delta   = 0.0;
+
+    // Attempt to get delta with time1 NULL
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL time");
+    delta = psTimeDelta(time1,time2);
+    if(fabs(delta-0.0) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return 0 for NULL time argument");
+        return 1;
+    }
+
+    // Set time 1
+    time1 = psTimeAlloc(PS_TIME_UTC);
+
+    // Attempt to get delta with time2 NULL
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL time");
+    delta = psTimeDelta(time1,time2);
+    if(fabs(delta-0.0) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return 0 for NULL time argument");
+        return 2;
+    }
+
+    // Set time 2
+    time2 = psTimeAlloc(PS_TIME_UTC);
+
+    // Set time1 invalid
+    time1->sec = 0;
+    time1->nsec = 2e9;
+    // Attempt to get delta with time1 invalid
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid time");
+    delta = psTimeDelta(time1,time2);
+    if(fabs(delta-0.0) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return 0 for invalid time argument");
+        return 3;
+    }
+
+    // Set time 1 valid
+    time1->sec = testTime1SecondsUTC;
+    time1->nsec = testTime1NanosecondsUTC;
+
+    // Set time 2 invalid
+    time2->sec = 0;
+    time2->nsec = 2e9;
+    // Attempt to get delta with time2 invalid
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid time");
+    delta = psTimeDelta(time1,time2);
+    if(fabs(delta-0.0) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return 0 for invalid time argument");
+        return 4;
+    }
+
+    // Set time 2 valid but different type
+    time2->sec = newTestTime1SecondsUTC;
+    time2->nsec = newTestTime1NanosecondsUTC;
+    time2->type = PS_TIME_TAI;
+    // Attempt to get delta with different time types
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for incorrect type");
+    delta = psTimeDelta(time1,time2);
+    if(fabs(delta-0.0) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return 0 for invalid time argument");
+        return 5;
+    }
+
+    // Set time 2 to same as time 1
+    time2->type = PS_TIME_UTC;
+    // Attempt to get delta with valid times of the same type
+    delta = psTimeDelta(time2,time1);
+    if(fabs(delta-deltaTime1) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Delta %lf not as expected %lf",
+                delta,deltaTime1);
+        return 6;
+    }
+
+    // Set time 1 and 2 as different times with same type
+    time1->sec = testTime1SecondsTT;
+    time1->nsec = testTime1NanosecondsTT;
+    time1->type = PS_TIME_TT;
+    time2->sec = newTestTime1SecondsTT;
+    time2->nsec = newTestTime1NanosecondsTT;
+    time2->type = PS_TIME_TT;
+    // Attempt to get delta with valid times of the same type
+    delta = psTimeDelta(time2,time1);
+    if(fabs(delta-deltaTime2) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Delta %lf not as expected %lf",
+                delta,deltaTime2);
+        return 7;
+    }
+
+    // Set time 1 and 2 as different times with same type
+    time1->sec = testTime2SecondsUTC;
+    time1->nsec = testTime2NanosecondsUTC;
+    time1->type = PS_TIME_UTC;
+    time2->sec = newTestTime2SecondsUTC;
+    time2->nsec = newTestTime2NanosecondsUTC;
+    time2->type = PS_TIME_UTC;
+    // Attempt to get delta with valid times of the same type
+    delta = psTimeDelta(time2,time1);
+    if(fabs(delta-deltaTime3) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Delta %lf not as expected %lf",
+                delta,deltaTime3);
+        return 7;
+    }
+
+    psFree(time1);
+    psFree(time2);
+
+    return 0;
+}
+
+psS32 testTimeConvert1(void)
+{
+    psTime*     time    = NULL;
+    char*       timeStr = NULL;
+
+    for(psS32 i = 0; i < APPB_TESTS; i++) {
+
+        // Initialize time for test time B1
+        time = psTimeAlloc(PS_TIME_TAI);
+        time->sec = testTimeBSeconds[i];
+        time->nsec = testTimeBNanoseconds[i];
+
+        // Verify TAI ISO string
+        timeStr = psTimeToISO(time);
+        if(strcmp(timeStr,testTimeBStrTAI[i]) != 0) {
+            psError(PS_ERR_UNKNOWN,true,"TAI ISO string %s not as expected %s",timeStr,testTimeBStrTAI[i]);
+            return i+1;
+        }
+        psFree(timeStr);
+
+        time = psTimeConvert(time,PS_TIME_TT);
+        timeStr = psTimeToISO(time);
+        if(strcmp(timeStr,testTimeBStrTT[i]) != 0) {
+            psError(PS_ERR_UNKNOWN,true,"TT ISO string %s not as expected %s",timeStr,testTimeBStrTT[i]);
+            return i+10;
+        }
+        psFree(timeStr);
+
+        // Verify UTC ISO string
+        time = psTimeConvert(time,PS_TIME_UTC);
+        time->leapsecond = testTimeBLeapsecond[i];
+        timeStr = psTimeToISO(time);
+        if(strcmp(timeStr,testTimeBStrUTC[i]) != 0) {
+            if(strncmp(timeStr,"1999-01-01T00:00:60.0Z", 10) != 0) {
+                psError(PS_ERR_UNKNOWN,true,"UTC ISO string %s not as expected %s",timeStr,testTimeBStrUTC[i]);
+                return i+20;
+            }
+        }
+        psFree(timeStr);
+
+        time = psTimeConvert(time,PS_TIME_UT1);
+        timeStr = psTimeToISO(time);
+        if(strcmp(timeStr,testTimeBStrUT1[i]) != 0) {
+            if(strncmp(timeStr,"1999-01-01T00:00:60.2Z", 10) != 0) {
+                psError(PS_ERR_UNKNOWN,true,"UT1 ISO string %s not as expected %s",timeStr,testTimeBStrUT1[i]);
+                return i+30;
+            }
+        }
+        psFree(timeStr);
+
+        psFree(time);
+    }
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_04.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_04.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/astro/tst_psTime_04.c	(revision 22322)
@@ -0,0 +1,177 @@
+/** @file  tst_psTime_04.c
+ *
+ *  @brief Test driver for psTime functions
+ *
+ *  This test driver contains the following tests for psTime:
+ *      Test A - Initialize time
+ *      Test B - Attempt to open non-existant time config file
+ *      Test C - Attempt to open non-existant time data files
+ *      Test D - Attempt to read incorrect number of files
+ *      Test E - Attempt to read incorrect number of from values
+ *      Test F - Attempt to read data file with typo in number
+ *      Test G - Free data
+ *      Test H - Attempt to use all timer functions
+ *      Test I - Tidal Corrections to UT1-UTC
+ *
+ *  @author  Ross Harman, MHPCC
+ *  @author  Eric Van Alst, MHPCC
+ *  @author  David Robbins, MHPCC
+ *
+ *  @version $Revision: 1.9 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-01-31 23:24:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include "config.h"
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testTimeInit1(void);
+static psS32 testTimeInit2(void);
+static psS32 testTimeInit3(void);
+static psS32 testTimeInit4(void);
+static psS32 testTimeInit5(void);
+static psS32 testTimeInit6(void);
+static psS32 testTimer1(void);
+static psS32 testTideUT1Corr(void);
+
+testDescription tests[] = {
+                              {testTimeInit1,1,"p_psTimeInit",0,false},
+                              {testTimeInit2,2,"p_psTimeInit",0,false},
+                              {testTimeInit3,3,"p_psTimeInit",0,false},
+                              {testTimeInit4,4,"p_psTimeInit",0,false},
+                              {testTimeInit5,5,"p_psTimeInit",0,false},
+                              {testTimeInit6,6,"p_psTimeInit",0,false},
+                              {testTimer1,8,"psTimer",0,false},
+                              {testTideUT1Corr,9,"psTime_TideUT1Corr",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    if(!runTestSuite(stderr,"psTime",tests,argc,argv)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+psS32 testTimeInit1(void)
+{
+    // Test A - Initialize time
+    psLibInit("pslib.config");
+    psLibFinalize();
+
+    return 0;
+}
+
+psS32 testTimeInit2(void)
+{
+    // Test B - Attempt to open non-existant time config file
+    psLibInit("zzz");
+
+    return 0;
+}
+
+psS32 testTimeInit3(void)
+{
+    // Test C - Attempt to open non-existant time data files
+    psLibInit("test.psTime.config1");
+    psLibFinalize();
+
+    return 0;
+}
+
+psS32 testTimeInit4(void)
+{
+    // Test D - Attempt to read incorrect number of files
+    psLibInit("test.psTime.config2");
+    psLibFinalize();
+
+    return 0;
+}
+
+psS32 testTimeInit5(void)
+{
+    // Test E - Attempt to read incorrect number of from values
+    psLibInit("test.psTime.config3");
+    psLibFinalize();
+
+    return 0;
+}
+
+psS32 testTimeInit6(void)
+{
+    // Test F - Attempt to read data file with typo in number
+    psLibInit("test.psTime.config4");
+    psLibFinalize();
+
+    return 0;
+}
+
+psS32 testTimer1(void)
+{
+    psF64 testTime = 0.0;
+    psF64 testTime2 = 0.0;
+    psF64 testTime3 = 0.0;
+
+    if ( !psTimerStart("newTime") ) {
+        printf("TimerStart Failed\n");
+        return 1;
+    }
+    testTime = psTimerMark("newTime");
+    if (testTime == 0.0) {
+        printf("TimerMark Failed\n");
+        return 2;
+    }
+    testTime2 = psTimerClear("newTime");
+    if (testTime2 == 0.0) {
+        printf("TimerClear Failed\n");
+        return 3;
+    }
+    psTimerStart("newTime");
+    testTime3 = psTimerStop();
+    if (testTime3 == 0.0) {
+        printf("TimerStop Failed\n");
+        return 4;
+    }
+    return 0;
+}
+
+psS32 testTideUT1Corr(void)
+{
+    psTime *empty = NULL;
+    psTime *tide = NULL;
+    psTime *noTide = psTimeAlloc(PS_TIME_UTC);
+    noTide->sec = 1049160600;
+    noTide->nsec = 0;
+    noTide->leapsecond = false;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    empty = psTime_TideUT1Corr(tide);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psTime_TideUT1Corr failed to return NULL for NULL input time.\n");
+        return 1;
+    }
+
+    empty = psTime_TideUT1Corr(noTide);
+    if (empty->sec != 1049160599 || empty->nsec != 656981971) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psTime_TideUT1Corr failed to return correct values.\n");
+        printf("\nsec = %ld, nsec = %u\n", (long int)empty->sec, empty->nsec);
+        return 2;
+    }
+
+    if (!p_psTimeFinalize()) {
+        return 3;
+    }
+
+    psFree(empty);
+    psFree(noTide);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/db/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/db/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/db/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+temp
+.deps
+.libs
+Makefile
+Makefile.in
+tst_psDB
+*.bb
+*.bbg
+*.da
+gmon.out
+tap_psDB
Index: /tags/sj_tags/sj_root_20080929/psLib/test/db/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/db/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/db/Makefile.am	(revision 22322)
@@ -0,0 +1,39 @@
+AM_CPPFLAGS = \
+        $(SRCINC) \
+        -I$(top_srcdir)/test/tap/src \
+        -I$(top_srcdir)/test/pstap/src \
+        $(PSLIB_CFLAGS)
+
+AM_LDFLAGS = \
+        $(top_builddir)/src/libpslib.la  \
+        $(top_builddir)/test/tap/src/libtap.la \
+        $(top_builddir)/test/pstap/src/libpstap.la \
+        $(PSLIB_LIBS)
+
+TEST_PROGS = \
+	tap_psDB
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+clean-local:
+	mysql test -e "drop table1" 2> /dev/null || \
+	mysql test -e "drop table2" 2> /dev/null || \
+	mysql test -e "drop table3" 2> /dev/null || \
+	mysql test -e "drop table4" 2> /dev/null || \
+	mysql test -e "drop table5" 2> /dev/null || \
+	mysql test -e "drop table6" 2> /dev/null || \
+	mysql test -e "drop table7" 2> /dev/null || \
+	mysql test -e "drop table8" 2> /dev/null || \
+	mysql test -e "drop table9" 2> /dev/null || \
+	mysql test -e "drop table10" 2> /dev/null || \
+	mysql test -e "drop table11" 2> /dev/null || \
+	true
+
+test: check
Index: /tags/sj_tags/sj_root_20080929/psLib/test/db/tap_psDB.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/db/tap_psDB.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/db/tap_psDB.c	(revision 22322)
@@ -0,0 +1,266 @@
+/** @file  tap_psDB.c
+ *
+ *  @brief Contains the tests for psDB.[ch]
+ *
+ *  @author Aaron Culliney, MHPCC
+ *  @author Joshua Hoblitt, University of Hawaii
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-03-27 22:52:02 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <string.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+#include "pslib.h"
+
+#define HOST    "localhost"
+#define USER    "test"
+#define PASSWD  ""
+#define DBNAME  "test"
+#define PORT    0
+
+int main(int argc, char* argv[])
+{
+#if DEBUG
+    psTraceSetLevel("psLib.db", 10);
+    psTraceSetLevel("err", 10);
+#endif
+
+    plan_tests(34 + 1);
+
+    // see if we can open a db connection at all
+    {
+        psDB *dbh = psDBInit(HOST, USER, PASSWD, DBNAME, PORT);
+        ok(dbh, "open a database handle");
+        if (!dbh) {
+            done();
+        }
+        psFree(dbh);
+    }
+
+    // test new database creation
+    // the next 3 tests will fail as the MySQL test account has insufficent
+    // permissions
+    {
+        psDB *dbh = psDBInit(HOST, USER, PASSWD, DBNAME, PORT);
+
+        skip_start(!psDBCreate(dbh, "foobar"), 4,
+                "you probably don't have proper database permissions");
+        ok(psDBCreate(dbh, "foobar"), "psDBCreate()");
+        ok(psDBChange(dbh, "foobar"), "psDBChange()");
+        ok(psDBDrop(dbh, "foobar"), "psDBDrop()");
+        // change back to the test dbname
+        ok(psDBChange(dbh, DBNAME), "psDBChange()");
+        skip_end();
+
+        psFree(dbh);
+    }
+
+    // psDBCreateTable() & psDBDropTable()
+    {
+        psDB *dbh = psDBInit(HOST, USER, PASSWD, DBNAME, PORT);
+        psMetadata *md = psMetadataAlloc();
+        psMetadataAdd(md, PS_LIST_TAIL, "foo", PS_DATA_S32, "Primary Key", 0);
+        psMetadataAdd(md, PS_LIST_TAIL, "bar", PS_DATA_BOOL, "Key", false);
+        psMetadataAdd(md, PS_LIST_TAIL, "baz", PS_DATA_F64,  NULL, 0.0);
+        psMetadataAdd(md, PS_LIST_TAIL, "boing", PS_DATA_STRING, NULL, "60");
+
+        ok(psDBCreateTable(dbh, "bar", md), "psDBCreateTable() - create table");
+        psFree(md);
+
+        ok(psDBDropTable(dbh, "bar"), "psDBDropTable() - drop table");
+
+        psFree(dbh);
+    }
+
+    {
+        psDB *dbh = psDBInit(HOST, USER, PASSWD, DBNAME, PORT);
+        psMetadata *md = psMetadataAlloc();
+        psMetadataAdd(md, PS_LIST_TAIL, "foo", PS_TYPE_S32, "Primary Key", 0);
+        psDBCreateTable(dbh, "bar", md);
+
+        ok(!psDBCreateTable(dbh, "bar", md), "psDBCreateTable() - table already exists");
+
+        psFree(md);
+
+        psDBDropTable(dbh, "bar");
+
+        ok(!psDBDropTable(dbh, "fubar"), "psDBDropTable() - non-existant table");
+        psFree(dbh);
+    }
+
+    {
+        psDB *dbh = psDBInit(HOST, USER, PASSWD, DBNAME, PORT);
+
+        // setup yak table for later tests
+        {
+            psMetadata *md = psMetadataAlloc();
+            psMetadataAdd(md, PS_LIST_TAIL, "hair", PS_TYPE_F32, NULL, 0.0);
+            psDBCreateTable(dbh, "yak", md);
+            psFree(md);
+        }
+
+        // setup horse table for later tests
+        {
+            psMetadata *md = psMetadataAlloc();
+            psMetadataAdd(md, PS_LIST_TAIL, "color", PS_DATA_STRING, NULL, "100");
+            psDBCreateTable(dbh, "horse", md);
+            psFree(md);
+        }
+
+        // psDBInsertOneRow()
+        {
+            psMetadata *md = psMetadataAlloc();
+            psMetadataAdd(md, PS_LIST_TAIL, "hair", PS_TYPE_F32, NULL, 10e3);
+
+            ok(psDBInsertOneRow(dbh, "yak", md),"psDBInsertOneRow() - number");
+
+            psFree(md);
+        }
+
+        {
+            psMetadata *md = psMetadataAlloc();
+            psMetadataAdd(md, PS_LIST_TAIL, "hair", PS_TYPE_F32, NULL, NAN);
+            ok(psDBInsertOneRow(dbh, "yak", md),"psDBInsertOneRow() - nan");
+
+            psFree(md);
+        }
+
+        // psDBInsertRows()
+        {
+            psArray *rowSet = psArrayAllocEmpty(3);
+            psMetadata *row = psMetadataAlloc();
+            psMetadataAdd(row, PS_LIST_TAIL, "color", PS_DATA_STRING, NULL, "brown");
+            psArrayAdd(rowSet, 0, row);
+            psFree(row);
+
+            row = psMetadataAlloc();
+            psMetadataAdd(row, PS_LIST_TAIL, "color", PS_DATA_STRING, NULL, NULL);
+            psArrayAdd(rowSet, 0, row);
+            psFree(row);
+
+            row = psMetadataAlloc();
+            psMetadataAdd(row, PS_LIST_TAIL, "color", PS_DATA_STRING, NULL, "pink");
+            psArrayAdd(rowSet, 0, row);
+            psFree(row);
+
+            ok(psDBInsertRows(dbh, "horse", rowSet), "psDBInsertRows() - multi-row insert");
+
+            psFree(rowSet);
+        }
+
+        // psDBSelectColumn()
+        {
+            psArray *column = psDBSelectColumn(dbh, "horse", "color", 42);
+            ok(column, "psDBSelectColumn() - select");
+            is_long(psArrayLength(column), 3, "psDBSelectColumn() - number of elements");
+
+            // XXX this test is depending on the order the rows come out it...
+            // this shouldn't be depended upon
+            is_str((char *)column->data[0], "brown", "horse.color == 'brown'");
+            ok((char *)column->data[1] == NULL,    "horse.color == NULL");
+            is_str((char *)column->data[2], "pink",  "horse.color == 'pink'");
+
+            psFree(column);
+        }
+
+        // psDBSelectColumnNum()
+        {
+            psVector *column = psDBSelectColumnNum(dbh, "yak", "hair", PS_TYPE_F32, 42);
+            ok(column, "psDBSelectColumnNum() - select");
+            is_long(psVectorLength(column), 2, "psDBSelectColumnNum() - number of elements");
+
+            is_float(column->data.F32[0], 10e3, "hair == 10e3")
+            is_float(column->data.F32[1], NAN, "hair == NAN")
+
+            psFree(column);
+        }
+
+        // psDBSelectRows()
+        {
+            psMetadata *md = psMetadataAlloc();
+            psMetadataAdd(md, PS_LIST_TAIL, "color", PS_DATA_STRING, NULL, "brown");
+
+            psArray *resultSet = psDBSelectRows(dbh, "horse", md, 0);
+            ok(resultSet, "psDBSelectRows() - select");
+            is_long(psArrayLength(resultSet), 1, "psDBSelectRows() - number of rows");
+
+            psFree(md);
+            for (long i = 0; i < resultSet->n; i++) {
+                md = (psMetadata *)resultSet->data[0];
+
+                psMetadataItem *item = psListGet(md->list, 0);
+                is_str(item->name, "color", "column name");
+                is_str((char *)item->data.V, "brown", "column value");
+            }
+            psFree(resultSet);
+
+        }
+
+        // psDBDumpRows()
+        {
+            psArray *resultSet = psDBDumpRows(dbh, "horse");
+            ok(resultSet, "psDBDumpRows() - dump");
+            ok(psArrayLength(resultSet) == 3, "psDBDumpRows() - number of rows");
+            psFree(resultSet);
+        }
+
+        // psDBDumpCols()
+        {
+            psMetadata *columns = psDBDumpCols(dbh, "horse");
+            ok(columns, "psDBDumpCols() - dump");
+            ok(psListLength(columns->list) == 1, "psDBDumpCols() - number of columns");
+
+            psMetadataItem *item = psListGet(columns->list, 0);
+            is_str(item->name, "color", "column name");
+            is_long(psArrayLength((psArray*)item->data.V), 3, "column array length");
+
+            psFree(columns);
+        }
+
+        // psDBUpdateRows()
+        {
+            psMetadata *where = psMetadataAlloc();
+            psMetadataAdd(where, PS_LIST_TAIL, "color", PS_DATA_STRING, NULL, "pink");
+
+            psMetadata *value = psMetadataAlloc();
+            psMetadataAdd(value, PS_LIST_TAIL, "color", PS_DATA_STRING, NULL, "HOT pink");
+
+            psU64 rowsAffected = psDBUpdateRows(dbh, "horse", where, value);
+            psFree(where);
+
+            ok(rowsAffected, "psDBUpdateRows() - pink -> HOT pink");
+
+            psArray *resultSet = psDBSelectRows(dbh, "horse", value, 0);
+            psFree(value);
+
+            for (long i = 0; i < resultSet->n; i++) {
+                psMetadata *md = (psMetadata *)resultSet->data[0];
+
+                psMetadataItem *item = psListGet(md->list, 0);
+                is_str((char *)item->data.V, "HOT pink", "psDBUpdateRows() - psDBSelectRows() found HOT pink");
+
+            }
+
+            psFree(resultSet);
+        }
+
+        // psDBDeleteRows()
+        ok(psDBDeleteRows(dbh, "yak", NULL, 0), "psDBDeleteRows()");
+
+        // cleanup other tests
+        psDBDropTable(dbh, "horse");
+        psDBDropTable(dbh, "yak");
+
+        psFree(dbh);
+    }
+
+
+    done();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/db/tst_psDB.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/db/tst_psDB.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/db/tst_psDB.c	(revision 22322)
@@ -0,0 +1,2224 @@
+/** @file  tst_psDB.c
+ *
+ * -*- mode: C; c-basic-indent: 4; tab-width: 8; indent-tabs-mode: nil -*-
+ * vim: set cindent ts=8 sw=4 expandtab:
+ *
+ *  @brief Contains the tests for psDB.[ch]
+ *
+ *
+ *  @author Aaron Culliney, MHPCC
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-04-20 01:13:11 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+#define STR_1 "hello world!"
+#define STR_2 "foobar"
+#define S32_1 1974
+#define S32_2 -18
+#define F32_1 3.14159
+#define F32_2 3.33
+#define F64_1 2.7182818
+#define F64_2 1.23456789
+#define B_1 TRUE
+#define B_2 FALSE
+
+#define ERROR_TOL   0.001
+
+#define CONST_ROW_1_STR      "randrow1"
+#define CONST_ROW_1_S32      12345
+#define CONST_ROW_1_F32      9876.5
+#define CONST_ROW_1_F64      456.75
+#define CONST_ROW_1_BOOL     TRUE
+
+#define CONST_ROW_2_STR      "randrow2"
+#define CONST_ROW_2_S32      2345
+#define CONST_ROW_2_F32      876.5
+#define CONST_ROW_2_F64      56.75
+#define CONST_ROW_2_BOOL     FALSE
+
+#define TAB_COL_0_NAME      "key_string"
+#define TAB_COL_1_NAME      "key_s32"
+#define TAB_COL_2_NAME      "key_f32"
+#define TAB_COL_3_NAME      "key_f64"
+#define TAB_COL_4_NAME      "key_bool"
+#define TAB_COL_5_NAME      "key_s32_0"
+
+static psDB *_init_psDB( void );
+static psMetadata *_get_CreateTableMetadata( void );
+static psMetadata *_get_RowMetadataValues(const char *val0, psS32 val1, psF32 val2, psF64 val3, psBool val4);
+static psMetadata *_get_RowMetadata(
+    const char *key0, const char *comm0, const char *val0,
+    const char *key1, const char *comm1, psS32  val1,
+    const char *key2, const char *comm2, psF32  val2,
+    const char *key3, const char *comm3, psF64  val3,
+    const char *key4, const char *comm4, psBool val4,
+    const char *key5, const char *comm5, psS32  val5);
+static psMetadata *_get_row( void );
+static psMetadata *_get_invalid_row( void );
+static psMetadata *_get_where( void );
+static psMetadata *_get_invalid_where( void );
+static psMetadata *_get_where_bad_value( void );
+static psMetadata *_get_update_values(const char *val0, psS32 val1, psF32 val2, psF64 val3, psBool val4);
+static bool _check_row(psMetadata* row);
+static bool _check_row_update(psMetadata* row);
+static bool _check_const_row1(psMetadata* row);
+static bool _check_const_row2(psMetadata* row);
+
+//static psS32 TPDBCreate( void );
+//static psS32 TPDBDrop( void );
+static psS32 TPDBInit( void );
+static psS32 TPDBChange( void );
+static psS32 TPDBCreateTable( void );
+static psS32 TPDBDropTable( void );
+static psS32 TPDBSelectColumn( void );
+static psS32 TPDBSelectColumnNum( void );
+static psS32 TPDBSelectRows( void );
+static psS32 TPDBInsertOneRow( void );
+static psS32 TPDBInsertRows( void );
+static psS32 TPDBDumpRows( void );
+static psS32 TPDBDumpCols( void );
+static psS32 TPDBUpdateRows( void );
+static psS32 TPDBDeleteRows( void );
+
+testDescription tests[] = {
+                              {TPDBInit,           841,  "dbInit",            0, false},
+                              {TPDBChange,         842,  "dbChange",          0, false},
+                              {TPDBCreateTable,    843,  "dbCreateTable",     0, false},
+                              {TPDBDropTable,      844,  "dbDropTable",       0, false},
+                              {TPDBSelectColumn,   845,  "dbSelectColumn",    0, false},
+                              {TPDBSelectColumnNum,846,  "dbSelectColumnNum", 0, false},
+                              {TPDBSelectRows,     847,  "dbSelectRows",      0, false},
+                              {TPDBInsertOneRow,   848,  "dbInsertOneRow",    0, false},
+                              {TPDBInsertRows,     849,  "dbInsertRows",      0, false},
+                              {TPDBDumpRows,       850,  "dbDumpRows",        0, false},
+                              {TPDBDumpCols,       851,  "dbDumpCols",        0, false},
+                              {TPDBUpdateRows,     852,  "dbUpdateRows",      0, false},
+                              {TPDBDeleteRows,     853,  "dbDeleteRows",      0, false},
+                              //{TPDBCreate,        -14, "dbCreate",          0, false},
+                              //{TPDBDrop,          -15, "dbDrop",            0, false},
+                              {NULL}
+                          };
+
+static const char *host    = "localhost";
+static const char *user    = "test";
+static const char *passwd  = "";
+static const char *dbname  = "test";
+
+// internal method to initialize DB
+psDB *_init_psDB( void )
+{
+    psDB *dbh = NULL;
+
+    // Initialize database connection with test database
+    dbh = psDBInit(host, user, passwd, dbname);
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "could not initialize DB connection in %s!", __func__ );
+    }
+
+    return dbh;
+}
+
+psMetadata *_get_CreateTableMetadata( void )
+{
+    return _get_RowMetadata(
+               TAB_COL_0_NAME, "comment-string",     "90000", // value used to determine size of field
+               TAB_COL_1_NAME,    "Primary Key",        0,       // values unused here ...
+               TAB_COL_2_NAME,    "Key",                0.0,
+               TAB_COL_3_NAME,    "",                   0.0,
+               TAB_COL_4_NAME,   "",                   FALSE,
+               TAB_COL_5_NAME,  "Key,AUTO_INCREMENT", 0);
+}
+
+psMetadata *_get_RowMetadataValues(const char *val0, psS32 val1, psF32 val2, psF64 val3, psBool val4)
+{
+    return _get_RowMetadata(
+               TAB_COL_0_NAME, "comment-string", val0,
+               TAB_COL_1_NAME,    "comment-s32",    val1,
+               TAB_COL_2_NAME,    "comment-f32",    val2,
+               TAB_COL_3_NAME,    "comment-f64",    val3,
+               TAB_COL_4_NAME,   "comment-bool",   val4,
+               NULL,         NULL,             0.0);
+}
+
+psMetadata *_get_RowMetadata(
+    const char *key0, const char *comm0, const char *val0,
+    const char *key1, const char *comm1, psS32  val1,
+    const char *key2, const char *comm2, psF32  val2,
+    const char *key3, const char *comm3, psF64  val3,
+    const char *key4, const char *comm4, psBool val4,
+    const char *key5, const char *comm5, psS32  val5)
+{
+    psMetadata *md = NULL;
+    psMetadataItem *str=NULL, *s32_0=NULL, *s32=NULL, *f32=NULL, *f64=NULL, *b;
+
+    md = psMetadataAlloc();
+    if (md == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "could not create psMetadata in %s!", __func__ );
+        return NULL;
+    }
+    if (key0 != NULL) {
+        str = psMetadataItemAllocStr(key0, comm0, val0);
+    }
+    if (key5 != NULL) {
+        s32_0 = psMetadataItemAllocS32(key5, comm5, val5);
+    }
+    s32 = psMetadataItemAllocS32 (key1, comm1, val1);
+    f32 = psMetadataItemAllocF32 (key2, comm2, val2);
+    f64 = psMetadataItemAllocF64 (key3, comm3, val3);
+    b   = psMetadataItemAllocBool(key4, comm4, val4);
+
+    if ( ((key0 != NULL) && (str == NULL)) || (s32 == NULL) || (f32 == NULL) || (f64 == NULL) || (b == NULL) ||
+            ((key5 != NULL) && (s32_0 == NULL)) ) {
+        psError(PS_ERR_UNKNOWN, true, "could not create psMetadataItem(s) in %s!", __func__ );
+        return NULL;
+    }
+    if ( ((key0 != NULL) && !psMetadataAddItem(md, str, 0, 0)) ||
+            !psMetadataAddItem(md, s32, 0, 0) || !psMetadataAddItem(md, f32, 0, 0) ||
+            !psMetadataAddItem(md, f64, 0, 0) || !psMetadataAddItem(md, b,   0, 0) ||
+            ((key5 != NULL) && !psMetadataAddItem(md, s32_0, 0, 0))) {
+        psError(PS_ERR_UNKNOWN, true, "could not add psMetadataItem(s) to psMetadata in %s!", __func__ );
+        return NULL;
+    }
+
+    if (str != NULL) {
+        psFree(str);
+    }
+    psFree(s32);
+    psFree(f32);
+    psFree(f64);
+    psFree(b);
+    if (s32_0 != NULL) {
+        psFree(s32_0);
+    }
+
+    return md;
+}
+
+psMetadata *_get_const_row1( void )
+{
+    char buf[32];
+    snprintf(buf, 32, "%s", CONST_ROW_1_STR );
+    return _get_RowMetadata(
+               TAB_COL_0_NAME, "comment-string", buf,
+               TAB_COL_1_NAME,    "comment-s32",    CONST_ROW_1_S32,
+               TAB_COL_2_NAME,    "comment-f32",    CONST_ROW_1_F32,
+               TAB_COL_3_NAME,    "comment-f64",    CONST_ROW_1_F64,
+               TAB_COL_4_NAME,   "comment-bool",   CONST_ROW_1_BOOL,
+               NULL,         NULL,             0.0);
+}
+
+psMetadata *_get_const_row2( void )
+{
+    char buf[32];
+    snprintf(buf, 32, "%s",CONST_ROW_2_STR);
+    return _get_RowMetadata(
+               TAB_COL_0_NAME, "comment-string", buf,
+               TAB_COL_1_NAME,    "comment-s32",    CONST_ROW_2_S32,
+               TAB_COL_2_NAME,    "comment-f32",    CONST_ROW_2_F32,
+               TAB_COL_3_NAME,    "comment-f64",    CONST_ROW_2_F64,
+               TAB_COL_4_NAME,   "comment-bool",   CONST_ROW_2_BOOL,
+               NULL,         NULL,             0.0);
+}
+
+psMetadata *_get_row( void )
+{
+    return _get_RowMetadata(
+               TAB_COL_0_NAME, "comment-string", STR_1,
+               TAB_COL_1_NAME,    "comment-s32",    S32_1,
+               TAB_COL_2_NAME,    "comment-f32",    F32_1,
+               TAB_COL_3_NAME,    "comment-f64",    F64_1,
+               TAB_COL_4_NAME,   "comment-bool",   B_1,
+               NULL,         NULL,             0.0);
+}
+
+psMetadata *_get_invalid_row( void )
+{
+    return _get_RowMetadata(
+               TAB_COL_0_NAME, "comment-string", STR_1,
+               TAB_COL_1_NAME,    "comment-s32",    S32_1,
+               "key_999",    "comment-f32",    F32_1,
+               TAB_COL_3_NAME,    "comment-f64",    F64_1,
+               TAB_COL_4_NAME,   "comment-bool",   B_1,
+               NULL,         NULL,             0.0);
+}
+
+psMetadata *_get_where( void )
+{
+    psMetadata *md = NULL;
+    psMetadataItem *s32=NULL;
+
+    md = psMetadataAlloc();
+    if (md == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "could not create psMetadata in %s!", __func__ );
+        return NULL;
+    }
+    s32 = psMetadataItemAllocS32(TAB_COL_1_NAME, "", S32_1);
+    if (s32 == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "could not create psMetadataItem in %s!", __func__ );
+        return NULL;
+    }
+    if (!psMetadataAddItem(md, s32, 0, 0)) {
+        psError(PS_ERR_UNKNOWN, true, "could not add psMetadataItem(s) to psMetadata in %s!", __func__ );
+        return NULL;
+    }
+
+    psFree(s32);
+    return md;
+}
+
+psMetadata *_get_invalid_where( void )
+{
+    psMetadata *md = NULL;
+    psMetadataItem *s32=NULL;
+
+    md = psMetadataAlloc();
+    if (md == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "could not create psMetadata in %s!", __func__ );
+        return NULL;
+    }
+    s32 = psMetadataItemAllocS32("col999", "", S32_1);
+    if (s32 == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "could not create psMetadataItem in %s!", __func__ );
+        return NULL;
+    }
+    if (!psMetadataAddItem(md, s32, 0, 0)) {
+        psError(PS_ERR_UNKNOWN, true, "could not add psMetadataItem(s) to psMetadata in %s!", __func__ );
+        return NULL;
+    }
+
+    psFree(s32);
+    return md;
+}
+
+psMetadata *_get_where_bad_value( void )
+{
+    psMetadata *md = NULL;
+    psMetadataItem *s32=NULL;
+
+    md = psMetadataAlloc();
+    if (md == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "could not create psMetadata in %s!", __func__ );
+        return NULL;
+    }
+    s32 = psMetadataItemAllocS32(TAB_COL_1_NAME, "", S32_1 + 1);
+    if (s32 == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "could not create psMetadataItem in %s!", __func__ );
+        return NULL;
+    }
+    if (!psMetadataAddItem(md, s32, 0, 0)) {
+        psError(PS_ERR_UNKNOWN, true, "could not add psMetadataItem(s) to psMetadata in %s!", __func__ );
+        return NULL;
+    }
+
+    psFree(s32);
+    return md;
+}
+
+psMetadata *_get_update_values(const char *val0, psS32 val1, psF32 val2, psF64 val3, psBool val4)
+{
+    return _get_RowMetadataValues(val0, val1, val2, val3, val4);
+}
+
+bool _check_row(psMetadata* row)
+{
+    bool              returnValue = true;
+    psMetadataItem*   item        = NULL;
+
+    item = psMetadataLookup(row,TAB_COL_0_NAME);
+    if((item==NULL) || (strcmp(item->data.V,STR_1)!=0)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_string item %s not as expected %s",
+                item->data.V,STR_1);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_1_NAME);
+    if((item==NULL) || (item->data.S32 != S32_1)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_s32 item %d not as expected %d",
+                item->data.S32, S32_1);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_2_NAME);
+    if((item==NULL) || (fabs(item->data.F32-F32_1)>ERROR_TOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_f32 item %f not as expected %f",
+                item->data.F32, F32_1);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_3_NAME);
+    if((item==NULL) || (fabs(item->data.F64-F64_1)>ERROR_TOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_f64 item %lf not as expected %lf",
+                item->data.F64,F64_1);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_4_NAME);
+    if((item==NULL) || (item->data.B != B_1)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_bool item %d not as expected %d",
+                item->data.B, B_1);
+        return false;
+    }
+
+    return returnValue;
+}
+
+bool _check_row_update(psMetadata* row)
+{
+    bool              returnValue = true;
+    psMetadataItem*   item        = NULL;
+
+    item = psMetadataLookup(row,TAB_COL_0_NAME);
+    if((item==NULL) || (strcmp(item->data.V,STR_2)!=0)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_string item %s not as expected %s",
+                item->data.V,STR_2);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_1_NAME);
+    if((item==NULL) || (item->data.S32 != S32_2)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_s32 item %d not as expected %d",
+                item->data.S32, S32_2);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_2_NAME);
+    if((item==NULL) || (fabs(item->data.F32-F32_2)>ERROR_TOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_f32 item %f not as expected %f",
+                item->data.F32, F32_2);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_3_NAME);
+    if((item==NULL) || (fabs(item->data.F64-F64_2)>ERROR_TOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_f64 item %lf not as expected %lf",
+                item->data.F64,F64_2);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_4_NAME);
+    if((item==NULL) || (item->data.B != B_2)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_bool item %d not as expected %d",
+                item->data.B, B_2);
+        return false;
+    }
+
+    return returnValue;
+}
+
+bool _check_const_row1(psMetadata* row)
+{
+    bool              returnValue = true;
+    psMetadataItem*   item        = NULL;
+
+    item = psMetadataLookup(row,TAB_COL_0_NAME);
+    if((item==NULL) || (strcmp(item->data.V,CONST_ROW_1_STR)!=0)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_string item %s not as expected %s",
+                item->data.V,CONST_ROW_1_STR);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_1_NAME);
+    if((item==NULL) || (item->data.S32 != CONST_ROW_1_S32)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_s32 item %d not as expected %d",
+                item->data.S32, CONST_ROW_1_S32);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_2_NAME);
+    if((item==NULL) || (fabs(item->data.F32-CONST_ROW_1_F32)>ERROR_TOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_f32 item %f not as expected %f",
+                item->data.F32, CONST_ROW_1_F32);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_3_NAME);
+    if((item==NULL) || (fabs(item->data.F64-CONST_ROW_1_F64)>ERROR_TOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_f64 item %lf not as expected %lf",
+                item->data.F64,CONST_ROW_1_F64);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_4_NAME);
+    if((item==NULL) || (item->data.B != CONST_ROW_1_BOOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_bool item %d not as expected %d",
+                item->data.B, CONST_ROW_1_BOOL);
+        return false;
+    }
+
+    return returnValue;
+}
+
+bool _check_const_row2(psMetadata* row)
+{
+    bool              returnValue = true;
+    psMetadataItem*   item        = NULL;
+
+    item = psMetadataLookup(row,TAB_COL_0_NAME);
+    if((item==NULL) || (strcmp(item->data.V,CONST_ROW_2_STR)!=0)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_string item %s not as expected %s",
+                item->data.V,CONST_ROW_2_STR);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_1_NAME);
+    if((item==NULL) || (item->data.S32 != CONST_ROW_2_S32)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_s32 item %d not as expected %d",
+                item->data.S32, CONST_ROW_2_S32);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_2_NAME);
+    if((item==NULL) || (fabs(item->data.F32-CONST_ROW_2_F32)>ERROR_TOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_f32 item %f not as expected %f",
+                item->data.F32, CONST_ROW_2_F32);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_3_NAME);
+    if((item==NULL) || (fabs(item->data.F64-CONST_ROW_2_F64)>ERROR_TOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_f64 item %lf not as expected %lf",
+                item->data.F64,CONST_ROW_2_F64);
+        return false;
+    }
+
+    item = psMetadataLookup(row,TAB_COL_4_NAME);
+    if((item==NULL) || (item->data.B != CONST_ROW_2_BOOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Column key_bool item %d not as expected %d",
+                item->data.B, CONST_ROW_2_BOOL);
+        return false;
+    }
+
+    return returnValue;
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    srand(time(NULL));
+
+    return ( ! runTestSuite( stderr, "psDB", tests, argc, argv ) );
+}
+
+// Testpoint #841, initialize/break-down MySQL connection.
+psS32 TPDBInit( void )
+{
+    psDB *dbh = NULL;
+
+    // Initialize database with valid arguments
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL to be returned for valid arguments");
+        return 1;
+    }
+
+    // Cleanup database/connection with valid psDB object
+    psDBCleanup(dbh);
+
+    // Attempt to initialize database with invalid arguments
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid host");
+    dbh = psDBInit("xxx",NULL,NULL,NULL);
+    if(dbh != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL psDB with invalid arguments");
+        return 2;
+    }
+
+    // Attempt cleanup database/connection with NULL psDB object
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL database objec");
+    psDBCleanup(NULL);
+
+    return 0;
+}
+
+// Testpoint #842, psDBChange shall change databases.
+psS32 TPDBChange( void )
+{
+    psDB *dbh = NULL;
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL for valid arguments");
+        return 1;
+    }
+
+    // Attempt to change database with valid arguments
+    if(!psDBChange(dbh, dbname)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false for valid arguments");
+        return 2;
+    }
+
+    // Attemp to change database to invalid database name
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid database");
+    if(psDBChange(dbh,"abc")) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return true for unknown database name");
+        return 3;
+    }
+
+    // Cleanup database connection
+    psDBCleanup(dbh);
+
+    // Attempt to change database with NULL database object
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for null database object");
+    if(psDBChange(NULL,dbname)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return true for NULL database object");
+        return 4;
+    }
+
+    return 0;
+}
+
+// Testpoint #843, psDBCreateTable shall create tables in the test database ...
+psS32 TPDBCreateTable( void )
+{
+    psDB *dbh = NULL;
+    const char* table = "table1";
+
+    // Create metadata for table definition
+    psMetadata *md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL for valid argument in database connection");
+        return 1;
+    }
+
+    // Attempt to create database table with valid arguments
+    if(!psDBCreateTable(dbh, table, md)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return of false for valid arguments");
+        return 2;
+    } else {
+        // Drop the table from the test database
+        psDBDropTable(dbh, table);
+    }
+
+    // Free metadata definition of table
+    psFree(md);
+
+    // Attempt to create table with NULL table definition
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL parameter");
+    if(psDBCreateTable(dbh,table,NULL)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return of true for NULL metadata object");
+        return 3;
+    }
+
+    // Attempt to create table with NULL connection
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL parameter");
+    if(psDBCreateTable(NULL, table, md)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return of true for NULL database object");
+        return 4;
+    }
+
+    // Close/cleanup database connection
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+// Testpoint #844, psDBDropTable shall remove tables in the test database ...
+psS32 TPDBDropTable( void )
+{
+    psDB *dbh = NULL;
+    const char* table = "table2";
+
+    // Create table definition metadata
+    psMetadata *row=NULL, *md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return to initialize database");
+        return 1;
+    }
+
+    // Create table in database
+    if(psDBCreateTable(dbh, table, md)) {
+
+        // Drop table with valid arguments
+        if(!psDBDropTable(dbh, table)) {
+            psError(PS_ERR_UNKNOWN,true,"Did not expect return false for valid arguments");
+            return 2;
+        }
+
+        // insert should fail since the table has been drop from database
+        psLogMsg( __func__, PS_LOG_INFO, "psDBDropTable: insert should fail here...\n" );
+        row = _get_row();
+        if (psDBInsertOneRow(dbh, table, row)) {
+            psError(PS_ERR_UNKNOWN,true,"Did not expect to successfully insert row into dropped table");
+            return 3;
+        }
+        psFree(row);
+    } else {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false to create table");
+        return 4;
+    }
+
+    // Attempt to drop table from NULL psDB object
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL database object");
+    if(psDBDropTable(NULL,table)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return false as expected for NULL database object");
+        return 5;
+    }
+
+    // Attempt to drop table from valid psDB but invalid table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid table");
+    if(psDBDropTable(dbh,"table99")) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return false as expected for invalid table");
+        return 6;
+    }
+
+    // Attempt to drop table from valid psDB but NULL table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL table");
+    if(psDBDropTable(dbh,NULL)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return false as expected for NULL table");
+        return 7;
+    }
+
+    psFree(md);
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+// Testpoint #845, TPDBSelectColumn shall write/select a column from a test table ...
+psS32 TPDBSelectColumn( void )
+{
+    psDB *dbh = NULL;
+    const char* table = "table3";
+    psArray *ary = NULL;
+    char *ptr=NULL;
+
+    // Create table definition metadata
+    psMetadata *row=NULL, *md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return NULL to initialize database connection");
+        return 1;
+    }
+
+    // Create database table
+    if(!psDBCreateTable(dbh,table,md)) {
+        psError(PS_ERR_UNKNOWN,true,"Unable to create database table for testing");
+        psDBCleanup(dbh);
+        return 2;
+    }
+
+    // Insert known constant row #1
+    row = _get_const_row1();
+    if(!psDBInsertOneRow(dbh, table, row)) {
+        psError(PS_ERR_UNKNOWN,true,"Unable to add rows to test database");
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 3;
+    }
+    psFree(row);
+
+    // Insert known constant row #2
+    row = _get_const_row2();
+    if(!psDBInsertOneRow(dbh, table, row)) {
+        psError(PS_ERR_UNKNOWN,true,"Unable to add rows to test database");
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 4;
+    }
+    psFree(row);
+
+    // Insert known row
+    row = _get_row();
+    if(!psDBInsertOneRow(dbh, table, row)) {
+        psError(PS_ERR_UNKNOWN,true,"Unable to add rows to test database");
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 5;
+    }
+    psFree(row);
+
+    // Select all items in column key_string with valid arguments
+    ary = psDBSelectColumn(dbh, table, TAB_COL_0_NAME, 0);
+    if (ary == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return value of NULL");
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 6;
+    }
+
+    // Verify array contents
+    if(ary->n != 3) {
+        psError(PS_ERR_UNKNOWN,true,"Array number of items %d not equal to expected %d",
+                ary->n, 3);
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 60;
+    }
+    ptr = psArrayGet(ary, 2);
+    if (strcmp(ptr, CONST_ROW_1_STR)) {
+        psError(PS_ERR_UNKNOWN,true,"key_str column entry[%d] = %s not as expected %s",
+                2,CONST_ROW_1_STR,ptr);
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 7;
+    }
+    //    psFree(ptr);
+    ptr = psArrayGet(ary, 1);
+    if (strcmp(ptr, CONST_ROW_2_STR)) {
+        psError(PS_ERR_UNKNOWN,true,"key_str column entry[%d] = %s not as expected %s",
+                1,CONST_ROW_2_STR,ptr);
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 8;
+    }
+    //    psFree(ptr);
+    ptr = psArrayGet(ary, 0);
+    if (strcmp(ptr, STR_1)) {
+        psError(PS_ERR_UNKNOWN,true,"key_str column entry[%d] = %s not as expected %s",
+                0,STR_1,ptr);
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 9;
+    }
+    //    psFree(ptr);
+    psFree(ary);
+
+    // Select items in column key_bool with limit 10 and valid arguments
+    ary = psDBSelectColumn(dbh, table, TAB_COL_4_NAME, 10);
+    if (ary == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return value of NULL");
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 10;
+    }
+
+    // Verify array contents
+    if(ary->n != 3) {
+        psError(PS_ERR_UNKNOWN,true,"Array number of items %d not equal to expected %d",
+                ary->n, 3);
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 100;
+    }
+    ptr = psArrayGet(ary, 2);
+    if(atoi(ptr) != CONST_ROW_1_BOOL) {
+        psError(PS_ERR_UNKNOWN,true,"key_bool column entry[%d] = %d not as expected %d",
+                2,CONST_ROW_1_BOOL,atoi(ptr));
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 11;
+    }
+    //    psFree(ptr);
+    ptr = psArrayGet(ary, 1);
+    if(atoi(ptr) != CONST_ROW_2_BOOL) {
+        psError(PS_ERR_UNKNOWN,true,"key_bool column entry[%d] = %d not as expected %d",
+                1,CONST_ROW_2_BOOL,atoi(ptr));
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 12;
+    }
+    //    psFree(ptr);
+    ptr = psArrayGet(ary, 0);
+    if(atoi(ptr) != B_1) {
+        psError(PS_ERR_UNKNOWN,true,"key_bool column entry[%d] = %d not as expected %d",
+                0,B_1,atoi(ptr));
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 13;
+    }
+    //    psFree(ptr);
+    psFree(ary);
+
+    // Attempt to select columns from NULL database object
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL database");
+    if(psDBSelectColumn(NULL,table,"key_str",10) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL return for selecting from NULL database");
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 14;
+    }
+
+    // Attempt to select column from NULL table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL table");
+    if(psDBSelectColumn(dbh,NULL,"key_str",10) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL return for NULL table");
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 15;
+    }
+
+    // Attempt to select column from invalid table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid table");
+    if(psDBSelectColumn(dbh,"table99","key_str",10) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL return for invalid table");
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 16;
+    }
+
+    // Attempt to select invalid column from valid database object and valid table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid column");
+    if(psDBSelectColumn(dbh,table,"key_null",10) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL return for invalid column");
+        psDBDropTable(dbh, table);
+        psDBCleanup(dbh);
+        return 17;
+    }
+
+    // Clean up table and memory
+    psDBDropTable(dbh, table);
+    psFree(md);
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+// Testpoint #846, TPDBSelectColumnNum shall write/select a column from a test table ...
+psS32 TPDBSelectColumnNum( void )
+{
+    psDB *dbh = NULL;
+    const char* table = "table4";
+    psVector *vec = NULL;
+
+    // Create table definition metadata
+    psMetadata *row=NULL, *md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expected NULL return to initialize database");
+        return 1;
+    }
+
+    // Create database table for test
+    if(!psDBCreateTable(dbh, table, md)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false for create table");
+        return 2;
+    }
+
+    // Initialize database table with known rows
+    row = _get_const_row1();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    row = _get_const_row2();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    row = _get_row();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    // Use valid arguments to select column with numeric values to return vector
+    vec = psDBSelectColumnNum(dbh, table, TAB_COL_1_NAME, PS_TYPE_S32, 0);
+
+    // Verify vector returned
+    if (vec == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return with valid arguments");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 3;
+    }
+    // Verify vector values
+    if(vec->n != 3) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return vector size not equal to 3");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 30;
+    }
+    if(vec->type.type != PS_TYPE_S32) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return vector type not equal to PS_TYPE_S32");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 31;
+    }
+    if ((vec->data.S32)[0] != S32_1) {
+        psError(PS_ERR_UNKNOWN,true,"vector value[%d] = %d not as expected %d",
+                0,vec->data.S32[0], S32_1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 4;
+    }
+    if ((vec->data.S32)[1] != CONST_ROW_2_S32) {
+        psError(PS_ERR_UNKNOWN,true,"vector value[%d] = %d not as expected %d",
+                1,vec->data.S32[1], CONST_ROW_2_S32);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 5;
+    }
+    if ((vec->data.S32)[2] != CONST_ROW_1_S32) {
+        psError(PS_ERR_UNKNOWN,true,"vector value[%d] = %d not as expected %d",
+                2,vec->data.S32[2], CONST_ROW_1_S32);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 6;
+    }
+    psFree(vec);
+
+    // Use valid arguments to select column with numeric values to return vector
+    vec = psDBSelectColumnNum(dbh, table, TAB_COL_4_NAME, PS_TYPE_BOOL, 0);
+
+    // Verify vector returned
+    if (vec == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return with valid arguments");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 7;
+    }
+    // Verify vector values
+    if(vec->n != 3) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return vector size not equal to 3");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 8;
+    }
+    if(vec->type.type != PS_TYPE_BOOL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return vector type not equal to PS_TYPE_BOOL");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 9;
+    }
+    if ((vec->data.U8)[0] != B_1) {
+        psError(PS_ERR_UNKNOWN,true,"vector value[%d] = %d not as expected %d",
+                0,vec->data.U8[0],B_1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 10;
+    }
+    if ((vec->data.U8)[1] != CONST_ROW_2_BOOL) {
+        psError(PS_ERR_UNKNOWN,true,"vector value[%d] = %d not as expected %d",
+                0,vec->data.U8[0],CONST_ROW_2_BOOL);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 11;
+    }
+    if ((vec->data.U8)[2] != CONST_ROW_1_BOOL) {
+        psError(PS_ERR_UNKNOWN,true,"vector value[%d] = %d not as expected %d",
+                0,vec->data.U8[0],CONST_ROW_1_BOOL);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 12;
+    }
+    psFree(vec);
+
+    // Attempt to select columns from NULL database
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for NULL database object");
+    if(psDBSelectColumnNum(NULL,table,TAB_COL_4_NAME,PS_TYPE_BOOL,10) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL return with NULL database object");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 13;
+    }
+
+    // Attempt to select columns from NULL table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for NULL table");
+    if(psDBSelectColumnNum(dbh,NULL,TAB_COL_4_NAME,PS_TYPE_BOOL,10) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL return with NULL table");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 14;
+    }
+
+    // Attempt to select columns from invalid column
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for invalid column");
+    if(psDBSelectColumnNum(dbh,table,"key_999",PS_TYPE_BOOL,10) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL return with NULL column");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 15;
+    }
+
+    // Attempt to select columns from invalid column
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for invalid type");
+    if(psDBSelectColumnNum(dbh,table,TAB_COL_0_NAME,0,10) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL return with invalid type");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 16;
+    }
+
+    psDBDropTable(dbh, table);
+    psFree(md);
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+// Testpoint #847, TPDBSelectRows shall write/select data from a test table ...
+psS32 TPDBSelectRows( void )
+{
+    int i;
+    psDB *dbh = NULL;
+    const char* table = "table5";
+    psArray *ary=NULL, *ary1=NULL, *ary2=NULL;
+    psMetadataItem *item = NULL;
+
+    // Create database table definition
+    psMetadata *where=NULL, *row=NULL, *md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expected NULL return to initialize database");
+        return 1;
+    }
+
+    psDBDropTable(dbh,table);
+    // Create database table for test
+    if(!psDBCreateTable(dbh, table, md)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false for create table");
+        psDBCleanup(dbh);
+        return 2;
+    }
+
+    // Initialize database table with known rows
+    row = _get_const_row1();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    row = _get_const_row2();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    row = _get_row();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    // Generate valid where clause
+    where = _get_where();
+
+    // Select rows with non limit
+    ary1 = psDBSelectRows(dbh, table, where, 0);
+
+    // Select rows with a limit
+    ary2 = psDBSelectRows(dbh, table, where, 1);
+    psFree(where);
+
+    // Cycle through both arrays to verify results
+    for (i=0; i<2; i++) {
+        ary = i ? ary2 : ary1;
+
+        // Verify return array not NULL
+        if (ary == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Did not expect return NULL");
+            psDBDropTable(dbh,table);
+            psDBCleanup(dbh);
+            return 1;
+        }
+
+        if(ary->n != 1) {
+            psError(PS_ERR_UNKNOWN,true,"Number of rows %d not as expected %d",
+                    ary->n, 1);
+            psDBDropTable(dbh,table);
+            psDBCleanup(dbh);
+            return 2;
+        }
+
+        // Get metadata which contains row from array
+        row = (psMetadata*)psArrayGet(ary, 0);
+
+        // Get key_s32 item from metadata
+        item = psMetadataLookup(row, TAB_COL_1_NAME);
+        if (item == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Could not find key_s32 item in row metadata");
+            psDBDropTable(dbh,table);
+            psDBCleanup(dbh);
+            return 2;
+        }
+        if (item->data.S32 != S32_1) {
+            psError(PS_ERR_UNKNOWN,true,"Row item %d not as expected %d",
+                    item->data.S32,S32_1);
+            psDBDropTable(dbh,table);
+            psDBCleanup(dbh);
+            return 3;
+        }
+
+        // Get key_bool item from metadata
+        item = psMetadataLookup(row, TAB_COL_4_NAME);
+        if (item == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Could not find key_bool item in row metadata");
+            psDBDropTable(dbh,table);
+            psDBCleanup(dbh);
+            return 2;
+        }
+        if (item->data.B != B_1) {
+            psError(PS_ERR_UNKNOWN,true,"Row item %d not as expected %d",
+                    item->data.S32,S32_1);
+            psDBDropTable(dbh,table);
+            psDBCleanup(dbh);
+            return 4;
+        }
+        psFree(ary);
+        //        psFree(row);
+    }
+
+    // Attempt to select rows with NULL database object
+    psLogMsg(__func__,PS_LOG_INFO,
+             "Following should generate an error message for NULL database object");
+    if(psDBSelectRows(NULL, table, where, 0) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL return for NULL database object");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 5;
+    }
+
+    // Attempt to select rows with invalid table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid table");
+    if(psDBSelectRows(dbh, NULL, where, 0) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL return for NULL database object");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 5;
+    }
+
+    // Select rows with no where specified
+    ary1 = psDBSelectRows(dbh, table, NULL, 0);
+    if(ary1 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return using NULL where");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 6;
+    }
+    if(ary1->n != 3) {
+        psError(PS_ERR_UNKNOWN,true,"Rows return %d not as expected %d",
+                ary->n,3);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 7;
+    }
+    psFree(ary1);
+
+    psDBDropTable(dbh, table);
+    psFree(md);
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+// Testpoint #848, TPDBInsertOneRow shall write a row of data into a test table ...
+psS32 TPDBInsertOneRow( void )
+{
+    psDB *dbh = NULL;
+    const char* table = "table6";
+    psMetadata *invalidRow = NULL;
+    psArray*    ary = NULL;
+
+    // Create table definition metadata
+    psMetadata *row=NULL, *md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return NULL for database initialization");
+        return 1;
+    }
+
+    // Create database table
+    if(!psDBCreateTable(dbh, table, md)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false for create database");
+        psDBCleanup(dbh);
+        return 2;
+    }
+
+    // Insert a single row with valid arguments
+    row = _get_const_row1();
+    if(!psDBInsertOneRow(dbh, table, row)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false for inserting row w/ valid arguments");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 3;
+    }
+    // Verify row was added to table
+    ary = psDBSelectRows(dbh,table,NULL,0);
+    if(ary->n != 1) {
+        psError(PS_ERR_UNKNOWN,true,"Number of rows %d not as expected %d",
+                ary->n,1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 30;
+    }
+    psFree(ary);
+
+    // Insert a single row with NULL database
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL database");
+    if(psDBInsertOneRow(NULL,table,row)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return value true for NULL database");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 4;
+    }
+
+    // Insert a single row which has column which does not match table
+    invalidRow = _get_invalid_row();
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid column item");
+    if(psDBInsertOneRow(dbh,table,invalidRow)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return value true for invalid column item");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 6;
+    }
+
+    // Insert a single row with invalid table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid table");
+    if(psDBInsertOneRow(dbh,"table999",row)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return value true for invalid table");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 5;
+    }
+
+    // Insert a single row with NULL row
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL row");
+    if(psDBInsertOneRow(dbh,table,NULL)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return value true for NULL row");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 6;
+    }
+
+    psFree(row);
+    psFree(invalidRow);
+    psDBDropTable(dbh, table);
+    psFree(md);
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+// Testpoint #849, TPDBInsertRows shall write rows of data into a test table ...
+psS32 TPDBInsertRows( void )
+{
+    psDB *dbh = NULL;
+    const char* table = "table7";
+    psArray *rowSet = NULL;
+    psArray* dumpRowSet = NULL;
+    psMetadataItem *item=NULL;
+    psArray* ary = NULL;
+    psMetadata* invalidRow = NULL;
+    psArray* invalidRowSet = NULL;
+
+    //Create database table definition metadata
+    psMetadata *row=NULL, *md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL for initializing database connection");
+        return 1;
+    }
+
+    // Create database table for testing
+    if(!psDBCreateTable(dbh, table, md)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false for creating table");
+        psDBCleanup(dbh);
+        return 2;
+    }
+
+    // Create array of row elements
+    rowSet = psArrayAlloc(3);
+    rowSet->n = 0;
+
+    row = _get_const_row1();
+    psArrayAdd(rowSet, 0, row);
+    psFree(row);
+
+    row = _get_const_row2();
+    psArrayAdd(rowSet, 0, row);
+    psFree(row);
+
+    row = _get_row();
+    psArrayAdd(rowSet, 0, row);
+    psFree(row);
+
+    // Insert rows with valid arguments
+    if(!psDBInsertRows(dbh, table, rowSet)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false for inserting rows w/ valid args");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 3;
+    }
+
+    // Verify row was added to table
+    ary = psDBSelectRows(dbh,table,NULL,0);
+    if(ary->n != 3) {
+        psError(PS_ERR_UNKNOWN,true,"Number of rows %d not as expected %d",
+                ary->n,3);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 30;
+    }
+    psFree(ary);
+
+    // extra checks ...
+    dumpRowSet = psDBDumpRows(dbh, table);
+    if (dumpRowSet == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL from dump rows");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 4;
+    }
+
+    // Get first row from array
+    row = (psMetadata*)psArrayGet(dumpRowSet, 0);
+    // Verify contents for column key_s32
+    item = psMetadataLookup(row, TAB_COL_1_NAME);
+    if ((item == NULL) || (item->data.S32 != S32_1)) {
+        psError(PS_ERR_UNKNOWN,true,"Column value %d not as expected %d",
+                item->data.S32, S32_1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 5;
+    }
+    // Verify contents for column key_bool
+    item = psMetadataLookup(row, TAB_COL_4_NAME);
+    if ((item == NULL) || (item->data.B != B_1)) {
+        psError(PS_ERR_UNKNOWN,true,"Column value %d not as expected %d",
+                item->data.B, B_1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 6;
+    }
+    psFree(dumpRowSet);
+    //    psFree(row);
+
+    // Insert rows with NULL database object
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL database");
+    if(psDBInsertRows(NULL,table,rowSet)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return of false for NULL database");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 7;
+    }
+
+    // Insert rows with NULL table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL table");
+    if(psDBInsertRows(dbh,NULL,rowSet)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return of false for NULL table");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 70;
+    }
+
+    // Insert rows with invalid table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid table");
+    if(psDBInsertRows(dbh,"table999",rowSet)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return of false for invalid table");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 8;
+    }
+
+    // Insert rows with NULL array of rows to be inserted
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL row array");
+    if(psDBInsertRows(dbh,table,NULL)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return of false for NULL row array");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 9;
+    }
+
+    // Insert a single row which has column which does not match table
+    invalidRow = _get_invalid_row();
+    // Create array of invalid row element
+    invalidRowSet = psArrayAlloc(1);
+    invalidRowSet->n = 0;
+    psArrayAdd(invalidRowSet, 0, invalidRow);
+    psFree(invalidRow);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid column item");
+    if(psDBInsertRows(dbh,table,invalidRowSet)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return value true for invalid column item");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 10;
+    }
+    psFree(invalidRowSet);
+
+    psFree(rowSet);
+    psDBDropTable(dbh, table);
+    psFree(md);
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+// Testpoint #850, TPDBDumpRows shall dump all rows from a test table ...
+psS32 TPDBDumpRows( void )
+{
+    psDB *dbh = NULL;
+    const char* table = "table8";
+    psArray *ary = NULL;
+
+    // Create table definition metadata
+    psMetadata *row=NULL, *md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return NULL from initializing database");
+        return 1;
+    }
+
+    // Create table for testing
+    if(!psDBCreateTable(dbh, table, md)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false from creating table");
+        psDBCleanup(dbh);
+        return 2;
+    }
+
+    // Insert known data rows into table
+    row = _get_const_row1();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    row = _get_const_row2();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    row = _get_row();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    // Dump row with valid parameters
+    ary = psDBDumpRows(dbh, table);
+    if (ary == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return NULL for dump rows");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 3;
+    }
+    if(ary->n != 3) {
+        psError(PS_ERR_UNKNOWN,true,"Rows dumped %d not as expected %d",
+                ary->n, 3);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 4;
+    }
+
+    row = (psMetadata*)psArrayGet(ary, 0);
+    if(!_check_row(row)) {
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 5;
+    }
+    //    psFree(row);
+    row = (psMetadata*)psArrayGet(ary, 1);
+    if(!_check_const_row2(row)) {
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 6;
+    }
+    //    psFree(row);
+    row = (psMetadata*)psArrayGet(ary, 2);
+    if(!_check_const_row1(row)) {
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 7;
+    }
+    //    psFree(row);
+    psFree(ary);
+
+    // Attempt to dump row with database NULL
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL database");
+    if(psDBDumpRows(NULL,table)!=NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return non-NULL for NULL database specified");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 8;
+    }
+
+    // Attempt to dump row with table NULL
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL table");
+    if(psDBDumpRows(dbh,NULL)!=NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return non-NULL for NULL table specified");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 9;
+    }
+
+    psDBDropTable(dbh, table);
+
+    psFree(md);
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+// Testpoint #851, TPDBDumpCols shall dump all cols from a test table ...
+psS32 TPDBDumpCols( void )
+{
+    psDB*                dbh     = NULL;
+    const char*          table   = "table9";
+    psMetadata*          row     = NULL;
+    psMetadata*          meta    = NULL;
+    psMetadata*          md      = NULL;
+    psMetadataIterator*  mdIter  = NULL;
+    psMetadataItem*      mdItem  = NULL;
+    int                  itemNum = 0;
+
+    // Create database table definition
+    md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return to initialize database");
+        return 1;
+    }
+
+    // Create database table for testing
+    if(!psDBCreateTable(dbh, table, md)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return of false to create test table");
+        psDBCleanup(dbh);
+        return 2;
+    }
+
+    // Add row with known data values
+    row = _get_const_row1();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    row = _get_const_row2();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    row = _get_row();
+    psDBInsertOneRow(dbh, table, row);
+    psFree(row);
+
+    // Dump columns with valid arguments
+    meta = psDBDumpCols(dbh, table);
+    mdIter = psMetadataIteratorAlloc(meta,PS_LIST_HEAD,NULL);
+
+    // Verify contents of metadata returned
+    if (meta == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return value NULL for dumpCols with valid args");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 3;
+    }
+    if(meta->list->n != 6) {
+        psError(PS_ERR_UNKNOWN,true,"Number of cols = %d not as expected %d",
+                meta->list->n,6);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 4;
+    }
+    // Verify the metadata items
+    itemNum = 0;
+    while ((mdItem = psMetadataGetAndIncrement(mdIter)) != NULL) {
+        switch(itemNum) {
+        case 0:
+            if(mdItem->type != PS_DATA_VECTOR) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d type %d not as expected %d",
+                        itemNum,mdItem->type,PS_DATA_VECTOR);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 5*(itemNum+1);
+            }
+            if(((psVector*)mdItem->data.V)->type.type != PS_TYPE_S32) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector type %d not as expected %d",
+                        itemNum,((psVector*)mdItem->data.V)->type.type, PS_TYPE_S32);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 6*(itemNum+1);
+            }
+            if(((psVector*)mdItem->data.V)->n != 3) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector size %d not as expected %d",
+                        itemNum,((psVector*)mdItem->data.V)->n,3);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 7*(itemNum+1);
+            }
+            if(strcmp(mdItem->name,TAB_COL_5_NAME) != 0) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d name %s not as expected %s",
+                        itemNum,mdItem->name,TAB_COL_5_NAME);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 8*(itemNum+1);
+            }
+            break;
+        case 1:
+            if(mdItem->type != PS_DATA_VECTOR) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d type %d not as expected %d",
+                        itemNum,mdItem->type,PS_DATA_VECTOR);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 5*(itemNum+1);
+            }
+            if(((psVector*)mdItem->data.V)->type.type != PS_TYPE_BOOL) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector type %d not as expected %d",
+                        itemNum,((psVector*)mdItem->data.V)->type.type, PS_TYPE_BOOL);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 6*(itemNum+1);
+            }
+            if(((psVector*)mdItem->data.V)->n != 3) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector size %d not as expected %d",
+                        itemNum,((psVector*)mdItem->data.V)->n,3);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 7*(itemNum+1);
+            }
+            if(strcmp(mdItem->name,TAB_COL_4_NAME) != 0) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d name %s not as expected %s",
+                        itemNum,mdItem->name,TAB_COL_4_NAME);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 8*(itemNum+1);
+            }
+            break;
+        case 2:
+            if(mdItem->type != PS_DATA_VECTOR) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d type %d not as expected %d",
+                        itemNum,mdItem->type,PS_DATA_VECTOR);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 5*(itemNum+1);
+            }
+            if(((psVector*)mdItem->data.V)->type.type != PS_TYPE_F64) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector type %d not as expected %d",
+                        itemNum,((psVector*)mdItem->data.V)->type.type, PS_TYPE_F64);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 6*(itemNum+1);
+            }
+            if(((psVector*)mdItem->data.V)->n != 3) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector size %d not as expected %d",
+                        itemNum,((psVector*)mdItem->data.V)->n,3);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 7*(itemNum+1);
+            }
+            if(strcmp(mdItem->name,TAB_COL_3_NAME) != 0) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d name %s not as expected %s",
+                        itemNum,mdItem->name,TAB_COL_3_NAME);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 8*(itemNum+1);
+            }
+            break;
+        case 3:
+            if(mdItem->type != PS_DATA_VECTOR) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d type %d not as expected %d",
+                        itemNum,mdItem->type,PS_DATA_VECTOR);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 5*(itemNum+1);
+            }
+            if(((psVector*)mdItem->data.V)->type.type != PS_TYPE_F32) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector type %d not as expected %d",
+                        itemNum,((psVector*)mdItem->data.V)->type.type, PS_TYPE_F32);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 6*(itemNum+1);
+            }
+            if(((psVector*)mdItem->data.V)->n != 3) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector size %d not as expected %d",
+                        itemNum,((psVector*)mdItem->data.V)->n,3);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 7*(itemNum+1);
+            }
+            if(strcmp(mdItem->name,TAB_COL_2_NAME) != 0) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d name %s not as expected %s",
+                        itemNum,mdItem->name,TAB_COL_2_NAME);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 8*(itemNum+1);
+            }
+            break;
+        case 4:
+            if(mdItem->type != PS_DATA_VECTOR) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d type %d not as expected %d",
+                        itemNum,mdItem->type,PS_DATA_VECTOR);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 5*(itemNum+1);
+            }
+            if(((psVector*)mdItem->data.V)->type.type != PS_TYPE_S32) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector type %d not as expected %d",
+                        itemNum,((psVector*)mdItem->data.V)->type.type, PS_TYPE_S32);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 6*(itemNum+1);
+            }
+            if(((psVector*)mdItem->data.V)->n != 3) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector size %d not as expected %d",
+                        itemNum,((psVector*)mdItem->data.V)->n,3);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 7*(itemNum+1);
+            }
+            if(strcmp(mdItem->name,TAB_COL_1_NAME) != 0) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d name %s not as expected %s",
+                        itemNum,mdItem->name,TAB_COL_1_NAME);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 8*(itemNum+1);
+            }
+            break;
+        case 5:
+            if(mdItem->type != PS_DATA_ARRAY) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d type %d not as expected %d",
+                        itemNum,mdItem->type,PS_DATA_ARRAY);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 5*(itemNum+1);
+            }
+            if(((psArray*)mdItem->data.V)->n != 3) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d vector size %d not as expected %d",
+                        itemNum,((psArray*)mdItem->data.V)->n,3);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 7*(itemNum+1);
+            }
+            if(strcmp(mdItem->name,TAB_COL_0_NAME) != 0) {
+                psError(PS_ERR_UNKNOWN,true,"Column #%d name %s not as expected %s",
+                        itemNum,mdItem->name,TAB_COL_0_NAME);
+                psDBDropTable(dbh,table);
+                psDBCleanup(dbh);
+                return 8*(itemNum+1);
+            }
+            break;
+
+        default:
+            break;
+        }
+        itemNum++;
+    }
+    psFree(mdIter);
+    psFree(meta);
+
+    // Attempt to dump columns from NULL database object
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL database");
+    if(psDBDumpCols(NULL,table) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL when dumping columns from NULL database");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 10;
+    }
+
+    // Attempt to dump columns for NULL table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL table");
+    if(psDBDumpCols(dbh,NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL when dumping columns from NULL table");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 11;
+    }
+
+
+    psDBDropTable(dbh, table);
+
+    psFree(md);
+
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+// Testpoint #852, TPDBUpdateRows shall update rows in a test table ...
+psS32 TPDBUpdateRows( void )
+{
+    psDB*            dbh       = NULL;
+    const char*      table     = "table10";
+    psArray*         ary       = NULL;
+    psArray*         rowSet    = NULL;
+    psMetadata*      row       = NULL;
+    psMetadata*      where     = NULL;
+    psMetadata*      updates   = NULL;
+    psMetadata*      md        = NULL;
+    int              chgRows   = 0;
+
+    // Create database table definition
+    md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return NULL when initializing database");
+        return 1;
+    }
+
+    // Create test table
+    if(!psDBCreateTable(dbh, table, md)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false to create test table");
+        psDBCleanup(dbh);
+        return 2;
+    }
+
+    // Create array to hold rows to be added to test table
+    rowSet = psArrayAlloc(3);
+    rowSet->n = 0;
+
+    row = _get_const_row1();
+    psArrayAdd(rowSet, 0, row);
+    psFree(row);
+
+    row = _get_const_row2();
+    psArrayAdd(rowSet, 0, row);
+    psFree(row);
+
+    row = _get_row();
+    psArrayAdd(rowSet, 0, row);
+    psFree(row);
+
+    // Insert rows into test table
+    if(!psDBInsertRows(dbh, table, rowSet)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false for inserting rows");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 3;
+    }
+
+    // Create where metadata to specify update - only one row
+    where = _get_where();
+
+    // Create update metadata to specify values
+    updates = _get_update_values(STR_2, S32_2, F32_2, F64_2, B_2);
+
+    // Perform database update with valid parameters
+    chgRows = psDBUpdateRows(dbh,table,where,updates);
+    if(chgRows != 1) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return other than 1 for valid arguments");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 4;
+    }
+    // Verify row contents after update
+    ary = psDBDumpRows(dbh, table);
+    if (ary == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return from dump rows");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 5;
+    }
+    row = (psMetadata*)psArrayGet(ary, 0);
+    if(!_check_row_update(row)) {
+        psError(PS_ERR_UNKNOWN,true,"Update row not verified");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 6;
+    }
+    //    psFree(row);
+    psFree(ary);
+
+    // Attempt to update rows with NULL database
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL database");
+    chgRows = psDBUpdateRows(NULL,table,where,updates);
+    if(chgRows != -1) {
+        psError(PS_ERR_UNKNOWN,true,"Updated rows %ld not as expected %ld",
+                chgRows,-1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 7;
+    }
+
+    // Attempt to update rows with invalid table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid table");
+    chgRows = psDBUpdateRows(dbh,"table999",where,updates);
+    if(chgRows != -1) {
+        psError(PS_ERR_UNKNOWN,true,"Updated rows %ld not as expected %ld",
+                chgRows,-1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 8;
+    }
+
+    // Attempt to update rows with NULL updates
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL updates");
+    chgRows = psDBUpdateRows(dbh,table,where,NULL);
+    if(chgRows != -1) {
+        psError(PS_ERR_UNKNOWN,true,"Updated rows %ld not as expected %ld",
+                chgRows,-1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 8;
+    }
+
+    // Attempt to update rows with invalid where specifying non-existent column
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid where");
+    psFree(where);
+    where = _get_invalid_where();
+    chgRows = psDBUpdateRows(dbh,table,where,updates);
+    if(chgRows != -1) {
+        psError(PS_ERR_UNKNOWN,true,"Updated rows %ld not as expected %ld",
+                chgRows,-1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 9;
+    }
+
+    // Attempt to update rows with bad value in where statement
+    psFree(where);
+    where = _get_where_bad_value();
+    chgRows = psDBUpdateRows(dbh,table,where,updates);
+    if(chgRows != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Updated rows %ld not as expected %ld",
+                chgRows,0);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 10;
+    }
+    // Verify row contents after update - no change
+    ary = psDBDumpRows(dbh, table);
+    if (ary == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return from dump rows");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 11;
+    }
+    row = (psMetadata*)psArrayGet(ary, 0);
+    if(!_check_row_update(row)) {
+        psError(PS_ERR_UNKNOWN,true,"Update row not verified");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 12;
+    }
+    //    psFree(row);
+    psFree(ary);
+
+    psFree(updates);
+    psFree(where);
+    psFree(rowSet);
+    psDBDropTable(dbh, table);
+
+    psFree(md);
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+// Testpoint #853, TPDBDeleteRows shall update rows in a test table ...
+psS32 TPDBDeleteRows( void )
+{
+    psDB*         dbh       = NULL;
+    const char*   table     = "table11";
+    psArray*      ary       = NULL;
+    psArray*      rowSet    = NULL;
+    psMetadata*   where     = NULL;
+    psMetadata*   row       = NULL;
+    psMetadata*   md        = NULL;
+    int           chgRows   = 0;
+
+    // Create database table definition
+    md = _get_CreateTableMetadata();
+
+    // Initialize database connection
+    dbh = _init_psDB();
+    if (dbh == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return NULL for initialize database");
+        return 1;
+    }
+
+    // Create test table
+    if(!psDBCreateTable(dbh, table, md)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false for creating test table");
+        psDBCleanup(dbh);
+        return 2;
+    }
+
+    // Create known data rows to put into test table
+    rowSet = psArrayAlloc(3);
+    rowSet->n = 0;
+    row = _get_const_row1();
+    psArrayAdd(rowSet, 0, row);
+    psFree(row);
+    row = _get_const_row2();
+    psArrayAdd(rowSet, 0, row);
+    psFree(row);
+    row = _get_row();
+    psArrayAdd(rowSet, 0, row);
+    psFree(row);
+    if(!psDBInsertRows(dbh, table, rowSet)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return false for inserting data into table");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 3;
+    }
+
+    // Create where clause to specify the row to remove
+    where = _get_where();
+
+    // Delete row with valid arguments
+    chgRows = psDBDeleteRows(dbh,table,where,10);
+    if(chgRows != 1) {
+        psError(PS_ERR_UNKNOWN,true,"Deleted rows %ld not as expected %ld",
+                chgRows,1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 4;
+    }
+    // Verify table contents
+    ary = psDBDumpRows(dbh, table);
+    if (ary == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return NULL for dump rows");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 5;
+    }
+    if (ary->n != 2) {
+        psError(PS_ERR_UNKNOWN,true,"Remaining rows %d not as expected %d",
+                ary->n,2);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 6;
+    }
+    psFree(ary);
+
+    // Attempt to delete row just deleted again
+    chgRows = psDBDeleteRows(dbh,table,where,10);
+    if(chgRows != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Deleted rows %ld not as expected %ld",
+                chgRows,0);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 7;
+    }
+    // Verify table contents
+    ary = psDBDumpRows(dbh, table);
+    if (ary == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return NULL for dump rows");
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 8;
+    }
+    if (ary->n != 2) {
+        psError(PS_ERR_UNKNOWN,true,"Remaining rows %d not as expected %d",
+                ary->n,2);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 9;
+    }
+    psFree(ary);
+
+    // Attempt to delete row with NULL database
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL database");
+    chgRows = psDBDeleteRows(NULL,table,where,10);
+    if(chgRows > 0) {
+        psError(PS_ERR_UNKNOWN,true,"Deleted rows %ld not as expected %ld",
+                chgRows, -1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 10;
+    }
+
+    // Attempt to delete row with invalid table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid table");
+    chgRows = psDBDeleteRows(dbh,"table999",where,10);
+    if(chgRows > 0) {
+        psError(PS_ERR_UNKNOWN,true,"Deleted rows %ld not as expected %ld",
+                chgRows, -1);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 11;
+    }
+
+    // Attempt to delete row with NULL where - deletes all rows
+    chgRows = psDBDeleteRows(dbh,table,NULL,10);
+    if(chgRows != 2) {
+        psError(PS_ERR_UNKNOWN,true,"Deleted rows %ld not as expected %ld",
+                chgRows, 2);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 12;
+    }
+    // Verify table contents
+    ary = psDBDumpRows(dbh, table);
+    if (ary != NULL && ary->n != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Remaining rows %d not as expected %d",
+                ary->n,0);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 14;
+    }
+    psFree(ary);
+
+    // Attempt to delete row with NULL where - deletes all rows from an empty table
+    chgRows = psDBDeleteRows(dbh,table,NULL,10);
+    if(chgRows != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Deleted rows %ld not as expected %d",
+                chgRows, 0);
+        psDBDropTable(dbh,table);
+        psDBCleanup(dbh);
+        return 15;
+    }
+
+    psFree(where);
+    psFree(rowSet);
+    psDBDropTable(dbh, table);
+
+    psFree(md);
+    psDBCleanup(dbh);
+
+    return 0;
+}
+
+//
+// Tests for psDBCreate and psDBDrop can only be executed if the user
+// has system admin privileges for the SQL server so the tests have been
+// commented out.
+//
+// Testpoint #XXX, psDBCreate shall create a new test database.
+//psS32 TPDBCreate( void )
+//{
+//    const char *test_db = "ps_test_db";
+//    psS32 failed = 0;
+//    psDB *dbh = NULL;
+//
+//    dbh = _init_psDB();
+//    if (dbh == NULL) {
+//        return 1;
+//    }
+//
+//    psLogMsg( __func__, PS_LOG_INFO, "psDBCreate shall create new new test database.\n" );
+//
+//    failed = ! psDBCreate(dbh, test_db);
+//    if (!failed) {
+//        psDBDrop(dbh, test_db);
+//    }
+//
+//    psDBCleanup(dbh);
+//
+//    return failed;
+//}
+
+// Testpoint #XXX, psDBDrop shall create a new test database.
+//psS32 TPDBDrop( void )
+//{
+//    const char *test_db = "ps_test_db";
+//    psS32 failed = 0;
+//    psDB *dbh = NULL;
+//
+//    dbh = _init_psDB();
+//    if (dbh == NULL) {
+//        return 1;
+//    }
+//
+//    psLogMsg( __func__, PS_LOG_INFO, "psDBDrop shall create/drop a new new test database.\n" );
+//
+//    failed = psDBCreate(dbh, test_db);
+//    if (!failed) {
+//        failed = ! psDBDrop(dbh, test_db);
+//    }
+//
+//    psDBCleanup(dbh);
+//
+//    return failed;
+//}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fft/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fft/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fft/.cvsignore	(revision 22322)
@@ -0,0 +1,15 @@
+temp
+.deps
+.libs
+Makefile
+Makefile.in
+tst_psImageFFT
+tst_psVectorFFT
+*.bb
+*.bbg
+*.da
+gmon.out
+tap_psImageFFT2
+tap_psVectorFFT2
+tap_psImageFFT
+tap_psVectorFFT
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fft/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fft/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fft/Makefile.am	(revision 22322)
@@ -0,0 +1,26 @@
+AM_CPPFLAGS = \
+    $(SRCINC) \
+    -I$(top_srcdir)/test/tap/src \
+    -I$(top_srcdir)/test/pstap/src \
+    $(PSLIB_CFLAGS)
+
+AM_LDFLAGS = \
+    $(top_builddir)/src/libpslib.la  \
+    $(top_builddir)/test/tap/src/libtap.la \
+    $(top_builddir)/test/pstap/src/libpstap.la \
+    $(PSLIB_LIBS)
+
+TEST_PROGS = \
+	tap_psImageFFT \
+	tap_psVectorFFT
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fft/tap_psImageFFT.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fft/tap_psImageFFT.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fft/tap_psImageFFT.c	(revision 22322)
@@ -0,0 +1,129 @@
+#include <stdio.h>
+#include <math.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+#define TOL 2.0e-5                      // Tolerance for comparison
+
+
+// Generate image with single high pixel
+static psImage *generateImage(int numCols, int numRows)
+{
+    psImage *image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            image->data.F32[y][x] = 1.2 * cos(2.0 * M_PI * x / numCols + M_PI / 4.0) +
+                3.4 * sin(2.0 * M_PI * y / numRows + M_PI);
+        }
+    }
+    return image;
+}
+
+// FFT forward, then back --- do I get what I started with?
+// A total of 6 tests here.
+static void testFFT(int numCols, int numRows)
+{
+    psMemId id = psMemGetId();
+
+    psImage *old = generateImage(numCols, numRows);
+    psImage *fftReal = NULL, *fftImag = NULL;
+    bool result = psImageForwardFFT(&fftReal, &fftImag, old);
+    ok(result, "forward fft result");
+    skip_start(!result || !fftReal || !fftImag, 3, "forward fft failed");
+    ok(fftReal->type.type == PS_TYPE_F32 && fftImag->type.type == PS_TYPE_F32, "forward fft types");
+    psImage *new = NULL;
+    result = psImageBackwardFFT(&new, fftReal, fftImag, old->numCols);
+    ok(result, "backward fft result");
+    skip_start(!result || !new, 2, "backward fft failed");
+    ok(new->type.type == PS_TYPE_F32, "backward fft type");
+    float maxDev = 0.0;                 // Maximum deviation from expected
+    for (int y = 0; y < old->numRows; y++) {
+        for (int x = 0; x < old->numCols; x++) {
+            float dev = fabs(new->data.F32[y][x] / numCols / numRows - old->data.F32[y][x]);
+            if (dev > maxDev) {
+                maxDev = dev;
+            }
+        }
+    }
+    ok(maxDev < TOL, "maximum deviation: %f", maxDev);
+    psFree(new);
+    skip_end();
+    skip_end();
+
+    psFree(fftReal);
+    psFree(fftImag);
+    psFree(old);
+    ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+
+    return;
+}
+
+
+int main(int argc, char *argv[])
+{
+    plan_tests(8 + 6 * 5);
+
+    // Test with NULL real arg
+    {
+        psMemId id = psMemGetId();
+        psImage *real = psImageAlloc(512, 512, PS_TYPE_F32);
+        psImage *imag = psImageAlloc(512, 512, PS_TYPE_F32);
+        psImage *in = psImageAlloc(512, 512, PS_TYPE_F32);
+        bool rc = psImageForwardFFT(NULL, &imag, in);
+        ok(rc == false, "psImageForwardFFT() returned FALSE with a NULL real image input");
+        psFree(real);
+        psFree(imag);
+        psFree(in);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with NULL imag arg
+    {
+        psMemId id = psMemGetId();
+        psImage *real = psImageAlloc(512, 512, PS_TYPE_F32);
+        psImage *imag = psImageAlloc(512, 512, PS_TYPE_F32);
+        psImage *in = psImageAlloc(512, 512, PS_TYPE_F32);
+        bool rc = psImageForwardFFT(&real, NULL, in);
+        ok(rc == false, "psImageForwardFFT() returned FALSE with a NULL imaginary image input");
+        psFree(real);
+        psFree(imag);
+        psFree(in);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with NULL input arg
+    {
+        psMemId id = psMemGetId();
+        psImage *real = psImageAlloc(512, 512, PS_TYPE_F32);
+        psImage *imag = psImageAlloc(512, 512, PS_TYPE_F32);
+        psImage *in = psImageAlloc(512, 512, PS_TYPE_F32);
+        bool rc = psImageForwardFFT(&real, &imag, NULL);
+        ok(rc == false, "psImageForwardFFT() returned FALSE with a NULL real image input");
+        psFree(real);
+        psFree(imag);
+        psFree(in);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with incorrect input image type
+    {
+        psMemId id = psMemGetId();
+        psImage *real = psImageAlloc(512, 512, PS_TYPE_F32);
+        psImage *imag = psImageAlloc(512, 512, PS_TYPE_F32);
+        psImage *in = psImageAlloc(512, 512, PS_TYPE_F64);
+        bool rc = psImageForwardFFT(NULL, &imag, in);
+        ok(rc == false, "psImageForwardFFT() returned FALSE with incorrect input image type");
+        psFree(real);
+        psFree(imag);
+        psFree(in);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    testFFT(10, 10);                    // Real quick test
+    testFFT(10, 20);                    // Testing differing numCols, numRows
+    testFFT(20, 10);                    // Testing differing numCols, numRows
+    testFFT(611, 610);                  // Test something like an OTA cell
+    testFFT(2048, 4096);                // Test something like a megacam chip
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fft/tap_psVectorFFT.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fft/tap_psVectorFFT.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fft/tap_psVectorFFT.c	(revision 22322)
@@ -0,0 +1,123 @@
+#include <stdio.h>
+#include <math.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+#define TOL 2.5e-5                      // Tolerance for comparison
+
+
+// Generate image with single high pixel
+static psVector *generateVector(long num)
+{
+    psVector *vector = psVectorAlloc(num, PS_TYPE_F32);
+    for (long i = 0; i < num; i++) {
+        vector->data.F32[i] = 1.2 * cos(2.0 * M_PI * i / num + M_PI / 4.0) +
+            3.4 * sin(0.5 * 2.0 * M_PI * i / num + M_PI);
+    }
+    return vector;
+}
+
+// FFT forward, then back --- do I get what I started with?
+// A total of 6 tests here.
+static void testFFT(long num)
+{
+    psMemId id = psMemGetId();
+
+    psVector *old = generateVector(num);
+    psVector *fftReal = NULL, *fftImag = NULL;
+    bool result = psVectorForwardFFT(&fftReal, &fftImag, old);
+    ok(result, "forward fft result");
+    skip_start(!result || !fftReal || !fftImag, 3, "forward fft failed");
+    ok(fftReal->type.type == PS_TYPE_F32 && fftImag->type.type == PS_TYPE_F32, "forward fft types");
+    psVector *new = NULL;
+    result = psVectorBackwardFFT(&new, fftReal, fftImag, old->n);
+    ok(result, "backward fft result");
+    skip_start(!result || !new, 2, "backward fft failed");
+    ok(new->type.type == PS_TYPE_F32, "backward fft type");
+    float maxDev = 0.0;                 // Maximum deviation from expected
+    for (long i = 0; i < old->n; i++) {
+        float dev = fabs(new->data.F32[i] / num - old->data.F32[i]);
+        if (dev > maxDev) {
+            maxDev = dev;
+        }
+    }
+    ok(maxDev < TOL, "maximum deviation: %f", maxDev);
+    psFree(new);
+    skip_end();
+    skip_end();
+
+    psFree(fftReal);
+    psFree(fftImag);
+    psFree(old);
+    ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+
+    return;
+}
+
+
+int main(int argc, char *argv[])
+{
+    plan_tests(8 + 6 * 3);
+
+    // Test with NULL real arg
+    {
+        psMemId id = psMemGetId();
+        psVector *real = psVectorAlloc(512, PS_TYPE_F32);;
+        psVector *imag = psVectorAlloc(512, PS_TYPE_F32);
+        psVector *in = psVectorAlloc(512, PS_TYPE_F32);
+        bool rc = psVectorForwardFFT(NULL, &imag, in);
+        ok(rc == false, "psVectorForwardFFT() returned FALSE with a null real vector input");
+        psFree(real);
+        psFree(imag);
+        psFree(in);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with NULL imag arg
+    {
+        psMemId id = psMemGetId();
+        psVector *real = psVectorAlloc(512, PS_TYPE_F32);;
+        psVector *imag = psVectorAlloc(512, PS_TYPE_F32);
+        psVector *in = psVectorAlloc(512, PS_TYPE_F32);
+        bool rc = psVectorForwardFFT(&real, NULL, in);
+        ok(rc == false, "psVectorForwardFFT() returned FALSE with a null imag vector input");
+        psFree(real);
+        psFree(imag);
+        psFree(in);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with NULL input arg
+    {
+        psMemId id = psMemGetId();
+        psVector *real = psVectorAlloc(512, PS_TYPE_F32);;
+        psVector *imag = psVectorAlloc(512, PS_TYPE_F32);
+        psVector *in = psVectorAlloc(512, PS_TYPE_F32);
+        bool rc = psVectorForwardFFT(&real, &imag, NULL);
+        ok(rc == false, "psVectorForwardFFT() returned FALSE with a null input vector input");
+        psFree(real);
+        psFree(imag);
+        psFree(in);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with incorrect type for input arg
+    {
+        psMemId id = psMemGetId();
+        psVector *real = psVectorAlloc(512, PS_TYPE_F32);;
+        psVector *imag = psVectorAlloc(512, PS_TYPE_F32);
+        psVector *in = psVectorAlloc(512, PS_TYPE_F64);
+        bool rc = psVectorForwardFFT(&real, &imag, in);
+        ok(rc == false, "psVectorForwardFFT() returned FALSE with a incorrect input vector type");
+        psFree(real);
+        psFree(imag);
+        psFree(in);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    testFFT(128);                       // Real quick test
+    testFFT(2048);                      // Test something big
+    testFFT(123456);                    // Test something really big
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fft/tst_psImageFFT.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fft/tst_psImageFFT.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fft/tst_psImageFFT.c	(revision 22322)
@@ -0,0 +1,664 @@
+/** @file  tst_psImageFFT.c
+ *
+ *  @brief Contains the tests for psFFT.[ch]
+ *
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-07-13 02:46:59 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <complex.h>
+#include <math.h>
+#include <float.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+#define GENIMAGE(img,c,r,TYP, valueFcn) \
+img = psImageAlloc(c,r,PS_TYPE_##TYP); \
+for (psU32 row=0;row<r;row++) { \
+    ps##TYP* imgRow = img->data.TYP[row]; \
+    for (psU32 col=0;col<c;col++) { \
+        imgRow[col] = (ps##TYP)(valueFcn); \
+    } \
+}
+
+static psS32 testImageFFT(void);
+static psS32 testImageRealImaginary(void);
+static psS32 testImageComplex(void);
+static psS32 testImageConjugate(void);
+static psS32 testImagePowerSpectrum(void);
+
+testDescription tests[] = {
+                              {testImageFFT,632,"psImageFFT",0,false},
+                              {testImageRealImaginary,633,"psImageRealImaginary",0,false},
+                              {testImageComplex,634,"psImageComplex",0,false},
+                              {testImageConjugate,635,"psImageConjugate",0,false},
+                              {testImagePowerSpectrum,636,"psImagePowerSpectrum",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    return (! runTestSuite(stderr,"psFFT",tests,argc,argv) );
+}
+
+psS32 testImageFFT(void)
+{
+    psImage* img = NULL;
+    psImage* img2 = NULL;
+    psImage* img3 = NULL;
+    psU32 m = 128;
+    psU32 n = 64;
+    psImage* img4 = NULL;
+    psImage* img5 = NULL;
+
+    /*
+    1. assign a image to a radial sinisoid
+    2. perform a forward transform
+    3. verify that the only significant component cooresponds to the freqency of the input in step 1.
+    4. perform a reverse transform
+    5. compare to original (should be equal to within a reasonable error)
+    */
+
+    // 1. assign a image to a radial sinisoid
+    GENIMAGE(img,m,n,F32, sinf((32.0f-row)/32.0f*M_PI)+sinf((64.0f-col)/64.0f*M_PI));
+
+    // 2. perform a forward transform
+    img2 = psImageFFT(img2,img,PS_FFT_FORWARD);
+    if (img2->type.type != PS_TYPE_C32) {
+        psError(PS_ERR_UNKNOWN, true,"FFT didn't produce complex values?");
+        return 1;
+    }
+    if (img2->numCols != m || img2->numRows != n) {
+        psError(PS_ERR_UNKNOWN, true,"FFT didn't produce proper size result (%dx%d vs. expected %dx%d).",
+                img2->numCols,img2->numRows,m,n);
+        return 2;
+    }
+
+    // 3. verify that the only significant component cooresponds to the freqency of the input in step 1.
+    for (psU32 row=0;row<n;row++) {
+        psC32* img2Row = img2->data.C32[row];
+        for (psU32 col=0;col<m;col++) {
+            psF32 mag = cabsf(img2Row[col])/m/n;
+            if (mag > 0.1f) {
+                // must be (0,1) or (0,n-1) or (1,0) or (m-1,0)
+                if (! (col == 0 && (row == 1 || row == n-1))
+                        && ! (row == 0 && (col==1 || col == m-1)) ) {
+                    psError(PS_ERR_UNKNOWN, true,"Result invalid at %d,%d (%.2f)",col,row,mag);
+                    return 3;
+                }
+            } else
+                if ( (col == 0 && (row == 1 || row == n-1))
+                        || (row == 0 && (col==1 || col == m-1)) ) {
+                    psError(PS_ERR_UNKNOWN, true,"Result invalid at %d,%d (%.2f)",col,row,mag);
+                    return 4;
+                }
+        }
+    }
+
+
+    // 4. perform a reverse transform
+    img3 = psImageFFT(img3,img2,PS_FFT_REVERSE);
+
+    if (img3->type.type != PS_TYPE_C32) {
+        psError(PS_ERR_UNKNOWN, true,"FFT didn't produce complex values?");
+        return 5;
+    }
+
+    if (img3->numCols != m || img3->numRows != n) {
+        psError(PS_ERR_UNKNOWN, true,"FFT didn't produce proper size result (%dx%d vs. expected %dx%d).",
+                img3->numCols,img3->numRows,m,n);
+        return 6;
+    }
+
+    for (psU32 row=0;row<n;row++) {
+        psC32* img3Row = img3->data.C32[row];
+        psF32* imgRow = img->data.F32[row];
+        for (psU32 col=0;col<m;col++) {
+            psF32 pixel = creal(img3Row[col])/m/n;
+            if (fabsf(pixel-imgRow[col]) > 0.1) {
+                psError(PS_ERR_UNKNOWN, true,"Reverse FFT didn't give original image back (%d,%d %.2f vs %.2f)",
+                        col,row,pixel,imgRow[col]);
+                return 7;
+            }
+        }
+    }
+
+    // 4. perform a reverse transform to real result
+    img3 = psImageFFT(img3,img2,PS_FFT_REVERSE|PS_FFT_REAL_RESULT);
+
+    if (img3->type.type != PS_TYPE_F32) {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,img3->type.type)
+        psError(PS_ERR_UNKNOWN, true,"FFT asked to make real result, but I got a %s type image?",typeStr);
+        return 8;
+    }
+
+    if (img3->numCols != m || img3->numRows != n) {
+        psError(PS_ERR_UNKNOWN, true,"FFT didn't produce proper size result (%dx%d vs. expected %dx%d).",
+                img3->numCols,img3->numRows,m,n);
+        return 9;
+    }
+
+    for (psU32 row=0;row<n;row++) {
+        psF32* img3Row = img3->data.F32[row];
+        psF32* imgRow = img->data.F32[row];
+        for (psU32 col=0;col<m;col++) {
+            psF32 pixel = img3Row[col]/m/n;
+            if (fabsf(pixel-imgRow[col]) > 0.1) {
+                psError(PS_ERR_UNKNOWN, true,"Reverse FFT didn't give original image back (%d,%d %.2f vs %.2f)",
+                        col,row,pixel,imgRow[col]);
+                return 10;
+            }
+        }
+    }
+
+    // check if error occurs if FORWARD and REVERSE are both given.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    if (psImageFFT(NULL,img2,PS_FFT_REVERSE|PS_FFT_FORWARD) != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"PS_FFT_REVERSE|PS_FFT_FORWARD option produced something?");
+        return 11;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    if (psImageFFT(NULL,img2,PS_FFT_FORWARD|PS_FFT_REAL_RESULT) != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"PS_FFT_FORWARD|PS_FFT_REAL_RESULT option produced something?");
+        return 12;
+    }
+
+    /* Verify return null and program execution doesn't stop if input image is null */
+    img4 = psImageFFT(NULL,NULL,PS_FFT_FORWARD);
+    if (img4 != NULL ) {
+        psError(PS_ERR_UNKNOWN, true,"psImageFFT should return null for a null input image.");
+        return 10;
+    }
+
+    /* Verify return null and program execution doesn't stop if input image is invalid direction */
+    GENIMAGE(img4,8,8,S8,row+col);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error for invalid direction.");
+    img5 = psImageFFT(NULL,img4,PS_FFT_REAL_RESULT);
+    if (img5 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageFFT should return null for an invalid FFT direction.");
+        return 11;
+    }
+    psFree(img4);
+
+    psFree(img);
+    psFree(img2);
+    psFree(img3);
+
+    return 0;
+}
+
+psS32 testImageRealImaginary(void)
+{
+    psImage* img = NULL;
+    psImage* img2 = NULL;
+    psImage* img3 = NULL;
+    psImage* c64Img = NULL;
+    psImage* c64Img2 = NULL;
+    psImage* c64Img3 = NULL;
+    psImage* ncImg = NULL;
+    psImage* ncImg2 = NULL;
+    psImage* ncImg3 = NULL;
+
+    psU32 m = 128;
+    psU32 n = 64;
+
+    /*
+    1. create a C32 complex vector with distinctly different real and imaginary parts.
+    2. call psImageReal and psImageImaginary
+    3. compare results to the real/imaginary components of input
+    */
+
+    // 1. create a C32 complex vector with distinctly different real and imaginary parts.
+    GENIMAGE(img,m,n,C32, row + I * col);
+
+    // 2. call psImageReal and psImageImaginary
+    img2 = psImageReal(img2,img);
+    if (img2 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageReal returned a NULL?");
+        return 1;
+    }
+    if (img2->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN, true,"psImageReal returned a wrong type (%d)?",
+                img2->type.type);
+        return 2;
+    }
+
+    img3 = psImageImaginary(img3,img);
+    if (img3 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageImaginary returned a NULL?");
+        return 3;
+    }
+    if (img3->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN, true,"psImageImaginary returned a wrong type (%d)?",
+                img3->type.type);
+        return 4;
+    }
+
+    // 3. compare results to the real/imaginary components of input
+    for (psU32 row=0;row<n;row++) {
+        psF32* img2Row = img2->data.F32[row];
+        psF32* img3Row = img3->data.F32[row];
+        for (psU32 col=0;col<m;col++) {
+            if (fabsf(img2Row[col] - row) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,"psImageReal didn't return the real portion at n=%d",
+                        n);
+                return 5;
+            }
+            if (fabsf(img3Row[col] - col) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,"psImageImaginary didn't return the imag portion at n=%d",
+                        n);
+                return 6;
+            }
+        }
+    }
+
+    psFree(img);
+    psFree(img2);
+    psFree(img3);
+
+    /*
+    1. create a C64 complex vector with distinctly different real and imaginary parts.
+    2. call psImageReal and psImageImaginary
+    3. compare results to the real/imaginary components of input
+    */
+
+    // 1. create a C64 complex vector with distinctly different real and imaginary parts.
+    GENIMAGE(c64Img,m,n,C64, row + I * col);
+
+    // 2. call psImageReal and psImageImaginary
+    c64Img2 = psImageReal(c64Img2,c64Img);
+    if (c64Img2 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageReal returned a NULL?");
+        return 1;
+    }
+    if (c64Img2->type.type != PS_TYPE_F64) {
+        psError(PS_ERR_UNKNOWN, true,"psImageReal returned a wrong type (%d)?",
+                img2->type.type);
+        return 2;
+    }
+
+    c64Img3 = psImageImaginary(c64Img3,c64Img);
+    if (c64Img3 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageImaginary returned a NULL?");
+        return 3;
+    }
+    if (c64Img3->type.type != PS_TYPE_F64) {
+        psError(PS_ERR_UNKNOWN, true,"psImageImaginary returned a wrong type (%d)?",
+                c64Img3->type.type);
+        return 4;
+    }
+
+    // 3. compare results to the real/imaginary components of input
+    for (psU32 row=0;row<n;row++) {
+        psF64* img2Row = c64Img2->data.F64[row];
+        psF64* img3Row = c64Img3->data.F64[row];
+        for (psU32 col=0;col<m;col++) {
+            if (fabsf(img2Row[col] - row) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,"psImageReal didn't return the real portion at n=%d",
+                        n);
+                return 5;
+            }
+            if (fabsf(img3Row[col] - col) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,"psImageImaginary didn't return the imag portion at n=%d",
+                        n);
+                return 6;
+            }
+        }
+    }
+
+    GENIMAGE(ncImg,m,n,F32,row+col);
+    ncImg2 = psImageReal(ncImg2,ncImg);
+    ncImg3 = psImageImaginary(ncImg3,ncImg);
+    if(ncImg2 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psImageReal returned NULL");
+        return 40;
+    }
+    if(ncImg2->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN,true,"psImageReal returned a wrong type");
+        return 41;
+    }
+    if(ncImg3 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psImageImaginary returned NULL");
+        return 42;
+    }
+    if(ncImg3->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN,true,"psImageImaginary returned NULL");
+        return 43;
+    }
+    for(psU32 row=0; row<n; row++) {
+        psF32* ncImg2Row = ncImg2->data.F32[row];
+        psF32* ncImg3Row = ncImg3->data.F32[row];
+        for(psU32 col=0; col<m; col++) {
+            if(fabsf(ncImg2Row[col] - (row+col)) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN,true,"psImageReal didn't return the real portion");
+                return 45;
+            }
+            if(fabsf(ncImg3Row[col] - 0) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN,true,"psImageImaginary didn't return the imaginary portion");
+                return 46;
+            }
+        }
+    }
+    psFree(ncImg);
+    psFree(ncImg2);
+    psFree(ncImg3);
+
+    psFree(c64Img);
+    psFree(c64Img2);
+    psFree(c64Img3);
+
+    // Perform psImageReal with null input
+    if(psImageReal(NULL,NULL) != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"psImageReal did not return null with null input");
+        return 10;
+    }
+
+    // Perform psImageImaginary with null input
+    if(psImageImaginary(NULL,NULL) != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"psImageImaginary did not return null with null input");
+        return 10;
+    }
+
+    return 0;
+}
+
+psS32 testImageComplex(void)
+{
+    psImage* img = NULL;
+    psImage* img2 = NULL;
+    psImage* img3 = NULL;
+    psImage* c64Img = NULL;
+    psImage* c64Img2 = NULL;
+    psImage* c64Img3 = NULL;
+    psImage* pImg = NULL;
+    psImage* pImg2 = NULL;
+    psImage* pImg3 = NULL;
+
+    psU32 m = 128;
+    psU32 n = 64;
+
+    /*
+    1. create two unique psF32 vectors of the same size
+    2. call psImageComplex
+    3. verify that the result is a psC32
+    4. use crealf and cimagf on step 2 results
+    5. compare step 4 results to input.
+
+    6. create a psF32 and a psF64 vector of the same size
+    7. call psImageComplex
+    8. verify that an appropriate error occurred.
+
+    9. create two psf32 vectors of different sizes
+    10. call psImageComplex
+    11. verify thet an appropriate error occurred.
+    */
+
+    // 1. create two unique psF32 vectors of the same size
+    GENIMAGE(img,m,n,F32,row);
+    GENIMAGE(img2,m,n,F32,col);
+    GENIMAGE(c64Img,m,n,F64,row);
+    GENIMAGE(c64Img2,m,n,F64,col);
+    GENIMAGE(pImg,m,n,S16,row);
+    GENIMAGE(pImg2,m,n,S16,col);
+
+    // 2. call psImageComplex
+    img3 = psImageComplex(img3,img,img2);
+    c64Img3 = psImageComplex(c64Img3,c64Img,c64Img2);
+
+    // 3. verify that the result is a psC32
+    if (img3->type.type != PS_TYPE_C32) {
+        psError(PS_ERR_UNKNOWN, true,"Image Type from psImageComplex is not complex? (%d)",
+                img3->type.type);
+        return 1;
+    }
+    if (c64Img3->type.type != PS_TYPE_C64) {
+        psError(PS_ERR_UNKNOWN,true,"Image type from psImageComplex is not complex");
+        return 10;
+    }
+
+    // 4. call psImageReal and psImageImaginary on step 2 results (not needed, just use crealf/cimagf)
+    // 5. compare step 4 results to input.
+    for (psU32 row=0;row<n;row++) {
+        psC32* img3Row = img3->data.C32[row];
+        psC64* c64Img3Row = c64Img3->data.C64[row];
+        for (psU32 col=0;col<m;col++) {
+            if (fabsf(crealf(img3Row[col]) - row) > FLT_EPSILON ||
+                    fabsf(cimagf(img3Row[col]) - col) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,"psImageComplex result is invalid (%d,%d, %.2f+%.2fi)",
+                        col,row,crealf(img3Row[col]),cimagf(img3Row[col]));
+                return 2;
+            }
+            if (fabsf(crealf(c64Img3Row[col]) - row) > FLT_EPSILON ||
+                    fabsf(cimagf(c64Img3Row[col]) - col) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN,true,"psImageComplex result is invalid");
+                return 3;
+            }
+        }
+    }
+
+    // 6. create a psF32 and a psF64 image of the same size
+    img2 = psImageRecycle(img2,m,n,PS_TYPE_F64);
+
+    // 7. call psImageComplex
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error (type mismatch).");
+    img3 = psImageComplex(img3,img,img2);
+    // 8. verify that an appropriate error occurred. (this partially has to be done via inspection)
+    if (img3 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageComplex returned a image though input types mismatched.");
+        return 3;
+    }
+
+    // 9. create two psf32 vectors of different sizes
+    img2 = psImageRecycle(img2,m/2,n,PS_TYPE_F32);
+
+    // 10. call psImageComplex
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error (size mismatch).");
+    img3 = psImageComplex(img3,img,img2);
+
+    // 11. verify thet an appropriate error occurred.
+    if (img3 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageComplex returned a image though input sizes mismatched.");
+        return 4;
+    }
+
+    // Perform psImageComplex with invalid type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
+    pImg3 = psImageComplex(pImg3,pImg,pImg2);
+    if ( pImg3 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psImageComplex did not return NULL");
+        return 50;
+    }
+
+    psFree(img);
+    psFree(img2);
+    psFree(img3);
+    psFree(c64Img);
+    psFree(c64Img2);
+    psFree(c64Img3);
+    psFree(pImg);
+    psFree(pImg2);
+
+    // Perform psImageComplex with null input
+    if(psImageComplex(NULL,NULL,NULL) != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"psImageComplex did not return null with null input");
+        return 20;
+    }
+
+    return 0;
+}
+
+psS32 testImageConjugate(void)
+{
+    psImage* img = NULL;
+    psImage* img2 = NULL;
+    psImage* c64Img = NULL;
+    psImage* c64Img2 = NULL;
+    psImage* pImg = NULL;
+    psImage* pImg2 = NULL;
+
+    psU32 m = 128;
+    psU32 n = 64;
+
+    /*
+    1. create a psC32 with unique real and imaginary values.
+    2. call psImageConjugate
+    3. verify result is psC32
+    4. verify each value is conjugate of input (a+bi -> a-bi)
+    */
+
+    // 1. create a psC32 with unique real and imaginary values.
+    GENIMAGE(img,m,n,C32, row + I * col);
+    GENIMAGE(c64Img,m,n,C64,row + I*col);
+    GENIMAGE(pImg,m,n,F32,row+col);
+
+    // 2. call psImageConjugate
+    img2 = psImageConjugate(img2,img);
+    c64Img2 = psImageConjugate(c64Img2,c64Img);
+    pImg2 = psImageConjugate(pImg2,pImg);
+
+    // 3. verify result is psC32
+    if (img2->type.type != PS_TYPE_C32) {
+        psError(PS_ERR_UNKNOWN, true,"the psImageConjugate didn't return a C32 image.");
+        return 1;
+    }
+    if (c64Img2->type.type != PS_TYPE_C64) {
+        psError(PS_ERR_UNKNOWN,true,"psImageConjugate did not return a C64 image");
+        return 10;
+    }
+    if (pImg2->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN,true,"psImageConjugate did not return a F32 image");
+        return 11;
+    }
+
+    // 4. verify each value is conjugate of input (a+bi -> a-bi)
+    for (psU32 row=0;row<n;row++) {
+        psC32* img2Row = img2->data.C32[row];
+        psC64* c64Img2Row = c64Img2->data.C64[row];
+        psF32* pImg2Row = pImg2->data.F32[row];
+        for (psU32 col=0;col<m;col++) {
+            if (fabsf(crealf(img2Row[col]) - row) > FLT_EPSILON ||
+                    fabsf(cimagf(img2Row[col]) + col) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,"psImageComplex result is invalid (%d,%d, %.2f+%.2fi)",
+                        col,row,crealf(img2Row[col]),cimagf(img2Row[col]));
+                return 2;
+            }
+            if (fabsf(crealf(c64Img2Row[col]) - row) > FLT_EPSILON ||
+                    fabsf(cimagf(c64Img2Row[col]) + col) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN,true,"psImageComplex result is invalid");
+                return 20;
+            }
+            if (fabsf(pImg2Row[col] - (row+col)) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN,true,"psImageComplex result is invalid");
+                return 21;
+            }
+        }
+    }
+
+    psFree(img);
+    psFree(img2);
+    psFree(c64Img);
+    psFree(c64Img2);
+    psFree(pImg);
+    psFree(pImg2);
+
+    // Perform psImageConjugate with null input
+    if(psImageConjugate(NULL,NULL) != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"psImageConjugate did not return null with null input");
+        return 30;
+    }
+
+    return 0;
+}
+
+psS32 testImagePowerSpectrum(void)
+{
+    psImage* img = NULL;
+    psImage* img2 = NULL;
+    psImage* c64Img = NULL;
+    psImage* c64Img2 = NULL;
+    psImage* pImg = NULL;
+
+    psU32 m = 128;
+    psU32 n = 64;
+
+    /*
+    1. create a psC32 vector with unique real and imaginary components
+    2. call psImagePowerSpectrum
+    3. verify result is psF32
+    4. verify the values are the square of the absolute values of the original
+    */
+
+    // 1. create a psC32 vector with unique real and imaginary components
+    GENIMAGE(img,m,n,C32, row + I * col);
+    GENIMAGE(c64Img,m,n,C64,row+I*col);
+    GENIMAGE(pImg,m,n,F32,row+col);
+
+    // 2. call psImagePowerSpectrum
+    img2 = psImagePowerSpectrum(img2,img);
+    c64Img2 = psImagePowerSpectrum(c64Img2, c64Img);
+
+    // 3. verify result is psF32
+    if (img2->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN, true,"the type was not PS_TYPE_F32.");
+        return 1;
+    }
+    if (c64Img2->type.type != PS_TYPE_F64) {
+        psError(PS_ERR_UNKNOWN,true,"psImagePowerSpectrum did not return PS_TYPE_F64");
+        return 10;
+    }
+
+    // 4. verify the values are the square of the absolute values of the original
+    for (psU32 row=0;row<n;row++) {
+        psC32* imgRow = img->data.C32[row];
+        psF32* img2Row = img2->data.F32[row];
+        psC64* c64ImgRow = c64Img->data.C64[row];
+        psF64* c64Img2Row = c64Img2->data.F64[row];
+        for (psU32 col=0;col<m;col++) {
+            psF32 power = cabs(imgRow[col]);
+            psF64 power64 = cabs(c64ImgRow[col]);
+            power *= power/n/n/m/m;
+            power64 *= power64/n/n/m/m;
+
+            if (fabsf(img2Row[col] - power) > 2.0f*FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,"psImagePowerSpectrum result is invalid (%d,%d, %.2f vs %.2f)",
+                        col,row,img2Row[col],power);
+                return 2;
+            }
+            if (fabsf(c64Img2Row[col] - power64) > 2.0f*FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN,true,"psImagePowerSpectrum result is invalid");
+                return 22;
+            }
+        }
+    }
+
+    psFree(img);
+    psFree(img2);
+    psFree(c64Img);
+    psFree(c64Img2);
+
+    // Perform psImagePowerSpectrum with invalid input
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message.");
+    if(psImagePowerSpectrum(NULL,pImg) != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"psImagePowerSpectrum did not return null with invalid type");
+        return 41;
+    }
+    psFree(pImg);
+
+    // Perform psImagePowerSpectrum with null input
+    if(psImagePowerSpectrum(NULL,NULL) != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"psImagePowerSpectrum did not return null with null input");
+        return 40;
+    }
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fft/tst_psVectorFFT.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fft/tst_psVectorFFT.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fft/tst_psVectorFFT.c	(revision 22322)
@@ -0,0 +1,642 @@
+/** @file  tst_psVectorFFT.c
+*
+*  @brief Contains the tests for psFFT.[ch]
+*
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-07-13 02:46:59 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <math.h>
+#include <float.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+#define GENIMAGE(img,c,r,TYP, valueFcn) \
+img = psImageAlloc(c,r,PS_TYPE_##TYP); \
+for (psU32 row=0;row<r;row++) { \
+    ps##TYP* imgRow = img->data.TYP[row]; \
+    for (psU32 col=0;col<c;col++) { \
+        imgRow[col] = (ps##TYP)(valueFcn); \
+    } \
+}
+
+static psS32 testVectorFFT( void );
+static psS32 testVectorRealImaginary( void );
+static psS32 testVectorComplex( void );
+static psS32 testVectorConjugate( void );
+static psS32 testVectorPowerSpectrum( void );
+
+testDescription tests[] = {
+                              {testVectorFFT, 600, "psVectorFFT", 0, false},
+                              {testVectorRealImaginary, 601, "psVectorRealImaginary", 0, false},
+                              {testVectorComplex, 602, "psVectorComplex", 0, false},
+                              {testVectorConjugate, 603, "psVectorConjugate", 0, false},
+                              {testVectorPowerSpectrum, 604, "psVectorPowerSpectrum", 0, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psFFT", tests, argc, argv ) );
+}
+
+psS32 testVectorFFT( void )
+{
+    psVector * vec = NULL;
+    psVector* vec2 = NULL;
+    psVector* vec3 = NULL;
+    psVector* vec4 = NULL;
+
+    /*
+    1. assign a vector to a sinisoid
+    2. perform a forward transform
+    3. verify that the only significant component cooresponds to the freqency of the input in step 1.
+    4. perform a reverse transform
+    5. compare to original (should be equal to within a reasonable error)
+    */
+
+    // 1. assign a vector to a sinisoid
+    vec = psVectorAlloc( 100, PS_TYPE_F32 );
+    vec->n = vec->nalloc;
+    for ( psU32 n = 0; n < 100; n++ ) {
+        vec->data.F32[ n ] = sinf( ( psF32 ) n / 50.0f * M_PI );
+    }
+
+    // 2. perform a forward transform
+    vec2 = psVectorFFT( NULL, vec, PS_FFT_FORWARD );
+    if ( vec2->type.type != PS_TYPE_C32 ) {
+        psError(PS_ERR_UNKNOWN,true, "FFT didn't produce complex values?" );
+        return 1;
+    }
+
+    // 2a. verify output vector is same size as input
+    if ( vec2->n != vec->n ) {
+        psError(PS_ERR_UNKNOWN,true, "FFT didn't return vector with same size as input");
+        return 10;
+    }
+
+
+    // 3. verify that the only significant component cooresponds to the freqency of the input in step 1.
+    for ( psU32 n = 0; n < 100; n++ ) {
+        if ( n == 1 || n == 99 ) {
+            if ( fabsf( cabsf( vec2->data.C32[ n ] ) - 50.0f ) > 0.1f ) {
+                psError(PS_ERR_UNKNOWN,true, "FFT didn't work for vector (n=%d)", n );
+                return 2;
+            }
+        } else {
+            if ( fabsf( cabsf( vec2->data.C32[ n ] ) ) > 0.1f ) {
+                psError(PS_ERR_UNKNOWN,true, "FFT didn't work for vector (n=%d)", n );
+                return 3;
+            }
+        }
+    }
+
+    // 4. perform a reverse transform
+    vec3 = psVectorFFT( NULL, vec2, PS_FFT_REVERSE );
+    if ( vec3->type.type != PS_TYPE_C32 ) {
+        psError(PS_ERR_UNKNOWN,true, "FFT didn't produce complex values?" );
+        return 4;
+    }
+    if ( vec3->n != vec2->n ) {
+        psError(PS_ERR_UNKNOWN,true, "FFT didn't return vector with same size as input");
+        return 40;
+    }
+    for ( psU32 n = 0; n < 100; n++ ) {
+        psF32 val = sinf( ( psF32 ) n / 50.0f * M_PI );
+        psF32 vecVal = crealf( vec3->data.C32[ n ] ) / 100;
+        if ( fabsf( vecVal - val ) > 0.1f ) {
+            psError(PS_ERR_UNKNOWN,true, "Reverse FFT didn't give me the original vector back (n=%d) (%.2f vs %.2f)",
+                    n, vecVal, val );
+            return 5;
+        }
+    }
+
+    // Perform a reverse transform with real flag set
+    vec4 = psVectorFFT(NULL,vec2, (PS_FFT_REVERSE | PS_FFT_REAL_RESULT));
+    if(vec4->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN,true,"FFT with real result did not produce real values");
+        return 80;
+    }
+
+    // Perform vector FFT with invalid direction flags
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
+    if ( psVectorFFT(NULL,vec2,(psFFTFlags)0) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with invalid direction");
+        return 70;
+    }
+
+    psFree( vec );
+    psFree( vec2 );
+    psFree( vec3 );
+    psFree( vec4 );
+
+    // Perform vector FFT with null input
+    if ( psVectorFFT(NULL,NULL,PS_FFT_FORWARD) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with null input vector");
+        return 64;
+    }
+
+    return 0;
+}
+
+psS32 testVectorRealImaginary( void )
+{
+    psVector * vec = NULL;
+    psVector* vec2 = NULL;
+    psVector* vec3 = NULL;
+    psVector* vec4 = NULL;
+    psVector* vec5 = NULL;
+    psVector* vec6 = NULL;
+    psVector* vec7 = NULL;
+    psVector* vec8 = NULL;
+    psVector* vec9 = NULL;
+    psVector* vec10 = NULL;
+    psVector* vec11 = NULL;
+
+    /*
+    1. create a C32 complex vector with distinctly different real and imaginary parts.
+    2. call psVectorReal and psVectorImaginary
+    3. compare results to the real/imaginary components of input
+    */
+
+    // 1. create a C32 complex vector with distinctly different real and imaginary parts.
+    vec = psVectorAlloc( 100, PS_TYPE_C32 );
+    vec8 = psVectorAlloc( 100, PS_TYPE_C64 );
+    vec10 = psVectorAlloc( 100, PS_TYPE_C64 );
+    vec->n = vec->nalloc;
+    vec8->n = vec8->nalloc;
+    vec10->n = vec10->nalloc;
+    for ( psU32 n = 0; n < 100; n++ ) {
+        vec->data.C32[ n ] = n + I * ( n * 2 );
+        vec8->data.C64[n] = n + I * ( n * 2 );
+        vec10->data.C64[n] = n + I * ( n * 2 );
+    }
+    vec4 = psVectorAlloc( 100, PS_TYPE_F32);
+    vec4->n = vec4->nalloc;
+    vec6 = psVectorAlloc( 100, PS_TYPE_F32);
+    vec6->n = vec6->nalloc;
+    for ( psU32 n = 0; n < 100; n++ ) {
+        vec4->data.F32[n] = n;
+        vec6->data.F32[n] = n;
+    }
+
+    // 2. call psVectorReal and psVectorImaginary
+    vec2 = psVectorReal( vec2, vec );
+    if ( vec2 == NULL ) {
+        psError(PS_ERR_UNKNOWN,true, "psVectorReal returned a NULL?" );
+        return 1;
+    }
+    if ( vec2->type.type != PS_TYPE_F32 ) {
+        psError(PS_ERR_UNKNOWN,true, "psVectorReal returned a wrong type (%d)?",
+                vec2->type.type );
+        return 2;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning.");
+    vec5 = psVectorReal(vec5, vec4);
+    if (vec5 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorReal returned NULL");
+        return 10;
+    }
+    if ( vec5->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorReal returned the wrong type");
+        return 11;
+    }
+
+    vec9 = psVectorReal(vec9,vec8);
+    if(vec9 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorReal returned NULL");
+        return 20;
+    }
+    if(vec9->type.type != PS_TYPE_F64) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorReal returned the wrong type");
+        return 21;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning.");
+    vec3 = psVectorImaginary( vec3, vec );
+    if ( vec3 == NULL ) {
+        psError(PS_ERR_UNKNOWN,true, "psVectorImaginary returned a NULL?" );
+        return 3;
+    }
+    if ( vec3->type.type != PS_TYPE_F32 ) {
+        psError(PS_ERR_UNKNOWN,true, "psVectorImaginary returned a wrong type (%d)?",
+                vec3->type.type );
+        return 4;
+    }
+
+    vec7 = psVectorImaginary(vec7, vec6);
+    if(vec7 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorImage returned NULL");
+        return 12;
+    }
+    if(vec7->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorImaginary returned the wrong type");
+        return 13;
+    }
+
+    vec11 = psVectorImaginary(vec11, vec10);
+    if(vec11 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorImaginary returned NULL");
+        return 14;
+    }
+    if(vec11->type.type != PS_TYPE_F64) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorImaginary returned the wrong type");
+        return 15;
+    }
+
+    // 3. compare results to the real/imaginary components of input
+    for ( psU32 n = 0; n < 100; n++ ) {
+        psF32 r = n;
+        psF32 i = ( n * 2 );
+        psF64 rr = n;
+        psF64 ii = ( n * 2 );
+        if ( fabsf( vec2->data.F32[ n ] - r ) > FLT_EPSILON ) {
+            psError(PS_ERR_UNKNOWN,true, "psVectorReal didn't return the real portion at n=%d",
+                    n );
+            return 5;
+        }
+        if ( fabsf( vec3->data.F32[ n ] - i ) > FLT_EPSILON ) {
+            psError(PS_ERR_UNKNOWN,true, "psVectorImaginary didn't return the real portion at n=%d",
+                    n );
+            return 6;
+        }
+        if ( fabsf( vec5->data.F32[n] - r) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN,true,"psVectorReal didn't return the real portion at n=%d",n);
+            return 50;
+        }
+        if ( fabsf( vec7->data.F32[n] - 0) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN,true,"psVectorImaginary did not return the imaginary portion at n=%d",n);
+            return 51;
+        }
+        if ( fabsf(vec9->data.F64[n] - rr) > FLT_EPSILON ) {
+            psError(PS_ERR_UNKNOWN,true,"psVectorReal did not return the real portion at n=%d",n);
+            return 52;
+        }
+        if ( fabsf(vec11->data.F64[n] - ii) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN,true,"psVectorImaginary did not return the imaginary portion at n=%d",n);
+            return 53;
+        }
+    }
+
+    psFree( vec );
+    psFree( vec2 );
+    psFree( vec3 );
+    psFree( vec4 );
+    psFree( vec5 );
+    psFree( vec6 );
+    psFree( vec7 );
+    psFree( vec8 );
+    psFree( vec9 );
+    psFree( vec10 );
+    psFree( vec11 );
+
+    // Perform vector Real with null input
+    if ( psVectorReal(NULL,NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with null input vector");
+        return 63;
+    }
+    // Perform vector Imaginary with null input
+    if ( psVectorImaginary(NULL,NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with null input vector");
+        return 73;
+    }
+
+    return 0;
+}
+
+psS32 testVectorComplex( void )
+{
+    psVector * vec = NULL;
+    psVector* vec2 = NULL;
+    psVector* vec3 = NULL;
+    psVector* vec4 = NULL;
+    psVector* vec5 = NULL;
+    psVector* vec6 = NULL;
+
+    /*
+    1. create two unique psF32 vectors of the same size
+    2. call psVectorComplex
+    3. verify that the result is a psC32
+    4. call psVectorReal and psVectorImaginary on step 2 results
+    5. compare step 4 results to input.
+
+    6. create a psF32 and a psF64 vector of the same size
+    7. call psVectorComplex
+    8. verify that an appropriate error occurred.
+
+    9. create two psf32 vectors of different sizes
+    10. call psVectorComplex
+    11. verify thet an appropriate error occurred.
+    */
+
+    // 1. create two unique psF32 vectors of the same size
+    vec = psVectorAlloc( 100, PS_TYPE_F32 );
+    vec2 = psVectorAlloc( 100, PS_TYPE_F32 );
+    vec4 = psVectorAlloc( 100, PS_TYPE_F64 );
+    vec5 = psVectorAlloc( 100, PS_TYPE_F64 );
+    vec->n = vec->nalloc;
+    vec2->n = vec2->nalloc;
+    vec4->n = vec4->nalloc;
+    vec5->n = vec5->nalloc;
+    for ( psU32 n = 0; n < 100; n++ ) {
+        vec->data.F32[ n ] = n;
+        vec2->data.F32[ n ] = ( n * 2 );
+        vec4->data.F64[ n ] = n;
+        vec5->data.F64[ n ] = ( n * 2 );
+    }
+
+    // 2. call psVectorComplex
+    vec3 = psVectorComplex( vec3, vec, vec2 );
+
+    // 3. verify that the result is a psC32
+    if ( vec3->type.type != PS_TYPE_C32 ) {
+        psError(PS_ERR_UNKNOWN,true, "Vector Type from psVectorComplex is not complex? (%d)",
+                vec3->type.type );
+        return 1;
+    }
+
+    // 4. call psVectorReal and psVectorImaginary on step 2 results (not needed, just use crealf/cimagf)
+    // 5. compare step 4 results to input.
+    for ( psU32 n = 0; n < 100; n++ ) {
+        if ( fabsf( crealf( vec3->data.C32[ n ] ) - n ) > FLT_EPSILON ||
+                fabsf( cimagf( vec3->data.C32[ n ] ) - ( n * 2 ) ) > FLT_EPSILON ) {
+            psError(PS_ERR_UNKNOWN,true, "psVectorComplex result is invalid (n=%d, %.2f+%.2fi)",
+                    n, crealf( vec3->data.C32[ n ] ), cimagf( vec3->data.C32[ n ] ) );
+            return 2;
+        };
+    }
+
+
+    // 6. create a psF32 and a psF64 vector of the same size
+    vec2 = psVectorRecycle( vec2, 100, PS_TYPE_F64 );
+
+    // 7. call psVectorComplex
+    psLogMsg(__func__, PS_LOG_INFO, "Following should be an error (type mismatch)." );
+    vec3 = psVectorComplex( vec3, vec, vec2 );
+    // 8. verify that an appropriate error occurred. (this partially has to be done via inspection)
+    if ( vec3 != NULL ) {
+        psError(PS_ERR_UNKNOWN,true, "psVectorComplex returned a vector though input types mismatched." );
+        return 3;
+    }
+
+    // 9. create two psf32 vectors of different sizes
+    vec2 = psVectorRecycle( vec2, 200, PS_TYPE_F32 );
+
+    // 10. call psVectorComplex
+    vec3 = psVectorComplex( vec3, vec, vec2 );
+
+    // 11. verify thet an appropriate error occurred. (actually, it isn't an error...)
+    if ( vec3->n != 100 ) {
+        psError(PS_ERR_UNKNOWN,true, "psVectorComplex returned a larger vector than the input supported (%d).", vec3->n );
+        return 4;
+    }
+
+    // Verify the function works with psF64 type
+    vec6 = psVectorComplex(vec6, vec4, vec5);
+    if( vec6->type.type != PS_TYPE_C64 ) {
+        psError(PS_ERR_UNKNOWN,true,"Vector return type is not complex");
+        return 40;
+    }
+
+    // Verify error message generated with input of invalid type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
+    vec4->type.type = PS_TYPE_S8;
+    vec5->type.type = PS_TYPE_S8;
+    vec6 = psVectorComplex(vec6, vec4, vec5);
+    if(vec6 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid type");
+        return 50;
+    }
+    vec4->type.type = PS_TYPE_F64;
+    vec5->type.type = PS_TYPE_F64;
+
+    psFree( vec );
+    psFree( vec2 );
+    psFree( vec3 );
+    psFree( vec4 );
+    psFree( vec5 );
+
+    // Perform vector complex with null input
+    if ( psVectorComplex(NULL,NULL,NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with null input vector");
+        return 62;
+    }
+
+    return 0;
+}
+
+psS32 testVectorConjugate( void )
+{
+    psVector * vec = NULL;
+    psVector* vec2 = NULL;
+
+    /*
+    1. create a psC32 with unique real and imaginary values.
+    2. call psVectorConjugate
+    3. verify result is psC32
+    4. verify each value is conjugate of input (a+bi -> a-bi)
+    */
+
+    // 1. create a psC32 with unique real and imaginary values.
+    vec = psVectorAlloc( 100, PS_TYPE_C32 );
+    vec->n = vec->nalloc;
+    for ( psU32 n = 0; n < 100; n++ ) {
+        vec->data.C32[ n ] = n + I * ( n * 2 );
+    }
+
+    // 2. call psVectorConjugate
+    vec2 = psVectorConjugate( vec2, vec );
+
+    // 3. verify result is psC32
+    if ( vec2->type.type != PS_TYPE_C32 ) {
+        psError(PS_ERR_UNKNOWN,true, "the psVectorConjugate didn't return a C32 vector" );
+        return 1;
+    }
+
+    // 4. verify each value is conjugate of input (a+bi -> a-bi)
+    for ( psU32 n = 0; n < 100; n++ ) {
+        if ( fabsf( crealf( vec->data.C32[ n ] ) - crealf( vec2->data.C32[ n ] ) ) > FLT_EPSILON ||
+                fabsf( cimagf( vec->data.C32[ n ] ) + cimagf( vec2->data.C32[ n ] ) ) > FLT_EPSILON ) {
+            psError(PS_ERR_UNKNOWN,true, "psVectorConjugate result is invalid (n=%d, %.2f+%.2fi)",
+                    n, crealf( vec2->data.C32[ n ] ), cimagf( vec2->data.C32[ n ] ) );
+            return 2;
+        };
+    }
+
+    psFree( vec );
+
+    // Perform conjugate for non-complex number
+    vec = psVectorAlloc( 100, PS_TYPE_F32 );
+    vec->n = vec->nalloc;
+    for ( psU32 n = 0; n < 100; n++ ) {
+        vec->data.F32[ n ] = n;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning message.");
+    vec2 = psVectorConjugate(vec2,vec);
+    if(vec2->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorConjugate did not return a F32 vector");
+        return 10;
+    }
+    for ( psU32 n = 0; n < 100; n++ ) {
+        if( vec->data.F32[n] != vec2->data.F32[n] ) {
+            psError(PS_ERR_UNKNOWN,true,"psVectorConjugate result is invalid (n=%d)",n);
+            return 11;
+        }
+    }
+    psFree(vec);
+
+    // Perform vector conjugate with C64 type
+    vec = psVectorAlloc( 100, PS_TYPE_C64 );
+    vec->n = vec->nalloc;
+    for ( psU32 n = 0; n < 100; n++ ) {
+        vec->data.C64[n] = n + I * ( n * 2 );
+    }
+    vec2 = psVectorConjugate(vec2,vec);
+    if(vec2->type.type != PS_TYPE_C64) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorConjugate did not return a C64 vector");
+        return 12;
+    }
+    for ( psU32 n = 0; n < 100; n++ ) {
+        if ( fabsf( crealf(vec->data.C64[n]) - crealf(vec2->data.C64[n])) > FLT_EPSILON ||
+                fabsf( cimagf(vec->data.C64[n]) + cimagf(vec2->data.C64[n])) > FLT_EPSILON ) {
+            psError(PS_ERR_UNKNOWN,true,"psVectorConjugate result is invalid (n=%d)",n);
+            return 13;
+        }
+    }
+    psFree(vec);
+
+    // Perform vector conjugate with null input (vec2 should be freed too)
+    if ( psVectorConjugate(vec2,NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with null input vector");
+        return 61;
+    }
+
+    return 0;
+}
+
+psS32 testVectorPowerSpectrum( void )
+{
+    psVector * vec = NULL;
+    psVector* vec2 = NULL;
+    psVector* vec3 = NULL;
+    psVector* vec4 = NULL;
+
+    psF32 val;
+    psF64 val1;
+
+    /*
+    1. create a psC32 vector with unique real and imaginary components
+    2. call psVectorPowerSpectrum
+    3. verify result is psF32
+    4. verify the values are the square of the absolute values of the original
+    */
+
+    // 1. create a psC32 vector with unique real and imaginary components
+    vec = psVectorAlloc( 100, PS_TYPE_C32 );
+    vec->n = vec->nalloc;
+    vec3 = psVectorAlloc(100,PS_TYPE_C64);
+    vec3->n = vec3->nalloc;
+    for ( psU32 n = 0; n < 100; n++ ) {
+        vec->data.C32[ n ] = n + I * sinf( ( ( psF32 ) n ) / 50.f * M_PI );
+        vec3->data.C64[ n ] = n + I * sinf( ( ( psF64 ) n ) / 50.f * M_PI );
+    }
+
+    // 2. call psVectorPowerSpectrum
+    vec2 = psVectorPowerSpectrum( vec2, vec );
+    vec4 = psVectorPowerSpectrum( vec4, vec3 );
+
+    // 3. verify result is psF32
+    if ( vec2->type.type != PS_TYPE_F32 ) {
+        psError(PS_ERR_UNKNOWN,true, "the type was not PS_TYPE_F32." );
+        return 1;
+    }
+    if ( vec4->type.type != PS_TYPE_F64 ) {
+        psError(PS_ERR_UNKNOWN,true,"psPowerSpectrum did not return type PS_TYPE_F64 type = %d",vec4->type.type);
+        return 20;
+    }
+
+    // 3a. verify result is the same size a input
+    // Awaiting IfA direction on bug #228
+    //    if ( vec2->n != vec->n ) {
+    //       psError(PS_ERR_UNKNOWN,true, "Output vector size different(%d) than input vector(%d)",vec2->n, vec->n);
+    //       return 10;
+    //    }
+
+    // 4. verify the values are the square of the absolute values of the original
+    //   (ADD specifies something else)
+    //   P_0 = |C_0|^2/N^2
+    //   P_j = (|C_j|^2+|C_N-j|^2)/N^2
+    //   P_N/2 = |C_N/2|^2/N^2
+    //  where j = 1,2,...,(N/2-1)
+
+    val = cabsf( vec->data.C32[ 0 ] ) * cabsf( vec->data.C32[ 0 ] ) / 100 / 100;
+    val1= cabsf( vec3->data.C64[0] ) * cabsf(vec3->data.C64[0])/100/100;
+    if ( fabsf( vec2->data.F32[ 0 ] - val ) > FLT_EPSILON ) {
+        psError(PS_ERR_UNKNOWN,true, "psVectorPowerSpectrum result is invalid (n=0, %.2f %.2f)",
+                vec2->data.F32[ 0 ], val );
+        return 2;
+    }
+    if ( fabsf( vec4->data.C64[0] - val1 ) > FLT_EPSILON ) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorPowerSpectrum result is invalid (n=0)");
+        return 21;
+    }
+
+    for ( psU32 n = 1; n < 50; n++ ) {
+        val = ( cabsf( vec->data.C32[ n ] ) * cabsf( vec->data.C32[ n ] ) +
+                cabsf( vec->data.C32[ 100 - n ] ) * cabsf( vec->data.C32[ 100 - n ] ) ) / 100 / 100;
+        val1 = (cabsf(vec3->data.C64[n]) * cabsf(vec3->data.C64[n]) +
+                cabsf(vec3->data.C64[100-n]) * cabsf(vec3->data.C64[100-n]))/100/100;
+        if ( fabsf( val - vec2->data.F32[ n ] ) > 10*FLT_EPSILON ) {
+            psError(PS_ERR_UNKNOWN,true, "psVectorPowerSpectrum result is invalid (n=%d, %.2f %.2f)",
+                    n, vec2->data.F32[ n ], val );
+            return 2;
+        }
+        if (fabsf(val1 - vec4->data.F64[n]) > 10*FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN,true,"psVectorPowerSpectrum result is invalid (n=%d, %.2f %.2f)",n,vec4->data.F64[n],val1);
+            return 22;
+        }
+    }
+
+    val = cabsf( vec->data.C32[ 50 ] ) * cabsf( vec->data.C32[ 50 ] ) / 100 / 100;
+    if ( fabsf( vec2->data.F32[ 50 ] - val ) > 10*FLT_EPSILON ) {
+        psError(PS_ERR_UNKNOWN,true, "psVectorPowerSpectrum result is invalid (n=50, %.2f %.2f)",
+                vec2->data.F32[ 0 ], val );
+        return 2;
+    };
+
+    psFree( vec );
+    psFree( vec2 );
+    psFree( vec3 );
+    psFree( vec4 );
+
+    // Perform vector power spectrum with non-complex number
+    vec = psVectorAlloc(100,PS_TYPE_F32);
+    vec->n = vec->nalloc;
+    for( psU32 n=0; n<100; n++) {
+        vec->data.F32[n] = n;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
+    if(psVectorPowerSpectrum(NULL,vec) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorPowerSpectrum did not return a null vector.");
+        return 10;
+    }
+
+    // Perform vector power spectrum with null input
+    if ( psVectorPowerSpectrum(NULL,NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with null input vector");
+        return 60;
+    }
+
+    psFree(vec);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fits/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fits/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fits/.cvsignore	(revision 22322)
@@ -0,0 +1,15 @@
+temp
+.deps
+.libs
+Makefile
+Makefile.in
+multi.fits
+table.fits
+tmpImages
+tap_psFitsBlank_00
+*.bb
+*.bbg
+*.da
+gmon.out
+tap_psFits
+tap_psFitsImage
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fits/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fits/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fits/Makefile.am	(revision 22322)
@@ -0,0 +1,30 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSLIB_CFLAGS)
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpslib.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSLIB_LIBS)
+
+TEST_PROGS = \
+	tap_psFits \
+	tap_psFitsBlank_00 \
+	tap_psFitsImage
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* multi.fits table.fits tmpImages/* core core.* *~ \
+	*.bb *.bbg *.da gmon.out
+
+test: check
+
+tests: $(check_PROGRAMS)
+	$(top_srcdir)/test/test.pl
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fits/tap_psFits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fits/tap_psFits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fits/tap_psFits.c	(revision 22322)
@@ -0,0 +1,1233 @@
+/** @file  tst_psFits.c
+*
+*  @brief Contains the tests for psFits.[ch]
+*
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-11-29 01:29:20 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#include <unistd.h>
+
+#define GENIMAGE(img,c,r,TYP, valueFcn) \
+img = psImageAlloc(c,r,PS_TYPE_##TYP); \
+for (psU32 row=0;row<r;row++) { \
+    ps##TYP* imgRow = img->data.TYP[row]; \
+    for (psU32 col=0;col<c;col++) { \
+        imgRow[col] = (ps##TYP)(valueFcn); \
+    } \
+}
+
+//static bool makeMulti(void);  // implicitly tests psFitsSetExtName
+//static bool makeTable(void);
+const char* multiFilename = "multi.fits";
+const char* tableFilename = "table.fits";
+const int tableNumRows = 10;
+
+
+// N.B., the tests to Image read/write was liberally taken from the now
+// deprecated psImageReadSection/psImageWriteSection function tests.
+
+bool makeMulti(void)
+{
+    psFits* fitsFile = psFitsOpen(multiFilename,"w");
+
+    if (fitsFile == NULL) {
+        diag("Could not create 'multi' FITS file");
+        return false;
+    }
+
+    psImage* image = psImageAlloc(16,16,PS_TYPE_F32);
+
+    char extname[80];
+    for (int lcv = 0; lcv < 8; lcv++) {
+        snprintf(extname,80,"ext-%d", lcv);
+
+        psMetadata* header = psMetadataAlloc();
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYINT",
+                      PS_DATA_S32,
+                      "psS32 Item", (psS32)lcv);
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYFLT",
+                      PS_DATA_F32,
+                      "psF32 Item", (float)(1.0f/(float)(1+lcv)));
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYDBL",
+                      PS_DATA_F64,
+                      "psF64 Item", (double)(1.0/(double)(1+lcv)));
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYBOOL",
+                      PS_DATA_BOOL,
+                      "psBool Item",
+                      (lcv%2 == 0));
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYSTR",
+                      PS_DATA_STRING,
+                      "String Item",
+                      extname);
+
+        // set the pixels in the image
+        psImageInit(image, (float)lcv);
+        if (!psFitsWriteImage(fitsFile,header,image,0,extname)) {
+            diag("Could not write image");
+        }
+        psFree(header);
+    }
+    psFree(image);
+    psFree(fitsFile);
+
+    return true;
+}
+
+bool makeTable(void)
+{
+// XXX: Should we be checking for memory leaks in this function?
+//    psMemId id = psMemGetId();
+    psFits* fitsFile = psFitsOpen(tableFilename, "w");
+    if (fitsFile == NULL) {
+        diag("Could not create 'table' FITS file");
+        return false;
+    }
+
+    // make the PHU an image (per FITS standard, it must be)
+    psImage* image = psImageAlloc(16,16,PS_TYPE_F32);
+
+    if (!psFitsWriteImage(fitsFile,NULL,image,1,NULL)) {
+        diag("Could not write PHU image");
+        return false;
+    }
+
+    psFree(image);
+
+    // build a table structure
+    psArray* table = psArrayAlloc(tableNumRows);
+    psMetadata* header = NULL;
+    for (int row = 0; row < tableNumRows; row++) {
+        header = psMetadataAlloc();
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYINT",
+                      PS_DATA_S32,
+                      "psS32 Item",
+                      (psS32)row);
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYFLT",
+                      PS_DATA_F32,
+                      "psF32 Item",
+                      (float)(1.0f/(float)(1+row)));
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYDBL",
+                      PS_DATA_F64,
+                      "psF64 Item",
+                      (double)(1.0/(double)(1+row)));
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYBOOL",
+                      PS_DATA_BOOL,
+                      "psBool Item",
+                      (row%2 == 0));
+
+        char* str = NULL;
+        psStringAppend(&str,"row=%d",row+1);
+        psMetadataAdd(header,PS_LIST_TAIL, "MYSTR",
+                      PS_DATA_STRING,
+                      "psString Item",
+                      str);
+        psFree(str);
+
+        psVector* vec = psVectorAlloc(5,PS_TYPE_S32);
+        for (int x=0; x < 4; x++) {
+            vec->data.S32[x] = x*10+row;
+        }
+        psMetadataAdd(header,PS_LIST_TAIL, "MYVEC",
+                      PS_DATA_VECTOR,
+                      "psVector Item",
+                      vec);
+        psFree(vec);
+
+        table->data[row] = header;
+    }
+
+    bool rc = psFitsWriteTable(fitsFile, NULL, table, NULL);
+
+    psFree(table);
+    psFree(fitsFile);
+    return(rc);
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(509);
+
+    // tst_psFitsOpen()
+    {
+        psMemId id = psMemGetId();
+        ok(makeMulti(), "Created 'multi' FITS file");
+        psFits* fitsFile = psFitsOpen(multiFilename,"r");
+        ok(fitsFile != NULL, "psFitsOpen returned non-NULL on existing file");
+        int extNum = psFitsGetExtNum(fitsFile);
+        ok(extNum == 0, "psFitsOpen was queued to the PHU");
+        psFitsClose(fitsFile);
+
+        // make sure the file doesn't already exist.
+        // XXX: What is F_OK?
+        //        if (access("new.fits", F_OK) == 0) {
+        //            if (remove
+        //                    ("new.fits") != 0) {
+        //                psError(PS_ERR_UNKNOWN, false,
+        //                        "Couldn't delete the new.fits file");
+        //                return 3;
+        //            }
+        //        }
+
+        fitsFile = psFitsOpen("new.fits","w");
+        ok(fitsFile != NULL, "psFitsOpen returned non-NULL on w mode");
+
+        // write something to the file, otherwise CFITSIO will complain on close
+        psImage* img = psImageAlloc(16,16,PS_TYPE_F32);
+        psFitsWriteImage(fitsFile,NULL,img,1,NULL);
+        // psFree should be equivalent to psFitsClose
+        psFree(fitsFile);
+
+        // now, if psFitsOpen actually created the file, I shouldn't error in removing it.
+        ok(remove
+           ("new.fits") == 0, "psFitsOpen seemed to have created a new file");
+
+        fitsFile = psFitsOpen("new.fits","w+");
+        ok(fitsFile != NULL, "psFitsOpen returned non-NULL on w+ mode");
+
+        // write something to the file, otherwise CFITSIO will complain on close
+        psFitsWriteImage(fitsFile,NULL,img,1,NULL);
+        psFitsClose(fitsFile);
+
+        // now, if psFitsOpen actually created the file, I shouldn't error in removing it.
+        ok(remove
+           ("new.fits") == 0, "psFitsOpen seemed to have created a new file");
+
+        fitsFile = psFitsOpen("new.fits","a");
+        ok(fitsFile != NULL, "psFitsOpen returned non-NULL on a mode");
+
+        // write something to the file, otherwise CFITSIO will complain on close
+        psFitsWriteImage(fitsFile,NULL,img,1,NULL);
+        psFitsClose(fitsFile);
+        fitsFile = psFitsOpen("new.fits","a+");
+        ok(fitsFile != NULL, "psFitsOpen returned non-NULL on a+ mode");
+
+        psFitsClose(fitsFile);
+        // now, if psFitsOpen actually created the file, I shouldn't error in removing it.
+        ok(remove
+           ("new.fits") == 0, "psFitsOpen seemed to have created a new file");
+
+        // Attempt to allocate with NULL filename
+        // Following should generate an error message
+        // XXX: Verify error
+        fitsFile = psFitsOpen(NULL,"r");
+        ok(fitsFile == NULL, "psFitsOpen returned NULL for NULL input");
+
+        // Attempt to use an unallowed mode
+        // Following should generate an error message
+        // XXX: Verify error
+        fitsFile = psFitsOpen("new.fits","b+");
+        ok(fitsFile == NULL, "psFitsOpen returned NULL for NULL input");
+
+        psFree(img);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // tst_psFitsMoveExtName()
+    {
+        psMemId id = psMemGetId();
+
+        ok(makeMulti(), "Created 'multi' FITS file");
+        if (! makeMulti() )
+        {
+            return 1;
+        }
+
+        psFits* fits = psFitsOpen(multiFilename,"r");
+
+        if (fits == NULL)
+        {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsOpen returned NULL on existing file");
+            return 1;
+        }
+
+        int numHDUs = psFitsGetSize(fits);
+
+        if (numHDUs < 2)
+        {
+            psError(PS_ERR_UNKNOWN,true,
+                    "The 'multi' FITS file does not have multiple HDUs");
+            return 2;
+        }
+
+        char extName[80];
+        psRegion region = {0,0,0,0};
+
+        for (int lcv = 0; lcv < numHDUs; lcv++)
+        {
+            snprintf(extName,80,"ext-%d",lcv);
+            // try to move to the named extension.
+            if (! psFitsMoveExtName(fits, extName) ) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "Failed to move to ext-%d",
+                        lcv);
+                return 3;
+            }
+
+            // check to see if I can retrieve the name back from the psFits object.
+            char* nameFromFile = psFitsGetExtName(fits);
+            if (strcmp(nameFromFile,extName) != 0) { // hey, it didn't move?
+                psError(PS_ERR_UNKNOWN, false,
+                        "Failed to retrieve the extension name back ('%s' vs '%s'",
+                        nameFromFile, extName);
+                return 3;
+            }
+            psFree(nameFromFile);
+
+            // check that the image is associated to the extension moved, i.e.,
+            // did we really move to the proper extension?
+            psImage* image = NULL;
+            image = psFitsReadImage(fits,region,0);
+
+            if (image == NULL || abs(image->data.F32[0][0] - (float)lcv) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,
+                        "The image pixel 0,0 of ext-%d was %g, expected %d",
+                        lcv,image->data.F32[0][0],lcv);
+                return 4;
+            }
+            psFree(image);
+        }
+
+        for (int lcv = numHDUs-1; lcv >= 0; lcv--)
+        {
+            snprintf(extName,80,"ext-%d",lcv);
+            // try to move to the named extension.
+            if (! psFitsMoveExtName(fits, extName) ) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "Failed to move to ext-%d",
+                        lcv);
+                return 5;
+            }
+
+            // check to see if I can retrieve the name back from the psFits object.
+            char* nameFromFile = psFitsGetExtName(fits);
+            if (strcmp(nameFromFile,extName) != 0) { // hey, it didn't move?
+                psError(PS_ERR_UNKNOWN, false,
+                        "Failed to retrieve the extension name back ('%s' vs '%s'",
+                        nameFromFile, extName);
+                return 5;
+            }
+            psFree(nameFromFile);
+
+            // check that the image is associated to the extension moved, i.e.,
+            // did we really move to the proper extension?
+            psImage* image = NULL;
+            image = psFitsReadImage(fits,region,0);
+
+            if (abs(image->data.F32[0][0] - (float)lcv) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,
+                        "The image pixel 0,0 of ext-%d was %g, expected %d",
+                        lcv,image->data.F32[0][0],lcv);
+                return 6;
+            }
+            psFree(image);
+        }
+
+        // check to see if given a bogus extension name, it errors.
+        // Following should be an error
+        // XXX: Verify error
+        if (psFitsMoveExtName(fits, "bogus") || psErrorGetStackSize() != 1)
+        {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Moving to non-existant HDU didn't fail");
+            return 7;
+        }
+
+        // check to see if given a NULL psFits, it errors.
+        // Following should be an error
+        // XXX: Verify error
+        if (psFitsMoveExtName(NULL, "bogus") || psErrorGetStackSize() != 1)
+        {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Operation of NULL psFits didn't fail");
+            return 8;
+        }
+
+        // check to see if given a NULL extname, it errors.
+        // Following should be an error
+        // XXX: Verify error
+        if (psFitsMoveExtName(fits, NULL) || psErrorGetStackSize() != 1)
+        {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Operation of NULL extname didn't fail");
+            return 9;
+        }
+
+        psFree(fits);
+
+        // Attempt to get ext name from null fits file
+        // Following should generate an error for NULL fits file
+        if(psFitsGetExtName(NULL) != NULL)
+        {
+            psError(PS_ERR_UNKNOWN,true,"Expected NULL return from psFitsGetExtName with NULL fits file");
+            return 10;
+        }
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // tst_psFitsMoveExtNum()
+    {
+        psMemId id = psMemGetId();
+
+        ok(makeMulti(), "Created 'multi' FITS file");
+        psFits* fits = psFitsOpen(multiFilename,"r");
+        ok(fits != NULL, "psFitsOpen returned non-NULL on existing file");
+
+        int numHDUs = psFitsGetSize(fits);
+        // as a side test, let's make sure psFitsGetSize can handle NULL.
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        ok(psFitsGetSize(NULL) == 0 && psErrorGetStackSize() == 1,
+           "The 'multi' FITS file has multiple HDUs");
+
+        ok(numHDUs == 8, "The 'multi' FITS file does not have multiple HDUs");
+
+        psRegion region = {0,0,0,0};
+        // test absolute positioning
+        for (int lcv = 0; lcv < numHDUs; lcv++)
+        {
+            // try to move to the extension
+            ok(psFitsMoveExtNum(fits, lcv, false), "Moved to extension %d", lcv);
+
+            // check to see if I can retrieve the number back from the psFits object.
+            ok(psFitsGetExtNum(fits) == lcv, "Retrieved the extension number back (%d vs %d)",
+               psFitsGetExtNum(fits), lcv);
+
+            // check that the image is associated to the extension moved, i.e.,
+            // did we really move to the proper extension?
+            psImage* image = NULL;
+            image = psFitsReadImage(fits,region,0);
+            ok(image != NULL && abs(image->data.F32[0][0] - (float)lcv) <= FLT_EPSILON,
+               "The image pixel 0,0 of ext-%d was %g, expected %d",
+               lcv,image->data.F32[0][0],lcv);
+            psFree(image);
+        }
+
+        for (int lcv = numHDUs-1; lcv >= 0; lcv--)
+        {
+            // try to move to the extension
+            ok(psFitsMoveExtNum(fits, lcv, false), "Moved to extension %d", lcv);
+
+            // check that the image is associated to the extension moved, i.e.,
+            // did we really move to the proper extension?
+            psImage* image = NULL;
+            image = psFitsReadImage(fits,region,0);
+
+            ok(abs(image->data.F32[0][0] - (float)lcv) <= FLT_EPSILON,
+               "The image pixel 0,0 of ext-%d was %g, expected %d",
+               lcv, image->data.F32[0][0],lcv);
+            psFree(image);
+        }
+
+        // test relative positioning
+        psFitsMoveExtNum(fits,0,false);
+        for (int lcv = 1; lcv < numHDUs; lcv++)
+        {
+            // try to move to the extension
+            ok(psFitsMoveExtNum(fits, 1, true), "Failed to move to extension %d", lcv);
+
+            // check to see if I can retrieve the number back from the psFits object.
+            ok(psFitsGetExtNum(fits) == lcv,
+               "Failed to retrieve the extension number back (%d vs %d)",
+               psFitsGetExtNum(fits), lcv);
+
+            // check that the image is associated to the extension moved, i.e.,
+            // did we really move to the proper extension?
+            psImage* image = NULL;
+            image = psFitsReadImage(fits,region,0);
+
+            ok(image != NULL && abs(image->data.F32[0][0] - (float)lcv) <= FLT_EPSILON,
+               "The image pixel 0,0 of ext-%d was %g, expected %d",
+               lcv,image->data.F32[0][0],lcv);
+            psFree(image);
+        }
+
+        for (int lcv = numHDUs-2; lcv >= 0; lcv--)
+        {
+            // try to move to the extension
+            ok(psFitsMoveExtNum(fits, -1, true), "Failed to move to extension %d", lcv);
+
+            // check to see if I can retrieve the number back from the psFits object.
+            ok(psFitsGetExtNum(fits) == lcv,
+               "Failed to retrieve the extension number back (%d vs %d)",
+               psFitsGetExtNum(fits), lcv);
+
+            // check that the image is associated to the extension moved, i.e.,
+            // did we really move to the proper extension?
+            psImage* image = NULL;
+            image = psFitsReadImage(fits,region,0);
+
+            ok(abs(image->data.F32[0][0] - (float)lcv) <= FLT_EPSILON,
+               "The image pixel 0,0 of ext-%d was %g, expected %d",
+               lcv,image->data.F32[0][0],lcv);
+            psFree(image);
+        }
+
+
+        // check to see if given a negative extension number, it errors.
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        ok(!psFitsMoveExtNum(fits, -1, false) && psErrorGetStackSize() == 1,
+           "Moving to negative HDU did fail");
+
+        // check to see if relative positioning beyond PHU, it errors.
+        psFitsMoveExtNum(fits,0,false);
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        ok(!psFitsMoveExtNum(fits, -1, true) && psErrorGetStackSize() == 1,
+           "Moving to negative HDU did fail");
+
+        // check to see if given a extension greater than the total #HDUs, it errors.
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        ok(!psFitsMoveExtNum(fits, numHDUs, false) && psErrorGetStackSize() == 1,
+           "Moving to a HDU beyond the file's contents did fail");
+
+        // check to see if relative positioning beyond PHU, it errors.
+        psFitsMoveExtNum(fits,numHDUs-1,false);
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        ok(!psFitsMoveExtNum(fits, 1, true) && psErrorGetStackSize() == 1,
+           "Moving to negative HDU did fail");
+
+        // check to see if given a NULL psFits, it errors.
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        ok(!psFitsMoveExtNum(NULL, 0, false) && psErrorGetStackSize() == 1,
+           "Operation of NULL psFits didt fail");
+        psFitsClose(fits);
+
+        // Attempt to get ext name from null fits file
+        // Following should generate an error for NULL fits file
+        // XXX: Verify error
+        ok(psFitsGetExtNum(NULL) == PS_FITS_TYPE_NONE,
+           "Expected NULL return from psFitsGetExtNum with NULL fits file");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+
+    }
+
+
+    // tst_psFitsReadHeader()
+    {
+        psMemId id = psMemGetId();
+        ok(makeMulti(), "Created 'multi' FITS file");
+
+        psFits* fits = psFitsOpen(multiFilename,"r");
+        ok(fits != NULL, "psFitsOpen returned non-NULL on existing file");
+
+        int numHDUs = psFitsGetSize(fits);
+        ok(numHDUs >= 8, "The 'multi' FITS file has multiple HDUs");
+
+        char extname[80];
+        for (int hdunum = 0; hdunum < numHDUs; hdunum++)
+        {
+            snprintf(extname,80,"ext-%d",hdunum);
+            psFitsMoveExtNum(fits,hdunum,false);
+
+            psMetadata* header = psFitsReadHeader(NULL,fits);
+            ok(header != NULL, "Read header");
+
+            psMetadata* header2 = psMetadataAlloc();
+            header2 = psFitsReadHeader(header2,fits);
+            ok(header2 != NULL, "Read header");
+
+            ok(header->list->n >= 1 && header->list->n == header2->list->n,
+               "Reading the header given a NULL input psMetadata did not differ from giving an existing psMetadata");
+
+            // check for the extra metadata items
+            psS32 intItem = psMetadataLookupS32(NULL,header, "MYINT");
+            psF32 fltItem = psMetadataLookupF32(NULL,header, "MYFLT");
+            psF64 dblItem = psMetadataLookupF64(NULL,header, "MYDBL");
+            psMetadataItem* boolItem = psMetadataLookup(header, "MYBOOL");
+            psString strItem = psMetadataLookupStr(NULL, header, "MYSTR");
+
+            ok(intItem == hdunum, "Retrieved psS32 metadata item from file");
+
+            ok(fabsf(fltItem - 1.0f/(float)(1+hdunum)) <= FLT_EPSILON,
+               "Retrieved psF32 metadata item from file.  Got %f vs %f",
+               fltItem,1.0f/(float)(1+hdunum));
+
+            ok(abs(dblItem - 1.0/(double)(1+hdunum)) <= DBL_EPSILON,
+               "Retrieved psF64 metadata item from file.  Got %g vs %g",
+               dblItem, 1.0/(double)(1+hdunum));
+
+            ok(boolItem != NULL && boolItem->type == PS_DATA_BOOL,
+               "Retrieved psBool metadata item from file");
+
+            ok(strItem != NULL && strncmp(strItem,extname,strlen(extname)) == 0,
+               "Retrieved string metadata item from file.  Got '%s' vs '%s' (%d)",
+               strItem,extname,strlen(extname));
+
+            psFree(header);
+            psFree(header2);
+        }
+
+        // Following should be an error (input psFits = NULL)
+        // XXX: Verify error
+        psMetadata* header = psFitsReadHeader(NULL,NULL);
+        ok(header == NULL && psErrorGetStackSize() == 1,
+           "psFitsReadHeader didn't error on a NULL psFits");
+
+        psFree(fits);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // tst_psFitsReadHeaderSet()
+    {
+        psMemId id = psMemGetId();
+        ok(makeMulti(), "Created 'multi' FITS file");
+
+        psFits* fits = psFitsOpen(multiFilename,"r");
+        ok(fits != NULL, "psFitsOpen returned non-NULL on existing file");
+
+        int numHDUs = psFitsGetSize(fits);
+        ok(numHDUs >= 8, "The 'multi' FITS file has multiple HDUs");
+
+        // move to the middle
+        psFitsMoveExtNum(fits,numHDUs/2, false);
+        psMetadata* headerSet = psFitsReadHeaderSet(NULL,fits);
+        ok(headerSet != NULL, "psFitsReadHeaderSet returned non-NULL");
+
+        ok(psFitsGetExtNum(fits) == numHDUs/2, "psFitsReadHeaderSet did not change the CHU");
+
+        char extname[80];
+        for (int i = 0; i < numHDUs; i++)
+        {
+            if (i == 0) {
+                snprintf(extname, 80, "PHU");
+            } else {
+                snprintf(extname, 80, "ext-%d", i);
+            }
+
+            psMetadata* header = psMetadataLookupPtr(NULL,headerSet, extname);
+
+            ok(header != NULL, "psFitsReadHeader returned non-NULL for HDU#%d", i);
+
+            psS32 intItem = psMetadataLookupS32(NULL, header, "MYINT");
+            ok(intItem == i, "psFitsReadHeader for HDU#%d had a MYINT of %d, expected %d",
+               intItem, i);
+        }
+
+        psMetadata* set3 = psFitsReadHeaderSet(NULL,fits);
+        ok(set3 != NULL, "psFitsReadHeaderSet returned non-NULL");
+
+        for (int i = 0; i < numHDUs; i++)
+        {
+            if (i == 0) {
+                snprintf(extname, 80, "PHU");
+            } else {
+                snprintf(extname, 80, "ext-%d", i);
+            }
+
+            psMetadata* header = psMetadataLookupPtr(NULL, set3, extname);
+            ok(header != NULL, "psFitsReadHeader returned non-NULL for HDU#%d", i);
+
+            psS32 intItem = psMetadataLookupS32(NULL, header, "MYINT");
+            ok(intItem == i, "psFitsReadHeader for HDU#%d had a MYINT of %d, expected %d",
+               intItem, i);
+        }
+
+        set3 = psFitsReadHeaderSet(set3,NULL);
+        ok(set3 == NULL, "psFitsReadHeaderSet returned NULL given a NULL psFits");
+
+
+        psFree(headerSet);
+        psFitsClose(fits);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // tst_psFitsReadTable()
+    if (1) {
+        psMemId id = psMemGetId();
+        ok(makeTable(), "Created 'table' FITS file");
+        psFits* fits = psFitsOpen(tableFilename, "r");
+        ok(fits != NULL, "psFitsOpen returned non-NULL on existing file");
+
+        psFitsMoveExtNum(fits,1,false);
+        psArray* table = psFitsReadTable(fits);
+        ok(table != NULL, "psFitsReadTable returned non-NULL");
+        ok(table->n == tableNumRows, "Expected %d rows, read %d",
+           tableNumRows, table->n);
+
+        for (int row = 0; row < table->n; row++) {
+            psMetadata* rowData = table->data[row];
+
+            psS32 intItem = psMetadataLookupS32(NULL, rowData, "MYINT");
+            psF32 fltItem = psMetadataLookupF32(NULL, rowData, "MYFLT");
+            psF64 dblItem = psMetadataLookupF64(NULL, rowData, "MYDBL");
+            psBool boolItem = psMetadataLookupBool(NULL, rowData, "MYBOOL");
+            psString strItem = psMetadataLookupStr(NULL, rowData, "MYSTR");
+            psVector* vecItem = psMetadataLookupPtr(NULL, rowData, "MYVEC");
+
+            ok(intItem == row,
+               "Failed to retrieve psS32 metadata item from file (row=%d).  Got %d vs %d",
+               row, intItem, row);
+
+            ok(fabsf(fltItem - 1.0f/(float)(1+row)) <= FLT_EPSILON,
+               "Retrieved psF32 metadata item from file (row=%d).  Got %f vs %f",
+               row, fltItem,1.0f/(float)(1+row));
+
+            ok(abs(dblItem - 1.0/(double)(1+row)) <= DBL_EPSILON,
+               "Retrieved psF64 metadata item from file (row=%d).  Got %g vs %g",
+               row, dblItem, 1.0/(double)(1+row));
+
+            ok(!(boolItem != ((row&0x01) == 0)),
+               "Retrieved psBool metadata item from file (row=%d). Got %d vs %d",
+               row, boolItem, ((row&0x01) == 0));
+
+            char strValue[16];
+            snprintf(strValue,16,"row=%d",row+1);
+            ok(strncmp(strItem,strValue,strlen(strValue)) == 0,
+               "Retrieved psString metadata item from file (row=%d). Got '%s' vs '%s'",
+               row, strItem, strValue);
+
+            ok(vecItem != NULL, "Retrieved psVector metadata item from file (row=%d)", row);
+
+            ok(vecItem->type.type == PS_DATA_S32 &&
+               vecItem->data.S32[0] == row &&
+               vecItem->data.S32[3] == row+30,
+               "Retrieved psVector (row=%d) had expected values [%d,%d,%d,%d] vs [%d,%d,%d,%d]",
+               row,vecItem->data.S32[0], vecItem->data.S32[1],
+               vecItem->data.S32[2], vecItem->data.S32[3],
+               row,row+10,row+20,row+30);
+        }
+
+        psFree(table);
+        psFree(fits);
+
+        psArray* nullTest = psFitsReadTable(NULL);
+        ok(nullTest == NULL && psErrorGetStackSize() == 1,
+           "psFitsReadTable returned NULL when given NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // tst_psFitsReadTableColumnNum()
+    if (1) {
+        psMemId id = psMemGetId();
+        ok(makeTable(), "Created 'table' FITS file");
+        psFits* fits = psFitsOpen(tableFilename,"r");
+        ok(fits != NULL, "psFitsOpen returned non-NULL on existing file");
+
+        psFitsMoveExtNum(fits,1,false);
+        // read the column data via number
+        psVector* colData;
+        psElemType type[4] = {PS_TYPE_S32, PS_TYPE_F32, PS_TYPE_F32, PS_TYPE_BOOL};
+        psElemType altType[4] = {PS_TYPE_S64, PS_TYPE_F64, PS_TYPE_F64, PS_TYPE_BOOL};
+        char* colname[4] = {"MYINT","MYFLT","MYDBL","MYBOOL"};
+        psF64 expectedValues[4][10] = {
+                                          {0,1,2,3,4,5,6,7,8,9},
+                                          {1.0,1.0/2.0,1.0/3.0,1.0/4.0,1.0/5.0,1.0/6.0,1.0/7.0,1.0/8.0,1.0/9.0},
+                                          {1.0,1.0/2.0,1.0/3.0,1.0/4.0,1.0/5.0,1.0/6.0,1.0/7.0,1.0/8.0,1.0/9.0},
+                                          {1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0}
+                                      };
+
+        for (int col = 0; col < 4; col++) {
+            colData = psFitsReadTableColumnNum(fits,colname[col]);
+            ok(colData != NULL, "psFitsReadTableColumnNum returned non-NULL for col=%d");
+
+            if (colData->type.type != type[col] &&
+                    colData->type.type != altType[col]) {
+                char* typeRead;
+                char* typeExpected;
+                PS_TYPE_NAME(typeRead, colData->type.type);
+                PS_TYPE_NAME(typeExpected, type[col]);
+
+                ok(false, "psFitsReadTableColumnNum returned different type, %s vs %s, for col=%d",
+                   typeRead, typeExpected, col);
+            } else {
+                ok(true, "psFitsReadTableColumnNum returned same type");
+            }
+            ok(colData->n == tableNumRows,
+               "psFitsReadTableColumnNum returned same number of rows, %d vs %d, for col=%d",
+               colData->n, tableNumRows, col);
+            for (int row = 0; row < tableNumRows; row++) {
+                ok(abs(p_psVectorGetElementF64(colData,row) - expectedValues[col][row]) <= FLT_EPSILON,
+                   "psFitsReadTableColumnNum returned values (%g vs %g) for col=%d",
+                   p_psVectorGetElementF64(colData,row), expectedValues[col][row], col);
+            }
+            psFree(colData);
+        }
+
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        psVector* data = psFitsReadTableColumnNum(NULL,colname[0]);
+        psErr* err = psErrorLast();
+        ok(data == NULL, "psFitsReadTableColumnNum did return NULL with NULL psFits");
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL,
+           "psFitsReadTableColumnNum did return error with NULL psFits");
+        psFree(err);
+
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        data = psFitsReadTableColumnNum(fits,"BOGUS");
+        err = psErrorLast();
+        ok(data == NULL, "psFitsReadTableColumnNum did return NULL with bogus column name");
+        ok(err->code == PS_ERR_IO, "psFitsReadTableColumnNum did return error with bogus column name");
+        psFree(err);
+        psFree(fits);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // tst_psFitsReadTableColumn()
+    if (1) {
+        psMemId id = psMemGetId();
+        ok(makeTable(), "Created 'table' FITS file");
+        psFits* fits = psFitsOpen(tableFilename,"r");
+        ok(fits != NULL, "psFitsOpen returned non-NULL on existing file");
+
+        psFitsMoveExtNum(fits,1,false);
+        // read the column data via number
+        psArray* colData;
+        char* colname[4] = {"MYINT","MYFLT","MYDBL","MYBOOL"};
+        psF64 expectedValues[3][10] = {
+                                          {0,1,2,3,4,5,6,7,8,9},
+                                          {1.0,1.0/2.0,1.0/3.0,1.0/4.0,1.0/5.0,1.0/6.0,1.0/7.0,1.0/8.0,1.0/9.0},
+                                          {1.0,1.0/2.0,1.0/3.0,1.0/4.0,1.0/5.0,1.0/6.0,1.0/7.0,1.0/8.0,1.0/9.0}
+                                      };
+        char* expectedBoolValues[10] = {"T","F","T","F","T","F","T","F","T","F"};
+        char* expectedStrValues[10] = {"row=1","row=2","row=3","row=4","row=5","row=6","row=7","row=8","row=9","row=10"};
+
+        bool errorFlag = false;
+        for (int col = 0; col < 4; col++) {
+            colData = psFitsReadTableColumn(fits,colname[col]);
+            if (colData == NULL) {
+                diag("psFitsReadTableColumn returned NULL for col=%d", col);
+                errorFlag = true;
+            }
+            if (colData->n != tableNumRows) {
+                diag("psFitsReadTableColumn returned different number of rows, %d vs %d, for col=%d",
+                     colData->n, tableNumRows, col);
+                errorFlag = true;
+            }
+            if (col < 3) {
+                for (int row = 0; row < tableNumRows; row++) {
+                    if (abs(atof((char*)colData->data[row]) - expectedValues[col][row]) > 0.0001) {
+                        diag("psFitsReadTableColumn returned unexpected values (%g vs %g) for col=%d",
+                             atof((char*)colData->data[row]), expectedValues[col][row], col);
+                        errorFlag = true;
+                    }
+                }
+            } else if (col == 3) {
+                for (int row = 0; row < tableNumRows; row++) {
+                    if (strncmp(colData->data[row],expectedBoolValues[row],
+                                strlen(expectedBoolValues[row])) != 0) {
+                        diag("psFitsReadTableColumn returned unexpected values ('%s' vs '%s') for col=%d",
+                             (char*)colData->data[row], expectedBoolValues[row], col);
+                        errorFlag = true;
+                    }
+                }
+            } else if (col == 4) {
+                for (int row = 0; row < tableNumRows; row++) {
+                    if (strncmp(colData->data[row],expectedStrValues[row],
+                                strlen(expectedStrValues[row])) != 0) {
+                        diag("psFitsReadTableColumn returned unexpected values ('%s' vs '%s') for col=%d",
+                             (char*)colData->data[row], expectedStrValues[row], col);
+                        errorFlag = true;
+                    }
+                }
+            }
+            psFree(colData);
+        }
+        ok(!errorFlag, "psFitsReadTableColumn() produced the correct data");
+
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        psArray* data = psFitsReadTableColumn(NULL,"MYINT");
+        psErr* err = psErrorLast();
+        ok(data == NULL, "psFitsReadTableColumn did return NULL with NULL psFits");
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL,
+           "psFitsReadTableColumn did error with NULL psFits");
+        psFree(err);
+
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        data = psFitsReadTableColumn(fits,"BOGUS");
+        err = psErrorLast();
+        ok(data == NULL, "psFitsReadTableColumn did return NULL with col=\"BOGUS\"");
+        ok(err->code == PS_ERR_IO, "psFitsReadTableColumn did error with col=\"BOGUS\"");
+        psFree(err);
+
+        psFree(fits);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // tst_psFitsUpdateTable()
+    if (1) {
+        psMemId id = psMemGetId();
+        psErr* err;
+        char* strValue[] = {
+                               "row A",
+                               "row B",
+                               "row C",
+                               "row D",
+                               "row E",
+                               "row F",
+                               "row G",
+                               "row H",
+                               "row I",
+                               "row J",
+                               "row K"
+                           };
+        ok(makeTable(), "Created 'table' FITS file");
+        psFits* fits = psFitsOpen(tableFilename,"rw");
+        ok(fits != NULL, "psFitsOpen returned NULL on existing file");
+        psFitsMoveExtNum(fits,1,false);
+        // change the data in the file, going past by one (implicit new row of data)
+        bool errorFlag = false;
+        for (int row = 0; row < tableNumRows+1; row++) {
+            psMetadata* md = psMetadataAlloc();
+            psMetadataAddF32(md,PS_LIST_TAIL,"MYFLT", 0,"",(float)row/-10.0);
+            psMetadataAddF64(md,PS_LIST_TAIL,"MYDBL", 0,"",(double)row/-100.0);
+            psMetadataAddS32(md,PS_LIST_TAIL,"MYINT", 0,"",-row);
+            psMetadataAddBool(md,PS_LIST_TAIL,"MYBOOL", 0,"",((row & 1) == 1));
+            psMetadataAddStr(md,PS_LIST_TAIL,"MYSTR", 0,"",strValue[row]);
+
+            if (!psFitsUpdateTable(fits,md,row)) {
+                diag("psFitsUpdateTable returned false, but expected true for row=%d", row);
+                errorFlag = true;
+            }
+            psFree(md);
+        }
+        ok(!errorFlag, "psFitsUpdateTable() produced the correct data");
+
+        for (int row = 0; row < tableNumRows+1; row++) {
+            psMetadata* md = psFitsReadTableRow(fits, row);
+            if (abs(psMetadataLookupF32(NULL,md,"MYFLT") - (float)row/-10.0) > FLT_EPSILON) {
+                diag("psFitsUpdateTable did not change the float value for row=%d", row);
+                errorFlag = true;
+            }
+            if (abs(psMetadataLookupF64(NULL,md,"MYDBL") - (float)row/-100.0) > FLT_EPSILON) {
+                diag("psFitsUpdateTable did not change the double value for row=%d", row);
+                errorFlag = true;
+            }
+            if (psMetadataLookupS32(NULL,md,"MYINT") != -row) {
+                diag("psFitsUpdateTable did not change the integer value for row=%d", row);
+                errorFlag = true;
+            }
+            if (psMetadataLookupBool(NULL,md,"MYBOOL") != ((row &1) == 1)) {
+                diag("psFitsUpdateTable did not change the boolean value for row=%d", row);
+                errorFlag = true;
+            }
+            psString mystr = psMetadataLookupStr(NULL,md,"MYSTR");
+            if (strncmp(mystr,strValue[row], strlen(strValue[row])) != 0) {
+                diag("psFitsUpdateTable did not change the string value for row=%d", row);
+                errorFlag = true;
+            }
+            psFree(md);
+        }
+        ok(!errorFlag, "psFitsUpdateTable() produced the correct data");
+
+        psMetadata* md = psMetadataAlloc();
+        psMetadataAddF32(md,PS_LIST_TAIL,"BOGUS", 0,"",-1.0f);
+        // Following should be a warning
+        // XXX: Verify warning
+        psErrorClear();
+        ok(psFitsUpdateTable(fits,md,0), "psFitsUpdateTable did return false with bogus column data");
+        psFree(md);
+
+        md = psMetadataAlloc();
+        psMetadataAddF32(md,PS_LIST_TAIL,"MYFLT", 0,"",-1.0f);
+        psMetadataAddF64(md,PS_LIST_TAIL,"MYDBL", 0,"",-2.0);
+        psMetadataAddS32(md,PS_LIST_TAIL,"MYINT", 0,"",-3);
+
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        ok(psFitsUpdateTable(NULL,md,0), "psFitsUpdateTable did return false with NULL psFits");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL, "psFitsUpdateTable did error with NULL psFits");
+        psFree(err);
+
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        ok(psFitsUpdateTable(fits,NULL,0), "psFitsUpdateTable did return false with NULL psMetadata");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL, "psFitsUpdateTable did error with NULL psMetadata");
+        psFree(err);
+
+        // Following should be an error
+        // XXX: Verify error
+        psErrorClear();
+        ok(psFitsUpdateTable(fits,md,-1), "psFitsUpdateTable did return false with row=-1");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_IO, "psFitsUpdateTable did error with row=-1");
+
+        psFree(err);
+        psFree(md);
+        psFitsClose(fits);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageRead()
+    {
+        psS32 N = 256;
+        psS32 M = 128;
+
+        // This function shall open the specified FITS file, read the specified data
+        // and place the data into a psImage structure. This function shall generate
+        // an error message and return NULL if any of the input parameters are out of
+        // range, image file doesn't exist or image is zero or one dimensional.
+        // Verify the returned psImage structure contains expected values, if the input
+        // parameter filename specifies an available FITS file with known 2dimensional
+        // data, input parameters col, row, ncol, nrow, z specify data range with the
+        // FITS file. Cases should include 1x1, Nx1, 1xN, NxN and MxN sub images and
+        // total FITS file image. (done in macro)
+        // Verify the returned psImage structure is equal to the input parameter
+        // 'output', if specified. (done in macro)
+
+        #define testReadTypeSize(m, n, readM0, readN0, readM, readN, TYP, filename) \
+        { \
+            psMemId id = psMemGetId(); \
+            psImage* img = NULL; \
+            psImage* img2 = NULL; \
+            psImage* img3 = NULL; \
+            psImage* img4 = NULL; \
+            \
+            GENIMAGE(img,m,n,TYP,row+2*col); \
+            img2 = psImageCopy(img2,img,PS_TYPE_##TYP); \
+            GENIMAGE(img3,m,n,TYP,row+2*col); \
+            psImageClip(img3,32.0,32.0,120.0,120.0); \
+            img4 = psImageCopy(img4,img3,PS_TYPE_##TYP); \
+            psFits* fits = psFitsOpen(filename, "w"); \
+            ok(psFitsWriteImage(fits, NULL, img, 2, NULL), "Wrote test image %s",filename); \
+            ok(psFitsUpdateImage(fits,img3, 0,0, 1), "Wrote test image %s",filename); \
+            ok(psFitsWriteImage(fits,NULL, img3, 2, NULL), "Wrote test image %s",filename); \
+            ok(psFitsUpdateImage(fits,img, 0, 0, 1), "Wrote test image %s",filename); \
+            psFree(img); \
+            psFree(fits); \
+            img = NULL; \
+            psFree(img3); \
+            img3 = NULL; \
+            fits = psFitsOpen(filename,"r"); \
+            psRegion reg = {readM0, readM, readN0, readN}; \
+            img = psFitsReadImage(fits, reg, 0); \
+            img3 = psFitsReadImage(fits, reg, 1); \
+            ok(img3 != NULL, "Read test image %s",filename); \
+            bool errorFlag = false; \
+            for (psU32 row = readN0; row < readN; row++) { \
+                ps##TYP* imgRow = img->data.TYP[row-readN0]; \
+                ps##TYP* img2Row = img2->data.TYP[row]; \
+                ps##TYP* img3Row = img3->data.TYP[row-readN0]; \
+                ps##TYP* img4Row = img4->data.TYP[row]; \
+                for (psU32 col = readM0; col < readM; col++) { \
+                    if (fabsf(imgRow[col-readM0]-img2Row[col]) > FLT_EPSILON) { \
+                        diag("Image changed in I/O operation at %d,%d,0 (%.2f vs %.2f) for %s", \
+                             col,row,(psF32)imgRow[col-readM0],(psF32)img2Row[col],filename); \
+                        errorFlag = true; \
+                    } \
+                    if (fabsf(img3Row[col-readM0]-img4Row[col]) > FLT_EPSILON) { \
+                        diag("Image changed in I/O operation at %d,%d,1 (%.2f vs %.2f) for %s", \
+                             col,row,(psF32)img3Row[col-readM0],(psF32)img4Row[col],filename); \
+                        errorFlag = true; \
+                    } \
+                } \
+            } \
+            ok(!errorFlag, "psFitsReadImage() produced the correct data"); \
+            psFree(img); \
+            img = NULL; \
+            psFree(img3); \
+            img3 = NULL; \
+            psFitsMoveExtNum(fits,1, false); \
+            img3 = psFitsReadImage(fits, reg, 0); \
+            img = psFitsReadImage(fits, reg, 1); \
+            ok(img != NULL, "Read test image %s",filename); \
+            errorFlag = false; \
+            for (psU32 row = readN0; row < readN; row++) { \
+                ps##TYP* imgRow = img->data.TYP[row-readN0]; \
+                ps##TYP* img2Row = img2->data.TYP[row]; \
+                ps##TYP* img3Row = img3->data.TYP[row-readN0]; \
+                ps##TYP* img4Row = img4->data.TYP[row]; \
+                for (psU32 col = readM0; col < readM; col++) { \
+                    if (fabsf(imgRow[col-readM0]-img2Row[col]) > FLT_EPSILON) { \
+                        diag("Image changed in I/O operation at %d,%d,0 (%.2f vs %.2f) for %s", \
+                             col,row,(psF32)imgRow[col-readM0],(psF32)img2Row[col],filename); \
+                        errorFlag = true; \
+                    } \
+                    if (fabsf(img3Row[col-readM0]-img4Row[col]) > FLT_EPSILON) { \
+                        diag("Image changed in I/O operation at %d,%d,1 (%.2f vs %.2f) for %s", \
+                             col,row,(psF32)img3Row[col-readM0],(psF32)img4Row[col],filename); \
+                        errorFlag = true; \
+                    } \
+                } \
+            } \
+            ok(!errorFlag, "psFitsReadImage() produced the correct data"); \
+            psFree(img); \
+            psFree(img2); \
+            psFree(img3); \
+            psFree(img4); \
+            psFree(fits); \
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks"); \
+        }
+
+        #define testReadType(TYP,filename) \
+        testReadTypeSize(1,1,0,0,0,0,TYP,"tmpImages/1x1_" filename); \
+        testReadTypeSize(M,1,M/4,0,M*3/4,0,TYP,"tmpImages/Mx1_" filename); \
+        testReadTypeSize(1,N,0,N/4,0,N*3/4,TYP,"tmpImages/1xN_" filename); \
+        testReadTypeSize(M,N,M/4,N/4,M*3/4,N*3/4,TYP,"tmpImages/MxN_" filename);
+
+        system("mkdir tmpImages");
+
+        testReadType(U8,"U8.fits");
+        testReadType(S8,"S8.fits");   // Not a requirement
+        testReadType(S16,"S16.fits");
+        testReadType(U16,"U16.fits"); // Not a requirement
+        testReadType(S32,"S32.fits");
+        testReadType(U32,"U32.fits"); // Not a requirement
+        testReadType(F32,"F32.fits");
+        testReadType(F64,"F64.fits");
+
+        // Attempt to read from NULL fits object
+        // Following should generate error message for NULL psFits
+        // XXX: Verify error
+        // XXXX: This is seg-faulting
+        if (0)
+        {
+            psMemId id = psMemGetId();
+            psRegion region = {
+                                  0,0,0,0
+                              };
+            ok(psFitsReadImage(NULL,region,0) == NULL, "Did return NULL for NULL psFits");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+            \
+        }
+    }
+
+
+    // testImageWrite()
+    {
+        psMemId id = psMemGetId();
+        psImage* img = NULL;
+        psImage* img2 = NULL;
+        psS32 m = 64;
+        psS32 n = 96;
+
+        // This function shall write the specified section within a psImage structure
+        // to a FITS file. If the specifiedfile exists, then data should overwrite the
+        // section to write. If the specified file doesn't exist, it shall be created.
+        // If an extenstion is specified, then a basic primary header data unit shall
+        //  be created.
+        //
+        // Verify a FITS file named filename is generated and contains expected
+        // values, if the input parameter input contains known data values, input
+        // parameters col, row, ncol, nrow specify a valid data region within psImage
+        // structure.
+        //
+        // Verify a FITS file named filename is generated and contains a primary
+        // header data unit with extension with expected values, if the input
+        // parameter input contains known data values, input parameters col, row,
+        // ncol, nrow specify a valid data region within psImage structure and
+        // extname and/or extnum specify an extenstion to write.
+        //
+        // N.B. : these are done in testImageRead tests, see above.
+        //
+        // Verify a FITS file named filename is overwritten and contains
+        // expected values, if the input parameter input contains known data values,
+        // input parameters col, row, ncol, nrow specify a valid data region within
+        // psImage structure.
+
+        GENIMAGE(img,m,n,F32,0);
+        GENIMAGE(img2,m,n,F32,row+2*col);
+        system("mkdir tmpImages");
+        psFits* fits = psFitsOpen("tmpImages/writeTest.fits","w");
+
+        ok(psFitsWriteImage(fits, NULL, img,1, NULL), "Wrote writeTest.fits");
+        ok(psFitsUpdateImage(fits, img2, 0, 0, 0), "Updated writeTest.fits");
+        psFree(img);
+        psFree(img2);
+
+        // Did it really overwrite the pixel values?  Let's read it in and see.
+        psFitsClose(fits);
+
+        psRegion region = {0,0,0,0};
+        fits = psFitsOpen("tmpImages/writeTest.fits","r");
+        img = NULL;
+        img = psFitsReadImage(fits, region, 0);
+        ok(img != NULL, "Read in writeTest.fits");
+        bool errorFlag = false;
+        for (psU32 row=0;row<n;row++)
+        {
+            psF32* imgRow = img->data.F32[row];
+            for (psU32 col=0;col<m;col++) {
+                if (fabsf(imgRow[col] - (row+2*col)) > FLT_EPSILON) {
+                    diag("The image values were not overwritten at %d,%d (%.2f vs %.2f)",
+                         col,row,imgRow[col],(row+2*col));
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psFitsReadImage() produced the correct data");
+
+        psFree(img);
+
+        // Verify false is returned and program execution is not stopped, if the input image
+        // is null.
+        // Following should generate an error message because input image is null
+        // XXX: Verify error
+        ok(!psFitsWriteImage(fits,NULL,NULL, 1, NULL),
+           "psImageWriteSection did return false when input image is NULL");
+        psFree(fits);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // tst_psFitsWriteHeader()
+    {
+        psMemId id = psMemGetId();
+        ok(makeMulti(), "Created 'multi' FITS file");
+
+        psMetadata* header   = psMetadataAlloc();
+        psFits*     fitsFile = psFitsOpen(multiFilename,"a+");
+
+        // Test psFitsReadWrite generates files from psFitsWriteImage which calls psFitsWriteHeader
+        // so these additional tests check for error conditions
+        // Attempt call function with NULL metadata
+        ok(!psFitsWriteHeader(fitsFile, NULL), "Expected return of true for NULL metadata pointer");
+        psFree(fitsFile);
+
+        // Attempt to call function with NULL fits
+        ok(!psFitsWriteHeader(NULL, header), "Expected return of true for NULL fits file pointer");
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fits/tap_psFitsBlank_00.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fits/tap_psFitsBlank_00.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fits/tap_psFitsBlank_00.c	(revision 22322)
@@ -0,0 +1,82 @@
+#include <pslib.h>
+#include <string.h>
+#include <unistd.h>     // needed for unlink()
+
+#include "tap.h"
+#include "pstap.h"
+
+int main(void)
+
+{
+    plan_tests(15);
+
+    psArray *table = psArrayAllocEmpty (10);
+    for (int i = 0; i < table->nalloc; i++) {
+        psMetadata *row = psMetadataAlloc ();
+        psMetadataAdd (row, PS_LIST_TAIL, "X_PIX",   PS_DATA_F32, "", 2.0*i);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_PIX",   PS_DATA_F32, "", 4.0*i);
+        psArrayAdd (table, 10, row);
+    }
+
+    psImage *image = psImageAlloc (512, 512, PS_DATA_F32);
+
+    psFits *fits = psFitsOpen ("test.fits", "w");
+
+    psMetadata *header = psMetadataAlloc ();
+
+    for (int i = 0; i < 5; i++) {
+        char name[80];
+
+        sprintf (name, "blank.%02d", i);
+        psMetadataAddStr (header, PS_LIST_TAIL, "TESTNAME", PS_META_REPLACE, "test name", name);
+        // psFitsMoveLast (fits);
+        psFitsWriteBlank (fits, header, "testext");
+
+        sprintf (name, "image.%02d", i);
+        psMetadataAddStr (header, PS_LIST_TAIL, "TESTNAME", PS_META_REPLACE, "test name", name);
+        psFitsWriteImage (fits, header, image, 1, "testext");
+
+        sprintf (name, "table.%02d", i);
+        psMetadataAddStr (header, PS_LIST_TAIL, "TESTNAME", PS_META_REPLACE, "test name", name);
+        psMetadataAddStr (header, PS_LIST_TAIL, "EXTTYPE", PS_META_REPLACE, "type", "SMPDATA");
+        psMetadataAddStr (header, PS_LIST_TAIL, "EXTHEAD", PS_META_REPLACE, "head", "image.00");
+        psMetadataAddStr (header, PS_LIST_TAIL, "EXTDATA", PS_META_REPLACE, "data", "blank.00");
+        psFitsWriteTable (fits, header, table, "testext");
+    }
+
+    psFitsClose (fits);
+
+    fits = psFitsOpen ("test.fits", "r");
+    for (int i = 0; i < 5; i++) {
+        char *name, expect[80];
+
+        psMetadata *outhead = psMetadataAlloc ();
+
+        fprintf (stderr, "top: %d\n", psFitsGetExtNum (fits));
+
+        psFitsReadHeader (outhead, fits);
+        sprintf (expect, "blank.%02d", i);
+        name = psMetadataLookupStr (NULL, outhead, "TESTNAME");
+        ok (!strcmp(name, expect), "got TESTNAME = %s, expected %s", name, expect);
+        psFitsMoveExtNum (fits, 1, true);
+        fprintf (stderr, "curr: %d\n", psFitsGetExtNum (fits));
+
+        psFitsReadHeader (outhead, fits);
+        sprintf (expect, "image.%02d", i);
+        name = psMetadataLookupStr (NULL, outhead, "TESTNAME");
+        ok (!strcmp(name, expect), "got TESTNAME = %s, expected %s", name, expect);
+        psFitsMoveExtNum (fits, 1, true);
+        fprintf (stderr, "curr: %d\n", psFitsGetExtNum (fits));
+
+        psFitsReadHeader (outhead, fits);
+        sprintf (expect, "table.%02d", i);
+        name = psMetadataLookupStr (NULL, outhead, "TESTNAME");
+        ok (!strcmp(name, expect), "got TESTNAME = %s, expected %s", name, expect);
+        psFitsMoveExtNum (fits, 1, true);
+        fprintf (stderr, "curr: %d\n", psFitsGetExtNum (fits));
+    }
+    psFitsClose (fits);
+    unlink ("test.fits");
+
+    exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fits/tap_psFitsImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fits/tap_psFitsImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fits/tap_psFitsImage.c	(revision 22322)
@@ -0,0 +1,96 @@
+#include <stdio.h>
+#include "tap.h"
+#include "pstap.h"
+#include <pslib.h>
+
+#define NUMCOLS 12                      // Number of columns for image
+#define NUMROWS 34                      // Number of rows for image
+#define BITPIX 32                       // Bits per pixel
+#define OUTTYPE PS_TYPE_F64             // Expected output type
+
+// Generate an image
+static psImage *generateImage(void)
+{
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 12345); // Random number generator
+    psImage *image = psImageAlloc(NUMCOLS, NUMROWS, PS_TYPE_F32); // Generated image
+    for (int y = 0; y < NUMROWS; y++) {
+        for (int x = 0; x < NUMCOLS; x++) {
+            image->data.F32[y][x] = ((x + y) % 2) ? NAN : psRandomUniform(rng);
+        }
+    }
+    psFree(rng);
+    return image;
+}
+
+
+int main(int argc, const char *argv[])
+{
+    plan_tests(7);
+
+    {
+        psMemId id = psMemGetId();
+
+        psString name = NULL;           // Name of FITS image
+        psStringAppend(&name, "%s.fits", argv[0]);
+
+        psImage *original = generateImage(); // Image to test
+        psMetadata *header = psMetadataAlloc(); // FITS header
+
+        psFits *fits = psFitsOpen(name, "w");
+
+        // Set compression
+        psVector *tiles = psVectorAlloc(2, PS_TYPE_S32);
+        tiles->data.S32[0] = 0;
+        tiles->data.S32[1] = 1;
+        ok(psFitsSetCompression(fits, PS_FITS_COMPRESS_RICE, tiles, 4, 0, 0), "Set compression");
+        psFree(tiles);
+
+        // Set quantisation
+        fits->options = psFitsOptionsAlloc();
+        fits->options->bitpix = BITPIX;
+        fits->options->scaling = PS_FITS_SCALE_STDEV_BOTH;
+        fits->options->stdevBits = 24;
+
+        bool write = psFitsWriteImage(fits, header, original, 0, NULL);
+        psFitsClose(fits);
+
+        ok(write, "Write image");
+        skip_start(!fits || !write, 4, "Unable to write image");
+        {
+            psFits *fits = psFitsOpen(name, "r");
+            psImage *image = psFitsReadImage(fits, psRegionSet(0,0,0,0), 0);
+            psFitsClose(fits);
+
+            ok(image, "Read image");
+            skip_start(!image, 3, "Unable to read image");
+            {
+                ok(image->type.type == OUTTYPE, "Image type");
+                ok(image->numCols == NUMCOLS && image->numRows == NUMROWS, "Image size");
+                bool consistent = true; // Images are consistent?
+                double tol = 1.0 / (double)(1 << (BITPIX - 1)) + FLT_EPSILON; // Expected tolerance
+                for (int y = 0; y < NUMROWS; y++) {
+                    for (int x = 0; x < NUMCOLS; x++) {
+                        double imageVal = psImageGet(image, x, y);
+                        if ((!isfinite(original->data.F32[y][x]) && isfinite(imageVal)) ||
+                            (isfinite(original->data.F32[y][x]) && !isfinite(imageVal) &&
+                             original->data.F32[y][x] < 1.0 - 0.5 * tol) ||
+                            (fabs(original->data.F32[y][x] - imageVal) > tol)) {
+                            consistent = false;
+                            diag("Inconsistent at %d,%d: %lf vs %f --> %le", x, y,
+                                 imageVal, original->data.F32[y][x], imageVal - original->data.F32[y][x]);
+                        }
+                    }
+                }
+                ok(consistent, "Images consistent.");
+                psFree(image);
+            }
+            skip_end();
+        }
+        skip_end();
+        psFree(original);
+        psFree(header);
+        psFree(name);
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fits/tst_psFits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fits/tst_psFits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fits/tst_psFits.c	(revision 22322)
@@ -0,0 +1,1665 @@
+/** @file  tst_psFits.c
+*
+*  @brief Contains the tests for psFits.[ch]
+*
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.25 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-07-26 03:37:04 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+#define GENIMAGE(img,c,r,TYP, valueFcn) \
+img = psImageAlloc(c,r,PS_TYPE_##TYP); \
+for (psU32 row=0;row<r;row++) { \
+    ps##TYP* imgRow = img->data.TYP[row]; \
+    for (psU32 col=0;col<c;col++) { \
+        imgRow[col] = (ps##TYP)(valueFcn); \
+    } \
+}
+
+static bool makeMulti(void);  // implicitly tests psFitsSetExtName
+static bool makeTable(void);
+const char* multiFilename = "multi.fits";
+const char* tableFilename = "table.fits";
+const int tableNumRows = 10;
+
+
+// N.B., the tests to Image read/write was liberally taken from the now
+// deprecated psImageReadSection/psImageWriteSection function tests.
+static psS32 testImageRead(void);
+static psS32 testImageWrite(void);
+
+static psS32 tst_psFitsOpen( void );
+static psS32 tst_psFitsMoveExtName( void ); // also tests psFitsGetExtName
+static psS32 tst_psFitsMoveExtNum( void );  // also tests psFitsGetExtNum, psFitsGetSize
+static psS32 tst_psFitsReadHeader( void );
+static psS32 tst_psFitsReadHeaderSet( void );
+static psS32 tst_psFitsReadTable( void );
+static psS32 tst_psFitsReadTableColumnNum(void);
+static psS32 tst_psFitsReadTableColumn(void);
+static psS32 tst_psFitsUpdateTable(void);
+static psS32 tst_psFitsWriteHeader(void);
+
+testDescription tests[] = {
+                              {tst_psFitsOpen, 801, "psFitsOpen", 0, false},
+                              {tst_psFitsMoveExtName, 802, "psFitsMoveExtName", 0, false},
+                              {tst_psFitsMoveExtName, 802, "psFitsGetExtName", 0, true},
+                              {tst_psFitsMoveExtNum, 803, "psFitsMoveExtNum", 0, false},
+                              {tst_psFitsMoveExtNum, 803, "psFitsGetExtNum", 0, true},
+                              {tst_psFitsMoveExtNum, 803, "psFitsGetSize", 0, true},
+                              {tst_psFitsReadHeader, 804, "psFitsReadHeader", 0, false},
+                              {tst_psFitsReadHeaderSet,805, "psFitsReadHeaderSet", 0, false},
+                              {tst_psFitsReadTable,809, "psFitsReadTable", 0, false},
+                              {tst_psFitsReadTableColumnNum,836, "psFitsReadTableColumnNum", 0, false},
+                              {tst_psFitsReadTableColumn,839, "psFitsReadTableColumn", 0, false},
+                              {tst_psFitsUpdateTable,840, "psFitsUpdateTable", 0, false},
+                              {testImageRead,567, "psFitsReadImage", 0, false},
+                              {testImageWrite,569, "psFitsWriteImage", 0, false},
+                              {tst_psFitsWriteHeader,000,"psFitsWriteHeader",0,false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psFits", tests, argc, argv ) );
+}
+
+bool makeMulti(void)
+{
+    psFits* fitsFile = psFitsOpen(multiFilename,"w");
+
+    if (fitsFile == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Could not create 'multi' FITS file.");
+        return false;
+    }
+
+    psImage* image = psImageAlloc(16,16,PS_TYPE_F32);
+
+    char extname[80];
+    for (int lcv = 0; lcv < 8; lcv++) {
+        snprintf(extname,80,"ext-%d", lcv);
+
+        psMetadata* header = psMetadataAlloc();
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYINT",
+                      PS_DATA_S32,
+                      "psS32 Item", (psS32)lcv);
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYFLT",
+                      PS_DATA_F32,
+                      "psF32 Item", (float)(1.0f/(float)(1+lcv)));
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYDBL",
+                      PS_DATA_F64,
+                      "psF64 Item", (double)(1.0/(double)(1+lcv)));
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYBOOL",
+                      PS_DATA_BOOL,
+                      "psBool Item",
+                      (lcv%2 == 0));
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYSTR",
+                      PS_DATA_STRING,
+                      "String Item",
+                      extname);
+
+        // set the pixels in the image
+        psImageInit(image, (float)lcv);
+        if (! psFitsWriteImage(fitsFile,header,image,0,extname) ) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Could not write image.");
+            return false;
+        }
+
+        psFree(header);
+    }
+    psFree(image);
+    psFree(fitsFile);
+
+    return true;
+}
+
+bool makeTable(void)
+{
+    psFits* fitsFile = psFitsOpen(tableFilename,"w");
+
+    if (fitsFile == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Could not create 'table' FITS file.");
+        return false;
+    }
+
+    // make the PHU an image (per FITS standard, it must be)
+    psImage* image = psImageAlloc(16,16,PS_TYPE_F32);
+
+    if (! psFitsWriteImage(fitsFile,NULL,image,1,NULL) ) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Could not write PHU image.");
+        return false;
+    }
+
+    psFree(image);
+
+    // build a table structure
+    psArray* table = psArrayAlloc(tableNumRows);
+    //    table->n = tableNumRows;
+    psMetadata* header = NULL;
+    for (int row = 0; row < tableNumRows; row++) {
+        header = psMetadataAlloc();
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYINT",
+                      PS_DATA_S32,
+                      "psS32 Item",
+                      (psS32)row);
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYFLT",
+                      PS_DATA_F32,
+                      "psF32 Item",
+                      (float)(1.0f/(float)(1+row)));
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYDBL",
+                      PS_DATA_F64,
+                      "psF64 Item",
+                      (double)(1.0/(double)(1+row)));
+
+        psMetadataAdd(header,PS_LIST_TAIL, "MYBOOL",
+                      PS_DATA_BOOL,
+                      "psBool Item",
+                      (row%2 == 0));
+
+        char* str = NULL;
+        psStringAppend(&str,"row=%d",row+1);
+        psMetadataAdd(header,PS_LIST_TAIL, "MYSTR",
+                      PS_DATA_STRING,
+                      "psString Item",
+                      str);
+        psFree(str);
+
+        psVector* vec = psVectorAlloc(5,PS_TYPE_S32);
+        for (int x=0; x < 4; x++) {
+            vec->data.S32[x] = x*10+row;
+            vec->n++;
+        }
+        psMetadataAdd(header,PS_LIST_TAIL, "MYVEC",
+                      PS_DATA_VECTOR,
+                      "psVector Item",
+                      vec);
+        psFree(vec);
+
+        table->data[row] = header;
+        table->n++;
+    }
+
+    psFitsWriteTable(fitsFile, NULL, table, NULL);
+
+    psFree(table);
+    psFree(fitsFile);
+    //    return 1;
+    return (!psMemCheckLeaks(15,NULL,stderr,false));
+}
+
+psS32 tst_psFitsOpen( void )
+{
+
+    if (! makeMulti() ) {
+        return 1;
+    }
+
+    psFits* fitsFile = psFitsOpen(multiFilename,"r");
+
+    if (fitsFile == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on existing file.");
+        return 1;
+    }
+
+    int extNum = psFitsGetExtNum(fitsFile);
+    if (extNum != 0) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen was not queued to the PHU, but to extension #%d.",
+                extNum);
+        return 2;
+    }
+
+    psFitsClose(fitsFile);
+
+    // make sure the file doesn't already exist.
+    if (access("new.fits", F_OK) == 0) {
+        if (remove
+                ("new.fits") != 0) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Couldn't delete the new.fits file.");
+            return 3;
+        }
+    }
+
+    fitsFile = psFitsOpen("new.fits","w");
+
+    if (fitsFile == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on w mode.");
+        return 4;
+    }
+
+    // write something to the file, otherwise CFITSIO will complain on close
+    psImage* img = psImageAlloc(16,16,PS_TYPE_F32);
+    psFitsWriteImage(fitsFile,NULL,img,1,NULL);
+
+    psFree(fitsFile); // psFree should be equivalent to psFitsClose
+
+    // now, if psFitsOpen actually created the file, I shouldn't error in removing it.
+    if (remove
+            ("new.fits") != 0) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen seemed to not have created a new file.");
+        return 5;
+    }
+
+    fitsFile = psFitsOpen("new.fits","w+");
+
+    if (fitsFile == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on w+ mode.");
+        return 6;
+    }
+
+    // write something to the file, otherwise CFITSIO will complain on close
+    psFitsWriteImage(fitsFile,NULL,img,1,NULL);
+
+    psFitsClose(fitsFile);
+
+    // now, if psFitsOpen actually created the file, I shouldn't error in removing it.
+    if (remove
+            ("new.fits") != 0) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen seemed to not have created a new file.");
+        return 7;
+    }
+
+    fitsFile = psFitsOpen("new.fits","a");
+
+    if (fitsFile == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on a mode.");
+        return 8;
+    }
+
+    // write something to the file, otherwise CFITSIO will complain on close
+    psFitsWriteImage(fitsFile,NULL,img,1,NULL);
+
+    psFitsClose(fitsFile);
+
+    fitsFile = psFitsOpen("new.fits","a+");
+
+    if (fitsFile == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on a+ mode.");
+        return 9;
+    }
+
+    psFitsClose(fitsFile);
+
+    // now, if psFitsOpen actually created the file, I shouldn't error in removing it.
+    if (remove
+            ("new.fits") != 0) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen seemed to not have created a new file.");
+        return 10;
+    }
+
+    // Attempt to allocate with NULL filename
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    fitsFile = psFitsOpen(NULL,"r");
+    if(fitsFile != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psFitsOpen did not return NULL for NULL input");
+        return 11;
+    }
+
+    // Attempt to use an invalid mode
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    fitsFile = psFitsOpen("new.fits","b+");
+    if(fitsFile != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psFitsOpen did not return NULL for NULL input");
+        return 12;
+    }
+
+    psFree(img);
+
+    return 0;
+}
+
+psS32 tst_psFitsMoveExtName( void )
+{
+
+    if (! makeMulti() ) {
+        return 1;
+    }
+
+    psFits* fits = psFitsOpen(multiFilename,"r");
+
+    if (fits == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on existing file.");
+        return 1;
+    }
+
+    int numHDUs = psFitsGetSize(fits);
+
+    if (numHDUs < 2) {
+        psError(PS_ERR_UNKNOWN,true,
+                "The 'multi' FITS file does not have multiple HDUs.");
+        return 2;
+    }
+
+    char extName[80];
+    psRegion region = {0,0,0,0};
+
+    for (int lcv = 0; lcv < numHDUs; lcv++) {
+        snprintf(extName,80,"ext-%d",lcv);
+        // try to move to the named extension.
+        if (! psFitsMoveExtName(fits, extName) ) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to move to ext-%d.",
+                    lcv);
+            return 3;
+        }
+
+        // check to see if I can retrieve the name back from the psFits object.
+        char* nameFromFile = psFitsGetExtName(fits);
+        if (strcmp(nameFromFile,extName) != 0) { // hey, it didn't move?
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to retrieve the extension name back ('%s' vs '%s'",
+                    nameFromFile, extName);
+            return 3;
+        }
+        psFree(nameFromFile);
+
+        // check that the image is associated to the extension moved, i.e.,
+        // did we really move to the proper extension?
+        psImage* image = NULL;
+        image = psFitsReadImage(fits,region,0);
+
+        if (image == NULL || abs(image->data.F32[0][0] - (float)lcv) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "The image pixel 0,0 of ext-%d was %g, expected %d.",
+                    lcv,image->data.F32[0][0],lcv);
+            return 4;
+        }
+        psFree(image);
+    }
+
+    for (int lcv = numHDUs-1; lcv >= 0; lcv--) {
+        snprintf(extName,80,"ext-%d",lcv);
+        // try to move to the named extension.
+        if (! psFitsMoveExtName(fits, extName) ) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to move to ext-%d.",
+                    lcv);
+            return 5;
+        }
+
+        // check to see if I can retrieve the name back from the psFits object.
+        char* nameFromFile = psFitsGetExtName(fits);
+        if (strcmp(nameFromFile,extName) != 0) { // hey, it didn't move?
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to retrieve the extension name back ('%s' vs '%s'",
+                    nameFromFile, extName);
+            return 5;
+        }
+        psFree(nameFromFile);
+
+        // check that the image is associated to the extension moved, i.e.,
+        // did we really move to the proper extension?
+        psImage* image = NULL;
+        image = psFitsReadImage(fits,region,0);
+
+        if (abs(image->data.F32[0][0] - (float)lcv) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "The image pixel 0,0 of ext-%d was %g, expected %d.",
+                    lcv,image->data.F32[0][0],lcv);
+            return 6;
+        }
+        psFree(image);
+    }
+
+    // check to see if given a bogus extension name, it errors.
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error.");
+    if (psFitsMoveExtName(fits, "bogus") || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Moving to non-existant HDU didn't fail.");
+        return 7;
+    }
+
+    // check to see if given a NULL psFits, it errors.
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error.");
+    if (psFitsMoveExtName(NULL, "bogus") || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Operation of NULL psFits didn't fail.");
+        return 8;
+    }
+
+    // check to see if given a NULL extname, it errors.
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error.");
+    if (psFitsMoveExtName(fits, NULL) || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Operation of NULL extname didn't fail.");
+        return 9;
+    }
+
+    psFree(fits);
+
+    // Attempt to get ext name from null fits file
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for NULL fits file");
+    if(psFitsGetExtName(NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected NULL return from psFitsGetExtName with NULL fits file");
+        return 10;
+    }
+
+    return 0;
+}
+
+psS32 tst_psFitsMoveExtNum( void )
+{
+
+    if (! makeMulti() ) {
+        return 1;
+    }
+
+    psFits* fits = psFitsOpen(multiFilename,"r");
+
+    if (fits == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on existing file.");
+        return 1;
+    }
+
+    int numHDUs = psFitsGetSize(fits);
+
+    // as a side test, let's make sure psFitsGetSize can handle NULL.
+    psLogMsg(__func__,PS_LOG_INFO,
+             "Following should be an error.");
+    psErrorClear();
+    if (psFitsGetSize(NULL) != 0 || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN,true,
+                "The 'multi' FITS file does not have multiple HDUs.");
+        return 2;
+    }
+
+    if (numHDUs != 8) {
+        psError(PS_ERR_UNKNOWN,true,
+                "The 'multi' FITS file does not have multiple HDUs.");
+        return 2;
+    }
+
+    psRegion region = {0,0,0,0};
+
+    // test absolute positioning
+    for (int lcv = 0; lcv < numHDUs; lcv++) {
+        // try to move to the extension
+        if (! psFitsMoveExtNum(fits, lcv, false) ) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to move to extension %d.",
+                    lcv);
+            return 3;
+        }
+
+        // check to see if I can retrieve the number back from the psFits object.
+        if (psFitsGetExtNum(fits) != lcv) { // hey, it didn't move?
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to retrieve the extension number back (%d vs %d)",
+                    psFitsGetExtNum(fits), lcv);
+            return 5;
+        }
+
+        // check that the image is associated to the extension moved, i.e.,
+        // did we really move to the proper extension?
+        psImage* image = NULL;
+        image = psFitsReadImage(fits,region,0);
+        if (image == NULL || abs(image->data.F32[0][0] - (float)lcv) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "The image pixel 0,0 of ext-%d was %g, expected %d.",
+                    lcv,image->data.F32[0][0],lcv);
+            return 4;
+        }
+        psFree(image);
+    }
+
+    for (int lcv = numHDUs-1; lcv >= 0; lcv--) {
+        // try to move to the extension
+        if (! psFitsMoveExtNum(fits, lcv, false) ) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to move to extension %d.",
+                    lcv);
+            return 5;
+        }
+
+        // check that the image is associated to the extension moved, i.e.,
+        // did we really move to the proper extension?
+        psImage* image = NULL;
+        image = psFitsReadImage(fits,region,0);
+
+        if (abs(image->data.F32[0][0] - (float)lcv) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "The image pixel 0,0 of ext-%d was %g, expected %d.",
+                    lcv,image->data.F32[0][0],lcv);
+            return 6;
+        }
+        psFree(image);
+    }
+
+    // test relative positioning
+    psFitsMoveExtNum(fits,0,false);
+    for (int lcv = 1; lcv < numHDUs; lcv++) {
+        // try to move to the extension
+        if (! psFitsMoveExtNum(fits, 1, true) ) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to move to extension %d.",
+                    lcv);
+            return 13;
+        }
+
+        // check to see if I can retrieve the number back from the psFits object.
+        if (psFitsGetExtNum(fits) != lcv) { // hey, it didn't move?
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to retrieve the extension number back (%d vs %d)",
+                    psFitsGetExtNum(fits), lcv);
+            return 13;
+        }
+
+        // check that the image is associated to the extension moved, i.e.,
+        // did we really move to the proper extension?
+        psImage* image = NULL;
+        image = psFitsReadImage(fits,region,0);
+
+        if (image == NULL || abs(image->data.F32[0][0] - (float)lcv) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "The image pixel 0,0 of ext-%d was %g, expected %d.",
+                    lcv,image->data.F32[0][0],lcv);
+            return 14;
+        }
+        psFree(image);
+    }
+
+    for (int lcv = numHDUs-2; lcv >= 0; lcv--) {
+        // try to move to the extension
+        if (! psFitsMoveExtNum(fits, -1, true) ) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to move to extension %d.",
+                    lcv);
+            return 15;
+        }
+
+        // check to see if I can retrieve the number back from the psFits object.
+        if (psFitsGetExtNum(fits) != lcv) { // hey, it didn't move?
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to retrieve the extension number back (%d vs %d)",
+                    psFitsGetExtNum(fits), lcv);
+            return 15;
+        }
+
+        // check that the image is associated to the extension moved, i.e.,
+        // did we really move to the proper extension?
+        psImage* image = NULL;
+        image = psFitsReadImage(fits,region,0);
+
+        if (abs(image->data.F32[0][0] - (float)lcv) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "The image pixel 0,0 of ext-%d was %g, expected %d.",
+                    lcv,image->data.F32[0][0],lcv);
+            return 16;
+        }
+        psFree(image);
+    }
+
+    // check to see if given a negative extension number, it errors.
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error.");
+    psErrorClear();
+    if (psFitsMoveExtNum(fits, -1, false) || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Moving to negative HDU didn't fail.");
+        return 21;
+    }
+
+    // check to see if relative positioning beyond PHU, it errors.
+    psFitsMoveExtNum(fits,0,false);
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error.");
+    psErrorClear();
+    if (psFitsMoveExtNum(fits, -1, true) || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Moving to negative HDU didn't fail.");
+        return 22;
+    }
+
+
+    // check to see if given a extension greater than the total #HDUs, it errors.
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error.");
+    psErrorClear();
+    if (psFitsMoveExtNum(fits, numHDUs, false) || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Moving to a HDU beyond the file's contents didn't fail.");
+        return 31;
+    }
+
+    // check to see if relative positioning beyond PHU, it errors.
+    psFitsMoveExtNum(fits,numHDUs-1,false);
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error.");
+    psErrorClear();
+    if (psFitsMoveExtNum(fits, 1, true) || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Moving to negative HDU didn't fail.");
+        return 32;
+    }
+
+    // check to see if given a NULL psFits, it errors.
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error.");
+    psErrorClear();
+    if (psFitsMoveExtNum(NULL, 0, false) || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Operation of NULL psFits didn't fail.");
+        return 40;
+    }
+
+    psFitsClose(fits);
+
+    // Attempt to get ext name from null fits file
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for NULL fits file");
+    if(psFitsGetExtNum(NULL) != PS_FITS_TYPE_NONE) {
+        psError(PS_ERR_UNKNOWN,true,"Expected NULL return from psFitsGetExtNum with NULL fits file");
+        return 10;
+    }
+
+    return 0;
+}
+
+static psS32 tst_psFitsReadHeader( void )
+{
+    if (! makeMulti() ) {
+        return 1;
+    }
+
+    psFits* fits = psFitsOpen(multiFilename,"r");
+
+    if (fits == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on existing file.");
+        return 1;
+    }
+
+    int numHDUs = psFitsGetSize(fits);
+
+    if (numHDUs < 8) {
+        psError(PS_ERR_UNKNOWN,true,
+                "The 'multi' FITS file does not have multiple HDUs.");
+        return 2;
+    }
+
+    char extname[80];
+    for (int hdunum = 0; hdunum < numHDUs; hdunum++) {
+        snprintf(extname,80,"ext-%d",hdunum);
+
+        psFitsMoveExtNum(fits,hdunum,false);
+
+        psMetadata* header = psFitsReadHeader(NULL,fits);
+        if (header == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to read header");
+            return 3;
+        }
+
+        psMetadata* header2 = psMetadataAlloc();
+        header2 = psFitsReadHeader(header2,fits);
+        if (header2 == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to read header");
+            return 11;
+        }
+
+        if (header->list->n < 1 || header->list->n != header2->list->n) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Reading the header given a NULL input psMetadata differed "
+                    "from giving an existing psMetadata.");
+            return 12;
+        }
+
+        // check for the extra metadata items
+        psS32 intItem = psMetadataLookupS32(NULL,header, "MYINT");
+        psF32 fltItem = psMetadataLookupF32(NULL,header, "MYFLT");
+        psF64 dblItem = psMetadataLookupF64(NULL,header, "MYDBL");
+        psMetadataItem* boolItem = psMetadataLookup(header, "MYBOOL");
+        psString strItem = psMetadataLookupStr(NULL, header, "MYSTR");
+
+        if (intItem != hdunum) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve psS32 metadata item from file.");
+            return 20;
+        }
+
+        if (fabsf(fltItem - 1.0f/(float)(1+hdunum)) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve psF32 metadata item from file.  Got %f vs %f",
+                    fltItem,1.0f/(float)(1+hdunum));
+            return 21;
+        }
+
+        if (abs(dblItem - 1.0/(double)(1+hdunum)) > DBL_EPSILON) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve psF64 metadata item from file.  Got %g vs %g",
+                    dblItem, 1.0/(double)(1+hdunum));
+            return 22;
+        }
+
+        if (boolItem == NULL ||
+                boolItem->type != PS_DATA_BOOL) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve psBool metadata item from file.");
+            return 23;
+        }
+
+        if (strItem == NULL || strncmp(strItem,extname,strlen(extname)) != 0) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve string metadata item from file.  Got '%s' vs '%s' (%d)",
+                    strItem,extname,strlen(extname));
+            return 24;
+        }
+
+        psFree(header);
+        psFree(header2);
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"following should be an error (input psFits = NULL)");
+    psMetadata* header = psFitsReadHeader(NULL,NULL);
+
+    if (header != NULL || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psFitsReadHeader didn't error on a NULL psFits.");
+        return 30;
+    }
+
+
+    psFree(fits);
+
+    return 0;
+}
+
+static psS32 tst_psFitsReadHeaderSet( void )
+{
+    if (! makeMulti() ) {
+        return 1;
+    }
+
+    psFits* fits = psFitsOpen(multiFilename,"r");
+
+    if (fits == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on existing file.");
+        return 1;
+    }
+
+    int numHDUs = psFitsGetSize(fits);
+
+    if (numHDUs < 8) {
+        psError(PS_ERR_UNKNOWN,true,
+                "The 'multi' FITS file does not have multiple HDUs.");
+        return 2;
+    }
+
+    // move to the middle
+    psFitsMoveExtNum(fits,numHDUs/2, false);
+
+    psMetadata* headerSet = psFitsReadHeaderSet(NULL,fits);
+
+    if (headerSet == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadHeaderSet returned NULL unexpectedly.");
+        return 3;
+    }
+
+    if (psFitsGetExtNum(fits) != numHDUs/2) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadHeaderSet changed the CHU.");
+        return 4;
+    }
+
+    char extname[80];
+    for (int i = 0; i < numHDUs; i++) {
+        if (i == 0) {
+            snprintf(extname, 80, "PHU");
+        } else {
+            snprintf(extname, 80, "ext-%d", i);
+        }
+
+        psMetadata* header = psMetadataLookupPtr(NULL,headerSet, extname);
+
+        if (header == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsReadHeader returned NULL unexpectedly for HDU#%d.",
+                    i);
+            return 5;
+        }
+
+        psS32 intItem = psMetadataLookupS32(NULL, header, "MYINT");
+
+        if (intItem != i) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsReadHeader for HDU#%d had a MYINT of %d, expected %d.",
+                    intItem, i);
+            return 6;
+        }
+    }
+
+    psMetadata* set3 = psFitsReadHeaderSet(NULL,fits);
+    if (set3 == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadHeaderSet returned NULL unexpectedly.");
+        return 11;
+    }
+
+    for (int i = 0; i < numHDUs; i++) {
+        if (i == 0) {
+            snprintf(extname, 80, "PHU");
+        } else {
+            snprintf(extname, 80, "ext-%d", i);
+        }
+
+        psMetadata* header = psMetadataLookupPtr(NULL, set3, extname);
+
+        if (header == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsReadHeader returned NULL unexpectedly for HDU#%d.",
+                    i);
+            return 5;
+        }
+
+        psS32 intItem = psMetadataLookupS32(NULL, header, "MYINT");
+
+        if (intItem != i) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsReadHeader for HDU#%d had a MYINT of %d, expected %d.",
+                    intItem, i);
+            return 6;
+        }
+    }
+
+    set3 = psFitsReadHeaderSet(set3,NULL);
+    if (set3 != NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadHeaderSet returned non-NULL given a NULL psFits.");
+        return 10;
+    }
+
+    psFree(headerSet);
+
+    psFitsClose(fits);
+
+    return 0;
+}
+
+static psS32 tst_psFitsReadTable( void )
+{
+
+
+    if (! makeTable()) {
+        return 111;
+    }
+
+    psFits* fits = psFitsOpen(tableFilename,"r");
+
+    if (fits == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on existing file.");
+        return 1;
+    }
+
+    psFitsMoveExtNum(fits,1,false);
+
+    psArray* table = psFitsReadTable(fits);
+
+    if (table == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadTable returned NULL unexpectedly.");
+        return 2;
+    }
+
+    if (table->n != tableNumRows) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Expected %d rows, but read %d.",
+                tableNumRows, table->n);
+        return 3;
+    }
+
+
+    for (int row = 0; row < table->n; row++) {
+        psMetadata* rowData = table->data[row];
+
+        psS32 intItem = psMetadataLookupS32(NULL, rowData, "MYINT");
+        psF32 fltItem = psMetadataLookupF32(NULL, rowData, "MYFLT");
+        psF64 dblItem = psMetadataLookupF64(NULL, rowData, "MYDBL");
+        psBool boolItem = psMetadataLookupBool(NULL, rowData, "MYBOOL");
+        psString strItem = psMetadataLookupStr(NULL, rowData, "MYSTR");
+        psVector* vecItem = psMetadataLookupPtr(NULL, rowData, "MYVEC");
+
+        if (intItem != row) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve psS32 metadata item from file (row=%d).  Got %d vs %d",
+                    row, intItem, row);
+            return 20;
+        }
+
+        if (fabsf(fltItem - 1.0f/(float)(1+row)) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve psF32 metadata item from file (row=%d).  Got %f vs %f",
+                    row, fltItem,1.0f/(float)(1+row));
+            return 21;
+        }
+
+        if (abs(dblItem - 1.0/(double)(1+row)) > DBL_EPSILON) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve psF64 metadata item from file (row=%d).  Got %g vs %g",
+                    row, dblItem, 1.0/(double)(1+row));
+            return 22;
+        }
+
+        if ( boolItem != ((row&0x01) == 0)) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve psBool metadata item from file (row=%d). Got %d vs %d",
+                    row, boolItem, ((row&0x01) == 0));
+            return 23;
+        }
+
+        char strValue[16];
+        snprintf(strValue,16,"row=%d",row+1);
+        if ( strncmp(strItem,strValue,strlen(strValue)) != 0) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve psString metadata item from file (row=%d). Got '%s' vs '%s'",
+                    row, strItem, strValue);
+            return 24;
+        }
+
+        if ( vecItem == NULL ) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Failed to retrieve psVector metadata item from file (row=%d).",
+                    row);
+            return 25;
+        }
+
+        if ( vecItem->type.type != PS_DATA_S32 ||
+                vecItem->data.S32[0] != row ||
+                vecItem->data.S32[3] != row+30 ) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Retrieved psVector (row=%d) didn't match expected values [%d,%d,%d,%d] vs [%d,%d,%d,%d].",
+                    row,vecItem->data.S32[0], vecItem->data.S32[1],
+                    vecItem->data.S32[2], vecItem->data.S32[3],
+                    row,row+10,row+20,row+30);
+            return 26;
+        }
+
+    }
+
+    psFree(table);
+    psFree(fits);
+
+    psArray* nullTest = psFitsReadTable(NULL);
+
+    if (nullTest != NULL || psErrorGetStackSize() != 1) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psFitsReadTable returned non-NULL when given NULL.");
+        return 30;
+    }
+
+    return 0;
+}
+
+static psS32 tst_psFitsReadTableColumnNum( void )
+{
+    if (! makeTable()) {
+        return 111;
+    }
+
+    psFits* fits = psFitsOpen(tableFilename,"r");
+
+    if (fits == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on existing file.");
+        return 1;
+    }
+
+    psFitsMoveExtNum(fits,1,false);
+
+    // read the column data via number
+    psVector* colData;
+    psElemType type[4] = {PS_TYPE_S32, PS_TYPE_F32, PS_TYPE_F32, PS_TYPE_BOOL};
+    psElemType altType[4] = {PS_TYPE_S64, PS_TYPE_F64, PS_TYPE_F64, PS_TYPE_BOOL};
+    char* colname[4] = {"MYINT","MYFLT","MYDBL","MYBOOL"};
+    psF64 expectedValues[4][10] = {
+                                      {0,1,2,3,4,5,6,7,8,9},
+                                      {1.0,1.0/2.0,1.0/3.0,1.0/4.0,1.0/5.0,1.0/6.0,1.0/7.0,1.0/8.0,1.0/9.0},
+                                      {1.0,1.0/2.0,1.0/3.0,1.0/4.0,1.0/5.0,1.0/6.0,1.0/7.0,1.0/8.0,1.0/9.0},
+                                      {1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0}
+                                  };
+
+    for (int col = 0; col < 4; col++) {
+        colData = psFitsReadTableColumnNum(fits,colname[col]);
+        if (colData == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsReadTableColumnNum returned NULL for col=%d",
+                    col);
+            return 2;
+        }
+        if (colData->type.type != type[col] &&
+                colData->type.type != altType[col]) {
+            char* typeRead;
+            char* typeExpected;
+            PS_TYPE_NAME(typeRead, colData->type.type);
+            PS_TYPE_NAME(typeExpected, type[col]);
+
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsReadTableColumnNum returned different type, %s vs %s, for col=%d",
+                    typeRead, typeExpected, col);
+            return 3;
+        }
+        if (colData->n != tableNumRows) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsReadTableColumnNum returned different number of rows, %d vs %d, for col=%d",
+                    colData->n, tableNumRows, col);
+            return 4;
+        }
+        for (int row = 0; row < tableNumRows; row++) {
+            if (abs(p_psVectorGetElementF64(colData,row) - expectedValues[col][row]) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "psFitsReadTableColumnNum returned unexpected values (%g vs %g) for col=%d",
+                        p_psVectorGetElementF64(colData,row), expectedValues[col][row], col);
+                return 5;
+            }
+        }
+        psFree(colData);
+    }
+
+    psWarning("Following should be an error.");
+    psErrorClear();
+    psVector* data = psFitsReadTableColumnNum(NULL,colname[0]);
+    psErr* err = psErrorLast();
+    if (data != NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadTableColumnNum did not return NULL with NULL psFits");
+        return 6;
+    }
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadTableColumnNum did not error with NULL psFits");
+        return 7;
+    }
+    psFree(err);
+
+    psWarning("Following should be an error.");
+    psErrorClear();
+    data = psFitsReadTableColumnNum(fits,"BOGUS");
+    err = psErrorLast();
+    if (data != NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadTableColumnNum did not return NULL with bogus column name.");
+        return 8;
+    }
+    if (err->code != PS_ERR_IO) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadTableColumnNum did not error with bogus column name.");
+        return 9;
+    }
+    psFree(err);
+
+    psFree(fits);
+
+    return 0;
+}
+
+static psS32 tst_psFitsReadTableColumn( void )
+{
+    if (! makeTable()) {
+        return 111;
+    }
+
+    psFits* fits = psFitsOpen(tableFilename,"r");
+
+    if (fits == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on existing file.");
+        return 1;
+    }
+
+    psFitsMoveExtNum(fits,1,false);
+
+    // read the column data via number
+    psArray* colData;
+    char* colname[4] = {"MYINT","MYFLT","MYDBL","MYBOOL"};
+    psF64 expectedValues[3][10] = {
+                                      {0,1,2,3,4,5,6,7,8,9},
+                                      {1.0,1.0/2.0,1.0/3.0,1.0/4.0,1.0/5.0,1.0/6.0,1.0/7.0,1.0/8.0,1.0/9.0},
+                                      {1.0,1.0/2.0,1.0/3.0,1.0/4.0,1.0/5.0,1.0/6.0,1.0/7.0,1.0/8.0,1.0/9.0}
+                                  };
+    char* expectedBoolValues[10] = {"T","F","T","F","T","F","T","F","T","F"};
+    char* expectedStrValues[10] = {"row=1","row=2","row=3","row=4","row=5","row=6","row=7","row=8","row=9","row=10"};
+
+    for (int col = 0; col < 4; col++) {
+        colData = psFitsReadTableColumn(fits,colname[col]);
+        if (colData == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsReadTableColumn returned NULL for col=%d",
+                    col);
+            return 2;
+        }
+        if (colData->n != tableNumRows) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsReadTableColumn returned different number of rows, %d vs %d, for col=%d",
+                    colData->n, tableNumRows, col);
+            return 4;
+        }
+        if (col < 3) {
+            for (int row = 0; row < tableNumRows; row++) {
+                if (abs(atof((char*)colData->data[row]) - expectedValues[col][row]) > 0.0001) {
+                    psError(PS_ERR_UNKNOWN, false,
+                            "psFitsReadTableColumn returned unexpected values (%g vs %g) for col=%d",
+                            atof((char*)colData->data[row]), expectedValues[col][row], col);
+                    return 5;
+                }
+            }
+        } else if (col == 3) {
+            for (int row = 0; row < tableNumRows; row++) {
+                if (strncmp(colData->data[row],expectedBoolValues[row],
+                            strlen(expectedBoolValues[row])) != 0) {
+                    psError(PS_ERR_UNKNOWN, false,
+                            "psFitsReadTableColumn returned unexpected values ('%s' vs '%s') for col=%d",
+                            (char*)colData->data[row], expectedBoolValues[row], col);
+                    return 5;
+                }
+            }
+        } else if (col == 4) {
+            for (int row = 0; row < tableNumRows; row++) {
+                if (strncmp(colData->data[row],expectedStrValues[row],
+                            strlen(expectedStrValues[row])) != 0) {
+                    psError(PS_ERR_UNKNOWN, false,
+                            "psFitsReadTableColumn returned unexpected values ('%s' vs '%s') for col=%d",
+                            (char*)colData->data[row], expectedStrValues[row], col);
+                    return 5;
+                }
+            }
+        }
+
+        psFree(colData);
+    }
+
+    psWarning("Following should be an error.");
+    psErrorClear();
+    psArray* data = psFitsReadTableColumn(NULL,"MYINT");
+    psErr* err = psErrorLast();
+    if (data != NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadTableColumn did not return NULL with NULL psFits");
+        return 6;
+    }
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadTableColumn did not error with NULL psFits");
+        return 7;
+    }
+    psFree(err);
+
+    psWarning(__func__,"Following should be an error.");
+    psErrorClear();
+    data = psFitsReadTableColumn(fits,"BOGUS");
+    err = psErrorLast();
+    if (data != NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadTableColumn did not return NULL with col=\"BOGUS\"");
+        return 8;
+    }
+    if (err->code != PS_ERR_IO) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsReadTableColumn did not error with col=\"BOGUS\"");
+        return 9;
+    }
+    psFree(err);
+
+    psFree(fits);
+
+    return 0;
+}
+
+static psS32 tst_psFitsUpdateTable( void )
+{
+    psErr* err;
+    char* strValue[] = {
+                           "row A",
+                           "row B",
+                           "row C",
+                           "row D",
+                           "row E",
+                           "row F",
+                           "row G",
+                           "row H",
+                           "row I",
+                           "row J",
+                           "row K"
+                       };
+
+
+    if (! makeTable()) {
+        return 111;
+    }
+
+    psFits* fits = psFitsOpen(tableFilename,"rw");
+
+    if (fits == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsOpen returned NULL on existing file.");
+        return 2;
+    }
+
+    psFitsMoveExtNum(fits,1,false);
+
+    // change the data in the file, going past by one (implicit new row of data)
+    for (int row = 0; row < tableNumRows+1; row++) {
+        psMetadata* md = psMetadataAlloc();
+        psMetadataAddF32(md,PS_LIST_TAIL,"MYFLT", 0,"",(float)row/-10.0);
+        psMetadataAddF64(md,PS_LIST_TAIL,"MYDBL", 0,"",(double)row/-100.0);
+        psMetadataAddS32(md,PS_LIST_TAIL,"MYINT", 0,"",-row);
+        psMetadataAddBool(md,PS_LIST_TAIL,"MYBOOL", 0,"",((row & 1) == 1));
+        psMetadataAddStr(md,PS_LIST_TAIL,"MYSTR", 0,"",strValue[row]);
+
+        if (! psFitsUpdateTable(fits,md,row)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsUpdateTable returned false, but expected true for row=%d.",
+                    row);
+            return 3;
+        }
+        psFree(md);
+    }
+
+    for (int row = 0; row < tableNumRows+1; row++) {
+        psMetadata* md = psFitsReadTableRow(fits, row);
+        if (abs(psMetadataLookupF32(NULL,md,"MYFLT") - (float)row/-10.0) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsUpdateTable did not change the float value for row=%d.",
+                    row);
+            return 4;
+        }
+        if (abs(psMetadataLookupF64(NULL,md,"MYDBL") - (float)row/-100.0) > FLT_EPSILON) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsUpdateTable did not change the double value for row=%d.",
+                    row);
+            return 5;
+        }
+        if (psMetadataLookupS32(NULL,md,"MYINT") != -row) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsUpdateTable did not change the integer value for row=%d.",
+                    row);
+            return 6;
+        }
+        if (psMetadataLookupBool(NULL,md,"MYBOOL") != ((row &1) == 1)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsUpdateTable did not change the boolean value for row=%d.",
+                    row);
+            return 7;
+        }
+        psString mystr = psMetadataLookupStr(NULL,md,"MYSTR");
+        if (strncmp(mystr,strValue[row],
+                    strlen(strValue[row])) != 0) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "psFitsUpdateTable did not change the string value for row=%d.",
+                    row);
+            return 8;
+        }
+        psFree(md);
+    }
+
+    psMetadata* md = psMetadataAlloc();
+    psMetadataAddF32(md,PS_LIST_TAIL,"BOGUS", 0,"",-1.0f);
+    psWarning("Following should be a warning.");
+    psErrorClear();
+    if (! psFitsUpdateTable(fits,md,0)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsUpdateTable did not return false with bogus column data");
+        return 10;
+    }
+    psFree(md);
+
+    md = psMetadataAlloc();
+    psMetadataAddF32(md,PS_LIST_TAIL,"MYFLT", 0,"",-1.0f);
+    psMetadataAddF64(md,PS_LIST_TAIL,"MYDBL", 0,"",-2.0);
+    psMetadataAddS32(md,PS_LIST_TAIL,"MYINT", 0,"",-3);
+
+    psWarning("Following should be an error.");
+    psErrorClear();
+    if (psFitsUpdateTable(NULL,md,0)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsUpdateTable did not return false with NULL psFits");
+        return 20;
+    }
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsUpdateTable did not error with NULL psFits");
+        return 21;
+    }
+    psFree(err);
+
+    psWarning("Following should be an error.");
+    psErrorClear();
+    if (psFitsUpdateTable(fits,NULL,0)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsUpdateTable did not return false with NULL psMetadata");
+        return 22;
+    }
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsUpdateTable did not error with NULL psMetadata");
+        return 23;
+    }
+    psFree(err);
+
+    psWarning("Following should be an error.");
+    psErrorClear();
+    if (psFitsUpdateTable(fits,md,-1)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsUpdateTable did not return false with row=-1");
+        return 24;
+    }
+    err = psErrorLast();
+    if (err->code != PS_ERR_IO) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psFitsUpdateTable did not error with row=-1");
+        return 25;
+    }
+
+    psFree(err);
+    psFree(md);
+    psFitsClose(fits);
+
+    return 0;
+}
+
+psS32 testImageRead(void)
+{
+    psS32 N = 256;
+    psS32 M = 128;
+
+    /*
+        This function shall open the specified FITS file, read the specified data
+        and place the data into a psImage structure. This function shall generate
+        an error message and return NULL if any of the input parameters are out of
+        range, image file doesn't exist or image is zero or one dimensional.
+
+        Verify the returned psImage structure contains expected values, if the input
+        parameter filename specifies an available FITS file with known 2dimensional
+        data, input parameters col, row, ncol, nrow, z specify data range with the
+        FITS file. Cases should include 1x1, Nx1, 1xN, NxN and MxN sub images and
+        total FITS file image. (done in macro)
+
+        Verify the returned psImage structure is equal to the input parameter
+        'output', if specified. (done in macro)
+
+        */
+
+    /* generate FITS file to read */
+
+    #define testReadTypeSize(m, n, readM0, readN0, readM, readN, TYP, filename) \
+    { \
+        psImage* img = NULL; \
+        psImage* img2 = NULL; \
+        psImage* img3 = NULL; \
+        psImage* img4 = NULL; \
+        /*        psImagimge* img_ref = NULL; */ \
+        \
+        GENIMAGE(img,m,n,TYP,row+2*col); \
+        img2 = psImageCopy(img2,img,PS_TYPE_##TYP); \
+        GENIMAGE(img3,m,n,TYP,row+2*col); \
+        psImageClip(img3,32.0,32.0,120.0,120.0); \
+        img4 = psImageCopy(img4,img3,PS_TYPE_##TYP); \
+        psFits* fits = psFitsOpen(filename, "w"); \
+        if (! psFitsWriteImage(fits, NULL, img, 2, NULL)) { \
+            psError(PS_ERR_UNKNOWN, true,"Failed to write test image %s",filename); \
+            return 1; \
+        } \
+        if (! psFitsUpdateImage(fits,img3, 0,0, 1)) { \
+            psError(PS_ERR_UNKNOWN, true,"Failed to write test image %s",filename); \
+            return 2; \
+        } \
+        if (! psFitsWriteImage(fits,NULL, img3, 2, NULL)) { \
+            psError(PS_ERR_UNKNOWN, true,"Failed to write test image %s",filename); \
+            return 3; \
+        } \
+        if (! psFitsUpdateImage(fits,img, 0, 0, 1)) { \
+            psError(PS_ERR_UNKNOWN, true,"Failed to write test image %s",filename); \
+            return 4; \
+        } \
+        psFree(img); \
+        psFree(fits); \
+        img = NULL; \
+        psFree(img3); \
+        img3 = NULL; \
+        fits = psFitsOpen(filename,"r"); \
+        psRegion reg = {readM0, readM, readN0, readN}; \
+        img = psFitsReadImage(fits, reg, 0); \
+        img3 = psFitsReadImage(fits, reg, 1); \
+        if (img3 == NULL) { \
+            psError(PS_ERR_UNKNOWN, true,"Failed to read test image %s",filename); \
+            return 6; \
+        } \
+        for (psU32 row = readN0; row < readN; row++) { \
+            ps##TYP* imgRow = img->data.TYP[row-readN0]; \
+            ps##TYP* img2Row = img2->data.TYP[row]; \
+            ps##TYP* img3Row = img3->data.TYP[row-readN0]; \
+            ps##TYP* img4Row = img4->data.TYP[row]; \
+            for (psU32 col = readM0; col < readM; col++) { \
+                if (fabsf(imgRow[col-readM0]-img2Row[col]) > FLT_EPSILON) { \
+                    psError(PS_ERR_UNKNOWN, true,"Image changed in I/O operation at %d,%d,0 (%.2f vs %.2f) for %s", \
+                            col,row,(psF32)imgRow[col-readM0],(psF32)img2Row[col],filename); \
+                    return 7; \
+                } \
+                if (fabsf(img3Row[col-readM0]-img4Row[col]) > FLT_EPSILON) { \
+                    psError(PS_ERR_UNKNOWN, true,"Image changed in I/O operation at %d,%d,1 (%.2f vs %.2f) for %s", \
+                            col,row,(psF32)img3Row[col-readM0],(psF32)img4Row[col],filename); \
+                    return 8; \
+                } \
+            } \
+        } \
+        psFree(img); \
+        img = NULL; \
+        psFree(img3); \
+        img3 = NULL; \
+        psFitsMoveExtNum(fits,1, false); \
+        img3 = psFitsReadImage(fits, reg, 0); \
+        img = psFitsReadImage(fits, reg, 1); \
+        if (img == NULL) { \
+            psError(PS_ERR_UNKNOWN, true,"Failed to read test image %s",filename); \
+            return 9; \
+        } \
+        for (psU32 row = readN0; row < readN; row++) { \
+            ps##TYP* imgRow = img->data.TYP[row-readN0]; \
+            ps##TYP* img2Row = img2->data.TYP[row]; \
+            ps##TYP* img3Row = img3->data.TYP[row-readN0]; \
+            ps##TYP* img4Row = img4->data.TYP[row]; \
+            for (psU32 col = readM0; col < readM; col++) { \
+                if (fabsf(imgRow[col-readM0]-img2Row[col]) > FLT_EPSILON) { \
+                    psError(PS_ERR_UNKNOWN, true,"Image changed in I/O operation at %d,%d,0 (%.2f vs %.2f) for %s", \
+                            col,row,(psF32)imgRow[col-readM0],(psF32)img2Row[col],filename); \
+                    return 10; \
+                } \
+                if (fabsf(img3Row[col-readM0]-img4Row[col]) > FLT_EPSILON) { \
+                    psError(PS_ERR_UNKNOWN, true,"Image changed in I/O operation at %d,%d,1 (%.2f vs %.2f) for %s", \
+                            col,row,(psF32)img3Row[col-readM0],(psF32)img4Row[col],filename); \
+                    return 11; \
+                } \
+            } \
+        } \
+        psFree(img); \
+        psFree(img2); \
+        psFree(img3); \
+        psFree(img4); \
+        psFree(fits); \
+    }
+
+    #define testReadType(TYP,filename) \
+    testReadTypeSize(1,1,0,0,0,0,TYP,"tmpImages/1x1_" filename); \
+    testReadTypeSize(M,1,M/4,0,M*3/4,0,TYP,"tmpImages/Mx1_" filename); \
+    testReadTypeSize(1,N,0,N/4,0,N*3/4,TYP,"tmpImages/1xN_" filename); \
+    testReadTypeSize(M,N,M/4,N/4,M*3/4,N*3/4,TYP,"tmpImages/MxN_" filename);
+
+    mkdir("tmpImages",0777);
+
+    testReadType(U8,"U8.fits");
+    testReadType(S8,"S8.fits");   // Not a requirement
+    testReadType(S16,"S16.fits");
+    testReadType(U16,"U16.fits"); // Not a requirement
+    testReadType(S32,"S32.fits");
+    testReadType(U32,"U32.fits"); // Not a requirement
+    testReadType(F32,"F32.fits");
+    testReadType(F64,"F64.fits");
+
+    // Attempt to read from NULL fits object
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL psFits");
+    psRegion region = {
+                          0,0,0,0
+                      };
+    if(psFitsReadImage(NULL,region,0) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for NULL psFits");
+        return 100;
+    }
+
+    return 0;
+}
+
+psS32 testImageWrite(void)
+{
+    psImage* img = NULL;
+    psImage* img2 = NULL;
+    psS32 m = 64;
+    psS32 n = 96;
+
+    /*
+    This function shall write the specified section within a psImage structure
+    to a FITS file. If the specifiedfile exists, then data should overwrite the
+    section to write. If the specified file doesn't exist, it shall be created.
+    If an extenstion is specified, then a basic primary header data unit shall
+    be created.
+    */
+
+    /*
+    Verify a FITS file named filename is generated and contains expected
+    values, if the input parameter input contains known data values, input
+    parameters col, row, ncol, nrow specify a valid data region within psImage
+    structure.
+
+    Verify a FITS file named filename is generated and contains a primary
+    header data unit with extension with expected values, if the input
+    parameter input contains known data values, input parameters col, row,
+    ncol, nrow specify a valid data region within psImage structure and
+    extname and/or extnum specify an extenstion to write.
+
+    N.B. : these are done in testImageRead tests, see above.
+    */
+
+    /*
+    Verify a FITS file named filename is overwritten and contains
+    expected values, if the input parameter input contains known data values,
+    input parameters col, row, ncol, nrow specify a valid data region within
+    psImage structure.
+    */
+
+    GENIMAGE(img,m,n,F32,0);
+    GENIMAGE(img2,m,n,F32,row+2*col);
+    mkdir("tmpImages",0777);
+    psFits* fits = psFitsOpen("tmpImages/writeTest.fits","w");
+
+    if (! psFitsWriteImage(fits, NULL, img,1, NULL)) {
+        psError(PS_ERR_UNKNOWN, true,"Couldn't write writeTest.fits.");
+        return 14;
+    }
+    if (! psFitsUpdateImage(fits, img2, 0, 0, 0)) {
+        psError(PS_ERR_UNKNOWN, true,"Couldn't update writeTest.fits.");
+        return 15;
+    }
+    psFree(img);
+    psFree(img2);
+
+    // Did it really overwrite the pixel values?  Let's read it in and see.
+    psFitsClose(fits);
+
+    psRegion region = {0,0,0,0};
+    fits = psFitsOpen("tmpImages/writeTest.fits","r");
+    img = NULL;
+    img = psFitsReadImage(fits, region, 0);
+    if (img == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"Could not read in writeTest.fits.");
+        return 16;
+    }
+    for (psU32 row=0;row<n;row++) {
+        psF32* imgRow = img->data.F32[row];
+        for (psU32 col=0;col<m;col++) {
+            if (fabsf(imgRow[col] - (row+2*col)) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,"The image values were not overwritten at %d,%d (%.2f vs %.2f)",
+                        col,row,imgRow[col],(row+2*col));
+                return 17;
+            }
+        }
+    }
+
+    psFree(img);
+
+    /*
+    Verify false is returned and program execution is not stopped, if the input image
+    is null.
+    */
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message because input image is null.");
+    if ( psFitsWriteImage(fits,NULL,NULL, 1, NULL) ) {
+        psError(PS_ERR_UNKNOWN, true,"psImageWriteSection did not return false when input image is NULL.");
+        return 20;
+    }
+
+    psFree(fits);
+
+    return 0;
+}
+
+psS32 tst_psFitsWriteHeader(void)
+{
+    if (! makeMulti() ) {
+        return 1;
+    }
+
+    psMetadata* header   = psMetadataAlloc();
+    psFits*     fitsFile = psFitsOpen(multiFilename,"a+");
+
+    // Test psFitsReadWrite generates files from psFitsWriteImage which calls psFitsWriteHeader
+    // so these additional tests check for error conditions
+    // Attempt call function with NULL metadata
+    if(psFitsWriteHeader(fitsFile, NULL)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return of true for NULL metadata pointer");
+        return 2;
+    }
+
+    psFree(fitsFile);
+
+    // Attempt to call function with NULL fits
+    if(psFitsWriteHeader(NULL, header)) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return of true for NULL fits file pointer");
+        return 3;
+    }
+    psFree(header);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/fBiOut.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/fBiOut.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/fBiOut.fits	(revision 22322)
@@ -0,0 +1,6427 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B~
+B~záB~p€B~ffB~\)B~QìB~G®B~=qB~33B~(öB~
+žB~{B~
+=B~  B}õÃB}ë
+B}áHB}×
+B}ÌÍB}ÂB}žRB}®B}£×B}B}\B}
+B}záB}p€B}ffB}\)B}QìB}G®B}=qB}33B}(öB}
+žB}{B}
+=B}  B|õÃB|ë
+B|áHB|×
+B|ÌÍB|ÂB|žRB|®B|£×B|B|\B|
+B|záB|p€B|ffB|\)B|QìB|G®B|=qB|33B|(öB|
+žB|{B|
+=B|  Bz
+BzzáBzp€BzffBz\)BzQìBzG®Bz=qBz33Bz(öBz
+žBz{Bz
+=Bz  ByõÃByë
+ByáHBy×
+ByÌÍByÂByžRBy®By£×ByBy\By
+ByzáByp€ByffBy\)ByQìByG®By=qBy33By(öBy
+žBy{By
+=By  BxõÃBxë
+BxáHBx×
+BxÌÍBxÂBxžRBx®Bx£×BxBx\Bx
+BxzáBxp€BxffBx\)BxQìBxG®Bx=qBx33Bx(öBx
+žBx{Bx
+=Bx  Bv
+BvzáBvp€BvffBv\)BvQìBvG®Bv=qBv33Bv(öBv
+žBv{Bv
+=Bv  BuõÃBuë
+BuáHBu×
+BuÌÍBuÂBužRBu®Bu£×BuBu\Bu
+BuzáBup€BuffBu\)BuQìBuG®Bu=qBu33Bu(öBu
+žBu{Bu
+=Bu  BtõÃBtë
+BtáHBt×
+BtÌÍBtÂBtžRBt®Bt£×BtBt\Bt
+BtzáBtp€BtffBt\)BtQìBtG®Bt=qBt33Bt(öBt
+žBt{Bt
+=Bt  Br
+BrzáBrp€BrffBr\)BrQìBrG®Br=qBr33Br(öBr
+žBr{Br
+=Br  BqõÃBqë
+BqáHBq×
+BqÌÍBqÂBqžRBq®Bq£×BqBq\Bq
+BqzáBqp€BqffBq\)BqQìBqG®Bq=qBq33Bq(öBq
+žBq{Bq
+=Bq  BpõÃBpë
+BpáHBp×
+BpÌÍBpÂBpžRBp®Bp£×BpBp\Bp
+BpzáBpp€BpffBp\)BpQìBpG®Bp=qBp33Bp(öBp
+žBp{Bp
+=Bp  Bn
+BnzáBnp€BnffBn\)BnQìBnG®Bn=qBn33Bn(öBn
+žBn{Bn
+=Bn  BmõÃBmë
+BmáHBm×
+BmÌÍBmÂBmžRBm®Bm£×BmBm\Bm
+BmzáBmp€BmffBm\)BmQìBmG®Bm=qBm33Bm(öBm
+žBm{Bm
+=Bm  BlõÃBlë
+BláHBl×
+BlÌÍBlÂBlžRBl®Bl£×BlBl\Bl
+BlzáBlp€BlffBl\)BlQìBlG®Bl=qBl33Bl(öBl
+žBl{Bl
+=Bl  Bj
+BjzáBjp€BjffBj\)BjQìBjG®Bj=qBj33Bj(öBj
+žBj{Bj
+=Bj  BiõÃBië
+BiáHBi×
+BiÌÍBiÂBižRBi®Bi£×BiBi\Bi
+BizáBip€BiffBi\)BiQìBiG®Bi=qBi33Bi(öBi
+žBi{Bi
+=Bi  BhõÃBhë
+BháHBh×
+BhÌÍBhÂBhžRBh®Bh£×BhBh\Bh
+BhzáBhp€BhffBh\)BhQìBhG®Bh=qBh33Bh(öBh
+žBh{Bh
+=Bh  Bf
+BfzáBfp€BfffBf\)BfQìBfG®Bf=qBf33Bf(öBf
+žBf{Bf
+=Bf  BeõÃBeë
+BeáHBe×
+BeÌÍBeÂBežRBe®Be£×BeBe\Be
+BezáBep€BeffBe\)BeQìBeG®Be=qBe33Be(öBe
+žBe{Be
+=Be  BdõÃBdë
+BdáHBd×
+BdÌÍBdÂBdžRBd®Bd£×BdBd\Bd
+BdzáBdp€BdffBd\)BdQìBdG®Bd=qBd33Bd(öBd
+žBd{Bd
+=Bd  Bb
+BbzáBbp€BbffBb\)BbQìBbG®Bb=qBb33Bb(öBb
+žBb{Bb
+=Bb  BaõÃBaë
+BaáHBa×
+BaÌÍBaÂBažRBa®Ba£×BaBa\Ba
+BazáBap€BaffBa\)BaQìBaG®Ba=qBa33Ba(öBa
+žBa{Ba
+=Ba  B`õÃB`ë
+B`áHB`×
+B`ÌÍB`ÂB`žRB`®B`£×B`B`\B`
+B`záB`p€B`ffB`\)B`QìB`G®B`=qB`33B`(öB`
+žB`{B`
+=B`  B^
+B^záB^p€B^ffB^\)B^QìB^G®B^=qB^33B^(öB^
+žB^{B^
+=B^  B]õÃB]ë
+B]áHB]×
+B]ÌÍB]ÂB]žRB]®B]£×B]B]\B]
+B]záB]p€B]ffB]\)B]QìB]G®B]=qB]33B](öB]
+žB]{B]
+=B]  B\õÃB\ë
+B\áHB\×
+B\ÌÍB\ÂB\žRB\®B\£×B\B\\B\
+B\záB\p€B\ffB\\)B\QìB\G®B\=qB\33B\(öB\
+žB\{B\
+=B\  BZ
+BZzáBZp€BZffBZ\)BZQìBZG®BZ=qBZ33BZ(öBZ
+žBZ{BZ
+=BZ  BYõÃBYë
+BYáHBY×
+BYÌÍBYÂBYžRBY®BY£×BYBY\BY
+BYzáBYp€BYffBY\)BYQìBYG®BY=qBY33BY(öBY
+žBY{BY
+=BY  BXõÃBXë
+BXáHBX×
+BXÌÍBXÂBXžRBX®BX£×BXBX\BX
+BXzáBXp€BXffBX\)BXQìBXG®BX=qBX33BX(öBX
+žBX{BX
+=BX  BV
+BVzáBVp€BVffBV\)BVQìBVG®BV=qBV33BV(öBV
+žBV{BV
+=BV  BUõÃBUë
+BUáHBU×
+BUÌÍBUÂBUžRBU®BU£×BUBU\BU
+BUzáBUp€BUffBU\)BUQìBUG®BU=qBU33BU(öBU
+žBU{BU
+=BU  BTõÃBTë
+BTáHBT×
+BTÌÍBTÂBTžRBT®BT£×BTBT\BT
+BTzáBTp€BTffBT\)BTQìBTG®BT=qBT33BT(öBT
+žBT{BT
+=BT  BR
+BRzáBRp€BRffBR\)BRQìBRG®BR=qBR33BR(öBR
+žBR{BR
+=BR  BQõÃBQë
+BQáHBQ×
+BQÌÍBQÂBQžRBQ®BQ£×BQBQ\BQ
+BQzáBQp€BQffBQ\)BQQìBQG®BQ=qBQ33BQ(öBQ
+žBQ{BQ
+=BQ  BPõÃBPë
+BPáHBP×
+BPÌÍBPÂBPžRBP®BP£×BPBP\BP
+BPzáBPp€BPffBP\)BPQìBPG®BP=qBP33BP(öBP
+žBP{BP
+=BP  BN
+BNzáBNp€BNffBN\)BNQìBNG®BN=qBN33BN(öBN
+žBN{BN
+=BN  BMõÃBMë
+BMáHBM×
+BMÌÍBMÂBMžRBM®BM£×BMBM\BM
+BMzáBMp€BMffBM\)BMQìBMG®BM=qBM33BM(öBM
+žBM{BM
+=BM  BLõÃBLë
+BLáHBL×
+BLÌÍBLÂBLžRBL®BL£×BLBL\BL
+BLzáBLp€BLffBL\)BLQìBLG®BL=qBL33BL(öBL
+žBL{BL
+=BL  BJ
+BJzáBJp€BJffBJ\)BJQìBJG®BJ=qBJ33BJ(öBJ
+žBJ{BJ
+=BJ  BIõÃBIë
+BIáHBI×
+BIÌÍBIÂBIžRBI®BI£×BIBI\BI
+BIzáBIp€BIffBI\)BIQìBIG®BI=qBI33BI(öBI
+žBI{BI
+=BI  BHõÃBHë
+BHáHBH×
+BHÌÍBHÂBHžRBH®BH£×BHBH\BH
+BHzáBHp€BHffBH\)BHQìBHG®BH=qBH33BH(öBH
+žBH{BH
+=BH  BF
+BFzáBFp€BFffBF\)BFQìBFG®BF=qBF33BF(öBF
+žBF{BF
+=BF  BEõÃBEë
+BEáHBE×
+BEÌÍBEÂBEžRBE®BE£×BEBE\BE
+BEzáBEp€BEffBE\)BEQìBEG®BE=qBE33BE(öBE
+žBE{BE
+=BE  BDõÃBDë
+BDáHBD×
+BDÌÍBDÂBDžRBD®BD£×BDBD\BD
+BDzáBDp€BDffBD\)BDQìBDG®BD=qBD33BD(öBD
+žBD{BD
+=BD  BB
+BBzáBBp€BBffBB\)BBQìBBG®BB=qBB33BB(öBB
+žBB{BB
+=BB  BAõÃBAë
+BAáHBA×
+BAÌÍBAÂBAžRBA®BA£×BABA\BA
+BAzáBAp€BAffBA\)BAQìBAG®BA=qBA33BA(öBA
+žBA{BA
+=BA  B@õÃB@ë
+B@áHB@×
+B@ÌÍB@ÂB@žRB@®B@£×B@B@\B@
+B@záB@p€B@ffB@\)B@QìB@G®B@=qB@33B@(öB@
+žB@{B@
+=B@  B>
+B>záB>p€B>ffB>\)B>QìB>G®B>=qB>33B>(öB>
+žB>{B>
+=B>  B=õÃB=ë
+B=áHB=×
+B=ÌÍB=ÂB=žRB=®B=£×B=B=\B=
+B=záB=p€B=ffB=\)B=QìB=G®B==qB=33B=(öB=
+žB={B=
+=B=  B<õÃB<ë
+B<áHB<×
+B<ÌÍB<ÂB<žRB<®B<£×B<B<\B<
+B<záB<p€B<ffB<\)B<QìB<G®B<=qB<33B<(öB<
+žB<{B<
+=B<  B:
+B:záB:p€B:ffB:\)B:QìB:G®B:=qB:33B:(öB:
+žB:{B:
+=B:  B9õÃB9ë
+B9áHB9×
+B9ÌÍB9ÂB9žRB9®B9£×B9B9\B9
+B9záB9p€B9ffB9\)B9QìB9G®B9=qB933B9(öB9
+žB9{B9
+=B9  B8õÃB8ë
+B8áHB8×
+B8ÌÍB8ÂB8žRB8®B8£×B8B8\B8
+B8záB8p€B8ffB8\)B8QìB8G®B8=qB833B8(öB8
+žB8{B8
+=B8  B6
+B6záB6p€B6ffB6\)B6QìB6G®B6=qB633B6(öB6
+žB6{B6
+=B6  B5õÃB5ë
+B5áHB5×
+B5ÌÍB5ÂB5žRB5®B5£×B5B5\B5
+B5záB5p€B5ffB5\)B5QìB5G®B5=qB533B5(öB5
+žB5{B5
+=B5  B4õÃB4ë
+B4áHB4×
+B4ÌÍB4ÂB4žRB4®B4£×B4B4\B4
+B4záB4p€B4ffB4\)B4QìB4G®B4=qB433B4(öB4
+žB4{B4
+=B4  B2
+B2záB2p€B2ffB2\)B2QìB2G®B2=qB233B2(öB2
+žB2{B2
+=B2  B1õÃB1ë
+B1áHB1×
+B1ÌÍB1ÂB1žRB1®B1£×B1B1\B1
+B1záB1p€B1ffB1\)B1QìB1G®B1=qB133B1(öB1
+žB1{B1
+=B1  B0õÃB0ë
+B0áHB0×
+B0ÌÍB0ÂB0žRB0®B0£×B0B0\B0
+B0záB0p€B0ffB0\)B0QìB0G®B0=qB033B0(öB0
+žB0{B0
+=B0  B.
+B.záB.p€B.ffB.\)B.QìB.G®B.=qB.33B.(öB.
+žB.{B.
+=B.  B-õÃB-ë
+B-áHB-×
+B-ÌÍB-ÂB-žRB-®B-£×B-B-\B-
+B-záB-p€B-ffB-\)B-QìB-G®B-=qB-33B-(öB-
+žB-{B-
+=B-  B,õÃB,ë
+B,áHB,×
+B,ÌÍB,ÂB,žRB,®B,£×B,B,\B,
+B,záB,p€B,ffB,\)B,QìB,G®B,=qB,33B,(öB,
+žB,{B,
+=B,  B*
+B*záB*p€B*ffB*\)B*QìB*G®B*=qB*33B*(öB*
+žB*{B*
+=B*  B)õÃB)ë
+B)áHB)×
+B)ÌÍB)ÂB)žRB)®B)£×B)B)\B)
+B)záB)p€B)ffB)\)B)QìB)G®B)=qB)33B)(öB)
+žB){B)
+=B)  B(õÃB(ë
+B(áHB(×
+B(ÌÍB(ÂB(žRB(®B(£×B(B(\B(
+B(záB(p€B(ffB(\)B(QìB(G®B(=qB(33B((öB(
+žB({B(
+=B(  B&
+B&záB&p€B&ffB&\)B&QìB&G®B&=qB&33B&(öB&
+žB&{B&
+=B&  B%õÃB%ë
+B%áHB%×
+B%ÌÍB%ÂB%žRB%®B%£×B%B%\B%
+B%záB%p€B%ffB%\)B%QìB%G®B%=qB%33B%(öB%
+žB%{B%
+=B%  B$õÃB$ë
+B$áHB$×
+B$ÌÍB$ÂB$žRB$®B$£×B$B$\B$
+B$záB$p€B$ffB$\)B$QìB$G®B$=qB$33B$(öB$
+žB${B$
+=B$  B"
+B"záB"p€B"ffB"\)B"QìB"G®B"=qB"33B"(öB"
+žB"{B"
+=B"  B!õÃB!ë
+B!áHB!×
+B!ÌÍB!ÂB!žRB!®B!£×B!B!\B!
+B!záB!p€B!ffB!\)B!QìB!G®B!=qB!33B!(öB!
+žB!{B!
+=B!  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B	õÃB	ë
+B	áHB	×
+B	ÌÍB	ÂB	žRB	®B	£×B	B	\B	
+B	záB	p€B	ffB	\)B	QìB	G®B	=qB	33B	(öB	
+žB	{B	
+=B	  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   Aý
+=AüõÃAüáHAüÌÍAüžRAü£×Aü\AüzáAüffAüQìAü=qAü(öAü{Aü  Aûë
+Aû×
+AûÂAû®AûAû
+Aûp€Aû\)AûG®Aû33Aû
+žAû
+=AúõÃAúáHAúÌÍAúžRAú£×Aú\AúzáAúffAúQìAú=qAú(öAú{Aú  Aùë
+Aù×
+AùÂAù®AùAù
+Aùp€Aù\)AùG®Aù33Aù
+žAù
+=AøõÃAøáHAøÌÍAøžRAø£×Aø\AøzáAøffAøQìAø=qAø(öAø{Aø  Aõ
+=AôõÃAôáHAôÌÍAôžRAô£×Aô\AôzáAôffAôQìAô=qAô(öAô{Aô  Aóë
+Aó×
+AóÂAó®AóAó
+Aóp€Aó\)AóG®Aó33Aó
+žAó
+=AòõÃAòáHAòÌÍAòžRAò£×Aò\AòzáAòffAòQìAò=qAò(öAò{Aò  Añë
+Añ×
+AñÂAñ®AñAñ
+Añp€Añ\)AñG®Añ33Añ
+žAñ
+=AðõÃAðáHAðÌÍAðžRAð£×Að\AðzáAðffAðQìAð=qAð(öAð{Að  Aí
+=AìõÃAìáHAìÌÍAìžRAì£×Aì\AìzáAìffAìQìAì=qAì(öAì{Aì  Aëë
+Aë×
+AëÂAë®AëAë
+Aëp€Aë\)AëG®Aë33Aë
+žAë
+=AêõÃAêáHAêÌÍAêžRAê£×Aê\AêzáAêffAêQìAê=qAê(öAê{Aê  Aéë
+Aé×
+AéÂAé®AéAé
+Aép€Aé\)AéG®Aé33Aé
+žAé
+=AèõÃAèáHAèÌÍAèžRAè£×Aè\AèzáAèffAèQìAè=qAè(öAè{Aè  Aå
+=AäõÃAäáHAäÌÍAäžRAä£×Aä\AäzáAäffAäQìAä=qAä(öAä{Aä  Aãë
+Aã×
+AãÂAã®AãAã
+Aãp€Aã\)AãG®Aã33Aã
+žAã
+=AâõÃAâáHAâÌÍAâžRAâ£×Aâ\AâzáAâffAâQìAâ=qAâ(öAâ{Aâ  Aáë
+Aá×
+AáÂAá®AáAá
+Aáp€Aá\)AáG®Aá33Aá
+žAá
+=AàõÃAàáHAàÌÍAàžRAà£×Aà\AàzáAàffAàQìAà=qAà(öAà{Aà  AÝ
+=AÜõÃAÜáHAÜÌÍAÜžRAÜ£×AÜ\AÜzáAÜffAÜQìAÜ=qAÜ(öAÜ{AÜ  AÛë
+AÛ×
+AÛÂAÛ®AÛAÛ
+AÛp€AÛ\)AÛG®AÛ33AÛ
+žAÛ
+=AÚõÃAÚáHAÚÌÍAÚžRAÚ£×AÚ\AÚzáAÚffAÚQìAÚ=qAÚ(öAÚ{AÚ  AÙë
+AÙ×
+AÙÂAÙ®AÙAÙ
+AÙp€AÙ\)AÙG®AÙ33AÙ
+žAÙ
+=AØõÃAØáHAØÌÍAØžRAØ£×AØ\AØzáAØffAØQìAØ=qAØ(öAØ{AØ  AÕ
+=AÔõÃAÔáHAÔÌÍAÔžRAÔ£×AÔ\AÔzáAÔffAÔQìAÔ=qAÔ(öAÔ{AÔ  AÓë
+AÓ×
+AÓÂAÓ®AÓAÓ
+AÓp€AÓ\)AÓG®AÓ33AÓ
+žAÓ
+=AÒõÃAÒáHAÒÌÍAÒžRAÒ£×AÒ\AÒzáAÒffAÒQìAÒ=qAÒ(öAÒ{AÒ  AÑë
+AÑ×
+AÑÂAÑ®AÑAÑ
+AÑp€AÑ\)AÑG®AÑ33AÑ
+žAÑ
+=AÐõÃAÐáHAÐÌÍAÐžRAÐ£×AÐ\AÐzáAÐffAÐQìAÐ=qAÐ(öAÐ{AÐ  AÍ
+=AÌõÃAÌáHAÌÌÍAÌžRAÌ£×AÌ\AÌzáAÌffAÌQìAÌ=qAÌ(öAÌ{AÌ  AËë
+AË×
+AËÂAË®AËAË
+AËp€AË\)AËG®AË33AË
+žAË
+=AÊõÃAÊáHAÊÌÍAÊžRAÊ£×AÊ\AÊzáAÊffAÊQìAÊ=qAÊ(öAÊ{AÊ  AÉë
+AÉ×
+AÉÂAÉ®AÉAÉ
+AÉp€AÉ\)AÉG®AÉ33AÉ
+žAÉ
+=AÈõÃAÈáHAÈÌÍAÈžRAÈ£×AÈ\AÈzáAÈffAÈQìAÈ=qAÈ(öAÈ{AÈ  AÅ
+=AÄõÃAÄáHAÄÌÍAÄžRAÄ£×AÄ\AÄzáAÄffAÄQìAÄ=qAÄ(öAÄ{AÄ  AÃë
+AÃ×
+AÃÂAÃ®AÃAÃ
+AÃp€AÃ\)AÃG®AÃ33AÃ
+žAÃ
+=AÂõÃAÂáHAÂÌÍAÂžRAÂ£×AÂ\AÂzáAÂffAÂQìAÂ=qAÂ(öAÂ{AÂ  AÁë
+AÁ×
+AÁÂAÁ®AÁAÁ
+AÁp€AÁ\)AÁG®AÁ33AÁ
+žAÁ
+=AÀõÃAÀáHAÀÌÍAÀžRAÀ£×AÀ\AÀzáAÀffAÀQìAÀ=qAÀ(öAÀ{AÀ  Aœ
+=AŒõÃAŒáHAŒÌÍAŒžRAŒ£×AŒ\AŒzáAŒffAŒQìAŒ=qAŒ(öAŒ{AŒ  A»ë
+A»×
+A»ÂA»®A»A»
+A»p€A»\)A»G®A»33A»
+žA»
+=AºõÃAºáHAºÌÍAºžRAº£×Aº\AºzáAºffAºQìAº=qAº(öAº{Aº  A¹ë
+A¹×
+A¹ÂA¹®A¹A¹
+A¹p€A¹\)A¹G®A¹33A¹
+žA¹
+=AžõÃAžáHAžÌÍAžžRAž£×Až\AžzáAžffAžQìAž=qAž(öAž{Až  Aµ
+=AŽõÃAŽáHAŽÌÍAŽžRAŽ£×AŽ\AŽzáAŽffAŽQìAŽ=qAŽ(öAŽ{AŽ  A³ë
+A³×
+A³ÂA³®A³A³
+A³p€A³\)A³G®A³33A³
+žA³
+=A²õÃA²áHA²ÌÍA²žRA²£×A²\A²záA²ffA²QìA²=qA²(öA²{A²  A±ë
+A±×
+A±ÂA±®A±A±
+A±p€A±\)A±G®A±33A±
+žA±
+=A°õÃA°áHA°ÌÍA°žRA°£×A°\A°záA°ffA°QìA°=qA°(öA°{A°  A­
+=A¬õÃA¬áHA¬ÌÍA¬žRA¬£×A¬\A¬záA¬ffA¬QìA¬=qA¬(öA¬{A¬  A«ë
+A«×
+A«ÂA«®A«A«
+A«p€A«\)A«G®A«33A«
+žA«
+=AªõÃAªáHAªÌÍAªžRAª£×Aª\AªzáAªffAªQìAª=qAª(öAª{Aª  A©ë
+A©×
+A©ÂA©®A©A©
+A©p€A©\)A©G®A©33A©
+žA©
+=AšõÃAšáHAšÌÍAšžRAš£×Aš\AšzáAšffAšQìAš=qAš(öAš{Aš  A¥
+=A€õÃA€áHA€ÌÍA€žRA€£×A€\A€záA€ffA€QìA€=qA€(öA€{A€  A£ë
+A£×
+A£ÂA£®A£A£
+A£p€A£\)A£G®A£33A£
+žA£
+=A¢õÃA¢áHA¢ÌÍA¢žRA¢£×A¢\A¢záA¢ffA¢QìA¢=qA¢(öA¢{A¢  A¡ë
+A¡×
+A¡ÂA¡®A¡A¡
+A¡p€A¡\)A¡G®A¡33A¡
+žA¡
+=A õÃA áHA ÌÍA žRA £×A \A záA ffA QìA =qA (öA {A   A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Az{Ayë
+AyÂAyAyp€AyG®Ay
+žAxõÃAxÌÍAx£×AxzáAxQìAx(öAx  Aw×
+Aw®Aw
+Aw\)Aw33Aw
+=AváHAvžRAv\AvffAv=qAv{Auë
+AuÂAuAup€AuG®Au
+žAtõÃAtÌÍAt£×AtzáAtQìAt(öAt  As×
+As®As
+As\)As33As
+=AráHAržRAr\ArffAr=qAr{Aqë
+AqÂAqAqp€AqG®Aq
+žApõÃApÌÍAp£×ApzáApQìAp(öAp  Aj{Aië
+AiÂAiAip€AiG®Ai
+žAhõÃAhÌÍAh£×AhzáAhQìAh(öAh  Ag×
+Ag®Ag
+Ag\)Ag33Ag
+=AfáHAfžRAf\AfffAf=qAf{Aeë
+AeÂAeAep€AeG®Ae
+žAdõÃAdÌÍAd£×AdzáAdQìAd(öAd  Ac×
+Ac®Ac
+Ac\)Ac33Ac
+=AbáHAbžRAb\AbffAb=qAb{Aaë
+AaÂAaAap€AaG®Aa
+žA`õÃA`ÌÍA`£×A`záA`QìA`(öA`  AZ{AYë
+AYÂAYAYp€AYG®AY
+žAXõÃAXÌÍAX£×AXzáAXQìAX(öAX  AW×
+AW®AW
+AW\)AW33AW
+=AVáHAVžRAV\AVffAV=qAV{AUë
+AUÂAUAUp€AUG®AU
+žATõÃATÌÍAT£×ATzáATQìAT(öAT  AS×
+AS®AS
+AS\)AS33AS
+=ARáHARžRAR\ARffAR=qAR{AQë
+AQÂAQAQp€AQG®AQ
+žAPõÃAPÌÍAP£×APzáAPQìAP(öAP  AJ{AIë
+AIÂAIAIp€AIG®AI
+žAHõÃAHÌÍAH£×AHzáAHQìAH(öAH  AG×
+AG®AG
+AG\)AG33AG
+=AFáHAFžRAF\AFffAF=qAF{AEë
+AEÂAEAEp€AEG®AE
+žADõÃADÌÍAD£×ADzáADQìAD(öAD  AC×
+AC®AC
+AC\)AC33AC
+=ABáHABžRAB\ABffAB=qAB{AAë
+AAÂAAAAp€AAG®AA
+žA@õÃA@ÌÍA@£×A@záA@QìA@(öA@  A:{A9ë
+A9ÂA9A9p€A9G®A9
+žA8õÃA8ÌÍA8£×A8záA8QìA8(öA8  A7×
+A7®A7
+A7\)A733A7
+=A6áHA6žRA6\A6ffA6=qA6{A5ë
+A5ÂA5A5p€A5G®A5
+žA4õÃA4ÌÍA4£×A4záA4QìA4(öA4  A3×
+A3®A3
+A3\)A333A3
+=A2áHA2žRA2\A2ffA2=qA2{A1ë
+A1ÂA1A1p€A1G®A1
+žA0õÃA0ÌÍA0£×A0záA0QìA0(öA0  A*{A)ë
+A)ÂA)A)p€A)G®A)
+žA(õÃA(ÌÍA(£×A(záA(QìA((öA(  A'×
+A'®A'
+A'\)A'33A'
+=A&áHA&žRA&\A&ffA&=qA&{A%ë
+A%ÂA%A%p€A%G®A%
+žA$õÃA$ÌÍA$£×A$záA$QìA$(öA$  A#×
+A#®A#
+A#\)A#33A#
+=A"áHA"žRA"\A"ffA"=qA"{A!ë
+A!ÂA!A!p€A!G®A!
+žA õÃA ÌÍA £×A záA QìA (öA   A{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A
+{A	ë
+A	ÂA	A	p€A	G®A	
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žA õÃA ÌÍA £×A záA QìA (öA   @ô(ö@ó×
+@ó
+@ó33@òáH@ò\@ò=q@ñë
+@ñ@ñG®@ðõÂ@ð£×@ðQì@ð  @ï®@ï\)@ï
+=@îžR@îff@î{@íÂ@íp€@í
+ž@ìÌÍ@ìzá@ì(ö@ë×
+@ë
+@ë33@êáH@ê\@ê=q@éë
+@é@éG®@èõÃ@è£×@èQì@è  @ç®@ç\)@ç
+=@æžR@æff@æ{@åÂ@åp€@å
+ž@äÌÍ@äzá@ä(ö@ã×
+@ã
+@ã33@âáH@â\@â=q@áë
+@á@áG®@àõÃ@à£×@àQì@à  @Ô(ö@Ó×
+@Ó
+@Ó33@ÒáH@Ò\@Ò=q@Ñë
+@Ñ@ÑG®@ÐõÂ@Ð£×@ÐQì@Ð  @Ï®@Ï\)@Ï
+=@ÎžR@Îff@Î{@ÍÂ@Íp€@Í
+ž@ÌÌÍ@Ìzá@Ì(ö@Ë×
+@Ë
+@Ë33@ÊáH@Ê\@Ê=q@Éë
+@É@ÉG®@ÈõÃ@È£×@ÈQì@È  @Ç®@Ç\)@Ç
+=@ÆžR@Æff@Æ{@ÅÂ@Åp€@Å
+ž@ÄÌÍ@Äzá@Ä(ö@Ã×
+@Ã
+@Ã33@ÂáH@Â\@Â=q@Áë
+@Á@ÁG®@ÀõÃ@À£×@ÀQì@À  @Ž(ö@³×
+@³
+@³33@²áH@²\@²=q@±ë
+@±@±G®@°õÂ@°£×@°Qì@°  @¯®@¯\)@¯
+=@®žR@®ff@®{@­Â@­p€@­
+ž@¬ÌÍ@¬zá@¬(ö@«×
+@«
+@«33@ªáH@ª\@ª=q@©ë
+@©@©G®@šõÃ@š£×@šQì@š  @§®@§\)@§
+=@ŠžR@Šff@Š{@¥Â@¥p€@¥
+ž@€ÌÍ@€zá@€(ö@£×
+@£
+@£33@¢áH@¢\@¢=q@¡ë
+@¡@¡G®@ õÃ@ £×@ Qì@   @(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÂ@£×@Qì@  @®@\)@
+=@žR@ff@{@Â@p€@
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @®@\)@
+=@žR@ff@{@
+Â@
+p€@
+
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @hQì@g®@g
+>@fff@eÂ@e
+ž@dzá@c×
+@c33@b\@aë
+@aG®@`£×@`  @_\)@^žR@^{@]p€@\ÌÍ@\(ö@[
+@ZáH@Z=q@Y@XõÂ@XQì@W®@W
+>@Vff@UÂ@U
+ž@Tzá@S×
+@S33@R\@Që
+@QG®@P£×@P  @O\)@NžR@N{@Mp€@LÌÍ@L(ö@K
+@JáH@J=q@I@HõÃ@HQì@G®@G
+=@Fff@EÂ@E
+ž@Dzá@C×
+@C33@B\@Aë
+@AG®@@£×@@  @(Qì@'®@'
+>@&ff@%Â@%
+ž@$zá@#×
+@#33@"\@!ë
+@!G®@ £×@   @\)@
+žR@
+{@
+p€@
+ÌÍ@
+(ö@
+@áH@=q@@õÂ@Qì@®@
+>@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@£×@  @\)@žR@{@
+p€@
+ÌÍ@
+(ö@
+
+@
+áH@
+=q@	@õÃ@Qì@®@
+=@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@ £×@   ?Ð£×?Ï\)?Î{?ÌÌÍ?Ë
+
+?Ê=p?ÈõÂ?Ç®?Æff?Å
+ž?Ã×
+?Â\?ÁG®?À  ?ŸžR?œp€?Œ(ö?ºáH?¹?žQì?·
+>?µÂ?Žzá?³33?±ë
+?°£×?¯\)?®{?¬ÌÍ?«
+?ª=q?šõÂ?§®?Šff?¥
+ž?£×
+?¢\?¡G®?   ?žR?p€?(ö?áH??Qì?
+>?Â?zá?33?ë
+?£×?\)?{?ÌÍ?
+?=q?õÃ?®?ff?
+
+ž?×
+?\?G®?  ?!G®?
+žR?
+(ö??
+=?zá?ë
+?\)?
+ÌÍ?
+=q?®?
+ž?\?   >úáH>õÂ>ð£×>ë
+>æff>áG®>Ü(ö>×
+=>Ñë
+>ÌÌÍ>Ç®>Â\>œp€>žQì>³33>®{>šõÃ>£×
+>žR>>zá>\)>=q>
+
+ž>  >uÂ>k
+>aG®>W
+=>LÌÍ>B\>8Qì>.{>#×
+>>\)>
+ž=õÂ=áG®=ÌÌÍ=žQì=£×
+=\)=uÂ=LÌÍ=#×
+<õÂ<£×
+<#×
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~bB~Æ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BxºrB{-B~[B~vK¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BsKBuæBx²ÜB{³B~LB~gÏ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bmj#Bp=ÞBs
+ŽBu×Bx€aB{q7B~>B~YT¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BgÁûBj¶BmbBp/cBrü9BuÉBxæB{bœB~/B~JÙ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BbÓBdíBgºdBj;BmTBp çBríŸBuºBxkB{TAB~!B~<^¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B\q«B_EfBb<BdßBg«éBjx¿BmEBpmBrßCBu¬BxxðB{EÆB~B~-ã¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BVÉBY?B\jB_6ìBbÂBdÐBgoBjjFBm7
+BpòBrÐÉBuBxjuB{7LB~"B~g¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BQ!\BSõBVÁíBYÄB\[B_(pBaõGBdÂ
+BgôBj[ÊBm(¡BoõwBrÂMBu$Bx[úB{(ÐB}õ§B~ì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BKy4BNLïBQÅBSæBV³rBYHB\MB_öBaæÌBd³¢BgyBjMOBm%BoæüBr³ÒBušBxMB{UB}ç+B~q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BEÑ
+BH€ÇBKqBN>tBQ
+JBSØ BV€÷BYqÎB\>€B_
+zBaØQBd¥'BgqýBj>ÔBm
+ªBoØBr¥WBur-Bx?B{
+ÚB}Ø°B}óö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B@(äBBüBEÉuBHLBKc"BN/øBPüÏBSÉŠBV|BYcRB\0)B^üÿBaÉÕBd¬BgcBj0XBlý/BoÊBrÛBuc²Bx0Bzý_B}Ê5B}åz¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B:œB=TxB@!NBBî%BEºûBHÑBKTšBN!BPîUBS»+BVBYTØB\!®B^î
+Ba»[Bd1BgUBj!ÞBlîŽBo»BraBuU8Bx"BzîäB}»»B}Öÿ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B4ØB7¬PB:y&B=EýB@ÓBBß©BE¬BHyWBKF-BNBPßÚBS¬°BVyBYF]B\3B^à	Ba¬àBdy¶BgFBjcBlà9Bo­BryæBuFŒBxBzàiB}­?B}È¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B/0mB2(B4ÐþB7ÕB:j«B=7B@XBBÑ/BEBHjÛBK7²BNBPÑ^BS5BVk
+BY7áB\žB^ÑBadBdk;Bg8BjèBlÑŸBoBrkkBu8ABxBzÑîB}ÄB}º¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B)EB,\ B/(ÖB1õ­B4ÂB7YB:\0B=)B?õÝBBÂ³BEBH\`BK)6BMö
+BPÂãBS¹BV\BY)fB[ö<B^ÃBaéBd\ÀBg)BiölBlÃCBoBr\ïBu)ÆBwöBzÃrB}HB}«¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B#à
+B&³ØB)®B,M
+B/[B1ç1B4ŽB7ßB:MµB=B?çbBBŽ8BEBHMåBK»BMçBPŽhBS>BVNBYëB[çÁB^ŽBanBdNDBgBiçñBlŽÇBoBrNtBuJBwè BzŽ÷B}ÍB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+7õB!
+°B#ØB&¥]B)r3B,?	B/
+àB1Ø·B4¥B7rcB:?:B=
+B?ØæBB¥œBErBH?iBK
+@BMÙBP¥ìBSrÃBV?BY
+pB[ÙFB^Š
+BaróBd?ÉBg
+BiÙvBlŠLBos"Br?øBu
+ÏBwÙ¥BzŠ|B}sRB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BÎBcB
+0_B ý6B#Ê
+B&âB)c¹B,0B.ýfB1Ê<B4B7céB:0¿B<ýB?ÊlBBBBEdBH0ïBJýÅBMÊBPrBSdIBV1BXýõB[ÊÌB^¢BadxBd1OBfþ%BiÊûBlÑBodšBr1~BtþUBwË+BzB}dØB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BçŠB»aB7BUB
+!äB îºB#»B&hB)U>B,"B.îëB1»ÁB4B7UnB:"DB<ïB?»ñBBÇBEUBH"tBJïJBMŒ!BP÷BSUÍBV"€BXïzB[ŒPB^'BaUýBd"ÓBfï©BiŒBlVBoV-Br#BtïÙBwŒ°BzB}V\B}q ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+?~B9BàB¬æByŒBFB
+iB à@B#­B&yìB)FÃB,B.àoB1­FB4z
+B7FòB:ÉB<àB?­uBBzLBEG"BHùBJàÏBM­¥BPz|BSGRBV(BXàÿB[­ÕB^z«BaGBdXBfá.Bi®BlzÛBoG±BrBtá^Bw®4Bz{
+B}GáB}c$¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BVB
+kB
+7çBŸBÑBjBkAB8B
+îB ÑÄB#B&kqB)8GB,
+B.ÑôB1ÊB4k¡B78wB:MB<Ò$B?úBBkÑBE8§BH}BJÒTBM*BPl BS8×BV­BXÒB[YB^l0Ba9BdÝBfÒ³BiBll`Bo96Br
+BtÒãBw¹BzlB}9fB}Tª¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bï.BÂéB¿B
+\B
+)lBöBBÃBðB\ÆB)B
+ösB ÃIB#B&\öB))ÌB+ö¢B.ÃyB1OB4]%B7)üB9öÒB<Ã©B?BB]UBE*,BG÷BJÃØBM¯BP]
+BS*[BU÷1BXÄB[ÞB^]µBa*Bc÷aBfÄ8BiBl]äBo*»Bq÷BtÄgBw>Bz^B}*ëB}F.¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aø
+Aþ5BçBŽnBDB
+NB
+ñBçÈBŽBtBNKB!B
+ç÷B ŽÎB#€B&NzB)QB+è'B.ŽýB1ÔB4NªB7B9èWB<µ-B?BBNÚBE°BGèBJµ]BM3BPO	BSàBUè¶BXµB[cB^O9Ba
+BcèæBfµŒBiBlOiBo
+?BqéBtµíBwÃBzOB}
+pB}7³¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aí=ŒAòå3Aø~àAþBÙ
+B¥òBrÉB
+? B
+
+vBÙLBŠ#BrùB?ÏB
+ŠB
+Ù|B ŠRB#s)B&?ÿB)
+ÕB+Ù¬B.ŠB1sYB4@/B7
+B9ÙÜB<Š²B?sBB@_BE
+5BGÚ
+BJŠáBMsžBP@BS
+eBUÚ;BX§B[sèB^@ŸBa
+BcÚkBf§ABitBl@îBo
+ÅBqÚBt§qBwtHBzA
+B}
+ôB})8¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AáílAçãAí.AòÈ=AøaêAýûBÊ¡BxBdNB
+1$B
+ýûBÊÑB§Bd~B1TBþ*B
+ËB ×B#d­B&1B(þZB+Ë1B.B1dÝB41ŽB6þB9Ë`B<7B?e
+BB1ãBDþ¹BGËBJfBMe=BP2BRþéBUËÀBXB[elB^2CB`ÿBcËïBfÆBieBl2sBnÿIBqÌ BtöBweÌBz2£B|ÿyB}Œ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÖ
+AÜDAáÞ@AçwíAíAò«GAøDóAýÞ BŒ&BüBUÓB
+"©B
+ïBŒVB,BVB"ÙBï¯B
+Œ
+B \B#V2B&#	B(ïßB+ŒµB.B1VbB4#8B6ðB9ŒåB<»B?VBB#hBDð>BGœBJëBMVÁBP#BRðnBUœDBXB[VñB^#ÇB`ðBcœuBfKBiW!Bl#øBnðÎBqœ€Bt{BwWQBz$'B|ðþB}
+A¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AËLÌAÐôCAÖðAÜ'AáÁJAçZ÷Aìô£AòPAø'üAýÁ©B­«BzBGWB
+.B
+áB­ÚBz±BGB]Bá4B
+®
+B záB#G·B&B(ádB+®:B.{B1GçB4œB6áB9®iB<{@B?HBBíBDáÃBG®BJ{pBMHFBP
+BRáóBU®ÉBX{B[HvB^MB`â#Bc®ùBf{ÐBiHŠBl|BnâSBq¯)Bt{ÿBwHÖBz¬B|âB|ýÆ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A¿ü|AÅ£óAË= AÐ×MAÖpúAÜ
+§Aá€SAç> Aì×¬AòqYAø
+Aý€²B/BlB8ÜB
+²B
+ÒB_Bl5B9
+BâBÒ¹B
+B leB#9<B&B(ÒèB+¿B.lB19kB4AB6ÓB9îB<lÅB?9BBqBDÓHBG 
+BJlôBM9ËBP¡BRÓwBU NBXm%B[9ûB^ÑB`ÓšBc ~BfmTBi:+BlBnÓ×Bq ®BtmBw:ZBz1B|ÔB|ïJ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AŽ¬,AºS£A¿íPAÅýAË ªAÐºWAÖTAÛí°Aá\Aç!	Aìº¶AòTbA÷îAýŒBŽB]B*aB	÷7B
+Ä
+BäB]ºB*B÷gBÄ=B
+B ]êB#*ÀB%÷B(ÄmB+CB.^B1*ðB3÷ÆB6ÄB9sB<^IB?+ BA÷öBDÄÌBG£BJ^yBM+OBOø&BRÄýBUÓBX^©B[+B]øVB`Å,BcBf^ÙBi+¯BkøBnÅ\Bq2Bt_	Bw+ßByøµB|ÅB|àÏ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A©[ÜA¯SAŽ Aº6­A¿ÐZAÅjAË³AÐ`AÖ7
+AÛÐ¹AájfAçAì¿Aò7lA÷ÑAýjÆB9BOBåB	èŒB
+µBiBO?B
+BèìBµÂB
+B OoB#
+EB%éB(µñB+ÈB.OB1
+uB3éKB6¶!B9øB<OÎB?
+€BAé{BD¶QBG'BJOþBM
+ÕBOé«BR¶BUXBXP.B[
+B]éÛB`¶±BcBfP^Bi
+4Bkê
+Bn¶áBq·BtPBw
+dByê:B|·B|ÒT¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+A£³A©L¯A®æ\AŽ	Aº¶A¿³bAÅMAÊæ»AÐhAÖAÛ³ÁAáMnAæçAìÈAòuA÷Ž"AýMÎBsœB@B
+jB	ÚAB
+§BsíB@ÄB
+BÚpB§GB
+t
+B @óB#
+ÉB%Ú B(§vB+tMB.A#B1
+ùB3ÚÐB6§ŠB9t|B<ASB?)BAÚÿBD§ÖBGt­BJABMYBOÛ0BRšBUtÜBXA³B[B]Û_B`š6Bcu
+BfAâBi¹BkÛBnšeBqu<BtBBwèByÛ¿B|šB|ÃØ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A»;Ab²Aü_A£
+A©/¹A®ÉfAŽcA¹ü¿A¿kAÅ0AÊÉÅAÐcqAÕý
+AÛËAá0xAæÊ%AìcÒAñý~A÷+Aý0×BeBB2BþïB	ËÅB
+BerB2HBÿBËõBËB
+e¡B 2xB"ÿNB%Ì%B(ûB+eÑB.2šB0ÿ~B3ÌTB6+B9fB<2×B>ÿ®BAÌ
+BD[BGf1BJ3BLÿÞBOÌŽBRBUfaBX37B[ B]ÌäB`ºBcfBf3gBi =BkÍBnêBqfÀBt3Bw mByÍCB|B|µ]¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AjëAbA¬AEŒAßiA£yA©ÂA®¬oAŽFA¹ßÈA¿yuAÅ!AÊ¬ÎAÐF{AÕà(AÛyÕAáAæ­.AìFÛAñàA÷z4AýáBVÇB#BðtB	œJB
+ BV÷B#ÍBð£BœyBPB
+W&B #ýB"ðÓB%œ©B(B+WVB.$,B0ñB3œÙB6¯B9WB<$]B>ñ3BAŸ	BDàBGW¶BJ$BLñcBOŸ9BRBUWæBX$ŒBZñB]ŸiB`?BcXBf$ìBhñÂBkŸBnoBqXEBt%BvñòByŸÈB|B|Šâ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Ax58AÂA[¿AõlAA(ÆAÂrA£\AšõËA®xAŽ)%A¹ÂÑA¿\~AÄö+AÊØAÐ)
+AÕÃ2AÛ\ÞAàöAæ7Aì)äAñÃA÷]=AüöêBHLB"BáøB	®ÏB
+{¥BH{BQBâ(B®þB{ÕB
+H«B B"âXB%¯.B(|B+HÛB.±B0âB3¯^B6|5B9I
+B<áB>âžBA¯BD|dBGI;BJBLâçBO¯ŸBR|BUIjBXABZãB]¯íB`|ÄBcIBfpBhãGBk°
+Bn|óBqIÊBt BvãvBy°MB|}$B|g¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AaAlãAxßA¥
+A>ÉAØvAr"A
+ÏA¥{A£?(AšØÕA®rAŽ
+.A¹¥ÛA¿?AÄÙ5AÊrâAÐ
+AÕŠ;AÛ?çAàÙAæsAAì
+íAñŠA÷@GAüÙóB9ÐB§BÓ}B	 SB
+m)B: BÖBÓ­B BmYB
+:0B B"ÓÜB% ³B(mB+:_B.6B0Ô
+B3 ãB6m¹B9:B<fB>Ô<BA¡BDméBG:¿BJBLÔlBO¡BBRnBU:ïBXÅBZÔB]¡rB`nHBc;BfõBhÔËBk¡¢BnnxBq;NBt%BvÔüBy¡ÒB|nšB|ì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AJóûAVBèAav@Al©AwÜñA&A!ÒA»AU+AîØA
+A£"1Aš»ÞA®UA³ï8A¹åA¿"AÄŒ>AÊUëAÏïAÕDAÛ"ñAàŒAæVJAëï÷Añ£A÷#QAüŒýB+UBø+BÅB	ØB
+^®B+
+Bø[BÅ1BB^ÞB
++ŽBøB"ÅaB%7B(_B++åB-ø»B0ÅB3hB6_>B9,B;øëB>ÅÁBABD_nBG,DBIùBLÅñBOÇBR_BU,tBWùJBZÆ B]÷B`_ÍBc,£BeùzBhÆPBk&Bn_ýBq,ÔBsùªBvÆByWB|`-B|{p¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A4S]A?¢JAJÕ¢AVûAa<SAlo¬Aw£Ak/AÛAA85AÑáAkA£;AšèA®8A³ÒBA¹kîA¿AÄGAÊ8ôAÏÒ¡AÕlMAÛúAà§Aæ9SAëÓAñl®A÷ZAü B
+ÙBé°B¶B	]B
+P3B
+	BéàB¶¶BBPcB
+
+9BêB"¶æB%œB(PB+
+iB-ê@B0·B3ìB6PÃB9
+B;êoB>·FBA
+BDPòBG
+ÉBIêBL·uBOLBRQ"BU
+øBWêÏBZ·¥B]{B`QRBc
+(BeêþBh·ÕBk¬BnQBq
+XBsë/BvžByÛB|Q²B|lõ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+²¿A)¬A45A?h]AJµAUÏAafAl5¿AwiAN8AçåAA>AŽëANA¢èEAšòA®A³µKA¹N÷AŸè€AÄQAÊýAÏµªAÕOWAÚéAà±Aæ
+^Aë¶
+AñO·AöécAüB^BÛ5Bš
+B	táB
+AžBBÛdBš;BuBAçB
+ŸBÛB"škB%uAB(BB+îB-ÛÄB0šB3uqB6BGB9
+B;ÛôB>šÊBAu¡BDBwBGMBIÜ$BLšúBOuÐBRB§BU}BWÜSBZ©*B]v B`BÖBc­BeÜBh©ZBkv0BnCBqÝBsÜ³Bv©Byv`B|C6B|^z¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A!AaA
+fA(Ç¿A3ûA?.pAJaÈAU!A`ÈyAkûÑAw/*A1BAÊîAdAþHAõA1¢A¢ËNAšdûA­þ§A³TA¹2AŸË­AÄeZAÉÿAÏ³AÕ2aAÚÌAàeºAåÿgAëAñ2ÀAöÌmAüfB ÿãBÌ¹BB	ffB
+3<B BÌéB¿BfB3mB
+ CBÍB"ðB%fÆB(3B+ sB-ÍIB0B3föB63ÌB9 ¢B;ÍyB>OBAg%BD3üBG ÒBIÍšBLBOgUBR4+BUBWÍØBZ®B]g
+B`4\Bc2BeÎBhßBkgµBn4BqbBsÎ8BvBygåB|4»B|Oÿ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @àã@÷ÞAóÇA' A
+ZxA(ÑA3Á)A>ôAJ'ÚAU[2A`AkÁäAvõ<AJA­÷AG€AáQAzýAªA¢®VAšHA­á°A³{\A¹	AŸ®¶AÄHbAÉâAÏ{œAÕiAÚ¯AàHÂAåâoAë|
+AñÈAö¯uAüI"B ñgBŸ=BB	WêB
+$ÀBñBŸmBDBXB$ðB
+ñÇBŸB"sB%XJB(% B*ñöB-ŸÍB0£B3XyB6%PB8ò&B;ŸüB>ÓBAX©BD%BFòVBI¿,BLBOXÙBR%¯BTò
+BW¿\BZ3B]Y	B`%ßBbò¶Be¿BhbBkY9Bn&BpòåBs¿ŒBvByYiB|&?B|A¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @³¡È@Ê?¢@àŠS@÷
+A¹ÚAí3A
+ A(SäA3<A>ºAIííAU!FA`TAköAv»OA÷TAA*­AÄZA^A÷³A¢`Aš+
+A­Ä¹A³^fAžøAŸÀAÄ+mAÉÅAÏ^ÆAÔørAÚAà+ÌAåÅxAë_%AðøÒAö~Aü,+B âëB¯ÂB|B	InB
+EBã
+B¯òB|ÈBIBuB
+ãKB°"B"|øB%IÎB(¥B*ã{B-°QB0}(B3IþB6ÕB8ã«B;°B>}WBAJ.BDBFãÚBI°±BL}BOJ]BR4BTä
+BW°áBZ}·B]JB`dBbä:Be±Bh}çBkJœBnBpäjBs±ABv~ByJíB|ÄB|3¿  ¿  ¿  ¿  ¿  ¿  @`@þe@³e@ÉËÇ@à2x@ö)AíA³FA
+æA(öA3MOA>šAIŽ ATçXA`±AkN	AvbAÚ]At
+A
+·A§cAAAÚŒA¢tiAšA­§ÂA³ApAžÛ
+AŸtÉAÄvAÉš"AÏAÏAÔÛ|AÚu(AàÕAåšAëB.AðÛÛAöuAü5B ÔpB¡FBn
+B	:ôB
+ÊBÔ B¡wBnMB;#BúB
+ÔÐB¡ŠB"n}B%;SB()B*Õ B-¡ÖB0n­B3;B6YB8Õ/B;¢B>nÜBA;²BDBFÕ_BI¢5BLo
+BO;ãBR¹BTÕBW¢fBZo<B]<B`éBbÕ¿Be¢BholBk<BBn	BpÕïBs¢ÅBvoBy<rB|	HB|$¿  ¿  ¿  ¿  @2>@_zS@#Ú@@²ñ<@ÉWì@ßŸ@ö%OAF AyXA
+¬±A'à
+A3bA>FºAIzAT­kA_àÄAk
+AvGuAœgAWAðÀAlA$AœÆA¢WrA§ñ A­ÍA³$yAžŸ&AŸWÒAÃñAÉ,AÏ$ØAÔŸ
+AÚX2AßñÞAåAë%9AðŸåAöXAûò>B ÅõBÌB_¢B	,xB
+ùOBÆ%BûB_ÒB,šBù~B
+ÆUB+B"`B%,ØB'ù®B*Æ
+B-[B0`1B3-B5ùÞB8ÆŽB;B>`aBA-7BCú
+BFÆäBI»BL`BO-gBQú>BTÇBWêBZ`ÁB]-B_úmBbÇDBeBh`ñBk-ÇBmúBpÇtBsJBva By-÷B{úÍB|¿  ¿  ?¯xI@÷Ú@1Å<@^@
+° @°@²}b@Èä@ßJÃ@õ±tA
+A?lA
+rÄA'Š
+A2ÙuA>
+ÍAI@&ATs~A_Š×AjÚ/Av
+A pA:
+AÓÉAmvA"A ÐA¢:}A§Ô)A­mÖA³Až¡/AŸ:ÜAÃÔAÉn5AÏâAÔ¡AÚ;;AßÔéAånAëBAð¡îAö;AûÕHB ·zBPBQ'B	
+ýB
+êÓB·ªBBQVB
+-BëB
+·ÙB°B"QB%
+]B'ë3B*ž	B-ßB0Q¶B3
+B5ëbB8ž9B;
+B>QåBA
+ŒBCëBFžiBI
+?BLRBO
+ìBQëÂBTžBW
+oBZREB]
+B_ëòBbžÉBe
+BhRuBkLBmì"BpžøBs
+ÏBvR¥By{B{ìQB|?!G®?)Õ?®
+@$@0Ý@]ªé@
+<%@¢×@²	@Èp8@ÞÖé@õ=AÒ&A~A
+8×A'l/A2A=ÒàAI9AT9A_léAj BAuÓAyA
+&A¶ÒAPAê-AÙA¢
+A§·2A­PßA²êAž8AŸ
+åAÃ·AÉQ>AÎêëAÔAÚ
+EAß·òAåQAêëKAðøAö
+€AûžQB šÿBuÕBB«B	B
+ÜXB©.BvBBÛB±BÜB
+©^Bv5B"C
+B%áB'Ü·B*©B-vdB0C:B3B5ÜçB8©œB;vB>CkBAABCÝBF©îBIvÄBLCBOqBQÝGBTª
+BWvôBZCÊB]¡B_ÝwBbªMBew$BhCúBkÐBmÝ§Bpª}BswSBvD)By B{ÝÖ¿  ?b(?&6¬?¬¶@(p@/õÒ@\Ã4@ÈK@.ü@±­@Çü^@Þc@ôÉÀA9AËAþêA'2BA2eA=óAHÌKASÿ€A_2üAjfUAu­AfA 0AÝA3AÍ6AfâA¢ A§<A­3èA²ÍAžgBAŸ îAÃAÉ4IAÎÍõAÔg¢AÚNAßûAå4šAêÎTAðhAö®AûZB BgZB40B	B
+ÍÝB³BgB4`B6BÎ
+B
+ãBg¹B"4B%fB'Î<B*B-géB04¿B3B5ÎlB8CB;hB>4ïBAÆBCÎBFrBIhIBL5BOõBQÎÌBT¢BWhyBZ5OB]%B_ÎüBbÒBehšBh5BkUBmÏ+BpBshØBv5®By	j¿  ¿  ?ÃV?"×?ªæ°@@º@/
+@[Û~@Tp@»!@±!Ò@Ç@Ýï3@ôUäA^KA£AÄüA&øTA2+¬A=_AH]ASÅ¶A^ùAj,gAu_¿AIA
+ã9A|æAA°?AIìA¡ãA§}EA­òA²°AžJKAœãùAÃ}¥AÉRAÎ°þAÔJ«AÙäXAß~Aå±Aê±^AðK
+Aõä·Aû~eB BXÞB%µBòB
+¿aB8BYB%åBò»B¿B
+gBY>B"&B$òêB'¿ÁB*B-YmB0&DB2óB5¿ñB8ÇB;YB>&tB@óJBCÀ!BF÷BIYÍBL&€BNózBQÀQBT'BWYýBZ&ÔB\óªB_ÀBbWBeZ-Bh'BjóÙBmÀ°BpBsaB¿  ¿  ¿  ¿  ?$?
+ù?©F@Y@.&h@ZóÊ@à@GG@°­÷@Çš@Ý{Y@óâ
+A$^AW¶AA&ŸgA1ñ¿A=%AHXpASÉA^¿!AiòyAu%ÒA,A
+ÆCA_ïAùAHA,õA¡Æ¢A§`NA¬ùûA²©Až-UAœÇAÃ`¯AÈú[AÎAÔ-ŽAÙÇaAßaAäúºAêgAð.AõÇÁAûanB }BJcB9BäB
+°æB}œBJBiBä?B±B
+}ìBJÂB"B$äoB'±EB*~
+B-JóB0ÉB2äB5±vB8~LB;K"B>ùB@äÏBC±¥BF~|BIKRBL)BNäÿBQ±ÕBT~¬BWKBZXB\å/B_²Bb~ÛBeK±BhBjå^Bm¹¿  ¿  ¿  ¿  ¿  ¿  ?
+²?Z1?§GÝ@ qQ@->³@Z
+@l»@Ól@°:
+@Æ Î@Ý@ón0AêpA
+ÉAQ!A&zA1·ÒA<ë+AH
+ASQÛA^
+4AižAtëåAA
+©LABøAÜ¥AvRAþA¡©«A§CYA¬ÝA²v²Až_Aœª
+AÃCžAÈÝdAÎwAÔŸAÙªjAßDAäÝÅAêwqAð
+AõªÊAûDwB oB;èBŸBÕB
+¢kBoAB<BîBÕÄB¢B
+oqB<GB"	
+B$ÕôB'¢ËB*o¡B-<wB0	NB2Ö$B5¢úB8oÑB;<§B>	}B@ÖTBC£*BFpBI<×BL	­BNÖBQ£ZBTp0BW=BZ	ÝB\Ö³B_£Bbp`Be=6Bhò¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?æá?»X?¥xp?ÿ5@,Vý@Y$_@øà@_@¯ÆC@Æ,ó@Ü€@òúTA°AãÜA4A&JA1}åA<±=AGäASïA^KGAi~At±øAåQA
+UA&A¿®AY[Aó	A¡µA§&bA¬ÀA²Y»A·óhAœAÃ&ÁAÈÀnAÎZAÓóÇAÙuAß'!AäÀÎAêZzAïô'AõÔAû'B `B-mBúCBÇB
+ïB`ÆB-BúrBÇIBB
+`õB-ÌB!ú£B$ÇyB'OB*a&B--üB/úÒB2Ç©B5B8aUB;.,B=ûB@ÇÙBC¯BFa
+BI.\BKû2BNÈBQßBTaµBW.BYûaB\È8B_BbhÊ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+H?
+?£©?ýCÌ@+oI@X<ª@
+@ë·@¯Rh@Å¹@ÜÊ@òzAvA©ïAÝGA&A1CøA<wQAGª©ARÞA^ZAiD²Atx
+A«dA
+o^A	
+A¢¹A<eAÖA¡o¿A§	kA¬£A²<ÄA·ÖqAœp
+AÃ	ÊAÈ£wAÎ=%AÓÖÑAÙp~Aß
+*Aä£×Aê=Aï×0AõpÝAû
+B RB
+ñBëÇBžB
+
+tBRJB!Bë÷BžÍB
+€B
+R{BQB!ì'B$žþB'
+ÔB*RªB-B/ìWB2¹-B5B8RÚB;±B=ìB@¹]BC4BFS
+BIàBKì·BN¹BQcBTS9BW BYìæB\À¢¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?	©>?}¬?¡Ù?ût_@*@WTô@+@wÛ@®Þ@ÅE=@Û«î@òA<šAp A£YA%Ö²A1
+
+A<=bAGp»AR€A]×lAi
+ÄAt>
+AquA
+RhAìA
+ÁAnA¹A¡RÇAŠìsA¬ A²ÍA·¹yAœS&AÂìÔAÈAÎ -AÓ¹ÚAÙSAÞí3AäßAê Aïº9AõSåAúíB CBvBÝLBª"B
+vùBCÏB¥BÝ|BªSBw)B
+CÿBÖB!Ý¬B$ªB'wYB*D/B-B/ÝÜB2ª²B5wB8D_B;5B=Þ
+B@ªâBCwžBFDBIeBKÞ;BN«BQwèBTDŸBWz¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+k?
+ÞÚ? 
+2?ù€ö@)Þ@Vm@@Q@@®j²@ÄÑc@Û8@ñÅA»A6AilA%ÄA0Ð
+A<uAG6ÎARj&A]AhÐ×At/A7A
+5qAÏ
+AhÊAwA#A¡5ÐAŠÏ}A¬i)A²ÖA·Aœ60AÂÏÝAÈiAÎ6AÓãAÙ6AÞÐ<AäiéAêAïBAõ6ïAúÐB 5$BúBÎÑB§B
+h}B5TB+BÏB×Bh®B
+5BZB!Ï1B$B'hÝB*5ŽB-B/ÏaB27B5i
+B85äB;ºB=ÏB@gBCi=BF6BIéBKÏÀBNBQpR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?k?	@?:Å?÷Õ@(ž(@U
+@)u@&@­ö×@Ä]@ÚÄ9@ñ*êAÈÎAü&A/A%b×A00A;ÉAFüáAR09A]cAhêAsÊBA~ýA
+zA²'AKÓAåA-A¡ÙAŠ²A¬L4A±åàA·Aœ:AÂ²æAÈLAÍæ?AÓìAÙAÞ³EAäLòAéæAïLAõùAú³¥B &©BóBÀUB,B
+ZB&ÙBó¯BÀB\BZ2B
+'	B
+óßB!ÀµB$B'ZbB*'9B,ôB/ÀåB2ŒB5ZB8'hB:ô?B=ÁB@ëBCZÁBF'BHônBKÈ*¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ý?¡/?k\?ö!@'Ðs@TÕ@µ@
+L@­ý@Ãé®@ÚP_@ð·AáAÂ9AõA%(êA0\CA;AFÂóAQöLA])€Ah\ýAsUA~Ã®AûA0A.ÝAÈAb6A ûäAŠA¬/=A±ÈêA·bAŒüCAÂïAÈ/AÍÉIAÓbõAØü¢AÞOAä/üAéÉ©AïcUAôýAú¯B -BåB±ÛB~±B
+KB^Bå4B²
+B~áBK·B
+B
+ådB!²:B$B'KçB*œB,åB/²jB2@B5LB8íB:åÃB=²B@pBCLFBF ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ö[ë?U?ï?ô6Ž@&èœ@S¶
+@AÀ@šq@­"@ÃuÓ@ÙÜ@ðC4ATóAKA»€A$îüA0"TA;U­AFAQŒ^A\ï¶Ah#AsVgA~ÀAÞAx9AæA«AE@A ÞíAŠxA¬FA±«óA·EAŒßLAÂxùAÈ¥AÍ¬RAÓEÿAØß¬AÞyYAäAé¬²AïF_Aôà
+AúyžB 	³BÖB£_Bp6B
+=
+B	âBÖ¹B£BpeB=<B
+
+B
+ÖéB!£¿B$pB'=lB*
+BB,×B/£ïB2pÅB5=B8
+qB:×HB=€
+B@wÚ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ï
+I>üÇ?Ì?ògK@&@RÎj@Ì@4@¬G@Ãù@Ùh©@ïÏZAAN^A¶A$µA/ègA;ÀAFOAQqA\µÉAgé!As
+zA~OÓAÁA[DAôðAA(JA ÁöAŠ[£A«õOA±üA·(©AŒÂUAÂ\AÇõ¯AÍ\AÓ)	AØÂ¶AÞ\bAãöAé»Aï)hAôÃAú\ÁAÿönBÈBäBaºB
+.B
+ûgBÈ=BBaêB.ÁBûB
+ÈmB!DB$bB'.ðB)ûÇB,ÈB/sB2bIB5/ B7ûöB:Ï²¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >çà¥>õc?ý
+?ðâ@%T@Qæ¶@~Ž@Àœ@¬'m@Â
+@ØôÏ@ï[AáAqAGÉA${"A/®zA:áÓAF+AQHA\{ÜAg¯5ArâA~çA€ A>MA×úAqŠA
+SA €ÿAŠ>¬A«ØYA±rA·
+²AŒ¥_AÂ?
+AÇØ¹AÍrfAÓ
+AØ¥¿AÞ?kAãÙAérÅAï
+qAôŠ
+Aú?ËAÿÙxB¹BiBS?B
+ B
+ììB¹ÂBBSoB EBí
+B
+¹òB!ÈB$SB' uB)íKB,º!B/øB2SÎB5'¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >à£>îK ?-¬?îÈq@$1@Pþý@}Ì_@Là@«³@ÂB@Øó@îç€A§*A
+ÚA
+ÛA$A4A/tA:§äAEÛ=AQA\AîAguFAršA}ÛøA©A!UA»AT®Aî[A AŠ!ŽA«»aA±UA¶î»AŒhAÂ"AÇ»ÁAÍUnAÒïAØÇAÞ"tAãŒ AéUÍAîïzAô&Aú"ÔAÿŒB«BwìBDÃB
+B
+ÞpB«FBx
+BDóBÉBÞB
+«vB!xLB$E"B'øB)ÞÏB,«¥B/a¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ùe]>ç
+û?^C?ìù@#Iç@PH@|äª@Ù@«?·@ÁŠh@Ø
+@îsÊAm=A
+ AÓîA$FA/:A:møAE¡PAPÔšA\Ag;YArn²A}¢
+Aj²A^A
+A7žAÑdA kAŠŸA«kA±8A¶ÑÅAŒkqAÂ
+AÇÊAÍ8wAÒÒ$AØkÐAÞ}Aã*Aé8ÖAîÒAôl1AúÝAÿBBiqB6HB
+
+B
+ÏôBËBi¡B6wBNBÐ$B
+úB!iÐB$6§B'}B)×9¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ò'»>ßÐY?Û?ë)@"b3@O/@{üö@e,@ªËÝ@Á2@×?@íÿïA3PA
+fšAA#ÍZA/ ²A:4
+AEgcAP»A[ÎAglAr4ÅA}h
+AM»AçhAAÁAŽnA NA¥çÈA«uA±!A¶ŽÎAŒNzAÁè'AÇÔAÍAÒµ-AØNÚAÝèAã4AéáAîµAôO:AùèæAÿB BZöB'ÌB
+ô£B
+ÁyBOB[&B'üBôÒBÁšB
+B![UB$/¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Êê>Øµ?¿r?éZ6@!z~@NGà@{A@ñR@ªX@ÀŸ³@×%d@íAùcA
+,ŒA`A#lA.ÆÅA9ú
+AE-vAP`ÎA['AfÇAqú×A}./A0ÄAÊqAd
+AýËAxA 1%A¥ÊÑA«d~A°þ*A¶×AŒ1AÁË0AÇdÝAÌþAÒ7AØ1äAÝËAãe=AèþêAîAô2CAùËðAÿeB€BL{BQB
+æ'B
+²þBÔBLªBBæWB³-B
+é¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ã¬s>ÑTñ?ð?çÆ@ Å@M`'@z-@}u@©ä&@ÀJ×@Ö±@í9A¿uA
+òÍA&&A#Y~A.×A9À/ADóAP&àA[Z8AfAqÀéA|ôBAÎA­{AG(AàÕAzA .A¥­ÚA«GA°á4A¶zàAŒAÁ®:AÇGçAÌáAÒ{AAØíAÝ®AãHFAèáóAî{ AôLAù®ùAÿH¥Bq)B=ÿB
+ÖB
+×¬B
+€BqXB>/B
+BÞÁ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ŒnÐ>ÊN? ?å»\@«@Lxr@yEÔ@	@©pL@¿Öý@Ö=®@ì€_A
+A
+žàAì9A#A.RêA9BAD¹AOìòA[ KAfS€AqüA|ºUAöØA
+A*1AÃÞA]A÷7A¥äA«*A°Ä=A¶]êA»÷AÁDAÇ*ñAÌÄAÒ^JA×÷öAÝ£Aã+PAèÄüAî^©AóøVAùAÿ+¯Bb®B/BüZB
+É0B
+BbÝB6¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >µ1,>ÂÙª?Q/?ãëó@
+Ã\@KŸ@x^ @Á@šür@¿c#@ÕÉÔ@ì0
+AKA
+~óA²LA"å€A.üA9LTAD­AO³AZæ^Af·AqMA|iAÙáAsA
+:AŠçA@AÚ@A¥síA«
+A°§GA¶@ôA»Ú¡AÁtMAÇ
+úAÌ§ŠAÒASA×Û AÝt¬AãYAèšAîA²AóÛ`Aùu
+Aÿ·BT2B!BíßB
+ºµB
+q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >­ó>»?Æ?â
+@
+Ûš@J©
+@wvl@!ç@š@ŸïI@ÕUù@ëŒ«A®A
+EAx^A"«¶A-ßA9hADEÀAOyAZ¬qAeßÉAq"A|F|AŒêAVAðDAðA#AœJA¥V÷Aªð€A°QA¶#ýA»œªAÁWVAÆñAÌ°AÒ$\A×Ÿ	AÝW¶AâñbAèAî$œAóŸiAùXAþñÁBE·BBæI¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Šµå>Ž^C?²U?àM@
+óï@IÁQ@v³@®
+@š»@Ÿ{m@Ôâ
+@ëHÎA ×¿A
+
+A>pA"qÉA-¥!A8ØzAD
+ÒAO?*AZrAe¥ÜApÙ4A|
+AóA9AÓLAlùAŠA SA¥: AªÓ¬A°mYA¶A» ²AÁ:_AÆÔ
+AÌmžAÒeA×¡AÝ:¿AâÔlAènAîÅAó¡rAù;
+AþÔËB>!¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >xA>­ À?âô?Þ}¹@
+
+?@HÙ¡@u§@:3@§ ã@Ÿ@ÔnD@êÔöA ÓA
+Ñ,AA"7ÝA-k5A8ACÑæAO?AZ8AekïApGA{Ò¡AýA
+ªA¶WAPAé±A]A¥
+
+Aª¶¶A°PcAµêA»ŒAÁ
+iAÆ·AÌPÂAÑêpA×
+AÝ
+ÉAâ·vAèQ#AíêÏAó|Aù+ó¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >:>¥ã??Ü®P@$@Gñì@t¿N@ÆX@§-@œº@Óúk@êaA cæA
+?AÊA!ýïA-1HA8d¡ACùANËQAYþ©Ae2Ape[A{ŽAfAÿŽAaA3
+AÌºAffA¥ AªÀA°3lAµÍA»fÆAÁ rAÆ AÌ3ÍAÑÍyA×g&AÝ ÓAâAè4,AíÛ£¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >üú>¥x?D"?ÚÞç@<Ö@G
+8@s×@R~@Š¹/@œß@Ó@éíAA )ùA
+]QAªA!ÄA,÷[A8*³AC^
+ANdAYÄœAdøAp+nA{^ÇAIAâœA|jAA¯ÃAIpA€ã
+Aª|ÉA°vAµ°"A»IÐAÀã}AÆ})AÌÖAÑ°A×J/AÜãÜAâS¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >¿W>h?~é?Ù@U*@F"@rïí@Þ§@ŠEX@Œ¬	@Óº@éyk@ÿà
+A
+#gAV¿A!A,œpA7ðÈAC$!ANWyAYÒAdŸ*AoñA{$ÜA,AÅÈA_tAù!AÍA,zA€Æ'Aª_ÓA¯ùAµ.A»,ÚAÀÆAÆ`4AËùàAÑA×;¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >³>*q?{JÂ?×@%@mv@E:×@r9@jÍ@¥Ñ~@Œ8/@Òà@é@ÿlBA
+éyA
+ÒA!P*A,A7¶ÛABê4AN
+AYPäAd=Ao·AzêïA$AšÑAB}AÜ*Au×AA€©1AªBÞA¯ÜAµv7A»äAÀ©AÆC=AËêŽ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >v
+>ìÍ?w«ð?Õpœ@
+Á@DS#@q @öó@¥]€@»ÄU@Ò+@è¶@þøgA
+¯AâåA!=A,IA7|îAB°FAMãAYøAdJPAo}šAz±Aò-AÚA%A¿3AXáAòA€:Aª%çA¯¿AµY@AºòíAÀd¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >h
+×>¯j?t
+>?Ó¡d@@Ckv@p8Ø@
+@€éÎ@»P~@Ñ·/@è
+à@þA
+u¡AšúA ÜRA,ªA7CABv\AM©ŽAXÝ
+AdeAoCœAzwAÕ8AnäAA¢?A;ëAÕA€oEAªñA¯¢AµJ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Y>tã?pnm?ÑÑû@¶`@BÂ@oQ$@C@€uó@ºÜ¥@ÑCU@çª@þ·A
+;ŽAo
+A ¢eA+ÕŸA7	AB<nAMoÇAX£AcÖxAo	ÐAz=)AžBAQïAëA
+HA
+õAž¡A€RNA©ùÅ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >KJ>fhÆ?lÏ»?Ð¢@ÎŽ@A@niw@m@€
+@ºhÎ@ÐÏ@ç60@ýáA
+ÉA5"A hzA+ÒA6Ï+ABAM5ÜAXi4AcAnÏåAz?ALA4ùAÎŠAhRAÿA©v¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ><>Wí?i0è?Î39@ç @@Ža@mÃ@'@£C@¹ôô@Ð[¥@æÂV@ý)A	ÇÜAû4A .A+aåA6>AAÈALûïAX/GAcbAnøAyÉRA~VAA±¯AY&¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >.»>Ir7?e?ÌcÐ@ÿK@?Ì­@l@³ž@£i@¹@ÏçË@æN|@üµ-A	ïAÁGAô A+'øA6[QAA©ALÂAWõZAc(³An\
+AydAa_AÖ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >€s>:÷p?aóe?Êx@@>å @k²b@?â@¢Š@¹
+D@Ïsõ@åÚ¥@üAVA	TA\AºµA*î
+A6!eAATŸALAW»oAbîÇAn" Ayq¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >)/>,|,?^T?ÈÅ@/ê@=ýL@jÊ­@Ì@¢2¹@žj@Ï @åfË@ûÍ}A	AMoAÇA*Ž A5çyAAÑALN)AWAbÐo¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >­é>
+ æ?ZµÃ?ÆõŠ@H5@=@iâù@X.@¡Ÿß@ž%@Î@@äòò@ûY¢Aà)AAFÛA*z3A5­A@àäAL/Ñ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =èeE>?W?Å&M@`@<-ë@hûM@äW@¡K@·±º@Îj@ä@úåÌAŠ?AÙA
+ïA*@HA55¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =Ën¹>
+Ù?Sx@?ÃVä@xÕ@;F7@h@p~@ ×.@·=ß@Í€@ä
+A@úqòAlQAªA
+î¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =®x1=å *?OÙ?Á@
+(@:^@g+ì@ü§@ cX@¶Ê	@Í0º@ãk@ùþ
+AMû¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =€=È)?L:Œ?¿ž#@
+©t@9vÖ@fD8@Í@ï~@¶V/@ÌŒà@ãZº¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =i.=«3?Hë?œèº@
+ÁÀ@8"@e\@ó@{€@¶~¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =/)==?Dý9?Œa@
+Ú@7§u@dt×@ØF¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  <êwô=bì?A^h?ºIù@	ò_@7.¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  <m;Š=( Ü?=¿?¹W6¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  9°ç\=
+J¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ?!G®?Ð£×@(Qì@hQì@(ö@Ž(ö@Ô(ö@ô(öA
+{A{A*{A:{AJ{AZ{Aj{Az{A
+
+=A
+=A
+=A
+=A¥
+=A­
+=Aµ
+=Aœ
+=AÅ
+=AÍ
+=AÕ
+=AÝ
+=Aå
+=Aí
+=Aõ
+=Aý
+=B
+B
+B
+
+B
+B
+B
+B
+B
+
+B"
+B&
+B*
+B.
+B2
+B6
+B:
+B>
+BB
+BF
+BJ
+BN
+BR
+BV
+BZ
+B^
+Bb
+Bf
+Bj
+Bn
+Br
+Bv
+Bz
+B~
+?
+žR?Ï\)@'®@g®@×
+@³×
+@Ó×
+@ó×
+A	ë
+Aë
+A)ë
+A9ë
+AIë
+AYë
+Aië
+Ayë
+AõÃAõÃAõÃAõÃA€õÃA¬õÃAŽõÃAŒõÃAÄõÃAÌõÃAÔõÃAÜõÃAäõÃAìõÃAôõÃAüõÃBzáBzáB
+záBzáBzáBzáBzáB
+záB"záB&záB*záB.záB2záB6záB:záB>záBBzáBFzáBJzáBNzáBRzáBVzáBZzáB^záBbzáBfzáBjzáBnzáBrzáBvzáBzzáB~zá?
+(ö?Î{@'
+>@g
+>@
+@³
+@Ó
+@ó
+A	ÂAÂA)ÂA9ÂAIÂAYÂAiÂAyÂAáHAáHAáHAáHA€áHA¬áHAŽáHAŒáHAÄáHAÌáHAÔáHAÜáHAäáHAìáHAôáHAüáHBp€Bp€B
+p€Bp€Bp€Bp€Bp€B
+p€B"p€B&p€B*p€B.p€B2p€B6p€B:p€B>p€BBp€BFp€BJp€BNp€BRp€BVp€BZp€B^p€Bbp€Bfp€Bjp€Bnp€Brp€Bvp€Bzp€B~p€??ÌÌÍ@&ff@fff@33@³33@Ó33@ó33A	AA)A9AIAYAiAyAÌÍAÌÍAÌÍAÌÍA€ÌÍA¬ÌÍAŽÌÍAŒÌÍAÄÌÍAÌÌÍAÔÌÍAÜÌÍAäÌÍAìÌÍAôÌÍAüÌÍBffBffB
+ffBffBffBffBffB
+ffB"ffB&ffB*ffB.ffB2ffB6ffB:ffB>ffBBffBFffBJffBNffBRffBVffBZffB^ffBbffBfffBjffBnffBrffBvffBzffB~ff?
+=?Ë
+
+@%Â@eÂ@áH@²áH@ÒáH@òáHA	p€Ap€A)p€A9p€AIp€AYp€Aip€Ayp€AžRAžRAžRAžRA€žRA¬žRAŽžRAŒžRAÄžRAÌžRAÔžRAÜžRAäžRAìžRAôžRAüžRB\)B\)B
+\)B\)B\)B\)B\)B
+\)B"\)B&\)B*\)B.\)B2\)B6\)B:\)B>\)BB\)BF\)BJ\)BN\)BR\)BV\)BZ\)B^\)Bb\)Bf\)Bj\)Bn\)Br\)Bv\)Bz\)B~\)?zá?Ê=p@%
+ž@e
+ž@\@²\@Ò\@ò\A	G®AG®A)G®A9G®AIG®AYG®AiG®AyG®A£×A£×A£×A£×A€£×A¬£×AŽ£×AŒ£×AÄ£×AÌ£×AÔ£×AÜ£×Aä£×Aì£×Aô£×Aü£×BQìBQìB
+QìBQìBQìBQìBQìB
+QìB"QìB&QìB*QìB.QìB2QìB6QìB:QìB>QìBBQìBFQìBJQìBNQìBRQìBVQìBZQìB^QìBbQìBfQìBjQìBnQìBrQìBvQìBzQìB~Qì?ë
+?ÈõÂ@$zá@dzá@=q@²=q@Ò=q@ò=qA	
+žA
+žA)
+žA9
+žAI
+žAY
+žAi
+žAy
+žA\A\A\A\A€\A¬\AŽ\AŒ\AÄ\AÌ\AÔ\AÜ\Aä\Aì\Aô\Aü\BG®BG®B
+G®BG®BG®BG®BG®B
+G®B"G®B&G®B*G®B.G®B2G®B6G®B:G®B>G®BBG®BFG®BJG®BNG®BRG®BVG®BZG®B^G®BbG®BfG®BjG®BnG®BrG®BvG®BzG®B~G®?\)?Ç®@#×
+@c×
+@ë
+@±ë
+@Ñë
+@ñë
+AõÃAõÃA(õÃA8õÃAHõÃAXõÃAhõÃAxõÃAzáAzáAzáAzáA€záA¬záAŽzáAŒzáAÄzáAÌzáAÔzáAÜzáAäzáAìzáAôzáAüzáB=qB=qB
+=qB=qB=qB=qB=qB
+=qB"=qB&=qB*=qB.=qB2=qB6=qB:=qB>=qBB=qBF=qBJ=qBN=qBR=qBV=qBZ=qB^=qBb=qBf=qBj=qBn=qBr=qBv=qBz=qB~=q?
+ÌÍ?Æff@#33@c33@@±@Ñ@ñAÌÍAÌÍA(ÌÍA8ÌÍAHÌÍAXÌÍAhÌÍAxÌÍAffAffAffAffA€ffA¬ffAŽffAŒffAÄffAÌffAÔffAÜffAäffAìffAôffAüffB33B33B
+33B33B33B33B33B
+33B"33B&33B*33B.33B233B633B:33B>33BB33BF33BJ33BN33BR33BV33BZ33B^33Bb33Bf33Bj33Bn33Br33Bv33Bz33B~33?
+=q?Å
+ž@"\@b\@G®@±G®@ÑG®@ñG®A£×A£×A(£×A8£×AH£×AX£×Ah£×Ax£×AQìAQìAQìAQìA€QìA¬QìAŽQìAŒQìAÄQìAÌQìAÔQìAÜQìAäQìAìQìAôQìAüQìB(öB(öB
+(öB(öB(öB(öB(öB
+(öB"(öB&(öB*(öB.(öB2(öB6(öB:(öB>(öBB(öBF(öBJ(öBN(öBR(öBV(öBZ(öB^(öBb(öBf(öBj(öBn(öBr(öBv(öBz(öB~(ö?®?Ã×
+@!ë
+@aë
+@õÂ@°õÂ@ÐõÂ@ðõÂAzáAzáA(záA8záAHzáAXzáAhzáAxzáA=qA=qA=qA=qA€=qA¬=qAŽ=qAŒ=qAÄ=qAÌ=qAÔ=qAÜ=qAä=qAì=qAô=qAü=qB
+žB
+žB
+
+žB
+žB
+žB
+žB
+žB
+
+žB"
+žB&
+žB*
+žB.
+žB2
+žB6
+žB:
+žB>
+žBB
+žBF
+žBJ
+žBN
+žBR
+žBV
+žBZ
+žB^
+žBb
+žBf
+žBj
+žBn
+žBr
+žBv
+žBz
+žB~
+ž?
+ž?Â\@!G®@aG®@£×@°£×@Ð£×@ð£×AQìAQìA(QìA8QìAHQìAXQìAhQìAxQìA(öA(öA(öA(öA€(öA¬(öAŽ(öAŒ(öAÄ(öAÌ(öAÔ(öAÜ(öAä(öAì(öAô(öAü(öB{B{B
+{B{B{B{B{B
+{B"{B&{B*{B.{B2{B6{B:{B>{BB{BF{BJ{BN{BR{BV{BZ{B^{Bb{Bf{Bj{Bn{Br{Bv{Bz{B~{?\?ÁG®@ £×@`£×@Qì@°Qì@ÐQì@ðQìA(öA(öA((öA8(öAH(öAX(öAh(öAx(öA{A{A{A{A€{A¬{AŽ{AŒ{AÄ{AÌ{AÔ{AÜ{Aä{Aì{Aô{Aü{B
+=B
+=B
+
+=B
+=B
+=B
+=B
+=B
+
+=B"
+=B&
+=B*
+=B.
+=B2
+=B6
+=B:
+=B>
+=BB
+=BF
+=BJ
+=BN
+=BR
+=BV
+=BZ
+=B^
+=Bb
+=Bf
+=Bj
+=Bn
+=Br
+=Bv
+=Bz
+=B~
+=?   ?À  @   @`  @  @°  @Ð  @ð  A  A  A(  A8  AH  AX  Ah  Ax  A  A  A  A  A€  A¬  AŽ  AŒ  AÄ  AÌ  AÔ  AÜ  Aä  Aì  Aô  Aü  B  B  B
+  B  B  B  B  B
+  B"  B&  B*  B.  B2  B6  B:  B>  BB  BF  BJ  BN  BR  BV  BZ  B^  Bb  Bf  Bj  Bn  Br  Bv  Bz  B~  >úáH?ŸžR@\)@_\)@®@¯®@Ï®@ï®A×
+A×
+A'×
+A7×
+AG×
+AW×
+Ag×
+Aw×
+Aë
+Aë
+Aë
+Aë
+A£ë
+A«ë
+A³ë
+A»ë
+AÃë
+AËë
+AÓë
+AÛë
+Aãë
+Aëë
+Aóë
+Aûë
+BõÃBõÃB	õÃB
+õÃBõÃBõÃBõÃB
+õÃB!õÃB%õÃB)õÃB-õÃB1õÃB5õÃB9õÃB=õÃBAõÃBEõÃBIõÃBMõÃBQõÃBUõÃBYõÃB]õÃBaõÃBeõÃBiõÃBmõÃBqõÃBuõÃByõÃB}õÃ>õÂ?œp€@
+žR@^žR@\)@¯\)@Ï\)@ï\)A®A®A'®A7®AG®AW®Ag®Aw®A×
+A×
+A×
+A×
+A£×
+A«×
+A³×
+A»×
+AÃ×
+AË×
+AÓ×
+AÛ×
+Aã×
+Aë×
+Aó×
+Aû×
+Bë
+Bë
+B	ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B!ë
+B%ë
+B)ë
+B-ë
+B1ë
+B5ë
+B9ë
+B=ë
+BAë
+BEë
+BIë
+BMë
+BQë
+BUë
+BYë
+B]ë
+Baë
+Beë
+Bië
+Bmë
+Bqë
+Buë
+Byë
+B}ë
+>ð£×?Œ(ö@
+{@^{@
+=@¯
+=@Ï
+=@ï
+=A
+A
+A'
+A7
+AG
+AW
+Ag
+Aw
+AÂAÂAÂAÂA£ÂA«ÂA³ÂA»ÂAÃÂAËÂAÓÂAÛÂAãÂAëÂAóÂAûÂBáHBáHB	áHB
+áHBáHBáHBáHB
+áHB!áHB%áHB)áHB-áHB1áHB5áHB9áHB=áHBAáHBEáHBIáHBMáHBQáHBUáHBYáHB]áHBaáHBeáHBiáHBmáHBqáHBuáHByáHB}áH>ë
+?ºáH@
+p€@]p€@žR@®žR@ÎžR@îžRA\)A\)A'\)A7\)AG\)AW\)Ag\)Aw\)A®A®A®A®A£®A«®A³®A»®AÃ®AË®AÓ®AÛ®Aã®Aë®Aó®Aû®B×
+B×
+B	×
+B
+×
+B×
+B×
+B×
+B
+×
+B!×
+B%×
+B)×
+B-×
+B1×
+B5×
+B9×
+B=×
+BA×
+BE×
+BI×
+BM×
+BQ×
+BU×
+BY×
+B]×
+Ba×
+Be×
+Bi×
+Bm×
+Bq×
+Bu×
+By×
+B}×
+>æff?¹@
+ÌÍ@\ÌÍ@ff@®ff@Îff@îffA33A33A'33A733AG33AW33Ag33Aw33AAAAA£A«A³A»AÃAËAÓAÛAãAëAóAûBÌÍBÌÍB	ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB!ÌÍB%ÌÍB)ÌÍB-ÌÍB1ÌÍB5ÌÍB9ÌÍB=ÌÍBAÌÍBEÌÍBIÌÍBMÌÍBQÌÍBUÌÍBYÌÍB]ÌÍBaÌÍBeÌÍBiÌÍBmÌÍBqÌÍBuÌÍByÌÍB}ÌÍ>áG®?žQì@
+(ö@\(ö@{@®{@Î{@î{A
+=A
+=A'
+=A7
+=AG
+=AW
+=Ag
+=Aw
+=A
+A
+A
+A
+A£
+A«
+A³
+A»
+AÃ
+AË
+AÓ
+AÛ
+Aã
+Aë
+Aó
+Aû
+BÂBÂB	ÂB
+ÂBÂBÂBÂB
+ÂB!ÂB%ÂB)ÂB-ÂB1ÂB5ÂB9ÂB=ÂBAÂBEÂBIÂBMÂBQÂBUÂBYÂB]ÂBaÂBeÂBiÂBmÂBqÂBuÂByÂB}Â>Ü(ö?·
+>@
+@[
+@Â@­Â@ÍÂ@íÂAáHAáHA&áHA6áHAFáHAVáHAfáHAváHAp€Ap€Ap€Ap€A£p€A«p€A³p€A»p€AÃp€AËp€AÓp€AÛp€Aãp€Aëp€Aóp€Aûp€BžRBžRB	žRB
+žRBžRBžRBžRB
+žRB!žRB%žRB)žRB-žRB1žRB5žRB9žRB=žRBAžRBEžRBIžRBMžRBQžRBUžRBYžRB]žRBažRBežRBižRBmžRBqžRBužRByžRB}žR>×
+=?µÂ@áH@ZáH@p€@­p€@Íp€@íp€AžRAžRA&žRA6žRAFžRAVžRAfžRAvžRA\)A\)A\)A\)A£\)A«\)A³\)A»\)AÃ\)AË\)AÓ\)AÛ\)Aã\)Aë\)Aó\)Aû\)B®B®B	®B
+®B®B®B®B
+®B!®B%®B)®B-®B1®B5®B9®B=®BA®BE®BI®BM®BQ®BU®BY®B]®Ba®Be®Bi®Bm®Bq®Bu®By®B}®>Ñë
+?Žzá@=q@Z=q@
+ž@­
+ž@Í
+ž@í
+žA\A\A&\A6\AF\AV\Af\Av\AG®AG®AG®AG®A£G®A«G®A³G®A»G®AÃG®AËG®AÓG®AÛG®AãG®AëG®AóG®AûG®B£×B£×B	£×B
+£×B£×B£×B£×B
+£×B!£×B%£×B)£×B-£×B1£×B5£×B9£×B=£×BA£×BE£×BI£×BM£×BQ£×BU£×BY£×B]£×Ba£×Be£×Bi£×Bm£×Bq£×Bu£×By£×B}£×>ÌÌÍ?³33@@Y@ÌÍ@¬ÌÍ@ÌÌÍ@ìÌÍAffAffA&ffA6ffAFffAVffAfffAvffA33A33A33A33A£33A«33A³33A»33AÃ33AË33AÓ33AÛ33Aã33Aë33Aó33Aû33BBB	B
+BBBB
+B!B%B)B-B1B5B9B=BABEBIBMBQBUBYB]BaBeBiBmBqBuByB}>Ç®?±ë
+@õÂ@XõÂ@zá@¬zá@Ìzá@ìzáA=qA=qA&=qA6=qAF=qAV=qAf=qAv=qA
+žA
+žA
+žA
+žA£
+žA«
+žA³
+žA»
+žAÃ
+žAË
+žAÓ
+žAÛ
+žAã
+žAë
+žAó
+žAû
+žB\B\B	\B
+\B\B\B\B
+\B!\B%\B)\B-\B1\B5\B9\B=\BA\BE\BI\BM\BQ\BU\BY\B]\Ba\Be\Bi\Bm\Bq\Bu\By\B}\>Â\?°£×@Qì@XQì@(ö@¬(ö@Ì(ö@ì(öA{A{A&{A6{AF{AV{Af{Av{A
+=A
+=A
+=A
+=A£
+=A«
+=A³
+=A»
+=AÃ
+=AË
+=AÓ
+=AÛ
+=Aã
+=Aë
+=Aó
+=Aû
+=B
+B
+B	
+B
+
+B
+B
+B
+B
+
+B!
+B%
+B)
+B-
+B1
+B5
+B9
+B=
+BA
+BE
+BI
+BM
+BQ
+BU
+BY
+B]
+Ba
+Be
+Bi
+Bm
+Bq
+Bu
+By
+B}
+>œp€?¯\)@®@W®@×
+@«×
+@Ë×
+@ë×
+Aë
+Aë
+A%ë
+A5ë
+AEë
+AUë
+Aeë
+Auë
+AõÃAõÃAõÃAõÃA¢õÃAªõÃA²õÃAºõÃAÂõÃAÊõÃAÒõÃAÚõÃAâõÃAêõÃAòõÃAúõÃBzáBzáB	záB
+záBzáBzáBzáB
+záB!záB%záB)záB-záB1záB5záB9záB=záBAzáBEzáBIzáBMzáBQzáBUzáBYzáB]záBazáBezáBizáBmzáBqzáBuzáByzáB}zá>žQì?®{@
+>@W
+>@
+@«
+@Ë
+@ë
+AÂAÂA%ÂA5ÂAEÂAUÂAeÂAuÂAáHAáHAáHAáHA¢áHAªáHA²áHAºáHAÂáHAÊáHAÒáHAÚáHAâáHAêáHAòáHAúáHBp€Bp€B	p€B
+p€Bp€Bp€Bp€B
+p€B!p€B%p€B)p€B-p€B1p€B5p€B9p€B=p€BAp€BEp€BIp€BMp€BQp€BUp€BYp€B]p€Bap€Bep€Bip€Bmp€Bqp€Bup€Byp€B}p€>³33?¬ÌÍ@ff@Vff@33@«33@Ë33@ë33AAA%A5AEAUAeAuAÌÍAÌÍAÌÍAÌÍA¢ÌÍAªÌÍA²ÌÍAºÌÍAÂÌÍAÊÌÍAÒÌÍAÚÌÍAâÌÍAêÌÍAòÌÍAúÌÍBffBffB	ffB
+ffBffBffBffB
+ffB!ffB%ffB)ffB-ffB1ffB5ffB9ffB=ffBAffBEffBIffBMffBQffBUffBYffB]ffBaffBeffBiffBmffBqffBuffByffB}ff>®{?«
+@Â@UÂ@áH@ªáH@ÊáH@êáHAp€Ap€A%p€A5p€AEp€AUp€Aep€Aup€AžRAžRAžRAžRA¢žRAªžRA²žRAºžRAÂžRAÊžRAÒžRAÚžRAâžRAêžRAòžRAúžRB\)B\)B	\)B
+\)B\)B\)B\)B
+\)B!\)B%\)B)\)B-\)B1\)B5\)B9\)B=\)BA\)BE\)BI\)BM\)BQ\)BU\)BY\)B]\)Ba\)Be\)Bi\)Bm\)Bq\)Bu\)By\)B}\)>šõÃ?ª=q@
+ž@U
+ž@\@ª\@Ê\@ê\AG®AG®A%G®A5G®AEG®AUG®AeG®AuG®A£×A£×A£×A£×A¢£×Aª£×A²£×Aº£×AÂ£×AÊ£×AÒ£×AÚ£×Aâ£×Aê£×Aò£×Aú£×BQìBQìB	QìB
+QìBQìBQìBQìB
+QìB!QìB%QìB)QìB-QìB1QìB5QìB9QìB=QìBAQìBEQìBIQìBMQìBQQìBUQìBYQìB]QìBaQìBeQìBiQìBmQìBqQìBuQìByQìB}Qì>£×
+?šõÂ@zá@Tzá@=q@ª=q@Ê=q@ê=qA
+žA
+žA%
+žA5
+žAE
+žAU
+žAe
+žAu
+žA\A\A\A\A¢\Aª\A²\Aº\AÂ\AÊ\AÒ\AÚ\Aâ\Aê\Aò\Aú\BG®BG®B	G®B
+G®BG®BG®BG®B
+G®B!G®B%G®B)G®B-G®B1G®B5G®B9G®B=G®BAG®BEG®BIG®BMG®BQG®BUG®BYG®B]G®BaG®BeG®BiG®BmG®BqG®BuG®ByG®B}G®>žR?§®@×
+@S×
+@ë
+@©ë
+@Éë
+@éë
+AõÃAõÃA$õÃA4õÃADõÃATõÃAdõÃAtõÃAzáAzáAzáAzáA¢záAªzáA²záAºzáAÂzáAÊzáAÒzáAÚzáAâzáAêzáAòzáAúzáB=qB=qB	=qB
+=qB=qB=qB=qB
+=qB!=qB%=qB)=qB-=qB1=qB5=qB9=qB==qBA=qBE=qBI=qBM=qBQ=qBU=qBY=qB]=qBa=qBe=qBi=qBm=qBq=qBu=qBy=qB}=q>?Šff@33@S33@@©@É@éAÌÍAÌÍA$ÌÍA4ÌÍADÌÍATÌÍAdÌÍAtÌÍAffAffAffAffA¢ffAªffA²ffAºffAÂffAÊffAÒffAÚffAâffAêffAòffAúffB33B33B	33B
+33B33B33B33B
+33B!33B%33B)33B-33B133B533B933B=33BA33BE33BI33BM33BQ33BU33BY33B]33Ba33Be33Bi33Bm33Bq33Bu33By33B}33>zá?¥
+ž@\@R\@G®@©G®@ÉG®@éG®A£×A£×A$£×A4£×AD£×AT£×Ad£×At£×AQìAQìAQìAQìA¢QìAªQìA²QìAºQìAÂQìAÊQìAÒQìAÚQìAâQìAêQìAòQìAúQìB(öB(öB	(öB
+(öB(öB(öB(öB
+(öB!(öB%(öB)(öB-(öB1(öB5(öB9(öB=(öBA(öBE(öBI(öBM(öBQ(öBU(öBY(öB](öBa(öBe(öBi(öBm(öBq(öBu(öBy(öB}(ö>\)?£×
+@ë
+@Që
+@õÃ@šõÃ@ÈõÃ@èõÃAzáAzáA$záA4záADzáATzáAdzáAtzáA=qA=qA=qA=qA¢=qAª=qA²=qAº=qAÂ=qAÊ=qAÒ=qAÚ=qAâ=qAê=qAò=qAú=qB
+žB
+žB	
+žB
+
+žB
+žB
+žB
+žB
+
+žB!
+žB%
+žB)
+žB-
+žB1
+žB5
+žB9
+žB=
+žBA
+žBE
+žBI
+žBM
+žBQ
+žBU
+žBY
+žB]
+žBa
+žBe
+žBi
+žBm
+žBq
+žBu
+žBy
+žB}
+ž>=q?¢\@G®@QG®@£×@š£×@È£×@è£×AQìAQìA$QìA4QìADQìATQìAdQìAtQìA(öA(öA(öA(öA¢(öAª(öA²(öAº(öAÂ(öAÊ(öAÒ(öAÚ(öAâ(öAê(öAò(öAú(öB{B{B	{B
+{B{B{B{B
+{B!{B%{B){B-{B1{B5{B9{B={BA{BE{BI{BM{BQ{BU{BY{B]{Ba{Be{Bi{Bm{Bq{Bu{By{B}{>
+
+ž?¡G®@£×@P£×@Qì@šQì@ÈQì@èQìA(öA(öA$(öA4(öAD(öAT(öAd(öAt(öA{A{A{A{A¢{Aª{A²{Aº{AÂ{AÊ{AÒ{AÚ{Aâ{Aê{Aò{Aú{B
+=B
+=B	
+=B
+=B
+=B
+=B
+=B
+
+=B!
+=B%
+=B)
+=B-
+=B1
+=B5
+=B9
+=B=
+=BA
+=BE
+=BI
+=BM
+=BQ
+=BU
+=BY
+=B]
+=Ba
+=Be
+=Bi
+=Bm
+=Bq
+=Bu
+=By
+=B}
+=>  ?   @  @P  @  @š  @È  @è  A  A  A$  A4  AD  AT  Ad  At  A  A  A  A  A¢  Aª  A²  Aº  AÂ  AÊ  AÒ  AÚ  Aâ  Aê  Aò  Aú  B  B  B	  B
+  B  B  B  B
+  B!  B%  B)  B-  B1  B5  B9  B=  BA  BE  BI  BM  BQ  BU  BY  B]  Ba  Be  Bi  Bm  Bq  Bu  By  B}  >uÂ?žR@\)@O\)@®@§®@Ç®@ç®A×
+A×
+A#×
+A3×
+AC×
+AS×
+Ac×
+As×
+Aë
+Aë
+Aë
+Aë
+A¡ë
+A©ë
+A±ë
+A¹ë
+AÁë
+AÉë
+AÑë
+AÙë
+Aáë
+Aéë
+Añë
+Aùë
+B õÃBõÃBõÃB
+õÃBõÃBõÃBõÃB
+õÃB õÃB$õÃB(õÃB,õÃB0õÃB4õÃB8õÃB<õÃB@õÃBDõÃBHõÃBLõÃBPõÃBTõÃBXõÃB\õÃB`õÃBdõÃBhõÃBlõÃBpõÃBtõÃBxõÃB|õÃ>k
+?p€@žR@NžR@\)@§\)@Ç\)@ç\)A®A®A#®A3®AC®AS®Ac®As®A×
+A×
+A×
+A×
+A¡×
+A©×
+A±×
+A¹×
+AÁ×
+AÉ×
+AÑ×
+AÙ×
+Aá×
+Aé×
+Añ×
+Aù×
+B ë
+Bë
+Bë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B ë
+B$ë
+B(ë
+B,ë
+B0ë
+B4ë
+B8ë
+B<ë
+B@ë
+BDë
+BHë
+BLë
+BPë
+BTë
+BXë
+B\ë
+B`ë
+Bdë
+Bhë
+Blë
+Bpë
+Btë
+Bxë
+B|ë
+>aG®?(ö@{@N{@
+=@§
+=@Ç
+=@ç
+=A
+A
+A#
+A3
+AC
+AS
+Ac
+As
+AÂAÂAÂAÂA¡ÂA©ÂA±ÂA¹ÂAÁÂAÉÂAÑÂAÙÂAáÂAéÂAñÂAùÂB áHBáHBáHB
+áHBáHBáHBáHB
+áHB áHB$áHB(áHB,áHB0áHB4áHB8áHB<áHB@áHBDáHBHáHBLáHBPáHBTáHBXáHB\áHB`áHBdáHBháHBláHBpáHBtáHBxáHB|áH>W
+=?áH@
+p€@Mp€@žR@ŠžR@ÆžR@æžRA\)A\)A#\)A3\)AC\)AS\)Ac\)As\)A®A®A®A®A¡®A©®A±®A¹®AÁ®AÉ®AÑ®AÙ®Aá®Aé®Añ®Aù®B ×
+B×
+B×
+B
+×
+B×
+B×
+B×
+B
+×
+B ×
+B$×
+B(×
+B,×
+B0×
+B4×
+B8×
+B<×
+B@×
+BD×
+BH×
+BL×
+BP×
+BT×
+BX×
+B\×
+B`×
+Bd×
+Bh×
+Bl×
+Bp×
+Bt×
+Bx×
+B|×
+>LÌÍ?@
+ÌÍ@LÌÍ@ff@Šff@Æff@æffA33A33A#33A333AC33AS33Ac33As33AAAAA¡A©A±A¹AÁAÉAÑAÙAáAéAñAùB ÌÍBÌÍBÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB ÌÍB$ÌÍB(ÌÍB,ÌÍB0ÌÍB4ÌÍB8ÌÍB<ÌÍB@ÌÍBDÌÍBHÌÍBLÌÍBPÌÍBTÌÍBXÌÍB\ÌÍB`ÌÍBdÌÍBhÌÍBlÌÍBpÌÍBtÌÍBxÌÍB|ÌÍ>B\?Qì@
+(ö@L(ö@{@Š{@Æ{@æ{A
+=A
+=A#
+=A3
+=AC
+=AS
+=Ac
+=As
+=A
+A
+A
+A
+A¡
+A©
+A±
+A¹
+AÁ
+AÉ
+AÑ
+AÙ
+Aá
+Aé
+Añ
+Aù
+B ÂBÂBÂB
+ÂBÂBÂBÂB
+ÂB ÂB$ÂB(ÂB,ÂB0ÂB4ÂB8ÂB<ÂB@ÂBDÂBHÂBLÂBPÂBTÂBXÂB\ÂB`ÂBdÂBhÂBlÂBpÂBtÂBxÂB|Â>8Qì?
+>@
+
+@K
+@
+Â@¥Â@ÅÂ@åÂAáHAáHA"áHA2áHABáHARáHAbáHAráHAp€Ap€Ap€Ap€A¡p€A©p€A±p€A¹p€AÁp€AÉp€AÑp€AÙp€Aáp€Aép€Añp€Aùp€B žRBžRBžRB
+žRBžRBžRBžRB
+žRB žRB$žRB(žRB,žRB0žRB4žRB8žRB<žRB@žRBDžRBHžRBLžRBPžRBTžRBXžRB\žRB`žRBdžRBhžRBlžRBpžRBtžRBxžRB|žR>.{?Â@
+áH@JáH@
+p€@¥p€@Åp€@åp€AžRAžRA"žRA2žRABžRARžRAbžRAržRA\)A\)A\)A\)A¡\)A©\)A±\)A¹\)AÁ\)AÉ\)AÑ\)AÙ\)Aá\)Aé\)Añ\)Aù\)B ®B®B®B
+®B®B®B®B
+®B ®B$®B(®B,®B0®B4®B8®B<®B@®BD®BH®BL®BP®BT®BX®B\®B`®Bd®Bh®Bl®Bp®Bt®Bx®B|®>#×
+?zá@
+=q@J=q@
+
+ž@¥
+ž@Å
+ž@å
+žA\A\A"\A2\AB\AR\Ab\Ar\AG®AG®AG®AG®A¡G®A©G®A±G®A¹G®AÁG®AÉG®AÑG®AÙG®AáG®AéG®AñG®AùG®B £×B£×B£×B
+£×B£×B£×B£×B
+£×B £×B$£×B(£×B,£×B0£×B4£×B8£×B<£×B@£×BD£×BH£×BL£×BP£×BT£×BX£×B\£×B`£×Bd£×Bh£×Bl£×Bp£×Bt£×Bx£×B|£×>?33@	@I@ÌÍ@€ÌÍ@ÄÌÍ@äÌÍAffAffA"ffA2ffABffARffAbffArffA33A33A33A33A¡33A©33A±33A¹33AÁ33AÉ33AÑ33AÙ33Aá33Aé33Añ33Aù33B BBB
+BBBB
+B B$B(B,B0B4B8B<B@BDBHBLBPBTBXB\B`BdBhBlBpBtBxB|>\)?ë
+@õÃ@HõÃ@zá@€zá@Äzá@äzáA=qA=qA"=qA2=qAB=qAR=qAb=qAr=qA
+žA
+žA
+žA
+žA¡
+žA©
+žA±
+žA¹
+žAÁ
+žAÉ
+žAÑ
+žAÙ
+žAá
+žAé
+žAñ
+žAù
+žB \B\B\B
+\B\B\B\B
+\B \B$\B(\B,\B0\B4\B8\B<\B@\BD\BH\BL\BP\BT\BX\B\\B`\Bd\Bh\Bl\Bp\Bt\Bx\B|\>
+ž?£×@Qì@HQì@(ö@€(ö@Ä(ö@ä(öA{A{A"{A2{AB{AR{Ab{Ar{A
+=A
+=A
+=A
+=A¡
+=A©
+=A±
+=A¹
+=AÁ
+=AÉ
+=AÑ
+=AÙ
+=Aá
+=Aé
+=Añ
+=Aù
+=B 
+B
+B
+B
+
+B
+B
+B
+B
+
+B 
+B$
+B(
+B,
+B0
+B4
+B8
+B<
+B@
+BD
+BH
+BL
+BP
+BT
+BX
+B\
+B`
+Bd
+Bh
+Bl
+Bp
+Bt
+Bx
+B|
+=õÂ?\)@®@G®@×
+@£×
+@Ã×
+@ã×
+Aë
+Aë
+A!ë
+A1ë
+AAë
+AQë
+Aaë
+Aqë
+AõÃAõÃAõÃAõÃA õÃAšõÃA°õÃAžõÃAÀõÃAÈõÃAÐõÃAØõÃAàõÃAèõÃAðõÃAøõÃB záBzáBzáB
+záBzáBzáBzáB
+záB záB$záB(záB,záB0záB4záB8záB<záB@záBDzáBHzáBLzáBPzáBTzáBXzáB\záB`záBdzáBhzáBlzáBpzáBtzáBxzáB|zá=áG®?{@
+=@G
+=@
+@£
+@Ã
+@ã
+AÂAÂA!ÂA1ÂAAÂAQÂAaÂAqÂAáHAáHAáHAáHA áHAšáHA°áHAžáHAÀáHAÈáHAÐáHAØáHAàáHAèáHAðáHAøáHB p€Bp€Bp€B
+p€Bp€Bp€Bp€B
+p€B p€B$p€B(p€B,p€B0p€B4p€B8p€B<p€B@p€BDp€BHp€BLp€BPp€BTp€BXp€B\p€B`p€Bdp€Bhp€Blp€Bpp€Btp€Bxp€B|p€=ÌÌÍ?ÌÍ@ff@Fff@33@£33@Ã33@ã33AAA!A1AAAQAaAqAÌÍAÌÍAÌÍAÌÍA ÌÍAšÌÍA°ÌÍAžÌÍAÀÌÍAÈÌÍAÐÌÍAØÌÍAàÌÍAèÌÍAðÌÍAøÌÍB ffBffBffB
+ffBffBffBffB
+ffB ffB$ffB(ffB,ffB0ffB4ffB8ffB<ffB@ffBDffBHffBLffBPffBTffBXffB\ffB`ffBdffBhffBlffBpffBtffBxffB|ff=žQì?
+@Â@EÂ@áH@¢áH@ÂáH@âáHAp€Ap€A!p€A1p€AAp€AQp€Aap€Aqp€AžRAžRAžRAžRA žRAšžRA°žRAžžRAÀžRAÈžRAÐžRAØžRAàžRAèžRAðžRAøžRB \)B\)B\)B
+\)B\)B\)B\)B
+\)B \)B$\)B(\)B,\)B0\)B4\)B8\)B<\)B@\)BD\)BH\)BL\)BP\)BT\)BX\)B\\)B`\)Bd\)Bh\)Bl\)Bp\)Bt\)Bx\)B|\)=£×
+?=q@
+ž@E
+ž@\@¢\@Â\@â\AG®AG®A!G®A1G®AAG®AQG®AaG®AqG®A£×A£×A£×A£×A £×Aš£×A°£×Až£×AÀ£×AÈ£×AÐ£×AØ£×Aà£×Aè£×Að£×Aø£×B QìBQìBQìB
+QìBQìBQìBQìB
+QìB QìB$QìB(QìB,QìB0QìB4QìB8QìB<QìB@QìBDQìBHQìBLQìBPQìBTQìBXQìB\QìB`QìBdQìBhQìBlQìBpQìBtQìBxQìB|Qì=\)?õÃ@zá@Dzá@=q@¢=q@Â=q@â=qA
+žA
+žA!
+žA1
+žAA
+žAQ
+žAa
+žAq
+žA\A\A\A\A \Aš\A°\Až\AÀ\AÈ\AÐ\AØ\Aà\Aè\Að\Aø\B G®BG®BG®B
+G®BG®BG®BG®B
+G®B G®B$G®B(G®B,G®B0G®B4G®B8G®B<G®B@G®BDG®BHG®BLG®BPG®BTG®BXG®B\G®B`G®BdG®BhG®BlG®BpG®BtG®BxG®B|G®=uÂ?®@×
+@C×
+@ë
+@¡ë
+@Áë
+@áë
+A õÃAõÃA õÃA0õÃA@õÃAPõÃA`õÃApõÃAzáAzáAzáAzáA záAšzáA°záAžzáAÀzáAÈzáAÐzáAØzáAàzáAèzáAðzáAøzáB =qB=qB=qB
+=qB=qB=qB=qB
+=qB =qB$=qB(=qB,=qB0=qB4=qB8=qB<=qB@=qBD=qBH=qBL=qBP=qBT=qBX=qB\=qB`=qBd=qBh=qBl=qBp=qBt=qBx=qB|=q=LÌÍ?ff@33@C33@@¡@Á@áA ÌÍAÌÍA ÌÍA0ÌÍA@ÌÍAPÌÍA`ÌÍApÌÍAffAffAffAffA ffAšffA°ffAžffAÀffAÈffAÐffAØffAàffAèffAðffAøffB 33B33B33B
+33B33B33B33B
+33B 33B$33B(33B,33B033B433B833B<33B@33BD33BH33BL33BP33BT33BX33B\33B`33Bd33Bh33Bl33Bp33Bt33Bx33B|33=#×
+?
+
+ž@\@B\@G®@¡G®@ÁG®@áG®A £×A£×A £×A0£×A@£×AP£×A`£×Ap£×AQìAQìAQìAQìA QìAšQìA°QìAžQìAÀQìAÈQìAÐQìAØQìAàQìAèQìAðQìAøQìB (öB(öB(öB
+(öB(öB(öB(öB
+(öB (öB$(öB((öB,(öB0(öB4(öB8(öB<(öB@(öBD(öBH(öBL(öBP(öBT(öBX(öB\(öB`(öBd(öBh(öBl(öBp(öBt(öBx(öB|(ö<õÂ?×
+@ë
+@Aë
+@õÃ@ õÃ@ÀõÃ@àõÃA záAzáA záA0záA@záAPzáA`záApzáA=qA=qA=qA=qA =qAš=qA°=qAž=qAÀ=qAÈ=qAÐ=qAØ=qAà=qAè=qAð=qAø=qB 
+žB
+žB
+žB
+
+žB
+žB
+žB
+žB
+
+žB 
+žB$
+žB(
+žB,
+žB0
+žB4
+žB8
+žB<
+žB@
+žBD
+žBH
+žBL
+žBP
+žBT
+žBX
+žB\
+žB`
+žBd
+žBh
+žBl
+žBp
+žBt
+žBx
+žB|
+ž<£×
+?\@G®@AG®@£×@ £×@À£×@à£×A QìAQìA QìA0QìA@QìAPQìA`QìApQìA(öA(öA(öA(öA (öAš(öA°(öAž(öAÀ(öAÈ(öAÐ(öAØ(öAà(öAè(öAð(öAø(öB {B{B{B
+{B{B{B{B
+{B {B${B({B,{B0{B4{B8{B<{B@{BD{BH{BL{BP{BT{BX{B\{B`{Bd{Bh{Bl{Bp{Bt{Bx{B|{<#×
+?G®@ £×@@£×@Qì@ Qì@ÀQì@àQìA (öA(öA (öA0(öA@(öAP(öA`(öAp(öA{A{A{A{A {Aš{A°{Až{AÀ{AÈ{AÐ{AØ{Aà{Aè{Að{Aø{B 
+=B
+=B
+=B
+
+=B
+=B
+=B
+=B
+
+=B 
+=B$
+=B(
+=B,
+=B0
+=B4
+=B8
+=B<
+=B@
+=BD
+=BH
+=BL
+=BP
+=BT
+=BX
+=B\
+=B`
+=Bd
+=Bh
+=Bl
+=Bp
+=Bt
+=Bx
+=B|
+=    ?  @   @@  @  @   @À  @à  A   A  A   A0  A@  AP  A`  Ap  A  A  A  A  A   Aš  A°  Až  AÀ  AÈ  AÐ  AØ  Aà  Aè  Að  Aø  B   B  B  B
+  B  B  B  B
+  B   B$  B(  B,  B0  B4  B8  B<  B@  BD  BH  BL  BP  BT  BX  B\  B`  Bd  Bh  Bl  Bp  Bt  Bx  B|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?!1?)ì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+Æ?(þ?¯m®@ý¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?óô?$h,?­E@:@29Q@_¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?U"? É:?«ÎÌ@}@1Q@_«@]â@D¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?¶P?
+*h?©ÿb@ŽÈ@0ià@^
+ø@
+ê@Ä@³ @ÊB¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ??w?š/ê@Í
+@/#@]7;@
+v)@P¶@³+B@ÊÎ@ààZ@÷Œ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+x­?ì¥?Š`@ åX@.o@\O@
+O@ÜÜ@²·g@Éô@àl@÷G
+AÌAb|¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?ÙÚ?M³?€?ÿû6@-²²@[gÊ@q@hý@²C@É
+@ßø¡@öÓ.AÖÜAD"A
+±hA)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?;	?®á?¢Á?þ+Î@,Êþ@Z@@õ#@±Ï¯@Èª;@ßÈ@ö_TAðA
+5A
+wzA(äÀA4RA?£¶¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ? 7?
+? ò6?ü\e@+ãJ@Ya@Šœ@I@±[Õ@È6a@ßí@õëyAcAÐHA
+=A(ªÓA4A?
+^AJò€AVDT¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ùúÊ?q
+?"Ÿ?úì@*û@X°€@2Þ@
+j@°ç÷@ÇÂ@Þ@õwA)AYA
+A(päA3Þ*A?KoAJžŽAV%úAa@Aläð¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >òœ(?ÒL?SU?øœ@*Ù@WÈð@¿@@°t
+@ÇNš@Þ)5@õÁAï&A\lA
+É±A(6÷A3€<A?AJ~ÈAUì
+AaYRAlÆAx3ÞAÂÇ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ë? 3Z?Ü?öî
+@),
+@Vá4@K&@%³@° >@ÆÚÊ@ÝµV@ôâAµ7A"}A
+ÂA'ýA3jMA>×AJDØAU²
+AadAl©AwùîA³Aj=A¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >äAà>ù)?Žs?õ
+¢@(Dh@Uù@×L@±Ø@¯d@Æfñ@ÝA|@ô
+A{IAèA
+UÕA'ÃA30`A>ŠAJ
+ëAUx1A`åvAlRŒAwÀA€AMHAëAºAcf¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ý<>ñë,?äú?óO)@'\¬@UÃ@cm@=ú@¯
+@Åó@ÜÍ@óš+AA[A® A
+åA'+A2öqA>c·AIÐüAU>BA`«AlÍAwAy­A0PAæóAAT:A
+ÜA£³µ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ÕÆ>ê­??ñÀ@&t÷@T*@ï@Ê@®€«@Å8@ÜYÃ@ó4PAnAt³AáùA'O>A2ŒA>)ÉAIAUUA`qAkÞàAwL%A\¶AYAÉýA A7CAíçA£€A©[,A¯¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Îõ>ão€?F?ï°G@%;@SBR@{µ@VA@®0Í@Å
+Y@Ûåå@òÀqAÍ~A:ÄAš
+A'OA2A=ïÚAI]ATÊeA`7«Ak€ñAw6A?ŸAöaA­AcšALAÐïA£A©>6A®ôÙAŽ«{AºTT¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ÇKQ>Ü2?v¯?íàÞ@$¥@RZ@Û@âg@­Œó@Ä@Ûr
+@òLAA ÖAn
+A&ÛbA2HšA=µíAI#3ATxA_ýœAkkAvØIA"ÈAÙkAAF±AýUA³øA£jA©!?A®×âAŽAºE)A¿ûËAÅ€€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >À
+®>Ôô
+?§6?ìe@#œÊ@Qrá@'ù@n@­I@Ä#¡@Úþ-@ñØ¹AY¢AÆèA4-A&¡rA2žA={þAHéDATVA_ÃÏAk1AvYAÐAŒsAsA)ºAà]A A£M€A©GA®ºëAŽqAº(1A¿ÞÕAÅxAËLAÐôó¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >žÐ
+>Í¶z?×Î?êAü@"Ö@P-@~@E@ú®@¬Õ;@Ã¯Ç@ÚS@ñdßAµAúAú@A&gA1ÔËA=BAH¯VAT
+A_âAj÷'AvdmAèÚA}AV!A
+ÃAÃfAz
+A£0­AšçPA®ôAŽTAº
+;A¿ÁÞAÅxAË/%AÐåÈAÖjAÜEC¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >±f>Æx?U?èr@!îY@O£p@}X@Ð@¬a\@Ã;è@Úu@ðñAåÆAS
+AÀQA&-A1ÜA="AHugASâ¬A_OòAjœ8Av*~AËâAA9)AïÌAŠpA]A£µAšÊYA®üAŽ7A¹îCA¿€æAÅ[AË-AÐÈÐAÖtAÜ6Aáì¹Aç¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ªTÃ>¿:ó?8ì?æ£@!¥@N»»@|pÔ@ö@«í@ÂÈ@Ù¢@ð}&A«ÙAAdA%ó©A1`ïA<Î4AH;zASšÀA_AjJAuðA®ëAeA
+2AÒÖAyA@
+A¢öÀAš­bA®dAŽ©A¹ÑLA¿ïAÅ>AÊõ6AÐ«ÚAÖb}AÜ AáÏÄAçgAí=	Aòåâ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >£>·ý?is?äÓ¡@ 
+ç@MÓÿ@{@@«y€@ÂT0@Ù.Œ@ð	HAqéAß/ALuA%¹»A1' A<EAHASnÐA^ÜAjI\Au¶¡AôAHAÿ:AµÞAlA#%A¢ÙÈAškA®GA³ý±A¹ŽTA¿jøAÅ!AÊØ>AÐâAÖE
+AÛü)Aá²ÌAçioAí AòÖ¶AøXAþ61¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ù|>°¿??ã@@77@LìN@z¡g@+@@«Ì@ÁàX@Øºä@ïpA7þA¥CAA%ÎA0íA<ZZAGÇAS4äA^¢*AjpAu|µAtýA+ AâDAçAOA.A¢ŒÑAšsuA®*A³à»A¹_A¿NAÅ€AÊ»HAÐqëAÖ(AÛß2AáÕAçLyAí
+Aò¹¿AøpcAþ'BîÕBÃA¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >×>©è?
+Ê©?á4×@
+O@L@y¹²@·f@ªñ@Ál~@ØG
+@ï!AþAkVAØA%EáA0³&A< lAG²ARúøA^h=AiÕAuBÈAXAªAÅMA{ðA2Aé7A¢ÚAšV~A®
+!A³ÃÅA¹zhA¿1
+AÄç¯AÊQAÐTôAÖ
+AÛÂ;AáxÞAç/Aìæ%AòÉAøSlAþ
+BàYB»«BýB
+ki¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >^4>¢DD?û@?ßen@
+gÎ@K
+å@xÑþ@C@ª
+@Àø€@×Ó/@î­ŒAÄ#A1iA®A%
+ôA0y:A;æAGSÄARÁ
+A^.PAiAuÛA;A
+ñŽAšWA^úAAÌ@A¢äAš9A­ð*A³ŠÎA¹]qA¿AÄÊžAÊ[AÐ7ÿAÕî¡AÛ¥DAá[èAçAìÉ.AòÒAø6uAýíBÑÞB­0BB
+cÓB
+?%B¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  > > ?+×?Ý@
+@J51@wêJ@Ï±@©ª=@ÀÉ@×_U@î9âA6A
+÷|AdÁA$ÒA0?LA;¬AGØAR
+A]ôbAiašAtÎîA
+A
+ÔœAaABAø§A¯JA¢eíAš
+A­Ó4A³×A¹@zAŸ÷
+AÄ­ÁAÊdeAÐAÕÑ«AÛOAá>ñAæõAì¬8AòbÛAø~AýÐ"BÃcBŽBzB
+UXB
+0©B
+ûBçMB»¹¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >}ÅØ>Èü?\n?ÛÆ@e@IM}@w@[×@©6c@Àï@Öë{@íÆAPIA
+œA*ÕA$A0_A;r¥AFßêARM0A]ºvAi'»At A#A
+·ÆAnjA%
+AÛ±ATA¢H÷A§ÿA­¶=A³làA¹#AŸÚ'AÄÊAÊGnAÏþAÕŽµAÛkXAá!ûAæØAìAAòEäA÷üAý³+BŽçB9BkB
+FÜB
+".BýBØÑBŽ#BuBcá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >oJ>X?}
+?Ù÷3@°±@HeÈ@vá@çý@šÂ@¿@Öw¡@íR-A\A
+¡AðçA$^-A/ËsA;8žAF¥ýARCA]AhíÎAt[AÈYA
+ÐAQsAAŸºAu]A¢,A§â€A­GA³OêA¹AŸœ0AÄsÔAÊ*wAÏáAÕŸAÛNaAáAæ»šAìrKAò(ïA÷ßAý4BŠlBŸB]B
+8aB
+³BïBÊVB¥šBùB\KB
+7B!
+	¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >`ÏI>
+Mµ?y{8?Ø'Ë@Èý@G~@u3,@t"@šN®@¿);@ÖÆ@ìÞSAÜoA
+IµA¶úA$$?A/
+A:þËAFlAQÙVA]FAh³áAt!&AlA
+}ÙA4|Aë A¡ÃAXfA¢
+A§Å­A­|QA³2ôAžéAŸ :AÄVÝAÊ
+AÏÄ$AÕzÇAÛ1jAàèAæ±AìUUAò
+øA÷ÂAýy?BñBsBBNB
+)æB
+7BàB»ÛB,Br~BMÐB
+)!B!sB#ßÅB&Ž1¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >RT >|  ?uÜf?ÖXb@áH@F_@tKw@ H@§ÚÔ@Ÿµ`@Õì@ìjyA¢A
+ÇA}
+A#êSA/WA:ÄÝAF2#AQiA]
+¯AhyôAsç9ATA
+`ãAAÎ)AÌA;pA¡òA§š¶A­_ZA³ýAžÌ¡AŸDAÄ9çAÉðAÏ§-AÕ]ÐAÛtAàËAæºAì8^AñïA÷¥¥Aý\HBuBdÇB@B
+jB
+öŒBÒB­_B±BdB?TB
+ŠB õøB#ÑIB&¬B)íB,\Y¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >CØž>m€Ø?r=?Ôù@ù@E®«@scÃ@m@§fú@ŸA@Õ
+@ëöAhA
+ÕÚAC A#°eA/
+«A:ñAEø6AQe{A\ÒÁAh@As­MAA
+CìAúA±3AgÖA
+yA¡Õ
+A§ÀA­BcA²ùAž¯ªAŸfMAÄ
+ñAÉÓAÏ7AÕ@ÚAÚ÷}Aà® AædÄAìgAñÒ
+A÷®Aý?QBzúBVLB1B
+
+ïB
+èABÃBäBz6BUB0ÙB
+
++B ç|B#ÂÎB& B)yqB,TÃB/0B2¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >5]q>_)?nÃ?Ò¹@ß@DÆö@r|@@Šó@œÍ«@Ôš8@ëÄA.šA
+íA	3A#vxA.ãŸA:QAEŸIAQ+A\ÔAhAss_A~à¥A
+&öAÝA<AJàAA¡ž&A§nÉA­%lA²ÜAž³AŸIVAÃÿúAÉ¶AÏmAAÕ#äAÚÚAà*AæGÍAëþpAñµA÷k·Aý"ZBlBGÐB#"B	þtB
+ÙÅBµBiBkºBG
+B"^B
+ý¯B ÙB#ŽSB&€B)jöB,FHB/!B1üëB4Ø=B7¬©¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >&â)>P®?k ?Ðê/@*/@CßF@q^@€»@ŠG@œYÓ@Ô4_@ëëA ô»A
+bAÏGA#<A.©ÒA:AE]APñ¢A\^èAgÌ.As9sA~Š¹A
+
+ AÀ£AwGA-êAäA¡1A§QÔA­wA²¿AžuœAŸ,aAÃãAÉ§AÏPKAÕîAÚœAàt5Aæ*ØAëá{Añ
+A÷NÁAýeB^B9UB§B	ïøB
+ËJBŠBíB]?B8BâB
+ï4B ÊB#¥×B&)B)\{B,7ÌB/
+B1îpB4ÉÁB7¥B:eB=TÑ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >fâ>B3?ga
+?ÏŸ@Bv@B÷@p¬Š@0ß@Š
+k@Œå÷@ÓÀ@êA ºÍA
+(AXA#A.oäA9Ý)AEJoAP·ŽA\$úAg?Arÿ
+A~lÊAíA£¬AZOAòAÇA¡~9A§4ÜA¬ëA²¢#AžXÆAŸiAÃÆ
+AÉ|°AÏ3SAÔéöAÚ AàW=Aæ
+áAëÄAñ{'A÷1ÊAüèmBOB*ÚB,B	á}B
+ŒÏB BsrBNÄB*BgB
+à¹B Œ
+B#\B&r®B)MÿB,)QB/£B1ßôB4»FB7B:qéB=M;B@(BBüù¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >	ë>3·?cÂD?ÍKQ@ZÀ@B×@oÄï@œ@¥@Œr
+@ÓLš@ê'4A àA
+î%A[kA"È±A.5öA9£<AEAP}ÇA[ë
+AgXRArÅA~2ÝAÐAµA=XAóüAªA¡aBA§æA¬ÎA²
+,Až;ÐAœòsAÃ©AÉ_¹AÏ\AÔÍ AÚ£Aà:FAåðêAë§Añ^1A÷ÔAüËwBA
+B
+_B÷°B	ÓB
+®TB¥Bd÷B@HBBöìB
+Ò=B ­B#áB&d2B)?B,ÖB.ö'B1ÑyB4¬ËB7
+B:cnB=>ÀB@BBõcBEÐµBH¥!¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =öà©>%<3?`#k?Ë{ä@s	@A(!@nÝ9@I)@¥#Ž@»þ@@ÒØÍ@é³YA FòA
+Ž8A!}A"ÂA-üA9iNADÖAPCÙA[±Ag
+dArªA}øñA³
+AiŸA bA×AšA¡DLAŠúïA¬±A²h6Až
+ÙAœÕ|AÃ AÉBÃAÎùfAÔ°	AÚf¬Aà
+PAåÓóAëAñA:Aö÷ÝAü®B2B
+ãBé5B	ÄB
+ØB{*BV|B1ÍB
+BèpB
+ÃÂB B#zeB&U·B)1	B,
+ZB.ç¬B1ÂþB4OB7y¡B:TóB=0DB@
+BBæèBEÂ9BHBKxÝBNMI¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =Ùê>Àí?\?É¬{@U@@@l@mõ@ÕN@€¯Ú@»g@Òdò@é?~A 
+A
+zJAçA"TÖA-ÂA9/`ADŠAP	ìA[w1AfäwArQœA}¿A%ALÈAlAºAp²A¡'UAŠÝøA¬A²K?AžâAœžAÃo)AÉ%ÌAÎÜpAÔAÚI¶Aà YAå¶üAëm Añ$CAöÚæAüB$BÿhBÚºB	¶
+B
+]Bl¯BH B#RBþ€BÙõB
+µGB B#kêB&G<B)"B+ýßB.Ù1B1ŽB4ÔB7k&B:FwB=!ÉB?ýBBØlBE³ŸBHBKjaBNE³BQ!BSõq¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =Œó>E?XåÀ?ÇÝ@£@?X¶@m
+Î@as@€;ÿ@»@Ññ@èË€@ÿŠ0A
+@]A­¢A"èA-.A8õtADb¹AOÏþA[=DAfªArÏA}
+Ay.A/ÒAæuAASŒA¡
+^AŠÁA¬w¥A².HA·äìAœAÃR2AÉÖAÎ¿yAÔv
+AÚ,ÀAßãcAåAëP©AñLAöœðAütBBðíBÌ>B	§B
+âB^3B9
+B×Bð(BËzB
+ŠÌB 
+B#]oB&8ÀB)B+ïdB.ÊµB1ŠB4YB7\ªB:7üB=NB?îBBÉñBE¥CBHBK[æBN78BQBSíÛBVÉ-BY¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =ý=ó@?UFæ?Æ
+¡@»ç@>pÿ@l&@í@£È$@º¢°@Ñ}<@èWÈ@ÿ2SA
+oAsµA!àúA-N?A8»
+AD(ËAOA[VAfpAqÝáA}K'A\7AÚAÉ}A!A6ÄA ígAŠ€
+A¬Z­A²QA·ÇôAœ~AÃ5;AÈëÞAÎ¢AÔY%AÚÈAßÆkAå}Aë3²AðêUAö øAüWBBâpBœÂB	B
+teBO·B+	BZBá¬BŒþB
+OB s¡B#NóB&*DB)B+àçB.Œ9B1B4rÜB7N.B:)B=ÑB?à#BB»uBEÆBHrBKMjBN(»BQ
+BSß_BVº°BYB\qTB_EÀ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =t=Ö?Qš?Ä>6@Ô2@=I@k>a@yœ@£TH@º.Õ@Ñ	a@çãí@þŸyA
+ÌA9ÈA!§
+A-SA8ACîÝAO\#AZÉiAf6¯Aq£ôA}9A?@AõãA¬Ac*AÍA ÐqAŠA¬=·A±ô[A·ªýAœa¡AÃDAÈÎçAÎ
+AÔ<.AÙòÑAß©uAå`Aë»AðÍ_AöAü:¥B ø£BÓõB¯GB	B
+eêBA<B
+B÷ßBÓ1B®B
+ÔB e&B#@wB&ÉB(÷B+ÒlB.­ŸB1B4daB7?³B:B<öVB?ÑšBB¬ùBEKBHcBK>îBN@BPõBSÐãBV¬5BYB\bØB_>*Bb|Bdíè¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =LÍ=¹Šä?N	:?ÂnÌ@ì}@<¡@jV¬@â@¢ào@¹ºû@Ð@çp@þJA
+AÿÚA!m A,ÚfA8G«ACŽñAO"6AZ{AeüÁAqjA|×LA"IAØíAAF3Aü×A ³zAŠj
+A¬ ÁA±×dA·AœD«AÂûMAÈ±ñAÎhAÔ7AÙÕÛAß~AåC!AêùÅAð°hAög
+Aü
+¯B ê(BÅzB ÌB	|
+B
+WoB2ÀBBédBÄµB B
+{YB VªB#1üB&
+NB(èB+ÃñB.CB1zB4UæB717B:
+B<çÛB?Ã,BB~BEyÐBHU!BK0sBN
+ÅBPçBSÂhBVºBYy
+B\T]B_/¯Bb
+ BdæRBgÁ€Bj¿  ¿  ¿  ¿  ¿  ¿  =2³=°8?Jjd?À`@Ç@;¹ß@inö@@¢l@¹G@Ð!¬@æü8@ýÖÂA
+X§AÅíA!32A, wA8
+œAC{ANèHAZUAeÂÓAq0A|`ASA»öArA)=AßàA AŠM'A¬ÊA±ºmA·qAœ'ŽAÂÞWAÈûAÎKAÔAAÙžäAßoAå&+AêÜÎAðqAöJAü žB Û­B¶ÿBPB	m¢B
+HôB$EBÿBÚèB¶:BB
+lÝB H/B##B%þÒB(Ú$B+µvB.ÇB1lB4GkB7"ŒB9þB<Ù_B?Ž±BBBEkTBHFŠBK!øBMýIBPØBS³íBV>BYjB\EâB_!3Baü
+Bd××Bg³(BjzBmiÌBp>8¿  ¿  ¿  ¿  <°/=s4?FË?ŸÏø@
+
+@:Ò*@hB@
+-@¡ø¹@žÓE@Ï­Ñ@æ]@ýbèA
+
+¹AÿA ùEA,fA7ÓÐACAAN®[AZ¡AeæApö,A|csAè]A AU£A
+FAÂéA yAŠ00A«æÓA±wA·TAœ
+œAÂÁaAÈxAÎ.§AÓåKAÙíAßRAå	4Aê¿×Aðv{Aö-
+AûãÁB Í2BšBÕB	_'B
+:xBÊBñ
+BÌmB§¿BB
+^bB 9ŽB#B%ðWB(Ë©B+ŠúB.LB1]B48ïB7AB9ïB<ÊäB?Š6BBBE\ÙBH8+BK|BMîÎBPÊ BS¥qBVÃBY\B\7fB_žBaî
+BdÉ[Bg€­BjÿBm[PBp6¢BsôBuæ`¿  ¿  ;òÃë=E
+ê?C,œ?œ @
+5^@9êu@g@ªS@¡ß@ž_k@Ï9÷@æ@üïA	äÍARA ¿WA,,A7ãAC)ANtnAYá³AeNùApŒ?A|)
+AËfA	A8­AïPA¥óA \AŠ9A«ÉÝA±A·7#AŒíÇAÂ€jAÈ[
+AÎ±AÓÈTAÙ~÷Aß5Aäì=Aê¢áAðYAö'AûÆËB Ÿ·BBuZB	P«B
++ýBOBâ BœòBDBtB
+OçB +8B#B%áÜB(œ-B+B.sÑB1O"B4*tB7ÆB9áB<ŒiB?»BBs
+BEN^BH)¯BKBMàSBP»€BSöBVrHBYMB\(ëB_=BaßBdºàBg2BjqBmLÕBp('BsxBuÞÊBxº
+B{    =
+??è?»1"@
+Mš@9¿@f·×@6x@¡@·ë@ÎÆ
+@å š@ü{4A	ªàA%A 
+kA+ò°A7_õABÍ;AN:AY§ÇAe
+ApQA{ïA®oAeA¶AÒYAýA ? A¥öCA«¬æA±cA·-AŒÐÐAÂsAÈ>AÍôºAÓ«]AÙbAß€AäÏGAê
+ëAð<Aõó1Aû©ÔB °;BBfßB	B0B
+
+BøÓBÔ%B¯wBÈBfB
+AlB 
+œB"øB%Ó`B(®²B+B.eUB1@§B4ùB6÷JB9ÒB<­îB??BBdBE?ãBH4BJöBMÑ×BP­)BS{BVcÌBY?
+B\pB^õÁBaÑBd¬eBg¶BjcBm>ZBp«BrôýBuÐOBx« B{òB~bD¿  ?=š\?¹a·@
+eó@8
+@eÐ"@Â@ *@·w¶@ÎRB@å,Î@üZA	pòAÞ8A K~A+žÃA7&	ABNAN AYmÙAdÛApHeA{µ«AyAH
+Aþ¿AµcAlA "©A¥ÙMA«ðA±FA¶ý6AŒ³ÙAÂj}AÈ! AÍ×ÃAÓgAÙE
+AÞû­Aä²QAêhôAðAõÖ;AûÝB ¡ÀB}BXcB	3µB
+BêXBÅªB ûB|MBWB
+2ðB BB"éB%ÄåB( 7B+{B.VÚB12,B4
+}B6èÏB9Ä!B<rB?zÄBBVBE1gBH
+¹BJè
+BMÃ\BP®BSyÿBVUQBY0£B\
+ôB^çFBaÂBdéBgy;BjTBm/ÞBp
+0BræBuÁÓBx%B{xwB~SÈB~}¿  ¿  @	ì@73U@dèl@NÂ@ )N@·Û@ÍÞg@äžó@û~A	7A€JA A+~ÕA6ìABY`AMÆŠAY3ëAd¡0ApvA{{ŒAtA+$AáÈAkAOA ²A¥ŒUA«røA±)A¶à?AŒâAÂM
+AÈ(AÍºÌAÓqoAÙ(AÞÞ¶AäYAêKüAð Aõ¹CAûoæB EBnBIèB	%:B
+ BÛÝB·/BBmÒBI#B
+$uBÿÇB"ÛB%¶jB(ŒB+m
+B.H_B1#°B3ÿB6ÚTB9µ¥B<÷B?lIBBGBE"ìBGþ>BJÙBMŽáBP3BSkBVFÖBY"'B[ýyB^ØËBaŽ
+BdnBgjÀBjFBm!cBoüµBrØBu³XBxªB{iûB~EMB~o¿  ¿  ¿  ¿  @do
+@Úè@µt@¶ @Íj@äE@û£AýAj]A×£A+DèA6²-ABsAM¹AXùþAdgDAoÔA{AÏAWA.AÄÑA{tA2Aè»A¥^A«VA±
+¥A¶ÃHAŒyìAÂ0AÇç2AÍÕAÓTxAÙ
+
+AÞÁ¿AäxbAê/Aïå©AõLAûRðB ÉB`B;mB	ŸB
+òBÍbBš³BB_WB:šB
+úBñKB"ÌB%§ïB(@B+^B.9äB15B3ðB6ËØB9§*B<|B?]ÍBB9BEqBGïÂBJËBMŠfBP·BS]	BV8[BY¬B[îþB^ÊOBa¥¡BdóBg\DBj7BmèBoî9BrÉBu€ÝBx.B{[B~6ÒB~`¿  ¿  ¿  ¿  ¿  ¿  @xÂ@¶
+%@Ìö±@ãÑ=@ú«ÊAÃ*A0pAµA+
+ûA6xAAAåAMRËAXÀAd-WAoA{âA:Añ8A§ÛA^~A!AËÄA¥hA«9
+A°ï®A¶ŠRAŒ\õAÂAÇÊ<AÍßAÓ7AØî%AÞ€ÈAä[lAêAïÈ²AõVAû5ùB vNBQ B,ñB	CB
+ãBŸæB8BuBPÛB,-B
+BâÐB"Ÿ"B%sB(tÅB+PB.+hB1ºB3â
+B6œ]B9¯B<t B?ORBB*€BEõBGáGBJŒBMêBPs<BSNBV)ßBY1B[àB^»ÔBa&BdrwBgMÉBj)BmlBoßŸBr»BuaBxq³B{MB~(VB~R¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @Ìº @ã]c@ú7ïA=AöAcÈA*ÑA6>SAA«AMßAX$AcóiAo`¯AzÍõA
+AÔAAäAAAø+A®ÎA¥eqA«
+A°ÒžA¶[AŒ?þAÁö¢AÇ­EAÍcèAÓAØÑ/AÞÒAä>uAéõAï«ŒAõb_AûB gÓBC$B
+vBùÈB
+ÕB°kBœBgBB`B
+²B
+ùBÔUB"¯§B%øB(fJB+AB.
+íB0ø?B3ÓB6®âB94B<e
+B?@×BB
+(BD÷zBGÒÌBJ®
+BMoBPdÁBS@BVdBXö¶B[ÒB^­YBa«BdcüBg?NBjBlõñBoÑCBr¬BuæBxc8B{>B~ÛB~C¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ùû<AOOAŒA)ÚA* A6eAAq«ALÞðAXL6Ac¹|Ao&ÁAzA §A·JAmîA$AÛ4AØA¥H{Aªÿ
+A°µÁA¶ldAŒ#AÁÙ«AÇNAÍFòAÒýAØŽ8AÞjÜAä!AéØ"AïÅAõEhAúü
+B YXB4ªBûBëLB
+ÆB¡ðB}ABXB3åB6B
+êBÅÚB"¡+B%|}B(WÏB+3 B.rB0éÃB3ÅB6 gB9{žB<W
+B?2\BB
+­BDèÿBGÄPBJ¢BMzôBPVEBS1BV
+éBXè:B[ÃB^ÞBaz/BdUBg0ÓBj
+$BlçvBoÂÇBrBuykBxTŒB{0B~
+`B~5 ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A<A
+ïíA*]3A5ÊxAA7ŸAL¥AXIAcAnìÔAzZAã°ATAP÷AAŸ>AtáA¥+Aªâ(A°ËA¶OnAŒAÁŒŽAÇsXAÍ)ûAÒàAØBAÞMåAäAé»,AïqÏAõ(rAúßB JÜB&.BBÜÒB
+ž#BtBnÆBJB%iB »B
+Ü
+B·^B"°B%nB(ISB+$¥B-ÿ÷B0ÛHB3¶B6ëB9m=B<HB?#àBAÿ2BDÚBGµÕBJ'BMlxBPGÊBS#
+BUþmBXÙ¿B[µB^bBakŽBdGBg"WBiý©BlØûBoŽLBrBujïBxFAB{!B}üäB~&¥¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A*>ÚA5A@ýÑALkAWØ\AcE¡An²çAz .AÆ¹A}]A4 Aê€A¡GAWêA¥AªÅ1A°{ÔA¶2xA»éAÁŸAÇVaAÍ
+AÒÃšAØzKAÞ0îAãçAé5AïTØAõ
+|AúÂB <aB³BóBÎVB
+©šBúB`KB;BîBò@B
+ÍBšãB"5B%_B(:ØB+*B-ñ{B0ÌÍB3šB6pB9^ÂB<:B?eBAð·BDÌBG§ZBJ«BM]ýBP9OBS BUïòBXËDB[ŠB^çBa]9Bd8BgÜBiï.BlÊBo¥ÑBr#Bu\tBx7ÆB{B}îiB~*¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A@ßwAL1(AWnAc
+³AnxùAyæ>A©ÃA`fAAÍ¬AOA:óA€ñAªš9A°^ÝA¶A»Ì#AÁÇAÇ9jAÌð
+AÒŠ°AØ]SAÞ÷AãÊAé=Aï7áAôîAú¥'B -äB	6BäB¿ÚB
+,Bv}BQÏB-!BrBãÃB
+¿BgB"užB%Q
+B(,\B+­B-âÿB0ŸQB3¢B6tôB9PFB<+B?éBAâ:BDœBGÞBJt/BMOBP*ÒBS$BUávBXŒÇB[B^skBaNŒBd*Bg`Bià±BlŒBoUBrrŠBuMøBx)JB{B}ßíB~	®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AWAbÑÆAn?
+Ay¬QAÌACoAúA°¶AgXA
+üA€ÔAªCA°AæAµøA»¯-AÁeÐAÇ
+sAÌÓAÒºAØ@]AÝ÷ Aã­£AédGAïêAôÑAú0B iBú»BÖ
+B±^B
+°BhBCTB
+¥Bù÷BÕIB
+°BëB"g=B%BB(
+àB*ù2B-ÔB0¯ÕB3'B6fyB9AÊB<
+
+B>ønBAÓ¿BD¯BGbBJeŽBMABP
+WBR÷©BUÒúBX®LB[B^dïBa@ABdBföäBiÒ6Bl­BoÙBrd+Bu?}BxÎBzö B}ÑrB}û3¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  An ²AyrdAoÕA&yAÝ
+A¿AJcAA€·šAªnLA°$ïAµÛA»6AÁHÙAÆÿ}AÌ¶ AÒlÃAØ#gAÝÚ
+Aã­AéGPAîýóAôŽAúk:B îBì?BÇB¢ãB
+~4BYB4ØB*Bë|BÆÍB
+¢B}qB"XÂB%4B(eB*ê·B-ÆB0¡ZB3|¬B6WýB93OB<¡B>éòBAÅDBD BG{çBJW9BM2BP
+ÜBRé.BUÄBXÑB[{"B^VtBa1ÆBd
+BfèiBiÃ»Bl
+Boz^BrU°Bu1Bx
+SBzç¥B}ÂöB}ìž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A`©A	AÀ%AvÉA-lAäA€³AªQVA°øAµŸA»u?AÁ+ãAÆâAÌ)AÒOÍAØpAÝœAãs·Aé*ZAîàýAô AúNCB sBÝÅB¹BgB
+o¹BK
+B&\B®BÝ BžRB
+€BnõB"JGB%%B( êB*Ü;B-·B0ßB3n0B6IB9$ÔB< %B>ÛwBA¶ÉBDBGmlBJHŸBM$BOÿaBRÚ²BU¶BXVB[l§B^GùBa#JBcþBfÙîBiµ?BlBokãBrG4Bu"BwýØBzÙ)B}Ž{B}Þ=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A°ùAYÒAuAÇA€}ŒAª4_A¯ëAµ¡ŠA»XHAÁìAÆÅAÌ|3AÒ2ÖA×éyAÝ 
+AãVÀAé
+cAîÄAôzªAú1MAÿçïBÏIBªB
+íB
+a>B<BáBó3BÎB©ÖB
+
+(B`zB";ÌB%
+B'òoB*ÍÁB-©B0cB3_µB6;B9XB;ñªB>ÌüBAšMBDBG^ñBJ:BBMBOðæBRÌ7BU§BXÚB[^,B^9~BaÏBcð!BfËrBiŠÄBlBo]gBr8¹Bu
+Bwï\BzÊ®B}Š B}ÏÁ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AIAª"A€`ÅAªiA¯Î
+Aµ¯A»;SAÀñöAÆšAÌ_<AÒßA×ÌAÝ&Aã9ÉAèðmAî§Aô]³AúWAÿÊøBÀÎB BwqB
+RÃB.B	fBä·BÀ	B[B
+v¬BQþB"-PB%¢B'ãôB*¿EB-B0uéB3Q:B6,B9ÝB;ã/B>ŸBAÒBDu$BGPuBJ+ÇBMBOâjBRœŒBUBXt_B[O±B^+BaTBcáŠBfŒ÷BiIBlsBoNìBr*>BuBwàáBzŒ3B}B}ÁF¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A€QA©úrA¯±Aµg¹A»
+\AÀÔÿAÆ£AÌBFAÑøèA×¯AÝf/Aã
+ÓAèÓvAîAô@œAù÷`Aÿ®B²SB€BhöB
+DHBBúëBÖ=B±BßB
+h1BCB"
+ÔB$ú&B'ÕxB*°ÊB-
+B0gmB3B¿B6
+B8ùbB;Ô³B>°BAWBDfšBGAúBJ
+LBLøBOÓïBR¯ABUBXeäB[A6B^
+B`÷ÙBcÓ*Bf®|BiÎBleBo@qBrÂBt÷BwÒfBz­·B}	B}²Ë¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A¯¡éAµJÂA»eAÀž	AÆn¬AÌ%OAÑÛóA×AÝI8AâÿÜAè¶Aîm#Aô#ÆAùÚiAÿ
+B£×B)BZ{B
+5ÌB
+BìpBÇÁB£B~eB
+Y¶B5B"YB$ë«B'ÆüB*¢NB-} B0XòB34DB6B8êçB;Æ9B>¡BA|ÛBDX-BG3BJÐBLê"BOÅtBR ÅBU|BXWiB[2ºB^
+B`é^BcÄ¯Bf Bi{RBlV€Bo1öBr
+GBtèBwÃêBz<B}zB}€O¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aºò9AÀAÆQµAÌYAÑŸüA×uAÝ,CAââæAèAîP,AôÏAùœsAÿtB\Bp®BKÿB
+'QB£BÝôB¹FBBoéB
+K;B&B"ÞB$Ý/B'žB*ÓB-o$B0JvB3%ÈB6B8ÜlB;·œB>BAnaBDI²BG%BJ UBLÛ§BO¶øBRJBUmBXHíB[$?B]ÿB`ÚâBc¶4BfBil×BlH)Bo#zBqþÌBtÚ
+BwµoBzÁB}lB}Ô¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÆBAËëbAÑ¢A×X©AÝLAâÅïAè|Aî36AóéØAù |AÿWBáBb3B=B
+ÖB
+ô'BÏyBªËB
+BanB
+<ÀBB!ócB$ÎµB'ªB*
+WB-`©B0;ûB3LB5òB8ÍðB;©BB>BA_åBD;7BGBIñÚBLÍ+BOš}BRÏBU_ BX:rB[ÄB]ñB`ÌgBc§¹Bf
+Bi^\Bl9®BoÿBqðQBtË¢BwŠôBzFB}]B}Y¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÑÙA×;²AÜòUAâšùAè_Aî?AóÌãAùAÿ:'BxfBS·B/	B
+
+[B
+å¬BÀþBOBw¡BRóB
+.DB	B!äèB$À9B'B*vÝB-R.B0-B3ÑB5ä#B8¿tB;ÆB>vBAQjBD,ŒBG
+BIã_BLŸ±BOBRuSBUP¥BX+÷B[HB]âB`œìBc=BftBiOáBl+2BoBqáÖBtœ'BwyBzsÊB}O
+B}xÞ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÜã)AâAèB¥AíùIAó¯ìAùfAÿ
+1BiêBE<B B
+ûßB
+×1B²BÔBi&BDwB
+ÉB
+ûB!ÖlB$±ŸB'B*haB-C³B0B2úVB5Õ§B8°ùB;KB>gBABîBD
+@BFùBIÔäBL°5BOBRfÙBUB*BX
+{BZøÍB]ÔB`¯pBcÂBffBiAeBl
+·Bnø	BqÓZBt®¬BwþBzeOB}@¡B}jb¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aè3yAíÜRAóõAùIAÿ ;B[oB6ÁBB
+ídB
+È¶B€BYBZ«B5üB
+NB
+ìB!ÇñB$£CB'~B*YæB-58B0B2ëÛB5Ç-B8¢~B;}ÏB>Y!BA4sBDÄBFëBIÆhBL¡ºBO}
+BRX]BU3¯BXBZêRB]Å£B` õBc|GBfWBi2êBl<BnéBqÄßBt 1Bw{BzVÔB}2&B}[ç¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AóÉAù,¢AþãDBLôB(EBB
+ÞéB
+º:BBpÞBL/B'B
+ÓB
+Þ$B!¹vB$ÇB'pB*KkB-&ŒB0B2Ý`B5ž±B8B;oUB>JŠBA%÷BDIBFÜBI·ìBL>BOnBRIâBU%4BX 
+BZÛ×B]·)B`zBcmËBfI
+Bi$oBkÿÀBnÛBq¶dBtµBwmBzHYB}#ªB}Ml¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AþÔB>xBÊBõ
+B
+ÐmB
+«¿BBbbB=ŽBBôWB
+Ï©B!ªûB$LB'aB*<ïB-AB/óB2ÎäB5ª6B8
+B;`ÙB><+BA}BCòÎBFÎBI©qBLÃBO`BR;fBUžBWò
+BZÍ\B]š­B`ÿBc_QBf:¢BióBkñEBnÌBq§èBt:Bw^Bz9ÝB}/B}>ñ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B4Bæ B
+ÁòB
+DBxBSçB/9B
+BåÜB
+Á.B!B$wÑB'S#B*.tB-	ÆB/åB2ÀiB5»B8w
+B;R^B>-°BA	BCäSBF¿¥BIöBLvGBOQBR,ëBU<BWãBZŸàB]2B`uBcPÕBf,'BiyBkâÊBnŸBqmBtt¿BwPBz+bB}ŽB}0u¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+º\B
+ÈBjBElB œBüB×aB
+²²B!B$iVB'D§B*ùB,ûKB/ÖB2±îB5?B8hB;CãB>4B@úBCÕØBF±)BI{BLgÍBOC
+BR
+oBTùÁBWÕBZ°dB]¶B`gBcBZBf
+¬BhøýBkÔOBn¯¡BqòBtfCBwABz
+çB|ø8B}!ú¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BbB6ïBABíBÈäB
+€6B!B$ZÙB'6+B*}B,ìÎB/È B2£rB5~ÃB8ZB;5fB>žB@ì
+BCÇ[BF¢­BI}ÿBLYPBO4¢BRôBTëEBWÆBZ¡èB]}:B`XBc3ÝBf/BhêBkÅÓBn¡$Bq|vBtWÈBw3BzjB|éŒB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+«BßBºiB
+»B!q
+B$L^B''°B*B,ÞSB/¹¥B2öB5pHB8KB;&ëB>=B@ÝBCžàBF2BIoBLJÕBO&'BRxBTÜÊBWž
+BZmB]nŸB`JBc%bBf ³BhÜBk·WBn©BqmûBtILBw$ByÿðB|ÛAB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B²ÓB
+?B!bB$=ãB'4B)ôB,ÏØB/«)B2{B5aÍB8=
+B;pB=óÂB@ÏBCªeBF
+¶BIaBL<ZBO«BQòýBTÎOBW© BZòB]`DB`;BcæBeò8BhÍBkšÛBn-Bq_Bt:ÑBw#ByñtB|ÌÆB|ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B!ZûB$/gB'
+¹B)æ
+B,Á\B/®B2x B5SQB8.£B;	õB=åFB@ÀBCêBFw;BIRBL-ÞBO	0BQäBT¿ÓBW%BZvwB]QÈB`-BclBeãœBh¿Bk`Bnu²BqQBt,UBw§ByâùB|ŸKB|è
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B'#B)×B,²áB/3B2iB5DÖB8 (B:ûyB=ÖËB@²
+BCnBFhÀBIDBLcBNúµBQÖBT±XBWªBZgûB]CMB`
+BbùðBeÕBBh°BkåBng6BqBBt
+ÚBvù+ByÔ}B|¯ÏB|Ù¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B,«JB/¶B2[B56ZB8«B:ìýB=ÈOB@£ BC~òBFZDBI5BLçBNì9BQÇBT¢ÜBW~-BZYB]4ÑB`"BbëtBeÆÆBh¢Bk}iBnX»Bq4
+Bt]Bvê¯ByÆB|¡RB|Ë¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B2SrB5'ÞB80B:ÞB=¹ÓB@%BCpwBFKÈBI'BLlBNÝœBQ¹BTaBWo²BZKB]&UB`§BbÜùBežJBhBknîBnJ?Bq%Bt ãBvÜ4By·
+B|×B|Œ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B7ûB:ÐB=«XB@ªBCaûBF=MBIBKóðBNÏBBQªBT
+åBWa7BZ<B]ÚB_ó,BbÎ}Be©ÏBh
+!Bk`rBn;ÄBqBsògBvÍ¹By©
+B|\B|®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B=£ÂB@x.BCSBF.ÒBI
+#BKåuBNÀÇBQBTwjBWRŒBZ.
+B]	_B_ä±BbÀBeTBhv¥BkQ÷Bn-IBqBsãìBv¿>ByB|uáB|¥¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BCKéBF UBHû§BKÖùBN²JBQBThîBWD?BZB\úãB_Ö4Bb±BeØBhh)BkC{Bn
+ÌBpú
+BsÕpBv°ÁByB|geB|)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BHôBKÈ}BN£ÏBQ!BTZrBW5ÄBZB\ìgB_Ç¹Bb£
+Be~\BhY®Bk5 BnQBpë£BsÆôBv¢FBy}B|XéB|®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BN9BQp¥BTK÷BW'IBZB\ÝìB_¹>BbBeoáBhK3Bk&BnÖBpÝ(BsžyBvËByo
+B|JnB|t3¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BTDaBWÍBYôB\ÏqB_ªÂBbBeafBh<·Bk	Bmó[BpÎ¬Bs©þBv
+PBy`¡B|;óB|e·¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BYìB\ÀôB_FBbwBeRéBh.;Bk	BmäÞBpÀ0BsBvvÓByR%B|-wB|W<¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B_°Bbi
+BeDnBhÀBjûBmÖcBp±µBsBvhXByCªB|
+ûB|HÁ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Be<ØBhDBjìBmÇèBp£9Bs~BvYÝBy5.B|B|:F¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bjå Bm¹lBpŸBspBvKaBy&³B|B|+Ê¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bp'BsaBv<åBy7B{óB|
+O¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bv5OBy	»B{å
+B|Ô¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B{ÝwB| X¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 <#×
+<£×
+<õÂ=#×
+=LÌÍ=uÂ=\)=£×
+=žQì=ÌÌÍ=áG®=õÂ>
+ž>\)>>#×
+>.{>8Qì>B\>LÌÍ>W
+=>aG®>k
+>uÂ>  >
+
+ž>=q>\)>zá>>žR>£×
+>šõÃ>®{>³33>žQì>œp€>Â\>Ç®>ÌÌÍ>Ñë
+>×
+=>Ü(ö>áG®>æff>ë
+>ð£×>õÂ>úáH?   ?\?
+ž?®?
+=q?
+ÌÍ?\)?ë
+?zá?
+=??
+(ö?
+žR?!G®?  ?G®?\?×
+?
+
+ž?ff?®?õÃ?=q?
+?ÌÍ?{?\)?£×?ë
+?33?zá?Â?
+>?Qì??áH?(ö?p€?žR?   ?¡G®?¢\?£×
+?¥
+ž?Šff?§®?šõÂ?ª=q?«
+?¬ÌÍ?®{?¯\)?°£×?±ë
+?³33?Žzá?µÂ?·
+>?žQì?¹?ºáH?Œ(ö?œp€?ŸžR?À  ?ÁG®?Â\?Ã×
+?Å
+ž?Æff?Ç®?ÈõÂ?Ê=p?Ë
+
+?ÌÌÍ?Î{?Ï\)?Ð£×@   @ £×@G®@ë
+@\@33@×
+@zá@
+ž@Â@ff@
+=@®@Qì@õÃ@	@
+=q@
+áH@
+
+@
+(ö@
+ÌÍ@
+p€@{@žR@\)@  @£×@G®@ë
+@\@33@×
+@zá@
+ž@Â@ff@
+>@®@Qì@õÂ@@=q@áH@
+@
+(ö@
+ÌÍ@
+p€@
+{@
+žR@\)@   @ £×@!G®@!ë
+@"\@#33@#×
+@$zá@%
+ž@%Â@&ff@'
+>@'®@(Qì@@  @@£×@AG®@Aë
+@B\@C33@C×
+@Dzá@E
+ž@EÂ@Fff@G
+=@G®@HQì@HõÃ@I@J=q@JáH@K
+@L(ö@LÌÍ@Mp€@N{@NžR@O\)@P  @P£×@QG®@Që
+@R\@S33@S×
+@Tzá@U
+ž@UÂ@Vff@W
+>@W®@XQì@XõÂ@Y@Z=q@ZáH@[
+@\(ö@\ÌÍ@]p€@^{@^žR@_\)@`  @`£×@aG®@aë
+@b\@c33@c×
+@dzá@e
+ž@eÂ@fff@g
+>@g®@hQì@  @Qì@£×@õÃ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@zá@ÌÍ@
+
+ž@
+p€@
+Â@{@ff@žR@
+=@\)@®@  @Qì@£×@õÃ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@zá@ÌÍ@
+ž@p€@Â@{@ff@žR@
+=@\)@®@  @Qì@£×@õÂ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@   @ Qì@ £×@ õÃ@¡G®@¡@¡ë
+@¢=q@¢\@¢áH@£33@£
+@£×
+@€(ö@€zá@€ÌÍ@¥
+ž@¥p€@¥Â@Š{@Šff@ŠžR@§
+=@§\)@§®@š  @šQì@š£×@šõÃ@©G®@©@©ë
+@ª=q@ª\@ªáH@«33@«
+@«×
+@¬(ö@¬zá@¬ÌÍ@­
+ž@­p€@­Â@®{@®ff@®žR@¯
+=@¯\)@¯®@°  @°Qì@°£×@°õÂ@±G®@±@±ë
+@²=q@²\@²áH@³33@³
+@³×
+@Ž(ö@À  @ÀQì@À£×@ÀõÃ@ÁG®@Á@Áë
+@Â=q@Â\@ÂáH@Ã33@Ã
+@Ã×
+@Ä(ö@Äzá@ÄÌÍ@Å
+ž@Åp€@ÅÂ@Æ{@Æff@ÆžR@Ç
+=@Ç\)@Ç®@È  @ÈQì@È£×@ÈõÃ@ÉG®@É@Éë
+@Ê=q@Ê\@ÊáH@Ë33@Ë
+@Ë×
+@Ì(ö@Ìzá@ÌÌÍ@Í
+ž@Íp€@ÍÂ@Î{@Îff@ÎžR@Ï
+=@Ï\)@Ï®@Ð  @ÐQì@Ð£×@ÐõÂ@ÑG®@Ñ@Ñë
+@Ò=q@Ò\@ÒáH@Ó33@Ó
+@Ó×
+@Ô(ö@à  @àQì@à£×@àõÃ@áG®@á@áë
+@â=q@â\@âáH@ã33@ã
+@ã×
+@ä(ö@äzá@äÌÍ@å
+ž@åp€@åÂ@æ{@æff@æžR@ç
+=@ç\)@ç®@è  @èQì@è£×@èõÃ@éG®@é@éë
+@ê=q@ê\@êáH@ë33@ë
+@ë×
+@ì(ö@ìzá@ìÌÍ@í
+ž@íp€@íÂ@î{@îff@îžR@ï
+=@ï\)@ï®@ð  @ðQì@ð£×@ðõÂ@ñG®@ñ@ñë
+@ò=q@ò\@òáH@ó33@ó
+@ó×
+@ô(öA   A (öA QìA záA £×A ÌÍA õÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA	
+žA	G®A	p€A	A	ÂA	ë
+A
+{A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A   A (öA QìA záA £×A ÌÍA õÃA!
+žA!G®A!p€A!A!ÂA!ë
+A"{A"=qA"ffA"\A"žRA"áHA#
+=A#33A#\)A#
+A#®A#×
+A$  A$(öA$QìA$záA$£×A$ÌÍA$õÃA%
+žA%G®A%p€A%A%ÂA%ë
+A&{A&=qA&ffA&\A&žRA&áHA'
+=A'33A'\)A'
+A'®A'×
+A(  A((öA(QìA(záA(£×A(ÌÍA(õÃA)
+žA)G®A)p€A)A)ÂA)ë
+A*{A0  A0(öA0QìA0záA0£×A0ÌÍA0õÃA1
+žA1G®A1p€A1A1ÂA1ë
+A2{A2=qA2ffA2\A2žRA2áHA3
+=A333A3\)A3
+A3®A3×
+A4  A4(öA4QìA4záA4£×A4ÌÍA4õÃA5
+žA5G®A5p€A5A5ÂA5ë
+A6{A6=qA6ffA6\A6žRA6áHA7
+=A733A7\)A7
+A7®A7×
+A8  A8(öA8QìA8záA8£×A8ÌÍA8õÃA9
+žA9G®A9p€A9A9ÂA9ë
+A:{A@  A@(öA@QìA@záA@£×A@ÌÍA@õÃAA
+žAAG®AAp€AAAAÂAAë
+AB{AB=qABffAB\ABžRABáHAC
+=AC33AC\)AC
+AC®AC×
+AD  AD(öADQìADzáAD£×ADÌÍADõÃAE
+žAEG®AEp€AEAEÂAEë
+AF{AF=qAFffAF\AFžRAFáHAG
+=AG33AG\)AG
+AG®AG×
+AH  AH(öAHQìAHzáAH£×AHÌÍAHõÃAI
+žAIG®AIp€AIAIÂAIë
+AJ{AP  AP(öAPQìAPzáAP£×APÌÍAPõÃAQ
+žAQG®AQp€AQAQÂAQë
+AR{AR=qARffAR\ARžRARáHAS
+=AS33AS\)AS
+AS®AS×
+AT  AT(öATQìATzáAT£×ATÌÍATõÃAU
+žAUG®AUp€AUAUÂAUë
+AV{AV=qAVffAV\AVžRAVáHAW
+=AW33AW\)AW
+AW®AW×
+AX  AX(öAXQìAXzáAX£×AXÌÍAXõÃAY
+žAYG®AYp€AYAYÂAYë
+AZ{A`  A`(öA`QìA`záA`£×A`ÌÍA`õÃAa
+žAaG®Aap€AaAaÂAaë
+Ab{Ab=qAbffAb\AbžRAbáHAc
+=Ac33Ac\)Ac
+Ac®Ac×
+Ad  Ad(öAdQìAdzáAd£×AdÌÍAdõÃAe
+žAeG®Aep€AeAeÂAeë
+Af{Af=qAfffAf\AfžRAfáHAg
+=Ag33Ag\)Ag
+Ag®Ag×
+Ah  Ah(öAhQìAhzáAh£×AhÌÍAhõÃAi
+žAiG®Aip€AiAiÂAië
+Aj{Ap  Ap(öApQìApzáAp£×ApÌÍApõÃAq
+žAqG®Aqp€AqAqÂAqë
+Ar{Ar=qArffAr\AržRAráHAs
+=As33As\)As
+As®As×
+At  At(öAtQìAtzáAt£×AtÌÍAtõÃAu
+žAuG®Aup€AuAuÂAuë
+Av{Av=qAvffAv\AvžRAváHAw
+=Aw33Aw\)Aw
+Aw®Aw×
+Ax  Ax(öAxQìAxzáAx£×AxÌÍAxõÃAy
+žAyG®Ayp€AyAyÂAyë
+Az{A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A   A {A (öA =qA QìA ffA záA \A £×A žRA ÌÍA áHA õÃA¡
+=A¡
+žA¡33A¡G®A¡\)A¡p€A¡
+A¡A¡®A¡ÂA¡×
+A¡ë
+A¢  A¢{A¢(öA¢=qA¢QìA¢ffA¢záA¢\A¢£×A¢žRA¢ÌÍA¢áHA¢õÃA£
+=A£
+žA£33A£G®A£\)A£p€A£
+A£A£®A£ÂA£×
+A£ë
+A€  A€{A€(öA€=qA€QìA€ffA€záA€\A€£×A€žRA€ÌÍA€áHA€õÃA¥
+=Aš  Aš{Aš(öAš=qAšQìAšffAšzáAš\Aš£×AšžRAšÌÍAšáHAšõÃA©
+=A©
+žA©33A©G®A©\)A©p€A©
+A©A©®A©ÂA©×
+A©ë
+Aª  Aª{Aª(öAª=qAªQìAªffAªzáAª\Aª£×AªžRAªÌÍAªáHAªõÃA«
+=A«
+žA«33A«G®A«\)A«p€A«
+A«A«®A«ÂA«×
+A«ë
+A¬  A¬{A¬(öA¬=qA¬QìA¬ffA¬záA¬\A¬£×A¬žRA¬ÌÍA¬áHA¬õÃA­
+=A°  A°{A°(öA°=qA°QìA°ffA°záA°\A°£×A°žRA°ÌÍA°áHA°õÃA±
+=A±
+žA±33A±G®A±\)A±p€A±
+A±A±®A±ÂA±×
+A±ë
+A²  A²{A²(öA²=qA²QìA²ffA²záA²\A²£×A²žRA²ÌÍA²áHA²õÃA³
+=A³
+žA³33A³G®A³\)A³p€A³
+A³A³®A³ÂA³×
+A³ë
+AŽ  AŽ{AŽ(öAŽ=qAŽQìAŽffAŽzáAŽ\AŽ£×AŽžRAŽÌÍAŽáHAŽõÃAµ
+=Až  Až{Až(öAž=qAžQìAžffAžzáAž\Až£×AžžRAžÌÍAžáHAžõÃA¹
+=A¹
+žA¹33A¹G®A¹\)A¹p€A¹
+A¹A¹®A¹ÂA¹×
+A¹ë
+Aº  Aº{Aº(öAº=qAºQìAºffAºzáAº\Aº£×AºžRAºÌÍAºáHAºõÃA»
+=A»
+žA»33A»G®A»\)A»p€A»
+A»A»®A»ÂA»×
+A»ë
+AŒ  AŒ{AŒ(öAŒ=qAŒQìAŒffAŒzáAŒ\AŒ£×AŒžRAŒÌÍAŒáHAŒõÃAœ
+=AÀ  AÀ{AÀ(öAÀ=qAÀQìAÀffAÀzáAÀ\AÀ£×AÀžRAÀÌÍAÀáHAÀõÃAÁ
+=AÁ
+žAÁ33AÁG®AÁ\)AÁp€AÁ
+AÁAÁ®AÁÂAÁ×
+AÁë
+AÂ  AÂ{AÂ(öAÂ=qAÂQìAÂffAÂzáAÂ\AÂ£×AÂžRAÂÌÍAÂáHAÂõÃAÃ
+=AÃ
+žAÃ33AÃG®AÃ\)AÃp€AÃ
+AÃAÃ®AÃÂAÃ×
+AÃë
+AÄ  AÄ{AÄ(öAÄ=qAÄQìAÄffAÄzáAÄ\AÄ£×AÄžRAÄÌÍAÄáHAÄõÃAÅ
+=AÈ  AÈ{AÈ(öAÈ=qAÈQìAÈffAÈzáAÈ\AÈ£×AÈžRAÈÌÍAÈáHAÈõÃAÉ
+=AÉ
+žAÉ33AÉG®AÉ\)AÉp€AÉ
+AÉAÉ®AÉÂAÉ×
+AÉë
+AÊ  AÊ{AÊ(öAÊ=qAÊQìAÊffAÊzáAÊ\AÊ£×AÊžRAÊÌÍAÊáHAÊõÃAË
+=AË
+žAË33AËG®AË\)AËp€AË
+AËAË®AËÂAË×
+AËë
+AÌ  AÌ{AÌ(öAÌ=qAÌQìAÌffAÌzáAÌ\AÌ£×AÌžRAÌÌÍAÌáHAÌõÃAÍ
+=AÐ  AÐ{AÐ(öAÐ=qAÐQìAÐffAÐzáAÐ\AÐ£×AÐžRAÐÌÍAÐáHAÐõÃAÑ
+=AÑ
+žAÑ33AÑG®AÑ\)AÑp€AÑ
+AÑAÑ®AÑÂAÑ×
+AÑë
+AÒ  AÒ{AÒ(öAÒ=qAÒQìAÒffAÒzáAÒ\AÒ£×AÒžRAÒÌÍAÒáHAÒõÃAÓ
+=AÓ
+žAÓ33AÓG®AÓ\)AÓp€AÓ
+AÓAÓ®AÓÂAÓ×
+AÓë
+AÔ  AÔ{AÔ(öAÔ=qAÔQìAÔffAÔzáAÔ\AÔ£×AÔžRAÔÌÍAÔáHAÔõÃAÕ
+=AØ  AØ{AØ(öAØ=qAØQìAØffAØzáAØ\AØ£×AØžRAØÌÍAØáHAØõÃAÙ
+=AÙ
+žAÙ33AÙG®AÙ\)AÙp€AÙ
+AÙAÙ®AÙÂAÙ×
+AÙë
+AÚ  AÚ{AÚ(öAÚ=qAÚQìAÚffAÚzáAÚ\AÚ£×AÚžRAÚÌÍAÚáHAÚõÃAÛ
+=AÛ
+žAÛ33AÛG®AÛ\)AÛp€AÛ
+AÛAÛ®AÛÂAÛ×
+AÛë
+AÜ  AÜ{AÜ(öAÜ=qAÜQìAÜffAÜzáAÜ\AÜ£×AÜžRAÜÌÍAÜáHAÜõÃAÝ
+=Aà  Aà{Aà(öAà=qAàQìAàffAàzáAà\Aà£×AàžRAàÌÍAàáHAàõÃAá
+=Aá
+žAá33AáG®Aá\)Aáp€Aá
+AáAá®AáÂAá×
+Aáë
+Aâ  Aâ{Aâ(öAâ=qAâQìAâffAâzáAâ\Aâ£×AâžRAâÌÍAâáHAâõÃAã
+=Aã
+žAã33AãG®Aã\)Aãp€Aã
+AãAã®AãÂAã×
+Aãë
+Aä  Aä{Aä(öAä=qAäQìAäffAäzáAä\Aä£×AäžRAäÌÍAäáHAäõÃAå
+=Aè  Aè{Aè(öAè=qAèQìAèffAèzáAè\Aè£×AèžRAèÌÍAèáHAèõÃAé
+=Aé
+žAé33AéG®Aé\)Aép€Aé
+AéAé®AéÂAé×
+Aéë
+Aê  Aê{Aê(öAê=qAêQìAêffAêzáAê\Aê£×AêžRAêÌÍAêáHAêõÃAë
+=Aë
+žAë33AëG®Aë\)Aëp€Aë
+AëAë®AëÂAë×
+Aëë
+Aì  Aì{Aì(öAì=qAìQìAìffAìzáAì\Aì£×AìžRAìÌÍAìáHAìõÃAí
+=Að  Að{Að(öAð=qAðQìAðffAðzáAð\Að£×AðžRAðÌÍAðáHAðõÃAñ
+=Añ
+žAñ33AñG®Añ\)Añp€Añ
+AñAñ®AñÂAñ×
+Añë
+Aò  Aò{Aò(öAò=qAòQìAòffAòzáAò\Aò£×AòžRAòÌÍAòáHAòõÃAó
+=Aó
+žAó33AóG®Aó\)Aóp€Aó
+AóAó®AóÂAó×
+Aóë
+Aô  Aô{Aô(öAô=qAôQìAôffAôzáAô\Aô£×AôžRAôÌÍAôáHAôõÃAõ
+=Aø  Aø{Aø(öAø=qAøQìAøffAøzáAø\Aø£×AøžRAøÌÍAøáHAøõÃAù
+=Aù
+žAù33AùG®Aù\)Aùp€Aù
+AùAù®AùÂAù×
+Aùë
+Aú  Aú{Aú(öAú=qAúQìAúffAúzáAú\Aú£×AúžRAúÌÍAúáHAúõÃAû
+=Aû
+žAû33AûG®Aû\)Aûp€Aû
+AûAû®AûÂAû×
+Aûë
+Aü  Aü{Aü(öAü=qAüQìAüffAüzáAü\Aü£×AüžRAüÌÍAüáHAüõÃAý
+=B   B 
+=B {B 
+žB (öB 33B =qB G®B QìB \)B ffB p€B záB 
+B \B B £×B ®B žRB ÂB ÌÍB ×
+B áHB ë
+B õÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB	  B	
+=B	{B	
+žB	(öB	33B	=qB	G®B	QìB	\)B	ffB	p€B	záB	
+B	\B	B	£×B	®B	žRB	ÂB	ÌÍB	×
+B	áHB	ë
+B	õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B   B 
+=B {B 
+žB (öB 33B =qB G®B QìB \)B ffB p€B záB 
+B \B B £×B ®B žRB ÂB ÌÍB ×
+B áHB ë
+B õÃB!  B!
+=B!{B!
+žB!(öB!33B!=qB!G®B!QìB!\)B!ffB!p€B!záB!
+B!\B!B!£×B!®B!žRB!ÂB!ÌÍB!×
+B!áHB!ë
+B!õÃB"  B"
+=B"{B"
+žB"(öB"33B"=qB"G®B"QìB"\)B"ffB"p€B"záB"
+B$  B$
+=B${B$
+žB$(öB$33B$=qB$G®B$QìB$\)B$ffB$p€B$záB$
+B$\B$B$£×B$®B$žRB$ÂB$ÌÍB$×
+B$áHB$ë
+B$õÃB%  B%
+=B%{B%
+žB%(öB%33B%=qB%G®B%QìB%\)B%ffB%p€B%záB%
+B%\B%B%£×B%®B%žRB%ÂB%ÌÍB%×
+B%áHB%ë
+B%õÃB&  B&
+=B&{B&
+žB&(öB&33B&=qB&G®B&QìB&\)B&ffB&p€B&záB&
+B(  B(
+=B({B(
+žB((öB(33B(=qB(G®B(QìB(\)B(ffB(p€B(záB(
+B(\B(B(£×B(®B(žRB(ÂB(ÌÍB(×
+B(áHB(ë
+B(õÃB)  B)
+=B){B)
+žB)(öB)33B)=qB)G®B)QìB)\)B)ffB)p€B)záB)
+B)\B)B)£×B)®B)žRB)ÂB)ÌÍB)×
+B)áHB)ë
+B)õÃB*  B*
+=B*{B*
+žB*(öB*33B*=qB*G®B*QìB*\)B*ffB*p€B*záB*
+B,  B,
+=B,{B,
+žB,(öB,33B,=qB,G®B,QìB,\)B,ffB,p€B,záB,
+B,\B,B,£×B,®B,žRB,ÂB,ÌÍB,×
+B,áHB,ë
+B,õÃB-  B-
+=B-{B-
+žB-(öB-33B-=qB-G®B-QìB-\)B-ffB-p€B-záB-
+B-\B-B-£×B-®B-žRB-ÂB-ÌÍB-×
+B-áHB-ë
+B-õÃB.  B.
+=B.{B.
+žB.(öB.33B.=qB.G®B.QìB.\)B.ffB.p€B.záB.
+B0  B0
+=B0{B0
+žB0(öB033B0=qB0G®B0QìB0\)B0ffB0p€B0záB0
+B0\B0B0£×B0®B0žRB0ÂB0ÌÍB0×
+B0áHB0ë
+B0õÃB1  B1
+=B1{B1
+žB1(öB133B1=qB1G®B1QìB1\)B1ffB1p€B1záB1
+B1\B1B1£×B1®B1žRB1ÂB1ÌÍB1×
+B1áHB1ë
+B1õÃB2  B2
+=B2{B2
+žB2(öB233B2=qB2G®B2QìB2\)B2ffB2p€B2záB2
+B4  B4
+=B4{B4
+žB4(öB433B4=qB4G®B4QìB4\)B4ffB4p€B4záB4
+B4\B4B4£×B4®B4žRB4ÂB4ÌÍB4×
+B4áHB4ë
+B4õÃB5  B5
+=B5{B5
+žB5(öB533B5=qB5G®B5QìB5\)B5ffB5p€B5záB5
+B5\B5B5£×B5®B5žRB5ÂB5ÌÍB5×
+B5áHB5ë
+B5õÃB6  B6
+=B6{B6
+žB6(öB633B6=qB6G®B6QìB6\)B6ffB6p€B6záB6
+B8  B8
+=B8{B8
+žB8(öB833B8=qB8G®B8QìB8\)B8ffB8p€B8záB8
+B8\B8B8£×B8®B8žRB8ÂB8ÌÍB8×
+B8áHB8ë
+B8õÃB9  B9
+=B9{B9
+žB9(öB933B9=qB9G®B9QìB9\)B9ffB9p€B9záB9
+B9\B9B9£×B9®B9žRB9ÂB9ÌÍB9×
+B9áHB9ë
+B9õÃB:  B:
+=B:{B:
+žB:(öB:33B:=qB:G®B:QìB:\)B:ffB:p€B:záB:
+B<  B<
+=B<{B<
+žB<(öB<33B<=qB<G®B<QìB<\)B<ffB<p€B<záB<
+B<\B<B<£×B<®B<žRB<ÂB<ÌÍB<×
+B<áHB<ë
+B<õÃB=  B=
+=B={B=
+žB=(öB=33B==qB=G®B=QìB=\)B=ffB=p€B=záB=
+B=\B=B=£×B=®B=žRB=ÂB=ÌÍB=×
+B=áHB=ë
+B=õÃB>  B>
+=B>{B>
+žB>(öB>33B>=qB>G®B>QìB>\)B>ffB>p€B>záB>
+B@  B@
+=B@{B@
+žB@(öB@33B@=qB@G®B@QìB@\)B@ffB@p€B@záB@
+B@\B@B@£×B@®B@žRB@ÂB@ÌÍB@×
+B@áHB@ë
+B@õÃBA  BA
+=BA{BA
+žBA(öBA33BA=qBAG®BAQìBA\)BAffBAp€BAzáBA
+BA\BABA£×BA®BAžRBAÂBAÌÍBA×
+BAáHBAë
+BAõÃBB  BB
+=BB{BB
+žBB(öBB33BB=qBBG®BBQìBB\)BBffBBp€BBzáBB
+BD  BD
+=BD{BD
+žBD(öBD33BD=qBDG®BDQìBD\)BDffBDp€BDzáBD
+BD\BDBD£×BD®BDžRBDÂBDÌÍBD×
+BDáHBDë
+BDõÃBE  BE
+=BE{BE
+žBE(öBE33BE=qBEG®BEQìBE\)BEffBEp€BEzáBE
+BE\BEBE£×BE®BEžRBEÂBEÌÍBE×
+BEáHBEë
+BEõÃBF  BF
+=BF{BF
+žBF(öBF33BF=qBFG®BFQìBF\)BFffBFp€BFzáBF
+BH  BH
+=BH{BH
+žBH(öBH33BH=qBHG®BHQìBH\)BHffBHp€BHzáBH
+BH\BHBH£×BH®BHžRBHÂBHÌÍBH×
+BHáHBHë
+BHõÃBI  BI
+=BI{BI
+žBI(öBI33BI=qBIG®BIQìBI\)BIffBIp€BIzáBI
+BI\BIBI£×BI®BIžRBIÂBIÌÍBI×
+BIáHBIë
+BIõÃBJ  BJ
+=BJ{BJ
+žBJ(öBJ33BJ=qBJG®BJQìBJ\)BJffBJp€BJzáBJ
+BL  BL
+=BL{BL
+žBL(öBL33BL=qBLG®BLQìBL\)BLffBLp€BLzáBL
+BL\BLBL£×BL®BLžRBLÂBLÌÍBL×
+BLáHBLë
+BLõÃBM  BM
+=BM{BM
+žBM(öBM33BM=qBMG®BMQìBM\)BMffBMp€BMzáBM
+BM\BMBM£×BM®BMžRBMÂBMÌÍBM×
+BMáHBMë
+BMõÃBN  BN
+=BN{BN
+žBN(öBN33BN=qBNG®BNQìBN\)BNffBNp€BNzáBN
+BP  BP
+=BP{BP
+žBP(öBP33BP=qBPG®BPQìBP\)BPffBPp€BPzáBP
+BP\BPBP£×BP®BPžRBPÂBPÌÍBP×
+BPáHBPë
+BPõÃBQ  BQ
+=BQ{BQ
+žBQ(öBQ33BQ=qBQG®BQQìBQ\)BQffBQp€BQzáBQ
+BQ\BQBQ£×BQ®BQžRBQÂBQÌÍBQ×
+BQáHBQë
+BQõÃBR  BR
+=BR{BR
+žBR(öBR33BR=qBRG®BRQìBR\)BRffBRp€BRzáBR
+BT  BT
+=BT{BT
+žBT(öBT33BT=qBTG®BTQìBT\)BTffBTp€BTzáBT
+BT\BTBT£×BT®BTžRBTÂBTÌÍBT×
+BTáHBTë
+BTõÃBU  BU
+=BU{BU
+žBU(öBU33BU=qBUG®BUQìBU\)BUffBUp€BUzáBU
+BU\BUBU£×BU®BUžRBUÂBUÌÍBU×
+BUáHBUë
+BUõÃBV  BV
+=BV{BV
+žBV(öBV33BV=qBVG®BVQìBV\)BVffBVp€BVzáBV
+BX  BX
+=BX{BX
+žBX(öBX33BX=qBXG®BXQìBX\)BXffBXp€BXzáBX
+BX\BXBX£×BX®BXžRBXÂBXÌÍBX×
+BXáHBXë
+BXõÃBY  BY
+=BY{BY
+žBY(öBY33BY=qBYG®BYQìBY\)BYffBYp€BYzáBY
+BY\BYBY£×BY®BYžRBYÂBYÌÍBY×
+BYáHBYë
+BYõÃBZ  BZ
+=BZ{BZ
+žBZ(öBZ33BZ=qBZG®BZQìBZ\)BZffBZp€BZzáBZ
+B\  B\
+=B\{B\
+žB\(öB\33B\=qB\G®B\QìB\\)B\ffB\p€B\záB\
+B\\B\B\£×B\®B\žRB\ÂB\ÌÍB\×
+B\áHB\ë
+B\õÃB]  B]
+=B]{B]
+žB](öB]33B]=qB]G®B]QìB]\)B]ffB]p€B]záB]
+B]\B]B]£×B]®B]žRB]ÂB]ÌÍB]×
+B]áHB]ë
+B]õÃB^  B^
+=B^{B^
+žB^(öB^33B^=qB^G®B^QìB^\)B^ffB^p€B^záB^
+B`  B`
+=B`{B`
+žB`(öB`33B`=qB`G®B`QìB`\)B`ffB`p€B`záB`
+B`\B`B`£×B`®B`žRB`ÂB`ÌÍB`×
+B`áHB`ë
+B`õÃBa  Ba
+=Ba{Ba
+žBa(öBa33Ba=qBaG®BaQìBa\)BaffBap€BazáBa
+Ba\BaBa£×Ba®BažRBaÂBaÌÍBa×
+BaáHBaë
+BaõÃBb  Bb
+=Bb{Bb
+žBb(öBb33Bb=qBbG®BbQìBb\)BbffBbp€BbzáBb
+Bd  Bd
+=Bd{Bd
+žBd(öBd33Bd=qBdG®BdQìBd\)BdffBdp€BdzáBd
+Bd\BdBd£×Bd®BdžRBdÂBdÌÍBd×
+BdáHBdë
+BdõÃBe  Be
+=Be{Be
+žBe(öBe33Be=qBeG®BeQìBe\)BeffBep€BezáBe
+Be\BeBe£×Be®BežRBeÂBeÌÍBe×
+BeáHBeë
+BeõÃBf  Bf
+=Bf{Bf
+žBf(öBf33Bf=qBfG®BfQìBf\)BfffBfp€BfzáBf
+Bh  Bh
+=Bh{Bh
+žBh(öBh33Bh=qBhG®BhQìBh\)BhffBhp€BhzáBh
+Bh\BhBh£×Bh®BhžRBhÂBhÌÍBh×
+BháHBhë
+BhõÃBi  Bi
+=Bi{Bi
+žBi(öBi33Bi=qBiG®BiQìBi\)BiffBip€BizáBi
+Bi\BiBi£×Bi®BižRBiÂBiÌÍBi×
+BiáHBië
+BiõÃBj  Bj
+=Bj{Bj
+žBj(öBj33Bj=qBjG®BjQìBj\)BjffBjp€BjzáBj
+Bl  Bl
+=Bl{Bl
+žBl(öBl33Bl=qBlG®BlQìBl\)BlffBlp€BlzáBl
+Bl\BlBl£×Bl®BlžRBlÂBlÌÍBl×
+BláHBlë
+BlõÃBm  Bm
+=Bm{Bm
+žBm(öBm33Bm=qBmG®BmQìBm\)BmffBmp€BmzáBm
+Bm\BmBm£×Bm®BmžRBmÂBmÌÍBm×
+BmáHBmë
+BmõÃBn  Bn
+=Bn{Bn
+žBn(öBn33Bn=qBnG®BnQìBn\)BnffBnp€BnzáBn
+Bp  Bp
+=Bp{Bp
+žBp(öBp33Bp=qBpG®BpQìBp\)BpffBpp€BpzáBp
+Bp\BpBp£×Bp®BpžRBpÂBpÌÍBp×
+BpáHBpë
+BpõÃBq  Bq
+=Bq{Bq
+žBq(öBq33Bq=qBqG®BqQìBq\)BqffBqp€BqzáBq
+Bq\BqBq£×Bq®BqžRBqÂBqÌÍBq×
+BqáHBqë
+BqõÃBr  Br
+=Br{Br
+žBr(öBr33Br=qBrG®BrQìBr\)BrffBrp€BrzáBr
+Bt  Bt
+=Bt{Bt
+žBt(öBt33Bt=qBtG®BtQìBt\)BtffBtp€BtzáBt
+Bt\BtBt£×Bt®BtžRBtÂBtÌÍBt×
+BtáHBtë
+BtõÃBu  Bu
+=Bu{Bu
+žBu(öBu33Bu=qBuG®BuQìBu\)BuffBup€BuzáBu
+Bu\BuBu£×Bu®BužRBuÂBuÌÍBu×
+BuáHBuë
+BuõÃBv  Bv
+=Bv{Bv
+žBv(öBv33Bv=qBvG®BvQìBv\)BvffBvp€BvzáBv
+Bx  Bx
+=Bx{Bx
+žBx(öBx33Bx=qBxG®BxQìBx\)BxffBxp€BxzáBx
+Bx\BxBx£×Bx®BxžRBxÂBxÌÍBx×
+BxáHBxë
+BxõÃBy  By
+=By{By
+žBy(öBy33By=qByG®ByQìBy\)ByffByp€ByzáBy
+By\ByBy£×By®ByžRByÂByÌÍBy×
+ByáHByë
+ByõÃBz  Bz
+=Bz{Bz
+žBz(öBz33Bz=qBzG®BzQìBz\)BzffBzp€BzzáBz
+B|  B|
+=B|{B|
+žB|(öB|33B|=qB|G®B|QìB|\)B|ffB|p€B|záB|
+B|\B|B|£×B|®B|žRB|ÂB|ÌÍB|×
+B|áHB|ë
+B|õÃB}  B}
+=B}{B}
+žB}(öB}33B}=qB}G®B}QìB}\)B}ffB}p€B}záB}
+B}\B}B}£×B}®B}žRB}ÂB}ÌÍB}×
+B}áHB}ë
+B}õÃB~  B~
+=B~{B~
+žB~(öB~33B~=qB~G®B~QìB~\)B~ffB~p€B~záB~
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =
+(Ð9±¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?¹V:?=œ =(a<m<{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @7-@	ñâ?ºHþ?A\r=bn|<êxs¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @Ø@dt\@7Šù@
+Ù?Œg?DûC=-Ë=/)S¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @¶<@{b@±@e\@8@
+Á;?œç°?HÔ=«"U=ig¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ãZx@ÌŒ@¶Uí@ï<@@fCµ@9vR@
+šï?¿·?L8Š=Èâ=Á¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AMÚ@ùýÚ@ã)@Í0x@¶ÉÇ@ c@üf@g+i@:^@
+£?Á?O×x=åm=®xL¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+îxAAl3@úqµ@ä
+@Í€S@·=¢@ Öñ@p@@h
+@;Eº@xX?ÃUê?SvJ>ý=ËnÙ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A5A*@)A
+ÑAÙxAŠ @úå@ä~Þ@Î-@·±|@¡JË@ä@húÒ@<-o@`
+?Å%S?W>~C=èee¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AL/°A@àÃA5­kA*zAFºAaAà	@ûYa@äò°@Îÿ@ž%N@¡Ÿ@Wì@iâv@=@G°?Æô?Z³¬>
+ø>­÷¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AbÐNAWaALN	AA°A5çXA*³ÿA§AMNA	ö@ûÍ;@åf@ÎÿÙ@ž(@¢2w@ËÆ@jÊ*@=üÇ@/e?ÈÄ?^R~>,sÏ>)>¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AypìAn!ÿAbî§AW»NALöAATA6!EA*íìAºA<A	Sã@üA@åÚd@Ïs³@¹
+@¢ŠQ@?¡@k±ß@>ä|@?Ên?añO>:ï>€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÆAaOAyEAn[ìAc(AWõ;ALÁãAAA6[2A+'ÚAôAÁ(A	Ð@üŽï@æN>@Ïç@¹Ý@£,@³z@l@?Ì1@þÎ?ÌbÖ?e!>Ij[>.Ì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AYA±AðA~DAyÉ.AnÕAcb}AX/$ALûÌAAÈtA6A+aÂA .jAûA	Ç¹@ý(Â@æÂ@Ð[`@¹ô®@£þ@'M@m8@@³Õ@æs?Î2?i.²>Wä£><¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A©dAíAh@AÎA4çA:AzAnÏÂAcjAXiAM5¹AB`A6ÏA+°A hWA4ÿA
+Š@ý@ç5ê@ÐÏ:@ºh@€Ø@'@nhì@A@Î'?Ð?lÍ
+>f_ê>KZ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A©ùŽA€R=AžA
+äA
+7AëAQÞAž1Az=Ao	°AcÖWAX¢þAMoŠAB<NA7õA+ÕA ¢DAnëA
+;@þv@ç©Å@ÑC@ºÜc@€u²@@oP¡@B>@µÛ?ÑÐñ?plW>tÛ1>Y¡¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AµJA¯¢AªàA€o4AÕA;ÚA¢.AAnÓAÕ'AzvõAoCAdDAXÜìAM©ABv;A7BâA,A Ü1AšÙA
+u@þP@è
+@Ñ¶î@»P=@€é@Û@p8U@Cjó@?Ó Z?t
+(>«<>h
+è¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÀTAºòÝAµY0A¯¿Aª%×A€*Aò~AXÑA¿#A%wAÊAò
+Az°âAo}AdJ1AYÙAMãAB°'A7|ÏA,IwA!AâÆA
+¯m@þø*@èy@Ò*È@»Ä@¥]f@ö¶@q 	@DR§@
+D?ÕoÃ?w©ú>èà>v/¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AËê¢AÆC+AÀ©~A»ÒAµv%A¯ÜxAªBÌA€©AqAuÅAÜABlAš¿AAzêËAo·sAdAYPÁAN
+iABêA7¶¹A,`A!PA
+¯A
+éV@ÿkü@éK@Ò@Œ7ê@¥Ñ8@j@r®@E:K@lè?×?
+?{H>&>»¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A×:òAÑ{AËùÎAÆ`"AÀÆuA»,ÈAµ
+A¯ùoAª_ÁA€ÆA,hAŒAùA_bAÅ¶A,	A{$žAoñ_AdŸAY¯ANWWAC#þA7ð¥A,œMA!ôAVA
+#D@ÿßÖ@éy&@Ót@Œ«Ä@ŠE@Þb@rïb@F" @T?Ùt?~ç]>c§>¿_¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AâBAÜãËA×J
+AÑ°rAÌÅAÆ}AÀãlA»I¿Aµ°A°eAª|žA€ã
+AI_A¯²AA|YAâ¬AI A{^ŠAp+MAd÷õAYÄANCAC]ëA8*A,÷:A!ÃâAA
+]0A )Ø@éí @ÓO@œ@Šží@R<@s×@G	Ž@<Q?ÚÝÝ?C>¡K>ý¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AíÛAè4AânAÝ ÂA×gAÑÍhAÌ3ŒAÆAÁ aA»fµAµÍA°3\Aª¯A¥ AfVAÌ©A2üAPAÿ£AeöA{Ape:Ae1áAYþANË0ACØA8dA-1'A!ýÎAÊvA
+
+A cÆ@ê`Ú@Óú)@œx@§,Ç@Æ@tŸÌ@Gñh@$?Ü­F?>¥Þî>:Š¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aù+âAókAíêŸAèQAâ·eAÝ
+žA×
+AÑê_AÌP±AÆ·AÁ
+XA»¬AµéÿA°PRAª¶ŠA¥
+ùALAé AOóA¶FA
+AìA{ÒAp'AekÎAZ8vAO
+ACÑÅA8lA-kA"7ŒAdA
+Ñ
+A ²@êÔŽ@Ôn@ŸR@§ ¢@9ñ@uŠ@HÙ
+@
+
+º?Þ|¯?áé>­
+>xJ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B>AþÔ¹Aù;
+Aó¡`Aî³AènAâÔZAÝ:­A× ÿAÒSAÌmŠAÆÓúAÁ:MA»  A¶ôA°mGAªÓA¥9îA AAAlçAÓ:A9AáA|
+hApÙAe¥žAZr_AO?AD
+®A8ØVA-€þA"q¥A>LA
+
+ôA ×@ëH@ÔáÖ@Ÿ{%@št@­Ã@v$@IÀÁ@
+ó_?àK÷?±2>ŽYµ>Šµí¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bæ@BBE®Aþñ¯AùXAóŸVAî$ªAèýAâñOAÝW£A×œöAÒ$JAÌAÆððAÁWDA»œA¶#êA°>AªðA¥VäAœ7A#AÝAð1AVAŒØA|FVAqýAeß€AZ¬LAOxôADEA9CA-ÞêA"«Ax9A
+DáA@ëŒa@ÕU°@Ÿîÿ@šN@!@wuÙ@Jšv@
+Û?â`?>»Y>­ó¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+hB
+º¬BíÖB! BT)AÿŠAùtúAóÛMAîAAè§óAãFAÝtA×ÚíAÒA@AÌ§AÇ
+çAÁt:A»ÚA¶@áA°§4A«
+A¥sÚAÚ-A@AŠÔA
+(As{AÙÎA|CAqLêAfAZæ:AO²áADA9L0A.×A"åA²'A
+~ÏAKv@ì0;@ÕÉ@¿bÙ@šü)@x@x]@K*@
+ÂÇ?ãêÊ?P>ÂÔü>µ15¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B6BbÔB
+þB
+É(BüQB/{Bb¥Aÿ+AùïAóøCAî^AèÄêAã+=AÝA×÷äAÒ^7AÌÄAÇ*ÞAÁ1A»÷A¶]×A°Ä*A«*}A¥ÑA÷$A]xAÃËA*
+ArAöÅA|º0AqØAfSA[ &AOìÎAD¹uA9
+A.RÅA#mAìA
+žŒA
+c@ì€@Ö=e@¿ÖŽ@©p@	R@yEB@Lwß@ª|?åº2?m>Ê¡>ŒnÙ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BÞžB
+üB>&BqPB
+€yB
+×£B
+ÍB=öBq AÿHAù®æAô:Aî{AèáàAãH4AÝ®AØÚAÒ{.AÌáAÇGÔAÁ®'AŒzA¶zÎA°á!A«GtA¥­ÈA AznAàÂAGA­hA»A|ô
+AqÀÄAflA[ZAP&»ADócA9À
+A.²A#YZA&A
+ò©A¿P@íð@Ö±?@ÀJ@©ãÝ@},@z,ö@M_@ 1?ç?îÖ>ÑPD>Ã¬|¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+ßB³#BæMBwBL BÊB
+²ôB
+æ
+BGBLqBAÿeAùËÛAô2.AîAèþÕAãe(AÝË|AØ1ÏAÒ"AÌþuAÇdÈAÁË
+AŒ1oA¶ÂA°þA«diA¥ÊŒA 1AcAý¶Ad	AÊ\A0°A}.Aqú­AfÇUA[ýAP`¥AE-LA9ùôA.ÆA#CA_êA
+,Aù:@íÂ@×%@ÀŸ`@ªW¯@ðþ@{@NG8@!yÕ?éXå?Ÿ>Øh>Êê ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B$/B![KB
+uBÁBôÈB'òB[
+BEB
+ÁoB
+ôB'ÂBZìBAÿAùèÒAôO%AîµxAéÌAãAÝèrAØNÅAÒµAÍlAÇ¿AÁèAŒNfA¶Ž¹A±
+A«`A¥ç³A NAŽYA¬A AçSAMŠA}góAr4AgCA[ÍêAPAEg9A:3áA/ A#Í0AØA
+fA3&@íÿ@×ë@Á2:@ªË@dÙ@{üO@O.ì@"a?ë(M?>ßË
+>Ò'Ä¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B)×/B'sB$6B!iÇB
+ðBÐBDB6mBiBÁB
+ÏêB
+B6>BigBAÿtAúÈAôl
+AîÒoAé8ÂAãAÞhAØkŒAÒÒAÍ8bAÇ¶AÂ	AŒk\A¶Ñ°A±8A«VAŠ©A jüAÑPA7£AöAJAjA}¡áArnAg;0A\×APÔAE¡&A:mÎA/:vA$
+AÓÄA
+ lAm@îsv@Ø
+Å@ÁŠ@«?c@Ø³@|ä@P @#I>?ì÷·?\ñ>ç¯>Ùeh¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B/WB,«B)ÞÅB'ïB$EB!xBB
+«lBÞB¿BDéBxB«<B
+ÞfB
+BD¹BwâB«
+AÿŒkAú"¿AôAîïeAéUžAãŒ
+AÞ"_AØ²AÒïAÍUYAÇ»¬AÂ" AŒSA¶îŠA±TùA«»LAŠ! A óAîFATAºíA!@AA}ÛÎAršuAgu
+A\AÄAQlAEÛA:§»A/tbA$A
+A
+²A
+ÚYA§@îçQ@Ø@Âï@«³>@L@}Ëž@PþV@$0ó?îÇ ?,Z>îFT>à£
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B5'B2SÃB/íB,ºB)í@B' jB$SB!œB
+¹çBíB :BSdBB¹·B
+ìáB
+ 
+BS4B^B¹AÿÙbAú?µAôŠAï
+\Aér¯AãÙAÞ?VAØ¥©AÓ
+üAÍrPAÇØ£AÂ>öAŒ¥IA·
+A±qðA«ØCAŠ>A €êA
+=AqA×äA>7A€A~»ArâbAg¯
+A\{²AQHYAF A:ášA/®PA$z÷AGAFAàî@ï[+@Øôz@ÂÉ@¬'@Àg@~³l@Qæ
+@%§?ð?ûÄ>õù>çà°¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B:ÏŠB7ûêB5/B2b>B/gB,ÈB)û»B'.äB$bB!8B
+ÈaBûB.µBaÞBBÈ1B
+û[B
+.
+Ba®BØBÈAÿöVAú\ªAôÂýAï)PAé€Aãõ÷AÞ\JAØÂAÓ(ñAÍDAÇõAÂ[êAŒÂ>A·(A±äA«õ8AŠ[A ÁÞA(2A
+AôØA[,AÁ~A~O£As
+LAgèóA\µAQBAFNêA;A/è9A$ŽàAAN/A×@ïÎý@ÙhL@Ã@¬ê@49@@RÍ¯@& L?òeÒ?Ë
+>üÁ
+>ï
+U¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B@wÎB=€B:×<B8
+fB5=B2p¹B/£ãB,×
+B*
+6B'=`B$pB!£³B
+ÖÝB
+
+B=0BpYB£BÖ­B	ÖB
+= Bp*B£SBÖ}B 	§Aúy AôßôAïFGAé¬AäîAÞyAAØßAÓEçAÍ¬:AÈAÂxáAŒß4A·EA±«ÛA¬.AŠxA ÞÕAE(A«|AÎAx"AÞuA~AsV8Ah"àA\ïAQŒ/AF×A;U~A0"&A$îÍA»uA
+ATÄ@ðB×@ÙÜ&@Ãuu@­Ä@š@Ac@Sµc@&è ?ô5;?u?ÿ`>ö[ø¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BFöBCL:B@dB=²B:å·B8áB5L
+B24B/²^B,åB*±B'KÛB$B!².B
+åXB
+BK«B~ÕB±þBå(BRB
+K{B~¥B±ÏBäøB !AúAôüêAïc>AéÉAä/äAÞ7AØüAÓbÞAÍÉ1AÈ/AÂØAŒü+A·b~A±ÈÒA¬/%AŠxA ûÌAb
+AÈrA.ÅAAûlA~Ã~As&Ah\ÍA])uAQö
+AFÂÄA;kA0\A%(»AõbAÂ	A±@ð¶±@ÚP @ÃéO@­@î@µ=@T@'Ïµ?ö€?iÞ?2>ý¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BKÈ
+BHôbBF'BCZ¶B@ßB=Á	B:ô3B8'\B5ZB2°B/ÀÙB,ôB*'-B'ZVB$B!À©B
+óÓB
+&ýBZ&BPBÀzBó£B&ÍB
+Y÷B BÀIBósB &Aú³AõáAï4AéæAäLÚAÞ³.AÙAÓÔAÍæ(AÈL{AÂ²ÎAœ"A·uA±åÈA¬L
+AŠ²nA¡ÂAAåhAKŒA²A
+bA~ýkAsÊAhºA]cbAR0	AFü±A;ÉYA0 A%b§A/OAû÷AÈ@ñ*@ÚÃÛ@Ä]*@­öy@È@)@UÌ@(·i?÷Ô
+?9G?	=?k ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BQpFBNBKÏŽBIÞBF6BCi1B@[B=ÏB;®B85ØB5iB2+B/ÏUB-~B*5šB'hÑB$ûB!Ï%BNB
+5xBh¢BËBÎõBB5HB
+hqBBÎÅBîB 5AúÐAõ6×Aï*Aê~AäiÑAÞÐ$AÙ6xAÓËAÎ
+AÈirAÂÏÅAœ6A·lA²ŸA¬iAŠÏeA¡5žA
+A_Ah²AÏA
+5YA7XAt AhÐ§A]OARi÷AG6A<EA0ÏíA%Ai<A5äA@ñf@Û7µ@ÄÑ@®jS@¢@ò@Vl@)
+?ù£v? °?
+ÛÖ?
+r¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BWmBTD±BQwÛBN«BKÞ.BIXBFDBCw«B@ªÕB=ÝÿB;(B8DRB5w|B2ª¥B/ÝÏB-øB*D"B'wLB$ªuB!ÝBÉB
+CòBw
+BªFBÝoBBCÂB
+vìBªBÝ?BiB CAúíxAõSÌAïºAê rAäÆAÞíAÙSlAÓ¹ÀAÎ AÈfAÂìºAœS
+A·¹`A²³A¬AŠìZA¡R­A¹ ATA
+§AëúA
+RNAqAAt=éAi
+A]×8AR£ßAGpA<=/A1	ÖA%Ö~A£%AoÍA<t@ò8@Û«@ÅDÖ@®Þ%@wt@Ä@WT%@*Â?ûrŸ?¡×ù?zg?	©D¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B\ÀBYìÙBW BTS-BQVBN¹BKìªBIÓBFRýBC'B@¹PB=ìzB;€B8RÍB5
+÷B2¹ B/ìJB-tB*RB'
+ÇB$žñB!ìBDB
+RnB
+BžÀBëêBBR=B
+
+gBžBë»B
+åB RAû
+oAõpÂAï×Aê=iAä£ŒAß
+AÙpcAÓÖ¶AÎ=
+AÈ£\AÃ	°AœpA·ÖVA²<ªA¬¢ýA§	PA¡o€AÕ÷A<JA¢AñA
+oDA«.AtwÖAiD}A^%ARÝÍAGªtA<w
+A1CÃA&kAÝA©ºAvb@ò@Üa@Åž°@¯R @ëO@@X;Ù@+nv?ýB'?£§b?:?
+H¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BbhœB_B\È+BYûUBW.~BTašBQÒBNÇûBKû%BI.OBFaxBC¢B@ÇÌB=úõB;.B8aHB5rB2ÇB/úÅB--ïB*aB'BB$ÇlB!úB-¿B
+`èBBÇ<BúeB-B`¹B
+ãBÇ
+Bú6B-`B `Aû'fAõ¹Aïô
+AêZ`AäÀ³Aß'AÙZAÓó¬AÎZ AÈÀSAÃ&ŠAœúA·óMA²Y A¬¿ôA§&GA¡AòîAYAA¿A%çA
+:AåAt±ÃAi~kA^KASºAGäaA<±	A1}°A&JXA Aã§A°N@òùì@Ü<@Æ,@¯ÅÚ@_)@øx@Y#@,V+?ÿ?¥vË?ž
+?æè¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BhåBe=)BbpSB_£}B\ÖŠBZ	ÐBW<úBTp#BQ£MBNÖwBL	 BI<ÊBFoôBC£
+B@ÖGB>	pB;<B8oÄB5¢íB2ÖB0	AB-<jB*oB'¢ŸB$ÕçB"	B<:B
+odB¢BÕ·BáB<
+Bo5B
+¢^BÕB²B;ÛB oAûD\Aõª°AðAêwVAäÝªAßCüAÙªPAÔ£AÎvöAÈÝJAÃCAœ©ðAžDA²vA¬ÜêA§C>A¡©AäAv7AÜABÞA
+©1AAtë°AižXA^ÿASQ§AH
+NA<êöA1·A&EAPìA
+Aê<@ómÇ@Ý@Æ e@°9Ž@Ó@lR@Z
+B@-=ß@ p|?§F3?VÝ?
+¹¿  ¿  ¿  ¿  ¿  ¿  Bm¹
+BjåQBh{BeK¥Bb~ÎB_±øB\å"BZKBWKuBT~BQ±ÈBNäòBL
+BIKEBF~oBC±B@äÂB>ìB;KB8~?B5±iB2äB0ŒB-JæB*~B'±8B$äbB"BJµB
+}ßB±	Bä3B]BJB}°B
+°ÚBäB,BJVB }AûaSAõÇŠAð-úAêLAäú Aß`óAÙÇFAÔ-AÎíAÈú@AÃ`AœÆçAž-:A²A¬ùáA§`4A¡ÆA,ÚA.AùA_ÔA
+Æ(A,{Au%AiòEA^ŸìASAHX<A=$ãA1ñA&Ÿ2AÚAWA$)@óá¡@Ýzð@Ç?@°­@FÞ@à-@Zòö@.%@X1?©?
+õ¯?$¿  ¿  ¿  ¿  Bsa4BpxBmÀ¢BjóÌBh&õBeZBbIB_ÀrB\óBZ&ÆBWYïBTBQÀCBNólBL&BIY¿BFéBCÀB@ó<B>&fB;YB8¹B5¿ãB2ó
+B0&6B-Y_B*B'¿³B$òÜB"&BY0B
+ZB¿Bò­B%×BYB*B
+¿SBò}B%§BXÐB úAû~HAõäAðJîAê±AAåAß}èAÙä;AÔJAÎ°âAÉ5AÃ}AœãÜAžJ/A²°A­ÕA§}(A¡ã|AIÏA°"AvA|ÉA
+ã
+AIpAu_Aj,.A^øÖASÅ}AH$A=^ÌA2+tA&øAÄÃAkA^@ôUs@ÝîÂ@Ç@±!a@º°@Sÿ@[Ú@/
+8@?Õ?ªäæ?"B?Ã^¿  ¿  By	\Bv5 BshÊBpôBmÏ
+BkGBh5qBehBbÄB_ÎîB]BZ5ABWhkBTBQÎŸBOçBL5BIh;BFdBCÎBAžB>4áB;h
+B85B5Î^B3B04±B-gÛB*B'Î.B%XB"4Bg¬B
+ÕBÍÿB)B4RBg{B¥B
+ÍÏB	 øB4"BgLB uAû>AöAðgäAêÎ8Aå4AßÞAÚ2AÔg
+AÎÍØAÉ4,AÃAŸ ÒAžg%A²ÍxA­3ÌA§A¢ rAfÆAÍA3lAÀA AffAutAjfA_2ÂASÿjAHÌA=¹A2eaA'2	Aþ°AËWAÿ@ôÉN@Þb@Çûì@±;@.@ÇÙ@\ÂO@/ôí@'?¬ŽO?&3?b/B|  B{ÝÈByòBvD
+BswEBpªoBmÝBkÂBhCìBewBbª?B_ÝiB]BZCŒBWvæBTªBQÝ9BOcBLCBIv¶BF©àBCÝ	BA3B>C]B;vB8©¯B5ÜÙB3B0C,B-vVB*©B'ÜªB%ÔB"BýBv'B
+©QBÜzB£BBÍBu÷B© B
+ÜJB	tBBBuÇB šñAûž4Aö
+AðÛAêë.AåQAß·ÕAÚ
+(AÔ|AÎêÏAÉQ"AÃ·uAŸ
+ÈAž
+A²êoA­PÂA§·A¢
+iAŒAêAPcA¶¶A
+	A\AuÓ`Aj A_l°AT9WAIÿA=Ò§A2NA'kõA
+8AEAÑí@õ=(@ÞÖw@ÈoÆ@²	@¢e@
+;³@]ª@0Ü¢@??®ž?)Ñå¿  B|B{ìDBymBvRBs
+ÁBpžêBmìBk>BhRgBe
+Bbž»B_ëäB]BZR7BW
+aBTžBQëŽBO
+ÞBLRBI
+1BFž[BCë
+BA
+®B>Q×B;
+B8ž+B5ëTB3
+~B0QšB-ÒB*·üB'ë%B%
+OB"QyB¢B
+·ËBêõB
+BQHBrB·B
+êÅB	
+ïBQBBB ·lAûÕ+Aö;~Að¡ÒAë%AånxAßÔÌAÚ;AÔ¡rAÏÅAÉnAÃÔlAŸ:¿Až¡A³fA­m¹A§Ô
+A¢:`A ³AAmYAÓ¬A: A SAv
+NAjÙõA_ŠATsEAI?ìA>
+A2Ù;A'¥ãA
+rA?2A
+Ù@õ±@ßJQ@Èã¡@²|ð@>@
+¯@^¹@1ÄV@öó?¯vw¿  ¿  B|B{ú¿By-éBvaBs<BpÇfBmúBk-¹Bh`ãBe
+BbÇ6B_ú_B]-BZ`³BWÜBTÇBQú0BO-YBL`BI­BFÆÖBCùÿBA-)B>`SB;|B8ÆŠB5ùÐB3,úB0`$B-MB*ÆwB'ù¡B%,ÊB"_óB
+B
+ÆGBùpB,B_ÄBíBÆB
+ùAB	,jB_BŸB ÅçAûò"AöXuAðŸÈAë%
+AåoAßñÂAÚXAÔŸhAÏ$ŒAÉAÃñbAŸW¶AžŸ	A³$\A­°A§ñA¢WVAœ©A#üAPAð£AVöAœJAvG;AkãA_àAT­1AIyÙA>FA3)A'ßÐA
+¬wAyAEÇ@ö$Ý@ßŸ,@ÉWz@²ðÊ@@#h@_yn@2=¶¿  ¿  ¿  ¿  B|$B|	:By<dBvoBs¢·BpÕáBn	
+Bk<4Bho^Be¢BbÕ±B`ÛB]<BZo.BW¢XBTÕBR«BO;ÕBLnþBI¢'BFÕQBD{BA;€B>nÎB;¡øB8Õ"B6LB3;uB0nB-¡ÉB*ÔòB(B%;EB"noB¡B
+ÔÂBìB;Bn?B¡iBÔB
+ŒB	:æBnB¡9B ÔbAüAöulAðÛ¿AëBAåšeAàžAÚu
+AÔÛ_AÏA²AÉšAÄYAŸt¬AžÛ A³ASA­§ŠAš
+ùA¢tLAÚ A@óA§FA
+AsíAÚ@Av(AkMÏA`wATçAI³ÇA>nA3MA(œA
+æeA³
+AŽ@ö·@à2@ÉËU@³d€@ýó@`¿  ¿  ¿  ¿  ¿  ¿  B|3B|¶ByJßBv~	Bs±3Bpä\BnBkJ¯Bh}ÙBe±Bbä,B`VB]JBZ}©BW°ÓBTãýBR&BOJOBL}yBI°£BFãÌBDöBAJ B>}JB;°tB8ãB6ÇB3IñB0}B-°CB*ãmB(B%IÀB"|êB°B
+ã=BgBIB|ºB¯äBãB
+7B	IaB|B¯ŽB âÞAü,AöbAðøµAë_AåÅ\Aà+¯AÚAÔøVAÏ^©AÉÄüAÄ+PAŸ£Až÷öA³^IA­ÄAš*ðA¢CA÷A]êAÄ=A*AäA÷7Av»AkœA`TeAU!
+AIí³A>º[A3A(SªA
+ RAìúA¹¡@÷
+@à¥à@Ê?/@³¡T¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|AB|&/ByYYBvBs¿¬BpòÕBn%ÿBkY)BhRBe¿|BbòŠB`%ÏB]XùBZ#BW¿LBTòuBR%BOXÉBLòBI¿
+BFòFBD%pBAXB>ÃB;ŸíB8òB6%@B3XiB0B-ŸœB*ñæB(%B%X:B"cBŸB
+ñ·B$àBX
+B4BŸ]BñB
+$°B	WÚBBŸ-B ñWAüIAö¯TAñšAë{ûAåâNAàH¢AÚ®õAÕHAÏ{AÉáïAÄHBAŸ®A¹èA³{<A­áAšGâA¢®6AAzÜAá0AGA­×A)AvôûAkÁ¢A`IAUZñAJ'A>ô@A3ÀèA(A
+Z7A&ÞAó@÷[@àâ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|OÿB|4ªBygÔBvýBsÎ'BqQBn4zBkg€BhÎBeÍ÷Bc!B`4KB]gtBZBWÍÇBU ñBR4BOgDBLnBIÍBG ÂBD3ëBAgB>?B;ÍhB9 B63»B3fåB0B-Í8B+ bB(3B%fµB"ßBÍB
+ 2B3\Bf
+B¯BÌØB B
+3,B	fUBBÌšB ÿÒAüeøAöÌKAñ2AëòAåÿEAàeAÚËìAÕ2?AÏAÉþåAÄe8AŸËA¹1ßA³2A­þAšdÙA¢Ë,A1AÓAþ'AdyAÊÌA1 Aw.çAkûA`È7AUÞAJaA?..A3úÕA(Ç|A
+$A`ÌAÞ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|^zB|C%ByvOBv©yBsÜ¢BqÌBnBöBkvBh©IBeÜsBcB`BÅB]uïBZ©BWÜBBUlBRBBOuÀBLšêBIÜBG=BDBgBAuB>š¹B;ÛãB9
+B6B6B3u`B0šB-Û³B+ÝB(BB%u0B"šZBÛB
+­BA×Bu Bš*BÛTB}B
+A§B	tÐB§úBÛ$BMAüîAöéBAñOAëµèAæ
+<AàAÚèâAÕO5AÏµAÊÜAÄ/AŸèA¹NÖA³µ)A®|AšÐA¢è#ANwAŽÉA
+ApAçÃANAwhÕAl5|Aa$AUÎÌAJsA?hA44ÂA)jA
+²|¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|lõB|Q¡ByÊBv·ôBsë
+Bq
+GBnQqBkBh·ÄBeêíBc
+B`QAB]jBZ·BWêŸBU
+èBRQBO;BL·eBIêBG
+žBDPáBA
+B>·5B;ê^B9
+B6P²B3ÛB0·B-ê/B+
+XB(PB%¬B"¶ÕBéÿB
+
+(BPRB|B¶¥BéÏB
+øB
+P"B	LB¶uBéB
+ÉAüåA÷8AñlAëÒßAæ92Aà
+AÛØAÕl,AÏÒAÊ8ÒAÄ&A¿yA¹kÌA³Ò A®8sAšÇA£AklAÑÀA8AfAºAk
+Aw¢ÂAlojAa<AVžAJÕ`A?¢A4S¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|{pB|`
+ByFBvÆoBsùBq,ÃBn_ìBkBhÆ?BeùiBc,B`_ŒB]æBZÆBWù:BU,cBR_BO·BLÅàBIù	BG,3BD_]BAB>Å°B;øÚB9,B6_-B3WB0ÅB-øªB++ÔB(^ýB%'B"ÅPBøzB
++€B^ÍB÷BÅ BøJB+tB
+^B	ÇBÄñBøB+DAüŒÜA÷#/AñAëïÕAæV(AàŒ|AÛ"ÏAÕ"AÏïvAÊUÉAÄŒ
+A¿"pA¹ÃA³ïA®UiAš»ŒA£"AcAî¶AU
+A»]A!°AAwÜ¯Al©VAauþAVBŠAJóž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ìB|nBy¡ÁBvÔëBtBq;=BnngBk¡BhÔºBfäBc;B`n8B]¡bBZÔBXµBU:ßBRnBO¡1BLÔ[BJ
+BG:®BDmØBA¡B>Ô+B<UB9:B6mšB3 ÒB0ÓüB.%B+:OB(mxB% ¢B"ÓÌB õB
+:BmHB rBÓBÅB9ïB
+mB	 BBÓlBB9¿AüÙÒA÷@%AñŠxAì
+ÌAæsAàÙrAÛ?ÆAÕŠAÐ
+lAÊrÀAÄÙA¿?gA¹¥¹AŽ
+
+A®r`AšØ³A£?A¥ZA
+­Ar AØTA>§A€úAxAlãDAaV¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|gB|}By°<BvãeBtBqI¹Bn|âBk°
+Bhã6Bf`BcIB`|³B]¯ÝBZãBX0BUIYBR|BO¯­BLâÖBJ BGI*BD|SBA¯}B>â§B<ÐB9HúB6|$B3¯MB0âwB. B+HÊB({ôB%¯
+B"âGB pB
+HB{ÄB®íBâBABHjB
+{B	®ŸBáçBBH;AüöÈA÷]
+AñÃoAì)ÂAæAàöiAÛ\ŒAÕÃAÐ)cAÊ·AÄö	A¿\\A¹Â°AŽ)A®VAšõªA£[ýAÂPA(€A÷AõKA[AÁñAx4ô¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ŠâB|ByŸ·BvñáBt%
+BqX4Bn^BkŸBhñ²Bf$ÛBcXB`/B]ŸXBZñBX$«BUWÕBRþBOŸ(BLñRBJ${BGW¥BDÏBAœøB>ñ"B<$LB9WuB6B3œÈB0ðòB.$
+B+WEB(oB%œB"ðÂB #ìB
+WB?BœiBðB#ŒBVæB
+B	œ9BðcB#BV¶Aý¿A÷zAñàfAìF¹Aæ­
+Aá`AÛy³AÕàAÐFYAÊ¬¬AÅ A¿ySA¹ßŠAŽEúA®¬MA© A£xôAßGAEA«îA@AjÉ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|µ]B|	ByÍ2Bw \Bt3Bqf°BnÚBkÍBi -Bf3WBcfB`©B]ÌÓBZÿýBX3&BUfPBRzBOÌ£BLÿÍBJ2÷BGf BDJBAÌtB>ÿB<2ÇB9eðB6B3ÌDB0ÿmB.2B+eÀB(êB%ÌB"ÿ=B 2gB
+eBºBËäBÿB27BeaB
+B	ËŽBþÞB2Be1Aý0¶A÷	Añý\Aìc°AæÊAá0WAÛ©AÕüüAÐcPAÊÉ£AÅ/öA¿JA¹üAŽbðA®ÉDA©/A£ëAü>AbA»¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÃØB|šByÛ®BwØBtBBqu+BnšUBkÛBišBfAÑBctûB`š%B]ÛNB[xBXA¢BUtËBR§õBOÛBMHBJArBGtBD§ÅBAÚïB?B<ABB9tlB6§B3Ú¿B1
+èB.AB+t<B(§eB%ÚB#
+¹B @âB
+t
+B§6BÚ_B
+B@³BsÜB
+§B	Ú0B
+YB@Bs¬AýM¬A÷Ž AòSAì§AææùAáMLAÛ³ AÖóAÐFAÊæAÅLíA¿³@AºAŽçA®æ;A©LA£²àA
+i¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÒTB|¶þByê(Bw
+QBtP{Bq¥Bn¶ÎBké÷Bi
+!BfPKBctB`¶B]éÈB[
+ñBXPBUEBR¶nBOéBM
+ÂBJOëBGBD¶>BAéhB?
+B<O»B9åB6¶B3é8B1
+bB.OB+µB(µßB%éB#
+2B O\B
+
+Bµ¯BèÙB
+BO,BVB
+µB	è©BÒBNüB&AýjA÷ÐóAò7EAìAçìAáj?AÛÐAÖ6æAÐ9AËAÅiàA¿Ð3Aº6AŽÚA¯,A©[µ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|àÐB|ÅyByø£Bw+ÍBt^öBqBnÅIBkøsBi+Bf^ÆBcðB`ÅB]øCB[+mBX^BUÀBRÄêBOøBM+=BJ^fBGBDÄºBA÷ãB?+
+B<^6B9`B6ÄB3÷³B1*ÝB.^B+0B(ÄZB%÷B#*­B ]×B
+BÄ*B÷TB*~B]§BÑB
+ÃúB	÷$B*NB]wB¡AýA÷íèAòT<AìºAç âAá6AÛíAÖSÜAÐº0AË AÅ×A¿í*AºS|AŽ¬¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ïKB|ÓõBz
+Bw:GBtmqBq BnÓÄBlîBi:BfmABc kB`ÓB^ŸB[9èBXmBU ;BRÓeBPBM9žBJlâBG 
+BDÓ5BB^B?9B<l²B9ÛB6ÓB4/B19XB.lB+¬B(ÒÕB&ÿB#9)B lRB
+|BÒŠBÏB8ùBl"BLB
+ÒvB
+B8ÉBkóB
+Aý€Aø
+ßAòq2Aì×Aç=ÙAá€,AÜ
+AÖpÓAÐ×'AË=zAÅ£ÌA¿üU¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ýÆB|âoBzBwHÃBt{ìBq¯Bnâ@BliBiHBf{œBc®æB`âB^:B[HcBX{BU®¶BRáàBP
+BMH3BJ{]BG®BDá°BBÚB?HB<{-B9®WB6áB4ªB1GÔB.zýB+®'B(áQB&zB#G€B zÎB
+­÷Bá!BJBGtBzB­ÇB
+àñB
+BGDBznB­AýÁAø'ÖAò)Aìô|AçZÐAáÁ#AÜ'wAÖÊAÐô
+AËL¥¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+AB|ðëBz$BwW>BthBqœBnð»Bl#åBiWBf8BcœbB`ðB^#µB[VÞBXBUœ2BRð[BP#
+BMV®BJØBGœBDð+BB#UB?VB<šB9ŒÒB6ïüB4#%B1VOB.yB+Œ¢B(ïÌB&"öB#VB IB
+ŒrBïB"ÆBUïBBŒCB
+ïlB
+"BUÀBéBŒAýÞyAøDÌAò« AísAçwÇAáÞAÜDlAÖõ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ŒB|ÿfBz2Bwe¹BtãBqÌ
+Bnÿ6Bl2`BieBf³BcËÝB`ÿB^20B[eZBXBUË­BRþÖBP2 BMe*BJSBGË}BDþ§BB1ÐB?dúB<$B9ËMB6þwB41¡B1dÊB.ôB+Ë
+B(þGB&1qB#dB ÄB
+ÊîBþB1ABdkBBÊŸB
+ýèB
+1Bd;BeBÊAýûpAøaÃAòÈAí.jAçŒAáíE¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B})8B}
+áBzA
+Bwt5Bt§^BqÚBo
+²Bl@ÛBitBf§.BcÚXBa
+B^@«B[sÕBXŠþBUÚ(BS
+RBP@{BMs¥BJŠÏBGÙøBE
+"BB@LB?suB<ŠB9ÙÉB7
+òB4@
+B1sFB.ŠoB+ÙB)
+ÂB&?ìB#sB Š?B
+ÙiB
+B?ŒBræBŠBÙ9B
+
+cB
+?Br¶B¥ßBÙ
+AþgAø~ºAòå
+Aí=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}7³B}
+]BzOBw°BtµÚBqéBo
+-BlOVBiBfµªBcèÓBaýB^O&B[PBXµzBUè£BSÍBPN÷BM BJµJBGètBEBBNÇB?ñB<µB9èDB7nB4NB1ÁB.ŽêB+èB)>B&NgB#B Ž»B
+çäBBN8BaBŽBçµB
+ÞB
+NB2BŽ[Bç
+Aþ5\Aøå¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}F.B}*ØBz^Bw+BtÄUBq÷~Bo*šBl]ÒBiûBfÄ%Bc÷NBa*xB^]¢B[ËBXÃõBU÷BS*HBP]rBMBJÃÅBGöïBE*BB]BB?lB<ÃB9ö¿B7)éB4]B1<B.ÃfB+öB))¹B&\ãB#
+B Ã6B
+ö`B)B\³BÝBÃBö/B
+)ZB
+\B­BÂ×Bï¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}TªB}9SBzl}BwŠBtÒÐBrúBo9#BllMBivBfÒ BdÊBa8óB^l
+B[GBXÒpBVBS8ÄBPkíBMBJÒABHjBE8BBkŸB?çB<ÒB::B78dB4kB1·B.ÑáB,
+B)84B&k^B#B Ñ±B
+ÛB8Bk.BWBÑB«B
+7ÕB
+jÿBC¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}c%B}GÌBzzöBw® BtáIBrsBoGBlzÆBi­ðBfáBdCBaGmB^zB[­ÀBXàêBVBSG=BPzgBM­BJàºBHäBEG
+BBz7B?­`B<àB:ŽB7FÝB4zB1­1B.àZB,B)F®B&y×B#­B à+B
+TBF}ByšB¬ÑBßûB%B
+?i¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}q B}VHBzqBwŒBtïÄBr"îBoVBlABiŒkBfïBd"ŸBaUèB^B[Œ;BXïeBV"BSUžBPâBMŒ
+BJï5BH"_BEUBB²B?»ÜB<ïB:"/B7UYB4B1»¬B.îÖB,!ÿB)U)B&SB#»|B î¥B
+!ÐBTùB#B»MBç¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}B}dÃBzìBwËBtþ@Br1iBodBlœBiÊæBfþBd1:BadcB^B[Ê·BXýàBV1
+BSd4BP]BMÊBJý°BH0ÚBEdBB-B?ÊWB<ýB:0ªB7cÔB4þB1Ê'B.ýQB,0{B)c€B&ÍB#ÉøB ý!B
+0KBcuB¹¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}B}s>BzŠhBwÙBu
+»Br?åBosBlŠ8BiÙbBg
+Bd?µBarßB^ŠB[Ù2BY
+\BV?
+BSr¯BP¥ØBMÙBK
+,BH?UBErBB¥©B?ØÒB=
+üB:?&B7rOB4¥yB1Ø£B/
+ÌB,>õB)r B&¥IB#ØsB!
+B
+7á¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}B}¹BzŽãBwè
+Bu6BrN`BoBlŽ³BiçÝBgBdN0BaZB^ŽB[ç­BY×BVN BS*BPŽTBMç}BK§BHMÑBEúBBŽ$B?çNB=wB:M¡B7ËB4³ôB1ç
+B/HB,MqB)B&³ÅB#à	¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}«B}5BzÃ^BwöBu)²Br\ÛBoBlÃ/BiöXBg)Bd\¬BaÕB^ÂÿB[ö(BY)RBV\|BS¥BPÂÏBMõùBK)"BH\LBEvBBÂB?õÉB=(óB:\
+B7EB4ÂpB1õB/(ÃB,[íB)1¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ºB}°BzÑÚBxBu8-BrkWBoBlÑªBjÔBg7ýBdk'BaPB^ÑzB\€BY7ÍBVj÷BS!BPÑJBNtBK7BHjÇBEñBBÑB@DB=7mB:jB7ÁB4ÐëB2B/0Y¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÈB}­+BzàUBxBuFšBryÒBo¬üBlà%BjOBgFxBdy¢Ba¬ÌB^ßõB\BYFIBVyrBS¬BPßÆBNïBKFBHyCBE¬lBBßB@ÀB=EéB:yB7¬=B4Ø¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÖÿB}»§BzîÐBx!úBuU$BrMBo»wBlî Bj!ÊBgTôBd
+Ba»GB^îqB\!BYTÄBVîBS»BPîABN!kBKTBHœBEºèBBîB@!;B=TeB:©¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}åzB}Ê"BzýLBx0uBucBrÈBoÉòBlý
+Bj0EBgcoBdBaÉÂB^üìB\0BYc?BViBSÉBPüŒBN/åBKcBH9BEÉcBBüB@(Ñ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}óöB}ØB{
+ÅBx>îBurBr¥BBoØkBm
+Bj>¿BgqèBd¥BaØ<B_
+eB\>BYq¹BV€âBSØ
+BQ
+6BN>_BKqBH€³BEÐ÷¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~qB}çB{@BxMjBuBr³œBoæçBmBjM:BgdBd³Baæ·B_áB\M
+BY3BV³^BSæBQ±BNLÛBKy¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~ìB}õB{(»Bx[åBuBrÂ8BoõbBm(Bj[µBgßBdÂ	Baõ2B_([B\[BY¯BVÁÙBSõBQ!G¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~gB~
+B{77Bxj`BuBrÐŽBpÝBm7Bjj1BgZBdÐBb®B_6×B\jBY+BVÉo¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~-ãB~B{E²BxxÜBu¬Brß/BpYBmEBjx«Bg«ÖBdÞÿBb)B_ESB\q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~<^B~!B{T-BxWBuºBríªBp ÓBmSþBj'BgºQBdí{Bb¿¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~JÙB~/B{b©BxÒBuÈûBrü&Bp/OBmbyBj£BgÁç¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~YUB~=úB{q#Bx€MBu×wBs
+¡Bp=ËBmj¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~gÏB~LuB{Bx²ÉBuåóBs7¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~vKB~ZñB{Bxº_¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~ÇB~b
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B|  Bx  Bt  Bp  Bl  Bh  Bd  B`  B\  BX  BT  BP  BL  BH  BD  B@  B<  B8  B4  B0  B,  B(  B$  B   B
+  B  B  B  B
+  B  B  B   Aø  Að  Aè  Aà  AØ  AÐ  AÈ  AÀ  Až  A°  Aš  A   A  A  A  A  Ap  A`  AP  A@  A0  A   A  A   @à  @À  @   @  @@  @   ?      B|
+=Bx
+=Bt
+=Bp
+=Bl
+=Bh
+=Bd
+=B`
+=B\
+=BX
+=BT
+=BP
+=BL
+=BH
+=BD
+=B@
+=B<
+=B8
+=B4
+=B0
+=B,
+=B(
+=B$
+=B 
+=B
+
+=B
+=B
+=B
+=B
+
+=B
+=B
+=B 
+=Aø{Að{Aè{Aà{AØ{AÐ{AÈ{AÀ{Až{A°{Aš{A {A{A{A{A{Ap(öA`(öAP(öA@(öA0(öA (öA(öA (ö@àQì@ÀQì@ Qì@Qì@@£×@ £×?G®<#×
+B|{Bx{Bt{Bp{Bl{Bh{Bd{B`{B\{BX{BT{BP{BL{BH{BD{B@{B<{B8{B4{B0{B,{B({B${B {B
+{B{B{B{B
+{B{B{B {Aø(öAð(öAè(öAà(öAØ(öAÐ(öAÈ(öAÀ(öAž(öA°(öAš(öA (öA(öA(öA(öA(öApQìA`QìAPQìA@QìA0QìA QìAQìA Qì@à£×@À£×@ £×@£×@AG®@G®?\<£×
+B|
+žBx
+žBt
+žBp
+žBl
+žBh
+žBd
+žB`
+žB\
+žBX
+žBT
+žBP
+žBL
+žBH
+žBD
+žB@
+žB<
+žB8
+žB4
+žB0
+žB,
+žB(
+žB$
+žB 
+žB
+
+žB
+žB
+žB
+žB
+
+žB
+žB
+žB 
+žAø=qAð=qAè=qAà=qAØ=qAÐ=qAÈ=qAÀ=qAž=qA°=qAš=qA =qA=qA=qA=qA=qApzáA`záAPzáA@záA0záA záAzáA zá@àõÃ@ÀõÃ@ õÃ@õÃ@Aë
+@ë
+?×
+<õÂB|(öBx(öBt(öBp(öBl(öBh(öBd(öB`(öB\(öBX(öBT(öBP(öBL(öBH(öBD(öB@(öB<(öB8(öB4(öB0(öB,(öB((öB$(öB (öB
+(öB(öB(öB(öB
+(öB(öB(öB (öAøQìAðQìAèQìAàQìAØQìAÐQìAÈQìAÀQìAžQìA°QìAšQìA QìAQìAQìAQìAQìAp£×A`£×AP£×A@£×A0£×A £×A£×A £×@áG®@ÁG®@¡G®@G®@B\@\?
+
+ž=#×
+B|33Bx33Bt33Bp33Bl33Bh33Bd33B`33B\33BX33BT33BP33BL33BH33BD33B@33B<33B833B433B033B,33B(33B$33B 33B
+33B33B33B33B
+33B33B33B 33AøffAðffAèffAàffAØffAÐffAÈffAÀffAžffA°ffAšffA ffAffAffAffAffApÌÍA`ÌÍAPÌÍA@ÌÍA0ÌÍA ÌÍAÌÍA ÌÍ@á@Á@¡@@C33@33?ff=LÌÍB|=qBx=qBt=qBp=qBl=qBh=qBd=qB`=qB\=qBX=qBT=qBP=qBL=qBH=qBD=qB@=qB<=qB8=qB4=qB0=qB,=qB(=qB$=qB =qB
+=qB=qB=qB=qB
+=qB=qB=qB =qAøzáAðzáAèzáAàzáAØzáAÐzáAÈzáAÀzáAžzáA°záAšzáA záAzáAzáAzáAzáApõÃA`õÃAPõÃA@õÃA0õÃA õÃAõÃA õÃ@áë
+@Áë
+@¡ë
+@ë
+@C×
+@×
+?®=uÂB|G®BxG®BtG®BpG®BlG®BhG®BdG®B`G®B\G®BXG®BTG®BPG®BLG®BHG®BDG®B@G®B<G®B8G®B4G®B0G®B,G®B(G®B$G®B G®B
+G®BG®BG®BG®B
+G®BG®BG®B G®Aø\Að\Aè\Aà\AØ\AÐ\AÈ\AÀ\Až\A°\Aš\A \A\A\A\A\Aq
+žAa
+žAQ
+žAA
+žA1
+žA!
+žA
+žA
+ž@â=q@Â=q@¢=q@=q@Dzá@zá?õÃ=\)B|QìBxQìBtQìBpQìBlQìBhQìBdQìB`QìB\QìBXQìBTQìBPQìBLQìBHQìBDQìB@QìB<QìB8QìB4QìB0QìB,QìB(QìB$QìB QìB
+QìBQìBQìBQìB
+QìBQìBQìB QìAø£×Að£×Aè£×Aà£×AØ£×AÐ£×AÈ£×AÀ£×Až£×A°£×Aš£×A £×A£×A£×A£×A£×AqG®AaG®AQG®AAG®A1G®A!G®AG®AG®@â\@Â\@¢\@\@E
+ž@
+ž?=q=£×
+B|\)Bx\)Bt\)Bp\)Bl\)Bh\)Bd\)B`\)B\\)BX\)BT\)BP\)BL\)BH\)BD\)B@\)B<\)B8\)B4\)B0\)B,\)B(\)B$\)B \)B
+\)B\)B\)B\)B
+\)B\)B\)B \)AøžRAðžRAèžRAàžRAØžRAÐžRAÈžRAÀžRAžžRA°žRAšžRA žRAžRAžRAžRAžRAqp€Aap€AQp€AAp€A1p€A!p€Ap€Ap€@âáH@ÂáH@¢áH@áH@EÂ@Â?
+=žQìB|ffBxffBtffBpffBlffBhffBdffB`ffB\ffBXffBTffBPffBLffBHffBDffB@ffB<ffB8ffB4ffB0ffB,ffB(ffB$ffB ffB
+ffBffBffBffB
+ffBffBffB ffAøÌÍAðÌÍAèÌÍAàÌÍAØÌÍAÐÌÍAÈÌÍAÀÌÍAžÌÍA°ÌÍAšÌÍA ÌÍAÌÍAÌÍAÌÍAÌÍAqAaAQAAA1A!AA@ã33@Ã33@£33@33@Fff@ff?ÌÍ=ÌÌÍB|p€Bxp€Btp€Bpp€Blp€Bhp€Bdp€B`p€B\p€BXp€BTp€BPp€BLp€BHp€BDp€B@p€B<p€B8p€B4p€B0p€B,p€B(p€B$p€B p€B
+p€Bp€Bp€Bp€B
+p€Bp€Bp€B p€AøáHAðáHAèáHAàáHAØáHAÐáHAÈáHAÀáHAžáHA°áHAšáHA áHAáHAáHAáHAáHAqÂAaÂAQÂAAÂA1ÂA!ÂAÂAÂ@ã
+@Ã
+@£
+@
+@G
+=@
+=?{=áG®B|záBxzáBtzáBpzáBlzáBhzáBdzáB`záB\záBXzáBTzáBPzáBLzáBHzáBDzáB@záB<záB8záB4záB0záB,záB(záB$záB záB
+záBzáBzáBzáB
+záBzáBzáB záAøõÃAðõÃAèõÃAàõÃAØõÃAÐõÃAÈõÃAÀõÃAžõÃA°õÃAšõÃA õÃAõÃAõÃAõÃAõÃAqë
+Aaë
+AQë
+AAë
+A1ë
+A!ë
+Aë
+Aë
+@ã×
+@Ã×
+@£×
+@×
+@G®@®?\)=õÂB|
+Bx
+Bt
+Bp
+Bl
+Bh
+Bd
+B`
+B\
+BX
+BT
+BP
+BL
+BH
+BD
+B@
+B<
+B8
+B4
+B0
+B,
+B(
+B$
+B 
+B
+
+B
+B
+B
+B
+
+B
+B
+B 
+Aù
+=Añ
+=Aé
+=Aá
+=AÙ
+=AÑ
+=AÉ
+=AÁ
+=A¹
+=A±
+=A©
+=A¡
+=A
+=A
+=A
+=A
+=Ar{Ab{AR{AB{A2{A"{A{A{@ä(ö@Ä(ö@€(ö@(ö@HQì@Qì?£×>
+žB|\Bx\Bt\Bp\Bl\Bh\Bd\B`\B\\BX\BT\BP\BL\BH\BD\B@\B<\B8\B4\B0\B,\B(\B$\B \B
+\B\B\B\B
+\B\B\B \Aù
+žAñ
+žAé
+žAá
+žAÙ
+žAÑ
+žAÉ
+žAÁ
+žA¹
+žA±
+žA©
+žA¡
+žA
+žA
+žA
+žA
+žAr=qAb=qAR=qAB=qA2=qA"=qA=qA=q@äzá@Äzá@€zá@zá@HõÃ@õÃ?ë
+>\)B|BxBtBpBlBhBdB`B\BXBTBPBLBHBDB@B<B8B4B0B,B(B$B B
+BBBB
+BBB Aù33Añ33Aé33Aá33AÙ33AÑ33AÉ33AÁ33A¹33A±33A©33A¡33A33A33A33A33ArffAbffARffABffA2ffA"ffAffAff@äÌÍ@ÄÌÍ@€ÌÍ@ÌÍ@I@	?33>B|£×Bx£×Bt£×Bp£×Bl£×Bh£×Bd£×B`£×B\£×BX£×BT£×BP£×BL£×BH£×BD£×B@£×B<£×B8£×B4£×B0£×B,£×B(£×B$£×B £×B
+£×B£×B£×B£×B
+£×B£×B£×B £×AùG®AñG®AéG®AáG®AÙG®AÑG®AÉG®AÁG®A¹G®A±G®A©G®A¡G®AG®AG®AG®AG®Ar\Ab\AR\AB\A2\A"\A\A\@å
+ž@Å
+ž@¥
+ž@
+
+ž@J=q@
+=q?zá>#×
+B|®Bx®Bt®Bp®Bl®Bh®Bd®B`®B\®BX®BT®BP®BL®BH®BD®B@®B<®B8®B4®B0®B,®B(®B$®B ®B
+®B®B®B®B
+®B®B®B ®Aù\)Añ\)Aé\)Aá\)AÙ\)AÑ\)AÉ\)AÁ\)A¹\)A±\)A©\)A¡\)A\)A\)A\)A\)AržRAbžRARžRABžRA2žRA"žRAžRAžR@åp€@Åp€@¥p€@
+p€@JáH@
+áH?Â>.{B|žRBxžRBtžRBpžRBlžRBhžRBdžRB`žRB\žRBXžRBTžRBPžRBLžRBHžRBDžRB@žRB<žRB8žRB4žRB0žRB,žRB(žRB$žRB žRB
+žRBžRBžRBžRB
+žRBžRBžRB žRAùp€Añp€Aép€Aáp€AÙp€AÑp€AÉp€AÁp€A¹p€A±p€A©p€A¡p€Ap€Ap€Ap€Ap€AráHAbáHARáHABáHA2áHA"áHAáHAáH@åÂ@ÅÂ@¥Â@
+Â@K
+@
+
+?
+>>8QìB|ÂBxÂBtÂBpÂBlÂBhÂBdÂB`ÂB\ÂBXÂBTÂBPÂBLÂBHÂBDÂB@ÂB<ÂB8ÂB4ÂB0ÂB,ÂB(ÂB$ÂB ÂB
+ÂBÂBÂBÂB
+ÂBÂBÂB ÂAù
+Añ
+Aé
+Aá
+AÙ
+AÑ
+AÉ
+AÁ
+A¹
+A±
+A©
+A¡
+A
+A
+A
+A
+As
+=Ac
+=AS
+=AC
+=A3
+=A#
+=A
+=A
+=@æ{@Æ{@Š{@{@L(ö@
+(ö?Qì>B\B|ÌÍBxÌÍBtÌÍBpÌÍBlÌÍBhÌÍBdÌÍB`ÌÍB\ÌÍBXÌÍBTÌÍBPÌÍBLÌÍBHÌÍBDÌÍB@ÌÍB<ÌÍB8ÌÍB4ÌÍB0ÌÍB,ÌÍB(ÌÍB$ÌÍB ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍBÌÍBÌÍB ÌÍAùAñAéAáAÙAÑAÉAÁA¹A±A©A¡AAAAAs33Ac33AS33AC33A333A#33A33A33@æff@Æff@Šff@ff@LÌÍ@
+ÌÍ?>LÌÍB|×
+Bx×
+Bt×
+Bp×
+Bl×
+Bh×
+Bd×
+B`×
+B\×
+BX×
+BT×
+BP×
+BL×
+BH×
+BD×
+B@×
+B<×
+B8×
+B4×
+B0×
+B,×
+B(×
+B$×
+B ×
+B
+×
+B×
+B×
+B×
+B
+×
+B×
+B×
+B ×
+Aù®Añ®Aé®Aá®AÙ®AÑ®AÉ®AÁ®A¹®A±®A©®A¡®A®A®A®A®As\)Ac\)AS\)AC\)A3\)A#\)A\)A\)@æžR@ÆžR@ŠžR@žR@Mp€@
+p€?áH>W
+=B|áHBxáHBtáHBpáHBláHBháHBdáHB`áHB\áHBXáHBTáHBPáHBLáHBHáHBDáHB@áHB<áHB8áHB4áHB0áHB,áHB(áHB$áHB áHB
+áHBáHBáHBáHB
+áHBáHBáHB áHAùÂAñÂAéÂAáÂAÙÂAÑÂAÉÂAÁÂA¹ÂA±ÂA©ÂA¡ÂAÂAÂAÂAÂAs
+Ac
+AS
+AC
+A3
+A#
+A
+A
+@ç
+=@Ç
+=@§
+=@
+=@N{@{?(ö>aG®B|ë
+Bxë
+Btë
+Bpë
+Blë
+Bhë
+Bdë
+B`ë
+B\ë
+BXë
+BTë
+BPë
+BLë
+BHë
+BDë
+B@ë
+B<ë
+B8ë
+B4ë
+B0ë
+B,ë
+B(ë
+B$ë
+B ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+Bë
+Bë
+B ë
+Aù×
+Añ×
+Aé×
+Aá×
+AÙ×
+AÑ×
+AÉ×
+AÁ×
+A¹×
+A±×
+A©×
+A¡×
+A×
+A×
+A×
+A×
+As®Ac®AS®AC®A3®A#®A®A®@ç\)@Ç\)@§\)@\)@NžR@žR?p€>k
+B|õÃBxõÃBtõÃBpõÃBlõÃBhõÃBdõÃB`õÃB\õÃBXõÃBTõÃBPõÃBLõÃBHõÃBDõÃB@õÃB<õÃB8õÃB4õÃB0õÃB,õÃB(õÃB$õÃB õÃB
+õÃBõÃBõÃBõÃB
+õÃBõÃBõÃB õÃAùë
+Añë
+Aéë
+Aáë
+AÙë
+AÑë
+AÉë
+AÁë
+A¹ë
+A±ë
+A©ë
+A¡ë
+Aë
+Aë
+Aë
+Aë
+As×
+Ac×
+AS×
+AC×
+A3×
+A#×
+A×
+A×
+@ç®@Ç®@§®@®@O\)@\)?žR>uÂB}  By  Bu  Bq  Bm  Bi  Be  Ba  B]  BY  BU  BQ  BM  BI  BE  BA  B=  B9  B5  B1  B-  B)  B%  B!  B
+  B  B  B  B
+  B	  B  B  Aú  Aò  Aê  Aâ  AÚ  AÒ  AÊ  AÂ  Aº  A²  Aª  A¢  A  A  A  A  At  Ad  AT  AD  A4  A$  A  A  @è  @È  @š  @  @P  @  ?   >  B}
+=By
+=Bu
+=Bq
+=Bm
+=Bi
+=Be
+=Ba
+=B]
+=BY
+=BU
+=BQ
+=BM
+=BI
+=BE
+=BA
+=B=
+=B9
+=B5
+=B1
+=B-
+=B)
+=B%
+=B!
+=B
+
+=B
+=B
+=B
+=B
+=B	
+=B
+=B
+=Aú{Aò{Aê{Aâ{AÚ{AÒ{AÊ{AÂ{Aº{A²{Aª{A¢{A{A{A{A{At(öAd(öAT(öAD(öA4(öA$(öA(öA(ö@èQì@ÈQì@šQì@Qì@P£×@£×?¡G®>
+
+žB}{By{Bu{Bq{Bm{Bi{Be{Ba{B]{BY{BU{BQ{BM{BI{BE{BA{B={B9{B5{B1{B-{B){B%{B!{B
+{B{B{B{B
+{B	{B{B{Aú(öAò(öAê(öAâ(öAÚ(öAÒ(öAÊ(öAÂ(öAº(öA²(öAª(öA¢(öA(öA(öA(öA(öAtQìAdQìATQìADQìA4QìA$QìAQìAQì@è£×@È£×@š£×@£×@QG®@G®?¢\>=qB}
+žBy
+žBu
+žBq
+žBm
+žBi
+žBe
+žBa
+žB]
+žBY
+žBU
+žBQ
+žBM
+žBI
+žBE
+žBA
+žB=
+žB9
+žB5
+žB1
+žB-
+žB)
+žB%
+žB!
+žB
+
+žB
+žB
+žB
+žB
+
+žB	
+žB
+žB
+žAú=qAò=qAê=qAâ=qAÚ=qAÒ=qAÊ=qAÂ=qAº=qA²=qAª=qA¢=qA=qA=qA=qA=qAtzáAdzáATzáADzáA4záA$záAzáAzá@èõÃ@ÈõÃ@šõÃ@õÃ@Që
+@ë
+?£×
+>\)B}(öBy(öBu(öBq(öBm(öBi(öBe(öBa(öB](öBY(öBU(öBQ(öBM(öBI(öBE(öBA(öB=(öB9(öB5(öB1(öB-(öB)(öB%(öB!(öB
+(öB(öB(öB(öB
+(öB	(öB(öB(öAúQìAòQìAêQìAâQìAÚQìAÒQìAÊQìAÂQìAºQìA²QìAªQìA¢QìAQìAQìAQìAQìAt£×Ad£×AT£×AD£×A4£×A$£×A£×A£×@éG®@ÉG®@©G®@G®@R\@\?¥
+ž>záB}33By33Bu33Bq33Bm33Bi33Be33Ba33B]33BY33BU33BQ33BM33BI33BE33BA33B=33B933B533B133B-33B)33B%33B!33B
+33B33B33B33B
+33B	33B33B33AúffAòffAêffAâffAÚffAÒffAÊffAÂffAºffA²ffAªffA¢ffAffAffAffAffAtÌÍAdÌÍATÌÍADÌÍA4ÌÍA$ÌÍAÌÍAÌÍ@é@É@©@@S33@33?Šff>B}=qBy=qBu=qBq=qBm=qBi=qBe=qBa=qB]=qBY=qBU=qBQ=qBM=qBI=qBE=qBA=qB==qB9=qB5=qB1=qB-=qB)=qB%=qB!=qB
+=qB=qB=qB=qB
+=qB	=qB=qB=qAúzáAòzáAêzáAâzáAÚzáAÒzáAÊzáAÂzáAºzáA²záAªzáA¢záAzáAzáAzáAzáAtõÃAdõÃATõÃADõÃA4õÃA$õÃAõÃAõÃ@éë
+@Éë
+@©ë
+@ë
+@S×
+@×
+?§®>žRB}G®ByG®BuG®BqG®BmG®BiG®BeG®BaG®B]G®BYG®BUG®BQG®BMG®BIG®BEG®BAG®B=G®B9G®B5G®B1G®B-G®B)G®B%G®B!G®B
+G®BG®BG®BG®B
+G®B	G®BG®BG®Aú\Aò\Aê\Aâ\AÚ\AÒ\AÊ\AÂ\Aº\A²\Aª\A¢\A\A\A\A\Au
+žAe
+žAU
+žAE
+žA5
+žA%
+žA
+žA
+ž@ê=q@Ê=q@ª=q@=q@Tzá@zá?šõÂ>£×
+B}QìByQìBuQìBqQìBmQìBiQìBeQìBaQìB]QìBYQìBUQìBQQìBMQìBIQìBEQìBAQìB=QìB9QìB5QìB1QìB-QìB)QìB%QìB!QìB
+QìBQìBQìBQìB
+QìB	QìBQìBQìAú£×Aò£×Aê£×Aâ£×AÚ£×AÒ£×AÊ£×AÂ£×Aº£×A²£×Aª£×A¢£×A£×A£×A£×A£×AuG®AeG®AUG®AEG®A5G®A%G®AG®AG®@ê\@Ê\@ª\@\@U
+ž@
+ž?ª=q>šõÃB}\)By\)Bu\)Bq\)Bm\)Bi\)Be\)Ba\)B]\)BY\)BU\)BQ\)BM\)BI\)BE\)BA\)B=\)B9\)B5\)B1\)B-\)B)\)B%\)B!\)B
+\)B\)B\)B\)B
+\)B	\)B\)B\)AúžRAòžRAêžRAâžRAÚžRAÒžRAÊžRAÂžRAºžRA²žRAªžRA¢žRAžRAžRAžRAžRAup€Aep€AUp€AEp€A5p€A%p€Ap€Ap€@êáH@ÊáH@ªáH@áH@UÂ@Â?«
+>®{B}ffByffBuffBqffBmffBiffBeffBaffB]ffBYffBUffBQffBMffBIffBEffBAffB=ffB9ffB5ffB1ffB-ffB)ffB%ffB!ffB
+ffBffBffBffB
+ffB	ffBffBffAúÌÍAòÌÍAêÌÍAâÌÍAÚÌÍAÒÌÍAÊÌÍAÂÌÍAºÌÍA²ÌÍAªÌÍA¢ÌÍAÌÍAÌÍAÌÍAÌÍAuAeAUAEA5A%AA@ë33@Ë33@«33@33@Vff@ff?¬ÌÍ>³33B}p€Byp€Bup€Bqp€Bmp€Bip€Bep€Bap€B]p€BYp€BUp€BQp€BMp€BIp€BEp€BAp€B=p€B9p€B5p€B1p€B-p€B)p€B%p€B!p€B
+p€Bp€Bp€Bp€B
+p€B	p€Bp€Bp€AúáHAòáHAêáHAâáHAÚáHAÒáHAÊáHAÂáHAºáHA²áHAªáHA¢áHAáHAáHAáHAáHAuÂAeÂAUÂAEÂA5ÂA%ÂAÂAÂ@ë
+@Ë
+@«
+@
+@W
+>@
+>?®{>žQìB}záByzáBuzáBqzáBmzáBizáBezáBazáB]záBYzáBUzáBQzáBMzáBIzáBEzáBAzáB=záB9záB5záB1záB-záB)záB%záB!záB
+záBzáBzáBzáB
+záB	záBzáBzáAúõÃAòõÃAêõÃAâõÃAÚõÃAÒõÃAÊõÃAÂõÃAºõÃA²õÃAªõÃA¢õÃAõÃAõÃAõÃAõÃAuë
+Aeë
+AUë
+AEë
+A5ë
+A%ë
+Aë
+Aë
+@ë×
+@Ë×
+@«×
+@×
+@W®@®?¯\)>œp€B}
+By
+Bu
+Bq
+Bm
+Bi
+Be
+Ba
+B]
+BY
+BU
+BQ
+BM
+BI
+BE
+BA
+B=
+B9
+B5
+B1
+B-
+B)
+B%
+B!
+B
+
+B
+B
+B
+B
+
+B	
+B
+B
+Aû
+=Aó
+=Aë
+=Aã
+=AÛ
+=AÓ
+=AË
+=AÃ
+=A»
+=A³
+=A«
+=A£
+=A
+=A
+=A
+=A
+=Av{Af{AV{AF{A6{A&{A{A{@ì(ö@Ì(ö@¬(ö@(ö@XQì@Qì?°£×>Â\B}\By\Bu\Bq\Bm\Bi\Be\Ba\B]\BY\BU\BQ\BM\BI\BE\BA\B=\B9\B5\B1\B-\B)\B%\B!\B
+\B\B\B\B
+\B	\B\B\Aû
+žAó
+žAë
+žAã
+žAÛ
+žAÓ
+žAË
+žAÃ
+žA»
+žA³
+žA«
+žA£
+žA
+žA
+žA
+žA
+žAv=qAf=qAV=qAF=qA6=qA&=qA=qA=q@ìzá@Ìzá@¬zá@zá@XõÂ@õÂ?±ë
+>Ç®B}ByBuBqBmBiBeBaB]BYBUBQBMBIBEBAB=B9B5B1B-B)B%B!B
+BBBB
+B	BBAû33Aó33Aë33Aã33AÛ33AÓ33AË33AÃ33A»33A³33A«33A£33A33A33A33A33AvffAfffAVffAFffA6ffA&ffAffAff@ìÌÍ@ÌÌÍ@¬ÌÍ@ÌÍ@Y@?³33>ÌÌÍB}£×By£×Bu£×Bq£×Bm£×Bi£×Be£×Ba£×B]£×BY£×BU£×BQ£×BM£×BI£×BE£×BA£×B=£×B9£×B5£×B1£×B-£×B)£×B%£×B!£×B
+£×B£×B£×B£×B
+£×B	£×B£×B£×AûG®AóG®AëG®AãG®AÛG®AÓG®AËG®AÃG®A»G®A³G®A«G®A£G®AG®AG®AG®AG®Av\Af\AV\AF\A6\A&\A\A\@í
+ž@Í
+ž@­
+ž@
+ž@Z=q@=q?Žzá>Ñë
+B}®By®Bu®Bq®Bm®Bi®Be®Ba®B]®BY®BU®BQ®BM®BI®BE®BA®B=®B9®B5®B1®B-®B)®B%®B!®B
+®B®B®B®B
+®B	®B®B®Aû\)Aó\)Aë\)Aã\)AÛ\)AÓ\)AË\)AÃ\)A»\)A³\)A«\)A£\)A\)A\)A\)A\)AvžRAfžRAVžRAFžRA6žRA&žRAžRAžR@íp€@Íp€@­p€@p€@ZáH@áH?µÂ>×
+=B}žRByžRBužRBqžRBmžRBižRBežRBažRB]žRBYžRBUžRBQžRBMžRBIžRBEžRBAžRB=žRB9žRB5žRB1žRB-žRB)žRB%žRB!žRB
+žRBžRBžRBžRB
+žRB	žRBžRBžRAûp€Aóp€Aëp€Aãp€AÛp€AÓp€AËp€AÃp€A»p€A³p€A«p€A£p€Ap€Ap€Ap€Ap€AváHAfáHAVáHAFáHA6áHA&áHAáHAáH@íÂ@ÍÂ@­Â@Â@[
+@
+?·
+>>Ü(öB}ÂByÂBuÂBqÂBmÂBiÂBeÂBaÂB]ÂBYÂBUÂBQÂBMÂBIÂBEÂBAÂB=ÂB9ÂB5ÂB1ÂB-ÂB)ÂB%ÂB!ÂB
+ÂBÂBÂBÂB
+ÂB	ÂBÂBÂAû
+Aó
+Aë
+Aã
+AÛ
+AÓ
+AË
+AÃ
+A»
+A³
+A«
+A£
+A
+A
+A
+A
+Aw
+=Ag
+=AW
+=AG
+=A7
+=A'
+=A
+=A
+=@î{@Î{@®{@{@\(ö@
+(ö?žQì>áG®B}ÌÍByÌÍBuÌÍBqÌÍBmÌÍBiÌÍBeÌÍBaÌÍB]ÌÍBYÌÍBUÌÍBQÌÍBMÌÍBIÌÍBEÌÍBAÌÍB=ÌÍB9ÌÍB5ÌÍB1ÌÍB-ÌÍB)ÌÍB%ÌÍB!ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB	ÌÍBÌÍBÌÍAûAóAëAãAÛAÓAËAÃA»A³A«A£AAAAAw33Ag33AW33AG33A733A'33A33A33@îff@Îff@®ff@ff@\ÌÍ@
+ÌÍ?¹>æffB}×
+By×
+Bu×
+Bq×
+Bm×
+Bi×
+Be×
+Ba×
+B]×
+BY×
+BU×
+BQ×
+BM×
+BI×
+BE×
+BA×
+B=×
+B9×
+B5×
+B1×
+B-×
+B)×
+B%×
+B!×
+B
+×
+B×
+B×
+B×
+B
+×
+B	×
+B×
+B×
+Aû®Aó®Aë®Aã®AÛ®AÓ®AË®AÃ®A»®A³®A«®A£®A®A®A®A®Aw\)Ag\)AW\)AG\)A7\)A'\)A\)A\)@îžR@ÎžR@®žR@žR@]p€@
+p€?ºáH>ë
+B}áHByáHBuáHBqáHBmáHBiáHBeáHBaáHB]áHBYáHBUáHBQáHBMáHBIáHBEáHBAáHB=áHB9áHB5áHB1áHB-áHB)áHB%áHB!áHB
+áHBáHBáHBáHB
+áHB	áHBáHBáHAûÂAóÂAëÂAãÂAÛÂAÓÂAËÂAÃÂA»ÂA³ÂA«ÂA£ÂAÂAÂAÂAÂAw
+Ag
+AW
+AG
+A7
+A'
+A
+A
+@ï
+=@Ï
+=@¯
+=@
+=@^{@
+{?Œ(ö>ð£×B}ë
+Byë
+Buë
+Bqë
+Bmë
+Bië
+Beë
+Baë
+B]ë
+BYë
+BUë
+BQë
+BMë
+BIë
+BEë
+BAë
+B=ë
+B9ë
+B5ë
+B1ë
+B-ë
+B)ë
+B%ë
+B!ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B	ë
+Bë
+Bë
+Aû×
+Aó×
+Aë×
+Aã×
+AÛ×
+AÓ×
+AË×
+AÃ×
+A»×
+A³×
+A«×
+A£×
+A×
+A×
+A×
+A×
+Aw®Ag®AW®AG®A7®A'®A®A®@ï\)@Ï\)@¯\)@\)@^žR@
+žR?œp€>õÂB}õÃByõÃBuõÃBqõÃBmõÃBiõÃBeõÃBaõÃB]õÃBYõÃBUõÃBQõÃBMõÃBIõÃBEõÃBAõÃB=õÃB9õÃB5õÃB1õÃB-õÃB)õÃB%õÃB!õÃB
+õÃBõÃBõÃBõÃB
+õÃB	õÃBõÃBõÃAûë
+Aóë
+Aëë
+Aãë
+AÛë
+AÓë
+AËë
+AÃë
+A»ë
+A³ë
+A«ë
+A£ë
+Aë
+Aë
+Aë
+Aë
+Aw×
+Ag×
+AW×
+AG×
+A7×
+A'×
+A×
+A×
+@ï®@Ï®@¯®@®@_\)@\)?ŸžR>úáHB~  Bz  Bv  Br  Bn  Bj  Bf  Bb  B^  BZ  BV  BR  BN  BJ  BF  BB  B>  B:  B6  B2  B.  B*  B&  B"  B
+  B  B  B  B  B
+  B  B  Aü  Aô  Aì  Aä  AÜ  AÔ  AÌ  AÄ  AŒ  AŽ  A¬  A€  A  A  A  A  Ax  Ah  AX  AH  A8  A(  A  A  @ð  @Ð  @°  @  @`  @   ?À  ?   B~
+=Bz
+=Bv
+=Br
+=Bn
+=Bj
+=Bf
+=Bb
+=B^
+=BZ
+=BV
+=BR
+=BN
+=BJ
+=BF
+=BB
+=B>
+=B:
+=B6
+=B2
+=B.
+=B*
+=B&
+=B"
+=B
+
+=B
+=B
+=B
+=B
+=B
+
+=B
+=B
+=Aü{Aô{Aì{Aä{AÜ{AÔ{AÌ{AÄ{AŒ{AŽ{A¬{A€{A{A{A{A{Ax(öAh(öAX(öAH(öA8(öA((öA(öA(ö@ðQì@ÐQì@°Qì@Qì@`£×@ £×?ÁG®?\B~{Bz{Bv{Br{Bn{Bj{Bf{Bb{B^{BZ{BV{BR{BN{BJ{BF{BB{B>{B:{B6{B2{B.{B*{B&{B"{B
+{B{B{B{B{B
+{B{B{Aü(öAô(öAì(öAä(öAÜ(öAÔ(öAÌ(öAÄ(öAŒ(öAŽ(öA¬(öA€(öA(öA(öA(öA(öAxQìAhQìAXQìAHQìA8QìA(QìAQìAQì@ð£×@Ð£×@°£×@£×@aG®@!G®?Â\?
+žB~
+žBz
+žBv
+žBr
+žBn
+žBj
+žBf
+žBb
+žB^
+žBZ
+žBV
+žBR
+žBN
+žBJ
+žBF
+žBB
+žB>
+žB:
+žB6
+žB2
+žB.
+žB*
+žB&
+žB"
+žB
+
+žB
+žB
+žB
+žB
+žB
+
+žB
+žB
+žAü=qAô=qAì=qAä=qAÜ=qAÔ=qAÌ=qAÄ=qAŒ=qAŽ=qA¬=qA€=qA=qA=qA=qA=qAxzáAhzáAXzáAHzáA8záA(záAzáAzá@ðõÂ@ÐõÂ@°õÂ@õÂ@aë
+@!ë
+?Ã×
+?®B~(öBz(öBv(öBr(öBn(öBj(öBf(öBb(öB^(öBZ(öBV(öBR(öBN(öBJ(öBF(öBB(öB>(öB:(öB6(öB2(öB.(öB*(öB&(öB"(öB
+(öB(öB(öB(öB(öB
+(öB(öB(öAüQìAôQìAìQìAäQìAÜQìAÔQìAÌQìAÄQìAŒQìAŽQìA¬QìA€QìAQìAQìAQìAQìAx£×Ah£×AX£×AH£×A8£×A(£×A£×A£×@ñG®@ÑG®@±G®@G®@b\@"\?Å
+ž?
+=qB~33Bz33Bv33Br33Bn33Bj33Bf33Bb33B^33BZ33BV33BR33BN33BJ33BF33BB33B>33B:33B633B233B.33B*33B&33B"33B
+33B33B33B33B33B
+33B33B33AüffAôffAìffAäffAÜffAÔffAÌffAÄffAŒffAŽffA¬ffA€ffAffAffAffAffAxÌÍAhÌÍAXÌÍAHÌÍA8ÌÍA(ÌÍAÌÍAÌÍ@ñ@Ñ@±@@c33@#33?Æff?
+ÌÍB~=qBz=qBv=qBr=qBn=qBj=qBf=qBb=qB^=qBZ=qBV=qBR=qBN=qBJ=qBF=qBB=qB>=qB:=qB6=qB2=qB.=qB*=qB&=qB"=qB
+=qB=qB=qB=qB=qB
+=qB=qB=qAüzáAôzáAìzáAäzáAÜzáAÔzáAÌzáAÄzáAŒzáAŽzáA¬záA€záAzáAzáAzáAzáAxõÃAhõÃAXõÃAHõÃA8õÃA(õÃAõÃAõÃ@ñë
+@Ñë
+@±ë
+@ë
+@c×
+@#×
+?Ç®?\)B~G®BzG®BvG®BrG®BnG®BjG®BfG®BbG®B^G®BZG®BVG®BRG®BNG®BJG®BFG®BBG®B>G®B:G®B6G®B2G®B.G®B*G®B&G®B"G®B
+G®BG®BG®BG®BG®B
+G®BG®BG®Aü\Aô\Aì\Aä\AÜ\AÔ\AÌ\AÄ\AŒ\AŽ\A¬\A€\A\A\A\A\Ay
+žAi
+žAY
+žAI
+žA9
+žA)
+žA
+žA	
+ž@ò=q@Ò=q@²=q@=q@dzá@$zá?ÈõÂ?ë
+B~QìBzQìBvQìBrQìBnQìBjQìBfQìBbQìB^QìBZQìBVQìBRQìBNQìBJQìBFQìBBQìB>QìB:QìB6QìB2QìB.QìB*QìB&QìB"QìB
+QìBQìBQìBQìBQìB
+QìBQìBQìAü£×Aô£×Aì£×Aä£×AÜ£×AÔ£×AÌ£×AÄ£×AŒ£×AŽ£×A¬£×A€£×A£×A£×A£×A£×AyG®AiG®AYG®AIG®A9G®A)G®AG®A	G®@ò\@Ò\@²\@\@e
+ž@%
+ž?Ê=p?záB~\)Bz\)Bv\)Br\)Bn\)Bj\)Bf\)Bb\)B^\)BZ\)BV\)BR\)BN\)BJ\)BF\)BB\)B>\)B:\)B6\)B2\)B.\)B*\)B&\)B"\)B
+\)B\)B\)B\)B\)B
+\)B\)B\)AüžRAôžRAìžRAäžRAÜžRAÔžRAÌžRAÄžRAŒžRAŽžRA¬žRA€žRAžRAžRAžRAžRAyp€Aip€AYp€AIp€A9p€A)p€Ap€A	p€@òáH@ÒáH@²áH@áH@eÂ@%Â?Ë
+
+?
+=B~ffBzffBvffBrffBnffBjffBfffBbffB^ffBZffBVffBRffBNffBJffBFffBBffB>ffB:ffB6ffB2ffB.ffB*ffB&ffB"ffB
+ffBffBffBffBffB
+ffBffBffAüÌÍAôÌÍAìÌÍAäÌÍAÜÌÍAÔÌÍAÌÌÍAÄÌÍAŒÌÍAŽÌÍA¬ÌÍA€ÌÍAÌÍAÌÍAÌÍAÌÍAyAiAYAIA9A)AA	@ó33@Ó33@³33@33@fff@&ff?ÌÌÍ?B~p€Bzp€Bvp€Brp€Bnp€Bjp€Bfp€Bbp€B^p€BZp€BVp€BRp€BNp€BJp€BFp€BBp€B>p€B:p€B6p€B2p€B.p€B*p€B&p€B"p€B
+p€Bp€Bp€Bp€Bp€B
+p€Bp€Bp€AüáHAôáHAìáHAäáHAÜáHAÔáHAÌáHAÄáHAŒáHAŽáHA¬áHA€áHAáHAáHAáHAáHAyÂAiÂAYÂAIÂA9ÂA)ÂAÂA	Â@ó
+@Ó
+@³
+@
+@g
+>@'
+>?Î{?
+(öB~záBzzáBvzáBrzáBnzáBjzáBfzáBbzáB^záBZzáBVzáBRzáBNzáBJzáBFzáBBzáB>záB:záB6záB2záB.záB*záB&záB"záB
+záBzáBzáBzáBzáB
+záBzáBzáAüõÃAôõÃAìõÃAäõÃAÜõÃAÔõÃAÌõÃAÄõÃAŒõÃAŽõÃA¬õÃA€õÃAõÃAõÃAõÃAõÃAyë
+Aië
+AYë
+AIë
+A9ë
+A)ë
+Aë
+A	ë
+@ó×
+@Ó×
+@³×
+@×
+@g®@'®?Ï\)?
+žRB~
+Bz
+Bv
+Br
+Bn
+Bj
+Bf
+Bb
+B^
+BZ
+BV
+BR
+BN
+BJ
+BF
+BB
+B>
+B:
+B6
+B2
+B.
+B*
+B&
+B"
+B
+
+B
+B
+B
+B
+B
+
+B
+B
+Aý
+=Aõ
+=Aí
+=Aå
+=AÝ
+=AÕ
+=AÍ
+=AÅ
+=Aœ
+=Aµ
+=A­
+=A¥
+=A
+=A
+=A
+=A
+
+=Az{Aj{AZ{AJ{A:{A*{A{A
+{@ô(ö@Ô(ö@Ž(ö@(ö@hQì@(Qì?Ð£×?!G®                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B| XB{Ýf¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÔB{äþBy	¬Bv5@¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|
+OB{óyBy(Bv<ÖBsaBp¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|+ÊB|õBy&£BvKQBsp Bp®Bm¹\Bjäð¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|:FB|pBy5
+BvYÍBs~{Bp£)BmÇ×BjìBh4Be<È¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|HÁB|
+ëByCBvhHBsöBp±¥BmÖSBjûBh¯BeD^Bbi
+B_ ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|W<B|-gByRBvvÃBsrBpÀ BmäÎBk	}Bh.+BeRÙBbwB_6B\ÀäBYìx¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|e·B|;âBy`Bv
+?Bs©íBpÎBmóJBkøBh<ŠBeaUBbB_ª±B\Ï_BYôBWŒBTDP¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|t3B|J]Byo
+BvºBsžhBpÝBnÅBk&sBhK"BeoÐBb~B_¹-B\ÝÛBZBW'7BTKæBQpBN(¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|®B|XØBy}Bv¢5BsÆãBpëBn@Bk4ïBhYBe~KBb¢úB_ÇšB\ìVBZBW5³BTZaBQBN£ŸBKÈlBHô ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|)B|gTByBv°°BsÕ_Bpú
+Bn
+»BkCjBhhBeÇBb±uB_Ö#B\úÒBZBWD.BThÝBQBN²9BKÖçBHûBF DBCKØ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|¥B|uÑByBv¿.BsãÜBqBn-9BkQçBhvBeDBb¿òB_ä¡B]	OBZ-ýBWR¬BTwZBQBNÀ·BKåeBI
+BF.ÁBCSpB@x
+B=£²¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|®B|LByšûBvÍ©BsòWBqBn;ŽBk`bBh
+Be©¿BbÎmB_ó
+B]ÊBZ<yBWa'BT
+ÕBQªBNÏ2BKóàBIBF==BCaëB@B=«HB:ÏöB7û¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ŒB|ÇBy·uBvÜ$Bt ÓBq%BnJ/BknÞBhBež:BbÜéB`B]&EBZJôBWo¢BTQBQžÿBNÝ­BL\BI'
+BFKžBCpgB@B=¹ÃB:ÞqB8 B5'ÎB2Sb¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ËB|¡BByÅñBvêBtMBq3üBnX«Bk}YBh¢BeÆ¶BbëdB`B]4ÁBZYoBW~
+BT¢ÌBQÇzBNì)BL×BI5
+BFZ4BC~âB@£B=È?B:ìíB8B56IB2ZøB/ŠB,«:¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÙB|¯ŸByÔlBvùBt
+ÉBqBwBng%BkÔBh°BeÕ1BbùßB`
+B]C<BZgêBWBT±GBQÕõBNú€BLRBIDBFh¯BC]B@²
+B=ÖºB:ûhB8 B5DÅB2isB/!B,²ÐB)×~B'¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|è
+B|Ÿ9ByâçBwBt,DBqPòBnu¡BkOBhŸýBeã¬Bc[B`-	B]Q·BZvfBWBT¿ÂBQäqBO	BL-ÍBIR|BFw*BCÙB@ÀB=å5B;	äB8.B5S@B2wïB/B,ÁKB)åùB'
+šB$/VB!Zê¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|öB|ÌµByñcBwBt:¿Bq_nBn
+BkšÊBhÍyBeò'BcÕB`;B]`3BZáBW©BTÎ>BQòìBOBL<IBI`÷BF
+¥BCªTB@ÏB=ó±B;_B8=
+B5aŒB2jB/«B,ÏÇB)ôuB'#B$=ÑB!bB
+.B²Â¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}B|Û0ByÿÞBw$BtI;BqméBnBk·FBhÛôBf ¢Bc%QB`IÿB]n­BZ\BWž
+BTÜ¹BRgBO&BLJÄBIorBF!BCžÏB@Ý}B>,B;&ÚB8KB5p7B2åB/¹B,ÞBB*ðB''B$LMB!pûB
+©BºXBßB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}B|é«BzYBw3BtW¶Bq|eBn¡BkÅÁBhêoBf
+Bc3ÌB`XzB]})BZ¡×BWÆ
+BTë4BRãBO4BLY?BI}îBF¢BCÇJB@ëùB>§B;5UB8ZB5~²B2£aB/ÈB,ìœB*lB'6B$ZÈB!wB
+€%BÈÓBíB0B6ÞBbr¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}!úB|ø&Bz
+ÕBwABtf1BqàBn¯BkÔ=BhøëBf
+BcBGB`föB]€BZ°RBWÕBTù¯BR
+]BOC
+BLg»BIiBF±BCÕÆB@útB>"B;CÑB8hB5-B2±ÜB/ÖB,û9B*çB'DB$iDB!òB
+² B×OBûýB «BEYBjB
+¶B
+ºJ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}0uB}€Bz+RBwP Btt¯Bq]BnŸ
+BkâºBihBf,BcPÅB`usB]!BZŸÐBWã~BU,BR,ÛBOQBLv7BIæBF¿BCäCBAñB>- B;RNB8vüB5«B2ÀYB/åB-	¶B*.dB'SB$wÁB!oB
+Á
+BåÌB
+zB/)BS×Bx
+B
+3B
+ÁâBæB$¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}>ñB}Bz9ÍBw^|Bt*Bq§ØBnÌBkñ5BiãBf:Bc_@B`ïB]šBZÍKBWñùBUšBR;VBO`BL³BI©aBFÎBCòŸBAmB><B;`ÉB8
+xB5ª&B2ÎÔB/óB-1B*<ßB'aB$<B!ªëB
+ÏBôGBöB=€BbRBB
+«¯B
+Ð]Bõ
+BºB>hAþÓø¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}MlB}#BzHIBwl÷Bt¥Bq¶TBnÛBkÿ°Bi$_BfI
+Bcm»B`jB]·BZÛÇBX uBU%#BRIÑBOnBL.BI·ÜBFÜBD9BA%çB>JB;oEB8óB5ž¡B2ÝPB0þB-&¬B*K[B'p	B$·B!¹fB
+ÞB
+ÃB'qBLBpÎB|B
+º*B
+ÞÙBB(5BLãAþã#Aù,Aóš¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}[çB}2BzVÄBw{rBt !BqÄÏBné}Bl,Bi2ÚBfWBc|7B` åB]ÅBZêBBXðBU3BRXMBO|ûBL¡©BIÆXBFëBDŽBA4cB>YB;}¿B8¢nB5Ç
+B2ëËB0yB-5(B*YÖB'~B$£3B!ÇáB
+ìB
+>B5ìBZBIB£÷B
+ÈŠB
+íTBB6±B[_Aÿ AùIwAóÔAíÜ1Aè3X¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}jbB}@Bze?BwîBt®BqÓJBn÷ùBl
+§BiAUBffBc²B`¯`B]ÔBZøœBX
+kBUBBRfÈBOwBL°%BIÔÓBFùBD
+0BABÞB>gB;;B8°éB5ÕB2úFB0
+õB-C£B*hQB' B$±®B!Ö\B
+û
+B
+¹BDgBiBÄB²sB
+×!B
+ûÏB ~BE,BiÚAÿ
+AùfnAó¯ËAíù'AèBAâáAÜã¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}xÝB}O
+BzsºBwhBtœBqáÆBotBl+"BiOÑBftBc-B`œÜB]âB[8BX+çBUPBRuCBOòBLŸ BIãOBGýBD,«BAQYB>vB;¶B8¿dB5äB3ÁB0-oB-R
+B*vÍB'{B$À)B!äØB	B
+.4BRãBwB?BÀîB
+åB
+
+KB.ùBS§BxVAÿ:AùeAóÌÂAî
+Aè_{Aâš×AÜò4A×;AÑž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}YB}]Bz5BwŠäBtËBqð@BoïBl9Bi^LBfúBc§©B`ÌWB]ñB[ŽBX:bBU_BR¿BOšmBLÍBIñÊBGxBD;'BA_ÕB>B;©1B8ÍàB5òB3<B0;ëB-`B*
+GB'©öB$Î¥B!óSBB
+<°Ba^B
+Bª»BÏiB
+ôB
+ÆB=tBb#BÑAÿVÿAù [Aóé·Aî3Aè|rAâÅÎAÝ+A×XAÑ¡äAËëAAÆBh¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÔB}lBz±Bwµ_BtÚ
+BqþŒBo#jBlHBilÇBfvBc¶$B`ÚÒB]ÿB[$/BXHÝBUmBR:BO¶èBLÛBJ EBG$óBDI¢BAnPB>ÿB;·­B8Ü[B6	B3%žB0JfB-oB*ÃB'žqB$ÝB"ÎB&}B
+K+BoÙBB¹6BÝäBB
+'ABKïBpBLAÿsõAùœQAô®AîP
+AègAââÅAÝ,"A×u~AÑŸÛAÌ7AÆQAÀñAºò¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}€OB}z~Bz,BwÃÚBtèBr
+7Bo1åBlVBi{BBfðBcÄB`éNB^
+üB[2ªBXWYBU|BR µBOÅdBLêBJÀBG3oBDX
+BA|ËB>¡zB;Æ(B8ê×B6
+B343B0XáB-}B*¢>B'ÆìB$ëB"IB4÷B
+YŠB~UB£BÇ±Bì`BB
+5ŒBZkBB£ÇAÿëAùÚHAô#¥AîmAè¶^Aâÿ»AÝIA×uAÑÛÒAÌ%.AÆnAÀ·çA»DAµJ¡A¯¡È¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}²ÊB}ùBz­§BwÒVBt÷Br²Bo@aBleBiœBf®lBcÓB`÷ÈB^
+wB[A&BXeÔBUBR¯1BOÓßBLøBJ
+<BGAêBDfBAGB>¯õB;Ô£B8ùRB6
+ B3B¯B0g]B-
+B*°¹B'ÕhB$úB"
+ÄBCsB
+h!BÏB±~BÖ-BúÛBB
+D8BhæBB²CAÿ­âAù÷?Aô@AîøAèÓUAã
+±AÝfA×¯kAÑøÇAÌB%AÆAÀÔÞA»
+;AµgA¯°ôA©úQA€Qx¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÁFB}vBzŒ$BwàÓBuBr*0BoNÞBlsBi;BfŒéBcáBaFB^*ôB[O¢BXtQBU BRœ®BOâ\BM
+BJ+¹BGPgBDuBAÄB>ŸrB;ã!B9ÏB6,}B3Q,B0uÚB-B*¿7B'ãåB%B"-BBQðB
+vBMB¿ûBä©B	XB.B
+RµBwcBBÀÀAÿÊÜAú9Aô]AîŠóAèðOAã9¬AÝ	A×ÌeAÒÂAÌ_AÆš{AÀñÙA»;6AµA¯ÍïAªKA€`šAªA,¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÏÁB}¥òBzÊ BwïNBuüBr8«Bo]YBlBiŠ¶BfËdBcðBaÁB^9oB[^
+BXÌBU§zBRÌ)BOðØBMBJ:4BG^ãBDBAš?B>ÌîB;ñB9JB6:ùB3_§B0UB-©B*Í²B'òaB%B";œB`kB
+
+B©ÈBÎvBó%BÓB<B
+a0B
+ßBªBÏ;AÿçÓAú10AôzAîÃéAé
+FAãV£AÝÿA×é\AÒ2¹AÌ|AÆÅrAÁÏA»X+Aµ¡A¯êæAª4BA€}AÆûAXAYµA°Ü¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}Þ=B}ŽmBzÙBwýÊBu"xBrG&BokÔBlBiµ1BfÙàBcþBa#<B^GëB[lBXGBUµöBRÚ€BOÿRBM$BJH°BGm^BD
+BA¶»B>ÛiB< B9$ÆB6ItB3n"B0ÑB-·B*Ü-B( ÜB%%B"J9BnçB
+BžCBÜòB B&NBJýB
+o«BYB¹BÝ·B eAúN&AôAîàßAé*=AãsAÝŒöAØSAÒO¯AÌ
+AÆâiAÁ+ÅA»u"AµŸA°ÛAªQ9A€AãòA-OAv«AÀA	eA`¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ìžB}ÂèBzçBx
+EBu0óBrU¢BozPBlþBiÃ¬Bfè[Bd
+	Ba1žB^VfB[{BXÃBUÄqBRéBP
+ÎBM2|BJW*BG{ÙBD BAÅ6B>éäB<B93AB6WïB3|B0¡LB-ÅúB*ê©B(WB%4B"XŽB}bB
+¢BÆ¿BëmBB4ÊBYxB
+~&B¢ÕBÇBì1B àAúk
+AôŽyAîýÖAéG3AãAÝÙíAØ#IAÒlŠAÌ¶AÆÿ_AÁHŒA»AµÛuA°$ÒAªn/A€·A éAJFA¢AÜÿA&[AožAyr)An y¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}û3B}ÑdBzöBxÀBu?oBrd
+BoËBl­zBiÒ(BföÖBdBa@3B^dáB[BX®>BUÒìBR÷BP
+IBM@÷BJeŠBGTBD¯BAÓ±B>ø`B<
+B9AŒB6fkB3B0¯ÇB-ÔvB*ù$B(
+ÒB%BB"g/BÝB
+°BÕ:BùéB
+BCEBgóB
+¢B±PBÕþBú­B [AúAôÑpAïÍAéd)Aã­AÝöãAØ@?AÒAÌÒùAÇ
+VAÁe³A»¯AµølA°AÉAª%A€ÔA
+ßAg;A°AùöACRA¯Ay¬An>ÑAbÑAWÛ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~	®B}ßßB{Bx)<BuMêBrrBoGBl»õBià£BgRBd* BaN®B^s\B[
+BXŒ¹BUáhBSBP*ÄBMOsBJt!BGÏBDœ~BAâ,B?ÚB<+B9P8B6tæB3B0ŸCB-âñB+B(,NB%PüB"uªBYB
+¿BãµBdB-BQÁBvoB
+
+B¿ËBäzB	(B -ÖAú¥
+AôîgAï7ÃAé AãÊ}AÞÙAØ]6AÒŠAÌïïAÇ9MAÁ©A»ÌA¶cA°^¿Aªš
+A€ñyA:ÕA2AÍAëA`IA©ŠAyæAnxŸAc
+yAW3AL0íA@ß=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~*B}îZB{Bx7·Bu\eBrBo¥ÂBlÊpBiïBgÍBd8{Ba]*B^ØB[ŠBXË4BUïãBSBP9@BM]îBJBG§KBDËùBAð§B?VB<:B9^²B6aB3šB0ÌŸB-ñlB+B(:ÉB%_wB"&BšÔB
+ÍBò1BßB;B`<BêB
+©BÎGBòõB£B <RAúÂAõ
+]AïTºAéAãçsAÞ0ÐAØz-AÒÃAÍ
+æAÇVCAÁA»èýA¶2YA°{¶AªÅA¥oAWÌA¡)Aê
+A3âA}?AÆAzñAn²«AcEeAWØ ALjÛA@ýA5OA*>¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~&¥B}üÕB{!BxF2BujàBrBoŽ=BlØìBiýBg"HBdF÷Bak¥B^SB[µBXÙ°BUþ^BS#
+BPG»BMliBJBGµÆBDÚtBAÿ#B?#ÑB<HB9m.B6ÜB3¶B0Û9B-ÿèB+$B(IDB%móB"¡B·OB
+ÛþB ¬B%ZBJ	Bn·BeB
+žBÜÂBpB&B JÍAúÞ÷Aõ(TAïq±Aé»
+AäjAÞMÇAØ#AÒàAÍ)ÝAÇs9AÁŒAŒóA¶OOA°­Aªâ	A¥+fAtÃAŸA|APÙA5AãAzYÞAnìAcSAX
+AL€ÇAA7A5Ê=A*\÷A
+ï±A¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~5 B~
+QB{/ÿBxT­Buy\Br
+BoÂžBlçgBj
+Bg0ÄBdUrBaz B^ÏB[Ã}BXè+BV
+ÚBS1BPV6BMzäBJBGÄABDèðBB
+B?2LB<VûB9{©B6 WB3ÅB0éŽB.bB+3B(WÀB%|nB"¡
+BÅËB
+êyB'B3ÖBXB}2B¡áB
+ÆBë=BìB4B YHAúûíAõEJAï§AéØAä!aAÞjœAØŽAÒýwAÍFÓAÇ0AÁÙAŒ"éA¶lFA°µ£AªþÿA¥H]A¹AÛA$sAmÏA·,A AzËAo&
+Ac¹@AXKúALÞµAAqoA6)A*äA)AŒYAO@ùúÆ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~CB~ÌB{>zBxc)Bu×Br¬
+BoÑ4BlõâBjBg??BdcíBaB^­JB[ÑøBXö§BVUBS@BPd²BM`BJ®BGÒŒBD÷kBB
+B?@ÈB<evB9$B6®ÓB3ÓB0ø/B.
+ÞB+AB(f:B%éB"¯BÔFB
+øôB
+£BBQBfÿB®B°\B
+Õ
+Bù¹B
+gBCB gÄAûäAõbAAï«AéôúAä>WAÞŽAØÑAÓmAÍcÊAÇ­'AÁöAŒ?àA¶=A°ÒA«öA¥eSA®¯Aø
+AAiAÆAÔ#A
+AzÍžAo`rAcó-AX
+çAM¢AA«\A6>A*ÐÑAcAöFA@ú7v@ã\ë@Ì¹¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~RB~(IB{LøBxqŠBuTBr»Boß±Bm_Bj)BgMŒBdrjBaB^»ÇB[àvBY$BV)ÒBSNBPs/BMÝBJŒBGá:BEèBB*B?OEB<sóB9¢B6œPB3áþB1­B.+[B+P	B(tžB%fB"ŸBâÃB
+rB, BPÎBu}B+BŸÙB
+ãB	6B,äBQB vAAû5ßAõ;AïÈAêõAä[QAÞ€®AØî
+AÓ7hAÍÅAÇÊ!AÂ~AŒ\ÛA¶Š7A°ïA«8ñA¥MAËªAA^cA§ÁAñ
+A:zA{­AohAd-"AX¿ÜAMRAAåQA6x
+A+
+ÆAA0;AÂõ@ú«a@ãÐÕ@ÌöJ@¶¿@x^¿  ¿  ¿  ¿  ¿  ¿  B~`B~6ÅB{[sBx!Bu€ÐBrÉ~Boî,BmÛBj7Bg\7BdæBa¥B^ÊBB[îñBYBV8NBS\üBPªBMŠYBJËBGïµBEdBB9B?]ÀB<nB9§
+B6ËËB3ðzB1(B.9ÖB+^
+B(3B%§áB"ÌBñ>B
+ìB:B_JBøBšŠBÍUB
+òB	±B;`B`B ŒAûRÕAõ2AïåAê.ëAäxHAÞÁ¥AÙ
+AÓT^AÍ»AÇçAÂ0uAŒyÑA¶Ã.A±
+A«UçA¥DAè¡A1ýA{ZAÄ·AAWqA{AAoÔUAdgAXùÊAMAB>A6±ùA+D³A×nAj(Aüã@û:@äD¯@Íj$@¶@µ@Ú@dnD¿  ¿  ¿  ¿  B~o
+B~E?B{iîBxBu³KBr×ùBoüšBm!VBjFBgj³BdaBaŽB^ØŸB[ýlBY"BVFÉBSkwBP&BMŽÔBJÙBGþ1BE"ßBBGB?l<B<êB9µB6ÚFB3þõB1#£B.HRB+m B(®B%¶]B"Û
+Bÿ¹B
+$hBIBmÄBsB·"BÛÐB
+ ~B	%-BIÛBnB 8AûoÌAõ¹)Að
+AêKâAä?AÞÞAÙ'øAÓqUAÍº±AÈAÂMkAŒÈA¶à%A±)A«rÞA¥Œ;A ANôAQAá­A+
+AtgA{{ApBAd üAY3·AMÆqABY,A6ëæA+~ A [A€A	6Ð@û@äž@ÍÝþ@·s@ (è@N]@dç€@72@	ëÊ¿  ¿  B~}B~S»B{xiBxBuÁÆBræuBp
+#Bm/ÑBjTBgy.BdÜBaÂB^ç9B\
+çBY0BVUDBSyòBP¡BMÃOBJçþBH
+¬BE1ZBBV	B?z·B<eB9ÄB6èÂB4
+pB12
+B.VÍB+{{B( *B%ÄØB"éB 5B
+2ãBWB|@B îBÅBêKB
+úB	3šBXVB}B ¡³AûÂAõÖAð|AêhÙAä²5AÞûAÙDïAÓKAÍ×šAÈ!AÂjaAŒ³ŸA¶ýA±FxA«ÕA¥Ù1A "AkëAµGAþ€AHA]A{µtApH/AdÚéAYm€AN ^ABA7%ÓA+žA KHAÞA	pœ@üï@å,d@ÎQÙ@·wM@ Â@Â7@eÏX@8A@
+e)?¹`%?=¥@B~
+B~b7B{äBx«BuÐABrôïBpBm>MBjbûBg©Bd¬XBaÑB^õŽB\cBY?BVc¿BSnBP­
+BMÑÊBJöyBH'BE?ÖBBdB?2B<­áB9ÒB6÷=B4ìB1@B.eHB+öB(®¥B%ÓSB"øB 
+°B
+A^Bf
+B»B¯iBÔBøÆB
+
+tB	B#BfÒBB °.Aû©¹AõóAð<rAê
+ÏAäÏ,AßAÙaåAÓ«BAÍôAÈ=ûAÂXAŒÐµA·A±cnA«¬ËA¥ö(A ?
+AáAÒ>AAd÷A®TA{ïaAp
+Ae×AY§AN:KABÍA7_ÀA+ò{A 
+5AðA	ªª@üzÉ@å >@ÎÅ³@·ë(@¡@6@f·
+@9õ@
+LÞ?»/??Ÿ=
+f$¿  B{{BxºBuÞŒBskBp(BmLÇBjqvBg%BdºÓBaßB_0B\(ÞBYMBVr;BSéBP»BMàFBKôBH)¢BENQBBrÿB?®B<Œ\B9á
+B7¹B4*gB1OB.sÄB+rB(œ B%áÎB#}B ++B
+OÚBtB6BœåBâBAB
++ðB	PBuLBûB ŸªAûÆ¯Aö
+AðYiAê¢ÅAäì"Aß5AÙ~ÜAÓÈ9AÎAÈZòAÂ€OAŒí«A·7A±eA«ÉÁAŠ
+A \{A¥ØAï5A8AîAËKA|)OApŒ	AeNÃAYá~ANt9ACóA7­A,,hA ¿"AQÝA	ä@üî£@æ@Ï9@ž_@¡w@©ì@gÁ@9é©@
+4?Œþö?C)=ES?;ò¿
+¿  ¿  BuæSBsçBp6Bm[CBjñBg€BdÉNBaíýB_«B\7YBY\BV¶BS¥dBPÊBMîÁBKoBH8
+BE\ÌBBzB?Š)B<Ê×B9ïB74B48âB1]B.?B+ŠíB(ËB%ðJB#øB 9ŠB
+^UBB§²BÌ`BñBœB
+:kB	_BÈBšvB Í$AûãŠAö-Aðv_Aê¿ŒAå	AßRuAÙÒAÓå/AÎ.AÈwéAÂÁEAœ
+¢A·SÿA±[A«æžAŠ0A yqAÂÎA
++AUAåAèAA|c<ApõöAe±AZkAN®%AC@àA7ÓA,fUA ùAÊA
+
+@ýb}@æò@Ï­g@žÒÜ@¡øQ@
+Æ@hu@:Ñ^@
+
+G?ŸÎ_?FÈa=@Y<°ø¿  ¿  ¿  ¿  Bp>,BmiÀBjmBg³
+Bd×ÊBaüxB_!'B\EÖBYjBV2BS³áBPØBMý=BK!ìBHFBEkHBB÷B?Ž¥B<ÙSB9þB7"°B4G_B1l
+B.»B+µjB(ÚB%þÆB##uB H#B
+lÑBB¶.BÚÜBÿB$9B
+HçB	mBDB¶òB Û¡Aü AöIûAðXAêÜµAå&AßonAÙžËAÔ'AÎKAÈáAÂÞ>Aœ'A·p÷A±ºTA¬±AŠM
+A jAßÇA)#ArA»ÝA:A|-Aq/çAeÂ¢AZU\ANèACzÑA8
+A, FA!3AÅ»A
+Xu@ýÖ_@æûÔ@Ð!I@¹FŸ@¢l3@š@in9@;¹"@
+?Àç?Jgr=º=2¿  ¿  ¿  ¿  ¿  ¿  BjBgÁBdæEBb
+ôB_/¢B\TPBYxÿBV®BSÂ\BPç
+BN
+¹BK0gBHUBEyÄBBrB?Ã B<çÏB:
+}B71+B4UÚB1zB.7B+ÃåB(èB&
+BB#1ðB VB
+{MBûBÄ©BéWBB2ŽB
+WcB	|B ¿BÅnB ê
+Aü
+AöfòAð°OAêù«AåCAßeAÙÕÁAÔ
+AÎh{AÈ±×AÂû4AœDA·îA±×KA¬ §AŠjA ³aAüœAFAwAØÓA"0A|×AqiÔAeüAZIAO"ACŽŸA8GyA,Ú3A!líAÿšA
+c@þJ:@ço¯@Ð#@¹º@¢à
+@@jUî@< Ö@ë¿?ÂmQ?ND=¹F=L1¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BdíÜBboB_>
+B\bÌBYzBV¬(BSÐ×BPõBN4BK>âBHcBE?BB¬íB?ÑB<öJB:øB7?§B4dUB1B.­²B+Ò`B(÷B&œB#@kB eB
+ÈB®vBÓ%B÷ÓB
+BA/B
+eÞB	B¯;BÓéB øAü:AöèAðÍEAë¢Aå_ÿAß©[AÙòžAÔ<AÎ
+qAÈÎÎAÃ+AœaA·ªäA±ôAA¬=AŠûA ÐWAŽAcA¬mAõÊA?'A}Aq£ÂAf6|AZÉ6AO[ðACî«A8fA- A!ŠÛA9A
+ÌO@þŸ@çã@Ñý@º.r@£Sç@y\@k=¢@=@Ós?Ä<¹?Q¥=Ö
+Ê=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B_EŽB\qGBYõBVº€BSßRBQ BN(¯BKM^BHr
+BEºBB»iB?àB=ÅB:)tB7N"B4rÐB1B.Œ-B+àÛB)B&*8B#NçB sB
+CBŒòBá BNB*ýBO«B
+tYB	Bœ¶BâdBAüWAö ßAðê;Aë3Aå|õAßÆRAÚ¯AÔY
+AÎ¢hAÈëÅAÃ5!Aœ~~A·ÇÛA²7A¬ZAŠ£ñA íNA6«AAÉdAÁA\
+A}JôAqÝ®AfpiA[$AOÞAD(A8»RA-N
+A!àÈAsA
+=@ÿ1î@èWc@Ñ|Ø@º¢L@£ÇÁ@í6@l%V@>p?@»(?Æ
+!?UCç=ó|V=ü§¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BYBVÉBSíÍBQ|BN7*BK[ØBHBE¥6BBÉäB?îB=AB:7ïB7\B4LB1¥úB.ÊšB+ïWB)B&8³B#]bB B
+Š¿BËmBðBÊB9xB^&B
+ÕB	§BÌ1BðßBAütyAöœÕAñ2AëPAåëAßãHAÚ,¥AÔvAÎ¿_AÉ»AÃRAœuA·äÑA²..A¬wAŠÀçA¡
+DAS¡AþAæ[A/·AyA}áArAfªVA[=AOÏËADbA8õ@A-úA"ŽA­oA
+@*@ÿ¥È@èË=@Ñð²@»'@€;@a@m
+
+@?Wô@¢Ü?ÇÛ?Xâž>9r=Œó5¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BSõeBQ øBNEŠBKjUBHBE³±BBØ`B?ýB=!œB:FkB7kB4ÈB1ŽvB.Ù%B+ýÓB)"B&G0B#kÞB B
+µ;BÙéBþB#FBGôBl£B
+QB	µÿBÚ®Bÿ\B$
+AüqAöÚÎAñ$+AëmAå¶äAà AAÚIAÔúAÎÜWAÉ%ŽAÃoAœžmAžÊA²K'A¬AŠÝàA¡'=ApA¹öASAL°A
+A}ŸÓArQAfäGA[wAP	ŒADvA9/1A-ÁìA"TŠAç`A
+zA 
+Õ@é?@Òd@»	@€¯~@Ôó@môÐ@@?ž@¡?É«?\Ê>µž=ÙéÁ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BNM=BKxÐBH~BEÂ-BBæÛB@
+B=08B:TçB7yB4CB1ÂòB.ç B,
+NB)0ýB&U«B#zYB B
+Ã¶BèdB
+B1ÁBVpB{
+B
+ÌB	Ä{Bé)B
+×B2Aü®hAö÷ÅAñA!Aë~AåÓÛAà
+7AÚfAÔ¯ñAÎùMAÉBªAÃAœÕdAž
+ÁA²h
+A¬±zAŠú×A¡D3AAÖíA IAiŠA³A}øÀArzAg
+5A[°ïAPC©ADÖdA9i
+A-ûØA"A!NA
+ŽA FÂ@é²ù@ÒØn@»ýã@¥#X@HÍ@nÜ@A'm@rV?Ëz|?` >%0þ=öàN¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BH¥BEÐšBBõVB@B=>³B:caB7B4¬¿B1ÑmB.öB,ÊB)?xB&d&B#ÕB ­B
+Ò1BöàBB@<BdëBB
+®HB	ÒöB÷€B
+SBAAüË_A÷ŒAñ^Aë§uAåðÑAà:.AÚAÔÌçAÏDAÉ_¡AÃšýAœòZAž;·A²
+A¬ÎqA§ÍA¡a*AªAóãA=@AAÏùA~2¬ArÅgAgX!A[êÜAP}AEQA9£
+A.5ÆA"ÈA[:A
+íõA °@ê&Ô@ÓLH@Œqœ@¥2@Œ§@oÄ8@B!@Z
+?ÍIå?c¿m>3¬E>	ëm¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BBüíB@(B=M.B:qÝB7B4»9B1ßèB/B,)EB)MóB&r¢B#PB »þB
+à­B[B*	BNžBsfBB
+ŒÃB	áqB B*ÎBO|AüèUA÷1±Añ{AëÄlAæ
+ÈAàW%AÚ AÔéÞAÏ3;AÉ|AÃÅôAŸQAžX­A²¢
+A¬ëgA§4ÄA¡~!AÇ}AÚAZ7A£AìðA~lArÿTAgA\$ÉAP·AEJ>A9ÜùA.o³A#mA(A
+'âA º@ê®@ÓÀ"@Œå@Š
+
+@0@p«í@BöÖ@AŸ?ÏN?g^>>B'>fŽ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B=TÅB:XB7¥B4ÉµB1îcB/B,7ÀB)\oB&
+B#¥ËB ÊzB
+ï(BÖB8
+B]3BáBŠB
+Ë>B	ïìBB9IB]øAýKA÷NšAñAëáaAæ*¿Aàt
+AÚœxAÕÕAÏP1AÉAÃâëAŸ,GAžu€A²¿A­]A§QºA¡AätA-ÑAw-AÀA
+	çA~ŠAs9AAgËüA\^¶APñqAE+A:åA.© A#<[AÏA
+aÏA ô@ë@Ô3ý@œYr@Š~æ@€\@q¡@CÞ@)s?Ðè·?jý>P¢Ó>&áû¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B7¬B4Ø1B1üßB/!B,F<B)jêB&B#ŽHB ØöB
+ý€B"SBGBk¯B^Bµ
+B
+ÙºB	þiB#BGÅBltAý"DA÷k¡AñŽýAëþZAæG·AàAÚÚqAÕ#ÎAÏm*AÉ¶AÃÿãAŸI@AžA²ÛùA­%VA§n³A¡žAlAJÉA&AÝA
+&ßA~àxAss2AhíA\§AQ+bAEŸ
+A:P×A.ãA#vKA	A
+ÁA.{@ëk@Ô§ß@œÍT@ŠòÉ@>@r{e@DÆO@7?Òž@?n#>_>5]C¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B2vB/0	B,T·B)yfB&B#ÂÂB çqB
+
+ B0ÎBU|Bz+BÙBÃB
+è6B
+
+äB1BVABzïAý?;A÷AñÑôAìQAæd­Aà®
+AÚ÷gAÕ@ÃAÏ!AÉÓ~AÄ
+ÚAŸf7Až¯A²øðA­BMA§©A¡ÕA
+cAg¿A±
+AúyA
+CÖAeAs­ Ah?ÚA\ÒAQeOAEø	A:ÄA/
+~A#°9ABóA
+Õ­Ahh@ëöE@Õº@ŸA/@§f£@@sc@E®@øì?Ô©?r:õ>mb>CØ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B,\NB)áB&¬B#Ñ>B õìB
+B?IBcøBŠB­TBÒB
+ö±B
+_B@BdŒBjAý\1A÷¥AñîëAì8GAæ€AàËAÛ]AÕ]ºAÏ§AÉðsAÄ9ÑAŸ-AžÌA³çA­_CA§š A¡ñýA;YA¶AÎAoA
+`ÌATSAsç
+AhyÇA]
+AQ<AF1öA:Ä±A/WkA#ê&A|àA
+A¢U@ìj@Õ@Ÿµ@§Ú~@ÿò@tJÎ@F·@à ?ÖW?uÙÅ>|€>RSÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B&Ž&B#ß¹B!gB
+)BMÄBrrB!B»ÐBà~B
+,B
+)ÛBNBs7BæAýy(A÷Â
+Aò
+áAìU>AæAàç÷AÛ1TAÕz±AÏÄ
+AÊ
+jAÄVÇAŸ #AžéA³2ÝA­|:A§ÅA¢óAXPA¡­Aë	A4fA
+}ÃA?At úAh³ŽA]FnAQÙ)AFkäA:þA/XA$$A¶ÍA
+IAÜB@ìÝù@Ön@¿(ã@šNX@sÌ@u2@G}k@ÈT?Ø&z?yx>
+Hv>`Ï¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B!
+þB
+7B\?BîB¥BÊJBîùB
+šB
+8VB]B³BŠaAý
+A÷ß{Aò(ØAìr5Aæ»AáîAÛNKAÕ§AÏáAÊ*aAÄsœAŸœA¹wA³OÓA­1A§âA¢+êAuGAŸ£A AQ]A
+¹AÈ,AtZçAhí¡A]\ARAF¥ÐA;8A/ËFA$^ AðºA
+uA/@íQÓ@ÖwH@¿œ@šÂ2@ç§@v7@He @°	?Ùõã?}i>>oJ\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bc×BjBŽBØÇBýuB
+"#B
+FÒBkB/BŽÝAý³A÷üsAòEÐAì-AæØAá!çAÛkCAÕŽ AÏýýAÊGYAÄ¶AŸÚA¹#oA³lÌA­¶)A§ÿ
+A¢HãA?AÛA$ùAnUA
+·²AAtØAi'A]ºMARMAFßÂA;r|A06A$ñA*¬A
+œfAP @íÅµ@Öë+@À@©6@[@wü@ILä@Í?ÛÅl?[=>Ä>>}Å€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B»¯BçBB
+ðB
+0B
+UMByûBªBÃYAýÐAøjAòbÇAì¬#AæõAá>ÝAÛ:AÕÑAÐóAÊdPAÄ­­AŸ÷	A¹@fA³ÃA­ÓAš
+|A¢eÙA¯5AøAAïALA
+Ô©A
+AtÎÅAiaA]ô:ARôAG¯A;¬iA0?$A$ÑÞAdA
+÷SA@î9@×_@Àz@©©ï@Ïc@wé°@J4@
+?ÝÕ?*Š>â> v¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BB
+?B
+cÈBwB­%BÑÓAýíAø6aAòœAìÉAçwAá[ÓAÛ¥0AÕîAÐ7êAÊGAÄÊ£A¿ A¹]]A³Š¹A­ðAš9sA¢ÏAÌ,AA^åAšCA
+ñA:üAu²AimA^.'ARÀáAGSA;æVA0yA%
+ËAA1@AÃú@î­j@×Òß@ÀøT@ª
+É@C>@xÑe@K
+M@
+g6?ßd=?ú>¢?
+>^¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+k_BòB» BàOAþ	ûAøSWAòŽAìæAç/mAáxÊAÛÂ'AÖ
+AÐTàAÊ=AÄçA¿0÷A¹zSA³Ã°A®
+
+AšViA¢ÆAé#A2A{ÜAÅ9AAWòAuBAiÕYA^hARúÏAGA< CA0²þA%EžAØsAk-Aýè@ï!D@ØF¹@Ál.@ª£@·@y¹@L@
+Nê?á3Š?
+Éx>©}*>œ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BÃ7BîÊAþ&ñAøpNAò¹«AíAçLdAáÁAÛß
+AÖ(zAÐq×AÊ»3AÅA¿MíA¹JA³à§A®*Ašs`A¢ŒœAAOvAÓAâ/A+AtéAu|AjGA^¢AS4»AGÇvA<Z1A0ìëA%¥A`A¥A7Õ@ï@Øº@Áà@«}@*ò@z Í@Lë¶@6?ã?á>°ºÍ>Ùb¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aþ6AøFAòÖ£Aí  Açi]Aá²¹AÛüAÖEsAÐÏAÊØ,AÅ!A¿jåA¹ŽBA³ýA®FüAšYA¢ÙµA#AloAµËAÿ(AH
+AáAu¶}AjI8A^ÛòASn­AHgA<!A1&ÜA%¹ALQAß
+AqÆ@ð	@Ù.u@ÂSë@«y_@Ô@{@MÓ{@ 
+c?äÒ?hk>·øñ>£¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AòåÏAí<öAçSAáÏ°AÜ
+AÖbiAÐ«ÆAÊõ#AÅ>A¿ÜA¹Ñ9AŽA®còAš­OA¢ö¬A@	AeAÒÂA
+Ae{A®ØAuðjAj%A_ßASšAH;TA<ÎA1`ÉA%óA>AùA«³@ð|Û@Ù¢P@ÂÇÅ@«í9@¯@|pG@N»/@!?æ¢?7Ó>¿6>ªT©¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AçAáìŠAÜ6AÖ`AÐÈœAËAÅ[vA¿€ÓA¹î/AŽ7A®éAšÊEA£¢A\ÿAŠ\Aï¹A9ArAËÏAv*XAjœA_OÌASâAHuAA=üA1¶A&-qAÀ+ARåAå @ððµ@Ú*@Ã;@¬a@@}Wú@O¢ä@!íÌ?èqj?<>Æt6>±J¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÜE/AÖVAÐå³AË/AÅxmA¿ÁÉAº
+&AŽTA®ßAšç<A£0AyõAÃRA
+¯AV
+AiAèÅAvdEAjöÿA_ºAT
+tAH¯.A=AéA1Ô£A&g^AúAÓA@ñd@Ú@Ã¯y@¬Ôî@úb@~?¯@P@"Õ?ê@Ó?Ö€>Í±Ú>žÏï¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÐôßAËLAÅcA¿ÞÀAº(
+AŽqyA®ºÖA©3A£MAìAàIA)¥AsAŒ_AŒAv2Ak0íA_Ã§ATVaAHé
+A={ÖA2A&¡KA4AÆÀAYz@ñØi@ÚýÞ@Ä#S@­HÈ@n=@'d@QrM@#œ6?ì<?Š
+>Ôï~>À
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÅ€A¿ûžAºEAŽrA®×ÏA©!+A£jA³åAýAAFAûAÙWA"ŽAvØ#AkjÝA_ýATSAI#
+A=µÇA2HA&Û<AmöA ±Ak@òLK@ÛqÁ@Ä5@­Œª@â@@RZ@$€ú?íßÅ?u>Ü-¢>ÇK5¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AºTAAŽ«hA®ôÅA©>"A£AÐÛA8AcA¬ñAöNA?«AwAk€ËA`7
+ATÊ?AI\úA=ïµA2oA')A§äA:AÍX@òÀ&@Ûå@Å
+@®0
+@Uù@{n@SAÆ@%®?ï¯.?Dÿ>ãkE>ÎÚ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A¯ñA©[A£€uAíÒA7/AAÉèAEA\¡AwKýAkÞžA`qsAU-AIçA>)¡A2Œ\A'OAáÑAtAF@ó4 @ÜYu@Å~ê@®€_@ÉÓ@ïI@T)z@&tc?ñ~?i>êšé>ÕÆ}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A£³¡A
+ÈAT%AAæßA0;AyAw
+êAl¥A`«_AU>AIÐÕA>cA2öIA'A
+ŸA®yAA3@ó§Û@ÜÍO@ÅòÄ@¯9@=®@c#@U.@'\?óN ?ãÒ>ñæ>Ý!¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AcQAºxAÕAM2AAw¿ØAlRA`åLAUxAJ
+ÁA>|A307A'ÂñA
+U«AèeA{ @ôŽ@ÝA*@Æf@¯@±@Öý@Uøã@(CÌ?õ
+i?³:>ù$1>äAÅ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AAj*A³AwùÈAlAa>AU±øAJD²A>×mA3j'A'üâA
+A"WAµ@ô@Ýµ
+@ÆÚ@¯ÿö@%k@Jß@Và§@)+?öìò?Ã? 1*>ëi¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÂ³Ax3¶AlÆpAaY*AUëåAJ~ A?ZA3€A(6ÏA
+ÉA\DAîþ@õq@Þ(æ@ÇN[@°sÏ@D@Ÿº@WÈ\@*E?øŒ[?R,?Ïü>òœ
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AläÉAaAV%ÒAJžA?KGA3ÞA(pŒA
+vA1A(ë@õwK@ÞÀ@ÇÂ5@°çª@
+@2@X°@*úù?úÃ?!?nÍ>ùú®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AVD+AJòzA?
+4A4îA(ª©A
+=dAÐ
+AbØ@õë%@ß@È6@±[@ù@Šn@YÄ@+â­?ü[,? ðý?
+
+? )¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A?£A4QàA(äA
+wTA
+AÊ@ö_@ß}@È©ñ@±Ïg@ôÛ@P@Z@,Êr?þ*µ?¢À?¬°?:û¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A)óA
+±BACüAÖ¶@öÒâ@ßøW@É
+Ì@²CA@hµ@*@[g=@-²&?ÿú
+?€ï?K?ÙÌ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AbUA€@÷FŒ@àl1@ÉŠ@²·@Ü@
+@\Nò@.Ú@ äÃ?Š_X?êU?
+x¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @÷n@àà
+@Ê@³*õ@Pj@
+uß@]6Š@/@Ìx?š.Á?'?q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ÊB2@³Ð@ÄD@
+é¹@^
+[@0iC@Ž,?©þ*?
+'ø?¶B¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ þ@]@_@1Q@ñ?«Í³? Ç
+?U¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @_@28œ@¥?­
+?$eÝ?óç¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ý	?¯l
+?(®?
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?)ê;?!1¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B~
+B~záB~p€B~ffB~\)B~QìB~G®B~=qB~33B~(öB~
+žB~{B~
+=B~  B}õÃB}ë
+B}áHB}×
+B}ÌÍB}ÂB}žRB}®B}£×B}B}\B}
+B}záB}p€B}ffB}\)B}QìB}G®B}=qB}33B}(öB}
+žB}{B}
+=B}  B|õÃB|ë
+B|áHB|×
+B|ÌÍB|ÂB|žRB|®B|£×B|B|\B|
+B|záB|p€B|ffB|\)B|QìB|G®B|=qB|33B|(öB|
+žB|{B|
+=B|  Bz
+BzzáBzp€BzffBz\)BzQìBzG®Bz=qBz33Bz(öBz
+žBz{Bz
+=Bz  ByõÃByë
+ByáHBy×
+ByÌÍByÂByžRBy®By£×ByBy\By
+ByzáByp€ByffBy\)ByQìByG®By=qBy33By(öBy
+žBy{By
+=By  BxõÃBxë
+BxáHBx×
+BxÌÍBxÂBxžRBx®Bx£×BxBx\Bx
+BxzáBxp€BxffBx\)BxQìBxG®Bx=qBx33Bx(öBx
+žBx{Bx
+=Bx  Bv
+BvzáBvp€BvffBv\)BvQìBvG®Bv=qBv33Bv(öBv
+žBv{Bv
+=Bv  BuõÃBuë
+BuáHBu×
+BuÌÍBuÂBužRBu®Bu£×BuBu\Bu
+BuzáBup€BuffBu\)BuQìBuG®Bu=qBu33Bu(öBu
+žBu{Bu
+=Bu  BtõÃBtë
+BtáHBt×
+BtÌÍBtÂBtžRBt®Bt£×BtBt\Bt
+BtzáBtp€BtffBt\)BtQìBtG®Bt=qBt33Bt(öBt
+žBt{Bt
+=Bt  Br
+BrzáBrp€BrffBr\)BrQìBrG®Br=qBr33Br(öBr
+žBr{Br
+=Br  BqõÃBqë
+BqáHBq×
+BqÌÍBqÂBqžRBq®Bq£×BqBq\Bq
+BqzáBqp€BqffBq\)BqQìBqG®Bq=qBq33Bq(öBq
+žBq{Bq
+=Bq  BpõÃBpë
+BpáHBp×
+BpÌÍBpÂBpžRBp®Bp£×BpBp\Bp
+BpzáBpp€BpffBp\)BpQìBpG®Bp=qBp33Bp(öBp
+žBp{Bp
+=Bp  Bn
+BnzáBnp€BnffBn\)BnQìBnG®Bn=qBn33Bn(öBn
+žBn{Bn
+=Bn  BmõÃBmë
+BmáHBm×
+BmÌÍBmÂBmžRBm®Bm£×BmBm\Bm
+BmzáBmp€BmffBm\)BmQìBmG®Bm=qBm33Bm(öBm
+žBm{Bm
+=Bm  BlõÃBlë
+BláHBl×
+BlÌÍBlÂBlžRBl®Bl£×BlBl\Bl
+BlzáBlp€BlffBl\)BlQìBlG®Bl=qBl33Bl(öBl
+žBl{Bl
+=Bl  Bj
+BjzáBjp€BjffBj\)BjQìBjG®Bj=qBj33Bj(öBj
+žBj{Bj
+=Bj  BiõÃBië
+BiáHBi×
+BiÌÍBiÂBižRBi®Bi£×BiBi\Bi
+BizáBip€BiffBi\)BiQìBiG®Bi=qBi33Bi(öBi
+žBi{Bi
+=Bi  BhõÃBhë
+BháHBh×
+BhÌÍBhÂBhžRBh®Bh£×BhBh\Bh
+BhzáBhp€BhffBh\)BhQìBhG®Bh=qBh33Bh(öBh
+žBh{Bh
+=Bh  Bf
+BfzáBfp€BfffBf\)BfQìBfG®Bf=qBf33Bf(öBf
+žBf{Bf
+=Bf  BeõÃBeë
+BeáHBe×
+BeÌÍBeÂBežRBe®Be£×BeBe\Be
+BezáBep€BeffBe\)BeQìBeG®Be=qBe33Be(öBe
+žBe{Be
+=Be  BdõÃBdë
+BdáHBd×
+BdÌÍBdÂBdžRBd®Bd£×BdBd\Bd
+BdzáBdp€BdffBd\)BdQìBdG®Bd=qBd33Bd(öBd
+žBd{Bd
+=Bd  Bb
+BbzáBbp€BbffBb\)BbQìBbG®Bb=qBb33Bb(öBb
+žBb{Bb
+=Bb  BaõÃBaë
+BaáHBa×
+BaÌÍBaÂBažRBa®Ba£×BaBa\Ba
+BazáBap€BaffBa\)BaQìBaG®Ba=qBa33Ba(öBa
+žBa{Ba
+=Ba  B`õÃB`ë
+B`áHB`×
+B`ÌÍB`ÂB`žRB`®B`£×B`B`\B`
+B`záB`p€B`ffB`\)B`QìB`G®B`=qB`33B`(öB`
+žB`{B`
+=B`  B^
+B^záB^p€B^ffB^\)B^QìB^G®B^=qB^33B^(öB^
+žB^{B^
+=B^  B]õÃB]ë
+B]áHB]×
+B]ÌÍB]ÂB]žRB]®B]£×B]B]\B]
+B]záB]p€B]ffB]\)B]QìB]G®B]=qB]33B](öB]
+žB]{B]
+=B]  B\õÃB\ë
+B\áHB\×
+B\ÌÍB\ÂB\žRB\®B\£×B\B\\B\
+B\záB\p€B\ffB\\)B\QìB\G®B\=qB\33B\(öB\
+žB\{B\
+=B\  BZ
+BZzáBZp€BZffBZ\)BZQìBZG®BZ=qBZ33BZ(öBZ
+žBZ{BZ
+=BZ  BYõÃBYë
+BYáHBY×
+BYÌÍBYÂBYžRBY®BY£×BYBY\BY
+BYzáBYp€BYffBY\)BYQìBYG®BY=qBY33BY(öBY
+žBY{BY
+=BY  BXõÃBXë
+BXáHBX×
+BXÌÍBXÂBXžRBX®BX£×BXBX\BX
+BXzáBXp€BXffBX\)BXQìBXG®BX=qBX33BX(öBX
+žBX{BX
+=BX  BV
+BVzáBVp€BVffBV\)BVQìBVG®BV=qBV33BV(öBV
+žBV{BV
+=BV  BUõÃBUë
+BUáHBU×
+BUÌÍBUÂBUžRBU®BU£×BUBU\BU
+BUzáBUp€BUffBU\)BUQìBUG®BU=qBU33BU(öBU
+žBU{BU
+=BU  BTõÃBTë
+BTáHBT×
+BTÌÍBTÂBTžRBT®BT£×BTBT\BT
+BTzáBTp€BTffBT\)BTQìBTG®BT=qBT33BT(öBT
+žBT{BT
+=BT  BR
+BRzáBRp€BRffBR\)BRQìBRG®BR=qBR33BR(öBR
+žBR{BR
+=BR  BQõÃBQë
+BQáHBQ×
+BQÌÍBQÂBQžRBQ®BQ£×BQBQ\BQ
+BQzáBQp€BQffBQ\)BQQìBQG®BQ=qBQ33BQ(öBQ
+žBQ{BQ
+=BQ  BPõÃBPë
+BPáHBP×
+BPÌÍBPÂBPžRBP®BP£×BPBP\BP
+BPzáBPp€BPffBP\)BPQìBPG®BP=qBP33BP(öBP
+žBP{BP
+=BP  BN
+BNzáBNp€BNffBN\)BNQìBNG®BN=qBN33BN(öBN
+žBN{BN
+=BN  BMõÃBMë
+BMáHBM×
+BMÌÍBMÂBMžRBM®BM£×BMBM\BM
+BMzáBMp€BMffBM\)BMQìBMG®BM=qBM33BM(öBM
+žBM{BM
+=BM  BLõÃBLë
+BLáHBL×
+BLÌÍBLÂBLžRBL®BL£×BLBL\BL
+BLzáBLp€BLffBL\)BLQìBLG®BL=qBL33BL(öBL
+žBL{BL
+=BL  BJ
+BJzáBJp€BJffBJ\)BJQìBJG®BJ=qBJ33BJ(öBJ
+žBJ{BJ
+=BJ  BIõÃBIë
+BIáHBI×
+BIÌÍBIÂBIžRBI®BI£×BIBI\BI
+BIzáBIp€BIffBI\)BIQìBIG®BI=qBI33BI(öBI
+žBI{BI
+=BI  BHõÃBHë
+BHáHBH×
+BHÌÍBHÂBHžRBH®BH£×BHBH\BH
+BHzáBHp€BHffBH\)BHQìBHG®BH=qBH33BH(öBH
+žBH{BH
+=BH  BF
+BFzáBFp€BFffBF\)BFQìBFG®BF=qBF33BF(öBF
+žBF{BF
+=BF  BEõÃBEë
+BEáHBE×
+BEÌÍBEÂBEžRBE®BE£×BEBE\BE
+BEzáBEp€BEffBE\)BEQìBEG®BE=qBE33BE(öBE
+žBE{BE
+=BE  BDõÃBDë
+BDáHBD×
+BDÌÍBDÂBDžRBD®BD£×BDBD\BD
+BDzáBDp€BDffBD\)BDQìBDG®BD=qBD33BD(öBD
+žBD{BD
+=BD  BB
+BBzáBBp€BBffBB\)BBQìBBG®BB=qBB33BB(öBB
+žBB{BB
+=BB  BAõÃBAë
+BAáHBA×
+BAÌÍBAÂBAžRBA®BA£×BABA\BA
+BAzáBAp€BAffBA\)BAQìBAG®BA=qBA33BA(öBA
+žBA{BA
+=BA  B@õÃB@ë
+B@áHB@×
+B@ÌÍB@ÂB@žRB@®B@£×B@B@\B@
+B@záB@p€B@ffB@\)B@QìB@G®B@=qB@33B@(öB@
+žB@{B@
+=B@  B>
+B>záB>p€B>ffB>\)B>QìB>G®B>=qB>33B>(öB>
+žB>{B>
+=B>  B=õÃB=ë
+B=áHB=×
+B=ÌÍB=ÂB=žRB=®B=£×B=B=\B=
+B=záB=p€B=ffB=\)B=QìB=G®B==qB=33B=(öB=
+žB={B=
+=B=  B<õÃB<ë
+B<áHB<×
+B<ÌÍB<ÂB<žRB<®B<£×B<B<\B<
+B<záB<p€B<ffB<\)B<QìB<G®B<=qB<33B<(öB<
+žB<{B<
+=B<  B:
+B:záB:p€B:ffB:\)B:QìB:G®B:=qB:33B:(öB:
+žB:{B:
+=B:  B9õÃB9ë
+B9áHB9×
+B9ÌÍB9ÂB9žRB9®B9£×B9B9\B9
+B9záB9p€B9ffB9\)B9QìB9G®B9=qB933B9(öB9
+žB9{B9
+=B9  B8õÃB8ë
+B8áHB8×
+B8ÌÍB8ÂB8žRB8®B8£×B8B8\B8
+B8záB8p€B8ffB8\)B8QìB8G®B8=qB833B8(öB8
+žB8{B8
+=B8  B6
+B6záB6p€B6ffB6\)B6QìB6G®B6=qB633B6(öB6
+žB6{B6
+=B6  B5õÃB5ë
+B5áHB5×
+B5ÌÍB5ÂB5žRB5®B5£×B5B5\B5
+B5záB5p€B5ffB5\)B5QìB5G®B5=qB533B5(öB5
+žB5{B5
+=B5  B4õÃB4ë
+B4áHB4×
+B4ÌÍB4ÂB4žRB4®B4£×B4B4\B4
+B4záB4p€B4ffB4\)B4QìB4G®B4=qB433B4(öB4
+žB4{B4
+=B4  B2
+B2záB2p€B2ffB2\)B2QìB2G®B2=qB233B2(öB2
+žB2{B2
+=B2  B1õÃB1ë
+B1áHB1×
+B1ÌÍB1ÂB1žRB1®B1£×B1B1\B1
+B1záB1p€B1ffB1\)B1QìB1G®B1=qB133B1(öB1
+žB1{B1
+=B1  B0õÃB0ë
+B0áHB0×
+B0ÌÍB0ÂB0žRB0®B0£×B0B0\B0
+B0záB0p€B0ffB0\)B0QìB0G®B0=qB033B0(öB0
+žB0{B0
+=B0  B.
+B.záB.p€B.ffB.\)B.QìB.G®B.=qB.33B.(öB.
+žB.{B.
+=B.  B-õÃB-ë
+B-áHB-×
+B-ÌÍB-ÂB-žRB-®B-£×B-B-\B-
+B-záB-p€B-ffB-\)B-QìB-G®B-=qB-33B-(öB-
+žB-{B-
+=B-  B,õÃB,ë
+B,áHB,×
+B,ÌÍB,ÂB,žRB,®B,£×B,B,\B,
+B,záB,p€B,ffB,\)B,QìB,G®B,=qB,33B,(öB,
+žB,{B,
+=B,  B*
+B*záB*p€B*ffB*\)B*QìB*G®B*=qB*33B*(öB*
+žB*{B*
+=B*  B)õÃB)ë
+B)áHB)×
+B)ÌÍB)ÂB)žRB)®B)£×B)B)\B)
+B)záB)p€B)ffB)\)B)QìB)G®B)=qB)33B)(öB)
+žB){B)
+=B)  B(õÃB(ë
+B(áHB(×
+B(ÌÍB(ÂB(žRB(®B(£×B(B(\B(
+B(záB(p€B(ffB(\)B(QìB(G®B(=qB(33B((öB(
+žB({B(
+=B(  B&
+B&záB&p€B&ffB&\)B&QìB&G®B&=qB&33B&(öB&
+žB&{B&
+=B&  B%õÃB%ë
+B%áHB%×
+B%ÌÍB%ÂB%žRB%®B%£×B%B%\B%
+B%záB%p€B%ffB%\)B%QìB%G®B%=qB%33B%(öB%
+žB%{B%
+=B%  B$õÃB$ë
+B$áHB$×
+B$ÌÍB$ÂB$žRB$®B$£×B$B$\B$
+B$záB$p€B$ffB$\)B$QìB$G®B$=qB$33B$(öB$
+žB${B$
+=B$  B"
+B"záB"p€B"ffB"\)B"QìB"G®B"=qB"33B"(öB"
+žB"{B"
+=B"  B!õÃB!ë
+B!áHB!×
+B!ÌÍB!ÂB!žRB!®B!£×B!B!\B!
+B!záB!p€B!ffB!\)B!QìB!G®B!=qB!33B!(öB!
+žB!{B!
+=B!  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B	õÃB	ë
+B	áHB	×
+B	ÌÍB	ÂB	žRB	®B	£×B	B	\B	
+B	záB	p€B	ffB	\)B	QìB	G®B	=qB	33B	(öB	
+žB	{B	
+=B	  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   Aý
+=AüõÃAüáHAüÌÍAüžRAü£×Aü\AüzáAüffAüQìAü=qAü(öAü{Aü  Aûë
+Aû×
+AûÂAû®AûAû
+Aûp€Aû\)AûG®Aû33Aû
+žAû
+=AúõÃAúáHAúÌÍAúžRAú£×Aú\AúzáAúffAúQìAú=qAú(öAú{Aú  Aùë
+Aù×
+AùÂAù®AùAù
+Aùp€Aù\)AùG®Aù33Aù
+žAù
+=AøõÃAøáHAøÌÍAøžRAø£×Aø\AøzáAøffAøQìAø=qAø(öAø{Aø  Aõ
+=AôõÃAôáHAôÌÍAôžRAô£×Aô\AôzáAôffAôQìAô=qAô(öAô{Aô  Aóë
+Aó×
+AóÂAó®AóAó
+Aóp€Aó\)AóG®Aó33Aó
+žAó
+=AòõÃAòáHAòÌÍAòžRAò£×Aò\AòzáAòffAòQìAò=qAò(öAò{Aò  Añë
+Añ×
+AñÂAñ®AñAñ
+Añp€Añ\)AñG®Añ33Añ
+žAñ
+=AðõÃAðáHAðÌÍAðžRAð£×Að\AðzáAðffAðQìAð=qAð(öAð{Að  Aí
+=AìõÃAìáHAìÌÍAìžRAì£×Aì\AìzáAìffAìQìAì=qAì(öAì{Aì  Aëë
+Aë×
+AëÂAë®AëAë
+Aëp€Aë\)AëG®Aë33Aë
+žAë
+=AêõÃAêáHAêÌÍAêžRAê£×Aê\AêzáAêffAêQìAê=qAê(öAê{Aê  Aéë
+Aé×
+AéÂAé®AéAé
+Aép€Aé\)AéG®Aé33Aé
+žAé
+=AèõÃAèáHAèÌÍAèžRAè£×Aè\AèzáAèffAèQìAè=qAè(öAè{Aè  Aå
+=AäõÃAäáHAäÌÍAäžRAä£×Aä\AäzáAäffAäQìAä=qAä(öAä{Aä  Aãë
+Aã×
+AãÂAã®AãAã
+Aãp€Aã\)AãG®Aã33Aã
+žAã
+=AâõÃAâáHAâÌÍAâžRAâ£×Aâ\AâzáAâffAâQìAâ=qAâ(öAâ{Aâ  Aáë
+Aá×
+AáÂAá®AáAá
+Aáp€Aá\)AáG®Aá33Aá
+žAá
+=AàõÃAàáHAàÌÍAàžRAà£×Aà\AàzáAàffAàQìAà=qAà(öAà{Aà  AÝ
+=AÜõÃAÜáHAÜÌÍAÜžRAÜ£×AÜ\AÜzáAÜffAÜQìAÜ=qAÜ(öAÜ{AÜ  AÛë
+AÛ×
+AÛÂAÛ®AÛAÛ
+AÛp€AÛ\)AÛG®AÛ33AÛ
+žAÛ
+=AÚõÃAÚáHAÚÌÍAÚžRAÚ£×AÚ\AÚzáAÚffAÚQìAÚ=qAÚ(öAÚ{AÚ  AÙë
+AÙ×
+AÙÂAÙ®AÙAÙ
+AÙp€AÙ\)AÙG®AÙ33AÙ
+žAÙ
+=AØõÃAØáHAØÌÍAØžRAØ£×AØ\AØzáAØffAØQìAØ=qAØ(öAØ{AØ  AÕ
+=AÔõÃAÔáHAÔÌÍAÔžRAÔ£×AÔ\AÔzáAÔffAÔQìAÔ=qAÔ(öAÔ{AÔ  AÓë
+AÓ×
+AÓÂAÓ®AÓAÓ
+AÓp€AÓ\)AÓG®AÓ33AÓ
+žAÓ
+=AÒõÃAÒáHAÒÌÍAÒžRAÒ£×AÒ\AÒzáAÒffAÒQìAÒ=qAÒ(öAÒ{AÒ  AÑë
+AÑ×
+AÑÂAÑ®AÑAÑ
+AÑp€AÑ\)AÑG®AÑ33AÑ
+žAÑ
+=AÐõÃAÐáHAÐÌÍAÐžRAÐ£×AÐ\AÐzáAÐffAÐQìAÐ=qAÐ(öAÐ{AÐ  AÍ
+=AÌõÃAÌáHAÌÌÍAÌžRAÌ£×AÌ\AÌzáAÌffAÌQìAÌ=qAÌ(öAÌ{AÌ  AËë
+AË×
+AËÂAË®AËAË
+AËp€AË\)AËG®AË33AË
+žAË
+=AÊõÃAÊáHAÊÌÍAÊžRAÊ£×AÊ\AÊzáAÊffAÊQìAÊ=qAÊ(öAÊ{AÊ  AÉë
+AÉ×
+AÉÂAÉ®AÉAÉ
+AÉp€AÉ\)AÉG®AÉ33AÉ
+žAÉ
+=AÈõÃAÈáHAÈÌÍAÈžRAÈ£×AÈ\AÈzáAÈffAÈQìAÈ=qAÈ(öAÈ{AÈ  AÅ
+=AÄõÃAÄáHAÄÌÍAÄžRAÄ£×AÄ\AÄzáAÄffAÄQìAÄ=qAÄ(öAÄ{AÄ  AÃë
+AÃ×
+AÃÂAÃ®AÃAÃ
+AÃp€AÃ\)AÃG®AÃ33AÃ
+žAÃ
+=AÂõÃAÂáHAÂÌÍAÂžRAÂ£×AÂ\AÂzáAÂffAÂQìAÂ=qAÂ(öAÂ{AÂ  AÁë
+AÁ×
+AÁÂAÁ®AÁAÁ
+AÁp€AÁ\)AÁG®AÁ33AÁ
+žAÁ
+=AÀõÃAÀáHAÀÌÍAÀžRAÀ£×AÀ\AÀzáAÀffAÀQìAÀ=qAÀ(öAÀ{AÀ  Aœ
+=AŒõÃAŒáHAŒÌÍAŒžRAŒ£×AŒ\AŒzáAŒffAŒQìAŒ=qAŒ(öAŒ{AŒ  A»ë
+A»×
+A»ÂA»®A»A»
+A»p€A»\)A»G®A»33A»
+žA»
+=AºõÃAºáHAºÌÍAºžRAº£×Aº\AºzáAºffAºQìAº=qAº(öAº{Aº  A¹ë
+A¹×
+A¹ÂA¹®A¹A¹
+A¹p€A¹\)A¹G®A¹33A¹
+žA¹
+=AžõÃAžáHAžÌÍAžžRAž£×Až\AžzáAžffAžQìAž=qAž(öAž{Až  Aµ
+=AŽõÃAŽáHAŽÌÍAŽžRAŽ£×AŽ\AŽzáAŽffAŽQìAŽ=qAŽ(öAŽ{AŽ  A³ë
+A³×
+A³ÂA³®A³A³
+A³p€A³\)A³G®A³33A³
+žA³
+=A²õÃA²áHA²ÌÍA²žRA²£×A²\A²záA²ffA²QìA²=qA²(öA²{A²  A±ë
+A±×
+A±ÂA±®A±A±
+A±p€A±\)A±G®A±33A±
+žA±
+=A°õÃA°áHA°ÌÍA°žRA°£×A°\A°záA°ffA°QìA°=qA°(öA°{A°  A­
+=A¬õÃA¬áHA¬ÌÍA¬žRA¬£×A¬\A¬záA¬ffA¬QìA¬=qA¬(öA¬{A¬  A«ë
+A«×
+A«ÂA«®A«A«
+A«p€A«\)A«G®A«33A«
+žA«
+=AªõÃAªáHAªÌÍAªžRAª£×Aª\AªzáAªffAªQìAª=qAª(öAª{Aª  A©ë
+A©×
+A©ÂA©®A©A©
+A©p€A©\)A©G®A©33A©
+žA©
+=AšõÃAšáHAšÌÍAšžRAš£×Aš\AšzáAšffAšQìAš=qAš(öAš{Aš  A¥
+=A€õÃA€áHA€ÌÍA€žRA€£×A€\A€záA€ffA€QìA€=qA€(öA€{A€  A£ë
+A£×
+A£ÂA£®A£A£
+A£p€A£\)A£G®A£33A£
+žA£
+=A¢õÃA¢áHA¢ÌÍA¢žRA¢£×A¢\A¢záA¢ffA¢QìA¢=qA¢(öA¢{A¢  A¡ë
+A¡×
+A¡ÂA¡®A¡A¡
+A¡p€A¡\)A¡G®A¡33A¡
+žA¡
+=A õÃA áHA ÌÍA žRA £×A \A záA ffA QìA =qA (öA {A   A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Az{Ayë
+AyÂAyAyp€AyG®Ay
+žAxõÃAxÌÍAx£×AxzáAxQìAx(öAx  Aw×
+Aw®Aw
+Aw\)Aw33Aw
+=AváHAvžRAv\AvffAv=qAv{Auë
+AuÂAuAup€AuG®Au
+žAtõÃAtÌÍAt£×AtzáAtQìAt(öAt  As×
+As®As
+As\)As33As
+=AráHAržRAr\ArffAr=qAr{Aqë
+AqÂAqAqp€AqG®Aq
+žApõÃApÌÍAp£×ApzáApQìAp(öAp  Aj{Aië
+AiÂAiAip€AiG®Ai
+žAhõÃAhÌÍAh£×AhzáAhQìAh(öAh  Ag×
+Ag®Ag
+Ag\)Ag33Ag
+=AfáHAfžRAf\AfffAf=qAf{Aeë
+AeÂAeAep€AeG®Ae
+žAdõÃAdÌÍAd£×AdzáAdQìAd(öAd  Ac×
+Ac®Ac
+Ac\)Ac33Ac
+=AbáHAbžRAb\AbffAb=qAb{Aaë
+AaÂAaAap€AaG®Aa
+žA`õÃA`ÌÍA`£×A`záA`QìA`(öA`  AZ{AYë
+AYÂAYAYp€AYG®AY
+žAXõÃAXÌÍAX£×AXzáAXQìAX(öAX  AW×
+AW®AW
+AW\)AW33AW
+=AVáHAVžRAV\AVffAV=qAV{AUë
+AUÂAUAUp€AUG®AU
+žATõÃATÌÍAT£×ATzáATQìAT(öAT  AS×
+AS®AS
+AS\)AS33AS
+=ARáHARžRAR\ARffAR=qAR{AQë
+AQÂAQAQp€AQG®AQ
+žAPõÃAPÌÍAP£×APzáAPQìAP(öAP  AJ{AIë
+AIÂAIAIp€AIG®AI
+žAHõÃAHÌÍAH£×AHzáAHQìAH(öAH  AG×
+AG®AG
+AG\)AG33AG
+=AFáHAFžRAF\AFffAF=qAF{AEë
+AEÂAEAEp€AEG®AE
+žADõÃADÌÍAD£×ADzáADQìAD(öAD  AC×
+AC®AC
+AC\)AC33AC
+=ABáHABžRAB\ABffAB=qAB{AAë
+AAÂAAAAp€AAG®AA
+žA@õÃA@ÌÍA@£×A@záA@QìA@(öA@  A:{A9ë
+A9ÂA9A9p€A9G®A9
+žA8õÃA8ÌÍA8£×A8záA8QìA8(öA8  A7×
+A7®A7
+A7\)A733A7
+=A6áHA6žRA6\A6ffA6=qA6{A5ë
+A5ÂA5A5p€A5G®A5
+žA4õÃA4ÌÍA4£×A4záA4QìA4(öA4  A3×
+A3®A3
+A3\)A333A3
+=A2áHA2žRA2\A2ffA2=qA2{A1ë
+A1ÂA1A1p€A1G®A1
+žA0õÃA0ÌÍA0£×A0záA0QìA0(öA0  A*{A)ë
+A)ÂA)A)p€A)G®A)
+žA(õÃA(ÌÍA(£×A(záA(QìA((öA(  A'×
+A'®A'
+A'\)A'33A'
+=A&áHA&žRA&\A&ffA&=qA&{A%ë
+A%ÂA%A%p€A%G®A%
+žA$õÃA$ÌÍA$£×A$záA$QìA$(öA$  A#×
+A#®A#
+A#\)A#33A#
+=A"áHA"žRA"\A"ffA"=qA"{A!ë
+A!ÂA!A!p€A!G®A!
+žA õÃA ÌÍA £×A záA QìA (öA   A{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A
+{A	ë
+A	ÂA	A	p€A	G®A	
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žA õÃA ÌÍA £×A záA QìA (öA   @ô(ö@ó×
+@ó
+@ó33@òáH@ò\@ò=q@ñë
+@ñ@ñG®@ðõÂ@ð£×@ðQì@ð  @ï®@ï\)@ï
+=@îžR@îff@î{@íÂ@íp€@í
+ž@ìÌÍ@ìzá@ì(ö@ë×
+@ë
+@ë33@êáH@ê\@ê=q@éë
+@é@éG®@èõÃ@è£×@èQì@è  @ç®@ç\)@ç
+=@æžR@æff@æ{@åÂ@åp€@å
+ž@äÌÍ@äzá@ä(ö@ã×
+@ã
+@ã33@âáH@â\@â=q@áë
+@á@áG®@àõÃ@à£×@àQì@à  @Ô(ö@Ó×
+@Ó
+@Ó33@ÒáH@Ò\@Ò=q@Ñë
+@Ñ@ÑG®@ÐõÂ@Ð£×@ÐQì@Ð  @Ï®@Ï\)@Ï
+=@ÎžR@Îff@Î{@ÍÂ@Íp€@Í
+ž@ÌÌÍ@Ìzá@Ì(ö@Ë×
+@Ë
+@Ë33@ÊáH@Ê\@Ê=q@Éë
+@É@ÉG®@ÈõÃ@È£×@ÈQì@È  @Ç®@Ç\)@Ç
+=@ÆžR@Æff@Æ{@ÅÂ@Åp€@Å
+ž@ÄÌÍ@Äzá@Ä(ö@Ã×
+@Ã
+@Ã33@ÂáH@Â\@Â=q@Áë
+@Á@ÁG®@ÀõÃ@À£×@ÀQì@À  @Ž(ö@³×
+@³
+@³33@²áH@²\@²=q@±ë
+@±@±G®@°õÂ@°£×@°Qì@°  @¯®@¯\)@¯
+=@®žR@®ff@®{@­Â@­p€@­
+ž@¬ÌÍ@¬zá@¬(ö@«×
+@«
+@«33@ªáH@ª\@ª=q@©ë
+@©@©G®@šõÃ@š£×@šQì@š  @§®@§\)@§
+=@ŠžR@Šff@Š{@¥Â@¥p€@¥
+ž@€ÌÍ@€zá@€(ö@£×
+@£
+@£33@¢áH@¢\@¢=q@¡ë
+@¡@¡G®@ õÃ@ £×@ Qì@   @(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÂ@£×@Qì@  @®@\)@
+=@žR@ff@{@Â@p€@
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @®@\)@
+=@žR@ff@{@
+Â@
+p€@
+
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @hQì@g®@g
+>@fff@eÂ@e
+ž@dzá@c×
+@c33@b\@aë
+@aG®@`£×@`  @_\)@^žR@^{@]p€@\ÌÍ@\(ö@[
+@ZáH@Z=q@Y@XõÂ@XQì@W®@W
+>@Vff@UÂ@U
+ž@Tzá@S×
+@S33@R\@Që
+@QG®@P£×@P  @O\)@NžR@N{@Mp€@LÌÍ@L(ö@K
+@JáH@J=q@I@HõÃ@HQì@G®@G
+=@Fff@EÂ@E
+ž@Dzá@C×
+@C33@B\@Aë
+@AG®@@£×@@  @(Qì@'®@'
+>@&ff@%Â@%
+ž@$zá@#×
+@#33@"\@!ë
+@!G®@ £×@   @\)@
+žR@
+{@
+p€@
+ÌÍ@
+(ö@
+@áH@=q@@õÂ@Qì@®@
+>@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@£×@  @\)@žR@{@
+p€@
+ÌÍ@
+(ö@
+
+@
+áH@
+=q@	@õÃ@Qì@®@
+=@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@ £×@   ?Ð£×?Ï\)?Î{?ÌÌÍ?Ë
+
+?Ê=p?ÈõÂ?Ç®?Æff?Å
+ž?Ã×
+?Â\?ÁG®?À  ?ŸžR?œp€?Œ(ö?ºáH?¹?žQì?·
+>?µÂ?Žzá?³33?±ë
+?°£×?¯\)?®{?¬ÌÍ?«
+?ª=q?šõÂ?§®?Šff?¥
+ž?£×
+?¢\?¡G®?   ?žR?p€?(ö?áH??Qì?
+>?Â?zá?33?ë
+?£×?\)?{?ÌÍ?
+?=q?õÃ?®?ff?
+
+ž?×
+?\?G®?  ?!G®?
+žR?
+(ö??
+=?zá?ë
+?\)?
+ÌÍ?
+=q?®?
+ž?\?   >úáH>õÂ>ð£×>ë
+>æff>áG®>Ü(ö>×
+=>Ñë
+>ÌÌÍ>Ç®>Â\>œp€>žQì>³33>®{>šõÃ>£×
+>žR>>zá>\)>=q>
+
+ž>  >uÂ>k
+>aG®>W
+=>LÌÍ>B\>8Qì>.{>#×
+>>\)>
+ž=õÂ=áG®=ÌÌÍ=žQì=£×
+=\)=uÂ=LÌÍ=#×
+<õÂ<£×
+<#×
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/fOut.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/fOut.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/fOut.fits	(revision 22322)
@@ -0,0 +1,8424 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B~
+B~záB~p€B~ffB~\)B~QìB~G®B~=qB~33B~(öB~
+žB~{B~
+=B~  B}õÃB}ë
+B}áHB}×
+B}ÌÍB}ÂB}žRB}®B}£×B}B}\B}
+B}záB}p€B}ffB}\)B}QìB}G®B}=qB}33B}(öB}
+žB}{B}
+=B}  B|õÃB|ë
+B|áHB|×
+B|ÌÍB|ÂB|žRB|®B|£×B|B|\B|
+B|záB|p€B|ffB|\)B|QìB|G®B|=qB|33B|(öB|
+žB|{B|
+=B|  Bz
+BzzáBzp€BzffBz\)BzQìBzG®Bz=qBz33Bz(öBz
+žBz{Bz
+=Bz  ByõÃByë
+ByáHBy×
+ByÌÍByÂByžRBy®By£×ByBy\By
+ByzáByp€ByffBy\)ByQìByG®By=qBy33By(öBy
+žBy{By
+=By  BxõÃBxë
+BxáHBx×
+BxÌÍBxÂBxžRBx®Bx£×BxBx\Bx
+BxzáBxp€BxffBx\)BxQìBxG®Bx=qBx33Bx(öBx
+žBx{Bx
+=Bx  Bv
+BvzáBvp€BvffBv\)BvQìBvG®Bv=qBv33Bv(öBv
+žBv{Bv
+=Bv  BuõÃBuë
+BuáHBu×
+BuÌÍBuÂBužRBu®Bu£×BuBu\Bu
+BuzáBup€BuffBu\)BuQìBuG®Bu=qBu33Bu(öBu
+žBu{Bu
+=Bu  BtõÃBtë
+BtáHBt×
+BtÌÍBtÂBtžRBt®Bt£×BtBt\Bt
+BtzáBtp€BtffBt\)BtQìBtG®Bt=qBt33Bt(öBt
+žBt{Bt
+=Bt  Br
+BrzáBrp€BrffBr\)BrQìBrG®Br=qBr33Br(öBr
+žBr{Br
+=Br  BqõÃBqë
+BqáHBq×
+BqÌÍBqÂBqžRBq®Bq£×BqBq\Bq
+BqzáBqp€BqffBq\)BqQìBqG®Bq=qBq33Bq(öBq
+žBq{Bq
+=Bq  BpõÃBpë
+BpáHBp×
+BpÌÍBpÂBpžRBp®Bp£×BpBp\Bp
+BpzáBpp€BpffBp\)BpQìBpG®Bp=qBp33Bp(öBp
+žBp{Bp
+=Bp  Bn
+BnzáBnp€BnffBn\)BnQìBnG®Bn=qBn33Bn(öBn
+žBn{Bn
+=Bn  BmõÃBmë
+BmáHBm×
+BmÌÍBmÂBmžRBm®Bm£×BmBm\Bm
+BmzáBmp€BmffBm\)BmQìBmG®Bm=qBm33Bm(öBm
+žBm{Bm
+=Bm  BlõÃBlë
+BláHBl×
+BlÌÍBlÂBlžRBl®Bl£×BlBl\Bl
+BlzáBlp€BlffBl\)BlQìBlG®Bl=qBl33Bl(öBl
+žBl{Bl
+=Bl  Bj
+BjzáBjp€BjffBj\)BjQìBjG®Bj=qBj33Bj(öBj
+žBj{Bj
+=Bj  BiõÃBië
+BiáHBi×
+BiÌÍBiÂBižRBi®Bi£×BiBi\Bi
+BizáBip€BiffBi\)BiQìBiG®Bi=qBi33Bi(öBi
+žBi{Bi
+=Bi  BhõÃBhë
+BháHBh×
+BhÌÍBhÂBhžRBh®Bh£×BhBh\Bh
+BhzáBhp€BhffBh\)BhQìBhG®Bh=qBh33Bh(öBh
+žBh{Bh
+=Bh  Bf
+BfzáBfp€BfffBf\)BfQìBfG®Bf=qBf33Bf(öBf
+žBf{Bf
+=Bf  BeõÃBeë
+BeáHBe×
+BeÌÍBeÂBežRBe®Be£×BeBe\Be
+BezáBep€BeffBe\)BeQìBeG®Be=qBe33Be(öBe
+žBe{Be
+=Be  BdõÃBdë
+BdáHBd×
+BdÌÍBdÂBdžRBd®Bd£×BdBd\Bd
+BdzáBdp€BdffBd\)BdQìBdG®Bd=qBd33Bd(öBd
+žBd{Bd
+=Bd  Bb
+BbzáBbp€BbffBb\)BbQìBbG®Bb=qBb33Bb(öBb
+žBb{Bb
+=Bb  BaõÃBaë
+BaáHBa×
+BaÌÍBaÂBažRBa®Ba£×BaBa\Ba
+BazáBap€BaffBa\)BaQìBaG®Ba=qBa33Ba(öBa
+žBa{Ba
+=Ba  B`õÃB`ë
+B`áHB`×
+B`ÌÍB`ÂB`žRB`®B`£×B`B`\B`
+B`záB`p€B`ffB`\)B`QìB`G®B`=qB`33B`(öB`
+žB`{B`
+=B`  B^
+B^záB^p€B^ffB^\)B^QìB^G®B^=qB^33B^(öB^
+žB^{B^
+=B^  B]õÃB]ë
+B]áHB]×
+B]ÌÍB]ÂB]žRB]®B]£×B]B]\B]
+B]záB]p€B]ffB]\)B]QìB]G®B]=qB]33B](öB]
+žB]{B]
+=B]  B\õÃB\ë
+B\áHB\×
+B\ÌÍB\ÂB\žRB\®B\£×B\B\\B\
+B\záB\p€B\ffB\\)B\QìB\G®B\=qB\33B\(öB\
+žB\{B\
+=B\  BZ
+BZzáBZp€BZffBZ\)BZQìBZG®BZ=qBZ33BZ(öBZ
+žBZ{BZ
+=BZ  BYõÃBYë
+BYáHBY×
+BYÌÍBYÂBYžRBY®BY£×BYBY\BY
+BYzáBYp€BYffBY\)BYQìBYG®BY=qBY33BY(öBY
+žBY{BY
+=BY  BXõÃBXë
+BXáHBX×
+BXÌÍBXÂBXžRBX®BX£×BXBX\BX
+BXzáBXp€BXffBX\)BXQìBXG®BX=qBX33BX(öBX
+žBX{BX
+=BX  BV
+BVzáBVp€BVffBV\)BVQìBVG®BV=qBV33BV(öBV
+žBV{BV
+=BV  BUõÃBUë
+BUáHBU×
+BUÌÍBUÂBUžRBU®BU£×BUBU\BU
+BUzáBUp€BUffBU\)BUQìBUG®BU=qBU33BU(öBU
+žBU{BU
+=BU  BTõÃBTë
+BTáHBT×
+BTÌÍBTÂBTžRBT®BT£×BTBT\BT
+BTzáBTp€BTffBT\)BTQìBTG®BT=qBT33BT(öBT
+žBT{BT
+=BT  BR
+BRzáBRp€BRffBR\)BRQìBRG®BR=qBR33BR(öBR
+žBR{BR
+=BR  BQõÃBQë
+BQáHBQ×
+BQÌÍBQÂBQžRBQ®BQ£×BQBQ\BQ
+BQzáBQp€BQffBQ\)BQQìBQG®BQ=qBQ33BQ(öBQ
+žBQ{BQ
+=BQ  BPõÃBPë
+BPáHBP×
+BPÌÍBPÂBPžRBP®BP£×BPBP\BP
+BPzáBPp€BPffBP\)BPQìBPG®BP=qBP33BP(öBP
+žBP{BP
+=BP  BN
+BNzáBNp€BNffBN\)BNQìBNG®BN=qBN33BN(öBN
+žBN{BN
+=BN  BMõÃBMë
+BMáHBM×
+BMÌÍBMÂBMžRBM®BM£×BMBM\BM
+BMzáBMp€BMffBM\)BMQìBMG®BM=qBM33BM(öBM
+žBM{BM
+=BM  BLõÃBLë
+BLáHBL×
+BLÌÍBLÂBLžRBL®BL£×BLBL\BL
+BLzáBLp€BLffBL\)BLQìBLG®BL=qBL33BL(öBL
+žBL{BL
+=BL  BJ
+BJzáBJp€BJffBJ\)BJQìBJG®BJ=qBJ33BJ(öBJ
+žBJ{BJ
+=BJ  BIõÃBIë
+BIáHBI×
+BIÌÍBIÂBIžRBI®BI£×BIBI\BI
+BIzáBIp€BIffBI\)BIQìBIG®BI=qBI33BI(öBI
+žBI{BI
+=BI  BHõÃBHë
+BHáHBH×
+BHÌÍBHÂBHžRBH®BH£×BHBH\BH
+BHzáBHp€BHffBH\)BHQìBHG®BH=qBH33BH(öBH
+žBH{BH
+=BH  BF
+BFzáBFp€BFffBF\)BFQìBFG®BF=qBF33BF(öBF
+žBF{BF
+=BF  BEõÃBEë
+BEáHBE×
+BEÌÍBEÂBEžRBE®BE£×BEBE\BE
+BEzáBEp€BEffBE\)BEQìBEG®BE=qBE33BE(öBE
+žBE{BE
+=BE  BDõÃBDë
+BDáHBD×
+BDÌÍBDÂBDžRBD®BD£×BDBD\BD
+BDzáBDp€BDffBD\)BDQìBDG®BD=qBD33BD(öBD
+žBD{BD
+=BD  BB
+BBzáBBp€BBffBB\)BBQìBBG®BB=qBB33BB(öBB
+žBB{BB
+=BB  BAõÃBAë
+BAáHBA×
+BAÌÍBAÂBAžRBA®BA£×BABA\BA
+BAzáBAp€BAffBA\)BAQìBAG®BA=qBA33BA(öBA
+žBA{BA
+=BA  B@õÃB@ë
+B@áHB@×
+B@ÌÍB@ÂB@žRB@®B@£×B@B@\B@
+B@záB@p€B@ffB@\)B@QìB@G®B@=qB@33B@(öB@
+žB@{B@
+=B@  B>
+B>záB>p€B>ffB>\)B>QìB>G®B>=qB>33B>(öB>
+žB>{B>
+=B>  B=õÃB=ë
+B=áHB=×
+B=ÌÍB=ÂB=žRB=®B=£×B=B=\B=
+B=záB=p€B=ffB=\)B=QìB=G®B==qB=33B=(öB=
+žB={B=
+=B=  B<õÃB<ë
+B<áHB<×
+B<ÌÍB<ÂB<žRB<®B<£×B<B<\B<
+B<záB<p€B<ffB<\)B<QìB<G®B<=qB<33B<(öB<
+žB<{B<
+=B<  B:
+B:záB:p€B:ffB:\)B:QìB:G®B:=qB:33B:(öB:
+žB:{B:
+=B:  B9õÃB9ë
+B9áHB9×
+B9ÌÍB9ÂB9žRB9®B9£×B9B9\B9
+B9záB9p€B9ffB9\)B9QìB9G®B9=qB933B9(öB9
+žB9{B9
+=B9  B8õÃB8ë
+B8áHB8×
+B8ÌÍB8ÂB8žRB8®B8£×B8B8\B8
+B8záB8p€B8ffB8\)B8QìB8G®B8=qB833B8(öB8
+žB8{B8
+=B8  B6
+B6záB6p€B6ffB6\)B6QìB6G®B6=qB633B6(öB6
+žB6{B6
+=B6  B5õÃB5ë
+B5áHB5×
+B5ÌÍB5ÂB5žRB5®B5£×B5B5\B5
+B5záB5p€B5ffB5\)B5QìB5G®B5=qB533B5(öB5
+žB5{B5
+=B5  B4õÃB4ë
+B4áHB4×
+B4ÌÍB4ÂB4žRB4®B4£×B4B4\B4
+B4záB4p€B4ffB4\)B4QìB4G®B4=qB433B4(öB4
+žB4{B4
+=B4  B2
+B2záB2p€B2ffB2\)B2QìB2G®B2=qB233B2(öB2
+žB2{B2
+=B2  B1õÃB1ë
+B1áHB1×
+B1ÌÍB1ÂB1žRB1®B1£×B1B1\B1
+B1záB1p€B1ffB1\)B1QìB1G®B1=qB133B1(öB1
+žB1{B1
+=B1  B0õÃB0ë
+B0áHB0×
+B0ÌÍB0ÂB0žRB0®B0£×B0B0\B0
+B0záB0p€B0ffB0\)B0QìB0G®B0=qB033B0(öB0
+žB0{B0
+=B0  B.
+B.záB.p€B.ffB.\)B.QìB.G®B.=qB.33B.(öB.
+žB.{B.
+=B.  B-õÃB-ë
+B-áHB-×
+B-ÌÍB-ÂB-žRB-®B-£×B-B-\B-
+B-záB-p€B-ffB-\)B-QìB-G®B-=qB-33B-(öB-
+žB-{B-
+=B-  B,õÃB,ë
+B,áHB,×
+B,ÌÍB,ÂB,žRB,®B,£×B,B,\B,
+B,záB,p€B,ffB,\)B,QìB,G®B,=qB,33B,(öB,
+žB,{B,
+=B,  B*
+B*záB*p€B*ffB*\)B*QìB*G®B*=qB*33B*(öB*
+žB*{B*
+=B*  B)õÃB)ë
+B)áHB)×
+B)ÌÍB)ÂB)žRB)®B)£×B)B)\B)
+B)záB)p€B)ffB)\)B)QìB)G®B)=qB)33B)(öB)
+žB){B)
+=B)  B(õÃB(ë
+B(áHB(×
+B(ÌÍB(ÂB(žRB(®B(£×B(B(\B(
+B(záB(p€B(ffB(\)B(QìB(G®B(=qB(33B((öB(
+žB({B(
+=B(  B&
+B&záB&p€B&ffB&\)B&QìB&G®B&=qB&33B&(öB&
+žB&{B&
+=B&  B%õÃB%ë
+B%áHB%×
+B%ÌÍB%ÂB%žRB%®B%£×B%B%\B%
+B%záB%p€B%ffB%\)B%QìB%G®B%=qB%33B%(öB%
+žB%{B%
+=B%  B$õÃB$ë
+B$áHB$×
+B$ÌÍB$ÂB$žRB$®B$£×B$B$\B$
+B$záB$p€B$ffB$\)B$QìB$G®B$=qB$33B$(öB$
+žB${B$
+=B$  B"
+B"záB"p€B"ffB"\)B"QìB"G®B"=qB"33B"(öB"
+žB"{B"
+=B"  B!õÃB!ë
+B!áHB!×
+B!ÌÍB!ÂB!žRB!®B!£×B!B!\B!
+B!záB!p€B!ffB!\)B!QìB!G®B!=qB!33B!(öB!
+žB!{B!
+=B!  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B	õÃB	ë
+B	áHB	×
+B	ÌÍB	ÂB	žRB	®B	£×B	B	\B	
+B	záB	p€B	ffB	\)B	QìB	G®B	=qB	33B	(öB	
+žB	{B	
+=B	  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   Aý
+=AüõÃAüáHAüÌÍAüžRAü£×Aü\AüzáAüffAüQìAü=qAü(öAü{Aü  Aûë
+Aû×
+AûÂAû®AûAû
+Aûp€Aû\)AûG®Aû33Aû
+žAû
+=AúõÃAúáHAúÌÍAúžRAú£×Aú\AúzáAúffAúQìAú=qAú(öAú{Aú  Aùë
+Aù×
+AùÂAù®AùAù
+Aùp€Aù\)AùG®Aù33Aù
+žAù
+=AøõÃAøáHAøÌÍAøžRAø£×Aø\AøzáAøffAøQìAø=qAø(öAø{Aø  Aõ
+=AôõÃAôáHAôÌÍAôžRAô£×Aô\AôzáAôffAôQìAô=qAô(öAô{Aô  Aóë
+Aó×
+AóÂAó®AóAó
+Aóp€Aó\)AóG®Aó33Aó
+žAó
+=AòõÃAòáHAòÌÍAòžRAò£×Aò\AòzáAòffAòQìAò=qAò(öAò{Aò  Añë
+Añ×
+AñÂAñ®AñAñ
+Añp€Añ\)AñG®Añ33Añ
+žAñ
+=AðõÃAðáHAðÌÍAðžRAð£×Að\AðzáAðffAðQìAð=qAð(öAð{Að  Aí
+=AìõÃAìáHAìÌÍAìžRAì£×Aì\AìzáAìffAìQìAì=qAì(öAì{Aì  Aëë
+Aë×
+AëÂAë®AëAë
+Aëp€Aë\)AëG®Aë33Aë
+žAë
+=AêõÃAêáHAêÌÍAêžRAê£×Aê\AêzáAêffAêQìAê=qAê(öAê{Aê  Aéë
+Aé×
+AéÂAé®AéAé
+Aép€Aé\)AéG®Aé33Aé
+žAé
+=AèõÃAèáHAèÌÍAèžRAè£×Aè\AèzáAèffAèQìAè=qAè(öAè{Aè  Aå
+=AäõÃAäáHAäÌÍAäžRAä£×Aä\AäzáAäffAäQìAä=qAä(öAä{Aä  Aãë
+Aã×
+AãÂAã®AãAã
+Aãp€Aã\)AãG®Aã33Aã
+žAã
+=AâõÃAâáHAâÌÍAâžRAâ£×Aâ\AâzáAâffAâQìAâ=qAâ(öAâ{Aâ  Aáë
+Aá×
+AáÂAá®AáAá
+Aáp€Aá\)AáG®Aá33Aá
+žAá
+=AàõÃAàáHAàÌÍAàžRAà£×Aà\AàzáAàffAàQìAà=qAà(öAà{Aà  AÝ
+=AÜõÃAÜáHAÜÌÍAÜžRAÜ£×AÜ\AÜzáAÜffAÜQìAÜ=qAÜ(öAÜ{AÜ  AÛë
+AÛ×
+AÛÂAÛ®AÛAÛ
+AÛp€AÛ\)AÛG®AÛ33AÛ
+žAÛ
+=AÚõÃAÚáHAÚÌÍAÚžRAÚ£×AÚ\AÚzáAÚffAÚQìAÚ=qAÚ(öAÚ{AÚ  AÙë
+AÙ×
+AÙÂAÙ®AÙAÙ
+AÙp€AÙ\)AÙG®AÙ33AÙ
+žAÙ
+=AØõÃAØáHAØÌÍAØžRAØ£×AØ\AØzáAØffAØQìAØ=qAØ(öAØ{AØ  AÕ
+=AÔõÃAÔáHAÔÌÍAÔžRAÔ£×AÔ\AÔzáAÔffAÔQìAÔ=qAÔ(öAÔ{AÔ  AÓë
+AÓ×
+AÓÂAÓ®AÓAÓ
+AÓp€AÓ\)AÓG®AÓ33AÓ
+žAÓ
+=AÒõÃAÒáHAÒÌÍAÒžRAÒ£×AÒ\AÒzáAÒffAÒQìAÒ=qAÒ(öAÒ{AÒ  AÑë
+AÑ×
+AÑÂAÑ®AÑAÑ
+AÑp€AÑ\)AÑG®AÑ33AÑ
+žAÑ
+=AÐõÃAÐáHAÐÌÍAÐžRAÐ£×AÐ\AÐzáAÐffAÐQìAÐ=qAÐ(öAÐ{AÐ  AÍ
+=AÌõÃAÌáHAÌÌÍAÌžRAÌ£×AÌ\AÌzáAÌffAÌQìAÌ=qAÌ(öAÌ{AÌ  AËë
+AË×
+AËÂAË®AËAË
+AËp€AË\)AËG®AË33AË
+žAË
+=AÊõÃAÊáHAÊÌÍAÊžRAÊ£×AÊ\AÊzáAÊffAÊQìAÊ=qAÊ(öAÊ{AÊ  AÉë
+AÉ×
+AÉÂAÉ®AÉAÉ
+AÉp€AÉ\)AÉG®AÉ33AÉ
+žAÉ
+=AÈõÃAÈáHAÈÌÍAÈžRAÈ£×AÈ\AÈzáAÈffAÈQìAÈ=qAÈ(öAÈ{AÈ  AÅ
+=AÄõÃAÄáHAÄÌÍAÄžRAÄ£×AÄ\AÄzáAÄffAÄQìAÄ=qAÄ(öAÄ{AÄ  AÃë
+AÃ×
+AÃÂAÃ®AÃAÃ
+AÃp€AÃ\)AÃG®AÃ33AÃ
+žAÃ
+=AÂõÃAÂáHAÂÌÍAÂžRAÂ£×AÂ\AÂzáAÂffAÂQìAÂ=qAÂ(öAÂ{AÂ  AÁë
+AÁ×
+AÁÂAÁ®AÁAÁ
+AÁp€AÁ\)AÁG®AÁ33AÁ
+žAÁ
+=AÀõÃAÀáHAÀÌÍAÀžRAÀ£×AÀ\AÀzáAÀffAÀQìAÀ=qAÀ(öAÀ{AÀ  Aœ
+=AŒõÃAŒáHAŒÌÍAŒžRAŒ£×AŒ\AŒzáAŒffAŒQìAŒ=qAŒ(öAŒ{AŒ  A»ë
+A»×
+A»ÂA»®A»A»
+A»p€A»\)A»G®A»33A»
+žA»
+=AºõÃAºáHAºÌÍAºžRAº£×Aº\AºzáAºffAºQìAº=qAº(öAº{Aº  A¹ë
+A¹×
+A¹ÂA¹®A¹A¹
+A¹p€A¹\)A¹G®A¹33A¹
+žA¹
+=AžõÃAžáHAžÌÍAžžRAž£×Až\AžzáAžffAžQìAž=qAž(öAž{Až  Aµ
+=AŽõÃAŽáHAŽÌÍAŽžRAŽ£×AŽ\AŽzáAŽffAŽQìAŽ=qAŽ(öAŽ{AŽ  A³ë
+A³×
+A³ÂA³®A³A³
+A³p€A³\)A³G®A³33A³
+žA³
+=A²õÃA²áHA²ÌÍA²žRA²£×A²\A²záA²ffA²QìA²=qA²(öA²{A²  A±ë
+A±×
+A±ÂA±®A±A±
+A±p€A±\)A±G®A±33A±
+žA±
+=A°õÃA°áHA°ÌÍA°žRA°£×A°\A°záA°ffA°QìA°=qA°(öA°{A°  A­
+=A¬õÃA¬áHA¬ÌÍA¬žRA¬£×A¬\A¬záA¬ffA¬QìA¬=qA¬(öA¬{A¬  A«ë
+A«×
+A«ÂA«®A«A«
+A«p€A«\)A«G®A«33A«
+žA«
+=AªõÃAªáHAªÌÍAªžRAª£×Aª\AªzáAªffAªQìAª=qAª(öAª{Aª  A©ë
+A©×
+A©ÂA©®A©A©
+A©p€A©\)A©G®A©33A©
+žA©
+=AšõÃAšáHAšÌÍAšžRAš£×Aš\AšzáAšffAšQìAš=qAš(öAš{Aš  A¥
+=A€õÃA€áHA€ÌÍA€žRA€£×A€\A€záA€ffA€QìA€=qA€(öA€{A€  A£ë
+A£×
+A£ÂA£®A£A£
+A£p€A£\)A£G®A£33A£
+žA£
+=A¢õÃA¢áHA¢ÌÍA¢žRA¢£×A¢\A¢záA¢ffA¢QìA¢=qA¢(öA¢{A¢  A¡ë
+A¡×
+A¡ÂA¡®A¡A¡
+A¡p€A¡\)A¡G®A¡33A¡
+žA¡
+=A õÃA áHA ÌÍA žRA £×A \A záA ffA QìA =qA (öA {A   A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Az{Ayë
+AyÂAyAyp€AyG®Ay
+žAxõÃAxÌÍAx£×AxzáAxQìAx(öAx  Aw×
+Aw®Aw
+Aw\)Aw33Aw
+=AváHAvžRAv\AvffAv=qAv{Auë
+AuÂAuAup€AuG®Au
+žAtõÃAtÌÍAt£×AtzáAtQìAt(öAt  As×
+As®As
+As\)As33As
+=AráHAržRAr\ArffAr=qAr{Aqë
+AqÂAqAqp€AqG®Aq
+žApõÃApÌÍAp£×ApzáApQìAp(öAp  Aj{Aië
+AiÂAiAip€AiG®Ai
+žAhõÃAhÌÍAh£×AhzáAhQìAh(öAh  Ag×
+Ag®Ag
+Ag\)Ag33Ag
+=AfáHAfžRAf\AfffAf=qAf{Aeë
+AeÂAeAep€AeG®Ae
+žAdõÃAdÌÍAd£×AdzáAdQìAd(öAd  Ac×
+Ac®Ac
+Ac\)Ac33Ac
+=AbáHAbžRAb\AbffAb=qAb{Aaë
+AaÂAaAap€AaG®Aa
+žA`õÃA`ÌÍA`£×A`záA`QìA`(öA`  AZ{AYë
+AYÂAYAYp€AYG®AY
+žAXõÃAXÌÍAX£×AXzáAXQìAX(öAX  AW×
+AW®AW
+AW\)AW33AW
+=AVáHAVžRAV\AVffAV=qAV{AUë
+AUÂAUAUp€AUG®AU
+žATõÃATÌÍAT£×ATzáATQìAT(öAT  AS×
+AS®AS
+AS\)AS33AS
+=ARáHARžRAR\ARffAR=qAR{AQë
+AQÂAQAQp€AQG®AQ
+žAPõÃAPÌÍAP£×APzáAPQìAP(öAP  AJ{AIë
+AIÂAIAIp€AIG®AI
+žAHõÃAHÌÍAH£×AHzáAHQìAH(öAH  AG×
+AG®AG
+AG\)AG33AG
+=AFáHAFžRAF\AFffAF=qAF{AEë
+AEÂAEAEp€AEG®AE
+žADõÃADÌÍAD£×ADzáADQìAD(öAD  AC×
+AC®AC
+AC\)AC33AC
+=ABáHABžRAB\ABffAB=qAB{AAë
+AAÂAAAAp€AAG®AA
+žA@õÃA@ÌÍA@£×A@záA@QìA@(öA@  A:{A9ë
+A9ÂA9A9p€A9G®A9
+žA8õÃA8ÌÍA8£×A8záA8QìA8(öA8  A7×
+A7®A7
+A7\)A733A7
+=A6áHA6žRA6\A6ffA6=qA6{A5ë
+A5ÂA5A5p€A5G®A5
+žA4õÃA4ÌÍA4£×A4záA4QìA4(öA4  A3×
+A3®A3
+A3\)A333A3
+=A2áHA2žRA2\A2ffA2=qA2{A1ë
+A1ÂA1A1p€A1G®A1
+žA0õÃA0ÌÍA0£×A0záA0QìA0(öA0  A*{A)ë
+A)ÂA)A)p€A)G®A)
+žA(õÃA(ÌÍA(£×A(záA(QìA((öA(  A'×
+A'®A'
+A'\)A'33A'
+=A&áHA&žRA&\A&ffA&=qA&{A%ë
+A%ÂA%A%p€A%G®A%
+žA$õÃA$ÌÍA$£×A$záA$QìA$(öA$  A#×
+A#®A#
+A#\)A#33A#
+=A"áHA"žRA"\A"ffA"=qA"{A!ë
+A!ÂA!A!p€A!G®A!
+žA õÃA ÌÍA £×A záA QìA (öA   A{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A
+{A	ë
+A	ÂA	A	p€A	G®A	
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žA õÃA ÌÍA £×A záA QìA (öA   @ô(ö@ó×
+@ó
+@ó33@òáH@ò\@ò=q@ñë
+@ñ@ñG®@ðõÂ@ð£×@ðQì@ð  @ï®@ï\)@ï
+=@îžR@îff@î{@íÂ@íp€@í
+ž@ìÌÍ@ìzá@ì(ö@ë×
+@ë
+@ë33@êáH@ê\@ê=q@éë
+@é@éG®@èõÃ@è£×@èQì@è  @ç®@ç\)@ç
+=@æžR@æff@æ{@åÂ@åp€@å
+ž@äÌÍ@äzá@ä(ö@ã×
+@ã
+@ã33@âáH@â\@â=q@áë
+@á@áG®@àõÃ@à£×@àQì@à  @Ô(ö@Ó×
+@Ó
+@Ó33@ÒáH@Ò\@Ò=q@Ñë
+@Ñ@ÑG®@ÐõÂ@Ð£×@ÐQì@Ð  @Ï®@Ï\)@Ï
+=@ÎžR@Îff@Î{@ÍÂ@Íp€@Í
+ž@ÌÌÍ@Ìzá@Ì(ö@Ë×
+@Ë
+@Ë33@ÊáH@Ê\@Ê=q@Éë
+@É@ÉG®@ÈõÃ@È£×@ÈQì@È  @Ç®@Ç\)@Ç
+=@ÆžR@Æff@Æ{@ÅÂ@Åp€@Å
+ž@ÄÌÍ@Äzá@Ä(ö@Ã×
+@Ã
+@Ã33@ÂáH@Â\@Â=q@Áë
+@Á@ÁG®@ÀõÃ@À£×@ÀQì@À  @Ž(ö@³×
+@³
+@³33@²áH@²\@²=q@±ë
+@±@±G®@°õÂ@°£×@°Qì@°  @¯®@¯\)@¯
+=@®žR@®ff@®{@­Â@­p€@­
+ž@¬ÌÍ@¬zá@¬(ö@«×
+@«
+@«33@ªáH@ª\@ª=q@©ë
+@©@©G®@šõÃ@š£×@šQì@š  @§®@§\)@§
+=@ŠžR@Šff@Š{@¥Â@¥p€@¥
+ž@€ÌÍ@€zá@€(ö@£×
+@£
+@£33@¢áH@¢\@¢=q@¡ë
+@¡@¡G®@ õÃ@ £×@ Qì@   @(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÂ@£×@Qì@  @®@\)@
+=@žR@ff@{@Â@p€@
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @®@\)@
+=@žR@ff@{@
+Â@
+p€@
+
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @hQì@g®@g
+>@fff@eÂ@e
+ž@dzá@c×
+@c33@b\@aë
+@aG®@`£×@`  @_\)@^žR@^{@]p€@\ÌÍ@\(ö@[
+@ZáH@Z=q@Y@XõÂ@XQì@W®@W
+>@Vff@UÂ@U
+ž@Tzá@S×
+@S33@R\@Që
+@QG®@P£×@P  @O\)@NžR@N{@Mp€@LÌÍ@L(ö@K
+@JáH@J=q@I@HõÃ@HQì@G®@G
+=@Fff@EÂ@E
+ž@Dzá@C×
+@C33@B\@Aë
+@AG®@@£×@@  @(Qì@'®@'
+>@&ff@%Â@%
+ž@$zá@#×
+@#33@"\@!ë
+@!G®@ £×@   @\)@
+žR@
+{@
+p€@
+ÌÍ@
+(ö@
+@áH@=q@@õÂ@Qì@®@
+>@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@£×@  @\)@žR@{@
+p€@
+ÌÍ@
+(ö@
+
+@
+áH@
+=q@	@õÃ@Qì@®@
+=@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@ £×@   ?Ð£×?Ï\)?Î{?ÌÌÍ?Ë
+
+?Ê=p?ÈõÂ?Ç®?Æff?Å
+ž?Ã×
+?Â\?ÁG®?À  ?ŸžR?œp€?Œ(ö?ºáH?¹?žQì?·
+>?µÂ?Žzá?³33?±ë
+?°£×?¯\)?®{?¬ÌÍ?«
+?ª=q?šõÂ?§®?Šff?¥
+ž?£×
+?¢\?¡G®?   ?žR?p€?(ö?áH??Qì?
+>?Â?zá?33?ë
+?£×?\)?{?ÌÍ?
+?=q?õÃ?®?ff?
+
+ž?×
+?\?G®?  ?!G®?
+žR?
+(ö??
+=?zá?ë
+?\)?
+ÌÍ?
+=q?®?
+ž?\?   >úáH>õÂ>ð£×>ë
+>æff>áG®>Ü(ö>×
+=>Ñë
+>ÌÌÍ>Ç®>Â\>œp€>žQì>³33>®{>šõÃ>£×
+>žR>>zá>\)>=q>
+
+ž>  >uÂ>k
+>aG®>W
+=>LÌÍ>B\>8Qì>.{>#×
+>>\)>
+ž=õÂ=áG®=ÌÌÍ=žQì=£×
+=\)=uÂ=LÌÍ=#×
+<õÂ<£×
+<#×
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bz
+B~zá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bv
+BzzáBzzáB~p€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bn
+BrzáBvzáBzp€BzffB~\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bj
+BnzáBnzáBrp€BvffBz\)Bz\)B~Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bf
+BfzáBjzáBnp€BnffBr\)Bv\)BzQìBzG®B~G®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B^
+BbzáBfzáBfp€BjffBn\)Bn\)BrQìBvG®BzG®Bz=qB~33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BZ
+BZzáB^záBbp€BfffBf\)Bj\)BnQìBnG®BrG®Bv=qBz33Bz(öB~(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BR
+BVzáBZzáBZp€B^ffBb\)Bf\)BfQìBjG®BnG®Bn=qBr33Bv(öBz(öBz
+žB~{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BN
+BRzáBRzáBVp€BZffBZ\)B^\)BbQìBfG®BfG®Bj=qBn33Bn(öBr(öBv
+žBz{Bz{B~
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BJ
+BJzáBNzáBRp€BRffBV\)BZ\)BZQìB^G®BbG®Bf=qBf33Bj(öBn(öBn
+žBr{Bv{Bz
+=Bz  B~  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BB
+BFzáBJzáBJp€BNffBR\)BR\)BVQìBZG®BZG®B^=qBb33Bf(öBf(öBj
+žBn{Bn{Br
+=Bv  Bz  ByõÃB}ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B>
+B>záBBzáBFp€BJffBJ\)BN\)BRQìBRG®BVG®BZ=qBZ33B^(öBb(öBf
+žBf{Bj{Bn
+=Bn  Br  BuõÃByë
+ByáHB}áH¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B6
+B:záB>záB>p€BBffBF\)BJ\)BJQìBNG®BRG®BR=qBV33BZ(öBZ(öB^
+žBb{Bf{Bf
+=Bj  Bn  BmõÃBqë
+BuáHByáHBy×
+B}ÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B2
+B6záB6záB:p€B>ffB>\)BB\)BFQìBJG®BJG®BN=qBR33BR(öBV(öBZ
+žBZ{B^{Bb
+=Bf  Bf  BiõÃBmë
+BmáHBqáHBu×
+ByÌÍByÌÍB}Â¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B*
+B.záB2záB6p€B6ffB:\)B>\)B>QìBBG®BFG®BJ=qBJ33BN(öBR(öBR
+žBV{BZ{BZ
+=B^  Bb  BeõÃBeë
+BiáHBmáHBm×
+BqÌÍBuÌÍByÂByžRB}®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B&
+B*záB*záB.p€B2ffB6\)B6\)B:QìB>G®B>G®BB=qBF33BJ(öBJ(öBN
+žBR{BR{BV
+=BZ  BZ  B]õÃBaë
+BeáHBeáHBi×
+BmÌÍBmÌÍBqÂBužRBy®By®B}£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B"
+B"záB&záB*p€B*ffB.\)B2\)B6QìB6G®B:G®B>=qB>33BB(öBF(öBJ
+žBJ{BN{BR
+=BR  BV  BYõÃBYë
+B]áHBaáHBe×
+BeÌÍBiÌÍBmÂBmžRBq®Bu®By£×ByB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+B
+záB"záB"p€B&ffB*\)B*\)B.QìB2G®B6G®B6=qB:33B>(öB>(öBB
+žBF{BJ{BJ
+=BN  BR  BQõÃBUë
+BYáHBYáHB]×
+BaÌÍBeÌÍBeÂBižRBm®Bm®Bq£×BuByBy\B}
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáBzáB
+p€B"ffB"\)B&\)B*QìB*G®B.G®B2=qB633B6(öB:(öB>
+žB>{BB{BF
+=BJ  BJ  BMõÃBQë
+BQáHBUáHBY×
+BYÌÍB]ÌÍBaÂBežRBe®Bi®Bm£×BmBqBu\By
+By
+B}zá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáBzáBp€BffB
+\)B"\)B"QìB&G®B*G®B*=qB.33B2(öB6(öB6
+žB:{B>{B>
+=BB  BF  BIõÃBIë
+BMáHBQáHBQ×
+BUÌÍBYÌÍBYÂB]žRBa®Be®Be£×BiBmBm\Bq
+Bu
+ByzáByp€B}ff¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+
+BzáBzáBp€BffB\)B\)B
+QìB"G®B"G®B&=qB*33B*(öB.(öB2
+žB6{B6{B:
+=B>  B>  BAõÃBEë
+BIáHBIáHBM×
+BQÌÍBQÌÍBUÂBYžRBY®B]®Ba£×BeBeBi\Bm
+Bm
+BqzáBup€ByffByffB}\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáB
+záBp€BffB\)B\)BQìBG®B
+G®B"=qB"33B&(öB*(öB*
+žB.{B2{B6
+=B6  B:  B=õÃB=ë
+BAáHBEáHBI×
+BIÌÍBMÌÍBQÂBQžRBU®BY®BY£×B]BaBe\Be
+Bi
+BmzáBmp€BqffBuffBy\)ByQìB}Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aý
+=BzáBzáBp€B
+ffB\)B\)BQìBG®BG®B=qB
+33B"(öB"(öB&
+žB*{B*{B.
+=B2  B6  B5õÃB9ë
+B=áHB=áHBA×
+BEÌÍBIÌÍBIÂBMžRBQ®BQ®BU£×BYBYB]\Ba
+Be
+BezáBip€BmffBmffBq\)BuQìByQìByG®B}=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aõ
+=AôõÃAüõÃBp€BffB\)B
+\)BQìBG®BG®B=qB33B(öB
+(öB"
+žB"{B&{B*
+=B*  B.  B1õÃB5ë
+B5áHB9áHB=×
+B=ÌÍBAÌÍBEÂBIžRBI®BM®BQ£×BQBUBY\BY
+B]
+BazáBep€BeffBiffBm\)BmQìBqQìBuG®By=qBy33B}33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aå
+=AìõÃAôõÃAôáHAüÌÍB\)B\)BQìB
+G®BG®B=qB33B(öB(öB
+žB
+{B"{B"
+=B&  B*  B)õÃB-ë
+B1áHB5áHB5×
+B9ÌÍB=ÌÍB=ÂBAžRBE®BI®BI£×BMBQBQ\BU
+BY
+BYzáB]p€BaffBeffBe\)BiQìBmQìBmG®Bq=qBu33By33By(öB}
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÝ
+=AäõÃAäõÃAìáHAôÌÍAôžRAüžRBQìBG®BG®B
+=qB33B(öB(öB
+žB{B{B
+
+=B"  B"  B%õÃB)ë
+B)áHB-áHB1×
+B5ÌÍB5ÌÍB9ÂB=žRB=®BA®BE£×BIBIBM\BQ
+BQ
+BUzáBYp€BYffB]ffBa\)BeQìBeQìBiG®Bm=qBm33Bq33Bu(öBy
+žBy
+žB}{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÍ
+=AÔõÃAÜõÃAäáHAäÌÍAìžRAôžRAô£×Aü\BG®B=qB33B
+(öB(öB
+žB{B{B
+=B  B
+  B!õÃB!ë
+B%áHB)áHB)×
+B-ÌÍB1ÌÍB5ÂB5žRB9®B=®B=£×BABEBI\BI
+BM
+BQzáBQp€BUffBYffBY\)B]QìBaQìBeG®Be=qBi33Bm33Bm(öBq
+žBu
+žBy{By
+=B}  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÅ
+=AÌõÃAÌõÃAÔáHAÜÌÍAäžRAäžRAì£×Aô\Aô\AüzáB33B(öB(öB
+
+žB{B{B
+=B  B  BõÃB
+ë
+B!áHB!áHB%×
+B)ÌÍB)ÌÍB-ÂB1žRB5®B5®B9£×B=B=BA\BE
+BI
+BIzáBMp€BQffBQffBU\)BYQìBYQìB]G®Ba=qBe33Be33Bi(öBm
+žBm
+žBq{Bu
+=By  By  B|õÃ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aœ
+=AŒõÃAÄõÃAÌáHAÌÌÍAÔžRAÜžRAä£×Aä\Aì\AôzáAôffAüQìB(öB
+žB{B
+{B
+=B  B  BõÃBë
+BáHB
+áHB!×
+B!ÌÍB%ÌÍB)ÂB)žRB-®B1®B5£×B5B9B=\B=
+BA
+BEzáBIp€BIffBMffBQ\)BQQìBUQìBYG®BY=qB]33Ba33Be(öBe
+žBi
+žBm{Bm
+=Bq  Bu  BxõÃBxë
+B|ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A­
+=AŽõÃAŒõÃAŒáHAÄÌÍAÌžRAÌžRAÔ£×AÜ\Aä\AäzáAìffAôQìAôQìAü=qB{B{B
+=B
+  B  B
+õÃBë
+BáHBáHB×
+B
+ÌÍB!ÌÍB!ÂB%žRB)®B)®B-£×B1B5B5\B9
+B=
+B=záBAp€BEffBIffBI\)BMQìBQQìBQG®BU=qBY33BY33B](öBa
+žBe
+žBe{Bi
+=Bm  Bm  BpõÃBtë
+Bxë
+BxáHB|×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A¥
+=A¬õÃA¬õÃAŽáHAŒÌÍAŒžRAÄžRAÌ£×AÌ\AÔ\AÜzáAäffAäQìAìQìAô=qAô(öAü(öB
+=B  B  B	õÃB
+ë
+B
+áHBáHB×
+BÌÍBÌÍB
+ÂB!žRB!®B%®B)£×B)B-B1\B5
+B5
+B9záB=p€B=ffBAffBE\)BIQìBIQìBMG®BQ=qBQ33BU33BY(öBY
+žB]
+žBa{Be
+=Be  Bi  BlõÃBlë
+Bpë
+BtáHBx×
+Bx×
+B|ÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+=AõÃA€õÃA¬áHA¬ÌÍAŽžRAŒžRAŒ£×AÄ\AÌ\AÌzáAÔffAÜQìAäQìAä=qAì(öAô(öAô{Aü  B  BõÃBë
+B	áHB
+áHB
+×
+BÌÍBÌÍBÂBžRB
+®B!®B!£×B%B)B)\B-
+B1
+B5záB5p€B9ffB=ffB=\)BAQìBEQìBIG®BI=qBM33BQ33BQ(öBU
+žBY
+žBY{B]
+=Ba  Be  BdõÃBhë
+Blë
+BláHBp×
+Bt×
+BxÌÍBxÂB|žR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+=AõÃAõÃAáHA€ÌÍA¬žRA¬žRAŽ£×AŒ\AŒ\AÄzáAÌffAÌQìAÔQìAÜ=qAä(öAä(öAì{Aô  Aô  Aûë
+Bë
+BáHBáHB	×
+B
+ÌÍB
+ÌÍBÂBžRB®B®B
+£×B!B!B%\B)
+B)
+B-záB1p€B5ffB5ffB9\)B=QìB=QìBAG®BE=qBI33BI33BM(öBQ
+žBQ
+žBU{BY
+=BY  B]  B`õÃBdë
+Bdë
+BháHBl×
+Bl×
+BpÌÍBtÂBxžRBxžRB|®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+
+=AõÃAõÃAáHAÌÍAžRA€žRA¬£×A¬\AŽ\AŒzáAŒffAÄQìAÌQìAÌ=qAÔ(öAÜ(öAä{Aä  Aì  Aóë
+Aó×
+AûÂBáHB×
+BÌÍB	ÌÍB
+ÂB
+žRB®B®B£×BB
+B!\B!
+B%
+B)záB)p€B-ffB1ffB5\)B5QìB9QìB=G®B==qBA33BE33BI(öBI
+žBM
+žBQ{BQ
+=BU  BY  BXõÃB\ë
+B`ë
+BdáHBd×
+Bh×
+BlÌÍBlÂBpžRBtžRBx®Bx£×B|£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aj{Ayë
+AõÃAáHAÌÍAžRAžRA£×A€\A¬\A¬záAŽffAŒQìAŒQìAÄ=qAÌ(öAÌ(öAÔ{AÜ  Aä  Aãë
+Aë×
+AóÂAóÂAû®BÌÍBÌÍBÂB	žRB
+®B
+®B£×BBB\B
+
+B!
+B!záB%p€B)ffB)ffB-\)B1QìB5QìB5G®B9=qB=33B=33BA(öBE
+žBI
+žBI{BM
+=BQ  BQ  BTõÃBXë
+BXë
+B\áHB`×
+Bd×
+BdÌÍBhÂBlžRBlžRBp®Bt£×Bx£×BxB|\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AZ{AYë
+Aië
+AyÂAÌÍAžRAžRA£×A\A\A€záA¬ffA¬QìAŽQìAŒ=qAŒ(öAÄ(öAÌ{AÌ  AÔ  AÛë
+Aã×
+AãÂAëÂAó®AóAûBÂBžRB®B	®B
+£×B
+BB\B
+B
+B
+záB!p€B!ffB%ffB)\)B)QìB-QìB1G®B5=qB533B933B=(öB=
+žBA
+žBE{BI
+=BI  BM  BPõÃBPë
+BTë
+BXáHBX×
+B\×
+B`ÌÍBdÂBdžRBhžRBl®Bl£×Bp£×BtBx\Bx
+B|
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A:{AIë
+AYë
+AYÂAiAyp€AžRA£×A\A\AzáAffA€QìA¬QìA¬=qAŽ(öAŒ(öAŒ{AÄ  AÌ  AËë
+AÓ×
+AÛÂAãÂAã®AëAóAó
+Aûp€B®B®B£×B	B
+B
+\B
+B
+BzáBp€B
+ffB!ffB!\)B%QìB)QìB)G®B-=qB133B533B5(öB9
+žB=
+žB={BA
+=BE  BI  BHõÃBLë
+BPë
+BPáHBT×
+BX×
+BXÌÍB\ÂB`žRBdžRBd®Bh£×Bl£×BlBp\Bt
+Bx
+BxzáB|p€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A*{A9ë
+A9ë
+AIÂAYAYp€Aip€AyG®A\A\AzáAffAQìAQìA€=qA¬(öA¬(öAŽ{AŒ  AŒ  AÃë
+AË×
+AËÂAÓÂAÛ®AãAãAë
+Aóp€Aó\)Aû\)B£×BBB	\B
+
+B
+
+BzáBp€BffBffB
+\)B!QìB!QìB%G®B)=qB)33B-33B1(öB5
+žB5
+žB9{B=
+=B=  BA  BDõÃBHë
+BHë
+BLáHBP×
+BP×
+BTÌÍBXÂBXžRB\žRB`®Bd£×Bd£×BhBl\Bl
+Bp
+BtzáBxp€Bxp€B|ff¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A{Aë
+A)ë
+A9ÂA9AIp€AYp€AYG®Ai
+žAy
+žAzáAffAQìAQìA=qA(öA€(öA¬{A¬  AŽ  A»ë
+A»×
+AÃÂAËÂAË®AÓAÛAã
+Aãp€Aë\)Aó\)AóG®Aû33BB\B
+B	
+B
+záB
+p€BffBffB\)BQìB
+QìB!G®B!=qB%33B)33B)(öB-
+žB1
+žB5{B5
+=B9  B=  B<õÃB@ë
+BDë
+BHáHBH×
+BL×
+BPÌÍBPÂBTžRBXžRBX®B\£×B`£×BdBd\Bh
+Bl
+BlzáBpp€Btp€BxffBx\)B|\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ô(öA	ë
+Aë
+AÂA)A9p€A9p€AIG®AY
+žAY
+žAhõÃAxÌÍAQìAQìA=qA(öA(öA{A€  A¬  A«ë
+A³×
+A»ÂA»ÂAÃ®AËAËAÓ
+AÛp€Aã\)Aã\)AëG®Aó33Aó33Aû
+žB
+B
+BzáB	p€B
+ffB
+ffB\)BQìBQìBG®B
+=qB!33B!33B%(öB)
+žB)
+žB-{B1
+=B5  B5  B8õÃB<ë
+B<ë
+B@áHBD×
+BH×
+BHÌÍBLÂBPžRBPžRBT®BX£×BX£×B\B`\Bd
+Bd
+BhzáBlp€Blp€BpffBt\)Bx\)BxQìB|G®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @Ô(ö@Ó×
+@ó×
+A	ÂAAp€A)p€A9G®A9
+žAI
+žAXõÃAXÌÍAh£×Ax£×A=qA(öA(öA{A  A  A£ë
+A«×
+A«ÂA³ÂA»®A»AÃAË
+AËp€AÓ\)AÛ\)AãG®Aã33Aë33Aó
+žAó
+=Aû
+=BzáBp€BffB	ffB
+\)B
+QìBQìBG®B=qB33B
+33B!(öB!
+žB%
+žB){B)
+=B-  B1  B4õÃB4ë
+B8ë
+B<áHB<×
+B@×
+BDÌÍBHÂBHžRBLžRBP®BP£×BT£×BXBX\B\
+B`
+BdzáBdp€Bhp€BlffBl\)Bp\)BtQìBxG®Bx=qB|=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @(ö@³×
+@Ó×
+@Ó
+@ó33A	p€Ap€AG®A)
+žA9
+žA8õÃAHÌÍAX£×AX£×AhzáAxQìA(öA{A  A  Aë
+A×
+A£ÂA«ÂA«®A³A»A»
+AÃp€AË\)AË\)AÓG®AÛ33Aã33Aã
+žAë
+=Aó
+=AòõÃAúáHBffBffB\)B	QìB
+QìB
+G®B=qB33B33B(öB
+
+žB!
+žB!{B%
+=B)  B)  B,õÃB0ë
+B4ë
+B4áHB8×
+B<×
+B<ÌÍB@ÂBDžRBHžRBH®BL£×BP£×BPBT\BX
+BX
+B\záB`p€Bdp€BdffBh\)Bl\)BlQìBpG®Bt=qBx=qBx33B|(ö¿  ¿  ¿  ¿  ¿  ¿  @hQì@×
+@×
+@³
+@Ó33@ÒáH@òáHA	G®A
+žA
+žA(õÃA8ÌÍA8£×AH£×AXzáAXQìAhQìAx(öA  A  Aë
+A×
+AÂAÂA£®A«A«A³
+A»p€A»\)AÃ\)AËG®AË33AÓ33AÛ
+žAã
+=Aã
+=AêõÃAòáHAòÌÍAúÌÍB\)BQìBQìB	G®B
+=qB
+33B33B(öB
+žB
+žB
+{B!
+=B!  B%  B(õÃB(ë
+B,ë
+B0áHB4×
+B4×
+B8ÌÍB<ÂB<žRB@žRBD®BH£×BH£×BLBP\BP
+BT
+BXzáBXp€B\p€B`ffBd\)Bd\)BhQìBlG®Bl=qBp=qBt33Bx(öBx(öB|
+ž¿  ¿  ¿  ¿  ?Ð£×@'®@g®@
+@33@²áH@ÒáH@Ò\@ò=qA	
+žAõÃAÌÍA(£×A8£×A8záAHQìAXQìAX(öAh  Ax  Aë
+A×
+AÂAÂA®AA£A«
+A«p€A³\)A»\)A»G®AÃ33AË33AË
+žAÓ
+=AÛ
+=AâõÃAâáHAêÌÍAòÌÍAòžRAú£×BQìBG®B=qB	33B
+33B
+(öB
+žB
+žB{B
+=B
+  B!  B õÃB$ë
+B(ë
+B(áHB,×
+B0×
+B4ÌÍB4ÂB8žRB<žRB<®B@£×BD£×BHBH\BL
+BP
+BPzáBTp€BXp€BXffB\\)B`\)BdQìBdG®Bh=qBl=qBl33Bp(öBt(öBx
+žBx{B|
+=¿  ¿  ?!G®?Ï\)?Ï\)@'
+>@fff@áH@áH@²\@Ò=q@Ò=q@ñë
+AÌÍA£×A£×A(záA8QìA8QìAH(öAX  AX  Ag×
+Aw®AÂAÂA®AAA
+A£p€A«\)A«\)A³G®A»33A»33AÃ
+žAË
+=AË
+=AÒõÃAÚáHAâÌÍAâÌÍAêžRAò£×Aò£×Aú\B=qB33B33B	(öB
+
+žB
+
+žB{B
+=B  B  B
+õÃB ë
+B ë
+B$áHB(×
+B(×
+B,ÌÍB0ÂB4žRB4žRB8®B<£×B<£×B@BD\BH
+BH
+BLzáBPp€BPp€BTffBX\)BX\)B\QìB`G®Bd=qBd=qBh33Bl(öBl(öBp
+žBt{Bx
+=Bx
+=B|  ¿  ¿  ?
+žR?Î{?ÌÌÍ@%Â@eÂ@\@=q@²=q@Ñë
+@Ñ@ñG®A£×AzáAQìA(QìA8(öA8  AH  AW×
+AW®Ag
+Aw
+A®AAA
+Ap€A\)A£\)A«G®A«33A³33A»
+žA»
+=AÃ
+=AÊõÃAÊáHAÒÌÍAÚÌÍAâžRAâ£×Aê£×Aò\AòzáAúffB33B(öB
+žB	
+žB
+{B
+=B  B  BõÃBë
+B
+ë
+B áHB ×
+B$×
+B(ÌÍB(ÂB,žRB0žRB4®B4£×B8£×B<B<\B@
+BD
+BHzáBHp€BLp€BPffBP\)BT\)BXQìBXG®B\=qB`=qBd33Bd(öBh(öBl
+žBl{Bp
+=Bt
+=Bx  ¿  ¿  ¿  ¿  ??Ë
+
+?Ë
+
+@%
+ž@dzá@=q@ë
+@±@ÑG®@ÑG®@ðõÂAQìAQìA(öA(  A8  A7×
+AG®AW
+AW
+Ag\)Aw33AA
+Ap€A\)A\)AG®A£33A«33A«
+žA³
+=A»
+=AºõÃAÂáHAÊÌÍAÊÌÍAÒžRAÚ£×Aâ£×Aâ\AêzáAòffAòffAúQìB
+žB
+žB{B	
+=B
+  B
+  BõÃBë
+Bë
+BáHB
+×
+B ×
+B ÌÍB$ÂB(žRB(žRB,®B0£×B4£×B4B8\B<
+B<
+B@záBDp€BHp€BHffBL\)BP\)BPQìBTG®BX=qBX=qB\33B`(öBd(öBd
+žBh{Bl
+=Bl
+=Bp  ¿  ¿  ¿  ¿  ¿  ¿  ?
+=?Ê=p?ÈõÂ@$zá@c×
+@@G®@±G®@ÐõÂ@Ð£×@ð£×A(öA  A  A'×
+A7®A7
+AG
+AW\)AW33Ag33Aw
+=Ap€A\)A\)AG®A33A33A£
+žA«
+=A«
+=A²õÃAºáHAºÌÍAÂÌÍAÊžRAÊ£×AÒ£×AÚ\AâzáAâffAêffAòQìAò=qAú=qB{B
+=B  B	  B
+õÃB
+ë
+Bë
+BáHB×
+B×
+B
+ÌÍB ÂB žRB$žRB(®B(£×B,£×B0B4\B4
+B8
+B<záB<p€B@p€BDffBH\)BH\)BLQìBPG®BP=qBT=qBX33BX(öB\(öB`
+žBd{Bd
+=Bh
+=Bl  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?ë
+?ÈõÂ?Ç®@#33@b\@G®@õÂ@°£×@Ð£×@ÐQì@ð  A  A×
+A®A'
+A7
+A7\)AG33AW33AW
+=AfáHAvžRA\)AG®A33A33A
+žA
+=A£
+=AªõÃAªáHA²ÌÍAºÌÍAºžRAÂ£×AÊ£×AÊ\AÒzáAÚffAâffAâQìAê=qAò=qAò(öAú{B  B  BõÃBë
+B
+ë
+B
+áHB×
+B×
+BÌÍBÂB
+žRB žRB ®B$£×B(£×B(B,\B0
+B4
+B4záB8p€B<p€B<ffB@\)BD\)BHQìBHG®BL=qBP=qBP33BT(öBX(öBX
+žB\{B`
+=Bd
+=Bd  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?\)?Æff?Å
+ž@"\@aë
+@£×@£×@°Qì@Ð  @Ð  @ï®A®A
+A
+A'\)A733A733AG
+=AVáHAVžRAfžRAv\A33A33A
+žA
+=A
+=AõÃA¢áHAªÌÍAªÌÍA²žRAº£×Aº£×AÂ\AÊzáAÊffAÒffAÚQìAâ=qAâ=qAê(öAò{Aò  Aú  B õÃBë
+Bë
+BáHB
+×
+B
+×
+BÌÍBÂBžRBžRB
+®B £×B £×B$B(\B(
+B,
+B0záB4p€B4p€B8ffB<\)B<\)B@QìBDG®BH=qBH=qBL33BP(öBP(öBT
+žBX{BX
+=B\
+=B`  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+=q?Å
+ž?Ã×
+@!G®@aG®@Qì@  @°  @Ï®@Ï\)@ï
+=A
+A\)A33A'33A7
+=A6áHAFžRAVžRAV\AfffAvffA
+žA
+=A
+=AõÃAáHAÌÍA¢ÌÍAªžRAª£×A²£×Aº\AºzáAÂffAÊffAÊQìAÒ=qAÚ=qAâ(öAâ{Aê  Aò  Añë
+Aù×
+B ë
+BáHB×
+B×
+B
+ÌÍB
+ÂBžRBžRB®B£×B
+£×B B \B$
+B(
+B(záB,p€B0p€B4ffB4\)B8\)B<QìB<G®B@=qBD=qBH33BH(öBL(öBP
+žBP{BT
+=BX
+=BX  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?®?Â\?Â\@ £×@`  @  @®@¯\)@Ï
+=@Ï
+=@îžRA33A33A
+=A&áHA6žRA6žRAF\AVffAVffAf=qAv{A
+=AõÃAáHAÌÍAÌÍAžRA¢£×Aª£×Aª\A²záAºffAºffAÂQìAÊ=qAÊ=qAÒ(öAÚ{Aâ  Aâ  Aéë
+Añ×
+Añ×
+AùÂB ×
+B×
+BÌÍBÂB
+žRB
+žRB®B£×B£×BB
+\B 
+B 
+B$záB(p€B(p€B,ffB0\)B4\)B4QìB8G®B<=qB<=qB@33BD(öBH(öBH
+žBL{BP
+=BP
+=BT  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+ž?ÁG®?À  @   @_\)@\)@
+=@¯
+=@ÎžR@Îff@îffA
+=AáHAžRA&žRA6\A6ffAFffAV=qAV{Af{Auë
+AáHAÌÍAÌÍAžRA£×A£×A¢\AªzáAªffA²ffAºQìAº=qAÂ=qAÊ(öAÊ{AÒ  AÚ  Aáë
+Aá×
+Aé×
+AñÂAñ®Aù®B ÌÍBÂBžRBžRB
+®B
+£×B£×BB\B
+B
+
+B záB p€B$p€B(ffB(\)B,\)B0QìB4G®B4=qB8=qB<33B<(öB@(öBD
+žBH{BH
+=BL
+=BP  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?   ?À  ?ŸžR@
+žR@^{@
+=@žR@®ff@Îff@Î{@íÂAžRAžRA\A&ffA6ffA6=qAF{AV{AUë
+AeÂAuAÌÍAžRA£×A£×A\AzáA¢ffAªffAªQìA²=qAº=qAº(öAÂ{AÊ  AÊ  AÑë
+AÙ×
+Aá×
+AáÂAé®Añ®AñAù
+B žRBžRB®B£×B
+£×B
+B\B
+B
+BzáB
+p€B p€B ffB$\)B(\)B(QìB,G®B0=qB4=qB433B8(öB<(öB<
+žB@{BD
+=BH
+=BH  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >úáH?œp€?Œ(ö@
+{@]p€@ff@ff@®{@ÍÂ@Íp€@íp€A\AffAffA&=qA6{A6{AEë
+AUÂAUAeAup€A£×A£×A\AzáAffAffA¢QìAª=qAª=qA²(öAº{Aº  AÂ  AÉë
+AÉ×
+AÑ×
+AÙÂAá®Aá®AéAñ
+Añp€Aùp€B ®B£×B£×BB
+\B
+
+B
+BzáBp€Bp€B
+ffB \)B \)B$QìB(G®B(=qB,=qB033B4(öB4(öB8
+žB<{B<
+=B@
+=BD  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ð£×?Œ(ö?ºáH@
+ÌÍ@\ÌÍ@{@Â@­p€@Íp€@Í
+ž@ìÌÍAffA=qA{A&{A5ë
+A5ÂAEAUAUp€AeG®AuG®A\AzáAffAffAQìA=qA¢=qAª(öAª{A²  Aº  A¹ë
+AÁ×
+AÉ×
+AÉÂAÑ®AÙ®AáAá
+Aép€Añp€Añ\)AùG®B £×BB\B
+B
+
+B
+záBp€Bp€BffB\)B
+\)B QìB G®B$=qB(=qB(33B,(öB0(öB4
+žB4{B8
+=B<
+=B<  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ë
+?¹?¹@
+(ö@[
+@p€@p€@­
+ž@ÌÌÍ@ÌÌÍ@ìzáA{A{Aë
+A%ÂA5A5AEp€AUG®AUG®Ae
+žAtõÃAffAffAQìA=qA=qA(öA¢{Aª  Aª  A±ë
+A¹×
+A¹×
+AÁÂAÉ®AÉ®AÑAÙ
+Aáp€Aáp€Aé\)AñG®AñG®Aù33B \B
+B
+BzáB
+p€B
+p€BffB\)B\)BQìB
+G®B =qB =qB$33B((öB((öB,
+žB0{B4
+=B4
+=B8  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >æff?žQì?·
+>@áH@ZáH@
+ž@ÌÍ@¬ÌÍ@Ìzá@Ì(ö@ì(öAë
+AÂAA%A5p€A5G®AEG®AU
+žATõÃAdÌÍAtÌÍAQìA=qA=qA(öA{A  A¢  A©ë
+A©×
+A±×
+A¹ÂA¹®AÁ®AÉAÉ
+AÑp€AÙp€Aá\)AáG®AéG®Añ33Añ
+žAù
+=B 
+BzáBp€Bp€B
+ffB
+\)B\)BQìBG®B=qB
+=qB 33B (öB$(öB(
+žB({B,
+=B0
+=B4  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ü(ö?µÂ?µÂ@=q@Y@ÌÍ@zá@¬(ö@Ì(ö@Ë×
+@ë
+AAAp€A%G®A5G®A5
+žADõÃATÌÍATÌÍAd£×AtzáA=qA(öA{A  A  Aë
+A¡×
+A©×
+A©ÂA±®A¹®A¹AÁ
+AÉp€AÉp€AÑ\)AÙG®AáG®Aá33Aé
+žAñ
+=Añ
+=AøõÃB p€Bp€BffB\)B
+\)B
+QìBG®B=qB=qB33B
+(öB (öB 
+žB${B(
+=B(
+=B,  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >×
+=?Žzá?³33@@XõÂ@(ö@(ö@«×
+@Ë
+@Ë33@ë33Ap€AG®AG®A%
+žA4õÃA4ÌÍADÌÍAT£×ATzáAdzáAtQìA{A  A  Aë
+A×
+A×
+A¡ÂA©®A©®A±A¹
+A¹p€AÁp€AÉ\)AÉG®AÑG®AÙ33Aá
+žAá
+=Aé
+=AðõÃAðáHAøáHB ffB\)B\)BQìB
+G®B
+=qB=qB33B(öB(öB
+
+žB {B 
+=B$
+=B(  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ÌÌÍ?³33?±ë
+@Qì@XQì@×
+@
+@«33@Ë33@ÊáH@ê\AG®A
+žAõÃA$ÌÍA4ÌÍA4£×ADzáATzáATQìAd(öAt  A  Aë
+A×
+A×
+AÂA®A¡®A©A©
+A±p€A¹p€A¹\)AÁG®AÉG®AÉ33AÑ
+žAÙ
+=Aá
+=AàõÃAèáHAðáHAðÌÍAøžRB \)BQìBG®B=qB
+=qB
+33B(öB(öB
+žB{B
+
+=B 
+=B   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ç®?°£×?°£×@®@W
+>@33@33@ªáH@Ê\@Ê\@ê=qAõÃAÌÍAÌÍA$£×A4záA4záADQìAT(öAT  Ad  As×
+A×
+A×
+AÂA®A®AA¡
+A©p€A©p€A±\)A¹G®A¹G®AÁ33AÉ
+žAÉ
+=AÑ
+=AØõÃAàáHAàáHAèÌÍAðžRAðžRAø£×B G®B=qB=qB33B
+(öB
+(öB
+žB{B
+=B
+=B
+  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Â\?¯\)?®{@ff@Vff@áH@\@ª\@Ê=q@Éë
+@éAÌÍA£×AzáA$záA4QìA4(öAD  AT  AS×
+Ac®As®AÂA®A®AA
+Ap€A¡p€A©\)A©G®A±G®A¹33A¹
+žAÁ
+=AÉ
+=AÈõÃAÐáHAØáHAàÌÍAàžRAèžRAð£×Að\AøzáB =qB33B(öB(öB
+
+žB
+{B
+=B
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >žQì?¬ÌÍ?¬ÌÍ@Â@U
+ž@\@=q@©ë
+@É@É@éG®AzáAzáAQìA$(öA4  A4  AC×
+AS®AS®Ac
+As\)A®AA
+Ap€Ap€A\)A¡G®A©G®A©33A±
+žA¹
+=A¹
+=AÀõÃAÈáHAÈáHAÐÌÍAØžRAàžRAà£×Aè\AðzáAðzáAøffB (öB(öB
+žB{B
+
+=B
+
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >³33?«
+?ª=q@
+ž@Tzá@ë
+@@©@ÉG®@ÈõÃ@èõÃAQìA(öA  A$  A3×
+A3®AC®AS
+AS\)Ac\)As33A
+Ap€Ap€A\)AG®AG®A¡33A©
+žA©
+=A±
+=AžõÃAžáHAÀáHAÈÌÍAÈžRAÐžRAØ£×Aà\AàzáAèzáAðffAðQìAøQìB 
+žB{B
+=B
+=B
+  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >šõÃ?ª=q?šõÂ@×
+@S33@@G®@šõÃ@ÈõÃ@È£×@èQìA  A  A×
+A#®A3®A3
+AC\)AS\)AS33Ac
+=AráHAp€A\)AG®AG®A33A
+žA¡
+=A©
+=AšõÃA°áHAžáHAžÌÍAÀžRAÈžRAÈ£×AÐ\AØzáAàzáAàffAèQìAðQìAð=qAø(öB 
+=B
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >£×
+?§®?Šff@33@R\@õÃ@õÃ@š£×@ÈQì@È  @è  A×
+A®A®A#
+A3\)A3\)AC33AS
+=ARáHAbáHAržRAG®AG®A33A
+žA
+=A
+=A õÃAšáHAšáHA°ÌÍAžžRAžžRAÀ£×AÈ\AÈzáAÐzáAØffAàQìAàQìAè=qAð(öAð{Aø{B   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >?Šff?¥
+ž@ë
+@Që
+@£×@Qì@š  @È  @Ç®@ç\)A®A
+A\)A#\)A333A3
+=ABáHARáHARžRAb\Ar\A33A
+žA
+=A
+=AõÃAáHA áHAšÌÍAšžRA°žRAž£×Až\AÀzáAÈzáAÈffAÐQìAØQìAà=qAà(öAè{Að{Að  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >zá?£×
+?£×
+@G®@P£×@  @  @§®@Ç\)@Ç\)@ç
+=A\)A\)A33A#
+=A2áHA2áHABžRAR\AR\AbffAr=qA
+=A
+=AõÃAáHAáHAÌÍA žRAšžRAš£×A°\AžzáAžzáAÀffAÈQìAÈQìAÐ=qAØ(öAà{Aà{Aè  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >\)?¢\?¡G®@  @P  @®@\)@§\)@Ç
+=@ÆžR@æžRA33A
+=AáHA"áHA2žRA2\AB\ARffAR=qAb{Ar{AõÃAáHAáHAÌÍAžRAžRA £×Aš\AšzáA°záAžffAžQìAÀQìAÈ=qAÈ(öAÐ{AØ{Aà  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >
+
+ž?   ?   @\)@NžR@\)@
+=@ŠžR@ÆžR@Æff@æ{AáHAáHAžRA"\A2\A2ffAB=qAR{AR{Aaë
+AqÂAáHAÌÍAžRAžRA£×A\A záAšzáAšffA°QìAžQìAž=qAÀ(öAÈ{AÈ{AÐ  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >  ?žR?p€@žR@N{@žR@žR@Šff@Æ{@ÅÂ@åÂAžRA\A\A"ffA2=qA2{AB{AQë
+AQÂAaÂAqAžRAžRA£×A\AzáAzáA ffAšQìAšQìA°=qAž(öAž{AÀ{AÈ  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >k
+?p€?(ö@
+p€@Mp€@ff@{@¥Â@ÅÂ@Åp€@å
+žA\AffA=qA"{A2{A1ë
+AAÂAQÂAQAap€Aqp€A£×A\AzáAzáAffAQìA QìAš=qAš(öA°{Až{Až  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >aG®?áH?áH@
+ÌÍ@L(ö@
+Â@
+Â@¥p€@Å
+ž@Å
+ž@äÌÍA=qA{A{A!ë
+A1ÂA1ÂAAAQp€AQp€AaG®Aq
+žAzáAzáAffAQìAQìA=qA (öAš{Aš{A°  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >W
+=??Qì@
+
+@K
+@
+p€@
+
+ž@¥
+ž@ÄÌÍ@Äzá@ä(öA{Aë
+AÂA!ÂA1A1p€AAp€AQG®AQ
+žA`õÃApõÃAffAQìAQìA=qA(öA{A {Aš  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >B\?
+>?
+>@
+áH@J=q@
+
+ž@ÌÍ@€zá@Ä(ö@Ä(ö@ã×
+AÂAÂAA!p€A1p€A1G®AA
+žAPõÃAPõÃA`ÌÍAp£×AQìA=qA(öA{A{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >8Qì?Â?zá@
+=q@I@zá@(ö@€(ö@Ã×
+@Ã
+@ã
+AAp€Ap€A!G®A1
+žA0õÃA@õÃAPÌÍAP£×A`£×ApzáA(öA{A{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >#×
+?zá?33@õÃ@HQì@(ö@×
+@£
+@Ã
+@Ã33@âáHAp€AG®A
+žA õÃA0õÃA0ÌÍA@£×AP£×APzáA`QìAp(öA{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >?ë
+?£×@Qì@G®@
+@
+@£33@ÂáH@ÂáH@â\A
+žAõÃAõÃA ÌÍA0£×A0£×A@záAPQìAP(öA`(öAp  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >
+ž?£×?\)@
+=@G
+=@33@áH@¢áH@Â\@Â=q@áë
+A õÃAÌÍA£×A £×A0záA0QìA@(öAP(öAP  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =õÂ?{?{@ff@EÂ@áH@\@¢=q@Áë
+@Áë
+@áA £×A£×AzáA QìA0(öA0(öA@  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =áG®?ÌÍ?
+@Â@E
+ž@=q@ë
+@¡ë
+@Á@ÁG®@áG®A záAQìA(öA (öA0  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =žQì?
+?=q@zá@C×
+@ë
+@@¡G®@ÁG®@ÀõÃ@à£×A (öA(öA  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =£×
+?õÃ?®@×
+@C33@G®@G®@ õÃ@À£×@ÀQì@àQìA   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =uÂ?®?ff@\@B\@õÃ@£×@ Qì@ÀQì@À  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =LÌÍ?
+
+ž?
+
+ž@ë
+@AG®@Qì@Qì@   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =#×
+?×
+?\@ £×@@£×@  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  <£×
+?G®?G®@   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  <#×
+?  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ?!G®?Ð£×@(Qì@hQì@(ö@Ž(ö@Ô(ö@ô(öA
+{A{A*{A:{AJ{AZ{Aj{Az{A
+
+=A
+=A
+=A
+=A¥
+=A­
+=Aµ
+=Aœ
+=AÅ
+=AÍ
+=AÕ
+=AÝ
+=Aå
+=Aí
+=Aõ
+=Aý
+=B
+B
+B
+
+B
+B
+B
+B
+B
+
+B"
+B&
+B*
+B.
+B2
+B6
+B:
+B>
+BB
+BF
+BJ
+BN
+BR
+BV
+BZ
+B^
+Bb
+Bf
+Bj
+Bn
+Br
+Bv
+Bz
+B~
+?
+žR?Ï\)@'®@g®@×
+@³×
+@Ó×
+@ó×
+A	ë
+Aë
+A)ë
+A9ë
+AIë
+AYë
+Aië
+Ayë
+AõÃAõÃAõÃAõÃA€õÃA¬õÃAŽõÃAŒõÃAÄõÃAÌõÃAÔõÃAÜõÃAäõÃAìõÃAôõÃAüõÃBzáBzáB
+záBzáBzáBzáBzáB
+záB"záB&záB*záB.záB2záB6záB:záB>záBBzáBFzáBJzáBNzáBRzáBVzáBZzáB^záBbzáBfzáBjzáBnzáBrzáBvzáBzzáB~zá?
+(ö?Î{@'
+>@g
+>@
+@³
+@Ó
+@ó
+A	ÂAÂA)ÂA9ÂAIÂAYÂAiÂAyÂAáHAáHAáHAáHA€áHA¬áHAŽáHAŒáHAÄáHAÌáHAÔáHAÜáHAäáHAìáHAôáHAüáHBp€Bp€B
+p€Bp€Bp€Bp€Bp€B
+p€B"p€B&p€B*p€B.p€B2p€B6p€B:p€B>p€BBp€BFp€BJp€BNp€BRp€BVp€BZp€B^p€Bbp€Bfp€Bjp€Bnp€Brp€Bvp€Bzp€B~p€??ÌÌÍ@&ff@fff@33@³33@Ó33@ó33A	AA)A9AIAYAiAyAÌÍAÌÍAÌÍAÌÍA€ÌÍA¬ÌÍAŽÌÍAŒÌÍAÄÌÍAÌÌÍAÔÌÍAÜÌÍAäÌÍAìÌÍAôÌÍAüÌÍBffBffB
+ffBffBffBffBffB
+ffB"ffB&ffB*ffB.ffB2ffB6ffB:ffB>ffBBffBFffBJffBNffBRffBVffBZffB^ffBbffBfffBjffBnffBrffBvffBzffB~ff?
+=?Ë
+
+@%Â@eÂ@áH@²áH@ÒáH@òáHA	p€Ap€A)p€A9p€AIp€AYp€Aip€Ayp€AžRAžRAžRAžRA€žRA¬žRAŽžRAŒžRAÄžRAÌžRAÔžRAÜžRAäžRAìžRAôžRAüžRB\)B\)B
+\)B\)B\)B\)B\)B
+\)B"\)B&\)B*\)B.\)B2\)B6\)B:\)B>\)BB\)BF\)BJ\)BN\)BR\)BV\)BZ\)B^\)Bb\)Bf\)Bj\)Bn\)Br\)Bv\)Bz\)B~\)?zá?Ê=p@%
+ž@e
+ž@\@²\@Ò\@ò\A	G®AG®A)G®A9G®AIG®AYG®AiG®AyG®A£×A£×A£×A£×A€£×A¬£×AŽ£×AŒ£×AÄ£×AÌ£×AÔ£×AÜ£×Aä£×Aì£×Aô£×Aü£×BQìBQìB
+QìBQìBQìBQìBQìB
+QìB"QìB&QìB*QìB.QìB2QìB6QìB:QìB>QìBBQìBFQìBJQìBNQìBRQìBVQìBZQìB^QìBbQìBfQìBjQìBnQìBrQìBvQìBzQìB~Qì?ë
+?ÈõÂ@$zá@dzá@=q@²=q@Ò=q@ò=qA	
+žA
+žA)
+žA9
+žAI
+žAY
+žAi
+žAy
+žA\A\A\A\A€\A¬\AŽ\AŒ\AÄ\AÌ\AÔ\AÜ\Aä\Aì\Aô\Aü\BG®BG®B
+G®BG®BG®BG®BG®B
+G®B"G®B&G®B*G®B.G®B2G®B6G®B:G®B>G®BBG®BFG®BJG®BNG®BRG®BVG®BZG®B^G®BbG®BfG®BjG®BnG®BrG®BvG®BzG®B~G®?\)?Ç®@#×
+@c×
+@ë
+@±ë
+@Ñë
+@ñë
+AõÃAõÃA(õÃA8õÃAHõÃAXõÃAhõÃAxõÃAzáAzáAzáAzáA€záA¬záAŽzáAŒzáAÄzáAÌzáAÔzáAÜzáAäzáAìzáAôzáAüzáB=qB=qB
+=qB=qB=qB=qB=qB
+=qB"=qB&=qB*=qB.=qB2=qB6=qB:=qB>=qBB=qBF=qBJ=qBN=qBR=qBV=qBZ=qB^=qBb=qBf=qBj=qBn=qBr=qBv=qBz=qB~=q?
+ÌÍ?Æff@#33@c33@@±@Ñ@ñAÌÍAÌÍA(ÌÍA8ÌÍAHÌÍAXÌÍAhÌÍAxÌÍAffAffAffAffA€ffA¬ffAŽffAŒffAÄffAÌffAÔffAÜffAäffAìffAôffAüffB33B33B
+33B33B33B33B33B
+33B"33B&33B*33B.33B233B633B:33B>33BB33BF33BJ33BN33BR33BV33BZ33B^33Bb33Bf33Bj33Bn33Br33Bv33Bz33B~33?
+=q?Å
+ž@"\@b\@G®@±G®@ÑG®@ñG®A£×A£×A(£×A8£×AH£×AX£×Ah£×Ax£×AQìAQìAQìAQìA€QìA¬QìAŽQìAŒQìAÄQìAÌQìAÔQìAÜQìAäQìAìQìAôQìAüQìB(öB(öB
+(öB(öB(öB(öB(öB
+(öB"(öB&(öB*(öB.(öB2(öB6(öB:(öB>(öBB(öBF(öBJ(öBN(öBR(öBV(öBZ(öB^(öBb(öBf(öBj(öBn(öBr(öBv(öBz(öB~(ö?®?Ã×
+@!ë
+@aë
+@õÂ@°õÂ@ÐõÂ@ðõÂAzáAzáA(záA8záAHzáAXzáAhzáAxzáA=qA=qA=qA=qA€=qA¬=qAŽ=qAŒ=qAÄ=qAÌ=qAÔ=qAÜ=qAä=qAì=qAô=qAü=qB
+žB
+žB
+
+žB
+žB
+žB
+žB
+žB
+
+žB"
+žB&
+žB*
+žB.
+žB2
+žB6
+žB:
+žB>
+žBB
+žBF
+žBJ
+žBN
+žBR
+žBV
+žBZ
+žB^
+žBb
+žBf
+žBj
+žBn
+žBr
+žBv
+žBz
+žB~
+ž?
+ž?Â\@!G®@aG®@£×@°£×@Ð£×@ð£×AQìAQìA(QìA8QìAHQìAXQìAhQìAxQìA(öA(öA(öA(öA€(öA¬(öAŽ(öAŒ(öAÄ(öAÌ(öAÔ(öAÜ(öAä(öAì(öAô(öAü(öB{B{B
+{B{B{B{B{B
+{B"{B&{B*{B.{B2{B6{B:{B>{BB{BF{BJ{BN{BR{BV{BZ{B^{Bb{Bf{Bj{Bn{Br{Bv{Bz{B~{?\?ÁG®@ £×@`£×@Qì@°Qì@ÐQì@ðQìA(öA(öA((öA8(öAH(öAX(öAh(öAx(öA{A{A{A{A€{A¬{AŽ{AŒ{AÄ{AÌ{AÔ{AÜ{Aä{Aì{Aô{Aü{B
+=B
+=B
+
+=B
+=B
+=B
+=B
+=B
+
+=B"
+=B&
+=B*
+=B.
+=B2
+=B6
+=B:
+=B>
+=BB
+=BF
+=BJ
+=BN
+=BR
+=BV
+=BZ
+=B^
+=Bb
+=Bf
+=Bj
+=Bn
+=Br
+=Bv
+=Bz
+=B~
+=?   ?À  @   @`  @  @°  @Ð  @ð  A  A  A(  A8  AH  AX  Ah  Ax  A  A  A  A  A€  A¬  AŽ  AŒ  AÄ  AÌ  AÔ  AÜ  Aä  Aì  Aô  Aü  B  B  B
+  B  B  B  B  B
+  B"  B&  B*  B.  B2  B6  B:  B>  BB  BF  BJ  BN  BR  BV  BZ  B^  Bb  Bf  Bj  Bn  Br  Bv  Bz  B~  >úáH?ŸžR@\)@_\)@®@¯®@Ï®@ï®A×
+A×
+A'×
+A7×
+AG×
+AW×
+Ag×
+Aw×
+Aë
+Aë
+Aë
+Aë
+A£ë
+A«ë
+A³ë
+A»ë
+AÃë
+AËë
+AÓë
+AÛë
+Aãë
+Aëë
+Aóë
+Aûë
+BõÃBõÃB	õÃB
+õÃBõÃBõÃBõÃB
+õÃB!õÃB%õÃB)õÃB-õÃB1õÃB5õÃB9õÃB=õÃBAõÃBEõÃBIõÃBMõÃBQõÃBUõÃBYõÃB]õÃBaõÃBeõÃBiõÃBmõÃBqõÃBuõÃByõÃB}õÃ>õÂ?œp€@
+žR@^žR@\)@¯\)@Ï\)@ï\)A®A®A'®A7®AG®AW®Ag®Aw®A×
+A×
+A×
+A×
+A£×
+A«×
+A³×
+A»×
+AÃ×
+AË×
+AÓ×
+AÛ×
+Aã×
+Aë×
+Aó×
+Aû×
+Bë
+Bë
+B	ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B!ë
+B%ë
+B)ë
+B-ë
+B1ë
+B5ë
+B9ë
+B=ë
+BAë
+BEë
+BIë
+BMë
+BQë
+BUë
+BYë
+B]ë
+Baë
+Beë
+Bië
+Bmë
+Bqë
+Buë
+Byë
+B}ë
+>ð£×?Œ(ö@
+{@^{@
+=@¯
+=@Ï
+=@ï
+=A
+A
+A'
+A7
+AG
+AW
+Ag
+Aw
+AÂAÂAÂAÂA£ÂA«ÂA³ÂA»ÂAÃÂAËÂAÓÂAÛÂAãÂAëÂAóÂAûÂBáHBáHB	áHB
+áHBáHBáHBáHB
+áHB!áHB%áHB)áHB-áHB1áHB5áHB9áHB=áHBAáHBEáHBIáHBMáHBQáHBUáHBYáHB]áHBaáHBeáHBiáHBmáHBqáHBuáHByáHB}áH>ë
+?ºáH@
+p€@]p€@žR@®žR@ÎžR@îžRA\)A\)A'\)A7\)AG\)AW\)Ag\)Aw\)A®A®A®A®A£®A«®A³®A»®AÃ®AË®AÓ®AÛ®Aã®Aë®Aó®Aû®B×
+B×
+B	×
+B
+×
+B×
+B×
+B×
+B
+×
+B!×
+B%×
+B)×
+B-×
+B1×
+B5×
+B9×
+B=×
+BA×
+BE×
+BI×
+BM×
+BQ×
+BU×
+BY×
+B]×
+Ba×
+Be×
+Bi×
+Bm×
+Bq×
+Bu×
+By×
+B}×
+>æff?¹@
+ÌÍ@\ÌÍ@ff@®ff@Îff@îffA33A33A'33A733AG33AW33Ag33Aw33AAAAA£A«A³A»AÃAËAÓAÛAãAëAóAûBÌÍBÌÍB	ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB!ÌÍB%ÌÍB)ÌÍB-ÌÍB1ÌÍB5ÌÍB9ÌÍB=ÌÍBAÌÍBEÌÍBIÌÍBMÌÍBQÌÍBUÌÍBYÌÍB]ÌÍBaÌÍBeÌÍBiÌÍBmÌÍBqÌÍBuÌÍByÌÍB}ÌÍ>áG®?žQì@
+(ö@\(ö@{@®{@Î{@î{A
+=A
+=A'
+=A7
+=AG
+=AW
+=Ag
+=Aw
+=A
+A
+A
+A
+A£
+A«
+A³
+A»
+AÃ
+AË
+AÓ
+AÛ
+Aã
+Aë
+Aó
+Aû
+BÂBÂB	ÂB
+ÂBÂBÂBÂB
+ÂB!ÂB%ÂB)ÂB-ÂB1ÂB5ÂB9ÂB=ÂBAÂBEÂBIÂBMÂBQÂBUÂBYÂB]ÂBaÂBeÂBiÂBmÂBqÂBuÂByÂB}Â>Ü(ö?·
+>@
+@[
+@Â@­Â@ÍÂ@íÂAáHAáHA&áHA6áHAFáHAVáHAfáHAváHAp€Ap€Ap€Ap€A£p€A«p€A³p€A»p€AÃp€AËp€AÓp€AÛp€Aãp€Aëp€Aóp€Aûp€BžRBžRB	žRB
+žRBžRBžRBžRB
+žRB!žRB%žRB)žRB-žRB1žRB5žRB9žRB=žRBAžRBEžRBIžRBMžRBQžRBUžRBYžRB]žRBažRBežRBižRBmžRBqžRBužRByžRB}žR>×
+=?µÂ@áH@ZáH@p€@­p€@Íp€@íp€AžRAžRA&žRA6žRAFžRAVžRAfžRAvžRA\)A\)A\)A\)A£\)A«\)A³\)A»\)AÃ\)AË\)AÓ\)AÛ\)Aã\)Aë\)Aó\)Aû\)B®B®B	®B
+®B®B®B®B
+®B!®B%®B)®B-®B1®B5®B9®B=®BA®BE®BI®BM®BQ®BU®BY®B]®Ba®Be®Bi®Bm®Bq®Bu®By®B}®>Ñë
+?Žzá@=q@Z=q@
+ž@­
+ž@Í
+ž@í
+žA\A\A&\A6\AF\AV\Af\Av\AG®AG®AG®AG®A£G®A«G®A³G®A»G®AÃG®AËG®AÓG®AÛG®AãG®AëG®AóG®AûG®B£×B£×B	£×B
+£×B£×B£×B£×B
+£×B!£×B%£×B)£×B-£×B1£×B5£×B9£×B=£×BA£×BE£×BI£×BM£×BQ£×BU£×BY£×B]£×Ba£×Be£×Bi£×Bm£×Bq£×Bu£×By£×B}£×>ÌÌÍ?³33@@Y@ÌÍ@¬ÌÍ@ÌÌÍ@ìÌÍAffAffA&ffA6ffAFffAVffAfffAvffA33A33A33A33A£33A«33A³33A»33AÃ33AË33AÓ33AÛ33Aã33Aë33Aó33Aû33BBB	B
+BBBB
+B!B%B)B-B1B5B9B=BABEBIBMBQBUBYB]BaBeBiBmBqBuByB}>Ç®?±ë
+@õÂ@XõÂ@zá@¬zá@Ìzá@ìzáA=qA=qA&=qA6=qAF=qAV=qAf=qAv=qA
+žA
+žA
+žA
+žA£
+žA«
+žA³
+žA»
+žAÃ
+žAË
+žAÓ
+žAÛ
+žAã
+žAë
+žAó
+žAû
+žB\B\B	\B
+\B\B\B\B
+\B!\B%\B)\B-\B1\B5\B9\B=\BA\BE\BI\BM\BQ\BU\BY\B]\Ba\Be\Bi\Bm\Bq\Bu\By\B}\>Â\?°£×@Qì@XQì@(ö@¬(ö@Ì(ö@ì(öA{A{A&{A6{AF{AV{Af{Av{A
+=A
+=A
+=A
+=A£
+=A«
+=A³
+=A»
+=AÃ
+=AË
+=AÓ
+=AÛ
+=Aã
+=Aë
+=Aó
+=Aû
+=B
+B
+B	
+B
+
+B
+B
+B
+B
+
+B!
+B%
+B)
+B-
+B1
+B5
+B9
+B=
+BA
+BE
+BI
+BM
+BQ
+BU
+BY
+B]
+Ba
+Be
+Bi
+Bm
+Bq
+Bu
+By
+B}
+>œp€?¯\)@®@W®@×
+@«×
+@Ë×
+@ë×
+Aë
+Aë
+A%ë
+A5ë
+AEë
+AUë
+Aeë
+Auë
+AõÃAõÃAõÃAõÃA¢õÃAªõÃA²õÃAºõÃAÂõÃAÊõÃAÒõÃAÚõÃAâõÃAêõÃAòõÃAúõÃBzáBzáB	záB
+záBzáBzáBzáB
+záB!záB%záB)záB-záB1záB5záB9záB=záBAzáBEzáBIzáBMzáBQzáBUzáBYzáB]záBazáBezáBizáBmzáBqzáBuzáByzáB}zá>žQì?®{@
+>@W
+>@
+@«
+@Ë
+@ë
+AÂAÂA%ÂA5ÂAEÂAUÂAeÂAuÂAáHAáHAáHAáHA¢áHAªáHA²áHAºáHAÂáHAÊáHAÒáHAÚáHAâáHAêáHAòáHAúáHBp€Bp€B	p€B
+p€Bp€Bp€Bp€B
+p€B!p€B%p€B)p€B-p€B1p€B5p€B9p€B=p€BAp€BEp€BIp€BMp€BQp€BUp€BYp€B]p€Bap€Bep€Bip€Bmp€Bqp€Bup€Byp€B}p€>³33?¬ÌÍ@ff@Vff@33@«33@Ë33@ë33AAA%A5AEAUAeAuAÌÍAÌÍAÌÍAÌÍA¢ÌÍAªÌÍA²ÌÍAºÌÍAÂÌÍAÊÌÍAÒÌÍAÚÌÍAâÌÍAêÌÍAòÌÍAúÌÍBffBffB	ffB
+ffBffBffBffB
+ffB!ffB%ffB)ffB-ffB1ffB5ffB9ffB=ffBAffBEffBIffBMffBQffBUffBYffB]ffBaffBeffBiffBmffBqffBuffByffB}ff>®{?«
+@Â@UÂ@áH@ªáH@ÊáH@êáHAp€Ap€A%p€A5p€AEp€AUp€Aep€Aup€AžRAžRAžRAžRA¢žRAªžRA²žRAºžRAÂžRAÊžRAÒžRAÚžRAâžRAêžRAòžRAúžRB\)B\)B	\)B
+\)B\)B\)B\)B
+\)B!\)B%\)B)\)B-\)B1\)B5\)B9\)B=\)BA\)BE\)BI\)BM\)BQ\)BU\)BY\)B]\)Ba\)Be\)Bi\)Bm\)Bq\)Bu\)By\)B}\)>šõÃ?ª=q@
+ž@U
+ž@\@ª\@Ê\@ê\AG®AG®A%G®A5G®AEG®AUG®AeG®AuG®A£×A£×A£×A£×A¢£×Aª£×A²£×Aº£×AÂ£×AÊ£×AÒ£×AÚ£×Aâ£×Aê£×Aò£×Aú£×BQìBQìB	QìB
+QìBQìBQìBQìB
+QìB!QìB%QìB)QìB-QìB1QìB5QìB9QìB=QìBAQìBEQìBIQìBMQìBQQìBUQìBYQìB]QìBaQìBeQìBiQìBmQìBqQìBuQìByQìB}Qì>£×
+?šõÂ@zá@Tzá@=q@ª=q@Ê=q@ê=qA
+žA
+žA%
+žA5
+žAE
+žAU
+žAe
+žAu
+žA\A\A\A\A¢\Aª\A²\Aº\AÂ\AÊ\AÒ\AÚ\Aâ\Aê\Aò\Aú\BG®BG®B	G®B
+G®BG®BG®BG®B
+G®B!G®B%G®B)G®B-G®B1G®B5G®B9G®B=G®BAG®BEG®BIG®BMG®BQG®BUG®BYG®B]G®BaG®BeG®BiG®BmG®BqG®BuG®ByG®B}G®>žR?§®@×
+@S×
+@ë
+@©ë
+@Éë
+@éë
+AõÃAõÃA$õÃA4õÃADõÃATõÃAdõÃAtõÃAzáAzáAzáAzáA¢záAªzáA²záAºzáAÂzáAÊzáAÒzáAÚzáAâzáAêzáAòzáAúzáB=qB=qB	=qB
+=qB=qB=qB=qB
+=qB!=qB%=qB)=qB-=qB1=qB5=qB9=qB==qBA=qBE=qBI=qBM=qBQ=qBU=qBY=qB]=qBa=qBe=qBi=qBm=qBq=qBu=qBy=qB}=q>?Šff@33@S33@@©@É@éAÌÍAÌÍA$ÌÍA4ÌÍADÌÍATÌÍAdÌÍAtÌÍAffAffAffAffA¢ffAªffA²ffAºffAÂffAÊffAÒffAÚffAâffAêffAòffAúffB33B33B	33B
+33B33B33B33B
+33B!33B%33B)33B-33B133B533B933B=33BA33BE33BI33BM33BQ33BU33BY33B]33Ba33Be33Bi33Bm33Bq33Bu33By33B}33>zá?¥
+ž@\@R\@G®@©G®@ÉG®@éG®A£×A£×A$£×A4£×AD£×AT£×Ad£×At£×AQìAQìAQìAQìA¢QìAªQìA²QìAºQìAÂQìAÊQìAÒQìAÚQìAâQìAêQìAòQìAúQìB(öB(öB	(öB
+(öB(öB(öB(öB
+(öB!(öB%(öB)(öB-(öB1(öB5(öB9(öB=(öBA(öBE(öBI(öBM(öBQ(öBU(öBY(öB](öBa(öBe(öBi(öBm(öBq(öBu(öBy(öB}(ö>\)?£×
+@ë
+@Që
+@õÃ@šõÃ@ÈõÃ@èõÃAzáAzáA$záA4záADzáATzáAdzáAtzáA=qA=qA=qA=qA¢=qAª=qA²=qAº=qAÂ=qAÊ=qAÒ=qAÚ=qAâ=qAê=qAò=qAú=qB
+žB
+žB	
+žB
+
+žB
+žB
+žB
+žB
+
+žB!
+žB%
+žB)
+žB-
+žB1
+žB5
+žB9
+žB=
+žBA
+žBE
+žBI
+žBM
+žBQ
+žBU
+žBY
+žB]
+žBa
+žBe
+žBi
+žBm
+žBq
+žBu
+žBy
+žB}
+ž>=q?¢\@G®@QG®@£×@š£×@È£×@è£×AQìAQìA$QìA4QìADQìATQìAdQìAtQìA(öA(öA(öA(öA¢(öAª(öA²(öAº(öAÂ(öAÊ(öAÒ(öAÚ(öAâ(öAê(öAò(öAú(öB{B{B	{B
+{B{B{B{B
+{B!{B%{B){B-{B1{B5{B9{B={BA{BE{BI{BM{BQ{BU{BY{B]{Ba{Be{Bi{Bm{Bq{Bu{By{B}{>
+
+ž?¡G®@£×@P£×@Qì@šQì@ÈQì@èQìA(öA(öA$(öA4(öAD(öAT(öAd(öAt(öA{A{A{A{A¢{Aª{A²{Aº{AÂ{AÊ{AÒ{AÚ{Aâ{Aê{Aò{Aú{B
+=B
+=B	
+=B
+=B
+=B
+=B
+=B
+
+=B!
+=B%
+=B)
+=B-
+=B1
+=B5
+=B9
+=B=
+=BA
+=BE
+=BI
+=BM
+=BQ
+=BU
+=BY
+=B]
+=Ba
+=Be
+=Bi
+=Bm
+=Bq
+=Bu
+=By
+=B}
+=>  ?   @  @P  @  @š  @È  @è  A  A  A$  A4  AD  AT  Ad  At  A  A  A  A  A¢  Aª  A²  Aº  AÂ  AÊ  AÒ  AÚ  Aâ  Aê  Aò  Aú  B  B  B	  B
+  B  B  B  B
+  B!  B%  B)  B-  B1  B5  B9  B=  BA  BE  BI  BM  BQ  BU  BY  B]  Ba  Be  Bi  Bm  Bq  Bu  By  B}  >uÂ?žR@\)@O\)@®@§®@Ç®@ç®A×
+A×
+A#×
+A3×
+AC×
+AS×
+Ac×
+As×
+Aë
+Aë
+Aë
+Aë
+A¡ë
+A©ë
+A±ë
+A¹ë
+AÁë
+AÉë
+AÑë
+AÙë
+Aáë
+Aéë
+Añë
+Aùë
+B õÃBõÃBõÃB
+õÃBõÃBõÃBõÃB
+õÃB õÃB$õÃB(õÃB,õÃB0õÃB4õÃB8õÃB<õÃB@õÃBDõÃBHõÃBLõÃBPõÃBTõÃBXõÃB\õÃB`õÃBdõÃBhõÃBlõÃBpõÃBtõÃBxõÃB|õÃ>k
+?p€@žR@NžR@\)@§\)@Ç\)@ç\)A®A®A#®A3®AC®AS®Ac®As®A×
+A×
+A×
+A×
+A¡×
+A©×
+A±×
+A¹×
+AÁ×
+AÉ×
+AÑ×
+AÙ×
+Aá×
+Aé×
+Añ×
+Aù×
+B ë
+Bë
+Bë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B ë
+B$ë
+B(ë
+B,ë
+B0ë
+B4ë
+B8ë
+B<ë
+B@ë
+BDë
+BHë
+BLë
+BPë
+BTë
+BXë
+B\ë
+B`ë
+Bdë
+Bhë
+Blë
+Bpë
+Btë
+Bxë
+B|ë
+>aG®?(ö@{@N{@
+=@§
+=@Ç
+=@ç
+=A
+A
+A#
+A3
+AC
+AS
+Ac
+As
+AÂAÂAÂAÂA¡ÂA©ÂA±ÂA¹ÂAÁÂAÉÂAÑÂAÙÂAáÂAéÂAñÂAùÂB áHBáHBáHB
+áHBáHBáHBáHB
+áHB áHB$áHB(áHB,áHB0áHB4áHB8áHB<áHB@áHBDáHBHáHBLáHBPáHBTáHBXáHB\áHB`áHBdáHBháHBláHBpáHBtáHBxáHB|áH>W
+=?áH@
+p€@Mp€@žR@ŠžR@ÆžR@æžRA\)A\)A#\)A3\)AC\)AS\)Ac\)As\)A®A®A®A®A¡®A©®A±®A¹®AÁ®AÉ®AÑ®AÙ®Aá®Aé®Añ®Aù®B ×
+B×
+B×
+B
+×
+B×
+B×
+B×
+B
+×
+B ×
+B$×
+B(×
+B,×
+B0×
+B4×
+B8×
+B<×
+B@×
+BD×
+BH×
+BL×
+BP×
+BT×
+BX×
+B\×
+B`×
+Bd×
+Bh×
+Bl×
+Bp×
+Bt×
+Bx×
+B|×
+>LÌÍ?@
+ÌÍ@LÌÍ@ff@Šff@Æff@æffA33A33A#33A333AC33AS33Ac33As33AAAAA¡A©A±A¹AÁAÉAÑAÙAáAéAñAùB ÌÍBÌÍBÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB ÌÍB$ÌÍB(ÌÍB,ÌÍB0ÌÍB4ÌÍB8ÌÍB<ÌÍB@ÌÍBDÌÍBHÌÍBLÌÍBPÌÍBTÌÍBXÌÍB\ÌÍB`ÌÍBdÌÍBhÌÍBlÌÍBpÌÍBtÌÍBxÌÍB|ÌÍ>B\?Qì@
+(ö@L(ö@{@Š{@Æ{@æ{A
+=A
+=A#
+=A3
+=AC
+=AS
+=Ac
+=As
+=A
+A
+A
+A
+A¡
+A©
+A±
+A¹
+AÁ
+AÉ
+AÑ
+AÙ
+Aá
+Aé
+Añ
+Aù
+B ÂBÂBÂB
+ÂBÂBÂBÂB
+ÂB ÂB$ÂB(ÂB,ÂB0ÂB4ÂB8ÂB<ÂB@ÂBDÂBHÂBLÂBPÂBTÂBXÂB\ÂB`ÂBdÂBhÂBlÂBpÂBtÂBxÂB|Â>8Qì?
+>@
+
+@K
+@
+Â@¥Â@ÅÂ@åÂAáHAáHA"áHA2áHABáHARáHAbáHAráHAp€Ap€Ap€Ap€A¡p€A©p€A±p€A¹p€AÁp€AÉp€AÑp€AÙp€Aáp€Aép€Añp€Aùp€B žRBžRBžRB
+žRBžRBžRBžRB
+žRB žRB$žRB(žRB,žRB0žRB4žRB8žRB<žRB@žRBDžRBHžRBLžRBPžRBTžRBXžRB\žRB`žRBdžRBhžRBlžRBpžRBtžRBxžRB|žR>.{?Â@
+áH@JáH@
+p€@¥p€@Åp€@åp€AžRAžRA"žRA2žRABžRARžRAbžRAržRA\)A\)A\)A\)A¡\)A©\)A±\)A¹\)AÁ\)AÉ\)AÑ\)AÙ\)Aá\)Aé\)Añ\)Aù\)B ®B®B®B
+®B®B®B®B
+®B ®B$®B(®B,®B0®B4®B8®B<®B@®BD®BH®BL®BP®BT®BX®B\®B`®Bd®Bh®Bl®Bp®Bt®Bx®B|®>#×
+?zá@
+=q@J=q@
+
+ž@¥
+ž@Å
+ž@å
+žA\A\A"\A2\AB\AR\Ab\Ar\AG®AG®AG®AG®A¡G®A©G®A±G®A¹G®AÁG®AÉG®AÑG®AÙG®AáG®AéG®AñG®AùG®B £×B£×B£×B
+£×B£×B£×B£×B
+£×B £×B$£×B(£×B,£×B0£×B4£×B8£×B<£×B@£×BD£×BH£×BL£×BP£×BT£×BX£×B\£×B`£×Bd£×Bh£×Bl£×Bp£×Bt£×Bx£×B|£×>?33@	@I@ÌÍ@€ÌÍ@ÄÌÍ@äÌÍAffAffA"ffA2ffABffARffAbffArffA33A33A33A33A¡33A©33A±33A¹33AÁ33AÉ33AÑ33AÙ33Aá33Aé33Añ33Aù33B BBB
+BBBB
+B B$B(B,B0B4B8B<B@BDBHBLBPBTBXB\B`BdBhBlBpBtBxB|>\)?ë
+@õÃ@HõÃ@zá@€zá@Äzá@äzáA=qA=qA"=qA2=qAB=qAR=qAb=qAr=qA
+žA
+žA
+žA
+žA¡
+žA©
+žA±
+žA¹
+žAÁ
+žAÉ
+žAÑ
+žAÙ
+žAá
+žAé
+žAñ
+žAù
+žB \B\B\B
+\B\B\B\B
+\B \B$\B(\B,\B0\B4\B8\B<\B@\BD\BH\BL\BP\BT\BX\B\\B`\Bd\Bh\Bl\Bp\Bt\Bx\B|\>
+ž?£×@Qì@HQì@(ö@€(ö@Ä(ö@ä(öA{A{A"{A2{AB{AR{Ab{Ar{A
+=A
+=A
+=A
+=A¡
+=A©
+=A±
+=A¹
+=AÁ
+=AÉ
+=AÑ
+=AÙ
+=Aá
+=Aé
+=Añ
+=Aù
+=B 
+B
+B
+B
+
+B
+B
+B
+B
+
+B 
+B$
+B(
+B,
+B0
+B4
+B8
+B<
+B@
+BD
+BH
+BL
+BP
+BT
+BX
+B\
+B`
+Bd
+Bh
+Bl
+Bp
+Bt
+Bx
+B|
+=õÂ?\)@®@G®@×
+@£×
+@Ã×
+@ã×
+Aë
+Aë
+A!ë
+A1ë
+AAë
+AQë
+Aaë
+Aqë
+AõÃAõÃAõÃAõÃA õÃAšõÃA°õÃAžõÃAÀõÃAÈõÃAÐõÃAØõÃAàõÃAèõÃAðõÃAøõÃB záBzáBzáB
+záBzáBzáBzáB
+záB záB$záB(záB,záB0záB4záB8záB<záB@záBDzáBHzáBLzáBPzáBTzáBXzáB\záB`záBdzáBhzáBlzáBpzáBtzáBxzáB|zá=áG®?{@
+=@G
+=@
+@£
+@Ã
+@ã
+AÂAÂA!ÂA1ÂAAÂAQÂAaÂAqÂAáHAáHAáHAáHA áHAšáHA°áHAžáHAÀáHAÈáHAÐáHAØáHAàáHAèáHAðáHAøáHB p€Bp€Bp€B
+p€Bp€Bp€Bp€B
+p€B p€B$p€B(p€B,p€B0p€B4p€B8p€B<p€B@p€BDp€BHp€BLp€BPp€BTp€BXp€B\p€B`p€Bdp€Bhp€Blp€Bpp€Btp€Bxp€B|p€=ÌÌÍ?ÌÍ@ff@Fff@33@£33@Ã33@ã33AAA!A1AAAQAaAqAÌÍAÌÍAÌÍAÌÍA ÌÍAšÌÍA°ÌÍAžÌÍAÀÌÍAÈÌÍAÐÌÍAØÌÍAàÌÍAèÌÍAðÌÍAøÌÍB ffBffBffB
+ffBffBffBffB
+ffB ffB$ffB(ffB,ffB0ffB4ffB8ffB<ffB@ffBDffBHffBLffBPffBTffBXffB\ffB`ffBdffBhffBlffBpffBtffBxffB|ff=žQì?
+@Â@EÂ@áH@¢áH@ÂáH@âáHAp€Ap€A!p€A1p€AAp€AQp€Aap€Aqp€AžRAžRAžRAžRA žRAšžRA°žRAžžRAÀžRAÈžRAÐžRAØžRAàžRAèžRAðžRAøžRB \)B\)B\)B
+\)B\)B\)B\)B
+\)B \)B$\)B(\)B,\)B0\)B4\)B8\)B<\)B@\)BD\)BH\)BL\)BP\)BT\)BX\)B\\)B`\)Bd\)Bh\)Bl\)Bp\)Bt\)Bx\)B|\)=£×
+?=q@
+ž@E
+ž@\@¢\@Â\@â\AG®AG®A!G®A1G®AAG®AQG®AaG®AqG®A£×A£×A£×A£×A £×Aš£×A°£×Až£×AÀ£×AÈ£×AÐ£×AØ£×Aà£×Aè£×Að£×Aø£×B QìBQìBQìB
+QìBQìBQìBQìB
+QìB QìB$QìB(QìB,QìB0QìB4QìB8QìB<QìB@QìBDQìBHQìBLQìBPQìBTQìBXQìB\QìB`QìBdQìBhQìBlQìBpQìBtQìBxQìB|Qì=\)?õÃ@zá@Dzá@=q@¢=q@Â=q@â=qA
+žA
+žA!
+žA1
+žAA
+žAQ
+žAa
+žAq
+žA\A\A\A\A \Aš\A°\Až\AÀ\AÈ\AÐ\AØ\Aà\Aè\Að\Aø\B G®BG®BG®B
+G®BG®BG®BG®B
+G®B G®B$G®B(G®B,G®B0G®B4G®B8G®B<G®B@G®BDG®BHG®BLG®BPG®BTG®BXG®B\G®B`G®BdG®BhG®BlG®BpG®BtG®BxG®B|G®=uÂ?®@×
+@C×
+@ë
+@¡ë
+@Áë
+@áë
+A õÃAõÃA õÃA0õÃA@õÃAPõÃA`õÃApõÃAzáAzáAzáAzáA záAšzáA°záAžzáAÀzáAÈzáAÐzáAØzáAàzáAèzáAðzáAøzáB =qB=qB=qB
+=qB=qB=qB=qB
+=qB =qB$=qB(=qB,=qB0=qB4=qB8=qB<=qB@=qBD=qBH=qBL=qBP=qBT=qBX=qB\=qB`=qBd=qBh=qBl=qBp=qBt=qBx=qB|=q=LÌÍ?ff@33@C33@@¡@Á@áA ÌÍAÌÍA ÌÍA0ÌÍA@ÌÍAPÌÍA`ÌÍApÌÍAffAffAffAffA ffAšffA°ffAžffAÀffAÈffAÐffAØffAàffAèffAðffAøffB 33B33B33B
+33B33B33B33B
+33B 33B$33B(33B,33B033B433B833B<33B@33BD33BH33BL33BP33BT33BX33B\33B`33Bd33Bh33Bl33Bp33Bt33Bx33B|33=#×
+?
+
+ž@\@B\@G®@¡G®@ÁG®@áG®A £×A£×A £×A0£×A@£×AP£×A`£×Ap£×AQìAQìAQìAQìA QìAšQìA°QìAžQìAÀQìAÈQìAÐQìAØQìAàQìAèQìAðQìAøQìB (öB(öB(öB
+(öB(öB(öB(öB
+(öB (öB$(öB((öB,(öB0(öB4(öB8(öB<(öB@(öBD(öBH(öBL(öBP(öBT(öBX(öB\(öB`(öBd(öBh(öBl(öBp(öBt(öBx(öB|(ö<õÂ?×
+@ë
+@Aë
+@õÃ@ õÃ@ÀõÃ@àõÃA záAzáA záA0záA@záAPzáA`záApzáA=qA=qA=qA=qA =qAš=qA°=qAž=qAÀ=qAÈ=qAÐ=qAØ=qAà=qAè=qAð=qAø=qB 
+žB
+žB
+žB
+
+žB
+žB
+žB
+žB
+
+žB 
+žB$
+žB(
+žB,
+žB0
+žB4
+žB8
+žB<
+žB@
+žBD
+žBH
+žBL
+žBP
+žBT
+žBX
+žB\
+žB`
+žBd
+žBh
+žBl
+žBp
+žBt
+žBx
+žB|
+ž<£×
+?\@G®@AG®@£×@ £×@À£×@à£×A QìAQìA QìA0QìA@QìAPQìA`QìApQìA(öA(öA(öA(öA (öAš(öA°(öAž(öAÀ(öAÈ(öAÐ(öAØ(öAà(öAè(öAð(öAø(öB {B{B{B
+{B{B{B{B
+{B {B${B({B,{B0{B4{B8{B<{B@{BD{BH{BL{BP{BT{BX{B\{B`{Bd{Bh{Bl{Bp{Bt{Bx{B|{<#×
+?G®@ £×@@£×@Qì@ Qì@ÀQì@àQìA (öA(öA (öA0(öA@(öAP(öA`(öAp(öA{A{A{A{A {Aš{A°{Až{AÀ{AÈ{AÐ{AØ{Aà{Aè{Að{Aø{B 
+=B
+=B
+=B
+
+=B
+=B
+=B
+=B
+
+=B 
+=B$
+=B(
+=B,
+=B0
+=B4
+=B8
+=B<
+=B@
+=BD
+=BH
+=BL
+=BP
+=BT
+=BX
+=B\
+=B`
+=Bd
+=Bh
+=Bl
+=Bp
+=Bt
+=Bx
+=B|
+=    ?  @   @@  @  @   @À  @à  A   A  A   A0  A@  AP  A`  Ap  A  A  A  A  A   Aš  A°  Až  AÀ  AÈ  AÐ  AØ  Aà  Aè  Að  Aø  B   B  B  B
+  B  B  B  B
+  B   B$  B(  B,  B0  B4  B8  B<  B@  BD  BH  BL  BP  BT  BX  B\  B`  Bd  Bh  Bl  Bp  Bt  Bx  B|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+žR?Ð£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+(ö?Ï\)?Ï\)@(Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+=?ÌÌÍ?Î{@'®@g®@(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?zá?Ë
+
+?Ë
+
+@&ff@g
+>@×
+@×
+@Ž(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?ë
+?ÈõÂ?Ê=p@%Â@eÂ@33@
+@³×
+@Ó×
+@Ô(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+ÌÍ?Ç®?ÈõÂ@$zá@e
+ž@áH@áH@³33@Ó
+@Ó×
+@ó×
+A
+{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+=q?Å
+ž?Æff@#×
+@dzá@=q@\@²áH@ÒáH@Ó33@ó
+A	ë
+Aë
+A{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+ž?Ã×
+?Å
+ž@"\@c33@ë
+@=q@²=q@Ò\@ÒáH@òáHA	AÂAë
+A)ë
+A:{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?\?Â\?Â\@!ë
+@b\@G®@@±ë
+@Ò=q@Ò=q@ò\A	p€Ap€AA)ÂA9ë
+A9ë
+AJ{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?   ?À  ?ÁG®@!G®@aG®@õÂ@G®@±G®@Ñ@Ñë
+@ò=qA	
+žAG®Ap€A)p€A9A9ÂAIë
+AYë
+AZ{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >õÂ?ŸžR?À  @   @`£×@£×@£×@°õÂ@ÑG®@ÑG®@ñAõÃA
+žA
+žA)G®A9p€A9p€AIAYÂAYë
+Aië
+Az{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ð£×?Œ(ö?œp€@\)@`  @  @Qì@°£×@Ð£×@ÐõÂ@ñG®A£×AÌÍAõÃA)
+žA9
+žA9G®AIp€AYp€AYAiÂAyë
+AõÃA
+
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >æff?ºáH?Œ(ö@
+{@^žR@®@  @°  @ÐQì@Ð£×@ð£×AzáA£×A£×A(ÌÍA8õÃA9
+žAI
+žAYG®AYp€Aip€AyAáHAõÃAõÃA
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >áG®?¹?¹@
+p€@^{@
+=@\)@¯®@Ð  @Ð  @ðQìAQìAQìAzáA(£×A8£×A8ÌÍAHõÃAY
+žAY
+žAiG®Ayp€AžRAÌÍAáHAõÃAõÃA
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >×
+=?·
+>?žQì@
+ÌÍ@\ÌÍ@žR@
+=@¯
+=@Ï\)@Ï®@ð  A  A(öAQìA(QìA8záA8£×AH£×AXÌÍAXõÃAi
+žAy
+žA£×AžRAžRAÌÍAáHAõÃA€õÃA­
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ñë
+?µÂ?µÂ@
+@\(ö@ff@ff@®žR@Ï
+=@Ï
+=@ï\)A×
+A  A  A((öA8QìA8QìAHzáAX£×AX£×AhÌÍAxõÃA\A\A£×AžRAžRAÌÍA€áHA¬õÃA¬õÃAµ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ÌÌÍ?³33?Žzá@áH@ZáH@Â@{@®ff@Îff@ÎžR@ï
+=A
+A®A×
+A(  A8  A8(öAHQìAXQìAXzáAh£×Ax£×AffAzáA\A\A£×AžRA€žRA¬ÌÍA¬áHAŽõÃAŒõÃAœ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Â\?±ë
+?³33@@Z=q@p€@p€@­Â@Î{@Îff@îffA\)A
+A
+A'®A7×
+A8  AH  AX(öAXQìAhQìAxzáAQìAQìAffAzáA\A\A€£×A¬žRA¬žRAŽÌÍAŒáHAŒõÃAÄõÃAÍ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >œp€?°£×?°£×@õÂ@Y@ÌÍ@
+ž@­p€@Íp€@ÍÂ@î{A33A33A\)A'
+A7
+A7®AG×
+AX  AX  Ah(öAxQìA(öA=qAQìAQìAffAzáA€\A¬\A¬£×AŽžRAŒžRAŒÌÍAÄáHAÌõÃAÌõÃAÕ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >³33?®{?¯\)@Qì@XQì@zá@ÌÍ@¬ÌÍ@Í
+ž@Íp€@íp€AáHA
+=A33A'33A7\)A7
+AG
+AW®AW×
+Ah  Ax  A{A(öA(öA=qAQìAQìA€ffA¬záA¬\AŽ\AŒ£×AŒžRAÄžRAÌÌÍAÌáHAÔõÃAÜõÃAå
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >®{?¬ÌÍ?¬ÌÍ@
+>@W®@(ö@(ö@¬zá@ÌÌÍ@ÌÌÍ@í
+žAžRAžRAáHA'
+=A733A733AG\)AW
+AW
+Ag®Aw×
+A  A  A{A(öA(öA=qA€QìA¬QìA¬ffAŽzáAŒ\AŒ\AÄ£×AÌžRAÌžRAÔÌÍAÜáHAäõÃAäõÃAí
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >šõÃ?ª=q?«
+@ff@Vff@
+@×
+@¬(ö@Ì(ö@Ìzá@ìÌÍAffA\AžRA&žRA6áHA7
+=AG33AW33AW\)Ag
+Aw
+A×
+Aë
+A  A  A{A(öA€(öA¬=qA¬QìAŽQìAŒffAŒzáAÄ\AÌ\AÌ£×AÔžRAÜžRAäÌÍAäáHAìõÃAôõÃAõ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >žR?šõÂ?ª=q@
+ž@UÂ@33@33@«
+@Ë×
+@Ì(ö@ì(öA=qAffAffA&\A6žRA6žRAFáHAW
+=AW33Ag33Aw\)AÂAÂA×
+Aë
+A  A  A€{A¬(öA¬(öAŽ=qAŒQìAŒQìAÄffAÌzáAÌ\AÔ\AÜ£×AäžRAäžRAìÌÍAôáHAôõÃAüõÃB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >?Šff?§®@zá@U
+ž@\@áH@«33@Ë33@Ë
+@ë×
+A{A{A=qA&ffA6ffA6\AFžRAVžRAVáHAg
+=Aw33AA®AÂAÂA×
+Aë
+A€  A¬  A¬{AŽ(öAŒ(öAŒ=qAÄQìAÌQìAÌffAÔzáAÜ\Aä\Aä£×AìžRAôžRAôÌÍAüáHBzáBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >\)?¥
+ž?Šff@33@S×
+@=q@\@ª\@ÊáH@Ë33@ë33AÂAë
+A{A&{A6=qA6ffAFffAV\AVžRAfžRAváHA
+AAA®AÂAÂA£×
+A«ë
+A¬  AŽ  AŒ{AŒ(öAÄ(öAÌ=qAÌQìAÔQìAÜffAäzáAä\Aì\Aô£×AôžRAüžRBffBp€BzáB
+záB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >=q?£×
+?£×
+@\@S33@@ë
+@ª=q@Ê\@Ê\@êáHAAAÂA%ë
+A6{A6{AF=qAVffAVffAf\AvžRA\)Ap€A
+AAA®A£ÂA«ÂA«×
+A³ë
+AŒ  AŒ  AÄ{AÌ(öAÌ(öAÔ=qAÜQìAäQìAäffAìzáAô\Aô\Aü£×B\)B\)BffB
+p€BzáBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >  ?¡G®?¢\@ë
+@Që
+@G®@@©@Éë
+@Ê=q@ê\AG®Ap€AA%A5ÂA5ë
+AF{AV{AV=qAfffAvffAG®A\)A\)Ap€A
+AA£A«®A«ÂA³ÂA»×
+A»ë
+AÄ  AÌ  AÌ{AÔ(öAÜ(öAä=qAäQìAìQìAôffAôzáAü\BG®BQìB\)B
+\)BffBp€BzáBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >uÂ?   ?   @£×@QG®@õÃ@õÃ@©G®@É@É@éë
+A
+žAG®AG®A%p€A5A5AEÂAUë
+AV{Af{Av=qA33A33AG®A\)A\)Ap€A£
+A«A«A³®A»ÂA»ÂAÃ×
+AËë
+AÌ  AÔ  AÜ{Aä(öAä(öAì=qAôQìAôQìAüffB=qBG®BG®B
+QìB\)B\)BffBp€BzáBzáB
+
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >k
+?p€?žR@  @P  @Qì@£×@šõÃ@ÈõÃ@ÉG®@éAÌÍAõÃA
+žA%G®A5G®A5p€AEAUAUÂAeë
+Av{A
+=A
+žA33A33AG®A\)A£\)A«p€A«
+A³A»A»®AÃÂAËÂAË×
+AÓë
+AÜ  Aä  Aä{Aì(öAô(öAô=qAüQìB(öB33B=qB
+G®BG®BQìB\)B\)BffBp€B
+záB"záB"
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >W
+=?(ö?p€@žR@O\)@  @  @šQì@È£×@ÈõÃ@èõÃA£×AÌÍAÌÍA$õÃA5
+žA5G®AEG®AUp€AUAeAuÂAõÃA
+=A
+=A
+žA33A33A£G®A«\)A«\)A³p€A»
+A»AÃAË®AËÂAÓÂAÛ×
+Aãë
+Aä  Aì  Aô{Aô(öAü(öB
+žB(öB(öB
+33B=qBG®BG®BQìB\)B\)B
+ffB"p€B"záB&záB*
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >LÌÍ?áH?áH@{@NžR@\)@®@š  @È  @ÈQì@è£×AzáAzáA£×A$ÌÍA4ÌÍA4õÃAE
+žAUG®AUG®Aep€AuAÌÍAáHAõÃA
+=A
+=A
+žA£33A«33A«G®A³\)A»\)A»p€AÃ
+AËAËAÓ®AÛÂAãÂAã×
+Aëë
+Aô  Aô  Aü{B{B{B
+žB
+(öB(öB33B=qBG®BG®BQìB
+\)B"\)B"ffB&p€B*záB*záB.
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >8Qì?Qì?@
+p€@Mp€@
+=@\)@§\)@Ç®@È  @è  A(öAQìAzáA$záA4£×A4ÌÍADÌÍATõÃAU
+žAeG®AuG®AžRAÌÍAÌÍAáHAõÃA
+=A£
+=A«
+žA«33A³33A»G®A»\)AÃ\)AËp€AË
+AÓAÛAã®AãÂAëÂAó×
+Aóë
+Aü  B  B
+=B{B
+{B
+žB(öB(öB33B=qBG®B
+G®B"QìB"\)B&\)B*ffB*p€B.záB2záB6
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >.{?
+>?
+>@
+(ö@LÌÍ@žR@žR@§
+=@Ç\)@Ç\)@ç®A  A  A(öA$QìA4záA4záAD£×ATÌÍATÌÍAdõÃAu
+žA£×A£×AžRAÌÍAÌÍAáHA¢õÃA«
+=A«
+=A³
+žA»33A»33AÃG®AË\)AË\)AÓp€AÛ
+AãAãAë®AóÂAóÂAû×
+BõÃB  B  B
+
+=B{B{B
+žB(öB(öB33B
+=qB"G®B"G®B&QìB*\)B*\)B.ffB2p€B6záB6záB:
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >#×
+?zá?Â@
+
+@K
+@{@ff@ŠžR@ÆžR@Ç
+=@ç\)A®A×
+A  A$  A4(öA4QìADzáATzáAT£×AdÌÍAtÌÍAzáA\A£×A£×AžRAÌÍA¢ÌÍAªáHAªõÃA³
+=A»
+=A»
+žAÃ33AË33AËG®AÓ\)AÛ\)Aãp€Aã
+AëAóAó®AûÂBáHBë
+BõÃB
+  B  B
+=B{B{B
+žB(öB
+(öB"33B"=qB&G®B*G®B*QìB.\)B2\)B6ffB6p€B:záB>záB>
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >\)?33?zá@
+=q@JáH@
+Â@
+Â@Š{@Æff@ÆžR@æžRA
+A®A®A#×
+A4  A4  AD(öATQìATzáAdzáAt£×AffAffAzáA\A£×A£×A¢žRAªÌÍAªÌÍA²áHAºõÃA»
+=AÃ
+=AË
+žAË33AÓ33AÛG®Aã\)Aã\)Aëp€Aó
+AóAûB×
+BáHBáHB	ë
+B
+õÃB  B  B
+=B{B{B
+
+žB"(öB"(öB&33B*=qB*G®B.G®B2QìB6\)B6\)B:ffB>p€B>záBBzáBF
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >
+ž?£×?ë
+@	@J=q@
+
+ž@
+p€@¥Â@ÅÂ@Æ{@æffA\)A\)A
+A#®A3®A3×
+AD  AT  AT(öAdQìAtzáA=qAQìAffAffAzáA\A¢£×Aª£×AªžRA²ÌÍAºÌÍAºáHAÂõÃAË
+=AË
+=AÓ
+žAÛ33Aã33AãG®Aë\)Aó\)Aóp€Aû
+BÌÍBÌÍB×
+B	áHB
+áHB
+ë
+BõÃB  B  B
+=B
+{B"{B"
+žB&(öB*(öB*33B.=qB2G®B6G®B6QìB:\)B>\)B>ffBBp€BFzáBJzáBJ
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =áG®?\)?£×@Qì@HõÃ@ÌÍ@
+
+ž@¥
+ž@Åp€@ÅÂ@åÂA
+=A33A\)A#\)A3
+A3®AC®AS×
+AT  Ad  At(öA(öA=qA=qAQìAffAffA¢záAª\Aª£×A²£×AºžRAºÌÍAÂÌÍAÊáHAÊõÃAÓ
+=AÛ
+=Aã
+žAã33Aë33AóG®Aó\)Aû\)BžRBÂBÌÍB	ÌÍB
+×
+B
+áHBáHBë
+BõÃB  B
+  B"
+=B"{B&{B*
+žB*(öB.(öB233B6=qB6G®B:G®B>QìB>\)BB\)BFffBJp€BJzáBNzáBR
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =ÌÌÍ?{?{@®@HQì@(ö@zá@€ÌÍ@Å
+ž@Å
+ž@åp€AáHAáHA
+=A#33A3\)A3\)AC
+AS®AS®Ac×
+At  A  A{A(öA=qA=qAQìA¢ffAªffAªzáA²\Aº£×Aº£×AÂžRAÊÌÍAÊÌÍAÒáHAÚõÃAã
+=Aã
+=Aë
+žAó33Aó33AûG®B®B®BžRB	ÂB
+ÌÍB
+ÌÍB×
+BáHBáHBë
+B
+õÃB"  B"  B&
+=B*{B*{B.
+žB2(öB6(öB633B:=qB>G®B>G®BBQìBF\)BJ\)BJffBNp€BRzáBRzáBV
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =žQì?
+?ÌÍ@
+=@G
+=@×
+@(ö@€(ö@Äzá@ÄÌÍ@å
+žA\AžRAáHA"áHA3
+=A333AC\)AS\)AS
+Ac®As®Aë
+A  A  A{A(öA=qA¢=qAªQìAªffA²ffAºzáAº\AÂ£×AÊ£×AÊžRAÒÌÍAÚÌÍAâáHAâõÃAë
+=Aó
+=Aó
+žAû33BB£×B®B	®B
+žRB
+ÂBÌÍBÌÍB×
+BáHB
+áHB!ë
+B!õÃB&  B*  B*
+=B.{B2{B6
+žB6(öB:(öB>33B>=qBBG®BFG®BJQìBJ\)BN\)BRffBRp€BVzáBZzáBZ
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =\)?=q?
+@Â@Fff@
+@
+@£×
+@Ä(ö@Ä(ö@äzáAffA\A\A"žRA2áHA2áHAC
+=AS33AS\)Ac\)As
+A×
+A×
+Aë
+A  A  A{A¢(öAª=qAª=qA²QìAºffAºffAÂzáAÊ\AÊ£×AÒ£×AÚžRAâÌÍAâÌÍAêáHAòõÃAó
+=Aû
+=B\BBB	£×B
+®B
+®BžRBÂBÌÍBÌÍB
+×
+B!áHB!áHB%ë
+B)õÃB*  B.  B2
+=B6{B6{B:
+žB>(öB>(öBB33BF=qBJG®BJG®BNQìBR\)BR\)BVffBZp€BZzáB^záBb
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =uÂ?®?õÃ@
+ž@EÂ@áH@33@£
+@Ã
+@Ã×
+@ä(öA{A=qAffA"\A2\A2žRABáHARáHAS
+=Ac33As\)A®AÂA×
+A×
+Aë
+A  A¢  Aª{Aª(öA²=qAº=qAºQìAÂffAÊffAÊzáAÒ\AÚ£×Aâ£×AâžRAêÌÍAòÌÍAòáHAúõÃB
+B
+B\B	B
+B
+£×B®B®BžRBÂB
+ÌÍB!ÌÍB!×
+B%áHB)áHB)ë
+B-õÃB2  B6  B6
+=B:{B>{B>
+žBB(öBF(öBJ33BJ=qBNG®BRG®BRQìBV\)BZ\)BZffB^p€BbzáBfzáBf
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =#×
+?ff?®@×
+@Dzá@\@áH@¢áH@Ã33@Ã
+@ã
+Aë
+A{A{A"=qA2ffA2\AB\ARžRARáHAbáHAs
+=AA®A®AÂA×
+A×
+A¡ë
+Aª  Aª  A²{Aº(öAº=qAÂ=qAÊQìAÊffAÒffAÚzáAâ\Aâ£×Aê£×AòžRAòÌÍAúÌÍBp€BzáB
+B	
+B
+\B
+BB£×B®B®B
+žRB!ÂB!ÌÍB%ÌÍB)×
+B)áHB-áHB1ë
+B5õÃB6  B:  B>
+=B>{BB{BF
+žBJ(öBJ(öBN33BR=qBRG®BVG®BZQìBZ\)B^\)BbffBfp€BfzáBjzáBn
+¿  ¿  ¿  ¿  ¿  ¿  <õÂ?
+
+ž?
+
+ž@33@C×
+@ë
+@=q@¢\@ÂáH@ÂáH@ã33AÂAÂAë
+A"{A2{A2=qABffAR\AR\AbžRAráHAp€A
+AA®A®AÂA¡×
+A©×
+A©ë
+A²  Aº  Aº{AÂ(öAÊ=qAÊ=qAÒQìAÚffAâffAâzáAê\Aò£×Aò£×AúžRBffBffBp€B	záB
+
+B
+
+B\BBB£×B
+®B!®B!žRB%ÂB)ÌÍB)ÌÍB-×
+B1áHB5áHB5ë
+B9õÃB>  B>  BB
+=BF{BJ{BJ
+žBN(öBR(öBR33BV=qBZG®BZG®B^QìBb\)Bf\)BfffBjp€BnzáBnzáBr
+¿  ¿  ¿  ¿  <#×
+?\?×
+@\@B\@@ë
+@¡ë
+@Â=q@Â\@âáHAp€AAÂA!ÂA1ë
+A2{AB{AR=qARffAb\Ar\A\)Ap€Ap€A
+AA®A¡®A©ÂA©×
+A±×
+A¹ë
+Aº  AÂ  AÊ{AÊ(öAÒ=qAÚ=qAâQìAâffAêffAòzáAò\Aú£×BQìB\)BffB	ffB
+p€B
+záB
+B
+B\BB
+B!£×B!®B%®B)žRB)ÂB-ÌÍB1ÌÍB5×
+B5áHB9áHB=ë
+B=õÃBB  BF  BJ
+=BJ{BN{BR
+žBR(öBV(öBZ33BZ=qB^G®BbG®BfQìBf\)Bj\)BnffBnp€BrzáBvzáBz
+¿  ¿      ?G®?G®@G®@Aë
+@G®@G®@¡@Áë
+@Áë
+@â=qAG®Ap€Ap€A!A1ÂA1ÂAAë
+AR{AR{Ab=qArffAG®AG®A\)Ap€Ap€A
+A¡A©®A©®A±ÂA¹×
+A¹×
+AÁë
+AÊ  AÊ  AÒ{AÚ(öAâ=qAâ=qAêQìAòffAòffAúzáBG®BQìBQìB	\)B
+ffB
+ffBp€BzáB
+B
+B
+\B!B!B%£×B)®B)®B-žRB1ÂB5ÌÍB5ÌÍB9×
+B=áHB=áHBAë
+BEõÃBJ  BJ  BN
+=BR{BR{BV
+žBZ(öBZ(öB^33Bb=qBfG®BfG®BjQìBn\)Bn\)BrffBvp€BzzáBzzáB~
+¿  ¿  ?  @ £×@@£×@£×@õÃ@¡G®@ÁG®@Á@áë
+A õÃA
+žAG®A!p€A1p€A1AAÂAQÂAQë
+Ab{Ar{A
+žA33AG®AG®A\)Ap€A¡p€A©
+A©A±®A¹®A¹ÂAÁ×
+AÉ×
+AÉë
+AÒ  AÚ  Aâ{Aâ(öAê=qAò=qAòQìAúffB33B=qBG®B	QìB
+QìB
+\)BffBffBp€BzáB
+
+B!
+B!\B%B)B)£×B-®B1®B5žRB5ÂB9ÌÍB=ÌÍB=×
+BAáHBEáHBIë
+BIõÃBN  BR  BR
+=BV{BZ{BZ
+žB^(öBb(öBf33Bf=qBjG®BnG®BnQìBr\)Bv\)BzffBzp€B~zá¿  ¿  ¿  ¿  @@  @Qì@Qì@ £×@ÀõÃ@ÁG®@áG®A ÌÍAõÃAõÃA!
+žA1G®A1p€AAp€AQAQÂAaÂAqë
+A
+=A
+=A
+žA33AG®AG®A¡\)A©p€A©p€A±
+A¹A¹®AÁ®AÉÂAÉ×
+AÑ×
+AÙë
+Aâ  Aâ  Aê{Aò(öAò=qAú=qB(öB33B33B	=qB
+G®B
+QìBQìB\)BffBffB
+p€B!záB!
+B%
+B)\B)B-B1£×B5®B5®B9žRB=ÂB=ÌÍBAÌÍBE×
+BIáHBIáHBMë
+BQõÃBR  BV  BZ
+=BZ{B^{Bb
+žBf(öBf(öBj33Bn=qBnG®BrG®BvQìBz\)Bz\)B~ff¿  ¿  ¿  ¿  ¿  ¿  @  @ Qì@ÀQì@À£×@àõÃA £×A£×AÌÍA õÃA0õÃA1
+žAAG®AQp€AQp€AaAqÂAáHAõÃA
+=A
+=A
+žA33A¡G®A©G®A©\)A±p€A¹p€A¹
+AÁAÉ®AÉ®AÑÂAÙ×
+Aá×
+Aáë
+Aê  Aò  Aò{Aú(öB
+žB
+žB(öB	33B
+33B
+=qBG®BQìBQìB\)B
+ffB!ffB!p€B%záB)
+B)
+B-\B1B5B5£×B9®B=®B=žRBAÂBEÌÍBIÌÍBI×
+BMáHBQáHBQë
+BUõÃBZ  BZ  B^
+=Bb{Bf{Bf
+žBj(öBn(öBn33Br=qBvG®BzG®BzQìB~\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @À  @ÀQì@àQìA QìAzáA£×A £×A0ÌÍA0õÃA@õÃAQ
+žAQG®Aap€Aqp€AÌÍAáHAáHAõÃA
+=A
+=A¡
+žA©33A©G®A±G®A¹\)A¹p€AÁp€AÉ
+AÉAÑ®AÙ®AáÂAá×
+Aé×
+Añë
+Aò  Aú  B
+=B{B
+žB	
+žB
+(öB
+33B33B=qBG®BQìB
+QìB!\)B!ffB%ffB)p€B)záB-
+B1
+B5\B5B9B=£×B=®BA®BEžRBIÂBIÌÍBMÌÍBQ×
+BQáHBUáHBYë
+BYõÃB^  Bb  Bf
+=Bf{Bj{Bn
+žBn(öBr(öBv33Bz=qBzG®B~G®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @à  A (öA(öAQìA záA0£×A0£×A@ÌÍAPõÃAPõÃAa
+žAqG®AžRAžRAÌÍAáHAáHAõÃA¡
+=A©
+=A©
+žA±33A¹G®A¹G®AÁ\)AÉp€AÉp€AÑ
+AÙAá®Aá®AéÂAñ×
+Añ×
+Aùë
+B  B  B
+=B	{B
+
+žB
+
+žB(öB33B33B=qB
+G®B!QìB!QìB%\)B)ffB)ffB-p€B1záB5
+B5
+B9\B=B=BA£×BE®BI®BIžRBMÂBQÌÍBQÌÍBU×
+BYáHBYáHB]ë
+BaõÃBf  Bf  Bj
+=Bn{Bn{Br
+žBv(öBz(öBz33B~=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A(öA (öA0QìA0záA@£×AP£×APÌÍA`õÃApõÃA\A£×AžRAžRAÌÍAáHA áHAšõÃA©
+=A±
+=A¹
+žA¹33AÁG®AÉG®AÉ\)AÑp€AÙp€Aá
+AáAé®Añ®AñÂAù×
+B ë
+BõÃB  B	  B
+=B
+{B
+žB
+žB(öB33B
+33B!=qB!G®B%QìB)QìB)\)B-ffB1ffB5p€B5záB9
+B=
+B=\BABEBI£×BI®BM®BQžRBQÂBUÌÍBYÌÍBY×
+B]áHBaáHBeë
+BeõÃBj  Bn  Bn
+=Br{Bv{Bz
+žBz(öB~(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A   A0(öA0(öA@QìAPzáAP£×A`£×ApÌÍAzáAzáA\A£×AžRAžRA ÌÍAšáHAšáHA°õÃA¹
+=A¹
+=AÁ
+žAÉ33AÉG®AÑG®AÙ\)Aáp€Aáp€Aé
+AñAñ®Aù®B áHBë
+Bë
+BõÃB
+  B
+  B
+=B{B
+žB
+žB
+(öB!33B!33B%=qB)G®B)QìB-QìB1\)B5ffB5ffB9p€B=záB=
+BA
+BE\BIBIBM£×BQ®BQ®BUžRBYÂBYÌÍB]ÌÍBa×
+BeáHBeáHBië
+BmõÃBn  Br  Bv
+=Bz{Bz{B~
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A0  A@(öAP(öAPQìA`záAp£×AQìAffAzáAzáA\A£×A žRAšžRAšÌÍA°áHAžáHAžõÃAÁ
+=AÉ
+=AÉ
+žAÑ33AÙG®AáG®Aá\)Aép€Añp€Añ
+AùB ×
+B×
+BáHBë
+B
+ë
+B
+õÃB  B  B
+=B{B
+
+žB!
+žB!(öB%33B)33B)=qB-G®B1QìB5QìB5\)B9ffB=ffB=p€BAzáBE
+BI
+BI\BMBQBQ£×BU®BY®BYžRB]ÂBaÌÍBeÌÍBe×
+BiáHBmáHBmë
+BqõÃBv  Bz  Bz
+=B~{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AP  AP(öA`(öApQìA=qAQìAQìAffAzáAzáA \Aš£×AšžRA°žRAžÌÍAžáHAÀáHAÈõÃAÉ
+=AÑ
+=AÙ
+žAá33AáG®AéG®Añ\)Añp€Aùp€B ÂBÌÍB×
+B×
+B
+áHB
+ë
+Bë
+BõÃB  B  B
+
+=B!{B!
+žB%
+žB)(öB)33B-33B1=qB5G®B5QìB9QìB=\)B=ffBAffBEp€BIzáBI
+BM
+BQ\BQBUBY£×BY®B]®BažRBeÂBeÌÍBiÌÍBm×
+BmáHBqáHBuë
+ByõÃBz  B~  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A`  Ap(öA{A(öA=qAQìAQìAffA záAšzáAš\A°£×AžžRAžžRAÀÌÍAÈáHAÈáHAÐõÃAÙ
+=Aá
+=Aá
+žAé33AñG®AñG®Aù\)B žRBžRBÂBÌÍB
+×
+B
+×
+BáHBë
+Bë
+BõÃB
+  B!  B!
+=B%{B)
+žB)
+žB-(öB133B533B5=qB9G®B=QìB=QìBA\)BEffBIffBIp€BMzáBQ
+BQ
+BU\BYBYB]£×Ba®Be®BežRBiÂBmÌÍBmÌÍBq×
+BuáHByáHByë
+B}õÃ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{A{A(öA=qAQìA QìAšffAšzáA°záAž\Až£×AÀžRAÈžRAÈÌÍAÐáHAØáHAàõÃAá
+=Aé
+=Añ
+žAñ33AùG®B £×B®BžRBžRB
+ÂB
+ÌÍB×
+B×
+BáHBë
+B
+ë
+B õÃB!  B%  B)
+=B){B-
+žB1
+žB5(öB533B933B==qB=G®BAQìBEQìBI\)BIffBMffBQp€BQzáBU
+BY
+BY\B]BaBe£×Be®Bi®BmžRBmÂBqÌÍBuÌÍBy×
+ByáHB}áH¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{A{A(öA =qAšQìAšQìA°ffAžzáAžzáAÀ\AÈ£×AÈžRAÐžRAØÌÍAàáHAàáHAèõÃAñ
+=Añ
+=Aù
+žB B£×B£×B®B
+žRB
+žRBÂBÌÍB×
+B×
+B
+áHB ë
+B ë
+B$õÃB)  B)  B-
+=B1{B5
+žB5
+žB9(öB=33B=33BA=qBEG®BIQìBIQìBM\)BQffBQffBUp€BYzáBY
+B]
+Ba\BeBeBi£×Bm®Bm®BqžRBuÂByÌÍByÌÍB}×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{A {Aš(öAš=qA°QìAžQìAžffAÀzáAÈzáAÈ\AÐ£×AØžRAàžRAàÌÍAèáHAðáHAðõÃAù
+=B 
+B\BB£×B
+£×B
+®BžRBžRBÂBÌÍB
+×
+B ×
+B áHB$ë
+B(ë
+B(õÃB-  B1  B5
+=B5{B9
+žB=
+žB=(öBA33BE33BI=qBIG®BMQìBQQìBQ\)BUffBYffBYp€B]záBa
+Be
+Be\BiBmBm£×Bq®Bu®ByžRByÂB}ÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A   Aš{Aš{A°(öAž=qAžQìAÀQìAÈffAÈzáAÐzáAØ\Aà£×AàžRAèžRAðÌÍAðáHAøáHB záB
+B
+B\B
+B
+£×B£×B®BžRBžRB
+ÂB ÌÍB ×
+B$×
+B(áHB(ë
+B,ë
+B0õÃB5  B5  B9
+=B={B=
+žBA
+žBE(öBI33BI33BM=qBQG®BQQìBUQìBY\)BYffB]ffBap€BezáBe
+Bi
+Bm\BmBqBu£×By®By®B}žR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aš  A°{Až{Až(öAÀ=qAÈQìAÈQìAÐffAØzáAàzáAà\Aè£×AðžRAðžRAøÌÍB p€Bp€BzáB
+B
+
+B
+\BB£×B£×B®B
+žRB žRB ÂB$ÌÍB(×
+B(×
+B,áHB0ë
+B4ë
+B4õÃB9  B=  B=
+=BA{BE
+žBI
+žBI(öBM33BQ33BQ=qBUG®BYQìBYQìB]\)BaffBeffBep€BizáBm
+Bm
+Bq\BuByBy£×B}®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Až  Až{AÀ{AÈ(öAÈ=qAÐQìAØQìAàffAàzáAèzáAð\Að£×AøžRB \)BffBp€Bp€B
+záB
+
+B
+B\BB£×B
+£×B ®B žRB$žRB(ÂB(ÌÍB,×
+B0×
+B4áHB4ë
+B8ë
+B<õÃB=  BA  BE
+=BI{BI
+žBM
+žBQ(öBQ33BU33BY=qBYG®B]QìBaQìBe\)BeffBiffBmp€BmzáBq
+Bu
+By\ByB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÀ  AÈ{AÈ{AÐ(öAØ=qAàQìAàQìAèffAðzáAðzáAø\B QìB\)B\)BffB
+p€B
+p€BzáB
+B
+B\B
+B £×B £×B$®B(žRB(žRB,ÂB0ÌÍB4×
+B4×
+B8áHB<ë
+B<ë
+B@õÃBE  BI  BI
+=BM{BQ
+žBQ
+žBU(öBY33BY33B]=qBaG®BeQìBeQìBi\)BmffBmffBqp€BuzáBy
+By
+B}\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÈ  AÐ{AØ{Aà(öAà=qAèQìAðQìAðffAøzáB =qBG®BQìB\)B
+\)B
+ffBp€Bp€BzáB
+B
+
+B \B B$£×B(£×B(®B,žRB0žRB4ÂB4ÌÍB8×
+B<×
+B<áHB@ë
+BDë
+BHõÃBI  BM  BQ
+=BQ{BU
+žBY
+žBY(öB]33Ba33Be=qBeG®BiQìBmQìBm\)BqffBuffByp€ByzáB}
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AØ  Aà{Aà{Aè(öAð=qAðQìAøQìB 33B=qB=qBG®B
+QìB
+\)B\)BffBp€Bp€B
+záB 
+B 
+B$\B(B(£×B,£×B0®B4žRB4žRB8ÂB<ÌÍB<×
+B@×
+BDáHBHë
+BHë
+BLõÃBQ  BQ  BU
+=BY{BY
+žB]
+žBa(öBe33Be33Bi=qBmG®BmQìBqQìBu\)ByffByffB}p€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aà  Aè{Að{Að(öAø=qB (öB(öB33B=qB
+=qB
+G®BQìB\)B\)BffB
+p€B p€B záB$
+B(
+B(\B,B0£×B4£×B4®B8žRB<žRB<ÂB@ÌÍBD×
+BH×
+BHáHBLë
+BPë
+BPõÃBU  BY  BY
+=B]{Ba
+žBe
+žBe(öBi33Bm33Bm=qBqG®BuQìByQìBy\)B}ff¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Að  Að{Aø{B {B
+žB(öB(öB
+33B
+=qB=qBG®BQìB\)B
+\)B ffB p€B$p€B(záB(
+B,
+B0\B4B4£×B8£×B<®B<žRB@žRBDÂBHÌÍBH×
+BL×
+BPáHBPë
+BTë
+BXõÃBY  B]  Ba
+=Be{Be
+žBi
+žBm(öBm33Bq33Bu=qByG®ByQìB}Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aø  B 
+=B
+=B{B
+žB
+(öB
+(öB33B=qB=qBG®B
+QìB \)B \)B$ffB(p€B(p€B,záB0
+B4
+B4\B8B<£×B<£×B@®BDžRBHžRBHÂBLÌÍBP×
+BP×
+BTáHBXë
+BXë
+B\õÃBa  Be  Be
+=Bi{Bm
+žBm
+žBq(öBu33By33By=qB}G®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+=B
+=B
+{B
+
+žB(öB(öB33B=qB
+=qB G®B QìB$\)B(\)B(ffB,p€B0p€B4záB4
+B8
+B<\B<B@£×BD£×BH®BHžRBLžRBPÂBPÌÍBT×
+BX×
+BXáHB\ë
+B`ë
+BdõÃBe  Bi  Bm
+=Bm{Bq
+žBu
+žBy(öBy33B}33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+
+=B
+
+=B{B
+žB(öB(öB
+33B =qB =qB$G®B(QìB(\)B,\)B0ffB4p€B4p€B8záB<
+B<
+B@\BDBH£×BH£×BL®BPžRBPžRBTÂBXÌÍBX×
+B\×
+B`áHBdë
+Bdë
+BhõÃBm  Bm  Bq
+=Bu{By
+žBy
+žB}(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+  B
+=B
+=B{B
+žB
+(öB (öB 33B$=qB(=qB(G®B,QìB0\)B4\)B4ffB8p€B<p€B<záB@
+BD
+BH\BHBL£×BP£×BP®BTžRBXžRBXÂB\ÌÍB`×
+Bd×
+BdáHBhë
+Blë
+BlõÃBq  Bu  By
+=By{B}
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+=B
+=B
+{B 
+žB (öB$(öB(33B(=qB,=qB0G®B4QìB4\)B8\)B<ffB<p€B@p€BDzáBH
+BH
+BL\BPBP£×BT£×BX®BXžRB\žRB`ÂBdÌÍBd×
+Bh×
+BláHBlë
+Bpë
+BtõÃBy  By  B}
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+
+=B 
+=B {B$
+žB((öB((öB,33B0=qB4=qB4G®B8QìB<\)B<\)B@ffBDp€BHp€BHzáBL
+BP
+BP\BTBX£×BX£×B\®B`žRBdžRBdÂBhÌÍBl×
+Bl×
+BpáHBtë
+Bxë
+BxõÃB}  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B   B 
+=B$
+=B({B(
+žB,(öB0(öB433B4=qB8=qB<G®B<QìB@\)BD\)BHffBHp€BLp€BPzáBP
+BT
+BX\BXB\£×B`£×Bd®BdžRBhžRBlÂBlÌÍBp×
+Bt×
+BxáHBxë
+B|ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B$  B(
+=B(
+=B,{B0
+žB4(öB4(öB833B<=qB<=qB@G®BDQìBH\)BH\)BLffBPp€BPp€BTzáBX
+BX
+B\\B`Bd£×Bd£×Bh®BlžRBlžRBpÂBtÌÍBx×
+Bx×
+B|áH¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B(  B,
+=B0
+=B4{B4
+žB8(öB<(öB<33B@=qBD=qBHG®BHQìBL\)BP\)BPffBTp€BXp€BXzáB\
+B`
+Bd\BdBh£×Bl£×Bl®BpžRBtžRBxÂBxÌÍB|×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B0  B4
+=B4
+=B8{B<
+žB<(öB@(öBD33BH=qBH=qBLG®BPQìBP\)BT\)BXffBXp€B\p€B`záBd
+Bd
+Bh\BlBl£×Bp£×Bt®BxžRBxžRB|Â¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B4  B8
+=B<
+=B<{B@
+žBD(öBH(öBH33BL=qBP=qBPG®BTQìBX\)BX\)B\ffB`p€Bdp€BdzáBh
+Bl
+Bl\BpBt£×Bx£×Bx®B|žR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B<  B<
+=B@
+=BD{BH
+žBH(öBL(öBP33BP=qBT=qBXG®BXQìB\\)B`\)BdffBdp€Bhp€BlzáBl
+Bp
+Bt\BxBx£×B|£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B@  BD
+=BH
+=BH{BL
+žBP(öBP(öBT33BX=qBX=qB\G®B`QìBd\)Bd\)BhffBlp€Blp€BpzáBt
+Bx
+Bx\B|¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BH  BH
+=BL
+=BP{BP
+žBT(öBX(öBX33B\=qB`=qBdG®BdQìBh\)Bl\)BlffBpp€Btp€BxzáBx
+B|
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BL  BP
+=BP
+=BT{BX
+žBX(öB\(öB`33Bd=qBd=qBhG®BlQìBl\)Bp\)BtffBxp€Bxp€B|zá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BP  BT
+=BX
+=BX{B\
+žB`(öBd(öBd33Bh=qBl=qBlG®BpQìBt\)Bx\)BxffB|p€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BX  BX
+=B\
+=B`{Bd
+žBd(öBh(öBl33Bl=qBp=qBtG®BxQìBx\)B|\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B\  B`
+=Bd
+=Bd{Bh
+žBl(öBl(öBp33Bt=qBx=qBxG®B|Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bd  Bd
+=Bh
+=Bl{Bl
+žBp(öBt(öBx33Bx=qB|=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bh  Bl
+=Bl
+=Bp{Bt
+žBx(öBx(öB|33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bl  Bp
+=Bt
+=Bx{Bx
+žB|(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bt  Bx
+=Bx
+=B|{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bx  B|
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 <#×
+<£×
+<õÂ=#×
+=LÌÍ=uÂ=\)=£×
+=žQì=ÌÌÍ=áG®=õÂ>
+ž>\)>>#×
+>.{>8Qì>B\>LÌÍ>W
+=>aG®>k
+>uÂ>  >
+
+ž>=q>\)>zá>>žR>£×
+>šõÃ>®{>³33>žQì>œp€>Â\>Ç®>ÌÌÍ>Ñë
+>×
+=>Ü(ö>áG®>æff>ë
+>ð£×>õÂ>úáH?   ?\?
+ž?®?
+=q?
+ÌÍ?\)?ë
+?zá?
+=??
+(ö?
+žR?!G®?  ?G®?\?×
+?
+
+ž?ff?®?õÃ?=q?
+?ÌÍ?{?\)?£×?ë
+?33?zá?Â?
+>?Qì??áH?(ö?p€?žR?   ?¡G®?¢\?£×
+?¥
+ž?Šff?§®?šõÂ?ª=q?«
+?¬ÌÍ?®{?¯\)?°£×?±ë
+?³33?Žzá?µÂ?·
+>?žQì?¹?ºáH?Œ(ö?œp€?ŸžR?À  ?ÁG®?Â\?Ã×
+?Å
+ž?Æff?Ç®?ÈõÂ?Ê=p?Ë
+
+?ÌÌÍ?Î{?Ï\)?Ð£×@   @ £×@G®@ë
+@\@33@×
+@zá@
+ž@Â@ff@
+=@®@Qì@õÃ@	@
+=q@
+áH@
+
+@
+(ö@
+ÌÍ@
+p€@{@žR@\)@  @£×@G®@ë
+@\@33@×
+@zá@
+ž@Â@ff@
+>@®@Qì@õÂ@@=q@áH@
+@
+(ö@
+ÌÍ@
+p€@
+{@
+žR@\)@   @ £×@!G®@!ë
+@"\@#33@#×
+@$zá@%
+ž@%Â@&ff@'
+>@'®@(Qì@@  @@£×@AG®@Aë
+@B\@C33@C×
+@Dzá@E
+ž@EÂ@Fff@G
+=@G®@HQì@HõÃ@I@J=q@JáH@K
+@L(ö@LÌÍ@Mp€@N{@NžR@O\)@P  @P£×@QG®@Që
+@R\@S33@S×
+@Tzá@U
+ž@UÂ@Vff@W
+>@W®@XQì@XõÂ@Y@Z=q@ZáH@[
+@\(ö@\ÌÍ@]p€@^{@^žR@_\)@`  @`£×@aG®@aë
+@b\@c33@c×
+@dzá@e
+ž@eÂ@fff@g
+>@g®@hQì@  @Qì@£×@õÃ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@zá@ÌÍ@
+
+ž@
+p€@
+Â@{@ff@žR@
+=@\)@®@  @Qì@£×@õÃ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@zá@ÌÍ@
+ž@p€@Â@{@ff@žR@
+=@\)@®@  @Qì@£×@õÂ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@   @ Qì@ £×@ õÃ@¡G®@¡@¡ë
+@¢=q@¢\@¢áH@£33@£
+@£×
+@€(ö@€zá@€ÌÍ@¥
+ž@¥p€@¥Â@Š{@Šff@ŠžR@§
+=@§\)@§®@š  @šQì@š£×@šõÃ@©G®@©@©ë
+@ª=q@ª\@ªáH@«33@«
+@«×
+@¬(ö@¬zá@¬ÌÍ@­
+ž@­p€@­Â@®{@®ff@®žR@¯
+=@¯\)@¯®@°  @°Qì@°£×@°õÂ@±G®@±@±ë
+@²=q@²\@²áH@³33@³
+@³×
+@Ž(ö@À  @ÀQì@À£×@ÀõÃ@ÁG®@Á@Áë
+@Â=q@Â\@ÂáH@Ã33@Ã
+@Ã×
+@Ä(ö@Äzá@ÄÌÍ@Å
+ž@Åp€@ÅÂ@Æ{@Æff@ÆžR@Ç
+=@Ç\)@Ç®@È  @ÈQì@È£×@ÈõÃ@ÉG®@É@Éë
+@Ê=q@Ê\@ÊáH@Ë33@Ë
+@Ë×
+@Ì(ö@Ìzá@ÌÌÍ@Í
+ž@Íp€@ÍÂ@Î{@Îff@ÎžR@Ï
+=@Ï\)@Ï®@Ð  @ÐQì@Ð£×@ÐõÂ@ÑG®@Ñ@Ñë
+@Ò=q@Ò\@ÒáH@Ó33@Ó
+@Ó×
+@Ô(ö@à  @àQì@à£×@àõÃ@áG®@á@áë
+@â=q@â\@âáH@ã33@ã
+@ã×
+@ä(ö@äzá@äÌÍ@å
+ž@åp€@åÂ@æ{@æff@æžR@ç
+=@ç\)@ç®@è  @èQì@è£×@èõÃ@éG®@é@éë
+@ê=q@ê\@êáH@ë33@ë
+@ë×
+@ì(ö@ìzá@ìÌÍ@í
+ž@íp€@íÂ@î{@îff@îžR@ï
+=@ï\)@ï®@ð  @ðQì@ð£×@ðõÂ@ñG®@ñ@ñë
+@ò=q@ò\@òáH@ó33@ó
+@ó×
+@ô(öA   A (öA QìA záA £×A ÌÍA õÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA	
+žA	G®A	p€A	A	ÂA	ë
+A
+{A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A   A (öA QìA záA £×A ÌÍA õÃA!
+žA!G®A!p€A!A!ÂA!ë
+A"{A"=qA"ffA"\A"žRA"áHA#
+=A#33A#\)A#
+A#®A#×
+A$  A$(öA$QìA$záA$£×A$ÌÍA$õÃA%
+žA%G®A%p€A%A%ÂA%ë
+A&{A&=qA&ffA&\A&žRA&áHA'
+=A'33A'\)A'
+A'®A'×
+A(  A((öA(QìA(záA(£×A(ÌÍA(õÃA)
+žA)G®A)p€A)A)ÂA)ë
+A*{A0  A0(öA0QìA0záA0£×A0ÌÍA0õÃA1
+žA1G®A1p€A1A1ÂA1ë
+A2{A2=qA2ffA2\A2žRA2áHA3
+=A333A3\)A3
+A3®A3×
+A4  A4(öA4QìA4záA4£×A4ÌÍA4õÃA5
+žA5G®A5p€A5A5ÂA5ë
+A6{A6=qA6ffA6\A6žRA6áHA7
+=A733A7\)A7
+A7®A7×
+A8  A8(öA8QìA8záA8£×A8ÌÍA8õÃA9
+žA9G®A9p€A9A9ÂA9ë
+A:{A@  A@(öA@QìA@záA@£×A@ÌÍA@õÃAA
+žAAG®AAp€AAAAÂAAë
+AB{AB=qABffAB\ABžRABáHAC
+=AC33AC\)AC
+AC®AC×
+AD  AD(öADQìADzáAD£×ADÌÍADõÃAE
+žAEG®AEp€AEAEÂAEë
+AF{AF=qAFffAF\AFžRAFáHAG
+=AG33AG\)AG
+AG®AG×
+AH  AH(öAHQìAHzáAH£×AHÌÍAHõÃAI
+žAIG®AIp€AIAIÂAIë
+AJ{AP  AP(öAPQìAPzáAP£×APÌÍAPõÃAQ
+žAQG®AQp€AQAQÂAQë
+AR{AR=qARffAR\ARžRARáHAS
+=AS33AS\)AS
+AS®AS×
+AT  AT(öATQìATzáAT£×ATÌÍATõÃAU
+žAUG®AUp€AUAUÂAUë
+AV{AV=qAVffAV\AVžRAVáHAW
+=AW33AW\)AW
+AW®AW×
+AX  AX(öAXQìAXzáAX£×AXÌÍAXõÃAY
+žAYG®AYp€AYAYÂAYë
+AZ{A`  A`(öA`QìA`záA`£×A`ÌÍA`õÃAa
+žAaG®Aap€AaAaÂAaë
+Ab{Ab=qAbffAb\AbžRAbáHAc
+=Ac33Ac\)Ac
+Ac®Ac×
+Ad  Ad(öAdQìAdzáAd£×AdÌÍAdõÃAe
+žAeG®Aep€AeAeÂAeë
+Af{Af=qAfffAf\AfžRAfáHAg
+=Ag33Ag\)Ag
+Ag®Ag×
+Ah  Ah(öAhQìAhzáAh£×AhÌÍAhõÃAi
+žAiG®Aip€AiAiÂAië
+Aj{Ap  Ap(öApQìApzáAp£×ApÌÍApõÃAq
+žAqG®Aqp€AqAqÂAqë
+Ar{Ar=qArffAr\AržRAráHAs
+=As33As\)As
+As®As×
+At  At(öAtQìAtzáAt£×AtÌÍAtõÃAu
+žAuG®Aup€AuAuÂAuë
+Av{Av=qAvffAv\AvžRAváHAw
+=Aw33Aw\)Aw
+Aw®Aw×
+Ax  Ax(öAxQìAxzáAx£×AxÌÍAxõÃAy
+žAyG®Ayp€AyAyÂAyë
+Az{A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A   A {A (öA =qA QìA ffA záA \A £×A žRA ÌÍA áHA õÃA¡
+=A¡
+žA¡33A¡G®A¡\)A¡p€A¡
+A¡A¡®A¡ÂA¡×
+A¡ë
+A¢  A¢{A¢(öA¢=qA¢QìA¢ffA¢záA¢\A¢£×A¢žRA¢ÌÍA¢áHA¢õÃA£
+=A£
+žA£33A£G®A£\)A£p€A£
+A£A£®A£ÂA£×
+A£ë
+A€  A€{A€(öA€=qA€QìA€ffA€záA€\A€£×A€žRA€ÌÍA€áHA€õÃA¥
+=Aš  Aš{Aš(öAš=qAšQìAšffAšzáAš\Aš£×AšžRAšÌÍAšáHAšõÃA©
+=A©
+žA©33A©G®A©\)A©p€A©
+A©A©®A©ÂA©×
+A©ë
+Aª  Aª{Aª(öAª=qAªQìAªffAªzáAª\Aª£×AªžRAªÌÍAªáHAªõÃA«
+=A«
+žA«33A«G®A«\)A«p€A«
+A«A«®A«ÂA«×
+A«ë
+A¬  A¬{A¬(öA¬=qA¬QìA¬ffA¬záA¬\A¬£×A¬žRA¬ÌÍA¬áHA¬õÃA­
+=A°  A°{A°(öA°=qA°QìA°ffA°záA°\A°£×A°žRA°ÌÍA°áHA°õÃA±
+=A±
+žA±33A±G®A±\)A±p€A±
+A±A±®A±ÂA±×
+A±ë
+A²  A²{A²(öA²=qA²QìA²ffA²záA²\A²£×A²žRA²ÌÍA²áHA²õÃA³
+=A³
+žA³33A³G®A³\)A³p€A³
+A³A³®A³ÂA³×
+A³ë
+AŽ  AŽ{AŽ(öAŽ=qAŽQìAŽffAŽzáAŽ\AŽ£×AŽžRAŽÌÍAŽáHAŽõÃAµ
+=Až  Až{Až(öAž=qAžQìAžffAžzáAž\Až£×AžžRAžÌÍAžáHAžõÃA¹
+=A¹
+žA¹33A¹G®A¹\)A¹p€A¹
+A¹A¹®A¹ÂA¹×
+A¹ë
+Aº  Aº{Aº(öAº=qAºQìAºffAºzáAº\Aº£×AºžRAºÌÍAºáHAºõÃA»
+=A»
+žA»33A»G®A»\)A»p€A»
+A»A»®A»ÂA»×
+A»ë
+AŒ  AŒ{AŒ(öAŒ=qAŒQìAŒffAŒzáAŒ\AŒ£×AŒžRAŒÌÍAŒáHAŒõÃAœ
+=AÀ  AÀ{AÀ(öAÀ=qAÀQìAÀffAÀzáAÀ\AÀ£×AÀžRAÀÌÍAÀáHAÀõÃAÁ
+=AÁ
+žAÁ33AÁG®AÁ\)AÁp€AÁ
+AÁAÁ®AÁÂAÁ×
+AÁë
+AÂ  AÂ{AÂ(öAÂ=qAÂQìAÂffAÂzáAÂ\AÂ£×AÂžRAÂÌÍAÂáHAÂõÃAÃ
+=AÃ
+žAÃ33AÃG®AÃ\)AÃp€AÃ
+AÃAÃ®AÃÂAÃ×
+AÃë
+AÄ  AÄ{AÄ(öAÄ=qAÄQìAÄffAÄzáAÄ\AÄ£×AÄžRAÄÌÍAÄáHAÄõÃAÅ
+=AÈ  AÈ{AÈ(öAÈ=qAÈQìAÈffAÈzáAÈ\AÈ£×AÈžRAÈÌÍAÈáHAÈõÃAÉ
+=AÉ
+žAÉ33AÉG®AÉ\)AÉp€AÉ
+AÉAÉ®AÉÂAÉ×
+AÉë
+AÊ  AÊ{AÊ(öAÊ=qAÊQìAÊffAÊzáAÊ\AÊ£×AÊžRAÊÌÍAÊáHAÊõÃAË
+=AË
+žAË33AËG®AË\)AËp€AË
+AËAË®AËÂAË×
+AËë
+AÌ  AÌ{AÌ(öAÌ=qAÌQìAÌffAÌzáAÌ\AÌ£×AÌžRAÌÌÍAÌáHAÌõÃAÍ
+=AÐ  AÐ{AÐ(öAÐ=qAÐQìAÐffAÐzáAÐ\AÐ£×AÐžRAÐÌÍAÐáHAÐõÃAÑ
+=AÑ
+žAÑ33AÑG®AÑ\)AÑp€AÑ
+AÑAÑ®AÑÂAÑ×
+AÑë
+AÒ  AÒ{AÒ(öAÒ=qAÒQìAÒffAÒzáAÒ\AÒ£×AÒžRAÒÌÍAÒáHAÒõÃAÓ
+=AÓ
+žAÓ33AÓG®AÓ\)AÓp€AÓ
+AÓAÓ®AÓÂAÓ×
+AÓë
+AÔ  AÔ{AÔ(öAÔ=qAÔQìAÔffAÔzáAÔ\AÔ£×AÔžRAÔÌÍAÔáHAÔõÃAÕ
+=AØ  AØ{AØ(öAØ=qAØQìAØffAØzáAØ\AØ£×AØžRAØÌÍAØáHAØõÃAÙ
+=AÙ
+žAÙ33AÙG®AÙ\)AÙp€AÙ
+AÙAÙ®AÙÂAÙ×
+AÙë
+AÚ  AÚ{AÚ(öAÚ=qAÚQìAÚffAÚzáAÚ\AÚ£×AÚžRAÚÌÍAÚáHAÚõÃAÛ
+=AÛ
+žAÛ33AÛG®AÛ\)AÛp€AÛ
+AÛAÛ®AÛÂAÛ×
+AÛë
+AÜ  AÜ{AÜ(öAÜ=qAÜQìAÜffAÜzáAÜ\AÜ£×AÜžRAÜÌÍAÜáHAÜõÃAÝ
+=Aà  Aà{Aà(öAà=qAàQìAàffAàzáAà\Aà£×AàžRAàÌÍAàáHAàõÃAá
+=Aá
+žAá33AáG®Aá\)Aáp€Aá
+AáAá®AáÂAá×
+Aáë
+Aâ  Aâ{Aâ(öAâ=qAâQìAâffAâzáAâ\Aâ£×AâžRAâÌÍAâáHAâõÃAã
+=Aã
+žAã33AãG®Aã\)Aãp€Aã
+AãAã®AãÂAã×
+Aãë
+Aä  Aä{Aä(öAä=qAäQìAäffAäzáAä\Aä£×AäžRAäÌÍAäáHAäõÃAå
+=Aè  Aè{Aè(öAè=qAèQìAèffAèzáAè\Aè£×AèžRAèÌÍAèáHAèõÃAé
+=Aé
+žAé33AéG®Aé\)Aép€Aé
+AéAé®AéÂAé×
+Aéë
+Aê  Aê{Aê(öAê=qAêQìAêffAêzáAê\Aê£×AêžRAêÌÍAêáHAêõÃAë
+=Aë
+žAë33AëG®Aë\)Aëp€Aë
+AëAë®AëÂAë×
+Aëë
+Aì  Aì{Aì(öAì=qAìQìAìffAìzáAì\Aì£×AìžRAìÌÍAìáHAìõÃAí
+=Að  Að{Að(öAð=qAðQìAðffAðzáAð\Að£×AðžRAðÌÍAðáHAðõÃAñ
+=Añ
+žAñ33AñG®Añ\)Añp€Añ
+AñAñ®AñÂAñ×
+Añë
+Aò  Aò{Aò(öAò=qAòQìAòffAòzáAò\Aò£×AòžRAòÌÍAòáHAòõÃAó
+=Aó
+žAó33AóG®Aó\)Aóp€Aó
+AóAó®AóÂAó×
+Aóë
+Aô  Aô{Aô(öAô=qAôQìAôffAôzáAô\Aô£×AôžRAôÌÍAôáHAôõÃAõ
+=Aø  Aø{Aø(öAø=qAøQìAøffAøzáAø\Aø£×AøžRAøÌÍAøáHAøõÃAù
+=Aù
+žAù33AùG®Aù\)Aùp€Aù
+AùAù®AùÂAù×
+Aùë
+Aú  Aú{Aú(öAú=qAúQìAúffAúzáAú\Aú£×AúžRAúÌÍAúáHAúõÃAû
+=Aû
+žAû33AûG®Aû\)Aûp€Aû
+AûAû®AûÂAû×
+Aûë
+Aü  Aü{Aü(öAü=qAüQìAüffAüzáAü\Aü£×AüžRAüÌÍAüáHAüõÃAý
+=B   B 
+=B {B 
+žB (öB 33B =qB G®B QìB \)B ffB p€B záB 
+B \B B £×B ®B žRB ÂB ÌÍB ×
+B áHB ë
+B õÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB	  B	
+=B	{B	
+žB	(öB	33B	=qB	G®B	QìB	\)B	ffB	p€B	záB	
+B	\B	B	£×B	®B	žRB	ÂB	ÌÍB	×
+B	áHB	ë
+B	õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B   B 
+=B {B 
+žB (öB 33B =qB G®B QìB \)B ffB p€B záB 
+B \B B £×B ®B žRB ÂB ÌÍB ×
+B áHB ë
+B õÃB!  B!
+=B!{B!
+žB!(öB!33B!=qB!G®B!QìB!\)B!ffB!p€B!záB!
+B!\B!B!£×B!®B!žRB!ÂB!ÌÍB!×
+B!áHB!ë
+B!õÃB"  B"
+=B"{B"
+žB"(öB"33B"=qB"G®B"QìB"\)B"ffB"p€B"záB"
+B$  B$
+=B${B$
+žB$(öB$33B$=qB$G®B$QìB$\)B$ffB$p€B$záB$
+B$\B$B$£×B$®B$žRB$ÂB$ÌÍB$×
+B$áHB$ë
+B$õÃB%  B%
+=B%{B%
+žB%(öB%33B%=qB%G®B%QìB%\)B%ffB%p€B%záB%
+B%\B%B%£×B%®B%žRB%ÂB%ÌÍB%×
+B%áHB%ë
+B%õÃB&  B&
+=B&{B&
+žB&(öB&33B&=qB&G®B&QìB&\)B&ffB&p€B&záB&
+B(  B(
+=B({B(
+žB((öB(33B(=qB(G®B(QìB(\)B(ffB(p€B(záB(
+B(\B(B(£×B(®B(žRB(ÂB(ÌÍB(×
+B(áHB(ë
+B(õÃB)  B)
+=B){B)
+žB)(öB)33B)=qB)G®B)QìB)\)B)ffB)p€B)záB)
+B)\B)B)£×B)®B)žRB)ÂB)ÌÍB)×
+B)áHB)ë
+B)õÃB*  B*
+=B*{B*
+žB*(öB*33B*=qB*G®B*QìB*\)B*ffB*p€B*záB*
+B,  B,
+=B,{B,
+žB,(öB,33B,=qB,G®B,QìB,\)B,ffB,p€B,záB,
+B,\B,B,£×B,®B,žRB,ÂB,ÌÍB,×
+B,áHB,ë
+B,õÃB-  B-
+=B-{B-
+žB-(öB-33B-=qB-G®B-QìB-\)B-ffB-p€B-záB-
+B-\B-B-£×B-®B-žRB-ÂB-ÌÍB-×
+B-áHB-ë
+B-õÃB.  B.
+=B.{B.
+žB.(öB.33B.=qB.G®B.QìB.\)B.ffB.p€B.záB.
+B0  B0
+=B0{B0
+žB0(öB033B0=qB0G®B0QìB0\)B0ffB0p€B0záB0
+B0\B0B0£×B0®B0žRB0ÂB0ÌÍB0×
+B0áHB0ë
+B0õÃB1  B1
+=B1{B1
+žB1(öB133B1=qB1G®B1QìB1\)B1ffB1p€B1záB1
+B1\B1B1£×B1®B1žRB1ÂB1ÌÍB1×
+B1áHB1ë
+B1õÃB2  B2
+=B2{B2
+žB2(öB233B2=qB2G®B2QìB2\)B2ffB2p€B2záB2
+B4  B4
+=B4{B4
+žB4(öB433B4=qB4G®B4QìB4\)B4ffB4p€B4záB4
+B4\B4B4£×B4®B4žRB4ÂB4ÌÍB4×
+B4áHB4ë
+B4õÃB5  B5
+=B5{B5
+žB5(öB533B5=qB5G®B5QìB5\)B5ffB5p€B5záB5
+B5\B5B5£×B5®B5žRB5ÂB5ÌÍB5×
+B5áHB5ë
+B5õÃB6  B6
+=B6{B6
+žB6(öB633B6=qB6G®B6QìB6\)B6ffB6p€B6záB6
+B8  B8
+=B8{B8
+žB8(öB833B8=qB8G®B8QìB8\)B8ffB8p€B8záB8
+B8\B8B8£×B8®B8žRB8ÂB8ÌÍB8×
+B8áHB8ë
+B8õÃB9  B9
+=B9{B9
+žB9(öB933B9=qB9G®B9QìB9\)B9ffB9p€B9záB9
+B9\B9B9£×B9®B9žRB9ÂB9ÌÍB9×
+B9áHB9ë
+B9õÃB:  B:
+=B:{B:
+žB:(öB:33B:=qB:G®B:QìB:\)B:ffB:p€B:záB:
+B<  B<
+=B<{B<
+žB<(öB<33B<=qB<G®B<QìB<\)B<ffB<p€B<záB<
+B<\B<B<£×B<®B<žRB<ÂB<ÌÍB<×
+B<áHB<ë
+B<õÃB=  B=
+=B={B=
+žB=(öB=33B==qB=G®B=QìB=\)B=ffB=p€B=záB=
+B=\B=B=£×B=®B=žRB=ÂB=ÌÍB=×
+B=áHB=ë
+B=õÃB>  B>
+=B>{B>
+žB>(öB>33B>=qB>G®B>QìB>\)B>ffB>p€B>záB>
+B@  B@
+=B@{B@
+žB@(öB@33B@=qB@G®B@QìB@\)B@ffB@p€B@záB@
+B@\B@B@£×B@®B@žRB@ÂB@ÌÍB@×
+B@áHB@ë
+B@õÃBA  BA
+=BA{BA
+žBA(öBA33BA=qBAG®BAQìBA\)BAffBAp€BAzáBA
+BA\BABA£×BA®BAžRBAÂBAÌÍBA×
+BAáHBAë
+BAõÃBB  BB
+=BB{BB
+žBB(öBB33BB=qBBG®BBQìBB\)BBffBBp€BBzáBB
+BD  BD
+=BD{BD
+žBD(öBD33BD=qBDG®BDQìBD\)BDffBDp€BDzáBD
+BD\BDBD£×BD®BDžRBDÂBDÌÍBD×
+BDáHBDë
+BDõÃBE  BE
+=BE{BE
+žBE(öBE33BE=qBEG®BEQìBE\)BEffBEp€BEzáBE
+BE\BEBE£×BE®BEžRBEÂBEÌÍBE×
+BEáHBEë
+BEõÃBF  BF
+=BF{BF
+žBF(öBF33BF=qBFG®BFQìBF\)BFffBFp€BFzáBF
+BH  BH
+=BH{BH
+žBH(öBH33BH=qBHG®BHQìBH\)BHffBHp€BHzáBH
+BH\BHBH£×BH®BHžRBHÂBHÌÍBH×
+BHáHBHë
+BHõÃBI  BI
+=BI{BI
+žBI(öBI33BI=qBIG®BIQìBI\)BIffBIp€BIzáBI
+BI\BIBI£×BI®BIžRBIÂBIÌÍBI×
+BIáHBIë
+BIõÃBJ  BJ
+=BJ{BJ
+žBJ(öBJ33BJ=qBJG®BJQìBJ\)BJffBJp€BJzáBJ
+BL  BL
+=BL{BL
+žBL(öBL33BL=qBLG®BLQìBL\)BLffBLp€BLzáBL
+BL\BLBL£×BL®BLžRBLÂBLÌÍBL×
+BLáHBLë
+BLõÃBM  BM
+=BM{BM
+žBM(öBM33BM=qBMG®BMQìBM\)BMffBMp€BMzáBM
+BM\BMBM£×BM®BMžRBMÂBMÌÍBM×
+BMáHBMë
+BMõÃBN  BN
+=BN{BN
+žBN(öBN33BN=qBNG®BNQìBN\)BNffBNp€BNzáBN
+BP  BP
+=BP{BP
+žBP(öBP33BP=qBPG®BPQìBP\)BPffBPp€BPzáBP
+BP\BPBP£×BP®BPžRBPÂBPÌÍBP×
+BPáHBPë
+BPõÃBQ  BQ
+=BQ{BQ
+žBQ(öBQ33BQ=qBQG®BQQìBQ\)BQffBQp€BQzáBQ
+BQ\BQBQ£×BQ®BQžRBQÂBQÌÍBQ×
+BQáHBQë
+BQõÃBR  BR
+=BR{BR
+žBR(öBR33BR=qBRG®BRQìBR\)BRffBRp€BRzáBR
+BT  BT
+=BT{BT
+žBT(öBT33BT=qBTG®BTQìBT\)BTffBTp€BTzáBT
+BT\BTBT£×BT®BTžRBTÂBTÌÍBT×
+BTáHBTë
+BTõÃBU  BU
+=BU{BU
+žBU(öBU33BU=qBUG®BUQìBU\)BUffBUp€BUzáBU
+BU\BUBU£×BU®BUžRBUÂBUÌÍBU×
+BUáHBUë
+BUõÃBV  BV
+=BV{BV
+žBV(öBV33BV=qBVG®BVQìBV\)BVffBVp€BVzáBV
+BX  BX
+=BX{BX
+žBX(öBX33BX=qBXG®BXQìBX\)BXffBXp€BXzáBX
+BX\BXBX£×BX®BXžRBXÂBXÌÍBX×
+BXáHBXë
+BXõÃBY  BY
+=BY{BY
+žBY(öBY33BY=qBYG®BYQìBY\)BYffBYp€BYzáBY
+BY\BYBY£×BY®BYžRBYÂBYÌÍBY×
+BYáHBYë
+BYõÃBZ  BZ
+=BZ{BZ
+žBZ(öBZ33BZ=qBZG®BZQìBZ\)BZffBZp€BZzáBZ
+B\  B\
+=B\{B\
+žB\(öB\33B\=qB\G®B\QìB\\)B\ffB\p€B\záB\
+B\\B\B\£×B\®B\žRB\ÂB\ÌÍB\×
+B\áHB\ë
+B\õÃB]  B]
+=B]{B]
+žB](öB]33B]=qB]G®B]QìB]\)B]ffB]p€B]záB]
+B]\B]B]£×B]®B]žRB]ÂB]ÌÍB]×
+B]áHB]ë
+B]õÃB^  B^
+=B^{B^
+žB^(öB^33B^=qB^G®B^QìB^\)B^ffB^p€B^záB^
+B`  B`
+=B`{B`
+žB`(öB`33B`=qB`G®B`QìB`\)B`ffB`p€B`záB`
+B`\B`B`£×B`®B`žRB`ÂB`ÌÍB`×
+B`áHB`ë
+B`õÃBa  Ba
+=Ba{Ba
+žBa(öBa33Ba=qBaG®BaQìBa\)BaffBap€BazáBa
+Ba\BaBa£×Ba®BažRBaÂBaÌÍBa×
+BaáHBaë
+BaõÃBb  Bb
+=Bb{Bb
+žBb(öBb33Bb=qBbG®BbQìBb\)BbffBbp€BbzáBb
+Bd  Bd
+=Bd{Bd
+žBd(öBd33Bd=qBdG®BdQìBd\)BdffBdp€BdzáBd
+Bd\BdBd£×Bd®BdžRBdÂBdÌÍBd×
+BdáHBdë
+BdõÃBe  Be
+=Be{Be
+žBe(öBe33Be=qBeG®BeQìBe\)BeffBep€BezáBe
+Be\BeBe£×Be®BežRBeÂBeÌÍBe×
+BeáHBeë
+BeõÃBf  Bf
+=Bf{Bf
+žBf(öBf33Bf=qBfG®BfQìBf\)BfffBfp€BfzáBf
+Bh  Bh
+=Bh{Bh
+žBh(öBh33Bh=qBhG®BhQìBh\)BhffBhp€BhzáBh
+Bh\BhBh£×Bh®BhžRBhÂBhÌÍBh×
+BháHBhë
+BhõÃBi  Bi
+=Bi{Bi
+žBi(öBi33Bi=qBiG®BiQìBi\)BiffBip€BizáBi
+Bi\BiBi£×Bi®BižRBiÂBiÌÍBi×
+BiáHBië
+BiõÃBj  Bj
+=Bj{Bj
+žBj(öBj33Bj=qBjG®BjQìBj\)BjffBjp€BjzáBj
+Bl  Bl
+=Bl{Bl
+žBl(öBl33Bl=qBlG®BlQìBl\)BlffBlp€BlzáBl
+Bl\BlBl£×Bl®BlžRBlÂBlÌÍBl×
+BláHBlë
+BlõÃBm  Bm
+=Bm{Bm
+žBm(öBm33Bm=qBmG®BmQìBm\)BmffBmp€BmzáBm
+Bm\BmBm£×Bm®BmžRBmÂBmÌÍBm×
+BmáHBmë
+BmõÃBn  Bn
+=Bn{Bn
+žBn(öBn33Bn=qBnG®BnQìBn\)BnffBnp€BnzáBn
+Bp  Bp
+=Bp{Bp
+žBp(öBp33Bp=qBpG®BpQìBp\)BpffBpp€BpzáBp
+Bp\BpBp£×Bp®BpžRBpÂBpÌÍBp×
+BpáHBpë
+BpõÃBq  Bq
+=Bq{Bq
+žBq(öBq33Bq=qBqG®BqQìBq\)BqffBqp€BqzáBq
+Bq\BqBq£×Bq®BqžRBqÂBqÌÍBq×
+BqáHBqë
+BqõÃBr  Br
+=Br{Br
+žBr(öBr33Br=qBrG®BrQìBr\)BrffBrp€BrzáBr
+Bt  Bt
+=Bt{Bt
+žBt(öBt33Bt=qBtG®BtQìBt\)BtffBtp€BtzáBt
+Bt\BtBt£×Bt®BtžRBtÂBtÌÍBt×
+BtáHBtë
+BtõÃBu  Bu
+=Bu{Bu
+žBu(öBu33Bu=qBuG®BuQìBu\)BuffBup€BuzáBu
+Bu\BuBu£×Bu®BužRBuÂBuÌÍBu×
+BuáHBuë
+BuõÃBv  Bv
+=Bv{Bv
+žBv(öBv33Bv=qBvG®BvQìBv\)BvffBvp€BvzáBv
+Bx  Bx
+=Bx{Bx
+žBx(öBx33Bx=qBxG®BxQìBx\)BxffBxp€BxzáBx
+Bx\BxBx£×Bx®BxžRBxÂBxÌÍBx×
+BxáHBxë
+BxõÃBy  By
+=By{By
+žBy(öBy33By=qByG®ByQìBy\)ByffByp€ByzáBy
+By\ByBy£×By®ByžRByÂByÌÍBy×
+ByáHByë
+ByõÃBz  Bz
+=Bz{Bz
+žBz(öBz33Bz=qBzG®BzQìBz\)BzffBzp€BzzáBz
+B|  B|
+=B|{B|
+žB|(öB|33B|=qB|G®B|QìB|\)B|ffB|p€B|záB|
+B|\B|B|£×B|®B|žRB|ÂB|ÌÍB|×
+B|áHB|ë
+B|õÃB}  B}
+=B}{B}
+žB}(öB}33B}=qB}G®B}QìB}\)B}ffB}p€B}záB}
+B}\B}B}£×B}®B}žRB}ÂB}ÌÍB}×
+B}áHB}ë
+B}õÃB~  B~
+=B~{B~
+žB~(öB~33B~=qB~G®B~QìB~\)B~ffB~p€B~záB~
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?  <#×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @   ?G®?G®<£×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @  @@£×@ £×?\?×
+=#×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @   @Qì@Qì@AG®@ë
+?
+
+ž?
+
+ž=LÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @À  @ÀQì@ Qì@£×@õÃ@B\@\?ff?®=uÂ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A   @àQì@ÀQì@À£×@ õÃ@G®@G®@C33@×
+?®?õÃ=£×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A(öA (ö@à£×@ÀõÃ@ÁG®@¡G®@@ë
+@C×
+@zá?=q?
+=žQì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A0  A (öA(öAQìA zá@áG®@ÁG®@Á@¡ë
+@ë
+@=q@E
+ž@Â?
+?ÌÍ=áG®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A@  A0(öA0(öA QìAzáA£×A £×@á@Áë
+@Áë
+@¢=q@\@áH@EÂ@ff?{?{=õÂ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AP  AP(öA@(öA0QìA0záA £×A£×AÌÍA õÃ@áë
+@Â=q@Â\@¢áH@áH@33@G
+=@
+=?\)?£×>
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Ap  A`(öAP(öAPQìA@záA0£×A0£×A ÌÍAõÃAõÃA
+ž@â\@ÂáH@ÂáH@£33@
+@
+@G®@Qì?£×?ë
+>¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{Ap(öA`QìAPzáAP£×A@£×A0ÌÍA0õÃA õÃA
+žAG®Ap€@âáH@Ã33@Ã
+@£
+@×
+@(ö@HQì@õÃ?33?zá>#×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{A{A(öApzáA`£×AP£×APÌÍA@õÃA0õÃA1
+žA!G®Ap€Ap€A@ã
+@Ã
+@Ã×
+@€(ö@(ö@zá@I@
+=q?zá?Â>8Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{A{A(öA=qAQìAp£×A`ÌÍAPõÃAPõÃAA
+žA1G®A1p€A!p€AAÂAÂ@ã×
+@Ä(ö@Ä(ö@€zá@ÌÍ@
+
+ž@J=q@
+áH?
+>?
+>>B\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aš  A {A{A(öA=qAQìAQìAffApõÃA`õÃAQ
+žAQG®AAp€A1p€A1A!ÂAÂAë
+A{@ä(ö@Äzá@ÄÌÍ@¥
+ž@
+
+ž@
+p€@K
+@
+
+?Qì?>W
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A°  Aš{Aš{A (öA=qAQìAQìAffAzáAzáAq
+žAaG®AQp€AQp€AAA1ÂA1ÂA!ë
+A{A{A=q@äÌÍ@Å
+ž@Å
+ž@¥p€@
+Â@
+Â@L(ö@
+ÌÍ?áH?áH>aG®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Až  Až{A°{Aš(öAš=qA QìAQìAffAzáAzáA\A£×Aqp€Aap€AQAQÂAAÂA1ë
+A2{A"{A=qAffA\@å
+ž@Åp€@ÅÂ@¥Â@{@ff@Mp€@
+p€?(ö?p€>k
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÈ  AÀ{Až{Až(öA°=qAšQìAšQìA ffAzáAzáA\A£×AžRAžRAqAaÂAQÂAQë
+AB{A2{A2=qA"ffA\A\AžR@åÂ@ÅÂ@Æ{@Šff@žR@žR@N{@žR?p€?žR>  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÐ  AÈ{AÈ{AÀ(öAž=qAžQìA°QìAšffAšzáA záA\A£×AžRAžRAÌÍAáHAqÂAaë
+AR{AR{AB=qA2ffA2\A"\AžRAáHAáH@æ{@Æff@ÆžR@ŠžR@
+=@\)@NžR@\)?   ?   >
+
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aà  AØ{AÐ{AÈ(öAÈ=qAÀQìAžQìAžffA°záAšzáAš\A £×AžRAžRAÌÍAáHAáHAõÃAr{Ab{AR=qARffAB\A2\A2žRA"áHAáHA
+=A33@æžR@ÆžR@Ç
+=@§\)@\)@®@P  @  ?¡G®?¢\>\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aè  Aà{Aà{AØ(öAÐ=qAÈQìAÈQìAÀffAžzáAžzáA°\Aš£×AšžRA žRAÌÍAáHAáHAõÃA
+=A
+=Ar=qAbffAR\AR\ABžRA2áHA2áHA#
+=A33A\)A\)@ç
+=@Ç\)@Ç\)@§®@  @  @P£×@G®?£×
+?£×
+>zá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Að  Að{Aè{Aà(öAà=qAØQìAÐQìAÈffAÈzáAÀzáAž\Až£×A°žRAšžRAšÌÍA áHAáHAõÃA
+=A
+=A
+žA33Ar\Ab\ARžRARáHABáHA3
+=A333A#\)A\)A
+A®@ç\)@Ç®@È  @š  @Qì@£×@Që
+@ë
+?¥
+ž?Šff>¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B   Aø{Að{Að(öAè=qAàQìAàQìAØffAÐzáAÈzáAÈ\AÀ£×AžžRAžžRA°ÌÍAšáHAšáHA õÃA
+=A
+=A
+žA33AG®AG®AržRAbáHARáHAS
+=AC33A3\)A3\)A#
+A®A®A×
+@è  @È  @ÈQì@š£×@õÃ@õÃ@R\@33?Šff?§®>£×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+=B 
+=Aø(öAð=qAðQìAèQìAàffAàzáAØzáAÐ\AÈ£×AÈžRAÀžRAžÌÍAžáHA°áHAšõÃA©
+=A¡
+=A
+žA33AG®AG®A\)Ap€AráHAc
+=AS33AS\)AC\)A3
+A3®A#®A×
+A  A  @èQì@È£×@ÈõÃ@šõÃ@G®@@S33@×
+?šõÂ?ª=q>šõÃ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+  B
+=B
+=B{B 
+žAøQìAðQìAðffAèzáAàzáAà\AØ£×AÐžRAÈžRAÈÌÍAÀáHAžáHAžõÃA±
+=A©
+=A©
+žA¡33AG®AG®A\)Ap€Ap€A
+As33Ac\)AS\)AS
+AC®A3®A3×
+A$  A  A(öAQì@èõÃ@ÈõÃ@ÉG®@©@@ë
+@Tzá@
+ž?ª=q?«
+>³33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+
+=B
+
+=B{B
+žB(öB (öAøffAðzáAðzáAè\Aà£×AàžRAØžRAÐÌÍAÈáHAÈáHAÀõÃA¹
+=A¹
+=A±
+žA©33A©G®A¡G®A\)Ap€Ap€A
+AA®As\)Ac
+AS®AS®AC×
+A4  A4  A$(öAQìAzáAzá@éG®@É@É@©ë
+@=q@\@U
+ž@Â?¬ÌÍ?¬ÌÍ>žQì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+=B
+=B
+{B
+
+žB(öB(öB33B =qAøzáAð\Að£×AèžRAàžRAàÌÍAØáHAÐáHAÈõÃAÉ
+=AÁ
+=A¹
+žA¹33A±G®A©G®A©\)A¡p€Ap€A
+AA®A®AÂAs®Ac®AS×
+AT  AD  A4(öA4QìA$záAzáA£×AÌÍ@é@Éë
+@Ê=q@ª\@\@áH@Vff@ff?®{?¯\)>Â\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+  B
+=B
+=B{B
+žB
+(öB
+(öB33B=qB=qB G®Aø£×AðžRAðžRAèÌÍAàáHAàáHAØõÃAÑ
+=AÉ
+=AÉ
+žAÁ33A¹G®A¹G®A±\)A©p€A©p€A¡
+AA®A®AÂA×
+A×
+As×
+Ad  AT  AT(öADQìA4záA4záA$£×AÌÍAÌÍAõÃ@ê=q@Ê\@Ê\@ªáH@33@33@W
+>@®?°£×?°£×>Ç®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B   B 
+=B
+
+=B{B
+žB(öB(öB
+33B
+=qB=qBG®BQìB \)AøžRAðÌÍAðáHAèáHAàõÃAá
+=AÙ
+=AÑ
+žAÉ33AÉG®AÁG®A¹\)A¹p€A±p€A©
+A©A¡®A®AÂA×
+A×
+Aë
+A  At  Ad(öATQìATzáADzáA4£×A4ÌÍA$ÌÍAõÃA
+žAG®@ê\@ÊáH@Ë33@«33@
+@×
+@XQì@Qì?±ë
+?³33>ÌÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B(  B$
+=B 
+=B {B
+
+žB(öB(öB33B=qB
+=qB
+G®BQìB\)B\)B ffAøáHAðáHAðõÃAé
+=Aá
+=Aá
+žAÙ33AÑG®AÉG®AÉ\)AÁp€A¹p€A¹
+A±A©®A©®A¡ÂA×
+A×
+Aë
+A  A  A{AtQìAdzáATzáAT£×ADÌÍA4ÌÍA4õÃA%
+žAG®AG®Ap€@ë33@Ë33@Ë
+@«×
+@(ö@(ö@XõÂ@?³33?Žzá>×
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B,  B(
+=B(
+=B${B 
+žB (öB
+(öB33B=qB=qBG®B
+QìB
+\)B\)BffBp€B p€AøõÃAñ
+=Añ
+=Aé
+žAá33AáG®AÙG®AÑ\)AÉp€AÉp€AÁ
+A¹A¹®A±®A©ÂA©×
+A¡×
+Aë
+A  A  A{A(öA=qAtzáAd£×ATÌÍATÌÍADõÃA5
+žA5G®A%G®Ap€AA@ë
+@Ë×
+@Ì(ö@¬(ö@zá@ÌÍ@Y@=q?µÂ?µÂ>Ü(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B4  B0
+=B,
+=B({B(
+žB$(öB (öB 33B
+=qB=qBG®BQìB\)B
+\)B
+ffBp€Bp€BzáB 
+Aù
+=Añ
+žAñ33AéG®AáG®Aá\)AÙp€AÑp€AÉ
+AÉAÁ®A¹®A¹ÂA±×
+A©×
+A©ë
+A¢  A  A{A(öA=qA=qAQìAtÌÍAdÌÍATõÃAU
+žAEG®A5G®A5p€A%AAÂAë
+@ì(ö@Ì(ö@Ìzá@¬ÌÍ@ÌÍ@
+ž@ZáH@áH?·
+>?žQì>æff¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B8  B4
+=B4
+=B0{B,
+žB((öB((öB$33B =qB =qB
+G®BQìB\)B\)BffB
+p€B
+p€BzáB
+B
+B \Aù33AñG®AñG®Aé\)Aáp€Aáp€AÙ
+AÑAÉ®AÉ®AÁÂA¹×
+A¹×
+A±ë
+Aª  Aª  A¢{A(öA=qA=qAQìAffAffAtõÃAe
+žAUG®AUG®AEp€A5A5A%ÂAë
+A{A{@ìzá@ÌÌÍ@ÌÌÍ@­
+ž@p€@p€@[
+@
+(ö?¹?¹>ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B<  B<
+=B8
+=B4{B4
+žB0(öB,(öB(33B(=qB$=qB G®B QìB
+\)B\)BffBp€Bp€B
+záB
+
+B
+B\BB £×AùG®Añ\)Añp€Aép€Aá
+AáAÙ®AÑ®AÉÂAÉ×
+AÁ×
+A¹ë
+Aº  A²  Aª{Aª(öA¢=qA=qAQìAffAffAzáA\AuG®AeG®AUp€AUAEA5ÂA5ë
+A&{A{A=qAff@ìÌÍ@Í
+ž@Íp€@­p€@Â@{@\ÌÍ@
+ÌÍ?ºáH?Œ(ö>ð£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BD  B@
+=B<
+=B<{B8
+žB4(öB4(öB033B,=qB(=qB(G®B$QìB \)B \)B
+ffBp€Bp€BzáB
+B
+
+B
+\BB£×B£×B ®Aùp€Añp€Añ
+AéAá®Aá®AÙÂAÑ×
+AÉ×
+AÉë
+AÂ  Aº  Aº{A²(öAª=qAª=qA¢QìAffAffAzáA\A£×A£×Aup€AeAUAUÂAEë
+A6{A6{A&=qAffAffA\@íp€@Íp€@ÍÂ@®{@ff@ff@]p€@
+{?Œ(ö?œp€>úáH¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BH  BH
+=BD
+=B@{B<
+žB<(öB8(öB433B4=qB0=qB,G®B(QìB(\)B$\)B ffB p€B
+p€BzáB
+B
+B\B
+B
+£×B£×B®BžRB žRAù
+AñAñ®Aé®AáÂAá×
+AÙ×
+AÑë
+AÊ  AÊ  AÂ{Aº(öAº=qA²=qAªQìAªffA¢ffAzáA\A£×A£×AžRAÌÍAuAeÂAUë
+AV{AF{A6=qA6ffA&ffA\AžRAžR@íÂ@Î{@Îff@®ff@žR@
+=@^{@
+žR?ŸžR?À  ?   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BP  BL
+=BH
+=BH{BD
+žB@(öB<(öB<33B8=qB4=qB4G®B0QìB,\)B(\)B(ffB$p€B p€B záB
+
+B
+B\BB£×B
+£×B
+®BžRBžRBÂB ÌÍAù®Añ®AñÂAé×
+Aá×
+Aáë
+AÚ  AÒ  AÊ{AÊ(öAÂ=qAº=qAºQìA²ffAªffAªzáA¢\A£×A£×AžRAÌÍAÌÍAáHAuë
+Af{AV{AV=qAFffA6ffA6\A&žRAžRAáHA
+=@îff@Îff@ÎžR@¯
+=@
+=@\)@_\)@   ?À  ?ÁG®?
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BT  BP
+=BP
+=BL{BH
+žBH(öBD(öB@33B<=qB<=qB8G®B4QìB4\)B0\)B,ffB(p€B(p€B$záB 
+B 
+B
+\BB£×B£×B®B
+žRB
+žRBÂBÌÍB×
+B ×
+AùÂAñ×
+Añ×
+Aéë
+Aâ  Aâ  AÚ{AÒ(öAÊ=qAÊ=qAÂQìAºffAºffA²záAª\Aª£×A¢£×AžRAÌÍAÌÍAáHAõÃA
+=Av{Af=qAVffAVffAF\A6žRA6žRA&áHA
+=A33A33@îžR@Ï
+=@Ï
+=@¯\)@®@  @`  @ £×?Â\?Â\?®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BX  BX
+=BT
+=BP{BP
+žBL(öBH(öBH33BD=qB@=qB<G®B<QìB8\)B4\)B4ffB0p€B,p€B(záB(
+B$
+B \B B
+£×B£×B®BžRBžRB
+ÂB
+ÌÍB×
+B×
+BáHB ë
+Aù×
+Añë
+Aò  Aê  Aâ{Aâ(öAÚ=qAÒ=qAÊQìAÊffAÂffAºzáAº\A²£×Aª£×AªžRA¢ÌÍAÌÍAáHAõÃA
+=A
+=A
+žAvffAfffAV\AVžRAFžRA6áHA7
+=A'33A33A\)A
+@ï
+=@Ï\)@Ï®@°  @  @Qì@aG®@!G®?Ã×
+?Å
+ž?
+=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B`  B\
+=BX
+=BX{BT
+žBP(öBP(öBL33BH=qBH=qBDG®B@QìB<\)B<\)B8ffB4p€B4p€B0záB,
+B(
+B(\B$B £×B £×B
+®BžRBžRBÂBÌÍB
+×
+B
+×
+BáHBë
+Bë
+B õÃAú  Aò  Aò{Aê(öAâ=qAâ=qAÚQìAÒffAÊffAÊzáAÂ\Aº£×Aº£×A²žRAªÌÍAªÌÍA¢áHAõÃA
+=A
+=A
+žA33A33Av\AfžRAVžRAVáHAG
+=A733A733A'\)A
+A
+A®@ï®@Ð  @Ð  @°Qì@£×@£×@aë
+@"\?Å
+ž?Æff?\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bd  Bd
+=B`
+=B\{BX
+žBX(öBT(öBP33BP=qBL=qBHG®BHQìBD\)B@\)B<ffB<p€B8p€B4záB4
+B0
+B,\B(B(£×B$£×B ®B žRB
+žRBÂBÌÍB×
+B×
+B
+áHB
+ë
+Bë
+BõÃB  B  Aú{Aò(öAò=qAê=qAâQìAâffAÚffAÒzáAÊ\AÊ£×AÂ£×AºžRAºÌÍA²ÌÍAªáHAªõÃA£
+=A
+=A
+žA33A33AG®A\)AvžRAfáHAW
+=AW33AG33A7\)A7
+A'
+A®A×
+A  @ð  @ÐQì@Ð£×@°£×@õÂ@G®@b\@#33?Ç®?ÈõÂ?ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bl  Bh
+=Bd
+=Bd{B`
+žB\(öBX(öBX33BT=qBP=qBPG®BLQìBH\)BH\)BDffB@p€B<p€B<záB8
+B4
+B4\B0B,£×B(£×B(®B$žRB žRB ÂB
+ÌÍB×
+B×
+BáHBë
+B
+ë
+B
+õÃB	  B  B
+=B{Aú=qAò=qAòQìAêffAâffAâzáAÚ\AÒ£×AÊ£×AÊžRAÂÌÍAºÌÍAºáHA²õÃA«
+=A«
+=A£
+žA33A33AG®A\)A\)Ap€Aw
+=Ag33AW33AW\)AG
+A7
+A7®A'×
+A  A  A(ö@ð£×@Ð£×@ÐõÂ@±G®@G®@@c×
+@$zá?ÈõÂ?Ê=p?
+=¿  ¿  ¿  ¿  ¿  ¿  Bp  Bl
+=Bl
+=Bh{Bd
+žBd(öB`(öB\33BX=qBX=qBTG®BPQìBP\)BL\)BHffBHp€BDp€B@záB<
+B<
+B8\B4B4£×B0£×B,®B(žRB(žRB$ÂB ÌÍB ×
+B
+×
+BáHBë
+Bë
+BõÃB
+  B
+  B	
+=B{B
+žB
+žAúQìAòffAòffAêzáAâ\Aâ£×AÚ£×AÒžRAÊÌÍAÊÌÍAÂáHAºõÃA»
+=A³
+=A«
+žA«33A£33AG®A\)A\)Ap€A
+AAw33Ag\)AW
+AW
+AG®A7×
+A8  A(  A(öAQìAQì@ðõÂ@ÑG®@ÑG®@±@ë
+@=q@dzá@%
+ž?Ë
+
+?Ë
+
+?¿  ¿  ¿  ¿  Bx  Bt
+=Bp
+=Bl{Bl
+žBh(öBd(öBd33B`=qB\=qBXG®BXQìBT\)BP\)BPffBLp€BHp€BHzáBD
+B@
+B<\B<B8£×B4£×B4®B0žRB,žRB(ÂB(ÌÍB$×
+B ×
+B áHB
+ë
+Bë
+BõÃB  B  B
+=B
+{B	
+žB
+žB(öB33AúffAòzáAò\Aê£×Aâ£×AâžRAÚÌÍAÒÌÍAÊáHAÊõÃAÃ
+=A»
+=A»
+žA³33A«33A«G®A£\)A\)Ap€A
+AAA®Aw
+Ag
+AW®AW×
+AH  A8  A8(öA(QìAQìAzáA£×@ñG®@Ñ@Ñë
+@²=q@=q@\@eÂ@%Â?ÌÌÍ?Î{?
+žR¿  ¿  B|  Bx
+=Bx
+=Bt{Bp
+žBl(öBl(öBh33Bd=qBd=qB`G®B\QìBX\)BX\)BTffBPp€BPp€BLzáBH
+BH
+BD\B@B<£×B<£×B8®B4žRB4žRB0ÂB,ÌÍB(×
+B(×
+B$áHB ë
+B ë
+B
+õÃB  B  B
+=B{B
+
+žB
+
+žB	(öB33B33B=qAú\Aò£×Aò£×AêžRAâÌÍAâÌÍAÚáHAÒõÃAË
+=AË
+=AÃ
+žA»33A»33A³G®A«\)A«\)A£p€A
+AAA®AÂAÂAw®Ag×
+AX  AX  AH(öA8QìA8QìA(záA£×A£×AÌÍ@ñë
+@Ò=q@Ò=q@²\@áH@áH@fff@'
+>?Ï\)?Ï\)?!G®¿  ¿  B|
+=Bx{Bx
+žBt(öBp(öBl33Bl=qBh=qBdG®BdQìB`\)B\\)BXffBXp€BTp€BPzáBP
+BL
+BH\BHBD£×B@£×B<®B<žRB8žRB4ÂB4ÌÍB0×
+B,×
+B(áHB(ë
+B$ë
+B õÃB!  B
+  B
+=B{B
+žB
+žB
+(öB
+33B	33B=qBG®BQìAú£×AòžRAòÌÍAêÌÍAâáHAâõÃAÛ
+=AÓ
+=AË
+žAË33AÃ33A»G®A»\)A³\)A«p€A«
+A£AA®AÂAÂA×
+Aë
+Ax  Ah  AX(öAXQìAHQìA8záA8£×A(£×AÌÍAõÃA	
+ž@ò=q@Ò\@ÒáH@²áH@33@
+@g®@'®?Ð£×¿  ¿  ¿  ¿  B|
+žBx(öBx(öBt33Bp=qBl=qBlG®BhQìBd\)Bd\)B`ffB\p€BXp€BXzáBT
+BP
+BP\BLBH£×BH£×BD®B@žRB<žRB<ÂB8ÌÍB4×
+B4×
+B0áHB,ë
+B(ë
+B(õÃB%  B!  B!
+=B
+{B
+žB
+žB(öB33B
+33B
+=qB	G®BQìBQìB\)AúÌÍAòÌÍAòáHAêõÃAã
+=Aã
+=AÛ
+žAÓ33AË33AËG®AÃ\)A»\)A»p€A³
+A«A«A£®AÂAÂA×
+Aë
+A  A  Ax(öAhQìAXQìAXzáAH£×A8£×A8ÌÍA(õÃA
+žA
+žA	G®@òáH@ÒáH@Ó33@³
+@×
+@×
+@hQì¿  ¿  ¿  ¿  ¿  ¿  B|(öBx33Bx=qBt=qBpG®BlQìBl\)Bh\)BdffBdp€B`p€B\záBX
+BX
+BT\BPBP£×BL£×BH®BHžRBDžRB@ÂB<ÌÍB<×
+B8×
+B4áHB4ë
+B0ë
+B,õÃB)  B)  B%
+=B!{B!
+žB
+
+žB(öB33B33B=qB
+G®B
+QìB	QìB\)BffBffAúáHAòõÃAó
+=Aë
+=Aã
+žAã33AÛ33AÓG®AË\)AË\)AÃp€A»
+A»A³A«®A«ÂA£ÂA×
+Aë
+A  A  A{A(öAxQìAhzáAX£×AX£×AHÌÍA8õÃA9
+žA)
+žAG®Ap€A	p€@ó33@Ó
+@Ó×
+@³×
+@(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|=qBx=qBxG®BtQìBp\)Bl\)BlffBhp€Bdp€BdzáB`
+B\
+BX\BXBT£×BP£×BP®BLžRBHžRBHÂBDÌÍB@×
+B<×
+B<áHB8ë
+B4ë
+B4õÃB1  B-  B)
+=B){B%
+žB!
+žB!(öB
+33B33B=qBG®BQìB
+QìB
+\)B	ffBffBp€BzáAû
+=Aó
+=Aó
+žAë33Aã33AãG®AÛ\)AÓ\)AËp€AË
+AÃA»A»®A³ÂA«ÂA«×
+A£ë
+A  A  A{A(öA(öA=qAx£×Ah£×AXÌÍAXõÃAI
+žA9
+žA9G®A)p€Ap€AA	Â@ó×
+@Ó×
+@Ô(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|G®BxQìBx\)Bt\)BpffBlp€Blp€BhzáBd
+Bd
+B`\B\BX£×BX£×BT®BPžRBPžRBLÂBHÌÍBH×
+BD×
+B@áHB<ë
+B<ë
+B8õÃB5  B5  B1
+=B-{B)
+žB)
+žB%(öB!33B!33B
+=qBG®BQìBQìB\)B
+ffB
+ffB	p€BzáB
+B
+Aû
+žAó33Aó33AëG®Aã\)Aã\)AÛp€AÓ
+AËAËAÃ®A»ÂA»ÂA³×
+A«ë
+A¬  A€  A{A(öA(öA=qAQìAQìAxÌÍAhõÃAY
+žAY
+žAIG®A9p€A9p€A)AÂAë
+A	ë
+@ô(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|\)Bx\)BxffBtp€Bpp€BlzáBl
+Bh
+Bd\BdB`£×B\£×BX®BXžRBTžRBPÂBPÌÍBL×
+BH×
+BHáHBDë
+B@ë
+B<õÃB=  B9  B5
+=B5{B1
+žB-
+žB)(öB)33B%33B!=qB!G®B
+QìBQìB\)BffBffB
+p€B
+záB	
+B
+B\BAû33AóG®Aó\)Aë\)Aãp€Aã
+AÛAÓAË®AËÂAÃÂA»×
+A»ë
+AŽ  A¬  A¬{A€(öA(öA=qAQìAQìAffAzáAy
+žAi
+žAYG®AYp€AIp€A9A9ÂA)ë
+Aë
+A{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ffBxp€Bxp€BtzáBp
+Bl
+Bl\BhBd£×Bd£×B`®B\žRBXžRBXÂBTÌÍBP×
+BP×
+BLáHBHë
+BHë
+BDõÃBA  B=  B=
+=B9{B5
+žB5
+žB1(öB-33B)33B)=qB%G®B!QìB!QìB
+\)BffBffBp€BzáB
+
+B
+
+B	\BBB£×Aû\)Aó\)Aóp€Aë
+AãAãAÛ®AÓÂAËÂAË×
+AÃë
+AŒ  AŒ  AŽ{A¬(öA¬(öA€=qAQìAQìAffAzáA\A\AyG®Aip€AYp€AYAIÂA9ë
+A9ë
+A*{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|p€BxzáBx
+Bt
+Bp\BlBl£×Bh£×Bd®BdžRB`žRB\ÂBXÌÍBX×
+BT×
+BPáHBPë
+BLë
+BHõÃBI  BE  BA
+=B={B=
+žB9
+žB5(öB533B133B-=qB)G®B)QìB%QìB!\)B!ffB
+ffBp€BzáB
+B
+B
+\B
+B	B£×B®B®Aûp€Aó
+AóAëAã®AãÂAÛÂAÓ×
+AËë
+AÌ  AÄ  AŒ{AŒ(öAŽ(öA¬=qA¬QìA€QìAffAzáA\A\A£×AžRAyp€AiAYÂAYë
+AIë
+A:{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|
+Bx
+Bx\BtBp£×Bl£×Bl®BhžRBdžRBdÂB`ÌÍB\×
+BX×
+BXáHBTë
+BPë
+BPõÃBM  BI  BI
+=BE{BA
+žB=
+žB=(öB933B533B5=qB1G®B-QìB)QìB)\)B%ffB!ffB!p€B
+záB
+B
+B\BB
+B
+£×B	®B®BžRBÂAûAóAó®AëÂAãÂAã×
+AÛë
+AÔ  AÌ  AÌ{AÄ(öAŒ(öAŒ=qAŽQìA¬QìA¬ffA€záA\A\A£×AžRAžRAÌÍAyÂAië
+AYë
+AZ{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|\BxBx£×Bt£×Bp®BlžRBlžRBhÂBdÌÍBd×
+B`×
+B\áHBXë
+BXë
+BTõÃBQ  BQ  BM
+=BI{BI
+žBE
+žBA(öB=33B=33B9=qB5G®B5QìB1QìB-\)B)ffB)ffB%p€B!záB!
+B
+
+B\BBB£×B
+®B
+®B	žRBÂBÌÍBÌÍAû®AóÂAóÂAë×
+Aãë
+Aä  AÜ  AÔ{AÌ(öAÌ(öAÄ=qAŒQìAŒQìAŽffA¬záA¬\A€\A£×AžRAžRAÌÍAáHAõÃAyë
+Aj{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|£×Bx£×Bx®BtžRBpžRBlÂBlÌÍBh×
+Bd×
+BdáHB`ë
+B\ë
+BXõÃBY  BU  BQ
+=BQ{BM
+žBI
+žBI(öBE33BA33B==qB=G®B9QìB5QìB5\)B1ffB-ffB)p€B)záB%
+B!
+B!\B
+BB£×B®B®B
+žRB
+ÂB	ÌÍBÌÍB×
+BáHAûÂAó×
+Aóë
+Aì  Aä  Aä{AÜ(öAÔ(öAÌ=qAÌQìAÄQìAŒffAŒzáAŽ\A¬\A¬£×A€žRAžRAÌÍAáHAõÃAõÃA
+
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|®BxžRBxžRBtÂBpÌÍBl×
+Bl×
+BháHBdë
+Bdë
+B`õÃB]  BY  BY
+=BU{BQ
+žBQ
+žBM(öBI33BI33BE=qBAG®B=QìB=QìB9\)B5ffB5ffB1p€B-záB)
+B)
+B%\B!B!B
+£×B®B®BžRBÂB
+ÌÍB
+ÌÍB	×
+BáHBáHBë
+Aûë
+Aô  Aô  Aì{Aä(öAä(öAÜ=qAÔQìAÌQìAÌffAÄzáAŒ\AŒ\AŽ£×A¬žRA¬žRA€ÌÍAáHAõÃAõÃA
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|žRBxÂBxÌÍBt×
+Bp×
+BláHBlë
+Bhë
+BdõÃBe  Ba  B]
+=BY{BY
+žBU
+žBQ(öBQ33BM33BI=qBIG®BEQìBAQìB=\)B=ffB9ffB5p€B5záB1
+B-
+B)\B)B%B!£×B!®B
+®BžRBÂBÌÍBÌÍB
+×
+B
+áHB	áHBë
+BõÃB  Aü  Aô{Aô(öAì(öAä=qAäQìAÜQìAÔffAÌzáAÌ\AÄ\AŒ£×AŒžRAŽžRA¬ÌÍA¬áHA€õÃAõÃA
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÌÍBx×
+Bx×
+BtáHBpë
+Blë
+BlõÃBi  Be  Be
+=Ba{B]
+žBY
+žBY(öBU33BQ33BQ=qBMG®BIQìBIQìBE\)BAffB=ffB=p€B9záB5
+B5
+B1\B-B)B)£×B%®B!®B!žRB
+ÂBÌÍBÌÍB×
+BáHB
+áHB
+ë
+B	õÃB  B  B
+=Aü(öAô(öAô=qAìQìAäQìAäffAÜzáAÔ\AÌ\AÌ£×AÄžRAŒžRAŒÌÍAŽáHA¬õÃA¬õÃA¥
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|×
+BxáHBxë
+Btë
+BpõÃBm  Bm  Bi
+=Be{Be
+žBa
+žB](öBY33BY33BU=qBQG®BQQìBMQìBI\)BIffBEffBAp€B=záB=
+B9
+B5\B5B1B-£×B)®B)®B%žRB!ÂB!ÌÍB
+ÌÍB×
+BáHBáHBë
+B
+õÃB  B
+  B
+=B{B{Aü=qAôQìAôQìAìffAäzáAä\AÜ\AÔ£×AÌžRAÌžRAÄÌÍAŒáHAŒõÃAŽõÃA­
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ë
+Bxë
+BxõÃBu  Bq  Bm
+=Bm{Bi
+žBe
+žBe(öBa33B]33BY=qBYG®BUQìBQQìBQ\)BMffBIffBIp€BEzáBA
+B=
+B=\B9B5B5£×B1®B-®B)žRB)ÂB%ÌÍB!ÌÍB!×
+B
+áHBáHBë
+BõÃB  B  B
+=B
+{B{B
+žB(öAüQìAôffAôzáAì\Aä\Aä£×AÜžRAÔžRAÌÌÍAÌáHAÄõÃAŒõÃAœ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|õÃBy  By  Bu
+=Bq{Bm
+žBm
+žBi(öBe33Be33Ba=qB]G®BYQìBYQìBU\)BQffBQffBMp€BIzáBI
+BE
+BA\B=B=B9£×B5®B5®B1žRB-ÂB)ÌÍB)ÌÍB%×
+B!áHB!áHB
+ë
+BõÃB  B  B
+=B{B{B
+
+žB(öB(öB33AüzáAô\Aô\Aì£×AäžRAäžRAÜÌÍAÔáHAÌõÃAÌõÃAÅ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}  By
+=By{Bu
+žBq
+žBm(öBm33Bi33Be=qBeG®BaQìB]QìBY\)BYffBUffBQp€BQzáBM
+BI
+BI\BEBAB=£×B=®B9®B5žRB5ÂB1ÌÍB-ÌÍB)×
+B)áHB%áHB!ë
+B!õÃB
+  B  B
+=B{B{B
+žB(öB
+(öB33B=qBG®Aü\Aô£×AôžRAìžRAäÌÍAäáHAÜõÃAÔõÃAÍ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}{By
+žBy
+žBu(öBq33Bm33Bm=qBiG®BeQìBeQìBa\)B]ffBYffBYp€BUzáBQ
+BQ
+BM\BIBIBE£×BA®B=®B=žRB9ÂB5ÌÍB5ÌÍB1×
+B-áHB)áHB)ë
+B%õÃB"  B"  B
+
+=B{B{B
+žB(öB(öB33B
+=qBG®BG®BQìAüžRAôžRAôÌÍAìáHAäõÃAäõÃAÝ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+žBy(öBy33Bu33Bq=qBmG®BmQìBiQìBe\)BeffBaffB]p€BYzáBY
+BU
+BQ\BQBMBI£×BI®BE®BAžRB=ÂB=ÌÍB9ÌÍB5×
+B5áHB1áHB-ë
+B)õÃB*  B&  B"
+=B"{B
+{B
+žB(öB(öB33B=qBG®B
+G®BQìB\)B\)AüÌÍAôáHAôõÃAìõÃAå
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}33By33By=qBuG®BqQìBmQìBm\)BiffBeffBep€BazáB]
+BY
+BY\BUBQBQ£×BM®BI®BIžRBEÂBAÌÍB=ÌÍB=×
+B9áHB5áHB5ë
+B1õÃB.  B*  B*
+=B&{B"{B"
+žB
+(öB(öB33B=qBG®BG®BQìB
+\)B\)BffBp€AüõÃAôõÃAõ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}=qByG®ByQìBuQìBq\)BmffBmffBip€BezáBe
+Ba
+B]\BYBYBU£×BQ®BQ®BMžRBIÂBIÌÍBEÌÍBA×
+B=áHB=áHB9ë
+B5õÃB6  B2  B.
+=B*{B*{B&
+žB"(öB"(öB
+33B=qBG®BG®BQìB\)B\)B
+ffBp€BzáBzáAý
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}QìByQìBy\)BuffBqffBmp€BmzáBi
+Be
+Be\BaB]BY£×BY®BU®BQžRBQÂBMÌÍBIÌÍBI×
+BEáHBAáHB=ë
+B=õÃB:  B6  B6
+=B2{B.{B*
+žB*(öB&(öB"33B"=qB
+G®BG®BQìB\)B\)BffBp€B
+záBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}\)ByffByffBup€BqzáBm
+Bm
+Bi\BeBeBa£×B]®BY®BYžRBUÂBQÌÍBQÌÍBM×
+BIáHBIáHBEë
+BAõÃB>  B>  B:
+=B6{B6{B2
+žB.(öB*(öB*33B&=qB"G®B"G®B
+QìB\)B\)BffBp€BzáBzáB
+
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ffByp€ByzáBu
+Bq
+Bm\BmBiBe£×Be®Ba®B]žRBYÂBYÌÍBUÌÍBQ×
+BQáHBMáHBIë
+BIõÃBF  BB  B>
+=B>{B:{B6
+žB6(öB2(öB.33B*=qB*G®B&G®B"QìB"\)B
+\)BffBp€BzáBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}záBy
+By
+Bu\BqBmBm£×Bi®Be®BežRBaÂB]ÌÍBYÌÍBY×
+BUáHBQáHBQë
+BMõÃBJ  BJ  BF
+=BB{B>{B>
+žB:(öB6(öB633B2=qB.G®B*G®B*QìB&\)B"\)B"ffB
+p€BzáBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+By\ByBuBq£×Bm®Bm®BižRBeÂBeÌÍBaÌÍB]×
+BYáHBYáHBUë
+BQõÃBR  BN  BJ
+=BJ{BF{BB
+žB>(öB>(öB:33B6=qB6G®B2G®B.QìB*\)B*\)B&ffB"p€B"záB
+záB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ByBy£×Bu®Bq®BmžRBmÂBiÌÍBeÌÍBe×
+BaáHB]áHBYë
+BYõÃBV  BR  BR
+=BN{BJ{BJ
+žBF(öBB(öB>33B>=qB:G®B6G®B6QìB2\)B.\)B*ffB*p€B&záB"záB"
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}£×By®By®BužRBqÂBmÌÍBmÌÍBi×
+BeáHBeáHBaë
+B]õÃBZ  BZ  BV
+=BR{BR{BN
+žBJ(öBJ(öBF33BB=qB>G®B>G®B:QìB6\)B6\)B2ffB.p€B*záB*záB&
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}®ByžRByÂBuÌÍBqÌÍBm×
+BmáHBiáHBeë
+BeõÃBb  B^  BZ
+=BZ{BV{BR
+žBR(öBN(öBJ33BJ=qBFG®BBG®B>QìB>\)B:\)B6ffB6p€B2záB.záB*
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÂByÌÍByÌÍBu×
+BqáHBmáHBmë
+BiõÃBf  Bf  Bb
+=B^{BZ{BZ
+žBV(öBR(öBR33BN=qBJG®BJG®BFQìBB\)B>\)B>ffB:p€B6záB6záB2
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÌÍBy×
+ByáHBuáHBqë
+BmõÃBn  Bj  Bf
+=Bf{Bb{B^
+žBZ(öBZ(öBV33BR=qBRG®BNG®BJQìBJ\)BF\)BBffB>p€B>záB:záB6
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}áHByáHByë
+BuõÃBr  Bn  Bn
+=Bj{Bf{Bf
+žBb(öB^(öBZ33BZ=qBVG®BRG®BRQìBN\)BJ\)BJffBFp€BBzáB>záB>
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ë
+ByõÃBz  Bv  Br
+=Bn{Bn{Bj
+žBf(öBf(öBb33B^=qBZG®BZG®BVQìBR\)BR\)BNffBJp€BJzáBFzáBB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~  Bz  Bz
+=Bv{Br{Bn
+žBn(öBj(öBf33Bf=qBbG®B^G®BZQìBZ\)BV\)BRffBRp€BNzáBJzáBJ
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~
+=Bz{Bz{Bv
+žBr(öBn(öBn33Bj=qBfG®BfG®BbQìB^\)BZ\)BZffBVp€BRzáBRzáBN
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~{Bz
+žBz(öBv(öBr33Bn=qBnG®BjG®BfQìBf\)Bb\)B^ffBZp€BZzáBVzáBR
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~(öBz(öBz33Bv=qBrG®BnG®BnQìBj\)Bf\)BfffBbp€B^záBZzáBZ
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~33Bz=qBzG®BvG®BrQìBn\)Bn\)BjffBfp€BfzáBbzáB^
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~G®BzG®BzQìBv\)Br\)BnffBnp€BjzáBfzáBf
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~QìBz\)Bz\)BvffBrp€BnzáBnzáBj
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~\)BzffBzp€BvzáBrzáBn
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~p€BzzáBzzáBv
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~záBz
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B|  Bx  Bt  Bp  Bl  Bh  Bd  B`  B\  BX  BT  BP  BL  BH  BD  B@  B<  B8  B4  B0  B,  B(  B$  B   B
+  B  B  B  B
+  B  B  B   Aø  Að  Aè  Aà  AØ  AÐ  AÈ  AÀ  Až  A°  Aš  A   A  A  A  A  Ap  A`  AP  A@  A0  A   A  A   @à  @À  @   @  @@  @   ?      B|
+=Bx
+=Bt
+=Bp
+=Bl
+=Bh
+=Bd
+=B`
+=B\
+=BX
+=BT
+=BP
+=BL
+=BH
+=BD
+=B@
+=B<
+=B8
+=B4
+=B0
+=B,
+=B(
+=B$
+=B 
+=B
+
+=B
+=B
+=B
+=B
+
+=B
+=B
+=B 
+=Aø{Að{Aè{Aà{AØ{AÐ{AÈ{AÀ{Až{A°{Aš{A {A{A{A{A{Ap(öA`(öAP(öA@(öA0(öA (öA(öA (ö@àQì@ÀQì@ Qì@Qì@@£×@ £×?G®<#×
+B|{Bx{Bt{Bp{Bl{Bh{Bd{B`{B\{BX{BT{BP{BL{BH{BD{B@{B<{B8{B4{B0{B,{B({B${B {B
+{B{B{B{B
+{B{B{B {Aø(öAð(öAè(öAà(öAØ(öAÐ(öAÈ(öAÀ(öAž(öA°(öAš(öA (öA(öA(öA(öA(öApQìA`QìAPQìA@QìA0QìA QìAQìA Qì@à£×@À£×@ £×@£×@AG®@G®?\<£×
+B|
+žBx
+žBt
+žBp
+žBl
+žBh
+žBd
+žB`
+žB\
+žBX
+žBT
+žBP
+žBL
+žBH
+žBD
+žB@
+žB<
+žB8
+žB4
+žB0
+žB,
+žB(
+žB$
+žB 
+žB
+
+žB
+žB
+žB
+žB
+
+žB
+žB
+žB 
+žAø=qAð=qAè=qAà=qAØ=qAÐ=qAÈ=qAÀ=qAž=qA°=qAš=qA =qA=qA=qA=qA=qApzáA`záAPzáA@záA0záA záAzáA zá@àõÃ@ÀõÃ@ õÃ@õÃ@Aë
+@ë
+?×
+<õÂB|(öBx(öBt(öBp(öBl(öBh(öBd(öB`(öB\(öBX(öBT(öBP(öBL(öBH(öBD(öB@(öB<(öB8(öB4(öB0(öB,(öB((öB$(öB (öB
+(öB(öB(öB(öB
+(öB(öB(öB (öAøQìAðQìAèQìAàQìAØQìAÐQìAÈQìAÀQìAžQìA°QìAšQìA QìAQìAQìAQìAQìAp£×A`£×AP£×A@£×A0£×A £×A£×A £×@áG®@ÁG®@¡G®@G®@B\@\?
+
+ž=#×
+B|33Bx33Bt33Bp33Bl33Bh33Bd33B`33B\33BX33BT33BP33BL33BH33BD33B@33B<33B833B433B033B,33B(33B$33B 33B
+33B33B33B33B
+33B33B33B 33AøffAðffAèffAàffAØffAÐffAÈffAÀffAžffA°ffAšffA ffAffAffAffAffApÌÍA`ÌÍAPÌÍA@ÌÍA0ÌÍA ÌÍAÌÍA ÌÍ@á@Á@¡@@C33@33?ff=LÌÍB|=qBx=qBt=qBp=qBl=qBh=qBd=qB`=qB\=qBX=qBT=qBP=qBL=qBH=qBD=qB@=qB<=qB8=qB4=qB0=qB,=qB(=qB$=qB =qB
+=qB=qB=qB=qB
+=qB=qB=qB =qAøzáAðzáAèzáAàzáAØzáAÐzáAÈzáAÀzáAžzáA°záAšzáA záAzáAzáAzáAzáApõÃA`õÃAPõÃA@õÃA0õÃA õÃAõÃA õÃ@áë
+@Áë
+@¡ë
+@ë
+@C×
+@×
+?®=uÂB|G®BxG®BtG®BpG®BlG®BhG®BdG®B`G®B\G®BXG®BTG®BPG®BLG®BHG®BDG®B@G®B<G®B8G®B4G®B0G®B,G®B(G®B$G®B G®B
+G®BG®BG®BG®B
+G®BG®BG®B G®Aø\Að\Aè\Aà\AØ\AÐ\AÈ\AÀ\Až\A°\Aš\A \A\A\A\A\Aq
+žAa
+žAQ
+žAA
+žA1
+žA!
+žA
+žA
+ž@â=q@Â=q@¢=q@=q@Dzá@zá?õÃ=\)B|QìBxQìBtQìBpQìBlQìBhQìBdQìB`QìB\QìBXQìBTQìBPQìBLQìBHQìBDQìB@QìB<QìB8QìB4QìB0QìB,QìB(QìB$QìB QìB
+QìBQìBQìBQìB
+QìBQìBQìB QìAø£×Að£×Aè£×Aà£×AØ£×AÐ£×AÈ£×AÀ£×Až£×A°£×Aš£×A £×A£×A£×A£×A£×AqG®AaG®AQG®AAG®A1G®A!G®AG®AG®@â\@Â\@¢\@\@E
+ž@
+ž?=q=£×
+B|\)Bx\)Bt\)Bp\)Bl\)Bh\)Bd\)B`\)B\\)BX\)BT\)BP\)BL\)BH\)BD\)B@\)B<\)B8\)B4\)B0\)B,\)B(\)B$\)B \)B
+\)B\)B\)B\)B
+\)B\)B\)B \)AøžRAðžRAèžRAàžRAØžRAÐžRAÈžRAÀžRAžžRA°žRAšžRA žRAžRAžRAžRAžRAqp€Aap€AQp€AAp€A1p€A!p€Ap€Ap€@âáH@ÂáH@¢áH@áH@EÂ@Â?
+=žQìB|ffBxffBtffBpffBlffBhffBdffB`ffB\ffBXffBTffBPffBLffBHffBDffB@ffB<ffB8ffB4ffB0ffB,ffB(ffB$ffB ffB
+ffBffBffBffB
+ffBffBffB ffAøÌÍAðÌÍAèÌÍAàÌÍAØÌÍAÐÌÍAÈÌÍAÀÌÍAžÌÍA°ÌÍAšÌÍA ÌÍAÌÍAÌÍAÌÍAÌÍAqAaAQAAA1A!AA@ã33@Ã33@£33@33@Fff@ff?ÌÍ=ÌÌÍB|p€Bxp€Btp€Bpp€Blp€Bhp€Bdp€B`p€B\p€BXp€BTp€BPp€BLp€BHp€BDp€B@p€B<p€B8p€B4p€B0p€B,p€B(p€B$p€B p€B
+p€Bp€Bp€Bp€B
+p€Bp€Bp€B p€AøáHAðáHAèáHAàáHAØáHAÐáHAÈáHAÀáHAžáHA°áHAšáHA áHAáHAáHAáHAáHAqÂAaÂAQÂAAÂA1ÂA!ÂAÂAÂ@ã
+@Ã
+@£
+@
+@G
+=@
+=?{=áG®B|záBxzáBtzáBpzáBlzáBhzáBdzáB`záB\záBXzáBTzáBPzáBLzáBHzáBDzáB@záB<záB8záB4záB0záB,záB(záB$záB záB
+záBzáBzáBzáB
+záBzáBzáB záAøõÃAðõÃAèõÃAàõÃAØõÃAÐõÃAÈõÃAÀõÃAžõÃA°õÃAšõÃA õÃAõÃAõÃAõÃAõÃAqë
+Aaë
+AQë
+AAë
+A1ë
+A!ë
+Aë
+Aë
+@ã×
+@Ã×
+@£×
+@×
+@G®@®?\)=õÂB|
+Bx
+Bt
+Bp
+Bl
+Bh
+Bd
+B`
+B\
+BX
+BT
+BP
+BL
+BH
+BD
+B@
+B<
+B8
+B4
+B0
+B,
+B(
+B$
+B 
+B
+
+B
+B
+B
+B
+
+B
+B
+B 
+Aù
+=Añ
+=Aé
+=Aá
+=AÙ
+=AÑ
+=AÉ
+=AÁ
+=A¹
+=A±
+=A©
+=A¡
+=A
+=A
+=A
+=A
+=Ar{Ab{AR{AB{A2{A"{A{A{@ä(ö@Ä(ö@€(ö@(ö@HQì@Qì?£×>
+žB|\Bx\Bt\Bp\Bl\Bh\Bd\B`\B\\BX\BT\BP\BL\BH\BD\B@\B<\B8\B4\B0\B,\B(\B$\B \B
+\B\B\B\B
+\B\B\B \Aù
+žAñ
+žAé
+žAá
+žAÙ
+žAÑ
+žAÉ
+žAÁ
+žA¹
+žA±
+žA©
+žA¡
+žA
+žA
+žA
+žA
+žAr=qAb=qAR=qAB=qA2=qA"=qA=qA=q@äzá@Äzá@€zá@zá@HõÃ@õÃ?ë
+>\)B|BxBtBpBlBhBdB`B\BXBTBPBLBHBDB@B<B8B4B0B,B(B$B B
+BBBB
+BBB Aù33Añ33Aé33Aá33AÙ33AÑ33AÉ33AÁ33A¹33A±33A©33A¡33A33A33A33A33ArffAbffARffABffA2ffA"ffAffAff@äÌÍ@ÄÌÍ@€ÌÍ@ÌÍ@I@	?33>B|£×Bx£×Bt£×Bp£×Bl£×Bh£×Bd£×B`£×B\£×BX£×BT£×BP£×BL£×BH£×BD£×B@£×B<£×B8£×B4£×B0£×B,£×B(£×B$£×B £×B
+£×B£×B£×B£×B
+£×B£×B£×B £×AùG®AñG®AéG®AáG®AÙG®AÑG®AÉG®AÁG®A¹G®A±G®A©G®A¡G®AG®AG®AG®AG®Ar\Ab\AR\AB\A2\A"\A\A\@å
+ž@Å
+ž@¥
+ž@
+
+ž@J=q@
+=q?zá>#×
+B|®Bx®Bt®Bp®Bl®Bh®Bd®B`®B\®BX®BT®BP®BL®BH®BD®B@®B<®B8®B4®B0®B,®B(®B$®B ®B
+®B®B®B®B
+®B®B®B ®Aù\)Añ\)Aé\)Aá\)AÙ\)AÑ\)AÉ\)AÁ\)A¹\)A±\)A©\)A¡\)A\)A\)A\)A\)AržRAbžRARžRABžRA2žRA"žRAžRAžR@åp€@Åp€@¥p€@
+p€@JáH@
+áH?Â>.{B|žRBxžRBtžRBpžRBlžRBhžRBdžRB`žRB\žRBXžRBTžRBPžRBLžRBHžRBDžRB@žRB<žRB8žRB4žRB0žRB,žRB(žRB$žRB žRB
+žRBžRBžRBžRB
+žRBžRBžRB žRAùp€Añp€Aép€Aáp€AÙp€AÑp€AÉp€AÁp€A¹p€A±p€A©p€A¡p€Ap€Ap€Ap€Ap€AráHAbáHARáHABáHA2áHA"áHAáHAáH@åÂ@ÅÂ@¥Â@
+Â@K
+@
+
+?
+>>8QìB|ÂBxÂBtÂBpÂBlÂBhÂBdÂB`ÂB\ÂBXÂBTÂBPÂBLÂBHÂBDÂB@ÂB<ÂB8ÂB4ÂB0ÂB,ÂB(ÂB$ÂB ÂB
+ÂBÂBÂBÂB
+ÂBÂBÂB ÂAù
+Añ
+Aé
+Aá
+AÙ
+AÑ
+AÉ
+AÁ
+A¹
+A±
+A©
+A¡
+A
+A
+A
+A
+As
+=Ac
+=AS
+=AC
+=A3
+=A#
+=A
+=A
+=@æ{@Æ{@Š{@{@L(ö@
+(ö?Qì>B\B|ÌÍBxÌÍBtÌÍBpÌÍBlÌÍBhÌÍBdÌÍB`ÌÍB\ÌÍBXÌÍBTÌÍBPÌÍBLÌÍBHÌÍBDÌÍB@ÌÍB<ÌÍB8ÌÍB4ÌÍB0ÌÍB,ÌÍB(ÌÍB$ÌÍB ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍBÌÍBÌÍB ÌÍAùAñAéAáAÙAÑAÉAÁA¹A±A©A¡AAAAAs33Ac33AS33AC33A333A#33A33A33@æff@Æff@Šff@ff@LÌÍ@
+ÌÍ?>LÌÍB|×
+Bx×
+Bt×
+Bp×
+Bl×
+Bh×
+Bd×
+B`×
+B\×
+BX×
+BT×
+BP×
+BL×
+BH×
+BD×
+B@×
+B<×
+B8×
+B4×
+B0×
+B,×
+B(×
+B$×
+B ×
+B
+×
+B×
+B×
+B×
+B
+×
+B×
+B×
+B ×
+Aù®Añ®Aé®Aá®AÙ®AÑ®AÉ®AÁ®A¹®A±®A©®A¡®A®A®A®A®As\)Ac\)AS\)AC\)A3\)A#\)A\)A\)@æžR@ÆžR@ŠžR@žR@Mp€@
+p€?áH>W
+=B|áHBxáHBtáHBpáHBláHBháHBdáHB`áHB\áHBXáHBTáHBPáHBLáHBHáHBDáHB@áHB<áHB8áHB4áHB0áHB,áHB(áHB$áHB áHB
+áHBáHBáHBáHB
+áHBáHBáHB áHAùÂAñÂAéÂAáÂAÙÂAÑÂAÉÂAÁÂA¹ÂA±ÂA©ÂA¡ÂAÂAÂAÂAÂAs
+Ac
+AS
+AC
+A3
+A#
+A
+A
+@ç
+=@Ç
+=@§
+=@
+=@N{@{?(ö>aG®B|ë
+Bxë
+Btë
+Bpë
+Blë
+Bhë
+Bdë
+B`ë
+B\ë
+BXë
+BTë
+BPë
+BLë
+BHë
+BDë
+B@ë
+B<ë
+B8ë
+B4ë
+B0ë
+B,ë
+B(ë
+B$ë
+B ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+Bë
+Bë
+B ë
+Aù×
+Añ×
+Aé×
+Aá×
+AÙ×
+AÑ×
+AÉ×
+AÁ×
+A¹×
+A±×
+A©×
+A¡×
+A×
+A×
+A×
+A×
+As®Ac®AS®AC®A3®A#®A®A®@ç\)@Ç\)@§\)@\)@NžR@žR?p€>k
+B|õÃBxõÃBtõÃBpõÃBlõÃBhõÃBdõÃB`õÃB\õÃBXõÃBTõÃBPõÃBLõÃBHõÃBDõÃB@õÃB<õÃB8õÃB4õÃB0õÃB,õÃB(õÃB$õÃB õÃB
+õÃBõÃBõÃBõÃB
+õÃBõÃBõÃB õÃAùë
+Añë
+Aéë
+Aáë
+AÙë
+AÑë
+AÉë
+AÁë
+A¹ë
+A±ë
+A©ë
+A¡ë
+Aë
+Aë
+Aë
+Aë
+As×
+Ac×
+AS×
+AC×
+A3×
+A#×
+A×
+A×
+@ç®@Ç®@§®@®@O\)@\)?žR>uÂB}  By  Bu  Bq  Bm  Bi  Be  Ba  B]  BY  BU  BQ  BM  BI  BE  BA  B=  B9  B5  B1  B-  B)  B%  B!  B
+  B  B  B  B
+  B	  B  B  Aú  Aò  Aê  Aâ  AÚ  AÒ  AÊ  AÂ  Aº  A²  Aª  A¢  A  A  A  A  At  Ad  AT  AD  A4  A$  A  A  @è  @È  @š  @  @P  @  ?   >  B}
+=By
+=Bu
+=Bq
+=Bm
+=Bi
+=Be
+=Ba
+=B]
+=BY
+=BU
+=BQ
+=BM
+=BI
+=BE
+=BA
+=B=
+=B9
+=B5
+=B1
+=B-
+=B)
+=B%
+=B!
+=B
+
+=B
+=B
+=B
+=B
+=B	
+=B
+=B
+=Aú{Aò{Aê{Aâ{AÚ{AÒ{AÊ{AÂ{Aº{A²{Aª{A¢{A{A{A{A{At(öAd(öAT(öAD(öA4(öA$(öA(öA(ö@èQì@ÈQì@šQì@Qì@P£×@£×?¡G®>
+
+žB}{By{Bu{Bq{Bm{Bi{Be{Ba{B]{BY{BU{BQ{BM{BI{BE{BA{B={B9{B5{B1{B-{B){B%{B!{B
+{B{B{B{B
+{B	{B{B{Aú(öAò(öAê(öAâ(öAÚ(öAÒ(öAÊ(öAÂ(öAº(öA²(öAª(öA¢(öA(öA(öA(öA(öAtQìAdQìATQìADQìA4QìA$QìAQìAQì@è£×@È£×@š£×@£×@QG®@G®?¢\>=qB}
+žBy
+žBu
+žBq
+žBm
+žBi
+žBe
+žBa
+žB]
+žBY
+žBU
+žBQ
+žBM
+žBI
+žBE
+žBA
+žB=
+žB9
+žB5
+žB1
+žB-
+žB)
+žB%
+žB!
+žB
+
+žB
+žB
+žB
+žB
+
+žB	
+žB
+žB
+žAú=qAò=qAê=qAâ=qAÚ=qAÒ=qAÊ=qAÂ=qAº=qA²=qAª=qA¢=qA=qA=qA=qA=qAtzáAdzáATzáADzáA4záA$záAzáAzá@èõÃ@ÈõÃ@šõÃ@õÃ@Që
+@ë
+?£×
+>\)B}(öBy(öBu(öBq(öBm(öBi(öBe(öBa(öB](öBY(öBU(öBQ(öBM(öBI(öBE(öBA(öB=(öB9(öB5(öB1(öB-(öB)(öB%(öB!(öB
+(öB(öB(öB(öB
+(öB	(öB(öB(öAúQìAòQìAêQìAâQìAÚQìAÒQìAÊQìAÂQìAºQìA²QìAªQìA¢QìAQìAQìAQìAQìAt£×Ad£×AT£×AD£×A4£×A$£×A£×A£×@éG®@ÉG®@©G®@G®@R\@\?¥
+ž>záB}33By33Bu33Bq33Bm33Bi33Be33Ba33B]33BY33BU33BQ33BM33BI33BE33BA33B=33B933B533B133B-33B)33B%33B!33B
+33B33B33B33B
+33B	33B33B33AúffAòffAêffAâffAÚffAÒffAÊffAÂffAºffA²ffAªffA¢ffAffAffAffAffAtÌÍAdÌÍATÌÍADÌÍA4ÌÍA$ÌÍAÌÍAÌÍ@é@É@©@@S33@33?Šff>B}=qBy=qBu=qBq=qBm=qBi=qBe=qBa=qB]=qBY=qBU=qBQ=qBM=qBI=qBE=qBA=qB==qB9=qB5=qB1=qB-=qB)=qB%=qB!=qB
+=qB=qB=qB=qB
+=qB	=qB=qB=qAúzáAòzáAêzáAâzáAÚzáAÒzáAÊzáAÂzáAºzáA²záAªzáA¢záAzáAzáAzáAzáAtõÃAdõÃATõÃADõÃA4õÃA$õÃAõÃAõÃ@éë
+@Éë
+@©ë
+@ë
+@S×
+@×
+?§®>žRB}G®ByG®BuG®BqG®BmG®BiG®BeG®BaG®B]G®BYG®BUG®BQG®BMG®BIG®BEG®BAG®B=G®B9G®B5G®B1G®B-G®B)G®B%G®B!G®B
+G®BG®BG®BG®B
+G®B	G®BG®BG®Aú\Aò\Aê\Aâ\AÚ\AÒ\AÊ\AÂ\Aº\A²\Aª\A¢\A\A\A\A\Au
+žAe
+žAU
+žAE
+žA5
+žA%
+žA
+žA
+ž@ê=q@Ê=q@ª=q@=q@Tzá@zá?šõÂ>£×
+B}QìByQìBuQìBqQìBmQìBiQìBeQìBaQìB]QìBYQìBUQìBQQìBMQìBIQìBEQìBAQìB=QìB9QìB5QìB1QìB-QìB)QìB%QìB!QìB
+QìBQìBQìBQìB
+QìB	QìBQìBQìAú£×Aò£×Aê£×Aâ£×AÚ£×AÒ£×AÊ£×AÂ£×Aº£×A²£×Aª£×A¢£×A£×A£×A£×A£×AuG®AeG®AUG®AEG®A5G®A%G®AG®AG®@ê\@Ê\@ª\@\@U
+ž@
+ž?ª=q>šõÃB}\)By\)Bu\)Bq\)Bm\)Bi\)Be\)Ba\)B]\)BY\)BU\)BQ\)BM\)BI\)BE\)BA\)B=\)B9\)B5\)B1\)B-\)B)\)B%\)B!\)B
+\)B\)B\)B\)B
+\)B	\)B\)B\)AúžRAòžRAêžRAâžRAÚžRAÒžRAÊžRAÂžRAºžRA²žRAªžRA¢žRAžRAžRAžRAžRAup€Aep€AUp€AEp€A5p€A%p€Ap€Ap€@êáH@ÊáH@ªáH@áH@UÂ@Â?«
+>®{B}ffByffBuffBqffBmffBiffBeffBaffB]ffBYffBUffBQffBMffBIffBEffBAffB=ffB9ffB5ffB1ffB-ffB)ffB%ffB!ffB
+ffBffBffBffB
+ffB	ffBffBffAúÌÍAòÌÍAêÌÍAâÌÍAÚÌÍAÒÌÍAÊÌÍAÂÌÍAºÌÍA²ÌÍAªÌÍA¢ÌÍAÌÍAÌÍAÌÍAÌÍAuAeAUAEA5A%AA@ë33@Ë33@«33@33@Vff@ff?¬ÌÍ>³33B}p€Byp€Bup€Bqp€Bmp€Bip€Bep€Bap€B]p€BYp€BUp€BQp€BMp€BIp€BEp€BAp€B=p€B9p€B5p€B1p€B-p€B)p€B%p€B!p€B
+p€Bp€Bp€Bp€B
+p€B	p€Bp€Bp€AúáHAòáHAêáHAâáHAÚáHAÒáHAÊáHAÂáHAºáHA²áHAªáHA¢áHAáHAáHAáHAáHAuÂAeÂAUÂAEÂA5ÂA%ÂAÂAÂ@ë
+@Ë
+@«
+@
+@W
+>@
+>?®{>žQìB}záByzáBuzáBqzáBmzáBizáBezáBazáB]záBYzáBUzáBQzáBMzáBIzáBEzáBAzáB=záB9záB5záB1záB-záB)záB%záB!záB
+záBzáBzáBzáB
+záB	záBzáBzáAúõÃAòõÃAêõÃAâõÃAÚõÃAÒõÃAÊõÃAÂõÃAºõÃA²õÃAªõÃA¢õÃAõÃAõÃAõÃAõÃAuë
+Aeë
+AUë
+AEë
+A5ë
+A%ë
+Aë
+Aë
+@ë×
+@Ë×
+@«×
+@×
+@W®@®?¯\)>œp€B}
+By
+Bu
+Bq
+Bm
+Bi
+Be
+Ba
+B]
+BY
+BU
+BQ
+BM
+BI
+BE
+BA
+B=
+B9
+B5
+B1
+B-
+B)
+B%
+B!
+B
+
+B
+B
+B
+B
+
+B	
+B
+B
+Aû
+=Aó
+=Aë
+=Aã
+=AÛ
+=AÓ
+=AË
+=AÃ
+=A»
+=A³
+=A«
+=A£
+=A
+=A
+=A
+=A
+=Av{Af{AV{AF{A6{A&{A{A{@ì(ö@Ì(ö@¬(ö@(ö@XQì@Qì?°£×>Â\B}\By\Bu\Bq\Bm\Bi\Be\Ba\B]\BY\BU\BQ\BM\BI\BE\BA\B=\B9\B5\B1\B-\B)\B%\B!\B
+\B\B\B\B
+\B	\B\B\Aû
+žAó
+žAë
+žAã
+žAÛ
+žAÓ
+žAË
+žAÃ
+žA»
+žA³
+žA«
+žA£
+žA
+žA
+žA
+žA
+žAv=qAf=qAV=qAF=qA6=qA&=qA=qA=q@ìzá@Ìzá@¬zá@zá@XõÂ@õÂ?±ë
+>Ç®B}ByBuBqBmBiBeBaB]BYBUBQBMBIBEBAB=B9B5B1B-B)B%B!B
+BBBB
+B	BBAû33Aó33Aë33Aã33AÛ33AÓ33AË33AÃ33A»33A³33A«33A£33A33A33A33A33AvffAfffAVffAFffA6ffA&ffAffAff@ìÌÍ@ÌÌÍ@¬ÌÍ@ÌÍ@Y@?³33>ÌÌÍB}£×By£×Bu£×Bq£×Bm£×Bi£×Be£×Ba£×B]£×BY£×BU£×BQ£×BM£×BI£×BE£×BA£×B=£×B9£×B5£×B1£×B-£×B)£×B%£×B!£×B
+£×B£×B£×B£×B
+£×B	£×B£×B£×AûG®AóG®AëG®AãG®AÛG®AÓG®AËG®AÃG®A»G®A³G®A«G®A£G®AG®AG®AG®AG®Av\Af\AV\AF\A6\A&\A\A\@í
+ž@Í
+ž@­
+ž@
+ž@Z=q@=q?Žzá>Ñë
+B}®By®Bu®Bq®Bm®Bi®Be®Ba®B]®BY®BU®BQ®BM®BI®BE®BA®B=®B9®B5®B1®B-®B)®B%®B!®B
+®B®B®B®B
+®B	®B®B®Aû\)Aó\)Aë\)Aã\)AÛ\)AÓ\)AË\)AÃ\)A»\)A³\)A«\)A£\)A\)A\)A\)A\)AvžRAfžRAVžRAFžRA6žRA&žRAžRAžR@íp€@Íp€@­p€@p€@ZáH@áH?µÂ>×
+=B}žRByžRBužRBqžRBmžRBižRBežRBažRB]žRBYžRBUžRBQžRBMžRBIžRBEžRBAžRB=žRB9žRB5žRB1žRB-žRB)žRB%žRB!žRB
+žRBžRBžRBžRB
+žRB	žRBžRBžRAûp€Aóp€Aëp€Aãp€AÛp€AÓp€AËp€AÃp€A»p€A³p€A«p€A£p€Ap€Ap€Ap€Ap€AváHAfáHAVáHAFáHA6áHA&áHAáHAáH@íÂ@ÍÂ@­Â@Â@[
+@
+?·
+>>Ü(öB}ÂByÂBuÂBqÂBmÂBiÂBeÂBaÂB]ÂBYÂBUÂBQÂBMÂBIÂBEÂBAÂB=ÂB9ÂB5ÂB1ÂB-ÂB)ÂB%ÂB!ÂB
+ÂBÂBÂBÂB
+ÂB	ÂBÂBÂAû
+Aó
+Aë
+Aã
+AÛ
+AÓ
+AË
+AÃ
+A»
+A³
+A«
+A£
+A
+A
+A
+A
+Aw
+=Ag
+=AW
+=AG
+=A7
+=A'
+=A
+=A
+=@î{@Î{@®{@{@\(ö@
+(ö?žQì>áG®B}ÌÍByÌÍBuÌÍBqÌÍBmÌÍBiÌÍBeÌÍBaÌÍB]ÌÍBYÌÍBUÌÍBQÌÍBMÌÍBIÌÍBEÌÍBAÌÍB=ÌÍB9ÌÍB5ÌÍB1ÌÍB-ÌÍB)ÌÍB%ÌÍB!ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB	ÌÍBÌÍBÌÍAûAóAëAãAÛAÓAËAÃA»A³A«A£AAAAAw33Ag33AW33AG33A733A'33A33A33@îff@Îff@®ff@ff@\ÌÍ@
+ÌÍ?¹>æffB}×
+By×
+Bu×
+Bq×
+Bm×
+Bi×
+Be×
+Ba×
+B]×
+BY×
+BU×
+BQ×
+BM×
+BI×
+BE×
+BA×
+B=×
+B9×
+B5×
+B1×
+B-×
+B)×
+B%×
+B!×
+B
+×
+B×
+B×
+B×
+B
+×
+B	×
+B×
+B×
+Aû®Aó®Aë®Aã®AÛ®AÓ®AË®AÃ®A»®A³®A«®A£®A®A®A®A®Aw\)Ag\)AW\)AG\)A7\)A'\)A\)A\)@îžR@ÎžR@®žR@žR@]p€@
+p€?ºáH>ë
+B}áHByáHBuáHBqáHBmáHBiáHBeáHBaáHB]áHBYáHBUáHBQáHBMáHBIáHBEáHBAáHB=áHB9áHB5áHB1áHB-áHB)áHB%áHB!áHB
+áHBáHBáHBáHB
+áHB	áHBáHBáHAûÂAóÂAëÂAãÂAÛÂAÓÂAËÂAÃÂA»ÂA³ÂA«ÂA£ÂAÂAÂAÂAÂAw
+Ag
+AW
+AG
+A7
+A'
+A
+A
+@ï
+=@Ï
+=@¯
+=@
+=@^{@
+{?Œ(ö>ð£×B}ë
+Byë
+Buë
+Bqë
+Bmë
+Bië
+Beë
+Baë
+B]ë
+BYë
+BUë
+BQë
+BMë
+BIë
+BEë
+BAë
+B=ë
+B9ë
+B5ë
+B1ë
+B-ë
+B)ë
+B%ë
+B!ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B	ë
+Bë
+Bë
+Aû×
+Aó×
+Aë×
+Aã×
+AÛ×
+AÓ×
+AË×
+AÃ×
+A»×
+A³×
+A«×
+A£×
+A×
+A×
+A×
+A×
+Aw®Ag®AW®AG®A7®A'®A®A®@ï\)@Ï\)@¯\)@\)@^žR@
+žR?œp€>õÂB}õÃByõÃBuõÃBqõÃBmõÃBiõÃBeõÃBaõÃB]õÃBYõÃBUõÃBQõÃBMõÃBIõÃBEõÃBAõÃB=õÃB9õÃB5õÃB1õÃB-õÃB)õÃB%õÃB!õÃB
+õÃBõÃBõÃBõÃB
+õÃB	õÃBõÃBõÃAûë
+Aóë
+Aëë
+Aãë
+AÛë
+AÓë
+AËë
+AÃë
+A»ë
+A³ë
+A«ë
+A£ë
+Aë
+Aë
+Aë
+Aë
+Aw×
+Ag×
+AW×
+AG×
+A7×
+A'×
+A×
+A×
+@ï®@Ï®@¯®@®@_\)@\)?ŸžR>úáHB~  Bz  Bv  Br  Bn  Bj  Bf  Bb  B^  BZ  BV  BR  BN  BJ  BF  BB  B>  B:  B6  B2  B.  B*  B&  B"  B
+  B  B  B  B  B
+  B  B  Aü  Aô  Aì  Aä  AÜ  AÔ  AÌ  AÄ  AŒ  AŽ  A¬  A€  A  A  A  A  Ax  Ah  AX  AH  A8  A(  A  A  @ð  @Ð  @°  @  @`  @   ?À  ?   B~
+=Bz
+=Bv
+=Br
+=Bn
+=Bj
+=Bf
+=Bb
+=B^
+=BZ
+=BV
+=BR
+=BN
+=BJ
+=BF
+=BB
+=B>
+=B:
+=B6
+=B2
+=B.
+=B*
+=B&
+=B"
+=B
+
+=B
+=B
+=B
+=B
+=B
+
+=B
+=B
+=Aü{Aô{Aì{Aä{AÜ{AÔ{AÌ{AÄ{AŒ{AŽ{A¬{A€{A{A{A{A{Ax(öAh(öAX(öAH(öA8(öA((öA(öA(ö@ðQì@ÐQì@°Qì@Qì@`£×@ £×?ÁG®?\B~{Bz{Bv{Br{Bn{Bj{Bf{Bb{B^{BZ{BV{BR{BN{BJ{BF{BB{B>{B:{B6{B2{B.{B*{B&{B"{B
+{B{B{B{B{B
+{B{B{Aü(öAô(öAì(öAä(öAÜ(öAÔ(öAÌ(öAÄ(öAŒ(öAŽ(öA¬(öA€(öA(öA(öA(öA(öAxQìAhQìAXQìAHQìA8QìA(QìAQìAQì@ð£×@Ð£×@°£×@£×@aG®@!G®?Â\?
+žB~
+žBz
+žBv
+žBr
+žBn
+žBj
+žBf
+žBb
+žB^
+žBZ
+žBV
+žBR
+žBN
+žBJ
+žBF
+žBB
+žB>
+žB:
+žB6
+žB2
+žB.
+žB*
+žB&
+žB"
+žB
+
+žB
+žB
+žB
+žB
+žB
+
+žB
+žB
+žAü=qAô=qAì=qAä=qAÜ=qAÔ=qAÌ=qAÄ=qAŒ=qAŽ=qA¬=qA€=qA=qA=qA=qA=qAxzáAhzáAXzáAHzáA8záA(záAzáAzá@ðõÂ@ÐõÂ@°õÂ@õÂ@aë
+@!ë
+?Ã×
+?®B~(öBz(öBv(öBr(öBn(öBj(öBf(öBb(öB^(öBZ(öBV(öBR(öBN(öBJ(öBF(öBB(öB>(öB:(öB6(öB2(öB.(öB*(öB&(öB"(öB
+(öB(öB(öB(öB(öB
+(öB(öB(öAüQìAôQìAìQìAäQìAÜQìAÔQìAÌQìAÄQìAŒQìAŽQìA¬QìA€QìAQìAQìAQìAQìAx£×Ah£×AX£×AH£×A8£×A(£×A£×A£×@ñG®@ÑG®@±G®@G®@b\@"\?Å
+ž?
+=qB~33Bz33Bv33Br33Bn33Bj33Bf33Bb33B^33BZ33BV33BR33BN33BJ33BF33BB33B>33B:33B633B233B.33B*33B&33B"33B
+33B33B33B33B33B
+33B33B33AüffAôffAìffAäffAÜffAÔffAÌffAÄffAŒffAŽffA¬ffA€ffAffAffAffAffAxÌÍAhÌÍAXÌÍAHÌÍA8ÌÍA(ÌÍAÌÍAÌÍ@ñ@Ñ@±@@c33@#33?Æff?
+ÌÍB~=qBz=qBv=qBr=qBn=qBj=qBf=qBb=qB^=qBZ=qBV=qBR=qBN=qBJ=qBF=qBB=qB>=qB:=qB6=qB2=qB.=qB*=qB&=qB"=qB
+=qB=qB=qB=qB=qB
+=qB=qB=qAüzáAôzáAìzáAäzáAÜzáAÔzáAÌzáAÄzáAŒzáAŽzáA¬záA€záAzáAzáAzáAzáAxõÃAhõÃAXõÃAHõÃA8õÃA(õÃAõÃAõÃ@ñë
+@Ñë
+@±ë
+@ë
+@c×
+@#×
+?Ç®?\)B~G®BzG®BvG®BrG®BnG®BjG®BfG®BbG®B^G®BZG®BVG®BRG®BNG®BJG®BFG®BBG®B>G®B:G®B6G®B2G®B.G®B*G®B&G®B"G®B
+G®BG®BG®BG®BG®B
+G®BG®BG®Aü\Aô\Aì\Aä\AÜ\AÔ\AÌ\AÄ\AŒ\AŽ\A¬\A€\A\A\A\A\Ay
+žAi
+žAY
+žAI
+žA9
+žA)
+žA
+žA	
+ž@ò=q@Ò=q@²=q@=q@dzá@$zá?ÈõÂ?ë
+B~QìBzQìBvQìBrQìBnQìBjQìBfQìBbQìB^QìBZQìBVQìBRQìBNQìBJQìBFQìBBQìB>QìB:QìB6QìB2QìB.QìB*QìB&QìB"QìB
+QìBQìBQìBQìBQìB
+QìBQìBQìAü£×Aô£×Aì£×Aä£×AÜ£×AÔ£×AÌ£×AÄ£×AŒ£×AŽ£×A¬£×A€£×A£×A£×A£×A£×AyG®AiG®AYG®AIG®A9G®A)G®AG®A	G®@ò\@Ò\@²\@\@e
+ž@%
+ž?Ê=p?záB~\)Bz\)Bv\)Br\)Bn\)Bj\)Bf\)Bb\)B^\)BZ\)BV\)BR\)BN\)BJ\)BF\)BB\)B>\)B:\)B6\)B2\)B.\)B*\)B&\)B"\)B
+\)B\)B\)B\)B\)B
+\)B\)B\)AüžRAôžRAìžRAäžRAÜžRAÔžRAÌžRAÄžRAŒžRAŽžRA¬žRA€žRAžRAžRAžRAžRAyp€Aip€AYp€AIp€A9p€A)p€Ap€A	p€@òáH@ÒáH@²áH@áH@eÂ@%Â?Ë
+
+?
+=B~ffBzffBvffBrffBnffBjffBfffBbffB^ffBZffBVffBRffBNffBJffBFffBBffB>ffB:ffB6ffB2ffB.ffB*ffB&ffB"ffB
+ffBffBffBffBffB
+ffBffBffAüÌÍAôÌÍAìÌÍAäÌÍAÜÌÍAÔÌÍAÌÌÍAÄÌÍAŒÌÍAŽÌÍA¬ÌÍA€ÌÍAÌÍAÌÍAÌÍAÌÍAyAiAYAIA9A)AA	@ó33@Ó33@³33@33@fff@&ff?ÌÌÍ?B~p€Bzp€Bvp€Brp€Bnp€Bjp€Bfp€Bbp€B^p€BZp€BVp€BRp€BNp€BJp€BFp€BBp€B>p€B:p€B6p€B2p€B.p€B*p€B&p€B"p€B
+p€Bp€Bp€Bp€Bp€B
+p€Bp€Bp€AüáHAôáHAìáHAäáHAÜáHAÔáHAÌáHAÄáHAŒáHAŽáHA¬áHA€áHAáHAáHAáHAáHAyÂAiÂAYÂAIÂA9ÂA)ÂAÂA	Â@ó
+@Ó
+@³
+@
+@g
+>@'
+>?Î{?
+(öB~záBzzáBvzáBrzáBnzáBjzáBfzáBbzáB^záBZzáBVzáBRzáBNzáBJzáBFzáBBzáB>záB:záB6záB2záB.záB*záB&záB"záB
+záBzáBzáBzáBzáB
+záBzáBzáAüõÃAôõÃAìõÃAäõÃAÜõÃAÔõÃAÌõÃAÄõÃAŒõÃAŽõÃA¬õÃA€õÃAõÃAõÃAõÃAõÃAyë
+Aië
+AYë
+AIë
+A9ë
+A)ë
+Aë
+A	ë
+@ó×
+@Ó×
+@³×
+@×
+@g®@'®?Ï\)?
+žRB~
+Bz
+Bv
+Br
+Bn
+Bj
+Bf
+Bb
+B^
+BZ
+BV
+BR
+BN
+BJ
+BF
+BB
+B>
+B:
+B6
+B2
+B.
+B*
+B&
+B"
+B
+
+B
+B
+B
+B
+B
+
+B
+B
+Aý
+=Aõ
+=Aí
+=Aå
+=AÝ
+=AÕ
+=AÍ
+=AÅ
+=Aœ
+=Aµ
+=A­
+=A¥
+=A
+=A
+=A
+=A
+
+=Az{Aj{AZ{AJ{A:{A*{A{A
+{@ô(ö@Ô(ö@Ž(ö@(ö@hQì@(Qì?Ð£×?!G®                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|
+=Bx  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|{Bx
+=Bx
+=Bt  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|(öBx
+žBx{Bt
+=Bp
+=Bl  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|33Bx(öBx(öBt
+žBp{Bl
+=Bl
+=Bh  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|=qBx=qBx33Bt(öBp(öBl
+žBl{Bh
+=Bd
+=Bd  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|QìBxG®Bx=qBt=qBp33Bl(öBl(öBh
+žBd{Bd
+=B`
+=B\  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|\)Bx\)BxQìBtG®Bp=qBl=qBl33Bh(öBd(öBd
+žB`{B\
+=BX
+=BX  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|p€BxffBx\)Bt\)BpQìBlG®Bl=qBh=qBd33Bd(öB`(öB\
+žBX{BX
+=BT
+=BP  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|záBxp€Bxp€BtffBp\)Bl\)BlQìBhG®Bd=qBd=qB`33B\(öBX(öBX
+žBT{BP
+=BP
+=BL  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|
+Bx
+BxzáBtp€Bpp€BlffBl\)Bh\)BdQìBdG®B`=qB\=qBX33BX(öBT(öBP
+žBP{BL
+=BH
+=BH  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|Bx\Bx
+Bt
+BpzáBlp€Blp€BhffBd\)Bd\)B`QìB\G®BX=qBX=qBT33BP(öBP(öBL
+žBH{BH
+=BD
+=B@  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|£×Bx£×BxBt\Bp
+Bl
+BlzáBhp€Bdp€BdffB`\)B\\)BXQìBXG®BT=qBP=qBP33BL(öBH(öBH
+žBD{B@
+=B<
+=B<  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|žRBx®Bx£×Bt£×BpBl\Bl
+Bh
+BdzáBdp€B`p€B\ffBX\)BX\)BTQìBPG®BP=qBL=qBH33BH(öBD(öB@
+žB<{B<
+=B8
+=B4  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÂBxžRBxžRBt®Bp£×Bl£×BlBh\Bd
+Bd
+B`záB\p€BXp€BXffBT\)BP\)BPQìBLG®BH=qBH=qBD33B@(öB<(öB<
+žB8{B4
+=B4
+=B0  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|×
+BxÌÍBxÂBtžRBpžRBl®Bl£×Bh£×BdBd\B`
+B\
+BXzáBXp€BTp€BPffBP\)BL\)BHQìBHG®BD=qB@=qB<33B<(öB8(öB4
+žB4{B0
+=B,
+=B(  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|áHBx×
+Bx×
+BtÌÍBpÂBlžRBlžRBh®Bd£×Bd£×B`B\\BX
+BX
+BTzáBPp€BPp€BLffBH\)BH\)BDQìB@G®B<=qB<=qB833B4(öB4(öB0
+žB,{B(
+=B(
+=B$  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ë
+Bxë
+BxáHBt×
+Bp×
+BlÌÍBlÂBhžRBdžRBd®B`£×B\£×BXBX\BT
+BP
+BPzáBLp€BHp€BHffBD\)B@\)B<QìB<G®B8=qB4=qB433B0(öB,(öB(
+žB({B$
+=B 
+=B   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}  BxõÃBxë
+Btë
+BpáHBl×
+Bl×
+BhÌÍBdÂBdžRB`žRB\®BX£×BX£×BTBP\BP
+BL
+BHzáBHp€BDp€B@ffB<\)B<\)B8QìB4G®B4=qB0=qB,33B((öB((öB$
+žB {B 
+=B
+
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+=By  By  BtõÃBpë
+Blë
+BláHBh×
+Bd×
+BdÌÍB`ÂB\žRBXžRBX®BT£×BP£×BPBL\BH
+BH
+BDzáB@p€B<p€B<ffB8\)B4\)B4QìB0G®B,=qB(=qB(33B$(öB (öB 
+žB
+{B
+=B
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+žBy{By
+=Bu  Bq  BlõÃBlë
+Bhë
+BdáHBd×
+B`×
+B\ÌÍBXÂBXžRBTžRBP®BP£×BL£×BHBH\BD
+B@
+B<záB<p€B8p€B4ffB4\)B0\)B,QìB(G®B(=qB$=qB 33B (öB
+(öB
+žB{B
+=B
+=B
+  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}(öBy
+žBy
+žBu{Bq
+=Bm  Bm  BhõÃBdë
+Bdë
+B`áHB\×
+BX×
+BXÌÍBTÂBPžRBPžRBL®BH£×BH£×BDB@\B<
+B<
+B8záB4p€B4p€B0ffB,\)B(\)B(QìB$G®B =qB =qB
+33B(öB(öB
+žB{B
+
+=B
+
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}33By33By(öBu
+žBq
+žBm{Bm
+=Bi  Be  BdõÃB`ë
+B\ë
+BXáHBX×
+BT×
+BPÌÍBPÂBLžRBHžRBH®BD£×B@£×B<B<\B8
+B4
+B4záB0p€B,p€B(ffB(\)B$\)B QìB G®B
+=qB=qB33B(öB(öB
+
+žB
+{B
+=B
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}G®By=qBy33Bu33Bq(öBm
+žBm
+žBi{Be
+=Be  Ba  B\õÃBXë
+BXë
+BTáHBP×
+BP×
+BLÌÍBHÂBHžRBDžRB@®B<£×B<£×B8B4\B4
+B0
+B,záB(p€B(p€B$ffB \)B \)B
+QìBG®B=qB=qB33B
+(öB
+(öB
+žB{B
+=B 
+=Aø  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}QìByQìByG®Bu=qBq33Bm33Bm(öBi
+žBe
+žBe{Ba
+=B]  BY  BXõÃBTë
+BPë
+BPáHBL×
+BH×
+BHÌÍBDÂB@žRB<žRB<®B8£×B4£×B4B0\B,
+B(
+B(záB$p€B p€B ffB
+\)B\)BQìBG®B=qB
+=qB
+33B(öB(öB
+žB {Aø{Að{Að  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ffBy\)ByQìBuQìBqG®Bm=qBm33Bi33Be(öBe
+žBa
+žB]{BY
+=BY  BU  BPõÃBPë
+BLë
+BHáHBH×
+BD×
+B@ÌÍB<ÂB<žRB8žRB4®B4£×B0£×B,B(\B(
+B$
+B záB p€B
+p€BffB\)B\)BQìB
+G®B
+=qB=qB33B(öB (öAø=qAð(öAð{Aè{Aà  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}p€ByffByffBu\)BqQìBmQìBmG®Bi=qBe33Be33Ba(öB]
+žBY
+žBY{BU
+=BQ  BQ  BLõÃBHë
+BHë
+BDáHB@×
+B<×
+B<ÌÍB8ÂB4žRB4žRB0®B,£×B(£×B(B$\B 
+B 
+B
+záBp€Bp€BffB\)B
+\)B
+QìBG®B=qB=qB 33AøQìAðQìAð=qAè(öAà{Aà{AØ  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+ByzáByp€BuffBqffBm\)BmQìBiQìBeG®Be=qBa33B]33BY(öBY
+žBU
+žBQ{BQ
+=BM  BI  BHõÃBDë
+B@ë
+B<áHB<×
+B8×
+B4ÌÍB4ÂB0žRB,žRB(®B(£×B$£×B B \B
+
+B
+BzáBp€Bp€B
+ffB
+\)B\)BQìBG®B =qAøzáAðffAðQìAèQìAà=qAà(öAØ{AÐ{AÈ  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}\By
+By
+BuzáBqp€BmffBmffBi\)BeQìBeQìBaG®B]=qBY33BY33BU(öBQ
+žBQ
+žBM{BI
+=BI  BE  B@õÃB<ë
+B<ë
+B8áHB4×
+B4×
+B0ÌÍB,ÂB(žRB(žRB$®B £×B £×B
+B\B
+B
+BzáB
+p€B
+p€BffB\)B\)B QìAø\AðzáAðzáAèffAàQìAàQìAØ=qAÐ(öAÈ{AÈ{AÀ  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ByBy\Bu
+Bq
+BmzáBmp€BiffBeffBe\)BaQìB]QìBYG®BY=qBU33BQ33BQ(öBM
+žBI
+žBI{BE
+=BA  B=  B<õÃB8ë
+B4ë
+B4áHB0×
+B,×
+B(ÌÍB(ÂB$žRB žRB ®B
+£×B£×BB\B
+B
+
+B
+záBp€Bp€BffB \)AøžRAð£×Að\AèzáAàzáAàffAØQìAÐQìAÈ=qAÈ(öAÀ{Až{Až  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}®By£×ByBuBq\Bm
+Bm
+BizáBep€BeffBaffB]\)BYQìBYQìBUG®BQ=qBQ33BM33BI(öBI
+žBE
+žBA{B=
+=B=  B9  B4õÃB4ë
+B0ë
+B,áHB(×
+B(×
+B$ÌÍB ÂB žRB
+žRB®B£×B£×BB
+\B
+
+B
+BzáBp€B p€AøÌÍAðžRAðžRAè£×Aà\AàzáAØzáAÐffAÈQìAÈQìAÀ=qAž(öAž{A°{Aš  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}žRBy®By®Bu£×BqBmBm\Bi
+Be
+BezáBap€B]ffBYffBY\)BUQìBQQìBQG®BM=qBI33BI33BE(öBA
+žB=
+žB={B9
+=B5  B5  B0õÃB,ë
+B(ë
+B(áHB$×
+B ×
+B ÌÍB
+ÂBžRBžRB®B£×B
+£×B
+B\B
+B
+B záAøáHAðáHAðÌÍAèžRAàžRAà£×AØ\AÐzáAÈzáAÈffAÀQìAžQìAž=qA°(öAš{Aš{A   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÌÍByÂByžRBu®Bq®Bm£×BmBiBe\Be
+Ba
+B]záBYp€BYffBUffBQ\)BQQìBMQìBIG®BI=qBE33BA33B=(öB=
+žB9
+žB5{B5
+=B1  B-  B(õÃB(ë
+B$ë
+B áHB ×
+B
+×
+BÌÍBÂBžRBžRB
+®B
+£×B£×BB\B 
+Aù
+=AðõÃAðáHAèáHAàÌÍAàžRAØžRAÐ£×AÈ\AÈzáAÀzáAžffAžQìA°QìAš=qAš(öA {A{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}×
+ByÌÍByÌÍBuÂBqžRBm®Bm®Bi£×BeBeBa\B]
+BY
+BYzáBUp€BQffBQffBM\)BIQìBIQìBEG®BA=qB=33B=33B9(öB5
+žB5
+žB1{B-
+=B)  B)  B$õÃB ë
+B ë
+B
+áHB×
+B×
+BÌÍBÂB
+žRB
+žRB®B£×B£×B Aù
+žAñ
+=Añ
+=AèõÃAàáHAàáHAØÌÍAÐžRAÈžRAÈ£×AÀ\AžzáAžzáA°ffAšQìAšQìA =qA(öA{A{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}áHByáHBy×
+BuÌÍBqÌÍBmÂBmžRBi®Be®Be£×BaB]BY\BY
+BU
+BQzáBQp€BMffBIffBI\)BEQìBAQìB=G®B==qB933B533B5(öB1
+žB-
+žB){B)
+=B%  B!  B õÃB
+ë
+Bë
+BáHB×
+B×
+B
+ÌÍB
+ÂBžRBžRB®B £×AùG®Añ33Añ
+žAé
+=Aá
+=AàõÃAØáHAÐáHAÈÌÍAÈžRAÀžRAž£×Až\A°záAšzáAšffA QìAQìA=qA(öA{A{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}õÃByë
+ByáHBuáHBq×
+BmÌÍBmÌÍBiÂBežRBe®Ba®B]£×BYBYBU\BQ
+BQ
+BMzáBIp€BIffBEffBA\)B=QìB=QìB9G®B5=qB533B133B-(öB)
+žB)
+žB%{B!
+=B!  B
+  BõÃBë
+Bë
+BáHB
+×
+B
+×
+BÌÍBÂBžRB žRAù\)AñG®AñG®Aé33Aá
+žAá
+=AÙ
+=AÐõÃAÈáHAÈáHAÀÌÍAžžRAžžRA°£×Aš\AšzáA záAffAQìAQìA=qA(öA{Ap(öA`  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~  Bz  ByõÃBuë
+BqáHBmáHBm×
+BiÌÍBeÌÍBeÂBažRB]®BY®BY£×BUBQBQ\BM
+BI
+BIzáBEp€BAffB=ffB=\)B9QìB5QìB5G®B1=qB-33B)33B)(öB%
+žB!
+žB!{B
+
+=B  B  BõÃBë
+B
+ë
+B
+áHB×
+B×
+BÌÍB ÂAùp€Añp€Añ\)AéG®AáG®Aá33AÙ
+žAÑ
+=AÉ
+=AÈõÃAÀáHAžáHAžÌÍA°žRAšžRAš£×A \AzáAzáAffAQìAQìA=qApQìA`(öAP(öAP  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~{Bz
+=Bz  Bv  BqõÃBmë
+BmáHBiáHBe×
+BeÌÍBaÌÍB]ÂBYžRBY®BU®BQ£×BQBMBI\BI
+BE
+BAzáB=p€B=ffB9ffB5\)B5QìB1QìB-G®B)=qB)33B%33B!(öB!
+žB
+
+žB{B
+=B  B  B
+õÃB
+ë
+Bë
+BáHB×
+B ×
+AùAñ
+Añp€Aép€Aá\)AáG®AÙG®AÑ33AÉ
+žAÉ
+=AÁ
+=AžõÃAžáHA°áHAšÌÍAšžRA žRA£×A\AzáAzáAffAQìAp£×A`záAPQìAP(öA@(öA0  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~
+žBz{Bz{Bv
+=Br  Bn  BmõÃBië
+BeáHBeáHBa×
+B]ÌÍBYÌÍBYÂBUžRBQ®BQ®BM£×BIBIBE\BA
+B=
+B=záB9p€B5ffB5ffB1\)B-QìB)QìB)G®B%=qB!33B!33B
+(öB
+žB
+žB{B
+=B
+  B
+  BõÃBë
+Bë
+B áHAù®Añ®AñAé
+Aáp€Aáp€AÙ\)AÑG®AÉG®AÉ33AÁ
+žA¹
+=A¹
+=A°õÃAšáHAšáHA ÌÍAžRAžRA£×A\AzáAzáApÌÍA`£×AP£×APzáA@QìA0(öA0(öA   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~(öBz(öBz
+žBv{Br{Bn
+=Bn  Bj  BeõÃBeë
+BaáHB]áHBY×
+BYÌÍBUÌÍBQÂBQžRBM®BI®BI£×BEBAB=\B=
+B9
+B5záB5p€B1ffB-ffB)\)B)QìB%QìB!G®B!=qB
+33B33B(öB
+žB
+žB
+{B
+=B	  B  BõÃB ë
+Aù×
+AñÂAñ®Aé®AáAá
+AÙp€AÑp€AÉ\)AÉG®AÁG®A¹33A¹
+žA±
+=A©
+=AšõÃA áHAáHAÌÍAžRAžRA£×A\ApõÃA`õÃAPÌÍAP£×A@£×A0záA0QìA (öA(öA  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~=qBz33Bz(öBv(öBr
+žBn{Bn{Bj
+=Bf  Bf  BaõÃB]ë
+BYáHBYáHBU×
+BQÌÍBQÌÍBMÂBIžRBI®BE®BA£×B=B=B9\B5
+B5
+B1záB-p€B)ffB)ffB%\)B!QìB!QìB
+G®B=qB33B33B(öB
+
+žB
+
+žB	{B
+=B  B  Aùë
+Añ×
+Añ×
+AéÂAá®Aá®AÙAÑ
+AÉp€AÉp€AÁ\)A¹G®A¹G®A±33A©
+žA©
+=A¡
+=AõÃAáHAáHAÌÍAžRAžRAqG®Aa
+žAPõÃAPõÃA@ÌÍA0£×A0£×A záAQìA(öA (ö@à  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~G®BzG®Bz=qBv33Br(öBn(öBn
+žBj{Bf{Bf
+=Bb  B^  BYõÃBYë
+BUáHBQáHBQ×
+BMÌÍBIÌÍBIÂBEžRBA®B=®B=£×B9B5B5\B1
+B-
+B)záB)p€B%ffB!ffB!\)B
+QìBQìBG®B=qB33B
+33B
+(öB	
+žB
+žB{B
+=Aú  Aò  Añë
+Aé×
+Aá×
+AáÂAÙ®AÑ®AÉAÉ
+AÁp€A¹p€A¹\)A±G®A©G®A©33A¡
+žA
+=A
+=AõÃAáHAáHAÌÍAqp€Aap€AQG®AQ
+žA@õÃA0õÃA0ÌÍA £×A£×AzáA Qì@àQì@ÀQì@À  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~\)BzQìBzG®BvG®Br=qBn33Bn(öBj(öBf
+žBf{Bb{B^
+=BZ  BZ  BUõÃBQë
+BQáHBMáHBI×
+BIÌÍBEÌÍBAÂB=žRB=®B9®B5£×B5B1B-\B)
+B)
+B%záB!p€B!ffB
+ffB\)BQìBQìBG®B
+=qB
+33B	33B(öB
+žB
+žAú(öAò{Aò  Aê  Aáë
+Aá×
+AÙ×
+AÑÂAÉ®AÉ®AÁA¹
+A¹p€A±p€A©\)A©G®A¡G®A33A
+žA
+=A
+=AõÃAáHAqÂAaAQp€AQp€AAG®A1
+žA0õÃA õÃAÌÍA£×A £×@àõÃ@À£×@ÀQì@ Qì@  ¿  ¿  ¿  ¿  ¿  ¿  B~ffBz\)Bz\)BvQìBrG®BnG®Bn=qBj33Bf(öBf(öBb
+žB^{BZ{BZ
+=BV  BR  BQõÃBMë
+BIáHBIáHBE×
+BAÌÍB=ÌÍB=ÂB9žRB5®B5®B1£×B-B)B)\B%
+B!
+B!záB
+p€BffBffB\)BQìB
+QìB
+G®B	=qB33B33B(öAú=qAò=qAò(öAê{Aâ  Aâ  AÙë
+AÑ×
+AÉ×
+AÉÂAÁ®A¹®A¹A±
+A©p€A©p€A¡\)AG®AG®A33A
+žA
+=A
+=Aqë
+AaÂAQÂAQAAp€A1p€A1G®A!
+žAõÃAõÃA ÌÍ@áG®@ÁG®@ÀõÃ@ £×@Qì@Qì@@  ¿  ¿  ¿  ¿  B~záBzp€BzffBv\)Br\)BnQìBnG®BjG®Bf=qBf33Bb(öB^(öBZ
+žBZ{BV{BR
+=BR  BN  BIõÃBIë
+BEáHBAáHB=×
+B=ÌÍB9ÌÍB5ÂB5žRB1®B-®B)£×B)B%B!\B!
+B
+
+BzáBp€BffBffB
+\)B
+QìB	QìBG®B=qB33AúffAòQìAò=qAê=qAâ(öAâ{AÚ  AÒ  AÉë
+AÉ×
+AÁ×
+A¹ÂA¹®A±®A©A©
+A¡p€Ap€A\)AG®AG®A33A
+žAr{Ab{AQë
+AQÂAAÂA1A1p€A!p€AG®A
+žA õÃ@áë
+@Á@ÁG®@¡G®@õÃ@£×@@£×@ £×?  ¿  ¿  B~
+BzzáBzzáBvp€BrffBn\)Bn\)BjQìBfG®BfG®Bb=qB^33BZ(öBZ(öBV
+žBR{BR{BN
+=BJ  BJ  BEõÃBAë
+B=áHB=áHB9×
+B5ÌÍB5ÌÍB1ÂB-žRB)®B)®B%£×B!B!B
+\B
+B
+BzáBp€B
+ffB
+ffB	\)BQìBQìBG®AúzáAòffAòffAêQìAâ=qAâ=qAÚ(öAÒ{AÊ  AÊ  AÁë
+A¹×
+A¹×
+A±ÂA©®A©®A¡A
+Ap€Ap€A\)AG®AG®ArffAb=qAR{AR{AAë
+A1ÂA1ÂA!Ap€Ap€AG®@â=q@Áë
+@Áë
+@¡@G®@G®@Aë
+@G®?G®?G®    ¿  ¿  Bz
+BvzáBrzáBnp€BnffBj\)Bf\)BfQìBbG®B^G®BZ=qBZ33BV(öBR(öBR
+žBN{BJ{BJ
+=BF  BB  B=õÃB=ë
+B9áHB5áHB5×
+B1ÌÍB-ÌÍB)ÂB)žRB%®B!®B!£×B
+BB\B
+B
+B
+záB
+p€B	ffBffB\)BQìAú£×Aò\AòzáAêffAâffAâQìAÚ=qAÒ=qAÊ(öAÊ{AÂ  Aº  A¹ë
+A±×
+A©×
+A©ÂA¡®A®AA
+Ap€Ap€A\)Ar\Ab\ARffAR=qAB{A2{A1ë
+A!ÂAÂAAp€@âáH@Â\@Â=q@¡ë
+@ë
+@@B\@\?×
+?\<#×
+¿  ¿  ¿  ¿  Br
+BnzáBnzáBjp€BfffBf\)Bb\)B^QìBZG®BZG®BV=qBR33BR(öBN(öBJ
+žBJ{BF{BB
+=B>  B>  B9õÃB5ë
+B5áHB1áHB-×
+B)ÌÍB)ÌÍB%ÂB!žRB!®B
+®B£×BBB\B
+
+B
+
+B	záBp€BffBffAúžRAò£×Aò£×Aê\AâzáAâffAÚffAÒQìAÊ=qAÊ=qAÂ(öAº{Aº  A²  A©ë
+A©×
+A¡×
+AÂA®A®AA
+Ap€AráHAbžRAR\AR\ABffA2=qA2{A"{Aë
+AÂAÂ@ã33@ÂáH@ÂáH@¢\@=q@ë
+@C×
+@33?
+
+ž?
+
+ž<õÂ¿  ¿  ¿  ¿  ¿  ¿  Bn
+BjzáBfzáBfp€BbffB^\)BZ\)BZQìBVG®BRG®BR=qBN33BJ(öBJ(öBF
+žBB{B>{B>
+=B:  B6  B5õÃB1ë
+B-áHB)áHB)×
+B%ÌÍB!ÌÍB!ÂB
+žRB®B®B£×BB
+B
+\B	
+B
+BzáBp€AúÌÍAòÌÍAòžRAê£×Aâ£×Aâ\AÚzáAÒffAÊffAÊQìAÂ=qAº=qAº(öA²{Aª  Aª  A¡ë
+A×
+A×
+AÂA®A®AAs
+=AbáHARáHARžRAB\A2\A2ffA"=qA{A{Aë
+@ã
+@Ã
+@Ã33@¢áH@áH@\@Dzá@×
+?®?ff=#×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bf
+BfzáBbzáB^p€BZffBZ\)BV\)BRQìBRG®BNG®BJ=qBJ33BF(öBB(öB>
+žB>{B:{B6
+=B6  B2  B-õÃB)ë
+B)áHB%áHB!×
+B!ÌÍB
+ÌÍBÂBžRB®B®B
+£×B
+B	B\B
+B
+AúõÃAòáHAòÌÍAêÌÍAâžRAâ£×AÚ£×AÒ\AÊzáAÊffAÂffAºQìAº=qA²=qAª(öAª{A¢  A  Aë
+A×
+A×
+AÂA®As\)Ac33AS
+=ARáHABáHA2žRA2\A"\AffA=qA{@ä(ö@Ã×
+@Ã
+@£
+@33@áH@EÂ@
+ž?õÃ?®=uÂ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bb
+B^záBZzáBZp€BVffBR\)BR\)BNQìBJG®BJG®BF=qBB33B>(öB>(öB:
+žB6{B6{B2
+=B.  B*  B)õÃB%ë
+B!áHB!áHB
+×
+BÌÍBÌÍBÂBžRB
+®B
+®B	£×BBB\Aû
+=Aó
+=AòõÃAêáHAâÌÍAâÌÍAÚžRAÒ£×AÊ£×AÊ\AÂzáAºffAºffA²QìAª=qAª=qA¢(öA{A  A  Aë
+A×
+A×
+As
+Ac\)AS\)AS33AC
+=A2áHA2áHA"žRA\A\Aff@äzá@Ä(ö@Ä(ö@£×
+@
+@
+@Fff@Â?
+?=q=\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BZ
+BZzáBVzáBRp€BRffBN\)BJ\)BJQìBFG®BBG®B>=qB>33B:(öB6(öB6
+žB2{B.{B*
+=B*  B&  B!õÃB!ë
+B
+áHBáHB×
+BÌÍBÌÍB
+ÂB
+žRB	®B®B£×BAû33Aó
+žAó
+=Aë
+=AâõÃAâáHAÚÌÍAÒÌÍAÊžRAÊ£×AÂ£×Aº\AºzáA²ffAªffAªQìA¢=qA=qA(öA{A  A  Aë
+As®Ac®AS
+AS\)AC\)A333A3
+=A"áHAáHAžRA\@å
+ž@ÄÌÍ@Äzá@€(ö@(ö@×
+@G
+=@
+=?ÌÍ?
+=žQì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BV
+BRzáBRzáBNp€BJffBJ\)BF\)BBQìB>G®B>G®B:=qB633B6(öB2(öB.
+žB*{B*{B&
+=B"  B"  B
+õÃBë
+BáHBáHB×
+B
+ÌÍB
+ÌÍB	ÂBžRB®B®AûG®Aó33Aó33Aë
+žAã
+=Aã
+=AÚõÃAÒáHAÊÌÍAÊÌÍAÂžRAº£×Aº£×A²\AªzáAªffA¢ffAQìA=qA=qA(öA{A  At  Ac×
+AS®AS®AC
+A3\)A3\)A#33A
+=AáHAáH@åp€@Å
+ž@Å
+ž@€ÌÍ@zá@(ö@HQì@®?{?{=ÌÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BR
+BNzáBJzáBJp€BFffBB\)B>\)B>QìB:G®B6G®B6=qB233B.(öB*(öB*
+žB&{B"{B"
+=B
+  B  BõÃBë
+BáHB
+áHB
+×
+B	ÌÍBÌÍBÂBžRAû\)Aó\)AóG®Aë33Aã33Aã
+žAÛ
+=AÓ
+=AÊõÃAÊáHAÂÌÍAºÌÍAºžRA²£×Aª£×Aª\A¢záAffAffAQìA=qA=qA(öAt(öAd  AT  AS×
+AC®A3®A3
+A#\)A\)A33A
+=@åÂ@ÅÂ@Åp€@¥
+ž@
+
+ž@ÌÍ@HõÃ@Qì?£×?\)=áG®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BJ
+BJzáBFzáBBp€B>ffB>\)B:\)B6QìB6G®B2G®B.=qB*33B*(öB&(öB"
+žB"{B
+{B
+=B  B  BõÃB
+ë
+B
+áHB	áHB×
+BÌÍBÌÍAû
+Aóp€Aó\)Aë\)AãG®Aã33AÛ33AÓ
+žAË
+=AË
+=AÂõÃAºáHAºÌÍA²ÌÍAªžRAª£×A¢£×A\AzáAffAffAQìA=qAtzáAdQìAT(öAT  AD  A3×
+A3®A#®A
+A\)A\)@æff@Æ{@ÅÂ@¥Â@
+p€@
+
+ž@J=q@	?ë
+?£×>
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BF
+BBzáB>záB>p€B:ffB6\)B6\)B2QìB.G®B*G®B*=qB&33B"(öB"(öB
+
+žB{B{B
+=B  B  B
+õÃB	ë
+BáHBáHB×
+AûAóAó
+Aëp€Aã\)Aã\)AÛG®AÓ33AË33AË
+žAÃ
+=A»
+=AºõÃA²áHAªÌÍAªÌÍA¢žRA£×A£×A\AzáAffAffAt£×AdzáATzáATQìAD(öA4  A4  A#×
+A®A®A
+@æžR@ÆžR@Æff@Š{@
+Â@
+Â@JáH@
+=q?zá?33>\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B>
+B>záB:záB6p€B6ffB2\)B.\)B*QìB*G®B&G®B"=qB"33B
+(öB(öB
+žB{B{B
+=B  B
+  BõÃBë
+BáHAûÂAó®AóAëAã
+Aãp€AÛ\)AÓ\)AËG®AË33AÃ33A»
+žA»
+=A³
+=AªõÃAªáHA¢ÌÍAÌÍAžRA£×A£×A\AzáAtÌÍAdÌÍAT£×ATzáADzáA4QìA4(öA$  A  A×
+A®@ç\)@Ç
+=@ÆžR@ŠžR@ff@{@K
+@
+
+?Â?zá>#×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B:
+B6záB6záB2p€B.ffB*\)B*\)B&QìB"G®B"G®B
+=qB33B(öB(öB
+žB{B{B
+
+=B  B  BõÃAû×
+AóÂAóÂAë®AãAãAÛ
+AÓp€AË\)AË\)AÃG®A»33A»33A³
+žA«
+=A«
+=A¢õÃAáHAÌÍAÌÍAžRA£×A£×Au
+žAdõÃATÌÍATÌÍAD£×A4záA4záA$QìA(öA  A  @ç®@Ç\)@Ç\)@§
+=@žR@žR@LÌÍ@
+(ö?
+>?
+>>.{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B6
+B2záB.záB*p€B*ffB&\)B"\)B"QìB
+G®BG®B=qB33B(öB(öB
+žB
+{B{B
+=B  Aü  Aóë
+Aó×
+AëÂAãÂAã®AÛAÓAË
+AËp€AÃ\)A»\)A»G®A³33A«33A«
+žA£
+=A
+=AõÃAáHAÌÍAÌÍAžRAuG®AeG®AU
+žATõÃADÌÍA4ÌÍA4£×A$záAzáAQìA(ö@è  @È  @Ç®@§\)@\)@
+=@Mp€@
+p€??Qì>8Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B.
+B*záB*záB&p€B"ffB"\)B
+\)BQìBG®BG®B=qB33B(öB
+(öB
+žB{B{Aü{Aô  Aô  Aëë
+Aã×
+AãÂAÛÂAÓ®AËAËAÃ
+A»p€A»\)A³\)A«G®A«33A£33A
+žA
+=A
+=AõÃAáHAÌÍAuAep€AUG®AUG®AE
+žA4õÃA4ÌÍA$ÌÍA£×AzáAzá@è£×@ÈQì@È  @š  @®@\)@NžR@{?áH?áH>LÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B*
+B&záB"záB"p€B
+ffB\)B\)BQìBG®BG®B=qB
+33B(öB(öB
+žAü(öAô(öAô{Aì  Aä  Aãë
+AÛ×
+AÓÂAËÂAË®AÃA»A»
+A³p€A«\)A«\)A£G®A33A33A
+žA
+=A
+=AõÃAuÂAeAUAUp€AEG®A5G®A5
+žA$õÃAÌÍAÌÍA£×@èõÃ@ÈõÃ@È£×@šQì@  @  @O\)@žR?p€?(ö>W
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B"
+B"záB
+záBp€BffB\)B\)BQìBG®B
+G®B=qB33B(öAüQìAô=qAô(öAì(öAä{Aä  AÜ  AÓë
+AË×
+AËÂAÃÂA»®A»A³A«
+A«p€A£\)A\)AG®A33A33A
+žA
+=Av{Aeë
+AUÂAUAEA5p€A5G®A%G®A
+žAõÃAÌÍ@é@ÉG®@ÈõÃ@šõÃ@£×@Qì@P  @  ?žR?p€>k
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+
+BzáBzáBp€BffB\)B\)B
+QìBG®BG®B=qAüffAôQìAôQìAì=qAä(öAä(öAÜ{AÔ  AÌ  AËë
+AÃ×
+A»ÂA»ÂA³®A«A«A£
+Ap€A\)A\)AG®A33A33Av=qAf{AV{AUë
+AEÂA5A5A%p€AG®AG®A
+ž@éë
+@É@É@©G®@õÃ@õÃ@QG®@£×?   ?   >uÂ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáBzáBp€BffB
+\)B\)BQìBG®Aü\AôzáAôffAìQìAäQìAä=qAÜ(öAÔ(öAÌ{AÌ  AÄ  A»ë
+A»×
+A³ÂA«ÂA«®A£AA
+Ap€A\)A\)AG®AvffAfffAV=qAV{AF{A5ë
+A5ÂA%AAp€AG®@ê\@Ê=q@Éë
+@©@@G®@Që
+@ë
+?¢\?¡G®>  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáBzáB
+p€BffB\)B\)Aü£×Aô\Aô\AìzáAäffAäQìAÜQìAÔ=qAÌ(öAÌ(öAÄ{AŒ  AŒ  A³ë
+A«×
+A«ÂA£ÂA®AAA
+Ap€A\)AvžRAf\AVffAVffAF=qA6{A6{A%ë
+AÂAA@êáH@Ê\@Ê\@ª=q@ë
+@@S33@\?£×
+?£×
+>=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+B
+záBzáBp€BffAüžRAôžRAô£×Aì\Aä\AäzáAÜffAÔQìAÌQìAÌ=qAÄ(öAŒ(öAŒ{AŽ  A¬  A«ë
+A£×
+AÂAÂA®AAA
+AváHAfžRAVžRAV\AFffA6ffA6=qA&{A{Aë
+AÂ@ë33@Ë33@ÊáH@ª\@\@=q@S×
+@33?Šff?¥
+ž>\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáBzáAüáHAôÌÍAôžRAìžRAä£×Aä\AÜ\AÔzáAÌffAÌQìAÄQìAŒ=qAŒ(öAŽ(öA¬{A¬  A€  Aë
+A×
+AÂAÂA®AAw33Ag
+=AVáHAVžRAFžRA6\A6ffA&ffA=qA{A{@ë×
+@Ë
+@Ë33@«33@áH@\@U
+ž@zá?§®?Šff>¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+AüõÃAôõÃAôáHAìÌÍAäžRAäžRAÜ£×AÔ\AÌ\AÌzáAÄffAŒQìAŒQìAŽ=qA¬(öA¬(öA€{A  A  Aë
+A×
+AÂAÂAw\)Ag33AW33AW
+=AFáHA6žRA6žRA&\AffAffA=q@ì(ö@Ì(ö@Ë×
+@«
+@33@33@UÂ@
+ž?ª=q?šõÂ>žR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aõ
+=AôõÃAìõÃAäáHAäÌÍAÜžRAÔžRAÌ£×AÌ\AÄ\AŒzáAŒffAŽQìA¬QìA¬=qA€(öA(öA{A  A  Aë
+A×
+Aw
+Ag
+AW\)AW33AG33A7
+=A6áHA&žRAžRA\Aff@ìÌÍ@Ìzá@Ì(ö@¬(ö@×
+@
+@Vff@ff?«
+?ª=q>šõÃ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aí
+=AäõÃAäõÃAÜáHAÔÌÍAÌžRAÌžRAÄ£×AŒ\AŒ\AŽzáA¬ffA¬QìA€QìA=qA(öA(öA{A  A  Aw×
+Ag®AW
+AW
+AG\)A733A733A'
+=AáHAžRAžR@í
+ž@ÌÌÍ@ÌÌÍ@¬zá@(ö@(ö@W®@
+>?¬ÌÍ?¬ÌÍ>®{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aå
+=AÜõÃAÔõÃAÌáHAÌÌÍAÄžRAŒžRAŒ£×AŽ\A¬\A¬záA€ffAQìAQìA=qA(öA(öA{Ax  Ah  AW×
+AW®AG
+A7
+A7\)A'33A33A
+=AáH@íp€@Íp€@Í
+ž@¬ÌÍ@ÌÍ@zá@XQì@Qì?¯\)?®{>³33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÕ
+=AÌõÃAÌõÃAÄáHAŒÌÍAŒžRAŽžRA¬£×A¬\A€\AzáAffAQìAQìA=qA(öAxQìAh(öAX  AX  AG×
+A7®A7
+A'
+A\)A33A33@î{@ÍÂ@Íp€@­p€@
+ž@ÌÍ@Y@õÂ?°£×?°£×>œp€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÍ
+=AÄõÃAŒõÃAŒáHAŽÌÍA¬žRA¬žRA€£×A\A\AzáAffAQìAQìAxzáAhQìAXQìAX(öAH  A8  A7×
+A'®A
+A
+A\)@îff@Îff@Î{@­Â@p€@p€@Z=q@?³33?±ë
+>Â\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aœ
+=AŒõÃAŽõÃA¬áHA¬ÌÍA€žRAžRA£×A\A\AzáAffAx£×Ah£×AXzáAXQìAHQìA8(öA8  A(  A×
+A®A
+@ï
+=@ÎžR@Îff@®ff@{@Â@ZáH@áH?Žzá?³33>ÌÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aµ
+=A¬õÃA¬õÃA€áHAÌÍAžRAžRA£×A\A\AxõÃAhÌÍAX£×AX£×AHzáA8QìA8QìA((öA  A  A×
+@ï\)@Ï
+=@Ï
+=@®žR@ff@ff@\(ö@
+?µÂ?µÂ>Ñë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A­
+=A€õÃAõÃAáHAÌÍAžRAžRA£×Ay
+žAi
+žAXõÃAXÌÍAH£×A8£×A8záA(QìAQìA(öA  @ð  @Ï®@Ï\)@¯
+=@
+=@žR@\ÌÍ@
+ÌÍ?žQì?·
+>>×
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+=AõÃAõÃAáHAÌÍAžRAyp€AiG®AY
+žAY
+žAHõÃA8ÌÍA8£×A(£×AzáAQìAQì@ðQì@Ð  @Ð  @¯®@\)@
+=@^{@
+p€?¹?¹>áG®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+=AõÃAõÃAáHAyAip€AYp€AYG®AI
+žA9
+žA8õÃA(ÌÍA£×A£×Azá@ð£×@Ð£×@ÐQì@°  @  @®@^žR@
+{?Œ(ö?ºáH>æff¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+
+=AõÃAyë
+AiÂAYAYp€AIp€A9G®A9
+žA)
+žAõÃAÌÍA£×@ñG®@ÐõÂ@Ð£×@°£×@Qì@  @`  @\)?œp€?Œ(ö>ð£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Az{Aië
+AYë
+AYÂAIA9p€A9p€A)G®A
+žA
+žAõÃ@ñ@ÑG®@ÑG®@°õÂ@£×@£×@`£×@   ?À  ?ŸžR>õÂ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AZ{AYë
+AIë
+A9ÂA9A)p€Ap€AG®A	
+ž@ò=q@Ñë
+@Ñ@±G®@G®@õÂ@aG®@!G®?ÁG®?À  ?   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AJ{A9ë
+A9ë
+A)ÂAAp€A	p€@ò\@Ò=q@Ò=q@±ë
+@@G®@b\@!ë
+?Â\?Â\?\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A:{A)ë
+Aë
+AÂA	@òáH@ÒáH@Ò\@²=q@=q@ë
+@c33@"\?Å
+ž?Ã×
+?
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A{Aë
+A	ë
+@ó
+@Ó33@ÒáH@²áH@\@=q@dzá@#×
+?Æff?Å
+ž?
+=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+{@ó×
+@Ó×
+@Ó
+@³33@áH@áH@e
+ž@$zá?ÈõÂ?Ç®?
+ÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @Ô(ö@Ó×
+@³×
+@
+@33@eÂ@%Â?Ê=p?ÈõÂ?ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @Ž(ö@×
+@×
+@g
+>@&ff?Ë
+
+?Ë
+
+?zá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @(ö@g®@'®?Î{?ÌÌÍ?
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @(Qì?Ï\)?Ï\)?
+(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?Ð£×?
+žR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B~
+B~záB~p€B~ffB~\)B~QìB~G®B~=qB~33B~(öB~
+žB~{B~
+=B~  B}õÃB}ë
+B}áHB}×
+B}ÌÍB}ÂB}žRB}®B}£×B}B}\B}
+B}záB}p€B}ffB}\)B}QìB}G®B}=qB}33B}(öB}
+žB}{B}
+=B}  B|õÃB|ë
+B|áHB|×
+B|ÌÍB|ÂB|žRB|®B|£×B|B|\B|
+B|záB|p€B|ffB|\)B|QìB|G®B|=qB|33B|(öB|
+žB|{B|
+=B|  Bz
+BzzáBzp€BzffBz\)BzQìBzG®Bz=qBz33Bz(öBz
+žBz{Bz
+=Bz  ByõÃByë
+ByáHBy×
+ByÌÍByÂByžRBy®By£×ByBy\By
+ByzáByp€ByffBy\)ByQìByG®By=qBy33By(öBy
+žBy{By
+=By  BxõÃBxë
+BxáHBx×
+BxÌÍBxÂBxžRBx®Bx£×BxBx\Bx
+BxzáBxp€BxffBx\)BxQìBxG®Bx=qBx33Bx(öBx
+žBx{Bx
+=Bx  Bv
+BvzáBvp€BvffBv\)BvQìBvG®Bv=qBv33Bv(öBv
+žBv{Bv
+=Bv  BuõÃBuë
+BuáHBu×
+BuÌÍBuÂBužRBu®Bu£×BuBu\Bu
+BuzáBup€BuffBu\)BuQìBuG®Bu=qBu33Bu(öBu
+žBu{Bu
+=Bu  BtõÃBtë
+BtáHBt×
+BtÌÍBtÂBtžRBt®Bt£×BtBt\Bt
+BtzáBtp€BtffBt\)BtQìBtG®Bt=qBt33Bt(öBt
+žBt{Bt
+=Bt  Br
+BrzáBrp€BrffBr\)BrQìBrG®Br=qBr33Br(öBr
+žBr{Br
+=Br  BqõÃBqë
+BqáHBq×
+BqÌÍBqÂBqžRBq®Bq£×BqBq\Bq
+BqzáBqp€BqffBq\)BqQìBqG®Bq=qBq33Bq(öBq
+žBq{Bq
+=Bq  BpõÃBpë
+BpáHBp×
+BpÌÍBpÂBpžRBp®Bp£×BpBp\Bp
+BpzáBpp€BpffBp\)BpQìBpG®Bp=qBp33Bp(öBp
+žBp{Bp
+=Bp  Bn
+BnzáBnp€BnffBn\)BnQìBnG®Bn=qBn33Bn(öBn
+žBn{Bn
+=Bn  BmõÃBmë
+BmáHBm×
+BmÌÍBmÂBmžRBm®Bm£×BmBm\Bm
+BmzáBmp€BmffBm\)BmQìBmG®Bm=qBm33Bm(öBm
+žBm{Bm
+=Bm  BlõÃBlë
+BláHBl×
+BlÌÍBlÂBlžRBl®Bl£×BlBl\Bl
+BlzáBlp€BlffBl\)BlQìBlG®Bl=qBl33Bl(öBl
+žBl{Bl
+=Bl  Bj
+BjzáBjp€BjffBj\)BjQìBjG®Bj=qBj33Bj(öBj
+žBj{Bj
+=Bj  BiõÃBië
+BiáHBi×
+BiÌÍBiÂBižRBi®Bi£×BiBi\Bi
+BizáBip€BiffBi\)BiQìBiG®Bi=qBi33Bi(öBi
+žBi{Bi
+=Bi  BhõÃBhë
+BháHBh×
+BhÌÍBhÂBhžRBh®Bh£×BhBh\Bh
+BhzáBhp€BhffBh\)BhQìBhG®Bh=qBh33Bh(öBh
+žBh{Bh
+=Bh  Bf
+BfzáBfp€BfffBf\)BfQìBfG®Bf=qBf33Bf(öBf
+žBf{Bf
+=Bf  BeõÃBeë
+BeáHBe×
+BeÌÍBeÂBežRBe®Be£×BeBe\Be
+BezáBep€BeffBe\)BeQìBeG®Be=qBe33Be(öBe
+žBe{Be
+=Be  BdõÃBdë
+BdáHBd×
+BdÌÍBdÂBdžRBd®Bd£×BdBd\Bd
+BdzáBdp€BdffBd\)BdQìBdG®Bd=qBd33Bd(öBd
+žBd{Bd
+=Bd  Bb
+BbzáBbp€BbffBb\)BbQìBbG®Bb=qBb33Bb(öBb
+žBb{Bb
+=Bb  BaõÃBaë
+BaáHBa×
+BaÌÍBaÂBažRBa®Ba£×BaBa\Ba
+BazáBap€BaffBa\)BaQìBaG®Ba=qBa33Ba(öBa
+žBa{Ba
+=Ba  B`õÃB`ë
+B`áHB`×
+B`ÌÍB`ÂB`žRB`®B`£×B`B`\B`
+B`záB`p€B`ffB`\)B`QìB`G®B`=qB`33B`(öB`
+žB`{B`
+=B`  B^
+B^záB^p€B^ffB^\)B^QìB^G®B^=qB^33B^(öB^
+žB^{B^
+=B^  B]õÃB]ë
+B]áHB]×
+B]ÌÍB]ÂB]žRB]®B]£×B]B]\B]
+B]záB]p€B]ffB]\)B]QìB]G®B]=qB]33B](öB]
+žB]{B]
+=B]  B\õÃB\ë
+B\áHB\×
+B\ÌÍB\ÂB\žRB\®B\£×B\B\\B\
+B\záB\p€B\ffB\\)B\QìB\G®B\=qB\33B\(öB\
+žB\{B\
+=B\  BZ
+BZzáBZp€BZffBZ\)BZQìBZG®BZ=qBZ33BZ(öBZ
+žBZ{BZ
+=BZ  BYõÃBYë
+BYáHBY×
+BYÌÍBYÂBYžRBY®BY£×BYBY\BY
+BYzáBYp€BYffBY\)BYQìBYG®BY=qBY33BY(öBY
+žBY{BY
+=BY  BXõÃBXë
+BXáHBX×
+BXÌÍBXÂBXžRBX®BX£×BXBX\BX
+BXzáBXp€BXffBX\)BXQìBXG®BX=qBX33BX(öBX
+žBX{BX
+=BX  BV
+BVzáBVp€BVffBV\)BVQìBVG®BV=qBV33BV(öBV
+žBV{BV
+=BV  BUõÃBUë
+BUáHBU×
+BUÌÍBUÂBUžRBU®BU£×BUBU\BU
+BUzáBUp€BUffBU\)BUQìBUG®BU=qBU33BU(öBU
+žBU{BU
+=BU  BTõÃBTë
+BTáHBT×
+BTÌÍBTÂBTžRBT®BT£×BTBT\BT
+BTzáBTp€BTffBT\)BTQìBTG®BT=qBT33BT(öBT
+žBT{BT
+=BT  BR
+BRzáBRp€BRffBR\)BRQìBRG®BR=qBR33BR(öBR
+žBR{BR
+=BR  BQõÃBQë
+BQáHBQ×
+BQÌÍBQÂBQžRBQ®BQ£×BQBQ\BQ
+BQzáBQp€BQffBQ\)BQQìBQG®BQ=qBQ33BQ(öBQ
+žBQ{BQ
+=BQ  BPõÃBPë
+BPáHBP×
+BPÌÍBPÂBPžRBP®BP£×BPBP\BP
+BPzáBPp€BPffBP\)BPQìBPG®BP=qBP33BP(öBP
+žBP{BP
+=BP  BN
+BNzáBNp€BNffBN\)BNQìBNG®BN=qBN33BN(öBN
+žBN{BN
+=BN  BMõÃBMë
+BMáHBM×
+BMÌÍBMÂBMžRBM®BM£×BMBM\BM
+BMzáBMp€BMffBM\)BMQìBMG®BM=qBM33BM(öBM
+žBM{BM
+=BM  BLõÃBLë
+BLáHBL×
+BLÌÍBLÂBLžRBL®BL£×BLBL\BL
+BLzáBLp€BLffBL\)BLQìBLG®BL=qBL33BL(öBL
+žBL{BL
+=BL  BJ
+BJzáBJp€BJffBJ\)BJQìBJG®BJ=qBJ33BJ(öBJ
+žBJ{BJ
+=BJ  BIõÃBIë
+BIáHBI×
+BIÌÍBIÂBIžRBI®BI£×BIBI\BI
+BIzáBIp€BIffBI\)BIQìBIG®BI=qBI33BI(öBI
+žBI{BI
+=BI  BHõÃBHë
+BHáHBH×
+BHÌÍBHÂBHžRBH®BH£×BHBH\BH
+BHzáBHp€BHffBH\)BHQìBHG®BH=qBH33BH(öBH
+žBH{BH
+=BH  BF
+BFzáBFp€BFffBF\)BFQìBFG®BF=qBF33BF(öBF
+žBF{BF
+=BF  BEõÃBEë
+BEáHBE×
+BEÌÍBEÂBEžRBE®BE£×BEBE\BE
+BEzáBEp€BEffBE\)BEQìBEG®BE=qBE33BE(öBE
+žBE{BE
+=BE  BDõÃBDë
+BDáHBD×
+BDÌÍBDÂBDžRBD®BD£×BDBD\BD
+BDzáBDp€BDffBD\)BDQìBDG®BD=qBD33BD(öBD
+žBD{BD
+=BD  BB
+BBzáBBp€BBffBB\)BBQìBBG®BB=qBB33BB(öBB
+žBB{BB
+=BB  BAõÃBAë
+BAáHBA×
+BAÌÍBAÂBAžRBA®BA£×BABA\BA
+BAzáBAp€BAffBA\)BAQìBAG®BA=qBA33BA(öBA
+žBA{BA
+=BA  B@õÃB@ë
+B@áHB@×
+B@ÌÍB@ÂB@žRB@®B@£×B@B@\B@
+B@záB@p€B@ffB@\)B@QìB@G®B@=qB@33B@(öB@
+žB@{B@
+=B@  B>
+B>záB>p€B>ffB>\)B>QìB>G®B>=qB>33B>(öB>
+žB>{B>
+=B>  B=õÃB=ë
+B=áHB=×
+B=ÌÍB=ÂB=žRB=®B=£×B=B=\B=
+B=záB=p€B=ffB=\)B=QìB=G®B==qB=33B=(öB=
+žB={B=
+=B=  B<õÃB<ë
+B<áHB<×
+B<ÌÍB<ÂB<žRB<®B<£×B<B<\B<
+B<záB<p€B<ffB<\)B<QìB<G®B<=qB<33B<(öB<
+žB<{B<
+=B<  B:
+B:záB:p€B:ffB:\)B:QìB:G®B:=qB:33B:(öB:
+žB:{B:
+=B:  B9õÃB9ë
+B9áHB9×
+B9ÌÍB9ÂB9žRB9®B9£×B9B9\B9
+B9záB9p€B9ffB9\)B9QìB9G®B9=qB933B9(öB9
+žB9{B9
+=B9  B8õÃB8ë
+B8áHB8×
+B8ÌÍB8ÂB8žRB8®B8£×B8B8\B8
+B8záB8p€B8ffB8\)B8QìB8G®B8=qB833B8(öB8
+žB8{B8
+=B8  B6
+B6záB6p€B6ffB6\)B6QìB6G®B6=qB633B6(öB6
+žB6{B6
+=B6  B5õÃB5ë
+B5áHB5×
+B5ÌÍB5ÂB5žRB5®B5£×B5B5\B5
+B5záB5p€B5ffB5\)B5QìB5G®B5=qB533B5(öB5
+žB5{B5
+=B5  B4õÃB4ë
+B4áHB4×
+B4ÌÍB4ÂB4žRB4®B4£×B4B4\B4
+B4záB4p€B4ffB4\)B4QìB4G®B4=qB433B4(öB4
+žB4{B4
+=B4  B2
+B2záB2p€B2ffB2\)B2QìB2G®B2=qB233B2(öB2
+žB2{B2
+=B2  B1õÃB1ë
+B1áHB1×
+B1ÌÍB1ÂB1žRB1®B1£×B1B1\B1
+B1záB1p€B1ffB1\)B1QìB1G®B1=qB133B1(öB1
+žB1{B1
+=B1  B0õÃB0ë
+B0áHB0×
+B0ÌÍB0ÂB0žRB0®B0£×B0B0\B0
+B0záB0p€B0ffB0\)B0QìB0G®B0=qB033B0(öB0
+žB0{B0
+=B0  B.
+B.záB.p€B.ffB.\)B.QìB.G®B.=qB.33B.(öB.
+žB.{B.
+=B.  B-õÃB-ë
+B-áHB-×
+B-ÌÍB-ÂB-žRB-®B-£×B-B-\B-
+B-záB-p€B-ffB-\)B-QìB-G®B-=qB-33B-(öB-
+žB-{B-
+=B-  B,õÃB,ë
+B,áHB,×
+B,ÌÍB,ÂB,žRB,®B,£×B,B,\B,
+B,záB,p€B,ffB,\)B,QìB,G®B,=qB,33B,(öB,
+žB,{B,
+=B,  B*
+B*záB*p€B*ffB*\)B*QìB*G®B*=qB*33B*(öB*
+žB*{B*
+=B*  B)õÃB)ë
+B)áHB)×
+B)ÌÍB)ÂB)žRB)®B)£×B)B)\B)
+B)záB)p€B)ffB)\)B)QìB)G®B)=qB)33B)(öB)
+žB){B)
+=B)  B(õÃB(ë
+B(áHB(×
+B(ÌÍB(ÂB(žRB(®B(£×B(B(\B(
+B(záB(p€B(ffB(\)B(QìB(G®B(=qB(33B((öB(
+žB({B(
+=B(  B&
+B&záB&p€B&ffB&\)B&QìB&G®B&=qB&33B&(öB&
+žB&{B&
+=B&  B%õÃB%ë
+B%áHB%×
+B%ÌÍB%ÂB%žRB%®B%£×B%B%\B%
+B%záB%p€B%ffB%\)B%QìB%G®B%=qB%33B%(öB%
+žB%{B%
+=B%  B$õÃB$ë
+B$áHB$×
+B$ÌÍB$ÂB$žRB$®B$£×B$B$\B$
+B$záB$p€B$ffB$\)B$QìB$G®B$=qB$33B$(öB$
+žB${B$
+=B$  B"
+B"záB"p€B"ffB"\)B"QìB"G®B"=qB"33B"(öB"
+žB"{B"
+=B"  B!õÃB!ë
+B!áHB!×
+B!ÌÍB!ÂB!žRB!®B!£×B!B!\B!
+B!záB!p€B!ffB!\)B!QìB!G®B!=qB!33B!(öB!
+žB!{B!
+=B!  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B	õÃB	ë
+B	áHB	×
+B	ÌÍB	ÂB	žRB	®B	£×B	B	\B	
+B	záB	p€B	ffB	\)B	QìB	G®B	=qB	33B	(öB	
+žB	{B	
+=B	  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   Aý
+=AüõÃAüáHAüÌÍAüžRAü£×Aü\AüzáAüffAüQìAü=qAü(öAü{Aü  Aûë
+Aû×
+AûÂAû®AûAû
+Aûp€Aû\)AûG®Aû33Aû
+žAû
+=AúõÃAúáHAúÌÍAúžRAú£×Aú\AúzáAúffAúQìAú=qAú(öAú{Aú  Aùë
+Aù×
+AùÂAù®AùAù
+Aùp€Aù\)AùG®Aù33Aù
+žAù
+=AøõÃAøáHAøÌÍAøžRAø£×Aø\AøzáAøffAøQìAø=qAø(öAø{Aø  Aõ
+=AôõÃAôáHAôÌÍAôžRAô£×Aô\AôzáAôffAôQìAô=qAô(öAô{Aô  Aóë
+Aó×
+AóÂAó®AóAó
+Aóp€Aó\)AóG®Aó33Aó
+žAó
+=AòõÃAòáHAòÌÍAòžRAò£×Aò\AòzáAòffAòQìAò=qAò(öAò{Aò  Añë
+Añ×
+AñÂAñ®AñAñ
+Añp€Añ\)AñG®Añ33Añ
+žAñ
+=AðõÃAðáHAðÌÍAðžRAð£×Að\AðzáAðffAðQìAð=qAð(öAð{Að  Aí
+=AìõÃAìáHAìÌÍAìžRAì£×Aì\AìzáAìffAìQìAì=qAì(öAì{Aì  Aëë
+Aë×
+AëÂAë®AëAë
+Aëp€Aë\)AëG®Aë33Aë
+žAë
+=AêõÃAêáHAêÌÍAêžRAê£×Aê\AêzáAêffAêQìAê=qAê(öAê{Aê  Aéë
+Aé×
+AéÂAé®AéAé
+Aép€Aé\)AéG®Aé33Aé
+žAé
+=AèõÃAèáHAèÌÍAèžRAè£×Aè\AèzáAèffAèQìAè=qAè(öAè{Aè  Aå
+=AäõÃAäáHAäÌÍAäžRAä£×Aä\AäzáAäffAäQìAä=qAä(öAä{Aä  Aãë
+Aã×
+AãÂAã®AãAã
+Aãp€Aã\)AãG®Aã33Aã
+žAã
+=AâõÃAâáHAâÌÍAâžRAâ£×Aâ\AâzáAâffAâQìAâ=qAâ(öAâ{Aâ  Aáë
+Aá×
+AáÂAá®AáAá
+Aáp€Aá\)AáG®Aá33Aá
+žAá
+=AàõÃAàáHAàÌÍAàžRAà£×Aà\AàzáAàffAàQìAà=qAà(öAà{Aà  AÝ
+=AÜõÃAÜáHAÜÌÍAÜžRAÜ£×AÜ\AÜzáAÜffAÜQìAÜ=qAÜ(öAÜ{AÜ  AÛë
+AÛ×
+AÛÂAÛ®AÛAÛ
+AÛp€AÛ\)AÛG®AÛ33AÛ
+žAÛ
+=AÚõÃAÚáHAÚÌÍAÚžRAÚ£×AÚ\AÚzáAÚffAÚQìAÚ=qAÚ(öAÚ{AÚ  AÙë
+AÙ×
+AÙÂAÙ®AÙAÙ
+AÙp€AÙ\)AÙG®AÙ33AÙ
+žAÙ
+=AØõÃAØáHAØÌÍAØžRAØ£×AØ\AØzáAØffAØQìAØ=qAØ(öAØ{AØ  AÕ
+=AÔõÃAÔáHAÔÌÍAÔžRAÔ£×AÔ\AÔzáAÔffAÔQìAÔ=qAÔ(öAÔ{AÔ  AÓë
+AÓ×
+AÓÂAÓ®AÓAÓ
+AÓp€AÓ\)AÓG®AÓ33AÓ
+žAÓ
+=AÒõÃAÒáHAÒÌÍAÒžRAÒ£×AÒ\AÒzáAÒffAÒQìAÒ=qAÒ(öAÒ{AÒ  AÑë
+AÑ×
+AÑÂAÑ®AÑAÑ
+AÑp€AÑ\)AÑG®AÑ33AÑ
+žAÑ
+=AÐõÃAÐáHAÐÌÍAÐžRAÐ£×AÐ\AÐzáAÐffAÐQìAÐ=qAÐ(öAÐ{AÐ  AÍ
+=AÌõÃAÌáHAÌÌÍAÌžRAÌ£×AÌ\AÌzáAÌffAÌQìAÌ=qAÌ(öAÌ{AÌ  AËë
+AË×
+AËÂAË®AËAË
+AËp€AË\)AËG®AË33AË
+žAË
+=AÊõÃAÊáHAÊÌÍAÊžRAÊ£×AÊ\AÊzáAÊffAÊQìAÊ=qAÊ(öAÊ{AÊ  AÉë
+AÉ×
+AÉÂAÉ®AÉAÉ
+AÉp€AÉ\)AÉG®AÉ33AÉ
+žAÉ
+=AÈõÃAÈáHAÈÌÍAÈžRAÈ£×AÈ\AÈzáAÈffAÈQìAÈ=qAÈ(öAÈ{AÈ  AÅ
+=AÄõÃAÄáHAÄÌÍAÄžRAÄ£×AÄ\AÄzáAÄffAÄQìAÄ=qAÄ(öAÄ{AÄ  AÃë
+AÃ×
+AÃÂAÃ®AÃAÃ
+AÃp€AÃ\)AÃG®AÃ33AÃ
+žAÃ
+=AÂõÃAÂáHAÂÌÍAÂžRAÂ£×AÂ\AÂzáAÂffAÂQìAÂ=qAÂ(öAÂ{AÂ  AÁë
+AÁ×
+AÁÂAÁ®AÁAÁ
+AÁp€AÁ\)AÁG®AÁ33AÁ
+žAÁ
+=AÀõÃAÀáHAÀÌÍAÀžRAÀ£×AÀ\AÀzáAÀffAÀQìAÀ=qAÀ(öAÀ{AÀ  Aœ
+=AŒõÃAŒáHAŒÌÍAŒžRAŒ£×AŒ\AŒzáAŒffAŒQìAŒ=qAŒ(öAŒ{AŒ  A»ë
+A»×
+A»ÂA»®A»A»
+A»p€A»\)A»G®A»33A»
+žA»
+=AºõÃAºáHAºÌÍAºžRAº£×Aº\AºzáAºffAºQìAº=qAº(öAº{Aº  A¹ë
+A¹×
+A¹ÂA¹®A¹A¹
+A¹p€A¹\)A¹G®A¹33A¹
+žA¹
+=AžõÃAžáHAžÌÍAžžRAž£×Až\AžzáAžffAžQìAž=qAž(öAž{Až  Aµ
+=AŽõÃAŽáHAŽÌÍAŽžRAŽ£×AŽ\AŽzáAŽffAŽQìAŽ=qAŽ(öAŽ{AŽ  A³ë
+A³×
+A³ÂA³®A³A³
+A³p€A³\)A³G®A³33A³
+žA³
+=A²õÃA²áHA²ÌÍA²žRA²£×A²\A²záA²ffA²QìA²=qA²(öA²{A²  A±ë
+A±×
+A±ÂA±®A±A±
+A±p€A±\)A±G®A±33A±
+žA±
+=A°õÃA°áHA°ÌÍA°žRA°£×A°\A°záA°ffA°QìA°=qA°(öA°{A°  A­
+=A¬õÃA¬áHA¬ÌÍA¬žRA¬£×A¬\A¬záA¬ffA¬QìA¬=qA¬(öA¬{A¬  A«ë
+A«×
+A«ÂA«®A«A«
+A«p€A«\)A«G®A«33A«
+žA«
+=AªõÃAªáHAªÌÍAªžRAª£×Aª\AªzáAªffAªQìAª=qAª(öAª{Aª  A©ë
+A©×
+A©ÂA©®A©A©
+A©p€A©\)A©G®A©33A©
+žA©
+=AšõÃAšáHAšÌÍAšžRAš£×Aš\AšzáAšffAšQìAš=qAš(öAš{Aš  A¥
+=A€õÃA€áHA€ÌÍA€žRA€£×A€\A€záA€ffA€QìA€=qA€(öA€{A€  A£ë
+A£×
+A£ÂA£®A£A£
+A£p€A£\)A£G®A£33A£
+žA£
+=A¢õÃA¢áHA¢ÌÍA¢žRA¢£×A¢\A¢záA¢ffA¢QìA¢=qA¢(öA¢{A¢  A¡ë
+A¡×
+A¡ÂA¡®A¡A¡
+A¡p€A¡\)A¡G®A¡33A¡
+žA¡
+=A õÃA áHA ÌÍA žRA £×A \A záA ffA QìA =qA (öA {A   A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Az{Ayë
+AyÂAyAyp€AyG®Ay
+žAxõÃAxÌÍAx£×AxzáAxQìAx(öAx  Aw×
+Aw®Aw
+Aw\)Aw33Aw
+=AváHAvžRAv\AvffAv=qAv{Auë
+AuÂAuAup€AuG®Au
+žAtõÃAtÌÍAt£×AtzáAtQìAt(öAt  As×
+As®As
+As\)As33As
+=AráHAržRAr\ArffAr=qAr{Aqë
+AqÂAqAqp€AqG®Aq
+žApõÃApÌÍAp£×ApzáApQìAp(öAp  Aj{Aië
+AiÂAiAip€AiG®Ai
+žAhõÃAhÌÍAh£×AhzáAhQìAh(öAh  Ag×
+Ag®Ag
+Ag\)Ag33Ag
+=AfáHAfžRAf\AfffAf=qAf{Aeë
+AeÂAeAep€AeG®Ae
+žAdõÃAdÌÍAd£×AdzáAdQìAd(öAd  Ac×
+Ac®Ac
+Ac\)Ac33Ac
+=AbáHAbžRAb\AbffAb=qAb{Aaë
+AaÂAaAap€AaG®Aa
+žA`õÃA`ÌÍA`£×A`záA`QìA`(öA`  AZ{AYë
+AYÂAYAYp€AYG®AY
+žAXõÃAXÌÍAX£×AXzáAXQìAX(öAX  AW×
+AW®AW
+AW\)AW33AW
+=AVáHAVžRAV\AVffAV=qAV{AUë
+AUÂAUAUp€AUG®AU
+žATõÃATÌÍAT£×ATzáATQìAT(öAT  AS×
+AS®AS
+AS\)AS33AS
+=ARáHARžRAR\ARffAR=qAR{AQë
+AQÂAQAQp€AQG®AQ
+žAPõÃAPÌÍAP£×APzáAPQìAP(öAP  AJ{AIë
+AIÂAIAIp€AIG®AI
+žAHõÃAHÌÍAH£×AHzáAHQìAH(öAH  AG×
+AG®AG
+AG\)AG33AG
+=AFáHAFžRAF\AFffAF=qAF{AEë
+AEÂAEAEp€AEG®AE
+žADõÃADÌÍAD£×ADzáADQìAD(öAD  AC×
+AC®AC
+AC\)AC33AC
+=ABáHABžRAB\ABffAB=qAB{AAë
+AAÂAAAAp€AAG®AA
+žA@õÃA@ÌÍA@£×A@záA@QìA@(öA@  A:{A9ë
+A9ÂA9A9p€A9G®A9
+žA8õÃA8ÌÍA8£×A8záA8QìA8(öA8  A7×
+A7®A7
+A7\)A733A7
+=A6áHA6žRA6\A6ffA6=qA6{A5ë
+A5ÂA5A5p€A5G®A5
+žA4õÃA4ÌÍA4£×A4záA4QìA4(öA4  A3×
+A3®A3
+A3\)A333A3
+=A2áHA2žRA2\A2ffA2=qA2{A1ë
+A1ÂA1A1p€A1G®A1
+žA0õÃA0ÌÍA0£×A0záA0QìA0(öA0  A*{A)ë
+A)ÂA)A)p€A)G®A)
+žA(õÃA(ÌÍA(£×A(záA(QìA((öA(  A'×
+A'®A'
+A'\)A'33A'
+=A&áHA&žRA&\A&ffA&=qA&{A%ë
+A%ÂA%A%p€A%G®A%
+žA$õÃA$ÌÍA$£×A$záA$QìA$(öA$  A#×
+A#®A#
+A#\)A#33A#
+=A"áHA"žRA"\A"ffA"=qA"{A!ë
+A!ÂA!A!p€A!G®A!
+žA õÃA ÌÍA £×A záA QìA (öA   A{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A
+{A	ë
+A	ÂA	A	p€A	G®A	
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žA õÃA ÌÍA £×A záA QìA (öA   @ô(ö@ó×
+@ó
+@ó33@òáH@ò\@ò=q@ñë
+@ñ@ñG®@ðõÂ@ð£×@ðQì@ð  @ï®@ï\)@ï
+=@îžR@îff@î{@íÂ@íp€@í
+ž@ìÌÍ@ìzá@ì(ö@ë×
+@ë
+@ë33@êáH@ê\@ê=q@éë
+@é@éG®@èõÃ@è£×@èQì@è  @ç®@ç\)@ç
+=@æžR@æff@æ{@åÂ@åp€@å
+ž@äÌÍ@äzá@ä(ö@ã×
+@ã
+@ã33@âáH@â\@â=q@áë
+@á@áG®@àõÃ@à£×@àQì@à  @Ô(ö@Ó×
+@Ó
+@Ó33@ÒáH@Ò\@Ò=q@Ñë
+@Ñ@ÑG®@ÐõÂ@Ð£×@ÐQì@Ð  @Ï®@Ï\)@Ï
+=@ÎžR@Îff@Î{@ÍÂ@Íp€@Í
+ž@ÌÌÍ@Ìzá@Ì(ö@Ë×
+@Ë
+@Ë33@ÊáH@Ê\@Ê=q@Éë
+@É@ÉG®@ÈõÃ@È£×@ÈQì@È  @Ç®@Ç\)@Ç
+=@ÆžR@Æff@Æ{@ÅÂ@Åp€@Å
+ž@ÄÌÍ@Äzá@Ä(ö@Ã×
+@Ã
+@Ã33@ÂáH@Â\@Â=q@Áë
+@Á@ÁG®@ÀõÃ@À£×@ÀQì@À  @Ž(ö@³×
+@³
+@³33@²áH@²\@²=q@±ë
+@±@±G®@°õÂ@°£×@°Qì@°  @¯®@¯\)@¯
+=@®žR@®ff@®{@­Â@­p€@­
+ž@¬ÌÍ@¬zá@¬(ö@«×
+@«
+@«33@ªáH@ª\@ª=q@©ë
+@©@©G®@šõÃ@š£×@šQì@š  @§®@§\)@§
+=@ŠžR@Šff@Š{@¥Â@¥p€@¥
+ž@€ÌÍ@€zá@€(ö@£×
+@£
+@£33@¢áH@¢\@¢=q@¡ë
+@¡@¡G®@ õÃ@ £×@ Qì@   @(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÂ@£×@Qì@  @®@\)@
+=@žR@ff@{@Â@p€@
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @®@\)@
+=@žR@ff@{@
+Â@
+p€@
+
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @hQì@g®@g
+>@fff@eÂ@e
+ž@dzá@c×
+@c33@b\@aë
+@aG®@`£×@`  @_\)@^žR@^{@]p€@\ÌÍ@\(ö@[
+@ZáH@Z=q@Y@XõÂ@XQì@W®@W
+>@Vff@UÂ@U
+ž@Tzá@S×
+@S33@R\@Që
+@QG®@P£×@P  @O\)@NžR@N{@Mp€@LÌÍ@L(ö@K
+@JáH@J=q@I@HõÃ@HQì@G®@G
+=@Fff@EÂ@E
+ž@Dzá@C×
+@C33@B\@Aë
+@AG®@@£×@@  @(Qì@'®@'
+>@&ff@%Â@%
+ž@$zá@#×
+@#33@"\@!ë
+@!G®@ £×@   @\)@
+žR@
+{@
+p€@
+ÌÍ@
+(ö@
+@áH@=q@@õÂ@Qì@®@
+>@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@£×@  @\)@žR@{@
+p€@
+ÌÍ@
+(ö@
+
+@
+áH@
+=q@	@õÃ@Qì@®@
+=@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@ £×@   ?Ð£×?Ï\)?Î{?ÌÌÍ?Ë
+
+?Ê=p?ÈõÂ?Ç®?Æff?Å
+ž?Ã×
+?Â\?ÁG®?À  ?ŸžR?œp€?Œ(ö?ºáH?¹?žQì?·
+>?µÂ?Žzá?³33?±ë
+?°£×?¯\)?®{?¬ÌÍ?«
+?ª=q?šõÂ?§®?Šff?¥
+ž?£×
+?¢\?¡G®?   ?žR?p€?(ö?áH??Qì?
+>?Â?zá?33?ë
+?£×?\)?{?ÌÍ?
+?=q?õÃ?®?ff?
+
+ž?×
+?\?G®?  ?!G®?
+žR?
+(ö??
+=?zá?ë
+?\)?
+ÌÍ?
+=q?®?
+ž?\?   >úáH>õÂ>ð£×>ë
+>æff>áG®>Ü(ö>×
+=>Ñë
+>ÌÌÍ>Ç®>Â\>œp€>žQì>³33>®{>šõÃ>£×
+>žR>>zá>\)>=q>
+
+ž>  >uÂ>k
+>aG®>W
+=>LÌÍ>B\>8Qì>.{>#×
+>>\)>
+ž=õÂ=áG®=ÌÌÍ=žQì=£×
+=\)=uÂ=LÌÍ=#×
+<õÂ<£×
+<#×
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/sBiOut.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/sBiOut.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/sBiOut.fits	(revision 22322)
@@ -0,0 +1,1414 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; = ?ÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : < >ÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; =ÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : <ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ;ÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 :ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9ÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6ÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - /ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , .ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + -ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * ,ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) +ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( *ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' )ÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & (ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % 'ÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ &ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # %ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! #ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   "ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  !ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+  ÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ ÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+ÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+       ÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+       ÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+      ÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+     ÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+     ÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+    ÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+    ÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+   ÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+   ÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+  ÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ ÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ ÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ    ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ   ÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁÿÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÁÿÃÿÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ÿ¿ÿÂÿÄÿÆÿÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœÿŸÿÀÿÂÿÄÿÆÿÉÿÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹ÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % &ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - /ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 1 3 5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - 0 2 4 6 7ÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 2 4 7 9 :ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 1 3 5 7 9 < =ÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - 0 2 4 6 8 : < >ÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 2 4 7 9 ; = >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 1 3 5 7 9 < <ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - 0 2 4 6 8 : ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 2 4 7 9 :ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 1 3 5 7 8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - 0 2 4 6 7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 2 4 5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 1 3 4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - 0 2 2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 1ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - .ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) *ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & 'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % &ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ $ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " #ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿñÿóÿõÿ÷ÿùÿüÿþ       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõÿ÷ÿùÿûÿýÿÿ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿùÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿüÿþ    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ   ÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ     ÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ       ÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ         ÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	ÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+ÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+  ÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+    ÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+      ÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+        ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+          ÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+            ÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+              ÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+ ÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    !ÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " #ÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ %ÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & 'ÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( )ÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * +ÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , -ÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . /ÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1ÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3ÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7ÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9ÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ;ÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < =           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿ§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿœÿÿÿÿÿÿÿÿÿÿÿÿÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿŸÿÿÿÿÿÿÿÿÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿ¿ÿÿÿÿÿÿÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿÁ      ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÿ         ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÃÿÿÿÿ          ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÿÿÿÿÿÿÿ            ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÿÿÿÿÿÿÿÿÿÿÿ              ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                  ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+  
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ         
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ           
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ             
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ               
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                 
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                   
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                     
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+                     
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+  
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	 	                 ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	 	            ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ! ! !     
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	 	       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ " # " ! !     
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	 	   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ $ $ $ # " ! !     
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % & % $ $ # " ! !     
+ 
+ 
+ 
+                     
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ & ' & & % $ $ # " ! !     
+ 
+ 
+ 
+                  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ( ( ( ' & & % $ $ # " ! !     
+ 
+ 
+ 
+              ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) * ) ( ( ' & & % $ $ # " ! !     
+ 
+ 
+ 
+          ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ + + + * ) ( ( ' & & % $ $ # " ! !     
+ 
+ 
+ 
+      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ , - , + + * ) ( ( ' & & % $ $ # " ! !     
+ 
+ 
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - . - - , + + * ) ( ( ' & & % $ $ # " ! !     
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ / 0 / . - - , + + * ) ( ( ' & & % $ $ # " ! ! ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 0 1 0 0 / . - - , + + * ) ( ( ' & & % $ $ "ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2 2 2 1 0 0 / . - - , + + * ) ( ( ' & %ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3 4 3 2 2 1 0 0 / . - - , + + * ) (ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 5 4 4 3 2 2 1 0 0 / . - - , +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 6 7 6 5 4 4 3 2 2 1 0 0 / -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 7 8 7 7 6 5 4 4 3 2 2 0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9 9 9 8 7 7 6 5 4 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ : ; : 9 9 8 7 6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ < < < ; : 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ = > = <ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ > >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ   ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+    ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿ    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿ      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿ 
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿ 
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿ  
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿ  
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿ   
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿ   
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿ    
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿ    
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿ     
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿ     
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿ      
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿ      
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿ       
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿ       
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿ        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿ        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿ 
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿ 
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ   
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠ % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿš ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ© ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿª ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ« * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ® - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ° / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ± 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ² 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽ 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµ 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ· 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿž 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿº 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ» : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒ ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœ < : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸ = ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ > < : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀ ? = ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁ                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿþÿüÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿþÿüÿùÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿÿÿýÿûÿùÿ÷ÿõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      ÿþÿüÿúÿøÿöÿôÿòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ! !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ # "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ $ $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ & % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ' & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ * ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ + + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿ©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ . - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 0 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 1 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2 2 0 - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 4 3 1 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 4 2 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 7 6 4 2 0 - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 8 7 5 3 1 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ : 9 7 4 2 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ; : 8 6 4 2 0 - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ < < 9 7 5 3 1 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ > = ; 9 7 4 2 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ ? > < : 8 6 4 2 0 - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿ = < 9 7 5 3 1 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ : 9 7 4 2 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿÿÿÿ 7 6 4 2 0 - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 3 1 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ / - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ , + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ & % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿ©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ«ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿ¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿ¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÿÉÿÆÿÄÿÂÿÀÿŸÿœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿÆÿÄÿÂÿ¿ÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÿÃÿÁÿÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÿÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ? > = < ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	           = < ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþ ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿü 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿú 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿø 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿö 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿô 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿò / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿð - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿî + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿì ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿê ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿè % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿä !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâ  
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿà 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞ               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜ             
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚ           
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØ         
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖ       
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔ     
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒ   
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐ 
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊ         ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈ       ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆ     ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄ   ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ > >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ < = > =ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9 : ; < < <ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 6 7 8 9 9 : ; :ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3 4 5 6 7 7 8 9 9 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 0 2 2 3 4 4 5 6 7 7 8 7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - / 0 0 1 2 2 3 4 4 5 6 7 6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ + , - - . / 0 0 1 2 2 3 4 4 5 5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ( ) * + + , - - . / 0 0 1 2 2 3 4 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % & ' ( ( ) * + + , - - . / 0 0 1 2 2 2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ " $ $ % & & ' ( ( ) * + + , - - . / 0 0 1 0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ! ! " # $ $ % & & ' ( ( ) * + + , - - . / 0 /ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+     ! ! " # $ $ % & & ' ( ( ) * + + , - - . -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+ 
+ 
+     ! ! " # $ $ % & & ' ( ( ) * + + , - ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       
+ 
+ 
+ 
+     ! ! " # $ $ % & & ' ( ( ) * + + +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ           
+ 
+ 
+ 
+     ! ! " # $ $ % & & ' ( ( ) * )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ               
+ 
+ 
+ 
+     ! ! " # $ $ % & & ' ( ( (ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                   
+ 
+ 
+ 
+     ! ! " # $ $ % & & ' &ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+                     
+ 
+ 
+ 
+     ! ! " # $ $ % & %ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+     ! ! " # $ $ $ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    	 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+     ! ! " # "ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        	 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+     ! ! !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ             	 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ                  	 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+  
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+                     
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+                    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+                  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+                ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+              ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+            ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+          ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+  
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ              ÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ            ÿÿÿÿÿÿÿÿÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ          ÿÿÿÿÿÃÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ         ÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ    ÿÿÿÁÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿÿÿÿÿÿÿ¿ÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿÿÿÿÿÿÿÿÿŸÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿÿÿÿÿÿÿÿÿÿÿÿÿœÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; = ?ÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : < >ÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; =ÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : <ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ;ÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 :ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9ÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6ÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - /ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , .ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + -ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * ,ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) +ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( *ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' )ÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & (ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % 'ÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ &ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # %ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! #ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   "ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  !ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+  ÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ ÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+ÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+       ÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+       ÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+      ÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+     ÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+     ÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+    ÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+    ÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+   ÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+   ÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+  ÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ ÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ ÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ    ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ   ÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
Index: /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/sOut.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/sOut.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/fits/verified/sOut.fits	(revision 22322)
@@ -0,0 +1,1450 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; = ?ÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : < >ÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; =ÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : <ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ;ÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 :ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9ÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6ÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - /ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , .ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + -ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * ,ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) +ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( *ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' )ÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & (ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % 'ÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ &ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # %ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! #ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   "ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  !ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+  ÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ ÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+ÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+       ÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+       ÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+      ÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+     ÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+     ÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+    ÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+    ÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+   ÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+   ÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+  ÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ ÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ ÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ    ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ   ÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ÿÂÿÂÿÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœÿÀÿÁÿÄÿÆÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÿ¿ÿ¿ÿÂÿÅÿÈÿÈÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÿœÿŸÿÁÿÃÿÆÿÇÿÊÿÌÿÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹ÿŒÿœÿ¿ÿÂÿÅÿÅÿÈÿËÿÌÿÎÿÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿºÿ»ÿŸÿÁÿÃÿÄÿÇÿÉÿÊÿÍÿÐÿÒÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÿ¹ÿºÿŒÿ¿ÿÂÿÃÿÅÿÈÿÉÿËÿÎÿÑÿÒÿÔÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµÿžÿžÿ»ÿŸÿÀÿÁÿÄÿÇÿÇÿÊÿÍÿÏÿÐÿÓÿÖÿÖÿÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ¶ÿ·ÿºÿŒÿ¿ÿÀÿÂÿÅÿÆÿÉÿËÿÎÿÏÿÑÿÔÿÕÿØÿÚÿÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²ÿµÿ¶ÿžÿ»ÿŸÿŸÿÁÿÄÿÄÿÇÿÊÿÍÿÍÿÐÿÓÿÓÿÖÿÙÿÚÿÜÿßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÿ³ÿŽÿ·ÿºÿŒÿœÿÀÿÂÿÃÿÆÿÈÿËÿÌÿÏÿÑÿÒÿÕÿ×ÿØÿÛÿÞÿàÿáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÿ²ÿ³ÿµÿžÿ»ÿŒÿŸÿÁÿÂÿÄÿÇÿÊÿÊÿÍÿÐÿÑÿÓÿÖÿ×ÿÙÿÜÿßÿàÿâÿåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿ±ÿ±ÿŽÿ·ÿ¹ÿºÿœÿÀÿÀÿÃÿÆÿÈÿÉÿÌÿÎÿÏÿÒÿÕÿÕÿØÿÛÿÝÿÞÿáÿäÿäÿçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ¯ÿ°ÿ³ÿµÿžÿ¹ÿ»ÿŸÿ¿ÿÂÿÄÿÇÿÈÿÊÿÍÿÎÿÐÿÓÿÔÿ×ÿÙÿÜÿÝÿßÿâÿãÿæÿèÿëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«ÿ®ÿ®ÿ±ÿŽÿ·ÿ·ÿºÿœÿœÿÀÿÃÿÆÿÆÿÉÿÌÿÌÿÏÿÒÿÒÿÕÿØÿÛÿÛÿÞÿáÿáÿäÿçÿêÿêÿíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿ¬ÿ­ÿ°ÿ²ÿµÿ¶ÿ¹ÿ»ÿŒÿ¿ÿÁÿÄÿÅÿÈÿÊÿËÿÎÿÐÿÑÿÔÿÖÿÙÿÚÿÝÿßÿàÿãÿåÿèÿéÿìÿîÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿ«ÿ¬ÿ®ÿ±ÿŽÿŽÿ·ÿºÿ»ÿœÿÀÿÃÿÃÿÆÿÉÿÊÿÌÿÏÿÐÿÒÿÕÿØÿØÿÛÿÞÿßÿáÿäÿçÿçÿêÿíÿîÿðÿóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ÿªÿªÿ­ÿ°ÿ²ÿ³ÿ¶ÿžÿ¹ÿŒÿ¿ÿÁÿÂÿÅÿÇÿÈÿËÿÎÿÎÿÑÿÔÿÖÿ×ÿÚÿÜÿÝÿàÿãÿåÿæÿéÿëÿìÿïÿòÿòÿõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥ÿšÿ©ÿ¬ÿ®ÿ±ÿ²ÿŽÿ·ÿžÿºÿœÿÀÿÁÿÃÿÆÿÇÿÉÿÌÿÍÿÐÿÒÿÕÿÖÿØÿÛÿÜÿÞÿáÿäÿåÿçÿêÿëÿíÿðÿñÿôÿöÿùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿ§ÿ§ÿªÿ­ÿ°ÿ°ÿ³ÿ¶ÿ¶ÿ¹ÿŒÿŸÿ¿ÿÂÿÅÿÅÿÈÿËÿËÿÎÿÑÿÔÿÔÿ×ÿÚÿÚÿÝÿàÿâÿãÿæÿéÿéÿìÿïÿïÿòÿõÿøÿøÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ÿ¥ÿŠÿ©ÿ«ÿ®ÿ¯ÿ²ÿŽÿµÿžÿºÿœÿŸÿÀÿÃÿÄÿÇÿÉÿÊÿÍÿÏÿÒÿÓÿÖÿØÿÙÿÜÿÞÿáÿâÿäÿçÿèÿëÿíÿîÿñÿóÿöÿ÷ÿúÿüÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿ¥ÿ§ÿªÿ­ÿ­ÿ°ÿ³ÿŽÿ¶ÿ¹ÿŒÿŒÿ¿ÿÂÿÂÿÅÿÈÿÉÿËÿÎÿÑÿÑÿÔÿ×ÿØÿÚÿÝÿàÿàÿãÿæÿæÿéÿìÿíÿïÿòÿõÿõÿøÿûÿüÿþ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ£ÿŠÿ©ÿ«ÿ¬ÿ¯ÿ±ÿ²ÿµÿžÿºÿ»ÿŸÿÀÿÁÿÄÿÆÿÇÿÊÿÍÿÏÿÐÿÓÿÕÿÖÿÙÿÜÿÞÿßÿâÿäÿåÿèÿêÿëÿîÿñÿóÿôÿ÷ÿùÿúÿý    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¢ÿ€ÿ§ÿªÿ«ÿ­ÿ°ÿ±ÿ³ÿ¶ÿ¹ÿºÿŒÿ¿ÿÀÿÂÿÅÿÆÿÈÿËÿÎÿÏÿÑÿÔÿÕÿ×ÿÚÿÝÿÞÿàÿãÿäÿæÿéÿêÿìÿïÿòÿóÿõÿøÿùÿûÿþ    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ£ÿŠÿšÿ©ÿ¬ÿ¯ÿ¯ÿ²ÿµÿ·ÿžÿ»ÿŸÿŸÿÁÿÄÿÄÿÇÿÊÿÌÿÍÿÐÿÓÿÓÿÖÿÙÿÛÿÜÿßÿâÿâÿåÿèÿèÿëÿîÿðÿñÿôÿ÷ÿ÷ÿúÿýÿÿ      	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ€ÿ§ÿšÿªÿ­ÿ®ÿ±ÿ³ÿ¶ÿ·ÿ¹ÿŒÿœÿÀÿÂÿÃÿÆÿÈÿËÿÌÿÎÿÑÿÒÿÕÿ×ÿÚÿÛÿÝÿàÿáÿäÿæÿçÿêÿìÿïÿðÿòÿõÿöÿùÿûÿþÿÿ     
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ£ÿŠÿŠÿ©ÿ¬ÿ¬ÿ¯ÿ²ÿµÿµÿžÿ»ÿ»ÿŸÿÁÿÂÿÄÿÇÿÊÿÊÿÍÿÐÿÐÿÓÿÖÿÙÿÙÿÜÿßÿßÿâÿåÿæÿèÿëÿîÿîÿñÿôÿôÿ÷ÿúÿýÿý      	 
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿ¥ÿšÿªÿ«ÿ®ÿ°ÿ³ÿŽÿ·ÿ¹ÿºÿœÿ¿ÿÀÿÃÿÆÿÈÿÉÿÌÿÎÿÏÿÒÿÔÿ×ÿØÿÛÿÝÿÞÿáÿãÿäÿçÿêÿìÿíÿðÿòÿóÿöÿøÿûÿüÿÿ     
+ 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ£ÿ£ÿŠÿ©ÿªÿ¬ÿ¯ÿ²ÿ²ÿµÿžÿ¹ÿ»ÿŸÿ¿ÿÁÿÄÿÇÿÈÿÊÿÍÿÎÿÐÿÓÿÖÿÖÿÙÿÜÿÝÿßÿâÿãÿåÿèÿëÿìÿîÿñÿòÿôÿ÷ÿúÿúÿý      	 	 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¢ÿ¥ÿ§ÿšÿ«ÿ®ÿ°ÿ±ÿŽÿ¶ÿ·ÿºÿœÿœÿÀÿÃÿÅÿÆÿÉÿÌÿÌÿÏÿÒÿÔÿÕÿØÿÚÿÛÿÞÿáÿáÿäÿçÿéÿêÿíÿðÿðÿóÿöÿøÿùÿüÿþÿÿ     
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ£ÿŠÿ§ÿ©ÿ¬ÿ¯ÿ°ÿ²ÿµÿ¶ÿžÿ»ÿŒÿ¿ÿÁÿÄÿÅÿÇÿÊÿËÿÎÿÐÿÓÿÔÿÖÿÙÿÚÿÜÿßÿàÿãÿåÿèÿéÿëÿîÿïÿòÿôÿ÷ÿøÿúÿýÿþ      	 
+ 
+      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ¥ÿ¥ÿšÿ«ÿ­ÿ®ÿ±ÿŽÿŽÿ·ÿºÿºÿœÿÀÿÃÿÃÿÆÿÉÿÉÿÌÿÏÿÒÿÒÿÕÿØÿØÿÛÿÞÿÞÿáÿäÿçÿçÿêÿíÿíÿðÿóÿöÿöÿùÿüÿüÿÿ     
+ 
+        
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ€ÿ§ÿ©ÿ¬ÿ­ÿ¯ÿ²ÿ³ÿ¶ÿžÿ¹ÿŒÿŸÿÁÿÂÿÅÿÇÿÈÿËÿÍÿÐÿÑÿÔÿÖÿ×ÿÚÿÜÿÝÿàÿâÿåÿæÿéÿëÿìÿïÿñÿôÿõÿøÿúÿûÿþ      	 
+ 
+       
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿ¥ÿšÿ«ÿ«ÿ®ÿ±ÿ±ÿŽÿ·ÿžÿºÿœÿÀÿÀÿÃÿÆÿÇÿÉÿÌÿÏÿÏÿÒÿÕÿÖÿØÿÛÿÜÿÞÿáÿäÿäÿçÿêÿëÿíÿðÿóÿóÿöÿùÿúÿüÿÿ      
+        
+ 
+   #ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ€ÿ§ÿ©ÿªÿ­ÿ¯ÿ°ÿ³ÿµÿ¶ÿ¹ÿŒÿŸÿ¿ÿÂÿÄÿÅÿÈÿËÿÍÿÎÿÑÿÓÿÔÿ×ÿÚÿÚÿÝÿàÿâÿãÿæÿèÿéÿìÿïÿñÿòÿõÿ÷ÿøÿûÿþ       
+ 
+ 
+       
+  " $ %ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿšÿ©ÿ«ÿ®ÿ¯ÿ±ÿŽÿµÿ·ÿºÿœÿŸÿÀÿÃÿÄÿÆÿÉÿÌÿÍÿÏÿÒÿÓÿÕÿØÿÙÿÜÿÞÿáÿâÿäÿçÿèÿêÿíÿðÿñÿóÿöÿ÷ÿùÿüÿÿ       
+ 
+        
+   # $ & )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿŠÿ§ÿªÿ­ÿ­ÿ°ÿ³ÿ³ÿ¶ÿ¹ÿ»ÿŒÿ¿ÿÂÿÂÿÅÿÈÿÊÿËÿÎÿÑÿÑÿÔÿ×ÿ×ÿÚÿÝÿàÿàÿãÿæÿæÿéÿìÿîÿïÿòÿõÿõÿøÿûÿýÿþ     
+ 
+ 
+       
+  ! " % ( ( +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿŠÿšÿ«ÿ¬ÿ¯ÿ±ÿ²ÿµÿ·ÿºÿ»ÿœÿÀÿÁÿÄÿÆÿÉÿÊÿÌÿÏÿÐÿÓÿÕÿÖÿÙÿÛÿÞÿßÿâÿäÿåÿèÿêÿíÿîÿðÿóÿôÿ÷ÿùÿüÿýÿÿ     	 
+        
+   ! # & ' * , -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿ€ÿ§ÿªÿªÿ­ÿ°ÿ±ÿ³ÿ¶ÿ¹ÿ¹ÿŒÿ¿ÿ¿ÿÂÿÅÿÈÿÈÿËÿÎÿÎÿÑÿÔÿÕÿ×ÿÚÿÝÿÝÿàÿãÿäÿæÿéÿìÿìÿïÿòÿòÿõÿøÿûÿûÿþ      
+ 
+       
+   " % % ( + , . 1ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿŠÿšÿ©ÿ¬ÿ®ÿ¯ÿ²ÿµÿ·ÿžÿ»ÿœÿŸÿÁÿÃÿÆÿÇÿÊÿÌÿÍÿÐÿÒÿÓÿÖÿÙÿÛÿÜÿßÿáÿâÿåÿèÿêÿëÿîÿðÿñÿôÿöÿùÿúÿýÿÿ      	 
+        
+ 
+ ! # $ ' ) * - 0 2 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¡ÿ€ÿ§ÿšÿªÿ­ÿ®ÿ°ÿ³ÿ¶ÿ·ÿ¹ÿŒÿœÿ¿ÿÂÿÅÿÅÿÈÿËÿÌÿÎÿÑÿÒÿÔÿ×ÿÚÿÛÿÝÿàÿáÿãÿæÿéÿêÿìÿïÿðÿòÿõÿøÿøÿûÿþÿÿ     
+ 
+       
+ 
+  " # % ( ) + . 1 2 4 7ÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ£ÿ¥ÿŠÿ©ÿ¬ÿ¬ÿ¯ÿ²ÿŽÿµÿžÿ»ÿ»ÿŸÿÁÿÃÿÄÿÇÿÉÿÊÿÍÿÐÿÐÿÓÿÖÿØÿÙÿÜÿßÿßÿâÿåÿçÿèÿëÿîÿîÿñÿôÿöÿ÷ÿúÿüÿý      	 
+ 
+        
+   ! $ ' ' * - / 0 3 6 6 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿ¥ÿ§ÿªÿ«ÿ®ÿ°ÿ³ÿŽÿ¶ÿ¹ÿºÿœÿ¿ÿÂÿÃÿÅÿÈÿÉÿËÿÎÿÏÿÒÿÔÿ×ÿØÿÚÿÝÿÞÿáÿãÿæÿçÿéÿìÿíÿðÿòÿõÿöÿøÿûÿüÿþ     
+ 
+ 
+       
+    " % & ) + . / 1 4 5 8 : =ÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ£ÿ£ÿŠÿ©ÿ©ÿ¬ÿ¯ÿ²ÿ²ÿµÿžÿžÿ»ÿŸÿÁÿÁÿÄÿÇÿÇÿÊÿÍÿÍÿÐÿÓÿÖÿÖÿÙÿÜÿÜÿßÿâÿåÿåÿèÿëÿëÿîÿñÿôÿôÿ÷ÿúÿúÿý       	 	 
+        
+ 
+ ! $ $ ' * - - 0 3 3 6 9 < < ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¢ÿ¥ÿ§ÿšÿ«ÿ­ÿ°ÿ±ÿŽÿ¶ÿ·ÿºÿŒÿ¿ÿÀÿÃÿÅÿÆÿÉÿËÿÌÿÏÿÑÿÔÿÕÿØÿÚÿÛÿÞÿàÿãÿäÿçÿéÿêÿíÿïÿòÿóÿöÿøÿùÿüÿþÿÿ     
+ 
+       
+ 
+   " # & ( + , / 1 2 5 7 : ; >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ£ÿŠÿ§ÿ©ÿ¬ÿ¯ÿ¯ÿ²ÿµÿ¶ÿžÿ»ÿŸÿŸÿÁÿÄÿÅÿÇÿÊÿËÿÍÿÐÿÓÿÓÿÖÿÙÿÚÿÜÿßÿâÿâÿåÿèÿéÿëÿîÿñÿñÿôÿ÷ÿøÿúÿýÿþ      	 
+ 
+       
+ 
+ ! " $ ' * * - 0 1 3 6 9 9 <ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ¥ÿ¥ÿšÿ«ÿ­ÿ®ÿ±ÿ³ÿŽÿ·ÿºÿŒÿœÿÀÿÂÿÃÿÆÿÉÿÉÿÌÿÏÿÑÿÒÿÕÿ×ÿØÿÛÿÞÿàÿáÿäÿæÿçÿêÿíÿïÿðÿóÿõÿöÿùÿüÿüÿÿ     
+ 
+        
+     # & ( ) , . / 2 5 7 8 ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ£ÿ€ÿ§ÿ©ÿ¬ÿ­ÿ¯ÿ²ÿ³ÿµÿžÿ»ÿŒÿŸÿÁÿÂÿÄÿÇÿÈÿËÿÍÿÐÿÑÿÓÿÖÿ×ÿÙÿÜÿßÿàÿâÿåÿæÿèÿëÿîÿïÿñÿôÿõÿ÷ÿúÿûÿþ      	 
+ 
+        
+  " $ ' ( * - . 0 3 6 7 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ¢ÿ¥ÿšÿ«ÿ«ÿ®ÿ±ÿ±ÿŽÿ·ÿ¹ÿºÿœÿÀÿÀÿÃÿÆÿÆÿÉÿÌÿÏÿÏÿÒÿÕÿÕÿØÿÛÿÝÿÞÿáÿäÿäÿçÿêÿìÿíÿðÿóÿóÿöÿùÿùÿüÿÿ      
+        
+ 
+   # & & ) , , / 2 4 5 8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ€ÿŠÿ©ÿªÿ­ÿ¯ÿ°ÿ³ÿµÿžÿ¹ÿ»ÿŸÿ¿ÿÂÿÄÿÅÿÈÿÊÿÍÿÎÿÑÿÓÿÔÿ×ÿÙÿÜÿÝÿßÿâÿãÿæÿèÿëÿìÿîÿñÿòÿõÿ÷ÿøÿûÿý       
+ 
+        
+  ! $ % ( * + . 0 3 4 6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿšÿšÿ«ÿ®ÿ¯ÿ±ÿŽÿ·ÿ·ÿºÿœÿœÿÀÿÃÿÄÿÆÿÉÿÌÿÌÿÏÿÒÿÓÿÕÿØÿÛÿÛÿÞÿáÿáÿäÿçÿêÿêÿíÿðÿðÿóÿöÿ÷ÿùÿüÿÿÿÿ     
+         
+   # # & ) * , / 2 2 5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿŠÿ§ÿªÿ¬ÿ­ÿ°ÿ³ÿµÿ¶ÿ¹ÿ»ÿŒÿ¿ÿÁÿÂÿÅÿÈÿÊÿËÿÎÿÐÿÑÿÔÿ×ÿÙÿÚÿÝÿßÿàÿãÿåÿèÿéÿìÿîÿïÿòÿôÿõÿøÿûÿýÿþ     
+ 
+ 
+       
+  ! " % ' ( + . 0 1 4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ¥ÿŠÿšÿ«ÿ¬ÿ®ÿ±ÿŽÿµÿ·ÿºÿ»ÿœÿÀÿÁÿÃÿÆÿÉÿÊÿÌÿÏÿÐÿÒÿÕÿØÿÙÿÛÿÞÿßÿáÿäÿçÿçÿêÿíÿîÿðÿóÿôÿöÿùÿüÿýÿÿ     
+ 
+        
+   ! # & ' ) , / 0 2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ€ÿ§ÿªÿªÿ­ÿ°ÿ²ÿ³ÿ¶ÿ¹ÿ¹ÿŒÿ¿ÿ¿ÿÂÿÅÿÇÿÈÿËÿÎÿÎÿÑÿÔÿÖÿ×ÿÚÿÝÿÝÿàÿãÿåÿæÿéÿëÿìÿïÿòÿòÿõÿøÿúÿûÿþ     	 
+ 
+       
+ 
+  " % % ( + - . 1ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿ¥ÿšÿ©ÿ¬ÿ®ÿ±ÿ²ÿŽÿ·ÿžÿ»ÿœÿŸÿÁÿÃÿÆÿÇÿÉÿÌÿÍÿÐÿÒÿÕÿÖÿØÿÛÿÜÿßÿáÿäÿåÿçÿêÿëÿíÿðÿñÿôÿöÿùÿúÿüÿÿ      	 
+        
+ 
+   # $ ' ) , - /ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿ§ÿ§ÿªÿ­ÿ°ÿ°ÿ³ÿ¶ÿ¶ÿ¹ÿŒÿœÿ¿ÿÂÿÅÿÅÿÈÿËÿËÿÎÿÑÿÔÿÔÿ×ÿÚÿÚÿÝÿàÿãÿãÿæÿéÿéÿìÿïÿïÿòÿõÿøÿøÿûÿþÿþ     
+ 
+ 
+      
+ 
+  " " % ( + + .ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿ©ÿ«ÿ®ÿ¯ÿ²ÿŽÿµÿžÿºÿ»ÿŸÿÁÿÃÿÄÿÇÿÉÿÊÿÍÿÏÿÒÿÓÿÖÿØÿÙÿÜÿÞÿáÿâÿåÿçÿèÿëÿíÿîÿñÿóÿöÿ÷ÿúÿüÿý      	 
+ 
+        
+   ! $ & ) * -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿ­ÿ­ÿ°ÿ³ÿŽÿ¶ÿ¹ÿºÿŒÿ¿ÿÂÿÃÿÅÿÈÿÉÿËÿÎÿÑÿÑÿÔÿ×ÿØÿÚÿÝÿàÿàÿãÿæÿçÿéÿìÿíÿïÿòÿõÿõÿøÿûÿüÿþ     
+ 
+ 
+       
+    " % ( ( +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ¯ÿ±ÿ²ÿµÿžÿžÿ»ÿŸÿÀÿÁÿÄÿÇÿÇÿÊÿÍÿÏÿÐÿÓÿÕÿÖÿÙÿÜÿÞÿßÿâÿäÿåÿèÿëÿëÿîÿñÿóÿôÿ÷ÿùÿúÿý       	 
+        
+ 
+ ! $ & ' *ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ±ÿ³ÿ¶ÿ·ÿºÿŒÿ¿ÿÀÿÂÿÅÿÆÿÉÿËÿÎÿÏÿÑÿÔÿÕÿ×ÿÚÿÝÿÞÿàÿãÿäÿæÿéÿêÿíÿïÿòÿóÿõÿøÿùÿûÿþ      
+ 
+       
+ 
+   " % & (ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²ÿµÿµÿžÿ»ÿŸÿŸÿÁÿÄÿÄÿÇÿÊÿÍÿÍÿÐÿÓÿÓÿÖÿÙÿÛÿÜÿßÿâÿâÿåÿèÿèÿëÿîÿñÿñÿôÿ÷ÿ÷ÿúÿýÿÿ      	 
+ 
+        
+ ! $ $ 'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ·ÿ¹ÿŒÿœÿÀÿÂÿÃÿÆÿÈÿËÿÌÿÏÿÑÿÒÿÕÿ×ÿÚÿÛÿÝÿàÿáÿäÿæÿçÿêÿìÿïÿðÿóÿõÿöÿùÿûÿþÿÿ     
+ 
+        
+  " # &ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿ»ÿ»ÿŸÿÁÿÂÿÄÿÇÿÊÿÊÿÍÿÐÿÑÿÓÿÖÿÙÿÙÿÜÿßÿßÿâÿåÿæÿèÿëÿîÿîÿñÿôÿõÿ÷ÿúÿýÿý      	 
+ 
+        
+ ! ! $ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿœÿ¿ÿÀÿÃÿÆÿÈÿÉÿÌÿÎÿÏÿÒÿÕÿ×ÿØÿÛÿÝÿÞÿáÿãÿäÿçÿêÿìÿíÿðÿòÿóÿöÿùÿûÿüÿÿ      
+        
+    #ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸÿ¿ÿÁÿÄÿÇÿÈÿÊÿÍÿÎÿÐÿÓÿÖÿ×ÿÙÿÜÿÝÿßÿâÿãÿåÿèÿëÿìÿîÿñÿòÿôÿ÷ÿúÿûÿý       	 
+        
+  !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÃÿÅÿÆÿÉÿÌÿÌÿÏÿÒÿÔÿÕÿØÿÛÿÛÿÞÿáÿáÿäÿçÿéÿêÿíÿðÿðÿóÿöÿøÿùÿüÿÿÿÿ     
+ 
+       
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÿÅÿÇÿÊÿËÿÎÿÐÿÓÿÔÿÖÿÙÿÚÿÝÿßÿàÿãÿåÿèÿéÿëÿîÿïÿòÿôÿ÷ÿøÿúÿýÿþ     	 
+ 
+       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÿÉÿÉÿÌÿÏÿÒÿÒÿÕÿØÿØÿÛÿÞÿßÿáÿäÿçÿçÿêÿíÿíÿðÿóÿöÿöÿùÿüÿüÿÿ     
+ 
+        
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÿËÿÍÿÐÿÑÿÔÿÖÿ×ÿÚÿÜÿÝÿàÿãÿåÿæÿéÿëÿìÿïÿñÿôÿõÿøÿúÿûÿþ      	 
+ 
+       
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÏÿÏÿÒÿÕÿÖÿØÿÛÿÜÿÞÿáÿäÿåÿçÿêÿëÿíÿðÿóÿóÿöÿùÿúÿüÿÿ      	 
+       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÿÑÿÓÿÔÿ×ÿÚÿÚÿÝÿàÿâÿãÿæÿéÿéÿìÿïÿñÿòÿõÿ÷ÿøÿûÿþÿþ     
+ 
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÿÓÿÕÿØÿÙÿÜÿÞÿáÿâÿäÿçÿèÿëÿíÿðÿñÿóÿöÿ÷ÿùÿüÿý       
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÿ×ÿ×ÿÚÿÝÿàÿàÿãÿæÿæÿéÿìÿïÿïÿòÿõÿõÿøÿûÿûÿþ     
+ 
+ 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÿÙÿÛÿÞÿßÿâÿäÿåÿèÿêÿíÿîÿñÿóÿôÿ÷ÿùÿúÿýÿÿ     	 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÿÝÿÝÿàÿãÿäÿæÿéÿìÿìÿïÿòÿóÿõÿøÿùÿûÿþ      
+ 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÿßÿáÿâÿåÿèÿêÿëÿîÿðÿñÿôÿ÷ÿ÷ÿúÿýÿÿ      	 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÿáÿãÿæÿéÿêÿìÿïÿðÿòÿõÿöÿùÿûÿþÿÿ     
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâÿåÿçÿèÿëÿîÿîÿñÿôÿôÿ÷ÿúÿýÿý      	 
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÿçÿéÿìÿíÿðÿòÿóÿöÿøÿûÿüÿÿ     
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿëÿëÿîÿñÿòÿôÿ÷ÿúÿúÿý      	 	 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêÿíÿïÿðÿóÿöÿøÿùÿüÿþÿÿ     
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîÿïÿñÿôÿ÷ÿøÿúÿýÿþ      	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿóÿõÿöÿùÿüÿüÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÿõÿ÷ÿúÿûÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöÿùÿùÿüÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿûÿý    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ   ÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ     ÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ       ÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ         ÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	ÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+ÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+  ÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+    ÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+      ÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+        ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+          ÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+            ÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+              ÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+ ÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    !ÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " #ÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ %ÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & 'ÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( )ÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * +ÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , -ÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . /ÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1ÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3ÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7ÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9ÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ;ÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < =           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿ¡ÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿ¥ÿ£ÿ€ÿ£ÿ¢ÿ ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿ§ÿ§ÿŠÿ¥ÿŠÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ«ÿ©ÿšÿ©ÿšÿŠÿ§ÿŠÿ€ÿ£ÿ€ÿ£ÿ¡ÿ¢ÿ¡ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿ­ÿ­ÿ¬ÿ«ÿªÿªÿ©ÿšÿšÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ£ÿ¢ÿ¡ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ±ÿ¯ÿ®ÿ¯ÿ®ÿ¬ÿ«ÿ¬ÿªÿ©ÿªÿ©ÿ§ÿŠÿ§ÿ¥ÿ€ÿ¥ÿ£ÿ¢ÿ£ÿ¢ÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ³ÿ±ÿ²ÿ±ÿ°ÿ°ÿ¯ÿ®ÿ¬ÿ­ÿ¬ÿ«ÿ«ÿªÿ©ÿ§ÿšÿ§ÿ¥ÿŠÿ¥ÿ€ÿ€ÿ£ÿ¢ÿ ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÿµÿµÿŽÿ³ÿŽÿ²ÿ±ÿ²ÿ°ÿ¯ÿ®ÿ¯ÿ­ÿ¬ÿ­ÿ«ÿªÿ©ÿ©ÿšÿ§ÿšÿŠÿ¥ÿŠÿ€ÿ£ÿ¢ÿ£ÿ¡ÿ ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿ¹ÿ·ÿ¶ÿ·ÿ¶ÿŽÿµÿŽÿ²ÿ³ÿ²ÿ±ÿ¯ÿ°ÿ¯ÿ­ÿ®ÿ­ÿ«ÿªÿ«ÿªÿšÿ©ÿšÿŠÿ§ÿŠÿ¥ÿ£ÿ€ÿ£ÿ¡ÿ¢ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÿ»ÿ»ÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿµÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ¯ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿªÿ©ÿšÿ©ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ£ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸÿ¿ÿœÿŒÿœÿŒÿºÿ¹ÿºÿžÿ·ÿžÿ·ÿµÿ¶ÿµÿ³ÿ²ÿ³ÿ±ÿ°ÿ±ÿ°ÿ®ÿ­ÿ®ÿ¬ÿ«ÿ¬ÿ«ÿ©ÿªÿ©ÿ§ÿŠÿ§ÿ¥ÿ€ÿ¥ÿ€ÿ¢ÿ¡ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÿÁÿ¿ÿÀÿ¿ÿŸÿŸÿœÿŒÿºÿ»ÿºÿ¹ÿ¹ÿžÿ·ÿ·ÿ¶ÿµÿ³ÿŽÿ³ÿ²ÿ²ÿ±ÿ°ÿ®ÿ¯ÿ®ÿ­ÿ­ÿ¬ÿ«ÿ«ÿªÿ©ÿ§ÿšÿ§ÿŠÿŠÿ¥ÿ€ÿ¢ÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÿÅÿÃÿÂÿÁÿÂÿÀÿ¿ÿÀÿŸÿœÿŒÿœÿ»ÿºÿ»ÿ¹ÿžÿ¹ÿ·ÿ¶ÿµÿ¶ÿŽÿ³ÿŽÿ²ÿ±ÿ°ÿ±ÿ¯ÿ®ÿ¯ÿ­ÿ¬ÿ­ÿ«ÿªÿ©ÿªÿšÿ§ÿšÿŠÿ¥ÿ€ÿ¥ÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÿÇÿÅÿÆÿÅÿÄÿÂÿÃÿÂÿÀÿÁÿÀÿ¿ÿœÿŸÿœÿ»ÿŒÿ»ÿ¹ÿºÿ¹ÿžÿ¶ÿ·ÿ¶ÿŽÿµÿŽÿ³ÿ±ÿ²ÿ±ÿ¯ÿ°ÿ¯ÿ­ÿ®ÿ­ÿ¬ÿªÿ«ÿªÿšÿ©ÿšÿ§ÿ¥ÿŠÿ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÿÉÿÉÿÈÿÇÿÈÿÆÿÅÿÄÿÄÿÃÿÂÿÃÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿŒÿºÿ¹ÿžÿžÿ·ÿ¶ÿ·ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ°ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ«ÿ©ÿšÿ§ÿ§ÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÿÍÿËÿÊÿËÿÊÿÈÿÉÿÈÿÆÿÅÿÆÿÅÿÃÿÄÿÃÿÁÿÀÿÁÿ¿ÿŸÿ¿ÿŸÿŒÿœÿŒÿºÿ¹ÿºÿ¹ÿ·ÿžÿ·ÿµÿŽÿµÿ³ÿ²ÿ³ÿ²ÿ°ÿ±ÿ°ÿ®ÿ­ÿ®ÿ­ÿ«ÿ¬ÿ«ÿ©ÿšÿ©ÿšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÿÏÿÏÿÎÿÍÿÌÿÌÿËÿÊÿÊÿÉÿÈÿÇÿÇÿÆÿÅÿÅÿÄÿÃÿÁÿÂÿÁÿÀÿÀÿ¿ÿŸÿŸÿœÿŒÿ»ÿ»ÿºÿ¹ÿ¹ÿžÿ·ÿµÿ¶ÿµÿŽÿŽÿ³ÿ²ÿ²ÿ±ÿ°ÿ¯ÿ¯ÿ®ÿ­ÿ­ÿ¬ÿ«ÿªÿªÿ©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÿÓÿÑÿÐÿÑÿÐÿÎÿÍÿÎÿÌÿËÿÌÿËÿÉÿÈÿÉÿÇÿÆÿÇÿÅÿÄÿÃÿÄÿÂÿÁÿÂÿÀÿ¿ÿÀÿ¿ÿœÿŒÿœÿ»ÿºÿ»ÿ¹ÿžÿ·ÿžÿ¶ÿµÿ¶ÿŽÿ³ÿŽÿ³ÿ±ÿ°ÿ±ÿ¯ÿ®ÿ¯ÿ®ÿ¬ÿ«ÿ¬ÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÿÕÿÓÿÔÿÓÿÒÿÒÿÑÿÐÿÎÿÏÿÎÿÍÿÍÿÌÿËÿÉÿÊÿÉÿÇÿÈÿÇÿÆÿÄÿÅÿÄÿÂÿÃÿÂÿÁÿÁÿÀÿ¿ÿœÿŸÿœÿ»ÿŒÿ»ÿºÿžÿ¹ÿžÿ¶ÿ·ÿ¶ÿµÿµÿŽÿ³ÿ±ÿ²ÿ±ÿ°ÿ°ÿ¯ÿ®ÿ¬ÿ­ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØÿ×ÿ×ÿÖÿÕÿÖÿÔÿÓÿÔÿÒÿÑÿÐÿÑÿÏÿÎÿÏÿÍÿÌÿËÿËÿÊÿÉÿÊÿÈÿÇÿÆÿÆÿÅÿÄÿÅÿÃÿÂÿÃÿÁÿÀÿ¿ÿ¿ÿŸÿœÿŸÿŒÿ»ÿºÿºÿ¹ÿžÿ¹ÿ·ÿ¶ÿ·ÿµÿŽÿ³ÿŽÿ²ÿ±ÿ²ÿ°ÿ¯ÿ®ÿ®ÿ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÿÛÿÙÿØÿÙÿØÿÖÿ×ÿÖÿÔÿÕÿÔÿÓÿÑÿÒÿÑÿÏÿÐÿÏÿÍÿÌÿÍÿÌÿÊÿËÿÊÿÈÿÇÿÈÿÇÿÅÿÆÿÅÿÃÿÄÿÃÿÁÿÀÿÁÿÀÿŸÿ¿ÿŸÿŒÿ»ÿŒÿ»ÿ¹ÿºÿ¹ÿ·ÿžÿ·ÿ¶ÿŽÿµÿŽÿ²ÿ³ÿ²ÿ°ÿ¯ÿ°ÿ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÿÝÿÝÿÜÿÛÿÚÿÚÿÙÿØÿØÿ×ÿÖÿ×ÿÕÿÔÿÓÿÓÿÒÿÑÿÑÿÐÿÏÿÎÿÎÿÍÿÌÿÌÿËÿÊÿÉÿÉÿÈÿÇÿÇÿÆÿÅÿÅÿÄÿÃÿÂÿÂÿÁÿÀÿÀÿ¿ÿŸÿœÿœÿŒÿ»ÿ»ÿºÿ¹ÿºÿžÿ·ÿ¶ÿ¶ÿµÿŽÿŽÿ³ÿ²ÿ±ÿ±ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÿáÿßÿÞÿßÿÞÿÜÿÛÿÜÿÚÿÙÿÚÿÙÿ×ÿØÿ×ÿÕÿÔÿÕÿÓÿÒÿÓÿÒÿÐÿÏÿÐÿÎÿÍÿÎÿÍÿËÿÊÿËÿÉÿÈÿÉÿÇÿÆÿÇÿÆÿÄÿÃÿÄÿÂÿÁÿÂÿÁÿ¿ÿŸÿ¿ÿœÿŒÿœÿŒÿºÿ»ÿºÿžÿ·ÿžÿ¶ÿµÿ¶ÿµÿ³ÿ²ÿ³ÿ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäÿãÿáÿâÿáÿàÿàÿßÿÞÿÜÿÝÿÜÿÛÿÛÿÚÿÙÿÙÿØÿ×ÿÕÿÖÿÕÿÔÿÔÿÓÿÒÿÐÿÑÿÐÿÏÿÏÿÎÿÍÿËÿÌÿËÿÉÿÊÿÉÿÈÿÈÿÇÿÆÿÄÿÅÿÄÿÃÿÃÿÂÿÁÿ¿ÿÀÿ¿ÿŸÿŸÿœÿŒÿŒÿ»ÿºÿžÿ¹ÿžÿ·ÿ·ÿ¶ÿµÿ³ÿŽÿ³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÿçÿåÿäÿãÿäÿâÿáÿâÿàÿßÿÞÿßÿÝÿÜÿÝÿÛÿÚÿÛÿÙÿØÿ×ÿØÿÖÿÕÿÖÿÔÿÓÿÒÿÓÿÑÿÐÿÑÿÏÿÎÿÍÿÍÿÌÿËÿÌÿÊÿÉÿÊÿÈÿÇÿÆÿÇÿÅÿÄÿÅÿÃÿÂÿÁÿÂÿÀÿ¿ÿÀÿŸÿœÿŸÿŒÿ»ÿºÿ»ÿ¹ÿžÿ¹ÿ·ÿ¶ÿµÿ¶ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêÿéÿçÿèÿçÿæÿäÿåÿäÿâÿãÿâÿáÿßÿàÿßÿÝÿÞÿÝÿÛÿÜÿÛÿÚÿØÿÙÿØÿÖÿ×ÿÖÿÕÿÓÿÔÿÓÿÑÿÒÿÑÿÏÿÎÿÏÿÎÿÌÿÍÿÌÿÊÿËÿÊÿÉÿÇÿÈÿÇÿÅÿÆÿÅÿÄÿÂÿÃÿÂÿÀÿÁÿÀÿŸÿ¿ÿŸÿœÿ»ÿŒÿ»ÿ¹ÿºÿ¹ÿžÿ¶ÿ·ÿ¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìÿëÿëÿêÿéÿêÿèÿçÿæÿæÿåÿäÿåÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÞÿÜÿÛÿÚÿÚÿÙÿØÿÙÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿÍÿËÿÊÿÉÿÉÿÈÿÇÿÈÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿÁÿ¿ÿŸÿœÿœÿŒÿ»ÿŒÿºÿ¹ÿžÿžÿ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîÿïÿíÿìÿíÿìÿêÿëÿêÿèÿçÿèÿçÿåÿæÿåÿãÿâÿãÿáÿàÿáÿàÿÞÿßÿÞÿÜÿÛÿÜÿÛÿÙÿÚÿÙÿ×ÿÖÿ×ÿÕÿÔÿÕÿÔÿÒÿÑÿÒÿÐÿÏÿÐÿÏÿÍÿÎÿÍÿËÿÊÿËÿÊÿÈÿÉÿÈÿÆÿÅÿÆÿÄÿÃÿÄÿÃÿÁÿÂÿÁÿ¿ÿŸÿ¿ÿŸÿŒÿœÿŒÿºÿ¹ÿºÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÿñÿïÿðÿïÿîÿîÿíÿìÿìÿëÿêÿéÿéÿèÿçÿçÿæÿåÿãÿäÿãÿâÿâÿáÿàÿàÿßÿÞÿÝÿÝÿÜÿÛÿÛÿÚÿÙÿ×ÿØÿ×ÿÖÿÖÿÕÿÔÿÒÿÓÿÒÿÑÿÑÿÐÿÏÿÏÿÎÿÍÿÌÿÌÿËÿÊÿÊÿÉÿÈÿÆÿÇÿÆÿÅÿÅÿÄÿÃÿÃÿÂÿÁÿÀÿÀÿ¿ÿŸÿŸÿœÿŒÿºÿ»ÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÿõÿóÿòÿñÿòÿðÿïÿðÿîÿíÿîÿíÿëÿêÿëÿéÿèÿéÿçÿæÿåÿæÿäÿãÿäÿâÿáÿâÿáÿßÿÞÿßÿÝÿÜÿÝÿÛÿÚÿÙÿÚÿØÿ×ÿØÿÖÿÕÿÔÿÕÿÓÿÒÿÓÿÑÿÐÿÑÿÐÿÎÿÍÿÎÿÌÿËÿÌÿÊÿÉÿÈÿÉÿÇÿÆÿÇÿÅÿÄÿÅÿÄÿÂÿÁÿÂÿÀÿ¿ÿÀÿŸÿœÿŒÿœÿ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿ÷ÿõÿöÿõÿôÿòÿóÿòÿðÿñÿðÿïÿïÿîÿíÿëÿìÿëÿéÿêÿéÿèÿæÿçÿæÿäÿåÿäÿãÿãÿâÿáÿßÿàÿßÿÝÿÞÿÝÿÜÿÚÿÛÿÚÿØÿÙÿØÿ×ÿÕÿÖÿÕÿÓÿÔÿÓÿÒÿÒÿÑÿÐÿÎÿÏÿÎÿÌÿÍÿÌÿËÿÉÿÊÿÉÿÇÿÈÿÇÿÆÿÆÿÅÿÄÿÂÿÃÿÂÿÀÿÁÿÀÿ¿ÿœÿŸÿœÿÿÿÿÿÿÿÿÿÿÿÿÿúÿùÿùÿøÿ÷ÿøÿöÿõÿôÿôÿóÿòÿóÿñÿðÿñÿïÿîÿíÿíÿìÿëÿìÿêÿéÿèÿèÿçÿæÿçÿåÿäÿåÿãÿâÿáÿáÿàÿßÿàÿÞÿÝÿÜÿÜÿÛÿÚÿÛÿÙÿØÿ×ÿ×ÿÖÿÕÿÖÿÔÿÓÿÔÿÒÿÑÿÐÿÐÿÏÿÎÿÏÿÍÿÌÿËÿËÿÊÿÉÿÊÿÈÿÇÿÈÿÆÿÅÿÄÿÄÿÃÿÂÿÃÿÁÿÀÿ¿ÿ¿ÿŸÿÿÿÿÿÿÿÿÿþÿýÿûÿúÿûÿúÿøÿùÿøÿöÿõÿöÿõÿóÿôÿóÿñÿòÿñÿïÿîÿïÿîÿìÿíÿìÿêÿéÿêÿéÿçÿèÿçÿåÿæÿåÿãÿâÿãÿâÿàÿáÿàÿÞÿÝÿÞÿÝÿÛÿÜÿÛÿÙÿØÿÙÿØÿÖÿ×ÿÖÿÔÿÕÿÔÿÒÿÑÿÒÿÑÿÏÿÐÿÏÿÍÿÌÿÍÿÌÿÊÿËÿÊÿÈÿÉÿÈÿÆÿÅÿÆÿÅÿÃÿÄÿÃÿÁÿÀÿÁÿÀÿÿÿÿ  ÿÿÿÿÿþÿýÿüÿüÿûÿúÿúÿùÿøÿ÷ÿ÷ÿöÿõÿõÿôÿóÿóÿòÿñÿðÿðÿïÿîÿîÿíÿìÿëÿëÿêÿéÿéÿèÿçÿçÿæÿåÿäÿäÿãÿâÿâÿáÿàÿßÿßÿÞÿÝÿÝÿÜÿÛÿÚÿÚÿÙÿØÿØÿ×ÿÖÿÖÿÕÿÔÿÓÿÓÿÒÿÑÿÑÿÐÿÏÿÎÿÎÿÍÿÌÿÌÿËÿÊÿÊÿÉÿÈÿÇÿÇÿÆÿÅÿÅÿÄÿÃÿÂÿÂÿÁÿÿÿÿ      ÿþÿýÿþÿüÿûÿüÿûÿùÿøÿùÿ÷ÿöÿ÷ÿõÿôÿõÿôÿòÿñÿòÿðÿïÿðÿïÿíÿìÿíÿëÿêÿëÿéÿèÿéÿèÿæÿåÿæÿäÿãÿäÿãÿáÿàÿáÿßÿÞÿßÿÞÿÜÿÛÿÜÿÚÿÙÿÚÿØÿ×ÿØÿ×ÿÕÿÔÿÕÿÓÿÒÿÓÿÒÿÐÿÏÿÐÿÎÿÍÿÎÿÌÿËÿÌÿËÿÉÿÈÿÉÿÇÿÆÿÇÿÆÿÄÿÃÿÿÿÿÿÿÿÿ      ÿþÿÿÿþÿýÿýÿüÿûÿùÿúÿùÿ÷ÿøÿ÷ÿöÿöÿõÿôÿòÿóÿòÿñÿñÿðÿïÿíÿîÿíÿëÿìÿëÿêÿêÿéÿèÿæÿçÿæÿåÿåÿäÿãÿáÿâÿáÿàÿàÿßÿÞÿÜÿÝÿÜÿÚÿÛÿÚÿÙÿÙÿØÿ×ÿÕÿÖÿÕÿÔÿÔÿÓÿÒÿÐÿÑÿÐÿÎÿÏÿÎÿÍÿÍÿÌÿËÿÉÿÊÿÉÿÈÿÈÿÇÿÿÿÿÿÿÿÿÿÿÿÿ        ÿÿÿþÿÿÿýÿüÿûÿûÿúÿùÿúÿøÿ÷ÿøÿöÿõÿôÿõÿóÿòÿóÿñÿðÿïÿïÿîÿíÿîÿìÿëÿìÿêÿéÿèÿéÿçÿæÿçÿåÿäÿãÿäÿâÿáÿâÿàÿßÿÞÿÞÿÝÿÜÿÝÿÛÿÚÿÛÿÙÿØÿ×ÿØÿÖÿÕÿÖÿÔÿÓÿÒÿÒÿÑÿÐÿÑÿÏÿÎÿÏÿÍÿÌÿËÿÌÿÊÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        ÿÿ  ÿÿÿýÿüÿýÿüÿúÿûÿúÿøÿùÿøÿ÷ÿõÿöÿõÿóÿôÿóÿñÿðÿñÿðÿîÿïÿîÿìÿíÿìÿëÿéÿêÿéÿçÿèÿçÿæÿäÿåÿäÿâÿãÿâÿàÿßÿàÿßÿÝÿÞÿÝÿÛÿÜÿÛÿÚÿØÿÙÿØÿÖÿ×ÿÖÿÔÿÓÿÔÿÓÿÑÿÒÿÑÿÏÿÐÿÏÿÎÿÌÿÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ            ÿÿÿþÿþÿýÿüÿüÿûÿúÿûÿùÿøÿ÷ÿ÷ÿöÿõÿõÿôÿóÿòÿòÿñÿðÿðÿïÿîÿïÿíÿìÿëÿëÿêÿéÿêÿèÿçÿæÿæÿåÿäÿäÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÞÿÜÿÛÿÚÿÚÿÙÿØÿØÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÒÿÐÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	            ÿÿ  ÿþÿýÿþÿýÿûÿüÿûÿùÿøÿùÿ÷ÿöÿ÷ÿöÿôÿóÿôÿòÿñÿòÿñÿïÿðÿïÿíÿìÿíÿìÿêÿëÿêÿèÿçÿèÿæÿåÿæÿåÿãÿâÿãÿáÿàÿáÿàÿÞÿßÿÞÿÜÿÛÿÜÿÚÿÙÿÚÿÙÿ×ÿÖÿ×ÿÕÿÔÿÕÿÔÿÒÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 	 	              ÿÿÿÿÿþÿýÿýÿüÿûÿùÿúÿùÿøÿøÿ÷ÿöÿôÿõÿôÿóÿóÿòÿñÿñÿðÿïÿîÿîÿíÿìÿìÿëÿêÿèÿéÿèÿçÿçÿæÿåÿãÿäÿãÿâÿâÿáÿàÿàÿßÿÞÿÜÿÝÿÜÿÛÿÛÿÚÿÙÿ×ÿØÿ×ÿÖÿÖÿÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 	              ÿÿÿþÿÿÿýÿüÿûÿüÿúÿùÿúÿøÿ÷ÿöÿ÷ÿõÿôÿõÿóÿòÿóÿòÿðÿïÿðÿîÿíÿîÿìÿëÿêÿëÿéÿèÿéÿçÿæÿåÿæÿäÿãÿäÿâÿáÿâÿàÿßÿÞÿßÿÝÿÜÿÝÿÛÿÚÿÙÿÚÿØÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 
+ 
+  	           ÿÿ  ÿÿÿþÿüÿýÿüÿúÿûÿúÿùÿ÷ÿøÿ÷ÿõÿöÿõÿôÿôÿóÿòÿðÿñÿðÿîÿïÿîÿíÿëÿìÿëÿéÿêÿéÿèÿæÿçÿæÿäÿåÿäÿâÿãÿâÿáÿßÿàÿßÿÝÿÞÿÝÿÜÿÚÿÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+  
+ 
+ 
+ 
+ 	  	            ÿÿÿþÿþÿýÿüÿýÿûÿúÿùÿùÿøÿ÷ÿøÿöÿõÿöÿôÿóÿòÿòÿñÿðÿñÿïÿîÿíÿíÿìÿëÿìÿêÿéÿèÿèÿçÿæÿæÿåÿäÿåÿãÿâÿáÿáÿàÿßÿàÿÞÿÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 
+ 
+ 
+ 	 
+ 	            ÿÿ  ÿÿÿýÿþÿýÿûÿúÿûÿúÿøÿùÿøÿöÿ÷ÿöÿôÿóÿôÿóÿñÿòÿñÿïÿîÿïÿîÿìÿíÿìÿêÿéÿêÿèÿçÿèÿçÿåÿæÿåÿãÿâÿãÿâÿàÿáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+ 
+ 
+ 
+ 
+ 	              ÿÿÿÿÿþÿýÿüÿüÿûÿúÿúÿùÿøÿøÿ÷ÿöÿõÿõÿôÿóÿóÿòÿñÿðÿðÿïÿîÿîÿíÿìÿêÿëÿêÿéÿéÿèÿçÿçÿæÿåÿäÿäÿãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        
+ 
+ 
+ 
+ 
+ 	 
+                ÿþÿýÿþÿüÿûÿüÿúÿùÿúÿùÿ÷ÿöÿ÷ÿõÿôÿõÿôÿòÿñÿòÿðÿïÿðÿîÿíÿìÿíÿëÿêÿëÿéÿèÿéÿèÿæÿåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ         
+  
+ 
+ 
+ 
+ 
+  	             ÿþÿÿÿþÿüÿýÿüÿûÿûÿúÿùÿ÷ÿøÿ÷ÿöÿöÿõÿôÿòÿóÿòÿðÿñÿðÿïÿíÿîÿíÿëÿìÿëÿêÿêÿéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ            
+ 
+ 
+ 
+ 
+ 
+ 	  	             ÿÿÿþÿÿÿýÿüÿýÿûÿúÿùÿúÿøÿ÷ÿøÿöÿõÿôÿôÿóÿòÿóÿñÿðÿïÿïÿîÿíÿîÿìÿëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ             
+  
+ 
+ 
+ 
+ 	 
+ 	           ÿÿ  ÿÿÿýÿþÿýÿüÿúÿûÿúÿøÿùÿøÿöÿõÿöÿõÿóÿôÿóÿñÿðÿñÿðÿîÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                
+ 
+ 
+ 
+ 
+ 
+ 	              ÿÿ  ÿþÿýÿüÿüÿûÿúÿúÿùÿøÿ÷ÿ÷ÿöÿõÿõÿôÿóÿòÿòÿñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                   
+ 
+ 
+ 
+ 	 
+                ÿþÿýÿþÿüÿûÿüÿûÿùÿøÿùÿ÷ÿöÿ÷ÿöÿôÿóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                     
+ 
+ 
+ 
+ 
+ 	 	             ÿþÿÿÿþÿýÿýÿüÿûÿùÿúÿùÿøÿøÿ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+  
+                   
+ 
+ 
+ 
+ 
+ 
+ 	              ÿÿÿþÿÿÿýÿüÿûÿüÿúÿùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 
+                   
+  
+ 
+ 
+ 
+ 
+  	           ÿÿ  ÿÿÿþÿüÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+  
+ 
+                     
+  
+ 
+ 
+ 
+ 	              ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ !     
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 	 
+ 	         ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ " ! !    
+ 
+ 
+ 
+ 
+                    
+ 
+ 
+ 
+ 
+ 
+ 	 	     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ # " # "      
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % $ $ # "   !   
+  
+ 
+  
+                   
+  
+ 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ & % & $ # " " !   !  
+ 
+ 
+ 
+  
+                   
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ( & ' & $ # $ # ! " !  
+  
+ 
+ 
+ 
+                ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) ( ( ' & % % $ # # " !      
+ 
+ 
+ 
+ 
+            ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ * ) * ) ' & ' % $ % $ " ! "      
+ 
+ 
+ 
+         ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ , + + * ) ' ( ' & & % $ " # "   !     
+ 
+  
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - , - + * ) * ( ' ( & % $ $ # " # !   !  
+ 
+ 
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ / - . - , * + * ( ) ( & % & % # $ # ! " !   
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 0 / 0 . - , , + * * ) ( ' ' & % % $ # $ " !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2 0 1 0 . - . , + , + ) ( ) ' & ' & $ %ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3 2 2 1 0 . / . - - , + ) * ) ( ( 'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 4 3 4 2 1 0 1 / . / - , + , * )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 6 4 5 4 3 1 2 1 / 0 / . , -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 7 6 7 5 4 3 3 2 1 2 0 /ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9 7 8 7 5 4 5 4 2 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ : 9 9 8 7 6 6 5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ; : ; : 8 7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ = < < ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ > =ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ   ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+    ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿ    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿ      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿ 
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿ 
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿ  
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿ  
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿ   
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿ   
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿ    
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿ    
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿ     
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿ     
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿ      
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿ      
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿ       
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿ       
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿ        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿ        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿ 
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿ 
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ   
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠ % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿš ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ© ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿª ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ« * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ® - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ° / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ± 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ² 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽ 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµ 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ· 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿž 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿº 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ» : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒ ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœ < : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸ = ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ > < : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀ ? = ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁ                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿýÿûÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿÿÿüÿùÿùÿöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿþÿûÿúÿ÷ÿõÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿÿÿüÿüÿùÿöÿõÿóÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	     ÿþÿýÿúÿøÿ÷ÿôÿñÿïÿîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+    ÿÿÿþÿüÿùÿøÿöÿóÿðÿïÿíÿêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 	 	     ÿýÿúÿúÿ÷ÿôÿòÿñÿîÿëÿëÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+    ÿÿÿüÿûÿøÿöÿóÿòÿðÿíÿìÿéÿçÿæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+ 	     ÿýÿýÿúÿ÷ÿôÿôÿñÿîÿîÿëÿèÿçÿåÿâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+    ÿÿÿþÿûÿùÿöÿõÿòÿðÿïÿìÿêÿéÿæÿãÿáÿàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 	     ÿÿÿýÿúÿ÷ÿ÷ÿôÿñÿðÿîÿëÿêÿèÿåÿâÿáÿßÿÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 
+     ÿþÿûÿùÿøÿõÿóÿòÿïÿìÿìÿéÿæÿäÿãÿàÿÝÿÝÿÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 	    ÿÿÿýÿúÿùÿ÷ÿôÿóÿñÿîÿíÿêÿèÿåÿäÿâÿßÿÞÿÛÿÙÿÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 
+ 
+    ÿþÿûÿûÿøÿõÿõÿòÿïÿïÿìÿéÿæÿæÿãÿàÿàÿÝÿÚÿ×ÿ×ÿÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+      ÿýÿüÿùÿ÷ÿöÿóÿñÿðÿíÿëÿèÿçÿäÿâÿáÿÞÿÜÿÙÿØÿÕÿÓÿÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+ 
+    ÿþÿþÿûÿøÿ÷ÿõÿòÿñÿïÿìÿéÿéÿæÿãÿâÿàÿÝÿÚÿÚÿ×ÿÔÿÓÿÑÿÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        
+ 	     ÿÿÿüÿúÿùÿöÿóÿóÿðÿíÿëÿêÿçÿåÿäÿáÿÞÿÜÿÛÿØÿÖÿÕÿÒÿÏÿÏÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+       
+ 
+ 	     ÿþÿûÿúÿøÿõÿôÿñÿïÿìÿëÿéÿæÿåÿãÿàÿÝÿÜÿÚÿ×ÿÖÿÔÿÑÿÐÿÍÿËÿÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+        
+ 
+    ÿÿÿüÿüÿùÿöÿöÿóÿðÿíÿíÿêÿçÿçÿäÿáÿßÿÞÿÛÿØÿØÿÕÿÒÿÒÿÏÿÌÿÉÿÉÿÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+       
+ 
+ 	    ÿþÿýÿúÿøÿ÷ÿôÿòÿïÿîÿëÿéÿèÿåÿãÿàÿßÿÝÿÚÿÙÿÖÿÔÿÓÿÐÿÎÿËÿÊÿÇÿÅÿÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+       
+ 
+    ÿÿÿÿÿüÿùÿøÿöÿóÿðÿðÿíÿêÿéÿçÿäÿáÿáÿÞÿÛÿÛÿØÿÕÿÔÿÒÿÏÿÌÿÌÿÉÿÆÿÅÿÃÿÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ !  
+        
+ 	      ÿýÿûÿúÿ÷ÿôÿòÿñÿîÿìÿëÿèÿåÿãÿâÿßÿÝÿÜÿÙÿ×ÿÖÿÓÿÐÿÎÿÍÿÊÿÈÿÇÿÄÿÁÿ¿ÿŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ #    
+        
+     ÿÿÿüÿûÿùÿöÿóÿòÿðÿíÿìÿêÿçÿäÿãÿáÿÞÿÝÿÛÿØÿ×ÿÕÿÒÿÏÿÎÿÌÿÉÿÈÿÆÿÃÿÀÿ¿ÿœÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ $ ! ! 
+        
+ 
+ 	     ÿýÿýÿúÿ÷ÿõÿôÿñÿîÿîÿëÿèÿæÿåÿâÿßÿßÿÜÿÙÿÙÿÖÿÓÿÑÿÐÿÍÿÊÿÊÿÇÿÄÿÂÿÁÿŸÿ»ÿ»ÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ & # "  
+        
+ 
+    ÿÿÿþÿûÿùÿöÿõÿóÿðÿïÿìÿêÿçÿæÿäÿáÿàÿÝÿÛÿÚÿ×ÿÕÿÒÿÑÿÏÿÌÿËÿÈÿÆÿÃÿÂÿÀÿœÿŒÿ¹ÿ·ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ' $ $ ! 
+        
+ 
+ 	     ÿÿÿýÿúÿ÷ÿ÷ÿôÿñÿñÿîÿëÿèÿèÿåÿâÿâÿßÿÜÿÛÿÙÿÖÿÓÿÓÿÐÿÍÿÍÿÊÿÇÿÄÿÄÿÁÿŸÿŸÿ»ÿžÿµÿµÿ²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ( & % "   
+ 
+       
+ 
+     ÿþÿûÿùÿøÿõÿóÿòÿïÿíÿêÿéÿæÿäÿãÿàÿÞÿÝÿÚÿ×ÿÕÿÔÿÑÿÏÿÎÿËÿÉÿÆÿÅÿÂÿÀÿ¿ÿŒÿºÿ·ÿ¶ÿ³ÿ±ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ * ' & $ ! 
+ 
+        
+ 	      ÿýÿúÿùÿ÷ÿôÿóÿñÿîÿëÿëÿèÿåÿäÿâÿßÿÞÿÜÿÙÿÖÿÕÿÓÿÐÿÏÿÍÿÊÿÇÿÇÿÄÿÁÿÀÿŸÿ»ÿžÿžÿµÿ²ÿ±ÿ¯ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ + ( ( % "    
+       
+ 
+ 
+    ÿþÿüÿûÿøÿõÿõÿòÿïÿíÿìÿéÿçÿæÿãÿàÿàÿÝÿÚÿØÿ×ÿÔÿÑÿÑÿÎÿËÿÉÿÈÿÅÿÃÿÂÿ¿ÿŒÿºÿ¹ÿ¶ÿŽÿ³ÿ°ÿ­ÿ­ÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - * ) & $ !   
+        
+ 
+ 	     ÿýÿüÿúÿ÷ÿöÿóÿñÿîÿíÿëÿèÿçÿåÿâÿáÿÞÿÜÿÙÿØÿÖÿÓÿÒÿÏÿÍÿÊÿÉÿÇÿÄÿÃÿÁÿŸÿ»ÿºÿžÿµÿŽÿ²ÿ¯ÿ®ÿ«ÿ©ÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ . + + ( % " "  
+ 
+      
+ 
+ 
+    ÿþÿþÿûÿøÿøÿõÿòÿïÿïÿìÿéÿéÿæÿãÿãÿàÿÝÿÚÿÚÿ×ÿÔÿÔÿÑÿÎÿËÿËÿÈÿÅÿÅÿÂÿ¿ÿœÿŒÿ¹ÿ¶ÿ¶ÿ³ÿ°ÿ°ÿ­ÿªÿ§ÿ§ÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ / - , ) ' $ #   
+ 
+        
+ 	     ÿÿÿüÿúÿùÿöÿôÿñÿðÿíÿëÿêÿçÿåÿäÿáÿßÿÜÿÛÿØÿÖÿÕÿÒÿÐÿÍÿÌÿÉÿÇÿÆÿÃÿÁÿŸÿœÿ»ÿžÿ·ÿŽÿ²ÿ±ÿ®ÿ¬ÿ©ÿšÿ¥ÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 1 . - + ( % % "  
+ 
+       
+ 
+ 	    ÿþÿûÿúÿøÿõÿòÿòÿïÿìÿëÿéÿæÿåÿãÿàÿÝÿÝÿÚÿ×ÿÖÿÔÿÑÿÎÿÎÿËÿÈÿÇÿÅÿÂÿ¿ÿ¿ÿŒÿ¹ÿ¹ÿ¶ÿ³ÿ²ÿ°ÿ­ÿªÿªÿ§ÿ€ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2 0 / , ) ' & # !   
+        
+ 
+    ÿÿÿýÿüÿùÿöÿôÿóÿðÿîÿíÿêÿçÿçÿäÿáÿßÿÞÿÛÿÙÿØÿÕÿÒÿÐÿÏÿÌÿÊÿÉÿÆÿÃÿÁÿÀÿœÿ»ÿºÿ·ÿµÿŽÿ±ÿ®ÿ¬ÿ«ÿšÿŠÿ¥ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 4 1 0 . + ( ' % " !  
+       
+ 
+ 
+    ÿþÿýÿûÿøÿõÿôÿòÿïÿîÿìÿéÿèÿåÿãÿàÿßÿÝÿÚÿÙÿ×ÿÔÿÑÿÐÿÎÿËÿÊÿÈÿÅÿÂÿÁÿ¿ÿŒÿ»ÿ¹ÿ¶ÿµÿ³ÿ°ÿ­ÿ¬ÿªÿ§ÿŠÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 2 2 / , * ) & # #   
+         
+    ÿÿÿÿÿüÿùÿ÷ÿöÿóÿðÿðÿíÿêÿêÿçÿäÿáÿáÿÞÿÛÿÛÿØÿÕÿÓÿÒÿÏÿÌÿÌÿÉÿÆÿÄÿÃÿÀÿœÿœÿºÿ·ÿ·ÿŽÿ±ÿ¯ÿ®ÿ«ÿšÿšÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 6 4 3 0 . + * ( % $ !  
+        
+ 
+      ÿýÿûÿøÿ÷ÿõÿòÿñÿîÿìÿëÿèÿæÿãÿâÿßÿÝÿÜÿÙÿ×ÿÔÿÓÿÑÿÎÿÍÿÊÿÈÿÅÿÄÿÂÿ¿ÿŸÿ»ÿ¹ÿžÿµÿ³ÿ°ÿ¯ÿ­ÿªÿ©ÿŠÿ€ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 8 5 4 2 / , , ) & & #   
+ 
+        
+     ÿÿÿüÿùÿùÿöÿóÿóÿðÿíÿìÿêÿçÿäÿäÿáÿÞÿÝÿÛÿØÿÕÿÕÿÒÿÏÿÏÿÌÿÉÿÆÿÆÿÃÿÀÿÀÿœÿºÿ¹ÿ·ÿŽÿ±ÿ±ÿ®ÿ«ÿ«ÿšÿ¥ÿ¢ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9 7 6 3 0 . - * ( ' $ "  
+        
+ 
+ 	     ÿþÿûÿúÿ÷ÿõÿôÿñÿïÿîÿëÿèÿæÿåÿâÿàÿßÿÜÿÙÿ×ÿÖÿÓÿÑÿÐÿÍÿËÿÈÿÇÿÄÿÂÿÁÿŸÿŒÿ»ÿžÿµÿ³ÿ²ÿ¯ÿ­ÿ¬ÿ©ÿ§ÿ€ÿ£ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ; 8 7 5 2 / . , ) ( & #     
+        
+ 
+    ÿÿÿüÿüÿùÿöÿõÿóÿðÿïÿíÿêÿçÿæÿäÿáÿàÿÞÿÛÿØÿ×ÿÕÿÒÿÑÿÏÿÌÿÉÿÉÿÆÿÃÿÂÿÀÿœÿŒÿºÿ·ÿŽÿ³ÿ±ÿ®ÿ­ÿ«ÿšÿ¥ÿ¥ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ < 9 9 6 3 1 0 - * * ' $ " ! 
+ 
+       
+ 
+ 	     ÿþÿýÿúÿøÿ÷ÿôÿñÿñÿîÿëÿéÿèÿåÿâÿâÿßÿÜÿÚÿÙÿÖÿÓÿÓÿÐÿÍÿËÿÊÿÇÿÅÿÄÿÁÿŸÿŸÿ»ÿžÿ¶ÿµÿ²ÿ¯ÿ¯ÿ¬ÿ©ÿ§ÿŠÿ£ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ > ; : 7 5 2 1 / , + ( & # "   
+ 
+       
+ 
+    ÿÿÿþÿüÿùÿøÿöÿóÿòÿïÿíÿêÿéÿçÿäÿãÿàÿÞÿÛÿÚÿØÿÕÿÔÿÑÿÏÿÌÿËÿÉÿÆÿÅÿÃÿÀÿ¿ÿŒÿºÿ·ÿ¶ÿŽÿ±ÿ°ÿ­ÿ«ÿšÿ§ÿ¥ÿ¢ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ? < < 9 6 3 3 0 - - * ' $ $ ! 
+ 
+        
+ 	 	      ÿýÿúÿúÿ÷ÿôÿôÿñÿîÿëÿëÿèÿåÿåÿâÿßÿÜÿÜÿÙÿÖÿÖÿÓÿÐÿÍÿÍÿÊÿÇÿÇÿÄÿÁÿÁÿŸÿ»ÿžÿžÿµÿ²ÿ²ÿ¯ÿ¬ÿ©ÿ©ÿŠÿ£ÿ£ÿ ÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿ = : 8 5 4 1 / . + ) & % "    
+       
+ 
+ 
+    ÿþÿüÿûÿøÿöÿõÿòÿðÿíÿìÿéÿçÿæÿãÿáÿÞÿÝÿÚÿØÿ×ÿÔÿÒÿÏÿÎÿËÿÉÿÈÿÅÿÃÿÂÿ¿ÿœÿºÿ¹ÿ¶ÿŽÿ³ÿ°ÿ®ÿ«ÿªÿ§ÿ¥ÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9 6 6 3 0 / - * ' ' $ !   
+        
+ 
+ 	     ÿýÿüÿúÿ÷ÿöÿôÿñÿîÿîÿëÿèÿçÿåÿâÿßÿßÿÜÿÙÿØÿÖÿÓÿÐÿÐÿÍÿÊÿÉÿÇÿÄÿÃÿÁÿŸÿ»ÿ»ÿžÿµÿŽÿ²ÿ¯ÿ¬ÿ¬ÿ©ÿŠÿ¥ÿ£ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿ 7 4 2 1 . + ) ( % # "  
+ 
+       
+ 
+    ÿÿÿþÿûÿøÿøÿõÿòÿðÿïÿìÿêÿéÿæÿãÿáÿàÿÝÿÛÿÚÿ×ÿÔÿÒÿÑÿÎÿÌÿËÿÈÿÅÿÅÿÂÿ¿ÿœÿŒÿ¹ÿ·ÿ¶ÿ³ÿ°ÿ®ÿ­ÿªÿšÿ§ÿ€ÿ¡ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3 2 0 - * ) ' $ # ! 
+ 
+        
+ 	     ÿÿÿýÿúÿùÿöÿôÿñÿðÿîÿëÿêÿèÿåÿâÿáÿßÿÜÿÛÿÙÿÖÿÓÿÒÿÐÿÍÿÌÿÊÿÇÿÆÿÃÿÁÿŸÿœÿ»ÿžÿ·ÿµÿ²ÿ¯ÿ®ÿ¬ÿ©ÿšÿŠÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 1 . , + ( % % "   
+       
+ 
+     ÿþÿûÿûÿøÿõÿòÿòÿïÿìÿìÿéÿæÿäÿãÿàÿÝÿÝÿÚÿ×ÿÕÿÔÿÑÿÎÿÎÿËÿÈÿÈÿÅÿÂÿ¿ÿ¿ÿŒÿ¹ÿ¹ÿ¶ÿ³ÿ±ÿ°ÿ­ÿªÿªÿ§ÿ€ÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - , * ' & # !   
+        
+ 	    ÿÿÿýÿüÿùÿ÷ÿôÿóÿðÿîÿíÿêÿèÿåÿäÿâÿßÿÞÿÛÿÙÿÖÿÕÿÓÿÐÿÏÿÌÿÊÿÉÿÆÿÄÿÁÿÀÿœÿ»ÿºÿ·ÿµÿ²ÿ±ÿ¯ÿ¬ÿ«ÿšÿŠÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ + ( ( % " !  
+       
+ 
+ 
+    ÿþÿýÿûÿøÿõÿõÿòÿïÿîÿìÿéÿæÿæÿãÿàÿàÿÝÿÚÿ×ÿ×ÿÔÿÑÿÑÿÎÿËÿÊÿÈÿÅÿÂÿÂÿ¿ÿŒÿ»ÿ¹ÿ¶ÿ³ÿ³ÿ°ÿ­ÿ­ÿªÿ§ÿŠÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) & $ #   
+        
+ 
+      ÿÿÿüÿùÿ÷ÿöÿóÿñÿðÿíÿêÿèÿçÿäÿâÿáÿÞÿÜÿÙÿØÿÕÿÓÿÒÿÏÿÍÿÌÿÉÿÆÿÄÿÃÿÀÿŸÿœÿºÿ·ÿµÿŽÿ±ÿ¯ÿ®ÿ«ÿ©ÿšÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % $ "  
+       
+ 
+ 
+      ÿþÿûÿøÿ÷ÿõÿòÿñÿïÿìÿéÿèÿæÿãÿâÿàÿÝÿÚÿÚÿ×ÿÔÿÓÿÑÿÎÿÍÿËÿÈÿÅÿÄÿÂÿ¿ÿŸÿŒÿ¹ÿ¶ÿµÿ³ÿ°ÿ¯ÿ­ÿªÿ©ÿ§ÿ€ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ #   
+ 
+        
+     ÿÿÿüÿúÿùÿöÿóÿóÿðÿíÿëÿêÿçÿäÿäÿáÿÞÿÜÿÛÿØÿÖÿÕÿÒÿÏÿÏÿÌÿÉÿÇÿÆÿÃÿÀÿÀÿœÿºÿžÿ·ÿŽÿ±ÿ±ÿ®ÿ«ÿ«ÿšÿ¥ÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+       
+ 
+ 	     ÿþÿûÿúÿøÿõÿôÿñÿïÿìÿëÿéÿæÿåÿâÿàÿÝÿÜÿÚÿ×ÿÖÿÔÿÑÿÐÿÍÿËÿÈÿÇÿÅÿÂÿÁÿŸÿŒÿ¹ÿžÿ¶ÿ³ÿ²ÿ¯ÿ­ÿ¬ÿ©ÿ§ÿ€ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+        
+ 
+    ÿÿÿüÿüÿùÿöÿöÿóÿðÿíÿíÿêÿçÿçÿäÿáÿÞÿÞÿÛÿØÿØÿÕÿÒÿÒÿÏÿÌÿÉÿÉÿÆÿÃÿÃÿÀÿœÿºÿºÿ·ÿŽÿŽÿ±ÿ®ÿ­ÿ«ÿšÿ¥ÿ¥ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       
+ 
+ 	     ÿþÿýÿúÿøÿ÷ÿôÿòÿïÿîÿëÿéÿèÿåÿãÿàÿßÿÜÿÚÿÙÿÖÿÔÿÓÿÐÿÎÿËÿÊÿÇÿÅÿÄÿÁÿ¿ÿŒÿ»ÿžÿ¶ÿµÿ²ÿ°ÿ¯ÿ¬ÿ©ÿ§ÿŠÿ£ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+    ÿÿÿþÿüÿùÿøÿöÿóÿðÿðÿíÿêÿéÿçÿäÿáÿáÿÞÿÛÿÚÿØÿÕÿÔÿÒÿÏÿÌÿÌÿÉÿÆÿÅÿÃÿÀÿœÿœÿºÿ·ÿ¶ÿŽÿ±ÿ°ÿ®ÿ«ÿšÿ§ÿ¥ÿ¢ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 	 	     ÿýÿúÿúÿ÷ÿôÿòÿñÿîÿìÿëÿèÿåÿãÿâÿßÿÝÿÜÿÙÿÖÿÖÿÓÿÐÿÎÿÍÿÊÿÈÿÇÿÄÿÁÿ¿ÿŸÿ»ÿ¹ÿžÿµÿ²ÿ²ÿ¯ÿ¬ÿªÿ©ÿŠÿ£ÿ£ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 
+    ÿÿÿüÿûÿøÿöÿóÿòÿðÿíÿìÿêÿçÿäÿãÿáÿÞÿÝÿÛÿØÿ×ÿÔÿÒÿÏÿÎÿÌÿÉÿÈÿÆÿÃÿÀÿ¿ÿœÿºÿ¹ÿ·ÿŽÿ³ÿ°ÿ®ÿ«ÿªÿšÿ¥ÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+ 	     ÿýÿýÿúÿ÷ÿôÿôÿñÿîÿîÿëÿèÿæÿåÿâÿßÿßÿÜÿÙÿÙÿÖÿÓÿÐÿÐÿÍÿÊÿÊÿÇÿÄÿÂÿÁÿŸÿ»ÿ»ÿžÿµÿµÿ²ÿ¯ÿ¬ÿ¬ÿ©ÿŠÿŠÿ£ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+    ÿÿÿþÿûÿùÿöÿõÿòÿðÿïÿìÿêÿçÿæÿäÿáÿàÿÝÿÛÿÚÿ×ÿÕÿÒÿÑÿÎÿÌÿËÿÈÿÆÿÃÿÂÿÀÿœÿŒÿ¹ÿ·ÿ¶ÿ³ÿ±ÿ®ÿ­ÿªÿšÿ§ÿ€ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	     ÿÿÿýÿúÿ÷ÿ÷ÿôÿñÿðÿîÿëÿèÿèÿåÿâÿâÿßÿÜÿÛÿÙÿÖÿÓÿÓÿÐÿÍÿÌÿÊÿÇÿÄÿÄÿÁÿŸÿŸÿ»ÿžÿ·ÿµÿ²ÿ¯ÿ¯ÿ¬ÿ©ÿšÿŠÿ£ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿþÿûÿùÿøÿõÿóÿòÿïÿìÿêÿéÿæÿäÿãÿàÿÞÿÝÿÚÿ×ÿÕÿÔÿÑÿÏÿÎÿËÿÈÿÆÿÅÿÂÿÀÿ¿ÿŒÿºÿ¹ÿ¶ÿ³ÿ±ÿ°ÿ­ÿ«ÿªÿ§ÿ€ÿ¢ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿýÿúÿùÿ÷ÿôÿóÿñÿîÿëÿêÿèÿåÿäÿâÿßÿÞÿÜÿÙÿÖÿÕÿÓÿÐÿÏÿÍÿÊÿÇÿÆÿÄÿÁÿÀÿŸÿ»ÿºÿžÿµÿ²ÿ±ÿ¯ÿ¬ÿ«ÿ©ÿŠÿ£ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿþÿüÿûÿøÿõÿõÿòÿïÿíÿìÿéÿæÿæÿãÿàÿàÿÝÿÚÿØÿ×ÿÔÿÑÿÑÿÎÿËÿÉÿÈÿÅÿÂÿÂÿ¿ÿŒÿŒÿ¹ÿ¶ÿŽÿ³ÿ°ÿ­ÿ­ÿªÿ§ÿ¥ÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿüÿúÿ÷ÿöÿóÿñÿîÿíÿëÿèÿçÿäÿâÿáÿÞÿÜÿÙÿØÿÖÿÓÿÒÿÏÿÍÿÊÿÉÿÇÿÄÿÃÿÀÿŸÿœÿºÿžÿµÿŽÿ²ÿ¯ÿ®ÿ«ÿ©ÿŠÿ¥ÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿøÿøÿõÿòÿïÿïÿìÿéÿéÿæÿãÿâÿàÿÝÿÚÿÚÿ×ÿÔÿÔÿÑÿÎÿËÿËÿÈÿÅÿÅÿÂÿ¿ÿŸÿŒÿ¹ÿ¶ÿ¶ÿ³ÿ°ÿ°ÿ­ÿªÿ§ÿ§ÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿöÿôÿñÿðÿíÿëÿêÿçÿåÿäÿáÿÞÿÜÿÛÿØÿÖÿÕÿÒÿÐÿÍÿÌÿÉÿÇÿÆÿÃÿÁÿÀÿœÿºÿžÿ·ÿŽÿ²ÿ±ÿ®ÿ¬ÿ©ÿšÿ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõÿòÿòÿïÿìÿëÿéÿæÿåÿãÿàÿÝÿÜÿÚÿ×ÿÖÿÔÿÑÿÎÿÎÿËÿÈÿÇÿÅÿÂÿÁÿ¿ÿŒÿ¹ÿžÿ¶ÿ³ÿ²ÿ°ÿ­ÿªÿªÿ§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÿðÿîÿíÿêÿçÿçÿäÿáÿßÿÞÿÛÿØÿØÿÕÿÒÿÐÿÏÿÌÿÊÿÉÿÆÿÃÿÃÿÀÿœÿ»ÿºÿ·ÿŽÿŽÿ±ÿ®ÿ¬ÿ«ÿšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿîÿìÿéÿèÿåÿãÿàÿßÿÝÿÚÿÙÿÖÿÔÿÑÿÐÿÎÿËÿÊÿÈÿÅÿÄÿÁÿ¿ÿŒÿ»ÿ¹ÿ¶ÿµÿ²ÿ°ÿ­ÿ¬ÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíÿêÿêÿçÿäÿáÿáÿÞÿÛÿÛÿØÿÕÿÒÿÒÿÏÿÌÿÌÿÉÿÆÿÆÿÃÿÀÿœÿœÿºÿ·ÿ·ÿŽÿ±ÿ®ÿ®ÿ«ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿèÿæÿãÿâÿßÿÝÿÜÿÙÿ×ÿÔÿÓÿÐÿÎÿÍÿÊÿÈÿÇÿÄÿÂÿ¿ÿŸÿ»ÿ¹ÿžÿµÿ³ÿ°ÿ¯ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÿäÿäÿáÿÞÿÝÿÛÿØÿÕÿÕÿÒÿÏÿÎÿÌÿÉÿÈÿÆÿÃÿÀÿÀÿœÿºÿ¹ÿ·ÿŽÿ±ÿ±ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÿâÿàÿßÿÜÿÙÿ×ÿÖÿÓÿÑÿÐÿÍÿÊÿÊÿÇÿÄÿÂÿÁÿŸÿŒÿ»ÿžÿµÿ³ÿ²ÿ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÿàÿÞÿÛÿØÿ×ÿÕÿÒÿÑÿÏÿÌÿËÿÈÿÆÿÃÿÂÿÀÿœÿŒÿºÿ·ÿŽÿ³ÿ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÿÜÿÚÿÙÿÖÿÓÿÓÿÐÿÍÿÍÿÊÿÇÿÄÿÄÿÁÿŸÿŸÿ»ÿžÿ¶ÿµÿ²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÿÚÿØÿÕÿÔÿÑÿÏÿÎÿËÿÉÿÆÿÅÿÂÿÀÿ¿ÿŒÿºÿ·ÿ¶ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÿÖÿÖÿÓÿÐÿÏÿÍÿÊÿÇÿÇÿÄÿÁÿÀÿŸÿ»ÿžÿžÿµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×ÿÔÿÒÿÑÿÎÿËÿÉÿÈÿÅÿÃÿÂÿ¿ÿŒÿºÿ¹ÿ¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÒÿÐÿÍÿÊÿÉÿÇÿÄÿÃÿÁÿŸÿ»ÿºÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÿÎÿÌÿËÿÈÿÅÿÅÿÂÿ¿ÿœÿŒÿ¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÿÌÿÊÿÇÿÆÿÃÿÁÿŸÿœÿ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÈÿÈÿÅÿÂÿ¿ÿ¿ÿŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÆÿÄÿÁÿÀÿœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÿÂÿÂÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÿÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ? > = < ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	           = < ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþ ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿü 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿú 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿø 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿö 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿô 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿò / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿð - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿî + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿì ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿê ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿè % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿä !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâ  
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿà 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞ               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜ             
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚ           
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØ         
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖ       
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔ     
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒ   
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐ 
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊ         ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈ       ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆ     ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄ   ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ = >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ; < < =ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 7 8 : ; : ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 6 6 7 8 9 9 :ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3 2 4 5 4 5 7 8 7 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ / 0 2 1 2 3 3 4 5 7 6 7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - , . / 0 / 1 2 1 3 4 5 4 6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) * , + , - / . / 1 0 1 2 4 3 4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ' ( ( ) * ) + , - - . / . 0 1 2 2 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % $ & ' & ' ) ( ) + , + , . - . 0 1 0 2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ! " $ # $ % % & ' ' ( ) * * + , , - . 0 / 0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+   ! " ! # $ # % & % & ( ) ( * + * , - . - /ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+ 
+ 
+  !   ! # " # $ $ % & ( ' ( * ) * + - , -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+  
+ 
+     !   " # " $ % & & ' ( ' ) * + + ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ          
+ 
+ 
+ 
+      " ! " $ % $ % ' & ' ) * ) *ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ             
+ 
+ 
+ 
+ 
+      ! " # # $ % % & ' ( ( )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                 
+ 
+ 
+ 
+  
+  ! " ! # $ # $ & ' & (ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+                   
+  
+ 
+ 
+ 
+  !   ! " " # $ & % &ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 
+  
+                   
+  
+ 
+  
+   !   " # $ $ %ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+ 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+      " # " #ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      	 	 
+ 
+ 
+ 
+ 
+ 
+                    
+ 
+ 
+ 
+ 
+    ! ! "ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ          	 
+ 	 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+     !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ               	 
+ 
+ 
+ 
+  
+                     
+ 
+  
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿüÿþÿÿ  ÿÿ            	  
+ 
+ 
+ 
+ 
+  
+                   
+ 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿúÿüÿûÿüÿýÿÿÿþÿÿ               	 
+ 
+ 
+ 
+ 
+ 
+                   
+  
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿøÿøÿùÿúÿùÿûÿüÿýÿýÿþÿÿÿþ              	 	 
+ 
+ 
+ 
+ 
+                    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÿôÿöÿ÷ÿöÿ÷ÿùÿøÿùÿûÿüÿûÿüÿþÿýÿþ                 
+ 	 
+ 
+ 
+ 
+                  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÿòÿòÿóÿôÿõÿõÿöÿ÷ÿ÷ÿøÿùÿúÿúÿûÿüÿüÿýÿþ  ÿÿ               	 
+ 
+ 
+ 
+ 
+ 
+               ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿîÿðÿñÿðÿñÿóÿôÿóÿõÿöÿõÿöÿøÿùÿøÿúÿûÿúÿüÿýÿþÿýÿÿ  ÿÿ            	 
+ 	 
+ 
+ 
+ 
+  
+            ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿìÿîÿíÿîÿïÿïÿðÿñÿóÿòÿóÿôÿôÿõÿöÿøÿ÷ÿøÿúÿùÿúÿûÿýÿüÿýÿÿÿþÿÿ              	  	 
+ 
+ 
+ 
+ 
+ 
+           ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéÿêÿêÿëÿìÿëÿíÿîÿíÿïÿðÿñÿðÿòÿóÿòÿôÿõÿöÿöÿ÷ÿøÿ÷ÿùÿúÿûÿûÿüÿýÿüÿþÿÿÿþ              	  
+ 
+ 
+ 
+ 
+  
+        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÿæÿèÿéÿèÿéÿëÿêÿëÿíÿìÿíÿîÿðÿïÿðÿòÿñÿòÿôÿõÿôÿõÿ÷ÿöÿ÷ÿùÿúÿùÿúÿüÿûÿüÿþÿýÿþ                 
+ 	 
+ 
+ 
+ 
+ 
+       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÿäÿäÿåÿæÿçÿçÿèÿéÿéÿêÿëÿêÿìÿíÿîÿîÿïÿðÿðÿñÿòÿóÿóÿôÿõÿõÿöÿ÷ÿøÿøÿùÿúÿúÿûÿüÿüÿýÿþÿÿÿÿ               	 
+ 
+ 
+ 
+ 
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÿàÿâÿãÿâÿãÿåÿæÿåÿçÿèÿçÿèÿêÿéÿêÿìÿíÿìÿîÿïÿîÿïÿñÿòÿñÿóÿôÿóÿôÿöÿ÷ÿöÿøÿùÿøÿúÿûÿúÿûÿýÿþÿýÿÿ  ÿÿ             	 
+ 	 
+ 
+ 
+ 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝÿÞÿàÿßÿàÿáÿáÿâÿãÿåÿäÿåÿæÿæÿçÿèÿèÿéÿêÿìÿëÿìÿíÿíÿîÿïÿñÿðÿñÿòÿòÿóÿôÿöÿõÿöÿøÿ÷ÿøÿùÿùÿúÿûÿýÿüÿýÿþÿþÿÿ             	  	 
+ 
+ 
+ 
+  
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÿÚÿÜÿÝÿÞÿÝÿßÿàÿßÿáÿâÿãÿâÿäÿåÿäÿæÿçÿæÿèÿéÿêÿéÿëÿìÿëÿíÿîÿïÿîÿðÿñÿðÿòÿóÿôÿôÿõÿöÿõÿ÷ÿøÿ÷ÿùÿúÿûÿúÿüÿýÿüÿþÿÿ  ÿÿ            	  
+ 
+ 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×ÿØÿÚÿÙÿÚÿÛÿÝÿÜÿÝÿßÿÞÿßÿàÿâÿáÿâÿäÿãÿäÿæÿåÿæÿçÿéÿèÿéÿëÿêÿëÿìÿîÿíÿîÿðÿïÿðÿòÿóÿòÿóÿõÿôÿõÿ÷ÿöÿ÷ÿøÿúÿùÿúÿüÿûÿüÿýÿÿÿþÿÿ               	 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÿÖÿÖÿ×ÿØÿ×ÿÙÿÚÿÛÿÛÿÜÿÝÿÜÿÞÿßÿàÿàÿáÿâÿâÿãÿäÿãÿåÿæÿçÿçÿèÿéÿèÿêÿëÿìÿìÿíÿîÿîÿïÿðÿñÿñÿòÿóÿóÿôÿõÿôÿöÿ÷ÿøÿøÿùÿúÿùÿûÿüÿýÿýÿþÿÿÿÿ               	 	 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÒÿÔÿÕÿÔÿÕÿ×ÿÖÿ×ÿÙÿÚÿÙÿÚÿÜÿÛÿÜÿÞÿßÿÞÿàÿáÿàÿáÿãÿâÿãÿåÿæÿåÿæÿèÿçÿèÿêÿëÿêÿìÿíÿìÿíÿïÿðÿïÿñÿòÿñÿòÿôÿóÿôÿöÿ÷ÿöÿ÷ÿùÿøÿùÿûÿüÿûÿýÿþÿýÿþ  ÿÿ             	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÿÐÿÒÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿØÿØÿÙÿÚÿÚÿÛÿÜÿÞÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿäÿäÿåÿæÿæÿçÿèÿêÿéÿêÿëÿëÿìÿíÿïÿîÿïÿðÿðÿñÿòÿòÿóÿôÿõÿõÿöÿ÷ÿ÷ÿøÿùÿûÿúÿûÿüÿüÿýÿþÿþÿÿ            ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÿÌÿÎÿÏÿÐÿÏÿÑÿÒÿÑÿÓÿÔÿÓÿÔÿÖÿ×ÿÖÿØÿÙÿØÿÚÿÛÿÜÿÛÿÝÿÞÿÝÿßÿàÿßÿàÿâÿãÿâÿäÿåÿäÿæÿçÿèÿçÿéÿêÿéÿëÿìÿíÿìÿîÿïÿîÿðÿñÿðÿñÿóÿôÿóÿõÿöÿõÿ÷ÿøÿùÿøÿúÿûÿúÿüÿýÿüÿýÿÿ  ÿÿ        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÊÿÌÿËÿÌÿÍÿÏÿÎÿÏÿÑÿÐÿÑÿÒÿÒÿÓÿÔÿÖÿÕÿÖÿØÿ×ÿØÿÙÿÛÿÚÿÛÿÝÿÜÿÝÿÞÿÞÿßÿàÿâÿáÿâÿäÿãÿäÿåÿçÿæÿçÿéÿèÿéÿêÿìÿëÿìÿîÿíÿîÿïÿïÿðÿñÿóÿòÿóÿõÿôÿõÿöÿøÿ÷ÿøÿúÿùÿúÿûÿûÿüÿýÿÿÿþÿÿ        ÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿÈÿÈÿÉÿÊÿÉÿËÿÌÿÍÿÍÿÎÿÏÿÎÿÐÿÑÿÐÿÒÿÓÿÔÿÔÿÕÿÖÿÕÿ×ÿØÿÙÿÙÿÚÿÛÿÚÿÜÿÝÿÜÿÞÿßÿàÿàÿáÿâÿáÿãÿäÿåÿåÿæÿçÿæÿèÿéÿêÿêÿëÿìÿëÿíÿîÿíÿïÿðÿñÿñÿòÿóÿòÿôÿõÿöÿöÿ÷ÿøÿ÷ÿùÿúÿùÿûÿüÿýÿýÿþÿÿÿþ      ÿÿÿÿÿÿÿÿÿÃÿÄÿÆÿÇÿÆÿÇÿÉÿÈÿÉÿËÿÌÿËÿÌÿÎÿÍÿÎÿÐÿÏÿÐÿÒÿÓÿÒÿÓÿÕÿÔÿÕÿ×ÿØÿ×ÿØÿÚÿÙÿÚÿÜÿÛÿÜÿÞÿßÿÞÿßÿáÿàÿáÿãÿäÿãÿäÿæÿåÿæÿèÿéÿèÿéÿëÿêÿëÿíÿìÿíÿïÿðÿïÿðÿòÿñÿòÿôÿõÿôÿõÿ÷ÿöÿ÷ÿùÿøÿùÿûÿüÿûÿüÿþÿýÿþ      ÿÿÿÿÿÁÿÂÿÂÿÃÿÄÿÅÿÅÿÆÿÇÿÇÿÈÿÉÿÊÿÊÿËÿÌÿÌÿÍÿÎÿÎÿÏÿÐÿÑÿÑÿÒÿÓÿÓÿÔÿÕÿÖÿÖÿ×ÿØÿØÿÙÿÚÿÚÿÛÿÜÿÝÿÝÿÞÿßÿßÿàÿáÿâÿâÿãÿäÿäÿåÿæÿçÿçÿèÿéÿéÿêÿëÿëÿìÿíÿîÿîÿïÿðÿðÿñÿòÿóÿóÿôÿõÿõÿöÿ÷ÿ÷ÿøÿùÿúÿúÿûÿüÿüÿýÿþÿÿÿÿ  ÿÿÿÿÿÀÿÁÿÀÿÁÿÃÿÄÿÃÿÅÿÆÿÅÿÆÿÈÿÉÿÈÿÊÿËÿÊÿÌÿÍÿÌÿÍÿÏÿÐÿÏÿÑÿÒÿÑÿÒÿÔÿÕÿÔÿÖÿ×ÿÖÿØÿÙÿØÿÙÿÛÿÜÿÛÿÝÿÞÿÝÿÞÿàÿáÿàÿâÿãÿâÿãÿåÿæÿåÿçÿèÿçÿéÿêÿéÿêÿìÿíÿìÿîÿïÿîÿïÿñÿòÿñÿóÿôÿóÿõÿöÿõÿöÿøÿùÿøÿúÿûÿúÿûÿýÿþÿÿÿÿÿÿÿÿÿŸÿ¿ÿ¿ÿÀÿÁÿÃÿÂÿÃÿÄÿÄÿÅÿÆÿÈÿÇÿÈÿÊÿÉÿÊÿËÿËÿÌÿÍÿÏÿÎÿÏÿÐÿÐÿÑÿÒÿÔÿÓÿÔÿÖÿÕÿÖÿ×ÿ×ÿØÿÙÿÛÿÚÿÛÿÜÿÜÿÝÿÞÿàÿßÿàÿáÿáÿâÿãÿåÿäÿåÿçÿæÿçÿèÿèÿéÿêÿìÿëÿìÿíÿíÿîÿïÿñÿðÿñÿóÿòÿóÿôÿôÿõÿöÿøÿ÷ÿøÿùÿùÿúÿÿÿÿÿÿÿÿÿÿÿÿÿœÿŸÿœÿ¿ÿÀÿÁÿÀÿÂÿÃÿÂÿÄÿÅÿÆÿÆÿÇÿÈÿÇÿÉÿÊÿÉÿËÿÌÿÍÿÌÿÎÿÏÿÎÿÐÿÑÿÒÿÒÿÓÿÔÿÓÿÕÿÖÿÕÿ×ÿØÿÙÿØÿÚÿÛÿÚÿÜÿÝÿÞÿÝÿßÿàÿßÿáÿâÿãÿãÿäÿåÿäÿæÿçÿæÿèÿéÿêÿéÿëÿìÿëÿíÿîÿïÿïÿðÿñÿðÿòÿóÿòÿôÿõÿöÿõÿ÷ÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÿœÿŒÿœÿŸÿÀÿ¿ÿÀÿÂÿÁÿÂÿÄÿÅÿÄÿÅÿÇÿÆÿÇÿÉÿÈÿÉÿÊÿÌÿËÿÌÿÎÿÍÿÎÿÐÿÑÿÐÿÑÿÓÿÒÿÓÿÕÿÔÿÕÿÖÿØÿ×ÿØÿÚÿÙÿÚÿÛÿÝÿÜÿÝÿßÿÞÿßÿáÿâÿáÿâÿäÿãÿäÿæÿåÿæÿçÿéÿèÿéÿëÿêÿëÿíÿîÿíÿîÿðÿïÿðÿòÿñÿòÿóÿõÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿ»ÿºÿŒÿœÿŸÿŸÿ¿ÿÀÿÀÿÁÿÂÿÃÿÃÿÄÿÅÿÅÿÆÿÇÿÆÿÈÿÉÿÊÿÊÿËÿÌÿÌÿÍÿÎÿÏÿÏÿÐÿÑÿÑÿÒÿÓÿÒÿÔÿÕÿÖÿÖÿ×ÿØÿ×ÿÙÿÚÿÛÿÛÿÜÿÝÿÝÿÞÿßÿàÿàÿáÿâÿâÿãÿäÿãÿåÿæÿçÿçÿèÿéÿéÿêÿëÿìÿìÿíÿîÿîÿïÿðÿïÿñÿòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿºÿ¹ÿºÿŒÿœÿŒÿŸÿ¿ÿŸÿ¿ÿÁÿÂÿÁÿÃÿÄÿÃÿÄÿÆÿÅÿÆÿÈÿÉÿÈÿÊÿËÿÊÿËÿÍÿÎÿÍÿÏÿÐÿÏÿÐÿÒÿÑÿÒÿÔÿÕÿÔÿÕÿ×ÿÖÿ×ÿÙÿÚÿÙÿÛÿÜÿÛÿÜÿÞÿßÿÞÿàÿáÿàÿáÿãÿâÿãÿåÿæÿåÿçÿèÿçÿèÿêÿëÿêÿìÿíÿìÿíÿïÿîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ÿžÿžÿ¹ÿºÿŒÿ»ÿŒÿœÿœÿŸÿ¿ÿÁÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÈÿÇÿÈÿÉÿÉÿÊÿËÿÍÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿÙÿØÿÙÿÚÿÚÿÛÿÜÿÞÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿåÿäÿåÿæÿæÿçÿèÿêÿéÿêÿëÿëÿìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÿ·ÿ¶ÿžÿ¹ÿºÿ¹ÿ»ÿŒÿ»ÿœÿŸÿ¿ÿŸÿÀÿÁÿÀÿÂÿÃÿÂÿÄÿÅÿÆÿÅÿÇÿÈÿÇÿÉÿÊÿËÿÊÿÌÿÍÿÌÿÎÿÏÿÎÿÏÿÑÿÒÿÑÿÓÿÔÿÓÿÕÿÖÿ×ÿÖÿØÿÙÿØÿÚÿÛÿÜÿÛÿÝÿÞÿÝÿßÿàÿßÿáÿâÿãÿâÿäÿåÿäÿæÿçÿèÿçÿéÿêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ¶ÿµÿ¶ÿ·ÿ¹ÿžÿ¹ÿ»ÿºÿ»ÿŒÿŸÿœÿŸÿÀÿ¿ÿÀÿÂÿÁÿÂÿÃÿÅÿÄÿÅÿÇÿÆÿÇÿÈÿÊÿÉÿÊÿÌÿËÿÌÿÍÿÍÿÎÿÏÿÑÿÐÿÑÿÓÿÒÿÓÿÔÿÖÿÕÿÖÿØÿ×ÿØÿÙÿÛÿÚÿÛÿÝÿÜÿÝÿßÿÞÿßÿàÿâÿáÿâÿäÿãÿäÿåÿçÿæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÿŽÿ³ÿµÿ¶ÿ·ÿ·ÿžÿ¹ÿžÿºÿ»ÿŒÿŒÿœÿŸÿŸÿ¿ÿÀÿ¿ÿÁÿÂÿÃÿÃÿÄÿÅÿÄÿÆÿÇÿÈÿÈÿÉÿÊÿÉÿËÿÌÿËÿÍÿÎÿÏÿÏÿÐÿÑÿÐÿÒÿÓÿÔÿÔÿÕÿÖÿÕÿ×ÿØÿÙÿÙÿÚÿÛÿÛÿÜÿÝÿÜÿÞÿßÿàÿàÿáÿâÿáÿãÿäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÿ³ÿ²ÿ³ÿµÿ¶ÿµÿ¶ÿžÿ·ÿžÿºÿ»ÿºÿŒÿœÿŒÿœÿ¿ÿŸÿ¿ÿÁÿÂÿÁÿÂÿÄÿÃÿÄÿÆÿÇÿÆÿÇÿÉÿÈÿÉÿËÿÊÿËÿÍÿÎÿÍÿÎÿÐÿÏÿÐÿÒÿÓÿÒÿÓÿÕÿÔÿÕÿ×ÿØÿ×ÿÙÿÚÿÙÿÚÿÜÿÛÿÜÿÞÿßÿÞÿßÿáÿàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ±ÿ±ÿ²ÿ³ÿŽÿŽÿµÿ¶ÿ¶ÿ·ÿžÿºÿ¹ÿºÿ»ÿ»ÿŒÿœÿœÿŸÿ¿ÿÀÿÀÿÁÿÂÿÂÿÃÿÄÿÅÿÅÿÆÿÇÿÇÿÈÿÉÿÉÿÊÿËÿÌÿÌÿÍÿÎÿÎÿÏÿÐÿÑÿÑÿÒÿÓÿÓÿÔÿÕÿ×ÿÖÿ×ÿØÿØÿÙÿÚÿÚÿÛÿÜÿÝÿÝÿÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÿ°ÿ¯ÿ°ÿ²ÿ³ÿ²ÿŽÿµÿŽÿ¶ÿ·ÿžÿ·ÿ¹ÿºÿ¹ÿ»ÿŒÿ»ÿŒÿŸÿ¿ÿŸÿÀÿÁÿÀÿÁÿÃÿÄÿÃÿÅÿÆÿÅÿÇÿÈÿÇÿÈÿÊÿËÿÊÿÌÿÍÿÌÿÍÿÏÿÐÿÏÿÑÿÒÿÑÿÓÿÔÿÕÿÔÿÖÿ×ÿÖÿØÿÙÿØÿÙÿÛÿÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­ÿ®ÿ®ÿ¯ÿ°ÿ²ÿ±ÿ²ÿŽÿ³ÿŽÿµÿ·ÿ¶ÿ·ÿ¹ÿžÿ¹ÿºÿºÿ»ÿŒÿŸÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÃÿÂÿÃÿÅÿÄÿÅÿÆÿÆÿÇÿÈÿÊÿÉÿÊÿËÿËÿÌÿÍÿÏÿÎÿÏÿÑÿÐÿÑÿÒÿÔÿÓÿÔÿÖÿÕÿÖÿ×ÿ×ÿØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ­ÿ¬ÿ®ÿ¯ÿ°ÿ°ÿ±ÿ²ÿ±ÿ³ÿŽÿµÿµÿ¶ÿ·ÿ¶ÿžÿ¹ÿžÿºÿ»ÿŒÿ»ÿœÿŸÿœÿ¿ÿÀÿÁÿÁÿÂÿÃÿÂÿÄÿÅÿÄÿÆÿÇÿÈÿÇÿÉÿÊÿÉÿËÿÌÿÍÿÍÿÎÿÏÿÎÿÐÿÑÿÒÿÒÿÓÿÔÿÓÿÕÿÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿ¬ÿ«ÿ¬ÿ®ÿ¯ÿ®ÿ¯ÿ±ÿ°ÿ±ÿ³ÿŽÿ³ÿŽÿ¶ÿµÿ¶ÿžÿ·ÿžÿ¹ÿ»ÿºÿ»ÿœÿŒÿœÿ¿ÿÀÿ¿ÿÀÿÂÿÁÿÂÿÄÿÃÿÄÿÅÿÇÿÆÿÇÿÉÿÈÿÉÿËÿÌÿËÿÌÿÎÿÍÿÎÿÐÿÑÿÐÿÑÿÓÿÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ÿªÿªÿ«ÿ¬ÿ­ÿ­ÿ®ÿ¯ÿ¯ÿ°ÿ±ÿ²ÿ²ÿ³ÿŽÿŽÿµÿ¶ÿµÿ·ÿžÿ¹ÿ¹ÿºÿ»ÿ»ÿŒÿœÿŸÿŸÿ¿ÿÀÿÀÿÁÿÂÿÁÿÃÿÄÿÅÿÅÿÆÿÇÿÇÿÈÿÉÿÊÿÊÿËÿÌÿÌÿÍÿÎÿÏÿÏÿÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿ©ÿšÿ©ÿ«ÿ¬ÿ«ÿ­ÿ®ÿ­ÿ®ÿ°ÿ±ÿ°ÿ²ÿ³ÿ²ÿ³ÿµÿŽÿµÿ·ÿžÿ·ÿ¹ÿºÿ¹ÿºÿŒÿœÿŒÿŸÿ¿ÿŸÿ¿ÿÁÿÀÿÁÿÃÿÄÿÃÿÅÿÆÿÅÿÆÿÈÿÉÿÈÿÊÿËÿÊÿËÿÍÿÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿ§ÿ§ÿšÿ©ÿ«ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ°ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ·ÿ¶ÿ·ÿžÿžÿ¹ÿºÿŒÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÃÿÂÿÃÿÄÿÄÿÅÿÆÿÈÿÇÿÈÿÉÿÉÿÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥ÿŠÿ¥ÿ§ÿšÿ©ÿšÿªÿ«ÿªÿ¬ÿ­ÿ®ÿ­ÿ¯ÿ°ÿ¯ÿ±ÿ²ÿ±ÿ³ÿŽÿµÿŽÿ¶ÿ·ÿ¶ÿžÿ¹ÿºÿ¹ÿ»ÿŒÿ»ÿœÿŸÿœÿ¿ÿÀÿÁÿÀÿÂÿÃÿÂÿÄÿÅÿÆÿÅÿÇÿÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ÿ¥ÿ€ÿ¥ÿŠÿšÿ§ÿšÿªÿ©ÿªÿ«ÿ­ÿ¬ÿ­ÿ¯ÿ®ÿ¯ÿ±ÿ°ÿ±ÿ²ÿŽÿ³ÿŽÿ¶ÿµÿ¶ÿ·ÿ¹ÿžÿ¹ÿ»ÿºÿ»ÿœÿŒÿœÿŸÿÀÿ¿ÿÀÿÂÿÁÿÂÿÃÿÅÿÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿ¢ÿ€ÿ¥ÿŠÿŠÿ§ÿšÿ§ÿ©ÿªÿ«ÿ«ÿ¬ÿ­ÿ­ÿ®ÿ¯ÿ®ÿ°ÿ±ÿ²ÿ²ÿ³ÿŽÿ³ÿµÿ¶ÿ·ÿ·ÿžÿ¹ÿ¹ÿºÿ»ÿºÿŒÿœÿŸÿŸÿ¿ÿÀÿ¿ÿÁÿÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¡ÿ¢ÿ€ÿ¥ÿ€ÿ¥ÿ§ÿŠÿ§ÿ©ÿªÿ©ÿ«ÿ¬ÿ«ÿ¬ÿ®ÿ­ÿ®ÿ°ÿ±ÿ°ÿ±ÿ³ÿ²ÿ³ÿµÿ¶ÿµÿ·ÿžÿ·ÿžÿºÿ¹ÿºÿŒÿœÿŒÿœÿ¿ÿŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ£ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ©ÿšÿ©ÿªÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ¯ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿµÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿ»ÿ»ÿŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¢ÿ¡ÿ£ÿ€ÿ£ÿ¥ÿŠÿ§ÿŠÿšÿ©ÿšÿªÿ«ÿªÿ«ÿ­ÿ®ÿ­ÿ¯ÿ°ÿ¯ÿ±ÿ²ÿ³ÿ²ÿŽÿµÿŽÿ¶ÿ·ÿ¶ÿ·ÿ¹ÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ ÿ¡ÿ£ÿ¢ÿ£ÿ€ÿŠÿ¥ÿŠÿšÿ§ÿšÿ©ÿ©ÿªÿ«ÿ­ÿ¬ÿ­ÿ¯ÿ®ÿ¯ÿ°ÿ²ÿ±ÿ²ÿŽÿ³ÿŽÿµÿµÿ¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ ÿ¢ÿ£ÿ€ÿ€ÿ¥ÿŠÿ¥ÿ§ÿšÿ§ÿ©ÿªÿ«ÿ«ÿ¬ÿ­ÿ¬ÿ®ÿ¯ÿ°ÿ°ÿ±ÿ²ÿ±ÿ³ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿ¢ÿ£ÿ¢ÿ£ÿ¥ÿ€ÿ¥ÿ§ÿŠÿ§ÿ©ÿªÿ©ÿªÿ¬ÿ«ÿ¬ÿ®ÿ¯ÿ®ÿ¯ÿ±ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¡ÿ¢ÿ£ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿšÿšÿ©ÿªÿªÿ«ÿ¬ÿ­ÿ­ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ¡ÿ¢ÿ¡ÿ£ÿ€ÿ£ÿ€ÿŠÿ§ÿŠÿšÿ©ÿšÿ©ÿ«ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿŠÿ¥ÿŠÿ§ÿ§ÿšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ ÿ¢ÿ£ÿ€ÿ£ÿ¥ÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿ¡ÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; = ?ÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : < >ÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; =ÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : <ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ;ÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 :ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9ÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6ÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - /ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , .ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + -ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * ,ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) +ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( *ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' )ÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & (ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % 'ÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ &ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # %ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! #ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   "ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  !ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+  ÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ ÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+ÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+       ÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+       ÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+      ÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+     ÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+     ÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+    ÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+    ÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+   ÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+   ÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+  ÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ ÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ ÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ    ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ   ÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/.cvsignore	(revision 22322)
@@ -0,0 +1,38 @@
+temp
+.deps
+.libs
+Makefile
+Makefile.in
+tst_psImageConvolve
+tst_psImageGeomManip
+tst_psImageInterpolate
+tst_psImagePixelExtract
+tst_psImagePixelManip
+tst_psImageStats
+tst_psImageStructManip
+fBiOut.fits
+fOut.fits
+sBiOut.fits
+sOut.fits
+tst_psImageMaskOps
+tst_psImageSmooth
+*.bb
+*.bbg
+*.da
+gmon.out
+tap_psImageShift
+tap_psImageShiftKernel
+tap_psImageConvolve
+tap_psImageConvolve2
+convolutionBench
+tap_psImageInterpolate2
+tap_psImageGeomManip
+tap_psImageMaskOps
+tap_psImagePixelExtract
+tap_psImagePixelManip
+tap_psImageSmooth
+tap_psImageStructManip
+tap_psImageMap
+
+tap_psImageMapFit
+tap_psImageMapFit2
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/Makefile.am	(revision 22322)
@@ -0,0 +1,46 @@
+
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSLIB_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpslib.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSLIB_LIBS)
+
+TEST_PROGS = \
+	tap_psImageShift \
+	tap_psImageGeomManip \
+	tap_psImagePixelManip \
+	tap_psImageSmooth \
+	tap_psImageStructManip \
+	tap_psImageConvolve \
+	tap_psImageConvolve2 \
+	tap_psImagePixelExtract \
+	tap_psImageInterpolate2 \
+	tap_psImageMap \
+	tap_psImageMapFit \
+	tap_psImageMapFit2 \
+	tap_psImageMaskOps
+
+#	tap_psImageShiftKernel
+#	tap_psImageInterpolate
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* fOut.fits sOut.fits fBiOut.fits sBiOut.fits \
+	core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
+
+noinst_PROGRAMS = convolutionBench
+convolutionBench_SOURCES = convolutionBench.c
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/convolutionBench.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/convolutionBench.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/convolutionBench.c	(revision 22322)
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <pslib.h>
+
+// Generate image with single high pixel
+static psImage *generateImage(int numCols, int numRows, psRandom *rng)
+{
+    psImage *image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            image->data.F32[y][x] = psRandomGaussian(rng);
+        }
+    }
+    return image;
+}
+
+
+static psKernel *generateKernel(int numCols, int numRows)
+{
+    psKernel *kernel = psKernelAlloc(-numCols, numCols, -numRows, numRows);
+    for (int y = -numRows; y <= numRows; y++) {
+        for (int x = -numCols; x <= numCols; x++) {
+            kernel->kernel[y][x] = psGaussian(sqrtf((float)(x*x + y*y)), 0.0,
+                                              (float)PS_MIN(numCols, numRows) / 3.0, true);
+        }
+    }
+    return kernel;
+}
+
+static void runBench(int imageCols, int imageRows, int kernelCols, int kernelRows, int iter, psRandom *rng)
+{
+    double direct = 0.0, fft = 0.0;     // Sum of elapsed times for the two methods
+    for (int i = 0; i < iter; i++) {
+        {
+            psImage *image = generateImage(imageCols, imageRows, rng);
+            psKernel *kernel = generateKernel(kernelCols, kernelRows);
+            psTimerStart("direct");
+            psImage *convolved = psImageConvolveDirect(NULL, image, kernel);
+            direct += psTimerMark("direct");
+            psFree(convolved);
+            psFree(kernel);
+            psFree(image);
+            psTimerClear("direct");
+        }
+
+        {
+            psImage *image = generateImage(imageCols, imageRows, rng);
+            psKernel *kernel = generateKernel(kernelCols, kernelRows);
+            psTimerStart("fft");
+            psImage *convolved = psImageConvolveFFT(NULL, image, NULL, 0, kernel);
+            fft += psTimerMark("fft");
+            psFree(convolved);
+            psFree(kernel);
+            psFree(image);
+            psTimerClear("fft");
+        }
+    }
+
+    char size[16];
+    sprintf(size, "%dx%d", imageCols, imageRows);
+    printf("%15s", size);
+    sprintf(size, "%dx%d", 2*kernelCols+1, 2*kernelRows+1);
+    printf(" %15s", size);
+    printf("        %8f        %8f\n", direct / iter, fft / iter);
+}
+
+
+
+int main(int argc, char *argv[])
+{
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+
+    printf("#%14s%16s        %8s        %8s\n", "Image", "Kernel", "Direct", "FFT");
+    runBench( 100,  100, 3, 3, 10, rng);
+    runBench( 200,  200, 3, 3, 10, rng);
+    runBench( 400,  400, 3, 3, 10, rng);
+    runBench( 600,  600, 3, 3, 10, rng);
+    runBench( 800,  800, 3, 3,  8, rng);
+    runBench(1000, 1000, 3, 3,  6, rng);
+    runBench(2000, 2000, 3, 3,  4, rng);
+    runBench(4000, 4000, 3, 3,  2, rng);
+
+    runBench(600, 600,  1,  1, 10, rng);
+    runBench(600, 600,  2,  2, 10, rng);
+    runBench(600, 600,  3,  3, 10, rng);
+    runBench(600, 600,  4,  4, 10, rng);
+    runBench(600, 600,  6,  6,  8, rng);
+    runBench(600, 600,  8,  8,  4, rng);
+    runBench(600, 600, 10, 10,  2, rng);
+    runBench(600, 600, 15, 15,  2, rng);
+
+    psFree(rng);
+    psTimerStop();
+
+    return EXIT_SUCCESS;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageConvolve.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageConvolve.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageConvolve.c	(revision 22322)
@@ -0,0 +1,390 @@
+/** @file  tst_psImageConvolve.c
+ *
+ *  @brief Contains the tests for psImageConvolve.[ch]
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-05-01 00:08:52 $
+ *
+ *  XXX: Must test the tRelative parameter to psKernelGenerate()
+ *  XXX: Must test psImageConvolveFFT()
+ *  XXX: Make sure psImageConvolveDirect() is correct.
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+bool testKernelAlloc(psS32 xMin, psS32 xMax, psS32 yMin, psS32 yMax)
+{
+    {
+        psMemId id = psMemGetId();
+        psKernel *k = psKernelAlloc(xMin, xMax, yMin, yMax);
+        ok(k != NULL, "psKernelAlloc returned non-NULL for [%d:%d,%d:%d]",
+           xMin, xMax, yMin, yMax);
+        skip_start(k == NULL, 2, "Skipping tests because psKernelAlloc() returned NULL");
+        ok(k->xMin == xMin && k->xMax == xMax &&
+           k->yMin == yMin && k->yMax == yMax, "Min/max members, [%d:%d,%d:%d].  Should be [%d:%d,%d:%d]",
+           k->xMin,k->xMax, k->yMin, k->yMax,
+           xMin, xMax, yMin, yMax);
+
+        ok(k->image->numCols == xMax-xMin+1 && k->image->numRows == yMax-yMin+1,
+           "Size of the kernel image (%dx%d vs %dx%d)",
+           xMax-xMin+1, yMax-yMin+1, k->image->numCols, k->image->numRows);
+
+        bool errorFlag = false;
+        for (psS32 j=yMin; j<yMax; j++) {
+            if (k->kernel[j]+xMin != k->image->data.PS_TYPE_KERNEL_DATA[j-yMin]) {
+                diag("ERROR: The kernel pointer was set wrong for row %d", j);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psKernelAlloc() produced the correct data values");
+        skip_end();
+        psFree(k);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Following should be a warning (xMin > xMax)
+    {
+        psMemId id = psMemGetId();
+        psKernel *k = psKernelAlloc(5, -5, -2, 2);
+        ok(k != NULL, "psKernelAlloc returned non-NULL with xMin > xMax");
+        skip_start(k == NULL, 1, "Skipping tests because psKernelAlloc() returned NULL");
+        ok(k->xMin == -5 && k->xMax == 5, "psKernelAlloc did swap xMin & xMax");
+        skip_end();
+        psFree(k);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Following should be a warning (yMin > yMax)
+    {
+        psMemId id = psMemGetId();
+        psKernel *k = psKernelAlloc(-2, 2, 5, -5);
+        ok(k != NULL, "psKernelAlloc returned non-NULL with yMin > yMax");
+        skip_start(k == NULL, 1, "Skipping tests because psKernelAlloc() returned NULL");
+        ok(k->yMin == -5 && k->yMax == 5, "psKernelAlloc did swap yMin & yMax");
+        skip_end();
+        psFree(k);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    return(true);
+}
+
+bool testKernelGenerate(void)
+{
+    psS32 size = 5;
+    psS32 t[] = { 1, 2, 8, 9, 10 };
+    psS32 x[] = { 0, 1, 0, -1, 0 };
+    psS32 y[] = { 2, 1, -1, -2, 0 };
+    float sum;
+
+    {
+        psMemId id = psMemGetId();
+        psVector* xVec = psVectorAlloc(size,PS_TYPE_U32);
+        psVector* yVec = psVectorAlloc(size,PS_TYPE_U32);
+        psVector* tVec = psVectorAlloc(size,PS_TYPE_U32);
+
+        for (psS32 i = 0; i < size; i++) {
+            xVec->data.U32[i] = x[i];
+            yVec->data.U32[i] = y[i];
+            tVec->data.U32[i] = t[i];
+        }
+
+        psKernel* result = psKernelGenerate(tVec, xVec, yVec, false, false);
+        ok(result != NULL, "psKernelGenerate() returned non-NULL");
+        skip_start(result == NULL, 3, "Skipping tests because psKernelGenerate() returned NULL");
+        ok(result->xMin == -1 && result->xMax == 1 &&
+           result->yMin == -2 && result->yMax == 2,
+           "psKernelGenerate result had a range of [%d:%d,%d:%d].  Suppose to be [-2:2,-1:1]",
+           result->xMin, result->xMax, result->yMin, result->yMax);
+
+        sum = 0.0;
+        printf("Resulting kernel:\n");
+        for (psS32 y = result->yMin; y <= result->yMax; y++) {
+            for (psS32 x = result->xMin; x <= result->xMax; x++) {
+                printf(" %6.2f ", result->kernel[y][x]);
+                sum += result->kernel[y][x];
+            }
+            printf("\n");
+        }
+        ok(fabsf(1.0 - sum) <= FLT_EPSILON, "psKernelGenerate result is normalized (sum=%g)", sum);
+
+        ok(fabsf(result->kernel[-2][0] - 0.1) <= FLT_EPSILON &&
+           fabsf(result->kernel[ -1][ -1] - 0.1) <= FLT_EPSILON &&
+           fabsf(result->kernel[ 1][ 0] - 0.6) <= FLT_EPSILON &&
+           fabsf(result->kernel[2][1] - 0.1) <= FLT_EPSILON,
+           "psKernelGenerate result values, %g,%g,%g,%g.  Suppose to be 0.1,0.1,0.6,0.1",
+           result->kernel[-2][0], result->kernel[-1][-1],
+           result->kernel[1][0], result->kernel[2][1]);
+        skip_end();
+        psFree(result);
+        psFree(xVec);
+        psFree(yVec);
+        psFree(tVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    ok(false, "XXXX: Skipping this psKernelGenerate() because of bugs");
+    if (0) {
+        psMemId id = psMemGetId();
+        psVector* xVec = psVectorAlloc(size,PS_TYPE_U32);
+        psVector* yVec = psVectorAlloc(size,PS_TYPE_U32);
+        psVector* tVec = psVectorAlloc(size,PS_TYPE_U32);
+
+        for (psS32 i = 0; i < size; i++) {
+            xVec->data.S16[i] = x[i];
+            yVec->data.S16[i] = y[i];
+            tVec->data.S16[i] = t[i];
+        }
+
+        psKernel *result = psKernelGenerate(tVec, xVec, yVec, false, true);
+        ok(result != NULL, "psKernelGenerate() returned non-NULL");
+        skip_start(result == NULL, 3, "Skipping tests because psKernelGenerate() returned NULL");
+        ok (result->xMin == 0 && result->xMax == 1 &&
+            result->yMin == 0 && result->yMax == 3,
+            "psKernelGenerate result had a range of [%d:%d,%d:%d].  Suppose to be [0:1,1:2]",
+            result->xMin, result->xMax, result->yMin, result->yMax);
+
+        // XXXX: The bug surfaces here
+        sum = 0.0;
+        if (1) {
+            printf("Resulting kernel (relative=true) (%u - %u) (%u - %u):\n",
+                   result->yMin, result->yMax, result->xMin, result->xMax);
+            for (psS32 y = result->yMin; y <= result->yMax; y++) {
+                // printf("y is %d\n", y);
+                for (psS32 x = result->xMin; x <= result->xMax; x++) {
+                    printf(" %6.2f ", result->kernel[y][x]);
+                    sum += result->kernel[y][x];
+                }
+                printf("\n");
+            }
+        }
+        ok(fabsf(1.0 - sum) <= FLT_EPSILON, "psKernelGenerate result is normalized (sum=%g)", sum);
+
+        ok(fabsf(result->kernel[0][0] - 19.0/30.0) <= FLT_EPSILON &&
+           fabsf(result->kernel[2][0] - 1.0/30.0) <= FLT_EPSILON &&
+           fabsf(result->kernel[2][1] - 8.0/30.0) <= FLT_EPSILON &&
+           fabsf(result->kernel[3][1] - 2.0/30.0) <= FLT_EPSILON,
+           "psKernelGenerate result values, %g,%g;%g,%g. Suppose to be 2,6;1,0",
+           result->kernel[0][0], result->kernel[2][0],
+           result->kernel[2][1], result->kernel[3][1]);
+        skip_end();
+        psFree(result);
+        psFree(xVec);
+        psFree(yVec);
+        psFree(tVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Following should be an error
+    if (1) {
+        psMemId id = psMemGetId();
+        psVector* xVec = psVectorAlloc(size,PS_TYPE_U32);
+        psVector* yVec = psVectorAlloc(size,PS_TYPE_U32);
+        psVector* tVec = psVectorAlloc(size,PS_TYPE_U32);
+
+        for (psS32 i = 0; i < size; i++) {
+            psVectorSet(xVec, i, x[i]+0.1);
+            yVec->data.F32[i] = y[i]+0.2;
+            tVec->data.F32[i] = t[i]+0.3;
+        }
+
+        tVec->n--; // decrease size by one to make vectors unequal in length.
+        psKernel *result = psKernelGenerate(tVec, xVec, yVec, false, false);
+        ok(result == NULL, "psKernelGenerate returned NULL given differing sized vectors");
+        psFree(result);
+        psFree(xVec);
+        psFree(yVec);
+        psFree(tVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    psVector* xVec = psVectorAlloc(size,PS_TYPE_U32);
+    psVector* yVec = psVectorAlloc(size,PS_TYPE_U32);
+    psVector* tVec = psVectorAlloc(size,PS_TYPE_U32);
+    // Following should be a error (time vector NULL)
+    {
+        psMemId id = psMemGetId();
+        psKernel *result = psKernelGenerate(NULL, xVec, yVec, false, true);
+        ok(result == NULL, "psKernelGenerate returned NULL with no time vector");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Following should be a error (x vector NULL)
+    {
+        psMemId id = psMemGetId();
+        psKernel *result = psKernelGenerate(tVec, NULL, yVec, false, true);
+        ok(result == NULL, "psKernelGenerate returned NULL with no x vector");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Following should be a error (y vector NULL
+    {
+        psMemId id = psMemGetId();
+        psKernel *result = psKernelGenerate(tVec, xVec, NULL, false, true);
+        ok(result == NULL, "psKernelGenerate returned NULL with no y vector");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psFree(xVec);
+    psFree(yVec);
+    psFree(tVec);
+    return(true);
+}
+
+
+bool testImageConvolve(void)
+{
+    psMemId id = psMemGetId();
+    const psS32 r = 200;
+    const psS32 c = 300;
+    psS32 sum;
+
+    // approximate a normalized gaussian kernel.
+    psKernel* g = psKernelAlloc(-1,1,-1,1);
+    g->kernel[-1][-1] = g->kernel[-1][1] = g->kernel[1][-1] = g->kernel[1][1] = 0.0113;
+    g->kernel[1][0] = g->kernel[-1][0] = g->kernel[0][-1] = g->kernel[0][1] = 0.0838;
+    g->kernel[0][0] = 0.6193;
+
+    // create a normalized non-symetric kernel.
+    psKernel* nsk = psKernelAlloc(0,2,0,2);
+    sum = 0.0;
+    for (psS32 i=0;i<2;i++) {
+        for (psS32 j=0;j<2;j++) {
+            nsk->kernel[i][j] = i+j;
+            sum = i+j;
+        }
+    }
+    for (psS32 i=0;i<2;i++) {
+        for (psS32 j=0;j<2;j++) {
+            nsk->kernel[i][j] /= sum;
+        }
+    }
+
+
+    psImage* img = psImageAlloc(c,r,PS_TYPE_F32);
+    memset(img->data.F32[0],0,c*r*PSELEMTYPE_SIZEOF(PS_TYPE_F32));
+    img->data.F32[0][0] = 1.0f;
+    img->data.F32[r/2][c/2] = 1.0f;
+    img->data.F32[r-1][c/2] = 1.0f;
+
+    // test spacial convolution of gaussian
+    ok(false, "XXXX: Skipping this psImageConvolve() because of bugs");
+/*
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* out = psImageConvolveDirect(NULL, img, g);
+        ok(out != NULL, "psImageConvolveDirect() returned non-NULL");
+        skip_start(out == NULL, 3, "Skipping tests because psImageConvolveDirect() returned NULL");
+        ok(out->numCols == c && out->numRows == r,
+           "psImageConvolveDirect result image is %dx%d, expected %dx%d",
+           out->numCols, out->numRows, c,r);
+        ok(out->type.type == PS_TYPE_F32, "psImageConvolveDirect() produced the correct type");
+
+        // test values
+        bool errorFlag = false;
+        for (psS32 i=-1;i<1;i++) {
+            for (psS32 j=-1;j<1;j++) {
+                if (fabsf(out->data.F32[r/2+i][c/2+j] - g->kernel[i][j]) > 0.0001) {
+                    diag("Convolved image wrong at %d,%d.  Value is %g, expected %g",
+                         c/2+j,r/2+i, out->data.F32[r/2+i][c/2+j], g->kernel[i][j]);
+                    errorFlag = true;
+                }
+                if (i >= 0 && j >= 0 && fabsf(out->data.F32[i][j] - g->kernel[i][j]) > 0.0001) {
+                    diag("Convolved image wrong at %d,%d.  Value is %g, expected %g",
+                         j, i, out->data.F32[i][j], g->kernel[i][j]);
+                    errorFlag = true;
+                }
+                if (i <= 0 && fabsf(out->data.F32[r-1+i][c/2+j] - g->kernel[i][j]) > 0.0001) {
+                    diag("Convolved image wrong at %d,%d.  Value is %g, expected %g",
+                         c/2+j,r-1+i, out->data.F32[r-1+i][c/2+j], g->kernel[i][j]);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psKernelGenerate() produced the correct data values");
+        skip_end();
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test fourier convolution of gaussian
+    ok(false, "XXXX: Skipping this psImageConvolveDirect() because of bugs");
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* out = psImageConvolveDirect(NULL, img, g);
+        psImage* out2 = psImageConvolveDirect(out, img, g);
+
+        ok(out != NULL, "psImageConvolveDirect() returned non-NULL");
+        ok(out2 != NULL, "psImageConvolveDirect() returned non-NULL");
+        skip_start(out == NULL || out2 == NULL, 3, "Skipping tests because psImageConvolveDirect() returned NULL");
+        ok(out == out2, "psImageConvolveDirect did recycle the supplied out image struct");
+        ok(out->numCols == c && out->numRows == r,
+           "psImageConvolveDirect result image is %dx%d, expected %dx%d",
+           out->numCols, out->numRows, c,r);
+        ok(out->type.type == PS_TYPE_F32, "psImageConvolveDirect() produced the correct type");
+
+        // test values
+        bool errorFlag = false;
+        for (psS32 i=-1;i<1;i++) {
+            for (psS32 j=-1;j<1;j++) {
+                if (fabsf(out->data.F32[r/2+i][c/2+j] - g->kernel[i][j]) > 0.01) {
+                    diag("Convolved image wrong at %d,%d.  Value is %g, expected %g",
+                         c/2+j,r/2+i, out->data.F32[r/2+i][c/2+j], g->kernel[i][j]);
+                    errorFlag = true;
+                }
+                if (i >= 0 && j >= 0 && fabsf(out->data.F32[i][j] - g->kernel[i][j]) > 0.01) {
+                    diag("Convolved image wrong at %d,%d.  Value is %g, expected %g",
+                         j,i, out->data.F32[i][j], g->kernel[i][j]);
+                    errorFlag = true;
+                }
+                if (i <= 0 && fabsf(out->data.F32[r-1+i][c/2+j] - g->kernel[i][j]) > 0.01) {
+                    diag("Convolved image wrong at %d,%d.  Value is %g, expected %g",
+                         c/2+j,r-1+i, out->data.F32[r-1+i][c/2+j], g->kernel[i][j]);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psKernelGenerate() produced the correct data values");
+        skip_end();
+        psFree(out);
+        psFree(out2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+*/
+
+    psFree(g);
+    psFree(img);
+    psFree(nsk);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    return(true);
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(62);
+
+    testKernelAlloc(-5, 0, -4, 0);
+    testKernelAlloc(0, 5, 0, 4);
+    testKernelAlloc(-10, -5, -8, -4);
+    testKernelAlloc(5, 10, 4, 8);
+    testKernelGenerate();
+    testImageConvolve();
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageConvolve2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageConvolve2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageConvolve2.c	(revision 22322)
@@ -0,0 +1,244 @@
+#include <stdio.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+#define IMAGE_SIZE 21
+#define KERNEL_SIZE 3
+#define TOL 1.0e-6
+#define MASK_INITIAL 0x08
+#define MASK_FINAL   0x80
+
+
+// Generate image with single high pixel
+static psImage *generateImage(void)
+{
+    psImage *image = psImageAlloc(IMAGE_SIZE, IMAGE_SIZE, PS_TYPE_F32);
+    psImageInit(image, 0.0);
+    image->data.F32[IMAGE_SIZE/2][IMAGE_SIZE/2] = 1.0;
+    return image;
+}
+
+// Generate mask with single pixel set
+static psImage *generateMask(void)
+{
+    psImage *mask = psImageAlloc(IMAGE_SIZE, IMAGE_SIZE, PS_TYPE_MASK);
+    psImageInit(mask, 0);
+    mask->data.PS_TYPE_MASK_DATA[IMAGE_SIZE/2][IMAGE_SIZE/2] = MASK_INITIAL;
+    return mask;
+}
+
+// Generate Gaussian kernel
+static psKernel *generateKernel(void)
+{
+    psKernel *kernel = psKernelAlloc(- KERNEL_SIZE, KERNEL_SIZE, - KERNEL_SIZE, KERNEL_SIZE);
+    for (int y = - KERNEL_SIZE; y <= KERNEL_SIZE; y++) {
+        for (int x = - KERNEL_SIZE; x <= KERNEL_SIZE; x++) {
+            kernel->kernel[y][x] = exp(- x*x - y*y);
+        }
+    }
+    kernel->kernel[KERNEL_SIZE/3][KERNEL_SIZE/2] = 1.0;
+    return kernel;
+}
+
+// Check the convolved image matches the kernel
+static bool checkConvolved(const psImage *image, const psKernel *kernel)
+{
+    bool correct = true;
+    for (int y = 0; y < IMAGE_SIZE; y++) {
+        for (int x = 0; x < IMAGE_SIZE; x++) {
+            if (x < IMAGE_SIZE/2 - KERNEL_SIZE || x > IMAGE_SIZE/2 + KERNEL_SIZE ||
+                y < IMAGE_SIZE/2 - KERNEL_SIZE || y > IMAGE_SIZE/2 + KERNEL_SIZE) {
+                if (fabs(image->data.F32[y][x]) > TOL) {
+                    diag("%d,%d --> %f", x, y, fabs(image->data.F32[y][x]));
+                    correct = false;
+                }
+            } else {
+                // Position relative to the centre
+                int kx = x - IMAGE_SIZE/2;
+                int ky = y - IMAGE_SIZE/2;
+                if (fabs(image->data.F32[y][x] - kernel->kernel[ky][kx]) > TOL) {
+                    diag("%d,%d --> %f", x, y, fabs(image->data.F32[y][x] - kernel->kernel[ky][kx]));
+                    correct = false;
+                }
+            }
+        }
+    }
+    return correct;
+}
+
+// Check the convolved mask matches the kernel
+static bool checkConvolvedMask(const psImage *mask)
+{
+    bool correct = true;
+    for (int y = 0; y < IMAGE_SIZE; y++) {
+        for (int x = 0; x < IMAGE_SIZE; x++) {
+            if (x < IMAGE_SIZE/2 - KERNEL_SIZE || x > IMAGE_SIZE/2 + KERNEL_SIZE ||
+                y < IMAGE_SIZE/2 - KERNEL_SIZE || y > IMAGE_SIZE/2 + KERNEL_SIZE) {
+                if (mask->data.PS_TYPE_MASK_DATA[y][x] != 0) {
+                    diag("%d,%d --> %d", x, y, mask->data.PS_TYPE_MASK_DATA[y][x]);
+                    correct = false;
+                }
+            } else if (x == IMAGE_SIZE/2 && y == IMAGE_SIZE/2) {
+                if (mask->data.PS_TYPE_MASK_DATA[y][x] != (MASK_INITIAL | MASK_FINAL)) {
+                    diag("%d,%d --> %d", x, y, mask->data.PS_TYPE_MASK_DATA[y][x]);
+                    correct = false;
+                }
+            } else if (mask->data.PS_TYPE_MASK_DATA[y][x] != MASK_FINAL) {
+                diag("%d,%d --> %d", x, y, mask->data.PS_TYPE_MASK_DATA[y][x]);
+                correct = false;
+            }
+        }
+    }
+    return correct;
+}
+
+
+int main(int argc, char *argv[])
+{
+    plan_tests(30);
+
+    diag("psImageConvolve tests");
+
+    {
+        diag("Mask convolution");
+        // Better (separable) mask convolution: 5 tests
+        psMemId id = psMemGetId();
+
+        psImage *mask = generateMask();
+
+        psImage *convolved = psImageConvolveMask(NULL, mask, MASK_INITIAL, MASK_FINAL,
+                                                 -KERNEL_SIZE, KERNEL_SIZE, -KERNEL_SIZE, KERNEL_SIZE);
+        ok(convolved, "convolution result");
+        skip_start(!convolved, 3, "convolution failed");
+        ok(convolved->type.type == PS_TYPE_MASK, "output type");
+        ok(convolved->numCols == IMAGE_SIZE && convolved->numRows == IMAGE_SIZE, "output size %dx%d",
+           convolved->numCols, convolved->numRows);
+        ok(checkConvolvedMask(convolved), "convolved mask correct");
+        psFree(convolved);
+        skip_end();
+
+        psFree(mask);
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        diag("Mask convolution in-place");
+        // Better (separable) in-place FFT mask convolution: 5 tests
+        psMemId id = psMemGetId();
+
+        psImage *mask = generateMask();
+
+        bool result = psImageConvolveMask(mask, mask, MASK_INITIAL, MASK_FINAL, -KERNEL_SIZE, KERNEL_SIZE,
+                                          -KERNEL_SIZE, KERNEL_SIZE);
+        ok(result, "convolution result");
+        skip_start(!result, 3, "convolution failed");
+        ok(mask->type.type == PS_TYPE_MASK, "output type");
+        ok(mask->numCols == IMAGE_SIZE && mask->numRows == IMAGE_SIZE, "output size %dx%d",
+           mask->numCols, mask->numRows);
+        ok(checkConvolvedMask(mask), "convolved mask correct");
+        skip_end();
+
+        psFree(mask);
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        diag("Direct convolution");
+        // Direct convolution: 5 tests
+        psMemId id = psMemGetId();
+
+        psImage *image = generateImage();
+        psKernel *kernel = generateKernel();
+
+        psImage *convolved = psImageConvolveDirect(NULL, image, kernel);
+        ok(convolved, "convolution result");
+        skip_start(!convolved, 3, "convolution failed");
+        ok(convolved->type.type == PS_TYPE_F32, "output type");
+        ok(convolved->numCols == IMAGE_SIZE && convolved->numRows == IMAGE_SIZE, "output size %dx%d",
+           convolved->numCols, convolved->numRows);
+        ok(checkConvolved(convolved, kernel), "convolved image correct");
+        psFree(convolved);
+        skip_end();
+
+        psFree(kernel);
+        psFree(image);
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        diag("FFT convolution");
+        // FFT convolution: 5 tests
+        psMemId id = psMemGetId();
+
+        psImage *image = generateImage();
+        psKernel *kernel = generateKernel();
+
+        psImage *convolved = psImageConvolveFFT(NULL, image, NULL, 0, kernel);
+        ok(convolved, "convolution result");
+        skip_start(!convolved, 3, "convolution failed");
+        ok(convolved->type.type == PS_TYPE_F32, "output type");
+        ok(convolved->numCols == IMAGE_SIZE && convolved->numRows == IMAGE_SIZE, "output size %dx%d",
+           convolved->numCols, convolved->numRows);
+        ok(checkConvolved(convolved, kernel), "convolved image correct");
+        psFree(convolved);
+        skip_end();
+
+        psFree(kernel);
+        psFree(image);
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        diag("FFT mask convolution");
+        // FFT mask convolution: 5 tests
+        psMemId id = psMemGetId();
+
+        psImage *mask = generateMask();
+
+        psImage *convolved = psImageConvolveMaskFFT(NULL, mask, MASK_INITIAL, MASK_FINAL,
+                                                    -KERNEL_SIZE, KERNEL_SIZE, -KERNEL_SIZE, KERNEL_SIZE,
+                                                    0.5);
+        ok(convolved, "convolution result");
+        skip_start(!convolved, 3, "convolution failed");
+        ok(convolved->type.type == PS_TYPE_MASK, "output type");
+        ok(convolved->numCols == IMAGE_SIZE && convolved->numRows == IMAGE_SIZE, "output size %dx%d",
+           convolved->numCols, convolved->numRows);
+        ok(checkConvolvedMask(convolved), "convolved mask correct");
+        psFree(convolved);
+        skip_end();
+
+        psFree(mask);
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        diag("FFT mask convolution in-place");
+        // In-place FFT mask convolution: 5 tests
+        psMemId id = psMemGetId();
+
+        psImage *mask = generateMask();
+
+        bool result = psImageConvolveMaskFFT(mask, mask, MASK_INITIAL, MASK_FINAL, -KERNEL_SIZE, KERNEL_SIZE,
+                                             -KERNEL_SIZE, KERNEL_SIZE, 0.5);
+        ok(result, "convolution result");
+        skip_start(!result, 3, "convolution failed");
+        ok(mask->type.type == PS_TYPE_MASK, "output type");
+        ok(mask->numCols == IMAGE_SIZE && mask->numRows == IMAGE_SIZE, "output size %dx%d",
+           mask->numCols, mask->numRows);
+        ok(checkConvolvedMask(mask), "convolved mask correct");
+        skip_end();
+
+        psFree(mask);
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageGeomManip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageGeomManip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageGeomManip.c	(revision 22322)
@@ -0,0 +1,1074 @@
+/** @file  tst_psImageGeomManip.c
+ *
+ *  @brief Contains the tests for psImageManip.[ch]
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-06-04 20:25:32 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define VERBOSE 0
+
+void genericImageRollTest(int numRows, int numCols)
+{
+    psMemId id = psMemGetId();
+    psImage *in;
+    psImage *out;
+    psImage *out2;
+
+    // The function psImageRoll shall generate a new psImage structure by
+    // rolling the input image the correponding number of pixels in the vertical
+    // and/or horizontal direction. The image output image shall be the same size
+    // as the input image. Values which roll off the image are wrapped to the
+    // other side.
+    //
+    // Verify the returned psImage structure contains expected values, if the
+    // input image contains known values and the roll performed is known.
+    // Cases should include no roll, vertical roll, horizontal roll and
+    // combination vertical/horizontal rolls. Positive and negative rolls
+    // should be performed.
+
+    in = psImageAlloc(numCols,numRows,PS_TYPE_F32);
+    for (psS32 row=0;row<numRows;row++) {
+        for (psS32 col=0;col<numCols;col++) {
+            in->data.F32[row][col] = (psF32)row+(psF32)col/1000.0f;
+        }
+    }
+
+    out = psImageRoll(NULL,in,0,0);
+    bool errorFlag = false;
+    for (psS32 row=0;row<numRows;row++) {
+        psF32 *inRow = in->data.F32[row];
+        psF32 *outRow = out->data.F32[row];
+        for (psS32 col=0;col<numCols;col++) {
+            if (inRow[col] != outRow[col]) {
+                diag("psImageRoll didn't produce expected result "
+                     "at %d,%d (%f vs %f) for dx=0, dy=0",
+                     col,row,inRow[col],outRow[col]);
+                errorFlag = true;
+            }
+        }
+    }
+    ok(!errorFlag, "psImageRoll() produced the correct data values (no roll)");
+
+    errorFlag = false;
+    out2 = psImageRoll(out,in,numCols/4,0);
+    for (psS32 row=0;row<numRows;row++)
+    {
+        psF32 *inRow = in->data.F32[row];
+        psF32 *outRow = out->data.F32[row];
+        for (psS32 col=0;col<numCols;col++) {
+            if (inRow[(col+numCols/4) % numCols] != outRow[col]) {
+                diag("psImageRoll didn't produce expected result "
+                     "at %d,%d (%f vs %f) for dx=numCols/4, dy=0",
+                     col,row,inRow[(col+numCols/4) % numCols],outRow[col]);
+                errorFlag = true;
+            }
+        }
+    }
+    ok(!errorFlag, "psImageRoll() produced the correct data values (column roll only");
+
+    // Verify the returned psImage structure pointer is equal to the input
+    // parameter out if provided.
+    ok(out2 == out, "psImageRoll did recycle the out psImage");
+
+    errorFlag = false;
+    out = psImageRoll(out,in,0,numRows/4);
+    for (psS32 row=0;row<numRows;row++)
+    {
+        psF32 *inRow = in->data.F32[(row+numRows/4)%numRows];
+        psF32 *outRow = out->data.F32[row];
+        for (psS32 col=0;col<numCols;col++) {
+            if (inRow[col] != outRow[col]) {
+                diag("psImageRoll didn't produce expected result "
+                     "at %d,%d (%f vs %f) for dx=0, dy=numRows/4",
+                     col,row,inRow[col],outRow[col]);
+                errorFlag = true;
+            }
+        }
+    }
+    ok(!errorFlag, "psImageRoll() produced the correct data values (row roll onlt)");
+
+    errorFlag = false;
+    out = psImageRoll(out,in,numCols/4,numRows/4);
+    for (psS32 row=0;row<numRows;row++)
+    {
+        psF32 *inRow = in->data.F32[(row+numRows/4)%numRows];
+        psF32 *outRow = out->data.F32[row];
+        for (psS32 col=0;col<numCols;col++) {
+            if (inRow[(col+numCols/4) % numCols] != outRow[col]) {
+                diag("psImageRoll didn't produce expected result "
+                     "at %d,%d (%f vs %f) for dx=numCols/4, dy=numRows/4",
+                     col,row,inRow[(col+numCols/4) % numCols],outRow[col]);
+                errorFlag = true;
+            }
+        }
+    }
+    ok(!errorFlag, "psImageRoll() produced the correct data values (column and row roll)");
+
+    errorFlag = false;
+    out = psImageRoll(out,in,-numCols/4,0);
+    for (psS32 row=0;row<numRows;row++)
+    {
+        psF32 *inRow = in->data.F32[row];
+        psF32 *outRow = out->data.F32[row];
+        for (psS32 col=0;col<numCols;col++) {
+            if (inRow[(col+(numCols-numCols/4)) % numCols] != outRow[col]) {
+                diag("psImageRoll didn't produce expected result "
+                     "at %d,%d (%f vs %f) for dx=-numCols/4, dy=0",
+                     col,row,inRow[(col+(numCols-numCols/4)) % numCols],outRow[col]);
+                errorFlag = true;
+            }
+        }
+    }
+    ok(!errorFlag, "psImageRoll() produced the correct data values (negative column roll)");
+
+    errorFlag = false;
+    out = psImageRoll(out,in,0,-numRows/4);
+    for (psS32 row=0;row<numRows;row++)
+    {
+        psF32 *inRow = in->data.F32[(row+numRows-numRows/4)%numRows];
+        psF32 *outRow = out->data.F32[row];
+        for (psS32 col=0;col<numCols;col++) {
+            if (inRow[col] != outRow[col]) {
+                diag("psImageRoll didn't produce expected result "
+                     "at %d,%d (%f vs %f) for dx=0, dy=-numRows/4",
+                     col,row,inRow[col],outRow[col]);
+                errorFlag = true;
+            }
+        }
+    }
+    ok(!errorFlag, "psImageRoll() produced the correct data values (negative row roll)");
+
+    errorFlag = false;
+    out = psImageRoll(out,in,-numCols/4,-numRows/4);
+    for (psS32 row=0;row<numRows;row++)
+    {
+        psF32 *inRow = in->data.F32[(row+numRows-numRows/4)%numRows];
+        psF32 *outRow = out->data.F32[row];
+        for (psS32 col=0;col<numCols;col++) {
+            if (inRow[(col+numCols-numCols/4) % numCols] != outRow[col]) {
+                diag("psImageRoll didn't produce expected result "
+                     "at %d,%d (%f vs %f) for dx=numCols/4, dy=numRows/4",
+                     col,row,inRow[(col+numCols-numCols/4) % numCols],outRow[col]);
+                errorFlag = true;
+            }
+        }
+    }
+    ok(!errorFlag, "psImageRoll() produced the correct data values (negative column and row roll)");
+    psFree(in);
+    psFree(out);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+}
+
+
+
+bool testImageShiftCase(psS32 cols,
+                        psS32 rows,
+                        float colShift,
+                        float rowShift)
+{
+    psImage *fOut = NULL;
+    psImage *sOut = NULL;
+    psImage *fImg = psImageAlloc(cols,rows,PS_TYPE_F32);
+    psImage *sImg = psImageAlloc(cols,rows,PS_TYPE_S16);
+    psImage *fBiOut = psImageAlloc(cols,rows,PS_TYPE_F32);
+    psImage *sBiOut = psImageAlloc(cols,rows,PS_TYPE_S16);
+    bool errorFlag = false;
+
+    for(psS32 row=0;row<rows;row++) {
+        psF32 *fRow = fImg->data.F32[row];
+        psS16 *sRow = sImg->data.S16[row];
+        for (psS32 col=0;col<cols;col++) {
+            fRow[col] = (psF32)(row)+(psF32)(col)/100.0f;
+            sRow[col] = row-2*col;
+        }
+    }
+
+    fOut = psImageShift(fOut, fImg, colShift, rowShift, NAN, PS_INTERPOLATE_FLAT);
+    sOut = psImageShift(sOut, sImg, colShift, rowShift, -1, PS_INTERPOLATE_FLAT);
+    fBiOut = psImageShift(fBiOut, fImg, colShift, rowShift, NAN, PS_INTERPOLATE_BILINEAR);
+    sBiOut = psImageShift(sBiOut, sImg, colShift, rowShift, -1, PS_INTERPOLATE_BILINEAR);
+
+    {
+        psImageInterpolateOptions *tmpIntOpts = psImageInterpolateOptionsAlloc(
+            PS_INTERPOLATE_FLAT, fImg, NULL, NULL, 0, NAN, NAN, 0, 0, 0.0);
+        double imgVal;
+        double varVal;
+        psMaskType maskVal;
+        for(psS32 row=0;row<rows;row++) {
+            psF32 *fRow = fOut->data.F32[row];
+            for (psS32 col=0;col<cols;col++) {
+                psImageInterpolate(&imgVal, &varVal, &maskVal, col+0.5-colShift,
+                                   row+0.5-rowShift, tmpIntOpts);
+                if (fabsf(fRow[col] - imgVal) > FLT_EPSILON) {
+                    if (VERBOSE) diag("Float image not shifted correctly at %d,%d (%g vs %g) (flat interpolation)",
+                         row,col,fRow[col],imgVal);
+                    errorFlag = true;
+                }
+            }
+        }
+        psFree(tmpIntOpts);
+    }
+
+    {
+        psImageInterpolateOptions *tmpIntOpts = psImageInterpolateOptionsAlloc(
+            PS_INTERPOLATE_FLAT, sImg, NULL, NULL, 0, -1, NAN, 0, 0, 0.0);
+        double imgVal;
+        double varVal;
+        psMaskType maskVal;
+
+        for(psS32 row=0;row<rows;row++) {
+            psS16 *sRow = sOut->data.S16[row];
+            for (psS32 col=0;col<cols;col++) {
+                psImageInterpolate(&imgVal, &varVal, &maskVal, col+0.5-colShift,
+                                   row+0.5-rowShift, tmpIntOpts);
+                psS16 sValue = (psS16) imgVal;
+                if (sRow[col] != sValue) {
+                    if (VERBOSE) diag("Short image not shifted correctly at %d,%d (%d vs %d) (flat interpolation)",
+                        row,col,sRow[col],sValue);
+                    errorFlag = true;
+                }
+            }
+        }
+        psFree(tmpIntOpts);
+    }
+
+
+    {
+        psImageInterpolateOptions *tmpIntOpts = psImageInterpolateOptionsAlloc(
+            PS_INTERPOLATE_BILINEAR, fImg, NULL, NULL, 0, NAN, NAN, 0, 0, 0.0);
+        double imgVal;
+        double varVal;
+        psMaskType maskVal;
+        for(psS32 row=0;row<rows;row++) {
+            psF32 *fBiRow = fBiOut->data.F32[row];
+            for (psS32 col=0;col<cols;col++) {
+                psImageInterpolate(&imgVal, &varVal, &maskVal, col+0.5-colShift,
+                                   row+0.5-rowShift, tmpIntOpts);
+                psF32 fBiValue = imgVal;
+                if (fabsf(fBiRow[col] - fBiValue) > FLT_EPSILON) {
+                    if (VERBOSE) diag("Float image not shifted correctly at %d,%d (%g vs %g) (bilinear interpolation)",
+                         row,col,fBiRow[col],fBiValue);
+                    errorFlag = true;
+                }
+            }
+        }
+        psFree(tmpIntOpts);
+    }
+
+
+    {
+        psImageInterpolateOptions *tmpIntOpts = psImageInterpolateOptionsAlloc(
+            PS_INTERPOLATE_BILINEAR, sImg, NULL, NULL, 0, -1, NAN, 0, 0, 0.0);
+        double imgVal;
+        double varVal;
+        psMaskType maskVal;
+        for(psS32 row=0;row<rows;row++) {
+            psS16 *sBiRow = sBiOut->data.S16[row];
+            for (psS32 col=0;col<cols;col++) {
+                psImageInterpolate(&imgVal, &varVal, &maskVal, col+0.5-colShift,
+                                   row+0.5-rowShift, tmpIntOpts);
+                psS16 sBiValue = (psS16) imgVal;
+                if (sBiRow[col] != sBiValue) {
+                    if (VERBOSE) diag("Short image not shifted correctly at %d,%d (%d vs %d) (bilinear interpolation)",
+                         row,col,sBiRow[col],sBiValue);
+                    errorFlag = true;
+                }
+            }
+        }
+        psFree(tmpIntOpts);
+    }
+
+    if (errorFlag) {
+        diag("Short or Float image not shifted correctly");
+    }
+    psFree(fImg);
+    psFree(sImg);
+    psFree(fOut);
+    psFree(sOut);
+    psFree(fBiOut);
+    psFree(sBiOut);
+
+    return !errorFlag;
+}
+
+
+psS32 main(psS32 argc, char *argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(280);
+
+    // test psImageRebin()
+    // This function shall generate a rescaled version of a psImage
+    // structure derived from a specified statistics method.
+    if (1) {
+        psMemId id = psMemGetId();
+        psImage *in = NULL;
+        psImage *out = NULL;
+        psImage *out2 = NULL;
+        psImage *out3 = NULL;
+        psImage *mask = NULL;
+        psImage *meanTruth = NULL;
+        psImage *meanTruthWMask = NULL;
+        psImage *maxTruth = NULL;
+        psStats stats;
+
+        //        Verify the returned psImage structure contains expected values, if the
+        //        input parameter input contains known data, the input scale is a known
+        //        value with a known statistical method specified in stats. Cases should
+        //        include at least two different scales and statistical methods. Comparison
+        //        of expected values should include a delta to allow testing on different
+        //        platforms.
+        #define testRebinType(DATATYPE)  \
+        { \
+            psMemId id = psMemGetId();\
+            in = psImageAlloc(16,16,PS_TYPE_##DATATYPE); \
+            mask = psImageAlloc(16,16,PS_TYPE_U8); \
+            meanTruth = psImageAlloc(4,4,PS_TYPE_F32); \
+            meanTruthWMask = psImageAlloc(4,4,PS_TYPE_F32); \
+            maxTruth = psImageAlloc(6,6,PS_TYPE_F32); \
+            memset(meanTruth->data.F32[0],0,sizeof(psF32)*4*4); \
+            memset(meanTruthWMask->data.F32[0],0,sizeof(psF32)*4*4); \
+            memset(maxTruth->data.F32[0],0,sizeof(psF32)*6*6); \
+            for (psS32 row = 0; row<16; row++) { \
+                ps##DATATYPE *inRow = in->data.DATATYPE[row]; \
+                psF32 *meanTruthRow = meanTruth->data.F32[row/4]; \
+                psF32 *meanTruthWMaskRow = meanTruthWMask->data.F32[row/4]; \
+                psF32 *maxTruthRow = maxTruth->data.F32[row/3]; \
+                psU8 *maskRow = mask->data.U8[row]; \
+                for (psS32 col = 0; col<16; col++) { \
+                    if(col != 15) { \
+                        maskRow[col] = 0; \
+                    } else { \
+                        maskRow[col] = 1; \
+                    } \
+                    inRow[col] = row + col; \
+                    meanTruthRow[col/4] += row + col; \
+                    if (maxTruthRow[col/3] < row + col) { \
+                        maxTruthRow[col/3] = row+col; \
+                    } \
+                    if(maskRow[col] == 0 ) { \
+                        meanTruthWMaskRow[col/4] += row + col; \
+                    } \
+                } \
+            } \
+            for (psS32 row = 0; row<4; row++) { \
+                psF32 *meanTruthRow = meanTruth->data.F32[row]; \
+                psF32 *meanTruthWMaskRow = meanTruthWMask->data.F32[row]; \
+                for (psS32 col = 0; col<4; col++) { \
+                    meanTruthRow[col] /= 16; \
+                    if ( col == 3 ) { \
+                        meanTruthWMaskRow[col] /= 12; \
+                    } else { \
+                        meanTruthWMaskRow[col] /= 16; \
+                    } \
+                } \
+            } \
+            stats.options = PS_STAT_SAMPLE_MEAN; \
+            { \
+                out = psImageRebin(NULL,in,NULL,0,4,&stats); \
+                ok(out != NULL, "psImageRebin returned non-NULL"); \
+                ok(out->numRows == 4 && out->numCols == 4, \
+                   "psImageRebin did produce the proper size image"); \
+                bool errorFlag = false; \
+                for (psS32 row = 0; row<4; row++) { \
+                    ps##DATATYPE *outRow = out->data.DATATYPE[row]; \
+                    psF32 *truthRow = meanTruth->data.F32[row]; \
+                    for (psS32 col = 0; col<4; col++) { \
+                        if (fabsf((float)outRow[col]-(float)truthRow[col]) > FLT_EPSILON) { \
+                            diag("psImageRebin didn't produce the proper mean " \
+                                 "result at (%d,%d) [%f vs %f]", \
+                                 col,row,outRow[col],truthRow[col]); \
+                            errorFlag = true;\
+                        } \
+                    } \
+                } \
+                ok(!errorFlag, "psImageRebin() produced the correct data"); \
+            } \
+            { \
+                stats.options = PS_STAT_SAMPLE_MEAN; \
+                out3 = psImageRebin(NULL,in,mask,1,4,&stats); \
+                bool errorFlag = false; \
+                for (psS32 row = 0; row<4; row++) { \
+                    ps##DATATYPE *outRow = out3->data.DATATYPE[row]; \
+                    psF32 *truthRow = meanTruthWMask->data.F32[row]; \
+                    for ( psS32 col = 0; col<4; col++) { \
+                        if(abs((psS32)outRow[col]-(psS32)truthRow[col]) > FLT_EPSILON) { \
+                            diag("psImageRebin with mask didn't produce the proper mean " \
+                                 "result at (%d,%d) [%f vs %f]", \
+                                 col,row,outRow[col],truthRow[col]); \
+                            errorFlag = true;\
+                        } \
+                    } \
+                    ok(!errorFlag, "psImageRebin() produced the correct data"); \
+                } \
+            } \
+            stats.options = PS_STAT_MAX; \
+            { \
+                out2 = psImageRebin(out,in,NULL,0,3,&stats); \
+                ok(out == out2, "psImageRebin didt recycle a psImage properly"); \
+                ok(out != NULL, "psImageRebin returned non-NULL"); \
+                ok(out->numRows == 6 && out->numCols == 6, \
+                   "psImageRebin did produce the proper size image"); \
+                bool errorFlag = false; \
+                for (psS32 row = 0; row<6; row++) { \
+                    ps##DATATYPE *outRow = out->data.DATATYPE[row]; \
+                    psF32 *truthRow = maxTruth->data.F32[row]; \
+                    for (psS32 col = 0; col<6; col++) { \
+                        if (fabsf((float)outRow[col]-(float)truthRow[col]) > FLT_EPSILON) { \
+                            diag("psImageRebin didn't produce the proper " \
+                                 "max result at (%d,%d) [%f vs %f]", \
+                                 col,row,outRow[col],truthRow[col]); \
+                            errorFlag = true;\
+                        } \
+                    } \
+                } \
+                ok(!errorFlag, "psImageRebin() produced the correct data"); \
+            } \
+            psFree(in); \
+            psFree(out); \
+            psFree(out3); \
+            psFree(mask); \
+            psFree(meanTruth); \
+            psFree(meanTruthWMask); \
+            psFree(maxTruth); \
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks"); \
+        }
+
+        testRebinType(F32);
+        testRebinType(F64);
+        testRebinType(U16);
+        testRebinType(S8);
+        // Verify the returned psImage structure is NULL and program execution
+        // doesn't stop, if the input image type is not supported.
+        // Following should be an error for unsupported type
+        // XXX: Verify error
+        in = psImageAlloc(16,16,PS_TYPE_U8);
+        mask = psImageAlloc(16,16,PS_TYPE_F32);
+        stats.options = PS_STAT_SAMPLE_MEAN;
+        out = psImageRebin(NULL,in,NULL,0,4,&stats);
+        ok(out == NULL, "psImageRebin returned NULL for unsupported type");
+
+        // Verify the returned psImage structure is NULL and program execution
+        // doesn't stop, if the mask type is not U8
+        // Following should be an error for invallid mask type
+        // XXX: Verify error
+        out = psImageRebin(NULL,in,mask,1,4,&stats);
+        ok(out == NULL, "psImageRebin returned NULL for incorrect mask");
+        psFree(mask);
+        psFree(in);
+
+        // Verify the returned psImage structure is NULL and program execution
+        // doesn't stop, if the input parameter input is NULL.
+        out2 = psImageRebin(NULL,NULL,NULL,0,1,&stats);
+        ok(out2 == NULL, "psImageRebin returned NULL with NULL input");
+
+        // Verify the returned psImage structure is NULL and program execution
+        // doesn't stop, if the input parameter scale is less than or equal to zero.
+        in = psImageAlloc(16, 16, PS_TYPE_F32);
+        // Following should be an error for scale < 0
+        // XXX: Verify error
+        out2 = psImageRebin(NULL,in,NULL,0,0,&stats);
+        ok(out2 == NULL, "psImageRebin returned NULL when the scale was zero");
+
+        // Verify the returned psImage structure is NULL and program execution
+        // doesn't stop, if the input parameter stats is NULL.
+        // Following should be an error for stats NULL
+        // XXX: Verify error
+        out2 = psImageRebin(NULL,in,NULL,0,1,NULL);
+        ok(out2 == NULL, "psImageRebin returned an NULL when the stats was NULL");
+
+        // Verify the returned psImage structure is NULL and program execution
+        // doesn't stop, if the input parameter psStats structure member options
+        // is zero or any value which doesn't correspond to a valid statistical
+        // method.
+        // Following should be an error for stats options 0
+        // XXX: Verify error
+        stats.options = 0;
+        out2 = psImageRebin(NULL,in,NULL,0,1,&stats);
+        ok(out2 == NULL, "psImageRebin returned an NULL when the stats options was zero");
+
+        //Following should be an error for stat options use range");
+        // XXX: Verify error
+        stats.options = PS_STAT_USE_RANGE;
+        out2 = psImageRebin(NULL,in,NULL,0,1,&stats);
+        ok(out2 == NULL, "psImageRebin returned an image though the stats options was PS_STAT_USE_RANGE");
+
+        // Verify the returned psImage structure is NULL and program execution
+        // doesn't stop, if the input parameter psStats structure member options
+        // specifies more than one valid statistical method.
+        // Following should be an error for stats with multiple options
+        // XXX: Verify error
+        stats.options = PS_STAT_SAMPLE_MEAN + PS_STAT_MAX;
+        out2 = psImageRebin(NULL,in,NULL,0,1,&stats);
+        ok(out2 == NULL, "psImageRebin returned an NULL when the stats options was PS_STAT_SAMPLE_MEAN+PS_STAT_MAX");
+
+        psFree(in);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test psImageRoll()
+    if (1) {
+        psMemId id = psMemGetId();
+        // Perform generic tests with various image sizes
+        genericImageRollTest(8, 1);
+        genericImageRollTest(1, 8);
+        genericImageRollTest(8, 8);
+        genericImageRollTest(8, 16);
+        genericImageRollTest(16, 8);
+
+        psImage *in;
+        psImage *out;
+        psImage *out2;
+        psS32 rows = 64;
+        psS32 cols = 64;
+        psS32 rows1 = 8;
+        psS32 cols1 = 8;
+        in = psImageAlloc(cols,rows,PS_TYPE_F32);
+        for (psS32 row=0;row<rows;row++) {
+            for (psS32 col=0;col<cols;col++) {
+                in->data.F32[row][col] = (psF32)row+(psF32)col/1000.0f;
+            }
+        }
+
+        bool errorFlag = false;
+        // Verify the returned psImage structure pointer is NULL and program
+        // execution doesn't stop, if input parameter input is NULL.
+        out2 = psImageRoll(NULL,NULL,0,0);
+        if (out2 != NULL) {
+            psError(PS_ERR_UNKNOWN, true,"psImageRoll did not return NULL though input image was NULL!?");
+            return 2;
+        }
+
+        psFree(in);
+        psFree(out);
+
+        #define testRollType(DATATYPE) \
+        in = psImageAlloc(rows1,cols1,PS_TYPE_##DATATYPE); \
+        \
+        for (psS32 row=0;row<rows1;row++) { \
+            ps##DATATYPE *inRow = in->data.DATATYPE[row]; \
+            for (psS32 col=0;col<cols1;col++) { \
+                inRow[col] = (ps##DATATYPE)row+(ps##DATATYPE)col; \
+            } \
+        } \
+        \
+        errorFlag = false; \
+        out = psImageRoll(NULL,in,rows1/4,cols1/4); \
+        for (psS32 row=0;row<rows1;row++) { \
+            ps##DATATYPE *inRow = in->data.DATATYPE[(row+rows1/4)%rows1]; \
+            ps##DATATYPE *outRow = out->data.DATATYPE[row]; \
+            for (psS32 col=0;col<cols1;col++) { \
+                if (inRow[(col+cols1/4)%cols1] != outRow[col]) { \
+                    diag("psImageRoll didn't produce expected result " \
+                         "at %d,%d (%f vs %f) for dx=0, dy=0", \
+                         col,row,(float)inRow[col],(float)outRow[col]); \
+                    errorFlag = true; \
+                } \
+            } \
+        } \
+        psFree(in); \
+        psFree(out); \
+        ok(!errorFlag, "psImageRoll() produced the correct data values");
+
+        testRollType(U8);
+        testRollType(U16);
+        testRollType(S8);
+        testRollType(S16);
+        testRollType(F64);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test psImageRotate()
+    if (1) {
+        psMemId id = psMemGetId();
+        // This function shall calculate a new psImage structure based upon the
+        // rotation of a given psImage structure. The center of rotation shall be the
+        // center pixel of the input image.
+        //
+        // The following steps of the testpoint are done manually via inspection of
+        // fOut.fits & sOut.fits.
+        //
+        //  * Verify the returned psImage structure contains expected values, if
+        //    the input parameter psImage contains known values. Cases should
+        //    include rotations of 0, 45, 90, 135, 180, 225, 270, 315, 360 and at leat one
+        //    other arbitrary angle. Cases of the input image should include image
+        //    with a center pixel and an image without a center pixel.
+        //  * Verify the returned psImage structure contains pixels set to exposed value, if
+        //    the rotation and input psImage to not correspond to the output image.
+
+        psS32 rows = 64;
+        psS32 cols = 64;
+        psImage *fOut = psImageAlloc(cols,rows,PS_TYPE_F32);
+        psImage *sOut = NULL;
+        psImage *fBiOut = psImageAlloc(cols,rows,PS_TYPE_F32);
+        psImage *sBiOut = NULL;
+        psImage *fTruth = NULL;
+        psImage *sTruth = NULL;
+        psImage *fBiTruth = NULL;
+        psImage *sBiTruth = NULL;
+        psImage *fImg = psImageAlloc(cols,rows,PS_TYPE_F32);
+        psImage *sImg = psImageAlloc(cols,rows,PS_TYPE_S16);
+
+        for(psS32 row=0;row<rows;row++) {
+            psF32 *fRow = fImg->data.F32[row];
+            psS16 *sRow = sImg->data.S16[row];
+            for (psS32 col=0;col<cols;col++) {
+                fRow[col] = (psF32)(row)+(psF32)(col)/100.0f;
+                sRow[col] = row-2*col;
+            }
+        }
+
+        // since interpolation is involved, etc., the simplist way to verify things
+        // is to verify the results manually and bless it for automated comparison
+        // thereafter
+
+        // write results of various rotates to a file and verify with truth images
+        system("mkdir temp");
+        psS32 index = 0;
+        psBool fail = false;
+        psF32 radianRot;
+
+        psFits *fOutFile = psFitsOpen("fOut.fits","w");
+        psFits *sOutFile = psFitsOpen("sOut.fits","w");
+        psFits *fBiOutFile = psFitsOpen("fBiOut.fits","w");
+        psFits *sBiOutFile = psFitsOpen("sBiOut.fits","w");
+        ok(fOutFile != NULL && sOutFile != NULL && fBiOutFile != NULL && sBiOutFile != NULL,
+           "psFitsOpen() created the output files");
+
+//            psFits *fTruthFile = psFitsOpen("imageops/verified/fOut.fits","r");
+//            psFits *sTruthFile = psFitsOpen("imageops/verified/sOut.fits","r");
+//            psFits *fBiTruthFile = psFitsOpen("imageops/verified/fBiOut.fits","r");
+//            psFits *sBiTruthFile = psFitsOpen("imageops/verified/sBiOut.fits","r");
+        psFits *fTruthFile = psFitsOpen("verified/fOut.fits","r");
+        psFits *sTruthFile = psFitsOpen("verified/sOut.fits","r");
+        psFits *fBiTruthFile = psFitsOpen("verified/fBiOut.fits","r");
+        psFits *sBiTruthFile = psFitsOpen("verified/sBiOut.fits","r");
+        ok(fTruthFile != NULL && sTruthFile != NULL && fBiTruthFile != NULL && sBiTruthFile != NULL,
+
+           "psFitsOpen() opened the truth files");
+
+        psRegion regionAll = psRegionSet(0,0,0,0);
+        for (psS32 rot=-180;rot<=180;rot+=45)
+        {
+            psImage *oldOut = fOut;
+            psImage *oldBiOut = fBiOut;
+            if (rot == 90) {
+                radianRot = M_PI_2;
+            } else if (rot == -90) {
+                radianRot = M_PI+M_PI_2;
+            } else if (rot == 180 || rot == -180) {
+                radianRot = M_PI;
+            } else {
+                radianRot = ((float)rot)*M_PI/180.0;
+            }
+
+            fOut = psImageRotate(fOut,fImg,radianRot,-1.0,PS_INTERPOLATE_FLAT);
+            fBiOut = psImageRotate(fBiOut,fImg,radianRot,-1.0,PS_INTERPOLATE_BILINEAR);
+            // Verify the returned psImage structure is equal to the input
+            // parameter out if provided.
+            ok(fOut != NULL, "psImageRotate() returned non-NULL (psImageRotate(), float image, with FLAT interpolation)");
+            ok(oldOut == fOut, "psImageRotate(): the output recycle functionality was successful");
+            ok(fBiOut != NULL, "psImageRotate() returned non-NULL (psImageRotate(), float image, with BILINEAR interpolation)");
+            ok(oldBiOut == fBiOut, "psImageRotate(): the output recycle functionality was successful");
+            sOut = psImageRotate(sOut,sImg,radianRot,-1.0,PS_INTERPOLATE_FLAT);
+            ok(sOut != NULL, "psImageRotate() returned non-NULL (psImageRotate(), short image, with FLAT interpolation)");
+            sBiOut = psImageRotate(sBiOut,sImg,radianRot,-1.0,PS_INTERPOLATE_BILINEAR);
+            ok(sBiOut != NULL, "psImageRotate() returned non-NULL (psImageRotate(), short image, with BILINEAR interpolation)");
+            ok(psFitsWriteImage(fOutFile, NULL, fOut, 1, NULL), "psFitsWriteImage() successful");
+            ok(psFitsWriteImage(sOutFile, NULL, sOut, 1, NULL), "psFitsWriteImage() successful");
+            ok(psFitsWriteImage(fBiOutFile, NULL, fBiOut, 1, NULL), "psFitsWriteImage() successful");
+            ok(psFitsWriteImage(sBiOutFile, NULL, sBiOut, 1, NULL), "psFitsWriteImage() successful");
+
+            // now, let's compare this with the verified file
+            psFitsMoveExtNum(fTruthFile, index, false);
+            psFitsMoveExtNum(sTruthFile, index, false);
+            psFitsMoveExtNum(fBiTruthFile, index, false);
+            psFitsMoveExtNum(sBiTruthFile, index, false);
+            psFree(fTruth);
+            psFree(sTruth);
+            psFree(fBiTruth);
+            psFree(sBiTruth);
+            fTruth = NULL;
+            sTruth = NULL;
+            fBiTruth = NULL;
+            sBiTruth = NULL;
+            fTruth = psFitsReadImage(fTruthFile, regionAll, 0);
+            sTruth = psFitsReadImage(sTruthFile, regionAll, 0);
+            fBiTruth = psFitsReadImage(fBiTruthFile, regionAll, 0);
+            sBiTruth = psFitsReadImage(sBiTruthFile, regionAll, 0);
+            bool errorFlag = false;
+            if (fTruth == NULL) {
+                diag("verified psF32 image failed to be read (%d deg. rotation)", rot);
+                errorFlag = true;
+                fail = true;
+            } else {
+                if(fTruth->numRows != fOut->numRows || fTruth->numCols != fOut->numCols) {
+                    diag("Rotated float image size did not equal truth image for %d deg rotation (%dx%d vs %dx%d)",
+                         rot,fOut->numCols,fOut->numRows,fTruth->numCols,fTruth->numRows);
+                    errorFlag = true;
+                } else {
+                    for (psS32 row=0;row<fTruth->numRows;row++) {
+                        psF32 *truthRow = fTruth->data.F32[row];
+                        psF32 *outRow = fOut->data.F32[row];
+                        for (psS32 col=0;col<fTruth->numCols;col++) {
+                            if (fabsf(truthRow[col]-outRow[col]) > 1) {
+                                if (VERBOSE) diag("Float Image mismatch (%f vs %f) at %d,%d",
+                                     outRow[col], truthRow[col],col,row);
+                                errorFlag = true;
+                            }
+                        }
+                    }
+                }
+            }
+            ok(!errorFlag, "psImageRotate() produced the correct data values (%d degree rotation), float images, FLAT interpolation", rot);
+
+
+            errorFlag = false;
+            if (sTruth == NULL) {
+                diag("verified psS16 image failed to be read (%d deg. rotation)",rot);
+                errorFlag = true;
+            } else {
+                if (sTruth->numRows != sOut->numRows ||
+                        sTruth->numCols != sOut->numCols) {
+                    diag("Rotated psS16 image size did not match truth image for %d deg rotation",rot);
+                    errorFlag = true;
+                } else {
+                    for (psS32 row=0;row<sTruth->numRows;row++) {
+                        psS16 *truthRow = sTruth->data.S16[row];
+                        psS16 *outRow = sOut->data.S16[row];
+                        for (psS32 col=0;col<sTruth->numCols;col++) {
+                            if (fabsf(truthRow[col]-outRow[col]) > 1) {
+                                if (VERBOSE) diag("Short Image mismatch (%d vs %d) at %d,%d",
+                                     outRow[col], truthRow[col],col,row);
+                                errorFlag = true;
+                            }
+                        }
+                    }
+                }
+            }
+            ok(!errorFlag, "psImageRotate() produced the correct data values (%d degree rotation), short images, FLAT interpolation", rot);
+            errorFlag = false;
+            if (fBiTruth == NULL) {
+                diag("verified psF32 Bi image failed to be read (%d deg. rotation)",
+                     rot);
+                errorFlag = true;
+            } else {
+                if (fBiTruth->numRows != fBiOut->numRows || fBiTruth->numCols != fBiOut->numCols) {
+                    diag("Rotated float image size did not match truth "
+                         "image for %d deg rotation (%dx%d vs %dx%d). BILINEAR",
+                         rot,fBiOut->numCols,fBiOut->numRows,fBiTruth->numCols,fBiTruth->numRows);
+                    errorFlag = true;
+                } else {
+                    for (psS32 row=0;row<fBiTruth->numRows;row++) {
+                        psF32 *truthRow = fBiTruth->data.F32[row];
+                        psF32 *outRow = fBiOut->data.F32[row];
+                        for (psS32 col=0;col<fBiTruth->numCols;col++) {
+                            if (fabsf(truthRow[col]-outRow[col]) > 1) {
+                                if (VERBOSE) diag("Float Image mismatch (%f vs %f) at %d,%d. BILINEAR",
+                                     outRow[col], truthRow[col],col,row);
+                                errorFlag = true;
+                            }
+                        }
+                    }
+                }
+            }
+            ok(!errorFlag, "psImageRotate() produced the correct data values (%d degree rotation), float images, BILINEAR interpolation", rot);
+            if (sBiTruth == NULL) {
+                diag("verified psS16 image failed to be read "
+                     "(%d deg. rotation) BILINEAR",rot);
+                errorFlag = true;
+                fail = true;
+            } else {
+                if (sBiTruth->numRows != sBiOut->numRows ||
+                        sBiTruth->numCols != sBiOut->numCols) {
+                    diag("Rotated psS16 image size did not match truth "
+                         "image for %d deg rotation. BILINEAR",rot);
+                    errorFlag = true;
+                } else {
+                    for (psS32 row=0;row<sBiTruth->numRows;row++) {
+                        psS16 *truthRow = sBiTruth->data.S16[row];
+                        psS16 *outRow = sBiOut->data.S16[row];
+                        for (psS32 col=0;col<sBiTruth->numCols;col++) {
+                            if (fabsf(truthRow[col]-outRow[col]) > 1) {
+                                if (VERBOSE) diag("Short Image mismatch (%d vs %d) "
+                                     "at %d,%d. BILINEAR",
+                                     outRow[col], truthRow[col],col,row);
+                                errorFlag = true;
+                            }
+                        }
+                    }
+                }
+            }
+            ok(!errorFlag, "psImageRotate() produced the correct data values (%d degree rotation), short images, BILINEAR interpolation", rot);
+
+            index++;
+        }
+//HERE
+        // Verify the returned psImage structure pointer is NULL and program
+        // execution doesn't stop, if the input parameter input is NULL.
+        // Following should be an error
+        // XXX: Verify error
+        fOut = psImageRotate(fOut,NULL,0,0,PS_INTERPOLATE_FLAT);
+        ok(fOut == NULL, "NULL was returned when the input image was NULL");
+
+        // Verify the returned psImage structure pointer is NULL and program
+        // execution doesn't stop, if the specified interpolation mode is unallowed
+        // Following should be an error for unallowed interpolation type
+        // XXX: Verify error
+        fOut = psImageRotate(fOut, fImg, 33, 0, -1);
+        ok(fOut == NULL, "NULL was returned when the interpolation mode is unallowed");
+
+        psFree(sOut);
+        psFree(fImg);
+        psFree(sImg);
+        psFree(fTruth);
+        psFree(sTruth);
+        psFree(sBiOut);
+        psFree(fBiTruth);
+        psFree(sBiTruth);
+        psFree(fBiOut);
+
+        psFree(fOutFile);
+        psFree(sOutFile);
+        psFree(fBiOutFile);
+        psFree(sBiOutFile);
+
+        psFree(fTruthFile);
+        psFree(sTruthFile);
+        psFree(fBiTruthFile);
+        psFree(sBiTruthFile);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageShift()
+    if (1) {
+        psMemId id = psMemGetId();
+        // This functions shall generate a new psImage structure by shifting the
+        // input psImage structure a specified number of pixels in the horizontal
+        // and/or vertical directions.
+        //
+        // Verify the returned psImage structure contains expected values, if the
+        // input psImage structure contains known values and a know shift in the
+        // vertical and/or horizontal directions. Cases should include no shift,
+        // vertical only(up,down), horizontal only(right, left), and combination
+        // shift. Cases should include fractional shifts. Comparison of expected
+        // values should include a delta to allow for testing on different
+        // platforms.
+        //
+        // Verify the returned psImage structure contains values for pixels not in
+        // the original image set to the input parameter exposed.
+
+        // integer shift
+        if (1)
+        {
+            ok(testImageShiftCase(64,128,0.0f,0.0f), "psImageShift (0, 0)");
+            ok(testImageShiftCase(64,128,0.0f,2.0f), "psImageShift (0, 16)");
+            ok(testImageShiftCase(64,128,0.0f,-16.0f), "psImageShift (0, -16)");
+            ok(testImageShiftCase(64,128,32.0f,0.0f), "psImageShift (32, 0)");
+            ok(testImageShiftCase(64,128,-32.0f,0.0f), "psImageShift (-32, 0)");
+            ok(testImageShiftCase(64,128,32.0f,16.0f), "psImageShift (32, 16)");
+            ok(testImageShiftCase(64,128,32.0f,-16.0f), "psImageShift (32, -16)");
+            ok(testImageShiftCase(64,128,-32.0f,16.0f), "psImageShift (-32, 16)");
+            ok(testImageShiftCase(64,128,-32.0f,-16.0f), "psImageShift (-32, -16)");
+
+            // fractional shift
+            ok(testImageShiftCase(64,128,0.0f,16.4f), "psImageShift (0, 16.4)");
+            ok(testImageShiftCase(64,128,0.0f,-16.4f), "psImageShift (0, -16.4)");
+            ok(testImageShiftCase(64,128,32.7f,0.0f), "psImageShift (32.7, 0)");
+            ok(testImageShiftCase(64,128,-32.7f,0.0f), "psImageShift (-32.7, 0)");
+            ok(testImageShiftCase(64,128,32.6f,16.2f), "psImageShift (32.6, 16.2)");
+            ok(testImageShiftCase(64,128,32.6f,-16.2f), "psImageShift (32.6, -16.2)");
+            ok(testImageShiftCase(64,128,-32.6f,16.2f), "psImageShift (-32.6, 16.2)");
+            ok(testImageShiftCase(64,128,-32.6f,-16.2f), "psImageShift (-32.6, -16.2)");
+        }
+
+        // Verify the returned psImage structure pointer is equal to the input
+        // parameter out if provided.
+        psImage *fImg = psImageAlloc(32,32,PS_TYPE_F32);
+        psImage *fRecycle = psImageAlloc(32,32,PS_TYPE_F32);
+        psImage *fOut = psImageShift(fRecycle, fImg, 8,8, NAN, PS_INTERPOLATE_FLAT);
+        ok(fRecycle == fOut, "psImageShift did recycle my image");
+
+        // Verify the returned psImage structure pointer is NULL and program
+        // execution doesn't stop, if the input psImage structure pointer is NULL.
+        // Following should be an error
+        // XXX: Verify error
+        fOut = psImageShift(fOut,NULL,8,8,NAN,PS_INTERPOLATE_FLAT);
+        ok(fOut == NULL, "psImageShift did return NULL given a NULL input image");
+
+        // Verify the returned psImage structure is NULL and program execution
+        // doesn't stop, if the specified interpolation mode is unallowed.
+        // Following should be an error for unallowed interpolation mode
+        // XXX: Verify error
+        fOut = psImageShift(fOut,fImg,8,8,NAN,-1);
+        ok(fOut == NULL, "psImageShift did return NULL given an unallowed interpolation mode");
+
+        psFree(fImg);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test psImageResample()
+    if (1) {
+        psMemId id = psMemGetId();
+        psS32 rows = 60;
+        psS32 cols = 80;
+        psImage *result = NULL;
+        psS32 scale = 4;
+        psErr *err;
+
+        psImage *image = psImageAlloc(cols, rows, PS_TYPE_F32);
+        for(psS32 row=0;row<rows;row++) {
+            for (psS32 col=0;col<cols;col++) {
+                image->data.F32[row][col] = row+2*col;
+            }
+        }
+        result = psImageCopy(NULL,image,PS_TYPE_F64);
+        psImage *orig = result;
+        result = psImageResample(result,image,scale,PS_INTERPOLATE_FLAT);
+        ok(result != NULL, "psImageResample() returned non-NULL");
+        ok(result == orig, "psImageResample() recycled image");
+        ok(result->type.type == PS_TYPE_F32, "psImageResample() produced the correct type");
+        ok(result->numCols == image->numCols*scale && result->numRows == image->numRows*scale,
+           "psImageResample() produced the correct size");
+
+        bool errorFlag = false;
+        {
+            psImageInterpolateOptions *tmpIntOpts = psImageInterpolateOptionsAlloc(
+                PS_INTERPOLATE_FLAT, image, NULL, NULL, 0, -1.0, NAN, 0, 0, 0.0);
+            double imgVal;
+            double varVal;
+            psMaskType maskVal;
+            psF32 truthValue;
+            for(psS32 row=0;row<result->numRows;row++) {
+                for (psS32 col=0;col<result->numCols;col++) {
+//                    truthValue = psImagePixelInterpolate(image,
+//                                                         (float)col/(float)scale,(float)row/(float)scale,
+//                                                         NULL,0,-1,PS_INTERPOLATE_FLAT);
+                    psImageInterpolate(&imgVal, &varVal, &maskVal, 
+                                       (float)col/(float)scale,(float)row/(float)scale,
+                                       tmpIntOpts);
+
+                    if (fabs(truthValue - result->data.F32[row][col]) > FLT_EPSILON) {
+                        if (VERBOSE) diag("value bad at (%d,%d).  Got %g, expected %g",
+                             col,row,result->data.F32[row][col], truthValue);
+                        errorFlag = true;
+                    }
+                }
+            }
+            psFree(tmpIntOpts);
+        }
+
+        ok(!errorFlag, "psImageResample() produced the correct data values");
+
+        // verify that image=NULL is handled properly.
+        psErrorClear();
+        result = psImageResample(result,NULL,scale,PS_INTERPOLATE_FLAT);
+        ok(result == NULL, "psImageResample() returned NULL with NULL input");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL, "psImageResample() produced the correct error message");
+        psFree(err);
+
+        // verify that scale < 1 is handled properly
+        psErrorClear();
+        result = psImageResample(result,image,0,PS_INTERPOLATE_FLAT);
+        ok(result == NULL, "psImageResample() returned NULL with scale < 1");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_VALUE, "psImageResample() produced the correct error message");
+        psFree(err);
+
+        // verify that unallowed interpolation mode is handled properly
+        psErrorClear();
+        result = psImageResample(result,image,2,-1);
+        ok(result == NULL, "psImageResample() returned NULL with unallowed interpolation mode");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_VALUE, "psImageResample() produced the correct error message");
+        psFree(err);
+
+        // Verify that that an unallowed image type is handled properly
+        psErrorClear();
+        psImage *invImage = psImageAlloc(cols,rows,PS_TYPE_BOOL);
+        memset(invImage->p_rawDataBuffer,0,cols*rows*PSELEMTYPE_SIZEOF(PS_TYPE_BOOL)); // make sure the image is of all NULLs
+        result = psImageResample(result,invImage,2,PS_INTERPOLATE_FLAT);
+        ok(result == NULL, "psImageResample() returned NULL with unallowed image type");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_TYPE, "psImageResample() produced the correct error message");
+        psFree(err);
+
+        psFree(image);
+        psFree(result);
+        psFree(invImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test psImageTransform()
+    if (1) {
+        psMemId id = psMemGetId();
+        int cols = 16;
+        int rows = 32;
+
+        psPlaneTransform *trans = psPlaneTransformAlloc(2,2);
+        trans->x->coeff[1][0] = 0.5;
+        trans->y->coeff[0][1] = 1.5;
+
+        psImage *in = psImageAlloc(cols,rows,PS_TYPE_F32);
+        for (psS32 row=0;row<rows;row++) {
+            for (psS32 col=0;col<cols;col++) {
+                in->data.F32[row][col] = (psF32)row+(psF32)col/1000.0f;
+            }
+        }
+        P_PSIMAGE_SET_COL0(in, 1);
+        psImage *out = psImageTransform(NULL, NULL, in, NULL, 0, trans,
+                                        psRegionSet(1,1+cols*2,0,rows*2), NULL,
+                                        PS_INTERPOLATE_FLAT, -1);
+
+        ok(out != NULL, "psImageTransform() returned non-NULL");
+        ok(out->type.type == PS_TYPE_F32, "psImageTransform() produced the correct type");
+        ok(out->numRows == rows*2 && out->numCols == cols*2, "psImageTransform() produced the correct size");
+
+        if (1) {
+            psMemId id = psMemGetId();
+            psImageInterpolateOptions *tmpIntOpts = psImageInterpolateOptionsAlloc(
+                PS_INTERPOLATE_FLAT, in, NULL, NULL, 0, -1.0, NAN, 0, 0, 0.0);
+
+            double imgVal;
+            double varVal;
+            psMaskType maskVal;
+            bool errorFlag = false;
+            for (psS32 row=0;row<out->numRows;row++) {
+                for (psS32 col=0;col<cols;col++) {
+                    psImageInterpolate(&imgVal, &varVal, &maskVal, 
+                                       col*trans->x->coeff[1][0]+trans->x->coeff[0][0]+1,
+                                       row*trans->y->coeff[0][1]+trans->y->coeff[0][0],
+                                       tmpIntOpts);
+                    float inValue = imgVal;
+                    if (fabsf(out->data.F32[row][col] - inValue) > 0.01) {
+                        diag("out at %d,%d was %g, expected %g", col,row,
+                              out->data.F32[row][col], inValue);
+                        errorFlag = true;
+                    }
+                }
+            }
+            psFree(tmpIntOpts);
+            ok(!errorFlag, "psImageTransform() produced the correct data values");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(out);
+        psFree(in);
+        psFree(trans);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageInterpolate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageInterpolate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageInterpolate.c	(revision 22322)
@@ -0,0 +1,327 @@
+/** @file tst_psImageInterpolate.c
+ *
+ * @brief Contains the tests for psImagePixelInterpolate
+ *
+ * @author Eric Van Alst, MHPCC
+ *
+ * @version $Revision: 1.3 $
+ *          $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-14 21:20:28 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define GENIMAGE(img,c,r,TYP,valueFcn) \
+img = psImageAlloc(c,r,PS_TYPE_##TYP); \
+for(psU32 row=0; row<r; row++) { \
+    ps##TYP* imgRow = img->data.TYP[row]; \
+    for(psU32 col=0; col<c; col++) { \
+        imgRow[col] = (ps##TYP)(valueFcn); \
+    } \
+}
+
+#define CHECK_INTERP_VALUE(img,x,y,mask,maskval,exposed,TYPE,expected) \
+{ \
+    psF32 val = (psF32)psImagePixelInterpolate(img,x,y,mask,maskval,exposed,PS_INTERPOLATE_##TYPE); \
+    ok(fabsf(val-expected)<FLT_EPSILON, "psImagePixelInterpolate() correct"); \
+}
+
+#define CHECK_INTERP_BY_TYPE(TYPE) \
+{ \
+    psImage* img1; \
+    GENIMAGE(img1,10,10,TYPE,row+col) \
+    CHECK_INTERP_VALUE(img1,1.9,1.6,NULL,0,0,FLAT,2.0) \
+    CHECK_INTERP_VALUE(img1,4.0,2.0,NULL,0,0,BILINEAR,5.0) \
+    psFree(img1); \
+}
+
+#define CHECK_INTERP_BY_TYPE_ORIG(TYPE) \
+{ \
+    psImage* img1; \
+    GENIMAGE(img1,10,10,TYPE,row+col) \
+    CHECK_INTERP_VALUE(img1,1.9,1.6,NULL,0,0,FLAT,2.0) \
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(53);
+
+    // testInterpolateFlatBilinear(void)
+    // Perform simple(four neighbor) FLAT interpolation for all types
+    // Perform bilinear interpolation for all types
+    CHECK_INTERP_BY_TYPE(S8)
+    CHECK_INTERP_BY_TYPE(S16)
+    CHECK_INTERP_BY_TYPE(S32)
+    CHECK_INTERP_BY_TYPE(S64)
+    CHECK_INTERP_BY_TYPE(U8)
+    CHECK_INTERP_BY_TYPE(U16)
+    CHECK_INTERP_BY_TYPE(U32)
+    CHECK_INTERP_BY_TYPE(U64)
+    CHECK_INTERP_BY_TYPE(F32)
+    CHECK_INTERP_BY_TYPE(F64)
+
+    // testInterpolateError(void)
+    // Perform interpolation with NULL input image
+    // Following should generate an error message
+    // XXX: we do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psF32 val = psImagePixelInterpolate(NULL,1.2,1.2,NULL,0,5.0,PS_INTERPOLATE_FLAT);
+        ok(val == 5.0, "psImagePixelInterpolate() returned the unexposed value (NULL image)");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Perform interpolation with unallowed input image type
+    // Following should generate an error message
+    // XXX: we do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psImage *img1 = psImageAlloc(10,10,PS_TYPE_BOOL);
+        psF32 val = psImagePixelInterpolate(img1,1.2,1.2,NULL,0,10.0,PS_INTERPOLATE_FLAT);
+        ok(val == 10.0, "psImagePixelInterpolate() returned the unexposed value (unallowed image type)");
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Perform interpolation with unallowed method
+    // Following should generate an error message
+    // XXX: we do not test the error generation
+    {
+        psMemId id = psMemGetId();
+        psImage *img1 = psImageAlloc(10,10,PS_TYPE_BOOL);
+        psF32 val = psImagePixelInterpolate(img1,1.2,1.2,NULL,0,10.0,PS_INTERPOLATE_BILINEAR+1);
+        ok(val == 10.0, "psImagePixelInterpolate() returned the unexposed value (unallowed interpolation method)");
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // The following assumptions about the test image are:
+    //
+    //                     col
+    //             0   1   2   3   4   5   6   7  ...
+    //
+    // row 0       0   1   2   3   4   5   6   7
+    // row 1       1   2   3   4   5   6   7   8
+    // row 2       2   3   4   5   6   7   8   9
+    // ...
+    // testInterpolateMaskFlatBilinear(void)
+    psImage* img1 = NULL;
+    psImage* msk1 = NULL;
+
+    // Perform interpolate with mask and mask value set at nearest pixel to verify
+    // the unexposed value is return
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][1] = 1;
+        CHECK_INTERP_VALUE(img1,1.9,1.6,msk1,1,10.0,FLAT,10.0)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask and one mask value for bilinear (upper left pixel)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][3] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.25)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask and one mask value for bilinear (upper right pixel)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][4] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,4.75)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask and one mask value for bilinear (lower left pixel)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[2][3] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.25)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask and one mask value for bilinear (lower right pixel)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[2][4] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,4.75)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask and one mask value for bilinear (upper pixels)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][3] = 1;
+        msk1->data.U8[1][4] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.5)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask and one mask value for bilinear (lower pixels)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[2][3] = 1;
+        msk1->data.U8[2][4] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,4.5)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask and one mask value for bilinear (left pixels)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][3] = 1;
+        msk1->data.U8[2][3] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.5)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask and one mask value for bilinear (right pixels)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][4] = 1;
+        msk1->data.U8[2][4] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,4.5)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask only one valid pixel (upper left)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][4] = 1;
+        msk1->data.U8[2][3] = 1;
+        msk1->data.U8[2][4] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,4.0)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask only one valid pixel (upper right)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][3] = 1;
+        msk1->data.U8[2][3] = 1;
+        msk1->data.U8[2][4] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.0)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask only one valid pixel (lower left)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][3] = 1;
+        msk1->data.U8[1][4] = 1;
+        msk1->data.U8[2][4] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.0)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask only one valid pixel (lower right)
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][3] = 1;
+        msk1->data.U8[1][4] = 1;
+        msk1->data.U8[2][3] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,6.0)
+        psFree(msk1);
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Perform interpolate with mask no valid pixels
+    {
+        psMemId id = psMemGetId();
+        GENIMAGE(img1,10,10,F32,row+col)
+        GENIMAGE(msk1,10,10,U8,0)
+        msk1->data.U8[1][3] = 1;
+        msk1->data.U8[1][4] = 1;
+        msk1->data.U8[2][3] = 1;
+        msk1->data.U8[2][4] = 1;
+        CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,0.0)
+        psFree(msk1);
+        psFree(img1);
+        return 0;
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Perform interpolation for a 1D image
+    {
+        psMemId id = psMemGetId();
+        psImage* img1 = NULL;
+        GENIMAGE(img1,10,1,F32,row+col)
+        CHECK_INTERP_VALUE(img1,4.0,0.0,NULL,0,100.0,BILINEAR,3.5)
+        psFree(img1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageInterpolate2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageInterpolate2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageInterpolate2.c	(revision 22322)
@@ -0,0 +1,165 @@
+#include <stdio.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+#define NUM_POINTS 32
+
+// Function to define mask values
+psMaskType maskFunc(float x, float y)
+{
+    return 0;
+}
+
+// Function to define variance values
+double varianceFunc(float x, float y)
+{
+    return sqrtf(10.0 + 0.1 * x + 0.2 * y);
+}
+
+// Function to define image values
+double imageFunc(float x, float y)
+{
+    return 0.01 * x + 0.02 * y;
+}
+
+// Generate a dummy mask image
+psImage *generateMask(int numCols, int numRows)
+{
+    psImage *image = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+    for (int j = 0; j < numRows; j++) {
+        for (int i = 0; i < numCols; i++) {
+            psImageSet(image, i, j, maskFunc(i + 0.5, j + 0.5));
+        }
+    }
+    return image;
+}
+
+// Generate a dummy variance image
+psImage *generateVariance(int numCols, int numRows, psElemType type)
+{
+    psImage *image = psImageAlloc(numCols, numRows, type);
+    for (int j = 0; j < numRows; j++) {
+        for (int i = 0; i < numCols; i++) {
+            psImageSet(image, i, j, varianceFunc(i + 0.5, j + 0.5));
+        }
+    }
+    return image;
+}
+
+// Generate a dummy image
+psImage *generateImage(int numCols, int numRows, psElemType type)
+{
+    psImage *image = psImageAlloc(numCols, numRows, type);
+    for (int j = 0; j < numRows; j++) {
+        for (int i = 0; i < numCols; i++) {
+            psImageSet(image, i, j, imageFunc(i + 0.5, j + 0.5));
+        }
+    }
+    return image;
+}
+
+// Check a particular combination of image and interpolation parameters
+// Each has 2 + 2*num tests
+void check(int xSize, int ySize,        // Size of image
+           psElemType type,             // Type for image
+           int num,                     // Number of samples over image
+           psImageInterpolateMode mode, // Interpolation mode
+           int xKernel, int yKernel,    // Size of interpolation kernel
+           float tol                    // Tolerance
+    )
+{
+
+    psMemId id = psMemGetLastId();
+
+    psImage *image = generateImage(xSize, ySize, type);
+    psImage *variance = NULL; // generateVariance(xSize, ySize, type);
+    psImage *mask = NULL; // generateMask(xSize, ySize);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 12345);
+
+    psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(mode, image,
+                                                                       variance, mask, 0, NAN, NAN,
+                                                                       0, 0, 0.0);
+    ok(interp, "Interpolation options set");
+    skip_start(!interp, 2 * num, "Interpolation options failed");
+
+    for (int i = 0; i < num; i++) {
+        float x = psRandomUniform(rng) * (xSize - 1);
+        float y = psRandomUniform(rng) * (ySize - 1);
+
+        // Values from interpolation
+        double imageVal, varianceVal;
+        psMaskType maskVal;
+
+        psImageInterpolateStatus status = psImageInterpolate(&imageVal, &varianceVal, &maskVal, x, y, interp);
+        ok(status != PS_INTERPOLATE_STATUS_ERROR, "Interpolation at %.2f,%.2f (%x)", x, y, status);
+        skip_start(status == PS_INTERPOLATE_STATUS_ERROR, 1, "Interpolation failed");
+
+        int xCentral, yCentral;         // Central pixel of interpolation
+        switch (mode) {
+          case PS_INTERPOLATE_BICUBE:
+          case PS_INTERPOLATE_GAUSS:
+            xCentral = x;
+            yCentral = y;
+            break;
+          default:
+            xCentral = floor(x - 0.5);
+            yCentral = floor(y - 0.5);
+            break;
+        }
+
+        if (xCentral - (xKernel - 1) / 2 < 0 || xCentral + xKernel / 2 > xSize - 1 ||
+            yCentral - (yKernel - 1) / 2 < 0 || yCentral + yKernel / 2 > ySize - 1) {
+            ok(status == PS_INTERPOLATE_STATUS_BAD || status == PS_INTERPOLATE_STATUS_POOR,
+               "Interpolation at border");
+        } else {
+            is_double_tol(imageVal, imageFunc(x, y), tol, "Interpolation = %f vs %f",
+                          imageVal, imageFunc(x, y));
+        }
+
+        // Variance is very very very difficult to test without having the answer already
+        // Mask might be a bit easier.
+        // Defer these for now.
+
+        skip_end();
+    }
+
+    psFree(interp);
+    skip_end();
+
+    psFree(rng);
+    psFree(image);
+    psFree(variance);
+    psFree(mask);
+
+    ok(!psMemCheckLeaks(id, NULL, NULL, false), "No memory leaks");
+
+    return;
+}
+
+int main(int argc, char *argv[])
+{
+    plan_tests(792);
+
+    check(16, 16, PS_TYPE_F32, 32, PS_INTERPOLATE_BILINEAR, 2, 2, 1.0e-6); // 66 tests
+    check(16, 16, PS_TYPE_F64, 32, PS_INTERPOLATE_BILINEAR, 2, 2, 1.0e-6); // 66 tests
+
+    check(16, 16, PS_TYPE_F32, 32, PS_INTERPOLATE_BICUBE, 3, 3, 1.0e-6); // 66 tests
+    check(16, 16, PS_TYPE_F64, 32, PS_INTERPOLATE_BICUBE, 3, 3, 1.0e-6); // 66 tests
+
+    check(16, 16, PS_TYPE_F32, 32, PS_INTERPOLATE_GAUSS, 3, 3, 1.0e-3); // 66 tests
+    check(16, 16, PS_TYPE_F64, 32, PS_INTERPOLATE_GAUSS, 3, 3, 1.0e-3); // 66 tests
+
+    check(16, 16, PS_TYPE_F32, 32, PS_INTERPOLATE_LANCZOS2, 4, 4, 1.0e-3); // 66 tests
+    check(16, 16, PS_TYPE_F64, 32, PS_INTERPOLATE_LANCZOS2, 4, 4, 1.0e-3); // 66 tests
+
+    check(32, 32, PS_TYPE_F32, 32, PS_INTERPOLATE_LANCZOS3, 6, 6, 1.0e-3); // 66 tests
+    check(32, 32, PS_TYPE_F64, 32, PS_INTERPOLATE_LANCZOS3, 6, 6, 1.0e-3); // 66 tests
+
+    check(32, 32, PS_TYPE_F32, 32, PS_INTERPOLATE_LANCZOS4, 8, 8, 1.0e-3); // 66 tests
+    check(32, 32, PS_TYPE_F64, 32, PS_INTERPOLATE_LANCZOS4, 8, 8, 1.0e-3); // 66 tests
+
+    return exit_status();
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMap.c	(revision 22322)
@@ -0,0 +1,244 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int SaveImage (psMetadata *header, psImage *image, char *filename) {
+
+    psFits *fits = psFitsOpen (filename, "w");
+    psFitsWriteImage (fits, NULL, image, 0, NULL);
+    psFitsClose (fits);
+    return (TRUE);
+}
+
+# define C00 +0.5
+# define C01 +0.1
+# define C10 -0.2
+
+int main (void)
+{
+
+    // plan_tests(0);
+    
+    // *** tests to demonstrate the validity of the algorithm or concept ***
+
+    // make a model for a well-sampled field of a simple function (f = ax + by + c)
+    # if (0)
+    {
+	// function is defined over the range 0-1000, 0-1000
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 1000;
+	binning->nYfine = 1000;
+	binning->nXruff = 5;
+	binning->nYruff = 5;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *y = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *f = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+	for (int ix = 0; ix < 1000; ix += 20) {
+	    for (int iy = 0; iy < 1000; iy += 20) {
+		x->data.F32[x->n] = ix;
+		y->data.F32[y->n] = iy;
+		f->data.F32[f->n] = C00 + C10*ix + C01*iy;
+		psVectorExtend (x, 100, 1);
+		psVectorExtend (y, 100, 1);
+		psVectorExtend (f, 100, 1);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// allocate a map, but we have no field image to supply
+	// XXX this function needs to correct for the mean superpixel position 
+	// which is sampled...
+	psImageMapGenerate (map, x, y, f, 0.1);
+	psFree (binning);
+
+	fprintf (stderr, "nGood: %d\n", map->nGood);
+	fprintf (stderr, "nPoor: %d\n", map->nPoor);
+	fprintf (stderr, "nBad:  %d\n", map->nBad);
+	
+	SaveImage (NULL, map->map, "map.fits");
+	
+	psImage *field = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		field->data.F32[iy][ix] = C00 + C10*ix + C01*iy;
+	    }
+	}
+	SaveImage (NULL, field, "field.fits");
+
+	// measure difference between model (map) and data (field)
+	for (int ix = 0; ix < map->map->numCols; ix++) {
+	    for (int iy = 0; iy < map->map->numRows; iy++) {
+		int xo = psImageBinningGetFineX(map->binning, ix + 0.5);
+		int yo = psImageBinningGetFineY(map->binning, iy + 0.5);
+		float df = field->data.F32[yo][xo] - map->map->data.F32[iy][ix];
+		fprintf (stderr, "%d %d -> %d %d  :  %f = %f - %f\n", ix, iy, xo, yo, df, field->data.F32[yo][xo], map->map->data.F32[iy][ix]);
+	    }
+	}
+
+	psImage *model = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel (ix + 0.5, iy + 0.5, map->map, map->binning);
+	    }
+	}
+	SaveImage (NULL, model, "model.fits");
+    }
+    # endif
+
+    // make a model for a poorly-sampled field of a simple function (f = ax + by + c)
+
+    // choose a model scale for a poorly-sampled field of a simple function (f = ax + by + c)
+
+    // make a model for a well-sampled field of a high-order polynomial function (f = (a(x-xo)^2 + b(y-yo)^2) + c)
+    # if (0)
+    {
+	// function is defined over the range 0-1000, 0-1000
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 1000;
+	binning->nYfine = 1000;
+	binning->nXruff = 5;
+	binning->nYruff = 5;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *y = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *f = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+	for (int ix = 0; ix < 1000; ix += 20) {
+	    for (int iy = 0; iy < 1000; iy += 20) {
+		x->data.F32[x->n] = ix;
+		y->data.F32[y->n] = iy;
+		f->data.F32[f->n] = C10*PS_SQR(ix-500) + C01*PS_SQR(iy-500);
+		psVectorExtend (x, 100, 1);
+		psVectorExtend (y, 100, 1);
+		psVectorExtend (f, 100, 1);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// allocate a map, but we have no field image to supply
+	// XXX this function needs to correct for the mean superpixel position 
+	// which is sampled...
+	psImageMapGenerate (map, x, y, f, 0.1);
+	psFree (binning);
+
+	fprintf (stderr, "nGood: %d\n", map->nGood);
+	fprintf (stderr, "nPoor: %d\n", map->nPoor);
+	fprintf (stderr, "nBad:  %d\n", map->nBad);
+	
+	SaveImage (NULL, map->map, "map.fits");
+	
+	psImage *field = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		field->data.F32[iy][ix] = C10*PS_SQR(ix-500) + C01*PS_SQR(iy-500);
+	    }
+	}
+	SaveImage (NULL, field, "field.fits");
+
+	// measure difference between model (map) and data (field)
+	for (int ix = 0; ix < map->map->numCols; ix++) {
+	    for (int iy = 0; iy < map->map->numRows; iy++) {
+		int xo = psImageBinningGetFineX(map->binning, ix + 0.5);
+		int yo = psImageBinningGetFineY(map->binning, iy + 0.5);
+		float df = field->data.F32[yo][xo] - map->map->data.F32[iy][ix];
+		fprintf (stderr, "%d %d -> %d %d  :  %f = %f - %f\n", ix, iy, xo, yo, df, field->data.F32[yo][xo], map->map->data.F32[iy][ix]);
+	    }
+	}
+    }
+    # endif
+
+    // make a model for a well-sampled field of a high-order polynomial function (f = (a(x-xo)^2 + b(y-yo)^2) + c)
+    # if (1)
+    {
+	// function is defined over the range 0-1000, 0-1000
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 1000;
+	binning->nYfine = 1000;
+	binning->nXruff = 5;
+	binning->nYruff = 5;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *y = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *f = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+	for (int ix = 0; ix < 1000; ix += 20) {
+	    for (int iy = 0; iy < 1000; iy += 20) {
+		x->data.F32[x->n] = ix;
+		y->data.F32[y->n] = iy;
+		f->data.F32[f->n] = C10*(ix-500) + PS_SQR(C01*(iy-500));
+		psVectorExtend (x, 100, 1);
+		psVectorExtend (y, 100, 1);
+		psVectorExtend (f, 100, 1);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// allocate a map, but we have no field image to supply
+	// XXX this function needs to correct for the mean superpixel position 
+	// which is sampled...
+	psImageMapGenerate (map, x, y, f, NULL, 0.1);
+	psFree (binning);
+
+	fprintf (stderr, "nGood: %d\n", map->nGood);
+	fprintf (stderr, "nPoor: %d\n", map->nPoor);
+	fprintf (stderr, "nBad:  %d\n", map->nBad);
+	
+	SaveImage (NULL, map->map, "map.fits");
+	
+	psImage *field = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		field->data.F32[iy][ix] = C10*(ix-500) + PS_SQR(C01*(iy-500));
+	    }
+	}
+	SaveImage (NULL, field, "field.fits");
+
+	// measure difference between model (map) and data (field)
+	for (int ix = 0; ix < map->map->numCols; ix++) {
+	    for (int iy = 0; iy < map->map->numRows; iy++) {
+		int xo = psImageBinningGetFineX(map->binning, ix + 0.5);
+		int yo = psImageBinningGetFineY(map->binning, iy + 0.5);
+		float df = field->data.F32[yo][xo] - map->map->data.F32[iy][ix];
+		fprintf (stderr, "%d %d -> %d %d  :  %f = %f - %f\n", ix, iy, xo, yo, df, field->data.F32[yo][xo], map->map->data.F32[iy][ix]);
+	    }
+	}
+
+
+	psImage *model = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		// XXX the binned coordinates are probably off by 0.5 pix, or we are interpolating 
+		// to the wrong binned coordinate.  
+		// XXX fix edge cases
+		model->data.F32[iy][ix] = psImageUnbinPixel (ix + 0.5, iy + 0.5, map->map, map->binning);
+	    }
+	}
+	SaveImage (NULL, model, "model.fits");
+    }
+    # endif
+
+    // make a model for a poorly-sampled field of a non-polynomial function (f = (a(x-xo)^2 + b(y-yo)^2)^-1)
+
+    // choose a model scale for a poorly-sampled field of a non-polynomial function (f = (a(x-xo)^2 + b(y-yo)^2)^-1)
+
+}   
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMapFit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMapFit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMapFit.c	(revision 22322)
@@ -0,0 +1,843 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+// save function used to dump out test images while debugging algorithm
+int SaveImage (psMetadata *header, psImage *image, char *filename) {
+
+    psFits *fits = psFitsOpen (filename, "w");
+    psFitsWriteImage (fits, NULL, image, 0, NULL);
+    psFitsClose (fits);
+    return (TRUE);
+}
+
+# define TEST_4PT_0 1
+# define TEST_4PT_1 1
+# define TEST_4PT_2 1
+# define TEST_4PT_3 1
+# define TEST_6PT_0 1
+# define TEST_9PT_0 1
+# define TEST_9PT_1 1
+# define TEST_9PT_2 1
+# define TEST_9PT_3 1
+# define TEST_9PT_4 1
+# define TEST_9PT_5 1
+
+int main (void)
+{
+
+    plan_tests(219);
+    
+    // *** tests to demonstrate the validity of the algorithm or concept ***
+
+    // test with unconstrained cell: 3x3 grid fitted to 8 points with simple slope and scale difference
+    # if (TEST_9PT_5)
+    {
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 6;
+	binning->nYfine = 6;
+	binning->nXruff = 3;
+	binning->nYruff = 3;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (50, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (50, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (50, PS_TYPE_F32);
+
+	// the underlying field is f = ix + iy, where ix,iy are fine pixel coordinates
+	// place the measurement points exactly on the ruff reference pixel centers
+	int n = 0;
+	for (float ix = 1.0; ix < 6.0; ix += 2.0) {
+	    for (float iy = 1.0; iy < 6.0; iy += 2.0) {
+		if ((ix == 1.0) && (iy == 1.0)) continue;
+		x->data.F32[n] = ix;
+		y->data.F32[n] = iy;
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		n++;
+	    }
+	}
+	x->n = n;
+	y->n = n;
+	f->n = n;
+
+	psImage *field = psImageAlloc(6, 6, PS_TYPE_F32);
+	for (int ix = 0; ix < 6; ix++) {
+	    for (int iy = 0; iy < 6; iy++) {
+		field->data.F32[iy][ix] = (ix + 0.5) + (iy + 0.5);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		if ((ix < 3) && (iy < 3)) {
+		    is_float (model->data.F32[iy][ix], NAN, "model matches expected NaN");
+		} else {
+		    is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], 1e-5, "model matches inputs");
+		}
+	    }
+	}
+
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    // test for more points: 3x3 grid fitted to 9 points with simple slope and scale difference
+    # if (TEST_9PT_4)
+    {
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 6;
+	binning->nYfine = 6;
+	binning->nXruff = 3;
+	binning->nYruff = 3;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (50, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (50, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (50, PS_TYPE_F32);
+
+	// the underlying field is f = ix + iy, where ix,iy are fine pixel coordinates
+	// place the measurement points exactly on the ruff reference pixel centers
+	int n = 0;
+	for (float ix = 0.5; ix < 6.0; ix += 1.0) {
+	    for (float iy = 0.5; iy < 6.0; iy += 1.0) {
+		x->data.F32[n] = ix;
+		y->data.F32[n] = iy;
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		n++;
+	    }
+	}
+	x->n = n;
+	y->n = n;
+	f->n = n;
+
+	psImage *field = psImageAlloc(6, 6, PS_TYPE_F32);
+	for (int ix = 0; ix < 6; ix++) {
+	    for (int iy = 0; iy < 6; iy++) {
+		field->data.F32[iy][ix] = (ix + 0.5) + (iy + 0.5);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], 1e-5, "model matches inputs");
+	    }
+	}
+
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    // test for more points: 3x3 grid fitted to 9 points with simple slope and scale difference
+    # if (TEST_9PT_3)
+    {
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 6;
+	binning->nYfine = 6;
+	binning->nXruff = 3;
+	binning->nYruff = 3;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (50, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (50, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (50, PS_TYPE_F32);
+
+	// the underlying field is f = ix + iy, where ix,iy are fine pixel coordinates
+	// place the measurement points exactly on the ruff reference pixel centers
+	int n = 0;
+	for (float ix = 1.0; ix < 6; ix += 2.0) {
+	    for (float iy = 1.0; iy < 6; iy += 2.0) {
+		y->data.F32[n] = iy;
+		x->data.F32[n] = ix;
+		if ((ix == 1.0) && (iy == 1.0)) {
+		    x->data.F32[n] = ix - 0.1;
+		}
+		if ((ix == 3.0) && (iy == 1.0)) {
+		    y->data.F32[n] = iy - 0.1;
+		}
+		if ((ix == 5.0) && (iy == 3.0)) {
+		    x->data.F32[n] = ix + 0.1;
+		}
+		if ((ix == 3.0) && (iy == 5.0)) {
+		    y->data.F32[n] = iy + 0.1;
+		}
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		n++;
+	    }
+	}
+	x->n = n;
+	y->n = n;
+	f->n = n;
+
+	psImage *field = psImageAlloc(6, 6, PS_TYPE_F32);
+	for (int ix = 0; ix < 6; ix++) {
+	    for (int iy = 0; iy < 6; iy++) {
+		field->data.F32[iy][ix] = (ix + 0.5) + (iy + 0.5);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], 1e-5, "model matches inputs");
+	    }
+	}
+
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+	
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    // test for more points: 3x3 grid fitted to 9 points with simple slope and scale difference
+# if (TEST_9PT_2)
+    {
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 3;
+	binning->nYfine = 3;
+	binning->nXruff = 3;
+	binning->nYruff = 3;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (50, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (50, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (50, PS_TYPE_F32);
+
+	// the underlying field is f = ix + iy, where ix,iy are fine pixel coordinates
+	// place the measurement points exactly on the ruff reference pixel centers
+	int n = 0;
+	for (float ix = 0.0; ix < 3; ix += 1.0) {
+	    for (float iy = 0.0; iy < 3; iy += 1.0) {
+		x->data.F32[n] = ix + 0.5;
+		y->data.F32[n] = iy + 0.5;
+
+		if (ix == 0.0) {
+		    x->data.F32[n] -= 0.1;
+		}
+		if (iy == 0.0) {
+		    y->data.F32[n] -= 0.1;
+		}
+		if (ix == 2.0) {
+		    x->data.F32[n] += 0.1;
+		}
+		if (iy == 2.0) {
+		    y->data.F32[n] += 0.1;
+		}
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		n++;
+	    }
+	}
+	x->n = n;
+	y->n = n;
+	f->n = n;
+
+	psImage *field = psImageAlloc(3, 3, PS_TYPE_F32);
+	for (int ix = 0; ix < 3; ix++) {
+	    for (int iy = 0; iy < 3; iy++) {
+		field->data.F32[iy][ix] = (ix + 0.5) + (iy + 0.5);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], 1e-5, "model matches inputs");
+	    }
+	}
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+# endif
+
+    // still a simple test: 3x3 grid fitted to 9 points with simple slope and scale difference
+    # if (TEST_9PT_1)
+    {
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 6;
+	binning->nYfine = 6;
+	binning->nXruff = 3;
+	binning->nYruff = 3;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (9, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (9, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (9, PS_TYPE_F32);
+
+	// the underlying field is f = ix + iy, where ix,iy are fine pixel coordinates
+	// place the measurement points exactly on the ruff reference pixel centers
+	int n = 0;
+	for (int ix = 1; ix < 6; ix += 2) {
+	    for (int iy = 1; iy < 6; iy += 2) {
+		x->data.F32[n] = ix;
+		y->data.F32[n] = iy;
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		n++;
+	    }
+	}
+
+	psImage *field = psImageAlloc(6, 6, PS_TYPE_F32);
+	for (int ix = 0; ix < 6; ix++) {
+	    for (int iy = 0; iy < 6; iy++) {
+		field->data.F32[iy][ix] = (ix + 0.5) + (iy + 0.5);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], FLT_EPSILON, "model matches inputs");
+	    }
+	}
+
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    // very simple test: 3x3 grid fitted to 9 points with simple slope
+    # if (TEST_9PT_0)
+    {
+	// function is defined over the range 0-1000, 0-1000
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 3;
+	binning->nYfine = 3;
+	binning->nXruff = 3;
+	binning->nYruff = 3;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (9, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (9, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (9, PS_TYPE_F32);
+
+	int n = 0;
+	psImage *field = psImageAlloc(3, 3, PS_TYPE_F32);
+	for (int ix = 0; ix < 3; ix++) {
+	    for (int iy = 0; iy < 3; iy++) {
+		x->data.F32[n] = ix + 0.5;
+		y->data.F32[n] = iy + 0.5;
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		field->data.F32[iy][ix] = f->data.F32[n];
+		n++;
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], FLT_EPSILON, "model matches inputs");
+	    }
+	}
+
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    // test for more points: 3x3 grid fitted to 9 points with simple slope and scale difference
+    # if (TEST_6PT_0)
+    {
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 3;
+	binning->nYfine = 2;
+	binning->nXruff = 3;
+	binning->nYruff = 2;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (50, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (50, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (50, PS_TYPE_F32);
+
+	// the underlying field is f = ix + iy, where ix,iy are fine pixel coordinates
+	// place the measurement points exactly on the ruff reference pixel centers
+	int n = 0;
+	for (float ix = 0.5; ix < 3.0; ix += 1.0) {
+	    for (float iy = 0.5; iy < 2.0; iy += 1.0) {
+		// x->data.F32[n] = ix;
+		// y->data.F32[n] = iy;
+		if ((ix == 0.5) && (iy == 0.5)) {
+		    x->data.F32[n] = ix + 0.0;
+		    y->data.F32[n] = iy + 0.1; // add in both points.  
+		    // f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		    // n++;
+		    // x->data.F32[n] = ix + 0.0;
+		    // y->data.F32[n] = iy - 0.1;
+		} else {
+		    x->data.F32[n] = ix;
+		    y->data.F32[n] = iy;
+		}
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		n++;
+	    }
+	}
+	x->n = n;
+	y->n = n;
+	f->n = n;
+
+	psImage *field = psImageAlloc(3, 2, PS_TYPE_F32);
+	for (int ix = 0; ix < 3; ix++) {
+	    for (int iy = 0; iy < 2; iy++) {
+		field->data.F32[iy][ix] = (ix + 0.5) + (iy + 0.5);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], FLT_EPSILON, "model matches inputs");
+	    }
+	}
+
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    // more complex test: 2x2 grid fitted to 16 points with simple slope offset from grid centers
+    // this one uses points inset relative to the reference points so they are all visible
+    // to the reference
+    # if (TEST_4PT_3)
+    {
+	// function is defined over the range 0-1000, 0-1000
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 4;
+	binning->nYfine = 4;
+	binning->nXruff = 2;
+	binning->nYruff = 2;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (16, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (16, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (16, PS_TYPE_F32);
+
+	int n = 0;
+	// actual field is f = x + y, where x & y are the subpixel positions
+	for (float ix = 0.5; ix < 4; ix += 1.0) {
+	    for (float iy = 0.5; iy < 4; iy += 1.0) {
+		x->data.F32[n] = ix;
+		y->data.F32[n] = iy;
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		n++;
+	    }
+	}
+
+	psImage *field = psImageAlloc(4, 4, PS_TYPE_F32);
+	for (int ix = 0; ix < 4; ix++) {
+	    for (int iy = 0; iy < 4; iy++) {
+		field->data.F32[iy][ix] = (ix + 0.5) + (iy + 0.5);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], 10*FLT_EPSILON, "model matches inputs");
+	    }
+	}
+
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    // more complex test: 2x2 grid fitted to 4 points with simple slope offset from grid centers
+    // this one uses points inset relative to the reference points so they are all visible
+    // to the reference
+    # if (TEST_4PT_2)
+    {
+	// function is defined over the range 0-1000, 0-1000
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 2;
+	binning->nYfine = 2;
+	binning->nXruff = 2;
+	binning->nYruff = 2;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (5, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (5, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (5, PS_TYPE_F32);
+
+	int n = 0;
+	psImage *field = psImageAlloc(2, 2, PS_TYPE_F32);
+	// actual field is f = x + y, where x & y are the subpixel positions
+	for (int ix = 0; ix < 2; ix++) {
+	    for (int iy = 0; iy < 2; iy++) {
+		# if (1)
+		if (!ix && !iy) {
+		    x->data.F32[n] = ix + 0.5 - 0.1;
+		    y->data.F32[n] = iy + 0.5 - 0.0;
+// 		    f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+// 		    n++;
+// 		    x->data.F32[n] = ix + 0.5;
+//		    y->data.F32[n] = iy + 0.5 - 0.1;
+		} else {
+		    x->data.F32[n] = ix + 0.5;
+		    y->data.F32[n] = iy + 0.5;
+		}
+		# endif
+		# if (0)
+		// offset points from centers
+		if (ix) {
+		    x->data.F32[n] = ix + 0.5 - 0.1;
+		} else {
+		    x->data.F32[n] = ix + 0.5 + 0.1;
+		}
+		if (iy) {
+		    y->data.F32[n] = iy + 0.5 - 0.1;
+		} else {
+		    y->data.F32[n] = iy + 0.5 + 0.1;
+		}
+		# endif
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		field->data.F32[iy][ix] = ix + 0.5 + iy + 0.5;
+		n++;
+	    }
+	}
+	x->n = n;
+	y->n = n;
+	f->n = n;
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], 5*FLT_EPSILON, "model matches inputs");
+	    }
+	}
+
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    // still a simple test: 2x2 grid fitted to 4 points with simple slope and scale difference
+    # if (TEST_4PT_1)
+    {
+	// function is defined over the range 0-1000, 0-1000
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 4;
+	binning->nYfine = 4;
+	binning->nXruff = 2;
+	binning->nYruff = 2;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (4, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (4, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (4, PS_TYPE_F32);
+
+	// the underlying field is f = ix + iy, where ix,iy are fine pixel coordinates
+	int n = 0;
+	for (int ix = 1; ix < 4; ix += 2) {
+	    for (int iy = 1; iy < 4; iy += 2) {
+		x->data.F32[n] = ix;
+		y->data.F32[n] = iy;
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		n++;
+	    }
+	}
+
+	psImage *field = psImageAlloc(4, 4, PS_TYPE_F32);
+	for (int ix = 0; ix < 4; ix++) {
+	    for (int iy = 0; iy < 4; iy++) {
+		field->data.F32[iy][ix] = (ix + 0.5) + (iy + 0.5);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], FLT_EPSILON, "model matches inputs");
+	    }
+	}
+
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    // simplest possible test: 2x2 grid fitted to 4 points with simple slope
+    # if (TEST_4PT_0)
+    {
+	// function is defined over the range 0-1000, 0-1000
+        psMemId id = psMemGetId();
+
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 2;
+	binning->nYfine = 2;
+	binning->nXruff = 2;
+	binning->nYruff = 2;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAlloc (4, PS_TYPE_F32);
+	psVector *y = psVectorAlloc (4, PS_TYPE_F32);
+	psVector *f = psVectorAlloc (4, PS_TYPE_F32);
+
+	int n = 0;
+	psImage *field = psImageAlloc(2, 2, PS_TYPE_F32);
+	for (int ix = 0; ix < 2; ix++) {
+	    for (int iy = 0; iy < 2; iy++) {
+		x->data.F32[n] = ix + 0.5;
+		y->data.F32[n] = iy + 0.5;
+		f->data.F32[n] = x->data.F32[n] + y->data.F32[n];
+		field->data.F32[iy][ix] = f->data.F32[n];
+		n++;
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+
+	psImage *model = psImageAlloc(field->numCols, field->numRows, PS_TYPE_F32);
+	for (int ix = 0; ix < model->numCols; ix++) {
+	    for (int iy = 0; iy < model->numRows; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+		is_float_tol (model->data.F32[iy][ix], field->data.F32[iy][ix], FLT_EPSILON, "model matches inputs");
+	    }
+	}
+
+	// SaveImage (NULL, map->map, "map.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, model, "model.fits");
+
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    return exit_status();
+}   
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMapFit2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMapFit2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMapFit2.c	(revision 22322)
@@ -0,0 +1,257 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int SaveImage (psMetadata *header, psImage *image, char *filename) {
+
+    psFits *fits = psFitsOpen (filename, "w");
+    psFitsWriteImage (fits, NULL, image, 0, NULL);
+    psFitsClose (fits);
+    return (TRUE);
+}
+
+# define TEST1 0
+# define TEST2 1
+# define TEST3 0
+
+# define C00 +0.0
+# define C01 -2.0
+# define C10 +1.0
+
+int main (void)
+{
+
+    plan_tests(1);
+
+    // make a model for a well-sampled field of a simple function (f = ax + by + c)
+    # if (TEST1)
+    {
+        psMemId id = psMemGetId();
+
+	// function is defined over the range 0-1000, 0-1000
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 1000;
+	binning->nYfine = 1000;
+	binning->nXruff = 10;
+	binning->nYruff = 10;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *y = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *f = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+	for (int ix = 0; ix < 1000; ix += 20) {
+	    for (int iy = 0; iy < 1000; iy += 20) {
+		x->data.F32[x->n] = ix;
+		y->data.F32[y->n] = iy;
+		f->data.F32[f->n] = C00 + C10*ix + C01*iy;
+		psVectorExtend (x, 100, 1);
+		psVectorExtend (y, 100, 1);
+		psVectorExtend (f, 100, 1);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// fit the data to the map
+	psImageMapFit (map, x, y, f, NULL);
+
+	psImage *field = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		field->data.F32[iy][ix] = C00 + C10*ix + C01*iy;
+	    }
+	}
+
+	// measure difference between model (map) and data (field)
+	for (int ix = 0; ix < map->map->numCols; ix++) {
+	    for (int iy = 0; iy < map->map->numRows; iy++) {
+		int xo = psImageBinningGetFineX(map->binning, ix + 0.5);
+		int yo = psImageBinningGetFineY(map->binning, iy + 0.5);
+		is_float_tol (field->data.F32[yo][xo], map->map->data.F32[iy][ix], 1e-3, "model matches data");
+	    }
+	}
+
+	// XXX test on the model or don't bother
+	psImage *model = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+	    }
+	}
+
+	// SaveImage (NULL, model, "model.fits");
+	// SaveImage (NULL, field, "field.fits");
+	// SaveImage (NULL, map->map, "map.fits");
+	
+	psFree (model);
+	psFree (binning);
+	psFree (map);
+	psFree (stats);
+	psFree (field);
+	psFree (x);
+	psFree (y);
+	psFree (f);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    # endif
+
+    // make a model for a poorly-sampled field of a simple function (f = ax + by + c)
+
+    // choose a model scale for a poorly-sampled field of a simple function (f = ax + by + c)
+
+    // make a model for a well-sampled field of a high-order polynomial function (f = (a(x-xo)^2 + b(y-yo)^2) + c)
+    # if (TEST2)
+    {
+	// function is defined over the range 0-1000, 0-1000
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 1000;
+	binning->nYfine = 1000;
+	binning->nXruff = 5;
+	binning->nYruff = 5;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *y = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *f = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+	for (int ix = 0; ix < 1000; ix += 20) {
+	    for (int iy = 0; iy < 1000; iy += 20) {
+		x->data.F32[x->n] = ix + 0,5;
+		y->data.F32[y->n] = iy + 0.5;
+		f->data.F32[f->n] = PS_SQR(x->data.F32[x->n]) + PS_SQR(y->data.F32[x->n]);
+		psVectorExtend (x, 100, 1);
+		psVectorExtend (y, 100, 1);
+		psVectorExtend (f, 100, 1);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// allocate a map, but we have no field image to supply
+	// XXX this function needs to correct for the mean superpixel position 
+	// which is sampled...
+	psImageMapFit (map, NULL, 0, x, y, f, NULL);
+	psFree (binning);
+
+	SaveImage (NULL, map->map, "map.fits");
+	
+	psImage *field = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		field->data.F32[iy][ix] = PS_SQR(ix+0.5) + PS_SQR(iy+0.5);
+	    }
+	}
+	SaveImage (NULL, field, "field.fits");
+
+	// measure difference between model (map) and data (field)
+	for (int ix = 0; ix < map->map->numCols; ix++) {
+	    for (int iy = 0; iy < map->map->numRows; iy++) {
+		int xo = psImageBinningGetFineX(map->binning, ix + 0.5);
+		int yo = psImageBinningGetFineY(map->binning, iy + 0.5);
+		float df = field->data.F32[yo][xo] - map->map->data.F32[iy][ix];
+		fprintf (stderr, "%d %d -> %d %d  :  %f = %f - %f\n", ix, iy, xo, yo, df, field->data.F32[yo][xo], map->map->data.F32[iy][ix]);
+	    }
+	}
+
+	// XXX test on the model or don't bother
+	psImage *model = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+	    }
+	}
+	SaveImage (NULL, model, "model.fits");
+    }
+    # endif
+
+    // make a model for a well-sampled field of a high-order polynomial function (f = (a(x-xo)^2 + b(y-yo)^2) + c)
+    # if (TEST3)
+    {
+	// function is defined over the range 0-1000, 0-1000
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXfine = 1000;
+	binning->nYfine = 1000;
+	binning->nXruff = 5;
+	binning->nYruff = 5;
+
+	// generate a grid of test data points
+	psVector *x = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *y = psVectorAllocEmpty (100, PS_TYPE_F32);
+	psVector *f = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+	for (int ix = 0; ix < 1000; ix += 20) {
+	    for (int iy = 0; iy < 1000; iy += 20) {
+		x->data.F32[x->n] = ix;
+		y->data.F32[y->n] = iy;
+		f->data.F32[f->n] = C10*(ix-500) + PS_SQR(C01*(iy-500));
+		psVectorExtend (x, 100, 1);
+		psVectorExtend (y, 100, 1);
+		psVectorExtend (f, 100, 1);
+	    }
+	}
+
+	psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+	// scale defines both field and map image sizes (nXfine, nXruff)
+	psImageMap *map = psImageMapAlloc (NULL, binning, stats);
+
+	// allocate a map, but we have no field image to supply
+	// XXX this function needs to correct for the mean superpixel position 
+	// which is sampled...
+	psImageMapGenerate (map, x, y, f, 0.1);
+	psFree (binning);
+
+	fprintf (stderr, "nGood: %d\n", map->nGood);
+	fprintf (stderr, "nPoor: %d\n", map->nPoor);
+	fprintf (stderr, "nBad:  %d\n", map->nBad);
+	
+	SaveImage (NULL, map->map, "map.fits");
+	
+	psImage *field = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		field->data.F32[iy][ix] = C10*(ix-500) + PS_SQR(C01*(iy-500));
+	    }
+	}
+	SaveImage (NULL, field, "field.fits");
+
+	// measure difference between model (map) and data (field)
+	for (int ix = 0; ix < map->map->numCols; ix++) {
+	    for (int iy = 0; iy < map->map->numRows; iy++) {
+		int xo = psImageBinningGetFineX(map->binning, ix + 0.5);
+		int yo = psImageBinningGetFineY(map->binning, iy + 0.5);
+		float df = field->data.F32[yo][xo] - map->map->data.F32[iy][ix];
+		fprintf (stderr, "%d %d -> %d %d  :  %f = %f - %f\n", ix, iy, xo, yo, df, field->data.F32[yo][xo], map->map->data.F32[iy][ix]);
+	    }
+	}
+
+
+	psImage *model = psImageAlloc(1000, 1000, PS_TYPE_F32);
+	for (int ix = 0; ix < 1000; ix++) {
+	    for (int iy = 0; iy < 1000; iy++) {
+		// XXX the binned coordinates are probably off by 0.5 pix, or we are interpolating 
+		// to the wrong binned coordinate.  
+		// XXX fix edge cases
+		model->data.F32[iy][ix] = psImageUnbinPixel_V2 (ix + 0.5, iy + 0.5, map->map, map->binning);
+	    }
+	}
+	SaveImage (NULL, model, "model.fits");
+    }
+    # endif
+
+    // make a model for a poorly-sampled field of a non-polynomial function (f = (a(x-xo)^2 + b(y-yo)^2)^-1)
+
+    // choose a model scale for a poorly-sampled field of a non-polynomial function (f = (a(x-xo)^2 + b(y-yo)^2)^-1)
+    
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMaskOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMaskOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageMaskOps.c	(revision 22322)
@@ -0,0 +1,741 @@
+/** @file  tst_psImageMaskOps.c
+ *
+ *  @brief Contains the tests for psMaskOps.[ch]
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-05-02 04:14:33 $
+ *
+ *  XXX: In general, the image tests with (1, N) and (N, 1) failed and have
+ *  been excluded.
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define N 20
+#define MASK_VAL 10
+#define GROW_SIZE 5
+#define GROW_VAL 15
+
+bool genericImageGrowMaskTest(int rows, int cols, int maskVal, int growSize, int growVal)
+{
+    // Try with legitimate data
+    // XXX: psImageGrowMask() is currently growing a circular mask when the SCD
+    // specs a square mask.
+    {
+        psMemId id = psMemGetId();
+        psImage *in = psImageAlloc(rows, cols, PS_TYPE_MASK);
+        psImage *inout = psImageAlloc(rows, cols, PS_TYPE_MASK);
+        for (int i = 0 ; i < rows ; i++) {
+            for (int j = 0 ; j < cols ; j++) {
+                in->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+        in->data.PS_TYPE_MASK_DATA[N/2][N/2] = maskVal;
+        psImage *out = psImageGrowMask(NULL, in, maskVal, growSize, growVal);
+        ok(out != NULL, "psImageGrowMask() returned non-NULL with legitimate data");
+        skip_start(out == NULL, 1, "Skipping tests because psImageGrowMask() returned NULL");
+        ok(out->type.type == PS_TYPE_MASK, "psImageGrowMask() returned correct type mask");
+        ok((out->numRows == in->numRows) && (out->numCols == in->numCols),
+          "psImageGrowMask() returned correct size mask");
+        bool errorFlag = false;
+        for (int i = 0 ; i < rows ; i++) {
+            for (int j = 0 ; j < cols ; j++) {
+                if (((rows/2-i)*(rows/2-i)+(cols/2-j)*(cols/2-j)) <= (growSize*growSize)) {
+                    if (out->data.PS_TYPE_MASK_DATA[i][j] != growVal) {
+                        diag("out[%d][%d] is incorrect (%d)", i, j, out->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (out->data.PS_TYPE_MASK_DATA[i][j] != 0) {
+                        diag("out[%d][%d] is incorrect (%d)", i, j, out->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+        ok(!errorFlag, "psImageGrowMask() produced the correct data values");
+        skip_end();
+        psFree(in);
+        psFree(out);
+        psFree(inout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    return true;
+}
+
+bool testInRegion(psRegion region, int row, int col)
+{
+    return(row >= region.y0 && row <= region.y1 && col >= region.x0 && col <= region.x1);
+}
+
+
+bool genericImageMaskRegionTest(int numRows, int numCols)
+{
+    psMemId id = psMemGetId();
+    psMaskType maskVal = 1;
+    psRegion reg;
+    reg.x0 = numCols/4;
+    reg.x1 = 3 * numCols/4;
+    reg.y0 = numRows/4;
+    reg.y1 = 3 * numRows/4;
+    psImage *img = psImageAlloc(numRows, numCols, PS_TYPE_MASK);
+
+    psBool errorFlag = false;
+    // psImageMaskRegion(): logical or
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+        psImageMaskRegion(img, reg, "|", maskVal);
+        errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (testInRegion(reg, i, j)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageMaskRegion() produced the correct results for logical or");
+
+    // psImageMaskRegion(): logical or
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+        psImageMaskRegion(img, reg, "=", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (testInRegion(reg, i, j)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageMaskRegion() produced the correct results for =");
+
+    // psImageMaskRegion(): logical and
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0xff;
+            }
+        }
+        maskVal = 0xf;
+        psImageMaskRegion(img, reg, "&", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (testInRegion(reg, i, j)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xff) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xff", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageMaskRegion() produced the correct results for logical and");
+
+    // psImageMaskRegion(): logical and
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0xff;
+            }
+        }
+        maskVal = 0xf;
+        psImageMaskRegion(img, reg, "^", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (testInRegion(reg, i, j)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xf0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xf0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xff) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xff", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageMaskRegion() produced the correct results for logical xor");
+
+    psFree(img);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    return(true);
+}
+
+
+
+bool genericImageKeepRegionTest(int numRows, int numCols)
+{
+    psMemId id = psMemGetId();
+    psMaskType maskVal = 1;
+    psRegion reg;
+    reg.x0 = numCols/4;
+    reg.x1 = 3 * numCols/4;
+    reg.y0 = numRows/4;
+    reg.y1 = 3 * numRows/4;
+    psImage *img = psImageAlloc(numRows, numCols, PS_TYPE_MASK);
+
+    psBool errorFlag = false;
+    // psImageKeepRegion(): logical or
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+        psImageKeepRegion(img, reg, "|", maskVal);
+        errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (!testInRegion(reg, i, j)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageKeepRegion() produced the correct results for logical or");
+
+    // psImageKeepRegion(): logical or
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+        psImageKeepRegion(img, reg, "=", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (!testInRegion(reg, i, j)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageKeepRegion() produced the correct results for =");
+
+    // psImageKeepRegion(): logical and
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0xff;
+            }
+        }
+        maskVal = 0xf;
+        psImageKeepRegion(img, reg, "&", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (!testInRegion(reg, i, j)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xff) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xff", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageKeepRegion() produced the correct results for logical and");
+
+    // psImageKeepRegion(): logical and
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0xff;
+            }
+        }
+        maskVal = 0xf;
+        psImageKeepRegion(img, reg, "^", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (!testInRegion(reg, i, j)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xf0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xf0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xff) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xff", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageKeepRegion() produced the correct results for logical xor");
+
+    psFree(img);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    return(true);
+}
+
+
+bool testInCircle(psImage *img, double xCenter, double yCenter, double radius, double xCoord, double yCoord)
+{
+    return((((xCoord+img->col0)-xCenter) * ((xCoord+img->col0)-xCenter) +
+             ((yCoord+img->row0) - yCenter) * ((yCoord+img->row0) - yCenter)) <= (radius * radius));
+}
+
+
+
+bool genericImageMaskCircleTest(int numRows, int numCols, int x, int y, int radius)
+{
+    psMemId id = psMemGetId();
+    psMaskType maskVal = 1;
+    psImage *img = psImageAlloc(numRows, numCols, PS_TYPE_MASK);
+
+    psBool errorFlag = false;
+    // psImageMaskCircle(): logical or
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+        psImageMaskCircle(img, (psF64) x, (psF64) y, (psF64) radius, "|", maskVal);
+        errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (testInCircle(img, (psF64) x, (psF64) y, (psF64) radius, (psF64) j, (psF64) i)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageMaskCircle() produced the correct results for logical or");
+
+    // psImageMaskCircle(): logical or
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+        psImageMaskCircle(img, (psF64) x, (psF64) y, (psF64) radius, "=", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (testInCircle(img, (psF64) x, (psF64) y, (psF64) radius, (psF64) j, (psF64) i)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageMaskCircle() produced the correct results for =");
+
+    // psImageMaskCircle(): logical and
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0xff;
+            }
+        }
+        maskVal = 0xf;
+        psImageMaskCircle(img, (psF64) x, (psF64) y, (psF64) radius, "&", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (testInCircle(img, (psF64) x, (psF64) y, (psF64) radius, (psF64) j, (psF64) i)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xff) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xff", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageMaskCircle() produced the correct results for logical and");
+
+    // psImageMaskCircle(): logical and
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0xff;
+            }
+        }
+        maskVal = 0xf;
+        psImageMaskCircle(img, (psF64) x, (psF64) y, (psF64) radius, "^", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (testInCircle(img, (psF64) x, (psF64) y, (psF64) radius, (psF64) j, (psF64) i)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xf0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xf0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xff) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xff", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageMaskCircle() produced the correct results for logical xor");
+
+    psFree(img);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    return(true);
+}
+
+
+bool genericImageKeepCircleTest(int numRows, int numCols, int x, int y, int radius)
+{
+    psMemId id = psMemGetId();
+    psMaskType maskVal = 1;
+    psImage *img = psImageAlloc(numRows, numCols, PS_TYPE_MASK);
+
+    psBool errorFlag = false;
+    // psImageKeepCircle(): logical or
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+        psImageKeepCircle(img, (psF64) x, (psF64) y, (psF64) radius, "|", maskVal);
+        errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (!testInCircle(img, (psF64) x, (psF64) y, (psF64) radius, (psF64) j, (psF64) i)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageKeepCircle() produced the correct results for logical or");
+
+    // psImageKeepCircle(): logical or
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+        psImageKeepCircle(img, (psF64) x, (psF64) y, (psF64) radius, "=", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (!testInCircle(img, (psF64) x, (psF64) y, (psF64) radius, (psF64) j, (psF64) i)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageKeepCircle() produced the correct results for =");
+
+    // psImageKeepCircle(): logical and
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0xff;
+            }
+        }
+        maskVal = 0xf;
+        psImageKeepCircle(img, (psF64) x, (psF64) y, (psF64) radius, "&", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (!testInCircle(img, (psF64) x, (psF64) y, (psF64) radius, (psF64) j, (psF64) i)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != maskVal) {
+                        diag("ERROR: img[%d][%d] is %d, should be %d", i, j, img->data.PS_TYPE_MASK_DATA[i][j], maskVal);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xff) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xff", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageKeepCircle() produced the correct results for logical and");
+
+    // psImageKeepCircle(): logical and
+    {
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0xff;
+            }
+        }
+        maskVal = 0xf;
+        psImageKeepCircle(img, (psF64) x, (psF64) y, (psF64) radius, "^", maskVal);
+        psBool errorFlag = false;
+        for (int i = 0 ; i < numRows ; i++) {
+            for (int j = 0 ; j < numCols ; j++) {
+                if (!testInCircle(img, (psF64) x, (psF64) y, (psF64) radius, (psF64) j, (psF64) i)) {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xf0) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xf0", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                } else {
+                    if (img->data.PS_TYPE_MASK_DATA[i][j] != 0xff) {
+                        diag("ERROR: img[%d][%d] is %d, should be 0xff", i, j, img->data.PS_TYPE_MASK_DATA[i][j]);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+    }
+    ok(!errorFlag, "psImageKeepCircle() produced the correct results for logical xor");
+
+    psFree(img);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    return(true);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(47);
+
+    // psImageGrowMask()
+    //   *psImageGrowMask(psImage *out, const psImage *in,
+    //                    psMaskType maskVal, unsigned int growSize,
+    //                    psMaskType growVal)
+    // psImageGrowMask grows specified values on the input mask image, in,
+    // returning the result. If out is NULL, then a new image of the same type
+    // and dimension as in shall be allocated and returned; otherwise out shall
+    // be modified. If out is non-NULL and does not have the same size and type
+    // as in, the function shall generate an error and return NULL.  Pixels in
+    // the in image within growSize pixels (either horizontal or vertical) of a
+    // pixel which matches the maskVal shall have the corresponding pixel in the
+    // out image set to the growValue.
+
+    psMaskType maskVal = 0;
+    psMaskType growVal = 0;
+    unsigned int growSize = 0;
+
+    // return null for null input image
+    {
+        psMemId id = psMemGetId();
+        psImage *inout = psImageAlloc(N, N, PS_TYPE_MASK);
+        psImage *out = psImageGrowMask(inout, NULL, maskVal, growSize, growVal);
+        ok(out == NULL, "psImageGrowMask() returned NULL with NULL input image");
+        psFree(out);
+        psFree(inout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // return null for incompatible image size
+    {
+        psMemId id = psMemGetId();
+        psImage *in = psImageAlloc(5, 5, PS_TYPE_MASK);
+        psImage *inout = psImageAlloc(2, 2, PS_TYPE_MASK);
+        psImage *out = psImageGrowMask(inout, in, maskVal, growSize, growVal);
+        ok(out == NULL, "psImageGrowMask() returned NULL with incompatible image size");
+        psFree(in);
+        psFree(out);
+        psFree(inout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // return null for incompatible out image type
+    {
+        psMemId id = psMemGetId();
+        psImage *in = psImageAlloc(5, 5, PS_TYPE_MASK);
+        psImage *inout = psImageAlloc(5, 5, PS_TYPE_F32);
+        psImage *out = psImageGrowMask(inout, in, maskVal, growSize, growVal);
+        ok(out == NULL, "psImageGrowMask() returned NULL with incompatible out image type");
+        psFree(in);
+        psFree(out);
+        psFree(inout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // return NULL for input image that doesn't match PS_TYPE_MASK
+    {
+        psMemId id = psMemGetId();
+        psImage *in = psImageAlloc(5, 5, PS_TYPE_F32);
+        psImage *inout = psImageAlloc(5, 5, PS_TYPE_MASK);
+        psImage *out = psImageGrowMask(inout, in, maskVal, growSize, growVal);
+        ok(out == NULL, "psImageGrowMask() returned NULL with incompatible in image type");
+        psFree(in);
+        psFree(out);
+        psFree(inout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    // XXX: These tests fail
+    if (0) {
+        genericImageGrowMaskTest(N, 1, MASK_VAL, GROW_SIZE, GROW_VAL);
+    }
+    if (0) {
+        genericImageGrowMaskTest(1, N, MASK_VAL, GROW_SIZE, GROW_VAL);
+    }
+    // Try with grow region outside image
+    genericImageGrowMaskTest(N, N, MASK_VAL, N*2, GROW_VAL);
+    // Try with zero-size grow region
+    genericImageGrowMaskTest(N, N, MASK_VAL, 0, GROW_VAL);
+    // Try with sensible parameters
+    genericImageGrowMaskTest(N, N, MASK_VAL, GROW_SIZE, GROW_VAL);
+
+
+
+    // psImageMaskRegion
+    // Generate error NULL input image that doesn't match PS_TYPE_MASK
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psRegion reg;
+        reg.x0 = 1;
+        reg.x1 = 2;
+        reg.y0 = 1;
+        reg.y1 = 2;
+        psMaskType maskVal = 2;
+        psImageMaskRegion(NULL, reg, "|", maskVal);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    if (0) genericImageMaskRegionTest(N, 1);
+    if (0) genericImageMaskRegionTest(1, N);
+    genericImageMaskRegionTest(N, N);
+
+
+    // psImageKeepRegion
+    // Generate error NULL input image that doesn't match PS_TYPE_MASK
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psRegion reg;
+        reg.x0 = 1;
+        reg.x1 = 2;
+        reg.y0 = 1;
+        reg.y1 = 2;
+        psImageKeepRegion(NULL, reg, "|", 1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    if (0) genericImageKeepRegionTest(N, 1);
+    if (0) genericImageKeepRegionTest(1, N);
+    genericImageKeepRegionTest(N, N);
+
+
+    // psImageMaskCircle
+    // Generate error NULL input image that doesn't match PS_TYPE_MASK
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psImageMaskCircle(NULL, 1.0, 2.0, 3.0, "|", 1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    // XXX: These seg fault
+    if (0) genericImageMaskCircleTest(N, 1, N/2, 1, N/4);
+    if (0) genericImageMaskCircleTest(1, N, 1, N/2, N/4);
+    genericImageMaskCircleTest(N, N, N/2, N/2, N/4);
+
+
+    // psImageKeepCircle
+    // Generate error NULL input image that doesn't match PS_TYPE_MASK
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psImageKeepCircle(NULL, 1.0, 2.0, 3.0, "|", 1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    // XXX: These seg fault
+    if (0) genericImageKeepCircleTest(N, 1, N/2, 1, N/4);
+    if (0) genericImageKeepCircleTest(1, N, 1, N/2, N/4);
+    genericImageKeepCircleTest(N, N, N/2, N/2, N/4);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImagePixelExtract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImagePixelExtract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImagePixelExtract.c	(revision 22322)
@@ -0,0 +1,1254 @@
+/** @file  tst_psImageExtraction.c
+*
+*  @brief Contains the tests for psImageExtraction.[ch]
+*
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-06-04 20:25:32 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+void genericImageRowColTests(int numRows, int numCols)
+{
+    psMemId id = psMemGetId();
+    psImage *image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    for (int i = 0 ; i < numRows; i++) {
+        for (int j = 0 ; j < numCols; j++) {
+            image->data.F32[0][0] = (float) i+j;
+        }
+    }            
+
+    bool errorFlag = false;
+    for (int i = 0 ; i < numRows; i++) {
+        psVector *out = psImageRow(NULL, image, i);
+        ok(out != NULL, "psImageRow returned non-NULL");
+        if (out != NULL) {
+            for (int j = 0 ; j < numCols; j++) {
+                if (out->data.F32[j] != image->data.F32[i][j]) {
+                    diag("TEST ERROR: image->data.F32[%d][%d] is %f, should be %f", i, j, 
+                         image->data.F32[i][j], out->data.F32[j]);
+                    errorFlag = true;
+                }
+            }
+            psFree(out);
+        } else {
+            errorFlag = true;
+        }
+    }
+    ok(!errorFlag, "psImageRow() passed tests with correct data inputs");
+    psFree(image);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+}
+
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(295);
+
+    // test psImageSlice()
+    {
+        psMemId id = psMemGetId();
+        const psS32 r = 200;
+        const psS32 c = 300;
+        const psS32 m = r / 2 -1;
+        const psS32 n = r / 4 -1;
+        psImage* image;
+        psPixels* positions = psPixelsAlloc(r);
+        psImage* mask = psImageAlloc(c, r, PS_TYPE_MASK);
+        psStats* stat = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    
+
+        // This function shall extract pixels from a specified region of a psImage
+        // structure into a vector using a specified statistical method.
+        //    
+        // Verify the returned psVector structure contains expected data, if the
+        // input psImage input has known data and the psStats structure specifies
+        // a known statistical method. At least two different statistical methods
+        // should be used within a valid range of psImage data. Allow for a delta
+        // when comparing results to allow for testing a different platforms. Two
+        // cases should be used for each possible direction. Data region cases
+        // should include 0x0, 1x1, Nx1, 1xN, NxN, MxN
+        for (psS32 row = 0;row < r;row++) {
+            psMaskType* maskRow = mask->data.PS_TYPE_MASK_DATA[row];
+            for (psS32 col = 0;col < c;col++) {
+                maskRow[col] = 0;
+            }
+        }
+    
+        #define PSIMAGESLICE_TEST1(TYPE,M,N,DIRECTION,TRUTH_SIZE,TRUTHPIX_X,TRUTHPIX_Y,TESTNUM) \
+        { \
+            psMemId id = psMemGetId(); \
+            psVector* out = NULL; \
+            bool errorFlag = false; \
+            image = psImageAlloc(c, r, PS_TYPE_##TYPE); \
+            for (psS32 row = 0;row < r;row++) { \
+                ps##TYPE *imageRow = image->data.TYPE[row]; \
+                ps##TYPE rowOffset = row * 2; \
+                for (psS32 col = 0;col < c;col++) { \
+                    imageRow[col] = col + rowOffset; \
+                } \
+            } \
+            P_PSIMAGE_SET_COL0(image, 1); \
+            P_PSIMAGE_SET_ROW0(image, 1); \
+            out = psImageSlice(out,positions,image,mask,1, \
+                               psRegionSet(1+c/10,1+c/10+M,1+r/10,1+r/10+N),DIRECTION,stat); \
+            \
+            if (out->n != TRUTH_SIZE) { \
+                diag("Number of results is wrong (%d, not %d)", \
+                      out->n,TRUTH_SIZE); \
+                errorFlag = true; \
+            } \
+            \
+            if (positions->n != TRUTH_SIZE) { \
+                diag("Number of results for positions vector is wrong (%d, not %d)", \
+                      positions->n,TRUTH_SIZE); \
+                errorFlag = true; \
+            } \
+            \
+            for (psS32 i=0;i<out->n;i++) { \
+                if (fabs(out->data.F64[i]-image->data.TYPE[r/10+TRUTHPIX_Y][c/10+TRUTHPIX_X]) > 1.0/(psF64)r) { \
+                    diag("Improper result at position %d.  Got %g, expected %g",i, \
+                          out->data.F64[i],image->data.TYPE[r/10+TRUTHPIX_Y][c/10+TRUTHPIX_X]); \
+                    errorFlag = true; \
+                } \
+                if (DIRECTION == PS_CUT_X_POS || DIRECTION == PS_CUT_X_NEG) { \
+                    if (positions->data[i].x != c/10+TRUTHPIX_X) { \
+                        diag("Improper positions (%d vs %d) result @ %d.", \
+                              positions->data[i].x,c/10+TRUTHPIX_X,i); \
+                        errorFlag = true; \
+                    } \
+                } else { \
+                    if (positions->data[i].y != r/10+TRUTHPIX_Y) { \
+                        diag("Improper positions (%d vs %d) result @ %d.", \
+                              positions->data[i].y,r/10+TRUTHPIX_Y,i); \
+                        errorFlag = true; \
+                    } \
+                } \
+            } \
+            ok(!errorFlag, "psImageSlice() produced correct data values (test number %d", TESTNUM); \
+            psFree(out); \
+            psFree(image); \
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks"); \
+        }
+    
+        //
+        #define PSIMAGESLICE_TEST(TYPE) \
+        PSIMAGESLICE_TEST1(TYPE, m, n, PS_CUT_X_POS, m, i, n / 2, 0); \
+        PSIMAGESLICE_TEST1(TYPE, m, n, PS_CUT_X_NEG, m, m - 1 - i, n / 2, 1); \
+        PSIMAGESLICE_TEST1(TYPE, m, n, PS_CUT_Y_POS, n, m / 2, i, 2); \
+        PSIMAGESLICE_TEST1(TYPE, m, n, PS_CUT_Y_NEG, n, m / 2, n - 1 - i, 3); \
+        \
+        PSIMAGESLICE_TEST1(TYPE, m, 1, PS_CUT_X_POS, m, i, 0, 4); \
+        PSIMAGESLICE_TEST1(TYPE, m, 1, PS_CUT_X_NEG, m, m - 1 - i, 0, 5); \
+        PSIMAGESLICE_TEST1(TYPE, m, 1, PS_CUT_Y_POS, 1, m / 2, 0, 6); \
+        PSIMAGESLICE_TEST1(TYPE, m, 1, PS_CUT_Y_NEG, 1, m / 2, 0, 7); \
+        \
+        PSIMAGESLICE_TEST1(TYPE, 1, n, PS_CUT_X_POS, 1, 0, n / 2, 8); \
+        PSIMAGESLICE_TEST1(TYPE, 1, n, PS_CUT_X_NEG, 1, 0, n / 2, 9); \
+        PSIMAGESLICE_TEST1(TYPE, 1, n, PS_CUT_Y_POS, n, 0, i, 10); \
+        PSIMAGESLICE_TEST1(TYPE, 1, n, PS_CUT_Y_NEG, n, 0, n - 1 - i, 11); \
+        \
+        PSIMAGESLICE_TEST1(TYPE, 1, 1, PS_CUT_X_POS, 1, 0, 0, 12); \
+        PSIMAGESLICE_TEST1(TYPE, 1, 1, PS_CUT_X_NEG, 1, 0, 0, 13); \
+        PSIMAGESLICE_TEST1(TYPE, 1, 1, PS_CUT_Y_POS, 1, 0, 0, 14); \
+        PSIMAGESLICE_TEST1(TYPE, 1, 1, PS_CUT_Y_NEG, 1, 0, 0, 15); \
+    
+        PSIMAGESLICE_TEST(F32);
+
+        PSIMAGESLICE_TEST(F64);
+        PSIMAGESLICE_TEST(U16);
+    
+        image = psImageAlloc(c, r, PS_TYPE_F32);
+        // Verify the returned psVector structure pointer is null and program
+        // execution doesn't stop, if input psImage input is null.
+        // Following should be an error
+        // XXX: Verify error
+
+
+        psVector *out = NULL;
+        out = psImageSlice(out,
+                           NULL, NULL,
+                           NULL, 0,
+                           psRegionSet(c/10, c/10 + 1, r/10, r/10 + 1),
+                           PS_CUT_X_POS,
+                           stat);
+        ok(out == NULL, "psImageSlice() returned NULL with NULL input");
+    
+        // Verify the returned psVector structure pointer is null and program
+        // execution doesn't stop, if input psStats stats is null.
+        // Following should be an error
+        // XXX: Verify error
+        out = psImageSlice(out,
+                            NULL, image,
+                            mask, 1,
+                            psRegionSet(c/10, c/10 + 1, r/10, r/10 + 1),
+                            PS_CUT_X_POS,
+                            NULL);
+        ok(out == NULL, "psImageSlice() returned NULL with NULL psStats");
+            psError(PS_ERR_UNKNOWN,true, "Giving a NULL stat struct, psImageSlice didn't return NULL as expected");
+
+        // Verify the returned psVector structure pointer is null and program
+        // executions doesn't stop, if the input direction is not set to one of
+        // the two valid values.
+        // Following should be an error
+        // XXX: Verify error
+        out = psImageSlice(out, NULL,
+                           image,
+                           mask, 1,
+                           psRegionSet(c/10, c/10 + 1, r/10, r/10 + 1),
+                           5,
+                           stat);
+        ok(out == NULL, "psImageSlice() returned NULL with bogus direction flag");
+
+        // Verify the returned psVector structure pointer is null and program
+        // execution doesn't stop, if the input nrow and/or ncol are zero.
+        // Following should be an error
+        // XXX: Verify error
+        out = psImageSlice(out,
+                            NULL,
+                            image,
+                            mask, 1,
+                            psRegionSet(c/10, c/10, r/10, r/10),
+                            PS_CUT_X_POS,
+                            stat);
+        ok(out == NULL, "psImageSlice() returned NULL with 0x0 region");
+    
+        // Verify the returned psVector structure pointer is null and program
+        // execution doesn't stop, if the inputs row, col, nrow, ncol specify a
+        // regions of data that is not within the input psImage structure.
+        // Following should be an error
+        // XXX: Verify error
+        out = psImageSlice(out, NULL,
+                            image,
+                            mask, 1,
+                            psRegionSet(c+1, c+2, r/10, r/10 + 10),
+                            PS_CUT_X_POS,
+                            stat);
+        ok(out == NULL, "psImageSlice() returned NULL with unallowed x position");
+    
+        // Following should be an error
+        // XXX: Verify error
+        out = psImageSlice(out, NULL,
+                            image,
+                            mask, 1,
+                            psRegionSet(c/10, c/10 + 1, r+1,r+5),
+                            PS_CUT_X_POS,
+                            stat);
+        ok(out == NULL, "psImageSlice() returned NULL with unallowed y position");
+    
+        // Following should be an error
+        // XXX: Verify error
+        out = psImageSlice(out, NULL,
+                            image,
+                            mask, 1,
+                            psRegionSet(c/10, c+1, r/10, r/10+1),
+                            PS_CUT_X_POS,
+                            stat);
+        ok(out == NULL, "psImageSlice() returned NULL with unallowed numCols");
+    
+        // Following should be an error
+        // XXX: Verify error
+        out = psImageSlice(out, NULL,
+                            image,
+                            mask, 1,
+                            psRegionSet(c/10, c/10 + 1, r/10, r + 1),
+                            PS_CUT_X_POS,
+                            stat);
+        ok(out == NULL, "psImageSlice() returned NULL with unallowed numRows");
+    
+        // Verify the returned psVector structure pointer is null and program
+        // execution doesn't stop, if the input psStat structure member options is
+        // zero which indicates no statistic method specified.
+        // Following should be an error
+        // XXX: Verify error
+        stat->options = 0;
+        out = psImageSlice(out, NULL,
+                            image,
+                            mask, 1,
+                            psRegionSet(c/10, c/10 + 1, r/10, r/10+1),
+                            PS_CUT_X_POS,
+                            stat);
+        ok(out == NULL, "psImageSlice() returned NULL with unallowed numRows");
+    
+        // Verify that a mask of different size than the input image returns null and program
+        // execution doesn't stop.
+        // Following should be an error mask size != image size
+        // XXX: Verify error
+        stat->options = PS_STAT_SAMPLE_MEDIAN;
+        psImage* maskSz = psImageAlloc(r, c, PS_TYPE_MASK);
+        out = psImageSlice(out, NULL,
+                            image,
+                            maskSz, 1,
+                            psRegionSet(c/10, c/10 + 1, r/10, r/10 + 1),
+                            PS_CUT_X_POS,
+                            stat);
+        ok(out == NULL, "psImageSlice() returned NULL with differing mask size and image size");
+    
+        // Verify the a unallowed type mask returns null and program execution doesn't stop.
+        // Following should be an error unallowed mask type
+        // XXX: Verify error
+        psImage* maskS8 = psImageAlloc(c, r, PS_TYPE_S8);
+        out =  psImageSlice(out, NULL,
+                             image,
+                             maskS8, 1,
+                             psRegionSet(c/10, c/10 + 1, r/10, r/10 + 1),
+                             PS_CUT_X_POS,
+                             stat);
+        ok(out == NULL, "psImageSlice() returned NULL with unallowed mask type");
+    
+        //Added tests after subimage changes.
+        psFree(image);
+        image = psImageAlloc(c, r, PS_TYPE_F64);
+        for (psS32 row = 0;row < r;row++) {
+            psF64 *imageRow = image->data.F64[row];
+            psF64 rowOffset = row * 2;
+            for (psS32 col = 0;col < c;col++) {
+                imageRow[col] = col + rowOffset;
+            }
+        }
+        P_PSIMAGE_SET_COL0(image, 1);
+        P_PSIMAGE_SET_ROW0(image, 1);
+        psFree(out);
+        out = NULL;
+        out = psImageSlice(out,positions,image,mask,1,
+                           psRegionSet(1,c,1,r),PS_CUT_X_POS,stat);
+        ok(out != NULL, "psImageSlice() with correct inputs");
+        psFree(out);
+        out = NULL;
+
+        //Return NULL for incorrect image inputs.
+        P_PSIMAGE_SET_ROW0(image, -1);
+        // Following should generate error message
+        // XXX: Verify error
+        out = psImageSlice(out,positions,image,mask,1,
+                           psRegionSet(1,c,1,r),PS_CUT_X_POS,stat);
+        ok(out == NULL, "psImageSlice returned NULL for unallowed specified input");
+
+        P_PSIMAGE_SET_COL0(image, -1);
+        P_PSIMAGE_SET_ROW0(image, 1);
+        // Following should generate error message
+        // XXX: Verify error
+        out = psImageSlice(out,positions,image,mask,1,
+                           psRegionSet(1,c,1,r),PS_CUT_X_POS,stat);
+        ok(out == NULL, "psImageSlice returned NULL for unallowed specified input");
+
+        P_PSIMAGE_SET_COL0(image, -1);
+        // Return NULL for incorrect region inputs.
+        // Following should generate error message
+        // XXX: Verify error
+        out = psImageSlice(out,positions,image,mask,1,
+                           psRegionSet(0,c,1,r),PS_CUT_X_POS,stat);
+        ok(out == NULL, "psImageSlice returned NULL for unallowed specified input");
+
+        // Following should generate error message
+        // XXX: Verify error
+        out = psImageSlice(out,positions,image,mask,1,
+                           psRegionSet(1,c,1,r+1),PS_CUT_X_POS,stat);
+        ok(out == NULL, "psImageSlice returned NULL for unallowed specified input");
+
+        // Following should generate error message
+        // XXX: Verify error
+        out = psImageSlice(out,positions,image,mask,1,
+                           psRegionSet(1,c,1,-r-2),PS_CUT_X_POS,stat);
+        ok(out == NULL, "psImageSlice returned NULL for unallowed specified input");
+
+        // Following should generate error message
+        // XXX: Verify error
+        out = psImageSlice(out,positions,image,mask,1,
+                           psRegionSet(c,1,1,r),PS_CUT_X_POS,stat);
+        ok(out == NULL, "psImageSlice returned NULL for unallowed specified input");
+
+
+        // Following should generate error message
+        // XXX: Verify error
+        out = psImageSlice(out,positions,image,mask,1,
+                           psRegionSet(1,1,1,1),PS_CUT_X_POS,stat);
+        ok(out == NULL, "psImageSlice returned NULL for unallowed specified input");
+
+    
+        //Make sure that regions match appropriately...
+        out = psImageSlice(out,positions,image,mask,1,
+                           psRegionSet(1,-1,1,-1),PS_CUT_Y_NEG,stat);
+        psVector *out2 = NULL;
+        out2 = psImageSlice(out2,positions,image,mask,1,
+                            psRegionSet(0,0,0,0),PS_CUT_Y_NEG,stat);
+        ok(out != NULL, "psImageSlice returned non-NULL for valid inputs");
+        ok(out2 != NULL, "psImageSlice returned non-NULL for valid inputs");
+        skip_start(out == NULL || out2 == NULL, 2, "Skipping tests because psImageSlice() returned NULL");
+        ok(out->n == out2->n, "psImageSlice returned matching vectors for equivalent inputs");
+        ok(out->data.F64[out->n-1] == out2->data.F64[out2->n-1], "psImageSlice returned matching vectors for equivalent inputs");
+        skip_end();
+        psFree(out2);
+        psFree(image);
+        psFree(positions);
+        psFree(mask);
+        psFree(out);
+        psFree(stat);
+        psFree(maskS8);
+        psFree(maskSz);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageCut()
+    {
+        psMemId id = psMemGetId();
+        psS32 c = 300;
+        psS32 r = 200;
+        psS32 numPoints = 15;
+        float startCol[] = { 40,150, 40,  0,280, 40,280, -1,300, 20, 20, 20, 20, 20, 20};
+        float endCol[] =   {240,150,240,299, 40,240, 40,240,240, -1,300,240,240,240,240};
+        float startRow[] = { 20, 10,100,  0, 20,180,180, 10, 10, 10, 10, -1,200, 10, 10};
+        float endRow[] =   {160,180,100,199,160, 10, 10,180,180,180,180,180,180, -1,200};
+        psBool success[] = {true,true,true,true,true,true,true,false,false,false,false,false,false,false,false};
+        psU32 length = 100;
+    
+        psImage* image = psImageAlloc(c,r,PS_TYPE_F32);
+        psImage* mask = psImageAlloc(c,r,PS_TYPE_MASK);
+        for (psS32 row = 0; row < image->numRows; row++) {
+            for (psS32 col = 0; col < image->numCols; col++) {
+                image->data.F32[row][col] = (psF32)col + (psF32)row/1000.0f;
+                if ((row & 0x0F) == 0) {
+                    mask->data.PS_TYPE_MASK_DATA[row][col] = 1;
+                } else {
+                    mask->data.PS_TYPE_MASK_DATA[row][col] = 0;
+                }
+            }
+        }
+        psVector* rows = psVectorAlloc(length,PS_TYPE_F32);
+        psVector* cols = psVectorAlloc(length,PS_TYPE_F32);
+    
+        psVector* result = psVectorAlloc(length,PS_TYPE_F32);
+        for (psS32 n = 0; n < numPoints; n++) {
+            psVector* orig = result;
+            if (! success[n]) {
+                // The following should be an error
+                // XXX: Verify error
+            }
+            if (n == 1) {
+                result = psImageCut(result,
+                                    cols,rows,
+                                    image,
+                                    NULL,0,
+                                    psRegionSet(startCol[n], endCol[n], startRow[n],endRow[n]),
+                                    length,
+                                    PS_INTERPOLATE_FLAT);
+            } else {
+                result = psImageCut(result,
+                                    cols,rows,
+                                    image,
+                                    mask,1,
+                                    psRegionSet(startCol[n], endCol[n], startRow[n], endRow[n]),
+                                    length,
+                                    PS_INTERPOLATE_FLAT);
+            }
+
+            bool errorFlag = false;    
+            if (success[n]) {
+                ok(result != NULL, "psImageCut returned non-NULL");
+                ok(orig != NULL, "orig image is non-NULL");
+                ok(orig == result, "psImageCut did recycle the out parameter properly");
+                psImageInterpolateOptions *tmpIntOptsNoMask = psImageInterpolateOptionsAlloc(
+                    PS_INTERPOLATE_FLAT, image, NULL, NULL, 0, 0, NAN, 0, 0, 0.0);
+                psImageInterpolateOptions *tmpIntOptsMask = psImageInterpolateOptionsAlloc(
+                    PS_INTERPOLATE_FLAT, image, NULL, NULL, 1, 0, NAN, 0, 0, 0.0);
+                double imgVal;
+                double varVal;
+                psMaskType maskVal;
+    
+                float deltaRow = (endRow[n]-startRow[n])/(length-1);
+                float deltaCol = (endCol[n]-startCol[n])/(length-1);
+                psF32 truth;
+                for (psS32 i = 0; i < length; i++) {
+                    float x = (float)startCol[n]+(float)i*deltaCol;
+                    float y = (float)startRow[n]+(float)i*deltaRow;
+                    if (n == 1) {
+//                        truth = psImagePixelInterpolate(image, x, y,
+//                                                         NULL,0,0,PS_INTERPOLATE_FLAT);
+                          psImageInterpolate(&imgVal, &varVal, &maskVal, x, y, tmpIntOptsNoMask);
+                          truth = imgVal;
+                    } else {
+//                        truth = psImagePixelInterpolate(image, x, y,
+//                                                         mask,1,0,PS_INTERPOLATE_FLAT);
+                          psImageInterpolate(&imgVal, &varVal, &maskVal, x, y, tmpIntOptsMask);
+                          truth = imgVal;
+                    }
+                    if (fabs(result->data.F32[i]-truth) > FLT_EPSILON) {
+                        diag("Bad result in position %d; Found %g but expected %g.",
+                              i, result->data.F32[i], truth);
+                        errorFlag = true;
+                    }
+                    if (fabsf(x - cols->data.F32[i]) > FLT_EPSILON ||
+                            fabsf(y - rows->data.F32[i]) > FLT_EPSILON) {
+                        diag("Bad resulting col/row at index %d; Found (%g,%g) but expected (%g,%g).",
+                              i, cols->data.F32[i], rows->data.F32[i], x, y);
+                        errorFlag = true;
+                    }
+                }
+                psFree(tmpIntOptsNoMask);
+                psFree(tmpIntOptsMask);
+            } else {
+                if (result != NULL) {
+                    diag("psImageCut did not return NULL with a cut of (%g,%g)->(%g,%g).",
+                          startCol[n],startRow[n],endCol[n],endRow[n]);
+                    errorFlag = true;
+                }
+                psErr* err = psErrorLast();
+                if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+                    diag("psImageCut did not generate proper error message.");
+                    errorFlag = true;
+                }
+                psFree(err);
+            }
+            ok(!errorFlag, "psImageCut() produced the correct data values");
+        }
+
+        // Following should be an error (NULL image)
+        // XXX: Verify error
+        result = psImageCut(result,
+                            cols,rows,
+                            NULL,
+                            mask,1,
+                            psRegionSet(startCol[0], endCol[0], startRow[0], endRow[0]),
+                            length,
+                            PS_INTERPOLATE_FLAT);
+        ok(result == NULL, "psImageCut did return NULL given NULL image");
+        psErr* err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL, 
+          "psImageCut did generate proper error message given NULL image");
+        psFree(err);
+    
+        // Following should be an error (length=0)
+        // XXX: Verify error
+        result = psImageCut(result,
+                            cols,rows,
+                            image,
+                            mask,1,
+                            psRegionSet(startCol[0], endCol[0], startRow[0], endRow[0]),
+                            0,
+                            PS_INTERPOLATE_FLAT);
+        ok(result == NULL, "psImageCut did return NULL given length=0");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_VALUE,
+          "psImageCut did generate proper error message given length=0");
+        psFree(err);
+    
+        psFree(result);
+        psFree(image);
+        psFree(mask);
+        psFree(rows);
+        psFree(cols);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test psImageRadialCut()
+    {
+        psMemId id = psMemGetId();
+        psS32 c = 300;
+        psS32 r = 200;
+        psS32 centerX = c/2;
+        psS32 centerY = r/2;
+        psErr* err = NULL;
+    
+        psImage* image = psImageAlloc(c,r,PS_TYPE_F32);
+        psImage* mask = psImageAlloc(c,r,PS_TYPE_MASK);
+        for (psS32 row = 0; row < image->numRows; row++) {
+            for (psS32 col = 0; col < image->numCols; col++) {
+                image->data.F32[row][col] = sqrtf((col-centerX)*(col-centerX)+(row-centerY)*(row-centerY));
+                if ((row & 0x0F) == 0) {
+                    mask->data.PS_TYPE_MASK_DATA[row][col] = 1;
+                } else {
+                    mask->data.PS_TYPE_MASK_DATA[row][col] = 0;
+                }
+            }
+        }
+    
+        psStats* stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psVector* radii = psVectorAlloc(10,PS_TYPE_F32);
+        for (psS32 i=0; i < 10; i++) {
+            radii->data.F32[i] = 10+i*10;
+        }
+    
+        bool errorFlag = false;    
+        psVector* result = psVectorAlloc(10,PS_TYPE_F32);
+        if (1) {
+            result = psImageRadialCut(result,image,mask,1,centerX,centerY,radii,stat);
+            ok(result != NULL, "psImageRadialCut returned non-NULL");
+            ok(result->type.type == PS_TYPE_F64, "psImageRadialCut produced the correct type");
+            for (psS32 i=0; i < 9; i++) {
+                if (fabs(result->data.F64[i] - (15.0+i*10)) > 1) {
+                    diag("Result was not as expected for radii #%d (%g, expected %d +/- 1)",
+                          result->data.F64[i], (15.0+i*10));
+                    errorFlag = true;
+                }
+            }
+            ok(!errorFlag, "psImageRadialCut() produced the correct data values");
+        }    
+        // again, but without mask
+        psVector* orig = result;
+        if (1) {
+            result = psImageRadialCut(result,image,NULL,1,centerX,centerY,radii,stat);
+            ok(result != NULL, "psImageRadialCut returned non-NULL");
+            ok(result == orig, "psImageRadialCut recycle output parameter");
+    
+            errorFlag = false;    
+            for (psS32 i=0; i < 9; i++) {
+                if (fabs(result->data.F64[i] - (15.0+i*10)) > 1) {
+                    diag("Result was not as expected for radii #%d (%g, expected %d +/- 1)",
+                          result->data.F64[i], (15.0+i*10));
+                    errorFlag = true;
+                }
+            }
+            ok(!errorFlag, "psImageRadialCut() produced the correct data values");
+        }    
+
+    
+        // NULL input image...
+        // Following should be an error
+        // XXX: Verify error
+        result = psImageRadialCut(result,NULL,NULL,1,centerX,centerY,radii,stat);
+        ok(result == NULL, "psImageRadialCut returned NULL with NULL input");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL, 
+          "psImageRadialCut did generate proper error message");
+        psFree(err);
+    
+
+        // NULL input radii...
+        // Following should be an error
+        // XXX: Verify error
+        result = psImageRadialCut(result,image,mask,1,centerX,centerY,NULL,stat);
+        ok(result == NULL, "psImageRadialCut returned NULL with NULL input radii");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL,
+          "psImageRadialCut did generate proper error message");
+        psFree(err);
+
+    
+        // NULL input stat...
+        // Following should be an error
+        // XXX: Verify error
+        result = psImageRadialCut(result,image,mask,1,centerX,centerY,radii,NULL);
+        ok(result == NULL, "psImageRadialCut returned NULL with NULL input stats");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL,
+          "psImageRadialCut did generate proper error message");
+        psFree(err);
+    
+
+        // Bad center X
+        // Following should be an error
+        // XXX: Verify error
+        result = psImageRadialCut(result,image,mask,1,
+                                  c+1,centerY,radii,stat);
+        ok(result == NULL, "psImageRadialCut returned NULL with bad center X");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_VALUE,
+          "psImageRadialCut did generate proper error message");
+        psFree(err);
+
+    
+        // Bad center Y
+        // Following should be an error
+        // XXX: Verify error
+        result = psImageRadialCut(result,image,mask,1,
+                                  centerX,r+1,radii,stat);
+        ok(result == NULL, "psImageRadialCut returned NULL with bad center Y");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_VALUE,
+          "psImageRadialCut did generate proper error message");
+        psFree(err);
+
+    
+        // Bad mask type (N.B., swapped image/mask to do this)
+        // Following should be an error
+        // XXX: Verify error
+        result = psImageRadialCut(result,mask,image,1,
+                                  centerX,r+1,radii,stat);
+        ok(result == NULL, "psImageRadialCut returned NULL with bad mask type");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_TYPE,
+          "psImageRadialCut did generate proper error message");
+        psFree(err);
+
+    
+        // Bad mask size
+        psImage* mask2 = psImageAlloc(c/2,r/2,PS_TYPE_MASK);
+        // Following should be an error
+        // XXX: Verify error
+        result = psImageRadialCut(result,image, mask2, 1,
+                                  centerX,centerY,radii,stat);
+        psFree(mask2);
+        ok(result == NULL, "psImageRadialCut returned NULL with bad mask size");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_SIZE,
+          "psImageRadialCut did generate proper error message");
+        psFree(err);
+
+    
+        // Bad radii size
+        psVector* radii2 = psVectorAlloc(1,PS_TYPE_MASK);
+        // Following should be an error
+        // XXX: Verify error
+        result = psImageRadialCut(result,image, mask, 1,
+                                  centerX,centerY,radii2,stat);
+        psFree(radii2);
+        ok(result == NULL, "psImageRadialCut returned NULL with bad radii size");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_SIZE,
+          "psImageRadialCut did generate proper error message");
+        psFree(err);
+
+    
+        // bad input stat option...
+        stat->options = 0;
+        // Following should be an error
+        // XXX: Verify error
+        result = psImageRadialCut(result,image,mask,1,centerX,centerY,radii,stat);
+        stat->options = PS_STAT_SAMPLE_MEAN;
+        ok(result == NULL, "psImageRadialCut returned NULL with bad input stat option");
+        err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_VALUE,
+          "psImageRadialCut did generate proper error message");
+        psFree(err);
+    
+        psFree(image);
+        psFree(mask);
+        psFree(radii);
+        psFree(stat);
+        psFree(result);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");    
+    }
+
+
+    // testImageRowColError()
+    {
+        psMemId id = psMemGetId();
+        psImage *image = NULL;
+        psVector *out = NULL;
+        int num = 0;
+    
+        // Following should generate error message(for row)
+        // XXX: Verify error
+        out = psImageRow(NULL, NULL, num);
+        ok(out == NULL, "psImageRow() returned NULL with NULL input image");
+
+        // Following should generate error message(for col)
+        // XXX: Verify error
+        out = psImageCol(NULL, NULL, num);
+        ok(out == NULL, "psImagecol() returned NULL with NULL input image");
+        image = psImageAlloc(3, 3, PS_TYPE_F64);
+
+
+        //Test for unallowed row0.
+        P_PSIMAGE_SET_ROW0(image, -2);
+        // Following should generate error message(for row)
+        // XXX: Verify error
+        out = psImageRow(NULL, image, num);
+        ok(out == NULL, "psImageRow() returned NULL with unallowed row0");
+        // Following should generate error message(for col)
+        // XXX: Verify error
+        out = psImageCol(NULL, image, num);
+        ok(out == NULL, "psImageCol() returned NULL with unallowed row0");
+
+    
+        //Test for unallowed col0.
+        P_PSIMAGE_SET_COL0(image, -1);
+        P_PSIMAGE_SET_ROW0(image, 5);
+        // Following should generate error message(for row)
+        // XXX: Verify error
+        out = psImageRow(NULL, image, num);
+        ok(out == NULL, "psImageRow() returned NULL with unallowed col0");
+        // Following should generate error message(for col)
+        // XXX: Verify error
+        out = psImageCol(NULL, image, num);
+        ok(out == NULL, "psImageCol() returned NULL with unallowed col0");
+
+    
+        //Test for unallowed numRows
+        P_PSIMAGE_SET_COL0(image, 10);
+        *(int*)&(image->numRows) = -1;
+        // Following should generate error message(for row)
+        // XXX: Verify error
+        out = psImageRow(NULL, image, num);
+        ok(out == NULL, "psImageRow() returned NULL with unallowed numRows");
+        // Following should generate error message(for col)
+        // XXX: Verify error
+        out = psImageCol(NULL, image, num);
+        ok(out == NULL, "psImageCol() returned NULL with unallowed numRows");
+
+
+        //Test for unallowed numCols
+        *(int*)&(image->numRows) = 3;
+        *(int*)&(image->numCols) = -1;
+        // Following should generate error message(for row)
+        // XXX: Verify error
+        out = psImageRow(NULL, image, num);
+        ok(out == NULL, "psImageRow() returned NULL with unallowed numCols");
+        // Following should generate error message(for col)
+        // XXX: Verify error
+        out = psImageCol(NULL, image, num);
+        ok(out == NULL, "psImageCol() returned NULL with unallowed numCols");
+
+
+        //Test for unallowed row/col number specified.
+        *(int*)&(image->numCols) = 3;
+        num = 8;
+        // Following should generate error message(for row)
+        // XXX: Verify error
+        out = psImageRow(NULL, image, num);
+        ok(out == NULL, "psImageRow() returned NULL with unallowed row/col number");
+        num = 13;
+        // Following should generate error message(for col)
+        // XXX: Verify error
+        out = psImageCol(NULL, image, num);
+        ok(out == NULL, "psImageCol() returned NULL with unallowed row/col number");
+
+
+        num = 3;
+        // Following should generate error message(for row)
+        // XXX: Verify error
+        out = psImageRow(NULL, image, num);
+        ok(out == NULL, "psImageRow() returned NULL");
+        num = 8;
+        // Following should generate error message(for col)
+        // XXX: Verify error
+        out = psImageCol(NULL, image, num);
+        ok(out == NULL, "psImageCol() returned NULL");
+
+
+        num = -10;
+        // Following should generate error message(for row)
+        // XXX: Verify error
+        out = psImageRow(NULL, image, num);
+        ok(out == NULL, "psImageRow() returned NULL");
+        num = -14;
+        // Following should generate error message(for col)
+        // XXX: Verify error
+        out = psImageCol(NULL, image, num);
+        ok(out == NULL, "psImageCol() returned NULL");
+
+        // Test on correct input data for several sizes    
+        genericImageRowColTests(1, 8);
+        genericImageRowColTests(8, 1);
+        genericImageRowColTests(8, 8);
+        genericImageRowColTests(8, 16);
+        genericImageRowColTests(16, 8);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");    
+    }
+
+    // testImageRowColF64()
+    {
+        psMemId id = psMemGetId();
+        psVector *rowcol = NULL;
+        psVector *empty = NULL;
+        psImage *image = NULL;
+    
+        image = psImageAlloc(3, 3, PS_TYPE_F64);
+        rowcol = psVectorAlloc(3, PS_TYPE_F64);
+
+        image->data.F64[0][0] = 666.666;
+        image->data.F64[1][0] = 66.6;
+        image->data.F64[2][0] = 6.66;
+        image->data.F64[0][1] = 6.6;
+        image->data.F64[1][1] = 6.666;
+        image->data.F64[2][1] = 66.666;
+        image->data.F64[0][2] = 666.6;
+        image->data.F64[1][2] = 666.66;
+        image->data.F64[2][2] = 66.66;
+    
+        //Test for error with NULL image
+        empty = psImageCol(empty, NULL, 0);
+        ok(empty == NULL, "psImageCol returned NULL for NULL image input");
+        //Test for error with Out of Range Row
+        empty = psImageRow(empty, image, 5);
+        ok(empty == NULL, "psImageRow returned NULL for out of range row input");
+
+        rowcol->data.F64[0] = 1.1;
+        rowcol->data.F64[2] = 2.2;
+        //Test recycling of non-NULL vector & correct output
+        rowcol = psImageCol(rowcol, image, 1);
+        double test1, test2;
+        double TOLTST = .001;
+        test1 = fabs(rowcol->data.F64[0]-6.6);
+        test2 = fabs(rowcol->data.F64[2]-66.666);
+        ok((test1<=TOLTST) && (test2<=TOLTST), "psImageCol returned correct values");
+
+        rowcol = psImageRow(rowcol, image, 1);
+        test1 = fabs(rowcol->data.F64[0]-66.6);
+        test2 = fabs(rowcol->data.F64[2]-666.66);
+        ok(!((test1>TOLTST) || (test2>TOLTST)),
+           "psImageRow returned correct values");
+        psFree(rowcol);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageRowColF32()
+    {
+        psMemId id = psMemGetId();
+        psVector *rowcol = NULL;
+        psVector *empty = NULL;
+        psImage *image = NULL;
+        psImage *emptyImage = NULL;
+    
+        float test1;
+        float test2;
+        float TOLTST = .01;
+    
+        image = psImageAlloc(3, 3, PS_TYPE_F32);
+        rowcol = psVectorAlloc(3, PS_TYPE_F32);
+        rowcol->n = rowcol->nalloc;
+    
+        image->data.F32[0][0] = 666.666;
+        image->data.F32[1][0] = 66.6;
+        image->data.F32[2][0] = 6.66;
+        image->data.F32[0][1] = 6.6;
+        image->data.F32[1][1] = 6.666;
+        image->data.F32[2][1] = 66.666;
+        image->data.F32[0][2] = 666.6;
+        image->data.F32[1][2] = 666.66;
+        image->data.F32[2][2] = 66.66;
+    
+        //Test for error with NULL image
+        empty = psImageCol(empty, emptyImage, 0);
+        ok(empty == NULL, "psImageCol returned NULL for NULL image input");
+        //Test for error with Out of Range Row
+        empty = psImageRow(empty, image, 5);
+        ok(empty == NULL, "psImageRow returned NULL for out of range row input");
+
+        rowcol->data.F32[0] = 1.1;
+        rowcol->data.F32[2] = 2.2;
+        //Test recycling of non-NULL vector & correct output
+        rowcol = psImageCol(rowcol, image, 1);
+        test1 = fabs(rowcol->data.F32[0]-6.6);
+        test2 = fabs(rowcol->data.F32[2]-66.666);
+        ok((test1<=TOLTST) && (test2<=TOLTST), "psImageCol returned correct values");
+    
+        psFree(rowcol);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageRowColU64()
+    {
+        psMemId id = psMemGetId();
+        psVector *rowcol = NULL;
+        psVector *empty = NULL;
+        psImage *image = NULL;
+        psImage *emptyImage = NULL;
+    
+        image = psImageAlloc(3, 3, PS_TYPE_U64);
+        rowcol = psVectorAlloc(3, PS_TYPE_U64);
+    
+        image->data.U64[0][0] = 1;
+        image->data.U64[1][0] = 2;
+        image->data.U64[2][0] = 3;
+        image->data.U64[0][1] = 4;
+        image->data.U64[1][1] = 5;
+        image->data.U64[2][1] = 6;
+        image->data.U64[0][2] = 7;
+        image->data.U64[1][2] = 8;
+        image->data.U64[2][2] = 9;
+    
+        //Test for error with NULL image
+        empty = psImageCol(empty, emptyImage, 0);
+        ok(empty == NULL, "psImageCol returned NULL for NULL image input");
+        //Test for error with Out of Range Row
+        empty = psImageRow(empty, image, 5);
+        ok(empty == NULL, "psImageRow returned NULL for out of range row input");
+
+        //Test recycling of non-NULL vector & correct output
+        rowcol = psImageCol(rowcol, image, 1);
+        ok((rowcol->data.U64[0] == 4) && (rowcol->data.U64[2] == 6),
+          "psImageCol returned correct values (%u %u)",
+           rowcol->data.U64[0], rowcol->data.U64[2]);
+        psFree(rowcol);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageRowColU32()
+    {
+        psMemId id = psMemGetId();
+        psVector *rowcol = NULL;
+        psVector *empty = NULL;
+        psImage *image = NULL;
+        psImage *emptyImage = NULL;
+    
+        image = psImageAlloc(3, 3, PS_TYPE_U32);
+        rowcol = psVectorAlloc(3, PS_TYPE_U32);
+    
+        image->data.U32[0][0] = 1;
+        image->data.U32[1][0] = 2;
+        image->data.U32[2][0] = 3;
+        image->data.U32[0][1] = 4;
+        image->data.U32[1][1] = 5;
+        image->data.U32[2][1] = 6;
+        image->data.U32[0][2] = 7;
+        image->data.U32[1][2] = 8;
+        image->data.U32[2][2] = 9;
+    
+        //Test for error with NULL image
+        empty = psImageCol(empty, emptyImage, 0);
+        ok(empty == NULL, "psImageCol returned NULL for NULL image input");
+        //Test for error with Out of Range Row
+        empty = psImageRow(empty, image, 5);
+        ok(empty == NULL, "psImageRow returned NULL for out of range row input");
+
+        //Test recycling of non-NULL vector & correct output
+        rowcol = psImageCol(rowcol, image, 1);
+        ok(rowcol->data.U32[0] == 4 && rowcol->data.U32[2] == 6,
+          "psImageCol returned correct values (%d %d)",
+          rowcol->data.U32[0] == 4, rowcol->data.U32[2]);
+    
+        psFree(rowcol);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageRowColS32()
+    {
+        psMemId id = psMemGetId();
+        psVector *rowcol = NULL;
+        psVector *empty = NULL;
+        psImage *image = NULL;
+        psImage *emptyImage = NULL;
+    
+        image = psImageAlloc(3, 3, PS_TYPE_S32);
+        rowcol = psVectorAlloc(3, PS_TYPE_S32);
+    
+        image->data.S32[0][0] = 1;
+        image->data.S32[1][0] = 2;
+        image->data.S32[2][0] = 3;
+        image->data.S32[0][1] = 4;
+        image->data.S32[1][1] = 5;
+        image->data.S32[2][1] = 6;
+        image->data.S32[0][2] = 7;
+        image->data.S32[1][2] = 8;
+        image->data.S32[2][2] = 9;
+
+        //Test for error with NULL image
+        empty = psImageCol(empty, emptyImage, 0);
+        ok(empty == NULL, "psImageCol returned NULL for NULL image input");
+        //Test for error with Out of Range Row
+        empty = psImageRow(empty, image, 5);
+        ok(empty == NULL, "psImageRow returned NULL for out of range row input");
+
+        //Test recycling of non-NULL vector & correct output
+        rowcol = psImageCol(rowcol, image, 1);
+        ok(rowcol->data.S32[0] == 4 && rowcol->data.S32[2] == 6,
+          "psImageCol returned correct values (%d %d)",
+           rowcol->data.S32[0], rowcol->data.S32[2]);
+        psFree(rowcol);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageRowColS64()
+    {
+        psMemId id = psMemGetId();
+        psVector *rowcol = NULL;
+        psVector *empty = NULL;
+        psImage *image = NULL;
+        psImage *emptyImage = NULL;
+    
+        image = psImageAlloc(3, 3, PS_TYPE_S64);
+        rowcol = psVectorAlloc(3, PS_TYPE_S64);
+    
+        image->data.S64[0][0] = 1;
+        image->data.S64[1][0] = 2;
+        image->data.S64[2][0] = 3;
+        image->data.S64[0][1] = 4;
+        image->data.S64[1][1] = 5;
+        image->data.S64[2][1] = 6;
+        image->data.S64[0][2] = 7;
+        image->data.S64[1][2] = 8;
+        image->data.S64[2][2] = 9;
+    
+        //Test for error with NULL image
+        empty = psImageCol(empty, emptyImage, 0);
+        ok(empty == NULL, "psImageCol returned NULL for NULL image input");
+        //Test for error with Out of Range Row
+        empty = psImageRow(empty, image, 5);
+        ok(empty == NULL, "psImageRow returned NULL for out of range row input");
+
+        //Test recycling of non-NULL vector & correct output
+        rowcol = psImageCol(rowcol, image, 1);
+        ok(rowcol->data.S64[0] == 4 && rowcol->data.S64[2] == 6,
+          "psImageCol returned correct values");
+        psFree(rowcol);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageRowColS16()
+    {
+        psMemId id = psMemGetId();
+        psVector *rowcol = NULL;
+        psVector *empty = NULL;
+        psImage *image = NULL;
+        psImage *emptyImage = NULL;
+    
+        image = psImageAlloc(3, 3, PS_TYPE_S16);
+        rowcol = psVectorAlloc(3, PS_TYPE_S16);
+    
+        image->data.S16[0][0] = 1;
+        image->data.S16[1][0] = 2;
+        image->data.S16[2][0] = 3;
+        image->data.S16[0][1] = 4;
+        image->data.S16[1][1] = 5;
+        image->data.S16[2][1] = 6;
+        image->data.S16[0][2] = 7;
+        image->data.S16[1][2] = 8;
+        image->data.S16[2][2] = 9;
+    
+        //Test for error with NULL image
+        empty = psImageCol(empty, emptyImage, 0);
+        ok(empty == NULL, "psImageCol returned NULL for NULL image input");
+        //Test for error with Out of Range Row
+        empty = psImageRow(empty, image, 5);
+        ok(empty == NULL, "psImageRow returned NULL for out of range row input");
+
+        //Test recycling of non-NULL vector & correct output
+        rowcol = psImageCol(rowcol, image, 1);
+        ok(rowcol->data.S16[0] == 4 && rowcol->data.S16[2] == 6,
+           "psImageRow returned correct values");
+        psFree(rowcol);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageRowColU16()
+    {
+        psMemId id = psMemGetId();
+        psVector *rowcol = NULL;
+        psVector *empty = NULL;
+        psImage *image = NULL;
+        psImage *emptyImage = NULL;
+    
+        image = psImageAlloc(3, 3, PS_TYPE_U16);
+        rowcol = psVectorAlloc(3, PS_TYPE_U16);
+    
+        image->data.U16[0][0] = 1;
+        image->data.U16[1][0] = 2;
+        image->data.U16[2][0] = 3;
+        image->data.U16[0][1] = 4;
+        image->data.U16[1][1] = 5;
+        image->data.U16[2][1] = 6;
+        image->data.U16[0][2] = 7;
+        image->data.U16[1][2] = 8;
+        image->data.U16[2][2] = 9;
+    
+        //Test for error with NULL image
+        empty = psImageCol(empty, emptyImage, 0);
+        ok(empty == NULL, "psImageCol returned NULL for NULL image input");
+        //Test for error with Out of Range Row
+        empty = psImageRow(empty, image, 5);
+        ok(empty == NULL, "psImageRow returned NULL for out of range row input");
+
+        //Test recycling of non-NULL vector & correct output
+        rowcol = psImageCol(rowcol, image, 1);
+        ok(rowcol->data.U16[0] == 4 && rowcol->data.U16[2] == 6,
+           "psImageRow returned correct values");
+        psFree(rowcol);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageRowColU8()
+    {
+        psMemId id = psMemGetId();
+        psVector *rowcol = NULL;
+        psVector *empty = NULL;
+        psImage *image = NULL;
+        psImage *emptyImage = NULL;
+    
+        image = psImageAlloc(3, 3, PS_TYPE_U8);
+        rowcol = psVectorAlloc(3, PS_TYPE_U8);
+    
+        image->data.U8[0][0] = 1;
+        image->data.U8[1][0] = 2;
+        image->data.U8[2][0] = 3;
+        image->data.U8[0][1] = 4;
+        image->data.U8[1][1] = 5;
+        image->data.U8[2][1] = 6;
+        image->data.U8[0][2] = 7;
+        image->data.U8[1][2] = 8;
+        image->data.U8[2][2] = 9;
+    
+        //Test for error with NULL image
+        empty = psImageCol(empty, emptyImage, 0);
+        ok(empty == NULL, "psImageCol returned NULL for NULL image input");
+        //Test for error with Out of Range Row
+        empty = psImageRow(empty, image, 5);
+        ok(empty == NULL, "psImageRow returned NULL for out of range row input");
+
+        //Test recycling of non-NULL vector & correct output
+        rowcol = psImageCol(rowcol, image, 1);
+        ok(rowcol->data.U8[0] == 4 && rowcol->data.U8[2] == 6,
+           "psImageRow returned correct values");
+        psFree(rowcol);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageRowColS8()
+    {
+        psMemId id = psMemGetId();
+        psVector *rowcol = NULL;
+        psVector *empty = NULL;
+        psImage *image = NULL;
+        psImage *emptyImage = NULL;
+    
+        image = psImageAlloc(3, 3, PS_TYPE_S8);
+        rowcol = psVectorAlloc(3, PS_TYPE_S8);
+    
+        image->data.S8[0][0] = 1;
+        image->data.S8[1][0] = 2;
+        image->data.S8[2][0] = 3;
+        image->data.S8[0][1] = 4;
+        image->data.S8[1][1] = 5;
+        image->data.S8[2][1] = 6;
+        image->data.S8[0][2] = 7;
+        image->data.S8[1][2] = 8;
+        image->data.S8[2][2] = 9;
+
+        //Test for error with NULL image
+        empty = psImageCol(empty, emptyImage, 0);
+        ok(empty == NULL, "psImageCol returned NULL for NULL image input");
+        //Test for error with Out of Range Row
+        empty = psImageRow(empty, image, 5);
+        ok(empty == NULL, "psImageRow returned NULL for out of range row input");
+
+        //Test recycling of non-NULL vector & correct output
+        rowcol = psImageCol(rowcol, image, 1);
+        ok(rowcol->data.S8[0] == 4 && rowcol->data.S8[2] == 6,
+           "psImageRow returned correct values");
+        psFree(rowcol);
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImagePixelManip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImagePixelManip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImagePixelManip.c	(revision 22322)
@@ -0,0 +1,482 @@
+/** @file  tst_psImageManip.c
+ *
+ *  @brief Contains the tests for psImageManip.[ch]
+ *
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-06-04 20:25:32 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define VERBOSE false
+
+void genericImageClipTest(int numRows, int numCols) {
+    psMemId id = psMemGetId();
+
+    psImage *image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    for (int row = 0 ; row < numRows ; row++) {
+        for (int col = 0 ; col < numCols ; col++) {
+            image->data.F32[row][col] = (float) (row + col);
+        }
+    }
+    psF32 min = (psF64)numRows/2.0;
+    psF32 max = (psF64)numRows;
+
+    psS32 retVal = psImageClip(image, min, (double)PS_MIN_F32, max, (double)PS_MAX_F32);
+    int numClipped = 0;
+    bool errorFlag = false;
+    for (int row=0;row<numRows;row++) {
+        for (int col=0;col<numCols;col++) {
+            psF32 value = (psF32)(row+col);
+            if (value < min) {
+                numClipped++;
+                value = PS_MIN_F32;
+            } else if (value > max) {
+                numClipped++;
+                value = PS_MAX_F32;
+            }
+            if (fabsf(image->data.F32[row][col]-value) > FLT_EPSILON) {
+                diag("Pixel value is not as expected (%g vs %g) at %u,%u",
+                     (psF64)image->data.F32[row][col], (psF64)value, col, row);
+                errorFlag = true;
+            }
+        }
+    }
+    ok(!errorFlag, "psImageClip() produced the correct data values");
+    ok(retVal == numClipped, "Got the expected number of clips");
+    psFree(image);
+
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(122);
+
+    // test psImageClip()
+    {
+        psMemId id = psMemGetId();
+
+        //psImageClip shall limit the minimum and maximum data value within a psImage structure
+
+        // psImageClip shall limit the minimum and maximum data value within a
+        // psImage structure to a specified min and max value.
+        //
+        // Verify the returned integer is equal to the number of pixels clipped,
+        // if the input psImage structure contains known values and input parameters
+        // min and max have know values.
+        //
+        // Verify the psImage structure specified by the input parameter input is
+        // modified to contain the expected values, if the input psImage structure
+        // contains known values, min and max are specified and vmin and vmax
+        // parameters are known.
+        //
+        // Verify the retuned integer is zero, psImage structure input is unmodified
+        // and program executions doesn't stop, if input parameter psImage structure
+        // pointer is NULL.
+        //
+        // Verify the retuned integer is zero, psImage structure input is unmodified
+        // and program executions doesn't stop, if input parameter min is larger than max.
+        // create image
+
+        genericImageClipTest(8, 1);
+        genericImageClipTest(1, 8);
+        genericImageClipTest(8, 8);
+        genericImageClipTest(8, 16);
+        genericImageClipTest(16, 8);
+
+        #define testImageClipByType(datatype) \
+        { \
+            psU32 c = 128; \
+            psU32 r = 256; \
+            psF64 min; \
+            psF64 max; \
+            psS32 numClipped = 0; \
+            psS32 retVal; \
+            psMemId id = psMemGetId(); \
+            psImage *img = psImageAlloc(c,r,PS_TYPE_##datatype); \
+            for (psU32 row=0;row<r;row++) { \
+                ps##datatype* imgRow = img->data.datatype[row]; \
+                for (psU32 col=0;col<c;col++) { \
+                    imgRow[col] = (ps##datatype)(row+col); \
+                } \
+            } \
+            min = (psF64)r/2.0; \
+            max = (psF64)r; \
+            \
+            retVal = psImageClip(img,min,(double)PS_MIN_##datatype,max,(double)PS_MAX_##datatype); \
+            \
+            numClipped = 0; \
+            bool errorFlag = false; \
+            for (psU32 row=0;row<r;row++) { \
+                ps##datatype* imgRow = img->data.datatype[row]; \
+                for (psU32 col=0;col<c;col++) { \
+                    ps##datatype value = (ps##datatype)(row+col); \
+                    if (value < min) { \
+                        numClipped++; \
+                        value = PS_MIN_##datatype; \
+                    } else if (value > max) { \
+                        numClipped++; \
+                        value = PS_MAX_##datatype; \
+                    } \
+                    if (fabsf(imgRow[col]-value) > FLT_EPSILON) { \
+                        diag("Pixel value is not as expected (%g vs %g) at %u,%u", \
+                             (psF64)imgRow[col],(psF64)value,col,row); \
+                        errorFlag = true; \
+                    } \
+                } \
+            } \
+            ok(!errorFlag, "psImageClip() produced the correct data values"); \
+            ok(retVal == numClipped, "Got the expected number of clips"); \
+            psFree(img); \
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks"); \
+        }
+
+        testImageClipByType(F64);
+        testImageClipByType(F32);
+        testImageClipByType(S32);
+        testImageClipByType(S16);
+        testImageClipByType(S8);
+        testImageClipByType(U32);
+        testImageClipByType(U16);
+        testImageClipByType(U8);
+
+        psF64 min=0.0;
+        psF64 max=0.0;
+        psS32 retVal;
+        psImage *img = NULL;
+        // Verify the retuned integer is zero, psImage structure input is unmodified
+        // and program executions doesn't stop, if input parameter psImage structure
+        // pointer is NULL.
+        retVal = psImageClip(NULL,min,-1.0f,max,-2.0f);
+        ok(retVal == 0, "Expected zero return for clips of a NULL image");
+
+        // Verify the retuned integer is zero, psImage structure input is unmodified
+        // and program executions doesn't stop, if input parameter min is larger than max.
+        // Following should be an error (max<min)
+        // XXX: Verify error
+        retVal = psImageClip(img,max,-1.0f,min,-2.0f);
+        ok(retVal == 0, "Expected zero return for clips when max < min");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageClipNAN()
+    {
+        psMemId id = psMemGetId();
+        psImage* img = NULL;
+        psU32 c = 128;
+        psU32 r = 256;
+        psS32 numClipped = 0;
+        psS32 retVal;
+
+        // psImageClipNaN shall modified pixel values of NaN with a specified value");
+
+        // psImageClipNaN shall modify a psImage structure with pixel values set
+        // to NaN to a value specified as an input parameter.
+        //
+        // Verify the returned integer is equal to the number of pixels modified
+        // and the psImage is modified at locations where NaN pixels where
+        // located to the value specified in the input parameter value.
+        //
+        // Verify the returned integer is zero and program execution doesn't stop,
+        // if the input parameter psImage structure pointer is NULL.
+        // create image
+        #define testImageClipNaNByType(datatype) \
+        { \
+            psMemId id = psMemGetId(); \
+            img = psImageAlloc(c,r,PS_TYPE_##datatype); \
+            for (unsigned row=0;row<r;row++) { \
+                ps##datatype* imgRow = img->data.datatype[row]; \
+                for (unsigned col=0;col<c;col++) { \
+                    if (row == col) { \
+                        imgRow[col] = NAN; \
+                    } else if (row+1 == col) { \
+                        imgRow[col] = INFINITY; \
+                    } else { \
+                        imgRow[col] = (ps##datatype)(row+col); \
+                    } \
+                } \
+            } \
+            \
+            retVal = psImageClipNaN(img,-1.0f); \
+            \
+            numClipped = 0; \
+            bool errorFlag = false; \
+            for (unsigned row=0;row<r;row++) { \
+                ps##datatype* imgRow = img->data.datatype[row]; \
+                for (unsigned col=0;col<c;col++) { \
+                    ps##datatype value = (ps##datatype)(row+col); \
+                    if ( (row == col) || (row+1 == col) ) { \
+                        numClipped++; \
+                        value = -1.0; \
+                    } \
+                    if (fabsf(imgRow[col]-value) > FLT_EPSILON) { \
+                        diag("Pixel value is not as expected (%f vs %f) at %d,%d", \
+                             imgRow[col],value,col,row); \
+                        errorFlag = true; \
+                    } \
+                } \
+            } \
+            ok(!errorFlag, "psImageClip() produced the correct data values"); \
+            ok(retVal == numClipped, "Got the expected number of clips"); \
+            psFree(img); \
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks"); \
+        }
+
+        testImageClipNaNByType(F32);
+        testImageClipNaNByType(F64);
+
+        // Verify the retuned integer is zero, psImage structure input is unmodified
+        // and program executions doesn't stop, if input parameter psImage structure
+        // pointer is NULL.
+        retVal = psImageClipNaN(NULL,-1.0f);
+        ok(retVal == 0, "Expected zero return for clips of a NULL image");
+
+        // Verify program execution doesn't stop if the input image type is something
+        // other than F32, F64.
+        img = psImageAlloc(c,r,PS_TYPE_S32);
+        // Following should be an error (incorrect type)
+        // XXX: Verify error
+        retVal = psImageClipNaN(img,2.0f);
+        ok(retVal == 0, "Expected zero return for clip of incorrect image type");
+        psFree(img);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // testImageOverlay()
+    {
+        psMemId id = psMemGetId();
+        psImage* img = NULL;
+        psImage* img2 = NULL;
+        psImage* img3 = NULL;
+        psImage* img4 = NULL;
+        psU32 c = 128;
+        psU32 r = 256;
+        psS32 retVal;
+
+        // psImageSectionOverlay shall modified pixel values in a psImage structure to
+        // be equal to the value of the originaldata and an overlay image with a
+        // specified operation. Valid operations include =, +, -, *, /.
+        //
+        // Verify the returned integer is zero
+        // and the input parameter psImage structure is modified at the specified
+        // location and range with the given overlay image and the specified
+        // function. Cases should include all the valid operations. Comparison of
+        // expected values should include a delta to allow for testing on
+        // different platforms.
+        #define testOverlayTypeOP(DATATYPE,OP,OPSTRING) \
+        { \
+            psMemId id = psMemGetId(); \
+            img = psImageAlloc(c,r,PS_TYPE_##DATATYPE); \
+            for (unsigned row=0;row<r;row++) { \
+                ps##DATATYPE* imgRow = img->data.DATATYPE[row]; \
+                for (unsigned col=0;col<c;col++) { \
+                    imgRow[col] = 6.0; \
+                } \
+            } \
+            img2 = psImageAlloc(c/2,r/2,PS_TYPE_##DATATYPE); \
+            for (unsigned row=0;row<r/2;row++) { \
+                ps##DATATYPE* img2Row = img2->data.DATATYPE[row]; \
+                for (unsigned col=0;col<c/2;col++) { \
+                    img2Row[col] = 2.0; \
+                } \
+            } \
+            retVal = psImageOverlaySection(img,img2,c/4,r/4,OPSTRING); \
+            if (retVal == 0) { \
+                psError(PS_ERR_UNKNOWN, true,"psImageOverlaySection returned zero with %s op", \
+                        OPSTRING); \
+                return 1; \
+            } \
+            bool errorFlag = false; \
+            for (unsigned row=0;row<r;row++) { \
+                ps##DATATYPE* imgRow = img->data.DATATYPE[row]; \
+                ps##DATATYPE* img2Row = img2->data.DATATYPE[row]; \
+                for (unsigned col=0;col<c;col++) { \
+                    ps##DATATYPE val = 6.0; \
+                    if ( ! (row < r/4 || row >= r/2+r/4 || col < c/4 || col >= c/2+c/4)) { \
+                        val OP 2.0; \
+                    } \
+                    if (fabsf(imgRow[col] - val) > FLT_EPSILON) { \
+                        diag("Value incorrect at %d,%d (%.2f vs %.2f for %s)", \
+                             col,row,imgRow[col],val,OPSTRING); \
+                        errorFlag = true; \
+                    } \
+                    if (row < r/2 && col < c/2 && fabsf(img2Row[col] - 2.0) > FLT_EPSILON) { \
+                        diag("Overlay modified at %d,%d (%.2f for %s)", \
+                             col,row,img2Row[col],OPSTRING); \
+                        errorFlag = true; \
+                    } \
+                } \
+            } \
+            ok(!errorFlag, "psImageOverlaySection() produced the correct data values"); \
+            psFree(img); \
+            psFree(img2); \
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks"); \
+        }
+
+        #define testOverlayType(DATATYPE) \
+        testOverlayTypeOP(DATATYPE,+=,"+"); \
+        testOverlayTypeOP(DATATYPE,-=,"-"); \
+        testOverlayTypeOP(DATATYPE,*=,"*");\
+        testOverlayTypeOP(DATATYPE,/=,"/");\
+        testOverlayTypeOP(DATATYPE,=,"=");
+
+        testOverlayType(F64);
+        testOverlayType(F32);
+        testOverlayType(S16);
+        testOverlayType(S8);
+        testOverlayType(U16);
+        testOverlayType(U8);
+
+        // Verify the returned integer is equal to non-zero and the input psImage structure
+        // is unmodified, if the overlay specified is not within the data range of the
+        // input psImage structure.
+        img = psImageAlloc(c,r,PS_TYPE_F32);
+        for (unsigned row=0;row<r;row++)
+        {
+            psF32* imgRow = img->data.F32[row];
+            for (unsigned col=0;col<c;col++) {
+                imgRow[col] = 6.0f;
+            }
+        }
+        img2 = psImageAlloc(c,r,PS_TYPE_F32);
+        for (unsigned row=0;row<r;row++)
+        {
+            psF32* img2Row = img2->data.F32[row];
+            for (unsigned col=0;col<c;col++) {
+                img2Row[col] = 2.0f;
+            }
+        }
+        img3 = psImageAlloc(c,r,PS_TYPE_S64);
+        for (unsigned row=0;row<r;row++)
+        {
+            psS64* img3Row = img3->data.S64[row];
+            for (unsigned col=0;col<c;col++) {
+                img3Row[col] = 6.0f;
+            }
+        }
+        img4 = psImageAlloc(c,r,PS_TYPE_S64);
+        for (unsigned row=0;row<r;row++)
+        {
+            psS64* img4Row = img4->data.S64[row];
+            for (unsigned col=0;col<c;col++) {
+                img4Row[col] = 2.0f;
+            }
+        }
+
+        // Following should error as overlay isn't within image boundaries
+        // XXX: Verify error
+        retVal = psImageOverlaySection(img,img2,c/4,r/4,"+");
+        ok(retVal == 0, "psImageOverlaySection did return zero when overlay too big");
+
+        bool errorFlag = false;
+        for (unsigned row=0;row<r;row++)
+        {
+            psF32* imgRow = img->data.F32[row];
+            for (unsigned col=0;col<c;col++) {
+                if (imgRow[col] != 6.0f) {
+                    diag("Input image modified when overlay size too big");
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psImageOverlaySection() produced the correct data values");
+
+
+        // Verify the returned integer is equal to non-zero, the input psImage
+        // structure is unmodified and program execution doesn't stop, if the
+        // overlay specified is NULL.
+        // Following should error as overlay is NULL
+        // XXX: Verify error
+        retVal = psImageOverlaySection(img,NULL,c/4,r/4,"+");
+        ok(retVal == 0, "psImageOverlaySection did return zero when overlay too big");
+        errorFlag = false;
+        for (unsigned row=0;row<r;row++)
+        {
+            psF32* imgRow = img->data.F32[row];
+            for (unsigned col=0;col<c;col++) {
+                if (imgRow[col] != 6.0f) {
+                    diag("Input image modified when overlay NULL");
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psImageOverlaySection() produced the correct data values");
+
+
+        // Verify the returned integer is equal to non-zero and program execution
+        // doesn't stop, if the input parameter image is NULL.
+        // Following should error as image input is NULL
+        // XXX: Verify error
+        retVal = psImageOverlaySection(NULL,img2,c/4,r/4,"+");
+        ok(retVal == 0, "psImageOverlaySection returned zero when overlay too big");
+
+
+        // Verify the return integer is equal to non-zero and program execution
+        // doesn't stop, if the specified operator is not =,+,-,*,/
+        // Following should error as operator is incorrect
+        // XXX: Verify error
+        retVal = psImageOverlaySection(img,img2,0,0,"$");
+        ok(retVal == 0, "psImageOverlaySection returned zero when overlay too big");
+
+
+        // Verify the return integer is equal to non-zero and program execution
+        // doesn't stop, if the specified operator is NULL
+        // Following should error as operator is incorrect
+        // XXX: Verify error
+        retVal = psImageOverlaySection(img,img2,0,0,NULL);
+        ok(retVal == 0, "psImageOverlaySection returned zero when overlay operator is NULL");
+
+
+        // Verify the return integer is equal to non-zero and program execution
+        // doesn't stop, if overlay image is a different type than the input image
+        // Following should error as overlay is a different type
+        // XXX: Verify error
+        retVal = psImageOverlaySection(img,img3,0,0,"+");
+        ok(retVal == 0, "psImageOverlaySection returned zero when overlay image type is different than input image");
+
+
+        // Verify program execution doen't stop, if the overly image contains
+        // zero values with division operation is specified.
+        // XXX: This currently doesn't work.  Apparently, the psImageOverlaySection()
+        // will happily divide by 0.0.
+        for (unsigned row=0;row<r;row++)
+        {
+            psF32* img2Row = img2->data.F32[row];
+            for (unsigned col=0;col<c;col++) {
+                img2Row[col] = 0.0f;
+            }
+        }
+        retVal = psImageOverlaySection(img,img2,0,0,"/");
+        ok(retVal != 0, "psImageOverlaySection returned non-zero when checking divide-by-zero");
+        errorFlag = false;
+
+        for (unsigned row=0;row<r;row++)
+        {
+            for (unsigned col=0;col<c;col++) {
+                if (!isnan(img->data.F32[row][col])) {
+                    if (VERBOSE) diag("img[%d][%d] is %f, should be NAN\n", row, col, img->data.F32[row][col]);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psImageOverlaySection() properly set data to NANs");
+
+        psFree(img);
+        psFree(img2);
+        psFree(img3);
+        psFree(img4);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageShift.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageShift.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageShift.c	(revision 22322)
@@ -0,0 +1,179 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int main (void)
+{
+    plan_tests(39);
+
+//    diag("psImageShift() tests");
+
+    // tests using BILINEAR interpolation
+    {
+        psMemId id = psMemGetId();
+
+//        diag("shift a delta function by an integer offset");
+
+        // generate simple image (delta function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // use delta function as input test image
+        image->data.F32[10][10] = 1;
+
+        // integer shifts should be exact
+        psImage *shift = psImageShift (NULL, image, 1.0, 1.0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (shift->data.F32[10][10], 0.0, "point 10,10 should be 0.0 : %f", shift->data.F32[10][10]);
+        is_float (shift->data.F32[10][11], 0.0, "point 10,11 should be 0.0 : %f", shift->data.F32[10][11]);
+        is_float (shift->data.F32[10][12], 0.0, "point 10,12 should be 0.0 : %f", shift->data.F32[10][12]);
+        is_float (shift->data.F32[11][10], 0.0, "point 11,10 should be 0.0 : %f", shift->data.F32[11][10]);
+        is_float (shift->data.F32[11][11], 1.0, "point 11,11 should be 1.0 : %f", shift->data.F32[11][11]);
+        is_float (shift->data.F32[11][12], 0.0, "point 11,12 should be 0.0 : %f", shift->data.F32[11][12]);
+        is_float (shift->data.F32[12][10], 0.0, "point 12,10 should be 0.0 : %f", shift->data.F32[12][10]);
+        is_float (shift->data.F32[12][11], 0.0, "point 12,11 should be 0.0 : %f", shift->data.F32[12][11]);
+        is_float (shift->data.F32[12][12], 0.0, "point 12,12 should be 0.0 : %f", shift->data.F32[12][12]);
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // tests using BILINEAR interpolation
+    {
+        psMemId id = psMemGetId();
+
+//        diag("shift a delta function by an fractional pixel offset");
+
+        // generate simple image (delta function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // XXX is this needed?
+        psImageInit (image, 0.0);
+
+        // use delta function as input test image
+        image->data.F32[10][10] = 1;
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShift (NULL, image, 0.2, 0.4, 0.0, PS_INTERPOLATE_BILINEAR);
+//        diag("these require tolerance of 4 epsilon (why?)");
+        is_float_tol (shift->data.F32[10][10], 0.8*0.6, 4*FLT_EPSILON, "point 10,10 should be %f : %f", 0.8*0.6, shift->data.F32[10][10]);
+        is_float_tol (shift->data.F32[10][11], 0.2*0.6, 4*FLT_EPSILON, "point 10,11 should be %f : %f", 0.2*0.6, shift->data.F32[10][11]);
+        is_float_tol (shift->data.F32[10][12], 0.0,     4*FLT_EPSILON, "point 10,12 should be %f : %f", 0.0,     shift->data.F32[10][12]);
+        is_float_tol (shift->data.F32[11][10], 0.8*0.4, 4*FLT_EPSILON, "point 11,10 should be %f : %f", 0.8*0.4, shift->data.F32[11][10]);
+        is_float_tol (shift->data.F32[11][11], 0.2*0.4, 4*FLT_EPSILON, "point 11,11 should be %f : %f", 0.2*0.4, shift->data.F32[11][11]);
+        is_float_tol (shift->data.F32[11][12], 0.0,     4*FLT_EPSILON, "point 11,12 should be %f : %f", 0.0,     shift->data.F32[11][12]);
+        is_float_tol (shift->data.F32[12][10], 0.0,     4*FLT_EPSILON, "point 12,10 should be %f : %f", 0.0,     shift->data.F32[12][10]);
+        is_float_tol (shift->data.F32[12][11], 0.0,     4*FLT_EPSILON, "point 12,11 should be %f : %f", 0.0,     shift->data.F32[12][11]);
+        is_float_tol (shift->data.F32[12][12], 0.0,     4*FLT_EPSILON, "point 12,12 should be %f : %f", 0.0,     shift->data.F32[12][12]);
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test by measuring gaussian centroid before and after
+    // using BILINEAR interpolation
+    {
+        psMemId id = psMemGetId();
+
+//        diag("shift a gaussian and measure centroid");
+
+        // generate simple image (gauss function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // build a simple gaussian (16,16),(2,1) as input test image
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                float z = PS_SQR(i - 16.0)/8.0 + PS_SQR(j - 16.0)/2.0;
+                image->data.F32[j][i] = exp(-z);
+            }
+        }
+
+        // measure centroid of unshifted gaussian (should be 16.0,16.0)
+        float xo = 0.0;
+        float yo = 0.0;
+        float no = 0.0;
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                xo += i*image->data.F32[j][i];
+                yo += j*image->data.F32[j][i];
+                no += image->data.F32[j][i];
+            }
+        }
+        xo /= no;
+        yo /= no;
+
+//        diag ("these require a tolerance of 100 epsilon");
+        is_float_tol (xo, 16.0, 100*FLT_EPSILON, "x-centroid should be %f : %f", 16.0, xo);
+        is_float_tol (xo, 16.0, 100*FLT_EPSILON, "y-centroid should be %f : %f", 16.0, yo);
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShift (NULL, image, 1.2, -1.2, 0.0, PS_INTERPOLATE_BILINEAR);
+
+        // measure centroid of shifted gaussian (should be 17.2,14.8)
+        xo = yo = no = 0.0;
+        for (int j = 0; j < shift->numRows; j++)
+        {
+            for (int i = 0; i < shift->numCols; i++) {
+                xo += i*shift->data.F32[j][i];
+                yo += j*shift->data.F32[j][i];
+                no += shift->data.F32[j][i];
+            }
+        }
+        xo /= no;
+        yo /= no;
+
+//        diag ("these require a tolerance of 100 epsilon");
+        is_float_tol (xo, 17.2, 100*FLT_EPSILON, "x-centroid should be %f : %f", 17.2, xo);
+        is_float_tol (yo, 14.8, 100*FLT_EPSILON, "y-centroid should be %f : %f", 14.8, yo);
+
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // tests using BICUBE interpolation
+    {
+        psMemId id = psMemGetId();
+
+        // generate simple image (x ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // use delta function as input test image
+        image->data.F32[11][11] = 1;
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShift (NULL, image, 0.2, 0.4, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float_tol (shift->data.F32[10][10], -0.157778, 4*FLT_EPSILON, "point 10,10 should be %f : %f", -0.157778, shift->data.F32[10][10]);
+        is_float_tol (shift->data.F32[10][11], +0.168889, 4*FLT_EPSILON, "point 10,11 should be %f : %f", +0.168889, shift->data.F32[10][11]);
+        is_float_tol (shift->data.F32[10][12], -0.131111, 4*FLT_EPSILON, "point 10,12 should be %f : %f", -0.131111, shift->data.F32[10][12]);
+        is_float_tol (shift->data.F32[11][10], +0.142221, 4*FLT_EPSILON, "point 11,10 should be %f : %f", +0.142221, shift->data.F32[11][10]);
+        is_float_tol (shift->data.F32[11][11], +0.488884, 4*FLT_EPSILON, "point 11,11 should be %f : %f", +0.488884, shift->data.F32[11][11]);
+        is_float_tol (shift->data.F32[11][12], +0.208878, 4*FLT_EPSILON, "point 11,12 should be %f : %f", +0.208878, shift->data.F32[11][12]);
+        is_float_tol (shift->data.F32[12][10], -0.064500, 4*FLT_EPSILON, "point 12,10 should be %f : %f", -0.064500, shift->data.F32[12][10]);
+        is_float_tol (shift->data.F32[12][11], +0.302066, 4*FLT_EPSILON, "point 12,11 should be %f : %f", +0.302066, shift->data.F32[12][11]);
+        is_float_tol (shift->data.F32[12][12], +0.041884, 4*FLT_EPSILON, "point 12,12 should be %f : %f", +0.041884, shift->data.F32[12][12]);
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageShiftKernel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageShiftKernel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageShiftKernel.c	(revision 22322)
@@ -0,0 +1,353 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int main (void)
+{
+    plan_tests(34);
+
+//    diag("psImageShiftKernel() tests");
+
+    // tests using BILINEAR interpolation
+    // skip until we implement the integer shift portion
+    if (0) {
+        psMemId id = psMemGetId();
+
+//        diag("shift a delta function by an integer offset");
+
+        // generate simple image (delta function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // use delta function as input test image
+        image->data.F32[10][10] = 1;
+
+        // integer shifts should be exact
+        psImage *shift = psImageShiftKernel (NULL, image, 1.0, 1.0, PS_INTERPOLATE_BILINEAR);
+        is_float (shift->data.F32[10][10], 0.0, "point 10,10 should be 0.0 : %f", shift->data.F32[10][10]);
+        is_float (shift->data.F32[10][11], 0.0, "point 10,11 should be 0.0 : %f", shift->data.F32[10][11]);
+        is_float (shift->data.F32[10][12], 0.0, "point 10,12 should be 0.0 : %f", shift->data.F32[10][12]);
+        is_float (shift->data.F32[11][10], 0.0, "point 11,10 should be 0.0 : %f", shift->data.F32[11][10]);
+        is_float (shift->data.F32[11][11], 1.0, "point 11,11 should be 1.0 : %f", shift->data.F32[11][11]);
+        is_float (shift->data.F32[11][12], 0.0, "point 11,12 should be 0.0 : %f", shift->data.F32[11][12]);
+        is_float (shift->data.F32[12][10], 0.0, "point 12,10 should be 0.0 : %f", shift->data.F32[12][10]);
+        is_float (shift->data.F32[12][11], 0.0, "point 12,11 should be 0.0 : %f", shift->data.F32[12][11]);
+        is_float (shift->data.F32[12][12], 0.0, "point 12,12 should be 0.0 : %f", shift->data.F32[12][12]);
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // tests using BILINEAR interpolation
+    // XXX skip until we implement BILINEAR
+    if (0) {
+        psMemId id = psMemGetId();
+
+//        diag("shift a delta function by an fractional pixel offset");
+
+        // generate simple image (delta function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // XXX is this needed?
+        psImageInit (image, 0.0);
+
+        // use delta function as input test image
+        image->data.F32[10][10] = 1;
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShiftKernel (NULL, image, 0.2, 0.4, PS_INTERPOLATE_BICUBE);
+//        diag("these require tolerance of 4 epsilon (why?)");
+        is_float_tol (shift->data.F32[10][10], 0.8*0.6, 4*FLT_EPSILON, "point 10,10 should be %f : %f", 0.8*0.6, shift->data.F32[10][10]);
+        is_float_tol (shift->data.F32[10][11], 0.2*0.6, 4*FLT_EPSILON, "point 10,11 should be %f : %f", 0.2*0.6, shift->data.F32[10][11]);
+        is_float_tol (shift->data.F32[10][12], 0.0,     4*FLT_EPSILON, "point 10,12 should be %f : %f", 0.0,     shift->data.F32[10][12]);
+        is_float_tol (shift->data.F32[11][10], 0.8*0.4, 4*FLT_EPSILON, "point 11,10 should be %f : %f", 0.8*0.4, shift->data.F32[11][10]);
+        is_float_tol (shift->data.F32[11][11], 0.2*0.4, 4*FLT_EPSILON, "point 11,11 should be %f : %f", 0.2*0.4, shift->data.F32[11][11]);
+        is_float_tol (shift->data.F32[11][12], 0.0,     4*FLT_EPSILON, "point 11,12 should be %f : %f", 0.0,     shift->data.F32[11][12]);
+        is_float_tol (shift->data.F32[12][10], 0.0,     4*FLT_EPSILON, "point 12,10 should be %f : %f", 0.0,     shift->data.F32[12][10]);
+        is_float_tol (shift->data.F32[12][11], 0.0,     4*FLT_EPSILON, "point 12,11 should be %f : %f", 0.0,     shift->data.F32[12][11]);
+        is_float_tol (shift->data.F32[12][12], 0.0,     4*FLT_EPSILON, "point 12,12 should be %f : %f", 0.0,     shift->data.F32[12][12]);
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test by measuring gaussian centroid before and after
+    // using BILINEAR interpolation
+    // XXX skip until we implement BILINEAR
+    if (0) {
+        psMemId id = psMemGetId();
+
+//        diag("shift a gaussian and measure centroid");
+
+        // generate simple image (gauss function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // build a simple gaussian (16,16),(2,1) as input test image
+        for (int j = 0; j < image->numRows; j++) {
+            for (int i = 0; i < image->numCols; i++) {
+                float z = PS_SQR(i - 16.0)/8.0 + PS_SQR(j - 16.0)/2.0;
+                image->data.F32[j][i] = exp(-z);
+            }
+        }
+
+        // measure centroid of unshifted gaussian (should be 16.0,16.0)
+        float xo = 0.0;
+        float yo = 0.0;
+        float no = 0.0;
+        for (int j = 0; j < image->numRows; j++) {
+            for (int i = 0; i < image->numCols; i++) {
+                xo += i*image->data.F32[j][i];
+                yo += j*image->data.F32[j][i];
+                no += image->data.F32[j][i];
+            }
+        }
+        xo /= no;
+        yo /= no;
+
+//        diag ("these require a tolerance of 100 epsilon");
+        is_float_tol (xo, 16.0, 100*FLT_EPSILON, "x-centroid should be %f : %f", 16.0, xo);
+        is_float_tol (xo, 16.0, 100*FLT_EPSILON, "y-centroid should be %f : %f", 16.0, yo);
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShiftKernel (NULL, image, 1.2, -1.2, PS_INTERPOLATE_BILINEAR);
+
+        // measure centroid of shifted gaussian (should be 17.2,14.8)
+        xo = yo = no = 0.0;
+        for (int j = 0; j < shift->numRows; j++) {
+            for (int i = 0; i < shift->numCols; i++) {
+                xo += i*shift->data.F32[j][i];
+                yo += j*shift->data.F32[j][i];
+                no += shift->data.F32[j][i];
+            }
+        }
+        xo /= no;
+        yo /= no;
+
+//        diag ("these require a tolerance of 100 epsilon");
+        is_float_tol (xo, 17.2, 100*FLT_EPSILON, "x-centroid should be %f : %f", 17.2, xo);
+        is_float_tol (yo, 14.8, 100*FLT_EPSILON, "y-centroid should be %f : %f", 14.8, yo);
+
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // tests using BICUBE interpolation
+    {
+        psMemId id = psMemGetId();
+
+        // generate simple image (x ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // use delta function as input test image
+        image->data.F32[11][11] = 1;
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShiftKernel (NULL, image, 0.2, 0.4, PS_INTERPOLATE_BICUBE);
+//        diag ("please check these values: this kernel is very steep and ringy.  what is the right BICUBE kernel?");
+        is_float_tol (shift->data.F32[10][10], -1.444445, 4*FLT_EPSILON, "point 10,10 should be %f : %f", -1.444445, shift->data.F32[10][10]);
+        is_float_tol (shift->data.F32[10][11], -0.277778, 4*FLT_EPSILON, "point 10,11 should be %f : %f", -0.277778, shift->data.F32[10][11]);
+        is_float_tol (shift->data.F32[10][12], -0.777778, 4*FLT_EPSILON, "point 10,12 should be %f : %f", -0.777778, shift->data.F32[10][12]);
+        is_float_tol (shift->data.F32[11][10], +0.055556, 4*FLT_EPSILON, "point 11,10 should be %f : %f", +0.055556, shift->data.F32[11][10]);
+        is_float_tol (shift->data.F32[11][11], +1.222222, 4*FLT_EPSILON, "point 11,11 should be %f : %f", +1.222222, shift->data.F32[11][11]);
+        is_float_tol (shift->data.F32[11][12], +0.722222, 4*FLT_EPSILON, "point 11,12 should be %f : %f", +0.722222, shift->data.F32[11][12]);
+        is_float_tol (shift->data.F32[12][10], -0.111111, 4*FLT_EPSILON, "point 12,10 should be %f : %f", -0.111111, shift->data.F32[12][10]);
+        is_float_tol (shift->data.F32[12][11], +1.055556, 4*FLT_EPSILON, "point 12,11 should be %f : %f", +1.055556, shift->data.F32[12][11]);
+        is_float_tol (shift->data.F32[12][12], +0.555556, 4*FLT_EPSILON, "point 12,12 should be %f : %f", +0.555556, shift->data.F32[12][12]);
+
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test by measuring gaussian centroid before and after
+    // using BICUBE interpolation
+    {
+        psMemId id = psMemGetId();
+
+//        diag("shift a gaussian and measure centroid");
+
+        // generate simple image (gauss function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // build a simple gaussian (16,16),(2,1) as input test image
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                float z = PS_SQR(i - 16.0)/8.0 + PS_SQR(j - 16.0)/2.0;
+                image->data.F32[j][i] = exp(-z);
+            }
+        }
+
+        // measure centroid of unshifted gaussian (should be 16.0,16.0)
+        float xo = 0.0;
+        float yo = 0.0;
+        float no = 0.0;
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                xo += i*image->data.F32[j][i];
+                yo += j*image->data.F32[j][i];
+                no += image->data.F32[j][i];
+            }
+        }
+        xo /= no;
+        yo /= no;
+
+//        diag ("these require a tolerance of 100 epsilon");
+        is_float_tol (xo, 16.0, 100*FLT_EPSILON, "x-centroid should be %f : %f", 16.0, xo);
+        is_float_tol (xo, 16.0, 100*FLT_EPSILON, "y-centroid should be %f : %f", 16.0, yo);
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShiftKernel (NULL, image, 0.2, -0.2, PS_INTERPOLATE_BICUBE);
+
+        // measure centroid of shifted gaussian (should be 17.2,14.8)
+        xo = yo = no = 0.0;
+        for (int j = 0; j < shift->numRows; j++)
+        {
+            for (int i = 0; i < shift->numCols; i++) {
+                xo += i*shift->data.F32[j][i];
+                yo += j*shift->data.F32[j][i];
+                no += shift->data.F32[j][i];
+            }
+        }
+        xo /= no;
+        yo /= no;
+
+//        diag ("these require a tolerance of 100 epsilon");
+        is_float_tol (xo, 16.2, 100*FLT_EPSILON, "x-centroid should be %f : %f", 16.2, xo);
+        is_float_tol (yo, 15.8, 100*FLT_EPSILON, "y-centroid should be %f : %f", 15.8, yo);
+
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // tests using GAUSS interpolation
+    {
+        psMemId id = psMemGetId();
+
+        // generate simple image (x ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // use delta function as input test image
+        image->data.F32[11][11] = 1;
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShiftKernel (NULL, image, 0.0, 0.0, PS_INTERPOLATE_GAUSS);
+        is_float_tol (shift->data.F32[10][10], -0.157778, 4*FLT_EPSILON, "point 10,10 should be %f : %f", -0.157778, shift->data.F32[10][10]);
+        is_float_tol (shift->data.F32[10][11], +0.168889, 4*FLT_EPSILON, "point 10,11 should be %f : %f", +0.168889, shift->data.F32[10][11]);
+        is_float_tol (shift->data.F32[10][12], -0.131111, 4*FLT_EPSILON, "point 10,12 should be %f : %f", -0.131111, shift->data.F32[10][12]);
+        is_float_tol (shift->data.F32[11][10], +0.142221, 4*FLT_EPSILON, "point 11,10 should be %f : %f", +0.142221, shift->data.F32[11][10]);
+        is_float_tol (shift->data.F32[11][11], +0.488884, 4*FLT_EPSILON, "point 11,11 should be %f : %f", +0.488884, shift->data.F32[11][11]);
+        is_float_tol (shift->data.F32[11][12], +0.208878, 4*FLT_EPSILON, "point 11,12 should be %f : %f", +0.208878, shift->data.F32[11][12]);
+        is_float_tol (shift->data.F32[12][10], -0.064500, 4*FLT_EPSILON, "point 12,10 should be %f : %f", -0.064500, shift->data.F32[12][10]);
+        is_float_tol (shift->data.F32[12][11], +0.302066, 4*FLT_EPSILON, "point 12,11 should be %f : %f", +0.302066, shift->data.F32[12][11]);
+        is_float_tol (shift->data.F32[12][12], +0.041884, 4*FLT_EPSILON, "point 12,12 should be %f : %f", +0.041884, shift->data.F32[12][12]);
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test by measuring gaussian centroid before and after
+    // using GAUSS interpolation
+    {
+        psMemId id = psMemGetId();
+
+//        diag("shift a gaussian and measure centroid");
+
+        // generate simple image (gauss function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // build a simple gaussian (16,16),(2,1) as input test image
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                float z = PS_SQR(i - 16.0)/8.0 + PS_SQR(j - 16.0)/2.0;
+                image->data.F32[j][i] = exp(-z);
+            }
+        }
+
+        // measure centroid of unshifted gaussian (should be 16.0,16.0)
+        float xo = 0.0;
+        float yo = 0.0;
+        float no = 0.0;
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                xo += i*image->data.F32[j][i];
+                yo += j*image->data.F32[j][i];
+                no += image->data.F32[j][i];
+            }
+        }
+        xo /= no;
+        yo /= no;
+
+//        diag ("these require a tolerance of 100 epsilon");
+        is_float_tol (xo, 16.0, 100*FLT_EPSILON, "x-centroid should be %f : %f", 16.0, xo);
+        is_float_tol (xo, 16.0, 100*FLT_EPSILON, "y-centroid should be %f : %f", 16.0, yo);
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShiftKernel (NULL, image, 0.2, 0.4, PS_INTERPOLATE_GAUSS);
+
+        // measure centroid of shifted gaussian (should be 17.2,14.8)
+        xo = yo = no = 0.0;
+        for (int j = 0; j < shift->numRows; j++)
+        {
+            for (int i = 0; i < shift->numCols; i++) {
+                xo += i*shift->data.F32[j][i];
+                yo += j*shift->data.F32[j][i];
+                no += shift->data.F32[j][i];
+            }
+        }
+        xo /= no;
+        yo /= no;
+
+//        diag ("these require a tolerance of 100 epsilon");
+        is_float_tol (xo, 16.2, 100*FLT_EPSILON, "x-centroid should be %f : %f", 16.2, xo);
+        is_float_tol (yo, 15.8, 100*FLT_EPSILON, "y-centroid should be %f : %f", 15.8, yo);
+
+        psFits *fits = psFitsOpen ("gauss.fits", "w");
+        psFitsWriteImage (fits, NULL, image, 0, NULL);
+        psFitsClose (fits);
+
+        fits = psFitsOpen ("shift.fits", "w");
+        psFitsWriteImage (fits, NULL, shift, 0, NULL);
+        psFitsClose (fits);
+
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageSmooth.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageSmooth.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageSmooth.c	(revision 22322)
@@ -0,0 +1,165 @@
+/** @file  tst_psImageConvolve.c
+ *
+ *  This code will test the psImageSmooth() routine.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-02-27 23:56:12 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_ROWS 20
+#define NUM_COLS 20
+#define SIGMA 1.0
+#define NSIGMA 5.0
+#define TS00_IM_NULL            0x00000001
+#define TS00_IM_F32             0x00000002
+#define TS00_IM_F64             0x00000004
+#define TS00_IM_S32             0x00000008
+#define VERBOSE 0
+
+static psBool testImageSmoothGeneric(
+    psU32 flags,
+    psS32 numCols,
+    psS32 numRows,
+    psF64 sigma,
+    psF64 Nsigma,
+    psBool expectedRC)
+{
+    psMemId id = psMemGetId();
+    psBool testStatus = true;
+    psImage *img = NULL;
+
+    if (VERBOSE) {
+        if (expectedRC == false) {
+            printf("This test should generate FALSE.\n");
+        } else {
+            printf("This test should generate TRUE.\n");
+        }
+    }
+
+    if (flags & TS00_IM_NULL) {
+        if (VERBOSE) printf("        using a NULL image\n");
+    }
+
+    if (flags & TS00_IM_F32) {
+        if (VERBOSE) printf("        using a psF32 image\n");
+        img = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        // Set a checkboard pattern
+        for (psS32 i = 0 ; i < numRows ; i++) {
+            for (psS32 j = 0 ; j < numCols ; j++) {
+                if ((i%2) != (j%2)) {
+                    img->data.F32[i][j] = 1.0;
+                } else {
+                    img->data.F32[i][j] = 0.0;
+                }
+            }
+        }
+    }
+    if (VERBOSE) {
+        p_psImagePrint(1, img, "The Smoothed Image");
+    }
+
+    if (flags & TS00_IM_F64) {
+        if (VERBOSE)
+            printf("        using a psF64 image\n");
+        img = psImageAlloc(numCols, numRows, PS_TYPE_F64);
+        // Set a checkboard pattern
+        for (psS32 i = 0 ; i < numRows ; i++) {
+            for (psS32 j = 0 ; j < numCols ; j++) {
+                if ((i%2) != (j%2)) {
+                    img->data.F64[i][j] = 1.0;
+                } else {
+                    img->data.F64[i][j] = 0.0;
+                }
+            }
+        }
+    }
+
+    if (flags & TS00_IM_S32) {
+        if (VERBOSE)
+            printf("        using a psS32 image\n");
+        img = psImageAlloc(numCols, numRows, PS_TYPE_S32);
+        // Set a checkboard pattern
+        for (psS32 i = 0 ; i < numRows ; i++) {
+            for (psS32 j = 0 ; j < numCols ; j++) {
+                if ((i%2) != (j%2)) {
+                    img->data.S32[i][j] = 1;
+                } else {
+                    img->data.S32[i][j] = 0;
+                }
+            }
+        }
+    }
+    if (VERBOSE) {
+        printf(" %d columns\n", numCols);
+        printf(" %d rows\n", numRows);
+        printf(" sigma is %.2f\n", sigma);
+        printf(" Nsigma is %.2f\n", Nsigma);
+    }
+
+    psBool rc = psImageSmooth(img, sigma, Nsigma);
+    if (rc == false) {
+        if (expectedRC == true) {
+            diag("TEST ERROR: psImageSmooth returned FALSE\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            diag("TEST ERROR: psImageSmooth returned TRUE\n");
+            testStatus = false;
+        }
+        if (VERBOSE) {
+            p_psImagePrint(1, img, "The Smoothed Image");
+        }
+
+        if (flags & TS00_IM_F32) {
+            for (psS32 i = 1 ; i < numRows-1 ; i++) {
+                for (psS32 j = 1 ; j < numCols-1 ; j++) {
+                    if ((fabs(img->data.F32[i][j] - 0.5) > 0.1)) {
+                        diag("TEST ERROR: img[%d][%d] was %f, expected 0.5\n",
+                             i, j, img->data.F32[i][j]);
+                        testStatus = false;
+                    }
+                }
+            }
+        }
+
+        if (flags & TS00_IM_F64) {
+            for (psS32 i = 1 ; i < numRows-1 ; i++) {
+                for (psS32 j = 1 ; j < numCols-1 ; j++) {
+                    if ((fabs(img->data.F64[i][j] - 0.5) > 0.1)) {
+                        diag("TEST ERROR: img[%d][%d] was %f, expected 0.5\n",
+                             i, j, img->data.F64[i][j]);
+                        testStatus = false;
+                    }
+                }
+            }
+        }
+    }
+    psFree(img);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    return(testStatus);
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(14);
+
+    ok(testImageSmoothGeneric(TS00_IM_F32, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    ok(testImageSmoothGeneric(TS00_IM_F32, 1, NUM_ROWS, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    ok(testImageSmoothGeneric(TS00_IM_F32, NUM_COLS, 1, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    ok(testImageSmoothGeneric(TS00_IM_F32, 1, 1, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    ok(testImageSmoothGeneric(TS00_IM_F64, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    ok(testImageSmoothGeneric(TS00_IM_S32, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, false), "testImageSmoothGeneric() successful");
+    ok(testImageSmoothGeneric(TS00_IM_NULL, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, false), "testImageSmoothGeneric() successful");
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageStats.c	(revision 22322)
@@ -0,0 +1,615 @@
+/*
+*  C Implementation: %{MODULE}
+*
+* Description:
+*
+* XXX: Must do sub Region tests
+* XXX: Must do (1, N) and (N, 1) size tests
+*
+* Copyright: See COPYING file that comes with this distribution
+*
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+bool testInRegion(psRegion region, int row, int col)
+{
+    return(row >= region.y0 && row < region.y1 && col >= region.x0 && col < region.x1);
+}
+
+void genericImageCountPixelMaskTest(int numRows, int numCols)
+{
+    psMemId id = psMemGetId();
+
+    psRegion reg;
+    reg.x0 = 0;
+    reg.x1 = 0;
+    reg.y0 = 0;
+    reg.y1 = 0;
+    psImage *img = psImageAlloc(numRows, numCols, PS_TYPE_MASK);
+    int expNumPix = 0;
+    for (int i = 0 ; i < numRows ; i++) {
+        for (int j = 0 ; j < numCols ; j++) {
+            if (0 == i%2) {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 1;
+                expNumPix++;
+            } else {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+    }
+    psS32 numPix = psImageCountPixelMask(img, reg, 1);
+    ok(numPix == expNumPix, "psImageCountPixelMask() returned the correct number of pixels (no region)");
+
+    reg.x0 = numCols/4;
+    reg.x1 = 3 * numCols/4;
+    reg.y0 = numRows/4;
+    reg.y1 = 3 * numRows/4;
+    expNumPix = 0;
+    img->col0 = 0;
+    img->row0 = 0;
+    for (int i = 0 ; i < numRows ; i++) {
+        for (int j = 0 ; j < numCols ; j++) {
+            if (testInRegion(reg, i, j)) {
+                if (0 == i%2) {
+                    img->data.PS_TYPE_MASK_DATA[i][j] = 1;
+                    expNumPix++;
+                } else {
+                    img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+                }
+            } else {
+                img->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+    }
+    numPix = psImageCountPixelMask(img, reg, 1);
+    ok(numPix == expNumPix, "psImageCountPixelMask() returned the correct number of pixels (with region)");
+    psFree(img);
+
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+}
+
+
+void genericImageStatsTest(int numRows, int numCols)
+{
+    psMemId id = psMemGetId();
+    psImage *img = psImageAlloc(numRows, numCols, PS_TYPE_F32);
+    psImage *msk = psImageAlloc(numRows, numCols, PS_TYPE_MASK);
+    psImage *imgSub = psImageSubset(img, psRegionSet(numRows/4, 3*numRows/4,
+                                    numCols/4, 3*numCols/4));
+    psImage *mskSub = psImageSubset(img, psRegionSet(numRows/4, 3*numRows/4,
+                                    numCols/4, 3*numCols/4));
+    float meanNoMask = 0.0;
+    float numPixelsNoMask = 0.0;
+    float meanMask = 0.0;
+    float numPixelsMask = 0.0;
+    for (int i = 0; i < numRows; i++) {
+        for (int j = 0; j < numRows; j++) {
+            if (0 == i%2) {
+                img->data.F32[i][j] = (float) (i + j);
+                msk->data.PS_TYPE_MASK_DATA[i][j] = 1;
+                meanNoMask+= img->data.F32[i][j];
+                numPixelsNoMask+= 1.0;
+            } else {
+                img->data.F32[i][j] = (float) (i + j + 100);
+                msk->data.PS_TYPE_MASK_DATA[i][j] = 0;
+                meanNoMask+= img->data.F32[i][j];
+                meanMask+= img->data.F32[i][j];
+                numPixelsMask+= 1.0;
+                numPixelsNoMask+= 1.0;
+            }
+        }
+    }
+    meanNoMask/= numPixelsNoMask;
+    meanMask/= numPixelsMask;
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psStats* tmpStats = myStats;
+
+    // Calculate Sample Mean with nomask
+    myStats = psImageStats(myStats, img, NULL, 0);
+    ok(myStats != NULL, "psImageStats() returned non-NULL");
+    ok(myStats == tmpStats, "psImageStats() returned the original psStats struct");
+    ok(fabs(myStats->sampleMean - meanNoMask) < .001, "psImageStats() calculated the mean correctly (no mask)");
+
+    // Calculate Sample Mean with mask
+    myStats = psImageStats(myStats, img, msk, 1);
+    ok(myStats != NULL, "psImageStats() returned non-NULL");
+    ok(fabs(myStats->sampleMean - meanMask) < .001, "psImageStats() calculated the mean correctly (with mask)");
+
+
+    // Repeat previous tests using the sub image
+    meanNoMask = 0.0;
+    numPixelsNoMask = 0.0;
+    meanMask = 0.0;
+    numPixelsMask = 0.0;
+    for (int i = 0; i < imgSub->numRows; i++) {
+        for (int j = 0; j < imgSub->numRows; j++) {
+            if (0 == i%2) {
+                imgSub->data.F32[i][j] = (float) (i + j);
+                mskSub->data.PS_TYPE_MASK_DATA[i][j] = 1;
+                meanNoMask+= imgSub->data.F32[i][j];
+                numPixelsNoMask+= 1.0;
+            } else {
+                imgSub->data.F32[i][j] = (float) (i + j + 100);
+                mskSub->data.PS_TYPE_MASK_DATA[i][j] = 0;
+                meanNoMask+= imgSub->data.F32[i][j];
+                meanMask+= imgSub->data.F32[i][j];
+                numPixelsMask+= 1.0;
+                numPixelsNoMask+= 1.0;
+            }
+        }
+    }
+    meanNoMask/= numPixelsNoMask;
+    meanMask/= numPixelsMask;
+    tmpStats = myStats;
+
+    // Calculate Sample Mean with nomask (sub image)
+    // XXXX: We skip these tests because they seg-fault.  Not sure if the source
+    // is wrong or the tests are.
+    ok(false, "XXXX: skipping psImageStats() tests on a subRegion because of seg faults");
+    if (0) {
+        myStats = psImageStats(myStats, imgSub, NULL, 0);
+        ok(myStats != NULL, "psImageStats() returned non-NULL");
+        ok(myStats == tmpStats, "psImageStats() returned the original psStats struct");
+        ok(fabs(myStats->sampleMean - meanNoMask) < .001, "psImageStats() calculated the mean correctly (no mask) (subImage)");
+        printf("Expected %f, got %f\n", meanNoMask, myStats->sampleMean);
+
+        // Calculate Sample Mean with mask (sub image)
+        myStats = psImageStats(myStats, imgSub, mskSub, 1);
+        ok(myStats != NULL, "psImageStats() returned non-NULL");
+        ok(fabs(myStats->sampleMean - meanMask) < .001, "psImageStats() calculated the mean correctly (with mask) (subImage)");
+    }
+
+    psFree(myStats);
+    psFree(img);
+    psFree(msk);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+}
+
+
+#define I_POLY(X, Y) ((A) + (B * X) + (C * Y) + (D * X * Y) + (E * X * X) + (F * Y * Y))
+
+void genericFitChebyF32Test(int numCols, int numRows)
+{
+    psMemId id = psMemGetId();
+    const double A = 1.0;
+    const double B = 2.0;
+    const double C = 3.0;
+    const double D = 4.0;
+    const double E = 5.0;
+    const double F = 6.0;
+    const double ERROR_TOL = 0.1;
+    psImage *tmpImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *outImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    for (int i=0;i<numRows;i++) {
+        for (int j=0;j<numCols;j++) {
+            tmpImage->data.F32[i][j] = I_POLY(((float) i), ((float) j));
+        }
+    }
+
+    psPolynomial2D *chebys = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, 5, 5);
+    skip_start(chebys == NULL, 5, "Skipping tests because psPolynomial2DAlloc() returned NULL");
+    psPolynomial2D *chebys2 = psImageFitPolynomial(chebys, tmpImage);
+    ok(chebys2 != NULL, "psImageFitPolynomial() returned non-NULL");
+    ok(chebys2 == chebys, "psImageFitPolynomial() correctly recylced polynomial input");
+    psImage *outImage2 = psImageEvalPolynomial(outImage, chebys);
+    ok(outImage2 != NULL, "psImageEvalPolynomial() returned non-NULL");
+    ok(outImage2 == outImage, "psImageEvalPolynomial() correctly recylced input image");
+
+    bool errorFlag = false;
+    for (int i=1 ; i < numRows ; i++) {
+        for (int j=0 ; j < numCols ; j++) {
+            float expect = tmpImage->data.F32[i][j];
+            float actual = outImage->data.F32[i][j];
+            if (fabs((actual - expect) / expect) > ERROR_TOL) {
+                diag("ERROR: pixel [%d][%d] is %.2f should be %.2f\n", i, j, actual, expect);
+                errorFlag = true;
+            }
+        }
+    }
+    ok(!errorFlag, "psImageFitPolynomial() and psImageEvalPolynomial() produced the correct results (F32)");
+    skip_end();
+    psFree(tmpImage);
+    psFree(outImage);
+    psFree(chebys);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+}
+
+void genericFitChebyF64Test(int numCols, int numRows)
+{
+    psMemId id = psMemGetId();
+    const double A = 1.0;
+    const double B = 2.0;
+    const double C = 3.0;
+    const double D = 4.0;
+    const double E = 5.0;
+    const double F = 6.0;
+    const double ERROR_TOL = 0.1;
+    psImage *tmpImage = psImageAlloc(numCols, numRows, PS_TYPE_F64);
+    psImage *outImage = psImageAlloc(numCols, numRows, PS_TYPE_F64);
+    for (int i=0;i<numRows;i++) {
+        for (int j=0;j<numCols;j++) {
+            tmpImage->data.F64[i][j] = I_POLY(((float) i), ((float) j));
+        }
+    }
+
+    psPolynomial2D *chebys = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, 5, 5);
+    skip_start(chebys == NULL, 5, "Skipping tests because psPolynomial2DAlloc() returned NULL");
+    psPolynomial2D *chebys2 = psImageFitPolynomial(chebys, tmpImage);
+    ok(chebys2 != NULL, "psImageFitPolynomial() returned non-NULL");
+    ok(chebys2 == chebys, "psImageFitPolynomial() correctly recylced polynomial input");
+    psImage *outImage2 = psImageEvalPolynomial(outImage, chebys);
+    ok(outImage2 != NULL, "psImageEvalPolynomial() returned non-NULL");
+    ok(outImage2 == outImage, "psImageEvalPolynomial() correctly recylced input image");
+
+    bool errorFlag = false;
+    for (int i=1 ; i < numRows ; i++) {
+        for (int j=0 ; j < numCols ; j++) {
+            psF64 expect = tmpImage->data.F64[i][j];
+            psF64 actual = outImage->data.F64[i][j];
+            if (fabs((actual - expect) / expect) > ERROR_TOL) {
+                diag("ERROR: pixel [%d][%d] is %.2f should be %.2f\n", i, j, actual, expect);
+                errorFlag = true;
+            }
+        }
+    }
+    ok(!errorFlag, "psImageFitPolynomial() and psImageEvalPolynomial() produced the correct results (F64)");
+    skip_end();
+    psFree(tmpImage);
+    psFree(outImage);
+    psFree(chebys);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+}
+
+
+void testPsImageFitPolynomial()
+{
+    psMemId id = psMemGetId();
+    const int IMAGE_SIZE = 16;
+    const int CHEBY_X_DIM = 8;
+    const int CHEBY_Y_DIM = 8;
+    const int THRESHOLD = 1;
+    psImage *imgIn = psImageAlloc(IMAGE_SIZE, IMAGE_SIZE, PS_TYPE_F32);
+    psImage *imgOut = psImageAlloc(IMAGE_SIZE, IMAGE_SIZE, PS_TYPE_F32);
+    for (int i = 0;i < IMAGE_SIZE;i++) {
+        for (int j = 0;j < IMAGE_SIZE;j++) {
+            imgIn->data.F32[ i ][ j ] = 4.0;
+            imgIn->data.F32[ i ][ j ] = (float) (i + j);
+            imgIn->data.F32[ i ][ j ] = (float) (i + j) + (4.0 * (float) i);
+            imgOut->data.F32[ i ][ j ] = 0.0;
+        }
+    }
+    psPolynomial2D *my2DPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, CHEBY_X_DIM-1, CHEBY_Y_DIM-1);
+    my2DPoly = psImageFitPolynomial(my2DPoly, imgIn);
+    ok(my2DPoly != NULL, "psImageFitPolynomial() returned non-NULL");
+
+    imgOut = psImageEvalPolynomial(imgOut, my2DPoly);
+    bool errorFlag = false;
+    for (int i = 0;i < IMAGE_SIZE;i++) {
+        for (int j = 0;j < IMAGE_SIZE;j++) {
+            if (fabs(imgOut->data.F32[ i ][ j ] - imgIn->data.F32[ i ][ j ]) > THRESHOLD) {
+                diag("Pixel (%d, %d) is %.2f, should be %.2f\n", i, j,
+                     imgOut->data.F32[ i ][ j ],
+                     imgIn->data.F32[ i ][ j ]);
+                errorFlag = true;
+            }
+
+        }
+    }
+    ok(!errorFlag, "psImageFitPolynomial() and psImageEvalPolynomial() produced the correct result");
+    psFree(imgIn);
+    psFree(imgOut);
+    psFree(my2DPoly);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+}
+
+void genericpsImageHistogramTest(int numRows, int numCols, int numBins)
+{
+    if ((0 != numRows%4) ||
+        (0 != numRows%numBins) ||
+        (0 != numBins%2)) {
+        ok(false, "numRows must be a multiple of 4, and must be a integer multiple of numBins");
+    }
+
+    psImage *img = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *msk = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+    // XXX: Do subRegion tests later
+//    psImage *imgSub = psImageSubset(img, psRegionSet(numRows/4, 3*numRows/4,
+//                                    numCols/4, 3*numCols/4));
+//    psImage *mskSub = psImageSubset(img, psRegionSet(numRows/4, 3*numRows/4,
+//                                    numCols/4, 3*numCols/4));
+    for (int i = 0; i < numRows; i++) {
+        for (int j = 0; j < numCols; j++) {
+            img->data.F32[i][j] = 0.4 + (float) (i);
+            if (i >= numRows/2) {
+                msk->data.PS_TYPE_MASK_DATA[i][j] = 1;
+            } else {
+                msk->data.PS_TYPE_MASK_DATA[i][j] = 0;
+            }
+        }
+    }
+
+    if (0) {
+        for (int i = 0; i < numRows; i++) {
+            for (int j = 0; j < numCols; j++) {
+                printf("(%.1f) ", img->data.F32[i][j]);
+            }
+            printf("\n");
+        }
+        for (int i = 0; i < numRows; i++) {
+            for (int j = 0; j < numCols; j++) {
+                printf("(%d) ", msk->data.PS_TYPE_MASK_DATA[i][j]);
+            }
+            printf("\n");
+        }
+    }
+    
+    // Calculate Histogram with no mask
+    {
+        psMemId id = psMemGetId();
+        psHistogram *hist = psHistogramAlloc(0.0, (float) (numRows), numBins);
+        psHistogram *hist2 = psImageHistogram(hist, img, NULL, 0);
+        ok(hist2 != NULL, "psImageHistogram() returned non-NULL");
+        skip_start(hist2 == NULL, 1, "Skipping tests because psImageHistogram() returned NULL");
+        ok(hist2 == hist, "psImageHistogram() correctly recycled the input psHistogram");
+        bool errorFlag = false;
+        for (int i = 0;i < numBins;i++) {
+            if (hist2->nums->data.F32[i] != (numCols * (numRows/numBins))) {
+                diag("ERROR: Bin number %d bounds: (%.1f - %.1f) data (%.2f)\n", i,
+                      hist2->bounds->data.F32[i],
+                      hist2->bounds->data.F32[i+1],
+                      hist2->nums->data.F32[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psImageHistogram() produced the correct results (no mask)");
+        skip_end();
+        psFree(hist);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Calculate Histogram with mask
+    {
+        psMemId id = psMemGetId();
+        psHistogram *hist = psHistogramAlloc(0.0, (float) (numRows), numBins);
+        psHistogram *hist2 = psImageHistogram(hist, img, msk, 1);
+        ok(hist2 != NULL, "psImageHistogram() returned non-NULL");
+        skip_start(hist2 == NULL, 1, "Skipping tests because psImageHistogram() returned NULL");
+        ok(hist2 == hist, "psImageHistogram() correctly recycled the input psHistogram");
+        bool errorFlag = false;
+        for (int i = 0 ; i < numBins ; i++) {
+            if (i < numBins/2) {
+                if (hist2->nums->data.F32[i] != (numCols * (numRows/numBins))) {
+                    diag("ERROR: Bin number %d bounds: (%.1f - %.1f) data (%.2f)\n", i,
+                          hist2->bounds->data.F32[i],
+                          hist2->bounds->data.F32[i+1],
+                          hist2->nums->data.F32[i]);
+                    errorFlag = true;
+                }
+            } else {
+                if (hist2->nums->data.F32[i] != 0) {
+                    diag("ERROR: Bin number %d bounds: (%.1f - %.1f) data (%.2f)\n", i,
+                          hist2->bounds->data.F32[i],
+                          hist2->bounds->data.F32[i+1],
+                          hist2->nums->data.F32[i]);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psImageHistogram() produced the correct results (no mask)");
+        skip_end();
+        psFree(hist);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psFree(img);
+    psFree(msk);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(58);
+
+    // --------------------------------------------------------------
+    // psImageCountPixelMask()
+    // Ensure psImageCountPixelMask returns -1 for NULL input image
+    // Following should generate error
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psRegion reg;
+        reg.x0 = 0;
+        reg.x1 = 1;
+        reg.y0 = 0;
+        reg.y1 = 4;
+        psS32 numPix = psImageCountPixelMask(NULL, reg, 1);
+        ok(numPix == -1, "psImageCountPixelMask() returned -1 for NULL input image");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Ensure psImageCountPixelMask returns -1 for wrong input image type
+    // Following should generate error
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psImage *in = psImageAlloc(16, 16, PS_TYPE_F32);
+        psRegion reg;
+        reg.x0 = 0;
+        reg.x1 = 1;
+        reg.y0 = 0;
+        reg.y1 = 4;
+        psS32 numPix = psImageCountPixelMask(in, reg, 1);
+        ok(numPix == -1, "psImageCountPixelMask() returned -1 for incorrect input image type");
+        psFree(in);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Ensure psImageCountPixelMask returns -1 for bad region
+    // Following should generate error
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psImage *in = psImageAlloc(16, 16, PS_TYPE_MASK);
+        psRegion reg;
+        reg.x0 = -1;
+        reg.x1 = 0;
+        reg.y0 = 0;
+        reg.y1 = 0;
+        psS32 numPix = psImageCountPixelMask(in, reg, 1);
+        ok(numPix == -1, "psImageCountPixelMask() returned -1 for bad region x0");
+        reg.x0 = 0;
+        reg.y0 = -1;
+        numPix = psImageCountPixelMask(in, reg, 1);
+        ok(numPix == -1, "psImageCountPixelMask() returned -1 for bad region y0");
+        // XXX: Should this fail?
+        if (0) {
+            reg.x0 = 1;
+            reg.x1 = 1;
+            reg.y0 = 1;
+            reg.y1 = 1;
+            numPix = psImageCountPixelMask(in, reg, 1);
+            ok(numPix == -1, "psImageCountPixelMask() returned -1 for bad region ");
+        }
+        psFree(in);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    genericImageCountPixelMaskTest(10, 10);
+
+
+
+    // --------------------------------------------------------------
+    // psImageStats()
+    // Ensure psImageStats() returns NULL for NULL input image
+    // Following should generate error
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psStats *myStats2 = psImageStats(myStats, NULL, NULL, 0);
+        ok(myStats2 == NULL, "psImageStats() returned NULL with NULL input image");
+        psFree(myStats);
+        psFree(myStats2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Ensure psImageStats() returns NULL for NULL psStats
+    // Following should generate error
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(10, 10, PS_TYPE_F32);
+        psStats *myStats = psImageStats(NULL, img, NULL, 0);
+        ok(myStats == NULL, "psImageStats() returned NULL with NULL psStats arg");
+        psFree(img);
+        psFree(myStats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    genericImageStatsTest(10, 10);
+
+
+
+    // --------------------------------------------------------------
+    // psImageFitPolynomial() and psImageEvalPolynomial()
+    // Following should be an error message for NULL coeffs argument
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(10, 10, PS_TYPE_F32);
+        psImage *img2 = psImageEvalPolynomial(img, NULL);
+        ok(img2 == NULL, "psImageEvalPolynomial() returned NULL with NULL coeffs argument");
+        psFree(img);
+        psFree(img2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Following should be a error message for NULL input argument
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psPolynomial2D *poly2D = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, 5, 5);
+        psImage *img = psImageEvalPolynomial(NULL, poly2D);
+        ok(img == NULL, "psImageEvalPolynomial() returned NULL with NULL input image");
+        psFree(img);
+        psFree(poly2D);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Following should be an error message for NULL coeffs argument
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(10, 10, PS_TYPE_F32);
+        psPolynomial2D *poly2D = psImageFitPolynomial(NULL, img);
+        ok(poly2D == NULL, "psImageFitPolynomial() returned a NULL with NULL coeffs arg");
+        psFree(img);
+        psFree(poly2D);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Following should be a error message for NULL input argument
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psPolynomial2D *poly2D = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, 5, 5);
+        psPolynomial2D *poly2D2 = psImageFitPolynomial(poly2D, NULL);
+        ok(poly2D2 == NULL, "psImageFitPolynomial() returned a NULL with NULL input image");
+        psFree(poly2D);
+        psFree(poly2D2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    genericFitChebyF32Test(10, 10);
+    genericFitChebyF64Test(10, 10);
+    // XXX: This next test is not necessary.  Previous two functions make it redundant.
+    testPsImageFitPolynomial();
+
+
+
+    // --------------------------------------------------------------
+    // psImageHistogram()
+    // Following should be a error message for NULL psHistogram argument
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(10, 10, PS_TYPE_F32);
+        psHistogram *hist = psImageHistogram(NULL, img, NULL, 0);
+        ok(hist == NULL, "psImageHistogram() returned NULL for NULL psHistogram arg");
+        psFree(img);
+        psFree(hist);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Following should be a error message for NULL psImage argument
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psHistogram *hist = psHistogramAlloc(0.0, 100000.0, 1000);
+        psHistogram *hist2 = psImageHistogram(hist, NULL, NULL, 0);
+        ok(hist2 == NULL, "psImageHistogram() returned NULL for NULL psHistogram arg");
+        psFree(hist);
+        psFree(hist2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Following should be a error message for incorrect mask type
+    // XXX: Verify error
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(10, 10, PS_TYPE_F32);
+        psHistogram *hist = psHistogramAlloc(0.0, 100000.0, 1000);
+        psImage *msk = psImageAlloc(10, 10, PS_TYPE_S8);
+        psHistogram *hist2 = psImageHistogram(hist, img, msk, 1);
+        ok(hist2 == NULL, "psImageHistogram() returned NULL for incorrect mask type");
+        psFree(hist);
+        psFree(hist2);
+        psFree(img);
+        psFree(msk);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    genericpsImageHistogramTest(16, 15, 4);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageStructManip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageStructManip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tap_psImageStructManip.c	(revision 22322)
@@ -0,0 +1,523 @@
+/** @file  tap_psImageStructManip.c
+*
+*  @brief Contains the tests for psImageExtraction.[ch]
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  psLib functions tested:
+*     psImageSubset()
+*     psImageCopy()
+*     psImageTrim()
+*
+*  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-09-20 23:56:10 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+void genericImageSubsetTest(int numRows, int numCols)
+{
+    // psImageSubset shall create child image of a specified size from a
+    // parent psImage structure
+    // psImageSubset()
+    {
+        psMemId id = psMemGetId();
+        psImage preSubsetStruct;
+        psRegion region1 = psRegionSet(0, numCols/2, 0, numRows/2);
+        psRegion region2 = psRegionSet(numCols/4, numCols/4+numCols/2, numRows/4, numRows/4+numRows/2);
+
+	// test basic subset creation 
+	{ 
+	    psImage* original = psImageAlloc(numCols,numRows,PS_TYPE_U32);
+	    for (psS32 row=0;row<numRows;row++) {
+		for (psS32 col=0;col<numCols;col++) {
+		    original->data.F32[row][col] = row*1000+col;
+		}
+	    }
+
+	    // XXX this is not being used in this section
+	    memcpy(&preSubsetStruct, original, sizeof(psImage));
+	    psImage* subset1 = psImageSubset(original, region2);
+	    ok(subset1, "psImageSubset() returned non-NULL (subset1)");
+	    skip_start(subset1 == NULL, 25, "Skipping tests because psImageSubset() returned NULL");
+	    psImage* subset2 = psImageSubset(original, region1);
+	    ok(subset2, "psImageSubset() returned non-NULL (subset2)");
+	    skip_start(subset2 == NULL, 24, "Skipping tests because psImageSubset() returned NULL");
+
+	    // Verify the returned psImage structure members nrow and ncol are equal to
+	    // the input parameter nrow and ncol respectively
+	    ok(subset1->numCols == numCols/2 && subset1->numRows == numRows/2,
+	       "psImageSubset output size set properly");
+	    ok(subset2->numCols == numCols/2 && subset2->numRows == numRows/2,
+	       "psImageSubset output size set properly");
+
+	    // Verify the returned psImage structure contains expected values in the
+	    // row member, if the input psImage structure image contains known values
+	    bool errorFlag = false;
+	    for (psS32 row=0;row<numRows/2;row++) {
+		for (psS32 col=0;col<numCols/2;col++) {
+		    if (subset1->data.U32[row][col] != original->data.U32[row+numRows/4][col+numCols/4]) {
+			diag("psImageSubset output #1 was wrong at %dx%d (%d vs %d).",
+			     row,col,subset1->data.U32[row][col], original->data.U32[row+numRows/4][col+numCols/4]);
+			errorFlag = true;
+		    }
+		    if (subset2->data.U32[row][col] != original->data.U32[row][col]) {
+			diag("psImageSubset output #1 was wrong at %dx%d (%d vs %d).",
+			     row,col,subset1->data.U32[row][col], original->data.U32[row][col]);
+			errorFlag = true;
+		    }
+		}
+	    }
+	    ok(!errorFlag, "psImageSubset() produced the expected values");
+
+	    // Verify the returned psImage structure member type is equal to the input
+	    // psImage structure member type
+	    ok(subset1->type.type == PS_TYPE_U32, "psImageSubset() type was correct (subset1)");
+	    ok(subset2->type.type == PS_TYPE_U32, "psImageSubset() type was correct (subset2)");
+
+	    // Verify the returned psImage structure members row0 and col0 are equal to
+	    // the input parameters row0 and col0 respectively
+	    ok(subset1->col0 == numCols/4 && subset1->row0 == numRows/4,
+	       "psImageSubset() set col0/row0 correctly (subset1)");
+	    ok(subset2->col0 == 0 && subset2->row0 == 0,
+	       "psImageSubset() set col0/row0 correctly (subset2)");
+
+	    // Verify the returned psImage structure member parent is equal to the
+	    // input psImage structure pointer image
+	    ok(subset1->parent == original && subset2->parent == original, "psImageSubset() set ->parent correctly");
+
+	    // Verify the returned psImage structure member children is null
+	    ok(subset1->children == NULL && subset2->children == NULL, "psImageSubset() set ->children correctly");
+
+	    // Verify the input psImage structure image only has the following members
+	    // changed: 1) Nchildren is increased by one. 2) parent contains pointer psImage structure
+	    // out at parent[Nchildren-1]
+	    ok(original->children != NULL && original->children->n == 2, "psImageSubset did increment number of children by one per subset.");
+	    ok(original->children->data[0] == subset1 && original->children->data[1] == subset2,
+	       "psImageSubset did properly store the children pointers.");
+
+	    psFree (subset1);
+	    psFree (subset2);
+	    skip_end();
+	    skip_end();
+	    psFree (original);
+	}
+
+	// test free in reverse order from above
+	{ 
+	    psImage* original = psImageAlloc(numCols,numRows,PS_TYPE_U32);
+	    for (psS32 row=0;row<numRows;row++) {
+		for (psS32 col=0;col<numCols;col++) {
+		    original->data.F32[row][col] = row*1000+col;
+		}
+	    }
+
+	    // XXX this is not being used in this section
+	    memcpy(&preSubsetStruct, original, sizeof(psImage));
+	    psImage* subset1 = psImageSubset(original, region2);
+	    ok(subset1, "psImageSubset() returned non-NULL (subset1)");
+	    psImage* subset2 = psImageSubset(original, region1);
+	    ok(subset2, "psImageSubset() returned non-NULL (subset2)");
+
+	    psFree (original);
+	    psFree (subset1);
+	    psFree (subset2);
+	}
+
+	{
+	    psImage* original = psImageAlloc(numCols,numRows,PS_TYPE_U32);
+	    for (psS32 row=0;row<numRows;row++) {
+		for (psS32 col=0;col<numCols;col++) {
+		    original->data.F32[row][col] = row*1000+col;
+		}
+	    }
+
+	    // XXX this is not being used in this section
+	    memcpy(&preSubsetStruct, original, sizeof(psImage));
+
+	    // Verify the returned psImage structure pointer is null and program
+	    // execution doesn't stop, if the input parameter image is null.
+	    // Also verified the input psImage structure is not modified
+	    // An error should follow...
+	    // XXX: Verify error
+	    psImage* subset1 = psImageSubset(NULL,region1);
+	    ok(subset1 == NULL, "psImageSubset returned NULL when input image was NULL.");
+
+	    // Verify the returned psImage structure pointer is null and program
+	    // execution doesn't stop, if the input parameters nrow and/or ncol are zero.
+	    // Also verify input psImage structure is not modified
+	    // An error should follow...
+	    // XXX: Verify error
+	    {
+		memcpy(&preSubsetStruct,original,sizeof(psImage));
+		subset1 = psImageSubset(original, psRegionSet(0,numCols/2,numRows/2,numRows/2));
+		ok(subset1 == NULL, "psImageSubset returned NULL when numRows=0.");
+		// An error should follow...
+		// XXX: Verify error
+		subset1 = psImageSubset(original,psRegionSet(numCols/2,numCols/2,0,numRows/2));
+		ok(subset1 == NULL, "psImageSubset returned NULL when numCols=0.");
+		ok(memcmp(original,&preSubsetStruct,sizeof(psImage)) == 0,
+		   "psImageSubset didn't change the original struct though it failed to subset.");
+	    }
+
+	    // Verify the returned psImage structure pointer is null and program
+	    // execution doesn't stop, if the input parameters row0 and col0 are not within
+	    // the range of values of psImage structure image
+	    // An error should follow...
+	    // XXX: Verify error
+	    {
+		subset1 = psImageSubset(original, psRegionSet(0,numCols/2, 0,numRows*2));
+		ok(subset1 == NULL,
+		   "psImageSubset returned NULL when subset origin was outside of image (via cols)");
+		// An error should follow...
+		// XXX: Verify error
+		subset1 = psImageSubset(original,psRegionSet(0,numCols*2,0,numRows/2));
+		ok(subset1 == NULL,
+		   "psImageSubset returned NULL when subset origin was outside of image (via rows)");
+		// An error should follow...
+		// XXX: Verify error
+		subset1 = psImageSubset(original, psRegionSet(-1,numCols/2,0,numRows/2));
+		ok(subset1 == NULL,
+		   "psImageSubset returned NULL when subset origin was outside of image (col0=-1)");
+		// An error should follow...
+		// XXX: Verify error
+		subset1 = psImageSubset(original, psRegionSet(0,numCols/2,-1,numRows/2));
+		ok(subset1 == NULL,
+		   "psImageSubset returned NULL when subset origin was outside of image (row0=-1)");
+	    }    
+
+	    // Verify the returned psImage structure pointer is null and program
+	    // execution doesn't stop if the input parameters nrow, ncol, row0 and col0
+	    // specify a range of data not within the input psImage structure image.  Also
+	    // verify the input psImage structure is not modified
+	    // An error should follow...
+	    // XXX: Verify error
+	    {
+		subset1 = psImageSubset(original,psRegionSet(0,numCols/2,0,numRows+1));
+		ok(subset1 == NULL,
+		   "psImageSubset returned NULL when subset was outside of image (via rows)");
+		// An error should follow...
+		// XXX: Verify error
+		subset1 = psImageSubset(original, psRegionSet(0,numCols+1,0,numRows/2));
+		ok(subset1 == NULL,
+		   "psImageSubset returned NULL when subset was outside of image (via cols)");
+		// An error should follow...
+		// XXX: Verify error
+		subset1 = psImageSubset(original,psRegionSet(0,numCols+1,0,numRows+1));
+		ok(subset1 == NULL,
+		   "psImageSubset returned NULL when subset was outside of image (via row+cols)");
+	    }
+	    psFree(original);
+	}
+        ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+    }
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(239);
+
+    genericImageSubsetTest(128, 256);
+    genericImageSubsetTest(256, 128);
+    genericImageSubsetTest(128, 128);
+
+    // psImageCopy()
+    {
+        psMemId id = psMemGetId();
+        psU32 c = 128;
+        psU32 r = 256;
+
+        psImage *img = psImageAlloc(c,r,PS_TYPE_F32);
+        for (unsigned row=0;row<r;row++) {
+            for (unsigned col=0;col<c;col++) {
+                img->data.F32[row][col] = (psF32)(row+col);
+            }
+        }
+        psImage *img2 = psImageAlloc(c,r,PS_TYPE_F32);
+        for (unsigned row=0;row<r;row++) {
+            for (unsigned col=0;col<c;col++) {
+                img2->data.F32[row][col] = 0.0f;
+            }
+        }
+
+        psImage *img3 = psImageCopy(img2,img,PS_TYPE_F32);
+        // Verify the returned psImage structure pointer is equal to the input
+        // parameter output.
+        ok(img2 == img3, "psImageCopy(): recycled input image");
+
+        // Verify the returned psImage structure is the same type as the input image
+        // structure if the specified output argument is NULL.
+        psImage *img4 = psImageCopy(NULL,img,PS_TYPE_F32);
+        ok(img4 != NULL, "psImageCopy() returned non-NULL with NULL output argument");
+        ok(img4->type.type == img->type.type, "psImageCopy() set the correct image type");
+        bool errorFlag = false;
+        for (psU32 row=0;row<r;row++) {
+            psF32* imgInRow = img->data.F32[row];
+            psF32* imgOutRow = img4->data.F32[row];
+            for (unsigned col=0;col<c;col++) {
+                if(imgInRow[col] != imgOutRow[col]) {
+                    diag("Input image not equal to output image at %d,%d", col, row);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psImageCopy() set data correctly");
+
+
+        // Verify the returned psImage structure member are equal to the values in
+        // the input psImage structure input.
+        #define testImageCopyType(IN,OUT) \
+        { \
+            bool errorFlag = false; \
+            img = psImageRecycle(img,c,r,PS_TYPE_##IN); \
+            for (unsigned row=0;row<r;row++) { \
+                ps##IN* imgRow = img->data.IN[row]; \
+                for (unsigned col=0;col<c;col++) { \
+                    imgRow[col] = (ps##IN)(row+col); \
+                } \
+            } \
+            img2 = psImageCopy(img2,img,PS_TYPE_##OUT); \
+            if(img2 != NULL) { \
+                ok(img2, "psImageCopy() returned non-NULL"); \
+                for (psU32 row=0;row<r;row++) { \
+                    ps##IN* imgRow = img->data.IN[row]; \
+                    ps##OUT* img2Row = img2->data.OUT[row]; \
+                    for (psU32 col=0;col<c;col++) { \
+                        if (abs(imgRow[col] - (ps##IN)(row+col)) > 0.5) { \
+                            diag("Input image was changed at %d,%d", col,row); \
+                            errorFlag = true; \
+                        } \
+                        if (abs(img2Row[col] - (ps##OUT)(imgRow[col])) > 0.5) { \
+                            diag("returned psImage values after copy don't match at %d,%d " \
+                                 "(%d vs %d)",\
+                                 col,row,img2Row[col], (ps##OUT)(imgRow[col])); \
+                            errorFlag = true; \
+                        } \
+                    } \
+                } \
+                ok(!errorFlag, "psImageCopy() set image data correctly"); \
+            } else {\
+                ok(img2, "psImageCopy() returned non-NULL"); \
+            } \
+        }
+
+        #define testImageCopyTypes(IN) \
+        testImageCopyType(IN,F32);\
+        testImageCopyType(IN,F64); \
+        testImageCopyType(IN,U8); \
+        testImageCopyType(IN,U16); \
+        testImageCopyType(IN,U32); \
+        testImageCopyType(IN,S8);\
+        testImageCopyType(IN,S16);\
+        testImageCopyType(IN,S32);
+
+        testImageCopyTypes(U8);
+        testImageCopyTypes(U16);
+        testImageCopyTypes(U32);
+        testImageCopyTypes(S8);
+        testImageCopyTypes(S16);
+        testImageCopyTypes(S32);
+        testImageCopyTypes(F32);
+        testImageCopyTypes(F64);
+
+        // Verify the returned psImage structure pointer is null and program
+        // execution doesn't stop, if the input parameter input is null.
+        // An error should follow...
+        // XXX: Verify error
+        img3 = psImageCopy(NULL,NULL,PS_TYPE_F32);
+        ok(img3 == NULL, "psImageCopy() returned NULL when input image was NULL");
+
+        psFree(img);
+        psFree(img2);
+        psFree(img3);
+        psFree(img4);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psImageTrim()
+    // XXX: This should probably be broken into separate blocks
+    {
+        psMemId id = psMemGetId();
+        psS32 r = 200;
+        psS32 c = 300;
+        psS32 qtrR = r/4;
+        psS32 qtrC = c/4;
+        psS32 halfR = r/2;
+        psS32 halfC = c/2;
+        psRegion centerHalf = {qtrC, qtrC+halfC, qtrR, qtrR+halfR};
+
+        psImage* image = psImageAlloc(c,r,PS_TYPE_F32);
+        for (psS32 row = 0; row < image->numRows; row++) {
+            for (psS32 col = 0; col < image->numCols; col++) {
+                image->data.F32[row][col] = (psF32)col + (psF32)row/1000.0f;
+            }
+        }
+        // invoke psImageTrim with non-NULL image, and a valid region
+        // x0,y0->x1,y1 (using only positive values). Verify that:
+        // a. the return psImage is the same as the input psImage.
+        // b. the size of the psImage is x1-x0 by y1-y0.
+        // c. the pixel values coorespond to the region [x0:x1-1,y0:y1-1].
+        psImage* image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+        psImage* image2 = psImageTrim(image1,centerHalf);
+        ok(image1 == image2, "psImageTrim() Return value same as input value");
+        ok(image2->numCols == halfC && image2->numRows == halfR,
+           "The resulting image size was %dx%d, should be %dx%d.",
+           image2->numCols, image2->numRows,
+           halfC, halfR);
+
+        bool errorFlag = false;
+        for (psS32 row = 0; row < image2->numRows; row++) {
+            for (psS32 col = 0; col < image2->numCols; col++) {
+                if (fabsf(image2->data.F32[row][col] - image->data.F32[row+qtrR][col+qtrC])
+                        > FLT_EPSILON) {
+                    diag("The value at (%d,%d) was %g, but should be %g.",
+                         col,row,
+                         image2->data.F32[row][col],
+                         image->data.F32[row+qtrR][col+qtrC]);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psImageTrim() set image data correctly");
+
+
+        // 2. invoke psImageTrim with non-NULL image and valid region where x1=0,
+        //    y1=0. Verify that:
+        //    a. the return psImage size is numCols-x0 by numRows-y0
+        //    b. the pixel values coorespond to the region
+        //       [x0:numCols-1,y0:numRows-1].
+        image1 = psImageCopy(image1,image,PS_TYPE_F32);
+        image2 = psImageTrim(image1,psRegionSet(qtrC,0,qtrR,0));
+        ok(image1 == image2, "psImageTrim() Return value same as input value");
+        ok(image2->numCols == image->numCols-qtrC && image2->numRows == image->numRows-qtrR,
+           "The resulting image size was %dx%d, it should be %dx%d.",
+           image2->numCols, image2->numRows,
+           image->numCols-qtrC, image->numRows-qtrR);
+
+        for (psS32 row = 0; row < image2->numRows; row++) {
+            for (psS32 col = 0; col < image2->numCols; col++) {
+                if (fabsf(image2->data.F32[row][col] -
+                          image->data.F32[row+qtrR][col+qtrC]) > FLT_EPSILON) {
+                    diag("The value at (%d,%d) was %g, but should be %g.",
+                         col,row,
+                         image2->data.F32[row][col],
+                         image->data.F32[row+qtrR][col+qtrC]);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psImageTrim() set image data correctly");
+
+
+        //  Invoke psImageTrim with x1<0, y1<0. Verify:
+        //      a. the psImage size is (numCols+x1)-x0 by (numRows+y1)-y0.
+        //      b. the pixel values coorespond to the region
+        //         [x0:numCols+x1-1,y0:numRows+y1-1].
+        image1 = psImageCopy(image1,image,PS_TYPE_F32);
+        image2 = psImageTrim(image1,psRegionSet(qtrC,-qtrC, qtrR,-qtrR));
+        ok(image1 == image2, "psImageTrim() Return value same as input value");
+        ok(image2->numCols == image->numCols-qtrC-qtrC && image2->numRows == image->numRows-qtrR-qtrR,
+           "The resulting image size was %dx%d, but should be %dx%d.",
+           image2->numCols, image2->numRows,
+           image->numCols-qtrC, image->numRows-qtrR);
+        for (psS32 row = 0; row < image2->numRows; row++) {
+            for (psS32 col = 0; col < image2->numCols; col++) {
+                if (fabsf(image2->data.F32[row][col] -
+                          image->data.F32[row+qtrR][col+qtrC]) > FLT_EPSILON) {
+                    diag("The value at (%d,%d) was %g, but should be %g.",
+                         col,row,
+                         image2->data.F32[row][col],
+                         image->data.F32[row+qtrR][col+qtrC]);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psImageTrim() set image data correctly");
+        psFree(image1);
+
+
+        //  Invoke psImageTrim with image=NULL Verify:
+        //      a. execution does not cease.
+        //      b. return value is NULL
+        //      c. appropriate error is generated.
+        // An error should follow...
+        // XXX: Verify errors
+        image2 = psImageTrim(NULL, psRegionSet(qtrC,0,qtrR,0));
+        ok(image2 == NULL, "psImageTrim returned NULL given a NULL input image");
+        psErr* err = psErrorLast();
+        ok(err != NULL && err->code == PS_ERR_BAD_PARAMETER_NULL,
+           "psImageTrim did generate an appropriate error for NULL input image.");
+        psFree(err);
+
+
+        // Verify when psRegion has a coord at -1
+        image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+        image2 = psImageTrim(image1, psRegionSet(-1,0,0,0));
+        ok(image2 == NULL, "psImageTrim returned NULL given x0=-1.");
+        err = psErrorLast();
+        ok(err != NULL && err->code == PS_ERR_BAD_PARAMETER_VALUE,
+           "psImageTrim did generate an appropriate error for x0=-1.");
+        psFree(err);
+
+
+        // Verify when psRegion has a coord at -1
+        image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+        image2 = psImageTrim(image1, psRegionSet(0,0,-1,0));
+        ok(image2 == NULL, "psImageTrim returned NULL given y0=-1.");
+        err = psErrorLast();
+        ok(err != NULL && err->code == PS_ERR_BAD_PARAMETER_VALUE,
+           "psImageTrim did generate an appropriate error for y0=-1");
+        psFree(err);
+
+
+        // Verify when psRegion has a coord at outside the image range
+        image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+        image2 = psImageTrim(image1, psRegionSet(0,image->numCols+1,0,0));
+        ok(image2 == NULL, "psImageTrim returned NULL given x1=numCols+1");
+        err = psErrorLast();
+        ok(err != NULL && err->code == PS_ERR_BAD_PARAMETER_VALUE,
+           "psImageTrim did generate an appropriate error for x1=numCols+1");
+        psFree(err);
+
+
+        // Verify when psRegion has a coord at outside the image range
+        image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+        image2 = psImageTrim(image1, psRegionSet(0,0,0,image->numRows+1));
+        ok(image2 == NULL, "psImageTrim returned NULL given y1=numRows+1");
+        err = psErrorLast();
+        ok(err != NULL && err->code == PS_ERR_BAD_PARAMETER_VALUE,
+           "psImageTrim did generate an appropriate error for y1=numRows+1");
+        psFree(err);
+
+
+        // Verify when psRegion has a coord at outside the image range
+        image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+        image2 = psImageTrim(image1, psRegionSet(0,0,0,(((psF32)image->numRows)*-1.0)));
+        ok(image2 == NULL, "psImageTrim returned NULL given y1=-numRows");
+        err = psErrorLast();
+        ok(err != NULL && err->code == PS_ERR_BAD_PARAMETER_VALUE,
+           "psImageTrim did generate an appropriate error for y1=-numRows");
+        psFree(err);
+
+
+        // Verify when psRegion has a coord at outside the image range
+        image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+        image2 = psImageTrim(image1, psRegionSet(0,(((psF32)image->numCols)*-1.0),0,0));
+        ok(image2 == NULL, "psImageTrim returned NULL given x1=-numCols");
+        err = psErrorLast();
+        ok(err != NULL && err->code == PS_ERR_BAD_PARAMETER_VALUE,
+           "psImageTrim did generate an appropriate error for x1=-numCols");
+
+
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageConvolve.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageConvolve.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageConvolve.c	(revision 22322)
@@ -0,0 +1,445 @@
+/** @file  tst_psImageConvolve.c
+ *
+ *  @brief Contains the tests for psImageConvolve.[ch]
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-02-24 23:43:15 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "psType.h"
+
+static psS32 testKernelAlloc(void);
+static psS32 testKernelGenerate(void);
+static psS32 testImageConvolve(void);
+
+testDescription tests[] = {
+                              {testKernelAlloc,731,"psKernelAlloc",0,false},
+                              {testKernelGenerate,732,"psKernelGenerate",0,false},
+                              {testImageConvolve,733,"psImageConvolve",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    return ! runTestSuite(stderr,"psImage",tests,argc,argv);
+}
+
+static psS32 testKernelAlloc(void)
+{
+    psS32 numCases = 4;
+    psS32 xMin[] = { -5,  0,-10,  5};
+    psS32 xMax[] = {  0,  5, -5, 10};
+    psS32 yMin[] = { -4,  0, -8,  4};
+    psS32 yMax[] = {  0,  4, -4,  8};
+    psS32 i;
+    psKernel* k;
+
+    for (i=0;i<numCases;i++) {
+        k = psKernelAlloc(xMin[i],xMax[i],yMin[i],yMax[i]);
+
+        if (k == NULL) {
+            psError(PS_ERR_UNKNOWN, true,"psKernelAlloc returned NULL for [%d:%d,%d:%d].",
+                    xMin[i], xMax[i], yMin[i], yMax[i]);
+            return i*10+1;
+        }
+
+        if (k->xMin != xMin[i] || k->xMax != xMax[i] ||
+                k->yMin != yMin[i] || k->yMax != yMax[i]) {
+            psError(PS_ERR_UNKNOWN, true,"Min/max members, [%d:%d,%d:%d], of psKernel wrong. Should be [%d:%d,%d:%d].",
+                    k->xMin,k->xMax, k->yMin, k->yMax,
+                    xMin[i], xMax[i], yMin[i], yMax[i]);
+            return i*10+2;
+        }
+
+        if (k->image->numCols != xMax[i]-xMin[i]+1 ||
+                k->image->numRows != yMax[i]-yMin[i]+1) {
+            psError(PS_ERR_UNKNOWN, true,"Size of the kernel image is wrong (%dx%d vs %dx%d).",
+                    xMax[i]-xMin[i]+1, yMax[i]-yMin[i]+1,
+                    k->image->numCols, k->image->numRows);
+            return i*10+2;
+        }
+
+        for (psS32 j=yMin[i]; j<yMax[i]; j++) {
+            if (k->kernel[j]+xMin[i] != k->image->data.PS_TYPE_KERNEL_DATA[j-yMin[i]]) {
+                psError(PS_ERR_UNKNOWN, true,"The kernel pointer was set wrong for row %d.",
+                        j);
+                return i*10+4;
+            }
+        }
+
+        psFree(k);
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be a warning (xMin > xMax)");
+    k = psKernelAlloc(5, -5, -2, 2);
+    if (k == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelAlloc returned NULL for xMin > xMax.");
+        return i*10+5;
+    }
+
+    if (k->xMin != -5 || k->xMax != 5) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelAlloc didn't swap xMin & xMax.");
+        return i*10+6;
+    }
+
+    psFree(k);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be a warning (yMin > yMax)");
+    k = psKernelAlloc(-2, 2, 5, -5);
+    if (k == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelAlloc returned NULL for yMin > yMax.");
+        return i*10+7;
+    }
+
+    if (k->yMin != -5 || k->yMax != 5) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelAlloc didn't swap yMin & yMax.");
+        return i*10+8;
+    }
+
+    psFree(k);
+
+    return 0;
+}
+
+static psS32 testKernelGenerate(void)
+{
+    psS32 size = 5;
+    psS32 t[] = { 1, 2, 8, 9, 10 };
+    psS32 x[] = { 0, 1, 0, -1, 0 };
+    psS32 y[] = { 2, 1, -1, -2, 0 };
+    float sum;
+
+    psVector* xVec = psVectorAlloc(size,PS_TYPE_U32);
+    psVector* yVec = psVectorAlloc(size,PS_TYPE_U32);
+    psVector* tVec = psVectorAlloc(size,PS_TYPE_U32);
+
+    for (psS32 i = 0; i < size; i++) {
+        xVec->data.U32[i] = x[i];
+        xVec->n++;
+        yVec->data.U32[i] = y[i];
+        yVec->n++;
+        tVec->data.U32[i] = t[i];
+        tVec->n++;
+    }
+
+    psKernel* result = psKernelGenerate(tVec, xVec, yVec, false);
+
+    if (result == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate returned NULL.");
+        return 1;
+    }
+
+    if (result->xMin != -1 || result->xMax != 1 ||
+            result->yMin != -2 || result->yMax != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate result had a range of [%d:%d,%d:%d].  Suppose to be [-2:2,-1:1].",
+                result->xMin, result->xMax, result->yMin, result->yMax);
+        return 2;
+    }
+
+    sum = 0.0;
+    printf("Resulting kernel:\n");
+    for (psS32 y = result->yMin; y <= result->yMax; y++) {
+        for (psS32 x = result->xMin; x <= result->xMax; x++) {
+            printf(" %6.2f ", result->kernel[y][x]);
+            sum += result->kernel[y][x];
+        }
+        printf("\n");
+    }
+    if (fabsf(1.0 - sum) > FLT_EPSILON) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate result is not normalized (sum=%g).",
+                sum);
+
+        return 3;
+
+    }
+
+    if (fabsf(result->kernel[-2][0] - 0.1) > FLT_EPSILON ||
+            fabsf(result->kernel[ -1][ -1] - 0.1) > FLT_EPSILON ||
+            fabsf(result->kernel[ 1][ 0] - 0.6) > FLT_EPSILON ||
+            fabsf(result->kernel[2][1] - 0.1) > FLT_EPSILON) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate result values, %g,%g,%g,%g, are wrong. Suppose to be 0.1,0.1,0.6,0.1",
+                result->kernel[-2][0], result->kernel[-1][-1],
+                result->kernel[1][0], result->kernel[2][1]);
+
+        return 4;
+    }
+
+    psFree(result);
+    psFree(xVec);
+    psFree(yVec);
+    psFree(tVec);
+
+    xVec = psVectorAlloc(size,PS_TYPE_S16);
+    yVec = psVectorAlloc(size,PS_TYPE_S16);
+    tVec = psVectorAlloc(size,PS_TYPE_S16);
+
+    for (psS32 i = 0; i < size; i++) {
+        xVec->data.S16[i] = x[i];
+        xVec->n++;
+        yVec->data.S16[i] = y[i];
+        yVec->n++;
+        tVec->data.S16[i] = t[i];
+        tVec->n++;
+    }
+
+    result = psKernelGenerate(tVec, xVec, yVec, true);
+
+    if (result == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate returned NULL.");
+        return 5;
+    }
+
+    if (result->xMin != 0 || result->xMax != 1 ||
+            result->yMin != 0 || result->yMax != 3) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate result had a range of [%d:%d,%d:%d].  Suppose to be [0:1,1:2].",
+                result->xMin, result->xMax, result->yMin, result->yMax);
+        return 6;
+    }
+
+    sum = 0.0;
+    printf("Resulting kernel (relative=true):\n");
+    for (psS32 y = result->yMin; y <= result->yMax; y++) {
+        for (psS32 x = result->xMin; x <= result->xMax; x++) {
+            printf(" %6.2f ", result->kernel[y][x]);
+            sum += result->kernel[y][x];
+        }
+        printf("\n");
+    }
+    if (fabsf(1.0 - sum) > FLT_EPSILON) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate result is not normalized (sum=%g).",
+                sum);
+
+        return 7;
+
+    }
+
+    if (fabsf(result->kernel[0][0] - 19.0/30.0) > FLT_EPSILON ||
+            fabsf(result->kernel[2][0] - 1.0/30.0) > FLT_EPSILON ||
+            fabsf(result->kernel[2][1] - 8.0/30.0) > FLT_EPSILON ||
+            fabsf(result->kernel[3][1] - 2.0/30.0) > FLT_EPSILON) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate result values, %g,%g;%g,%g, are wrong. Suppose to be 2,6;1,0.",
+                result->kernel[0][0], result->kernel[2][0],
+                result->kernel[2][1], result->kernel[3][1]);
+
+        return 8;
+    }
+
+    psFree(result);
+    psFree(xVec);
+    psFree(yVec);
+    psFree(tVec);
+
+    xVec = psVectorAlloc(size,PS_TYPE_F32);
+    yVec = psVectorAlloc(size,PS_TYPE_F32);
+    tVec = psVectorAlloc(size,PS_TYPE_F32);
+
+    for (psS32 i = 0; i < size; i++) {
+        //        xVec->data.F32[i] = x[i]+0.1;
+        //        xVec->n++;
+        psVectorSet(xVec, i, x[i]+0.1);
+        yVec->data.F32[i] = y[i]+0.2;
+        yVec->n++;
+        tVec->data.F32[i] = t[i]+0.3;
+        tVec->n++;
+    }
+
+    tVec->n--; // decrease size by one to make vectors unequal in length.
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error.");
+    result = psKernelGenerate(tVec, xVec, yVec, false);
+    if (result != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate returned non-NULL given differing sized vectors.");
+        return 9;
+    }
+
+    psFree(result);
+
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be a error (time vector NULL).");
+    result = psKernelGenerate(NULL, xVec, yVec, true);
+    if (result != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate returned a kernel with no time vector.");
+        return 11;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be a error (x vector NULL).");
+    result = psKernelGenerate(tVec, NULL, yVec, true);
+    if (result != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate returned a kernel with no x vector.");
+        return 11;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be a error (y vector NULL).");
+    result = psKernelGenerate(tVec, xVec, NULL, true);
+    if (result != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psKernelGenerate returned a kernel with no y vector.");
+        return 11;
+    }
+
+    psFree(xVec);
+    psFree(yVec);
+    psFree(tVec);
+    psFree(result);
+
+    return 0;
+}
+
+static psS32 testImageConvolve(void)
+{
+    const psS32 r = 200;
+    const psS32 c = 300;
+    psS32 sum;
+
+    // approximate a normalized gaussian kernel.
+    psKernel* g = psKernelAlloc(-1,1,-1,1);
+    g->kernel[-1][-1] =
+        g->kernel[-1][1] =
+            g->kernel[1][-1] =
+                g->kernel[1][1] = 0.0113;
+    g->kernel[1][0] =
+        g->kernel[-1][0] =
+            g->kernel[0][-1] =
+                g->kernel[0][1] = 0.0838;
+    g->kernel[0][0] = 0.6193;
+
+    // create a normalized non-symetric kernel.
+    psKernel* nsk = psKernelAlloc(0,2,0,2);
+    sum = 0.0;
+    for (psS32 i=0;i<2;i++) {
+        for (psS32 j=0;j<2;j++) {
+            nsk->kernel[i][j] = i+j;
+            sum = i+j;
+        }
+    }
+    for (psS32 i=0;i<2;i++) {
+        for (psS32 j=0;j<2;j++) {
+            nsk->kernel[i][j] /= sum;
+        }
+    }
+
+
+    psImage* img = psImageAlloc(c,r,PS_TYPE_F32);
+    memset(img->data.F32[0],0,c*r*PSELEMTYPE_SIZEOF(PS_TYPE_F32));
+    img->data.F32[0][0] = 1.0f;
+    img->data.F32[r/2][c/2] = 1.0f;
+    img->data.F32[r-1][c/2] = 1.0f;
+
+    // test spacial convolution of gaussian
+    psLogMsg(__func__,PS_LOG_INFO,"Testing direct gaussian convolution");
+    psImage* out = psImageConvolve(NULL, img, g, true);
+
+    if (out == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "psImageConvolve returned a NULL for direct gaussian case.");
+        return 1;
+    }
+
+    if (out->numCols != c || out->numRows != r) {
+        psError(PS_ERR_UNKNOWN, true, "psImageConvolve result image is %dx%d, but expected %dx%d.",
+                out->numCols, out->numRows,
+                c,r);
+        return 2;
+    }
+
+    if (out->type.type != PS_TYPE_F32) {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,out->type.type);
+        psError(PS_ERR_UNKNOWN, true, "psImageConvolve result image is of type %s, not psF32.",
+                typeStr);
+        return 3;
+    }
+
+    // test values
+    for (psS32 i=-1;i<1;i++) {
+        for (psS32 j=-1;j<1;j++) {
+            if (fabsf(out->data.F32[r/2+i][c/2+j] - g->kernel[i][j]) > 0.0001) {
+                psError(PS_ERR_UNKNOWN, true,"Convolved image wrong at %d,%d.  Value is %g, expected %g.",
+                        c/2+j,r/2+i,
+                        out->data.F32[r/2+i][c/2+j], g->kernel[i][j]);
+                return 4;
+            }
+            if (i >= 0 && j >= 0 && fabsf(out->data.F32[i][j] - g->kernel[i][j]) > 0.0001) {
+                psError(PS_ERR_UNKNOWN, true,"Convolved image wrong at %d,%d.  Value is %g, expected %g.",
+                        j,i,
+                        out->data.F32[i][j], g->kernel[i][j]);
+                return 5;
+            }
+            if (i <= 0 && fabsf(out->data.F32[r-1+i][c/2+j] - g->kernel[i][j]) > 0.0001) {
+                psError(PS_ERR_UNKNOWN, true,"Convolved image wrong at %d,%d.  Value is %g, expected %g.",
+                        c/2+j,r-1+i,
+                        out->data.F32[r-1+i][c/2+j], g->kernel[i][j]);
+                return 6;
+            }
+        }
+    }
+
+    // test fourier convolution of gaussian
+    psLogMsg(__func__,PS_LOG_INFO,"Testing fourier gaussian convolution");
+    psImage* out2 = psImageConvolve(out, img, g, false);
+
+    if (out == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "psImageConvolve returned a NULL for gaussian case.");
+        return 10;
+    }
+
+    if (out != out2) {
+        psError(PS_ERR_UNKNOWN, true, "psImageConvolve didn't recycle the supplied out image struct.");
+        return 11;
+    }
+
+    if (out->numCols != c || out->numRows != r) {
+        psError(PS_ERR_UNKNOWN, true, "psImageConvolve result image is %dx%d, but expected %dx%d.",
+                out->numCols, out->numRows,
+                c,r);
+        return 12;
+    }
+
+    if (out->type.type != PS_TYPE_F32) {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,out->type.type);
+        psError(PS_ERR_UNKNOWN, true, "psImageConvolve result image is of type %s, not psF32.",
+                typeStr);
+        return 13;
+    }
+
+    // test values
+    for (psS32 i=-1;i<1;i++) {
+        for (psS32 j=-1;j<1;j++) {
+            if (fabsf(out->data.F32[r/2+i][c/2+j] - g->kernel[i][j]) > 0.01) {
+                psError(PS_ERR_UNKNOWN, true,"Convolved image wrong at %d,%d.  Value is %g, expected %g.",
+                        c/2+j,r/2+i,
+                        out->data.F32[r/2+i][c/2+j], g->kernel[i][j]);
+                return 14;
+            }
+            if (i >= 0 && j >= 0 && fabsf(out->data.F32[i][j] - g->kernel[i][j]) > 0.01) {
+                psError(PS_ERR_UNKNOWN, true,"Convolved image wrong at %d,%d.  Value is %g, expected %g.",
+                        j,i,
+                        out->data.F32[i][j], g->kernel[i][j]);
+                return 15;
+            }
+            if (i <= 0 && fabsf(out->data.F32[r-1+i][c/2+j] - g->kernel[i][j]) > 0.01) {
+                psError(PS_ERR_UNKNOWN, true,"Convolved image wrong at %d,%d.  Value is %g, expected %g.",
+                        c/2+j,r-1+i,
+                        out->data.F32[r-1+i][c/2+j], g->kernel[i][j]);
+                return 16;
+            }
+        }
+    }
+
+    psFree(g);
+    psFree(img);
+    psFree(nsk);
+    psFree(out);
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageGeomManip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageGeomManip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageGeomManip.c	(revision 22322)
@@ -0,0 +1,1150 @@
+/** @file  tst_psImageGeomManip.c
+ *
+ *  @brief Contains the tests for psImageManip.[ch]
+ *
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-04-20 01:13:11 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <complex.h>
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string.h>                    // for memset
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "psType.h"
+
+static psS32 testImageRebin(void);
+static psS32 testImageRoll(void);
+static psS32 testImageRotate(void);
+static psS32 testImageShift(void);
+static psS32 testImageShiftCase(psS32 cols, psS32 rows, float colShift,float rowShift);
+static psS32 testImageResample(void);
+static psS32 testImageTransform(void);
+
+testDescription tests[] = {
+                              {testImageRebin,559,"psImageRebin",0,false},
+                              {testImageRoll,562,"psImageRoll",0,false},
+                              {testImageRotate,560,"psImageRotate",0,false},
+                              {testImageShift,561,"psImageShift",0,false},
+                              {testImageResample,743,"psImageResample",0,false},
+                              {testImageTransform,-1,"psImageTransform",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    return ! runTestSuite(stderr,"psImage",tests,argc,argv);
+}
+
+static psS32 testImageRebin(void)
+{
+
+    /*
+    This function shall generate a rescaled version of a psImage structure
+    derived from a specified statistics method.
+    */
+
+    psImage* in = NULL;
+    psImage* out = NULL;
+    psImage* out2 = NULL;
+    psImage* out3 = NULL;
+    psImage* mask = NULL;
+    psImage* meanTruth = NULL;
+    psImage* meanTruthWMask = NULL;
+    psImage* maxTruth = NULL;
+    psStats stats;
+
+    /*
+    Verify the returned psImage structure contains expected values, if the
+    input parameter input contains known data, the input scale is a known
+    value with a known statistical method specified in stats. Cases should
+    include at least two different scales and statistical methods. Comparison
+    of expected values should include a delta to allow testing on different
+    platforms.
+    */
+
+    #define testRebinType(DATATYPE)  \
+    in = psImageAlloc(16,16,PS_TYPE_##DATATYPE); \
+    mask = psImageAlloc(16,16,PS_TYPE_U8); \
+    meanTruth = psImageAlloc(4,4,PS_TYPE_F32); \
+    meanTruthWMask = psImageAlloc(4,4,PS_TYPE_F32); \
+    maxTruth = psImageAlloc(6,6,PS_TYPE_F32); \
+    memset(meanTruth->data.F32[0],0,sizeof(psF32)*4*4); \
+    memset(meanTruthWMask->data.F32[0],0,sizeof(psF32)*4*4); \
+    memset(maxTruth->data.F32[0],0,sizeof(psF32)*6*6); \
+    for (psS32 row = 0; row<16; row++) { \
+        ps##DATATYPE* inRow = in->data.DATATYPE[row]; \
+        psF32* meanTruthRow = meanTruth->data.F32[row/4]; \
+        psF32* meanTruthWMaskRow = meanTruthWMask->data.F32[row/4]; \
+        psF32* maxTruthRow = maxTruth->data.F32[row/3]; \
+        psU8* maskRow = mask->data.U8[row]; \
+        for (psS32 col = 0; col<16; col++) { \
+            if(col != 15) { \
+                maskRow[col] = 0; \
+            } else { \
+                maskRow[col] = 1; \
+            } \
+            inRow[col] = row + col; \
+            meanTruthRow[col/4] += row + col; \
+            if (maxTruthRow[col/3] < row + col) { \
+                maxTruthRow[col/3] = row+col; \
+            } \
+            if(maskRow[col] == 0 ) { \
+                meanTruthWMaskRow[col/4] += row + col; \
+            } \
+        } \
+    } \
+    for (psS32 row = 0; row<4; row++) { \
+        psF32* meanTruthRow = meanTruth->data.F32[row]; \
+        psF32* meanTruthWMaskRow = meanTruthWMask->data.F32[row]; \
+        for (psS32 col = 0; col<4; col++) { \
+            meanTruthRow[col] /= 16; \
+            if ( col == 3 ) { \
+                meanTruthWMaskRow[col] /= 12; \
+            } else { \
+                meanTruthWMaskRow[col] /= 16; \
+            } \
+        } \
+    } \
+    stats.options = PS_STAT_SAMPLE_MEAN; \
+    out = psImageRebin(NULL,in,NULL,0,4,&stats); \
+    if (out == NULL) { \
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin returned a NULL pointer!?"); \
+        return 1; \
+    } \
+    if (out->numRows != 4 || out->numCols != 4) { \
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin didn't produce the proper size image " \
+                "(%d x %d).", \
+                out->numCols, out->numRows); \
+        return 2; \
+    } \
+    for (psS32 row = 0; row<4; row++) { \
+        ps##DATATYPE* outRow = out->data.DATATYPE[row]; \
+        psF32* truthRow = meanTruth->data.F32[row]; \
+        for (psS32 col = 0; col<4; col++) { \
+            if (fabsf((float)outRow[col]-(float)truthRow[col]) > FLT_EPSILON) { \
+                psError(PS_ERR_UNKNOWN, true,"psImageRebin didn't produce the proper mean " \
+                        "result at (%d,%d) [%f vs %f].", \
+                        col,row,outRow[col],truthRow[col]); \
+                return 3; \
+            } \
+        } \
+    } \
+    stats.options = PS_STAT_SAMPLE_MEAN; \
+    out3 = psImageRebin(NULL,in,mask,1,4,&stats); \
+    for (psS32 row = 0; row<4; row++) { \
+        ps##DATATYPE* outRow = out3->data.DATATYPE[row]; \
+        psF32* truthRow = meanTruthWMask->data.F32[row]; \
+        for ( psS32 col = 0; col<4; col++) { \
+            if(abs((psS32)outRow[col]-(psS32)truthRow[col]) > FLT_EPSILON) { \
+                psError(PS_ERR_UNKNOWN, true,"psImageRebin with mask didn't produce the proper mean " \
+                        "result at (%d,%d) [%f vs %f].", \
+                        col,row,outRow[col],truthRow[col]); \
+                return 3; \
+            } \
+        } \
+    } \
+    stats.options = PS_STAT_MAX; \
+    out2 = psImageRebin(out,in,NULL,0,3,&stats); \
+    if (out != out2) { \
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin didn't recycle a psImage properly!?"); \
+        return 7; \
+    } \
+    if (out == NULL) { \
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin returned a NULL pointer!?"); \
+        return 4; \
+    } \
+    if (out->numRows != 6 || out->numCols != 6) { \
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin didn't produce the proper size image " \
+                "(%d x %d).", \
+                out->numCols, out->numRows); \
+        return 5; \
+    } \
+    for (psS32 row = 0; row<6; row++) { \
+        ps##DATATYPE* outRow = out->data.DATATYPE[row]; \
+        psF32* truthRow = maxTruth->data.F32[row]; \
+        for (psS32 col = 0; col<6; col++) { \
+            if (fabsf((float)outRow[col]-(float)truthRow[col]) > FLT_EPSILON) { \
+                psError(PS_ERR_UNKNOWN, true,"psImageRebin didn't produce the proper " \
+                        "max result at (%d,%d) [%f vs %f].", \
+                        col,row,outRow[col],truthRow[col]); \
+                return 6; \
+            } \
+        } \
+    } \
+    psFree(in); \
+    psFree(out); \
+    psFree(out3); \
+    psFree(mask); \
+    psFree(meanTruth); \
+    psFree(meanTruthWMask); \
+    psFree(maxTruth);
+
+    testRebinType(F32);
+    testRebinType(F64);
+    testRebinType(U16);
+    testRebinType(S8);
+
+    // Verify the returned psImage structure is null and program execution
+    // doesn't stop, if the input image type is not supported.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error for unsupported type.");
+    in = psImageAlloc(16,16,PS_TYPE_U8);
+    mask = psImageAlloc(16,16,PS_TYPE_F32);
+    stats.options = PS_STAT_SAMPLE_MEAN;
+    out = psImageRebin(NULL,in,NULL,0,4,&stats);
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin return an image eventhough the "
+                "type is not handled.");
+        return 14;
+    }
+    // Verify the returned psImage structure is null and program execution
+    // doesn't stop, if the mask type is not U8
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error for invallid mask type.");
+    out = psImageRebin(NULL,in,mask,1,4,&stats);
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin return an image eventhough the "
+                "mask is not the correct type.");
+        return 17;
+    }
+    psFree(mask);
+    psFree(in);
+
+    // Verify the returned psImage structure is null and program execution
+    // doesn't stop, if the input parameter input is null.
+
+    out2 = psImageRebin(NULL,NULL,NULL,0,1,&stats);
+
+    if (out2 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin returned an image though the input was "
+                "NULL!?");
+        return 8;
+    }
+
+    // Verify the returned psImage structure is null and program execution
+    // doesn't stop, if the input parameter scale is less than or equal to zero.
+    in = psImageAlloc(16, 16, PS_TYPE_F32);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error for scale < 0.");
+    out2 = psImageRebin(NULL,in,NULL,0,0,&stats);
+
+    if (out2 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin returned an image though the scale was "
+                "zero!?");
+        return 9;
+    }
+
+    // Verify the returned psImage structure is null and program execution
+    // doesn't stop, if the input parameter stats is null.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error for stats null.");
+    out2 = psImageRebin(NULL,in,NULL,0,1,NULL);
+
+    if (out2 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin returned an image though the stats was "
+                "NULL!?");
+        return 10;
+    }
+
+    // Verify the returned psImage structure is null and program execution
+    // doesn't stop, if the input parameter psStats structure member options
+    // is zero or any value which doesn't correspond to a valid statistical
+    // method.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error for stats options 0.");
+    stats.options = 0;
+    out2 = psImageRebin(NULL,in,NULL,0,1,&stats);
+
+    if (out2 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin returned an image though the stats "
+                "options was zero!?");
+        return 11;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error for stat options use range.");
+    stats.options = PS_STAT_USE_RANGE;
+    out2 = psImageRebin(NULL,in,NULL,0,1,&stats);
+
+    if (out2 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin returned an image though the stats "
+                "options was PS_STAT_USE_RANGE!?");
+        return 12;
+    }
+
+    // Verify the returned psImage structure is null and program execution
+    // doesn't stop, if the input parameter psStats structure member options
+    // specifies more than one valid statistical method.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error for stats with multiple options.");
+    stats.options = PS_STAT_SAMPLE_MEAN + PS_STAT_MAX;
+    out2 = psImageRebin(NULL,in,NULL,0,1,&stats);
+
+    if (out2 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageRebin returned an image though the stats "
+                "options was PS_STAT_SAMPLE_MEAN+PS_STAT_MAX!?");
+        return 13;
+    }
+
+    psFree(in);
+
+    return 0;
+}
+
+static psS32 testImageRoll(void)
+{
+
+    psImage* in;
+    psImage* out;
+    psImage* out2;
+    psS32 rows = 64;
+    psS32 cols = 64;
+    psS32 rows1 = 8;
+    psS32 cols1 = 8;
+
+    /*
+     The function psImageRoll shall generate a new psImage structure by
+     rolling the input image the correponding number of pixels in the vertical
+     and/or horizontal direction. The image output image shall be the same size
+     as the input image. Values which roll off the image are wrapped to the
+     other side.
+
+     Verify the returned psImage structure contains expected values, if the
+     input image contains known values and the roll performed is known.
+     Cases should include no roll, vertical roll, horizontal roll and
+     combination vertical/horizontal rolls. Positive and negative rolls
+     should be performed.
+    */
+
+    in = psImageAlloc(cols,rows,PS_TYPE_F32);
+    for (psS32 row=0;row<rows;row++) {
+        psF32* inRow = in->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            inRow[col] = (psF32)row+(psF32)col/1000.0f;
+        }
+    }
+
+    out = psImageRoll(NULL,in,0,0);
+    for (psS32 row=0;row<rows;row++) {
+        psF32* inRow = in->data.F32[row];
+        psF32* outRow = out->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            if (inRow[col] != outRow[col]) {
+                psError(PS_ERR_UNKNOWN, true,"psImageRoll didn't produce expected result "
+                        "at %d,%d (%f vs %f) for dx=0, dy=0.",
+                        col,row,inRow[col],outRow[col]);
+                return 3;
+            }
+        }
+    }
+
+    out2 = psImageRoll(out,in,cols/4,0);
+    for (psS32 row=0;row<rows;row++) {
+        psF32* inRow = in->data.F32[row];
+        psF32* outRow = out->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            if (inRow[(col+cols/4) % cols] != outRow[col]) {
+                psError(PS_ERR_UNKNOWN, true,"psImageRoll didn't produce expected result "
+                        "at %d,%d (%f vs %f) for dx=cols/4, dy=0.",
+                        col,row,inRow[(col+cols/4) % cols],outRow[col]);
+                return 4;
+            }
+        }
+    }
+
+    // Verify the returned psImage structure pointer is equal to the input
+    // parameter out if provided.
+    if (out2 != out) {
+        psError(PS_ERR_UNKNOWN, true,"psImageRoll didn't recycle my out psImage!?");
+        return 1;
+    }
+
+    out = psImageRoll(out,in,0,rows/4);
+    for (psS32 row=0;row<rows;row++) {
+        psF32* inRow = in->data.F32[(row+rows/4)%rows];
+        psF32* outRow = out->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            if (inRow[col] != outRow[col]) {
+                psError(PS_ERR_UNKNOWN, true,"psImageRoll didn't produce expected result "
+                        "at %d,%d (%f vs %f) for dx=0, dy=rows/4.",
+                        col,row,inRow[col],outRow[col]);
+                return 5;
+            }
+        }
+    }
+
+    out = psImageRoll(out,in,cols/4,rows/4);
+    for (psS32 row=0;row<rows;row++) {
+        psF32* inRow = in->data.F32[(row+rows/4)%rows];
+        psF32* outRow = out->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            if (inRow[(col+cols/4) % cols] != outRow[col]) {
+                psError(PS_ERR_UNKNOWN, true,"psImageRoll didn't produce expected result "
+                        "at %d,%d (%f vs %f) for dx=cols/4, dy=rows/4.",
+                        col,row,inRow[(col+cols/4) % cols],outRow[col]);
+                return 6;
+            }
+        }
+    }
+
+    out = psImageRoll(out,in,-cols/4,0);
+    for (psS32 row=0;row<rows;row++) {
+        psF32* inRow = in->data.F32[row];
+        psF32* outRow = out->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            if (inRow[(col+(cols-cols/4)) % cols] != outRow[col]) {
+                psError(PS_ERR_UNKNOWN, true,"psImageRoll didn't produce expected result "
+                        "at %d,%d (%f vs %f) for dx=-cols/4, dy=0.",
+                        col,row,inRow[(col+(cols-cols/4)) % cols],outRow[col]);
+                return 7;
+            }
+        }
+    }
+
+    out = psImageRoll(out,in,0,-rows/4);
+    for (psS32 row=0;row<rows;row++) {
+        psF32* inRow = in->data.F32[(row+rows-rows/4)%rows];
+        psF32* outRow = out->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            if (inRow[col] != outRow[col]) {
+                psError(PS_ERR_UNKNOWN, true,"psImageRoll didn't produce expected result "
+                        "at %d,%d (%f vs %f) for dx=0, dy=-rows/4.",
+                        col,row,inRow[col],outRow[col]);
+                return 8;
+            }
+        }
+    }
+
+    out = psImageRoll(out,in,-cols/4,-rows/4);
+    for (psS32 row=0;row<rows;row++) {
+        psF32* inRow = in->data.F32[(row+rows-rows/4)%rows];
+        psF32* outRow = out->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            if (inRow[(col+cols-cols/4) % cols] != outRow[col]) {
+                psError(PS_ERR_UNKNOWN, true,"psImageRoll didn't produce expected result "
+                        "at %d,%d (%f vs %f) for dx=cols/4, dy=rows/4.",
+                        col,row,inRow[(col+cols-cols/4) % cols],outRow[col]);
+                return 9;
+            }
+        }
+    }
+
+
+    // Verify the returned psImage structure pointer is null and program
+    // execution doesn't stop, if input parameter input is null.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error.");
+    out2 = psImageRoll(NULL,NULL,0,0);
+    if (out2 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageRoll did not return NULL though input image was NULL!?");
+        return 2;
+    }
+
+    psFree(in);
+    psFree(out);
+
+    #define testRollType(DATATYPE) \
+    in = psImageAlloc(rows1,cols1,PS_TYPE_##DATATYPE); \
+    \
+    for (psS32 row=0;row<rows1;row++) { \
+        ps##DATATYPE* inRow = in->data.DATATYPE[row]; \
+        for (psS32 col=0;col<cols1;col++) { \
+            inRow[col] = (ps##DATATYPE)row+(ps##DATATYPE)col; \
+        } \
+    } \
+    \
+    out = psImageRoll(NULL,in,rows1/4,cols1/4); \
+    for (psS32 row=0;row<rows1;row++) { \
+        ps##DATATYPE* inRow = in->data.DATATYPE[(row+rows1/4)%rows1]; \
+        ps##DATATYPE* outRow = out->data.DATATYPE[row]; \
+        for (psS32 col=0;col<cols1;col++) { \
+            if (inRow[(col+cols1/4)%cols1] != outRow[col]) { \
+                psError(PS_ERR_UNKNOWN, true,"psImageRoll didn't produce expected result " \
+                        "at %d,%d (%f vs %f) for dx=0, dy=0.", \
+                        col,row,(float)inRow[col],(float)outRow[col]); \
+                return 3; \
+            } \
+        } \
+    } \
+    psFree(in); \
+    psFree(out);
+
+    testRollType(U8);
+    testRollType(U16);
+    testRollType(S8);
+    testRollType(S16);
+    testRollType(F64);
+    testRollType(C32);
+    testRollType(C64);
+
+    return 0;
+}
+
+psS32 testImageRotate(void)
+{
+    /*
+
+    This function shall calculate a new psImage structure based upon the
+    rotation of a given psImage structure. The center of rotation shall be the
+    center pixel of the input image.
+
+    The following steps of the testpoint are done manually via inspection of
+    fOut.fits & sOut.fits.
+
+        * Verify the returned psImage structure contains expected values, if
+          the input parameter psImage contains known values. Cases should
+          include rotations of 0, 45, 90, 135, 180, 225, 270, 315, 360 and at leat one
+          other arbitrary angle. Cases of the input image should include image
+          with a center pixel and an image without a center pixel.
+        * Verify the returned psImage structure contains pixels set to exposed value, if
+          the rotation and input psImage to not correspond to the output image.
+
+    */
+
+    psImage* fOut = NULL;
+    psImage* sOut = NULL;
+    psImage* fBiOut = NULL;
+    psImage* sBiOut = NULL;
+    psImage* fTruth = NULL;
+    psImage* sTruth = NULL;
+    psImage* fBiTruth = NULL;
+    psImage* sBiTruth = NULL;
+    psS32 rows = 64;
+    psS32 cols = 64;
+    psImage* fImg = psImageAlloc(cols,rows,PS_TYPE_F32);
+    psImage* sImg = psImageAlloc(cols,rows,PS_TYPE_S16);
+
+    for(psS32 row=0;row<rows;row++) {
+        psF32* fRow = fImg->data.F32[row];
+        psS16* sRow = sImg->data.S16[row];
+        for (psS32 col=0;col<cols;col++) {
+            fRow[col] = (psF32)(row)+(psF32)(col)/100.0f;
+            sRow[col] = row-2*col;
+        }
+    }
+
+    // since interpolation is involved, etc., the simplist way to verify things
+    // is to verify the results manually and bless it for automated comparison
+    // thereafter
+
+
+    // write results of various rotates to a file and verify with truth images
+    mkdir("temp",0777);
+    psS32 index = 0;
+    psBool fail = false;
+    psF32 radianRot;
+
+    psFits* fOutFile = psFitsOpen("fOut.fits","w");
+    psFits* sOutFile = psFitsOpen("sOut.fits","w");
+    psFits* fBiOutFile = psFitsOpen("fBiOut.fits","w");
+    psFits* sBiOutFile = psFitsOpen("sBiOut.fits","w");
+    if (fOutFile == NULL ||sOutFile == NULL || fBiOutFile == NULL || sBiOutFile == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "Can not create output files, so why continue!?");
+        return 1;
+    }
+
+    psFits* fTruthFile = psFitsOpen(VERIFIED_DIR "/fOut.fits","r");
+    psFits* sTruthFile = psFitsOpen(VERIFIED_DIR "/sOut.fits","r");
+    psFits* fBiTruthFile = psFitsOpen(VERIFIED_DIR "/fBiOut.fits","r");
+    psFits* sBiTruthFile = psFitsOpen(VERIFIED_DIR "/sBiOut.fits","r");
+    if (fTruthFile == NULL ||sTruthFile == NULL || fBiTruthFile == NULL || sBiTruthFile == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "Can not open truth files, so why continue!?");
+        return 1;
+    }
+
+    psRegion regionAll = psRegionSet(0,0,0,0);
+    for (psS32 rot=-180;rot<=180;rot+=45) {
+        psImage* oldOut = fOut;
+        psImage* oldBiOut = fBiOut;
+        if (rot == 90) {
+            radianRot = M_PI_2;
+        } else if (rot == -90) {
+            radianRot = M_PI+M_PI_2;
+        } else if (rot == 180 || rot == -180) {
+            radianRot = M_PI;
+        } else {
+            radianRot = ((float)rot)*M_PI/180.0;
+        }
+
+        fOut = psImageRotate(fOut,fImg,radianRot,-1.0,PS_INTERPOLATE_FLAT);
+        fBiOut = psImageRotate(fBiOut,fImg,radianRot,-1.0,PS_INTERPOLATE_BILINEAR);
+        // Verify the returned psImage structure is equal to the input
+        // parameter out if provided.
+        if (oldOut != NULL && oldOut != fOut) {
+            psError(PS_ERR_UNKNOWN, true,"the output recycle functionality failed");
+            return 2;
+        }
+        if (oldBiOut != NULL && oldBiOut != fBiOut) {
+            psError(PS_ERR_UNKNOWN, true,"the output recycle functionality failed");
+            return 4;
+        }
+        sOut = psImageRotate(sOut,sImg,radianRot,-1.0,PS_INTERPOLATE_FLAT);
+        sBiOut = psImageRotate(sBiOut,sImg,radianRot,-1.0,PS_INTERPOLATE_BILINEAR);
+
+        if (! psFitsWriteImage(fOutFile, NULL, fOut, 1, NULL) ) {
+            psError(PS_ERR_UNKNOWN, true,"Can not write fOut.");
+            return 20;
+        }
+        if (! psFitsWriteImage(sOutFile, NULL, sOut, 1, NULL) ) {
+            psError(PS_ERR_UNKNOWN, true,"Can not write sOut.");
+            return 21;
+        }
+        if (! psFitsWriteImage(fBiOutFile, NULL, fBiOut, 1, NULL) ) {
+            psError(PS_ERR_UNKNOWN, true,"Can not write fBiOut.fits.");
+            return 40;
+        }
+        if (! psFitsWriteImage(sBiOutFile, NULL, sBiOut, 1, NULL) ) {
+            psError(PS_ERR_UNKNOWN, true,"Can not write sBiOut.fits.");
+            return 41;
+        }
+
+        // now, let's compare this with the verified file
+        psFitsMoveExtNum(fTruthFile, index, false);
+        psFitsMoveExtNum(sTruthFile, index, false);
+        psFitsMoveExtNum(fBiTruthFile, index, false);
+        psFitsMoveExtNum(sBiTruthFile, index, false);
+        psFree(fTruth);
+        psFree(sTruth);
+        psFree(fBiTruth);
+        psFree(sBiTruth);
+        fTruth = NULL;
+        sTruth = NULL;
+        fBiTruth = NULL;
+        sBiTruth = NULL;
+        fTruth = psFitsReadImage(fTruthFile, regionAll, 0);
+        sTruth = psFitsReadImage(sTruthFile, regionAll, 0);
+        fBiTruth = psFitsReadImage(fBiTruthFile, regionAll, 0);
+        sBiTruth = psFitsReadImage(sBiTruthFile, regionAll, 0);
+        if (fTruth == NULL) {
+            psError(PS_ERR_UNKNOWN, true,"verified psF32 image failed to be read (%d deg. rotation)",
+                    rot);
+            fail = true;
+        } else {
+            if (fTruth->numRows != fOut->numRows || fTruth->numCols != fOut->numCols) {
+                psError(PS_ERR_UNKNOWN, true,"Rotated float image size did not match truth "
+                        "image for %d deg rotation (%dx%d vs %dx%d).",
+                        rot,fOut->numCols,fOut->numRows,fTruth->numCols,fTruth->numRows);
+                fail = true;
+            } else {
+                for (psS32 row=0;row<fTruth->numRows;row++) {
+                    psF32* truthRow = fTruth->data.F32[row];
+                    psF32* outRow = fOut->data.F32[row];
+                    for (psS32 col=0;col<fTruth->numCols;col++) {
+                        if (fabsf(truthRow[col]-outRow[col]) > 1) {
+                            psError(PS_ERR_UNKNOWN, true,"Float Image mismatch (%f vs %f) at %d,%d.",
+                                    outRow[col], truthRow[col],col,row);
+                            fail = true;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (sTruth == NULL) {
+            psError(PS_ERR_UNKNOWN, true,"verified psS16 image failed to be read "
+                    "(%d deg. rotation)",rot);
+            fail = true;
+        } else {
+            if (sTruth->numRows != sOut->numRows ||
+                    sTruth->numCols != sOut->numCols) {
+                psError(PS_ERR_UNKNOWN, true,"Rotated psS16 image size did not match truth "
+                        "image for %d deg rotation.",rot);
+                fail = true;
+            } else {
+                for (psS32 row=0;row<sTruth->numRows;row++) {
+                    psS16* truthRow = sTruth->data.S16[row];
+                    psS16* outRow = sOut->data.S16[row];
+                    for (psS32 col=0;col<sTruth->numCols;col++) {
+                        if (fabsf(truthRow[col]-outRow[col]) > 1) {
+                            psError(PS_ERR_UNKNOWN, true,"Short Image mismatch (%d vs %d) "
+                                    "at %d,%d.",
+                                    outRow[col], truthRow[col],col,row);
+                            fail = true;
+                        }
+                    }
+                }
+            }
+        }
+
+
+        if (fBiTruth == NULL) {
+            psError(PS_ERR_UNKNOWN, true,"verified psF32 Bi image failed to be read (%d deg. rotation)",
+                    rot);
+            fail = true;
+        } else {
+            if (fBiTruth->numRows != fBiOut->numRows || fBiTruth->numCols != fBiOut->numCols) {
+                psError(PS_ERR_UNKNOWN, true,"Rotated float image size did not match truth "
+                        "image for %d deg rotation (%dx%d vs %dx%d). BILINEAR",
+                        rot,fBiOut->numCols,fBiOut->numRows,fBiTruth->numCols,fBiTruth->numRows);
+                fail = true;
+            } else {
+                for (psS32 row=0;row<fBiTruth->numRows;row++) {
+                    psF32* truthRow = fBiTruth->data.F32[row];
+                    psF32* outRow = fBiOut->data.F32[row];
+                    for (psS32 col=0;col<fBiTruth->numCols;col++) {
+                        if (fabsf(truthRow[col]-outRow[col]) > 1) {
+                            psError(PS_ERR_UNKNOWN, true,"Float Image mismatch (%f vs %f) at %d,%d. BILINEAR",
+                                    outRow[col], truthRow[col],col,row);
+                            fail = true;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (sBiTruth == NULL) {
+            psError(PS_ERR_UNKNOWN, true,"verified psS16 image failed to be read "
+                    "(%d deg. rotation) BILINEAR",rot);
+            fail = true;
+        } else {
+            if (sBiTruth->numRows != sBiOut->numRows ||
+                    sBiTruth->numCols != sBiOut->numCols) {
+                psError(PS_ERR_UNKNOWN, true,"Rotated psS16 image size did not match truth "
+                        "image for %d deg rotation. BILINEAR",rot);
+                fail = true;
+            } else {
+                for (psS32 row=0;row<sBiTruth->numRows;row++) {
+                    psS16* truthRow = sBiTruth->data.S16[row];
+                    psS16* outRow = sBiOut->data.S16[row];
+                    for (psS32 col=0;col<sBiTruth->numCols;col++) {
+                        if (fabsf(truthRow[col]-outRow[col]) > 1) {
+                            psError(PS_ERR_UNKNOWN, true,"Short Image mismatch (%d vs %d) "
+                                    "at %d,%d. BILINEAR",
+                                    outRow[col], truthRow[col],col,row);
+                            fail = true;
+                        }
+                    }
+                }
+            }
+        }
+
+        index++;
+    }
+
+    if (fail) {
+        psError(PS_ERR_UNKNOWN, true,"One or more images didn't match truth or truth did "
+                "not exist.");
+        return 10;
+    }
+
+
+    // Verify the returned psImage structure pointer is null and program
+    // execution doesn't stop, if the input parameter input is null.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
+    fOut = psImageRotate(fOut,NULL,0,0,PS_INTERPOLATE_FLAT);
+    if (fOut != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"NULL wasn't returned though the input image was NULL.");
+        return 3;
+    }
+
+    // Verify the returned psImage structure pointer is null and program
+    // execution doesn't stop, if the specified interpolation mode is invalid
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error for invalid "
+             "interpolation type.");
+    fOut = psImageRotate(fOut, fImg, 33, 0, -1);
+    if (fOut != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"NULL wasn't returned though the interpolation mode "
+                "is invalid.");
+        return 4;
+    }
+
+    psFree(sOut);
+    psFree(fImg);
+    psFree(sImg);
+    psFree(fTruth);
+    psFree(sTruth);
+    psFree(sBiOut);
+    psFree(fBiTruth);
+    psFree(sBiTruth);
+    psFree(fBiOut);
+
+    psFree(fOutFile);
+    psFree(sOutFile);
+    psFree(fBiOutFile);
+    psFree(sBiOutFile);
+
+    psFree(fTruthFile);
+    psFree(sTruthFile);
+    psFree(fBiTruthFile);
+    psFree(sBiTruthFile);
+
+    return 0;
+}
+
+static psS32 testImageShift(void)
+{
+    /* psImageShift:
+
+       This functions shall generate a new psImage structure by shifting the
+       input psImage structure a specified number of pixels in the horizontal
+       and/or vertical directions.
+
+       Verify the returned psImage structure contains expected values, if the
+       input psImage structure contains known values and a know shift in the
+       vertical and/or horizontal directions. Cases should include no shift,
+       vertical only(up,down), horizontal only(right, left), and combination
+       shift. Cases should include fractional shifts. Comparison of expected
+       values should include a delta to allow for testing on different
+       platforms.
+
+       Verify the returned psImage structure contains values for pixels not in
+       the original image set to the input parameter exposed.
+
+    */
+
+    psS32 retVal=0;
+
+    // integer shift
+    retVal |= testImageShiftCase(64,128,0.0f,0.0f);
+    retVal |= testImageShiftCase(64,128,0.0f,16.0f);
+    retVal |= testImageShiftCase(64,128,0.0f,-16.0f);
+    retVal |= testImageShiftCase(64,128,32.0f,0.0f);
+    retVal |= testImageShiftCase(64,128,-32.0f,0.0f);
+    retVal |= testImageShiftCase(64,128,32.0f,16.0f);
+    retVal |= testImageShiftCase(64,128,32.0f,-16.0f);
+    retVal |= testImageShiftCase(64,128,-32.0f,16.0f);
+    retVal |= testImageShiftCase(64,128,-32.0f,-16.0f);
+
+    if (retVal != 0) {
+        return retVal;
+    }
+
+    // fractional shift
+    retVal |= testImageShiftCase(64,128,0.0f,16.4f);
+    retVal |= testImageShiftCase(64,128,0.0f,-16.4f);
+    retVal |= testImageShiftCase(64,128,32.7f,0.0f);
+    retVal |= testImageShiftCase(64,128,-32.7f,0.0f);
+    retVal |= testImageShiftCase(64,128,32.6f,16.2f);
+    retVal |= testImageShiftCase(64,128,32.6f,-16.2f);
+    retVal |= testImageShiftCase(64,128,-32.6f,16.2f);
+    retVal |= testImageShiftCase(64,128,-32.6f,-16.2f);
+
+    if (retVal != 0) {
+        return retVal;
+    }
+
+    /*
+       Verify the returned psImage structure pointer is equal to the input
+       parameter out if provided.
+    */
+    psImage* fImg = psImageAlloc(32,32,PS_TYPE_F32);
+    psImage* fRecycle = psImageAlloc(32,32,PS_TYPE_F32);
+    psImage* fOut = psImageShift(fRecycle, fImg, 8,8, NAN, PS_INTERPOLATE_FLAT);
+
+    if (fRecycle != fOut) {
+        psError(PS_ERR_UNKNOWN, true,"psImageShift didn't recycle my image?");
+        return 10;
+    }
+
+    /*
+       Verify the returned psImage structure pointer is null and program
+       execution doesn't stop, if the input psImage structure pointer is null.
+    */
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error...");
+    fOut = psImageShift(fOut,NULL,8,8,NAN,PS_INTERPOLATE_FLAT);
+    if (fOut != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageShift didn't return NULL given a NULL input image.");
+        return 11;
+    }
+
+    // Verify the returned psImage structure is null and program execution
+    // doesn't stop, if the specified interpolation mode is invalid.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error for invalid interpolation mode.");
+    fOut = psImageShift(fOut,fImg,8,8,NAN,-1);
+    if (fOut != NULL ) {
+        psError(PS_ERR_UNKNOWN, true,"psImageShift didn't return NULL given an invalid interpolation mode.");
+        return 12;
+    }
+
+    psFree(fImg);
+
+    return 0;
+}
+
+static psS32 testImageShiftCase(psS32 cols,
+                                psS32 rows,
+                                float colShift,
+                                float rowShift)
+{
+    psImage* fOut = NULL;
+    psImage* sOut = NULL;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Testing psImageShift with a %dx%d image for "
+             "a shift of %g,%g.",cols,rows,colShift,rowShift);
+
+    psImage* fImg = psImageAlloc(cols,rows,PS_TYPE_F32);
+    psImage* sImg = psImageAlloc(cols,rows,PS_TYPE_S16);
+    psImage* fBiOut = psImageAlloc(cols,rows,PS_TYPE_F32);
+    psImage* sBiOut = psImageAlloc(cols,rows,PS_TYPE_S16);
+
+    for(psS32 row=0;row<rows;row++) {
+        psF32* fRow = fImg->data.F32[row];
+        psS16* sRow = sImg->data.S16[row];
+        for (psS32 col=0;col<cols;col++) {
+            fRow[col] = (psF32)(row)+(psF32)(col)/100.0f;
+            sRow[col] = row-2*col;
+        }
+    }
+
+    fOut = psImageShift(fOut, fImg, colShift, rowShift, NAN, PS_INTERPOLATE_FLAT);
+    sOut = psImageShift(sOut, sImg, colShift, rowShift, -1, PS_INTERPOLATE_FLAT);
+    fBiOut = psImageShift(fBiOut, fImg, colShift, rowShift, NAN, PS_INTERPOLATE_BILINEAR);
+    sBiOut = psImageShift(sBiOut, sImg, colShift, rowShift, -1, PS_INTERPOLATE_BILINEAR);
+
+    for(psS32 row=0;row<rows;row++) {
+        psF32* fRow = fOut->data.F32[row];
+        psS16* sRow = sOut->data.S16[row];
+        psF32* fBiRow = fBiOut->data.F32[row];
+        psS16* sBiRow = sBiOut->data.S16[row];
+
+        for (psS32 col=0;col<cols;col++) {
+            psF32 fValue = psImagePixelInterpolate(fImg,col+colShift,
+                                                   row+rowShift,NULL,0,NAN,PS_INTERPOLATE_FLAT);
+            psS16 sValue = (psS16)psImagePixelInterpolate(sImg,col+colShift,
+                           row+rowShift,NULL,0,-1,PS_INTERPOLATE_FLAT);
+
+            psF32 fBiValue = psImagePixelInterpolate(fImg,col+colShift,
+                             row+rowShift,NULL,0,NAN,PS_INTERPOLATE_BILINEAR);
+            psS16 sBiValue = (psS16)psImagePixelInterpolate(sImg,col+colShift,
+                             row+rowShift,NULL,0,-1,PS_INTERPOLATE_BILINEAR);
+
+            if (fabsf(fRow[col] - fValue) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,"Float image not shifted correctly at %d,%d (%g vs %g)",
+                        col,row,fRow[col],fValue);
+                return 1;
+            }
+            if (sRow[col] != sValue) {
+                psError(PS_ERR_UNKNOWN, true,"Short image not shifted correctly at %d,%d (%d vs %d)",
+                        col,row,sRow[col],sValue);
+                return 2;
+            }
+            if (fabsf(fBiRow[col] - fBiValue) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN, true,"Float image not shifted correctly at %d,%d (%g vs %g)",
+                        col,row,fBiRow[col],fBiValue);
+                return 1;
+            }
+            if (sBiRow[col] != sBiValue) {
+                psError(PS_ERR_UNKNOWN, true,"Short image not shifted correctly at %d,%d (%d vs %d)",
+                        col,row,sBiRow[col],sBiValue);
+                return 2;
+            }
+        }
+    }
+
+    psFree(fImg);
+    psFree(sImg);
+    psFree(fOut);
+    psFree(sOut);
+    psFree(fBiOut);
+    psFree(sBiOut);
+
+    return 0;
+}
+
+static psS32 testImageResample(void)
+{
+
+    psS32 rows = 60;
+    psS32 cols = 80;
+    psImage* result = NULL;
+    psS32 scale = 4;
+    psErr* err;
+
+    psImage* image = psImageAlloc(cols,rows,PS_TYPE_F32);
+    for(psS32 row=0;row<rows;row++) {
+        psF32* imageRow = image->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            imageRow[col] = row+2*col;
+        }
+    }
+    result = psImageCopy(NULL,image,PS_TYPE_F64);
+    psImage* orig = result;
+    result = psImageResample(result,image,scale,PS_INTERPOLATE_FLAT);
+
+    if (result == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "NULL return unexpected");
+        return 1;
+    }
+
+    if (result != orig) {
+        psLogMsg(__func__,PS_LOG_ERROR,"failure to recycle image.");
+        return 2;
+    }
+
+    if (result->type.type != PS_TYPE_F32) {
+        psLogMsg(__func__,PS_LOG_ERROR,"unexpected type");
+        return 3;
+    }
+
+
+    if (result->numCols != image->numCols*scale ||
+            result->numRows != image->numRows*scale) {
+        psLogMsg(__func__,PS_LOG_ERROR,"The size of the result is %dx%d, but %dx%d was expected.",
+                 result->numCols,result->numRows,
+                 image->numCols*scale, image->numRows*scale);
+        return 4;
+    }
+
+    psF32 truthValue;
+    for(psS32 row=0;row<result->numRows;row++) {
+        for (psS32 col=0;col<result->numCols;col++) {
+            truthValue = psImagePixelInterpolate(image,
+                                                 (float)col/(float)scale,(float)row/(float)scale,
+                                                 NULL,0,-1,PS_INTERPOLATE_FLAT);
+            if (fabs(truthValue - result->data.F32[row][col]) > FLT_EPSILON) {
+                psLogMsg(__func__,PS_LOG_ERROR,"value bad at (%d,%d).  Got %g, expected %g.",
+                         col,row,result->data.F32[row][col], truthValue);
+                return 5;
+            }
+        }
+    }
+
+    // verify that image=null is handled properly.
+    psErrorClear();
+    result = psImageResample(result,NULL,scale,PS_INTERPOLATE_FLAT);
+
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "return was not NULL, as expected.");
+        return 6;
+    }
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "error message was not appropriate type.");
+        return 7;
+    }
+    psFree(err);
+
+    // verify that scale < 1 is handled properly
+    psErrorClear();
+    result = psImageResample(result,image,0,PS_INTERPOLATE_FLAT);
+
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "return was not NULL, as expected.");
+        return 8;
+    }
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "error message was not appropriate type.");
+        return 9;
+    }
+    psFree(err);
+
+    // verify that invalid interpolation mode is handled properly
+    psErrorClear();
+    result = psImageResample(result,image,2,-1);
+
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "return was not NULL, as expected.");
+        return 10;
+    }
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "error message was not appropriate type.");
+        return 11;
+    }
+    psFree(err);
+
+    // Verify that that an invalid image type is handled properly
+    psErrorClear();
+    psImage* invImage = psImageAlloc(cols,rows,PS_TYPE_BOOL);
+    memset(invImage->p_rawDataBuffer,0,cols*rows*PSELEMTYPE_SIZEOF(PS_TYPE_BOOL)); // make sure the image is of all NULLs
+    result = psImageResample(result,invImage,2,PS_INTERPOLATE_FLAT);
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"return was not NULL, as expected.");
+        return 20;
+    }
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_TYPE) {
+        psLogMsg(__func__,PS_LOG_ERROR,"error message was not appropriate type.");
+        return 21;
+    }
+    psFree(err);
+
+    psFree(image);
+    psFree(result);
+    psFree(invImage);
+
+    return 0;
+}
+
+static psS32 testImageTransform(void)
+{
+    int cols = 16;
+    int rows = 32;
+
+    psPlaneTransform* trans = psPlaneTransformAlloc(2,2);
+    trans->x->coeff[1][0] = 0.5;
+    trans->y->coeff[0][1] = 1.5;
+
+    psImage* in = psImageAlloc(cols,rows,PS_TYPE_F32);
+    for (psS32 row=0;row<rows;row++) {
+        psF32* inRow = in->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            inRow[col] = (psF32)row+(psF32)col/1000.0f;
+        }
+    }
+    in->col0 = 1;
+    psImage* out = psImageTransform(NULL,
+                                    NULL,
+                                    in,
+                                    NULL,
+                                    0,
+                                    trans,
+                                    psRegionSet(1,1+cols*2,0,rows*2),
+                                    NULL,
+                                    PS_INTERPOLATE_FLAT,
+                                    -1);
+
+    if (out == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "out == NULL");
+        return 1;
+    }
+    if (out->type.type != PS_TYPE_F32) {
+        psError(PS_ERR_UNKNOWN, false,
+                "out->type.type != PS_TYPE_F32, out->type.type == %d",
+                out->type.type);
+        return 2;
+    }
+    if (out->numRows != rows*2 || out->numCols != cols*2) {
+        psError(PS_ERR_UNKNOWN, false,
+                "out size is %dx%d, not %dx%d",
+                out->numCols, out->numRows, cols*2, rows);
+        return 3;
+    }
+
+    for (psS32 row=0;row<out->numRows;row++) {
+        psF32* outRow = out->data.F32[row];
+        for (psS32 col=0;col<cols;col++) {
+            float inValue = p_psImagePixelInterpolateFLAT_F32(in,
+                            col*trans->x->coeff[1][0]+trans->x->coeff[0][0]+1,
+                            row*trans->y->coeff[0][1]+trans->y->coeff[0][0],
+                            NULL, 0,
+                            -1);
+            if (fabsf(outRow[col] - inValue) > 0.01) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "out at %d,%d was %g, expected %g",
+                        col,row,outRow[col], inValue);
+                return 4;
+            }
+        }
+    }
+
+    psFree(out);
+    psFree(in);
+    psFree(trans);
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageInterpolate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageInterpolate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageInterpolate.c	(revision 22322)
@@ -0,0 +1,282 @@
+/** @file tst_psImageInterpolate.c
+ *
+ * @brief Contains the tests for psImagePixelInterpolate
+ *
+ * @author Eric Van Alst, MHPCC
+ *
+ * @version $Revision: 1.1 $
+ *          $Name: not supported by cvs2svn $
+ * @date $Date: 2005-07-13 02:47:00 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+#define GENIMAGE(img,c,r,TYP,valueFcn) \
+img = psImageAlloc(c,r,PS_TYPE_##TYP); \
+for(psU32 row=0; row<r; row++) { \
+    ps##TYP* imgRow = img->data.TYP[row]; \
+    for(psU32 col=0; col<c; col++) { \
+        imgRow[col] = (ps##TYP)(valueFcn); \
+    } \
+}
+
+#define CHECK_INTERP_VALUE(img,x,y,mask,maskval,exposed,TYPE,expected) \
+val = (psF32)psImagePixelInterpolate(img,x,y,mask,maskval,exposed,PS_INTERPOLATE_##TYPE); \
+printf("returned = %.2f    expected = %.2f\n",val,expected); \
+if(fabsf(val-expected)>FLT_EPSILON) { \
+    psError(PS_ERR_UNKNOWN,true,"Return value is not as expected."); \
+    return 1; \
+}
+
+#define CHECK_INTERP_BY_TYPE(TYPE) \
+GENIMAGE(img1,10,10,TYPE,row+col) \
+CHECK_INTERP_VALUE(img1,1.9,1.6,NULL,0,0,FLAT,2.0) \
+CHECK_INTERP_VALUE(img1,4.0,2.0,NULL,0,0,BILINEAR,5.0) \
+psFree(img1);
+
+static psS32 testInterpolateFlatBilinear(void);
+static psS32 testInterpolateError(void);
+static psS32 testInterpolateMaskFlatBilinear(void);
+static psS32 testInterpolate1D(void);
+
+testDescription tests[] = {
+                              {testInterpolateFlatBilinear,999,"psImagePixelInterpolate",0,false},
+                              {testInterpolateError,999,"psImagePixelInterpolate",0,false},
+                              {testInterpolateMaskFlatBilinear,999,"psImagePixelInterpolate",0,false},
+                              {testInterpolate1D,999,"psImagePixelInterpolate",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    if ( !runTestSuite(stderr,"psImagePixelInterpolate",tests,argc,argv) ) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+psS32 testInterpolateFlatBilinear(void)
+{
+    psImage* img1;
+    psF32 val = 0;
+
+    // Perform simple(four neighbor) FLAT interpolation for all types
+    // Perform bilinear interpolation for all types
+    CHECK_INTERP_BY_TYPE(S8)
+    CHECK_INTERP_BY_TYPE(S16)
+    CHECK_INTERP_BY_TYPE(S32)
+    CHECK_INTERP_BY_TYPE(S64)
+    CHECK_INTERP_BY_TYPE(U8)
+    CHECK_INTERP_BY_TYPE(U16)
+    CHECK_INTERP_BY_TYPE(U32)
+    CHECK_INTERP_BY_TYPE(U64)
+    CHECK_INTERP_BY_TYPE(F32)
+    CHECK_INTERP_BY_TYPE(F64)
+    CHECK_INTERP_BY_TYPE(C32)
+    CHECK_INTERP_BY_TYPE(C64)
+
+    return 0;
+}
+
+psS32 testInterpolateError(void)
+{
+    psF32 val = 0;
+    psImage* img1 = NULL;
+
+    // Perform interpolation with NULL input image and verify return and error message
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    val = psImagePixelInterpolate(img1,1.2,1.2,NULL,0,5.0,PS_INTERPOLATE_FLAT);
+    if(val != 5.0) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return the unexposed value");
+        return 10;
+    }
+
+    // Perform interpolation with invalid input image type
+    img1 = psImageAlloc(10,10,PS_TYPE_BOOL);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    val = psImagePixelInterpolate(img1,1.2,1.2,NULL,0,10.0,PS_INTERPOLATE_FLAT);
+    if(val != 10.0) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return the unexposed value");
+        return 11;
+    }
+
+    // Perform interpolation with invalid method
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    val = psImagePixelInterpolate(img1,1.2,1.2,NULL,0,10.0,PS_INTERPOLATE_BILINEAR+1);
+    if(val != 10.0) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return the unexposed value");
+        return 12;
+    }
+
+    psFree(img1);
+
+    return 0;
+}
+
+// The following assumptions about the test image are:
+//
+//                     col
+//             0   1   2   3   4   5   6   7  ...
+//
+// row 0       0   1   2   3   4   5   6   7
+// row 1       1   2   3   4   5   6   7   8
+// row 2       2   3   4   5   6   7   8   9
+// ...
+
+psS32 testInterpolateMaskFlatBilinear(void)
+{
+    psImage* img1 = NULL;
+    psImage* msk1 = NULL;
+    psF32 val = 0;
+
+    // Perform interpolate with mask and mask value set at nearest pixel to verify
+    // the unexposed value is return
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][1] = 1;
+    CHECK_INTERP_VALUE(img1,1.9,1.6,msk1,1,10.0,FLAT,10.0)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask and one mask value for bilinear (upper left pixel)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][3] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.25)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask and one mask value for bilinear (upper right pixel)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][4] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,4.75)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask and one mask value for bilinear (lower left pixel)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[2][3] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.25)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask and one mask value for bilinear (lower right pixel)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[2][4] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,4.75)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask and one mask value for bilinear (upper pixels)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][3] = 1;
+    msk1->data.U8[1][4] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.5)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask and one mask value for bilinear (lower pixels)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[2][3] = 1;
+    msk1->data.U8[2][4] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,4.5)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask and one mask value for bilinear (left pixels)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][3] = 1;
+    msk1->data.U8[2][3] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.5)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask and one mask value for bilinear (right pixels)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][4] = 1;
+    msk1->data.U8[2][4] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,4.5)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask only one valid pixel (upper left)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][4] = 1;
+    msk1->data.U8[2][3] = 1;
+    msk1->data.U8[2][4] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,4.0)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask only one valid pixel (upper right)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][3] = 1;
+    msk1->data.U8[2][3] = 1;
+    msk1->data.U8[2][4] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.0)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask only one valid pixel (lower left)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][3] = 1;
+    msk1->data.U8[1][4] = 1;
+    msk1->data.U8[2][4] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,5.0)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask only one valid pixel (lower right)
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][3] = 1;
+    msk1->data.U8[1][4] = 1;
+    msk1->data.U8[2][3] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,6.0)
+    psFree(msk1);
+    psFree(img1);
+
+    // Perform interpolate with mask no valid pixels
+    GENIMAGE(img1,10,10,F32,row+col)
+    GENIMAGE(msk1,10,10,U8,0)
+    msk1->data.U8[1][3] = 1;
+    msk1->data.U8[1][4] = 1;
+    msk1->data.U8[2][3] = 1;
+    msk1->data.U8[2][4] = 1;
+    CHECK_INTERP_VALUE(img1,4.0,2.0,msk1,1,0,BILINEAR,0.0)
+    psFree(msk1);
+    psFree(img1);
+    return 0;
+}
+
+// Perform interpolation for a 1D image
+psS32 testInterpolate1D(void)
+{
+    psImage* img1 = NULL;
+    psF32 val = 0;
+
+    GENIMAGE(img1,10,1,F32,row+col)
+    CHECK_INTERP_VALUE(img1,4.0,0.0,NULL,0,100.0,BILINEAR,3.5)
+
+    psFree(img1);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageMaskOps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageMaskOps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageMaskOps.c	(revision 22322)
@@ -0,0 +1,222 @@
+/** @file  tst_psImageMaskOps.c
+ *
+ *  @brief Contains the tests for psMaskOps.[ch]
+ *
+ *
+ *  @author David Robbins, MHPCC
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-08 03:51:20 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <complex.h>
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string.h>                    // for memset
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "psType.h"
+
+static psS32 testImageKeepMask(void);
+static psS32 testImageGrowMask(void);
+
+testDescription tests[] = {
+                              {testImageKeepMask,0,"psImageKeep and Mask",0,false},
+                              {testImageGrowMask,0,"psImageGrowMask",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    return ! runTestSuite(stderr,"psImage",tests,argc,argv);
+}
+
+psS32 testImageKeepMask(void)
+{
+    //psImageMaskRegion
+    //psImageKeepRegion
+    //psImageMaskCircle
+    //psImageKeepCircle
+    psImage *in = psImageAlloc(3,3,PS_TYPE_MASK);
+    psRegion reg;
+    reg.x0 = 0;
+    reg.x1 = 1;
+    reg.y0 = 0;
+    reg.y1 = 1;
+    psMaskType mask = 2;
+    in->data.PS_TYPE_MASK_DATA[0][0] = 2;
+    in->data.PS_TYPE_MASK_DATA[0][1] = 0;
+    in->data.PS_TYPE_MASK_DATA[0][2] = 1;
+    in->data.PS_TYPE_MASK_DATA[1][0] = 4;
+    in->data.PS_TYPE_MASK_DATA[1][1] = 0;
+    in->data.PS_TYPE_MASK_DATA[1][2] = 3;
+    in->data.PS_TYPE_MASK_DATA[2][0] = 2;
+    in->data.PS_TYPE_MASK_DATA[2][1] = 1;
+    in->data.PS_TYPE_MASK_DATA[2][2] = 2;
+
+    printf("\n Mask Region------");
+    psImageMaskRegion(in, reg, "|", mask);
+    for(int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            printf("\nin->data.u8 [i][j] i=%d, j=%d = %u", i, j, in->data.PS_TYPE_MASK_DATA[i][j]);
+        }
+    }
+
+    in->data.PS_TYPE_MASK_DATA[0][0] = 2;
+    in->data.PS_TYPE_MASK_DATA[0][1] = 0;
+    in->data.PS_TYPE_MASK_DATA[0][2] = 1;
+    in->data.PS_TYPE_MASK_DATA[1][0] = 4;
+    in->data.PS_TYPE_MASK_DATA[1][1] = 0;
+    in->data.PS_TYPE_MASK_DATA[1][2] = 3;
+    in->data.PS_TYPE_MASK_DATA[2][0] = 2;
+    in->data.PS_TYPE_MASK_DATA[2][1] = 1;
+    in->data.PS_TYPE_MASK_DATA[2][2] = 2;
+    psImageKeepRegion(in, reg, "AND", mask);
+    printf("\n Keep Region------");
+    for(int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            printf("\nin->data.u8 [i][j] i=%d, j=%d = %u", i, j, in->data.PS_TYPE_MASK_DATA[i][j]);
+        }
+    }
+
+    //Mask Circle and Keep Circle Functions
+    in->data.PS_TYPE_MASK_DATA[0][0] = 2;
+    in->data.PS_TYPE_MASK_DATA[0][1] = 0;
+    in->data.PS_TYPE_MASK_DATA[0][2] = 1;
+    in->data.PS_TYPE_MASK_DATA[1][0] = 4;
+    in->data.PS_TYPE_MASK_DATA[1][1] = 0;
+    in->data.PS_TYPE_MASK_DATA[1][2] = 3;
+    in->data.PS_TYPE_MASK_DATA[2][0] = 2;
+    in->data.PS_TYPE_MASK_DATA[2][1] = 1;
+    in->data.PS_TYPE_MASK_DATA[2][2] = 2;
+    psImageMaskCircle(in, 1, 1, 1, "XOR", mask);
+    printf("\n Mask Circle------");
+    for(int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            printf("\nin->data.u8 [i][j] i=%d, j=%d = %u", i, j, in->data.PS_TYPE_MASK_DATA[i][j]);
+        }
+    }
+
+    in->data.PS_TYPE_MASK_DATA[0][0] = 2;
+    in->data.PS_TYPE_MASK_DATA[0][1] = 0;
+    in->data.PS_TYPE_MASK_DATA[0][2] = 1;
+    in->data.PS_TYPE_MASK_DATA[1][0] = 4;
+    in->data.PS_TYPE_MASK_DATA[1][1] = 0;
+    in->data.PS_TYPE_MASK_DATA[1][2] = 3;
+    in->data.PS_TYPE_MASK_DATA[2][0] = 2;
+    in->data.PS_TYPE_MASK_DATA[2][1] = 1;
+    in->data.PS_TYPE_MASK_DATA[2][2] = 2;
+    psImageKeepCircle(in, 1, 1, 1, "=", mask);
+    printf("\n Keep Circle------");
+    for(int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            printf("\nin->data.u8 [i][j] i=%d, j=%d = %u", i, j, in->data.PS_TYPE_MASK_DATA[i][j]);
+        }
+    }
+    fflush(stdout);
+
+    //Error Checks
+    //incorrect logical operation
+    printf("\n");
+    psImageKeepRegion(in, reg, "+", mask);
+    //null image
+    psImage *none = NULL;
+    psImageMaskCircle(none, 1, 1, 1, "&", mask);
+
+    psFree(in);
+    return 0;
+}
+
+psS32 testImageGrowMask(void)
+{
+    psImage *in = NULL;
+    psImage *out = NULL;
+    psImage *test = NULL;
+    psMaskType maskVal = 0;
+    psMaskType growVal = 0;
+    unsigned int growSize = 0;
+    //return null for null input image
+    out = psImageGrowMask(out, in, maskVal, growSize, growVal);
+    if (out != NULL) {
+        fprintf(stderr,
+                "psImageGrowMask failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    in = psImageAlloc(5, 5, PS_TYPE_MASK);
+    //return null for incompatible image size
+    test = psImageAlloc(2, 2, PS_TYPE_MASK);
+    out = psImageGrowMask(test, in, maskVal, growSize, growVal);
+    if (out != NULL) {
+        fprintf(stderr,
+                "psImageGrowMask failed to return NULL for incompatible image size.\n");
+        return 2;
+    }
+    //return null for incompatible out image type
+    test = psImageRecycle(test, 5, 5, PS_TYPE_F32);
+    out = psImageGrowMask(test, in, maskVal, growSize, growVal);
+    if (out != NULL) {
+        fprintf(stderr,
+                "psImageGrowMask failed to return NULL for incompatible output image type.\n");
+        return 3;
+    }
+    //return NULL for input image that doesn't match PS_TYPE_MASK
+    in = psImageRecycle(in, 5, 5, PS_TYPE_F32);
+    out = psImageGrowMask(test, in, maskVal, growSize, growVal);
+    if (out != NULL) {
+        fprintf(stderr,
+                "psImageGrowMask failed to return NULL for incompatible input image type.\n");
+        return 4;
+    }
+    //Test for valid function (image growth)
+    in = psImageRecycle(in, 5, 5, PS_TYPE_MASK);
+    in->data.PS_TYPE_MASK_DATA[0][0] = 1;
+    in->data.PS_TYPE_MASK_DATA[0][1] = 1;
+    in->data.PS_TYPE_MASK_DATA[0][2] = 1;
+    in->data.PS_TYPE_MASK_DATA[0][3] = 1;
+    in->data.PS_TYPE_MASK_DATA[0][4] = 1;
+    in->data.PS_TYPE_MASK_DATA[1][0] = 1;
+    in->data.PS_TYPE_MASK_DATA[1][1] = 1;
+    in->data.PS_TYPE_MASK_DATA[1][2] = 2;
+    in->data.PS_TYPE_MASK_DATA[1][3] = 1;
+    in->data.PS_TYPE_MASK_DATA[1][4] = 1;
+    in->data.PS_TYPE_MASK_DATA[2][0] = 1;
+    in->data.PS_TYPE_MASK_DATA[2][1] = 1;
+    in->data.PS_TYPE_MASK_DATA[2][2] = 2;
+    in->data.PS_TYPE_MASK_DATA[2][3] = 1;
+    in->data.PS_TYPE_MASK_DATA[2][4] = 1;
+    in->data.PS_TYPE_MASK_DATA[3][0] = 1;
+    in->data.PS_TYPE_MASK_DATA[3][1] = 1;
+    in->data.PS_TYPE_MASK_DATA[3][2] = 1;
+    in->data.PS_TYPE_MASK_DATA[3][3] = 1;
+    in->data.PS_TYPE_MASK_DATA[3][4] = 2;
+    in->data.PS_TYPE_MASK_DATA[4][0] = 1;
+    in->data.PS_TYPE_MASK_DATA[4][1] = 1;
+    in->data.PS_TYPE_MASK_DATA[4][2] = 1;
+    in->data.PS_TYPE_MASK_DATA[4][3] = 1;
+    in->data.PS_TYPE_MASK_DATA[4][4] = 1;
+    maskVal = 2;
+    growSize = 1;
+    growVal = 2;
+
+    out = psImageGrowMask(out, in, maskVal, growSize, growVal);
+    //0,2 1,1 1,3 2,1 2,3 3,2 should all be 3.  All other should be unchanged.
+    for (int i = 0; i < 5; i++) {
+        printf("\n ");
+        for (int j = 0; j < 5; j++) {
+            printf("  %d,%d= %d  ", i, j, out->data.PS_TYPE_MASK_DATA[i][j]);
+        }
+    }
+    psFree(out);
+    psFree(in);
+    psFree(test);
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImagePixelExtract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImagePixelExtract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImagePixelExtract.c	(revision 22322)
@@ -0,0 +1,1557 @@
+/** @file  tst_psImageExtraction.c
+*
+*  @brief Contains the tests for psImageExtraction.[ch]
+*
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-05-16 23:12:20 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include<stdlib.h>
+#include<string.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "psType.h"
+
+static psS32 testImageSlice(void);
+static psS32 testImageCut(void);
+static psS32 testImageRadialCut(void);
+static psS32 testImageRowColError(void);
+static psS32 testImageRowColF32(void);
+static psS32 testImageRowColF64(void);
+static psS32 testImageRowColS8(void);
+static psS32 testImageRowColS16(void);
+static psS32 testImageRowColS32(void);
+static psS32 testImageRowColS64(void);
+static psS32 testImageRowColU8(void);
+static psS32 testImageRowColU16(void);
+static psS32 testImageRowColU32(void);
+static psS32 testImageRowColU64(void);
+
+
+
+testDescription tests[] = {
+                              {testImageSlice, 552, "psImageSlice", 0, false},
+                              {testImageCut, 555, "psImageCut", 0, false},
+                              {testImageRadialCut, 556, "psImageRadialCut", 0, false},
+                              {testImageRowColError, 557, "testImageRowColError", 0, false},
+                              {testImageRowColF32, 558, "psImageRowColF32", 0, false},
+                              {testImageRowColF64, 559, "psImageRowColF64", 0, false},
+                              {testImageRowColU8, 560, "psImageRowColU8", 0, false},
+                              {testImageRowColU16, 561, "psImageRowColU16", 0, false},
+                              {testImageRowColU32, 562, "psImageRowColU32", 0, false},
+                              {testImageRowColU64, 563, "psImageRowColU64", 0, false},
+                              {testImageRowColS8, 564, "psImageRowColS8", 0, false},
+                              {testImageRowColS16, 565, "psImageRowColS16", 0, false},
+                              {testImageRowColS32, 566, "psImageRowColS32", 0, false},
+                              {testImageRowColS64, 567, "psImageRowColS64", 0, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    return ! runTestSuite( stderr, "psImage", tests, argc, argv );
+}
+
+psS32 testImageSlice(void)
+{
+    const psS32 r = 200;
+    const psS32 c = 300;
+    const psS32 m = r / 2 -1;
+    const psS32 n = r / 4 -1;
+    psVector* out = NULL;
+    psImage* image;
+    psPixels* positions = psPixelsAlloc( r );
+    psImage* mask = psImageAlloc( c, r, PS_TYPE_MASK );
+    psStats* stat = psStatsAlloc( PS_STAT_SAMPLE_MEDIAN );
+
+    /*
+        This function shall extract pixels from a specified region of a psImage
+        structure into a vector using a specified statistical method.
+
+        Verify the returned psVector structure contains expected data, if the
+        input psImage input has known data and the psStats structure specifies
+        a known statistical method. At least two different statistical methods
+        should be used within a valid range of psImage data. Allow for a delta
+        when comparing results to allow for testing a different platforms. Two
+        cases should be used for each possible direction. Data region cases
+        should include 0x0, 1x1, Nx1, 1xN, NxN, MxN
+
+     */
+
+    for ( psS32 row = 0;row < r;row++ ) {
+        psMaskType* maskRow = mask->data.PS_TYPE_MASK_DATA[row];
+        for ( psS32 col = 0;col < c;col++ ) {
+            maskRow[ col ] = 0;
+        }
+    }
+
+    #define PSIMAGESLICE_TEST1(TYPE,M,N,DIRECTION,TRUTH_SIZE,TRUTHPIX_X,TRUTHPIX_Y,TESTNUM) \
+    image = psImageAlloc( c, r, PS_TYPE_##TYPE ); \
+    for ( psS32 row = 0;row < r;row++ ) { \
+        ps##TYPE *imageRow = image->data.TYPE[ row ]; \
+        ps##TYPE rowOffset = row * 2; \
+        for ( psS32 col = 0;col < c;col++ ) { \
+            imageRow[ col ] = col + rowOffset; \
+        } \
+    } \
+    image->col0 = 1; \
+    image->row0 = 1; \
+    out = psImageSlice(out,positions,image,mask,1, \
+                       psRegionSet(1+c/10,1+c/10+M,1+r/10,1+r/10+N),DIRECTION,stat); \
+    \
+    if (out->n != TRUTH_SIZE) { \
+        psError(PS_ERR_UNKNOWN,true,"Number of results is wrong (%d, not %d)", \
+                out->n,TRUTH_SIZE); \
+        return TESTNUM*4+1; \
+    } \
+    \
+    if (positions->n != TRUTH_SIZE) { \
+        psError(PS_ERR_UNKNOWN,true,"Number of results for positions vector is wrong (%d, not %d)", \
+                positions->n,TRUTH_SIZE); \
+        return TESTNUM*4+2; \
+    } \
+    \
+    for (psS32 i=0;i<out->n;i++) { \
+        if (fabs(out->data.F64[i]-image->data.TYPE[r/10+TRUTHPIX_Y][c/10+TRUTHPIX_X]) > 1.0/(psF64)r) { \
+            psError(PS_ERR_UNKNOWN,true,"Improper result at position %d.  Got %g, expected %g",i, \
+                    out->data.F64[i],image->data.TYPE[r/10+TRUTHPIX_Y][c/10+TRUTHPIX_X]); \
+            return TESTNUM*4+3; \
+        } \
+        if (DIRECTION == PS_CUT_X_POS || DIRECTION == PS_CUT_X_NEG) { \
+            if (positions->data[i].x != c/10+TRUTHPIX_X) { \
+                psError(PS_ERR_UNKNOWN,true,"Improper positions (%d vs %d) result @ %d.", \
+                        positions->data[i].x,c/10+TRUTHPIX_X,i); \
+                return TESTNUM*4+4; \
+            } \
+        } else { \
+            if (positions->data[i].y != r/10+TRUTHPIX_Y) { \
+                psError(PS_ERR_UNKNOWN,true,"Improper positions (%d vs %d) result @ %d.", \
+                        positions->data[i].y,r/10+TRUTHPIX_Y,i); \
+                return TESTNUM*4+4; \
+            } \
+        } \
+    } \
+    psFree(image);
+
+    #define PSIMAGESLICE_TEST(TYPE) \
+    /* test MxN case */ \
+    PSIMAGESLICE_TEST1(TYPE, m, n, PS_CUT_X_POS, m, i, n / 2, 0 ); \
+    PSIMAGESLICE_TEST1(TYPE, m, n, PS_CUT_X_NEG, m, m - 1 - i, n / 2, 1 ); \
+    PSIMAGESLICE_TEST1(TYPE, m, n, PS_CUT_Y_POS, n, m / 2, i, 2 ); \
+    PSIMAGESLICE_TEST1(TYPE, m, n, PS_CUT_Y_NEG, n, m / 2, n - 1 - i, 3 ); \
+    \
+    /* test Mx1 case */ \
+    PSIMAGESLICE_TEST1(TYPE, m, 1, PS_CUT_X_POS, m, i, 0, 4 ); \
+    PSIMAGESLICE_TEST1(TYPE, m, 1, PS_CUT_X_NEG, m, m - 1 - i, 0, 5 ); \
+    PSIMAGESLICE_TEST1(TYPE, m, 1, PS_CUT_Y_POS, 1, m / 2, 0, 6 ); \
+    PSIMAGESLICE_TEST1(TYPE, m, 1, PS_CUT_Y_NEG, 1, m / 2, 0, 7 ); \
+    \
+    /* test 1xN case */ \
+    PSIMAGESLICE_TEST1(TYPE, 1, n, PS_CUT_X_POS, 1, 0, n / 2, 8 ); \
+    PSIMAGESLICE_TEST1(TYPE, 1, n, PS_CUT_X_NEG, 1, 0, n / 2, 9 ); \
+    PSIMAGESLICE_TEST1(TYPE, 1, n, PS_CUT_Y_POS, n, 0, i, 10 ); \
+    PSIMAGESLICE_TEST1(TYPE, 1, n, PS_CUT_Y_NEG, n, 0, n - 1 - i, 11 ); \
+    \
+    /* test 1x1 case */ \
+    PSIMAGESLICE_TEST1(TYPE, 1, 1, PS_CUT_X_POS, 1, 0, 0, 12 ); \
+    PSIMAGESLICE_TEST1(TYPE, 1, 1, PS_CUT_X_NEG, 1, 0, 0, 13 ); \
+    PSIMAGESLICE_TEST1(TYPE, 1, 1, PS_CUT_Y_POS, 1, 0, 0, 14 ); \
+    PSIMAGESLICE_TEST1(TYPE, 1, 1, PS_CUT_Y_NEG, 1, 0, 0, 15 ); \
+
+    PSIMAGESLICE_TEST(F32);
+    PSIMAGESLICE_TEST(F64);
+    PSIMAGESLICE_TEST(U16);
+
+    image = psImageAlloc( c, r, PS_TYPE_F32 );
+
+    /*
+       Verify the returned psVector structure pointer is null and program
+       execution doesn't stop, if input psImage input is null.
+
+    */
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error." );
+    out = psImageSlice( out,
+                        NULL, NULL,
+                        NULL, 0,
+                        psRegionSet(c/10, c/10 + 1, r/10, r/10 + 1),
+                        PS_CUT_X_POS,
+                        stat );
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Giving a NULL image, psImageSlice didn't return NULL as expected" );
+        return 101;
+    }
+
+
+    /*
+       Verify the returned psVector structure pointer is null and program
+       execution doesn't stop, if input psStats stats is null.
+    */
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error." );
+    out = psImageSlice( out,
+                        NULL, image,
+                        mask, 1,
+                        psRegionSet(c/10, c/10 + 1, r/10, r/10 + 1),
+                        PS_CUT_X_POS,
+                        NULL );
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Giving a NULL stat struct, psImageSlice didn't return NULL as expected" );
+        return 102;
+    }
+    /*
+
+       Verify the returned psVector structure pointer is null and program
+       executions doesn't stop, if the input direction is not set to one of
+       the two valid values.
+    */
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error." );
+    out = psImageSlice( out, NULL,
+                        image,
+                        mask, 1,
+                        psRegionSet(c/10, c/10 + 1, r/10, r/10 + 1),
+                        5,
+                        stat);
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Giving a bogus direction flag, psImageSlice didn't return NULL as expected" );
+        return 103;
+    }
+
+    /*
+       Verify the returned psVector structure pointer is null and program
+       execution doesn't stop, if the input nrow and/or ncol are zero.
+    */
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error." );
+    out = psImageSlice( out,
+                        NULL,
+                        image,
+                        mask, 1,
+                        psRegionSet(c/10, c/10, r/10, r/10),
+                        PS_CUT_X_POS,
+                        stat );
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Giving a 0x0 region, psImageSlice didn't return NULL as expected" );
+        return 104;
+    }
+
+    /*
+       Verify the returned psVector structure pointer is null and program
+       execution doesn't stop, if the inputs row, col, nrow, ncol specify a
+       regions of data that is not within the input psImage structure.
+    */
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error." );
+    out = psImageSlice( out, NULL,
+                        image,
+                        mask, 1,
+                        psRegionSet(c+1, c+2, r/10, r/10 + 10),
+                        PS_CUT_X_POS,
+                        stat );
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Giving an invalid x position, psImageSlice didn't return NULL as expected" );
+        return 105;
+    }
+
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error." );
+    out = psImageSlice( out, NULL,
+                        image,
+                        mask, 1,
+                        psRegionSet(c/10, c/10 + 1, r+1,r+5),
+                        PS_CUT_X_POS,
+                        stat );
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Giving an invalid y position, psImageSlice didn't return NULL as expected" );
+        return 106;
+    }
+
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error." );
+    out = psImageSlice( out, NULL,
+                        image,
+                        mask, 1,
+                        psRegionSet(c/10, c+1, r/10, r/10+1),
+                        PS_CUT_X_POS,
+                        stat);
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Giving an invalid numCols, psImageSlice didn't return NULL as expected" );
+        return 107;
+    }
+
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error." );
+    out = psImageSlice( out, NULL,
+                        image,
+                        mask, 1,
+                        psRegionSet(c/10, c/10 + 1, r/10, r + 1),
+                        PS_CUT_X_POS,
+                        stat);
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Giving an invalid numRows, psImageSlice didn't return NULL as expected" );
+        return 108;
+    }
+
+    /*
+       Verify the returned psVector structure pointer is null and program
+       execution doesn't stop, if the input psStat structure member options is
+       zero which indicates no statistic method specified.
+    */
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error." );
+    stat->options = 0;
+    out = psImageSlice( out, NULL,
+                        image,
+                        mask, 1,
+                        psRegionSet(c/10, c/10 + 1, r/10, r/10+1),
+                        PS_CUT_X_POS,
+                        stat);
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Giving an invalid numRows, psImageSlice didn't return NULL as expected" );
+        return 109;
+    }
+
+    /* Verify that a mask of different size than the input image returns null and program
+       execution doesn't stop.
+    */
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error mask size != image size.");
+    stat->options = PS_STAT_SAMPLE_MEDIAN;
+    psImage* maskSz = psImageAlloc( r, c, PS_TYPE_MASK );
+    out = psImageSlice( out, NULL,
+                        image,
+                        maskSz, 1,
+                        psRegionSet(c/10, c/10 + 1, r/10, r/10 + 1),
+                        PS_CUT_X_POS,
+                        stat);
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Mask size different than image size didn't return NULL as expected" );
+        return 110;
+    }
+
+    /* Verify the a invalid type mask returns null and program execution doesn't stop.
+    */
+    psLogMsg( __func__, PS_LOG_INFO, "Following should be an error invalid mask type.");
+    psImage* maskS8 = psImageAlloc( c, r, PS_TYPE_S8 );
+    out =  psImageSlice( out, NULL,
+                         image,
+                         maskS8, 1,
+                         psRegionSet(c/10, c/10 + 1, r/10, r/10 + 1),
+                         PS_CUT_X_POS,
+                         stat);
+    if ( out != NULL ) {
+        psError( PS_ERR_UNKNOWN,true, "Mask invalid type didn't return NULL as expected.");
+        return 111;
+    }
+
+    //Added tests after subimage changes.
+    psFree(image);
+    image = psImageAlloc( c, r, PS_TYPE_F64 );
+    for ( psS32 row = 0;row < r;row++ ) {
+        psF64 *imageRow = image->data.F64[ row ];
+        psF64 rowOffset = row * 2;
+        for ( psS32 col = 0;col < c;col++ ) {
+            imageRow[ col ] = col + rowOffset;
+        }
+    }
+    image->col0 = 1;
+    image->row0 = 1;
+    psFree(out);
+    out = NULL;
+    out = psImageSlice(out,positions,image,mask,1,
+                       psRegionSet(1,c,1,r),PS_CUT_X_POS,stat);
+    if (out == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psImageSlice failed to return the correct psVector.  Got NULL instead.\n");
+        return 112;
+    }
+    psFree(out);
+    out = NULL;
+    //Return NULL for incorrect image inputs.
+    image->row0 = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    out = psImageSlice(out,positions,image,mask,1,
+                       psRegionSet(1,c,1,r),PS_CUT_X_POS,stat);
+    if (out != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSlice failed to return NULL for invalid specified input.\n");
+        return 113;
+    }
+    image->col0 = -1;
+    image->row0 = 1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    out = psImageSlice(out,positions,image,mask,1,
+                       psRegionSet(1,c,1,r),PS_CUT_X_POS,stat);
+    if (out != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSlice failed to return NULL for invalid specified input.\n");
+        return 114;
+    }
+    image->col0 = 1;
+    //Return NULL for incorrect region inputs.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    out = psImageSlice(out,positions,image,mask,1,
+                       psRegionSet(0,c,1,r),PS_CUT_X_POS,stat);
+    if (out != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSlice failed to return NULL for invalid specified input.\n");
+        return 115;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    out = psImageSlice(out,positions,image,mask,1,
+                       psRegionSet(1,c,1,r+1),PS_CUT_X_POS,stat);
+    if (out != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSlice failed to return NULL for invalid specified input.\n");
+        return 116;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    out = psImageSlice(out,positions,image,mask,1,
+                       psRegionSet(1,c,1,-r-2),PS_CUT_X_POS,stat);
+    if (out != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSlice failed to return NULL for invalid specified input.\n");
+        return 117;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    out = psImageSlice(out,positions,image,mask,1,
+                       psRegionSet(c,1,1,r),PS_CUT_X_POS,stat);
+    if (out != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSlice failed to return NULL for invalid specified input.\n");
+        return 118;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    out = psImageSlice(out,positions,image,mask,1,
+                       psRegionSet(1,1,1,1),PS_CUT_X_POS,stat);
+    if (out != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSlice failed to return NULL for invalid specified input.\n");
+        return 119;
+    }
+
+    //Make sure that regions match appropriately...
+    out = psImageSlice(out,positions,image,mask,1,
+                       psRegionSet(1,-1,1,-1),PS_CUT_Y_NEG,stat);
+    psVector *out2 = NULL;
+    out2 = psImageSlice(out2,positions,image,mask,1,
+                        psRegionSet(0,0,0,0),PS_CUT_Y_NEG,stat);
+    if (out == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psImageSlice incorrectly returned NULL for valid inputs.\n");
+        return 120;
+    } else if (out2 == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psImageSlice incorrectly returned NULL for valid inputs.\n");
+        return 121;
+    } else if (out->n != out2->n) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSlice failed to return matching vectors for equivalent inputs.\n");
+        return 122;
+    } else if (out->data.F64[out->n-1] != out2->data.F64[out2->n-1] ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSlice failed to return matching vectors for equivalent inputs.\n");
+        return 123;
+    }
+    psFree(out2);
+
+    psFree( image );
+    psFree( positions );
+    psFree( mask );
+    psFree( out );
+    psFree( stat );
+    psFree( maskS8 );
+    psFree( maskSz );
+
+    return 0;
+
+}
+
+static psS32 testImageCut(void)
+{
+    psS32 c = 300;
+    psS32 r = 200;
+    psS32 numPoints = 15;
+    float startCol[] = { 40,150, 40,  0,280, 40,280, -1,300, 20, 20, 20, 20, 20, 20};
+    float endCol[] =   {240,150,240,299, 40,240, 40,240,240, -1,300,240,240,240,240};
+    float startRow[] = { 20, 10,100,  0, 20,180,180, 10, 10, 10, 10, -1,200, 10, 10};
+    float endRow[] =   {160,180,100,199,160, 10, 10,180,180,180,180,180,180, -1,200};
+    psBool success[] = {true,true,true,true,true,true,true,false,false,false,false,false,false,false,false};
+    psU32 length = 100;
+
+    psImage* image = psImageAlloc(c,r,PS_TYPE_F32);
+    psImage* mask = psImageAlloc(c,r,PS_TYPE_MASK);
+    for (psS32 row = 0; row < image->numRows; row++) {
+        for (psS32 col = 0; col < image->numCols; col++) {
+            image->data.F32[row][col] = (psF32)col + (psF32)row/1000.0f;
+            if ((row & 0x0F) == 0) {
+                mask->data.PS_TYPE_MASK_DATA[row][col] = 1;
+            } else {
+                mask->data.PS_TYPE_MASK_DATA[row][col] = 0;
+            }
+        }
+    }
+    psVector* rows = psVectorAlloc(length,PS_TYPE_F32);
+    psVector* cols = psVectorAlloc(length,PS_TYPE_F32);
+
+    psVector* result = NULL;
+    for (psS32 n = 0; n < numPoints; n++) {
+        psVector* orig = result;
+        if (! success[n]) {
+            psLogMsg(__func__,PS_LOG_INFO,"The following should be an error.");
+        }
+        if (n == 1) {
+            result = psImageCut(result,
+                                cols,rows,
+                                image,
+                                NULL,0,
+                                psRegionSet(startCol[n], endCol[n], startRow[n],endRow[n]),
+                                length,
+                                PS_INTERPOLATE_FLAT);
+        } else {
+            result = psImageCut(result,
+                                cols,rows,
+                                image,
+                                mask,1,
+                                psRegionSet(startCol[n], endCol[n], startRow[n], endRow[n]),
+                                length,
+                                PS_INTERPOLATE_FLAT);
+        }
+
+        if (success[n]) {
+            if (result == NULL) {
+                psLogMsg(__func__,PS_LOG_ERROR,
+                         "psImageCut returned NULL instead of a valid result.");
+                return n*10+1;
+            }
+
+            if (orig != NULL && orig != result) {
+                psLogMsg(__func__,PS_LOG_ERROR,
+                         "psImageCut didn't recycle the out parameter properly.");
+                return n*10+2;
+            }
+
+            float deltaRow = (endRow[n]-startRow[n])/(length-1);
+            float deltaCol = (endCol[n]-startCol[n])/(length-1);
+            psF32 truth;
+            for (psS32 i = 0; i < length; i++) {
+                float x = (float)startCol[n]+(float)i*deltaCol;
+                float y = (float)startRow[n]+(float)i*deltaRow;
+                if (n == 1) {
+                    truth = psImagePixelInterpolate( image, x, y,
+                                                     NULL,0,0,PS_INTERPOLATE_FLAT);
+                } else {
+                    truth = psImagePixelInterpolate( image, x, y,
+                                                     mask,1,0,PS_INTERPOLATE_FLAT);
+                }
+                if (fabs(result->data.F32[i]-truth) > FLT_EPSILON) {
+                    psLogMsg(__func__,PS_LOG_ERROR,
+                             "Bad result in position %d; Found %g but expected %g.",
+                             i, result->data.F32[i], truth);
+                    return n*10+5;
+                }
+                if (fabsf(x - cols->data.F32[i]) > FLT_EPSILON ||
+                        fabsf(y - rows->data.F32[i]) > FLT_EPSILON) {
+                    psLogMsg(__func__,PS_LOG_ERROR,
+                             "Bad resulting col/row at index %d; Found (%g,%g) but expected (%g,%g).",
+                             i, cols->data.F32[i], rows->data.F32[i], x, y);
+                    return n*10+6;
+                }
+            }
+        } else {
+            if (result != NULL) {
+                psLogMsg(__func__,PS_LOG_ERROR,
+                         "psImageCut did not return NULL with a cut of (%g,%g)->(%g,%g).",
+                         startCol[n],startRow[n],endCol[n],endRow[n]);
+                return n*10+7;
+            }
+            psErr* err = psErrorLast();
+            if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+                psLogMsg(__func__,PS_LOG_ERROR,
+                         "psImageCut did not generate proper error message.");
+                return 105;
+            }
+            psFree(err);
+        }
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error (NULL image).");
+    result = psImageCut(result,
+                        cols,rows,
+                        NULL,
+                        mask,1,
+                        psRegionSet(startCol[0], endCol[0], startRow[0], endRow[0]),
+                        length,
+                        PS_INTERPOLATE_FLAT);
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageCut did not return NULL given NULL image.");
+        return 100;
+    }
+    psErr* err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageCut did not generate proper error message given NULL image.");
+        return 101;
+    }
+    psFree(err);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error (length=0).");
+    result = psImageCut(result,
+                        cols,rows,
+                        image,
+                        mask,1,
+                        psRegionSet(startCol[0], endCol[0], startRow[0], endRow[0]),
+                        0,
+                        PS_INTERPOLATE_FLAT);
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageCut did not return NULL given length=0.");
+        return 102;
+    }
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageCut did not generate proper error message given length=0.");
+        return 103;
+    }
+    psFree(err);
+
+    psFree(result);
+    psFree(image);
+    psFree(mask);
+    psFree(rows);
+    psFree(cols);
+
+    return 0;
+}
+
+static psS32 testImageRadialCut(void)
+{
+    psS32 c = 300;
+    psS32 r = 200;
+    psS32 centerX = c/2;
+    psS32 centerY = r/2;
+    psErr* err = NULL;
+
+    psImage* image = psImageAlloc(c,r,PS_TYPE_F32);
+    psImage* mask = psImageAlloc(c,r,PS_TYPE_MASK);
+    for (psS32 row = 0; row < image->numRows; row++) {
+        for (psS32 col = 0; col < image->numCols; col++) {
+            image->data.F32[row][col] = sqrtf((col-centerX)*(col-centerX)+(row-centerY)*(row-centerY));
+            if ((row & 0x0F) == 0) {
+                mask->data.PS_TYPE_MASK_DATA[row][col] = 1;
+            } else {
+                mask->data.PS_TYPE_MASK_DATA[row][col] = 0;
+            }
+        }
+    }
+
+    psStats* stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psVector* radii = psVectorAlloc(10,PS_TYPE_F32);
+    for (psS32 i=0; i < 10; i++) {
+        radii->data.F32[i] = 10+i*10;
+        radii->n++;
+    }
+
+    psVector* result = NULL;
+
+    result = psImageRadialCut(result,image,mask,1,centerX,centerY,radii,stat);
+
+    if (result == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value of NULL unexpected.");
+        return 1;
+    }
+
+    if (result->type.type != PS_TYPE_F64) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return type not psF64, as expected.");
+        return 2;
+    }
+
+    for (psS32 i=0; i < 9; i++) {
+        if (fabs(result->data.F64[i] - (15.0+i*10)) > 1) {
+            psLogMsg(__func__,PS_LOG_ERROR,
+                     "Result was not as expected for radii #%d (%g, expected %d +/- 1)",
+                     result->data.F64[i], (15.0+i*10) );
+            return 3+i;
+        }
+    }
+
+    // again, but without mask
+    psVector* orig = result;
+    result = psImageRadialCut(result,image,NULL,1,centerX,centerY,radii,stat);
+
+    if (result == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value of NULL unexpected.");
+        return 12;
+    }
+
+    if (result != orig) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value of is not same as input parameter 'out'.");
+        return 13;
+    }
+
+    for (psS32 i=0; i < 9; i++) {
+        if (fabs(result->data.F64[i] - (15.0+i*10)) > 1) {
+            psLogMsg(__func__,PS_LOG_ERROR,
+                     "Result was not as expected for radii #%d (%g, expected %d +/- 1)",
+                     result->data.F64[i], (15.0+i*10) );
+            return 14+i;
+        }
+    }
+
+    // NULL input image...
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    result = psImageRadialCut(result,NULL,NULL,1,centerX,centerY,radii,stat);
+
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not NULL as expected.");
+        return 23;
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageRadialCut did not generate proper error message.");
+        return 24;
+    }
+    psFree(err);
+
+    // NULL input radii...
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    result = psImageRadialCut(result,image,mask,1,centerX,centerY,NULL,stat);
+
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not NULL as expected.");
+        return 23;
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageRadialCut did not generate proper error message.");
+        return 24;
+    }
+    psFree(err);
+
+    // NULL input stat...
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    result = psImageRadialCut(result,image,mask,1,centerX,centerY,radii,NULL);
+
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not NULL as expected.");
+        return 23;
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageRadialCut did not generate proper error message.");
+        return 24;
+    }
+    psFree(err);
+
+    // Bad center X
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    result = psImageRadialCut(result,image,mask,1,
+                              c+1,centerY,radii,stat);
+
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not NULL as expected.");
+        return 25;
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageRadialCut did not generate proper error message.");
+        return 26;
+    }
+    psFree(err);
+
+    // Bad center Y
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    result = psImageRadialCut(result,image,mask,1,
+                              centerX,r+1,radii,stat);
+
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not NULL as expected.");
+        return 27;
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageRadialCut did not generate proper error message.");
+        return 28;
+    }
+    psFree(err);
+
+    // Bad mask type (N.B., swapped image/mask to do this)
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    result = psImageRadialCut(result,mask,image,1,
+                              centerX,r+1,radii,stat);
+
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not NULL as expected.");
+        return 29;
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_TYPE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageRadialCut did not generate proper error message.");
+        return 30;
+    }
+    psFree(err);
+
+    // Bad mask size
+    psImage* mask2 = psImageAlloc(c/2,r/2,PS_TYPE_MASK);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    result = psImageRadialCut(result,image, mask2, 1,
+                              centerX,centerY,radii,stat);
+    psFree(mask2);
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not NULL as expected.");
+        return 31;
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_SIZE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageRadialCut did not generate proper error message.");
+        return 32;
+    }
+    psFree(err);
+
+    // Bad radii size
+    psVector* radii2 = psVectorAlloc(1,PS_TYPE_MASK);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    result = psImageRadialCut(result,image, mask, 1,
+                              centerX,centerY,radii2,stat);
+    psFree(radii2);
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not NULL as expected.");
+        return 33;
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_SIZE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageRadialCut did not generate proper error message.");
+        return 34;
+    }
+    psFree(err);
+
+    // bad input stat option...
+    stat->options = 0;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    result = psImageRadialCut(result,image,mask,1,centerX,centerY,radii,stat);
+    stat->options = PS_STAT_SAMPLE_MEAN;
+
+    if (result != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not NULL as expected.");
+        return 35;
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageRadialCut did not generate proper error message.");
+        return 36;
+    }
+    psFree(err);
+
+    psFree(image);
+    psFree(mask);
+    psFree(radii);
+    psFree(stat);
+    psFree(result);
+
+    return 0;
+}
+
+psS32 testImageRowColError(void)
+{
+    psImage *image = NULL;
+    psVector *out = NULL;
+    int num = 0;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for row)");
+    out = psImageRow(NULL, image, num);
+    if (out != NULL) {
+        return 1;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for col)");
+    out = psImageCol(NULL, image, num);
+    if (out != NULL) {
+        return 1;
+    }
+
+    image = psImageAlloc(3, 3, PS_TYPE_F64);
+
+    //Test for invalid row0.
+    *(psS32*)&(image->row0) = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for row)");
+    out = psImageRow(NULL, image, num);
+    if (out != NULL) {
+        return 2;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for col)");
+    out = psImageCol(NULL, image, num);
+    if (out != NULL) {
+        return 2;
+    }
+
+    //Test for invalid col0.
+    *(psS32*)&(image->row0) = 5;
+    *(psS32*)&(image->col0) = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for row)");
+    out = psImageRow(NULL, image, num);
+    if (out != NULL) {
+        return 3;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for col)");
+    out = psImageCol(NULL, image, num);
+    if (out != NULL) {
+        return 3;
+    }
+
+    //Test for invalid numRows
+    *(psS32*)&(image->col0) = 10;
+    *(int*)&(image->numRows) = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for row)");
+    out = psImageRow(NULL, image, num);
+    if (out != NULL) {
+        return 4;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for col)");
+    out = psImageCol(NULL, image, num);
+    if (out != NULL) {
+        return 4;
+    }
+    //Test for invalid numCols
+    *(int*)&(image->numRows) = 3;
+    *(int*)&(image->numCols) = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for row)");
+    out = psImageRow(NULL, image, num);
+    if (out != NULL) {
+        return 5;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for col)");
+    out = psImageCol(NULL, image, num);
+    if (out != NULL) {
+        return 5;
+    }
+    //Test for invalid row/col number specified.
+    *(int*)&(image->numCols) = 3;
+    num = 8;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for row)");
+    out = psImageRow(NULL, image, num);
+    if (out != NULL) {
+        return 6;
+    }
+    num = 13;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for col)");
+    out = psImageCol(NULL, image, num);
+    if (out != NULL) {
+        return 6;
+    }
+    num = 3;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for row)");
+    out = psImageRow(NULL, image, num);
+    if (out != NULL) {
+        return 7;
+    }
+    num = 8;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for col)");
+    out = psImageCol(NULL, image, num);
+    if (out != NULL) {
+        return 7;
+    }
+    num = -10;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for row)");
+    out = psImageRow(NULL, image, num);
+    if (out != NULL) {
+        return 8;
+    }
+    num = -14;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message(for col)");
+    out = psImageCol(NULL, image, num);
+    if (out != NULL) {
+        return 8;
+    }
+
+    //Test valid cases.
+    image->col0 = 10;
+    image->row0 = 5;
+    *(int*)&(image->numRows) = 3;
+    *(int*)&(image->numCols) = 3;
+    image->data.F64[0][0] = 666.666;
+    image->data.F64[1][0] = 66.6;
+    image->data.F64[2][0] = 6.66;
+    image->data.F64[0][1] = 6.6;
+    image->data.F64[1][1] = 6.666;
+    image->data.F64[2][1] = 66.666;
+    image->data.F64[0][2] = 666.6;
+    image->data.F64[1][2] = 666.66;
+    image->data.F64[2][2] = 66.66;
+    num = 7;
+    out = psImageRow(out, image, num);
+    if (out == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psImageRow failed to return correct psVector output.\n");
+        return 10;
+    } else {
+        psFree(out);
+        out = NULL;
+    }
+    num = 11;
+    out = psImageCol(NULL, image, num);
+    if (out == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psImageCol failed to return correct psVector output.\n");
+        return 10;
+    } else {
+        psFree(out);
+        out = NULL;
+    }
+
+    num = -3;
+    out = psImageRow(out, image, num);
+    if (out == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psImageRow failed to return correct psVector output.\n");
+        return 10;
+    } else {
+        psFree(out);
+        out = NULL;
+    }
+    num = -1;
+    out = psImageCol(NULL, image, num);
+    if (out == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psImageCol failed to return correct psVector output.\n");
+        return 10;
+    }
+    psFree(out);
+    psFree(image);
+    return 0;
+}
+
+psS32 testImageRowColF64(void)
+{
+    psVector *rowcol = NULL;
+    psVector *empty = NULL;
+    psImage *image = NULL;
+    psImage *emptyImage = NULL;
+
+    image = psImageAlloc(3, 3, PS_TYPE_F64);
+    rowcol = psVectorAlloc(3, PS_TYPE_F64);
+
+    image->data.F64[0][0] = 666.666;
+    image->data.F64[1][0] = 66.6;
+    image->data.F64[2][0] = 6.66;
+    image->data.F64[0][1] = 6.6;
+    image->data.F64[1][1] = 6.666;
+    image->data.F64[2][1] = 66.666;
+    image->data.F64[0][2] = 666.6;
+    image->data.F64[1][2] = 666.66;
+    image->data.F64[2][2] = 66.66;
+
+    //Test for error with NULL image
+    empty = psImageCol(empty, emptyImage, 0);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    //Test for error with Out of Range Row
+    empty = psImageRow(empty, image, 5);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return NULL for out of range row input.\n");
+        return 2;
+    }
+    rowcol->data.F64[0] = 1.1;
+    rowcol->data.F64[2] = 2.2;
+    //Test recycling of non-NULL vector & correct output
+    rowcol = psImageCol(rowcol, image, 1);
+    double test1, test2;
+    double TOLTST = .001;
+    test1 = fabs(rowcol->data.F64[0]-6.6);
+    test2 = fabs(rowcol->data.F64[2]-66.666);
+    if ( (test1>TOLTST) || (test2>TOLTST) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return correct values.\n");
+        return 3;
+    }
+    rowcol = psImageRow(rowcol, image, 1);
+    test1 = fabs(rowcol->data.F64[0]-66.6);
+    test2 = fabs(rowcol->data.F64[2]-666.66);
+    if ( (test1>TOLTST) || (test2>TOLTST) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return correct values.\n");
+        return 4;
+    }
+
+    psFree(rowcol);
+    psFree(image);
+    return 0;
+}
+
+psS32 testImageRowColF32(void)
+{
+    psVector *rowcol = NULL;
+    psVector *empty = NULL;
+    psImage *image = NULL;
+    psImage *emptyImage = NULL;
+
+    float test1;
+    float test2;
+    float TOLTST = .01;
+
+    image = psImageAlloc(3, 3, PS_TYPE_F32);
+    rowcol = psVectorAlloc(3, PS_TYPE_F32);
+    rowcol->n = rowcol->nalloc;
+
+    image->data.F32[0][0] = 666.666;
+    image->data.F32[1][0] = 66.6;
+    image->data.F32[2][0] = 6.66;
+    image->data.F32[0][1] = 6.6;
+    image->data.F32[1][1] = 6.666;
+    image->data.F32[2][1] = 66.666;
+    image->data.F32[0][2] = 666.6;
+    image->data.F32[1][2] = 666.66;
+    image->data.F32[2][2] = 66.66;
+
+    //Test for error with NULL image
+    empty = psImageCol(empty, emptyImage, 0);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    //Test for error with Out of Range Row
+    empty = psImageRow(empty, image, 5);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return NULL for out of range row input.\n");
+        return 2;
+    }
+    rowcol->data.F32[0] = 1.1;
+    rowcol->data.F32[2] = 2.2;
+    //Test recycling of non-NULL vector & correct output
+    rowcol = psImageCol(rowcol, image, 1);
+    test1 = fabs(rowcol->data.F32[0]-6.6);
+    test2 = fabs(rowcol->data.F32[2]-66.666);
+    if ( (test1>TOLTST) || (test2>TOLTST) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return correct values.\n");
+        return 3;
+    }
+
+    psFree(rowcol);
+    psFree(image);
+    return 0;
+}
+
+psS32 testImageRowColU64(void)
+{
+    psVector *rowcol = NULL;
+    psVector *empty = NULL;
+    psImage *image = NULL;
+    psImage *emptyImage = NULL;
+
+    image = psImageAlloc(3, 3, PS_TYPE_U64);
+    rowcol = psVectorAlloc(3, PS_TYPE_U64);
+
+    image->data.U64[0][0] = 666666;
+    image->data.U64[1][0] = 666;
+    image->data.U64[2][0] = 666;
+    image->data.U64[0][1] = 66;
+    image->data.U64[1][1] = 6666;
+    image->data.U64[2][1] = 66666;
+    image->data.U64[0][2] = 6666;
+    image->data.U64[1][2] = 66666;
+    image->data.U64[2][2] = 6666;
+
+    //Test for error with NULL image
+    empty = psImageCol(empty, emptyImage, 0);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    //Test for error with Out of Range Row
+    empty = psImageRow(empty, image, 5);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return NULL for out of range row input.\n");
+        return 2;
+    }
+    rowcol->data.U64[0] = 11;
+    rowcol->data.U64[2] = 22;
+    //Test recycling of non-NULL vector & correct output
+    rowcol = psImageCol(rowcol, image, 1);
+    if (rowcol->data.U64[0] != 666 && rowcol->data.U64[2] != 66666) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return correct values.\n");
+        return 3;
+    }
+
+    psFree(rowcol);
+    psFree(image);
+    return 0;
+}
+
+psS32 testImageRowColU32(void)
+{
+    psVector *rowcol = NULL;
+    psVector *empty = NULL;
+    psImage *image = NULL;
+    psImage *emptyImage = NULL;
+
+    image = psImageAlloc(3, 3, PS_TYPE_U32);
+    rowcol = psVectorAlloc(3, PS_TYPE_U32);
+
+    image->data.U32[0][0] = 666666;
+    image->data.U32[1][0] = 666;
+    image->data.U32[2][0] = 666;
+    image->data.U32[0][1] = 66;
+    image->data.U32[1][1] = 6666;
+    image->data.U32[2][1] = 66666;
+    image->data.U32[0][2] = 6666;
+    image->data.U32[1][2] = 66666;
+    image->data.U32[2][2] = 6666;
+
+    //Test for error with NULL image
+    empty = psImageCol(empty, emptyImage, 0);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    //Test for error with Out of Range Row
+    empty = psImageRow(empty, image, 5);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return NULL for out of range row input.\n");
+        return 2;
+    }
+    rowcol->data.U32[0] = 11;
+    rowcol->data.U32[2] = 22;
+    //Test recycling of non-NULL vector & correct output
+    rowcol = psImageCol(rowcol, image, 1);
+    if (rowcol->data.U32[0] != 666 && rowcol->data.U32[2] != 66666) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return correct values.\n");
+        return 3;
+    }
+
+    psFree(rowcol);
+    psFree(image);
+    return 0;
+}
+
+psS32 testImageRowColS32(void)
+{
+    psVector *rowcol = NULL;
+    psVector *empty = NULL;
+    psImage *image = NULL;
+    psImage *emptyImage = NULL;
+
+    image = psImageAlloc(3, 3, PS_TYPE_S32);
+    rowcol = psVectorAlloc(3, PS_TYPE_S32);
+
+    image->data.S32[0][0] = 666666;
+    image->data.S32[1][0] = 666;
+    image->data.S32[2][0] = 666;
+    image->data.S32[0][1] = 66;
+    image->data.S32[1][1] = 6666;
+    image->data.S32[2][1] = 66666;
+    image->data.S32[0][2] = 6666;
+    image->data.S32[1][2] = 66666;
+    image->data.S32[2][2] = 6666;
+
+    //Test for error with NULL image
+    empty = psImageCol(empty, emptyImage, 0);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    //Test for error with Out of Range Row
+    empty = psImageRow(empty, image, 5);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return NULL for out of range row input.\n");
+        return 2;
+    }
+    rowcol->data.S32[0] = 11;
+    rowcol->data.S32[2] = 22;
+    //Test recycling of non-NULL vector & correct output
+    rowcol = psImageCol(rowcol, image, 1);
+    if (rowcol->data.S32[0] != 666 && rowcol->data.S32[2] != 66666) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return correct values.\n");
+        return 3;
+    }
+
+    psFree(rowcol);
+    psFree(image);
+    return 0;
+}
+
+psS32 testImageRowColS64(void)
+{
+    psVector *rowcol = NULL;
+    psVector *empty = NULL;
+    psImage *image = NULL;
+    psImage *emptyImage = NULL;
+
+    image = psImageAlloc(3, 3, PS_TYPE_S64);
+    rowcol = psVectorAlloc(3, PS_TYPE_S64);
+
+    image->data.S64[0][0] = 666666;
+    image->data.S64[1][0] = 666;
+    image->data.S64[2][0] = 666;
+    image->data.S64[0][1] = 66;
+    image->data.S64[1][1] = 6666;
+    image->data.S64[2][1] = 66666;
+    image->data.S64[0][2] = 6666;
+    image->data.S64[1][2] = 66666;
+    image->data.S64[2][2] = 6666;
+
+    //Test for error with NULL image
+    empty = psImageCol(empty, emptyImage, 0);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    //Test for error with Out of Range Row
+    empty = psImageRow(empty, image, 5);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return NULL for out of range row input.\n");
+        return 2;
+    }
+    rowcol->data.S64[0] = 11;
+    rowcol->data.S64[2] = 22;
+    //Test recycling of non-NULL vector & correct output
+    rowcol = psImageCol(rowcol, image, 1);
+    if (rowcol->data.S64[0] != 666 && rowcol->data.S64[2] != 66666) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return correct values.\n");
+        return 3;
+    }
+
+    psFree(rowcol);
+    psFree(image);
+    return 0;
+}
+
+psS32 testImageRowColS16(void)
+{
+    psVector *rowcol = NULL;
+    psVector *empty = NULL;
+    psImage *image = NULL;
+    psImage *emptyImage = NULL;
+
+    image = psImageAlloc(3, 3, PS_TYPE_S16);
+    rowcol = psVectorAlloc(3, PS_TYPE_S16);
+
+    image->data.S16[0][0] = 3333;
+    image->data.S16[1][0] = 666;
+    image->data.S16[2][0] = 666;
+    image->data.S16[0][1] = 66;
+    image->data.S16[1][1] = 6666;
+    image->data.S16[2][1] = 4444;
+    image->data.S16[0][2] = 6666;
+    image->data.S16[1][2] = 4444;
+    image->data.S16[2][2] = 6666;
+
+    //Test for error with NULL image
+    empty = psImageCol(empty, emptyImage, 0);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    //Test for error with Out of Range Row
+    empty = psImageRow(empty, image, 5);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return NULL for out of range row input.\n");
+        return 2;
+    }
+    rowcol->data.S16[0] = 11;
+    rowcol->data.S16[2] = 22;
+    //Test recycling of non-NULL vector & correct output
+    rowcol = psImageCol(rowcol, image, 1);
+    if (rowcol->data.S16[0] != 666 && rowcol->data.S16[2] != 4444) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return correct values.\n");
+        return 3;
+    }
+
+    psFree(rowcol);
+    psFree(image);
+    return 0;
+}
+
+psS32 testImageRowColU16(void)
+{
+    psVector *rowcol = NULL;
+    psVector *empty = NULL;
+    psImage *image = NULL;
+    psImage *emptyImage = NULL;
+
+    image = psImageAlloc(3, 3, PS_TYPE_U16);
+    rowcol = psVectorAlloc(3, PS_TYPE_U16);
+
+    image->data.S16[0][0] = 3333;
+    image->data.S16[1][0] = 666;
+    image->data.S16[2][0] = 666;
+    image->data.S16[0][1] = 66;
+    image->data.S16[1][1] = 6666;
+    image->data.S16[2][1] = 4444;
+    image->data.S16[0][2] = 6666;
+    image->data.S16[1][2] = 4444;
+    image->data.S16[2][2] = 6666;
+
+    //Test for error with NULL image
+    empty = psImageCol(empty, emptyImage, 0);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    //Test for error with Out of Range Row
+    empty = psImageRow(empty, image, 5);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return NULL for out of range row input.\n");
+        return 2;
+    }
+    rowcol->data.U16[0] = 11;
+    rowcol->data.U16[2] = 22;
+    //Test recycling of non-NULL vector & correct output
+    rowcol = psImageCol(rowcol, image, 1);
+    if (rowcol->data.U16[0] != 666 && rowcol->data.U16[2] != 4444) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return correct values.\n");
+        return 3;
+    }
+
+    psFree(rowcol);
+    psFree(image);
+    return 0;
+}
+
+psS32 testImageRowColU8(void)
+{
+    psVector *rowcol = NULL;
+    psVector *empty = NULL;
+    psImage *image = NULL;
+    psImage *emptyImage = NULL;
+
+    image = psImageAlloc(3, 3, PS_TYPE_U8);
+    rowcol = psVectorAlloc(3, PS_TYPE_U8);
+
+    image->data.U8[0][0] = 244;
+    image->data.U8[1][0] = 123;
+    image->data.U8[2][0] = 123;
+    image->data.U8[0][1] = 66;
+    image->data.U8[1][1] = 199;
+    image->data.U8[2][1] = 249;
+    image->data.U8[0][2] = 199;
+    image->data.U8[1][2] = 249;
+    image->data.U8[2][2] = 199;
+
+    //Test for error with NULL image
+    empty = psImageCol(empty, emptyImage, 0);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    //Test for error with Out of Range Row
+    empty = psImageRow(empty, image, 5);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return NULL for out of range row input.\n");
+        return 2;
+    }
+    rowcol->data.U8[0] = 11;
+    rowcol->data.U8[2] = 22;
+    //Test recycling of non-NULL vector & correct output
+    rowcol = psImageCol(rowcol, image, 1);
+    if (rowcol->data.U8[0] != 123 && rowcol->data.U8[2] != 249) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return correct values.\n");
+        return 3;
+    }
+
+    psFree(rowcol);
+    psFree(image);
+    return 0;
+}
+psS32 testImageRowColS8(void)
+{
+    psVector *rowcol = NULL;
+    psVector *empty = NULL;
+    psImage *image = NULL;
+    psImage *emptyImage = NULL;
+
+    image = psImageAlloc(3, 3, PS_TYPE_S8);
+    rowcol = psVectorAlloc(3, PS_TYPE_S8);
+
+    image->data.S8[0][0] = 44;
+    image->data.S8[1][0] = 23;
+    image->data.S8[2][0] = 23;
+    image->data.S8[0][1] = 66;
+    image->data.S8[1][1] = 99;
+    image->data.S8[2][1] = 49;
+    image->data.S8[0][2] = 99;
+    image->data.S8[1][2] = 49;
+    image->data.S8[2][2] = 99;
+
+    //Test for error with NULL image
+    empty = psImageCol(empty, emptyImage, 0);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return NULL for NULL image input.\n");
+        return 1;
+    }
+    //Test for error with Out of Range Row
+    empty = psImageRow(empty, image, 5);
+    if (empty != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageRow failed to return NULL for out of range row input.\n");
+        return 2;
+    }
+    rowcol->data.S8[0] = 11;
+    rowcol->data.S8[2] = 22;
+    //Test recycling of non-NULL vector & correct output
+    rowcol = psImageCol(rowcol, image, 1);
+    if (rowcol->data.S8[0] != 23 && rowcol->data.S8[2] != 49) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCol failed to return correct values.\n");
+        return 3;
+    }
+
+    psFree(rowcol);
+    psFree(image);
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImagePixelManip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImagePixelManip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImagePixelManip.c	(revision 22322)
@@ -0,0 +1,797 @@
+/** @file  tst_psImageManip.c
+ *
+ *  @brief Contains the tests for psImageManip.[ch]
+ *
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-06 02:41:07 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <complex.h>
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string.h>                    // for memset
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "psType.h"
+
+static psS32 testImageClip(void);
+static psS32 testImageClipNAN(void);
+static psS32 testImageClipComplexRegion(void);
+static psS32 testImageOverlay(void);
+
+testDescription tests[] = {
+                              {testImageClip,571,"psImageClip",0,false},
+                              {testImageClipNAN,572,"psImageClipNAN",0,false},
+                              {testImageClipComplexRegion,673,"psImageClipComplexRegion",0,false},
+                              {testImageOverlay,573,"psImageOverlay",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    return ! runTestSuite(stderr,"psImage",tests,argc,argv);
+}
+
+
+psS32 testImageClip(void)
+{
+    psImage* img = NULL;
+    psU32 c = 128;
+    psU32 r = 256;
+    psF64 min;
+    psF64 max;
+    psS32 numClipped = 0;
+    psS32 retVal;
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "psImageClip shall limit the minimum and maximum data value within a psImage structure");
+
+    /*
+
+        psImageClip shall limit the minimum and maximum data value within a
+        psImage structure to a specified min and max value.
+
+        Verify the returned integer is equal to the number of pixels clipped,
+        if the input psImage structure contains known values and input parameters
+        min and max have know values.
+
+        Verify the psImage structure specified by the input parameter input is
+        modified to contain the expected values, if the input psImage structure
+        contains known values, min and max are specified and vmin and vmax
+        parameters are known.
+
+        Verify the retuned integer is zero, psImage structure input is unmodified
+        and program executions doesn't stop, if input parameter psImage structure
+        pointer is null.
+
+        Verify the retuned integer is zero, psImage structure input is unmodified
+        and program executions doesn't stop, if input parameter min is larger than max.
+    */
+
+    // create image
+    #define testImageClipByType(datatype) \
+    img = psImageAlloc(c,r,PS_TYPE_##datatype); \
+    for (psU32 row=0;row<r;row++) { \
+        ps##datatype* imgRow = img->data.datatype[row]; \
+        for (psU32 col=0;col<c;col++) { \
+            imgRow[col] = (ps##datatype)(row+col); \
+        } \
+    } \
+    min = (psF64)r/2.0; \
+    max = (psF64)r; \
+    \
+    retVal = psImageClip(img,min,(double)PS_MIN_##datatype,max,(double)PS_MAX_##datatype); \
+    \
+    numClipped = 0; \
+    for (psU32 row=0;row<r;row++) { \
+        ps##datatype* imgRow = img->data.datatype[row]; \
+        for (psU32 col=0;col<c;col++) { \
+            ps##datatype value = (ps##datatype)(row+col); \
+            if (value < min) { \
+                numClipped++; \
+                value = PS_MIN_##datatype; \
+            } else if (value > max) { \
+                numClipped++; \
+                value = PS_MAX_##datatype; \
+            } \
+            if (fabsf(imgRow[col]-value) > FLT_EPSILON) { \
+                psError(PS_ERR_UNKNOWN, true,"Pixel value is not as expected (%g vs %g) at %u,%u", \
+                        (psF64)imgRow[col],(psF64)value,col,row); \
+                return 1; \
+            } \
+        } \
+    } \
+    if (retVal != numClipped) { \
+        psError(PS_ERR_UNKNOWN, true,"Expected %d clips, but got %d", \
+                numClipped,retVal); \
+        return 2; \
+    } \
+    psFree(img);
+
+    #define testImageClipByComplexType(datatype) \
+    img = psImageAlloc(c,r,PS_TYPE_##datatype); \
+    for (psU32 row=0;row<r;row++) { \
+        ps##datatype* imgRow = img->data.datatype[row]; \
+        for (psU32 col=0;col<c;col++) { \
+            imgRow[col] = (ps##datatype)(row+I*col); \
+        } \
+    } \
+    min = (float)r/2.0f; \
+    max = (float)r; \
+    \
+    retVal = psImageClip(img,min,-1.0f,max,-2.0f); \
+    \
+    numClipped = 0; \
+    for (psU32 row=0;row<r;row++) { \
+        ps##datatype* imgRow = img->data.datatype[row]; \
+        for (psU32 col=0;col<c;col++) { \
+            ps##datatype value = row+I*col; \
+            if (cabs(value) < min) { \
+                numClipped++; \
+                value = -1.0f; \
+            } else if (cabs(value) > max) { \
+                numClipped++; \
+                value = -2.0f; \
+            } \
+            if (fabsf(creal(imgRow[col])-creal(value)) > FLT_EPSILON || \
+                    fabsf(cimag(imgRow[col])-cimag(value)) > FLT_EPSILON) { \
+                psError(PS_ERR_UNKNOWN, true,"Pixel value is not as expected (%.2f+%.2fi vs %.2f+%.2fi) at %u,%u", \
+                        creal(imgRow[col]),cimag(imgRow[col]),creal(value),cimag(value),col,row); \
+                return 1; \
+            } \
+        } \
+    } \
+    if (retVal != numClipped) { \
+        psError(PS_ERR_UNKNOWN, true,"Expected %d clips, but got %d", \
+                numClipped,retVal); \
+        return 2; \
+    } \
+    psFree(img);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping of F64 imagery");
+    testImageClipByType(F64);
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping of F32 imagery");
+    testImageClipByType(F32);
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping of S32 imagery");
+    testImageClipByType(S32);
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping of S16 imagery");
+    testImageClipByType(S16);
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping of S8 imagery");
+    testImageClipByType(S8);
+    //    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping of U32 imagery");
+    //    testImageClipByType(U32);
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping of U16 imagery");
+    testImageClipByType(U16);
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping of U8 imagery");
+    testImageClipByType(U8);
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping of C32 imagery");
+    testImageClipByComplexType(C32);
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping of C64 imagery");
+    testImageClipByComplexType(C64);
+
+    // Verify the retuned integer is zero, psImage structure input is unmodified
+    // and program executions doesn't stop, if input parameter psImage structure
+    // pointer is null.
+    retVal = psImageClip(NULL,min,-1.0f,max,-2.0f);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for clips of a NULL image.");
+        return 3;
+    }
+
+    // Verify the retuned integer is zero, psImage structure input is unmodified
+    // and program executions doesn't stop, if input parameter min is larger than max.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error (max<min)");
+    retVal = psImageClip(img,max,-1.0f,min,-2.0f);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for clips when max < min.");
+        return 4;
+    }
+
+    return 0;
+}
+
+psS32 testImageClipNAN(void)
+{
+    psImage* img = NULL;
+    psU32 c = 128;
+    psU32 r = 256;
+    psS32 numClipped = 0;
+    psS32 retVal;
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "psImageClipNaN shall modified pixel values of NaN with a specified value");
+
+    /*
+        psImageClipNaN shall modify a psImage structure with pixel values set
+        to NaN to a value specified as an input parameter.
+
+        Verify the returned integer is equal to the number of pixels modified
+        and the psImage is modified at locations where NaN pixels where
+        located to the value specified in the input parameter value.
+
+        Verify the returned integer is zero and program execution doesn't stop,
+        if the input parameter psImage structure pointer is null.
+    */
+
+    // create image
+    #define testImageClipNaNByType(datatype) \
+    img = psImageAlloc(c,r,PS_TYPE_##datatype); \
+    for (unsigned row=0;row<r;row++) { \
+        ps##datatype* imgRow = img->data.datatype[row]; \
+        for (unsigned col=0;col<c;col++) { \
+            if (row == col) { \
+                imgRow[col] = NAN; \
+            } else if (row+1 == col) { \
+                imgRow[col] = INFINITY; \
+            } else { \
+                imgRow[col] = (ps##datatype)(row+col); \
+            } \
+        } \
+    } \
+    \
+    retVal = psImageClipNaN(img,-1.0f); \
+    \
+    numClipped = 0; \
+    for (unsigned row=0;row<r;row++) { \
+        ps##datatype* imgRow = img->data.datatype[row]; \
+        for (unsigned col=0;col<c;col++) { \
+            ps##datatype value = (ps##datatype)(row+col); \
+            if ( (row == col) || (row+1 == col) ) { \
+                numClipped++; \
+                value = -1.0; \
+            } \
+            if (fabsf(imgRow[col]-value) > FLT_EPSILON) { \
+                psError(PS_ERR_UNKNOWN, true,"Pixel value is not as expected (%f vs %f) at %d,%d", \
+                        imgRow[col],value,col,row); \
+                return 1; \
+            } \
+        } \
+    } \
+    if (retVal != numClipped) { \
+        psError(PS_ERR_UNKNOWN, true,"Expected %d clips, but got %d", \
+                numClipped,retVal); \
+        return 2; \
+    } \
+    psFree(img);
+
+    testImageClipNaNByType(F32);
+    testImageClipNaNByType(F64);
+    testImageClipNaNByType(C32);
+    testImageClipNaNByType(C64);
+
+    // Verify the retuned integer is zero, psImage structure input is unmodified
+    // and program executions doesn't stop, if input parameter psImage structure
+    // pointer is null.
+    retVal = psImageClipNaN(NULL,-1.0f);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for clips of a NULL image.");
+        return 3;
+    }
+
+    // Verify program execution doesn't stop if the input image type is something
+    // other than F32, F64, C32, C64.
+    img = psImageAlloc(c,r,PS_TYPE_S32);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error (invalid type)");
+    retVal = psImageClipNaN(img,2.0f);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for clip of invalid image type.");
+        return 4;
+    }
+    psFree(img);
+
+    return 0;
+}
+
+psS32 testImageClipComplexRegion(void)
+{
+    psImage* img = NULL;
+    psU32 c = 1024;
+    psU32 r = 2048;
+    psS32 numClipped = 0;
+    psS32 retVal;
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "psImageClipNaN shall modified pixel values of NaN with a specified value");
+
+    /*
+    1. Create a complex image with a wide range of complex values
+
+    2. call psImageClipComplexRegion with min and max where there is at least
+       2 pixels in the image above) that is:
+        a) real(p) < real(min) && complex(p) < complex(min),
+        b) real(p) < real(min) && complex(min) < complex(p) < complex(max)
+        c) real(min) < real(p) < real(max) && complex(p) < complex(min)
+        d) real(min) < real(p) < real(max) && complex(min) < complex(p) < complex(max)
+        e) real(p) > real(max) && complex(min) < complex(p) < complex(max)
+        f) real(pmin) < real(p) < real(max) && complex(p) > complex(max)
+        g) real(p) > real(max) && complex(p) > complex(max)
+        h) real(p) < real(min) && complex(p) > complex(max)
+        i) real(p) > real(max) && complex(p) < complex(min)
+
+    3. verify that All pixels in case (a), (b), and (c) have the value vmin
+
+    4. verify that all pixels in case (d) are unchanged from input
+
+    5. verify that all pixels in case (e), (f), (g), (h), and (i) have the
+       value vmax
+
+    */
+
+    #define testImageClipComplexByType(datatype,MIN,MAX) /* datatype must be complex */ \
+    /* create image */ \
+    img = psImageAlloc(c,r,PS_TYPE_##datatype); \
+    for (unsigned row=0;row<r;row++) { \
+        ps##datatype* imgRow = img->data.datatype[row]; \
+        for (unsigned col=0;col<c;col++) { \
+            imgRow[col] = row+I*col; \
+        } \
+    } \
+    \
+    retVal = psImageClipComplexRegion(img,MIN,-1.0-1.0*I,MAX,-2.0-2.0*I); \
+    \
+    numClipped = 0; \
+    for (unsigned row=0;row<r;row++) { \
+        ps##datatype* imgRow = img->data.datatype[row]; \
+        for (unsigned col=0;col<c;col++) { \
+            ps##datatype value = (ps##datatype)(row+I*col); \
+            if ( (row > creal(MAX)) || (col > cimag(MAX)) ) { \
+                numClipped++; \
+                value = -2.0-2.0*I; \
+            } else if ((row < creal(MIN)) || (col < cimag(MIN)) ) { \
+                numClipped++; \
+                value = -1.0-1.0*I; \
+            } \
+            if (cabs(imgRow[col]-value) > FLT_EPSILON) { \
+                psError(PS_ERR_UNKNOWN, true,"Pixel value is not as expected (%g%+gi vs %g%+gi) at %d,%d", \
+                        creal(imgRow[col]),cimag(imgRow[col]),creal(value),cimag(value),col,row); \
+                return 1; \
+            } \
+        } \
+    } \
+    if (retVal != numClipped) { \
+        psError(PS_ERR_UNKNOWN, true,"Expected %d clips, but got %d", \
+                numClipped,retVal); \
+        return 2; \
+    } \
+    psFree(img);
+
+    complex double min = ((double)r)/5.0+I*((double)c)/4.0;
+    complex double max = ((double)r)/3.0+I*((double)c)/2.0;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping at %g%+gi to %g%+gi for psC32",
+             creal(min),cimag(min),creal(max),cimag(max));
+
+    testImageClipComplexByType(C32,min,max);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Testing clipping at %g%+gi to %g%+gi for psC64",
+             creal(min),cimag(min),creal(max),cimag(max));
+    testImageClipComplexByType(C64,min,max);
+
+    //  6. Call psImageClipComplexRegion with NULL input parameter; should error
+    //     but not stop execution.
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(NULL,0,0,0,0);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for clips of a NULL image.");
+        return 3;
+    }
+
+
+    img = psImageAlloc(c,r,PS_TYPE_C32);
+    for (unsigned row=0;row<r;row++) {
+        psC32* imgRow = img->data.C32[row];
+        for (unsigned col=0;col<c;col++) {
+            imgRow[col] = row+I*col;
+        }
+    }
+
+    //  7. Call psImageClipComplexRegion with min > max; should error and return 0,
+    //     but not stop execution
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,10.0+I*1.0,-1.0,5.0+5.0*I,-2.0);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for creal(min)>creal(max).");
+        return 3;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,1.0+I*10.0,-1.0,5.0+5.0*I,-2.0);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for cimag(min)>cimag(max).");
+        return 3;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,10.0+I*10.0,-1.0,5.0+5.0*I,-2.0);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for min>max.");
+        return 3;
+    }
+
+    //  8. Call psImageClipComplexRegion with the follow vmin/vmax values; each
+    //     should error and return 0, but not stop execution
+    //      a) vmin < datatype region's minimum
+    //      b) vmax < datatype region's minimum
+    //      c) vmin > datatype region's maximum
+    //      d) vmax > datatype region's maximum
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,
+                                      1.0+I*1.0,
+                                      -2.0*(double)FLT_MAX,
+                                      5.0+5.0*I,
+                                      0.0);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for vmin not in datatype range.");
+        return 80;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,
+                                      1.0+I*1.0,
+                                      2.0*(double)FLT_MAX,
+                                      5.0+5.0*I,
+                                      0.0);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for vmin not in datatype range.");
+        return 81;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,
+                                      1.0+I*1.0,
+                                      FLT_EPSILON-2.0*(double)FLT_MAX*I,
+                                      5.0+5.0*I,
+                                      0.0);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for vmin not in datatype range.");
+        return 82;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,
+                                      1.0+I*1.0,
+                                      FLT_EPSILON+2.0*(double)FLT_MAX*I,
+                                      5.0+5.0*I,
+                                      0.0);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for vmin not in datatype range.");
+        return 83;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,
+                                      1.0+I*1.0,
+                                      0.0,
+                                      5.0+5.0*I,
+                                      -2.0*(double)FLT_MAX);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for vmax not in datatype range.");
+        return 84;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,
+                                      1.0+I*1.0,
+                                      0.0,
+                                      5.0+5.0*I,
+                                      2.0*(double)FLT_MAX);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for vmax not in datatype range.");
+        return 85;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,
+                                      1.0+I*1.0,
+                                      0.0,
+                                      5.0+5.0*I,
+                                      FLT_EPSILON-2.0*(double)FLT_MAX*I);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for vmax not in datatype range.");
+        return 87;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error:");
+    retVal = psImageClipComplexRegion(img,
+                                      1.0+I*1.0,
+                                      0.0,
+                                      5.0+5.0*I,
+                                      FLT_EPSILON+2.0*(double)FLT_MAX*I);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for vmax not in datatype range.");
+        return 88;
+    }
+
+
+    // now check if vmin > vmax is OK
+    for (unsigned row=0;row<r;row++) {
+        psC32* imgRow = img->data.C32[row];
+        for (unsigned col=0;col<c;col++) {
+            imgRow[col] = row+I*col;
+        }
+    }
+    retVal = psImageClipComplexRegion(img,
+                                      1.0+I*1.0,
+                                      10.0,
+                                      5.0+5.0*I,
+                                      0.0);
+    if (retVal == 0) {
+        psError(PS_ERR_UNKNOWN, true,"Didn't expect zero return for vmin > vmax.");
+        return 83;
+    }
+
+
+    psFree(img);
+    img = NULL;
+
+    //  9. Call psImageClipComplexRegion with the max value out of datatype's
+    //     range; should clip as expected (see step 1-5). Repeat with min value
+    //     out of datatype's range.
+
+    testImageClipComplexByType(C32,-(double)FLT_MAX*2.0-I*(double)FLT_MAX*2.0,10.0+I*10.0);
+    testImageClipComplexByType(C32,10.0+I*10.0,(double)FLT_MAX*2.0+I*(double)FLT_MAX*2.0);
+
+    // Verify program execution doesn't stop if the input image type is something
+    // other than C32, C64.
+    img = psImageAlloc(c,r,PS_TYPE_S32);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error (invalid type)");
+    retVal = psImageClipComplexRegion(img,2.0,10.0,5.0,0.0);
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Expected zero return for clip of invalid image type.");
+        return 84;
+    }
+    psFree(img);
+
+    return 0;
+}
+
+psS32 testImageOverlay(void)
+{
+
+    psImage* img = NULL;
+    psImage* img2 = NULL;
+    psImage* img3 = NULL;
+    psImage* img4 = NULL;
+    psU32 c = 128;
+    psU32 r = 256;
+    psS32 retVal;
+
+    /*
+    psImageSectionOverlay shall modified pixel values in a psImage structure to
+    be equal to the value of the originaldata and an overlay image with a
+    specified operation. Valid operations include =, +, -, *, /.
+
+    Verify the returned integer is zero
+    and the input parameter psImage structure is modified at the specified
+    location and range with the given overlay image and the specified
+    function. Cases should include all the valid operations. Comparison of
+    expected values should include a delta to allow for testing on
+    different platforms.
+
+    */
+
+    #define testOverlayTypeOP(DATATYPE,OP,OPSTRING) \
+    img = psImageAlloc(c,r,PS_TYPE_##DATATYPE); \
+    for (unsigned row=0;row<r;row++) { \
+        ps##DATATYPE* imgRow = img->data.DATATYPE[row]; \
+        for (unsigned col=0;col<c;col++) { \
+            imgRow[col] = 6.0; \
+        } \
+    } \
+    img2 = psImageAlloc(c/2,r/2,PS_TYPE_##DATATYPE); \
+    for (unsigned row=0;row<r/2;row++) { \
+        ps##DATATYPE* img2Row = img2->data.DATATYPE[row]; \
+        for (unsigned col=0;col<c/2;col++) { \
+            img2Row[col] = 2.0; \
+        } \
+    } \
+    retVal = psImageOverlaySection(img,img2,c/4,r/4,OPSTRING); \
+    if (retVal == 0) { \
+        psError(PS_ERR_UNKNOWN, true,"psImageOverlaySection returned zero with %s op", \
+                OPSTRING); \
+        return 1; \
+    } \
+    for (unsigned row=0;row<r;row++) { \
+        ps##DATATYPE* imgRow = img->data.DATATYPE[row]; \
+        ps##DATATYPE* img2Row = img2->data.DATATYPE[row]; \
+        for (unsigned col=0;col<c;col++) { \
+            ps##DATATYPE val = 6.0; \
+            if ( ! (row < r/4 || row >= r/2+r/4 || col < c/4 || col >= c/2+c/4)) { \
+                val OP 2.0; \
+            } \
+            if (fabsf(imgRow[col] - val) > FLT_EPSILON) { \
+                psError(PS_ERR_UNKNOWN, true,"Value incorrect at %d,%d (%.2f vs %.2f for %s)", \
+                        col,row,imgRow[col],val,OPSTRING); \
+                return 2; \
+            } \
+            if (row < r/2 && col < c/2 && fabsf(img2Row[col] - 2.0) > FLT_EPSILON) { \
+                psError(PS_ERR_UNKNOWN, true,"Overlay modified at %d,%d (%.2f for %s)", \
+                        col,row,img2Row[col],OPSTRING); \
+                return 2; \
+            } \
+        } \
+    } \
+    psFree(img); \
+    psFree(img2);
+
+    #define testOverlayType(DATATYPE) \
+    testOverlayTypeOP(DATATYPE,+=,"+"); \
+    testOverlayTypeOP(DATATYPE,-=,"-"); \
+    testOverlayTypeOP(DATATYPE,*=,"*");\
+    testOverlayTypeOP(DATATYPE,/=,"/");\
+    testOverlayTypeOP(DATATYPE,=,"=");
+
+    testOverlayType(C64);
+    testOverlayType(C32);
+    testOverlayType(F64);
+    testOverlayType(F32);
+    testOverlayType(S16);
+    testOverlayType(S8);
+    testOverlayType(U16);
+    testOverlayType(U8);
+
+    /*
+    Verify the returned integer is equal to non-zero and the input psImage structure
+    is unmodified, if the overlay specified is not within the data range of the
+    input psImage structure.
+    */
+
+    img = psImageAlloc(c,r,PS_TYPE_F32);
+    for (unsigned row=0;row<r;row++) {
+        psF32* imgRow = img->data.F32[row];
+        for (unsigned col=0;col<c;col++) {
+            imgRow[col] = 6.0f;
+        }
+    }
+    img2 = psImageAlloc(c,r,PS_TYPE_F32);
+    for (unsigned row=0;row<r;row++) {
+        psF32* img2Row = img2->data.F32[row];
+        for (unsigned col=0;col<c;col++) {
+            img2Row[col] = 2.0f;
+        }
+    }
+    img3 = psImageAlloc(c,r,PS_TYPE_S64);
+    for (unsigned row=0;row<r;row++) {
+        psS64* img3Row = img3->data.S64[row];
+        for (unsigned col=0;col<c;col++) {
+            img3Row[col] = 6.0f;
+        }
+    }
+    img4 = psImageAlloc(c,r,PS_TYPE_S64);
+    for (unsigned row=0;row<r;row++) {
+        psS64* img4Row = img4->data.S64[row];
+        for (unsigned col=0;col<c;col++) {
+            img4Row[col] = 2.0f;
+        }
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should error as overlay isn't "
+             "within image boundaries");
+    retVal = psImageOverlaySection(img,img2,c/4,r/4,"+");
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"psImageOverlaySection did not return "
+                "zero even though overlay too big");
+        return 3;
+    }
+    for (unsigned row=0;row<r;row++) {
+        psF32* imgRow = img->data.F32[row];
+        for (unsigned col=0;col<c;col++) {
+            if (imgRow[col] != 6.0f) {
+                psError(PS_ERR_UNKNOWN, true,"Input image modified when overlay size too big");
+                return 4;
+            }
+        }
+    }
+
+    /*
+    Verify the returned integer is equal to non-zero, the input psImage
+    structure is unmodified and program execution doesn't stop, if the
+    overlay specified is null.
+    */
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should error as overlay is NULL");
+    retVal = psImageOverlaySection(img,NULL,c/4,r/4,"+");
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"psImageOverlaySection did not return "
+                "zero even though overlay too big");
+        return 5;
+    }
+    for (unsigned row=0;row<r;row++) {
+        psF32* imgRow = img->data.F32[row];
+        for (unsigned col=0;col<c;col++) {
+            if (imgRow[col] != 6.0f) {
+                psError(PS_ERR_UNKNOWN, true,"Input image modified when overlay NULL");
+                return 6;
+            }
+        }
+    }
+
+    /*
+    Verify the returned integer is equal to non-zero and program execution
+    doesn't stop, if the input parameter image is null.
+    */
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should error as image input is NULL");
+    retVal = psImageOverlaySection(NULL,img2,c/4,r/4,"+");
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"psImageOverlaySection returned non-zero even though "
+                "overlay too big");
+        return 7;
+    }
+
+    /*
+    Verify the return integer is equal to non-zero and program execution
+    doesn't stop, if the specified operator is not =,+,-,*,/
+    */
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should error as operator is invalid");
+    retVal = psImageOverlaySection(img,img2,0,0,"$");
+    if (retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"psImageOverlaySection returned non-zero even though "
+                "overlay operator is invalid");
+        return 8;
+    }
+
+    /*
+    Verify the return integer is equal to non-zero and program execution
+    doesn't stop, if the specified operator is NULL
+    */
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should error as operator is invalid");
+    retVal = psImageOverlaySection(img,img2,0,0,NULL);
+    if(retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"psImageOverlaySection returned non-zero even though "
+                "overlay operator is NULL");
+        return 9;
+    }
+
+    /*
+    Verify the return integer is equal to non-zero and program execution
+    doesn't stop, if overlay image is a different type than the input image
+    */
+    psLogMsg(__func__,PS_LOG_INFO,"Following should error as overlay is "
+             "a different type");
+    retVal = psImageOverlaySection(img,img3,0,0,"+");
+    if(retVal != 0) {
+        psError(PS_ERR_UNKNOWN, true,"psImageOverlaySection returned nonzero eventhough "
+                " overlay image type is different than input image.");
+        return 10;
+    }
+
+    /*
+    Verify program execution doen't stop, if the overly image contains
+    zero values with division operation is specified.
+    */
+    for (unsigned row=0;row<r;row++) {
+        psF32* img2Row = img2->data.F32[row];
+        for (unsigned col=0;col<c;col++) {
+            img2Row[col] = 0.0f;
+        }
+    }
+    retVal = psImageOverlaySection(img,img2,0,0,"/");
+    if (retVal == 0) {
+        psError(PS_ERR_UNKNOWN, true,"psImageOverlaySection returned zero when "
+                "checking divide-by-zero.");
+        return 12;
+    }
+
+    psFree(img);
+    psFree(img2);
+    psFree(img3);
+    psFree(img4);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageSmooth.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageSmooth.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageSmooth.c	(revision 22322)
@@ -0,0 +1,179 @@
+/** @file  tst_psImageConvolve.c
+ *
+ *  This code will test the psImageSmooth() routine.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-05-09 03:20:00 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "psType.h"
+
+static psS32 testImageSmooth();
+
+testDescription tests[] = {
+                              {testImageSmooth, 0, "psImageSmooth", 0, false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+
+    return ! runTestSuite(stderr, "psImage", tests, argc, argv);
+}
+
+#define NUM_ROWS 20
+#define NUM_COLS 20
+#define SIGMA 1.0
+#define NSIGMA 5.0
+#define TS00_IM_NULL            0x00000001
+#define TS00_IM_F32             0x00000002
+#define TS00_IM_F64             0x00000004
+#define TS00_IM_S32             0x00000008
+#define VERBOSE 0
+
+static psBool testImageSmoothGeneric(
+    psU32 flags,
+    psS32 numCols,
+    psS32 numRows,
+    psF64 sigma,
+    psF64 Nsigma,
+    psBool expectedRC)
+{
+    psBool testStatus = true;
+    psImage *img = NULL;
+
+    printPositiveTestHeader(stdout, "psImageConvolve.c", "Image Smoothing Routine");
+
+    if (expectedRC == false) {
+        printf("This test should generate FALSE.\n");
+    } else {
+        printf("This test should generate TRUE.\n");
+    }
+
+    if (flags & TS00_IM_NULL) {
+        printf("        using a NULL image\n");
+    }
+
+    if (flags & TS00_IM_F32) {
+        printf("        using a psF32 image\n");
+        img = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        // Set a checkboard pattern
+        for (psS32 i = 0 ; i < numRows ; i++) {
+            for (psS32 j = 0 ; j < numCols ; j++) {
+                if ((i%2) != (j%2)) {
+                    img->data.F32[i][j] = 1.0;
+                } else {
+                    img->data.F32[i][j] = 0.0;
+                }
+            }
+        }
+    }
+    if (VERBOSE) {
+        p_psImagePrint(1, img, "The Smoothed Image");
+    }
+
+    if (flags & TS00_IM_F64) {
+        printf("        using a psF64 image\n");
+        img = psImageAlloc(numCols, numRows, PS_TYPE_F64);
+        // Set a checkboard pattern
+        for (psS32 i = 0 ; i < numRows ; i++) {
+            for (psS32 j = 0 ; j < numCols ; j++) {
+                if ((i%2) != (j%2)) {
+                    img->data.F64[i][j] = 1.0;
+                } else {
+                    img->data.F64[i][j] = 0.0;
+                }
+            }
+        }
+    }
+
+    if (flags & TS00_IM_S32) {
+        printf("        using a psS32 image\n");
+        img = psImageAlloc(numCols, numRows, PS_TYPE_S32);
+        // Set a checkboard pattern
+        for (psS32 i = 0 ; i < numRows ; i++) {
+            for (psS32 j = 0 ; j < numCols ; j++) {
+                if ((i%2) != (j%2)) {
+                    img->data.S32[i][j] = 1;
+                } else {
+                    img->data.S32[i][j] = 0;
+                }
+            }
+        }
+    }
+    printf(" %d columns\n", numCols);
+    printf(" %d rows\n", numRows);
+    printf(" sigma is %.2f\n", sigma);
+    printf(" Nsigma is %.2f\n", Nsigma);
+
+    psBool rc = psImageSmooth(img, sigma, Nsigma);
+    if (rc == false) {
+        if (expectedRC == true) {
+            printf("TEST ERROR: psImageSmooth returned FALSE\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            printf("TEST ERROR: psImageSmooth returned TRUE\n");
+            testStatus = false;
+        }
+        if (VERBOSE) {
+            p_psImagePrint(1, img, "The Smoothed Image");
+        }
+
+        if (flags & TS00_IM_F32) {
+            for (psS32 i = 1 ; i < numRows-1 ; i++) {
+                for (psS32 j = 1 ; j < numCols-1 ; j++) {
+                    if ((fabs(img->data.F32[i][j] - 0.5) > 0.1)) {
+                        printf("TEST ERROR: img[%d][%d] was %f, expected 0.5\n",
+                               i, j, img->data.F32[i][j]);
+                        testStatus = false;
+                    }
+                }
+            }
+        }
+
+        if (flags & TS00_IM_F64) {
+            for (psS32 i = 1 ; i < numRows-1 ; i++) {
+                for (psS32 j = 1 ; j < numCols-1 ; j++) {
+                    if ((fabs(img->data.F64[i][j] - 0.5) > 0.1)) {
+                        printf("TEST ERROR: img[%d][%d] was %f, expected 0.5\n",
+                               i, j, img->data.F64[i][j]);
+                        testStatus = false;
+                    }
+                }
+            }
+        }
+    }
+    psFree(img);
+
+    return(testStatus);
+}
+
+static psS32 testImageSmooth()
+{
+    int bad = 0x00;            // What's bad?
+
+    bad |= (testImageSmoothGeneric(TS00_IM_F32, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, true)    ? 0x00 : 0x01);
+    bad |= (testImageSmoothGeneric(TS00_IM_F32, 1, NUM_ROWS, SIGMA, NSIGMA, true)           ? 0x00 : 0x02);
+    bad |= (testImageSmoothGeneric(TS00_IM_F32, NUM_COLS, 1, SIGMA, NSIGMA, true)           ? 0x00 : 0x04);
+    bad |= (testImageSmoothGeneric(TS00_IM_F32, 1, 1, SIGMA, NSIGMA, true)                  ? 0x00 : 0x08);
+    bad |= (testImageSmoothGeneric(TS00_IM_F64, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, true)    ? 0x00 : 0x10);
+    bad |= (testImageSmoothGeneric(TS00_IM_S32, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, false)   ? 0x00 : 0x20);
+    bad |= (testImageSmoothGeneric(TS00_IM_NULL, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, false)  ? 0x00 : 0x40);
+
+    return bad;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageStats.c	(revision 22322)
@@ -0,0 +1,819 @@
+/*
+*  C Implementation: %{MODULE}
+*
+* Description:
+*
+*
+* Author: %{AUTHOR} <%{EMAIL}>, (C) %{YEAR}
+*
+* Copyright: See COPYING file that comes with this distribution
+*
+*/
+
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testPsImageHistogram(void);
+static psS32 testPsImageStats(void);
+static psS32 testPsImageFitPolynomial(void);
+static psS32 testPsImagePixelInterpolate(void);
+static psS32 testPsImageEvalPolynom(void);
+static psS32 testImageCountPixel(void);
+
+static bool FitChebyF32(int numCols, int numRows);
+static bool FitChebyF64(int numCols, int numRows);
+
+testDescription tests[] = {
+                              {testPsImageHistogram, 0, "psImageHistogram", 0, false},
+                              {testPsImageStats, 1, "psImageStats", 0, false},
+                              {testPsImageFitPolynomial, 2, "psImageFitPolynomial", 0, false},
+                              {testPsImagePixelInterpolate, 3, "psImagePixelInterpolate", 0, false},
+                              {testPsImageEvalPolynom, 4, "psImageEvalPolynom()", 0, false},
+                              {testImageCountPixel, 5, "psImageCountPixel", 0, false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+
+    return ! runTestSuite(stderr,"psImage",tests,argc,argv);
+}
+
+
+
+static psS32 testPsImageHistogram(void)
+{
+    const int NUM_BINS = 20;
+    const int N = 32;
+    const int M = 64;
+
+    psHistogram * myHist = NULL;
+    psHistogram *myHist2 = NULL;
+    psImage *tmpImage = NULL;
+    psImage *tmpImage2 = NULL;
+    psImage *tmpMask = NULL;
+    psImage *tmpMask2 = NULL;
+    psS32 testStatus = true;
+    psS32 nb = 0;
+    psS32 i = 0;
+    psS32 j = 0;
+    psS32 IMAGE_X_SIZE = 0;
+    psS32 IMAGE_Y_SIZE = 0;
+    psS32 currentId = 0;
+
+    currentId = psMemGetId();
+    for ( nb = 0;nb < 7;nb++ ) {
+        if ( nb == 0 ) {
+            IMAGE_X_SIZE = 1;
+            IMAGE_Y_SIZE = 1;
+        }
+        if ( nb == 1 ) {
+            IMAGE_X_SIZE = 1;
+            IMAGE_Y_SIZE = N;
+        }
+        if ( nb == 2 ) {
+            IMAGE_X_SIZE = N;
+            IMAGE_Y_SIZE = 1;
+        }
+        if ( nb == 3 ) {
+            IMAGE_X_SIZE = N;
+            IMAGE_Y_SIZE = N;
+        }
+        if ( nb == 4 ) {
+            IMAGE_X_SIZE = N;
+            IMAGE_Y_SIZE = M;
+        }
+        if ( nb == 5 ) {
+            IMAGE_X_SIZE = M;
+            IMAGE_Y_SIZE = N;
+        }
+        if ( nb == 6 ) {
+            IMAGE_X_SIZE = M;
+            IMAGE_Y_SIZE = N;
+        }
+        fprintf(stderr, "*******************************\n" );
+        if (nb != 6) {
+            fprintf(stderr, "* IMAGE SIZE is (%d by %d)\n", IMAGE_X_SIZE, IMAGE_Y_SIZE );
+        } else {
+            fprintf(stderr, "* IMAGE SUBSET SIZE is (%d by %d)\n", IMAGE_X_SIZE, IMAGE_Y_SIZE );
+        }
+        fprintf(stderr, "*******************************\n" );
+        /*********************************************************************/
+        /*  Allocate and initialize data structures                      */
+        /*********************************************************************/
+        if (nb != 6) {
+            tmpImage = psImageAlloc( IMAGE_X_SIZE, IMAGE_Y_SIZE, PS_TYPE_F32 );
+            tmpMask = psImageAlloc( IMAGE_X_SIZE, IMAGE_Y_SIZE, PS_TYPE_U8 );
+            tmpImage2 = NULL;
+            tmpMask2 = NULL;
+        } else {
+            tmpImage2 = psImageAlloc( IMAGE_X_SIZE*2, IMAGE_Y_SIZE*2, PS_TYPE_F32 );
+            tmpMask2 = psImageAlloc( IMAGE_X_SIZE*2, IMAGE_Y_SIZE*2, PS_TYPE_U8 );
+            tmpImage = psImageSubset(tmpImage2, psRegionSet(IMAGE_X_SIZE/4, IMAGE_X_SIZE/4+IMAGE_X_SIZE,
+                                     IMAGE_Y_SIZE/4, IMAGE_Y_SIZE/4+IMAGE_Y_SIZE));
+            tmpMask= psImageSubset(tmpMask2, psRegionSet(IMAGE_X_SIZE/4, IMAGE_X_SIZE/4+IMAGE_X_SIZE,
+                                   IMAGE_Y_SIZE/4, IMAGE_Y_SIZE/4+IMAGE_Y_SIZE));
+        }
+
+
+        for ( i = 0;i < tmpImage->numRows;i++ ) {
+            for ( j = 0;j < tmpImage->numCols;j++ ) {
+                tmpImage->data.F32[ i ][ j ] = ( float ) ( i + j + 0.1 );
+            }
+        }
+
+
+        for ( i = 0;i < tmpMask->numRows;i++ ) {
+            for ( j = 0;j < tmpMask->numCols;j++ ) {
+                if ( ( i > ( tmpMask->numRows / 2 ) ) &&
+                        ( j > ( tmpMask->numCols / 2 ) ) ) {
+                    tmpMask->data.U8[ i ][ j ] = 1;
+                } else {
+                    tmpMask->data.U8[ i ][ j ] = 0;
+                }
+            }
+        }
+
+        /*************************************************************************/
+        /*  Calculate Histogram with no mask                             */
+        /*************************************************************************/
+
+        myHist = psHistogramAlloc( 0.0, ( float ) ( IMAGE_X_SIZE + IMAGE_Y_SIZE ),
+                                   NUM_BINS );
+        myHist = psImageHistogram( myHist, tmpImage, NULL, 0 );
+        for ( i = 0;i < NUM_BINS;i++ ) {
+            fprintf(stderr, "Bin number %d bounds: (%.1f - %.1f) data (%.2f)\n", i,
+                    myHist->bounds->data.F32[ i ],
+                    myHist->bounds->data.F32[ i + 1 ],
+                    myHist->nums->data.F32[ i ] );
+        }
+        psFree( myHist );
+
+        /*************************************************************************/
+        /*  Calculate Histogram with mask                                */
+        /*************************************************************************/
+
+        myHist = psHistogramAlloc( 0.0, ( float ) ( IMAGE_X_SIZE + IMAGE_Y_SIZE ),
+                                   NUM_BINS );
+        myHist = psImageHistogram( myHist, tmpImage, tmpMask, 1 );
+        for ( i = 0;i < NUM_BINS;i++ ) {
+            fprintf( stderr, "Bin number %d bounds: (%.2f - %.2f) data (%.2f)\n", i,
+                     myHist->bounds->data.F32[ i ],
+                     myHist->bounds->data.F32[ i + 1 ],
+                     myHist->nums->data.F32[ i ] );
+        }
+
+        /*************************************************************************/
+        /*  Deallocate data structures                                   */
+        /*************************************************************************/
+        psFree( myHist );
+        psFree( tmpImage );
+        psFree( tmpImage2 );
+        psFree( tmpMask );
+        psFree( tmpMask2 );
+    }
+    tmpImage = psImageAlloc( IMAGE_X_SIZE, IMAGE_Y_SIZE, PS_TYPE_F32 );
+    myHist = psHistogramAlloc( 0.0, ( float ) ( IMAGE_X_SIZE + IMAGE_Y_SIZE ),
+                               NUM_BINS );
+    myHist2 = psImageHistogram( NULL, tmpImage, NULL, 0 );
+    if ( myHist2 != NULL ) {
+        fprintf(stderr, "ERROR: myHist2 not equal to NULL\n" );
+    }
+
+    myHist2 = psImageHistogram( myHist, NULL, NULL, 0 );
+    myHist2 = psImageHistogram( NULL, tmpImage, NULL, 0 );
+    if ( myHist2 != NULL ) {
+        fprintf(stderr, "ERROR: myHist2 not equal to NULL\n" );
+    }
+
+
+    tmpMask = psImageAlloc( IMAGE_X_SIZE, IMAGE_Y_SIZE, PS_TYPE_S8 );
+    for ( i = 0;i < tmpMask->numRows;i++ ) {
+        for ( j = 0;j < tmpMask->numCols;j++ ) {
+            if ( ( i > ( tmpMask->numRows / 2 ) ) &&
+                    ( j > ( tmpMask->numCols / 2 ) ) ) {
+                tmpMask->data.S8[ i ][ j ] = 1;
+            } else {
+                tmpMask->data.S8[ i ][ j ] = 0;
+            }
+        }
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error for invalid mask type.");
+    myHist2 = psImageHistogram(myHist,tmpImage,tmpMask,1);
+    if (myHist2 != NULL) {
+        fprintf(stderr,"ERROR: myHist2 not equal to NULL\n");
+    }
+
+    psFree( tmpMask );
+    psFree(myHist);
+    psFree(myHist2);
+    psFree( tmpImage );
+
+    return ( !testStatus );
+}
+
+static psS32 testPsImageFitPolynomial()
+{
+    const int IMAGE_SIZE = 16;
+    const int CHEBY_X_DIM = 8;
+    const int CHEBY_Y_DIM = 8;
+    const int THRESHOLD = 10;
+
+    psStats * myStats = NULL;
+    psImage *tmpImage = NULL;
+    psImage *outImage = NULL;
+    psImage *nullImage = NULL;
+    psPolynomial2D *my2DPoly = NULL;
+    psPolynomial2D *null2DPoly = NULL;
+    psS32 testStatus = true;
+    psS32 i = 0;
+    psS32 j = 0;
+    psS32 currentId = 0;
+
+    currentId = psMemGetId();
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    tmpImage = psImageAlloc( IMAGE_SIZE, IMAGE_SIZE, PS_TYPE_F32 );
+    outImage = psImageAlloc( IMAGE_SIZE, IMAGE_SIZE, PS_TYPE_F32 );
+    for ( i = 0;i < IMAGE_SIZE;i++ ) {
+        for ( j = 0;j < IMAGE_SIZE;j++ ) {
+            tmpImage->data.F32[ i ][ j ] = 4.0;
+            tmpImage->data.F32[ i ][ j ] = ( float ) ( i + j );
+            tmpImage->data.F32[ i ][ j ] = ( float ) ( i + j ) + ( 4.0 * ( float ) i );
+            outImage->data.F32[ i ][ j ] = 0.0;
+        }
+    }
+    my2DPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, CHEBY_X_DIM-1, CHEBY_Y_DIM-1);
+    /*************************************************************************/
+    /*  Calculate Chebyshev Polynomials, no mask                     */
+    /*************************************************************************/
+    my2DPoly = psImageFitPolynomial( my2DPoly, tmpImage );
+    if (my2DPoly == NULL) {
+        printf("TEST ERROR: psImageFitPolynomial() returned NULL.\n");
+        testStatus = false;
+    }
+    for ( i = 0;i < CHEBY_X_DIM;i++ ) {
+        for ( j = 0;j < CHEBY_Y_DIM;j++ ) {
+            fprintf(stderr, "Cheby Polynomial (%d, %d) coefficient is %.2f\n", i, j,
+                    0.0001f+my2DPoly->coeff[ i ][ j ] );
+        }
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error message for NULL coeffs argument.");
+    null2DPoly = psImageFitPolynomial( NULL, tmpImage );
+    if ( null2DPoly != NULL ) {
+        fprintf(stderr,"ERROR: psImageFitPolynomial did not return NULL with invalid coeffs argument.");
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be a error message for NULL input argument.");
+    null2DPoly = psImageFitPolynomial( null2DPoly, NULL );
+    if ( null2DPoly != NULL ) {
+        fprintf(stderr,"ERROR: psImageFitPolynomial did not return NULL with null input image.");
+    }
+
+    /*************************************************************************/
+    /*  Evaluate Chebyshev Polynomials, no mask                      */
+    /*************************************************************************/
+    outImage = psImageEvalPolynomial( outImage, my2DPoly );
+    for ( i = 0;i < IMAGE_SIZE;i++ ) {
+        for ( j = 0;j < IMAGE_SIZE;j++ ) {
+
+            //             fprintf(stderr,"pixel[%d][%d] is (%.2f, %.2f)\n", i, j,
+            //                     tmpImage->data.F32[i][j],
+            //                     outImage->data.F32[i][j]);
+            if ( fabs( outImage->data.F32[ i ][ j ] - tmpImage->data.F32[ i ][ j ] ) > THRESHOLD ) {
+                fprintf(stderr, "Pixel (%d, %d) is %.2f, should be %.2f\n", i, j,
+                        outImage->data.F32[ i ][ j ],
+                        tmpImage->data.F32[ i ][ j ] );
+            }
+
+        }
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error message for NULL coeffs argument.");
+    nullImage = psImageEvalPolynomial( outImage, NULL );
+    if ( nullImage != NULL ) {
+        fprintf(stderr,"ERROR: psImageEvalPolynomial did not return NULL with invalid coeffs argument.");
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be a error message for NULL input argument.");
+    nullImage = psImageEvalPolynomial( NULL, my2DPoly );
+    if ( nullImage != NULL ) {
+        fprintf(stderr,"ERROR: psImageEvalPolynomial did not return NULL with null input image.");
+    }
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                   */
+    /*************************************************************************/
+    psFree( myStats );
+    psFree( tmpImage );
+    psFree( outImage );
+    psFree( my2DPoly );
+
+    return ( !testStatus );
+}
+
+static psS32 testPsImagePixelInterpolate()
+{
+    const int IMAGE_SIZE = 10;
+
+    psImage *tmpImage   = NULL;
+    psS32 testStatus      = true;
+    psS32 i               = 0;
+    psS32 j               = 0;
+    float pixel         = 0.0;
+    float x             = 0.0;
+    float y             = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    tmpImage = psImageAlloc(IMAGE_SIZE, IMAGE_SIZE, PS_TYPE_F32);
+    for (i=0;i<IMAGE_SIZE;i++) {
+        for (j=0;j<IMAGE_SIZE;j++) {
+            tmpImage->data.F32[i][j] = 4.0;
+            tmpImage->data.F32[i][j] = (float) (i + j) + (4.0 * (float) i);
+            tmpImage->data.F32[i][j] = (float) (i + j);
+            fprintf(stderr,"%.1f ", tmpImage->data.F32[i][j]);
+        }
+        fprintf(stderr,"\n");
+    }
+    for (i=0;i<IMAGE_SIZE-1;i++) {
+        for (j=0;j<IMAGE_SIZE-1;j++) {
+            x = 0.2 + (float) i;
+            y = 0.2 + (float) j;
+            pixel = psImagePixelInterpolate(tmpImage,
+                                            x, y,
+                                            NULL, 0,
+                                            0,
+                                            PS_INTERPOLATE_BILINEAR);
+            fprintf(stderr,"%.1f ", pixel);
+        }
+        fprintf(stderr,"\n");
+    }
+
+    for (i=0;i<IMAGE_SIZE-1;i++) {
+        for (j=0;j<IMAGE_SIZE-1;j++) {
+            x = 0.2 + (float) i;
+            y = 0.2 + (float) j;
+            pixel = psImagePixelInterpolate(tmpImage,
+                                            x, y,
+                                            NULL,0,
+                                            0,
+                                            PS_INTERPOLATE_BILINEAR);
+            fprintf(stderr,"image[%.1f][%.1f] is interpolated at %.1f\n", x, y, pixel);
+        }
+    }
+
+    psFree(tmpImage);
+
+    return (!testStatus);
+}
+
+#define I_POLY(X, Y) \
+((A) + (B * X) + (C * Y) + (D * X * Y) + (E * X * X) + (F * Y * Y)) \
+
+static bool FitChebyF32(int numCols, int numRows)
+{
+    const double A = 1.0;
+    const double B = 2.0;
+    const double C = 3.0;
+    const double D = 4.0;
+    const double E = 5.0;
+    const double F = 6.0;
+    const double ERROR_TOL = 0.1;
+
+    psImage *tmpImage     = NULL;
+    psImage *outImage     = NULL;
+    bool testStatus      = true;
+    psS32 i               = 0;
+    psS32 j               = 0;
+    float chi2 = 0.0;
+
+
+    fprintf(stderr,"psImageFitPolynomial(), psImageEvalPolynom(): (%d by %d)\n", numRows, numCols);
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+
+    tmpImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    outImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            tmpImage->data.F32[i][j] = I_POLY(((float) i), ((float) j));
+        }
+    }
+
+    psPolynomial2D *chebys = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, 5, 5);
+
+    chebys = psImageFitPolynomial(chebys, tmpImage);
+
+    outImage = psImageEvalPolynomial(outImage, chebys);
+
+    chi2 = 0.0;
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            float expect = tmpImage->data.F32[i][j];
+            float actual = outImage->data.F32[i][j];
+            chi2+= (actual-expect) * (actual-expect);
+            if (fabs((actual - expect) / expect) > ERROR_TOL) {
+                fprintf(stderr,"pixel [%d][%d] is %.2f should be %.2f\n", i, j, actual, expect);
+            }
+        }
+    }
+    chi2/= ((float) (numCols * numRows));
+    fprintf(stderr,"The chi-squared per pixel is %.2f\n", chi2);
+
+    psFree(tmpImage);
+    psFree(outImage);
+    psFree(chebys);
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                   */
+    /*************************************************************************/
+
+    return(testStatus);
+}
+
+static bool FitChebyF64(int numCols, int numRows)
+{
+    const double A = 1.0;
+    const double B = 2.0;
+    const double C = 3.0;
+    const double D = 4.0;
+    const double E = 5.0;
+    const double F = 6.0;
+    const double ERROR_TOL = 0.1;
+
+    psImage *tmpImage     = NULL;
+    psImage *outImage     = NULL;
+    bool testStatus      = true;
+    psS32 i               = 0;
+    psS32 j               = 0;
+    double chi2 = 0.0;
+
+
+    fprintf(stderr,"psImageFitPolynomial(), psImageEvalPolynom(): (%d by %d)\n", numRows, numCols);
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+
+    tmpImage = psImageAlloc(numCols, numRows, PS_TYPE_F64);
+    outImage = psImageAlloc(numCols, numRows, PS_TYPE_F64);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            tmpImage->data.F64[i][j] = I_POLY(((double) i), ((double) j));
+        }
+    }
+
+    psPolynomial2D *chebys = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, 5, 5);
+
+    chebys = psImageFitPolynomial(chebys, tmpImage);
+
+    outImage = psImageEvalPolynomial(outImage, chebys);
+
+    chi2 = 0.0;
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            double expect = tmpImage->data.F64[i][j];
+            double actual = outImage->data.F64[i][j];
+            chi2+= (actual-expect) * (actual-expect);
+            if (fabs((actual - expect) / expect) > ERROR_TOL) {
+                fprintf(stderr,"pixel [%d][%d] is %.2f should be %.2f\n", i, j, actual, expect);
+            }
+        }
+    }
+    chi2/= ((double) (numCols * numRows));
+    fprintf(stderr,"The chi-squared per pixel is %.2f\n", chi2);
+
+    psFree(tmpImage);
+    psFree(outImage);
+    psFree(chebys);
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                   */
+    /*************************************************************************/
+
+    return(testStatus);
+}
+
+static psS32 testPsImageEvalPolynom()
+{
+    if (!FitChebyF32(1, 1)) {
+        return 1;
+    }
+    if (!FitChebyF32(1, 5)) {
+        return 2;
+    }
+    if (!FitChebyF32(5, 1)) {
+        return 3;
+    }
+    if (!FitChebyF32(5, 5)) {
+        return 4;
+    }
+    if (!FitChebyF64(1, 1)) {
+        return 5;
+    }
+    if (!FitChebyF64(1, 5)) {
+        return 6;
+    }
+    if (!FitChebyF64(5, 1)) {
+        return 7;
+    }
+    if (!FitChebyF64(5, 5)) {
+        return 8;
+    }
+    return 0;
+}
+
+psS32 testImageCountPixel(void)
+{
+    long numPix = 0;
+    long numPix2 = 0;
+    psImage *in = NULL;
+    psRegion reg;
+    reg.x0 = 0;
+    reg.x1 = 1;
+    reg.y0 = 0;
+    reg.y1 = 4;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    numPix = psImageCountPixelMask(in, reg, 1);
+    if (numPix != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCountPixelMask failed to return -1 for NULL Vector input.\n");
+        return 1;
+    }
+
+    numPix = 0;
+    in = psImageAlloc(5, 5, PS_TYPE_S32);
+
+    psImage *in2 = NULL;
+    in2 = psImageAlloc(1, 5, PS_TYPE_U8);
+
+    in->data.S32[0][0] = 0;
+    in->data.S32[1][0] = 1;
+    in->data.S32[2][0] = 0;
+    in->data.S32[3][0] = 1;
+    in->data.S32[4][0] = 0;
+    in2->data.U8[0][0] = 0;
+    in2->data.U8[1][0] = 1;
+    in2->data.U8[2][0] = 0;
+    in2->data.U8[3][0] = 1;
+    in2->data.U8[4][0] = 0;
+
+    //    numPix = psImageCountPixelMask(in, reg, 1);
+    numPix2 = psImageCountPixelMask(in2, reg, 1);
+    numPix = -1;
+    //numPix should be -1 from using S32's
+    /*    if (numPix != -1) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "psImageCountPixelMask failed to return -1 for wrong type of Image input.\n");
+            return 2;
+        }
+    */
+    if (numPix2 == -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCountPixelMask returned -1 for Correct Image input\n");
+        return 3;
+    }
+    if (numPix2 != 2) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCountPixelMask returned incorrect pixel count %ld (!=2)\n", numPix2);
+        return 4;
+    }
+    //Test for smaller region than image returns only 1 pixel counted
+    reg.y1 = 2.1;
+    numPix2 = psImageCountPixelMask(in2, reg, 1);
+    if (numPix2 != 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCountPixelMask returned incorrect pixel count %ld\n", numPix2);
+        return 5;
+    }
+    //Test for -1 upper bnds in region = 5, 1 limits = 2 pixels returned
+    reg.x1 = 1;
+    reg.y1 = -1;
+    numPix2 = psImageCountPixelMask(in2, reg, 1);
+    if (numPix2 != 2) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCountPixelMask returned incorrect pixel count %ld\n", numPix2);
+        return 100;
+    }
+
+    //Test for invalid region (1 pixel, lower = upper)
+    reg.x0 = 1;
+    reg.y0 = 1;
+    reg.x1 = 1;
+    reg.y1 = 1;
+    //    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    numPix2 = psImageCountPixelMask(in2, reg, 1);
+    if (numPix2 != 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCountPixelMask failed to return correct value (0) for 1 pixel region.\n");
+        return 9;
+    }
+
+    //Test for whole image (region = 0,0 0,0 )
+    reg.x0 = 0;
+    reg.x1 = 0;
+    reg.y0 = 0;
+    reg.y1 = 0;
+    numPix2 = psImageCountPixelMask(in2, reg, 1);
+    if (numPix2 != 2) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCountPixelMask returned incorrect pixel count %ld\n", numPix2);
+        return 10;
+    }
+
+    //Test a subimage.
+    reg.x1 = 1;
+    reg.y0 = 1;
+    reg.y1 = 5;
+    numPix2 = -1;
+    in2->row0 = 1;
+    numPix2 = psImageCountPixelMask(in2, reg, 1);
+    if (numPix2 != 2) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageCountPixelMask returned incorrect pixel count %ld (!=2)\n", numPix2);
+        return 11;
+    }
+
+    psFree(in);
+    psFree(in2);
+    return 0;
+}
+
+static psS32 testPsImageStats()
+{
+    psTraceSetLevel("p_psVectorRobustStats", 0);
+
+    const int N  = 32;
+    const int M = 64;
+
+    psStats * myStats = NULL;
+    psStats *myStats2 = NULL;
+    psImage *tmpImage = NULL;
+    psImage *tmpImage2 = NULL;
+    psImage *tmpMask = NULL;
+    psImage *tmpMask2 = NULL;
+    psS32 testStatus = true;
+    psS32 nb = 0;
+    psS32 i = 0;
+    psS32 j = 0;
+    psS32 IMAGE_X_SIZE = 0;
+    psS32 IMAGE_Y_SIZE = 0;
+    psS32 currentId = 0;
+
+    currentId = psMemGetId();
+    for ( nb = 0;nb < 7;nb++ ) {
+        if ( nb == 0 ) {
+            IMAGE_X_SIZE = 1;
+            IMAGE_Y_SIZE = 1;
+        }
+        if ( nb == 1 ) {
+            IMAGE_X_SIZE = 1;
+            IMAGE_Y_SIZE = N;
+        }
+        if ( nb == 2 ) {
+            IMAGE_X_SIZE = N;
+            IMAGE_Y_SIZE = 1;
+        }
+        if ( nb == 3 ) {
+            IMAGE_X_SIZE = N;
+            IMAGE_Y_SIZE = N;
+        }
+        if ( nb == 4 ) {
+            IMAGE_X_SIZE = N;
+            IMAGE_Y_SIZE = M;
+        }
+        if ( nb == 5 ) {
+            IMAGE_X_SIZE = M;
+            IMAGE_Y_SIZE = N;
+        }
+        if ( nb == 6 ) {
+            IMAGE_X_SIZE = M;
+            IMAGE_Y_SIZE = N;
+        }
+        fprintf(stderr, "*******************************\n" );
+        if (nb != 6) {
+            fprintf(stderr, "* IMAGE SIZE is (%d by %d)\n", IMAGE_X_SIZE, IMAGE_Y_SIZE );
+        } else {
+            fprintf(stderr, "* IMAGE SUBSET SIZE is (%d by %d)\n", IMAGE_X_SIZE, IMAGE_Y_SIZE );
+        }
+        fprintf(stderr, "*******************************\n" );
+
+        /*************************************************************************/
+        /*  Allocate and initialize data structures                      */
+        /*************************************************************************/
+        if (nb != 6) {
+            tmpImage = psImageAlloc( IMAGE_X_SIZE, IMAGE_Y_SIZE, PS_TYPE_F32 );
+            tmpMask = psImageAlloc( IMAGE_X_SIZE, IMAGE_Y_SIZE, PS_TYPE_U8 );
+            tmpImage2 = NULL;
+            tmpMask2 = NULL;
+        } else {
+            tmpImage2 = psImageAlloc( IMAGE_X_SIZE*2, IMAGE_Y_SIZE*2, PS_TYPE_F32 );
+            tmpMask2 = psImageAlloc( IMAGE_X_SIZE*2, IMAGE_Y_SIZE*2, PS_TYPE_U8 );
+            tmpImage = psImageSubset(tmpImage2, psRegionSet(IMAGE_X_SIZE/4, IMAGE_X_SIZE/4+IMAGE_X_SIZE,
+                                     IMAGE_Y_SIZE/4, IMAGE_Y_SIZE/4+IMAGE_Y_SIZE));
+            tmpMask = psImageSubset(tmpMask2, psRegionSet(IMAGE_X_SIZE/4, IMAGE_X_SIZE/4+IMAGE_X_SIZE,
+                                    IMAGE_Y_SIZE/4, IMAGE_Y_SIZE/4+IMAGE_Y_SIZE));
+        }
+
+        for ( i = 0;i < tmpImage->numRows;i++ ) {
+            for ( j = 0;j < tmpImage->numCols;j++ ) {
+                tmpImage->data.F32[ i ][ j ] = ( float ) ( i + j );
+            }
+        }
+
+        for ( i = 0;i < tmpMask->numRows;i++ ) {
+            for ( j = 0;j < tmpMask->numCols;j++ ) {
+                if ( ( i > ( tmpMask->numRows / 2 ) ) &&
+                        ( j > ( tmpMask->numCols / 2 ) ) ) {
+                    tmpMask->data.U8[ i ][ j ] = 1;
+                } else {
+                    tmpMask->data.U8[ i ][ j ] = 0;
+                }
+            }
+        }
+
+        myStats = psStatsAlloc( PS_STAT_SAMPLE_MEAN );
+        /*************************************************************************/
+        /*  Calculate Sample Mean with no mask                           */
+        /*************************************************************************/
+        psStats* tmpStats = myStats;
+        myStats = psImageStats( myStats, tmpImage, NULL, 0 );
+        if ( myStats != tmpStats ) {
+            fprintf(stderr,"ERROR: input psStats not equal to return psStats\n");
+            psAbort("Failed input psStats equal to returned psStats");
+        }
+        fprintf(stderr, "The sample mean was %.2f\n", myStats->sampleMean );
+
+        /*************************************************************************/
+        /*  Calculate Sample Mean with mask                              */
+        /*************************************************************************/
+
+        myStats = psImageStats( myStats, tmpImage, tmpMask, 1 );
+        if (myStats == NULL) {
+            fprintf(stderr,"TEST ERROR: psImageStats() returned NULL.\n");
+        } else {
+            fprintf(stderr, "The sample mean was %.2f\n", myStats->sampleMean );
+        }
+
+        /*************************************************************************/
+        /*  Deallocate data structures                                   */
+        /*************************************************************************/
+        psFree( myStats );
+        psFree( tmpImage );
+        psFree( tmpMask );
+        psFree( tmpImage2 );
+        psFree( tmpMask2 );
+    }
+
+    /*************************************************************************/
+    /*  Test With Various Null Inputs                                        */
+    /*************************************************************************/
+
+    tmpImage = psImageAlloc( IMAGE_X_SIZE, IMAGE_Y_SIZE, PS_TYPE_F32 );
+    tmpMask = psImageAlloc( IMAGE_X_SIZE, IMAGE_Y_SIZE, PS_TYPE_U8 );
+    myStats = psStatsAlloc( 0 );
+
+    myStats2 = psImageStats( myStats, NULL, NULL, 0 );
+    if ( myStats2 != NULL ) {
+        fprintf(stderr, "ERROR: myStats2 = psImageStats(myStats, NULL, NULL, 0) != NULL\n" );
+    }
+
+    myStats2 = psImageStats( NULL, tmpImage, NULL, 0 );
+    if ( myStats2 != NULL ) {
+        fprintf(stderr, "ERROR: myStats2 = psImageStats(NULL, tmpImage, NULL, 0) != NULL\n" );
+    }
+
+    myStats2 = psImageStats( myStats, tmpImage, NULL, 0 );
+
+    psFree( myStats );
+    psFree( tmpImage );
+    psFree( tmpMask );
+
+    tmpImage = psImageAlloc( IMAGE_X_SIZE, IMAGE_Y_SIZE, PS_TYPE_F32);
+    tmpMask = psImageAlloc( IMAGE_X_SIZE, IMAGE_Y_SIZE, PS_TYPE_U32);
+    myStats = psStatsAlloc( PS_STAT_SAMPLE_MEAN );
+    myStats2 = psImageStats( myStats, tmpImage, tmpMask, 0 );
+    if ( myStats2 != NULL ) {
+        fprintf(stderr, "ERROR: psImageStats did not return null when mask is invalid type.\n");
+    }
+    psFree(tmpImage);
+    psFree(tmpMask);
+    psFree(myStats);
+
+    return ( !testStatus );
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageStructManip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageStructManip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/tst_psImageStructManip.c	(revision 22322)
@@ -0,0 +1,702 @@
+/** @file  tst_psImageExtraction.c
+*
+*  @brief Contains the tests for psImageExtraction.[ch]
+*
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-05-05 02:48:34 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include<stdlib.h>
+#include<string.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "psType.h"
+
+static psS32 testImageSubset(void);
+static psS32 testImageCopy(void);
+static psS32 testImageTrim(void);
+
+
+testDescription tests[] = {
+                              {testImageSubset,547,"psImageSubset",0,false},
+                              {testImageSubset,550,"psImageSubset",0,true},
+                              {testImageCopy,551,"psImageCopy",0,false},
+                              {testImageTrim, 744, "psImageTrim", 0, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    return ! runTestSuite( stderr, "psImage", tests, argc, argv );
+}
+
+// #547: psImageSubset shall create child image of a specified size from a parent psImage structure
+psS32 testImageSubset(void)
+{
+    psImage preSubsetStruct;
+    psImage* original;
+    psImage* subset1 = NULL;
+    psImage* subset2 = NULL;
+    psImage* subset3 = NULL;
+    psS32 c = 128;
+    psS32 r = 256;
+    psRegion region1 = psRegionSet(0,c/2,0,r/2);
+    psRegion region2 = psRegionSet(c/4,c/4+c/2,r/4,r/4+r/2);
+
+    original = psImageAlloc(c,r,PS_TYPE_U32);
+    for (psS32 row=0;row<r;row++) {
+        for (psS32 col=0;col<c;col++) {
+            original->data.F32[row][col] = row*1000+col;
+        }
+    }
+
+    memcpy(&preSubsetStruct,original,sizeof(psImage));
+
+    subset2 = psImageSubset(original,region2);
+
+    subset3 = psImageSubset(original,region1);
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "Verify the returned psImage structure members nrow and ncol are equal to "
+             "the input parameter nrow and ncol respectively.");
+
+    if (subset2->numCols != c/2 || subset2->numRows != r/2) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset output size was not proper(%dx%d, should be %dx%d).",
+                subset2->numCols, subset2->numRows, c/2,r/2);
+        return 1;
+    }
+
+    if (subset3->numCols != c/2 || subset3->numRows != r/2) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset output size was not proper(%dx%d, should be %dx%d).",
+                subset3->numCols, subset3->numRows, c/2,r/2);
+        return 2;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "Verify the returned psImage structure contains expected values in the "
+             "row member, if the input psImage structure image contains known values.");
+
+    for (psS32 row=0;row<r/2;row++) {
+        for (psS32 col=0;col<c/2;col++) {
+            if (subset2->data.U32[row][col] != original->data.U32[row+r/4][col+c/4]) {
+                psError(PS_ERR_UNKNOWN,true,"psImageSubset output #1 was wrong at %dx%d (%d vs %d).",
+                        row,col,subset2->data.U32[row][col], original->data.U32[row+r/4][col+c/4]);
+                return 3;
+            }
+            if (subset3->data.U32[row][col] != original->data.U32[row][col]) {
+                psError(PS_ERR_UNKNOWN,true,"psImageSubset output #1 was wrong at %dx%d (%d vs %d).",
+                        row,col,subset2->data.U32[row][col], original->data.U32[row][col]);
+                return 4;
+            }
+        }
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "Verify the returned psImage structure member type is equal to the input "
+             "psImage structure member type.");
+
+    if (subset2->type.type != PS_TYPE_U32) {
+        psError(PS_ERR_UNKNOWN,true,"psImageSubset output type was not proper(%d, should be %d).",
+                subset2->type.type, PS_TYPE_U32);
+        return 6;
+    }
+    if (subset3->type.type != PS_TYPE_U32) {
+        psError(PS_ERR_UNKNOWN,true,"psImageSubset output type was not proper(%d, should be %d).",
+                subset3->type.type, PS_TYPE_U32);
+        return 7;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "Verify the returned psImage structure members row0 and col0 are equal to "
+             "the input parameters row0 and col0 respectively.");
+
+    if (subset2->col0 != c/4 || subset2->row0 != r/4) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset didn't set col0/row0 for subset2 (%d/%d, should be %d/%d).",
+                subset2->col0,subset2->row0,c/4,r/4);
+        return 8;
+    }
+    if (subset3->col0 != 0 || subset3->row0 != 0) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset didn't set col0/row0 for subset3 (%d/%d, should be %d/%d).",
+                subset3->col0,subset3->row0,0,0);
+        return 9;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "Verify the returned psImage structure member parent is equal to the "
+             "input psImage structure pointer image.");
+
+    if (subset2->parent != original || subset3->parent != original) {
+        psError(PS_ERR_UNKNOWN,true,"psImageSubset didn't set parent.");
+        return 10;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Verify the returned psImage structure member children is null.");
+
+    if (subset2->children != NULL || subset3->children != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psImageSubset didn't set children to NULL.");
+        return 11;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "Verify the input psImage structure image only has the following members "
+             "changed: 1) Nchildren is increased by one. 2) parent contains pointer psImage structure "
+             "out at parent[Nchildren-1].");
+
+    if (original->children == NULL || original->children->n != 2) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset didn't increment number of children by one per subset.");
+        return 12;
+    }
+    if (original->children->data[0] != subset2 || original->children->data[1] != subset3) {
+        psError(PS_ERR_UNKNOWN,true,"psImageSubset didn't properly store the children pointers.");
+        return 13;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Verify the returned psImage structure pointer is null and program "
+             "execution doesn't stop, if the input parameter image is null. Also verified the input "
+             "psImage structure is not modified.");
+
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    subset1 = psImageSubset(NULL,region1);
+    if (subset1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psImageSubset didn't return NULL when input image was NULL.");
+        return 14;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Verify the returned psImage structure pointer is null and program "
+             " execution doesn't stop, if the input parameters nrow and/or ncol are zero. Also verify "
+             "input psImage structure is not modified.");
+
+    memcpy(&preSubsetStruct,original,sizeof(psImage));
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    subset1 = psImageSubset(original, psRegionSet(0,c/2,r/2,r/2));
+    if (subset1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psImageSubset didn't return NULL when numRows=0.");
+        return 15;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    subset1 = psImageSubset(original,psRegionSet(c/2,c/2,0,r/2));
+    if (subset1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psImageSubset didn't return NULL when numCols=0.");
+        return 16;
+    }
+    if (memcmp(original,&preSubsetStruct,sizeof(psImage)) != 0) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset changed the original struct though it failed to subset.");
+        return 17;
+    }
+
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "Verify the returned psImage structure pointer is null and program "
+             "execution doesn't stop, if the input parameters row0 and col0 are not within "
+             "the range of values of psImage structure image.");
+
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    subset1 = psImageSubset(original, psRegionSet(0,c/2, 0,r*2));
+    if (subset1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset didn't return NULL when subset origin was outside of "
+                "image (via cols).");
+        return 18;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    subset1 = psImageSubset(original,psRegionSet(0,c*2,0,r/2));
+    if (subset1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset didn't return NULL when subset origin was outside of "
+                "image (via rows).");
+        return 19;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    subset1 = psImageSubset(original, psRegionSet(-1,c/2,0,r/2));
+    if (subset1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset didn't return NULL when subset origin was outside of "
+                "image (col0=-1).");
+        return 20;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    subset1 = psImageSubset(original, psRegionSet(0,c/2,-1,r/2));
+    if (subset1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset didn't return NULL when subset origin was outside of "
+                "image (row0=-1).");
+        return 21;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,
+             "Verify the returned psImage structure pointer is null and program "
+             "execution doesn't stop if the input parameters nrow, ncol, row0 and col0 "
+             "specify a range of data not within the input psImage structure image.  Also "
+             "verify the input psImage structure is not modified.");
+
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    subset1 = psImageSubset(original,psRegionSet(0,c/2,0,r+1));
+    if (subset1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset didn't return NULL when subset was outside of image (via rows).");
+        return 22;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    subset1 = psImageSubset(original, psRegionSet(0,c+1,0,r/2));
+    if (subset1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset didn't return NULL when subset was outside of image (via cols).");
+        return 23;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    subset1 = psImageSubset(original,psRegionSet(0,c+1,0,r+1));
+    if (subset1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageSubset didn't return NULL when subset was outside of image (via row+cols).");
+        return 24;
+    }
+
+    psLogMsg(__func__, PS_LOG_INFO,
+             "psImageFreeChildren shall deallocate any children images of a "
+             "psImage structure");
+
+    memcpy(&preSubsetStruct,original,sizeof(psImage));
+
+    psImageFreeChildren(original);
+
+    // Verify the returned psImage structure member Nchildren is set to zero.
+    if (original->children != NULL && original->children->n > 0) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageFreeChildren didn't set number of children to zero.");
+        return 25;
+    }
+
+    //Verify the returned psImage structure members type, nrow, ncol, row0, col0, rows
+    // and parent are not modified.
+    if (preSubsetStruct.numRows != original->numRows ||
+            preSubsetStruct.numCols != original->numCols ||
+            preSubsetStruct.row0 != original->row0 ||
+            preSubsetStruct.col0 != original->col0) {
+
+        psError(PS_ERR_UNKNOWN,true,
+                "psImageFreeChildren modified parent's non-children elements.");
+        return 27;
+    }
+
+    psFree(original);
+
+    return 0;
+}
+
+psS32 testImageCopy(void)
+{
+    psImage* img = NULL;
+    psImage* img2 = NULL;
+    psImage* img3 = NULL;
+    psImage* img4 = NULL;
+    psU32 c = 128;
+    psU32 r = 256;
+
+    img = psImageAlloc(c,r,PS_TYPE_F32);
+    for (unsigned row=0;row<r;row++) {
+        psF32* imgRow = img->data.F32[row];
+        for (unsigned col=0;col<c;col++) {
+            imgRow[col] = (psF32)(row+col);
+        }
+    }
+    img2 = psImageAlloc(c,r,PS_TYPE_F32);
+    for (unsigned row=0;row<r;row++) {
+        psF32* img2Row = img2->data.F32[row];
+        for (unsigned col=0;col<c;col++) {
+            img2Row[col] = 0.0f;
+        }
+    }
+
+    img3 = psImageCopy(img2,img,PS_TYPE_F32);
+
+    // Verify the returned psImage structure pointer is equal to the input parameter output.
+    if (img2 != img3) {
+        psError(PS_ERR_UNKNOWN, true,"the image given for recycled wasn't");
+        return 1;
+    }
+
+    // Verify the returned psImage structure is the same type as the input image structure
+    // if the specified output argument is NULL.
+    img4 = psImageCopy(NULL,img,PS_TYPE_F32);
+    if (img4 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"output image doesn't exist");
+        return 4;
+    }
+    if (img4->type.type != img->type.type) {
+        psError(PS_ERR_UNKNOWN, true,"output image is not the same type as input image");
+        return 4;
+    }
+    for (psU32 row=0;row<r;row++) {
+        psF32* imgInRow = img->data.F32[row];
+        psF32* imgOutRow = img4->data.F32[row];
+        for (unsigned col=0;col<c;col++) {
+            if( imgInRow[col] != imgOutRow[col] ) {
+                psError(PS_ERR_UNKNOWN, true,"Input image not equal to output image at %d,%d!",
+                        col, row);
+                return 4;
+            }
+        }
+    }
+
+    // Verify the returned psImage structure member are equal to the values in
+    // the input psImage structure input.
+
+    #define testImageCopyType(IN,OUT) \
+    img = psImageRecycle(img,c,r,PS_TYPE_##IN); \
+    for (unsigned row=0;row<r;row++) { \
+        ps##IN* imgRow = img->data.IN[row]; \
+        for (unsigned col=0;col<c;col++) { \
+            imgRow[col] = (ps##IN)(row+col); \
+        } \
+    } \
+    img2 = psImageCopy(img2,img,PS_TYPE_##OUT); \
+    if (img2 == NULL) { \
+        psError(PS_ERR_UNKNOWN, true,"psImageCopy failed to copy U8."); \
+        return 2; \
+    } \
+    for (psU32 row=0;row<r;row++) { \
+        ps##IN* imgRow = img->data.IN[row]; \
+        ps##OUT* img2Row = img2->data.OUT[row]; \
+        for (psU32 col=0;col<c;col++) { \
+            if (abs(imgRow[col] - (ps##IN)(row+col)) > 0.5) { \
+                psError(PS_ERR_UNKNOWN, true,"Input image was changed at %d,%d!", \
+                        col,row); \
+                return 2; \
+            } \
+            if (abs(img2Row[col] - (ps##OUT)(imgRow[col])) > 0.5) { \
+                psError(PS_ERR_UNKNOWN, true, \
+                        "returned psImage values after copy don't match at %d,%d " \
+                        "(%d vs %d)",\
+                        col,row,img2Row[col], (ps##OUT)(imgRow[col])); \
+                return 2; \
+            } \
+        } \
+    }
+
+    #define testImageCopyTypes(IN) \
+    printf("to psF32\n"); \
+    testImageCopyType(IN,F32);\
+    printf("to psF64\n"); \
+    testImageCopyType(IN,F64); \
+    printf("to psU8\n"); \
+    testImageCopyType(IN,U8); \
+    printf("to psU16\n"); \
+    testImageCopyType(IN,U16); \
+    printf("to psU32\n"); \
+    testImageCopyType(IN,U32); \
+    printf("to psS8\n"); \
+    testImageCopyType(IN,S8);\
+    printf("to psS16\n"); \
+    testImageCopyType(IN,S16);\
+    printf("to psS32\n"); \
+    testImageCopyType(IN,S32);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Image Copy Test for psU8");
+    testImageCopyTypes(U8);
+    psLogMsg(__func__,PS_LOG_INFO,"Image Copy Test for psU16");
+    testImageCopyTypes(U16);
+    psLogMsg(__func__,PS_LOG_INFO,"Image Copy Test for psU32");
+    testImageCopyTypes(U32);
+    psLogMsg(__func__,PS_LOG_INFO,"Image Copy Test for psS8");
+    testImageCopyTypes(S8);
+    psLogMsg(__func__,PS_LOG_INFO,"Image Copy Test for psS16");
+    testImageCopyTypes(S16);
+    psLogMsg(__func__,PS_LOG_INFO,"Image Copy Test for psS32");
+    testImageCopyTypes(S32);
+    psLogMsg(__func__,PS_LOG_INFO,"Image Copy Test for psF32");
+    testImageCopyTypes(F32);
+    psLogMsg(__func__,PS_LOG_INFO,"Image Copy Test for psF64");
+    testImageCopyTypes(F64);
+
+    // Verify the returned psImage structure pointer is null and program
+    // execution doesn't stop, if the input parameter input is null.
+    psLogMsg(__func__,PS_LOG_INFO,"An error should follow...");
+    img3 = psImageCopy(NULL,NULL,PS_TYPE_F32);
+    if (img3 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psImageCopy didn't return NULL when input image was NULL.");
+        return 3;
+    }
+
+    psFree(img);
+    psFree(img2);
+    psFree(img3);
+    psFree(img4);
+
+    return 0;
+}
+
+static psS32 testImageTrim(void)
+{
+    psS32 r = 200;
+    psS32 c = 300;
+    psS32 qtrR = r/4;
+    psS32 qtrC = c/4;
+    psS32 halfR = r/2;
+    psS32 halfC = c/2;
+    psRegion centerHalf = {qtrC,qtrC+halfC,qtrR,qtrR+halfR};
+
+    psImage* image = psImageAlloc(c,r,PS_TYPE_F32);
+    for (psS32 row = 0; row < image->numRows; row++) {
+        for (psS32 col = 0; col < image->numCols; col++) {
+            image->data.F32[row][col] = (psF32)col + (psF32)row/1000.0f;
+        }
+    }
+
+    /*
+        1. invoke psImageTrim with non-NULL image, and a valid region
+           x0,y0->x1,y1 (using only positive values). Verify that:
+            a. the return psImage is the same as the input psImage.
+            b. the size of the psImage is x1-x0 by y1-y0.
+            c. the pixel values coorespond to the region [x0:x1-1,y0:y1-1].
+    */
+    psImage* image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+    psImage* image2 = psImageTrim(image1,centerHalf);
+
+    if (image1 != image2) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not same as input value.  Not done in-place?");
+        return 1;
+    }
+
+    if (image2->numCols != halfC ||
+            image2->numRows != halfR) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "The resulting image size was %dx%d, but should be %dx%d.",
+                 image2->numCols, image2->numRows,
+                 halfC, halfR);
+        return 2;
+    }
+
+    for (psS32 row = 0; row < image2->numRows; row++) {
+        for (psS32 col = 0; col < image2->numCols; col++) {
+            if (fabsf(image2->data.F32[row][col] - image->data.F32[row+qtrR][col+qtrC])
+                    > FLT_EPSILON) {
+                psLogMsg(__func__,PS_LOG_ERROR,
+                         "The value at (%d,%d) was %g, but should be %g.",
+                         col,row,
+                         image2->data.F32[row][col],
+                         image->data.F32[row+qtrR][col+qtrC]);
+                return 3;
+            }
+        }
+    }
+
+    /*
+        2. invoke psImageTrim with non-NULL image and valid region where x1=0,
+           y1=0. Verify that:
+            a. the return psImage size is numCols-x0 by numRows-y0
+            b. the pixel values coorespond to the region
+               [x0:numCols-1,y0:numRows-1].
+    */
+    image1 = psImageCopy(image1,image,PS_TYPE_F32);
+    image2 = psImageTrim(image1,psRegionSet(qtrC,0,qtrR,0));
+
+    if (image1 != image2) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not same as input value.  Not done in-place?");
+        return 11;
+    }
+
+    if (image2->numCols != image->numCols-qtrC ||
+            image2->numRows != image->numRows-qtrR) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "The resulting image size was %dx%d, but should be %dx%d.",
+                 image2->numCols, image2->numRows,
+                 image->numCols-qtrC, image->numRows-qtrR);
+        return 12;
+    }
+
+    for (psS32 row = 0; row < image2->numRows; row++) {
+        for (psS32 col = 0; col < image2->numCols; col++) {
+            if (fabsf(image2->data.F32[row][col] -
+                      image->data.F32[row+qtrR][col+qtrC]) > FLT_EPSILON) {
+                psLogMsg(__func__,PS_LOG_ERROR,
+                         "The value at (%d,%d) was %g, but should be %g.",
+                         col,row,
+                         image2->data.F32[row][col],
+                         image->data.F32[row+qtrR][col+qtrC]);
+                return 13;
+            }
+        }
+    }
+
+    /*
+        4. invoke psImageTrim with x1<0, y1<0. Verify:
+            a. the psImage size is (numCols+x1)-x0 by (numRows+y1)-y0.
+            b. the pixel values coorespond to the region
+               [x0:numCols+x1-1,y0:numRows+y1-1].
+
+    */
+    image1 = psImageCopy(image1,image,PS_TYPE_F32);
+    image2 = psImageTrim(image1,psRegionSet(qtrC,-qtrC, qtrR,-qtrR));
+
+    if (image1 != image2) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "Return value not same as input value.  Not done in-place?");
+        return 21;
+    }
+
+    if (image2->numCols != image->numCols-qtrC-qtrC ||
+            image2->numRows != image->numRows-qtrR-qtrR) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "The resulting image size was %dx%d, but should be %dx%d.",
+                 image2->numCols, image2->numRows,
+                 image->numCols-qtrC, image->numRows-qtrR);
+        return 22;
+    }
+
+    for (psS32 row = 0; row < image2->numRows; row++) {
+        for (psS32 col = 0; col < image2->numCols; col++) {
+            if (fabsf(image2->data.F32[row][col] -
+                      image->data.F32[row+qtrR][col+qtrC]) > FLT_EPSILON) {
+                psLogMsg(__func__,PS_LOG_ERROR,
+                         "The value at (%d,%d) was %g, but should be %g.",
+                         col,row,
+                         image2->data.F32[row][col],
+                         image->data.F32[row+qtrR][col+qtrC]);
+                return 23;
+            }
+        }
+    }
+
+    psFree(image2);
+
+    /*
+        6. invoke psImageTrim with image=NULL Verify:
+            a. execution does not cease.
+            b. return value is NULL
+            c. appropriate error is generated.
+    */
+
+    image2 = psImageTrim(NULL, psRegionSet(qtrC,0,qtrR,0));
+
+    if (image2 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not return NULL given a NULL input image.");
+        return 31;
+    }
+    psErr* err = psErrorLast();
+    if (err == NULL || err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not generate an appropriate error for NULL input image.");
+        psErrorStackPrint(stderr,"Error Stack:");
+        return 32;
+    }
+    psFree(err);
+
+    image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+    image2 = psImageTrim(image1, psRegionSet(-1,0,0,0));
+
+    if (image2 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not return NULL given x0=-1.");
+        return 33;
+    }
+    err = psErrorLast();
+    if (err == NULL || err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not generate an appropriate error for x0=-1.");
+        psErrorStackPrint(stderr,"Error Stack:");
+        return 34;
+    }
+    psFree(err);
+
+    image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+    image2 = psImageTrim(image1, psRegionSet(0,0,-1,0));
+
+    if (image2 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not return NULL given y0=-1.");
+        return 35;
+    }
+    err = psErrorLast();
+    if (err == NULL || err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not generate an appropriate error for y0=-1.");
+        psErrorStackPrint(stderr,"Error Stack:");
+        return 36;
+    }
+    psFree(err);
+
+    image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+    image2 = psImageTrim(image1, psRegionSet(0,image->numCols+1,0,0));
+
+    if (image2 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not return NULL given x1=numCols+1.");
+        return 37;
+    }
+    err = psErrorLast();
+    if (err == NULL || err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not generate an appropriate error for x1=numCols+1.");
+        psErrorStackPrint(stderr,"Error Stack:");
+        return 38;
+    }
+    psFree(err);
+
+    image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+    image2 = psImageTrim(image1, psRegionSet(0,0,0,image->numRows+1));
+
+    if (image2 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not return NULL given y1=numRows+1.");
+        return 39;
+    }
+    err = psErrorLast();
+    if (err == NULL || err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not generate an appropriate error for y1=numRows+1.");
+        psErrorStackPrint(stderr,"Error Stack:");
+        return 40;
+    }
+    psFree(err);
+
+    image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+    image2 = psImageTrim(image1, psRegionSet(0,(((psF32)image->numCols)*-1.0),0,0));
+
+    if (image2 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not return NULL given x1=-numCols.");
+        return 41;
+    }
+    err = psErrorLast();
+    if (err == NULL || err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not generate an appropriate error for x1=-numCols.");
+        psErrorStackPrint(stderr,"Error Stack:");
+        return 42;
+    }
+    psFree(err);
+
+    image1 = psImageCopy(NULL,image,PS_TYPE_F32);
+    image2 = psImageTrim(image1, psRegionSet(0,0,0,(((psF32)image->numRows)*-1.0)));
+
+    if (image2 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not return NULL given y1=-numRows.");
+        return 41;
+    }
+    err = psErrorLast();
+    if (err == NULL || err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psImageTrim did not generate an appropriate error for y1=-numRows.");
+        psErrorStackPrint(stderr,"Error Stack:");
+        return 42;
+    }
+    psFree(err);
+
+    psFree(image);
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/fBiOut.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/fBiOut.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/fBiOut.fits	(revision 22322)
@@ -0,0 +1,6427 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B~
+B~záB~p€B~ffB~\)B~QìB~G®B~=qB~33B~(öB~
+žB~{B~
+=B~  B}õÃB}ë
+B}áHB}×
+B}ÌÍB}ÂB}žRB}®B}£×B}B}\B}
+B}záB}p€B}ffB}\)B}QìB}G®B}=qB}33B}(öB}
+žB}{B}
+=B}  B|õÃB|ë
+B|áHB|×
+B|ÌÍB|ÂB|žRB|®B|£×B|B|\B|
+B|záB|p€B|ffB|\)B|QìB|G®B|=qB|33B|(öB|
+žB|{B|
+=B|  Bz
+BzzáBzp€BzffBz\)BzQìBzG®Bz=qBz33Bz(öBz
+žBz{Bz
+=Bz  ByõÃByë
+ByáHBy×
+ByÌÍByÂByžRBy®By£×ByBy\By
+ByzáByp€ByffBy\)ByQìByG®By=qBy33By(öBy
+žBy{By
+=By  BxõÃBxë
+BxáHBx×
+BxÌÍBxÂBxžRBx®Bx£×BxBx\Bx
+BxzáBxp€BxffBx\)BxQìBxG®Bx=qBx33Bx(öBx
+žBx{Bx
+=Bx  Bv
+BvzáBvp€BvffBv\)BvQìBvG®Bv=qBv33Bv(öBv
+žBv{Bv
+=Bv  BuõÃBuë
+BuáHBu×
+BuÌÍBuÂBužRBu®Bu£×BuBu\Bu
+BuzáBup€BuffBu\)BuQìBuG®Bu=qBu33Bu(öBu
+žBu{Bu
+=Bu  BtõÃBtë
+BtáHBt×
+BtÌÍBtÂBtžRBt®Bt£×BtBt\Bt
+BtzáBtp€BtffBt\)BtQìBtG®Bt=qBt33Bt(öBt
+žBt{Bt
+=Bt  Br
+BrzáBrp€BrffBr\)BrQìBrG®Br=qBr33Br(öBr
+žBr{Br
+=Br  BqõÃBqë
+BqáHBq×
+BqÌÍBqÂBqžRBq®Bq£×BqBq\Bq
+BqzáBqp€BqffBq\)BqQìBqG®Bq=qBq33Bq(öBq
+žBq{Bq
+=Bq  BpõÃBpë
+BpáHBp×
+BpÌÍBpÂBpžRBp®Bp£×BpBp\Bp
+BpzáBpp€BpffBp\)BpQìBpG®Bp=qBp33Bp(öBp
+žBp{Bp
+=Bp  Bn
+BnzáBnp€BnffBn\)BnQìBnG®Bn=qBn33Bn(öBn
+žBn{Bn
+=Bn  BmõÃBmë
+BmáHBm×
+BmÌÍBmÂBmžRBm®Bm£×BmBm\Bm
+BmzáBmp€BmffBm\)BmQìBmG®Bm=qBm33Bm(öBm
+žBm{Bm
+=Bm  BlõÃBlë
+BláHBl×
+BlÌÍBlÂBlžRBl®Bl£×BlBl\Bl
+BlzáBlp€BlffBl\)BlQìBlG®Bl=qBl33Bl(öBl
+žBl{Bl
+=Bl  Bj
+BjzáBjp€BjffBj\)BjQìBjG®Bj=qBj33Bj(öBj
+žBj{Bj
+=Bj  BiõÃBië
+BiáHBi×
+BiÌÍBiÂBižRBi®Bi£×BiBi\Bi
+BizáBip€BiffBi\)BiQìBiG®Bi=qBi33Bi(öBi
+žBi{Bi
+=Bi  BhõÃBhë
+BháHBh×
+BhÌÍBhÂBhžRBh®Bh£×BhBh\Bh
+BhzáBhp€BhffBh\)BhQìBhG®Bh=qBh33Bh(öBh
+žBh{Bh
+=Bh  Bf
+BfzáBfp€BfffBf\)BfQìBfG®Bf=qBf33Bf(öBf
+žBf{Bf
+=Bf  BeõÃBeë
+BeáHBe×
+BeÌÍBeÂBežRBe®Be£×BeBe\Be
+BezáBep€BeffBe\)BeQìBeG®Be=qBe33Be(öBe
+žBe{Be
+=Be  BdõÃBdë
+BdáHBd×
+BdÌÍBdÂBdžRBd®Bd£×BdBd\Bd
+BdzáBdp€BdffBd\)BdQìBdG®Bd=qBd33Bd(öBd
+žBd{Bd
+=Bd  Bb
+BbzáBbp€BbffBb\)BbQìBbG®Bb=qBb33Bb(öBb
+žBb{Bb
+=Bb  BaõÃBaë
+BaáHBa×
+BaÌÍBaÂBažRBa®Ba£×BaBa\Ba
+BazáBap€BaffBa\)BaQìBaG®Ba=qBa33Ba(öBa
+žBa{Ba
+=Ba  B`õÃB`ë
+B`áHB`×
+B`ÌÍB`ÂB`žRB`®B`£×B`B`\B`
+B`záB`p€B`ffB`\)B`QìB`G®B`=qB`33B`(öB`
+žB`{B`
+=B`  B^
+B^záB^p€B^ffB^\)B^QìB^G®B^=qB^33B^(öB^
+žB^{B^
+=B^  B]õÃB]ë
+B]áHB]×
+B]ÌÍB]ÂB]žRB]®B]£×B]B]\B]
+B]záB]p€B]ffB]\)B]QìB]G®B]=qB]33B](öB]
+žB]{B]
+=B]  B\õÃB\ë
+B\áHB\×
+B\ÌÍB\ÂB\žRB\®B\£×B\B\\B\
+B\záB\p€B\ffB\\)B\QìB\G®B\=qB\33B\(öB\
+žB\{B\
+=B\  BZ
+BZzáBZp€BZffBZ\)BZQìBZG®BZ=qBZ33BZ(öBZ
+žBZ{BZ
+=BZ  BYõÃBYë
+BYáHBY×
+BYÌÍBYÂBYžRBY®BY£×BYBY\BY
+BYzáBYp€BYffBY\)BYQìBYG®BY=qBY33BY(öBY
+žBY{BY
+=BY  BXõÃBXë
+BXáHBX×
+BXÌÍBXÂBXžRBX®BX£×BXBX\BX
+BXzáBXp€BXffBX\)BXQìBXG®BX=qBX33BX(öBX
+žBX{BX
+=BX  BV
+BVzáBVp€BVffBV\)BVQìBVG®BV=qBV33BV(öBV
+žBV{BV
+=BV  BUõÃBUë
+BUáHBU×
+BUÌÍBUÂBUžRBU®BU£×BUBU\BU
+BUzáBUp€BUffBU\)BUQìBUG®BU=qBU33BU(öBU
+žBU{BU
+=BU  BTõÃBTë
+BTáHBT×
+BTÌÍBTÂBTžRBT®BT£×BTBT\BT
+BTzáBTp€BTffBT\)BTQìBTG®BT=qBT33BT(öBT
+žBT{BT
+=BT  BR
+BRzáBRp€BRffBR\)BRQìBRG®BR=qBR33BR(öBR
+žBR{BR
+=BR  BQõÃBQë
+BQáHBQ×
+BQÌÍBQÂBQžRBQ®BQ£×BQBQ\BQ
+BQzáBQp€BQffBQ\)BQQìBQG®BQ=qBQ33BQ(öBQ
+žBQ{BQ
+=BQ  BPõÃBPë
+BPáHBP×
+BPÌÍBPÂBPžRBP®BP£×BPBP\BP
+BPzáBPp€BPffBP\)BPQìBPG®BP=qBP33BP(öBP
+žBP{BP
+=BP  BN
+BNzáBNp€BNffBN\)BNQìBNG®BN=qBN33BN(öBN
+žBN{BN
+=BN  BMõÃBMë
+BMáHBM×
+BMÌÍBMÂBMžRBM®BM£×BMBM\BM
+BMzáBMp€BMffBM\)BMQìBMG®BM=qBM33BM(öBM
+žBM{BM
+=BM  BLõÃBLë
+BLáHBL×
+BLÌÍBLÂBLžRBL®BL£×BLBL\BL
+BLzáBLp€BLffBL\)BLQìBLG®BL=qBL33BL(öBL
+žBL{BL
+=BL  BJ
+BJzáBJp€BJffBJ\)BJQìBJG®BJ=qBJ33BJ(öBJ
+žBJ{BJ
+=BJ  BIõÃBIë
+BIáHBI×
+BIÌÍBIÂBIžRBI®BI£×BIBI\BI
+BIzáBIp€BIffBI\)BIQìBIG®BI=qBI33BI(öBI
+žBI{BI
+=BI  BHõÃBHë
+BHáHBH×
+BHÌÍBHÂBHžRBH®BH£×BHBH\BH
+BHzáBHp€BHffBH\)BHQìBHG®BH=qBH33BH(öBH
+žBH{BH
+=BH  BF
+BFzáBFp€BFffBF\)BFQìBFG®BF=qBF33BF(öBF
+žBF{BF
+=BF  BEõÃBEë
+BEáHBE×
+BEÌÍBEÂBEžRBE®BE£×BEBE\BE
+BEzáBEp€BEffBE\)BEQìBEG®BE=qBE33BE(öBE
+žBE{BE
+=BE  BDõÃBDë
+BDáHBD×
+BDÌÍBDÂBDžRBD®BD£×BDBD\BD
+BDzáBDp€BDffBD\)BDQìBDG®BD=qBD33BD(öBD
+žBD{BD
+=BD  BB
+BBzáBBp€BBffBB\)BBQìBBG®BB=qBB33BB(öBB
+žBB{BB
+=BB  BAõÃBAë
+BAáHBA×
+BAÌÍBAÂBAžRBA®BA£×BABA\BA
+BAzáBAp€BAffBA\)BAQìBAG®BA=qBA33BA(öBA
+žBA{BA
+=BA  B@õÃB@ë
+B@áHB@×
+B@ÌÍB@ÂB@žRB@®B@£×B@B@\B@
+B@záB@p€B@ffB@\)B@QìB@G®B@=qB@33B@(öB@
+žB@{B@
+=B@  B>
+B>záB>p€B>ffB>\)B>QìB>G®B>=qB>33B>(öB>
+žB>{B>
+=B>  B=õÃB=ë
+B=áHB=×
+B=ÌÍB=ÂB=žRB=®B=£×B=B=\B=
+B=záB=p€B=ffB=\)B=QìB=G®B==qB=33B=(öB=
+žB={B=
+=B=  B<õÃB<ë
+B<áHB<×
+B<ÌÍB<ÂB<žRB<®B<£×B<B<\B<
+B<záB<p€B<ffB<\)B<QìB<G®B<=qB<33B<(öB<
+žB<{B<
+=B<  B:
+B:záB:p€B:ffB:\)B:QìB:G®B:=qB:33B:(öB:
+žB:{B:
+=B:  B9õÃB9ë
+B9áHB9×
+B9ÌÍB9ÂB9žRB9®B9£×B9B9\B9
+B9záB9p€B9ffB9\)B9QìB9G®B9=qB933B9(öB9
+žB9{B9
+=B9  B8õÃB8ë
+B8áHB8×
+B8ÌÍB8ÂB8žRB8®B8£×B8B8\B8
+B8záB8p€B8ffB8\)B8QìB8G®B8=qB833B8(öB8
+žB8{B8
+=B8  B6
+B6záB6p€B6ffB6\)B6QìB6G®B6=qB633B6(öB6
+žB6{B6
+=B6  B5õÃB5ë
+B5áHB5×
+B5ÌÍB5ÂB5žRB5®B5£×B5B5\B5
+B5záB5p€B5ffB5\)B5QìB5G®B5=qB533B5(öB5
+žB5{B5
+=B5  B4õÃB4ë
+B4áHB4×
+B4ÌÍB4ÂB4žRB4®B4£×B4B4\B4
+B4záB4p€B4ffB4\)B4QìB4G®B4=qB433B4(öB4
+žB4{B4
+=B4  B2
+B2záB2p€B2ffB2\)B2QìB2G®B2=qB233B2(öB2
+žB2{B2
+=B2  B1õÃB1ë
+B1áHB1×
+B1ÌÍB1ÂB1žRB1®B1£×B1B1\B1
+B1záB1p€B1ffB1\)B1QìB1G®B1=qB133B1(öB1
+žB1{B1
+=B1  B0õÃB0ë
+B0áHB0×
+B0ÌÍB0ÂB0žRB0®B0£×B0B0\B0
+B0záB0p€B0ffB0\)B0QìB0G®B0=qB033B0(öB0
+žB0{B0
+=B0  B.
+B.záB.p€B.ffB.\)B.QìB.G®B.=qB.33B.(öB.
+žB.{B.
+=B.  B-õÃB-ë
+B-áHB-×
+B-ÌÍB-ÂB-žRB-®B-£×B-B-\B-
+B-záB-p€B-ffB-\)B-QìB-G®B-=qB-33B-(öB-
+žB-{B-
+=B-  B,õÃB,ë
+B,áHB,×
+B,ÌÍB,ÂB,žRB,®B,£×B,B,\B,
+B,záB,p€B,ffB,\)B,QìB,G®B,=qB,33B,(öB,
+žB,{B,
+=B,  B*
+B*záB*p€B*ffB*\)B*QìB*G®B*=qB*33B*(öB*
+žB*{B*
+=B*  B)õÃB)ë
+B)áHB)×
+B)ÌÍB)ÂB)žRB)®B)£×B)B)\B)
+B)záB)p€B)ffB)\)B)QìB)G®B)=qB)33B)(öB)
+žB){B)
+=B)  B(õÃB(ë
+B(áHB(×
+B(ÌÍB(ÂB(žRB(®B(£×B(B(\B(
+B(záB(p€B(ffB(\)B(QìB(G®B(=qB(33B((öB(
+žB({B(
+=B(  B&
+B&záB&p€B&ffB&\)B&QìB&G®B&=qB&33B&(öB&
+žB&{B&
+=B&  B%õÃB%ë
+B%áHB%×
+B%ÌÍB%ÂB%žRB%®B%£×B%B%\B%
+B%záB%p€B%ffB%\)B%QìB%G®B%=qB%33B%(öB%
+žB%{B%
+=B%  B$õÃB$ë
+B$áHB$×
+B$ÌÍB$ÂB$žRB$®B$£×B$B$\B$
+B$záB$p€B$ffB$\)B$QìB$G®B$=qB$33B$(öB$
+žB${B$
+=B$  B"
+B"záB"p€B"ffB"\)B"QìB"G®B"=qB"33B"(öB"
+žB"{B"
+=B"  B!õÃB!ë
+B!áHB!×
+B!ÌÍB!ÂB!žRB!®B!£×B!B!\B!
+B!záB!p€B!ffB!\)B!QìB!G®B!=qB!33B!(öB!
+žB!{B!
+=B!  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B	õÃB	ë
+B	áHB	×
+B	ÌÍB	ÂB	žRB	®B	£×B	B	\B	
+B	záB	p€B	ffB	\)B	QìB	G®B	=qB	33B	(öB	
+žB	{B	
+=B	  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   Aý
+=AüõÃAüáHAüÌÍAüžRAü£×Aü\AüzáAüffAüQìAü=qAü(öAü{Aü  Aûë
+Aû×
+AûÂAû®AûAû
+Aûp€Aû\)AûG®Aû33Aû
+žAû
+=AúõÃAúáHAúÌÍAúžRAú£×Aú\AúzáAúffAúQìAú=qAú(öAú{Aú  Aùë
+Aù×
+AùÂAù®AùAù
+Aùp€Aù\)AùG®Aù33Aù
+žAù
+=AøõÃAøáHAøÌÍAøžRAø£×Aø\AøzáAøffAøQìAø=qAø(öAø{Aø  Aõ
+=AôõÃAôáHAôÌÍAôžRAô£×Aô\AôzáAôffAôQìAô=qAô(öAô{Aô  Aóë
+Aó×
+AóÂAó®AóAó
+Aóp€Aó\)AóG®Aó33Aó
+žAó
+=AòõÃAòáHAòÌÍAòžRAò£×Aò\AòzáAòffAòQìAò=qAò(öAò{Aò  Añë
+Añ×
+AñÂAñ®AñAñ
+Añp€Añ\)AñG®Añ33Añ
+žAñ
+=AðõÃAðáHAðÌÍAðžRAð£×Að\AðzáAðffAðQìAð=qAð(öAð{Að  Aí
+=AìõÃAìáHAìÌÍAìžRAì£×Aì\AìzáAìffAìQìAì=qAì(öAì{Aì  Aëë
+Aë×
+AëÂAë®AëAë
+Aëp€Aë\)AëG®Aë33Aë
+žAë
+=AêõÃAêáHAêÌÍAêžRAê£×Aê\AêzáAêffAêQìAê=qAê(öAê{Aê  Aéë
+Aé×
+AéÂAé®AéAé
+Aép€Aé\)AéG®Aé33Aé
+žAé
+=AèõÃAèáHAèÌÍAèžRAè£×Aè\AèzáAèffAèQìAè=qAè(öAè{Aè  Aå
+=AäõÃAäáHAäÌÍAäžRAä£×Aä\AäzáAäffAäQìAä=qAä(öAä{Aä  Aãë
+Aã×
+AãÂAã®AãAã
+Aãp€Aã\)AãG®Aã33Aã
+žAã
+=AâõÃAâáHAâÌÍAâžRAâ£×Aâ\AâzáAâffAâQìAâ=qAâ(öAâ{Aâ  Aáë
+Aá×
+AáÂAá®AáAá
+Aáp€Aá\)AáG®Aá33Aá
+žAá
+=AàõÃAàáHAàÌÍAàžRAà£×Aà\AàzáAàffAàQìAà=qAà(öAà{Aà  AÝ
+=AÜõÃAÜáHAÜÌÍAÜžRAÜ£×AÜ\AÜzáAÜffAÜQìAÜ=qAÜ(öAÜ{AÜ  AÛë
+AÛ×
+AÛÂAÛ®AÛAÛ
+AÛp€AÛ\)AÛG®AÛ33AÛ
+žAÛ
+=AÚõÃAÚáHAÚÌÍAÚžRAÚ£×AÚ\AÚzáAÚffAÚQìAÚ=qAÚ(öAÚ{AÚ  AÙë
+AÙ×
+AÙÂAÙ®AÙAÙ
+AÙp€AÙ\)AÙG®AÙ33AÙ
+žAÙ
+=AØõÃAØáHAØÌÍAØžRAØ£×AØ\AØzáAØffAØQìAØ=qAØ(öAØ{AØ  AÕ
+=AÔõÃAÔáHAÔÌÍAÔžRAÔ£×AÔ\AÔzáAÔffAÔQìAÔ=qAÔ(öAÔ{AÔ  AÓë
+AÓ×
+AÓÂAÓ®AÓAÓ
+AÓp€AÓ\)AÓG®AÓ33AÓ
+žAÓ
+=AÒõÃAÒáHAÒÌÍAÒžRAÒ£×AÒ\AÒzáAÒffAÒQìAÒ=qAÒ(öAÒ{AÒ  AÑë
+AÑ×
+AÑÂAÑ®AÑAÑ
+AÑp€AÑ\)AÑG®AÑ33AÑ
+žAÑ
+=AÐõÃAÐáHAÐÌÍAÐžRAÐ£×AÐ\AÐzáAÐffAÐQìAÐ=qAÐ(öAÐ{AÐ  AÍ
+=AÌõÃAÌáHAÌÌÍAÌžRAÌ£×AÌ\AÌzáAÌffAÌQìAÌ=qAÌ(öAÌ{AÌ  AËë
+AË×
+AËÂAË®AËAË
+AËp€AË\)AËG®AË33AË
+žAË
+=AÊõÃAÊáHAÊÌÍAÊžRAÊ£×AÊ\AÊzáAÊffAÊQìAÊ=qAÊ(öAÊ{AÊ  AÉë
+AÉ×
+AÉÂAÉ®AÉAÉ
+AÉp€AÉ\)AÉG®AÉ33AÉ
+žAÉ
+=AÈõÃAÈáHAÈÌÍAÈžRAÈ£×AÈ\AÈzáAÈffAÈQìAÈ=qAÈ(öAÈ{AÈ  AÅ
+=AÄõÃAÄáHAÄÌÍAÄžRAÄ£×AÄ\AÄzáAÄffAÄQìAÄ=qAÄ(öAÄ{AÄ  AÃë
+AÃ×
+AÃÂAÃ®AÃAÃ
+AÃp€AÃ\)AÃG®AÃ33AÃ
+žAÃ
+=AÂõÃAÂáHAÂÌÍAÂžRAÂ£×AÂ\AÂzáAÂffAÂQìAÂ=qAÂ(öAÂ{AÂ  AÁë
+AÁ×
+AÁÂAÁ®AÁAÁ
+AÁp€AÁ\)AÁG®AÁ33AÁ
+žAÁ
+=AÀõÃAÀáHAÀÌÍAÀžRAÀ£×AÀ\AÀzáAÀffAÀQìAÀ=qAÀ(öAÀ{AÀ  Aœ
+=AŒõÃAŒáHAŒÌÍAŒžRAŒ£×AŒ\AŒzáAŒffAŒQìAŒ=qAŒ(öAŒ{AŒ  A»ë
+A»×
+A»ÂA»®A»A»
+A»p€A»\)A»G®A»33A»
+žA»
+=AºõÃAºáHAºÌÍAºžRAº£×Aº\AºzáAºffAºQìAº=qAº(öAº{Aº  A¹ë
+A¹×
+A¹ÂA¹®A¹A¹
+A¹p€A¹\)A¹G®A¹33A¹
+žA¹
+=AžõÃAžáHAžÌÍAžžRAž£×Až\AžzáAžffAžQìAž=qAž(öAž{Až  Aµ
+=AŽõÃAŽáHAŽÌÍAŽžRAŽ£×AŽ\AŽzáAŽffAŽQìAŽ=qAŽ(öAŽ{AŽ  A³ë
+A³×
+A³ÂA³®A³A³
+A³p€A³\)A³G®A³33A³
+žA³
+=A²õÃA²áHA²ÌÍA²žRA²£×A²\A²záA²ffA²QìA²=qA²(öA²{A²  A±ë
+A±×
+A±ÂA±®A±A±
+A±p€A±\)A±G®A±33A±
+žA±
+=A°õÃA°áHA°ÌÍA°žRA°£×A°\A°záA°ffA°QìA°=qA°(öA°{A°  A­
+=A¬õÃA¬áHA¬ÌÍA¬žRA¬£×A¬\A¬záA¬ffA¬QìA¬=qA¬(öA¬{A¬  A«ë
+A«×
+A«ÂA«®A«A«
+A«p€A«\)A«G®A«33A«
+žA«
+=AªõÃAªáHAªÌÍAªžRAª£×Aª\AªzáAªffAªQìAª=qAª(öAª{Aª  A©ë
+A©×
+A©ÂA©®A©A©
+A©p€A©\)A©G®A©33A©
+žA©
+=AšõÃAšáHAšÌÍAšžRAš£×Aš\AšzáAšffAšQìAš=qAš(öAš{Aš  A¥
+=A€õÃA€áHA€ÌÍA€žRA€£×A€\A€záA€ffA€QìA€=qA€(öA€{A€  A£ë
+A£×
+A£ÂA£®A£A£
+A£p€A£\)A£G®A£33A£
+žA£
+=A¢õÃA¢áHA¢ÌÍA¢žRA¢£×A¢\A¢záA¢ffA¢QìA¢=qA¢(öA¢{A¢  A¡ë
+A¡×
+A¡ÂA¡®A¡A¡
+A¡p€A¡\)A¡G®A¡33A¡
+žA¡
+=A õÃA áHA ÌÍA žRA £×A \A záA ffA QìA =qA (öA {A   A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Az{Ayë
+AyÂAyAyp€AyG®Ay
+žAxõÃAxÌÍAx£×AxzáAxQìAx(öAx  Aw×
+Aw®Aw
+Aw\)Aw33Aw
+=AváHAvžRAv\AvffAv=qAv{Auë
+AuÂAuAup€AuG®Au
+žAtõÃAtÌÍAt£×AtzáAtQìAt(öAt  As×
+As®As
+As\)As33As
+=AráHAržRAr\ArffAr=qAr{Aqë
+AqÂAqAqp€AqG®Aq
+žApõÃApÌÍAp£×ApzáApQìAp(öAp  Aj{Aië
+AiÂAiAip€AiG®Ai
+žAhõÃAhÌÍAh£×AhzáAhQìAh(öAh  Ag×
+Ag®Ag
+Ag\)Ag33Ag
+=AfáHAfžRAf\AfffAf=qAf{Aeë
+AeÂAeAep€AeG®Ae
+žAdõÃAdÌÍAd£×AdzáAdQìAd(öAd  Ac×
+Ac®Ac
+Ac\)Ac33Ac
+=AbáHAbžRAb\AbffAb=qAb{Aaë
+AaÂAaAap€AaG®Aa
+žA`õÃA`ÌÍA`£×A`záA`QìA`(öA`  AZ{AYë
+AYÂAYAYp€AYG®AY
+žAXõÃAXÌÍAX£×AXzáAXQìAX(öAX  AW×
+AW®AW
+AW\)AW33AW
+=AVáHAVžRAV\AVffAV=qAV{AUë
+AUÂAUAUp€AUG®AU
+žATõÃATÌÍAT£×ATzáATQìAT(öAT  AS×
+AS®AS
+AS\)AS33AS
+=ARáHARžRAR\ARffAR=qAR{AQë
+AQÂAQAQp€AQG®AQ
+žAPõÃAPÌÍAP£×APzáAPQìAP(öAP  AJ{AIë
+AIÂAIAIp€AIG®AI
+žAHõÃAHÌÍAH£×AHzáAHQìAH(öAH  AG×
+AG®AG
+AG\)AG33AG
+=AFáHAFžRAF\AFffAF=qAF{AEë
+AEÂAEAEp€AEG®AE
+žADõÃADÌÍAD£×ADzáADQìAD(öAD  AC×
+AC®AC
+AC\)AC33AC
+=ABáHABžRAB\ABffAB=qAB{AAë
+AAÂAAAAp€AAG®AA
+žA@õÃA@ÌÍA@£×A@záA@QìA@(öA@  A:{A9ë
+A9ÂA9A9p€A9G®A9
+žA8õÃA8ÌÍA8£×A8záA8QìA8(öA8  A7×
+A7®A7
+A7\)A733A7
+=A6áHA6žRA6\A6ffA6=qA6{A5ë
+A5ÂA5A5p€A5G®A5
+žA4õÃA4ÌÍA4£×A4záA4QìA4(öA4  A3×
+A3®A3
+A3\)A333A3
+=A2áHA2žRA2\A2ffA2=qA2{A1ë
+A1ÂA1A1p€A1G®A1
+žA0õÃA0ÌÍA0£×A0záA0QìA0(öA0  A*{A)ë
+A)ÂA)A)p€A)G®A)
+žA(õÃA(ÌÍA(£×A(záA(QìA((öA(  A'×
+A'®A'
+A'\)A'33A'
+=A&áHA&žRA&\A&ffA&=qA&{A%ë
+A%ÂA%A%p€A%G®A%
+žA$õÃA$ÌÍA$£×A$záA$QìA$(öA$  A#×
+A#®A#
+A#\)A#33A#
+=A"áHA"žRA"\A"ffA"=qA"{A!ë
+A!ÂA!A!p€A!G®A!
+žA õÃA ÌÍA £×A záA QìA (öA   A{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A
+{A	ë
+A	ÂA	A	p€A	G®A	
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žA õÃA ÌÍA £×A záA QìA (öA   @ô(ö@ó×
+@ó
+@ó33@òáH@ò\@ò=q@ñë
+@ñ@ñG®@ðõÂ@ð£×@ðQì@ð  @ï®@ï\)@ï
+=@îžR@îff@î{@íÂ@íp€@í
+ž@ìÌÍ@ìzá@ì(ö@ë×
+@ë
+@ë33@êáH@ê\@ê=q@éë
+@é@éG®@èõÃ@è£×@èQì@è  @ç®@ç\)@ç
+=@æžR@æff@æ{@åÂ@åp€@å
+ž@äÌÍ@äzá@ä(ö@ã×
+@ã
+@ã33@âáH@â\@â=q@áë
+@á@áG®@àõÃ@à£×@àQì@à  @Ô(ö@Ó×
+@Ó
+@Ó33@ÒáH@Ò\@Ò=q@Ñë
+@Ñ@ÑG®@ÐõÂ@Ð£×@ÐQì@Ð  @Ï®@Ï\)@Ï
+=@ÎžR@Îff@Î{@ÍÂ@Íp€@Í
+ž@ÌÌÍ@Ìzá@Ì(ö@Ë×
+@Ë
+@Ë33@ÊáH@Ê\@Ê=q@Éë
+@É@ÉG®@ÈõÃ@È£×@ÈQì@È  @Ç®@Ç\)@Ç
+=@ÆžR@Æff@Æ{@ÅÂ@Åp€@Å
+ž@ÄÌÍ@Äzá@Ä(ö@Ã×
+@Ã
+@Ã33@ÂáH@Â\@Â=q@Áë
+@Á@ÁG®@ÀõÃ@À£×@ÀQì@À  @Ž(ö@³×
+@³
+@³33@²áH@²\@²=q@±ë
+@±@±G®@°õÂ@°£×@°Qì@°  @¯®@¯\)@¯
+=@®žR@®ff@®{@­Â@­p€@­
+ž@¬ÌÍ@¬zá@¬(ö@«×
+@«
+@«33@ªáH@ª\@ª=q@©ë
+@©@©G®@šõÃ@š£×@šQì@š  @§®@§\)@§
+=@ŠžR@Šff@Š{@¥Â@¥p€@¥
+ž@€ÌÍ@€zá@€(ö@£×
+@£
+@£33@¢áH@¢\@¢=q@¡ë
+@¡@¡G®@ õÃ@ £×@ Qì@   @(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÂ@£×@Qì@  @®@\)@
+=@žR@ff@{@Â@p€@
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @®@\)@
+=@žR@ff@{@
+Â@
+p€@
+
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @hQì@g®@g
+>@fff@eÂ@e
+ž@dzá@c×
+@c33@b\@aë
+@aG®@`£×@`  @_\)@^žR@^{@]p€@\ÌÍ@\(ö@[
+@ZáH@Z=q@Y@XõÂ@XQì@W®@W
+>@Vff@UÂ@U
+ž@Tzá@S×
+@S33@R\@Që
+@QG®@P£×@P  @O\)@NžR@N{@Mp€@LÌÍ@L(ö@K
+@JáH@J=q@I@HõÃ@HQì@G®@G
+=@Fff@EÂ@E
+ž@Dzá@C×
+@C33@B\@Aë
+@AG®@@£×@@  @(Qì@'®@'
+>@&ff@%Â@%
+ž@$zá@#×
+@#33@"\@!ë
+@!G®@ £×@   @\)@
+žR@
+{@
+p€@
+ÌÍ@
+(ö@
+@áH@=q@@õÂ@Qì@®@
+>@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@£×@  @\)@žR@{@
+p€@
+ÌÍ@
+(ö@
+
+@
+áH@
+=q@	@õÃ@Qì@®@
+=@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@ £×@   ?Ð£×?Ï\)?Î{?ÌÌÍ?Ë
+
+?Ê=p?ÈõÂ?Ç®?Æff?Å
+ž?Ã×
+?Â\?ÁG®?À  ?ŸžR?œp€?Œ(ö?ºáH?¹?žQì?·
+>?µÂ?Žzá?³33?±ë
+?°£×?¯\)?®{?¬ÌÍ?«
+?ª=q?šõÂ?§®?Šff?¥
+ž?£×
+?¢\?¡G®?   ?žR?p€?(ö?áH??Qì?
+>?Â?zá?33?ë
+?£×?\)?{?ÌÍ?
+?=q?õÃ?®?ff?
+
+ž?×
+?\?G®?  ?!G®?
+žR?
+(ö??
+=?zá?ë
+?\)?
+ÌÍ?
+=q?®?
+ž?\?   >úáH>õÂ>ð£×>ë
+>æff>áG®>Ü(ö>×
+=>Ñë
+>ÌÌÍ>Ç®>Â\>œp€>žQì>³33>®{>šõÃ>£×
+>žR>>zá>\)>=q>
+
+ž>  >uÂ>k
+>aG®>W
+=>LÌÍ>B\>8Qì>.{>#×
+>>\)>
+ž=õÂ=áG®=ÌÌÍ=žQì=£×
+=\)=uÂ=LÌÍ=#×
+<õÂ<£×
+<#×
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~bB~Æ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BxºrB{-B~[B~vK¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BsKBuæBx²ÜB{³B~LB~gÏ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bmj#Bp=ÞBs
+ŽBu×Bx€aB{q7B~>B~YT¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BgÁûBj¶BmbBp/cBrü9BuÉBxæB{bœB~/B~JÙ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BbÓBdíBgºdBj;BmTBp çBríŸBuºBxkB{TAB~!B~<^¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B\q«B_EfBb<BdßBg«éBjx¿BmEBpmBrßCBu¬BxxðB{EÆB~B~-ã¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BVÉBY?B\jB_6ìBbÂBdÐBgoBjjFBm7
+BpòBrÐÉBuBxjuB{7LB~"B~g¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BQ!\BSõBVÁíBYÄB\[B_(pBaõGBdÂ
+BgôBj[ÊBm(¡BoõwBrÂMBu$Bx[úB{(ÐB}õ§B~ì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BKy4BNLïBQÅBSæBV³rBYHB\MB_öBaæÌBd³¢BgyBjMOBm%BoæüBr³ÒBušBxMB{UB}ç+B~q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BEÑ
+BH€ÇBKqBN>tBQ
+JBSØ BV€÷BYqÎB\>€B_
+zBaØQBd¥'BgqýBj>ÔBm
+ªBoØBr¥WBur-Bx?B{
+ÚB}Ø°B}óö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B@(äBBüBEÉuBHLBKc"BN/øBPüÏBSÉŠBV|BYcRB\0)B^üÿBaÉÕBd¬BgcBj0XBlý/BoÊBrÛBuc²Bx0Bzý_B}Ê5B}åz¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B:œB=TxB@!NBBî%BEºûBHÑBKTšBN!BPîUBS»+BVBYTØB\!®B^î
+Ba»[Bd1BgUBj!ÞBlîŽBo»BraBuU8Bx"BzîäB}»»B}Öÿ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B4ØB7¬PB:y&B=EýB@ÓBBß©BE¬BHyWBKF-BNBPßÚBS¬°BVyBYF]B\3B^à	Ba¬àBdy¶BgFBjcBlà9Bo­BryæBuFŒBxBzàiB}­?B}È¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B/0mB2(B4ÐþB7ÕB:j«B=7B@XBBÑ/BEBHjÛBK7²BNBPÑ^BS5BVk
+BY7áB\žB^ÑBadBdk;Bg8BjèBlÑŸBoBrkkBu8ABxBzÑîB}ÄB}º¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B)EB,\ B/(ÖB1õ­B4ÂB7YB:\0B=)B?õÝBBÂ³BEBH\`BK)6BMö
+BPÂãBS¹BV\BY)fB[ö<B^ÃBaéBd\ÀBg)BiölBlÃCBoBr\ïBu)ÆBwöBzÃrB}HB}«¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B#à
+B&³ØB)®B,M
+B/[B1ç1B4ŽB7ßB:MµB=B?çbBBŽ8BEBHMåBK»BMçBPŽhBS>BVNBYëB[çÁB^ŽBanBdNDBgBiçñBlŽÇBoBrNtBuJBwè BzŽ÷B}ÍB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+7õB!
+°B#ØB&¥]B)r3B,?	B/
+àB1Ø·B4¥B7rcB:?:B=
+B?ØæBB¥œBErBH?iBK
+@BMÙBP¥ìBSrÃBV?BY
+pB[ÙFB^Š
+BaróBd?ÉBg
+BiÙvBlŠLBos"Br?øBu
+ÏBwÙ¥BzŠ|B}sRB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BÎBcB
+0_B ý6B#Ê
+B&âB)c¹B,0B.ýfB1Ê<B4B7céB:0¿B<ýB?ÊlBBBBEdBH0ïBJýÅBMÊBPrBSdIBV1BXýõB[ÊÌB^¢BadxBd1OBfþ%BiÊûBlÑBodšBr1~BtþUBwË+BzB}dØB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BçŠB»aB7BUB
+!äB îºB#»B&hB)U>B,"B.îëB1»ÁB4B7UnB:"DB<ïB?»ñBBÇBEUBH"tBJïJBMŒ!BP÷BSUÍBV"€BXïzB[ŒPB^'BaUýBd"ÓBfï©BiŒBlVBoV-Br#BtïÙBwŒ°BzB}V\B}q ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+?~B9BàB¬æByŒBFB
+iB à@B#­B&yìB)FÃB,B.àoB1­FB4z
+B7FòB:ÉB<àB?­uBBzLBEG"BHùBJàÏBM­¥BPz|BSGRBV(BXàÿB[­ÕB^z«BaGBdXBfá.Bi®BlzÛBoG±BrBtá^Bw®4Bz{
+B}GáB}c$¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BVB
+kB
+7çBŸBÑBjBkAB8B
+îB ÑÄB#B&kqB)8GB,
+B.ÑôB1ÊB4k¡B78wB:MB<Ò$B?úBBkÑBE8§BH}BJÒTBM*BPl BS8×BV­BXÒB[YB^l0Ba9BdÝBfÒ³BiBll`Bo96Br
+BtÒãBw¹BzlB}9fB}Tª¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bï.BÂéB¿B
+\B
+)lBöBBÃBðB\ÆB)B
+ösB ÃIB#B&\öB))ÌB+ö¢B.ÃyB1OB4]%B7)üB9öÒB<Ã©B?BB]UBE*,BG÷BJÃØBM¯BP]
+BS*[BU÷1BXÄB[ÞB^]µBa*Bc÷aBfÄ8BiBl]äBo*»Bq÷BtÄgBw>Bz^B}*ëB}F.¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aø
+Aþ5BçBŽnBDB
+NB
+ñBçÈBŽBtBNKB!B
+ç÷B ŽÎB#€B&NzB)QB+è'B.ŽýB1ÔB4NªB7B9èWB<µ-B?BBNÚBE°BGèBJµ]BM3BPO	BSàBUè¶BXµB[cB^O9Ba
+BcèæBfµŒBiBlOiBo
+?BqéBtµíBwÃBzOB}
+pB}7³¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aí=ŒAòå3Aø~àAþBÙ
+B¥òBrÉB
+? B
+
+vBÙLBŠ#BrùB?ÏB
+ŠB
+Ù|B ŠRB#s)B&?ÿB)
+ÕB+Ù¬B.ŠB1sYB4@/B7
+B9ÙÜB<Š²B?sBB@_BE
+5BGÚ
+BJŠáBMsžBP@BS
+eBUÚ;BX§B[sèB^@ŸBa
+BcÚkBf§ABitBl@îBo
+ÅBqÚBt§qBwtHBzA
+B}
+ôB})8¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AáílAçãAí.AòÈ=AøaêAýûBÊ¡BxBdNB
+1$B
+ýûBÊÑB§Bd~B1TBþ*B
+ËB ×B#d­B&1B(þZB+Ë1B.B1dÝB41ŽB6þB9Ë`B<7B?e
+BB1ãBDþ¹BGËBJfBMe=BP2BRþéBUËÀBXB[elB^2CB`ÿBcËïBfÆBieBl2sBnÿIBqÌ BtöBweÌBz2£B|ÿyB}Œ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÖ
+AÜDAáÞ@AçwíAíAò«GAøDóAýÞ BŒ&BüBUÓB
+"©B
+ïBŒVB,BVB"ÙBï¯B
+Œ
+B \B#V2B&#	B(ïßB+ŒµB.B1VbB4#8B6ðB9ŒåB<»B?VBB#hBDð>BGœBJëBMVÁBP#BRðnBUœDBXB[VñB^#ÇB`ðBcœuBfKBiW!Bl#øBnðÎBqœ€Bt{BwWQBz$'B|ðþB}
+A¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AËLÌAÐôCAÖðAÜ'AáÁJAçZ÷Aìô£AòPAø'üAýÁ©B­«BzBGWB
+.B
+áB­ÚBz±BGB]Bá4B
+®
+B záB#G·B&B(ádB+®:B.{B1GçB4œB6áB9®iB<{@B?HBBíBDáÃBG®BJ{pBMHFBP
+BRáóBU®ÉBX{B[HvB^MB`â#Bc®ùBf{ÐBiHŠBl|BnâSBq¯)Bt{ÿBwHÖBz¬B|âB|ýÆ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A¿ü|AÅ£óAË= AÐ×MAÖpúAÜ
+§Aá€SAç> Aì×¬AòqYAø
+Aý€²B/BlB8ÜB
+²B
+ÒB_Bl5B9
+BâBÒ¹B
+B leB#9<B&B(ÒèB+¿B.lB19kB4AB6ÓB9îB<lÅB?9BBqBDÓHBG 
+BJlôBM9ËBP¡BRÓwBU NBXm%B[9ûB^ÑB`ÓšBc ~BfmTBi:+BlBnÓ×Bq ®BtmBw:ZBz1B|ÔB|ïJ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AŽ¬,AºS£A¿íPAÅýAË ªAÐºWAÖTAÛí°Aá\Aç!	Aìº¶AòTbA÷îAýŒBŽB]B*aB	÷7B
+Ä
+BäB]ºB*B÷gBÄ=B
+B ]êB#*ÀB%÷B(ÄmB+CB.^B1*ðB3÷ÆB6ÄB9sB<^IB?+ BA÷öBDÄÌBG£BJ^yBM+OBOø&BRÄýBUÓBX^©B[+B]øVB`Å,BcBf^ÙBi+¯BkøBnÅ\Bq2Bt_	Bw+ßByøµB|ÅB|àÏ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A©[ÜA¯SAŽ Aº6­A¿ÐZAÅjAË³AÐ`AÖ7
+AÛÐ¹AájfAçAì¿Aò7lA÷ÑAýjÆB9BOBåB	èŒB
+µBiBO?B
+BèìBµÂB
+B OoB#
+EB%éB(µñB+ÈB.OB1
+uB3éKB6¶!B9øB<OÎB?
+€BAé{BD¶QBG'BJOþBM
+ÕBOé«BR¶BUXBXP.B[
+B]éÛB`¶±BcBfP^Bi
+4Bkê
+Bn¶áBq·BtPBw
+dByê:B|·B|ÒT¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+A£³A©L¯A®æ\AŽ	Aº¶A¿³bAÅMAÊæ»AÐhAÖAÛ³ÁAáMnAæçAìÈAòuA÷Ž"AýMÎBsœB@B
+jB	ÚAB
+§BsíB@ÄB
+BÚpB§GB
+t
+B @óB#
+ÉB%Ú B(§vB+tMB.A#B1
+ùB3ÚÐB6§ŠB9t|B<ASB?)BAÚÿBD§ÖBGt­BJABMYBOÛ0BRšBUtÜBXA³B[B]Û_B`š6Bcu
+BfAâBi¹BkÛBnšeBqu<BtBBwèByÛ¿B|šB|ÃØ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A»;Ab²Aü_A£
+A©/¹A®ÉfAŽcA¹ü¿A¿kAÅ0AÊÉÅAÐcqAÕý
+AÛËAá0xAæÊ%AìcÒAñý~A÷+Aý0×BeBB2BþïB	ËÅB
+BerB2HBÿBËõBËB
+e¡B 2xB"ÿNB%Ì%B(ûB+eÑB.2šB0ÿ~B3ÌTB6+B9fB<2×B>ÿ®BAÌ
+BD[BGf1BJ3BLÿÞBOÌŽBRBUfaBX37B[ B]ÌäB`ºBcfBf3gBi =BkÍBnêBqfÀBt3Bw mByÍCB|B|µ]¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AjëAbA¬AEŒAßiA£yA©ÂA®¬oAŽFA¹ßÈA¿yuAÅ!AÊ¬ÎAÐF{AÕà(AÛyÕAáAæ­.AìFÛAñàA÷z4AýáBVÇB#BðtB	œJB
+ BV÷B#ÍBð£BœyBPB
+W&B #ýB"ðÓB%œ©B(B+WVB.$,B0ñB3œÙB6¯B9WB<$]B>ñ3BAŸ	BDàBGW¶BJ$BLñcBOŸ9BRBUWæBX$ŒBZñB]ŸiB`?BcXBf$ìBhñÂBkŸBnoBqXEBt%BvñòByŸÈB|B|Šâ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Ax58AÂA[¿AõlAA(ÆAÂrA£\AšõËA®xAŽ)%A¹ÂÑA¿\~AÄö+AÊØAÐ)
+AÕÃ2AÛ\ÞAàöAæ7Aì)äAñÃA÷]=AüöêBHLB"BáøB	®ÏB
+{¥BH{BQBâ(B®þB{ÕB
+H«B B"âXB%¯.B(|B+HÛB.±B0âB3¯^B6|5B9I
+B<áB>âžBA¯BD|dBGI;BJBLâçBO¯ŸBR|BUIjBXABZãB]¯íB`|ÄBcIBfpBhãGBk°
+Bn|óBqIÊBt BvãvBy°MB|}$B|g¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AaAlãAxßA¥
+A>ÉAØvAr"A
+ÏA¥{A£?(AšØÕA®rAŽ
+.A¹¥ÛA¿?AÄÙ5AÊrâAÐ
+AÕŠ;AÛ?çAàÙAæsAAì
+íAñŠA÷@GAüÙóB9ÐB§BÓ}B	 SB
+m)B: BÖBÓ­B BmYB
+:0B B"ÓÜB% ³B(mB+:_B.6B0Ô
+B3 ãB6m¹B9:B<fB>Ô<BA¡BDméBG:¿BJBLÔlBO¡BBRnBU:ïBXÅBZÔB]¡rB`nHBc;BfõBhÔËBk¡¢BnnxBq;NBt%BvÔüBy¡ÒB|nšB|ì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AJóûAVBèAav@Al©AwÜñA&A!ÒA»AU+AîØA
+A£"1Aš»ÞA®UA³ï8A¹åA¿"AÄŒ>AÊUëAÏïAÕDAÛ"ñAàŒAæVJAëï÷Añ£A÷#QAüŒýB+UBø+BÅB	ØB
+^®B+
+Bø[BÅ1BB^ÞB
++ŽBøB"ÅaB%7B(_B++åB-ø»B0ÅB3hB6_>B9,B;øëB>ÅÁBABD_nBG,DBIùBLÅñBOÇBR_BU,tBWùJBZÆ B]÷B`_ÍBc,£BeùzBhÆPBk&Bn_ýBq,ÔBsùªBvÆByWB|`-B|{p¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A4S]A?¢JAJÕ¢AVûAa<SAlo¬Aw£Ak/AÛAA85AÑáAkA£;AšèA®8A³ÒBA¹kîA¿AÄGAÊ8ôAÏÒ¡AÕlMAÛúAà§Aæ9SAëÓAñl®A÷ZAü B
+ÙBé°B¶B	]B
+P3B
+	BéàB¶¶BBPcB
+
+9BêB"¶æB%œB(PB+
+iB-ê@B0·B3ìB6PÃB9
+B;êoB>·FBA
+BDPòBG
+ÉBIêBL·uBOLBRQ"BU
+øBWêÏBZ·¥B]{B`QRBc
+(BeêþBh·ÕBk¬BnQBq
+XBsë/BvžByÛB|Q²B|lõ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+²¿A)¬A45A?h]AJµAUÏAafAl5¿AwiAN8AçåAA>AŽëANA¢èEAšòA®A³µKA¹N÷AŸè€AÄQAÊýAÏµªAÕOWAÚéAà±Aæ
+^Aë¶
+AñO·AöécAüB^BÛ5Bš
+B	táB
+AžBBÛdBš;BuBAçB
+ŸBÛB"škB%uAB(BB+îB-ÛÄB0šB3uqB6BGB9
+B;ÛôB>šÊBAu¡BDBwBGMBIÜ$BLšúBOuÐBRB§BU}BWÜSBZ©*B]v B`BÖBc­BeÜBh©ZBkv0BnCBqÝBsÜ³Bv©Byv`B|C6B|^z¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A!AaA
+fA(Ç¿A3ûA?.pAJaÈAU!A`ÈyAkûÑAw/*A1BAÊîAdAþHAõA1¢A¢ËNAšdûA­þ§A³TA¹2AŸË­AÄeZAÉÿAÏ³AÕ2aAÚÌAàeºAåÿgAëAñ2ÀAöÌmAüfB ÿãBÌ¹BB	ffB
+3<B BÌéB¿BfB3mB
+ CBÍB"ðB%fÆB(3B+ sB-ÍIB0B3föB63ÌB9 ¢B;ÍyB>OBAg%BD3üBG ÒBIÍšBLBOgUBR4+BUBWÍØBZ®B]g
+B`4\Bc2BeÎBhßBkgµBn4BqbBsÎ8BvBygåB|4»B|Oÿ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @àã@÷ÞAóÇA' A
+ZxA(ÑA3Á)A>ôAJ'ÚAU[2A`AkÁäAvõ<AJA­÷AG€AáQAzýAªA¢®VAšHA­á°A³{\A¹	AŸ®¶AÄHbAÉâAÏ{œAÕiAÚ¯AàHÂAåâoAë|
+AñÈAö¯uAüI"B ñgBŸ=BB	WêB
+$ÀBñBŸmBDBXB$ðB
+ñÇBŸB"sB%XJB(% B*ñöB-ŸÍB0£B3XyB6%PB8ò&B;ŸüB>ÓBAX©BD%BFòVBI¿,BLBOXÙBR%¯BTò
+BW¿\BZ3B]Y	B`%ßBbò¶Be¿BhbBkY9Bn&BpòåBs¿ŒBvByYiB|&?B|A¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @³¡È@Ê?¢@àŠS@÷
+A¹ÚAí3A
+ A(SäA3<A>ºAIííAU!FA`TAköAv»OA÷TAA*­AÄZA^A÷³A¢`Aš+
+A­Ä¹A³^fAžøAŸÀAÄ+mAÉÅAÏ^ÆAÔørAÚAà+ÌAåÅxAë_%AðøÒAö~Aü,+B âëB¯ÂB|B	InB
+EBã
+B¯òB|ÈBIBuB
+ãKB°"B"|øB%IÎB(¥B*ã{B-°QB0}(B3IþB6ÕB8ã«B;°B>}WBAJ.BDBFãÚBI°±BL}BOJ]BR4BTä
+BW°áBZ}·B]JB`dBbä:Be±Bh}çBkJœBnBpäjBs±ABv~ByJíB|ÄB|3¿  ¿  ¿  ¿  ¿  ¿  @`@þe@³e@ÉËÇ@à2x@ö)AíA³FA
+æA(öA3MOA>šAIŽ ATçXA`±AkN	AvbAÚ]At
+A
+·A§cAAAÚŒA¢tiAšA­§ÂA³ApAžÛ
+AŸtÉAÄvAÉš"AÏAÏAÔÛ|AÚu(AàÕAåšAëB.AðÛÛAöuAü5B ÔpB¡FBn
+B	:ôB
+ÊBÔ B¡wBnMB;#BúB
+ÔÐB¡ŠB"n}B%;SB()B*Õ B-¡ÖB0n­B3;B6YB8Õ/B;¢B>nÜBA;²BDBFÕ_BI¢5BLo
+BO;ãBR¹BTÕBW¢fBZo<B]<B`éBbÕ¿Be¢BholBk<BBn	BpÕïBs¢ÅBvoBy<rB|	HB|$¿  ¿  ¿  ¿  @2>@_zS@#Ú@@²ñ<@ÉWì@ßŸ@ö%OAF AyXA
+¬±A'à
+A3bA>FºAIzAT­kA_àÄAk
+AvGuAœgAWAðÀAlA$AœÆA¢WrA§ñ A­ÍA³$yAžŸ&AŸWÒAÃñAÉ,AÏ$ØAÔŸ
+AÚX2AßñÞAåAë%9AðŸåAöXAûò>B ÅõBÌB_¢B	,xB
+ùOBÆ%BûB_ÒB,šBù~B
+ÆUB+B"`B%,ØB'ù®B*Æ
+B-[B0`1B3-B5ùÞB8ÆŽB;B>`aBA-7BCú
+BFÆäBI»BL`BO-gBQú>BTÇBWêBZ`ÁB]-B_úmBbÇDBeBh`ñBk-ÇBmúBpÇtBsJBva By-÷B{úÍB|¿  ¿  ?¯xI@÷Ú@1Å<@^@
+° @°@²}b@Èä@ßJÃ@õ±tA
+A?lA
+rÄA'Š
+A2ÙuA>
+ÍAI@&ATs~A_Š×AjÚ/Av
+A pA:
+AÓÉAmvA"A ÐA¢:}A§Ô)A­mÖA³Až¡/AŸ:ÜAÃÔAÉn5AÏâAÔ¡AÚ;;AßÔéAånAëBAð¡îAö;AûÕHB ·zBPBQ'B	
+ýB
+êÓB·ªBBQVB
+-BëB
+·ÙB°B"QB%
+]B'ë3B*ž	B-ßB0Q¶B3
+B5ëbB8ž9B;
+B>QåBA
+ŒBCëBFžiBI
+?BLRBO
+ìBQëÂBTžBW
+oBZREB]
+B_ëòBbžÉBe
+BhRuBkLBmì"BpžøBs
+ÏBvR¥By{B{ìQB|?!G®?)Õ?®
+@$@0Ý@]ªé@
+<%@¢×@²	@Èp8@ÞÖé@õ=AÒ&A~A
+8×A'l/A2A=ÒàAI9AT9A_léAj BAuÓAyA
+&A¶ÒAPAê-AÙA¢
+A§·2A­PßA²êAž8AŸ
+åAÃ·AÉQ>AÎêëAÔAÚ
+EAß·òAåQAêëKAðøAö
+€AûžQB šÿBuÕBB«B	B
+ÜXB©.BvBBÛB±BÜB
+©^Bv5B"C
+B%áB'Ü·B*©B-vdB0C:B3B5ÜçB8©œB;vB>CkBAABCÝBF©îBIvÄBLCBOqBQÝGBTª
+BWvôBZCÊB]¡B_ÝwBbªMBew$BhCúBkÐBmÝ§Bpª}BswSBvD)By B{ÝÖ¿  ?b(?&6¬?¬¶@(p@/õÒ@\Ã4@ÈK@.ü@±­@Çü^@Þc@ôÉÀA9AËAþêA'2BA2eA=óAHÌKASÿ€A_2üAjfUAu­AfA 0AÝA3AÍ6AfâA¢ A§<A­3èA²ÍAžgBAŸ îAÃAÉ4IAÎÍõAÔg¢AÚNAßûAå4šAêÎTAðhAö®AûZB BgZB40B	B
+ÍÝB³BgB4`B6BÎ
+B
+ãBg¹B"4B%fB'Î<B*B-géB04¿B3B5ÎlB8CB;hB>4ïBAÆBCÎBFrBIhIBL5BOõBQÎÌBT¢BWhyBZ5OB]%B_ÎüBbÒBehšBh5BkUBmÏ+BpBshØBv5®By	j¿  ¿  ?ÃV?"×?ªæ°@@º@/
+@[Û~@Tp@»!@±!Ò@Ç@Ýï3@ôUäA^KA£AÄüA&øTA2+¬A=_AH]ASÅ¶A^ùAj,gAu_¿AIA
+ã9A|æAA°?AIìA¡ãA§}EA­òA²°AžJKAœãùAÃ}¥AÉRAÎ°þAÔJ«AÙäXAß~Aå±Aê±^AðK
+Aõä·Aû~eB BXÞB%µBòB
+¿aB8BYB%åBò»B¿B
+gBY>B"&B$òêB'¿ÁB*B-YmB0&DB2óB5¿ñB8ÇB;YB>&tB@óJBCÀ!BF÷BIYÍBL&€BNózBQÀQBT'BWYýBZ&ÔB\óªB_ÀBbWBeZ-Bh'BjóÙBmÀ°BpBsaB¿  ¿  ¿  ¿  ?$?
+ù?©F@Y@.&h@ZóÊ@à@GG@°­÷@Çš@Ý{Y@óâ
+A$^AW¶AA&ŸgA1ñ¿A=%AHXpASÉA^¿!AiòyAu%ÒA,A
+ÆCA_ïAùAHA,õA¡Æ¢A§`NA¬ùûA²©Až-UAœÇAÃ`¯AÈú[AÎAÔ-ŽAÙÇaAßaAäúºAêgAð.AõÇÁAûanB }BJcB9BäB
+°æB}œBJBiBä?B±B
+}ìBJÂB"B$äoB'±EB*~
+B-JóB0ÉB2äB5±vB8~LB;K"B>ùB@äÏBC±¥BF~|BIKRBL)BNäÿBQ±ÕBT~¬BWKBZXB\å/B_²Bb~ÛBeK±BhBjå^Bm¹¿  ¿  ¿  ¿  ¿  ¿  ?
+²?Z1?§GÝ@ qQ@->³@Z
+@l»@Ól@°:
+@Æ Î@Ý@ón0AêpA
+ÉAQ!A&zA1·ÒA<ë+AH
+ASQÛA^
+4AižAtëåAA
+©LABøAÜ¥AvRAþA¡©«A§CYA¬ÝA²v²Až_Aœª
+AÃCžAÈÝdAÎwAÔŸAÙªjAßDAäÝÅAêwqAð
+AõªÊAûDwB oB;èBŸBÕB
+¢kBoAB<BîBÕÄB¢B
+oqB<GB"	
+B$ÕôB'¢ËB*o¡B-<wB0	NB2Ö$B5¢úB8oÑB;<§B>	}B@ÖTBC£*BFpBI<×BL	­BNÖBQ£ZBTp0BW=BZ	ÝB\Ö³B_£Bbp`Be=6Bhò¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?æá?»X?¥xp?ÿ5@,Vý@Y$_@øà@_@¯ÆC@Æ,ó@Ü€@òúTA°AãÜA4A&JA1}åA<±=AGäASïA^KGAi~At±øAåQA
+UA&A¿®AY[Aó	A¡µA§&bA¬ÀA²Y»A·óhAœAÃ&ÁAÈÀnAÎZAÓóÇAÙuAß'!AäÀÎAêZzAïô'AõÔAû'B `B-mBúCBÇB
+ïB`ÆB-BúrBÇIBB
+`õB-ÌB!ú£B$ÇyB'OB*a&B--üB/úÒB2Ç©B5B8aUB;.,B=ûB@ÇÙBC¯BFa
+BI.\BKû2BNÈBQßBTaµBW.BYûaB\È8B_BbhÊ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+H?
+?£©?ýCÌ@+oI@X<ª@
+@ë·@¯Rh@Å¹@ÜÊ@òzAvA©ïAÝGA&A1CøA<wQAGª©ARÞA^ZAiD²Atx
+A«dA
+o^A	
+A¢¹A<eAÖA¡o¿A§	kA¬£A²<ÄA·ÖqAœp
+AÃ	ÊAÈ£wAÎ=%AÓÖÑAÙp~Aß
+*Aä£×Aê=Aï×0AõpÝAû
+B RB
+ñBëÇBžB
+
+tBRJB!Bë÷BžÍB
+€B
+R{BQB!ì'B$žþB'
+ÔB*RªB-B/ìWB2¹-B5B8RÚB;±B=ìB@¹]BC4BFS
+BIàBKì·BN¹BQcBTS9BW BYìæB\À¢¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?	©>?}¬?¡Ù?ût_@*@WTô@+@wÛ@®Þ@ÅE=@Û«î@òA<šAp A£YA%Ö²A1
+
+A<=bAGp»AR€A]×lAi
+ÄAt>
+AquA
+RhAìA
+ÁAnA¹A¡RÇAŠìsA¬ A²ÍA·¹yAœS&AÂìÔAÈAÎ -AÓ¹ÚAÙSAÞí3AäßAê Aïº9AõSåAúíB CBvBÝLBª"B
+vùBCÏB¥BÝ|BªSBw)B
+CÿBÖB!Ý¬B$ªB'wYB*D/B-B/ÝÜB2ª²B5wB8D_B;5B=Þ
+B@ªâBCwžBFDBIeBKÞ;BN«BQwèBTDŸBWz¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+k?
+ÞÚ? 
+2?ù€ö@)Þ@Vm@@Q@@®j²@ÄÑc@Û8@ñÅA»A6AilA%ÄA0Ð
+A<uAG6ÎARj&A]AhÐ×At/A7A
+5qAÏ
+AhÊAwA#A¡5ÐAŠÏ}A¬i)A²ÖA·Aœ60AÂÏÝAÈiAÎ6AÓãAÙ6AÞÐ<AäiéAêAïBAõ6ïAúÐB 5$BúBÎÑB§B
+h}B5TB+BÏB×Bh®B
+5BZB!Ï1B$B'hÝB*5ŽB-B/ÏaB27B5i
+B85äB;ºB=ÏB@gBCi=BF6BIéBKÏÀBNBQpR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?k?	@?:Å?÷Õ@(ž(@U
+@)u@&@­ö×@Ä]@ÚÄ9@ñ*êAÈÎAü&A/A%b×A00A;ÉAFüáAR09A]cAhêAsÊBA~ýA
+zA²'AKÓAåA-A¡ÙAŠ²A¬L4A±åàA·Aœ:AÂ²æAÈLAÍæ?AÓìAÙAÞ³EAäLòAéæAïLAõùAú³¥B &©BóBÀUB,B
+ZB&ÙBó¯BÀB\BZ2B
+'	B
+óßB!ÀµB$B'ZbB*'9B,ôB/ÀåB2ŒB5ZB8'hB:ô?B=ÁB@ëBCZÁBF'BHônBKÈ*¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ý?¡/?k\?ö!@'Ðs@TÕ@µ@
+L@­ý@Ãé®@ÚP_@ð·AáAÂ9AõA%(êA0\CA;AFÂóAQöLA])€Ah\ýAsUA~Ã®AûA0A.ÝAÈAb6A ûäAŠA¬/=A±ÈêA·bAŒüCAÂïAÈ/AÍÉIAÓbõAØü¢AÞOAä/üAéÉ©AïcUAôýAú¯B -BåB±ÛB~±B
+KB^Bå4B²
+B~áBK·B
+B
+ådB!²:B$B'KçB*œB,åB/²jB2@B5LB8íB:åÃB=²B@pBCLFBF ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ö[ë?U?ï?ô6Ž@&èœ@S¶
+@AÀ@šq@­"@ÃuÓ@ÙÜ@ðC4ATóAKA»€A$îüA0"TA;U­AFAQŒ^A\ï¶Ah#AsVgA~ÀAÞAx9AæA«AE@A ÞíAŠxA¬FA±«óA·EAŒßLAÂxùAÈ¥AÍ¬RAÓEÿAØß¬AÞyYAäAé¬²AïF_Aôà
+AúyžB 	³BÖB£_Bp6B
+=
+B	âBÖ¹B£BpeB=<B
+
+B
+ÖéB!£¿B$pB'=lB*
+BB,×B/£ïB2pÅB5=B8
+qB:×HB=€
+B@wÚ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ï
+I>üÇ?Ì?ògK@&@RÎj@Ì@4@¬G@Ãù@Ùh©@ïÏZAAN^A¶A$µA/ègA;ÀAFOAQqA\µÉAgé!As
+zA~OÓAÁA[DAôðAA(JA ÁöAŠ[£A«õOA±üA·(©AŒÂUAÂ\AÇõ¯AÍ\AÓ)	AØÂ¶AÞ\bAãöAé»Aï)hAôÃAú\ÁAÿönBÈBäBaºB
+.B
+ûgBÈ=BBaêB.ÁBûB
+ÈmB!DB$bB'.ðB)ûÇB,ÈB/sB2bIB5/ B7ûöB:Ï²¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >çà¥>õc?ý
+?ðâ@%T@Qæ¶@~Ž@Àœ@¬'m@Â
+@ØôÏ@ï[AáAqAGÉA${"A/®zA:áÓAF+AQHA\{ÜAg¯5ArâA~çA€ A>MA×úAqŠA
+SA €ÿAŠ>¬A«ØYA±rA·
+²AŒ¥_AÂ?
+AÇØ¹AÍrfAÓ
+AØ¥¿AÞ?kAãÙAérÅAï
+qAôŠ
+Aú?ËAÿÙxB¹BiBS?B
+ B
+ììB¹ÂBBSoB EBí
+B
+¹òB!ÈB$SB' uB)íKB,º!B/øB2SÎB5'¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >à£>îK ?-¬?îÈq@$1@Pþý@}Ì_@Là@«³@ÂB@Øó@îç€A§*A
+ÚA
+ÛA$A4A/tA:§äAEÛ=AQA\AîAguFAršA}ÛøA©A!UA»AT®Aî[A AŠ!ŽA«»aA±UA¶î»AŒhAÂ"AÇ»ÁAÍUnAÒïAØÇAÞ"tAãŒ AéUÍAîïzAô&Aú"ÔAÿŒB«BwìBDÃB
+B
+ÞpB«FBx
+BDóBÉBÞB
+«vB!xLB$E"B'øB)ÞÏB,«¥B/a¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ùe]>ç
+û?^C?ìù@#Iç@PH@|äª@Ù@«?·@ÁŠh@Ø
+@îsÊAm=A
+ AÓîA$FA/:A:møAE¡PAPÔšA\Ag;YArn²A}¢
+Aj²A^A
+A7žAÑdA kAŠŸA«kA±8A¶ÑÅAŒkqAÂ
+AÇÊAÍ8wAÒÒ$AØkÐAÞ}Aã*Aé8ÖAîÒAôl1AúÝAÿBBiqB6HB
+
+B
+ÏôBËBi¡B6wBNBÐ$B
+úB!iÐB$6§B'}B)×9¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ò'»>ßÐY?Û?ë)@"b3@O/@{üö@e,@ªËÝ@Á2@×?@íÿïA3PA
+fšAA#ÍZA/ ²A:4
+AEgcAP»A[ÎAglAr4ÅA}h
+AM»AçhAAÁAŽnA NA¥çÈA«uA±!A¶ŽÎAŒNzAÁè'AÇÔAÍAÒµ-AØNÚAÝèAã4AéáAîµAôO:AùèæAÿB BZöB'ÌB
+ô£B
+ÁyBOB[&B'üBôÒBÁšB
+B![UB$/¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Êê>Øµ?¿r?éZ6@!z~@NGà@{A@ñR@ªX@ÀŸ³@×%d@íAùcA
+,ŒA`A#lA.ÆÅA9ú
+AE-vAP`ÎA['AfÇAqú×A}./A0ÄAÊqAd
+AýËAxA 1%A¥ÊÑA«d~A°þ*A¶×AŒ1AÁË0AÇdÝAÌþAÒ7AØ1äAÝËAãe=AèþêAîAô2CAùËðAÿeB€BL{BQB
+æ'B
+²þBÔBLªBBæWB³-B
+é¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ã¬s>ÑTñ?ð?çÆ@ Å@M`'@z-@}u@©ä&@ÀJ×@Ö±@í9A¿uA
+òÍA&&A#Y~A.×A9À/ADóAP&àA[Z8AfAqÀéA|ôBAÎA­{AG(AàÕAzA .A¥­ÚA«GA°á4A¶zàAŒAÁ®:AÇGçAÌáAÒ{AAØíAÝ®AãHFAèáóAî{ AôLAù®ùAÿH¥Bq)B=ÿB
+ÖB
+×¬B
+€BqXB>/B
+BÞÁ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ŒnÐ>ÊN? ?å»\@«@Lxr@yEÔ@	@©pL@¿Öý@Ö=®@ì€_A
+A
+žàAì9A#A.RêA9BAD¹AOìòA[ KAfS€AqüA|ºUAöØA
+A*1AÃÞA]A÷7A¥äA«*A°Ä=A¶]êA»÷AÁDAÇ*ñAÌÄAÒ^JA×÷öAÝ£Aã+PAèÄüAî^©AóøVAùAÿ+¯Bb®B/BüZB
+É0B
+BbÝB6¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >µ1,>ÂÙª?Q/?ãëó@
+Ã\@KŸ@x^ @Á@šür@¿c#@ÕÉÔ@ì0
+AKA
+~óA²LA"å€A.üA9LTAD­AO³AZæ^Af·AqMA|iAÙáAsA
+:AŠçA@AÚ@A¥síA«
+A°§GA¶@ôA»Ú¡AÁtMAÇ
+úAÌ§ŠAÒASA×Û AÝt¬AãYAèšAîA²AóÛ`Aùu
+Aÿ·BT2B!BíßB
+ºµB
+q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >­ó>»?Æ?â
+@
+Ûš@J©
+@wvl@!ç@š@ŸïI@ÕUù@ëŒ«A®A
+EAx^A"«¶A-ßA9hADEÀAOyAZ¬qAeßÉAq"A|F|AŒêAVAðDAðA#AœJA¥V÷Aªð€A°QA¶#ýA»œªAÁWVAÆñAÌ°AÒ$\A×Ÿ	AÝW¶AâñbAèAî$œAóŸiAùXAþñÁBE·BBæI¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Šµå>Ž^C?²U?àM@
+óï@IÁQ@v³@®
+@š»@Ÿ{m@Ôâ
+@ëHÎA ×¿A
+
+A>pA"qÉA-¥!A8ØzAD
+ÒAO?*AZrAe¥ÜApÙ4A|
+AóA9AÓLAlùAŠA SA¥: AªÓ¬A°mYA¶A» ²AÁ:_AÆÔ
+AÌmžAÒeA×¡AÝ:¿AâÔlAènAîÅAó¡rAù;
+AþÔËB>!¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >xA>­ À?âô?Þ}¹@
+
+?@HÙ¡@u§@:3@§ ã@Ÿ@ÔnD@êÔöA ÓA
+Ñ,AA"7ÝA-k5A8ACÑæAO?AZ8AekïApGA{Ò¡AýA
+ªA¶WAPAé±A]A¥
+
+Aª¶¶A°PcAµêA»ŒAÁ
+iAÆ·AÌPÂAÑêpA×
+AÝ
+ÉAâ·vAèQ#AíêÏAó|Aù+ó¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >:>¥ã??Ü®P@$@Gñì@t¿N@ÆX@§-@œº@Óúk@êaA cæA
+?AÊA!ýïA-1HA8d¡ACùANËQAYþ©Ae2Ape[A{ŽAfAÿŽAaA3
+AÌºAffA¥ AªÀA°3lAµÍA»fÆAÁ rAÆ AÌ3ÍAÑÍyA×g&AÝ ÓAâAè4,AíÛ£¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >üú>¥x?D"?ÚÞç@<Ö@G
+8@s×@R~@Š¹/@œß@Ó@éíAA )ùA
+]QAªA!ÄA,÷[A8*³AC^
+ANdAYÄœAdøAp+nA{^ÇAIAâœA|jAA¯ÃAIpA€ã
+Aª|ÉA°vAµ°"A»IÐAÀã}AÆ})AÌÖAÑ°A×J/AÜãÜAâS¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >¿W>h?~é?Ù@U*@F"@rïí@Þ§@ŠEX@Œ¬	@Óº@éyk@ÿà
+A
+#gAV¿A!A,œpA7ðÈAC$!ANWyAYÒAdŸ*AoñA{$ÜA,AÅÈA_tAù!AÍA,zA€Æ'Aª_ÓA¯ùAµ.A»,ÚAÀÆAÆ`4AËùàAÑA×;¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >³>*q?{JÂ?×@%@mv@E:×@r9@jÍ@¥Ñ~@Œ8/@Òà@é@ÿlBA
+éyA
+ÒA!P*A,A7¶ÛABê4AN
+AYPäAd=Ao·AzêïA$AšÑAB}AÜ*Au×AA€©1AªBÞA¯ÜAµv7A»äAÀ©AÆC=AËêŽ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >v
+>ìÍ?w«ð?Õpœ@
+Á@DS#@q @öó@¥]€@»ÄU@Ò+@è¶@þøgA
+¯AâåA!=A,IA7|îAB°FAMãAYøAdJPAo}šAz±Aò-AÚA%A¿3AXáAòA€:Aª%çA¯¿AµY@AºòíAÀd¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >h
+×>¯j?t
+>?Ó¡d@@Ckv@p8Ø@
+@€éÎ@»P~@Ñ·/@è
+à@þA
+u¡AšúA ÜRA,ªA7CABv\AM©ŽAXÝ
+AdeAoCœAzwAÕ8AnäAA¢?A;ëAÕA€oEAªñA¯¢AµJ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Y>tã?pnm?ÑÑû@¶`@BÂ@oQ$@C@€uó@ºÜ¥@ÑCU@çª@þ·A
+;ŽAo
+A ¢eA+ÕŸA7	AB<nAMoÇAX£AcÖxAo	ÐAz=)AžBAQïAëA
+HA
+õAž¡A€RNA©ùÅ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >KJ>fhÆ?lÏ»?Ð¢@ÎŽ@A@niw@m@€
+@ºhÎ@ÐÏ@ç60@ýáA
+ÉA5"A hzA+ÒA6Ï+ABAM5ÜAXi4AcAnÏåAz?ALA4ùAÎŠAhRAÿA©v¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ><>Wí?i0è?Î39@ç @@Ža@mÃ@'@£C@¹ôô@Ð[¥@æÂV@ý)A	ÇÜAû4A .A+aåA6>AAÈALûïAX/GAcbAnøAyÉRA~VAA±¯AY&¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >.»>Ir7?e?ÌcÐ@ÿK@?Ì­@l@³ž@£i@¹@ÏçË@æN|@üµ-A	ïAÁGAô A+'øA6[QAA©ALÂAWõZAc(³An\
+AydAa_AÖ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >€s>:÷p?aóe?Êx@@>å @k²b@?â@¢Š@¹
+D@Ïsõ@åÚ¥@üAVA	TA\AºµA*î
+A6!eAATŸALAW»oAbîÇAn" Ayq¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >)/>,|,?^T?ÈÅ@/ê@=ýL@jÊ­@Ì@¢2¹@žj@Ï @åfË@ûÍ}A	AMoAÇA*Ž A5çyAAÑALN)AWAbÐo¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >­é>
+ æ?ZµÃ?ÆõŠ@H5@=@iâù@X.@¡Ÿß@ž%@Î@@äòò@ûY¢Aà)AAFÛA*z3A5­A@àäAL/Ñ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =èeE>?W?Å&M@`@<-ë@hûM@äW@¡K@·±º@Îj@ä@úåÌAŠ?AÙA
+ïA*@HA55¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =Ën¹>
+Ù?Sx@?ÃVä@xÕ@;F7@h@p~@ ×.@·=ß@Í€@ä
+A@úqòAlQAªA
+î¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =®x1=å *?OÙ?Á@
+(@:^@g+ì@ü§@ cX@¶Ê	@Í0º@ãk@ùþ
+AMû¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =€=È)?L:Œ?¿ž#@
+©t@9vÖ@fD8@Í@ï~@¶V/@ÌŒà@ãZº¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =i.=«3?Hë?œèº@
+ÁÀ@8"@e\@ó@{€@¶~¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =/)==?Dý9?Œa@
+Ú@7§u@dt×@ØF¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  <êwô=bì?A^h?ºIù@	ò_@7.¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  <m;Š=( Ü?=¿?¹W6¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  9°ç\=
+J¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ?!G®?Ð£×@(Qì@hQì@(ö@Ž(ö@Ô(ö@ô(öA
+{A{A*{A:{AJ{AZ{Aj{Az{A
+
+=A
+=A
+=A
+=A¥
+=A­
+=Aµ
+=Aœ
+=AÅ
+=AÍ
+=AÕ
+=AÝ
+=Aå
+=Aí
+=Aõ
+=Aý
+=B
+B
+B
+
+B
+B
+B
+B
+B
+
+B"
+B&
+B*
+B.
+B2
+B6
+B:
+B>
+BB
+BF
+BJ
+BN
+BR
+BV
+BZ
+B^
+Bb
+Bf
+Bj
+Bn
+Br
+Bv
+Bz
+B~
+?
+žR?Ï\)@'®@g®@×
+@³×
+@Ó×
+@ó×
+A	ë
+Aë
+A)ë
+A9ë
+AIë
+AYë
+Aië
+Ayë
+AõÃAõÃAõÃAõÃA€õÃA¬õÃAŽõÃAŒõÃAÄõÃAÌõÃAÔõÃAÜõÃAäõÃAìõÃAôõÃAüõÃBzáBzáB
+záBzáBzáBzáBzáB
+záB"záB&záB*záB.záB2záB6záB:záB>záBBzáBFzáBJzáBNzáBRzáBVzáBZzáB^záBbzáBfzáBjzáBnzáBrzáBvzáBzzáB~zá?
+(ö?Î{@'
+>@g
+>@
+@³
+@Ó
+@ó
+A	ÂAÂA)ÂA9ÂAIÂAYÂAiÂAyÂAáHAáHAáHAáHA€áHA¬áHAŽáHAŒáHAÄáHAÌáHAÔáHAÜáHAäáHAìáHAôáHAüáHBp€Bp€B
+p€Bp€Bp€Bp€Bp€B
+p€B"p€B&p€B*p€B.p€B2p€B6p€B:p€B>p€BBp€BFp€BJp€BNp€BRp€BVp€BZp€B^p€Bbp€Bfp€Bjp€Bnp€Brp€Bvp€Bzp€B~p€??ÌÌÍ@&ff@fff@33@³33@Ó33@ó33A	AA)A9AIAYAiAyAÌÍAÌÍAÌÍAÌÍA€ÌÍA¬ÌÍAŽÌÍAŒÌÍAÄÌÍAÌÌÍAÔÌÍAÜÌÍAäÌÍAìÌÍAôÌÍAüÌÍBffBffB
+ffBffBffBffBffB
+ffB"ffB&ffB*ffB.ffB2ffB6ffB:ffB>ffBBffBFffBJffBNffBRffBVffBZffB^ffBbffBfffBjffBnffBrffBvffBzffB~ff?
+=?Ë
+
+@%Â@eÂ@áH@²áH@ÒáH@òáHA	p€Ap€A)p€A9p€AIp€AYp€Aip€Ayp€AžRAžRAžRAžRA€žRA¬žRAŽžRAŒžRAÄžRAÌžRAÔžRAÜžRAäžRAìžRAôžRAüžRB\)B\)B
+\)B\)B\)B\)B\)B
+\)B"\)B&\)B*\)B.\)B2\)B6\)B:\)B>\)BB\)BF\)BJ\)BN\)BR\)BV\)BZ\)B^\)Bb\)Bf\)Bj\)Bn\)Br\)Bv\)Bz\)B~\)?zá?Ê=p@%
+ž@e
+ž@\@²\@Ò\@ò\A	G®AG®A)G®A9G®AIG®AYG®AiG®AyG®A£×A£×A£×A£×A€£×A¬£×AŽ£×AŒ£×AÄ£×AÌ£×AÔ£×AÜ£×Aä£×Aì£×Aô£×Aü£×BQìBQìB
+QìBQìBQìBQìBQìB
+QìB"QìB&QìB*QìB.QìB2QìB6QìB:QìB>QìBBQìBFQìBJQìBNQìBRQìBVQìBZQìB^QìBbQìBfQìBjQìBnQìBrQìBvQìBzQìB~Qì?ë
+?ÈõÂ@$zá@dzá@=q@²=q@Ò=q@ò=qA	
+žA
+žA)
+žA9
+žAI
+žAY
+žAi
+žAy
+žA\A\A\A\A€\A¬\AŽ\AŒ\AÄ\AÌ\AÔ\AÜ\Aä\Aì\Aô\Aü\BG®BG®B
+G®BG®BG®BG®BG®B
+G®B"G®B&G®B*G®B.G®B2G®B6G®B:G®B>G®BBG®BFG®BJG®BNG®BRG®BVG®BZG®B^G®BbG®BfG®BjG®BnG®BrG®BvG®BzG®B~G®?\)?Ç®@#×
+@c×
+@ë
+@±ë
+@Ñë
+@ñë
+AõÃAõÃA(õÃA8õÃAHõÃAXõÃAhõÃAxõÃAzáAzáAzáAzáA€záA¬záAŽzáAŒzáAÄzáAÌzáAÔzáAÜzáAäzáAìzáAôzáAüzáB=qB=qB
+=qB=qB=qB=qB=qB
+=qB"=qB&=qB*=qB.=qB2=qB6=qB:=qB>=qBB=qBF=qBJ=qBN=qBR=qBV=qBZ=qB^=qBb=qBf=qBj=qBn=qBr=qBv=qBz=qB~=q?
+ÌÍ?Æff@#33@c33@@±@Ñ@ñAÌÍAÌÍA(ÌÍA8ÌÍAHÌÍAXÌÍAhÌÍAxÌÍAffAffAffAffA€ffA¬ffAŽffAŒffAÄffAÌffAÔffAÜffAäffAìffAôffAüffB33B33B
+33B33B33B33B33B
+33B"33B&33B*33B.33B233B633B:33B>33BB33BF33BJ33BN33BR33BV33BZ33B^33Bb33Bf33Bj33Bn33Br33Bv33Bz33B~33?
+=q?Å
+ž@"\@b\@G®@±G®@ÑG®@ñG®A£×A£×A(£×A8£×AH£×AX£×Ah£×Ax£×AQìAQìAQìAQìA€QìA¬QìAŽQìAŒQìAÄQìAÌQìAÔQìAÜQìAäQìAìQìAôQìAüQìB(öB(öB
+(öB(öB(öB(öB(öB
+(öB"(öB&(öB*(öB.(öB2(öB6(öB:(öB>(öBB(öBF(öBJ(öBN(öBR(öBV(öBZ(öB^(öBb(öBf(öBj(öBn(öBr(öBv(öBz(öB~(ö?®?Ã×
+@!ë
+@aë
+@õÂ@°õÂ@ÐõÂ@ðõÂAzáAzáA(záA8záAHzáAXzáAhzáAxzáA=qA=qA=qA=qA€=qA¬=qAŽ=qAŒ=qAÄ=qAÌ=qAÔ=qAÜ=qAä=qAì=qAô=qAü=qB
+žB
+žB
+
+žB
+žB
+žB
+žB
+žB
+
+žB"
+žB&
+žB*
+žB.
+žB2
+žB6
+žB:
+žB>
+žBB
+žBF
+žBJ
+žBN
+žBR
+žBV
+žBZ
+žB^
+žBb
+žBf
+žBj
+žBn
+žBr
+žBv
+žBz
+žB~
+ž?
+ž?Â\@!G®@aG®@£×@°£×@Ð£×@ð£×AQìAQìA(QìA8QìAHQìAXQìAhQìAxQìA(öA(öA(öA(öA€(öA¬(öAŽ(öAŒ(öAÄ(öAÌ(öAÔ(öAÜ(öAä(öAì(öAô(öAü(öB{B{B
+{B{B{B{B{B
+{B"{B&{B*{B.{B2{B6{B:{B>{BB{BF{BJ{BN{BR{BV{BZ{B^{Bb{Bf{Bj{Bn{Br{Bv{Bz{B~{?\?ÁG®@ £×@`£×@Qì@°Qì@ÐQì@ðQìA(öA(öA((öA8(öAH(öAX(öAh(öAx(öA{A{A{A{A€{A¬{AŽ{AŒ{AÄ{AÌ{AÔ{AÜ{Aä{Aì{Aô{Aü{B
+=B
+=B
+
+=B
+=B
+=B
+=B
+=B
+
+=B"
+=B&
+=B*
+=B.
+=B2
+=B6
+=B:
+=B>
+=BB
+=BF
+=BJ
+=BN
+=BR
+=BV
+=BZ
+=B^
+=Bb
+=Bf
+=Bj
+=Bn
+=Br
+=Bv
+=Bz
+=B~
+=?   ?À  @   @`  @  @°  @Ð  @ð  A  A  A(  A8  AH  AX  Ah  Ax  A  A  A  A  A€  A¬  AŽ  AŒ  AÄ  AÌ  AÔ  AÜ  Aä  Aì  Aô  Aü  B  B  B
+  B  B  B  B  B
+  B"  B&  B*  B.  B2  B6  B:  B>  BB  BF  BJ  BN  BR  BV  BZ  B^  Bb  Bf  Bj  Bn  Br  Bv  Bz  B~  >úáH?ŸžR@\)@_\)@®@¯®@Ï®@ï®A×
+A×
+A'×
+A7×
+AG×
+AW×
+Ag×
+Aw×
+Aë
+Aë
+Aë
+Aë
+A£ë
+A«ë
+A³ë
+A»ë
+AÃë
+AËë
+AÓë
+AÛë
+Aãë
+Aëë
+Aóë
+Aûë
+BõÃBõÃB	õÃB
+õÃBõÃBõÃBõÃB
+õÃB!õÃB%õÃB)õÃB-õÃB1õÃB5õÃB9õÃB=õÃBAõÃBEõÃBIõÃBMõÃBQõÃBUõÃBYõÃB]õÃBaõÃBeõÃBiõÃBmõÃBqõÃBuõÃByõÃB}õÃ>õÂ?œp€@
+žR@^žR@\)@¯\)@Ï\)@ï\)A®A®A'®A7®AG®AW®Ag®Aw®A×
+A×
+A×
+A×
+A£×
+A«×
+A³×
+A»×
+AÃ×
+AË×
+AÓ×
+AÛ×
+Aã×
+Aë×
+Aó×
+Aû×
+Bë
+Bë
+B	ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B!ë
+B%ë
+B)ë
+B-ë
+B1ë
+B5ë
+B9ë
+B=ë
+BAë
+BEë
+BIë
+BMë
+BQë
+BUë
+BYë
+B]ë
+Baë
+Beë
+Bië
+Bmë
+Bqë
+Buë
+Byë
+B}ë
+>ð£×?Œ(ö@
+{@^{@
+=@¯
+=@Ï
+=@ï
+=A
+A
+A'
+A7
+AG
+AW
+Ag
+Aw
+AÂAÂAÂAÂA£ÂA«ÂA³ÂA»ÂAÃÂAËÂAÓÂAÛÂAãÂAëÂAóÂAûÂBáHBáHB	áHB
+áHBáHBáHBáHB
+áHB!áHB%áHB)áHB-áHB1áHB5áHB9áHB=áHBAáHBEáHBIáHBMáHBQáHBUáHBYáHB]áHBaáHBeáHBiáHBmáHBqáHBuáHByáHB}áH>ë
+?ºáH@
+p€@]p€@žR@®žR@ÎžR@îžRA\)A\)A'\)A7\)AG\)AW\)Ag\)Aw\)A®A®A®A®A£®A«®A³®A»®AÃ®AË®AÓ®AÛ®Aã®Aë®Aó®Aû®B×
+B×
+B	×
+B
+×
+B×
+B×
+B×
+B
+×
+B!×
+B%×
+B)×
+B-×
+B1×
+B5×
+B9×
+B=×
+BA×
+BE×
+BI×
+BM×
+BQ×
+BU×
+BY×
+B]×
+Ba×
+Be×
+Bi×
+Bm×
+Bq×
+Bu×
+By×
+B}×
+>æff?¹@
+ÌÍ@\ÌÍ@ff@®ff@Îff@îffA33A33A'33A733AG33AW33Ag33Aw33AAAAA£A«A³A»AÃAËAÓAÛAãAëAóAûBÌÍBÌÍB	ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB!ÌÍB%ÌÍB)ÌÍB-ÌÍB1ÌÍB5ÌÍB9ÌÍB=ÌÍBAÌÍBEÌÍBIÌÍBMÌÍBQÌÍBUÌÍBYÌÍB]ÌÍBaÌÍBeÌÍBiÌÍBmÌÍBqÌÍBuÌÍByÌÍB}ÌÍ>áG®?žQì@
+(ö@\(ö@{@®{@Î{@î{A
+=A
+=A'
+=A7
+=AG
+=AW
+=Ag
+=Aw
+=A
+A
+A
+A
+A£
+A«
+A³
+A»
+AÃ
+AË
+AÓ
+AÛ
+Aã
+Aë
+Aó
+Aû
+BÂBÂB	ÂB
+ÂBÂBÂBÂB
+ÂB!ÂB%ÂB)ÂB-ÂB1ÂB5ÂB9ÂB=ÂBAÂBEÂBIÂBMÂBQÂBUÂBYÂB]ÂBaÂBeÂBiÂBmÂBqÂBuÂByÂB}Â>Ü(ö?·
+>@
+@[
+@Â@­Â@ÍÂ@íÂAáHAáHA&áHA6áHAFáHAVáHAfáHAváHAp€Ap€Ap€Ap€A£p€A«p€A³p€A»p€AÃp€AËp€AÓp€AÛp€Aãp€Aëp€Aóp€Aûp€BžRBžRB	žRB
+žRBžRBžRBžRB
+žRB!žRB%žRB)žRB-žRB1žRB5žRB9žRB=žRBAžRBEžRBIžRBMžRBQžRBUžRBYžRB]žRBažRBežRBižRBmžRBqžRBužRByžRB}žR>×
+=?µÂ@áH@ZáH@p€@­p€@Íp€@íp€AžRAžRA&žRA6žRAFžRAVžRAfžRAvžRA\)A\)A\)A\)A£\)A«\)A³\)A»\)AÃ\)AË\)AÓ\)AÛ\)Aã\)Aë\)Aó\)Aû\)B®B®B	®B
+®B®B®B®B
+®B!®B%®B)®B-®B1®B5®B9®B=®BA®BE®BI®BM®BQ®BU®BY®B]®Ba®Be®Bi®Bm®Bq®Bu®By®B}®>Ñë
+?Žzá@=q@Z=q@
+ž@­
+ž@Í
+ž@í
+žA\A\A&\A6\AF\AV\Af\Av\AG®AG®AG®AG®A£G®A«G®A³G®A»G®AÃG®AËG®AÓG®AÛG®AãG®AëG®AóG®AûG®B£×B£×B	£×B
+£×B£×B£×B£×B
+£×B!£×B%£×B)£×B-£×B1£×B5£×B9£×B=£×BA£×BE£×BI£×BM£×BQ£×BU£×BY£×B]£×Ba£×Be£×Bi£×Bm£×Bq£×Bu£×By£×B}£×>ÌÌÍ?³33@@Y@ÌÍ@¬ÌÍ@ÌÌÍ@ìÌÍAffAffA&ffA6ffAFffAVffAfffAvffA33A33A33A33A£33A«33A³33A»33AÃ33AË33AÓ33AÛ33Aã33Aë33Aó33Aû33BBB	B
+BBBB
+B!B%B)B-B1B5B9B=BABEBIBMBQBUBYB]BaBeBiBmBqBuByB}>Ç®?±ë
+@õÂ@XõÂ@zá@¬zá@Ìzá@ìzáA=qA=qA&=qA6=qAF=qAV=qAf=qAv=qA
+žA
+žA
+žA
+žA£
+žA«
+žA³
+žA»
+žAÃ
+žAË
+žAÓ
+žAÛ
+žAã
+žAë
+žAó
+žAû
+žB\B\B	\B
+\B\B\B\B
+\B!\B%\B)\B-\B1\B5\B9\B=\BA\BE\BI\BM\BQ\BU\BY\B]\Ba\Be\Bi\Bm\Bq\Bu\By\B}\>Â\?°£×@Qì@XQì@(ö@¬(ö@Ì(ö@ì(öA{A{A&{A6{AF{AV{Af{Av{A
+=A
+=A
+=A
+=A£
+=A«
+=A³
+=A»
+=AÃ
+=AË
+=AÓ
+=AÛ
+=Aã
+=Aë
+=Aó
+=Aû
+=B
+B
+B	
+B
+
+B
+B
+B
+B
+
+B!
+B%
+B)
+B-
+B1
+B5
+B9
+B=
+BA
+BE
+BI
+BM
+BQ
+BU
+BY
+B]
+Ba
+Be
+Bi
+Bm
+Bq
+Bu
+By
+B}
+>œp€?¯\)@®@W®@×
+@«×
+@Ë×
+@ë×
+Aë
+Aë
+A%ë
+A5ë
+AEë
+AUë
+Aeë
+Auë
+AõÃAõÃAõÃAõÃA¢õÃAªõÃA²õÃAºõÃAÂõÃAÊõÃAÒõÃAÚõÃAâõÃAêõÃAòõÃAúõÃBzáBzáB	záB
+záBzáBzáBzáB
+záB!záB%záB)záB-záB1záB5záB9záB=záBAzáBEzáBIzáBMzáBQzáBUzáBYzáB]záBazáBezáBizáBmzáBqzáBuzáByzáB}zá>žQì?®{@
+>@W
+>@
+@«
+@Ë
+@ë
+AÂAÂA%ÂA5ÂAEÂAUÂAeÂAuÂAáHAáHAáHAáHA¢áHAªáHA²áHAºáHAÂáHAÊáHAÒáHAÚáHAâáHAêáHAòáHAúáHBp€Bp€B	p€B
+p€Bp€Bp€Bp€B
+p€B!p€B%p€B)p€B-p€B1p€B5p€B9p€B=p€BAp€BEp€BIp€BMp€BQp€BUp€BYp€B]p€Bap€Bep€Bip€Bmp€Bqp€Bup€Byp€B}p€>³33?¬ÌÍ@ff@Vff@33@«33@Ë33@ë33AAA%A5AEAUAeAuAÌÍAÌÍAÌÍAÌÍA¢ÌÍAªÌÍA²ÌÍAºÌÍAÂÌÍAÊÌÍAÒÌÍAÚÌÍAâÌÍAêÌÍAòÌÍAúÌÍBffBffB	ffB
+ffBffBffBffB
+ffB!ffB%ffB)ffB-ffB1ffB5ffB9ffB=ffBAffBEffBIffBMffBQffBUffBYffB]ffBaffBeffBiffBmffBqffBuffByffB}ff>®{?«
+@Â@UÂ@áH@ªáH@ÊáH@êáHAp€Ap€A%p€A5p€AEp€AUp€Aep€Aup€AžRAžRAžRAžRA¢žRAªžRA²žRAºžRAÂžRAÊžRAÒžRAÚžRAâžRAêžRAòžRAúžRB\)B\)B	\)B
+\)B\)B\)B\)B
+\)B!\)B%\)B)\)B-\)B1\)B5\)B9\)B=\)BA\)BE\)BI\)BM\)BQ\)BU\)BY\)B]\)Ba\)Be\)Bi\)Bm\)Bq\)Bu\)By\)B}\)>šõÃ?ª=q@
+ž@U
+ž@\@ª\@Ê\@ê\AG®AG®A%G®A5G®AEG®AUG®AeG®AuG®A£×A£×A£×A£×A¢£×Aª£×A²£×Aº£×AÂ£×AÊ£×AÒ£×AÚ£×Aâ£×Aê£×Aò£×Aú£×BQìBQìB	QìB
+QìBQìBQìBQìB
+QìB!QìB%QìB)QìB-QìB1QìB5QìB9QìB=QìBAQìBEQìBIQìBMQìBQQìBUQìBYQìB]QìBaQìBeQìBiQìBmQìBqQìBuQìByQìB}Qì>£×
+?šõÂ@zá@Tzá@=q@ª=q@Ê=q@ê=qA
+žA
+žA%
+žA5
+žAE
+žAU
+žAe
+žAu
+žA\A\A\A\A¢\Aª\A²\Aº\AÂ\AÊ\AÒ\AÚ\Aâ\Aê\Aò\Aú\BG®BG®B	G®B
+G®BG®BG®BG®B
+G®B!G®B%G®B)G®B-G®B1G®B5G®B9G®B=G®BAG®BEG®BIG®BMG®BQG®BUG®BYG®B]G®BaG®BeG®BiG®BmG®BqG®BuG®ByG®B}G®>žR?§®@×
+@S×
+@ë
+@©ë
+@Éë
+@éë
+AõÃAõÃA$õÃA4õÃADõÃATõÃAdõÃAtõÃAzáAzáAzáAzáA¢záAªzáA²záAºzáAÂzáAÊzáAÒzáAÚzáAâzáAêzáAòzáAúzáB=qB=qB	=qB
+=qB=qB=qB=qB
+=qB!=qB%=qB)=qB-=qB1=qB5=qB9=qB==qBA=qBE=qBI=qBM=qBQ=qBU=qBY=qB]=qBa=qBe=qBi=qBm=qBq=qBu=qBy=qB}=q>?Šff@33@S33@@©@É@éAÌÍAÌÍA$ÌÍA4ÌÍADÌÍATÌÍAdÌÍAtÌÍAffAffAffAffA¢ffAªffA²ffAºffAÂffAÊffAÒffAÚffAâffAêffAòffAúffB33B33B	33B
+33B33B33B33B
+33B!33B%33B)33B-33B133B533B933B=33BA33BE33BI33BM33BQ33BU33BY33B]33Ba33Be33Bi33Bm33Bq33Bu33By33B}33>zá?¥
+ž@\@R\@G®@©G®@ÉG®@éG®A£×A£×A$£×A4£×AD£×AT£×Ad£×At£×AQìAQìAQìAQìA¢QìAªQìA²QìAºQìAÂQìAÊQìAÒQìAÚQìAâQìAêQìAòQìAúQìB(öB(öB	(öB
+(öB(öB(öB(öB
+(öB!(öB%(öB)(öB-(öB1(öB5(öB9(öB=(öBA(öBE(öBI(öBM(öBQ(öBU(öBY(öB](öBa(öBe(öBi(öBm(öBq(öBu(öBy(öB}(ö>\)?£×
+@ë
+@Që
+@õÃ@šõÃ@ÈõÃ@èõÃAzáAzáA$záA4záADzáATzáAdzáAtzáA=qA=qA=qA=qA¢=qAª=qA²=qAº=qAÂ=qAÊ=qAÒ=qAÚ=qAâ=qAê=qAò=qAú=qB
+žB
+žB	
+žB
+
+žB
+žB
+žB
+žB
+
+žB!
+žB%
+žB)
+žB-
+žB1
+žB5
+žB9
+žB=
+žBA
+žBE
+žBI
+žBM
+žBQ
+žBU
+žBY
+žB]
+žBa
+žBe
+žBi
+žBm
+žBq
+žBu
+žBy
+žB}
+ž>=q?¢\@G®@QG®@£×@š£×@È£×@è£×AQìAQìA$QìA4QìADQìATQìAdQìAtQìA(öA(öA(öA(öA¢(öAª(öA²(öAº(öAÂ(öAÊ(öAÒ(öAÚ(öAâ(öAê(öAò(öAú(öB{B{B	{B
+{B{B{B{B
+{B!{B%{B){B-{B1{B5{B9{B={BA{BE{BI{BM{BQ{BU{BY{B]{Ba{Be{Bi{Bm{Bq{Bu{By{B}{>
+
+ž?¡G®@£×@P£×@Qì@šQì@ÈQì@èQìA(öA(öA$(öA4(öAD(öAT(öAd(öAt(öA{A{A{A{A¢{Aª{A²{Aº{AÂ{AÊ{AÒ{AÚ{Aâ{Aê{Aò{Aú{B
+=B
+=B	
+=B
+=B
+=B
+=B
+=B
+
+=B!
+=B%
+=B)
+=B-
+=B1
+=B5
+=B9
+=B=
+=BA
+=BE
+=BI
+=BM
+=BQ
+=BU
+=BY
+=B]
+=Ba
+=Be
+=Bi
+=Bm
+=Bq
+=Bu
+=By
+=B}
+=>  ?   @  @P  @  @š  @È  @è  A  A  A$  A4  AD  AT  Ad  At  A  A  A  A  A¢  Aª  A²  Aº  AÂ  AÊ  AÒ  AÚ  Aâ  Aê  Aò  Aú  B  B  B	  B
+  B  B  B  B
+  B!  B%  B)  B-  B1  B5  B9  B=  BA  BE  BI  BM  BQ  BU  BY  B]  Ba  Be  Bi  Bm  Bq  Bu  By  B}  >uÂ?žR@\)@O\)@®@§®@Ç®@ç®A×
+A×
+A#×
+A3×
+AC×
+AS×
+Ac×
+As×
+Aë
+Aë
+Aë
+Aë
+A¡ë
+A©ë
+A±ë
+A¹ë
+AÁë
+AÉë
+AÑë
+AÙë
+Aáë
+Aéë
+Añë
+Aùë
+B õÃBõÃBõÃB
+õÃBõÃBõÃBõÃB
+õÃB õÃB$õÃB(õÃB,õÃB0õÃB4õÃB8õÃB<õÃB@õÃBDõÃBHõÃBLõÃBPõÃBTõÃBXõÃB\õÃB`õÃBdõÃBhõÃBlõÃBpõÃBtõÃBxõÃB|õÃ>k
+?p€@žR@NžR@\)@§\)@Ç\)@ç\)A®A®A#®A3®AC®AS®Ac®As®A×
+A×
+A×
+A×
+A¡×
+A©×
+A±×
+A¹×
+AÁ×
+AÉ×
+AÑ×
+AÙ×
+Aá×
+Aé×
+Añ×
+Aù×
+B ë
+Bë
+Bë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B ë
+B$ë
+B(ë
+B,ë
+B0ë
+B4ë
+B8ë
+B<ë
+B@ë
+BDë
+BHë
+BLë
+BPë
+BTë
+BXë
+B\ë
+B`ë
+Bdë
+Bhë
+Blë
+Bpë
+Btë
+Bxë
+B|ë
+>aG®?(ö@{@N{@
+=@§
+=@Ç
+=@ç
+=A
+A
+A#
+A3
+AC
+AS
+Ac
+As
+AÂAÂAÂAÂA¡ÂA©ÂA±ÂA¹ÂAÁÂAÉÂAÑÂAÙÂAáÂAéÂAñÂAùÂB áHBáHBáHB
+áHBáHBáHBáHB
+áHB áHB$áHB(áHB,áHB0áHB4áHB8áHB<áHB@áHBDáHBHáHBLáHBPáHBTáHBXáHB\áHB`áHBdáHBháHBláHBpáHBtáHBxáHB|áH>W
+=?áH@
+p€@Mp€@žR@ŠžR@ÆžR@æžRA\)A\)A#\)A3\)AC\)AS\)Ac\)As\)A®A®A®A®A¡®A©®A±®A¹®AÁ®AÉ®AÑ®AÙ®Aá®Aé®Añ®Aù®B ×
+B×
+B×
+B
+×
+B×
+B×
+B×
+B
+×
+B ×
+B$×
+B(×
+B,×
+B0×
+B4×
+B8×
+B<×
+B@×
+BD×
+BH×
+BL×
+BP×
+BT×
+BX×
+B\×
+B`×
+Bd×
+Bh×
+Bl×
+Bp×
+Bt×
+Bx×
+B|×
+>LÌÍ?@
+ÌÍ@LÌÍ@ff@Šff@Æff@æffA33A33A#33A333AC33AS33Ac33As33AAAAA¡A©A±A¹AÁAÉAÑAÙAáAéAñAùB ÌÍBÌÍBÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB ÌÍB$ÌÍB(ÌÍB,ÌÍB0ÌÍB4ÌÍB8ÌÍB<ÌÍB@ÌÍBDÌÍBHÌÍBLÌÍBPÌÍBTÌÍBXÌÍB\ÌÍB`ÌÍBdÌÍBhÌÍBlÌÍBpÌÍBtÌÍBxÌÍB|ÌÍ>B\?Qì@
+(ö@L(ö@{@Š{@Æ{@æ{A
+=A
+=A#
+=A3
+=AC
+=AS
+=Ac
+=As
+=A
+A
+A
+A
+A¡
+A©
+A±
+A¹
+AÁ
+AÉ
+AÑ
+AÙ
+Aá
+Aé
+Añ
+Aù
+B ÂBÂBÂB
+ÂBÂBÂBÂB
+ÂB ÂB$ÂB(ÂB,ÂB0ÂB4ÂB8ÂB<ÂB@ÂBDÂBHÂBLÂBPÂBTÂBXÂB\ÂB`ÂBdÂBhÂBlÂBpÂBtÂBxÂB|Â>8Qì?
+>@
+
+@K
+@
+Â@¥Â@ÅÂ@åÂAáHAáHA"áHA2áHABáHARáHAbáHAráHAp€Ap€Ap€Ap€A¡p€A©p€A±p€A¹p€AÁp€AÉp€AÑp€AÙp€Aáp€Aép€Añp€Aùp€B žRBžRBžRB
+žRBžRBžRBžRB
+žRB žRB$žRB(žRB,žRB0žRB4žRB8žRB<žRB@žRBDžRBHžRBLžRBPžRBTžRBXžRB\žRB`žRBdžRBhžRBlžRBpžRBtžRBxžRB|žR>.{?Â@
+áH@JáH@
+p€@¥p€@Åp€@åp€AžRAžRA"žRA2žRABžRARžRAbžRAržRA\)A\)A\)A\)A¡\)A©\)A±\)A¹\)AÁ\)AÉ\)AÑ\)AÙ\)Aá\)Aé\)Añ\)Aù\)B ®B®B®B
+®B®B®B®B
+®B ®B$®B(®B,®B0®B4®B8®B<®B@®BD®BH®BL®BP®BT®BX®B\®B`®Bd®Bh®Bl®Bp®Bt®Bx®B|®>#×
+?zá@
+=q@J=q@
+
+ž@¥
+ž@Å
+ž@å
+žA\A\A"\A2\AB\AR\Ab\Ar\AG®AG®AG®AG®A¡G®A©G®A±G®A¹G®AÁG®AÉG®AÑG®AÙG®AáG®AéG®AñG®AùG®B £×B£×B£×B
+£×B£×B£×B£×B
+£×B £×B$£×B(£×B,£×B0£×B4£×B8£×B<£×B@£×BD£×BH£×BL£×BP£×BT£×BX£×B\£×B`£×Bd£×Bh£×Bl£×Bp£×Bt£×Bx£×B|£×>?33@	@I@ÌÍ@€ÌÍ@ÄÌÍ@äÌÍAffAffA"ffA2ffABffARffAbffArffA33A33A33A33A¡33A©33A±33A¹33AÁ33AÉ33AÑ33AÙ33Aá33Aé33Añ33Aù33B BBB
+BBBB
+B B$B(B,B0B4B8B<B@BDBHBLBPBTBXB\B`BdBhBlBpBtBxB|>\)?ë
+@õÃ@HõÃ@zá@€zá@Äzá@äzáA=qA=qA"=qA2=qAB=qAR=qAb=qAr=qA
+žA
+žA
+žA
+žA¡
+žA©
+žA±
+žA¹
+žAÁ
+žAÉ
+žAÑ
+žAÙ
+žAá
+žAé
+žAñ
+žAù
+žB \B\B\B
+\B\B\B\B
+\B \B$\B(\B,\B0\B4\B8\B<\B@\BD\BH\BL\BP\BT\BX\B\\B`\Bd\Bh\Bl\Bp\Bt\Bx\B|\>
+ž?£×@Qì@HQì@(ö@€(ö@Ä(ö@ä(öA{A{A"{A2{AB{AR{Ab{Ar{A
+=A
+=A
+=A
+=A¡
+=A©
+=A±
+=A¹
+=AÁ
+=AÉ
+=AÑ
+=AÙ
+=Aá
+=Aé
+=Añ
+=Aù
+=B 
+B
+B
+B
+
+B
+B
+B
+B
+
+B 
+B$
+B(
+B,
+B0
+B4
+B8
+B<
+B@
+BD
+BH
+BL
+BP
+BT
+BX
+B\
+B`
+Bd
+Bh
+Bl
+Bp
+Bt
+Bx
+B|
+=õÂ?\)@®@G®@×
+@£×
+@Ã×
+@ã×
+Aë
+Aë
+A!ë
+A1ë
+AAë
+AQë
+Aaë
+Aqë
+AõÃAõÃAõÃAõÃA õÃAšõÃA°õÃAžõÃAÀõÃAÈõÃAÐõÃAØõÃAàõÃAèõÃAðõÃAøõÃB záBzáBzáB
+záBzáBzáBzáB
+záB záB$záB(záB,záB0záB4záB8záB<záB@záBDzáBHzáBLzáBPzáBTzáBXzáB\záB`záBdzáBhzáBlzáBpzáBtzáBxzáB|zá=áG®?{@
+=@G
+=@
+@£
+@Ã
+@ã
+AÂAÂA!ÂA1ÂAAÂAQÂAaÂAqÂAáHAáHAáHAáHA áHAšáHA°áHAžáHAÀáHAÈáHAÐáHAØáHAàáHAèáHAðáHAøáHB p€Bp€Bp€B
+p€Bp€Bp€Bp€B
+p€B p€B$p€B(p€B,p€B0p€B4p€B8p€B<p€B@p€BDp€BHp€BLp€BPp€BTp€BXp€B\p€B`p€Bdp€Bhp€Blp€Bpp€Btp€Bxp€B|p€=ÌÌÍ?ÌÍ@ff@Fff@33@£33@Ã33@ã33AAA!A1AAAQAaAqAÌÍAÌÍAÌÍAÌÍA ÌÍAšÌÍA°ÌÍAžÌÍAÀÌÍAÈÌÍAÐÌÍAØÌÍAàÌÍAèÌÍAðÌÍAøÌÍB ffBffBffB
+ffBffBffBffB
+ffB ffB$ffB(ffB,ffB0ffB4ffB8ffB<ffB@ffBDffBHffBLffBPffBTffBXffB\ffB`ffBdffBhffBlffBpffBtffBxffB|ff=žQì?
+@Â@EÂ@áH@¢áH@ÂáH@âáHAp€Ap€A!p€A1p€AAp€AQp€Aap€Aqp€AžRAžRAžRAžRA žRAšžRA°žRAžžRAÀžRAÈžRAÐžRAØžRAàžRAèžRAðžRAøžRB \)B\)B\)B
+\)B\)B\)B\)B
+\)B \)B$\)B(\)B,\)B0\)B4\)B8\)B<\)B@\)BD\)BH\)BL\)BP\)BT\)BX\)B\\)B`\)Bd\)Bh\)Bl\)Bp\)Bt\)Bx\)B|\)=£×
+?=q@
+ž@E
+ž@\@¢\@Â\@â\AG®AG®A!G®A1G®AAG®AQG®AaG®AqG®A£×A£×A£×A£×A £×Aš£×A°£×Až£×AÀ£×AÈ£×AÐ£×AØ£×Aà£×Aè£×Að£×Aø£×B QìBQìBQìB
+QìBQìBQìBQìB
+QìB QìB$QìB(QìB,QìB0QìB4QìB8QìB<QìB@QìBDQìBHQìBLQìBPQìBTQìBXQìB\QìB`QìBdQìBhQìBlQìBpQìBtQìBxQìB|Qì=\)?õÃ@zá@Dzá@=q@¢=q@Â=q@â=qA
+žA
+žA!
+žA1
+žAA
+žAQ
+žAa
+žAq
+žA\A\A\A\A \Aš\A°\Až\AÀ\AÈ\AÐ\AØ\Aà\Aè\Að\Aø\B G®BG®BG®B
+G®BG®BG®BG®B
+G®B G®B$G®B(G®B,G®B0G®B4G®B8G®B<G®B@G®BDG®BHG®BLG®BPG®BTG®BXG®B\G®B`G®BdG®BhG®BlG®BpG®BtG®BxG®B|G®=uÂ?®@×
+@C×
+@ë
+@¡ë
+@Áë
+@áë
+A õÃAõÃA õÃA0õÃA@õÃAPõÃA`õÃApõÃAzáAzáAzáAzáA záAšzáA°záAžzáAÀzáAÈzáAÐzáAØzáAàzáAèzáAðzáAøzáB =qB=qB=qB
+=qB=qB=qB=qB
+=qB =qB$=qB(=qB,=qB0=qB4=qB8=qB<=qB@=qBD=qBH=qBL=qBP=qBT=qBX=qB\=qB`=qBd=qBh=qBl=qBp=qBt=qBx=qB|=q=LÌÍ?ff@33@C33@@¡@Á@áA ÌÍAÌÍA ÌÍA0ÌÍA@ÌÍAPÌÍA`ÌÍApÌÍAffAffAffAffA ffAšffA°ffAžffAÀffAÈffAÐffAØffAàffAèffAðffAøffB 33B33B33B
+33B33B33B33B
+33B 33B$33B(33B,33B033B433B833B<33B@33BD33BH33BL33BP33BT33BX33B\33B`33Bd33Bh33Bl33Bp33Bt33Bx33B|33=#×
+?
+
+ž@\@B\@G®@¡G®@ÁG®@áG®A £×A£×A £×A0£×A@£×AP£×A`£×Ap£×AQìAQìAQìAQìA QìAšQìA°QìAžQìAÀQìAÈQìAÐQìAØQìAàQìAèQìAðQìAøQìB (öB(öB(öB
+(öB(öB(öB(öB
+(öB (öB$(öB((öB,(öB0(öB4(öB8(öB<(öB@(öBD(öBH(öBL(öBP(öBT(öBX(öB\(öB`(öBd(öBh(öBl(öBp(öBt(öBx(öB|(ö<õÂ?×
+@ë
+@Aë
+@õÃ@ õÃ@ÀõÃ@àõÃA záAzáA záA0záA@záAPzáA`záApzáA=qA=qA=qA=qA =qAš=qA°=qAž=qAÀ=qAÈ=qAÐ=qAØ=qAà=qAè=qAð=qAø=qB 
+žB
+žB
+žB
+
+žB
+žB
+žB
+žB
+
+žB 
+žB$
+žB(
+žB,
+žB0
+žB4
+žB8
+žB<
+žB@
+žBD
+žBH
+žBL
+žBP
+žBT
+žBX
+žB\
+žB`
+žBd
+žBh
+žBl
+žBp
+žBt
+žBx
+žB|
+ž<£×
+?\@G®@AG®@£×@ £×@À£×@à£×A QìAQìA QìA0QìA@QìAPQìA`QìApQìA(öA(öA(öA(öA (öAš(öA°(öAž(öAÀ(öAÈ(öAÐ(öAØ(öAà(öAè(öAð(öAø(öB {B{B{B
+{B{B{B{B
+{B {B${B({B,{B0{B4{B8{B<{B@{BD{BH{BL{BP{BT{BX{B\{B`{Bd{Bh{Bl{Bp{Bt{Bx{B|{<#×
+?G®@ £×@@£×@Qì@ Qì@ÀQì@àQìA (öA(öA (öA0(öA@(öAP(öA`(öAp(öA{A{A{A{A {Aš{A°{Až{AÀ{AÈ{AÐ{AØ{Aà{Aè{Að{Aø{B 
+=B
+=B
+=B
+
+=B
+=B
+=B
+=B
+
+=B 
+=B$
+=B(
+=B,
+=B0
+=B4
+=B8
+=B<
+=B@
+=BD
+=BH
+=BL
+=BP
+=BT
+=BX
+=B\
+=B`
+=Bd
+=Bh
+=Bl
+=Bp
+=Bt
+=Bx
+=B|
+=    ?  @   @@  @  @   @À  @à  A   A  A   A0  A@  AP  A`  Ap  A  A  A  A  A   Aš  A°  Až  AÀ  AÈ  AÐ  AØ  Aà  Aè  Að  Aø  B   B  B  B
+  B  B  B  B
+  B   B$  B(  B,  B0  B4  B8  B<  B@  BD  BH  BL  BP  BT  BX  B\  B`  Bd  Bh  Bl  Bp  Bt  Bx  B|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?!1?)ì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+Æ?(þ?¯m®@ý¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?óô?$h,?­E@:@29Q@_¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?U"? É:?«ÎÌ@}@1Q@_«@]â@D¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?¶P?
+*h?©ÿb@ŽÈ@0ià@^
+ø@
+ê@Ä@³ @ÊB¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ??w?š/ê@Í
+@/#@]7;@
+v)@P¶@³+B@ÊÎ@ààZ@÷Œ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+x­?ì¥?Š`@ åX@.o@\O@
+O@ÜÜ@²·g@Éô@àl@÷G
+AÌAb|¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?ÙÚ?M³?€?ÿû6@-²²@[gÊ@q@hý@²C@É
+@ßø¡@öÓ.AÖÜAD"A
+±hA)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?;	?®á?¢Á?þ+Î@,Êþ@Z@@õ#@±Ï¯@Èª;@ßÈ@ö_TAðA
+5A
+wzA(äÀA4RA?£¶¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ? 7?
+? ò6?ü\e@+ãJ@Ya@Šœ@I@±[Õ@È6a@ßí@õëyAcAÐHA
+=A(ªÓA4A?
+^AJò€AVDT¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ùúÊ?q
+?"Ÿ?úì@*û@X°€@2Þ@
+j@°ç÷@ÇÂ@Þ@õwA)AYA
+A(päA3Þ*A?KoAJžŽAV%úAa@Aläð¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >òœ(?ÒL?SU?øœ@*Ù@WÈð@¿@@°t
+@ÇNš@Þ)5@õÁAï&A\lA
+É±A(6÷A3€<A?AJ~ÈAUì
+AaYRAlÆAx3ÞAÂÇ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ë? 3Z?Ü?öî
+@),
+@Vá4@K&@%³@° >@ÆÚÊ@ÝµV@ôâAµ7A"}A
+ÂA'ýA3jMA>×AJDØAU²
+AadAl©AwùîA³Aj=A¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >äAà>ù)?Žs?õ
+¢@(Dh@Uù@×L@±Ø@¯d@Æfñ@ÝA|@ô
+A{IAèA
+UÕA'ÃA30`A>ŠAJ
+ëAUx1A`åvAlRŒAwÀA€AMHAëAºAcf¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ý<>ñë,?äú?óO)@'\¬@UÃ@cm@=ú@¯
+@Åó@ÜÍ@óš+AA[A® A
+åA'+A2öqA>c·AIÐüAU>BA`«AlÍAwAy­A0PAæóAAT:A
+ÜA£³µ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ÕÆ>ê­??ñÀ@&t÷@T*@ï@Ê@®€«@Å8@ÜYÃ@ó4PAnAt³AáùA'O>A2ŒA>)ÉAIAUUA`qAkÞàAwL%A\¶AYAÉýA A7CAíçA£€A©[,A¯¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Îõ>ão€?F?ï°G@%;@SBR@{µ@VA@®0Í@Å
+Y@Ûåå@òÀqAÍ~A:ÄAš
+A'OA2A=ïÚAI]ATÊeA`7«Ak€ñAw6A?ŸAöaA­AcšALAÐïA£A©>6A®ôÙAŽ«{AºTT¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ÇKQ>Ü2?v¯?íàÞ@$¥@RZ@Û@âg@­Œó@Ä@Ûr
+@òLAA ÖAn
+A&ÛbA2HšA=µíAI#3ATxA_ýœAkkAvØIA"ÈAÙkAAF±AýUA³øA£jA©!?A®×âAŽAºE)A¿ûËAÅ€€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >À
+®>Ôô
+?§6?ìe@#œÊ@Qrá@'ù@n@­I@Ä#¡@Úþ-@ñØ¹AY¢AÆèA4-A&¡rA2žA={þAHéDATVA_ÃÏAk1AvYAÐAŒsAsA)ºAà]A A£M€A©GA®ºëAŽqAº(1A¿ÞÕAÅxAËLAÐôó¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >žÐ
+>Í¶z?×Î?êAü@"Ö@P-@~@E@ú®@¬Õ;@Ã¯Ç@ÚS@ñdßAµAúAú@A&gA1ÔËA=BAH¯VAT
+A_âAj÷'AvdmAèÚA}AV!A
+ÃAÃfAz
+A£0­AšçPA®ôAŽTAº
+;A¿ÁÞAÅxAË/%AÐåÈAÖjAÜEC¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >±f>Æx?U?èr@!îY@O£p@}X@Ð@¬a\@Ã;è@Úu@ðñAåÆAS
+AÀQA&-A1ÜA="AHugASâ¬A_OòAjœ8Av*~AËâAA9)AïÌAŠpA]A£µAšÊYA®üAŽ7A¹îCA¿€æAÅ[AË-AÐÈÐAÖtAÜ6Aáì¹Aç¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ªTÃ>¿:ó?8ì?æ£@!¥@N»»@|pÔ@ö@«í@ÂÈ@Ù¢@ð}&A«ÙAAdA%ó©A1`ïA<Î4AH;zASšÀA_AjJAuðA®ëAeA
+2AÒÖAyA@
+A¢öÀAš­bA®dAŽ©A¹ÑLA¿ïAÅ>AÊõ6AÐ«ÚAÖb}AÜ AáÏÄAçgAí=	Aòåâ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >£>·ý?is?äÓ¡@ 
+ç@MÓÿ@{@@«y€@ÂT0@Ù.Œ@ð	HAqéAß/ALuA%¹»A1' A<EAHASnÐA^ÜAjI\Au¶¡AôAHAÿ:AµÞAlA#%A¢ÙÈAškA®GA³ý±A¹ŽTA¿jøAÅ!AÊØ>AÐâAÖE
+AÛü)Aá²ÌAçioAí AòÖ¶AøXAþ61¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ù|>°¿??ã@@77@LìN@z¡g@+@@«Ì@ÁàX@Øºä@ïpA7þA¥CAA%ÎA0íA<ZZAGÇAS4äA^¢*AjpAu|µAtýA+ AâDAçAOA.A¢ŒÑAšsuA®*A³à»A¹_A¿NAÅ€AÊ»HAÐqëAÖ(AÛß2AáÕAçLyAí
+Aò¹¿AøpcAþ'BîÕBÃA¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >×>©è?
+Ê©?á4×@
+O@L@y¹²@·f@ªñ@Ál~@ØG
+@ï!AþAkVAØA%EáA0³&A< lAG²ARúøA^h=AiÕAuBÈAXAªAÅMA{ðA2Aé7A¢ÚAšV~A®
+!A³ÃÅA¹zhA¿1
+AÄç¯AÊQAÐTôAÖ
+AÛÂ;AáxÞAç/Aìæ%AòÉAøSlAþ
+BàYB»«BýB
+ki¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >^4>¢DD?û@?ßen@
+gÎ@K
+å@xÑþ@C@ª
+@Àø€@×Ó/@î­ŒAÄ#A1iA®A%
+ôA0y:A;æAGSÄARÁ
+A^.PAiAuÛA;A
+ñŽAšWA^úAAÌ@A¢äAš9A­ð*A³ŠÎA¹]qA¿AÄÊžAÊ[AÐ7ÿAÕî¡AÛ¥DAá[èAçAìÉ.AòÒAø6uAýíBÑÞB­0BB
+cÓB
+?%B¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  > > ?+×?Ý@
+@J51@wêJ@Ï±@©ª=@ÀÉ@×_U@î9âA6A
+÷|AdÁA$ÒA0?LA;¬AGØAR
+A]ôbAiašAtÎîA
+A
+ÔœAaABAø§A¯JA¢eíAš
+A­Ó4A³×A¹@zAŸ÷
+AÄ­ÁAÊdeAÐAÕÑ«AÛOAá>ñAæõAì¬8AòbÛAø~AýÐ"BÃcBŽBzB
+UXB
+0©B
+ûBçMB»¹¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >}ÅØ>Èü?\n?ÛÆ@e@IM}@w@[×@©6c@Àï@Öë{@íÆAPIA
+œA*ÕA$A0_A;r¥AFßêARM0A]ºvAi'»At A#A
+·ÆAnjA%
+AÛ±ATA¢H÷A§ÿA­¶=A³làA¹#AŸÚ'AÄÊAÊGnAÏþAÕŽµAÛkXAá!ûAæØAìAAòEäA÷üAý³+BŽçB9BkB
+FÜB
+".BýBØÑBŽ#BuBcá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >oJ>X?}
+?Ù÷3@°±@HeÈ@vá@çý@šÂ@¿@Öw¡@íR-A\A
+¡AðçA$^-A/ËsA;8žAF¥ýARCA]AhíÎAt[AÈYA
+ÐAQsAAŸºAu]A¢,A§â€A­GA³OêA¹AŸœ0AÄsÔAÊ*wAÏáAÕŸAÛNaAáAæ»šAìrKAò(ïA÷ßAý4BŠlBŸB]B
+8aB
+³BïBÊVB¥šBùB\KB
+7B!
+	¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >`ÏI>
+Mµ?y{8?Ø'Ë@Èý@G~@u3,@t"@šN®@¿);@ÖÆ@ìÞSAÜoA
+IµA¶úA$$?A/
+A:þËAFlAQÙVA]FAh³áAt!&AlA
+}ÙA4|Aë A¡ÃAXfA¢
+A§Å­A­|QA³2ôAžéAŸ :AÄVÝAÊ
+AÏÄ$AÕzÇAÛ1jAàèAæ±AìUUAò
+øA÷ÂAýy?BñBsBBNB
+)æB
+7BàB»ÛB,Br~BMÐB
+)!B!sB#ßÅB&Ž1¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >RT >|  ?uÜf?ÖXb@áH@F_@tKw@ H@§ÚÔ@Ÿµ`@Õì@ìjyA¢A
+ÇA}
+A#êSA/WA:ÄÝAF2#AQiA]
+¯AhyôAsç9ATA
+`ãAAÎ)AÌA;pA¡òA§š¶A­_ZA³ýAžÌ¡AŸDAÄ9çAÉðAÏ§-AÕ]ÐAÛtAàËAæºAì8^AñïA÷¥¥Aý\HBuBdÇB@B
+jB
+öŒBÒB­_B±BdB?TB
+ŠB õøB#ÑIB&¬B)íB,\Y¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >CØž>m€Ø?r=?Ôù@ù@E®«@scÃ@m@§fú@ŸA@Õ
+@ëöAhA
+ÕÚAC A#°eA/
+«A:ñAEø6AQe{A\ÒÁAh@As­MAA
+CìAúA±3AgÖA
+yA¡Õ
+A§ÀA­BcA²ùAž¯ªAŸfMAÄ
+ñAÉÓAÏ7AÕ@ÚAÚ÷}Aà® AædÄAìgAñÒ
+A÷®Aý?QBzúBVLB1B
+
+ïB
+èABÃBäBz6BUB0ÙB
+
++B ç|B#ÂÎB& B)yqB,TÃB/0B2¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >5]q>_)?nÃ?Ò¹@ß@DÆö@r|@@Šó@œÍ«@Ôš8@ëÄA.šA
+íA	3A#vxA.ãŸA:QAEŸIAQ+A\ÔAhAss_A~à¥A
+&öAÝA<AJàAA¡ž&A§nÉA­%lA²ÜAž³AŸIVAÃÿúAÉ¶AÏmAAÕ#äAÚÚAà*AæGÍAëþpAñµA÷k·Aý"ZBlBGÐB#"B	þtB
+ÙÅBµBiBkºBG
+B"^B
+ý¯B ÙB#ŽSB&€B)jöB,FHB/!B1üëB4Ø=B7¬©¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >&â)>P®?k ?Ðê/@*/@CßF@q^@€»@ŠG@œYÓ@Ô4_@ëëA ô»A
+bAÏGA#<A.©ÒA:AE]APñ¢A\^èAgÌ.As9sA~Š¹A
+
+ AÀ£AwGA-êAäA¡1A§QÔA­wA²¿AžuœAŸ,aAÃãAÉ§AÏPKAÕîAÚœAàt5Aæ*ØAëá{Añ
+A÷NÁAýeB^B9UB§B	ïøB
+ËJBŠBíB]?B8BâB
+ï4B ÊB#¥×B&)B)\{B,7ÌB/
+B1îpB4ÉÁB7¥B:eB=TÑ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >fâ>B3?ga
+?ÏŸ@Bv@B÷@p¬Š@0ß@Š
+k@Œå÷@ÓÀ@êA ºÍA
+(AXA#A.oäA9Ý)AEJoAP·ŽA\$úAg?Arÿ
+A~lÊAíA£¬AZOAòAÇA¡~9A§4ÜA¬ëA²¢#AžXÆAŸiAÃÆ
+AÉ|°AÏ3SAÔéöAÚ AàW=Aæ
+áAëÄAñ{'A÷1ÊAüèmBOB*ÚB,B	á}B
+ŒÏB BsrBNÄB*BgB
+à¹B Œ
+B#\B&r®B)MÿB,)QB/£B1ßôB4»FB7B:qéB=M;B@(BBüù¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >	ë>3·?cÂD?ÍKQ@ZÀ@B×@oÄï@œ@¥@Œr
+@ÓLš@ê'4A àA
+î%A[kA"È±A.5öA9£<AEAP}ÇA[ë
+AgXRArÅA~2ÝAÐAµA=XAóüAªA¡aBA§æA¬ÎA²
+,Až;ÐAœòsAÃ©AÉ_¹AÏ\AÔÍ AÚ£Aà:FAåðêAë§Añ^1A÷ÔAüËwBA
+B
+_B÷°B	ÓB
+®TB¥Bd÷B@HBBöìB
+Ò=B ­B#áB&d2B)?B,ÖB.ö'B1ÑyB4¬ËB7
+B:cnB=>ÀB@BBõcBEÐµBH¥!¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =öà©>%<3?`#k?Ë{ä@s	@A(!@nÝ9@I)@¥#Ž@»þ@@ÒØÍ@é³YA FòA
+Ž8A!}A"ÂA-üA9iNADÖAPCÙA[±Ag
+dArªA}øñA³
+AiŸA bA×AšA¡DLAŠúïA¬±A²h6Až
+ÙAœÕ|AÃ AÉBÃAÎùfAÔ°	AÚf¬Aà
+PAåÓóAëAñA:Aö÷ÝAü®B2B
+ãBé5B	ÄB
+ØB{*BV|B1ÍB
+BèpB
+ÃÂB B#zeB&U·B)1	B,
+ZB.ç¬B1ÂþB4OB7y¡B:TóB=0DB@
+BBæèBEÂ9BHBKxÝBNMI¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =Ùê>Àí?\?É¬{@U@@@l@mõ@ÕN@€¯Ú@»g@Òdò@é?~A 
+A
+zJAçA"TÖA-ÂA9/`ADŠAP	ìA[w1AfäwArQœA}¿A%ALÈAlAºAp²A¡'UAŠÝøA¬A²K?AžâAœžAÃo)AÉ%ÌAÎÜpAÔAÚI¶Aà YAå¶üAëm Añ$CAöÚæAüB$BÿhBÚºB	¶
+B
+]Bl¯BH B#RBþ€BÙõB
+µGB B#kêB&G<B)"B+ýßB.Ù1B1ŽB4ÔB7k&B:FwB=!ÉB?ýBBØlBE³ŸBHBKjaBNE³BQ!BSõq¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =Œó>E?XåÀ?ÇÝ@£@?X¶@m
+Î@as@€;ÿ@»@Ññ@èË€@ÿŠ0A
+@]A­¢A"èA-.A8õtADb¹AOÏþA[=DAfªArÏA}
+Ay.A/ÒAæuAASŒA¡
+^AŠÁA¬w¥A².HA·äìAœAÃR2AÉÖAÎ¿yAÔv
+AÚ,ÀAßãcAåAëP©AñLAöœðAütBBðíBÌ>B	§B
+âB^3B9
+B×Bð(BËzB
+ŠÌB 
+B#]oB&8ÀB)B+ïdB.ÊµB1ŠB4YB7\ªB:7üB=NB?îBBÉñBE¥CBHBK[æBN78BQBSíÛBVÉ-BY¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =ý=ó@?UFæ?Æ
+¡@»ç@>pÿ@l&@í@£È$@º¢°@Ñ}<@èWÈ@ÿ2SA
+oAsµA!àúA-N?A8»
+AD(ËAOA[VAfpAqÝáA}K'A\7AÚAÉ}A!A6ÄA ígAŠ€
+A¬Z­A²QA·ÇôAœ~AÃ5;AÈëÞAÎ¢AÔY%AÚÈAßÆkAå}Aë3²AðêUAö øAüWBBâpBœÂB	B
+teBO·B+	BZBá¬BŒþB
+OB s¡B#NóB&*DB)B+àçB.Œ9B1B4rÜB7N.B:)B=ÑB?à#BB»uBEÆBHrBKMjBN(»BQ
+BSß_BVº°BYB\qTB_EÀ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =t=Ö?Qš?Ä>6@Ô2@=I@k>a@yœ@£TH@º.Õ@Ñ	a@çãí@þŸyA
+ÌA9ÈA!§
+A-SA8ACîÝAO\#AZÉiAf6¯Aq£ôA}9A?@AõãA¬Ac*AÍA ÐqAŠA¬=·A±ô[A·ªýAœa¡AÃDAÈÎçAÎ
+AÔ<.AÙòÑAß©uAå`Aë»AðÍ_AöAü:¥B ø£BÓõB¯GB	B
+eêBA<B
+B÷ßBÓ1B®B
+ÔB e&B#@wB&ÉB(÷B+ÒlB.­ŸB1B4daB7?³B:B<öVB?ÑšBB¬ùBEKBHcBK>îBN@BPõBSÐãBV¬5BYB\bØB_>*Bb|Bdíè¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =LÍ=¹Šä?N	:?ÂnÌ@ì}@<¡@jV¬@â@¢ào@¹ºû@Ð@çp@þJA
+AÿÚA!m A,ÚfA8G«ACŽñAO"6AZ{AeüÁAqjA|×LA"IAØíAAF3Aü×A ³zAŠj
+A¬ ÁA±×dA·AœD«AÂûMAÈ±ñAÎhAÔ7AÙÕÛAß~AåC!AêùÅAð°hAög
+Aü
+¯B ê(BÅzB ÌB	|
+B
+WoB2ÀBBédBÄµB B
+{YB VªB#1üB&
+NB(èB+ÃñB.CB1zB4UæB717B:
+B<çÛB?Ã,BB~BEyÐBHU!BK0sBN
+ÅBPçBSÂhBVºBYy
+B\T]B_/¯Bb
+ BdæRBgÁ€Bj¿  ¿  ¿  ¿  ¿  ¿  =2³=°8?Jjd?À`@Ç@;¹ß@inö@@¢l@¹G@Ð!¬@æü8@ýÖÂA
+X§AÅíA!32A, wA8
+œAC{ANèHAZUAeÂÓAq0A|`ASA»öArA)=AßàA AŠM'A¬ÊA±ºmA·qAœ'ŽAÂÞWAÈûAÎKAÔAAÙžäAßoAå&+AêÜÎAðqAöJAü žB Û­B¶ÿBPB	m¢B
+HôB$EBÿBÚèB¶:BB
+lÝB H/B##B%þÒB(Ú$B+µvB.ÇB1lB4GkB7"ŒB9þB<Ù_B?Ž±BBBEkTBHFŠBK!øBMýIBPØBS³íBV>BYjB\EâB_!3Baü
+Bd××Bg³(BjzBmiÌBp>8¿  ¿  ¿  ¿  <°/=s4?FË?ŸÏø@
+
+@:Ò*@hB@
+-@¡ø¹@žÓE@Ï­Ñ@æ]@ýbèA
+
+¹AÿA ùEA,fA7ÓÐACAAN®[AZ¡AeæApö,A|csAè]A AU£A
+FAÂéA yAŠ00A«æÓA±wA·TAœ
+œAÂÁaAÈxAÎ.§AÓåKAÙíAßRAå	4Aê¿×Aðv{Aö-
+AûãÁB Í2BšBÕB	_'B
+:xBÊBñ
+BÌmB§¿BB
+^bB 9ŽB#B%ðWB(Ë©B+ŠúB.LB1]B48ïB7AB9ïB<ÊäB?Š6BBBE\ÙBH8+BK|BMîÎBPÊ BS¥qBVÃBY\B\7fB_žBaî
+BdÉ[Bg€­BjÿBm[PBp6¢BsôBuæ`¿  ¿  ;òÃë=E
+ê?C,œ?œ @
+5^@9êu@g@ªS@¡ß@ž_k@Ï9÷@æ@üïA	äÍARA ¿WA,,A7ãAC)ANtnAYá³AeNùApŒ?A|)
+AËfA	A8­AïPA¥óA \AŠ9A«ÉÝA±A·7#AŒíÇAÂ€jAÈ[
+AÎ±AÓÈTAÙ~÷Aß5Aäì=Aê¢áAðYAö'AûÆËB Ÿ·BBuZB	P«B
++ýBOBâ BœòBDBtB
+OçB +8B#B%áÜB(œ-B+B.sÑB1O"B4*tB7ÆB9áB<ŒiB?»BBs
+BEN^BH)¯BKBMàSBP»€BSöBVrHBYMB\(ëB_=BaßBdºàBg2BjqBmLÕBp('BsxBuÞÊBxº
+B{    =
+??è?»1"@
+Mš@9¿@f·×@6x@¡@·ë@ÎÆ
+@å š@ü{4A	ªàA%A 
+kA+ò°A7_õABÍ;AN:AY§ÇAe
+ApQA{ïA®oAeA¶AÒYAýA ? A¥öCA«¬æA±cA·-AŒÐÐAÂsAÈ>AÍôºAÓ«]AÙbAß€AäÏGAê
+ëAð<Aõó1Aû©ÔB °;BBfßB	B0B
+
+BøÓBÔ%B¯wBÈBfB
+AlB 
+œB"øB%Ó`B(®²B+B.eUB1@§B4ùB6÷JB9ÒB<­îB??BBdBE?ãBH4BJöBMÑ×BP­)BS{BVcÌBY?
+B\pB^õÁBaÑBd¬eBg¶BjcBm>ZBp«BrôýBuÐOBx« B{òB~bD¿  ?=š\?¹a·@
+eó@8
+@eÐ"@Â@ *@·w¶@ÎRB@å,Î@üZA	pòAÞ8A K~A+žÃA7&	ABNAN AYmÙAdÛApHeA{µ«AyAH
+Aþ¿AµcAlA "©A¥ÙMA«ðA±FA¶ý6AŒ³ÙAÂj}AÈ! AÍ×ÃAÓgAÙE
+AÞû­Aä²QAêhôAðAõÖ;AûÝB ¡ÀB}BXcB	3µB
+BêXBÅªB ûB|MBWB
+2ðB BB"éB%ÄåB( 7B+{B.VÚB12,B4
+}B6èÏB9Ä!B<rB?zÄBBVBE1gBH
+¹BJè
+BMÃ\BP®BSyÿBVUQBY0£B\
+ôB^çFBaÂBdéBgy;BjTBm/ÞBp
+0BræBuÁÓBx%B{xwB~SÈB~}¿  ¿  @	ì@73U@dèl@NÂ@ )N@·Û@ÍÞg@äžó@û~A	7A€JA A+~ÕA6ìABY`AMÆŠAY3ëAd¡0ApvA{{ŒAtA+$AáÈAkAOA ²A¥ŒUA«røA±)A¶à?AŒâAÂM
+AÈ(AÍºÌAÓqoAÙ(AÞÞ¶AäYAêKüAð Aõ¹CAûoæB EBnBIèB	%:B
+ BÛÝB·/BBmÒBI#B
+$uBÿÇB"ÛB%¶jB(ŒB+m
+B.H_B1#°B3ÿB6ÚTB9µ¥B<÷B?lIBBGBE"ìBGþ>BJÙBMŽáBP3BSkBVFÖBY"'B[ýyB^ØËBaŽ
+BdnBgjÀBjFBm!cBoüµBrØBu³XBxªB{iûB~EMB~o¿  ¿  ¿  ¿  @do
+@Úè@µt@¶ @Íj@äE@û£AýAj]A×£A+DèA6²-ABsAM¹AXùþAdgDAoÔA{AÏAWA.AÄÑA{tA2Aè»A¥^A«VA±
+¥A¶ÃHAŒyìAÂ0AÇç2AÍÕAÓTxAÙ
+
+AÞÁ¿AäxbAê/Aïå©AõLAûRðB ÉB`B;mB	ŸB
+òBÍbBš³BB_WB:šB
+úBñKB"ÌB%§ïB(@B+^B.9äB15B3ðB6ËØB9§*B<|B?]ÍBB9BEqBGïÂBJËBMŠfBP·BS]	BV8[BY¬B[îþB^ÊOBa¥¡BdóBg\DBj7BmèBoî9BrÉBu€ÝBx.B{[B~6ÒB~`¿  ¿  ¿  ¿  ¿  ¿  @xÂ@¶
+%@Ìö±@ãÑ=@ú«ÊAÃ*A0pAµA+
+ûA6xAAAåAMRËAXÀAd-WAoA{âA:Añ8A§ÛA^~A!AËÄA¥hA«9
+A°ï®A¶ŠRAŒ\õAÂAÇÊ<AÍßAÓ7AØî%AÞ€ÈAä[lAêAïÈ²AõVAû5ùB vNBQ B,ñB	CB
+ãBŸæB8BuBPÛB,-B
+BâÐB"Ÿ"B%sB(tÅB+PB.+hB1ºB3â
+B6œ]B9¯B<t B?ORBB*€BEõBGáGBJŒBMêBPs<BSNBV)ßBY1B[àB^»ÔBa&BdrwBgMÉBj)BmlBoßŸBr»BuaBxq³B{MB~(VB~R¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @Ìº @ã]c@ú7ïA=AöAcÈA*ÑA6>SAA«AMßAX$AcóiAo`¯AzÍõA
+AÔAAäAAAø+A®ÎA¥eqA«
+A°ÒžA¶[AŒ?þAÁö¢AÇ­EAÍcèAÓAØÑ/AÞÒAä>uAéõAï«ŒAõb_AûB gÓBC$B
+vBùÈB
+ÕB°kBœBgBB`B
+²B
+ùBÔUB"¯§B%øB(fJB+AB.
+íB0ø?B3ÓB6®âB94B<e
+B?@×BB
+(BD÷zBGÒÌBJ®
+BMoBPdÁBS@BVdBXö¶B[ÒB^­YBa«BdcüBg?NBjBlõñBoÑCBr¬BuæBxc8B{>B~ÛB~C¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ùû<AOOAŒA)ÚA* A6eAAq«ALÞðAXL6Ac¹|Ao&ÁAzA §A·JAmîA$AÛ4AØA¥H{Aªÿ
+A°µÁA¶ldAŒ#AÁÙ«AÇNAÍFòAÒýAØŽ8AÞjÜAä!AéØ"AïÅAõEhAúü
+B YXB4ªBûBëLB
+ÆB¡ðB}ABXB3åB6B
+êBÅÚB"¡+B%|}B(WÏB+3 B.rB0éÃB3ÅB6 gB9{žB<W
+B?2\BB
+­BDèÿBGÄPBJ¢BMzôBPVEBS1BV
+éBXè:B[ÃB^ÞBaz/BdUBg0ÓBj
+$BlçvBoÂÇBrBuykBxTŒB{0B~
+`B~5 ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A<A
+ïíA*]3A5ÊxAA7ŸAL¥AXIAcAnìÔAzZAã°ATAP÷AAŸ>AtáA¥+Aªâ(A°ËA¶OnAŒAÁŒŽAÇsXAÍ)ûAÒàAØBAÞMåAäAé»,AïqÏAõ(rAúßB JÜB&.BBÜÒB
+ž#BtBnÆBJB%iB »B
+Ü
+B·^B"°B%nB(ISB+$¥B-ÿ÷B0ÛHB3¶B6ëB9m=B<HB?#àBAÿ2BDÚBGµÕBJ'BMlxBPGÊBS#
+BUþmBXÙ¿B[µB^bBakŽBdGBg"WBiý©BlØûBoŽLBrBujïBxFAB{!B}üäB~&¥¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A*>ÚA5A@ýÑALkAWØ\AcE¡An²çAz .AÆ¹A}]A4 Aê€A¡GAWêA¥AªÅ1A°{ÔA¶2xA»éAÁŸAÇVaAÍ
+AÒÃšAØzKAÞ0îAãçAé5AïTØAõ
+|AúÂB <aB³BóBÎVB
+©šBúB`KB;BîBò@B
+ÍBšãB"5B%_B(:ØB+*B-ñ{B0ÌÍB3šB6pB9^ÂB<:B?eBAð·BDÌBG§ZBJ«BM]ýBP9OBS BUïòBXËDB[ŠB^çBa]9Bd8BgÜBiï.BlÊBo¥ÑBr#Bu\tBx7ÆB{B}îiB~*¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A@ßwAL1(AWnAc
+³AnxùAyæ>A©ÃA`fAAÍ¬AOA:óA€ñAªš9A°^ÝA¶A»Ì#AÁÇAÇ9jAÌð
+AÒŠ°AØ]SAÞ÷AãÊAé=Aï7áAôîAú¥'B -äB	6BäB¿ÚB
+,Bv}BQÏB-!BrBãÃB
+¿BgB"užB%Q
+B(,\B+­B-âÿB0ŸQB3¢B6tôB9PFB<+B?éBAâ:BDœBGÞBJt/BMOBP*ÒBS$BUávBXŒÇB[B^skBaNŒBd*Bg`Bià±BlŒBoUBrrŠBuMøBx)JB{B}ßíB~	®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AWAbÑÆAn?
+Ay¬QAÌACoAúA°¶AgXA
+üA€ÔAªCA°AæAµøA»¯-AÁeÐAÇ
+sAÌÓAÒºAØ@]AÝ÷ Aã­£AédGAïêAôÑAú0B iBú»BÖ
+B±^B
+°BhBCTB
+¥Bù÷BÕIB
+°BëB"g=B%BB(
+àB*ù2B-ÔB0¯ÕB3'B6fyB9AÊB<
+
+B>ønBAÓ¿BD¯BGbBJeŽBMABP
+WBR÷©BUÒúBX®LB[B^dïBa@ABdBföäBiÒ6Bl­BoÙBrd+Bu?}BxÎBzö B}ÑrB}û3¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  An ²AyrdAoÕA&yAÝ
+A¿AJcAA€·šAªnLA°$ïAµÛA»6AÁHÙAÆÿ}AÌ¶ AÒlÃAØ#gAÝÚ
+Aã­AéGPAîýóAôŽAúk:B îBì?BÇB¢ãB
+~4BYB4ØB*Bë|BÆÍB
+¢B}qB"XÂB%4B(eB*ê·B-ÆB0¡ZB3|¬B6WýB93OB<¡B>éòBAÅDBD BG{çBJW9BM2BP
+ÜBRé.BUÄBXÑB[{"B^VtBa1ÆBd
+BfèiBiÃ»Bl
+Boz^BrU°Bu1Bx
+SBzç¥B}ÂöB}ìž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A`©A	AÀ%AvÉA-lAäA€³AªQVA°øAµŸA»u?AÁ+ãAÆâAÌ)AÒOÍAØpAÝœAãs·Aé*ZAîàýAô AúNCB sBÝÅB¹BgB
+o¹BK
+B&\B®BÝ BžRB
+€BnõB"JGB%%B( êB*Ü;B-·B0ßB3n0B6IB9$ÔB< %B>ÛwBA¶ÉBDBGmlBJHŸBM$BOÿaBRÚ²BU¶BXVB[l§B^GùBa#JBcþBfÙîBiµ?BlBokãBrG4Bu"BwýØBzÙ)B}Ž{B}Þ=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A°ùAYÒAuAÇA€}ŒAª4_A¯ëAµ¡ŠA»XHAÁìAÆÅAÌ|3AÒ2ÖA×éyAÝ 
+AãVÀAé
+cAîÄAôzªAú1MAÿçïBÏIBªB
+íB
+a>B<BáBó3BÎB©ÖB
+
+(B`zB";ÌB%
+B'òoB*ÍÁB-©B0cB3_µB6;B9XB;ñªB>ÌüBAšMBDBG^ñBJ:BBMBOðæBRÌ7BU§BXÚB[^,B^9~BaÏBcð!BfËrBiŠÄBlBo]gBr8¹Bu
+Bwï\BzÊ®B}Š B}ÏÁ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AIAª"A€`ÅAªiA¯Î
+Aµ¯A»;SAÀñöAÆšAÌ_<AÒßA×ÌAÝ&Aã9ÉAèðmAî§Aô]³AúWAÿÊøBÀÎB BwqB
+RÃB.B	fBä·BÀ	B[B
+v¬BQþB"-PB%¢B'ãôB*¿EB-B0uéB3Q:B6,B9ÝB;ã/B>ŸBAÒBDu$BGPuBJ+ÇBMBOâjBRœŒBUBXt_B[O±B^+BaTBcáŠBfŒ÷BiIBlsBoNìBr*>BuBwàáBzŒ3B}B}ÁF¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A€QA©úrA¯±Aµg¹A»
+\AÀÔÿAÆ£AÌBFAÑøèA×¯AÝf/Aã
+ÓAèÓvAîAô@œAù÷`Aÿ®B²SB€BhöB
+DHBBúëBÖ=B±BßB
+h1BCB"
+ÔB$ú&B'ÕxB*°ÊB-
+B0gmB3B¿B6
+B8ùbB;Ô³B>°BAWBDfšBGAúBJ
+LBLøBOÓïBR¯ABUBXeäB[A6B^
+B`÷ÙBcÓ*Bf®|BiÎBleBo@qBrÂBt÷BwÒfBz­·B}	B}²Ë¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A¯¡éAµJÂA»eAÀž	AÆn¬AÌ%OAÑÛóA×AÝI8AâÿÜAè¶Aîm#Aô#ÆAùÚiAÿ
+B£×B)BZ{B
+5ÌB
+BìpBÇÁB£B~eB
+Y¶B5B"YB$ë«B'ÆüB*¢NB-} B0XòB34DB6B8êçB;Æ9B>¡BA|ÛBDX-BG3BJÐBLê"BOÅtBR ÅBU|BXWiB[2ºB^
+B`é^BcÄ¯Bf Bi{RBlV€Bo1öBr
+GBtèBwÃêBz<B}zB}€O¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aºò9AÀAÆQµAÌYAÑŸüA×uAÝ,CAââæAèAîP,AôÏAùœsAÿtB\Bp®BKÿB
+'QB£BÝôB¹FBBoéB
+K;B&B"ÞB$Ý/B'žB*ÓB-o$B0JvB3%ÈB6B8ÜlB;·œB>BAnaBDI²BG%BJ UBLÛ§BO¶øBRJBUmBXHíB[$?B]ÿB`ÚâBc¶4BfBil×BlH)Bo#zBqþÌBtÚ
+BwµoBzÁB}lB}Ô¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÆBAËëbAÑ¢A×X©AÝLAâÅïAè|Aî36AóéØAù |AÿWBáBb3B=B
+ÖB
+ô'BÏyBªËB
+BanB
+<ÀBB!ócB$ÎµB'ªB*
+WB-`©B0;ûB3LB5òB8ÍðB;©BB>BA_åBD;7BGBIñÚBLÍ+BOš}BRÏBU_ BX:rB[ÄB]ñB`ÌgBc§¹Bf
+Bi^\Bl9®BoÿBqðQBtË¢BwŠôBzFB}]B}Y¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÑÙA×;²AÜòUAâšùAè_Aî?AóÌãAùAÿ:'BxfBS·B/	B
+
+[B
+å¬BÀþBOBw¡BRóB
+.DB	B!äèB$À9B'B*vÝB-R.B0-B3ÑB5ä#B8¿tB;ÆB>vBAQjBD,ŒBG
+BIã_BLŸ±BOBRuSBUP¥BX+÷B[HB]âB`œìBc=BftBiOáBl+2BoBqáÖBtœ'BwyBzsÊB}O
+B}xÞ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÜã)AâAèB¥AíùIAó¯ìAùfAÿ
+1BiêBE<B B
+ûßB
+×1B²BÔBi&BDwB
+ÉB
+ûB!ÖlB$±ŸB'B*haB-C³B0B2úVB5Õ§B8°ùB;KB>gBABîBD
+@BFùBIÔäBL°5BOBRfÙBUB*BX
+{BZøÍB]ÔB`¯pBcÂBffBiAeBl
+·Bnø	BqÓZBt®¬BwþBzeOB}@¡B}jb¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aè3yAíÜRAóõAùIAÿ ;B[oB6ÁBB
+ídB
+È¶B€BYBZ«B5üB
+NB
+ìB!ÇñB$£CB'~B*YæB-58B0B2ëÛB5Ç-B8¢~B;}ÏB>Y!BA4sBDÄBFëBIÆhBL¡ºBO}
+BRX]BU3¯BXBZêRB]Å£B` õBc|GBfWBi2êBl<BnéBqÄßBt 1Bw{BzVÔB}2&B}[ç¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AóÉAù,¢AþãDBLôB(EBB
+ÞéB
+º:BBpÞBL/B'B
+ÓB
+Þ$B!¹vB$ÇB'pB*KkB-&ŒB0B2Ý`B5ž±B8B;oUB>JŠBA%÷BDIBFÜBI·ìBL>BOnBRIâBU%4BX 
+BZÛ×B]·)B`zBcmËBfI
+Bi$oBkÿÀBnÛBq¶dBtµBwmBzHYB}#ªB}Ml¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AþÔB>xBÊBõ
+B
+ÐmB
+«¿BBbbB=ŽBBôWB
+Ï©B!ªûB$LB'aB*<ïB-AB/óB2ÎäB5ª6B8
+B;`ÙB><+BA}BCòÎBFÎBI©qBLÃBO`BR;fBUžBWò
+BZÍ\B]š­B`ÿBc_QBf:¢BióBkñEBnÌBq§èBt:Bw^Bz9ÝB}/B}>ñ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B4Bæ B
+ÁòB
+DBxBSçB/9B
+BåÜB
+Á.B!B$wÑB'S#B*.tB-	ÆB/åB2ÀiB5»B8w
+B;R^B>-°BA	BCäSBF¿¥BIöBLvGBOQBR,ëBU<BWãBZŸàB]2B`uBcPÕBf,'BiyBkâÊBnŸBqmBtt¿BwPBz+bB}ŽB}0u¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+º\B
+ÈBjBElB œBüB×aB
+²²B!B$iVB'D§B*ùB,ûKB/ÖB2±îB5?B8hB;CãB>4B@úBCÕØBF±)BI{BLgÍBOC
+BR
+oBTùÁBWÕBZ°dB]¶B`gBcBZBf
+¬BhøýBkÔOBn¯¡BqòBtfCBwABz
+çB|ø8B}!ú¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BbB6ïBABíBÈäB
+€6B!B$ZÙB'6+B*}B,ìÎB/È B2£rB5~ÃB8ZB;5fB>žB@ì
+BCÇ[BF¢­BI}ÿBLYPBO4¢BRôBTëEBWÆBZ¡èB]}:B`XBc3ÝBf/BhêBkÅÓBn¡$Bq|vBtWÈBw3BzjB|éŒB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+«BßBºiB
+»B!q
+B$L^B''°B*B,ÞSB/¹¥B2öB5pHB8KB;&ëB>=B@ÝBCžàBF2BIoBLJÕBO&'BRxBTÜÊBWž
+BZmB]nŸB`JBc%bBf ³BhÜBk·WBn©BqmûBtILBw$ByÿðB|ÛAB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B²ÓB
+?B!bB$=ãB'4B)ôB,ÏØB/«)B2{B5aÍB8=
+B;pB=óÂB@ÏBCªeBF
+¶BIaBL<ZBO«BQòýBTÎOBW© BZòB]`DB`;BcæBeò8BhÍBkšÛBn-Bq_Bt:ÑBw#ByñtB|ÌÆB|ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B!ZûB$/gB'
+¹B)æ
+B,Á\B/®B2x B5SQB8.£B;	õB=åFB@ÀBCêBFw;BIRBL-ÞBO	0BQäBT¿ÓBW%BZvwB]QÈB`-BclBeãœBh¿Bk`Bnu²BqQBt,UBw§ByâùB|ŸKB|è
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B'#B)×B,²áB/3B2iB5DÖB8 (B:ûyB=ÖËB@²
+BCnBFhÀBIDBLcBNúµBQÖBT±XBWªBZgûB]CMB`
+BbùðBeÕBBh°BkåBng6BqBBt
+ÚBvù+ByÔ}B|¯ÏB|Ù¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B,«JB/¶B2[B56ZB8«B:ìýB=ÈOB@£ BC~òBFZDBI5BLçBNì9BQÇBT¢ÜBW~-BZYB]4ÑB`"BbëtBeÆÆBh¢Bk}iBnX»Bq4
+Bt]Bvê¯ByÆB|¡RB|Ë¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B2SrB5'ÞB80B:ÞB=¹ÓB@%BCpwBFKÈBI'BLlBNÝœBQ¹BTaBWo²BZKB]&UB`§BbÜùBežJBhBknîBnJ?Bq%Bt ãBvÜ4By·
+B|×B|Œ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B7ûB:ÐB=«XB@ªBCaûBF=MBIBKóðBNÏBBQªBT
+åBWa7BZ<B]ÚB_ó,BbÎ}Be©ÏBh
+!Bk`rBn;ÄBqBsògBvÍ¹By©
+B|\B|®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B=£ÂB@x.BCSBF.ÒBI
+#BKåuBNÀÇBQBTwjBWRŒBZ.
+B]	_B_ä±BbÀBeTBhv¥BkQ÷Bn-IBqBsãìBv¿>ByB|uáB|¥¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BCKéBF UBHû§BKÖùBN²JBQBThîBWD?BZB\úãB_Ö4Bb±BeØBhh)BkC{Bn
+ÌBpú
+BsÕpBv°ÁByB|geB|)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BHôBKÈ}BN£ÏBQ!BTZrBW5ÄBZB\ìgB_Ç¹Bb£
+Be~\BhY®Bk5 BnQBpë£BsÆôBv¢FBy}B|XéB|®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BN9BQp¥BTK÷BW'IBZB\ÝìB_¹>BbBeoáBhK3Bk&BnÖBpÝ(BsžyBvËByo
+B|JnB|t3¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BTDaBWÍBYôB\ÏqB_ªÂBbBeafBh<·Bk	Bmó[BpÎ¬Bs©þBv
+PBy`¡B|;óB|e·¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BYìB\ÀôB_FBbwBeRéBh.;Bk	BmäÞBpÀ0BsBvvÓByR%B|-wB|W<¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B_°Bbi
+BeDnBhÀBjûBmÖcBp±µBsBvhXByCªB|
+ûB|HÁ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Be<ØBhDBjìBmÇèBp£9Bs~BvYÝBy5.B|B|:F¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bjå Bm¹lBpŸBspBvKaBy&³B|B|+Ê¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bp'BsaBv<åBy7B{óB|
+O¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bv5OBy	»B{å
+B|Ô¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B{ÝwB| X¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 <#×
+<£×
+<õÂ=#×
+=LÌÍ=uÂ=\)=£×
+=žQì=ÌÌÍ=áG®=õÂ>
+ž>\)>>#×
+>.{>8Qì>B\>LÌÍ>W
+=>aG®>k
+>uÂ>  >
+
+ž>=q>\)>zá>>žR>£×
+>šõÃ>®{>³33>žQì>œp€>Â\>Ç®>ÌÌÍ>Ñë
+>×
+=>Ü(ö>áG®>æff>ë
+>ð£×>õÂ>úáH?   ?\?
+ž?®?
+=q?
+ÌÍ?\)?ë
+?zá?
+=??
+(ö?
+žR?!G®?  ?G®?\?×
+?
+
+ž?ff?®?õÃ?=q?
+?ÌÍ?{?\)?£×?ë
+?33?zá?Â?
+>?Qì??áH?(ö?p€?žR?   ?¡G®?¢\?£×
+?¥
+ž?Šff?§®?šõÂ?ª=q?«
+?¬ÌÍ?®{?¯\)?°£×?±ë
+?³33?Žzá?µÂ?·
+>?žQì?¹?ºáH?Œ(ö?œp€?ŸžR?À  ?ÁG®?Â\?Ã×
+?Å
+ž?Æff?Ç®?ÈõÂ?Ê=p?Ë
+
+?ÌÌÍ?Î{?Ï\)?Ð£×@   @ £×@G®@ë
+@\@33@×
+@zá@
+ž@Â@ff@
+=@®@Qì@õÃ@	@
+=q@
+áH@
+
+@
+(ö@
+ÌÍ@
+p€@{@žR@\)@  @£×@G®@ë
+@\@33@×
+@zá@
+ž@Â@ff@
+>@®@Qì@õÂ@@=q@áH@
+@
+(ö@
+ÌÍ@
+p€@
+{@
+žR@\)@   @ £×@!G®@!ë
+@"\@#33@#×
+@$zá@%
+ž@%Â@&ff@'
+>@'®@(Qì@@  @@£×@AG®@Aë
+@B\@C33@C×
+@Dzá@E
+ž@EÂ@Fff@G
+=@G®@HQì@HõÃ@I@J=q@JáH@K
+@L(ö@LÌÍ@Mp€@N{@NžR@O\)@P  @P£×@QG®@Që
+@R\@S33@S×
+@Tzá@U
+ž@UÂ@Vff@W
+>@W®@XQì@XõÂ@Y@Z=q@ZáH@[
+@\(ö@\ÌÍ@]p€@^{@^žR@_\)@`  @`£×@aG®@aë
+@b\@c33@c×
+@dzá@e
+ž@eÂ@fff@g
+>@g®@hQì@  @Qì@£×@õÃ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@zá@ÌÍ@
+
+ž@
+p€@
+Â@{@ff@žR@
+=@\)@®@  @Qì@£×@õÃ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@zá@ÌÍ@
+ž@p€@Â@{@ff@žR@
+=@\)@®@  @Qì@£×@õÂ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@   @ Qì@ £×@ õÃ@¡G®@¡@¡ë
+@¢=q@¢\@¢áH@£33@£
+@£×
+@€(ö@€zá@€ÌÍ@¥
+ž@¥p€@¥Â@Š{@Šff@ŠžR@§
+=@§\)@§®@š  @šQì@š£×@šõÃ@©G®@©@©ë
+@ª=q@ª\@ªáH@«33@«
+@«×
+@¬(ö@¬zá@¬ÌÍ@­
+ž@­p€@­Â@®{@®ff@®žR@¯
+=@¯\)@¯®@°  @°Qì@°£×@°õÂ@±G®@±@±ë
+@²=q@²\@²áH@³33@³
+@³×
+@Ž(ö@À  @ÀQì@À£×@ÀõÃ@ÁG®@Á@Áë
+@Â=q@Â\@ÂáH@Ã33@Ã
+@Ã×
+@Ä(ö@Äzá@ÄÌÍ@Å
+ž@Åp€@ÅÂ@Æ{@Æff@ÆžR@Ç
+=@Ç\)@Ç®@È  @ÈQì@È£×@ÈõÃ@ÉG®@É@Éë
+@Ê=q@Ê\@ÊáH@Ë33@Ë
+@Ë×
+@Ì(ö@Ìzá@ÌÌÍ@Í
+ž@Íp€@ÍÂ@Î{@Îff@ÎžR@Ï
+=@Ï\)@Ï®@Ð  @ÐQì@Ð£×@ÐõÂ@ÑG®@Ñ@Ñë
+@Ò=q@Ò\@ÒáH@Ó33@Ó
+@Ó×
+@Ô(ö@à  @àQì@à£×@àõÃ@áG®@á@áë
+@â=q@â\@âáH@ã33@ã
+@ã×
+@ä(ö@äzá@äÌÍ@å
+ž@åp€@åÂ@æ{@æff@æžR@ç
+=@ç\)@ç®@è  @èQì@è£×@èõÃ@éG®@é@éë
+@ê=q@ê\@êáH@ë33@ë
+@ë×
+@ì(ö@ìzá@ìÌÍ@í
+ž@íp€@íÂ@î{@îff@îžR@ï
+=@ï\)@ï®@ð  @ðQì@ð£×@ðõÂ@ñG®@ñ@ñë
+@ò=q@ò\@òáH@ó33@ó
+@ó×
+@ô(öA   A (öA QìA záA £×A ÌÍA õÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA	
+žA	G®A	p€A	A	ÂA	ë
+A
+{A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A   A (öA QìA záA £×A ÌÍA õÃA!
+žA!G®A!p€A!A!ÂA!ë
+A"{A"=qA"ffA"\A"žRA"áHA#
+=A#33A#\)A#
+A#®A#×
+A$  A$(öA$QìA$záA$£×A$ÌÍA$õÃA%
+žA%G®A%p€A%A%ÂA%ë
+A&{A&=qA&ffA&\A&žRA&áHA'
+=A'33A'\)A'
+A'®A'×
+A(  A((öA(QìA(záA(£×A(ÌÍA(õÃA)
+žA)G®A)p€A)A)ÂA)ë
+A*{A0  A0(öA0QìA0záA0£×A0ÌÍA0õÃA1
+žA1G®A1p€A1A1ÂA1ë
+A2{A2=qA2ffA2\A2žRA2áHA3
+=A333A3\)A3
+A3®A3×
+A4  A4(öA4QìA4záA4£×A4ÌÍA4õÃA5
+žA5G®A5p€A5A5ÂA5ë
+A6{A6=qA6ffA6\A6žRA6áHA7
+=A733A7\)A7
+A7®A7×
+A8  A8(öA8QìA8záA8£×A8ÌÍA8õÃA9
+žA9G®A9p€A9A9ÂA9ë
+A:{A@  A@(öA@QìA@záA@£×A@ÌÍA@õÃAA
+žAAG®AAp€AAAAÂAAë
+AB{AB=qABffAB\ABžRABáHAC
+=AC33AC\)AC
+AC®AC×
+AD  AD(öADQìADzáAD£×ADÌÍADõÃAE
+žAEG®AEp€AEAEÂAEë
+AF{AF=qAFffAF\AFžRAFáHAG
+=AG33AG\)AG
+AG®AG×
+AH  AH(öAHQìAHzáAH£×AHÌÍAHõÃAI
+žAIG®AIp€AIAIÂAIë
+AJ{AP  AP(öAPQìAPzáAP£×APÌÍAPõÃAQ
+žAQG®AQp€AQAQÂAQë
+AR{AR=qARffAR\ARžRARáHAS
+=AS33AS\)AS
+AS®AS×
+AT  AT(öATQìATzáAT£×ATÌÍATõÃAU
+žAUG®AUp€AUAUÂAUë
+AV{AV=qAVffAV\AVžRAVáHAW
+=AW33AW\)AW
+AW®AW×
+AX  AX(öAXQìAXzáAX£×AXÌÍAXõÃAY
+žAYG®AYp€AYAYÂAYë
+AZ{A`  A`(öA`QìA`záA`£×A`ÌÍA`õÃAa
+žAaG®Aap€AaAaÂAaë
+Ab{Ab=qAbffAb\AbžRAbáHAc
+=Ac33Ac\)Ac
+Ac®Ac×
+Ad  Ad(öAdQìAdzáAd£×AdÌÍAdõÃAe
+žAeG®Aep€AeAeÂAeë
+Af{Af=qAfffAf\AfžRAfáHAg
+=Ag33Ag\)Ag
+Ag®Ag×
+Ah  Ah(öAhQìAhzáAh£×AhÌÍAhõÃAi
+žAiG®Aip€AiAiÂAië
+Aj{Ap  Ap(öApQìApzáAp£×ApÌÍApõÃAq
+žAqG®Aqp€AqAqÂAqë
+Ar{Ar=qArffAr\AržRAráHAs
+=As33As\)As
+As®As×
+At  At(öAtQìAtzáAt£×AtÌÍAtõÃAu
+žAuG®Aup€AuAuÂAuë
+Av{Av=qAvffAv\AvžRAváHAw
+=Aw33Aw\)Aw
+Aw®Aw×
+Ax  Ax(öAxQìAxzáAx£×AxÌÍAxõÃAy
+žAyG®Ayp€AyAyÂAyë
+Az{A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A   A {A (öA =qA QìA ffA záA \A £×A žRA ÌÍA áHA õÃA¡
+=A¡
+žA¡33A¡G®A¡\)A¡p€A¡
+A¡A¡®A¡ÂA¡×
+A¡ë
+A¢  A¢{A¢(öA¢=qA¢QìA¢ffA¢záA¢\A¢£×A¢žRA¢ÌÍA¢áHA¢õÃA£
+=A£
+žA£33A£G®A£\)A£p€A£
+A£A£®A£ÂA£×
+A£ë
+A€  A€{A€(öA€=qA€QìA€ffA€záA€\A€£×A€žRA€ÌÍA€áHA€õÃA¥
+=Aš  Aš{Aš(öAš=qAšQìAšffAšzáAš\Aš£×AšžRAšÌÍAšáHAšõÃA©
+=A©
+žA©33A©G®A©\)A©p€A©
+A©A©®A©ÂA©×
+A©ë
+Aª  Aª{Aª(öAª=qAªQìAªffAªzáAª\Aª£×AªžRAªÌÍAªáHAªõÃA«
+=A«
+žA«33A«G®A«\)A«p€A«
+A«A«®A«ÂA«×
+A«ë
+A¬  A¬{A¬(öA¬=qA¬QìA¬ffA¬záA¬\A¬£×A¬žRA¬ÌÍA¬áHA¬õÃA­
+=A°  A°{A°(öA°=qA°QìA°ffA°záA°\A°£×A°žRA°ÌÍA°áHA°õÃA±
+=A±
+žA±33A±G®A±\)A±p€A±
+A±A±®A±ÂA±×
+A±ë
+A²  A²{A²(öA²=qA²QìA²ffA²záA²\A²£×A²žRA²ÌÍA²áHA²õÃA³
+=A³
+žA³33A³G®A³\)A³p€A³
+A³A³®A³ÂA³×
+A³ë
+AŽ  AŽ{AŽ(öAŽ=qAŽQìAŽffAŽzáAŽ\AŽ£×AŽžRAŽÌÍAŽáHAŽõÃAµ
+=Až  Až{Až(öAž=qAžQìAžffAžzáAž\Až£×AžžRAžÌÍAžáHAžõÃA¹
+=A¹
+žA¹33A¹G®A¹\)A¹p€A¹
+A¹A¹®A¹ÂA¹×
+A¹ë
+Aº  Aº{Aº(öAº=qAºQìAºffAºzáAº\Aº£×AºžRAºÌÍAºáHAºõÃA»
+=A»
+žA»33A»G®A»\)A»p€A»
+A»A»®A»ÂA»×
+A»ë
+AŒ  AŒ{AŒ(öAŒ=qAŒQìAŒffAŒzáAŒ\AŒ£×AŒžRAŒÌÍAŒáHAŒõÃAœ
+=AÀ  AÀ{AÀ(öAÀ=qAÀQìAÀffAÀzáAÀ\AÀ£×AÀžRAÀÌÍAÀáHAÀõÃAÁ
+=AÁ
+žAÁ33AÁG®AÁ\)AÁp€AÁ
+AÁAÁ®AÁÂAÁ×
+AÁë
+AÂ  AÂ{AÂ(öAÂ=qAÂQìAÂffAÂzáAÂ\AÂ£×AÂžRAÂÌÍAÂáHAÂõÃAÃ
+=AÃ
+žAÃ33AÃG®AÃ\)AÃp€AÃ
+AÃAÃ®AÃÂAÃ×
+AÃë
+AÄ  AÄ{AÄ(öAÄ=qAÄQìAÄffAÄzáAÄ\AÄ£×AÄžRAÄÌÍAÄáHAÄõÃAÅ
+=AÈ  AÈ{AÈ(öAÈ=qAÈQìAÈffAÈzáAÈ\AÈ£×AÈžRAÈÌÍAÈáHAÈõÃAÉ
+=AÉ
+žAÉ33AÉG®AÉ\)AÉp€AÉ
+AÉAÉ®AÉÂAÉ×
+AÉë
+AÊ  AÊ{AÊ(öAÊ=qAÊQìAÊffAÊzáAÊ\AÊ£×AÊžRAÊÌÍAÊáHAÊõÃAË
+=AË
+žAË33AËG®AË\)AËp€AË
+AËAË®AËÂAË×
+AËë
+AÌ  AÌ{AÌ(öAÌ=qAÌQìAÌffAÌzáAÌ\AÌ£×AÌžRAÌÌÍAÌáHAÌõÃAÍ
+=AÐ  AÐ{AÐ(öAÐ=qAÐQìAÐffAÐzáAÐ\AÐ£×AÐžRAÐÌÍAÐáHAÐõÃAÑ
+=AÑ
+žAÑ33AÑG®AÑ\)AÑp€AÑ
+AÑAÑ®AÑÂAÑ×
+AÑë
+AÒ  AÒ{AÒ(öAÒ=qAÒQìAÒffAÒzáAÒ\AÒ£×AÒžRAÒÌÍAÒáHAÒõÃAÓ
+=AÓ
+žAÓ33AÓG®AÓ\)AÓp€AÓ
+AÓAÓ®AÓÂAÓ×
+AÓë
+AÔ  AÔ{AÔ(öAÔ=qAÔQìAÔffAÔzáAÔ\AÔ£×AÔžRAÔÌÍAÔáHAÔõÃAÕ
+=AØ  AØ{AØ(öAØ=qAØQìAØffAØzáAØ\AØ£×AØžRAØÌÍAØáHAØõÃAÙ
+=AÙ
+žAÙ33AÙG®AÙ\)AÙp€AÙ
+AÙAÙ®AÙÂAÙ×
+AÙë
+AÚ  AÚ{AÚ(öAÚ=qAÚQìAÚffAÚzáAÚ\AÚ£×AÚžRAÚÌÍAÚáHAÚõÃAÛ
+=AÛ
+žAÛ33AÛG®AÛ\)AÛp€AÛ
+AÛAÛ®AÛÂAÛ×
+AÛë
+AÜ  AÜ{AÜ(öAÜ=qAÜQìAÜffAÜzáAÜ\AÜ£×AÜžRAÜÌÍAÜáHAÜõÃAÝ
+=Aà  Aà{Aà(öAà=qAàQìAàffAàzáAà\Aà£×AàžRAàÌÍAàáHAàõÃAá
+=Aá
+žAá33AáG®Aá\)Aáp€Aá
+AáAá®AáÂAá×
+Aáë
+Aâ  Aâ{Aâ(öAâ=qAâQìAâffAâzáAâ\Aâ£×AâžRAâÌÍAâáHAâõÃAã
+=Aã
+žAã33AãG®Aã\)Aãp€Aã
+AãAã®AãÂAã×
+Aãë
+Aä  Aä{Aä(öAä=qAäQìAäffAäzáAä\Aä£×AäžRAäÌÍAäáHAäõÃAå
+=Aè  Aè{Aè(öAè=qAèQìAèffAèzáAè\Aè£×AèžRAèÌÍAèáHAèõÃAé
+=Aé
+žAé33AéG®Aé\)Aép€Aé
+AéAé®AéÂAé×
+Aéë
+Aê  Aê{Aê(öAê=qAêQìAêffAêzáAê\Aê£×AêžRAêÌÍAêáHAêõÃAë
+=Aë
+žAë33AëG®Aë\)Aëp€Aë
+AëAë®AëÂAë×
+Aëë
+Aì  Aì{Aì(öAì=qAìQìAìffAìzáAì\Aì£×AìžRAìÌÍAìáHAìõÃAí
+=Að  Að{Að(öAð=qAðQìAðffAðzáAð\Að£×AðžRAðÌÍAðáHAðõÃAñ
+=Añ
+žAñ33AñG®Añ\)Añp€Añ
+AñAñ®AñÂAñ×
+Añë
+Aò  Aò{Aò(öAò=qAòQìAòffAòzáAò\Aò£×AòžRAòÌÍAòáHAòõÃAó
+=Aó
+žAó33AóG®Aó\)Aóp€Aó
+AóAó®AóÂAó×
+Aóë
+Aô  Aô{Aô(öAô=qAôQìAôffAôzáAô\Aô£×AôžRAôÌÍAôáHAôõÃAõ
+=Aø  Aø{Aø(öAø=qAøQìAøffAøzáAø\Aø£×AøžRAøÌÍAøáHAøõÃAù
+=Aù
+žAù33AùG®Aù\)Aùp€Aù
+AùAù®AùÂAù×
+Aùë
+Aú  Aú{Aú(öAú=qAúQìAúffAúzáAú\Aú£×AúžRAúÌÍAúáHAúõÃAû
+=Aû
+žAû33AûG®Aû\)Aûp€Aû
+AûAû®AûÂAû×
+Aûë
+Aü  Aü{Aü(öAü=qAüQìAüffAüzáAü\Aü£×AüžRAüÌÍAüáHAüõÃAý
+=B   B 
+=B {B 
+žB (öB 33B =qB G®B QìB \)B ffB p€B záB 
+B \B B £×B ®B žRB ÂB ÌÍB ×
+B áHB ë
+B õÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB	  B	
+=B	{B	
+žB	(öB	33B	=qB	G®B	QìB	\)B	ffB	p€B	záB	
+B	\B	B	£×B	®B	žRB	ÂB	ÌÍB	×
+B	áHB	ë
+B	õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B   B 
+=B {B 
+žB (öB 33B =qB G®B QìB \)B ffB p€B záB 
+B \B B £×B ®B žRB ÂB ÌÍB ×
+B áHB ë
+B õÃB!  B!
+=B!{B!
+žB!(öB!33B!=qB!G®B!QìB!\)B!ffB!p€B!záB!
+B!\B!B!£×B!®B!žRB!ÂB!ÌÍB!×
+B!áHB!ë
+B!õÃB"  B"
+=B"{B"
+žB"(öB"33B"=qB"G®B"QìB"\)B"ffB"p€B"záB"
+B$  B$
+=B${B$
+žB$(öB$33B$=qB$G®B$QìB$\)B$ffB$p€B$záB$
+B$\B$B$£×B$®B$žRB$ÂB$ÌÍB$×
+B$áHB$ë
+B$õÃB%  B%
+=B%{B%
+žB%(öB%33B%=qB%G®B%QìB%\)B%ffB%p€B%záB%
+B%\B%B%£×B%®B%žRB%ÂB%ÌÍB%×
+B%áHB%ë
+B%õÃB&  B&
+=B&{B&
+žB&(öB&33B&=qB&G®B&QìB&\)B&ffB&p€B&záB&
+B(  B(
+=B({B(
+žB((öB(33B(=qB(G®B(QìB(\)B(ffB(p€B(záB(
+B(\B(B(£×B(®B(žRB(ÂB(ÌÍB(×
+B(áHB(ë
+B(õÃB)  B)
+=B){B)
+žB)(öB)33B)=qB)G®B)QìB)\)B)ffB)p€B)záB)
+B)\B)B)£×B)®B)žRB)ÂB)ÌÍB)×
+B)áHB)ë
+B)õÃB*  B*
+=B*{B*
+žB*(öB*33B*=qB*G®B*QìB*\)B*ffB*p€B*záB*
+B,  B,
+=B,{B,
+žB,(öB,33B,=qB,G®B,QìB,\)B,ffB,p€B,záB,
+B,\B,B,£×B,®B,žRB,ÂB,ÌÍB,×
+B,áHB,ë
+B,õÃB-  B-
+=B-{B-
+žB-(öB-33B-=qB-G®B-QìB-\)B-ffB-p€B-záB-
+B-\B-B-£×B-®B-žRB-ÂB-ÌÍB-×
+B-áHB-ë
+B-õÃB.  B.
+=B.{B.
+žB.(öB.33B.=qB.G®B.QìB.\)B.ffB.p€B.záB.
+B0  B0
+=B0{B0
+žB0(öB033B0=qB0G®B0QìB0\)B0ffB0p€B0záB0
+B0\B0B0£×B0®B0žRB0ÂB0ÌÍB0×
+B0áHB0ë
+B0õÃB1  B1
+=B1{B1
+žB1(öB133B1=qB1G®B1QìB1\)B1ffB1p€B1záB1
+B1\B1B1£×B1®B1žRB1ÂB1ÌÍB1×
+B1áHB1ë
+B1õÃB2  B2
+=B2{B2
+žB2(öB233B2=qB2G®B2QìB2\)B2ffB2p€B2záB2
+B4  B4
+=B4{B4
+žB4(öB433B4=qB4G®B4QìB4\)B4ffB4p€B4záB4
+B4\B4B4£×B4®B4žRB4ÂB4ÌÍB4×
+B4áHB4ë
+B4õÃB5  B5
+=B5{B5
+žB5(öB533B5=qB5G®B5QìB5\)B5ffB5p€B5záB5
+B5\B5B5£×B5®B5žRB5ÂB5ÌÍB5×
+B5áHB5ë
+B5õÃB6  B6
+=B6{B6
+žB6(öB633B6=qB6G®B6QìB6\)B6ffB6p€B6záB6
+B8  B8
+=B8{B8
+žB8(öB833B8=qB8G®B8QìB8\)B8ffB8p€B8záB8
+B8\B8B8£×B8®B8žRB8ÂB8ÌÍB8×
+B8áHB8ë
+B8õÃB9  B9
+=B9{B9
+žB9(öB933B9=qB9G®B9QìB9\)B9ffB9p€B9záB9
+B9\B9B9£×B9®B9žRB9ÂB9ÌÍB9×
+B9áHB9ë
+B9õÃB:  B:
+=B:{B:
+žB:(öB:33B:=qB:G®B:QìB:\)B:ffB:p€B:záB:
+B<  B<
+=B<{B<
+žB<(öB<33B<=qB<G®B<QìB<\)B<ffB<p€B<záB<
+B<\B<B<£×B<®B<žRB<ÂB<ÌÍB<×
+B<áHB<ë
+B<õÃB=  B=
+=B={B=
+žB=(öB=33B==qB=G®B=QìB=\)B=ffB=p€B=záB=
+B=\B=B=£×B=®B=žRB=ÂB=ÌÍB=×
+B=áHB=ë
+B=õÃB>  B>
+=B>{B>
+žB>(öB>33B>=qB>G®B>QìB>\)B>ffB>p€B>záB>
+B@  B@
+=B@{B@
+žB@(öB@33B@=qB@G®B@QìB@\)B@ffB@p€B@záB@
+B@\B@B@£×B@®B@žRB@ÂB@ÌÍB@×
+B@áHB@ë
+B@õÃBA  BA
+=BA{BA
+žBA(öBA33BA=qBAG®BAQìBA\)BAffBAp€BAzáBA
+BA\BABA£×BA®BAžRBAÂBAÌÍBA×
+BAáHBAë
+BAõÃBB  BB
+=BB{BB
+žBB(öBB33BB=qBBG®BBQìBB\)BBffBBp€BBzáBB
+BD  BD
+=BD{BD
+žBD(öBD33BD=qBDG®BDQìBD\)BDffBDp€BDzáBD
+BD\BDBD£×BD®BDžRBDÂBDÌÍBD×
+BDáHBDë
+BDõÃBE  BE
+=BE{BE
+žBE(öBE33BE=qBEG®BEQìBE\)BEffBEp€BEzáBE
+BE\BEBE£×BE®BEžRBEÂBEÌÍBE×
+BEáHBEë
+BEõÃBF  BF
+=BF{BF
+žBF(öBF33BF=qBFG®BFQìBF\)BFffBFp€BFzáBF
+BH  BH
+=BH{BH
+žBH(öBH33BH=qBHG®BHQìBH\)BHffBHp€BHzáBH
+BH\BHBH£×BH®BHžRBHÂBHÌÍBH×
+BHáHBHë
+BHõÃBI  BI
+=BI{BI
+žBI(öBI33BI=qBIG®BIQìBI\)BIffBIp€BIzáBI
+BI\BIBI£×BI®BIžRBIÂBIÌÍBI×
+BIáHBIë
+BIõÃBJ  BJ
+=BJ{BJ
+žBJ(öBJ33BJ=qBJG®BJQìBJ\)BJffBJp€BJzáBJ
+BL  BL
+=BL{BL
+žBL(öBL33BL=qBLG®BLQìBL\)BLffBLp€BLzáBL
+BL\BLBL£×BL®BLžRBLÂBLÌÍBL×
+BLáHBLë
+BLõÃBM  BM
+=BM{BM
+žBM(öBM33BM=qBMG®BMQìBM\)BMffBMp€BMzáBM
+BM\BMBM£×BM®BMžRBMÂBMÌÍBM×
+BMáHBMë
+BMõÃBN  BN
+=BN{BN
+žBN(öBN33BN=qBNG®BNQìBN\)BNffBNp€BNzáBN
+BP  BP
+=BP{BP
+žBP(öBP33BP=qBPG®BPQìBP\)BPffBPp€BPzáBP
+BP\BPBP£×BP®BPžRBPÂBPÌÍBP×
+BPáHBPë
+BPõÃBQ  BQ
+=BQ{BQ
+žBQ(öBQ33BQ=qBQG®BQQìBQ\)BQffBQp€BQzáBQ
+BQ\BQBQ£×BQ®BQžRBQÂBQÌÍBQ×
+BQáHBQë
+BQõÃBR  BR
+=BR{BR
+žBR(öBR33BR=qBRG®BRQìBR\)BRffBRp€BRzáBR
+BT  BT
+=BT{BT
+žBT(öBT33BT=qBTG®BTQìBT\)BTffBTp€BTzáBT
+BT\BTBT£×BT®BTžRBTÂBTÌÍBT×
+BTáHBTë
+BTõÃBU  BU
+=BU{BU
+žBU(öBU33BU=qBUG®BUQìBU\)BUffBUp€BUzáBU
+BU\BUBU£×BU®BUžRBUÂBUÌÍBU×
+BUáHBUë
+BUõÃBV  BV
+=BV{BV
+žBV(öBV33BV=qBVG®BVQìBV\)BVffBVp€BVzáBV
+BX  BX
+=BX{BX
+žBX(öBX33BX=qBXG®BXQìBX\)BXffBXp€BXzáBX
+BX\BXBX£×BX®BXžRBXÂBXÌÍBX×
+BXáHBXë
+BXõÃBY  BY
+=BY{BY
+žBY(öBY33BY=qBYG®BYQìBY\)BYffBYp€BYzáBY
+BY\BYBY£×BY®BYžRBYÂBYÌÍBY×
+BYáHBYë
+BYõÃBZ  BZ
+=BZ{BZ
+žBZ(öBZ33BZ=qBZG®BZQìBZ\)BZffBZp€BZzáBZ
+B\  B\
+=B\{B\
+žB\(öB\33B\=qB\G®B\QìB\\)B\ffB\p€B\záB\
+B\\B\B\£×B\®B\žRB\ÂB\ÌÍB\×
+B\áHB\ë
+B\õÃB]  B]
+=B]{B]
+žB](öB]33B]=qB]G®B]QìB]\)B]ffB]p€B]záB]
+B]\B]B]£×B]®B]žRB]ÂB]ÌÍB]×
+B]áHB]ë
+B]õÃB^  B^
+=B^{B^
+žB^(öB^33B^=qB^G®B^QìB^\)B^ffB^p€B^záB^
+B`  B`
+=B`{B`
+žB`(öB`33B`=qB`G®B`QìB`\)B`ffB`p€B`záB`
+B`\B`B`£×B`®B`žRB`ÂB`ÌÍB`×
+B`áHB`ë
+B`õÃBa  Ba
+=Ba{Ba
+žBa(öBa33Ba=qBaG®BaQìBa\)BaffBap€BazáBa
+Ba\BaBa£×Ba®BažRBaÂBaÌÍBa×
+BaáHBaë
+BaõÃBb  Bb
+=Bb{Bb
+žBb(öBb33Bb=qBbG®BbQìBb\)BbffBbp€BbzáBb
+Bd  Bd
+=Bd{Bd
+žBd(öBd33Bd=qBdG®BdQìBd\)BdffBdp€BdzáBd
+Bd\BdBd£×Bd®BdžRBdÂBdÌÍBd×
+BdáHBdë
+BdõÃBe  Be
+=Be{Be
+žBe(öBe33Be=qBeG®BeQìBe\)BeffBep€BezáBe
+Be\BeBe£×Be®BežRBeÂBeÌÍBe×
+BeáHBeë
+BeõÃBf  Bf
+=Bf{Bf
+žBf(öBf33Bf=qBfG®BfQìBf\)BfffBfp€BfzáBf
+Bh  Bh
+=Bh{Bh
+žBh(öBh33Bh=qBhG®BhQìBh\)BhffBhp€BhzáBh
+Bh\BhBh£×Bh®BhžRBhÂBhÌÍBh×
+BháHBhë
+BhõÃBi  Bi
+=Bi{Bi
+žBi(öBi33Bi=qBiG®BiQìBi\)BiffBip€BizáBi
+Bi\BiBi£×Bi®BižRBiÂBiÌÍBi×
+BiáHBië
+BiõÃBj  Bj
+=Bj{Bj
+žBj(öBj33Bj=qBjG®BjQìBj\)BjffBjp€BjzáBj
+Bl  Bl
+=Bl{Bl
+žBl(öBl33Bl=qBlG®BlQìBl\)BlffBlp€BlzáBl
+Bl\BlBl£×Bl®BlžRBlÂBlÌÍBl×
+BláHBlë
+BlõÃBm  Bm
+=Bm{Bm
+žBm(öBm33Bm=qBmG®BmQìBm\)BmffBmp€BmzáBm
+Bm\BmBm£×Bm®BmžRBmÂBmÌÍBm×
+BmáHBmë
+BmõÃBn  Bn
+=Bn{Bn
+žBn(öBn33Bn=qBnG®BnQìBn\)BnffBnp€BnzáBn
+Bp  Bp
+=Bp{Bp
+žBp(öBp33Bp=qBpG®BpQìBp\)BpffBpp€BpzáBp
+Bp\BpBp£×Bp®BpžRBpÂBpÌÍBp×
+BpáHBpë
+BpõÃBq  Bq
+=Bq{Bq
+žBq(öBq33Bq=qBqG®BqQìBq\)BqffBqp€BqzáBq
+Bq\BqBq£×Bq®BqžRBqÂBqÌÍBq×
+BqáHBqë
+BqõÃBr  Br
+=Br{Br
+žBr(öBr33Br=qBrG®BrQìBr\)BrffBrp€BrzáBr
+Bt  Bt
+=Bt{Bt
+žBt(öBt33Bt=qBtG®BtQìBt\)BtffBtp€BtzáBt
+Bt\BtBt£×Bt®BtžRBtÂBtÌÍBt×
+BtáHBtë
+BtõÃBu  Bu
+=Bu{Bu
+žBu(öBu33Bu=qBuG®BuQìBu\)BuffBup€BuzáBu
+Bu\BuBu£×Bu®BužRBuÂBuÌÍBu×
+BuáHBuë
+BuõÃBv  Bv
+=Bv{Bv
+žBv(öBv33Bv=qBvG®BvQìBv\)BvffBvp€BvzáBv
+Bx  Bx
+=Bx{Bx
+žBx(öBx33Bx=qBxG®BxQìBx\)BxffBxp€BxzáBx
+Bx\BxBx£×Bx®BxžRBxÂBxÌÍBx×
+BxáHBxë
+BxõÃBy  By
+=By{By
+žBy(öBy33By=qByG®ByQìBy\)ByffByp€ByzáBy
+By\ByBy£×By®ByžRByÂByÌÍBy×
+ByáHByë
+ByõÃBz  Bz
+=Bz{Bz
+žBz(öBz33Bz=qBzG®BzQìBz\)BzffBzp€BzzáBz
+B|  B|
+=B|{B|
+žB|(öB|33B|=qB|G®B|QìB|\)B|ffB|p€B|záB|
+B|\B|B|£×B|®B|žRB|ÂB|ÌÍB|×
+B|áHB|ë
+B|õÃB}  B}
+=B}{B}
+žB}(öB}33B}=qB}G®B}QìB}\)B}ffB}p€B}záB}
+B}\B}B}£×B}®B}žRB}ÂB}ÌÍB}×
+B}áHB}ë
+B}õÃB~  B~
+=B~{B~
+žB~(öB~33B~=qB~G®B~QìB~\)B~ffB~p€B~záB~
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =
+(Ð9±¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?¹V:?=œ =(a<m<{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @7-@	ñâ?ºHþ?A\r=bn|<êxs¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @Ø@dt\@7Šù@
+Ù?Œg?DûC=-Ë=/)S¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @¶<@{b@±@e\@8@
+Á;?œç°?HÔ=«"U=ig¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ãZx@ÌŒ@¶Uí@ï<@@fCµ@9vR@
+šï?¿·?L8Š=Èâ=Á¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AMÚ@ùýÚ@ã)@Í0x@¶ÉÇ@ c@üf@g+i@:^@
+£?Á?O×x=åm=®xL¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+îxAAl3@úqµ@ä
+@Í€S@·=¢@ Öñ@p@@h
+@;Eº@xX?ÃUê?SvJ>ý=ËnÙ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A5A*@)A
+ÑAÙxAŠ @úå@ä~Þ@Î-@·±|@¡JË@ä@húÒ@<-o@`
+?Å%S?W>~C=èee¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AL/°A@àÃA5­kA*zAFºAaAà	@ûYa@äò°@Îÿ@ž%N@¡Ÿ@Wì@iâv@=@G°?Æô?Z³¬>
+ø>­÷¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AbÐNAWaALN	AA°A5çXA*³ÿA§AMNA	ö@ûÍ;@åf@ÎÿÙ@ž(@¢2w@ËÆ@jÊ*@=üÇ@/e?ÈÄ?^R~>,sÏ>)>¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AypìAn!ÿAbî§AW»NALöAATA6!EA*íìAºA<A	Sã@üA@åÚd@Ïs³@¹
+@¢ŠQ@?¡@k±ß@>ä|@?Ên?añO>:ï>€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÆAaOAyEAn[ìAc(AWõ;ALÁãAAA6[2A+'ÚAôAÁ(A	Ð@üŽï@æN>@Ïç@¹Ý@£,@³z@l@?Ì1@þÎ?ÌbÖ?e!>Ij[>.Ì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AYA±AðA~DAyÉ.AnÕAcb}AX/$ALûÌAAÈtA6A+aÂA .jAûA	Ç¹@ý(Â@æÂ@Ð[`@¹ô®@£þ@'M@m8@@³Õ@æs?Î2?i.²>Wä£><¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A©dAíAh@AÎA4çA:AzAnÏÂAcjAXiAM5¹AB`A6ÏA+°A hWA4ÿA
+Š@ý@ç5ê@ÐÏ:@ºh@€Ø@'@nhì@A@Î'?Ð?lÍ
+>f_ê>KZ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A©ùŽA€R=AžA
+äA
+7AëAQÞAž1Az=Ao	°AcÖWAX¢þAMoŠAB<NA7õA+ÕA ¢DAnëA
+;@þv@ç©Å@ÑC@ºÜc@€u²@@oP¡@B>@µÛ?ÑÐñ?plW>tÛ1>Y¡¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AµJA¯¢AªàA€o4AÕA;ÚA¢.AAnÓAÕ'AzvõAoCAdDAXÜìAM©ABv;A7BâA,A Ü1AšÙA
+u@þP@è
+@Ñ¶î@»P=@€é@Û@p8U@Cjó@?Ó Z?t
+(>«<>h
+è¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÀTAºòÝAµY0A¯¿Aª%×A€*Aò~AXÑA¿#A%wAÊAò
+Az°âAo}AdJ1AYÙAMãAB°'A7|ÏA,IwA!AâÆA
+¯m@þø*@èy@Ò*È@»Ä@¥]f@ö¶@q 	@DR§@
+D?ÕoÃ?w©ú>èà>v/¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AËê¢AÆC+AÀ©~A»ÒAµv%A¯ÜxAªBÌA€©AqAuÅAÜABlAš¿AAzêËAo·sAdAYPÁAN
+iABêA7¶¹A,`A!PA
+¯A
+éV@ÿkü@éK@Ò@Œ7ê@¥Ñ8@j@r®@E:K@lè?×?
+?{H>&>»¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A×:òAÑ{AËùÎAÆ`"AÀÆuA»,ÈAµ
+A¯ùoAª_ÁA€ÆA,hAŒAùA_bAÅ¶A,	A{$žAoñ_AdŸAY¯ANWWAC#þA7ð¥A,œMA!ôAVA
+#D@ÿßÖ@éy&@Ót@Œ«Ä@ŠE@Þb@rïb@F" @T?Ùt?~ç]>c§>¿_¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AâBAÜãËA×J
+AÑ°rAÌÅAÆ}AÀãlA»I¿Aµ°A°eAª|žA€ã
+AI_A¯²AA|YAâ¬AI A{^ŠAp+MAd÷õAYÄANCAC]ëA8*A,÷:A!ÃâAA
+]0A )Ø@éí @ÓO@œ@Šží@R<@s×@G	Ž@<Q?ÚÝÝ?C>¡K>ý¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AíÛAè4AânAÝ ÂA×gAÑÍhAÌ3ŒAÆAÁ aA»fµAµÍA°3\Aª¯A¥ AfVAÌ©A2üAPAÿ£AeöA{Ape:Ae1áAYþANË0ACØA8dA-1'A!ýÎAÊvA
+
+A cÆ@ê`Ú@Óú)@œx@§,Ç@Æ@tŸÌ@Gñh@$?Ü­F?>¥Þî>:Š¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aù+âAókAíêŸAèQAâ·eAÝ
+žA×
+AÑê_AÌP±AÆ·AÁ
+XA»¬AµéÿA°PRAª¶ŠA¥
+ùALAé AOóA¶FA
+AìA{ÒAp'AekÎAZ8vAO
+ACÑÅA8lA-kA"7ŒAdA
+Ñ
+A ²@êÔŽ@Ôn@ŸR@§ ¢@9ñ@uŠ@HÙ
+@
+
+º?Þ|¯?áé>­
+>xJ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B>AþÔ¹Aù;
+Aó¡`Aî³AènAâÔZAÝ:­A× ÿAÒSAÌmŠAÆÓúAÁ:MA»  A¶ôA°mGAªÓA¥9îA AAAlçAÓ:A9AáA|
+hApÙAe¥žAZr_AO?AD
+®A8ØVA-€þA"q¥A>LA
+
+ôA ×@ëH@ÔáÖ@Ÿ{%@št@­Ã@v$@IÀÁ@
+ó_?àK÷?±2>ŽYµ>Šµí¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bæ@BBE®Aþñ¯AùXAóŸVAî$ªAèýAâñOAÝW£A×œöAÒ$JAÌAÆððAÁWDA»œA¶#êA°>AªðA¥VäAœ7A#AÝAð1AVAŒØA|FVAqýAeß€AZ¬LAOxôADEA9CA-ÞêA"«Ax9A
+DáA@ëŒa@ÕU°@Ÿîÿ@šN@!@wuÙ@Jšv@
+Û?â`?>»Y>­ó¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+hB
+º¬BíÖB! BT)AÿŠAùtúAóÛMAîAAè§óAãFAÝtA×ÚíAÒA@AÌ§AÇ
+çAÁt:A»ÚA¶@áA°§4A«
+A¥sÚAÚ-A@AŠÔA
+(As{AÙÎA|CAqLêAfAZæ:AO²áADA9L0A.×A"åA²'A
+~ÏAKv@ì0;@ÕÉ@¿bÙ@šü)@x@x]@K*@
+ÂÇ?ãêÊ?P>ÂÔü>µ15¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B6BbÔB
+þB
+É(BüQB/{Bb¥Aÿ+AùïAóøCAî^AèÄêAã+=AÝA×÷äAÒ^7AÌÄAÇ*ÞAÁ1A»÷A¶]×A°Ä*A«*}A¥ÑA÷$A]xAÃËA*
+ArAöÅA|º0AqØAfSA[ &AOìÎAD¹uA9
+A.RÅA#mAìA
+žŒA
+c@ì€@Ö=e@¿ÖŽ@©p@	R@yEB@Lwß@ª|?åº2?m>Ê¡>ŒnÙ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BÞžB
+üB>&BqPB
+€yB
+×£B
+ÍB=öBq AÿHAù®æAô:Aî{AèáàAãH4AÝ®AØÚAÒ{.AÌáAÇGÔAÁ®'AŒzA¶zÎA°á!A«GtA¥­ÈA AznAàÂAGA­hA»A|ô
+AqÀÄAflA[ZAP&»ADócA9À
+A.²A#YZA&A
+ò©A¿P@íð@Ö±?@ÀJ@©ãÝ@},@z,ö@M_@ 1?ç?îÖ>ÑPD>Ã¬|¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+ßB³#BæMBwBL BÊB
+²ôB
+æ
+BGBLqBAÿeAùËÛAô2.AîAèþÕAãe(AÝË|AØ1ÏAÒ"AÌþuAÇdÈAÁË
+AŒ1oA¶ÂA°þA«diA¥ÊŒA 1AcAý¶Ad	AÊ\A0°A}.Aqú­AfÇUA[ýAP`¥AE-LA9ùôA.ÆA#CA_êA
+,Aù:@íÂ@×%@ÀŸ`@ªW¯@ðþ@{@NG8@!yÕ?éXå?Ÿ>Øh>Êê ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B$/B![KB
+uBÁBôÈB'òB[
+BEB
+ÁoB
+ôB'ÂBZìBAÿAùèÒAôO%AîµxAéÌAãAÝèrAØNÅAÒµAÍlAÇ¿AÁèAŒNfA¶Ž¹A±
+A«`A¥ç³A NAŽYA¬A AçSAMŠA}góAr4AgCA[ÍêAPAEg9A:3áA/ A#Í0AØA
+fA3&@íÿ@×ë@Á2:@ªË@dÙ@{üO@O.ì@"a?ë(M?>ßË
+>Ò'Ä¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B)×/B'sB$6B!iÇB
+ðBÐBDB6mBiBÁB
+ÏêB
+B6>BigBAÿtAúÈAôl
+AîÒoAé8ÂAãAÞhAØkŒAÒÒAÍ8bAÇ¶AÂ	AŒk\A¶Ñ°A±8A«VAŠ©A jüAÑPA7£AöAJAjA}¡áArnAg;0A\×APÔAE¡&A:mÎA/:vA$
+AÓÄA
+ lAm@îsv@Ø
+Å@ÁŠ@«?c@Ø³@|ä@P @#I>?ì÷·?\ñ>ç¯>Ùeh¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B/WB,«B)ÞÅB'ïB$EB!xBB
+«lBÞB¿BDéBxB«<B
+ÞfB
+BD¹BwâB«
+AÿŒkAú"¿AôAîïeAéUžAãŒ
+AÞ"_AØ²AÒïAÍUYAÇ»¬AÂ" AŒSA¶îŠA±TùA«»LAŠ! A óAîFATAºíA!@AA}ÛÎAršuAgu
+A\AÄAQlAEÛA:§»A/tbA$A
+A
+²A
+ÚYA§@îçQ@Ø@Âï@«³>@L@}Ëž@PþV@$0ó?îÇ ?,Z>îFT>à£
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B5'B2SÃB/íB,ºB)í@B' jB$SB!œB
+¹çBíB :BSdBB¹·B
+ìáB
+ 
+BS4B^B¹AÿÙbAú?µAôŠAï
+\Aér¯AãÙAÞ?VAØ¥©AÓ
+üAÍrPAÇØ£AÂ>öAŒ¥IA·
+A±qðA«ØCAŠ>A €êA
+=AqA×äA>7A€A~»ArâbAg¯
+A\{²AQHYAF A:ášA/®PA$z÷AGAFAàî@ï[+@Øôz@ÂÉ@¬'@Àg@~³l@Qæ
+@%§?ð?ûÄ>õù>çà°¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B:ÏŠB7ûêB5/B2b>B/gB,ÈB)û»B'.äB$bB!8B
+ÈaBûB.µBaÞBBÈ1B
+û[B
+.
+Ba®BØBÈAÿöVAú\ªAôÂýAï)PAé€Aãõ÷AÞ\JAØÂAÓ(ñAÍDAÇõAÂ[êAŒÂ>A·(A±äA«õ8AŠ[A ÁÞA(2A
+AôØA[,AÁ~A~O£As
+LAgèóA\µAQBAFNêA;A/è9A$ŽàAAN/A×@ïÎý@ÙhL@Ã@¬ê@49@@RÍ¯@& L?òeÒ?Ë
+>üÁ
+>ï
+U¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B@wÎB=€B:×<B8
+fB5=B2p¹B/£ãB,×
+B*
+6B'=`B$pB!£³B
+ÖÝB
+
+B=0BpYB£BÖ­B	ÖB
+= Bp*B£SBÖ}B 	§Aúy AôßôAïFGAé¬AäîAÞyAAØßAÓEçAÍ¬:AÈAÂxáAŒß4A·EA±«ÛA¬.AŠxA ÞÕAE(A«|AÎAx"AÞuA~AsV8Ah"àA\ïAQŒ/AF×A;U~A0"&A$îÍA»uA
+ATÄ@ðB×@ÙÜ&@Ãuu@­Ä@š@Ac@Sµc@&è ?ô5;?u?ÿ`>ö[ø¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BFöBCL:B@dB=²B:å·B8áB5L
+B24B/²^B,åB*±B'KÛB$B!².B
+åXB
+BK«B~ÕB±þBå(BRB
+K{B~¥B±ÏBäøB !AúAôüêAïc>AéÉAä/äAÞ7AØüAÓbÞAÍÉ1AÈ/AÂØAŒü+A·b~A±ÈÒA¬/%AŠxA ûÌAb
+AÈrA.ÅAAûlA~Ã~As&Ah\ÍA])uAQö
+AFÂÄA;kA0\A%(»AõbAÂ	A±@ð¶±@ÚP @ÃéO@­@î@µ=@T@'Ïµ?ö€?iÞ?2>ý¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BKÈ
+BHôbBF'BCZ¶B@ßB=Á	B:ô3B8'\B5ZB2°B/ÀÙB,ôB*'-B'ZVB$B!À©B
+óÓB
+&ýBZ&BPBÀzBó£B&ÍB
+Y÷B BÀIBósB &Aú³AõáAï4AéæAäLÚAÞ³.AÙAÓÔAÍæ(AÈL{AÂ²ÎAœ"A·uA±åÈA¬L
+AŠ²nA¡ÂAAåhAKŒA²A
+bA~ýkAsÊAhºA]cbAR0	AFü±A;ÉYA0 A%b§A/OAû÷AÈ@ñ*@ÚÃÛ@Ä]*@­öy@È@)@UÌ@(·i?÷Ô
+?9G?	=?k ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BQpFBNBKÏŽBIÞBF6BCi1B@[B=ÏB;®B85ØB5iB2+B/ÏUB-~B*5šB'hÑB$ûB!Ï%BNB
+5xBh¢BËBÎõBB5HB
+hqBBÎÅBîB 5AúÐAõ6×Aï*Aê~AäiÑAÞÐ$AÙ6xAÓËAÎ
+AÈirAÂÏÅAœ6A·lA²ŸA¬iAŠÏeA¡5žA
+A_Ah²AÏA
+5YA7XAt AhÐ§A]OARi÷AG6A<EA0ÏíA%Ai<A5äA@ñf@Û7µ@ÄÑ@®jS@¢@ò@Vl@)
+?ù£v? °?
+ÛÖ?
+r¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BWmBTD±BQwÛBN«BKÞ.BIXBFDBCw«B@ªÕB=ÝÿB;(B8DRB5w|B2ª¥B/ÝÏB-øB*D"B'wLB$ªuB!ÝBÉB
+CòBw
+BªFBÝoBBCÂB
+vìBªBÝ?BiB CAúíxAõSÌAïºAê rAäÆAÞíAÙSlAÓ¹ÀAÎ AÈfAÂìºAœS
+A·¹`A²³A¬AŠìZA¡R­A¹ ATA
+§AëúA
+RNAqAAt=éAi
+A]×8AR£ßAGpA<=/A1	ÖA%Ö~A£%AoÍA<t@ò8@Û«@ÅDÖ@®Þ%@wt@Ä@WT%@*Â?ûrŸ?¡×ù?zg?	©D¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B\ÀBYìÙBW BTS-BQVBN¹BKìªBIÓBFRýBC'B@¹PB=ìzB;€B8RÍB5
+÷B2¹ B/ìJB-tB*RB'
+ÇB$žñB!ìBDB
+RnB
+BžÀBëêBBR=B
+
+gBžBë»B
+åB RAû
+oAõpÂAï×Aê=iAä£ŒAß
+AÙpcAÓÖ¶AÎ=
+AÈ£\AÃ	°AœpA·ÖVA²<ªA¬¢ýA§	PA¡o€AÕ÷A<JA¢AñA
+oDA«.AtwÖAiD}A^%ARÝÍAGªtA<w
+A1CÃA&kAÝA©ºAvb@ò@Üa@Åž°@¯R @ëO@@X;Ù@+nv?ýB'?£§b?:?
+H¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BbhœB_B\È+BYûUBW.~BTašBQÒBNÇûBKû%BI.OBFaxBC¢B@ÇÌB=úõB;.B8aHB5rB2ÇB/úÅB--ïB*aB'BB$ÇlB!úB-¿B
+`èBBÇ<BúeB-B`¹B
+ãBÇ
+Bú6B-`B `Aû'fAõ¹Aïô
+AêZ`AäÀ³Aß'AÙZAÓó¬AÎZ AÈÀSAÃ&ŠAœúA·óMA²Y A¬¿ôA§&GA¡AòîAYAA¿A%çA
+:AåAt±ÃAi~kA^KASºAGäaA<±	A1}°A&JXA Aã§A°N@òùì@Ü<@Æ,@¯ÅÚ@_)@øx@Y#@,V+?ÿ?¥vË?ž
+?æè¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BhåBe=)BbpSB_£}B\ÖŠBZ	ÐBW<úBTp#BQ£MBNÖwBL	 BI<ÊBFoôBC£
+B@ÖGB>	pB;<B8oÄB5¢íB2ÖB0	AB-<jB*oB'¢ŸB$ÕçB"	B<:B
+odB¢BÕ·BáB<
+Bo5B
+¢^BÕB²B;ÛB oAûD\Aõª°AðAêwVAäÝªAßCüAÙªPAÔ£AÎvöAÈÝJAÃCAœ©ðAžDA²vA¬ÜêA§C>A¡©AäAv7AÜABÞA
+©1AAtë°AižXA^ÿASQ§AH
+NA<êöA1·A&EAPìA
+Aê<@ómÇ@Ý@Æ e@°9Ž@Ó@lR@Z
+B@-=ß@ p|?§F3?VÝ?
+¹¿  ¿  ¿  ¿  ¿  ¿  Bm¹
+BjåQBh{BeK¥Bb~ÎB_±øB\å"BZKBWKuBT~BQ±ÈBNäòBL
+BIKEBF~oBC±B@äÂB>ìB;KB8~?B5±iB2äB0ŒB-JæB*~B'±8B$äbB"BJµB
+}ßB±	Bä3B]BJB}°B
+°ÚBäB,BJVB }AûaSAõÇŠAð-úAêLAäú Aß`óAÙÇFAÔ-AÎíAÈú@AÃ`AœÆçAž-:A²A¬ùáA§`4A¡ÆA,ÚA.AùA_ÔA
+Æ(A,{Au%AiòEA^ŸìASAHX<A=$ãA1ñA&Ÿ2AÚAWA$)@óá¡@Ýzð@Ç?@°­@FÞ@à-@Zòö@.%@X1?©?
+õ¯?$¿  ¿  ¿  ¿  Bsa4BpxBmÀ¢BjóÌBh&õBeZBbIB_ÀrB\óBZ&ÆBWYïBTBQÀCBNólBL&BIY¿BFéBCÀB@ó<B>&fB;YB8¹B5¿ãB2ó
+B0&6B-Y_B*B'¿³B$òÜB"&BY0B
+ZB¿Bò­B%×BYB*B
+¿SBò}B%§BXÐB úAû~HAõäAðJîAê±AAåAß}èAÙä;AÔJAÎ°âAÉ5AÃ}AœãÜAžJ/A²°A­ÕA§}(A¡ã|AIÏA°"AvA|ÉA
+ã
+AIpAu_Aj,.A^øÖASÅ}AH$A=^ÌA2+tA&øAÄÃAkA^@ôUs@ÝîÂ@Ç@±!a@º°@Sÿ@[Ú@/
+8@?Õ?ªäæ?"B?Ã^¿  ¿  By	\Bv5 BshÊBpôBmÏ
+BkGBh5qBehBbÄB_ÎîB]BZ5ABWhkBTBQÎŸBOçBL5BIh;BFdBCÎBAžB>4áB;h
+B85B5Î^B3B04±B-gÛB*B'Î.B%XB"4Bg¬B
+ÕBÍÿB)B4RBg{B¥B
+ÍÏB	 øB4"BgLB uAû>AöAðgäAêÎ8Aå4AßÞAÚ2AÔg
+AÎÍØAÉ4,AÃAŸ ÒAžg%A²ÍxA­3ÌA§A¢ rAfÆAÍA3lAÀA AffAutAjfA_2ÂASÿjAHÌA=¹A2eaA'2	Aþ°AËWAÿ@ôÉN@Þb@Çûì@±;@.@ÇÙ@\ÂO@/ôí@'?¬ŽO?&3?b/B|  B{ÝÈByòBvD
+BswEBpªoBmÝBkÂBhCìBewBbª?B_ÝiB]BZCŒBWvæBTªBQÝ9BOcBLCBIv¶BF©àBCÝ	BA3B>C]B;vB8©¯B5ÜÙB3B0C,B-vVB*©B'ÜªB%ÔB"BýBv'B
+©QBÜzB£BBÍBu÷B© B
+ÜJB	tBBBuÇB šñAûž4Aö
+AðÛAêë.AåQAß·ÕAÚ
+(AÔ|AÎêÏAÉQ"AÃ·uAŸ
+ÈAž
+A²êoA­PÂA§·A¢
+iAŒAêAPcA¶¶A
+	A\AuÓ`Aj A_l°AT9WAIÿA=Ò§A2NA'kõA
+8AEAÑí@õ=(@ÞÖw@ÈoÆ@²	@¢e@
+;³@]ª@0Ü¢@??®ž?)Ñå¿  B|B{ìDBymBvRBs
+ÁBpžêBmìBk>BhRgBe
+Bbž»B_ëäB]BZR7BW
+aBTžBQëŽBO
+ÞBLRBI
+1BFž[BCë
+BA
+®B>Q×B;
+B8ž+B5ëTB3
+~B0QšB-ÒB*·üB'ë%B%
+OB"QyB¢B
+·ËBêõB
+BQHBrB·B
+êÅB	
+ïBQBBB ·lAûÕ+Aö;~Að¡ÒAë%AånxAßÔÌAÚ;AÔ¡rAÏÅAÉnAÃÔlAŸ:¿Až¡A³fA­m¹A§Ô
+A¢:`A ³AAmYAÓ¬A: A SAv
+NAjÙõA_ŠATsEAI?ìA>
+A2Ù;A'¥ãA
+rA?2A
+Ù@õ±@ßJQ@Èã¡@²|ð@>@
+¯@^¹@1ÄV@öó?¯vw¿  ¿  B|B{ú¿By-éBvaBs<BpÇfBmúBk-¹Bh`ãBe
+BbÇ6B_ú_B]-BZ`³BWÜBTÇBQú0BO-YBL`BI­BFÆÖBCùÿBA-)B>`SB;|B8ÆŠB5ùÐB3,úB0`$B-MB*ÆwB'ù¡B%,ÊB"_óB
+B
+ÆGBùpB,B_ÄBíBÆB
+ùAB	,jB_BŸB ÅçAûò"AöXuAðŸÈAë%
+AåoAßñÂAÚXAÔŸhAÏ$ŒAÉAÃñbAŸW¶AžŸ	A³$\A­°A§ñA¢WVAœ©A#üAPAð£AVöAœJAvG;AkãA_àAT­1AIyÙA>FA3)A'ßÐA
+¬wAyAEÇ@ö$Ý@ßŸ,@ÉWz@²ðÊ@@#h@_yn@2=¶¿  ¿  ¿  ¿  B|$B|	:By<dBvoBs¢·BpÕáBn	
+Bk<4Bho^Be¢BbÕ±B`ÛB]<BZo.BW¢XBTÕBR«BO;ÕBLnþBI¢'BFÕQBD{BA;€B>nÎB;¡øB8Õ"B6LB3;uB0nB-¡ÉB*ÔòB(B%;EB"noB¡B
+ÔÂBìB;Bn?B¡iBÔB
+ŒB	:æBnB¡9B ÔbAüAöulAðÛ¿AëBAåšeAàžAÚu
+AÔÛ_AÏA²AÉšAÄYAŸt¬AžÛ A³ASA­§ŠAš
+ùA¢tLAÚ A@óA§FA
+AsíAÚ@Av(AkMÏA`wATçAI³ÇA>nA3MA(œA
+æeA³
+AŽ@ö·@à2@ÉËU@³d€@ýó@`¿  ¿  ¿  ¿  ¿  ¿  B|3B|¶ByJßBv~	Bs±3Bpä\BnBkJ¯Bh}ÙBe±Bbä,B`VB]JBZ}©BW°ÓBTãýBR&BOJOBL}yBI°£BFãÌBDöBAJ B>}JB;°tB8ãB6ÇB3IñB0}B-°CB*ãmB(B%IÀB"|êB°B
+ã=BgBIB|ºB¯äBãB
+7B	IaB|B¯ŽB âÞAü,AöbAðøµAë_AåÅ\Aà+¯AÚAÔøVAÏ^©AÉÄüAÄ+PAŸ£Až÷öA³^IA­ÄAš*ðA¢CA÷A]êAÄ=A*AäA÷7Av»AkœA`TeAU!
+AIí³A>º[A3A(SªA
+ RAìúA¹¡@÷
+@à¥à@Ê?/@³¡T¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|AB|&/ByYYBvBs¿¬BpòÕBn%ÿBkY)BhRBe¿|BbòŠB`%ÏB]XùBZ#BW¿LBTòuBR%BOXÉBLòBI¿
+BFòFBD%pBAXB>ÃB;ŸíB8òB6%@B3XiB0B-ŸœB*ñæB(%B%X:B"cBŸB
+ñ·B$àBX
+B4BŸ]BñB
+$°B	WÚBBŸ-B ñWAüIAö¯TAñšAë{ûAåâNAàH¢AÚ®õAÕHAÏ{AÉáïAÄHBAŸ®A¹èA³{<A­áAšGâA¢®6AAzÜAá0AGA­×A)AvôûAkÁ¢A`IAUZñAJ'A>ô@A3ÀèA(A
+Z7A&ÞAó@÷[@àâ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|OÿB|4ªBygÔBvýBsÎ'BqQBn4zBkg€BhÎBeÍ÷Bc!B`4KB]gtBZBWÍÇBU ñBR4BOgDBLnBIÍBG ÂBD3ëBAgB>?B;ÍhB9 B63»B3fåB0B-Í8B+ bB(3B%fµB"ßBÍB
+ 2B3\Bf
+B¯BÌØB B
+3,B	fUBBÌšB ÿÒAüeøAöÌKAñ2AëòAåÿEAàeAÚËìAÕ2?AÏAÉþåAÄe8AŸËA¹1ßA³2A­þAšdÙA¢Ë,A1AÓAþ'AdyAÊÌA1 Aw.çAkûA`È7AUÞAJaA?..A3úÕA(Ç|A
+$A`ÌAÞ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|^zB|C%ByvOBv©yBsÜ¢BqÌBnBöBkvBh©IBeÜsBcB`BÅB]uïBZ©BWÜBBUlBRBBOuÀBLšêBIÜBG=BDBgBAuB>š¹B;ÛãB9
+B6B6B3u`B0šB-Û³B+ÝB(BB%u0B"šZBÛB
+­BA×Bu Bš*BÛTB}B
+A§B	tÐB§úBÛ$BMAüîAöéBAñOAëµèAæ
+<AàAÚèâAÕO5AÏµAÊÜAÄ/AŸèA¹NÖA³µ)A®|AšÐA¢è#ANwAŽÉA
+ApAçÃANAwhÕAl5|Aa$AUÎÌAJsA?hA44ÂA)jA
+²|¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|lõB|Q¡ByÊBv·ôBsë
+Bq
+GBnQqBkBh·ÄBeêíBc
+B`QAB]jBZ·BWêŸBU
+èBRQBO;BL·eBIêBG
+žBDPáBA
+B>·5B;ê^B9
+B6P²B3ÛB0·B-ê/B+
+XB(PB%¬B"¶ÕBéÿB
+
+(BPRB|B¶¥BéÏB
+øB
+P"B	LB¶uBéB
+ÉAüåA÷8AñlAëÒßAæ92Aà
+AÛØAÕl,AÏÒAÊ8ÒAÄ&A¿yA¹kÌA³Ò A®8sAšÇA£AklAÑÀA8AfAºAk
+Aw¢ÂAlojAa<AVžAJÕ`A?¢A4S¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|{pB|`
+ByFBvÆoBsùBq,ÃBn_ìBkBhÆ?BeùiBc,B`_ŒB]æBZÆBWù:BU,cBR_BO·BLÅàBIù	BG,3BD_]BAB>Å°B;øÚB9,B6_-B3WB0ÅB-øªB++ÔB(^ýB%'B"ÅPBøzB
++€B^ÍB÷BÅ BøJB+tB
+^B	ÇBÄñBøB+DAüŒÜA÷#/AñAëïÕAæV(AàŒ|AÛ"ÏAÕ"AÏïvAÊUÉAÄŒ
+A¿"pA¹ÃA³ïA®UiAš»ŒA£"AcAî¶AU
+A»]A!°AAwÜ¯Al©VAauþAVBŠAJóž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ìB|nBy¡ÁBvÔëBtBq;=BnngBk¡BhÔºBfäBc;B`n8B]¡bBZÔBXµBU:ßBRnBO¡1BLÔ[BJ
+BG:®BDmØBA¡B>Ô+B<UB9:B6mšB3 ÒB0ÓüB.%B+:OB(mxB% ¢B"ÓÌB õB
+:BmHB rBÓBÅB9ïB
+mB	 BBÓlBB9¿AüÙÒA÷@%AñŠxAì
+ÌAæsAàÙrAÛ?ÆAÕŠAÐ
+lAÊrÀAÄÙA¿?gA¹¥¹AŽ
+
+A®r`AšØ³A£?A¥ZA
+­Ar AØTA>§A€úAxAlãDAaV¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|gB|}By°<BvãeBtBqI¹Bn|âBk°
+Bhã6Bf`BcIB`|³B]¯ÝBZãBX0BUIYBR|BO¯­BLâÖBJ BGI*BD|SBA¯}B>â§B<ÐB9HúB6|$B3¯MB0âwB. B+HÊB({ôB%¯
+B"âGB pB
+HB{ÄB®íBâBABHjB
+{B	®ŸBáçBBH;AüöÈA÷]
+AñÃoAì)ÂAæAàöiAÛ\ŒAÕÃAÐ)cAÊ·AÄö	A¿\\A¹Â°AŽ)A®VAšõªA£[ýAÂPA(€A÷AõKA[AÁñAx4ô¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ŠâB|ByŸ·BvñáBt%
+BqX4Bn^BkŸBhñ²Bf$ÛBcXB`/B]ŸXBZñBX$«BUWÕBRþBOŸ(BLñRBJ${BGW¥BDÏBAœøB>ñ"B<$LB9WuB6B3œÈB0ðòB.$
+B+WEB(oB%œB"ðÂB #ìB
+WB?BœiBðB#ŒBVæB
+B	œ9BðcB#BV¶Aý¿A÷zAñàfAìF¹Aæ­
+Aá`AÛy³AÕàAÐFYAÊ¬¬AÅ A¿ySA¹ßŠAŽEúA®¬MA© A£xôAßGAEA«îA@AjÉ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|µ]B|	ByÍ2Bw \Bt3Bqf°BnÚBkÍBi -Bf3WBcfB`©B]ÌÓBZÿýBX3&BUfPBRzBOÌ£BLÿÍBJ2÷BGf BDJBAÌtB>ÿB<2ÇB9eðB6B3ÌDB0ÿmB.2B+eÀB(êB%ÌB"ÿ=B 2gB
+eBºBËäBÿB27BeaB
+B	ËŽBþÞB2Be1Aý0¶A÷	Añý\Aìc°AæÊAá0WAÛ©AÕüüAÐcPAÊÉ£AÅ/öA¿JA¹üAŽbðA®ÉDA©/A£ëAü>AbA»¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÃØB|šByÛ®BwØBtBBqu+BnšUBkÛBišBfAÑBctûB`š%B]ÛNB[xBXA¢BUtËBR§õBOÛBMHBJArBGtBD§ÅBAÚïB?B<ABB9tlB6§B3Ú¿B1
+èB.AB+t<B(§eB%ÚB#
+¹B @âB
+t
+B§6BÚ_B
+B@³BsÜB
+§B	Ú0B
+YB@Bs¬AýM¬A÷Ž AòSAì§AææùAáMLAÛ³ AÖóAÐFAÊæAÅLíA¿³@AºAŽçA®æ;A©LA£²àA
+i¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÒTB|¶þByê(Bw
+QBtP{Bq¥Bn¶ÎBké÷Bi
+!BfPKBctB`¶B]éÈB[
+ñBXPBUEBR¶nBOéBM
+ÂBJOëBGBD¶>BAéhB?
+B<O»B9åB6¶B3é8B1
+bB.OB+µB(µßB%éB#
+2B O\B
+
+Bµ¯BèÙB
+BO,BVB
+µB	è©BÒBNüB&AýjA÷ÐóAò7EAìAçìAáj?AÛÐAÖ6æAÐ9AËAÅiàA¿Ð3Aº6AŽÚA¯,A©[µ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|àÐB|ÅyByø£Bw+ÍBt^öBqBnÅIBkøsBi+Bf^ÆBcðB`ÅB]øCB[+mBX^BUÀBRÄêBOøBM+=BJ^fBGBDÄºBA÷ãB?+
+B<^6B9`B6ÄB3÷³B1*ÝB.^B+0B(ÄZB%÷B#*­B ]×B
+BÄ*B÷TB*~B]§BÑB
+ÃúB	÷$B*NB]wB¡AýA÷íèAòT<AìºAç âAá6AÛíAÖSÜAÐº0AË AÅ×A¿í*AºS|AŽ¬¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ïKB|ÓõBz
+Bw:GBtmqBq BnÓÄBlîBi:BfmABc kB`ÓB^ŸB[9èBXmBU ;BRÓeBPBM9žBJlâBG 
+BDÓ5BB^B?9B<l²B9ÛB6ÓB4/B19XB.lB+¬B(ÒÕB&ÿB#9)B lRB
+|BÒŠBÏB8ùBl"BLB
+ÒvB
+B8ÉBkóB
+Aý€Aø
+ßAòq2Aì×Aç=ÙAá€,AÜ
+AÖpÓAÐ×'AË=zAÅ£ÌA¿üU¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ýÆB|âoBzBwHÃBt{ìBq¯Bnâ@BliBiHBf{œBc®æB`âB^:B[HcBX{BU®¶BRáàBP
+BMH3BJ{]BG®BDá°BBÚB?HB<{-B9®WB6áB4ªB1GÔB.zýB+®'B(áQB&zB#G€B zÎB
+­÷Bá!BJBGtBzB­ÇB
+àñB
+BGDBznB­AýÁAø'ÖAò)Aìô|AçZÐAáÁ#AÜ'wAÖÊAÐô
+AËL¥¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+AB|ðëBz$BwW>BthBqœBnð»Bl#åBiWBf8BcœbB`ðB^#µB[VÞBXBUœ2BRð[BP#
+BMV®BJØBGœBDð+BB#UB?VB<šB9ŒÒB6ïüB4#%B1VOB.yB+Œ¢B(ïÌB&"öB#VB IB
+ŒrBïB"ÆBUïBBŒCB
+ïlB
+"BUÀBéBŒAýÞyAøDÌAò« AísAçwÇAáÞAÜDlAÖõ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ŒB|ÿfBz2Bwe¹BtãBqÌ
+Bnÿ6Bl2`BieBf³BcËÝB`ÿB^20B[eZBXBUË­BRþÖBP2 BMe*BJSBGË}BDþ§BB1ÐB?dúB<$B9ËMB6þwB41¡B1dÊB.ôB+Ë
+B(þGB&1qB#dB ÄB
+ÊîBþB1ABdkBBÊŸB
+ýèB
+1Bd;BeBÊAýûpAøaÃAòÈAí.jAçŒAáíE¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B})8B}
+áBzA
+Bwt5Bt§^BqÚBo
+²Bl@ÛBitBf§.BcÚXBa
+B^@«B[sÕBXŠþBUÚ(BS
+RBP@{BMs¥BJŠÏBGÙøBE
+"BB@LB?suB<ŠB9ÙÉB7
+òB4@
+B1sFB.ŠoB+ÙB)
+ÂB&?ìB#sB Š?B
+ÙiB
+B?ŒBræBŠBÙ9B
+
+cB
+?Br¶B¥ßBÙ
+AþgAø~ºAòå
+Aí=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}7³B}
+]BzOBw°BtµÚBqéBo
+-BlOVBiBfµªBcèÓBaýB^O&B[PBXµzBUè£BSÍBPN÷BM BJµJBGètBEBBNÇB?ñB<µB9èDB7nB4NB1ÁB.ŽêB+èB)>B&NgB#B Ž»B
+çäBBN8BaBŽBçµB
+ÞB
+NB2BŽ[Bç
+Aþ5\Aøå¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}F.B}*ØBz^Bw+BtÄUBq÷~Bo*šBl]ÒBiûBfÄ%Bc÷NBa*xB^]¢B[ËBXÃõBU÷BS*HBP]rBMBJÃÅBGöïBE*BB]BB?lB<ÃB9ö¿B7)éB4]B1<B.ÃfB+öB))¹B&\ãB#
+B Ã6B
+ö`B)B\³BÝBÃBö/B
+)ZB
+\B­BÂ×Bï¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}TªB}9SBzl}BwŠBtÒÐBrúBo9#BllMBivBfÒ BdÊBa8óB^l
+B[GBXÒpBVBS8ÄBPkíBMBJÒABHjBE8BBkŸB?çB<ÒB::B78dB4kB1·B.ÑáB,
+B)84B&k^B#B Ñ±B
+ÛB8Bk.BWBÑB«B
+7ÕB
+jÿBC¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}c%B}GÌBzzöBw® BtáIBrsBoGBlzÆBi­ðBfáBdCBaGmB^zB[­ÀBXàêBVBSG=BPzgBM­BJàºBHäBEG
+BBz7B?­`B<àB:ŽB7FÝB4zB1­1B.àZB,B)F®B&y×B#­B à+B
+TBF}ByšB¬ÑBßûB%B
+?i¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}q B}VHBzqBwŒBtïÄBr"îBoVBlABiŒkBfïBd"ŸBaUèB^B[Œ;BXïeBV"BSUžBPâBMŒ
+BJï5BH"_BEUBB²B?»ÜB<ïB:"/B7UYB4B1»¬B.îÖB,!ÿB)U)B&SB#»|B î¥B
+!ÐBTùB#B»MBç¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}B}dÃBzìBwËBtþ@Br1iBodBlœBiÊæBfþBd1:BadcB^B[Ê·BXýàBV1
+BSd4BP]BMÊBJý°BH0ÚBEdBB-B?ÊWB<ýB:0ªB7cÔB4þB1Ê'B.ýQB,0{B)c€B&ÍB#ÉøB ý!B
+0KBcuB¹¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}B}s>BzŠhBwÙBu
+»Br?åBosBlŠ8BiÙbBg
+Bd?µBarßB^ŠB[Ù2BY
+\BV?
+BSr¯BP¥ØBMÙBK
+,BH?UBErBB¥©B?ØÒB=
+üB:?&B7rOB4¥yB1Ø£B/
+ÌB,>õB)r B&¥IB#ØsB!
+B
+7á¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}B}¹BzŽãBwè
+Bu6BrN`BoBlŽ³BiçÝBgBdN0BaZB^ŽB[ç­BY×BVN BS*BPŽTBMç}BK§BHMÑBEúBBŽ$B?çNB=wB:M¡B7ËB4³ôB1ç
+B/HB,MqB)B&³ÅB#à	¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}«B}5BzÃ^BwöBu)²Br\ÛBoBlÃ/BiöXBg)Bd\¬BaÕB^ÂÿB[ö(BY)RBV\|BS¥BPÂÏBMõùBK)"BH\LBEvBBÂB?õÉB=(óB:\
+B7EB4ÂpB1õB/(ÃB,[íB)1¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ºB}°BzÑÚBxBu8-BrkWBoBlÑªBjÔBg7ýBdk'BaPB^ÑzB\€BY7ÍBVj÷BS!BPÑJBNtBK7BHjÇBEñBBÑB@DB=7mB:jB7ÁB4ÐëB2B/0Y¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÈB}­+BzàUBxBuFšBryÒBo¬üBlà%BjOBgFxBdy¢Ba¬ÌB^ßõB\BYFIBVyrBS¬BPßÆBNïBKFBHyCBE¬lBBßB@ÀB=EéB:yB7¬=B4Ø¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÖÿB}»§BzîÐBx!úBuU$BrMBo»wBlî Bj!ÊBgTôBd
+Ba»GB^îqB\!BYTÄBVîBS»BPîABN!kBKTBHœBEºèBBîB@!;B=TeB:©¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}åzB}Ê"BzýLBx0uBucBrÈBoÉòBlý
+Bj0EBgcoBdBaÉÂB^üìB\0BYc?BViBSÉBPüŒBN/åBKcBH9BEÉcBBüB@(Ñ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}óöB}ØB{
+ÅBx>îBurBr¥BBoØkBm
+Bj>¿BgqèBd¥BaØ<B_
+eB\>BYq¹BV€âBSØ
+BQ
+6BN>_BKqBH€³BEÐ÷¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~qB}çB{@BxMjBuBr³œBoæçBmBjM:BgdBd³Baæ·B_áB\M
+BY3BV³^BSæBQ±BNLÛBKy¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~ìB}õB{(»Bx[åBuBrÂ8BoõbBm(Bj[µBgßBdÂ	Baõ2B_([B\[BY¯BVÁÙBSõBQ!G¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~gB~
+B{77Bxj`BuBrÐŽBpÝBm7Bjj1BgZBdÐBb®B_6×B\jBY+BVÉo¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~-ãB~B{E²BxxÜBu¬Brß/BpYBmEBjx«Bg«ÖBdÞÿBb)B_ESB\q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~<^B~!B{T-BxWBuºBríªBp ÓBmSþBj'BgºQBdí{Bb¿¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~JÙB~/B{b©BxÒBuÈûBrü&Bp/OBmbyBj£BgÁç¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~YUB~=úB{q#Bx€MBu×wBs
+¡Bp=ËBmj¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~gÏB~LuB{Bx²ÉBuåóBs7¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~vKB~ZñB{Bxº_¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~ÇB~b
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B|  Bx  Bt  Bp  Bl  Bh  Bd  B`  B\  BX  BT  BP  BL  BH  BD  B@  B<  B8  B4  B0  B,  B(  B$  B   B
+  B  B  B  B
+  B  B  B   Aø  Að  Aè  Aà  AØ  AÐ  AÈ  AÀ  Až  A°  Aš  A   A  A  A  A  Ap  A`  AP  A@  A0  A   A  A   @à  @À  @   @  @@  @   ?      B|
+=Bx
+=Bt
+=Bp
+=Bl
+=Bh
+=Bd
+=B`
+=B\
+=BX
+=BT
+=BP
+=BL
+=BH
+=BD
+=B@
+=B<
+=B8
+=B4
+=B0
+=B,
+=B(
+=B$
+=B 
+=B
+
+=B
+=B
+=B
+=B
+
+=B
+=B
+=B 
+=Aø{Að{Aè{Aà{AØ{AÐ{AÈ{AÀ{Až{A°{Aš{A {A{A{A{A{Ap(öA`(öAP(öA@(öA0(öA (öA(öA (ö@àQì@ÀQì@ Qì@Qì@@£×@ £×?G®<#×
+B|{Bx{Bt{Bp{Bl{Bh{Bd{B`{B\{BX{BT{BP{BL{BH{BD{B@{B<{B8{B4{B0{B,{B({B${B {B
+{B{B{B{B
+{B{B{B {Aø(öAð(öAè(öAà(öAØ(öAÐ(öAÈ(öAÀ(öAž(öA°(öAš(öA (öA(öA(öA(öA(öApQìA`QìAPQìA@QìA0QìA QìAQìA Qì@à£×@À£×@ £×@£×@AG®@G®?\<£×
+B|
+žBx
+žBt
+žBp
+žBl
+žBh
+žBd
+žB`
+žB\
+žBX
+žBT
+žBP
+žBL
+žBH
+žBD
+žB@
+žB<
+žB8
+žB4
+žB0
+žB,
+žB(
+žB$
+žB 
+žB
+
+žB
+žB
+žB
+žB
+
+žB
+žB
+žB 
+žAø=qAð=qAè=qAà=qAØ=qAÐ=qAÈ=qAÀ=qAž=qA°=qAš=qA =qA=qA=qA=qA=qApzáA`záAPzáA@záA0záA záAzáA zá@àõÃ@ÀõÃ@ õÃ@õÃ@Aë
+@ë
+?×
+<õÂB|(öBx(öBt(öBp(öBl(öBh(öBd(öB`(öB\(öBX(öBT(öBP(öBL(öBH(öBD(öB@(öB<(öB8(öB4(öB0(öB,(öB((öB$(öB (öB
+(öB(öB(öB(öB
+(öB(öB(öB (öAøQìAðQìAèQìAàQìAØQìAÐQìAÈQìAÀQìAžQìA°QìAšQìA QìAQìAQìAQìAQìAp£×A`£×AP£×A@£×A0£×A £×A£×A £×@áG®@ÁG®@¡G®@G®@B\@\?
+
+ž=#×
+B|33Bx33Bt33Bp33Bl33Bh33Bd33B`33B\33BX33BT33BP33BL33BH33BD33B@33B<33B833B433B033B,33B(33B$33B 33B
+33B33B33B33B
+33B33B33B 33AøffAðffAèffAàffAØffAÐffAÈffAÀffAžffA°ffAšffA ffAffAffAffAffApÌÍA`ÌÍAPÌÍA@ÌÍA0ÌÍA ÌÍAÌÍA ÌÍ@á@Á@¡@@C33@33?ff=LÌÍB|=qBx=qBt=qBp=qBl=qBh=qBd=qB`=qB\=qBX=qBT=qBP=qBL=qBH=qBD=qB@=qB<=qB8=qB4=qB0=qB,=qB(=qB$=qB =qB
+=qB=qB=qB=qB
+=qB=qB=qB =qAøzáAðzáAèzáAàzáAØzáAÐzáAÈzáAÀzáAžzáA°záAšzáA záAzáAzáAzáAzáApõÃA`õÃAPõÃA@õÃA0õÃA õÃAõÃA õÃ@áë
+@Áë
+@¡ë
+@ë
+@C×
+@×
+?®=uÂB|G®BxG®BtG®BpG®BlG®BhG®BdG®B`G®B\G®BXG®BTG®BPG®BLG®BHG®BDG®B@G®B<G®B8G®B4G®B0G®B,G®B(G®B$G®B G®B
+G®BG®BG®BG®B
+G®BG®BG®B G®Aø\Að\Aè\Aà\AØ\AÐ\AÈ\AÀ\Až\A°\Aš\A \A\A\A\A\Aq
+žAa
+žAQ
+žAA
+žA1
+žA!
+žA
+žA
+ž@â=q@Â=q@¢=q@=q@Dzá@zá?õÃ=\)B|QìBxQìBtQìBpQìBlQìBhQìBdQìB`QìB\QìBXQìBTQìBPQìBLQìBHQìBDQìB@QìB<QìB8QìB4QìB0QìB,QìB(QìB$QìB QìB
+QìBQìBQìBQìB
+QìBQìBQìB QìAø£×Að£×Aè£×Aà£×AØ£×AÐ£×AÈ£×AÀ£×Až£×A°£×Aš£×A £×A£×A£×A£×A£×AqG®AaG®AQG®AAG®A1G®A!G®AG®AG®@â\@Â\@¢\@\@E
+ž@
+ž?=q=£×
+B|\)Bx\)Bt\)Bp\)Bl\)Bh\)Bd\)B`\)B\\)BX\)BT\)BP\)BL\)BH\)BD\)B@\)B<\)B8\)B4\)B0\)B,\)B(\)B$\)B \)B
+\)B\)B\)B\)B
+\)B\)B\)B \)AøžRAðžRAèžRAàžRAØžRAÐžRAÈžRAÀžRAžžRA°žRAšžRA žRAžRAžRAžRAžRAqp€Aap€AQp€AAp€A1p€A!p€Ap€Ap€@âáH@ÂáH@¢áH@áH@EÂ@Â?
+=žQìB|ffBxffBtffBpffBlffBhffBdffB`ffB\ffBXffBTffBPffBLffBHffBDffB@ffB<ffB8ffB4ffB0ffB,ffB(ffB$ffB ffB
+ffBffBffBffB
+ffBffBffB ffAøÌÍAðÌÍAèÌÍAàÌÍAØÌÍAÐÌÍAÈÌÍAÀÌÍAžÌÍA°ÌÍAšÌÍA ÌÍAÌÍAÌÍAÌÍAÌÍAqAaAQAAA1A!AA@ã33@Ã33@£33@33@Fff@ff?ÌÍ=ÌÌÍB|p€Bxp€Btp€Bpp€Blp€Bhp€Bdp€B`p€B\p€BXp€BTp€BPp€BLp€BHp€BDp€B@p€B<p€B8p€B4p€B0p€B,p€B(p€B$p€B p€B
+p€Bp€Bp€Bp€B
+p€Bp€Bp€B p€AøáHAðáHAèáHAàáHAØáHAÐáHAÈáHAÀáHAžáHA°áHAšáHA áHAáHAáHAáHAáHAqÂAaÂAQÂAAÂA1ÂA!ÂAÂAÂ@ã
+@Ã
+@£
+@
+@G
+=@
+=?{=áG®B|záBxzáBtzáBpzáBlzáBhzáBdzáB`záB\záBXzáBTzáBPzáBLzáBHzáBDzáB@záB<záB8záB4záB0záB,záB(záB$záB záB
+záBzáBzáBzáB
+záBzáBzáB záAøõÃAðõÃAèõÃAàõÃAØõÃAÐõÃAÈõÃAÀõÃAžõÃA°õÃAšõÃA õÃAõÃAõÃAõÃAõÃAqë
+Aaë
+AQë
+AAë
+A1ë
+A!ë
+Aë
+Aë
+@ã×
+@Ã×
+@£×
+@×
+@G®@®?\)=õÂB|
+Bx
+Bt
+Bp
+Bl
+Bh
+Bd
+B`
+B\
+BX
+BT
+BP
+BL
+BH
+BD
+B@
+B<
+B8
+B4
+B0
+B,
+B(
+B$
+B 
+B
+
+B
+B
+B
+B
+
+B
+B
+B 
+Aù
+=Añ
+=Aé
+=Aá
+=AÙ
+=AÑ
+=AÉ
+=AÁ
+=A¹
+=A±
+=A©
+=A¡
+=A
+=A
+=A
+=A
+=Ar{Ab{AR{AB{A2{A"{A{A{@ä(ö@Ä(ö@€(ö@(ö@HQì@Qì?£×>
+žB|\Bx\Bt\Bp\Bl\Bh\Bd\B`\B\\BX\BT\BP\BL\BH\BD\B@\B<\B8\B4\B0\B,\B(\B$\B \B
+\B\B\B\B
+\B\B\B \Aù
+žAñ
+žAé
+žAá
+žAÙ
+žAÑ
+žAÉ
+žAÁ
+žA¹
+žA±
+žA©
+žA¡
+žA
+žA
+žA
+žA
+žAr=qAb=qAR=qAB=qA2=qA"=qA=qA=q@äzá@Äzá@€zá@zá@HõÃ@õÃ?ë
+>\)B|BxBtBpBlBhBdB`B\BXBTBPBLBHBDB@B<B8B4B0B,B(B$B B
+BBBB
+BBB Aù33Añ33Aé33Aá33AÙ33AÑ33AÉ33AÁ33A¹33A±33A©33A¡33A33A33A33A33ArffAbffARffABffA2ffA"ffAffAff@äÌÍ@ÄÌÍ@€ÌÍ@ÌÍ@I@	?33>B|£×Bx£×Bt£×Bp£×Bl£×Bh£×Bd£×B`£×B\£×BX£×BT£×BP£×BL£×BH£×BD£×B@£×B<£×B8£×B4£×B0£×B,£×B(£×B$£×B £×B
+£×B£×B£×B£×B
+£×B£×B£×B £×AùG®AñG®AéG®AáG®AÙG®AÑG®AÉG®AÁG®A¹G®A±G®A©G®A¡G®AG®AG®AG®AG®Ar\Ab\AR\AB\A2\A"\A\A\@å
+ž@Å
+ž@¥
+ž@
+
+ž@J=q@
+=q?zá>#×
+B|®Bx®Bt®Bp®Bl®Bh®Bd®B`®B\®BX®BT®BP®BL®BH®BD®B@®B<®B8®B4®B0®B,®B(®B$®B ®B
+®B®B®B®B
+®B®B®B ®Aù\)Añ\)Aé\)Aá\)AÙ\)AÑ\)AÉ\)AÁ\)A¹\)A±\)A©\)A¡\)A\)A\)A\)A\)AržRAbžRARžRABžRA2žRA"žRAžRAžR@åp€@Åp€@¥p€@
+p€@JáH@
+áH?Â>.{B|žRBxžRBtžRBpžRBlžRBhžRBdžRB`žRB\žRBXžRBTžRBPžRBLžRBHžRBDžRB@žRB<žRB8žRB4žRB0žRB,žRB(žRB$žRB žRB
+žRBžRBžRBžRB
+žRBžRBžRB žRAùp€Añp€Aép€Aáp€AÙp€AÑp€AÉp€AÁp€A¹p€A±p€A©p€A¡p€Ap€Ap€Ap€Ap€AráHAbáHARáHABáHA2áHA"áHAáHAáH@åÂ@ÅÂ@¥Â@
+Â@K
+@
+
+?
+>>8QìB|ÂBxÂBtÂBpÂBlÂBhÂBdÂB`ÂB\ÂBXÂBTÂBPÂBLÂBHÂBDÂB@ÂB<ÂB8ÂB4ÂB0ÂB,ÂB(ÂB$ÂB ÂB
+ÂBÂBÂBÂB
+ÂBÂBÂB ÂAù
+Añ
+Aé
+Aá
+AÙ
+AÑ
+AÉ
+AÁ
+A¹
+A±
+A©
+A¡
+A
+A
+A
+A
+As
+=Ac
+=AS
+=AC
+=A3
+=A#
+=A
+=A
+=@æ{@Æ{@Š{@{@L(ö@
+(ö?Qì>B\B|ÌÍBxÌÍBtÌÍBpÌÍBlÌÍBhÌÍBdÌÍB`ÌÍB\ÌÍBXÌÍBTÌÍBPÌÍBLÌÍBHÌÍBDÌÍB@ÌÍB<ÌÍB8ÌÍB4ÌÍB0ÌÍB,ÌÍB(ÌÍB$ÌÍB ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍBÌÍBÌÍB ÌÍAùAñAéAáAÙAÑAÉAÁA¹A±A©A¡AAAAAs33Ac33AS33AC33A333A#33A33A33@æff@Æff@Šff@ff@LÌÍ@
+ÌÍ?>LÌÍB|×
+Bx×
+Bt×
+Bp×
+Bl×
+Bh×
+Bd×
+B`×
+B\×
+BX×
+BT×
+BP×
+BL×
+BH×
+BD×
+B@×
+B<×
+B8×
+B4×
+B0×
+B,×
+B(×
+B$×
+B ×
+B
+×
+B×
+B×
+B×
+B
+×
+B×
+B×
+B ×
+Aù®Añ®Aé®Aá®AÙ®AÑ®AÉ®AÁ®A¹®A±®A©®A¡®A®A®A®A®As\)Ac\)AS\)AC\)A3\)A#\)A\)A\)@æžR@ÆžR@ŠžR@žR@Mp€@
+p€?áH>W
+=B|áHBxáHBtáHBpáHBláHBháHBdáHB`áHB\áHBXáHBTáHBPáHBLáHBHáHBDáHB@áHB<áHB8áHB4áHB0áHB,áHB(áHB$áHB áHB
+áHBáHBáHBáHB
+áHBáHBáHB áHAùÂAñÂAéÂAáÂAÙÂAÑÂAÉÂAÁÂA¹ÂA±ÂA©ÂA¡ÂAÂAÂAÂAÂAs
+Ac
+AS
+AC
+A3
+A#
+A
+A
+@ç
+=@Ç
+=@§
+=@
+=@N{@{?(ö>aG®B|ë
+Bxë
+Btë
+Bpë
+Blë
+Bhë
+Bdë
+B`ë
+B\ë
+BXë
+BTë
+BPë
+BLë
+BHë
+BDë
+B@ë
+B<ë
+B8ë
+B4ë
+B0ë
+B,ë
+B(ë
+B$ë
+B ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+Bë
+Bë
+B ë
+Aù×
+Añ×
+Aé×
+Aá×
+AÙ×
+AÑ×
+AÉ×
+AÁ×
+A¹×
+A±×
+A©×
+A¡×
+A×
+A×
+A×
+A×
+As®Ac®AS®AC®A3®A#®A®A®@ç\)@Ç\)@§\)@\)@NžR@žR?p€>k
+B|õÃBxõÃBtõÃBpõÃBlõÃBhõÃBdõÃB`õÃB\õÃBXõÃBTõÃBPõÃBLõÃBHõÃBDõÃB@õÃB<õÃB8õÃB4õÃB0õÃB,õÃB(õÃB$õÃB õÃB
+õÃBõÃBõÃBõÃB
+õÃBõÃBõÃB õÃAùë
+Añë
+Aéë
+Aáë
+AÙë
+AÑë
+AÉë
+AÁë
+A¹ë
+A±ë
+A©ë
+A¡ë
+Aë
+Aë
+Aë
+Aë
+As×
+Ac×
+AS×
+AC×
+A3×
+A#×
+A×
+A×
+@ç®@Ç®@§®@®@O\)@\)?žR>uÂB}  By  Bu  Bq  Bm  Bi  Be  Ba  B]  BY  BU  BQ  BM  BI  BE  BA  B=  B9  B5  B1  B-  B)  B%  B!  B
+  B  B  B  B
+  B	  B  B  Aú  Aò  Aê  Aâ  AÚ  AÒ  AÊ  AÂ  Aº  A²  Aª  A¢  A  A  A  A  At  Ad  AT  AD  A4  A$  A  A  @è  @È  @š  @  @P  @  ?   >  B}
+=By
+=Bu
+=Bq
+=Bm
+=Bi
+=Be
+=Ba
+=B]
+=BY
+=BU
+=BQ
+=BM
+=BI
+=BE
+=BA
+=B=
+=B9
+=B5
+=B1
+=B-
+=B)
+=B%
+=B!
+=B
+
+=B
+=B
+=B
+=B
+=B	
+=B
+=B
+=Aú{Aò{Aê{Aâ{AÚ{AÒ{AÊ{AÂ{Aº{A²{Aª{A¢{A{A{A{A{At(öAd(öAT(öAD(öA4(öA$(öA(öA(ö@èQì@ÈQì@šQì@Qì@P£×@£×?¡G®>
+
+žB}{By{Bu{Bq{Bm{Bi{Be{Ba{B]{BY{BU{BQ{BM{BI{BE{BA{B={B9{B5{B1{B-{B){B%{B!{B
+{B{B{B{B
+{B	{B{B{Aú(öAò(öAê(öAâ(öAÚ(öAÒ(öAÊ(öAÂ(öAº(öA²(öAª(öA¢(öA(öA(öA(öA(öAtQìAdQìATQìADQìA4QìA$QìAQìAQì@è£×@È£×@š£×@£×@QG®@G®?¢\>=qB}
+žBy
+žBu
+žBq
+žBm
+žBi
+žBe
+žBa
+žB]
+žBY
+žBU
+žBQ
+žBM
+žBI
+žBE
+žBA
+žB=
+žB9
+žB5
+žB1
+žB-
+žB)
+žB%
+žB!
+žB
+
+žB
+žB
+žB
+žB
+
+žB	
+žB
+žB
+žAú=qAò=qAê=qAâ=qAÚ=qAÒ=qAÊ=qAÂ=qAº=qA²=qAª=qA¢=qA=qA=qA=qA=qAtzáAdzáATzáADzáA4záA$záAzáAzá@èõÃ@ÈõÃ@šõÃ@õÃ@Që
+@ë
+?£×
+>\)B}(öBy(öBu(öBq(öBm(öBi(öBe(öBa(öB](öBY(öBU(öBQ(öBM(öBI(öBE(öBA(öB=(öB9(öB5(öB1(öB-(öB)(öB%(öB!(öB
+(öB(öB(öB(öB
+(öB	(öB(öB(öAúQìAòQìAêQìAâQìAÚQìAÒQìAÊQìAÂQìAºQìA²QìAªQìA¢QìAQìAQìAQìAQìAt£×Ad£×AT£×AD£×A4£×A$£×A£×A£×@éG®@ÉG®@©G®@G®@R\@\?¥
+ž>záB}33By33Bu33Bq33Bm33Bi33Be33Ba33B]33BY33BU33BQ33BM33BI33BE33BA33B=33B933B533B133B-33B)33B%33B!33B
+33B33B33B33B
+33B	33B33B33AúffAòffAêffAâffAÚffAÒffAÊffAÂffAºffA²ffAªffA¢ffAffAffAffAffAtÌÍAdÌÍATÌÍADÌÍA4ÌÍA$ÌÍAÌÍAÌÍ@é@É@©@@S33@33?Šff>B}=qBy=qBu=qBq=qBm=qBi=qBe=qBa=qB]=qBY=qBU=qBQ=qBM=qBI=qBE=qBA=qB==qB9=qB5=qB1=qB-=qB)=qB%=qB!=qB
+=qB=qB=qB=qB
+=qB	=qB=qB=qAúzáAòzáAêzáAâzáAÚzáAÒzáAÊzáAÂzáAºzáA²záAªzáA¢záAzáAzáAzáAzáAtõÃAdõÃATõÃADõÃA4õÃA$õÃAõÃAõÃ@éë
+@Éë
+@©ë
+@ë
+@S×
+@×
+?§®>žRB}G®ByG®BuG®BqG®BmG®BiG®BeG®BaG®B]G®BYG®BUG®BQG®BMG®BIG®BEG®BAG®B=G®B9G®B5G®B1G®B-G®B)G®B%G®B!G®B
+G®BG®BG®BG®B
+G®B	G®BG®BG®Aú\Aò\Aê\Aâ\AÚ\AÒ\AÊ\AÂ\Aº\A²\Aª\A¢\A\A\A\A\Au
+žAe
+žAU
+žAE
+žA5
+žA%
+žA
+žA
+ž@ê=q@Ê=q@ª=q@=q@Tzá@zá?šõÂ>£×
+B}QìByQìBuQìBqQìBmQìBiQìBeQìBaQìB]QìBYQìBUQìBQQìBMQìBIQìBEQìBAQìB=QìB9QìB5QìB1QìB-QìB)QìB%QìB!QìB
+QìBQìBQìBQìB
+QìB	QìBQìBQìAú£×Aò£×Aê£×Aâ£×AÚ£×AÒ£×AÊ£×AÂ£×Aº£×A²£×Aª£×A¢£×A£×A£×A£×A£×AuG®AeG®AUG®AEG®A5G®A%G®AG®AG®@ê\@Ê\@ª\@\@U
+ž@
+ž?ª=q>šõÃB}\)By\)Bu\)Bq\)Bm\)Bi\)Be\)Ba\)B]\)BY\)BU\)BQ\)BM\)BI\)BE\)BA\)B=\)B9\)B5\)B1\)B-\)B)\)B%\)B!\)B
+\)B\)B\)B\)B
+\)B	\)B\)B\)AúžRAòžRAêžRAâžRAÚžRAÒžRAÊžRAÂžRAºžRA²žRAªžRA¢žRAžRAžRAžRAžRAup€Aep€AUp€AEp€A5p€A%p€Ap€Ap€@êáH@ÊáH@ªáH@áH@UÂ@Â?«
+>®{B}ffByffBuffBqffBmffBiffBeffBaffB]ffBYffBUffBQffBMffBIffBEffBAffB=ffB9ffB5ffB1ffB-ffB)ffB%ffB!ffB
+ffBffBffBffB
+ffB	ffBffBffAúÌÍAòÌÍAêÌÍAâÌÍAÚÌÍAÒÌÍAÊÌÍAÂÌÍAºÌÍA²ÌÍAªÌÍA¢ÌÍAÌÍAÌÍAÌÍAÌÍAuAeAUAEA5A%AA@ë33@Ë33@«33@33@Vff@ff?¬ÌÍ>³33B}p€Byp€Bup€Bqp€Bmp€Bip€Bep€Bap€B]p€BYp€BUp€BQp€BMp€BIp€BEp€BAp€B=p€B9p€B5p€B1p€B-p€B)p€B%p€B!p€B
+p€Bp€Bp€Bp€B
+p€B	p€Bp€Bp€AúáHAòáHAêáHAâáHAÚáHAÒáHAÊáHAÂáHAºáHA²áHAªáHA¢áHAáHAáHAáHAáHAuÂAeÂAUÂAEÂA5ÂA%ÂAÂAÂ@ë
+@Ë
+@«
+@
+@W
+>@
+>?®{>žQìB}záByzáBuzáBqzáBmzáBizáBezáBazáB]záBYzáBUzáBQzáBMzáBIzáBEzáBAzáB=záB9záB5záB1záB-záB)záB%záB!záB
+záBzáBzáBzáB
+záB	záBzáBzáAúõÃAòõÃAêõÃAâõÃAÚõÃAÒõÃAÊõÃAÂõÃAºõÃA²õÃAªõÃA¢õÃAõÃAõÃAõÃAõÃAuë
+Aeë
+AUë
+AEë
+A5ë
+A%ë
+Aë
+Aë
+@ë×
+@Ë×
+@«×
+@×
+@W®@®?¯\)>œp€B}
+By
+Bu
+Bq
+Bm
+Bi
+Be
+Ba
+B]
+BY
+BU
+BQ
+BM
+BI
+BE
+BA
+B=
+B9
+B5
+B1
+B-
+B)
+B%
+B!
+B
+
+B
+B
+B
+B
+
+B	
+B
+B
+Aû
+=Aó
+=Aë
+=Aã
+=AÛ
+=AÓ
+=AË
+=AÃ
+=A»
+=A³
+=A«
+=A£
+=A
+=A
+=A
+=A
+=Av{Af{AV{AF{A6{A&{A{A{@ì(ö@Ì(ö@¬(ö@(ö@XQì@Qì?°£×>Â\B}\By\Bu\Bq\Bm\Bi\Be\Ba\B]\BY\BU\BQ\BM\BI\BE\BA\B=\B9\B5\B1\B-\B)\B%\B!\B
+\B\B\B\B
+\B	\B\B\Aû
+žAó
+žAë
+žAã
+žAÛ
+žAÓ
+žAË
+žAÃ
+žA»
+žA³
+žA«
+žA£
+žA
+žA
+žA
+žA
+žAv=qAf=qAV=qAF=qA6=qA&=qA=qA=q@ìzá@Ìzá@¬zá@zá@XõÂ@õÂ?±ë
+>Ç®B}ByBuBqBmBiBeBaB]BYBUBQBMBIBEBAB=B9B5B1B-B)B%B!B
+BBBB
+B	BBAû33Aó33Aë33Aã33AÛ33AÓ33AË33AÃ33A»33A³33A«33A£33A33A33A33A33AvffAfffAVffAFffA6ffA&ffAffAff@ìÌÍ@ÌÌÍ@¬ÌÍ@ÌÍ@Y@?³33>ÌÌÍB}£×By£×Bu£×Bq£×Bm£×Bi£×Be£×Ba£×B]£×BY£×BU£×BQ£×BM£×BI£×BE£×BA£×B=£×B9£×B5£×B1£×B-£×B)£×B%£×B!£×B
+£×B£×B£×B£×B
+£×B	£×B£×B£×AûG®AóG®AëG®AãG®AÛG®AÓG®AËG®AÃG®A»G®A³G®A«G®A£G®AG®AG®AG®AG®Av\Af\AV\AF\A6\A&\A\A\@í
+ž@Í
+ž@­
+ž@
+ž@Z=q@=q?Žzá>Ñë
+B}®By®Bu®Bq®Bm®Bi®Be®Ba®B]®BY®BU®BQ®BM®BI®BE®BA®B=®B9®B5®B1®B-®B)®B%®B!®B
+®B®B®B®B
+®B	®B®B®Aû\)Aó\)Aë\)Aã\)AÛ\)AÓ\)AË\)AÃ\)A»\)A³\)A«\)A£\)A\)A\)A\)A\)AvžRAfžRAVžRAFžRA6žRA&žRAžRAžR@íp€@Íp€@­p€@p€@ZáH@áH?µÂ>×
+=B}žRByžRBužRBqžRBmžRBižRBežRBažRB]žRBYžRBUžRBQžRBMžRBIžRBEžRBAžRB=žRB9žRB5žRB1žRB-žRB)žRB%žRB!žRB
+žRBžRBžRBžRB
+žRB	žRBžRBžRAûp€Aóp€Aëp€Aãp€AÛp€AÓp€AËp€AÃp€A»p€A³p€A«p€A£p€Ap€Ap€Ap€Ap€AváHAfáHAVáHAFáHA6áHA&áHAáHAáH@íÂ@ÍÂ@­Â@Â@[
+@
+?·
+>>Ü(öB}ÂByÂBuÂBqÂBmÂBiÂBeÂBaÂB]ÂBYÂBUÂBQÂBMÂBIÂBEÂBAÂB=ÂB9ÂB5ÂB1ÂB-ÂB)ÂB%ÂB!ÂB
+ÂBÂBÂBÂB
+ÂB	ÂBÂBÂAû
+Aó
+Aë
+Aã
+AÛ
+AÓ
+AË
+AÃ
+A»
+A³
+A«
+A£
+A
+A
+A
+A
+Aw
+=Ag
+=AW
+=AG
+=A7
+=A'
+=A
+=A
+=@î{@Î{@®{@{@\(ö@
+(ö?žQì>áG®B}ÌÍByÌÍBuÌÍBqÌÍBmÌÍBiÌÍBeÌÍBaÌÍB]ÌÍBYÌÍBUÌÍBQÌÍBMÌÍBIÌÍBEÌÍBAÌÍB=ÌÍB9ÌÍB5ÌÍB1ÌÍB-ÌÍB)ÌÍB%ÌÍB!ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB	ÌÍBÌÍBÌÍAûAóAëAãAÛAÓAËAÃA»A³A«A£AAAAAw33Ag33AW33AG33A733A'33A33A33@îff@Îff@®ff@ff@\ÌÍ@
+ÌÍ?¹>æffB}×
+By×
+Bu×
+Bq×
+Bm×
+Bi×
+Be×
+Ba×
+B]×
+BY×
+BU×
+BQ×
+BM×
+BI×
+BE×
+BA×
+B=×
+B9×
+B5×
+B1×
+B-×
+B)×
+B%×
+B!×
+B
+×
+B×
+B×
+B×
+B
+×
+B	×
+B×
+B×
+Aû®Aó®Aë®Aã®AÛ®AÓ®AË®AÃ®A»®A³®A«®A£®A®A®A®A®Aw\)Ag\)AW\)AG\)A7\)A'\)A\)A\)@îžR@ÎžR@®žR@žR@]p€@
+p€?ºáH>ë
+B}áHByáHBuáHBqáHBmáHBiáHBeáHBaáHB]áHBYáHBUáHBQáHBMáHBIáHBEáHBAáHB=áHB9áHB5áHB1áHB-áHB)áHB%áHB!áHB
+áHBáHBáHBáHB
+áHB	áHBáHBáHAûÂAóÂAëÂAãÂAÛÂAÓÂAËÂAÃÂA»ÂA³ÂA«ÂA£ÂAÂAÂAÂAÂAw
+Ag
+AW
+AG
+A7
+A'
+A
+A
+@ï
+=@Ï
+=@¯
+=@
+=@^{@
+{?Œ(ö>ð£×B}ë
+Byë
+Buë
+Bqë
+Bmë
+Bië
+Beë
+Baë
+B]ë
+BYë
+BUë
+BQë
+BMë
+BIë
+BEë
+BAë
+B=ë
+B9ë
+B5ë
+B1ë
+B-ë
+B)ë
+B%ë
+B!ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B	ë
+Bë
+Bë
+Aû×
+Aó×
+Aë×
+Aã×
+AÛ×
+AÓ×
+AË×
+AÃ×
+A»×
+A³×
+A«×
+A£×
+A×
+A×
+A×
+A×
+Aw®Ag®AW®AG®A7®A'®A®A®@ï\)@Ï\)@¯\)@\)@^žR@
+žR?œp€>õÂB}õÃByõÃBuõÃBqõÃBmõÃBiõÃBeõÃBaõÃB]õÃBYõÃBUõÃBQõÃBMõÃBIõÃBEõÃBAõÃB=õÃB9õÃB5õÃB1õÃB-õÃB)õÃB%õÃB!õÃB
+õÃBõÃBõÃBõÃB
+õÃB	õÃBõÃBõÃAûë
+Aóë
+Aëë
+Aãë
+AÛë
+AÓë
+AËë
+AÃë
+A»ë
+A³ë
+A«ë
+A£ë
+Aë
+Aë
+Aë
+Aë
+Aw×
+Ag×
+AW×
+AG×
+A7×
+A'×
+A×
+A×
+@ï®@Ï®@¯®@®@_\)@\)?ŸžR>úáHB~  Bz  Bv  Br  Bn  Bj  Bf  Bb  B^  BZ  BV  BR  BN  BJ  BF  BB  B>  B:  B6  B2  B.  B*  B&  B"  B
+  B  B  B  B  B
+  B  B  Aü  Aô  Aì  Aä  AÜ  AÔ  AÌ  AÄ  AŒ  AŽ  A¬  A€  A  A  A  A  Ax  Ah  AX  AH  A8  A(  A  A  @ð  @Ð  @°  @  @`  @   ?À  ?   B~
+=Bz
+=Bv
+=Br
+=Bn
+=Bj
+=Bf
+=Bb
+=B^
+=BZ
+=BV
+=BR
+=BN
+=BJ
+=BF
+=BB
+=B>
+=B:
+=B6
+=B2
+=B.
+=B*
+=B&
+=B"
+=B
+
+=B
+=B
+=B
+=B
+=B
+
+=B
+=B
+=Aü{Aô{Aì{Aä{AÜ{AÔ{AÌ{AÄ{AŒ{AŽ{A¬{A€{A{A{A{A{Ax(öAh(öAX(öAH(öA8(öA((öA(öA(ö@ðQì@ÐQì@°Qì@Qì@`£×@ £×?ÁG®?\B~{Bz{Bv{Br{Bn{Bj{Bf{Bb{B^{BZ{BV{BR{BN{BJ{BF{BB{B>{B:{B6{B2{B.{B*{B&{B"{B
+{B{B{B{B{B
+{B{B{Aü(öAô(öAì(öAä(öAÜ(öAÔ(öAÌ(öAÄ(öAŒ(öAŽ(öA¬(öA€(öA(öA(öA(öA(öAxQìAhQìAXQìAHQìA8QìA(QìAQìAQì@ð£×@Ð£×@°£×@£×@aG®@!G®?Â\?
+žB~
+žBz
+žBv
+žBr
+žBn
+žBj
+žBf
+žBb
+žB^
+žBZ
+žBV
+žBR
+žBN
+žBJ
+žBF
+žBB
+žB>
+žB:
+žB6
+žB2
+žB.
+žB*
+žB&
+žB"
+žB
+
+žB
+žB
+žB
+žB
+žB
+
+žB
+žB
+žAü=qAô=qAì=qAä=qAÜ=qAÔ=qAÌ=qAÄ=qAŒ=qAŽ=qA¬=qA€=qA=qA=qA=qA=qAxzáAhzáAXzáAHzáA8záA(záAzáAzá@ðõÂ@ÐõÂ@°õÂ@õÂ@aë
+@!ë
+?Ã×
+?®B~(öBz(öBv(öBr(öBn(öBj(öBf(öBb(öB^(öBZ(öBV(öBR(öBN(öBJ(öBF(öBB(öB>(öB:(öB6(öB2(öB.(öB*(öB&(öB"(öB
+(öB(öB(öB(öB(öB
+(öB(öB(öAüQìAôQìAìQìAäQìAÜQìAÔQìAÌQìAÄQìAŒQìAŽQìA¬QìA€QìAQìAQìAQìAQìAx£×Ah£×AX£×AH£×A8£×A(£×A£×A£×@ñG®@ÑG®@±G®@G®@b\@"\?Å
+ž?
+=qB~33Bz33Bv33Br33Bn33Bj33Bf33Bb33B^33BZ33BV33BR33BN33BJ33BF33BB33B>33B:33B633B233B.33B*33B&33B"33B
+33B33B33B33B33B
+33B33B33AüffAôffAìffAäffAÜffAÔffAÌffAÄffAŒffAŽffA¬ffA€ffAffAffAffAffAxÌÍAhÌÍAXÌÍAHÌÍA8ÌÍA(ÌÍAÌÍAÌÍ@ñ@Ñ@±@@c33@#33?Æff?
+ÌÍB~=qBz=qBv=qBr=qBn=qBj=qBf=qBb=qB^=qBZ=qBV=qBR=qBN=qBJ=qBF=qBB=qB>=qB:=qB6=qB2=qB.=qB*=qB&=qB"=qB
+=qB=qB=qB=qB=qB
+=qB=qB=qAüzáAôzáAìzáAäzáAÜzáAÔzáAÌzáAÄzáAŒzáAŽzáA¬záA€záAzáAzáAzáAzáAxõÃAhõÃAXõÃAHõÃA8õÃA(õÃAõÃAõÃ@ñë
+@Ñë
+@±ë
+@ë
+@c×
+@#×
+?Ç®?\)B~G®BzG®BvG®BrG®BnG®BjG®BfG®BbG®B^G®BZG®BVG®BRG®BNG®BJG®BFG®BBG®B>G®B:G®B6G®B2G®B.G®B*G®B&G®B"G®B
+G®BG®BG®BG®BG®B
+G®BG®BG®Aü\Aô\Aì\Aä\AÜ\AÔ\AÌ\AÄ\AŒ\AŽ\A¬\A€\A\A\A\A\Ay
+žAi
+žAY
+žAI
+žA9
+žA)
+žA
+žA	
+ž@ò=q@Ò=q@²=q@=q@dzá@$zá?ÈõÂ?ë
+B~QìBzQìBvQìBrQìBnQìBjQìBfQìBbQìB^QìBZQìBVQìBRQìBNQìBJQìBFQìBBQìB>QìB:QìB6QìB2QìB.QìB*QìB&QìB"QìB
+QìBQìBQìBQìBQìB
+QìBQìBQìAü£×Aô£×Aì£×Aä£×AÜ£×AÔ£×AÌ£×AÄ£×AŒ£×AŽ£×A¬£×A€£×A£×A£×A£×A£×AyG®AiG®AYG®AIG®A9G®A)G®AG®A	G®@ò\@Ò\@²\@\@e
+ž@%
+ž?Ê=p?záB~\)Bz\)Bv\)Br\)Bn\)Bj\)Bf\)Bb\)B^\)BZ\)BV\)BR\)BN\)BJ\)BF\)BB\)B>\)B:\)B6\)B2\)B.\)B*\)B&\)B"\)B
+\)B\)B\)B\)B\)B
+\)B\)B\)AüžRAôžRAìžRAäžRAÜžRAÔžRAÌžRAÄžRAŒžRAŽžRA¬žRA€žRAžRAžRAžRAžRAyp€Aip€AYp€AIp€A9p€A)p€Ap€A	p€@òáH@ÒáH@²áH@áH@eÂ@%Â?Ë
+
+?
+=B~ffBzffBvffBrffBnffBjffBfffBbffB^ffBZffBVffBRffBNffBJffBFffBBffB>ffB:ffB6ffB2ffB.ffB*ffB&ffB"ffB
+ffBffBffBffBffB
+ffBffBffAüÌÍAôÌÍAìÌÍAäÌÍAÜÌÍAÔÌÍAÌÌÍAÄÌÍAŒÌÍAŽÌÍA¬ÌÍA€ÌÍAÌÍAÌÍAÌÍAÌÍAyAiAYAIA9A)AA	@ó33@Ó33@³33@33@fff@&ff?ÌÌÍ?B~p€Bzp€Bvp€Brp€Bnp€Bjp€Bfp€Bbp€B^p€BZp€BVp€BRp€BNp€BJp€BFp€BBp€B>p€B:p€B6p€B2p€B.p€B*p€B&p€B"p€B
+p€Bp€Bp€Bp€Bp€B
+p€Bp€Bp€AüáHAôáHAìáHAäáHAÜáHAÔáHAÌáHAÄáHAŒáHAŽáHA¬áHA€áHAáHAáHAáHAáHAyÂAiÂAYÂAIÂA9ÂA)ÂAÂA	Â@ó
+@Ó
+@³
+@
+@g
+>@'
+>?Î{?
+(öB~záBzzáBvzáBrzáBnzáBjzáBfzáBbzáB^záBZzáBVzáBRzáBNzáBJzáBFzáBBzáB>záB:záB6záB2záB.záB*záB&záB"záB
+záBzáBzáBzáBzáB
+záBzáBzáAüõÃAôõÃAìõÃAäõÃAÜõÃAÔõÃAÌõÃAÄõÃAŒõÃAŽõÃA¬õÃA€õÃAõÃAõÃAõÃAõÃAyë
+Aië
+AYë
+AIë
+A9ë
+A)ë
+Aë
+A	ë
+@ó×
+@Ó×
+@³×
+@×
+@g®@'®?Ï\)?
+žRB~
+Bz
+Bv
+Br
+Bn
+Bj
+Bf
+Bb
+B^
+BZ
+BV
+BR
+BN
+BJ
+BF
+BB
+B>
+B:
+B6
+B2
+B.
+B*
+B&
+B"
+B
+
+B
+B
+B
+B
+B
+
+B
+B
+Aý
+=Aõ
+=Aí
+=Aå
+=AÝ
+=AÕ
+=AÍ
+=AÅ
+=Aœ
+=Aµ
+=A­
+=A¥
+=A
+=A
+=A
+=A
+
+=Az{Aj{AZ{AJ{A:{A*{A{A
+{@ô(ö@Ô(ö@Ž(ö@(ö@hQì@(Qì?Ð£×?!G®                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B| XB{Ýf¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÔB{äþBy	¬Bv5@¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|
+OB{óyBy(Bv<ÖBsaBp¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|+ÊB|õBy&£BvKQBsp Bp®Bm¹\Bjäð¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|:FB|pBy5
+BvYÍBs~{Bp£)BmÇ×BjìBh4Be<È¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|HÁB|
+ëByCBvhHBsöBp±¥BmÖSBjûBh¯BeD^Bbi
+B_ ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|W<B|-gByRBvvÃBsrBpÀ BmäÎBk	}Bh.+BeRÙBbwB_6B\ÀäBYìx¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|e·B|;âBy`Bv
+?Bs©íBpÎBmóJBkøBh<ŠBeaUBbB_ª±B\Ï_BYôBWŒBTDP¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|t3B|J]Byo
+BvºBsžhBpÝBnÅBk&sBhK"BeoÐBb~B_¹-B\ÝÛBZBW'7BTKæBQpBN(¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|®B|XØBy}Bv¢5BsÆãBpëBn@Bk4ïBhYBe~KBb¢úB_ÇšB\ìVBZBW5³BTZaBQBN£ŸBKÈlBHô ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|)B|gTByBv°°BsÕ_Bpú
+Bn
+»BkCjBhhBeÇBb±uB_Ö#B\úÒBZBWD.BThÝBQBN²9BKÖçBHûBF DBCKØ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|¥B|uÑByBv¿.BsãÜBqBn-9BkQçBhvBeDBb¿òB_ä¡B]	OBZ-ýBWR¬BTwZBQBNÀ·BKåeBI
+BF.ÁBCSpB@x
+B=£²¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|®B|LByšûBvÍ©BsòWBqBn;ŽBk`bBh
+Be©¿BbÎmB_ó
+B]ÊBZ<yBWa'BT
+ÕBQªBNÏ2BKóàBIBF==BCaëB@B=«HB:ÏöB7û¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ŒB|ÇBy·uBvÜ$Bt ÓBq%BnJ/BknÞBhBež:BbÜéB`B]&EBZJôBWo¢BTQBQžÿBNÝ­BL\BI'
+BFKžBCpgB@B=¹ÃB:ÞqB8 B5'ÎB2Sb¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ËB|¡BByÅñBvêBtMBq3üBnX«Bk}YBh¢BeÆ¶BbëdB`B]4ÁBZYoBW~
+BT¢ÌBQÇzBNì)BL×BI5
+BFZ4BC~âB@£B=È?B:ìíB8B56IB2ZøB/ŠB,«:¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÙB|¯ŸByÔlBvùBt
+ÉBqBwBng%BkÔBh°BeÕ1BbùßB`
+B]C<BZgêBWBT±GBQÕõBNú€BLRBIDBFh¯BC]B@²
+B=ÖºB:ûhB8 B5DÅB2isB/!B,²ÐB)×~B'¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|è
+B|Ÿ9ByâçBwBt,DBqPòBnu¡BkOBhŸýBeã¬Bc[B`-	B]Q·BZvfBWBT¿ÂBQäqBO	BL-ÍBIR|BFw*BCÙB@ÀB=å5B;	äB8.B5S@B2wïB/B,ÁKB)åùB'
+šB$/VB!Zê¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|öB|ÌµByñcBwBt:¿Bq_nBn
+BkšÊBhÍyBeò'BcÕB`;B]`3BZáBW©BTÎ>BQòìBOBL<IBI`÷BF
+¥BCªTB@ÏB=ó±B;_B8=
+B5aŒB2jB/«B,ÏÇB)ôuB'#B$=ÑB!bB
+.B²Â¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}B|Û0ByÿÞBw$BtI;BqméBnBk·FBhÛôBf ¢Bc%QB`IÿB]n­BZ\BWž
+BTÜ¹BRgBO&BLJÄBIorBF!BCžÏB@Ý}B>,B;&ÚB8KB5p7B2åB/¹B,ÞBB*ðB''B$LMB!pûB
+©BºXBßB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}B|é«BzYBw3BtW¶Bq|eBn¡BkÅÁBhêoBf
+Bc3ÌB`XzB]})BZ¡×BWÆ
+BTë4BRãBO4BLY?BI}îBF¢BCÇJB@ëùB>§B;5UB8ZB5~²B2£aB/ÈB,ìœB*lB'6B$ZÈB!wB
+€%BÈÓBíB0B6ÞBbr¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}!úB|ø&Bz
+ÕBwABtf1BqàBn¯BkÔ=BhøëBf
+BcBGB`föB]€BZ°RBWÕBTù¯BR
+]BOC
+BLg»BIiBF±BCÕÆB@útB>"B;CÑB8hB5-B2±ÜB/ÖB,û9B*çB'DB$iDB!òB
+² B×OBûýB «BEYBjB
+¶B
+ºJ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}0uB}€Bz+RBwP Btt¯Bq]BnŸ
+BkâºBihBf,BcPÅB`usB]!BZŸÐBWã~BU,BR,ÛBOQBLv7BIæBF¿BCäCBAñB>- B;RNB8vüB5«B2ÀYB/åB-	¶B*.dB'SB$wÁB!oB
+Á
+BåÌB
+zB/)BS×Bx
+B
+3B
+ÁâBæB$¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}>ñB}Bz9ÍBw^|Bt*Bq§ØBnÌBkñ5BiãBf:Bc_@B`ïB]šBZÍKBWñùBUšBR;VBO`BL³BI©aBFÎBCòŸBAmB><B;`ÉB8
+xB5ª&B2ÎÔB/óB-1B*<ßB'aB$<B!ªëB
+ÏBôGBöB=€BbRBB
+«¯B
+Ð]Bõ
+BºB>hAþÓø¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}MlB}#BzHIBwl÷Bt¥Bq¶TBnÛBkÿ°Bi$_BfI
+Bcm»B`jB]·BZÛÇBX uBU%#BRIÑBOnBL.BI·ÜBFÜBD9BA%çB>JB;oEB8óB5ž¡B2ÝPB0þB-&¬B*K[B'p	B$·B!¹fB
+ÞB
+ÃB'qBLBpÎB|B
+º*B
+ÞÙBB(5BLãAþã#Aù,Aóš¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}[çB}2BzVÄBw{rBt !BqÄÏBné}Bl,Bi2ÚBfWBc|7B` åB]ÅBZêBBXðBU3BRXMBO|ûBL¡©BIÆXBFëBDŽBA4cB>YB;}¿B8¢nB5Ç
+B2ëËB0yB-5(B*YÖB'~B$£3B!ÇáB
+ìB
+>B5ìBZBIB£÷B
+ÈŠB
+íTBB6±B[_Aÿ AùIwAóÔAíÜ1Aè3X¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}jbB}@Bze?BwîBt®BqÓJBn÷ùBl
+§BiAUBffBc²B`¯`B]ÔBZøœBX
+kBUBBRfÈBOwBL°%BIÔÓBFùBD
+0BABÞB>gB;;B8°éB5ÕB2úFB0
+õB-C£B*hQB' B$±®B!Ö\B
+û
+B
+¹BDgBiBÄB²sB
+×!B
+ûÏB ~BE,BiÚAÿ
+AùfnAó¯ËAíù'AèBAâáAÜã¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}xÝB}O
+BzsºBwhBtœBqáÆBotBl+"BiOÑBftBc-B`œÜB]âB[8BX+çBUPBRuCBOòBLŸ BIãOBGýBD,«BAQYB>vB;¶B8¿dB5äB3ÁB0-oB-R
+B*vÍB'{B$À)B!äØB	B
+.4BRãBwB?BÀîB
+åB
+
+KB.ùBS§BxVAÿ:AùeAóÌÂAî
+Aè_{Aâš×AÜò4A×;AÑž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}YB}]Bz5BwŠäBtËBqð@BoïBl9Bi^LBfúBc§©B`ÌWB]ñB[ŽBX:bBU_BR¿BOšmBLÍBIñÊBGxBD;'BA_ÕB>B;©1B8ÍàB5òB3<B0;ëB-`B*
+GB'©öB$Î¥B!óSBB
+<°Ba^B
+Bª»BÏiB
+ôB
+ÆB=tBb#BÑAÿVÿAù [Aóé·Aî3Aè|rAâÅÎAÝ+A×XAÑ¡äAËëAAÆBh¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÔB}lBz±Bwµ_BtÚ
+BqþŒBo#jBlHBilÇBfvBc¶$B`ÚÒB]ÿB[$/BXHÝBUmBR:BO¶èBLÛBJ EBG$óBDI¢BAnPB>ÿB;·­B8Ü[B6	B3%žB0JfB-oB*ÃB'žqB$ÝB"ÎB&}B
+K+BoÙBB¹6BÝäBB
+'ABKïBpBLAÿsõAùœQAô®AîP
+AègAââÅAÝ,"A×u~AÑŸÛAÌ7AÆQAÀñAºò¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}€OB}z~Bz,BwÃÚBtèBr
+7Bo1åBlVBi{BBfðBcÄB`éNB^
+üB[2ªBXWYBU|BR µBOÅdBLêBJÀBG3oBDX
+BA|ËB>¡zB;Æ(B8ê×B6
+B343B0XáB-}B*¢>B'ÆìB$ëB"IB4÷B
+YŠB~UB£BÇ±Bì`BB
+5ŒBZkBB£ÇAÿëAùÚHAô#¥AîmAè¶^Aâÿ»AÝIA×uAÑÛÒAÌ%.AÆnAÀ·çA»DAµJ¡A¯¡È¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}²ÊB}ùBz­§BwÒVBt÷Br²Bo@aBleBiœBf®lBcÓB`÷ÈB^
+wB[A&BXeÔBUBR¯1BOÓßBLøBJ
+<BGAêBDfBAGB>¯õB;Ô£B8ùRB6
+ B3B¯B0g]B-
+B*°¹B'ÕhB$úB"
+ÄBCsB
+h!BÏB±~BÖ-BúÛBB
+D8BhæBB²CAÿ­âAù÷?Aô@AîøAèÓUAã
+±AÝfA×¯kAÑøÇAÌB%AÆAÀÔÞA»
+;AµgA¯°ôA©úQA€Qx¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÁFB}vBzŒ$BwàÓBuBr*0BoNÞBlsBi;BfŒéBcáBaFB^*ôB[O¢BXtQBU BRœ®BOâ\BM
+BJ+¹BGPgBDuBAÄB>ŸrB;ã!B9ÏB6,}B3Q,B0uÚB-B*¿7B'ãåB%B"-BBQðB
+vBMB¿ûBä©B	XB.B
+RµBwcBBÀÀAÿÊÜAú9Aô]AîŠóAèðOAã9¬AÝ	A×ÌeAÒÂAÌ_AÆš{AÀñÙA»;6AµA¯ÍïAªKA€`šAªA,¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÏÁB}¥òBzÊ BwïNBuüBr8«Bo]YBlBiŠ¶BfËdBcðBaÁB^9oB[^
+BXÌBU§zBRÌ)BOðØBMBJ:4BG^ãBDBAš?B>ÌîB;ñB9JB6:ùB3_§B0UB-©B*Í²B'òaB%B";œB`kB
+
+B©ÈBÎvBó%BÓB<B
+a0B
+ßBªBÏ;AÿçÓAú10AôzAîÃéAé
+FAãV£AÝÿA×é\AÒ2¹AÌ|AÆÅrAÁÏA»X+Aµ¡A¯êæAª4BA€}AÆûAXAYµA°Ü¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}Þ=B}ŽmBzÙBwýÊBu"xBrG&BokÔBlBiµ1BfÙàBcþBa#<B^GëB[lBXGBUµöBRÚ€BOÿRBM$BJH°BGm^BD
+BA¶»B>ÛiB< B9$ÆB6ItB3n"B0ÑB-·B*Ü-B( ÜB%%B"J9BnçB
+BžCBÜòB B&NBJýB
+o«BYB¹BÝ·B eAúN&AôAîàßAé*=AãsAÝŒöAØSAÒO¯AÌ
+AÆâiAÁ+ÅA»u"AµŸA°ÛAªQ9A€AãòA-OAv«AÀA	eA`¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ìžB}ÂèBzçBx
+EBu0óBrU¢BozPBlþBiÃ¬Bfè[Bd
+	Ba1žB^VfB[{BXÃBUÄqBRéBP
+ÎBM2|BJW*BG{ÙBD BAÅ6B>éäB<B93AB6WïB3|B0¡LB-ÅúB*ê©B(WB%4B"XŽB}bB
+¢BÆ¿BëmBB4ÊBYxB
+~&B¢ÕBÇBì1B àAúk
+AôŽyAîýÖAéG3AãAÝÙíAØ#IAÒlŠAÌ¶AÆÿ_AÁHŒA»AµÛuA°$ÒAªn/A€·A éAJFA¢AÜÿA&[AožAyr)An y¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}û3B}ÑdBzöBxÀBu?oBrd
+BoËBl­zBiÒ(BföÖBdBa@3B^dáB[BX®>BUÒìBR÷BP
+IBM@÷BJeŠBGTBD¯BAÓ±B>ø`B<
+B9AŒB6fkB3B0¯ÇB-ÔvB*ù$B(
+ÒB%BB"g/BÝB
+°BÕ:BùéB
+BCEBgóB
+¢B±PBÕþBú­B [AúAôÑpAïÍAéd)Aã­AÝöãAØ@?AÒAÌÒùAÇ
+VAÁe³A»¯AµølA°AÉAª%A€ÔA
+ßAg;A°AùöACRA¯Ay¬An>ÑAbÑAWÛ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~	®B}ßßB{Bx)<BuMêBrrBoGBl»õBià£BgRBd* BaN®B^s\B[
+BXŒ¹BUáhBSBP*ÄBMOsBJt!BGÏBDœ~BAâ,B?ÚB<+B9P8B6tæB3B0ŸCB-âñB+B(,NB%PüB"uªBYB
+¿BãµBdB-BQÁBvoB
+
+B¿ËBäzB	(B -ÖAú¥
+AôîgAï7ÃAé AãÊ}AÞÙAØ]6AÒŠAÌïïAÇ9MAÁ©A»ÌA¶cA°^¿Aªš
+A€ñyA:ÕA2AÍAëA`IA©ŠAyæAnxŸAc
+yAW3AL0íA@ß=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~*B}îZB{Bx7·Bu\eBrBo¥ÂBlÊpBiïBgÍBd8{Ba]*B^ØB[ŠBXË4BUïãBSBP9@BM]îBJBG§KBDËùBAð§B?VB<:B9^²B6aB3šB0ÌŸB-ñlB+B(:ÉB%_wB"&BšÔB
+ÍBò1BßB;B`<BêB
+©BÎGBòõB£B <RAúÂAõ
+]AïTºAéAãçsAÞ0ÐAØz-AÒÃAÍ
+æAÇVCAÁA»èýA¶2YA°{¶AªÅA¥oAWÌA¡)Aê
+A3âA}?AÆAzñAn²«AcEeAWØ ALjÛA@ýA5OA*>¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~&¥B}üÕB{!BxF2BujàBrBoŽ=BlØìBiýBg"HBdF÷Bak¥B^SB[µBXÙ°BUþ^BS#
+BPG»BMliBJBGµÆBDÚtBAÿ#B?#ÑB<HB9m.B6ÜB3¶B0Û9B-ÿèB+$B(IDB%móB"¡B·OB
+ÛþB ¬B%ZBJ	Bn·BeB
+žBÜÂBpB&B JÍAúÞ÷Aõ(TAïq±Aé»
+AäjAÞMÇAØ#AÒàAÍ)ÝAÇs9AÁŒAŒóA¶OOA°­Aªâ	A¥+fAtÃAŸA|APÙA5AãAzYÞAnìAcSAX
+AL€ÇAA7A5Ê=A*\÷A
+ï±A¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~5 B~
+QB{/ÿBxT­Buy\Br
+BoÂžBlçgBj
+Bg0ÄBdUrBaz B^ÏB[Ã}BXè+BV
+ÚBS1BPV6BMzäBJBGÄABDèðBB
+B?2LB<VûB9{©B6 WB3ÅB0éŽB.bB+3B(WÀB%|nB"¡
+BÅËB
+êyB'B3ÖBXB}2B¡áB
+ÆBë=BìB4B YHAúûíAõEJAï§AéØAä!aAÞjœAØŽAÒýwAÍFÓAÇ0AÁÙAŒ"éA¶lFA°µ£AªþÿA¥H]A¹AÛA$sAmÏA·,A AzËAo&
+Ac¹@AXKúALÞµAAqoA6)A*äA)AŒYAO@ùúÆ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~CB~ÌB{>zBxc)Bu×Br¬
+BoÑ4BlõâBjBg??BdcíBaB^­JB[ÑøBXö§BVUBS@BPd²BM`BJ®BGÒŒBD÷kBB
+B?@ÈB<evB9$B6®ÓB3ÓB0ø/B.
+ÞB+AB(f:B%éB"¯BÔFB
+øôB
+£BBQBfÿB®B°\B
+Õ
+Bù¹B
+gBCB gÄAûäAõbAAï«AéôúAä>WAÞŽAØÑAÓmAÍcÊAÇ­'AÁöAŒ?àA¶=A°ÒA«öA¥eSA®¯Aø
+AAiAÆAÔ#A
+AzÍžAo`rAcó-AX
+çAM¢AA«\A6>A*ÐÑAcAöFA@ú7v@ã\ë@Ì¹¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~RB~(IB{LøBxqŠBuTBr»Boß±Bm_Bj)BgMŒBdrjBaB^»ÇB[àvBY$BV)ÒBSNBPs/BMÝBJŒBGá:BEèBB*B?OEB<sóB9¢B6œPB3áþB1­B.+[B+P	B(tžB%fB"ŸBâÃB
+rB, BPÎBu}B+BŸÙB
+ãB	6B,äBQB vAAû5ßAõ;AïÈAêõAä[QAÞ€®AØî
+AÓ7hAÍÅAÇÊ!AÂ~AŒ\ÛA¶Š7A°ïA«8ñA¥MAËªAA^cA§ÁAñ
+A:zA{­AohAd-"AX¿ÜAMRAAåQA6x
+A+
+ÆAA0;AÂõ@ú«a@ãÐÕ@ÌöJ@¶¿@x^¿  ¿  ¿  ¿  ¿  ¿  B~`B~6ÅB{[sBx!Bu€ÐBrÉ~Boî,BmÛBj7Bg\7BdæBa¥B^ÊBB[îñBYBV8NBS\üBPªBMŠYBJËBGïµBEdBB9B?]ÀB<nB9§
+B6ËËB3ðzB1(B.9ÖB+^
+B(3B%§áB"ÌBñ>B
+ìB:B_JBøBšŠBÍUB
+òB	±B;`B`B ŒAûRÕAõ2AïåAê.ëAäxHAÞÁ¥AÙ
+AÓT^AÍ»AÇçAÂ0uAŒyÑA¶Ã.A±
+A«UçA¥DAè¡A1ýA{ZAÄ·AAWqA{AAoÔUAdgAXùÊAMAB>A6±ùA+D³A×nAj(Aüã@û:@äD¯@Íj$@¶@µ@Ú@dnD¿  ¿  ¿  ¿  B~o
+B~E?B{iîBxBu³KBr×ùBoüšBm!VBjFBgj³BdaBaŽB^ØŸB[ýlBY"BVFÉBSkwBP&BMŽÔBJÙBGþ1BE"ßBBGB?l<B<êB9µB6ÚFB3þõB1#£B.HRB+m B(®B%¶]B"Û
+Bÿ¹B
+$hBIBmÄBsB·"BÛÐB
+ ~B	%-BIÛBnB 8AûoÌAõ¹)Að
+AêKâAä?AÞÞAÙ'øAÓqUAÍº±AÈAÂMkAŒÈA¶à%A±)A«rÞA¥Œ;A ANôAQAá­A+
+AtgA{{ApBAd üAY3·AMÆqABY,A6ëæA+~ A [A€A	6Ð@û@äž@ÍÝþ@·s@ (è@N]@dç€@72@	ëÊ¿  ¿  B~}B~S»B{xiBxBuÁÆBræuBp
+#Bm/ÑBjTBgy.BdÜBaÂB^ç9B\
+çBY0BVUDBSyòBP¡BMÃOBJçþBH
+¬BE1ZBBV	B?z·B<eB9ÄB6èÂB4
+pB12
+B.VÍB+{{B( *B%ÄØB"éB 5B
+2ãBWB|@B îBÅBêKB
+úB	3šBXVB}B ¡³AûÂAõÖAð|AêhÙAä²5AÞûAÙDïAÓKAÍ×šAÈ!AÂjaAŒ³ŸA¶ýA±FxA«ÕA¥Ù1A "AkëAµGAþ€AHA]A{µtApH/AdÚéAYm€AN ^ABA7%ÓA+žA KHAÞA	pœ@üï@å,d@ÎQÙ@·wM@ Â@Â7@eÏX@8A@
+e)?¹`%?=¥@B~
+B~b7B{äBx«BuÐABrôïBpBm>MBjbûBg©Bd¬XBaÑB^õŽB\cBY?BVc¿BSnBP­
+BMÑÊBJöyBH'BE?ÖBBdB?2B<­áB9ÒB6÷=B4ìB1@B.eHB+öB(®¥B%ÓSB"øB 
+°B
+A^Bf
+B»B¯iBÔBøÆB
+
+tB	B#BfÒBB °.Aû©¹AõóAð<rAê
+ÏAäÏ,AßAÙaåAÓ«BAÍôAÈ=ûAÂXAŒÐµA·A±cnA«¬ËA¥ö(A ?
+AáAÒ>AAd÷A®TA{ïaAp
+Ae×AY§AN:KABÍA7_ÀA+ò{A 
+5AðA	ªª@üzÉ@å >@ÎÅ³@·ë(@¡@6@f·
+@9õ@
+LÞ?»/??Ÿ=
+f$¿  B{{BxºBuÞŒBskBp(BmLÇBjqvBg%BdºÓBaßB_0B\(ÞBYMBVr;BSéBP»BMàFBKôBH)¢BENQBBrÿB?®B<Œ\B9á
+B7¹B4*gB1OB.sÄB+rB(œ B%áÎB#}B ++B
+OÚBtB6BœåBâBAB
++ðB	PBuLBûB ŸªAûÆ¯Aö
+AðYiAê¢ÅAäì"Aß5AÙ~ÜAÓÈ9AÎAÈZòAÂ€OAŒí«A·7A±eA«ÉÁAŠ
+A \{A¥ØAï5A8AîAËKA|)OApŒ	AeNÃAYá~ANt9ACóA7­A,,hA ¿"AQÝA	ä@üî£@æ@Ï9@ž_@¡w@©ì@gÁ@9é©@
+4?Œþö?C)=ES?;ò¿
+¿  ¿  BuæSBsçBp6Bm[CBjñBg€BdÉNBaíýB_«B\7YBY\BV¶BS¥dBPÊBMîÁBKoBH8
+BE\ÌBBzB?Š)B<Ê×B9ïB74B48âB1]B.?B+ŠíB(ËB%ðJB#øB 9ŠB
+^UBB§²BÌ`BñBœB
+:kB	_BÈBšvB Í$AûãŠAö-Aðv_Aê¿ŒAå	AßRuAÙÒAÓå/AÎ.AÈwéAÂÁEAœ
+¢A·SÿA±[A«æžAŠ0A yqAÂÎA
++AUAåAèAA|c<ApõöAe±AZkAN®%AC@àA7ÓA,fUA ùAÊA
+
+@ýb}@æò@Ï­g@žÒÜ@¡øQ@
+Æ@hu@:Ñ^@
+
+G?ŸÎ_?FÈa=@Y<°ø¿  ¿  ¿  ¿  Bp>,BmiÀBjmBg³
+Bd×ÊBaüxB_!'B\EÖBYjBV2BS³áBPØBMý=BK!ìBHFBEkHBB÷B?Ž¥B<ÙSB9þB7"°B4G_B1l
+B.»B+µjB(ÚB%þÆB##uB H#B
+lÑBB¶.BÚÜBÿB$9B
+HçB	mBDB¶òB Û¡Aü AöIûAðXAêÜµAå&AßonAÙžËAÔ'AÎKAÈáAÂÞ>Aœ'A·p÷A±ºTA¬±AŠM
+A jAßÇA)#ArA»ÝA:A|-Aq/çAeÂ¢AZU\ANèACzÑA8
+A, FA!3AÅ»A
+Xu@ýÖ_@æûÔ@Ð!I@¹FŸ@¢l3@š@in9@;¹"@
+?Àç?Jgr=º=2¿  ¿  ¿  ¿  ¿  ¿  BjBgÁBdæEBb
+ôB_/¢B\TPBYxÿBV®BSÂ\BPç
+BN
+¹BK0gBHUBEyÄBBrB?Ã B<çÏB:
+}B71+B4UÚB1zB.7B+ÃåB(èB&
+BB#1ðB VB
+{MBûBÄ©BéWBB2ŽB
+WcB	|B ¿BÅnB ê
+Aü
+AöfòAð°OAêù«AåCAßeAÙÕÁAÔ
+AÎh{AÈ±×AÂû4AœDA·îA±×KA¬ §AŠjA ³aAüœAFAwAØÓA"0A|×AqiÔAeüAZIAO"ACŽŸA8GyA,Ú3A!líAÿšA
+c@þJ:@ço¯@Ð#@¹º@¢à
+@@jUî@< Ö@ë¿?ÂmQ?ND=¹F=L1¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BdíÜBboB_>
+B\bÌBYzBV¬(BSÐ×BPõBN4BK>âBHcBE?BB¬íB?ÑB<öJB:øB7?§B4dUB1B.­²B+Ò`B(÷B&œB#@kB eB
+ÈB®vBÓ%B÷ÓB
+BA/B
+eÞB	B¯;BÓéB øAü:AöèAðÍEAë¢Aå_ÿAß©[AÙòžAÔ<AÎ
+qAÈÎÎAÃ+AœaA·ªäA±ôAA¬=AŠûA ÐWAŽAcA¬mAõÊA?'A}Aq£ÂAf6|AZÉ6AO[ðACî«A8fA- A!ŠÛA9A
+ÌO@þŸ@çã@Ñý@º.r@£Sç@y\@k=¢@=@Ós?Ä<¹?Q¥=Ö
+Ê=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B_EŽB\qGBYõBVº€BSßRBQ BN(¯BKM^BHr
+BEºBB»iB?àB=ÅB:)tB7N"B4rÐB1B.Œ-B+àÛB)B&*8B#NçB sB
+CBŒòBá BNB*ýBO«B
+tYB	Bœ¶BâdBAüWAö ßAðê;Aë3Aå|õAßÆRAÚ¯AÔY
+AÎ¢hAÈëÅAÃ5!Aœ~~A·ÇÛA²7A¬ZAŠ£ñA íNA6«AAÉdAÁA\
+A}JôAqÝ®AfpiA[$AOÞAD(A8»RA-N
+A!àÈAsA
+=@ÿ1î@èWc@Ñ|Ø@º¢L@£ÇÁ@í6@l%V@>p?@»(?Æ
+!?UCç=ó|V=ü§¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BYBVÉBSíÍBQ|BN7*BK[ØBHBE¥6BBÉäB?îB=AB:7ïB7\B4LB1¥úB.ÊšB+ïWB)B&8³B#]bB B
+Š¿BËmBðBÊB9xB^&B
+ÕB	§BÌ1BðßBAütyAöœÕAñ2AëPAåëAßãHAÚ,¥AÔvAÎ¿_AÉ»AÃRAœuA·äÑA²..A¬wAŠÀçA¡
+DAS¡AþAæ[A/·AyA}áArAfªVA[=AOÏËADbA8õ@A-úA"ŽA­oA
+@*@ÿ¥È@èË=@Ñð²@»'@€;@a@m
+
+@?Wô@¢Ü?ÇÛ?Xâž>9r=Œó5¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BSõeBQ øBNEŠBKjUBHBE³±BBØ`B?ýB=!œB:FkB7kB4ÈB1ŽvB.Ù%B+ýÓB)"B&G0B#kÞB B
+µ;BÙéBþB#FBGôBl£B
+QB	µÿBÚ®Bÿ\B$
+AüqAöÚÎAñ$+AëmAå¶äAà AAÚIAÔúAÎÜWAÉ%ŽAÃoAœžmAžÊA²K'A¬AŠÝàA¡'=ApA¹öASAL°A
+A}ŸÓArQAfäGA[wAP	ŒADvA9/1A-ÁìA"TŠAç`A
+zA 
+Õ@é?@Òd@»	@€¯~@Ôó@môÐ@@?ž@¡?É«?\Ê>µž=ÙéÁ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BNM=BKxÐBH~BEÂ-BBæÛB@
+B=08B:TçB7yB4CB1ÂòB.ç B,
+NB)0ýB&U«B#zYB B
+Ã¶BèdB
+B1ÁBVpB{
+B
+ÌB	Ä{Bé)B
+×B2Aü®hAö÷ÅAñA!Aë~AåÓÛAà
+7AÚfAÔ¯ñAÎùMAÉBªAÃAœÕdAž
+ÁA²h
+A¬±zAŠú×A¡D3AAÖíA IAiŠA³A}øÀArzAg
+5A[°ïAPC©ADÖdA9i
+A-ûØA"A!NA
+ŽA FÂ@é²ù@ÒØn@»ýã@¥#X@HÍ@nÜ@A'm@rV?Ëz|?` >%0þ=öàN¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BH¥BEÐšBBõVB@B=>³B:caB7B4¬¿B1ÑmB.öB,ÊB)?xB&d&B#ÕB ­B
+Ò1BöàBB@<BdëBB
+®HB	ÒöB÷€B
+SBAAüË_A÷ŒAñ^Aë§uAåðÑAà:.AÚAÔÌçAÏDAÉ_¡AÃšýAœòZAž;·A²
+A¬ÎqA§ÍA¡a*AªAóãA=@AAÏùA~2¬ArÅgAgX!A[êÜAP}AEQA9£
+A.5ÆA"ÈA[:A
+íõA °@ê&Ô@ÓLH@Œqœ@¥2@Œ§@oÄ8@B!@Z
+?ÍIå?c¿m>3¬E>	ëm¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BBüíB@(B=M.B:qÝB7B4»9B1ßèB/B,)EB)MóB&r¢B#PB »þB
+à­B[B*	BNžBsfBB
+ŒÃB	áqB B*ÎBO|AüèUA÷1±Añ{AëÄlAæ
+ÈAàW%AÚ AÔéÞAÏ3;AÉ|AÃÅôAŸQAžX­A²¢
+A¬ëgA§4ÄA¡~!AÇ}AÚAZ7A£AìðA~lArÿTAgA\$ÉAP·AEJ>A9ÜùA.o³A#mA(A
+'âA º@ê®@ÓÀ"@Œå@Š
+
+@0@p«í@BöÖ@AŸ?ÏN?g^>>B'>fŽ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B=TÅB:XB7¥B4ÉµB1îcB/B,7ÀB)\oB&
+B#¥ËB ÊzB
+ï(BÖB8
+B]3BáBŠB
+Ë>B	ïìBB9IB]øAýKA÷NšAñAëáaAæ*¿Aàt
+AÚœxAÕÕAÏP1AÉAÃâëAŸ,GAžu€A²¿A­]A§QºA¡AätA-ÑAw-AÀA
+	çA~ŠAs9AAgËüA\^¶APñqAE+A:åA.© A#<[AÏA
+aÏA ô@ë@Ô3ý@œYr@Š~æ@€\@q¡@CÞ@)s?Ðè·?jý>P¢Ó>&áû¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B7¬B4Ø1B1üßB/!B,F<B)jêB&B#ŽHB ØöB
+ý€B"SBGBk¯B^Bµ
+B
+ÙºB	þiB#BGÅBltAý"DA÷k¡AñŽýAëþZAæG·AàAÚÚqAÕ#ÎAÏm*AÉ¶AÃÿãAŸI@AžA²ÛùA­%VA§n³A¡žAlAJÉA&AÝA
+&ßA~àxAss2AhíA\§AQ+bAEŸ
+A:P×A.ãA#vKA	A
+ÁA.{@ëk@Ô§ß@œÍT@ŠòÉ@>@r{e@DÆO@7?Òž@?n#>_>5]C¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B2vB/0	B,T·B)yfB&B#ÂÂB çqB
+
+ B0ÎBU|Bz+BÙBÃB
+è6B
+
+äB1BVABzïAý?;A÷AñÑôAìQAæd­Aà®
+AÚ÷gAÕ@ÃAÏ!AÉÓ~AÄ
+ÚAŸf7Až¯A²øðA­BMA§©A¡ÕA
+cAg¿A±
+AúyA
+CÖAeAs­ Ah?ÚA\ÒAQeOAEø	A:ÄA/
+~A#°9ABóA
+Õ­Ahh@ëöE@Õº@ŸA/@§f£@@sc@E®@øì?Ô©?r:õ>mb>CØ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B,\NB)áB&¬B#Ñ>B õìB
+B?IBcøBŠB­TBÒB
+ö±B
+_B@BdŒBjAý\1A÷¥AñîëAì8GAæ€AàËAÛ]AÕ]ºAÏ§AÉðsAÄ9ÑAŸ-AžÌA³çA­_CA§š A¡ñýA;YA¶AÎAoA
+`ÌATSAsç
+AhyÇA]
+AQ<AF1öA:Ä±A/WkA#ê&A|àA
+A¢U@ìj@Õ@Ÿµ@§Ú~@ÿò@tJÎ@F·@à ?ÖW?uÙÅ>|€>RSÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B&Ž&B#ß¹B!gB
+)BMÄBrrB!B»ÐBà~B
+,B
+)ÛBNBs7BæAýy(A÷Â
+Aò
+áAìU>AæAàç÷AÛ1TAÕz±AÏÄ
+AÊ
+jAÄVÇAŸ #AžéA³2ÝA­|:A§ÅA¢óAXPA¡­Aë	A4fA
+}ÃA?At úAh³ŽA]FnAQÙ)AFkäA:þA/XA$$A¶ÍA
+IAÜB@ìÝù@Ön@¿(ã@šNX@sÌ@u2@G}k@ÈT?Ø&z?yx>
+Hv>`Ï¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B!
+þB
+7B\?BîB¥BÊJBîùB
+šB
+8VB]B³BŠaAý
+A÷ß{Aò(ØAìr5Aæ»AáîAÛNKAÕ§AÏáAÊ*aAÄsœAŸœA¹wA³OÓA­1A§âA¢+êAuGAŸ£A AQ]A
+¹AÈ,AtZçAhí¡A]\ARAF¥ÐA;8A/ËFA$^ AðºA
+uA/@íQÓ@ÖwH@¿œ@šÂ2@ç§@v7@He @°	?Ùõã?}i>>oJ\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bc×BjBŽBØÇBýuB
+"#B
+FÒBkB/BŽÝAý³A÷üsAòEÐAì-AæØAá!çAÛkCAÕŽ AÏýýAÊGYAÄ¶AŸÚA¹#oA³lÌA­¶)A§ÿ
+A¢HãA?AÛA$ùAnUA
+·²AAtØAi'A]ºMARMAFßÂA;r|A06A$ñA*¬A
+œfAP @íÅµ@Öë+@À@©6@[@wü@ILä@Í?ÛÅl?[=>Ä>>}Å€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B»¯BçBB
+ðB
+0B
+UMByûBªBÃYAýÐAøjAòbÇAì¬#AæõAá>ÝAÛ:AÕÑAÐóAÊdPAÄ­­AŸ÷	A¹@fA³ÃA­ÓAš
+|A¢eÙA¯5AøAAïALA
+Ô©A
+AtÎÅAiaA]ô:ARôAG¯A;¬iA0?$A$ÑÞAdA
+÷SA@î9@×_@Àz@©©ï@Ïc@wé°@J4@
+?ÝÕ?*Š>â> v¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BB
+?B
+cÈBwB­%BÑÓAýíAø6aAòœAìÉAçwAá[ÓAÛ¥0AÕîAÐ7êAÊGAÄÊ£A¿ A¹]]A³Š¹A­ðAš9sA¢ÏAÌ,AA^åAšCA
+ñA:üAu²AimA^.'ARÀáAGSA;æVA0yA%
+ËAA1@AÃú@î­j@×Òß@ÀøT@ª
+É@C>@xÑe@K
+M@
+g6?ßd=?ú>¢?
+>^¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+k_BòB» BàOAþ	ûAøSWAòŽAìæAç/mAáxÊAÛÂ'AÖ
+AÐTàAÊ=AÄçA¿0÷A¹zSA³Ã°A®
+
+AšViA¢ÆAé#A2A{ÜAÅ9AAWòAuBAiÕYA^hARúÏAGA< CA0²þA%EžAØsAk-Aýè@ï!D@ØF¹@Ál.@ª£@·@y¹@L@
+Nê?á3Š?
+Éx>©}*>œ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BÃ7BîÊAþ&ñAøpNAò¹«AíAçLdAáÁAÛß
+AÖ(zAÐq×AÊ»3AÅA¿MíA¹JA³à§A®*Ašs`A¢ŒœAAOvAÓAâ/A+AtéAu|AjGA^¢AS4»AGÇvA<Z1A0ìëA%¥A`A¥A7Õ@ï@Øº@Áà@«}@*ò@z Í@Lë¶@6?ã?á>°ºÍ>Ùb¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aþ6AøFAòÖ£Aí  Açi]Aá²¹AÛüAÖEsAÐÏAÊØ,AÅ!A¿jåA¹ŽBA³ýA®FüAšYA¢ÙµA#AloAµËAÿ(AH
+AáAu¶}AjI8A^ÛòASn­AHgA<!A1&ÜA%¹ALQAß
+AqÆ@ð	@Ù.u@ÂSë@«y_@Ô@{@MÓ{@ 
+c?äÒ?hk>·øñ>£¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AòåÏAí<öAçSAáÏ°AÜ
+AÖbiAÐ«ÆAÊõ#AÅ>A¿ÜA¹Ñ9AŽA®còAš­OA¢ö¬A@	AeAÒÂA
+Ae{A®ØAuðjAj%A_ßASšAH;TA<ÎA1`ÉA%óA>AùA«³@ð|Û@Ù¢P@ÂÇÅ@«í9@¯@|pG@N»/@!?æ¢?7Ó>¿6>ªT©¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AçAáìŠAÜ6AÖ`AÐÈœAËAÅ[vA¿€ÓA¹î/AŽ7A®éAšÊEA£¢A\ÿAŠ\Aï¹A9ArAËÏAv*XAjœA_OÌASâAHuAA=üA1¶A&-qAÀ+ARåAå @ððµ@Ú*@Ã;@¬a@@}Wú@O¢ä@!íÌ?èqj?<>Æt6>±J¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÜE/AÖVAÐå³AË/AÅxmA¿ÁÉAº
+&AŽTA®ßAšç<A£0AyõAÃRA
+¯AV
+AiAèÅAvdEAjöÿA_ºAT
+tAH¯.A=AéA1Ô£A&g^AúAÓA@ñd@Ú@Ã¯y@¬Ôî@úb@~?¯@P@"Õ?ê@Ó?Ö€>Í±Ú>žÏï¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÐôßAËLAÅcA¿ÞÀAº(
+AŽqyA®ºÖA©3A£MAìAàIA)¥AsAŒ_AŒAv2Ak0íA_Ã§ATVaAHé
+A={ÖA2A&¡KA4AÆÀAYz@ñØi@ÚýÞ@Ä#S@­HÈ@n=@'d@QrM@#œ6?ì<?Š
+>Ôï~>À
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÅ€A¿ûžAºEAŽrA®×ÏA©!+A£jA³åAýAAFAûAÙWA"ŽAvØ#AkjÝA_ýATSAI#
+A=µÇA2HA&Û<AmöA ±Ak@òLK@ÛqÁ@Ä5@­Œª@â@@RZ@$€ú?íßÅ?u>Ü-¢>ÇK5¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AºTAAŽ«hA®ôÅA©>"A£AÐÛA8AcA¬ñAöNA?«AwAk€ËA`7
+ATÊ?AI\úA=ïµA2oA')A§äA:AÍX@òÀ&@Ûå@Å
+@®0
+@Uù@{n@SAÆ@%®?ï¯.?Dÿ>ãkE>ÎÚ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A¯ñA©[A£€uAíÒA7/AAÉèAEA\¡AwKýAkÞžA`qsAU-AIçA>)¡A2Œ\A'OAáÑAtAF@ó4 @ÜYu@Å~ê@®€_@ÉÓ@ïI@T)z@&tc?ñ~?i>êšé>ÕÆ}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A£³¡A
+ÈAT%AAæßA0;AyAw
+êAl¥A`«_AU>AIÐÕA>cA2öIA'A
+ŸA®yAA3@ó§Û@ÜÍO@ÅòÄ@¯9@=®@c#@U.@'\?óN ?ãÒ>ñæ>Ý!¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AcQAºxAÕAM2AAw¿ØAlRA`åLAUxAJ
+ÁA>|A307A'ÂñA
+U«AèeA{ @ôŽ@ÝA*@Æf@¯@±@Öý@Uøã@(CÌ?õ
+i?³:>ù$1>äAÅ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AAj*A³AwùÈAlAa>AU±øAJD²A>×mA3j'A'üâA
+A"WAµ@ô@Ýµ
+@ÆÚ@¯ÿö@%k@Jß@Và§@)+?öìò?Ã? 1*>ëi¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÂ³Ax3¶AlÆpAaY*AUëåAJ~ A?ZA3€A(6ÏA
+ÉA\DAîþ@õq@Þ(æ@ÇN[@°sÏ@D@Ÿº@WÈ\@*E?øŒ[?R,?Ïü>òœ
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AläÉAaAV%ÒAJžA?KGA3ÞA(pŒA
+vA1A(ë@õwK@ÞÀ@ÇÂ5@°çª@
+@2@X°@*úù?úÃ?!?nÍ>ùú®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AVD+AJòzA?
+4A4îA(ª©A
+=dAÐ
+AbØ@õë%@ß@È6@±[@ù@Šn@YÄ@+â­?ü[,? ðý?
+
+? )¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A?£A4QàA(äA
+wTA
+AÊ@ö_@ß}@È©ñ@±Ïg@ôÛ@P@Z@,Êr?þ*µ?¢À?¬°?:û¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A)óA
+±BACüAÖ¶@öÒâ@ßøW@É
+Ì@²CA@hµ@*@[g=@-²&?ÿú
+?€ï?K?ÙÌ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AbUA€@÷FŒ@àl1@ÉŠ@²·@Ü@
+@\Nò@.Ú@ äÃ?Š_X?êU?
+x¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @÷n@àà
+@Ê@³*õ@Pj@
+uß@]6Š@/@Ìx?š.Á?'?q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ÊB2@³Ð@ÄD@
+é¹@^
+[@0iC@Ž,?©þ*?
+'ø?¶B¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ þ@]@_@1Q@ñ?«Í³? Ç
+?U¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @_@28œ@¥?­
+?$eÝ?óç¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ý	?¯l
+?(®?
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?)ê;?!1¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B~
+B~záB~p€B~ffB~\)B~QìB~G®B~=qB~33B~(öB~
+žB~{B~
+=B~  B}õÃB}ë
+B}áHB}×
+B}ÌÍB}ÂB}žRB}®B}£×B}B}\B}
+B}záB}p€B}ffB}\)B}QìB}G®B}=qB}33B}(öB}
+žB}{B}
+=B}  B|õÃB|ë
+B|áHB|×
+B|ÌÍB|ÂB|žRB|®B|£×B|B|\B|
+B|záB|p€B|ffB|\)B|QìB|G®B|=qB|33B|(öB|
+žB|{B|
+=B|  Bz
+BzzáBzp€BzffBz\)BzQìBzG®Bz=qBz33Bz(öBz
+žBz{Bz
+=Bz  ByõÃByë
+ByáHBy×
+ByÌÍByÂByžRBy®By£×ByBy\By
+ByzáByp€ByffBy\)ByQìByG®By=qBy33By(öBy
+žBy{By
+=By  BxõÃBxë
+BxáHBx×
+BxÌÍBxÂBxžRBx®Bx£×BxBx\Bx
+BxzáBxp€BxffBx\)BxQìBxG®Bx=qBx33Bx(öBx
+žBx{Bx
+=Bx  Bv
+BvzáBvp€BvffBv\)BvQìBvG®Bv=qBv33Bv(öBv
+žBv{Bv
+=Bv  BuõÃBuë
+BuáHBu×
+BuÌÍBuÂBužRBu®Bu£×BuBu\Bu
+BuzáBup€BuffBu\)BuQìBuG®Bu=qBu33Bu(öBu
+žBu{Bu
+=Bu  BtõÃBtë
+BtáHBt×
+BtÌÍBtÂBtžRBt®Bt£×BtBt\Bt
+BtzáBtp€BtffBt\)BtQìBtG®Bt=qBt33Bt(öBt
+žBt{Bt
+=Bt  Br
+BrzáBrp€BrffBr\)BrQìBrG®Br=qBr33Br(öBr
+žBr{Br
+=Br  BqõÃBqë
+BqáHBq×
+BqÌÍBqÂBqžRBq®Bq£×BqBq\Bq
+BqzáBqp€BqffBq\)BqQìBqG®Bq=qBq33Bq(öBq
+žBq{Bq
+=Bq  BpõÃBpë
+BpáHBp×
+BpÌÍBpÂBpžRBp®Bp£×BpBp\Bp
+BpzáBpp€BpffBp\)BpQìBpG®Bp=qBp33Bp(öBp
+žBp{Bp
+=Bp  Bn
+BnzáBnp€BnffBn\)BnQìBnG®Bn=qBn33Bn(öBn
+žBn{Bn
+=Bn  BmõÃBmë
+BmáHBm×
+BmÌÍBmÂBmžRBm®Bm£×BmBm\Bm
+BmzáBmp€BmffBm\)BmQìBmG®Bm=qBm33Bm(öBm
+žBm{Bm
+=Bm  BlõÃBlë
+BláHBl×
+BlÌÍBlÂBlžRBl®Bl£×BlBl\Bl
+BlzáBlp€BlffBl\)BlQìBlG®Bl=qBl33Bl(öBl
+žBl{Bl
+=Bl  Bj
+BjzáBjp€BjffBj\)BjQìBjG®Bj=qBj33Bj(öBj
+žBj{Bj
+=Bj  BiõÃBië
+BiáHBi×
+BiÌÍBiÂBižRBi®Bi£×BiBi\Bi
+BizáBip€BiffBi\)BiQìBiG®Bi=qBi33Bi(öBi
+žBi{Bi
+=Bi  BhõÃBhë
+BháHBh×
+BhÌÍBhÂBhžRBh®Bh£×BhBh\Bh
+BhzáBhp€BhffBh\)BhQìBhG®Bh=qBh33Bh(öBh
+žBh{Bh
+=Bh  Bf
+BfzáBfp€BfffBf\)BfQìBfG®Bf=qBf33Bf(öBf
+žBf{Bf
+=Bf  BeõÃBeë
+BeáHBe×
+BeÌÍBeÂBežRBe®Be£×BeBe\Be
+BezáBep€BeffBe\)BeQìBeG®Be=qBe33Be(öBe
+žBe{Be
+=Be  BdõÃBdë
+BdáHBd×
+BdÌÍBdÂBdžRBd®Bd£×BdBd\Bd
+BdzáBdp€BdffBd\)BdQìBdG®Bd=qBd33Bd(öBd
+žBd{Bd
+=Bd  Bb
+BbzáBbp€BbffBb\)BbQìBbG®Bb=qBb33Bb(öBb
+žBb{Bb
+=Bb  BaõÃBaë
+BaáHBa×
+BaÌÍBaÂBažRBa®Ba£×BaBa\Ba
+BazáBap€BaffBa\)BaQìBaG®Ba=qBa33Ba(öBa
+žBa{Ba
+=Ba  B`õÃB`ë
+B`áHB`×
+B`ÌÍB`ÂB`žRB`®B`£×B`B`\B`
+B`záB`p€B`ffB`\)B`QìB`G®B`=qB`33B`(öB`
+žB`{B`
+=B`  B^
+B^záB^p€B^ffB^\)B^QìB^G®B^=qB^33B^(öB^
+žB^{B^
+=B^  B]õÃB]ë
+B]áHB]×
+B]ÌÍB]ÂB]žRB]®B]£×B]B]\B]
+B]záB]p€B]ffB]\)B]QìB]G®B]=qB]33B](öB]
+žB]{B]
+=B]  B\õÃB\ë
+B\áHB\×
+B\ÌÍB\ÂB\žRB\®B\£×B\B\\B\
+B\záB\p€B\ffB\\)B\QìB\G®B\=qB\33B\(öB\
+žB\{B\
+=B\  BZ
+BZzáBZp€BZffBZ\)BZQìBZG®BZ=qBZ33BZ(öBZ
+žBZ{BZ
+=BZ  BYõÃBYë
+BYáHBY×
+BYÌÍBYÂBYžRBY®BY£×BYBY\BY
+BYzáBYp€BYffBY\)BYQìBYG®BY=qBY33BY(öBY
+žBY{BY
+=BY  BXõÃBXë
+BXáHBX×
+BXÌÍBXÂBXžRBX®BX£×BXBX\BX
+BXzáBXp€BXffBX\)BXQìBXG®BX=qBX33BX(öBX
+žBX{BX
+=BX  BV
+BVzáBVp€BVffBV\)BVQìBVG®BV=qBV33BV(öBV
+žBV{BV
+=BV  BUõÃBUë
+BUáHBU×
+BUÌÍBUÂBUžRBU®BU£×BUBU\BU
+BUzáBUp€BUffBU\)BUQìBUG®BU=qBU33BU(öBU
+žBU{BU
+=BU  BTõÃBTë
+BTáHBT×
+BTÌÍBTÂBTžRBT®BT£×BTBT\BT
+BTzáBTp€BTffBT\)BTQìBTG®BT=qBT33BT(öBT
+žBT{BT
+=BT  BR
+BRzáBRp€BRffBR\)BRQìBRG®BR=qBR33BR(öBR
+žBR{BR
+=BR  BQõÃBQë
+BQáHBQ×
+BQÌÍBQÂBQžRBQ®BQ£×BQBQ\BQ
+BQzáBQp€BQffBQ\)BQQìBQG®BQ=qBQ33BQ(öBQ
+žBQ{BQ
+=BQ  BPõÃBPë
+BPáHBP×
+BPÌÍBPÂBPžRBP®BP£×BPBP\BP
+BPzáBPp€BPffBP\)BPQìBPG®BP=qBP33BP(öBP
+žBP{BP
+=BP  BN
+BNzáBNp€BNffBN\)BNQìBNG®BN=qBN33BN(öBN
+žBN{BN
+=BN  BMõÃBMë
+BMáHBM×
+BMÌÍBMÂBMžRBM®BM£×BMBM\BM
+BMzáBMp€BMffBM\)BMQìBMG®BM=qBM33BM(öBM
+žBM{BM
+=BM  BLõÃBLë
+BLáHBL×
+BLÌÍBLÂBLžRBL®BL£×BLBL\BL
+BLzáBLp€BLffBL\)BLQìBLG®BL=qBL33BL(öBL
+žBL{BL
+=BL  BJ
+BJzáBJp€BJffBJ\)BJQìBJG®BJ=qBJ33BJ(öBJ
+žBJ{BJ
+=BJ  BIõÃBIë
+BIáHBI×
+BIÌÍBIÂBIžRBI®BI£×BIBI\BI
+BIzáBIp€BIffBI\)BIQìBIG®BI=qBI33BI(öBI
+žBI{BI
+=BI  BHõÃBHë
+BHáHBH×
+BHÌÍBHÂBHžRBH®BH£×BHBH\BH
+BHzáBHp€BHffBH\)BHQìBHG®BH=qBH33BH(öBH
+žBH{BH
+=BH  BF
+BFzáBFp€BFffBF\)BFQìBFG®BF=qBF33BF(öBF
+žBF{BF
+=BF  BEõÃBEë
+BEáHBE×
+BEÌÍBEÂBEžRBE®BE£×BEBE\BE
+BEzáBEp€BEffBE\)BEQìBEG®BE=qBE33BE(öBE
+žBE{BE
+=BE  BDõÃBDë
+BDáHBD×
+BDÌÍBDÂBDžRBD®BD£×BDBD\BD
+BDzáBDp€BDffBD\)BDQìBDG®BD=qBD33BD(öBD
+žBD{BD
+=BD  BB
+BBzáBBp€BBffBB\)BBQìBBG®BB=qBB33BB(öBB
+žBB{BB
+=BB  BAõÃBAë
+BAáHBA×
+BAÌÍBAÂBAžRBA®BA£×BABA\BA
+BAzáBAp€BAffBA\)BAQìBAG®BA=qBA33BA(öBA
+žBA{BA
+=BA  B@õÃB@ë
+B@áHB@×
+B@ÌÍB@ÂB@žRB@®B@£×B@B@\B@
+B@záB@p€B@ffB@\)B@QìB@G®B@=qB@33B@(öB@
+žB@{B@
+=B@  B>
+B>záB>p€B>ffB>\)B>QìB>G®B>=qB>33B>(öB>
+žB>{B>
+=B>  B=õÃB=ë
+B=áHB=×
+B=ÌÍB=ÂB=žRB=®B=£×B=B=\B=
+B=záB=p€B=ffB=\)B=QìB=G®B==qB=33B=(öB=
+žB={B=
+=B=  B<õÃB<ë
+B<áHB<×
+B<ÌÍB<ÂB<žRB<®B<£×B<B<\B<
+B<záB<p€B<ffB<\)B<QìB<G®B<=qB<33B<(öB<
+žB<{B<
+=B<  B:
+B:záB:p€B:ffB:\)B:QìB:G®B:=qB:33B:(öB:
+žB:{B:
+=B:  B9õÃB9ë
+B9áHB9×
+B9ÌÍB9ÂB9žRB9®B9£×B9B9\B9
+B9záB9p€B9ffB9\)B9QìB9G®B9=qB933B9(öB9
+žB9{B9
+=B9  B8õÃB8ë
+B8áHB8×
+B8ÌÍB8ÂB8žRB8®B8£×B8B8\B8
+B8záB8p€B8ffB8\)B8QìB8G®B8=qB833B8(öB8
+žB8{B8
+=B8  B6
+B6záB6p€B6ffB6\)B6QìB6G®B6=qB633B6(öB6
+žB6{B6
+=B6  B5õÃB5ë
+B5áHB5×
+B5ÌÍB5ÂB5žRB5®B5£×B5B5\B5
+B5záB5p€B5ffB5\)B5QìB5G®B5=qB533B5(öB5
+žB5{B5
+=B5  B4õÃB4ë
+B4áHB4×
+B4ÌÍB4ÂB4žRB4®B4£×B4B4\B4
+B4záB4p€B4ffB4\)B4QìB4G®B4=qB433B4(öB4
+žB4{B4
+=B4  B2
+B2záB2p€B2ffB2\)B2QìB2G®B2=qB233B2(öB2
+žB2{B2
+=B2  B1õÃB1ë
+B1áHB1×
+B1ÌÍB1ÂB1žRB1®B1£×B1B1\B1
+B1záB1p€B1ffB1\)B1QìB1G®B1=qB133B1(öB1
+žB1{B1
+=B1  B0õÃB0ë
+B0áHB0×
+B0ÌÍB0ÂB0žRB0®B0£×B0B0\B0
+B0záB0p€B0ffB0\)B0QìB0G®B0=qB033B0(öB0
+žB0{B0
+=B0  B.
+B.záB.p€B.ffB.\)B.QìB.G®B.=qB.33B.(öB.
+žB.{B.
+=B.  B-õÃB-ë
+B-áHB-×
+B-ÌÍB-ÂB-žRB-®B-£×B-B-\B-
+B-záB-p€B-ffB-\)B-QìB-G®B-=qB-33B-(öB-
+žB-{B-
+=B-  B,õÃB,ë
+B,áHB,×
+B,ÌÍB,ÂB,žRB,®B,£×B,B,\B,
+B,záB,p€B,ffB,\)B,QìB,G®B,=qB,33B,(öB,
+žB,{B,
+=B,  B*
+B*záB*p€B*ffB*\)B*QìB*G®B*=qB*33B*(öB*
+žB*{B*
+=B*  B)õÃB)ë
+B)áHB)×
+B)ÌÍB)ÂB)žRB)®B)£×B)B)\B)
+B)záB)p€B)ffB)\)B)QìB)G®B)=qB)33B)(öB)
+žB){B)
+=B)  B(õÃB(ë
+B(áHB(×
+B(ÌÍB(ÂB(žRB(®B(£×B(B(\B(
+B(záB(p€B(ffB(\)B(QìB(G®B(=qB(33B((öB(
+žB({B(
+=B(  B&
+B&záB&p€B&ffB&\)B&QìB&G®B&=qB&33B&(öB&
+žB&{B&
+=B&  B%õÃB%ë
+B%áHB%×
+B%ÌÍB%ÂB%žRB%®B%£×B%B%\B%
+B%záB%p€B%ffB%\)B%QìB%G®B%=qB%33B%(öB%
+žB%{B%
+=B%  B$õÃB$ë
+B$áHB$×
+B$ÌÍB$ÂB$žRB$®B$£×B$B$\B$
+B$záB$p€B$ffB$\)B$QìB$G®B$=qB$33B$(öB$
+žB${B$
+=B$  B"
+B"záB"p€B"ffB"\)B"QìB"G®B"=qB"33B"(öB"
+žB"{B"
+=B"  B!õÃB!ë
+B!áHB!×
+B!ÌÍB!ÂB!žRB!®B!£×B!B!\B!
+B!záB!p€B!ffB!\)B!QìB!G®B!=qB!33B!(öB!
+žB!{B!
+=B!  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B	õÃB	ë
+B	áHB	×
+B	ÌÍB	ÂB	žRB	®B	£×B	B	\B	
+B	záB	p€B	ffB	\)B	QìB	G®B	=qB	33B	(öB	
+žB	{B	
+=B	  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   Aý
+=AüõÃAüáHAüÌÍAüžRAü£×Aü\AüzáAüffAüQìAü=qAü(öAü{Aü  Aûë
+Aû×
+AûÂAû®AûAû
+Aûp€Aû\)AûG®Aû33Aû
+žAû
+=AúõÃAúáHAúÌÍAúžRAú£×Aú\AúzáAúffAúQìAú=qAú(öAú{Aú  Aùë
+Aù×
+AùÂAù®AùAù
+Aùp€Aù\)AùG®Aù33Aù
+žAù
+=AøõÃAøáHAøÌÍAøžRAø£×Aø\AøzáAøffAøQìAø=qAø(öAø{Aø  Aõ
+=AôõÃAôáHAôÌÍAôžRAô£×Aô\AôzáAôffAôQìAô=qAô(öAô{Aô  Aóë
+Aó×
+AóÂAó®AóAó
+Aóp€Aó\)AóG®Aó33Aó
+žAó
+=AòõÃAòáHAòÌÍAòžRAò£×Aò\AòzáAòffAòQìAò=qAò(öAò{Aò  Añë
+Añ×
+AñÂAñ®AñAñ
+Añp€Añ\)AñG®Añ33Añ
+žAñ
+=AðõÃAðáHAðÌÍAðžRAð£×Að\AðzáAðffAðQìAð=qAð(öAð{Að  Aí
+=AìõÃAìáHAìÌÍAìžRAì£×Aì\AìzáAìffAìQìAì=qAì(öAì{Aì  Aëë
+Aë×
+AëÂAë®AëAë
+Aëp€Aë\)AëG®Aë33Aë
+žAë
+=AêõÃAêáHAêÌÍAêžRAê£×Aê\AêzáAêffAêQìAê=qAê(öAê{Aê  Aéë
+Aé×
+AéÂAé®AéAé
+Aép€Aé\)AéG®Aé33Aé
+žAé
+=AèõÃAèáHAèÌÍAèžRAè£×Aè\AèzáAèffAèQìAè=qAè(öAè{Aè  Aå
+=AäõÃAäáHAäÌÍAäžRAä£×Aä\AäzáAäffAäQìAä=qAä(öAä{Aä  Aãë
+Aã×
+AãÂAã®AãAã
+Aãp€Aã\)AãG®Aã33Aã
+žAã
+=AâõÃAâáHAâÌÍAâžRAâ£×Aâ\AâzáAâffAâQìAâ=qAâ(öAâ{Aâ  Aáë
+Aá×
+AáÂAá®AáAá
+Aáp€Aá\)AáG®Aá33Aá
+žAá
+=AàõÃAàáHAàÌÍAàžRAà£×Aà\AàzáAàffAàQìAà=qAà(öAà{Aà  AÝ
+=AÜõÃAÜáHAÜÌÍAÜžRAÜ£×AÜ\AÜzáAÜffAÜQìAÜ=qAÜ(öAÜ{AÜ  AÛë
+AÛ×
+AÛÂAÛ®AÛAÛ
+AÛp€AÛ\)AÛG®AÛ33AÛ
+žAÛ
+=AÚõÃAÚáHAÚÌÍAÚžRAÚ£×AÚ\AÚzáAÚffAÚQìAÚ=qAÚ(öAÚ{AÚ  AÙë
+AÙ×
+AÙÂAÙ®AÙAÙ
+AÙp€AÙ\)AÙG®AÙ33AÙ
+žAÙ
+=AØõÃAØáHAØÌÍAØžRAØ£×AØ\AØzáAØffAØQìAØ=qAØ(öAØ{AØ  AÕ
+=AÔõÃAÔáHAÔÌÍAÔžRAÔ£×AÔ\AÔzáAÔffAÔQìAÔ=qAÔ(öAÔ{AÔ  AÓë
+AÓ×
+AÓÂAÓ®AÓAÓ
+AÓp€AÓ\)AÓG®AÓ33AÓ
+žAÓ
+=AÒõÃAÒáHAÒÌÍAÒžRAÒ£×AÒ\AÒzáAÒffAÒQìAÒ=qAÒ(öAÒ{AÒ  AÑë
+AÑ×
+AÑÂAÑ®AÑAÑ
+AÑp€AÑ\)AÑG®AÑ33AÑ
+žAÑ
+=AÐõÃAÐáHAÐÌÍAÐžRAÐ£×AÐ\AÐzáAÐffAÐQìAÐ=qAÐ(öAÐ{AÐ  AÍ
+=AÌõÃAÌáHAÌÌÍAÌžRAÌ£×AÌ\AÌzáAÌffAÌQìAÌ=qAÌ(öAÌ{AÌ  AËë
+AË×
+AËÂAË®AËAË
+AËp€AË\)AËG®AË33AË
+žAË
+=AÊõÃAÊáHAÊÌÍAÊžRAÊ£×AÊ\AÊzáAÊffAÊQìAÊ=qAÊ(öAÊ{AÊ  AÉë
+AÉ×
+AÉÂAÉ®AÉAÉ
+AÉp€AÉ\)AÉG®AÉ33AÉ
+žAÉ
+=AÈõÃAÈáHAÈÌÍAÈžRAÈ£×AÈ\AÈzáAÈffAÈQìAÈ=qAÈ(öAÈ{AÈ  AÅ
+=AÄõÃAÄáHAÄÌÍAÄžRAÄ£×AÄ\AÄzáAÄffAÄQìAÄ=qAÄ(öAÄ{AÄ  AÃë
+AÃ×
+AÃÂAÃ®AÃAÃ
+AÃp€AÃ\)AÃG®AÃ33AÃ
+žAÃ
+=AÂõÃAÂáHAÂÌÍAÂžRAÂ£×AÂ\AÂzáAÂffAÂQìAÂ=qAÂ(öAÂ{AÂ  AÁë
+AÁ×
+AÁÂAÁ®AÁAÁ
+AÁp€AÁ\)AÁG®AÁ33AÁ
+žAÁ
+=AÀõÃAÀáHAÀÌÍAÀžRAÀ£×AÀ\AÀzáAÀffAÀQìAÀ=qAÀ(öAÀ{AÀ  Aœ
+=AŒõÃAŒáHAŒÌÍAŒžRAŒ£×AŒ\AŒzáAŒffAŒQìAŒ=qAŒ(öAŒ{AŒ  A»ë
+A»×
+A»ÂA»®A»A»
+A»p€A»\)A»G®A»33A»
+žA»
+=AºõÃAºáHAºÌÍAºžRAº£×Aº\AºzáAºffAºQìAº=qAº(öAº{Aº  A¹ë
+A¹×
+A¹ÂA¹®A¹A¹
+A¹p€A¹\)A¹G®A¹33A¹
+žA¹
+=AžõÃAžáHAžÌÍAžžRAž£×Až\AžzáAžffAžQìAž=qAž(öAž{Až  Aµ
+=AŽõÃAŽáHAŽÌÍAŽžRAŽ£×AŽ\AŽzáAŽffAŽQìAŽ=qAŽ(öAŽ{AŽ  A³ë
+A³×
+A³ÂA³®A³A³
+A³p€A³\)A³G®A³33A³
+žA³
+=A²õÃA²áHA²ÌÍA²žRA²£×A²\A²záA²ffA²QìA²=qA²(öA²{A²  A±ë
+A±×
+A±ÂA±®A±A±
+A±p€A±\)A±G®A±33A±
+žA±
+=A°õÃA°áHA°ÌÍA°žRA°£×A°\A°záA°ffA°QìA°=qA°(öA°{A°  A­
+=A¬õÃA¬áHA¬ÌÍA¬žRA¬£×A¬\A¬záA¬ffA¬QìA¬=qA¬(öA¬{A¬  A«ë
+A«×
+A«ÂA«®A«A«
+A«p€A«\)A«G®A«33A«
+žA«
+=AªõÃAªáHAªÌÍAªžRAª£×Aª\AªzáAªffAªQìAª=qAª(öAª{Aª  A©ë
+A©×
+A©ÂA©®A©A©
+A©p€A©\)A©G®A©33A©
+žA©
+=AšõÃAšáHAšÌÍAšžRAš£×Aš\AšzáAšffAšQìAš=qAš(öAš{Aš  A¥
+=A€õÃA€áHA€ÌÍA€žRA€£×A€\A€záA€ffA€QìA€=qA€(öA€{A€  A£ë
+A£×
+A£ÂA£®A£A£
+A£p€A£\)A£G®A£33A£
+žA£
+=A¢õÃA¢áHA¢ÌÍA¢žRA¢£×A¢\A¢záA¢ffA¢QìA¢=qA¢(öA¢{A¢  A¡ë
+A¡×
+A¡ÂA¡®A¡A¡
+A¡p€A¡\)A¡G®A¡33A¡
+žA¡
+=A õÃA áHA ÌÍA žRA £×A \A záA ffA QìA =qA (öA {A   A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Az{Ayë
+AyÂAyAyp€AyG®Ay
+žAxõÃAxÌÍAx£×AxzáAxQìAx(öAx  Aw×
+Aw®Aw
+Aw\)Aw33Aw
+=AváHAvžRAv\AvffAv=qAv{Auë
+AuÂAuAup€AuG®Au
+žAtõÃAtÌÍAt£×AtzáAtQìAt(öAt  As×
+As®As
+As\)As33As
+=AráHAržRAr\ArffAr=qAr{Aqë
+AqÂAqAqp€AqG®Aq
+žApõÃApÌÍAp£×ApzáApQìAp(öAp  Aj{Aië
+AiÂAiAip€AiG®Ai
+žAhõÃAhÌÍAh£×AhzáAhQìAh(öAh  Ag×
+Ag®Ag
+Ag\)Ag33Ag
+=AfáHAfžRAf\AfffAf=qAf{Aeë
+AeÂAeAep€AeG®Ae
+žAdõÃAdÌÍAd£×AdzáAdQìAd(öAd  Ac×
+Ac®Ac
+Ac\)Ac33Ac
+=AbáHAbžRAb\AbffAb=qAb{Aaë
+AaÂAaAap€AaG®Aa
+žA`õÃA`ÌÍA`£×A`záA`QìA`(öA`  AZ{AYë
+AYÂAYAYp€AYG®AY
+žAXõÃAXÌÍAX£×AXzáAXQìAX(öAX  AW×
+AW®AW
+AW\)AW33AW
+=AVáHAVžRAV\AVffAV=qAV{AUë
+AUÂAUAUp€AUG®AU
+žATõÃATÌÍAT£×ATzáATQìAT(öAT  AS×
+AS®AS
+AS\)AS33AS
+=ARáHARžRAR\ARffAR=qAR{AQë
+AQÂAQAQp€AQG®AQ
+žAPõÃAPÌÍAP£×APzáAPQìAP(öAP  AJ{AIë
+AIÂAIAIp€AIG®AI
+žAHõÃAHÌÍAH£×AHzáAHQìAH(öAH  AG×
+AG®AG
+AG\)AG33AG
+=AFáHAFžRAF\AFffAF=qAF{AEë
+AEÂAEAEp€AEG®AE
+žADõÃADÌÍAD£×ADzáADQìAD(öAD  AC×
+AC®AC
+AC\)AC33AC
+=ABáHABžRAB\ABffAB=qAB{AAë
+AAÂAAAAp€AAG®AA
+žA@õÃA@ÌÍA@£×A@záA@QìA@(öA@  A:{A9ë
+A9ÂA9A9p€A9G®A9
+žA8õÃA8ÌÍA8£×A8záA8QìA8(öA8  A7×
+A7®A7
+A7\)A733A7
+=A6áHA6žRA6\A6ffA6=qA6{A5ë
+A5ÂA5A5p€A5G®A5
+žA4õÃA4ÌÍA4£×A4záA4QìA4(öA4  A3×
+A3®A3
+A3\)A333A3
+=A2áHA2žRA2\A2ffA2=qA2{A1ë
+A1ÂA1A1p€A1G®A1
+žA0õÃA0ÌÍA0£×A0záA0QìA0(öA0  A*{A)ë
+A)ÂA)A)p€A)G®A)
+žA(õÃA(ÌÍA(£×A(záA(QìA((öA(  A'×
+A'®A'
+A'\)A'33A'
+=A&áHA&žRA&\A&ffA&=qA&{A%ë
+A%ÂA%A%p€A%G®A%
+žA$õÃA$ÌÍA$£×A$záA$QìA$(öA$  A#×
+A#®A#
+A#\)A#33A#
+=A"áHA"žRA"\A"ffA"=qA"{A!ë
+A!ÂA!A!p€A!G®A!
+žA õÃA ÌÍA £×A záA QìA (öA   A{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A
+{A	ë
+A	ÂA	A	p€A	G®A	
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žA õÃA ÌÍA £×A záA QìA (öA   @ô(ö@ó×
+@ó
+@ó33@òáH@ò\@ò=q@ñë
+@ñ@ñG®@ðõÂ@ð£×@ðQì@ð  @ï®@ï\)@ï
+=@îžR@îff@î{@íÂ@íp€@í
+ž@ìÌÍ@ìzá@ì(ö@ë×
+@ë
+@ë33@êáH@ê\@ê=q@éë
+@é@éG®@èõÃ@è£×@èQì@è  @ç®@ç\)@ç
+=@æžR@æff@æ{@åÂ@åp€@å
+ž@äÌÍ@äzá@ä(ö@ã×
+@ã
+@ã33@âáH@â\@â=q@áë
+@á@áG®@àõÃ@à£×@àQì@à  @Ô(ö@Ó×
+@Ó
+@Ó33@ÒáH@Ò\@Ò=q@Ñë
+@Ñ@ÑG®@ÐõÂ@Ð£×@ÐQì@Ð  @Ï®@Ï\)@Ï
+=@ÎžR@Îff@Î{@ÍÂ@Íp€@Í
+ž@ÌÌÍ@Ìzá@Ì(ö@Ë×
+@Ë
+@Ë33@ÊáH@Ê\@Ê=q@Éë
+@É@ÉG®@ÈõÃ@È£×@ÈQì@È  @Ç®@Ç\)@Ç
+=@ÆžR@Æff@Æ{@ÅÂ@Åp€@Å
+ž@ÄÌÍ@Äzá@Ä(ö@Ã×
+@Ã
+@Ã33@ÂáH@Â\@Â=q@Áë
+@Á@ÁG®@ÀõÃ@À£×@ÀQì@À  @Ž(ö@³×
+@³
+@³33@²áH@²\@²=q@±ë
+@±@±G®@°õÂ@°£×@°Qì@°  @¯®@¯\)@¯
+=@®žR@®ff@®{@­Â@­p€@­
+ž@¬ÌÍ@¬zá@¬(ö@«×
+@«
+@«33@ªáH@ª\@ª=q@©ë
+@©@©G®@šõÃ@š£×@šQì@š  @§®@§\)@§
+=@ŠžR@Šff@Š{@¥Â@¥p€@¥
+ž@€ÌÍ@€zá@€(ö@£×
+@£
+@£33@¢áH@¢\@¢=q@¡ë
+@¡@¡G®@ õÃ@ £×@ Qì@   @(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÂ@£×@Qì@  @®@\)@
+=@žR@ff@{@Â@p€@
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @®@\)@
+=@žR@ff@{@
+Â@
+p€@
+
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @hQì@g®@g
+>@fff@eÂ@e
+ž@dzá@c×
+@c33@b\@aë
+@aG®@`£×@`  @_\)@^žR@^{@]p€@\ÌÍ@\(ö@[
+@ZáH@Z=q@Y@XõÂ@XQì@W®@W
+>@Vff@UÂ@U
+ž@Tzá@S×
+@S33@R\@Që
+@QG®@P£×@P  @O\)@NžR@N{@Mp€@LÌÍ@L(ö@K
+@JáH@J=q@I@HõÃ@HQì@G®@G
+=@Fff@EÂ@E
+ž@Dzá@C×
+@C33@B\@Aë
+@AG®@@£×@@  @(Qì@'®@'
+>@&ff@%Â@%
+ž@$zá@#×
+@#33@"\@!ë
+@!G®@ £×@   @\)@
+žR@
+{@
+p€@
+ÌÍ@
+(ö@
+@áH@=q@@õÂ@Qì@®@
+>@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@£×@  @\)@žR@{@
+p€@
+ÌÍ@
+(ö@
+
+@
+áH@
+=q@	@õÃ@Qì@®@
+=@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@ £×@   ?Ð£×?Ï\)?Î{?ÌÌÍ?Ë
+
+?Ê=p?ÈõÂ?Ç®?Æff?Å
+ž?Ã×
+?Â\?ÁG®?À  ?ŸžR?œp€?Œ(ö?ºáH?¹?žQì?·
+>?µÂ?Žzá?³33?±ë
+?°£×?¯\)?®{?¬ÌÍ?«
+?ª=q?šõÂ?§®?Šff?¥
+ž?£×
+?¢\?¡G®?   ?žR?p€?(ö?áH??Qì?
+>?Â?zá?33?ë
+?£×?\)?{?ÌÍ?
+?=q?õÃ?®?ff?
+
+ž?×
+?\?G®?  ?!G®?
+žR?
+(ö??
+=?zá?ë
+?\)?
+ÌÍ?
+=q?®?
+ž?\?   >úáH>õÂ>ð£×>ë
+>æff>áG®>Ü(ö>×
+=>Ñë
+>ÌÌÍ>Ç®>Â\>œp€>žQì>³33>®{>šõÃ>£×
+>žR>>zá>\)>=q>
+
+ž>  >uÂ>k
+>aG®>W
+=>LÌÍ>B\>8Qì>.{>#×
+>>\)>
+ž=õÂ=áG®=ÌÌÍ=žQì=£×
+=\)=uÂ=LÌÍ=#×
+<õÂ<£×
+<#×
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/fOut.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/fOut.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/fOut.fits	(revision 22322)
@@ -0,0 +1,8424 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B~
+B~záB~p€B~ffB~\)B~QìB~G®B~=qB~33B~(öB~
+žB~{B~
+=B~  B}õÃB}ë
+B}áHB}×
+B}ÌÍB}ÂB}žRB}®B}£×B}B}\B}
+B}záB}p€B}ffB}\)B}QìB}G®B}=qB}33B}(öB}
+žB}{B}
+=B}  B|õÃB|ë
+B|áHB|×
+B|ÌÍB|ÂB|žRB|®B|£×B|B|\B|
+B|záB|p€B|ffB|\)B|QìB|G®B|=qB|33B|(öB|
+žB|{B|
+=B|  Bz
+BzzáBzp€BzffBz\)BzQìBzG®Bz=qBz33Bz(öBz
+žBz{Bz
+=Bz  ByõÃByë
+ByáHBy×
+ByÌÍByÂByžRBy®By£×ByBy\By
+ByzáByp€ByffBy\)ByQìByG®By=qBy33By(öBy
+žBy{By
+=By  BxõÃBxë
+BxáHBx×
+BxÌÍBxÂBxžRBx®Bx£×BxBx\Bx
+BxzáBxp€BxffBx\)BxQìBxG®Bx=qBx33Bx(öBx
+žBx{Bx
+=Bx  Bv
+BvzáBvp€BvffBv\)BvQìBvG®Bv=qBv33Bv(öBv
+žBv{Bv
+=Bv  BuõÃBuë
+BuáHBu×
+BuÌÍBuÂBužRBu®Bu£×BuBu\Bu
+BuzáBup€BuffBu\)BuQìBuG®Bu=qBu33Bu(öBu
+žBu{Bu
+=Bu  BtõÃBtë
+BtáHBt×
+BtÌÍBtÂBtžRBt®Bt£×BtBt\Bt
+BtzáBtp€BtffBt\)BtQìBtG®Bt=qBt33Bt(öBt
+žBt{Bt
+=Bt  Br
+BrzáBrp€BrffBr\)BrQìBrG®Br=qBr33Br(öBr
+žBr{Br
+=Br  BqõÃBqë
+BqáHBq×
+BqÌÍBqÂBqžRBq®Bq£×BqBq\Bq
+BqzáBqp€BqffBq\)BqQìBqG®Bq=qBq33Bq(öBq
+žBq{Bq
+=Bq  BpõÃBpë
+BpáHBp×
+BpÌÍBpÂBpžRBp®Bp£×BpBp\Bp
+BpzáBpp€BpffBp\)BpQìBpG®Bp=qBp33Bp(öBp
+žBp{Bp
+=Bp  Bn
+BnzáBnp€BnffBn\)BnQìBnG®Bn=qBn33Bn(öBn
+žBn{Bn
+=Bn  BmõÃBmë
+BmáHBm×
+BmÌÍBmÂBmžRBm®Bm£×BmBm\Bm
+BmzáBmp€BmffBm\)BmQìBmG®Bm=qBm33Bm(öBm
+žBm{Bm
+=Bm  BlõÃBlë
+BláHBl×
+BlÌÍBlÂBlžRBl®Bl£×BlBl\Bl
+BlzáBlp€BlffBl\)BlQìBlG®Bl=qBl33Bl(öBl
+žBl{Bl
+=Bl  Bj
+BjzáBjp€BjffBj\)BjQìBjG®Bj=qBj33Bj(öBj
+žBj{Bj
+=Bj  BiõÃBië
+BiáHBi×
+BiÌÍBiÂBižRBi®Bi£×BiBi\Bi
+BizáBip€BiffBi\)BiQìBiG®Bi=qBi33Bi(öBi
+žBi{Bi
+=Bi  BhõÃBhë
+BháHBh×
+BhÌÍBhÂBhžRBh®Bh£×BhBh\Bh
+BhzáBhp€BhffBh\)BhQìBhG®Bh=qBh33Bh(öBh
+žBh{Bh
+=Bh  Bf
+BfzáBfp€BfffBf\)BfQìBfG®Bf=qBf33Bf(öBf
+žBf{Bf
+=Bf  BeõÃBeë
+BeáHBe×
+BeÌÍBeÂBežRBe®Be£×BeBe\Be
+BezáBep€BeffBe\)BeQìBeG®Be=qBe33Be(öBe
+žBe{Be
+=Be  BdõÃBdë
+BdáHBd×
+BdÌÍBdÂBdžRBd®Bd£×BdBd\Bd
+BdzáBdp€BdffBd\)BdQìBdG®Bd=qBd33Bd(öBd
+žBd{Bd
+=Bd  Bb
+BbzáBbp€BbffBb\)BbQìBbG®Bb=qBb33Bb(öBb
+žBb{Bb
+=Bb  BaõÃBaë
+BaáHBa×
+BaÌÍBaÂBažRBa®Ba£×BaBa\Ba
+BazáBap€BaffBa\)BaQìBaG®Ba=qBa33Ba(öBa
+žBa{Ba
+=Ba  B`õÃB`ë
+B`áHB`×
+B`ÌÍB`ÂB`žRB`®B`£×B`B`\B`
+B`záB`p€B`ffB`\)B`QìB`G®B`=qB`33B`(öB`
+žB`{B`
+=B`  B^
+B^záB^p€B^ffB^\)B^QìB^G®B^=qB^33B^(öB^
+žB^{B^
+=B^  B]õÃB]ë
+B]áHB]×
+B]ÌÍB]ÂB]žRB]®B]£×B]B]\B]
+B]záB]p€B]ffB]\)B]QìB]G®B]=qB]33B](öB]
+žB]{B]
+=B]  B\õÃB\ë
+B\áHB\×
+B\ÌÍB\ÂB\žRB\®B\£×B\B\\B\
+B\záB\p€B\ffB\\)B\QìB\G®B\=qB\33B\(öB\
+žB\{B\
+=B\  BZ
+BZzáBZp€BZffBZ\)BZQìBZG®BZ=qBZ33BZ(öBZ
+žBZ{BZ
+=BZ  BYõÃBYë
+BYáHBY×
+BYÌÍBYÂBYžRBY®BY£×BYBY\BY
+BYzáBYp€BYffBY\)BYQìBYG®BY=qBY33BY(öBY
+žBY{BY
+=BY  BXõÃBXë
+BXáHBX×
+BXÌÍBXÂBXžRBX®BX£×BXBX\BX
+BXzáBXp€BXffBX\)BXQìBXG®BX=qBX33BX(öBX
+žBX{BX
+=BX  BV
+BVzáBVp€BVffBV\)BVQìBVG®BV=qBV33BV(öBV
+žBV{BV
+=BV  BUõÃBUë
+BUáHBU×
+BUÌÍBUÂBUžRBU®BU£×BUBU\BU
+BUzáBUp€BUffBU\)BUQìBUG®BU=qBU33BU(öBU
+žBU{BU
+=BU  BTõÃBTë
+BTáHBT×
+BTÌÍBTÂBTžRBT®BT£×BTBT\BT
+BTzáBTp€BTffBT\)BTQìBTG®BT=qBT33BT(öBT
+žBT{BT
+=BT  BR
+BRzáBRp€BRffBR\)BRQìBRG®BR=qBR33BR(öBR
+žBR{BR
+=BR  BQõÃBQë
+BQáHBQ×
+BQÌÍBQÂBQžRBQ®BQ£×BQBQ\BQ
+BQzáBQp€BQffBQ\)BQQìBQG®BQ=qBQ33BQ(öBQ
+žBQ{BQ
+=BQ  BPõÃBPë
+BPáHBP×
+BPÌÍBPÂBPžRBP®BP£×BPBP\BP
+BPzáBPp€BPffBP\)BPQìBPG®BP=qBP33BP(öBP
+žBP{BP
+=BP  BN
+BNzáBNp€BNffBN\)BNQìBNG®BN=qBN33BN(öBN
+žBN{BN
+=BN  BMõÃBMë
+BMáHBM×
+BMÌÍBMÂBMžRBM®BM£×BMBM\BM
+BMzáBMp€BMffBM\)BMQìBMG®BM=qBM33BM(öBM
+žBM{BM
+=BM  BLõÃBLë
+BLáHBL×
+BLÌÍBLÂBLžRBL®BL£×BLBL\BL
+BLzáBLp€BLffBL\)BLQìBLG®BL=qBL33BL(öBL
+žBL{BL
+=BL  BJ
+BJzáBJp€BJffBJ\)BJQìBJG®BJ=qBJ33BJ(öBJ
+žBJ{BJ
+=BJ  BIõÃBIë
+BIáHBI×
+BIÌÍBIÂBIžRBI®BI£×BIBI\BI
+BIzáBIp€BIffBI\)BIQìBIG®BI=qBI33BI(öBI
+žBI{BI
+=BI  BHõÃBHë
+BHáHBH×
+BHÌÍBHÂBHžRBH®BH£×BHBH\BH
+BHzáBHp€BHffBH\)BHQìBHG®BH=qBH33BH(öBH
+žBH{BH
+=BH  BF
+BFzáBFp€BFffBF\)BFQìBFG®BF=qBF33BF(öBF
+žBF{BF
+=BF  BEõÃBEë
+BEáHBE×
+BEÌÍBEÂBEžRBE®BE£×BEBE\BE
+BEzáBEp€BEffBE\)BEQìBEG®BE=qBE33BE(öBE
+žBE{BE
+=BE  BDõÃBDë
+BDáHBD×
+BDÌÍBDÂBDžRBD®BD£×BDBD\BD
+BDzáBDp€BDffBD\)BDQìBDG®BD=qBD33BD(öBD
+žBD{BD
+=BD  BB
+BBzáBBp€BBffBB\)BBQìBBG®BB=qBB33BB(öBB
+žBB{BB
+=BB  BAõÃBAë
+BAáHBA×
+BAÌÍBAÂBAžRBA®BA£×BABA\BA
+BAzáBAp€BAffBA\)BAQìBAG®BA=qBA33BA(öBA
+žBA{BA
+=BA  B@õÃB@ë
+B@áHB@×
+B@ÌÍB@ÂB@žRB@®B@£×B@B@\B@
+B@záB@p€B@ffB@\)B@QìB@G®B@=qB@33B@(öB@
+žB@{B@
+=B@  B>
+B>záB>p€B>ffB>\)B>QìB>G®B>=qB>33B>(öB>
+žB>{B>
+=B>  B=õÃB=ë
+B=áHB=×
+B=ÌÍB=ÂB=žRB=®B=£×B=B=\B=
+B=záB=p€B=ffB=\)B=QìB=G®B==qB=33B=(öB=
+žB={B=
+=B=  B<õÃB<ë
+B<áHB<×
+B<ÌÍB<ÂB<žRB<®B<£×B<B<\B<
+B<záB<p€B<ffB<\)B<QìB<G®B<=qB<33B<(öB<
+žB<{B<
+=B<  B:
+B:záB:p€B:ffB:\)B:QìB:G®B:=qB:33B:(öB:
+žB:{B:
+=B:  B9õÃB9ë
+B9áHB9×
+B9ÌÍB9ÂB9žRB9®B9£×B9B9\B9
+B9záB9p€B9ffB9\)B9QìB9G®B9=qB933B9(öB9
+žB9{B9
+=B9  B8õÃB8ë
+B8áHB8×
+B8ÌÍB8ÂB8žRB8®B8£×B8B8\B8
+B8záB8p€B8ffB8\)B8QìB8G®B8=qB833B8(öB8
+žB8{B8
+=B8  B6
+B6záB6p€B6ffB6\)B6QìB6G®B6=qB633B6(öB6
+žB6{B6
+=B6  B5õÃB5ë
+B5áHB5×
+B5ÌÍB5ÂB5žRB5®B5£×B5B5\B5
+B5záB5p€B5ffB5\)B5QìB5G®B5=qB533B5(öB5
+žB5{B5
+=B5  B4õÃB4ë
+B4áHB4×
+B4ÌÍB4ÂB4žRB4®B4£×B4B4\B4
+B4záB4p€B4ffB4\)B4QìB4G®B4=qB433B4(öB4
+žB4{B4
+=B4  B2
+B2záB2p€B2ffB2\)B2QìB2G®B2=qB233B2(öB2
+žB2{B2
+=B2  B1õÃB1ë
+B1áHB1×
+B1ÌÍB1ÂB1žRB1®B1£×B1B1\B1
+B1záB1p€B1ffB1\)B1QìB1G®B1=qB133B1(öB1
+žB1{B1
+=B1  B0õÃB0ë
+B0áHB0×
+B0ÌÍB0ÂB0žRB0®B0£×B0B0\B0
+B0záB0p€B0ffB0\)B0QìB0G®B0=qB033B0(öB0
+žB0{B0
+=B0  B.
+B.záB.p€B.ffB.\)B.QìB.G®B.=qB.33B.(öB.
+žB.{B.
+=B.  B-õÃB-ë
+B-áHB-×
+B-ÌÍB-ÂB-žRB-®B-£×B-B-\B-
+B-záB-p€B-ffB-\)B-QìB-G®B-=qB-33B-(öB-
+žB-{B-
+=B-  B,õÃB,ë
+B,áHB,×
+B,ÌÍB,ÂB,žRB,®B,£×B,B,\B,
+B,záB,p€B,ffB,\)B,QìB,G®B,=qB,33B,(öB,
+žB,{B,
+=B,  B*
+B*záB*p€B*ffB*\)B*QìB*G®B*=qB*33B*(öB*
+žB*{B*
+=B*  B)õÃB)ë
+B)áHB)×
+B)ÌÍB)ÂB)žRB)®B)£×B)B)\B)
+B)záB)p€B)ffB)\)B)QìB)G®B)=qB)33B)(öB)
+žB){B)
+=B)  B(õÃB(ë
+B(áHB(×
+B(ÌÍB(ÂB(žRB(®B(£×B(B(\B(
+B(záB(p€B(ffB(\)B(QìB(G®B(=qB(33B((öB(
+žB({B(
+=B(  B&
+B&záB&p€B&ffB&\)B&QìB&G®B&=qB&33B&(öB&
+žB&{B&
+=B&  B%õÃB%ë
+B%áHB%×
+B%ÌÍB%ÂB%žRB%®B%£×B%B%\B%
+B%záB%p€B%ffB%\)B%QìB%G®B%=qB%33B%(öB%
+žB%{B%
+=B%  B$õÃB$ë
+B$áHB$×
+B$ÌÍB$ÂB$žRB$®B$£×B$B$\B$
+B$záB$p€B$ffB$\)B$QìB$G®B$=qB$33B$(öB$
+žB${B$
+=B$  B"
+B"záB"p€B"ffB"\)B"QìB"G®B"=qB"33B"(öB"
+žB"{B"
+=B"  B!õÃB!ë
+B!áHB!×
+B!ÌÍB!ÂB!žRB!®B!£×B!B!\B!
+B!záB!p€B!ffB!\)B!QìB!G®B!=qB!33B!(öB!
+žB!{B!
+=B!  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B	õÃB	ë
+B	áHB	×
+B	ÌÍB	ÂB	žRB	®B	£×B	B	\B	
+B	záB	p€B	ffB	\)B	QìB	G®B	=qB	33B	(öB	
+žB	{B	
+=B	  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   Aý
+=AüõÃAüáHAüÌÍAüžRAü£×Aü\AüzáAüffAüQìAü=qAü(öAü{Aü  Aûë
+Aû×
+AûÂAû®AûAû
+Aûp€Aû\)AûG®Aû33Aû
+žAû
+=AúõÃAúáHAúÌÍAúžRAú£×Aú\AúzáAúffAúQìAú=qAú(öAú{Aú  Aùë
+Aù×
+AùÂAù®AùAù
+Aùp€Aù\)AùG®Aù33Aù
+žAù
+=AøõÃAøáHAøÌÍAøžRAø£×Aø\AøzáAøffAøQìAø=qAø(öAø{Aø  Aõ
+=AôõÃAôáHAôÌÍAôžRAô£×Aô\AôzáAôffAôQìAô=qAô(öAô{Aô  Aóë
+Aó×
+AóÂAó®AóAó
+Aóp€Aó\)AóG®Aó33Aó
+žAó
+=AòõÃAòáHAòÌÍAòžRAò£×Aò\AòzáAòffAòQìAò=qAò(öAò{Aò  Añë
+Añ×
+AñÂAñ®AñAñ
+Añp€Añ\)AñG®Añ33Añ
+žAñ
+=AðõÃAðáHAðÌÍAðžRAð£×Að\AðzáAðffAðQìAð=qAð(öAð{Að  Aí
+=AìõÃAìáHAìÌÍAìžRAì£×Aì\AìzáAìffAìQìAì=qAì(öAì{Aì  Aëë
+Aë×
+AëÂAë®AëAë
+Aëp€Aë\)AëG®Aë33Aë
+žAë
+=AêõÃAêáHAêÌÍAêžRAê£×Aê\AêzáAêffAêQìAê=qAê(öAê{Aê  Aéë
+Aé×
+AéÂAé®AéAé
+Aép€Aé\)AéG®Aé33Aé
+žAé
+=AèõÃAèáHAèÌÍAèžRAè£×Aè\AèzáAèffAèQìAè=qAè(öAè{Aè  Aå
+=AäõÃAäáHAäÌÍAäžRAä£×Aä\AäzáAäffAäQìAä=qAä(öAä{Aä  Aãë
+Aã×
+AãÂAã®AãAã
+Aãp€Aã\)AãG®Aã33Aã
+žAã
+=AâõÃAâáHAâÌÍAâžRAâ£×Aâ\AâzáAâffAâQìAâ=qAâ(öAâ{Aâ  Aáë
+Aá×
+AáÂAá®AáAá
+Aáp€Aá\)AáG®Aá33Aá
+žAá
+=AàõÃAàáHAàÌÍAàžRAà£×Aà\AàzáAàffAàQìAà=qAà(öAà{Aà  AÝ
+=AÜõÃAÜáHAÜÌÍAÜžRAÜ£×AÜ\AÜzáAÜffAÜQìAÜ=qAÜ(öAÜ{AÜ  AÛë
+AÛ×
+AÛÂAÛ®AÛAÛ
+AÛp€AÛ\)AÛG®AÛ33AÛ
+žAÛ
+=AÚõÃAÚáHAÚÌÍAÚžRAÚ£×AÚ\AÚzáAÚffAÚQìAÚ=qAÚ(öAÚ{AÚ  AÙë
+AÙ×
+AÙÂAÙ®AÙAÙ
+AÙp€AÙ\)AÙG®AÙ33AÙ
+žAÙ
+=AØõÃAØáHAØÌÍAØžRAØ£×AØ\AØzáAØffAØQìAØ=qAØ(öAØ{AØ  AÕ
+=AÔõÃAÔáHAÔÌÍAÔžRAÔ£×AÔ\AÔzáAÔffAÔQìAÔ=qAÔ(öAÔ{AÔ  AÓë
+AÓ×
+AÓÂAÓ®AÓAÓ
+AÓp€AÓ\)AÓG®AÓ33AÓ
+žAÓ
+=AÒõÃAÒáHAÒÌÍAÒžRAÒ£×AÒ\AÒzáAÒffAÒQìAÒ=qAÒ(öAÒ{AÒ  AÑë
+AÑ×
+AÑÂAÑ®AÑAÑ
+AÑp€AÑ\)AÑG®AÑ33AÑ
+žAÑ
+=AÐõÃAÐáHAÐÌÍAÐžRAÐ£×AÐ\AÐzáAÐffAÐQìAÐ=qAÐ(öAÐ{AÐ  AÍ
+=AÌõÃAÌáHAÌÌÍAÌžRAÌ£×AÌ\AÌzáAÌffAÌQìAÌ=qAÌ(öAÌ{AÌ  AËë
+AË×
+AËÂAË®AËAË
+AËp€AË\)AËG®AË33AË
+žAË
+=AÊõÃAÊáHAÊÌÍAÊžRAÊ£×AÊ\AÊzáAÊffAÊQìAÊ=qAÊ(öAÊ{AÊ  AÉë
+AÉ×
+AÉÂAÉ®AÉAÉ
+AÉp€AÉ\)AÉG®AÉ33AÉ
+žAÉ
+=AÈõÃAÈáHAÈÌÍAÈžRAÈ£×AÈ\AÈzáAÈffAÈQìAÈ=qAÈ(öAÈ{AÈ  AÅ
+=AÄõÃAÄáHAÄÌÍAÄžRAÄ£×AÄ\AÄzáAÄffAÄQìAÄ=qAÄ(öAÄ{AÄ  AÃë
+AÃ×
+AÃÂAÃ®AÃAÃ
+AÃp€AÃ\)AÃG®AÃ33AÃ
+žAÃ
+=AÂõÃAÂáHAÂÌÍAÂžRAÂ£×AÂ\AÂzáAÂffAÂQìAÂ=qAÂ(öAÂ{AÂ  AÁë
+AÁ×
+AÁÂAÁ®AÁAÁ
+AÁp€AÁ\)AÁG®AÁ33AÁ
+žAÁ
+=AÀõÃAÀáHAÀÌÍAÀžRAÀ£×AÀ\AÀzáAÀffAÀQìAÀ=qAÀ(öAÀ{AÀ  Aœ
+=AŒõÃAŒáHAŒÌÍAŒžRAŒ£×AŒ\AŒzáAŒffAŒQìAŒ=qAŒ(öAŒ{AŒ  A»ë
+A»×
+A»ÂA»®A»A»
+A»p€A»\)A»G®A»33A»
+žA»
+=AºõÃAºáHAºÌÍAºžRAº£×Aº\AºzáAºffAºQìAº=qAº(öAº{Aº  A¹ë
+A¹×
+A¹ÂA¹®A¹A¹
+A¹p€A¹\)A¹G®A¹33A¹
+žA¹
+=AžõÃAžáHAžÌÍAžžRAž£×Až\AžzáAžffAžQìAž=qAž(öAž{Až  Aµ
+=AŽõÃAŽáHAŽÌÍAŽžRAŽ£×AŽ\AŽzáAŽffAŽQìAŽ=qAŽ(öAŽ{AŽ  A³ë
+A³×
+A³ÂA³®A³A³
+A³p€A³\)A³G®A³33A³
+žA³
+=A²õÃA²áHA²ÌÍA²žRA²£×A²\A²záA²ffA²QìA²=qA²(öA²{A²  A±ë
+A±×
+A±ÂA±®A±A±
+A±p€A±\)A±G®A±33A±
+žA±
+=A°õÃA°áHA°ÌÍA°žRA°£×A°\A°záA°ffA°QìA°=qA°(öA°{A°  A­
+=A¬õÃA¬áHA¬ÌÍA¬žRA¬£×A¬\A¬záA¬ffA¬QìA¬=qA¬(öA¬{A¬  A«ë
+A«×
+A«ÂA«®A«A«
+A«p€A«\)A«G®A«33A«
+žA«
+=AªõÃAªáHAªÌÍAªžRAª£×Aª\AªzáAªffAªQìAª=qAª(öAª{Aª  A©ë
+A©×
+A©ÂA©®A©A©
+A©p€A©\)A©G®A©33A©
+žA©
+=AšõÃAšáHAšÌÍAšžRAš£×Aš\AšzáAšffAšQìAš=qAš(öAš{Aš  A¥
+=A€õÃA€áHA€ÌÍA€žRA€£×A€\A€záA€ffA€QìA€=qA€(öA€{A€  A£ë
+A£×
+A£ÂA£®A£A£
+A£p€A£\)A£G®A£33A£
+žA£
+=A¢õÃA¢áHA¢ÌÍA¢žRA¢£×A¢\A¢záA¢ffA¢QìA¢=qA¢(öA¢{A¢  A¡ë
+A¡×
+A¡ÂA¡®A¡A¡
+A¡p€A¡\)A¡G®A¡33A¡
+žA¡
+=A õÃA áHA ÌÍA žRA £×A \A záA ffA QìA =qA (öA {A   A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Az{Ayë
+AyÂAyAyp€AyG®Ay
+žAxõÃAxÌÍAx£×AxzáAxQìAx(öAx  Aw×
+Aw®Aw
+Aw\)Aw33Aw
+=AváHAvžRAv\AvffAv=qAv{Auë
+AuÂAuAup€AuG®Au
+žAtõÃAtÌÍAt£×AtzáAtQìAt(öAt  As×
+As®As
+As\)As33As
+=AráHAržRAr\ArffAr=qAr{Aqë
+AqÂAqAqp€AqG®Aq
+žApõÃApÌÍAp£×ApzáApQìAp(öAp  Aj{Aië
+AiÂAiAip€AiG®Ai
+žAhõÃAhÌÍAh£×AhzáAhQìAh(öAh  Ag×
+Ag®Ag
+Ag\)Ag33Ag
+=AfáHAfžRAf\AfffAf=qAf{Aeë
+AeÂAeAep€AeG®Ae
+žAdõÃAdÌÍAd£×AdzáAdQìAd(öAd  Ac×
+Ac®Ac
+Ac\)Ac33Ac
+=AbáHAbžRAb\AbffAb=qAb{Aaë
+AaÂAaAap€AaG®Aa
+žA`õÃA`ÌÍA`£×A`záA`QìA`(öA`  AZ{AYë
+AYÂAYAYp€AYG®AY
+žAXõÃAXÌÍAX£×AXzáAXQìAX(öAX  AW×
+AW®AW
+AW\)AW33AW
+=AVáHAVžRAV\AVffAV=qAV{AUë
+AUÂAUAUp€AUG®AU
+žATõÃATÌÍAT£×ATzáATQìAT(öAT  AS×
+AS®AS
+AS\)AS33AS
+=ARáHARžRAR\ARffAR=qAR{AQë
+AQÂAQAQp€AQG®AQ
+žAPõÃAPÌÍAP£×APzáAPQìAP(öAP  AJ{AIë
+AIÂAIAIp€AIG®AI
+žAHõÃAHÌÍAH£×AHzáAHQìAH(öAH  AG×
+AG®AG
+AG\)AG33AG
+=AFáHAFžRAF\AFffAF=qAF{AEë
+AEÂAEAEp€AEG®AE
+žADõÃADÌÍAD£×ADzáADQìAD(öAD  AC×
+AC®AC
+AC\)AC33AC
+=ABáHABžRAB\ABffAB=qAB{AAë
+AAÂAAAAp€AAG®AA
+žA@õÃA@ÌÍA@£×A@záA@QìA@(öA@  A:{A9ë
+A9ÂA9A9p€A9G®A9
+žA8õÃA8ÌÍA8£×A8záA8QìA8(öA8  A7×
+A7®A7
+A7\)A733A7
+=A6áHA6žRA6\A6ffA6=qA6{A5ë
+A5ÂA5A5p€A5G®A5
+žA4õÃA4ÌÍA4£×A4záA4QìA4(öA4  A3×
+A3®A3
+A3\)A333A3
+=A2áHA2žRA2\A2ffA2=qA2{A1ë
+A1ÂA1A1p€A1G®A1
+žA0õÃA0ÌÍA0£×A0záA0QìA0(öA0  A*{A)ë
+A)ÂA)A)p€A)G®A)
+žA(õÃA(ÌÍA(£×A(záA(QìA((öA(  A'×
+A'®A'
+A'\)A'33A'
+=A&áHA&žRA&\A&ffA&=qA&{A%ë
+A%ÂA%A%p€A%G®A%
+žA$õÃA$ÌÍA$£×A$záA$QìA$(öA$  A#×
+A#®A#
+A#\)A#33A#
+=A"áHA"žRA"\A"ffA"=qA"{A!ë
+A!ÂA!A!p€A!G®A!
+žA õÃA ÌÍA £×A záA QìA (öA   A{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A
+{A	ë
+A	ÂA	A	p€A	G®A	
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žA õÃA ÌÍA £×A záA QìA (öA   @ô(ö@ó×
+@ó
+@ó33@òáH@ò\@ò=q@ñë
+@ñ@ñG®@ðõÂ@ð£×@ðQì@ð  @ï®@ï\)@ï
+=@îžR@îff@î{@íÂ@íp€@í
+ž@ìÌÍ@ìzá@ì(ö@ë×
+@ë
+@ë33@êáH@ê\@ê=q@éë
+@é@éG®@èõÃ@è£×@èQì@è  @ç®@ç\)@ç
+=@æžR@æff@æ{@åÂ@åp€@å
+ž@äÌÍ@äzá@ä(ö@ã×
+@ã
+@ã33@âáH@â\@â=q@áë
+@á@áG®@àõÃ@à£×@àQì@à  @Ô(ö@Ó×
+@Ó
+@Ó33@ÒáH@Ò\@Ò=q@Ñë
+@Ñ@ÑG®@ÐõÂ@Ð£×@ÐQì@Ð  @Ï®@Ï\)@Ï
+=@ÎžR@Îff@Î{@ÍÂ@Íp€@Í
+ž@ÌÌÍ@Ìzá@Ì(ö@Ë×
+@Ë
+@Ë33@ÊáH@Ê\@Ê=q@Éë
+@É@ÉG®@ÈõÃ@È£×@ÈQì@È  @Ç®@Ç\)@Ç
+=@ÆžR@Æff@Æ{@ÅÂ@Åp€@Å
+ž@ÄÌÍ@Äzá@Ä(ö@Ã×
+@Ã
+@Ã33@ÂáH@Â\@Â=q@Áë
+@Á@ÁG®@ÀõÃ@À£×@ÀQì@À  @Ž(ö@³×
+@³
+@³33@²áH@²\@²=q@±ë
+@±@±G®@°õÂ@°£×@°Qì@°  @¯®@¯\)@¯
+=@®žR@®ff@®{@­Â@­p€@­
+ž@¬ÌÍ@¬zá@¬(ö@«×
+@«
+@«33@ªáH@ª\@ª=q@©ë
+@©@©G®@šõÃ@š£×@šQì@š  @§®@§\)@§
+=@ŠžR@Šff@Š{@¥Â@¥p€@¥
+ž@€ÌÍ@€zá@€(ö@£×
+@£
+@£33@¢áH@¢\@¢=q@¡ë
+@¡@¡G®@ õÃ@ £×@ Qì@   @(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÂ@£×@Qì@  @®@\)@
+=@žR@ff@{@Â@p€@
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @®@\)@
+=@žR@ff@{@
+Â@
+p€@
+
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @hQì@g®@g
+>@fff@eÂ@e
+ž@dzá@c×
+@c33@b\@aë
+@aG®@`£×@`  @_\)@^žR@^{@]p€@\ÌÍ@\(ö@[
+@ZáH@Z=q@Y@XõÂ@XQì@W®@W
+>@Vff@UÂ@U
+ž@Tzá@S×
+@S33@R\@Që
+@QG®@P£×@P  @O\)@NžR@N{@Mp€@LÌÍ@L(ö@K
+@JáH@J=q@I@HõÃ@HQì@G®@G
+=@Fff@EÂ@E
+ž@Dzá@C×
+@C33@B\@Aë
+@AG®@@£×@@  @(Qì@'®@'
+>@&ff@%Â@%
+ž@$zá@#×
+@#33@"\@!ë
+@!G®@ £×@   @\)@
+žR@
+{@
+p€@
+ÌÍ@
+(ö@
+@áH@=q@@õÂ@Qì@®@
+>@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@£×@  @\)@žR@{@
+p€@
+ÌÍ@
+(ö@
+
+@
+áH@
+=q@	@õÃ@Qì@®@
+=@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@ £×@   ?Ð£×?Ï\)?Î{?ÌÌÍ?Ë
+
+?Ê=p?ÈõÂ?Ç®?Æff?Å
+ž?Ã×
+?Â\?ÁG®?À  ?ŸžR?œp€?Œ(ö?ºáH?¹?žQì?·
+>?µÂ?Žzá?³33?±ë
+?°£×?¯\)?®{?¬ÌÍ?«
+?ª=q?šõÂ?§®?Šff?¥
+ž?£×
+?¢\?¡G®?   ?žR?p€?(ö?áH??Qì?
+>?Â?zá?33?ë
+?£×?\)?{?ÌÍ?
+?=q?õÃ?®?ff?
+
+ž?×
+?\?G®?  ?!G®?
+žR?
+(ö??
+=?zá?ë
+?\)?
+ÌÍ?
+=q?®?
+ž?\?   >úáH>õÂ>ð£×>ë
+>æff>áG®>Ü(ö>×
+=>Ñë
+>ÌÌÍ>Ç®>Â\>œp€>žQì>³33>®{>šõÃ>£×
+>žR>>zá>\)>=q>
+
+ž>  >uÂ>k
+>aG®>W
+=>LÌÍ>B\>8Qì>.{>#×
+>>\)>
+ž=õÂ=áG®=ÌÌÍ=žQì=£×
+=\)=uÂ=LÌÍ=#×
+<õÂ<£×
+<#×
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bz
+B~zá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bv
+BzzáBzzáB~p€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bn
+BrzáBvzáBzp€BzffB~\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bj
+BnzáBnzáBrp€BvffBz\)Bz\)B~Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bf
+BfzáBjzáBnp€BnffBr\)Bv\)BzQìBzG®B~G®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B^
+BbzáBfzáBfp€BjffBn\)Bn\)BrQìBvG®BzG®Bz=qB~33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BZ
+BZzáB^záBbp€BfffBf\)Bj\)BnQìBnG®BrG®Bv=qBz33Bz(öB~(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BR
+BVzáBZzáBZp€B^ffBb\)Bf\)BfQìBjG®BnG®Bn=qBr33Bv(öBz(öBz
+žB~{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BN
+BRzáBRzáBVp€BZffBZ\)B^\)BbQìBfG®BfG®Bj=qBn33Bn(öBr(öBv
+žBz{Bz{B~
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BJ
+BJzáBNzáBRp€BRffBV\)BZ\)BZQìB^G®BbG®Bf=qBf33Bj(öBn(öBn
+žBr{Bv{Bz
+=Bz  B~  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BB
+BFzáBJzáBJp€BNffBR\)BR\)BVQìBZG®BZG®B^=qBb33Bf(öBf(öBj
+žBn{Bn{Br
+=Bv  Bz  ByõÃB}ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B>
+B>záBBzáBFp€BJffBJ\)BN\)BRQìBRG®BVG®BZ=qBZ33B^(öBb(öBf
+žBf{Bj{Bn
+=Bn  Br  BuõÃByë
+ByáHB}áH¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B6
+B:záB>záB>p€BBffBF\)BJ\)BJQìBNG®BRG®BR=qBV33BZ(öBZ(öB^
+žBb{Bf{Bf
+=Bj  Bn  BmõÃBqë
+BuáHByáHBy×
+B}ÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B2
+B6záB6záB:p€B>ffB>\)BB\)BFQìBJG®BJG®BN=qBR33BR(öBV(öBZ
+žBZ{B^{Bb
+=Bf  Bf  BiõÃBmë
+BmáHBqáHBu×
+ByÌÍByÌÍB}Â¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B*
+B.záB2záB6p€B6ffB:\)B>\)B>QìBBG®BFG®BJ=qBJ33BN(öBR(öBR
+žBV{BZ{BZ
+=B^  Bb  BeõÃBeë
+BiáHBmáHBm×
+BqÌÍBuÌÍByÂByžRB}®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B&
+B*záB*záB.p€B2ffB6\)B6\)B:QìB>G®B>G®BB=qBF33BJ(öBJ(öBN
+žBR{BR{BV
+=BZ  BZ  B]õÃBaë
+BeáHBeáHBi×
+BmÌÍBmÌÍBqÂBužRBy®By®B}£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B"
+B"záB&záB*p€B*ffB.\)B2\)B6QìB6G®B:G®B>=qB>33BB(öBF(öBJ
+žBJ{BN{BR
+=BR  BV  BYõÃBYë
+B]áHBaáHBe×
+BeÌÍBiÌÍBmÂBmžRBq®Bu®By£×ByB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+B
+záB"záB"p€B&ffB*\)B*\)B.QìB2G®B6G®B6=qB:33B>(öB>(öBB
+žBF{BJ{BJ
+=BN  BR  BQõÃBUë
+BYáHBYáHB]×
+BaÌÍBeÌÍBeÂBižRBm®Bm®Bq£×BuByBy\B}
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáBzáB
+p€B"ffB"\)B&\)B*QìB*G®B.G®B2=qB633B6(öB:(öB>
+žB>{BB{BF
+=BJ  BJ  BMõÃBQë
+BQáHBUáHBY×
+BYÌÍB]ÌÍBaÂBežRBe®Bi®Bm£×BmBqBu\By
+By
+B}zá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáBzáBp€BffB
+\)B"\)B"QìB&G®B*G®B*=qB.33B2(öB6(öB6
+žB:{B>{B>
+=BB  BF  BIõÃBIë
+BMáHBQáHBQ×
+BUÌÍBYÌÍBYÂB]žRBa®Be®Be£×BiBmBm\Bq
+Bu
+ByzáByp€B}ff¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+
+BzáBzáBp€BffB\)B\)B
+QìB"G®B"G®B&=qB*33B*(öB.(öB2
+žB6{B6{B:
+=B>  B>  BAõÃBEë
+BIáHBIáHBM×
+BQÌÍBQÌÍBUÂBYžRBY®B]®Ba£×BeBeBi\Bm
+Bm
+BqzáBup€ByffByffB}\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáB
+záBp€BffB\)B\)BQìBG®B
+G®B"=qB"33B&(öB*(öB*
+žB.{B2{B6
+=B6  B:  B=õÃB=ë
+BAáHBEáHBI×
+BIÌÍBMÌÍBQÂBQžRBU®BY®BY£×B]BaBe\Be
+Bi
+BmzáBmp€BqffBuffBy\)ByQìB}Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aý
+=BzáBzáBp€B
+ffB\)B\)BQìBG®BG®B=qB
+33B"(öB"(öB&
+žB*{B*{B.
+=B2  B6  B5õÃB9ë
+B=áHB=áHBA×
+BEÌÍBIÌÍBIÂBMžRBQ®BQ®BU£×BYBYB]\Ba
+Be
+BezáBip€BmffBmffBq\)BuQìByQìByG®B}=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aõ
+=AôõÃAüõÃBp€BffB\)B
+\)BQìBG®BG®B=qB33B(öB
+(öB"
+žB"{B&{B*
+=B*  B.  B1õÃB5ë
+B5áHB9áHB=×
+B=ÌÍBAÌÍBEÂBIžRBI®BM®BQ£×BQBUBY\BY
+B]
+BazáBep€BeffBiffBm\)BmQìBqQìBuG®By=qBy33B}33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aå
+=AìõÃAôõÃAôáHAüÌÍB\)B\)BQìB
+G®BG®B=qB33B(öB(öB
+žB
+{B"{B"
+=B&  B*  B)õÃB-ë
+B1áHB5áHB5×
+B9ÌÍB=ÌÍB=ÂBAžRBE®BI®BI£×BMBQBQ\BU
+BY
+BYzáB]p€BaffBeffBe\)BiQìBmQìBmG®Bq=qBu33By33By(öB}
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÝ
+=AäõÃAäõÃAìáHAôÌÍAôžRAüžRBQìBG®BG®B
+=qB33B(öB(öB
+žB{B{B
+
+=B"  B"  B%õÃB)ë
+B)áHB-áHB1×
+B5ÌÍB5ÌÍB9ÂB=žRB=®BA®BE£×BIBIBM\BQ
+BQ
+BUzáBYp€BYffB]ffBa\)BeQìBeQìBiG®Bm=qBm33Bq33Bu(öBy
+žBy
+žB}{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÍ
+=AÔõÃAÜõÃAäáHAäÌÍAìžRAôžRAô£×Aü\BG®B=qB33B
+(öB(öB
+žB{B{B
+=B  B
+  B!õÃB!ë
+B%áHB)áHB)×
+B-ÌÍB1ÌÍB5ÂB5žRB9®B=®B=£×BABEBI\BI
+BM
+BQzáBQp€BUffBYffBY\)B]QìBaQìBeG®Be=qBi33Bm33Bm(öBq
+žBu
+žBy{By
+=B}  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÅ
+=AÌõÃAÌõÃAÔáHAÜÌÍAäžRAäžRAì£×Aô\Aô\AüzáB33B(öB(öB
+
+žB{B{B
+=B  B  BõÃB
+ë
+B!áHB!áHB%×
+B)ÌÍB)ÌÍB-ÂB1žRB5®B5®B9£×B=B=BA\BE
+BI
+BIzáBMp€BQffBQffBU\)BYQìBYQìB]G®Ba=qBe33Be33Bi(öBm
+žBm
+žBq{Bu
+=By  By  B|õÃ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aœ
+=AŒõÃAÄõÃAÌáHAÌÌÍAÔžRAÜžRAä£×Aä\Aì\AôzáAôffAüQìB(öB
+žB{B
+{B
+=B  B  BõÃBë
+BáHB
+áHB!×
+B!ÌÍB%ÌÍB)ÂB)žRB-®B1®B5£×B5B9B=\B=
+BA
+BEzáBIp€BIffBMffBQ\)BQQìBUQìBYG®BY=qB]33Ba33Be(öBe
+žBi
+žBm{Bm
+=Bq  Bu  BxõÃBxë
+B|ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A­
+=AŽõÃAŒõÃAŒáHAÄÌÍAÌžRAÌžRAÔ£×AÜ\Aä\AäzáAìffAôQìAôQìAü=qB{B{B
+=B
+  B  B
+õÃBë
+BáHBáHB×
+B
+ÌÍB!ÌÍB!ÂB%žRB)®B)®B-£×B1B5B5\B9
+B=
+B=záBAp€BEffBIffBI\)BMQìBQQìBQG®BU=qBY33BY33B](öBa
+žBe
+žBe{Bi
+=Bm  Bm  BpõÃBtë
+Bxë
+BxáHB|×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A¥
+=A¬õÃA¬õÃAŽáHAŒÌÍAŒžRAÄžRAÌ£×AÌ\AÔ\AÜzáAäffAäQìAìQìAô=qAô(öAü(öB
+=B  B  B	õÃB
+ë
+B
+áHBáHB×
+BÌÍBÌÍB
+ÂB!žRB!®B%®B)£×B)B-B1\B5
+B5
+B9záB=p€B=ffBAffBE\)BIQìBIQìBMG®BQ=qBQ33BU33BY(öBY
+žB]
+žBa{Be
+=Be  Bi  BlõÃBlë
+Bpë
+BtáHBx×
+Bx×
+B|ÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+=AõÃA€õÃA¬áHA¬ÌÍAŽžRAŒžRAŒ£×AÄ\AÌ\AÌzáAÔffAÜQìAäQìAä=qAì(öAô(öAô{Aü  B  BõÃBë
+B	áHB
+áHB
+×
+BÌÍBÌÍBÂBžRB
+®B!®B!£×B%B)B)\B-
+B1
+B5záB5p€B9ffB=ffB=\)BAQìBEQìBIG®BI=qBM33BQ33BQ(öBU
+žBY
+žBY{B]
+=Ba  Be  BdõÃBhë
+Blë
+BláHBp×
+Bt×
+BxÌÍBxÂB|žR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+=AõÃAõÃAáHA€ÌÍA¬žRA¬žRAŽ£×AŒ\AŒ\AÄzáAÌffAÌQìAÔQìAÜ=qAä(öAä(öAì{Aô  Aô  Aûë
+Bë
+BáHBáHB	×
+B
+ÌÍB
+ÌÍBÂBžRB®B®B
+£×B!B!B%\B)
+B)
+B-záB1p€B5ffB5ffB9\)B=QìB=QìBAG®BE=qBI33BI33BM(öBQ
+žBQ
+žBU{BY
+=BY  B]  B`õÃBdë
+Bdë
+BháHBl×
+Bl×
+BpÌÍBtÂBxžRBxžRB|®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+
+=AõÃAõÃAáHAÌÍAžRA€žRA¬£×A¬\AŽ\AŒzáAŒffAÄQìAÌQìAÌ=qAÔ(öAÜ(öAä{Aä  Aì  Aóë
+Aó×
+AûÂBáHB×
+BÌÍB	ÌÍB
+ÂB
+žRB®B®B£×BB
+B!\B!
+B%
+B)záB)p€B-ffB1ffB5\)B5QìB9QìB=G®B==qBA33BE33BI(öBI
+žBM
+žBQ{BQ
+=BU  BY  BXõÃB\ë
+B`ë
+BdáHBd×
+Bh×
+BlÌÍBlÂBpžRBtžRBx®Bx£×B|£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aj{Ayë
+AõÃAáHAÌÍAžRAžRA£×A€\A¬\A¬záAŽffAŒQìAŒQìAÄ=qAÌ(öAÌ(öAÔ{AÜ  Aä  Aãë
+Aë×
+AóÂAóÂAû®BÌÍBÌÍBÂB	žRB
+®B
+®B£×BBB\B
+
+B!
+B!záB%p€B)ffB)ffB-\)B1QìB5QìB5G®B9=qB=33B=33BA(öBE
+žBI
+žBI{BM
+=BQ  BQ  BTõÃBXë
+BXë
+B\áHB`×
+Bd×
+BdÌÍBhÂBlžRBlžRBp®Bt£×Bx£×BxB|\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AZ{AYë
+Aië
+AyÂAÌÍAžRAžRA£×A\A\A€záA¬ffA¬QìAŽQìAŒ=qAŒ(öAÄ(öAÌ{AÌ  AÔ  AÛë
+Aã×
+AãÂAëÂAó®AóAûBÂBžRB®B	®B
+£×B
+BB\B
+B
+B
+záB!p€B!ffB%ffB)\)B)QìB-QìB1G®B5=qB533B933B=(öB=
+žBA
+žBE{BI
+=BI  BM  BPõÃBPë
+BTë
+BXáHBX×
+B\×
+B`ÌÍBdÂBdžRBhžRBl®Bl£×Bp£×BtBx\Bx
+B|
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A:{AIë
+AYë
+AYÂAiAyp€AžRA£×A\A\AzáAffA€QìA¬QìA¬=qAŽ(öAŒ(öAŒ{AÄ  AÌ  AËë
+AÓ×
+AÛÂAãÂAã®AëAóAó
+Aûp€B®B®B£×B	B
+B
+\B
+B
+BzáBp€B
+ffB!ffB!\)B%QìB)QìB)G®B-=qB133B533B5(öB9
+žB=
+žB={BA
+=BE  BI  BHõÃBLë
+BPë
+BPáHBT×
+BX×
+BXÌÍB\ÂB`žRBdžRBd®Bh£×Bl£×BlBp\Bt
+Bx
+BxzáB|p€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A*{A9ë
+A9ë
+AIÂAYAYp€Aip€AyG®A\A\AzáAffAQìAQìA€=qA¬(öA¬(öAŽ{AŒ  AŒ  AÃë
+AË×
+AËÂAÓÂAÛ®AãAãAë
+Aóp€Aó\)Aû\)B£×BBB	\B
+
+B
+
+BzáBp€BffBffB
+\)B!QìB!QìB%G®B)=qB)33B-33B1(öB5
+žB5
+žB9{B=
+=B=  BA  BDõÃBHë
+BHë
+BLáHBP×
+BP×
+BTÌÍBXÂBXžRB\žRB`®Bd£×Bd£×BhBl\Bl
+Bp
+BtzáBxp€Bxp€B|ff¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A{Aë
+A)ë
+A9ÂA9AIp€AYp€AYG®Ai
+žAy
+žAzáAffAQìAQìA=qA(öA€(öA¬{A¬  AŽ  A»ë
+A»×
+AÃÂAËÂAË®AÓAÛAã
+Aãp€Aë\)Aó\)AóG®Aû33BB\B
+B	
+B
+záB
+p€BffBffB\)BQìB
+QìB!G®B!=qB%33B)33B)(öB-
+žB1
+žB5{B5
+=B9  B=  B<õÃB@ë
+BDë
+BHáHBH×
+BL×
+BPÌÍBPÂBTžRBXžRBX®B\£×B`£×BdBd\Bh
+Bl
+BlzáBpp€Btp€BxffBx\)B|\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @ô(öA	ë
+Aë
+AÂA)A9p€A9p€AIG®AY
+žAY
+žAhõÃAxÌÍAQìAQìA=qA(öA(öA{A€  A¬  A«ë
+A³×
+A»ÂA»ÂAÃ®AËAËAÓ
+AÛp€Aã\)Aã\)AëG®Aó33Aó33Aû
+žB
+B
+BzáB	p€B
+ffB
+ffB\)BQìBQìBG®B
+=qB!33B!33B%(öB)
+žB)
+žB-{B1
+=B5  B5  B8õÃB<ë
+B<ë
+B@áHBD×
+BH×
+BHÌÍBLÂBPžRBPžRBT®BX£×BX£×B\B`\Bd
+Bd
+BhzáBlp€Blp€BpffBt\)Bx\)BxQìB|G®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @Ô(ö@Ó×
+@ó×
+A	ÂAAp€A)p€A9G®A9
+žAI
+žAXõÃAXÌÍAh£×Ax£×A=qA(öA(öA{A  A  A£ë
+A«×
+A«ÂA³ÂA»®A»AÃAË
+AËp€AÓ\)AÛ\)AãG®Aã33Aë33Aó
+žAó
+=Aû
+=BzáBp€BffB	ffB
+\)B
+QìBQìBG®B=qB33B
+33B!(öB!
+žB%
+žB){B)
+=B-  B1  B4õÃB4ë
+B8ë
+B<áHB<×
+B@×
+BDÌÍBHÂBHžRBLžRBP®BP£×BT£×BXBX\B\
+B`
+BdzáBdp€Bhp€BlffBl\)Bp\)BtQìBxG®Bx=qB|=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @(ö@³×
+@Ó×
+@Ó
+@ó33A	p€Ap€AG®A)
+žA9
+žA8õÃAHÌÍAX£×AX£×AhzáAxQìA(öA{A  A  Aë
+A×
+A£ÂA«ÂA«®A³A»A»
+AÃp€AË\)AË\)AÓG®AÛ33Aã33Aã
+žAë
+=Aó
+=AòõÃAúáHBffBffB\)B	QìB
+QìB
+G®B=qB33B33B(öB
+
+žB!
+žB!{B%
+=B)  B)  B,õÃB0ë
+B4ë
+B4áHB8×
+B<×
+B<ÌÍB@ÂBDžRBHžRBH®BL£×BP£×BPBT\BX
+BX
+B\záB`p€Bdp€BdffBh\)Bl\)BlQìBpG®Bt=qBx=qBx33B|(ö¿  ¿  ¿  ¿  ¿  ¿  @hQì@×
+@×
+@³
+@Ó33@ÒáH@òáHA	G®A
+žA
+žA(õÃA8ÌÍA8£×AH£×AXzáAXQìAhQìAx(öA  A  Aë
+A×
+AÂAÂA£®A«A«A³
+A»p€A»\)AÃ\)AËG®AË33AÓ33AÛ
+žAã
+=Aã
+=AêõÃAòáHAòÌÍAúÌÍB\)BQìBQìB	G®B
+=qB
+33B33B(öB
+žB
+žB
+{B!
+=B!  B%  B(õÃB(ë
+B,ë
+B0áHB4×
+B4×
+B8ÌÍB<ÂB<žRB@žRBD®BH£×BH£×BLBP\BP
+BT
+BXzáBXp€B\p€B`ffBd\)Bd\)BhQìBlG®Bl=qBp=qBt33Bx(öBx(öB|
+ž¿  ¿  ¿  ¿  ?Ð£×@'®@g®@
+@33@²áH@ÒáH@Ò\@ò=qA	
+žAõÃAÌÍA(£×A8£×A8záAHQìAXQìAX(öAh  Ax  Aë
+A×
+AÂAÂA®AA£A«
+A«p€A³\)A»\)A»G®AÃ33AË33AË
+žAÓ
+=AÛ
+=AâõÃAâáHAêÌÍAòÌÍAòžRAú£×BQìBG®B=qB	33B
+33B
+(öB
+žB
+žB{B
+=B
+  B!  B õÃB$ë
+B(ë
+B(áHB,×
+B0×
+B4ÌÍB4ÂB8žRB<žRB<®B@£×BD£×BHBH\BL
+BP
+BPzáBTp€BXp€BXffB\\)B`\)BdQìBdG®Bh=qBl=qBl33Bp(öBt(öBx
+žBx{B|
+=¿  ¿  ?!G®?Ï\)?Ï\)@'
+>@fff@áH@áH@²\@Ò=q@Ò=q@ñë
+AÌÍA£×A£×A(záA8QìA8QìAH(öAX  AX  Ag×
+Aw®AÂAÂA®AAA
+A£p€A«\)A«\)A³G®A»33A»33AÃ
+žAË
+=AË
+=AÒõÃAÚáHAâÌÍAâÌÍAêžRAò£×Aò£×Aú\B=qB33B33B	(öB
+
+žB
+
+žB{B
+=B  B  B
+õÃB ë
+B ë
+B$áHB(×
+B(×
+B,ÌÍB0ÂB4žRB4žRB8®B<£×B<£×B@BD\BH
+BH
+BLzáBPp€BPp€BTffBX\)BX\)B\QìB`G®Bd=qBd=qBh33Bl(öBl(öBp
+žBt{Bx
+=Bx
+=B|  ¿  ¿  ?
+žR?Î{?ÌÌÍ@%Â@eÂ@\@=q@²=q@Ñë
+@Ñ@ñG®A£×AzáAQìA(QìA8(öA8  AH  AW×
+AW®Ag
+Aw
+A®AAA
+Ap€A\)A£\)A«G®A«33A³33A»
+žA»
+=AÃ
+=AÊõÃAÊáHAÒÌÍAÚÌÍAâžRAâ£×Aê£×Aò\AòzáAúffB33B(öB
+žB	
+žB
+{B
+=B  B  BõÃBë
+B
+ë
+B áHB ×
+B$×
+B(ÌÍB(ÂB,žRB0žRB4®B4£×B8£×B<B<\B@
+BD
+BHzáBHp€BLp€BPffBP\)BT\)BXQìBXG®B\=qB`=qBd33Bd(öBh(öBl
+žBl{Bp
+=Bt
+=Bx  ¿  ¿  ¿  ¿  ??Ë
+
+?Ë
+
+@%
+ž@dzá@=q@ë
+@±@ÑG®@ÑG®@ðõÂAQìAQìA(öA(  A8  A7×
+AG®AW
+AW
+Ag\)Aw33AA
+Ap€A\)A\)AG®A£33A«33A«
+žA³
+=A»
+=AºõÃAÂáHAÊÌÍAÊÌÍAÒžRAÚ£×Aâ£×Aâ\AêzáAòffAòffAúQìB
+žB
+žB{B	
+=B
+  B
+  BõÃBë
+Bë
+BáHB
+×
+B ×
+B ÌÍB$ÂB(žRB(žRB,®B0£×B4£×B4B8\B<
+B<
+B@záBDp€BHp€BHffBL\)BP\)BPQìBTG®BX=qBX=qB\33B`(öBd(öBd
+žBh{Bl
+=Bl
+=Bp  ¿  ¿  ¿  ¿  ¿  ¿  ?
+=?Ê=p?ÈõÂ@$zá@c×
+@@G®@±G®@ÐõÂ@Ð£×@ð£×A(öA  A  A'×
+A7®A7
+AG
+AW\)AW33Ag33Aw
+=Ap€A\)A\)AG®A33A33A£
+žA«
+=A«
+=A²õÃAºáHAºÌÍAÂÌÍAÊžRAÊ£×AÒ£×AÚ\AâzáAâffAêffAòQìAò=qAú=qB{B
+=B  B	  B
+õÃB
+ë
+Bë
+BáHB×
+B×
+B
+ÌÍB ÂB žRB$žRB(®B(£×B,£×B0B4\B4
+B8
+B<záB<p€B@p€BDffBH\)BH\)BLQìBPG®BP=qBT=qBX33BX(öB\(öB`
+žBd{Bd
+=Bh
+=Bl  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?ë
+?ÈõÂ?Ç®@#33@b\@G®@õÂ@°£×@Ð£×@ÐQì@ð  A  A×
+A®A'
+A7
+A7\)AG33AW33AW
+=AfáHAvžRA\)AG®A33A33A
+žA
+=A£
+=AªõÃAªáHA²ÌÍAºÌÍAºžRAÂ£×AÊ£×AÊ\AÒzáAÚffAâffAâQìAê=qAò=qAò(öAú{B  B  BõÃBë
+B
+ë
+B
+áHB×
+B×
+BÌÍBÂB
+žRB žRB ®B$£×B(£×B(B,\B0
+B4
+B4záB8p€B<p€B<ffB@\)BD\)BHQìBHG®BL=qBP=qBP33BT(öBX(öBX
+žB\{B`
+=Bd
+=Bd  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?\)?Æff?Å
+ž@"\@aë
+@£×@£×@°Qì@Ð  @Ð  @ï®A®A
+A
+A'\)A733A733AG
+=AVáHAVžRAfžRAv\A33A33A
+žA
+=A
+=AõÃA¢áHAªÌÍAªÌÍA²žRAº£×Aº£×AÂ\AÊzáAÊffAÒffAÚQìAâ=qAâ=qAê(öAò{Aò  Aú  B õÃBë
+Bë
+BáHB
+×
+B
+×
+BÌÍBÂBžRBžRB
+®B £×B £×B$B(\B(
+B,
+B0záB4p€B4p€B8ffB<\)B<\)B@QìBDG®BH=qBH=qBL33BP(öBP(öBT
+žBX{BX
+=B\
+=B`  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+=q?Å
+ž?Ã×
+@!G®@aG®@Qì@  @°  @Ï®@Ï\)@ï
+=A
+A\)A33A'33A7
+=A6áHAFžRAVžRAV\AfffAvffA
+žA
+=A
+=AõÃAáHAÌÍA¢ÌÍAªžRAª£×A²£×Aº\AºzáAÂffAÊffAÊQìAÒ=qAÚ=qAâ(öAâ{Aê  Aò  Añë
+Aù×
+B ë
+BáHB×
+B×
+B
+ÌÍB
+ÂBžRBžRB®B£×B
+£×B B \B$
+B(
+B(záB,p€B0p€B4ffB4\)B8\)B<QìB<G®B@=qBD=qBH33BH(öBL(öBP
+žBP{BT
+=BX
+=BX  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?®?Â\?Â\@ £×@`  @  @®@¯\)@Ï
+=@Ï
+=@îžRA33A33A
+=A&áHA6žRA6žRAF\AVffAVffAf=qAv{A
+=AõÃAáHAÌÍAÌÍAžRA¢£×Aª£×Aª\A²záAºffAºffAÂQìAÊ=qAÊ=qAÒ(öAÚ{Aâ  Aâ  Aéë
+Añ×
+Añ×
+AùÂB ×
+B×
+BÌÍBÂB
+žRB
+žRB®B£×B£×BB
+\B 
+B 
+B$záB(p€B(p€B,ffB0\)B4\)B4QìB8G®B<=qB<=qB@33BD(öBH(öBH
+žBL{BP
+=BP
+=BT  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+ž?ÁG®?À  @   @_\)@\)@
+=@¯
+=@ÎžR@Îff@îffA
+=AáHAžRA&žRA6\A6ffAFffAV=qAV{Af{Auë
+AáHAÌÍAÌÍAžRA£×A£×A¢\AªzáAªffA²ffAºQìAº=qAÂ=qAÊ(öAÊ{AÒ  AÚ  Aáë
+Aá×
+Aé×
+AñÂAñ®Aù®B ÌÍBÂBžRBžRB
+®B
+£×B£×BB\B
+B
+
+B záB p€B$p€B(ffB(\)B,\)B0QìB4G®B4=qB8=qB<33B<(öB@(öBD
+žBH{BH
+=BL
+=BP  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?   ?À  ?ŸžR@
+žR@^{@
+=@žR@®ff@Îff@Î{@íÂAžRAžRA\A&ffA6ffA6=qAF{AV{AUë
+AeÂAuAÌÍAžRA£×A£×A\AzáA¢ffAªffAªQìA²=qAº=qAº(öAÂ{AÊ  AÊ  AÑë
+AÙ×
+Aá×
+AáÂAé®Añ®AñAù
+B žRBžRB®B£×B
+£×B
+B\B
+B
+BzáB
+p€B p€B ffB$\)B(\)B(QìB,G®B0=qB4=qB433B8(öB<(öB<
+žB@{BD
+=BH
+=BH  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >úáH?œp€?Œ(ö@
+{@]p€@ff@ff@®{@ÍÂ@Íp€@íp€A\AffAffA&=qA6{A6{AEë
+AUÂAUAeAup€A£×A£×A\AzáAffAffA¢QìAª=qAª=qA²(öAº{Aº  AÂ  AÉë
+AÉ×
+AÑ×
+AÙÂAá®Aá®AéAñ
+Añp€Aùp€B ®B£×B£×BB
+\B
+
+B
+BzáBp€Bp€B
+ffB \)B \)B$QìB(G®B(=qB,=qB033B4(öB4(öB8
+žB<{B<
+=B@
+=BD  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ð£×?Œ(ö?ºáH@
+ÌÍ@\ÌÍ@{@Â@­p€@Íp€@Í
+ž@ìÌÍAffA=qA{A&{A5ë
+A5ÂAEAUAUp€AeG®AuG®A\AzáAffAffAQìA=qA¢=qAª(öAª{A²  Aº  A¹ë
+AÁ×
+AÉ×
+AÉÂAÑ®AÙ®AáAá
+Aép€Añp€Añ\)AùG®B £×BB\B
+B
+
+B
+záBp€Bp€BffB\)B
+\)B QìB G®B$=qB(=qB(33B,(öB0(öB4
+žB4{B8
+=B<
+=B<  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ë
+?¹?¹@
+(ö@[
+@p€@p€@­
+ž@ÌÌÍ@ÌÌÍ@ìzáA{A{Aë
+A%ÂA5A5AEp€AUG®AUG®Ae
+žAtõÃAffAffAQìA=qA=qA(öA¢{Aª  Aª  A±ë
+A¹×
+A¹×
+AÁÂAÉ®AÉ®AÑAÙ
+Aáp€Aáp€Aé\)AñG®AñG®Aù33B \B
+B
+BzáB
+p€B
+p€BffB\)B\)BQìB
+G®B =qB =qB$33B((öB((öB,
+žB0{B4
+=B4
+=B8  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >æff?žQì?·
+>@áH@ZáH@
+ž@ÌÍ@¬ÌÍ@Ìzá@Ì(ö@ì(öAë
+AÂAA%A5p€A5G®AEG®AU
+žATõÃAdÌÍAtÌÍAQìA=qA=qA(öA{A  A¢  A©ë
+A©×
+A±×
+A¹ÂA¹®AÁ®AÉAÉ
+AÑp€AÙp€Aá\)AáG®AéG®Añ33Añ
+žAù
+=B 
+BzáBp€Bp€B
+ffB
+\)B\)BQìBG®B=qB
+=qB 33B (öB$(öB(
+žB({B,
+=B0
+=B4  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ü(ö?µÂ?µÂ@=q@Y@ÌÍ@zá@¬(ö@Ì(ö@Ë×
+@ë
+AAAp€A%G®A5G®A5
+žADõÃATÌÍATÌÍAd£×AtzáA=qA(öA{A  A  Aë
+A¡×
+A©×
+A©ÂA±®A¹®A¹AÁ
+AÉp€AÉp€AÑ\)AÙG®AáG®Aá33Aé
+žAñ
+=Añ
+=AøõÃB p€Bp€BffB\)B
+\)B
+QìBG®B=qB=qB33B
+(öB (öB 
+žB${B(
+=B(
+=B,  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >×
+=?Žzá?³33@@XõÂ@(ö@(ö@«×
+@Ë
+@Ë33@ë33Ap€AG®AG®A%
+žA4õÃA4ÌÍADÌÍAT£×ATzáAdzáAtQìA{A  A  Aë
+A×
+A×
+A¡ÂA©®A©®A±A¹
+A¹p€AÁp€AÉ\)AÉG®AÑG®AÙ33Aá
+žAá
+=Aé
+=AðõÃAðáHAøáHB ffB\)B\)BQìB
+G®B
+=qB=qB33B(öB(öB
+
+žB {B 
+=B$
+=B(  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ÌÌÍ?³33?±ë
+@Qì@XQì@×
+@
+@«33@Ë33@ÊáH@ê\AG®A
+žAõÃA$ÌÍA4ÌÍA4£×ADzáATzáATQìAd(öAt  A  Aë
+A×
+A×
+AÂA®A¡®A©A©
+A±p€A¹p€A¹\)AÁG®AÉG®AÉ33AÑ
+žAÙ
+=Aá
+=AàõÃAèáHAðáHAðÌÍAøžRB \)BQìBG®B=qB
+=qB
+33B(öB(öB
+žB{B
+
+=B 
+=B   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ç®?°£×?°£×@®@W
+>@33@33@ªáH@Ê\@Ê\@ê=qAõÃAÌÍAÌÍA$£×A4záA4záADQìAT(öAT  Ad  As×
+A×
+A×
+AÂA®A®AA¡
+A©p€A©p€A±\)A¹G®A¹G®AÁ33AÉ
+žAÉ
+=AÑ
+=AØõÃAàáHAàáHAèÌÍAðžRAðžRAø£×B G®B=qB=qB33B
+(öB
+(öB
+žB{B
+=B
+=B
+  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Â\?¯\)?®{@ff@Vff@áH@\@ª\@Ê=q@Éë
+@éAÌÍA£×AzáA$záA4QìA4(öAD  AT  AS×
+Ac®As®AÂA®A®AA
+Ap€A¡p€A©\)A©G®A±G®A¹33A¹
+žAÁ
+=AÉ
+=AÈõÃAÐáHAØáHAàÌÍAàžRAèžRAð£×Að\AøzáB =qB33B(öB(öB
+
+žB
+{B
+=B
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >žQì?¬ÌÍ?¬ÌÍ@Â@U
+ž@\@=q@©ë
+@É@É@éG®AzáAzáAQìA$(öA4  A4  AC×
+AS®AS®Ac
+As\)A®AA
+Ap€Ap€A\)A¡G®A©G®A©33A±
+žA¹
+=A¹
+=AÀõÃAÈáHAÈáHAÐÌÍAØžRAàžRAà£×Aè\AðzáAðzáAøffB (öB(öB
+žB{B
+
+=B
+
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >³33?«
+?ª=q@
+ž@Tzá@ë
+@@©@ÉG®@ÈõÃ@èõÃAQìA(öA  A$  A3×
+A3®AC®AS
+AS\)Ac\)As33A
+Ap€Ap€A\)AG®AG®A¡33A©
+žA©
+=A±
+=AžõÃAžáHAÀáHAÈÌÍAÈžRAÐžRAØ£×Aà\AàzáAèzáAðffAðQìAøQìB 
+žB{B
+=B
+=B
+  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >šõÃ?ª=q?šõÂ@×
+@S33@@G®@šõÃ@ÈõÃ@È£×@èQìA  A  A×
+A#®A3®A3
+AC\)AS\)AS33Ac
+=AráHAp€A\)AG®AG®A33A
+žA¡
+=A©
+=AšõÃA°áHAžáHAžÌÍAÀžRAÈžRAÈ£×AÐ\AØzáAàzáAàffAèQìAðQìAð=qAø(öB 
+=B
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >£×
+?§®?Šff@33@R\@õÃ@õÃ@š£×@ÈQì@È  @è  A×
+A®A®A#
+A3\)A3\)AC33AS
+=ARáHAbáHAržRAG®AG®A33A
+žA
+=A
+=A õÃAšáHAšáHA°ÌÍAžžRAžžRAÀ£×AÈ\AÈzáAÐzáAØffAàQìAàQìAè=qAð(öAð{Aø{B   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >?Šff?¥
+ž@ë
+@Që
+@£×@Qì@š  @È  @Ç®@ç\)A®A
+A\)A#\)A333A3
+=ABáHARáHARžRAb\Ar\A33A
+žA
+=A
+=AõÃAáHA áHAšÌÍAšžRA°žRAž£×Až\AÀzáAÈzáAÈffAÐQìAØQìAà=qAà(öAè{Að{Að  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >zá?£×
+?£×
+@G®@P£×@  @  @§®@Ç\)@Ç\)@ç
+=A\)A\)A33A#
+=A2áHA2áHABžRAR\AR\AbffAr=qA
+=A
+=AõÃAáHAáHAÌÍA žRAšžRAš£×A°\AžzáAžzáAÀffAÈQìAÈQìAÐ=qAØ(öAà{Aà{Aè  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >\)?¢\?¡G®@  @P  @®@\)@§\)@Ç
+=@ÆžR@æžRA33A
+=AáHA"áHA2žRA2\AB\ARffAR=qAb{Ar{AõÃAáHAáHAÌÍAžRAžRA £×Aš\AšzáA°záAžffAžQìAÀQìAÈ=qAÈ(öAÐ{AØ{Aà  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >
+
+ž?   ?   @\)@NžR@\)@
+=@ŠžR@ÆžR@Æff@æ{AáHAáHAžRA"\A2\A2ffAB=qAR{AR{Aaë
+AqÂAáHAÌÍAžRAžRA£×A\A záAšzáAšffA°QìAžQìAž=qAÀ(öAÈ{AÈ{AÐ  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >  ?žR?p€@žR@N{@žR@žR@Šff@Æ{@ÅÂ@åÂAžRA\A\A"ffA2=qA2{AB{AQë
+AQÂAaÂAqAžRAžRA£×A\AzáAzáA ffAšQìAšQìA°=qAž(öAž{AÀ{AÈ  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >k
+?p€?(ö@
+p€@Mp€@ff@{@¥Â@ÅÂ@Åp€@å
+žA\AffA=qA"{A2{A1ë
+AAÂAQÂAQAap€Aqp€A£×A\AzáAzáAffAQìA QìAš=qAš(öA°{Až{Až  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >aG®?áH?áH@
+ÌÍ@L(ö@
+Â@
+Â@¥p€@Å
+ž@Å
+ž@äÌÍA=qA{A{A!ë
+A1ÂA1ÂAAAQp€AQp€AaG®Aq
+žAzáAzáAffAQìAQìA=qA (öAš{Aš{A°  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >W
+=??Qì@
+
+@K
+@
+p€@
+
+ž@¥
+ž@ÄÌÍ@Äzá@ä(öA{Aë
+AÂA!ÂA1A1p€AAp€AQG®AQ
+žA`õÃApõÃAffAQìAQìA=qA(öA{A {Aš  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >B\?
+>?
+>@
+áH@J=q@
+
+ž@ÌÍ@€zá@Ä(ö@Ä(ö@ã×
+AÂAÂAA!p€A1p€A1G®AA
+žAPõÃAPõÃA`ÌÍAp£×AQìA=qA(öA{A{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >8Qì?Â?zá@
+=q@I@zá@(ö@€(ö@Ã×
+@Ã
+@ã
+AAp€Ap€A!G®A1
+žA0õÃA@õÃAPÌÍAP£×A`£×ApzáA(öA{A{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >#×
+?zá?33@õÃ@HQì@(ö@×
+@£
+@Ã
+@Ã33@âáHAp€AG®A
+žA õÃA0õÃA0ÌÍA@£×AP£×APzáA`QìAp(öA{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >?ë
+?£×@Qì@G®@
+@
+@£33@ÂáH@ÂáH@â\A
+žAõÃAõÃA ÌÍA0£×A0£×A@záAPQìAP(öA`(öAp  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >
+ž?£×?\)@
+=@G
+=@33@áH@¢áH@Â\@Â=q@áë
+A õÃAÌÍA£×A £×A0záA0QìA@(öAP(öAP  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =õÂ?{?{@ff@EÂ@áH@\@¢=q@Áë
+@Áë
+@áA £×A£×AzáA QìA0(öA0(öA@  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =áG®?ÌÍ?
+@Â@E
+ž@=q@ë
+@¡ë
+@Á@ÁG®@áG®A záAQìA(öA (öA0  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =žQì?
+?=q@zá@C×
+@ë
+@@¡G®@ÁG®@ÀõÃ@à£×A (öA(öA  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =£×
+?õÃ?®@×
+@C33@G®@G®@ õÃ@À£×@ÀQì@àQìA   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =uÂ?®?ff@\@B\@õÃ@£×@ Qì@ÀQì@À  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =LÌÍ?
+
+ž?
+
+ž@ë
+@AG®@Qì@Qì@   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =#×
+?×
+?\@ £×@@£×@  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  <£×
+?G®?G®@   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  <#×
+?  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ?!G®?Ð£×@(Qì@hQì@(ö@Ž(ö@Ô(ö@ô(öA
+{A{A*{A:{AJ{AZ{Aj{Az{A
+
+=A
+=A
+=A
+=A¥
+=A­
+=Aµ
+=Aœ
+=AÅ
+=AÍ
+=AÕ
+=AÝ
+=Aå
+=Aí
+=Aõ
+=Aý
+=B
+B
+B
+
+B
+B
+B
+B
+B
+
+B"
+B&
+B*
+B.
+B2
+B6
+B:
+B>
+BB
+BF
+BJ
+BN
+BR
+BV
+BZ
+B^
+Bb
+Bf
+Bj
+Bn
+Br
+Bv
+Bz
+B~
+?
+žR?Ï\)@'®@g®@×
+@³×
+@Ó×
+@ó×
+A	ë
+Aë
+A)ë
+A9ë
+AIë
+AYë
+Aië
+Ayë
+AõÃAõÃAõÃAõÃA€õÃA¬õÃAŽõÃAŒõÃAÄõÃAÌõÃAÔõÃAÜõÃAäõÃAìõÃAôõÃAüõÃBzáBzáB
+záBzáBzáBzáBzáB
+záB"záB&záB*záB.záB2záB6záB:záB>záBBzáBFzáBJzáBNzáBRzáBVzáBZzáB^záBbzáBfzáBjzáBnzáBrzáBvzáBzzáB~zá?
+(ö?Î{@'
+>@g
+>@
+@³
+@Ó
+@ó
+A	ÂAÂA)ÂA9ÂAIÂAYÂAiÂAyÂAáHAáHAáHAáHA€áHA¬áHAŽáHAŒáHAÄáHAÌáHAÔáHAÜáHAäáHAìáHAôáHAüáHBp€Bp€B
+p€Bp€Bp€Bp€Bp€B
+p€B"p€B&p€B*p€B.p€B2p€B6p€B:p€B>p€BBp€BFp€BJp€BNp€BRp€BVp€BZp€B^p€Bbp€Bfp€Bjp€Bnp€Brp€Bvp€Bzp€B~p€??ÌÌÍ@&ff@fff@33@³33@Ó33@ó33A	AA)A9AIAYAiAyAÌÍAÌÍAÌÍAÌÍA€ÌÍA¬ÌÍAŽÌÍAŒÌÍAÄÌÍAÌÌÍAÔÌÍAÜÌÍAäÌÍAìÌÍAôÌÍAüÌÍBffBffB
+ffBffBffBffBffB
+ffB"ffB&ffB*ffB.ffB2ffB6ffB:ffB>ffBBffBFffBJffBNffBRffBVffBZffB^ffBbffBfffBjffBnffBrffBvffBzffB~ff?
+=?Ë
+
+@%Â@eÂ@áH@²áH@ÒáH@òáHA	p€Ap€A)p€A9p€AIp€AYp€Aip€Ayp€AžRAžRAžRAžRA€žRA¬žRAŽžRAŒžRAÄžRAÌžRAÔžRAÜžRAäžRAìžRAôžRAüžRB\)B\)B
+\)B\)B\)B\)B\)B
+\)B"\)B&\)B*\)B.\)B2\)B6\)B:\)B>\)BB\)BF\)BJ\)BN\)BR\)BV\)BZ\)B^\)Bb\)Bf\)Bj\)Bn\)Br\)Bv\)Bz\)B~\)?zá?Ê=p@%
+ž@e
+ž@\@²\@Ò\@ò\A	G®AG®A)G®A9G®AIG®AYG®AiG®AyG®A£×A£×A£×A£×A€£×A¬£×AŽ£×AŒ£×AÄ£×AÌ£×AÔ£×AÜ£×Aä£×Aì£×Aô£×Aü£×BQìBQìB
+QìBQìBQìBQìBQìB
+QìB"QìB&QìB*QìB.QìB2QìB6QìB:QìB>QìBBQìBFQìBJQìBNQìBRQìBVQìBZQìB^QìBbQìBfQìBjQìBnQìBrQìBvQìBzQìB~Qì?ë
+?ÈõÂ@$zá@dzá@=q@²=q@Ò=q@ò=qA	
+žA
+žA)
+žA9
+žAI
+žAY
+žAi
+žAy
+žA\A\A\A\A€\A¬\AŽ\AŒ\AÄ\AÌ\AÔ\AÜ\Aä\Aì\Aô\Aü\BG®BG®B
+G®BG®BG®BG®BG®B
+G®B"G®B&G®B*G®B.G®B2G®B6G®B:G®B>G®BBG®BFG®BJG®BNG®BRG®BVG®BZG®B^G®BbG®BfG®BjG®BnG®BrG®BvG®BzG®B~G®?\)?Ç®@#×
+@c×
+@ë
+@±ë
+@Ñë
+@ñë
+AõÃAõÃA(õÃA8õÃAHõÃAXõÃAhõÃAxõÃAzáAzáAzáAzáA€záA¬záAŽzáAŒzáAÄzáAÌzáAÔzáAÜzáAäzáAìzáAôzáAüzáB=qB=qB
+=qB=qB=qB=qB=qB
+=qB"=qB&=qB*=qB.=qB2=qB6=qB:=qB>=qBB=qBF=qBJ=qBN=qBR=qBV=qBZ=qB^=qBb=qBf=qBj=qBn=qBr=qBv=qBz=qB~=q?
+ÌÍ?Æff@#33@c33@@±@Ñ@ñAÌÍAÌÍA(ÌÍA8ÌÍAHÌÍAXÌÍAhÌÍAxÌÍAffAffAffAffA€ffA¬ffAŽffAŒffAÄffAÌffAÔffAÜffAäffAìffAôffAüffB33B33B
+33B33B33B33B33B
+33B"33B&33B*33B.33B233B633B:33B>33BB33BF33BJ33BN33BR33BV33BZ33B^33Bb33Bf33Bj33Bn33Br33Bv33Bz33B~33?
+=q?Å
+ž@"\@b\@G®@±G®@ÑG®@ñG®A£×A£×A(£×A8£×AH£×AX£×Ah£×Ax£×AQìAQìAQìAQìA€QìA¬QìAŽQìAŒQìAÄQìAÌQìAÔQìAÜQìAäQìAìQìAôQìAüQìB(öB(öB
+(öB(öB(öB(öB(öB
+(öB"(öB&(öB*(öB.(öB2(öB6(öB:(öB>(öBB(öBF(öBJ(öBN(öBR(öBV(öBZ(öB^(öBb(öBf(öBj(öBn(öBr(öBv(öBz(öB~(ö?®?Ã×
+@!ë
+@aë
+@õÂ@°õÂ@ÐõÂ@ðõÂAzáAzáA(záA8záAHzáAXzáAhzáAxzáA=qA=qA=qA=qA€=qA¬=qAŽ=qAŒ=qAÄ=qAÌ=qAÔ=qAÜ=qAä=qAì=qAô=qAü=qB
+žB
+žB
+
+žB
+žB
+žB
+žB
+žB
+
+žB"
+žB&
+žB*
+žB.
+žB2
+žB6
+žB:
+žB>
+žBB
+žBF
+žBJ
+žBN
+žBR
+žBV
+žBZ
+žB^
+žBb
+žBf
+žBj
+žBn
+žBr
+žBv
+žBz
+žB~
+ž?
+ž?Â\@!G®@aG®@£×@°£×@Ð£×@ð£×AQìAQìA(QìA8QìAHQìAXQìAhQìAxQìA(öA(öA(öA(öA€(öA¬(öAŽ(öAŒ(öAÄ(öAÌ(öAÔ(öAÜ(öAä(öAì(öAô(öAü(öB{B{B
+{B{B{B{B{B
+{B"{B&{B*{B.{B2{B6{B:{B>{BB{BF{BJ{BN{BR{BV{BZ{B^{Bb{Bf{Bj{Bn{Br{Bv{Bz{B~{?\?ÁG®@ £×@`£×@Qì@°Qì@ÐQì@ðQìA(öA(öA((öA8(öAH(öAX(öAh(öAx(öA{A{A{A{A€{A¬{AŽ{AŒ{AÄ{AÌ{AÔ{AÜ{Aä{Aì{Aô{Aü{B
+=B
+=B
+
+=B
+=B
+=B
+=B
+=B
+
+=B"
+=B&
+=B*
+=B.
+=B2
+=B6
+=B:
+=B>
+=BB
+=BF
+=BJ
+=BN
+=BR
+=BV
+=BZ
+=B^
+=Bb
+=Bf
+=Bj
+=Bn
+=Br
+=Bv
+=Bz
+=B~
+=?   ?À  @   @`  @  @°  @Ð  @ð  A  A  A(  A8  AH  AX  Ah  Ax  A  A  A  A  A€  A¬  AŽ  AŒ  AÄ  AÌ  AÔ  AÜ  Aä  Aì  Aô  Aü  B  B  B
+  B  B  B  B  B
+  B"  B&  B*  B.  B2  B6  B:  B>  BB  BF  BJ  BN  BR  BV  BZ  B^  Bb  Bf  Bj  Bn  Br  Bv  Bz  B~  >úáH?ŸžR@\)@_\)@®@¯®@Ï®@ï®A×
+A×
+A'×
+A7×
+AG×
+AW×
+Ag×
+Aw×
+Aë
+Aë
+Aë
+Aë
+A£ë
+A«ë
+A³ë
+A»ë
+AÃë
+AËë
+AÓë
+AÛë
+Aãë
+Aëë
+Aóë
+Aûë
+BõÃBõÃB	õÃB
+õÃBõÃBõÃBõÃB
+õÃB!õÃB%õÃB)õÃB-õÃB1õÃB5õÃB9õÃB=õÃBAõÃBEõÃBIõÃBMõÃBQõÃBUõÃBYõÃB]õÃBaõÃBeõÃBiõÃBmõÃBqõÃBuõÃByõÃB}õÃ>õÂ?œp€@
+žR@^žR@\)@¯\)@Ï\)@ï\)A®A®A'®A7®AG®AW®Ag®Aw®A×
+A×
+A×
+A×
+A£×
+A«×
+A³×
+A»×
+AÃ×
+AË×
+AÓ×
+AÛ×
+Aã×
+Aë×
+Aó×
+Aû×
+Bë
+Bë
+B	ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B!ë
+B%ë
+B)ë
+B-ë
+B1ë
+B5ë
+B9ë
+B=ë
+BAë
+BEë
+BIë
+BMë
+BQë
+BUë
+BYë
+B]ë
+Baë
+Beë
+Bië
+Bmë
+Bqë
+Buë
+Byë
+B}ë
+>ð£×?Œ(ö@
+{@^{@
+=@¯
+=@Ï
+=@ï
+=A
+A
+A'
+A7
+AG
+AW
+Ag
+Aw
+AÂAÂAÂAÂA£ÂA«ÂA³ÂA»ÂAÃÂAËÂAÓÂAÛÂAãÂAëÂAóÂAûÂBáHBáHB	áHB
+áHBáHBáHBáHB
+áHB!áHB%áHB)áHB-áHB1áHB5áHB9áHB=áHBAáHBEáHBIáHBMáHBQáHBUáHBYáHB]áHBaáHBeáHBiáHBmáHBqáHBuáHByáHB}áH>ë
+?ºáH@
+p€@]p€@žR@®žR@ÎžR@îžRA\)A\)A'\)A7\)AG\)AW\)Ag\)Aw\)A®A®A®A®A£®A«®A³®A»®AÃ®AË®AÓ®AÛ®Aã®Aë®Aó®Aû®B×
+B×
+B	×
+B
+×
+B×
+B×
+B×
+B
+×
+B!×
+B%×
+B)×
+B-×
+B1×
+B5×
+B9×
+B=×
+BA×
+BE×
+BI×
+BM×
+BQ×
+BU×
+BY×
+B]×
+Ba×
+Be×
+Bi×
+Bm×
+Bq×
+Bu×
+By×
+B}×
+>æff?¹@
+ÌÍ@\ÌÍ@ff@®ff@Îff@îffA33A33A'33A733AG33AW33Ag33Aw33AAAAA£A«A³A»AÃAËAÓAÛAãAëAóAûBÌÍBÌÍB	ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB!ÌÍB%ÌÍB)ÌÍB-ÌÍB1ÌÍB5ÌÍB9ÌÍB=ÌÍBAÌÍBEÌÍBIÌÍBMÌÍBQÌÍBUÌÍBYÌÍB]ÌÍBaÌÍBeÌÍBiÌÍBmÌÍBqÌÍBuÌÍByÌÍB}ÌÍ>áG®?žQì@
+(ö@\(ö@{@®{@Î{@î{A
+=A
+=A'
+=A7
+=AG
+=AW
+=Ag
+=Aw
+=A
+A
+A
+A
+A£
+A«
+A³
+A»
+AÃ
+AË
+AÓ
+AÛ
+Aã
+Aë
+Aó
+Aû
+BÂBÂB	ÂB
+ÂBÂBÂBÂB
+ÂB!ÂB%ÂB)ÂB-ÂB1ÂB5ÂB9ÂB=ÂBAÂBEÂBIÂBMÂBQÂBUÂBYÂB]ÂBaÂBeÂBiÂBmÂBqÂBuÂByÂB}Â>Ü(ö?·
+>@
+@[
+@Â@­Â@ÍÂ@íÂAáHAáHA&áHA6áHAFáHAVáHAfáHAváHAp€Ap€Ap€Ap€A£p€A«p€A³p€A»p€AÃp€AËp€AÓp€AÛp€Aãp€Aëp€Aóp€Aûp€BžRBžRB	žRB
+žRBžRBžRBžRB
+žRB!žRB%žRB)žRB-žRB1žRB5žRB9žRB=žRBAžRBEžRBIžRBMžRBQžRBUžRBYžRB]žRBažRBežRBižRBmžRBqžRBužRByžRB}žR>×
+=?µÂ@áH@ZáH@p€@­p€@Íp€@íp€AžRAžRA&žRA6žRAFžRAVžRAfžRAvžRA\)A\)A\)A\)A£\)A«\)A³\)A»\)AÃ\)AË\)AÓ\)AÛ\)Aã\)Aë\)Aó\)Aû\)B®B®B	®B
+®B®B®B®B
+®B!®B%®B)®B-®B1®B5®B9®B=®BA®BE®BI®BM®BQ®BU®BY®B]®Ba®Be®Bi®Bm®Bq®Bu®By®B}®>Ñë
+?Žzá@=q@Z=q@
+ž@­
+ž@Í
+ž@í
+žA\A\A&\A6\AF\AV\Af\Av\AG®AG®AG®AG®A£G®A«G®A³G®A»G®AÃG®AËG®AÓG®AÛG®AãG®AëG®AóG®AûG®B£×B£×B	£×B
+£×B£×B£×B£×B
+£×B!£×B%£×B)£×B-£×B1£×B5£×B9£×B=£×BA£×BE£×BI£×BM£×BQ£×BU£×BY£×B]£×Ba£×Be£×Bi£×Bm£×Bq£×Bu£×By£×B}£×>ÌÌÍ?³33@@Y@ÌÍ@¬ÌÍ@ÌÌÍ@ìÌÍAffAffA&ffA6ffAFffAVffAfffAvffA33A33A33A33A£33A«33A³33A»33AÃ33AË33AÓ33AÛ33Aã33Aë33Aó33Aû33BBB	B
+BBBB
+B!B%B)B-B1B5B9B=BABEBIBMBQBUBYB]BaBeBiBmBqBuByB}>Ç®?±ë
+@õÂ@XõÂ@zá@¬zá@Ìzá@ìzáA=qA=qA&=qA6=qAF=qAV=qAf=qAv=qA
+žA
+žA
+žA
+žA£
+žA«
+žA³
+žA»
+žAÃ
+žAË
+žAÓ
+žAÛ
+žAã
+žAë
+žAó
+žAû
+žB\B\B	\B
+\B\B\B\B
+\B!\B%\B)\B-\B1\B5\B9\B=\BA\BE\BI\BM\BQ\BU\BY\B]\Ba\Be\Bi\Bm\Bq\Bu\By\B}\>Â\?°£×@Qì@XQì@(ö@¬(ö@Ì(ö@ì(öA{A{A&{A6{AF{AV{Af{Av{A
+=A
+=A
+=A
+=A£
+=A«
+=A³
+=A»
+=AÃ
+=AË
+=AÓ
+=AÛ
+=Aã
+=Aë
+=Aó
+=Aû
+=B
+B
+B	
+B
+
+B
+B
+B
+B
+
+B!
+B%
+B)
+B-
+B1
+B5
+B9
+B=
+BA
+BE
+BI
+BM
+BQ
+BU
+BY
+B]
+Ba
+Be
+Bi
+Bm
+Bq
+Bu
+By
+B}
+>œp€?¯\)@®@W®@×
+@«×
+@Ë×
+@ë×
+Aë
+Aë
+A%ë
+A5ë
+AEë
+AUë
+Aeë
+Auë
+AõÃAõÃAõÃAõÃA¢õÃAªõÃA²õÃAºõÃAÂõÃAÊõÃAÒõÃAÚõÃAâõÃAêõÃAòõÃAúõÃBzáBzáB	záB
+záBzáBzáBzáB
+záB!záB%záB)záB-záB1záB5záB9záB=záBAzáBEzáBIzáBMzáBQzáBUzáBYzáB]záBazáBezáBizáBmzáBqzáBuzáByzáB}zá>žQì?®{@
+>@W
+>@
+@«
+@Ë
+@ë
+AÂAÂA%ÂA5ÂAEÂAUÂAeÂAuÂAáHAáHAáHAáHA¢áHAªáHA²áHAºáHAÂáHAÊáHAÒáHAÚáHAâáHAêáHAòáHAúáHBp€Bp€B	p€B
+p€Bp€Bp€Bp€B
+p€B!p€B%p€B)p€B-p€B1p€B5p€B9p€B=p€BAp€BEp€BIp€BMp€BQp€BUp€BYp€B]p€Bap€Bep€Bip€Bmp€Bqp€Bup€Byp€B}p€>³33?¬ÌÍ@ff@Vff@33@«33@Ë33@ë33AAA%A5AEAUAeAuAÌÍAÌÍAÌÍAÌÍA¢ÌÍAªÌÍA²ÌÍAºÌÍAÂÌÍAÊÌÍAÒÌÍAÚÌÍAâÌÍAêÌÍAòÌÍAúÌÍBffBffB	ffB
+ffBffBffBffB
+ffB!ffB%ffB)ffB-ffB1ffB5ffB9ffB=ffBAffBEffBIffBMffBQffBUffBYffB]ffBaffBeffBiffBmffBqffBuffByffB}ff>®{?«
+@Â@UÂ@áH@ªáH@ÊáH@êáHAp€Ap€A%p€A5p€AEp€AUp€Aep€Aup€AžRAžRAžRAžRA¢žRAªžRA²žRAºžRAÂžRAÊžRAÒžRAÚžRAâžRAêžRAòžRAúžRB\)B\)B	\)B
+\)B\)B\)B\)B
+\)B!\)B%\)B)\)B-\)B1\)B5\)B9\)B=\)BA\)BE\)BI\)BM\)BQ\)BU\)BY\)B]\)Ba\)Be\)Bi\)Bm\)Bq\)Bu\)By\)B}\)>šõÃ?ª=q@
+ž@U
+ž@\@ª\@Ê\@ê\AG®AG®A%G®A5G®AEG®AUG®AeG®AuG®A£×A£×A£×A£×A¢£×Aª£×A²£×Aº£×AÂ£×AÊ£×AÒ£×AÚ£×Aâ£×Aê£×Aò£×Aú£×BQìBQìB	QìB
+QìBQìBQìBQìB
+QìB!QìB%QìB)QìB-QìB1QìB5QìB9QìB=QìBAQìBEQìBIQìBMQìBQQìBUQìBYQìB]QìBaQìBeQìBiQìBmQìBqQìBuQìByQìB}Qì>£×
+?šõÂ@zá@Tzá@=q@ª=q@Ê=q@ê=qA
+žA
+žA%
+žA5
+žAE
+žAU
+žAe
+žAu
+žA\A\A\A\A¢\Aª\A²\Aº\AÂ\AÊ\AÒ\AÚ\Aâ\Aê\Aò\Aú\BG®BG®B	G®B
+G®BG®BG®BG®B
+G®B!G®B%G®B)G®B-G®B1G®B5G®B9G®B=G®BAG®BEG®BIG®BMG®BQG®BUG®BYG®B]G®BaG®BeG®BiG®BmG®BqG®BuG®ByG®B}G®>žR?§®@×
+@S×
+@ë
+@©ë
+@Éë
+@éë
+AõÃAõÃA$õÃA4õÃADõÃATõÃAdõÃAtõÃAzáAzáAzáAzáA¢záAªzáA²záAºzáAÂzáAÊzáAÒzáAÚzáAâzáAêzáAòzáAúzáB=qB=qB	=qB
+=qB=qB=qB=qB
+=qB!=qB%=qB)=qB-=qB1=qB5=qB9=qB==qBA=qBE=qBI=qBM=qBQ=qBU=qBY=qB]=qBa=qBe=qBi=qBm=qBq=qBu=qBy=qB}=q>?Šff@33@S33@@©@É@éAÌÍAÌÍA$ÌÍA4ÌÍADÌÍATÌÍAdÌÍAtÌÍAffAffAffAffA¢ffAªffA²ffAºffAÂffAÊffAÒffAÚffAâffAêffAòffAúffB33B33B	33B
+33B33B33B33B
+33B!33B%33B)33B-33B133B533B933B=33BA33BE33BI33BM33BQ33BU33BY33B]33Ba33Be33Bi33Bm33Bq33Bu33By33B}33>zá?¥
+ž@\@R\@G®@©G®@ÉG®@éG®A£×A£×A$£×A4£×AD£×AT£×Ad£×At£×AQìAQìAQìAQìA¢QìAªQìA²QìAºQìAÂQìAÊQìAÒQìAÚQìAâQìAêQìAòQìAúQìB(öB(öB	(öB
+(öB(öB(öB(öB
+(öB!(öB%(öB)(öB-(öB1(öB5(öB9(öB=(öBA(öBE(öBI(öBM(öBQ(öBU(öBY(öB](öBa(öBe(öBi(öBm(öBq(öBu(öBy(öB}(ö>\)?£×
+@ë
+@Që
+@õÃ@šõÃ@ÈõÃ@èõÃAzáAzáA$záA4záADzáATzáAdzáAtzáA=qA=qA=qA=qA¢=qAª=qA²=qAº=qAÂ=qAÊ=qAÒ=qAÚ=qAâ=qAê=qAò=qAú=qB
+žB
+žB	
+žB
+
+žB
+žB
+žB
+žB
+
+žB!
+žB%
+žB)
+žB-
+žB1
+žB5
+žB9
+žB=
+žBA
+žBE
+žBI
+žBM
+žBQ
+žBU
+žBY
+žB]
+žBa
+žBe
+žBi
+žBm
+žBq
+žBu
+žBy
+žB}
+ž>=q?¢\@G®@QG®@£×@š£×@È£×@è£×AQìAQìA$QìA4QìADQìATQìAdQìAtQìA(öA(öA(öA(öA¢(öAª(öA²(öAº(öAÂ(öAÊ(öAÒ(öAÚ(öAâ(öAê(öAò(öAú(öB{B{B	{B
+{B{B{B{B
+{B!{B%{B){B-{B1{B5{B9{B={BA{BE{BI{BM{BQ{BU{BY{B]{Ba{Be{Bi{Bm{Bq{Bu{By{B}{>
+
+ž?¡G®@£×@P£×@Qì@šQì@ÈQì@èQìA(öA(öA$(öA4(öAD(öAT(öAd(öAt(öA{A{A{A{A¢{Aª{A²{Aº{AÂ{AÊ{AÒ{AÚ{Aâ{Aê{Aò{Aú{B
+=B
+=B	
+=B
+=B
+=B
+=B
+=B
+
+=B!
+=B%
+=B)
+=B-
+=B1
+=B5
+=B9
+=B=
+=BA
+=BE
+=BI
+=BM
+=BQ
+=BU
+=BY
+=B]
+=Ba
+=Be
+=Bi
+=Bm
+=Bq
+=Bu
+=By
+=B}
+=>  ?   @  @P  @  @š  @È  @è  A  A  A$  A4  AD  AT  Ad  At  A  A  A  A  A¢  Aª  A²  Aº  AÂ  AÊ  AÒ  AÚ  Aâ  Aê  Aò  Aú  B  B  B	  B
+  B  B  B  B
+  B!  B%  B)  B-  B1  B5  B9  B=  BA  BE  BI  BM  BQ  BU  BY  B]  Ba  Be  Bi  Bm  Bq  Bu  By  B}  >uÂ?žR@\)@O\)@®@§®@Ç®@ç®A×
+A×
+A#×
+A3×
+AC×
+AS×
+Ac×
+As×
+Aë
+Aë
+Aë
+Aë
+A¡ë
+A©ë
+A±ë
+A¹ë
+AÁë
+AÉë
+AÑë
+AÙë
+Aáë
+Aéë
+Añë
+Aùë
+B õÃBõÃBõÃB
+õÃBõÃBõÃBõÃB
+õÃB õÃB$õÃB(õÃB,õÃB0õÃB4õÃB8õÃB<õÃB@õÃBDõÃBHõÃBLõÃBPõÃBTõÃBXõÃB\õÃB`õÃBdõÃBhõÃBlõÃBpõÃBtõÃBxõÃB|õÃ>k
+?p€@žR@NžR@\)@§\)@Ç\)@ç\)A®A®A#®A3®AC®AS®Ac®As®A×
+A×
+A×
+A×
+A¡×
+A©×
+A±×
+A¹×
+AÁ×
+AÉ×
+AÑ×
+AÙ×
+Aá×
+Aé×
+Añ×
+Aù×
+B ë
+Bë
+Bë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B ë
+B$ë
+B(ë
+B,ë
+B0ë
+B4ë
+B8ë
+B<ë
+B@ë
+BDë
+BHë
+BLë
+BPë
+BTë
+BXë
+B\ë
+B`ë
+Bdë
+Bhë
+Blë
+Bpë
+Btë
+Bxë
+B|ë
+>aG®?(ö@{@N{@
+=@§
+=@Ç
+=@ç
+=A
+A
+A#
+A3
+AC
+AS
+Ac
+As
+AÂAÂAÂAÂA¡ÂA©ÂA±ÂA¹ÂAÁÂAÉÂAÑÂAÙÂAáÂAéÂAñÂAùÂB áHBáHBáHB
+áHBáHBáHBáHB
+áHB áHB$áHB(áHB,áHB0áHB4áHB8áHB<áHB@áHBDáHBHáHBLáHBPáHBTáHBXáHB\áHB`áHBdáHBháHBláHBpáHBtáHBxáHB|áH>W
+=?áH@
+p€@Mp€@žR@ŠžR@ÆžR@æžRA\)A\)A#\)A3\)AC\)AS\)Ac\)As\)A®A®A®A®A¡®A©®A±®A¹®AÁ®AÉ®AÑ®AÙ®Aá®Aé®Añ®Aù®B ×
+B×
+B×
+B
+×
+B×
+B×
+B×
+B
+×
+B ×
+B$×
+B(×
+B,×
+B0×
+B4×
+B8×
+B<×
+B@×
+BD×
+BH×
+BL×
+BP×
+BT×
+BX×
+B\×
+B`×
+Bd×
+Bh×
+Bl×
+Bp×
+Bt×
+Bx×
+B|×
+>LÌÍ?@
+ÌÍ@LÌÍ@ff@Šff@Æff@æffA33A33A#33A333AC33AS33Ac33As33AAAAA¡A©A±A¹AÁAÉAÑAÙAáAéAñAùB ÌÍBÌÍBÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB ÌÍB$ÌÍB(ÌÍB,ÌÍB0ÌÍB4ÌÍB8ÌÍB<ÌÍB@ÌÍBDÌÍBHÌÍBLÌÍBPÌÍBTÌÍBXÌÍB\ÌÍB`ÌÍBdÌÍBhÌÍBlÌÍBpÌÍBtÌÍBxÌÍB|ÌÍ>B\?Qì@
+(ö@L(ö@{@Š{@Æ{@æ{A
+=A
+=A#
+=A3
+=AC
+=AS
+=Ac
+=As
+=A
+A
+A
+A
+A¡
+A©
+A±
+A¹
+AÁ
+AÉ
+AÑ
+AÙ
+Aá
+Aé
+Añ
+Aù
+B ÂBÂBÂB
+ÂBÂBÂBÂB
+ÂB ÂB$ÂB(ÂB,ÂB0ÂB4ÂB8ÂB<ÂB@ÂBDÂBHÂBLÂBPÂBTÂBXÂB\ÂB`ÂBdÂBhÂBlÂBpÂBtÂBxÂB|Â>8Qì?
+>@
+
+@K
+@
+Â@¥Â@ÅÂ@åÂAáHAáHA"áHA2áHABáHARáHAbáHAráHAp€Ap€Ap€Ap€A¡p€A©p€A±p€A¹p€AÁp€AÉp€AÑp€AÙp€Aáp€Aép€Añp€Aùp€B žRBžRBžRB
+žRBžRBžRBžRB
+žRB žRB$žRB(žRB,žRB0žRB4žRB8žRB<žRB@žRBDžRBHžRBLžRBPžRBTžRBXžRB\žRB`žRBdžRBhžRBlžRBpžRBtžRBxžRB|žR>.{?Â@
+áH@JáH@
+p€@¥p€@Åp€@åp€AžRAžRA"žRA2žRABžRARžRAbžRAržRA\)A\)A\)A\)A¡\)A©\)A±\)A¹\)AÁ\)AÉ\)AÑ\)AÙ\)Aá\)Aé\)Añ\)Aù\)B ®B®B®B
+®B®B®B®B
+®B ®B$®B(®B,®B0®B4®B8®B<®B@®BD®BH®BL®BP®BT®BX®B\®B`®Bd®Bh®Bl®Bp®Bt®Bx®B|®>#×
+?zá@
+=q@J=q@
+
+ž@¥
+ž@Å
+ž@å
+žA\A\A"\A2\AB\AR\Ab\Ar\AG®AG®AG®AG®A¡G®A©G®A±G®A¹G®AÁG®AÉG®AÑG®AÙG®AáG®AéG®AñG®AùG®B £×B£×B£×B
+£×B£×B£×B£×B
+£×B £×B$£×B(£×B,£×B0£×B4£×B8£×B<£×B@£×BD£×BH£×BL£×BP£×BT£×BX£×B\£×B`£×Bd£×Bh£×Bl£×Bp£×Bt£×Bx£×B|£×>?33@	@I@ÌÍ@€ÌÍ@ÄÌÍ@äÌÍAffAffA"ffA2ffABffARffAbffArffA33A33A33A33A¡33A©33A±33A¹33AÁ33AÉ33AÑ33AÙ33Aá33Aé33Añ33Aù33B BBB
+BBBB
+B B$B(B,B0B4B8B<B@BDBHBLBPBTBXB\B`BdBhBlBpBtBxB|>\)?ë
+@õÃ@HõÃ@zá@€zá@Äzá@äzáA=qA=qA"=qA2=qAB=qAR=qAb=qAr=qA
+žA
+žA
+žA
+žA¡
+žA©
+žA±
+žA¹
+žAÁ
+žAÉ
+žAÑ
+žAÙ
+žAá
+žAé
+žAñ
+žAù
+žB \B\B\B
+\B\B\B\B
+\B \B$\B(\B,\B0\B4\B8\B<\B@\BD\BH\BL\BP\BT\BX\B\\B`\Bd\Bh\Bl\Bp\Bt\Bx\B|\>
+ž?£×@Qì@HQì@(ö@€(ö@Ä(ö@ä(öA{A{A"{A2{AB{AR{Ab{Ar{A
+=A
+=A
+=A
+=A¡
+=A©
+=A±
+=A¹
+=AÁ
+=AÉ
+=AÑ
+=AÙ
+=Aá
+=Aé
+=Añ
+=Aù
+=B 
+B
+B
+B
+
+B
+B
+B
+B
+
+B 
+B$
+B(
+B,
+B0
+B4
+B8
+B<
+B@
+BD
+BH
+BL
+BP
+BT
+BX
+B\
+B`
+Bd
+Bh
+Bl
+Bp
+Bt
+Bx
+B|
+=õÂ?\)@®@G®@×
+@£×
+@Ã×
+@ã×
+Aë
+Aë
+A!ë
+A1ë
+AAë
+AQë
+Aaë
+Aqë
+AõÃAõÃAõÃAõÃA õÃAšõÃA°õÃAžõÃAÀõÃAÈõÃAÐõÃAØõÃAàõÃAèõÃAðõÃAøõÃB záBzáBzáB
+záBzáBzáBzáB
+záB záB$záB(záB,záB0záB4záB8záB<záB@záBDzáBHzáBLzáBPzáBTzáBXzáB\záB`záBdzáBhzáBlzáBpzáBtzáBxzáB|zá=áG®?{@
+=@G
+=@
+@£
+@Ã
+@ã
+AÂAÂA!ÂA1ÂAAÂAQÂAaÂAqÂAáHAáHAáHAáHA áHAšáHA°áHAžáHAÀáHAÈáHAÐáHAØáHAàáHAèáHAðáHAøáHB p€Bp€Bp€B
+p€Bp€Bp€Bp€B
+p€B p€B$p€B(p€B,p€B0p€B4p€B8p€B<p€B@p€BDp€BHp€BLp€BPp€BTp€BXp€B\p€B`p€Bdp€Bhp€Blp€Bpp€Btp€Bxp€B|p€=ÌÌÍ?ÌÍ@ff@Fff@33@£33@Ã33@ã33AAA!A1AAAQAaAqAÌÍAÌÍAÌÍAÌÍA ÌÍAšÌÍA°ÌÍAžÌÍAÀÌÍAÈÌÍAÐÌÍAØÌÍAàÌÍAèÌÍAðÌÍAøÌÍB ffBffBffB
+ffBffBffBffB
+ffB ffB$ffB(ffB,ffB0ffB4ffB8ffB<ffB@ffBDffBHffBLffBPffBTffBXffB\ffB`ffBdffBhffBlffBpffBtffBxffB|ff=žQì?
+@Â@EÂ@áH@¢áH@ÂáH@âáHAp€Ap€A!p€A1p€AAp€AQp€Aap€Aqp€AžRAžRAžRAžRA žRAšžRA°žRAžžRAÀžRAÈžRAÐžRAØžRAàžRAèžRAðžRAøžRB \)B\)B\)B
+\)B\)B\)B\)B
+\)B \)B$\)B(\)B,\)B0\)B4\)B8\)B<\)B@\)BD\)BH\)BL\)BP\)BT\)BX\)B\\)B`\)Bd\)Bh\)Bl\)Bp\)Bt\)Bx\)B|\)=£×
+?=q@
+ž@E
+ž@\@¢\@Â\@â\AG®AG®A!G®A1G®AAG®AQG®AaG®AqG®A£×A£×A£×A£×A £×Aš£×A°£×Až£×AÀ£×AÈ£×AÐ£×AØ£×Aà£×Aè£×Að£×Aø£×B QìBQìBQìB
+QìBQìBQìBQìB
+QìB QìB$QìB(QìB,QìB0QìB4QìB8QìB<QìB@QìBDQìBHQìBLQìBPQìBTQìBXQìB\QìB`QìBdQìBhQìBlQìBpQìBtQìBxQìB|Qì=\)?õÃ@zá@Dzá@=q@¢=q@Â=q@â=qA
+žA
+žA!
+žA1
+žAA
+žAQ
+žAa
+žAq
+žA\A\A\A\A \Aš\A°\Až\AÀ\AÈ\AÐ\AØ\Aà\Aè\Að\Aø\B G®BG®BG®B
+G®BG®BG®BG®B
+G®B G®B$G®B(G®B,G®B0G®B4G®B8G®B<G®B@G®BDG®BHG®BLG®BPG®BTG®BXG®B\G®B`G®BdG®BhG®BlG®BpG®BtG®BxG®B|G®=uÂ?®@×
+@C×
+@ë
+@¡ë
+@Áë
+@áë
+A õÃAõÃA õÃA0õÃA@õÃAPõÃA`õÃApõÃAzáAzáAzáAzáA záAšzáA°záAžzáAÀzáAÈzáAÐzáAØzáAàzáAèzáAðzáAøzáB =qB=qB=qB
+=qB=qB=qB=qB
+=qB =qB$=qB(=qB,=qB0=qB4=qB8=qB<=qB@=qBD=qBH=qBL=qBP=qBT=qBX=qB\=qB`=qBd=qBh=qBl=qBp=qBt=qBx=qB|=q=LÌÍ?ff@33@C33@@¡@Á@áA ÌÍAÌÍA ÌÍA0ÌÍA@ÌÍAPÌÍA`ÌÍApÌÍAffAffAffAffA ffAšffA°ffAžffAÀffAÈffAÐffAØffAàffAèffAðffAøffB 33B33B33B
+33B33B33B33B
+33B 33B$33B(33B,33B033B433B833B<33B@33BD33BH33BL33BP33BT33BX33B\33B`33Bd33Bh33Bl33Bp33Bt33Bx33B|33=#×
+?
+
+ž@\@B\@G®@¡G®@ÁG®@áG®A £×A£×A £×A0£×A@£×AP£×A`£×Ap£×AQìAQìAQìAQìA QìAšQìA°QìAžQìAÀQìAÈQìAÐQìAØQìAàQìAèQìAðQìAøQìB (öB(öB(öB
+(öB(öB(öB(öB
+(öB (öB$(öB((öB,(öB0(öB4(öB8(öB<(öB@(öBD(öBH(öBL(öBP(öBT(öBX(öB\(öB`(öBd(öBh(öBl(öBp(öBt(öBx(öB|(ö<õÂ?×
+@ë
+@Aë
+@õÃ@ õÃ@ÀõÃ@àõÃA záAzáA záA0záA@záAPzáA`záApzáA=qA=qA=qA=qA =qAš=qA°=qAž=qAÀ=qAÈ=qAÐ=qAØ=qAà=qAè=qAð=qAø=qB 
+žB
+žB
+žB
+
+žB
+žB
+žB
+žB
+
+žB 
+žB$
+žB(
+žB,
+žB0
+žB4
+žB8
+žB<
+žB@
+žBD
+žBH
+žBL
+žBP
+žBT
+žBX
+žB\
+žB`
+žBd
+žBh
+žBl
+žBp
+žBt
+žBx
+žB|
+ž<£×
+?\@G®@AG®@£×@ £×@À£×@à£×A QìAQìA QìA0QìA@QìAPQìA`QìApQìA(öA(öA(öA(öA (öAš(öA°(öAž(öAÀ(öAÈ(öAÐ(öAØ(öAà(öAè(öAð(öAø(öB {B{B{B
+{B{B{B{B
+{B {B${B({B,{B0{B4{B8{B<{B@{BD{BH{BL{BP{BT{BX{B\{B`{Bd{Bh{Bl{Bp{Bt{Bx{B|{<#×
+?G®@ £×@@£×@Qì@ Qì@ÀQì@àQìA (öA(öA (öA0(öA@(öAP(öA`(öAp(öA{A{A{A{A {Aš{A°{Až{AÀ{AÈ{AÐ{AØ{Aà{Aè{Að{Aø{B 
+=B
+=B
+=B
+
+=B
+=B
+=B
+=B
+
+=B 
+=B$
+=B(
+=B,
+=B0
+=B4
+=B8
+=B<
+=B@
+=BD
+=BH
+=BL
+=BP
+=BT
+=BX
+=B\
+=B`
+=Bd
+=Bh
+=Bl
+=Bp
+=Bt
+=Bx
+=B|
+=    ?  @   @@  @  @   @À  @à  A   A  A   A0  A@  AP  A`  Ap  A  A  A  A  A   Aš  A°  Až  AÀ  AÈ  AÐ  AØ  Aà  Aè  Að  Aø  B   B  B  B
+  B  B  B  B
+  B   B$  B(  B,  B0  B4  B8  B<  B@  BD  BH  BL  BP  BT  BX  B\  B`  Bd  Bh  Bl  Bp  Bt  Bx  B|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+žR?Ð£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+(ö?Ï\)?Ï\)@(Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+=?ÌÌÍ?Î{@'®@g®@(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?zá?Ë
+
+?Ë
+
+@&ff@g
+>@×
+@×
+@Ž(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?ë
+?ÈõÂ?Ê=p@%Â@eÂ@33@
+@³×
+@Ó×
+@Ô(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+ÌÍ?Ç®?ÈõÂ@$zá@e
+ž@áH@áH@³33@Ó
+@Ó×
+@ó×
+A
+{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+=q?Å
+ž?Æff@#×
+@dzá@=q@\@²áH@ÒáH@Ó33@ó
+A	ë
+Aë
+A{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?
+ž?Ã×
+?Å
+ž@"\@c33@ë
+@=q@²=q@Ò\@ÒáH@òáHA	AÂAë
+A)ë
+A:{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?\?Â\?Â\@!ë
+@b\@G®@@±ë
+@Ò=q@Ò=q@ò\A	p€Ap€AA)ÂA9ë
+A9ë
+AJ{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?   ?À  ?ÁG®@!G®@aG®@õÂ@G®@±G®@Ñ@Ñë
+@ò=qA	
+žAG®Ap€A)p€A9A9ÂAIë
+AYë
+AZ{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >õÂ?ŸžR?À  @   @`£×@£×@£×@°õÂ@ÑG®@ÑG®@ñAõÃA
+žA
+žA)G®A9p€A9p€AIAYÂAYë
+Aië
+Az{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ð£×?Œ(ö?œp€@\)@`  @  @Qì@°£×@Ð£×@ÐõÂ@ñG®A£×AÌÍAõÃA)
+žA9
+žA9G®AIp€AYp€AYAiÂAyë
+AõÃA
+
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >æff?ºáH?Œ(ö@
+{@^žR@®@  @°  @ÐQì@Ð£×@ð£×AzáA£×A£×A(ÌÍA8õÃA9
+žAI
+žAYG®AYp€Aip€AyAáHAõÃAõÃA
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >áG®?¹?¹@
+p€@^{@
+=@\)@¯®@Ð  @Ð  @ðQìAQìAQìAzáA(£×A8£×A8ÌÍAHõÃAY
+žAY
+žAiG®Ayp€AžRAÌÍAáHAõÃAõÃA
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >×
+=?·
+>?žQì@
+ÌÍ@\ÌÍ@žR@
+=@¯
+=@Ï\)@Ï®@ð  A  A(öAQìA(QìA8záA8£×AH£×AXÌÍAXõÃAi
+žAy
+žA£×AžRAžRAÌÍAáHAõÃA€õÃA­
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Ñë
+?µÂ?µÂ@
+@\(ö@ff@ff@®žR@Ï
+=@Ï
+=@ï\)A×
+A  A  A((öA8QìA8QìAHzáAX£×AX£×AhÌÍAxõÃA\A\A£×AžRAžRAÌÍA€áHA¬õÃA¬õÃAµ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >ÌÌÍ?³33?Žzá@áH@ZáH@Â@{@®ff@Îff@ÎžR@ï
+=A
+A®A×
+A(  A8  A8(öAHQìAXQìAXzáAh£×Ax£×AffAzáA\A\A£×AžRA€žRA¬ÌÍA¬áHAŽõÃAŒõÃAœ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >Â\?±ë
+?³33@@Z=q@p€@p€@­Â@Î{@Îff@îffA\)A
+A
+A'®A7×
+A8  AH  AX(öAXQìAhQìAxzáAQìAQìAffAzáA\A\A€£×A¬žRA¬žRAŽÌÍAŒáHAŒõÃAÄõÃAÍ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >œp€?°£×?°£×@õÂ@Y@ÌÍ@
+ž@­p€@Íp€@ÍÂ@î{A33A33A\)A'
+A7
+A7®AG×
+AX  AX  Ah(öAxQìA(öA=qAQìAQìAffAzáA€\A¬\A¬£×AŽžRAŒžRAŒÌÍAÄáHAÌõÃAÌõÃAÕ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >³33?®{?¯\)@Qì@XQì@zá@ÌÍ@¬ÌÍ@Í
+ž@Íp€@íp€AáHA
+=A33A'33A7\)A7
+AG
+AW®AW×
+Ah  Ax  A{A(öA(öA=qAQìAQìA€ffA¬záA¬\AŽ\AŒ£×AŒžRAÄžRAÌÌÍAÌáHAÔõÃAÜõÃAå
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >®{?¬ÌÍ?¬ÌÍ@
+>@W®@(ö@(ö@¬zá@ÌÌÍ@ÌÌÍ@í
+žAžRAžRAáHA'
+=A733A733AG\)AW
+AW
+Ag®Aw×
+A  A  A{A(öA(öA=qA€QìA¬QìA¬ffAŽzáAŒ\AŒ\AÄ£×AÌžRAÌžRAÔÌÍAÜáHAäõÃAäõÃAí
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >šõÃ?ª=q?«
+@ff@Vff@
+@×
+@¬(ö@Ì(ö@Ìzá@ìÌÍAffA\AžRA&žRA6áHA7
+=AG33AW33AW\)Ag
+Aw
+A×
+Aë
+A  A  A{A(öA€(öA¬=qA¬QìAŽQìAŒffAŒzáAÄ\AÌ\AÌ£×AÔžRAÜžRAäÌÍAäáHAìõÃAôõÃAõ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >žR?šõÂ?ª=q@
+ž@UÂ@33@33@«
+@Ë×
+@Ì(ö@ì(öA=qAffAffA&\A6žRA6žRAFáHAW
+=AW33Ag33Aw\)AÂAÂA×
+Aë
+A  A  A€{A¬(öA¬(öAŽ=qAŒQìAŒQìAÄffAÌzáAÌ\AÔ\AÜ£×AäžRAäžRAìÌÍAôáHAôõÃAüõÃB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >?Šff?§®@zá@U
+ž@\@áH@«33@Ë33@Ë
+@ë×
+A{A{A=qA&ffA6ffA6\AFžRAVžRAVáHAg
+=Aw33AA®AÂAÂA×
+Aë
+A€  A¬  A¬{AŽ(öAŒ(öAŒ=qAÄQìAÌQìAÌffAÔzáAÜ\Aä\Aä£×AìžRAôžRAôÌÍAüáHBzáBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >\)?¥
+ž?Šff@33@S×
+@=q@\@ª\@ÊáH@Ë33@ë33AÂAë
+A{A&{A6=qA6ffAFffAV\AVžRAfžRAváHA
+AAA®AÂAÂA£×
+A«ë
+A¬  AŽ  AŒ{AŒ(öAÄ(öAÌ=qAÌQìAÔQìAÜffAäzáAä\Aì\Aô£×AôžRAüžRBffBp€BzáB
+záB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >=q?£×
+?£×
+@\@S33@@ë
+@ª=q@Ê\@Ê\@êáHAAAÂA%ë
+A6{A6{AF=qAVffAVffAf\AvžRA\)Ap€A
+AAA®A£ÂA«ÂA«×
+A³ë
+AŒ  AŒ  AÄ{AÌ(öAÌ(öAÔ=qAÜQìAäQìAäffAìzáAô\Aô\Aü£×B\)B\)BffB
+p€BzáBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >  ?¡G®?¢\@ë
+@Që
+@G®@@©@Éë
+@Ê=q@ê\AG®Ap€AA%A5ÂA5ë
+AF{AV{AV=qAfffAvffAG®A\)A\)Ap€A
+AA£A«®A«ÂA³ÂA»×
+A»ë
+AÄ  AÌ  AÌ{AÔ(öAÜ(öAä=qAäQìAìQìAôffAôzáAü\BG®BQìB\)B
+\)BffBp€BzáBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >uÂ?   ?   @£×@QG®@õÃ@õÃ@©G®@É@É@éë
+A
+žAG®AG®A%p€A5A5AEÂAUë
+AV{Af{Av=qA33A33AG®A\)A\)Ap€A£
+A«A«A³®A»ÂA»ÂAÃ×
+AËë
+AÌ  AÔ  AÜ{Aä(öAä(öAì=qAôQìAôQìAüffB=qBG®BG®B
+QìB\)B\)BffBp€BzáBzáB
+
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >k
+?p€?žR@  @P  @Qì@£×@šõÃ@ÈõÃ@ÉG®@éAÌÍAõÃA
+žA%G®A5G®A5p€AEAUAUÂAeë
+Av{A
+=A
+žA33A33AG®A\)A£\)A«p€A«
+A³A»A»®AÃÂAËÂAË×
+AÓë
+AÜ  Aä  Aä{Aì(öAô(öAô=qAüQìB(öB33B=qB
+G®BG®BQìB\)B\)BffBp€B
+záB"záB"
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >W
+=?(ö?p€@žR@O\)@  @  @šQì@È£×@ÈõÃ@èõÃA£×AÌÍAÌÍA$õÃA5
+žA5G®AEG®AUp€AUAeAuÂAõÃA
+=A
+=A
+žA33A33A£G®A«\)A«\)A³p€A»
+A»AÃAË®AËÂAÓÂAÛ×
+Aãë
+Aä  Aì  Aô{Aô(öAü(öB
+žB(öB(öB
+33B=qBG®BG®BQìB\)B\)B
+ffB"p€B"záB&záB*
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >LÌÍ?áH?áH@{@NžR@\)@®@š  @È  @ÈQì@è£×AzáAzáA£×A$ÌÍA4ÌÍA4õÃAE
+žAUG®AUG®Aep€AuAÌÍAáHAõÃA
+=A
+=A
+žA£33A«33A«G®A³\)A»\)A»p€AÃ
+AËAËAÓ®AÛÂAãÂAã×
+Aëë
+Aô  Aô  Aü{B{B{B
+žB
+(öB(öB33B=qBG®BG®BQìB
+\)B"\)B"ffB&p€B*záB*záB.
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >8Qì?Qì?@
+p€@Mp€@
+=@\)@§\)@Ç®@È  @è  A(öAQìAzáA$záA4£×A4ÌÍADÌÍATõÃAU
+žAeG®AuG®AžRAÌÍAÌÍAáHAõÃA
+=A£
+=A«
+žA«33A³33A»G®A»\)AÃ\)AËp€AË
+AÓAÛAã®AãÂAëÂAó×
+Aóë
+Aü  B  B
+=B{B
+{B
+žB(öB(öB33B=qBG®B
+G®B"QìB"\)B&\)B*ffB*p€B.záB2záB6
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >.{?
+>?
+>@
+(ö@LÌÍ@žR@žR@§
+=@Ç\)@Ç\)@ç®A  A  A(öA$QìA4záA4záAD£×ATÌÍATÌÍAdõÃAu
+žA£×A£×AžRAÌÍAÌÍAáHA¢õÃA«
+=A«
+=A³
+žA»33A»33AÃG®AË\)AË\)AÓp€AÛ
+AãAãAë®AóÂAóÂAû×
+BõÃB  B  B
+
+=B{B{B
+žB(öB(öB33B
+=qB"G®B"G®B&QìB*\)B*\)B.ffB2p€B6záB6záB:
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >#×
+?zá?Â@
+
+@K
+@{@ff@ŠžR@ÆžR@Ç
+=@ç\)A®A×
+A  A$  A4(öA4QìADzáATzáAT£×AdÌÍAtÌÍAzáA\A£×A£×AžRAÌÍA¢ÌÍAªáHAªõÃA³
+=A»
+=A»
+žAÃ33AË33AËG®AÓ\)AÛ\)Aãp€Aã
+AëAóAó®AûÂBáHBë
+BõÃB
+  B  B
+=B{B{B
+žB(öB
+(öB"33B"=qB&G®B*G®B*QìB.\)B2\)B6ffB6p€B:záB>záB>
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >\)?33?zá@
+=q@JáH@
+Â@
+Â@Š{@Æff@ÆžR@æžRA
+A®A®A#×
+A4  A4  AD(öATQìATzáAdzáAt£×AffAffAzáA\A£×A£×A¢žRAªÌÍAªÌÍA²áHAºõÃA»
+=AÃ
+=AË
+žAË33AÓ33AÛG®Aã\)Aã\)Aëp€Aó
+AóAûB×
+BáHBáHB	ë
+B
+õÃB  B  B
+=B{B{B
+
+žB"(öB"(öB&33B*=qB*G®B.G®B2QìB6\)B6\)B:ffB>p€B>záBBzáBF
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  >
+ž?£×?ë
+@	@J=q@
+
+ž@
+p€@¥Â@ÅÂ@Æ{@æffA\)A\)A
+A#®A3®A3×
+AD  AT  AT(öAdQìAtzáA=qAQìAffAffAzáA\A¢£×Aª£×AªžRA²ÌÍAºÌÍAºáHAÂõÃAË
+=AË
+=AÓ
+žAÛ33Aã33AãG®Aë\)Aó\)Aóp€Aû
+BÌÍBÌÍB×
+B	áHB
+áHB
+ë
+BõÃB  B  B
+=B
+{B"{B"
+žB&(öB*(öB*33B.=qB2G®B6G®B6QìB:\)B>\)B>ffBBp€BFzáBJzáBJ
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =áG®?\)?£×@Qì@HõÃ@ÌÍ@
+
+ž@¥
+ž@Åp€@ÅÂ@åÂA
+=A33A\)A#\)A3
+A3®AC®AS×
+AT  Ad  At(öA(öA=qA=qAQìAffAffA¢záAª\Aª£×A²£×AºžRAºÌÍAÂÌÍAÊáHAÊõÃAÓ
+=AÛ
+=Aã
+žAã33Aë33AóG®Aó\)Aû\)BžRBÂBÌÍB	ÌÍB
+×
+B
+áHBáHBë
+BõÃB  B
+  B"
+=B"{B&{B*
+žB*(öB.(öB233B6=qB6G®B:G®B>QìB>\)BB\)BFffBJp€BJzáBNzáBR
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =ÌÌÍ?{?{@®@HQì@(ö@zá@€ÌÍ@Å
+ž@Å
+ž@åp€AáHAáHA
+=A#33A3\)A3\)AC
+AS®AS®Ac×
+At  A  A{A(öA=qA=qAQìA¢ffAªffAªzáA²\Aº£×Aº£×AÂžRAÊÌÍAÊÌÍAÒáHAÚõÃAã
+=Aã
+=Aë
+žAó33Aó33AûG®B®B®BžRB	ÂB
+ÌÍB
+ÌÍB×
+BáHBáHBë
+B
+õÃB"  B"  B&
+=B*{B*{B.
+žB2(öB6(öB633B:=qB>G®B>G®BBQìBF\)BJ\)BJffBNp€BRzáBRzáBV
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =žQì?
+?ÌÍ@
+=@G
+=@×
+@(ö@€(ö@Äzá@ÄÌÍ@å
+žA\AžRAáHA"áHA3
+=A333AC\)AS\)AS
+Ac®As®Aë
+A  A  A{A(öA=qA¢=qAªQìAªffA²ffAºzáAº\AÂ£×AÊ£×AÊžRAÒÌÍAÚÌÍAâáHAâõÃAë
+=Aó
+=Aó
+žAû33BB£×B®B	®B
+žRB
+ÂBÌÍBÌÍB×
+BáHB
+áHB!ë
+B!õÃB&  B*  B*
+=B.{B2{B6
+žB6(öB:(öB>33B>=qBBG®BFG®BJQìBJ\)BN\)BRffBRp€BVzáBZzáBZ
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =\)?=q?
+@Â@Fff@
+@
+@£×
+@Ä(ö@Ä(ö@äzáAffA\A\A"žRA2áHA2áHAC
+=AS33AS\)Ac\)As
+A×
+A×
+Aë
+A  A  A{A¢(öAª=qAª=qA²QìAºffAºffAÂzáAÊ\AÊ£×AÒ£×AÚžRAâÌÍAâÌÍAêáHAòõÃAó
+=Aû
+=B\BBB	£×B
+®B
+®BžRBÂBÌÍBÌÍB
+×
+B!áHB!áHB%ë
+B)õÃB*  B.  B2
+=B6{B6{B:
+žB>(öB>(öBB33BF=qBJG®BJG®BNQìBR\)BR\)BVffBZp€BZzáB^záBb
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =uÂ?®?õÃ@
+ž@EÂ@áH@33@£
+@Ã
+@Ã×
+@ä(öA{A=qAffA"\A2\A2žRABáHARáHAS
+=Ac33As\)A®AÂA×
+A×
+Aë
+A  A¢  Aª{Aª(öA²=qAº=qAºQìAÂffAÊffAÊzáAÒ\AÚ£×Aâ£×AâžRAêÌÍAòÌÍAòáHAúõÃB
+B
+B\B	B
+B
+£×B®B®BžRBÂB
+ÌÍB!ÌÍB!×
+B%áHB)áHB)ë
+B-õÃB2  B6  B6
+=B:{B>{B>
+žBB(öBF(öBJ33BJ=qBNG®BRG®BRQìBV\)BZ\)BZffB^p€BbzáBfzáBf
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  =#×
+?ff?®@×
+@Dzá@\@áH@¢áH@Ã33@Ã
+@ã
+Aë
+A{A{A"=qA2ffA2\AB\ARžRARáHAbáHAs
+=AA®A®AÂA×
+A×
+A¡ë
+Aª  Aª  A²{Aº(öAº=qAÂ=qAÊQìAÊffAÒffAÚzáAâ\Aâ£×Aê£×AòžRAòÌÍAúÌÍBp€BzáB
+B	
+B
+\B
+BB£×B®B®B
+žRB!ÂB!ÌÍB%ÌÍB)×
+B)áHB-áHB1ë
+B5õÃB6  B:  B>
+=B>{BB{BF
+žBJ(öBJ(öBN33BR=qBRG®BVG®BZQìBZ\)B^\)BbffBfp€BfzáBjzáBn
+¿  ¿  ¿  ¿  ¿  ¿  <õÂ?
+
+ž?
+
+ž@33@C×
+@ë
+@=q@¢\@ÂáH@ÂáH@ã33AÂAÂAë
+A"{A2{A2=qABffAR\AR\AbžRAráHAp€A
+AA®A®AÂA¡×
+A©×
+A©ë
+A²  Aº  Aº{AÂ(öAÊ=qAÊ=qAÒQìAÚffAâffAâzáAê\Aò£×Aò£×AúžRBffBffBp€B	záB
+
+B
+
+B\BBB£×B
+®B!®B!žRB%ÂB)ÌÍB)ÌÍB-×
+B1áHB5áHB5ë
+B9õÃB>  B>  BB
+=BF{BJ{BJ
+žBN(öBR(öBR33BV=qBZG®BZG®B^QìBb\)Bf\)BfffBjp€BnzáBnzáBr
+¿  ¿  ¿  ¿  <#×
+?\?×
+@\@B\@@ë
+@¡ë
+@Â=q@Â\@âáHAp€AAÂA!ÂA1ë
+A2{AB{AR=qARffAb\Ar\A\)Ap€Ap€A
+AA®A¡®A©ÂA©×
+A±×
+A¹ë
+Aº  AÂ  AÊ{AÊ(öAÒ=qAÚ=qAâQìAâffAêffAòzáAò\Aú£×BQìB\)BffB	ffB
+p€B
+záB
+B
+B\BB
+B!£×B!®B%®B)žRB)ÂB-ÌÍB1ÌÍB5×
+B5áHB9áHB=ë
+B=õÃBB  BF  BJ
+=BJ{BN{BR
+žBR(öBV(öBZ33BZ=qB^G®BbG®BfQìBf\)Bj\)BnffBnp€BrzáBvzáBz
+¿  ¿      ?G®?G®@G®@Aë
+@G®@G®@¡@Áë
+@Áë
+@â=qAG®Ap€Ap€A!A1ÂA1ÂAAë
+AR{AR{Ab=qArffAG®AG®A\)Ap€Ap€A
+A¡A©®A©®A±ÂA¹×
+A¹×
+AÁë
+AÊ  AÊ  AÒ{AÚ(öAâ=qAâ=qAêQìAòffAòffAúzáBG®BQìBQìB	\)B
+ffB
+ffBp€BzáB
+B
+B
+\B!B!B%£×B)®B)®B-žRB1ÂB5ÌÍB5ÌÍB9×
+B=áHB=áHBAë
+BEõÃBJ  BJ  BN
+=BR{BR{BV
+žBZ(öBZ(öB^33Bb=qBfG®BfG®BjQìBn\)Bn\)BrffBvp€BzzáBzzáB~
+¿  ¿  ?  @ £×@@£×@£×@õÃ@¡G®@ÁG®@Á@áë
+A õÃA
+žAG®A!p€A1p€A1AAÂAQÂAQë
+Ab{Ar{A
+žA33AG®AG®A\)Ap€A¡p€A©
+A©A±®A¹®A¹ÂAÁ×
+AÉ×
+AÉë
+AÒ  AÚ  Aâ{Aâ(öAê=qAò=qAòQìAúffB33B=qBG®B	QìB
+QìB
+\)BffBffBp€BzáB
+
+B!
+B!\B%B)B)£×B-®B1®B5žRB5ÂB9ÌÍB=ÌÍB=×
+BAáHBEáHBIë
+BIõÃBN  BR  BR
+=BV{BZ{BZ
+žB^(öBb(öBf33Bf=qBjG®BnG®BnQìBr\)Bv\)BzffBzp€B~zá¿  ¿  ¿  ¿  @@  @Qì@Qì@ £×@ÀõÃ@ÁG®@áG®A ÌÍAõÃAõÃA!
+žA1G®A1p€AAp€AQAQÂAaÂAqë
+A
+=A
+=A
+žA33AG®AG®A¡\)A©p€A©p€A±
+A¹A¹®AÁ®AÉÂAÉ×
+AÑ×
+AÙë
+Aâ  Aâ  Aê{Aò(öAò=qAú=qB(öB33B33B	=qB
+G®B
+QìBQìB\)BffBffB
+p€B!záB!
+B%
+B)\B)B-B1£×B5®B5®B9žRB=ÂB=ÌÍBAÌÍBE×
+BIáHBIáHBMë
+BQõÃBR  BV  BZ
+=BZ{B^{Bb
+žBf(öBf(öBj33Bn=qBnG®BrG®BvQìBz\)Bz\)B~ff¿  ¿  ¿  ¿  ¿  ¿  @  @ Qì@ÀQì@À£×@àõÃA £×A£×AÌÍA õÃA0õÃA1
+žAAG®AQp€AQp€AaAqÂAáHAõÃA
+=A
+=A
+žA33A¡G®A©G®A©\)A±p€A¹p€A¹
+AÁAÉ®AÉ®AÑÂAÙ×
+Aá×
+Aáë
+Aê  Aò  Aò{Aú(öB
+žB
+žB(öB	33B
+33B
+=qBG®BQìBQìB\)B
+ffB!ffB!p€B%záB)
+B)
+B-\B1B5B5£×B9®B=®B=žRBAÂBEÌÍBIÌÍBI×
+BMáHBQáHBQë
+BUõÃBZ  BZ  B^
+=Bb{Bf{Bf
+žBj(öBn(öBn33Br=qBvG®BzG®BzQìB~\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @À  @ÀQì@àQìA QìAzáA£×A £×A0ÌÍA0õÃA@õÃAQ
+žAQG®Aap€Aqp€AÌÍAáHAáHAõÃA
+=A
+=A¡
+žA©33A©G®A±G®A¹\)A¹p€AÁp€AÉ
+AÉAÑ®AÙ®AáÂAá×
+Aé×
+Añë
+Aò  Aú  B
+=B{B
+žB	
+žB
+(öB
+33B33B=qBG®BQìB
+QìB!\)B!ffB%ffB)p€B)záB-
+B1
+B5\B5B9B=£×B=®BA®BEžRBIÂBIÌÍBMÌÍBQ×
+BQáHBUáHBYë
+BYõÃB^  Bb  Bf
+=Bf{Bj{Bn
+žBn(öBr(öBv33Bz=qBzG®B~G®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @à  A (öA(öAQìA záA0£×A0£×A@ÌÍAPõÃAPõÃAa
+žAqG®AžRAžRAÌÍAáHAáHAõÃA¡
+=A©
+=A©
+žA±33A¹G®A¹G®AÁ\)AÉp€AÉp€AÑ
+AÙAá®Aá®AéÂAñ×
+Añ×
+Aùë
+B  B  B
+=B	{B
+
+žB
+
+žB(öB33B33B=qB
+G®B!QìB!QìB%\)B)ffB)ffB-p€B1záB5
+B5
+B9\B=B=BA£×BE®BI®BIžRBMÂBQÌÍBQÌÍBU×
+BYáHBYáHB]ë
+BaõÃBf  Bf  Bj
+=Bn{Bn{Br
+žBv(öBz(öBz33B~=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A(öA (öA0QìA0záA@£×AP£×APÌÍA`õÃApõÃA\A£×AžRAžRAÌÍAáHA áHAšõÃA©
+=A±
+=A¹
+žA¹33AÁG®AÉG®AÉ\)AÑp€AÙp€Aá
+AáAé®Añ®AñÂAù×
+B ë
+BõÃB  B	  B
+=B
+{B
+žB
+žB(öB33B
+33B!=qB!G®B%QìB)QìB)\)B-ffB1ffB5p€B5záB9
+B=
+B=\BABEBI£×BI®BM®BQžRBQÂBUÌÍBYÌÍBY×
+B]áHBaáHBeë
+BeõÃBj  Bn  Bn
+=Br{Bv{Bz
+žBz(öB~(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A   A0(öA0(öA@QìAPzáAP£×A`£×ApÌÍAzáAzáA\A£×AžRAžRA ÌÍAšáHAšáHA°õÃA¹
+=A¹
+=AÁ
+žAÉ33AÉG®AÑG®AÙ\)Aáp€Aáp€Aé
+AñAñ®Aù®B áHBë
+Bë
+BõÃB
+  B
+  B
+=B{B
+žB
+žB
+(öB!33B!33B%=qB)G®B)QìB-QìB1\)B5ffB5ffB9p€B=záB=
+BA
+BE\BIBIBM£×BQ®BQ®BUžRBYÂBYÌÍB]ÌÍBa×
+BeáHBeáHBië
+BmõÃBn  Br  Bv
+=Bz{Bz{B~
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A0  A@(öAP(öAPQìA`záAp£×AQìAffAzáAzáA\A£×A žRAšžRAšÌÍA°áHAžáHAžõÃAÁ
+=AÉ
+=AÉ
+žAÑ33AÙG®AáG®Aá\)Aép€Añp€Añ
+AùB ×
+B×
+BáHBë
+B
+ë
+B
+õÃB  B  B
+=B{B
+
+žB!
+žB!(öB%33B)33B)=qB-G®B1QìB5QìB5\)B9ffB=ffB=p€BAzáBE
+BI
+BI\BMBQBQ£×BU®BY®BYžRB]ÂBaÌÍBeÌÍBe×
+BiáHBmáHBmë
+BqõÃBv  Bz  Bz
+=B~{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AP  AP(öA`(öApQìA=qAQìAQìAffAzáAzáA \Aš£×AšžRA°žRAžÌÍAžáHAÀáHAÈõÃAÉ
+=AÑ
+=AÙ
+žAá33AáG®AéG®Añ\)Añp€Aùp€B ÂBÌÍB×
+B×
+B
+áHB
+ë
+Bë
+BõÃB  B  B
+
+=B!{B!
+žB%
+žB)(öB)33B-33B1=qB5G®B5QìB9QìB=\)B=ffBAffBEp€BIzáBI
+BM
+BQ\BQBUBY£×BY®B]®BažRBeÂBeÌÍBiÌÍBm×
+BmáHBqáHBuë
+ByõÃBz  B~  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A`  Ap(öA{A(öA=qAQìAQìAffA záAšzáAš\A°£×AžžRAžžRAÀÌÍAÈáHAÈáHAÐõÃAÙ
+=Aá
+=Aá
+žAé33AñG®AñG®Aù\)B žRBžRBÂBÌÍB
+×
+B
+×
+BáHBë
+Bë
+BõÃB
+  B!  B!
+=B%{B)
+žB)
+žB-(öB133B533B5=qB9G®B=QìB=QìBA\)BEffBIffBIp€BMzáBQ
+BQ
+BU\BYBYB]£×Ba®Be®BežRBiÂBmÌÍBmÌÍBq×
+BuáHByáHByë
+B}õÃ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{A{A(öA=qAQìA QìAšffAšzáA°záAž\Až£×AÀžRAÈžRAÈÌÍAÐáHAØáHAàõÃAá
+=Aé
+=Añ
+žAñ33AùG®B £×B®BžRBžRB
+ÂB
+ÌÍB×
+B×
+BáHBë
+B
+ë
+B õÃB!  B%  B)
+=B){B-
+žB1
+žB5(öB533B933B==qB=G®BAQìBEQìBI\)BIffBMffBQp€BQzáBU
+BY
+BY\B]BaBe£×Be®Bi®BmžRBmÂBqÌÍBuÌÍBy×
+ByáHB}áH¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{A{A(öA =qAšQìAšQìA°ffAžzáAžzáAÀ\AÈ£×AÈžRAÐžRAØÌÍAàáHAàáHAèõÃAñ
+=Añ
+=Aù
+žB B£×B£×B®B
+žRB
+žRBÂBÌÍB×
+B×
+B
+áHB ë
+B ë
+B$õÃB)  B)  B-
+=B1{B5
+žB5
+žB9(öB=33B=33BA=qBEG®BIQìBIQìBM\)BQffBQffBUp€BYzáBY
+B]
+Ba\BeBeBi£×Bm®Bm®BqžRBuÂByÌÍByÌÍB}×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{A {Aš(öAš=qA°QìAžQìAžffAÀzáAÈzáAÈ\AÐ£×AØžRAàžRAàÌÍAèáHAðáHAðõÃAù
+=B 
+B\BB£×B
+£×B
+®BžRBžRBÂBÌÍB
+×
+B ×
+B áHB$ë
+B(ë
+B(õÃB-  B1  B5
+=B5{B9
+žB=
+žB=(öBA33BE33BI=qBIG®BMQìBQQìBQ\)BUffBYffBYp€B]záBa
+Be
+Be\BiBmBm£×Bq®Bu®ByžRByÂB}ÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A   Aš{Aš{A°(öAž=qAžQìAÀQìAÈffAÈzáAÐzáAØ\Aà£×AàžRAèžRAðÌÍAðáHAøáHB záB
+B
+B\B
+B
+£×B£×B®BžRBžRB
+ÂB ÌÍB ×
+B$×
+B(áHB(ë
+B,ë
+B0õÃB5  B5  B9
+=B={B=
+žBA
+žBE(öBI33BI33BM=qBQG®BQQìBUQìBY\)BYffB]ffBap€BezáBe
+Bi
+Bm\BmBqBu£×By®By®B}žR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aš  A°{Až{Až(öAÀ=qAÈQìAÈQìAÐffAØzáAàzáAà\Aè£×AðžRAðžRAøÌÍB p€Bp€BzáB
+B
+
+B
+\BB£×B£×B®B
+žRB žRB ÂB$ÌÍB(×
+B(×
+B,áHB0ë
+B4ë
+B4õÃB9  B=  B=
+=BA{BE
+žBI
+žBI(öBM33BQ33BQ=qBUG®BYQìBYQìB]\)BaffBeffBep€BizáBm
+Bm
+Bq\BuByBy£×B}®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Až  Až{AÀ{AÈ(öAÈ=qAÐQìAØQìAàffAàzáAèzáAð\Að£×AøžRB \)BffBp€Bp€B
+záB
+
+B
+B\BB£×B
+£×B ®B žRB$žRB(ÂB(ÌÍB,×
+B0×
+B4áHB4ë
+B8ë
+B<õÃB=  BA  BE
+=BI{BI
+žBM
+žBQ(öBQ33BU33BY=qBYG®B]QìBaQìBe\)BeffBiffBmp€BmzáBq
+Bu
+By\ByB}¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÀ  AÈ{AÈ{AÐ(öAØ=qAàQìAàQìAèffAðzáAðzáAø\B QìB\)B\)BffB
+p€B
+p€BzáB
+B
+B\B
+B £×B £×B$®B(žRB(žRB,ÂB0ÌÍB4×
+B4×
+B8áHB<ë
+B<ë
+B@õÃBE  BI  BI
+=BM{BQ
+žBQ
+žBU(öBY33BY33B]=qBaG®BeQìBeQìBi\)BmffBmffBqp€BuzáBy
+By
+B}\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÈ  AÐ{AØ{Aà(öAà=qAèQìAðQìAðffAøzáB =qBG®BQìB\)B
+\)B
+ffBp€Bp€BzáB
+B
+
+B \B B$£×B(£×B(®B,žRB0žRB4ÂB4ÌÍB8×
+B<×
+B<áHB@ë
+BDë
+BHõÃBI  BM  BQ
+=BQ{BU
+žBY
+žBY(öB]33Ba33Be=qBeG®BiQìBmQìBm\)BqffBuffByp€ByzáB}
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AØ  Aà{Aà{Aè(öAð=qAðQìAøQìB 33B=qB=qBG®B
+QìB
+\)B\)BffBp€Bp€B
+záB 
+B 
+B$\B(B(£×B,£×B0®B4žRB4žRB8ÂB<ÌÍB<×
+B@×
+BDáHBHë
+BHë
+BLõÃBQ  BQ  BU
+=BY{BY
+žB]
+žBa(öBe33Be33Bi=qBmG®BmQìBqQìBu\)ByffByffB}p€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aà  Aè{Að{Að(öAø=qB (öB(öB33B=qB
+=qB
+G®BQìB\)B\)BffB
+p€B p€B záB$
+B(
+B(\B,B0£×B4£×B4®B8žRB<žRB<ÂB@ÌÍBD×
+BH×
+BHáHBLë
+BPë
+BPõÃBU  BY  BY
+=B]{Ba
+žBe
+žBe(öBi33Bm33Bm=qBqG®BuQìByQìBy\)B}ff¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Að  Að{Aø{B {B
+žB(öB(öB
+33B
+=qB=qBG®BQìB\)B
+\)B ffB p€B$p€B(záB(
+B,
+B0\B4B4£×B8£×B<®B<žRB@žRBDÂBHÌÍBH×
+BL×
+BPáHBPë
+BTë
+BXõÃBY  B]  Ba
+=Be{Be
+žBi
+žBm(öBm33Bq33Bu=qByG®ByQìB}Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aø  B 
+=B
+=B{B
+žB
+(öB
+(öB33B=qB=qBG®B
+QìB \)B \)B$ffB(p€B(p€B,záB0
+B4
+B4\B8B<£×B<£×B@®BDžRBHžRBHÂBLÌÍBP×
+BP×
+BTáHBXë
+BXë
+B\õÃBa  Be  Be
+=Bi{Bm
+žBm
+žBq(öBu33By33By=qB}G®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+=B
+=B
+{B
+
+žB(öB(öB33B=qB
+=qB G®B QìB$\)B(\)B(ffB,p€B0p€B4záB4
+B8
+B<\B<B@£×BD£×BH®BHžRBLžRBPÂBPÌÍBT×
+BX×
+BXáHB\ë
+B`ë
+BdõÃBe  Bi  Bm
+=Bm{Bq
+žBu
+žBy(öBy33B}33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+
+=B
+
+=B{B
+žB(öB(öB
+33B =qB =qB$G®B(QìB(\)B,\)B0ffB4p€B4p€B8záB<
+B<
+B@\BDBH£×BH£×BL®BPžRBPžRBTÂBXÌÍBX×
+B\×
+B`áHBdë
+Bdë
+BhõÃBm  Bm  Bq
+=Bu{By
+žBy
+žB}(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+  B
+=B
+=B{B
+žB
+(öB (öB 33B$=qB(=qB(G®B,QìB0\)B4\)B4ffB8p€B<p€B<záB@
+BD
+BH\BHBL£×BP£×BP®BTžRBXžRBXÂB\ÌÍB`×
+Bd×
+BdáHBhë
+Blë
+BlõÃBq  Bu  By
+=By{B}
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+=B
+=B
+{B 
+žB (öB$(öB(33B(=qB,=qB0G®B4QìB4\)B8\)B<ffB<p€B@p€BDzáBH
+BH
+BL\BPBP£×BT£×BX®BXžRB\žRB`ÂBdÌÍBd×
+Bh×
+BláHBlë
+Bpë
+BtõÃBy  By  B}
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+
+=B 
+=B {B$
+žB((öB((öB,33B0=qB4=qB4G®B8QìB<\)B<\)B@ffBDp€BHp€BHzáBL
+BP
+BP\BTBX£×BX£×B\®B`žRBdžRBdÂBhÌÍBl×
+Bl×
+BpáHBtë
+Bxë
+BxõÃB}  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B   B 
+=B$
+=B({B(
+žB,(öB0(öB433B4=qB8=qB<G®B<QìB@\)BD\)BHffBHp€BLp€BPzáBP
+BT
+BX\BXB\£×B`£×Bd®BdžRBhžRBlÂBlÌÍBp×
+Bt×
+BxáHBxë
+B|ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B$  B(
+=B(
+=B,{B0
+žB4(öB4(öB833B<=qB<=qB@G®BDQìBH\)BH\)BLffBPp€BPp€BTzáBX
+BX
+B\\B`Bd£×Bd£×Bh®BlžRBlžRBpÂBtÌÍBx×
+Bx×
+B|áH¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B(  B,
+=B0
+=B4{B4
+žB8(öB<(öB<33B@=qBD=qBHG®BHQìBL\)BP\)BPffBTp€BXp€BXzáB\
+B`
+Bd\BdBh£×Bl£×Bl®BpžRBtžRBxÂBxÌÍB|×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B0  B4
+=B4
+=B8{B<
+žB<(öB@(öBD33BH=qBH=qBLG®BPQìBP\)BT\)BXffBXp€B\p€B`záBd
+Bd
+Bh\BlBl£×Bp£×Bt®BxžRBxžRB|Â¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B4  B8
+=B<
+=B<{B@
+žBD(öBH(öBH33BL=qBP=qBPG®BTQìBX\)BX\)B\ffB`p€Bdp€BdzáBh
+Bl
+Bl\BpBt£×Bx£×Bx®B|žR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B<  B<
+=B@
+=BD{BH
+žBH(öBL(öBP33BP=qBT=qBXG®BXQìB\\)B`\)BdffBdp€Bhp€BlzáBl
+Bp
+Bt\BxBx£×B|£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B@  BD
+=BH
+=BH{BL
+žBP(öBP(öBT33BX=qBX=qB\G®B`QìBd\)Bd\)BhffBlp€Blp€BpzáBt
+Bx
+Bx\B|¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BH  BH
+=BL
+=BP{BP
+žBT(öBX(öBX33B\=qB`=qBdG®BdQìBh\)Bl\)BlffBpp€Btp€BxzáBx
+B|
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BL  BP
+=BP
+=BT{BX
+žBX(öB\(öB`33Bd=qBd=qBhG®BlQìBl\)Bp\)BtffBxp€Bxp€B|zá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BP  BT
+=BX
+=BX{B\
+žB`(öBd(öBd33Bh=qBl=qBlG®BpQìBt\)Bx\)BxffB|p€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BX  BX
+=B\
+=B`{Bd
+žBd(öBh(öBl33Bl=qBp=qBtG®BxQìBx\)B|\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B\  B`
+=Bd
+=Bd{Bh
+žBl(öBl(öBp33Bt=qBx=qBxG®B|Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bd  Bd
+=Bh
+=Bl{Bl
+žBp(öBt(öBx33Bx=qB|=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bh  Bl
+=Bl
+=Bp{Bt
+žBx(öBx(öB|33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bl  Bp
+=Bt
+=Bx{Bx
+žB|(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bt  Bx
+=Bx
+=B|{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bx  B|
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 <#×
+<£×
+<õÂ=#×
+=LÌÍ=uÂ=\)=£×
+=žQì=ÌÌÍ=áG®=õÂ>
+ž>\)>>#×
+>.{>8Qì>B\>LÌÍ>W
+=>aG®>k
+>uÂ>  >
+
+ž>=q>\)>zá>>žR>£×
+>šõÃ>®{>³33>žQì>œp€>Â\>Ç®>ÌÌÍ>Ñë
+>×
+=>Ü(ö>áG®>æff>ë
+>ð£×>õÂ>úáH?   ?\?
+ž?®?
+=q?
+ÌÍ?\)?ë
+?zá?
+=??
+(ö?
+žR?!G®?  ?G®?\?×
+?
+
+ž?ff?®?õÃ?=q?
+?ÌÍ?{?\)?£×?ë
+?33?zá?Â?
+>?Qì??áH?(ö?p€?žR?   ?¡G®?¢\?£×
+?¥
+ž?Šff?§®?šõÂ?ª=q?«
+?¬ÌÍ?®{?¯\)?°£×?±ë
+?³33?Žzá?µÂ?·
+>?žQì?¹?ºáH?Œ(ö?œp€?ŸžR?À  ?ÁG®?Â\?Ã×
+?Å
+ž?Æff?Ç®?ÈõÂ?Ê=p?Ë
+
+?ÌÌÍ?Î{?Ï\)?Ð£×@   @ £×@G®@ë
+@\@33@×
+@zá@
+ž@Â@ff@
+=@®@Qì@õÃ@	@
+=q@
+áH@
+
+@
+(ö@
+ÌÍ@
+p€@{@žR@\)@  @£×@G®@ë
+@\@33@×
+@zá@
+ž@Â@ff@
+>@®@Qì@õÂ@@=q@áH@
+@
+(ö@
+ÌÍ@
+p€@
+{@
+žR@\)@   @ £×@!G®@!ë
+@"\@#33@#×
+@$zá@%
+ž@%Â@&ff@'
+>@'®@(Qì@@  @@£×@AG®@Aë
+@B\@C33@C×
+@Dzá@E
+ž@EÂ@Fff@G
+=@G®@HQì@HõÃ@I@J=q@JáH@K
+@L(ö@LÌÍ@Mp€@N{@NžR@O\)@P  @P£×@QG®@Që
+@R\@S33@S×
+@Tzá@U
+ž@UÂ@Vff@W
+>@W®@XQì@XõÂ@Y@Z=q@ZáH@[
+@\(ö@\ÌÍ@]p€@^{@^žR@_\)@`  @`£×@aG®@aë
+@b\@c33@c×
+@dzá@e
+ž@eÂ@fff@g
+>@g®@hQì@  @Qì@£×@õÃ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@zá@ÌÍ@
+
+ž@
+p€@
+Â@{@ff@žR@
+=@\)@®@  @Qì@£×@õÃ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@zá@ÌÍ@
+ž@p€@Â@{@ff@žR@
+=@\)@®@  @Qì@£×@õÂ@G®@@ë
+@=q@\@áH@33@
+@×
+@(ö@   @ Qì@ £×@ õÃ@¡G®@¡@¡ë
+@¢=q@¢\@¢áH@£33@£
+@£×
+@€(ö@€zá@€ÌÍ@¥
+ž@¥p€@¥Â@Š{@Šff@ŠžR@§
+=@§\)@§®@š  @šQì@š£×@šõÃ@©G®@©@©ë
+@ª=q@ª\@ªáH@«33@«
+@«×
+@¬(ö@¬zá@¬ÌÍ@­
+ž@­p€@­Â@®{@®ff@®žR@¯
+=@¯\)@¯®@°  @°Qì@°£×@°õÂ@±G®@±@±ë
+@²=q@²\@²áH@³33@³
+@³×
+@Ž(ö@À  @ÀQì@À£×@ÀõÃ@ÁG®@Á@Áë
+@Â=q@Â\@ÂáH@Ã33@Ã
+@Ã×
+@Ä(ö@Äzá@ÄÌÍ@Å
+ž@Åp€@ÅÂ@Æ{@Æff@ÆžR@Ç
+=@Ç\)@Ç®@È  @ÈQì@È£×@ÈõÃ@ÉG®@É@Éë
+@Ê=q@Ê\@ÊáH@Ë33@Ë
+@Ë×
+@Ì(ö@Ìzá@ÌÌÍ@Í
+ž@Íp€@ÍÂ@Î{@Îff@ÎžR@Ï
+=@Ï\)@Ï®@Ð  @ÐQì@Ð£×@ÐõÂ@ÑG®@Ñ@Ñë
+@Ò=q@Ò\@ÒáH@Ó33@Ó
+@Ó×
+@Ô(ö@à  @àQì@à£×@àõÃ@áG®@á@áë
+@â=q@â\@âáH@ã33@ã
+@ã×
+@ä(ö@äzá@äÌÍ@å
+ž@åp€@åÂ@æ{@æff@æžR@ç
+=@ç\)@ç®@è  @èQì@è£×@èõÃ@éG®@é@éë
+@ê=q@ê\@êáH@ë33@ë
+@ë×
+@ì(ö@ìzá@ìÌÍ@í
+ž@íp€@íÂ@î{@îff@îžR@ï
+=@ï\)@ï®@ð  @ðQì@ð£×@ðõÂ@ñG®@ñ@ñë
+@ò=q@ò\@òáH@ó33@ó
+@ó×
+@ô(öA   A (öA QìA záA £×A ÌÍA õÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA	
+žA	G®A	p€A	A	ÂA	ë
+A
+{A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A=qAffA\AžRAáHA
+=A33A\)A
+A®A×
+A  A(öAQìAzáA£×AÌÍAõÃA
+žAG®Ap€AAÂAë
+A{A   A (öA QìA záA £×A ÌÍA õÃA!
+žA!G®A!p€A!A!ÂA!ë
+A"{A"=qA"ffA"\A"žRA"áHA#
+=A#33A#\)A#
+A#®A#×
+A$  A$(öA$QìA$záA$£×A$ÌÍA$õÃA%
+žA%G®A%p€A%A%ÂA%ë
+A&{A&=qA&ffA&\A&žRA&áHA'
+=A'33A'\)A'
+A'®A'×
+A(  A((öA(QìA(záA(£×A(ÌÍA(õÃA)
+žA)G®A)p€A)A)ÂA)ë
+A*{A0  A0(öA0QìA0záA0£×A0ÌÍA0õÃA1
+žA1G®A1p€A1A1ÂA1ë
+A2{A2=qA2ffA2\A2žRA2áHA3
+=A333A3\)A3
+A3®A3×
+A4  A4(öA4QìA4záA4£×A4ÌÍA4õÃA5
+žA5G®A5p€A5A5ÂA5ë
+A6{A6=qA6ffA6\A6žRA6áHA7
+=A733A7\)A7
+A7®A7×
+A8  A8(öA8QìA8záA8£×A8ÌÍA8õÃA9
+žA9G®A9p€A9A9ÂA9ë
+A:{A@  A@(öA@QìA@záA@£×A@ÌÍA@õÃAA
+žAAG®AAp€AAAAÂAAë
+AB{AB=qABffAB\ABžRABáHAC
+=AC33AC\)AC
+AC®AC×
+AD  AD(öADQìADzáAD£×ADÌÍADõÃAE
+žAEG®AEp€AEAEÂAEë
+AF{AF=qAFffAF\AFžRAFáHAG
+=AG33AG\)AG
+AG®AG×
+AH  AH(öAHQìAHzáAH£×AHÌÍAHõÃAI
+žAIG®AIp€AIAIÂAIë
+AJ{AP  AP(öAPQìAPzáAP£×APÌÍAPõÃAQ
+žAQG®AQp€AQAQÂAQë
+AR{AR=qARffAR\ARžRARáHAS
+=AS33AS\)AS
+AS®AS×
+AT  AT(öATQìATzáAT£×ATÌÍATõÃAU
+žAUG®AUp€AUAUÂAUë
+AV{AV=qAVffAV\AVžRAVáHAW
+=AW33AW\)AW
+AW®AW×
+AX  AX(öAXQìAXzáAX£×AXÌÍAXõÃAY
+žAYG®AYp€AYAYÂAYë
+AZ{A`  A`(öA`QìA`záA`£×A`ÌÍA`õÃAa
+žAaG®Aap€AaAaÂAaë
+Ab{Ab=qAbffAb\AbžRAbáHAc
+=Ac33Ac\)Ac
+Ac®Ac×
+Ad  Ad(öAdQìAdzáAd£×AdÌÍAdõÃAe
+žAeG®Aep€AeAeÂAeë
+Af{Af=qAfffAf\AfžRAfáHAg
+=Ag33Ag\)Ag
+Ag®Ag×
+Ah  Ah(öAhQìAhzáAh£×AhÌÍAhõÃAi
+žAiG®Aip€AiAiÂAië
+Aj{Ap  Ap(öApQìApzáAp£×ApÌÍApõÃAq
+žAqG®Aqp€AqAqÂAqë
+Ar{Ar=qArffAr\AržRAráHAs
+=As33As\)As
+As®As×
+At  At(öAtQìAtzáAt£×AtÌÍAtõÃAu
+žAuG®Aup€AuAuÂAuë
+Av{Av=qAvffAv\AvžRAváHAw
+=Aw33Aw\)Aw
+Aw®Aw×
+Ax  Ax(öAxQìAxzáAx£×AxÌÍAxõÃAy
+žAyG®Ayp€AyAyÂAyë
+Az{A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A
+žA33AG®A\)Ap€A
+AA®AÂA×
+Aë
+A  A{A(öA=qAQìAffAzáA\A£×AžRAÌÍAáHAõÃA
+=A   A {A (öA =qA QìA ffA záA \A £×A žRA ÌÍA áHA õÃA¡
+=A¡
+žA¡33A¡G®A¡\)A¡p€A¡
+A¡A¡®A¡ÂA¡×
+A¡ë
+A¢  A¢{A¢(öA¢=qA¢QìA¢ffA¢záA¢\A¢£×A¢žRA¢ÌÍA¢áHA¢õÃA£
+=A£
+žA£33A£G®A£\)A£p€A£
+A£A£®A£ÂA£×
+A£ë
+A€  A€{A€(öA€=qA€QìA€ffA€záA€\A€£×A€žRA€ÌÍA€áHA€õÃA¥
+=Aš  Aš{Aš(öAš=qAšQìAšffAšzáAš\Aš£×AšžRAšÌÍAšáHAšõÃA©
+=A©
+žA©33A©G®A©\)A©p€A©
+A©A©®A©ÂA©×
+A©ë
+Aª  Aª{Aª(öAª=qAªQìAªffAªzáAª\Aª£×AªžRAªÌÍAªáHAªõÃA«
+=A«
+žA«33A«G®A«\)A«p€A«
+A«A«®A«ÂA«×
+A«ë
+A¬  A¬{A¬(öA¬=qA¬QìA¬ffA¬záA¬\A¬£×A¬žRA¬ÌÍA¬áHA¬õÃA­
+=A°  A°{A°(öA°=qA°QìA°ffA°záA°\A°£×A°žRA°ÌÍA°áHA°õÃA±
+=A±
+žA±33A±G®A±\)A±p€A±
+A±A±®A±ÂA±×
+A±ë
+A²  A²{A²(öA²=qA²QìA²ffA²záA²\A²£×A²žRA²ÌÍA²áHA²õÃA³
+=A³
+žA³33A³G®A³\)A³p€A³
+A³A³®A³ÂA³×
+A³ë
+AŽ  AŽ{AŽ(öAŽ=qAŽQìAŽffAŽzáAŽ\AŽ£×AŽžRAŽÌÍAŽáHAŽõÃAµ
+=Až  Až{Až(öAž=qAžQìAžffAžzáAž\Až£×AžžRAžÌÍAžáHAžõÃA¹
+=A¹
+žA¹33A¹G®A¹\)A¹p€A¹
+A¹A¹®A¹ÂA¹×
+A¹ë
+Aº  Aº{Aº(öAº=qAºQìAºffAºzáAº\Aº£×AºžRAºÌÍAºáHAºõÃA»
+=A»
+žA»33A»G®A»\)A»p€A»
+A»A»®A»ÂA»×
+A»ë
+AŒ  AŒ{AŒ(öAŒ=qAŒQìAŒffAŒzáAŒ\AŒ£×AŒžRAŒÌÍAŒáHAŒõÃAœ
+=AÀ  AÀ{AÀ(öAÀ=qAÀQìAÀffAÀzáAÀ\AÀ£×AÀžRAÀÌÍAÀáHAÀõÃAÁ
+=AÁ
+žAÁ33AÁG®AÁ\)AÁp€AÁ
+AÁAÁ®AÁÂAÁ×
+AÁë
+AÂ  AÂ{AÂ(öAÂ=qAÂQìAÂffAÂzáAÂ\AÂ£×AÂžRAÂÌÍAÂáHAÂõÃAÃ
+=AÃ
+žAÃ33AÃG®AÃ\)AÃp€AÃ
+AÃAÃ®AÃÂAÃ×
+AÃë
+AÄ  AÄ{AÄ(öAÄ=qAÄQìAÄffAÄzáAÄ\AÄ£×AÄžRAÄÌÍAÄáHAÄõÃAÅ
+=AÈ  AÈ{AÈ(öAÈ=qAÈQìAÈffAÈzáAÈ\AÈ£×AÈžRAÈÌÍAÈáHAÈõÃAÉ
+=AÉ
+žAÉ33AÉG®AÉ\)AÉp€AÉ
+AÉAÉ®AÉÂAÉ×
+AÉë
+AÊ  AÊ{AÊ(öAÊ=qAÊQìAÊffAÊzáAÊ\AÊ£×AÊžRAÊÌÍAÊáHAÊõÃAË
+=AË
+žAË33AËG®AË\)AËp€AË
+AËAË®AËÂAË×
+AËë
+AÌ  AÌ{AÌ(öAÌ=qAÌQìAÌffAÌzáAÌ\AÌ£×AÌžRAÌÌÍAÌáHAÌõÃAÍ
+=AÐ  AÐ{AÐ(öAÐ=qAÐQìAÐffAÐzáAÐ\AÐ£×AÐžRAÐÌÍAÐáHAÐõÃAÑ
+=AÑ
+žAÑ33AÑG®AÑ\)AÑp€AÑ
+AÑAÑ®AÑÂAÑ×
+AÑë
+AÒ  AÒ{AÒ(öAÒ=qAÒQìAÒffAÒzáAÒ\AÒ£×AÒžRAÒÌÍAÒáHAÒõÃAÓ
+=AÓ
+žAÓ33AÓG®AÓ\)AÓp€AÓ
+AÓAÓ®AÓÂAÓ×
+AÓë
+AÔ  AÔ{AÔ(öAÔ=qAÔQìAÔffAÔzáAÔ\AÔ£×AÔžRAÔÌÍAÔáHAÔõÃAÕ
+=AØ  AØ{AØ(öAØ=qAØQìAØffAØzáAØ\AØ£×AØžRAØÌÍAØáHAØõÃAÙ
+=AÙ
+žAÙ33AÙG®AÙ\)AÙp€AÙ
+AÙAÙ®AÙÂAÙ×
+AÙë
+AÚ  AÚ{AÚ(öAÚ=qAÚQìAÚffAÚzáAÚ\AÚ£×AÚžRAÚÌÍAÚáHAÚõÃAÛ
+=AÛ
+žAÛ33AÛG®AÛ\)AÛp€AÛ
+AÛAÛ®AÛÂAÛ×
+AÛë
+AÜ  AÜ{AÜ(öAÜ=qAÜQìAÜffAÜzáAÜ\AÜ£×AÜžRAÜÌÍAÜáHAÜõÃAÝ
+=Aà  Aà{Aà(öAà=qAàQìAàffAàzáAà\Aà£×AàžRAàÌÍAàáHAàõÃAá
+=Aá
+žAá33AáG®Aá\)Aáp€Aá
+AáAá®AáÂAá×
+Aáë
+Aâ  Aâ{Aâ(öAâ=qAâQìAâffAâzáAâ\Aâ£×AâžRAâÌÍAâáHAâõÃAã
+=Aã
+žAã33AãG®Aã\)Aãp€Aã
+AãAã®AãÂAã×
+Aãë
+Aä  Aä{Aä(öAä=qAäQìAäffAäzáAä\Aä£×AäžRAäÌÍAäáHAäõÃAå
+=Aè  Aè{Aè(öAè=qAèQìAèffAèzáAè\Aè£×AèžRAèÌÍAèáHAèõÃAé
+=Aé
+žAé33AéG®Aé\)Aép€Aé
+AéAé®AéÂAé×
+Aéë
+Aê  Aê{Aê(öAê=qAêQìAêffAêzáAê\Aê£×AêžRAêÌÍAêáHAêõÃAë
+=Aë
+žAë33AëG®Aë\)Aëp€Aë
+AëAë®AëÂAë×
+Aëë
+Aì  Aì{Aì(öAì=qAìQìAìffAìzáAì\Aì£×AìžRAìÌÍAìáHAìõÃAí
+=Að  Að{Að(öAð=qAðQìAðffAðzáAð\Að£×AðžRAðÌÍAðáHAðõÃAñ
+=Añ
+žAñ33AñG®Añ\)Añp€Añ
+AñAñ®AñÂAñ×
+Añë
+Aò  Aò{Aò(öAò=qAòQìAòffAòzáAò\Aò£×AòžRAòÌÍAòáHAòõÃAó
+=Aó
+žAó33AóG®Aó\)Aóp€Aó
+AóAó®AóÂAó×
+Aóë
+Aô  Aô{Aô(öAô=qAôQìAôffAôzáAô\Aô£×AôžRAôÌÍAôáHAôõÃAõ
+=Aø  Aø{Aø(öAø=qAøQìAøffAøzáAø\Aø£×AøžRAøÌÍAøáHAøõÃAù
+=Aù
+žAù33AùG®Aù\)Aùp€Aù
+AùAù®AùÂAù×
+Aùë
+Aú  Aú{Aú(öAú=qAúQìAúffAúzáAú\Aú£×AúžRAúÌÍAúáHAúõÃAû
+=Aû
+žAû33AûG®Aû\)Aûp€Aû
+AûAû®AûÂAû×
+Aûë
+Aü  Aü{Aü(öAü=qAüQìAüffAüzáAü\Aü£×AüžRAüÌÍAüáHAüõÃAý
+=B   B 
+=B {B 
+žB (öB 33B =qB G®B QìB \)B ffB p€B záB 
+B \B B £×B ®B žRB ÂB ÌÍB ×
+B áHB ë
+B õÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB	  B	
+=B	{B	
+žB	(öB	33B	=qB	G®B	QìB	\)B	ffB	p€B	záB	
+B	\B	B	£×B	®B	žRB	ÂB	ÌÍB	×
+B	áHB	ë
+B	õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B\BB£×B®BžRBÂBÌÍB×
+BáHBë
+BõÃB  B
+=B{B
+žB(öB33B=qBG®BQìB\)BffBp€BzáB
+B
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B
+\B
+B
+£×B
+®B
+žRB
+ÂB
+ÌÍB
+×
+B
+áHB
+ë
+B
+õÃB
+  B
+
+=B
+{B
+
+žB
+(öB
+33B
+=qB
+G®B
+QìB
+\)B
+ffB
+p€B
+záB
+
+B   B 
+=B {B 
+žB (öB 33B =qB G®B QìB \)B ffB p€B záB 
+B \B B £×B ®B žRB ÂB ÌÍB ×
+B áHB ë
+B õÃB!  B!
+=B!{B!
+žB!(öB!33B!=qB!G®B!QìB!\)B!ffB!p€B!záB!
+B!\B!B!£×B!®B!žRB!ÂB!ÌÍB!×
+B!áHB!ë
+B!õÃB"  B"
+=B"{B"
+žB"(öB"33B"=qB"G®B"QìB"\)B"ffB"p€B"záB"
+B$  B$
+=B${B$
+žB$(öB$33B$=qB$G®B$QìB$\)B$ffB$p€B$záB$
+B$\B$B$£×B$®B$žRB$ÂB$ÌÍB$×
+B$áHB$ë
+B$õÃB%  B%
+=B%{B%
+žB%(öB%33B%=qB%G®B%QìB%\)B%ffB%p€B%záB%
+B%\B%B%£×B%®B%žRB%ÂB%ÌÍB%×
+B%áHB%ë
+B%õÃB&  B&
+=B&{B&
+žB&(öB&33B&=qB&G®B&QìB&\)B&ffB&p€B&záB&
+B(  B(
+=B({B(
+žB((öB(33B(=qB(G®B(QìB(\)B(ffB(p€B(záB(
+B(\B(B(£×B(®B(žRB(ÂB(ÌÍB(×
+B(áHB(ë
+B(õÃB)  B)
+=B){B)
+žB)(öB)33B)=qB)G®B)QìB)\)B)ffB)p€B)záB)
+B)\B)B)£×B)®B)žRB)ÂB)ÌÍB)×
+B)áHB)ë
+B)õÃB*  B*
+=B*{B*
+žB*(öB*33B*=qB*G®B*QìB*\)B*ffB*p€B*záB*
+B,  B,
+=B,{B,
+žB,(öB,33B,=qB,G®B,QìB,\)B,ffB,p€B,záB,
+B,\B,B,£×B,®B,žRB,ÂB,ÌÍB,×
+B,áHB,ë
+B,õÃB-  B-
+=B-{B-
+žB-(öB-33B-=qB-G®B-QìB-\)B-ffB-p€B-záB-
+B-\B-B-£×B-®B-žRB-ÂB-ÌÍB-×
+B-áHB-ë
+B-õÃB.  B.
+=B.{B.
+žB.(öB.33B.=qB.G®B.QìB.\)B.ffB.p€B.záB.
+B0  B0
+=B0{B0
+žB0(öB033B0=qB0G®B0QìB0\)B0ffB0p€B0záB0
+B0\B0B0£×B0®B0žRB0ÂB0ÌÍB0×
+B0áHB0ë
+B0õÃB1  B1
+=B1{B1
+žB1(öB133B1=qB1G®B1QìB1\)B1ffB1p€B1záB1
+B1\B1B1£×B1®B1žRB1ÂB1ÌÍB1×
+B1áHB1ë
+B1õÃB2  B2
+=B2{B2
+žB2(öB233B2=qB2G®B2QìB2\)B2ffB2p€B2záB2
+B4  B4
+=B4{B4
+žB4(öB433B4=qB4G®B4QìB4\)B4ffB4p€B4záB4
+B4\B4B4£×B4®B4žRB4ÂB4ÌÍB4×
+B4áHB4ë
+B4õÃB5  B5
+=B5{B5
+žB5(öB533B5=qB5G®B5QìB5\)B5ffB5p€B5záB5
+B5\B5B5£×B5®B5žRB5ÂB5ÌÍB5×
+B5áHB5ë
+B5õÃB6  B6
+=B6{B6
+žB6(öB633B6=qB6G®B6QìB6\)B6ffB6p€B6záB6
+B8  B8
+=B8{B8
+žB8(öB833B8=qB8G®B8QìB8\)B8ffB8p€B8záB8
+B8\B8B8£×B8®B8žRB8ÂB8ÌÍB8×
+B8áHB8ë
+B8õÃB9  B9
+=B9{B9
+žB9(öB933B9=qB9G®B9QìB9\)B9ffB9p€B9záB9
+B9\B9B9£×B9®B9žRB9ÂB9ÌÍB9×
+B9áHB9ë
+B9õÃB:  B:
+=B:{B:
+žB:(öB:33B:=qB:G®B:QìB:\)B:ffB:p€B:záB:
+B<  B<
+=B<{B<
+žB<(öB<33B<=qB<G®B<QìB<\)B<ffB<p€B<záB<
+B<\B<B<£×B<®B<žRB<ÂB<ÌÍB<×
+B<áHB<ë
+B<õÃB=  B=
+=B={B=
+žB=(öB=33B==qB=G®B=QìB=\)B=ffB=p€B=záB=
+B=\B=B=£×B=®B=žRB=ÂB=ÌÍB=×
+B=áHB=ë
+B=õÃB>  B>
+=B>{B>
+žB>(öB>33B>=qB>G®B>QìB>\)B>ffB>p€B>záB>
+B@  B@
+=B@{B@
+žB@(öB@33B@=qB@G®B@QìB@\)B@ffB@p€B@záB@
+B@\B@B@£×B@®B@žRB@ÂB@ÌÍB@×
+B@áHB@ë
+B@õÃBA  BA
+=BA{BA
+žBA(öBA33BA=qBAG®BAQìBA\)BAffBAp€BAzáBA
+BA\BABA£×BA®BAžRBAÂBAÌÍBA×
+BAáHBAë
+BAõÃBB  BB
+=BB{BB
+žBB(öBB33BB=qBBG®BBQìBB\)BBffBBp€BBzáBB
+BD  BD
+=BD{BD
+žBD(öBD33BD=qBDG®BDQìBD\)BDffBDp€BDzáBD
+BD\BDBD£×BD®BDžRBDÂBDÌÍBD×
+BDáHBDë
+BDõÃBE  BE
+=BE{BE
+žBE(öBE33BE=qBEG®BEQìBE\)BEffBEp€BEzáBE
+BE\BEBE£×BE®BEžRBEÂBEÌÍBE×
+BEáHBEë
+BEõÃBF  BF
+=BF{BF
+žBF(öBF33BF=qBFG®BFQìBF\)BFffBFp€BFzáBF
+BH  BH
+=BH{BH
+žBH(öBH33BH=qBHG®BHQìBH\)BHffBHp€BHzáBH
+BH\BHBH£×BH®BHžRBHÂBHÌÍBH×
+BHáHBHë
+BHõÃBI  BI
+=BI{BI
+žBI(öBI33BI=qBIG®BIQìBI\)BIffBIp€BIzáBI
+BI\BIBI£×BI®BIžRBIÂBIÌÍBI×
+BIáHBIë
+BIõÃBJ  BJ
+=BJ{BJ
+žBJ(öBJ33BJ=qBJG®BJQìBJ\)BJffBJp€BJzáBJ
+BL  BL
+=BL{BL
+žBL(öBL33BL=qBLG®BLQìBL\)BLffBLp€BLzáBL
+BL\BLBL£×BL®BLžRBLÂBLÌÍBL×
+BLáHBLë
+BLõÃBM  BM
+=BM{BM
+žBM(öBM33BM=qBMG®BMQìBM\)BMffBMp€BMzáBM
+BM\BMBM£×BM®BMžRBMÂBMÌÍBM×
+BMáHBMë
+BMõÃBN  BN
+=BN{BN
+žBN(öBN33BN=qBNG®BNQìBN\)BNffBNp€BNzáBN
+BP  BP
+=BP{BP
+žBP(öBP33BP=qBPG®BPQìBP\)BPffBPp€BPzáBP
+BP\BPBP£×BP®BPžRBPÂBPÌÍBP×
+BPáHBPë
+BPõÃBQ  BQ
+=BQ{BQ
+žBQ(öBQ33BQ=qBQG®BQQìBQ\)BQffBQp€BQzáBQ
+BQ\BQBQ£×BQ®BQžRBQÂBQÌÍBQ×
+BQáHBQë
+BQõÃBR  BR
+=BR{BR
+žBR(öBR33BR=qBRG®BRQìBR\)BRffBRp€BRzáBR
+BT  BT
+=BT{BT
+žBT(öBT33BT=qBTG®BTQìBT\)BTffBTp€BTzáBT
+BT\BTBT£×BT®BTžRBTÂBTÌÍBT×
+BTáHBTë
+BTõÃBU  BU
+=BU{BU
+žBU(öBU33BU=qBUG®BUQìBU\)BUffBUp€BUzáBU
+BU\BUBU£×BU®BUžRBUÂBUÌÍBU×
+BUáHBUë
+BUõÃBV  BV
+=BV{BV
+žBV(öBV33BV=qBVG®BVQìBV\)BVffBVp€BVzáBV
+BX  BX
+=BX{BX
+žBX(öBX33BX=qBXG®BXQìBX\)BXffBXp€BXzáBX
+BX\BXBX£×BX®BXžRBXÂBXÌÍBX×
+BXáHBXë
+BXõÃBY  BY
+=BY{BY
+žBY(öBY33BY=qBYG®BYQìBY\)BYffBYp€BYzáBY
+BY\BYBY£×BY®BYžRBYÂBYÌÍBY×
+BYáHBYë
+BYõÃBZ  BZ
+=BZ{BZ
+žBZ(öBZ33BZ=qBZG®BZQìBZ\)BZffBZp€BZzáBZ
+B\  B\
+=B\{B\
+žB\(öB\33B\=qB\G®B\QìB\\)B\ffB\p€B\záB\
+B\\B\B\£×B\®B\žRB\ÂB\ÌÍB\×
+B\áHB\ë
+B\õÃB]  B]
+=B]{B]
+žB](öB]33B]=qB]G®B]QìB]\)B]ffB]p€B]záB]
+B]\B]B]£×B]®B]žRB]ÂB]ÌÍB]×
+B]áHB]ë
+B]õÃB^  B^
+=B^{B^
+žB^(öB^33B^=qB^G®B^QìB^\)B^ffB^p€B^záB^
+B`  B`
+=B`{B`
+žB`(öB`33B`=qB`G®B`QìB`\)B`ffB`p€B`záB`
+B`\B`B`£×B`®B`žRB`ÂB`ÌÍB`×
+B`áHB`ë
+B`õÃBa  Ba
+=Ba{Ba
+žBa(öBa33Ba=qBaG®BaQìBa\)BaffBap€BazáBa
+Ba\BaBa£×Ba®BažRBaÂBaÌÍBa×
+BaáHBaë
+BaõÃBb  Bb
+=Bb{Bb
+žBb(öBb33Bb=qBbG®BbQìBb\)BbffBbp€BbzáBb
+Bd  Bd
+=Bd{Bd
+žBd(öBd33Bd=qBdG®BdQìBd\)BdffBdp€BdzáBd
+Bd\BdBd£×Bd®BdžRBdÂBdÌÍBd×
+BdáHBdë
+BdõÃBe  Be
+=Be{Be
+žBe(öBe33Be=qBeG®BeQìBe\)BeffBep€BezáBe
+Be\BeBe£×Be®BežRBeÂBeÌÍBe×
+BeáHBeë
+BeõÃBf  Bf
+=Bf{Bf
+žBf(öBf33Bf=qBfG®BfQìBf\)BfffBfp€BfzáBf
+Bh  Bh
+=Bh{Bh
+žBh(öBh33Bh=qBhG®BhQìBh\)BhffBhp€BhzáBh
+Bh\BhBh£×Bh®BhžRBhÂBhÌÍBh×
+BháHBhë
+BhõÃBi  Bi
+=Bi{Bi
+žBi(öBi33Bi=qBiG®BiQìBi\)BiffBip€BizáBi
+Bi\BiBi£×Bi®BižRBiÂBiÌÍBi×
+BiáHBië
+BiõÃBj  Bj
+=Bj{Bj
+žBj(öBj33Bj=qBjG®BjQìBj\)BjffBjp€BjzáBj
+Bl  Bl
+=Bl{Bl
+žBl(öBl33Bl=qBlG®BlQìBl\)BlffBlp€BlzáBl
+Bl\BlBl£×Bl®BlžRBlÂBlÌÍBl×
+BláHBlë
+BlõÃBm  Bm
+=Bm{Bm
+žBm(öBm33Bm=qBmG®BmQìBm\)BmffBmp€BmzáBm
+Bm\BmBm£×Bm®BmžRBmÂBmÌÍBm×
+BmáHBmë
+BmõÃBn  Bn
+=Bn{Bn
+žBn(öBn33Bn=qBnG®BnQìBn\)BnffBnp€BnzáBn
+Bp  Bp
+=Bp{Bp
+žBp(öBp33Bp=qBpG®BpQìBp\)BpffBpp€BpzáBp
+Bp\BpBp£×Bp®BpžRBpÂBpÌÍBp×
+BpáHBpë
+BpõÃBq  Bq
+=Bq{Bq
+žBq(öBq33Bq=qBqG®BqQìBq\)BqffBqp€BqzáBq
+Bq\BqBq£×Bq®BqžRBqÂBqÌÍBq×
+BqáHBqë
+BqõÃBr  Br
+=Br{Br
+žBr(öBr33Br=qBrG®BrQìBr\)BrffBrp€BrzáBr
+Bt  Bt
+=Bt{Bt
+žBt(öBt33Bt=qBtG®BtQìBt\)BtffBtp€BtzáBt
+Bt\BtBt£×Bt®BtžRBtÂBtÌÍBt×
+BtáHBtë
+BtõÃBu  Bu
+=Bu{Bu
+žBu(öBu33Bu=qBuG®BuQìBu\)BuffBup€BuzáBu
+Bu\BuBu£×Bu®BužRBuÂBuÌÍBu×
+BuáHBuë
+BuõÃBv  Bv
+=Bv{Bv
+žBv(öBv33Bv=qBvG®BvQìBv\)BvffBvp€BvzáBv
+Bx  Bx
+=Bx{Bx
+žBx(öBx33Bx=qBxG®BxQìBx\)BxffBxp€BxzáBx
+Bx\BxBx£×Bx®BxžRBxÂBxÌÍBx×
+BxáHBxë
+BxõÃBy  By
+=By{By
+žBy(öBy33By=qByG®ByQìBy\)ByffByp€ByzáBy
+By\ByBy£×By®ByžRByÂByÌÍBy×
+ByáHByë
+ByõÃBz  Bz
+=Bz{Bz
+žBz(öBz33Bz=qBzG®BzQìBz\)BzffBzp€BzzáBz
+B|  B|
+=B|{B|
+žB|(öB|33B|=qB|G®B|QìB|\)B|ffB|p€B|záB|
+B|\B|B|£×B|®B|žRB|ÂB|ÌÍB|×
+B|áHB|ë
+B|õÃB}  B}
+=B}{B}
+žB}(öB}33B}=qB}G®B}QìB}\)B}ffB}p€B}záB}
+B}\B}B}£×B}®B}žRB}ÂB}ÌÍB}×
+B}áHB}ë
+B}õÃB~  B~
+=B~{B~
+žB~(öB~33B~=qB~G®B~QìB~\)B~ffB~p€B~záB~
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?  <#×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @   ?G®?G®<£×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @  @@£×@ £×?\?×
+=#×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @   @Qì@Qì@AG®@ë
+?
+
+ž?
+
+ž=LÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @À  @ÀQì@ Qì@£×@õÃ@B\@\?ff?®=uÂ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A   @àQì@ÀQì@À£×@ õÃ@G®@G®@C33@×
+?®?õÃ=£×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A(öA (ö@à£×@ÀõÃ@ÁG®@¡G®@@ë
+@C×
+@zá?=q?
+=žQì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A0  A (öA(öAQìA zá@áG®@ÁG®@Á@¡ë
+@ë
+@=q@E
+ž@Â?
+?ÌÍ=áG®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A@  A0(öA0(öA QìAzáA£×A £×@á@Áë
+@Áë
+@¢=q@\@áH@EÂ@ff?{?{=õÂ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AP  AP(öA@(öA0QìA0záA £×A£×AÌÍA õÃ@áë
+@Â=q@Â\@¢áH@áH@33@G
+=@
+=?\)?£×>
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Ap  A`(öAP(öAPQìA@záA0£×A0£×A ÌÍAõÃAõÃA
+ž@â\@ÂáH@ÂáH@£33@
+@
+@G®@Qì?£×?ë
+>¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{Ap(öA`QìAPzáAP£×A@£×A0ÌÍA0õÃA õÃA
+žAG®Ap€@âáH@Ã33@Ã
+@£
+@×
+@(ö@HQì@õÃ?33?zá>#×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{A{A(öApzáA`£×AP£×APÌÍA@õÃA0õÃA1
+žA!G®Ap€Ap€A@ã
+@Ã
+@Ã×
+@€(ö@(ö@zá@I@
+=q?zá?Â>8Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A  A{A{A(öA=qAQìAp£×A`ÌÍAPõÃAPõÃAA
+žA1G®A1p€A!p€AAÂAÂ@ã×
+@Ä(ö@Ä(ö@€zá@ÌÍ@
+
+ž@J=q@
+áH?
+>?
+>>B\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aš  A {A{A(öA=qAQìAQìAffApõÃA`õÃAQ
+žAQG®AAp€A1p€A1A!ÂAÂAë
+A{@ä(ö@Äzá@ÄÌÍ@¥
+ž@
+
+ž@
+p€@K
+@
+
+?Qì?>W
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A°  Aš{Aš{A (öA=qAQìAQìAffAzáAzáAq
+žAaG®AQp€AQp€AAA1ÂA1ÂA!ë
+A{A{A=q@äÌÍ@Å
+ž@Å
+ž@¥p€@
+Â@
+Â@L(ö@
+ÌÍ?áH?áH>aG®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Až  Až{A°{Aš(öAš=qA QìAQìAffAzáAzáA\A£×Aqp€Aap€AQAQÂAAÂA1ë
+A2{A"{A=qAffA\@å
+ž@Åp€@ÅÂ@¥Â@{@ff@Mp€@
+p€?(ö?p€>k
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÈ  AÀ{Až{Až(öA°=qAšQìAšQìA ffAzáAzáA\A£×AžRAžRAqAaÂAQÂAQë
+AB{A2{A2=qA"ffA\A\AžR@åÂ@ÅÂ@Æ{@Šff@žR@žR@N{@žR?p€?žR>  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÐ  AÈ{AÈ{AÀ(öAž=qAžQìA°QìAšffAšzáA záA\A£×AžRAžRAÌÍAáHAqÂAaë
+AR{AR{AB=qA2ffA2\A"\AžRAáHAáH@æ{@Æff@ÆžR@ŠžR@
+=@\)@NžR@\)?   ?   >
+
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aà  AØ{AÐ{AÈ(öAÈ=qAÀQìAžQìAžffA°záAšzáAš\A £×AžRAžRAÌÍAáHAáHAõÃAr{Ab{AR=qARffAB\A2\A2žRA"áHAáHA
+=A33@æžR@ÆžR@Ç
+=@§\)@\)@®@P  @  ?¡G®?¢\>\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aè  Aà{Aà{AØ(öAÐ=qAÈQìAÈQìAÀffAžzáAžzáA°\Aš£×AšžRA žRAÌÍAáHAáHAõÃA
+=A
+=Ar=qAbffAR\AR\ABžRA2áHA2áHA#
+=A33A\)A\)@ç
+=@Ç\)@Ç\)@§®@  @  @P£×@G®?£×
+?£×
+>zá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Að  Að{Aè{Aà(öAà=qAØQìAÐQìAÈffAÈzáAÀzáAž\Až£×A°žRAšžRAšÌÍA áHAáHAõÃA
+=A
+=A
+žA33Ar\Ab\ARžRARáHABáHA3
+=A333A#\)A\)A
+A®@ç\)@Ç®@È  @š  @Qì@£×@Që
+@ë
+?¥
+ž?Šff>¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B   Aø{Að{Að(öAè=qAàQìAàQìAØffAÐzáAÈzáAÈ\AÀ£×AžžRAžžRA°ÌÍAšáHAšáHA õÃA
+=A
+=A
+žA33AG®AG®AržRAbáHARáHAS
+=AC33A3\)A3\)A#
+A®A®A×
+@è  @È  @ÈQì@š£×@õÃ@õÃ@R\@33?Šff?§®>£×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+=B 
+=Aø(öAð=qAðQìAèQìAàffAàzáAØzáAÐ\AÈ£×AÈžRAÀžRAžÌÍAžáHA°áHAšõÃA©
+=A¡
+=A
+žA33AG®AG®A\)Ap€AráHAc
+=AS33AS\)AC\)A3
+A3®A#®A×
+A  A  @èQì@È£×@ÈõÃ@šõÃ@G®@@S33@×
+?šõÂ?ª=q>šõÃ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+  B
+=B
+=B{B 
+žAøQìAðQìAðffAèzáAàzáAà\AØ£×AÐžRAÈžRAÈÌÍAÀáHAžáHAžõÃA±
+=A©
+=A©
+žA¡33AG®AG®A\)Ap€Ap€A
+As33Ac\)AS\)AS
+AC®A3®A3×
+A$  A  A(öAQì@èõÃ@ÈõÃ@ÉG®@©@@ë
+@Tzá@
+ž?ª=q?«
+>³33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+
+=B
+
+=B{B
+žB(öB (öAøffAðzáAðzáAè\Aà£×AàžRAØžRAÐÌÍAÈáHAÈáHAÀõÃA¹
+=A¹
+=A±
+žA©33A©G®A¡G®A\)Ap€Ap€A
+AA®As\)Ac
+AS®AS®AC×
+A4  A4  A$(öAQìAzáAzá@éG®@É@É@©ë
+@=q@\@U
+ž@Â?¬ÌÍ?¬ÌÍ>žQì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B  B
+=B
+=B
+{B
+
+žB(öB(öB33B =qAøzáAð\Að£×AèžRAàžRAàÌÍAØáHAÐáHAÈõÃAÉ
+=AÁ
+=A¹
+žA¹33A±G®A©G®A©\)A¡p€Ap€A
+AA®A®AÂAs®Ac®AS×
+AT  AD  A4(öA4QìA$záAzáA£×AÌÍ@é@Éë
+@Ê=q@ª\@\@áH@Vff@ff?®{?¯\)>Â\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+  B
+=B
+=B{B
+žB
+(öB
+(öB33B=qB=qB G®Aø£×AðžRAðžRAèÌÍAàáHAàáHAØõÃAÑ
+=AÉ
+=AÉ
+žAÁ33A¹G®A¹G®A±\)A©p€A©p€A¡
+AA®A®AÂA×
+A×
+As×
+Ad  AT  AT(öADQìA4záA4záA$£×AÌÍAÌÍAõÃ@ê=q@Ê\@Ê\@ªáH@33@33@W
+>@®?°£×?°£×>Ç®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B   B 
+=B
+
+=B{B
+žB(öB(öB
+33B
+=qB=qBG®BQìB \)AøžRAðÌÍAðáHAèáHAàõÃAá
+=AÙ
+=AÑ
+žAÉ33AÉG®AÁG®A¹\)A¹p€A±p€A©
+A©A¡®A®AÂA×
+A×
+Aë
+A  At  Ad(öATQìATzáADzáA4£×A4ÌÍA$ÌÍAõÃA
+žAG®@ê\@ÊáH@Ë33@«33@
+@×
+@XQì@Qì?±ë
+?³33>ÌÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B(  B$
+=B 
+=B {B
+
+žB(öB(öB33B=qB
+=qB
+G®BQìB\)B\)B ffAøáHAðáHAðõÃAé
+=Aá
+=Aá
+žAÙ33AÑG®AÉG®AÉ\)AÁp€A¹p€A¹
+A±A©®A©®A¡ÂA×
+A×
+Aë
+A  A  A{AtQìAdzáATzáAT£×ADÌÍA4ÌÍA4õÃA%
+žAG®AG®Ap€@ë33@Ë33@Ë
+@«×
+@(ö@(ö@XõÂ@?³33?Žzá>×
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B,  B(
+=B(
+=B${B 
+žB (öB
+(öB33B=qB=qBG®B
+QìB
+\)B\)BffBp€B p€AøõÃAñ
+=Añ
+=Aé
+žAá33AáG®AÙG®AÑ\)AÉp€AÉp€AÁ
+A¹A¹®A±®A©ÂA©×
+A¡×
+Aë
+A  A  A{A(öA=qAtzáAd£×ATÌÍATÌÍADõÃA5
+žA5G®A%G®Ap€AA@ë
+@Ë×
+@Ì(ö@¬(ö@zá@ÌÍ@Y@=q?µÂ?µÂ>Ü(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B4  B0
+=B,
+=B({B(
+žB$(öB (öB 33B
+=qB=qBG®BQìB\)B
+\)B
+ffBp€Bp€BzáB 
+Aù
+=Añ
+žAñ33AéG®AáG®Aá\)AÙp€AÑp€AÉ
+AÉAÁ®A¹®A¹ÂA±×
+A©×
+A©ë
+A¢  A  A{A(öA=qA=qAQìAtÌÍAdÌÍATõÃAU
+žAEG®A5G®A5p€A%AAÂAë
+@ì(ö@Ì(ö@Ìzá@¬ÌÍ@ÌÍ@
+ž@ZáH@áH?·
+>?žQì>æff¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B8  B4
+=B4
+=B0{B,
+žB((öB((öB$33B =qB =qB
+G®BQìB\)B\)BffB
+p€B
+p€BzáB
+B
+B \Aù33AñG®AñG®Aé\)Aáp€Aáp€AÙ
+AÑAÉ®AÉ®AÁÂA¹×
+A¹×
+A±ë
+Aª  Aª  A¢{A(öA=qA=qAQìAffAffAtõÃAe
+žAUG®AUG®AEp€A5A5A%ÂAë
+A{A{@ìzá@ÌÌÍ@ÌÌÍ@­
+ž@p€@p€@[
+@
+(ö?¹?¹>ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B<  B<
+=B8
+=B4{B4
+žB0(öB,(öB(33B(=qB$=qB G®B QìB
+\)B\)BffBp€Bp€B
+záB
+
+B
+B\BB £×AùG®Añ\)Añp€Aép€Aá
+AáAÙ®AÑ®AÉÂAÉ×
+AÁ×
+A¹ë
+Aº  A²  Aª{Aª(öA¢=qA=qAQìAffAffAzáA\AuG®AeG®AUp€AUAEA5ÂA5ë
+A&{A{A=qAff@ìÌÍ@Í
+ž@Íp€@­p€@Â@{@\ÌÍ@
+ÌÍ?ºáH?Œ(ö>ð£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BD  B@
+=B<
+=B<{B8
+žB4(öB4(öB033B,=qB(=qB(G®B$QìB \)B \)B
+ffBp€Bp€BzáB
+B
+
+B
+\BB£×B£×B ®Aùp€Añp€Añ
+AéAá®Aá®AÙÂAÑ×
+AÉ×
+AÉë
+AÂ  Aº  Aº{A²(öAª=qAª=qA¢QìAffAffAzáA\A£×A£×Aup€AeAUAUÂAEë
+A6{A6{A&=qAffAffA\@íp€@Íp€@ÍÂ@®{@ff@ff@]p€@
+{?Œ(ö?œp€>úáH¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BH  BH
+=BD
+=B@{B<
+žB<(öB8(öB433B4=qB0=qB,G®B(QìB(\)B$\)B ffB p€B
+p€BzáB
+B
+B\B
+B
+£×B£×B®BžRB žRAù
+AñAñ®Aé®AáÂAá×
+AÙ×
+AÑë
+AÊ  AÊ  AÂ{Aº(öAº=qA²=qAªQìAªffA¢ffAzáA\A£×A£×AžRAÌÍAuAeÂAUë
+AV{AF{A6=qA6ffA&ffA\AžRAžR@íÂ@Î{@Îff@®ff@žR@
+=@^{@
+žR?ŸžR?À  ?   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BP  BL
+=BH
+=BH{BD
+žB@(öB<(öB<33B8=qB4=qB4G®B0QìB,\)B(\)B(ffB$p€B p€B záB
+
+B
+B\BB£×B
+£×B
+®BžRBžRBÂB ÌÍAù®Añ®AñÂAé×
+Aá×
+Aáë
+AÚ  AÒ  AÊ{AÊ(öAÂ=qAº=qAºQìA²ffAªffAªzáA¢\A£×A£×AžRAÌÍAÌÍAáHAuë
+Af{AV{AV=qAFffA6ffA6\A&žRAžRAáHA
+=@îff@Îff@ÎžR@¯
+=@
+=@\)@_\)@   ?À  ?ÁG®?
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BT  BP
+=BP
+=BL{BH
+žBH(öBD(öB@33B<=qB<=qB8G®B4QìB4\)B0\)B,ffB(p€B(p€B$záB 
+B 
+B
+\BB£×B£×B®B
+žRB
+žRBÂBÌÍB×
+B ×
+AùÂAñ×
+Añ×
+Aéë
+Aâ  Aâ  AÚ{AÒ(öAÊ=qAÊ=qAÂQìAºffAºffA²záAª\Aª£×A¢£×AžRAÌÍAÌÍAáHAõÃA
+=Av{Af=qAVffAVffAF\A6žRA6žRA&áHA
+=A33A33@îžR@Ï
+=@Ï
+=@¯\)@®@  @`  @ £×?Â\?Â\?®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BX  BX
+=BT
+=BP{BP
+žBL(öBH(öBH33BD=qB@=qB<G®B<QìB8\)B4\)B4ffB0p€B,p€B(záB(
+B$
+B \B B
+£×B£×B®BžRBžRB
+ÂB
+ÌÍB×
+B×
+BáHB ë
+Aù×
+Añë
+Aò  Aê  Aâ{Aâ(öAÚ=qAÒ=qAÊQìAÊffAÂffAºzáAº\A²£×Aª£×AªžRA¢ÌÍAÌÍAáHAõÃA
+=A
+=A
+žAvffAfffAV\AVžRAFžRA6áHA7
+=A'33A33A\)A
+@ï
+=@Ï\)@Ï®@°  @  @Qì@aG®@!G®?Ã×
+?Å
+ž?
+=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B`  B\
+=BX
+=BX{BT
+žBP(öBP(öBL33BH=qBH=qBDG®B@QìB<\)B<\)B8ffB4p€B4p€B0záB,
+B(
+B(\B$B £×B £×B
+®BžRBžRBÂBÌÍB
+×
+B
+×
+BáHBë
+Bë
+B õÃAú  Aò  Aò{Aê(öAâ=qAâ=qAÚQìAÒffAÊffAÊzáAÂ\Aº£×Aº£×A²žRAªÌÍAªÌÍA¢áHAõÃA
+=A
+=A
+žA33A33Av\AfžRAVžRAVáHAG
+=A733A733A'\)A
+A
+A®@ï®@Ð  @Ð  @°Qì@£×@£×@aë
+@"\?Å
+ž?Æff?\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bd  Bd
+=B`
+=B\{BX
+žBX(öBT(öBP33BP=qBL=qBHG®BHQìBD\)B@\)B<ffB<p€B8p€B4záB4
+B0
+B,\B(B(£×B$£×B ®B žRB
+žRBÂBÌÍB×
+B×
+B
+áHB
+ë
+Bë
+BõÃB  B  Aú{Aò(öAò=qAê=qAâQìAâffAÚffAÒzáAÊ\AÊ£×AÂ£×AºžRAºÌÍA²ÌÍAªáHAªõÃA£
+=A
+=A
+žA33A33AG®A\)AvžRAfáHAW
+=AW33AG33A7\)A7
+A'
+A®A×
+A  @ð  @ÐQì@Ð£×@°£×@õÂ@G®@b\@#33?Ç®?ÈõÂ?ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bl  Bh
+=Bd
+=Bd{B`
+žB\(öBX(öBX33BT=qBP=qBPG®BLQìBH\)BH\)BDffB@p€B<p€B<záB8
+B4
+B4\B0B,£×B(£×B(®B$žRB žRB ÂB
+ÌÍB×
+B×
+BáHBë
+B
+ë
+B
+õÃB	  B  B
+=B{Aú=qAò=qAòQìAêffAâffAâzáAÚ\AÒ£×AÊ£×AÊžRAÂÌÍAºÌÍAºáHA²õÃA«
+=A«
+=A£
+žA33A33AG®A\)A\)Ap€Aw
+=Ag33AW33AW\)AG
+A7
+A7®A'×
+A  A  A(ö@ð£×@Ð£×@ÐõÂ@±G®@G®@@c×
+@$zá?ÈõÂ?Ê=p?
+=¿  ¿  ¿  ¿  ¿  ¿  Bp  Bl
+=Bl
+=Bh{Bd
+žBd(öB`(öB\33BX=qBX=qBTG®BPQìBP\)BL\)BHffBHp€BDp€B@záB<
+B<
+B8\B4B4£×B0£×B,®B(žRB(žRB$ÂB ÌÍB ×
+B
+×
+BáHBë
+Bë
+BõÃB
+  B
+  B	
+=B{B
+žB
+žAúQìAòffAòffAêzáAâ\Aâ£×AÚ£×AÒžRAÊÌÍAÊÌÍAÂáHAºõÃA»
+=A³
+=A«
+žA«33A£33AG®A\)A\)Ap€A
+AAw33Ag\)AW
+AW
+AG®A7×
+A8  A(  A(öAQìAQì@ðõÂ@ÑG®@ÑG®@±@ë
+@=q@dzá@%
+ž?Ë
+
+?Ë
+
+?¿  ¿  ¿  ¿  Bx  Bt
+=Bp
+=Bl{Bl
+žBh(öBd(öBd33B`=qB\=qBXG®BXQìBT\)BP\)BPffBLp€BHp€BHzáBD
+B@
+B<\B<B8£×B4£×B4®B0žRB,žRB(ÂB(ÌÍB$×
+B ×
+B áHB
+ë
+Bë
+BõÃB  B  B
+=B
+{B	
+žB
+žB(öB33AúffAòzáAò\Aê£×Aâ£×AâžRAÚÌÍAÒÌÍAÊáHAÊõÃAÃ
+=A»
+=A»
+žA³33A«33A«G®A£\)A\)Ap€A
+AAA®Aw
+Ag
+AW®AW×
+AH  A8  A8(öA(QìAQìAzáA£×@ñG®@Ñ@Ñë
+@²=q@=q@\@eÂ@%Â?ÌÌÍ?Î{?
+žR¿  ¿  B|  Bx
+=Bx
+=Bt{Bp
+žBl(öBl(öBh33Bd=qBd=qB`G®B\QìBX\)BX\)BTffBPp€BPp€BLzáBH
+BH
+BD\B@B<£×B<£×B8®B4žRB4žRB0ÂB,ÌÍB(×
+B(×
+B$áHB ë
+B ë
+B
+õÃB  B  B
+=B{B
+
+žB
+
+žB	(öB33B33B=qAú\Aò£×Aò£×AêžRAâÌÍAâÌÍAÚáHAÒõÃAË
+=AË
+=AÃ
+žA»33A»33A³G®A«\)A«\)A£p€A
+AAA®AÂAÂAw®Ag×
+AX  AX  AH(öA8QìA8QìA(záA£×A£×AÌÍ@ñë
+@Ò=q@Ò=q@²\@áH@áH@fff@'
+>?Ï\)?Ï\)?!G®¿  ¿  B|
+=Bx{Bx
+žBt(öBp(öBl33Bl=qBh=qBdG®BdQìB`\)B\\)BXffBXp€BTp€BPzáBP
+BL
+BH\BHBD£×B@£×B<®B<žRB8žRB4ÂB4ÌÍB0×
+B,×
+B(áHB(ë
+B$ë
+B õÃB!  B
+  B
+=B{B
+žB
+žB
+(öB
+33B	33B=qBG®BQìAú£×AòžRAòÌÍAêÌÍAâáHAâõÃAÛ
+=AÓ
+=AË
+žAË33AÃ33A»G®A»\)A³\)A«p€A«
+A£AA®AÂAÂA×
+Aë
+Ax  Ah  AX(öAXQìAHQìA8záA8£×A(£×AÌÍAõÃA	
+ž@ò=q@Ò\@ÒáH@²áH@33@
+@g®@'®?Ð£×¿  ¿  ¿  ¿  B|
+žBx(öBx(öBt33Bp=qBl=qBlG®BhQìBd\)Bd\)B`ffB\p€BXp€BXzáBT
+BP
+BP\BLBH£×BH£×BD®B@žRB<žRB<ÂB8ÌÍB4×
+B4×
+B0áHB,ë
+B(ë
+B(õÃB%  B!  B!
+=B
+{B
+žB
+žB(öB33B
+33B
+=qB	G®BQìBQìB\)AúÌÍAòÌÍAòáHAêõÃAã
+=Aã
+=AÛ
+žAÓ33AË33AËG®AÃ\)A»\)A»p€A³
+A«A«A£®AÂAÂA×
+Aë
+A  A  Ax(öAhQìAXQìAXzáAH£×A8£×A8ÌÍA(õÃA
+žA
+žA	G®@òáH@ÒáH@Ó33@³
+@×
+@×
+@hQì¿  ¿  ¿  ¿  ¿  ¿  B|(öBx33Bx=qBt=qBpG®BlQìBl\)Bh\)BdffBdp€B`p€B\záBX
+BX
+BT\BPBP£×BL£×BH®BHžRBDžRB@ÂB<ÌÍB<×
+B8×
+B4áHB4ë
+B0ë
+B,õÃB)  B)  B%
+=B!{B!
+žB
+
+žB(öB33B33B=qB
+G®B
+QìB	QìB\)BffBffAúáHAòõÃAó
+=Aë
+=Aã
+žAã33AÛ33AÓG®AË\)AË\)AÃp€A»
+A»A³A«®A«ÂA£ÂA×
+Aë
+A  A  A{A(öAxQìAhzáAX£×AX£×AHÌÍA8õÃA9
+žA)
+žAG®Ap€A	p€@ó33@Ó
+@Ó×
+@³×
+@(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|=qBx=qBxG®BtQìBp\)Bl\)BlffBhp€Bdp€BdzáB`
+B\
+BX\BXBT£×BP£×BP®BLžRBHžRBHÂBDÌÍB@×
+B<×
+B<áHB8ë
+B4ë
+B4õÃB1  B-  B)
+=B){B%
+žB!
+žB!(öB
+33B33B=qBG®BQìB
+QìB
+\)B	ffBffBp€BzáAû
+=Aó
+=Aó
+žAë33Aã33AãG®AÛ\)AÓ\)AËp€AË
+AÃA»A»®A³ÂA«ÂA«×
+A£ë
+A  A  A{A(öA(öA=qAx£×Ah£×AXÌÍAXõÃAI
+žA9
+žA9G®A)p€Ap€AA	Â@ó×
+@Ó×
+@Ô(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|G®BxQìBx\)Bt\)BpffBlp€Blp€BhzáBd
+Bd
+B`\B\BX£×BX£×BT®BPžRBPžRBLÂBHÌÍBH×
+BD×
+B@áHB<ë
+B<ë
+B8õÃB5  B5  B1
+=B-{B)
+žB)
+žB%(öB!33B!33B
+=qBG®BQìBQìB\)B
+ffB
+ffB	p€BzáB
+B
+Aû
+žAó33Aó33AëG®Aã\)Aã\)AÛp€AÓ
+AËAËAÃ®A»ÂA»ÂA³×
+A«ë
+A¬  A€  A{A(öA(öA=qAQìAQìAxÌÍAhõÃAY
+žAY
+žAIG®A9p€A9p€A)AÂAë
+A	ë
+@ô(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|\)Bx\)BxffBtp€Bpp€BlzáBl
+Bh
+Bd\BdB`£×B\£×BX®BXžRBTžRBPÂBPÌÍBL×
+BH×
+BHáHBDë
+B@ë
+B<õÃB=  B9  B5
+=B5{B1
+žB-
+žB)(öB)33B%33B!=qB!G®B
+QìBQìB\)BffBffB
+p€B
+záB	
+B
+B\BAû33AóG®Aó\)Aë\)Aãp€Aã
+AÛAÓAË®AËÂAÃÂA»×
+A»ë
+AŽ  A¬  A¬{A€(öA(öA=qAQìAQìAffAzáAy
+žAi
+žAYG®AYp€AIp€A9A9ÂA)ë
+Aë
+A{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ffBxp€Bxp€BtzáBp
+Bl
+Bl\BhBd£×Bd£×B`®B\žRBXžRBXÂBTÌÍBP×
+BP×
+BLáHBHë
+BHë
+BDõÃBA  B=  B=
+=B9{B5
+žB5
+žB1(öB-33B)33B)=qB%G®B!QìB!QìB
+\)BffBffBp€BzáB
+
+B
+
+B	\BBB£×Aû\)Aó\)Aóp€Aë
+AãAãAÛ®AÓÂAËÂAË×
+AÃë
+AŒ  AŒ  AŽ{A¬(öA¬(öA€=qAQìAQìAffAzáA\A\AyG®Aip€AYp€AYAIÂA9ë
+A9ë
+A*{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|p€BxzáBx
+Bt
+Bp\BlBl£×Bh£×Bd®BdžRB`žRB\ÂBXÌÍBX×
+BT×
+BPáHBPë
+BLë
+BHõÃBI  BE  BA
+=B={B=
+žB9
+žB5(öB533B133B-=qB)G®B)QìB%QìB!\)B!ffB
+ffBp€BzáB
+B
+B
+\B
+B	B£×B®B®Aûp€Aó
+AóAëAã®AãÂAÛÂAÓ×
+AËë
+AÌ  AÄ  AŒ{AŒ(öAŽ(öA¬=qA¬QìA€QìAffAzáA\A\A£×AžRAyp€AiAYÂAYë
+AIë
+A:{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|
+Bx
+Bx\BtBp£×Bl£×Bl®BhžRBdžRBdÂB`ÌÍB\×
+BX×
+BXáHBTë
+BPë
+BPõÃBM  BI  BI
+=BE{BA
+žB=
+žB=(öB933B533B5=qB1G®B-QìB)QìB)\)B%ffB!ffB!p€B
+záB
+B
+B\BB
+B
+£×B	®B®BžRBÂAûAóAó®AëÂAãÂAã×
+AÛë
+AÔ  AÌ  AÌ{AÄ(öAŒ(öAŒ=qAŽQìA¬QìA¬ffA€záA\A\A£×AžRAžRAÌÍAyÂAië
+AYë
+AZ{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|\BxBx£×Bt£×Bp®BlžRBlžRBhÂBdÌÍBd×
+B`×
+B\áHBXë
+BXë
+BTõÃBQ  BQ  BM
+=BI{BI
+žBE
+žBA(öB=33B=33B9=qB5G®B5QìB1QìB-\)B)ffB)ffB%p€B!záB!
+B
+
+B\BBB£×B
+®B
+®B	žRBÂBÌÍBÌÍAû®AóÂAóÂAë×
+Aãë
+Aä  AÜ  AÔ{AÌ(öAÌ(öAÄ=qAŒQìAŒQìAŽffA¬záA¬\A€\A£×AžRAžRAÌÍAáHAõÃAyë
+Aj{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|£×Bx£×Bx®BtžRBpžRBlÂBlÌÍBh×
+Bd×
+BdáHB`ë
+B\ë
+BXõÃBY  BU  BQ
+=BQ{BM
+žBI
+žBI(öBE33BA33B==qB=G®B9QìB5QìB5\)B1ffB-ffB)p€B)záB%
+B!
+B!\B
+BB£×B®B®B
+žRB
+ÂB	ÌÍBÌÍB×
+BáHAûÂAó×
+Aóë
+Aì  Aä  Aä{AÜ(öAÔ(öAÌ=qAÌQìAÄQìAŒffAŒzáAŽ\A¬\A¬£×A€žRAžRAÌÍAáHAõÃAõÃA
+
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|®BxžRBxžRBtÂBpÌÍBl×
+Bl×
+BháHBdë
+Bdë
+B`õÃB]  BY  BY
+=BU{BQ
+žBQ
+žBM(öBI33BI33BE=qBAG®B=QìB=QìB9\)B5ffB5ffB1p€B-záB)
+B)
+B%\B!B!B
+£×B®B®BžRBÂB
+ÌÍB
+ÌÍB	×
+BáHBáHBë
+Aûë
+Aô  Aô  Aì{Aä(öAä(öAÜ=qAÔQìAÌQìAÌffAÄzáAŒ\AŒ\AŽ£×A¬žRA¬žRA€ÌÍAáHAõÃAõÃA
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|žRBxÂBxÌÍBt×
+Bp×
+BláHBlë
+Bhë
+BdõÃBe  Ba  B]
+=BY{BY
+žBU
+žBQ(öBQ33BM33BI=qBIG®BEQìBAQìB=\)B=ffB9ffB5p€B5záB1
+B-
+B)\B)B%B!£×B!®B
+®BžRBÂBÌÍBÌÍB
+×
+B
+áHB	áHBë
+BõÃB  Aü  Aô{Aô(öAì(öAä=qAäQìAÜQìAÔffAÌzáAÌ\AÄ\AŒ£×AŒžRAŽžRA¬ÌÍA¬áHA€õÃAõÃA
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÌÍBx×
+Bx×
+BtáHBpë
+Blë
+BlõÃBi  Be  Be
+=Ba{B]
+žBY
+žBY(öBU33BQ33BQ=qBMG®BIQìBIQìBE\)BAffB=ffB=p€B9záB5
+B5
+B1\B-B)B)£×B%®B!®B!žRB
+ÂBÌÍBÌÍB×
+BáHB
+áHB
+ë
+B	õÃB  B  B
+=Aü(öAô(öAô=qAìQìAäQìAäffAÜzáAÔ\AÌ\AÌ£×AÄžRAŒžRAŒÌÍAŽáHA¬õÃA¬õÃA¥
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|×
+BxáHBxë
+Btë
+BpõÃBm  Bm  Bi
+=Be{Be
+žBa
+žB](öBY33BY33BU=qBQG®BQQìBMQìBI\)BIffBEffBAp€B=záB=
+B9
+B5\B5B1B-£×B)®B)®B%žRB!ÂB!ÌÍB
+ÌÍB×
+BáHBáHBë
+B
+õÃB  B
+  B
+=B{B{Aü=qAôQìAôQìAìffAäzáAä\AÜ\AÔ£×AÌžRAÌžRAÄÌÍAŒáHAŒõÃAŽõÃA­
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ë
+Bxë
+BxõÃBu  Bq  Bm
+=Bm{Bi
+žBe
+žBe(öBa33B]33BY=qBYG®BUQìBQQìBQ\)BMffBIffBIp€BEzáBA
+B=
+B=\B9B5B5£×B1®B-®B)žRB)ÂB%ÌÍB!ÌÍB!×
+B
+áHBáHBë
+BõÃB  B  B
+=B
+{B{B
+žB(öAüQìAôffAôzáAì\Aä\Aä£×AÜžRAÔžRAÌÌÍAÌáHAÄõÃAŒõÃAœ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|õÃBy  By  Bu
+=Bq{Bm
+žBm
+žBi(öBe33Be33Ba=qB]G®BYQìBYQìBU\)BQffBQffBMp€BIzáBI
+BE
+BA\B=B=B9£×B5®B5®B1žRB-ÂB)ÌÍB)ÌÍB%×
+B!áHB!áHB
+ë
+BõÃB  B  B
+=B{B{B
+
+žB(öB(öB33AüzáAô\Aô\Aì£×AäžRAäžRAÜÌÍAÔáHAÌõÃAÌõÃAÅ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}  By
+=By{Bu
+žBq
+žBm(öBm33Bi33Be=qBeG®BaQìB]QìBY\)BYffBUffBQp€BQzáBM
+BI
+BI\BEBAB=£×B=®B9®B5žRB5ÂB1ÌÍB-ÌÍB)×
+B)áHB%áHB!ë
+B!õÃB
+  B  B
+=B{B{B
+žB(öB
+(öB33B=qBG®Aü\Aô£×AôžRAìžRAäÌÍAäáHAÜõÃAÔõÃAÍ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}{By
+žBy
+žBu(öBq33Bm33Bm=qBiG®BeQìBeQìBa\)B]ffBYffBYp€BUzáBQ
+BQ
+BM\BIBIBE£×BA®B=®B=žRB9ÂB5ÌÍB5ÌÍB1×
+B-áHB)áHB)ë
+B%õÃB"  B"  B
+
+=B{B{B
+žB(öB(öB33B
+=qBG®BG®BQìAüžRAôžRAôÌÍAìáHAäõÃAäõÃAÝ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+žBy(öBy33Bu33Bq=qBmG®BmQìBiQìBe\)BeffBaffB]p€BYzáBY
+BU
+BQ\BQBMBI£×BI®BE®BAžRB=ÂB=ÌÍB9ÌÍB5×
+B5áHB1áHB-ë
+B)õÃB*  B&  B"
+=B"{B
+{B
+žB(öB(öB33B=qBG®B
+G®BQìB\)B\)AüÌÍAôáHAôõÃAìõÃAå
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}33By33By=qBuG®BqQìBmQìBm\)BiffBeffBep€BazáB]
+BY
+BY\BUBQBQ£×BM®BI®BIžRBEÂBAÌÍB=ÌÍB=×
+B9áHB5áHB5ë
+B1õÃB.  B*  B*
+=B&{B"{B"
+žB
+(öB(öB33B=qBG®BG®BQìB
+\)B\)BffBp€AüõÃAôõÃAõ
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}=qByG®ByQìBuQìBq\)BmffBmffBip€BezáBe
+Ba
+B]\BYBYBU£×BQ®BQ®BMžRBIÂBIÌÍBEÌÍBA×
+B=áHB=áHB9ë
+B5õÃB6  B2  B.
+=B*{B*{B&
+žB"(öB"(öB
+33B=qBG®BG®BQìB\)B\)B
+ffBp€BzáBzáAý
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}QìByQìBy\)BuffBqffBmp€BmzáBi
+Be
+Be\BaB]BY£×BY®BU®BQžRBQÂBMÌÍBIÌÍBI×
+BEáHBAáHB=ë
+B=õÃB:  B6  B6
+=B2{B.{B*
+žB*(öB&(öB"33B"=qB
+G®BG®BQìB\)B\)BffBp€B
+záBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}\)ByffByffBup€BqzáBm
+Bm
+Bi\BeBeBa£×B]®BY®BYžRBUÂBQÌÍBQÌÍBM×
+BIáHBIáHBEë
+BAõÃB>  B>  B:
+=B6{B6{B2
+žB.(öB*(öB*33B&=qB"G®B"G®B
+QìB\)B\)BffBp€BzáBzáB
+
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ffByp€ByzáBu
+Bq
+Bm\BmBiBe£×Be®Ba®B]žRBYÂBYÌÍBUÌÍBQ×
+BQáHBMáHBIë
+BIõÃBF  BB  B>
+=B>{B:{B6
+žB6(öB2(öB.33B*=qB*G®B&G®B"QìB"\)B
+\)BffBp€BzáBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}záBy
+By
+Bu\BqBmBm£×Bi®Be®BežRBaÂB]ÌÍBYÌÍBY×
+BUáHBQáHBQë
+BMõÃBJ  BJ  BF
+=BB{B>{B>
+žB:(öB6(öB633B2=qB.G®B*G®B*QìB&\)B"\)B"ffB
+p€BzáBzáB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+By\ByBuBq£×Bm®Bm®BižRBeÂBeÌÍBaÌÍB]×
+BYáHBYáHBUë
+BQõÃBR  BN  BJ
+=BJ{BF{BB
+žB>(öB>(öB:33B6=qB6G®B2G®B.QìB*\)B*\)B&ffB"p€B"záB
+záB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ByBy£×Bu®Bq®BmžRBmÂBiÌÍBeÌÍBe×
+BaáHB]áHBYë
+BYõÃBV  BR  BR
+=BN{BJ{BJ
+žBF(öBB(öB>33B>=qB:G®B6G®B6QìB2\)B.\)B*ffB*p€B&záB"záB"
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}£×By®By®BužRBqÂBmÌÍBmÌÍBi×
+BeáHBeáHBaë
+B]õÃBZ  BZ  BV
+=BR{BR{BN
+žBJ(öBJ(öBF33BB=qB>G®B>G®B:QìB6\)B6\)B2ffB.p€B*záB*záB&
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}®ByžRByÂBuÌÍBqÌÍBm×
+BmáHBiáHBeë
+BeõÃBb  B^  BZ
+=BZ{BV{BR
+žBR(öBN(öBJ33BJ=qBFG®BBG®B>QìB>\)B:\)B6ffB6p€B2záB.záB*
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÂByÌÍByÌÍBu×
+BqáHBmáHBmë
+BiõÃBf  Bf  Bb
+=B^{BZ{BZ
+žBV(öBR(öBR33BN=qBJG®BJG®BFQìBB\)B>\)B>ffB:p€B6záB6záB2
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÌÍBy×
+ByáHBuáHBqë
+BmõÃBn  Bj  Bf
+=Bf{Bb{B^
+žBZ(öBZ(öBV33BR=qBRG®BNG®BJQìBJ\)BF\)BBffB>p€B>záB:záB6
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}áHByáHByë
+BuõÃBr  Bn  Bn
+=Bj{Bf{Bf
+žBb(öB^(öBZ33BZ=qBVG®BRG®BRQìBN\)BJ\)BJffBFp€BBzáB>záB>
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ë
+ByõÃBz  Bv  Br
+=Bn{Bn{Bj
+žBf(öBf(öBb33B^=qBZG®BZG®BVQìBR\)BR\)BNffBJp€BJzáBFzáBB
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~  Bz  Bz
+=Bv{Br{Bn
+žBn(öBj(öBf33Bf=qBbG®B^G®BZQìBZ\)BV\)BRffBRp€BNzáBJzáBJ
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~
+=Bz{Bz{Bv
+žBr(öBn(öBn33Bj=qBfG®BfG®BbQìB^\)BZ\)BZffBVp€BRzáBRzáBN
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~{Bz
+žBz(öBv(öBr33Bn=qBnG®BjG®BfQìBf\)Bb\)B^ffBZp€BZzáBVzáBR
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~(öBz(öBz33Bv=qBrG®BnG®BnQìBj\)Bf\)BfffBbp€B^záBZzáBZ
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~33Bz=qBzG®BvG®BrQìBn\)Bn\)BjffBfp€BfzáBbzáB^
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~G®BzG®BzQìBv\)Br\)BnffBnp€BjzáBfzáBf
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~QìBz\)Bz\)BvffBrp€BnzáBnzáBj
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~\)BzffBzp€BvzáBrzáBn
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~p€BzzáBzzáBv
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~záBz
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B|  Bx  Bt  Bp  Bl  Bh  Bd  B`  B\  BX  BT  BP  BL  BH  BD  B@  B<  B8  B4  B0  B,  B(  B$  B   B
+  B  B  B  B
+  B  B  B   Aø  Að  Aè  Aà  AØ  AÐ  AÈ  AÀ  Až  A°  Aš  A   A  A  A  A  Ap  A`  AP  A@  A0  A   A  A   @à  @À  @   @  @@  @   ?      B|
+=Bx
+=Bt
+=Bp
+=Bl
+=Bh
+=Bd
+=B`
+=B\
+=BX
+=BT
+=BP
+=BL
+=BH
+=BD
+=B@
+=B<
+=B8
+=B4
+=B0
+=B,
+=B(
+=B$
+=B 
+=B
+
+=B
+=B
+=B
+=B
+
+=B
+=B
+=B 
+=Aø{Að{Aè{Aà{AØ{AÐ{AÈ{AÀ{Až{A°{Aš{A {A{A{A{A{Ap(öA`(öAP(öA@(öA0(öA (öA(öA (ö@àQì@ÀQì@ Qì@Qì@@£×@ £×?G®<#×
+B|{Bx{Bt{Bp{Bl{Bh{Bd{B`{B\{BX{BT{BP{BL{BH{BD{B@{B<{B8{B4{B0{B,{B({B${B {B
+{B{B{B{B
+{B{B{B {Aø(öAð(öAè(öAà(öAØ(öAÐ(öAÈ(öAÀ(öAž(öA°(öAš(öA (öA(öA(öA(öA(öApQìA`QìAPQìA@QìA0QìA QìAQìA Qì@à£×@À£×@ £×@£×@AG®@G®?\<£×
+B|
+žBx
+žBt
+žBp
+žBl
+žBh
+žBd
+žB`
+žB\
+žBX
+žBT
+žBP
+žBL
+žBH
+žBD
+žB@
+žB<
+žB8
+žB4
+žB0
+žB,
+žB(
+žB$
+žB 
+žB
+
+žB
+žB
+žB
+žB
+
+žB
+žB
+žB 
+žAø=qAð=qAè=qAà=qAØ=qAÐ=qAÈ=qAÀ=qAž=qA°=qAš=qA =qA=qA=qA=qA=qApzáA`záAPzáA@záA0záA záAzáA zá@àõÃ@ÀõÃ@ õÃ@õÃ@Aë
+@ë
+?×
+<õÂB|(öBx(öBt(öBp(öBl(öBh(öBd(öB`(öB\(öBX(öBT(öBP(öBL(öBH(öBD(öB@(öB<(öB8(öB4(öB0(öB,(öB((öB$(öB (öB
+(öB(öB(öB(öB
+(öB(öB(öB (öAøQìAðQìAèQìAàQìAØQìAÐQìAÈQìAÀQìAžQìA°QìAšQìA QìAQìAQìAQìAQìAp£×A`£×AP£×A@£×A0£×A £×A£×A £×@áG®@ÁG®@¡G®@G®@B\@\?
+
+ž=#×
+B|33Bx33Bt33Bp33Bl33Bh33Bd33B`33B\33BX33BT33BP33BL33BH33BD33B@33B<33B833B433B033B,33B(33B$33B 33B
+33B33B33B33B
+33B33B33B 33AøffAðffAèffAàffAØffAÐffAÈffAÀffAžffA°ffAšffA ffAffAffAffAffApÌÍA`ÌÍAPÌÍA@ÌÍA0ÌÍA ÌÍAÌÍA ÌÍ@á@Á@¡@@C33@33?ff=LÌÍB|=qBx=qBt=qBp=qBl=qBh=qBd=qB`=qB\=qBX=qBT=qBP=qBL=qBH=qBD=qB@=qB<=qB8=qB4=qB0=qB,=qB(=qB$=qB =qB
+=qB=qB=qB=qB
+=qB=qB=qB =qAøzáAðzáAèzáAàzáAØzáAÐzáAÈzáAÀzáAžzáA°záAšzáA záAzáAzáAzáAzáApõÃA`õÃAPõÃA@õÃA0õÃA õÃAõÃA õÃ@áë
+@Áë
+@¡ë
+@ë
+@C×
+@×
+?®=uÂB|G®BxG®BtG®BpG®BlG®BhG®BdG®B`G®B\G®BXG®BTG®BPG®BLG®BHG®BDG®B@G®B<G®B8G®B4G®B0G®B,G®B(G®B$G®B G®B
+G®BG®BG®BG®B
+G®BG®BG®B G®Aø\Að\Aè\Aà\AØ\AÐ\AÈ\AÀ\Až\A°\Aš\A \A\A\A\A\Aq
+žAa
+žAQ
+žAA
+žA1
+žA!
+žA
+žA
+ž@â=q@Â=q@¢=q@=q@Dzá@zá?õÃ=\)B|QìBxQìBtQìBpQìBlQìBhQìBdQìB`QìB\QìBXQìBTQìBPQìBLQìBHQìBDQìB@QìB<QìB8QìB4QìB0QìB,QìB(QìB$QìB QìB
+QìBQìBQìBQìB
+QìBQìBQìB QìAø£×Að£×Aè£×Aà£×AØ£×AÐ£×AÈ£×AÀ£×Až£×A°£×Aš£×A £×A£×A£×A£×A£×AqG®AaG®AQG®AAG®A1G®A!G®AG®AG®@â\@Â\@¢\@\@E
+ž@
+ž?=q=£×
+B|\)Bx\)Bt\)Bp\)Bl\)Bh\)Bd\)B`\)B\\)BX\)BT\)BP\)BL\)BH\)BD\)B@\)B<\)B8\)B4\)B0\)B,\)B(\)B$\)B \)B
+\)B\)B\)B\)B
+\)B\)B\)B \)AøžRAðžRAèžRAàžRAØžRAÐžRAÈžRAÀžRAžžRA°žRAšžRA žRAžRAžRAžRAžRAqp€Aap€AQp€AAp€A1p€A!p€Ap€Ap€@âáH@ÂáH@¢áH@áH@EÂ@Â?
+=žQìB|ffBxffBtffBpffBlffBhffBdffB`ffB\ffBXffBTffBPffBLffBHffBDffB@ffB<ffB8ffB4ffB0ffB,ffB(ffB$ffB ffB
+ffBffBffBffB
+ffBffBffB ffAøÌÍAðÌÍAèÌÍAàÌÍAØÌÍAÐÌÍAÈÌÍAÀÌÍAžÌÍA°ÌÍAšÌÍA ÌÍAÌÍAÌÍAÌÍAÌÍAqAaAQAAA1A!AA@ã33@Ã33@£33@33@Fff@ff?ÌÍ=ÌÌÍB|p€Bxp€Btp€Bpp€Blp€Bhp€Bdp€B`p€B\p€BXp€BTp€BPp€BLp€BHp€BDp€B@p€B<p€B8p€B4p€B0p€B,p€B(p€B$p€B p€B
+p€Bp€Bp€Bp€B
+p€Bp€Bp€B p€AøáHAðáHAèáHAàáHAØáHAÐáHAÈáHAÀáHAžáHA°áHAšáHA áHAáHAáHAáHAáHAqÂAaÂAQÂAAÂA1ÂA!ÂAÂAÂ@ã
+@Ã
+@£
+@
+@G
+=@
+=?{=áG®B|záBxzáBtzáBpzáBlzáBhzáBdzáB`záB\záBXzáBTzáBPzáBLzáBHzáBDzáB@záB<záB8záB4záB0záB,záB(záB$záB záB
+záBzáBzáBzáB
+záBzáBzáB záAøõÃAðõÃAèõÃAàõÃAØõÃAÐõÃAÈõÃAÀõÃAžõÃA°õÃAšõÃA õÃAõÃAõÃAõÃAõÃAqë
+Aaë
+AQë
+AAë
+A1ë
+A!ë
+Aë
+Aë
+@ã×
+@Ã×
+@£×
+@×
+@G®@®?\)=õÂB|
+Bx
+Bt
+Bp
+Bl
+Bh
+Bd
+B`
+B\
+BX
+BT
+BP
+BL
+BH
+BD
+B@
+B<
+B8
+B4
+B0
+B,
+B(
+B$
+B 
+B
+
+B
+B
+B
+B
+
+B
+B
+B 
+Aù
+=Añ
+=Aé
+=Aá
+=AÙ
+=AÑ
+=AÉ
+=AÁ
+=A¹
+=A±
+=A©
+=A¡
+=A
+=A
+=A
+=A
+=Ar{Ab{AR{AB{A2{A"{A{A{@ä(ö@Ä(ö@€(ö@(ö@HQì@Qì?£×>
+žB|\Bx\Bt\Bp\Bl\Bh\Bd\B`\B\\BX\BT\BP\BL\BH\BD\B@\B<\B8\B4\B0\B,\B(\B$\B \B
+\B\B\B\B
+\B\B\B \Aù
+žAñ
+žAé
+žAá
+žAÙ
+žAÑ
+žAÉ
+žAÁ
+žA¹
+žA±
+žA©
+žA¡
+žA
+žA
+žA
+žA
+žAr=qAb=qAR=qAB=qA2=qA"=qA=qA=q@äzá@Äzá@€zá@zá@HõÃ@õÃ?ë
+>\)B|BxBtBpBlBhBdB`B\BXBTBPBLBHBDB@B<B8B4B0B,B(B$B B
+BBBB
+BBB Aù33Añ33Aé33Aá33AÙ33AÑ33AÉ33AÁ33A¹33A±33A©33A¡33A33A33A33A33ArffAbffARffABffA2ffA"ffAffAff@äÌÍ@ÄÌÍ@€ÌÍ@ÌÍ@I@	?33>B|£×Bx£×Bt£×Bp£×Bl£×Bh£×Bd£×B`£×B\£×BX£×BT£×BP£×BL£×BH£×BD£×B@£×B<£×B8£×B4£×B0£×B,£×B(£×B$£×B £×B
+£×B£×B£×B£×B
+£×B£×B£×B £×AùG®AñG®AéG®AáG®AÙG®AÑG®AÉG®AÁG®A¹G®A±G®A©G®A¡G®AG®AG®AG®AG®Ar\Ab\AR\AB\A2\A"\A\A\@å
+ž@Å
+ž@¥
+ž@
+
+ž@J=q@
+=q?zá>#×
+B|®Bx®Bt®Bp®Bl®Bh®Bd®B`®B\®BX®BT®BP®BL®BH®BD®B@®B<®B8®B4®B0®B,®B(®B$®B ®B
+®B®B®B®B
+®B®B®B ®Aù\)Añ\)Aé\)Aá\)AÙ\)AÑ\)AÉ\)AÁ\)A¹\)A±\)A©\)A¡\)A\)A\)A\)A\)AržRAbžRARžRABžRA2žRA"žRAžRAžR@åp€@Åp€@¥p€@
+p€@JáH@
+áH?Â>.{B|žRBxžRBtžRBpžRBlžRBhžRBdžRB`žRB\žRBXžRBTžRBPžRBLžRBHžRBDžRB@žRB<žRB8žRB4žRB0žRB,žRB(žRB$žRB žRB
+žRBžRBžRBžRB
+žRBžRBžRB žRAùp€Añp€Aép€Aáp€AÙp€AÑp€AÉp€AÁp€A¹p€A±p€A©p€A¡p€Ap€Ap€Ap€Ap€AráHAbáHARáHABáHA2áHA"áHAáHAáH@åÂ@ÅÂ@¥Â@
+Â@K
+@
+
+?
+>>8QìB|ÂBxÂBtÂBpÂBlÂBhÂBdÂB`ÂB\ÂBXÂBTÂBPÂBLÂBHÂBDÂB@ÂB<ÂB8ÂB4ÂB0ÂB,ÂB(ÂB$ÂB ÂB
+ÂBÂBÂBÂB
+ÂBÂBÂB ÂAù
+Añ
+Aé
+Aá
+AÙ
+AÑ
+AÉ
+AÁ
+A¹
+A±
+A©
+A¡
+A
+A
+A
+A
+As
+=Ac
+=AS
+=AC
+=A3
+=A#
+=A
+=A
+=@æ{@Æ{@Š{@{@L(ö@
+(ö?Qì>B\B|ÌÍBxÌÍBtÌÍBpÌÍBlÌÍBhÌÍBdÌÍB`ÌÍB\ÌÍBXÌÍBTÌÍBPÌÍBLÌÍBHÌÍBDÌÍB@ÌÍB<ÌÍB8ÌÍB4ÌÍB0ÌÍB,ÌÍB(ÌÍB$ÌÍB ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍBÌÍBÌÍB ÌÍAùAñAéAáAÙAÑAÉAÁA¹A±A©A¡AAAAAs33Ac33AS33AC33A333A#33A33A33@æff@Æff@Šff@ff@LÌÍ@
+ÌÍ?>LÌÍB|×
+Bx×
+Bt×
+Bp×
+Bl×
+Bh×
+Bd×
+B`×
+B\×
+BX×
+BT×
+BP×
+BL×
+BH×
+BD×
+B@×
+B<×
+B8×
+B4×
+B0×
+B,×
+B(×
+B$×
+B ×
+B
+×
+B×
+B×
+B×
+B
+×
+B×
+B×
+B ×
+Aù®Añ®Aé®Aá®AÙ®AÑ®AÉ®AÁ®A¹®A±®A©®A¡®A®A®A®A®As\)Ac\)AS\)AC\)A3\)A#\)A\)A\)@æžR@ÆžR@ŠžR@žR@Mp€@
+p€?áH>W
+=B|áHBxáHBtáHBpáHBláHBháHBdáHB`áHB\áHBXáHBTáHBPáHBLáHBHáHBDáHB@áHB<áHB8áHB4áHB0áHB,áHB(áHB$áHB áHB
+áHBáHBáHBáHB
+áHBáHBáHB áHAùÂAñÂAéÂAáÂAÙÂAÑÂAÉÂAÁÂA¹ÂA±ÂA©ÂA¡ÂAÂAÂAÂAÂAs
+Ac
+AS
+AC
+A3
+A#
+A
+A
+@ç
+=@Ç
+=@§
+=@
+=@N{@{?(ö>aG®B|ë
+Bxë
+Btë
+Bpë
+Blë
+Bhë
+Bdë
+B`ë
+B\ë
+BXë
+BTë
+BPë
+BLë
+BHë
+BDë
+B@ë
+B<ë
+B8ë
+B4ë
+B0ë
+B,ë
+B(ë
+B$ë
+B ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+Bë
+Bë
+B ë
+Aù×
+Añ×
+Aé×
+Aá×
+AÙ×
+AÑ×
+AÉ×
+AÁ×
+A¹×
+A±×
+A©×
+A¡×
+A×
+A×
+A×
+A×
+As®Ac®AS®AC®A3®A#®A®A®@ç\)@Ç\)@§\)@\)@NžR@žR?p€>k
+B|õÃBxõÃBtõÃBpõÃBlõÃBhõÃBdõÃB`õÃB\õÃBXõÃBTõÃBPõÃBLõÃBHõÃBDõÃB@õÃB<õÃB8õÃB4õÃB0õÃB,õÃB(õÃB$õÃB õÃB
+õÃBõÃBõÃBõÃB
+õÃBõÃBõÃB õÃAùë
+Añë
+Aéë
+Aáë
+AÙë
+AÑë
+AÉë
+AÁë
+A¹ë
+A±ë
+A©ë
+A¡ë
+Aë
+Aë
+Aë
+Aë
+As×
+Ac×
+AS×
+AC×
+A3×
+A#×
+A×
+A×
+@ç®@Ç®@§®@®@O\)@\)?žR>uÂB}  By  Bu  Bq  Bm  Bi  Be  Ba  B]  BY  BU  BQ  BM  BI  BE  BA  B=  B9  B5  B1  B-  B)  B%  B!  B
+  B  B  B  B
+  B	  B  B  Aú  Aò  Aê  Aâ  AÚ  AÒ  AÊ  AÂ  Aº  A²  Aª  A¢  A  A  A  A  At  Ad  AT  AD  A4  A$  A  A  @è  @È  @š  @  @P  @  ?   >  B}
+=By
+=Bu
+=Bq
+=Bm
+=Bi
+=Be
+=Ba
+=B]
+=BY
+=BU
+=BQ
+=BM
+=BI
+=BE
+=BA
+=B=
+=B9
+=B5
+=B1
+=B-
+=B)
+=B%
+=B!
+=B
+
+=B
+=B
+=B
+=B
+=B	
+=B
+=B
+=Aú{Aò{Aê{Aâ{AÚ{AÒ{AÊ{AÂ{Aº{A²{Aª{A¢{A{A{A{A{At(öAd(öAT(öAD(öA4(öA$(öA(öA(ö@èQì@ÈQì@šQì@Qì@P£×@£×?¡G®>
+
+žB}{By{Bu{Bq{Bm{Bi{Be{Ba{B]{BY{BU{BQ{BM{BI{BE{BA{B={B9{B5{B1{B-{B){B%{B!{B
+{B{B{B{B
+{B	{B{B{Aú(öAò(öAê(öAâ(öAÚ(öAÒ(öAÊ(öAÂ(öAº(öA²(öAª(öA¢(öA(öA(öA(öA(öAtQìAdQìATQìADQìA4QìA$QìAQìAQì@è£×@È£×@š£×@£×@QG®@G®?¢\>=qB}
+žBy
+žBu
+žBq
+žBm
+žBi
+žBe
+žBa
+žB]
+žBY
+žBU
+žBQ
+žBM
+žBI
+žBE
+žBA
+žB=
+žB9
+žB5
+žB1
+žB-
+žB)
+žB%
+žB!
+žB
+
+žB
+žB
+žB
+žB
+
+žB	
+žB
+žB
+žAú=qAò=qAê=qAâ=qAÚ=qAÒ=qAÊ=qAÂ=qAº=qA²=qAª=qA¢=qA=qA=qA=qA=qAtzáAdzáATzáADzáA4záA$záAzáAzá@èõÃ@ÈõÃ@šõÃ@õÃ@Që
+@ë
+?£×
+>\)B}(öBy(öBu(öBq(öBm(öBi(öBe(öBa(öB](öBY(öBU(öBQ(öBM(öBI(öBE(öBA(öB=(öB9(öB5(öB1(öB-(öB)(öB%(öB!(öB
+(öB(öB(öB(öB
+(öB	(öB(öB(öAúQìAòQìAêQìAâQìAÚQìAÒQìAÊQìAÂQìAºQìA²QìAªQìA¢QìAQìAQìAQìAQìAt£×Ad£×AT£×AD£×A4£×A$£×A£×A£×@éG®@ÉG®@©G®@G®@R\@\?¥
+ž>záB}33By33Bu33Bq33Bm33Bi33Be33Ba33B]33BY33BU33BQ33BM33BI33BE33BA33B=33B933B533B133B-33B)33B%33B!33B
+33B33B33B33B
+33B	33B33B33AúffAòffAêffAâffAÚffAÒffAÊffAÂffAºffA²ffAªffA¢ffAffAffAffAffAtÌÍAdÌÍATÌÍADÌÍA4ÌÍA$ÌÍAÌÍAÌÍ@é@É@©@@S33@33?Šff>B}=qBy=qBu=qBq=qBm=qBi=qBe=qBa=qB]=qBY=qBU=qBQ=qBM=qBI=qBE=qBA=qB==qB9=qB5=qB1=qB-=qB)=qB%=qB!=qB
+=qB=qB=qB=qB
+=qB	=qB=qB=qAúzáAòzáAêzáAâzáAÚzáAÒzáAÊzáAÂzáAºzáA²záAªzáA¢záAzáAzáAzáAzáAtõÃAdõÃATõÃADõÃA4õÃA$õÃAõÃAõÃ@éë
+@Éë
+@©ë
+@ë
+@S×
+@×
+?§®>žRB}G®ByG®BuG®BqG®BmG®BiG®BeG®BaG®B]G®BYG®BUG®BQG®BMG®BIG®BEG®BAG®B=G®B9G®B5G®B1G®B-G®B)G®B%G®B!G®B
+G®BG®BG®BG®B
+G®B	G®BG®BG®Aú\Aò\Aê\Aâ\AÚ\AÒ\AÊ\AÂ\Aº\A²\Aª\A¢\A\A\A\A\Au
+žAe
+žAU
+žAE
+žA5
+žA%
+žA
+žA
+ž@ê=q@Ê=q@ª=q@=q@Tzá@zá?šõÂ>£×
+B}QìByQìBuQìBqQìBmQìBiQìBeQìBaQìB]QìBYQìBUQìBQQìBMQìBIQìBEQìBAQìB=QìB9QìB5QìB1QìB-QìB)QìB%QìB!QìB
+QìBQìBQìBQìB
+QìB	QìBQìBQìAú£×Aò£×Aê£×Aâ£×AÚ£×AÒ£×AÊ£×AÂ£×Aº£×A²£×Aª£×A¢£×A£×A£×A£×A£×AuG®AeG®AUG®AEG®A5G®A%G®AG®AG®@ê\@Ê\@ª\@\@U
+ž@
+ž?ª=q>šõÃB}\)By\)Bu\)Bq\)Bm\)Bi\)Be\)Ba\)B]\)BY\)BU\)BQ\)BM\)BI\)BE\)BA\)B=\)B9\)B5\)B1\)B-\)B)\)B%\)B!\)B
+\)B\)B\)B\)B
+\)B	\)B\)B\)AúžRAòžRAêžRAâžRAÚžRAÒžRAÊžRAÂžRAºžRA²žRAªžRA¢žRAžRAžRAžRAžRAup€Aep€AUp€AEp€A5p€A%p€Ap€Ap€@êáH@ÊáH@ªáH@áH@UÂ@Â?«
+>®{B}ffByffBuffBqffBmffBiffBeffBaffB]ffBYffBUffBQffBMffBIffBEffBAffB=ffB9ffB5ffB1ffB-ffB)ffB%ffB!ffB
+ffBffBffBffB
+ffB	ffBffBffAúÌÍAòÌÍAêÌÍAâÌÍAÚÌÍAÒÌÍAÊÌÍAÂÌÍAºÌÍA²ÌÍAªÌÍA¢ÌÍAÌÍAÌÍAÌÍAÌÍAuAeAUAEA5A%AA@ë33@Ë33@«33@33@Vff@ff?¬ÌÍ>³33B}p€Byp€Bup€Bqp€Bmp€Bip€Bep€Bap€B]p€BYp€BUp€BQp€BMp€BIp€BEp€BAp€B=p€B9p€B5p€B1p€B-p€B)p€B%p€B!p€B
+p€Bp€Bp€Bp€B
+p€B	p€Bp€Bp€AúáHAòáHAêáHAâáHAÚáHAÒáHAÊáHAÂáHAºáHA²áHAªáHA¢áHAáHAáHAáHAáHAuÂAeÂAUÂAEÂA5ÂA%ÂAÂAÂ@ë
+@Ë
+@«
+@
+@W
+>@
+>?®{>žQìB}záByzáBuzáBqzáBmzáBizáBezáBazáB]záBYzáBUzáBQzáBMzáBIzáBEzáBAzáB=záB9záB5záB1záB-záB)záB%záB!záB
+záBzáBzáBzáB
+záB	záBzáBzáAúõÃAòõÃAêõÃAâõÃAÚõÃAÒõÃAÊõÃAÂõÃAºõÃA²õÃAªõÃA¢õÃAõÃAõÃAõÃAõÃAuë
+Aeë
+AUë
+AEë
+A5ë
+A%ë
+Aë
+Aë
+@ë×
+@Ë×
+@«×
+@×
+@W®@®?¯\)>œp€B}
+By
+Bu
+Bq
+Bm
+Bi
+Be
+Ba
+B]
+BY
+BU
+BQ
+BM
+BI
+BE
+BA
+B=
+B9
+B5
+B1
+B-
+B)
+B%
+B!
+B
+
+B
+B
+B
+B
+
+B	
+B
+B
+Aû
+=Aó
+=Aë
+=Aã
+=AÛ
+=AÓ
+=AË
+=AÃ
+=A»
+=A³
+=A«
+=A£
+=A
+=A
+=A
+=A
+=Av{Af{AV{AF{A6{A&{A{A{@ì(ö@Ì(ö@¬(ö@(ö@XQì@Qì?°£×>Â\B}\By\Bu\Bq\Bm\Bi\Be\Ba\B]\BY\BU\BQ\BM\BI\BE\BA\B=\B9\B5\B1\B-\B)\B%\B!\B
+\B\B\B\B
+\B	\B\B\Aû
+žAó
+žAë
+žAã
+žAÛ
+žAÓ
+žAË
+žAÃ
+žA»
+žA³
+žA«
+žA£
+žA
+žA
+žA
+žA
+žAv=qAf=qAV=qAF=qA6=qA&=qA=qA=q@ìzá@Ìzá@¬zá@zá@XõÂ@õÂ?±ë
+>Ç®B}ByBuBqBmBiBeBaB]BYBUBQBMBIBEBAB=B9B5B1B-B)B%B!B
+BBBB
+B	BBAû33Aó33Aë33Aã33AÛ33AÓ33AË33AÃ33A»33A³33A«33A£33A33A33A33A33AvffAfffAVffAFffA6ffA&ffAffAff@ìÌÍ@ÌÌÍ@¬ÌÍ@ÌÍ@Y@?³33>ÌÌÍB}£×By£×Bu£×Bq£×Bm£×Bi£×Be£×Ba£×B]£×BY£×BU£×BQ£×BM£×BI£×BE£×BA£×B=£×B9£×B5£×B1£×B-£×B)£×B%£×B!£×B
+£×B£×B£×B£×B
+£×B	£×B£×B£×AûG®AóG®AëG®AãG®AÛG®AÓG®AËG®AÃG®A»G®A³G®A«G®A£G®AG®AG®AG®AG®Av\Af\AV\AF\A6\A&\A\A\@í
+ž@Í
+ž@­
+ž@
+ž@Z=q@=q?Žzá>Ñë
+B}®By®Bu®Bq®Bm®Bi®Be®Ba®B]®BY®BU®BQ®BM®BI®BE®BA®B=®B9®B5®B1®B-®B)®B%®B!®B
+®B®B®B®B
+®B	®B®B®Aû\)Aó\)Aë\)Aã\)AÛ\)AÓ\)AË\)AÃ\)A»\)A³\)A«\)A£\)A\)A\)A\)A\)AvžRAfžRAVžRAFžRA6žRA&žRAžRAžR@íp€@Íp€@­p€@p€@ZáH@áH?µÂ>×
+=B}žRByžRBužRBqžRBmžRBižRBežRBažRB]žRBYžRBUžRBQžRBMžRBIžRBEžRBAžRB=žRB9žRB5žRB1žRB-žRB)žRB%žRB!žRB
+žRBžRBžRBžRB
+žRB	žRBžRBžRAûp€Aóp€Aëp€Aãp€AÛp€AÓp€AËp€AÃp€A»p€A³p€A«p€A£p€Ap€Ap€Ap€Ap€AváHAfáHAVáHAFáHA6áHA&áHAáHAáH@íÂ@ÍÂ@­Â@Â@[
+@
+?·
+>>Ü(öB}ÂByÂBuÂBqÂBmÂBiÂBeÂBaÂB]ÂBYÂBUÂBQÂBMÂBIÂBEÂBAÂB=ÂB9ÂB5ÂB1ÂB-ÂB)ÂB%ÂB!ÂB
+ÂBÂBÂBÂB
+ÂB	ÂBÂBÂAû
+Aó
+Aë
+Aã
+AÛ
+AÓ
+AË
+AÃ
+A»
+A³
+A«
+A£
+A
+A
+A
+A
+Aw
+=Ag
+=AW
+=AG
+=A7
+=A'
+=A
+=A
+=@î{@Î{@®{@{@\(ö@
+(ö?žQì>áG®B}ÌÍByÌÍBuÌÍBqÌÍBmÌÍBiÌÍBeÌÍBaÌÍB]ÌÍBYÌÍBUÌÍBQÌÍBMÌÍBIÌÍBEÌÍBAÌÍB=ÌÍB9ÌÍB5ÌÍB1ÌÍB-ÌÍB)ÌÍB%ÌÍB!ÌÍB
+ÌÍBÌÍBÌÍBÌÍB
+ÌÍB	ÌÍBÌÍBÌÍAûAóAëAãAÛAÓAËAÃA»A³A«A£AAAAAw33Ag33AW33AG33A733A'33A33A33@îff@Îff@®ff@ff@\ÌÍ@
+ÌÍ?¹>æffB}×
+By×
+Bu×
+Bq×
+Bm×
+Bi×
+Be×
+Ba×
+B]×
+BY×
+BU×
+BQ×
+BM×
+BI×
+BE×
+BA×
+B=×
+B9×
+B5×
+B1×
+B-×
+B)×
+B%×
+B!×
+B
+×
+B×
+B×
+B×
+B
+×
+B	×
+B×
+B×
+Aû®Aó®Aë®Aã®AÛ®AÓ®AË®AÃ®A»®A³®A«®A£®A®A®A®A®Aw\)Ag\)AW\)AG\)A7\)A'\)A\)A\)@îžR@ÎžR@®žR@žR@]p€@
+p€?ºáH>ë
+B}áHByáHBuáHBqáHBmáHBiáHBeáHBaáHB]áHBYáHBUáHBQáHBMáHBIáHBEáHBAáHB=áHB9áHB5áHB1áHB-áHB)áHB%áHB!áHB
+áHBáHBáHBáHB
+áHB	áHBáHBáHAûÂAóÂAëÂAãÂAÛÂAÓÂAËÂAÃÂA»ÂA³ÂA«ÂA£ÂAÂAÂAÂAÂAw
+Ag
+AW
+AG
+A7
+A'
+A
+A
+@ï
+=@Ï
+=@¯
+=@
+=@^{@
+{?Œ(ö>ð£×B}ë
+Byë
+Buë
+Bqë
+Bmë
+Bië
+Beë
+Baë
+B]ë
+BYë
+BUë
+BQë
+BMë
+BIë
+BEë
+BAë
+B=ë
+B9ë
+B5ë
+B1ë
+B-ë
+B)ë
+B%ë
+B!ë
+B
+ë
+Bë
+Bë
+Bë
+B
+ë
+B	ë
+Bë
+Bë
+Aû×
+Aó×
+Aë×
+Aã×
+AÛ×
+AÓ×
+AË×
+AÃ×
+A»×
+A³×
+A«×
+A£×
+A×
+A×
+A×
+A×
+Aw®Ag®AW®AG®A7®A'®A®A®@ï\)@Ï\)@¯\)@\)@^žR@
+žR?œp€>õÂB}õÃByõÃBuõÃBqõÃBmõÃBiõÃBeõÃBaõÃB]õÃBYõÃBUõÃBQõÃBMõÃBIõÃBEõÃBAõÃB=õÃB9õÃB5õÃB1õÃB-õÃB)õÃB%õÃB!õÃB
+õÃBõÃBõÃBõÃB
+õÃB	õÃBõÃBõÃAûë
+Aóë
+Aëë
+Aãë
+AÛë
+AÓë
+AËë
+AÃë
+A»ë
+A³ë
+A«ë
+A£ë
+Aë
+Aë
+Aë
+Aë
+Aw×
+Ag×
+AW×
+AG×
+A7×
+A'×
+A×
+A×
+@ï®@Ï®@¯®@®@_\)@\)?ŸžR>úáHB~  Bz  Bv  Br  Bn  Bj  Bf  Bb  B^  BZ  BV  BR  BN  BJ  BF  BB  B>  B:  B6  B2  B.  B*  B&  B"  B
+  B  B  B  B  B
+  B  B  Aü  Aô  Aì  Aä  AÜ  AÔ  AÌ  AÄ  AŒ  AŽ  A¬  A€  A  A  A  A  Ax  Ah  AX  AH  A8  A(  A  A  @ð  @Ð  @°  @  @`  @   ?À  ?   B~
+=Bz
+=Bv
+=Br
+=Bn
+=Bj
+=Bf
+=Bb
+=B^
+=BZ
+=BV
+=BR
+=BN
+=BJ
+=BF
+=BB
+=B>
+=B:
+=B6
+=B2
+=B.
+=B*
+=B&
+=B"
+=B
+
+=B
+=B
+=B
+=B
+=B
+
+=B
+=B
+=Aü{Aô{Aì{Aä{AÜ{AÔ{AÌ{AÄ{AŒ{AŽ{A¬{A€{A{A{A{A{Ax(öAh(öAX(öAH(öA8(öA((öA(öA(ö@ðQì@ÐQì@°Qì@Qì@`£×@ £×?ÁG®?\B~{Bz{Bv{Br{Bn{Bj{Bf{Bb{B^{BZ{BV{BR{BN{BJ{BF{BB{B>{B:{B6{B2{B.{B*{B&{B"{B
+{B{B{B{B{B
+{B{B{Aü(öAô(öAì(öAä(öAÜ(öAÔ(öAÌ(öAÄ(öAŒ(öAŽ(öA¬(öA€(öA(öA(öA(öA(öAxQìAhQìAXQìAHQìA8QìA(QìAQìAQì@ð£×@Ð£×@°£×@£×@aG®@!G®?Â\?
+žB~
+žBz
+žBv
+žBr
+žBn
+žBj
+žBf
+žBb
+žB^
+žBZ
+žBV
+žBR
+žBN
+žBJ
+žBF
+žBB
+žB>
+žB:
+žB6
+žB2
+žB.
+žB*
+žB&
+žB"
+žB
+
+žB
+žB
+žB
+žB
+žB
+
+žB
+žB
+žAü=qAô=qAì=qAä=qAÜ=qAÔ=qAÌ=qAÄ=qAŒ=qAŽ=qA¬=qA€=qA=qA=qA=qA=qAxzáAhzáAXzáAHzáA8záA(záAzáAzá@ðõÂ@ÐõÂ@°õÂ@õÂ@aë
+@!ë
+?Ã×
+?®B~(öBz(öBv(öBr(öBn(öBj(öBf(öBb(öB^(öBZ(öBV(öBR(öBN(öBJ(öBF(öBB(öB>(öB:(öB6(öB2(öB.(öB*(öB&(öB"(öB
+(öB(öB(öB(öB(öB
+(öB(öB(öAüQìAôQìAìQìAäQìAÜQìAÔQìAÌQìAÄQìAŒQìAŽQìA¬QìA€QìAQìAQìAQìAQìAx£×Ah£×AX£×AH£×A8£×A(£×A£×A£×@ñG®@ÑG®@±G®@G®@b\@"\?Å
+ž?
+=qB~33Bz33Bv33Br33Bn33Bj33Bf33Bb33B^33BZ33BV33BR33BN33BJ33BF33BB33B>33B:33B633B233B.33B*33B&33B"33B
+33B33B33B33B33B
+33B33B33AüffAôffAìffAäffAÜffAÔffAÌffAÄffAŒffAŽffA¬ffA€ffAffAffAffAffAxÌÍAhÌÍAXÌÍAHÌÍA8ÌÍA(ÌÍAÌÍAÌÍ@ñ@Ñ@±@@c33@#33?Æff?
+ÌÍB~=qBz=qBv=qBr=qBn=qBj=qBf=qBb=qB^=qBZ=qBV=qBR=qBN=qBJ=qBF=qBB=qB>=qB:=qB6=qB2=qB.=qB*=qB&=qB"=qB
+=qB=qB=qB=qB=qB
+=qB=qB=qAüzáAôzáAìzáAäzáAÜzáAÔzáAÌzáAÄzáAŒzáAŽzáA¬záA€záAzáAzáAzáAzáAxõÃAhõÃAXõÃAHõÃA8õÃA(õÃAõÃAõÃ@ñë
+@Ñë
+@±ë
+@ë
+@c×
+@#×
+?Ç®?\)B~G®BzG®BvG®BrG®BnG®BjG®BfG®BbG®B^G®BZG®BVG®BRG®BNG®BJG®BFG®BBG®B>G®B:G®B6G®B2G®B.G®B*G®B&G®B"G®B
+G®BG®BG®BG®BG®B
+G®BG®BG®Aü\Aô\Aì\Aä\AÜ\AÔ\AÌ\AÄ\AŒ\AŽ\A¬\A€\A\A\A\A\Ay
+žAi
+žAY
+žAI
+žA9
+žA)
+žA
+žA	
+ž@ò=q@Ò=q@²=q@=q@dzá@$zá?ÈõÂ?ë
+B~QìBzQìBvQìBrQìBnQìBjQìBfQìBbQìB^QìBZQìBVQìBRQìBNQìBJQìBFQìBBQìB>QìB:QìB6QìB2QìB.QìB*QìB&QìB"QìB
+QìBQìBQìBQìBQìB
+QìBQìBQìAü£×Aô£×Aì£×Aä£×AÜ£×AÔ£×AÌ£×AÄ£×AŒ£×AŽ£×A¬£×A€£×A£×A£×A£×A£×AyG®AiG®AYG®AIG®A9G®A)G®AG®A	G®@ò\@Ò\@²\@\@e
+ž@%
+ž?Ê=p?záB~\)Bz\)Bv\)Br\)Bn\)Bj\)Bf\)Bb\)B^\)BZ\)BV\)BR\)BN\)BJ\)BF\)BB\)B>\)B:\)B6\)B2\)B.\)B*\)B&\)B"\)B
+\)B\)B\)B\)B\)B
+\)B\)B\)AüžRAôžRAìžRAäžRAÜžRAÔžRAÌžRAÄžRAŒžRAŽžRA¬žRA€žRAžRAžRAžRAžRAyp€Aip€AYp€AIp€A9p€A)p€Ap€A	p€@òáH@ÒáH@²áH@áH@eÂ@%Â?Ë
+
+?
+=B~ffBzffBvffBrffBnffBjffBfffBbffB^ffBZffBVffBRffBNffBJffBFffBBffB>ffB:ffB6ffB2ffB.ffB*ffB&ffB"ffB
+ffBffBffBffBffB
+ffBffBffAüÌÍAôÌÍAìÌÍAäÌÍAÜÌÍAÔÌÍAÌÌÍAÄÌÍAŒÌÍAŽÌÍA¬ÌÍA€ÌÍAÌÍAÌÍAÌÍAÌÍAyAiAYAIA9A)AA	@ó33@Ó33@³33@33@fff@&ff?ÌÌÍ?B~p€Bzp€Bvp€Brp€Bnp€Bjp€Bfp€Bbp€B^p€BZp€BVp€BRp€BNp€BJp€BFp€BBp€B>p€B:p€B6p€B2p€B.p€B*p€B&p€B"p€B
+p€Bp€Bp€Bp€Bp€B
+p€Bp€Bp€AüáHAôáHAìáHAäáHAÜáHAÔáHAÌáHAÄáHAŒáHAŽáHA¬áHA€áHAáHAáHAáHAáHAyÂAiÂAYÂAIÂA9ÂA)ÂAÂA	Â@ó
+@Ó
+@³
+@
+@g
+>@'
+>?Î{?
+(öB~záBzzáBvzáBrzáBnzáBjzáBfzáBbzáB^záBZzáBVzáBRzáBNzáBJzáBFzáBBzáB>záB:záB6záB2záB.záB*záB&záB"záB
+záBzáBzáBzáBzáB
+záBzáBzáAüõÃAôõÃAìõÃAäõÃAÜõÃAÔõÃAÌõÃAÄõÃAŒõÃAŽõÃA¬õÃA€õÃAõÃAõÃAõÃAõÃAyë
+Aië
+AYë
+AIë
+A9ë
+A)ë
+Aë
+A	ë
+@ó×
+@Ó×
+@³×
+@×
+@g®@'®?Ï\)?
+žRB~
+Bz
+Bv
+Br
+Bn
+Bj
+Bf
+Bb
+B^
+BZ
+BV
+BR
+BN
+BJ
+BF
+BB
+B>
+B:
+B6
+B2
+B.
+B*
+B&
+B"
+B
+
+B
+B
+B
+B
+B
+
+B
+B
+Aý
+=Aõ
+=Aí
+=Aå
+=AÝ
+=AÕ
+=AÍ
+=AÅ
+=Aœ
+=Aµ
+=A­
+=A¥
+=A
+=A
+=A
+=A
+
+=Az{Aj{AZ{AJ{A:{A*{A{A
+{@ô(ö@Ô(ö@Ž(ö@(ö@hQì@(Qì?Ð£×?!G®                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|
+=Bx  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|{Bx
+=Bx
+=Bt  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|(öBx
+žBx{Bt
+=Bp
+=Bl  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|33Bx(öBx(öBt
+žBp{Bl
+=Bl
+=Bh  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|=qBx=qBx33Bt(öBp(öBl
+žBl{Bh
+=Bd
+=Bd  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|QìBxG®Bx=qBt=qBp33Bl(öBl(öBh
+žBd{Bd
+=B`
+=B\  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|\)Bx\)BxQìBtG®Bp=qBl=qBl33Bh(öBd(öBd
+žB`{B\
+=BX
+=BX  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|p€BxffBx\)Bt\)BpQìBlG®Bl=qBh=qBd33Bd(öB`(öB\
+žBX{BX
+=BT
+=BP  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|záBxp€Bxp€BtffBp\)Bl\)BlQìBhG®Bd=qBd=qB`33B\(öBX(öBX
+žBT{BP
+=BP
+=BL  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|
+Bx
+BxzáBtp€Bpp€BlffBl\)Bh\)BdQìBdG®B`=qB\=qBX33BX(öBT(öBP
+žBP{BL
+=BH
+=BH  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|Bx\Bx
+Bt
+BpzáBlp€Blp€BhffBd\)Bd\)B`QìB\G®BX=qBX=qBT33BP(öBP(öBL
+žBH{BH
+=BD
+=B@  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|£×Bx£×BxBt\Bp
+Bl
+BlzáBhp€Bdp€BdffB`\)B\\)BXQìBXG®BT=qBP=qBP33BL(öBH(öBH
+žBD{B@
+=B<
+=B<  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|žRBx®Bx£×Bt£×BpBl\Bl
+Bh
+BdzáBdp€B`p€B\ffBX\)BX\)BTQìBPG®BP=qBL=qBH33BH(öBD(öB@
+žB<{B<
+=B8
+=B4  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ÂBxžRBxžRBt®Bp£×Bl£×BlBh\Bd
+Bd
+B`záB\p€BXp€BXffBT\)BP\)BPQìBLG®BH=qBH=qBD33B@(öB<(öB<
+žB8{B4
+=B4
+=B0  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|×
+BxÌÍBxÂBtžRBpžRBl®Bl£×Bh£×BdBd\B`
+B\
+BXzáBXp€BTp€BPffBP\)BL\)BHQìBHG®BD=qB@=qB<33B<(öB8(öB4
+žB4{B0
+=B,
+=B(  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|áHBx×
+Bx×
+BtÌÍBpÂBlžRBlžRBh®Bd£×Bd£×B`B\\BX
+BX
+BTzáBPp€BPp€BLffBH\)BH\)BDQìB@G®B<=qB<=qB833B4(öB4(öB0
+žB,{B(
+=B(
+=B$  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B|ë
+Bxë
+BxáHBt×
+Bp×
+BlÌÍBlÂBhžRBdžRBd®B`£×B\£×BXBX\BT
+BP
+BPzáBLp€BHp€BHffBD\)B@\)B<QìB<G®B8=qB4=qB433B0(öB,(öB(
+žB({B$
+=B 
+=B   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}  BxõÃBxë
+Btë
+BpáHBl×
+Bl×
+BhÌÍBdÂBdžRB`žRB\®BX£×BX£×BTBP\BP
+BL
+BHzáBHp€BDp€B@ffB<\)B<\)B8QìB4G®B4=qB0=qB,33B((öB((öB$
+žB {B 
+=B
+
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+=By  By  BtõÃBpë
+Blë
+BláHBh×
+Bd×
+BdÌÍB`ÂB\žRBXžRBX®BT£×BP£×BPBL\BH
+BH
+BDzáB@p€B<p€B<ffB8\)B4\)B4QìB0G®B,=qB(=qB(33B$(öB (öB 
+žB
+{B
+=B
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+žBy{By
+=Bu  Bq  BlõÃBlë
+Bhë
+BdáHBd×
+B`×
+B\ÌÍBXÂBXžRBTžRBP®BP£×BL£×BHBH\BD
+B@
+B<záB<p€B8p€B4ffB4\)B0\)B,QìB(G®B(=qB$=qB 33B (öB
+(öB
+žB{B
+=B
+=B
+  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}(öBy
+žBy
+žBu{Bq
+=Bm  Bm  BhõÃBdë
+Bdë
+B`áHB\×
+BX×
+BXÌÍBTÂBPžRBPžRBL®BH£×BH£×BDB@\B<
+B<
+B8záB4p€B4p€B0ffB,\)B(\)B(QìB$G®B =qB =qB
+33B(öB(öB
+žB{B
+
+=B
+
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}33By33By(öBu
+žBq
+žBm{Bm
+=Bi  Be  BdõÃB`ë
+B\ë
+BXáHBX×
+BT×
+BPÌÍBPÂBLžRBHžRBH®BD£×B@£×B<B<\B8
+B4
+B4záB0p€B,p€B(ffB(\)B$\)B QìB G®B
+=qB=qB33B(öB(öB
+
+žB
+{B
+=B
+=B  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}G®By=qBy33Bu33Bq(öBm
+žBm
+žBi{Be
+=Be  Ba  B\õÃBXë
+BXë
+BTáHBP×
+BP×
+BLÌÍBHÂBHžRBDžRB@®B<£×B<£×B8B4\B4
+B0
+B,záB(p€B(p€B$ffB \)B \)B
+QìBG®B=qB=qB33B
+(öB
+(öB
+žB{B
+=B 
+=Aø  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}QìByQìByG®Bu=qBq33Bm33Bm(öBi
+žBe
+žBe{Ba
+=B]  BY  BXõÃBTë
+BPë
+BPáHBL×
+BH×
+BHÌÍBDÂB@žRB<žRB<®B8£×B4£×B4B0\B,
+B(
+B(záB$p€B p€B ffB
+\)B\)BQìBG®B=qB
+=qB
+33B(öB(öB
+žB {Aø{Að{Að  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ffBy\)ByQìBuQìBqG®Bm=qBm33Bi33Be(öBe
+žBa
+žB]{BY
+=BY  BU  BPõÃBPë
+BLë
+BHáHBH×
+BD×
+B@ÌÍB<ÂB<žRB8žRB4®B4£×B0£×B,B(\B(
+B$
+B záB p€B
+p€BffB\)B\)BQìB
+G®B
+=qB=qB33B(öB (öAø=qAð(öAð{Aè{Aà  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}p€ByffByffBu\)BqQìBmQìBmG®Bi=qBe33Be33Ba(öB]
+žBY
+žBY{BU
+=BQ  BQ  BLõÃBHë
+BHë
+BDáHB@×
+B<×
+B<ÌÍB8ÂB4žRB4žRB0®B,£×B(£×B(B$\B 
+B 
+B
+záBp€Bp€BffB\)B
+\)B
+QìBG®B=qB=qB 33AøQìAðQìAð=qAè(öAà{Aà{AØ  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}
+ByzáByp€BuffBqffBm\)BmQìBiQìBeG®Be=qBa33B]33BY(öBY
+žBU
+žBQ{BQ
+=BM  BI  BHõÃBDë
+B@ë
+B<áHB<×
+B8×
+B4ÌÍB4ÂB0žRB,žRB(®B(£×B$£×B B \B
+
+B
+BzáBp€Bp€B
+ffB
+\)B\)BQìBG®B =qAøzáAðffAðQìAèQìAà=qAà(öAØ{AÐ{AÈ  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}\By
+By
+BuzáBqp€BmffBmffBi\)BeQìBeQìBaG®B]=qBY33BY33BU(öBQ
+žBQ
+žBM{BI
+=BI  BE  B@õÃB<ë
+B<ë
+B8áHB4×
+B4×
+B0ÌÍB,ÂB(žRB(žRB$®B £×B £×B
+B\B
+B
+BzáB
+p€B
+p€BffB\)B\)B QìAø\AðzáAðzáAèffAàQìAàQìAØ=qAÐ(öAÈ{AÈ{AÀ  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ByBy\Bu
+Bq
+BmzáBmp€BiffBeffBe\)BaQìB]QìBYG®BY=qBU33BQ33BQ(öBM
+žBI
+žBI{BE
+=BA  B=  B<õÃB8ë
+B4ë
+B4áHB0×
+B,×
+B(ÌÍB(ÂB$žRB žRB ®B
+£×B£×BB\B
+B
+
+B
+záBp€Bp€BffB \)AøžRAð£×Að\AèzáAàzáAàffAØQìAÐQìAÈ=qAÈ(öAÀ{Až{Až  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}®By£×ByBuBq\Bm
+Bm
+BizáBep€BeffBaffB]\)BYQìBYQìBUG®BQ=qBQ33BM33BI(öBI
+žBE
+žBA{B=
+=B=  B9  B4õÃB4ë
+B0ë
+B,áHB(×
+B(×
+B$ÌÍB ÂB žRB
+žRB®B£×B£×BB
+\B
+
+B
+BzáBp€B p€AøÌÍAðžRAðžRAè£×Aà\AàzáAØzáAÐffAÈQìAÈQìAÀ=qAž(öAž{A°{Aš  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}žRBy®By®Bu£×BqBmBm\Bi
+Be
+BezáBap€B]ffBYffBY\)BUQìBQQìBQG®BM=qBI33BI33BE(öBA
+žB=
+žB={B9
+=B5  B5  B0õÃB,ë
+B(ë
+B(áHB$×
+B ×
+B ÌÍB
+ÂBžRBžRB®B£×B
+£×B
+B\B
+B
+B záAøáHAðáHAðÌÍAèžRAàžRAà£×AØ\AÐzáAÈzáAÈffAÀQìAžQìAž=qA°(öAš{Aš{A   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}ÌÍByÂByžRBu®Bq®Bm£×BmBiBe\Be
+Ba
+B]záBYp€BYffBUffBQ\)BQQìBMQìBIG®BI=qBE33BA33B=(öB=
+žB9
+žB5{B5
+=B1  B-  B(õÃB(ë
+B$ë
+B áHB ×
+B
+×
+BÌÍBÂBžRBžRB
+®B
+£×B£×BB\B 
+Aù
+=AðõÃAðáHAèáHAàÌÍAàžRAØžRAÐ£×AÈ\AÈzáAÀzáAžffAžQìA°QìAš=qAš(öA {A{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}×
+ByÌÍByÌÍBuÂBqžRBm®Bm®Bi£×BeBeBa\B]
+BY
+BYzáBUp€BQffBQffBM\)BIQìBIQìBEG®BA=qB=33B=33B9(öB5
+žB5
+žB1{B-
+=B)  B)  B$õÃB ë
+B ë
+B
+áHB×
+B×
+BÌÍBÂB
+žRB
+žRB®B£×B£×B Aù
+žAñ
+=Añ
+=AèõÃAàáHAàáHAØÌÍAÐžRAÈžRAÈ£×AÀ\AžzáAžzáA°ffAšQìAšQìA =qA(öA{A{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}áHByáHBy×
+BuÌÍBqÌÍBmÂBmžRBi®Be®Be£×BaB]BY\BY
+BU
+BQzáBQp€BMffBIffBI\)BEQìBAQìB=G®B==qB933B533B5(öB1
+žB-
+žB){B)
+=B%  B!  B õÃB
+ë
+Bë
+BáHB×
+B×
+B
+ÌÍB
+ÂBžRBžRB®B £×AùG®Añ33Añ
+žAé
+=Aá
+=AàõÃAØáHAÐáHAÈÌÍAÈžRAÀžRAž£×Až\A°záAšzáAšffA QìAQìA=qA(öA{A{A  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B}õÃByë
+ByáHBuáHBq×
+BmÌÍBmÌÍBiÂBežRBe®Ba®B]£×BYBYBU\BQ
+BQ
+BMzáBIp€BIffBEffBA\)B=QìB=QìB9G®B5=qB533B133B-(öB)
+žB)
+žB%{B!
+=B!  B
+  BõÃBë
+Bë
+BáHB
+×
+B
+×
+BÌÍBÂBžRB žRAù\)AñG®AñG®Aé33Aá
+žAá
+=AÙ
+=AÐõÃAÈáHAÈáHAÀÌÍAžžRAžžRA°£×Aš\AšzáA záAffAQìAQìA=qA(öA{Ap(öA`  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~  Bz  ByõÃBuë
+BqáHBmáHBm×
+BiÌÍBeÌÍBeÂBažRB]®BY®BY£×BUBQBQ\BM
+BI
+BIzáBEp€BAffB=ffB=\)B9QìB5QìB5G®B1=qB-33B)33B)(öB%
+žB!
+žB!{B
+
+=B  B  BõÃBë
+B
+ë
+B
+áHB×
+B×
+BÌÍB ÂAùp€Añp€Añ\)AéG®AáG®Aá33AÙ
+žAÑ
+=AÉ
+=AÈõÃAÀáHAžáHAžÌÍA°žRAšžRAš£×A \AzáAzáAffAQìAQìA=qApQìA`(öAP(öAP  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~{Bz
+=Bz  Bv  BqõÃBmë
+BmáHBiáHBe×
+BeÌÍBaÌÍB]ÂBYžRBY®BU®BQ£×BQBMBI\BI
+BE
+BAzáB=p€B=ffB9ffB5\)B5QìB1QìB-G®B)=qB)33B%33B!(öB!
+žB
+
+žB{B
+=B  B  B
+õÃB
+ë
+Bë
+BáHB×
+B ×
+AùAñ
+Añp€Aép€Aá\)AáG®AÙG®AÑ33AÉ
+žAÉ
+=AÁ
+=AžõÃAžáHA°áHAšÌÍAšžRA žRA£×A\AzáAzáAffAQìAp£×A`záAPQìAP(öA@(öA0  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~
+žBz{Bz{Bv
+=Br  Bn  BmõÃBië
+BeáHBeáHBa×
+B]ÌÍBYÌÍBYÂBUžRBQ®BQ®BM£×BIBIBE\BA
+B=
+B=záB9p€B5ffB5ffB1\)B-QìB)QìB)G®B%=qB!33B!33B
+(öB
+žB
+žB{B
+=B
+  B
+  BõÃBë
+Bë
+B áHAù®Añ®AñAé
+Aáp€Aáp€AÙ\)AÑG®AÉG®AÉ33AÁ
+žA¹
+=A¹
+=A°õÃAšáHAšáHA ÌÍAžRAžRA£×A\AzáAzáApÌÍA`£×AP£×APzáA@QìA0(öA0(öA   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~(öBz(öBz
+žBv{Br{Bn
+=Bn  Bj  BeõÃBeë
+BaáHB]áHBY×
+BYÌÍBUÌÍBQÂBQžRBM®BI®BI£×BEBAB=\B=
+B9
+B5záB5p€B1ffB-ffB)\)B)QìB%QìB!G®B!=qB
+33B33B(öB
+žB
+žB
+{B
+=B	  B  BõÃB ë
+Aù×
+AñÂAñ®Aé®AáAá
+AÙp€AÑp€AÉ\)AÉG®AÁG®A¹33A¹
+žA±
+=A©
+=AšõÃA áHAáHAÌÍAžRAžRA£×A\ApõÃA`õÃAPÌÍAP£×A@£×A0záA0QìA (öA(öA  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~=qBz33Bz(öBv(öBr
+žBn{Bn{Bj
+=Bf  Bf  BaõÃB]ë
+BYáHBYáHBU×
+BQÌÍBQÌÍBMÂBIžRBI®BE®BA£×B=B=B9\B5
+B5
+B1záB-p€B)ffB)ffB%\)B!QìB!QìB
+G®B=qB33B33B(öB
+
+žB
+
+žB	{B
+=B  B  Aùë
+Añ×
+Añ×
+AéÂAá®Aá®AÙAÑ
+AÉp€AÉp€AÁ\)A¹G®A¹G®A±33A©
+žA©
+=A¡
+=AõÃAáHAáHAÌÍAžRAžRAqG®Aa
+žAPõÃAPõÃA@ÌÍA0£×A0£×A záAQìA(öA (ö@à  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~G®BzG®Bz=qBv33Br(öBn(öBn
+žBj{Bf{Bf
+=Bb  B^  BYõÃBYë
+BUáHBQáHBQ×
+BMÌÍBIÌÍBIÂBEžRBA®B=®B=£×B9B5B5\B1
+B-
+B)záB)p€B%ffB!ffB!\)B
+QìBQìBG®B=qB33B
+33B
+(öB	
+žB
+žB{B
+=Aú  Aò  Añë
+Aé×
+Aá×
+AáÂAÙ®AÑ®AÉAÉ
+AÁp€A¹p€A¹\)A±G®A©G®A©33A¡
+žA
+=A
+=AõÃAáHAáHAÌÍAqp€Aap€AQG®AQ
+žA@õÃA0õÃA0ÌÍA £×A£×AzáA Qì@àQì@ÀQì@À  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B~\)BzQìBzG®BvG®Br=qBn33Bn(öBj(öBf
+žBf{Bb{B^
+=BZ  BZ  BUõÃBQë
+BQáHBMáHBI×
+BIÌÍBEÌÍBAÂB=žRB=®B9®B5£×B5B1B-\B)
+B)
+B%záB!p€B!ffB
+ffB\)BQìBQìBG®B
+=qB
+33B	33B(öB
+žB
+žAú(öAò{Aò  Aê  Aáë
+Aá×
+AÙ×
+AÑÂAÉ®AÉ®AÁA¹
+A¹p€A±p€A©\)A©G®A¡G®A33A
+žA
+=A
+=AõÃAáHAqÂAaAQp€AQp€AAG®A1
+žA0õÃA õÃAÌÍA£×A £×@àõÃ@À£×@ÀQì@ Qì@  ¿  ¿  ¿  ¿  ¿  ¿  B~ffBz\)Bz\)BvQìBrG®BnG®Bn=qBj33Bf(öBf(öBb
+žB^{BZ{BZ
+=BV  BR  BQõÃBMë
+BIáHBIáHBE×
+BAÌÍB=ÌÍB=ÂB9žRB5®B5®B1£×B-B)B)\B%
+B!
+B!záB
+p€BffBffB\)BQìB
+QìB
+G®B	=qB33B33B(öAú=qAò=qAò(öAê{Aâ  Aâ  AÙë
+AÑ×
+AÉ×
+AÉÂAÁ®A¹®A¹A±
+A©p€A©p€A¡\)AG®AG®A33A
+žA
+=A
+=Aqë
+AaÂAQÂAQAAp€A1p€A1G®A!
+žAõÃAõÃA ÌÍ@áG®@ÁG®@ÀõÃ@ £×@Qì@Qì@@  ¿  ¿  ¿  ¿  B~záBzp€BzffBv\)Br\)BnQìBnG®BjG®Bf=qBf33Bb(öB^(öBZ
+žBZ{BV{BR
+=BR  BN  BIõÃBIë
+BEáHBAáHB=×
+B=ÌÍB9ÌÍB5ÂB5žRB1®B-®B)£×B)B%B!\B!
+B
+
+BzáBp€BffBffB
+\)B
+QìB	QìBG®B=qB33AúffAòQìAò=qAê=qAâ(öAâ{AÚ  AÒ  AÉë
+AÉ×
+AÁ×
+A¹ÂA¹®A±®A©A©
+A¡p€Ap€A\)AG®AG®A33A
+žAr{Ab{AQë
+AQÂAAÂA1A1p€A!p€AG®A
+žA õÃ@áë
+@Á@ÁG®@¡G®@õÃ@£×@@£×@ £×?  ¿  ¿  B~
+BzzáBzzáBvp€BrffBn\)Bn\)BjQìBfG®BfG®Bb=qB^33BZ(öBZ(öBV
+žBR{BR{BN
+=BJ  BJ  BEõÃBAë
+B=áHB=áHB9×
+B5ÌÍB5ÌÍB1ÂB-žRB)®B)®B%£×B!B!B
+\B
+B
+BzáBp€B
+ffB
+ffB	\)BQìBQìBG®AúzáAòffAòffAêQìAâ=qAâ=qAÚ(öAÒ{AÊ  AÊ  AÁë
+A¹×
+A¹×
+A±ÂA©®A©®A¡A
+Ap€Ap€A\)AG®AG®ArffAb=qAR{AR{AAë
+A1ÂA1ÂA!Ap€Ap€AG®@â=q@Áë
+@Áë
+@¡@G®@G®@Aë
+@G®?G®?G®    ¿  ¿  Bz
+BvzáBrzáBnp€BnffBj\)Bf\)BfQìBbG®B^G®BZ=qBZ33BV(öBR(öBR
+žBN{BJ{BJ
+=BF  BB  B=õÃB=ë
+B9áHB5áHB5×
+B1ÌÍB-ÌÍB)ÂB)žRB%®B!®B!£×B
+BB\B
+B
+B
+záB
+p€B	ffBffB\)BQìAú£×Aò\AòzáAêffAâffAâQìAÚ=qAÒ=qAÊ(öAÊ{AÂ  Aº  A¹ë
+A±×
+A©×
+A©ÂA¡®A®AA
+Ap€Ap€A\)Ar\Ab\ARffAR=qAB{A2{A1ë
+A!ÂAÂAAp€@âáH@Â\@Â=q@¡ë
+@ë
+@@B\@\?×
+?\<#×
+¿  ¿  ¿  ¿  Br
+BnzáBnzáBjp€BfffBf\)Bb\)B^QìBZG®BZG®BV=qBR33BR(öBN(öBJ
+žBJ{BF{BB
+=B>  B>  B9õÃB5ë
+B5áHB1áHB-×
+B)ÌÍB)ÌÍB%ÂB!žRB!®B
+®B£×BBB\B
+
+B
+
+B	záBp€BffBffAúžRAò£×Aò£×Aê\AâzáAâffAÚffAÒQìAÊ=qAÊ=qAÂ(öAº{Aº  A²  A©ë
+A©×
+A¡×
+AÂA®A®AA
+Ap€AráHAbžRAR\AR\ABffA2=qA2{A"{Aë
+AÂAÂ@ã33@ÂáH@ÂáH@¢\@=q@ë
+@C×
+@33?
+
+ž?
+
+ž<õÂ¿  ¿  ¿  ¿  ¿  ¿  Bn
+BjzáBfzáBfp€BbffB^\)BZ\)BZQìBVG®BRG®BR=qBN33BJ(öBJ(öBF
+žBB{B>{B>
+=B:  B6  B5õÃB1ë
+B-áHB)áHB)×
+B%ÌÍB!ÌÍB!ÂB
+žRB®B®B£×BB
+B
+\B	
+B
+BzáBp€AúÌÍAòÌÍAòžRAê£×Aâ£×Aâ\AÚzáAÒffAÊffAÊQìAÂ=qAº=qAº(öA²{Aª  Aª  A¡ë
+A×
+A×
+AÂA®A®AAs
+=AbáHARáHARžRAB\A2\A2ffA"=qA{A{Aë
+@ã
+@Ã
+@Ã33@¢áH@áH@\@Dzá@×
+?®?ff=#×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bf
+BfzáBbzáB^p€BZffBZ\)BV\)BRQìBRG®BNG®BJ=qBJ33BF(öBB(öB>
+žB>{B:{B6
+=B6  B2  B-õÃB)ë
+B)áHB%áHB!×
+B!ÌÍB
+ÌÍBÂBžRB®B®B
+£×B
+B	B\B
+B
+AúõÃAòáHAòÌÍAêÌÍAâžRAâ£×AÚ£×AÒ\AÊzáAÊffAÂffAºQìAº=qA²=qAª(öAª{A¢  A  Aë
+A×
+A×
+AÂA®As\)Ac33AS
+=ARáHABáHA2žRA2\A"\AffA=qA{@ä(ö@Ã×
+@Ã
+@£
+@33@áH@EÂ@
+ž?õÃ?®=uÂ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Bb
+B^záBZzáBZp€BVffBR\)BR\)BNQìBJG®BJG®BF=qBB33B>(öB>(öB:
+žB6{B6{B2
+=B.  B*  B)õÃB%ë
+B!áHB!áHB
+×
+BÌÍBÌÍBÂBžRB
+®B
+®B	£×BBB\Aû
+=Aó
+=AòõÃAêáHAâÌÍAâÌÍAÚžRAÒ£×AÊ£×AÊ\AÂzáAºffAºffA²QìAª=qAª=qA¢(öA{A  A  Aë
+A×
+A×
+As
+Ac\)AS\)AS33AC
+=A2áHA2áHA"žRA\A\Aff@äzá@Ä(ö@Ä(ö@£×
+@
+@
+@Fff@Â?
+?=q=\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BZ
+BZzáBVzáBRp€BRffBN\)BJ\)BJQìBFG®BBG®B>=qB>33B:(öB6(öB6
+žB2{B.{B*
+=B*  B&  B!õÃB!ë
+B
+áHBáHB×
+BÌÍBÌÍB
+ÂB
+žRB	®B®B£×BAû33Aó
+žAó
+=Aë
+=AâõÃAâáHAÚÌÍAÒÌÍAÊžRAÊ£×AÂ£×Aº\AºzáA²ffAªffAªQìA¢=qA=qA(öA{A  A  Aë
+As®Ac®AS
+AS\)AC\)A333A3
+=A"áHAáHAžRA\@å
+ž@ÄÌÍ@Äzá@€(ö@(ö@×
+@G
+=@
+=?ÌÍ?
+=žQì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BV
+BRzáBRzáBNp€BJffBJ\)BF\)BBQìB>G®B>G®B:=qB633B6(öB2(öB.
+žB*{B*{B&
+=B"  B"  B
+õÃBë
+BáHBáHB×
+B
+ÌÍB
+ÌÍB	ÂBžRB®B®AûG®Aó33Aó33Aë
+žAã
+=Aã
+=AÚõÃAÒáHAÊÌÍAÊÌÍAÂžRAº£×Aº£×A²\AªzáAªffA¢ffAQìA=qA=qA(öA{A  At  Ac×
+AS®AS®AC
+A3\)A3\)A#33A
+=AáHAáH@åp€@Å
+ž@Å
+ž@€ÌÍ@zá@(ö@HQì@®?{?{=ÌÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BR
+BNzáBJzáBJp€BFffBB\)B>\)B>QìB:G®B6G®B6=qB233B.(öB*(öB*
+žB&{B"{B"
+=B
+  B  BõÃBë
+BáHB
+áHB
+×
+B	ÌÍBÌÍBÂBžRAû\)Aó\)AóG®Aë33Aã33Aã
+žAÛ
+=AÓ
+=AÊõÃAÊáHAÂÌÍAºÌÍAºžRA²£×Aª£×Aª\A¢záAffAffAQìA=qA=qA(öAt(öAd  AT  AS×
+AC®A3®A3
+A#\)A\)A33A
+=@åÂ@ÅÂ@Åp€@¥
+ž@
+
+ž@ÌÍ@HõÃ@Qì?£×?\)=áG®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BJ
+BJzáBFzáBBp€B>ffB>\)B:\)B6QìB6G®B2G®B.=qB*33B*(öB&(öB"
+žB"{B
+{B
+=B  B  BõÃB
+ë
+B
+áHB	áHB×
+BÌÍBÌÍAû
+Aóp€Aó\)Aë\)AãG®Aã33AÛ33AÓ
+žAË
+=AË
+=AÂõÃAºáHAºÌÍA²ÌÍAªžRAª£×A¢£×A\AzáAffAffAQìA=qAtzáAdQìAT(öAT  AD  A3×
+A3®A#®A
+A\)A\)@æff@Æ{@ÅÂ@¥Â@
+p€@
+
+ž@J=q@	?ë
+?£×>
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  BF
+BBzáB>záB>p€B:ffB6\)B6\)B2QìB.G®B*G®B*=qB&33B"(öB"(öB
+
+žB{B{B
+=B  B  B
+õÃB	ë
+BáHBáHB×
+AûAóAó
+Aëp€Aã\)Aã\)AÛG®AÓ33AË33AË
+žAÃ
+=A»
+=AºõÃA²áHAªÌÍAªÌÍA¢žRA£×A£×A\AzáAffAffAt£×AdzáATzáATQìAD(öA4  A4  A#×
+A®A®A
+@æžR@ÆžR@Æff@Š{@
+Â@
+Â@JáH@
+=q?zá?33>\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B>
+B>záB:záB6p€B6ffB2\)B.\)B*QìB*G®B&G®B"=qB"33B
+(öB(öB
+žB{B{B
+=B  B
+  BõÃBë
+BáHAûÂAó®AóAëAã
+Aãp€AÛ\)AÓ\)AËG®AË33AÃ33A»
+žA»
+=A³
+=AªõÃAªáHA¢ÌÍAÌÍAžRA£×A£×A\AzáAtÌÍAdÌÍAT£×ATzáADzáA4QìA4(öA$  A  A×
+A®@ç\)@Ç
+=@ÆžR@ŠžR@ff@{@K
+@
+
+?Â?zá>#×
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B:
+B6záB6záB2p€B.ffB*\)B*\)B&QìB"G®B"G®B
+=qB33B(öB(öB
+žB{B{B
+
+=B  B  BõÃAû×
+AóÂAóÂAë®AãAãAÛ
+AÓp€AË\)AË\)AÃG®A»33A»33A³
+žA«
+=A«
+=A¢õÃAáHAÌÍAÌÍAžRA£×A£×Au
+žAdõÃATÌÍATÌÍAD£×A4záA4záA$QìA(öA  A  @ç®@Ç\)@Ç\)@§
+=@žR@žR@LÌÍ@
+(ö?
+>?
+>>.{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B6
+B2záB.záB*p€B*ffB&\)B"\)B"QìB
+G®BG®B=qB33B(öB(öB
+žB
+{B{B
+=B  Aü  Aóë
+Aó×
+AëÂAãÂAã®AÛAÓAË
+AËp€AÃ\)A»\)A»G®A³33A«33A«
+žA£
+=A
+=AõÃAáHAÌÍAÌÍAžRAuG®AeG®AU
+žATõÃADÌÍA4ÌÍA4£×A$záAzáAQìA(ö@è  @È  @Ç®@§\)@\)@
+=@Mp€@
+p€??Qì>8Qì¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B.
+B*záB*záB&p€B"ffB"\)B
+\)BQìBG®BG®B=qB33B(öB
+(öB
+žB{B{Aü{Aô  Aô  Aëë
+Aã×
+AãÂAÛÂAÓ®AËAËAÃ
+A»p€A»\)A³\)A«G®A«33A£33A
+žA
+=A
+=AõÃAáHAÌÍAuAep€AUG®AUG®AE
+žA4õÃA4ÌÍA$ÌÍA£×AzáAzá@è£×@ÈQì@È  @š  @®@\)@NžR@{?áH?áH>LÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B*
+B&záB"záB"p€B
+ffB\)B\)BQìBG®BG®B=qB
+33B(öB(öB
+žAü(öAô(öAô{Aì  Aä  Aãë
+AÛ×
+AÓÂAËÂAË®AÃA»A»
+A³p€A«\)A«\)A£G®A33A33A
+žA
+=A
+=AõÃAuÂAeAUAUp€AEG®A5G®A5
+žA$õÃAÌÍAÌÍA£×@èõÃ@ÈõÃ@È£×@šQì@  @  @O\)@žR?p€?(ö>W
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B"
+B"záB
+záBp€BffB\)B\)BQìBG®B
+G®B=qB33B(öAüQìAô=qAô(öAì(öAä{Aä  AÜ  AÓë
+AË×
+AËÂAÃÂA»®A»A³A«
+A«p€A£\)A\)AG®A33A33A
+žA
+=Av{Aeë
+AUÂAUAEA5p€A5G®A%G®A
+žAõÃAÌÍ@é@ÉG®@ÈõÃ@šõÃ@£×@Qì@P  @  ?žR?p€>k
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+
+BzáBzáBp€BffB\)B\)B
+QìBG®BG®B=qAüffAôQìAôQìAì=qAä(öAä(öAÜ{AÔ  AÌ  AËë
+AÃ×
+A»ÂA»ÂA³®A«A«A£
+Ap€A\)A\)AG®A33A33Av=qAf{AV{AUë
+AEÂA5A5A%p€AG®AG®A
+ž@éë
+@É@É@©G®@õÃ@õÃ@QG®@£×?   ?   >uÂ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáBzáBp€BffB
+\)B\)BQìBG®Aü\AôzáAôffAìQìAäQìAä=qAÜ(öAÔ(öAÌ{AÌ  AÄ  A»ë
+A»×
+A³ÂA«ÂA«®A£AA
+Ap€A\)A\)AG®AvffAfffAV=qAV{AF{A5ë
+A5ÂA%AAp€AG®@ê\@Ê=q@Éë
+@©@@G®@Që
+@ë
+?¢\?¡G®>  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáBzáB
+p€BffB\)B\)Aü£×Aô\Aô\AìzáAäffAäQìAÜQìAÔ=qAÌ(öAÌ(öAÄ{AŒ  AŒ  A³ë
+A«×
+A«ÂA£ÂA®AAA
+Ap€A\)AvžRAf\AVffAVffAF=qA6{A6{A%ë
+AÂAA@êáH@Ê\@Ê\@ª=q@ë
+@@S33@\?£×
+?£×
+>=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+B
+záBzáBp€BffAüžRAôžRAô£×Aì\Aä\AäzáAÜffAÔQìAÌQìAÌ=qAÄ(öAŒ(öAŒ{AŽ  A¬  A«ë
+A£×
+AÂAÂA®AAA
+AváHAfžRAVžRAV\AFffA6ffA6=qA&{A{Aë
+AÂ@ë33@Ë33@ÊáH@ª\@\@=q@S×
+@33?Šff?¥
+ž>\)¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+BzáBzáAüáHAôÌÍAôžRAìžRAä£×Aä\AÜ\AÔzáAÌffAÌQìAÄQìAŒ=qAŒ(öAŽ(öA¬{A¬  A€  Aë
+A×
+AÂAÂA®AAw33Ag
+=AVáHAVžRAFžRA6\A6ffA&ffA=qA{A{@ë×
+@Ë
+@Ë33@«33@áH@\@U
+ž@zá?§®?Šff>¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  B
+AüõÃAôõÃAôáHAìÌÍAäžRAäžRAÜ£×AÔ\AÌ\AÌzáAÄffAŒQìAŒQìAŽ=qA¬(öA¬(öA€{A  A  Aë
+A×
+AÂAÂAw\)Ag33AW33AW
+=AFáHA6žRA6žRA&\AffAffA=q@ì(ö@Ì(ö@Ë×
+@«
+@33@33@UÂ@
+ž?ª=q?šõÂ>žR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aõ
+=AôõÃAìõÃAäáHAäÌÍAÜžRAÔžRAÌ£×AÌ\AÄ\AŒzáAŒffAŽQìA¬QìA¬=qA€(öA(öA{A  A  Aë
+A×
+Aw
+Ag
+AW\)AW33AG33A7
+=A6áHA&žRAžRA\Aff@ìÌÍ@Ìzá@Ì(ö@¬(ö@×
+@
+@Vff@ff?«
+?ª=q>šõÃ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aí
+=AäõÃAäõÃAÜáHAÔÌÍAÌžRAÌžRAÄ£×AŒ\AŒ\AŽzáA¬ffA¬QìA€QìA=qA(öA(öA{A  A  Aw×
+Ag®AW
+AW
+AG\)A733A733A'
+=AáHAžRAžR@í
+ž@ÌÌÍ@ÌÌÍ@¬zá@(ö@(ö@W®@
+>?¬ÌÍ?¬ÌÍ>®{¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aå
+=AÜõÃAÔõÃAÌáHAÌÌÍAÄžRAŒžRAŒ£×AŽ\A¬\A¬záA€ffAQìAQìA=qA(öA(öA{Ax  Ah  AW×
+AW®AG
+A7
+A7\)A'33A33A
+=AáH@íp€@Íp€@Í
+ž@¬ÌÍ@ÌÍ@zá@XQì@Qì?¯\)?®{>³33¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÕ
+=AÌõÃAÌõÃAÄáHAŒÌÍAŒžRAŽžRA¬£×A¬\A€\AzáAffAQìAQìA=qA(öAxQìAh(öAX  AX  AG×
+A7®A7
+A'
+A\)A33A33@î{@ÍÂ@Íp€@­p€@
+ž@ÌÍ@Y@õÂ?°£×?°£×>œp€¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AÍ
+=AÄõÃAŒõÃAŒáHAŽÌÍA¬žRA¬žRA€£×A\A\AzáAffAQìAQìAxzáAhQìAXQìAX(öAH  A8  A7×
+A'®A
+A
+A\)@îff@Îff@Î{@­Â@p€@p€@Z=q@?³33?±ë
+>Â\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aœ
+=AŒõÃAŽõÃA¬áHA¬ÌÍA€žRAžRA£×A\A\AzáAffAx£×Ah£×AXzáAXQìAHQìA8(öA8  A(  A×
+A®A
+@ï
+=@ÎžR@Îff@®ff@{@Â@ZáH@áH?Žzá?³33>ÌÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Aµ
+=A¬õÃA¬õÃA€áHAÌÍAžRAžRA£×A\A\AxõÃAhÌÍAX£×AX£×AHzáA8QìA8QìA((öA  A  A×
+@ï\)@Ï
+=@Ï
+=@®žR@ff@ff@\(ö@
+?µÂ?µÂ>Ñë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A­
+=A€õÃAõÃAáHAÌÍAžRAžRA£×Ay
+žAi
+žAXõÃAXÌÍAH£×A8£×A8záA(QìAQìA(öA  @ð  @Ï®@Ï\)@¯
+=@
+=@žR@\ÌÍ@
+ÌÍ?žQì?·
+>>×
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+=AõÃAõÃAáHAÌÍAžRAyp€AiG®AY
+žAY
+žAHõÃA8ÌÍA8£×A(£×AzáAQìAQì@ðQì@Ð  @Ð  @¯®@\)@
+=@^{@
+p€?¹?¹>áG®¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+=AõÃAõÃAáHAyAip€AYp€AYG®AI
+žA9
+žA8õÃA(ÌÍA£×A£×Azá@ð£×@Ð£×@ÐQì@°  @  @®@^žR@
+{?Œ(ö?ºáH>æff¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+
+=AõÃAyë
+AiÂAYAYp€AIp€A9G®A9
+žA)
+žAõÃAÌÍA£×@ñG®@ÐõÂ@Ð£×@°£×@Qì@  @`  @\)?œp€?Œ(ö>ð£×¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  Az{Aië
+AYë
+AYÂAIA9p€A9p€A)G®A
+žA
+žAõÃ@ñ@ÑG®@ÑG®@°õÂ@£×@£×@`£×@   ?À  ?ŸžR>õÂ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AZ{AYë
+AIë
+A9ÂA9A)p€Ap€AG®A	
+ž@ò=q@Ñë
+@Ñ@±G®@G®@õÂ@aG®@!G®?ÁG®?À  ?   ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  AJ{A9ë
+A9ë
+A)ÂAAp€A	p€@ò\@Ò=q@Ò=q@±ë
+@@G®@b\@!ë
+?Â\?Â\?\¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A:{A)ë
+Aë
+AÂA	@òáH@ÒáH@Ò\@²=q@=q@ë
+@c33@"\?Å
+ž?Ã×
+?
+ž¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A{Aë
+A	ë
+@ó
+@Ó33@ÒáH@²áH@\@=q@dzá@#×
+?Æff?Å
+ž?
+=q¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  A
+{@ó×
+@Ó×
+@Ó
+@³33@áH@áH@e
+ž@$zá?ÈõÂ?Ç®?
+ÌÍ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @Ô(ö@Ó×
+@³×
+@
+@33@eÂ@%Â?Ê=p?ÈõÂ?ë
+¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @Ž(ö@×
+@×
+@g
+>@&ff?Ë
+
+?Ë
+
+?zá¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @(ö@g®@'®?Î{?ÌÌÍ?
+=¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  @(Qì?Ï\)?Ï\)?
+(ö¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ?Ð£×?
+žR¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿  ¿                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             B~
+B~záB~p€B~ffB~\)B~QìB~G®B~=qB~33B~(öB~
+žB~{B~
+=B~  B}õÃB}ë
+B}áHB}×
+B}ÌÍB}ÂB}žRB}®B}£×B}B}\B}
+B}záB}p€B}ffB}\)B}QìB}G®B}=qB}33B}(öB}
+žB}{B}
+=B}  B|õÃB|ë
+B|áHB|×
+B|ÌÍB|ÂB|žRB|®B|£×B|B|\B|
+B|záB|p€B|ffB|\)B|QìB|G®B|=qB|33B|(öB|
+žB|{B|
+=B|  Bz
+BzzáBzp€BzffBz\)BzQìBzG®Bz=qBz33Bz(öBz
+žBz{Bz
+=Bz  ByõÃByë
+ByáHBy×
+ByÌÍByÂByžRBy®By£×ByBy\By
+ByzáByp€ByffBy\)ByQìByG®By=qBy33By(öBy
+žBy{By
+=By  BxõÃBxë
+BxáHBx×
+BxÌÍBxÂBxžRBx®Bx£×BxBx\Bx
+BxzáBxp€BxffBx\)BxQìBxG®Bx=qBx33Bx(öBx
+žBx{Bx
+=Bx  Bv
+BvzáBvp€BvffBv\)BvQìBvG®Bv=qBv33Bv(öBv
+žBv{Bv
+=Bv  BuõÃBuë
+BuáHBu×
+BuÌÍBuÂBužRBu®Bu£×BuBu\Bu
+BuzáBup€BuffBu\)BuQìBuG®Bu=qBu33Bu(öBu
+žBu{Bu
+=Bu  BtõÃBtë
+BtáHBt×
+BtÌÍBtÂBtžRBt®Bt£×BtBt\Bt
+BtzáBtp€BtffBt\)BtQìBtG®Bt=qBt33Bt(öBt
+žBt{Bt
+=Bt  Br
+BrzáBrp€BrffBr\)BrQìBrG®Br=qBr33Br(öBr
+žBr{Br
+=Br  BqõÃBqë
+BqáHBq×
+BqÌÍBqÂBqžRBq®Bq£×BqBq\Bq
+BqzáBqp€BqffBq\)BqQìBqG®Bq=qBq33Bq(öBq
+žBq{Bq
+=Bq  BpõÃBpë
+BpáHBp×
+BpÌÍBpÂBpžRBp®Bp£×BpBp\Bp
+BpzáBpp€BpffBp\)BpQìBpG®Bp=qBp33Bp(öBp
+žBp{Bp
+=Bp  Bn
+BnzáBnp€BnffBn\)BnQìBnG®Bn=qBn33Bn(öBn
+žBn{Bn
+=Bn  BmõÃBmë
+BmáHBm×
+BmÌÍBmÂBmžRBm®Bm£×BmBm\Bm
+BmzáBmp€BmffBm\)BmQìBmG®Bm=qBm33Bm(öBm
+žBm{Bm
+=Bm  BlõÃBlë
+BláHBl×
+BlÌÍBlÂBlžRBl®Bl£×BlBl\Bl
+BlzáBlp€BlffBl\)BlQìBlG®Bl=qBl33Bl(öBl
+žBl{Bl
+=Bl  Bj
+BjzáBjp€BjffBj\)BjQìBjG®Bj=qBj33Bj(öBj
+žBj{Bj
+=Bj  BiõÃBië
+BiáHBi×
+BiÌÍBiÂBižRBi®Bi£×BiBi\Bi
+BizáBip€BiffBi\)BiQìBiG®Bi=qBi33Bi(öBi
+žBi{Bi
+=Bi  BhõÃBhë
+BháHBh×
+BhÌÍBhÂBhžRBh®Bh£×BhBh\Bh
+BhzáBhp€BhffBh\)BhQìBhG®Bh=qBh33Bh(öBh
+žBh{Bh
+=Bh  Bf
+BfzáBfp€BfffBf\)BfQìBfG®Bf=qBf33Bf(öBf
+žBf{Bf
+=Bf  BeõÃBeë
+BeáHBe×
+BeÌÍBeÂBežRBe®Be£×BeBe\Be
+BezáBep€BeffBe\)BeQìBeG®Be=qBe33Be(öBe
+žBe{Be
+=Be  BdõÃBdë
+BdáHBd×
+BdÌÍBdÂBdžRBd®Bd£×BdBd\Bd
+BdzáBdp€BdffBd\)BdQìBdG®Bd=qBd33Bd(öBd
+žBd{Bd
+=Bd  Bb
+BbzáBbp€BbffBb\)BbQìBbG®Bb=qBb33Bb(öBb
+žBb{Bb
+=Bb  BaõÃBaë
+BaáHBa×
+BaÌÍBaÂBažRBa®Ba£×BaBa\Ba
+BazáBap€BaffBa\)BaQìBaG®Ba=qBa33Ba(öBa
+žBa{Ba
+=Ba  B`õÃB`ë
+B`áHB`×
+B`ÌÍB`ÂB`žRB`®B`£×B`B`\B`
+B`záB`p€B`ffB`\)B`QìB`G®B`=qB`33B`(öB`
+žB`{B`
+=B`  B^
+B^záB^p€B^ffB^\)B^QìB^G®B^=qB^33B^(öB^
+žB^{B^
+=B^  B]õÃB]ë
+B]áHB]×
+B]ÌÍB]ÂB]žRB]®B]£×B]B]\B]
+B]záB]p€B]ffB]\)B]QìB]G®B]=qB]33B](öB]
+žB]{B]
+=B]  B\õÃB\ë
+B\áHB\×
+B\ÌÍB\ÂB\žRB\®B\£×B\B\\B\
+B\záB\p€B\ffB\\)B\QìB\G®B\=qB\33B\(öB\
+žB\{B\
+=B\  BZ
+BZzáBZp€BZffBZ\)BZQìBZG®BZ=qBZ33BZ(öBZ
+žBZ{BZ
+=BZ  BYõÃBYë
+BYáHBY×
+BYÌÍBYÂBYžRBY®BY£×BYBY\BY
+BYzáBYp€BYffBY\)BYQìBYG®BY=qBY33BY(öBY
+žBY{BY
+=BY  BXõÃBXë
+BXáHBX×
+BXÌÍBXÂBXžRBX®BX£×BXBX\BX
+BXzáBXp€BXffBX\)BXQìBXG®BX=qBX33BX(öBX
+žBX{BX
+=BX  BV
+BVzáBVp€BVffBV\)BVQìBVG®BV=qBV33BV(öBV
+žBV{BV
+=BV  BUõÃBUë
+BUáHBU×
+BUÌÍBUÂBUžRBU®BU£×BUBU\BU
+BUzáBUp€BUffBU\)BUQìBUG®BU=qBU33BU(öBU
+žBU{BU
+=BU  BTõÃBTë
+BTáHBT×
+BTÌÍBTÂBTžRBT®BT£×BTBT\BT
+BTzáBTp€BTffBT\)BTQìBTG®BT=qBT33BT(öBT
+žBT{BT
+=BT  BR
+BRzáBRp€BRffBR\)BRQìBRG®BR=qBR33BR(öBR
+žBR{BR
+=BR  BQõÃBQë
+BQáHBQ×
+BQÌÍBQÂBQžRBQ®BQ£×BQBQ\BQ
+BQzáBQp€BQffBQ\)BQQìBQG®BQ=qBQ33BQ(öBQ
+žBQ{BQ
+=BQ  BPõÃBPë
+BPáHBP×
+BPÌÍBPÂBPžRBP®BP£×BPBP\BP
+BPzáBPp€BPffBP\)BPQìBPG®BP=qBP33BP(öBP
+žBP{BP
+=BP  BN
+BNzáBNp€BNffBN\)BNQìBNG®BN=qBN33BN(öBN
+žBN{BN
+=BN  BMõÃBMë
+BMáHBM×
+BMÌÍBMÂBMžRBM®BM£×BMBM\BM
+BMzáBMp€BMffBM\)BMQìBMG®BM=qBM33BM(öBM
+žBM{BM
+=BM  BLõÃBLë
+BLáHBL×
+BLÌÍBLÂBLžRBL®BL£×BLBL\BL
+BLzáBLp€BLffBL\)BLQìBLG®BL=qBL33BL(öBL
+žBL{BL
+=BL  BJ
+BJzáBJp€BJffBJ\)BJQìBJG®BJ=qBJ33BJ(öBJ
+žBJ{BJ
+=BJ  BIõÃBIë
+BIáHBI×
+BIÌÍBIÂBIžRBI®BI£×BIBI\BI
+BIzáBIp€BIffBI\)BIQìBIG®BI=qBI33BI(öBI
+žBI{BI
+=BI  BHõÃBHë
+BHáHBH×
+BHÌÍBHÂBHžRBH®BH£×BHBH\BH
+BHzáBHp€BHffBH\)BHQìBHG®BH=qBH33BH(öBH
+žBH{BH
+=BH  BF
+BFzáBFp€BFffBF\)BFQìBFG®BF=qBF33BF(öBF
+žBF{BF
+=BF  BEõÃBEë
+BEáHBE×
+BEÌÍBEÂBEžRBE®BE£×BEBE\BE
+BEzáBEp€BEffBE\)BEQìBEG®BE=qBE33BE(öBE
+žBE{BE
+=BE  BDõÃBDë
+BDáHBD×
+BDÌÍBDÂBDžRBD®BD£×BDBD\BD
+BDzáBDp€BDffBD\)BDQìBDG®BD=qBD33BD(öBD
+žBD{BD
+=BD  BB
+BBzáBBp€BBffBB\)BBQìBBG®BB=qBB33BB(öBB
+žBB{BB
+=BB  BAõÃBAë
+BAáHBA×
+BAÌÍBAÂBAžRBA®BA£×BABA\BA
+BAzáBAp€BAffBA\)BAQìBAG®BA=qBA33BA(öBA
+žBA{BA
+=BA  B@õÃB@ë
+B@áHB@×
+B@ÌÍB@ÂB@žRB@®B@£×B@B@\B@
+B@záB@p€B@ffB@\)B@QìB@G®B@=qB@33B@(öB@
+žB@{B@
+=B@  B>
+B>záB>p€B>ffB>\)B>QìB>G®B>=qB>33B>(öB>
+žB>{B>
+=B>  B=õÃB=ë
+B=áHB=×
+B=ÌÍB=ÂB=žRB=®B=£×B=B=\B=
+B=záB=p€B=ffB=\)B=QìB=G®B==qB=33B=(öB=
+žB={B=
+=B=  B<õÃB<ë
+B<áHB<×
+B<ÌÍB<ÂB<žRB<®B<£×B<B<\B<
+B<záB<p€B<ffB<\)B<QìB<G®B<=qB<33B<(öB<
+žB<{B<
+=B<  B:
+B:záB:p€B:ffB:\)B:QìB:G®B:=qB:33B:(öB:
+žB:{B:
+=B:  B9õÃB9ë
+B9áHB9×
+B9ÌÍB9ÂB9žRB9®B9£×B9B9\B9
+B9záB9p€B9ffB9\)B9QìB9G®B9=qB933B9(öB9
+žB9{B9
+=B9  B8õÃB8ë
+B8áHB8×
+B8ÌÍB8ÂB8žRB8®B8£×B8B8\B8
+B8záB8p€B8ffB8\)B8QìB8G®B8=qB833B8(öB8
+žB8{B8
+=B8  B6
+B6záB6p€B6ffB6\)B6QìB6G®B6=qB633B6(öB6
+žB6{B6
+=B6  B5õÃB5ë
+B5áHB5×
+B5ÌÍB5ÂB5žRB5®B5£×B5B5\B5
+B5záB5p€B5ffB5\)B5QìB5G®B5=qB533B5(öB5
+žB5{B5
+=B5  B4õÃB4ë
+B4áHB4×
+B4ÌÍB4ÂB4žRB4®B4£×B4B4\B4
+B4záB4p€B4ffB4\)B4QìB4G®B4=qB433B4(öB4
+žB4{B4
+=B4  B2
+B2záB2p€B2ffB2\)B2QìB2G®B2=qB233B2(öB2
+žB2{B2
+=B2  B1õÃB1ë
+B1áHB1×
+B1ÌÍB1ÂB1žRB1®B1£×B1B1\B1
+B1záB1p€B1ffB1\)B1QìB1G®B1=qB133B1(öB1
+žB1{B1
+=B1  B0õÃB0ë
+B0áHB0×
+B0ÌÍB0ÂB0žRB0®B0£×B0B0\B0
+B0záB0p€B0ffB0\)B0QìB0G®B0=qB033B0(öB0
+žB0{B0
+=B0  B.
+B.záB.p€B.ffB.\)B.QìB.G®B.=qB.33B.(öB.
+žB.{B.
+=B.  B-õÃB-ë
+B-áHB-×
+B-ÌÍB-ÂB-žRB-®B-£×B-B-\B-
+B-záB-p€B-ffB-\)B-QìB-G®B-=qB-33B-(öB-
+žB-{B-
+=B-  B,õÃB,ë
+B,áHB,×
+B,ÌÍB,ÂB,žRB,®B,£×B,B,\B,
+B,záB,p€B,ffB,\)B,QìB,G®B,=qB,33B,(öB,
+žB,{B,
+=B,  B*
+B*záB*p€B*ffB*\)B*QìB*G®B*=qB*33B*(öB*
+žB*{B*
+=B*  B)õÃB)ë
+B)áHB)×
+B)ÌÍB)ÂB)žRB)®B)£×B)B)\B)
+B)záB)p€B)ffB)\)B)QìB)G®B)=qB)33B)(öB)
+žB){B)
+=B)  B(õÃB(ë
+B(áHB(×
+B(ÌÍB(ÂB(žRB(®B(£×B(B(\B(
+B(záB(p€B(ffB(\)B(QìB(G®B(=qB(33B((öB(
+žB({B(
+=B(  B&
+B&záB&p€B&ffB&\)B&QìB&G®B&=qB&33B&(öB&
+žB&{B&
+=B&  B%õÃB%ë
+B%áHB%×
+B%ÌÍB%ÂB%žRB%®B%£×B%B%\B%
+B%záB%p€B%ffB%\)B%QìB%G®B%=qB%33B%(öB%
+žB%{B%
+=B%  B$õÃB$ë
+B$áHB$×
+B$ÌÍB$ÂB$žRB$®B$£×B$B$\B$
+B$záB$p€B$ffB$\)B$QìB$G®B$=qB$33B$(öB$
+žB${B$
+=B$  B"
+B"záB"p€B"ffB"\)B"QìB"G®B"=qB"33B"(öB"
+žB"{B"
+=B"  B!õÃB!ë
+B!áHB!×
+B!ÌÍB!ÂB!žRB!®B!£×B!B!\B!
+B!záB!p€B!ffB!\)B!QìB!G®B!=qB!33B!(öB!
+žB!{B!
+=B!  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+=B
+  B
+õÃB
+ë
+B
+áHB
+×
+B
+ÌÍB
+ÂB
+žRB
+®B
+£×B
+B
+\B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B
+
+B
+záB
+p€B
+ffB
+\)B
+QìB
+G®B
+=qB
+33B
+(öB
+
+žB
+{B
+
+=B
+  B	õÃB	ë
+B	áHB	×
+B	ÌÍB	ÂB	žRB	®B	£×B	B	\B	
+B	záB	p€B	ffB	\)B	QìB	G®B	=qB	33B	(öB	
+žB	{B	
+=B	  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  BõÃBë
+BáHB×
+BÌÍBÂBžRB®B£×BB\B
+BzáBp€BffB\)BQìBG®B=qB33B(öB
+žB{B
+=B  B õÃB ë
+B áHB ×
+B ÌÍB ÂB žRB ®B £×B B \B 
+B záB p€B ffB \)B QìB G®B =qB 33B (öB 
+žB {B 
+=B   Aý
+=AüõÃAüáHAüÌÍAüžRAü£×Aü\AüzáAüffAüQìAü=qAü(öAü{Aü  Aûë
+Aû×
+AûÂAû®AûAû
+Aûp€Aû\)AûG®Aû33Aû
+žAû
+=AúõÃAúáHAúÌÍAúžRAú£×Aú\AúzáAúffAúQìAú=qAú(öAú{Aú  Aùë
+Aù×
+AùÂAù®AùAù
+Aùp€Aù\)AùG®Aù33Aù
+žAù
+=AøõÃAøáHAøÌÍAøžRAø£×Aø\AøzáAøffAøQìAø=qAø(öAø{Aø  Aõ
+=AôõÃAôáHAôÌÍAôžRAô£×Aô\AôzáAôffAôQìAô=qAô(öAô{Aô  Aóë
+Aó×
+AóÂAó®AóAó
+Aóp€Aó\)AóG®Aó33Aó
+žAó
+=AòõÃAòáHAòÌÍAòžRAò£×Aò\AòzáAòffAòQìAò=qAò(öAò{Aò  Añë
+Añ×
+AñÂAñ®AñAñ
+Añp€Añ\)AñG®Añ33Añ
+žAñ
+=AðõÃAðáHAðÌÍAðžRAð£×Að\AðzáAðffAðQìAð=qAð(öAð{Að  Aí
+=AìõÃAìáHAìÌÍAìžRAì£×Aì\AìzáAìffAìQìAì=qAì(öAì{Aì  Aëë
+Aë×
+AëÂAë®AëAë
+Aëp€Aë\)AëG®Aë33Aë
+žAë
+=AêõÃAêáHAêÌÍAêžRAê£×Aê\AêzáAêffAêQìAê=qAê(öAê{Aê  Aéë
+Aé×
+AéÂAé®AéAé
+Aép€Aé\)AéG®Aé33Aé
+žAé
+=AèõÃAèáHAèÌÍAèžRAè£×Aè\AèzáAèffAèQìAè=qAè(öAè{Aè  Aå
+=AäõÃAäáHAäÌÍAäžRAä£×Aä\AäzáAäffAäQìAä=qAä(öAä{Aä  Aãë
+Aã×
+AãÂAã®AãAã
+Aãp€Aã\)AãG®Aã33Aã
+žAã
+=AâõÃAâáHAâÌÍAâžRAâ£×Aâ\AâzáAâffAâQìAâ=qAâ(öAâ{Aâ  Aáë
+Aá×
+AáÂAá®AáAá
+Aáp€Aá\)AáG®Aá33Aá
+žAá
+=AàõÃAàáHAàÌÍAàžRAà£×Aà\AàzáAàffAàQìAà=qAà(öAà{Aà  AÝ
+=AÜõÃAÜáHAÜÌÍAÜžRAÜ£×AÜ\AÜzáAÜffAÜQìAÜ=qAÜ(öAÜ{AÜ  AÛë
+AÛ×
+AÛÂAÛ®AÛAÛ
+AÛp€AÛ\)AÛG®AÛ33AÛ
+žAÛ
+=AÚõÃAÚáHAÚÌÍAÚžRAÚ£×AÚ\AÚzáAÚffAÚQìAÚ=qAÚ(öAÚ{AÚ  AÙë
+AÙ×
+AÙÂAÙ®AÙAÙ
+AÙp€AÙ\)AÙG®AÙ33AÙ
+žAÙ
+=AØõÃAØáHAØÌÍAØžRAØ£×AØ\AØzáAØffAØQìAØ=qAØ(öAØ{AØ  AÕ
+=AÔõÃAÔáHAÔÌÍAÔžRAÔ£×AÔ\AÔzáAÔffAÔQìAÔ=qAÔ(öAÔ{AÔ  AÓë
+AÓ×
+AÓÂAÓ®AÓAÓ
+AÓp€AÓ\)AÓG®AÓ33AÓ
+žAÓ
+=AÒõÃAÒáHAÒÌÍAÒžRAÒ£×AÒ\AÒzáAÒffAÒQìAÒ=qAÒ(öAÒ{AÒ  AÑë
+AÑ×
+AÑÂAÑ®AÑAÑ
+AÑp€AÑ\)AÑG®AÑ33AÑ
+žAÑ
+=AÐõÃAÐáHAÐÌÍAÐžRAÐ£×AÐ\AÐzáAÐffAÐQìAÐ=qAÐ(öAÐ{AÐ  AÍ
+=AÌõÃAÌáHAÌÌÍAÌžRAÌ£×AÌ\AÌzáAÌffAÌQìAÌ=qAÌ(öAÌ{AÌ  AËë
+AË×
+AËÂAË®AËAË
+AËp€AË\)AËG®AË33AË
+žAË
+=AÊõÃAÊáHAÊÌÍAÊžRAÊ£×AÊ\AÊzáAÊffAÊQìAÊ=qAÊ(öAÊ{AÊ  AÉë
+AÉ×
+AÉÂAÉ®AÉAÉ
+AÉp€AÉ\)AÉG®AÉ33AÉ
+žAÉ
+=AÈõÃAÈáHAÈÌÍAÈžRAÈ£×AÈ\AÈzáAÈffAÈQìAÈ=qAÈ(öAÈ{AÈ  AÅ
+=AÄõÃAÄáHAÄÌÍAÄžRAÄ£×AÄ\AÄzáAÄffAÄQìAÄ=qAÄ(öAÄ{AÄ  AÃë
+AÃ×
+AÃÂAÃ®AÃAÃ
+AÃp€AÃ\)AÃG®AÃ33AÃ
+žAÃ
+=AÂõÃAÂáHAÂÌÍAÂžRAÂ£×AÂ\AÂzáAÂffAÂQìAÂ=qAÂ(öAÂ{AÂ  AÁë
+AÁ×
+AÁÂAÁ®AÁAÁ
+AÁp€AÁ\)AÁG®AÁ33AÁ
+žAÁ
+=AÀõÃAÀáHAÀÌÍAÀžRAÀ£×AÀ\AÀzáAÀffAÀQìAÀ=qAÀ(öAÀ{AÀ  Aœ
+=AŒõÃAŒáHAŒÌÍAŒžRAŒ£×AŒ\AŒzáAŒffAŒQìAŒ=qAŒ(öAŒ{AŒ  A»ë
+A»×
+A»ÂA»®A»A»
+A»p€A»\)A»G®A»33A»
+žA»
+=AºõÃAºáHAºÌÍAºžRAº£×Aº\AºzáAºffAºQìAº=qAº(öAº{Aº  A¹ë
+A¹×
+A¹ÂA¹®A¹A¹
+A¹p€A¹\)A¹G®A¹33A¹
+žA¹
+=AžõÃAžáHAžÌÍAžžRAž£×Až\AžzáAžffAžQìAž=qAž(öAž{Až  Aµ
+=AŽõÃAŽáHAŽÌÍAŽžRAŽ£×AŽ\AŽzáAŽffAŽQìAŽ=qAŽ(öAŽ{AŽ  A³ë
+A³×
+A³ÂA³®A³A³
+A³p€A³\)A³G®A³33A³
+žA³
+=A²õÃA²áHA²ÌÍA²žRA²£×A²\A²záA²ffA²QìA²=qA²(öA²{A²  A±ë
+A±×
+A±ÂA±®A±A±
+A±p€A±\)A±G®A±33A±
+žA±
+=A°õÃA°áHA°ÌÍA°žRA°£×A°\A°záA°ffA°QìA°=qA°(öA°{A°  A­
+=A¬õÃA¬áHA¬ÌÍA¬žRA¬£×A¬\A¬záA¬ffA¬QìA¬=qA¬(öA¬{A¬  A«ë
+A«×
+A«ÂA«®A«A«
+A«p€A«\)A«G®A«33A«
+žA«
+=AªõÃAªáHAªÌÍAªžRAª£×Aª\AªzáAªffAªQìAª=qAª(öAª{Aª  A©ë
+A©×
+A©ÂA©®A©A©
+A©p€A©\)A©G®A©33A©
+žA©
+=AšõÃAšáHAšÌÍAšžRAš£×Aš\AšzáAšffAšQìAš=qAš(öAš{Aš  A¥
+=A€õÃA€áHA€ÌÍA€žRA€£×A€\A€záA€ffA€QìA€=qA€(öA€{A€  A£ë
+A£×
+A£ÂA£®A£A£
+A£p€A£\)A£G®A£33A£
+žA£
+=A¢õÃA¢áHA¢ÌÍA¢žRA¢£×A¢\A¢záA¢ffA¢QìA¢=qA¢(öA¢{A¢  A¡ë
+A¡×
+A¡ÂA¡®A¡A¡
+A¡p€A¡\)A¡G®A¡33A¡
+žA¡
+=A õÃA áHA ÌÍA žRA £×A \A záA ffA QìA =qA (öA {A   A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  A
+
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Aë
+A×
+AÂA®AA
+Ap€A\)AG®A33A
+žA
+=AõÃAáHAÌÍAžRA£×A\AzáAffAQìA=qA(öA{A  Az{Ayë
+AyÂAyAyp€AyG®Ay
+žAxõÃAxÌÍAx£×AxzáAxQìAx(öAx  Aw×
+Aw®Aw
+Aw\)Aw33Aw
+=AváHAvžRAv\AvffAv=qAv{Auë
+AuÂAuAup€AuG®Au
+žAtõÃAtÌÍAt£×AtzáAtQìAt(öAt  As×
+As®As
+As\)As33As
+=AráHAržRAr\ArffAr=qAr{Aqë
+AqÂAqAqp€AqG®Aq
+žApõÃApÌÍAp£×ApzáApQìAp(öAp  Aj{Aië
+AiÂAiAip€AiG®Ai
+žAhõÃAhÌÍAh£×AhzáAhQìAh(öAh  Ag×
+Ag®Ag
+Ag\)Ag33Ag
+=AfáHAfžRAf\AfffAf=qAf{Aeë
+AeÂAeAep€AeG®Ae
+žAdõÃAdÌÍAd£×AdzáAdQìAd(öAd  Ac×
+Ac®Ac
+Ac\)Ac33Ac
+=AbáHAbžRAb\AbffAb=qAb{Aaë
+AaÂAaAap€AaG®Aa
+žA`õÃA`ÌÍA`£×A`záA`QìA`(öA`  AZ{AYë
+AYÂAYAYp€AYG®AY
+žAXõÃAXÌÍAX£×AXzáAXQìAX(öAX  AW×
+AW®AW
+AW\)AW33AW
+=AVáHAVžRAV\AVffAV=qAV{AUë
+AUÂAUAUp€AUG®AU
+žATõÃATÌÍAT£×ATzáATQìAT(öAT  AS×
+AS®AS
+AS\)AS33AS
+=ARáHARžRAR\ARffAR=qAR{AQë
+AQÂAQAQp€AQG®AQ
+žAPõÃAPÌÍAP£×APzáAPQìAP(öAP  AJ{AIë
+AIÂAIAIp€AIG®AI
+žAHõÃAHÌÍAH£×AHzáAHQìAH(öAH  AG×
+AG®AG
+AG\)AG33AG
+=AFáHAFžRAF\AFffAF=qAF{AEë
+AEÂAEAEp€AEG®AE
+žADõÃADÌÍAD£×ADzáADQìAD(öAD  AC×
+AC®AC
+AC\)AC33AC
+=ABáHABžRAB\ABffAB=qAB{AAë
+AAÂAAAAp€AAG®AA
+žA@õÃA@ÌÍA@£×A@záA@QìA@(öA@  A:{A9ë
+A9ÂA9A9p€A9G®A9
+žA8õÃA8ÌÍA8£×A8záA8QìA8(öA8  A7×
+A7®A7
+A7\)A733A7
+=A6áHA6žRA6\A6ffA6=qA6{A5ë
+A5ÂA5A5p€A5G®A5
+žA4õÃA4ÌÍA4£×A4záA4QìA4(öA4  A3×
+A3®A3
+A3\)A333A3
+=A2áHA2žRA2\A2ffA2=qA2{A1ë
+A1ÂA1A1p€A1G®A1
+žA0õÃA0ÌÍA0£×A0záA0QìA0(öA0  A*{A)ë
+A)ÂA)A)p€A)G®A)
+žA(õÃA(ÌÍA(£×A(záA(QìA((öA(  A'×
+A'®A'
+A'\)A'33A'
+=A&áHA&žRA&\A&ffA&=qA&{A%ë
+A%ÂA%A%p€A%G®A%
+žA$õÃA$ÌÍA$£×A$záA$QìA$(öA$  A#×
+A#®A#
+A#\)A#33A#
+=A"áHA"žRA"\A"ffA"=qA"{A!ë
+A!ÂA!A!p€A!G®A!
+žA õÃA ÌÍA £×A záA QìA (öA   A{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A
+{A	ë
+A	ÂA	A	p€A	G®A	
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žAõÃAÌÍA£×AzáAQìA(öA  A×
+A®A
+A\)A33A
+=AáHAžRA\AffA=qA{Aë
+AÂAAp€AG®A
+žA õÃA ÌÍA £×A záA QìA (öA   @ô(ö@ó×
+@ó
+@ó33@òáH@ò\@ò=q@ñë
+@ñ@ñG®@ðõÂ@ð£×@ðQì@ð  @ï®@ï\)@ï
+=@îžR@îff@î{@íÂ@íp€@í
+ž@ìÌÍ@ìzá@ì(ö@ë×
+@ë
+@ë33@êáH@ê\@ê=q@éë
+@é@éG®@èõÃ@è£×@èQì@è  @ç®@ç\)@ç
+=@æžR@æff@æ{@åÂ@åp€@å
+ž@äÌÍ@äzá@ä(ö@ã×
+@ã
+@ã33@âáH@â\@â=q@áë
+@á@áG®@àõÃ@à£×@àQì@à  @Ô(ö@Ó×
+@Ó
+@Ó33@ÒáH@Ò\@Ò=q@Ñë
+@Ñ@ÑG®@ÐõÂ@Ð£×@ÐQì@Ð  @Ï®@Ï\)@Ï
+=@ÎžR@Îff@Î{@ÍÂ@Íp€@Í
+ž@ÌÌÍ@Ìzá@Ì(ö@Ë×
+@Ë
+@Ë33@ÊáH@Ê\@Ê=q@Éë
+@É@ÉG®@ÈõÃ@È£×@ÈQì@È  @Ç®@Ç\)@Ç
+=@ÆžR@Æff@Æ{@ÅÂ@Åp€@Å
+ž@ÄÌÍ@Äzá@Ä(ö@Ã×
+@Ã
+@Ã33@ÂáH@Â\@Â=q@Áë
+@Á@ÁG®@ÀõÃ@À£×@ÀQì@À  @Ž(ö@³×
+@³
+@³33@²áH@²\@²=q@±ë
+@±@±G®@°õÂ@°£×@°Qì@°  @¯®@¯\)@¯
+=@®žR@®ff@®{@­Â@­p€@­
+ž@¬ÌÍ@¬zá@¬(ö@«×
+@«
+@«33@ªáH@ª\@ª=q@©ë
+@©@©G®@šõÃ@š£×@šQì@š  @§®@§\)@§
+=@ŠžR@Šff@Š{@¥Â@¥p€@¥
+ž@€ÌÍ@€zá@€(ö@£×
+@£
+@£33@¢áH@¢\@¢=q@¡ë
+@¡@¡G®@ õÃ@ £×@ Qì@   @(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÂ@£×@Qì@  @®@\)@
+=@žR@ff@{@Â@p€@
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @®@\)@
+=@žR@ff@{@
+Â@
+p€@
+
+ž@ÌÍ@zá@(ö@×
+@
+@33@áH@\@=q@ë
+@@G®@õÃ@£×@Qì@  @hQì@g®@g
+>@fff@eÂ@e
+ž@dzá@c×
+@c33@b\@aë
+@aG®@`£×@`  @_\)@^žR@^{@]p€@\ÌÍ@\(ö@[
+@ZáH@Z=q@Y@XõÂ@XQì@W®@W
+>@Vff@UÂ@U
+ž@Tzá@S×
+@S33@R\@Që
+@QG®@P£×@P  @O\)@NžR@N{@Mp€@LÌÍ@L(ö@K
+@JáH@J=q@I@HõÃ@HQì@G®@G
+=@Fff@EÂ@E
+ž@Dzá@C×
+@C33@B\@Aë
+@AG®@@£×@@  @(Qì@'®@'
+>@&ff@%Â@%
+ž@$zá@#×
+@#33@"\@!ë
+@!G®@ £×@   @\)@
+žR@
+{@
+p€@
+ÌÍ@
+(ö@
+@áH@=q@@õÂ@Qì@®@
+>@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@£×@  @\)@žR@{@
+p€@
+ÌÍ@
+(ö@
+
+@
+áH@
+=q@	@õÃ@Qì@®@
+=@ff@Â@
+ž@zá@×
+@33@\@ë
+@G®@ £×@   ?Ð£×?Ï\)?Î{?ÌÌÍ?Ë
+
+?Ê=p?ÈõÂ?Ç®?Æff?Å
+ž?Ã×
+?Â\?ÁG®?À  ?ŸžR?œp€?Œ(ö?ºáH?¹?žQì?·
+>?µÂ?Žzá?³33?±ë
+?°£×?¯\)?®{?¬ÌÍ?«
+?ª=q?šõÂ?§®?Šff?¥
+ž?£×
+?¢\?¡G®?   ?žR?p€?(ö?áH??Qì?
+>?Â?zá?33?ë
+?£×?\)?{?ÌÍ?
+?=q?õÃ?®?ff?
+
+ž?×
+?\?G®?  ?!G®?
+žR?
+(ö??
+=?zá?ë
+?\)?
+ÌÍ?
+=q?®?
+ž?\?   >úáH>õÂ>ð£×>ë
+>æff>áG®>Ü(ö>×
+=>Ñë
+>ÌÌÍ>Ç®>Â\>œp€>žQì>³33>®{>šõÃ>£×
+>žR>>zá>\)>=q>
+
+ž>  >uÂ>k
+>aG®>W
+=>LÌÍ>B\>8Qì>.{>#×
+>>\)>
+ž=õÂ=áG®=ÌÌÍ=žQì=£×
+=\)=uÂ=LÌÍ=#×
+<õÂ<£×
+<#×
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/sBiOut.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/sBiOut.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/sBiOut.fits	(revision 22322)
@@ -0,0 +1,1414 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; = ?ÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : < >ÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; =ÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : <ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ;ÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 :ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9ÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6ÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - /ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , .ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + -ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * ,ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) +ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( *ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' )ÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & (ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % 'ÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ &ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # %ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! #ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   "ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  !ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+  ÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ ÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+ÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+       ÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+       ÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+      ÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+     ÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+     ÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+    ÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+    ÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+   ÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+   ÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+  ÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ ÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ ÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ    ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ   ÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁÿÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÁÿÃÿÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ÿ¿ÿÂÿÄÿÆÿÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœÿŸÿÀÿÂÿÄÿÆÿÉÿÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹ÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % &ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - /ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 1 3 5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - 0 2 4 6 7ÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 2 4 7 9 :ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 1 3 5 7 9 < =ÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - 0 2 4 6 8 : < >ÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 2 4 7 9 ; = >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 1 3 5 7 9 < <ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - 0 2 4 6 8 : ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 2 4 7 9 :ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 1 3 5 7 8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - 0 2 4 6 7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 2 4 5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 1 3 4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - 0 2 2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , . 0 1ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + - / 0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) + - .ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿ§ÿ©ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( * , -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ÿªÿ¬ÿ®ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & ( + +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ­ÿ¯ÿ±ÿ³ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % ' ) *ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ & ( )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " $ & 'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! # % &ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ÿžÿºÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+  ! $ $ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿ»ÿœÿ¿ÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+   " #ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœÿŸÿÀÿÂÿÄÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+  ! !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÿÆÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+        
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÿÉÿËÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÌÿÎÿÐÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÿÏÿÑÿÓÿÕÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÿÒÿÔÿÖÿØÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÿ×ÿÚÿÜÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÿÚÿÜÿßÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÿÝÿßÿáÿãÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÿàÿâÿäÿæÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÿãÿåÿçÿéÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäÿæÿèÿêÿìÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÿèÿëÿíÿïÿñÿóÿõÿ÷ÿùÿüÿþ       	 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêÿëÿíÿïÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíÿîÿðÿòÿôÿ÷ÿùÿûÿýÿÿ      	 	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿñÿóÿõÿ÷ÿùÿüÿþ       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõÿ÷ÿùÿûÿýÿÿ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿùÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿüÿþ    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ   ÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ     ÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ       ÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ         ÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	ÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+ÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+  ÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+    ÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+      ÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+        ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+          ÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+            ÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+              ÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+ ÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    !ÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " #ÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ %ÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & 'ÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( )ÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * +ÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , -ÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . /ÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1ÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3ÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7ÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9ÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ;ÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < =           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ¢ÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿŠÿ¥ÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ§ÿ§ÿ§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿ©ÿšÿ©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ©ÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ­ÿ¬ÿ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ®ÿ®ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿ³ÿ²ÿ³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿ³ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿ·ÿ¶ÿ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿžÿžÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿºÿ¹ÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿºÿ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿœÿÿÿÿÿÿÿÿÿÿÿÿÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿŸÿœÿŸÿÿÿÿÿÿÿÿÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿ¿ÿ¿ÿ¿ÿÿÿÿÿÿÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿÁ      ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÿ         ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÆÿÅÿÄÿÃÿÿÿÿ          ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÉÿÈÿÇÿÆÿÿÿÿÿÿÿÿ            ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿËÿËÿÊÿÉÿÿÿÿÿÿÿÿÿÿÿÿ              ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                  ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿ×ÿÖÿÕÿÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÚÿÙÿØÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÜÿÜÿÛÿÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+  
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿàÿßÿßÿÞÿÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿãÿâÿáÿáÿßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿæÿåÿäÿãÿâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ         
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿèÿçÿæÿåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ           
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿëÿêÿéÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ             
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿîÿíÿíÿìÿëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ               
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿñÿðÿïÿïÿíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                 
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿôÿóÿòÿòÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                   
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿ÷ÿöÿõÿôÿóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                     
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿùÿøÿ÷ÿöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+                     
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿüÿûÿúÿùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	 	                 ÿÿÿþÿþÿýÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+  
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	 	                 ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	 	            ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ! ! !     
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	 	       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ " # " ! !     
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	 	   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ $ $ $ # " ! !     
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % & % $ $ # " ! !     
+ 
+ 
+ 
+                     
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ & ' & & % $ $ # " ! !     
+ 
+ 
+ 
+                  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ( ( ( ' & & % $ $ # " ! !     
+ 
+ 
+ 
+              ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) * ) ( ( ' & & % $ $ # " ! !     
+ 
+ 
+ 
+          ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ + + + * ) ( ( ' & & % $ $ # " ! !     
+ 
+ 
+ 
+      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ , - , + + * ) ( ( ' & & % $ $ # " ! !     
+ 
+ 
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - . - - , + + * ) ( ( ' & & % $ $ # " ! !     
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ / 0 / . - - , + + * ) ( ( ' & & % $ $ # " ! ! ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 0 1 0 0 / . - - , + + * ) ( ( ' & & % $ $ "ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2 2 2 1 0 0 / . - - , + + * ) ( ( ' & %ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3 4 3 2 2 1 0 0 / . - - , + + * ) (ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 5 4 4 3 2 2 1 0 0 / . - - , +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 6 7 6 5 4 4 3 2 2 1 0 0 / -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 7 8 7 7 6 5 4 4 3 2 2 0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9 9 9 8 7 7 6 5 4 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ : ; : 9 9 8 7 6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ < < < ; : 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ = > = <ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ > >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ   ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+    ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿ    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿ      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿ 
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿ 
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿ  
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿ  
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿ   
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿ   
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿ    
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿ    
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿ     
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿ     
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿ      
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿ      
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿ       
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿ       
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿ        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿ        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿ 
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿ 
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ   
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠ % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿš ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ© ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿª ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ« * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ® - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ° / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ± 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ² 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽ 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµ 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ· 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿž 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿº 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ» : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒ ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœ < : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸ = ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ > < : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀ ? = ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁ                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿþÿüÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿþÿüÿùÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿÿÿýÿûÿùÿ÷ÿõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      ÿþÿüÿúÿøÿöÿôÿòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ! !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ # "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ $ $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ & % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ' & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ * ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ + + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿ©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ . - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 0 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 1 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2 2 0 - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 4 3 1 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 4 2 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 7 6 4 2 0 - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 8 7 5 3 1 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ : 9 7 4 2 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ; : 8 6 4 2 0 - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ < < 9 7 5 3 1 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ > = ; 9 7 4 2 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ ? > < : 8 6 4 2 0 - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿ = < 9 7 5 3 1 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ : 9 7 4 2 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿÿÿÿ 7 6 4 2 0 - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 3 1 / - + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2 0 . , * ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ / - + ) ' % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ , + ( & $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) ( & $ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ & % # !  
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ $ "   
+ 
+       
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ !  
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+        
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 	      ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 	     ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿþÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ¢ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿÿÿýÿûÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ÿþÿüÿúÿøÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ¥ÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿüÿùÿ÷ÿõÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿùÿ÷ÿôÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿšÿŠÿ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿöÿôÿòÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ©ÿ§ÿ§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÿóÿñÿïÿíÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÿðÿîÿìÿêÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ¬ÿªÿ©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿ«ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìÿëÿèÿæÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ¯ÿ­ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéÿèÿæÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ®ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÿåÿãÿáÿßÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ²ÿ°ÿ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäÿâÿàÿÞÿÜÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿ³ÿ±ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿ¶ÿŽÿ³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÿÚÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØÿ×ÿÕÿÓÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿ¹ÿ·ÿ¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÿÔÿÒÿÐÿÎÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿºÿžÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÑÿÏÿÍÿËÿÉÿÆÿÄÿÂÿÀÿŸÿŒÿºÿ¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿ¿ÿœÿ»ÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÿÉÿÆÿÄÿÂÿÀÿŸÿœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿÆÿÄÿÂÿ¿ÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÿÃÿÁÿÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÿÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ? > = < ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	           = < ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþ ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿü 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿú 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿø 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿö 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿô 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿò / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿð - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿî + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿì ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿê ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿè % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿä !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâ  
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿà 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞ               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜ             
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚ           
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØ         
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖ       
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔ     
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒ   
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐ 
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊ         ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈ       ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆ     ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄ   ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ > >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ < = > =ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9 : ; < < <ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 6 7 8 9 9 : ; :ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3 4 5 6 7 7 8 9 9 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 0 2 2 3 4 4 5 6 7 7 8 7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - / 0 0 1 2 2 3 4 4 5 6 7 6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ + , - - . / 0 0 1 2 2 3 4 4 5 5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ( ) * + + , - - . / 0 0 1 2 2 3 4 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % & ' ( ( ) * + + , - - . / 0 0 1 2 2 2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ " $ $ % & & ' ( ( ) * + + , - - . / 0 0 1 0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ! ! " # $ $ % & & ' ( ( ) * + + , - - . / 0 /ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+     ! ! " # $ $ % & & ' ( ( ) * + + , - - . -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+ 
+ 
+     ! ! " # $ $ % & & ' ( ( ) * + + , - ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       
+ 
+ 
+ 
+     ! ! " # $ $ % & & ' ( ( ) * + + +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ           
+ 
+ 
+ 
+     ! ! " # $ $ % & & ' ( ( ) * )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ               
+ 
+ 
+ 
+     ! ! " # $ $ % & & ' ( ( (ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                   
+ 
+ 
+ 
+     ! ! " # $ $ % & & ' &ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+                     
+ 
+ 
+ 
+     ! ! " # $ $ % & %ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+     ! ! " # $ $ $ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    	 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+     ! ! " # "ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        	 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+     ! ! !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ             	 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ                  	 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+  
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+                     
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+                    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+                  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+                ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+              ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+            ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+          ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+  
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  	 	 	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ                ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ              ÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ            ÿÿÿÿÿÿÿÿÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ          ÿÿÿÿÿÃÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ         ÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿþÿÿ    ÿÿÿÁÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿüÿýÿþÿÿÿÿÿÿÿ¿ÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿùÿúÿûÿüÿÿÿÿÿÿÿÿÿŸÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿöÿ÷ÿ÷ÿøÿùÿÿÿÿÿÿÿÿÿÿÿÿÿœÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿóÿôÿôÿõÿ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿðÿñÿòÿòÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿíÿîÿïÿïÿñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿëÿìÿíÿîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿèÿéÿêÿëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿåÿæÿæÿçÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿãÿäÿæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿßÿàÿáÿáÿãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÜÿÝÿÞÿßÿàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÚÿÛÿÜÿÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿ×ÿØÿÙÿÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿËÿÌÿÍÿÎÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÉÿÊÿËÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÆÿÇÿÈÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÂÿÂÿÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿºÿ»ÿŒÿœÿŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ¶ÿ¶ÿ·ÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ¯ÿ°ÿ±ÿ±ÿ³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ®ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿ©ÿªÿ«ÿ¬ÿ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ§ÿšÿ©ÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; = ?ÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : < >ÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; =ÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : <ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ;ÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 :ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9ÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6ÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - /ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , .ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + -ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * ,ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) +ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( *ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' )ÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & (ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % 'ÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ &ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # %ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! #ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   "ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  !ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+  ÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ ÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+ÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+       ÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+       ÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+      ÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+     ÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+     ÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+    ÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+    ÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+   ÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+   ÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+  ÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ ÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ ÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ    ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ   ÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
Index: /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/sOut.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/sOut.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/imageops/verified/sOut.fits	(revision 22322)
@@ -0,0 +1,1450 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; = ?ÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : < >ÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; =ÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : <ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ;ÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 :ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9ÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6ÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - /ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , .ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + -ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * ,ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) +ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( *ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' )ÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & (ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % 'ÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ &ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # %ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! #ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   "ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  !ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+  ÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ ÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+ÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+       ÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+       ÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+      ÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+     ÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+     ÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+    ÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+    ÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+   ÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+   ÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+  ÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ ÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ ÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ    ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ   ÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ÿÂÿÂÿÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœÿÀÿÁÿÄÿÆÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÿ¿ÿ¿ÿÂÿÅÿÈÿÈÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÿœÿŸÿÁÿÃÿÆÿÇÿÊÿÌÿÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹ÿŒÿœÿ¿ÿÂÿÅÿÅÿÈÿËÿÌÿÎÿÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿºÿ»ÿŸÿÁÿÃÿÄÿÇÿÉÿÊÿÍÿÐÿÒÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÿ¹ÿºÿŒÿ¿ÿÂÿÃÿÅÿÈÿÉÿËÿÎÿÑÿÒÿÔÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµÿžÿžÿ»ÿŸÿÀÿÁÿÄÿÇÿÇÿÊÿÍÿÏÿÐÿÓÿÖÿÖÿÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ¶ÿ·ÿºÿŒÿ¿ÿÀÿÂÿÅÿÆÿÉÿËÿÎÿÏÿÑÿÔÿÕÿØÿÚÿÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²ÿµÿ¶ÿžÿ»ÿŸÿŸÿÁÿÄÿÄÿÇÿÊÿÍÿÍÿÐÿÓÿÓÿÖÿÙÿÚÿÜÿßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÿ³ÿŽÿ·ÿºÿŒÿœÿÀÿÂÿÃÿÆÿÈÿËÿÌÿÏÿÑÿÒÿÕÿ×ÿØÿÛÿÞÿàÿáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÿ²ÿ³ÿµÿžÿ»ÿŒÿŸÿÁÿÂÿÄÿÇÿÊÿÊÿÍÿÐÿÑÿÓÿÖÿ×ÿÙÿÜÿßÿàÿâÿåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿ±ÿ±ÿŽÿ·ÿ¹ÿºÿœÿÀÿÀÿÃÿÆÿÈÿÉÿÌÿÎÿÏÿÒÿÕÿÕÿØÿÛÿÝÿÞÿáÿäÿäÿçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ¯ÿ°ÿ³ÿµÿžÿ¹ÿ»ÿŸÿ¿ÿÂÿÄÿÇÿÈÿÊÿÍÿÎÿÐÿÓÿÔÿ×ÿÙÿÜÿÝÿßÿâÿãÿæÿèÿëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«ÿ®ÿ®ÿ±ÿŽÿ·ÿ·ÿºÿœÿœÿÀÿÃÿÆÿÆÿÉÿÌÿÌÿÏÿÒÿÒÿÕÿØÿÛÿÛÿÞÿáÿáÿäÿçÿêÿêÿíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿ¬ÿ­ÿ°ÿ²ÿµÿ¶ÿ¹ÿ»ÿŒÿ¿ÿÁÿÄÿÅÿÈÿÊÿËÿÎÿÐÿÑÿÔÿÖÿÙÿÚÿÝÿßÿàÿãÿåÿèÿéÿìÿîÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿ«ÿ¬ÿ®ÿ±ÿŽÿŽÿ·ÿºÿ»ÿœÿÀÿÃÿÃÿÆÿÉÿÊÿÌÿÏÿÐÿÒÿÕÿØÿØÿÛÿÞÿßÿáÿäÿçÿçÿêÿíÿîÿðÿóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ÿªÿªÿ­ÿ°ÿ²ÿ³ÿ¶ÿžÿ¹ÿŒÿ¿ÿÁÿÂÿÅÿÇÿÈÿËÿÎÿÎÿÑÿÔÿÖÿ×ÿÚÿÜÿÝÿàÿãÿåÿæÿéÿëÿìÿïÿòÿòÿõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥ÿšÿ©ÿ¬ÿ®ÿ±ÿ²ÿŽÿ·ÿžÿºÿœÿÀÿÁÿÃÿÆÿÇÿÉÿÌÿÍÿÐÿÒÿÕÿÖÿØÿÛÿÜÿÞÿáÿäÿåÿçÿêÿëÿíÿðÿñÿôÿöÿùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿ§ÿ§ÿªÿ­ÿ°ÿ°ÿ³ÿ¶ÿ¶ÿ¹ÿŒÿŸÿ¿ÿÂÿÅÿÅÿÈÿËÿËÿÎÿÑÿÔÿÔÿ×ÿÚÿÚÿÝÿàÿâÿãÿæÿéÿéÿìÿïÿïÿòÿõÿøÿøÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ÿ¥ÿŠÿ©ÿ«ÿ®ÿ¯ÿ²ÿŽÿµÿžÿºÿœÿŸÿÀÿÃÿÄÿÇÿÉÿÊÿÍÿÏÿÒÿÓÿÖÿØÿÙÿÜÿÞÿáÿâÿäÿçÿèÿëÿíÿîÿñÿóÿöÿ÷ÿúÿüÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿ¥ÿ§ÿªÿ­ÿ­ÿ°ÿ³ÿŽÿ¶ÿ¹ÿŒÿŒÿ¿ÿÂÿÂÿÅÿÈÿÉÿËÿÎÿÑÿÑÿÔÿ×ÿØÿÚÿÝÿàÿàÿãÿæÿæÿéÿìÿíÿïÿòÿõÿõÿøÿûÿüÿþ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ£ÿŠÿ©ÿ«ÿ¬ÿ¯ÿ±ÿ²ÿµÿžÿºÿ»ÿŸÿÀÿÁÿÄÿÆÿÇÿÊÿÍÿÏÿÐÿÓÿÕÿÖÿÙÿÜÿÞÿßÿâÿäÿåÿèÿêÿëÿîÿñÿóÿôÿ÷ÿùÿúÿý    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¢ÿ€ÿ§ÿªÿ«ÿ­ÿ°ÿ±ÿ³ÿ¶ÿ¹ÿºÿŒÿ¿ÿÀÿÂÿÅÿÆÿÈÿËÿÎÿÏÿÑÿÔÿÕÿ×ÿÚÿÝÿÞÿàÿãÿäÿæÿéÿêÿìÿïÿòÿóÿõÿøÿùÿûÿþ    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ£ÿŠÿšÿ©ÿ¬ÿ¯ÿ¯ÿ²ÿµÿ·ÿžÿ»ÿŸÿŸÿÁÿÄÿÄÿÇÿÊÿÌÿÍÿÐÿÓÿÓÿÖÿÙÿÛÿÜÿßÿâÿâÿåÿèÿèÿëÿîÿðÿñÿôÿ÷ÿ÷ÿúÿýÿÿ      	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ€ÿ§ÿšÿªÿ­ÿ®ÿ±ÿ³ÿ¶ÿ·ÿ¹ÿŒÿœÿÀÿÂÿÃÿÆÿÈÿËÿÌÿÎÿÑÿÒÿÕÿ×ÿÚÿÛÿÝÿàÿáÿäÿæÿçÿêÿìÿïÿðÿòÿõÿöÿùÿûÿþÿÿ     
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ£ÿŠÿŠÿ©ÿ¬ÿ¬ÿ¯ÿ²ÿµÿµÿžÿ»ÿ»ÿŸÿÁÿÂÿÄÿÇÿÊÿÊÿÍÿÐÿÐÿÓÿÖÿÙÿÙÿÜÿßÿßÿâÿåÿæÿèÿëÿîÿîÿñÿôÿôÿ÷ÿúÿýÿý      	 
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿ¥ÿšÿªÿ«ÿ®ÿ°ÿ³ÿŽÿ·ÿ¹ÿºÿœÿ¿ÿÀÿÃÿÆÿÈÿÉÿÌÿÎÿÏÿÒÿÔÿ×ÿØÿÛÿÝÿÞÿáÿãÿäÿçÿêÿìÿíÿðÿòÿóÿöÿøÿûÿüÿÿ     
+ 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ£ÿ£ÿŠÿ©ÿªÿ¬ÿ¯ÿ²ÿ²ÿµÿžÿ¹ÿ»ÿŸÿ¿ÿÁÿÄÿÇÿÈÿÊÿÍÿÎÿÐÿÓÿÖÿÖÿÙÿÜÿÝÿßÿâÿãÿåÿèÿëÿìÿîÿñÿòÿôÿ÷ÿúÿúÿý      	 	 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¢ÿ¥ÿ§ÿšÿ«ÿ®ÿ°ÿ±ÿŽÿ¶ÿ·ÿºÿœÿœÿÀÿÃÿÅÿÆÿÉÿÌÿÌÿÏÿÒÿÔÿÕÿØÿÚÿÛÿÞÿáÿáÿäÿçÿéÿêÿíÿðÿðÿóÿöÿøÿùÿüÿþÿÿ     
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ£ÿŠÿ§ÿ©ÿ¬ÿ¯ÿ°ÿ²ÿµÿ¶ÿžÿ»ÿŒÿ¿ÿÁÿÄÿÅÿÇÿÊÿËÿÎÿÐÿÓÿÔÿÖÿÙÿÚÿÜÿßÿàÿãÿåÿèÿéÿëÿîÿïÿòÿôÿ÷ÿøÿúÿýÿþ      	 
+ 
+      ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ¥ÿ¥ÿšÿ«ÿ­ÿ®ÿ±ÿŽÿŽÿ·ÿºÿºÿœÿÀÿÃÿÃÿÆÿÉÿÉÿÌÿÏÿÒÿÒÿÕÿØÿØÿÛÿÞÿÞÿáÿäÿçÿçÿêÿíÿíÿðÿóÿöÿöÿùÿüÿüÿÿ     
+ 
+        
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ€ÿ§ÿ©ÿ¬ÿ­ÿ¯ÿ²ÿ³ÿ¶ÿžÿ¹ÿŒÿŸÿÁÿÂÿÅÿÇÿÈÿËÿÍÿÐÿÑÿÔÿÖÿ×ÿÚÿÜÿÝÿàÿâÿåÿæÿéÿëÿìÿïÿñÿôÿõÿøÿúÿûÿþ      	 
+ 
+       
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿ¥ÿšÿ«ÿ«ÿ®ÿ±ÿ±ÿŽÿ·ÿžÿºÿœÿÀÿÀÿÃÿÆÿÇÿÉÿÌÿÏÿÏÿÒÿÕÿÖÿØÿÛÿÜÿÞÿáÿäÿäÿçÿêÿëÿíÿðÿóÿóÿöÿùÿúÿüÿÿ      
+        
+ 
+   #ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ€ÿ§ÿ©ÿªÿ­ÿ¯ÿ°ÿ³ÿµÿ¶ÿ¹ÿŒÿŸÿ¿ÿÂÿÄÿÅÿÈÿËÿÍÿÎÿÑÿÓÿÔÿ×ÿÚÿÚÿÝÿàÿâÿãÿæÿèÿéÿìÿïÿñÿòÿõÿ÷ÿøÿûÿþ       
+ 
+ 
+       
+  " $ %ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿšÿ©ÿ«ÿ®ÿ¯ÿ±ÿŽÿµÿ·ÿºÿœÿŸÿÀÿÃÿÄÿÆÿÉÿÌÿÍÿÏÿÒÿÓÿÕÿØÿÙÿÜÿÞÿáÿâÿäÿçÿèÿêÿíÿðÿñÿóÿöÿ÷ÿùÿüÿÿ       
+ 
+        
+   # $ & )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿŠÿ§ÿªÿ­ÿ­ÿ°ÿ³ÿ³ÿ¶ÿ¹ÿ»ÿŒÿ¿ÿÂÿÂÿÅÿÈÿÊÿËÿÎÿÑÿÑÿÔÿ×ÿ×ÿÚÿÝÿàÿàÿãÿæÿæÿéÿìÿîÿïÿòÿõÿõÿøÿûÿýÿþ     
+ 
+ 
+       
+  ! " % ( ( +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿŠÿšÿ«ÿ¬ÿ¯ÿ±ÿ²ÿµÿ·ÿºÿ»ÿœÿÀÿÁÿÄÿÆÿÉÿÊÿÌÿÏÿÐÿÓÿÕÿÖÿÙÿÛÿÞÿßÿâÿäÿåÿèÿêÿíÿîÿðÿóÿôÿ÷ÿùÿüÿýÿÿ     	 
+        
+   ! # & ' * , -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿ€ÿ§ÿªÿªÿ­ÿ°ÿ±ÿ³ÿ¶ÿ¹ÿ¹ÿŒÿ¿ÿ¿ÿÂÿÅÿÈÿÈÿËÿÎÿÎÿÑÿÔÿÕÿ×ÿÚÿÝÿÝÿàÿãÿäÿæÿéÿìÿìÿïÿòÿòÿõÿøÿûÿûÿþ      
+ 
+       
+   " % % ( + , . 1ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿŠÿšÿ©ÿ¬ÿ®ÿ¯ÿ²ÿµÿ·ÿžÿ»ÿœÿŸÿÁÿÃÿÆÿÇÿÊÿÌÿÍÿÐÿÒÿÓÿÖÿÙÿÛÿÜÿßÿáÿâÿåÿèÿêÿëÿîÿðÿñÿôÿöÿùÿúÿýÿÿ      	 
+        
+ 
+ ! # $ ' ) * - 0 2 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¡ÿ€ÿ§ÿšÿªÿ­ÿ®ÿ°ÿ³ÿ¶ÿ·ÿ¹ÿŒÿœÿ¿ÿÂÿÅÿÅÿÈÿËÿÌÿÎÿÑÿÒÿÔÿ×ÿÚÿÛÿÝÿàÿáÿãÿæÿéÿêÿìÿïÿðÿòÿõÿøÿøÿûÿþÿÿ     
+ 
+       
+ 
+  " # % ( ) + . 1 2 4 7ÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ£ÿ¥ÿŠÿ©ÿ¬ÿ¬ÿ¯ÿ²ÿŽÿµÿžÿ»ÿ»ÿŸÿÁÿÃÿÄÿÇÿÉÿÊÿÍÿÐÿÐÿÓÿÖÿØÿÙÿÜÿßÿßÿâÿåÿçÿèÿëÿîÿîÿñÿôÿöÿ÷ÿúÿüÿý      	 
+ 
+        
+   ! $ ' ' * - / 0 3 6 6 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿ¥ÿ§ÿªÿ«ÿ®ÿ°ÿ³ÿŽÿ¶ÿ¹ÿºÿœÿ¿ÿÂÿÃÿÅÿÈÿÉÿËÿÎÿÏÿÒÿÔÿ×ÿØÿÚÿÝÿÞÿáÿãÿæÿçÿéÿìÿíÿðÿòÿõÿöÿøÿûÿüÿþ     
+ 
+ 
+       
+    " % & ) + . / 1 4 5 8 : =ÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ£ÿ£ÿŠÿ©ÿ©ÿ¬ÿ¯ÿ²ÿ²ÿµÿžÿžÿ»ÿŸÿÁÿÁÿÄÿÇÿÇÿÊÿÍÿÍÿÐÿÓÿÖÿÖÿÙÿÜÿÜÿßÿâÿåÿåÿèÿëÿëÿîÿñÿôÿôÿ÷ÿúÿúÿý       	 	 
+        
+ 
+ ! $ $ ' * - - 0 3 3 6 9 < < ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¢ÿ¥ÿ§ÿšÿ«ÿ­ÿ°ÿ±ÿŽÿ¶ÿ·ÿºÿŒÿ¿ÿÀÿÃÿÅÿÆÿÉÿËÿÌÿÏÿÑÿÔÿÕÿØÿÚÿÛÿÞÿàÿãÿäÿçÿéÿêÿíÿïÿòÿóÿöÿøÿùÿüÿþÿÿ     
+ 
+       
+ 
+   " # & ( + , / 1 2 5 7 : ; >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ£ÿŠÿ§ÿ©ÿ¬ÿ¯ÿ¯ÿ²ÿµÿ¶ÿžÿ»ÿŸÿŸÿÁÿÄÿÅÿÇÿÊÿËÿÍÿÐÿÓÿÓÿÖÿÙÿÚÿÜÿßÿâÿâÿåÿèÿéÿëÿîÿñÿñÿôÿ÷ÿøÿúÿýÿþ      	 
+ 
+       
+ 
+ ! " $ ' * * - 0 1 3 6 9 9 <ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ¥ÿ¥ÿšÿ«ÿ­ÿ®ÿ±ÿ³ÿŽÿ·ÿºÿŒÿœÿÀÿÂÿÃÿÆÿÉÿÉÿÌÿÏÿÑÿÒÿÕÿ×ÿØÿÛÿÞÿàÿáÿäÿæÿçÿêÿíÿïÿðÿóÿõÿöÿùÿüÿüÿÿ     
+ 
+        
+     # & ( ) , . / 2 5 7 8 ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ£ÿ€ÿ§ÿ©ÿ¬ÿ­ÿ¯ÿ²ÿ³ÿµÿžÿ»ÿŒÿŸÿÁÿÂÿÄÿÇÿÈÿËÿÍÿÐÿÑÿÓÿÖÿ×ÿÙÿÜÿßÿàÿâÿåÿæÿèÿëÿîÿïÿñÿôÿõÿ÷ÿúÿûÿþ      	 
+ 
+        
+  " $ ' ( * - . 0 3 6 7 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ¢ÿ¥ÿšÿ«ÿ«ÿ®ÿ±ÿ±ÿŽÿ·ÿ¹ÿºÿœÿÀÿÀÿÃÿÆÿÆÿÉÿÌÿÏÿÏÿÒÿÕÿÕÿØÿÛÿÝÿÞÿáÿäÿäÿçÿêÿìÿíÿðÿóÿóÿöÿùÿùÿüÿÿ      
+        
+ 
+   # & & ) , , / 2 4 5 8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ€ÿŠÿ©ÿªÿ­ÿ¯ÿ°ÿ³ÿµÿžÿ¹ÿ»ÿŸÿ¿ÿÂÿÄÿÅÿÈÿÊÿÍÿÎÿÑÿÓÿÔÿ×ÿÙÿÜÿÝÿßÿâÿãÿæÿèÿëÿìÿîÿñÿòÿõÿ÷ÿøÿûÿý       
+ 
+        
+  ! $ % ( * + . 0 3 4 6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¥ÿšÿšÿ«ÿ®ÿ¯ÿ±ÿŽÿ·ÿ·ÿºÿœÿœÿÀÿÃÿÄÿÆÿÉÿÌÿÌÿÏÿÒÿÓÿÕÿØÿÛÿÛÿÞÿáÿáÿäÿçÿêÿêÿíÿðÿðÿóÿöÿ÷ÿùÿüÿÿÿÿ     
+         
+   # # & ) * , / 2 2 5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ€ÿŠÿ§ÿªÿ¬ÿ­ÿ°ÿ³ÿµÿ¶ÿ¹ÿ»ÿŒÿ¿ÿÁÿÂÿÅÿÈÿÊÿËÿÎÿÐÿÑÿÔÿ×ÿÙÿÚÿÝÿßÿàÿãÿåÿèÿéÿìÿîÿïÿòÿôÿõÿøÿûÿýÿþ     
+ 
+ 
+       
+  ! " % ' ( + . 0 1 4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ¥ÿŠÿšÿ«ÿ¬ÿ®ÿ±ÿŽÿµÿ·ÿºÿ»ÿœÿÀÿÁÿÃÿÆÿÉÿÊÿÌÿÏÿÐÿÒÿÕÿØÿÙÿÛÿÞÿßÿáÿäÿçÿçÿêÿíÿîÿðÿóÿôÿöÿùÿüÿýÿÿ     
+ 
+        
+   ! # & ' ) , / 0 2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ€ÿ§ÿªÿªÿ­ÿ°ÿ²ÿ³ÿ¶ÿ¹ÿ¹ÿŒÿ¿ÿ¿ÿÂÿÅÿÇÿÈÿËÿÎÿÎÿÑÿÔÿÖÿ×ÿÚÿÝÿÝÿàÿãÿåÿæÿéÿëÿìÿïÿòÿòÿõÿøÿúÿûÿþ     	 
+ 
+       
+ 
+  " % % ( + - . 1ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿ¥ÿšÿ©ÿ¬ÿ®ÿ±ÿ²ÿŽÿ·ÿžÿ»ÿœÿŸÿÁÿÃÿÆÿÇÿÉÿÌÿÍÿÐÿÒÿÕÿÖÿØÿÛÿÜÿßÿáÿäÿåÿçÿêÿëÿíÿðÿñÿôÿöÿùÿúÿüÿÿ      	 
+        
+ 
+   # $ ' ) , - /ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿ§ÿ§ÿªÿ­ÿ°ÿ°ÿ³ÿ¶ÿ¶ÿ¹ÿŒÿœÿ¿ÿÂÿÅÿÅÿÈÿËÿËÿÎÿÑÿÔÿÔÿ×ÿÚÿÚÿÝÿàÿãÿãÿæÿéÿéÿìÿïÿïÿòÿõÿøÿøÿûÿþÿþ     
+ 
+ 
+      
+ 
+  " " % ( + + .ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿ©ÿ«ÿ®ÿ¯ÿ²ÿŽÿµÿžÿºÿ»ÿŸÿÁÿÃÿÄÿÇÿÉÿÊÿÍÿÏÿÒÿÓÿÖÿØÿÙÿÜÿÞÿáÿâÿåÿçÿèÿëÿíÿîÿñÿóÿöÿ÷ÿúÿüÿý      	 
+ 
+        
+   ! $ & ) * -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿ­ÿ­ÿ°ÿ³ÿŽÿ¶ÿ¹ÿºÿŒÿ¿ÿÂÿÃÿÅÿÈÿÉÿËÿÎÿÑÿÑÿÔÿ×ÿØÿÚÿÝÿàÿàÿãÿæÿçÿéÿìÿíÿïÿòÿõÿõÿøÿûÿüÿþ     
+ 
+ 
+       
+    " % ( ( +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ¯ÿ±ÿ²ÿµÿžÿžÿ»ÿŸÿÀÿÁÿÄÿÇÿÇÿÊÿÍÿÏÿÐÿÓÿÕÿÖÿÙÿÜÿÞÿßÿâÿäÿåÿèÿëÿëÿîÿñÿóÿôÿ÷ÿùÿúÿý       	 
+        
+ 
+ ! $ & ' *ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ±ÿ³ÿ¶ÿ·ÿºÿŒÿ¿ÿÀÿÂÿÅÿÆÿÉÿËÿÎÿÏÿÑÿÔÿÕÿ×ÿÚÿÝÿÞÿàÿãÿäÿæÿéÿêÿíÿïÿòÿóÿõÿøÿùÿûÿþ      
+ 
+       
+ 
+   " % & (ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²ÿµÿµÿžÿ»ÿŸÿŸÿÁÿÄÿÄÿÇÿÊÿÍÿÍÿÐÿÓÿÓÿÖÿÙÿÛÿÜÿßÿâÿâÿåÿèÿèÿëÿîÿñÿñÿôÿ÷ÿ÷ÿúÿýÿÿ      	 
+ 
+        
+ ! $ $ 'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ·ÿ¹ÿŒÿœÿÀÿÂÿÃÿÆÿÈÿËÿÌÿÏÿÑÿÒÿÕÿ×ÿÚÿÛÿÝÿàÿáÿäÿæÿçÿêÿìÿïÿðÿóÿõÿöÿùÿûÿþÿÿ     
+ 
+        
+  " # &ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿ»ÿ»ÿŸÿÁÿÂÿÄÿÇÿÊÿÊÿÍÿÐÿÑÿÓÿÖÿÙÿÙÿÜÿßÿßÿâÿåÿæÿèÿëÿîÿîÿñÿôÿõÿ÷ÿúÿýÿý      	 
+ 
+        
+ ! ! $ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿœÿ¿ÿÀÿÃÿÆÿÈÿÉÿÌÿÎÿÏÿÒÿÕÿ×ÿØÿÛÿÝÿÞÿáÿãÿäÿçÿêÿìÿíÿðÿòÿóÿöÿùÿûÿüÿÿ      
+        
+    #ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸÿ¿ÿÁÿÄÿÇÿÈÿÊÿÍÿÎÿÐÿÓÿÖÿ×ÿÙÿÜÿÝÿßÿâÿãÿåÿèÿëÿìÿîÿñÿòÿôÿ÷ÿúÿûÿý       	 
+        
+  !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÃÿÅÿÆÿÉÿÌÿÌÿÏÿÒÿÔÿÕÿØÿÛÿÛÿÞÿáÿáÿäÿçÿéÿêÿíÿðÿðÿóÿöÿøÿùÿüÿÿÿÿ     
+ 
+       
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÿÅÿÇÿÊÿËÿÎÿÐÿÓÿÔÿÖÿÙÿÚÿÝÿßÿàÿãÿåÿèÿéÿëÿîÿïÿòÿôÿ÷ÿøÿúÿýÿþ     	 
+ 
+       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÿÉÿÉÿÌÿÏÿÒÿÒÿÕÿØÿØÿÛÿÞÿßÿáÿäÿçÿçÿêÿíÿíÿðÿóÿöÿöÿùÿüÿüÿÿ     
+ 
+        
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÿËÿÍÿÐÿÑÿÔÿÖÿ×ÿÚÿÜÿÝÿàÿãÿåÿæÿéÿëÿìÿïÿñÿôÿõÿøÿúÿûÿþ      	 
+ 
+       
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÏÿÏÿÒÿÕÿÖÿØÿÛÿÜÿÞÿáÿäÿåÿçÿêÿëÿíÿðÿóÿóÿöÿùÿúÿüÿÿ      	 
+       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÿÑÿÓÿÔÿ×ÿÚÿÚÿÝÿàÿâÿãÿæÿéÿéÿìÿïÿñÿòÿõÿ÷ÿøÿûÿþÿþ     
+ 
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÿÓÿÕÿØÿÙÿÜÿÞÿáÿâÿäÿçÿèÿëÿíÿðÿñÿóÿöÿ÷ÿùÿüÿý       
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÿ×ÿ×ÿÚÿÝÿàÿàÿãÿæÿæÿéÿìÿïÿïÿòÿõÿõÿøÿûÿûÿþ     
+ 
+ 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÿÙÿÛÿÞÿßÿâÿäÿåÿèÿêÿíÿîÿñÿóÿôÿ÷ÿùÿúÿýÿÿ     	 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÿÝÿÝÿàÿãÿäÿæÿéÿìÿìÿïÿòÿóÿõÿøÿùÿûÿþ      
+ 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÿßÿáÿâÿåÿèÿêÿëÿîÿðÿñÿôÿ÷ÿ÷ÿúÿýÿÿ      	 
+   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÿáÿãÿæÿéÿêÿìÿïÿðÿòÿõÿöÿùÿûÿþÿÿ     
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâÿåÿçÿèÿëÿîÿîÿñÿôÿôÿ÷ÿúÿýÿý      	 
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÿçÿéÿìÿíÿðÿòÿóÿöÿøÿûÿüÿÿ     
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿëÿëÿîÿñÿòÿôÿ÷ÿúÿúÿý      	 	 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêÿíÿïÿðÿóÿöÿøÿùÿüÿþÿÿ     
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîÿïÿñÿôÿ÷ÿøÿúÿýÿþ      	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿóÿõÿöÿùÿüÿüÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÿõÿ÷ÿúÿûÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöÿùÿùÿüÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿûÿý    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿÿÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÿÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÿÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿ ÿ¡ÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿ¢ÿ£ÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿ€ÿ¥ÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿŠÿ§ÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿšÿ©ÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿªÿ«ÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿ¬ÿ­ÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿ®ÿ¯ÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿ°ÿ±ÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿ²ÿ³ÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿŽÿµÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿ¶ÿ·ÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿžÿ¹ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿºÿ»ÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿŒÿœÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿŸÿ¿ÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿÀÿÁÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿÿÂÿÃÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ   ÿÄÿÅÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ     ÿÆÿÇÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ       ÿÈÿÉÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ         ÿÊÿËÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	ÿÌÿÍÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ÿÎÿÏÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+ÿÐÿÑÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+  ÿÒÿÓÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+    ÿÔÿÕÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+      ÿÖÿ×ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+        ÿØÿÙÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+          ÿÚÿÛÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+            ÿÜÿÝÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+              ÿÞÿßÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ÿàÿáÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+ ÿâÿãÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    !ÿäÿåÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " #ÿæÿçÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ %ÿèÿéÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & 'ÿêÿëÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( )ÿìÿíÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * +ÿîÿïÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , -ÿðÿñÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . /ÿòÿóÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1ÿôÿõÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3ÿöÿ÷ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5ÿøÿùÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7ÿúÿûÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9ÿüÿýÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ;ÿþÿÿ           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < =           	 
+ 
+ 
+ 
+               
+ 
+ 
+    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿ¡ÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿ¥ÿ£ÿ€ÿ£ÿ¢ÿ ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿ§ÿ§ÿŠÿ¥ÿŠÿ€ÿ£ÿ¢ÿ¢ÿ¡ÿ ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ«ÿ©ÿšÿ©ÿšÿŠÿ§ÿŠÿ€ÿ£ÿ€ÿ£ÿ¡ÿ¢ÿ¡ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿ­ÿ­ÿ¬ÿ«ÿªÿªÿ©ÿšÿšÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ£ÿ¢ÿ¡ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ±ÿ¯ÿ®ÿ¯ÿ®ÿ¬ÿ«ÿ¬ÿªÿ©ÿªÿ©ÿ§ÿŠÿ§ÿ¥ÿ€ÿ¥ÿ£ÿ¢ÿ£ÿ¢ÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ³ÿ±ÿ²ÿ±ÿ°ÿ°ÿ¯ÿ®ÿ¬ÿ­ÿ¬ÿ«ÿ«ÿªÿ©ÿ§ÿšÿ§ÿ¥ÿŠÿ¥ÿ€ÿ€ÿ£ÿ¢ÿ ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÿµÿµÿŽÿ³ÿŽÿ²ÿ±ÿ²ÿ°ÿ¯ÿ®ÿ¯ÿ­ÿ¬ÿ­ÿ«ÿªÿ©ÿ©ÿšÿ§ÿšÿŠÿ¥ÿŠÿ€ÿ£ÿ¢ÿ£ÿ¡ÿ ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿ¹ÿ·ÿ¶ÿ·ÿ¶ÿŽÿµÿŽÿ²ÿ³ÿ²ÿ±ÿ¯ÿ°ÿ¯ÿ­ÿ®ÿ­ÿ«ÿªÿ«ÿªÿšÿ©ÿšÿŠÿ§ÿŠÿ¥ÿ£ÿ€ÿ£ÿ¡ÿ¢ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÿ»ÿ»ÿºÿ¹ÿžÿžÿ·ÿ¶ÿ¶ÿµÿŽÿµÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ¯ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿªÿ©ÿšÿ©ÿ§ÿŠÿ¥ÿ¥ÿ€ÿ£ÿ£ÿ¢ÿ¡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸÿ¿ÿœÿŒÿœÿŒÿºÿ¹ÿºÿžÿ·ÿžÿ·ÿµÿ¶ÿµÿ³ÿ²ÿ³ÿ±ÿ°ÿ±ÿ°ÿ®ÿ­ÿ®ÿ¬ÿ«ÿ¬ÿ«ÿ©ÿªÿ©ÿ§ÿŠÿ§ÿ¥ÿ€ÿ¥ÿ€ÿ¢ÿ¡ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÿÁÿ¿ÿÀÿ¿ÿŸÿŸÿœÿŒÿºÿ»ÿºÿ¹ÿ¹ÿžÿ·ÿ·ÿ¶ÿµÿ³ÿŽÿ³ÿ²ÿ²ÿ±ÿ°ÿ®ÿ¯ÿ®ÿ­ÿ­ÿ¬ÿ«ÿ«ÿªÿ©ÿ§ÿšÿ§ÿŠÿŠÿ¥ÿ€ÿ¢ÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÿÅÿÃÿÂÿÁÿÂÿÀÿ¿ÿÀÿŸÿœÿŒÿœÿ»ÿºÿ»ÿ¹ÿžÿ¹ÿ·ÿ¶ÿµÿ¶ÿŽÿ³ÿŽÿ²ÿ±ÿ°ÿ±ÿ¯ÿ®ÿ¯ÿ­ÿ¬ÿ­ÿ«ÿªÿ©ÿªÿšÿ§ÿšÿŠÿ¥ÿ€ÿ¥ÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÿÇÿÅÿÆÿÅÿÄÿÂÿÃÿÂÿÀÿÁÿÀÿ¿ÿœÿŸÿœÿ»ÿŒÿ»ÿ¹ÿºÿ¹ÿžÿ¶ÿ·ÿ¶ÿŽÿµÿŽÿ³ÿ±ÿ²ÿ±ÿ¯ÿ°ÿ¯ÿ­ÿ®ÿ­ÿ¬ÿªÿ«ÿªÿšÿ©ÿšÿ§ÿ¥ÿŠÿ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÿÉÿÉÿÈÿÇÿÈÿÆÿÅÿÄÿÄÿÃÿÂÿÃÿÁÿÀÿ¿ÿ¿ÿŸÿœÿœÿŒÿ»ÿŒÿºÿ¹ÿžÿžÿ·ÿ¶ÿ·ÿµÿŽÿ³ÿ³ÿ²ÿ±ÿ±ÿ°ÿ¯ÿ°ÿ®ÿ­ÿ¬ÿ¬ÿ«ÿªÿ«ÿ©ÿšÿ§ÿ§ÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÿÍÿËÿÊÿËÿÊÿÈÿÉÿÈÿÆÿÅÿÆÿÅÿÃÿÄÿÃÿÁÿÀÿÁÿ¿ÿŸÿ¿ÿŸÿŒÿœÿŒÿºÿ¹ÿºÿ¹ÿ·ÿžÿ·ÿµÿŽÿµÿ³ÿ²ÿ³ÿ²ÿ°ÿ±ÿ°ÿ®ÿ­ÿ®ÿ­ÿ«ÿ¬ÿ«ÿ©ÿšÿ©ÿšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÿÏÿÏÿÎÿÍÿÌÿÌÿËÿÊÿÊÿÉÿÈÿÇÿÇÿÆÿÅÿÅÿÄÿÃÿÁÿÂÿÁÿÀÿÀÿ¿ÿŸÿŸÿœÿŒÿ»ÿ»ÿºÿ¹ÿ¹ÿžÿ·ÿµÿ¶ÿµÿŽÿŽÿ³ÿ²ÿ²ÿ±ÿ°ÿ¯ÿ¯ÿ®ÿ­ÿ­ÿ¬ÿ«ÿªÿªÿ©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÿÓÿÑÿÐÿÑÿÐÿÎÿÍÿÎÿÌÿËÿÌÿËÿÉÿÈÿÉÿÇÿÆÿÇÿÅÿÄÿÃÿÄÿÂÿÁÿÂÿÀÿ¿ÿÀÿ¿ÿœÿŒÿœÿ»ÿºÿ»ÿ¹ÿžÿ·ÿžÿ¶ÿµÿ¶ÿŽÿ³ÿŽÿ³ÿ±ÿ°ÿ±ÿ¯ÿ®ÿ¯ÿ®ÿ¬ÿ«ÿ¬ÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÿÕÿÓÿÔÿÓÿÒÿÒÿÑÿÐÿÎÿÏÿÎÿÍÿÍÿÌÿËÿÉÿÊÿÉÿÇÿÈÿÇÿÆÿÄÿÅÿÄÿÂÿÃÿÂÿÁÿÁÿÀÿ¿ÿœÿŸÿœÿ»ÿŒÿ»ÿºÿžÿ¹ÿžÿ¶ÿ·ÿ¶ÿµÿµÿŽÿ³ÿ±ÿ²ÿ±ÿ°ÿ°ÿ¯ÿ®ÿ¬ÿ­ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØÿ×ÿ×ÿÖÿÕÿÖÿÔÿÓÿÔÿÒÿÑÿÐÿÑÿÏÿÎÿÏÿÍÿÌÿËÿËÿÊÿÉÿÊÿÈÿÇÿÆÿÆÿÅÿÄÿÅÿÃÿÂÿÃÿÁÿÀÿ¿ÿ¿ÿŸÿœÿŸÿŒÿ»ÿºÿºÿ¹ÿžÿ¹ÿ·ÿ¶ÿ·ÿµÿŽÿ³ÿŽÿ²ÿ±ÿ²ÿ°ÿ¯ÿ®ÿ®ÿ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÿÛÿÙÿØÿÙÿØÿÖÿ×ÿÖÿÔÿÕÿÔÿÓÿÑÿÒÿÑÿÏÿÐÿÏÿÍÿÌÿÍÿÌÿÊÿËÿÊÿÈÿÇÿÈÿÇÿÅÿÆÿÅÿÃÿÄÿÃÿÁÿÀÿÁÿÀÿŸÿ¿ÿŸÿŒÿ»ÿŒÿ»ÿ¹ÿºÿ¹ÿ·ÿžÿ·ÿ¶ÿŽÿµÿŽÿ²ÿ³ÿ²ÿ°ÿ¯ÿ°ÿ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÿÝÿÝÿÜÿÛÿÚÿÚÿÙÿØÿØÿ×ÿÖÿ×ÿÕÿÔÿÓÿÓÿÒÿÑÿÑÿÐÿÏÿÎÿÎÿÍÿÌÿÌÿËÿÊÿÉÿÉÿÈÿÇÿÇÿÆÿÅÿÅÿÄÿÃÿÂÿÂÿÁÿÀÿÀÿ¿ÿŸÿœÿœÿŒÿ»ÿ»ÿºÿ¹ÿºÿžÿ·ÿ¶ÿ¶ÿµÿŽÿŽÿ³ÿ²ÿ±ÿ±ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÿáÿßÿÞÿßÿÞÿÜÿÛÿÜÿÚÿÙÿÚÿÙÿ×ÿØÿ×ÿÕÿÔÿÕÿÓÿÒÿÓÿÒÿÐÿÏÿÐÿÎÿÍÿÎÿÍÿËÿÊÿËÿÉÿÈÿÉÿÇÿÆÿÇÿÆÿÄÿÃÿÄÿÂÿÁÿÂÿÁÿ¿ÿŸÿ¿ÿœÿŒÿœÿŒÿºÿ»ÿºÿžÿ·ÿžÿ¶ÿµÿ¶ÿµÿ³ÿ²ÿ³ÿ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäÿãÿáÿâÿáÿàÿàÿßÿÞÿÜÿÝÿÜÿÛÿÛÿÚÿÙÿÙÿØÿ×ÿÕÿÖÿÕÿÔÿÔÿÓÿÒÿÐÿÑÿÐÿÏÿÏÿÎÿÍÿËÿÌÿËÿÉÿÊÿÉÿÈÿÈÿÇÿÆÿÄÿÅÿÄÿÃÿÃÿÂÿÁÿ¿ÿÀÿ¿ÿŸÿŸÿœÿŒÿŒÿ»ÿºÿžÿ¹ÿžÿ·ÿ·ÿ¶ÿµÿ³ÿŽÿ³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÿçÿåÿäÿãÿäÿâÿáÿâÿàÿßÿÞÿßÿÝÿÜÿÝÿÛÿÚÿÛÿÙÿØÿ×ÿØÿÖÿÕÿÖÿÔÿÓÿÒÿÓÿÑÿÐÿÑÿÏÿÎÿÍÿÍÿÌÿËÿÌÿÊÿÉÿÊÿÈÿÇÿÆÿÇÿÅÿÄÿÅÿÃÿÂÿÁÿÂÿÀÿ¿ÿÀÿŸÿœÿŸÿŒÿ»ÿºÿ»ÿ¹ÿžÿ¹ÿ·ÿ¶ÿµÿ¶ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêÿéÿçÿèÿçÿæÿäÿåÿäÿâÿãÿâÿáÿßÿàÿßÿÝÿÞÿÝÿÛÿÜÿÛÿÚÿØÿÙÿØÿÖÿ×ÿÖÿÕÿÓÿÔÿÓÿÑÿÒÿÑÿÏÿÎÿÏÿÎÿÌÿÍÿÌÿÊÿËÿÊÿÉÿÇÿÈÿÇÿÅÿÆÿÅÿÄÿÂÿÃÿÂÿÀÿÁÿÀÿŸÿ¿ÿŸÿœÿ»ÿŒÿ»ÿ¹ÿºÿ¹ÿžÿ¶ÿ·ÿ¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìÿëÿëÿêÿéÿêÿèÿçÿæÿæÿåÿäÿåÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÞÿÜÿÛÿÚÿÚÿÙÿØÿÙÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÐÿÐÿÏÿÎÿÎÿÍÿÌÿÍÿËÿÊÿÉÿÉÿÈÿÇÿÈÿÆÿÅÿÄÿÄÿÃÿÂÿÂÿÁÿÀÿÁÿ¿ÿŸÿœÿœÿŒÿ»ÿŒÿºÿ¹ÿžÿžÿ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîÿïÿíÿìÿíÿìÿêÿëÿêÿèÿçÿèÿçÿåÿæÿåÿãÿâÿãÿáÿàÿáÿàÿÞÿßÿÞÿÜÿÛÿÜÿÛÿÙÿÚÿÙÿ×ÿÖÿ×ÿÕÿÔÿÕÿÔÿÒÿÑÿÒÿÐÿÏÿÐÿÏÿÍÿÎÿÍÿËÿÊÿËÿÊÿÈÿÉÿÈÿÆÿÅÿÆÿÄÿÃÿÄÿÃÿÁÿÂÿÁÿ¿ÿŸÿ¿ÿŸÿŒÿœÿŒÿºÿ¹ÿºÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÿñÿïÿðÿïÿîÿîÿíÿìÿìÿëÿêÿéÿéÿèÿçÿçÿæÿåÿãÿäÿãÿâÿâÿáÿàÿàÿßÿÞÿÝÿÝÿÜÿÛÿÛÿÚÿÙÿ×ÿØÿ×ÿÖÿÖÿÕÿÔÿÒÿÓÿÒÿÑÿÑÿÐÿÏÿÏÿÎÿÍÿÌÿÌÿËÿÊÿÊÿÉÿÈÿÆÿÇÿÆÿÅÿÅÿÄÿÃÿÃÿÂÿÁÿÀÿÀÿ¿ÿŸÿŸÿœÿŒÿºÿ»ÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÿõÿóÿòÿñÿòÿðÿïÿðÿîÿíÿîÿíÿëÿêÿëÿéÿèÿéÿçÿæÿåÿæÿäÿãÿäÿâÿáÿâÿáÿßÿÞÿßÿÝÿÜÿÝÿÛÿÚÿÙÿÚÿØÿ×ÿØÿÖÿÕÿÔÿÕÿÓÿÒÿÓÿÑÿÐÿÑÿÐÿÎÿÍÿÎÿÌÿËÿÌÿÊÿÉÿÈÿÉÿÇÿÆÿÇÿÅÿÄÿÅÿÄÿÂÿÁÿÂÿÀÿ¿ÿÀÿŸÿœÿŒÿœÿ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿ÷ÿõÿöÿõÿôÿòÿóÿòÿðÿñÿðÿïÿïÿîÿíÿëÿìÿëÿéÿêÿéÿèÿæÿçÿæÿäÿåÿäÿãÿãÿâÿáÿßÿàÿßÿÝÿÞÿÝÿÜÿÚÿÛÿÚÿØÿÙÿØÿ×ÿÕÿÖÿÕÿÓÿÔÿÓÿÒÿÒÿÑÿÐÿÎÿÏÿÎÿÌÿÍÿÌÿËÿÉÿÊÿÉÿÇÿÈÿÇÿÆÿÆÿÅÿÄÿÂÿÃÿÂÿÀÿÁÿÀÿ¿ÿœÿŸÿœÿÿÿÿÿÿÿÿÿÿÿÿÿúÿùÿùÿøÿ÷ÿøÿöÿõÿôÿôÿóÿòÿóÿñÿðÿñÿïÿîÿíÿíÿìÿëÿìÿêÿéÿèÿèÿçÿæÿçÿåÿäÿåÿãÿâÿáÿáÿàÿßÿàÿÞÿÝÿÜÿÜÿÛÿÚÿÛÿÙÿØÿ×ÿ×ÿÖÿÕÿÖÿÔÿÓÿÔÿÒÿÑÿÐÿÐÿÏÿÎÿÏÿÍÿÌÿËÿËÿÊÿÉÿÊÿÈÿÇÿÈÿÆÿÅÿÄÿÄÿÃÿÂÿÃÿÁÿÀÿ¿ÿ¿ÿŸÿÿÿÿÿÿÿÿÿþÿýÿûÿúÿûÿúÿøÿùÿøÿöÿõÿöÿõÿóÿôÿóÿñÿòÿñÿïÿîÿïÿîÿìÿíÿìÿêÿéÿêÿéÿçÿèÿçÿåÿæÿåÿãÿâÿãÿâÿàÿáÿàÿÞÿÝÿÞÿÝÿÛÿÜÿÛÿÙÿØÿÙÿØÿÖÿ×ÿÖÿÔÿÕÿÔÿÒÿÑÿÒÿÑÿÏÿÐÿÏÿÍÿÌÿÍÿÌÿÊÿËÿÊÿÈÿÉÿÈÿÆÿÅÿÆÿÅÿÃÿÄÿÃÿÁÿÀÿÁÿÀÿÿÿÿ  ÿÿÿÿÿþÿýÿüÿüÿûÿúÿúÿùÿøÿ÷ÿ÷ÿöÿõÿõÿôÿóÿóÿòÿñÿðÿðÿïÿîÿîÿíÿìÿëÿëÿêÿéÿéÿèÿçÿçÿæÿåÿäÿäÿãÿâÿâÿáÿàÿßÿßÿÞÿÝÿÝÿÜÿÛÿÚÿÚÿÙÿØÿØÿ×ÿÖÿÖÿÕÿÔÿÓÿÓÿÒÿÑÿÑÿÐÿÏÿÎÿÎÿÍÿÌÿÌÿËÿÊÿÊÿÉÿÈÿÇÿÇÿÆÿÅÿÅÿÄÿÃÿÂÿÂÿÁÿÿÿÿ      ÿþÿýÿþÿüÿûÿüÿûÿùÿøÿùÿ÷ÿöÿ÷ÿõÿôÿõÿôÿòÿñÿòÿðÿïÿðÿïÿíÿìÿíÿëÿêÿëÿéÿèÿéÿèÿæÿåÿæÿäÿãÿäÿãÿáÿàÿáÿßÿÞÿßÿÞÿÜÿÛÿÜÿÚÿÙÿÚÿØÿ×ÿØÿ×ÿÕÿÔÿÕÿÓÿÒÿÓÿÒÿÐÿÏÿÐÿÎÿÍÿÎÿÌÿËÿÌÿËÿÉÿÈÿÉÿÇÿÆÿÇÿÆÿÄÿÃÿÿÿÿÿÿÿÿ      ÿþÿÿÿþÿýÿýÿüÿûÿùÿúÿùÿ÷ÿøÿ÷ÿöÿöÿõÿôÿòÿóÿòÿñÿñÿðÿïÿíÿîÿíÿëÿìÿëÿêÿêÿéÿèÿæÿçÿæÿåÿåÿäÿãÿáÿâÿáÿàÿàÿßÿÞÿÜÿÝÿÜÿÚÿÛÿÚÿÙÿÙÿØÿ×ÿÕÿÖÿÕÿÔÿÔÿÓÿÒÿÐÿÑÿÐÿÎÿÏÿÎÿÍÿÍÿÌÿËÿÉÿÊÿÉÿÈÿÈÿÇÿÿÿÿÿÿÿÿÿÿÿÿ        ÿÿÿþÿÿÿýÿüÿûÿûÿúÿùÿúÿøÿ÷ÿøÿöÿõÿôÿõÿóÿòÿóÿñÿðÿïÿïÿîÿíÿîÿìÿëÿìÿêÿéÿèÿéÿçÿæÿçÿåÿäÿãÿäÿâÿáÿâÿàÿßÿÞÿÞÿÝÿÜÿÝÿÛÿÚÿÛÿÙÿØÿ×ÿØÿÖÿÕÿÖÿÔÿÓÿÒÿÒÿÑÿÐÿÑÿÏÿÎÿÏÿÍÿÌÿËÿÌÿÊÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        ÿÿ  ÿÿÿýÿüÿýÿüÿúÿûÿúÿøÿùÿøÿ÷ÿõÿöÿõÿóÿôÿóÿñÿðÿñÿðÿîÿïÿîÿìÿíÿìÿëÿéÿêÿéÿçÿèÿçÿæÿäÿåÿäÿâÿãÿâÿàÿßÿàÿßÿÝÿÞÿÝÿÛÿÜÿÛÿÚÿØÿÙÿØÿÖÿ×ÿÖÿÔÿÓÿÔÿÓÿÑÿÒÿÑÿÏÿÐÿÏÿÎÿÌÿÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ            ÿÿÿþÿþÿýÿüÿüÿûÿúÿûÿùÿøÿ÷ÿ÷ÿöÿõÿõÿôÿóÿòÿòÿñÿðÿðÿïÿîÿïÿíÿìÿëÿëÿêÿéÿêÿèÿçÿæÿæÿåÿäÿäÿãÿâÿáÿáÿàÿßÿßÿÞÿÝÿÞÿÜÿÛÿÚÿÚÿÙÿØÿØÿ×ÿÖÿÕÿÕÿÔÿÓÿÓÿÒÿÑÿÒÿÐÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	            ÿÿ  ÿþÿýÿþÿýÿûÿüÿûÿùÿøÿùÿ÷ÿöÿ÷ÿöÿôÿóÿôÿòÿñÿòÿñÿïÿðÿïÿíÿìÿíÿìÿêÿëÿêÿèÿçÿèÿæÿåÿæÿåÿãÿâÿãÿáÿàÿáÿàÿÞÿßÿÞÿÜÿÛÿÜÿÚÿÙÿÚÿÙÿ×ÿÖÿ×ÿÕÿÔÿÕÿÔÿÒÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 	 	              ÿÿÿÿÿþÿýÿýÿüÿûÿùÿúÿùÿøÿøÿ÷ÿöÿôÿõÿôÿóÿóÿòÿñÿñÿðÿïÿîÿîÿíÿìÿìÿëÿêÿèÿéÿèÿçÿçÿæÿåÿãÿäÿãÿâÿâÿáÿàÿàÿßÿÞÿÜÿÝÿÜÿÛÿÛÿÚÿÙÿ×ÿØÿ×ÿÖÿÖÿÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 	              ÿÿÿþÿÿÿýÿüÿûÿüÿúÿùÿúÿøÿ÷ÿöÿ÷ÿõÿôÿõÿóÿòÿóÿòÿðÿïÿðÿîÿíÿîÿìÿëÿêÿëÿéÿèÿéÿçÿæÿåÿæÿäÿãÿäÿâÿáÿâÿàÿßÿÞÿßÿÝÿÜÿÝÿÛÿÚÿÙÿÚÿØÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 
+ 
+  	           ÿÿ  ÿÿÿþÿüÿýÿüÿúÿûÿúÿùÿ÷ÿøÿ÷ÿõÿöÿõÿôÿôÿóÿòÿðÿñÿðÿîÿïÿîÿíÿëÿìÿëÿéÿêÿéÿèÿæÿçÿæÿäÿåÿäÿâÿãÿâÿáÿßÿàÿßÿÝÿÞÿÝÿÜÿÚÿÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+  
+ 
+ 
+ 
+ 	  	            ÿÿÿþÿþÿýÿüÿýÿûÿúÿùÿùÿøÿ÷ÿøÿöÿõÿöÿôÿóÿòÿòÿñÿðÿñÿïÿîÿíÿíÿìÿëÿìÿêÿéÿèÿèÿçÿæÿæÿåÿäÿåÿãÿâÿáÿáÿàÿßÿàÿÞÿÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 
+ 
+ 
+ 	 
+ 	            ÿÿ  ÿÿÿýÿþÿýÿûÿúÿûÿúÿøÿùÿøÿöÿ÷ÿöÿôÿóÿôÿóÿñÿòÿñÿïÿîÿïÿîÿìÿíÿìÿêÿéÿêÿèÿçÿèÿçÿåÿæÿåÿãÿâÿãÿâÿàÿáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+ 
+ 
+ 
+ 
+ 	              ÿÿÿÿÿþÿýÿüÿüÿûÿúÿúÿùÿøÿøÿ÷ÿöÿõÿõÿôÿóÿóÿòÿñÿðÿðÿïÿîÿîÿíÿìÿêÿëÿêÿéÿéÿèÿçÿçÿæÿåÿäÿäÿãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        
+ 
+ 
+ 
+ 
+ 	 
+                ÿþÿýÿþÿüÿûÿüÿúÿùÿúÿùÿ÷ÿöÿ÷ÿõÿôÿõÿôÿòÿñÿòÿðÿïÿðÿîÿíÿìÿíÿëÿêÿëÿéÿèÿéÿèÿæÿåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ         
+  
+ 
+ 
+ 
+ 
+  	             ÿþÿÿÿþÿüÿýÿüÿûÿûÿúÿùÿ÷ÿøÿ÷ÿöÿöÿõÿôÿòÿóÿòÿðÿñÿðÿïÿíÿîÿíÿëÿìÿëÿêÿêÿéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ            
+ 
+ 
+ 
+ 
+ 
+ 	  	             ÿÿÿþÿÿÿýÿüÿýÿûÿúÿùÿúÿøÿ÷ÿøÿöÿõÿôÿôÿóÿòÿóÿñÿðÿïÿïÿîÿíÿîÿìÿëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ             
+  
+ 
+ 
+ 
+ 	 
+ 	           ÿÿ  ÿÿÿýÿþÿýÿüÿúÿûÿúÿøÿùÿøÿöÿõÿöÿõÿóÿôÿóÿñÿðÿñÿðÿîÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                
+ 
+ 
+ 
+ 
+ 
+ 	              ÿÿ  ÿþÿýÿüÿüÿûÿúÿúÿùÿøÿ÷ÿ÷ÿöÿõÿõÿôÿóÿòÿòÿñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                   
+ 
+ 
+ 
+ 	 
+                ÿþÿýÿþÿüÿûÿüÿûÿùÿøÿùÿ÷ÿöÿ÷ÿöÿôÿóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                     
+ 
+ 
+ 
+ 
+ 	 	             ÿþÿÿÿþÿýÿýÿüÿûÿùÿúÿùÿøÿøÿ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+  
+                   
+ 
+ 
+ 
+ 
+ 
+ 	              ÿÿÿþÿÿÿýÿüÿûÿüÿúÿùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 
+                   
+  
+ 
+ 
+ 
+ 
+  	           ÿÿ  ÿÿÿþÿüÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+  
+ 
+                     
+  
+ 
+ 
+ 
+ 	              ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ !     
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 	 
+ 	         ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ " ! !    
+ 
+ 
+ 
+ 
+                    
+ 
+ 
+ 
+ 
+ 
+ 	 	     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ # " # "      
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+ 
+ 
+ 
+  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % $ $ # "   !   
+  
+ 
+  
+                   
+  
+ 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ & % & $ # " " !   !  
+ 
+ 
+ 
+  
+                   
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ( & ' & $ # $ # ! " !  
+  
+ 
+ 
+ 
+                ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) ( ( ' & % % $ # # " !      
+ 
+ 
+ 
+ 
+            ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ * ) * ) ' & ' % $ % $ " ! "      
+ 
+ 
+ 
+         ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ , + + * ) ' ( ' & & % $ " # "   !     
+ 
+  
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - , - + * ) * ( ' ( & % $ $ # " # !   !  
+ 
+ 
+ 
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ / - . - , * + * ( ) ( & % & % # $ # ! " !   
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 0 / 0 . - , , + * * ) ( ' ' & % % $ # $ " !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2 0 1 0 . - . , + , + ) ( ) ' & ' & $ %ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3 2 2 1 0 . / . - - , + ) * ) ( ( 'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 4 3 4 2 1 0 1 / . / - , + , * )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 6 4 5 4 3 1 2 1 / 0 / . , -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 7 6 7 5 4 3 3 2 1 2 0 /ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9 7 8 7 5 4 5 4 2 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ : 9 9 8 7 6 6 5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ; : ; : 8 7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ = < < ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ > =ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ   ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+    ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿ    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿ      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿ 
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿÿ 
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿ  
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿÿ  
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿÿ   
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿÿ   
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿÿ    
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿÿ    
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿÿ     
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿÿ     
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿÿ      
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿÿ      
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿÿ       
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿÿ       
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿÿ        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿÿ        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿÿ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ ÿ 
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡ÿ 
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ÿ   
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ÿ¡   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ÿ¢ !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ÿ£ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠÿ€ # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ÿ¥ $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿšÿŠ % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ©ÿ§ & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿªÿš ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ«ÿ© ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ÿª ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ÿ« * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ®ÿ¬ + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ÿ­ , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ°ÿ® - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ±ÿ¯ . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ²ÿ° / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ÿ± 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽÿ² 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµÿ³ 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ÿŽ 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ·ÿµ 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿžÿ¶ 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ÿ· 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿºÿž 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ»ÿ¹ 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒÿº 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœÿ» : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸÿŒ ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ÿœ < : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀÿŸ = ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁÿ¿ > < : 8 6 4 2 0 . , * ( & $ "   
+ 
+        
+ 
+      ÿþÿüÿúÿøÿöÿôÿòÿðÿîÿìÿêÿèÿæÿäÿâÿàÿÞÿÜÿÚÿØÿÖÿÔÿÒÿÐÿÎÿÌÿÊÿÈÿÆÿÄÿÂÿÀ ? = ; 9 7 5 3 1 / - + ) ' % # !  
+        
+ 
+ 	    ÿÿÿýÿûÿùÿ÷ÿõÿóÿñÿïÿíÿëÿéÿçÿåÿãÿáÿßÿÝÿÛÿÙÿ×ÿÕÿÓÿÑÿÏÿÍÿËÿÉÿÇÿÅÿÃÿÁ                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿýÿûÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿÿÿüÿùÿùÿöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     ÿþÿûÿúÿ÷ÿõÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿÿÿüÿüÿùÿöÿõÿóÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	     ÿþÿýÿúÿøÿ÷ÿôÿñÿïÿîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+    ÿÿÿþÿüÿùÿøÿöÿóÿðÿïÿíÿêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 	 	     ÿýÿúÿúÿ÷ÿôÿòÿñÿîÿëÿëÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+    ÿÿÿüÿûÿøÿöÿóÿòÿðÿíÿìÿéÿçÿæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+ 	     ÿýÿýÿúÿ÷ÿôÿôÿñÿîÿîÿëÿèÿçÿåÿâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+    ÿÿÿþÿûÿùÿöÿõÿòÿðÿïÿìÿêÿéÿæÿãÿáÿàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 	     ÿÿÿýÿúÿ÷ÿ÷ÿôÿñÿðÿîÿëÿêÿèÿåÿâÿáÿßÿÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 
+     ÿþÿûÿùÿøÿõÿóÿòÿïÿìÿìÿéÿæÿäÿãÿàÿÝÿÝÿÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 	    ÿÿÿýÿúÿùÿ÷ÿôÿóÿñÿîÿíÿêÿèÿåÿäÿâÿßÿÞÿÛÿÙÿÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 
+ 
+    ÿþÿûÿûÿøÿõÿõÿòÿïÿïÿìÿéÿæÿæÿãÿàÿàÿÝÿÚÿ×ÿ×ÿÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+      ÿýÿüÿùÿ÷ÿöÿóÿñÿðÿíÿëÿèÿçÿäÿâÿáÿÞÿÜÿÙÿØÿÕÿÓÿÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+ 
+    ÿþÿþÿûÿøÿ÷ÿõÿòÿñÿïÿìÿéÿéÿæÿãÿâÿàÿÝÿÚÿÚÿ×ÿÔÿÓÿÑÿÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ        
+ 	     ÿÿÿüÿúÿùÿöÿóÿóÿðÿíÿëÿêÿçÿåÿäÿáÿÞÿÜÿÛÿØÿÖÿÕÿÒÿÏÿÏÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+       
+ 
+ 	     ÿþÿûÿúÿøÿõÿôÿñÿïÿìÿëÿéÿæÿåÿãÿàÿÝÿÜÿÚÿ×ÿÖÿÔÿÑÿÐÿÍÿËÿÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+        
+ 
+    ÿÿÿüÿüÿùÿöÿöÿóÿðÿíÿíÿêÿçÿçÿäÿáÿßÿÞÿÛÿØÿØÿÕÿÒÿÒÿÏÿÌÿÉÿÉÿÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+       
+ 
+ 	    ÿþÿýÿúÿøÿ÷ÿôÿòÿïÿîÿëÿéÿèÿåÿãÿàÿßÿÝÿÚÿÙÿÖÿÔÿÓÿÐÿÎÿËÿÊÿÇÿÅÿÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+       
+ 
+    ÿÿÿÿÿüÿùÿøÿöÿóÿðÿðÿíÿêÿéÿçÿäÿáÿáÿÞÿÛÿÛÿØÿÕÿÔÿÒÿÏÿÌÿÌÿÉÿÆÿÅÿÃÿÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ !  
+        
+ 	      ÿýÿûÿúÿ÷ÿôÿòÿñÿîÿìÿëÿèÿåÿãÿâÿßÿÝÿÜÿÙÿ×ÿÖÿÓÿÐÿÎÿÍÿÊÿÈÿÇÿÄÿÁÿ¿ÿŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ #    
+        
+     ÿÿÿüÿûÿùÿöÿóÿòÿðÿíÿìÿêÿçÿäÿãÿáÿÞÿÝÿÛÿØÿ×ÿÕÿÒÿÏÿÎÿÌÿÉÿÈÿÆÿÃÿÀÿ¿ÿœÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ $ ! ! 
+        
+ 
+ 	     ÿýÿýÿúÿ÷ÿõÿôÿñÿîÿîÿëÿèÿæÿåÿâÿßÿßÿÜÿÙÿÙÿÖÿÓÿÑÿÐÿÍÿÊÿÊÿÇÿÄÿÂÿÁÿŸÿ»ÿ»ÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ & # "  
+        
+ 
+    ÿÿÿþÿûÿùÿöÿõÿóÿðÿïÿìÿêÿçÿæÿäÿáÿàÿÝÿÛÿÚÿ×ÿÕÿÒÿÑÿÏÿÌÿËÿÈÿÆÿÃÿÂÿÀÿœÿŒÿ¹ÿ·ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ' $ $ ! 
+        
+ 
+ 	     ÿÿÿýÿúÿ÷ÿ÷ÿôÿñÿñÿîÿëÿèÿèÿåÿâÿâÿßÿÜÿÛÿÙÿÖÿÓÿÓÿÐÿÍÿÍÿÊÿÇÿÄÿÄÿÁÿŸÿŸÿ»ÿžÿµÿµÿ²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ( & % "   
+ 
+       
+ 
+     ÿþÿûÿùÿøÿõÿóÿòÿïÿíÿêÿéÿæÿäÿãÿàÿÞÿÝÿÚÿ×ÿÕÿÔÿÑÿÏÿÎÿËÿÉÿÆÿÅÿÂÿÀÿ¿ÿŒÿºÿ·ÿ¶ÿ³ÿ±ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ * ' & $ ! 
+ 
+        
+ 	      ÿýÿúÿùÿ÷ÿôÿóÿñÿîÿëÿëÿèÿåÿäÿâÿßÿÞÿÜÿÙÿÖÿÕÿÓÿÐÿÏÿÍÿÊÿÇÿÇÿÄÿÁÿÀÿŸÿ»ÿžÿžÿµÿ²ÿ±ÿ¯ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ + ( ( % "    
+       
+ 
+ 
+    ÿþÿüÿûÿøÿõÿõÿòÿïÿíÿìÿéÿçÿæÿãÿàÿàÿÝÿÚÿØÿ×ÿÔÿÑÿÑÿÎÿËÿÉÿÈÿÅÿÃÿÂÿ¿ÿŒÿºÿ¹ÿ¶ÿŽÿ³ÿ°ÿ­ÿ­ÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - * ) & $ !   
+        
+ 
+ 	     ÿýÿüÿúÿ÷ÿöÿóÿñÿîÿíÿëÿèÿçÿåÿâÿáÿÞÿÜÿÙÿØÿÖÿÓÿÒÿÏÿÍÿÊÿÉÿÇÿÄÿÃÿÁÿŸÿ»ÿºÿžÿµÿŽÿ²ÿ¯ÿ®ÿ«ÿ©ÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ . + + ( % " "  
+ 
+      
+ 
+ 
+    ÿþÿþÿûÿøÿøÿõÿòÿïÿïÿìÿéÿéÿæÿãÿãÿàÿÝÿÚÿÚÿ×ÿÔÿÔÿÑÿÎÿËÿËÿÈÿÅÿÅÿÂÿ¿ÿœÿŒÿ¹ÿ¶ÿ¶ÿ³ÿ°ÿ°ÿ­ÿªÿ§ÿ§ÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ / - , ) ' $ #   
+ 
+        
+ 	     ÿÿÿüÿúÿùÿöÿôÿñÿðÿíÿëÿêÿçÿåÿäÿáÿßÿÜÿÛÿØÿÖÿÕÿÒÿÐÿÍÿÌÿÉÿÇÿÆÿÃÿÁÿŸÿœÿ»ÿžÿ·ÿŽÿ²ÿ±ÿ®ÿ¬ÿ©ÿšÿ¥ÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 1 . - + ( % % "  
+ 
+       
+ 
+ 	    ÿþÿûÿúÿøÿõÿòÿòÿïÿìÿëÿéÿæÿåÿãÿàÿÝÿÝÿÚÿ×ÿÖÿÔÿÑÿÎÿÎÿËÿÈÿÇÿÅÿÂÿ¿ÿ¿ÿŒÿ¹ÿ¹ÿ¶ÿ³ÿ²ÿ°ÿ­ÿªÿªÿ§ÿ€ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2 0 / , ) ' & # !   
+        
+ 
+    ÿÿÿýÿüÿùÿöÿôÿóÿðÿîÿíÿêÿçÿçÿäÿáÿßÿÞÿÛÿÙÿØÿÕÿÒÿÐÿÏÿÌÿÊÿÉÿÆÿÃÿÁÿÀÿœÿ»ÿºÿ·ÿµÿŽÿ±ÿ®ÿ¬ÿ«ÿšÿŠÿ¥ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 4 1 0 . + ( ' % " !  
+       
+ 
+ 
+    ÿþÿýÿûÿøÿõÿôÿòÿïÿîÿìÿéÿèÿåÿãÿàÿßÿÝÿÚÿÙÿ×ÿÔÿÑÿÐÿÎÿËÿÊÿÈÿÅÿÂÿÁÿ¿ÿŒÿ»ÿ¹ÿ¶ÿµÿ³ÿ°ÿ­ÿ¬ÿªÿ§ÿŠÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 2 2 / , * ) & # #   
+         
+    ÿÿÿÿÿüÿùÿ÷ÿöÿóÿðÿðÿíÿêÿêÿçÿäÿáÿáÿÞÿÛÿÛÿØÿÕÿÓÿÒÿÏÿÌÿÌÿÉÿÆÿÄÿÃÿÀÿœÿœÿºÿ·ÿ·ÿŽÿ±ÿ¯ÿ®ÿ«ÿšÿšÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 6 4 3 0 . + * ( % $ !  
+        
+ 
+      ÿýÿûÿøÿ÷ÿõÿòÿñÿîÿìÿëÿèÿæÿãÿâÿßÿÝÿÜÿÙÿ×ÿÔÿÓÿÑÿÎÿÍÿÊÿÈÿÅÿÄÿÂÿ¿ÿŸÿ»ÿ¹ÿžÿµÿ³ÿ°ÿ¯ÿ­ÿªÿ©ÿŠÿ€ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 8 5 4 2 / , , ) & & #   
+ 
+        
+     ÿÿÿüÿùÿùÿöÿóÿóÿðÿíÿìÿêÿçÿäÿäÿáÿÞÿÝÿÛÿØÿÕÿÕÿÒÿÏÿÏÿÌÿÉÿÆÿÆÿÃÿÀÿÀÿœÿºÿ¹ÿ·ÿŽÿ±ÿ±ÿ®ÿ«ÿ«ÿšÿ¥ÿ¢ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9 7 6 3 0 . - * ( ' $ "  
+        
+ 
+ 	     ÿþÿûÿúÿ÷ÿõÿôÿñÿïÿîÿëÿèÿæÿåÿâÿàÿßÿÜÿÙÿ×ÿÖÿÓÿÑÿÐÿÍÿËÿÈÿÇÿÄÿÂÿÁÿŸÿŒÿ»ÿžÿµÿ³ÿ²ÿ¯ÿ­ÿ¬ÿ©ÿ§ÿ€ÿ£ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ; 8 7 5 2 / . , ) ( & #     
+        
+ 
+    ÿÿÿüÿüÿùÿöÿõÿóÿðÿïÿíÿêÿçÿæÿäÿáÿàÿÞÿÛÿØÿ×ÿÕÿÒÿÑÿÏÿÌÿÉÿÉÿÆÿÃÿÂÿÀÿœÿŒÿºÿ·ÿŽÿ³ÿ±ÿ®ÿ­ÿ«ÿšÿ¥ÿ¥ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ < 9 9 6 3 1 0 - * * ' $ " ! 
+ 
+       
+ 
+ 	     ÿþÿýÿúÿøÿ÷ÿôÿñÿñÿîÿëÿéÿèÿåÿâÿâÿßÿÜÿÚÿÙÿÖÿÓÿÓÿÐÿÍÿËÿÊÿÇÿÅÿÄÿÁÿŸÿŸÿ»ÿžÿ¶ÿµÿ²ÿ¯ÿ¯ÿ¬ÿ©ÿ§ÿŠÿ£ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ > ; : 7 5 2 1 / , + ( & # "   
+ 
+       
+ 
+    ÿÿÿþÿüÿùÿøÿöÿóÿòÿïÿíÿêÿéÿçÿäÿãÿàÿÞÿÛÿÚÿØÿÕÿÔÿÑÿÏÿÌÿËÿÉÿÆÿÅÿÃÿÀÿ¿ÿŒÿºÿ·ÿ¶ÿŽÿ±ÿ°ÿ­ÿ«ÿšÿ§ÿ¥ÿ¢ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ? < < 9 6 3 3 0 - - * ' $ $ ! 
+ 
+        
+ 	 	      ÿýÿúÿúÿ÷ÿôÿôÿñÿîÿëÿëÿèÿåÿåÿâÿßÿÜÿÜÿÙÿÖÿÖÿÓÿÐÿÍÿÍÿÊÿÇÿÇÿÄÿÁÿÁÿŸÿ»ÿžÿžÿµÿ²ÿ²ÿ¯ÿ¬ÿ©ÿ©ÿŠÿ£ÿ£ÿ ÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿ = : 8 5 4 1 / . + ) & % "    
+       
+ 
+ 
+    ÿþÿüÿûÿøÿöÿõÿòÿðÿíÿìÿéÿçÿæÿãÿáÿÞÿÝÿÚÿØÿ×ÿÔÿÒÿÏÿÎÿËÿÉÿÈÿÅÿÃÿÂÿ¿ÿœÿºÿ¹ÿ¶ÿŽÿ³ÿ°ÿ®ÿ«ÿªÿ§ÿ¥ÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9 6 6 3 0 / - * ' ' $ !   
+        
+ 
+ 	     ÿýÿüÿúÿ÷ÿöÿôÿñÿîÿîÿëÿèÿçÿåÿâÿßÿßÿÜÿÙÿØÿÖÿÓÿÐÿÐÿÍÿÊÿÉÿÇÿÄÿÃÿÁÿŸÿ»ÿ»ÿžÿµÿŽÿ²ÿ¯ÿ¬ÿ¬ÿ©ÿŠÿ¥ÿ£ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿ 7 4 2 1 . + ) ( % # "  
+ 
+       
+ 
+    ÿÿÿþÿûÿøÿøÿõÿòÿðÿïÿìÿêÿéÿæÿãÿáÿàÿÝÿÛÿÚÿ×ÿÔÿÒÿÑÿÎÿÌÿËÿÈÿÅÿÅÿÂÿ¿ÿœÿŒÿ¹ÿ·ÿ¶ÿ³ÿ°ÿ®ÿ­ÿªÿšÿ§ÿ€ÿ¡ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3 2 0 - * ) ' $ # ! 
+ 
+        
+ 	     ÿÿÿýÿúÿùÿöÿôÿñÿðÿîÿëÿêÿèÿåÿâÿáÿßÿÜÿÛÿÙÿÖÿÓÿÒÿÐÿÍÿÌÿÊÿÇÿÆÿÃÿÁÿŸÿœÿ»ÿžÿ·ÿµÿ²ÿ¯ÿ®ÿ¬ÿ©ÿšÿŠÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 1 . , + ( % % "   
+       
+ 
+     ÿþÿûÿûÿøÿõÿòÿòÿïÿìÿìÿéÿæÿäÿãÿàÿÝÿÝÿÚÿ×ÿÕÿÔÿÑÿÎÿÎÿËÿÈÿÈÿÅÿÂÿ¿ÿ¿ÿŒÿ¹ÿ¹ÿ¶ÿ³ÿ±ÿ°ÿ­ÿªÿªÿ§ÿ€ÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - , * ' & # !   
+        
+ 	    ÿÿÿýÿüÿùÿ÷ÿôÿóÿðÿîÿíÿêÿèÿåÿäÿâÿßÿÞÿÛÿÙÿÖÿÕÿÓÿÐÿÏÿÌÿÊÿÉÿÆÿÄÿÁÿÀÿœÿ»ÿºÿ·ÿµÿ²ÿ±ÿ¯ÿ¬ÿ«ÿšÿŠÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ + ( ( % " !  
+       
+ 
+ 
+    ÿþÿýÿûÿøÿõÿõÿòÿïÿîÿìÿéÿæÿæÿãÿàÿàÿÝÿÚÿ×ÿ×ÿÔÿÑÿÑÿÎÿËÿÊÿÈÿÅÿÂÿÂÿ¿ÿŒÿ»ÿ¹ÿ¶ÿ³ÿ³ÿ°ÿ­ÿ­ÿªÿ§ÿŠÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) & $ #   
+        
+ 
+      ÿÿÿüÿùÿ÷ÿöÿóÿñÿðÿíÿêÿèÿçÿäÿâÿáÿÞÿÜÿÙÿØÿÕÿÓÿÒÿÏÿÍÿÌÿÉÿÆÿÄÿÃÿÀÿŸÿœÿºÿ·ÿµÿŽÿ±ÿ¯ÿ®ÿ«ÿ©ÿšÿ¥ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % $ "  
+       
+ 
+ 
+      ÿþÿûÿøÿ÷ÿõÿòÿñÿïÿìÿéÿèÿæÿãÿâÿàÿÝÿÚÿÚÿ×ÿÔÿÓÿÑÿÎÿÍÿËÿÈÿÅÿÄÿÂÿ¿ÿŸÿŒÿ¹ÿ¶ÿµÿ³ÿ°ÿ¯ÿ­ÿªÿ©ÿ§ÿ€ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ #   
+ 
+        
+     ÿÿÿüÿúÿùÿöÿóÿóÿðÿíÿëÿêÿçÿäÿäÿáÿÞÿÜÿÛÿØÿÖÿÕÿÒÿÏÿÏÿÌÿÉÿÇÿÆÿÃÿÀÿÀÿœÿºÿžÿ·ÿŽÿ±ÿ±ÿ®ÿ«ÿ«ÿšÿ¥ÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+       
+ 
+ 	     ÿþÿûÿúÿøÿõÿôÿñÿïÿìÿëÿéÿæÿåÿâÿàÿÝÿÜÿÚÿ×ÿÖÿÔÿÑÿÐÿÍÿËÿÈÿÇÿÅÿÂÿÁÿŸÿŒÿ¹ÿžÿ¶ÿ³ÿ²ÿ¯ÿ­ÿ¬ÿ©ÿ§ÿ€ÿ£ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+        
+ 
+    ÿÿÿüÿüÿùÿöÿöÿóÿðÿíÿíÿêÿçÿçÿäÿáÿÞÿÞÿÛÿØÿØÿÕÿÒÿÒÿÏÿÌÿÉÿÉÿÆÿÃÿÃÿÀÿœÿºÿºÿ·ÿŽÿŽÿ±ÿ®ÿ­ÿ«ÿšÿ¥ÿ¥ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ       
+ 
+ 	     ÿþÿýÿúÿøÿ÷ÿôÿòÿïÿîÿëÿéÿèÿåÿãÿàÿßÿÜÿÚÿÙÿÖÿÔÿÓÿÐÿÎÿËÿÊÿÇÿÅÿÄÿÁÿ¿ÿŒÿ»ÿžÿ¶ÿµÿ²ÿ°ÿ¯ÿ¬ÿ©ÿ§ÿŠÿ£ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      
+ 
+    ÿÿÿþÿüÿùÿøÿöÿóÿðÿðÿíÿêÿéÿçÿäÿáÿáÿÞÿÛÿÚÿØÿÕÿÔÿÒÿÏÿÌÿÌÿÉÿÆÿÅÿÃÿÀÿœÿœÿºÿ·ÿ¶ÿŽÿ±ÿ°ÿ®ÿ«ÿšÿ§ÿ¥ÿ¢ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+ 	 	     ÿýÿúÿúÿ÷ÿôÿòÿñÿîÿìÿëÿèÿåÿãÿâÿßÿÝÿÜÿÙÿÖÿÖÿÓÿÐÿÎÿÍÿÊÿÈÿÇÿÄÿÁÿ¿ÿŸÿ»ÿ¹ÿžÿµÿ²ÿ²ÿ¯ÿ¬ÿªÿ©ÿŠÿ£ÿ£ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    
+ 
+    ÿÿÿüÿûÿøÿöÿóÿòÿðÿíÿìÿêÿçÿäÿãÿáÿÞÿÝÿÛÿØÿ×ÿÔÿÒÿÏÿÎÿÌÿÉÿÈÿÆÿÃÿÀÿ¿ÿœÿºÿ¹ÿ·ÿŽÿ³ÿ°ÿ®ÿ«ÿªÿšÿ¥ÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+ 	     ÿýÿýÿúÿ÷ÿôÿôÿñÿîÿîÿëÿèÿæÿåÿâÿßÿßÿÜÿÙÿÙÿÖÿÓÿÐÿÐÿÍÿÊÿÊÿÇÿÄÿÂÿÁÿŸÿ»ÿ»ÿžÿµÿµÿ²ÿ¯ÿ¬ÿ¬ÿ©ÿŠÿŠÿ£ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+    ÿÿÿþÿûÿùÿöÿõÿòÿðÿïÿìÿêÿçÿæÿäÿáÿàÿÝÿÛÿÚÿ×ÿÕÿÒÿÑÿÎÿÌÿËÿÈÿÆÿÃÿÂÿÀÿœÿŒÿ¹ÿ·ÿ¶ÿ³ÿ±ÿ®ÿ­ÿªÿšÿ§ÿ€ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 	     ÿÿÿýÿúÿ÷ÿ÷ÿôÿñÿðÿîÿëÿèÿèÿåÿâÿâÿßÿÜÿÛÿÙÿÖÿÓÿÓÿÐÿÍÿÌÿÊÿÇÿÄÿÄÿÁÿŸÿŸÿ»ÿžÿ·ÿµÿ²ÿ¯ÿ¯ÿ¬ÿ©ÿšÿŠÿ£ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿþÿûÿùÿøÿõÿóÿòÿïÿìÿêÿéÿæÿäÿãÿàÿÞÿÝÿÚÿ×ÿÕÿÔÿÑÿÏÿÎÿËÿÈÿÆÿÅÿÂÿÀÿ¿ÿŒÿºÿ¹ÿ¶ÿ³ÿ±ÿ°ÿ­ÿ«ÿªÿ§ÿ€ÿ¢ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ    ÿýÿúÿùÿ÷ÿôÿóÿñÿîÿëÿêÿèÿåÿäÿâÿßÿÞÿÜÿÙÿÖÿÕÿÓÿÐÿÏÿÍÿÊÿÇÿÆÿÄÿÁÿÀÿŸÿ»ÿºÿžÿµÿ²ÿ±ÿ¯ÿ¬ÿ«ÿ©ÿŠÿ£ÿ¢ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿþÿüÿûÿøÿõÿõÿòÿïÿíÿìÿéÿæÿæÿãÿàÿàÿÝÿÚÿØÿ×ÿÔÿÑÿÑÿÎÿËÿÉÿÈÿÅÿÂÿÂÿ¿ÿŒÿŒÿ¹ÿ¶ÿŽÿ³ÿ°ÿ­ÿ­ÿªÿ§ÿ¥ÿ€ÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿüÿúÿ÷ÿöÿóÿñÿîÿíÿëÿèÿçÿäÿâÿáÿÞÿÜÿÙÿØÿÖÿÓÿÒÿÏÿÍÿÊÿÉÿÇÿÄÿÃÿÀÿŸÿœÿºÿžÿµÿŽÿ²ÿ¯ÿ®ÿ«ÿ©ÿŠÿ¥ÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿøÿøÿõÿòÿïÿïÿìÿéÿéÿæÿãÿâÿàÿÝÿÚÿÚÿ×ÿÔÿÔÿÑÿÎÿËÿËÿÈÿÅÿÅÿÂÿ¿ÿŸÿŒÿ¹ÿ¶ÿ¶ÿ³ÿ°ÿ°ÿ­ÿªÿ§ÿ§ÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿöÿôÿñÿðÿíÿëÿêÿçÿåÿäÿáÿÞÿÜÿÛÿØÿÖÿÕÿÒÿÐÿÍÿÌÿÉÿÇÿÆÿÃÿÁÿÀÿœÿºÿžÿ·ÿŽÿ²ÿ±ÿ®ÿ¬ÿ©ÿšÿ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõÿòÿòÿïÿìÿëÿéÿæÿåÿãÿàÿÝÿÜÿÚÿ×ÿÖÿÔÿÑÿÎÿÎÿËÿÈÿÇÿÅÿÂÿÁÿ¿ÿŒÿ¹ÿžÿ¶ÿ³ÿ²ÿ°ÿ­ÿªÿªÿ§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÿðÿîÿíÿêÿçÿçÿäÿáÿßÿÞÿÛÿØÿØÿÕÿÒÿÐÿÏÿÌÿÊÿÉÿÆÿÃÿÃÿÀÿœÿ»ÿºÿ·ÿŽÿŽÿ±ÿ®ÿ¬ÿ«ÿšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿîÿìÿéÿèÿåÿãÿàÿßÿÝÿÚÿÙÿÖÿÔÿÑÿÐÿÎÿËÿÊÿÈÿÅÿÄÿÁÿ¿ÿŒÿ»ÿ¹ÿ¶ÿµÿ²ÿ°ÿ­ÿ¬ÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíÿêÿêÿçÿäÿáÿáÿÞÿÛÿÛÿØÿÕÿÒÿÒÿÏÿÌÿÌÿÉÿÆÿÆÿÃÿÀÿœÿœÿºÿ·ÿ·ÿŽÿ±ÿ®ÿ®ÿ«ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿèÿæÿãÿâÿßÿÝÿÜÿÙÿ×ÿÔÿÓÿÐÿÎÿÍÿÊÿÈÿÇÿÄÿÂÿ¿ÿŸÿ»ÿ¹ÿžÿµÿ³ÿ°ÿ¯ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÿäÿäÿáÿÞÿÝÿÛÿØÿÕÿÕÿÒÿÏÿÎÿÌÿÉÿÈÿÆÿÃÿÀÿÀÿœÿºÿ¹ÿ·ÿŽÿ±ÿ±ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÿâÿàÿßÿÜÿÙÿ×ÿÖÿÓÿÑÿÐÿÍÿÊÿÊÿÇÿÄÿÂÿÁÿŸÿŒÿ»ÿžÿµÿ³ÿ²ÿ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÿàÿÞÿÛÿØÿ×ÿÕÿÒÿÑÿÏÿÌÿËÿÈÿÆÿÃÿÂÿÀÿœÿŒÿºÿ·ÿŽÿ³ÿ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÿÜÿÚÿÙÿÖÿÓÿÓÿÐÿÍÿÍÿÊÿÇÿÄÿÄÿÁÿŸÿŸÿ»ÿžÿ¶ÿµÿ²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÿÚÿØÿÕÿÔÿÑÿÏÿÎÿËÿÉÿÆÿÅÿÂÿÀÿ¿ÿŒÿºÿ·ÿ¶ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÿÖÿÖÿÓÿÐÿÏÿÍÿÊÿÇÿÇÿÄÿÁÿÀÿŸÿ»ÿžÿžÿµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×ÿÔÿÒÿÑÿÎÿËÿÉÿÈÿÅÿÃÿÂÿ¿ÿŒÿºÿ¹ÿ¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÒÿÐÿÍÿÊÿÉÿÇÿÄÿÃÿÁÿŸÿ»ÿºÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÿÎÿÌÿËÿÈÿÅÿÅÿÂÿ¿ÿœÿŒÿ¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÿÌÿÊÿÇÿÆÿÃÿÁÿŸÿœÿ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÈÿÈÿÅÿÂÿ¿ÿ¿ÿŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÆÿÄÿÁÿÀÿœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÿÂÿÂÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÿÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ? > = < ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	           = < ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþ ; : 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿü 9 8 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿú 7 6 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿø 5 4 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿö 3 2 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿô 1 0 / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿò / . - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿð - , + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿî + * ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿì ) ( ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿê ' & % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿè % $ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæ # " !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿä !    
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâ  
+ 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿà 
+ 
+               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞ               
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜ             
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚ           
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØ         
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖ       
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔ     
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒ   
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐ 
+ 
+ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎ 
+ 
+ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌ 	          ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊ         ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈ       ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆ     ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄ   ÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÿÿþÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿýÿüÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿûÿúÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿùÿøÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ÷ÿöÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿõÿôÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿóÿòÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿñÿðÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿïÿîÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿíÿìÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿëÿêÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿéÿèÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿçÿæÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿåÿäÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿãÿâÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿáÿàÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿßÿÞÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÝÿÜÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÛÿÚÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÙÿØÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿ×ÿÖÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÕÿÔÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÓÿÒÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÑÿÐÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÿÎÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÿÌÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÊÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÈÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿÆÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÿÄÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÿÂÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÁÿÀÿ¿ÿŸÿœÿŒÿ»ÿºÿ¹ÿžÿ·ÿ¶ÿµÿŽÿ³ÿ²ÿ±ÿ°ÿ¯ÿ®ÿ­ÿ¬ÿ«ÿªÿ©ÿšÿ§ÿŠÿ¥ÿ€ÿ£ÿ¢ÿ¡ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   91 / length of data axis 1                          NAXIS2  =                   91 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ = >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ; < < =ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 7 8 : ; : ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 6 6 7 8 9 9 :ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3 2 4 5 4 5 7 8 7 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ / 0 2 1 2 3 3 4 5 7 6 7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ - , . / 0 / 1 2 1 3 4 5 4 6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ) * , + , - / . / 1 0 1 2 4 3 4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ' ( ( ) * ) + , - - . / . 0 1 2 2 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % $ & ' & ' ) ( ) + , + , . - . 0 1 0 2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ! " $ # $ % % & ' ' ( ) * * + , , - . 0 / 0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+   ! " ! # $ # % & % & ( ) ( * + * , - . - /ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  
+ 
+ 
+ 
+  !   ! # " # $ $ % & ( ' ( * ) * + - , -ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ     
+  
+ 
+     !   " # " $ % & & ' ( ' ) * + + ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ          
+ 
+ 
+ 
+      " ! " $ % $ % ' & ' ) * ) *ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ             
+ 
+ 
+ 
+ 
+      ! " # # $ % % & ' ( ( )ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                 
+ 
+ 
+ 
+  
+  ! " ! # $ # $ & ' & (ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+                   
+  
+ 
+ 
+ 
+  !   ! " " # $ & % &ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
+ 
+ 
+ 
+  
+                   
+  
+ 
+  
+   !   " # $ $ %ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   
+ 
+ 
+ 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+      " # " #ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      	 	 
+ 
+ 
+ 
+ 
+ 
+                    
+ 
+ 
+ 
+ 
+    ! ! "ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ          	 
+ 	 
+ 
+ 
+ 
+                     
+ 
+ 
+ 
+     !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ               	 
+ 
+ 
+ 
+  
+                     
+ 
+  
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿüÿþÿÿ  ÿÿ            	  
+ 
+ 
+ 
+ 
+  
+                   
+ 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿúÿüÿûÿüÿýÿÿÿþÿÿ               	 
+ 
+ 
+ 
+ 
+ 
+                   
+  
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿøÿøÿùÿúÿùÿûÿüÿýÿýÿþÿÿÿþ              	 	 
+ 
+ 
+ 
+ 
+                    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÿôÿöÿ÷ÿöÿ÷ÿùÿøÿùÿûÿüÿûÿüÿþÿýÿþ                 
+ 	 
+ 
+ 
+ 
+                  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÿòÿòÿóÿôÿõÿõÿöÿ÷ÿ÷ÿøÿùÿúÿúÿûÿüÿüÿýÿþ  ÿÿ               	 
+ 
+ 
+ 
+ 
+ 
+               ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿîÿðÿñÿðÿñÿóÿôÿóÿõÿöÿõÿöÿøÿùÿøÿúÿûÿúÿüÿýÿþÿýÿÿ  ÿÿ            	 
+ 	 
+ 
+ 
+ 
+  
+            ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿìÿîÿíÿîÿïÿïÿðÿñÿóÿòÿóÿôÿôÿõÿöÿøÿ÷ÿøÿúÿùÿúÿûÿýÿüÿýÿÿÿþÿÿ              	  	 
+ 
+ 
+ 
+ 
+ 
+           ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéÿêÿêÿëÿìÿëÿíÿîÿíÿïÿðÿñÿðÿòÿóÿòÿôÿõÿöÿöÿ÷ÿøÿ÷ÿùÿúÿûÿûÿüÿýÿüÿþÿÿÿþ              	  
+ 
+ 
+ 
+ 
+  
+        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÿæÿèÿéÿèÿéÿëÿêÿëÿíÿìÿíÿîÿðÿïÿðÿòÿñÿòÿôÿõÿôÿõÿ÷ÿöÿ÷ÿùÿúÿùÿúÿüÿûÿüÿþÿýÿþ                 
+ 	 
+ 
+ 
+ 
+ 
+       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÿäÿäÿåÿæÿçÿçÿèÿéÿéÿêÿëÿêÿìÿíÿîÿîÿïÿðÿðÿñÿòÿóÿóÿôÿõÿõÿöÿ÷ÿøÿøÿùÿúÿúÿûÿüÿüÿýÿþÿÿÿÿ               	 
+ 
+ 
+ 
+ 
+ 
+     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÿàÿâÿãÿâÿãÿåÿæÿåÿçÿèÿçÿèÿêÿéÿêÿìÿíÿìÿîÿïÿîÿïÿñÿòÿñÿóÿôÿóÿôÿöÿ÷ÿöÿøÿùÿøÿúÿûÿúÿûÿýÿþÿýÿÿ  ÿÿ             	 
+ 	 
+ 
+ 
+ 
+    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝÿÞÿàÿßÿàÿáÿáÿâÿãÿåÿäÿåÿæÿæÿçÿèÿèÿéÿêÿìÿëÿìÿíÿíÿîÿïÿñÿðÿñÿòÿòÿóÿôÿöÿõÿöÿøÿ÷ÿøÿùÿùÿúÿûÿýÿüÿýÿþÿþÿÿ             	  	 
+ 
+ 
+ 
+  
+ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÿÚÿÜÿÝÿÞÿÝÿßÿàÿßÿáÿâÿãÿâÿäÿåÿäÿæÿçÿæÿèÿéÿêÿéÿëÿìÿëÿíÿîÿïÿîÿðÿñÿðÿòÿóÿôÿôÿõÿöÿõÿ÷ÿøÿ÷ÿùÿúÿûÿúÿüÿýÿüÿþÿÿ  ÿÿ            	  
+ 
+ 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×ÿØÿÚÿÙÿÚÿÛÿÝÿÜÿÝÿßÿÞÿßÿàÿâÿáÿâÿäÿãÿäÿæÿåÿæÿçÿéÿèÿéÿëÿêÿëÿìÿîÿíÿîÿðÿïÿðÿòÿóÿòÿóÿõÿôÿõÿ÷ÿöÿ÷ÿøÿúÿùÿúÿüÿûÿüÿýÿÿÿþÿÿ               	 
+ 
+ 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÿÖÿÖÿ×ÿØÿ×ÿÙÿÚÿÛÿÛÿÜÿÝÿÜÿÞÿßÿàÿàÿáÿâÿâÿãÿäÿãÿåÿæÿçÿçÿèÿéÿèÿêÿëÿìÿìÿíÿîÿîÿïÿðÿñÿñÿòÿóÿóÿôÿõÿôÿöÿ÷ÿøÿøÿùÿúÿùÿûÿüÿýÿýÿþÿÿÿÿ               	 	 
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÒÿÔÿÕÿÔÿÕÿ×ÿÖÿ×ÿÙÿÚÿÙÿÚÿÜÿÛÿÜÿÞÿßÿÞÿàÿáÿàÿáÿãÿâÿãÿåÿæÿåÿæÿèÿçÿèÿêÿëÿêÿìÿíÿìÿíÿïÿðÿïÿñÿòÿñÿòÿôÿóÿôÿöÿ÷ÿöÿ÷ÿùÿøÿùÿûÿüÿûÿýÿþÿýÿþ  ÿÿ             	ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÿÐÿÒÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿØÿØÿÙÿÚÿÚÿÛÿÜÿÞÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿäÿäÿåÿæÿæÿçÿèÿêÿéÿêÿëÿëÿìÿíÿïÿîÿïÿðÿðÿñÿòÿòÿóÿôÿõÿõÿöÿ÷ÿ÷ÿøÿùÿûÿúÿûÿüÿüÿýÿþÿþÿÿ            ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÿÌÿÎÿÏÿÐÿÏÿÑÿÒÿÑÿÓÿÔÿÓÿÔÿÖÿ×ÿÖÿØÿÙÿØÿÚÿÛÿÜÿÛÿÝÿÞÿÝÿßÿàÿßÿàÿâÿãÿâÿäÿåÿäÿæÿçÿèÿçÿéÿêÿéÿëÿìÿíÿìÿîÿïÿîÿðÿñÿðÿñÿóÿôÿóÿõÿöÿõÿ÷ÿøÿùÿøÿúÿûÿúÿüÿýÿüÿýÿÿ  ÿÿ        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÿÊÿÌÿËÿÌÿÍÿÏÿÎÿÏÿÑÿÐÿÑÿÒÿÒÿÓÿÔÿÖÿÕÿÖÿØÿ×ÿØÿÙÿÛÿÚÿÛÿÝÿÜÿÝÿÞÿÞÿßÿàÿâÿáÿâÿäÿãÿäÿåÿçÿæÿçÿéÿèÿéÿêÿìÿëÿìÿîÿíÿîÿïÿïÿðÿñÿóÿòÿóÿõÿôÿõÿöÿøÿ÷ÿøÿúÿùÿúÿûÿûÿüÿýÿÿÿþÿÿ        ÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿÈÿÈÿÉÿÊÿÉÿËÿÌÿÍÿÍÿÎÿÏÿÎÿÐÿÑÿÐÿÒÿÓÿÔÿÔÿÕÿÖÿÕÿ×ÿØÿÙÿÙÿÚÿÛÿÚÿÜÿÝÿÜÿÞÿßÿàÿàÿáÿâÿáÿãÿäÿåÿåÿæÿçÿæÿèÿéÿêÿêÿëÿìÿëÿíÿîÿíÿïÿðÿñÿñÿòÿóÿòÿôÿõÿöÿöÿ÷ÿøÿ÷ÿùÿúÿùÿûÿüÿýÿýÿþÿÿÿþ      ÿÿÿÿÿÿÿÿÿÃÿÄÿÆÿÇÿÆÿÇÿÉÿÈÿÉÿËÿÌÿËÿÌÿÎÿÍÿÎÿÐÿÏÿÐÿÒÿÓÿÒÿÓÿÕÿÔÿÕÿ×ÿØÿ×ÿØÿÚÿÙÿÚÿÜÿÛÿÜÿÞÿßÿÞÿßÿáÿàÿáÿãÿäÿãÿäÿæÿåÿæÿèÿéÿèÿéÿëÿêÿëÿíÿìÿíÿïÿðÿïÿðÿòÿñÿòÿôÿõÿôÿõÿ÷ÿöÿ÷ÿùÿøÿùÿûÿüÿûÿüÿþÿýÿþ      ÿÿÿÿÿÁÿÂÿÂÿÃÿÄÿÅÿÅÿÆÿÇÿÇÿÈÿÉÿÊÿÊÿËÿÌÿÌÿÍÿÎÿÎÿÏÿÐÿÑÿÑÿÒÿÓÿÓÿÔÿÕÿÖÿÖÿ×ÿØÿØÿÙÿÚÿÚÿÛÿÜÿÝÿÝÿÞÿßÿßÿàÿáÿâÿâÿãÿäÿäÿåÿæÿçÿçÿèÿéÿéÿêÿëÿëÿìÿíÿîÿîÿïÿðÿðÿñÿòÿóÿóÿôÿõÿõÿöÿ÷ÿ÷ÿøÿùÿúÿúÿûÿüÿüÿýÿþÿÿÿÿ  ÿÿÿÿÿÀÿÁÿÀÿÁÿÃÿÄÿÃÿÅÿÆÿÅÿÆÿÈÿÉÿÈÿÊÿËÿÊÿÌÿÍÿÌÿÍÿÏÿÐÿÏÿÑÿÒÿÑÿÒÿÔÿÕÿÔÿÖÿ×ÿÖÿØÿÙÿØÿÙÿÛÿÜÿÛÿÝÿÞÿÝÿÞÿàÿáÿàÿâÿãÿâÿãÿåÿæÿåÿçÿèÿçÿéÿêÿéÿêÿìÿíÿìÿîÿïÿîÿïÿñÿòÿñÿóÿôÿóÿõÿöÿõÿöÿøÿùÿøÿúÿûÿúÿûÿýÿþÿÿÿÿÿÿÿÿÿŸÿ¿ÿ¿ÿÀÿÁÿÃÿÂÿÃÿÄÿÄÿÅÿÆÿÈÿÇÿÈÿÊÿÉÿÊÿËÿËÿÌÿÍÿÏÿÎÿÏÿÐÿÐÿÑÿÒÿÔÿÓÿÔÿÖÿÕÿÖÿ×ÿ×ÿØÿÙÿÛÿÚÿÛÿÜÿÜÿÝÿÞÿàÿßÿàÿáÿáÿâÿãÿåÿäÿåÿçÿæÿçÿèÿèÿéÿêÿìÿëÿìÿíÿíÿîÿïÿñÿðÿñÿóÿòÿóÿôÿôÿõÿöÿøÿ÷ÿøÿùÿùÿúÿÿÿÿÿÿÿÿÿÿÿÿÿœÿŸÿœÿ¿ÿÀÿÁÿÀÿÂÿÃÿÂÿÄÿÅÿÆÿÆÿÇÿÈÿÇÿÉÿÊÿÉÿËÿÌÿÍÿÌÿÎÿÏÿÎÿÐÿÑÿÒÿÒÿÓÿÔÿÓÿÕÿÖÿÕÿ×ÿØÿÙÿØÿÚÿÛÿÚÿÜÿÝÿÞÿÝÿßÿàÿßÿáÿâÿãÿãÿäÿåÿäÿæÿçÿæÿèÿéÿêÿéÿëÿìÿëÿíÿîÿïÿïÿðÿñÿðÿòÿóÿòÿôÿõÿöÿõÿ÷ÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÿœÿŒÿœÿŸÿÀÿ¿ÿÀÿÂÿÁÿÂÿÄÿÅÿÄÿÅÿÇÿÆÿÇÿÉÿÈÿÉÿÊÿÌÿËÿÌÿÎÿÍÿÎÿÐÿÑÿÐÿÑÿÓÿÒÿÓÿÕÿÔÿÕÿÖÿØÿ×ÿØÿÚÿÙÿÚÿÛÿÝÿÜÿÝÿßÿÞÿßÿáÿâÿáÿâÿäÿãÿäÿæÿåÿæÿçÿéÿèÿéÿëÿêÿëÿíÿîÿíÿîÿðÿïÿðÿòÿñÿòÿóÿõÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿ»ÿºÿŒÿœÿŸÿŸÿ¿ÿÀÿÀÿÁÿÂÿÃÿÃÿÄÿÅÿÅÿÆÿÇÿÆÿÈÿÉÿÊÿÊÿËÿÌÿÌÿÍÿÎÿÏÿÏÿÐÿÑÿÑÿÒÿÓÿÒÿÔÿÕÿÖÿÖÿ×ÿØÿ×ÿÙÿÚÿÛÿÛÿÜÿÝÿÝÿÞÿßÿàÿàÿáÿâÿâÿãÿäÿãÿåÿæÿçÿçÿèÿéÿéÿêÿëÿìÿìÿíÿîÿîÿïÿðÿïÿñÿòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿºÿ¹ÿºÿŒÿœÿŒÿŸÿ¿ÿŸÿ¿ÿÁÿÂÿÁÿÃÿÄÿÃÿÄÿÆÿÅÿÆÿÈÿÉÿÈÿÊÿËÿÊÿËÿÍÿÎÿÍÿÏÿÐÿÏÿÐÿÒÿÑÿÒÿÔÿÕÿÔÿÕÿ×ÿÖÿ×ÿÙÿÚÿÙÿÛÿÜÿÛÿÜÿÞÿßÿÞÿàÿáÿàÿáÿãÿâÿãÿåÿæÿåÿçÿèÿçÿèÿêÿëÿêÿìÿíÿìÿíÿïÿîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ÿžÿžÿ¹ÿºÿŒÿ»ÿŒÿœÿœÿŸÿ¿ÿÁÿÀÿÁÿÂÿÂÿÃÿÄÿÄÿÅÿÆÿÈÿÇÿÈÿÉÿÉÿÊÿËÿÍÿÌÿÍÿÎÿÎÿÏÿÐÿÐÿÑÿÒÿÓÿÓÿÔÿÕÿÕÿÖÿ×ÿÙÿØÿÙÿÚÿÚÿÛÿÜÿÞÿÝÿÞÿßÿßÿàÿáÿáÿâÿãÿåÿäÿåÿæÿæÿçÿèÿêÿéÿêÿëÿëÿìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÿ·ÿ¶ÿžÿ¹ÿºÿ¹ÿ»ÿŒÿ»ÿœÿŸÿ¿ÿŸÿÀÿÁÿÀÿÂÿÃÿÂÿÄÿÅÿÆÿÅÿÇÿÈÿÇÿÉÿÊÿËÿÊÿÌÿÍÿÌÿÎÿÏÿÎÿÏÿÑÿÒÿÑÿÓÿÔÿÓÿÕÿÖÿ×ÿÖÿØÿÙÿØÿÚÿÛÿÜÿÛÿÝÿÞÿÝÿßÿàÿßÿáÿâÿãÿâÿäÿåÿäÿæÿçÿèÿçÿéÿêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÿ¶ÿµÿ¶ÿ·ÿ¹ÿžÿ¹ÿ»ÿºÿ»ÿŒÿŸÿœÿŸÿÀÿ¿ÿÀÿÂÿÁÿÂÿÃÿÅÿÄÿÅÿÇÿÆÿÇÿÈÿÊÿÉÿÊÿÌÿËÿÌÿÍÿÍÿÎÿÏÿÑÿÐÿÑÿÓÿÒÿÓÿÔÿÖÿÕÿÖÿØÿ×ÿØÿÙÿÛÿÚÿÛÿÝÿÜÿÝÿßÿÞÿßÿàÿâÿáÿâÿäÿãÿäÿåÿçÿæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÿŽÿ³ÿµÿ¶ÿ·ÿ·ÿžÿ¹ÿžÿºÿ»ÿŒÿŒÿœÿŸÿŸÿ¿ÿÀÿ¿ÿÁÿÂÿÃÿÃÿÄÿÅÿÄÿÆÿÇÿÈÿÈÿÉÿÊÿÉÿËÿÌÿËÿÍÿÎÿÏÿÏÿÐÿÑÿÐÿÒÿÓÿÔÿÔÿÕÿÖÿÕÿ×ÿØÿÙÿÙÿÚÿÛÿÛÿÜÿÝÿÜÿÞÿßÿàÿàÿáÿâÿáÿãÿäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÿ³ÿ²ÿ³ÿµÿ¶ÿµÿ¶ÿžÿ·ÿžÿºÿ»ÿºÿŒÿœÿŒÿœÿ¿ÿŸÿ¿ÿÁÿÂÿÁÿÂÿÄÿÃÿÄÿÆÿÇÿÆÿÇÿÉÿÈÿÉÿËÿÊÿËÿÍÿÎÿÍÿÎÿÐÿÏÿÐÿÒÿÓÿÒÿÓÿÕÿÔÿÕÿ×ÿØÿ×ÿÙÿÚÿÙÿÚÿÜÿÛÿÜÿÞÿßÿÞÿßÿáÿàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿ±ÿ±ÿ²ÿ³ÿŽÿŽÿµÿ¶ÿ¶ÿ·ÿžÿºÿ¹ÿºÿ»ÿ»ÿŒÿœÿœÿŸÿ¿ÿÀÿÀÿÁÿÂÿÂÿÃÿÄÿÅÿÅÿÆÿÇÿÇÿÈÿÉÿÉÿÊÿËÿÌÿÌÿÍÿÎÿÎÿÏÿÐÿÑÿÑÿÒÿÓÿÓÿÔÿÕÿ×ÿÖÿ×ÿØÿØÿÙÿÚÿÚÿÛÿÜÿÝÿÝÿÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÿ°ÿ¯ÿ°ÿ²ÿ³ÿ²ÿŽÿµÿŽÿ¶ÿ·ÿžÿ·ÿ¹ÿºÿ¹ÿ»ÿŒÿ»ÿŒÿŸÿ¿ÿŸÿÀÿÁÿÀÿÁÿÃÿÄÿÃÿÅÿÆÿÅÿÇÿÈÿÇÿÈÿÊÿËÿÊÿÌÿÍÿÌÿÍÿÏÿÐÿÏÿÑÿÒÿÑÿÓÿÔÿÕÿÔÿÖÿ×ÿÖÿØÿÙÿØÿÙÿÛÿÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­ÿ®ÿ®ÿ¯ÿ°ÿ²ÿ±ÿ²ÿŽÿ³ÿŽÿµÿ·ÿ¶ÿ·ÿ¹ÿžÿ¹ÿºÿºÿ»ÿŒÿŸÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÃÿÂÿÃÿÅÿÄÿÅÿÆÿÆÿÇÿÈÿÊÿÉÿÊÿËÿËÿÌÿÍÿÏÿÎÿÏÿÑÿÐÿÑÿÒÿÔÿÓÿÔÿÖÿÕÿÖÿ×ÿ×ÿØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÿ­ÿ¬ÿ®ÿ¯ÿ°ÿ°ÿ±ÿ²ÿ±ÿ³ÿŽÿµÿµÿ¶ÿ·ÿ¶ÿžÿ¹ÿžÿºÿ»ÿŒÿ»ÿœÿŸÿœÿ¿ÿÀÿÁÿÁÿÂÿÃÿÂÿÄÿÅÿÄÿÆÿÇÿÈÿÇÿÉÿÊÿÉÿËÿÌÿÍÿÍÿÎÿÏÿÎÿÐÿÑÿÒÿÒÿÓÿÔÿÓÿÕÿÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿ¬ÿ«ÿ¬ÿ®ÿ¯ÿ®ÿ¯ÿ±ÿ°ÿ±ÿ³ÿŽÿ³ÿŽÿ¶ÿµÿ¶ÿžÿ·ÿžÿ¹ÿ»ÿºÿ»ÿœÿŒÿœÿ¿ÿÀÿ¿ÿÀÿÂÿÁÿÂÿÄÿÃÿÄÿÅÿÇÿÆÿÇÿÉÿÈÿÉÿËÿÌÿËÿÌÿÎÿÍÿÎÿÐÿÑÿÐÿÑÿÓÿÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ÿªÿªÿ«ÿ¬ÿ­ÿ­ÿ®ÿ¯ÿ¯ÿ°ÿ±ÿ²ÿ²ÿ³ÿŽÿŽÿµÿ¶ÿµÿ·ÿžÿ¹ÿ¹ÿºÿ»ÿ»ÿŒÿœÿŸÿŸÿ¿ÿÀÿÀÿÁÿÂÿÁÿÃÿÄÿÅÿÅÿÆÿÇÿÇÿÈÿÉÿÊÿÊÿËÿÌÿÌÿÍÿÎÿÏÿÏÿÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿ©ÿšÿ©ÿ«ÿ¬ÿ«ÿ­ÿ®ÿ­ÿ®ÿ°ÿ±ÿ°ÿ²ÿ³ÿ²ÿ³ÿµÿŽÿµÿ·ÿžÿ·ÿ¹ÿºÿ¹ÿºÿŒÿœÿŒÿŸÿ¿ÿŸÿ¿ÿÁÿÀÿÁÿÃÿÄÿÃÿÅÿÆÿÅÿÆÿÈÿÉÿÈÿÊÿËÿÊÿËÿÍÿÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿ§ÿ§ÿšÿ©ÿ«ÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ°ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿ³ÿŽÿµÿ·ÿ¶ÿ·ÿžÿžÿ¹ÿºÿŒÿ»ÿŒÿœÿœÿŸÿ¿ÿ¿ÿÀÿÁÿÃÿÂÿÃÿÄÿÄÿÅÿÆÿÈÿÇÿÈÿÉÿÉÿÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥ÿŠÿ¥ÿ§ÿšÿ©ÿšÿªÿ«ÿªÿ¬ÿ­ÿ®ÿ­ÿ¯ÿ°ÿ¯ÿ±ÿ²ÿ±ÿ³ÿŽÿµÿŽÿ¶ÿ·ÿ¶ÿžÿ¹ÿºÿ¹ÿ»ÿŒÿ»ÿœÿŸÿœÿ¿ÿÀÿÁÿÀÿÂÿÃÿÂÿÄÿÅÿÆÿÅÿÇÿÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ÿ¥ÿ€ÿ¥ÿŠÿšÿ§ÿšÿªÿ©ÿªÿ«ÿ­ÿ¬ÿ­ÿ¯ÿ®ÿ¯ÿ±ÿ°ÿ±ÿ²ÿŽÿ³ÿŽÿ¶ÿµÿ¶ÿ·ÿ¹ÿžÿ¹ÿ»ÿºÿ»ÿœÿŒÿœÿŸÿÀÿ¿ÿÀÿÂÿÁÿÂÿÃÿÅÿÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ£ÿ¢ÿ€ÿ¥ÿŠÿŠÿ§ÿšÿ§ÿ©ÿªÿ«ÿ«ÿ¬ÿ­ÿ­ÿ®ÿ¯ÿ®ÿ°ÿ±ÿ²ÿ²ÿ³ÿŽÿ³ÿµÿ¶ÿ·ÿ·ÿžÿ¹ÿ¹ÿºÿ»ÿºÿŒÿœÿŸÿŸÿ¿ÿÀÿ¿ÿÁÿÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ¡ÿ¢ÿ€ÿ¥ÿ€ÿ¥ÿ§ÿŠÿ§ÿ©ÿªÿ©ÿ«ÿ¬ÿ«ÿ¬ÿ®ÿ­ÿ®ÿ°ÿ±ÿ°ÿ±ÿ³ÿ²ÿ³ÿµÿ¶ÿµÿ·ÿžÿ·ÿžÿºÿ¹ÿºÿŒÿœÿŒÿœÿ¿ÿŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ¡ÿ¢ÿ£ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿ©ÿšÿ©ÿªÿªÿ«ÿ¬ÿ¬ÿ­ÿ®ÿ¯ÿ¯ÿ°ÿ±ÿ±ÿ²ÿ³ÿµÿŽÿµÿ¶ÿ¶ÿ·ÿžÿžÿ¹ÿºÿ»ÿ»ÿŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ¢ÿ¡ÿ£ÿ€ÿ£ÿ¥ÿŠÿ§ÿŠÿšÿ©ÿšÿªÿ«ÿªÿ«ÿ­ÿ®ÿ­ÿ¯ÿ°ÿ¯ÿ±ÿ²ÿ³ÿ²ÿŽÿµÿŽÿ¶ÿ·ÿ¶ÿ·ÿ¹ÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ ÿ¡ÿ£ÿ¢ÿ£ÿ€ÿŠÿ¥ÿŠÿšÿ§ÿšÿ©ÿ©ÿªÿ«ÿ­ÿ¬ÿ­ÿ¯ÿ®ÿ¯ÿ°ÿ²ÿ±ÿ²ÿŽÿ³ÿŽÿµÿµÿ¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ ÿ¢ÿ£ÿ€ÿ€ÿ¥ÿŠÿ¥ÿ§ÿšÿ§ÿ©ÿªÿ«ÿ«ÿ¬ÿ­ÿ¬ÿ®ÿ¯ÿ°ÿ°ÿ±ÿ²ÿ±ÿ³ÿŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿ¢ÿ£ÿ¢ÿ£ÿ¥ÿ€ÿ¥ÿ§ÿŠÿ§ÿ©ÿªÿ©ÿªÿ¬ÿ«ÿ¬ÿ®ÿ¯ÿ®ÿ¯ÿ±ÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ¡ÿ¢ÿ£ÿ£ÿ€ÿ¥ÿ¥ÿŠÿ§ÿšÿšÿ©ÿªÿªÿ«ÿ¬ÿ­ÿ­ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ¡ÿ¢ÿ¡ÿ£ÿ€ÿ£ÿ€ÿŠÿ§ÿŠÿšÿ©ÿšÿ©ÿ«ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ ÿ¡ÿ¢ÿ¢ÿ£ÿ€ÿŠÿ¥ÿŠÿ§ÿ§ÿšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡ÿ ÿ¢ÿ£ÿ€ÿ£ÿ¥ÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿ¡ÿ£ÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                   16 / number of bits per data pixel                  NAXIS   =                    3 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          NAXIS2  =                   64 / length of data axis 2                          NAXIS3  =                    1 / length of data axis 3                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BZERO   =   0.000000000000E+00 / Pixel Value Offset                             BSCALE  =   1.000000000000E+00 / Pixel Value Scale                              END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; = ?ÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : < >ÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ; =ÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 : <ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9 ;ÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8 :ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7 9ÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6 8ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5 7ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4 6ÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3 5ÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2 4ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1 3ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0 2ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - / 1ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , . 0ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + - /ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * , .ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) + -ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( * ,ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' ) +ÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & ( *ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % ' )ÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ & (ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # % 'ÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ &ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! # %ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   " $ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  ! #ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+   "ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+  !ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+  ÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ ÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ 
+ÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+        
+ÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+        
+ÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+       ÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+       ÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+      ÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+      ÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+     ÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+     ÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+    ÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+    ÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+   ÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+   ÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+  ÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+  ÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ ÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ ÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ 
+ÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ 
+ÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	 
+ÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ       
+ÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ     	ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ      ÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ    ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ    ÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ   ÿÿ
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿ£ÿ¥ÿ§ÿ©ÿ«ÿ­ÿ¯ÿ±ÿ³ÿµÿ·ÿ¹ÿ»ÿœÿ¿ÿÁÿÃÿÅÿÇÿÉÿËÿÍÿÏÿÑÿÓÿÕÿ×ÿÙÿÛÿÝÿßÿáÿãÿåÿçÿéÿëÿíÿïÿñÿóÿõÿ÷ÿùÿûÿýÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢ÿ€ÿŠÿšÿªÿ¬ÿ®ÿ°ÿ²ÿŽÿ¶ÿžÿºÿŒÿŸÿÀÿÂÿÄÿÆÿÈÿÊÿÌÿÎÿÐÿÒÿÔÿÖÿØÿÚÿÜÿÞÿàÿâÿäÿæÿèÿêÿìÿîÿðÿòÿôÿöÿøÿúÿüÿþ                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
Index: /tags/sj_tags/sj_root_20080929/psLib/test/jpeg/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/jpeg/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/jpeg/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+temp
+.deps
+.libs
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/jpeg/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/jpeg/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/jpeg/Makefile.am	(revision 22322)
@@ -0,0 +1,18 @@
+AM_CPPFLAGS = $(SRCINC) -I$(top_srcdir)/test/tap/src $(PSLIB_CFLAGS)
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpslib.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(PSLIB_LIBS)
+
+TEST_PROGS =
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/.cvsignore	(revision 22322)
@@ -0,0 +1,54 @@
+temp
+.deps
+.libs
+Makefile
+Makefile.in
+seed_msglog1.txt
+seed_msglog2.txt
+*.bb
+*.bbg
+*.da
+gmon.out
+
+tap_psHist00
+tap_psHist01
+tap_psHist02
+tap_psHist03
+tap_psMD5
+tap_psMatrix01
+tap_psMatrix02
+tap_psMatrix03
+tap_psMatrix04
+tap_psMatrix05
+tap_psMatrix06
+tap_psMatrix07
+tap_psPolyFit1D
+tap_psPolyFit2D
+tap_psPolyFit3D
+tap_psPolyFit4D
+tap_psPolynomial
+tap_psPolynomialEval1D
+tap_psPolynomialEval2D
+tap_psPolynomialEval3D
+tap_psPolynomialEval4D
+tap_psPolynomialUtils_Derivatives
+tap_psSparse
+tap_psStats00
+tap_psStats01
+tap_psStats02
+tap_psStats03
+tap_psStats05
+tap_psStats06
+tap_psStats07
+tap_psStats08
+tap_psStats09
+tap_psStatsTiming
+tap_psStats_Sample_01
+tap_psFunc01
+tap_psHistogram
+tap_psMatrixVectorArithmetic01
+tap_psMatrixVectorArithmetic04
+tap_psMinimizePowell
+tap_psRandom
+tap_psSpline1D
+tap_psPolynomialMD
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/Makefile.am	(revision 22322)
@@ -0,0 +1,64 @@
+
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSLIB_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpslib.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSLIB_LIBS)
+
+TEST_PROGS = \
+	tap_psHistogram \
+	tap_psMD5 \
+	tap_psMatrix01 \
+	tap_psMatrix02 \
+	tap_psMatrix03 \
+	tap_psMatrix04 \
+	tap_psMatrix05 \
+	tap_psMatrix06 \
+	tap_psMatrix07 \
+	tap_psPolyFit1D \
+	tap_psPolyFit2D \
+	tap_psPolyFit3D \
+	tap_psPolyFit4D \
+	tap_psPolynomial \
+	tap_psPolynomialEval1D \
+	tap_psPolynomialEval2D \
+	tap_psPolynomialEval3D \
+	tap_psPolynomialEval4D \
+	tap_psPolynomialUtils_Derivatives \
+	tap_psSparse \
+	tap_psStats00 \
+	tap_psStats01 \
+	tap_psStats02 \
+	tap_psStats03 \
+	tap_psStats05 \
+	tap_psStats06 \
+	tap_psStats07 \
+	tap_psStats08 \
+	tap_psStats09 \
+	tap_psStatsTiming \
+	tap_psFunc01 \
+	tap_psStats_Sample_01 \
+	tap_psMatrixVectorArithmetic01 \
+	tap_psMatrixVectorArithmetic04 \
+	tap_psRandom \
+	tap_psMinimizePowell \
+	tap_psSpline1D \
+	tap_psPolynomialMD
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
+	$(top_srcdir)/test/test.pl
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psFunc01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psFunc01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psFunc01.c	(revision 22322)
@@ -0,0 +1,57 @@
+/*****************************************************************************
+    This routine must ensure that the psGaussian() shall evaluate a
+    specified Gaussian at some X.
+ 
+    It also tests the p_psGaussianDev() procedure.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define MY_MEAN 5.0
+#define MY_STDEV 2.0
+#define TOLERANCE 0.01
+psF32 truth1[10] = {0.008764, 0.026995, 0.064759, 0.120985, 0.176033, 0.199471, 0.176033, 0.120985, 0.064759, 0.026995};
+psF32 truth2[10] = {0.043937, 0.135335, 0.324652, 0.606531, 0.882497, 1.000000, 0.882497, 0.606531, 0.324652, 0.135335};
+
+int main()
+{
+    psLogSetFormat("HLNM");
+    plan_tests(4);
+
+
+    // Test the psGaussian(): normalized version
+    {
+        psMemId id = psMemGetId();
+        bool errorFlag = false;
+        for (psS32 x = 0 ; x < (int) (MY_MEAN * 2.0) ; x++)
+        {
+            psF32 actual = psGaussian((psF32) x, MY_MEAN, MY_STDEV, true);
+            if (fabs(truth1[x] - actual) > TOLERANCE) {
+                diag("ERROR: the Gaussian at %.2f was %f, should be %f", (psF32) x, actual, truth1[x]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psGaussian() produced consistent results (normalized)");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test the psGaussian(): non-normalized version
+    {
+        psMemId id = psMemGetId();
+        bool errorFlag = false;
+        for (psS32 x = 0 ; x < (int) (MY_MEAN * 2.0) ; x++)
+        {
+            psF32 actual = psGaussian((psF32) x, MY_MEAN, MY_STDEV, false);
+            if (fabs(truth2[x] - actual) > TOLERANCE) {
+                diag("ERROR: the Gaussian at %.2f was %f, should be %f", (psF32) x, actual, truth1[x]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psGaussian() produced consistent results (non-normalized)");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psHistogram.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psHistogram.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psHistogram.c	(revision 22322)
@@ -0,0 +1,435 @@
+/*****************************************************************************
+We test:
+    psHistogramAlloc()
+    psHistogramAllocGeneric()
+    psVectorHistogram(): uniform histograms
+    psVectorHistogram(): generic histograms
+*****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define ERROR_TOLERANCE 0.001
+#define NUM_DATA 10000
+
+void genericTestHistogramAlloc(psS32 numBins, psS32 lower, psS32 higher)
+{
+    // Allocate the psHistogram structure
+    {
+        psMemId id = psMemGetId();
+        psHistogram *myHist = psHistogramAlloc(lower, higher, numBins);
+        ok(myHist != NULL, "psHistogram allocated successfully");
+        skip_start(myHist == NULL, 7, "Skipping tests because psHistogramAlloc() failed");
+        ok(myHist->nums->n = numBins, "psHistogramAlloc() set the correct number of bins");
+        ok(myHist->bounds->n == numBins+1, "psHistogramAlloc() set the correct number of bounds");
+        ok(myHist->minNum == 0, "psHistogramAlloc() initialized minNum to 0.0");
+        ok(myHist->maxNum == 0, "psHistogramAlloc() initialized maxNum to 0.0");
+        ok(myHist->uniform == true, "psHistogramAlloc() initialized ->uniform to true");
+
+        bool errorFlag = false;
+        for (int i=0;i<numBins;i++)
+        {
+            if (myHist->nums->data.F32[i] != 0.0) {
+                diag("ERROR: myHist->nums->data.U32[%d] not initialized to 0.\n", i);
+                errorFlag = true;
+            }
+            myHist->nums->data.F32[i] = 0.0;
+        }
+        ok(!errorFlag, "psHistogramAlloc() initialized the data to 0.0");
+
+        errorFlag = false;
+        for (int i=0;i<numBins;i++)
+        {
+            psF32 expectedBound = (psF32) (i + 1);
+            if (fabs(expectedBound - myHist->bounds->data.F32[i]) > ERROR_TOLERANCE) {
+                diag("Bin number %d bounds: (%6.3f - %6.3f)\n", i,
+                     myHist->bounds->data.F32[i],
+                     myHist->bounds->data.F32[i+1]);
+                diag("Was %f, expected %f\n", myHist->bounds->data.F32[i], expectedBound);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psHistogramAlloc() set the histogram bounds correctly");
+
+        skip_end();
+        psFree(myHist);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
+
+void genericTestHistogramAllocGeneric(psS32 numBins, psS32 lower, psS32 higher)
+{
+    {
+        psMemId id = psMemGetId();
+        psVector *myBounds = psVectorAlloc(numBins+1, PS_TYPE_F32);
+        for (int i=0;i<numBins+1;i++) {
+            myBounds->data.F32[i] = lower + ((higher - lower) / (float) numBins) *
+		(float) i;
+        }
+        psHistogram *myHist = psHistogramAllocGeneric(myBounds);
+        ok(myHist != NULL, "psHistogram allocated successfully");
+        skip_start(myHist == NULL, 7, "Skipping tests because psHistogramAlloc() failed");
+        ok(myHist->nums->n = numBins, "psHistogramAllocGeneric() set the correct number of bins");
+        ok(myHist->bounds->n == numBins+1, "psHistogramAllocGeneric() set the correct number of bounds");
+        ok(myHist->minNum == 0, "psHistogramAllocGeneric() initialized minNum to 0.0");
+        ok(myHist->maxNum == 0, "psHistogramAllocGeneric() initialized maxNum to 0.0");
+        ok(myHist->uniform == false, "psHistogramAllocGeneric() initialized ->uniform to false");
+
+        bool errorFlag = false;
+        for (int i=0;i<numBins;i++) {
+            if (myHist->nums->data.F32[i] != 0.0) {
+                diag("ERROR: myHist->nums->data.F32[%d] not initialized to 0.\n", i);
+                errorFlag = false;
+            }
+        }
+        ok(!errorFlag, "psHistogramAllocGeneric() initialized the data to 0.0");
+
+        errorFlag = false;
+        for (int i=0;i<numBins;i++) {
+            psF32 expectedBound = (psF32) (i + 1);
+            if (fabs(expectedBound - myHist->bounds->data.F32[i]) > ERROR_TOLERANCE) {
+                diag("Bin number %d bounds: (%6.3f - %6.3f)\n", i,
+                     myHist->bounds->data.F32[i],
+                     myHist->bounds->data.F32[i+1]);
+                diag("Was %f, expected %f\n", myHist->bounds->data.F32[i], expectedBound);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psHistogramAlloc() set the histogram bounds correctly");
+
+        skip_end();
+        psFree(myHist);
+        psFree(myBounds);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+
+    }
+}
+
+
+void genericTestVectorHistogram1(psS32 numBins, psS32 lower, psS32 higher)
+{
+    if ((numBins%2) != 0) {
+        diag("Configuration error: must run this test with even number of bins");
+        return;
+    }
+    if ((NUM_DATA%numBins) != 0) {
+        diag("Configuration error: NUM_DATA must be an integer multiple of numBins");
+        return;
+    }
+
+    // Allocate the psHistogram structure, ensure that psVectorHistogram() sets
+    // the data correctly when called with a NULL mask.
+    {
+        psMemId id = psMemGetId();
+        psVector *myData = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        for (int i = 0;i < NUM_DATA;i++) {
+            myData->data.F32[i] = lower + ((higher - lower) / (float) NUM_DATA) * (float) i;
+        }
+
+        psHistogram *myHist = psHistogramAlloc(lower, higher, numBins);
+        bool rc = psVectorHistogram(myHist, myData, NULL, NULL, 0);
+        ok(rc == true, "psVectorHistogram() returned TRUE");
+        skip_start(myHist == NULL, 1, "Skipping tests because psVectorHistogram() returned NULL");
+
+        bool errorFlag = false;
+        for (int i = 0; i < numBins; i++) {
+
+	    // we need to explicitly count the number in each bin.  we can fool ourselves with
+	    // rounding otherwise
+	    int nValues = 0;
+	    for (int j = 0; j < myData->n; j++) {
+		// valid bin: bound[bin] <= value < bound[bin+1]
+		if (myData->data.F32[j] < myHist->bounds->data.F32[i]) continue;
+		if (myData->data.F32[j] >= myHist->bounds->data.F32[i+1]) continue;
+		nValues ++;
+	    }
+	    if (myHist->nums->data.F32[i] != nValues) {
+		diag("Bin number %d bounds: (%.2f - %.2f) data (%.2f) should be (%2d)\n",
+		     i,
+		     myHist->bounds->data.F32[i],
+		     myHist->bounds->data.F32[i+1],
+		     myHist->nums->data.F32[i], nValues);
+		errorFlag = true;
+	    }
+        }
+        ok(!errorFlag, "psVectorHistogram() correctly set the histogram");
+        skip_end();
+        psFree(myHist);
+        psFree(myData);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate the psHistogram structure, ensure that psVectorHistogram() sets
+    // the data correctly when called with a mask.
+    {
+        psMemId id = psMemGetId();
+        psVector *myData = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        myData->n = myData->nalloc;
+        for (int i = 0;i < NUM_DATA;i++) {
+            myData->data.F32[i] = lower + ((higher - lower) / (float) NUM_DATA) * (float) i;
+        }
+
+        psVector *myMask = psVectorAlloc(NUM_DATA, PS_TYPE_U8);
+        myMask->n = myMask->nalloc;
+        for (int i = 0;i < NUM_DATA;i++) {
+            if (i >= (NUM_DATA / 2)) {
+                myMask->data.U8[i] = 1;
+            } else {
+                myMask->data.U8[i] = 0;
+            }
+        }
+        psHistogram *myHist = psHistogramAlloc(lower, higher, numBins);
+        bool rc = psVectorHistogram(myHist, myData, NULL, myMask, 1);
+        ok(rc == true, "psVectorHistogram() returned TRUE");
+        skip_start(myHist == NULL, 1, "Skipping tests because psVectorHistogram() returned NULL");
+
+        bool errorFlag = false;
+        for (int i = 0;i < numBins;i++) {
+
+	    // we need to explicitly count the number in each bin.  we can fool ourselves with
+	    // rounding otherwise
+	    int nValues = 0;
+	    for (int j = 0; j < myData->n; j++) {
+		// valid bin: bound[bin] <= value < bound[bin+1]
+		if (myMask->data.U8[j] == 1) continue;
+		if (myData->data.F32[j] < myHist->bounds->data.F32[i]) continue;
+		if (myData->data.F32[j] >= myHist->bounds->data.F32[i+1]) continue;
+		nValues ++;
+	    }
+	    if (myHist->nums->data.F32[i] != nValues) {
+		diag("Bin number %d bounds: (%6.3f - %6.3f) data (%f) should be (%.2f)\n", i,
+		     myHist->bounds->data.F32[i],
+		     myHist->bounds->data.F32[i + 1],
+		     myHist->nums->data.F32[i], (float) (NUM_DATA/numBins));
+		errorFlag = true;
+	    }
+        }
+        ok(!errorFlag, "psVectorHistogram() correctly set the histogram");
+        skip_end();
+        psFree(myHist);
+        psFree(myMask);
+        psFree(myData);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify the return value is NULL and program execution doesn't stop,
+    // if input psHistogram parameter is NULL.
+    {
+        psMemId id = psMemGetId();
+        psVector *myData = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        myData->n = myData->nalloc;
+        for (int i = 0;i < NUM_DATA;i++) {
+            myData->data.F32[i] = lower + ((higher - lower) / (float) NUM_DATA) * (float) i;
+        }
+        ok(false == psVectorHistogram(NULL, myData, NULL, NULL, 0),
+	   "psVectorHistogram() returned FALSE with a NULL psHistogram input");
+        psFree(myData);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify the return value is the same as the input parameter myHist and
+    // program execution doesn't stop, if the input parameter myArray is
+    // NULL.
+    {
+        psMemId id = psMemGetId();
+        psHistogram *myHist = psHistogramAlloc(lower, higher, numBins);
+        ok(true == psVectorHistogram(myHist, NULL, NULL, NULL, 0),
+	   "psVectorHistogram() returns TRUE with a NULL input array");
+        psFree(myHist);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify the return value is the same as the input parameter myHist and
+    // program execution doesn't stop, if the input parameter myArray has no
+    // elements.
+    {
+        psMemId id = psMemGetId();
+        psVector *myData = psVectorAlloc(0, PS_TYPE_F32);
+        psHistogram *myHist = psHistogramAlloc(lower, higher, numBins);
+        ok(true == psVectorHistogram(myHist, NULL, NULL, NULL, 0),
+	   "psVectorHistogram() returns TRUE with an empty input array");
+        psFree(myHist);
+        psFree(myData);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
+//#define NUM_DATA 20
+void genericTestVectorHistogram2(psS32 numBins, psS32 lower, psS32 higher)
+{
+    if (numBins > NUM_DATA) {
+        diag("Configuration error: NUM_DATA must be larger than numBins");
+        return;
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *myData = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        for (int i = 0;i < NUM_DATA;i++) {
+            myData->data.F32[ i ] = lower + ((higher-lower) / (float) NUM_DATA) * (float) i;
+        }
+
+        psVector *myBounds = psVectorAlloc(numBins+1, PS_TYPE_F32);
+        for (int i=0;i<numBins+1;i++) {
+            myBounds->data.F32[i] = lower + ((higher - lower) / (float) numBins) * (float) i;
+        }
+
+        psHistogram *myHist = psHistogramAllocGeneric(myBounds);
+        ok(myHist->nums->n == numBins, "psHistogramAllocGeneric() set correct number of bins");
+        ok(myHist->bounds->n == numBins+1, "psHistogramAllocGeneric() set correct number of bounds");
+        ok(myHist->minNum == 0, "psHistogramAlloc() initialized minNum to 0.0");
+        ok(myHist->maxNum == 0, "psHistogramAlloc() initialized maxNum to 0.0");
+        ok(myHist->uniform == false, "psHistogramAlloc() initialized ->uniform to false");
+
+        bool errorFlag = false;
+        for (int i=0;i<numBins;i++) {
+            if (fabs(myHist->nums->data.F32[i]) > ERROR_TOLERANCE) {
+                diag("ERROR: myHist->nums->data.F32[%d] not initialized to 0.\n", i);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psVectorHistogram() correctly initialized the bins");
+
+        bool rc = psVectorHistogram(myHist, myData, NULL, NULL, 0);
+        ok(rc == true, "psVectorHistogram() returned TRUE");
+        skip_start(rc == false, 1, "Skipping tests because psVectorHistogram() returned FALSE");
+        for (int i=0;i<numBins;i++) {
+
+	    // we need to explicitly count the number in each bin.  we can fool ourselves with
+	    // rounding otherwise
+	    int nValues = 0;
+	    for (int j = 0; j < myData->n; j++) {
+		// valid bin: bound[bin] <= value < bound[bin+1]
+		if (myData->data.F32[j] < myHist->bounds->data.F32[i]) continue;
+		if (myData->data.F32[j] >= myHist->bounds->data.F32[i+1]) continue;
+		nValues ++;
+	    }
+	    if (myHist->nums->data.F32[i] != nValues) {
+                diag("Bin number %d bounds: (%.2f - %.2f): data: (%.2f) should be (%.2f)\n", i,
+                     myHist->bounds->data.F32[i],
+                     myHist->bounds->data.F32[i+1],
+                     myHist->nums->data.F32[i], nValues);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psVectorHistogram() correctly set the bins");
+        skip_end();
+        psFree(myData);
+        psFree(myHist);
+        psFree(myBounds);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
+
+psS32 main()
+{
+    psLogSetLevel(PS_LOG_INFO);
+    psLogSetFormat("HLNM");
+    plan_tests(168);
+
+
+    // psHistogramAlloc() tests
+    // Call with lower bound larger than upper bound.  Should return NULL.
+    {
+        psMemId id = psMemGetId();
+        psHistogram *myHist = psHistogramAlloc(10, 5, 1);
+        ok(myHist == NULL, "psHistogramAlloc() returned NULL with lower bound larger than upper bound");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call with 0 numBins.  Should return NULL.
+    {
+        psMemId id = psMemGetId();
+        psHistogram *myHist = psHistogramAlloc(5, 10, 0);
+        ok(myHist == NULL, "psHistogramAlloc() returned NULL with 0 numBins");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psHistogramAlloc() tests
+    {
+        genericTestHistogramAlloc(1, 1, 2);
+        genericTestHistogramAlloc(2, 1, 3);
+        genericTestHistogramAlloc(10, 1, 11);
+        genericTestHistogramAlloc(20, 1, 21);
+    }
+
+
+    // psHistogramAllocGeneric() tests
+    // Use NULL psVector* bounds struct.
+    {
+        psMemId id = psMemGetId();
+        psHistogram *myHist = psHistogramAllocGeneric(NULL);
+        ok(myHist == NULL, "psHistogram() returned NULL with NULL psVector* bounds struct");
+        psFree(myHist);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Use unallowed type for bounds
+    {
+        psMemId id = psMemGetId();
+        psS32 numBins = 10;
+        psS32 lower = 5;
+        psS32 higher = 10;
+        psVector *myBounds = psVectorAlloc(numBins+1, PS_TYPE_F64);
+        for (int i=0;i<numBins+1;i++) {
+            myBounds->data.F64[i] = lower + ((higher - lower) / (float) numBins) * (float) i;
+        }
+        psHistogram *myHist = psHistogramAllocGeneric(myBounds);
+        ok(myHist == NULL, "psHistogram() returned NULL with unallowed type for psVector* bounds struct");
+        ok(myBounds != NULL, "psHistogram() did not free bounds");
+        psFree(myHist);
+        psFree(myBounds);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Use only a single bound
+    {
+        psMemId id = psMemGetId();
+        psVector *myBounds = psVectorAlloc(1, PS_TYPE_F32);
+        psHistogram *myHist = psHistogramAllocGeneric(myBounds);
+        ok(myHist == NULL, "psHistogram() returned NULL with only a single bound");
+        ok(myBounds != NULL, "psHistogram() did not free bounds");
+        psFree(myHist);
+        psFree(myBounds);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psHistogramAllocGeneric() tests
+    {
+        genericTestHistogramAllocGeneric(1, 1, 2);
+        genericTestHistogramAllocGeneric(2, 1, 3);
+        genericTestHistogramAllocGeneric(10, 1, 11);
+        genericTestHistogramAllocGeneric(20, 1, 21);
+    }
+
+
+    // psVectorHistogram() tests: uniform histograms
+    {
+        genericTestVectorHistogram1(2, 1, 3);
+        genericTestVectorHistogram1(10, 1, 11);
+        genericTestVectorHistogram1(20, 1, 21);
+        genericTestVectorHistogram1(500, 1, 501);
+    }
+
+
+    // psVectorHistogram() tests: generic histograms
+    {
+        genericTestVectorHistogram2(1, 1, 2);
+        genericTestVectorHistogram2(2, 1, 3);
+        genericTestVectorHistogram2(10, 1, 11);
+        genericTestVectorHistogram2(20, 1, 21);
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMD5.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMD5.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMD5.c	(revision 22322)
@@ -0,0 +1,142 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+// The official MD5 test cases
+#define NUM_TESTS 7
+static const struct
+{
+    const char *input;                  // The input string
+    const char result[16];              // The hash
+    const char string[34];              // The stringified hash
+}
+tests[] =
+    {
+        { "",
+          "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\x09\x98\xec\xf8\x42\x7e",
+          "d41d8cd98f00b204e9800998ecf8427e" },
+        { "a",
+          "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8\x31\xc3\x99\xe2\x69\x77\x26\x61",
+          "0cc175b9c0f1b6a831c399e269772661" },
+        { "abc",
+          "\x90\x01\x50\x98\x3c\xd2\x4f\xb0\xd6\x96\x3f\x7d\x28\xe1\x7f\x72",
+          "900150983cd24fb0d6963f7d28e17f72" },
+        { "message digest",
+          "\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d\x52\x5a\x2f\x31\xaa\xf1\x61\xd0",
+          "f96b697d7cb7938d525a2f31aaf161d0" },
+        { "abcdefghijklmnopqrstuvwxyz",
+          "\xc3\xfc\xd3\xd7\x61\x92\xe4\x00\x7d\xfb\x49\x6c\xca\x67\xe1\x3b",
+          "c3fcd3d76192e4007dfb496cca67e13b" },
+        { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+          "\xd1\x74\xab\x98\xd2\x77\xd9\xf5\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f",
+          "d174ab98d277d9f5a5611c2c9f419d9f" },
+        { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+          "\x57\xed\xf4\xa2\x2b\xe3\xc9\x55\xac\x49\xda\x2e\x21\x07\xb6\x7a",
+          "57edf4a22be3c955ac49da2e2107b67a" }
+    };
+
+
+int main(void)
+{
+    plan_tests(NUM_TESTS * 3 +          // Strings
+               NUM_TESTS * 2 +          // Vectors
+               (NUM_TESTS - 1) * 2 +    // Images (except the empty string test)
+               (NUM_TESTS - 2) * 3 +    // Sub-images (except the empty and "a" string test)
+               1                        // Memory leaks
+              );
+
+//    diag("psMD5 tests");
+
+    // Strings
+    for (int i = 0; i < NUM_TESTS; i++) {
+        psString string = psStringCopy(tests[i].input); // String to test
+        psVector *hash = psStringMD5(string); // The MD5 hash
+        ok(hash, "hash generated");
+
+        skip_start(!hash, 2, "Skipping tests because hash calculation failed");
+
+        ok(memcmp(hash->data.U8, tests[i].result, 16) == 0, "hash matches");
+
+        psString hashString = psMD5toString(hash); // String of the hash
+        ok(strcmp(hashString, tests[i].string) == 0, "hash string matches");
+        psFree(hashString);
+
+        skip_end();
+
+        psFree(hash);
+        psFree(string);
+    }
+
+    // Vectors
+    for (int i = 0; i < NUM_TESTS; i++) {
+        psVector *vector = psVectorAlloc(strlen(tests[i].input), PS_TYPE_U8); // Vector to test
+        memcpy(vector->data.U8, tests[i].input, strlen(tests[i].input));
+        psVector *hash = psVectorMD5(vector); // The MD5 hash
+        ok(hash, "hash generated");
+
+        skip_start(!hash, 1, "Skipping test because hash calculation failed");
+        ok(memcmp(hash->data.U8, tests[i].result, 16) == 0, "hash matches");
+        skip_end();
+
+        psFree(hash);
+        psFree(vector);
+    }
+
+    // Images
+    for (int i = 0; i < NUM_TESTS; i++) {
+        if (strlen(tests[i].input) == 0) {
+            // We don't like images with size = 0,0
+            continue;
+        }
+
+        psImage *image = psImageAlloc(1, strlen(tests[i].input), PS_TYPE_U8); // Image to test
+        for (int j = 0; j < strlen(tests[i].input); j++) {
+            image->data.U8[j][0] = tests[i].input[j];
+        }
+
+        {
+            psVector *hash = psImageMD5(image);
+            ok(hash, "hash generated");
+
+            skip_start(!hash, 1, "Skipping test because hash calculation failed");
+            ok(memcmp(hash->data.U8, tests[i].result, 16) == 0, "hash matches");
+            skip_end();
+
+            psFree(hash);
+        }
+
+        if (strlen(tests[i].input) > 1) {
+            // Generate a subImage (so the source can't assume that everything's contiguous)
+            psRegion region = { 0, 1, 1, image->numRows }; // Region of interest --- all but the first char
+            psImage *subImage = psImageSubset(image, region);
+
+            // We're going to test against something that we calculate ourselves.  Not the most robust of
+            // tests, but we've already verified psStringMD5, so we cross our fingers and hope.
+            psVector *reference = psStringMD5(tests[i].input + 1);
+            ok(reference, "reference hash generated");
+            skip_start(!reference, 2, "Skipping tests because hash calculation failed");
+
+            psVector *hash = psImageMD5(subImage);
+            ok(hash, "hash generated");
+
+            skip_start(!hash, 1, "Skipping test because hash calculation failed");
+            ok(memcmp(reference->data.U8, hash->data.U8, 16) == 0, "hash matches");
+            skip_end();
+
+            psFree(hash);
+
+            skip_end();
+
+            psFree(reference);
+
+            psFree(subImage);
+        }
+
+        psFree(image);
+    }
+
+    done();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMathUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMathUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMathUtils.c	(revision 22322)
@@ -0,0 +1,222 @@
+/*****************************************************************************
+This routine contains code which tests the general math utility functions.
+ 
+XXX: This test never really was complete.  I simply converted the lates version
+to libtap format.  It really needs more work.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_DATA 10
+#define VERBOSE 0
+#define EXTRA_VERBOSE 0
+#define TS00_X_F32  0x00000001
+#define TS00_X_F64  0x00000002
+#define TS00_X_NULL  0x00000004
+#define TS00_DOMAIN_F32  0x00000008
+#define TS00_DOMAIN_F64  0x00000010
+#define TS00_DOMAIN_NULL 0x00000020
+#define TS00_RANGE_F32  0x00000040
+#define TS00_RANGE_F64  0x00000080
+#define TS00_RANGE_NULL  0x00000200
+#define TOLERANCE 0.01
+
+psS32 genericInterpolateTest(
+    psU32 flags,
+    psS32 order,
+    psS32 numData,
+    psF64 xValue,
+    psBool expectedRC)
+{
+    psS32 testStatus = true;
+    psScalar *x = NULL;
+    psVector *domain = NULL;
+    psVector *range = NULL;
+    psScalar *out = NULL;
+
+    psMemId id = psMemGetId();
+    if (expectedRC == false && VERBOSE) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (flags & TS00_X_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL x scalar\n");
+    }
+
+    if (flags & TS00_X_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 x scalar\n");
+        x = psScalarAlloc((psF32) xValue, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_X_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 x scalar\n");
+        x = psScalarAlloc((psF64) xValue, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_DOMAIN_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL domain vector\n");
+    }
+
+    if (flags & TS00_DOMAIN_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 domain vector\n");
+        domain = psVectorAlloc(numData, PS_TYPE_F32);
+        for (psS32 i=0;i<numData;i++) {
+            domain->data.F32[i] = (psF32) i;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                if (VERBOSE)
+                    printf("Original domain data %d: (%.1f)\n", i, domain->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_DOMAIN_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 domain vector\n");
+        domain = psVectorAlloc(numData, PS_TYPE_F64);
+        for (psS64 i=0;i<numData;i++) {
+            domain->data.F64[i] = (psF64) i;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original domain data %d: (%.1f)\n", i, domain->data.F64[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_RANGE_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL range vector\n");
+    }
+
+    if (flags & TS00_RANGE_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 range vector\n");
+        range = psVectorAlloc(numData, PS_TYPE_F32);
+        for (psS32 i=0;i<numData;i++) {
+            range->data.F32[i] = (psF32) 2*i;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original range data %d: (%.1f)\n", i, range->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_RANGE_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 range vector\n");
+        range = psVectorAlloc(numData, PS_TYPE_F64);
+        for (psS64 i=0;i<numData;i++) {
+            range->data.F64[i] = (psF64) 2*i;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original range data %d: (%.1f)\n", i, range->data.F64[i]);
+            }
+        }
+    }
+
+    out = p_psVectorInterpolate(NULL, domain, range, order, x);
+    if (out == NULL) {
+        if (expectedRC == true) {
+            diag("TEST ERROR: the p_psVectorInterpolate function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            diag("TEST ERROR: the p_psVectorInterpolate function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (flags & TS00_X_F32) {
+            if ((out->data.F32 - (2.0 * x->data.F32)) > TOLERANCE) {
+                diag("The interpolated value was %.2f: should be %.2f\n", out->data.F32, 2.0 * x->data.F32);
+                testStatus = false;
+            }
+
+        } else if (flags & TS00_X_F64) {
+            if ((out->data.F64 - (2.0 * x->data.F64)) > TOLERANCE) {
+                diag("The interpolated value was %.2f: should be %.2f\n", out->data.F64, 2.0 * x->data.F64);
+                testStatus = false;
+            }
+        }
+
+        /*
+        if (0) {
+                if (flags & TS00_F_F32) {
+                    expectData = f->data.F32[i];
+                } else if (flags & TS00_F_F64) {
+                    expectData = (psF32) f->data.F64[i];
+                } else if (flags & TS00_F_S32) {
+                    expectData = (psF32) f->data.S32[i];
+                }
+         
+                    if (flags & TS00_X_F32) {
+                        xData = x->data.F32[i];
+                    } else if (flags & TS00_X_F64) {
+                        xData = (psF32) x->data.F64[i];
+                    } else if (flags & TS00_X_S32) {
+                        xData = (psF32) x->data.S32[i];
+                    } else if (flags & TS00_X_NULL) {
+                        if (flags & TS00_POLY_ORD) {
+                            xData = (psF32) i;
+                        } else if (flags & TS00_POLY_CHEB) {
+                            xData = ((2.0 / ((psF32) (numData - 1))) * ((psF32) i)) - 1.0;
+                        }
+                    }
+         
+                    psF32 actualData = psPolynomial1DEval(myPoly, xData);
+         
+                    if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
+                        printf("TEST ERROR: Fitted data %d: (%.1f %.1f), expected was (%.1f)\n",
+                               i, xData, actualData, expectData);
+                        testStatus = false;
+                     } else {
+                        if (VERBOSE) {
+                            printf("GOOD: Fitted data %d: (%.1f %.1f), expected was (%.1f)\n",
+                                     i, xData, actualData, expectData);
+                        }
+                    }
+                }
+        }
+        */
+    }
+
+    psFree(x);
+    psFree(out);
+    psFree(domain);
+    psFree(range);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+
+    return (testStatus);
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    plan_tests(2);
+
+    //
+    // F32 tests:
+    //
+    // All Vectors non-NULL
+
+    ok(genericInterpolateTest(TS00_X_F32 | TS00_DOMAIN_F32 | TS00_RANGE_F32, 5, NUM_DATA, 5.5, true),
+       "p_psVectorInterpolate() worked");
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix01.c	(revision 22322)
@@ -0,0 +1,140 @@
+/** @file  tst_psMatrix_01.c
+*
+*  @brief Test driver for psMatrix transpose function
+*
+*  This test driver contains the following tests:
+*     Transpose input image into output image
+*     Transpose input image into auto allocated NULL output image
+*
+*  @author  Ross Harman, MHPCC
+*
+*  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2007-05-02 04:20:06 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define TOLERANCE 0.000001
+double truthMatrix[3][3] = {{1, 4, 7},
+                            {2, 5, 8},
+                            {3, 6, 9}};
+
+bool check_matrix(psImage *img)
+{
+    bool errorFlag = false;
+
+    for(psU32 i=0; i<img->numRows; i++) {
+        for(psU32 j=0; j<img->numCols; j++) {
+            if(img->type.type == PS_TYPE_F64) {
+                if(fabs(img->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,
+                         img->data.F64[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            } else if(img->type.type == PS_TYPE_F32) {
+                if(fabs(img->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,
+                         img->data.F32[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            }
+        }
+    }
+    return(errorFlag);
+}
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    plan_tests(14);
+
+
+    // Verify with NULL input params
+    {
+        psMemId id = psMemGetId();
+        psImage *outImage = psMatrixTranspose(NULL, NULL);
+        ok(outImage == NULL, "psMatrixTranspose() returned NULL with NULL input params");
+        psFree(outImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify with incorrect input image type
+    {
+        psMemId id = psMemGetId();
+        psImage *inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_S64);
+        psImage *outImage = psMatrixTranspose(NULL, inImage);
+        ok(outImage == NULL, "psMatrixTranspose() returned NULL with incorrect input image type");
+        psFree(inImage);
+        psFree(outImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    psImage *inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    psImage *outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    inImage->data.F64[0][0] = 1;
+    inImage->data.F64[0][1] = 2;
+    inImage->data.F64[0][2] = 3;
+    inImage->data.F64[1][0] = 4;
+    inImage->data.F64[1][1] = 5;
+    inImage->data.F64[1][2] = 6;
+    inImage->data.F64[2][0] = 7;
+    inImage->data.F64[2][1] = 8;
+    inImage->data.F64[2][2] = 9;
+    psImage *inImageF32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+    psImage *outImageF32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+    inImageF32->data.F32[0][0] = 1;
+    inImageF32->data.F32[0][1] = 2;
+    inImageF32->data.F32[0][2] = 3;
+    inImageF32->data.F32[1][0] = 4;
+    inImageF32->data.F32[1][1] = 5;
+    inImageF32->data.F32[1][2] = 6;
+    inImageF32->data.F32[2][0] = 7;
+    inImageF32->data.F32[2][1] = 8;
+    inImageF32->data.F32[2][2] = 9;
+
+    // Transpose input image into output image
+    {
+        psMemId id = psMemGetId();
+        psImage *tempImage = outImage;
+        outImage = psMatrixTranspose(outImage, inImage);
+        ok(outImage->type.dimen == PS_DIMEN_IMAGE, "psMatrixTranspose(): outImage has correct number of dimensions");
+        ok(outImage == tempImage, "psMatrixTranspose(): Return pointer equal to output argument pointer");
+        ok(!check_matrix(outImage), "Output image data set correctly");
+
+        tempImage = outImageF32;
+        outImageF32 = psMatrixTranspose(outImageF32, inImageF32);
+        ok(!check_matrix(outImageF32), "Output image data set correctly");
+        ok(outImageF32->type.dimen == PS_DIMEN_IMAGE, "psMatrixTranspose(): outImage has correct number of dimensions");
+        ok(outImageF32 == tempImage, "psMatrixTranspose(): Return pointer equal to output argument pointer");
+
+        psFree(outImage);
+        psFree(outImageF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Transpose input image into auto allocated NULL output image
+    {
+        psMemId id = psMemGetId();
+        psImage *outImageNull = psMatrixTranspose(NULL, inImage);
+        ok(!check_matrix(outImageNull), "Output image data set correctly");
+
+        psImage *outImageNullF32 = psMatrixTranspose(NULL, inImageF32);
+        check_matrix(outImageNullF32);
+        ok(!check_matrix(outImageNullF32), "Output image data set correctly");
+
+        psFree(outImageNull);
+        psFree(outImageNullF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psFree(inImage);
+    psFree(inImageF32);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix02.c	(revision 22322)
@@ -0,0 +1,131 @@
+/** @file  tst_psMatrix_02.c
+ *
+ *  @brief Test driver for negative tests for psMatrix transpose function
+ *
+ *  This test driver contains the following tests for psMatrix test point 2:
+ *     A)  Input pointer same as output pointer
+ *     B)  Null input psImage
+ *     C)  Incorrect type for input pointer
+ *     D)  Incorrect type for output pointer
+ *     E)  Matrix not square for output pointer
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.4 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2008-05-07 23:12:13 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main(psS32 argc,
+         char* argv[])
+{
+    psLogSetFormat("HLNM");
+    plan_tests(23);
+
+    // Input pointer same as output pointer
+    // XXX: This results in a seg fault.  It's not clear that passing this test is
+    // a requirement.  However, we should probably fix the case where the input
+    // image equals the output image.
+     {
+        psMemId id = psMemGetId();
+        psImage *inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        ok(psMatrixTranspose(inImage, inImage) == NULL, "psMatrixTranspose(): inImage == outImage results in NULL");
+        psFree(inImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Null input psImage
+    // Merge with tap_psMatrix01.c, get rid of this test (redundant)
+    {
+        psMemId id = psMemGetId();
+        psImage *nullImage = NULL;
+        psImage *outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        ok(psMatrixTranspose(outImage, nullImage) == NULL, "psMatrixTranspose(): inImage = NULL results in NULL return");
+        psFree(outImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Incorrect type for input pointer
+    {
+        psMemId id = psMemGetId();
+        psImage *inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_U8);
+        psImage *outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        ok(psMatrixTranspose(outImage, inImage) == NULL, "psMatrixTranspose(): inImage wrong type (U8) results in NULL return");
+        psFree(outImage);
+        psFree(inImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Incorrect type for input pointer
+    {
+        psMemId id = psMemGetId();
+        psImage *inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_S32);
+        psImage *outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        ok(psMatrixTranspose(outImage, inImage) == NULL, "psMatrixTranspose(): inImage wrong type (S32) results in NULL return");
+        psFree(outImage);
+        psFree(inImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Incorrect type for output pointer
+    {
+        psMemId id = psMemGetId();
+        psImage *inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        psImage *outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_U8);
+        outImage = psMatrixTranspose(outImage, inImage);
+        ok(outImage != NULL, "psMatrixTranspose() results in non-NULL return");
+
+        // check that the type was changed.
+        ok(outImage->type.type == PS_TYPE_F64, "the output type was changed to F64");
+        psFree(inImage);
+        psFree(outImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // output target matrix not square (Nx > Ny) for output pointer
+    {
+        psMemId id = psMemGetId();
+        psImage *inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        psImage *outImage = (psImage*)psImageAlloc(3, 2, PS_TYPE_F64);
+        ok(psMatrixTranspose(outImage, inImage) != NULL, "psMatrixTranspose(): non-square matrix results in non-NULL");
+        ok(outImage->numCols == 3, "psMatrixTranspose(): output matrix dimensions match input");
+        ok(outImage->numRows == 3, "psMatrixTranspose(): output matrix dimensions match input");
+        psFree(inImage);
+        psFree(outImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // input matrix not square (Nx < Ny) 
+    {
+        psMemId id = psMemGetId();
+        psImage *inImage = (psImage*)psImageAlloc(2, 3, PS_TYPE_F64);
+        psImage *outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        ok(psMatrixTranspose(outImage, inImage) != NULL, "psMatrixTranspose(): non-square matrix results in non-NULL");
+        ok(outImage->numCols == 3, "psMatrixTranspose(): output matrix dimensions match input");
+        ok(outImage->numRows == 2, "psMatrixTranspose(): output matrix dimensions match input");
+        psFree(inImage);
+        psFree(outImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // input matrix not square (Nx > Ny) 
+    {
+        psMemId id = psMemGetId();
+        psImage *inImage = (psImage*)psImageAlloc(3, 2, PS_TYPE_F64);
+        psImage *outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        ok(psMatrixTranspose(outImage, inImage) != NULL, "psMatrixTranspose(): non-square matrix results in non-NULL");
+        ok(outImage->numCols == 2, "psMatrixTranspose(): output matrix dimensions match input");
+        ok(outImage->numRows == 3, "psMatrixTranspose(): output matrix dimensions match input");
+        psFree(inImage);
+        psFree(outImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix03.c	(revision 22322)
@@ -0,0 +1,228 @@
+/** @file  tst_psMatrix_03.c
+ *
+ *  @brief Test driver for psMatrix LU functions
+ *
+ *  This test driver contains the following tests for psMatrix test point 3:
+ *     Create input and output images and vectors
+ *     Calculate LU matrix
+ *     Determine solution to matrix equation
+ *     Free input and output images and vectors
+ *     Attempt to use null image input argument
+ *     Attempt to use null input vector argument
+ *     Attempt to use null LU image argument
+ *
+ * XXX: Some tests should generate an error or warning, but we don't know how to test that.
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-05-02 04:14:33 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define TOLERANCE 0.000001
+double truthVector[3] = {4.000000, -2.000000, 3.000000};
+double truthMatrix[3][3] = {{4.000000,  5.000000,  6.000000},
+                            {0.750000, -2.750000, -6.500000},
+                            {0.500000, -0.545455, -0.545455}};
+
+psS32 checkMatrix(psImage *img)
+{
+    bool errorFlag = false;
+    for(psU32 i=0; i<img->numRows; i++) {
+        for(psU32 j=0; j<img->numCols; j++) {
+            if(img->type.type == PS_TYPE_F64) {
+                if(fabs(img->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,
+                         img->data.F64[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            } else if(img->type.type == PS_TYPE_F32) {
+                if(fabs(img->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,
+                         img->data.F32[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            }
+        }
+    }
+    return(errorFlag);
+}
+
+
+psS32 checkVector(psVector *vector)
+{
+    bool errorFlag = false;
+    for(psU32 i=0; i<vector->n; i++) {
+        if(vector->type.type == PS_TYPE_F64) {
+            if(fabs(vector->data.F64[i]-truthVector[i]) > TOLERANCE) {
+                diag("Vector values at element %d don't agree %lf vs %lf\n", i,
+                     vector->data.F64[i], truthVector[i]);
+                errorFlag = true;
+            }
+        } else if(vector->type.type == PS_TYPE_F32) {
+            if(fabs(vector->data.F32[i]-truthVector[i]) > TOLERANCE) {
+                diag("Vector values at element %d don't agree %f vs %lf\n", i,
+                     vector->data.F32[i], truthVector[i]);
+                errorFlag = true;
+            }
+        }
+    }
+    return(errorFlag);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    plan_tests(16);
+
+    // This test calculates the LU matrix and then solved the linear equations
+    // in F64 mode.
+    {
+        psMemId id = psMemGetId();
+        psImage *luImage = (psImage*) psImageAlloc(3, 3, PS_TYPE_F64);
+        psImage *tempImage = luImage;
+        psImage *inImage = (psImage*) psImageAlloc(3, 3, PS_TYPE_F64);
+        inImage->data.F64[0][0] =  2;
+        inImage->data.F64[0][1] =  4;
+        inImage->data.F64[0][2] =  6;
+        inImage->data.F64[1][0] =  4;
+        inImage->data.F64[1][1] =  5;
+        inImage->data.F64[1][2] =  6;
+        inImage->data.F64[2][0] =  3;
+        inImage->data.F64[2][1] =  1;
+        inImage->data.F64[2][2] = -2;
+        psVector *perm = (psVector*) psVectorAlloc(3, PS_TYPE_F64);
+        psVector *outVector = (psVector*) psVectorAlloc(3, PS_TYPE_F32);
+        psVector *tempVector = outVector;
+        psVector *inVector = (psVector*) psVectorAlloc(3, PS_TYPE_F64);
+
+        luImage = psMatrixLUD(luImage, &perm, inImage);
+        ok(luImage != NULL, "psMatrixLUD() produced a non-NULL LU matrix");
+        skip_start(luImage == NULL, 6, "Skipping tests because LU matrix was NULL");
+        ok(!checkMatrix(luImage), "psMatrixLUD() produced the correct LU matrix");
+        ok(luImage->type.dimen == PS_DIMEN_IMAGE, "The LU matrix has the correct ->dimen member");
+        ok(luImage == tempImage, "The LU matrix was not created from scratch");
+
+        // Determine solution to matrix equation
+        inVector->data.F64[0] = 18.0;
+        inVector->data.F64[1] = 24.0;
+        inVector->data.F64[2] =  4.0;
+        inVector->n = 3;
+
+        outVector = psMatrixLUSolve(outVector, luImage, inVector, perm);
+        ok(!checkVector(outVector), "psMatrixLUSolve() correctly solved the equations");
+        ok(outVector->type.dimen == PS_DIMEN_VECTOR, "The output vector hasthe correct ->dimen member");
+        ok(outVector == tempVector, "The output vector was not created from scratch");
+
+        skip_end();
+        psFree(inImage);
+        psFree(luImage);
+        psFree(perm);
+        psFree(outVector);
+        psFree(inVector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // This test calculates the LU matrix and then solved the linear equations
+    // in F32 mode.
+    {
+        psMemId id = psMemGetId();
+        psImage *luImage32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+        psImage *tempImage32 = luImage32;
+        psImage *inImage32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+        inImage32->data.F32[0][0] =  2;
+        inImage32->data.F32[0][1] =  4;
+        inImage32->data.F32[0][2] =  6;
+        inImage32->data.F32[1][0] =  4;
+        inImage32->data.F32[1][1] =  5;
+        inImage32->data.F32[1][2] =  6;
+        inImage32->data.F32[2][0] =  3;
+        inImage32->data.F32[2][1] =  1;
+        inImage32->data.F32[2][2] = -2;
+        psVector *perm32 = NULL;
+        psVector *outVector32 = (psVector*)psVectorAlloc(3, PS_TYPE_F32);
+        psVector *tempVector32 = outVector32;
+        psVector *inVector32 = (psVector*)psVectorAlloc(3, PS_TYPE_F32);
+        inVector32->data.F32[0] = 18.0;
+        inVector32->data.F32[1] = 24.0;
+        inVector32->data.F32[2] =  4.0;
+        inVector32->n = 3;
+
+        luImage32 = psMatrixLUD(luImage32, &perm32, inImage32);
+        ok(luImage32 != NULL, "psMatrixLUD() produced a non-NULL LU matrix");
+        skip_start(luImage32 == NULL, 6, "Skipping tests because LU matrix was NULL");
+        ok(!checkMatrix(luImage32), "psMatrixLUD() produced the correct LU matrix");
+        ok(luImage32->type.dimen == PS_DIMEN_IMAGE, "The LU matrix has the correct ->dimen member");
+        ok(luImage32 == tempImage32, "The LU matrix was not created from scratch");
+
+        // Determine solution to matrix equation
+
+        outVector32 = psMatrixLUSolve(outVector32, luImage32, inVector32, perm32);
+        ok(!checkVector(outVector32), "psMatrixLUSolve() correctly solved the equations");
+        ok(outVector32->type.dimen == PS_DIMEN_VECTOR, "The output vector hasthe correct ->dimen member");
+        ok(outVector32 == tempVector32, "The output vector was not created from scratch");
+
+        skip_end();
+        psFree(inImage32);
+        psFree(luImage32);
+        psFree(perm32);
+        psFree(outVector32);
+        psFree(inVector32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to use null image input argument
+    // XXX: This test should generate an error or warning
+    // XXX: This seg-faults
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage *imageTest = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        psMatrixLUD(imageTest, NULL, NULL);
+        psFree(imageTest);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to use null input vector argument
+    // XXX: This test should generate an error or warning
+    // XXX: This seg-faulta
+    if (0) {
+        psMemId id = psMemGetId();
+        psVector *vectorBad = NULL;
+        psVector *vectorBadOut = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+        psVector *permBad = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+        psImage *imageTest = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        psMatrixLUSolve(vectorBadOut, imageTest, vectorBad, permBad);
+        psFree(vectorBadOut);
+        psFree(permBad);
+        psFree(imageTest);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to use null LU image argument
+    // XXX: This test should generate an error or warning, but we don't know how to test that.
+    // XXX: This seg-faulta
+    if (0) {
+        psMemId id = psMemGetId();
+        psVector *vectorBadOut = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+        psVector *vectorBad = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+        psVector *permBad = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+        psMatrixLUSolve(vectorBadOut, NULL, vectorBad, permBad);
+        psFree(vectorBadOut);
+        psFree(vectorBad);
+        psFree(permBad);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix04.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix04.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix04.c	(revision 22322)
@@ -0,0 +1,170 @@
+/** @file  tst_psMatrix_04.c
+ *
+ *  @brief Test driver for psMatrix invert function
+ *
+ *  This test driver contains the following tests for psMatrix test point 4:
+ *     Create input and output images
+ *     Invert matrix and calculate determinant
+ *     Calculate determinant only
+ *     Free input and output images
+ *     Attempt to use null input image argument
+ *     Attempt to use null input float argument
+ *
+ * Sme tests should generate an error/warning, but we don't know how to test that.
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-12-20 20:02:29 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define TOLERANCE 0.000001
+double truthMatrix[3][3] = {{4.0000000, -4.333333, -2.333333},
+                            {-1.000000,  1.666667,  0.666667},
+                            {-1.000000,  0.666667,  0.666667}};
+double truthValue = 3.0;
+
+psS32 checkMatrix(psImage *img)
+{
+    bool errorFlag = false;
+    for(psU32 i=0; i<img->numRows; i++) {
+        for(psU32 j=0; j<img->numCols; j++) {
+            if(img->type.type == PS_TYPE_F64) {
+                if(fabs(img->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,
+                         img->data.F64[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            } else if(img->type.type == PS_TYPE_F32) {
+                if(fabs(img->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,
+                         img->data.F32[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            }
+        }
+    }
+    return(errorFlag);
+}
+
+psS32 checkValue(psF64 value)
+{
+    if(fabs(value-truthValue) > TOLERANCE) {
+        diag("Values don't agree %lf vs %lf\n", value, truthValue);
+        return(true);
+    } else {
+        return(false);
+    }
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    plan_tests(14);
+
+    // Invert matrix and calculate determinant: F64
+    {
+        psMemId id = psMemGetId();
+        float det = 0.0f;
+        float det2 = 0;
+        psImage *tempImage = NULL;
+        psImage *outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        psImage *inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        inImage->data.F64[0][0] =  2;
+        inImage->data.F64[0][1] =  4;
+        inImage->data.F64[0][2] =  3;
+        inImage->data.F64[1][0] =  0;
+        inImage->data.F64[1][1] =  1;
+        inImage->data.F64[1][2] = -1;
+        inImage->data.F64[2][0] =  3;
+        inImage->data.F64[2][1] =  5;
+        inImage->data.F64[2][2] =  7;
+
+        tempImage = outImage;
+        outImage = psMatrixInvert(outImage, inImage, &det);
+        ok(outImage != NULL, "psMatrixInvert() produced a non-NULL matrix");
+        skip_start(outImage == NULL, 4, "Skipping tests because the output matrix is NULL");
+        ok(!checkMatrix(outImage), "psMatrixInvert() produced the correct output matrix");
+        ok(!checkValue(det), "psMatrixInvert() produced the correct determinant");
+        ok(outImage->type.dimen == PS_DIMEN_IMAGE, "psMatrixInvert() produced the correct ->dimen member");
+        ok(outImage == tempImage, "psMatrixInvert() did not allocate a new output matrix");
+        det = 0.0f;
+
+        det2 = psMatrixDeterminant(inImage);
+        ok(!checkValue(det2), "psMatrixDeterminant() produced the correct determinant");
+
+        skip_end()
+        psFree(outImage);
+        psFree(inImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Invert matrix and calculate determinant: F32
+    {
+        psMemId id = psMemGetId();
+        float det = 0.0f;
+        float det2 = 0;
+        psImage *tempImage32 = NULL;
+        psImage *outImage32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+        psImage *inImage32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+        inImage32->data.F32[0][0] =  2;
+        inImage32->data.F32[0][1] =  4;
+        inImage32->data.F32[0][2] =  3;
+        inImage32->data.F32[1][0] =  0;
+        inImage32->data.F32[1][1] =  1;
+        inImage32->data.F32[1][2] = -1;
+        inImage32->data.F32[2][0] =  3;
+        inImage32->data.F32[2][1] =  5;
+        inImage32->data.F32[2][2] =  7;
+
+        tempImage32 = outImage32;
+        outImage32 = psMatrixInvert(outImage32, inImage32, &det);
+        ok(outImage32 != NULL, "psMatrixInvert() produced a non-NULL matrix");
+        skip_start(outImage32 == NULL, 4, "Skipping tests because the output matrix is NULL");
+        ok(!checkMatrix(outImage32), "psMatrixInvert() produced the correct output matrix");
+        ok(!checkValue(det), "psMatrixInvert() produced the correct determinant");
+        ok(outImage32->type.dimen == PS_DIMEN_IMAGE, "psMatrixInvert() produced the correct ->dimen member");
+        ok(outImage32 == tempImage32, "psMatrixInvert() did not allocate a new output matrix");
+
+        det2 = psMatrixDeterminant(inImage32);
+        ok(!checkValue(det2), "psMatrixDeterminant() produced the correct determinant");
+
+        skip_end()
+        psFree(outImage32);
+        psFree(inImage32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to use null input image argument
+    // XXX: This should generate an error/warning, but we don't know how to test that.
+    if (0) {
+        psMemId id = psMemGetId();
+        float det = 0.0f;
+        psImage *badOutImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        psMatrixInvert(badOutImage, NULL, &det);
+        psFree(badOutImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to use null input float argument
+    // XXX: This should generate an error/warning, but we don't know how to test that.
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage *badInImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        psImage *badOutImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+        psMatrixInvert(badOutImage, badInImage, NULL);
+        psFree(badInImage);
+        psFree(badOutImage);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix05.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix05.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix05.c	(revision 22322)
@@ -0,0 +1,120 @@
+/** @file  tst_psMatrix_05.c
+*
+*  @brief Test driver for psMatrix multiplication function
+*
+*  This test driver contains the following tests for psMatrix test point 5:
+*     A)  Create input and output images
+*     B)  Multiply images
+*     C)  Free input and output images
+*
+*  @author  Ross Harman, MHPCC
+*
+*  @version $Revision: 1.1 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2006-12-20 20:02:29 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define TOLERANCE 0.000001
+double truthMatrix[3][3] = {{  0.0, 52.0},
+                            {-14.0, 51.0}};
+
+psS32 checkMatrix(psImage *img)
+{
+    bool errorFlag = false;
+    for(psU32 i=0; i<img->numRows; i++) {
+        for(psU32 j=0; j<img->numCols; j++) {
+            if(img->type.type == PS_TYPE_F64) {
+                if(fabs(img->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,
+                         img->data.F64[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            } else if(img->type.type == PS_TYPE_F32) {
+                if(fabs(img->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,
+                         img->data.F32[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            }
+        }
+    }
+    return(errorFlag);
+}
+
+psS32 main(psS32 argc,
+           char* argv[])
+{
+    psLogSetFormat("HLNM");
+    plan_tests(6);
+
+    // Create input and output images, then multiple: F64 version
+    {
+        psMemId id = psMemGetId();
+        psImage * outImage = (psImage*) psImageAlloc(2, 2, PS_TYPE_F64);
+        psImage *inImage1 = (psImage*) psImageAlloc(3, 2, PS_TYPE_F64);
+        psImage *inImage2 = (psImage*) psImageAlloc(2, 3, PS_TYPE_F64);
+        inImage1->data.F64[0][0] = 2;
+        inImage1->data.F64[0][1] = 3;
+        inImage1->data.F64[0][2] = 4;
+        inImage1->data.F64[1][0] = -1;
+        inImage1->data.F64[1][1] = 2;
+        inImage1->data.F64[1][2] = 5;
+        inImage2->data.F64[0][0] = 4;
+        inImage2->data.F64[0][1] = 1;
+        inImage2->data.F64[1][0] = 0;
+        inImage2->data.F64[1][1] = 6;
+        inImage2->data.F64[2][0] = -2;
+        inImage2->data.F64[2][1] = 8;
+
+        // Test B - Multiply images
+        psMatrixMultiply(outImage, inImage1, inImage2);
+        ok(outImage != NULL, "psMatrixMultiply() produced a non-NULL output matrix");
+        ok(!checkMatrix(outImage), "psMatrixMultiply() produced the correct output matrix");
+
+        // Test C - Free input and output images
+        psFree(outImage);
+        psFree(inImage1);
+        psFree(inImage2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Create input and output images, then multiple: F32 version
+    {
+        psMemId id = psMemGetId();
+        psImage * outImage32 = NULL;
+        psImage *inImage132 = NULL;
+        psImage *inImage232 = NULL;
+        outImage32 = (psImage*)psImageAlloc(2, 2, PS_TYPE_F32);
+        inImage132 = (psImage*)psImageAlloc(3, 2, PS_TYPE_F32);
+        inImage232 = (psImage*)psImageAlloc(2, 3, PS_TYPE_F32);
+        inImage132->data.F32[0][0] = 2;
+        inImage132->data.F32[0][1] = 3;
+        inImage132->data.F32[0][2] = 4;
+        inImage132->data.F32[1][0] = -1;
+        inImage132->data.F32[1][1] = 2;
+        inImage132->data.F32[1][2] = 5;
+        inImage232->data.F32[0][0] = 4;
+        inImage232->data.F32[0][1] = 1;
+        inImage232->data.F32[1][0] = 0;
+        inImage232->data.F32[1][1] = 6;
+        inImage232->data.F32[2][0] = -2;
+        inImage232->data.F32[2][1] = 8;
+
+        // Test B - Multiply images
+        psMatrixMultiply(outImage32, inImage132, inImage232);
+        ok(outImage32 != NULL, "psMatrixMultiply() produced a non-NULL output matrix");
+        ok(!checkMatrix(outImage32), "psMatrixMultiply() produced the correct output matrix");
+
+        // Test C - Free input and output images
+        psFree(outImage32);
+        psFree(inImage132);
+        psFree(inImage232);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix06.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix06.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix06.c	(revision 22322)
@@ -0,0 +1,124 @@
+/** @file  tst_psMatrix_06.c
+*
+*  @brief Test driver for psMatrix Eigenvectors function
+*
+*  This test driver contains the following tests for psMatrix test point 6:
+*     A)  Create input and output images
+*     B)  Calculate Eigenvectors
+*     C)  Free input and output images
+*
+*  @author  Ross Harman, MHPCC
+*
+*  @version $Revision: 1.1 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2006-12-20 20:02:29 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define TOLERANCE 0.000001
+double truthMatrix[4][4] = {{0.792608,  0.582076, -0.179186, -0.029193},
+                            {0.451923, -0.370502,  0.741918,  0.328712},
+                            {0.322416, -0.509579, -0.100228, -0.791411},
+                            {0.252161, -0.514048, -0.638283,  0.514553}};
+
+
+psS32 checkMatrix(psImage *img)
+{
+    bool errorFlag = false;
+    for(psU32 i=0; i<img->numRows; i++) {
+        for(psU32 j=0; j<img->numCols; j++) {
+            if(img->type.type == PS_TYPE_F64) {
+                if(fabs(img->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,
+                         img->data.F64[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            } else if(img->type.type == PS_TYPE_F32) {
+                if(fabs(img->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,
+                         img->data.F32[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            }
+        }
+    }
+    return(errorFlag);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    plan_tests(6);
+
+    // Create input and output images, then produce Eigen vectors: F64 version
+    {
+        psMemId id = psMemGetId();
+        psImage * outImage = (psImage*) psImageAlloc(4, 4, PS_TYPE_F64);
+        psImage *inImage = (psImage*)  psImageAlloc(4, 4, PS_TYPE_F64);
+        inImage->data.F64[0][0] = 1./1.;
+        inImage->data.F64[0][1] = 1./2.;
+        inImage->data.F64[0][2] = 1./3.;
+        inImage->data.F64[0][3] = 1./4.;
+        inImage->data.F64[1][0] = 1./2.;
+        inImage->data.F64[1][1] = 1./3.;
+        inImage->data.F64[1][2] = 1./4.;
+        inImage->data.F64[1][3] = 1./5.;
+        inImage->data.F64[2][0] = 1./3.;
+        inImage->data.F64[2][1] = 1./4.;
+        inImage->data.F64[2][2] = 1./5.;
+        inImage->data.F64[2][3] = 1./6.;
+        inImage->data.F64[3][0] = 1./4.;
+        inImage->data.F64[3][1] = 1./5.;
+        inImage->data.F64[3][2] = 1./6.;
+        inImage->data.F64[3][3] = 1./7.;
+
+        psMatrixEigenvectors(outImage, inImage);
+        ok(outImage != NULL, "psMatrixEigenvectors() produced a NULL output Matrix");
+        skip_start(outImage == NULL, 1, "Skipping tests because output matrix was NULL");
+        ok(!checkMatrix(outImage), "psMatrixEigenvectors() produced the correct Matrix");
+        psFree(outImage);
+        psFree(inImage);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Create input and output images, then produce Eigen vectors: F32 version
+    {
+        psMemId id = psMemGetId();
+        psImage * outImage32 = (psImage*) psImageAlloc(4, 4, PS_TYPE_F32);
+        psImage *inImage32 = (psImage*)  psImageAlloc(4, 4, PS_TYPE_F32);
+        inImage32->data.F32[0][0] = 1./1.;
+        inImage32->data.F32[0][1] = 1./2.;
+        inImage32->data.F32[0][2] = 1./3.;
+        inImage32->data.F32[0][3] = 1./4.;
+        inImage32->data.F32[1][0] = 1./2.;
+        inImage32->data.F32[1][1] = 1./3.;
+        inImage32->data.F32[1][2] = 1./4.;
+        inImage32->data.F32[1][3] = 1./5.;
+        inImage32->data.F32[2][0] = 1./3.;
+        inImage32->data.F32[2][1] = 1./4.;
+        inImage32->data.F32[2][2] = 1./5.;
+        inImage32->data.F32[2][3] = 1./6.;
+        inImage32->data.F32[3][0] = 1./4.;
+        inImage32->data.F32[3][1] = 1./5.;
+        inImage32->data.F32[3][2] = 1./6.;
+        inImage32->data.F32[3][3] = 1./7.;
+
+        psMatrixEigenvectors(outImage32, inImage32);
+        ok(outImage32 != NULL, "psMatrixEigenvectors() produced a NULL output Matrix");
+        skip_start(outImage32 == NULL, 1, "Skipping tests because output matrix was NULL");
+        ok(!checkMatrix(outImage32), "psMatrixEigenvectors() produced the correct Matrix");
+        psFree(outImage32);
+        psFree(inImage32);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix07.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix07.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrix07.c	(revision 22322)
@@ -0,0 +1,340 @@
+/** @file  tst_psMatrix_07.c
+ *
+ *  @brief Test driver for psMatrix vector conversion functions
+ *
+ *  This test driver contains the following tests for psMatrix test point 7:
+ *     Create input and output images and vectors
+ *     Convert matrix to PS_DIMEN_VECTOR vector
+ *     Attempt to use null image input argument
+ *     Convert matrix to PS_DIMEN_TRANSV vector
+ *     XXX: Improper image size (NOT TESTED)
+ *     Convert PS_DIMEN_VECTOR vector to matrix
+ *     XXX: Attempt to use null input vector argument (NOT TESTED)
+ *     Convert PS_DIMEN_TRANSV vector to matrix
+ *     Free input and output images and vectors
+ *
+ * XXX: This should produce an error; how can we test it?
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-02-07 22:50:18 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define TOLERANCE 0.000001
+psF64 truthVector[3] = {0.0, 1.0, 2.0};
+psF32 truthVector_32[3] = {0.0, 1.0, 2.0};
+psF64 truthMatrix[3][1] = {{0.0}, {1.0}, {2.0}};
+psF32 truthMatrix_32[3][1] = {{0.0}, {1.0}, {2.0}};
+
+psS32 checkMatrix(psImage *img)
+{
+    bool errorFlag = false;
+    for(psU32 i=0; i<img->numRows; i++) {
+        for(psU32 j=0; j<img->numCols; j++) {
+            if(img->type.type == PS_TYPE_F64) {
+                if(fabs(img->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,
+                         img->data.F64[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            } else if(img->type.type == PS_TYPE_F32) {
+                if(fabs(img->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {
+                    diag("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,
+                         img->data.F32[i][j], truthMatrix[i][j]);
+                    errorFlag = true;
+                }
+            }
+        }
+    }
+    return(errorFlag);
+}
+
+psS32 checkVector(psVector *vector)
+{
+    bool errorFlag = false;
+    for(psU32 i=0; i<vector->n; i++) {
+        if(vector->type.type == PS_TYPE_F64) {
+            if(fabs(vector->data.F64[i]-truthVector[i]) > TOLERANCE) {
+                diag("Vector values at element %d don't agree %lf vs %lf\n", i,
+                     vector->data.F64[i], truthVector[i]);
+                errorFlag = true;
+            }
+        } else if(vector->type.type == PS_TYPE_F32) {
+            if(fabs(vector->data.F32[i]-truthVector[i]) > TOLERANCE) {
+                diag("Vector values at element %d don't agree %f vs %lf\n", i,
+                     vector->data.F32[i], truthVector[i]);
+                errorFlag = true;
+            }
+        }
+    }
+    return(errorFlag);
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    plan_tests(44);
+
+    // Convert matrix to PS_DIMEN_VECTOR vector: F64 version
+    {
+        psMemId id = psMemGetId();
+        psVector *v1 = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+        psImage *m1 = (psImage*)psImageAlloc(1, 3, PS_TYPE_F64);
+        m1->data.F64[0][0] = 0.0;
+        m1->data.F64[1][0] = 1.0;
+        m1->data.F64[2][0] = 2.0;
+        psVector *tempVector = v1;
+
+        v1 = psMatrixToVector(v1, m1);
+        ok(v1 != NULL, "psMatrixToVector() produced a non-NULL vector");
+        skip_start(v1 == NULL, 3, "Skipping tests because vector was NULL");
+        ok(!checkVector(v1), "psMatrixToVector() produced the correct vector");
+        ok(v1->type.dimen == PS_DIMEN_VECTOR, "psMatrixToVector() produced the correct ->dimen member");
+        ok(v1 == tempVector, "psMatrixToVector() did not allocate a new output vector");
+
+        skip_end();
+        psFree(m1);
+        psFree(v1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Convert matrix to PS_DIMEN_VECTOR vector: F32 version
+    {
+        psMemId id = psMemGetId();
+        psVector *v1_32 = (psVector*)psVectorAlloc(3, PS_TYPE_F32);
+        psImage *m1_32 = (psImage*)psImageAlloc(1,3,PS_TYPE_F32);
+        m1_32->data.F32[0][0] = 0.0;
+        m1_32->data.F32[1][0] = 1.0;
+        m1_32->data.F32[2][0] = 2.0;
+        psVector *tempVector_32 = v1_32;
+
+        v1_32 = psMatrixToVector(v1_32, m1_32);
+        ok(v1_32 != NULL, "psMatrixToVector() produced a non-NULL vector");
+        skip_start(v1_32 == NULL, 3, "Skipping tests because vector was NULL");
+        ok(!checkVector(v1_32), "psMatrixToVector() produced the correct vector");
+        ok(v1_32->type.dimen == PS_DIMEN_VECTOR, "psMatrixToVector() produced the correct ->dimen member");
+        ok(v1_32 == tempVector_32, "psMatrixToVector() did not allocate a new output vector");
+
+        skip_end();
+        psFree(m1_32);
+        psFree(v1_32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Attempt to use null image input argument: F64 version
+    {
+        psMemId id = psMemGetId();
+        psVector *v1 = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+        v1 = psMatrixToVector(v1, NULL);
+        ok(v1 == NULL, "psMatrixToVector() returned NULL with NULL matrix argument");
+        psFree(v1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Attempt to use null image input argument: F32 version
+    {
+        psMemId id = psMemGetId();
+        psVector *v1_32 = (psVector*)psVectorAlloc(3, PS_TYPE_F32);
+        v1_32 = psMatrixToVector(v1_32, NULL);
+        ok(v1_32 == NULL, "psMatrixToVector() returned NULL with NULL matrix argument");
+        psFree(v1_32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Convert matrix to PS_DIMEN_TRANSV vector: F64 version
+    {
+        psMemId id = psMemGetId();
+        psImage *m4 = (psImage*)psImageAlloc(3, 1, PS_TYPE_F64);
+        m4->data.F64[0][0] = 0.0;
+        m4->data.F64[0][1] = 1.0;
+        m4->data.F64[0][2] = 2.0;
+        psVector *v3 = psVectorAlloc(3, PS_TYPE_F64);
+        psVector *tempVector = v3;
+
+        v3->type.dimen = PS_DIMEN_TRANSV;
+        psMatrixToVector(v3, m4);
+        ok(v3 != NULL, "psMatrixToVector() produced non-NULL output VECTOR");
+        skip_start(v3 == NULL, 3, "Skipping tests because of non-NULL output VECTOR");
+        ok(!checkVector(v3), "psMatrixToVector() produced the correct vector");
+        ok(v3->type.dimen == PS_DIMEN_TRANSV, "psMatrixToVector() produced the correct ->dimen member");
+        ok(v3 == tempVector, "psMatrixToVector() did not allocate a new output vector");
+
+        skip_end();
+        psFree(m4);
+        psFree(v3);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Convert matrix to PS_DIMEN_TRANSV vector: F32 version
+    {
+        psMemId id = psMemGetId();
+        psImage *m4_32 = m4_32 = (psImage*)psImageAlloc(3,1,PS_TYPE_F32);
+        m4_32->data.F32[0][0] = 0.0;
+        m4_32->data.F32[0][1] = 1.0;
+        m4_32->data.F32[0][2] = 2.0;
+        psVector *v3_32 = psVectorAlloc(3, PS_TYPE_F32);
+        psVector *tempVector_32 = v3_32;
+
+        v3_32->type.dimen = PS_DIMEN_TRANSV;
+        psMatrixToVector(v3_32, m4_32);
+        ok(v3_32 != NULL, "psMatrixToVector() produced non-NULL output VECTOR");
+        skip_start(v3_32 == NULL, 3, "Skipping tests because of non-NULL output VECTOR");
+        ok(!checkVector(v3_32), "psMatrixToVector() produced the correct vector");
+        ok(v3_32->type.dimen == PS_DIMEN_TRANSV, "psMatrixToVector() produced the correct ->dimen member");
+        ok(v3_32 == tempVector_32, "psMatrixToVector() did not allocate a new output vector");
+
+        skip_end();
+        psFree(m4_32);
+        psFree(v3_32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Improper image size: F64 version
+    // XXX: This should produce an error; how can we test it?
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage *badImage = (psImage*)psImageAlloc(2, 2, PS_TYPE_F64);
+        psVector *v1 = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+        ok(psMatrixToVector(v1, badImage) == NULL, "psMatrixToVector() returned NULL with improper sizes");
+        psFree(badImage);
+        psFree(v1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Improper image size: F32 version
+    // XXX: This should produce an error; how can we test it?
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage *badImage_32 = (psImage*)psImageAlloc(2,2,PS_TYPE_F32);
+        psVector *v1_32 = (psVector*)psVectorAlloc(3, PS_TYPE_F32);
+        ok(psMatrixToVector(v1_32, badImage_32) == NULL, "psMatrixToVector() returned NULL with improper sizes");
+        psFree(badImage_32);
+        psFree(v1_32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Convert PS_DIMEN_VECTOR vector to matrix: F64 version
+    {
+        psMemId id = psMemGetId();
+        psImage *m2 = (psImage*)psImageAlloc(1, 3, PS_TYPE_F64);
+        psVector *v2 = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+        v2->data.F64[0] = 0.0;
+        v2->data.F64[1] = 1.0;
+        v2->data.F64[2] = 2.0;
+        v2->n = 3;
+
+        psImage *tempImage = m2;
+        m2 = psVectorToMatrix(m2, v2);
+        ok(m2 != NULL, "psVectorToMatrix() produced non-NULL output matrix");
+        skip_start(m2 == NULL, 3, "Skipping tests because of non-NULL output matrix");
+        ok(!checkMatrix(m2), "psVectorToMatrix() produced the correct vector");
+        ok(m2->type.dimen == PS_DIMEN_IMAGE, "psVectorToMatrix() produced the correct ->dimen member");
+        ok(m2 == tempImage, "psVectorToMatrix() did not allocate a new output matrix");
+
+        skip_end();
+        psFree(m2);
+        psFree(v2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Convert PS_DIMEN_VECTOR vector to matrix: F32 version
+    {
+        psMemId id = psMemGetId();
+        psImage *m2_32 = (psImage*)psImageAlloc(1,3,PS_TYPE_F32);
+        psVector *v2_32 = (psVector*)psVectorAlloc(3,PS_TYPE_F32);
+        v2_32->data.F32[0] = 0.0;
+        v2_32->data.F32[1] = 1.0;
+        v2_32->data.F32[2] = 2.0;
+        v2_32->n = 3;
+
+        psImage *tempImage_32 = m2_32;
+        m2_32 = psVectorToMatrix(m2_32, v2_32);
+        ok(m2_32 != NULL, "psVectorToMatrix() produced non-NULL output matrix");
+        skip_start(m2_32 == NULL, 3, "Skipping tests because of non-NULL output matrix");
+        ok(!checkMatrix(m2_32), "psVectorToMatrix() produced the correct vector");
+        ok(m2_32->type.dimen == PS_DIMEN_IMAGE, "psVectorToMatrix() produced the correct ->dimen member");
+        ok(m2_32 == tempImage_32, "psVectorToMatrix() did not allocate a new output matrix");
+
+        skip_end();
+        psFree(m2_32);
+        psFree(v2_32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Attempt to use null input vector argument: F64 version
+    // XXX: This should produce an error; how can we test it?
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage *m2 = (psImage*)psImageAlloc(1, 3, PS_TYPE_F64);
+        ok(psVectorToMatrix(m2, NULL) == NULL, "psVectorToMatrix() returned NULL with NULL input vector");
+        psFree(m2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Attempt to use null input vector argument: F32 version
+    // XXX: This should produce an error; how can we test it?
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage *m2_32 = (psImage*)psImageAlloc(1,3,PS_TYPE_F32);
+        ok(psVectorToMatrix(m2_32, NULL) == NULL, "psVectorToMatrix() returned NULL with NULL input vector");
+        psFree(m2_32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Convert PS_DIMEN_TRANSV vector to matrix: F64 version
+    {
+        psMemId id = psMemGetId();
+        psVector *v2 = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+        v2->data.F64[0] = 0.0;
+        v2->data.F64[1] = 1.0;
+        v2->data.F64[2] = 2.0;
+        v2->n = 3;
+        v2->type.dimen = PS_DIMEN_TRANSV;
+        psImage *m3 = (psImage*)psImageAlloc(3, 1, PS_TYPE_F64);
+        psImage *tempImage = m3;
+
+        psVectorToMatrix(m3, v2);
+        ok(m3 != NULL, "psVectorToMatrix() produced non-NULL output matrix");
+        skip_start(m3 == NULL, 3, "Skipping tests because of non-NULL output matrix");
+        ok(!checkMatrix(m3), "psVectorToMatrix() produced the correct vector");
+        ok(m3->type.dimen == PS_DIMEN_IMAGE, "psVectorToMatrix() produced the correct ->dimen member");
+        ok(m3 == tempImage, "psVectorToMatrix() did not allocate a new output matrix");
+
+        skip_end();
+        psFree(v2);
+        psFree(m3);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Convert PS_DIMEN_TRANSV vector to matrix: F32 version
+    {
+        psMemId id = psMemGetId();
+        psVector *v2_32 = (psVector*)psVectorAlloc(3,PS_TYPE_F32);
+        v2_32->data.F32[0] = 0.0;
+        v2_32->data.F32[1] = 1.0;
+        v2_32->data.F32[2] = 2.0;
+        v2_32->n = 3;
+        v2_32->type.dimen = PS_DIMEN_TRANSV;
+        psImage *m3_32 = (psImage*)psImageAlloc(3,1,PS_TYPE_F32);
+        psImage *tempImage_32 = m3_32;
+
+        psVectorToMatrix(m3_32, v2_32);
+        ok(m3_32 != NULL, "psVectorToMatrix() produced non-NULL output matrix");
+        skip_start(m3_32 == NULL, 3, "Skipping tests because of non-NULL output matrix");
+        ok(!checkMatrix(m3_32), "psVectorToMatrix() produced the correct vector");
+        ok(m3_32->type.dimen == PS_DIMEN_IMAGE, "psVectorToMatrix() produced the correct ->dimen member");
+        ok(m3_32 == tempImage_32, "psVectorToMatrix() did not allocate a new output matrix");
+
+        skip_end();
+        psFree(v2_32);
+        psFree(m3_32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic01.c	(revision 22322)
@@ -0,0 +1,247 @@
+/** @file  tst_psMatrixVectorArithmetic01.c
+ *
+ *  @brief Test driver for psMatrixVector arithmetic functions
+ *
+ *  This test driver tests combinations of matrix, vector, and scalar binary operations including:
+ *     Matrix-matrix with +,-,*,/ with S32, F32, F64
+ *     Matrix-vector with +,-,*,/ with S32, F32, F64
+ *     Matrix-scalar with +,-,*,/ with S32, F32, F64
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-05-02 04:14:33 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define VERBOSE 1
+
+#define CREATE_AND_SET_VECTOR(NAME,TYPE,VALUE,SIZE)                \
+psVector *NAME = (psVector*)psVectorAlloc(SIZE, PS_TYPE_##TYPE);   \
+{                                                                  \
+    ps##TYPE tmpScalar = VALUE;                                    \
+    for(psS32 i=0; i<SIZE; i++) {                                  \
+        NAME->data.TYPE[i] = tmpScalar;                            \
+        tmpScalar+= (ps##TYPE) 1;                                  \
+    }                                                              \
+}                                                                  \
+NAME->n = SIZE;
+
+
+#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS)          \
+psImage *NAME = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE);\
+{                                                                  \
+    ps##TYPE tmpScalar = VALUE;                                    \
+    for (psS32 i=0; i<NAME->numRows; i++) {                        \
+        for(psS32 j=0; j<NAME->numCols; j++) {                     \
+            NAME->data.TYPE[i][j] = tmpScalar;                     \
+            tmpScalar+= (ps##TYPE) 1;                              \
+        }                                                          \
+    }                                                              \
+}
+
+#define testBinaryOpMM(OP,TYPE,VALUE1,VALUE2,NROWS,NCOLS,ERRORFLAG,MEMORYFLAG) \
+{                                                                              \
+    psMemId id = psMemGetId();                                                 \
+    ERRORFLAG = false;                                                         \
+    MEMORYFLAG = false;                                                        \
+    CREATE_AND_SET_IMAGE(in1,TYPE,VALUE1,NROWS,NCOLS);                         \
+    CREATE_AND_SET_IMAGE(in2,TYPE,VALUE2,NROWS,NCOLS);                         \
+    psImage *out = (psImage*)psBinaryOp(NULL, in1, #OP, in2);                  \
+    for (psS32 i = 0 ; i < NROWS ; i++) {                                      \
+        for (psS32 j = 0 ; j < NCOLS ; j++) {                                  \
+            ps##TYPE expect = in1->data.TYPE[i][j] OP in2->data.TYPE[i][j];    \
+            if (fabs((psF32) (out->data.TYPE[i][j] - expect)) > 0.1) {         \
+                errorFlag = true;                                              \
+                if (VERBOSE) {                                                 \
+                    printf("TEST ERROR: outImage[%d][%d] is %f, should be %f\n", i, j, (psF32) out->data.TYPE[i][j], (psF32) expect); \
+                }                                                              \
+            }                                                                  \
+        }                                                                      \
+    }                                                                          \
+    psFree(in1);                                                               \
+    psFree(in2);                                                               \
+    psFree(out);                                                               \
+    MEMORYFLAG = psMemCheckLeaks(id, NULL, NULL, false);                       \
+}
+
+#define testBinaryOpMV(OP,TYPE,VALUE1,VALUE2,NROWS,NCOLS,ERRORFLAG,MEMORYFLAG) \
+{                                                                              \
+    psMemId id = psMemGetId();                                                 \
+    ERRORFLAG = false;                                                         \
+    MEMORYFLAG = false;                                                        \
+    CREATE_AND_SET_IMAGE(in,TYPE,VALUE1,NROWS,NCOLS);                          \
+    CREATE_AND_SET_VECTOR(inVector,TYPE,VALUE2,NROWS);                         \
+    psImage *out = (psImage*)psBinaryOp(NULL, in, #OP, inVector);              \
+    for (psS32 i = 0 ; i < NROWS ; i++) {                                      \
+        for (psS32 j = 0 ; j < NCOLS ; j++) {                                  \
+            ps##TYPE expect = in->data.TYPE[i][j] OP inVector->data.TYPE[i];   \
+            if (fabs((psF32) (out->data.TYPE[i][j] - expect)) > 0.1) {         \
+                errorFlag = true;                                              \
+                if (VERBOSE) {                                                 \
+                    printf("TEST ERROR: outImage[%d][%d] is %f, should be %f\n", i, j, (psF32) out->data.TYPE[i][j], (psF32) expect); \
+                }                                                              \
+            }                                                                  \
+        }                                                                      \
+    }                                                                          \
+    psFree(in);                                                                \
+    psFree(inVector);                                                          \
+    psFree(out);                                                               \
+    MEMORYFLAG = psMemCheckLeaks(id, NULL, NULL, false);                       \
+}
+
+#define testBinaryOpMS(OP,TYPE,VALUE1,VALUE2,NROWS,NCOLS,ERRORFLAG,MEMORYFLAG) \
+{                                                                              \
+    psMemId id = psMemGetId();                                                 \
+    ERRORFLAG = false;                                                         \
+    MEMORYFLAG = false;                                                        \
+    CREATE_AND_SET_IMAGE(in,TYPE,VALUE1,NROWS,NCOLS);                          \
+    psScalar *inScalar = (psScalar*)psScalarAlloc(VALUE2,PS_TYPE_##TYPE);      \
+    psImage *out = (psImage*)psBinaryOp(NULL, in, #OP, psScalarCopy(inScalar));\
+    for (psS32 i = 0 ; i < NROWS ; i++) {                                      \
+        for (psS32 j = 0 ; j < NCOLS ; j++) {                                  \
+            ps##TYPE expect = in->data.TYPE[i][j] OP inScalar->data.TYPE;      \
+            if (fabs((psF32) (out->data.TYPE[i][j] - expect)) > 0.1) {         \
+                errorFlag = true;                                              \
+                if (VERBOSE) {                                                 \
+                    printf("TEST ERROR: outImage[%d][%d] is %f, should be %f\n", i, j, (psF32) out->data.TYPE[i][j], (psF32) expect); \
+                }                                                              \
+            }                                                                  \
+        }                                                                      \
+    }                                                                          \
+    psFree(inScalar);                                                          \
+    psFree(in);                                                                \
+    psFree(out);                                                               \
+    MEMORYFLAG = psMemCheckLeaks(id, NULL, NULL, false);                       \
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    bool errorFlag = false;
+    bool memoryFlag = false;
+    plan_tests(72);
+
+    //Test matrix-matrix binary operations
+    testBinaryOpMM(+,S32,10,20,5,4,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, +, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(+,F32,10.0,10.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, +, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(+,F64,10.0,10.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, +, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(-,S32,20,10,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, -, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(-,F32,20.0,10.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, -, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(-,F64,20.0,10.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, -, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(*,S32,20,10,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, *, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(*,F32,20.0,10.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, *, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(*,F64,20.0,10.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, *, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(/,S32,20,10,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, /, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(/,F32,20.0,10.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, /, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMM(/,F64,20.0,10.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-matrix, /, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+
+    // Test Matrix-Vector binary operations
+    testBinaryOpMV(+,S32,10,5,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, +, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(+,F32,10.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, +, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(+,F64,10.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, +, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(-,S32,20,5,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, -, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(-,F32,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, -, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(-,F64,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, -, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(*,S32,20,5,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, *, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(*,F32,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, *, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(*,F64,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, *, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(/,S32,20,5,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, /, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(/,F32,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, /, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMV(/,F64,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-vector, /, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+
+    // Test Matrix-Scalar binary operations
+    testBinaryOpMS(+,S32,10,5,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, +, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(+,F32,10.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, +, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(+,F64,10.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, +, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(-,S32,20,5,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, -, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(-,F32,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, -, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(-,F64,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, -, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(*,S32,20,5,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, *, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(*,F32,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, *, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(*,F64,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, *, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(/,S32,20,5,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, /, S32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(/,F32,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, /, F32)");
+    ok(memoryFlag== false, "no memory leaks");
+    testBinaryOpMS(/,F64,20.0,5.0,3,2,errorFlag,memoryFlag);
+    ok(errorFlag== false, "psBinaryOp(): was successful: (matrix-scalar, /, F64)");
+    ok(memoryFlag== false, "no memory leaks");
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic02.c	(revision 22322)
@@ -0,0 +1,446 @@
+
+/** @file  tst_psMatrixVectorArithmetic02.c
+ *
+ *  @brief Test driver for psMatrixVector arithmetic functions
+ *
+ *  This test driver tests combinations of matrix, vector, and scalar unary operations including:
+ *     Matrix with all math operators with S32, F32, F64
+ *     Vector with all math operators with S32, F32, F64
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-05-02 04:14:33 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define CHECK_VECTOR(VECTOR,TYPE,TRUTH,ERRORFLAG)                                                                      \
+{ \
+    for(psS32 i=0; i<VECTOR->n; i++) {                                                                             \
+        if(cabs(VECTOR->data.TYPE[i])-cabs(TRUTH) > FLT_EPSILON){                                                \
+            ERRORFLAG = true; \
+            diag("ERROR:Truth and calculated values don't match for vector operation:\n");                     \
+            if(PS_IS_PSELEMTYPE_COMPLEX(VECTOR->type.type)) {                                                    \
+                diag("Truth: %.2f%+.2fi\n", creal(VECTOR->data.TYPE[i]), cimag(VECTOR->data.TYPE[i]));         \
+                diag("Calculated: %.2f%+.2fi\n", creal(TRUTH), cimag(TRUTH));                                  \
+            } else if(PS_IS_PSELEMTYPE_INT(VECTOR->type.type)) {                                                 \
+                diag("Truth: %d\n", (psS32)(VECTOR->data.TYPE[i]));                                              \
+                diag("Calculated: %d\n", (psS32)(TRUTH));                                                        \
+            } else {                                                                                             \
+                diag("Truth: %.2f\n", (double)(VECTOR->data.TYPE[i]));                                         \
+                diag("Calculated: %.2f\n", (double)(TRUTH));                                                   \
+            }                                                                                                    \
+            diag("\n"); \
+        }                                                                                                        \
+    }                                                                                                            \
+}
+
+#define CHECK_MATRIX(IMAGE,TYPE,TRUTH,ERRORFLAG)                                                                       \
+{ \
+    for(psS32 i=IMAGE->numRows-1; i>-1; i--) {                                                                     \
+        for(psS32 j=0; j<IMAGE->numCols; j++) {                                                                    \
+            if(cabs(IMAGE->data.TYPE[i][j])-cabs(TRUTH) > FLT_EPSILON){                                          \
+                ERRORFLAG = true; \
+                diag("ERROR:Truth and calculated values don't match for matrix operation:\n");                 \
+                if(PS_IS_PSELEMTYPE_COMPLEX(IMAGE->type.type)) {                                                 \
+                    diag("Truth: %.2f%+.2fi\n", creal(IMAGE->data.TYPE[i][j]), cimag(IMAGE->data.TYPE[i][j])); \
+                    diag("Calculated: %.2f%+.2fi\n", creal(TRUTH), cimag(TRUTH));                              \
+                } else if(PS_IS_PSELEMTYPE_INT(IMAGE->type.type)) {                                              \
+                    diag("Truth: %d\n", (psS32)(IMAGE->data.TYPE[i][j]));                                        \
+                    diag("Calculated: %d\n", (psS32)(TRUTH));                                                    \
+                } else {                                                                                         \
+                    diag("Truth: %.2f\n", (double)(IMAGE->data.TYPE[i][j]));                                   \
+                    diag("Calculated: %.2f\n", (double)(TRUTH));                                               \
+                }                                                                                                \
+                diag("\n");                                                                                            \
+                diag("\n");                                                                                            \
+            }                                                                                                    \
+        }                                                                                                        \
+    }                                                                                                            \
+}
+
+#define CREATE_AND_SET_VECTOR(NAME,TYPE,VALUE,SIZE)                                                          \
+psVector *NAME = (psVector*)psVectorAlloc(SIZE, PS_TYPE_##TYPE);                                             \
+for(psS32 i=0; i<SIZE; i++) {                                                                                  \
+    NAME->data.TYPE[i] = VALUE;                                                                              \
+}                                                                                                            \
+NAME->n = SIZE;
+
+
+#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS)                                                    \
+psImage *NAME = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE);                                          \
+for(psS32 i=0; i<NAME->numRows; i++) {                                                                         \
+    for(psS32 j=0; j<NAME->numCols; j++) {                                                                     \
+        NAME->data.TYPE[i][j] = VALUE;                                                                       \
+    }                                                                                                        \
+}
+
+
+#define CHECK_MEMORY \
+if( psMemCheckLeaks(0, NULL, stdout, false) != 0 ) {  \
+    psError(PS_ERR_UNKNOWN, true,"Memory leaks detected."); \
+    return 50; \
+} \
+psS32 nBad = psMemCheckCorruption(0); \
+if(nBad) { \
+    psError(PS_ERR_UNKNOWN, true,"ERROR: Found %d bad memory blocks\n", nBad); \
+    return 51; \
+}
+
+// Test matrix unary operations
+#define testUnaryOpM(OP,TYPE,VALUE1,VALUE2,NROWS,NCOLS,TRUTH,ERRORFLAG,MEMORYFLAG) \
+{                                                                                  \
+    psMemId id = psMemGetId();                                                     \
+    ERRORFLAG = false;                                                             \
+    MEMORYFLAG = false;                                                            \
+    CREATE_AND_SET_IMAGE(inImage,TYPE,VALUE1,NROWS,NCOLS);                         \
+    CREATE_AND_SET_IMAGE(outImage,TYPE,VALUE2,NROWS,NCOLS);                        \
+    outImage = (psImage*)psUnaryOp(outImage, inImage, #OP);                        \
+    CHECK_MATRIX(outImage,TYPE,TRUTH,ERRORFLAG);                                   \
+    psFree(inImage);                                                               \
+    psFree(outImage);                                                              \
+    MEMORYFLAG = psMemCheckLeaks(id, NULL, NULL, false);                           \
+}
+
+// Test vector unary operations
+#define testUnaryOpV(OP,TYPE,VALUE1,VALUE2,SIZE,TRUTH,ERRORFLAG,MEMORYFLAG)        \
+{                                                                                  \
+    psMemId id = psMemGetId();                                                     \
+    ERRORFLAG = false;                                                             \
+    MEMORYFLAG = false;                                                            \
+    CREATE_AND_SET_VECTOR(inVector,TYPE,VALUE1,SIZE);                              \
+    CREATE_AND_SET_VECTOR(outVector,TYPE,VALUE2,SIZE);                             \
+    outVector = (psVector*)psUnaryOp(outVector, inVector, #OP);                    \
+    CHECK_VECTOR(outVector,TYPE,TRUTH,ERRORFLAG);                                  \
+    psFree(inVector);                                                              \
+    psFree(outVector);                                                             \
+    MEMORYFLAG = psMemCheckLeaks (id, NULL, NULL, false);                          \
+}
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    plan_tests(272);
+    bool errorFlag = false;
+    bool memoryFlag = false;
+
+    testUnaryOpM( abs, S32, -10, 0, 3, 2, 10, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): abs was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( abs, F32, -10.0, 0.0, 3, 2, 10.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): abs was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( abs, F64, -10.0, 0.0, 3, 2, 10.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): abs was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( exp, S32, 10, 0, 3, 2, exp(10), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): exp was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( exp, F32, 10.0, 0.0, 3, 2, cexp(10.0), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): exp was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( exp, F64, 10.0, 0.0, 3, 2, cexp(10.0), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): exp was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( ln, S32, 10, 0, 3, 2, clog(10), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ln was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( ln, F32, 10.0, 0.0, 3, 2, clog(10.0), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ln was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( ln, F64, 10.0, 0.0, 3, 2, clog(10.0), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ln was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( ten, S32, 3, 0, 3, 2, 1000, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ten was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( ten, F32, 3.0, 0.0, 3, 2, 1000, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ten was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( ten, F64, 3.0, 0.0, 3, 2, 1000, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ten was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( log, S32, 1000, 0, 3, 2, 3, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): log was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( log, F32, 1000.0, 0.0, 3, 2, 3, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): log was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( log, F64, 1000.0, 0.0, 3, 2, 3, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): log was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( sin, S32, M_PI_2, 0, 3, 2, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): sin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( sin, F32, M_PI_2, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): sin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( sin, F64, M_PI_2, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): sin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dsin, S32, 90, 0, 3, 2 , 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dsin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dsin, F32, 90.0, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dsin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dsin, F64, 90.0, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dsin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( cos, S32, 0, 0, 3, 2, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): cos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( cos, F32, 0.0, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): cos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( cos, F64, 0.0, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): cos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dcos, S32, 0, 0, 3, 2, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dcos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dcos, F32, 0.0, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dcos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dcos, F64, 0.0, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dcos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( tan, S32, M_PI_4, 0, 3, 2, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): tan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( tan, F32, M_PI_4, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): tan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( tan, F64, M_PI_4, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): tan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dtan, S32, 45, 0, 3, 2, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dtan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dtan, F32, 45.0, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dtan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dtan, F64, 45.0, 0.0, 3, 2, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dtan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( asin, S32, 1, 0, 3, 2, M_PI_2, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): asin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( asin, F32, 1.0, 0.0, 3, 2, M_PI_2 , errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): asin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( asin, F64, 1.0, 0.0, 3, 2, M_PI_2, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): asin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dasin, S32, 1.0, 0, 3, 2, 90, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dasin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dasin, F32, 1.0, 0.0, 3, 2, 90.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dasin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dasin, F64, 1.0, 0.0, 3, 2, 90.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dasin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( acos, S32, 0, 0, 3, 2, M_PI_2, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): acos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( acos, F32, 0.0, 0.0, 3, 2, M_PI_2, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): acos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( acos, F64, 0.0, 0.0, 3, 2, M_PI_2, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): acos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dacos, S32, 0, 0, 3, 2, 90, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dacos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dacos, F32, 0.0, 0.0, 3, 2, 90.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dacos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( dacos, F64, 0.0, 0.0, 3, 2, 90.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dacos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( atan, S32, 1, 0, 3, 2, M_PI_4, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): atan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( atan, F32, 1.0, 0.0, 3, 2, M_PI_4, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): atan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( atan, F64, 1.0, 0.0, 3, 2, M_PI_4, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): atan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( datan, S32, 1, 0, 3, 2, 45, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): datan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( datan, F32, 1.0, 0.0, 3, 2, 45.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): datan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpM( datan, F64, 1.0, 0.0, 3, 2, 45.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): datan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+
+    testUnaryOpV( abs, S32, -10, 0, 3, 10, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): abs was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( abs, F32, -10.0, 0.0, 3, 10.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): abs was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( abs, F64, -10.0, 0.0, 3, 10.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): abs was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( exp, S32, 10, 0, 3, cexp(10), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): exp was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( exp, F32, 10.0, 0.0, 3, cexp(10.0), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): exp was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( exp, F64, 10.0, 0.0, 3, cexp(10.0), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): exp was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( ln, S32, 10, 0, 3, clog(10), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ln was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( ln, F32, 10.0, 0.0, 3, clog(10.0), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ln was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( ln, F64, 10.0, 0.0, 3, clog(10.0), errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ln was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( ten, S32, 3, 0, 3, 1000, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ten was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( ten, F32, 3.0, 0.0, 3, 1000, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ten was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( ten, F64, 3.0, 0.0, 3, 1000, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): ten was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( log, S32, 1000, 0, 3,  3, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): log was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( log, F32, 1000.0, 0.0, 3, 3, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): log was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( log, F64, 1000.0, 0.0, 3, 3, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): log was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( sin, S32, M_PI_2, 0, 3, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): sin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( sin, F32, M_PI_2, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): sin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( sin, F64, M_PI_2, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): sin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dsin, S32, 90, 0, 3, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dsin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dsin, F32, 90.0, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dsin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dsin, F64, 90.0, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dsin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( cos, S32, 0, 0, 3, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): cos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( cos, F32, 0.0, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): cos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( cos, F64, 0.0, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): cos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dcos, S32, 0, 0, 3, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dcos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dcos, F32, 0.0, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dcos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dcos, F64, 0.0, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dcos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( tan, S32, M_PI_4, 0, 3, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): tan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( tan, F32, M_PI_4, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): tan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( tan, F64, M_PI_4, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): tan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dtan, S32, 45, 0, 3, 1, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dtan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dtan, F32, 45.0, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dtan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dtan, F64, 45.0, 0.0, 3, 1.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dtan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( asin, S32, 1, 0, 3, M_PI_2, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): asin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( asin, F32, 1.0, 0.0, 3, M_PI_2 , errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): asin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( asin, F64, 1.0, 0.0, 3, M_PI_2, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): asin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dasin, S32, 1.0, 0, 3, 90, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dasin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dasin, F32, 1.0, 0.0, 3, 90.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dasin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dasin, F64, 1.0, 0.0, 3, 90.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dasin was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( acos, S32, 0, 0, 3, M_PI_2, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): acos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( acos, F32, 0.0, 0.0, 3, M_PI_2, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): acos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( acos, F64, 0.0, 0.0, 3, M_PI_2, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): acos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dacos, S32, 0, 0, 3, 90, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dacos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dacos, F32, 0.0, 0.0, 3, 90.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dacos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( dacos, F64, 0.0, 0.0, 3, 90.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): dacos was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( atan, S32, 1, 0, 3, M_PI_4, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): atan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( atan, F32, 1.0, 0.0, 3, M_PI_4, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): atan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( atan, F64, 1.0, 0.0, 3, M_PI_4, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): atan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( datan, S32, 1, 0, 3, 45, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): datan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( datan, F32, 1.0, 0.0, 3, 45.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): datan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+    testUnaryOpV( datan, F64, 1.0, 0.0, 3, 45.0, errorFlag, memoryFlag);
+    ok(errorFlag== false, "psUnaryOp(): datan was successful");
+    ok(memoryFlag== false, "no memory leaks");
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic03.c	(revision 22322)
@@ -0,0 +1,255 @@
+/** @file  tst_psMatrixVectorArithmetic03.c
+ *
+ *  @brief Test driver for psMatrixVector arithmetic functions
+ *
+ *  This test driver contains negative tests for psBinaryOp and psUanryOp:
+ *     Check for NULL arguments
+ *     Inconsistent element types
+ *     Inconsistent element count
+ *     Inconsistent dimensionality
+ *     Division by zero
+ *     Invalid operation
+ *
+ * XXX: Many of these tests are desinged to produce an error.  They are commented out.
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-05-02 04:14:33 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define CREATE_AND_SET_VECTOR(NAME,TYPE,VALUE,SIZE) \
+psVector *NAME = (psVector*)psVectorAlloc(SIZE, PS_TYPE_##TYPE); \
+for(psS32 i=0; i<SIZE; i++) { \
+    NAME->data.TYPE[i] = VALUE; \
+} \
+NAME->n = SIZE;
+
+
+#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS) \
+psImage *NAME = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE); \
+for(psS32 i=0; i<NAME->numRows; i++) { \
+    for(psS32 j=0; j<NAME->numCols; j++) { \
+        NAME->data.TYPE[i][j] = VALUE; \
+    } \
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    plan_tests(5);
+    psMemId idGlobal = psMemGetId();
+
+    CREATE_AND_SET_IMAGE(image1,F64,0,3,3);
+    CREATE_AND_SET_IMAGE(image2,F64,0,3,3);
+    CREATE_AND_SET_IMAGE(image3,F32,0,3,3);
+    CREATE_AND_SET_IMAGE(image4,F64,0,2,2);
+    CREATE_AND_SET_VECTOR(vector1,F64,0,2);
+    CREATE_AND_SET_VECTOR(vector2,F64,0,3);
+
+    // Check for NULL output argument
+    {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psBinaryOp(NULL, image1, "+", image2);
+        ok(image6 != NULL, "psBinaryOp() produced a non-NULL image given no output to recycle.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Check for NULL input argument #1
+
+    if (1) {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psBinaryOp(image6, NULL, "+", image2);
+        ok(image6 == NULL, "psBinaryOp() returned a NULL result given a NULL first operand.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Check for NULL input argument #2
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psBinaryOp(image6, image1, "+", NULL);
+        ok(image6 == NULL, "psBinaryOp() returned a NULL given a NULL second operand.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Check for NULL operand
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psBinaryOp(image6, image1, NULL, image2);
+        ok(image6 == NULL, "psBinaryOp returned a NULL given a NULL operator.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Check for null output
+    {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psUnaryOp(NULL, image1, "sin");
+        ok(image6 != NULL, "psUnaryOp produced an image given no output to recycle.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Check for NULL input arg
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psUnaryOp(image6, NULL, "sin");
+        ok(image6 == NULL, "psUnaryOp returned a NULL given a NULL operand.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Check for NULL operand
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psUnaryOp(image6, image1, NULL);
+        ok(image6 == NULL, "psUnaryOp returned a NULL operand.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Inconsistent element types
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psBinaryOp(image6, image3, "+", image2);
+        ok(image6 == NULL, "psBinaryOp returned a NULL given operands of different types");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Check unary op to convert to correct type
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = psImageCopy(image6,image2,PS_TYPE_F64);
+        image6 = (psImage*)psUnaryOp(image6, image3, "sin");
+        ok(!(image6 == NULL || image6->type.type != PS_TYPE_F32),
+           "psUnaryOp converted the type of the output.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Inconsistent element count
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psBinaryOp(image6, image4, "+", image2);
+        ok(image6 == NULL, "psBinaryOp returned a NULL given operands of different sizes.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Inconsistent element in input and output
+    // XXX: This fails, but should work.
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = psImageCopy(image6,image2,PS_TYPE_F64);
+        image6 = (psImage*)psUnaryOp(image6, image4, "sin");
+        ok(!(image6 == NULL ||
+             image6->numCols != image6->numCols ||
+             image6->numRows != image6->numRows),"psUnaryOp resized the output.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Inconsistent size of input 1 and input 2
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psBinaryOp(image6, vector1, "+", image2);
+        ok(image6 == NULL, "psBinaryOp returned a NULL given operands of different sizes.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Inconsistent size of input 1 and input 2
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        vector1->type.dimen = PS_DIMEN_TRANSV;
+        psImage* image6 = (psImage*)psBinaryOp(image6, vector1, "+", image2);
+        ok(image6 == NULL, "psBinaryOp returned a NULL given operands of different sizes.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Inconsistent dimensionality
+    // Following should generate an two error messages
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = psImageCopy(image6,image2,PS_TYPE_F64);
+        image6 = (psImage*)psUnaryOp(image6, vector2, "sin");
+        ok(image6 == NULL, "psUnaryOp returned NULL given wrong type out parameter.");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invalid operation
+    // Following should generate an error messgae
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psBinaryOp(image6, image1, "yarg", image2);
+        ok(image6 == NULL, "psBinaryOp returned NULL with invalid operator");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage* image6 = (psImage*)psUnaryOp(image6, image1, "yarg");
+        ok(image6 == NULL, "psUnaryOp returned NULL with invalid operator");
+        psFree(image6);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    CREATE_AND_SET_VECTOR(vector4,F64,0,3);
+    CREATE_AND_SET_VECTOR(vector5,F64,0,3);
+
+    // Input parameter with dimension of PS_DIMEN_OTHER
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        vector4->type.dimen = PS_DIMEN_OTHER;
+        ok(psBinaryOp(NULL,vector4,"+",vector5) == NULL, "psBinaryOp should return null when input dimen PS_DIMEN_OTHER.");
+        vector4->type.dimen = PS_DIMEN_VECTOR;
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Input parameter with dimension of PS_DIMEN_OTHER
+    // Following should generate an error message
+    if (0) {
+        psMemId id = psMemGetId();
+        vector4->type.dimen = PS_DIMEN_OTHER;
+        ok(psUnaryOp(NULL,vector4,"sin") == NULL, "psUnaryOp should return null when input dimen PS_DIMEN_OTHER");
+        vector4->type.dimen = PS_DIMEN_VECTOR;
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psFree(vector4);
+    psFree(vector5);
+    psFree(image1);
+    psFree(image2);
+    psFree(image3);
+    psFree(image4);
+    psFree(vector1);
+    psFree(vector2);
+    ok(!psMemCheckLeaks (idGlobal, NULL, NULL, false), "no memory leaks");
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic04.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic04.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMatrixVectorArithmetic04.c	(revision 22322)
@@ -0,0 +1,234 @@
+/** @file  tst_psMatrixVectorArithmetic04.c
+ *
+ *  @brief Test driver for psBinary arithmetic operations with scalars
+ *
+ *  This test driver will test the following binary operation with scalar inputs
+ *        vector addition with scalar in first argument
+ *        image addition with scalar in second argument
+ *
+ * XXX: The scalar functions at the end are producing memory corruption errors
+ * when they shouldn't.
+ *
+ * @author  Eric Van Alst, MHPCC
+ *
+ * @version $Revision: 1.1 $  $Name: not supported by cvs2svn $
+ * @date  $Date: 2007-01-06 00:48:54 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+// Create vector
+#define CREATE_AND_SET_VECTOR(NAME,TYPE,VALUE,SIZE) \
+psVector *NAME = psVectorAlloc(SIZE,PS_TYPE_##TYPE); \
+for(psS32 i=0; i<SIZE; i++) { \
+    NAME->data.TYPE[i] = VALUE; \
+} \
+NAME->n = SIZE;
+
+// Create image
+#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS) \
+psImage *NAME = psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE); \
+for(psS32 i=0; i<NAME->numRows; i++) { \
+    for(psS32 j=0; j<NAME->numCols; j++) { \
+        NAME->data.TYPE[i][j] = VALUE; \
+    } \
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    plan_tests(12);
+
+    // testBinOpScalarFirst(void)
+    {
+        psMemId id = psMemGetId();
+        bool psErrorFlag = false;
+        CREATE_AND_SET_VECTOR(vector1,S8,1,5)
+        CREATE_AND_SET_VECTOR(vector2,S8,0,5)
+        psScalar* inScalar1 = psScalarAlloc(2,PS_TYPE_S8);
+
+        // Add vector and scalar
+        vector2 = (psVector*)psBinaryOp(vector2,inScalar1,"+",vector1);
+        // Verify the result vector
+        for(psS32 i=0; i<vector2->n; i++)
+        {
+            if(vector2->data.S8[i] != 3 ) {
+                psErrorFlag = true;
+                diag("Unexpected value in return vector[%d]",i);
+            }
+        }
+        ok(!psErrorFlag, "psBinaryOp() produced the correct vector");
+        psFree(vector1);
+        psFree(vector2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        bool psErrorFlag = false;
+        CREATE_AND_SET_VECTOR(vector3,S8,1,5);
+        CREATE_AND_SET_VECTOR(vector4,S8,0,3);
+
+        psScalar* inScalar2 = psScalarAlloc(2,PS_TYPE_S8);
+        vector4->type.dimen = PS_DIMEN_TRANSV;
+        vector4 = (psVector*)psBinaryOp(vector4,inScalar2,"+",vector3);
+        for(psS32 i=0; i<vector4->n; i++)
+        {
+            if(vector4->data.S8[i] != 3 ) {
+                psErrorFlag = true;
+                diag("Unexpected value in return vector[%d]",i);
+            }
+        }
+        ok(!psErrorFlag, "psBinaryOp() produced the correct vector");
+        psFree(vector3);
+        psFree(vector4);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        bool psErrorFlag = false;
+        CREATE_AND_SET_IMAGE(image1,S8,1,5,5)
+        psImage* image2 = NULL;
+        psScalar* inScalar3 = psScalarAlloc(2,PS_TYPE_S8);
+
+        image2 = (psImage*)psBinaryOp(image2,inScalar3,"+",image1);
+        for(psS32 i=0; i<image2->numRows; i++)
+        {
+            for(psS32 j=0; j<image2->numCols; j++) {
+                if(image2->data.S8[i][j] != 3 ) {
+                    psErrorFlag = true;
+                    diag("Unexpected value in return image[%d][%d]",i,j);
+                }
+            }
+        }
+        ok(!psErrorFlag, "psBinaryOp() produced the correct matrix");
+        psFree(image1);
+        psFree(image2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        bool psErrorFlag = false;
+        CREATE_AND_SET_VECTOR(vector1,S8,1,5)
+        CREATE_AND_SET_VECTOR(vector2,S8,0,5)
+        psScalar* inScalar1 = psScalarAlloc(2,PS_TYPE_S8);
+
+        vector2 = (psVector*)psBinaryOp(vector2,vector1,"+",inScalar1);
+        for(psS32 i=0; i<vector2->n; i++)
+        {
+            if(vector2->data.S8[i] != 3 ) {
+                psErrorFlag = true;
+                diag("Unexpected value in return vector[%d]",i);
+            }
+        }
+        ok(!psErrorFlag, "psBinaryOp() produced the correct vector");
+        psFree(vector1);
+        psFree(vector2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        bool psErrorFlag = false;
+        CREATE_AND_SET_VECTOR(vector3,S8,1,5);
+        CREATE_AND_SET_VECTOR(vector4,S8,0,3);
+        psScalar* inScalar2 = psScalarAlloc(2,PS_TYPE_S8);
+
+        vector4->type.dimen = PS_DIMEN_TRANSV;
+        vector4 = (psVector*)psBinaryOp(vector4,vector3,"+",inScalar2);
+        for(psS32 i=0; i<vector4->n; i++)
+        {
+            if(vector4->data.S8[i] != 3 ) {
+                psErrorFlag = true;
+                diag("Unexpected value in return vector[%d]",i);
+            }
+        }
+        ok(!psErrorFlag, "psBinaryOp() produced the correct vector");
+        psFree(vector3);
+        psFree(vector4);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    if (1) {
+        psMemId id = psMemGetId();
+        bool psErrorFlag = false;
+        CREATE_AND_SET_IMAGE(image1,S8,1,5,5);
+        psImage* image2 = NULL;
+        psScalar* inScalar3 = psScalarAlloc(2,PS_TYPE_S8);
+
+        image2 = (psImage*)psBinaryOp(image2,image1,"+",inScalar3);
+        for(psS32 i=0; i<image2->numRows; i++) {
+            for(psS32 j=0; j<image2->numCols; j++) {
+                if(image2->data.S8[i][j] != 3 ) {
+                    psErrorFlag = true;
+                    diag("Unexpected value in return image[%d][%d]",i,j);
+                }
+            }
+        }
+        ok(!psErrorFlag, "psBinaryOp() produced the correct matrix");
+        psFree(image1);
+        psFree(image2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testBinOpScalarBoth
+    // This test fails.  It shouldn't.
+    if (0) {
+        psMemId id = psMemGetId();
+        psScalar* inScalar1 = psScalarAlloc(1,PS_TYPE_S8);
+        psScalar* inScalar2 = psScalarAlloc(2,PS_TYPE_S8);
+        psScalar* outScalar = psScalarAlloc(4,PS_TYPE_S8);
+
+        outScalar = (psScalar*)psBinaryOp(outScalar,inScalar1,"+",inScalar2);
+        ok(outScalar->data.S8 == 3, "psBinaryOp() produced the correct result");
+        psFree(outScalar);
+        psFree(inScalar1);
+        psFree(inScalar2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // This test fails.  It shouldn't.
+    if (0) {
+        psMemId id = psMemGetId();
+        psScalar* inScalar3 = psScalarAlloc(10,PS_TYPE_S8);
+        psScalar* inScalar4 = psScalarAlloc(20,PS_TYPE_S8);
+        psScalar* outScalar1 = NULL;
+
+        outScalar1 = (psScalar*)psBinaryOp(outScalar1,inScalar3,"+",inScalar4);
+        ok(outScalar1->data.S8 == 30, "psBinaryOp() produced the correct result");
+        psFree(outScalar1);
+        psFree(inScalar3);
+        psFree(inScalar4);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // This test fails.  It shouldn't.
+    // testUnaryOpScalar
+    if (0) {
+        psMemId id = psMemGetId();
+        psScalar* inScalar = psScalarAlloc(-1,PS_TYPE_F32);
+        psScalar* outScalar = NULL;
+
+        outScalar = (psScalar*)psUnaryOp(outScalar,inScalar,"abs");
+        ok(outScalar->data.F32 == 1, "psUnaryOp() produced the correct result");
+        psFree(outScalar);
+        psFree(inScalar);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMinimizeLMM.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMinimizeLMM.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMinimizeLMM.c	(revision 22322)
@@ -0,0 +1,274 @@
+/*****************************************************************************
+    This routine must ensure that psMinimizeLM() works correctly.
+ 
+    XXX: This code needs a lot of additional test case work.  The minimization
+	 currently fails and we don't attempt to check the output values.
+    XXX: Add tests for
+	covar arg set to non-NULL
+	constraint set to non-NULL
+        Set x->vectors to NULL, or use wrong types
+        yWt (errors) vector set to incorrect size, type.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_ITERATIONS 100
+#define ERR_TOL 1e-6
+#define NUM_DATA_POINTS 300
+#define NUM_PARAMS 3
+#define VERBOSE 0
+float expectedParm[NUM_PARAMS];
+
+// y = p2 + p0 * e^( x0^2 + x1^2 / (2 * p1^2) )
+float function(const psVector *params, const psVector *x)
+{
+    return params->data.F32[0] *
+           expf(- (PS_SQR(x->data.F32[0]) + PS_SQR(x->data.F32[1]) / 2.0 / PS_SQR(params->data.F32[1]))) +
+           params->data.F32[2];
+}
+
+void derivatives(psVector *deriv, const psVector *params, const psVector *x)
+{
+    deriv->data.F32[0] = expf(- (PS_SQR(x->data.F32[0]) + PS_SQR(x->data.F32[1]) / 2.0 / PS_SQR(params->data.F32[1])));
+    deriv->data.F32[1] = params->data.F32[0] * (PS_SQR(x->data.F32[0]) + PS_SQR(x->data.F32[1]) / 2.0 / PS_SQR(params->data.F32[1])) / params->data.F32[1] / params->data.F32[1] / params->data.F32[1] * expf(- (PS_SQR(x->data.F32[0]) + PS_SQR(x->data.F32[1]) / 2.0 / PS_SQR(params->data.F32[1])));
+    deriv->data.F32[2] = 1.0;
+}
+
+
+psF32 fitFunc(psVector *deriv,
+              psVector *params,
+              psVector *x)
+{
+    if ((deriv == NULL) || (params == NULL) || (x == NULL)) {
+        psError(PS_ERR_UNKNOWN, true, "deriv or params or x is NULL.\n");
+    }
+
+    derivatives(deriv, params, x);
+    return function(params, x);
+}
+
+
+#define NUM_ITER 10
+#define TOL  20.0
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(34);
+
+
+    // Test psMinimizationAlloc()
+    {
+        psMemId id = psMemGetId();
+        psMinimization *tmp = psMinimizationAlloc(NUM_ITER, TOL);
+        ok(tmp != NULL, "psMinimizationAlloc() returned non-NULL");
+        skip_start(tmp == NULL, 5, "Skipping tests because psMinimizationAlloc() failed");
+        ok(tmp->maxIter == NUM_ITER, "psMinimizationAlloc() properly set ->maxIter");
+        ok(tmp->tol == TOL, "psMinimizationAlloc() properly set ->tol");
+        ok(tmp->value == 0.0, "psMinimizationAlloc() properly set ->value");
+        ok(tmp->iter == 0, "psMinimizationAlloc() properly set ->iter (%d)", tmp->iter);
+        ok(isnan(tmp->lastDelta), "psMinimizationAlloc() properly set ->lastDelta (%f)", tmp->lastDelta);
+        skip_end();
+        psFree(tmp);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psMinConstraintAlloc()
+    {
+        psMemId id = psMemGetId();
+        psMinConstraint *tmp = psMinConstraintAlloc();
+        ok(tmp != NULL, "psMinConstraintAlloc() returned non-NULL");
+        skip_start(tmp == NULL, 2, "Skipping tests because psMinConstraintAlloc() failed");
+        ok(tmp->paramMask == NULL, "psMinConstraintAlloc() properly set ->paramMask");
+        ok(tmp->checkLimits == NULL, "psMinConstraintAlloc() properly set ->checkLimits");
+        psFree(tmp);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psMinimizeLMChi2(): unallowed input parameters.
+    {
+        psMemId id = psMemGetId();
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
+        psMinimization *min = psMinimizationAlloc(NUM_ITERATIONS, ERR_TOL);
+        psArray *ordinates = psArrayAlloc(NUM_DATA_POINTS);
+        psVector *coordinates = psVectorAlloc(NUM_DATA_POINTS, PS_TYPE_F32);
+        psVector *coordinatesF64 = psVectorAlloc(NUM_DATA_POINTS, PS_TYPE_F64);
+        psVector *errors = psVectorAlloc(NUM_DATA_POINTS, PS_TYPE_F32);
+        psVector *params = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psVector *paramsF64 = psVectorAlloc(NUM_PARAMS, PS_TYPE_F64);
+        psVector *trueParams = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+
+        // Test with psMinimization set to NULL
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizeLMChi2(NULL, NULL, params, NULL, ordinates,
+                           coordinates, errors, (psMinimizeLMChi2Func)fitFunc);
+            ok(!tmpBool, "psMinimizeLMChi2() returned FALSE with NULL psMinimization");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Test with params set to NULL
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizeLMChi2(min, NULL, NULL, NULL, ordinates,
+                           coordinates, errors, (psMinimizeLMChi2Func)fitFunc);
+            ok(!tmpBool, "psMinimizeLMChi2() returned FALSE with NULL params");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Test with params wrong type
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizeLMChi2(min, NULL, paramsF64, NULL, ordinates,
+                           coordinates, errors, (psMinimizeLMChi2Func)fitFunc);
+            ok(!tmpBool, "psMinimizeLMChi2() returned FALSE with NULL params");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Test with ordinates (x) set to NULL
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizeLMChi2(min, NULL, params, NULL, NULL,
+                           coordinates, errors, (psMinimizeLMChi2Func)fitFunc);
+            ok(!tmpBool, "psMinimizeLMChi2() returned FALSE with NULL ordinates (x)");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Test with coordinates (y) set to NULL
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizeLMChi2(min, NULL, params, NULL, ordinates,
+                           NULL, errors, (psMinimizeLMChi2Func)fitFunc);
+            ok(!tmpBool, "psMinimizeLMChi2() returned FALSE with NULL coordinates (y)");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Test with coordinates (y) wrong type (F64)
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizeLMChi2(min, NULL, params, NULL, ordinates,
+                           coordinatesF64, errors, (psMinimizeLMChi2Func)fitFunc);
+            ok(!tmpBool, "psMinimizeLMChi2() returned FALSE with coordinates (y) wrong type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Test with ordinates and coordinates wrong size
+        {
+            psMemId id = psMemGetId();
+            coordinates->n--;
+            bool tmpBool = psMinimizeLMChi2(min, NULL, params, NULL, ordinates,
+                           coordinates, errors, (psMinimizeLMChi2Func)fitFunc);
+            ok(!tmpBool, "psMinimizeLMChi2() returned FALSE with NULL psMinimization");
+            coordinates->n++;
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Test with function set to NULL
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizeLMChi2(min, NULL, params, NULL, ordinates,
+                           coordinates, errors, NULL);
+            ok(!tmpBool, "psMinimizeLMChi2() returned FALSE with NULL fit function");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(min);
+        psFree(params);
+        psFree(paramsF64);
+        psFree(trueParams);
+        psFree(ordinates);
+        psFree(coordinates);
+        psFree(coordinatesF64);
+        psFree(errors);
+        psFree(rng);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test psMinimizeLMChi2() with legitimate input values
+    // Currently this fails, and we do not attempt to verify output
+    {
+        psMemId id = psMemGetId();
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
+        psMinimization *min = psMinimizationAlloc(NUM_ITERATIONS, ERR_TOL);
+        psArray *ordinates = psArrayAlloc(NUM_DATA_POINTS);
+        psVector *coordinates = psVectorAlloc(NUM_DATA_POINTS, PS_TYPE_F32);
+        psVector *errors = psVectorAlloc(NUM_DATA_POINTS, PS_TYPE_F32);
+        psVector *params = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psVector *trueParams = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        bool tmpBool;
+        trueParams->data.F32[0] = 100.0;    // Normalisation
+        trueParams->data.F32[1] = 3.0;      // Width
+        trueParams->data.F32[2] = 10.0;     // Background
+
+        // Set parameters
+        for (long i = 0; i < NUM_PARAMS; i++)
+        {
+            // Ensure we're not starting right on the true value:
+            params->data.F32[i] = trueParams->data.F32[i] *
+                                  (1.0 - psRandomGaussian(rng) / 10.0);
+        }
+
+        for (long i = 0; i < NUM_DATA_POINTS; i++)
+        {
+            psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+            x->data.F32[0] = 10.0 * psRandomUniform(rng) - 5.0;
+            x->data.F32[1] = 10.0 * psRandomUniform(rng) - 5.0;
+            ordinates->data[i] = x;
+            // Add some noise
+            coordinates->data.F32[i] = function(params, x) + 0.1 * (2.0 * psRandomGaussian(rng) - 1.0);
+            errors->data.F32[i] = 0.1;
+            if (VERBOSE) {
+                printf("Data %ld: (%f, %f) --> %f\n",
+                       i, x->data.F32[0], x->data.F32[1], coordinates->data.F32[i]);
+            }
+        }
+
+        tmpBool = psMinimizeLMChi2(min, NULL, params, NULL, ordinates, coordinates, errors,
+                                   (psMinimizeLMChi2Func)fitFunc);
+        ok(tmpBool, "psMinimizeLMChi2() suceeded");
+        skip_start(!tmpBool, 4, "Skipping tests because psMinimizeLMChi2() failed");
+
+        printf("Minimisation took %d iterations\n", min->iter);
+        printf("chi^2 at the minimum is %.3g\n", min->value);
+        for (long i = 0; i < NUM_PARAMS; i++)
+        {
+            printf("Parameter %ld at the minimum is %.3f, expected: %f\n", i,
+                   params->data.F32[i], trueParams->data.F32[i]);
+        }
+        float diff = 0.0;
+        for (long i = 0; i < NUM_DATA_POINTS; i++)
+        {
+            psVector *x = ordinates->data[i];
+            float fitted = function(trueParams, x);
+            float expected = function(params, x);
+            diff += (fitted - expected) / fabsf(expected);
+            if (VERBOSE) {
+                printf("Data point %ld: Fitted: %f, expected: %f\n", i, fitted, expected);
+            }
+        }
+        printf("Mean relative difference is %f\n", diff/(float)NUM_DATA_POINTS);
+        skip_end();
+        psFree(min);
+        psFree(params);
+        psFree(trueParams);
+        psFree(ordinates);
+        psFree(coordinates);
+        psFree(errors);
+        psFree(rng);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMinimizePowell.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMinimizePowell.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psMinimizePowell.c	(revision 22322)
@@ -0,0 +1,186 @@
+/*****************************************************************************
+    This routine must ensure that psMinimizePowell() works correctly.
+ 
+    XXX: This needs extensive work
+    XXX: We test with a NULL and non-NULL paramMask, however, the mask
+         has all zero values.
+    XXX: The tests that should generate errors are if'ed out.
+    XXX: psMinimizeChi2Powell() is untested.
+    XXX: Also, the test currently fails because of memory corruption in
+         psMinimizePowell().
+    XXX: The unallowed input parameter tests could be more extensive
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define N 5
+#define MIN_VALUE 20.0
+#define NUM_PARAMS 10
+#define ERROR_TOLERANCE 0.10
+float expectedParm[NUM_PARAMS];
+psS32 testStatus = true;
+
+/*****************************************************************************
+myFunc(): This routine subtracts the associate value in expectedParm[] from
+each parameter and then squares it, then sums that for all parameters, then
+adds MIN_VALUE to it.  The minimum for this function will be MIN_VALUE, and
+will occur when each parameter equals the associated value in expectedParm[].
+ 
+This procedure ignores the coordinates, other than to ensure that they were
+passed correctly from psMinimizePowell().
+ *****************************************************************************/
+float myFunc(psVector *myParams,
+             psArray *myCoords)
+{
+    float sum = 0.0;
+    float coordData = 0.0;
+    float expData = 0.0;
+    psS32 i;
+
+    //
+    // Simply ensure that the coordinate data was passed correctly.
+    //
+    for (i=0;i<N;i++) {
+        coordData = ((psVector *) (myCoords->data[i]))->data.F32[0];
+        expData = (float) (i+10);
+        if (fabs(coordData - expData) > FLT_EPSILON) {
+            printf("ERROR(1): coordinate data was incorrectly passed to myFunc()\n");
+            printf("ERROR(1): was (%f) should be (%f)\n", coordData, expData);
+            testStatus = false;
+        }
+        coordData = ((psVector *) (myCoords->data[i]))->data.F32[1];
+        expData = (float) (i+3);
+        if (fabs(coordData - expData) > FLT_EPSILON) {
+            printf("ERROR(2): coordinate data was incorrectly passed to myFunc()\n");
+            printf("ERROR(2): was (%f) should be (%f)\n", coordData, expData);
+            testStatus = false;
+        }
+    }
+
+
+    sum = 0.0;
+    for (i=0;i<NUM_PARAMS;i++) {
+        sum+= (myParams->data.F32[i] - expectedParm[i]) * (myParams->data.F32[i] - expectedParm[i]);
+    }
+    sum = MIN_VALUE + (sum * sum);
+
+    return(sum);
+}
+
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    plan_tests(8);
+
+    // Check for various errors on unallowed input parameters
+    {
+        psVector *myParams = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psVector *myParamMask = psVectorAlloc(NUM_PARAMS, PS_TYPE_U8);
+        psMinimization *min = psMinimizationAlloc(100, 0.01);
+        psArray *myCoords = psArrayAlloc(N);
+
+        // Following should generate error for NULL minimize
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizePowell(NULL, myParams, myParamMask, myCoords,
+                                           (psMinimizePowellFunc) myFunc);
+            ok(!tmpBool, "psMinimizePowell() returned FALSE with NULL psMinimize param");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Following should generate error for NULL parameter vector
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizePowell(min, NULL, myParamMask, myCoords,(psMinimizePowellFunc) myFunc);
+            ok(!tmpBool, "psMinimizePowell() returned FALSE with NULL parameter param");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Following should generate error for NULL coords
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizePowell(min, myParams, myParamMask, NULL, (psMinimizePowellFunc) myFunc);
+            ok(!tmpBool, "psMinimizePowell() returned FALSE with NULL coords param");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Following should generate error for NULL function
+        {
+            psMemId id = psMemGetId();
+            bool tmpBool = psMinimizePowell(min, myParams, myParamMask, myCoords, NULL);
+            ok(!tmpBool, "psMinimizePowell() returned FALSE with NULL function param");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(myParams);
+        psFree(myParamMask);
+        psFree(min);
+        psFree(myCoords);
+    }
+
+
+    // Powell minimize with parameter mask
+    // XXX: This function aborts with a memory corruption error at around line
+    // 60 of psMinimizePowell.c, at the psFree(v):
+    //    if (fabs(baseFuncVal - currFuncVal) <= min->tol) {
+    //        psFree(v);
+    //        psFree(pQP);
+    if (0) {
+        psMemId id = psMemGetId();
+        psVector *myParams = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psMinimization *min = psMinimizationAlloc(100, 0.01);
+        psArray *myCoords = psArrayAlloc(N);
+        psVector *myParamMask = psVectorAlloc(NUM_PARAMS, PS_TYPE_U8);
+
+        for (psS32 i=0;i<N;i++)
+        {
+            myCoords->data[i] = (psPtr *) psVectorAlloc(2, PS_TYPE_F32);
+            ((psVector *) (myCoords->data[i]))->data.F32[0] = (float) (i+10);
+            ((psVector *) (myCoords->data[i]))->data.F32[1] = (float) (i+3);
+        }
+        for (psS32 i=0;i<NUM_PARAMS;i++)
+        {
+            expectedParm[i] = 2.32 + (float) (2 * i);
+            myParams->data.F32[i] = 0.0;
+            myParams->data.F32[i] = (float) i;
+            myParamMask->data.U8[i] = 0;
+        }
+
+        bool tmpBool = psMinimizePowell(min, myParams, NULL, myCoords,
+                                        (psMinimizePowellFunc) myFunc);
+        ok(tmpBool, "psMinimizePowell() returned sucessfully");
+        skip_start(!tmpBool, 0, "Skipping tests because psMinimizePowell() failed");
+
+        printf("\nThe minimum is %f (expected: %f)\n", min->value, MIN_VALUE);
+        for (psS32 i=0;i<NUM_PARAMS;i++)
+        {
+            printf("Parameter %d at the minimum is %.1f (expected: %.1f)\n", i,
+                   myParams->data.F32[i], expectedParm[i]);
+
+            if (fabs(myParams->data.F32[i] - expectedParm[i]) > fabs(ERROR_TOLERANCE * expectedParm[i])) {
+                printf("ERROR: Parameter %d: (%.1f), expected was (%.1f)\n",
+                       i, myParams->data.F32[i], expectedParm[i]);
+                testStatus = false;
+            } else {
+                printf("Parameter %d: (%.1f), expected was (%.1f)\n",
+                       i, myParams->data.F32[i], expectedParm[i]);
+            }
+        }
+
+        psFree(myCoords);
+        psFree(myParams);
+        psFree(myParamMask);
+        psFree(min);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // XXX: Add tests with active parameter mask
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit1D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit1D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit1D.c	(revision 22322)
@@ -0,0 +1,744 @@
+/*****************************************************************************
+This routine must ensure that various psLib functions which fit 1D polynomials
+to data work correctly.  There is a function genericTest() which creates
+vectors of data points (x and f), and populates them with the values from an
+arbitrary function setData().  It then calls appropriate 1D fitting function.
+It then evaluates the polynomial with the coefficients generated above and
+determines if they are within an error tolerance of the expected values.
+ 
+XXX: Try null stats.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_DATA 100
+#define POLY_ORDER 5
+#define A 2.0
+#define B 3.0
+#define C 4.0
+#define D 5.0
+#define E 6.0
+#define ERROR_TOLERANCE 0.10
+#define YERR 10.0
+#define VERBOSE 0
+#define EXTRA_VERBOSE 0
+#define NUM_ITERATIONS 5
+#define CLIP_SIGMA 4.0
+#define OUTLIERS true
+#define MASK_VALUE 1
+
+#define TS00_F_NULL  0x00000001
+#define TS00_F_F32  0x00000002
+#define TS00_F_F64  0x00000004
+#define TS00_F_S32  0x00000008
+#define TS00_X_NULL  0x00000010
+#define TS00_X_F32  0x00000020
+#define TS00_X_F64  0x00000040
+#define TS00_X_S32  0x00000080
+#define TS00_FERR_NULL  0x00000100
+#define TS00_FERR_F32  0x00000200
+#define TS00_FERR_F64  0x00000400
+#define TS00_FERR_S32  0x00000800
+#define TS00_MASK_NULL  0x00001000
+#define TS00_MASK_U8  0x00002000
+#define TS00_MASK_S32  0x00004000
+#define TS00_POLY_ORD  0x00008000
+#define TS00_POLY_CHEB  0x00010000
+#define TS00_CLIP_FIT  0x00020000
+
+psF32 setData(psF32 x)
+{
+    return(A + (B * x) + (C * x * x) + (D * x * x * x) + (E * x * x * x * x));
+}
+
+bool genericTest(
+    psU32 flags,
+    psS32 polyOrder,
+    psS32 numData,
+    bool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    bool testStatus = true;
+    psS32 memLeaks = 0;
+    psPolynomial1D *myPoly = NULL;
+    psVector *x = NULL;
+    psVector *f = NULL;
+    psVector *mask = NULL;
+    psVector *fErr = NULL;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    stats->clipSigma = CLIP_SIGMA;
+    stats->clipIter = NUM_ITERATIONS;
+    stats->options |= PS_STAT_SAMPLE_STDEV;
+
+    if (VERBOSE)
+        printf("psMinimize functions: 1D Polynomial Fitting Functions");
+
+    if (expectedRC == false && VERBOSE) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (VERBOSE) {
+        if (flags & TS00_CLIP_FIT) {
+            printf("        performing a clip-fit\n");
+        } else {
+            printf("        performing a non clip-fit\n");
+        }
+    }
+
+    if (flags & TS00_POLY_ORD) {
+        if (VERBOSE)
+            printf(" using ordinary polynomials\n");
+        myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, polyOrder);
+    }
+
+    if (flags & TS00_POLY_CHEB) {
+        if (VERBOSE)
+            printf(" using chebyshev polynomials\n");
+        myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, polyOrder);
+    }
+
+    if (flags & TS00_X_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL x vector\n");
+        numData = 30;
+    }
+
+    psVector *xTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *fTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using a known seed
+    for (int i = 0; i < numData; i++) {
+        xTruth->data.F64[i] = (flags & TS00_X_NULL) ? i : 2.0*psRandomUniform(rng) - 1.0;
+        fTruth->data.F64[i] = setData(xTruth->data.F64[i]);
+    }
+    if (flags & TS00_X_NULL && flags & TS00_POLY_CHEB) {
+        // Renormalise the indices
+        p_psNormalizeVectorRange(xTruth, -1.0, 1.0);
+    }
+    psFree(rng);
+    if (EXTRA_VERBOSE)
+        for (int i = 0; i < numData; i++) {
+            printf("Original %d: %f\t%f\n", i, xTruth->data.F64[i], fTruth->data.F64[i]);
+        }
+
+    if (flags & TS00_X_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F32);
+
+        #if 0
+
+        if (flags & TS00_POLY_CHEB) {
+            p_psNormalizeVectorRange(x, -1.0, 1.0);
+        }
+        #endif
+
+    }
+
+    if (flags & TS00_X_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_S32);
+
+        #if 0
+
+        if (flags & TS00_POLY_CHEB) {
+            p_psNormalizeVectorRange(x, -1, 1);
+        }
+        #endif
+
+    }
+
+    if (flags & TS00_X_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F64);
+
+        #if 0
+
+        if (flags & TS00_POLY_CHEB) {
+            p_psNormalizeVectorRange(x, -1.0, 1.0);
+        }
+        #endif
+
+    }
+
+    if (flags & TS00_F_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL f vector\n");
+    }
+
+    if (flags & TS00_F_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F32[numData/4]*= 2.0;
+            f->data.F32[numData/2]*= 2.0;
+            f->data.F32[3*numData/4]*= 2.0;
+        }
+    }
+
+    if (flags & TS00_F_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_S32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.S32[numData/4]*= 2.0;
+            f->data.S32[numData/2]*= 2.0;
+            f->data.S32[3*numData/4]*= 2.0;
+        }
+
+    }
+
+    if (flags & TS00_F_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F64);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F64[numData/4]*= 2.0;
+            f->data.F64[numData/2]*= 2.0;
+            f->data.F64[3*numData/4]*= 2.0;
+        }
+    }
+
+    if (flags & TS00_FERR_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL fErr vector\n");
+    }
+
+    if (flags & TS00_FERR_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F32[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_S32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.S32[i] = (psS32) YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F64);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F64[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_MASK_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL mask vector\n");
+    }
+
+    if (flags & TS00_MASK_U8) {
+        if (VERBOSE)
+            printf(" using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = 0;
+        }
+    }
+
+    if (flags & TS00_MASK_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = 0;
+        }
+    }
+
+    bool rc = false;
+    if (flags & TS00_CLIP_FIT) {
+        rc = psVectorClipFitPolynomial1D(myPoly, stats, mask, MASK_VALUE, f, fErr, x);
+    } else {
+        rc = psVectorFitPolynomial1D(myPoly, mask, MASK_VALUE, f, fErr, x);
+    }
+
+    if (!rc) {
+        if (expectedRC == true) {
+            diag("TEST ERROR: the 1D polynomial fitting function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            diag("TEST ERROR: the 1D polynomial fitting function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<polyOrder+1;i++) {
+                printf("Polynomial coefficient %d is %0.1f\n", i, myPoly->coeff[i]);
+            }
+        }
+
+        psVector *result = psPolynomial1DEvalVector(myPoly, xTruth);
+        for (psS32 i=0; i<numData; i++) {
+            // Skip the outliers.
+            if ((i == numData/4) || (i == numData/2) || (i == 3*numData/4)) {
+                continue;
+            }
+            psF32 expectData = fTruth->data.F64[i];
+            psF32 actualData = result->data.F64[i];
+            if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
+                diag("TEST ERROR: Fitted data %d: %.1f --> %.1f vs %.1f\n",
+                     i, xTruth->data.F64[i], actualData, expectData);
+                testStatus = false;
+            } else {
+                if (EXTRA_VERBOSE) {
+                    printf("GOOD: Fitted data %d: %1.f --> %.1f vs %.1f\n",
+                           i, xTruth->data.F64[i], actualData, expectData);
+                }
+            }
+        }
+        psFree(result);
+    }
+
+    psMemCheckCorruption(stderr, false);
+    psFree(myPoly);
+    psFree(mask);
+    psFree(x);
+    psFree(f);
+    psFree(xTruth);
+    psFree(fTruth);
+    psFree(fErr);
+    psFree(stats);
+    psMemCheckCorruption(stderr, false);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    return (testStatus);
+}
+
+/*****************************************************************************
+We test a variety of polynomial fitting routines, types, and polynomial types
+here:
+    F32 tests: Ordinary polys, non-clip fit
+    F64 tests: Ordinary polys, non-clip fit
+    F32 tests: Chebyshev polys, non-clip fit
+    F64 tests: Chebyshev polys, non-clip fit
+    F32 tests: Ordinary polys, clip fit
+    F64 tests: Ordinary polys, clip fit
+    F32 tests: Chebyshev polys, clip fit
+    F64 tests: Chebyshev polys, clip fit
+ *****************************************************************************/
+int main()
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(104);
+
+
+    // psVectorFitPolynomial1D()
+    // Test various erroneous input paramater configurations
+    {
+        psMemId id = psMemGetId();
+        psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, POLY_ORDER);
+        psVector *x = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *xS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *f = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *mask = psVectorAlloc(NUM_DATA, PS_TYPE_U8);
+        psVector *maskS8 = psVectorAlloc(NUM_DATA, PS_TYPE_S8);
+        psVector *fErr = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fErrS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+
+
+        // Set psPolynomial1D to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial1D(NULL, mask, MASK_VALUE, f, fErr, x);
+            ok(rc == false, "psVectorFitPolynomial1D() returned FALSE with NULL psPolynomial1D");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set mask to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial1D(myPoly, maskS8, MASK_VALUE, f, fErr, x);
+            ok(rc == false, "psVectorFitPolynomial1D() returned FALSE with mask set to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set f psVector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial1D(myPoly, mask, MASK_VALUE, fS32, fErr, x);
+            ok(rc == false, "psVectorFitPolynomial1D() returned FALSE: Set f psVector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set fError vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial1D(myPoly, mask, MASK_VALUE, f, fErrS32, x);
+            ok(rc == false, "psVectorFitPolynomial1D() returned FALSE: Set fError vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set x vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial1D(myPoly, mask, MASK_VALUE, f, fErr, xS32);
+            ok(rc == false, "psVectorFitPolynomial1D() returned FALSE: Set x vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect mask psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            mask->n++;
+            bool rc = psVectorFitPolynomial1D(myPoly, mask, MASK_VALUE, f, fErr, x);
+            mask->n--;
+            ok(rc == false, "psVectorFitPolynomial1D() returned FALSE: Incorrect mask psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect f psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            f->n++;
+            bool rc = psVectorFitPolynomial1D(myPoly, mask, MASK_VALUE, f, fErr, x);
+            f->n--;
+            ok(rc == false, "psVectorFitPolynomial1D() returned FALSE: Incorrect f psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect fErr psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            fErr->n++;
+            bool rc = psVectorFitPolynomial1D(myPoly, mask, MASK_VALUE, f, fErr, x);
+            fErr->n--;
+            ok(rc == false, "psVectorFitPolynomial1D() returned FALSE: Incorrect fErr psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect x psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            x->n++;
+            bool rc = psVectorFitPolynomial1D(myPoly, mask, MASK_VALUE, f, fErr, x);
+            x->n--;
+            ok(rc == false, "psVectorFitPolynomial1D() returned FALSE: Incorrect x psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(myPoly);
+        psFree(x);
+        psFree(xS32);
+        psFree(f);
+        psFree(fS32);
+        psFree(mask);
+        psFree(maskS8);
+        psFree(fErr);
+        psFree(fErrS32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorClipFitPolynomial1D()
+    // Test various erroneous input paramater configurations
+    {
+        psMemId id = psMemGetId();
+        psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, POLY_ORDER);
+        psVector *x = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *xS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *f = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *mask = psVectorAlloc(NUM_DATA, PS_TYPE_U8);
+        psVector *maskS8 = psVectorAlloc(NUM_DATA, PS_TYPE_S8);
+        psVector *fErr = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fErrS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+
+        // Set psPolynomial1D to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial1D(NULL, stats, mask, MASK_VALUE, f, fErr, x);
+            ok(rc == false, "psVectorClipFitPolynomial1D() returned FALSE with NULL psPolynomial1D");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set psStats to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial1D(myPoly, NULL, mask, MASK_VALUE, f, fErr, x);
+            ok(rc == false, "psVectorClipFitPolynomial1D() returned FALSE with NULL psStats");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set mask to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial1D(myPoly, stats, maskS8, MASK_VALUE, f, fErr, x);
+            ok(rc == false, "psVectorClipFitPolynomial1D() returned FALSE with mask set to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set f psVector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial1D(myPoly, stats, mask, MASK_VALUE, fS32, fErr, x);
+            ok(rc == false, "psVectorClipFitPolynomial1D() returned FALSE: Set f psVector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set fError vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial1D(myPoly, stats, mask, MASK_VALUE, f, fErrS32, x);
+            ok(rc == false, "psVectorClipFitPolynomial1D() returned FALSE: Set fError vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set x vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial1D(myPoly, stats, mask, MASK_VALUE, f, fErr, xS32);
+            ok(rc == false, "psVectorClipFitPolynomial1D() returned FALSE: Set x vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect mask psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            mask->n++;
+            bool rc = psVectorClipFitPolynomial1D(myPoly, stats, mask, MASK_VALUE, f, fErr, x);
+            mask->n--;
+            ok(rc == false, "psVectorClipFitPolynomial1D() returned FALSE: Incorrect mask psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect f psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            f->n++;
+            bool rc = psVectorClipFitPolynomial1D(myPoly, stats, mask, MASK_VALUE, f, fErr, x);
+            f->n--;
+            ok(rc == false, "psVectorClipFitPolynomial1D() returned FALSE: Incorrect f psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect fErr psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            fErr->n++;
+            bool rc = psVectorClipFitPolynomial1D(myPoly, stats, mask, MASK_VALUE, f, fErr, x);
+            fErr->n--;
+            ok(rc == false, "psVectorClipFitPolynomial1D() returned FALSE: Incorrect fErr psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect x psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            x->n++;
+            bool rc = psVectorClipFitPolynomial1D(myPoly, stats, mask, MASK_VALUE, f, fErr, x);
+            x->n--;
+            ok(rc == false, "psVectorClipFitPolynomial1D() returned FALSE: Incorrect x psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(myPoly);
+        psFree(x);
+        psFree(xS32);
+        psFree(f);
+        psFree(fS32);
+        psFree(mask);
+        psFree(maskS8);
+        psFree(fErr);
+        psFree(fErrS32);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    //
+    // F32 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit, Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit, Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit, Some Vectors NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit, Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit, F Vector NULL");
+    // Unallowable vector types
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit, Unallowable vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_S32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit, Unallowable vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_S32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit, Unallowable vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_S32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit, Unallowable vector types");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit, Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit, Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit, Mismatch vector types");
+
+    //
+    // F64 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit, Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit, Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit, Some Vectors NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit, Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit, F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit, Mismatch data type");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit, Mismatch data type");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit, Mismatch data type");
+
+
+    //
+    // F32 tests: Chebyshev polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, non-clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, non-clip fit.  Some Vectors NULL.");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, non-clip fit.  Some Vectors NULL.");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, non-clip fit.  Some Vectors NULL.");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, non-clip fit.  Some Vectors NULL.");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_NULL | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, false), "F32 tests: Chebyshev polys, non-clip fit.  F Vector NULL.");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, false), "F32 tests: Chebyshev polys, non-clip fit.  Mismatch vector types.");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, non-clip fit.  Mismatch vector types.");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, non-clip fit.  Mismatch vector types.");
+
+
+    //
+    // F64 tests: Chebyshev polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F64 tests: Chebyshev polys, non-clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F64 tests: Chebyshev polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F64 tests: Chebyshev polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F64 tests: Chebyshev polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F64 tests: Chebyshev polys, non-clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_NULL | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, false), "F64 tests: Chebyshev polys, non-clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, false), "F64 tests: Chebyshev polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F64 tests: Chebyshev polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true), "F64 tests: Chebyshev polys, non-clip fit.  Mismatch vector types");
+
+    //
+    // F32 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, clip fit");
+    // Some Vectors NULL
+    //    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    //    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    //    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+
+    //
+    // F64 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD
+                   | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true),
+       "F64 tests: Ordinary polys, clip fit");
+    // Some Vectors NULL
+    //    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    //    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_NULL | TS00_POLY_ORD
+                   | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false),
+       "F64 tests: Ordinary polys, clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+
+
+    //
+    // F32 tests: Chebyshev polys, clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, clip fit");
+    // Some Vectors NULL
+    //    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, clip fit.  Some Vectors NULL");
+    //    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, clip fit.  Some Vectors NULL");
+    //    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F32 tests: Chebyshev polys, clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_NULL | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F32 tests: Chebyshev polys, clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F32 tests: Chebyshev polys, clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F32 tests: Chebyshev polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F32 tests: Chebyshev polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F32 tests: Chebyshev polys, clip fit.  Mismatch vector types");
+
+
+    //
+    // F64 tests: Chebyshev polys, clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F64 tests: Chebyshev polys, clip fit");
+    // Some Vectors NULL
+    //    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F64 tests: Chebyshev polys, clip fit.  Some Vectors NULL");
+    //    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true), "F64 tests: Chebyshev polys, clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_NULL | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F64 tests: Chebyshev polys, clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F64 tests: Chebyshev polys, clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F64 tests: Chebyshev polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F64 tests: Chebyshev polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false), "F64 tests: Chebyshev polys, clip fit.  Mismatch vector types");
+
+    // Note: memory leaks tests are performed in the genericTest() subroutine.
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit2D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit2D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit2D.c	(revision 22322)
@@ -0,0 +1,713 @@
+/*****************************************************************************
+This routine must ensure that various psLib functions which fit 2D polynomials
+to data work correctly.  There is a function genericTest() which creates
+vectors of data points (x and f), and populates them with the values from an
+arbitrary function setData().  It then calls appropriate 2D fitting function.
+It then evaluates the polynomial with the coefficients generated above and
+determines if they are within an error tolerance of the expected values.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_DATA 100
+#define POLY_ORDER_X 2
+#define POLY_ORDER_Y 2
+#define A 2.0
+#define B 3.0
+#define C 4.0
+#define D 5.0
+#define E 6.0
+#define F 4.0
+#define ERROR_TOLERANCE 0.10
+#define YERR 10.0
+#define VERBOSE 0
+#define EXTRA_VERBOSE 0
+#define NUM_ITERATIONS 5
+#define CLIP_SIGMA 4.0
+#define OUTLIERS true
+#define MASK_VALUE 1
+
+#define TS00_F_NULL  0x00000001
+#define TS00_F_F32  0x00000002
+#define TS00_F_F64  0x00000004
+#define TS00_F_S32  0x00000008
+#define TS00_X_NULL  0x00000010
+#define TS00_X_F32  0x00000020
+#define TS00_X_F64  0x00000040
+#define TS00_X_S32  0x00000080
+#define TS00_FERR_NULL  0x00000100
+#define TS00_FERR_F32  0x00000200
+#define TS00_FERR_F64  0x00000400
+#define TS00_FERR_S32  0x00000800
+#define TS00_MASK_NULL  0x00001000
+#define TS00_MASK_U8  0x00002000
+#define TS00_MASK_S32  0x00004000
+#define TS00_POLY_ORD  0x00008000
+#define TS00_POLY_CHEB  0x00010000
+#define TS00_CLIP_FIT  0x00020000
+#define TS00_Y_NULL  0x00100000
+#define TS00_Y_F32  0x00200000
+#define TS00_Y_F64  0x00400000
+#define TS00_Y_S32  0x00800000
+
+psF32 setData(psF32 x, psF32 y)
+{
+    return(A + (B * x) + (C * x * x) + (D * y) + (E * y * y) + (F * x * y));
+}
+
+psS32 genericTest(
+    psU32 flags,
+    psS32 polyOrderX,
+    psS32 polyOrderY,
+    psS32 numData,
+    bool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    psS32 testStatus = true;
+    psS32 memLeaks = 0;
+    psPolynomial2D *myPoly = NULL;
+    psVector *x = NULL;
+    psVector *y = NULL;
+    psVector *f = NULL;
+    psVector *mask = NULL;
+    psVector *fErr = NULL;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    stats->clipSigma = CLIP_SIGMA;
+    stats->clipIter = NUM_ITERATIONS;
+    stats->options |= PS_STAT_SAMPLE_STDEV;
+
+    if (VERBOSE)
+        printf("psMinimize functions: 2D Polynomial Fitting Functions");
+
+    if (expectedRC == false && VERBOSE) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (VERBOSE) {
+        if (flags & TS00_CLIP_FIT) {
+            printf(" performing a clip-fit\n");
+        } else {
+            printf(" performing a non clip-fit\n");
+        }
+    }
+
+    if (flags & TS00_POLY_ORD) {
+        if (VERBOSE)
+            printf(" using ordinary polynomials\n");
+        myPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, polyOrderX, polyOrderY);
+    }
+
+
+    psVector *xTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *yTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *fTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    xTruth->n = numData;
+    yTruth->n = numData;
+    fTruth->n = numData;
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using an RNG with a known seed
+    for (int i = 0; i < numData; i++) {
+        xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        yTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        fTruth->data.F64[i] = setData(xTruth->data.F64[i], yTruth->data.F64[i]);
+    }
+    psFree(rng);
+
+    if (flags & TS00_X_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL x vector\n");
+    }
+
+    if (flags & TS00_X_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_X_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_X_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_Y_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL y vector\n");
+    }
+
+    if (flags & TS00_Y_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_Y_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_Y_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_F_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL f vector\n");
+    }
+
+    if (flags & TS00_F_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F32[numData/4]*= 2.0;
+            f->data.F32[numData/2]*= 2.0;
+            f->data.F32[3*numData/4]*= 2.0;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%.1f %.1f)\n", i, (psF32) i, f->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_F_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_S32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.S32[numData/4]*= 2.0;
+            f->data.S32[numData/2]*= 2.0;
+            f->data.S32[3*numData/4]*= 2.0;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%.1f %d)\n", i, (psF32) i, f->data.S32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_F_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F64);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F64[numData/4]*= 2.0;
+            f->data.F64[numData/2]*= 2.0;
+            f->data.F64[3*numData/4]*= 2.0;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%.1f %.1f)\n", i, (psF32) i, f->data.F64[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_FERR_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL fErr vector\n");
+    }
+
+    if (flags & TS00_FERR_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F32[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.S32[i] = (psS32) YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F64);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F64[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_MASK_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL mask vector\n");
+    }
+
+    if (flags & TS00_MASK_U8) {
+        if (VERBOSE)
+            printf(" using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = 0;
+        }
+    }
+
+    if (flags & TS00_MASK_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = 0;
+        }
+    }
+
+    bool rc = false;
+    if (flags & TS00_CLIP_FIT) {
+        rc = psVectorClipFitPolynomial2D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y);
+    } else {
+        rc = psVectorFitPolynomial2D(myPoly, mask, MASK_VALUE, f, fErr, x, y);
+    }
+
+    if (!rc) {
+        if (expectedRC == true) {
+            diag("TEST ERROR: the 2D polynomial fitting function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            diag("TEST ERROR: the 2D polynomial fitting function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<polyOrderX+1;i++) {
+                for (psS32 j=0;j<polyOrderY+1;j++) {
+                    printf("Polynomial coefficient [%d][%d] is %0.1f\n", i, j, myPoly->coeff[i][j]);
+                }
+            }
+        }
+
+        psVector *result = psPolynomial2DEvalVector(myPoly, xTruth, yTruth);
+        for (psS32 i=0 ;i<numData; i++) {
+            // Skip the outliers.
+            if ((i == numData/4) || (i == numData/2) || (i == 3*numData/4)) {
+                continue;
+            }
+            psF64 actualData = result->data.F64[i];
+            psF64 expectData = fTruth->data.F64[i];
+
+            if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
+                diag("TEST ERROR: Fitted data %d: (%.1f), expected was (%.1f)\n",
+                     i, actualData, expectData);
+                testStatus = false;
+            } else {
+                if (EXTRA_VERBOSE) {
+                    printf("GOOD: Fitted data %d: (%.1f), expected was (%.1f)\n",
+                           i, actualData, expectData);
+                }
+            }
+        }
+        psFree(result);
+    }
+
+    psMemCheckCorruption(stderr, false);
+    psFree(myPoly);
+    psFree(mask);
+    psFree(xTruth);
+    psFree(yTruth);
+    psFree(fTruth);
+    psFree(x);
+    psFree(y);
+    psFree(f);
+    psFree(fErr);
+    psFree(stats);
+    psMemCheckCorruption(stderr, false);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    return (testStatus);
+}
+
+/*****************************************************************************
+We test a variety of polynomial fitting routines, types, and polynomial types
+here:
+    F32 tests: Ordinary polys, non-clip fit
+    F64 tests: Ordinary polys, non-clip fit
+    F32 tests: Chebyshev polys, non-clip fit
+    F64 tests: Chebyshev polys, non-clip fit
+    F32 tests: Ordinary polys, clip fit
+    F64 tests: Ordinary polys, clip fit
+    F32 tests: Chebyshev polys, clip fit
+    F64 tests: Chebyshev polys, clip fit
+ *****************************************************************************/
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(88);
+
+
+    // psVectorFitPolynomial2D()
+    // Test various erroneous input paramater configurations
+    {
+        psMemId id = psMemGetId();
+        psPolynomial2D *myPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, POLY_ORDER_X, POLY_ORDER_Y);
+        psVector *x = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *xS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *y = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *yS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *f = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *mask = psVectorAlloc(NUM_DATA, PS_TYPE_U8);
+        psVector *maskS8 = psVectorAlloc(NUM_DATA, PS_TYPE_S8);
+        psVector *fErr = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fErrS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+
+
+        // Set psPolynomial2D to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial2D(NULL, mask, MASK_VALUE, f, fErr, x, y);
+            ok(rc == false, "psVectorFitPolynomial2D() returned FALSE with NULL psPolynomial2D");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set mask to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial2D(myPoly, maskS8, MASK_VALUE, f, fErr, x, y);
+            ok(rc == false, "psVectorFitPolynomial2D() returned FALSE with mask set to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set f psVector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial2D(myPoly, mask, MASK_VALUE, fS32, fErr, x, y);
+            ok(rc == false, "psVectorFitPolynomial2D() returned FALSE: Set f psVector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set fError vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial2D(myPoly, mask, MASK_VALUE, f, fErrS32, x, y);
+            ok(rc == false, "psVectorFitPolynomial2D() returned FALSE: Set fError vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set x vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial2D(myPoly, mask, MASK_VALUE, f, fErr, xS32, y);
+            ok(rc == true, "psVectorFitPolynomial2D() returned TRUE: x vector may be S32");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set y vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial2D(myPoly, mask, MASK_VALUE, f, fErr, x, yS32);
+            ok(rc == true, "psVectorFitPolynomial2D() returned TRUE: y vector may be S32");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect mask psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            mask->n++;
+            bool rc = psVectorFitPolynomial2D(myPoly, mask, MASK_VALUE, f, fErr, x, y);
+            mask->n--;
+            ok(rc == false, "psVectorFitPolynomial2D() returned FALSE: Incorrect mask psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect f psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            f->n++;
+            bool rc = psVectorFitPolynomial2D(myPoly, mask, MASK_VALUE, f, fErr, x, y);
+            f->n--;
+            ok(rc == false, "psVectorFitPolynomial2D() returned FALSE: Incorrect f psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect fErr psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            fErr->n++;
+            bool rc = psVectorFitPolynomial2D(myPoly, mask, MASK_VALUE, f, fErr, x, y);
+            fErr->n--;
+            ok(rc == false, "psVectorFitPolynomial2D() returned FALSE: Incorrect fErr psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect x psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            x->n++;
+            bool rc = psVectorFitPolynomial2D(myPoly, mask, MASK_VALUE, f, fErr, x, y);
+            x->n--;
+            ok(rc == false, "psVectorFitPolynomial2D() returned FALSE: Incorrect x psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(myPoly);
+        psFree(x);
+        psFree(xS32);
+        psFree(y);
+        psFree(yS32);
+        psFree(f);
+        psFree(fS32);
+        psFree(mask);
+        psFree(maskS8);
+        psFree(fErr);
+        psFree(fErrS32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorClipFitPolynomial2D()
+    // Test various erroneous input paramater configurations
+    {
+        psMemId id = psMemGetId();
+        psPolynomial2D *myPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, POLY_ORDER_X, POLY_ORDER_Y);
+        psVector *x = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *xS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *y = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *yS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *f = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *mask = psVectorAlloc(NUM_DATA, PS_TYPE_U8);
+        psVector *maskS8 = psVectorAlloc(NUM_DATA, PS_TYPE_S8);
+        psVector *fErr = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fErrS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+
+        // Set psPolynomial2D to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial2D(NULL, stats, mask, MASK_VALUE, f, fErr, x, y);
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE with NULL psPolynomial2D");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set psStats to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial2D(myPoly, NULL, mask, MASK_VALUE, f, fErr, x, y);
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE with NULL psStats");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set mask to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial2D(myPoly, stats, maskS8, MASK_VALUE, f, fErr, x, y);
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE with mask set to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set f psVector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial2D(myPoly, stats, mask, MASK_VALUE, fS32, fErr, x, y);
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE: Set f psVector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set fError vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial2D(myPoly, stats, mask, MASK_VALUE, f, fErrS32, x, y);
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE: Set fError vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set x vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial2D(myPoly, stats, mask, MASK_VALUE, f, fErr, xS32, y);
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE: Set x vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set y vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial2D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, yS32);
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE: Set y vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect mask psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            mask->n++;
+            bool rc = psVectorClipFitPolynomial2D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y);
+            mask->n--;
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE: Incorrect mask psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect f psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            f->n++;
+            bool rc = psVectorClipFitPolynomial2D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y);
+            f->n--;
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE: Incorrect f psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect fErr psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            fErr->n++;
+            bool rc = psVectorClipFitPolynomial2D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y);
+            fErr->n--;
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE: Incorrect fErr psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect x psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            x->n++;
+            bool rc = psVectorClipFitPolynomial2D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y);
+            x->n--;
+            ok(rc == false, "psVectorClipFitPolynomial2D() returned FALSE: Incorrect x psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(myPoly);
+        psFree(x);
+        psFree(xS32);
+        psFree(y);
+        psFree(yS32);
+        psFree(f);
+        psFree(fS32);
+        psFree(mask);
+        psFree(maskS8);
+        psFree(fErr);
+        psFree(fErrS32);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //
+    // F32 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+
+    //
+    // F64 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+
+    //
+    // F32 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F32 tests: Ordinary polys, clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+
+    //
+    // F64 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F64 tests: Ordinary polys, clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit3D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit3D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit3D.c	(revision 22322)
@@ -0,0 +1,797 @@
+/*****************************************************************************
+This routine must ensure that various psLib functions which fit 3D polynomials
+to data work correctly.  There is a function genericTest() which creates
+vectors of data points (x and f), and populates them with the values from an
+arbitrary function setData().  It then calls appropriate 3D fitting function.
+It then evaluates the polynomial with the coefficients generated above and
+determines if they are within an error tolerance of the expected values.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_DATA 100
+#define POLY_ORDER_X 2
+#define POLY_ORDER_Y 3
+#define POLY_ORDER_Z 2
+#define A 100.0
+#define B 2.0
+#define C 3.0
+#define D 4.0
+#define E 5.0
+#define F 4.0
+#define H 3.0
+#define J 3.0
+#define K 1.0
+#define L 5.0
+#define ERROR_TOLERANCE 0.10
+#define YERR 10.0
+#define VERBOSE 0
+#define EXTRA_VERBOSE 0
+#define NUM_ITERATIONS 5
+#define CLIP_SIGMA 4.0
+#define OUTLIERS true
+#define MASK_VALUE 1
+
+#define TS00_F_NULL  0x00000001
+#define TS00_F_F32  0x00000002
+#define TS00_F_F64  0x00000004
+#define TS00_F_S32  0x00000008
+#define TS00_X_NULL  0x00000010
+#define TS00_X_F32  0x00000020
+#define TS00_X_F64  0x00000040
+#define TS00_X_S32  0x00000080
+#define TS00_FERR_NULL  0x00000100
+#define TS00_FERR_F32  0x00000200
+#define TS00_FERR_F64  0x00000400
+#define TS00_FERR_S32  0x00000800
+#define TS00_MASK_NULL  0x00001000
+#define TS00_MASK_U8  0x00002000
+#define TS00_MASK_S32  0x00004000
+#define TS00_POLY_ORD  0x00008000
+#define TS00_POLY_CHEB  0x00010000
+#define TS00_CLIP_FIT  0x00020000
+#define TS00_Y_NULL  0x00100000
+#define TS00_Y_F32  0x00200000
+#define TS00_Y_F64  0x00400000
+#define TS00_Y_S32  0x00800000
+#define TS00_Z_NULL  0x01000000
+#define TS00_Z_F32  0x02000000
+#define TS00_Z_F64  0x04000000
+#define TS00_Z_S32  0x08000000
+
+psF32 setData(psF32 x, psF32 y, psF32 z)
+{
+    if (0) {
+        // Linear case, for testing.
+        return(A + (B * x) + (D * y) + (H * z));
+    } else {
+        return(A + (B * x) + (C * x * x) + (D * y) + (E * y * y) + (F * x * y) + (H * z) +
+               (J * z * z) + (K * x * z) + (L * y * z));
+    }
+}
+
+psS32 genericTest(
+    psU32 flags,
+    psS32 polyOrderX,
+    psS32 polyOrderY,
+    psS32 polyOrderZ,
+    psS32 numData,
+    bool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    psS32 testStatus = true;
+    psS32 memLeaks = 0;
+    psPolynomial3D *myPoly = NULL;
+    psVector *x = NULL;
+    psVector *y = NULL;
+    psVector *z = NULL;
+    psVector *f = NULL;
+    psVector *mask = NULL;
+    psVector *fErr = NULL;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    stats->clipSigma = CLIP_SIGMA;
+    stats->clipIter = NUM_ITERATIONS;
+    stats->options |= PS_STAT_SAMPLE_STDEV;
+
+    if (VERBOSE)
+        printf("psMinimize functions: 3D Polynomial Fitting Functions");
+
+    psVector *xTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *yTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *zTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *fTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    xTruth->n = numData;
+    yTruth->n = numData;
+    zTruth->n = numData;
+    fTruth->n = numData;
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using known seed
+    for (int i = 0; i < numData; i++) {
+        xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        yTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        zTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        fTruth->data.F64[i] = setData(xTruth->data.F64[i], yTruth->data.F64[i], zTruth->data.F64[i]);
+    }
+    psFree(rng);
+
+    if (expectedRC == false && VERBOSE) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (VERBOSE) {
+        if (flags & TS00_CLIP_FIT) {
+            printf(" performing a clip-fit\n");
+        } else {
+            printf(" performing a non clip-fit\n");
+        }
+    }
+
+    if (flags & TS00_POLY_ORD) {
+        if (VERBOSE)
+            printf(" using ordinary polynomials\n");
+        myPoly = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, polyOrderX, polyOrderY, polyOrderZ);
+    }
+
+    if (flags & TS00_X_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL x vector\n");
+    }
+
+    if (flags & TS00_X_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_X_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_X_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F64);
+    }
+
+
+    if (flags & TS00_Y_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL y vector\n");
+    }
+
+    if (flags & TS00_Y_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_Y_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_Y_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_Z_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL z vector\n");
+    }
+
+    if (flags & TS00_Z_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_Z_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_Z_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_F64);
+    }
+
+
+    if (flags & TS00_F_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL f vector\n");
+    }
+
+    if (flags & TS00_F_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F32[numData/4]*= 2.0;
+            f->data.F32[numData/2]*= 2.0;
+            f->data.F32[3*numData/4]*= 2.0;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%.1f)\n", i, f->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_F_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_S32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.S32[numData/4]*= 2.0;
+            f->data.S32[numData/2]*= 2.0;
+            f->data.S32[3*numData/4]*= 2.0;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%d)\n", i, f->data.S32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_F_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F64);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F64[numData/4]*= 2.0;
+            f->data.F64[numData/2]*= 2.0;
+            f->data.F64[3*numData/4]*= 2.0;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%.1f)\n", i, f->data.F64[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_FERR_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL fErr vector\n");
+    }
+
+    if (flags & TS00_FERR_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F32[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_S32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.S32[i] = (psS32) YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F64);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F64[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_MASK_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL mask vector\n");
+    }
+
+    if (flags & TS00_MASK_U8) {
+        if (VERBOSE)
+            printf(" using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = 0;
+        }
+    }
+
+    if (flags & TS00_MASK_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = 0;
+        }
+    }
+
+    bool rc = false;
+    if (flags & TS00_CLIP_FIT) {
+        rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z);
+    } else {
+        rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z);
+    }
+
+    if (!rc) {
+        if (expectedRC == true) {
+            diag("TEST ERROR: the 3D polynomial fitting function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            diag("TEST ERROR: the 3D polynomial fitting function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<polyOrderX+1;i++) {
+                for (psS32 j=0;j<polyOrderY+1;j++) {
+                    for (psS32 k=0;k<polyOrderZ+1;k++) {
+                        printf("Polynomial coefficient [%d][%d][%d] is %0.1f\n", i, j, k, myPoly->coeff[i][j][k]);
+                    }
+                }
+            }
+        }
+
+        psVector *result = psPolynomial3DEvalVector(myPoly, xTruth, yTruth, zTruth);
+        for (psS32 i=0 ;i<numData; i++) {
+            // Skip the outliers.
+            if ((i == numData/4) || (i == numData/2) || (i == 3*numData/4)) {
+                continue;
+            }
+            psF32 expectData = fTruth->data.F64[i];
+            psF32 actualData = result->data.F64[i];
+
+            if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
+                diag("TEST ERROR: Fitted data %d: (%.1f), expected was (%.1f)\n",
+                     i, actualData, expectData);
+                testStatus = false;
+            } else {
+                if (EXTRA_VERBOSE) {
+                    printf("GOOD: Fitted data %d: (%.1f), expected was (%.1f)\n",
+                           i, actualData, expectData);
+                }
+            }
+        }
+        psFree(result);
+    }
+
+    psMemCheckCorruption(stderr, false);
+    psFree(myPoly);
+    psFree(mask);
+    psFree(x);
+    psFree(y);
+    psFree(z);
+    psFree(f);
+    psFree(xTruth);
+    psFree(yTruth);
+    psFree(zTruth);
+    psFree(fTruth);
+    psFree(fErr);
+    psFree(stats);
+    psMemCheckCorruption(stderr, false);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    return (testStatus);
+}
+
+/*****************************************************************************
+We test a variety of polynomial fitting routines, types, and polynomial types
+here:
+    F32 tests: Ordinary polys, non-clip fit
+    F64 tests: Ordinary polys, non-clip fit
+    F32 tests: Chebyshev polys, non-clip fit
+    F64 tests: Chebyshev polys, non-clip fit
+    F32 tests: Ordinary polys, clip fit
+    F64 tests: Ordinary polys, clip fit
+    F32 tests: Chebyshev polys, clip fit
+    F64 tests: Chebyshev polys, clip fit
+ *****************************************************************************/
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(100);
+
+
+    // psVectorFitPolynomial3D()
+    // Test various erroneous input paramater configurations
+    {
+        psMemId id = psMemGetId();
+        psPolynomial3D *myPoly = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z);
+        psVector *x = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *xS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *y = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *yS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *z = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *zS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *f = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *mask = psVectorAlloc(NUM_DATA, PS_TYPE_U8);
+        psVector *maskS8 = psVectorAlloc(NUM_DATA, PS_TYPE_S8);
+        psVector *fErr = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fErrS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+
+
+        // Set psPolynomial3D to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial3D(NULL, mask, MASK_VALUE, f, fErr, x, y, z);
+            ok(rc == false, "psVectorFitPolynomial3D() returned FALSE with NULL psPolynomial3D");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set mask to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial3D(myPoly, maskS8, MASK_VALUE, f, fErr, x, y, z);
+            ok(rc == false, "psVectorFitPolynomial3D() returned FALSE with mask set to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set f psVector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, fS32, fErr, x, y, z);
+            ok(rc == false, "psVectorFitPolynomial3D() returned FALSE: Set f psVector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set fError vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, f, fErrS32, x, y, z);
+            ok(rc == false, "psVectorFitPolynomial3D() returned FALSE: Set fError vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set x vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, f, fErr, xS32, y, z);
+            ok(rc == true, "psVectorFitPolynomial3D() returned TRUE: x vector may be S32");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set y vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, f, fErr, x, yS32, z);
+            ok(rc == true, "psVectorFitPolynomial3D() returned TRUE: y vector may be S32");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set z vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, f, fErr, x, y, zS32);
+            ok(rc == true, "psVectorFitPolynomial3D() returned TRUE: z vector may be S32");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect mask psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            mask->n++;
+            bool rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z);
+            mask->n--;
+            ok(rc == false, "psVectorFitPolynomial3D() returned FALSE: Incorrect mask psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect f psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            f->n++;
+            bool rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z);
+            f->n--;
+            ok(rc == false, "psVectorFitPolynomial3D() returned FALSE: Incorrect f psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect fErr psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            fErr->n++;
+            bool rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z);
+            fErr->n--;
+            ok(rc == false, "psVectorFitPolynomial3D() returned FALSE: Incorrect fErr psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect x psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            x->n++;
+            bool rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z);
+            x->n--;
+            ok(rc == false, "psVectorFitPolynomial3D() returned FALSE: Incorrect x psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(myPoly);
+        psFree(x);
+        psFree(xS32);
+        psFree(y);
+        psFree(yS32);
+        psFree(z);
+        psFree(zS32);
+        psFree(f);
+        psFree(fS32);
+        psFree(mask);
+        psFree(maskS8);
+        psFree(fErr);
+        psFree(fErrS32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorClipFitPolynomial3D()
+    // Test various erroneous input paramater configurations
+    {
+        psMemId id = psMemGetId();
+        psPolynomial3D *myPoly = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z);
+        psVector *x = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *xS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *y = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *yS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *z = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *zS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *f = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *mask = psVectorAlloc(NUM_DATA, PS_TYPE_U8);
+        psVector *maskS8 = psVectorAlloc(NUM_DATA, PS_TYPE_S8);
+        psVector *fErr = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fErrS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+
+        // Set psPolynomial3D to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial3D(NULL, stats, mask, MASK_VALUE, f, fErr, x, y, z);
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE with NULL psPolynomial3D");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set psStats to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial3D(myPoly, NULL, mask, MASK_VALUE, f, fErr, x, y, z);
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE with NULL psStats");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set mask to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial3D(myPoly, stats, maskS8, MASK_VALUE, f, fErr, x, y, z);
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE with mask set to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set f psVector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, fS32, fErr, x, y, z);
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE: Set f psVector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set fError vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, f, fErrS32, x, y, z);
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE: Set fError vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set x vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, f, fErr, xS32, y, z);
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE: Set x vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set y vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, yS32, z);
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE: Set y vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set z vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, zS32);
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE: Set z vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect mask psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            mask->n++;
+            bool rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z);
+            mask->n--;
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE: Incorrect mask psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect f psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            f->n++;
+            bool rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z);
+            f->n--;
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE: Incorrect f psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect fErr psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            fErr->n++;
+            bool rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z);
+            fErr->n--;
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE: Incorrect fErr psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect x psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            x->n++;
+            bool rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z);
+            x->n--;
+            ok(rc == false, "psVectorClipFitPolynomial3D() returned FALSE: Incorrect x psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(myPoly);
+        psFree(x);
+        psFree(xS32);
+        psFree(y);
+        psFree(yS32);
+        psFree(z);
+        psFree(zS32);
+        psFree(f);
+        psFree(fS32);
+        psFree(mask);
+        psFree(maskS8);
+        psFree(fErr);
+        psFree(fErrS32);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //
+    // F32 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+
+    //
+    // F64 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+
+    //
+    // F32 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_Z_NULL | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+
+    //
+    // F64 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_Z_NULL | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit4D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit4D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolyFit4D.c	(revision 22322)
@@ -0,0 +1,1020 @@
+/*****************************************************************************
+This routine must ensure that various psLib functions which fit 4D polynomials
+to data work correctly.  There is a function genericTest() which creates
+vectors of data points (x and f), and populates them with the values from an
+arbitrary function setData().  It then calls appropriate 4D fitting function.
+It then evaluates the polynomial with the coefficients generated above and
+determines if they are within an error tolerance of the expected values.
+ 
+XXX: A few tests which previously worked (2006/02) now fail (2006/12).  See
+XXX comment below.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_DATA 200
+#define POLY_ORDER_X 1
+#define POLY_ORDER_Y 1
+#define POLY_ORDER_Z 1
+#define POLY_ORDER_T 1
+#define A 100.0
+#define B 2.0
+#define C 3.0
+#define D 4.0
+#define E 5.0
+#define F 6.0
+#define H 3.0
+#define J 3.0
+#define K 1.0
+#define L 5.0
+#define M 4.0
+#define N 3.0
+#define O 2.0
+#define P 1.0
+#define Q 5.0
+#define ERROR_TOLERANCE 0.05
+#define YERR 0.1
+#define VERBOSE 0
+#define EXTRA_VERBOSE 0
+#define NUM_ITERATIONS 5
+#define CLIP_SIGMA 3.0
+#define OUTLIERS true
+#define MASK_VALUE 1
+
+#define TS00_F_NULL  0x00000001
+#define TS00_F_F32  0x00000002
+#define TS00_F_F64  0x00000004
+#define TS00_F_S32  0x00000008
+#define TS00_X_NULL  0x00000010
+#define TS00_X_F32  0x00000020
+#define TS00_X_F64  0x00000040
+#define TS00_X_S32  0x00000080
+#define TS00_FERR_NULL  0x00000100
+#define TS00_FERR_F32  0x00000200
+#define TS00_FERR_F64  0x00000400
+#define TS00_FERR_S32  0x00000800
+#define TS00_MASK_NULL  0x00001000
+#define TS00_MASK_U8  0x00002000
+#define TS00_MASK_S32  0x00004000
+#define TS00_POLY_ORD  0x00008000
+#define TS00_POLY_CHEB  0x00010000
+#define TS00_CLIP_FIT  0x00020000
+#define TS00_Y_NULL  0x00100000
+#define TS00_Y_F32  0x00200000
+#define TS00_Y_F64  0x00400000
+#define TS00_Y_S32  0x00800000
+#define TS00_Z_NULL  0x01000000
+#define TS00_Z_F32  0x02000000
+#define TS00_Z_F64  0x04000000
+#define TS00_Z_S32  0x08000000
+#define TS00_T_NULL  0x10000000
+#define TS00_T_F32  0x20000000
+#define TS00_T_F64  0x40000000
+#define TS00_T_S32  0x80000000
+
+psF32 setData(psF32 x, psF32 y, psF32 z, psF32 t)
+{
+    if (0) {
+        // Linear case, for testing.
+        return(A + (B * x) + (C * y) + (D * z) + (E * t));
+    } else {
+        #if 0
+        return(A + (B * x) + (C * y) + (D * z) + (E * t) +
+               (F * x * x) + (H * y * y) + (J * z * z) + (K * t * t) +
+               (L * x * y) + (M * x * z) + (N * x * t) + (O * y * z) + (P * y * t) + (Q * z * t));
+        #else
+
+        return A + (B * x) + (C * y) + (D * z) + (E * t) + (F * x * y);
+        #endif
+
+    }
+}
+
+bool genericTest(
+    psU32 flags,
+    psS32 polyOrderX,
+    psS32 polyOrderY,
+    psS32 polyOrderZ,
+    psS32 polyOrderT,
+    psS32 numData,
+    bool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    bool testStatus = true;
+    psS32 memLeaks = 0;
+    psPolynomial4D *myPoly = NULL;
+    psVector *x = NULL;
+    psVector *y = NULL;
+    psVector *z = NULL;
+    psVector *t = NULL;
+    psVector *f = NULL;
+    psVector *mask = NULL;
+    psVector *fErr = NULL;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    stats->clipSigma = CLIP_SIGMA;
+    stats->clipIter = NUM_ITERATIONS;
+    stats->options |= PS_STAT_SAMPLE_STDEV;
+
+    if (VERBOSE)
+        printf("psMinimize functions: 4D Polynomial Fitting Functions");
+
+    psVector *xTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *yTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *zTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *tTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *fTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    xTruth->n = numData;
+    yTruth->n = numData;
+    zTruth->n = numData;
+    tTruth->n = numData;
+    fTruth->n = numData;
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using known seed
+    for (int i = 0; i < numData; i++) {
+        xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        yTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        zTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        tTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        fTruth->data.F64[i] = setData(xTruth->data.F64[i], yTruth->data.F64[i],
+                                      zTruth->data.F64[i], tTruth->data.F64[i]);
+    }
+    psFree(rng);
+
+    if (expectedRC == false && VERBOSE) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+    if (VERBOSE) {
+        if (flags & TS00_CLIP_FIT) {
+            printf(" performing a clip-fit\n");
+        } else {
+            printf(" performing a non clip-fit\n");
+        }
+    }
+
+    if (flags & TS00_POLY_ORD) {
+        if (VERBOSE)
+            printf(" using ordinary polynomials\n");
+        myPoly = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, polyOrderX, polyOrderY,
+                                     polyOrderZ, polyOrderT);
+
+        #if 1
+
+        for (int ix = 0; ix < polyOrderX + 1; ix++) {
+            for (int iy = 0; iy < polyOrderY + 1; iy++) {
+                for (int iz = 0; iz < polyOrderZ + 1; iz++) {
+                    for (int it = 0; it < polyOrderT + 1; it++) {
+                        myPoly->coeffMask[ix][iy][iz][it] = 0xff; // Mask it out
+                    }
+                }
+            }
+        }
+
+        // Put these back in
+        myPoly->coeffMask[0][0][0][0] = 0;   // A
+        myPoly->coeffMask[1][0][0][0] = 0;   // B * x
+        myPoly->coeffMask[0][1][0][0] = 0;   // C * y
+        myPoly->coeffMask[0][0][1][0] = 0;   // D * z
+        myPoly->coeffMask[0][0][0][1] = 0;   // E * t
+        myPoly->coeffMask[1][1][0][0] = 0;   // F * xy
+        #endif
+
+    }
+
+    if (flags & TS00_X_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL x vector\n");
+    }
+
+    if (flags & TS00_X_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_X_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_X_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F64);
+    }
+
+
+    if (flags & TS00_Y_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL y vector\n");
+    }
+
+    if (flags & TS00_Y_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_Y_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_Y_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_Z_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL z vector\n");
+    }
+
+    if (flags & TS00_Z_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_Z_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_Z_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_T_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL t vector\n");
+    }
+
+    if (flags & TS00_T_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 t vector\n");
+        t = psVectorCopy(NULL, tTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_T_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 t vector\n");
+        t = psVectorCopy(NULL, tTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_T_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 t vector\n");
+        t = psVectorCopy(NULL, tTruth, PS_TYPE_F64);
+    }
+
+
+    if (flags & TS00_F_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL f vector\n");
+    }
+
+    if (flags & TS00_F_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F32[numData/4] *= 2.0;
+            f->data.F32[numData/2] *= 2.0;
+            f->data.F32[3*numData/4] *= 2.0;
+        }
+    }
+
+    if (flags & TS00_F_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_S32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.S32[numData/4] *= 2.0;
+            f->data.S32[numData/2] *= 2.0;
+            f->data.S32[3*numData/4] *= 2.0;
+        }
+    }
+
+    if (flags & TS00_F_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F64);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F64[numData/4] *= 2.0;
+            f->data.F64[numData/2] *= 2.0;
+            f->data.F64[3*numData/4] *= 2.0;
+        }
+    }
+
+    if (flags & TS00_FERR_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL fErr vector\n");
+    }
+
+    if (flags & TS00_FERR_F32) {
+        if (VERBOSE)
+            printf(" using a psF32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F32[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_S32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.S32[i] = (psS32) YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_F64) {
+        if (VERBOSE)
+            printf(" using a psF64 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F64);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F64[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_MASK_NULL) {
+        if (VERBOSE)
+            printf(" using a NULL mask vector\n");
+    }
+
+    if (flags & TS00_MASK_U8) {
+        if (VERBOSE)
+            printf(" using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = 0;
+        }
+    }
+
+    if (flags & TS00_MASK_S32) {
+        if (VERBOSE)
+            printf(" using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = 0;
+        }
+    }
+
+    bool rc = false;
+    if (flags & TS00_CLIP_FIT) {
+        rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z, t);
+    } else {
+        rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z, t);
+    }
+
+    if (!rc) {
+        if (expectedRC == true) {
+            diag("TEST ERROR: the 4D polynomial fitting function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            diag("TEST ERROR: the 4D polynomial fitting function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<polyOrderX+1;i++) {
+                for (psS32 j=0;j<polyOrderY+1;j++) {
+                    for (psS32 k=0;k<polyOrderZ+1;k++) {
+                        for (psS32 l=0;l<polyOrderT+1;l++) {
+                            printf("Polynomial coefficient [%d][%d][%d][%d] is %0.1f\n",
+                                   i, j, k, l, myPoly->coeff[i][j][k][l]);
+                        }
+                    }
+                }
+            }
+        }
+
+        psVector *result = psPolynomial4DEvalVector(myPoly, xTruth, yTruth, zTruth, tTruth);
+        for (int i=0;i<numData; i++) {
+            // Skip the outliers.
+            if ((i == numData/4) || (i == numData/2) || (i == 3*numData/4)) {
+                continue;
+            }
+            psF32 expectData = fTruth->data.F64[i];
+            psF32 actualData = result->data.F64[i];
+
+            if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
+                diag("TEST ERROR: Fitted data %d: (%f), expected was (%f)\n",
+                     i, actualData, expectData);
+                testStatus = false;
+            } else {
+                if (EXTRA_VERBOSE) {
+                    printf("GOOD: Fitted data %d: (%.1f), expected was (%.1f)\n",
+                           i, actualData, expectData);
+                }
+            }
+        }
+        psFree(result);
+    }
+
+    psMemCheckCorruption(stderr, false);
+    psFree(myPoly);
+    psFree(mask);
+    psFree(x);
+    psFree(y);
+    psFree(z);
+    psFree(t);
+    psFree(f);
+    psFree(xTruth);
+    psFree(yTruth);
+    psFree(zTruth);
+    psFree(tTruth);
+    psFree(fTruth);
+    psFree(fErr);
+    psFree(stats);
+    psMemCheckCorruption(stderr, false);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    return (testStatus);
+}
+
+/*****************************************************************************
+We test a variety of polynomial fitting routines, types, and polynomial types
+here:
+    F32 tests: Ordinary polys, non-clip fit
+    F64 tests: Ordinary polys, non-clip fit
+    F32 tests: Chebyshev polys, non-clip fit
+    F64 tests: Chebyshev polys, non-clip fit
+    F32 tests: Ordinary polys, clip fit
+    F64 tests: Ordinary polys, clip fit
+    F32 tests: Chebyshev polys, clip fit
+    F64 tests: Chebyshev polys, clip fit
+ *****************************************************************************/
+int main()
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(112);
+
+
+    // psVectorFitPolynomial4D()
+    // Test various erroneous input paramater configurations
+    {
+        psMemId id = psMemGetId();
+        psPolynomial4D *myPoly = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, POLY_ORDER_T);
+        psVector *x = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *xS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *y = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *yS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *z = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *zS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *t = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *tS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *f = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *mask = psVectorAlloc(NUM_DATA, PS_TYPE_U8);
+        psVector *maskS8 = psVectorAlloc(NUM_DATA, PS_TYPE_S8);
+        psVector *fErr = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fErrS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+
+
+        // Set psPolynomial4D to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial4D(NULL, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            ok(rc == false, "psVectorFitPolynomial4D() returned FALSE with NULL psPolynomial4D");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set mask to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial4D(myPoly, maskS8, MASK_VALUE, f, fErr, x, y, z, t);
+            ok(rc == false, "psVectorFitPolynomial4D() returned FALSE with mask set to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set f psVector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, fS32, fErr, x, y, z, t);
+            ok(rc == false, "psVectorFitPolynomial4D() returned FALSE: Set f psVector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set fError vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErrS32, x, y, z, t);
+            ok(rc == false, "psVectorFitPolynomial4D() returned FALSE: Set fError vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set x vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErr, xS32, y, z, t);
+            ok(rc == true, "psVectorFitPolynomial4D() returned TRUE: x vector may be S32");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set y vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErr, x, yS32, z, t);
+            ok(rc == true, "psVectorFitPolynomial4D() returned TRUE: y vector may be S32");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set z vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErr, x, y, zS32, t);
+            ok(rc == true, "psVectorFitPolynomial4D() returned TRUE: z vector may be S32");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set t vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z, tS32);
+            ok(rc == true, "psVectorFitPolynomial4D() returned TRUE: t vector may be S32");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect mask psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            mask->n++;
+            bool rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            mask->n--;
+            ok(rc == false, "psVectorFitPolynomial4D() returned FALSE: Incorrect mask psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect f psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            f->n++;
+            bool rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            f->n--;
+            ok(rc == false, "psVectorFitPolynomial4D() returned FALSE: Incorrect f psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect fErr psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            fErr->n++;
+            bool rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            fErr->n--;
+            ok(rc == false, "psVectorFitPolynomial4D() returned FALSE: Incorrect fErr psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect x psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            x->n++;
+            bool rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            x->n--;
+            ok(rc == false, "psVectorFitPolynomial4D() returned FALSE: Incorrect x psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(myPoly);
+        psFree(x);
+        psFree(xS32);
+        psFree(y);
+        psFree(yS32);
+        psFree(z);
+        psFree(zS32);
+        psFree(t);
+        psFree(tS32);
+        psFree(f);
+        psFree(fS32);
+        psFree(mask);
+        psFree(maskS8);
+        psFree(fErr);
+        psFree(fErrS32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorClipFitPolynomial4D()
+    // Test various erroneous input paramater configurations
+    {
+        psMemId id = psMemGetId();
+        psPolynomial4D *myPoly = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, POLY_ORDER_T);
+        psVector *x = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *xS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *y = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *yS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *z = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *zS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *t = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *tS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *f = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psVector *mask = psVectorAlloc(NUM_DATA, PS_TYPE_U8);
+        psVector *maskS8 = psVectorAlloc(NUM_DATA, PS_TYPE_S8);
+        psVector *fErr = psVectorAlloc(NUM_DATA, PS_TYPE_F32);
+        psVector *fErrS32 = psVectorAlloc(NUM_DATA, PS_TYPE_S32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+
+        // Set psPolynomial4D to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial4D(NULL, stats, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE with NULL psPolynomial4D");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set psStats to NULL, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE with NULL psStats");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set mask to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, maskS8, MASK_VALUE, f, fErr, x, y, z, t);
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE with mask set to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set f psVector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, fS32, fErr, x, y, z, t);
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE: Set f psVector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set fError vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErrS32, x, y, z, t);
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE: Set fError vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set x vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, xS32, y, z, t);
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE: Set x vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set y vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, yS32, z, t);
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE: Set y vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set z vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, zS32, t);
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE: Set z vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Set t vector to incorrect type, should cause error
+        {
+            psMemId id = psMemGetId();
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z, tS32);
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE: Set t vector to incorrect type");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect mask psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            mask->n++;
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            mask->n--;
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE: Incorrect mask psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect f psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            f->n++;
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            f->n--;
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE: Incorrect f psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect fErr psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            fErr->n++;
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            fErr->n--;
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE: Incorrect fErr psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Incorrect x psVector size, should cause error
+        {
+            psMemId id = psMemGetId();
+            x->n++;
+            bool rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z, t);
+            x->n--;
+            ok(rc == false, "psVectorClipFitPolynomial4D() returned FALSE: Incorrect x psVector size");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(myPoly);
+        psFree(x);
+        psFree(xS32);
+        psFree(y);
+        psFree(yS32);
+        psFree(z);
+        psFree(zS32);
+        psFree(t);
+        psFree(tS32);
+        psFree(f);
+        psFree(fS32);
+        psFree(mask);
+        psFree(maskS8);
+        psFree(fErr);
+        psFree(fErrS32);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    
+    //
+    // F32 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit");
+
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_NULL
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_NULL | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F64
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+
+    //
+    // F64 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_NULL
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_NULL | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F32
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                   POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, non-clip fit.  Mismatch vector types");
+
+    //
+    // F32 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+
+    // XXX: These tests are failing; they shouldn't be.
+    /*
+        ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                                  | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                                  POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, clip fit");
+        // Some Vectors NULL
+        ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                                  | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                                  POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, true), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    */
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_NULL
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_NULL | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F64
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                   | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F32 tests: Ordinary polys, clip fit.  Mismatch vector types");
+
+    //
+    // F64 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, clip fit");
+    // Some Vectors NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, true), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_NULL
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_NULL | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Some Vectors NULL");
+    // F-vector NULL
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  F Vector NULL");
+    ok(genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  F Vector NULL");
+    // Mismatch vector types
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F32
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+    ok(genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                   | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                   POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false), "F64 tests: Ordinary polys, clip fit.  Mismatch vector types");
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomial.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomial.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomial.c	(revision 22322)
@@ -0,0 +1,358 @@
+/** tst_Func00.c
+*
+*    This routine must ensure that the psPolynomial structures are
+*    allocated and deallocated by the psPolynomialXXXlloc() procedures.
+*    It also calls the various psPolynomialXXXEval() procedures.
+*
+*    The F32 and F64 polynomials are tested for all orders (1 - 4) and for
+*    both ordinary and chebyshev polynomials.
+*
+*    NOTE: This test code requries the stdout file to verify that the results
+*    are good.
+*
+*    XXX: Modify these tests so that polynomials with a variety of different
+*    orders are created.
+* 
+*    XXX: Compare to FLT_EPSILON
+* 
+*    @version $Revision: 1.5 $  $Name: not supported by cvs2svn $
+*    @date $Date: 2008-05-05 00:09:04 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*
+*****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define ORDER    3
+#define VERBOSE  0
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(59);
+
+    // This test will allocate a 1D polynomial and verify the structure allocated
+    {
+        psMemId id = psMemGetId();
+        psPolynomial1D* my1DPoly  = NULL;
+
+        // Allocate polynomial
+        my1DPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, ORDER);
+        ok(my1DPoly != NULL, "1D polynomial allocated successfully");
+        skip_start(my1DPoly == NULL, 3, "Skipping tests because psPolynomial1DAlloc() failed");
+
+        // Verify polynomial structure members set properly
+        ok(my1DPoly->nX == ORDER, "psPolynomial1DAlloc(): Number of terms set correctly");
+        ok(my1DPoly->type == PS_POLYNOMIAL_ORD, "psPolynomial1DAlloc():  type set correctly");
+        bool errorFlag = false;
+        for(psS32 i = 0; i < ORDER+1; i++)
+        {
+            if (my1DPoly->coeff[i] != 0.0) {
+                diag("Coeff[%d] %lg not as expected %lg", i, my1DPoly->coeff[i], 0.0);
+                errorFlag = true;
+            }
+            if (my1DPoly->coeffErr[i] != 0.0) {
+                diag("CoeffErr[%d] %lg not as expected %lg", i, my1DPoly->coeffErr[i], 0.0);
+                errorFlag = true;
+            }
+            if (my1DPoly->coeffMask[i] != 0) {
+                diag("Mask[%d] %d not as expected %d", i, my1DPoly->coeffMask[i], 0);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial1D coefficients set correctly");
+
+        skip_end();
+        psFree(my1DPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // This test will allocate a Chebyshev 1D polynomial and verify the structure allocated
+    {
+        psMemId id = psMemGetId();
+        psPolynomial1D* my1DPoly  = NULL;
+        my1DPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, ORDER);
+        ok(my1DPoly != NULL, "Chebyshev 1D polynomial allocated successfully");
+        skip_start(my1DPoly == NULL, 1, "Skipping tests because psPolynomial1DAlloc() failed");
+        ok(my1DPoly->type == PS_POLYNOMIAL_CHEB, "psPolynomial1DAlloc(): Chebyshev  type set correctly");
+        skip_end();
+        psFree(my1DPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to allocate with negative order
+    // Following should generate error msg for negative terms
+    if (1) {
+        psMemId id = psMemGetId();
+        ok(psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, -1) == NULL,
+          "psPolynomial1DAlloc() returned NULL with negative polynomial order");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate polynomial with unallowable type
+    {
+        psMemId id = psMemGetId();
+        psPolynomial1D* polyOrd = psPolynomial1DAlloc(99, ORDER);
+        ok(polyOrd==NULL, "psPolynomial1DAlloc() returned NULL with unallowed type");
+        psFree(polyOrd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // This test will allocate a 2D polynomial and verify the structure allocated
+    {
+        psMemId id = psMemGetId();
+        psPolynomial2D* my2DPoly = NULL;
+
+        // Allocate polynomial
+        my2DPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, ORDER,ORDER+1);
+        ok(my2DPoly != NULL, "2D polynomial allocated successfully");
+        skip_start(my2DPoly == NULL, 4, "Skipping tests because psPolynomial2DAlloc() failed");
+
+        // Verify polynomial structure members set properly
+        ok(my2DPoly->nX == ORDER, "psPolynomial2DAlloc(): Number of terms (nX) set correctly");
+        ok(my2DPoly->nY == ORDER+1, "psPolynomial2DAlloc(): Number of terms (nY) set correctly");
+        ok(my2DPoly->type == PS_POLYNOMIAL_ORD, "psPolynomial2DAlloc(): type set correctly");
+
+        bool errorFlag = false;
+        for(psS32 i = 0; i < ORDER+1; i++)
+        {
+            for(psS32 j = 0; j < ORDER+2; j++) {
+                if (fabs(my2DPoly->coeff[i][j]) > FLT_EPSILON) {
+                    diag("Coeff[%d][%d] %lg not as expected %lg", i, j, my2DPoly->coeff[i][j], 0.0);
+                    errorFlag = true;
+                }
+                if (my2DPoly->coeffErr[i][j] != 0.0) {
+                    diag("CoeffErr[%d][%d] %lg not as expected %lg", i, j, my2DPoly->coeffErr[i][j], 0.0);
+                    errorFlag = true;
+                }
+                if (my2DPoly->coeffMask[i][j] != 0) {
+                    diag("Mask[%d][%d] %d not as expected %d", i, j, my2DPoly->coeffMask[i][j], 0);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "psPolynomial2D coefficients set correctly");
+        skip_end();
+        psFree(my2DPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // This test will allocate a Chebyshev 2D polynomial and verify the structure allocated
+    {
+        psMemId id = psMemGetId();
+        psPolynomial2D* my2DPoly = NULL;
+        my2DPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, ORDER,ORDER+1);
+        ok(my2DPoly != NULL, "Chebyshev 2D polynomial allocated successfully");
+        skip_start(my2DPoly == NULL, 1, "Skipping tests because psPolynomial2DAlloc() failed");
+        skip_end();
+        psFree(my2DPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to allocate with negative order
+    // Following should generate error msg for negative terms
+    if (1) {
+        psMemId id = psMemGetId();
+        ok(psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, -1, 1) == NULL, 
+          "psPolynomial2DAlloc() returned NULL with negative polynomial order");
+        ok(psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 1, -1) == NULL,
+          "psPolynomial2DAlloc() returned NULL with negative polynomial order");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate polynomial with unallowed type
+    {
+        psMemId id = psMemGetId();
+        psPolynomial2D* polyOrd = psPolynomial2DAlloc(99, ORDER, ORDER);
+        ok(polyOrd == NULL, "psPolynomial2DAlloc() returned NULL with unallowed tpye");
+        psFree(polyOrd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // This test will allocate a 3D polynomial and verify the structure allocated
+    {
+        psMemId id = psMemGetId();
+        psPolynomial3D* my3DPoly = NULL;
+
+        // Allocate polynomial
+        my3DPoly = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, ORDER, ORDER+1, ORDER+2);
+        ok(my3DPoly != NULL, "3D polynomial allocated successfully");
+        skip_start(my3DPoly == NULL, 5, "Skipping tests because psPolynomial3DAlloc() failed");
+
+        // Verify polynomial structure members set properly
+        ok(my3DPoly->nX == ORDER, "psPolynomial3DAlloc(): Number of terms (nX) set correctly");
+        ok(my3DPoly->nY == ORDER+1, "psPolynomial3DAlloc(): Number of terms (nY) set correctly");
+        ok(my3DPoly->nZ == ORDER+2, "psPolynomial3DAlloc(): Number of terms (nZ) set correctly");
+        ok(my3DPoly->type == PS_POLYNOMIAL_ORD, "psPolynomial3DAlloc(): type set correctly");
+
+        bool errorFlag = false;
+        for(psS32 i = 0; i < ORDER+1; i++)
+        {
+            for(psS32 j = 0; j < ORDER+2; j++) {
+                for(psS32 k = 0; k < ORDER+3; k++) {
+                    if (my3DPoly->coeff[i][j][k] != 0.0) {
+                        diag("Coeff[%d][%d][%d] %lg not as expected %lg",
+                             i, j, k, my3DPoly->coeff[i][j][k], 0.0);
+                        errorFlag = true;
+                    }
+                    if (my3DPoly->coeffErr[i][j][k] != 0.0) {
+                        diag("CoeffErr[%d][%d][%d] %lg not as expected %lg",
+                             i, j, k, my3DPoly->coeffErr[i][j][k], 0.0);
+                        errorFlag = true;
+                    }
+                    if (my3DPoly->coeffMask[i][j][k] != 0) {
+                        diag("Mask[%d][%d][%d] %d not as expected %d",
+                             i, j, k, my3DPoly->coeffMask[i][j][k], 0);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+        ok(!errorFlag, "psPolynomial3D coefficients set correctly");
+        skip_end();
+        psFree(my3DPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // This test will allocate a Chebyshev 3D polynomial and verify the structure allocated
+    {
+        psMemId id = psMemGetId();
+        psPolynomial3D* my3DPoly = NULL;
+        my3DPoly = psPolynomial3DAlloc(PS_POLYNOMIAL_CHEB, ORDER, ORDER+1, ORDER+2);
+        ok(my3DPoly != NULL, "Chebyshev 3D polynomial allocated successfully");
+        skip_start(my3DPoly == NULL, 1, "Skipping tests because psPolynomial2DAlloc() failed");
+        ok(my3DPoly->type == PS_POLYNOMIAL_CHEB, "psPolynomial3DAlloc(): Chebyshev type set correctly");
+        skip_end();
+        psFree(my3DPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to allocate with negative order
+    // Following should generate error msg for negative terms
+    if (1) {
+        psMemId id = psMemGetId();
+        ok(psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, -1, 1, 1) == NULL, 
+          "psPolynomial3DAlloc() returned NULL with negative polynomial order");
+        ok(psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, 1, -1, 1) == NULL, 
+          "psPolynomial3DAlloc() returned NULL with negative polynomial order");
+        ok(psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, 1, 1, -1) == NULL,
+          "psPolynomial3DAlloc() returned NULL with negative polynomial order");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate polynomial with unallowed type
+    {
+        psMemId id = psMemGetId();
+        psPolynomial3D* polyOrd = psPolynomial3DAlloc(99, ORDER, ORDER, ORDER);
+        ok(polyOrd == NULL, "psPolynomial3DAlloc() returned NULL with unallowed type");
+        psFree(polyOrd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+
+    }
+
+
+    // This test will allocate a 4D polynomial and verify the structure allocated
+    {
+        psMemId id = psMemGetId();
+        psPolynomial4D* my4DPoly = NULL;
+
+        // Allocate polynomial
+        my4DPoly = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, ORDER,ORDER+1,ORDER+2,ORDER+3);
+        ok(my4DPoly != NULL, "4D polynomial allocated successfully");
+        skip_start(my4DPoly == NULL, 6, "Skipping tests because psPolynomial4DAlloc() failed");
+
+        // Verify polynomial structure members set properly
+        ok(my4DPoly->nX == ORDER, "psPolynomial4DAlloc(): Number of terms (nX) set correctly");
+        ok(my4DPoly->nY == ORDER+1, "psPolynomial4DAlloc(): Number of terms (nY) set correctly");
+        ok(my4DPoly->nZ == ORDER+2, "psPolynomial4DAlloc(): Number of terms (nZ) set correctly");
+        ok(my4DPoly->nT == ORDER+3, "psPolynomial4DAlloc(): Number of terms (nT) set correctly");
+        ok(my4DPoly->type == PS_POLYNOMIAL_ORD, "psPolynomial4D    Alloc(): type set correctly");
+
+        bool errorFlag = false;
+        for(psS32 i = 0; i < ORDER+1; i++)
+        {
+            for(psS32 j = 0; j < ORDER+2; j++) {
+                for(psS32 k = 0; k < ORDER+3; k++) {
+                    for(psS32 l = 0; l < ORDER+4; l++) {
+                        if (my4DPoly->coeff[i][j][k][l] != 0.0) {
+                            diag("Coeff[%d][%d][%d][%d] %lg not as expected %lg",
+                                 i, j, k, l, my4DPoly->coeff[i][j][k][l], 0.0);
+                            errorFlag = true;
+                        }
+                        if (my4DPoly->coeffErr[i][j][k][l] != 0.0) {
+                            diag("CoeffErr[%d][%d][%d][%d] %lg not as expected %lg",
+                                 i, j, k, l, my4DPoly->coeffErr[i][j][k][l], 0.0);
+                            errorFlag = true;
+                        }
+                        if (my4DPoly->coeffMask[i][j][k][l] != 0) {
+                            diag("Mask[%d][%d][%d][%d] %d not as expected %d",
+                                 i, j, k, l, my4DPoly->coeffMask[i][j][k][l], 0);
+                            errorFlag = true;
+                        }
+                    }
+                }
+            }
+        }
+        ok(!errorFlag, "psPolynomial4D coefficients set correctly");
+        skip_end();
+        psFree(my4DPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // This test will allocate a Chebyshev 4D polynomial and verify the structure allocated
+    {
+        psMemId id = psMemGetId();
+        psPolynomial4D* my4DPoly = NULL;
+        my4DPoly = psPolynomial4DAlloc(PS_POLYNOMIAL_CHEB, ORDER,ORDER+1,ORDER+2,ORDER+3);
+        ok(my4DPoly != NULL, "Chebyshev 4D polynomial allocated successfully");
+        skip_start(my4DPoly == NULL, 1, "Skipping tests because psPolynomial4DAlloc() failed");
+        ok(my4DPoly->type == PS_POLYNOMIAL_CHEB, "psPolynomial4DAlloc(): Chebyshev type set correctly");
+        skip_end();
+        psFree(my4DPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to allocate with negative order
+    // Following should generate error msg for negative terms
+    if (1) {
+        psMemId id = psMemGetId();
+        ok(psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, -1, 1, 1, 1) == NULL, 
+          "psPolynomial4DAlloc() returned NULL with negative polynomial order");
+        ok(psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 1, -1, 1, 1) == NULL, 
+          "psPolynomial4DAlloc() returned NULL with negative polynomial order");
+        ok(psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 1, 1, -1, 1) == NULL,
+          "psPolynomial4DAlloc() returned NULL with negative polynomial order");
+        ok(psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 1, 1, 1, -1) == NULL,
+          "psPolynomial4DAlloc() returned NULL with negative polynomial order");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate polynomial with unallowed type
+    {
+        psMemId id = psMemGetId();
+        psPolynomial4D* polyOrd = psPolynomial4DAlloc(99, ORDER, ORDER, ORDER, ORDER);
+        ok(polyOrd == NULL, "psPolynomial4DAlloc() returned NULL with unallowed type");
+        psFree(polyOrd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval1D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval1D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval1D.c	(revision 22322)
@@ -0,0 +1,246 @@
+/**
+*
+*  This test driver will exercise the psPolynomialXDEval functions for both
+*  ORD and CHEB type polynomials.
+*
+*  @version  $Revision: 1.10 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2008-05-05 00:09:04 $
+*
+*  XXX: Probably should test single- and multi-dimensional polynomials in
+*  which one diminsion is constant (n == 1).
+*
+*  XXX: define ORDERS, not TERMS
+*
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*
+***************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define TERMS        4
+#define TESTPOINTS   5
+#define ERROR_TOL    0.001
+
+psF32 poly1DCoeff[TERMS]        = { -4.3, 2.3, -3.2, 1.9};
+psF64 Dpoly1DCoeff[TERMS]        = { -4.3, 2.3, -3.2, 1.9};
+psF32 poly1DMask[TERMS]         = {    0,   0,    1,   0  };
+
+psF32 poly1DXValue[TESTPOINTS]   = { 2.550000,    -9.8000,   0.00134,   -12.2500,   0.000};
+psF64 Dpoly1DXValue[TESTPOINTS]  = { 2.550000,    -9.8000,   0.00134,   -12.2500,   0.000};
+psF32 poly1DXResult[TESTPOINTS]  = {33.069613, -1815.1048, -4.296918, -3525.1797, -4.3000};
+psF64 Dpoly1DXResult[TESTPOINTS] = {33.069613, -1815.1048, -4.296918, -3525.1797, -4.3000};
+
+psF32 poly1DXChebValue[TESTPOINTS]   = { -0.99,    -0.33,     0.125,    0.564,    0.875};
+psF64 Dpoly1DXChebValue[TESTPOINTS]  = { -0.99,    -0.33,     0.125,    0.564,    0.875};
+psF32 poly1DXChebResult[TESTPOINTS]  = { -1.401196, 1.016252, 0.257813, 0.089625, 1.429688};
+psF64 Dpoly1DXChebResult[TESTPOINTS] = { -1.401196, 1.016252, 0.257813, 0.089625, 1.429688};
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(28);
+
+
+    // Evaluate a NULL polynomial
+    {
+        psMemId id = psMemGetId();
+        psF64 result = psPolynomial1DEval(NULL, 0.0);
+        ok(isnan(result), "psPolynomial1DEval() returned NAN with NULL psPolynomial");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate and evaluate an ordinary polynomial structure
+    {
+        psMemId id = psMemGetId();
+
+        // Allocate polynomial structure
+        psPolynomial1D* polyOrd = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, TERMS-1);
+        ok(polyOrd != NULL, "Ordinary psPolynomial1D successfully allocated");
+        skip_start(polyOrd == NULL, 1, "Skipping tests because psPolynomial1DAlloc() failed");
+
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++) {
+            polyOrd->coeff[i] = poly1DCoeff[i];
+            polyOrd->coeffMask[i]  = poly1DMask[i];
+        }
+
+        // Evaluate test points and verify results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++) {
+            psF64 result = psPolynomial1DEval(polyOrd,poly1DXValue[i]);
+            if (fabs(poly1DXResult[i]-result) > ERROR_TOL ) {
+                diag("Evaluated value %g not as expected %g", result, poly1DXResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial1DEval() successful (Ordinary)");
+        skip_end();
+        psFree(polyOrd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Allocate Cheby polynomial structure
+    {
+        bool errorFlag = false;
+        psMemId id = psMemGetId();
+        psPolynomial1D*  polyCheb = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1);
+        ok(polyCheb != NULL, "Chebyshev psPolynomial1D successfully allocated");
+        skip_start(polyCheb == NULL, 1, "Skipping tests because psPolynomial1DAlloc() failed");
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            polyCheb->coeff[i] = 1.0;
+            polyCheb->coeffMask[i]  = poly1DMask[i];
+        }
+        // Evaluate test points and verify results
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            psF64 resultCheb = psPolynomial1DEval(polyCheb,poly1DXChebValue[i]);
+            if (fabs(poly1DXChebResult[i]-resultCheb) > ERROR_TOL ) {
+                diag("Evaluated value %g not as expected %g", resultCheb, poly1DXChebResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial1DEval() successful (Chebyshev)");
+        skip_end();
+        psFree(polyCheb);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate polynomial, test the psPolynomial1DEvalVector() routines
+    {
+        psMemId id = psMemGetId();
+        // Create input vectors
+        psVector* inputOrd = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            inputOrd->data.F64[i] = poly1DXValue[i];
+            inputOrd->n++;
+        }
+
+        psPolynomial1D* polyOrd = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, TERMS-1);
+        ok(polyOrd != NULL, "Ordinary polynomial allocation successful");
+        skip_start(polyOrd == NULL, 6, "Skipping tests because psPolynomial1DAlloc() failed");
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            polyOrd->coeff[i] = poly1DCoeff[i];
+            polyOrd->coeffMask[i]  = poly1DMask[i];
+        }
+
+        // Evaluate the vectors
+        psVector* outputOrd = psPolynomial1DEvalVector(polyOrd, inputOrd);
+        ok(outputOrd != NULL, "psPolynomial1DEvalVector() generated non-NULL psVector");
+        ok(outputOrd->type.type == PS_TYPE_F64, "psPolynomial1DEvalVector() generated correct output type");
+
+        // Verify the results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            if (fabs(poly1DXResult[i]-outputOrd->data.F64[i]) > ERROR_TOL) {
+                diag("Result[%d] %lg not equal to expected %lg",
+                     i, outputOrd->data.F64[i], poly1DXResult[i]);
+                errorFlag = TRUE;
+            }
+        }
+        ok(!errorFlag, "psPolynomial1DEvalVector() produced the correct answers");
+
+        // Attempt to invoke function with NULL polynomial
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial1DEvalVector(NULL, inputOrd) == NULL, "psPolynomial1DEvalVector() produced NULL when called with NULL polynomial");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial1DEvalVector(polyOrd, NULL) == NULL, "psPolynomial1DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputOrd->type.type = PS_TYPE_U8;
+            ok(psPolynomial1DEvalVector(polyOrd,inputOrd) == NULL, "psPolynomial1DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        psFree(outputOrd);
+        skip_end();
+        psFree(inputOrd);
+        psFree(polyOrd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Allocate polynomial, test the psPolynomial1DEvalVector() routines (Chebyshev)
+    {
+        psMemId id = psMemGetId();
+        // Create input vectors
+        psVector* inputCheb = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            inputCheb->data.F64[i] = poly1DXChebValue[i];
+            inputCheb->n++;
+        }
+
+        psPolynomial1D* polyCheb = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1);
+        ok(polyCheb != NULL, "Ordinary polynomial allocation successful");
+        skip_start(polyCheb == NULL, 5, "Skipping tests because psPolynomial1DAlloc() failed");
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            polyCheb->coeff[i] = 1.0;
+            polyCheb->coeffMask[i]  = poly1DMask[i];
+        }
+
+        // Evaluate the vectors
+        psVector* outputCheb = psPolynomial1DEvalVector(polyCheb, inputCheb);
+        ok(outputCheb != NULL, "psPolynomial1DEvalVector() generated non-NULL psVector");
+        ok(outputCheb->type.type == PS_TYPE_F64, "psPolynomial1DEvalVector() generated correct output type");
+
+        // Verify the results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            if (fabs(poly1DXChebResult[i]-outputCheb->data.F64[i]) > ERROR_TOL) {
+                diag("ResultCheb[%d] %lg not equal to expected %lg",
+                     i, outputCheb->data.F64[i], poly1DXChebResult[i]);
+                errorFlag = TRUE;
+            }
+        }
+        ok(!errorFlag, "psPolynomial1DEvalVector() produced the correct answers");
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial1DEvalVector(polyCheb,NULL) == NULL, "psPolynomial1DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputCheb->type.type = PS_TYPE_U8;
+            ok(psPolynomial1DEvalVector(polyCheb,inputCheb) == NULL, "psPolynomial1DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        psFree(outputCheb);
+        skip_end();
+        psFree(inputCheb);
+        psFree(polyCheb);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval2D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval2D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval2D.c	(revision 22322)
@@ -0,0 +1,341 @@
+/** tap_psPolynomialEval2D.c
+*
+*  This test driver will exercise the psPolynomialXDEval functions for both
+*  ORD and CHEB type polynomials.
+*
+*  @version  $Revision: 1.8 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2008-05-05 00:09:04 $
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*
+***************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define  TERMS        4
+#define  TESTPOINTS   5
+#define  ERROR_TOL    0.001
+
+psF32 poly2DCoeff[TERMS][TERMS]   = {  { -4.3,  2.3, -3.2,  1.9},
+                                       {  1.2,  0.7, -0.3,  1.3},
+                                       {  0.4, -2.9,  0.8, -3.1},
+                                       { -1.1,  2.1,  1.9,  0.6}
+                                    };
+psF64 Dpoly2DCoeff[TERMS][TERMS]  = {  { -4.3,  2.3, -3.2,  1.9},
+                                       {  1.2,  0.7, -0.3,  1.3},
+                                       {  0.4, -2.9,  0.8, -3.1},
+                                       { -1.1,  2.1,  1.9,  0.6}
+                                    };
+psF32 poly2DMask[TERMS][TERMS]    = {  {  0,    0,    0,    1},
+                                       {  0,    0,    1,    0},
+                                       {  1,    0,    0,    0},
+                                       {  0,    0,    0,    1}
+                                    };
+
+psF32 poly2DXYValue[TESTPOINTS][2] = {  {  1.40,  0.55},
+                                        { -0.55,  1.40},
+                                        {  0.00,  2.34},
+                                        { -0.88,  0.00},
+                                        {  3.45, -0.78}
+                                     };
+psF32 Dpoly2DXYValue[TESTPOINTS][2] = {  {  1.40,  0.55},
+                                      { -0.55,  1.40},
+                                      {  0.00,  2.34},
+                                      { -0.88,  0.00},
+                                      {  3.45, -0.78}
+                                      };
+psF32 poly2DResult[TESTPOINTS] = {  -3.415938, -14.765687, -16.43992, -4.606381, -22.650702 };
+psF64 Dpoly2DResult[TESTPOINTS] = {  -3.415938, -14.765687, -16.43992, -4.606381, -22.650702 };
+
+psF32 poly2DXYChebValue[TESTPOINTS][2] = {  {  0.500,  0.500},
+        {  0.000,  0.250},
+        { -0.250,  0.000},
+        {  0.990,  0.150},
+        {  0.333, -0.666}
+                                         };
+psF32 Dpoly2DXYChebValue[TESTPOINTS][2] = {  {  0.500,  0.500},
+        {  0.000,  0.250},
+        { -0.250,  0.000},
+        {  0.990,  0.150},
+        {  0.333, -0.666}
+                                          };
+psF32 poly2DChebResult[TESTPOINTS] = {  0.750000, 1.687500, 0.625000, -0.113040, 0.386786 };
+psF32 Dpoly2DChebResult[TESTPOINTS] = {  0.750000, 1.687500, 0.625000, -0.113040, 0.386786 };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(38);
+
+
+    // Evaluate a NULL polynomial
+    {
+        psMemId id = psMemGetId();
+        psF64 result = psPolynomial2DEval(NULL, 0.0, 0.0);
+        ok(isnan(result), "psPolynomial2DEval() returned NAN with NULL psPolynomial");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate and evaluate an ordinary polynomial structure
+    {
+        psMemId id = psMemGetId();
+
+        // Allocate polynomial structure
+        psPolynomial2D*  polyOrd = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1);
+        ok(polyOrd != NULL, "Ordinary psPolynomial2D successfully allocated");
+        skip_start(polyOrd == NULL, 1, "Skipping tests because psPolynomial2DAlloc() failed");
+
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                polyOrd->coeff[i][j] = poly2DCoeff[i][j];
+                polyOrd->coeffMask[i][j]  = poly2DMask[i][j];
+            }
+        }
+
+        // Evaluate test points and verify results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            psF64 result = psPolynomial2DEval(polyOrd,Dpoly2DXYValue[i][0],Dpoly2DXYValue[i][1]);
+            if (fabs(Dpoly2DResult[i]-result) > ERROR_TOL ) {
+                diag("TEST ERROR: Evaluated value %f, should be %f\n", result, Dpoly2DResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial2DEval() successful (Ordinary)");
+        skip_end();
+        psFree(polyOrd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate and evaluate Cheby polynomial structure
+    {
+        psMemId id = psMemGetId();
+
+        // Allocate polynomial structure
+        psPolynomial2D*  polyCheb = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1);
+        ok(polyCheb != NULL, "Cheby psPolynomial2D successfully allocated");
+        skip_start(polyCheb == NULL, 1, "Skipping tests because psPolynomial2DAlloc() failed");
+
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                polyCheb->coeff[i][j] = 1.0;
+                polyCheb->coeffMask[i][j]  = poly2DMask[i][j];
+            }
+        }
+
+        // Evaluate test points and verify results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            psF64 resultCheb = psPolynomial2DEval(polyCheb,Dpoly2DXYChebValue[i][0],Dpoly2DXYChebValue[i][1]);
+            if (fabs(Dpoly2DChebResult[i]-resultCheb) > ERROR_TOL ) {
+                diag("TEST ERROR: Evaluated value %f, should be %f\n", resultCheb, Dpoly2DChebResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial2DEval() successful (Chebyshev)");
+        skip_end();
+        psFree(polyCheb);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate polynomial, test the psPolynomial2DEvalVector() routines
+    {
+        psMemId id = psMemGetId();
+        // Create input vectors
+        psVector* inputOrdX  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputOrdY  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            inputOrdX->data.F64[i]  = poly2DXYValue[i][0];
+            inputOrdY->data.F64[i]  = poly2DXYValue[i][1];
+            inputOrdX->n++;
+            inputOrdY->n++;
+        }
+
+        psPolynomial2D* polyOrd = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1);
+        ok(polyOrd != NULL, "Ordinary polynomial allocation successful");
+        skip_start(polyOrd == NULL, 8, "Skipping tests because psPolynomial2DAlloc() failed");
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                polyOrd->coeff[i][j] = poly2DCoeff[i][j];
+                polyOrd->coeffMask[i][j]  = poly2DMask[i][j];
+            }
+        }
+
+        // Evaluate the vectors
+        psVector* outputOrd = psPolynomial2DEvalVector(polyOrd, inputOrdX, inputOrdY);
+        ok(outputOrd != NULL, "psPolynomial2DEvalVector() generated non-NULL psVector");
+        ok(outputOrd->type.type == PS_TYPE_F64, "psPolynomial2DEvalVector() generated correct type");
+
+        // Verify the results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            if (fabs(poly2DResult[i]-outputOrd->data.F64[i]) > ERROR_TOL) {
+                diag("TEST ERROR: Result[%d] %lg not equal to expected %lg.\n",
+                     i, outputOrd->data.F64[i], poly2DResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial2DEvalVector() produced the correct answers");
+
+
+        // Attempt to invoke function with NULL polynomial
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial2DEvalVector(NULL, inputOrdX, inputOrdY) == NULL, "psPolynomial2DEvalVector() produced NULL when called with NULL polynomial");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial2DEvalVector(polyOrd,NULL,inputOrdY) == NULL, "psPolynomial2DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+    
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial2DEvalVector(polyOrd,inputOrdX,NULL) == NULL, "psPolynomial2DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+    
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputOrdX->type.type = PS_TYPE_U8;
+            ok(psPolynomial2DEvalVector(polyOrd,inputOrdX,inputOrdY) == NULL, "psPolynomial2DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        inputOrdX->type.type = PS_TYPE_F64;
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputOrdY->type.type = PS_TYPE_U8;
+            ok(psPolynomial2DEvalVector(polyOrd,inputOrdX, inputOrdY) == NULL, "psPolynomial2DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        psFree(outputOrd);
+        skip_end();
+        psFree(inputOrdX);
+        psFree(inputOrdY);
+        psFree(polyOrd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+
+    }
+
+
+    // Allocate polynomial, test the psPolynomial2DEvalVector() routines
+    {
+        psMemId id = psMemGetId();
+        // Create input vectors
+        psVector* inputChebX = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputChebY = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            inputChebX->data.F64[i] = poly2DXYChebValue[i][0];
+            inputChebY->data.F64[i] = poly2DXYChebValue[i][1];
+            inputChebX->n++;
+            inputChebY->n++;
+        }
+
+        psPolynomial2D* polyCheb = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1);
+        ok(polyCheb != NULL, "Cheby polynomial allocation successful");
+        skip_start(polyCheb == NULL, 8, "Skipping tests because psPolynomial2DAlloc() failed");
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                polyCheb->coeff[i][j] = 1.0;
+                polyCheb->coeffMask[i][j]  = poly2DMask[i][j];
+            }
+        }
+
+        // Evaluate the vectors
+        psVector* outputCheb = psPolynomial2DEvalVector(polyCheb, inputChebX, inputChebY);
+        ok(outputCheb != NULL, "psPolynomial2DEvalVector() generated non-NULL psVector");
+        ok(outputCheb->type.type == PS_TYPE_F64, "psPolynomial2DEvalVector() generated correct type");
+
+
+        // Verify the results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            if (fabs(poly2DChebResult[i]-outputCheb->data.F64[i]) > ERROR_TOL) {
+                diag("TEST ERROR: ResultCheb[%d] %lg not equal to expected %lg.\n",
+                     i, outputCheb->data.F64[i], poly2DChebResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial2DEvalVector() produced the correct answers");
+
+        // Attempt to invoke function with NULL polynomial
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial2DEvalVector(NULL, inputChebX, inputChebY) == NULL, "psPolynomial2DEvalVector() produced NULL when called with NULL polynomial");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial2DEvalVector(polyCheb,NULL,inputChebY) == NULL, "psPolynomial2DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial2DEvalVector(polyCheb,inputChebX,NULL) == NULL, "psPolynomial2DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputChebX->type.type = PS_TYPE_U8;
+            ok(psPolynomial2DEvalVector(polyCheb,inputChebX,inputChebY) == NULL, "psPolynomial2DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        inputChebX->type.type = PS_TYPE_F64;
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputChebY->type.type = PS_TYPE_U8;
+            ok(psPolynomial2DEvalVector(polyCheb,inputChebX, inputChebY) == NULL, "psPolynomial2DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        psFree(outputCheb);
+        skip_end();
+        psFree(inputChebX);
+        psFree(inputChebY);
+        psFree(polyCheb);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval3D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval3D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval3D.c	(revision 22322)
@@ -0,0 +1,447 @@
+/** tap_psPolynomialEval3D.c
+*
+*  This test driver will exercise the psPolynomialXDEval functions for both
+*  ORD and CHEB type polynomials.
+*
+*  @version  $Revision: 1.9 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2008-05-05 00:09:04 $
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*
+***************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define  TERMS        4
+#define  TESTPOINTS   5
+#define  ERROR_TOL    0.001
+
+psF32 poly3DCoeff[TERMS][TERMS][TERMS] = {  { { -1.1,  1.2, -1.3,  1.4},
+        {  1.5, -1.6,  1.7, -1.8},
+        {  0.1, -0.2,  0.3, -0.4},
+        { -0.5,  0.6, -0.7,  0.8}
+                                            },
+        { { -2.1,  2.2, -2.3,  2.4},
+          {  2.5, -2.6,  2.7, -2.8},
+          {  3.1, -3.2,  3.3, -3.4},
+          { -3.5,  3.6, -3.7,  3.8}
+        },
+        { { -4.1,  4.2, -4.3,  4.4},
+          {  4.5, -4.6,  4.7, -4.8},
+          {  5.1, -5.2,  5.3, -5.4},
+          { -5.5,  5.6, -5.7,  5.8}
+        },
+        { { -6.1,  6.2, -6.3,  6.4},
+          {  6.5, -6.6,  6.7, -6.8},
+          {  7.1, -7.2,  7.3, -7.4},
+          { -7.5,  7.6, -7.7,  7.8}
+        }
+                                         };
+psF64 Dpoly3DCoeff[TERMS][TERMS][TERMS] = {  { { -1.1,  1.2, -1.3,  1.4},
+        {  1.5, -1.6,  1.7, -1.8},
+        {  0.1, -0.2,  0.3, -0.4},
+        { -0.5,  0.6, -0.7,  0.8}
+                                             },
+        { { -2.1,  2.2, -2.3,  2.4},
+          {  2.5, -2.6,  2.7, -2.8},
+          {  3.1, -3.2,  3.3, -3.4},
+          { -3.5,  3.6, -3.7,  3.8}
+        },
+        { { -4.1,  4.2, -4.3,  4.4},
+          {  4.5, -4.6,  4.7, -4.8},
+          {  5.1, -5.2,  5.3, -5.4},
+          { -5.5,  5.6, -5.7,  5.8}
+        },
+        { { -6.1,  6.2, -6.3,  6.4},
+          {  6.5, -6.6,  6.7, -6.8},
+          {  7.1, -7.2,  7.3, -7.4},
+          { -7.5,  7.6, -7.7,  7.8}
+        }
+                                          };
+
+
+psF32 poly3DMask[TERMS][TERMS][TERMS]    = { {  {  0,    0,    0,    1},
+        {  0,    0,    1,    0},
+        {  1,    0,    0,    0},
+        {  0,    0,    0,    1}
+                                             },
+        {  {  1,    0,    0,    0},
+           {  1,    0,    0,    0},
+           {  1,    0,    0,    0},
+           {  1,    0,    0,    0}
+        },
+        {  {  0,    1,    0,    0},
+           {  0,    0,    1,    0},
+           {  0,    1,    0,    0},
+           {  0,    0,    1,    0}
+        },
+        {  {  1,    0,    0,    0},
+           {  0,    0,    0,    1},
+           {  1,    0,    0,    0},
+           {  0,    0,    0,    1}
+        },
+                                           };
+
+psF32 poly3DXYZValue[TESTPOINTS][3] = {  {  0.450, -0.780,  0.500},
+                                      {  0.297,  0.153, -0.354},
+                                      {  0.000,  0.153, -0.354},
+                                      {  0.297,  0.000, -0.354},
+                                      {  0.297,  0.153,  0.000}
+                                      };
+psF64 Dpoly3DXYZValue[TESTPOINTS][3] = {  {  0.450, -0.780,  0.500},
+                                       {  0.297,  0.153, -0.354},
+                                       {  0.000,  0.153, -0.354},
+                                       {  0.297,  0.000, -0.354},
+                                       {  0.297,  0.153,  0.000}
+                                       };
+psF32 poly3DResult[TESTPOINTS]  = { -1.298691, -2.011591, -1.359247, -2.548266, -1.139072};
+psF64 Dpoly3DResult[TESTPOINTS] = { -1.298691, -2.011591, -1.359247, -2.548266, -1.139072};
+
+
+psF32 poly3DXYZChebValue[TESTPOINTS][3] = {  {  0.000,  0.250, -0.250},
+        { -0.250,  0.000,  0.250},
+        {  0.250, -0.250,  0.000},
+        {  0.100, -0.300, -0.400},
+        {  0.990, -0.010,  0.500}
+                                          };
+psF64 Dpoly3DXYZChebValue[TESTPOINTS][3] = {  {  0.000,  0.250, -0.250},
+        { -0.250,  0.000,  0.250},
+        {  0.250, -0.250,  0.000},
+        {  0.100, -0.300, -0.400},
+        {  0.990, -0.010,  0.500}
+                                           };
+psF32 poly3DChebResult[TESTPOINTS]  = {  1.230469, 1.687500, 0.187500, -1.452707, 2.032344 };
+psF64 Dpoly3DChebResult[TESTPOINTS] = {  1.230469, 1.687500, 0.187500, -1.452707, 2.032344 };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(46);
+
+
+    // Evaluate NULL polynomial
+    {
+        psMemId id = psMemGetId();
+        psF64 result = psPolynomial3DEval(NULL, 0.0, 0.0, 0.0);
+        ok(isnan(result), "psPolynomial3DEval() returned NAN with NULL polynomial");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+
+    }
+
+
+    // Allocate and evaluate an ordinary polynomial structure
+    {
+        psMemId id = psMemGetId();
+
+        // Allocate polynomial structure
+        psPolynomial3D*  polyOrd = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1, TERMS-1);
+        ok(polyOrd != NULL, "Ordinary psPolynomial3D successfully allocated");
+        skip_start(polyOrd == NULL, 1, "Skipping tests because psPolynomial3DAlloc() failed");
+
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                for(psS32 k = 0; k < TERMS; k++) {
+                    polyOrd->coeff[i][j][k] = Dpoly3DCoeff[i][j][k];
+                    polyOrd->coeffMask[i][j][k]  = poly3DMask[i][j][k];
+                }
+            }
+        }
+
+        // Evaluate test points and verify results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            psF64 result = psPolynomial3DEval(polyOrd,Dpoly3DXYZValue[i][0],Dpoly3DXYZValue[i][1],
+                                              Dpoly3DXYZValue[i][2]);
+            if (fabs(Dpoly3DResult[i]-result) > ERROR_TOL ) {
+                diag("TEST ERROR: Evaluated value %lg not as expected %lg.\n",
+                     result, Dpoly3DResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial3DEval() successful (Ordinary)");
+
+        psFree(polyOrd);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Allocate and evaluate Cheby polynomial structure
+    {
+        psMemId id = psMemGetId();
+
+        // Allocate polynomial structure
+        psPolynomial3D*  polyCheb = psPolynomial3DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1, TERMS-1);
+        ok(polyCheb != NULL, "Ordinary psPolynomial3D successfully allocated");
+        skip_start(polyCheb == NULL, 1, "Skipping tests because psPolynomial3DAlloc() failed");
+
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                for(psS32 k = 0; k < TERMS; k++) {
+                    polyCheb->coeff[i][j][k] = 1.0;
+                    polyCheb->coeffMask[i][j][k]  = poly3DMask[i][j][k];
+                }
+            }
+        }
+
+        // Evaluate test points and verify results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            psF64 resultCheb = psPolynomial3DEval(polyCheb,Dpoly3DXYZChebValue[i][0],Dpoly3DXYZChebValue[i][1],
+                                                  Dpoly3DXYZChebValue[i][2]);
+            if (fabs(Dpoly3DChebResult[i]-resultCheb) > ERROR_TOL ) {
+                diag("TEST ERROR: Evaluated Chebyshev value %lg not as expected %lg.\n",
+                     resultCheb, Dpoly3DChebResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial3DEval() successful (Cheby)");
+
+        psFree(polyCheb);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate polynomial, test the psPolynomial3DEvalVector() routines
+    {
+        psMemId id = psMemGetId();
+        // Create input vectors
+        psVector* inputOrdX  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputOrdY  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputOrdZ  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            inputOrdX->data.F64[i]  = Dpoly3DXYZValue[i][0];
+            inputOrdY->data.F64[i]  = Dpoly3DXYZValue[i][1];
+            inputOrdZ->data.F64[i]  = Dpoly3DXYZValue[i][2];
+            inputOrdX->n++;
+            inputOrdY->n++;
+            inputOrdZ->n++;
+        }
+
+        psPolynomial3D* polyOrd = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1, TERMS-1);
+        ok(polyOrd != NULL, "Ordinary polynomial allocation successful");
+        skip_start(polyOrd == NULL, 10, "Skipping tests because psPolynomial3DAlloc() failed");
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                for(psS32 k = 0; k < TERMS; k++) {
+                    polyOrd->coeff[i][j][k] = Dpoly3DCoeff[i][j][k];
+                    polyOrd->coeffMask[i][j][k]  = poly3DMask[i][j][k];
+                }
+            }
+        }
+
+        // Evaluate the vectors
+        psVector* outputOrd = psPolynomial3DEvalVector(polyOrd,inputOrdX,inputOrdY,inputOrdZ);
+        ok(outputOrd != NULL, "psPolynomial3DEvalVector() generated non-NULL psVector");
+        ok(outputOrd->type.type == PS_TYPE_F64, "psPolynomial3DEvalVector() generated correct type");
+
+        // Verify the results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            if (fabs(Dpoly3DResult[i]-outputOrd->data.F64[i]) > ERROR_TOL) {
+                diag("TEST ERROR: Result[%d] %lg not equal to expected %lg.\n",
+                     i, outputOrd->data.F64[i], Dpoly3DResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial3DEvalVector() produced the correct answers");
+
+
+        // Attempt to invoke function with NULL polynomial
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial3DEvalVector(NULL,inputOrdX,inputOrdY,inputOrdZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with NULL polynomial");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial3DEvalVector(polyOrd,NULL,inputOrdY,inputOrdZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+    
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial3DEvalVector(polyOrd,inputOrdX,NULL,inputOrdZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial3DEvalVector(polyOrd,inputOrdX,inputOrdY,NULL) == NULL, "psPolynomial3DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputOrdX->type.type = PS_TYPE_U8;
+            ok(psPolynomial3DEvalVector(polyOrd,inputOrdX,inputOrdY,inputOrdZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputOrdX->type.type = PS_TYPE_F64;
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputOrdY->type.type = PS_TYPE_U8;
+            ok(psPolynomial3DEvalVector(polyOrd,inputOrdX,inputOrdY,inputOrdZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputOrdY->type.type = PS_TYPE_F64;
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputOrdZ->type.type = PS_TYPE_U8;
+            ok(psPolynomial3DEvalVector(polyOrd,inputOrdX,inputOrdY,inputOrdZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        psFree(outputOrd);
+        skip_end();
+        psFree(inputOrdX);
+        psFree(inputOrdY);
+        psFree(inputOrdZ);
+        psFree(polyOrd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate polynomial, test the psPolynomial3DEvalVector() routines
+    {
+        psMemId id = psMemGetId();
+        // Create input vectors
+        psVector* inputChebX = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputChebY = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputChebZ = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            inputChebX->data.F64[i] = Dpoly3DXYZChebValue[i][0];
+            inputChebY->data.F64[i] = Dpoly3DXYZChebValue[i][1];
+            inputChebZ->data.F64[i] = Dpoly3DXYZChebValue[i][2];
+            inputChebX->n++;
+            inputChebY->n++;
+            inputChebZ->n++;
+        }
+        psPolynomial3D* polyCheb = psPolynomial3DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1, TERMS-1);
+        ok(polyCheb != NULL, "Ordinary polynomial allocation successful");
+        skip_start(polyCheb == NULL, 10, "Skipping tests because psPolynomial3DAlloc() failed");
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                for(psS32 k = 0; k < TERMS; k++) {
+                    polyCheb->coeff[i][j][k] = 1.0;
+                    polyCheb->coeffMask[i][j][k]  = poly3DMask[i][j][k];
+                }
+            }
+        }
+
+        // Evaluate the vectors
+        psVector* outputCheb = psPolynomial3DEvalVector(polyCheb,inputChebX,inputChebY,inputChebZ);
+        ok(outputCheb != NULL, "psPolynomial3DEvalVector() generated non-NULL psVector");
+        ok(outputCheb->type.type == PS_TYPE_F64, "psPolynomial3DEvalVector() generated correct type");
+
+        // Verify the results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            if (fabs(Dpoly3DChebResult[i]-outputCheb->data.F64[i]) > ERROR_TOL) {
+                diag("TEST ERROR: ResultCheb[%d] %lg not equal to expected %lg.\n",
+                     i, outputCheb->data.F64[i], Dpoly3DChebResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial3DEvalVector() produced the correct answers");
+
+        // Attempt to invoke function with NULL polynomial
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial3DEvalVector(NULL,inputChebX,inputChebY,inputChebZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with NULL polynomial");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial3DEvalVector(polyCheb,NULL,inputChebY,inputChebZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial3DEvalVector(polyCheb,inputChebX,NULL,inputChebZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial3DEvalVector(polyCheb,inputChebX,inputChebY,NULL) == NULL, "psPolynomial3DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputChebX->type.type = PS_TYPE_U8;
+            ok(psPolynomial3DEvalVector(polyCheb,inputChebX,inputChebY,inputChebZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputChebX->type.type = PS_TYPE_F64;
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputChebY->type.type = PS_TYPE_U8;
+            ok(psPolynomial3DEvalVector(polyCheb,inputChebX,inputChebY,inputChebZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputChebY->type.type = PS_TYPE_F64;
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputChebZ->type.type = PS_TYPE_U8;
+            ok(psPolynomial3DEvalVector(polyCheb,inputChebX,inputChebY,inputChebZ) == NULL, "psPolynomial3DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        psFree(outputCheb);
+        skip_end();
+        psFree(inputChebX);
+        psFree(inputChebY);
+        psFree(inputChebZ);
+        psFree(polyCheb);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval4D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval4D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialEval4D.c	(revision 22322)
@@ -0,0 +1,742 @@
+/** tst_psFunc11.c
+*
+*  This test driver will exercise the psPolynomialXDEval functions for both
+*  ORD and CHEB type polynomials.
+*
+*  @version  $Revision: 1.7 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2008-05-05 00:09:04 $
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*
+***************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define  TERMS        4
+#define  TESTPOINTS   5
+#define  ERROR_TOL    0.001
+
+psF32 poly4DCoeff[TERMS][TERMS][TERMS][TERMS] = {
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            }
+        };
+psF64 Dpoly4DCoeff[TERMS][TERMS][TERMS][TERMS] = {
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            }
+        };
+
+psF32 poly4DMask[TERMS][TERMS][TERMS][TERMS]    = {
+            {
+                {
+                    {  0,    0,    0,    1},
+                    {  0,    0,    1,    0},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0}
+                },
+                {
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0},
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                }
+            },
+            {
+                {
+                    {  0,    0,    0,    1},
+                    {  0,    0,    1,    0},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0}
+                },
+                {
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0},
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                }
+            },
+            {
+                {
+                    {  0,    0,    0,    1},
+                    {  0,    0,    1,    0},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0}
+                },
+                {
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0},
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                }
+            },
+            {
+                {
+                    {  0,    0,    0,    1},
+                    {  0,    0,    1,    0},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0}
+                },
+                {
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0},
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                }
+            }
+        };
+
+psF32 poly4DWXYZValue[TESTPOINTS][4] = {
+                                           {  0.450, -0.780,  0.500, -0.123},
+                                           {  0.297,  0.153, -0.354,  0.000},
+                                           {  0.000,  0.153, -0.354,  0.321},
+                                           {  0.297,  0.000, -0.354,  0.321},
+                                           {  0.297,  0.153,  0.000,  0.321}
+                                       };
+psF64 Dpoly4DWXYZValue[TESTPOINTS][4] = {
+                                            {  0.450, -0.780,  0.500, -0.123},
+                                            {  0.297,  0.153, -0.354,  0.000},
+                                            {  0.000,  0.153, -0.354,  0.321},
+                                            {  0.297,  0.000, -0.354,  0.321},
+                                            {  0.297,  0.153,  0.000,  0.321}
+                                        };
+
+psF32 poly4DResult[TESTPOINTS]  = { -3.588753, -2.439566, -1.175955, -1.645497, -1.216915};
+psF64 Dpoly4DResult[TESTPOINTS]  = { -3.588753, -2.439566, -1.175955, -1.645497, -1.216915};
+
+psF32 poly4DWXYZChebValue[TESTPOINTS][4] = {
+            {  0.100,  0.000,  0.250, -0.250},
+            {  0.100, -0.250,  0.000,  0.250},
+            {  0.100,  0.250, -0.250,  0.000},
+            {  0.300,  0.200, -0.300, -0.400},
+            { -0.780,  0.990, -0.010,  0.500}
+        };
+psF64 Dpoly4DWXYZChebValue[TESTPOINTS][4] = {
+            {  0.100,  0.000,  0.250, -0.250},
+            {  0.100, -0.250,  0.000,  0.250},
+            {  0.100,  0.250, -0.250,  0.000},
+            {  0.300,  0.200, -0.300, -0.400},
+            { -0.780,  0.990, -0.010,  0.500}
+        };
+psF32 poly4DChebResult[TESTPOINTS]   = { -0.216563, -0.297000, -0.033000, 0.432198, 1.785601 };
+psF64 Dpoly4DChebResult[TESTPOINTS]  = { -0.216563, -0.297000, -0.033000, 0.432198, 1.785601 };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(54);
+
+
+    // Evaluate NULL polynomial
+    {
+        psMemId id = psMemGetId();
+        psF64 result = psPolynomial4DEval(NULL, 0.0, 0.0, 0.0, 0.0);
+        ok(isnan(result), "psPolynomial4DEval() returned NAN with unallowed type");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate and evaluate an ordinary polynomial structure
+    {
+        psMemId id = psMemGetId();
+        // Allocate polynomial structure
+        psPolynomial4D*  polyOrd = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1, TERMS-1, TERMS-1);
+        ok(polyOrd != NULL, "Ordinary psPolynomial4D successfully allocated");
+        skip_start(polyOrd == NULL, 1, "Skipping tests because psPolynomial4DAlloc() failed");
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                for(psS32 k = 0; k < TERMS; k++) {
+                    for(psS32 l = 0; l < TERMS; l++) {
+                        polyOrd->coeff[i][j][k][l] = Dpoly4DCoeff[i][j][k][l];
+                        polyOrd->coeffMask[i][j][k][l]  = poly4DMask[i][j][k][l];
+                    }
+                }
+            }
+        }
+
+        // Evaluate test points and verify results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            psF64 result = psPolynomial4DEval(polyOrd, Dpoly4DWXYZValue[i][0], Dpoly4DWXYZValue[i][1],
+                                              Dpoly4DWXYZValue[i][2], Dpoly4DWXYZValue[i][3]);
+            if (fabs(Dpoly4DResult[i]-result) > ERROR_TOL ) {
+                diag("TEST ERROR: Evaluated value %lg not as expected %lg.\n",
+                     result, Dpoly4DResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial3DEval() successful (Ordinary)");
+
+        psFree(polyOrd);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Allocate and evaluate an ordinary polynomial structure
+    {
+        psMemId id = psMemGetId();
+        // Allocate polynomial structure
+        psPolynomial4D*  polyCheb = psPolynomial4DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1, TERMS-1, TERMS-1);
+        ok(polyCheb != NULL, "Ordinary psPolynomial4D successfully allocated");
+        skip_start(polyCheb == NULL, 1, "Skipping tests because psPolynomial4DAlloc() failed");
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                for(psS32 k = 0; k < TERMS; k++) {
+                    for(psS32 l = 0; l < TERMS; l++) {
+                        polyCheb->coeff[i][j][k][l] = 1.0;
+                        polyCheb->coeffMask[i][j][k][l]  = poly4DMask[i][j][k][l];
+                    }
+                }
+            }
+        }
+
+        // Evaluate test points and verify results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            psF64 resultCheb = psPolynomial4DEval(polyCheb, Dpoly4DWXYZChebValue[i][0], Dpoly4DWXYZChebValue[i][1],
+                                                  Dpoly4DWXYZChebValue[i][2], Dpoly4DWXYZChebValue[i][3]);
+            if (fabs(Dpoly4DChebResult[i]-resultCheb) > ERROR_TOL ) {
+                diag("TEST ERROR: Evaluated Chebyshev value %lg not as expected %lg.\n",
+                     resultCheb, Dpoly4DChebResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial3DEval() successful (Ordinary)");
+
+        psFree(polyCheb);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Allocate polynomial, test the psPolynomial4DEvalVector() routines
+    {
+        psMemId id = psMemGetId();
+        // Create input vectors
+        psVector* inputOrdW  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputOrdX  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputOrdY  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputOrdZ  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            inputOrdW->data.F64[i]  = Dpoly4DWXYZValue[i][0];
+            inputOrdX->data.F64[i]  = Dpoly4DWXYZValue[i][1];
+            inputOrdY->data.F64[i]  = Dpoly4DWXYZValue[i][2];
+            inputOrdZ->data.F64[i]  = Dpoly4DWXYZValue[i][3];
+            inputOrdW->n++;
+            inputOrdX->n++;
+            inputOrdY->n++;
+            inputOrdZ->n++;
+        }
+
+        // Allocate polynomial
+        psPolynomial4D* polyOrd = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1, TERMS-1, TERMS-1);
+        ok(polyOrd != NULL, "Ordinary polynomial allocation successful");
+        skip_start(polyOrd == NULL, 12, "Skipping tests because psPolynomial4DAlloc() failed");
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                for(psS32 k = 0; k < TERMS; k++) {
+                    for(psS32 l = 0; l < TERMS; l++) {
+                        polyOrd->coeff[i][j][k][l] = Dpoly4DCoeff[i][j][k][l];
+                        polyOrd->coeffMask[i][j][k][l]  = poly4DMask[i][j][k][l];
+                    }
+                }
+            }
+        }
+
+        // Evaluate the vectors
+        psVector* outputOrd = psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,inputOrdZ);
+        ok(outputOrd != NULL, "psPolynomial4DEvalVector() generated non-NULL psVector");
+        ok(outputOrd->type.type == PS_TYPE_F64, "psPolynomial4DEvalVector() generated correct type");
+
+        // Verify the results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            if (fabs(Dpoly4DResult[i]-outputOrd->data.F64[i]) > ERROR_TOL) {
+                diag("TEST ERROR: Result[%d] %lg not equal to expected %lg",
+                     i, outputOrd->data.F64[i], Dpoly4DResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial4DEvalVector() produced the correct answers");
+
+
+        // Attempt to invoke function with NULL polynomial
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial4DEvalVector(NULL,inputOrdW,inputOrdX,inputOrdY,inputOrdZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with NULL polynomial");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial4DEvalVector(polyOrd,NULL,inputOrdX,inputOrdY,inputOrdZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial4DEvalVector(polyOrd,inputOrdW,NULL,inputOrdY,inputOrdZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,NULL,inputOrdZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,NULL) == NULL, "psPolynomial4DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputOrdX->type.type = PS_TYPE_U8;
+            ok(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,inputOrdZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputOrdX->type.type = PS_TYPE_F64;
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputOrdY->type.type = PS_TYPE_U8;
+            ok(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,inputOrdZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputOrdY->type.type = PS_TYPE_F64;
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputOrdZ->type.type = PS_TYPE_U8;
+            ok(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,inputOrdZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputOrdZ->type.type = PS_TYPE_F64;
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputOrdW->type.type = PS_TYPE_U8;
+            ok(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,inputOrdZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputOrdW->type.type = PS_TYPE_F64;
+        psFree(outputOrd);
+        skip_end();
+        psFree(inputOrdX);
+        psFree(inputOrdY);
+        psFree(inputOrdZ);
+        psFree(inputOrdW);
+        psFree(polyOrd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Allocate polynomial, test the psPolynomial4DEvalVector() routines
+    {
+        psMemId id = psMemGetId();
+        // Create input vectors
+        psVector* inputChebW = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputChebX = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputChebY = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        psVector* inputChebZ = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            inputChebW->data.F64[i] = Dpoly4DWXYZChebValue[i][0];
+            inputChebX->data.F64[i] = Dpoly4DWXYZChebValue[i][1];
+            inputChebY->data.F64[i] = Dpoly4DWXYZChebValue[i][2];
+            inputChebZ->data.F64[i] = Dpoly4DWXYZChebValue[i][3];
+            inputChebW->n++;
+            inputChebX->n++;
+            inputChebY->n++;
+            inputChebZ->n++;
+        }
+
+        // Allocate polynomial
+        psPolynomial4D* polyCheb = psPolynomial4DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1, TERMS-1, TERMS-1);
+        ok(polyCheb != NULL, "Ordinary polynomial allocation successful");
+        skip_start(polyCheb == NULL, 12, "Skipping tests because psPolynomial4DAlloc() failed");
+
+        // Set polynomial members
+        for(psS32 i = 0; i < TERMS; i++)
+        {
+            for(psS32 j = 0; j < TERMS; j++) {
+                for(psS32 k = 0; k < TERMS; k++) {
+                    for(psS32 l = 0; l < TERMS; l++) {
+                        polyCheb->coeff[i][j][k][l] = 1.0;
+                        polyCheb->coeffMask[i][j][k][l]  = poly4DMask[i][j][k][l];
+                    }
+                }
+            }
+        }
+
+        // Evaluate the vectors
+        psVector* outputCheb = psPolynomial4DEvalVector(polyCheb,inputChebW,inputChebX,inputChebY,inputChebZ);
+        ok(outputCheb != NULL, "psPolynomial4DEvalVector() generated non-NULL psVector");
+        ok(outputCheb->type.type == PS_TYPE_F64, "psPolynomial4DEvalVector() generated correct type");
+
+        // Verify the results
+        bool errorFlag = false;
+        for(psS32 i = 0; i < TESTPOINTS; i++)
+        {
+            if (fabs(Dpoly4DChebResult[i]-outputCheb->data.F64[i]) > ERROR_TOL) {
+                diag("TEST ERROR: ResultCheb[%d] %lg not equal to expected %lg",
+                     i, outputCheb->data.F64[i], Dpoly4DChebResult[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psPolynomial4DEvalVector() produced the correct answers");
+
+
+        // Attempt to invoke function with NULL polynomial
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial4DEvalVector(NULL,inputChebW,inputChebX,inputChebY,inputChebZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with NULL polynomial");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial4DEvalVector(polyCheb,NULL,inputChebX,inputChebY,inputChebZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial4DEvalVector(polyCheb,inputChebW,NULL,inputChebY,inputChebZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial4DEvalVector(polyCheb,inputChebW,inputChebX,NULL,inputChebZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with NULL input vector
+        {
+            psMemId id = psMemGetId();
+            ok(psPolynomial4DEvalVector(polyCheb,inputChebW,inputChebX,inputChebY,NULL) == NULL, "psPolynomial4DEvalVector() produced NULL when called with NULL input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputChebX->type.type = PS_TYPE_U8;
+            ok(psPolynomial4DEvalVector(polyCheb,inputChebW,inputChebX,inputChebY,inputChebZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputChebX->type.type = PS_TYPE_F64;
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputChebY->type.type = PS_TYPE_U8;
+            ok(psPolynomial4DEvalVector(polyCheb,inputChebW,inputChebX,inputChebY,inputChebZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputChebY->type.type = PS_TYPE_F64;
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputChebZ->type.type = PS_TYPE_U8;
+            ok(psPolynomial4DEvalVector(polyCheb,inputChebW,inputChebX,inputChebY,inputChebZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputChebZ->type.type = PS_TYPE_F64;
+
+
+        // Attempt to invoke function with a non F64 type input vector
+        {
+            psMemId id = psMemGetId();
+            inputChebW->type.type = PS_TYPE_U8;
+            ok(psPolynomial4DEvalVector(polyCheb,inputChebW,inputChebX,inputChebY,inputChebZ) == NULL, "psPolynomial4DEvalVector() produced NULL when called with non F64 input vector");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+        inputChebW->type.type = PS_TYPE_F64;
+        psFree(outputCheb);
+        skip_end();
+        psFree(inputChebW);
+        psFree(inputChebX);
+        psFree(inputChebY);
+        psFree(inputChebZ);
+        psFree(polyCheb);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialMD.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialMD.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialMD.c	(revision 22322)
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+#define XORDER 3
+#define YORDER 2
+static float constant = 1.0;
+static float xCoeff[XORDER] = { 5.0, -3.0, 2.0 };
+static float yCoeff[YORDER] = { -6.0, 4.0 };
+#define XNUM 10
+#define YNUM 10
+#define NUM ((XNUM)*(YNUM))
+#define TOL 1.0e-8
+
+int main(int argc, char *argv[])
+{
+    plan_tests(2 * (1 + (1 + XORDER + YORDER)) + 1);
+
+    {
+        psMemId id = psMemGetId();
+
+        psArray *ordinates = psArrayAlloc(NUM);
+        psVector *values = psVectorAlloc(NUM, PS_TYPE_F32);
+
+        for (int i = 0, index = 0; i < XNUM; i++) {
+            int x = i - XNUM/2;
+            for (int j = 0; j < YNUM; j++, index++) {
+                int y = j - YNUM/2;
+
+                float value = constant;
+                for (int k = 0; k < XORDER; k++) {
+                    value += xCoeff[k] * powf(x, k+1);
+                }
+                for (int k = 0; k < YORDER; k++) {
+                    value += yCoeff[k] * powf(y, k+1);
+                }
+
+                values->data.F32[index] = value;
+                psVector *ord = psVectorAlloc(2, PS_TYPE_F32);
+                ord->data.F32[0] = x;
+                ord->data.F32[1] = y;
+                ordinates->data[index] = ord;
+            }
+        }
+
+        psVector *orders = psVectorAlloc(2, PS_TYPE_S32);
+        orders->data.S32[0] = XORDER;
+        orders->data.S32[1] = YORDER;
+
+        {
+            psPolynomialMD *poly = psPolynomialMDAlloc(orders);
+            bool polyOK = psPolynomialMDFit(poly, values, NULL, NULL, 0, ordinates);
+            ok(polyOK, "Fit polynomial");
+            skip_start(!polyOK, 1+XORDER+YORDER, "Skipping coefficient checks since fit failed.");
+            int index = 0;
+            is_double_tol(poly->coeff->data.F64[index], constant, TOL, "Coefficient %d", index);
+            index++;
+            for (int i = 0; i < XORDER; i++, index++) {
+                is_double_tol(poly->coeff->data.F64[index], xCoeff[i], TOL, "Coefficient %d", index);
+            }
+            for (int i = 0; i < YORDER; i++, index++) {
+                is_double_tol(poly->coeff->data.F64[index], yCoeff[i], TOL, "Coefficient %d", index);
+            }
+            skip_end();
+            psFree(poly);
+        }
+
+        {
+            psPolynomialMD *poly = psPolynomialMDAlloc(orders);
+            values->data.F32[NUM/2] *= 10;
+            bool polyOK = psPolynomialMDClipFit(poly, values, NULL, NULL, 0, ordinates, 1, 3.0);
+            ok(polyOK, "Clip-fit polynomial");
+            skip_start(!polyOK, 1+XORDER+YORDER, "Skipping coefficient checks since clip-fit failed.");
+            int index = 0;
+            is_double_tol(poly->coeff->data.F64[index], constant, TOL, "Coefficient %d", index);
+            index++;
+            for (int i = 0; i < XORDER; i++, index++) {
+                is_double_tol(poly->coeff->data.F64[index], xCoeff[i], TOL, "Coefficient %d", index);
+            }
+            for (int i = 0; i < YORDER; i++, index++) {
+                is_double_tol(poly->coeff->data.F64[index], yCoeff[i], TOL, "Coefficient %d", index);
+            }
+            skip_end();
+            psFree(poly);
+        }
+
+        psFree(orders);
+        psFree(values);
+        psFree(ordinates);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialUtils_Derivatives.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialUtils_Derivatives.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psPolynomialUtils_Derivatives.c	(revision 22322)
@@ -0,0 +1,276 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int main (void)
+{
+    plan_tests(54);
+
+    note("psPolynomial2D Derivative tests");
+
+    // test psPolynomial2D_dX (no supplied output)
+    {
+        psMemId id = psMemGetId();
+
+        note ("test psPolynomial2D_dX (no supplied output)");
+
+        psPolynomial2D *poly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 2, 2);
+        ok(poly != NULL, "psPolynomial2D successfully allocated");
+        skip_start(poly == NULL, 5, "Skipping tests because psPolynomial2DAlloc() failed");
+
+        // build sample polynomial (upper-left elements only)
+        // z = 5 + 2x + 3y - 3x^2 + 4xy - 2y^2
+        // dz/dx = 2 - 6x + 4y
+        poly->coeff[0][0] = 5.0;
+        poly->coeff[1][0] = 2.0;
+        poly->coeff[0][1] = 3.0;
+        poly->coeff[2][0] = -3.0;
+        poly->coeff[1][1] = 4.0;
+        poly->coeff[0][2] = -2.0;
+
+        // mask remaining elements
+        poly->coeffMask[2][1] = 1;
+        poly->coeffMask[1][2] = 1;
+        poly->coeffMask[2][2] = 1;
+
+        psPolynomial2D *dX = psPolynomial2D_dX (NULL, poly);
+
+        ok(dX->nX == 1, "new x order is %d", dX->nX);
+        ok(dX->nY == 2, "new y order is %d", dX->nY);
+
+        is_float(dX->coeff[0][0], +2.0, "x^0 y^0 coeff is %f", dX->coeff[0][0]);
+        is_float(dX->coeff[1][0], -6.0, "x^1 y^0 coeff is %f", dX->coeff[1][0]);
+        is_float(dX->coeff[0][1], +4.0, "x^0 y^1 coeff is %f", dX->coeff[0][1]);
+
+        ok(!dX->coeffMask[0][0], "x^0 y^0 coeff is unmasked");
+        ok(!dX->coeffMask[1][0], "x^1 y^0 coeff is unmasked");
+        ok(!dX->coeffMask[0][1], "x^0 y^1 coeff is unmasked");
+
+        ok(dX->coeffMask[1][1], "x^1 y^1 coeff is masked");
+        ok(dX->coeffMask[1][2], "x^1 y^2 coeff is masked");
+
+        psFree (dX);
+        psFree (poly);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test psPolynomial2D_dX (supplied output)
+    {
+        psMemId id = psMemGetId();
+
+        note ("test psPolynomial2D_dX (supplied output)");
+
+        psPolynomial2D *poly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 2, 2);
+        ok(poly != NULL, "psPolynomial2D successfully allocated");
+        skip_start(poly == NULL, 5, "Skipping tests because psPolynomial2DAlloc() failed");
+
+        // build sample polynomial (upper-left elements only)
+        // z = 5 + 2x + 3y - 3x^2 + 4xy - 2y^2
+        // dz/dx = 2 - 6x + 4y
+        poly->coeff[0][0] = 5.0;
+        poly->coeff[1][0] = 2.0;
+        poly->coeff[0][1] = 3.0;
+        poly->coeff[2][0] = -3.0;
+        poly->coeff[1][1] = 4.0;
+        poly->coeff[0][2] = -2.0;
+
+        // mask remaining elements
+        poly->coeffMask[2][1] = 1;
+        poly->coeffMask[1][2] = 1;
+        poly->coeffMask[2][2] = 1;
+
+        psPolynomial2D *dX = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 2, 2);
+        psPolynomial2D_dX (dX, poly);
+
+        ok(dX->nX == 1, "new x order is %d", dX->nX);
+        ok(dX->nY == 2, "new y order is %d", dX->nY);
+
+        is_float(dX->coeff[0][0], +2.0, "x^0 y^0 coeff is %f", dX->coeff[0][0]);
+        is_float(dX->coeff[1][0], -6.0, "x^1 y^0 coeff is %f", dX->coeff[1][0]);
+        is_float(dX->coeff[0][1], +4.0, "x^0 y^1 coeff is %f", dX->coeff[0][1]);
+
+        ok(!dX->coeffMask[0][0], "x^0 y^0 coeff is unmasked");
+        ok(!dX->coeffMask[1][0], "x^1 y^0 coeff is unmasked");
+        ok(!dX->coeffMask[0][1], "x^0 y^1 coeff is unmasked");
+
+        ok(dX->coeffMask[1][1], "x^1 y^1 coeff is masked");
+        ok(dX->coeffMask[1][2], "x^1 y^2 coeff is masked");
+
+        psFree (dX);
+        psFree (poly);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test psPolynomial2D_dX (inPlace: supplied output == supplied input)
+    {
+        psMemId id = psMemGetId();
+
+        note ("test psPolynomial2D_dX (supplied output)");
+
+        psPolynomial2D *poly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 2, 2);
+        ok(poly != NULL, "psPolynomial2D successfully allocated");
+        skip_start(poly == NULL, 5, "Skipping tests because psPolynomial2DAlloc() failed");
+
+        // build sample polynomial (upper-left elements only)
+        // z = 5 + 2x + 3y - 3x^2 + 4xy - 2y^2
+        // dz/dx = 2 - 6x + 4y
+        poly->coeff[0][0] = 5.0;
+        poly->coeff[1][0] = 2.0;
+        poly->coeff[0][1] = 3.0;
+        poly->coeff[2][0] = -3.0;
+        poly->coeff[1][1] = 4.0;
+        poly->coeff[0][2] = -2.0;
+
+        // mask remaining elements
+        poly->coeffMask[2][1] = 1;
+        poly->coeffMask[1][2] = 1;
+        poly->coeffMask[2][2] = 1;
+
+        psPolynomial2D *result = psPolynomial2D_dX (poly, poly);
+        ok (result == NULL, "psPolynomial2D_dX failed as expected: cannot assign output to input");
+
+        psFree (poly);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test psPolynomial2D_dY (no supplied output)
+    {
+        psMemId id = psMemGetId();
+
+        note ("test psPolynomial2D_dY (no supplied output)");
+
+        psPolynomial2D *poly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 2, 2);
+        ok(poly != NULL, "psPolynomial2D successfully allocated");
+        skip_start(poly == NULL, 5, "Skipping tests because psPolynomial2DAlloc() failed");
+
+        // build sample polynomial (upper-left elements only)
+        // z = 5 + 2x + 3y - 3x^2 + 4xy - 2y^2
+        // dz/dy = 3 + 4x - 4y
+        poly->coeff[0][0] = 5.0;
+        poly->coeff[1][0] = 2.0;
+        poly->coeff[0][1] = 3.0;
+        poly->coeff[2][0] = -3.0;
+        poly->coeff[1][1] = 4.0;
+        poly->coeff[0][2] = -2.0;
+
+        // mask remaining elements
+        poly->coeffMask[2][1] = 1;
+        poly->coeffMask[1][2] = 1;
+        poly->coeffMask[2][2] = 1;
+
+        psPolynomial2D *dY = psPolynomial2D_dY (NULL, poly);
+
+        ok(dY->nX == 2, "new x order is %d", dY->nX);
+        ok(dY->nY == 1, "new y order is %d", dY->nY);
+
+        is_float(dY->coeff[0][0], +3.0, "x^0 y^0 coeff is %f", dY->coeff[0][0]);
+        is_float(dY->coeff[1][0], +4.0, "x^1 y^0 coeff is %f", dY->coeff[1][0]);
+        is_float(dY->coeff[0][1], -4.0, "x^0 y^1 coeff is %f", dY->coeff[0][1]);
+
+        ok(!dY->coeffMask[0][0], "x^0 y^0 coeff is unmasked");
+        ok(!dY->coeffMask[1][0], "x^1 y^0 coeff is unmasked");
+        ok(!dY->coeffMask[0][1], "x^0 y^1 coeff is unmasked");
+
+        ok(dY->coeffMask[1][1], "x^1 y^1 coeff is masked");
+        ok(dY->coeffMask[1][2], "x^1 y^2 coeff is masked");
+
+        psFree (dY);
+        psFree (poly);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test psPolynomial2D_dY (supplied output)
+    {
+        psMemId id = psMemGetId();
+
+        note ("test psPolynomial2D_dY (supplied output)");
+
+        psPolynomial2D *poly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 2, 2);
+        ok(poly != NULL, "psPolynomial2D successfully allocated");
+        skip_start(poly == NULL, 5, "Skipping tests because psPolynomial2DAlloc() failed");
+
+        // build sample polynomial (upper-left elements only)
+        // z = 5 + 2x + 3y - 3x^2 + 4xy - 2y^2
+        // dz/dy = 3 + 4x - 4y
+        poly->coeff[0][0] = 5.0;
+        poly->coeff[1][0] = 2.0;
+        poly->coeff[0][1] = 3.0;
+        poly->coeff[2][0] = -3.0;
+        poly->coeff[1][1] = 4.0;
+        poly->coeff[0][2] = -2.0;
+
+        // mask remaining elements
+        poly->coeffMask[2][1] = 1;
+        poly->coeffMask[1][2] = 1;
+        poly->coeffMask[2][2] = 1;
+
+        psPolynomial2D *dY = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 2, 2);
+        psPolynomial2D_dY (dY, poly);
+
+        ok(dY->nX == 2, "new x order is %d", dY->nX);
+        ok(dY->nY == 1, "new y order is %d", dY->nY);
+
+        is_float(dY->coeff[0][0], +3.0, "x^0 y^0 coeff is %f", dY->coeff[0][0]);
+        is_float(dY->coeff[1][0], +4.0, "x^1 y^0 coeff is %f", dY->coeff[1][0]);
+        is_float(dY->coeff[0][1], -4.0, "x^0 y^1 coeff is %f", dY->coeff[0][1]);
+
+        ok(!dY->coeffMask[0][0], "x^0 y^0 coeff is unmasked");
+        ok(!dY->coeffMask[1][0], "x^1 y^0 coeff is unmasked");
+        ok(!dY->coeffMask[0][1], "x^0 y^1 coeff is unmasked");
+
+        ok(dY->coeffMask[1][1], "x^1 y^1 coeff is masked");
+        ok(dY->coeffMask[1][2], "x^1 y^2 coeff is masked");
+
+        psFree (dY);
+        psFree (poly);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test psPolynomial2D_dY (supplied output)
+    {
+        psMemId id = psMemGetId();
+
+        note ("test psPolynomial2D_dY (supplied output)");
+
+        psPolynomial2D *poly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 2, 2);
+        ok(poly != NULL, "psPolynomial2D successfully allocated");
+        skip_start(poly == NULL, 5, "Skipping tests because psPolynomial2DAlloc() failed");
+
+        // build sample polynomial (upper-left elements only)
+        // z = 5 + 2x + 3y - 3x^2 + 4xy - 2y^2
+        // dz/dy = 3 + 4x - 4y
+        poly->coeff[0][0] = 5.0;
+        poly->coeff[1][0] = 2.0;
+        poly->coeff[0][1] = 3.0;
+        poly->coeff[2][0] = -3.0;
+        poly->coeff[1][1] = 4.0;
+        poly->coeff[0][2] = -2.0;
+
+        // mask remaining elements
+        poly->coeffMask[2][1] = 1;
+        poly->coeffMask[1][2] = 1;
+        poly->coeffMask[2][2] = 1;
+
+        psPolynomial2D *result = psPolynomial2D_dY (poly, poly);
+        ok (result == NULL, "psPolynomial2D_dY failed as expected: cannot assign output to input");
+
+        psFree (poly);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psRandom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psRandom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psRandom.c	(revision 22322)
@@ -0,0 +1,363 @@
+/*****************************************************************************
+This routine must ensure that the various random number generator functions
+work properly.
+ 
+    ensure that psRandom structs are properly allocated by psRandomAlloc().
+    ensure that psRandomUniform() produces a sequence of numbers with
+        proper mean and stdev.
+    ensure that psRandomGaussian() produces a sequence of numbers with
+        proper mean and stdev.
+    ensure that psRandomPoisson() produces a sequence of numbers with
+        proper mean and stdev.
+    ensure that psRandomReset() properly seeds the random number
+        generator for psRandomUniform().
+    ensure that psRandomReset() properly seeds the random number
+        generator for psRandomGaussian().
+    ensure that psRandomReset() properly seeds the random number
+        generator for psRandomPoisson().
+ 
+XXX: I removed a test that generated error output.  How should we test that?
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#include <math.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define NUM_DATA 10000
+#define SEED 54321
+#define SEED2 345
+#define UNIFORM_MEAN 0.5
+#define UNIFORM_STDEV 0.3
+#define GAUSSIAN_MEAN 0.0
+#define GAUSSIAN_STDEV 1.0
+#define POISSON_MEAN 15.0
+#define POISSON_STDEV (POISSON_MEAN / 4)
+#define ERROR_TOLERANCE 0.2
+#define VERBOSE 0
+# define is_float_tol_per(VALUE,EXPECT,TOL,COMMENT, ...)\
+ok((fabs((VALUE)-(EXPECT)) < (TOL)), COMMENT, ## __VA_ARGS__);
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    plan_tests(34);
+
+    // ensure that psRandom structs are properly allocated by psRandomAlloc()
+    {
+        psMemId id = psMemGetId();
+        // Valid type allocation
+        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAlloc() failed");
+        ok(myRNG->type == PS_RANDOM_TAUS, "psRandomAlloc() set type properly");
+        psFree(myRNG);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Unallowed type allocation
+    {
+        psMemId id = psMemGetId();
+        psRandom *myRNG = psRandomAlloc(100,SEED);
+        ok(myRNG == NULL, "psRandomAlloc() refused to generate psRandom with unallowed type");
+        psFree(myRNG);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Negative seed value
+    {
+        psMemId id = psMemGetId();
+        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS,-5);
+        ok(myRNG != NULL, "psRandomAlloc() allows negative seed");
+        psFree(myRNG);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // testRandomUniform(void)
+    {
+        // testRandomUniform()
+        psMemId id = psMemGetId();
+        psVector *rans = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans->n = rans->nalloc;
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 2, "Skipping tests because psRandomAlloc() failed");
+
+        // Initialize vector data with random number
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans->data.F64[i] = psRandomUniform(myRNG);
+        }
+
+        // Perform vector stats on random data (mean, stdev)
+        psVectorStats(stats, rans, NULL, NULL, 0);
+        stats->options = PS_STAT_SAMPLE_STDEV;
+        psVectorStats(stats, rans, NULL, NULL, 0);
+
+        // Verify mean and stdev
+        is_float_tol_per(stats->sampleMean, UNIFORM_MEAN, ERROR_TOLERANCE, "Mean is within expected range");
+        is_float_tol_per(stats->sampleStdev, UNIFORM_STDEV, ERROR_TOLERANCE, "StDev is within expected range");
+
+        skip_end();
+
+        psFree(myRNG);
+        psFree(rans);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Ensure psRandomUniform() returns 0 for NULL psRandom struct
+    {
+        psMemId id = psMemGetId();
+        ok(psRandomUniform(NULL) == 0, "Ensure psRandomUniform() returns 0 for NULL psRandom struct");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // testRandomGaussian(void)
+    {
+        diag("testRandomGaussian()");
+        psMemId id = psMemGetId();
+        psVector *rans = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans->n = rans->nalloc;
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 2, "Skipping tests because psRandomAlloc() failed");
+
+        // Initialize vector with random data
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans->data.F64[i] = psRandomGaussian(myRNG);
+        }
+
+        // Perform vector stats on data (mean, stdev)
+        psVectorStats(stats, rans, NULL, NULL, 0);
+        stats->options = PS_STAT_SAMPLE_STDEV;
+        psVectorStats(stats, rans, NULL, NULL, 0);
+
+        // Verify mean and stdev
+        is_float_tol_per(stats->sampleMean, GAUSSIAN_MEAN, ERROR_TOLERANCE, "Mean is within expected range");
+        is_float_tol_per(stats->sampleStdev, GAUSSIAN_STDEV, ERROR_TOLERANCE, "StDev is within expected range");
+
+        skip_end();
+
+        psFree(myRNG);
+        psFree(rans);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Ensure psRandomGaussian() returns 0 for NULL psRandom struct
+    {
+        psMemId id = psMemGetId();
+        ok(psRandomGaussian(NULL) == 0, "Ensure psRandomGaussian() returns 0 for NULL psRandom struct");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testRandomPoisson(void)
+    {
+        diag("testRandomPoisson()");
+        psMemId id = psMemGetId();
+        psVector *rans = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans->n = rans->nalloc;
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 2, "Skipping tests because psRandomAlloc() failed");
+
+        // Initialize vector with random data
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans->data.F64[i] = psRandomPoisson(myRNG, POISSON_MEAN);
+        }
+
+        // Perform vector stats on random data (mean, stdev)
+        psVectorStats(stats, rans, NULL, NULL, 0);
+        stats->options = PS_STAT_SAMPLE_STDEV;
+        psVectorStats(stats, rans, NULL, NULL, 0);
+        // Verify mean and stdev
+        is_float_tol_per(stats->sampleMean, POISSON_MEAN, ERROR_TOLERANCE, "Mean is within expected range");
+        is_float_tol_per(stats->sampleStdev, POISSON_STDEV, ERROR_TOLERANCE, "StDev is within expected range");
+
+        skip_end();
+
+        psFree(myRNG);
+        psFree(rans);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Ensure psRandomPoisson() returns 0 for NULL psRandom struct
+    {
+        psMemId id = psMemGetId();
+        ok(psRandomPoisson(NULL, POISSON_MEAN) == 0, "Ensure psRandomPoisson() returns 0 for NULL psRandom struct");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // testRandomResetUniform(void)
+    {
+        diag("testRandomUniform(): ensure the seed resets properly");
+        psMemId id = psMemGetId();
+        psVector *rans00 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans00->n = rans00->nalloc;
+        psVector *rans01 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans01->n = rans01->nalloc;
+        psVector *rans02 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans02->n = rans02->nalloc;
+
+        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAlloc() failed");
+
+
+        // Random reset
+        psRandomReset(myRNG, SEED);
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans00->data.F64[i] = psRandomUniform(myRNG);
+        }
+        psRandomReset(myRNG, SEED2);
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans01->data.F64[i] = psRandomUniform(myRNG);
+        }
+        psRandomReset(myRNG, SEED);
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans02->data.F64[i] = psRandomUniform(myRNG);
+        }
+
+        // Verify reset to original seed produces same results
+        psBool errorFlag = false;
+        for (psS32 i = 0 ; i < NUM_DATA ; i++)
+        {
+            if (rans00->data.F64[i] != rans02->data.F64[i]) {
+                if (VERBOSE) {
+                    psError(PS_ERR_UNKNOWN,true,"psRandomUniform did not produce the same results with the same seed");
+                }
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psRandomUniform() produced the same results with the same seed");
+        skip_end();
+        psFree(myRNG);
+        psFree(rans00);
+        psFree(rans01);
+        psFree(rans02);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // XXX: How to test the proper generation of error messages?
+    if (0) {
+        psRandom *myRNG1 = NULL;
+        myRNG1 = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+        //    psLogSetDestination("dest:stderr");
+        psLogSetDestination(0);
+        psRandomReset(myRNG1,0);
+        //    psLogSetDestination("dest:stderr");
+        psLogSetDestination(2);
+        psFree(myRNG1);
+
+        // Reset a NULL psRandom variable, should generate an error message
+        psRandomReset(NULL,SEED);
+    }
+
+    // testRandomResetGaussian(void)
+    {
+        // testRandomGaussian(): ensure the seed resets properly
+        psMemId id = psMemGetId();
+        psVector *rans00 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans00->n = rans00->nalloc;
+        psVector *rans01 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans01->n = rans01->nalloc;
+        psVector *rans02 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans02->n = rans02->nalloc;
+
+        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAlloc() failed");
+
+
+        // Initialize random data in vectors
+        psRandomReset(myRNG, SEED);
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans00->data.F64[i] = psRandomGaussian(myRNG);
+        }
+        psRandomReset(myRNG, SEED2);
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans01->data.F64[i] = psRandomGaussian(myRNG);
+        }
+        psRandomReset(myRNG, SEED);
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans02->data.F64[i] = psRandomGaussian(myRNG);
+        }
+
+        // Verify data from original seed produces same data after reset
+        psBool errorFlag = false;
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            if (rans00->data.F64[i] != rans02->data.F64[i]) {
+                if (VERBOSE) {
+                    psError(PS_ERR_UNKNOWN,true,"psRandomGaussian did not produce the same results with the same seed");
+                }
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psRandomGaussian() produced the same results with the same seed");
+        skip_end();
+        psFree(myRNG);
+        psFree(rans00);
+        psFree(rans01);
+        psFree(rans02);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testRandomResetPoisson(void)
+    {
+        diag("testRandomPoisson(): ensure the seed resets properly");
+        psMemId id = psMemGetId();
+        psVector *rans00 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans00->n = rans00->nalloc;
+        psVector *rans01 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans01->n = rans01->nalloc;
+        psVector *rans02 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+        rans02->n = rans02->nalloc;
+
+        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAlloc() failed");
+
+        // Initialize vectors with random data
+        psRandomReset(myRNG, SEED);
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans00->data.F64[i] = psRandomPoisson(myRNG, POISSON_MEAN);
+        }
+        psRandomReset(myRNG, SEED2);
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans01->data.F64[i] = psRandomPoisson(myRNG, POISSON_MEAN);
+        }
+        psRandomReset(myRNG, SEED);
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            rans02->data.F64[i] = psRandomPoisson(myRNG, POISSON_MEAN);
+        }
+
+        // Verify the original seed produces same data after reset
+        psBool errorFlag = false;
+        for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+            if (rans00->data.F64[i] != rans02->data.F64[i]) {
+                if (VERBOSE) {
+                    psError(PS_ERR_UNKNOWN,true,"psRandomPoisson did not produce the same results with the same seed");
+                }
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psRandomPoisson() produced the same results with the same seed");
+        skip_end();
+        psFree(myRNG);
+        psFree(rans00);
+        psFree(rans01);
+        psFree(rans02);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psSparse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psSparse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psSparse.c	(revision 22322)
@@ -0,0 +1,397 @@
+    #include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(40);
+
+
+    // test psSparseAlloc()
+    {
+        psMemId id = psMemGetId();
+        psSparse *matrix = psSparseAlloc(10, 20);
+        ok(matrix != NULL, "psSparse successfully allocated");
+        skip_start(matrix == NULL, 12, "skipping tests because psSparseAlloc() returned NULL");
+        ok(matrix->Aij != NULL, "psSparseAlloc() set ->Aij correctly");
+        ok(matrix->Aij->n == 0, "psSparseAlloc() set ->Aij->n correctly");
+        ok(matrix->Si != NULL, "psSparseAlloc() set ->Si correctly");
+        ok(matrix->Si->n == 0, "psSparseAlloc() set ->Si->n correctly");
+        ok(matrix->Sj != NULL, "psSparseAlloc() set ->Sj correctly");
+        ok(matrix->Sj->n == 0, "psSparseAlloc() set ->Sj->n correctly");
+        ok(matrix->Bfj != NULL, "psSparseAlloc() set ->Bfj correctly");
+        ok(matrix->Bfj->n == 10, "psSparseAlloc() set ->Bfj->n correctly");
+        ok(matrix->Qii != NULL, "psSparseAlloc() set ->Qii correctly");
+        ok(matrix->Qii->n == 10, "psSparseAlloc() set ->Qii->n correctly");
+        ok(matrix->Nelem == 0, "psSparseAlloc() set ->Nelem correctly");
+        ok(matrix->Nrows == 10, "psSparseAlloc() set ->Nrows correctly");
+        skip_end();
+        psFree(matrix);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test psSparseSolve for a simple normal example matrix
+    {
+        psMemId id = psMemGetId();
+        // the basic equation is Ax = b
+
+        // create a matrix A with diagonals of 1 and a small number of off diagonal elements.
+        // construct a vector x, construct the corresponding vector b by multiplication. solve
+        // Ax = b for x using psSparseSolve.  compare with the input values for x.
+
+        psSparse *matrix = psSparseAlloc(100, 100);
+        ok(matrix != NULL, "psSparse successfully allocated");
+        skip_start(matrix == NULL, 5, "Skipping tests because psSparseAlloc() failed");
+
+        for(int i = 0; i < 100; i++) {
+            psSparseMatrixElement(matrix, i, i, 1.0);
+            if (i + 1 < 100) {
+                psSparseMatrixElement(matrix, i + 1, i, 0.1);
+            }
+        }
+
+        // incoming matrix elements do not need to be in order; sort before
+        // applying sparse matrix
+        psSparseResort(matrix);
+
+        psVector *xRef = psVectorAlloc(100, PS_TYPE_F32);
+        for (int i = 0; i < 100; i++) {
+            xRef->data.F32[i] = 1.0;
+        }
+
+        psVector *bVec = psSparseMatrixTimesVector(NULL, matrix, xRef);
+
+        for (int i = 0; i < 100; i++) {
+            psSparseVectorElement(matrix, i, bVec->data.F32[i]);
+        }
+
+        psSparseConstraint constraint;
+        constraint.paramMin   = 0.0;
+        constraint.paramMax   = 1e8;
+        constraint.paramDelta = 1e8;
+
+        // solve for normalization terms (need include local sky?)
+        psVector *xFit = psSparseSolve(NULL, constraint, matrix, 4);
+
+        // measure stdev between xFit and xRes
+        float dS = 0;
+        float dS2 = 0;
+        for (int i = 0; i < 100; i++) {
+            float dX = xRef->data.F32[i] - xFit->data.F32[i];
+            // fprintf(stderr, "%5.3f %5.3f %5.3f %5.3f\n", bVec->data.F32[i], xRef->data.F32[i], xFit->data.F32[i], dX);
+            dS2 += PS_SQR(dX);
+            dS += dX;
+        }
+        dS /= 100.0;
+        dS2 = sqrt(dS2/100.0 - dS*dS);
+
+        is_float_tol(dS2, 0.0, 1e-4, "scatter: %.20f", dS2);
+
+        psFree(matrix);
+        psFree(xRef);
+        psFree(bVec);
+        psFree(xFit);
+
+        skip_end();
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test psSparseSolve for a simple non-normal example matrix
+    {
+        psMemId id = psMemGetId();
+        // the basic equation is Ax = b
+
+        // create a matrix A with diagonals of 1 and a small number of off diagonal elements.
+        // construct a vector x, construct the corresponding vector b by multiplication. solve
+        // Ax = b for x using psSparseSolve.  compare with the input values for x.
+
+        psSparse *matrix = psSparseAlloc(100, 100);
+        ok(matrix != NULL, "psSparse successfully allocated");
+        skip_start(matrix == NULL, 5, "Skipping tests because psSparseAlloc() failed");
+
+        for (int i = 0; i < 100; i++) {
+            psSparseMatrixElement(matrix, i, i, 5.0);
+            if (i + 1 < 100) {
+                psSparseMatrixElement(matrix, i + 1, i, 0.1);
+            }
+        }
+
+        // incoming matrix elements do not need to be in order; sort before
+        // applying sparse matrix
+        psSparseResort(matrix);
+
+        psVector *xRef = psVectorAlloc(100, PS_TYPE_F32);
+        for (int i = 0; i < 100; i++) {
+            xRef->data.F32[i] = 1.0;
+        }
+
+        psVector *bVec = psSparseMatrixTimesVector(NULL, matrix, xRef);
+
+        for (int i = 0; i < 100; i++) {
+            psSparseVectorElement(matrix, i, bVec->data.F32[i]);
+        }
+
+        psSparseConstraint constraint;
+        constraint.paramMin   = 0.0;
+        constraint.paramMax   = 1e8;
+        constraint.paramDelta = 1e8;
+
+        // solve for normalization terms(need include local sky?)
+        psVector *xFit = psSparseSolve(NULL, constraint, matrix, 4);
+
+        // measure stdev between xFit and xRes
+        float dS = 0;
+        float dS2 = 0;
+        for (int i = 0; i < 100; i++) {
+            float dX = xRef->data.F32[i] - xFit->data.F32[i];
+            // fprintf(stderr, "%5.3f %5.3f %5.3f %5.3f\n", bVec->data.F32[i], xRef->data.F32[i], xFit->data.F32[i], dX);
+            dS2 += PS_SQR(dX);
+            dS += dX;
+        }
+        dS /= 100.0;
+        dS2 = sqrt(dS2/100.0 - dS*dS);
+
+        is_float_tol(dS2, 0.0, 1e-4, "scatter: %.20f", dS2);
+
+        psFree(matrix);
+        psFree(xRef);
+        psFree(bVec);
+        psFree(xFit);
+
+        skip_end();
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    /* sample matrix equation:
+
+    1.0 0.1 0.1 | 1.0 |   |1.15|
+    0.1 1.0 0.2 | 1.0 | = |1.20|
+    0.1 0.2 0.5 | 0.5 |   |0.55|
+
+    lower product : 1.0*0.1 + 1.0*0.2 = 0.3
+    upper product : 0.1*0.5 = 0.05
+              : 0.2*0.5 = 0.10
+    */
+
+    // test psSparseBorderMultiply
+    {
+        psMemId id = psMemGetId();
+        // the basic equation(Ax = b) is:
+        // |S B'||x| = |f|
+        // |B Q ||y| = |g|
+
+        // construct a matrix S with diagonals of 1 and a small number of off diagonal elements.
+        // construct a border with finite values and a corresponding Q matrix
+        // construct a vector x, construct the corresponding vector b by multiplication. solve
+        // Ax = b for x using psSparseSolve.  compare with the input values for x.
+
+        int Nrows = 2;
+        int Nborder = 1;
+
+        // construct the sparse matrix
+        psSparse *matrix = psSparseAlloc(Nrows, Nrows);
+        ok(matrix != NULL, "psSparse successfully allocated");
+        skip_start(matrix == NULL, 5, "Skipping tests because psSparseAlloc() failed");
+
+        psSparseMatrixElement(matrix, 0, 0, 1.0);
+        psSparseMatrixElement(matrix, 1, 1, 1.0);
+        psSparseMatrixElement(matrix, 1, 0, 0.1);
+        psSparseResort(matrix);
+
+        // border region has a width of 1:
+        psSparseBorder *border = psSparseBorderAlloc(matrix, Nborder);
+
+        // construct the B component:
+        psSparseBorderElementB(border, 0, 0, 0.1);
+        psSparseBorderElementB(border, 1, 0, 0.2);
+
+        // construct the T component:
+        psSparseBorderElementT(border, 0, 0, 0.5);
+
+        // construct the X and Y vectors:
+        psVector *xRef = psVectorAlloc(Nrows, PS_TYPE_F32);
+        xRef->data.F32[0] = 1.0;
+        xRef->data.F32[1] = 1.0;
+        psVector *yRef = psVectorAlloc(Nborder, PS_TYPE_F32);
+        yRef->data.F32[0] = 0.5;
+
+        // construct the f and g vectors by multiplication:
+        psVector *fVec;
+
+        // test the support functions: LowerProduct
+        fVec = psSparseBorderLowerProduct(NULL, border, xRef);
+        is_float(fVec->n, 1.0, "f dimen: %d", fVec->n);
+        is_float(fVec->data.F32[0], 0.3, "f(0): %f", fVec->data.F32[0]);
+        psFree(fVec);
+
+        // test the support functions: Upper Product
+        fVec = psSparseBorderUpperProduct(NULL, border, yRef);
+        is_float(fVec->n, 2.0, "f dimen: %d", fVec->n);
+        is_float(fVec->data.F32[0], 0.05, "f(0): %f", fVec->data.F32[0]);
+        is_float(fVec->data.F32[1], 0.10, "f(0): %f", fVec->data.F32[1]);
+        psFree(fVec);
+
+        // test the support functions: Square Product
+        fVec = psSparseBorderSquareProduct(NULL, border, yRef);
+        is_float(fVec->n, 1.0, "f dimen: %d", fVec->n);
+        is_float(fVec->data.F32[0], 0.25, "f(0): %f", fVec->data.F32[0]);
+        psFree(fVec);
+
+        fVec = NULL;
+        psVector *gVec = NULL;
+        psSparseBorderMultiply(&fVec, &gVec, border, xRef, yRef);
+        is_float(fVec->data.F32[0], 1.15, "f(0): %f", fVec->data.F32[0]);
+        is_float(fVec->data.F32[1], 1.20, "f(1): %f", fVec->data.F32[1]);
+        is_float(gVec->data.F32[0], 0.55, "g(0): %f", gVec->data.F32[0]);
+
+        // supply the fVec and gVec data to the border
+        for (int i = 0; i < Nrows; i++) {
+            psSparseVectorElement(border->sparse, i, fVec->data.F32[i]);
+        }
+        for (int i = 0; i < Nborder; i++) {
+            psSparseBorderElementG(border, i, gVec->data.F32[i]);
+        }
+
+        // solve the border equation
+        psSparseConstraint constraint;
+        constraint.paramMin   = 0.0;
+        constraint.paramMax   = 1e8;
+        constraint.paramDelta = 1e8;
+
+        // solve for normalization terms (need include local sky?)
+        psVector *xFit = NULL;
+        psVector *yFit = NULL;
+        psSparseBorderSolve(&xFit, &yFit, constraint, border, 4);
+        is_float_tol(xFit->data.F32[0], 1.0, 1e-4, "f(0): %f", xFit->data.F32[0]);
+        is_float_tol(xFit->data.F32[1], 1.0, 1e-4, "f(1): %f", xFit->data.F32[1]);
+        is_float_tol(yFit->data.F32[0], 0.5, 1e-4, "g(0): %f", yFit->data.F32[0]);
+
+        psFree(xFit);
+        psFree(yFit);
+        psFree(fVec);
+        psFree(gVec);
+        psFree(xRef);
+        psFree(yRef);
+        psFree(border);
+        psFree(matrix);
+        skip_end();
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test psSparseBorderSolve for a simple example matrix
+    {
+        psMemId id = psMemGetId();
+        // the basic equation(Ax = b) is:
+        // |S B'||x| = |f|
+        // |B Q ||y| = |g|
+
+        // construct a matrix S with diagonals of 1 and a small number of off diagonal elements.
+        // construct a border with finite values and a corresponding Q matrix
+        // construct a vector x, construct the corresponding vector b by multiplication. solve
+        // Ax = b for x using psSparseSolve.  compare with the input values for x.
+
+        int Nrows = 10;
+        int Nborder = 1;
+
+        // construct the sparse matrix
+        psSparse *matrix = psSparseAlloc(Nrows, Nrows);
+        ok(matrix != NULL, "psSparse successfully allocated");
+        skip_start(matrix == NULL, 5, "Skipping tests because psSparseAlloc() failed");
+
+        for (int i = 0; i < Nrows; i++) {
+            psSparseMatrixElement(matrix, i, i, 1.0);
+            if (i + 1 < Nrows) {
+                psSparseMatrixElement(matrix, i + 1, i, 0.1);
+            }
+        }
+        psSparseResort(matrix);
+
+        // border region has a width of 1:
+        psSparseBorder *border = psSparseBorderAlloc(matrix, Nborder);
+
+        // construct the B component:
+        for (int i = 0; i < Nrows; i++) {
+            for (int j = 0; j < Nborder; j++) {
+                psSparseBorderElementB(border, i, j, 0.1);
+            }
+        }
+        is_float(border->Bij->data.F32[0][0], 0.1, "set matrix element %d,%d", 0, 0);
+
+        // construct the Q component:
+        for (int i = 0; i < Nborder; i++) {
+            for (int j = 0; j < Nborder; j++) {
+                psSparseBorderElementT(border, i, j, i+j+2);
+            }
+        }
+
+        // construct the X and Y vectors:
+        psVector *xRef = psVectorAlloc(Nrows, PS_TYPE_F32);
+        for (int i = 0; i < Nrows; i++) {
+            xRef->data.F32[i] = 1.0;
+        }
+        psVector *yRef = psVectorAlloc(Nborder, PS_TYPE_F32);
+        for (int i = 0; i < Nborder; i++) {
+            yRef->data.F32[i] = 1.0;
+        }
+
+        // construct the f and g vectors by multiplication:
+        psVector *fVec = NULL;
+        psVector *gVec = NULL;
+        psSparseBorderMultiply(&fVec, &gVec, border, xRef, yRef);
+
+        // supply the fVec and gVec data to the border
+        for (int i = 0; i < Nrows; i++) {
+            psSparseVectorElement(border->sparse, i, fVec->data.F32[i]);
+        }
+        for (int i = 0; i < Nborder; i++) {
+            psSparseBorderElementG(border, i, gVec->data.F32[i]);
+        }
+
+        // solve the border equation
+        psSparseConstraint constraint;
+        constraint.paramMin   = 0.0;
+        constraint.paramMax   = 1e8;
+        constraint.paramDelta = 1e8;
+
+        // solve for normalization terms(need include local sky?)
+        psVector *xFit = NULL;
+        psVector *yFit = NULL;
+        psSparseBorderSolve(&xFit, &yFit, constraint, border, 4);
+
+        // measure stdev between xFit and xRef
+        float dS = 0;
+        float dS2 = 0;
+        for (int i = 0; i < Nrows; i++) {
+            float dX = xRef->data.F32[i] - xFit->data.F32[i];
+            // fprintf(stderr, "%5.3f %5.3f %5.3f %5.3f\n", fVec->data.F32[i], xRef->data.F32[i], xFit->data.F32[i], dX);
+            dS2 += PS_SQR(dX);
+            dS += dX;
+        }
+        dS /= Nrows;
+        dS2 = sqrt(dS2/Nrows - dS*dS);
+        is_float_tol(dS2, 0.0, 1e-4, "scatter: %.20f", dS2);
+
+        // measure stdev between yFit and yRef
+        float dY = yRef->data.F32[0] - yFit->data.F32[0];
+        // fprintf(stderr, "%5.3f %5.3f %5.3f %5.3f\n", gVec->data.F32[0], yRef->data.F32[0], yFit->data.F32[0], dS);
+        is_float_tol(dY, 0.0, 2e-4, "scatter: %.20f", dY);
+
+        psFree(xRef);
+        psFree(yRef);
+        psFree(xFit);
+        psFree(yFit);
+        psFree(fVec);
+        psFree(gVec);
+        psFree(matrix);
+        psFree(border);
+
+        skip_end();
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psSpline1D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psSpline1D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psSpline1D.c	(revision 22322)
@@ -0,0 +1,336 @@
+/* @file  tst_psImageManip.c
+*
+*  @brief This file will contain tests for all of the public psLib functions
+*         that implement 1-D spline functionality:
+* psSpline1DAlloc()
+* psSpline1DEval()
+* psSpline1DEvalVector()
+* psVectorFitSpline1D()
+*
+*         This file is composed of the tests formerly in tst_psFunc02.c,
+*         tst_psFunc03.c, tst_psFunc04.c, tst_psFunc05.c, and tst_psFunc07.c.
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-05-02 04:20:06 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define ERROR_TOLERANCE_PERCENT    1.00
+#define VERBOSE 0
+/*********************************************************************************
+CheckErrorF32(psF32 actual, psF32 expect): this routine returns FALSE if the
+actual and expect arguments are with ERROR_TOLERANCE_PERCENT of each other,
+otherwise it returns TRUE.
+ ********************************************************************************/
+psBool CheckErrorF32(
+    psF32 actual,
+    psF32 expect)
+{
+    if ((fabs(actual - expect) / fabs(expect)) > ERROR_TOLERANCE_PERCENT) {
+        return(true);
+    } else {
+        return(false);
+    }
+}
+/*********************************************************************************
+CheckErrorF64(psF64 actual, psF64 expect): this routine returns FALSE if the
+actual and expect arguments are with ERROR_TOLERANCE_PERCENT of each other,
+otherwise it returns TRUE.
+ ********************************************************************************/
+psBool CheckErrorF64(
+    psF64 actual,
+    psF64 expect)
+{
+    if ((fabs(actual - expect) / fabs(expect)) > ERROR_TOLERANCE_PERCENT) {
+        return(true);
+    } else {
+        return(false);
+    }
+}
+
+psF32 myFunc00(psF32 x)
+{
+    return(x);
+}
+
+psF64 myFunc00_F64(psF64 x)
+{
+    return(x);
+}
+
+#define A 4.0
+#define B -3.0
+#define C 0.2
+#define D 0.1
+psF32 myFunc01(psF32 x)
+{
+    return(A + x * (B + x * (C + x * (D))));
+}
+psF64 myFunc01_F64(psF64 x)
+{
+    return(A + x * (B + x * (C + x * (D))));
+}
+
+typedef psF32 (*mappingFuncF32)(psF32 x);
+typedef psF64 (*mappingFuncF64)(psF64 x);
+
+bool genericF32Test(psS32 NumSplines, mappingFuncF32 func, bool xNull)
+{
+    // We test the psVectorFitSpline1D, psSpline1DEval() functions.  F32 version.
+    bool testStatus = true;
+    {
+        psMemId id = psMemGetId();
+        psVector *xF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+        psVector *yF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+        for (psS32 i=0;i<NumSplines+1;i++) {
+            xF32->data.F32[i] = (psF32) i;
+            yF32->data.F32[i] = (psF32) func((psF32) i);
+        }
+
+        psSpline1D *tmpSpline = NULL;
+        if (!xNull) {
+            tmpSpline = psVectorFitSpline1D(xF32, yF32);
+        } else {
+            tmpSpline = psVectorFitSpline1D(NULL, yF32);
+        }
+        if(tmpSpline == NULL) {
+            diag("psVectorFitSpline1D() returned NULL");
+            testStatus = false;
+        } else {
+            if (tmpSpline->n != NumSplines) {
+                diag("psVectorFitSpline1D() did not properly set the psSpline1D->n member");
+                testStatus = false;
+            }
+            if(tmpSpline->spline == NULL) {
+                diag("psVectorFitSpline1D() returned a NULL psSpline1D->spline member.");
+                testStatus = false;
+            }
+            for (psS32 i = 0 ; i < NumSplines ; i++) {
+                if (tmpSpline->spline[i] == NULL) {
+                    diag("psVectorFitSpline1D() returned a NULL psSpline1D->spline[%d] member.", i);
+                    testStatus = false;
+                }
+            }
+            if (tmpSpline->knots == NULL) {
+                diag("psVectorFitSpline1D() returned a NULL psSpline1D->knots member");
+                testStatus = false;
+            }
+            if (tmpSpline->p_psDeriv2 == NULL) {
+                diag("psVectorFitSpline1D()returned a NULL psSpline1D->p_psDeriv2 member");
+                testStatus = false;
+            }
+
+            // Test psSpline1DEval()
+            for (psS32 i=0;i<NumSplines;i++) {
+                psF32 x = 0.5 + (float) i;
+                psF32 y = psSpline1DEval(tmpSpline, x);
+                if (CheckErrorF32(y, func(x))) {
+                    testStatus = false;
+                    diag("TEST ERROR: f(%f) is %f, should be %f", x, y, myFunc00(x));
+                }
+            }
+
+            // Test psSpline1DEvalVector()
+            psVector *yF32Test = psSpline1DEvalVector(tmpSpline, xF32);
+            if (yF32Test == NULL) {
+                testStatus = false;
+                diag("TEST ERROR: psSpline1DEvalVector() returned NULL");
+            } else {
+                for (psS32 i=0;i<NumSplines;i++) {
+                    if (CheckErrorF32(yF32Test->data.F32[i], func(xF32->data.F32[i]))) {
+                        testStatus = false;
+                        diag("TEST ERROR: f(%f) is %f, should be %f", xF32->data.F32[i],
+                             yF32Test->data.F32[i], myFunc00(xF32->data.F32[i]));
+                    }
+                }
+                psFree(yF32Test);
+            }
+        }
+
+        psFree(tmpSpline);
+        psFree(xF32);
+        psFree(yF32);
+        if (psMemCheckLeaks (id, NULL, NULL, false)) {
+            diag("TEST ERROR: memory leaks");
+            testStatus = false;
+        }
+    }
+    return(testStatus);
+}
+
+
+// This is a duplicate cut-n-paste of the F32 function
+bool genericF64Test(psS32 NumSplines, mappingFuncF64 func, bool xNull)
+{
+    // We test the psVectorFitSpline1D, psSpline1DEval() functions.  F64 version.
+    bool testStatus = true;
+    {
+        psMemId id = psMemGetId();
+        psVector *xF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+        psVector *yF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+        for (psS32 i=0;i<NumSplines+1;i++) {
+            xF64->data.F64[i] = (psF64) i;
+            yF64->data.F64[i] = (psF64) func((psF64) i);
+        }
+
+        psSpline1D *tmpSpline = NULL;
+        if (!xNull) {
+            tmpSpline = psVectorFitSpline1D(xF64, yF64);
+        } else {
+            tmpSpline = psVectorFitSpline1D(NULL, yF64);
+        }
+        if(tmpSpline == NULL) {
+            diag("psVectorFitSpline1D() returned NULL");
+            testStatus = false;
+        } else {
+            if (tmpSpline->n != NumSplines) {
+                diag("psVectorFitSpline1D() did not properly set the psSpline1D->n member");
+                testStatus = false;
+            }
+            if(tmpSpline->spline == NULL) {
+                diag("psVectorFitSpline1D() returned a NULL psSpline1D->spline member.");
+                testStatus = false;
+            }
+            for (psS32 i = 0 ; i < NumSplines ; i++) {
+                if (tmpSpline->spline[i] == NULL) {
+                    diag("psVectorFitSpline1D() returned a NULL psSpline1D->spline[%d] member.", i);
+                    testStatus = false;
+                }
+            }
+            if (tmpSpline->knots == NULL) {
+                diag("psVectorFitSpline1D() returned a NULL psSpline1D->knots member");
+                testStatus = false;
+            }
+            if (tmpSpline->p_psDeriv2 == NULL) {
+                diag("psVectorFitSpline1D()returned a NULL psSpline1D->p_psDeriv2 member");
+                testStatus = false;
+            }
+
+            // Test psSpline1DEval()
+            for (psS32 i=0;i<NumSplines;i++) {
+                psF64 x = 0.5 + (float) i;
+                psF64 y = psSpline1DEval(tmpSpline, x);
+                if (CheckErrorF64(y, func(x))) {
+                    testStatus = false;
+                    diag("TEST ERROR psSpline1DEval(): f(%f) is %f, should be %f", x, y, myFunc00(x));
+                }
+            }
+
+            // Test psSpline1DEvalVector()
+            psVector *yF64Test = psSpline1DEvalVector(tmpSpline, xF64);
+            if (yF64Test == NULL) {
+                testStatus = false;
+                diag("TEST ERROR: psSpline1DEvalVector() returned NULL");
+            } else {
+                for (psS32 i=0;i<NumSplines;i++) {
+                    if (CheckErrorF64((psF64)yF64Test->data.F32[i], func(xF64->data.F64[i]))) {
+                        testStatus = false;
+                        diag("TEST ERROR psSpline1DEvalVector(): f(%f) is %f, should be %f", xF64->data.F64[i],
+                             yF64Test->data.F32[i], myFunc00(xF64->data.F64[i]));
+                    }
+                }
+                psFree(yF64Test);
+            }
+        }
+
+        psFree(tmpSpline);
+        psFree(xF64);
+        psFree(yF64);
+        if (psMemCheckLeaks (id, NULL, NULL, false)) {
+            diag("TEST ERROR: memory leaks");
+            testStatus = false;
+        }
+    }
+    return(testStatus);
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel( PS_LOG_INFO );
+    plan_tests(28);
+
+    // psSplineAllocTest()
+    {
+        psMemId id = psMemGetId();
+        psSpline1D *tmpSpline = psSpline1DAlloc();
+        ok(tmpSpline != NULL, "psSpline1DAlloc() returned non-NULL");
+        skip_start(tmpSpline == NULL, 4, "Skipping tests because psSpline1DAlloc() failed");
+        ok(tmpSpline->n == 0, "psSpline1DAlloc() properly set the psSpline1D->n member");
+        ok(tmpSpline->spline == NULL, "psSpline1DAlloc() properly set the psSpline1D->spline member");
+        ok(tmpSpline->knots == NULL, "psSpline1DAlloc() properly set the psSpline1D->knots member");
+        ok(tmpSpline->p_psDeriv2 == NULL, "psSpline1DAlloc() properly set the psSpline1D->p_psDeriv2 member");
+        psFree(tmpSpline);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // psSplineEvalTest_sub(): Call psVectorFitSpline1D with NULL arguments.
+    {
+        psMemId id = psMemGetId();
+        psSpline1D *tmpSpline = psVectorFitSpline1D(NULL, NULL);
+        ok(tmpSpline == NULL, "psVectorFitSpline1D() returns NULL with NULL arguments");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Verify psSpline1DEval() for NULL input
+    // Verify with spline->n==0, and spline->knots PS_TYPE_F64
+    {
+        psMemId id = psMemGetId();
+        float y = psSpline1DEval(NULL, 0.0);
+        ok(isnan(y), "psSpline1DEval() returned NAN will NULL input spline");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify psSpline1DEvalVector() for NULL input spline
+    {
+        psMemId id = psMemGetId();
+        psVector *x = psVectorAlloc(10, PS_TYPE_F32);
+        psVector *y = psSpline1DEvalVector(NULL, x);
+        ok(y == NULL, "psSpline1DEvalVector() returned NAN will NULL input spline");
+        psFree(x);
+        psFree(y);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify psSpline1DEvalVector() for NULL input vector
+    // XXX: Move this where we have a good spline.  It currently fails because
+    // were calling it with an un-fully-allocated one.
+    if (0) {
+        psMemId id = psMemGetId();
+        psSpline1D *tmpSpline = psSpline1DAlloc();
+        psVector *y = psSpline1DEvalVector(tmpSpline, NULL);
+        ok(y == NULL, "psSpline1DEvalVector() returned NAN will NULL input vector");
+        psFree(tmpSpline);
+        psFree(y);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    ok(genericF32Test(1, myFunc00, false), "Generic, simple mapping, F32 test. 1 spline");
+    ok(genericF32Test(95, myFunc00, false), "Generic, simple mapping, F32 test. 95 splines");
+    ok(genericF32Test(1, myFunc01, false), "Generic, simple mapping, F32 test. 1 spline");
+    ok(genericF32Test(95, myFunc01, false), "Generic, simple mapping, F32 test. 95 splines");
+    ok(genericF32Test(1, myFunc00, true), "Generic, simple mapping, F32 test. 1 spline");
+    ok(genericF32Test(95, myFunc00, true), "Generic, simple mapping, F32 test. 95 splines");
+    ok(genericF32Test(1, myFunc01, true), "Generic, simple mapping, F32 test. 1 spline");
+    ok(genericF32Test(95, myFunc01, true), "Generic, simple mapping, F32 test. 95 splines");
+    ok(genericF64Test(1, myFunc00_F64, false), "Generic, simple mapping, F64 test. 1 spline");
+    ok(genericF64Test(95, myFunc00_F64, false), "Generic, simple mapping, F64 test. 95 splines");
+    ok(genericF64Test(1, myFunc01_F64, false), "Generic, simple mapping, F64 test. 1 spline");
+    ok(genericF64Test(95, myFunc01_F64, false), "Generic, simple mapping, F64 test. 95 splines");
+    ok(genericF64Test(1, myFunc00_F64, true), "Generic, simple mapping, F64 test. 1 spline");
+    ok(genericF64Test(95, myFunc00_F64, true), "Generic, simple mapping, F64 test. 95 splines");
+    ok(genericF64Test(1, myFunc01_F64, true), "Generic, simple mapping, F64 test. 1 spline");
+    ok(genericF64Test(95, myFunc01_F64, true), "Generic, simple mapping, F64 test. 95 splines");
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats00.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats00.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats00.c	(revision 22322)
@@ -0,0 +1,292 @@
+/** @file  tst_psStats00.c
+ *
+ *  @brief Contains tests for psVectorStats with sample mean calculations
+ *
+ 
+ *  We extensively test the code with data type PS_TYPE_F32.  If these pass, we do a much
+ *  simpler test with data type PS_TYPE_U8, PS_TYPE_U16, PS_TYPE_F64.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-05-08 06:21:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define N 15
+
+static psF32 samplesF32[N] =
+    {
+        1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+        11.01, -12.02, 13.03, 14.04, -15.05
+    };
+static psS8  samplesS8[N]  =
+    {
+        1, 2, -3, 4, 5, -6, 7, 8, -9, 10, 11, -12, 13, 14, -15
+    };
+static psU16 samplesU16[N] =
+    {
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+    };
+static psF64 samplesF64[N] =
+    {
+        1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+        11.01, -12.02, 13.03, 14.04, -15.05
+    };
+static psF32 errorsF32[N] =
+    {
+        -0.10,  0.11, -0.12,  0.13, -0.14,  0.15, -0.16,  0.17,
+        -0.18,  0.19, -0.20,  0.21, -0.22,  0.23, -0.24
+    };
+
+static psF64 expectedMeanNoMaskF32              =  2.060667;
+static psF64 expectedMeanWithMaskF32            =  2.123846;
+static psF64 expectedMeanNoMaskS8               =  2.000000;
+static psF64 expectedMeanNoMaskU16              =  8.000000;
+static psF64 expectedMeanNoMaskF64              =  2.060667;
+static psF64 expectedMeanRangeNoMaskF32         =  0.137500;
+static psF64 expectedMeanRangeWithMaskF32       = -0.366667;
+static psF64 expectedWeightMeanNoMaskF32        =  2.020035;
+static psF64 expectedWeightMeanWithMaskF32      =  2.036018;
+static psF64 expectedWeightMeanNoMaskRangeF32   = -0.650684;
+static psF64 expectedWeightMeanWithMaskRangeF32 = -1.046423;
+
+#include <unistd.h>
+psS32 main(psS32 argc, char* argv[] )
+{
+    psMemId idGlobal = psMemGetId();
+    psLogSetFormat("HLNM");
+    plan_tests(60);
+
+    // Allocate data vectors for F32 tests
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psVector *myVector = psVectorAlloc(N, PS_TYPE_F32);
+    psVector *myErrors = psVectorAlloc(N, PS_TYPE_F32);
+    psVector *maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    // Set the appropriate values for the vector data.
+    for (long i = 0; i < N; i++) {
+        myVector->data.F32[i] =  samplesF32[i];
+        myErrors->data.F32[i] =  errorsF32[i];
+        if (i > 1) {
+            maskVector->data.U8[i] = 0;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    // Call psVectorStats() with no vector mask.
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded (F32: no mask vector, no error vector)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedMeanNoMaskF32, 1e-4,
+                     "The mean was %f, should be %f", myStats->sampleMean, expectedMeanNoMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke psVectorStats with no vector mask and error vector
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, myErrors, NULL, 0);
+        ok(result, "psVectorStats suceeded (F32: no mask vector, with error vector)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedWeightMeanNoMaskF32, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedWeightMeanNoMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke psVectorStats with no vector mask and data range
+    {
+        psMemId id = psMemGetId();
+        myStats->options = PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE;
+        myStats->min = -10.0;
+        myStats->max =   8.0;
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded (F32, no mask, no errors, with data range)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedMeanRangeNoMaskF32, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedMeanRangeNoMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Invoke psVectorStats with no vector mask, errors and data range
+    {
+        psMemId id = psMemGetId();
+        myStats->options = PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE;
+        bool result = psVectorStats(myStats, myVector, myErrors, NULL, 0);
+        ok(result, "psVectorStats suceeded (F32, no mask, with errors and data range)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedWeightMeanNoMaskRangeF32, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedWeightMeanNoMaskRangeF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call psVectorStats() with vector mask=1
+    {
+        psMemId id = psMemGetId();
+        myStats->options = PS_STAT_SAMPLE_MEAN;
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result, "psVectorStats suceeded (F32, with mask, no errors)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedMeanWithMaskF32, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedMeanWithMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke psVectorStats with vector mask and error vector
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, myErrors, maskVector, 1);
+        ok(result, "psVectorStats suceeded (F32, with mask and errors)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedWeightMeanWithMaskF32, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedWeightMeanWithMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke psVectorStats with vector mask and data range
+    {
+        psMemId id = psMemGetId();
+        myStats->options = PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE;
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result, "psVectorStats suceeded (F32, with mask and data range)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedMeanRangeWithMaskF32, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedMeanRangeWithMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke psVectorStats with vector mask, errors, and data range
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, myErrors, maskVector, 1);
+        ok(result, "psVectorStats suceeded (F32, withmask, errors, and data range)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedWeightMeanWithMaskRangeF32, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedWeightMeanWithMaskRangeF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call psVectorStats() with vector mask=2.
+    {
+        psMemId id = psMemGetId();
+        myStats->options = PS_STAT_SAMPLE_MEAN;
+        for (psS32 i = 0; i < N; i++) {
+            if (maskVector->data.U8[i] == 1) {
+                maskVector->data.U8[i] = 2;
+            }
+        }
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 2);
+        ok(result, "psVectorStats suceeded (F32, with mask = 2, no errors)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedMeanWithMaskF32, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedMeanWithMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call psVectorStats() with vector mask=3.
+    {
+        psMemId id = psMemGetId();
+        for (psS32 i = 0; i < N; i++)
+        {
+            if (maskVector->data.U8[i] == 2) {
+                maskVector->data.U8[i] = 3;
+            }
+        }
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 4);
+        ok(result, "psVectorStats suceeded (F32, with mask = 3, no errors)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedMeanNoMaskF32, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedMeanNoMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Mask all values and verify return is FALSE
+    {
+        psMemId id = psMemGetId();
+        for(psS32 i = 0; i < N; i++)
+        {
+            maskVector->data.U8[i] = 1;
+        }
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result == true, "psVectorStats() returned TRUE (All values masked)");
+        ok(isnan(myStats->sampleMean), "psVectorStats() returned NAN");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call psVectorStats() with NULL inputs.
+    {
+        psMemId id = psMemGetId();
+        ok(!psVectorStats(myStats, NULL, NULL, NULL, 0), "psVectorStats() returned false with NULL inputs");
+        ok(!psVectorStats(NULL, myVector, NULL, NULL, 0), "psVectorStats() returned false with NULL inputs");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+    psFree(myErrors);
+    psFree(maskVector);
+    ok(!psMemCheckLeaks (idGlobal, NULL, NULL, false), "no memory leaks");
+    ok(!psMemCheckLeaks (idGlobal, NULL, NULL, false), "no memory leaks");
+
+
+    // Test SampleMean: S8
+    {
+        psMemId id = psMemGetId();
+        psStats* myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psVector* myVector = psVectorAlloc(N, PS_TYPE_S8);
+        for (psS32 i = 0; i < N; i++)
+        {
+            myVector->data.S8[i] =  samplesS8[i];
+        }
+
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded (S8, no mask, no errors)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedMeanNoMaskS8, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedMeanNoMaskS8);
+        psFree(myStats);
+        psFree(myVector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test SampleMean: U16
+    {
+        psMemId id = psMemGetId();
+        psStats* myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psVector* myVector = psVectorAlloc(N, PS_TYPE_U16);
+        myVector->n = N;
+        for (psS32 i = 0; i < N; i++)
+        {
+            myVector->data.U16[i] =  samplesU16[i];
+        }
+
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded (U16, no mask, no errors)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedMeanNoMaskU16, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedMeanNoMaskU16);
+        psFree(myStats);
+        psFree(myVector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test SampleMean: F64
+    {
+        psMemId id = psMemGetId();
+        psStats* myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psVector* myVector = psVectorAlloc(N, PS_TYPE_F64);
+        myVector->n = N;
+        for (psS32 i = 0; i < N; i++)
+        {
+            myVector->data.F64[i] =  samplesF64[i];
+        }
+
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded (F64, no mask, no errors)");
+        ok(!isnan(myStats->sampleMean), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMean, expectedMeanNoMaskF64, 1e-4, "The mean was %f, should be %f", myStats->sampleMean, expectedMeanNoMaskF64);
+        psFree(myStats);
+        psFree(myVector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats01.c	(revision 22322)
@@ -0,0 +1,187 @@
+/** @file  tst_psStats01.c
+*
+*  @brief Contains tests for psVectorStats with max calculations.
+*
+*  We extensively test the code with data type PS_TYPE_F32.  If these pass, we
+*  do a much simpler test with data types PS_TYPE_U8, PS_TYPE_U16, PS_TYPE_F64.
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.4 $  $Name: not supported by cvs2svn $
+*  @date $Date: 2007-03-27 22:52:03 $
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define N 15
+
+static psF32 samplesF32[N] = { 1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+                               11.01, -12.02, 13.03, 14.04, -15.05 };
+static psS8  samplesS8[N]  = {1, 2, -3, 4, 5, -6, 7, 8, -9, 10, 11, -12, 13, 14, -15};
+static psU16 samplesU16[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+static psF64 samplesF64[N] = { 1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+                               11.01, -12.02, 13.03, 14.04, -15.05 };
+
+static psF64 expectedMaxNoMaskF32                = 14.04;
+static psF64 expectedMaxNoMaskS8                 = 14.00;
+static psF64 expectedMaxNoMaskU16                = 15.00;
+static psF64 expectedMaxNoMaskF64                = 14.04;
+
+static psF64 expectedMaxWithMaskF32              = 13.03;
+static psF64 expectedMaxRangeNoMaskF32           = 10.00;
+static psF64 expectedMaxRangeWithMaskF32         = 13.03;
+
+psS32 main(psS32 argc, char* argv[] )
+{
+    psMemId idGlobal = psMemGetId();
+    psLogSetFormat("HLNM");
+    plan_tests(32);
+
+    psStats* myStats = psStatsAlloc(PS_STAT_MAX);
+    psVector* myVector =  psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    psVector* maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.F32[i] = samplesF32[i];
+        if (i < 13) {
+            maskVector->data.U8[i] = 0;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    // Call psVectorStats() with no vector mask
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded");
+        ok(!isnan(myStats->max), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->max, expectedMaxNoMaskF32, 1e-4,
+                     "The max was %f, should be %f", myStats->max, expectedMaxNoMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call psVectorStats() with vector mask
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result, "psVectorStats suceeded");
+        ok(!isnan(myStats->max), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->max, expectedMaxWithMaskF32, 1e-4,
+                     "The max was %f, should be %f", myStats->max, expectedMaxWithMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke function with data range with no mask
+    {
+        psMemId id = psMemGetId();
+        myStats->options = PS_STAT_MAX | PS_STAT_USE_RANGE;
+        myStats->max = 10.2;
+        myStats->min = 0.0;
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded");
+        ok(!isnan(myStats->max), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->max, expectedMaxRangeNoMaskF32, 1e-4,
+                     "The max was %f, should be %f", myStats->max, expectedMaxRangeNoMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke function with data range and mask
+    {
+        psMemId id = psMemGetId();
+        myStats->max = 14.0;
+        myStats->min = 0.0;
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result, "psVectorStats suceeded");
+        ok(!isnan(myStats->max), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->max, expectedMaxRangeWithMaskF32, 1e-4,
+                     "The max was %f, should be %f", myStats->max, expectedMaxRangeWithMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke function with data range with no valid data
+    {
+        psMemId id = psMemGetId();
+        myStats->max = 100.00;
+        myStats->min = 90.00;
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(!result, "psVectorStats failed as expected");
+        ok(isnan(myStats->max), "psVectorStats() returned NAN");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+    psFree(maskVector);
+    ok(!psMemCheckLeaks (idGlobal, NULL, NULL, false), "no memory leaks");
+
+    // Test StatsMax: S8
+    {
+        psMemId id = psMemGetId();
+        psStats* myStats = psStatsAlloc(PS_STAT_MAX);
+        psVector* myVector = psVectorAlloc(N, PS_TYPE_S8);
+        myVector->n = N;
+        for (psS32 i = 0; i < N; i++)
+        {
+            myVector->data.S8[i] = samplesS8[i];
+        }
+
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded");
+        ok(!isnan(myStats->max), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->max, expectedMaxNoMaskS8, 1e-4,
+                     "The max was %f, should be %f", myStats->max, expectedMaxNoMaskS8);
+        psFree(myStats);
+        psFree(myVector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test StatsMax: U16
+    {
+        psMemId id = psMemGetId();
+        psStats* myStats = psStatsAlloc(PS_STAT_MAX);
+        psVector* myVector = psVectorAlloc(N, PS_TYPE_U16);
+        myVector->n = N;
+        for (psS32 i = 0; i < N; i++)
+        {
+            myVector->data.U16[i] = samplesU16[i];
+        }
+
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded");
+        ok(!isnan(myStats->max), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->max, expectedMaxNoMaskU16, 1e-4,
+                     "The max was %f, should be %f", myStats->max, expectedMaxNoMaskU16);
+        psFree(myStats);
+        psFree(myVector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test StatsMax: F64
+    {
+        psMemId id = psMemGetId();
+        psStats* myStats = psStatsAlloc(PS_STAT_MAX);
+        psVector* myVector = psVectorAlloc(N, PS_TYPE_F64);
+        myVector->n = N;
+        for (psS32 i = 0; i < N; i++)
+        {
+            myVector->data.F64[i] = samplesF64[i];
+        }
+
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded");
+        ok(!isnan(myStats->max), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->max, expectedMaxNoMaskF64, 1e-4,
+                     "The max was %f, should be %f", myStats->max, expectedMaxNoMaskF64);
+        psFree(myStats);
+        psFree(myVector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats02.c	(revision 22322)
@@ -0,0 +1,193 @@
+/** @file  tst_psStats02.c
+*
+*  @brief Contains tests for psVectorStats with min calculations
+*
+*  We extensively test the code with data type PS_TYPE_F32.  If these pass, we
+*  do a much simpler test with data types PS_TYPE_U8, PS_TYPE_U16, PS_TYPE_F64.
+*
+*  If the psStats,c code every changes such that vectors of different type
+*  are handled by different routines, then these tests must be extended.
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.4 $  $Name: not supported by cvs2svn $
+*  @date $Date: 2007-03-27 22:52:03 $
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define N 15
+
+static psF32 samplesF32[N] = { 1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+                               11.01, -12.02, 13.03, 14.04, -15.05 };
+static psS8  samplesS8[N]  = {1, 2, -3, 4, 5, -6, 7, 8, -9, 10, 11, -12, 13, 14, -15};
+static psU16 samplesU16[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+static psF64 samplesF64[N] = { 1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+                               11.01, -12.02, 13.03, 14.04, -15.05 };
+
+static psF64 expectedMinNoMaskF32                = -15.05;
+static psF64 expectedMinNoMaskS8                 = -15.00;
+static psF64 expectedMinNoMaskU16                = 1.00;
+static psF64 expectedMinNoMaskF64                = -15.05;
+
+static psF64 expectedMinWithMaskF32              = -12.02;
+static psF64 expectedMinRangeNoMaskF32           =   1.10;
+static psF64 expectedMinRangeWithMaskF32         = -12.02;
+
+psS32 main(psS32 argc, char* argv[] )
+{
+    psMemId idGlobal = psMemGetId();
+    psLogSetFormat("HLNM");
+    plan_tests(32);
+
+    psStats* myStats = psStatsAlloc(PS_STAT_MIN);
+    psVector* myVector = psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    psVector* maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.F32[i] = samplesF32[i];
+        if (i < 13) {
+            maskVector->data.U8[i] = 0;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded");
+        ok(!isnan(myStats->min), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->min, expectedMinNoMaskF32, 1e-4,
+                     "The min was %f, should be %f", myStats->min, expectedMinNoMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with vector mask.                       */
+    /*************************************************************************/
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result, "psVectorStats suceeded");
+        ok(!isnan(myStats->min), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->min, expectedMinWithMaskF32, 1e-4,
+                     "The min was %f, should be %f", myStats->min, expectedMinWithMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke function with data range with no mask
+    {
+        psMemId id = psMemGetId();
+        myStats->options = PS_STAT_MIN | PS_STAT_USE_RANGE;
+        myStats->max = 10.2;
+        myStats->min = 0.0;
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats suceeded");
+        ok(!isnan(myStats->min), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->min, expectedMinRangeNoMaskF32, 1e-4,
+                     "The min was %f, should be %f", myStats->min, expectedMinRangeNoMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke function with data range and mask
+    {
+        psMemId id = psMemGetId();
+        myStats->max = 10.1;
+        myStats->min = -15.00;
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->min), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->min, expectedMinRangeWithMaskF32, 1e-4,
+                     "The min was %f, should be %f", myStats->min, expectedMinRangeWithMaskF32);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Invoke function with data range with no valid data
+    {
+        psMemId id = psMemGetId();
+        myStats->max = 100.00;
+        myStats->min = 90.00;
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(!result, "psVectorStats failed as expected");
+        ok(isnan(myStats->min), "psVectorStats() returned NAN");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    psFree(myStats);
+    psFree(myVector);
+    psFree(maskVector);
+    ok(!psMemCheckLeaks (idGlobal, NULL, NULL, false), "no memory leaks");
+
+
+    // Test StatsMin: S8
+    {
+        psMemId id = psMemGetId();
+        psStats* myStats = psStatsAlloc(PS_STAT_MIN);
+        psVector* myVector = psVectorAlloc(N, PS_TYPE_S8);
+        myVector->n = N;
+        for (psS32 i = 0; i < N; i++)
+        {
+            myVector->data.S8[i] = samplesS8[i];
+        }
+
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->min), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->min, expectedMinNoMaskS8, 1e-4,
+                     "The min was %f, should be %f", myStats->min, expectedMinNoMaskS8);
+        psFree(myStats);
+        psFree(myVector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test StatsMin: U16
+    {
+        psMemId id = psMemGetId();
+        psStats* myStats = psStatsAlloc(PS_STAT_MAX);
+        psVector* myVector = psVectorAlloc(N, PS_TYPE_U16);
+        myVector->n = N;
+        for (psS32 i = 0; i < N; i++)
+        {
+            myVector->data.U16[i] = samplesU16[i];
+        }
+
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->min), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->min, expectedMinNoMaskU16, 1e-4,
+                     "The min was %f, should be %f", myStats->min, expectedMinNoMaskU16);
+        psFree(myStats);
+        psFree(myVector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test StatsMin: F64
+    {
+        psMemId id = psMemGetId();
+        psStats* myStats = psStatsAlloc(PS_STAT_MIN);
+        psVector* myVector = psVectorAlloc(N, PS_TYPE_F64);
+        myVector->n = N;
+        for (psS32 i = 0; i < N; i++)
+        {
+            myVector->data.F64[i] = samplesF64[i];
+        }
+
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->min), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->min, expectedMinNoMaskF64, 1e-4,
+                     "The min was %f, should be %f", myStats->min, expectedMinNoMaskF64);
+        psFree(myStats);
+        psFree(myVector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats03.c	(revision 22322)
@@ -0,0 +1,66 @@
+/*****************************************************************************
+    This routine must ensure that PS_STAT_SAMPLE_MEDIAN is correctly computed
+    by the procedure psVectorStats().
+ 
+    XXX: Must add tests for various data types, other than psF32.  Copy code
+    from tst_psStats00.c-tst_psStats02.c.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define N1 1029   // This should be an odd number.
+#define N ((4 * N1) + 1)
+float realMedianWithMask = (float) (N-3)/4;
+float realMedianNoMask = (float) (N-1)/2;
+
+psS32 main()
+{
+    psMemId idGlobal = psMemGetId();
+    psLogSetFormat("HLNM");
+    plan_tests(9);
+
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    psVector *myVector = psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    psVector *maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+    // Set the appropriate values for the vector data.
+    for (int i=0;i<N;i++) {
+        myVector->data.F32[i] = (float) i;
+        if (i < (N/2)) {
+            maskVector->data.U8[i] = 0;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    // Call psVectorStats() with no vector mask
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->sampleMedian), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMedian, realMedianNoMask, 1e-4,
+                     "The sample median was %f, should be %f", myStats->sampleMedian, realMedianNoMask);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call psVectorStats() with vector mask
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->sampleMedian), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleMedian, realMedianWithMask, 1e-4,
+                     "The sample median was %f, should be %f", myStats->sampleMedian, realMedianWithMask);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+    psFree(maskVector);
+    ok(!psMemCheckLeaks (idGlobal, NULL, NULL, false), "no memory leaks");
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats05.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats05.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats05.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*****************************************************************************
+    This routine must ensure that the psStats structure is correctly
+    allocated and deallocated by the procedure psStatsAlloc().
+ 
+    XXX: This should be test 00.  It should be merged into other tests
+         since it's so trivial.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define MISC_FLOAT_NUMBER 342.0
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    plan_tests(1);
+
+    {
+        psMemId id = psMemGetId();
+        psStats *myStats =psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        myStats->sampleMean = MISC_FLOAT_NUMBER;
+        myStats->sampleMedian = MISC_FLOAT_NUMBER;
+        myStats->sampleStdev = MISC_FLOAT_NUMBER;
+        myStats->sampleUQ = MISC_FLOAT_NUMBER;
+        myStats->sampleLQ = MISC_FLOAT_NUMBER;
+        myStats->robustMedian = MISC_FLOAT_NUMBER;
+        myStats->robustStdev = MISC_FLOAT_NUMBER;
+        myStats->robustUQ = MISC_FLOAT_NUMBER;
+        myStats->robustLQ = MISC_FLOAT_NUMBER;
+        myStats->robustN50 = MISC_FLOAT_NUMBER;
+        myStats->fittedMean = MISC_FLOAT_NUMBER;
+        myStats->fittedStdev = MISC_FLOAT_NUMBER;
+        myStats->fittedNfit = MISC_FLOAT_NUMBER;
+        myStats->clippedMean = MISC_FLOAT_NUMBER;
+        myStats->clippedStdev = MISC_FLOAT_NUMBER;
+        myStats->clippedNvalues = MISC_FLOAT_NUMBER;
+        myStats->clipSigma = MISC_FLOAT_NUMBER;
+        myStats->clipIter = MISC_FLOAT_NUMBER;
+        myStats->min = MISC_FLOAT_NUMBER;
+        myStats->max = MISC_FLOAT_NUMBER;
+        myStats->binsize = MISC_FLOAT_NUMBER;
+        myStats->nSubsample = MISC_FLOAT_NUMBER;
+        myStats->options = 0x0;
+        psFree(myStats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats06.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats06.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats06.c	(revision 22322)
@@ -0,0 +1,67 @@
+/*****************************************************************************
+    This routine must ensure that PS_STAT_SAMPLE_STDEV is correctly computed
+    by the procedure psArrayStats().
+ 
+    XXX: Must add tests for various data types, other than psF32.  Copy code
+    from tst_psStats00.c-tst_psStats02.c.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+//#include "float.h"
+//#include <math.h>
+#define N 15
+float realStdevNoMask = 4.472136;
+float realStdevWithMask = 2.160247;
+
+psS32 main()
+{
+    psMemId idGlobal = psMemGetId();
+    psLogSetFormat("HLNM");
+    plan_tests(9);
+
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_STDEV);
+    psVector *myVector = psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    psVector *maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+    psS32 count           = 0;
+    for (int i=0;i<N;i++) {
+        myVector->data.F32[i] = (float) i;
+        if (i < (N/2)) {
+            maskVector->data.U8[i] = 0;
+            count++;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    // Call psVectorStats() with no vector mask
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->sampleStdev), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleStdev, realStdevNoMask, 1e-4,
+                     "The mean was %f, should be %f", myStats->sampleStdev, realStdevNoMask);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call psVectorStats() with vector mask
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->sampleStdev), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleStdev, realStdevWithMask, 1e-4,
+                     "The mean was %f, should be %f", myStats->sampleStdev, realStdevWithMask);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+    psFree(maskVector);
+    ok(!psMemCheckLeaks (idGlobal, NULL, NULL, false), "no memory leaks");
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats07.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats07.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats07.c	(revision 22322)
@@ -0,0 +1,514 @@
+/*****************************************************************************
+   This routine must ensure that PS_STAT_ROBUST_QUARTILE is correctly computed
+   by the procedure psArrayStats().
+ 
+   XXX: Must add tests for various data types, other than psF32.  Copy code
+   from tst_psStats00.c-tst_psStats02.c.
+
+   XXX: This basically works.  Must add bad paramater tests, and test other
+   data types, different vector sizes, etc.
+*****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_DATA 1000
+#define MEAN 32.0
+#define STDEV 2.0
+#define PERCENT_OUTLIERS 1
+#define OUTLIER_MAGNITUDE (NUM_DATA * 10)
+#define ERROR_TOLERANCE .10
+#define ERRORS 1000.0
+#define SEED 1995
+#define VERBOSE 0
+#define EXTRA_VERBOSE 0
+
+#define TST_IN_NULL             0x00000001
+#define TST_IN_F32              0x00000002
+#define TST_IN_F64              0x00000004
+#define TST_IN_S8               0x00000008
+#define TST_IN_U16              0x00000010
+#define TST_IN_S32              0x00000020
+#define TST_ERRORS_NULL         0x00000040
+#define TST_ERRORS_F32          0x00000080
+#define TST_ERRORS_F64          0x00000100
+#define TST_ERRORS_S8           0x00000200
+#define TST_ERRORS_U16          0x00000400
+#define TST_ERRORS_S32          0x00000800
+#define TST_MASK_NULL           0x00001000
+#define TST_MASK_U8             0x00002000
+#define TST_MASK_S32            0x00004000
+
+/*****************************************************************************
+    p_psGaussianDev()
+ This private routine (formerly a psLib API routine) creates a psVector of the
+ specified size and type F32 and fills it with a random Gaussian distribution
+ of numbers with the specified mean and sigma.
+ 
+XXX: It's possible to have a different seed everytime.  However, for now,
+for testability, we use a common seed.
+ *****************************************************************************/
+#define PS_XXX_GAUSSIAN_SEED 1995
+psVector* p_psGaussianDev(psF32 mean,
+                          psF32 sigma,
+                          unsigned int Npts)
+{
+    PS_ASSERT_INT_NONNEGATIVE(Npts, NULL);
+
+    //    psRandom *r = psRandomAlloc(PS_RANDOM_TAUS, p_psRandomGetSystemSeed());
+    psRandom *r = psRandomAlloc(PS_RANDOM_TAUS, PS_XXX_GAUSSIAN_SEED);
+    psVector* gauss = psVectorAlloc(Npts, PS_TYPE_F32);
+    for (unsigned int i = 0; i < Npts; i++) {
+        gauss->data.F32[i] = mean + p_psRandomGaussian(r, sigma);
+        gauss->n++;
+    }
+    psFree(r);
+
+    return(gauss);
+}
+
+psBool genericRobustStatsTest(
+    unsigned int flags,
+    psS32 numData,
+    psU32 maskValue,
+    psBool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    psBool testStatus = true;
+    psS32 memLeaks = 0;
+    psVector *in = NULL;
+    psVector *errors = NULL;
+    psVector *mask = NULL;
+    srand(SEED);
+
+    if (expectedRC == true) {
+        if (VERBOSE)
+            printf("This test should not generate any errors.\n");
+    }
+    if (expectedRC == false) {
+        if (VERBOSE)
+            printf("This test should generate an error message, and return NULL.\n");
+    }
+    psVector *gaussVector = p_psGaussianDev(MEAN, STDEV, numData);
+
+
+    if (flags & TST_IN_NULL) {
+        if (VERBOSE)
+            printf("        using a NULL in vector\n");
+    }
+
+    if (flags & TST_IN_F32) {
+        if (VERBOSE)
+            printf("        using a psF32 in vector\n");
+        in = psVectorAlloc(numData, PS_TYPE_F32);
+        for (psS32 i=0;i<numData;i++) {
+            in->data.F32[i] = gaussVector->data.F32[i];
+//            in->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%.1f)\n", i, in->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TST_IN_F64) {
+        if (VERBOSE)
+            printf("        using a psF64 in vector\n");
+        in = psVectorAlloc(numData, PS_TYPE_F64);
+        for (psS32 i=0;i<numData;i++) {
+            in->data.F64[i] = (psF64) gaussVector->data.F32[i];
+//            in->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%.1f)\n", i, in->data.F64[i]);
+            }
+        }
+    }
+
+    if (flags & TST_IN_S8) {
+        if (VERBOSE)
+            printf("        using a psS8 in vector\n");
+        in = psVectorAlloc(numData, PS_TYPE_S8);
+        for (psS32 i=0;i<numData;i++) {
+            in->data.S8[i] = (psS8) gaussVector->data.F32[i];
+//            in->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.S8[i]);
+            }
+        }
+    }
+
+    if (flags & TST_IN_U16) {
+        if (VERBOSE)
+            printf("        using a psU16 in vector\n");
+        in = psVectorAlloc(numData, PS_TYPE_U16);
+        for (psS32 i=0;i<numData;i++) {
+            in->data.U16[i] = (psU16) gaussVector->data.F32[i];
+//            in->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.U16[i]);
+            }
+        }
+    }
+
+    if (flags & TST_IN_S32) {
+        if (VERBOSE)
+            printf("        using a psS32 in vector\n");
+        in = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            in->data.S32[i] = (psS32) gaussVector->data.F32[i];
+//            in->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.S32[i]);
+            }
+        }
+    }
+    psFree(gaussVector);
+
+    //    in->n = in->nalloc;
+    if (flags & TST_ERRORS_NULL) {
+        if (VERBOSE)
+            printf("        using a NULL errors vector\n");
+    }
+
+    if (flags & TST_ERRORS_F32) {
+        if (VERBOSE)
+            printf("        using a psF32 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_F32);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.F32[i] = ERRORS;
+//            errors->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original errors data %d: (%.1f)\n", i, errors->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TST_ERRORS_F64) {
+        if (VERBOSE)
+            printf("        using a psF64 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_F64);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.F64[i] = ERRORS;
+//            errors->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original errors data %d: (%.1f)\n", i, errors->data.F64[i]);
+            }
+        }
+    }
+
+    if (flags & TST_ERRORS_S8) {
+        if (VERBOSE)
+            printf("        using a psS8 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_S8);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.S8[i] = (psS8) ERRORS;
+//            errors->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original errors data %d: (%d)\n", i, errors->data.S8[i]);
+            }
+        }
+    }
+
+    if (flags & TST_ERRORS_U16) {
+        if (VERBOSE)
+            printf("        using a psU16 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_U16);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.U16[i] = (psU16) ERRORS;
+//            errors->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original errors data %d: (%d)\n", i, errors->data.U16[i]);
+            }
+        }
+    }
+
+    if (flags & TST_ERRORS_S32) {
+        if (VERBOSE)
+            printf("        using a psS32 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.S32[i] = (psS32) ERRORS;
+//            errors->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original errors data %d: (%d)\n", i, errors->data.S32[i]);
+            }
+        }
+    }
+
+
+    if (flags & TST_MASK_NULL) {
+        if (VERBOSE)
+            printf("        using a NULL mask vector\n");
+    }
+
+    if (flags & TST_MASK_U8) {
+        if (VERBOSE)
+            printf("        using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = (psU8) 0;
+//            mask->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original mask data %d: (%d)\n", i, mask->data.U8[i]);
+            }
+        }
+    }
+
+    if (flags & TST_MASK_S32) {
+        if (VERBOSE)
+            printf("        using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = (psS32) 0;
+//            mask->n++;
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original mask data %d: (%d)\n", i, mask->data.S32[i]);
+            }
+        }
+    }
+
+
+    //
+    // We calculate the sample mean and stdev without and outliers in the data.
+    // We will use this later in determining if the clipped stats are correct.
+    //
+    psF32 sampleMean=0.0;
+    psF32 sampleStdev=0.0;
+    psF32 sampleMedian=0.0;
+    psF32 sampleLQ=0.0;
+    psF32 sampleUQ=0.0;
+    if (expectedRC == true) {
+        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV | PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_QUARTILE);
+        bool rc = psVectorStats(myStats, in, NULL, NULL, maskValue);
+        if (rc == false) {
+            diag("TEST ERROR: the psVectorStats() function returned NULL.\n");
+            testStatus = false;
+        } else {
+            sampleMean = myStats->sampleMean;
+            sampleStdev = myStats->sampleStdev;
+            sampleMedian = myStats->sampleMedian;
+            sampleLQ = myStats->sampleLQ;
+            sampleUQ = myStats->sampleUQ;
+        }
+        psFree(myStats);
+    }
+
+    //
+    // We add a few outliers to the input data.
+    //
+    if (flags & TST_IN_F32) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.F32[i] = (psF32) OUTLIER_MAGNITUDE;
+            }
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%.1f)\n", i, in->data.F32[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_F64) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.F64[i] = (psF64) OUTLIER_MAGNITUDE;
+            }
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%.1f)\n", i, in->data.F64[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_S8) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.S8[i] = (psS8) OUTLIER_MAGNITUDE;
+            }
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.S8[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_U16) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.U16[i] = (psU16) OUTLIER_MAGNITUDE;
+            }
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.U16[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_S32) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.S32[i] = (psS32) OUTLIER_MAGNITUDE;
+            }
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.S32[i]);
+            }
+        }
+    }
+
+    //
+    // We call psVectorStats() and calculate the clipped stats.
+    //
+    psStats *myStats = psStatsAlloc(
+                           PS_STAT_ROBUST_MEDIAN |
+                           PS_STAT_ROBUST_STDEV |
+                           PS_STAT_ROBUST_QUARTILE |
+                           PS_STAT_FITTED_MEAN |
+                           PS_STAT_FITTED_STDEV);
+    bool rc = psVectorStats(myStats, in, errors, mask, maskValue);
+
+    if (rc == false) {
+        if (expectedRC == true) {
+            diag("TEST ERROR: the psVectorStats() function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            diag("TEST ERROR: the psVectorStats() function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        //
+        // Fitted Mean
+        //
+        if (fabs(myStats->fittedMean - sampleMean) > (ERROR_TOLERANCE * sampleMean)) {
+            diag("TEST ERROR: the fitted mean was %.2f, should have been %.2f\n", myStats->fittedMean, sampleMean);
+            testStatus = false;
+        } else if (EXTRA_VERBOSE) {
+            printf("GOOD: the fitted mean was %.2f, should have been %.2f\n", myStats->fittedMean, sampleMean);
+        }
+
+        //
+        // Fitted Stdev
+        //
+        if (fabs(myStats->fittedStdev - sampleStdev) > (ERROR_TOLERANCE * sampleStdev)) {
+            diag("TEST ERROR: the fitted stdev was %.2f, should have been %.2f\n", myStats->fittedStdev, sampleStdev);
+            testStatus = false;
+        } else if (EXTRA_VERBOSE) {
+            printf("GOOD: the fitted stdev was %.2f, should have been %.2f\n", myStats->fittedStdev, sampleStdev);
+        }
+
+        //
+        // Robust Stdev
+        //
+        if (fabs(myStats->robustStdev - sampleStdev) > (ERROR_TOLERANCE * sampleStdev)) {
+            diag("TEST ERROR: the robust stdev was %.2f, should have been %.2f\n", myStats->robustStdev, sampleStdev);
+            testStatus = false;
+        } else if (EXTRA_VERBOSE) {
+            printf("GOOD: the robust stdev was %.2f, should have been %.2f\n", myStats->robustStdev, sampleStdev);
+        }
+
+        //
+        // Robust Median
+        //
+        if (fabs(myStats->robustMedian - sampleMedian) > (ERROR_TOLERANCE * sampleMedian)) {
+            diag("TEST ERROR: the robust median was %.2f, should have been %.2f\n", myStats->robustMedian, sampleMedian);
+            testStatus = false;
+        } else if (EXTRA_VERBOSE) {
+            printf("GOOD: the robust median was %.2f, should have been %.2f\n", myStats->robustMedian, sampleMedian);
+        }
+
+        //
+        // Robust LQ
+        //
+        if (fabs(myStats->robustLQ - sampleLQ) > (ERROR_TOLERANCE * sampleLQ)) {
+            diag("TEST ERROR: the robust LQ was %.2f, should have been %.2f\n", myStats->robustLQ, sampleLQ);
+            testStatus = false;
+        } else if (EXTRA_VERBOSE) {
+            printf("GOOD: the robust LQ was %.2f, should have been %.2f\n", myStats->robustLQ, sampleLQ);
+        }
+
+        //
+        // Robust UQ
+        //
+        if (fabs(myStats->robustUQ - sampleUQ) > (ERROR_TOLERANCE * sampleUQ)) {
+            diag("TEST ERROR: the robust UQ was %.2f, should have been %.2f\n", myStats->robustUQ, sampleUQ);
+            testStatus = false;
+        } else if (EXTRA_VERBOSE) {
+            printf("GOOD: the robust UQ was %.2f, should have been %.2f\n", myStats->robustUQ, sampleUQ);
+        }
+    }
+
+    psFree(myStats);
+    psFree(in);
+    psFree(errors);
+    psFree(mask);
+    psMemCheckCorruption(stderr, false);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    return(testStatus);
+}
+
+
+psS32 main()
+{
+    psMemId id = psMemGetId();
+    psLogSetFormat("HLNM");
+    plan_tests(4);
+
+    ok(genericRobustStatsTest(TST_IN_F32 | TST_ERRORS_NULL | TST_MASK_NULL,
+       NUM_DATA, 1, true), "RobustStatsTest() F32 data, NULL errors, NULL mask");
+    ok(genericRobustStatsTest(TST_IN_F32 | TST_ERRORS_NULL | TST_MASK_U8, 
+       NUM_DATA, 1, true), "RobustStatsTest() F32 data, non-NULL errors and mask vector");
+    ok(genericRobustStatsTest(TST_IN_F32 | TST_ERRORS_F32 | TST_MASK_U8,
+        NUM_DATA, 1, true), "RobustStatsTest() F32 data, non-NULL errors and mask vector");
+
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats08.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats08.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats08.c	(revision 22322)
@@ -0,0 +1,92 @@
+/*****************************************************************************
+    This routine must ensure that PS_STAT_SAMPLE_QUARTILE is correctly computed
+    by the procedure psArrayStats().
+ 
+    XXX: Must add tests for various data types, other than psF32.  Copy code
+    from tst_psStats00.c-tst_psStats02.c.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define N1 25
+#define N (8 * N1) // Don't change this (N must be a multiple of 8)
+float realLQNoMask   = N/4.0;
+float realUQNoMask   = 3.0 * (N/4.0);
+float realLQWithMask   = N/8.0;
+float realUQWithMask   = 3.0 * (N/8.0);
+
+psS32 main()
+{
+    psMemId idGlobal = psMemGetId();
+    psLogSetFormat("HLNM");
+    plan_tests(18);
+
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_QUARTILE);
+    psVector *myVector = psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    psVector *maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+    for (int i=0;i<N;i++) {
+        myVector->data.F32[i] = (float) i;
+        if (i < (N/2)) {
+            maskVector->data.U8[i] = 0;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+
+    // Call psVectorStats() with no vector mask; test sampleLQ
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->sampleLQ), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleLQ, realLQNoMask, 1e-4,
+                     "The sampleLQ was %f, should be %f", myStats->sampleLQ, realLQNoMask);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call psVectorStats() with no vector mask; test sampleUQ
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, NULL, 0);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->sampleUQ), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleUQ, realUQNoMask, 1e-4,
+                     "The sampleUQ was %f, should be %f", myStats->sampleUQ, realUQNoMask);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call psVectorStats() with vector mask; test sampleLQ
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->sampleLQ), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleLQ, realLQWithMask, 1e-4,
+                     "The sampleLQ was %f, should be %f", myStats->sampleLQ,
+                     realLQWithMask);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call psVectorStats() with vector mask; test sampleUQ
+    {
+        psMemId id = psMemGetId();
+        bool result = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+        ok(result, "psVectorStats succeeded");
+        ok(!isnan(myStats->sampleUQ), "psVectorStats() returned non-NAN");
+        is_float_tol(myStats->sampleUQ, realUQWithMask, 1e-4,
+                     "The sampleUQ was %f, should be %f", myStats->sampleUQ,
+                     realUQWithMask);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+    psFree(maskVector);
+    ok(!psMemCheckLeaks (idGlobal, NULL, NULL, false), "no memory leaks");
+    ok(!psMemCheckLeaks (idGlobal, NULL, NULL, false), "no memory leaks");
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats09.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats09.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats09.c	(revision 22322)
@@ -0,0 +1,365 @@
+/*****************************************************************************
+    This routine must ensure that PS_STAT_CLIPPED_MEAN and
+    PS_STAT_CLIPPED_STDEV is calculate correctly by the procedure
+    psVectorStats().
+ 
+    XXX: The capability is here to test a wide variety of input parameters.
+    We must do this, later.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_DATA 1000
+#define VERBOSE 0
+#define EXTRA_VERBOSE 0
+#define PERCENT_OUTLIERS 2
+#define ERROR_TOLERANCE .10
+#define ERRORS 1.0
+#define SEED 1995
+
+#define TST_IN_NULL  0x00000001
+#define TST_IN_F32  0x00000002
+#define TST_IN_F64  0x00000004
+#define TST_IN_S8  0x00000008
+#define TST_IN_U16  0x00000010
+#define TST_IN_S32  0x00000020
+#define TST_ERRORS_NULL  0x00000040
+#define TST_ERRORS_F32  0x00000080
+#define TST_ERRORS_F64  0x00000100
+#define TST_ERRORS_S8  0x00000200
+#define TST_ERRORS_U16  0x00000400
+#define TST_ERRORS_S32  0x00000800
+#define TST_MASK_NULL  0x00001000
+#define TST_MASK_U8  0x00002000
+#define TST_MASK_S32  0x00004000
+
+
+psBool genericClippedStatsTest(
+    unsigned int flags,
+    psS32 numData,
+    psU32 maskValue,
+    psBool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    psBool testStatus = true;
+    psS32 memLeaks = 0;
+    psVector *in = NULL;
+    psVector *errors = NULL;
+    psVector *mask = NULL;
+    srand(SEED);
+
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
+    psVector *truth = psVectorAlloc(numData, PS_TYPE_F64);
+    for (long i = 0; i < numData; i++) {
+        truth->data.F64[i] = psRandomGaussian(rng);
+    }
+    psFree(rng);
+
+    if (expectedRC == true) {
+        if (VERBOSE)
+            printf("This test should not generate any errors.\n");
+    }
+    if (expectedRC == false) {
+        if (VERBOSE)
+            printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (flags & TST_IN_NULL) {
+        if (VERBOSE)
+            printf("        using a NULL in vector\n");
+    }
+
+    if (flags & TST_IN_F32) {
+        if (VERBOSE)
+            printf("        using a psF32 in vector\n");
+        in = psVectorCopy(in, truth, PS_TYPE_F32);
+    }
+
+    if (flags & TST_IN_F64) {
+        if (VERBOSE)
+            printf("        using a psF64 in vector\n");
+        in = psVectorCopy(in, truth, PS_TYPE_F64);
+    }
+
+    if (flags & TST_IN_S8) {
+        if (VERBOSE)
+            printf("        using a psS8 in vector\n");
+        in = psVectorCopy(in, truth, PS_TYPE_S8);
+    }
+
+    if (flags & TST_IN_U16) {
+        if (VERBOSE)
+            printf("        using a psU16 in vector\n");
+        in = psVectorCopy(in, truth, PS_TYPE_U16);
+    }
+
+    if (flags & TST_IN_S32) {
+        if (VERBOSE)
+            printf("        using a psS32 in vector\n");
+        in = psVectorCopy(in, truth, PS_TYPE_S32);
+    }
+
+    if (flags & TST_ERRORS_NULL) {
+        if (VERBOSE)
+            printf("        using a NULL errors vector\n");
+    }
+
+    if (flags & TST_ERRORS_F32) {
+        if (VERBOSE)
+            printf("        using a psF32 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_F32);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.F32[i] = ERRORS;
+        }
+    }
+
+    if (flags & TST_ERRORS_F64) {
+        if (VERBOSE)
+            printf("        using a psF64 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_F64);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.F64[i] = ERRORS;
+        }
+    }
+
+    if (flags & TST_ERRORS_S8) {
+        if (VERBOSE)
+            printf("        using a psS8 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_S8);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.S8[i] = (psS8) ERRORS;
+        }
+    }
+
+    if (flags & TST_ERRORS_U16) {
+        if (VERBOSE)
+            printf("        using a psU16 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_U16);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.U16[i] = (psU16) ERRORS;
+        }
+    }
+
+    if (flags & TST_ERRORS_S32) {
+        if (VERBOSE)
+            printf("        using a psS32 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.S32[i] = (psS32) ERRORS;
+        }
+    }
+
+
+    if (flags & TST_MASK_NULL) {
+        if (VERBOSE)
+            printf("        using a NULL mask vector\n");
+    }
+
+    if (flags & TST_MASK_U8) {
+        if (VERBOSE)
+            printf("        using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = (psU8) 0;
+        }
+    }
+
+    if (flags & TST_MASK_S32) {
+        if (VERBOSE)
+            printf("        using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = (psS32) 0;
+        }
+    }
+
+
+    //
+    // We add a few outliers to the input data.
+    //
+    psVector *outliers = psVectorAlloc(numData, PS_TYPE_U8);
+    psVectorInit(outliers, 0);
+    long numOutliers = 0;
+    if (flags & TST_IN_F32) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.F32[i] = 100.0;
+                outliers->data.U8[i] = 1;
+                numOutliers++;
+            }
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Data %d: (%f)\n", i, in->data.F32[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_F64) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.F64[i] = 100.0;
+                outliers->data.U8[i] = 1;
+                numOutliers++;
+            }
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Data %d: (%f)\n", i, in->data.F64[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_S8) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.S8[i] = 100;
+                outliers->data.U8[i] = 1;
+                numOutliers++;
+            }
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Data %d: (%d)\n", i, in->data.S8[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_U16) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.U16[i] = 100;
+                outliers->data.U8[i] = 1;
+                numOutliers++;
+            }
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Data %d: (%d)\n", i, in->data.U16[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_S32) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.S32[i] = 100;
+                outliers->data.U8[i] = 1;
+                numOutliers++;
+            }
+        }
+
+        if (EXTRA_VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Data %d: (%d)\n", i, in->data.S32[i]);
+            }
+        }
+    }
+
+    if (VERBOSE)
+        printf("%ld outliers.\n", numOutliers);
+
+    //
+    // We calculate the sample mean and stdev without and outliers in the data.
+    // We will use this later in determining if the clipped stats are correct.
+    //
+    psF32 sampleMean=0.0;
+    psF32 sampleStdev=0.0;
+    if (expectedRC == true) {
+        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        bool rc = psVectorStats(myStats, in, errors, outliers, 1);
+        if (rc == false) {
+            diag("TEST ERROR: the psVectorStats() function returned NULL (a).\n");
+            testStatus = false;
+        } else {
+            sampleMean = myStats->sampleMean;
+            sampleStdev = myStats->sampleStdev;
+        }
+        psFree(myStats);
+    }
+    psFree(outliers);
+
+    //
+    // We call psVectorStats() and calculate the clipped stats.
+    //
+    psStats *myStats = psStatsAlloc(PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+    myStats->clipSigma = 5.0;
+    myStats->clipIter = 2;
+    bool rc = psVectorStats(myStats, in, errors, mask, maskValue);
+    if (rc == false) {
+        if (expectedRC == true) {
+            diag("TEST ERROR: the psVectorStats() function returned NULL (b).\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            diag("TEST ERROR: the psVectorStats() function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (VERBOSE)
+            printf("Used %ld data points after clipping %ld.\n", myStats->clippedNvalues,
+                   in->n - myStats->clippedNvalues);
+
+        if (fabs(myStats->clippedMean - sampleMean) > (ERROR_TOLERANCE * sampleMean)) {
+            diag("TEST ERROR: the clipped mean was %f, should have been %f\n", myStats->clippedMean, sampleMean);
+            testStatus = false;
+        } else if (EXTRA_VERBOSE) {
+            printf("GOOD: the clipped mean was %f, should have been %f\n", myStats->clippedMean, sampleMean);
+        }
+
+        if (fabs(myStats->clippedStdev - sampleStdev) > (ERROR_TOLERANCE * sampleStdev)) {
+            diag("TEST ERROR: the clipped stdev was %f, should have been %f\n", myStats->clippedStdev, sampleStdev);
+            testStatus = false;
+        } else if (EXTRA_VERBOSE) {
+            printf("GOOD: the clipped stdev was %f, should have been %f\n", myStats->clippedStdev, sampleStdev);
+        }
+
+    }
+
+    psFree(myStats);
+    psFree(truth);
+    psFree(in);
+    psFree(errors);
+    psFree(mask);
+    psMemCheckCorruption(stderr, false);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    return(testStatus);
+}
+
+#define TRACE_LEVEL 0
+psS32 main()
+{
+    psMemId id = psMemGetId();
+    psLogSetFormat("HLNM");
+    plan_tests(6);
+
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorSampleMean", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorMax", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorMin", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorCheckNonEmpty", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorNValues", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorClippedStats", TRACE_LEVEL);
+    psTraceSetLevel("p_psNormalizeVectorRange", TRACE_LEVEL);
+    psTraceSetLevel("psStatsAlloc", TRACE_LEVEL);
+    psTraceSetLevel("p_psConvertToF32", TRACE_LEVEL);
+    psTraceSetLevel("psVectorStats", TRACE_LEVEL);
+
+    ok(genericClippedStatsTest(TST_IN_NULL | TST_ERRORS_NULL | TST_MASK_NULL, NUM_DATA, 1, false), "GenericClippedStatsTest(): NULL data");
+    ok(genericClippedStatsTest(TST_IN_F32 | TST_ERRORS_NULL | TST_MASK_NULL, NUM_DATA, 1, true), "GenericClippedStatsTest(): F32 data");
+    ok(genericClippedStatsTest(TST_IN_F64 | TST_ERRORS_NULL | TST_MASK_NULL, NUM_DATA, 1, true), "GenericClippedStatsTest(): F64 data");
+    ok(genericClippedStatsTest(TST_IN_F32 | TST_ERRORS_F32 | TST_MASK_NULL, NUM_DATA, 1, true), "GenericClippedStatsTest(): non-NULL error vector");
+    ok(genericClippedStatsTest(TST_IN_F32 | TST_ERRORS_NULL | TST_MASK_U8, NUM_DATA, 1, true), "GenericClippedStatsTest(): non-NULL mask vector");
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStatsTiming.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStatsTiming.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStatsTiming.c	(revision 22322)
@@ -0,0 +1,828 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+// example tap lines:
+// ok(condition, "condition succeeded");
+// skip_start(condition, Nskip, "Skipping tests because of failure");
+
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+struct timeval start, mark;
+
+int main (void)
+{
+    plan_tests(68);
+
+//    diag("psStats timing tests");
+
+    // build a gauss-deviate vector (mean = 0.0, sigma = 1.0) for tests
+    psRandom *seed = psRandomAlloc (PS_RANDOM_TAUS, 0);
+    psVector *rnd = psVectorAlloc (1000, PS_TYPE_F32);
+    for (int i = 0; i < rnd->n; i++) {
+        rnd->data.F32[i] = psRandomGaussian (seed);
+    }
+
+//    diag ("timing for sample mean");
+    /********** SAMPLE MEAN ***********/
+    // test stat sample mean (no mask, no range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN);
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, NULL, 0);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.1, "sample mean %f (mask: 0, range: 0): %.3f sec", stats->sampleMean, delta);
+        psFree (stats);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample mean (mask, no range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN);
+        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+        mask->data.U8[100] = 1;
+        mask->data.U8[200] = 1;
+        mask->data.U8[300] = 1;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, mask, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.12, "sample mean %f (mask: 1, range: 0): %.3f sec", stats->sampleMean, delta);
+        psFree (stats);
+        psFree (mask);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample mean (no mask, range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE);
+        stats->min = -10;
+        stats->max = +10;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, NULL, 0);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.18, "sample mean %f (mask: 0, range: 1): %.3f sec", stats->sampleMean, delta);
+        psFree (stats);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample mean (mask, range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE);
+        stats->min = -10;
+        stats->max = +10;
+        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+        mask->data.U8[100] = 1;
+        mask->data.U8[200] = 1;
+        mask->data.U8[300] = 1;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, mask, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.2, "sample mean %f (mask: 1, range: 1): %.3f sec", stats->sampleMean, delta);
+        psFree (stats);
+        psFree (mask);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample mean (mask, range : small sample)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE);
+        stats->min = -10;
+        stats->max = +10;
+        psVector *mask = psVectorAlloc (10, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+        mask->data.U8[3] = 1;
+        int nOld = rnd->n;
+
+        rnd->n = 10;
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, mask, 1);
+        }
+        gettimeofday (&mark, NULL);
+        rnd->n = nOld;
+
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.2, "sample mean %f (mask: 1, range: 1): %.3f sec", stats->sampleMean, delta);
+        psFree (stats);
+        psFree (mask);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+//    diag ("timing for sample median");
+    /********** SAMPLE MEDIAN ***********/
+    // test stat sample median (no mask, no range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, NULL, 0);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 2.8, "sample median %f (mask: 0, range: 0): %.3f sec", stats->sampleMedian, delta);
+        psFree (stats);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample median (mask, no range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+        mask->data.U8[100] = 1;
+        mask->data.U8[200] = 1;
+        mask->data.U8[300] = 1;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, mask, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 2.8, "sample median %f (mask: 1, range: 0): %.3f sec", stats->sampleMedian, delta);
+        psFree (stats);
+        psFree (mask);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample median (no mask, range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_USE_RANGE);
+        stats->min = -10;
+        stats->max = +10;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, NULL, 0);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 2.8, "sample median %f (mask: 0, range: 1): %.3f sec", stats->sampleMedian, delta);
+        psFree (stats);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample median (mask, range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_USE_RANGE);
+        stats->min = -10;
+        stats->max = +10;
+        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+        mask->data.U8[100] = 1;
+        mask->data.U8[200] = 1;
+        mask->data.U8[300] = 1;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, mask, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 2.8, "sample median %f (mask: 1, range: 1): %.3f sec", stats->sampleMedian, delta);
+        psFree (stats);
+        psFree (mask);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+//    diag ("timing for sample stdev");
+    /********** SAMPLE STDEV ***********/
+    // test stat sample stdev (no mask, no range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_STDEV);
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, NULL, 0);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.2, "sample stdev %f (mask: 0, range: 0): %.3f sec", stats->sampleStdev, delta);
+        psFree (stats);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample stdev (mask, no range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_STDEV);
+        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+        mask->data.U8[100] = 1;
+        mask->data.U8[200] = 1;
+        mask->data.U8[300] = 1;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, mask, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.27, "sample stdev %f (mask: 1, range: 0): %.3f sec", stats->sampleStdev, delta);
+        psFree (stats);
+        psFree (mask);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample stdev (no mask, range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_STDEV | PS_STAT_USE_RANGE);
+        stats->min = -10;
+        stats->max = +10;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, NULL, 0);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.36, "sample stdev %f (mask: 0, range: 1): %.3f sec", stats->sampleStdev, delta);
+        psFree (stats);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample stdev (mask, range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_STDEV | PS_STAT_USE_RANGE);
+        stats->min = -10;
+        stats->max = +10;
+        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+        mask->data.U8[100] = 1;
+        mask->data.U8[200] = 1;
+        mask->data.U8[300] = 1;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, mask, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.42, "sample stdev %f (mask: 1, range: 1): %.3f sec", stats->sampleStdev, delta);
+        psFree (stats);
+        psFree (mask);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test stat sample stdev (mask, range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_STDEV | PS_STAT_USE_RANGE);
+        stats->min = -10;
+        stats->max = +10;
+        psVector *mask = psVectorAlloc (10, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+        mask->data.U8[1] = 1;
+        int nOld = rnd->n;
+
+        rnd->n = 10;
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, mask, 1);
+        }
+        gettimeofday (&mark, NULL);
+        rnd->n = nOld;
+
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.42, "sample stdev %f (mask: 1, range: 1): %.3f sec", stats->sampleStdev, delta);
+        psFree (stats);
+        psFree (mask);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+//    diag ("timing for sample min,max");
+    /*************** MIN,MAX ******************/
+    // test stat min,max (no mask, no range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.17, "sample min,max %f,%f (mask: 0, range: 0): %.3f sec", stats->min, stats->max, delta);
+        psFree (stats);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    // test stat min,max (no mask, no range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
+        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+        mask->data.U8[100] = 1;
+        mask->data.U8[200] = 1;
+        mask->data.U8[300] = 1;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, mask, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.18, "sample min,max %f,%f (mask: 1, range: 0): %.3f sec", stats->min, stats->max, delta);
+        psFree (stats);
+        psFree (mask);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    // test stat min,max (no mask, no range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX | PS_STAT_USE_RANGE);
+        stats->min = -10;
+        stats->max = +10;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.22, "sample min,max %f,%f (mask: 0, range: 1): %.3f sec", stats->min, stats->max, delta);
+        psFree (stats);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    // test stat min,max (no mask, no range)
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX | PS_STAT_USE_RANGE);
+        stats->min = -10;
+        stats->max = +10;
+        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
+        psVectorInit (mask, 0);
+        mask->data.U8[100] = 1;
+        mask->data.U8[200] = 1;
+        mask->data.U8[300] = 1;
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 10000; i++)
+        {
+            psVectorStats (stats, rnd, NULL, mask, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.26, "sample min,max %f,%f (mask: 1, range: 1): %.3f sec", stats->min, stats->max, delta);
+        psFree (stats);
+        psFree (mask);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+//    diag ("timing for clipped stats");
+    /********** CLIPPED STATS ***********/
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+        psVector *rnd2 = psVectorAlloc (1000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.3, "clipped mean %f, stdev %f (mask: 0, range: 0): %.3f sec (1000 pts / 1000 loops)", stats->clippedMean, stats->clippedStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+        psVector *rnd2 = psVectorAlloc (3000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.5, "clipped mean %f, stdev %f (mask: 0, range: 0): %.3f sec (3000 pts / 1000 loops)", stats->clippedMean, stats->clippedStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+        psVector *rnd2 = psVectorAlloc (10000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 1.2, "clipped mean %f, stdev %f (mask: 0, range: 0): %.3f sec (10000 pts / 1000 loops)", stats->clippedMean, stats->clippedStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+//    diag ("timing for robust stats");
+    /********** ROBUST STATS ***********/
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE);
+        psVector *rnd2 = psVectorAlloc (1000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.3, "robust mean %f, stdev %f (mask: 0, range: 0): %.3f sec (1000 pts / 1000 loops)", stats->robustMedian, stats->robustStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE);
+        psVector *rnd2 = psVectorAlloc (3000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.5, "robust mean %f, stdev %f (mask: 0, range: 0): %.3f sec (3000 pts / 1000 loops)", stats->robustMedian, stats->robustStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE);
+        psVector *rnd2 = psVectorAlloc (10000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 1.2, "robust mean %f, stdev %f (mask: 0, range: 0): %.3f sec (10000 pts / 1000 loops)", stats->robustMedian, stats->robustStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+//    diag ("timing for fitted stats");
+    /********** FITTED TIMING ***********/
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV);
+        psVector *rnd2 = psVectorAlloc (1000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.7, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (1000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV);
+        psVector *rnd2 = psVectorAlloc (3000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.8, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (3000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV);
+        psVector *rnd2 = psVectorAlloc (10000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 2.2, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (10000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+//    diag ("timing for fitted (v2) stats");
+    /********** FITTED (v2) TIMING ***********/
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2);
+        psVector *rnd2 = psVectorAlloc (1000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.7, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (1000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2);
+        psVector *rnd2 = psVectorAlloc (3000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 0.8, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (3000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2);
+        psVector *rnd2 = psVectorAlloc (10000, PS_TYPE_F32);
+        for (int i = 0; i < rnd2->n; i++)
+        {
+            rnd2->data.F32[i] = psRandomGaussian (seed);
+        }
+
+        gettimeofday (&start, NULL);
+        for (int i = 0; i < 1000; i++)
+        {
+            psVectorStats (stats, rnd2, NULL, NULL, 1);
+        }
+        gettimeofday (&mark, NULL);
+        psF64 delta = DTIME(mark, start);
+        ok (delta < 2.2, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (10000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
+        psFree (stats);
+        psFree (rnd2);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+//    diag ("compare sample, robust, and fitted mean and stdev to theoretical");
+    // compare SAMPLE, FITTED, ROBUST mean to theoretical
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV);
+        psVector *sample = psVectorAlloc (1000, PS_TYPE_F32);
+        psVector *robust = psVectorAlloc (1000, PS_TYPE_F32);
+        psVector *fitted = psVectorAlloc (1000, PS_TYPE_F32);
+
+        for (int i = 0; i < 1000; i++)
+        {
+            // generate a new sample
+            for (int j = 0; j < rnd->n; j++) {
+                rnd->data.F32[j] = psRandomGaussian (seed);
+            }
+            // measure the stats
+            psVectorStats (stats, rnd, NULL, NULL, 1);
+            sample->data.F32[i] = stats->sampleMean;
+            robust->data.F32[i] = stats->robustMedian;
+            fitted->data.F32[i] = stats->fittedMean;
+        }
+        psFree (stats);
+
+        stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        psVectorStats (stats, sample, NULL, NULL, 1);
+        ok (stats->sampleStdev < 2/sqrt(1000), "sample mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
+        psVectorStats (stats, robust, NULL, NULL, 1);
+        ok (stats->sampleStdev < 2/sqrt(1000), "robust mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
+        psVectorStats (stats, fitted, NULL, NULL, 1);
+        ok (stats->sampleStdev < 2/sqrt(1000), "fitted mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
+        psFree (stats);
+        psFree (sample);
+        psFree (robust);
+        psFree (fitted);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+//    diag ("compare sample, robust, and fitted mean and stdev to theoretical");
+    // compare SAMPLE, FITTED_V2, ROBUST mean to theoretical
+    {
+        psMemId id = psMemGetId();
+
+        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2);
+        psVector *sample = psVectorAlloc (1000, PS_TYPE_F32);
+        psVector *robust = psVectorAlloc (1000, PS_TYPE_F32);
+        psVector *fitted = psVectorAlloc (1000, PS_TYPE_F32);
+
+        for (int i = 0; i < 1000; i++)
+        {
+            // generate a new sample
+            for (int j = 0; j < rnd->n; j++) {
+                rnd->data.F32[j] = psRandomGaussian (seed);
+            }
+            // measure the stats
+            psVectorStats (stats, rnd, NULL, NULL, 1);
+            sample->data.F32[i] = stats->sampleMean;
+            robust->data.F32[i] = stats->robustMedian;
+            fitted->data.F32[i] = stats->fittedMean;
+        }
+        psFree (stats);
+
+        stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        psVectorStats (stats, sample, NULL, NULL, 1);
+        ok (stats->sampleStdev < 2/sqrt(1000), "sample mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
+        psVectorStats (stats, robust, NULL, NULL, 1);
+        ok (stats->sampleStdev < 2/sqrt(1000), "robust mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
+        psVectorStats (stats, fitted, NULL, NULL, 1);
+        ok (stats->sampleStdev < 2/sqrt(1000), "fitted mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
+        psFree (stats);
+        psFree (sample);
+        psFree (robust);
+        psFree (fitted);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    return exit_status();
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStatsTiming.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStatsTiming.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStatsTiming.txt	(revision 22322)
@@ -0,0 +1,47 @@
+
+Running tap_psStatsTiming on alala (dual AMD Opteron, 64bit, 2.2GHz)
+yields the following timing results:
+
+# timing for sample mean (1000 loops of 10000 pts)
+ok 1 - sample mean 0.009719 (mask: 0, range: 0): 0.072 sec
+ok 3 - sample mean 0.011060 (mask: 1, range: 0): 0.119 sec
+ok 5 - sample mean 0.009719 (mask: 0, range: 1): 0.170 sec
+ok 7 - sample mean 0.011060 (mask: 1, range: 1): 0.198 sec
+
+# timing for sample median (1000 loops of 10000 pts)
+ok 9 - sample median 0.021781 (mask: 0, range: 0): 2.625 sec
+ok 11 - sample median 0.023795 (mask: 1, range: 0): 2.646 sec
+ok 13 - sample median 0.021781 (mask: 0, range: 1): 2.703 sec
+ok 15 - sample median 0.023795 (mask: 1, range: 1): 2.716 sec
+
+# timing for sample stdev (1000 loops of 10000 pts)
+ok 17 - sample stdev 0.964753 (mask: 0, range: 0): 0.193 sec
+ok 19 - sample stdev 0.965887 (mask: 1, range: 0): 0.257 sec
+ok 21 - sample stdev 0.964753 (mask: 0, range: 1): 0.353 sec
+ok 23 - sample stdev 0.965887 (mask: 1, range: 1): 0.401 sec
+
+# timing for sample min,max (1000 loops of 10000 pts)
+ok 25 - sample min,max -3.205688,2.706797 (mask: 0, range: 0): 0.125 sec
+ok 27 - sample min,max -3.205688,2.706797 (mask: 1, range: 0): 0.152 sec
+ok 29 - sample min,max -3.205688,2.706797 (mask: 0, range: 1): 0.201 sec
+ok 31 - sample min,max -3.205688,2.706797 (mask: 1, range: 1): 0.238 sec
+
+# timing for clipped stats
+not ok 33 - clipped mean -0.047714, stdev 0.991979 (mask: 0, range: 0): 0.369 sec (1000 pts / 1000 loops)
+not ok 35 - clipped mean 0.023963, stdev 0.972186 (mask: 0, range: 0): 1.219 sec (3000 pts / 1000 loops)
+not ok 37 - clipped mean -0.007020, stdev 0.985410 (mask: 0, range: 0): 4.883 sec (10000 pts / 1000 loops)
+
+NOTE: these fail because they are being compared to the 'robust' stats
+limits below.  The clipped mean algorithm should not be so slow (and
+apparently non-linear in npts).
+
+# timing for robust stats
+ok 39 - robust mean 0.123348, stdev 1.014896 (mask: 0, range: 0): 0.187 sec (1000 pts / 1000 loops)
+ok 41 - robust mean -0.006812, stdev 0.974468 (mask: 0, range: 0): 0.382 sec (3000 pts / 1000 loops)
+ok 43 - robust mean -0.013591, stdev 1.001539 (mask: 0, range: 0): 1.076 sec (10000 pts / 1000 loops)
+
+# timing for fitted stats
+ok 45 - fitted mean -0.029859, stdev 0.982947 (mask: 0, range: 0): 0.381 sec (1000 pts / 1000 loops)
+ok 47 - fitted mean 0.014660, stdev 0.956168 (mask: 0, range: 0): 0.727 sec (3000 pts / 1000 loops)
+ok 49 - fitted mean -0.008402, stdev 1.001366 (mask: 0, range: 0): 1.914 sec (10000 pts / 1000 loops)
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats_Sample_01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats_Sample_01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tap_psStats_Sample_01.c	(revision 22322)
@@ -0,0 +1,608 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+// example tap lines:
+// ok(condition, "condition succeeded");
+// skip_start(condition, Nskip, "Skipping tests because of failure");
+
+static float yraw_01[] = {
+                             11, 16, 13, 17, 17, 17, 23, 14, 18, 15,
+                             16, 13, 12, 17, 19, 15, 21, 18, 18, 17,
+                             15, 20, 26, 14, 12, 13, 19, 16, 14, 21,
+                             16, 12, 18, 14, 18, 25, 20, 16, 19, 16,
+                             14, 12, 20, 13, 12, 21, 16, 18, 11, 22,
+                             16, 23, 20, 21, 54, 12, 12, 16, 17, 14,
+                             18, 21, 19, 18, 16, 19, 18, 23, 21, 20,
+                             20, 22, 15, 13, 14, 15, 19, 16, 15, 23,
+                             14, 25, 18, 16, 20, 20, 19, 16, 22, 11,
+                             19, 12, 14, 21, 19, 15, 16, 35, 20, 18,
+                             14, 13, 18, 18, 19, 23, 19, 19, 13, 288,
+                             19, 17, 17, 19, 12, 18, 15, 14, 13, 16,
+                             10, 12, 22, 15, 13, 14, 15, 22, 16, 18,
+                             13, 19, 17, 17, 17, 16, 16, 15, 15, 24,
+                             25, 45, 23, 19, 18, 23, 12, 18, 20, 11,
+                             25, 16, 15, 15, 200, 16, 13, 19, 11, 16,
+                             19, 16, 13, 14, 12, 10, 17, 20, 25, 17,
+                             14, 13, 20, 9, 17, 13, 19, 16, 27, 19,
+                             20, 16, 15, 17, 15, 18, 20, 17, 19, 13,
+                             21, 11, 20, 19, 12, 17, 23, 8, 17, 13,
+                             11, 16, 29, 16, 16, 20, 13, 19, 20, 19,
+                             18, 22, 21, 20, 15, 11, 17, 17, 15, 17,
+                             13, 16, 19, 20, 21, 20, 18, 17, 99, 14,
+                             13, 20, 16, 18, 12, 15, 21, 17, 18, 20,
+                             19, 12, 9, 24, 10, 13, 18, 16, 15, 19,
+                             13, 19, 12, 22, 22, 20, 17, 13, 14, 14,
+                             20, 10, 19, 19, 18, 20, 15, 14, 13, 20,
+                             13, 14, 17, 19, 16, 25, 17, 15, 11, 19,
+                             19, 14, 15, 15, 12, 14, 16, 13, 20, 16,
+                             10, 15, 20, 21, 19, 17, 12, 18, 17, 17,
+                             20, 14, 19, 17, 19, 7, 12, 17, 24, 18,
+                             17, 13, 14, 16, 20, 15, 10, 18, 13, 18,
+                             17, 16, 13, 13, 21, 13, 15, 11, 15, 16,
+                             18, 13, 11, 18, 13, 12, 15, 17, 16, 19,
+                             12, 14, 20, 13, 26, 16, 20, 14, 12, 18,
+                             15, 24, 14, 12, 19, 19, 12, 15, 18, 20,
+                             16, 17625, 17, 17, 14, 22, 15, 17, 19, 17,
+                             15, 16, 13, 14, 18, 16, 11, 20, 20, 14,
+                             19, 16, 11, 12, 18, 13, 8, 20, 10, 17,
+                             18, 17, 21, 16, 18, 16, 16, 22, 19, 12,
+                             15, 14, 21, 13, 16, 11, 17, 12, 19, 18,
+                             18, 15, 18, 12, 17, 24, 12, 20, 21, 14,
+                             21, 14, 21, 11, 19, 13, 22, 15, 17, 21,
+                             21, 22, 16, 14, 21, 26, 17, 21, 20, 11,
+                             12, 15, 21, 19, 19, 17, 22, 16, 17, 18,
+                             13, 12, 13, 88, 14, 14, 9, 19, 18, 12,
+                             20, 16, 16, 14, 17, 19, 19, 6, 16, 15,
+                             20, 17, 10, 19, 15, 21, 20, 9, 14, 17,
+                             10, 27, 22, 18, 11, 19, 15, 15, 19, 13,
+                             15, 24, 22, 20, 19, 14, 16, 17, 12, 15,
+                             22, 12, 18, 19, 22, 18, 23, 19, 15, 15,
+                             8, 13, 24, 15, 12, 30, 16, 18, 15, 21,
+                             18, 14, 18, 20, 17, 12, 20, 19, 12, 17,
+                             13, 18, 20, 16, 23, 12, 17, 9, 18, 13,
+                             11, 15, 14, 20, 19, 16, 12, 19, 19, 18,
+                             22, 23, 18, 23, 18, 17, 19, 22, 15, 11,
+                             17, 20, 13, 19, 18, 17, 18, 20, 13, 14,
+                             14, 10, 22, 21, 13, 19, 14, 16, 14, 17,
+                             19, 14, 14, 26, 21, 18, 20, 11, 18, 19,
+                             20, 18, 15, 15, 19, 18, 15, 17, 12, 15,
+                             20, 18, 14, 22, 12, 13, 23, 22, 17, 10,
+                             15, 19, 18, 18, 17, 16, 19, 13, 19, 23,
+                             15, 13, 10, 14, 16, 14, 17, 15, 16, 20,
+                             17, 12, 20, 11, 19, 11, 20, 9, 27, 23,
+                             17, 18, 18, 19, 20, 15, 15, 18, 13, 13,
+                             21, 15, 18, 13, 19, 23, 19, 8, 21, 19,
+                             9, 22, 15, 14, 24, 17, 19, 15, 22, 19,
+                             18, 13, 11, 18, 13, 21, 17, 12, 13, 18,
+                             11, 16, 19, 16, 19, 24, 15, 16, 17, 18,
+                             19, 17, 19, 20, 18, 14, 16, 15, 20, 13,
+                             13, 16, 10, 15, 29, 13, 22, 18, 11, 13,
+                             18, 23, 13, 11, 19, 25, 18, 17, 17, 20,
+                             16, 16, 22, 12, 20, 39, 14, 21, 19, 10,
+                             61, 18, 24, 16, 14, 20, 20, 19, 16, 18,
+                             11, 16, 16, 20, 16, 7, 16, 14, 12, 24,
+                             20, 16, 17, 15, 11, 20, 13, 12, 25, 15,
+                             13, 15, 18, 12, 6, 18, 15, 24, 11, 15,
+                             14, 12, 14, 15, 15, 17, 20, 14, 21, 17,
+                             14, 19, 22, 13, 15, 12, 17, 19, 23, 15,
+                             17, 25, 15, 14, 23, 6, 17, 9, 10, 16,
+                             17, 17, 16, 18, 26, 15, 17, 23, 14, 11,
+                             22, 19, 21, 16, 15, 21, 15, 10, 22, 14,
+                             23, 12, 16, 20, 13, 16, 11, 19, 12, 19,
+                             16, 15, 43, 18, 16, 13, 27, 12, 13, 14,
+                             18, 15, 14, 18, 19, 14, 15, 21, 15, 20,
+                             13, 21, 19, 21, 18, 21, 13, 24, 22, 14,
+                             22, 14, 17, 21, 15, 17, 14, 16, 21, 11,
+                             17, 21, 26, 9, 12, 17, 15, 13, 13, 16,
+                             25, 23, 17, 13, 20, 16, 19, 17, 18, 19,
+                             17, 17, 20, 17, 17, 15, 20, 15, 10, 13,
+                             11, 18, 9, 19, 18, 28, 18, 19, 18, 15,
+                             22, 16, 16, 17, 15, 24, 19, 16, 10, 18,
+                             16, 15, 32, 17, 15, 17, 18, 11, 25, 15,
+                             19, 11, 20, 17, 17, 20, 18, 18, 16, 22,
+                             19, 15, 22, 16, 14, 21, 14, 14, 13, 14,
+                             14, 19, 21, 18, 18, 19, 12, 13, 19, 19,
+                             19, 16, 18, 16, 13, 17, 25, 17, 18, 16,
+                             25, 25, 19, 20, 13, 23, 13, 14, 18, 18,
+                             20, 27, 15, 23, 23, 17, 25, 13, 12, 19,
+                             17, 13, 25, 18, 13, 12, 15, 18, 16, 12,
+                         };
+
+static float yraw_02[] = {
+                             428, 405, 820, 581, 629, 396, 383, 400, 631, 413,
+                             614, 379, 646, 1629, 353, 2748, 400, 654, 1242, 556,
+                             1423, 524, 7261, 1997, 697, 1389, 818, 26580, 495, 26915,
+                             320, 1242, 592, 490, 580, 651, 421, 487, 1644, 1909,
+                             2791, 551, 386, 426, 504, 995, 598, 1235, 481, 589,
+                             619, 1608, 385, 687, 1119, 463, 1803, 338, 427, 2509,
+                             503, 426, 1305, 425, 394, 1173, 772, 672, 2239, 1171,
+                             1117, 583, 1028, 1837, 2287, 413, 762, 2109, 955, 959,
+                             1682, 1682, 1555, 481, 663, 987, 415, 668, 417, 1045,
+                             2094, 464, 3437, 1566, 487, 1067, 975, 1217, 6089, 383,
+                             394, 415, 1117, 522, 673, 1567, 559, 15690, 325, 591,
+                             607, 634, 966, 293, 1178, 334, 526, 552, 994, 1852,
+                             2689, 1175, 1221, 410, 933, 1106, 1094, 7888, 461, 2142,
+                             600, 395, 1963, 663, 882, 582, 456, 715, 459, 1527,
+                             1948, 1074, 1035, 556, 519, 624, 728, 415, 735, 414,
+                             392, 586, 745, 624, 588, 912, 1550, 2030, 451, 25635,
+                             774, 395, 452, 593, 644, 1807, 442, 33256, 1916, 1804,
+                             13222, 538, 1054, 433, 2055, 832, 545, 924, 2808, 597,
+                             456, 833, 812, 780, 422, 1189, 617, 1613, 1349, 30099,
+                             561, 2128, 1225, 1100, 415, 950, 403, 34824, 496, 388,
+                             670, 372, 503, 363, 604, 405, 1239, 612, 300, 17802,
+                             1560, 1123, 380, 2398, 1156, 1007, 1795, 311, 370, 1905,
+                             845, 1298, 1711, 1962, 408, 1421, 638, 657, 2339, 2242,
+                             3202, 2687, 1478, 618, 19826, 1263, 359, 888, 346, 673,
+                             487, 500, 2167, 1441, 382, 574, 360, 859, 2557, 441,
+                             1870, 361, 524, 864, 1910, 1993, 831, 542, 815, 677,
+                             321, 1018, 655, 1076, 680, 779, 518, 19929, 429, 514,
+                             972, 946, 388, 508, 1019, 316, 17112, 640, 2896, 1328,
+                             805, 432, 345, 858, 596, 488, 2105, 427, 1761, 711,
+                             689, 1003, 454, 427, 499, 468, 922, 394, 400, 2056,
+                             431, 433, 327, 1635, 438, 462, 602, 973, 761, 363,
+                             2206, 2335, 2451, 332, 1927, 1495, 649, 1112, 1036, 1360,
+                             539, 931, 1396, 408, 332, 871, 449, 508, 368, 1213,
+                             400, 479, 402, 1787, 426, 720, 846, 869, 650, 414,
+                             413, 19125, 385, 967, 1109, 22290, 918, 1752, 567, 1766,
+                             386, 1170, 619, 1963, 743, 605, 740, 517, 623, 631,
+                             430, 776, 750, 649, 778, 922, 1194, 868, 489, 770,
+                             387, 406, 545, 563, 1633, 469, 3058, 1600, 918, 1546,
+                             1969, 1611, 806, 699, 583, 581, 803, 657, 394, 1205,
+                             388, 709, 1174, 1849, 1571, 1051, 487, 1301, 1189, 1626,
+                             578, 398, 1514, 350, 501, 535, 1079, 1743, 1426, 1074,
+                             669, 3664, 757, 517, 1027, 1097, 1264, 2322, 359, 527,
+                             664, 560, 558, 22642, 331, 896, 1470, 934, 467, 1019,
+                             727, 424, 353, 601, 1097, 832, 510, 520, 1185, 1420,
+                             363, 965, 619, 2322, 4277, 20030, 2087, 2823, 560, 19312,
+                             1973, 770, 356, 832, 2467, 342, 660, 18751, 1706, 313,
+                             1069, 2490, 1238, 1013, 1471, 3348, 760, 871, 723, 397,
+                             1104, 371, 2382, 27958, 621, 339, 465, 616, 2282, 524,
+                             601, 379, 587, 534, 1408, 1260, 717, 1709, 965, 434,
+                             611, 1344, 387, 1660, 539, 831, 401, 842, 864, 1453,
+                             1746, 468, 391, 398, 376, 932, 342, 483, 824, 384,
+                             991, 591, 443, 419, 1112, 598, 379, 549, 511, 1626,
+                             1221, 14651, 2117, 1570, 899, 1063, 994, 2198, 424, 380,
+                             2611, 3374, 533, 565, 1098, 300, 857, 1517, 720, 1785,
+                             659, 9076, 1145, 476, 1588, 729, 551, 667, 1041, 364,
+                             447, 406, 1271, 1515, 345, 343, 577, 449, 571, 1066,
+                             1161, 1251, 332, 1669, 393, 5503, 466, 481, 18535, 420,
+                             1910, 1093, 4162, 448, 660, 395, 485, 450, 520, 461,
+                             406, 2414, 1150, 714, 411, 18470, 1054, 401, 582, 318,
+                             404, 434, 517, 3252, 1714, 997, 481, 833, 504, 407,
+                             490, 1942, 439, 3112, 910, 605, 727, 1347, 954, 565,
+                             493, 551, 535, 416, 393, 629, 4489, 395, 18156, 487,
+                             504, 618, 438, 1001, 21729, 644, 1340, 390, 355, 716,
+                             911, 575, 541, 2158, 8211, 959, 1004, 1370, 335, 611,
+                             385, 1382, 788, 1135, 929, 2275, 653, 549, 549, 1980,
+                             390, 556, 765, 1185, 1038, 493, 476, 387, 1980, 3695,
+                             808, 297, 1124, 829, 378, 835, 3024, 1273, 613, 1330,
+                             473, 15685, 3112, 628, 358, 1317, 607, 2270, 538, 808,
+                             1083, 287, 1598, 617, 438, 398, 435, 464, 476, 554,
+                             519, 507, 1171, 886, 717, 545, 1698, 1063, 533, 903,
+                             464, 2416, 1608, 18915, 337, 1222, 323, 981, 408, 1482,
+                             1268, 1413, 967, 392, 1373, 19138, 1133, 867, 1661, 2124,
+                             508, 304, 503, 406, 14095, 1091, 370, 435, 1231, 764,
+                             499, 2387, 3092, 361, 530, 945, 878, 1965, 5488, 1967,
+                             1148, 484, 565, 490, 995, 1807, 733, 331, 1273, 1231,
+                             459, 14455, 1510, 555, 7084, 841, 1024, 3561, 792, 440,
+                             1821, 574, 429, 1085, 691, 791, 667, 1684, 622, 1019,
+                             1704, 1105, 899, 307, 1987, 352, 639, 416, 428, 2525,
+                             312, 888, 444, 542, 364, 327, 400, 1629, 28582, 628,
+                             332, 513, 452, 361, 967, 21400, 544, 1103, 1134, 29599,
+                             638, 16500, 1148, 572, 364, 776, 794, 1967, 447, 1015,
+                             433, 619, 417, 897, 3099, 1957, 809, 401, 442, 529,
+                             433, 350, 895, 18687, 390, 553, 565, 1259, 21421, 1340,
+                             306, 2057, 473, 338, 889, 320, 974, 620, 1074, 1048,
+                             325, 300, 1867, 415, 358, 1721, 362, 436, 435, 484,
+                             1478, 1907, 590, 2146, 7816, 583, 381, 1610, 544, 376,
+                             2144, 589, 952, 389, 1145, 483, 958, 854, 814, 588,
+                             25947, 659, 830, 1045, 408, 880, 2352, 1651, 1288, 1022,
+                             1976, 1292, 838, 411, 796, 375, 879, 2339, 1047, 945,
+                             1922, 1322, 455, 358, 677, 25142, 977, 344, 1305, 421,
+                             1119, 682, 823, 1054, 554, 485, 1732, 1980, 326, 444,
+                             1762, 738, 15028, 413, 349, 2474, 2837, 1074, 763, 1400,
+                             686, 1184, 491, 6502, 399, 2194, 384, 968, 403, 462,
+                             499, 555, 1361, 568, 1315, 3912, 514, 514, 1066, 519,
+                             1024, 1307, 1056, 402, 914, 513, 444, 347, 406, 506,
+                             442, 614, 356, 1222, 597, 1162, 515, 22060, 2408, 2930,
+                             5935, 1057, 747, 1396, 400, 1576, 1212, 8900, 318, 688,
+                             2122, 1771, 316, 758, 445, 381, 1115, 394, 2284, 1515,
+                             1877, 637, 1279, 2291,
+                         };
+
+static float yraw_03[] = {
+                             183.6000061, 171.3600006, 182.5800018, 178.5, 176.4600067,
+                             176.4600067, 171.3600006, 178.5, 183.6000061, 175.4400024,
+                             159.1199951, 180.5399933, 183.6000061, 187.6799927, 175.4400024,
+                             181.5599976, 174.4199982, 179.5200043, 180.5399933, 187.6799927,
+                             183.6000061, 171.3600006, 177.4799957, 192.7799988, 184.6199951,
+                             176.4600067, 178.5, 182.5800018, 173.3999939, 191.7599945,
+                             202.9799957, 167.2799988, 181.5599976, 185.6399994, 173.3999939,
+                             159.1199951, 178.5, 188.6999969, 179.5200043, 178.5,
+                             184.6199951, 175.4400024, 186.6600037, 179.5200043, 183.6000061,
+                             182.5800018, 173.3999939, 181.5599976, 184.6199951, 177.4799957,
+                             162.1799927, 186.6600037, 163.1999969, 199.9199982, 173.3999939,
+                             170.3399963, 184.6199951, 190.7400055, 173.3999939, 178.5,
+                             189.7200012, 192.7799988, 166.2599945, 187.6799927, 174.4199982,
+                             176.4600067, 181.5599976, 199.9199982, 193.8000031, 170.3399963,
+                             182.5800018, 199.9199982, 183.6000061, 160.1399994, 184.6199951,
+                             195.8399963, 173.3999939, 189.7200012, 192.7799988, 162.1799927,
+                             174.4199982, 190.7400055, 175.4400024, 192.7799988, 181.5599976,
+                             185.6399994, 184.6199951, 166.2599945, 189.7200012, 192.7799988,
+                             183.6000061, 190.7400055, 188.6999969, 174.4199982, 184.6199951,
+                             186.6600037, 182.5800018, 173.3999939, 171.3600006, 166.2599945,
+                             166.2599945, 176.4600067, 147.8999939, 187.6799927, 166.2599945,
+                             173.3999939, 179.5200043, 166.2599945, 169.3200073, 172.3800049,
+                             174.4199982, 195.8399963, 192.7799988, 177.4799957, 181.5599976,
+                             163.1999969, 171.3600006, 194.8200073, 159.1199951, 173.3999939,
+                             182.5800018, 178.5, 179.5200043, 184.6199951, 179.5200043,
+                             164.2200012, 191.7599945, 177.4799957, 186.6600037, 168.3000031,
+                             168.3000031, 168.3000031, 180.5399933, 179.5200043, 175.4400024,
+                             174.4199982, 184.6199951, 186.6600037, 164.2200012, 169.3200073,
+                             187.6799927, 189.7200012, 166.2599945, 169.3200073, 187.6799927,
+                             172.3800049, 204, 194.8200073, 177.4799957, 188.6999969,
+                             171.3600006, 184.6199951, 182.5800018, 184.6199951, 176.4600067,
+                             186.6600037, 170.3399963, 170.3399963, 182.5800018, 184.6199951,
+                             168.3000031, 175.4400024, 176.4600067, 164.2200012, 183.6000061,
+                             161.1600037, 175.4400024, 167.2799988, 179.5200043, 176.4600067,
+                             175.4400024, 179.5200043, 158.1000061, 168.3000031, 168.3000031,
+                             179.5200043, 192.7799988, 190.7400055, 178.5, 173.3999939,
+                             180.5399933, 179.5200043, 191.7599945, 184.6199951, 177.4799957,
+                             182.5800018, 188.6999969, 169.3200073, 162.1799927, 172.3800049,
+                             160.1399994, 177.4799957, 176.4600067, 180.5399933, 170.3399963,
+                             168.3000031, 171.3600006, 180.5399933, 162.1799927, 163.1999969,
+                             170.3399963, 180.5399933, 164.2200012, 183.6000061, 178.5,
+                             193.8000031, 177.4799957, 178.5, 183.6000061, 192.7799988,
+                             157.0800018, 176.4600067, 190.7400055, 179.5200043, 157.0800018,
+                             178.5, 165.2400055, 195.8399963, 177.4799957, 166.2599945,
+                             172.3800049, 182.5800018, 175.4400024, 187.6799927, 164.2200012,
+                             178.5, 173.3999939, 173.3999939, 173.3999939, 172.3800049,
+                             183.6000061, 181.5599976, 163.1999969, 179.5200043, 193.8000031,
+                             172.3800049, 158.1000061, 171.3600006, 165.2400055, 183.6000061,
+                             187.6799927, 158.1000061, 171.3600006, 168.3000031, 166.2599945,
+                             166.2599945, 178.5, 179.5200043, 165.2400055, 176.4600067,
+                             160.1399994, 193.8000031, 170.3399963, 162.1799927, 180.5399933,
+                             159.1199951, 189.7200012, 160.1399994, 196.8600006, 173.3999939,
+                             170.3399963, 165.2400055, 193.8000031, 192.7799988, 184.6199951,
+                             151.9799957, 167.2799988, 162.1799927, 183.6000061, 166.2599945,
+                             177.4799957, 168.3000031, 191.7599945, 181.5599976, 166.2599945,
+                             171.3600006, 188.6999969, 172.3800049, 195.8399963, 189.7200012,
+                             177.4799957, 167.2799988, 191.7599945, 169.3200073, 181.5599976,
+                             180.5399933, 162.1799927, 171.3600006, 162.1799927, 171.3600006,
+                             177.4799957, 167.2799988, 180.5399933, 182.5800018, 187.6799927,
+                             175.4400024, 193.8000031, 174.4199982, 183.6000061, 179.5200043,
+                             164.2200012, 162.1799927, 186.6600037, 189.7200012, 186.6600037,
+                             171.3600006, 187.6799927, 189.7200012, 175.4400024, 184.6199951,
+                             172.3800049, 177.4799957, 183.6000061, 176.4600067, 171.3600006,
+                             172.3800049, 181.5599976, 175.4400024, 170.3399963, 196.8600006,
+                             201.9600067, 180.5399933, 176.4600067, 183.6000061, 175.4400024,
+                             184.6199951, 163.1999969, 177.4799957, 176.4600067, 175.4400024,
+                             192.7799988, 167.2799988, 176.4600067, 184.6199951, 184.6199951,
+                             177.4799957, 185.6399994, 196.8600006, 186.6600037, 184.6199951,
+                             169.3200073, 164.2200012, 185.6399994, 168.3000031, 186.6600037,
+                             180.5399933, 177.4799957, 177.4799957, 186.6600037, 191.7599945,
+                             177.4799957, 181.5599976, 166.2599945, 179.5200043, 184.6199951,
+                             178.5, 171.3600006, 184.6199951, 192.7799988, 182.5800018,
+                             186.6600037, 175.4400024, 182.5800018, 173.3999939, 186.6600037,
+                             162.1799927, 175.4400024, 196.8600006, 166.2599945, 189.7200012,
+                             189.7200012, 182.5800018, 172.3800049, 165.2400055, 191.7599945,
+                             174.4199982, 173.3999939, 178.5, 171.3600006, 157.0800018,
+                             182.5800018, 161.1600037, 195.8399963, 186.6600037, 167.2799988,
+                             166.2599945, 182.5800018, 178.5, 176.4600067, 181.5599976,
+                             184.6199951, 183.6000061, 179.5200043, 174.4199982, 167.2799988,
+                             187.6799927, 176.4600067, 165.2400055, 179.5200043, 157.0800018,
+                             171.3600006, 170.3399963, 175.4400024, 161.1600037, 185.6399994,
+                             169.3200073, 192.7799988, 175.4400024, 172.3800049, 180.5399933,
+                             183.6000061, 174.4199982, 176.4600067, 164.2200012, 183.6000061,
+                             179.5200043, 165.2400055, 169.3200073, 172.3800049, 149.9400024,
+                             175.4400024, 188.6999969, 190.7400055, 171.3600006, 172.3800049,
+                             183.6000061, 178.5, 165.2400055, 176.4600067, 177.4799957,
+                             188.6999969, 192.7799988, 183.6000061, 163.1999969, 186.6600037,
+                             183.6000061, 160.1399994, 167.2799988, 172.3800049, 179.5200043,
+                             189.7200012, 172.3800049, 177.4799957, 161.1600037, 174.4199982,
+                             190.7400055, 181.5599976, 187.6799927, 176.4600067, 176.4600067,
+                             183.6000061, 176.4600067, 188.6999969, 174.4199982, 170.3399963,
+                             185.6399994, 177.4799957, 172.3800049, 179.5200043, 175.4400024,
+                             170.3399963, 169.3200073, 176.4600067, 177.4799957, 169.3200073,
+                             199.9199982, 171.3600006, 194.8200073, 188.6999969, 193.8000031,
+                             182.5800018, 171.3600006, 177.4799957, 175.4400024, 172.3800049,
+                             166.2599945, 183.6000061, 157.0800018, 177.4799957, 193.8000031,
+                             168.3000031, 175.4400024, 175.4400024, 170.3399963, 191.7599945,
+                             189.7200012, 182.5800018, 177.4799957, 157.0800018, 174.4199982,
+                             189.7200012, 162.1799927, 184.6199951, 164.2200012, 157.0800018,
+                             197.8800049, 175.4400024, 184.6199951, 202.9799957, 190.7400055,
+                             171.3600006, 160.1399994, 162.1799927, 176.4600067, 180.5399933,
+                             206.0399933, 189.7200012, 170.3399963, 175.4400024, 175.4400024,
+                             185.6399994, 187.6799927, 168.3000031, 176.4600067, 177.4799957,
+                             185.6399994, 167.2799988, 178.5, 182.5800018, 179.5200043,
+                             173.3999939, 185.6399994, 196.8600006, 183.6000061, 162.1799927,
+                             176.4600067, 189.7200012, 208.0800018, 177.4799957, 163.1999969,
+                             187.6799927, 196.8600006, 180.5399933, 188.6999969, 163.1999969,
+                             187.6799927, 168.3000031, 182.5800018, 181.5599976, 174.4199982,
+                             181.5599976, 161.1600037, 163.1999969, 184.6199951, 190.7400055,
+                             181.5599976, 185.6399994, 186.6600037, 173.3999939, 172.3800049,
+                             179.5200043, 187.6799927, 191.7599945, 190.7400055, 183.6000061,
+                             166.2599945, 196.8600006, 172.3800049, 174.4199982, 181.5599976,
+                             177.4799957, 176.4600067, 188.6999969, 184.6199951, 169.3200073,
+                             178.5, 186.6600037, 174.4199982, 185.6399994, 201.9600067,
+                             171.3600006, 177.4799957, 183.6000061, 165.2400055, 189.7200012,
+                             188.6999969, 178.5, 163.1999969, 169.3200073, 178.5,
+                             182.5800018, 173.3999939, 177.4799957, 165.2400055, 163.1999969,
+                             175.4400024, 184.6199951, 189.7200012, 186.6600037, 188.6999969,
+                             163.1999969, 158.1000061, 172.3800049, 186.6600037, 173.3999939,
+                             157.0800018, 158.1000061, 172.3800049, 197.8800049, 171.3600006,
+                             172.3800049, 184.6199951, 173.3999939, 174.4199982, 175.4400024,
+                             166.2599945, 166.2599945, 172.3800049, 171.3600006, 181.5599976,
+                             181.5599976, 187.6799927, 180.5399933, 169.3200073, 182.5800018,
+                             178.5, 179.5200043, 184.6199951, 175.4400024, 175.4400024,
+                             158.1000061, 182.5800018, 196.8600006, 167.2799988, 178.5,
+                             174.4199982, 180.5399933, 195.8399963, 183.6000061, 200.9400024,
+                             189.7200012, 186.6600037, 173.3999939, 173.3999939, 180.5399933,
+                             172.3800049, 157.0800018, 163.1999969, 171.3600006, 190.7400055,
+                             196.8600006, 179.5200043, 175.4400024, 169.3200073, 158.1000061,
+                             157.0800018, 180.5399933, 173.3999939, 170.3399963, 175.4400024,
+                             193.8000031, 170.3399963, 164.2200012, 174.4199982, 185.6399994,
+                             178.5, 176.4600067, 176.4600067, 179.5200043, 176.4600067,
+                             171.3600006, 205.0200043, 184.6199951, 180.5399933, 165.2400055,
+                             167.2799988, 162.1799927, 165.2400055, 180.5399933, 169.3200073,
+                             176.4600067, 182.5800018, 182.5800018, 175.4400024, 186.6600037,
+                             182.5800018, 183.6000061, 163.1999969, 161.1600037, 189.7200012,
+                             181.5599976, 187.6799927, 173.3999939, 173.3999939, 177.4799957,
+                             179.5200043, 198.8999939, 177.4799957, 183.6000061, 154.0200043,
+                             188.6999969, 181.5599976, 177.4799957, 174.4199982, 202.9799957,
+                             168.3000031, 164.2200012, 187.6799927, 171.3600006, 189.7200012,
+                             185.6399994, 187.6799927, 157.0800018, 193.8000031, 160.1399994,
+                             166.2599945, 193.8000031, 166.2599945, 168.3000031, 179.5200043,
+                             181.5599976, 172.3800049, 183.6000061, 184.6199951, 180.5399933,
+                             177.4799957, 192.7799988, 171.3600006, 197.8800049, 190.7400055,
+                             182.5800018, 178.5, 189.7200012, 172.3800049, 199.9199982,
+                             183.6000061, 179.5200043, 170.3399963, 179.5200043, 181.5599976,
+                             178.5, 186.6600037, 177.4799957, 160.1399994, 176.4600067,
+                             173.3999939, 168.3000031, 180.5399933, 179.5200043, 175.4400024,
+                             188.6999969, 175.4400024, 178.5, 161.1600037, 181.5599976,
+                             184.6199951, 169.3200073, 187.6799927, 164.2200012, 176.4600067,
+                             176.4600067, 174.4199982, 189.7200012, 192.7799988, 181.5599976,
+                             165.2400055, 173.3999939, 184.6199951, 164.2200012, 181.5599976,
+                             167.2799988, 157.0800018, 175.4400024, 172.3800049, 172.3800049,
+                             170.3399963, 166.2599945, 185.6399994, 175.4400024, 184.6199951,
+                             179.5200043, 198.8999939, 189.7200012, 164.2200012, 198.8999939,
+                             169.3200073, 183.6000061, 191.7599945, 168.3000031, 178.5,
+                             172.3800049, 169.3200073, 196.8600006, 170.3399963, 192.7799988,
+                             183.6000061, 186.6600037, 181.5599976, 187.6799927, 198.8999939,
+                             167.2799988, 177.4799957, 165.2400055, 173.3999939, 182.5800018,
+                             190.7400055, 167.2799988, 184.6199951, 180.5399933, 165.2400055,
+                             166.2599945, 162.1799927, 175.4400024, 169.3200073, 187.6799927,
+                             155.0399933, 173.3999939, 165.2400055, 174.4199982, 183.6000061,
+                             167.2799988, 186.6600037, 175.4400024, 173.3999939, 177.4799957,
+                             192.7799988, 180.5399933, 191.7599945, 185.6399994, 194.8200073,
+                             201.9600067, 166.2599945, 171.3600006, 177.4799957, 194.8200073,
+                             191.7599945, 177.4799957, 167.2799988, 188.6999969, 172.3800049,
+                             162.1799927, 169.3200073, 198.8999939, 183.6000061, 170.3399963,
+                             190.7400055, 170.3399963, 169.3200073, 185.6399994, 181.5599976,
+                             166.2599945, 187.6799927, 169.3200073, 157.0800018, 165.2400055,
+                             176.4600067, 174.4199982, 166.2599945, 177.4799957, 195.8399963,
+                             187.6799927, 186.6600037, 194.8200073, 181.5599976, 172.3800049,
+                             166.2599945, 168.3000031, 183.6000061, 168.3000031, 174.4199982,
+                             185.6399994, 180.5399933, 181.5599976, 189.7200012, 172.3800049,
+                             183.6000061, 187.6799927, 183.6000061, 200.9400024, 184.6199951,
+                             173.3999939, 176.4600067, 172.3800049, 169.3200073, 166.2599945,
+                             186.6600037, 181.5599976, 161.1600037, 182.5800018, 179.5200043,
+                             178.5, 174.4199982, 170.3399963, 179.5200043, 193.8000031,
+                             188.6999969, 146.8800049, 192.7799988, 171.3600006, 178.5,
+                             177.4799957, 184.6199951, 180.5399933, 163.1999969, 159.1199951,
+                             160.1399994, 178.5, 176.4600067, 176.4600067, 192.7799988,
+                             161.1600037, 166.2599945, 162.1799927, 172.3800049, 175.4400024,
+                             168.3000031, 201.9600067, 188.6999969, 185.6399994, 175.4400024,
+                             175.4400024, 182.5800018, 182.5800018, 172.3800049, 175.4400024,
+                             179.5200043, 184.6199951, 163.1999969, 195.8399963, 180.5399933,
+                             170.3399963, 212.1600037, 166.2599945, 187.6799927, 179.5200043,
+                             178.5, 176.4600067, 172.3800049, 183.6000061, 179.5200043,
+                             176.4600067, 185.6399994, 161.1600037, 187.6799927, 167.2799988,
+                             187.6799927, 199.9199982, 187.6799927, 169.3200073, 158.1000061,
+                             200.9400024, 191.7599945, 179.5200043, 170.3399963, 186.6600037,
+                             170.3399963, 184.6199951, 189.7200012, 197.8800049, 186.6600037,
+                             171.3600006, 164.2200012, 183.6000061, 180.5399933, 165.2400055,
+                             160.1399994, 183.6000061, 166.2599945, 183.6000061, 196.8600006,
+                             175.4400024, 172.3800049, 172.3800049, 181.5599976, 177.4799957,
+                             173.3999939, 176.4600067, 180.5399933, 176.4600067, 178.5,
+                             163.1999969, 189.7200012, 175.4400024, 174.4199982, 185.6399994,
+                             182.5800018, 169.3200073, 194.8200073, 192.7799988, 188.6999969,
+                             193.8000031, 175.4400024, 165.2400055, 180.5399933, 184.6199951,
+                             176.4600067, 171.3600006, 188.6999969, 199.9199982, 183.6000061,
+                             169.3200073, 175.4400024, 180.5399933, 174.4199982, 167.2799988,
+                             184.6199951, 177.4799957, 180.5399933, 180.5399933, 184.6199951,
+                             178.5, 186.6600037, 161.1600037, 183.6000061, 168.3000031,
+                             188.6999969, 184.6199951, 171.3600006, 185.6399994, 167.2799988,
+                             162.1799927, 186.6600037, 175.4400024, 166.2599945, 182.5800018,
+                             171.3600006, 176.4600067, 192.7799988, 178.5, 168.3000031,
+                             182.5800018, 184.6199951, 148.9199982, 172.3800049, 168.3000031,
+                             181.5599976, 154.0200043, 166.2599945, 174.4199982, 166.2599945,
+                             178.5, 184.6199951, 176.4600067, 171.3600006, 188.6999969,
+                             177.4799957, 172.3800049, 176.4600067, 179.5200043, 176.4600067,
+                             161.1600037, 166.2599945, 181.5599976, 170.3399963, 176.4600067,
+                             163.1999969, 183.6000061, 206.0399933, 171.3600006, 186.6600037,
+                             176.4600067, 163.1999969, 179.5200043, 176.4600067, 182.5800018,
+                             167.2799988, 174.4199982, 188.6999969, 177.4799957, 190.7400055,
+                             176.4600067, 175.4400024, 174.4199982, 179.5200043, 182.5800018,
+                             182.5800018, 175.4400024, 165.2400055, 180.5399933, 178.5,
+                             176.4600067, 191.7599945, 173.3999939, 186.6600037, 165.2400055,
+                             173.3999939, 188.6999969, 172.3800049, 168.3000031, 193.8000031,
+                             180.5399933, 172.3800049, 180.5399933, 189.7200012, 170.3399963,
+                             170.3399963, 195.8399963, 173.3999939, 187.6799927, 165.2400055,
+                             147.8999939, 173.3999939, 184.6199951, 171.3600006, 178.5,
+                             163.1999969, 171.3600006, 190.7400055, 180.5399933, 156.0599976,
+                             164.2200012, 172.3800049, 202.9799957, 201.9600067, 175.4400024,
+                             193.8000031, 176.4600067, 182.5800018, 184.6199951, 178.5,
+                             169.3200073, 166.2599945, 173.3999939, 182.5800018, 171.3600006,
+                             167.2799988, 185.6399994, 179.5200043, 188.6999969, 194.8200073,
+                             177.4799957, 177.4799957, 164.2200012, 189.7200012, 193.8000031,
+                             173.3999939, 178.5, 184.6199951, 191.7599945, 191.7599945,
+                             162.1799927, 171.3600006, 165.2400055, 197.8800049, 169.3200073,
+                             178.5, 174.4199982, 183.6000061, 189.7200012, 181.5599976,
+                             185.6399994, 179.5200043, 181.5599976, 161.1600037, 173.3999939,
+                             187.6799927, 177.4799957, 182.5800018, 171.3600006, 188.6999969,
+                             194.8200073, 176.4600067, 182.5800018, 157.0800018, 169.3200073,
+                             170.3399963, 168.3000031, 190.7400055, 170.3399963, 185.6399994,
+                             195.8399963, 172.3800049, 213.1799927, 187.6799927, 187.6799927,
+                             178.5, 181.5599976, 183.6000061, 163.1999969, 169.3200073,
+                             180.5399933, 204, 175.4400024, 158.1000061, 174.4199982,
+                             183.6000061, 181.5599976, 181.5599976, 180.5399933, 172.3800049,
+                             188.6999969, 166.2599945, 196.8600006, 187.6799927, 196.8600006,
+                             164.2200012, 160.1399994, 165.2400055, 172.3800049, 177.4799957,
+                             173.3999939, 168.3000031, 181.5599976, 164.2200012, 177.4799957,
+                             182.5800018, 162.1799927, 168.3000031, 181.5599976, 179.5200043,
+                             171.3600006, 173.3999939, 176.4600067, 197.8800049, 172.3800049,
+                             189.7200012, 172.3800049, 161.1600037, 183.6000061, 173.3999939,
+                             188.6999969, 161.1600037, 180.5399933, 160.1399994, 166.2599945,
+                             164.2200012, 182.5800018, 169.3200073, 176.4600067, 186.6600037,
+                             192.7799988, 163.1999969, 153, 182.5800018, 184.6199951,
+                             166.2599945, 167.2799988, 186.6600037, 177.4799957, 174.4199982,
+                             175.4400024, 164.2200012, 180.5399933, 178.5, 176.4600067,
+                             164.2200012, 160.1399994, 178.5, 176.4600067, 180.5399933,
+                             182.5800018, 179.5200043, 169.3200073, 178.5, 172.3800049,
+                             171.3600006, 178.5, 166.2599945, 171.3600006, 181.5599976,
+                             162.1799927, 157.0800018, 189.7200012, 177.4799957, 164.2200012,
+                             179.5200043, 187.6799927, 172.3800049, 158.1000061, 164.2200012,
+                             177.4799957, 165.2400055, 185.6399994, 174.4199982, 188.6999969,
+                             173.3999939, 168.3000031, 164.2200012, 187.6799927, 209.1000061,
+                             172.3800049, 177.4799957, 170.3399963, 172.3800049, 164.2200012,
+                             190.7400055, 177.4799957, 165.2400055, 168.3000031, 173.3999939,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                         };
+
+int main (void)
+{
+    plan_tests(21);
+
+//    diag("psStats Tests with sample SDSS data from RHL and Megacam from EAM");
+//    diag("this file does not yet define a specific test");
+//    diag("the fitted mean is currently wrong for these two data sets");
+
+    {
+        psMemId id = psMemGetId();
+
+//        diag("sample 1 : problem with integer-binned data driven to tiny sigma values");
+        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV |
+                                       PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV |
+                                       PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV |
+                                       PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN |
+                                       PS_STAT_SAMPLE_STDEV | PS_STAT_USE_BINSIZE);
+        stats->binsize = 1.0;
+
+
+        // copy data in static array
+        int nPts = sizeof(yraw_01) / sizeof (float);
+        psVector *y = psVectorAlloc (nPts, PS_TYPE_F32);
+        for (int i = 0; i < y->n; i++) {
+            y->data.F32[i] = yraw_01[i];
+        }
+
+        psVectorStats (stats, y, NULL, NULL, 1);
+        ok (1, "sample  mean    %f, stdev %f", stats->sampleMean,   stats->sampleStdev);
+        ok (1, "sample  median  %f, stdev %f", stats->sampleMedian, stats->sampleStdev);
+        ok (1, "clipped mean    %f, stdev %f", stats->clippedMean,  stats->clippedStdev);
+        ok (1, "robust  median  %f, stdev %f", stats->robustMedian, stats->robustStdev);
+        ok (1, "fitted  mean    %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
+        psFree (stats);
+
+        stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2 | PS_STAT_USE_BINSIZE);
+        stats->binsize = 1.0;
+        psVectorStats (stats, y, NULL, NULL, 1);
+        ok (1, "fitted  mean v2 %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
+        psFree (stats);
+
+        psFree (y);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+
+//        diag("sample 2");
+        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV |
+                                       PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV |
+                                       PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV |
+                                       PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN |
+                                       PS_STAT_SAMPLE_STDEV);
+
+        // copy data in static array
+        int nPts = sizeof(yraw_02) / sizeof (float);
+        psVector *y = psVectorAlloc (nPts, PS_TYPE_F32);
+        for (int i = 0; i < y->n; i++) {
+            y->data.F32[i] = yraw_02[i];
+        }
+
+        psVectorStats (stats, y, NULL, NULL, 1);
+        ok (1, "sample  mean   %f, stdev %f", stats->sampleMean,   stats->sampleStdev);
+        ok (1, "sample  median %f, stdev %f", stats->sampleMedian, stats->sampleStdev);
+        ok (1, "clipped mean   %f, stdev %f", stats->clippedMean,  stats->clippedStdev);
+        ok (1, "robust  median %f, stdev %f", stats->robustMedian, stats->robustStdev);
+        ok (1, "fitted  mean   %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
+        psFree (stats);
+
+        stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2 | PS_STAT_USE_BINSIZE);
+        stats->binsize = 1.0;
+        psVectorStats (stats, y, NULL, NULL, 1);
+        ok (1, "fitted  mean v2 %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
+        psFree (stats);
+
+        psFree (y);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psMemId id = psMemGetId();
+
+//        diag("sample 3");
+        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV |
+                                       PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV |
+                                       PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV |
+                                       PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN |
+                                       PS_STAT_SAMPLE_STDEV | PS_STAT_USE_BINSIZE);
+        stats->binsize = 1.0;
+
+
+        // copy data in static array
+        int nPts = sizeof(yraw_03) / sizeof (float);
+        psVector *y = psVectorAlloc (nPts, PS_TYPE_F32);
+        for (int i = 0; i < y->n; i++) {
+            y->data.F32[i] = yraw_03[i];
+        }
+
+        psVectorStats (stats, y, NULL, NULL, 1);
+        ok (1, "sample  mean   %f, stdev %f", stats->sampleMean,   stats->sampleStdev);
+        ok (1, "sample  median %f, stdev %f", stats->sampleMedian, stats->sampleStdev);
+        ok (1, "clipped mean   %f, stdev %f", stats->clippedMean,  stats->clippedStdev);
+        ok (1, "robust  median %f, stdev %f", stats->robustMedian, stats->robustStdev);
+        ok (1, "fitted  mean   %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
+        psFree (stats);
+
+        stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2 | PS_STAT_USE_BINSIZE);
+        stats->binsize = 1.0;
+        psVectorStats (stats, y, NULL, NULL, 1);
+        ok (1, "fitted  mean v2 %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
+        psFree (stats);
+
+        psFree (y);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psFunc01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psFunc01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psFunc01.c	(revision 22322)
@@ -0,0 +1,77 @@
+/*****************************************************************************
+    This routine must ensure that the psGaussian() shall evaluate a
+    specified Gaussian at some X.
+ 
+    It also tests the p_psGaussianDev() procedure.
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psMemory.h"
+#include "psPolynomial.h"
+#define MY_MEAN 30.0
+#define MY_STDEV 2.0
+#define N 30
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    psS32 testStatus = true;
+    float x = 0.0;
+    psS32  memLeaks;
+    psS32  currentId = psMemGetId();
+    psVector *myGaussData = NULL;
+    printPositiveTestHeader(stdout,
+                            "psPolynomial functions",
+                            "psGaussian()");
+
+
+    for (x = 0.0 ; x < (MY_MEAN * 2.0) ; x+= 1.0) {
+        printf("normal psGaussian(%f) is %f\n", x, psGaussian(x, MY_MEAN, MY_STDEV, true));
+        x = x + 1.0;
+    }
+
+    for (x = 0.0 ; x < (MY_MEAN * 2.0) ; x+= 1.0) {
+        printf("NON-normal psGaussian(%f) is %f\n", x, psGaussian(x, MY_MEAN, MY_STDEV, false));
+        x = x + 1.0;
+    }
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    printFooter(stdout,
+                "psPolynomial functions",
+                "psGaussian()",
+                testStatus);
+
+
+    printPositiveTestHeader(stdout,
+                            "psPolynomial functions",
+                            "p_psGaussianDev()");
+
+    myGaussData = p_psGaussianDev(MY_MEAN, MY_STDEV, N);
+    for (psS32 i = 0; i < N ; i++) {
+        printf("Gaussian Deviate [%d] is %f\n", i, myGaussData->data.F32[i]);
+    }
+
+    if ( myGaussData->type.type != PS_TYPE_F32) {
+        psAbort("p_psGaussianDev did not return a vector of type F32");
+    }
+
+    psFree(myGaussData);
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    printFooter(stdout,
+                "psPolynomial functions",
+                "p_psGaussianDev()",
+                testStatus);
+
+    return (!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist00.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist00.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist00.c	(revision 22322)
@@ -0,0 +1,135 @@
+/*****************************************************************************
+    This routine must ensure that the psHistogram structure is correctly
+    allocated and deallocated by the procedure psHistogramAlloc().
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psMemory.h"
+#define LOWER 20.0
+#define UPPER 30.0
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("p_psVectorBinDisect", 0);
+    psTraceSetLevel("psHistogramAlloc", 0);
+    psTraceSetLevel("psHistogramAllocGeneric", 0);
+    psTraceSetLevel("UpdateHistogramBins", 0);
+    psTraceSetLevel("psVectorHistogram", 0);
+    psHistogram *myHist = NULL;
+    psS32 testStatus      = true;
+    psS32 memLeaks        = 0;
+    psS32 i               = 0;
+    psS32 nb              = 0;
+    psS32 numBins         = 0;
+    psS32 currentId       = 0;
+
+    currentId       = psMemGetId();
+    for (nb=0;nb<4;nb++) {
+        if (nb == 0)
+            numBins = 1;
+        if (nb == 1)
+            numBins = 2;
+        if (nb == 2)
+            numBins = 10;
+        if (nb == 3)
+            numBins = 20;
+        /*********************************************************************/
+        /*  Allocate and initialize data structures                          */
+        /*********************************************************************/
+        printPositiveTestHeader(stdout,
+                                "psStats functions",
+                                "Allocate the psHistogram structure.");
+
+        myHist = psHistogramAlloc(LOWER, UPPER, numBins);
+
+        if (myHist->nums->n != numBins) {
+            printf("ERROR: myHist->nums->n is wrong size (%ld)\n", myHist->nums->n);
+            testStatus = false;
+        }
+
+        if (myHist->bounds->n != numBins+1) {
+            printf("ERROR: myHist->bounds->n is wrong size (%ld)\n", myHist->bounds->n);
+            testStatus = false;
+        }
+
+        for (i=0;i<numBins;i++) {
+            if (myHist->nums->data.F32[i] != 0.0) {
+                printf("ERROR: myHist->nums->data.U32[%d] not initialized to 0.\n", i);
+                testStatus = false;
+            }
+            myHist->nums->data.F32[i] = 0.0;
+        }
+
+        if (myHist->minNum != 0) {
+            printf("ERROR: myHist->minNum is %d\n", myHist->minNum);
+            testStatus = false;
+        }
+
+        if (myHist->maxNum != 0) {
+            printf("myHist->maxNum is %d\n", myHist->maxNum);
+            testStatus = false;
+        }
+
+        if (myHist->uniform != true) {
+            printf("ERROR: myHist->uniform is %d\n", myHist->uniform);
+            testStatus = false;
+        }
+
+        for (i=0;i<numBins;i++) {
+            printf("Bin number %d bounds: (%6.3f - %6.3f)\n", i,
+                   myHist->bounds->data.F32[i],
+                   myHist->bounds->data.F32[i+1]);
+        }
+
+        psMemCheckCorruption(1);
+        psFree(myHist);
+        psMemCheckCorruption(1);
+
+        printFooter(stdout,
+                    "psStats functions",
+                    "Allocate the psHistogram structure.",
+                    testStatus);
+    }
+
+
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "Allocate the psHistogram structure. (UPPER<LOWER)");
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
+    myHist = psHistogramAlloc(UPPER, LOWER, numBins);
+    if (myHist != NULL) {
+        printf("ERROR: myHist != NULL\n");
+    }
+    printFooter(stdout,
+                "psStats functions",
+                "Allocate the psHistogram structure. (UPPER<LOWER)",
+                testStatus);
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                   */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "Deallocate the psHistogram structure.");
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    psMemCheckCorruption(1);
+
+    printFooter(stdout,
+                "psStats functions",
+                "Deallocate the psHistogram structure.",
+                testStatus);
+
+    return (!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist01.c	(revision 22322)
@@ -0,0 +1,124 @@
+/*****************************************************************************
+    This routine must ensure that the psHistogram structure is correctly
+    allocated and deallocated by the procedure psHistogramAllocGeneric().
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psMemory.h"
+#define LOWER 20.0
+#define UPPER 30.0
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("p_psVectorBinDisect", 0);
+    psTraceSetLevel("psHistogramAlloc", 0);
+    psTraceSetLevel("psHistogramAllocGeneric", 0);
+    psTraceSetLevel("UpdateHistogramBins", 0);
+    psTraceSetLevel("psVectorHistogram", 0);
+    psHistogram *myHist = NULL;
+    psVector *myBounds  = NULL;
+    psS32 testStatus      = true;
+    psS32 memLeaks        = 0;
+    psS32 i               = 0;
+    psS32 nb              = 0;
+    psS32 numBins         = 0;
+    psS32 currentId       = 0;
+
+    currentId       = psMemGetId();
+    for (nb=0;nb<4;nb++) {
+        if (nb == 0)
+            numBins = 1;
+        if (nb == 1)
+            numBins = 2;
+        if (nb == 2)
+            numBins = 10;
+        if (nb == 3)
+            numBins = 20;
+        /*********************************************************************/
+        /*  Allocate and initialize data structures                          */
+        /*********************************************************************/
+        printPositiveTestHeader(stdout,
+                                "psStats functions",
+                                "Allocate the psHistogram structure.");
+        myBounds = psVectorAlloc(numBins+1, PS_TYPE_F32);
+        myBounds->n = myBounds->nalloc;
+        for (i=0;i<numBins+1;i++) {
+            myBounds->data.F32[i] = LOWER + ((UPPER - LOWER) / (float) numBins) *
+                                    (float) i;
+        }
+        myHist = psHistogramAllocGeneric(myBounds);
+
+        if (myHist->nums->n != numBins) {
+            printf("ERROR: myHist->nums->n is wrong size (%ld)\n", myHist->nums->n);
+            testStatus = false;
+        }
+
+        if (myHist->bounds->n != numBins+1) {
+            printf("ERROR: myHist->bounds->n is wrong size (%ld)\n", myHist->bounds->n);
+            testStatus = false;
+        }
+
+        for (i=0;i<numBins;i++) {
+            if (myHist->nums->data.F32[i] != 0.0) {
+                printf("ERROR: myHist->nums->data.F32[%d] not initialized to 0.\n", i);
+                testStatus = false;
+            }
+            myHist->nums->data.F32[i] = 0.0;
+        }
+
+        if (myHist->minNum != 0) {
+            printf("ERROR: myHist->minNum is %d\n", myHist->minNum);
+            testStatus = false;
+        }
+
+        if (myHist->maxNum != 0) {
+            printf("myHist->maxNum is %d\n", myHist->maxNum);
+            testStatus = false;
+        }
+
+        if (myHist->uniform != false) {
+            printf("ERROR: myHist->uniform is %d\n", myHist->uniform);
+            testStatus = false;
+        }
+
+        for (i=0;i<numBins;i++) {
+            printf("Bin number %d bounds: (%6.3f - %6.3f)\n", i,
+                   myHist->bounds->data.F32[i],
+                   myHist->bounds->data.F32[i+1]);
+        }
+        psMemCheckCorruption(1);
+        psFree(myHist);
+        psMemCheckCorruption(1);
+        psFree(myBounds);
+
+        printFooter(stdout,
+                    "psStats functions",
+                    "Allocate the psHistogram structure.",
+                    testStatus);
+    }
+    /*************************************************************************/
+    /*  Deallocate data structures                                           */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "Deallocate the psHistogram structure.");
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    psMemCheckCorruption(1);
+
+    printFooter(stdout,
+                "psStats functions",
+                "Deallocate the psHistogram structure.",
+                testStatus);
+
+    return (!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist02.c	(revision 22322)
@@ -0,0 +1,191 @@
+/*****************************************************************************
+   This routine must ensure that the psHistogram structure is correctly
+   populated by the procedure psGetArrayHistogram().
+ 
+*****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psMemory.h"
+#define MISC_FLOAT_NUMBER 345.0
+#define MISC_INT_NUMBER 345
+#define LOWER 20.0
+#define UPPER 30.0
+#define NUM_DATA 10000
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    psHistogram * myHist = NULL;
+    psHistogram *myHist2 = NULL;
+    psVector *myData = NULL;
+    psVector *myMask = NULL;
+    psS32 testStatus = true;
+    psS32 memLeaks = 0;
+    psS32 nb = 0;
+    psS32 numBins = 0;
+    psS32 i = 0;
+    psS32 currentId = 0;
+
+    currentId = psMemGetId();
+
+    /*********************************************************************/
+    /*  Allocate and initialize data structures                          */
+    /*********************************************************************/
+    myData = psVectorAlloc( NUM_DATA, PS_TYPE_F32 );
+    myData->n = myData->nalloc;
+    for ( i = 0;i < NUM_DATA;i++ ) {
+        myData->data.F32[ i ] = LOWER + ( ( UPPER - LOWER ) / ( float ) NUM_DATA ) * ( float ) i;
+    }
+
+    myMask = psVectorAlloc( NUM_DATA, PS_TYPE_U8 );
+    myMask->n = myMask->nalloc;
+    for ( i = 0;i < NUM_DATA;i++ ) {
+        if ( i >= ( NUM_DATA / 2 ) ) {
+            myMask->data.U8[ i ] = 1;
+        } else {
+            myMask->data.U8[ i ] = 0;
+        }
+    }
+
+    for ( nb = 0;nb < 4;nb++ ) {
+        if ( nb == 0 )
+            numBins = 1;
+        if ( nb == 1 )
+            numBins = 2;
+        if ( nb == 2 )
+            numBins = 10;
+        if ( nb == 3 )
+            numBins = 20;
+
+        /*********************************************************************/
+        /*  Allocate and Perform Histogram, no mask                          */
+        /*********************************************************************/
+        printPositiveTestHeader( stdout,
+                                 "psStats functions",
+                                 "Allocate and Perform Histogram, no mask" );
+
+        myHist = psHistogramAlloc( LOWER, UPPER, numBins );
+        myHist = psVectorHistogram( myHist, myData, NULL, NULL, 0 );
+
+        for ( i = 0;i < numBins;i++ ) {
+            printf( "Bin number %d bounds: (%.2f - %.2f) data (%f)\n", i,
+                    myHist->bounds->data.F32[ i ],
+                    myHist->bounds->data.F32[ i + 1 ],
+                    myHist->nums->data.F32[ i ] );
+        }
+        psMemCheckCorruption( 1 );
+        psFree( myHist );
+        psMemCheckCorruption( 1 );
+
+        printFooter( stdout,
+                     "psStats functions",
+                     "Allocate and Perform Histogram, no mask",
+                     testStatus );
+
+        /*********************************************************************/
+        /*  Allocate and Perform Histogram with mask                         */
+        /*********************************************************************/
+        printPositiveTestHeader( stdout,
+                                 "psStats functions",
+                                 "Allocate and Perform Histogram with mask" );
+
+        myHist = psHistogramAlloc( LOWER, UPPER, numBins );
+        myHist = psVectorHistogram( myHist, myData, NULL, myMask, 1 );
+
+        for ( i = 0;i < numBins;i++ ) {
+            printf( "Bin number %d bounds: (%6.3f - %6.3f) data (%f)\n", i,
+                    myHist->bounds->data.F32[ i ],
+                    myHist->bounds->data.F32[ i + 1 ],
+                    myHist->nums->data.F32[ i ] );
+        }
+        psMemCheckCorruption( 1 );
+        psFree( myHist );
+        psMemCheckCorruption( 1 );
+
+        printFooter( stdout,
+                     "psStats functions",
+                     "Allocate and Perform Histogram with mask",
+                     testStatus );
+    }
+    psFree( myMask );
+
+    printPositiveTestHeader( stdout,
+                             "psStats functions",
+                             "Calling psVectorHistogram() with various NULL inputs." );
+
+
+
+    // ********************************************************************
+    // Verify the return value is null and program execution doesn't stop,
+    // if input parameter myHist is null.
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
+    myHist2 = psVectorHistogram( NULL, myData, NULL, NULL, 0 );
+    if ( myHist2 != NULL ) {
+        printf( "ERROR: myHist2!=NULL\n" );
+        testStatus = false;
+    }
+    psFree( myData );
+
+
+    // ********************************************************************
+    // Verify the return value is the same as the input parameter myHist and
+    // program execution doesn't stop, if the input parameter myArray is
+    // null.
+
+    myHist = psHistogramAlloc( LOWER, UPPER, numBins );
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
+    myHist = psVectorHistogram( myHist, NULL, NULL, NULL, 0 );
+    if ( myHist == NULL ) {
+        printf( "ERROR: myHist==NULL\n" );
+        testStatus = false;
+    }
+    psFree( myHist );
+
+
+    //    exit(0);
+    // ********************************************************************
+    // Verify the return value is the same as the input parameter myHist and
+    // program execution doesn't stop, if the input parameter myArray has no
+    // elements.
+    // NOTE: This code segment is commented out because psVectorAlloc returns
+    // NULL if called with an N element data.
+    /*
+    myData = psVectorAlloc(0, PS_TYPE_F32);
+    myData->n = myData->nalloc;
+    myHist = psHistogramAlloc(LOWER, UPPER, numBins);
+    myHist = psVectorHistogram(myHist, NULL, NULL, NULL, 0);
+    if (myHist == NULL) {
+        printf("ERROR: myHist==NULL\n");
+        testStatus = false;
+    }
+    psFree(myHist);
+    psFree(myData);
+    */
+    printFooter( stdout,
+                 "psStats functions",
+                 "Calling psVectorHistogram() with various NULL inputs.",
+                 testStatus );
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                   */
+    /*************************************************************************/
+    printPositiveTestHeader( stdout,
+                             "psStats functions",
+                             "Deallocate the psHistogram structure." );
+
+    psMemCheckCorruption( 1 );
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if ( 0 != memLeaks ) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks );
+    }
+    psMemCheckCorruption( 1 );
+
+    printFooter( stdout,
+                 "psStats functions",
+                 "Deallocate the psHistogram structure.",
+                 testStatus );
+
+    return ( !testStatus );
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psHist03.c	(revision 22322)
@@ -0,0 +1,138 @@
+/*****************************************************************************
+    This routine must ensure that the psHistogram structure is correctly
+    allocated and deallocated by the procedure psHistogramAllocGeneric() and
+    that it can be populated correctly by psVectorHistogram().
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psMemory.h"
+#define LOWER 20.0
+#define UPPER 30.0
+#define NUM_DATA 20
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("p_psVectorBinDisect", 0);
+    psTraceSetLevel("psHistogramAlloc", 0);
+    psTraceSetLevel("psHistogramAllocGeneric", 0);
+    psTraceSetLevel("UpdateHistogramBins", 0);
+    psTraceSetLevel("psVectorHistogram", 0);
+    psHistogram *myHist = NULL;
+    psVector *myBounds  = NULL;
+    psVector *myData = psVectorAlloc( NUM_DATA, PS_TYPE_F32 );
+    psS32 testStatus      = true;
+    psS32 memLeaks        = 0;
+    psS32 i               = 0;
+    psS32 nb              = 0;
+    psS32 numBins         = 0;
+    psS32 currentId       = 0;
+
+    currentId       = psMemGetId();
+
+    psTraceSetLevel(".psLib", 0);
+
+    myData->n = myData->nalloc;
+    for ( i = 0;i < NUM_DATA;i++ ) {
+        myData->data.F32[ i ] = LOWER + ( ( UPPER - LOWER ) / ( float ) NUM_DATA ) * ( float ) i;
+    }
+
+    for (nb=0;nb<4;nb++) {
+        if (nb == 0)
+            numBins = 1;
+        if (nb == 1)
+            numBins = 2;
+        if (nb == 2)
+            numBins = 10;
+        if (nb == 3)
+            numBins = 20;
+        /*********************************************************************/
+        /*  Allocate and initialize data structures                          */
+        /*********************************************************************/
+        printPositiveTestHeader(stdout,
+                                "psStats functions",
+                                "Allocate the psHistogram structure.");
+        myBounds = psVectorAlloc(numBins+1, PS_TYPE_F32);
+        myBounds->n = myBounds->nalloc;
+        for (i=0;i<numBins+1;i++) {
+            myBounds->data.F32[i] = LOWER + ((UPPER - LOWER) / (float) numBins) *
+                                    (float) i;
+        }
+        myHist = psHistogramAllocGeneric(myBounds);
+
+        if (myHist->nums->n != numBins) {
+            printf("ERROR: myHist->nums->n is wrong size (%ld)\n", myHist->nums->n);
+            testStatus = false;
+        }
+
+        if (myHist->bounds->n != numBins+1) {
+            printf("ERROR: myHist->bounds->n is wrong size (%ld)\n", myHist->bounds->n);
+            testStatus = false;
+        }
+
+        for (i=0;i<numBins;i++) {
+            if (myHist->nums->data.F32[i] != 0.0) {
+                printf("ERROR: myHist->nums->data.F32[%d] not initialized to 0.\n", i);
+                testStatus = false;
+            }
+            myHist->nums->data.F32[i] = 0.0;
+        }
+
+        if (myHist->minNum != 0) {
+            printf("ERROR: myHist->minNum is %d\n", myHist->minNum);
+            testStatus = false;
+        }
+
+        if (myHist->maxNum != 0) {
+            printf("myHist->maxNum is %d\n", myHist->maxNum);
+            testStatus = false;
+        }
+
+        if (myHist->uniform != false) {
+            printf("ERROR: myHist->uniform is %d\n", myHist->uniform);
+            testStatus = false;
+        }
+
+        myHist = psVectorHistogram( myHist, myData, NULL, NULL, 0 );
+        for (i=0;i<numBins;i++) {
+            printf("Bin number %d bounds: (%6.3f - %6.3f): data: (%f)\n", i,
+                   myHist->bounds->data.F32[i],
+                   myHist->bounds->data.F32[i+1],
+                   myHist->nums->data.F32[i]);
+        }
+
+        psMemCheckCorruption(1);
+        psFree(myHist);
+        psMemCheckCorruption(1);
+        psFree(myBounds);
+
+        printFooter(stdout,
+                    "psStats functions",
+                    "Allocate the psHistogram structure.",
+                    testStatus);
+    }
+    /*************************************************************************/
+    /*  Deallocate data structures                                           */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "Deallocate the psHistogram structure.");
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,NULL,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    psMemCheckCorruption(1);
+
+    printFooter(stdout,
+                "psStats functions",
+                "Deallocate the psHistogram structure.",
+                testStatus);
+
+    return (!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMathUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMathUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMathUtils.c	(revision 22322)
@@ -0,0 +1,214 @@
+/*****************************************************************************
+This routine contains code which tests the general math utility functions.
+ *****************************************************************************/
+#include <stdio.h>
+#include <math.h>
+#include "pslib.h"
+#include "psTest.h"
+#define NUM_DATA 10
+#define VERBOSE 0
+#define TS00_X_F32  0x00000001
+#define TS00_X_F64  0x00000002
+#define TS00_X_NULL  0x00000004
+#define TS00_DOMAIN_F32  0x00000008
+#define TS00_DOMAIN_F64  0x00000010
+#define TS00_DOMAIN_NULL 0x00000020
+#define TS00_RANGE_F32  0x00000040
+#define TS00_RANGE_F64  0x00000080
+#define TS00_RANGE_NULL  0x00000200
+
+psS32 genericInterpolateTest(
+    psU32 flags,
+    psS32 order,
+    psS32 numData,
+    psF64 xValue,
+    psBool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    psS32 testStatus = true;
+    psS32 memLeaks = 0;
+    psScalar *x = NULL;
+    psVector *domain = NULL;
+    psVector *range = NULL;
+    psScalar *out = NULL;
+
+    printPositiveTestHeader(stdout, "psMathUtils functions", "1D Interpolation routines");
+
+    if (expectedRC == false) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (flags & TS00_X_NULL) {
+        printf(" using a NULL x scalar\n");
+    }
+
+    if (flags & TS00_X_F32) {
+        printf(" using a psF32 x scalar\n");
+        x = psScalarAlloc((psF32) xValue, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_X_F64) {
+        printf(" using a psF64 x scalar\n");
+        x = psScalarAlloc((psF64) xValue, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_DOMAIN_NULL) {
+        printf(" using a NULL domain vector\n");
+    }
+
+    if (flags & TS00_DOMAIN_F32) {
+        printf(" using a psF32 domain vector\n");
+        domain = psVectorAlloc(numData, PS_TYPE_F32);
+        for (psS32 i=0;i<numData;i++) {
+            domain->data.F32[i] = (psF32) i;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original domain data %d: (%.1f)\n", i, domain->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_DOMAIN_F64) {
+        printf(" using a psF64 domain vector\n");
+        domain = psVectorAlloc(numData, PS_TYPE_F64);
+        for (psS64 i=0;i<numData;i++) {
+            domain->data.F64[i] = (psF64) i;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original domain data %d: (%.1f)\n", i, domain->data.F64[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_RANGE_NULL) {
+        printf(" using a NULL range vector\n");
+    }
+
+    if (flags & TS00_RANGE_F32) {
+        printf(" using a psF32 range vector\n");
+        range = psVectorAlloc(numData, PS_TYPE_F32);
+        for (psS32 i=0;i<numData;i++) {
+            range->data.F32[i] = (psF32) 2*i;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original range data %d: (%.1f)\n", i, range->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_RANGE_F64) {
+        printf(" using a psF64 range vector\n");
+        range = psVectorAlloc(numData, PS_TYPE_F64);
+        for (psS64 i=0;i<numData;i++) {
+            range->data.F64[i] = (psF64) 2*i;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original range data %d: (%.1f)\n", i, range->data.F64[i]);
+            }
+        }
+    }
+
+
+    out = p_psVectorInterpolate(NULL, domain, range, order, x);
+
+    if (out == NULL) {
+        if (expectedRC == true) {
+            printf("TEST ERROR: the p_psVectorInterpolate function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            printf("TEST ERROR: the p_psVectorInterpolate function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (flags & TS00_X_F32) {
+            printf("The interpolated value was %.2f: should be %.2f\n", out->data.F32, 2.0 * x->data.F32);
+        } else if (flags & TS00_X_F64) {
+            printf("The interpolated value was %.2f: should be %.2f\n", out->data.F64, 2.0 * x->data.F64);
+        }
+
+        /*
+        if (0) {
+                if (flags & TS00_F_F32) {
+                    expectData = f->data.F32[i];
+                } else if (flags & TS00_F_F64) {
+                    expectData = (psF32) f->data.F64[i];
+                } else if (flags & TS00_F_S32) {
+                    expectData = (psF32) f->data.S32[i];
+                }
+         
+                    if (flags & TS00_X_F32) {
+                        xData = x->data.F32[i];
+                    } else if (flags & TS00_X_F64) {
+                        xData = (psF32) x->data.F64[i];
+                    } else if (flags & TS00_X_S32) {
+                        xData = (psF32) x->data.S32[i];
+                    } else if (flags & TS00_X_NULL) {
+                        if (flags & TS00_POLY_ORD) {
+                            xData = (psF32) i;
+                        } else if (flags & TS00_POLY_CHEB) {
+                            xData = ((2.0 / ((psF32) (numData - 1))) * ((psF32) i)) - 1.0;
+                        }
+                    }
+         
+                    psF32 actualData = psPolynomial1DEval(myPoly, xData);
+         
+                    if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
+                        printf("TEST ERROR: Fitted data %d: (%.1f %.1f), expected was (%.1f)\n",
+                               i, xData, actualData, expectData);
+                        testStatus = false;
+                     } else {
+                        if (VERBOSE) {
+                            printf("GOOD: Fitted data %d: (%.1f %.1f), expected was (%.1f)\n",
+                                     i, xData, actualData, expectData);
+                        }
+                    }
+                }
+        }
+        */
+    }
+
+    psMemCheckCorruption(1);
+    psFree(x);
+    psFree(out);
+    psFree(domain);
+    psFree(range);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    printFooter(stdout, "psMinimize functions", "1D Interpolation routines", testStatus);
+
+    return (testStatus);
+}
+
+/*****************************************************************************
+We test a variety of 
+ *****************************************************************************/
+psS32 main()
+{
+    psBool testStatus = true;
+    psLogSetFormat("HLNM");
+    psTraceSetLevel(".", 0);
+    printPositiveTestHeader(stdout, "psMathUtils functions: 1D Interpolation routines", "");
+
+    //
+    // F32 tests:
+    //
+    // All Vectors non-NULL
+
+    testStatus &= genericInterpolateTest(TS00_X_F32 | TS00_DOMAIN_F32 | TS00_RANGE_F32, 5, NUM_DATA, 5.5, true);
+
+    printFooter(stdout, "psMinimize functions: 1D Polynomial Fitting Functions", "", testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix01.c	(revision 22322)
@@ -0,0 +1,132 @@
+/** @file  tst_psMatrix_01.c
+*
+*  @brief Test driver for psMatrix transpose function
+*
+*  This test driver contains the following tests for psMatrix test point 1:
+*     A)  Create input and output images
+*     B)  Transpose input image into output image
+*     C)  Transpose input image into auto allocated NULL output image
+*     D)  Free images and check for leaks
+*
+*  @author  Ross Harman, MHPCC
+*
+*  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2005-08-24 01:24:24 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define TOLERANCE 0.000001
+
+#define CHECK_MATRIX(IMAGE)                                                                                  \
+for(psU32 i=0; i<IMAGE->numRows; i++) {                                                                  \
+    for(psU32 j=0; j<IMAGE->numCols; j++) {                                                              \
+        if(IMAGE->type.type == PS_TYPE_F64) {                                                            \
+            if(fabs(IMAGE->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {                              \
+                printf("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,                 \
+                       IMAGE->data.F64[i][j], truthMatrix[i][j]);                                        \
+            }                                                                                            \
+        } else if(IMAGE->type.type == PS_TYPE_F32){                                                      \
+            if(fabs(IMAGE->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {                              \
+                printf("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,                  \
+                       IMAGE->data.F32[i][j], truthMatrix[i][j]);                                        \
+            }                                                                                            \
+        }                                                                                                \
+    }                                                                                                    \
+}
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psImage * tempImage = NULL;
+
+    double truthMatrix[3][3] = {{1, 4, 7},
+                                {2, 5, 8},
+                                {3, 6, 9}};
+
+    // Test A - Create input and output images
+    printPositiveTestHeader(stdout, "psMatrix", "Create input and output images");
+    psImage *inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    psImage *outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    inImage->data.F64[0][0] = 1;
+    inImage->data.F64[0][1] = 2;
+    inImage->data.F64[0][2] = 3;
+    inImage->data.F64[1][0] = 4;
+    inImage->data.F64[1][1] = 5;
+    inImage->data.F64[1][2] = 6;
+    inImage->data.F64[2][0] = 7;
+    inImage->data.F64[2][1] = 8;
+    inImage->data.F64[2][2] = 9;
+    psImage *inImageF32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+    psImage *outImageF32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+    inImageF32->data.F32[0][0] = 1;
+    inImageF32->data.F32[0][1] = 2;
+    inImageF32->data.F32[0][2] = 3;
+    inImageF32->data.F32[1][0] = 4;
+    inImageF32->data.F32[1][1] = 5;
+    inImageF32->data.F32[1][2] = 6;
+    inImageF32->data.F32[2][0] = 7;
+    inImageF32->data.F32[2][1] = 8;
+    inImageF32->data.F32[2][2] = 9;
+    printFooter(stdout, "psMatrix", "Create input and output images", true);
+
+
+    // Test B - Transpose input image into output image
+    printPositiveTestHeader(stdout, "psMatrix", "Transpose input image into output image");
+    tempImage = outImage;
+    outImage = psMatrixTranspose(outImage, inImage);
+    CHECK_MATRIX(outImage);
+    if (outImage->type.dimen != PS_DIMEN_IMAGE) {
+        printf( "Error: Resulting image is not PS_DIMEN_IMAGE\n");
+    } else if (outImage != tempImage) {
+        printf("Error: Return pointer not equal to output argument pointer\n");
+    }
+
+    tempImage = outImageF32;
+    outImageF32 = psMatrixTranspose(outImageF32, inImageF32);
+    CHECK_MATRIX(outImageF32);
+    if (outImageF32->type.dimen != PS_DIMEN_IMAGE) {
+        printf("Error: Resulting image is not PS_DIMEN_IMAGE\n");
+    } else if (outImageF32 != tempImage) {
+        printf("Error: Return pointer not equal to output argument pointer\n");
+    }
+    printFooter(stdout, "psMatrix", "Transpose input image into output image", true);
+
+
+    // Test C - Transpose input image into auto allocated NULL output image
+    printPositiveTestHeader(stdout, "psMatrix", "Transpose input image into auto allocated NULL output image");
+    psImage *outImageNull = NULL;
+    outImageNull = psMatrixTranspose(outImageNull, inImage);
+    CHECK_MATRIX(outImageNull);
+
+    psImage *outImageNullF32 = NULL;
+    outImageNullF32 = psMatrixTranspose(outImageNullF32, inImageF32);
+    CHECK_MATRIX(outImageNullF32);
+    printFooter(stdout, "psMatrix", "Transpose input image into auto allocated NULL output image", true);
+
+
+    // Test D - Free images and check for leaks
+    printPositiveTestHeader(stdout, "psMatrix", "Free images and check for leaks");
+    psFree(inImage);
+    psFree(outImage);
+    psFree(outImageNull);
+    psFree(inImageF32);
+    psFree(outImageF32);
+    psFree(outImageNullF32);
+    psS32 nLeaks = psMemCheckLeaks(0, NULL, stdout, false);
+    if (nLeaks != 0) {
+        printf("ERROR: Found %d memory leaks\n", nLeaks);
+    }
+    psS32 nBad = psMemCheckCorruption(0);
+    if (nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+    }
+    printFooter(stdout, "psMatrix" , "Free images and check for leaks", true);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix02.c	(revision 22322)
@@ -0,0 +1,101 @@
+/** @file  tst_psMatrix_02.c
+ *
+ *  @brief Test driver for negative tests for psMatrix transpose function
+ *
+ *  This test driver contains the following tests for psMatrix test point 2:
+ *     A)  Input pointer same as output pointer
+ *     B)  Null input psImage
+ *     C)  Incorrect type for input pointer
+ *     D)  Incorrect type for output pointer
+ *     E)  Matrix not square for output pointer
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-08-24 01:24:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+psS32 main(psS32 argc,
+           char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psImage *nullImage = NULL;
+    psImage *inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    psImage *outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    psImage *badImage1 = (psImage*)psImageAlloc(3, 3, PS_TYPE_C32);
+    psImage *badImage2 = (psImage*)psImageAlloc(3, 2, PS_TYPE_F64);
+
+    // Test A - Input pointer same as output pointer
+    printPositiveTestHeader(stdout,"psMatrix", "Input pointer same as output pointer");
+    psMemIncrRefCounter(inImage);
+    if (psMatrixTranspose(inImage, inImage) != NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "inImage = outImage didn't results in NULL return");
+        return 1;
+    }
+    if (psMemGetRefCounter(inImage) != 1) {
+        psError(PS_ERR_UNKNOWN, true,
+                "the output image was not freed on an error.");
+        return 2;
+    }
+    printFooter(stdout, "psMatrix", "Input pointer same as output pointer", true);
+
+    // Test B - Null input psImage
+    printPositiveTestHeader(stdout,"psMatrix", "Null input psImage");
+    psMemIncrRefCounter(outImage);
+    if (psMatrixTranspose(outImage, nullImage) != NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "inImage = outImage didn't results in NULL return");
+        return 3;
+    }
+    if (psMemGetRefCounter(outImage) != 1) {
+        psError(PS_ERR_UNKNOWN, true,
+                "the output image was not freed on an error.");
+        return 4;
+    }
+    printFooter(stdout, "psMatrix", "Null input psImage", true);
+
+    // Test C - Incorrect type for input pointer
+    printPositiveTestHeader(stdout,"psMatrix", "Incorrect type for input pointer");
+    psMemIncrRefCounter(outImage);
+    if (psMatrixTranspose(outImage, badImage1) != NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "inImage = outImage didn't results in NULL return");
+        return 5;
+    }
+    if (psMemGetRefCounter(outImage) != 1) {
+        psError(PS_ERR_UNKNOWN, true,
+                "the output image was not freed on an error.");
+        return 6;
+    }
+    printFooter(stdout, "psMatrix", "Incorrect type for input pointer", true);
+
+    // Test D - Incorrect type for output pointer
+    printPositiveTestHeader(stdout,"psMatrix", "Incorrect type for output pointer");
+    badImage1 = psMatrixTranspose(badImage1, inImage);
+    if (badImage1 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "inImage = outImage didn't results in NULL return");
+        return 7;
+    }
+    // check that the type was changed.
+    if (badImage1->type.type != PS_TYPE_F64) {
+        psError(PS_ERR_UNKNOWN, true,
+                "the output image was not freed on an error.");
+        return 8;
+    }
+    printFooter(stdout, "psMatrix", "Incorrect type for output pointer", true);
+
+    // Test E - Matrix not square for output pointer
+    printPositiveTestHeader(stdout,"psMatrix", "Matrix not square for output pointer");
+    psMatrixTranspose(badImage2, inImage);
+    printFooter(stdout, "psMatrix", "Matrix not square for output pointer", true);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix03.c	(revision 22322)
@@ -0,0 +1,235 @@
+/** @file  tst_psMatrix_03.c
+ *
+ *  @brief Test driver for psMatrix LU functions
+ *
+ *  This test driver contains the following tests for psMatrix test point 3:
+ *     A)  Create input and output images and vectors
+ *     B)  Calculate LU matrix
+ *     C)  Determine solution to matrix equation
+ *     D)  Free input and output images and vectors
+ *     E)  Attempt to use null image input argument
+ *     F)  Attempt to use null input vector argument
+ *     G)  ttempt to use null LU image argument
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-08-24 01:24:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define TOLERANCE 0.000001
+
+#define CHECK_MATRIX(IMAGE)                                                                                  \
+for(psU32 i=0; i<IMAGE->numRows; i++) {                                                                  \
+    for(psU32 j=0; j<IMAGE->numCols; j++) {                                                              \
+        if(IMAGE->type.type == PS_TYPE_F64) {                                                            \
+            if(fabs(IMAGE->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {                              \
+                printf("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,                 \
+                       IMAGE->data.F64[i][j], truthMatrix[i][j]);                                        \
+            }                                                                                            \
+        } else if(IMAGE->type.type == PS_TYPE_F32){                                                      \
+            if(fabs(IMAGE->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {                              \
+                printf("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,                  \
+                       IMAGE->data.F32[i][j], truthMatrix[i][j]);                                        \
+            }                                                                                            \
+        }                                                                                                \
+    }                                                                                                    \
+}
+
+#define CHECK_VECTOR(VECTOR)                                                                                 \
+for(psU32 i=0; i<VECTOR->n; i++) {                                                                       \
+    if(VECTOR->type.type == PS_TYPE_F64) {                                                               \
+        if(fabs(VECTOR->data.F64[i]-truthVector[i]) > TOLERANCE) {                                       \
+            printf("Vector values at element %d don't agree %lf vs %lf\n", i,                            \
+                   VECTOR->data.F64[i], truthVector[i]);                                                 \
+        }                                                                                                \
+    } else if(VECTOR->type.type == PS_TYPE_F32){                                                         \
+        if(fabs(VECTOR->data.F32[i]-truthVector[i]) > TOLERANCE) {                                       \
+            printf("Vector values at element %d don't agree %f vs %lf\n", i,                             \
+                   VECTOR->data.F32[i], truthVector[i]);                                                 \
+        }                                                                                                \
+    }                                                                                                    \
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psImage *luImage = NULL;
+    psImage *inImage = NULL;
+    psImage *tempImage = NULL;
+    psVector *tempVector = NULL;
+    psVector *perm = NULL;
+    psVector *outVector = NULL;
+    psVector *inVector = NULL;
+    psImage *luImage32 = NULL;
+    psImage *inImage32 = NULL;
+    psImage *tempImage32 = NULL;
+    psVector *tempVector32 = NULL;
+    psVector *perm32 = NULL;
+    psVector *outVector32 = NULL;
+    psVector *inVector32 = NULL;
+
+    double truthVector[3] = {
+                                4.000000,
+                                -2.000000,
+                                3.000000
+                            };
+
+    double truthMatrix[3][3] = {{4.000000,  5.000000,  6.000000},
+                                {0.750000, -2.750000, -6.500000},
+                                {0.500000, -0.545455, -0.545455}};
+
+
+    // Test A - Create input and output images and vectors
+    printPositiveTestHeader(stdout, "psMatrix", "Create input and output images and vectors");
+    luImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    perm = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+    outVector = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+    inVector = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+    inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    inImage->data.F64[0][0] =  2;
+    inImage->data.F64[0][1] =  4;
+    inImage->data.F64[0][2] =  6;
+    inImage->data.F64[1][0] =  4;
+    inImage->data.F64[1][1] =  5;
+    inImage->data.F64[1][2] =  6;
+    inImage->data.F64[2][0] =  3;
+    inImage->data.F64[2][1] =  1;
+    inImage->data.F64[2][2] = -2;
+    inVector->data.F64[0] = 18.0;
+    inVector->data.F64[1] = 24.0;
+    inVector->data.F64[2] =  4.0;
+    inVector->n = 3;
+    luImage32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+    outVector32 = (psVector*)psVectorAlloc(3, PS_TYPE_F32);
+    inVector32 = (psVector*)psVectorAlloc(3, PS_TYPE_F32);
+    inImage32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+    inImage32->data.F32[0][0] =  2;
+    inImage32->data.F32[0][1] =  4;
+    inImage32->data.F32[0][2] =  6;
+    inImage32->data.F32[1][0] =  4;
+    inImage32->data.F32[1][1] =  5;
+    inImage32->data.F32[1][2] =  6;
+    inImage32->data.F32[2][0] =  3;
+    inImage32->data.F32[2][1] =  1;
+    inImage32->data.F32[2][2] = -2;
+    inVector32->data.F32[0] = 18.0;
+    inVector32->data.F32[1] = 24.0;
+    inVector32->data.F32[2] =  4.0;
+    inVector32->n = 3;
+    printFooter(stdout, "psMatrix", "Create input and output images and vectors", true);
+
+
+    // Test B - Calculate LU matrix
+    printPositiveTestHeader(stdout, "psMatrix", "Calculate LU matrix");
+    tempImage = luImage;
+    luImage = psMatrixLUD(luImage, &perm, inImage);
+    CHECK_MATRIX(luImage);
+    if(luImage->type.dimen != PS_DIMEN_IMAGE) {
+        printf("Error: Resulting image is not PS_DIMEN_IMAGE\n");
+    } else if(luImage != tempImage) {
+        printf("Error: Return pointer not equal to output argument pointer\n");
+    }
+
+    tempImage32 = luImage32;
+    luImage32 = psMatrixLUD(luImage32, &perm32, inImage32);
+    CHECK_MATRIX(luImage32);
+    if(luImage32->type.dimen != PS_DIMEN_IMAGE) {
+        printf("Error: Resulting image is not PS_DIMEN_IMAGE\n");
+    } else if(luImage32 != tempImage32) {
+        printf("Error: Return pointer not equal to output argument pointer\n");
+    }
+    printFooter(stdout, "psMatrix", "Calculate LU matrix", true);
+
+    // Test C - Determine solution to matrix equation
+    printPositiveTestHeader(stdout, "psMatrix", "Determine solution to matrix equation");
+    tempVector = outVector;
+    outVector = psMatrixLUSolve(outVector, luImage, inVector, perm);
+    CHECK_VECTOR(outVector);
+    if(outVector->type.dimen != PS_DIMEN_VECTOR) {
+        printf("Error: Resulting image is not PS_DIMEN_VECTOR\n");
+    } else if(outVector != tempVector) {
+        printf("Error: Return pointer not equal to output argument pointer\n");
+    }
+
+    tempVector32 = outVector32;
+    outVector32 = psMatrixLUSolve(outVector32, luImage32, inVector32, perm32);
+    CHECK_VECTOR(outVector32);
+    if(outVector32->type.dimen != PS_DIMEN_VECTOR) {
+        printf("Error: Resulting image is not PS_DIMEN_VECTOR\n");
+    } else if(outVector32 != tempVector32) {
+        printf("Error: Return pointer not equal to output argument pointer\n");
+    }
+    printFooter(stdout, "psMatrix", "Determine solution to matrix equation", true);
+
+
+    // Test D - Free input and output images and vectors
+    printPositiveTestHeader(stdout, "psMatrix", "Free input and output images and vectors");
+    psFree(inImage);
+    psFree(luImage);
+    psFree(perm);
+    psFree(outVector);
+    psFree(inVector);
+    psFree(inImage32);
+    psFree(luImage32);
+    psFree(perm32);
+    psFree(outVector32);
+    psFree(inVector32);
+    if( psMemCheckLeaks(0, NULL, stdout, false) ) {
+        psError(PS_ERR_UNKNOWN,true,"Memory leaks detected");
+        return 10;
+    }
+    psS32 nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+    }
+    printFooter(stdout, "psMatrix" ,"Free input and output images and vectors", true);
+
+
+    // Test E - Attempt to use null image input argument
+    printNegativeTestHeader(stdout,"psMatrix", "Attempt to use null image input argument",
+                            "Invalid operation: inImage or its data is NULL.", 0);
+    psImage *imageTest = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    psMatrixLUD(imageTest, NULL, NULL);
+    printFooter(stdout, "psMatrix", "Attempt to use null image input argument", true);
+
+
+    // Test F - Attempt to use null input vector argument
+    printNegativeTestHeader(stdout,"psMatrix", "Attempt to use null input vector argument",
+                            "Invalid operation: inVector or its data is NULL.", 0);
+    psVector *vectorBad = NULL;
+    psVector *vectorBadOut = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+    psVector *permBad = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+    imageTest = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    psMatrixLUSolve(vectorBadOut, imageTest, vectorBad, permBad);
+    printFooter(stdout, "psMatrix", "Attempt to use null input vector argument", true);
+
+
+    // Test G - Attempt to use null LU image argument
+    printNegativeTestHeader(stdout,"psMatrix", "Attempt to use null LU image argument",
+                            "Invalid operation: inImage or its data is NULL.", 0);
+    vectorBadOut = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+    psMatrixLUSolve(vectorBadOut, NULL, vectorBad, permBad);
+    printFooter(stdout, "psMatrix", "Attempt to use null LU image argument", true);
+
+    psFree(permBad);
+    psFree(imageTest);
+
+    if( psMemCheckLeaks(0, NULL, stdout, false) ) {
+        psError(PS_ERR_UNKNOWN,true,"Memory leaks detected");
+        return 10;
+    }
+    nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+    }
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix04.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix04.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix04.c	(revision 22322)
@@ -0,0 +1,162 @@
+/** @file  tst_psMatrix_04.c
+ *
+ *  @brief Test driver for psMatrix invert function
+ *
+ *  This test driver contains the following tests for psMatrix test point 4:
+ *     A)  Create input and output images
+ *     B)  Invert matrix and calculate determinant
+ *     C)  Calculate determinant only
+ *     D)  Free input and output images
+ *     E)  Attempt to use null input image argument
+ *     F)  Attempt to use null input float argument
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-08-24 01:24:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+
+#define TOLERANCE 0.000001
+
+#define CHECK_MATRIX(IMAGE)                                                                                  \
+for(psU32 i=0; i<IMAGE->numRows; i++) {                                                                  \
+    for(psU32 j=0; j<IMAGE->numCols; j++) {                                                              \
+        if(IMAGE->type.type == PS_TYPE_F64) {                                                            \
+            if(fabs(IMAGE->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {                              \
+                printf("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,                 \
+                       IMAGE->data.F64[i][j], truthMatrix[i][j]);                                        \
+            }                                                                                            \
+        } else if(IMAGE->type.type == PS_TYPE_F32){                                                      \
+            if(fabs(IMAGE->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {                              \
+                printf("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,                  \
+                       IMAGE->data.F32[i][j], truthMatrix[i][j]);                                        \
+            }                                                                                            \
+        }                                                                                                \
+    }                                                                                                    \
+}
+
+#define CHECK_VALUE(VALUE)                                                                                   \
+if(fabs(VALUE-truthValue) > TOLERANCE) {                                                                     \
+    printf("Values don't agree %lf vs %lf\n", VALUE, truthValue);                                            \
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    float det = 0.0f;
+    float det2 = 0;
+    psImage *outImage = NULL;
+    psImage *inImage = NULL;
+    psImage *tempImage = NULL;
+    psImage *outImage32 = NULL;
+    psImage *inImage32 = NULL;
+    psImage *tempImage32 = NULL;
+
+    double truthMatrix[3][3] = {{4.0000000, -4.333333, -2.333333},
+                                {-1.000000,  1.666667,  0.666667},
+                                {-1.000000,  0.666667,  0.666667}};
+    double truthValue = 3.0;
+
+
+    // Test A - Create input and output images
+    printPositiveTestHeader(stdout, "psMatrix", "Create input and output images");
+    outImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    inImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    inImage->data.F64[0][0] =  2;
+    inImage->data.F64[0][1] =  4;
+    inImage->data.F64[0][2] =  3;
+    inImage->data.F64[1][0] =  0;
+    inImage->data.F64[1][1] =  1;
+    inImage->data.F64[1][2] = -1;
+    inImage->data.F64[2][0] =  3;
+    inImage->data.F64[2][1] =  5;
+    inImage->data.F64[2][2] =  7;
+
+    outImage32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+    inImage32 = (psImage*)psImageAlloc(3, 3, PS_TYPE_F32);
+    inImage32->data.F32[0][0] =  2;
+    inImage32->data.F32[0][1] =  4;
+    inImage32->data.F32[0][2] =  3;
+    inImage32->data.F32[1][0] =  0;
+    inImage32->data.F32[1][1] =  1;
+    inImage32->data.F32[1][2] = -1;
+    inImage32->data.F32[2][0] =  3;
+    inImage32->data.F32[2][1] =  5;
+    inImage32->data.F32[2][2] =  7;
+    printFooter(stdout, "psMatrix", "Create input and output images", true);
+
+
+    // Test B - Invert matrix and calculate determinant
+    printPositiveTestHeader(stdout, "psMatrix", "Invert matrix and calculate determinant");
+    tempImage = outImage;
+    outImage = psMatrixInvert(outImage, inImage, &det);
+    CHECK_MATRIX(outImage);
+    CHECK_VALUE(det);
+    if(outImage->type.dimen != PS_DIMEN_IMAGE) {
+        printf("Error: Resulting image is not PS_DIMEN_IMAGE\n");
+    } else if(outImage != tempImage) {
+        printf("Error: Return pointer not equal to output argument pointer\n");
+    }
+    det = 0.0f;
+    tempImage32 = outImage32;
+    outImage32 = psMatrixInvert(outImage32, inImage32, &det);
+    CHECK_MATRIX(outImage32);
+    CHECK_VALUE(det);
+    if(outImage32->type.dimen != PS_DIMEN_IMAGE) {
+        printf("Error: Resulting image is not PS_DIMEN_IMAGE\n");
+    } else if(outImage32 != tempImage32) {
+        printf("Error: Return pointer not equal to output argument pointer\n");
+    }
+    printFooter(stdout, "psMatrix", "Invert matrix and calculate determinant", true);
+
+
+    // Test C - Calculate determinant only
+    printPositiveTestHeader(stdout, "psMatrix", "Calculate determinant only");
+    det2 = psMatrixDeterminant(inImage);
+    CHECK_VALUE(det2);
+    det2 = psMatrixDeterminant(inImage32);
+    CHECK_VALUE(det2);
+    printFooter(stdout, "psMatrix", "Calculate determinant only", true);
+
+
+    // Test D - Free input and output images
+    printPositiveTestHeader(stdout, "psMatrix", "Free input and output images");
+    psFree(outImage);
+    psFree(inImage);
+    psFree(outImage32);
+    psFree(inImage32);
+    if(psMemCheckLeaks(0, NULL, stdout, false) != 0 ) {
+        psError(PS_ERR_UNKNOWN,true,"Memory leaks detected.");
+        return 10;
+    }
+    psS32 nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+    }
+    printFooter(stdout, "psMatrix" ,"Free input and output images", true);
+
+
+    // Test E - Attempt to use null input image argument
+    printNegativeTestHeader(stdout,"psMatrix", "Attempt to use null input image argument",
+                            "Invalid operation: inImage or its data is NULL.", 0);
+    psImage *badOutImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    psMatrixInvert(badOutImage, NULL, &det);
+    printFooter(stdout, "psMatrix", "Attempt to use null input image argument", true);
+
+
+    // Test F - Attempt to use null input float argument
+    printNegativeTestHeader(stdout,"psMatrix", "Attempt to use null input float argument",
+                            "Invalid operation: determinant argument is NULL.", 0);
+    psImage *badInImage = (psImage*)psImageAlloc(3, 3, PS_TYPE_F64);
+    psMatrixInvert(badOutImage, badInImage, NULL);
+    printFooter(stdout, "psMatrix", "Attempt to use null input float argument", true);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix05.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix05.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix05.c	(revision 22322)
@@ -0,0 +1,119 @@
+/** @file  tst_psMatrix_05.c
+*
+*  @brief Test driver for psMatrix multiplication function
+*
+*  This test driver contains the following tests for psMatrix test point 5:
+*     A)  Create input and output images
+*     B)  Multiply images
+*     C)  Free input and output images
+*
+*  @author  Ross Harman, MHPCC
+*
+*  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2005-08-24 01:24:24 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define TOLERANCE 0.000001
+
+#define CHECK_MATRIX(IMAGE)                                                                                  \
+for(psU32 i=0; i<IMAGE->numRows; i++) {                                                                  \
+    for(psU32 j=0; j<IMAGE->numCols; j++) {                                                              \
+        if(IMAGE->type.type == PS_TYPE_F64) {                                                            \
+            if(fabs(IMAGE->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {                              \
+                printf("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,                 \
+                       IMAGE->data.F64[i][j], truthMatrix[i][j]);                                        \
+            }                                                                                            \
+        } else if(IMAGE->type.type == PS_TYPE_F32){                                                      \
+            if(fabs(IMAGE->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {                              \
+                printf("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,                  \
+                       IMAGE->data.F32[i][j], truthMatrix[i][j]);                                        \
+            }                                                                                            \
+        }                                                                                                \
+    }                                                                                                    \
+}
+
+
+psS32 main( psS32 argc,
+            char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psImage * outImage = NULL;
+    psImage *inImage1 = NULL;
+    psImage *inImage2 = NULL;
+    psImage * outImage32 = NULL;
+    psImage *inImage132 = NULL;
+    psImage *inImage232 = NULL;
+
+    double truthMatrix[3][3] = {{  0.0, 52.0},
+                                {-14.0, 51.0}};
+
+    // Test A - Create input and output images
+    printPositiveTestHeader(stdout, "psMatrix", "Create input and output images");
+    outImage = (psImage*) psImageAlloc(2, 2, PS_TYPE_F64);
+    inImage1 = (psImage*) psImageAlloc(3, 2, PS_TYPE_F64);
+    inImage2 = (psImage*) psImageAlloc(2, 3, PS_TYPE_F64);
+    inImage1->data.F64[0][0] = 2;
+    inImage1->data.F64[0][1] = 3;
+    inImage1->data.F64[0][2] = 4;
+    inImage1->data.F64[1][0] = -1;
+    inImage1->data.F64[1][1] = 2;
+    inImage1->data.F64[1][2] = 5;
+    inImage2->data.F64[0][0] = 4;
+    inImage2->data.F64[0][1] = 1;
+    inImage2->data.F64[1][0] = 0;
+    inImage2->data.F64[1][1] = 6;
+    inImage2->data.F64[2][0] = -2;
+    inImage2->data.F64[2][1] = 8;
+    outImage32 = (psImage*)psImageAlloc(2, 2, PS_TYPE_F32);
+    inImage132 = (psImage*)psImageAlloc(3, 2, PS_TYPE_F32);
+    inImage232 = (psImage*)psImageAlloc(2, 3, PS_TYPE_F32);
+    inImage132->data.F32[0][0] = 2;
+    inImage132->data.F32[0][1] = 3;
+    inImage132->data.F32[0][2] = 4;
+    inImage132->data.F32[1][0] = -1;
+    inImage132->data.F32[1][1] = 2;
+    inImage132->data.F32[1][2] = 5;
+    inImage232->data.F32[0][0] = 4;
+    inImage232->data.F32[0][1] = 1;
+    inImage232->data.F32[1][0] = 0;
+    inImage232->data.F32[1][1] = 6;
+    inImage232->data.F32[2][0] = -2;
+    inImage232->data.F32[2][1] = 8;
+    printFooter( stdout, "psMatrix", "Create input and output images", true );
+
+
+    // Test B - Multiply images
+    printPositiveTestHeader(stdout, "psMatrix", "Multiply images");
+    psMatrixMultiply(outImage, inImage1, inImage2);
+    CHECK_MATRIX(outImage);
+    psMatrixMultiply(outImage32, inImage132, inImage232);
+    CHECK_MATRIX(outImage32);
+    printFooter(stdout, "psMatrix", "Multiply images", true);
+
+
+    // Test C - Free input and output images
+    printPositiveTestHeader( stdout, "psMatrix", "Free input and output images" );
+    psFree(outImage);
+    psFree(inImage1);
+    psFree(inImage2);
+    psFree(outImage32);
+    psFree(inImage132);
+    psFree(inImage232);
+    psS32 nLeaks = psMemCheckLeaks( 0, NULL, stdout, false );
+    if ( nLeaks != 0 ) {
+        printf( "ERROR: Found %d memory leaks\n", nLeaks );
+    }
+    psS32 nBad = psMemCheckCorruption( 0 );
+    if ( nBad ) {
+        printf( "ERROR: Found %d bad memory blocks\n", nBad );
+    }
+    printFooter( stdout, "psMatrix" , "Free input and output images", true );
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix06.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix06.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix06.c	(revision 22322)
@@ -0,0 +1,125 @@
+/** @file  tst_psMatrix_06.c
+*
+*  @brief Test driver for psMatrix Eigenvectors function
+*
+*  This test driver contains the following tests for psMatrix test point 6:
+*     A)  Create input and output images
+*     B)  Calculate Eigenvectors
+*     C)  Free input and output images
+*
+*  @author  Ross Harman, MHPCC
+*
+*  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2005-08-24 01:24:24 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define TOLERANCE 0.000001
+
+#define CHECK_MATRIX(IMAGE)                                                                                  \
+for(psU32 i=0; i<IMAGE->numRows; i++) {                                                                  \
+    for(psU32 j=0; j<IMAGE->numCols; j++) {                                                              \
+        if(IMAGE->type.type == PS_TYPE_F64) {                                                            \
+            if(fabs(IMAGE->data.F64[i][j]-truthMatrix[i][j]) > TOLERANCE) {                              \
+                printf("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,                 \
+                       IMAGE->data.F64[i][j], truthMatrix[i][j]);                                        \
+            }                                                                                            \
+        } else if(IMAGE->type.type == PS_TYPE_F32){                                                      \
+            if(fabs(IMAGE->data.F32[i][j]-truthMatrix[i][j]) > TOLERANCE) {                              \
+                printf("Matrix values at element %d, %d don't agree %f vs %lf\n", i, j,                  \
+                       IMAGE->data.F32[i][j], truthMatrix[i][j]);                                        \
+            }                                                                                            \
+        }                                                                                                \
+    }                                                                                                    \
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psImage * outImage = NULL;
+    psImage *inImage = NULL;
+    psImage * outImage32 = NULL;
+    psImage *inImage32 = NULL;
+
+    double truthMatrix[4][4] = {{0.792608,  0.582076, -0.179186, -0.029193},
+                                {0.451923, -0.370502,  0.741918,  0.328712},
+                                {0.322416, -0.509579, -0.100228, -0.791411},
+                                {0.252161, -0.514048, -0.638283,  0.514553}};
+
+
+    // Test A - Create input and output images
+    printPositiveTestHeader( stdout, "psMatrix", "Create input and output images" );
+    outImage = (psImage*) psImageAlloc(4, 4, PS_TYPE_F64);
+    inImage = (psImage*)  psImageAlloc(4, 4, PS_TYPE_F64);
+
+    inImage->data.F64[0][0] = 1./1.;
+    inImage->data.F64[0][1] = 1./2.;
+    inImage->data.F64[0][2] = 1./3.;
+    inImage->data.F64[0][3] = 1./4.;
+    inImage->data.F64[1][0] = 1./2.;
+    inImage->data.F64[1][1] = 1./3.;
+    inImage->data.F64[1][2] = 1./4.;
+    inImage->data.F64[1][3] = 1./5.;
+    inImage->data.F64[2][0] = 1./3.;
+    inImage->data.F64[2][1] = 1./4.;
+    inImage->data.F64[2][2] = 1./5.;
+    inImage->data.F64[2][3] = 1./6.;
+    inImage->data.F64[3][0] = 1./4.;
+    inImage->data.F64[3][1] = 1./5.;
+    inImage->data.F64[3][2] = 1./6.;
+    inImage->data.F64[3][3] = 1./7.;
+
+    outImage32 = (psImage*) psImageAlloc(4, 4, PS_TYPE_F32);
+    inImage32 = (psImage*)  psImageAlloc(4, 4, PS_TYPE_F32);
+    inImage32->data.F32[0][0] = 1./1.;
+    inImage32->data.F32[0][1] = 1./2.;
+    inImage32->data.F32[0][2] = 1./3.;
+    inImage32->data.F32[0][3] = 1./4.;
+    inImage32->data.F32[1][0] = 1./2.;
+    inImage32->data.F32[1][1] = 1./3.;
+    inImage32->data.F32[1][2] = 1./4.;
+    inImage32->data.F32[1][3] = 1./5.;
+    inImage32->data.F32[2][0] = 1./3.;
+    inImage32->data.F32[2][1] = 1./4.;
+    inImage32->data.F32[2][2] = 1./5.;
+    inImage32->data.F32[2][3] = 1./6.;
+    inImage32->data.F32[3][0] = 1./4.;
+    inImage32->data.F32[3][1] = 1./5.;
+    inImage32->data.F32[3][2] = 1./6.;
+    inImage32->data.F32[3][3] = 1./7.;
+    printFooter(stdout, "psMatrix", "Create input and output images", true);
+
+
+    // Test B - Calculate Eigenvectors
+    printPositiveTestHeader(stdout, "psMatrix", "Calculate Eigenvectors");
+    psMatrixEigenvectors(outImage, inImage);
+    CHECK_MATRIX(outImage);
+    psMatrixEigenvectors(outImage32, inImage32);
+    CHECK_MATRIX(outImage32);
+    printFooter(stdout, "psMatrix", "Calculate Eigenvectors", true);
+
+
+    // Test C - Free input and output images
+    printPositiveTestHeader(stdout, "psMatrix", "Free input and output images");
+    psFree(outImage);
+    psFree(inImage);
+    psFree(outImage32);
+    psFree(inImage32);
+    psS32 nLeaks = psMemCheckLeaks(0, NULL, stdout, false);
+    if (nLeaks != 0) {
+        printf("ERROR: Found %d memory leaks\n", nLeaks);
+    }
+    psS32 nBad = psMemCheckCorruption(0);
+    if (nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+    }
+    printFooter(stdout, "psMatrix" , "Free input and output images", true);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix07.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix07.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrix07.c	(revision 22322)
@@ -0,0 +1,314 @@
+/** @file  tst_psMatrix_07.c
+ *
+ *  @brief Test driver for psMatrix vector conversion functions
+ *
+ *  This test driver contains the following tests for psMatrix test point 7:
+ *     A)  Create input and output images and vectors
+ *     B)  Convert matrix to PS_DIMEN_VECTOR vector
+ *     C)  Attempt to use null image input argument
+ *     D)  Convert matrix to PS_DIMEN_TRANSV vector
+ *     E)  Improper image size
+ *     F)  Convert PS_DIMEN_VECTOR vector to matrix
+ *     G)  Attempt to use null input vector argument
+ *     H)  Convert PS_DIMEN_TRANSV vector to matrix
+ *     I)  Free input and output images and vectors
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-08-24 01:24:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+
+#define TOLERANCE 0.000001
+
+#define CHECK_MATRIX(IMAGE,TRUTH)                                                                        \
+for(psU32 i=0; i<IMAGE->numRows; i++) {                                                                  \
+    for(psU32 j=0; j<IMAGE->numCols; j++) {                                                              \
+        if(IMAGE->type.type == PS_TYPE_F64) {                                                            \
+            if(fabs(IMAGE->data.F64[i][j]-TRUTH[i][j]) > TOLERANCE) {                                    \
+                printf("Matrix values at element %d, %d don't agree %lf vs %lf\n", i, j,                 \
+                       IMAGE->data.F64[i][j], TRUTH[i][j]);                                              \
+            }                                                                                            \
+        } else if(IMAGE->type.type == PS_TYPE_F32){                                                      \
+            if(fabs(IMAGE->data.F32[i][j]-TRUTH[i][j]) > TOLERANCE) {                                    \
+                printf("Matrix values at element %d, %d don't agree %f vs %f\n", i, j,                  \
+                       IMAGE->data.F32[i][j], TRUTH[i][j]);                                              \
+            }                                                                                            \
+        }                                                                                                \
+    }                                                                                                    \
+}
+
+#define CHECK_VECTOR(VECTOR)                                                                                 \
+for(psU32 i=0; i<VECTOR->n; i++) {                                                                       \
+    if(VECTOR->type.type == PS_TYPE_F64) {                                                               \
+        if(fabs(VECTOR->data.F64[i]-truthVector[i]) > TOLERANCE) {                                       \
+            printf("Vector values at element %d don't agree %lf vs %lf\n", i,                            \
+                   VECTOR->data.F64[i], truthVector[i]);                                                 \
+        }                                                                                                \
+    } else if(VECTOR->type.type == PS_TYPE_F32){                                                         \
+        if(fabs(VECTOR->data.F32[i]-truthVector_32[i]) > TOLERANCE) {                                       \
+            printf("Vector values at element %d don't agree %f vs %lf\n", i,                             \
+                   VECTOR->data.F32[i], truthVector[i]);                                                 \
+        }                                                                                                \
+    }                                                                                                    \
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psVector *v1 = NULL;
+    psVector *v1_32 = NULL;
+    psVector *tempVector = NULL;
+    psVector *tempVector_32 = NULL;
+    psImage *tempImage = NULL;
+    psImage *tempImage_32 = NULL;
+    psImage *m1 = NULL;
+    psImage *m1_32 = NULL;
+    psVector *v2 = NULL;
+    psVector *v2_32 = NULL;
+    psVector *v3 = NULL;
+    psVector *v3_32 = NULL;
+    psImage *m2 = NULL;
+    psImage *m2_32 = NULL;
+    psImage *m3 = NULL;
+    psImage *m3_32 = NULL;
+    psImage *m4 = NULL;
+    psImage *m4_32 = NULL;
+    psImage *badImage = NULL;
+    psImage *badImage_32 = NULL;
+
+    psF64 truthVector[3] = {0.0, 1.0, 2.0};
+    psF32 truthVector_32[3] = {0.0, 1.0, 2.0};
+    psF64 truthMatrix[3][1] = {{0.0}, {1.0}, {2.0}};
+    psF32 truthMatrix_32[3][1] = {{0.0}, {1.0}, {2.0}};
+
+    // Test A - Create input and output images
+    printPositiveTestHeader(stdout, "psMatrix", "Create input and output images and vectors");
+    v1 = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+    v1_32 = (psVector*)psVectorAlloc(3, PS_TYPE_F32);
+    m1 = (psImage*)psImageAlloc(1, 3, PS_TYPE_F64);
+    m1_32 = (psImage*)psImageAlloc(1,3,PS_TYPE_F32);
+    v2 = (psVector*)psVectorAlloc(3, PS_TYPE_F64);
+    v2_32 = (psVector*)psVectorAlloc(3,PS_TYPE_F32);
+    m2 = (psImage*)psImageAlloc(1, 3, PS_TYPE_F64);
+    m2_32 = (psImage*)psImageAlloc(1,3,PS_TYPE_F32);
+    m3 = (psImage*)psImageAlloc(3, 1, PS_TYPE_F64);
+    m3_32 = (psImage*)psImageAlloc(3,1,PS_TYPE_F32);
+    m4 = (psImage*)psImageAlloc(3, 1, PS_TYPE_F64);
+    m4_32 = (psImage*)psImageAlloc(3,1,PS_TYPE_F32);
+    badImage = (psImage*)psImageAlloc(2, 2, PS_TYPE_F64);
+    badImage_32 = (psImage*)psImageAlloc(2,2,PS_TYPE_F32);
+    m1->data.F64[0][0] = 0.0;
+    m1->data.F64[1][0] = 1.0;
+    m1->data.F64[2][0] = 2.0;
+    m1_32->data.F32[0][0] = 0.0;
+    m1_32->data.F32[1][0] = 1.0;
+    m1_32->data.F32[2][0] = 2.0;
+    v2->data.F64[0] = 0.0;
+    v2->data.F64[1] = 1.0;
+    v2->data.F64[2] = 2.0;
+    v2->n = 3;
+    v2_32->data.F32[0] = 0.0;
+    v2_32->data.F32[1] = 1.0;
+    v2_32->data.F32[2] = 2.0;
+    v2_32->n = 3;
+    m4->data.F64[0][0] = 0.0;
+    m4->data.F64[0][1] = 1.0;
+    m4->data.F64[0][2] = 2.0;
+    m4_32->data.F32[0][0] = 0.0;
+    m4_32->data.F32[0][1] = 1.0;
+    m4_32->data.F32[0][2] = 2.0;
+    printFooter(stdout, "psMatrix", "Create input and output images and vectors", true);
+
+    // Test B - Convert matrix to PS_DIMEN_VECTOR vector
+    printPositiveTestHeader(stdout, "psMatrix", "Convert matrix to PS_DIMEN_VECTOR vector");
+    tempVector = v1;
+    v1 = psMatrixToVector(v1, m1);
+    CHECK_VECTOR(v1);
+    if(v1->type.dimen != PS_DIMEN_VECTOR) {
+        psError(PS_ERR_UNKNOWN,true,"Resulting image is not PS_DIMEN_VECTOR");
+        return 1;
+    } else if(v1 != tempVector) {
+        psError(PS_ERR_UNKNOWN,true,"Return pointer not equal to output argument pointer");
+        return 2;
+    }
+    tempVector_32 = v1_32;
+    v1_32 = psMatrixToVector(v1_32, m1_32);
+    CHECK_VECTOR(v1_32);
+    if(v1_32->type.dimen != PS_DIMEN_VECTOR) {
+        psError(PS_ERR_UNKNOWN,true,"Resulting image is not PS_DIMEN_VECTOR");
+        return 1;
+    } else if(v1_32 != tempVector_32) {
+        psError(PS_ERR_UNKNOWN,true,"Return pointer not equal to output argument pointer");
+        return 2;
+    }
+    printFooter(stdout, "psMatrix", "Convert matrix to PS_DIMEN_VECTOR vector", true);
+
+
+    // Test C - Attempt to use null image input argument
+    printNegativeTestHeader(stdout,"psMatrix", "Attempt to use null image input argument",
+                            "Invalid operation: inImage or its data is NULL.", 0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error.");
+    v1 = psMatrixToVector(v1, NULL);
+    if(v1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with NULL input");
+        return 3;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error.");
+    v1_32 = psMatrixToVector(v1_32, NULL);
+    if(v1_32 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with NULL input");
+        return 3;
+    }
+    printFooter(stdout, "psMatrix", "Attempt to use null image input argument", true);
+
+
+    // Test D - Convert matrix to PS_DIMEN_TRANSV vector
+    printPositiveTestHeader(stdout, "psMatrix", "Convert matrix to PS_DIMEN_TRANSV vector");
+    v3 = psVectorAlloc(3, PS_TYPE_F64);
+    tempVector = v3;
+    v3->type.dimen = PS_DIMEN_TRANSV;
+    psMatrixToVector(v3, m4);
+    CHECK_VECTOR(v3);
+    if(v3->type.dimen != PS_DIMEN_TRANSV) {
+        psError(PS_ERR_UNKNOWN,true,"Resulting image is not PS_DIMEN_TRANSV");
+        return 4;
+    } else if(v3 != tempVector) {
+        psError(PS_ERR_UNKNOWN,true,"Return pointer not equal to output argument pointer");
+        return 5;
+    }
+    v3_32 = psVectorAlloc(3, PS_TYPE_F32);
+    tempVector_32 = v3_32;
+    v3_32->type.dimen = PS_DIMEN_TRANSV;
+    psMatrixToVector(v3_32, m4_32);
+    CHECK_VECTOR(v3_32);
+    if(v3_32->type.dimen != PS_DIMEN_TRANSV) {
+        psError(PS_ERR_UNKNOWN,true,"Resulting image is not PS_DIMEN_TRANSV");
+        return 6;
+    } else if(v3_32 != tempVector_32) {
+        psError(PS_ERR_UNKNOWN,true,"Return pointer not equal to output argument pointer");
+        return 7;
+    }
+    printFooter(stdout, "psMatrix", "Convert matrix to PS_DIMEN_TRANSV vector", true);
+
+
+    // Test E - Improper image size
+    printNegativeTestHeader(stdout,"psMatrix", "Improper image size",
+                            "Image does not have dim with 1 col or 1 row: (2 x 2).", 0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if(psMatrixToVector(v1, badImage) != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with improper sizes");
+        return 8;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if(psMatrixToVector(v1_32, badImage_32) != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with improper sizes");
+        return 9;
+    }
+    printFooter(stdout, "psMatrix", "Improper image size", true);
+
+
+    // Test F - Convert PS_DIMEN_VECTOR vector to matrix
+    printPositiveTestHeader(stdout, "psMatrix", "Convert PS_DIMEN_VECTOR vector to matrix");
+    tempImage = m2;
+    m2 = psVectorToMatrix(m2, v2);
+    CHECK_MATRIX(m2,truthMatrix);
+    if(m2->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_UNKNOWN,true,"Resulting image is not PS_DIMEN_IMAGE");
+        return 10;
+    } else if(m2 != tempImage) {
+        psError(PS_ERR_UNKNOWN,true,"Return pointer not equal to output argument pointer");
+        return 11;
+    }
+    tempImage_32 = m2_32;
+    m2_32 = psVectorToMatrix(m2_32, v2_32);
+    CHECK_MATRIX(m2_32,truthMatrix_32);
+    if(m2_32->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_UNKNOWN,true,"Resulting image is not PS_DIMEN_IMAGE");
+        return 10;
+    } else if(m2_32 != tempImage_32) {
+        psError(PS_ERR_UNKNOWN,true,"Return pointer not equal to output argument pointer");
+        return 11;
+    }
+    printFooter(stdout, "psMatrix", "Convert PS_DIMEN_VECTOR vector to matrix", true);
+
+
+    // Test G - Attempt to use null input vector argument
+    printNegativeTestHeader(stdout,"psMatrix", "Attempt to use null input vector argument",
+                            "Invalid operation: inVector or its data is NULL.", 0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    if(psVectorToMatrix(m2, NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return output image");
+        return 12;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    if(psVectorToMatrix(m2_32, NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return output image");
+        return 13;
+    }
+    printFooter(stdout, "psMatrix", "Attempt to use null input vector argument", true);
+
+
+    // Test H - Convert PS_DIMEN_TRANSV vector to matrix
+    printPositiveTestHeader(stdout, "psMatrix", "Convert PS_DIMEN_TRANSV vector to matrix");
+    v2->type.dimen = PS_DIMEN_TRANSV;
+    tempImage = m3;
+    psVectorToMatrix(m3, v2);
+    CHECK_MATRIX(m3, truthMatrix);
+    if(m3->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_UNKNOWN,true,"Resulting image is not PS_DIMEN_IMAGE");
+        return 14;
+    } else if(m3 != tempImage) {
+        psError(PS_ERR_UNKNOWN,true,"Return pointer not equal to output argument pointer");
+        return 15;
+    }
+    v2_32->type.dimen = PS_DIMEN_TRANSV;
+    tempImage_32 = m3_32;
+    psVectorToMatrix(m3_32, v2_32);
+    CHECK_MATRIX(m3_32, truthMatrix_32);
+    if(m3_32->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_UNKNOWN,true,"Resulting image is not PS_DIMEN_IMAGE");
+        return 16;
+    } else if(m3_32 != tempImage_32) {
+        psError(PS_ERR_UNKNOWN,true,"Return pointer not equal to output argument pointer");
+        return 17;
+    }
+    printFooter(stdout, "psMatrix", "Convert PS_DIMEN_TRANSV vector to matrix", true);
+
+
+    // Test I - Free input and output images
+    printPositiveTestHeader(stdout, "psMatrix", "Free input and output images and vectors");
+    psFree(m1);
+    psFree(v1);
+    psFree(v2);
+    psFree(v3);
+    psFree(m3);
+    psFree(m4);
+    psFree(m1_32);
+    psFree(v1_32);
+    psFree(v2_32);
+    psFree(v3_32);
+    psFree(m3_32);
+    psFree(m4_32);
+    psFree(badImage);
+    psFree(badImage_32);
+    if( psMemCheckLeaks(0, NULL, stdout, false) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Memory leaks detected.");
+        return 10;
+    }
+    psS32 nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+    }
+    printFooter(stdout, "psMatrix" ,"Free input and output images and vectors", true);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic01.c	(revision 22322)
@@ -0,0 +1,206 @@
+/** @file  tst_psMatrixVectorArithmetic01.c
+ *
+ *  @brief Test driver for psMatrixVector arithmetic functions
+ *
+ *  This test driver tests combinations of matrix, vector, and scalar binary operations including:
+ *     Matrix-matrix with +,-,*,/ with S32, F32, F64, C32
+ *     Matrix-vector with +,-,*,/ with S32, F32, F64, C32
+ *     Matrix-scalar with +,-,*,/ with S32, F32, F64, C32
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-08-24 01:24:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define PRINT_SCALAR(SCALAR,TYPE)                                                                            \
+if(PS_IS_PSELEMTYPE_COMPLEX(SCALAR->type.type)) {                                                        \
+    printf("%f+%fi ", creal(SCALAR->data.TYPE), cimag(SCALAR->data.TYPE));                               \
+} else if(PS_IS_PSELEMTYPE_INT(SCALAR->type.type)) {                                                     \
+    printf("%d ", (psS32)SCALAR->data.TYPE);                                                               \
+} else {                                                                                                 \
+    printf("%f ", (double)SCALAR->data.TYPE);                                                            \
+}                                                                                                        \
+printf("\n\n");
+
+#define PRINT_VECTOR(VECTOR,TYPE)                                                                            \
+for(psS32 i=0; i<VECTOR->n; i++) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(VECTOR->type.type)) {                                                    \
+        printf("%f+%fi ", creal(VECTOR->data.TYPE[i]), cimag(VECTOR->data.TYPE[i]));                     \
+    } else if(PS_IS_PSELEMTYPE_INT(VECTOR->type.type)) {                                                 \
+        printf("%d ", (psS32)VECTOR->data.TYPE[i]);                                                        \
+    } else {                                                                                             \
+        printf("%f ", (double)VECTOR->data.TYPE[i]);                                                     \
+    }                                                                                                    \
+}                                                                                                        \
+printf("\n\n");
+
+
+#define PRINT_MATRIX(IMAGE,TYPE)                                                                             \
+for(psS32 i=IMAGE->numRows-1; i>-1; i--) {                                                                 \
+    for(psS32 j=0; j<IMAGE->numCols; j++) {                                                                \
+        if(PS_IS_PSELEMTYPE_COMPLEX(IMAGE->type.type)) {                                                 \
+            printf("%f+%fi ", creal(IMAGE->data.TYPE[i][j]), cimag(IMAGE->data.TYPE[i][j]));             \
+        } else if(PS_IS_PSELEMTYPE_INT(IMAGE->type.type)) {                                              \
+            printf("%d ", (psS32)IMAGE->data.TYPE[i][j]);                                                  \
+        } else {                                                                                         \
+            printf("%f ", (double)IMAGE->data.TYPE[i][j]);                                               \
+        }                                                                                                \
+    }                                                                                                    \
+    printf("\n");                                                                                        \
+}                                                                                                        \
+printf("\n");
+
+
+#define CREATE_AND_SET_VECTOR(NAME,TYPE,VALUE,SIZE)                                                          \
+psVector *NAME = (psVector*)psVectorAlloc(SIZE, PS_TYPE_##TYPE);                                         \
+for(psS32 i=0; i<SIZE; i++) {                                                                              \
+    NAME->data.TYPE[i] = VALUE;                                                                          \
+}                                                                                                        \
+NAME->n = SIZE;
+
+
+#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS)                                                    \
+psImage *NAME = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE);                                      \
+for(psS32 i=0; i<NAME->numRows; i++) {                                                                     \
+    for(psS32 j=0; j<NAME->numCols; j++) {                                                                 \
+        NAME->data.TYPE[i][j] = VALUE;                                                                   \
+    }                                                                                                    \
+}
+
+
+#define CHECK_MEMORY                                                                                     \
+if(psMemCheckLeaks(0, NULL, stdout, false) != 0 ) {                                                             \
+    psError(PS_ERR_UNKNOWN,true,"Memory leaks detected.");                                                \
+    return 10;                                                                                            \
+}                                                                                                        \
+psS32 nBad = psMemCheckCorruption(0);                                                                    \
+if(nBad) {                                                                                               \
+    printf("ERROR: Found %d bad memory blocks\n", nBad);                                                 \
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+
+    // Test matrix-matrix binary operations
+    #define testBinaryOpMM(OP,TYPE,VALUE1,VALUE2,NROWS,NCOLS)                                                \
+    {                                                                                                        \
+        printPositiveTestHeader(stdout, "psMatrixVectorArithmetic", "Test matrix-matrix psBinaryOp");        \
+        printf("Operation: %s\n", #OP);                                                                      \
+        CREATE_AND_SET_IMAGE(inImage,TYPE,VALUE1,NROWS,NCOLS);                                               \
+        CREATE_AND_SET_IMAGE(outImage,TYPE,VALUE2,NROWS,NCOLS);                                              \
+        printf("Input:\n");                                                                                  \
+        PRINT_MATRIX(inImage,TYPE);                                                                          \
+        PRINT_MATRIX(outImage,TYPE);                                                                         \
+        outImage = (psImage*)psBinaryOp(outImage, inImage, #OP, outImage);                                   \
+        printf("Output:\n");                                                                                 \
+        PRINT_MATRIX(outImage,TYPE);                                                                         \
+        psFree(inImage);                                                                                     \
+        psFree(outImage);                                                                                    \
+        CHECK_MEMORY;                                                                                        \
+        printFooter(stdout, "psMatrixVectorArithmetic", "Test matrix-matrix psBinaryOp", true);              \
+    }
+
+    testBinaryOpMM(+,S32,10,10,3,2);
+    testBinaryOpMM(+,F32,10.0,10.0,3,2);
+    testBinaryOpMM(+,F64,10.0,10.0,3,2);
+    testBinaryOpMM(+,C32,10.0+10.0i,10.0+10.0i,3,2);
+    testBinaryOpMM(-,S32,20,10,3,2);
+    testBinaryOpMM(-,F32,20.0,10.0,3,2);
+    testBinaryOpMM(-,F64,20.0,10.0,3,2);
+    testBinaryOpMM(*,C32,20.0+20.0i,10.0+10.0i,3,2);
+    testBinaryOpMM(*,S32,20,10,3,2);
+    testBinaryOpMM(*,F32,20.0,10.0,3,2);
+    testBinaryOpMM(*,F64,20.0,10.0,3,2);
+    testBinaryOpMM(*,C32,20.0+20.0i,10.0+10.0i,3,2);
+    testBinaryOpMM(/,C32,20.0+20.0i,10.0+10.0i,3,2);
+    testBinaryOpMM(/,S32,20,10,3,2);
+    testBinaryOpMM(/,F32,20.0,10.0,3,2);
+    testBinaryOpMM(/,F64,20.0,10.0,3,2);
+    testBinaryOpMM(/,C32,20.0+20.0i,10.0+10.0i,3,2);
+
+    // Test Matrix-Vector binary operations
+    #define testBinaryOpMV(OP,TYPE,VALUE1,VALUE2,NROWS,NCOLS)                                                \
+    {                                                                                                        \
+        printPositiveTestHeader(stdout, "psMatrixVectorArithmetic", "Test matrix-vector psBinaryOp");        \
+        printf("Operation: %s\n", #OP);                                                                      \
+        CREATE_AND_SET_IMAGE(outImage,TYPE,VALUE1,NROWS,NCOLS);                                              \
+        CREATE_AND_SET_VECTOR(inVector,TYPE,VALUE2,NROWS);                                                   \
+        printf("Input:\n");                                                                                  \
+        PRINT_MATRIX(outImage,TYPE);                                                                         \
+        PRINT_VECTOR(inVector,TYPE);                                                                         \
+        outImage = (psImage*)psBinaryOp(outImage, outImage, #OP, inVector);                                  \
+        printf("Output:\n");                                                                                 \
+        PRINT_MATRIX(outImage,TYPE);                                                                         \
+        psFree(inVector);                                                                                    \
+        psFree(outImage);                                                                                    \
+        CHECK_MEMORY;                                                                                        \
+        printFooter(stdout, "psMatrixVectorArithmetic", "Test matrix-vector psBinaryOp", true);              \
+    }
+
+    testBinaryOpMV(+,S32,10,5,3,2);
+    testBinaryOpMV(+,F32,10.0,5.0,3,2);
+    testBinaryOpMV(+,F64,10.0,5.0,3,2);
+    testBinaryOpMV(+,C32,10.0+10.0i,5.0+5.0i,3,2);
+    testBinaryOpMV(-,S32,20,5,3,2);
+    testBinaryOpMV(-,F32,20.0,5.0,3,2);
+    testBinaryOpMV(-,F64,20.0,5.0,3,2);
+    testBinaryOpMV(*,C32,20.0+20.0i,5.0+5.0i,3,2);
+    testBinaryOpMV(*,S32,20,5,3,2);
+    testBinaryOpMV(*,F32,20.0,5.0,3,2);
+    testBinaryOpMV(*,F64,20.0,5.0,3,2);
+    testBinaryOpMV(*,C32,20.0+20.0i,5.0+5.0i,3,2);
+    testBinaryOpMV(/,C32,20.0+20.0i,5.0+5.0i,3,2);
+    testBinaryOpMV(/,S32,20,5,3,2);
+    testBinaryOpMV(/,F32,20.0,5.0,3,2);
+    testBinaryOpMV(/,F64,20.0,5.0,3,2);
+    testBinaryOpMV(/,C32,20.0+20.0i,5.0+5.0i,3,2);
+
+    // Test Matrix-Scalar binary operations
+    #define testBinaryOpMS(OP,TYPE,VALUE1,VALUE2,NROWS,NCOLS)                                                \
+    {                                                                                                        \
+        printPositiveTestHeader(stdout, "psMatrixVectorArithmetic", "Test matrix-scalar psBinaryOp");        \
+        printf("Operation: %s\n", #OP);                                                                      \
+        CREATE_AND_SET_IMAGE(outImage,TYPE,VALUE1,NROWS,NCOLS);                                              \
+        psScalar *inScalar = (psScalar*)psScalarAlloc(VALUE2,PS_TYPE_##TYPE);                                \
+        printf("Input:\n");                                                                                  \
+        PRINT_MATRIX(outImage,TYPE);                                                                         \
+        PRINT_SCALAR(inScalar,TYPE);                                                                         \
+        outImage = (psImage*)psBinaryOp(outImage, outImage, #OP, psScalarCopy(inScalar));                    \
+        printf("Output:\n");                                                                                 \
+        PRINT_MATRIX(outImage,TYPE);                                                                         \
+        psFree(inScalar);                                                                                    \
+        psFree(outImage);                                                                                    \
+        CHECK_MEMORY;                                                                                        \
+        printFooter(stdout, "psMatrixVectorArithmetic", "Test matrix-scalar psBinaryOp", true);              \
+    }
+
+    testBinaryOpMS(+,S32,10,5,3,2);
+    testBinaryOpMS(+,F32,10.0,5.0,3,2);
+    testBinaryOpMS(+,F64,10.0,5.0,3,2);
+    testBinaryOpMS(+,C32,10.0+10.0i,5.0+5.0i,3,2);
+    testBinaryOpMS(-,S32,20,5,3,2);
+    testBinaryOpMS(-,F32,20.0,5.0,3,2);
+    testBinaryOpMS(-,F64,20.0,5.0,3,2);
+    testBinaryOpMS(*,C32,20.0+20.0i,5.0+5.0i,3,2);
+    testBinaryOpMS(*,S32,20,5,3,2);
+    testBinaryOpMS(*,F32,20.0,5.0,3,2);
+    testBinaryOpMS(*,F64,20.0,5.0,3,2);
+    testBinaryOpMS(*,C32,20.0+20.0i,5.0+5.0i,3,2);
+    testBinaryOpMS(/,C32,20.0+20.0i,5.0+5.0i,3,2);
+    testBinaryOpMS(/,S32,20,5,3,2);
+    testBinaryOpMS(/,F32,20.0,5.0,3,2);
+    testBinaryOpMS(/,F64,20.0,5.0,3,2);
+    testBinaryOpMS(/,C32,20.0+20.0i,5.0+5.0i,3,2);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic02.c	(revision 22322)
@@ -0,0 +1,269 @@
+
+/** @file  tst_psMatrixVectorArithmetic02.c
+ *
+ *  @brief Test driver for psMatrixVector arithmetic functions
+ *
+ *  This test driver tests combinations of matrix, vector, and scalar unary operations including:
+ *     Matrix with all math operators with S32, F32, F64, C32
+ *     Vector with all math operators with S32, F32, F64, C32
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-08-24 01:24:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#include <math.h>
+
+
+#define CHECK_VECTOR(VECTOR,TYPE,TRUTH)                                                                      \
+for(psS32 i=0; i<VECTOR->n; i++) {                                                                             \
+    if(cabs(VECTOR->data.TYPE[i])-cabs(TRUTH) > FLT_EPSILON){                                                \
+        printf("ERROR:Truth and calculated values don't match for vector operation:\n");                     \
+        if(PS_IS_PSELEMTYPE_COMPLEX(VECTOR->type.type)) {                                                    \
+            printf("Truth: %.2f%+.2fi\n", creal(VECTOR->data.TYPE[i]), cimag(VECTOR->data.TYPE[i]));         \
+            printf("Calculated: %.2f%+.2fi\n", creal(TRUTH), cimag(TRUTH));                                  \
+        } else if(PS_IS_PSELEMTYPE_INT(VECTOR->type.type)) {                                                 \
+            printf("Truth: %d\n", (psS32)(VECTOR->data.TYPE[i]));                                              \
+            printf("Calculated: %d\n", (psS32)(TRUTH));                                                        \
+        } else {                                                                                             \
+            printf("Truth: %.2f\n", (double)(VECTOR->data.TYPE[i]));                                         \
+            printf("Calculated: %.2f\n", (double)(TRUTH));                                                   \
+        }                                                                                                    \
+    }                                                                                                        \
+}                                                                                                            \
+printf("\n");
+
+
+#define CHECK_MATRIX(IMAGE,TYPE,TRUTH)                                                                       \
+for(psS32 i=IMAGE->numRows-1; i>-1; i--) {                                                                     \
+    for(psS32 j=0; j<IMAGE->numCols; j++) {                                                                    \
+        if(cabs(IMAGE->data.TYPE[i][j])-cabs(TRUTH) > FLT_EPSILON){                                          \
+            printf("ERROR:Truth and calculated values don't match for matrix operation:\n");                 \
+            if(PS_IS_PSELEMTYPE_COMPLEX(IMAGE->type.type)) {                                                 \
+                printf("Truth: %.2f%+.2fi\n", creal(IMAGE->data.TYPE[i][j]), cimag(IMAGE->data.TYPE[i][j])); \
+                printf("Calculated: %.2f%+.2fi\n", creal(TRUTH), cimag(TRUTH));                              \
+            } else if(PS_IS_PSELEMTYPE_INT(IMAGE->type.type)) {                                              \
+                printf("Truth: %d\n", (psS32)(IMAGE->data.TYPE[i][j]));                                        \
+                printf("Calculated: %d\n", (psS32)(TRUTH));                                                    \
+            } else {                                                                                         \
+                printf("Truth: %.2f\n", (double)(IMAGE->data.TYPE[i][j]));                                   \
+                printf("Calculated: %.2f\n", (double)(TRUTH));                                               \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+    printf("\n");                                                                                            \
+}                                                                                                            \
+printf("\n");
+
+
+#define CREATE_AND_SET_VECTOR(NAME,TYPE,VALUE,SIZE)                                                          \
+psVector *NAME = (psVector*)psVectorAlloc(SIZE, PS_TYPE_##TYPE);                                             \
+for(psS32 i=0; i<SIZE; i++) {                                                                                  \
+    NAME->data.TYPE[i] = VALUE;                                                                              \
+}                                                                                                            \
+NAME->n = SIZE;
+
+
+#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS)                                                    \
+psImage *NAME = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE);                                          \
+for(psS32 i=0; i<NAME->numRows; i++) {                                                                         \
+    for(psS32 j=0; j<NAME->numCols; j++) {                                                                     \
+        NAME->data.TYPE[i][j] = VALUE;                                                                       \
+    }                                                                                                        \
+}
+
+
+#define CHECK_MEMORY \
+if( psMemCheckLeaks(0, NULL, stdout, false) != 0 ) {  \
+    psError(PS_ERR_UNKNOWN, true,"Memory leaks detected."); \
+    return 50; \
+} \
+psS32 nBad = psMemCheckCorruption(0); \
+if(nBad) { \
+    psError(PS_ERR_UNKNOWN, true,"ERROR: Found %d bad memory blocks\n", nBad); \
+    return 51; \
+}
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    // Test matrix unary operations
+    #define testUnaryOpM(OP,TYPE,VALUE1,VALUE2,NROWS,NCOLS,TRUTH)                                            \
+    {                                                                                                        \
+        printPositiveTestHeader(stdout, "psMatrixVectorArithmetic", "Test matrix psUnaryOp");                \
+        printf("Operation: %s\n", #OP);                                                                      \
+        CREATE_AND_SET_IMAGE(inImage,TYPE,VALUE1,NROWS,NCOLS);                                               \
+        CREATE_AND_SET_IMAGE(outImage,TYPE,VALUE2,NROWS,NCOLS);                                              \
+        outImage = (psImage*)psUnaryOp(outImage, inImage, #OP);                                              \
+        CHECK_MATRIX(outImage,TYPE,TRUTH);                                                                   \
+        psFree(inImage);                                                                                     \
+        psFree(outImage);                                                                                    \
+        CHECK_MEMORY;                                                                                        \
+        printFooter(stdout, "psMatrixVectorArithmetic", "Test matrix psUnaryOp", true);                      \
+    }
+
+    testUnaryOpM( abs, S32, -10, 0, 3, 2, 10 );
+    testUnaryOpM( abs, F32, -10.0, 0.0, 3, 2, 10.0 );
+    testUnaryOpM( abs, F64, -10.0, 0.0, 3, 2, 10.0 );
+    testUnaryOpM( abs, C32, -10.0 - 10.0i, 0.0 + 0.0i, 3, 2,10+10i );
+    testUnaryOpM( exp, S32, 10, 0, 3, 2, cexp(10));
+    testUnaryOpM( exp, F32, 10.0, 0.0, 3, 2, cexp(10.0) );
+    testUnaryOpM( exp, F64, 10.0, 0.0, 3, 2, cexp(10.0) );
+    testUnaryOpM( exp, C32, 1.0 + 1.0i, 0.0 + 0.0i, 3, 2, cexp(1.0+1.0i) );
+    testUnaryOpM( ln, S32, 10, 0, 3, 2, clog(10) );
+    testUnaryOpM( ln, F32, 10.0, 0.0, 3, 2, clog(10.0) );
+    testUnaryOpM( ln, F64, 10.0, 0.0, 3, 2, clog(10.0) );
+    testUnaryOpM( ln, C32, 10.0 + 10.0i, 0.0 + 0.0i, 3, 2, clog(10.0+10.0i) );
+    testUnaryOpM( ten, S32, 3, 0, 3, 2, 1000 );
+    testUnaryOpM( ten, F32, 3.0, 0.0, 3, 2, 1000 );
+    testUnaryOpM( ten, F64, 3.0, 0.0, 3, 2, 1000 );
+    testUnaryOpM( ten, C32, 1.0 + 0.0i, 0.0 + 0.0i, 3, 2, 10.0 );
+    testUnaryOpM( log, S32, 1000, 0, 3, 2, 3 );
+    testUnaryOpM( log, F32, 1000.0, 0.0, 3, 2, 3 );
+    testUnaryOpM( log, F64, 1000.0, 0.0, 3, 2, 3 );
+    testUnaryOpM( log, C32, 1000.0 + 0.0i, 0.0 + 0.0i, 3, 2, 3 );
+    testUnaryOpM( sin, S32, M_PI_2, 0, 3, 2, 1 );
+    testUnaryOpM( sin, F32, M_PI_2, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( sin, F64, M_PI_2, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( sin, C32, M_PI_2 + 0.0i, 0.0 + 0.0i, 3, 2, 1.0 );
+    testUnaryOpM( dsin, S32, 90, 0, 3, 2 , 1);
+    testUnaryOpM( dsin, F32, 90.0, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( dsin, F64, 90.0, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( dsin, C32, 90.0 + 00.0i, 0.0 + 0.0i, 3, 2, 1.0 );
+    testUnaryOpM( cos, S32, 0, 0, 3, 2, 1 );
+    testUnaryOpM( cos, F32, 0.0, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( cos, F64, 0.0, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( cos, C32, 0.0 + 0.0i, 0.0 + 0.0i, 3, 2, 1.0 );
+    testUnaryOpM( dcos, S32, 0, 0, 3, 2, 1 );
+    testUnaryOpM( dcos, F32, 0.0, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( dcos, F64, 0.0, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( dcos, C32, 0.0 + 0.0i, 0.0 + 0.0i, 3, 2, 1.0 );
+    testUnaryOpM( tan, S32, M_PI_4, 0, 3, 2, 1);
+    testUnaryOpM( tan, F32, M_PI_4, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( tan, F64, M_PI_4, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( tan, C32, M_PI_4 + 0.0i, 0.0 + 0.0i, 3, 2, 1 );
+    testUnaryOpM( dtan, S32, 45, 0, 3, 2, 1 );
+    testUnaryOpM( dtan, F32, 45.0, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( dtan, F64, 45.0, 0.0, 3, 2, 1.0 );
+    testUnaryOpM( dtan, C32, 45.0 + 45.0i, 0.0 + 0.0i, 3, 2, 1.0 );
+    testUnaryOpM( asin, S32, 1, 0, 3, 2, M_PI_2);
+    testUnaryOpM( asin, F32, 1.0, 0.0, 3, 2, M_PI_2  );
+    testUnaryOpM( asin, F64, 1.0, 0.0, 3, 2, M_PI_2);
+    testUnaryOpM( asin, C32, 1.0 + 1.0i, 0.0 + 0.0i, 3, 2, M_PI_2);
+    testUnaryOpM( dasin, S32, 1.0, 0, 3, 2, 90 );
+    testUnaryOpM( dasin, F32, 1.0, 0.0, 3, 2, 90.0 );
+    testUnaryOpM( dasin, F64, 1.0, 0.0, 3, 2, 90.0 );
+    testUnaryOpM( dasin, C32, 1.0 + 1.0i, 0.0 + 0.0i, 3, 2, 90.0 );
+    testUnaryOpM( acos, S32, 0, 0, 3, 2, M_PI_2);
+    testUnaryOpM( acos, F32, 0.0, 0.0, 3, 2, M_PI_2 );
+    testUnaryOpM( acos, F64, 0.0, 0.0, 3, 2, M_PI_2 );
+    testUnaryOpM( acos, C32, 0.0 + 0.0i, 0.0 + 0.0i, 3, 2, M_PI_2 );
+    testUnaryOpM( dacos, S32, 0, 0, 3, 2, 90 );
+    testUnaryOpM( dacos, F32, 0.0, 0.0, 3, 2, 90.0 );
+    testUnaryOpM( dacos, F64, 0.0, 0.0, 3, 2, 90.0 );
+    testUnaryOpM( dacos, C32, 0.0 + 0.0i, 0.0 + 0.0i, 3, 2, 90.0 );
+    testUnaryOpM( atan, S32, 1, 0, 3, 2, M_PI_4);
+    testUnaryOpM( atan, F32, 1.0, 0.0, 3, 2, M_PI_4 );
+    testUnaryOpM( atan, F64, 1.0, 0.0, 3, 2, M_PI_4);
+    testUnaryOpM( atan, C32, 1.0 + 0.0i, 0.0 + 0.0i, 3, 2, M_PI_4);
+    testUnaryOpM( datan, S32, 1, 0, 3, 2, 45 );
+    testUnaryOpM( datan, F32, 1.0, 0.0, 3, 2, 45.0 );
+    testUnaryOpM( datan, F64, 1.0, 0.0, 3, 2, 45.0 );
+    testUnaryOpM( datan, C32, 1.0 + 0.0i, 0.0 + 0.0i, 3, 2, 45.0 );
+
+
+    // Test vector unary operations
+    #define testUnaryOpV(OP,TYPE,VALUE1,VALUE2,SIZE,TRUTH)                                                   \
+    {                                                                                                        \
+        printPositiveTestHeader(stdout, "psMatrixVectorArithmetic", "Test vector psUnaryOp");                \
+        printf("Operation: %s\n", #OP);                                                                      \
+        CREATE_AND_SET_VECTOR(inVector,TYPE,VALUE1,SIZE);                                                    \
+        CREATE_AND_SET_VECTOR(outVector,TYPE,VALUE2,SIZE);                                                   \
+        outVector = (psVector*)psUnaryOp(outVector, inVector, #OP);                                          \
+        CHECK_VECTOR(outVector,TYPE,TRUTH);                                                                  \
+        psFree(inVector);                                                                                    \
+        psFree(outVector);                                                                                   \
+        CHECK_MEMORY;                                                                                        \
+        printFooter(stdout, "psMatrixVectorArithmetic", "Test vector psUnaryOp", true);                      \
+    }
+
+    testUnaryOpV( abs, S32, -10, 0, 3, 10 );
+    testUnaryOpV( abs, F32, -10.0, 0.0, 3, 10.0 );
+    testUnaryOpV( abs, F64, -10.0, 0.0, 3, 10.0 );
+    testUnaryOpV( abs, C32, -10.0 - 10.0i, 0.0 + 0.0i, 3, 10+10i );
+    testUnaryOpV( exp, S32, 10, 0, 3, cexp(10));
+    testUnaryOpV( exp, F32, 10.0, 0.0, 3, cexp(10.0) );
+    testUnaryOpV( exp, F64, 10.0, 0.0, 3, cexp(10.0) );
+    testUnaryOpV( exp, C32, 1.0 + 1.0i, 0.0 + 0.0i, 3, cexp(1.0+1.0i) );
+    testUnaryOpV( ln, S32, 10, 0, 3, clog(10) );
+    testUnaryOpV( ln, F32, 10.0, 0.0, 3, clog(10.0) );
+    testUnaryOpV( ln, F64, 10.0, 0.0, 3, clog(10.0) );
+    testUnaryOpV( ln, C32, 10.0 + 10.0i, 0.0 + 0.0i, 3, clog(10.0+10.0i) );
+    testUnaryOpV( ten, S32, 3, 0, 3, 1000 );
+    testUnaryOpV( ten, F32, 3.0, 0.0, 3, 1000 );
+    testUnaryOpV( ten, F64, 3.0, 0.0, 3, 1000 );
+    testUnaryOpV( ten, C32, 1.0 + 0.0i, 0.0 + 0.0i, 3, 10.0 );
+    testUnaryOpV( log, S32, 1000, 0, 3,  3 );
+    testUnaryOpV( log, F32, 1000.0, 0.0, 3, 3 );
+    testUnaryOpV( log, F64, 1000.0, 0.0, 3, 3 );
+    testUnaryOpV( log, C32, 1000.0 + 0.0i, 0.0 + 0.0i, 3, 3 );
+    testUnaryOpV( sin, S32, M_PI_2, 0, 3, 1 );
+    testUnaryOpV( sin, F32, M_PI_2, 0.0, 3, 1.0 );
+    testUnaryOpV( sin, F64, M_PI_2, 0.0, 3, 1.0 );
+    testUnaryOpV( sin, C32, M_PI_2 + 0.0i, 0.0 + 0.0i, 3, 1.0 );
+    testUnaryOpV( dsin, S32, 90, 0, 3, 1);
+    testUnaryOpV( dsin, F32, 90.0, 0.0, 3, 1.0 );
+    testUnaryOpV( dsin, F64, 90.0, 0.0, 3, 1.0 );
+    testUnaryOpV( dsin, C32, 90.0 + 00.0i, 0.0 + 0.0i, 3, 1.0 );
+    testUnaryOpV( cos, S32, 0, 0, 3, 1 );
+    testUnaryOpV( cos, F32, 0.0, 0.0, 3, 1.0 );
+    testUnaryOpV( cos, F64, 0.0, 0.0, 3, 1.0 );
+    testUnaryOpV( cos, C32, 0.0 + 0.0i, 0.0 + 0.0i, 3, 1.0 );
+    testUnaryOpV( dcos, S32, 0, 0, 3, 1 );
+    testUnaryOpV( dcos, F32, 0.0, 0.0, 3, 1.0 );
+    testUnaryOpV( dcos, F64, 0.0, 0.0, 3, 1.0 );
+    testUnaryOpV( dcos, C32, 0.0 + 0.0i, 0.0 + 0.0i, 3, 1.0 );
+    testUnaryOpV( tan, S32, M_PI_4, 0, 3, 1);
+    testUnaryOpV( tan, F32, M_PI_4, 0.0, 3, 1.0 );
+    testUnaryOpV( tan, F64, M_PI_4, 0.0, 3, 1.0 );
+    testUnaryOpV( tan, C32, M_PI_4 + 0.0i, 0.0 + 0.0i, 3, 1 );
+    testUnaryOpV( dtan, S32, 45, 0, 3, 1 );
+    testUnaryOpV( dtan, F32, 45.0, 0.0, 3, 1.0 );
+    testUnaryOpV( dtan, F64, 45.0, 0.0, 3, 1.0 );
+    testUnaryOpV( dtan, C32, 45.0 + 45.0i, 0.0 + 0.0i, 3, 1.0 );
+    testUnaryOpV( asin, S32, 1, 0, 3, M_PI_2);
+    testUnaryOpV( asin, F32, 1.0, 0.0, 3, M_PI_2  );
+    testUnaryOpV( asin, F64, 1.0, 0.0, 3, M_PI_2);
+    testUnaryOpV( asin, C32, 1.0 + 1.0i, 0.0 + 0.0i, 3, M_PI_2);
+    testUnaryOpV( dasin, S32, 1.0, 0, 3, 90 );
+    testUnaryOpV( dasin, F32, 1.0, 0.0, 3, 90.0 );
+    testUnaryOpV( dasin, F64, 1.0, 0.0, 3, 90.0 );
+    testUnaryOpV( dasin, C32, 1.0 + 1.0i, 0.0 + 0.0i, 3, 90.0 );
+    testUnaryOpV( acos, S32, 0, 0, 3, M_PI_2);
+    testUnaryOpV( acos, F32, 0.0, 0.0, 3, M_PI_2 );
+    testUnaryOpV( acos, F64, 0.0, 0.0, 3, M_PI_2 );
+    testUnaryOpV( acos, C32, 0.0 + 0.0i, 0.0 + 0.0i, 3, M_PI_2 );
+    testUnaryOpV( dacos, S32, 0, 0, 3, 90 );
+    testUnaryOpV( dacos, F32, 0.0, 0.0, 3, 90.0 );
+    testUnaryOpV( dacos, F64, 0.0, 0.0, 3, 90.0 );
+    testUnaryOpV( dacos, C32, 0.0 + 0.0i, 0.0 + 0.0i, 3, 90.0 );
+    testUnaryOpV( atan, S32, 1, 0, 3, M_PI_4);
+    testUnaryOpV( atan, F32, 1.0, 0.0, 3, M_PI_4 );
+    testUnaryOpV( atan, F64, 1.0, 0.0, 3, M_PI_4);
+    testUnaryOpV( atan, C32, 1.0 + 0.0i, 0.0 + 0.0i, 3, M_PI_4);
+    testUnaryOpV( datan, S32, 1, 0, 3, 45 );
+    testUnaryOpV( datan, F32, 1.0, 0.0, 3, 45.0 );
+    testUnaryOpV( datan, F64, 1.0, 0.0, 3, 45.0 );
+    testUnaryOpV( datan, C32, 1.0 + 0.0i, 0.0 + 0.0i, 3, 45.0 );
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic03.c	(revision 22322)
@@ -0,0 +1,315 @@
+/** @file  tst_psMatrixVectorArithmetic03.c
+ *
+ *  @brief Test driver for psMatrixVector arithmetic functions
+ *
+ *  This test driver contains negative tests for psBinaryOp and psUanryOp:
+ *     Check for NULL arguments
+ *     Inconsistent element types
+ *     Inconsistent element count
+ *     Inconsistent dimensionality
+ *     Division by zero
+ *     Attempt to use min with complex numbers
+ *     Attempt to use max with complex numbers
+ *     Invalid operation
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-08-24 01:24:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define PRINT_VECTOR(VECTOR,TYPE) \
+for(psS32 i=0; i<VECTOR->n; i++) { \
+    if(PS_IS_PSELEMTYPE_COMPLEX(VECTOR->type.type)) { \
+        printf("%f+%fi ", creal(VECTOR->data.TYPE[i]), cimag(VECTOR->data.TYPE[i])); \
+    } else if(PS_IS_PSELEMTYPE_INT(VECTOR->type.type)) { \
+        printf("%d ", (psS32)VECTOR->data.TYPE[i]); \
+    } else { \
+        printf("%f ", (double)VECTOR->data.TYPE[i]); \
+    } \
+} \
+printf("\n\n");
+
+#define PRINT_MATRIX(IMAGE,TYPE) \
+for(psS32 i=IMAGE->numRows-1; i>-1; i--) { \
+    for(psS32 j=0; j<IMAGE->numCols; j++) { \
+        if(PS_IS_PSELEMTYPE_COMPLEX(IMAGE->type.type)) { \
+            printf("%f+%fi ", creal(IMAGE->data.TYPE[i][j]), cimag(IMAGE->data.TYPE[i][j])); \
+        } else if(PS_IS_PSELEMTYPE_INT(IMAGE->type.type)) { \
+            printf("%d ", (psS32)IMAGE->data.TYPE[i][j]); \
+        } else { \
+            printf("%f ", (double)IMAGE->data.TYPE[i][j]); \
+        } \
+    } \
+    printf("\n"); \
+} \
+printf("\n");
+
+
+#define CREATE_AND_SET_VECTOR(NAME,TYPE,VALUE,SIZE) \
+psVector *NAME = (psVector*)psVectorAlloc(SIZE, PS_TYPE_##TYPE); \
+for(psS32 i=0; i<SIZE; i++) { \
+    NAME->data.TYPE[i] = VALUE; \
+} \
+NAME->n = SIZE;
+
+
+#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS) \
+psImage *NAME = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE); \
+for(psS32 i=0; i<NAME->numRows; i++) { \
+    for(psS32 j=0; j<NAME->numCols; j++) { \
+        NAME->data.TYPE[i][j] = VALUE; \
+    } \
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    CREATE_AND_SET_IMAGE(image1,F64,0,3,3);
+    CREATE_AND_SET_IMAGE(image2,F64,0,3,3);
+    CREATE_AND_SET_IMAGE(image3,F32,0,3,3);
+    CREATE_AND_SET_IMAGE(image4,F64,0,2,2);
+    CREATE_AND_SET_IMAGE(image5,C32,1+1i,3,3);
+    CREATE_AND_SET_VECTOR(vector1,F64,0,2);
+    CREATE_AND_SET_VECTOR(vector2,F64,0,3);
+
+    // Check for NULL output argument
+    printPositiveTestHeader(stdout,"psBinaryOp", "Check for output generated");
+    psImage* image6 = (psImage*)psBinaryOp(NULL, image1, "+", image2);
+    if (image6 == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp failed to make an image given no output to recycle.");
+        return 1;
+    }
+    printFooter(stdout,"psBinaryOp","Check for output generated",true);
+
+    // Check for NULL input argument #1
+    printPositiveTestHeader(stdout,"psBinaryOp","Check for null input arg 1");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    image6 = (psImage*)psBinaryOp(image6, NULL, "+", image2);
+    if (image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given a NULL first operand.");
+        return 2;
+    }
+    printFooter(stdout,"psBinaryOp","Check for null input arg 1",true);
+
+    // Check for NULL input argument #2
+    printPositiveTestHeader(stdout,"psBinaryOp","Check for null input arg 2");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    image6 = (psImage*)psBinaryOp(image6, image1, "+", NULL);
+    if (image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given a NULL second operand.");
+        return 3;
+    }
+    printFooter(stdout,"psBinaryOp","Check for null input arg 2",true);
+
+    // Check for NULL operand
+    printPositiveTestHeader(stdout,"psBinaryOp","Check for null operand");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    image6 = (psImage*)psBinaryOp(image6, image1, NULL, image2);
+    if (image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given a NULL operator.");
+        return 4;
+    }
+    printFooter(stdout,"psBinaryOp","Check for null operand",true);
+
+    // Check for null output
+    printPositiveTestHeader(stdout,"psUnaryOp","Check for null output");
+    image6 = (psImage*)psUnaryOp(NULL, image1, "sin");
+    if (image6 == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp failed to make an image given no output to recycle.");
+        return 5;
+    }
+    printFooter(stdout,"psUnaryOp","Check for null output",true);
+
+    // Check for NULL input arg
+    printPositiveTestHeader(stdout,"psUnaryOp","Check for null input");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    image6 = (psImage*)psUnaryOp(image6, NULL, "sin");
+    if (image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp returned a result given a NULL operand.");
+        return 6;
+    }
+    printFooter(stdout,"psUnaryOp","Check for null input",true);
+
+    // Check for NULL operand
+    printPositiveTestHeader(stdout,"psUnaryOp","Check for null operator");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    image6 = (psImage*)psUnaryOp(image6, image1, NULL);
+    if (image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp returned a result given a NULL operator.");
+        return 7;
+    }
+    printFooter(stdout,"psUnaryOp","Check for null operator",true);
+
+    // Inconsistent element types
+    printPositiveTestHeader(stdout,"psBinaryOp", "Inconsistent element types");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    image6 = (psImage*)psBinaryOp(image6, image3, "+", image2);
+    if (image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given operands of different types.");
+        return 8;
+    }
+    printFooter(stdout,"psBinaryOp","Inconsistent element types",true);
+
+    // Check unary op to convert to correct type
+    printPositiveTestHeader(stdout,"psUnaryOp","Check output type conversion");
+    image6 = psImageCopy(image6,image2,PS_TYPE_F64);
+    image6 = (psImage*)psUnaryOp(image6, image3, "sin");
+    if (image6 == NULL || image6->type.type != PS_TYPE_F32) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp failed to convert the type of the output.");
+        return 9;
+    }
+    printFooter(stdout,"psUnaryOp","Check output type conversion",true);
+
+    // Inconsistent element count
+    printPositiveTestHeader(stdout,"psBinaryOp","Check for inconsistent elements");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    image6 = (psImage*)psBinaryOp(image6, image4, "+", image2);
+    if (image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given operands of different sizes.");
+        return 10;
+    }
+    printFooter(stdout,"psBinaryOp","Check for inconsistent elements",true);
+
+    // Inconsistent element in input and output
+    printPositiveTestHeader(stdout,"psUnaryOp","Check inconsistent elements in input and output");
+    image6 = psImageCopy(image6,image2,PS_TYPE_F64);
+    image6 = (psImage*)psUnaryOp(image6, image4, "sin");
+    if (image6 == NULL ||
+            image6->numCols != image6->numCols ||
+            image6->numRows != image6->numRows) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp failed to resize the output.");
+        return 11;
+    }
+    printFooter(stdout,"psUnaryOp","Check inconsistent elements in input and output",true);
+
+    // Inconsistent size of input 1 and input 2
+    printPositiveTestHeader(stdout,"psBinaryOp","Check inconsistent size");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    image6 = (psImage*)psBinaryOp(image6, vector1, "+", image2);
+    if (image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given operands of different sizes.");
+        return 12;
+    }
+    printFooter(stdout,"psBinaryOp","Check inconsistent size",true);
+
+    // Inconsistent size of input 1 and input 2
+    printPositiveTestHeader(stdout,"psBinaryOp","Check inconsistent size");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    vector1->type.dimen = PS_DIMEN_TRANSV;
+    image6 = (psImage*)psBinaryOp(image6, vector1, "+", image2);
+    if (image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given operands of different sizes.");
+        return 13;
+    }
+    printFooter(stdout, "psBinaryOp", "Check inconsistent size", true);
+
+
+    // Inconsistent dimensionality
+    printPositiveTestHeader(stdout,"psUnaryOp","Check inconsistent dimensionality");
+    image6 = psImageCopy(image6,image2,PS_TYPE_F64);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an two error messages");
+    image6 = (psImage*)psUnaryOp(image6, vector2, "sin");
+    if (image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp returned result given wrong type out parameter.");
+        return 14;
+    }
+    printFooter(stdout,"psUnaryOp","Check inconsistent dimensionality",true);
+
+    // Attempt to use min with complex numbers
+    printPositiveTestHeader(stdout,"psBinaryOp","Attempt to use min with complex numbers");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    image6 = (psImage*)psBinaryOp(image6, image5, "min", image5);
+    if(image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp returned result with min of complex numbers");
+        return 15;
+    }
+    printFooter(stdout, "psBinaryOp", "Attempt to use  min with complex numbers", true);
+
+
+    // Attempt to use max with complex numbers
+    printPositiveTestHeader(stdout,"psBinaryOp","Attempt to use max with complex numbers");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    image6 = (psImage*)psBinaryOp(image6, image5, "max", image5);
+    if(image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp returned result with max of complex numbers");
+        return 16;
+    }
+    printFooter(stdout, "psBinaryOp", "Attempt to use max with complex numbers", true);
+
+
+    // Invalid operation
+    printPositiveTestHeader(stdout,"psBinary","Attempt to use invalid operator");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error messgae");
+    image6 = (psImage*)psBinaryOp(image6, image1, "yarg", image2);
+    if(image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned result with invalid operator");
+        return 17;
+    }
+    printFooter(stdout,"psBinaryOp","Attempt to use invalid operator",true);
+
+    printPositiveTestHeader(stdout,"psUnaryOp","Attempt to use invalid operator");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    image6 = (psImage*)psUnaryOp(image6, image1, "yarg");
+    if(image6 != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp returned result with invalid operator");
+        return 18;
+    }
+    printFooter(stdout, "psUnaryOp", "Attempt to use invalid operator", true);
+
+    CREATE_AND_SET_VECTOR(vector4,F64,0,3);
+    CREATE_AND_SET_VECTOR(vector5,F64,0,3);
+
+    // Input parameter with dimension of PS_DIMEN_OTHER
+    printPositiveTestHeader(stdout,"psBinaryOp","Attempt to use input with PS_DIMEN_OTHER");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    vector4->type.dimen = PS_DIMEN_OTHER;
+    if ( psBinaryOp(NULL,vector4,"+",vector5) != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psBinaryOp should return null when input dimen PS_DIMEN_OTHER.");
+        return 19;
+    }
+    vector4->type.dimen = PS_DIMEN_VECTOR;
+    printFooter(stdout,"psBinaryOp","Attempt to use input with PS_DIMEN_OTHER",true);
+
+    // Input parameter with dimension of PS_DIMEN_OTHER
+    printPositiveTestHeader(stdout,"psUnaryOp","Attempt to use input with PS_DIMEN_OTHER");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    vector4->type.dimen = PS_DIMEN_OTHER;
+    if ( psUnaryOp(NULL,vector4,"sin") != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"psUnaryOp should return null when input dimen PS_DIMEN_OTHER");
+        return 20;
+    }
+    vector4->type.dimen = PS_DIMEN_VECTOR;
+    printFooter(stdout,"psUnaryOp","Attempt to use input with PS_DIMEN_OTHER",true);
+
+    psFree(vector4);
+    psFree(vector5);
+    psFree(image1);
+    psFree(image2);
+    psFree(image3);
+    psFree(image4);
+    psFree(image5);
+    psFree(vector1);
+    psFree(vector2);
+
+    psS32 nLeaks = psMemCheckLeaks(0,NULL,stdout,false);
+    if(nLeaks != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Memory leaks detected");
+        return 50;
+    }
+    psS32 nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        psError(PS_ERR_UNKNOWN,true,"Memory corruption detected");
+        return 51;
+    }
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic04.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic04.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMatrixVectorArithmetic04.c	(revision 22322)
@@ -0,0 +1,213 @@
+/** @file  tst_psMatrixVectorArithmetic04.c
+ *
+ *  @brief Test driver for psBinary arithmetic operations with scalars
+ *
+ *  This test driver will test the following binary operation with scalar inputs
+ *        vector addition with scalar in first argument
+ *        image addition with scalar in second argument
+ *
+ * @author  Eric Van Alst, MHPCC
+ *
+ * @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ * @date  $Date: 2005-08-24 01:24:24 $
+ *
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testBinOpScalarFirst(void);
+static psS32 testBinOpScalarSecond(void);
+static psS32 testBinOpScalarBoth(void);
+static psS32 testUnaryOpScalar(void);
+
+testDescription testsBinary[] = {
+                                    {testBinOpScalarFirst,737,"psBinaryOp",0,false},
+                                    {testBinOpScalarSecond,737,"psBinaryOp",0,false},
+                                    {testBinOpScalarBoth,737,"psBinaryOp",0,false},
+                                    {NULL}
+                                };
+
+testDescription testUnary[] = {
+                                  {testUnaryOpScalar,737,"psUnaryOp",0,false},
+                                  {NULL}
+                              };
+
+// Create vector
+#define CREATE_AND_SET_VECTOR(NAME,TYPE,VALUE,SIZE) \
+psVector *NAME = psVectorAlloc(SIZE,PS_TYPE_##TYPE); \
+for(psS32 i=0; i<SIZE; i++) { \
+    NAME->data.TYPE[i] = VALUE; \
+} \
+NAME->n = SIZE;
+
+// Create image
+#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS) \
+psImage *NAME = psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE); \
+for(psS32 i=0; i<NAME->numRows; i++) { \
+    for(psS32 j=0; j<NAME->numCols; j++) { \
+        NAME->data.TYPE[i][j] = VALUE; \
+    } \
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+
+    runTestSuite(stderr,"psBinaryOp",testsBinary,argc,argv);
+    runTestSuite(stderr,"psUnaryOp",testUnary,argc,argv);
+
+    return 0;
+}
+
+psS32 testBinOpScalarFirst(void)
+{
+    CREATE_AND_SET_VECTOR(vector1,S8,1,5)
+    CREATE_AND_SET_VECTOR(vector2,S8,0,5)
+
+    psScalar* inScalar1 = psScalarAlloc(2,PS_TYPE_S8);
+
+    // Add vector and scalar
+    vector2 = (psVector*)psBinaryOp(vector2,inScalar1,"+",vector1);
+    // Verify the result vector
+    for(psS32 i=0; i<vector2->n; i++) {
+        if(vector2->data.S8[i] != 3 ) {
+            psError(PS_ERR_UNKNOWN,true,"Unexpected value in return vector[%d]",i);
+            return 1;
+        }
+    }
+    psFree(vector1);
+    psFree(vector2);
+
+    CREATE_AND_SET_VECTOR(vector3,S8,1,5);
+    CREATE_AND_SET_VECTOR(vector4,S8,0,3);
+
+    psScalar* inScalar2 = psScalarAlloc(2,PS_TYPE_S8);
+    vector4->type.dimen = PS_DIMEN_TRANSV;
+    vector4 = (psVector*)psBinaryOp(vector4,inScalar2,"+",vector3);
+    for(psS32 i=0; i<vector4->n; i++) {
+        if(vector4->data.S8[i] != 3 ) {
+            psError(PS_ERR_UNKNOWN,true,"Unexpected value in return vector[%d]",i);
+            return 2;
+        }
+    }
+    psFree(vector3);
+    psFree(vector4);
+
+    CREATE_AND_SET_IMAGE(image1,S8,1,5,5)
+    psImage* image2 = NULL;
+
+    psScalar* inScalar3 = psScalarAlloc(2,PS_TYPE_S8);
+
+    image2 = (psImage*)psBinaryOp(image2,inScalar3,"+",image1);
+    for(psS32 i=0; i<image2->numRows; i++) {
+        for(psS32 j=0; j<image2->numCols; j++) {
+            if(image2->data.S8[i][j] != 3 ) {
+                psError(PS_ERR_UNKNOWN,true,"Unexpected value in return image[%d][%d]",i,j);
+                return 10;
+            }
+        }
+    }
+    psFree(image1);
+    psFree(image2);
+
+    return 0;
+}
+
+psS32 testBinOpScalarSecond(void)
+{
+    CREATE_AND_SET_VECTOR(vector1,S8,1,5)
+    CREATE_AND_SET_VECTOR(vector2,S8,0,5)
+
+    psScalar* inScalar1 = psScalarAlloc(2,PS_TYPE_S8);
+
+    vector2 = (psVector*)psBinaryOp(vector2,vector1,"+",inScalar1);
+    for(psS32 i=0; i<vector2->n; i++) {
+        if(vector2->data.S8[i] != 3 ) {
+            psError(PS_ERR_UNKNOWN,true,"Unexpected value in return vector[%d]",i);
+            return 1;
+        }
+    }
+    psFree(vector1);
+    psFree(vector2);
+
+    CREATE_AND_SET_VECTOR(vector3,S8,1,5);
+    CREATE_AND_SET_VECTOR(vector4,S8,0,3);
+
+    psScalar* inScalar2 = psScalarAlloc(2,PS_TYPE_S8);
+    vector4->type.dimen = PS_DIMEN_TRANSV;
+    vector4 = (psVector*)psBinaryOp(vector4,vector3,"+",inScalar2);
+    for(psS32 i=0; i<vector4->n; i++) {
+        if(vector4->data.S8[i] != 3 ) {
+            psError(PS_ERR_UNKNOWN,true,"Unexpected value in return vector[%d]",i);
+            return 2;
+        }
+    }
+    psFree(vector3);
+    psFree(vector4);
+
+    CREATE_AND_SET_IMAGE(image1,S8,1,5,5);
+    psImage* image2 = NULL;
+
+    psScalar* inScalar3 = psScalarAlloc(2,PS_TYPE_S8);
+
+    image2 = (psImage*)psBinaryOp(image2,image1,"+",inScalar3);
+    for(psS32 i=0; i<image2->numRows; i++) {
+        for(psS32 j=0; j<image2->numCols; j++) {
+            if(image2->data.S8[i][j] != 3 ) {
+                psError(PS_ERR_UNKNOWN,true,"Unexpected value in return image[%d][%d]",i,j);
+                return 10;
+            }
+        }
+    }
+    psFree(image1);
+    psFree(image2);
+
+    return 0;
+}
+
+psS32 testBinOpScalarBoth(void)
+{
+    psScalar* inScalar1 = psScalarAlloc(1,PS_TYPE_S8);
+    psScalar* inScalar2 = psScalarAlloc(2,PS_TYPE_S8);
+    psScalar* outScalar = psScalarAlloc(4,PS_TYPE_S8);
+
+    outScalar = (psScalar*)psBinaryOp(outScalar,inScalar1,"+",inScalar2);
+    if(outScalar->data.S8 != 3) {
+        psError(PS_ERR_UNKNOWN,true,"Unexpected value in return scalar");
+        return 3;
+    }
+    psFree(outScalar);
+
+    psScalar* inScalar3 = psScalarAlloc(10,PS_TYPE_S8);
+    psScalar* inScalar4 = psScalarAlloc(20,PS_TYPE_S8);
+    psScalar* outScalar1 = NULL;
+
+    outScalar1 = (psScalar*)psBinaryOp(outScalar1,inScalar3,"+",inScalar4);
+    if(outScalar1->data.S8 != 30 ) {
+        psError(PS_ERR_UNKNOWN,true,"Unexpected value in return scalar");
+        return 4;
+    }
+    psFree(outScalar1);
+
+    return 0;
+}
+
+psS32 testUnaryOpScalar(void)
+{
+    psScalar* inScalar = psScalarAlloc(-1,PS_TYPE_F32);
+    psScalar* outScalar = NULL;
+
+    outScalar = (psScalar*)psUnaryOp(outScalar,inScalar,"abs");
+    if(outScalar->data.F32 != 1) {
+        psError(PS_ERR_UNKNOWN,true,"Unexpected value in return scalar = %d  in = %d",outScalar->data.F32,inScalar->data.F32);
+        return 5;
+    }
+    psFree(outScalar);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMinimizeLMM.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMinimizeLMM.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMinimizeLMM.c	(revision 22322)
@@ -0,0 +1,296 @@
+/*****************************************************************************
+    This routine must ensure that psMinimizeLM() works correctly.
+ 
+    XXX: This code needs a lot of additional test case work.
+    XXX: Use the tst_template.
+    XXX: Print headers and footers.
+    XXX: Why are we flushing stdout?
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include <math.h>
+#define NUM_ITERATIONS 100
+#define ERR_TOL 1e-6
+#define NUM_DATA_POINTS 300
+#define NUM_PARAMS 3
+#define VERBOSE 0
+float expectedParm[NUM_PARAMS];
+
+
+float function(const psVector *params,      // Paramters
+               const psVector *x            // Ordinate
+              )
+{
+    return params->data.F32[0] *
+           expf(- (PS_SQR(x->data.F32[0]) + PS_SQR(x->data.F32[1]) / 2.0 / PS_SQR(params->data.F32[1]))) +
+           params->data.F32[2];
+}
+
+void derivatives(psVector *deriv,       // Derivatives
+                 const psVector *params,      // Paramters
+                 const psVector *x            // Ordinate
+                )
+{
+    deriv->data.F32[0] = expf(- (PS_SQR(x->data.F32[0]) + PS_SQR(x->data.F32[1]) / 2.0 / PS_SQR(params->data.F32[1])));
+    deriv->data.F32[1] = params->data.F32[0] * (PS_SQR(x->data.F32[0]) + PS_SQR(x->data.F32[1]) / 2.0 / PS_SQR(params->data.F32[1])) / params->data.F32[1] / params->data.F32[1] / params->data.F32[1] * expf(- (PS_SQR(x->data.F32[0]) + PS_SQR(x->data.F32[1]) / 2.0 / PS_SQR(params->data.F32[1])));
+    deriv->data.F32[2] = 1.0;
+}
+
+
+
+/*****************************************************************************
+myFunc():
+    sum = param[0] * x[0] * x[1] +
+          param[1] * x[0] +
+          param[2] * x[0]^2 +
+          param[3] * x[1] +
+          param[4] * x[1]^2
+ 
+ *****************************************************************************/
+psF32 fitFunc(psVector *deriv,
+              psVector *params,
+              psVector *x)
+{
+    if ((deriv == NULL) || (params == NULL) || (x == NULL)) {
+        psError(PS_ERR_UNKNOWN, true, "deriv or params or x is NULL.\n");
+    }
+
+    derivatives(deriv, params, x);
+    return function(params, x);
+}
+
+psS32 t01()
+{
+    psS32 currentId = psMemGetId();
+    psBool testStatus = true;
+
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
+    psMinimization *min = psMinimizationAlloc(NUM_ITERATIONS, ERR_TOL);
+    psArray *ordinates = psArrayAlloc(NUM_DATA_POINTS);
+    psVector *coordinates = psVectorAlloc(NUM_DATA_POINTS, PS_TYPE_F32);
+    psVector *errors = psVectorAlloc(NUM_DATA_POINTS, PS_TYPE_F32);
+    ordinates->n = NUM_DATA_POINTS;
+    coordinates->n = NUM_DATA_POINTS;
+    errors->n = NUM_DATA_POINTS;
+    psVector *params = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+    params->n = NUM_PARAMS;
+    psVector *trueParams = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+    trueParams->n = NUM_PARAMS;
+
+    trueParams->data.F32[0] = 100.0;    // Normalisation
+    trueParams->data.F32[1] = 3.0;      // Width
+    trueParams->data.F32[2] = 10.0;     // Background
+
+    // Set parameters
+    for (long i = 0; i < NUM_PARAMS; i++) {
+        // So we're not starting right on the true value:
+        params->data.F32[i] = trueParams->data.F32[i] * (1.0 - psRandomGaussian(rng) / 10.0);
+    }
+
+    for (long i = 0; i < NUM_DATA_POINTS; i++) {
+        psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+        x->data.F32[0] = 10.0 * psRandomUniform(rng) - 5.0;
+        x->data.F32[1] = 10.0 * psRandomUniform(rng) - 5.0;
+        ordinates->data[i] = x;
+        // Add some noise
+        coordinates->data.F32[i] = function(params, x) + 0.1 * (2.0 * psRandomGaussian(rng) - 1.0);
+        errors->data.F32[i] = 0.1;
+        if (VERBOSE) {
+            printf("Data %ld: (%f, %f) --> %f\n",
+                   i, x->data.F32[0], x->data.F32[1], coordinates->data.F32[i]);
+        }
+    }
+
+    if (!psMinimizeLMChi2(min, NULL, params, NULL, ordinates, coordinates, errors,
+                          (psMinimizeLMChi2Func)fitFunc)) {
+        printf("TEST ERROR: psMinimizeLMChi2() returned FALSE.\n");
+        fflush(stdout);
+        testStatus = false;
+    } else {
+        printf("Minimisation took %d iterations\n", min->iter);
+        printf("chi^2 at the minimum is %.3g\n", min->value);
+        for (long i = 0; i < NUM_PARAMS; i++) {
+            printf("Parameter %ld at the minimum is %.3f, expected: %f\n", i,
+                   params->data.F32[i], trueParams->data.F32[i]);
+            fflush(stdout);
+        }
+        float diff = 0.0;
+        for (long i = 0; i < NUM_DATA_POINTS; i++) {
+            psVector *x = ordinates->data[i];
+            float fitted = function(trueParams, x);
+            float expected = function(params, x);
+            diff += (fitted - expected) / fabsf(expected);
+            if (VERBOSE) {
+                printf("Data point %ld: Fitted: %f, expected: %f\n", i, fitted, expected);
+            }
+            fflush(stdout);
+        }
+        printf("Mean relative difference is %f\n", diff/(float)NUM_DATA_POINTS);
+
+    }
+
+    psFree(min);
+    psFree(params);
+    psFree(trueParams);
+    psFree(ordinates);
+    psFree(coordinates);
+    psFree(errors);
+    psFree(rng);
+    psMemCheckCorruption(1);
+    psS32 memLeaks = psMemCheckLeaks(currentId, NULL, NULL, false);
+    if (0 != memLeaks) {
+        printf("TEST ERROR: Memory Leaks! (%d leaks).\n", memLeaks);
+        fflush(stdout);
+        // XXX: This is causing a seg fault
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    return (testStatus);
+}
+
+#define NUM_ITER 10
+#define TOL  20.0
+psS32 tst_psMinimizationAlloc()
+{
+    psS32 currentId = psMemGetId();
+    psBool testStatus = true;
+
+    psMinimization *tmp = psMinimizationAlloc(NUM_ITER, TOL);
+    if (tmp == NULL) {
+        printf("TEST ERROR: psMinimizationAlloc() returned FALSE.\n");
+        testStatus = false;
+    } else {
+        if (tmp->maxIter != NUM_ITER) {
+            printf("TEST ERROR: psMinimizationAlloc() did not properly set ->maxIter.\n");
+            testStatus = false;
+        }
+
+        if (tmp->tol != TOL) {
+            printf("TEST ERROR: psMinimizationAlloc() did not properly set ->tol.\n");
+            testStatus = false;
+        }
+
+        if (tmp->value != 0.0) {
+            printf("TEST ERROR: psMinimizationAlloc() did not properly set ->value.\n");
+            testStatus = false;
+        }
+
+        if (tmp->iter != 0) {
+            printf("TEST ERROR: psMinimizationAlloc() did not properly set ->iter.\n");
+            testStatus = false;
+        }
+
+        if (!isnan(tmp->lastDelta)) {
+            printf("TEST ERROR: psMinimizationAlloc() did not properly set ->lastDelta.\n");
+            testStatus = false;
+        }
+        psFree(tmp);
+    }
+
+    psMemCheckCorruption(1);
+    psS32 memLeaks = psMemCheckLeaks(currentId, NULL, NULL, false);
+    if (0 != memLeaks) {
+        printf("TEST ERROR: Memory Leaks! (%d leaks).\n", memLeaks);
+        testStatus = false;
+    }
+
+    return(testStatus);
+}
+
+
+#define NUM_ITER 10
+#define TOL  20.0
+psS32 tst_psMinConstrainAlloc()
+{
+    psS32 currentId = psMemGetId();
+    psBool testStatus = true;
+
+    psMinConstrain *tmp = psMinConstrainAlloc(NUM_ITER, TOL);
+    if (tmp == NULL) {
+        printf("TEST ERROR: psMinConstrainAlloc() returned FALSE.\n");
+        testStatus = false;
+    } else {
+        if (tmp->paramMask != NULL) {
+            printf("TEST ERROR: psMinConstrainAlloc() did not properly set ->paramMask.\n");
+            testStatus = false;
+        }
+
+        if (tmp->paramMax != NULL) {
+            printf("TEST ERROR: psMinConstrainAlloc() did not properly set ->paramMax.\n");
+            testStatus = false;
+        }
+
+        if (tmp->paramMin != NULL) {
+            printf("TEST ERROR: psMinConstrainAlloc() did not properly set ->paramMin.\n");
+            testStatus = false;
+        }
+
+        if (tmp->paramDelta != NULL) {
+            printf("TEST ERROR: psMinConstrainAlloc() did not properly set ->paramDelta.\n");
+            testStatus = false;
+        }
+
+        psFree(tmp);
+    }
+
+    psMemCheckCorruption(1);
+    psS32 memLeaks = psMemCheckLeaks(currentId, NULL, NULL, false);
+    if (0 != memLeaks) {
+        printf("TEST ERROR: Memory Leaks! (%d leaks).\n", memLeaks);
+        testStatus = false;
+    }
+
+    return(testStatus);
+}
+
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    psTraceSetDestination(1);
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel(__func__, 0);
+    psTraceSetLevel("t01", 0);
+    psTraceSetLevel("psMinimizeLMChi2_OLD", 0);
+    psTraceSetLevel("psMinimizeLMChi2", 0);
+    psTraceSetLevel("psMinimizeGaussNewtonDelta", 0);
+    psTraceSetLevel("psMinimizeGaussNewtonDelta_EAM", 0);
+    psTraceSetLevel("p_psMinLM_GuessABP", 0);
+    psTraceSetLevel("p_psMinLM_GuessABP_EAM", 0);
+    psTraceSetLevel("p_psMinLM_SetABX", 0);
+    psTraceSetLevel("psGaussJordan", 0);
+    psTraceSetLevel("psMinimizationAlloc", 0);
+    psTraceSetLevel("psMinConstrainAlloc", 0);
+    psTraceSetLevel("psMemCheckMinimization", 0);
+    psTraceSetLevel("p_psDetermineBracket", 0);
+    psTraceSetLevel("p_psDetermineBracket2", 0);
+    psTraceSetLevel("p_psLineMin", 0);
+    psTraceSetLevel("psMinimizePowell", 0);
+    psTraceSetLevel("myPowellChi2Func", 0);
+    psTraceSetLevel("psMinimizeChi2Powell", 0);
+    psTraceSetLevel("BuildSums1D", 0);
+    psTraceSetLevel("BuildSums2D", 0);
+    psTraceSetLevel("Polynomial2DEvalVectorD", 0);
+    psTraceSetLevel("vectorFitPolynomial1DCheby", 0);
+    psTraceSetLevel("VectorFitPolynomial1DOrd", 0);
+    psTraceSetLevel("psVectorFitPolynomial1D", 0);
+    psTraceSetLevel("psVectorClipFitPolynomial1D", 0);
+
+    psTrace(__func__, 2, "Calling new psMinimize().\n");
+    psS32 testStatus = true;
+
+
+    testStatus &= t01();
+    testStatus &= tst_psMinimizationAlloc();
+    testStatus &= tst_psMinConstrainAlloc();
+    if (testStatus == true) {
+        printf("The LMM minimization tests PASSED.\n");
+    } else {
+        printf("The LMM minimization tests FAILED.\n");
+    }
+    printf("DONE\n");
+    fflush(stdout);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMinimizePowell.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMinimizePowell.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psMinimizePowell.c	(revision 22322)
@@ -0,0 +1,294 @@
+/*****************************************************************************
+    This routine must ensure that psMinimizePowell() works correctly.
+ 
+    We test with a NULL and non-NULL paramMask.
+ 
+    XXX: Must verify the stderr for the NULL parameter tests.
+    XXX: psMinimizeChi2Powell() is untested.
+ *****************************************************************************/
+#include <stdio.h>
+#include <math.h>
+#include "pslib.h"
+#include "psTest.h"
+#define N 5
+#define MIN_VALUE 20.0
+#define NUM_PARAMS 10
+#define ERROR_TOLERANCE 0.10
+float expectedParm[NUM_PARAMS];
+psS32 testStatus = true;
+
+/*****************************************************************************
+myFunc(): This routine subtracts the associate value in expectedParm[] from
+each parameter and then squares it, then sums that for all parameters, then
+adds MIN_VALUE to it.  The minimum for this function will be MIN_VALUE, and
+will occur when each parameter equals the associated value in expectedParm[].
+ 
+This procedure ignores the coordinates, other than to ensure that they were
+passed correctly from psMinimizePowell().
+ *****************************************************************************/
+float myFunc(psVector *myParams,
+             psArray *myCoords)
+{
+    float sum = 0.0;
+    float coordData = 0.0;
+    float expData = 0.0;
+    psS32 i;
+
+    //
+    // Simply ensure that the coordinate data was passed correctly.
+    //
+    for (i=0;i<N;i++) {
+        coordData = ((psVector *) (myCoords->data[i]))->data.F32[0];
+        expData = (float) (i+10);
+        if (fabs(coordData - expData) > FLT_EPSILON) {
+            printf("ERROR(1): coordinate data was incorrectly passed to myFunc()\n");
+            printf("ERROR(1): was (%f) should be (%f)\n", coordData, expData);
+            testStatus = false;
+        }
+        coordData = ((psVector *) (myCoords->data[i]))->data.F32[1];
+        expData = (float) (i+3);
+        if (fabs(coordData - expData) > FLT_EPSILON) {
+            printf("ERROR(2): coordinate data was incorrectly passed to myFunc()\n");
+            printf("ERROR(2): was (%f) should be (%f)\n", coordData, expData);
+            testStatus = false;
+        }
+    }
+
+
+    sum = 0.0;
+    for (i=0;i<NUM_PARAMS;i++) {
+        sum+= (myParams->data.F32[i] - expectedParm[i]) * (myParams->data.F32[i] - expectedParm[i]);
+    }
+    sum = MIN_VALUE + (sum * sum);
+
+    return(sum);
+}
+
+
+psS32 t00()
+{
+    psS32 currentId = psMemGetId();
+    psS32 memLeaks = 0;
+    psS32 i = 0;
+    psArray *myCoords;
+    psVector *myParams;
+    psVector *myParamMask;
+    psMinimization *min;
+
+    psTraceSetLevel(".psLib", 0);
+    /**************************************************************************
+     *************************************************************************/
+    myParams = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+    myParamMask = psVectorAlloc(NUM_PARAMS, PS_TYPE_U8);
+    myParams->n = NUM_PARAMS;
+    myParamMask->n = NUM_PARAMS;
+    min = psMinimizationAlloc(100, 0.01);
+
+    myCoords = psArrayAlloc(N);
+    myCoords->n = N;
+    for (i=0;i<N;i++) {
+        myCoords->data[i] = (psPtr *) psVectorAlloc(2, PS_TYPE_F32);
+        ((psVector *) (myCoords->data[i]))->data.F32[0] = (float) (i+10);
+        ((psVector *) (myCoords->data[i]))->data.F32[1] = (float) (i+3);
+    }
+    for (i=0;i<NUM_PARAMS;i++) {
+        expectedParm[i] = 2.32 + (float) (2 * i);
+        myParams->data.F32[i] = 0.0;
+        myParams->data.F32[i] = (float) i;
+        myParamMask->data.U8[i] = 0;
+    }
+
+    printPositiveTestHeader(stdout,
+                            "psMinimize functions",
+                            "psMinimizePowell()");
+
+    psMinimizePowell(min,
+                     myParams,
+                     myParamMask,
+                     myCoords,
+                     (psMinimizePowellFunc) myFunc);
+
+    printf("\nThe minimum is %f (expected: %f)\n", min->value, MIN_VALUE);
+
+    for (i=0;i<NUM_PARAMS;i++) {
+        printf("Parameter %d at the minimum is %.1f (expected: %.1f)\n", i,
+               myParams->data.F32[i], expectedParm[i]);
+
+        if (fabs(myParams->data.F32[i] - expectedParm[i]) > fabs(ERROR_TOLERANCE * expectedParm[i])) {
+            printf("ERROR: Parameter %d: (%.1f), expected was (%.1f)\n",
+                   i, myParams->data.F32[i], expectedParm[i]);
+            testStatus = false;
+        } else {
+            printf("Parameter %d: (%.1f), expected was (%.1f)\n",
+                   i, myParams->data.F32[i], expectedParm[i]);
+        }
+    }
+
+
+    psFree(myCoords);
+    psFree(myParams);
+    psFree(myParamMask);
+    psFree(min);
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    printFooter(stdout,
+                "psMinimize functions",
+                "psMinimizePowell()",
+                testStatus);
+
+
+    return (!testStatus);
+}
+
+psS32 t01()
+{
+    psS32 currentId = psMemGetId();
+    psS32 memLeaks = 0;
+    psS32 i = 0;
+    psArray *myCoords;
+    psVector *myParams;
+    psMinimization *min;
+
+    psTraceSetLevel(".psLib", 0);
+    /**************************************************************************
+     *************************************************************************/
+    myParams = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+    min = psMinimizationAlloc(100, 0.01);
+
+    myCoords = psArrayAlloc(N);
+    myCoords->n = N;
+    for (i=0;i<N;i++) {
+        myCoords->data[i] = (psPtr *) psVectorAlloc(2, PS_TYPE_F32);
+        ((psVector *) (myCoords->data[i]))->data.F32[0] = (float) (i+10);
+        ((psVector *) (myCoords->data[i]))->data.F32[1] = (float) (i+3);
+        ((psVector *) (myCoords->data[i]))->n = 2;
+    }
+    for (i=0;i<NUM_PARAMS;i++) {
+        expectedParm[i] = 2.32 + (float) (2 * i);
+        myParams->data.F32[i] = 0.0;
+        myParams->data.F32[i] = (float) i;
+        myParams->n++;
+    }
+
+    printPositiveTestHeader(stdout,
+                            "psMinimize functions",
+                            "psMinimizePowell()");
+
+    psMinimizePowell(min,
+                     myParams,
+                     NULL,
+                     myCoords,
+                     (psMinimizePowellFunc) myFunc);
+
+    printf("\nThe minimum is %f (expected: %f)\n", min->value, MIN_VALUE);
+
+    for (i=0;i<NUM_PARAMS;i++) {
+        printf("Parameter %d at the minimum is %.1f (expected: %.1f)\n", i,
+               myParams->data.F32[i], expectedParm[i]);
+
+        if (fabs(myParams->data.F32[i] - expectedParm[i]) > fabs(ERROR_TOLERANCE * expectedParm[i])) {
+            printf("ERROR: Parameter %d: (%.1f), expected was (%.1f)\n",
+                   i, myParams->data.F32[i], expectedParm[i]);
+            testStatus = false;
+        } else {
+            printf("Parameter %d: (%.1f), expected was (%.1f)\n",
+                   i, myParams->data.F32[i], expectedParm[i]);
+        }
+    }
+
+
+    psFree(myCoords);
+    psFree(myParams);
+    psFree(min);
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    printFooter(stdout,
+                "psMinimize functions",
+                "psMinimizePowell()",
+                testStatus);
+
+
+    return (!testStatus);
+}
+
+psS32 t02()
+{
+    psS32 currentId = psMemGetId();
+    psS32 memLeaks = 0;
+    psS32 i = 0;
+    psArray *myCoords;
+    psVector *myParams;
+    psVector *myParamMask;
+    psMinimization *min;
+
+    psTraceSetLevel(".psLib", 0);
+    /**************************************************************************
+     *************************************************************************/
+    myParams = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+    myParamMask = psVectorAlloc(NUM_PARAMS, PS_TYPE_U8);
+    min = psMinimizationAlloc(100, 0.01);
+
+    myCoords = psArrayAlloc(N);
+    myCoords->n = N;
+    for (i=0;i<N;i++) {
+        myCoords->data[i] = (psPtr *) psVectorAlloc(2, PS_TYPE_F32);
+        ((psVector *) (myCoords->data[i]))->data.F32[0] = (float) (i+10);
+        ((psVector *) (myCoords->data[i]))->data.F32[1] = (float) (i+3);
+        ((psVector *) (myCoords->data[i]))->n++;
+    }
+    for (i=0;i<NUM_PARAMS;i++) {
+        expectedParm[i] = 2.32 + (float) (2 * i);
+        myParams->data.F32[i] = 0.0;
+        myParams->data.F32[i] = (float) i;
+        myParamMask->data.U8[i] = 0;
+        myParams->n++;
+        myParamMask->n++;
+    }
+
+    printPositiveTestHeader(stdout,
+                            "psMinimize functions",
+                            "psMinimizePowell(): various NULL inputs");
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error for null minimize.");
+    psMinimizePowell(NULL, myParams, myParamMask, myCoords, (psMinimizePowellFunc) myFunc);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error for null parameter vector");
+    psMinimizePowell(min, NULL, myParamMask, myCoords,(psMinimizePowellFunc) myFunc);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error for null coords.");
+    psMinimizePowell(min, myParams, myParamMask, NULL, (psMinimizePowellFunc) myFunc);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error for null function");
+    psMinimizePowell(min, myParams, myParamMask, myCoords, NULL);
+
+    psFree(myCoords);
+    psFree(myParams);
+    psFree(myParamMask);
+    psFree(min);
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    printFooter(stdout,
+                "psMinimize functions",
+                "psMinimizePowell(): various NULL inputs",
+                testStatus);
+
+
+    return (!testStatus);
+}
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    t00();
+    t01();
+    t02();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit1D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit1D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit1D.c	(revision 22322)
@@ -0,0 +1,493 @@
+/*****************************************************************************
+This routine must ensure that various psLib functions which fit 1D polynomials
+to data work correctly.  There is a function genericTest() which creates
+vectors of data points (x and f), and populates them with the values from an
+arbitrary function setData().  It then calls appropriate 1D fitting function.
+It then evaluates the polynomial with the coefficients generated above and
+determines if they are within an error tolerance of the expected values.
+ 
+XXX: Try null stats.
+ *****************************************************************************/
+#include <stdio.h>
+#include <math.h>
+#include "pslib.h"
+#include "psTest.h"
+#define NUM_DATA 100
+#define POLY_ORDER 5
+#define A 2.0
+#define B 3.0
+#define C 4.0
+#define D 5.0
+#define E 6.0
+#define ERROR_TOLERANCE 0.10
+#define YERR 10.0
+#define VERBOSE 0
+#define NUM_ITERATIONS 5
+#define CLIP_SIGMA 4.0
+#define OUTLIERS true
+#define MASK_VALUE 1
+
+#define TS00_F_NULL  0x00000001
+#define TS00_F_F32  0x00000002
+#define TS00_F_F64  0x00000004
+#define TS00_F_S32  0x00000008
+#define TS00_X_NULL  0x00000010
+#define TS00_X_F32  0x00000020
+#define TS00_X_F64  0x00000040
+#define TS00_X_S32  0x00000080
+#define TS00_FERR_NULL  0x00000100
+#define TS00_FERR_F32  0x00000200
+#define TS00_FERR_F64  0x00000400
+#define TS00_FERR_S32  0x00000800
+#define TS00_MASK_NULL  0x00001000
+#define TS00_MASK_U8  0x00002000
+#define TS00_MASK_S32  0x00004000
+#define TS00_POLY_ORD  0x00008000
+#define TS00_POLY_CHEB  0x00010000
+#define TS00_CLIP_FIT  0x00020000
+
+psF32 setData(psF32 x)
+{
+    return(A + (B * x) + (C * x * x) + (D * x * x * x) + (E * x * x * x * x));
+}
+
+bool genericTest(
+    psU32 flags,
+    psS32 polyOrder,
+    psS32 numData,
+    psBool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    bool testStatus = true;
+    psS32 memLeaks = 0;
+    psPolynomial1D *myPoly = NULL;
+    psVector *x = NULL;
+    psVector *f = NULL;
+    psVector *mask = NULL;
+    psVector *fErr = NULL;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    stats->clipSigma = CLIP_SIGMA;
+    stats->clipIter = NUM_ITERATIONS;
+
+    printPositiveTestHeader(stdout, "psMinimize functions", "1D Polynomial Fitting Functions");
+
+    if (expectedRC == false) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (flags & TS00_CLIP_FIT) {
+        printf("        performing a clip-fit\n");
+    } else {
+        printf("        performing a non clip-fit\n");
+    }
+
+    if (flags & TS00_POLY_ORD) {
+        printf(" using ordinary polynomials\n");
+        myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, polyOrder);
+    }
+
+    if (flags & TS00_POLY_CHEB) {
+        printf(" using chebyshev polynomials\n");
+        myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, polyOrder);
+    }
+
+    if (flags & TS00_X_NULL) {
+        printf(" using a NULL x vector\n");
+        numData = 30;
+    }
+
+    psVector *xTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *fTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    xTruth->n = numData;
+    fTruth->n = numData;
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using a known seed
+    for (int i = 0; i < numData; i++) {
+        xTruth->data.F64[i] = (flags & TS00_X_NULL) ? i : 2.0*psRandomUniform(rng) - 1.0;
+        fTruth->data.F64[i] = setData(xTruth->data.F64[i]);
+    }
+    if (flags & TS00_X_NULL && flags & TS00_POLY_CHEB) {
+        // Renormalise the indices
+        p_psNormalizeVectorRange(xTruth, -1.0, 1.0);
+    }
+    psFree(rng);
+    #if VERBOSE
+
+    for (int i = 0; i < numData; i++) {
+        printf("Original %d: %f\t%f\n", i, xTruth->data.F64[i], fTruth->data.F64[i]);
+    }
+    #endif
+
+    if (flags & TS00_X_F32) {
+        printf(" using a psF32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F32);
+
+        #if 0
+
+        if (flags & TS00_POLY_CHEB) {
+            p_psNormalizeVectorRange(x, -1.0, 1.0);
+        }
+        #endif
+
+    }
+
+    if (flags & TS00_X_S32) {
+        printf(" using a psS32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_S32);
+
+        #if 0
+
+        if (flags & TS00_POLY_CHEB) {
+            p_psNormalizeVectorRange(x, -1, 1);
+        }
+        #endif
+
+    }
+
+    if (flags & TS00_X_F64) {
+        printf(" using a psF64 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F64);
+
+        #if 0
+
+        if (flags & TS00_POLY_CHEB) {
+            p_psNormalizeVectorRange(x, -1.0, 1.0);
+        }
+        #endif
+
+    }
+
+    if (flags & TS00_F_NULL) {
+        printf(" using a NULL f vector\n");
+    }
+
+    if (flags & TS00_F_F32) {
+        printf(" using a psF32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F32[numData/4]*= 2.0;
+            f->data.F32[numData/2]*= 2.0;
+            f->data.F32[3*numData/4]*= 2.0;
+        }
+    }
+
+    if (flags & TS00_F_S32) {
+        printf(" using a psS32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_S32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.S32[numData/4]*= 2.0;
+            f->data.S32[numData/2]*= 2.0;
+            f->data.S32[3*numData/4]*= 2.0;
+        }
+
+    }
+
+    if (flags & TS00_F_F64) {
+        printf(" using a psF64 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F64);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F64[numData/4]*= 2.0;
+            f->data.F64[numData/2]*= 2.0;
+            f->data.F64[3*numData/4]*= 2.0;
+        }
+    }
+
+    if (flags & TS00_FERR_NULL) {
+        printf(" using a NULL fErr vector\n");
+    }
+
+    if (flags & TS00_FERR_F32) {
+        printf(" using a psF32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F32[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_S32) {
+        printf(" using a psS32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_S32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.S32[i] = (psS32) YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_F64) {
+        printf(" using a psF64 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F64);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F64[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_MASK_NULL) {
+        printf(" using a NULL mask vector\n");
+    }
+
+    if (flags & TS00_MASK_U8) {
+        printf(" using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = 0;
+        }
+    }
+
+    if (flags & TS00_MASK_S32) {
+        printf(" using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = 0;
+        }
+    }
+
+    psPolynomial1D *rc = NULL;
+    if (flags & TS00_CLIP_FIT) {
+        rc = psVectorClipFitPolynomial1D(myPoly, stats, mask, MASK_VALUE, f, fErr, x);
+    } else {
+        rc = psVectorFitPolynomial1D(myPoly, mask, MASK_VALUE, f, fErr, x);
+    }
+
+    if (rc == NULL) {
+        if (expectedRC == true) {
+            printf("TEST ERROR: the 1D polynomial fitting function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            printf("TEST ERROR: the 1D polynomial fitting function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<polyOrder+1;i++) {
+                printf("Polynomial coefficient %d is %0.1f\n", i, myPoly->coeff[i]);
+            }
+        }
+
+        psVector *result = psPolynomial1DEvalVector(myPoly, xTruth);
+        for (psS32 i=0; i<numData; i++) {
+            // Skip the outliers.
+            if ((i == numData/4) || (i == numData/2) || (i == 3*numData/4)) {
+                continue;
+            }
+            psF32 expectData = fTruth->data.F64[i];
+            psF32 actualData = result->data.F64[i];
+            if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
+                printf("TEST ERROR: Fitted data %d: %.1f --> %.1f vs %.1f\n",
+                       i, xTruth->data.F64[i], actualData, expectData);
+                testStatus = false;
+            } else {
+                if (VERBOSE) {
+                    printf("GOOD: Fitted data %d: %1.f --> %.1f vs %.1f\n",
+                           i, xTruth->data.F64[i], actualData, expectData);
+                }
+            }
+        }
+        psFree(result);
+    }
+
+    psMemCheckCorruption(1);
+    psFree(myPoly);
+    psFree(mask);
+    psFree(x);
+    psFree(f);
+    psFree(xTruth);
+    psFree(fTruth);
+    psFree(fErr);
+    psFree(stats);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    printFooter(stdout, "psMinimize functions", "1D Polynomial Fitting Functions", testStatus);
+
+    return (testStatus);
+}
+
+/*****************************************************************************
+We test a variety of polynomial fitting routines, types, and polynomial types
+here:
+    F32 tests: Ordinary polys, non-clip fit
+    F64 tests: Ordinary polys, non-clip fit
+    F32 tests: Chebyshev polys, non-clip fit
+    F64 tests: Chebyshev polys, non-clip fit
+    F32 tests: Ordinary polys, clip fit
+    F64 tests: Ordinary polys, clip fit
+    F32 tests: Chebyshev polys, clip fit
+    F64 tests: Chebyshev polys, clip fit
+ *****************************************************************************/
+int main()
+{
+    psBool testStatus = true;
+    psLogSetFormat("HLNM");
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("psVectorClipFitPolynomial1D", 0);
+    psTraceSetLevel("VectorFitPolynomial1DOrd", 0);
+    psTraceSetLevel("VectorFitPolynomial1DCheb", 0);
+    psTraceSetLevel("vectorFitPolynomial1DCheb", 0);
+    psTraceSetLevel("vectorFitPolynomial1DChebSlow", 0);
+    psTraceSetLevel("vectorFitPolynomial1DChebFast", 0);
+    psTraceSetLevel("psVectorFitPolynomial1D", 0);
+    psTraceSetLevel("p_psCreateChebyshevPolys", 0);
+
+    printPositiveTestHeader(stdout, "psMinimize functions: 1D Polynomial Fitting Functions", "");
+
+    //
+    // F32 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false);
+    // Unallowable vector types
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_S32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_S32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_S32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false);
+
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+
+    //
+    // F64 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER, NUM_DATA, true);
+
+
+    //
+    // F32 tests: Chebyshev polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_NULL | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+
+
+    //
+    // F64 tests: Chebyshev polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_NULL | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F64 | TS00_POLY_CHEB, POLY_ORDER, NUM_DATA, true);
+
+    //
+    // F32 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+
+
+    //
+    // F64 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+
+
+    //
+    // F32 tests: Chebyshev polys, clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_NULL | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_NULL | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+
+
+    //
+    // F64 tests: Chebyshev polys, clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_NULL | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F32 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_F_F64 | TS00_POLY_CHEB | TS00_CLIP_FIT, POLY_ORDER, NUM_DATA, false);
+
+    printFooter(stdout, "psMinimize functions: 1D Polynomial Fitting Functions", "", testStatus);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit2D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit2D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit2D.c	(revision 22322)
@@ -0,0 +1,426 @@
+/*****************************************************************************
+This routine must ensure that various psLib functions which fit 2D polynomials
+to data work correctly.  There is a function genericTest() which creates
+vectors of data points (x and f), and populates them with the values from an
+arbitrary function setData().  It then calls appropriate 2D fitting function.
+It then evaluates the polynomial with the coefficients generated above and
+determines if they are within an error tolerance of the expected values.
+ *****************************************************************************/
+#include <stdio.h>
+#include <math.h>
+#include "pslib.h"
+#include "psTest.h"
+#define NUM_DATA 100
+#define POLY_ORDER_X 2
+#define POLY_ORDER_Y 3
+#define A 2.0
+#define B 3.0
+#define C 4.0
+#define D 5.0
+#define E 6.0
+#define F 4.0
+#define ERROR_TOLERANCE 0.10
+#define YERR 10.0
+#define VERBOSE 0
+#define NUM_ITERATIONS 5
+#define CLIP_SIGMA 4.0
+#define OUTLIERS true
+#define MASK_VALUE 1
+
+#define TS00_F_NULL  0x00000001
+#define TS00_F_F32  0x00000002
+#define TS00_F_F64  0x00000004
+#define TS00_F_S32  0x00000008
+#define TS00_X_NULL  0x00000010
+#define TS00_X_F32  0x00000020
+#define TS00_X_F64  0x00000040
+#define TS00_X_S32  0x00000080
+#define TS00_FERR_NULL  0x00000100
+#define TS00_FERR_F32  0x00000200
+#define TS00_FERR_F64  0x00000400
+#define TS00_FERR_S32  0x00000800
+#define TS00_MASK_NULL  0x00001000
+#define TS00_MASK_U8  0x00002000
+#define TS00_MASK_S32  0x00004000
+#define TS00_POLY_ORD  0x00008000
+#define TS00_POLY_CHEB  0x00010000
+#define TS00_CLIP_FIT  0x00020000
+#define TS00_Y_NULL  0x00100000
+#define TS00_Y_F32  0x00200000
+#define TS00_Y_F64  0x00400000
+#define TS00_Y_S32  0x00800000
+
+psF32 setData(psF32 x, psF32 y)
+{
+    return(A + (B * x) + (C * x * x) + (D * y) + (E * y * y) + (F * x * y));
+}
+
+psS32 genericTest(
+    psU32 flags,
+    psS32 polyOrderX,
+    psS32 polyOrderY,
+    psS32 numData,
+    psBool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    psS32 testStatus = true;
+    psS32 memLeaks = 0;
+    psPolynomial2D *myPoly = NULL;
+    psVector *x = NULL;
+    psVector *y = NULL;
+    psVector *f = NULL;
+    psVector *mask = NULL;
+    psVector *fErr = NULL;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    stats->clipSigma = CLIP_SIGMA;
+    stats->clipIter = NUM_ITERATIONS;
+
+    printPositiveTestHeader(stdout, "psMinimize functions", "2D Polynomial Fitting Functions");
+
+    if (expectedRC == false) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (flags & TS00_CLIP_FIT) {
+        printf(" performing a clip-fit\n");
+    } else {
+        printf(" performing a non clip-fit\n");
+    }
+
+    if (flags & TS00_POLY_ORD) {
+        printf(" using ordinary polynomials\n");
+        myPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, polyOrderX, polyOrderY);
+    }
+
+
+    psVector *xTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *yTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *fTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    xTruth->n = numData;
+    yTruth->n = numData;
+    fTruth->n = numData;
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using an RNG with a known seed
+    for (int i = 0; i < numData; i++) {
+        xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        yTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        fTruth->data.F64[i] = setData(xTruth->data.F64[i], yTruth->data.F64[i]);
+    }
+    psFree(rng);
+
+    if (flags & TS00_X_NULL) {
+        printf(" using a NULL x vector\n");
+    }
+
+    if (flags & TS00_X_F32) {
+        printf(" using a psF32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_X_S32) {
+        printf(" using a psS32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_X_F64) {
+        printf(" using a psF64 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_Y_NULL) {
+        printf(" using a NULL y vector\n");
+    }
+
+    if (flags & TS00_Y_F32) {
+        printf(" using a psF32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_Y_S32) {
+        printf(" using a psS32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_Y_F64) {
+        printf(" using a psF64 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_F_NULL) {
+        printf(" using a NULL f vector\n");
+    }
+
+    if (flags & TS00_F_F32) {
+        printf(" using a psF32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F32[numData/4]*= 2.0;
+            f->data.F32[numData/2]*= 2.0;
+            f->data.F32[3*numData/4]*= 2.0;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%.1f %.1f)\n", i, (psF32) i, f->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_F_S32) {
+        printf(" using a psS32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_S32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.S32[numData/4]*= 2.0;
+            f->data.S32[numData/2]*= 2.0;
+            f->data.S32[3*numData/4]*= 2.0;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%.1f %d)\n", i, (psF32) i, f->data.S32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_F_F64) {
+        printf(" using a psF64 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F64);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F64[numData/4]*= 2.0;
+            f->data.F64[numData/2]*= 2.0;
+            f->data.F64[3*numData/4]*= 2.0;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%.1f %.1f)\n", i, (psF32) i, f->data.F64[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_FERR_NULL) {
+        printf(" using a NULL fErr vector\n");
+    }
+
+    if (flags & TS00_FERR_F32) {
+        printf(" using a psF32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F32[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_S32) {
+        printf(" using a psS32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.S32[i] = (psS32) YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_F64) {
+        printf(" using a psF64 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F64);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F64[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_MASK_NULL) {
+        printf(" using a NULL mask vector\n");
+    }
+
+    if (flags & TS00_MASK_U8) {
+        printf(" using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = 0;
+        }
+    }
+
+    if (flags & TS00_MASK_S32) {
+        printf(" using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = 0;
+        }
+    }
+
+    psPolynomial2D *rc = NULL;
+    if (flags & TS00_CLIP_FIT) {
+        rc = psVectorClipFitPolynomial2D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y);
+    } else {
+        rc = psVectorFitPolynomial2D(myPoly, mask, MASK_VALUE, f, fErr, x, y);
+    }
+
+    if (rc == NULL) {
+        if (expectedRC == true) {
+            printf("TEST ERROR: the 2D polynomial fitting function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            printf("TEST ERROR: the 2D polynomial fitting function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<polyOrderX+1;i++) {
+                for (psS32 j=0;j<polyOrderY+1;j++) {
+                    printf("Polynomial coefficient [%d][%d] is %0.1f\n", i, j, myPoly->coeff[i][j]);
+                }
+            }
+        }
+
+        psVector *result = psPolynomial2DEvalVector(myPoly, xTruth, yTruth);
+        for (psS32 i=0 ;i<numData; i++) {
+            // Skip the outliers.
+            if ((i == numData/4) || (i == numData/2) || (i == 3*numData/4)) {
+                continue;
+            }
+            psF64 actualData = result->data.F64[i];
+            psF64 expectData = fTruth->data.F64[i];
+
+            if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
+                printf("TEST ERROR: Fitted data %d: (%.1f), expected was (%.1f)\n",
+                       i, actualData, expectData);
+                testStatus = false;
+            } else {
+                if (VERBOSE) {
+                    printf("GOOD: Fitted data %d: (%.1f), expected was (%.1f)\n",
+                           i, actualData, expectData);
+                }
+            }
+        }
+        psFree(result);
+    }
+
+    psMemCheckCorruption(1);
+    psFree(myPoly);
+    psFree(mask);
+    psFree(xTruth);
+    psFree(yTruth);
+    psFree(fTruth);
+    psFree(x);
+    psFree(y);
+    psFree(f);
+    psFree(fErr);
+    psFree(stats);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    printFooter(stdout, "psMinimize functions", "2D Polynomial Fitting Functions", testStatus);
+
+    return (testStatus);
+}
+
+/*****************************************************************************
+We test a variety of polynomial fitting routines, types, and polynomial types
+here:
+    F32 tests: Ordinary polys, non-clip fit
+    F64 tests: Ordinary polys, non-clip fit
+    F32 tests: Chebyshev polys, non-clip fit
+    F64 tests: Chebyshev polys, non-clip fit
+    F32 tests: Ordinary polys, clip fit
+    F64 tests: Ordinary polys, clip fit
+    F32 tests: Chebyshev polys, clip fit
+    F64 tests: Chebyshev polys, clip fit
+ *****************************************************************************/
+psS32 main()
+{
+    psBool testStatus = true;
+    psLogSetFormat("HLNM");
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("psVectorClipFitPolynomial2D", 0);
+    psTraceSetLevel("VectorFitPolynomial2DOrd", 0);
+    psTraceSetLevel("psVectorFitPolynomial2D", 0);
+
+    printPositiveTestHeader(stdout, "psMinimize functions: 2D Polynomial Fitting Functions", "");
+
+    //
+    // F32 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+
+    //
+    // F64 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+
+    //
+    // F32 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+
+    //
+    // F64 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, NUM_DATA, false);
+
+    printFooter(stdout, "psMinimize functions: 2D Polynomial Fitting Functions", "", testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit3D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit3D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit3D.c	(revision 22322)
@@ -0,0 +1,479 @@
+/*****************************************************************************
+This routine must ensure that various psLib functions which fit 3D polynomials
+to data work correctly.  There is a function genericTest() which creates
+vectors of data points (x and f), and populates them with the values from an
+arbitrary function setData().  It then calls appropriate 3D fitting function.
+It then evaluates the polynomial with the coefficients generated above and
+determines if they are within an error tolerance of the expected values.
+ *****************************************************************************/
+#include <stdio.h>
+#include <math.h>
+#include "pslib.h"
+#include "psTest.h"
+#define NUM_DATA 100
+#define POLY_ORDER_X 2
+#define POLY_ORDER_Y 3
+#define POLY_ORDER_Z 2
+#define A 100.0
+#define B 2.0
+#define C 3.0
+#define D 4.0
+#define E 5.0
+#define F 4.0
+#define H 3.0
+#define J 3.0
+#define K 1.0
+#define L 5.0
+#define ERROR_TOLERANCE 0.10
+#define YERR 10.0
+#define VERBOSE 0
+#define NUM_ITERATIONS 5
+#define CLIP_SIGMA 4.0
+#define OUTLIERS true
+#define MASK_VALUE 1
+
+#define TS00_F_NULL  0x00000001
+#define TS00_F_F32  0x00000002
+#define TS00_F_F64  0x00000004
+#define TS00_F_S32  0x00000008
+#define TS00_X_NULL  0x00000010
+#define TS00_X_F32  0x00000020
+#define TS00_X_F64  0x00000040
+#define TS00_X_S32  0x00000080
+#define TS00_FERR_NULL  0x00000100
+#define TS00_FERR_F32  0x00000200
+#define TS00_FERR_F64  0x00000400
+#define TS00_FERR_S32  0x00000800
+#define TS00_MASK_NULL  0x00001000
+#define TS00_MASK_U8  0x00002000
+#define TS00_MASK_S32  0x00004000
+#define TS00_POLY_ORD  0x00008000
+#define TS00_POLY_CHEB  0x00010000
+#define TS00_CLIP_FIT  0x00020000
+#define TS00_Y_NULL  0x00100000
+#define TS00_Y_F32  0x00200000
+#define TS00_Y_F64  0x00400000
+#define TS00_Y_S32  0x00800000
+#define TS00_Z_NULL  0x01000000
+#define TS00_Z_F32  0x02000000
+#define TS00_Z_F64  0x04000000
+#define TS00_Z_S32  0x08000000
+
+psF32 setData(psF32 x, psF32 y, psF32 z)
+{
+    if (0) {
+        // Linear case, for testing.
+        return(A + (B * x) + (D * y) + (H * z));
+    } else {
+        return(A + (B * x) + (C * x * x) + (D * y) + (E * y * y) + (F * x * y) + (H * z) +
+               (J * z * z) + (K * x * z) + (L * y * z));
+    }
+}
+
+psS32 genericTest(
+    psU32 flags,
+    psS32 polyOrderX,
+    psS32 polyOrderY,
+    psS32 polyOrderZ,
+    psS32 numData,
+    psBool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    psS32 testStatus = true;
+    psS32 memLeaks = 0;
+    psPolynomial3D *myPoly = NULL;
+    psVector *x = NULL;
+    psVector *y = NULL;
+    psVector *z = NULL;
+    psVector *f = NULL;
+    psVector *mask = NULL;
+    psVector *fErr = NULL;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    stats->clipSigma = CLIP_SIGMA;
+    stats->clipIter = NUM_ITERATIONS;
+
+    printPositiveTestHeader(stdout, "psMinimize functions", "3D Polynomial Fitting Functions");
+
+    psVector *xTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *yTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *zTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *fTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    xTruth->n = numData;
+    yTruth->n = numData;
+    zTruth->n = numData;
+    fTruth->n = numData;
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using known seed
+    for (int i = 0; i < numData; i++) {
+        xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        yTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        zTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        fTruth->data.F64[i] = setData(xTruth->data.F64[i], yTruth->data.F64[i], zTruth->data.F64[i]);
+    }
+    psFree(rng);
+
+    if (expectedRC == false) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (flags & TS00_CLIP_FIT) {
+        printf(" performing a clip-fit\n");
+    } else {
+        printf(" performing a non clip-fit\n");
+    }
+
+    if (flags & TS00_POLY_ORD) {
+        printf(" using ordinary polynomials\n");
+        myPoly = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, polyOrderX, polyOrderY, polyOrderZ);
+    }
+
+    if (flags & TS00_X_NULL) {
+        printf(" using a NULL x vector\n");
+    }
+
+    if (flags & TS00_X_F32) {
+        printf(" using a psF32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_X_S32) {
+        printf(" using a psS32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_X_F64) {
+        printf(" using a psF64 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F64);
+    }
+
+
+    if (flags & TS00_Y_NULL) {
+        printf(" using a NULL y vector\n");
+    }
+
+    if (flags & TS00_Y_F32) {
+        printf(" using a psF32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_Y_S32) {
+        printf(" using a psS32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_Y_F64) {
+        printf(" using a psF64 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_Z_NULL) {
+        printf(" using a NULL z vector\n");
+    }
+
+    if (flags & TS00_Z_F32) {
+        printf(" using a psF32 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_Z_S32) {
+        printf(" using a psS32 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_Z_F64) {
+        printf(" using a psF64 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_F64);
+    }
+
+
+    if (flags & TS00_F_NULL) {
+        printf(" using a NULL f vector\n");
+    }
+
+    if (flags & TS00_F_F32) {
+        printf(" using a psF32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F32[numData/4]*= 2.0;
+            f->data.F32[numData/2]*= 2.0;
+            f->data.F32[3*numData/4]*= 2.0;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%.1f)\n", i, f->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_F_S32) {
+        printf(" using a psS32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_S32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.S32[numData/4]*= 2.0;
+            f->data.S32[numData/2]*= 2.0;
+            f->data.S32[3*numData/4]*= 2.0;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%d)\n", i, f->data.S32[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_F_F64) {
+        printf(" using a psF64 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F64);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F64[numData/4]*= 2.0;
+            f->data.F64[numData/2]*= 2.0;
+            f->data.F64[3*numData/4]*= 2.0;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original data %d: (%.1f)\n", i, f->data.F64[i]);
+            }
+        }
+    }
+
+    if (flags & TS00_FERR_NULL) {
+        printf(" using a NULL fErr vector\n");
+    }
+
+    if (flags & TS00_FERR_F32) {
+        printf(" using a psF32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F32[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_S32) {
+        printf(" using a psS32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_S32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.S32[i] = (psS32) YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_F64) {
+        printf(" using a psF64 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F64);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F64[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_MASK_NULL) {
+        printf(" using a NULL mask vector\n");
+    }
+
+    if (flags & TS00_MASK_U8) {
+        printf(" using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = 0;
+        }
+    }
+
+    if (flags & TS00_MASK_S32) {
+        printf(" using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = 0;
+        }
+    }
+
+    psPolynomial3D *rc = NULL;
+    if (flags & TS00_CLIP_FIT) {
+        rc = psVectorClipFitPolynomial3D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z);
+    } else {
+        rc = psVectorFitPolynomial3D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z);
+    }
+
+    if (rc == NULL) {
+        if (expectedRC == true) {
+            printf("TEST ERROR: the 3D polynomial fitting function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            printf("TEST ERROR: the 3D polynomial fitting function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<polyOrderX+1;i++) {
+                for (psS32 j=0;j<polyOrderY+1;j++) {
+                    for (psS32 k=0;k<polyOrderZ+1;k++) {
+                        printf("Polynomial coefficient [%d][%d][%d] is %0.1f\n", i, j, k, myPoly->coeff[i][j][k]);
+                    }
+                }
+            }
+        }
+
+        psVector *result = psPolynomial3DEvalVector(myPoly, xTruth, yTruth, zTruth);
+        for (psS32 i=0 ;i<numData; i++) {
+            // Skip the outliers.
+            if ((i == numData/4) || (i == numData/2) || (i == 3*numData/4)) {
+                continue;
+            }
+            psF32 expectData = fTruth->data.F64[i];
+            psF32 actualData = result->data.F64[i];
+
+            if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
+                printf("TEST ERROR: Fitted data %d: (%.1f), expected was (%.1f)\n",
+                       i, actualData, expectData);
+                testStatus = false;
+            } else {
+                if (VERBOSE) {
+                    printf("GOOD: Fitted data %d: (%.1f), expected was (%.1f)\n",
+                           i, actualData, expectData);
+                }
+            }
+        }
+        psFree(result);
+    }
+
+    psMemCheckCorruption(1);
+    psFree(myPoly);
+    psFree(mask);
+    psFree(x);
+    psFree(y);
+    psFree(z);
+    psFree(f);
+    psFree(xTruth);
+    psFree(yTruth);
+    psFree(zTruth);
+    psFree(fTruth);
+    psFree(fErr);
+    psFree(stats);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    printFooter(stdout, "psMinimize functions", "3D Polynomial Fitting Functions", testStatus);
+
+    return (testStatus);
+}
+
+/*****************************************************************************
+We test a variety of polynomial fitting routines, types, and polynomial types
+here:
+    F32 tests: Ordinary polys, non-clip fit
+    F64 tests: Ordinary polys, non-clip fit
+    F32 tests: Chebyshev polys, non-clip fit
+    F64 tests: Chebyshev polys, non-clip fit
+    F32 tests: Ordinary polys, clip fit
+    F64 tests: Ordinary polys, clip fit
+    F32 tests: Chebyshev polys, clip fit
+    F64 tests: Chebyshev polys, clip fit
+ *****************************************************************************/
+psS32 main()
+{
+    psBool testStatus = true;
+    psLogSetFormat("HLNM");
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("psVectorClipFitPolynomial3D", 0);
+    psTraceSetLevel("VectorFitPolynomial3DOrd", 0);
+    psTraceSetLevel("psVectorFitPolynomial3D", 0);
+
+    printPositiveTestHeader(stdout, "psMinimize functions: 3D Polynomial Fitting Functions", "");
+
+    //
+    // F32 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+
+    //
+    // F64 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+
+    //
+    // F32 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_Z_NULL | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+
+    //
+    // F64 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_Z_NULL | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, NUM_DATA, false);
+
+    printFooter(stdout, "psMinimize functions: 3D Polynomial Fitting Functions", "", testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit4D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit4D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolyFit4D.c	(revision 22322)
@@ -0,0 +1,667 @@
+/*****************************************************************************
+This routine must ensure that various psLib functions which fit 4D polynomials
+to data work correctly.  There is a function genericTest() which creates
+vectors of data points (x and f), and populates them with the values from an
+arbitrary function setData().  It then calls appropriate 4D fitting function.
+It then evaluates the polynomial with the coefficients generated above and
+determines if they are within an error tolerance of the expected values.
+ *****************************************************************************/
+#include <stdio.h>
+#include <math.h>
+#include "pslib.h"
+#include "psTest.h"
+#define NUM_DATA 200
+#define POLY_ORDER_X 1
+#define POLY_ORDER_Y 1
+#define POLY_ORDER_Z 1
+#define POLY_ORDER_T 1
+#define A 100.0
+#define B 2.0
+#define C 3.0
+#define D 4.0
+#define E 5.0
+#define F 6.0
+#define H 3.0
+#define J 3.0
+#define K 1.0
+#define L 5.0
+#define M 4.0
+#define N 3.0
+#define O 2.0
+#define P 1.0
+#define Q 5.0
+#define ERROR_TOLERANCE 0.05
+#define YERR 0.1
+#define VERBOSE 0
+#define NUM_ITERATIONS 5
+#define CLIP_SIGMA 3.0
+#define OUTLIERS true
+#define MASK_VALUE 1
+
+#define TS00_F_NULL  0x00000001
+#define TS00_F_F32  0x00000002
+#define TS00_F_F64  0x00000004
+#define TS00_F_S32  0x00000008
+#define TS00_X_NULL  0x00000010
+#define TS00_X_F32  0x00000020
+#define TS00_X_F64  0x00000040
+#define TS00_X_S32  0x00000080
+#define TS00_FERR_NULL  0x00000100
+#define TS00_FERR_F32  0x00000200
+#define TS00_FERR_F64  0x00000400
+#define TS00_FERR_S32  0x00000800
+#define TS00_MASK_NULL  0x00001000
+#define TS00_MASK_U8  0x00002000
+#define TS00_MASK_S32  0x00004000
+#define TS00_POLY_ORD  0x00008000
+#define TS00_POLY_CHEB  0x00010000
+#define TS00_CLIP_FIT  0x00020000
+#define TS00_Y_NULL  0x00100000
+#define TS00_Y_F32  0x00200000
+#define TS00_Y_F64  0x00400000
+#define TS00_Y_S32  0x00800000
+#define TS00_Z_NULL  0x01000000
+#define TS00_Z_F32  0x02000000
+#define TS00_Z_F64  0x04000000
+#define TS00_Z_S32  0x08000000
+#define TS00_T_NULL  0x10000000
+#define TS00_T_F32  0x20000000
+#define TS00_T_F64  0x40000000
+#define TS00_T_S32  0x80000000
+
+psF32 setData(psF32 x, psF32 y, psF32 z, psF32 t)
+{
+    if (0) {
+        // Linear case, for testing.
+        return(A + (B * x) + (C * y) + (D * z) + (E * t));
+    } else {
+        #if 0
+        return(A + (B * x) + (C * y) + (D * z) + (E * t) +
+               (F * x * x) + (H * y * y) + (J * z * z) + (K * t * t) +
+               (L * x * y) + (M * x * z) + (N * x * t) + (O * y * z) + (P * y * t) + (Q * z * t));
+        #else
+
+        return A + (B * x) + (C * y) + (D * z) + (E * t) + (F * x * y);
+        #endif
+
+    }
+}
+
+bool genericTest(
+    psU32 flags,
+    psS32 polyOrderX,
+    psS32 polyOrderY,
+    psS32 polyOrderZ,
+    psS32 polyOrderT,
+    psS32 numData,
+    psBool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    bool testStatus = true;
+    psS32 memLeaks = 0;
+    psPolynomial4D *myPoly = NULL;
+    psVector *x = NULL;
+    psVector *y = NULL;
+    psVector *z = NULL;
+    psVector *t = NULL;
+    psVector *f = NULL;
+    psVector *mask = NULL;
+    psVector *fErr = NULL;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    stats->clipSigma = CLIP_SIGMA;
+    stats->clipIter = NUM_ITERATIONS;
+
+    printPositiveTestHeader(stdout, "psMinimize functions", "4D Polynomial Fitting Functions");
+
+    psVector *xTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *yTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *zTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *tTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    psVector *fTruth = psVectorAlloc(numData, PS_TYPE_F64);
+    xTruth->n = numData;
+    yTruth->n = numData;
+    zTruth->n = numData;
+    tTruth->n = numData;
+    fTruth->n = numData;
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using known seed
+    for (int i = 0; i < numData; i++) {
+        xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        yTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        zTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        tTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
+        fTruth->data.F64[i] = setData(xTruth->data.F64[i], yTruth->data.F64[i],
+                                      zTruth->data.F64[i], tTruth->data.F64[i]);
+    }
+    psFree(rng);
+
+    if (expectedRC == false) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (flags & TS00_CLIP_FIT) {
+        printf(" performing a clip-fit\n");
+    } else {
+        printf(" performing a non clip-fit\n");
+    }
+
+    if (flags & TS00_POLY_ORD) {
+        printf(" using ordinary polynomials\n");
+        myPoly = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, polyOrderX, polyOrderY,
+                                     polyOrderZ, polyOrderT);
+
+        #if 1
+
+        for (int ix = 0; ix < polyOrderX + 1; ix++) {
+            for (int iy = 0; iy < polyOrderY + 1; iy++) {
+                for (int iz = 0; iz < polyOrderZ + 1; iz++) {
+                    for (int it = 0; it < polyOrderT + 1; it++) {
+                        myPoly->mask[ix][iy][iz][it] = 0xff; // Mask it out
+                    }
+                }
+            }
+        }
+
+        // Put these back in
+        myPoly->mask[0][0][0][0] = 0;   // A
+        myPoly->mask[1][0][0][0] = 0;   // B * x
+        myPoly->mask[0][1][0][0] = 0;   // C * y
+        myPoly->mask[0][0][1][0] = 0;   // D * z
+        myPoly->mask[0][0][0][1] = 0;   // E * t
+        myPoly->mask[1][1][0][0] = 0;   // F * xy
+        #endif
+
+    }
+
+    if (flags & TS00_X_NULL) {
+        printf(" using a NULL x vector\n");
+    }
+
+    if (flags & TS00_X_F32) {
+        printf(" using a psF32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_X_S32) {
+        printf(" using a psS32 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_X_F64) {
+        printf(" using a psF64 x vector\n");
+        x = psVectorCopy(NULL, xTruth, PS_TYPE_F64);
+    }
+
+
+    if (flags & TS00_Y_NULL) {
+        printf(" using a NULL y vector\n");
+    }
+
+    if (flags & TS00_Y_F32) {
+        printf(" using a psF32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_Y_S32) {
+        printf(" using a psS32 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_Y_F64) {
+        printf(" using a psF64 y vector\n");
+        y = psVectorCopy(NULL, yTruth, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_Z_NULL) {
+        printf(" using a NULL z vector\n");
+    }
+
+    if (flags & TS00_Z_F32) {
+        printf(" using a psF32 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_Z_S32) {
+        printf(" using a psS32 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_Z_F64) {
+        printf(" using a psF64 z vector\n");
+        z = psVectorCopy(NULL, zTruth, PS_TYPE_F64);
+    }
+
+    if (flags & TS00_T_NULL) {
+        printf(" using a NULL t vector\n");
+    }
+
+    if (flags & TS00_T_F32) {
+        printf(" using a psF32 t vector\n");
+        t = psVectorCopy(NULL, tTruth, PS_TYPE_F32);
+    }
+
+    if (flags & TS00_T_S32) {
+        printf(" using a psS32 t vector\n");
+        t = psVectorCopy(NULL, tTruth, PS_TYPE_S32);
+    }
+
+    if (flags & TS00_T_F64) {
+        printf(" using a psF64 t vector\n");
+        t = psVectorCopy(NULL, tTruth, PS_TYPE_F64);
+    }
+
+
+    if (flags & TS00_F_NULL) {
+        printf(" using a NULL f vector\n");
+    }
+
+    if (flags & TS00_F_F32) {
+        printf(" using a psF32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F32[numData/4] *= 2.0;
+            f->data.F32[numData/2] *= 2.0;
+            f->data.F32[3*numData/4] *= 2.0;
+        }
+    }
+
+    if (flags & TS00_F_S32) {
+        printf(" using a psS32 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_S32);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.S32[numData/4] *= 2.0;
+            f->data.S32[numData/2] *= 2.0;
+            f->data.S32[3*numData/4] *= 2.0;
+        }
+    }
+
+    if (flags & TS00_F_F64) {
+        printf(" using a psF64 f vector\n");
+        f = psVectorCopy(NULL, fTruth, PS_TYPE_F64);
+        // Set a few outliers in the data.
+        if (OUTLIERS && (flags & TS00_CLIP_FIT)) {
+            f->data.F64[numData/4] *= 2.0;
+            f->data.F64[numData/2] *= 2.0;
+            f->data.F64[3*numData/4] *= 2.0;
+        }
+    }
+
+    if (flags & TS00_FERR_NULL) {
+        printf(" using a NULL fErr vector\n");
+    }
+
+    if (flags & TS00_FERR_F32) {
+        printf(" using a psF32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F32[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_S32) {
+        printf(" using a psS32 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_S32);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.S32[i] = (psS32) YERR;
+        }
+    }
+
+    if (flags & TS00_FERR_F64) {
+        printf(" using a psF64 fErr vector\n");
+        fErr = psVectorAlloc(numData, PS_TYPE_F64);
+        fErr->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            fErr->data.F64[i] = YERR;
+        }
+    }
+
+    if (flags & TS00_MASK_NULL) {
+        printf(" using a NULL mask vector\n");
+    }
+
+    if (flags & TS00_MASK_U8) {
+        printf(" using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = 0;
+        }
+    }
+
+    if (flags & TS00_MASK_S32) {
+        printf(" using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        mask->n = numData;
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = 0;
+        }
+    }
+
+    psPolynomial4D *rc = NULL;
+    if (flags & TS00_CLIP_FIT) {
+        rc = psVectorClipFitPolynomial4D(myPoly, stats, mask, MASK_VALUE, f, fErr, x, y, z, t);
+    } else {
+        rc = psVectorFitPolynomial4D(myPoly, mask, MASK_VALUE, f, fErr, x, y, z, t);
+    }
+
+    if (rc == NULL) {
+        if (expectedRC == true) {
+            printf("TEST ERROR: the 4D polynomial fitting function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            printf("TEST ERROR: the 4D polynomial fitting function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<polyOrderX+1;i++) {
+                for (psS32 j=0;j<polyOrderY+1;j++) {
+                    for (psS32 k=0;k<polyOrderZ+1;k++) {
+                        for (psS32 l=0;l<polyOrderT+1;l++) {
+                            printf("Polynomial coefficient [%d][%d][%d][%d] is %0.1f\n",
+                                   i, j, k, l, myPoly->coeff[i][j][k][l]);
+                        }
+                    }
+                }
+            }
+        }
+
+        psVector *result = psPolynomial4DEvalVector(myPoly, xTruth, yTruth, zTruth, tTruth);
+        for (int i=0;i<numData; i++) {
+            // Skip the outliers.
+            if ((i == numData/4) || (i == numData/2) || (i == 3*numData/4)) {
+                continue;
+            }
+            psF32 expectData = fTruth->data.F64[i];
+            psF32 actualData = result->data.F64[i];
+
+            if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
+                printf("TEST ERROR: Fitted data %d: (%f), expected was (%f)\n",
+                       i, actualData, expectData);
+                testStatus = false;
+            } else {
+                if (VERBOSE) {
+                    printf("GOOD: Fitted data %d: (%.1f), expected was (%.1f)\n",
+                           i, actualData, expectData);
+                }
+            }
+        }
+        psFree(result);
+    }
+
+    psMemCheckCorruption(1);
+    psFree(myPoly);
+    psFree(mask);
+    psFree(x);
+    psFree(y);
+    psFree(z);
+    psFree(t);
+    psFree(f);
+    psFree(xTruth);
+    psFree(yTruth);
+    psFree(zTruth);
+    psFree(tTruth);
+    psFree(fTruth);
+    psFree(fErr);
+    psFree(stats);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    printFooter(stdout, "psMinimize functions", "4D Polynomial Fitting Functions", testStatus);
+
+    return (testStatus);
+}
+
+/*****************************************************************************
+We test a variety of polynomial fitting routines, types, and polynomial types
+here:
+    F32 tests: Ordinary polys, non-clip fit
+    F64 tests: Ordinary polys, non-clip fit
+    F32 tests: Chebyshev polys, non-clip fit
+    F64 tests: Chebyshev polys, non-clip fit
+    F32 tests: Ordinary polys, clip fit
+    F64 tests: Ordinary polys, clip fit
+    F32 tests: Chebyshev polys, clip fit
+    F64 tests: Chebyshev polys, clip fit
+ *****************************************************************************/
+int main()
+{
+    psBool testStatus = true;
+    psLogSetFormat("HLNM");
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("psVectorClipFitPolynomial4D", 0);
+    psTraceSetLevel("VectorFitPolynomial4DOrd", 0);
+    psTraceSetLevel("psVectorFitPolynomial4D", 0);
+
+    printPositiveTestHeader(stdout, "psMinimize functions: 4D Polynomial Fitting Functions", "");
+
+    //
+    // F32 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32 | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, true);
+
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_NULL
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_NULL | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F64
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+
+    //
+    // F64 tests: Ordinary polys, non-clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_NULL
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_NULL | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_NULL | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F32
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F32 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F32 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD, POLY_ORDER_X, POLY_ORDER_Y, POLY_ORDER_Z,
+                              POLY_ORDER_T, NUM_DATA, false);
+
+    //
+    // F32 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_NULL | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_NULL | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_NULL
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_NULL | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F64
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F32 | TS00_X_F32 | TS00_Y_F32 | TS00_Z_F32
+                              | TS00_T_F32 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+
+    //
+    // F64 tests: Ordinary polys, clip fit
+    //
+    // All Vectors non-NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, true);
+    // Some Vectors NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_NULL | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, true);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_NULL | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_NULL | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_NULL
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_NULL | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    // F-vector NULL
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_NULL | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_NULL | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    // Mismatch vector types
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F32 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F32 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F32 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F32
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F32 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_U8 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F32 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+    testStatus &= genericTest(TS00_MASK_S32 | TS00_FERR_F64 | TS00_X_F64 | TS00_Y_F64 | TS00_Z_F64
+                              | TS00_T_F64 | TS00_F_F64 | TS00_POLY_ORD | TS00_CLIP_FIT, POLY_ORDER_X, POLY_ORDER_Y,
+                              POLY_ORDER_Z, POLY_ORDER_T, NUM_DATA, false);
+
+    printf("Status: %x\n", testStatus);
+
+    printFooter(stdout, "psMinimize functions: 4D Polynomial Fitting Functions", "", testStatus);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomial.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomial.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomial.c	(revision 22322)
@@ -0,0 +1,353 @@
+/** tst_Func00.c
+*
+*    This routine must ensure that the psPolynomial structures are
+*    allocated and deallocated by the psPolynomialXXXlloc() procedures.
+*    It also calls the various psPolynomialXXXEval() procedures.
+*
+*    The F32 and F64 polynomials are tested for all orders (1 - 4) and for
+*    both ordinary and chebyshev polynomials.
+*
+*    NOTE: This test code requries the stdout file to verify that the results
+*    are good.
+*
+*    XXX: Modify these tests so that polynomials with a variety of different
+*    orders are created.
+* 
+*    XXX: Compare to FLT_EPSILON
+* 
+*    @version $Revision: 1.1 $  $Name: not supported by cvs2svn $
+*    @date $Date: 2006-02-02 21:05:51 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*
+*****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+// Defines
+#define ORDER    3
+
+static psS32 testPolynomial1DAlloc(void);
+static psS32 testPolynomial2DAlloc(void);
+static psS32 testPolynomial3DAlloc(void);
+static psS32 testPolynomial4DAlloc(void);
+
+testDescription tests[] = {
+                              {testPolynomial1DAlloc,578,"psPolynomial1DAlloc",0,false},
+                              {testPolynomial2DAlloc,578,"psPolynomial2DAlloc",0,false},
+                              {testPolynomial3DAlloc,578,"psPolynomial3DAlloc",0,false},
+                              {testPolynomial4DAlloc,578,"psPolynomial4DAlloc",0,false},
+                              {NULL}
+                          };
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+
+    return !runTestSuite(stderr,"psPolynomialXD", tests, argc, argv);
+}
+
+
+// This test will allocate a 1D polynomial and verify the structure allocated
+psS32 testPolynomial1DAlloc(void)
+{
+    psPolynomial1D*  my1DPoly  = NULL;
+
+    // Allocate polynomial
+    my1DPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, ORDER);
+    // Verify structure allocated
+    if(my1DPoly == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL not expected");
+        return 1;
+    }
+    // Verify polynomial structure members set properly
+    if(my1DPoly->nX != ORDER) {
+        psError(PS_ERR_UNKNOWN,true,"Number of terms %d not as expected %d",
+                my1DPoly->nX, ORDER);
+        return 2;
+    }
+    if(my1DPoly->type != PS_POLYNOMIAL_ORD) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
+                my1DPoly->type, PS_POLYNOMIAL_ORD);
+        return 3;
+    }
+    for(psS32 i = 0; i < ORDER+1; i++) {
+        if(my1DPoly->coeff[i] != 0.0) {
+            psError(PS_ERR_UNKNOWN,true,"Coeff[%d] %lg not as expected %lg",
+                    i, my1DPoly->coeff[i], 0.0);
+            return 4;
+        }
+        if(my1DPoly->coeffErr[i] != 0.0) {
+            psError(PS_ERR_UNKNOWN,true,"CoeffErr[%d] %lg not as expected %lg",
+                    i, my1DPoly->coeffErr[i], 0.0);
+            return 5;
+        }
+        if(my1DPoly->mask[i] != 0) {
+            psError(PS_ERR_UNKNOWN,true,"Mask[%d] %d not as expected %d",
+                    i, my1DPoly->mask[i], 0);
+            return 6;
+        }
+    }
+    psFree(my1DPoly);
+
+    if (0) {
+        // Attempt to allocate with negative order
+        psLogMsg(__func__,PS_LOG_INFO,"Following should generate error msg for negative terms");
+        if(psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, -1) != NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Returned structure but expected NULL");
+            return 7;
+        }
+    }
+
+    return 0;
+}
+
+// This test will allocate a 2D polynomial and verify the structure allocated
+psS32 testPolynomial2DAlloc(void)
+{
+    psPolynomial2D* my2DPoly = NULL;
+
+    // Allocate polynomial
+    my2DPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, ORDER,ORDER+1);
+    // Verify structure allocated
+    if(my2DPoly == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL not expected");
+        return 1;
+    }
+    // Verify polynomial structure members set properly
+    if(my2DPoly->nX != ORDER) {
+        psError(PS_ERR_UNKNOWN,true,"Number of terms %d not as expected %d",
+                my2DPoly->nX, ORDER);
+        return 2;
+    }
+    // Verify polynomial structure members set properly
+    if(my2DPoly->nY != ORDER+1) {
+        psError(PS_ERR_UNKNOWN,true,"Number of terms %d not as expected %d",
+                my2DPoly->nY, ORDER+1);
+        return 3;
+    }
+    if(my2DPoly->type != PS_POLYNOMIAL_ORD) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
+                my2DPoly->type, PS_POLYNOMIAL_ORD);
+        return 4;
+    }
+
+    for(psS32 i = 0; i < ORDER+1; i++) {
+        for(psS32 j = 0; j < ORDER+2; j++) {
+            if(fabs(my2DPoly->coeff[i][j]) > FLT_EPSILON) {
+                psError(PS_ERR_UNKNOWN,true,"Coeff[%d][%d] %lg not as expected %lg",
+                        i, j, my2DPoly->coeff[i][j], 0.0);
+                return 5;
+            }
+            if(my2DPoly->coeffErr[i][j] != 0.0) {
+                psError(PS_ERR_UNKNOWN,true,"CoeffErr[%d][%d] %lg not as expected %lg",
+                        i, j, my2DPoly->coeffErr[i][j], 0.0);
+                return 6;
+            }
+            if(my2DPoly->mask[i][j] != 0) {
+                psError(PS_ERR_UNKNOWN,true,"Mask[%d][%d] %d not as expected %d",
+                        i, j, my2DPoly->mask[i][j], 0);
+                return 7;
+            }
+        }
+    }
+    psFree(my2DPoly);
+
+    if (0) {
+        // Attempt to allocate with negative order
+        psLogMsg(__func__,PS_LOG_INFO,"Following should generate error msg for negative terms");
+        if(psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, -1, 1) != NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Returned structure but expected NULL");
+            return 8;
+        }
+        // Attempt to allocate with negative order
+        psLogMsg(__func__,PS_LOG_INFO,"Following should generate error msg for negative terms");
+        if(psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 1, -1) != NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Returned structure but expected NULL");
+            return 9;
+        }
+    }
+    return 0;
+}
+
+// This test will allocate a 3D polynomial and verify the structure allocated
+psS32 testPolynomial3DAlloc(void)
+{
+    psPolynomial3D* my3DPoly = NULL;
+
+    // Allocate polynomial
+    my3DPoly = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, ORDER, ORDER+1, ORDER+2);
+    // Verify structure allocated
+    if(my3DPoly == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL not expected");
+        return 1;
+    }
+    // Verify polynomial structure members set properly
+    if(my3DPoly->nX != ORDER) {
+        psError(PS_ERR_UNKNOWN,true,"Number of terms %d not as expected %d",
+                my3DPoly->nX, ORDER);
+        return 2;
+    }
+    // Verify polynomial structure members set properly
+    if(my3DPoly->nY != ORDER+1) {
+        psError(PS_ERR_UNKNOWN,true,"Number of terms %d not as expected %d",
+                my3DPoly->nY, ORDER+1);
+        return 3;
+    }
+    // Verify polynomial structure members set properly
+    if(my3DPoly->nZ != ORDER+2) {
+        psError(PS_ERR_UNKNOWN,true,"Number of terms %d not as expected %d",
+                my3DPoly->nZ, ORDER+2);
+        return 4;
+    }
+    if(my3DPoly->type != PS_POLYNOMIAL_ORD) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
+                my3DPoly->type, PS_POLYNOMIAL_ORD);
+        return 5;
+    }
+    for(psS32 i = 0; i < ORDER+1; i++) {
+        for(psS32 j = 0; j < ORDER+2; j++) {
+            for(psS32 k = 0; k < ORDER+3; k++) {
+                if(my3DPoly->coeff[i][j][k] != 0.0) {
+                    psError(PS_ERR_UNKNOWN,true,"Coeff[%d][%d][%d] %lg not as expected %lg",
+                            i, j, k, my3DPoly->coeff[i][j][k], 0.0);
+                    return 6;
+                }
+                if(my3DPoly->coeffErr[i][j][k] != 0.0) {
+                    psError(PS_ERR_UNKNOWN,true,"CoeffErr[%d][%d][%d] %lg not as expected %lg",
+                            i, j, k, my3DPoly->coeffErr[i][j][k], 0.0);
+                    return 7;
+                }
+                if(my3DPoly->mask[i][j][k] != 0) {
+                    psError(PS_ERR_UNKNOWN,true,"Mask[%d][%d] %d not as expected %d",
+                            i, j, k, my3DPoly->mask[i][j][k], 0);
+                    return 8;
+                }
+            }
+        }
+    }
+    psFree(my3DPoly);
+
+    if (0) {
+        // Attempt to allocate with negative order
+        psLogMsg(__func__,PS_LOG_INFO,"Following should generate error msg for negative terms");
+        if(psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, -1, 1, 1) != NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Returned structure but expected NULL");
+            return 9;
+        }
+        // Attempt to allocate with negative order
+        psLogMsg(__func__,PS_LOG_INFO,"Following should generate error msg for negative terms");
+        if(psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, 1, -1, 1) != NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Returned structure but expected NULL");
+            return 10;
+        }
+        // Attempt to allocate with negative order
+        psLogMsg(__func__,PS_LOG_INFO,"Following should generate error msg for negative terms");
+        if(psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, 1, 1, -1) != NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Returned structure but expected NULL");
+            return 11;
+        }
+    }
+
+    return 0;
+}
+
+// This test will allocate a 4D polynomial and verify the structure allocated
+psS32 testPolynomial4DAlloc(void)
+{
+    psPolynomial4D* my4DPoly = NULL;
+
+    // Allocate polynomial
+    my4DPoly = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, ORDER,ORDER+1,ORDER+2,ORDER+3);
+    // Verify structure allocated
+    if(my4DPoly == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned NULL not expected");
+        return 1;
+    }
+    // Verify polynomial structure members set properly
+    if(my4DPoly->nX != ORDER) {
+        psError(PS_ERR_UNKNOWN,true,"Number of terms %d not as expected %d",
+                my4DPoly->nX, ORDER);
+        return 2;
+    }
+    // Verify polynomial structure members set properly
+    if(my4DPoly->nY != ORDER+1) {
+        psError(PS_ERR_UNKNOWN,true,"Number of terms %d not as expected %d",
+                my4DPoly->nX, ORDER+1);
+        return 3;
+    }
+    // Verify polynomial structure members set properly
+    if(my4DPoly->nZ != ORDER+2) {
+        psError(PS_ERR_UNKNOWN,true,"Number of terms %d not as expected %d",
+                my4DPoly->nZ, ORDER+2);
+        return 4;
+    }
+    // Verify polynomial structure members set properly
+    if(my4DPoly->nT != ORDER+3) {
+        psError(PS_ERR_UNKNOWN,true,"Number of terms %d not as expected %d",
+                my4DPoly->nT, ORDER+3);
+        return 5;
+    }
+    if(my4DPoly->type != PS_POLYNOMIAL_ORD) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
+                my4DPoly->type, PS_POLYNOMIAL_ORD);
+        return 6;
+    }
+    for(psS32 i = 0; i < ORDER+1; i++) {
+        for(psS32 j = 0; j < ORDER+2; j++) {
+            for(psS32 k = 0; k < ORDER+3; k++) {
+                for(psS32 l = 0; l < ORDER+4; l++) {
+                    if(my4DPoly->coeff[i][j][k][l] != 0.0) {
+                        psError(PS_ERR_UNKNOWN,true,"Coeff[%d][%d][%d][%d] %lg not as expected %lg",
+                                i, j, k, l, my4DPoly->coeff[i][j][k][l], 0.0);
+                        return 7;
+                    }
+                    if(my4DPoly->coeffErr[i][j][k][l] != 0.0) {
+                        psError(PS_ERR_UNKNOWN,true,"CoeffErr[%d][%d][%d][l] %lg not as expected %lg",
+                                i, j, k, l, my4DPoly->coeffErr[i][j][k][l], 0.0);
+                        return 8;
+                    }
+                    if(my4DPoly->mask[i][j][k][l] != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"Mask[%d][%d][%d][%d] %d not as expected %d",
+                                i, j, k, l, my4DPoly->mask[i][j][k][l], 0);
+                        return 9;
+                    }
+                }
+            }
+        }
+    }
+    psFree(my4DPoly);
+
+    if (0) {
+        // Attempt to allocate with negative order
+        psLogMsg(__func__,PS_LOG_INFO,"Following should generate error msg for negative terms");
+        if(psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, -1, 1, 1, 1) != NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Returned structure but expected NULL");
+            return 10;
+        }
+        // Attempt to allocate with negative order
+        psLogMsg(__func__,PS_LOG_INFO,"Following should generate error msg for negative terms");
+        if(psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 1, -1, 1, 1) != NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Returned structure but expected NULL");
+            return 11;
+        }
+        // Attempt to allocate with negative order
+        psLogMsg(__func__,PS_LOG_INFO,"Following should generate error msg for negative terms");
+        if(psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 1, 1, -1, 1) != NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Returned structure but expected NULL");
+            return 12;
+        }
+        // Attempt to allocate with negative order
+        psLogMsg(__func__,PS_LOG_INFO,"Following should generate error msg for negative terms");
+        if(psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 1, 1, 1, -1) != NULL) {
+            psError(PS_ERR_UNKNOWN,true,"Returned structure but expected NULL");
+            return 13;
+        }
+    }
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval1D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval1D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval1D.c	(revision 22322)
@@ -0,0 +1,197 @@
+/** tst_psFunc08.c
+*
+*  This test driver will exercise the psPolynomialXDEval functions for both
+*  ORD and CHEB type polynomials.
+*
+*  @version  $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2006-02-24 23:43:15 $
+*
+*  XXX: Probably should test single- and multi-dimensional polynomials in
+*  which one diminsion is constant (n == 1).
+*
+*  XXX: define ORDERS, not TERMS
+*
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*
+***************************************************************************/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define  TERMS        4
+#define  TESTPOINTS   5
+#define  ERROR_TOL    0.001
+
+static psS32 testPoly1DEval(void);
+static psS32 testPoly1DEvalVector(void);
+
+testDescription tests[] = {
+                              {testPoly1DEval,000,"psPolynomial1DEval",0,false},
+                              {testPoly1DEvalVector,000,"psPolynomial1DEvalVector",0,false},
+                              {NULL}
+                          };
+
+psF32 poly1DCoeff[TERMS]        = { -4.3, 2.3, -3.2, 1.9};
+psF64 Dpoly1DCoeff[TERMS]        = { -4.3, 2.3, -3.2, 1.9};
+psF32 poly1DMask[TERMS]         = {    0,   0,    1,   0  };
+
+psF32 poly1DXValue[TESTPOINTS]   = { 2.550000,    -9.8000,   0.00134,   -12.2500,   0.000};
+psF64 Dpoly1DXValue[TESTPOINTS]  = { 2.550000,    -9.8000,   0.00134,   -12.2500,   0.000};
+psF32 poly1DXResult[TESTPOINTS]  = {33.069613, -1815.1048, -4.296918, -3525.1797, -4.3000};
+psF64 Dpoly1DXResult[TESTPOINTS] = {33.069613, -1815.1048, -4.296918, -3525.1797, -4.3000};
+
+psF32 poly1DXChebValue[TESTPOINTS]   = { -0.99,    -0.33,     0.125,    0.564,    0.875};
+psF64 Dpoly1DXChebValue[TESTPOINTS]  = { -0.99,    -0.33,     0.125,    0.564,    0.875};
+psF32 poly1DXChebResult[TESTPOINTS]  = { -1.401196, 1.016252, 0.257813, 0.089625, 1.429688};
+psF64 Dpoly1DXChebResult[TESTPOINTS] = { -1.401196, 1.016252, 0.257813, 0.089625, 1.429688};
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+
+    return !runTestSuite(stderr,"psPolynomialXDEval", tests, argc, argv);
+}
+
+// This test will verify operation of 1D polynomial evaluation
+psS32 testPoly1DEval(void)
+{
+    psF64  result;
+    psF64  resultCheb;
+
+    // Allocate polynomial structure
+    psPolynomial1D*  polyOrd = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, TERMS-1);
+    psPolynomial1D*  polyCheb = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1);
+    // Set polynomial members
+    for(psS32 i = 0; i < TERMS; i++) {
+        polyOrd->coeff[i] = poly1DCoeff[i];
+        polyOrd->mask[i]  = poly1DMask[i];
+        polyCheb->coeff[i] = 1.0;
+        polyCheb->mask[i]  = poly1DMask[i];
+    }
+    // Evaluate test points and verify results
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        result = psPolynomial1DEval(polyOrd,poly1DXValue[i]);
+        if(fabs(poly1DXResult[i]-result) > ERROR_TOL ) {
+            psError(PS_ERR_UNKNOWN,true,"Evaluated value %g not as expected %g",
+                    result, poly1DXResult[i]);
+            return i;
+        }
+        resultCheb = psPolynomial1DEval(polyCheb,poly1DXChebValue[i]);
+        if(fabs(poly1DXChebResult[i]-resultCheb) > ERROR_TOL ) {
+            psError(PS_ERR_UNKNOWN,true,"Evaluated Chebyshev value %lg not as expected %lg",
+                    resultCheb, poly1DXChebResult[i]);
+            return 5*i;
+        }
+    }
+    psFree(polyOrd);
+    psFree(polyCheb);
+
+    // Allocate polynomial with invalid type
+    polyOrd = psPolynomial1DAlloc(99, TERMS-1);
+    // Attempt to evaluation invalid polynomial type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message invalid type");
+    result = psPolynomial1DEval(polyOrd,0.0);
+    if ( !isnan(result) ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for invalid polynomial type");
+        return 20;
+    }
+    psFree(polyOrd);
+
+    return 0;
+}
+
+
+psS32 testPoly1DEvalVector(void)
+{
+    // Allocate polynomial
+    psPolynomial1D* polyOrd = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, TERMS-1);
+    psPolynomial1D* polyCheb = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1);
+
+    // Set polynomial members
+    for(psS32 i = 0; i < TERMS; i++) {
+        polyOrd->coeff[i] = poly1DCoeff[i];
+        polyOrd->mask[i]  = poly1DMask[i];
+        polyCheb->coeff[i] = 1.0;
+        polyCheb->mask[i]  = poly1DMask[i];
+    }
+
+    // Create input vectors
+    psVector* inputOrd = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputCheb = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        inputOrd->data.F64[i] = poly1DXValue[i];
+        inputCheb->data.F64[i] = poly1DXChebValue[i];
+        inputOrd->n++;
+        inputCheb->n++;
+    }
+
+    // Evaluate the vectors
+    psVector* outputOrd = psPolynomial1DEvalVector(polyOrd, inputOrd);
+    if(outputOrd == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Unexpected return of NULL.");
+        return 1;
+    }
+    if(outputOrd->type.type != PS_TYPE_F64) {
+        psError(PS_ERR_UNKNOWN,true,"Output vector of type %d expected %d",
+                outputOrd->type.type, PS_TYPE_F64);
+        return 2;
+    }
+    psVector* outputCheb = psPolynomial1DEvalVector(polyCheb, inputCheb);
+    if(outputCheb == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Unexpected return of NULL.");
+        return 1;
+    }
+    if(outputCheb->type.type != PS_TYPE_F64) {
+        psError(PS_ERR_UNKNOWN,true,"Output vector of type %d expected %d",
+                outputCheb->type.type, PS_TYPE_F64);
+        return 2;
+    }
+
+    // Verify the results
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        if(fabs(poly1DXResult[i]-outputOrd->data.F64[i]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"Result[%d] %lg not equal to expected %lg",
+                    i, outputOrd->data.F64[i], poly1DXResult[i]);
+            return i*5;
+        }
+        if(fabs(poly1DXChebResult[i]-outputCheb->data.F64[i]) > ERROR_TOL) {
+            psError(PS_ERR_UNKNOWN,true,"ResultCheb[%d] %lg not equal to expected %lg",
+                    i, outputCheb->data.F64[i], poly1DXChebResult[i]);
+            return i*10;
+        }
+    }
+
+    // Attempt to invoke function with null polynomial
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL polynomial");
+    if(psPolynomial1DEvalVector(NULL, inputOrd) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Return of NULL expected for NULL polynomial");
+        return 60;
+    }
+
+    // Attempt to invoke function with null input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input vector");
+    if(psPolynomial1DEvalVector(polyOrd,NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Return of NULL expected for NULL input vector");
+        return 61;
+    }
+
+    // Attempt to invoke function with a non F64 type input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid input type");
+    inputOrd->type.type = PS_TYPE_U8;
+    if(psPolynomial1DEvalVector(polyOrd,inputOrd) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Return NULL expected for non-F64 input vector");
+        return 62;
+    }
+    inputOrd->type.type = PS_TYPE_F64;
+
+    psFree(inputOrd);
+    psFree(inputCheb);
+    psFree(outputOrd);
+    psFree(outputCheb);
+    psFree(polyOrd);
+    psFree(polyCheb);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval2D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval2D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval2D.c	(revision 22322)
@@ -0,0 +1,247 @@
+/** tst_psFunc09.c
+*
+*  This test driver will exercise the psPolynomialXDEval functions for both
+*  ORD and CHEB type polynomials.
+*
+*  @version  $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2006-02-24 23:43:15 $
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*
+***************************************************************************/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define  TERMS        4
+#define  TESTPOINTS   5
+#define  ERROR_TOL    0.001
+
+static psS32 testPoly2DEval(void);
+static psS32 testPoly2DEvalVector(void);
+
+testDescription tests[] = {
+                              {testPoly2DEval,583,"psPolynomial2DEval",true,false},
+                              {testPoly2DEvalVector,000,"psPolynomial2DEvalVector",true,false},
+                              {NULL}
+                          };
+
+psF32 poly2DCoeff[TERMS][TERMS]   = {  { -4.3,  2.3, -3.2,  1.9},
+                                       {  1.2,  0.7, -0.3,  1.3},
+                                       {  0.4, -2.9,  0.8, -3.1},
+                                       { -1.1,  2.1,  1.9,  0.6}
+                                    };
+psF64 Dpoly2DCoeff[TERMS][TERMS]  = {  { -4.3,  2.3, -3.2,  1.9},
+                                       {  1.2,  0.7, -0.3,  1.3},
+                                       {  0.4, -2.9,  0.8, -3.1},
+                                       { -1.1,  2.1,  1.9,  0.6}
+                                    };
+psF32 poly2DMask[TERMS][TERMS]    = {  {  0,    0,    0,    1},
+                                       {  0,    0,    1,    0},
+                                       {  1,    0,    0,    0},
+                                       {  0,    0,    0,    1}
+                                    };
+
+psF32 poly2DXYValue[TESTPOINTS][2] = {  {  1.40,  0.55},
+                                        { -0.55,  1.40},
+                                        {  0.00,  2.34},
+                                        { -0.88,  0.00},
+                                        {  3.45, -0.78}
+                                     };
+psF32 Dpoly2DXYValue[TESTPOINTS][2] = {  {  1.40,  0.55},
+                                      { -0.55,  1.40},
+                                      {  0.00,  2.34},
+                                      { -0.88,  0.00},
+                                      {  3.45, -0.78}
+                                      };
+psF32 poly2DResult[TESTPOINTS] = {  -3.415938, -14.765687, -16.43992, -4.606381, -22.650702 };
+psF64 Dpoly2DResult[TESTPOINTS] = {  -3.415938, -14.765687, -16.43992, -4.606381, -22.650702 };
+
+psF32 poly2DXYChebValue[TESTPOINTS][2] = {  {  0.500,  0.500},
+        {  0.000,  0.250},
+        { -0.250,  0.000},
+        {  0.990,  0.150},
+        {  0.333, -0.666}
+                                         };
+psF32 Dpoly2DXYChebValue[TESTPOINTS][2] = {  {  0.500,  0.500},
+        {  0.000,  0.250},
+        { -0.250,  0.000},
+        {  0.990,  0.150},
+        {  0.333, -0.666}
+                                          };
+psF32 poly2DChebResult[TESTPOINTS] = {  0.750000, 1.687500, 0.625000, -0.113040, 0.386786 };
+psF32 Dpoly2DChebResult[TESTPOINTS] = {  0.750000, 1.687500, 0.625000, -0.113040, 0.386786 };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+
+    return !runTestSuite(stderr,"psPolynomialXDEval", tests, argc, argv);
+}
+
+// This test will verify operation of 1D polynomial evaluation
+psS32 testPoly2DEval(void)
+{
+    psF64  result;
+    psF64  resultCheb;
+    psBool testStatus = true;
+
+    // Allocate polynomial structure
+    psPolynomial2D*  polyOrd = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1);
+    psPolynomial2D*  polyCheb = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1);
+    // Set polynomial members
+    for(psS32 i = 0; i < TERMS; i++) {
+        for(psS32 j = 0; j < TERMS; j++) {
+            polyOrd->coeff[i][j] = poly2DCoeff[i][j];
+            polyOrd->mask[i][j]  = poly2DMask[i][j];
+            polyCheb->coeff[i][j] = 1.0;
+            polyCheb->mask[i][j]  = poly2DMask[i][j];
+        }
+    }
+    // Evaluate test points and verify results
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        result = psPolynomial2DEval(polyOrd,Dpoly2DXYValue[i][0],Dpoly2DXYValue[i][1]);
+        if(fabs(Dpoly2DResult[i]-result) > ERROR_TOL ) {
+            printf("TEST ERROR: Evaluated value %f, should be %f\n", result, Dpoly2DResult[i]);
+            testStatus = false;
+        }
+        resultCheb = psPolynomial2DEval(polyCheb,Dpoly2DXYChebValue[i][0],Dpoly2DXYChebValue[i][1]);
+        if(fabs(Dpoly2DChebResult[i]-resultCheb) > ERROR_TOL ) {
+            printf("TEST ERROR: Evaluated value %f, should be %f\n", resultCheb, Dpoly2DChebResult[i]);
+            testStatus = false;
+        }
+    }
+    psFree(polyOrd);
+    psFree(polyCheb);
+
+    // Allocate polynomial with invalid type
+    polyOrd = psPolynomial2DAlloc(99, TERMS-1, TERMS-1);
+    // Attempt to evaluation invalid polynomial type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message invalid type");
+    result = psPolynomial2DEval(polyOrd,0.0, 0.0);
+    if ( !isnan(result) ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for invalid polynomial type");
+        testStatus = false;
+    }
+    psFree(polyOrd);
+
+    return(testStatus);
+}
+
+psS32 testPoly2DEvalVector(void)
+{
+    psBool testStatus = true;
+    // Allocate polynomial
+    psPolynomial2D* polyOrd = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1);
+    psPolynomial2D* polyCheb = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1);
+
+    // Set polynomial members
+    for(psS32 i = 0; i < TERMS; i++) {
+        for(psS32 j = 0; j < TERMS; j++) {
+            polyOrd->coeff[i][j] = poly2DCoeff[i][j];
+            polyOrd->mask[i][j]  = poly2DMask[i][j];
+            polyCheb->coeff[i][j] = 1.0;
+            polyCheb->mask[i][j]  = poly2DMask[i][j];
+        }
+    }
+
+    // Create input vectors
+    psVector* inputOrdX  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputOrdY  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputChebX = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputChebY = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        inputOrdX->data.F64[i]  = poly2DXYValue[i][0];
+        inputOrdY->data.F64[i]  = poly2DXYValue[i][1];
+        inputChebX->data.F64[i] = poly2DXYChebValue[i][0];
+        inputChebY->data.F64[i] = poly2DXYChebValue[i][1];
+        inputOrdX->n++;
+        inputOrdY->n++;
+        inputChebX->n++;
+        inputChebY->n++;
+    }
+
+    // Evaluate the vectors
+    psVector* outputOrd = psPolynomial2DEvalVector(polyOrd, inputOrdX, inputOrdY);
+    if(outputOrd == NULL) {
+        printf("TEST ERROR: Unexpected return of NULL.\n");
+        testStatus = false;
+    }
+    if(outputOrd->type.type != PS_TYPE_F64) {
+        printf("TEST ERROR: Output vector of type %d expected %d\n", outputOrd->type.type, PS_TYPE_F64);
+        testStatus = false;
+    }
+    psVector* outputCheb = psPolynomial2DEvalVector(polyCheb, inputChebX, inputChebY);
+    if(outputCheb == NULL) {
+        printf("TEST ERROR: Unexpected return of NULL.\n");
+        testStatus = false;
+    }
+    if(outputCheb->type.type != PS_TYPE_F64) {
+        printf("TEST ERROR: Output vector of type %d expected %d.\n", outputCheb->type.type, PS_TYPE_F64);
+        testStatus = false;
+    }
+
+    // Verify the results
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        if(fabs(poly2DResult[i]-outputOrd->data.F64[i]) > ERROR_TOL) {
+            printf("TEST ERROR: Result[%d] %lg not equal to expected %lg.\n",
+                   i, outputOrd->data.F64[i], poly2DResult[i]);
+            testStatus = false;
+        }
+        if(fabs(poly2DChebResult[i]-outputCheb->data.F64[i]) > ERROR_TOL) {
+            printf("TEST ERROR: ResultCheb[%d] %lg not equal to expected %lg.\n",
+                   i, outputCheb->data.F64[i], poly2DChebResult[i]);
+            testStatus = false;
+        }
+    }
+
+    // Attempt to invoke function with null polynomial
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL polynomial");
+    if(psPolynomial2DEvalVector(NULL, inputOrdX, inputOrdY) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL polynomial.\n");
+        testStatus = false;
+    }
+
+    // Attempt to invoke function with null input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input vector");
+    if(psPolynomial2DEvalVector(polyOrd,NULL,inputOrdY) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL input vector.\n");
+        testStatus = false;
+    }
+    // Attempt to invoke function with null input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input vector");
+    if(psPolynomial2DEvalVector(polyOrd,inputOrdX,NULL) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL input vector.\n");
+        testStatus = false;
+    }
+
+    // Attempt to invoke function with a non F64 type input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid input type");
+    inputOrdX->type.type = PS_TYPE_U8;
+    if(psPolynomial2DEvalVector(polyOrd,inputOrdX, inputOrdY) != NULL) {
+        printf("TEST ERROR: Return NULL expected for non-F64 input vector.\n");
+        testStatus = false;
+    }
+    inputOrdX->type.type = PS_TYPE_F64;
+    // Attempt to invoke function with a non F64 type input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid input type");
+    inputOrdY->type.type = PS_TYPE_U8;
+    if(psPolynomial2DEvalVector(polyOrd,inputOrdX, inputOrdY) != NULL) {
+        printf("TEST ERROR: Return NULL expected for non-F64 input vector.\n");
+        testStatus = false;
+    }
+    inputOrdY->type.type = PS_TYPE_F64;
+
+    psFree(inputOrdX);
+    psFree(inputOrdY);
+    psFree(inputChebX);
+    psFree(inputChebY);
+    psFree(outputOrd);
+    psFree(outputCheb);
+    psFree(polyOrd);
+    psFree(polyCheb);
+
+    return(testStatus);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval3D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval3D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval3D.c	(revision 22322)
@@ -0,0 +1,330 @@
+/** tst_psFunc10.c
+*
+*  This test driver will exercise the psPolynomialXDEval functions for both
+*  ORD and CHEB type polynomials.
+*
+*  @version  $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2006-02-24 23:43:15 $
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*
+***************************************************************************/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define  TERMS        4
+#define  TESTPOINTS   5
+#define  ERROR_TOL    0.001
+
+static psS32 testPoly3DEval(void);
+static psS32 testPoly3DEvalVector(void);
+
+testDescription tests[] = {
+                              {testPoly3DEval,583,"psPolynomial3DEval",true,false},
+                              {testPoly3DEvalVector,000,"psPolynomial3DEvalVector",true,false},
+                              {NULL}
+                          };
+
+psF32 poly3DCoeff[TERMS][TERMS][TERMS] = {  { { -1.1,  1.2, -1.3,  1.4},
+        {  1.5, -1.6,  1.7, -1.8},
+        {  0.1, -0.2,  0.3, -0.4},
+        { -0.5,  0.6, -0.7,  0.8}
+                                            },
+        { { -2.1,  2.2, -2.3,  2.4},
+          {  2.5, -2.6,  2.7, -2.8},
+          {  3.1, -3.2,  3.3, -3.4},
+          { -3.5,  3.6, -3.7,  3.8}
+        },
+        { { -4.1,  4.2, -4.3,  4.4},
+          {  4.5, -4.6,  4.7, -4.8},
+          {  5.1, -5.2,  5.3, -5.4},
+          { -5.5,  5.6, -5.7,  5.8}
+        },
+        { { -6.1,  6.2, -6.3,  6.4},
+          {  6.5, -6.6,  6.7, -6.8},
+          {  7.1, -7.2,  7.3, -7.4},
+          { -7.5,  7.6, -7.7,  7.8}
+        }
+                                         };
+psF64 Dpoly3DCoeff[TERMS][TERMS][TERMS] = {  { { -1.1,  1.2, -1.3,  1.4},
+        {  1.5, -1.6,  1.7, -1.8},
+        {  0.1, -0.2,  0.3, -0.4},
+        { -0.5,  0.6, -0.7,  0.8}
+                                             },
+        { { -2.1,  2.2, -2.3,  2.4},
+          {  2.5, -2.6,  2.7, -2.8},
+          {  3.1, -3.2,  3.3, -3.4},
+          { -3.5,  3.6, -3.7,  3.8}
+        },
+        { { -4.1,  4.2, -4.3,  4.4},
+          {  4.5, -4.6,  4.7, -4.8},
+          {  5.1, -5.2,  5.3, -5.4},
+          { -5.5,  5.6, -5.7,  5.8}
+        },
+        { { -6.1,  6.2, -6.3,  6.4},
+          {  6.5, -6.6,  6.7, -6.8},
+          {  7.1, -7.2,  7.3, -7.4},
+          { -7.5,  7.6, -7.7,  7.8}
+        }
+                                          };
+
+
+psF32 poly3DMask[TERMS][TERMS][TERMS]    = { {  {  0,    0,    0,    1},
+        {  0,    0,    1,    0},
+        {  1,    0,    0,    0},
+        {  0,    0,    0,    1}
+                                             },
+        {  {  1,    0,    0,    0},
+           {  1,    0,    0,    0},
+           {  1,    0,    0,    0},
+           {  1,    0,    0,    0}
+        },
+        {  {  0,    1,    0,    0},
+           {  0,    0,    1,    0},
+           {  0,    1,    0,    0},
+           {  0,    0,    1,    0}
+        },
+        {  {  1,    0,    0,    0},
+           {  0,    0,    0,    1},
+           {  1,    0,    0,    0},
+           {  0,    0,    0,    1}
+        },
+                                           };
+
+psF32 poly3DXYZValue[TESTPOINTS][3] = {  {  0.450, -0.780,  0.500},
+                                      {  0.297,  0.153, -0.354},
+                                      {  0.000,  0.153, -0.354},
+                                      {  0.297,  0.000, -0.354},
+                                      {  0.297,  0.153,  0.000}
+                                      };
+psF64 Dpoly3DXYZValue[TESTPOINTS][3] = {  {  0.450, -0.780,  0.500},
+                                       {  0.297,  0.153, -0.354},
+                                       {  0.000,  0.153, -0.354},
+                                       {  0.297,  0.000, -0.354},
+                                       {  0.297,  0.153,  0.000}
+                                       };
+psF32 poly3DResult[TESTPOINTS]  = { -1.298691, -2.011591, -1.359247, -2.548266, -1.139072};
+psF64 Dpoly3DResult[TESTPOINTS] = { -1.298691, -2.011591, -1.359247, -2.548266, -1.139072};
+
+
+psF32 poly3DXYZChebValue[TESTPOINTS][3] = {  {  0.000,  0.250, -0.250},
+        { -0.250,  0.000,  0.250},
+        {  0.250, -0.250,  0.000},
+        {  0.100, -0.300, -0.400},
+        {  0.990, -0.010,  0.500}
+                                          };
+psF64 Dpoly3DXYZChebValue[TESTPOINTS][3] = {  {  0.000,  0.250, -0.250},
+        { -0.250,  0.000,  0.250},
+        {  0.250, -0.250,  0.000},
+        {  0.100, -0.300, -0.400},
+        {  0.990, -0.010,  0.500}
+                                           };
+psF32 poly3DChebResult[TESTPOINTS]  = {  1.230469, 1.687500, 0.187500, -1.452707, 2.032344 };
+psF64 Dpoly3DChebResult[TESTPOINTS] = {  1.230469, 1.687500, 0.187500, -1.452707, 2.032344 };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+
+    return !runTestSuite(stderr,"psPolynomialXDEval", tests, argc, argv);
+}
+
+// This test will verify operation of 1D polynomial evaluation
+psS32 testPoly3DEval(void)
+{
+    psF64  result;
+    psF64  resultCheb;
+    psBool testStatus = true;
+
+    // Allocate polynomial structure
+    psPolynomial3D*  polyOrd = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1, TERMS-1);
+    psPolynomial3D*  polyCheb = psPolynomial3DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1, TERMS-1);
+    // Set polynomial members
+    for(psS32 i = 0; i < TERMS; i++) {
+        for(psS32 j = 0; j < TERMS; j++) {
+            for(psS32 k = 0; k < TERMS; k++) {
+                polyOrd->coeff[i][j][k] = Dpoly3DCoeff[i][j][k];
+                polyOrd->mask[i][j][k]  = poly3DMask[i][j][k];
+                polyCheb->coeff[i][j][k] = 1.0;
+                polyCheb->mask[i][j][k]  = poly3DMask[i][j][k];
+            }
+        }
+    }
+    // Evaluate test points and verify results
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        result = psPolynomial3DEval(polyOrd,Dpoly3DXYZValue[i][0],Dpoly3DXYZValue[i][1],
+                                    Dpoly3DXYZValue[i][2]);
+        if(fabs(Dpoly3DResult[i]-result) > ERROR_TOL ) {
+            printf("TEST ERROR: Evaluated value %lg not as expected %lg.\n",
+                   result, Dpoly3DResult[i]);
+            testStatus = false;
+        }
+        resultCheb = psPolynomial3DEval(polyCheb,Dpoly3DXYZChebValue[i][0],Dpoly3DXYZChebValue[i][1],
+                                        Dpoly3DXYZChebValue[i][2]);
+        if(fabs(Dpoly3DChebResult[i]-resultCheb) > ERROR_TOL ) {
+            printf("TEST ERROR: Evaluated Chebyshev value %lg not as expected %lg.\n",
+                   resultCheb, Dpoly3DChebResult[i]);
+            testStatus = false;
+        }
+    }
+    psFree(polyOrd);
+    psFree(polyCheb);
+
+    // Allocate polynomial with invalid type
+    polyOrd = psPolynomial3DAlloc(99, TERMS-1, TERMS-1, TERMS-1);
+    // Attempt to evaluation invalid polynomial type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message invalid type");
+    result = psPolynomial3DEval(polyOrd,0.0, 0.0, 0.0);
+    if ( !isnan(result) ) {
+        printf("TEST ERROR: Did not return NAN for invalid polynomial type.\n");
+        testStatus = false;
+    }
+    psFree(polyOrd);
+
+    return(testStatus);
+}
+
+psS32 testPoly3DEvalVector(void)
+{
+    psBool testStatus = true;
+    // Allocate polynomial
+    psPolynomial3D* polyOrd = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1, TERMS-1);
+    psPolynomial3D* polyCheb = psPolynomial3DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1, TERMS-1);
+
+    // Set polynomial members
+    for(psS32 i = 0; i < TERMS; i++) {
+        for(psS32 j = 0; j < TERMS; j++) {
+            for(psS32 k = 0; k < TERMS; k++) {
+                polyOrd->coeff[i][j][k] = Dpoly3DCoeff[i][j][k];
+                polyOrd->mask[i][j][k]  = poly3DMask[i][j][k];
+                polyCheb->coeff[i][j][k] = 1.0;
+                polyCheb->mask[i][j][k]  = poly3DMask[i][j][k];
+            }
+        }
+    }
+
+    // Create input vectors
+    psVector* inputOrdX  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputOrdY  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputOrdZ  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputChebX = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputChebY = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputChebZ = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        inputOrdX->data.F64[i]  = Dpoly3DXYZValue[i][0];
+        inputOrdY->data.F64[i]  = Dpoly3DXYZValue[i][1];
+        inputOrdZ->data.F64[i]  = Dpoly3DXYZValue[i][2];
+        inputChebX->data.F64[i] = Dpoly3DXYZChebValue[i][0];
+        inputChebY->data.F64[i] = Dpoly3DXYZChebValue[i][1];
+        inputChebZ->data.F64[i] = Dpoly3DXYZChebValue[i][2];
+        inputOrdX->n++;
+        inputOrdY->n++;
+        inputOrdZ->n++;
+        inputChebX->n++;
+        inputChebY->n++;
+        inputChebZ->n++;
+    }
+
+    // Evaluate the vectors
+    psVector* outputOrd = psPolynomial3DEvalVector(polyOrd,inputOrdX,inputOrdY,inputOrdZ);
+    if(outputOrd == NULL) {
+        printf("TEST ERROR: Unexpected return of NULL.\n");
+        testStatus = false;
+    }
+    if(outputOrd->type.type != PS_TYPE_F64) {
+        printf("TEST ERROR: Output vector of type %d expected %d.\n",
+               outputOrd->type.type, PS_TYPE_F64);
+        testStatus = false;
+    }
+    psVector* outputCheb = psPolynomial3DEvalVector(polyCheb,inputChebX,inputChebY,inputChebZ);
+    if(outputCheb == NULL) {
+        printf("TEST ERROR: Unexpected return of NULL.\n");
+        testStatus = false;
+    }
+    if(outputCheb->type.type != PS_TYPE_F64) {
+        printf("TEST ERROR: Output vector of type %d expected %d.\n",
+               outputCheb->type.type, PS_TYPE_F64);
+        testStatus = false;
+    }
+
+    // Verify the results
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        if(fabs(Dpoly3DResult[i]-outputOrd->data.F64[i]) > ERROR_TOL) {
+            printf("TEST ERROR: Result[%d] %lg not equal to expected %lg.\n",
+                   i, outputOrd->data.F64[i], Dpoly3DResult[i]);
+            testStatus = false;
+        }
+        if(fabs(Dpoly3DChebResult[i]-outputCheb->data.F64[i]) > ERROR_TOL) {
+            printf("TEST ERROR: ResultCheb[%d] %lg not equal to expected %lg.\n",
+                   i, outputCheb->data.F64[i], Dpoly3DChebResult[i]);
+            testStatus = false;
+        }
+    }
+
+    // Attempt to invoke function with null polynomial
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL polynomial");
+    if(psPolynomial3DEvalVector(NULL,inputOrdX,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL polynomial.\n");
+        testStatus = false;
+    }
+
+    // Attempt to invoke function with null input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input vector");
+    if(psPolynomial3DEvalVector(polyOrd,NULL,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL input vector.\n");
+        testStatus = false;
+    }
+    // Attempt to invoke function with null input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input vector");
+    if(psPolynomial3DEvalVector(polyOrd,inputOrdX,NULL,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL input vector.\n");
+        testStatus = false;
+    }
+    // Attempt to invoke function with null input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input vector");
+    if(psPolynomial3DEvalVector(polyOrd,inputOrdX,inputOrdY,NULL) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL input vector.\n");
+        testStatus = false;
+    }
+
+    // Attempt to invoke function with a non F64 type input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid input type");
+    inputOrdX->type.type = PS_TYPE_U8;
+    if(psPolynomial3DEvalVector(polyOrd,inputOrdX,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return NULL expected for non-F64 input vector.\n");
+        testStatus = false;
+    }
+    inputOrdX->type.type = PS_TYPE_F64;
+    // Attempt to invoke function with a non F64 type input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid input type");
+    inputOrdY->type.type = PS_TYPE_U8;
+    if(psPolynomial3DEvalVector(polyOrd,inputOrdX,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return NULL expected for non-F64 input vector.\n");
+        testStatus = false;
+    }
+    inputOrdY->type.type = PS_TYPE_F64;
+    // Attempt to invoke function with a non F64 type input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid input type");
+    inputOrdZ->type.type = PS_TYPE_U8;
+    if(psPolynomial3DEvalVector(polyOrd,inputOrdX,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return NULL expected for non-F64 input vector.\n");
+        testStatus = false;
+    }
+    inputOrdZ->type.type = PS_TYPE_F64;
+
+    psFree(inputOrdX);
+    psFree(inputOrdY);
+    psFree(inputOrdZ);
+    psFree(inputChebX);
+    psFree(inputChebY);
+    psFree(inputChebZ);
+    psFree(outputOrd);
+    psFree(outputCheb);
+    psFree(polyOrd);
+    psFree(polyCheb);
+
+    return(testStatus);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval4D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval4D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psPolynomialEval4D.c	(revision 22322)
@@ -0,0 +1,598 @@
+/** tst_psFunc11.c
+*
+*  This test driver will exercise the psPolynomialXDEval functions for both
+*  ORD and CHEB type polynomials.
+*
+*  @version  $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2006-02-24 23:43:15 $
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*
+***************************************************************************/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define  TERMS        4
+#define  TESTPOINTS   5
+#define  ERROR_TOL    0.001
+
+static psS32 testPoly4DEval(void);
+static psS32 testPoly4DEvalVector(void);
+
+testDescription tests[] = {
+                              {testPoly4DEval,583,"psPolynomial4DEval",true,false},
+                              {testPoly4DEvalVector,000,"psPolynomial4DEvalVector",true,false},
+                              {NULL}
+                          };
+
+psF32 poly4DCoeff[TERMS][TERMS][TERMS][TERMS] = {
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            }
+        };
+psF64 Dpoly4DCoeff[TERMS][TERMS][TERMS][TERMS] = {
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            },
+            {
+                {
+                    { -1.1,  1.2, -1.3,  1.4},
+                    {  1.5, -1.6,  1.7, -1.8},
+                    {  0.1, -0.2,  0.3, -0.4},
+                    { -0.5,  0.6, -0.7,  0.8}
+                },
+                {
+                    { -2.1,  2.2, -2.3,  2.4},
+                    {  2.5, -2.6,  2.7, -2.8},
+                    {  3.1, -3.2,  3.3, -3.4},
+                    { -3.5,  3.6, -3.7,  3.8}
+                },
+                { { -4.1,  4.2, -4.3,  4.4},
+                  {  4.5, -4.6,  4.7, -4.8},
+                  {  5.1, -5.2,  5.3, -5.4},
+                  { -5.5,  5.6, -5.7,  5.8}
+                },
+                { { -6.1,  6.2, -6.3,  6.4},
+                  {  6.5, -6.6,  6.7, -6.8},
+                  {  7.1, -7.2,  7.3, -7.4},
+                  { -7.5,  7.6, -7.7,  7.8}
+                }
+            }
+        };
+
+psF32 poly4DMask[TERMS][TERMS][TERMS][TERMS]    = {
+            {
+                {
+                    {  0,    0,    0,    1},
+                    {  0,    0,    1,    0},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0}
+                },
+                {
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0},
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                }
+            },
+            {
+                {
+                    {  0,    0,    0,    1},
+                    {  0,    0,    1,    0},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0}
+                },
+                {
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0},
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                }
+            },
+            {
+                {
+                    {  0,    0,    0,    1},
+                    {  0,    0,    1,    0},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0}
+                },
+                {
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0},
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                }
+            },
+            {
+                {
+                    {  0,    0,    0,    1},
+                    {  0,    0,    1,    0},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0},
+                    {  1,    0,    0,    0}
+                },
+                {
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0},
+                    {  0,    1,    0,    0},
+                    {  0,    0,    1,    0}
+                },
+                {
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1},
+                    {  1,    0,    0,    0},
+                    {  0,    0,    0,    1}
+                }
+            }
+        };
+
+psF32 poly4DWXYZValue[TESTPOINTS][4] = {
+                                           {  0.450, -0.780,  0.500, -0.123},
+                                           {  0.297,  0.153, -0.354,  0.000},
+                                           {  0.000,  0.153, -0.354,  0.321},
+                                           {  0.297,  0.000, -0.354,  0.321},
+                                           {  0.297,  0.153,  0.000,  0.321}
+                                       };
+psF64 Dpoly4DWXYZValue[TESTPOINTS][4] = {
+                                            {  0.450, -0.780,  0.500, -0.123},
+                                            {  0.297,  0.153, -0.354,  0.000},
+                                            {  0.000,  0.153, -0.354,  0.321},
+                                            {  0.297,  0.000, -0.354,  0.321},
+                                            {  0.297,  0.153,  0.000,  0.321}
+                                        };
+
+psF32 poly4DResult[TESTPOINTS]  = { -3.588753, -2.439566, -1.175955, -1.645497, -1.216915};
+psF64 Dpoly4DResult[TESTPOINTS]  = { -3.588753, -2.439566, -1.175955, -1.645497, -1.216915};
+
+psF32 poly4DWXYZChebValue[TESTPOINTS][4] = {
+            {  0.100,  0.000,  0.250, -0.250},
+            {  0.100, -0.250,  0.000,  0.250},
+            {  0.100,  0.250, -0.250,  0.000},
+            {  0.300,  0.200, -0.300, -0.400},
+            { -0.780,  0.990, -0.010,  0.500}
+        };
+psF64 Dpoly4DWXYZChebValue[TESTPOINTS][4] = {
+            {  0.100,  0.000,  0.250, -0.250},
+            {  0.100, -0.250,  0.000,  0.250},
+            {  0.100,  0.250, -0.250,  0.000},
+            {  0.300,  0.200, -0.300, -0.400},
+            { -0.780,  0.990, -0.010,  0.500}
+        };
+psF32 poly4DChebResult[TESTPOINTS]   = { -0.216563, -0.297000, -0.033000, 0.432198, 1.785601 };
+psF64 Dpoly4DChebResult[TESTPOINTS]  = { -0.216563, -0.297000, -0.033000, 0.432198, 1.785601 };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+
+    return !runTestSuite(stderr,"psPolynomialXDEval", tests, argc, argv);
+}
+
+// This test will verify operation of 4D polynomial evaluation
+psS32 testPoly4DEval(void)
+{
+    psBool testStatus = true;
+    // Allocate polynomial structure
+    psPolynomial4D*  polyOrd = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1, TERMS-1, TERMS-1);
+    psPolynomial4D*  polyCheb = psPolynomial4DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1, TERMS-1, TERMS-1);
+    // Set polynomial members
+    for(psS32 i = 0; i < TERMS; i++) {
+        for(psS32 j = 0; j < TERMS; j++) {
+            for(psS32 k = 0; k < TERMS; k++) {
+                for(psS32 l = 0; l < TERMS; l++) {
+                    polyOrd->coeff[i][j][k][l] = Dpoly4DCoeff[i][j][k][l];
+                    polyOrd->mask[i][j][k][l]  = poly4DMask[i][j][k][l];
+                    polyCheb->coeff[i][j][k][l] = 1.0;
+                    polyCheb->mask[i][j][k][l]  = poly4DMask[i][j][k][l];
+                }
+            }
+        }
+    }
+
+    // Evaluate test points and verify results
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        psF64 result = psPolynomial4DEval(polyOrd, Dpoly4DWXYZValue[i][0], Dpoly4DWXYZValue[i][1],
+                                          Dpoly4DWXYZValue[i][2], Dpoly4DWXYZValue[i][3]);
+        if(fabs(Dpoly4DResult[i]-result) > ERROR_TOL ) {
+            printf("TEST ERROR: Evaluated value %lg not as expected %lg.\n",
+                   result, Dpoly4DResult[i]);
+            testStatus = false;
+        }
+
+        psF64 resultCheb = psPolynomial4DEval(polyCheb, Dpoly4DWXYZChebValue[i][0], Dpoly4DWXYZChebValue[i][1],
+                                              Dpoly4DWXYZChebValue[i][2], Dpoly4DWXYZChebValue[i][3]);
+        if(fabs(Dpoly4DChebResult[i]-resultCheb) > ERROR_TOL ) {
+            printf("TEST ERROR: Evaluated Chebyshev value %lg not as expected %lg.\n",
+                   resultCheb, Dpoly4DChebResult[i]);
+            testStatus = false;
+        }
+    }
+
+    psFree(polyOrd);
+    psFree(polyCheb);
+
+    // Allocate polynomial with invalid type
+    polyOrd = psPolynomial4DAlloc(99, TERMS-1, TERMS-1, TERMS-1, TERMS-1);
+    // Attempt to evaluation invalid polynomial type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message invalid type");
+    psF64 result = psPolynomial4DEval(polyOrd,0.0, 0.0, 0.0, 0.0);
+    if ( !isnan(result) ) {
+        printf("TEST ERROR: Did not return NAN for invalid polynomial type");
+        testStatus = false;
+    }
+    psFree(polyOrd);
+
+    return(testStatus);
+}
+
+psS32 testPoly4DEvalVector(void)
+{
+    psBool testStatus = true;
+    // Allocate polynomial
+    psPolynomial4D* polyOrd = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, TERMS-1, TERMS-1, TERMS-1, TERMS-1);
+    psPolynomial4D* polyCheb = psPolynomial4DAlloc(PS_POLYNOMIAL_CHEB, TERMS-1, TERMS-1, TERMS-1, TERMS-1);
+
+    // Set polynomial members
+    for(psS32 i = 0; i < TERMS; i++) {
+        for(psS32 j = 0; j < TERMS; j++) {
+            for(psS32 k = 0; k < TERMS; k++) {
+                for(psS32 l = 0; l < TERMS; l++) {
+                    polyOrd->coeff[i][j][k][l] = Dpoly4DCoeff[i][j][k][l];
+                    polyOrd->mask[i][j][k][l]  = poly4DMask[i][j][k][l];
+                    polyCheb->coeff[i][j][k][l] = 1.0;
+                    polyCheb->mask[i][j][k][l]  = poly4DMask[i][j][k][l];
+                }
+            }
+        }
+    }
+
+    // Create input vectors
+    psVector* inputOrdW  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputOrdX  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputOrdY  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputOrdZ  = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputChebW = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputChebX = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputChebY = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    psVector* inputChebZ = psVectorAlloc(TESTPOINTS, PS_TYPE_F64);
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        inputOrdW->data.F64[i]  = Dpoly4DWXYZValue[i][0];
+        inputOrdX->data.F64[i]  = Dpoly4DWXYZValue[i][1];
+        inputOrdY->data.F64[i]  = Dpoly4DWXYZValue[i][2];
+        inputOrdZ->data.F64[i]  = Dpoly4DWXYZValue[i][3];
+        inputChebW->data.F64[i] = Dpoly4DWXYZChebValue[i][0];
+        inputChebX->data.F64[i] = Dpoly4DWXYZChebValue[i][1];
+        inputChebY->data.F64[i] = Dpoly4DWXYZChebValue[i][2];
+        inputChebZ->data.F64[i] = Dpoly4DWXYZChebValue[i][3];
+        inputOrdW->n++;
+        inputOrdX->n++;
+        inputOrdY->n++;
+        inputOrdZ->n++;
+        inputChebW->n++;
+        inputChebX->n++;
+        inputChebY->n++;
+        inputChebZ->n++;
+    }
+
+    // Evaluate the vectors
+    psVector* outputOrd = psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,inputOrdZ);
+    if(outputOrd == NULL) {
+        printf("TEST ERROR: Unexpected return of NULL.");
+        testStatus = false;
+    }
+    if(outputOrd->type.type != PS_TYPE_F64) {
+        printf("TEST ERROR: Output vector of type %d expected %d",
+               outputOrd->type.type, PS_TYPE_F64);
+        testStatus = false;
+    }
+
+    psVector* outputCheb = psPolynomial4DEvalVector(polyCheb,inputChebW,inputChebX,inputChebY,inputChebZ);
+    if(outputCheb == NULL) {
+        printf("TEST ERROR: Unexpected return of NULL.");
+        testStatus = false;
+    }
+    if(outputCheb->type.type != PS_TYPE_F64) {
+        printf("TEST ERROR: Output vector of type %d expected %d",
+               outputCheb->type.type, PS_TYPE_F64);
+        testStatus = false;
+    }
+
+    // Verify the results
+    for(psS32 i = 0; i < TESTPOINTS; i++) {
+        if(fabs(Dpoly4DResult[i]-outputOrd->data.F64[i]) > ERROR_TOL) {
+            printf("TEST ERROR: Result[%d] %lg not equal to expected %lg",
+                   i, outputOrd->data.F64[i], Dpoly4DResult[i]);
+            testStatus = false;
+        }
+        if(fabs(Dpoly4DChebResult[i]-outputCheb->data.F64[i]) > ERROR_TOL) {
+            printf("TEST ERROR: ResultCheb[%d] %lg not equal to expected %lg",
+                   i, outputCheb->data.F64[i], Dpoly4DChebResult[i]);
+            testStatus = false;
+        }
+    }
+
+    // Attempt to invoke function with null polynomial
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL polynomial");
+    if(psPolynomial4DEvalVector(NULL,inputOrdW,inputOrdX,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL polynomial");
+        testStatus = false;
+    }
+
+    // Attempt to invoke function with null input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input vector");
+    if(psPolynomial4DEvalVector(polyOrd,NULL,inputOrdX,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL input vector");
+        testStatus = false;
+    }
+    // Attempt to invoke function with null input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input vector");
+    if(psPolynomial4DEvalVector(polyOrd,inputOrdW,NULL,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL input vector");
+        testStatus = false;
+    }
+    // Attempt to invoke function with null input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input vector");
+    if(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,NULL,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL input vector");
+        testStatus = false;
+    }
+    // Attempt to invoke function with null input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input vector");
+    if(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,NULL) != NULL) {
+        printf("TEST ERROR: Return of NULL expected for NULL input vector");
+        testStatus = false;
+    }
+
+    // Attempt to invoke function with a non F64 type input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid input type");
+    inputOrdX->type.type = PS_TYPE_U8;
+    if(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return NULL expected for non-F64 input vector");
+        testStatus = false;
+    }
+    inputOrdX->type.type = PS_TYPE_F64;
+    // Attempt to invoke function with a non F64 type input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid input type");
+    inputOrdY->type.type = PS_TYPE_U8;
+    if(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return NULL expected for non-F64 input vector");
+        testStatus = false;
+    }
+    inputOrdY->type.type = PS_TYPE_F64;
+    // Attempt to invoke function with a non F64 type input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid input type");
+    inputOrdZ->type.type = PS_TYPE_U8;
+    if(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return NULL expected for non-F64 input vector");
+        testStatus = false;
+    }
+    inputOrdZ->type.type = PS_TYPE_F64;
+    // Attempt to invoke function with a non F64 type input vector
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid input type");
+    inputOrdW->type.type = PS_TYPE_U8;
+    if(psPolynomial4DEvalVector(polyOrd,inputOrdW,inputOrdX,inputOrdY,inputOrdZ) != NULL) {
+        printf("TEST ERROR: Return NULL expected for non-F64 input vector");
+        testStatus = false;
+    }
+    inputOrdW->type.type = PS_TYPE_F64;
+
+    psFree(inputOrdX);
+    psFree(inputOrdY);
+    psFree(inputOrdZ);
+    psFree(inputOrdW);
+    psFree(inputChebW);
+    psFree(inputChebX);
+    psFree(inputChebY);
+    psFree(inputChebZ);
+    psFree(outputOrd);
+    psFree(outputCheb);
+    psFree(polyOrd);
+    psFree(polyCheb);
+
+    return(testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psRandom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psRandom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psRandom.c	(revision 22322)
@@ -0,0 +1,409 @@
+/*****************************************************************************
+This routine must ensure that the various random number generator functions
+work properly.
+ 
+    t00(): ensure that psRandom structs are properly allocated by psRandomAlloc().
+    t01(): ensure that psRandomUniform() produces a sequence of numbers with
+    proper mean and stdev.
+    t02(): ensure that psRandomGaussian() produces a sequence of numbers with
+    proper mean and stdev.
+    t03(): ensure that psRandomPoisson() produces a sequence of numbers with
+    proper mean and stdev.
+    t04(): ensure that psRandomReset() properly seeds the random number
+    generator for psRandomUniform().
+    t05(): ensure that psRandomReset() properly seeds the random number
+    generator for psRandomGaussian().
+    t06(): ensure that psRandomReset() properly seeds the random number
+    generator for psRandomPoisson().
+ *****************************************************************************/
+#include <stdio.h>
+#include <math.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define NUM_DATA 10000
+#define SEED 54321
+#define SEED2 345
+#define UNIFORM_MEAN 0.5
+#define UNIFORM_STDEV 0.3
+#define GAUSSIAN_MEAN 0.0
+#define GAUSSIAN_STDEV 1.0
+#define POISSON_MEAN 15.0
+#define POISSON_STDEV (POISSON_MEAN / 4)
+#define ERROR_TOLERANCE 0.1
+psS32 testStatus = true;
+
+static psS32 testRandomAlloc(void);
+static psS32 testRandomUniform(void);
+static psS32 testRandomGaussian(void);
+static psS32 testRandomPoisson(void);
+static psS32 testRandomResetUniform(void);
+static psS32 testRandomResetGaussian(void);
+static psS32 testRandomResetPoisson(void);
+
+testDescription tests[] = {
+                              {testRandomAlloc,000,"psRandomAlloc",0,false},
+                              {testRandomUniform,000,"psRandomUniform",0,false},
+                              {testRandomGaussian,000,"psRandomGaussian",0,false},
+                              {testRandomPoisson,000,"psRandomPoisson",0,false},
+                              {testRandomResetUniform,000,"psRandomReset",0,false},
+                              {testRandomResetGaussian,000,"psRandomReset",0,false},
+                              {testRandomResetPoisson,000,"psRandomReset",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    if(!runTestSuite(stderr,"psRandom",tests,argc,argv)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+psS32 testRandomAlloc(void)
+{
+    psRandom *myRNG = NULL;
+
+    // Valid type allocation
+    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    if (myRNG == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
+        return 1;
+    }
+    if (myRNG->type != PS_RANDOM_TAUS) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",myRNG->type,PS_RANDOM_TAUS);
+        return 2;
+    }
+    psFree(myRNG);
+
+    // Valid type allocation with seed equal to zero
+    int fd1 = creat("seed_msglog1.txt", 0666);
+    //    psLogSetDestination("file:seed_msglog1.txt");
+    psLogSetDestination(fd1);
+    myRNG = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    //    psLogSetDestination("dest:stderr");
+    psLogSetDestination(2);
+    if (myRNG == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
+        return 3;
+    }
+    if (myRNG->type != PS_RANDOM_TAUS) {
+        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",myRNG->type,PS_RANDOM_TAUS);
+        return 4;
+    }
+    psFree(myRNG);
+
+    // Invalid type allocation
+    psLogMsg(__func__,PS_LOG_INFO,"Invalid type, should generate error message");
+    myRNG = psRandomAlloc(100,SEED);
+    if (myRNG != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid type");
+        return 5;
+    }
+
+    // Negative seed value
+    myRNG = psRandomAlloc(PS_RANDOM_TAUS,-5);
+    if(myRNG == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return allocated psRandom");
+        return 6;
+    }
+    close(fd1);
+    psFree(myRNG);
+
+    return 0;
+}
+
+psS32 testRandomUniform(void)
+{
+    psRandom *myRNG = NULL;
+    psVector *rans = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans->n = rans->nalloc;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    if (myRNG == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
+        return 1;
+    }
+
+    // Initialize vector data with random number
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans->data.F64[i] = psRandomUniform(myRNG);
+    }
+    // Perform vector stats on random data (mean, stdev)
+    stats = psVectorStats(stats, rans, NULL, NULL, 0);
+    stats->options = PS_STAT_SAMPLE_STDEV;
+    stats = psVectorStats(stats, rans, NULL, NULL, 0);
+    // Verify mean and stdev
+    if ((fabs(stats->sampleMean - UNIFORM_MEAN) / UNIFORM_MEAN) > ERROR_TOLERANCE) {
+        psError(PS_ERR_UNKNOWN,true,"psRandomUniform mean is %.2f, should be %.2f",
+                stats->sampleMean, UNIFORM_MEAN);
+        return 2;
+    }
+    if ((fabs(stats->sampleStdev - UNIFORM_STDEV) / UNIFORM_STDEV) > ERROR_TOLERANCE) {
+        psError(PS_ERR_UNKNOWN,true,"psRandomUniform stdev is %.2f, should be %.2f",
+                stats->sampleStdev, UNIFORM_STDEV);
+        return 3;
+    }
+
+    psFree(myRNG);
+    psFree(rans);
+    psFree(stats);
+
+    psLogMsg(__func__,PS_LOG_INFO,"NULL psRandom variable, should generate error message");
+    if(psRandomUniform(NULL) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return zero for null psRandom");
+        return 4;
+    }
+
+    return 0;
+}
+
+psS32 testRandomGaussian(void)
+{
+    psRandom *myRNG = NULL;
+    psVector *rans = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans->n = rans->nalloc;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    if (myRNG == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
+        return 1;
+    }
+
+    // Initialize vector with random data
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans->data.F64[i] = psRandomGaussian(myRNG);
+    }
+
+    // Perform vector stats on data (mean, stdev)
+    stats = psVectorStats(stats, rans, NULL, NULL, 0);
+    stats->options = PS_STAT_SAMPLE_STDEV;
+    stats = psVectorStats(stats, rans, NULL, NULL, 0);
+
+    // Verify statistics
+    if ((fabs(stats->sampleMean - GAUSSIAN_MEAN) / 1.0) > ERROR_TOLERANCE) {
+        psError(PS_ERR_UNKNOWN,true,"psRandomGaussian mean is %.2f, should be %.2f",
+                stats->sampleMean, GAUSSIAN_MEAN);
+        return 2;
+    }
+    if ((fabs(stats->sampleStdev - GAUSSIAN_STDEV) / GAUSSIAN_STDEV) > ERROR_TOLERANCE) {
+        psError(PS_ERR_UNKNOWN,true,"psRandomGaussian stdev is %.2f, should be %.2f",
+                stats->sampleStdev, GAUSSIAN_STDEV);
+        return 3;
+    }
+
+    psFree(myRNG);
+    psFree(rans);
+    psFree(stats);
+
+    psLogMsg(__func__,PS_LOG_INFO,"NULL psRandom variable, should generate error message");
+    if(psRandomGaussian(NULL) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return zero for null psRandom");
+        return 4;
+    }
+
+    return 0;
+}
+
+psS32 testRandomPoisson(void)
+{
+    psRandom *myRNG = NULL;
+    psVector *rans = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans->n = rans->nalloc;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    if (myRNG == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
+        return 1;
+    }
+
+    // Initialize vector with random data
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans->data.F64[i] = psRandomPoisson(myRNG, POISSON_MEAN);
+    }
+
+    // Perform statistics on data
+    stats = psVectorStats(stats, rans, NULL, NULL, 0);
+    stats->options = PS_STAT_SAMPLE_STDEV;
+    stats = psVectorStats(stats, rans, NULL, NULL, 0);
+    if ((fabs(stats->sampleMean - POISSON_MEAN) / POISSON_MEAN) > ERROR_TOLERANCE) {
+        psError(PS_ERR_UNKNOWN,true,"psRandomPoisson mean is %.2f, should be %.2f",
+                stats->sampleMean, POISSON_MEAN);
+        return 2;
+    }
+    if ((fabs(stats->sampleStdev - POISSON_STDEV) / POISSON_STDEV) > ERROR_TOLERANCE) {
+        psError(PS_ERR_UNKNOWN,true,"psRandomPoisson stdev is %.2f, should be %.2f",
+                stats->sampleStdev, POISSON_STDEV);
+        return 3;
+    }
+
+    psFree(myRNG);
+    psFree(rans);
+    psFree(stats);
+
+    psLogMsg(__func__,PS_LOG_INFO,"NULL psRandom variable, should generate error message");
+    if(psRandomPoisson(NULL, POISSON_MEAN) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return zero for null psRandom");
+        return 4;
+    }
+
+    return 0;
+}
+
+psS32 testRandomResetUniform(void)
+{
+    psRandom *myRNG = NULL;
+    psVector *rans00 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans00->n = rans00->nalloc;
+    psVector *rans01 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans01->n = rans01->nalloc;
+    psVector *rans02 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans02->n = rans02->nalloc;
+
+    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    if (myRNG == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
+        return 1;
+    }
+
+    // Random reset
+    psRandomReset(myRNG, SEED);
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans00->data.F64[i] = psRandomUniform(myRNG);
+    }
+    psRandomReset(myRNG, SEED2);
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans01->data.F64[i] = psRandomUniform(myRNG);
+    }
+    psRandomReset(myRNG, SEED);
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans02->data.F64[i] = psRandomUniform(myRNG);
+    }
+
+    // Verify reset to original seed produces same results
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        if (rans00->data.F64[i] != rans02->data.F64[i]) {
+            psError(PS_ERR_UNKNOWN,true,"psRandomUniform did not produce the same results with the same seed");
+            return i+1;
+        }
+    }
+
+    psFree(myRNG);
+    psFree(rans00);
+    psFree(rans01);
+    psFree(rans02);
+
+    psRandom *myRNG1 = NULL;
+    myRNG1 = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    //    psLogSetDestination("dest:stderr");
+    psLogSetDestination(0);
+    psRandomReset(myRNG1,0);
+    //    psLogSetDestination("dest:stderr");
+    psLogSetDestination(2);
+    psFree(myRNG1);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Reset a NULL psRandom variable, should generate an error message");
+    psRandomReset(NULL,SEED);
+
+    return 0;
+}
+
+psS32 testRandomResetGaussian(void)
+{
+    psRandom *myRNG = NULL;
+    psVector *rans00 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans00->n = rans00->nalloc;
+    psVector *rans01 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans01->n = rans01->nalloc;
+    psVector *rans02 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans02->n = rans02->nalloc;
+
+    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    if (myRNG == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
+        return 1;
+    }
+
+    // Initialize random data in vectors
+    psRandomReset(myRNG, SEED);
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans00->data.F64[i] = psRandomGaussian(myRNG);
+    }
+    psRandomReset(myRNG, SEED2);
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans01->data.F64[i] = psRandomGaussian(myRNG);
+    }
+    psRandomReset(myRNG, SEED);
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans02->data.F64[i] = psRandomGaussian(myRNG);
+    }
+
+    // Verify data from original seed produces same data after reset
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        if (rans00->data.F64[i] != rans02->data.F64[i]) {
+            psError(PS_ERR_UNKNOWN,true,"psRandomGaussian did not produce the same results with the same seed");
+            return 2;
+        }
+    }
+
+    psFree(myRNG);
+    psFree(rans00);
+    psFree(rans01);
+    psFree(rans02);
+
+    return 0;
+}
+
+psS32 testRandomResetPoisson(void)
+{
+    psRandom *myRNG = NULL;
+    psVector *rans00 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans00->n = rans00->nalloc;
+    psVector *rans01 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans01->n = rans01->nalloc;
+    psVector *rans02 = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
+    rans02->n = rans02->nalloc;
+
+    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    if (myRNG == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
+        return 1;
+    }
+
+    // Initialize vectors with random data
+    psRandomReset(myRNG, SEED);
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans00->data.F64[i] = psRandomPoisson(myRNG, POISSON_MEAN);
+    }
+    psRandomReset(myRNG, SEED2);
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans01->data.F64[i] = psRandomPoisson(myRNG, POISSON_MEAN);
+    }
+    psRandomReset(myRNG, SEED);
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        rans02->data.F64[i] = psRandomPoisson(myRNG, POISSON_MEAN);
+    }
+
+    // Verify the original seed produces same data after reset
+    for (psS32 i = 0 ; i < NUM_DATA ; i++) {
+        if (rans00->data.F64[i] != rans02->data.F64[i]) {
+            psError(PS_ERR_UNKNOWN,true,"psRandomPoisson did not produce the same results with the same seed");
+            return 2;
+        }
+    }
+
+    psFree(myRNG);
+    psFree(rans00);
+    psFree(rans01);
+    psFree(rans02);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psSparse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psSparse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psSparse.c	(revision 22322)
@@ -0,0 +1,59 @@
+
+void psSparseMatrixTest ()
+{
+
+    // build a sparse matrix
+    psSparse *sparse = psSparseAlloc (3, 9);
+
+    psSparseMatrixElement (sparse, 0, 0, 3.0);
+    psSparseMatrixElement (sparse, 1, 1, 2.0);
+    psSparseMatrixElement (sparse, 2, 2, 1.0);
+
+    psSparseMatrixElement (sparse, 1, 0, 0.1);
+    psSparseMatrixElement (sparse, 2, 0, -0.1);
+
+    psSparseResort (sparse);
+    for (int i = 0; i < sparse->Nelem; i++) {
+        fprintf (stderr, "%d %d %f\n",
+                 sparse->Si->data.S32[i],
+                 sparse->Sj->data.S32[i],
+                 sparse->Aij->data.F32[i]);
+    }
+
+    psVector *x = psVectorAlloc (3, PS_DATA_F32);
+    x->data.F32[0] = 3;
+    x->data.F32[1] = 5;
+    x->data.F32[2] = 7;
+    x->n = 3;
+    fprintf (stderr, "x: %f %f %f\n", x->data.F32[0], x->data.F32[1], x->data.F32[2]);
+
+
+    psVector *B = psSparseMatrixTimesVector (NULL, sparse, x);
+    fprintf (stderr, "B: %f %f %f\n", B->data.F32[0], B->data.F32[1], B->data.F32[2]);
+
+    sparse->Bfj->data.F32[0] = B->data.F32[0];
+    sparse->Bfj->data.F32[1] = B->data.F32[1];
+    sparse->Bfj->data.F32[2] = B->data.F32[2];
+
+    psSparseConstraint constraint;
+    constraint.paramMin   = -1e8;
+    constraint.paramMax   = +1e8;
+    constraint.paramDelta = +1e8;
+
+    x = psSparseSolve (x, constraint, sparse, 0);
+    fprintf (stderr, "x: %f %f %f\n", x->data.F32[0], x->data.F32[1], x->data.F32[2]);
+
+    x = psSparseSolve (x, constraint, sparse, 1);
+    fprintf (stderr, "x: %f %f %f\n", x->data.F32[0], x->data.F32[1], x->data.F32[2]);
+
+    x = psSparseSolve (x, constraint, sparse, 2);
+    fprintf (stderr, "x: %f %f %f\n", x->data.F32[0], x->data.F32[1], x->data.F32[2]);
+
+    x = psSparseSolve (x, constraint, sparse, 3);
+    fprintf (stderr, "x: %f %f %f\n", x->data.F32[0], x->data.F32[1], x->data.F32[2]);
+
+    x = psSparseSolve (x, constraint, sparse, 4);
+    fprintf (stderr, "x: %f %f %f\n", x->data.F32[0], x->data.F32[1], x->data.F32[2]);
+    return;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psSpline1D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psSpline1D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psSpline1D.c	(revision 22322)
@@ -0,0 +1,892 @@
+/* @file  tst_psImageManip.c
+*
+*  @brief This file will contain tests for all of the public psLib functions
+*         that implement 1-D spline functionality:
+* psSpline1DAlloc()
+* psSpline1DEval()
+* psSpline1DEvalVector()
+* psVectorFitSpline1D()
+*
+*         This file is composed of the tests formerly in tst_psFunc02.c,
+*         tst_psFunc03.c, tst_psFunc04.c, tst_psFunc05.c, and tst_psFunc07.c.
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-02-07 23:52:54 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+static psS32 psSplineAllocTest( void );
+static psS32 psSplineEvalTest( void );
+static psS32 psSplineEvalVectorTest( void );
+
+// {testFunction, testpointNumber, description, expected rc, boolean-ignore this test}
+testDescription tests[] = {
+                              {psSplineAllocTest, 665, "(TEST A) psSplineAllocTest",true, false},
+                              {psSplineEvalTest, 666, "(TEST B) psSplineEvalTest",true, false},
+                              {psSplineEvalVectorTest,667,"(TEST C) psSplineEvalVectorTest",true,false},
+                              {NULL}
+                          };
+
+#define  ERROR_TOLERANCE_PERCENT    1.00
+/*********************************************************************************
+CheckErrorF32(psF32 actual, psF32 expect): this routine returns FALSE if the
+actual and expect arguments are with ERROR_TOLERANCE_PERCENT of each other,
+otherwise it returns TRUE.
+ ********************************************************************************/
+psBool CheckErrorF32(
+    psF32 actual,
+    psF32 expect)
+{
+    if ((fabs(actual - expect) / fabs(expect)) > ERROR_TOLERANCE_PERCENT) {
+        return(true);
+    } else {
+        return(false);
+    }
+}
+
+psS32 main(
+    psS32 argc,
+    char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel( PS_LOG_INFO );
+    //
+    // Set the following trace levels to track what is happening in the
+    // various functions in psSpline.c
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("spline1DFree", 0);
+    psTraceSetLevel("calculateSecondDerivs", 0);
+    psTraceSetLevel("vectorBinDisectF32", 0);
+    psTraceSetLevel("vectorBinDisectF64", 0);
+    psTraceSetLevel("p_psVectorBinDisect", 0);
+    psTraceSetLevel("psSpline1DAlloc", 0);
+    psTraceSetLevel("psVectorFitSpline1D", 0);
+    psTraceSetLevel("psSpline1DEval", 0);
+    psTraceSetLevel("psSpline1DEvalVector", 0);
+
+    return ( ! runTestSuite( stderr, "psSpline1D", tests, argc, argv ) );
+}
+
+psS32 psSplineAllocTest()
+{
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("spline1DFree", 0);
+    psTraceSetLevel("calculateSecondDerivs", 0);
+    psTraceSetLevel("vectorBinDisectF32", 0);
+    psTraceSetLevel("vectorBinDisectF64", 0);
+    psTraceSetLevel("p_psVectorBinDisect", 0);
+    psTraceSetLevel("psSpline1DAlloc", 0);
+    psTraceSetLevel("psVectorFitSpline1D", 0);
+    psTraceSetLevel("psSpline1DEval", 0);
+    psTraceSetLevel("psSpline1DEvalVector", 0);
+
+    psS32 testStatus = true;
+    psS32  currentId = psMemGetId();
+
+    psSpline1D *tmpSpline = psSpline1DAlloc();
+    if (tmpSpline == NULL) {
+        printf("TEST ERROR: Could not allocate psSpline1D data structure\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->n != 0) {
+        printf("TEST ERROR: psSpline1DAlloc() did not properly set the psSpline1D->n member.\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->spline != NULL) {
+        printf("TEST ERROR: psSpline1DAlloc() did not properly set the psSpline1D->spline member.\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->knots != NULL) {
+        printf("TEST ERROR: psSpline1DAlloc() did not properly set the psSpline1D->knots member.\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->p_psDeriv2 != NULL) {
+        printf("TEST ERROR: psSpline1DAlloc() did not properly set the psSpline1D->p_psDeriv2 member.\n");
+        testStatus = false;
+    }
+    /*
+     XXX: Must allocate these members.
+        tmpSline->spline = (psPolynomial1D **) psAlloc(numSplines * sizeof(psPolynomial1D *));
+        for (psS32 i=0;i<numSplines;i++) {
+            spline->spline[i] = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 3);
+        }
+    */
+
+    psFree(tmpSpline);
+    psMemCheckCorruption(1);
+    psS32 memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    return(testStatus);
+}
+
+
+/*********************************************************************************
+psSplineEvalTest_sub(): We psVectorFitSpline1D with NULL arguments. psSpline1DEval()
+ ********************************************************************************/
+psS32 psSplineEvalTest_sub(psS32 NumSplines)
+{
+    psS32 testStatus = true;
+    psS32 memLeaks=0;
+    psS32  currentId = psMemGetId();
+
+    printf("\n");
+    printf("Calling psVectorFitSpline1D() with NULL y-vector.  Should generate error.\n");
+    psSpline1D *tmpSpline = psVectorFitSpline1D(NULL, NULL);
+    if (tmpSpline != NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not return NULL.\n");
+        testStatus = false;
+        psFree(tmpSpline);
+    }
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    return (testStatus);
+}
+
+
+/*********************************************************************************
+psSplineEvalTest_sub00(): We test the psVectorFitSpline1D, psSpline1DEval()
+functions with a very simple x->y mapping (defined by myFunc00() below).  We
+do this for both F32 and F64 versions of the input x and y vectors.
+ ********************************************************************************/
+psF32 myFunc00(psF32 x)
+{
+    return(x);
+}
+
+psS32 psSplineEvalTest_sub00(psS32 NumSplines)
+{
+    psS32 testStatus = true;
+    psS32 memLeaks=0;
+    psF32 x;
+    psF32 y;
+    psS32  currentId = psMemGetId();
+    psVector *xF32 = NULL;
+    psVector *xF64 = NULL;
+    psVector *yF32 = NULL;
+    psVector *yF64 = NULL;
+    psSpline1D *tmpSpline = NULL;
+
+    printf("\n");
+    printf("psSplineEvalTest_sub00(): We test the psVectorFitSpline1D, psSpline1DEval() functions with a very\n");
+    printf("simple x->y mapping (defined by myFunc00()).  We do this for both F32\n");
+    printf("and F64 versions of the input x and y vectors.\n");
+    printf("    Number of splines: %d\n", NumSplines);
+    /****************************************************************************/
+    /*    PS_TYPE_F32, PS_TYPE_F32 test          */
+    /****************************************************************************/
+    printf("Performing the F32 test....\n");
+    xF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    xF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    yF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    yF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    xF32->n = NumSplines+1;
+    xF64->n = NumSplines+1;
+    yF32->n = NumSplines+1;
+    yF64->n = NumSplines+1;
+
+    for (psS32 i=0;i<NumSplines+1;i++) {
+        xF32->data.F32[i] = (psF32) i;
+        xF64->data.F64[i] = (psF64) i;
+        yF32->data.F32[i] = (psF32) myFunc00(xF32->data.F32[i]);
+        yF64->data.F64[i] = (psF64) myFunc00(xF32->data.F32[i]);
+    }
+
+    tmpSpline = psVectorFitSpline1D(xF32, yF32);
+    if (tmpSpline == NULL) {
+        printf("TEST ERROR: Could not allocate psSpline1D data structure (1)\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->n != NumSplines) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->n member.\n");
+        printf("            psSpline1D->NumSplines was %d, should be %d.\n", tmpSpline->n, NumSplines);
+        testStatus = false;
+    }
+
+    if (tmpSpline->spline == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline member.\n");
+        testStatus = false;
+    } else {
+        for (psS32 i = 0 ; i < NumSplines ; i++) {
+            if (tmpSpline->spline[i] == NULL) {
+                printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline[%d] member.\n", i);
+                testStatus = false;
+            } else {
+                if (psTraceGetLevel(__func__) >= 6) {
+                    PS_POLY_PRINT_1D(tmpSpline->spline[i]);
+                }
+            }
+        }
+    }
+
+    if (tmpSpline->knots == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->knots member.\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->p_psDeriv2 == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->p_psDeriv2 member.\n");
+        testStatus = false;
+    }
+
+    if (testStatus == true) {
+        for (psS32 i=0;i<NumSplines;i++) {
+            x = 0.5 + (float) i;
+            y = psSpline1DEval(tmpSpline, x);
+            if (CheckErrorF32(y, myFunc00(x))) {
+                printf("TEST ERROR: f(%f) is %f, should be %f\n", x, y, myFunc00(x));
+                testStatus = false;
+            }
+        }
+    }
+
+    psFree(tmpSpline);
+    psFree(xF32);
+    psFree(xF64);
+    psFree(yF32);
+    psFree(yF64);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    /****************************************************************************/
+    /*    PS_TYPE_F64, PS_TYPE_F64 test          */
+    /****************************************************************************/
+    printf("Performing the F64 test....\n");
+    xF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    xF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    yF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    yF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    xF32->n = NumSplines+1;
+    xF64->n = NumSplines+1;
+    yF32->n = NumSplines+1;
+    yF64->n = NumSplines+1;
+
+    for (psS32 i=0;i<NumSplines+1;i++) {
+        xF32->data.F32[i] = (psF32) i;
+        xF64->data.F64[i] = (psF64) i;
+        yF32->data.F32[i] = (psF32) myFunc00(xF32->data.F32[i]);
+        yF64->data.F64[i] = (psF64) myFunc00(xF32->data.F32[i]);
+    }
+
+    tmpSpline = psVectorFitSpline1D(xF64, yF64);
+    if (tmpSpline == NULL) {
+        printf("TEST ERROR: Could not allocate psSpline1D data structure\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->n != NumSplines) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->n member.\n");
+        printf("            psSpline1D->NumSplines was %d, should be %d.\n", tmpSpline->n, NumSplines);
+        testStatus = false;
+    }
+
+    if (tmpSpline->spline == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline member.\n");
+        testStatus = false;
+    } else {
+        for (psS32 i = 0 ; i < NumSplines ; i++) {
+            if (tmpSpline->spline[i] == NULL) {
+                printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline[%d] member.\n", i);
+                testStatus = false;
+            } else {
+                if (psTraceGetLevel(__func__) >= 6) {
+                    PS_POLY_PRINT_1D(tmpSpline->spline[i]);
+                }
+            }
+        }
+    }
+
+    if (tmpSpline->knots == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->knots member.\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->p_psDeriv2 == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->p_psDeriv2 member.\n");
+        testStatus = false;
+    }
+
+    if (testStatus == true) {
+        for (psS32 i=0;i<NumSplines;i++) {
+            x = 0.5 + (float) i;
+            y = psSpline1DEval(tmpSpline, x);
+            if (CheckErrorF32(y, myFunc00(x))) {
+                printf("TEST ERROR: f(%f) is %f, should be %f\n", x, y, myFunc00(x));
+                testStatus = false;
+            }
+        }
+    }
+
+    psFree(tmpSpline);
+    psFree(xF32);
+    psFree(xF64);
+    psFree(yF32);
+    psFree(yF64);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    return (testStatus);
+}
+
+/*********************************************************************************
+psSplineEvalTest_sub00b(): We test the psVectorFitSpline1D, psSpline1DEval()
+functions with a more complicated x->y mapping (defined by myFunc00b() below).
+We do this for both F32 and F64 versions of the input x and y vectors.
+ ********************************************************************************/
+#define A 4.0
+#define B -3.0
+#define C 0.2
+#define D 0.1
+psF32 myFunc00b(psF32 x)
+{
+    return(A + x * (B + x * (C + x * (D))));
+}
+
+psS32 psSplineEvalTest_sub00b(psS32 NumSplines)
+{
+    printf("\n");
+    printf("psSplineEvalTest_sub00b(): We test the psVectorFitSpline1D, psSpline1DEval() functions with a\n");
+    printf("more complicated x->y mapping (defined by myFunc00b()).  We do this for\n");
+    printf("both F32 and F64 versions of the input x and y vectors.\n");
+    printf("    Number of splines: %d\n", NumSplines);
+    psS32 testStatus = true;
+    psS32 memLeaks=0;
+    psF32 x;
+    psF32 y;
+    psS32  currentId = psMemGetId();
+    psVector *xF32 = NULL;
+    psVector *xF64 = NULL;
+    psVector *yF32 = NULL;
+    psVector *yF64 = NULL;
+    psSpline1D *tmpSpline = NULL;
+
+    /****************************************************************************/
+    /*    PS_TYPE_F32, PS_TYPE_F32 test          */
+    /****************************************************************************/
+    printf("Performing the F32 test....\n");
+    xF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    xF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    yF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    yF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    xF32->n = NumSplines+1;
+    xF64->n = NumSplines+1;
+    yF32->n = NumSplines+1;
+    yF64->n = NumSplines+1;
+
+    for (psS32 i=0;i<NumSplines+1;i++) {
+        xF32->data.F32[i] = (psF32) i;
+        xF64->data.F64[i] = (psF64) i;
+        yF32->data.F32[i] = (psF32) myFunc00b(xF32->data.F32[i]);
+        yF64->data.F64[i] = (psF64) myFunc00b(xF32->data.F32[i]);
+    }
+
+    tmpSpline = psVectorFitSpline1D(xF32, yF32);
+    if (tmpSpline == NULL) {
+        printf("TEST ERROR: Could not allocate psSpline1D data structure\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->n != NumSplines) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->n member.\n");
+        printf("            psSpline1D->NumSplines was %d, should be %d.\n", tmpSpline->n, NumSplines);
+        testStatus = false;
+    }
+
+    if (tmpSpline->spline == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline member.\n");
+        testStatus = false;
+    } else {
+        for (psS32 i = 0 ; i < NumSplines ; i++) {
+            if (tmpSpline->spline[i] == NULL) {
+                printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline[%d] member.\n", i);
+                testStatus = false;
+            } else {
+                if (psTraceGetLevel(__func__) >= 6) {
+                    PS_POLY_PRINT_1D(tmpSpline->spline[i]);
+                }
+            }
+        }
+    }
+
+    if (tmpSpline->knots == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->knots member.\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->p_psDeriv2 == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->p_psDeriv2 member.\n");
+        testStatus = false;
+    }
+
+    if (testStatus == true) {
+        for (psS32 i=0;i<NumSplines;i++) {
+            x = 0.5 + (float) i;
+            y = psSpline1DEval(tmpSpline, x);
+            if (CheckErrorF32(y, myFunc00b(x))) {
+                printf("TEST ERROR: f(%f) is %f, should be %f\n", x, y, myFunc00b(x));
+                testStatus = false;
+            }
+        }
+    }
+
+    psFree(tmpSpline);
+    psFree(xF32);
+    psFree(xF64);
+    psFree(yF32);
+    psFree(yF64);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    /****************************************************************************/
+    /*    PS_TYPE_F64, PS_TYPE_F64 test          */
+    /****************************************************************************/
+    printf("Performing the F64 test....\n");
+    xF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    xF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    yF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    yF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    xF32->n = NumSplines+1;
+    xF64->n = NumSplines+1;
+    yF32->n = NumSplines+1;
+    yF64->n = NumSplines+1;
+
+    for (psS32 i=0;i<NumSplines+1;i++) {
+        xF32->data.F32[i] = (psF32) i;
+        xF64->data.F64[i] = (psF64) i;
+        yF32->data.F32[i] = (psF32) myFunc00b(xF32->data.F32[i]);
+        yF64->data.F64[i] = (psF64) myFunc00b(xF32->data.F32[i]);
+    }
+
+    tmpSpline = psVectorFitSpline1D(xF64, yF64);
+    if (tmpSpline == NULL) {
+        printf("TEST ERROR: Could not allocate psSpline1D data structure\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->n != NumSplines) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->n member.\n");
+        printf("            psSpline1D->NumSplines was %d, should be %d.\n", tmpSpline->n, NumSplines);
+        testStatus = false;
+    }
+
+    if (tmpSpline->spline == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline member.\n");
+        testStatus = false;
+    } else {
+        for (psS32 i = 0 ; i < NumSplines ; i++) {
+            if (tmpSpline->spline[i] == NULL) {
+                printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline[%d] member.\n", i);
+                testStatus = false;
+            } else {
+                if (psTraceGetLevel(__func__) >= 6) {
+                    PS_POLY_PRINT_1D(tmpSpline->spline[i]);
+                }
+            }
+        }
+    }
+
+    if (tmpSpline->knots == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->knots member.\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->p_psDeriv2 == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->p_psDeriv2 member.\n");
+        testStatus = false;
+    }
+
+    if (testStatus == true) {
+        for (psS32 i=0;i<NumSplines;i++) {
+            x = 0.5 + (float) i;
+            y = psSpline1DEval(tmpSpline, x);
+            if (CheckErrorF32(y, myFunc00b(x))) {
+                printf("TEST ERROR: f(%f) is %f, should be %f\n", x, y, myFunc00b(x));
+                testStatus = false;
+            }
+        }
+    }
+
+    psFree(tmpSpline);
+    psFree(xF32);
+    psFree(xF64);
+    psFree(yF32);
+    psFree(yF64);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    return (testStatus);
+}
+
+
+/*********************************************************************************
+psSplineEvalTest_sub00b():  This is similar to psSplineEvalTest_sub00b()
+except that the x vector is NULL.
+ ********************************************************************************/
+psS32 psSplineEvalTest_sub00c(psS32 NumSplines)
+{
+    printf("\n");
+    printf("psSplineEvalTest_sub00c(): This is similar to psSplineEvalTest_sub00b()\n");
+    printf("except that the x vector is NULL.\n");
+    printf("    Number of splines: %d\n", NumSplines);
+    psS32 testStatus = true;
+    psS32 memLeaks=0;
+    psF32 x;
+    psF32 y;
+    psS32  currentId = psMemGetId();
+    psVector *xF32 = NULL;
+    psVector *xF64 = NULL;
+    psVector *yF32 = NULL;
+    psVector *yF64 = NULL;
+    psSpline1D *tmpSpline = NULL;
+
+    /****************************************************************************/
+    /*    PS_TYPE_F32, PS_TYPE_F32 test          */
+    /****************************************************************************/
+    printf("Performing the F32 test....\n");
+    xF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    xF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    yF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    yF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    xF32->n = NumSplines+1;
+    xF64->n = NumSplines+1;
+    yF32->n = NumSplines+1;
+    yF64->n = NumSplines+1;
+
+    for (psS32 i=0;i<NumSplines+1;i++) {
+        xF32->data.F32[i] = (psF32) i;
+        xF64->data.F64[i] = (psF64) i;
+        yF32->data.F32[i] = (psF32) myFunc00b(xF32->data.F32[i]);
+        yF64->data.F64[i] = (psF64) myFunc00b(xF32->data.F32[i]);
+    }
+
+    tmpSpline = psVectorFitSpline1D(NULL, yF32);
+    if (tmpSpline == NULL) {
+        printf("TEST ERROR: Could not allocate psSpline1D data structure\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->n != NumSplines) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->n member.\n");
+        printf("            psSpline1D->NumSplines was %d, should be %d.\n", tmpSpline->n, NumSplines);
+        testStatus = false;
+    }
+
+    if (tmpSpline->spline == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline member.\n");
+        testStatus = false;
+    } else {
+        for (psS32 i = 0 ; i < NumSplines ; i++) {
+            if (tmpSpline->spline[i] == NULL) {
+                printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline[%d] member.\n", i);
+                testStatus = false;
+            } else {
+                if (psTraceGetLevel(__func__) >= 6) {
+                    PS_POLY_PRINT_1D(tmpSpline->spline[i]);
+                }
+            }
+        }
+    }
+
+    if (tmpSpline->knots == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->knots member.\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->p_psDeriv2 == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->p_psDeriv2 member.\n");
+        testStatus = false;
+    }
+
+    if (testStatus == true) {
+        for (psS32 i=0;i<NumSplines;i++) {
+            x = 0.5 + (float) i;
+            y = psSpline1DEval(tmpSpline, x);
+            if (CheckErrorF32(y, myFunc00b(x))) {
+                printf("TEST ERROR: f(%f) is %f, should be %f\n", x, y, myFunc00b(x));
+                testStatus = false;
+            }
+        }
+    }
+
+    psFree(tmpSpline);
+    psFree(xF32);
+    psFree(xF64);
+    psFree(yF32);
+    psFree(yF64);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    /****************************************************************************/
+    /*    PS_TYPE_F64, PS_TYPE_F64 test          */
+    /****************************************************************************/
+    printf("Performing the F64 test....\n");
+    xF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    xF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    yF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    yF64 = psVectorAlloc(NumSplines+1, PS_TYPE_F64);
+    xF32->n = NumSplines+1;
+    xF64->n = NumSplines+1;
+    yF32->n = NumSplines+1;
+    yF64->n = NumSplines+1;
+
+    for (psS32 i=0;i<NumSplines+1;i++) {
+        xF32->data.F32[i] = (psF32) i;
+        xF64->data.F64[i] = (psF64) i;
+        yF32->data.F32[i] = (psF32) myFunc00b(xF32->data.F32[i]);
+        yF64->data.F64[i] = (psF64) myFunc00b(xF32->data.F32[i]);
+    }
+
+    tmpSpline = psVectorFitSpline1D(NULL, yF64);
+    if (tmpSpline == NULL) {
+        printf("TEST ERROR: Could not allocate psSpline1D data structure\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->n != NumSplines) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->n member.\n");
+        printf("            psSpline1D->NumSplines was %d, should be %d.\n", tmpSpline->n, NumSplines);
+        testStatus = false;
+    }
+
+    if (tmpSpline->spline == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline member.\n");
+        testStatus = false;
+    } else {
+        for (psS32 i = 0 ; i < NumSplines ; i++) {
+            if (tmpSpline->spline[i] == NULL) {
+                printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline[%d] member.\n", i);
+                testStatus = false;
+            } else {
+                if (psTraceGetLevel(__func__) >= 6) {
+                    PS_POLY_PRINT_1D(tmpSpline->spline[i]);
+                }
+            }
+        }
+    }
+
+    if (tmpSpline->knots == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->knots member.\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->p_psDeriv2 == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->p_psDeriv2 member.\n");
+        testStatus = false;
+    }
+
+    if (testStatus == true) {
+        for (psS32 i=0;i<NumSplines;i++) {
+            x = 0.5 + (float) i;
+            y = psSpline1DEval(tmpSpline, x);
+            if (CheckErrorF32(y, myFunc00b(x))) {
+                printf("TEST ERROR: f(%f) is %f, should be %f\n", x, y, myFunc00b(x));
+                testStatus = false;
+            }
+        }
+    }
+
+    psFree(tmpSpline);
+    psFree(xF32);
+    psFree(xF64);
+    psFree(yF32);
+    psFree(yF64);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    return (testStatus);
+}
+
+
+
+psS32 psSplineEvalTest()
+{
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("spline1DFree", 0);
+    psTraceSetLevel("calculateSecondDerivs", 0);
+    psTraceSetLevel("vectorBinDisectF32", 0);
+    psTraceSetLevel("vectorBinDisectF64", 0);
+    psTraceSetLevel("p_psVectorBinDisect", 0);
+    psTraceSetLevel("psSpline1DAlloc", 0);
+    psTraceSetLevel("psVectorFitSpline1D", 0);
+    psTraceSetLevel("psSpline1DEval", 0);
+    psTraceSetLevel("psSpline1DEvalVector", 0);
+
+    psS32 testStatus = psSplineEvalTest_sub(10);
+
+    // HEY: XXX: Test with empty psVectors.
+    //    testStatus |= psSplineEvalTest_sub00(0);
+    //    testStatus |= psSplineEvalTest_sub00b(0);
+    //    testStatus |= psSplineEvalTest_sub00c(0);
+
+    testStatus |= psSplineEvalTest_sub00(1);
+    testStatus |= psSplineEvalTest_sub00b(1);
+    testStatus |= psSplineEvalTest_sub00c(1);
+
+    testStatus |= psSplineEvalTest_sub00(95);
+    testStatus |= psSplineEvalTest_sub00b(95);
+    testStatus |= psSplineEvalTest_sub00c(95);
+
+    return(testStatus);
+}
+
+
+
+psS32 psSplineEvalVectorTest_sub00(psS32 NumSplines)
+{
+    psS32 testStatus = true;
+    psS32 memLeaks=0;
+
+    psS32  currentId = psMemGetId();
+    psVector *xF32 = NULL;
+    psVector *yF32 = NULL;
+    psSpline1D *tmpSpline = NULL;
+    /****************************************************************************/
+    /*    PS_TYPE_F32, PS_TYPE_F32 test          */
+    /****************************************************************************/
+    printf("Performing the F32 test....\n");
+    xF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    yF32 = psVectorAlloc(NumSplines+1, PS_TYPE_F32);
+    psVector *xF32Test = psVectorAlloc(NumSplines, PS_TYPE_F32);
+    psVector *yF32Test = NULL;
+    psVector *xF64Test = psVectorAlloc(NumSplines, PS_TYPE_F64);
+    xF32->n = NumSplines+1;
+    yF32->n = NumSplines+1;
+    xF32Test->n = NumSplines;
+    xF64Test->n = NumSplines;
+
+
+    for (psS32 i=0;i<NumSplines+1;i++) {
+        xF32->data.F32[i] = (psF32) i;
+        yF32->data.F32[i] = (psF32) i;
+    }
+
+    tmpSpline = psVectorFitSpline1D(xF32, yF32);
+    if (tmpSpline == NULL) {
+        printf("TEST ERROR: Could not allocate psSpline1D data structure\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->n != NumSplines) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->n member.\n");
+        printf("            psSpline1D->NumSplines was %d, should be %d.\n", tmpSpline->n, NumSplines);
+        testStatus = false;
+    }
+
+    if (tmpSpline->spline == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline member.\n");
+        testStatus = false;
+    } else {
+        for (psS32 i = 0 ; i < NumSplines ; i++) {
+            if (tmpSpline->spline[i] == NULL) {
+                printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->spline[%d] member.\n", i);
+                testStatus = false;
+            } else {
+                if (psTraceGetLevel(__func__) >= 6) {
+                    PS_POLY_PRINT_1D(tmpSpline->spline[i]);
+                }
+            }
+        }
+    }
+
+    if (tmpSpline->knots == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->knots member.\n");
+        testStatus = false;
+    }
+
+    if (tmpSpline->p_psDeriv2 == NULL) {
+        printf("TEST ERROR: psVectorFitSpline1D() did not properly set the psSpline1D->p_psDeriv2 member.\n");
+        testStatus = false;
+    }
+
+    printf("Testing psSpline1DEvalVector() with an F32 vector.\n");
+    if (testStatus == true) {
+        for (psS32 i=0;i<NumSplines;i++) {
+            xF32Test->data.F32[i] = 0.5 + (psF32) i;
+        }
+        yF32Test = psSpline1DEvalVector(tmpSpline, xF32Test);
+
+        for (psS32 i=0;i<NumSplines;i++) {
+            if (CheckErrorF32(yF32Test->data.F32[i], xF32Test->data.F32[i])) {
+                printf("TEST ERROR: f(%f) is %f\n", xF32Test->data.F32[i], yF32Test->data.F32[i]);
+                testStatus = false;
+            }
+        }
+        psFree(yF32Test);
+    }
+
+    printf("Testing psSpline1DEvalVector() with an F64 vector.\n");
+    if (testStatus == true) {
+        for (psS32 i=0;i<NumSplines;i++) {
+            xF64Test->data.F64[i] = 0.5 + (psF64) i;
+        }
+        yF32Test = psSpline1DEvalVector(tmpSpline, xF64Test);
+
+        for (psS32 i=0;i<NumSplines;i++) {
+            if (CheckErrorF32(yF32Test->data.F32[i], (psF32) xF64Test->data.F64[i])) {
+                printf("TEST ERROR: f(%f) is %f\n", (psF32) xF64Test->data.F64[i], yF32Test->data.F32[i]);
+                testStatus = false;
+            }
+        }
+    }
+
+    psFree(tmpSpline);
+    psFree(xF32);
+    psFree(yF32);
+    psFree(xF32Test);
+    psFree(yF32Test);
+    psFree(xF64Test);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    return (testStatus);
+}
+
+psS32 psSplineEvalVectorTest()
+{
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("spline1DFree", 0);
+    psTraceSetLevel("calculateSecondDerivs", 0);
+    psTraceSetLevel("vectorBinDisectF32", 0);
+    psTraceSetLevel("vectorBinDisectF64", 0);
+    psTraceSetLevel("p_psVectorBinDisect", 0);
+    psTraceSetLevel("psSpline1DAlloc", 0);
+    psTraceSetLevel("psVectorFitSpline1D", 0);
+    psTraceSetLevel("psSpline1DEval", 0);
+    psTraceSetLevel("psSpline1DEvalVector", 0);
+
+    return(psSplineEvalVectorTest_sub00(10));
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats00.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats00.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats00.c	(revision 22322)
@@ -0,0 +1,435 @@
+/** @file  tst_psStats00.c
+*
+*  @brief Contains tests for psVectorStats with sample mean calculations
+*
+*  We extensively test the code with data type PS_TYPE_F32.  If these pass, we
+*  do a much simpler test with data type PS_TYPE_U8, PS_TYPE_U16, PS_TYPE_F64.
+*
+*  If the psStats,c code ever changes such that vectors of different type
+*  are handled by different routines, then these tests must be extended.
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.8 $  $Name: not supported by cvs2svn $
+*  @date $Date: 2006-07-28 00:44:05 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define ERROR_TOL  0.0001
+#define N 15
+#define VERBOSE 1
+static psS32 testStatsSampleMeanF32(void);
+static psS32 testStatsSampleMeanS8(void);
+static psS32 testStatsSampleMeanU16(void);
+static psS32 testStatsSampleMeanF64(void);
+
+testDescription tests[] = {
+                              {testStatsSampleMeanF32, 512, "psVectorStats",0,false},
+                              {testStatsSampleMeanS8, 512, "psVectorStats",0,false},
+                              {testStatsSampleMeanU16, 512, "psVectorStats",0,false},
+                              {testStatsSampleMeanF64, 512, "psVectorStats",0,false},
+                              {NULL}
+                          };
+
+static psF32 samplesF32[N] = { 1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+                               11.01, -12.02, 13.03, 14.04, -15.05 };
+static psS8  samplesS8[N]  = {1, 2, -3, 4, 5, -6, 7, 8, -9, 10, 11, -12, 13, 14, -15};
+static psU16 samplesU16[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+static psF64 samplesF64[N] = { 1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+                               11.01, -12.02, 13.03, 14.04, -15.05 };
+static psF32 errorsF32[N] = { -0.10,  0.11, -0.12,  0.13, -0.14,  0.15, -0.16,  0.17,
+                              -0.18,  0.19, -0.20,  0.21, -0.22,  0.23, -0.24 };
+
+static psF64 expectedMeanNoMaskF32              =  2.060667;
+static psF64 expectedMeanWithMaskF32            =  2.123846;
+static psF64 expectedMeanNoMaskS8               =  2.000000;
+static psF64 expectedMeanNoMaskU16              =  8.000000;
+static psF64 expectedMeanNoMaskF64              =  2.060667;
+static psF64 expectedMeanRangeNoMaskF32         =  0.137500;
+static psF64 expectedMeanRangeWithMaskF32       = -0.366667;
+static psF64 expectedWeightMeanNoMaskF32        =  1.807210;
+static psF64 expectedWeightMeanWithMaskF32      =  1.890217;
+static psF64 expectedWeightMeanNoMaskRangeF32   =  0.640952;
+static psF64 expectedWeightMeanWithMaskRangeF32 =  0.046574;
+
+psF64 rtc(void);
+psF64 sT, eT;
+psF64 diff;
+
+#include <unistd.h>
+psS32 main(psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("p_psVectorSampleMean", 10);
+    psTraceSetLevel("p_psVectorMax", 0);
+    psTraceSetLevel("p_psVectorMin", 0);
+    psTraceSetLevel("p_psVectorNValues", 0);
+    psTraceSetLevel("p_psNormalizeVectorRange", 0);
+    psTraceSetLevel("psStatsAlloc", 0);
+    psTraceSetLevel("p_psConvertToF32", 0);
+    psTraceSetLevel("psVectorStats", 0);
+
+    return ( ! runTestSuite(stderr, "psVectorStats",tests,argc,argv) );
+}
+
+psS32 testStatsSampleMeanF32(void)
+{
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psVector *myVector = psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    psVector *myErrors = psVectorAlloc(N, PS_TYPE_F32);
+    myErrors->n = N;
+    psVector *maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+
+    psF64 mean = 0.0;
+    // Set the appropriate values for the vector data.
+    for (long i = 0; i < N; i++) {
+        myVector->data.F32[i] =  samplesF32[i];
+        myErrors->data.F32[i] =  errorsF32[i];
+    }
+
+    // Set the mask vector and calculate the expected maximum.
+    for (psS32 i = 0; i < N; i++) {
+
+        if (i > 1) {
+            maskVector->data.U8[i] = 0;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    mean = myStats->sampleMean;
+    // Verify return value is as expected
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedMeanNoMaskF32);
+    }
+    if (isnan(myStats->sampleMean) || (fabs(mean - expectedMeanNoMaskF32) > ERROR_TOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Returned value %f not as expected %f",
+                mean, expectedMeanNoMaskF32);
+        return 1;
+    }
+
+    // Invoke psVectorStats with no vector mask and error vector
+    myStats = psVectorStats(myStats, myVector, myErrors, NULL, 0);
+    mean = myStats->sampleMean;
+    // Verify return value is as expected
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedWeightMeanNoMaskF32);
+    }
+    if (isnan(myStats->sampleMean) || (fabs(mean - expectedWeightMeanNoMaskF32) > ERROR_TOL)) {
+        psError(PS_ERR_UNKNOWN,true,"Returned value %f not as expected %f",
+                mean, expectedWeightMeanNoMaskF32);
+        return 10;
+    }
+
+    // Invoke psVectorStats with no vector mask and data range
+    myStats->min = -10.0;
+    myStats->max =   8.0;
+    myStats->options = PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE;
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    mean = myStats->sampleMean;
+    // Verify return value is as expected
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedMeanRangeNoMaskF32);
+    }
+    if ( fabs(mean - expectedMeanRangeNoMaskF32) > ERROR_TOL ) {
+        psError(PS_ERR_UNKNOWN,true,"Return value %f not as expected %f",
+                mean, expectedMeanRangeNoMaskF32);
+        return 2;
+    }
+
+    // Invoke psVectorStats with no vector mask, errors and data range
+    myStats = psVectorStats(myStats, myVector, myErrors, NULL, 0);
+    mean = myStats->sampleMean;
+    // Verify return value is as expected
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedWeightMeanNoMaskRangeF32);
+    }
+    if ( fabs(mean - expectedWeightMeanNoMaskRangeF32) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Return value %f not as expected %f",
+                mean, expectedWeightMeanNoMaskRangeF32);
+        return 20;
+    }
+    myStats->options = PS_STAT_SAMPLE_MEAN;
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with vector mask=1.                             */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+    mean = myStats->sampleMean;
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedMeanWithMaskF32);
+    }
+    if ( fabs(mean - expectedMeanWithMaskF32) > ERROR_TOL ) {
+        psError(PS_ERR_UNKNOWN,true,"Returned value %f not as expected %f",
+                mean, expectedMeanWithMaskF32);
+        return 3;
+    }
+
+    // Invoke psVectorStats with vector mask and error vector
+    myStats = psVectorStats(myStats, myVector, myErrors, maskVector, 1);
+    mean = myStats->sampleMean;
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedWeightMeanWithMaskF32);
+    }
+    if ( fabs(mean - expectedWeightMeanWithMaskF32) > ERROR_TOL ) {
+        psError(PS_ERR_UNKNOWN,true,"Returned value %f not as expected %f",
+                mean, expectedWeightMeanWithMaskF32);
+        return 30;
+    }
+
+    // Invoke psVectorStats with vector mask and data range
+    myStats->options = PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE;
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+    mean = myStats->sampleMean;
+    // Verify return value is as expected
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedMeanRangeWithMaskF32);
+    }
+    if ( fabs(mean - expectedMeanRangeWithMaskF32) > ERROR_TOL ) {
+        psError(PS_ERR_UNKNOWN,true,"Return value %f not as expected %f",
+                mean, expectedMeanRangeWithMaskF32);
+        return 4;
+    }
+
+    // Invoke psVectorStats with vector mask, errors, and data range
+    myStats = psVectorStats(myStats, myVector, myErrors, maskVector, 1);
+    mean = myStats->sampleMean;
+    // Verify return value is as expected
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedWeightMeanWithMaskRangeF32);
+    }
+    if ( fabs(mean - expectedWeightMeanWithMaskRangeF32) > ERROR_TOL ) {
+        psError(PS_ERR_UNKNOWN,true,"Return value %f not as expected %f",
+                mean, expectedWeightMeanWithMaskRangeF32);
+        return 40;
+    }
+    myStats->options = PS_STAT_SAMPLE_MEAN;
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with vector mask=2.                             */
+    /*************************************************************************/
+    // Set the mask vector and calculate the expected maximum.
+    // Set the mask vector.
+    for (psS32 i = 0; i < N; i++) {
+        if (maskVector->data.U8[i] == 1) {
+            maskVector->data.U8[i] = 2;
+        }
+    }
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 2);
+    mean = myStats->sampleMean;
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedMeanWithMaskF32);
+    }
+    if (fabs(mean - expectedMeanWithMaskF32) > ERROR_TOL )  {
+        psError(PS_ERR_UNKNOWN,true,"Returned value %f not as expected %f",
+                mean,expectedMeanWithMaskF32);
+        return 5;
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with vector mask=3.                             */
+    /*************************************************************************/
+    // Set the mask vector and calculate the expected maximum.
+    // Set the mask vector.
+    for (psS32 i = 0; i < N; i++) {
+        if (maskVector->data.U8[i] == 2) {
+            maskVector->data.U8[i] = 3;
+        }
+    }
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 4);
+    mean = myStats->sampleMean;
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedMeanNoMaskF32);
+    }
+    if (fabs(mean - expectedMeanNoMaskF32) > ERROR_TOL ) {
+        psError(PS_ERR_UNKNOWN,true,"Return value %f not as expected %f",
+                mean,expectedMeanNoMaskF32);
+        return 6;
+    }
+
+    // Mask all values and verify return is NAN
+    for(psS32 i = 0; i < N; i++) {
+        maskVector->data.U8[i] = 1;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate warning message");
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+    mean = myStats->sampleMean;
+    if( !isnan(mean) ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN with all values masked");
+        return 7;
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with NULL inputs.                               */
+    /*************************************************************************/
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
+    if( psVectorStats(myStats, NULL, NULL, NULL, 0) != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorStats did not return NULL");
+        return 8;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
+    psStats *myStats2 = psVectorStats(NULL, myVector, NULL, NULL, 0);
+    if ( myStats2 != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"psVectorStats did not return NULL");
+        return 9;
+    }
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                           */
+    /*************************************************************************/
+    psFree(myStats);
+    psFree(myVector);
+    psFree(myErrors);
+    psFree(maskVector);
+    psFree(myStats2);
+
+    return 0;
+}
+
+psS32 testStatsSampleMeanS8(void)
+{
+    psStats*  myStats  = NULL;
+    psVector* myVector = NULL;
+    psF64     mean     = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    myVector = psVectorAlloc(N, PS_TYPE_S8);
+    myVector->n = N;
+
+    mean = 0.0;
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.S8[i] =  samplesS8[i];
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    mean = myStats->sampleMean;
+    // Verify return value is as expected
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedMeanNoMaskS8);
+    }
+    if ( fabs(mean - expectedMeanNoMaskS8) > ERROR_TOL ) {
+        psError(PS_ERR_UNKNOWN,true,"Returned value %f not as expected %f",
+                mean, expectedMeanNoMaskS8);
+        return 1;
+    }
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                           */
+    /*************************************************************************/
+    psFree(myStats);
+    psFree(myVector);
+
+    return 0;
+}
+
+psS32 testStatsSampleMeanU16(void)
+{
+    psStats*  myStats  = NULL;
+    psVector* myVector = NULL;
+    psF64     mean     = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    myVector = psVectorAlloc(N, PS_TYPE_U16);
+    myVector->n = N;
+
+    mean = 0.0;
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.U16[i] =  samplesU16[i];
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    mean = myStats->sampleMean;
+    // Verify return value is as expected
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedMeanNoMaskU16);
+    }
+    if ( fabs(mean - expectedMeanNoMaskU16) > ERROR_TOL ) {
+        psError(PS_ERR_UNKNOWN,true,"Returned value %f not as expected %f",
+                mean, expectedMeanNoMaskU16);
+        return 1;
+    }
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                           */
+    /*************************************************************************/
+    psFree(myStats);
+    psFree(myVector);
+
+    return 0;
+}
+
+psS32 testStatsSampleMeanF64(void)
+{
+    psStats*  myStats  = NULL;
+    psVector* myVector = NULL;
+    psF64     mean     = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    myVector = psVectorAlloc(N, PS_TYPE_F64);
+    myVector->n = N;
+
+    mean = 0.0;
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.F64[i] =  samplesF64[i];
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    mean = myStats->sampleMean;
+    // Verify return value is as expected
+    if (VERBOSE) {
+        printf("psVectorStats() returned %.2f: expected was %.2f\n", mean, expectedMeanNoMaskF64);
+    }
+    if ( fabs(mean - expectedMeanNoMaskF64) > ERROR_TOL ) {
+        psError(PS_ERR_UNKNOWN,true,"Returned value %f not as expected %f",
+                mean, expectedMeanNoMaskF64);
+        return 1;
+    }
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                           */
+    /*************************************************************************/
+    psFree(myStats);
+    psFree(myVector);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats01.c	(revision 22322)
@@ -0,0 +1,276 @@
+/** @file  tst_psStats01.c
+*
+*  @brief Contains tests for psVectorStats with max calculations.
+*
+*  We extensively test the code with data type PS_TYPE_F32.  If these pass, we
+*  do a much simpler test with data types PS_TYPE_U8, PS_TYPE_U16, PS_TYPE_F64.
+*
+*  If the psStats,c code every changes such that vectors of different type
+*  are handled by different routines, then these tests must be extended.
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.4 $  $Name: not supported by cvs2svn $
+*  @date $Date: 2006-07-28 00:44:05 $
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define N 15
+#define ERROR_TOL  0.0001
+
+static psS32 testStatsMaxF32(void);
+static psS32 testStatsMaxS8(void);
+static psS32 testStatsMaxU16(void);
+static psS32 testStatsMaxF64(void);
+
+testDescription tests[] = {
+                              {testStatsMaxF32, 518, "psVectorStats", 0, false},
+                              {testStatsMaxS8, 518, "psVectorStats", 0, false},
+                              {testStatsMaxU16, 518, "psVectorStats", 0, false},
+                              {testStatsMaxF64, 518, "psVectorStats", 0, false},
+                              {NULL}
+                          };
+
+static psF32 samplesF32[N] = { 1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+                               11.01, -12.02, 13.03, 14.04, -15.05 };
+static psS8  samplesS8[N]  = {1, 2, -3, 4, 5, -6, 7, 8, -9, 10, 11, -12, 13, 14, -15};
+static psU16 samplesU16[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+static psF64 samplesF64[N] = { 1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+                               11.01, -12.02, 13.03, 14.04, -15.05 };
+
+static psF64 expectedMaxNoMaskF32                = 14.04;
+static psF64 expectedMaxNoMaskS8                 = 14.00;
+static psF64 expectedMaxNoMaskU16                = 15.00;
+static psF64 expectedMaxNoMaskF64                = 14.04;
+
+static psF64 expectedMaxWithMaskF32              = 13.03;
+static psF64 expectedMaxRangeNoMaskF32           = 10.00;
+static psF64 expectedMaxRangeWithMaskF32         = 13.03;
+
+psS32 main(psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("p_psVectorMax", 0);
+    psTraceSetLevel("p_psVectorMin", 0);
+    psTraceSetLevel("p_psVectorCheckNonEmpty", 0);
+    psTraceSetLevel("p_psVectorNValues", 0);
+    psTraceSetLevel("psStatsAlloc", 0);
+    psTraceSetLevel("p_psConvertToF32", 0);
+    psTraceSetLevel("psVectorStats", 0);
+
+    return ( ! runTestSuite(stderr, "psVectorStats", tests, argc, argv) );
+}
+
+psS32 testStatsMaxF32(void)
+{
+    psStats*  myStats    = NULL;
+    psVector* myVector   = NULL;
+    psVector* maskVector = NULL;
+    psF64     max        = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_MAX);
+    myVector = psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.F32[i] = samplesF32[i];
+    }
+
+    // Set the mask vector and calculate the expected maximum.
+    for (psS32 i = 0; i < N; i++) {
+        if (i < 13) {
+            maskVector->data.U8[i] = 0;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    max = myStats->max;
+
+    if (fabs(max - expectedMaxNoMaskF32) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Max no mask return value %lf not as expected %lf",
+                max, expectedMaxNoMaskF32);
+        return 1;
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with vector mask.                       */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+    max = myStats->max;
+    if (fabs(max - expectedMaxWithMaskF32) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Max with mask return value %lf not as expected %lf",
+                max, expectedMaxWithMaskF32);
+        return 2;
+    }
+
+    // Invoke function with data range with no mask
+    myStats->options = PS_STAT_MAX | PS_STAT_USE_RANGE;
+    myStats->max = 10.1;
+    myStats->min = 0.0;
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    max = myStats->max;
+
+    if(fabs(max - expectedMaxRangeNoMaskF32) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Max with range no mask %lf not as expected %lf",
+                max, expectedMaxRangeNoMaskF32);
+        return 3;
+    }
+
+    // Invoke function with data range and mask
+    myStats->max = 14.0;
+    myStats->min = 0.0;
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+    max = myStats->max;
+    if(fabs(max - expectedMaxRangeWithMaskF32) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Max with range with mask %lf not as expected %lf",
+                max, expectedMaxRangeWithMaskF32);
+        return 3;
+    }
+
+    // Invoke function with data range with no valid data
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    myStats->max = 100.00;
+    myStats->min = 90.00;
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    max = myStats->max;
+
+    if(!isnan(max)) {
+        psError(PS_ERR_UNKNOWN,true,"Max with range with no valid elemenets did not return NAN");
+        return 4;
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+    psFree(maskVector);
+
+    return 0;
+}
+
+psS32 testStatsMaxS8(void)
+{
+    psStats*  myStats    = NULL;
+    psVector* myVector   = NULL;
+    psF64     max        = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_MAX);
+    myVector = psVectorAlloc(N, PS_TYPE_S8);
+    myVector->n = N;
+
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.S8[i] = samplesS8[i];
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    max = myStats->max;
+
+    if (fabs(max - expectedMaxNoMaskS8) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Max no mask return value %lf not as expected %lf",
+                max, expectedMaxNoMaskS8);
+        return 1;
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+
+    return 0;
+}
+
+psS32 testStatsMaxU16(void)
+{
+    psStats*  myStats    = NULL;
+    psVector* myVector   = NULL;
+    psF64     max        = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_MAX);
+    myVector = psVectorAlloc(N, PS_TYPE_U16);
+    myVector->n = N;
+
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.U16[i] = samplesU16[i];
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    max = myStats->max;
+
+    if (fabs(max - expectedMaxNoMaskU16) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Max no mask return value %lf not as expected %lf",
+                max, expectedMaxNoMaskU16);
+        return 1;
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+
+    return 0;
+}
+
+psS32 testStatsMaxF64(void)
+{
+    psStats*  myStats    = NULL;
+    psVector* myVector   = NULL;
+    psF64     max        = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_MAX);
+    myVector = psVectorAlloc(N, PS_TYPE_F64);
+    myVector->n = N;
+
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.F64[i] = samplesF64[i];
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    max = myStats->max;
+
+    if (fabs(max - expectedMaxNoMaskF64) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Max no mask return value %lf not as expected %lf",
+                max, expectedMaxNoMaskF64);
+        return 1;
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats02.c	(revision 22322)
@@ -0,0 +1,277 @@
+/** @file  tst_psStats02.c
+*
+*  @brief Contains tests for psVectorStats with min calculations
+*
+*  We extensively test the code with data type PS_TYPE_F32.  If these pass, we
+*  do a much simpler test with data types PS_TYPE_U8, PS_TYPE_U16, PS_TYPE_F64.
+*
+*  If the psStats,c code every changes such that vectors of different type
+*  are handled by different routines, then these tests must be extended.
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.4 $  $Name: not supported by cvs2svn $
+*  @date $Date: 2006-07-28 00:44:05 $
+*
+* Copyright 2004-2005 Maui High Performance Computing Center, Univ. of Hawaii
+*/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define N 15
+#define ERROR_TOL  0.0001
+
+static psS32 testStatsMinF32(void);
+static psS32 testStatsMinS8(void);
+static psS32 testStatsMinU16(void);
+static psS32 testStatsMinF64(void);
+
+testDescription tests[] = {
+                              {testStatsMinF32, 518, "psVectorStats", 0, false},
+                              {testStatsMinS8, 518, "psVectorStats", 0, false},
+                              {testStatsMinU16, 518, "psVectorStats", 0, false},
+                              {testStatsMinF64, 518, "psVectorStats", 0, false},
+                              {NULL}
+                          };
+
+static psF32 samplesF32[N] = { 1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+                               11.01, -12.02, 13.03, 14.04, -15.05 };
+static psS8  samplesS8[N]  = {1, 2, -3, 4, 5, -6, 7, 8, -9, 10, 11, -12, 13, 14, -15};
+static psU16 samplesU16[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+static psF64 samplesF64[N] = { 1.1, 2.2, -3.3, 4.4, 5.5, -6.6, 7.7, 8.8, -9.9, 10.0,
+                               11.01, -12.02, 13.03, 14.04, -15.05 };
+
+static psF64 expectedMinNoMaskF32                = -15.05;
+static psF64 expectedMinNoMaskS8                 = -15.00;
+static psF64 expectedMinNoMaskU16                = 1.00;
+static psF64 expectedMinNoMaskF64                = -15.05;
+
+static psF64 expectedMinWithMaskF32              = -12.02;
+static psF64 expectedMinRangeNoMaskF32           =   1.10;
+static psF64 expectedMinRangeWithMaskF32         = -12.02;
+
+psS32 main(psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("p_psVectorMax", 0);
+    psTraceSetLevel("p_psVectorMin", 0);
+    psTraceSetLevel("p_psVectorCheckNonEmpty", 0);
+    psTraceSetLevel("p_psVectorNValues", 0);
+    psTraceSetLevel("p_psNormalizeVectorRange", 0);
+    psTraceSetLevel("psStatsAlloc", 0);
+    psTraceSetLevel("p_psConvertToF32", 0);
+    psTraceSetLevel("psVectorStats", 0);
+
+    return ( ! runTestSuite(stderr, "psVectorStats", tests, argc, argv) );
+}
+
+psS32 testStatsMinF32(void)
+{
+    psStats*  myStats    = NULL;
+    psVector* myVector   = NULL;
+    psVector* maskVector = NULL;
+    psF64     min        = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_MIN);
+    myVector = psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.F32[i] = samplesF32[i];
+    }
+
+    // Set the mask vector and calculate the expected maximum.
+    for (psS32 i = 0; i < N; i++) {
+        if (i < 13) {
+            maskVector->data.U8[i] = 0;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    min = myStats->min;
+
+    if (fabs(min - expectedMinNoMaskF32) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Min no mask return value %lf not as expected %lf",
+                min, expectedMinNoMaskF32);
+        return 1;
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with vector mask.                       */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+    min = myStats->min;
+    if (fabs(min - expectedMinWithMaskF32) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Min with mask return value %lf not as expected %lf",
+                min, expectedMinWithMaskF32);
+        return 2;
+    }
+
+    // Invoke function with data range with no mask
+    myStats->options = PS_STAT_MIN | PS_STAT_USE_RANGE;
+    myStats->max = 10.1;
+    myStats->min = 0.0;
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    min = myStats->min;
+
+    if(fabs(min - expectedMinRangeNoMaskF32) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Min with range no mask %lf not as expected %lf",
+                min, expectedMinRangeNoMaskF32);
+        return 3;
+    }
+
+    // Invoke function with data range and mask
+    myStats->max = 10.1;
+    myStats->min = -15.00;
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+    min = myStats->min;
+    if(fabs(min - expectedMinRangeWithMaskF32) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Min with range with mask %lf not as expected %lf",
+                min, expectedMinRangeWithMaskF32);
+        return 3;
+    }
+
+    // Invoke function with data range with no valid data
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    myStats->max = 100.00;
+    myStats->min = 90.00;
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    min = myStats->min;
+
+    if(!isnan(min)) {
+        psError(PS_ERR_UNKNOWN,true,"Min with range with no valid elemenets did not return NAN");
+        return 4;
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+    psFree(maskVector);
+
+    return 0;
+}
+
+psS32 testStatsMinS8(void)
+{
+    psStats*  myStats    = NULL;
+    psVector* myVector   = NULL;
+    psF64     min        = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_MIN);
+    myVector = psVectorAlloc(N, PS_TYPE_S8);
+    myVector->n = N;
+
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.S8[i] = samplesS8[i];
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    min = myStats->min;
+
+    if (fabs(min - expectedMinNoMaskS8) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Min no mask return value %lf not as expected %lf",
+                min, expectedMinNoMaskS8);
+        return 1;
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+
+    return 0;
+}
+
+psS32 testStatsMinU16(void)
+{
+    psStats*  myStats    = NULL;
+    psVector* myVector   = NULL;
+    psF64     min        = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_MAX);
+    myVector = psVectorAlloc(N, PS_TYPE_U16);
+    myVector->n = N;
+
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.U16[i] = samplesU16[i];
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    min = myStats->min;
+
+    if (fabs(min - expectedMinNoMaskU16) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Min no mask return value %lf not as expected %lf",
+                min, expectedMinNoMaskU16);
+        return 1;
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+
+    return 0;
+}
+
+psS32 testStatsMinF64(void)
+{
+    psStats*  myStats    = NULL;
+    psVector* myVector   = NULL;
+    psF64     min        = 0.0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_MIN);
+    myVector = psVectorAlloc(N, PS_TYPE_F64);
+    myVector->n = N;
+
+    // Set the appropriate values for the vector data.
+    for (psS32 i = 0; i < N; i++) {
+        myVector->data.F64[i] = samplesF64[i];
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    min = myStats->min;
+
+    if (fabs(min - expectedMinNoMaskF64) > ERROR_TOL) {
+        psError(PS_ERR_UNKNOWN,true,"Min no mask return value %lf not as expected %lf",
+                min, expectedMinNoMaskF64);
+        return 1;
+    }
+
+    psFree(myStats);
+    psFree(myVector);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats03.c	(revision 22322)
@@ -0,0 +1,136 @@
+/*****************************************************************************
+    This routine must ensure that PS_STAT_SAMPLE_MEDIAN is correctly computed
+    by the procedure psVectorStats().
+ 
+    XXX: Must add tests for various data types, other than psF32.  Copy code
+    from tst_psStats00.c-tst_psStats02.c.
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#define N1 1029   // This should be an odd number.
+#define N ((4 * N1) + 1)
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("p_psVectorSampleMean", 0);
+    psTraceSetLevel("p_psVectorCheckNonEmpty", 0);
+    psTraceSetLevel("p_psVectorNValues", 0);
+    psTraceSetLevel("p_psVectorSampleMedian", 0);
+    psTraceSetLevel("psStatsAlloc", 0);
+    psTraceSetLevel("p_psConvertToF32", 0);
+    psTraceSetLevel("psVectorStats", 0);
+
+    psStats *myStats    = NULL;
+    psS32 testStatus      = true;
+    psS32 globalTestStatus = true;
+    psS32 i               = 0;
+    psVector *myVector  = NULL;
+    psVector *maskVector= NULL;
+    float median        = 1e99;
+    float realMedianWithMask = (float) (N-3)/4;
+    float realMedianNoMask = (float) (N-1)/2;
+    psS32 currentId       = psMemGetId();
+    psS32 memLeaks        = 0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    myVector = psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+
+    // Set the appropriate values for the vector data.
+    for (i=0;i<N;i++) {
+        myVector->data.F32[i] = (float) i;
+    }
+
+    // Set the mask vector and calculate the expected median.
+    for (i=0;i<N;i++) {
+        if (i < (N/2)) {
+            maskVector->data.U8[i] = 0;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "PS_STAT_SAMPLE_MEDIAN: no vector mask");
+
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    median = myStats->sampleMedian;
+
+    printf("Called psVectorStats() on a vector with no elements masked.\n");
+    printf("The expected median was %f.  The calculated median was %f.\n",
+           realMedianNoMask, median);
+    if (median == realMedianNoMask) {
+        testStatus = true;
+    } else {
+        testStatus = false;
+        globalTestStatus = false;
+    }
+
+    printFooter(stdout,
+                "psStats functions",
+                "PS_STAT_SAMPLE_MEDIAN: no vector mask",
+                testStatus);
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with vector mask.                       */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "PS_STAT_SAMPLE_MEDIAN: with vector mask");
+
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+    median = myStats->sampleMedian;
+    printf("Called psVectorStats() on a vector with last N/2 elements masked.\n");
+    printf("The expected median was %f.  The calculated median was %f.\n",
+           realMedianWithMask, median);
+    if (median == realMedianWithMask) {
+        testStatus = true;
+    } else {
+        testStatus = false;
+        globalTestStatus = false;
+    }
+
+    printFooter(stdout,
+                "psStats functions",
+                "PS_STAT_SAMPLE_MEDIAN: with vector mask",
+                testStatus);
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                   */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "psStats(): deallocating memory");
+
+    psFree(myStats);
+    psFree(myVector);
+    psFree(maskVector);
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    printFooter(stdout,
+                "psStats functions",
+                "psStats(): deallocating memory",
+                testStatus);
+
+    return (!globalTestStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats05.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats05.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats05.c	(revision 22322)
@@ -0,0 +1,86 @@
+/*****************************************************************************
+    This routine must ensure that the psStats structure is correctly
+    allocated and deallocated by the procedure psStatsAlloc().
+ 
+    XXX: This should be test 00.
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#define MISC_FLOAT_NUMBER 345.0
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("psStatsAlloc", 0);
+
+    psStats *myStats    = NULL;
+    psS32 testStatus      = true;
+    psS32 currentId       = psMemGetId();
+    psS32 memLeaks        = 0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "Allocate the psStats structure.");
+
+    myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    myStats->sampleMean = MISC_FLOAT_NUMBER;
+    myStats->sampleMedian = MISC_FLOAT_NUMBER;
+    myStats->sampleStdev = MISC_FLOAT_NUMBER;
+    myStats->sampleUQ = MISC_FLOAT_NUMBER;
+    myStats->sampleLQ = MISC_FLOAT_NUMBER;
+    myStats->robustMedian = MISC_FLOAT_NUMBER;
+    myStats->robustStdev = MISC_FLOAT_NUMBER;
+    myStats->robustUQ = MISC_FLOAT_NUMBER;
+    myStats->robustLQ = MISC_FLOAT_NUMBER;
+    myStats->robustN50 = MISC_FLOAT_NUMBER;
+    myStats->fittedMean = MISC_FLOAT_NUMBER;
+    myStats->fittedStdev = MISC_FLOAT_NUMBER;
+    myStats->fittedNfit = MISC_FLOAT_NUMBER;
+    myStats->clippedMean = MISC_FLOAT_NUMBER;
+    myStats->clippedStdev = MISC_FLOAT_NUMBER;
+    myStats->clippedNvalues = MISC_FLOAT_NUMBER;
+    myStats->clipSigma = MISC_FLOAT_NUMBER;
+    myStats->clipIter = MISC_FLOAT_NUMBER;
+    myStats->min = MISC_FLOAT_NUMBER;
+    myStats->max = MISC_FLOAT_NUMBER;
+    myStats->binsize = MISC_FLOAT_NUMBER;
+    myStats->nSubsample = MISC_FLOAT_NUMBER;
+    myStats->options = 0x0;
+
+    psMemCheckCorruption(1);
+
+    printFooter(stdout,
+                "psStats functions",
+                "Allocate the psStats structure.",
+                testStatus);
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                   */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "Deallocate the psStats structure.");
+    psFree(myStats);
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    psMemCheckCorruption(1);
+
+    printFooter(stdout,
+                "psStats functions",
+                "Deallocate the psStats structure.",
+                testStatus);
+
+    return (!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats06.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats06.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats06.c	(revision 22322)
@@ -0,0 +1,147 @@
+/*****************************************************************************
+    This routine must ensure that PS_STAT_SAMPLE_STDEV is correctly computed
+    by the procedure psArrayStats().
+ 
+    XXX: Must add tests for various data types, other than psF32.  Copy code
+    from tst_psStats00.c-tst_psStats02.c.
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psTest.h"
+#include "float.h"
+#include <math.h>
+
+#define N 15
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("p_psVectorSampleMean", 0);
+    psTraceSetLevel("p_psVectorMax", 0);
+    psTraceSetLevel("p_psVectorMin", 0);
+    psTraceSetLevel("p_psVectorCheckNonEmpty", 0);
+    psTraceSetLevel("p_psVectorNValues", 0);
+    psTraceSetLevel("p_psVectorSampleStdevOLD", 0);
+    psTraceSetLevel("p_psVectorSampleStdev", 0);
+    psTraceSetLevel("psStatsAlloc", 0);
+    psTraceSetLevel("p_psConvertToF32", 0);
+    psTraceSetLevel("psVectorStats", 0);
+
+    psStats *myStats    = NULL;
+    psS32 testStatus      = true;
+    psS32 globalTestStatus = true;
+    psS32 i               = 0;
+    psVector *myVector  = NULL;
+    psVector *maskVector= NULL;
+    float stdev         = 0.0;
+    // NOTE: These values were calculated by running the function on the data.
+    // A: They must be changed if we adjust the number of data points.
+    // B: We don't really know that they are correct.
+    float realStdevNoMask   = 4.472136;
+    float realStdevWithMask = 2.160247;
+    psS32 count           = 0;
+    psS32 currentId       = psMemGetId();
+    psS32 memLeaks        = 0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_SAMPLE_STDEV);
+    myVector = psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+
+    stdev = 0.0;
+    // Set the appropriate values for the vector data.
+    for (i=0;i<N;i++) {
+        myVector->data.F32[i] = (float) i;
+    }
+
+    // Set the mask vector and calculate the expected maximum.
+    for (i=0;i<N;i++) {
+        if (i < (N/2)) {
+            maskVector->data.U8[i] = 0;
+            count++;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "PS_STAT_SAMPLE_STDEV: no vector mask");
+
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+    stdev = myStats->sampleStdev;
+
+    printf("Called psVectorStats() on a vector with no elements masked.\n");
+    printf("The expected stdev was %f; the calculated stdev was %f\n",
+           realStdevNoMask, stdev);
+    if (fabs(stdev - realStdevNoMask) <= 2.0 * FLT_EPSILON) {
+        testStatus = true;
+    } else {
+        testStatus = false;
+        globalTestStatus = false;
+    }
+    printFooter(stdout,
+                "psVector functions",
+                "PS_STAT_SAMPLE_STDEV: no vector mask",
+                testStatus);
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with vector mask.                       */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "PS_STAT_SAMPLE_STDEV: with vector mask");
+
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+    stdev = myStats->sampleStdev;
+    printf("Called psVectorStats() on a vector with last N/2 elements masked.\n");
+    printf("The expected stdev was %f; the calculated stdev was %f\n",
+           realStdevWithMask, stdev);
+    if (fabs(stdev - realStdevWithMask) <= 2.0 * FLT_EPSILON) {
+        testStatus = true;
+    } else {
+        testStatus = false;
+        globalTestStatus = false;
+    }
+
+    printFooter(stdout,
+                "psVector functions",
+                "PS_STAT_SAMPLE_STDEV: with vector mask",
+                testStatus);
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                   */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "psStats(): deallocating memory");
+
+    psFree(myStats);
+    psFree(myVector);
+    psFree(maskVector);
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    printFooter(stdout,
+                "psVector functions",
+                "psStats(): deallocating memory",
+                testStatus);
+
+    return (!globalTestStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats07.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats07.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats07.c	(revision 22322)
@@ -0,0 +1,491 @@
+/*****************************************************************************
+   This routine must ensure that PS_STAT_ROBUST_QUARTILE is correctly computed
+   by the procedure psArrayStats().
+ 
+   XXX: Must add tests for various data types, other than psF32.  Copy code
+   from tst_psStats00.c-tst_psStats02.c.
+*****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psTest.h"
+#include "float.h"
+#include <math.h>
+
+#define NUM_DATA 1000
+#define MEAN 32.0
+#define STDEV 2.0
+#define PERCENT_OUTLIERS 1
+#define OUTLIER_MAGNITUDE (NUM_DATA * 10)
+#define ERROR_TOLERANCE .10
+#define ERRORS 1000.0
+#define SEED 1995
+#define VERBOSE 0
+
+#define TST_IN_NULL             0x00000001
+#define TST_IN_F32              0x00000002
+#define TST_IN_F64              0x00000004
+#define TST_IN_S8               0x00000008
+#define TST_IN_U16              0x00000010
+#define TST_IN_S32              0x00000020
+#define TST_ERRORS_NULL         0x00000040
+#define TST_ERRORS_F32          0x00000080
+#define TST_ERRORS_F64          0x00000100
+#define TST_ERRORS_S8           0x00000200
+#define TST_ERRORS_U16          0x00000400
+#define TST_ERRORS_S32          0x00000800
+#define TST_MASK_NULL           0x00001000
+#define TST_MASK_U8             0x00002000
+#define TST_MASK_S32            0x00004000
+
+psBool genericRobustStatsTest(
+    unsigned int flags,
+    psS32 numData,
+    psU32 maskValue,
+    psBool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    psBool testStatus = true;
+    psS32 memLeaks = 0;
+    psVector *in = NULL;
+    psVector *errors = NULL;
+    psVector *mask = NULL;
+    srand(SEED);
+    printPositiveTestHeader(stdout, "psMathUtils functions", "psVectorStats Robust Stats Routine");
+
+    if (expectedRC == true) {
+        printf("This test should not generate any errors.\n");
+    }
+    if (expectedRC == false) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+    psVector *gaussVector = p_psGaussianDev(MEAN, STDEV, numData);
+
+
+    if (flags & TST_IN_NULL) {
+        printf("        using a NULL in vector\n");
+    }
+
+    if (flags & TST_IN_F32) {
+        printf("        using a psF32 in vector\n");
+        in = psVectorAlloc(numData, PS_TYPE_F32);
+        for (psS32 i=0;i<numData;i++) {
+            in->data.F32[i] = gaussVector->data.F32[i];
+            in->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%.1f)\n", i, in->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TST_IN_F64) {
+        printf("        using a psF64 in vector\n");
+        in = psVectorAlloc(numData, PS_TYPE_F64);
+        for (psS32 i=0;i<numData;i++) {
+            in->data.F64[i] = (psF64) gaussVector->data.F32[i];
+            in->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%.1f)\n", i, in->data.F64[i]);
+            }
+        }
+    }
+
+    if (flags & TST_IN_S8) {
+        printf("        using a psS8 in vector\n");
+        in = psVectorAlloc(numData, PS_TYPE_S8);
+        for (psS32 i=0;i<numData;i++) {
+            in->data.S8[i] = (psS8) gaussVector->data.F32[i];
+            in->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.S8[i]);
+            }
+        }
+    }
+
+    if (flags & TST_IN_U16) {
+        printf("        using a psU16 in vector\n");
+        in = psVectorAlloc(numData, PS_TYPE_U16);
+        for (psS32 i=0;i<numData;i++) {
+            in->data.U16[i] = (psU16) gaussVector->data.F32[i];
+            in->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.U16[i]);
+            }
+        }
+    }
+
+    if (flags & TST_IN_S32) {
+        printf("        using a psS32 in vector\n");
+        in = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            in->data.S32[i] = (psS32) gaussVector->data.F32[i];
+            in->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.S32[i]);
+            }
+        }
+    }
+    psFree(gaussVector);
+
+    //    in->n = in->nalloc;
+    if (flags & TST_ERRORS_NULL) {
+        printf("        using a NULL errors vector\n");
+    }
+
+    if (flags & TST_ERRORS_F32) {
+        printf("        using a psF32 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_F32);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.F32[i] = ERRORS;
+            errors->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original errors data %d: (%.1f)\n", i, errors->data.F32[i]);
+            }
+        }
+    }
+
+    if (flags & TST_ERRORS_F64) {
+        printf("        using a psF64 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_F64);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.F64[i] = ERRORS;
+            errors->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original errors data %d: (%.1f)\n", i, errors->data.F64[i]);
+            }
+        }
+    }
+
+    if (flags & TST_ERRORS_S8) {
+        printf("        using a psS8 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_S8);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.S8[i] = (psS8) ERRORS;
+            errors->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original errors data %d: (%d)\n", i, errors->data.S8[i]);
+            }
+        }
+    }
+
+    if (flags & TST_ERRORS_U16) {
+        printf("        using a psU16 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_U16);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.U16[i] = (psU16) ERRORS;
+            errors->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original errors data %d: (%d)\n", i, errors->data.U16[i]);
+            }
+        }
+    }
+
+    if (flags & TST_ERRORS_S32) {
+        printf("        using a psS32 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.S32[i] = (psS32) ERRORS;
+            errors->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original errors data %d: (%d)\n", i, errors->data.S32[i]);
+            }
+        }
+    }
+
+
+    if (flags & TST_MASK_NULL) {
+        printf("        using a NULL mask vector\n");
+    }
+
+    if (flags & TST_MASK_U8) {
+        printf("        using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = (psU8) 0;
+            mask->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original mask data %d: (%d)\n", i, mask->data.U8[i]);
+            }
+        }
+    }
+
+    if (flags & TST_MASK_S32) {
+        printf("        using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = (psS32) 0;
+            mask->n++;
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original mask data %d: (%d)\n", i, mask->data.S32[i]);
+            }
+        }
+    }
+
+
+    //
+    // We calculate the sample mean and stdev without and outliers in the data.
+    // We will use this later in determining if the clipped stats are correct.
+    //
+    psF32 sampleMean;
+    psF32 sampleStdev;
+    psF32 sampleMedian;
+    psF32 sampleLQ;
+    psF32 sampleUQ;
+    if (expectedRC == true) {
+        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV | PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_QUARTILE);
+        psStats *rc = psVectorStats(myStats, in, NULL, NULL, maskValue);
+        if (rc == NULL) {
+            printf("TEST ERROR: the psVectorStats() function returned NULL.\n");
+            testStatus = false;
+        } else {
+            sampleMean = myStats->sampleMean;
+            sampleStdev = myStats->sampleStdev;
+            sampleMedian = myStats->sampleMedian;
+            sampleLQ = myStats->sampleLQ;
+            sampleUQ = myStats->sampleUQ;
+        }
+        psFree(myStats);
+    }
+
+    //
+    // We add a few outliers to the input data.
+    //
+    if (flags & TST_IN_F32) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.F32[i] = (psF32) OUTLIER_MAGNITUDE;
+            }
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%.1f)\n", i, in->data.F32[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_F64) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.F64[i] = (psF64) OUTLIER_MAGNITUDE;
+            }
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%.1f)\n", i, in->data.F64[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_S8) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.S8[i] = (psS8) OUTLIER_MAGNITUDE;
+            }
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.S8[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_U16) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.U16[i] = (psU16) OUTLIER_MAGNITUDE;
+            }
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.U16[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_S32) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.S32[i] = (psS32) OUTLIER_MAGNITUDE;
+            }
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Original in data %d: (%d)\n", i, in->data.S32[i]);
+            }
+        }
+    }
+
+    //
+    // We call psVectorStats() and calculate the clipped stats.
+    //
+    psStats *myStats = psStatsAlloc(
+                           PS_STAT_ROBUST_MEDIAN |
+                           PS_STAT_ROBUST_STDEV |
+                           PS_STAT_ROBUST_QUARTILE |
+                           PS_STAT_FITTED_MEAN |
+                           PS_STAT_FITTED_STDEV);
+    psStats *rc = psVectorStats(myStats, in, errors, mask, maskValue);
+
+    if (rc == NULL) {
+        if (expectedRC == true) {
+            printf("TEST ERROR: the psVectorStats() function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            printf("TEST ERROR: the psVectorStats() function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        //
+        // Fitted Mean
+        //
+        if (fabs(myStats->fittedMean - sampleMean) > (ERROR_TOLERANCE * sampleMean)) {
+            printf("TEST ERROR: the fitted mean was %.2f, should have been %.2f\n", myStats->fittedMean, sampleMean);
+            testStatus = false;
+        } else if (VERBOSE) {
+            printf("GOOD: the fitted mean was %.2f, should have been %.2f\n", myStats->fittedMean, sampleMean);
+        }
+
+        //
+        // Fitted Stdev
+        //
+        if (fabs(myStats->fittedStdev - sampleStdev) > (ERROR_TOLERANCE * sampleStdev)) {
+            printf("TEST ERROR: the fitted stdev was %.2f, should have been %.2f\n", myStats->fittedStdev, sampleStdev);
+            testStatus = false;
+        } else if (VERBOSE) {
+            printf("GOOD: the fitted stdev was %.2f, should have been %.2f\n", myStats->fittedStdev, sampleStdev);
+        }
+
+        //
+        // Robust Stdev
+        //
+        if (fabs(myStats->robustStdev - sampleStdev) > (ERROR_TOLERANCE * sampleStdev)) {
+            printf("TEST ERROR: the robust stdev was %.2f, should have been %.2f\n", myStats->robustStdev, sampleStdev);
+            testStatus = false;
+        } else if (VERBOSE) {
+            printf("GOOD: the robust stdev was %.2f, should have been %.2f\n", myStats->robustStdev, sampleStdev);
+        }
+
+        //
+        // Robust Median
+        //
+        if (fabs(myStats->robustMedian - sampleMedian) > (ERROR_TOLERANCE * sampleMedian)) {
+            printf("TEST ERROR: the robust median was %.2f, should have been %.2f\n", myStats->robustMedian, sampleMedian);
+            testStatus = false;
+        } else if (VERBOSE) {
+            printf("GOOD: the robust median was %.2f, should have been %.2f\n", myStats->robustMedian, sampleMedian);
+        }
+
+        //
+        // Robust LQ
+        //
+        if (fabs(myStats->robustLQ - sampleLQ) > (ERROR_TOLERANCE * sampleLQ)) {
+            printf("TEST ERROR: the robust LQ was %.2f, should have been %.2f\n", myStats->robustLQ, sampleLQ);
+            testStatus = false;
+        } else if (VERBOSE) {
+            printf("GOOD: the robust LQ was %.2f, should have been %.2f\n", myStats->robustLQ, sampleLQ);
+        }
+
+        //
+        // Robust UQ
+        //
+        if (fabs(myStats->robustUQ - sampleUQ) > (ERROR_TOLERANCE * sampleUQ)) {
+            printf("TEST ERROR: the robust UQ was %.2f, should have been %.2f\n", myStats->robustUQ, sampleUQ);
+            testStatus = false;
+        } else if (VERBOSE) {
+            printf("GOOD: the robust UQ was %.2f, should have been %.2f\n", myStats->robustUQ, sampleUQ);
+        }
+    }
+
+    psFree(myStats);
+    psFree(in);
+    psFree(errors);
+    psFree(mask);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    return(testStatus);
+}
+
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psBool testStatus = true;
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    #define TRACE_LEVEL 0
+
+    psTraceSetLevel(".", TRACE_LEVEL);
+    psTraceSetLevel("psGaussian", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorMax", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorMin", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorCheckNonEmpty", TRACE_LEVEL);
+    psTraceSetLevel("p_psNormalizeVectorRange", TRACE_LEVEL);
+    psTraceSetLevel("p_ps1DPolyMedian", TRACE_LEVEL);
+    psTraceSetLevel("fitQuadraticSearchForYThenReturnX", TRACE_LEVEL);
+    psTraceSetLevel("PsVectorDup", TRACE_LEVEL);
+    psTraceSetLevel("psMinimizeLMChi2Gauss1D", TRACE_LEVEL);
+    psTraceSetLevel("LinInterpolate", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorRobustStats", TRACE_LEVEL);
+    psTraceSetLevel("psStatsAlloc", TRACE_LEVEL);
+    psTraceSetLevel("psHistogramAlloc", TRACE_LEVEL);
+    psTraceSetLevel("psHistogramAllocGeneric", TRACE_LEVEL);
+    psTraceSetLevel("UpdateHistogramBins", TRACE_LEVEL);
+    psTraceSetLevel("psVectorHistogram", TRACE_LEVEL);
+    psTraceSetLevel("p_psConvertToF32", TRACE_LEVEL);
+    psTraceSetLevel("psVectorStats", TRACE_LEVEL);
+
+    testStatus &= genericRobustStatsTest(TST_IN_F32 | TST_ERRORS_NULL | TST_MASK_NULL, NUM_DATA, 1, true);
+    testStatus &= genericRobustStatsTest(TST_IN_F32 | TST_ERRORS_NULL | TST_MASK_U8, NUM_DATA, 1, true);
+
+    if (testStatus) {
+        printf("TEST PASSED.\n");
+    } else {
+        printf("TEST FAILED.\n");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats08.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats08.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats08.c	(revision 22322)
@@ -0,0 +1,190 @@
+/*****************************************************************************
+    This routine must ensure that PS_STAT_SAMPLE_QUARTILE is correctly computed
+    by the procedure psArrayStats().
+ 
+    XXX: Must add tests for various data types, other than psF32.  Copy code
+    from tst_psStats00.c-tst_psStats02.c.
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psTest.h"
+#include "float.h"
+#include <math.h>
+
+#define N1 25  //
+#define N (8 * N1) // Don't change this (N must be a multiple of 8)
+
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("p_psVectorMax", 0);
+    psTraceSetLevel("p_psVectorMin", 0);
+    psTraceSetLevel("p_psVectorCheckNonEmpty", 0);
+    psTraceSetLevel("p_psVectorNValues", 0);
+    psTraceSetLevel("p_psVectorSampleQuartiles", 0);
+    psTraceSetLevel("psStatsAlloc", 0);
+    psTraceSetLevel("p_psConvertToF32", 0);
+    psTraceSetLevel("psVectorStats", 0);
+
+
+    psStats *myStats    = NULL;
+    psS32 testStatus      = true;
+    psS32 globalTestStatus = true;
+    psS32 i               = 0;
+    psVector *myVector  = NULL;
+    psVector *maskVector= NULL;
+    // NOTE: These values were calculated by running the function on the data.
+    // A: They must be changed if we adjust the number of data points.
+    // B: We don't really know that they are correct.
+    float realLQNoMask   = N/4.0;
+    float realUQNoMask   = 3.0 * (N/4.0);
+    float realLQWithMask   = N/8.0;
+    float realUQWithMask   = 3.0 * (N/8.0);
+    psS32 count           = 0;
+    psS32 currentId       = psMemGetId();
+    psS32 memLeaks        = 0;
+
+    /*************************************************************************/
+    /*  Allocate and initialize data structures                      */
+    /*************************************************************************/
+    myStats = psStatsAlloc(PS_STAT_SAMPLE_QUARTILE);
+    myVector = psVectorAlloc(N, PS_TYPE_F32);
+    myVector->n = N;
+    maskVector = psVectorAlloc(N, PS_TYPE_U8);
+    maskVector->n = N;
+
+    // Set the appropriate values for the vector data.
+    for (i=0;i<N;i++) {
+        myVector->data.F32[i] = (float) i;
+    }
+
+    // Set the mask vector and calculate the expected maximum.
+    for (i=0;i<N;i++) {
+        if (i < (N/2)) {
+            maskVector->data.U8[i] = 0;
+            count++;
+        } else {
+            maskVector->data.U8[i] = 1;
+        }
+    }
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with no vector mask.                    */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "PS_STAT_SAMPLE_LQ: no vector mask");
+
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+
+    printf("Called psVectorStats() on a vector with no elements masked.\n");
+    printf("The expected sampleLQ was %f; the calculated sampleLQ was %f\n",
+           realLQNoMask, myStats->sampleLQ);
+    if (fabs(realLQNoMask - myStats->sampleLQ) <= 2.0 * FLT_EPSILON) {
+        testStatus = true;
+    } else {
+        testStatus = false;
+        globalTestStatus = false;
+    }
+    printFooter(stdout,
+                "psVector functions",
+                "PS_STAT_SAMPLE_LQ: no vector mask",
+                testStatus);
+
+
+
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "PS_STAT_SAMPLE_UQ: no vector mask");
+
+    myStats = psVectorStats(myStats, myVector, NULL, NULL, 0);
+
+    printf("Called psVectorStats() on a vector with no elements masked.\n");
+    printf("The expected sampleUQ was %f; the calculated sampleUQ was %f\n",
+           realUQNoMask, myStats->sampleUQ);
+    if (fabs(realUQNoMask - myStats->sampleUQ) <= 2.0 * FLT_EPSILON) {
+        testStatus = true;
+    } else {
+        testStatus = false;
+        globalTestStatus = false;
+    }
+    printFooter(stdout,
+                "psVector functions",
+                "PS_STAT_SAMPLE_UQ: no vector mask",
+                testStatus);
+
+    /*************************************************************************/
+    /*  Call psVectorStats() with vector mask.                       */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "PS_STAT_SAMPLE_LQ: with vector mask");
+
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+
+    printf("Called psVectorStats() on a vector with elements masked.\n");
+    printf("The expected sampleLQ was %f; the calculated sampleLQ was %f\n",
+           realLQWithMask, myStats->sampleLQ);
+    if (fabs(realLQWithMask - myStats->sampleLQ) <= 2.0 * FLT_EPSILON) {
+        testStatus = true;
+    } else {
+        testStatus = false;
+        globalTestStatus = false;
+    }
+    printFooter(stdout,
+                "psVector functions",
+                "PS_STAT_SAMPLE_LQ: with vector mask",
+                testStatus);
+
+
+
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "PS_STAT_SAMPLE_UQ: with vector mask");
+
+    myStats = psVectorStats(myStats, myVector, NULL, maskVector, 1);
+
+    printf("Called psVectorStats() on a vector with elements masked.\n");
+    printf("The expected sampleUQ was %f; the calculated sampleUQ was %f\n",
+           realUQWithMask, myStats->sampleUQ);
+    if (fabs(realUQWithMask - myStats->sampleUQ) <= 2.0 * FLT_EPSILON) {
+        testStatus = true;
+    } else {
+        testStatus = false;
+        globalTestStatus = false;
+    }
+    printFooter(stdout,
+                "psVector functions",
+                "PS_STAT_SAMPLE_UQ: with vector mask",
+                testStatus);
+
+
+    /*************************************************************************/
+    /*  Deallocate data structures                                   */
+    /*************************************************************************/
+    printPositiveTestHeader(stdout,
+                            "psStats functions",
+                            "psStats(): deallocating memory");
+
+    psFree(myStats);
+    psFree(myVector);
+    psFree(maskVector);
+
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    printFooter(stdout,
+                "psVector functions",
+                "psStats(): deallocating memory",
+                testStatus);
+
+    return (!globalTestStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats09.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats09.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/math/tst_psStats09.c	(revision 22322)
@@ -0,0 +1,359 @@
+/*****************************************************************************
+    This routine must ensure that PS_STAT_CLIPPED_MEAN and
+    PS_STAT_CLIPPED_STDEV is calculate correctly by the procedure
+    psVectorStats().
+ 
+    XXX: The capability is here to test a wide variety of input parameters.
+    We must do this, later.
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "float.h"
+#include <math.h>
+
+#define NUM_DATA 1000
+#define VERBOSE 1
+#define PERCENT_OUTLIERS 2
+#define ERROR_TOLERANCE .10
+#define ERRORS 1.0
+#define SEED 1995
+
+#define TST_IN_NULL  0x00000001
+#define TST_IN_F32  0x00000002
+#define TST_IN_F64  0x00000004
+#define TST_IN_S8  0x00000008
+#define TST_IN_U16  0x00000010
+#define TST_IN_S32  0x00000020
+#define TST_ERRORS_NULL  0x00000040
+#define TST_ERRORS_F32  0x00000080
+#define TST_ERRORS_F64  0x00000100
+#define TST_ERRORS_S8  0x00000200
+#define TST_ERRORS_U16  0x00000400
+#define TST_ERRORS_S32  0x00000800
+#define TST_MASK_NULL  0x00001000
+#define TST_MASK_U8  0x00002000
+#define TST_MASK_S32  0x00004000
+
+
+psBool genericClippedStatsTest(
+    unsigned int flags,
+    psS32 numData,
+    psU32 maskValue,
+    psBool expectedRC)
+{
+    psS32 currentId = psMemGetId();
+    psBool testStatus = true;
+    psS32 memLeaks = 0;
+    psVector *in = NULL;
+    psVector *errors = NULL;
+    psVector *mask = NULL;
+    srand(SEED);
+    printPositiveTestHeader(stdout, "psMathUtils functions", "psVectorStats Clipped Stats Routine");
+
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
+    psVector *truth = psVectorAlloc(numData, PS_TYPE_F64);
+    truth->n = numData;
+    for (long i = 0; i < numData; i++) {
+        truth->data.F64[i] = psRandomGaussian(rng);
+    }
+    psFree(rng);
+
+    if (expectedRC == true) {
+        printf("This test should not generate any errors.\n");
+    }
+    if (expectedRC == false) {
+        printf("This test should generate an error message, and return NULL.\n");
+    }
+
+    if (flags & TST_IN_NULL) {
+        printf("        using a NULL in vector\n");
+    }
+
+    if (flags & TST_IN_F32) {
+        printf("        using a psF32 in vector\n");
+        in = psVectorCopy(in, truth, PS_TYPE_F32);
+    }
+
+    if (flags & TST_IN_F64) {
+        printf("        using a psF64 in vector\n");
+        in = psVectorCopy(in, truth, PS_TYPE_F64);
+    }
+
+    if (flags & TST_IN_S8) {
+        printf("        using a psS8 in vector\n");
+        in = psVectorCopy(in, truth, PS_TYPE_S8);
+    }
+
+    if (flags & TST_IN_U16) {
+        printf("        using a psU16 in vector\n");
+        in = psVectorCopy(in, truth, PS_TYPE_U16);
+    }
+
+    if (flags & TST_IN_S32) {
+        printf("        using a psS32 in vector\n");
+        in = psVectorCopy(in, truth, PS_TYPE_S32);
+    }
+
+    if (flags & TST_ERRORS_NULL) {
+        printf("        using a NULL errors vector\n");
+    }
+
+    if (flags & TST_ERRORS_F32) {
+        printf("        using a psF32 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_F32);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.F32[i] = ERRORS;
+            errors->n++;
+        }
+    }
+
+    if (flags & TST_ERRORS_F64) {
+        printf("        using a psF64 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_F64);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.F64[i] = ERRORS;
+            errors->n++;
+        }
+    }
+
+    if (flags & TST_ERRORS_S8) {
+        printf("        using a psS8 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_S8);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.S8[i] = (psS8) ERRORS;
+            errors->n++;
+        }
+    }
+
+    if (flags & TST_ERRORS_U16) {
+        printf("        using a psU16 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_U16);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.U16[i] = (psU16) ERRORS;
+            errors->n++;
+        }
+    }
+
+    if (flags & TST_ERRORS_S32) {
+        printf("        using a psS32 errors vector\n");
+        errors = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            errors->data.S32[i] = (psS32) ERRORS;
+            errors->n++;
+        }
+    }
+
+
+    if (flags & TST_MASK_NULL) {
+        printf("        using a NULL mask vector\n");
+    }
+
+    if (flags & TST_MASK_U8) {
+        printf("        using a psU8 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_U8);
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.U8[i] = (psU8) 0;
+            mask->n++;
+        }
+    }
+
+    if (flags & TST_MASK_S32) {
+        printf("        using a psS32 mask vector\n");
+        mask = psVectorAlloc(numData, PS_TYPE_S32);
+        for (psS32 i=0;i<numData;i++) {
+            mask->data.S32[i] = (psS32) 0;
+            mask->n++;
+        }
+    }
+
+
+    //
+    // We add a few outliers to the input data.
+    //
+    psVector *outliers = psVectorAlloc(numData, PS_TYPE_U8);
+    outliers->n = numData;
+    psVectorInit(outliers, 0);
+    long numOutliers = 0;
+    if (flags & TST_IN_F32) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.F32[i] = 100.0;
+                outliers->data.U8[i] = 1;
+                numOutliers++;
+            }
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Data %d: (%f)\n", i, in->data.F32[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_F64) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.F64[i] = 100.0;
+                outliers->data.U8[i] = 1;
+                numOutliers++;
+            }
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Data %d: (%f)\n", i, in->data.F64[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_S8) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.S8[i] = 100;
+                outliers->data.U8[i] = 1;
+                numOutliers++;
+            }
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Data %d: (%d)\n", i, in->data.S8[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_U16) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.U16[i] = 100;
+                outliers->data.U8[i] = 1;
+                numOutliers++;
+            }
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Data %d: (%d)\n", i, in->data.U16[i]);
+            }
+        }
+    }
+    if (flags & TST_IN_S32) {
+        for (psS32 i=0;i<numData;i++) {
+            if (PERCENT_OUTLIERS > (random() % 100)) {
+                in->data.S32[i] = 100;
+                outliers->data.U8[i] = 1;
+                numOutliers++;
+            }
+        }
+
+        if (VERBOSE) {
+            for (psS32 i=0;i<numData;i++) {
+                printf("Data %d: (%d)\n", i, in->data.S32[i]);
+            }
+        }
+    }
+
+    printf("%ld outliers.\n", numOutliers);
+
+    //
+    // We calculate the sample mean and stdev without and outliers in the data.
+    // We will use this later in determining if the clipped stats are correct.
+    //
+    psF32 sampleMean;
+    psF32 sampleStdev;
+    if (expectedRC == true) {
+        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        psStats *rc = psVectorStats(myStats, in, errors, outliers, 1);
+        if (rc == NULL) {
+            printf("TEST ERROR: the psVectorStats() function returned NULL.\n");
+            testStatus = false;
+        } else {
+            sampleMean = myStats->sampleMean;
+            sampleStdev = myStats->sampleStdev;
+        }
+        psFree(myStats);
+    }
+    psFree(outliers);
+
+    //
+    // We call psVectorStats() and calculate the clipped stats.
+    //
+    psStats *myStats = psStatsAlloc(PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+    myStats->clipSigma = 5.0;
+    myStats->clipIter = 2;
+    psStats *rc = psVectorStats(myStats, in, errors, mask, maskValue);
+    if (rc == NULL) {
+        if (expectedRC == true) {
+            printf("TEST ERROR: the psVectorStats() function returned NULL.\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            printf("TEST ERROR: the psVectorStats() function returned non-NULL.\n");
+            testStatus = false;
+        }
+
+        printf("Used %ld data points after clipping %ld.\n", myStats->clippedNvalues,
+               in->n - myStats->clippedNvalues);
+
+        if (fabs(myStats->clippedMean - sampleMean) > (ERROR_TOLERANCE * sampleMean)) {
+            printf("TEST ERROR: the clipped mean was %f, should have been %f\n", myStats->clippedMean, sampleMean);
+            testStatus = false;
+        } else if (VERBOSE) {
+            printf("GOOD: the clipped mean was %f, should have been %f\n", myStats->clippedMean, sampleMean);
+        }
+
+        if (fabs(myStats->clippedStdev - sampleStdev) > (ERROR_TOLERANCE * sampleStdev)) {
+            printf("TEST ERROR: the clipped stdev was %f, should have been %f\n", myStats->clippedStdev, sampleStdev);
+            testStatus = false;
+        } else if (VERBOSE) {
+            printf("GOOD: the clipped stdev was %f, should have been %f\n", myStats->clippedStdev, sampleStdev);
+        }
+
+    }
+
+    psFree(myStats);
+    psFree(truth);
+    psFree(in);
+    psFree(errors);
+    psFree(mask);
+    psMemCheckCorruption(1);
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+
+    return(testStatus);
+}
+
+#define TRACE_LEVEL 0
+psS32 main()
+{
+    psLogSetFormat("HLNM");
+    psBool testStatus = true;
+
+    //
+    // We list pertinent psStats.c functions here for debugging ease.
+    //
+    psTraceSetLevel(".", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorSampleMean", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorMax", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorMin", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorCheckNonEmpty", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorNValues", TRACE_LEVEL);
+    psTraceSetLevel("p_psVectorClippedStats", TRACE_LEVEL);
+    psTraceSetLevel("p_psNormalizeVectorRange", TRACE_LEVEL);
+    psTraceSetLevel("psStatsAlloc", TRACE_LEVEL);
+    psTraceSetLevel("p_psConvertToF32", TRACE_LEVEL);
+    psTraceSetLevel("psVectorStats", TRACE_LEVEL);
+
+    testStatus &= genericClippedStatsTest(TST_IN_NULL | TST_ERRORS_NULL | TST_MASK_NULL, NUM_DATA, 1, false);
+    testStatus &= genericClippedStatsTest(TST_IN_F32 | TST_ERRORS_NULL | TST_MASK_NULL, NUM_DATA, 1, true);
+    testStatus &= genericClippedStatsTest(TST_IN_F64 | TST_ERRORS_NULL | TST_MASK_NULL, NUM_DATA, 1, true);
+    testStatus &= genericClippedStatsTest(TST_IN_F32 | TST_ERRORS_F32 | TST_MASK_NULL, NUM_DATA, 1, true);
+    testStatus &= genericClippedStatsTest(TST_IN_F32 | TST_ERRORS_NULL | TST_MASK_U8, NUM_DATA, 1, true);
+
+    if (testStatus) {
+        printf("TEST PASSED\n");
+    } else {
+        printf("TEST FAILED\n");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/.cvsignore	(revision 22322)
@@ -0,0 +1,23 @@
+temp
+.deps
+.libs
+Makefile
+Makefile.in
+tst_psImage
+tst_psScalar
+tst_psVector
+tst_psVectorSort_01
+tst_psVectorSort_02
+tst_psVectorSort_03
+tst_psVectorSort_04
+tap_psVector
+*.bb
+*.bbg
+*.da
+gmon.out
+tap_psImageInterpolate
+tap_psImage
+tap_psScalar
+tap_psVectorSort
+tap_psVectorSortIndex
+tap_psVectorSelect
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/Makefile.am	(revision 22322)
@@ -0,0 +1,32 @@
+
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/src/mathtypes \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSLIB_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpslib.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSLIB_LIBS)
+
+TEST_PROGS = \
+	tap_psScalar \
+	tap_psImage \
+	tap_psVector \
+	tap_psVectorSort \
+	tap_psVectorSortIndex \
+	tap_psVectorSelect
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psImage.c	(revision 22322)
@@ -0,0 +1,416 @@
+/** @file  tst_psImage.c
+ *
+ *  @brief Contains the tests for psImage.[ch]
+ *
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-05-05 00:09:04 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+#include "pslib.h"
+#include "tap.h"
+#include "pstap.h"
+
+
+#define OK(exp) \
+exp ? setOkay(1) : setOkay(0)
+
+#define RET_OK          \
+if ( ! Okay ) {     \
+    psFree(image);  \
+    return;         \
+}
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(402);
+
+    // testImageAlloc()
+    {
+        psMemId id = psMemGetId();
+        psImage* image = NULL;
+        psU32 sizes = 6;
+        psU32 numCols[] = {0,1,1,100,100,150};
+        psU32 numRows[] = {0,1,100,1,150,100};
+        psU32 types = 12;
+        psElemType type[] = { PS_TYPE_S8, PS_TYPE_S16, PS_TYPE_S32, PS_TYPE_S64,
+                              PS_TYPE_U8, PS_TYPE_U16, PS_TYPE_U32, PS_TYPE_U64,
+                              PS_TYPE_F32, PS_TYPE_F64 };
+        for (psU32 t=0;t<types;t++) {
+            for (psU32 i=0;i<sizes;i++) {
+                // Following should be an error if numRows[i] == 0 || numCols[i]==0
+                image = psImageAlloc(numCols[i],numRows[i],type[t]);
+                if (image == NULL) {
+                    if (numRows[i] == 0 || numCols[i] == 0) {
+                        continue;
+                    }
+                    ok(0, "psImageAlloc returned NULL for type %x, size %dx%d.",
+                       type[t], numCols[i], numRows[i]);
+                    psFree(image);
+                }
+
+                ok(image->type.dimen==PS_DIMEN_IMAGE && image->type.type==type[t],
+                    "psImageAlloc allocated dimen/type (%d/%d), expecting "
+                    "PS_IMAGE_DIMEN/%d", image->type.dimen,
+                    image->type.type, type[t]);
+                ok(image->numCols == numCols[i] && image->numRows == numRows[i],
+                    "psImageAlloc allocated size %dx%d, expecting be %dx%d "
+                    "(type = %d)", image->numCols, image->numRows, numCols[i],
+                    numRows[i],type[t]);
+                ok(image->col0 == 0 && image->row0 == 0,
+                    "psImageAlloc returned row0/col0 of %d/%d.  Expected 0/0.",
+                    image->row0, image->row0);
+                ok(image->parent == NULL, "psImageAlloc returned NULL parent");
+                ok(image->children == NULL,
+                    "psImageAlloc returned NULL children array");
+
+
+                if (image->children != NULL) {
+                    psFree(image);
+                }
+
+                switch (type[t]) {
+                case PS_TYPE_U16: {
+                        psU32 rows = numRows[i];
+                        psU32 cols = numCols[i];
+    
+                        for (psS32 r=0;r<rows;r++) {
+                            for (psS32 c=0;c<cols;c++) {
+                                image->data.U16[r][c] = 2*c+r;
+                            }
+                        }
+                        bool errorFlag = false;
+                        for (psS32 r=0;r<rows;r++) {
+                            for (psS32 c=0;c<cols;c++) {
+                                if (image->data.U16[r][c] != 2*c+r) {
+                                    errorFlag = true;
+                                }
+                            }
+                        }
+                        ok(!errorFlag, "psImageAlloc() set data correctly (U16)");
+                    }
+                    break;
+                case PS_TYPE_F32: {
+                        psU32 rows = numRows[i];
+                        psU32 cols = numCols[i];
+    
+                        for (psS32 r=0;r<rows;r++) {
+                            for (psS32 c=0;c<cols;c++) {
+                                image->data.F32[r][c] = 2.0f*c+r;
+                            }
+                        }
+                        bool errorFlag = false;
+                        for (psS32 r=0;r<rows;r++) {
+                            for (psS32 c=0;c<cols;c++) {
+                                if (fabsf(image->data.F32[r][c] - (2.0f*c+r)) > FLT_EPSILON) {
+                                    errorFlag = true;
+                                }
+                            }
+                        }
+                        ok(!errorFlag, "psImageAlloc() set data correctly (F32)");
+                    }
+                    break;
+                case PS_TYPE_F64: {
+                        psU32 rows = numRows[i];
+                        psU32 cols = numCols[i];
+    
+                        for (psS32 r=0;r<rows;r++) {
+                            for (psS32 c=0;c<cols;c++) {
+                                image->data.F64[r][c] = 2.0f*c+r;
+                            }
+                        }
+                        bool errorFlag = false;
+                        for (psS32 r=0;r<rows;r++) {
+                            for (psS32 c=0;c<cols;c++) {
+                                if (fabs(image->data.F64[r][c] - (2.0f*c+r)) > DBL_EPSILON) {
+                                    errorFlag = true;
+                                }
+                            }
+                        }
+                        ok(!errorFlag, "psImageAlloc() set data correctly (F64)");
+                    }
+                    break;
+                default: {
+                        // ignore type and just use as byte bucket.
+                        psU32 rows = numRows[i];
+                        psU32 cols = numCols[i]*PSELEMTYPE_SIZEOF(type[t]);
+    
+                        for (psS32 r=0;r<rows;r++) {
+                            for (psS32 c=0;c<cols;c++) {
+                                image->data.U8[r][c] = (uint8_t)(r + c);
+                            }
+                        }
+                        bool errorFlag = false;
+                        for (psS32 r=0;r<rows;r++) {
+                            for (psS32 c=0;c<cols;c++) {
+                                if (image->data.U8[r][c] != (uint8_t)(r + c)) {
+                                    errorFlag = true;
+                                }
+                            }
+                        }
+                        ok(!errorFlag, "psImageAlloc() set data correctly (default)");
+                    }
+                }
+                psFree(image);
+            }
+        }
+
+        // #548: Verify no memory leaks or corruption are detected after a valid psImage structure with multiple
+        // children is freed.
+        image = psImageAlloc(100,100,PS_TYPE_F32);
+        psImageSubset(image,((psRegion) {50,0,70,20}));
+        psImageSubset(image,((psRegion) {70,20,90,40}));
+        psFree(image);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testRegion()
+    {
+        psMemId id = psMemGetId();
+        psRegion region = psRegionSet(1,2,3,4);
+        char *tmpStr = psRegionToString(region);
+        ok(!(region.x0 != 1 || region.x1 != 2 || region.y0 != 3 || region.y1 != 4),
+            "The region attributes are set to (%s)", tmpStr);
+        psFree(tmpStr);
+        region = psRegionFromString("[1:2,3:4]");
+        tmpStr = psRegionToString(region);
+        ok(!(region.x0 != 0 || region.x1 != 2 || region.y0 != 2 || region.y1 != 4),
+            "The region attributes are set to (%s)", tmpStr);
+        psFree(tmpStr);
+        region = psRegionFromString("[1:2,3:]");
+        ok(isnan(region.x0),
+            "psRegionFromString returned a NULL pointer given a malformed string.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //psRegionForImage
+    {
+        psMemId id = psMemGetId();
+        psImage *in;
+        psRegion inReg;
+        psRegion out;
+        in = psImageAlloc(1, 1, PS_TYPE_S32);
+        inReg = psRegionSet(1, 2, 1, 2);
+        out = psRegionForImage(in,inReg);
+        psRegion inReg2;
+        psRegion out2;
+        inReg2 = psRegionSet(-1, 0, -2, -1);
+        out2 = psRegionForImage(in, inReg2);
+    
+        ok(!( out.x0 != 1 || out.x1 != 1 || out.y0 != 1 || out.y1 != 1 ),
+           "Region For Image returned correct values");
+    
+        ok(!( out2.x0 != 0 || out2.x1 != 1 || out2.y0 != 0 || out2.y1 != 0 ),
+           "Region For Image returned correct values");
+        psFree(in);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //psRegionForSquare//
+    {
+        psMemId id = psMemGetId();
+        float X = 1;
+        float Y = 1;
+        float RAD = 1;
+        psRegion out;
+        out = psRegionForSquare(X, Y, RAD);
+        ok(!(out.x0 != 0 || out.x1 != 3 || out.y0 != 0 || out.y1!= 3),
+            "Region For Square returned correct values");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Initialize an image with a given value.//
+    {
+        psMemId id = psMemGetId();
+        psImage *in1 = NULL;
+        psImage *in2 = NULL;
+        psImage *in3 = NULL;
+        psImage *in4 = NULL;
+        int nRow = 1;
+        int nCol = 1;
+        in1 = psImageAlloc(nRow, nCol, PS_TYPE_U8);
+        ok(psImageInit(in1, 5 ),
+             "ImageAlloc.  U8 Case - 1x1");
+        nRow = 5;
+        in2 = psImageAlloc(nRow, nCol, PS_TYPE_F32);
+        ok(psImageInit(in2, 3),
+             "ImageAlloc.  F32 Case - 5x1");
+    
+        nCol = 5;
+        in3 = psImageAlloc(nRow, nCol, PS_TYPE_F64);
+        ok(psImageInit(in3, 3.14),
+             "ImageAlloc.  F64 Case - 5x5");
+    
+        in4 = psImageAlloc(nRow, nCol, PS_TYPE_S32);
+        ok(psImageInit(in4, 3),
+             "ImageAlloc.  S32 Case - 5x5");
+        psFree(in1);
+        psFree(in2);
+        psFree(in3);
+        psFree(in4);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageSet()
+    {
+        psMemId id = psMemGetId();
+        psImage *image = NULL;
+        image = psImageAlloc(5, 4, PS_TYPE_S32);
+
+        //Set the position of the subimage relative to the parent.
+        P_PSIMAGE_SET_COL0(image, 10);
+        P_PSIMAGE_SET_ROW0(image, 10);
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 5; j++) {
+                image->data.S32[i][j] = i+j;
+            }
+        }
+
+        //Attempt to set a position in a NULL psImage*
+        psImage *none = NULL;
+        ok(! psImageSet(none, 1, 1, 1),
+            "psImageSet returned non-null");
+
+        //Attempt to set a position in a psImage* with negative numCols, numRows
+        none = psImageAlloc(2, 2, PS_TYPE_S32);
+        *(int*)&none->numCols = -1;
+        ok(! psImageSet(none, 1, 1, 1),
+            "psImageSet return false when passed an image with negative numCols");
+
+        *(int*)&none->numCols = 2;
+        *(int*)&none->numRows = -1;
+        ok(!psImageSet(none, 1, 1, 1),
+             "psImageSet failed to return false when passed an image with negative numRows");
+
+        //Attempt to set a position in a psImage* with negative col0, row0
+        *(int*)&none->numRows = 2;
+        P_PSIMAGE_SET_ROW0(none, -1);
+        ok(!psImageSet(none, 1, 1, 1),
+             "psImageSet failed to return false when passed an image with negative col0");
+
+        P_PSIMAGE_SET_COL0(none, -1);
+        P_PSIMAGE_SET_ROW0(none, 0);
+        ok(!psImageSet(none, 1, 1, 1),
+             "psImageSet failed to return false when passed an image with negative col0");
+        psFree(none);
+
+        //Try to set a position inside of the subimage.
+        ok(psImageSet(image, 14, 12, 666),
+             "psImageSet failed to return true when passed valid parameters");
+
+        ok(image->data.S32[2][4] == 666,
+            "psImageSet set the data value. value=%d", image->data.S32[2][4]);
+
+        //Try to set a position inside of the subimage from the tail.
+        ok(psImageSet(image, -1, -1, 666),
+             "psImageSet return true when passed valid parameters");
+
+        ok(image->data.S32[3][4] == 666,
+            "psImageSet set (from the tail) the data values correctly");
+
+        //Try to set a position outside of the subimage but inside of the parent image.
+        ok(!psImageSet(image, 0, 0, 666),
+             "psImageSet return false when passed an invalid location");
+
+        ok(!psImageSet(image, 9, 10, 666),
+             "psImageSet return false when passed an invalid location");
+
+        //Try to set a position outside of the subimage.
+        ok(!psImageSet(image, 15, 14, 666),
+             "psImageSet return false when passed an invalid location");
+
+        //Try to set a position outside of the subimage, indexing from the tail.
+        ok(!psImageSet(image, -6, -1, 666),
+             "psImageSet return false when passed an invalid location");
+        psFree(image);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testImageGet()
+    {
+        psMemId id = psMemGetId();
+        psImage *image = NULL;
+        image = psImageAlloc(5, 3, PS_TYPE_S32);
+
+        //Set the position of the subimage relative to the parent.
+        P_PSIMAGE_SET_COL0(image, 10);
+        P_PSIMAGE_SET_ROW0(image, 10);
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 5; j++) {
+                image->data.S32[i][j] = i+j;
+            }
+        }
+    
+        //Attempt to get a position in a NULL psImage*
+        psImage *none = NULL;
+        ok(isnan( psImageGet(none, 1, 1) ),
+             "psImageGet return false when passed a NULL image");
+    
+        //Attempt to get a position in a psImage* with negative numCols, numRows
+        none = psImageAlloc(2, 2, PS_TYPE_S32);
+        *(int*)&none->numCols = -1;
+        ok(isnan( psImageGet(none, 1, 1) ),
+             "psImageGet return false when passed an image with negative numCols");
+    
+        *(int*)&none->numCols = 2;
+        *(int*)&none->numRows = -1;
+        ok(isnan( psImageGet(none, 1, 1) ),
+             "psImageGet return false when passed an image with negative numRows");
+    
+        //Attempt to get a position in a psImage* with negative col0, row0
+        *(int*)&none->numRows = 2;
+        P_PSIMAGE_SET_ROW0(none, -1);
+        ok(isnan( psImageGet(none, 1, 1) ),
+             "psImageGet return false when passed an image with negative col0");
+    
+        P_PSIMAGE_SET_COL0(none, -1);
+        P_PSIMAGE_SET_ROW0(none, 0);
+        ok(isnan( psImageGet(none, 1, 1) ),
+             "psImageGet return false when passed an image with negative col0");
+    
+        psFree(none);
+    
+    
+        //Try to get a position inside of the subimage.
+        ok(psImageGet(image, 14, 12) == 6,
+             "psImageGet return the correct value");
+    
+        //Try to get a position inside of the subimage from the tail.
+        ok(psImageGet(image, -1, -1) == 6,
+             "psImageGet return the correct value");
+    
+        //Try to get a position outside of the subimage but inside of the parent image.
+        ok(isnan( psImageGet(image, 1, 1) ),
+             "psImageGet return NAN when passed an invalid location");
+    
+        ok(isnan( psImageGet(image, 9, 10) ),
+             "psImageGet return NAN when passed an invalid location");
+    
+        //Try to set a position outside of the subimage.
+        ok(isnan( psImageGet(image, 15, 14) ),
+             "psImageGet return NAN when passed an invalid location");
+    
+        //Try to set a position outside of the subimage, indexing from the tail.
+        ok(isnan( psImageGet(image, -6, -1) ),
+             "psImageGet failed to return NAN when passed an invalid location");
+        psFree(image);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psImageInterpolate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psImageInterpolate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psImageInterpolate.c	(revision 22322)
@@ -0,0 +1,264 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int main (void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(47);
+
+    // very simple tests: no mask, bilinear mode, xramp image only
+    {
+        psMemId id = psMemGetId();
+        // generate simple image (x ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        image->data.F32[10][10] = 1;
+
+        // center of pixels is 0.5, 0.5
+        float value;
+
+        value = psImagePixelInterpolate (image, 10.5, 10.5, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 1.0, "pixel center value - %f", value);
+
+        value = psImagePixelInterpolate (image, 10.9, 10.5, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float_tol (value, 0.6, 4.0*FLT_EPSILON, "pixel value - %.20f", value);
+
+        value = psImagePixelInterpolate (image, 10.5, 10.9, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float_tol (value, 0.6, 4.0*FLT_EPSILON, "pixel value - %.20f", value);
+
+        value = psImagePixelInterpolate (image, 10.1, 10.5, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float_tol (value, 0.6, 4.0*FLT_EPSILON, "pixel value - %.20f", value);
+
+        skip_end();
+
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // very simple tests: no mask, bilinear mode, xramp image only
+    {
+        psMemId id = psMemGetId();
+        // generate simple image (x ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                image->data.F32[j][i] = i + 0.5;
+            }
+        }
+
+        // center of pixels is 0.5, 0.5
+        float value;
+
+        value = psImagePixelInterpolate (image, 2.5, 2.5, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 2.5, "pixel center value - %f", value);
+
+        value = psImagePixelInterpolate (image, 2.2, 2.5, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 2.2, "coord: 2.2, 2.5, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.8, 2.5, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 2.8, "coord: 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.8, 2.2, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 2.8, "coord: 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.8, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 2.8, "coord: 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 0.8, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 0.8, "coord: 0.8, value: %f", value);
+
+        // no extrapolation
+        value = psImagePixelInterpolate (image, 0.3, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 0.5, "coord: 0.3, value: %f", value);
+
+        value = psImagePixelInterpolate (image, -0.2, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 0.5, "coord: -0.2, value: %f", value);
+
+        skip_end();
+
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // very simple tests: no mask, bilinear mode, yramp image only
+    {
+        psMemId id = psMemGetId();
+        // generate simple image (y ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                image->data.F32[j][i] = j + 0.5;
+            }
+        }
+
+        // center of pixels is 0.5, 0.5
+        float value;
+
+        value = psImagePixelInterpolate (image, 2.5, 2.5, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 2.5, "pixel center value - %f", value);
+
+        value = psImagePixelInterpolate (image, 2.2, 2.2, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 2.2, "coord: 2.2, 2.5, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.5, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 2.8, "coord: 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.2, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 2.8, "coord: 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.8, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        is_float (value, 2.8, "coord: 2.8, value: %f", value);
+
+        skip_end();
+
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // very simple tests: no mask, bicube mode, xramp image only
+    {
+        psMemId id = psMemGetId();
+        // generate simple image (x ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                image->data.F32[j][i] = i + 0.5;
+            }
+        }
+
+        // center of pixels is 0.5, 0.5
+        float value;
+
+        value = psImagePixelInterpolate (image, 2.5, 2.5, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 2.5, "coord; 2.5, 2.5, value - %f", value);
+
+        value = psImagePixelInterpolate (image, 2.2, 2.5, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 2.2, "coord: 2.2, 2.5, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.8, 2.5, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 2.8, "coord: 2.8, 2.5, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.8, 2.2, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 2.8, "coord: 2.8, 2.2, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.8, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 2.8, "coord: 2.8, 2.8, value: %f", value);
+
+        // no extrapolation: these return the 'uncover' value
+        value = psImagePixelInterpolate (image, 0.8, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 0.0, "coord: 0.8, 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 0.3, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 0.0, "coord: 0.3, 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, -0.2, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 0.0, "coord: -0.2, 2.8, value: %f", value);
+
+        skip_end();
+
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // very simple tests: no mask, bilinear mode, yramp image only
+    {
+        psMemId id = psMemGetId();
+        // generate simple image (y ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                image->data.F32[j][i] = j + 0.5;
+            }
+        }
+
+        // center of pixels is 0.5, 0.5
+        float value;
+
+        value = psImagePixelInterpolate (image, 2.5, 2.5, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 2.5, "pixel center value - %f", value);
+
+        value = psImagePixelInterpolate (image, 2.2, 2.2, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 2.2, "coord: 2.2, 2.5, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.5, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 2.8, "coord: 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.2, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 2.8, "coord: 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.8, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 2.8, "coord: 2.8, value: %f", value);
+
+        skip_end();
+
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // very simple tests: no mask, bilinear mode, x,y 2nd order shape
+    {
+        psMemId id = psMemGetId();
+        // generate simple image (x ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                image->data.F32[j][i] = 0.25*PS_SQR(i + 0.5) + j + 0.5;
+            }
+        }
+
+        // center of pixels is 0.5, 0.5
+        float value;
+
+        value = psImagePixelInterpolate (image, 2.5, 2.5, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 4.0625, "pixel center value - %f", value);
+
+        value = psImagePixelInterpolate (image, 2.2, 2.2, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 3.41, "coord: 2.2, 2.5, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.5, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 4.3625002, "coord: 2.5, 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.2, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 4.010000229, "coord: 2.2, 2.8, value: %f", value);
+
+        value = psImagePixelInterpolate (image, 2.8, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
+        is_float (value, 4.75999975, "coord: 2.8, 2.8, value: %f", value);
+
+        skip_end();
+
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psScalar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psScalar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psScalar.c	(revision 22322)
@@ -0,0 +1,110 @@
+/** @file  tst_psScalar.c
+ *
+ *  @brief Contains the tests for psScalar.[ch]
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.5 $
+ *           $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-04-10 21:09:30 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+#define tstScalarAllocByType(datatype,value)            \
+{                                                       \
+    psScalar* scalar;                                   \
+    scalar = psScalarAlloc(value,PS_TYPE_##datatype);   \
+    ok(scalar != NULL, "psScalarAlloc successful");     \
+    skip_start(scalar==NULL,1,"Skipping 1 test because psScalarAlloc failed");\
+    ok(scalar->type.type==PS_TYPE_##datatype && scalar->data.datatype==value, \
+       "scalar datatype and value are legit" );        \
+    skip_end();                                         \
+    psFree(scalar);                                     \
+}
+
+
+#define tstScalarCopyByType(datatype,value)                 \
+{                                                           \
+    psScalar*  scalarOrig;                                  \
+    psScalar*  scalarCopy;                                  \
+    scalarOrig = psScalarAlloc(value,PS_TYPE_##datatype);   \
+    scalarCopy = psScalarCopy(scalarOrig);                  \
+    ok(scalarCopy != NULL, "psScalarCopy != NULL");         \
+    skip_start(scalarCopy==NULL,1,"Skipping 1 test since psScalarCopy failed");\
+    ok(scalarCopy->type.type == scalarOrig->type.type && scalarCopy->data.datatype == scalarOrig->data.datatype, "Scalar datatype and value legit"); \
+    skip_end();                                             \
+    psFree(scalarCopy);                                     \
+    psFree(scalarOrig);                                     \
+}
+
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(47);
+
+
+    // Verify return is null for invalid scalar type
+    {
+        psMemId id = psMemGetId();
+        psScalar* scalar = psScalarAlloc(true,PS_TYPE_BOOL);
+        ok(scalar == NULL, "psScalarAlloc returns null for invalid type");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Verify the proper allocation/deallocation of scalar objects of valid types
+    {
+        psMemId id = psMemGetId();
+        tstScalarAllocByType(S8,10);
+        tstScalarAllocByType(U8,12);
+        tstScalarAllocByType(S16,14);
+        tstScalarAllocByType(U16,16);
+        tstScalarAllocByType(S32,18);
+        tstScalarAllocByType(U32,20);
+        tstScalarAllocByType(S64,22);
+        tstScalarAllocByType(U64,24);
+        tstScalarAllocByType(F32,26);
+        tstScalarAllocByType(F64,28);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify the return is null for invalid scalar type in the original
+    {
+        psMemId id = psMemGetId();
+        psScalar* scalarOrig = psScalarAlloc(0,PS_TYPE_S8);
+        scalarOrig->type.type = PS_TYPE_BOOL;
+        psScalar* scalarCopy = psScalarCopy(scalarOrig);
+        ok(scalarCopy == NULL, "psScalarCopy returns NULL for invalid type");
+        scalarCopy = psScalarCopy(NULL);
+        ok(scalarCopy == NULL, "psScalarCopy returns NULL for NULL argument");
+        psFree(scalarOrig);
+        psFree(scalarCopy);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify the proper copying of scalar objects for all valid types
+    {
+        psMemId id = psMemGetId();
+        tstScalarCopyByType(S8,100);
+        tstScalarCopyByType(U8,110);
+        tstScalarCopyByType(S16,120);
+        tstScalarCopyByType(U16,130);
+        tstScalarCopyByType(S32,140);
+        tstScalarCopyByType(U32,150);
+        tstScalarCopyByType(S64,160);
+        tstScalarCopyByType(U64,170);
+        tstScalarCopyByType(F32,180);
+        tstScalarCopyByType(F64,190);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVector.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVector.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVector.c	(revision 22322)
@@ -0,0 +1,787 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main (void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(294);
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+        ok(psVec != NULL, "psVector successfully allocated");
+        skip_start(psVec == NULL, 4, "Skipping 4 tests because psVectorAlloc() failed");
+        ok(psVec->nalloc == 5, "Vector size = %ld", psVec->nalloc);
+        ok(psVec->n == 5, "Vector population = %ld", psVec->n);
+        ok(psVec->type.type == PS_TYPE_S32, "Vector type = %d", psVec->type.type);
+        ok(psVec->type.dimen == PS_DIMEN_VECTOR, "Vector dimen = %d", psVec->type.dimen);
+        skip_end();
+        psFree(psVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector* vecZero = psVectorAlloc(0, PS_TYPE_S32);
+        ok(vecZero != NULL, "zero-length vector allocated");
+        skip_start(vecZero == NULL, 2, "Skipping 2 tests because psVectorAlloc() failed");
+        ok(vecZero->nalloc == 0, "Vector size = %ld", vecZero->nalloc);
+        ok(vecZero->n == 0, "Vector population = %ld", vecZero->n);
+        skip_end();
+        psFree(vecZero);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector* vecBogus = psVectorAlloc(10, 0);
+        ok(vecBogus == NULL, "psVectorAlloc() doesn't not accept bogus type");
+        psErr *err = psErrorLast();
+        skip_start(err == NULL, 2, "Skipping 2 tests because psVectorAlloc() didn't fail as expect");
+        ok(strstr(err->name, "vectorAlloc"), "alloc failure - got error name %s", err->name);
+        ok(err->code == PS_ERR_BAD_PARAMETER_TYPE, "alloc failure - got error code %d", err->code);
+        skip_end();
+        psFree(vecBogus);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorRealloc() tests
+    {
+        psMemId id = psMemGetId();
+        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+        ok(psVec->n == 5, "Vector population = %ld", psVec->n);
+
+        // generate first part of vector
+        for(psS32 i = 0; i < 5; i++) {
+            psVec->data.S32[i] = i*10;
+        }
+
+        // Test C - Reallocate S32 vector bigger
+        ok(psVec->n == 5, "Vector population = %ld, should be %ld", psVec->n, 5);
+        psVec = psVectorRealloc(psVec,10);
+        ok(psVec != NULL, "test vector reallocated to bigger size");
+        skip_start(psVec == NULL, 4, "Skipping 4 tests because psVectorRealloc() failed");
+        ok(psVec->nalloc == 10, "Vector size = %ld", psVec->nalloc);
+        ok(psVec->n == 5, "Vector population = %ld", psVec->n);
+        ok(psVec->type.type == PS_TYPE_S32, "Vector type = %d", psVec->type.type);
+        ok(psVec->type.dimen == PS_DIMEN_VECTOR, "Vector dimen = %d", psVec->type.dimen);
+        skip_end();
+
+        // generate test data values
+        for(psS32 i = 5; i < 10; i++) {
+            psVec->data.S32[i] = i*10;
+            psVec->n++;
+        }
+
+        // test data values
+        skip_start(psVec == NULL, 10, "Skipping 10 tests because psVectorRealloc() failed");
+        for(psS32 i = 0; i < 10; i++) {
+            ok (psVec->data.S32[i] == i*10, "Elem %d = %d, expected %d", i, psVec->data.S32[i], i*10);
+        }
+        skip_end();
+        psFree(psVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test D - Reallocate S32 vector smaller
+    {
+        psMemId id = psMemGetId();
+        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+        // generate test data values
+        for(psS32 i = 0; i < 5; i++) {
+            psVec->data.S32[i] = i*10;
+            psVec->n++;
+        }
+        psVec = psVectorRealloc(psVec, 3);
+        ok(psVec != NULL, "test vector reallocated to smaller size");
+        skip_start(psVec == NULL, 5, "Skipping 6 tests because psVectorRealloc() failed");
+        ok(psVec->nalloc == 3, "Vector size = %ld", psVec->nalloc);
+        ok(psVec->n == 3, "Vector population = %ld", psVec->n);
+
+        // check that the test data values survived the realloc
+        for(psS32 i = 0; i < 3; i++) {
+            ok (psVec->data.S32[i] == i*10, "Elem %d = %d, expected %d", i, psVec->data.S32[i], i*10);
+        }
+        skip_end();
+        psFree(psVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // reallocate to 0 length
+    {
+        psMemId id = psMemGetId();
+        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+        psVec = psVectorRealloc(psVec,0);
+
+        ok(psVec != NULL, "test vector reallocated to zero length");
+        skip_start(psVec == NULL, 2, "Skipping 2 tests because psVectorRealloc() failed");
+        ok(psVec->nalloc == 0, "Vector size = %ld", psVec->nalloc);
+        ok(psVec->n == 0, "Vector population = %ld", psVec->n);
+        skip_end();
+
+        // Test E - Free S32 vector
+        // XXX not really a test...
+        psFree(psVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vecBogus = psVectorRealloc(NULL, 6);
+        ok(vecBogus == NULL, "psVectorRealloc() doesn't not accept bogus type");
+        psErr *err = psErrorLast();
+        skip_start(err == NULL, 2, "Skipping 2 tests because psVectorRealloc() didn't fail as expect");
+        ok(strstr(err->name, "psVectorRealloc"), "alloc failure - got error name %s", err->name);
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL, "alloc failure - got error code %d", err->code);
+        skip_end();
+        psFree(vecBogus);
+        psFree(err);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorExtend() tests
+    {
+        psMemId id = psMemGetId();
+        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+        psVec = psVectorExtend(psVec, 0, 2);
+        ok(psVec != NULL, "test vector extended");
+        skip_start(psVec == NULL, 2, "Skipping 2 tests because psVectorExtend() failed");
+        // Skipping this because it appears the psVectorExtend() changes since the test was written.
+        // ok(psVec->nalloc == 5, "Vector size = %ld", psVec->nalloc);
+        ok(psVec->n == 7, "Vector population = %ld", psVec->n);
+        skip_end();
+        psFree(psVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+        psVec = psVectorExtend(psVec, 0, 2);
+        psVec = psVectorExtend(psVec, 0, 2);
+        ok(psVec != NULL, "test vector extended");
+        skip_start(psVec == NULL, 2, "Skipping 2 tests because psVectorExtend() failed");
+        // Skipping this because it appears the psVectorExtend() changes since the test was written.
+        //ok(psVec->nalloc == 15, "Vector size = %ld", psVec->nalloc);
+        ok(psVec->n == 9,"Vector population = %ld", psVec->n);
+        skip_end();
+        psFree(psVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+        psVec = psVectorExtend(psVec, 0, 2);
+        psVec = psVectorExtend(psVec, 0, 2);
+        psVec = psVectorExtend(psVec, 0, -2);
+        ok(psVec != NULL, "test vector extended");
+        skip_start(psVec == NULL, 2, "Skipping 2 tests because psVectorExtend() failed");
+        // Skipping this because it appears the psVectorExtend() changes since the test was written.
+        // ok(psVec->nalloc == 15 ,"Vector size = %ld", psVec->nalloc);
+        ok(psVec->n == 7, "Vector population = %ld", psVec->n);
+        skip_end();
+        psFree(psVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+        psVec = psVectorExtend(psVec, 0, 2);
+        psVec = psVectorExtend(psVec, 0, 2);
+        psVec = psVectorExtend(psVec, 0, -2);
+        psVec = psVectorExtend(psVec, 0, -20);
+        ok(psVec != NULL, "test vector extended");
+        skip_start(psVec == NULL, 2, "Skipping 2 tests because psVectorExtend() failed");
+        // Skipping this because it appears the psVectorExtend() changes since the test was written.
+        // ok(psVec->nalloc == 15, "Vector size = %ld", psVec->nalloc);
+        ok(psVec->n == 0, "Vector population = %ld", psVec->n);
+        skip_end();
+        psFree(psVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorInit() tests
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_U8);
+        ok(psVectorInit(vec, 1 ), "U8 Case");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_U16);
+        ok(!psVectorInit(vec, PS_MAX_U64), "VectorInit failed.  U16 Case");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_U32);
+        ok(psVectorInit(vec, 10), "U32 Case");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_U64);
+        ok(psVectorInit(vec, PS_MIN_U64), "U64 Case");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_S8);
+        ok(!psVectorInit(vec, PS_MAX_S16), "VectorInit failed.  S8 Case");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_S16);
+        ok(psVectorInit(vec, -100), "S16 Case");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_S32);
+        ok(psVectorInit(vec, 1), "S32 Case");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // XXX is this test ok?
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_S64);
+        ok(psVectorInit(vec, PS_MAX_S64), "S64 Case");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_F32);
+        ok(psVectorInit(vec, 1.1), "F32 Case");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_F64);
+        ok(psVectorInit(vec, 1.4 ), "F64 Case");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorCreate() tests
+    {
+        psMemId id = psMemGetId();
+        psVector *test = psVectorCreate(NULL, 0.0, 10.0, 1.0, PS_TYPE_S8);
+        for (int i = 0; i < 10; i++) {
+            ok(test->data.S8[i] == i, "Vector data matches. i = %d, data=%d",
+               i, test->data.S8[i]);
+        }
+        psFree(test);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *test = psVectorCreate(NULL, 0.0, 10.0, 1.0, PS_TYPE_U8);
+        for (int i = 0; i < 10; i++) {
+            ok(test->data.U8[i] == i, "Vector data matches. i = %d, data=%d",
+               i, test->data.U8[i]);
+        }
+        psFree(test);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *test = psVectorCreate(NULL, 0.0, 10.0, 1.0, PS_TYPE_U32);
+        for (int i = 0; i < 10; i++) {
+            ok(test->data.U32[i] == i, "Vector data matches. i = %d, data=%d",
+               i, test->data.U32[i]);
+        }
+        psFree(test);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *test = psVectorCreate(NULL, 0.0, 10.0, 1.0, PS_TYPE_S32);
+        for (int i = 0; i < 10; i++) {
+            ok(test->data.S32[i] == i, "Vector data matches. i = %d, data=%d",
+               i, test->data.S32[i]);
+        }
+        psFree(test);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *input = psVectorAlloc(5, PS_TYPE_F32);
+        psVector *test = psVectorCreate(input, 0.0, 5.0, 0.5, PS_TYPE_F32);
+
+        for (int i = 0; i < 10; i++) {
+            ok(test->data.F32[i] == i * 0.5,
+               "Vector data matches. i = %d, data=%f", i, test->data.F32[i]);
+        }
+        psFree(input);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //  Test PS_TYPE_F64
+    //       PS_TYPE_S64
+    //       PS_TYPE_U64
+    {
+        psMemId id = psMemGetId();
+        psVector *test = psVectorCreate(NULL, 0.0, 10.0, 1.0, PS_TYPE_S64);
+        for (int i = 0; i < 10; i++)
+        {
+            ok(test->data.S64[i] == i,
+               "Vector data does not match. i = %d, data=%ld",
+               i, (long)test->data.S64[i]);
+        }
+        psFree(test);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *test = psVectorCreate(NULL, 0.0, 10.0, 1.0, PS_TYPE_U64);
+        for (int i = 0; i < 10; i++)
+        {
+            ok(test->data.U64[i] == i,
+               "Vector data does not match. i = %d, data=%ld",
+               i, (long)test->data.U64[i]);
+        }
+        psFree(test);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *input = psVectorAlloc(5, PS_TYPE_F64);
+        psVector *test = psVectorCreate(input, 0.0, 5.0, 0.5, PS_TYPE_F64);
+        for (int i = 0; i < 10; i++)
+        {
+            ok(test->data.F64[i] == i * 0.5,
+               "Vector data does not match. i = %d, data=%f",
+               i, test->data.F64[i]);
+        }
+        psFree(input);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //  Test PS_TYPE_U16
+    //       PS_TYPE_S16
+    {
+        psMemId id = psMemGetId();
+        psVector *test = psVectorCreate(NULL, 0.0, 10.0, 1.0, PS_TYPE_S16);
+        for (int i = 0; i < 10; i++)
+        {
+            ok(test->data.S16[i] == i, "Vector data matches. i = %d, data=%d",
+               i, test->data.S16[i]);
+        }
+        psFree(test);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *input = psVectorAlloc(5, PS_TYPE_U16);
+        psVector *test = psVectorCreate(input, 0.0, 20.0, 2.0, PS_TYPE_U16);
+        for (int i = 0; i < 10; i++)
+        {
+            ok(test->data.U16[i] == i * 2.0,
+               "Vector data matches. i = %d, data=%d", i, test->data.U16[i]);
+        }
+        psFree(input);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    #define TEST_VECTOR_TO_STRING(TYPE, SIZE, START, INC, EXPECTED)         \
+    {                                                                       \
+        psMemId id = psMemGetId(); \
+        const int asize = SIZE;  /* alloc size */                           \
+        const int osize = 100;   /* output size (max) */                    \
+        const char* const expected = EXPECTED;                              \
+        \
+        psVector *input = psVectorAlloc(asize, TYPE);                       \
+        psVector *test = psVectorCreate(input, START, START+asize*INC,      \
+                                        INC, TYPE);                                                     \
+        \
+        psString result = psVectorToString(test, osize);                    \
+        ok(strcmp(result, expected) == 0,                                   \
+           "psVectorToString expected:result = %s:%s", expected, result ); \
+        \
+        psFree(input);                                                      \
+        psFree(result);                                                      \
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks"); \
+    }
+
+    // psVectorToString() tests
+    TEST_VECTOR_TO_STRING(PS_TYPE_U8,  5, 1, 1, "[1,2,3,4,5]");
+    TEST_VECTOR_TO_STRING(PS_TYPE_U16, 5, 1, 1, "[1,2,3,4,5]");
+    TEST_VECTOR_TO_STRING(PS_TYPE_U32, 5, 1, 1, "[1,2,3,4,5]");
+    TEST_VECTOR_TO_STRING(PS_TYPE_U64, 5, 1, 1, "[1,2,3,4,5]");
+    TEST_VECTOR_TO_STRING(PS_TYPE_S8,  5, -100, 10,"[-100,-90,-80,-70,-60]");
+    TEST_VECTOR_TO_STRING(PS_TYPE_S16, 5, 0, -100, "[0,-100,-200,-300,-400]");
+    TEST_VECTOR_TO_STRING(PS_TYPE_S32, 5, 0, -100, "[0,-100,-200,-300,-400]");
+    TEST_VECTOR_TO_STRING(PS_TYPE_S64, 5, 0, -100, "[0,-100,-200,-300,-400]");
+    TEST_VECTOR_TO_STRING(PS_TYPE_F32,5,.123,1,"[0.123,1.123,2.123,3.123,4.123]");
+    TEST_VECTOR_TO_STRING(PS_TYPE_F64,5,.123,1,"[0.123,1.123,2.123,3.123,4.123]");
+
+
+    #define TEST_VECTOR_GET_ELEMENT_F64(ELEM_TYPE, SIZE, VALUE_TYPE,        \
+                                        INIT_VALUE )                                                        \
+    {                                                                       \
+        psMemId id = psMemGetId(); \
+        psVector *vec = psVectorAlloc(SIZE, ELEM_TYPE);                     \
+        vec->n = SIZE;                                                      \
+        VALUE_TYPE initValue = INIT_VALUE;                                  \
+        psVectorInit(vec, initValue);                                       \
+        psF64 result = p_psVectorGetElementF64(vec, 0);                     \
+        psF64 expected = (psF64)initValue;                                  \
+        ok(result == expected,                                              \
+           "p_psVectorGetElementF64 expected:result = %f:%f",expected,result);\
+        \
+        psFree(vec);                                                        \
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks"); \
+    }
+    // p_psVectorGetElementF64() tests
+    TEST_VECTOR_GET_ELEMENT_F64(PS_TYPE_U8, 1, psU8, 1);
+    TEST_VECTOR_GET_ELEMENT_F64(PS_TYPE_U16, 2, psU16, 2);
+    TEST_VECTOR_GET_ELEMENT_F64(PS_TYPE_U32, 3, psU32, 3);
+    TEST_VECTOR_GET_ELEMENT_F64(PS_TYPE_U64, 100, psU64, 999999);
+    TEST_VECTOR_GET_ELEMENT_F64(PS_TYPE_S8,  1, psS8, -1);
+    TEST_VECTOR_GET_ELEMENT_F64(PS_TYPE_S16, 2, psS16, -2);
+    TEST_VECTOR_GET_ELEMENT_F64(PS_TYPE_S32, 3, psS32, -3);
+    TEST_VECTOR_GET_ELEMENT_F64(PS_TYPE_S64, 100, psS64, -999999);
+    TEST_VECTOR_GET_ELEMENT_F64(PS_TYPE_F32,1, psF32, .123);
+    TEST_VECTOR_GET_ELEMENT_F64(PS_TYPE_F64,2, psF64, -123.123);
+
+
+    // XXX: Why are we testing private functions?
+    #define TEST_VECTOR_PRINT(ELEM_TYPE, SIZE, VALUE_TYPE, INIT_VALUE, FD,  \
+                              OUT_NAME, EXPECTED_RC )                                             \
+    {                                                                       \
+        psMemId id = psMemGetId(); \
+        psVector *vec = psVectorAlloc(SIZE, ELEM_TYPE);                     \
+        vec->n = SIZE;                                                      \
+        VALUE_TYPE initValue = INIT_VALUE;                                  \
+        psVectorInit(vec, initValue);                                       \
+        psBool result = p_psVectorPrint(FD, vec, OUT_NAME);                 \
+        ok(result == EXPECTED_RC,                                           \
+           "p_psVectorPrint expected:result = %d:%d",EXPECTED_RC,result);  \
+        \
+        psFree(vec);                                                        \
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks"); \
+    }
+    // p_psVectorPrint() tests
+    TEST_VECTOR_PRINT(PS_TYPE_U8, 1, psU8, 1, 1, "PS_TYPE_U8", TRUE);
+    TEST_VECTOR_PRINT(PS_TYPE_U16, 2, psU16, 2, 1, "PS_TYPE_U16", TRUE);
+    TEST_VECTOR_PRINT(PS_TYPE_U32, 3, psU32, 3, 1, "PS_TYPE_U32", TRUE);
+    TEST_VECTOR_PRINT(PS_TYPE_U64, 4, psU64, 999999, 1, "PS_TYPE_U64", TRUE);
+    TEST_VECTOR_PRINT(PS_TYPE_S8,  1, psS8, -1, 1, "PS_TYPE_S8", TRUE);
+    TEST_VECTOR_PRINT(PS_TYPE_S16, 2, psS16, -2, 1, "PS_TYPE_S16", TRUE);
+    TEST_VECTOR_PRINT(PS_TYPE_S32, 3, psS32, -3, 1, "PS_TYPE_S32", TRUE);
+    TEST_VECTOR_PRINT(PS_TYPE_S64, 4, psS64, -999999, 1, "PS_TYPE_S64", TRUE);
+    TEST_VECTOR_PRINT(PS_TYPE_F32,1, psF32, .123, 1, "PS_TYPE_F32", TRUE);
+    TEST_VECTOR_PRINT(PS_TYPE_F64,2, psF64, -123.123, 1, "PS_TYPE_F64", TRUE);
+
+    // psVectorSet() tests
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(5, PS_TYPE_S32);
+        ok(psVectorSet(vec, 0, 10),
+           "VectorSet set S32 at position 0");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(5, PS_TYPE_S32);
+        ok(!psVectorSet(vec, 10, 10),
+           "VectorSet failes to set S32 at out of range position");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(5, PS_TYPE_S32);
+        ok(psVectorSet(vec, 1, 4) == true,
+           "VectorSet set S32 at position 1");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorGet() tests
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(10, PS_TYPE_S32);
+        psVectorSet(vec, 0, 10);
+        ok((psS32)psVectorGet(vec, 0) == 10,
+           "VectorGet returned the correct S32 from position 0");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(5, PS_TYPE_S32);
+        psVectorSet(vec, -1, 4);
+        ok((psS32)psVectorGet(vec, -1) == 4,
+           "VectorGet returned the correct S32 from tail using -1");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorCountPixelMask() tests
+    // Ensure -1 return for NULL psVector input
+    {
+        psMemId id = psMemGetId();
+        ok(psVectorCountPixelMask(NULL, 1) == -1,
+           "psVectorCountPixelMask() returned -1 for NULL psVector input");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Ensure -1 return for incorrect TYPE psVector input
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(5, PS_TYPE_S32);
+        vec->data.S32[0] = 0;
+        vec->data.S32[1] = 1;
+        vec->data.S32[2] = 0;
+        vec->data.S32[3] = 1;
+        vec->data.S32[4] = 0;
+        ok(psVectorCountPixelMask(vec, 1) == -1,
+           "psVectorCountPixelMask() returned -1 for incorrect TYPE psVector input");
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Use correct inputs
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(5, PS_TYPE_U8);
+        vec->data.U8[0] = 0;
+        vec->data.U8[1] = 1;
+        vec->data.U8[2] = 0;
+        vec->data.U8[3] = 1;
+        vec->data.U8[4] = 0;
+        long numPix = psVectorCountPixelMask(vec, 1);
+        ok(numPix != -1, "psVectorCountPixelMask() did return -1 for correct psVector input");
+        ok(numPix == 2, "returned pixel count %d", numPix);
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Use correct inputs; psVector length of 0
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(0, PS_TYPE_U8);
+        long numPix = psVectorCountPixelMask(vec, 1);
+        ok(numPix != -1, "psVectorCountPixelMask() did return -1 for correct psVector input (length 0)");
+        ok(numPix == 0, "returned pixel count %d", numPix);
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Use correct inputs; psVector length of 1
+    {
+        psMemId id = psMemGetId();
+        psVector *vec = psVectorAlloc(1, PS_TYPE_U8);
+        vec->data.U8[0] = 0;
+        long numPix = psVectorCountPixelMask(vec, 1);
+        ok(numPix != -1, "psVectorCountPixelMask() did return -1 for correct psVector input (length 1)");
+        ok(numPix == 0, "returned pixel count %d", numPix);
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Use correct inputs; large psVector 
+    {
+        psMemId id = psMemGetId();
+        #define N 10
+        psVector *vec = psVectorAlloc(N, PS_TYPE_U8);
+        for (int i = 0 ; i < N ; i++) {
+            if (0 == i%2) {
+                vec->data.U8[i] = 0;
+            } else {
+                vec->data.U8[i] = 1;
+            }
+        }
+        long numPix = psVectorCountPixelMask(vec, 1);
+        ok(numPix != -1, "psVectorCountPixelMask() did return -1 for correct psVector input (length: %d)", N);
+        ok(numPix == N/2, "returned pixel count %d", numPix);
+        psFree(vec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorLength() tests
+    {
+        psMemId id = psMemGetId();
+        psVector *vector = psVectorAlloc(5, PS_TYPE_F32);
+        ok(psVectorLength(vector) == 5,
+           "returned the correct length of vector (was %d, should be 5)", psVectorLength(vector));
+        psFree(vector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vector = psVectorAlloc(5, PS_TYPE_F32);
+        ok(psVectorLength(vector) == 5,
+           "returned the correct length of vector");
+        psFree(vector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *vector = psVectorAlloc(6, PS_TYPE_F32);
+        ok(psVectorLength(vector) == 6,
+           "returned the correct length of vector");
+        psFree(vector);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        ok(psVectorLength(NULL) == -1, "returned -1 for a NULL input vector");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psArray *array = psArrayAlloc(5);
+        ok(psVectorLength((psVector*)array) == -1,
+           "returned -1 for an invalid input vector");
+        psFree(array);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psVectorCopy() tests
+    {
+        psMemId id = psMemGetId();
+        ok(psVectorCopy(NULL, NULL, PS_TYPE_F32) == NULL,
+           "returned a NULL vector for NULL input");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(5, PS_TYPE_F32);
+        for (int i = 0; i < 5; i++) {
+            in->data.F32[i] = i;
+        }
+        //Try copy of different type
+        psVector *copy = psVectorCopy(NULL, in, PS_TYPE_F64);
+        ok(copy != NULL, "returned a non NULL vector for correct input");
+        skip_start(copy == NULL, 1, "Skipping 1 test because psVectorCopy() failed");
+        ok(copy->data.F64[2] == 2.0,
+           "copy->data.f64[2] = %lf, in->data.f32[2] = %f",
+           copy->data.F64[2], in->data.F32[2]);
+        skip_end();
+        psFree(in);
+        psFree(copy);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // XXX psVectorRecycle needs it's own tests
+    // copy = psVectorRecycle(copy, in->n + 2, PS_TYPE_F64);
+    {
+        psMemId id = psMemGetId();
+        // Try copy of same type and non-NULL outVector of different type and
+        // size.
+        psVector *in = psVectorAlloc(5, PS_TYPE_F32);
+        in->n = 5;
+        for (int i = 0; i < 5; i++)
+        {
+            in->data.F32[i] = i;
+        }
+        psVector *copy = psVectorAlloc(5, PS_TYPE_F64);
+        copy = psVectorCopy(copy, in, in->type.type);
+
+        ok(copy != NULL, "returned a NULL vector for correct input");
+        skip_start(copy == NULL, 2,
+                   "Skipping 1 test because psVectorCopy() failed");
+        ok(copy->type.type == PS_TYPE_F32, "copy has the correct data type");
+        ok(copy->data.F32[2] == 2.0, "copy has the correct data value");
+        skip_end();
+
+        psFree(in);
+        psFree(copy);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVectorSelect.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVectorSelect.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVectorSelect.c	(revision 22322)
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define SIZE 1001                       // Size of vector
+#define NUM_NONZERO 50                  // Number of non-zero values
+
+// Test that results of psVectorSelect match what you'd get from psVectorSort
+// 2 tests
+static void testSelect(int size,        // Size of vector
+                       int numNonzero   // Number of non-zero values
+                       )
+{
+    psMemId id = psMemGetId();
+
+    psVector *vector = psVectorAlloc(size, PS_TYPE_F32);
+    psVectorInit(vector, 0.0);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 12345);
+    for (long i = 0; i < numNonzero; i++) {
+        long index = psRandomUniform(rng) * size;
+        vector->data.F32[index] = psRandomUniform(rng);
+    }
+    psFree(rng);
+
+    psVector *sorted = psVectorSort(NULL, vector); // Sorted vector
+    psVector *select = NULL;        // Vector for selecting
+    bool match = true;              // Everything matches
+    for (long i = 0; i < size && match; i++) {
+        select = psVectorSelect(select, vector, i);
+        if (select->data.F32[i] != sorted->data.F32[i]) {
+            match = false;
+            diag("Rank %ld doesn't match: %f vs %f", i, select->data.F32[i], sorted->data.F32[i]);
+        }
+    }
+    psFree(sorted);
+    psFree(select);
+    psFree(vector);
+
+    ok(match, "All selections match, size=%d, number non-zero=%d.", size, numNonzero);
+    ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+}
+
+// Test that results of psVectorSelectInPlace match what you'd get from psVectorSort
+// 2 tests
+static void testSelectInPlace(int size,        // Size of vector
+                       int numNonzero   // Number of non-zero values
+                       )
+{
+    psMemId id = psMemGetId();
+
+    psVector *vector = psVectorAlloc(size, PS_TYPE_F32);
+    psVectorInit(vector, 0.0);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 12345);
+    for (long i = 0; i < numNonzero; i++) {
+        long index = psRandomUniform(rng) * size;
+        vector->data.F32[index] = psRandomUniform(rng);
+    }
+    psFree(rng);
+
+    psVector *sorted = psVectorSort(NULL, vector); // Sorted vector
+    bool match = true;              // Everything matches
+    for (long i = 0; i < size && match; i++) {
+        psVectorSelectInPlace(vector, i);
+        if (vector->data.F32[i] != sorted->data.F32[i]) {
+            match = false;
+            diag("Rank %ld doesn't match: %f vs %f", i, vector->data.F32[i], sorted->data.F32[i]);
+        }
+    }
+    psFree(sorted);
+    psFree(vector);
+
+    ok(match, "All selections in-place match, size=%d, number non-zero=%d.", size, numNonzero);
+    ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+}
+
+
+int main(int argc, char *argv[])
+{
+    plan_tests(6 * 2 + 6 * 2);
+
+    testSelect(100, 0);
+    testSelect(100, 1);
+    testSelect(100, 10);
+    testSelect(100, 100);
+    testSelect(137, 13);
+    testSelect(137, 137);
+
+    testSelectInPlace(100, 0);
+    testSelectInPlace(100, 1);
+    testSelectInPlace(100, 10);
+    testSelectInPlace(100, 100);
+    testSelectInPlace(137, 13);
+    testSelectInPlace(137, 137);
+
+    return PS_EXIT_SUCCESS;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVectorSort.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVectorSort.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVectorSort.c	(revision 22322)
@@ -0,0 +1,263 @@
+/** @file  tst_psVectorSort_01.c
+ *
+ *  @brief Test driver for psVectorSort functions
+ *
+ *  This file is amalgamated from: tst_psVectorSort_01.c, tst_psVectorSort_03.c
+ *  tst_psVectorSort_04.c
+ *
+ *  Attempt to sort NULL input vector
+ *  Attempt to sort vector with unallowed type (i.e. PS_TYPE_BOOL)
+ *  Sort input vector with zero items
+ *  Sort vector with one element
+ *  Verify the sort for all supported types.
+ *  Sort input vector into itself
+ *  Output vector is smaller than input vector
+ *  Output vector is different type from input vector
+ *
+ *  @author  Ross Harman, GLG, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-05-08 06:21:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define VERBOSE 0
+
+#define tstVectorSortByType(datatype, boolOutNull, value) \
+{ \
+    psMemId id = psMemGetId(); \
+    psVector *out = NULL; \
+    psVector *in = psVectorAlloc(7, PS_TYPE_##datatype); \
+    if (boolOutNull) { \
+        out = NULL; \
+    } else { \
+        out = psVectorAlloc(7,PS_TYPE_##datatype); \
+    } \
+    in->data.datatype[0] = 7+value; \
+    in->data.datatype[1] = 9+value; \
+    in->data.datatype[2] = 3+value; \
+    in->data.datatype[3] = 1+value; \
+    in->data.datatype[4] = 5+value; \
+    in->data.datatype[5] = 5+value; \
+    in->data.datatype[6] = 0+value; \
+    psVector *tempVec = out; \
+    out = psVectorSort(out, in); \
+    if (!boolOutNull) { \
+        ok(tempVec == out, "Return value equal to orignal output argument passed to function"); \
+    } \
+    ok(out->type.type == PS_TYPE_##datatype, "psVectorSort() returned vector with correct type"); \
+    ok(out->n == 7, "psVectorSort() returned correct size vector"); \
+    skip_start(out == NULL, 7,"Skipping tests because psVectorSort() returned NULL."); \
+    ok(out->data.datatype[0] == in->data.datatype[6], "sort out[0] type %s",#datatype); \
+    ok(out->data.datatype[1] == in->data.datatype[3], "sort out[1] type %s",#datatype); \
+    ok(out->data.datatype[2] == in->data.datatype[2], "sort out[2] type %s",#datatype); \
+    ok(out->data.datatype[3] == in->data.datatype[4], "sort out[3] type %s",#datatype); \
+    ok(out->data.datatype[4] == in->data.datatype[5], "sort out[4] type %s",#datatype); \
+    ok(out->data.datatype[5] == in->data.datatype[0], "sort out[5] type %s",#datatype); \
+    ok(out->data.datatype[6] == in->data.datatype[1], "sort out[6] type %s",#datatype); \
+    skip_end(); \
+    psFree(in); \
+    psFree(out); \
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks"); \
+}
+
+
+psS32 main(psS32 argc,
+           char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(246);
+
+
+    // Attempt to sort NULL input vector
+    {
+        psMemId id = psMemGetId();
+        psVector *out = psVectorSort(NULL, NULL);
+        ok(out == NULL, "psVectorSort returned a NULL for NULL input vector" );
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to sort vector with unallowed type
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(7, PS_TYPE_BOOL);
+        psVector *out = psVectorSort(NULL, in);
+        ok(out == NULL, "Did return NULL on error");
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Sort vector with zero elements
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(0, PS_TYPE_F32);
+        psVector *out = psVectorSort(NULL, in);
+        ok(out != NULL, "psVectorSort returned a non-NULL vector" );
+        skip_start(out == NULL, 1, "psVectorSort returned a NULL vector" );
+        ok(out->n == 0, "psVectorSort correctly return a 0-length vector");
+        skip_end();
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Sort vector with one element
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(1, PS_TYPE_F32);
+        psVector *out = psVectorSort(NULL, in);
+        ok(out != NULL, "psVectorSort returned a non-NULL vector" );
+        skip_start(out == NULL, 1, "psVectorSort returned a NULL vector" );
+        ok(out->n == 1, "psVectorSort correctly return a 1-length vector");
+        skip_end();
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify the sort for all supported types.
+    {
+        // At first we test with a NULL out vector.
+        tstVectorSortByType(S8, true, 0);
+        tstVectorSortByType(S16, true, 1);
+        tstVectorSortByType(S32, true, 2);
+        tstVectorSortByType(S64, true, 3);
+        tstVectorSortByType(U8, true, 4);
+        tstVectorSortByType(U16, true, 5);
+        tstVectorSortByType(U32, true, 6);
+        tstVectorSortByType(U64, true, 7);
+        tstVectorSortByType(F32, true, 8);
+        tstVectorSortByType(F64, true, 9);
+        // We test with a non-NULL out vector.
+        tstVectorSortByType(S8, false, 0);
+        tstVectorSortByType(S16, false, 1);
+        tstVectorSortByType(S32, false, 2);
+        tstVectorSortByType(S64, false, 3);
+        tstVectorSortByType(U8, false, 4);
+        tstVectorSortByType(U16, false, 5);
+        tstVectorSortByType(U32, false, 6);
+        tstVectorSortByType(U64, false, 7);
+        tstVectorSortByType(F32, false, 8);
+        tstVectorSortByType(F64, false, 9);
+    }
+
+
+    // Sort input vector into itself
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(7, PS_TYPE_F32);
+        in->data.F32[0] = 0.7f;
+        in->data.F32[1] = 0.9f;
+        in->data.F32[2] = 0.3f;
+        in->data.F32[3] = 0.1f;
+        in->data.F32[4] = 0.5f;
+        in->data.F32[5] = 0.5f;
+        in->data.F32[6] = -2.0f;
+        in = psVectorSort(in, in);
+        skip_start(in == NULL,  7, "Skipping tests because psVectorSort() returned NULL.");
+        ok(in->data.F32[0] == -2.0f,"self sort in[0]");
+        ok(in->data.F32[1] == 0.1f,"self sort in[1]");
+        ok(in->data.F32[2] == 0.3f, "self sort in[2]");
+        ok(in->data.F32[3] == 0.5f, "self sort in[3]");
+        ok(in->data.F32[4] == 0.5f, "self sort in[4]");
+        ok(in->data.F32[5] == 0.7f, "self sort in[5]");
+        ok(in->data.F32[6] == 0.9f, "self sort in[6]");
+        skip_end();
+        psFree(in);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Output vector is smaller than input vector
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(7, PS_TYPE_F32);
+        psVector *out = psVectorAlloc(3, PS_TYPE_F32);
+        in->data.F32[0] = 0.7f;
+        in->data.F32[1] = 0.9f;
+        in->data.F32[2] = 0.3f;
+        in->data.F32[3] = 0.1f;
+        in->data.F32[4] = 0.5f;
+        in->data.F32[5] = 0.5f;
+        in->data.F32[6] = -2.0f;
+        out = psVectorSort(out, in);
+        skip_start(out == NULL,  7, "Skipping tests because psVectorSort() returned NULL.");
+        ok(out->data.F32[0] == -2.0f,"Smaller out vector: in[0]");
+        ok(out->data.F32[1] == 0.1f,"Smaller out vector: in[1]");
+        ok(out->data.F32[2] == 0.3f, "Smaller out vector: in[2]");
+        ok(out->data.F32[3] == 0.5f, "Smaller out vector: in[3]");
+        ok(out->data.F32[4] == 0.5f, "Smaller out vector: in[4]");
+        ok(out->data.F32[5] == 0.7f, "Smaller out vector: in[5]");
+        ok(out->data.F32[6] == 0.9f, "Smaller out vector: in[6]");
+        skip_end();
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Output vector is different type from input vector
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(7, PS_TYPE_F32);
+        psVector *out = psVectorAlloc(7, PS_TYPE_F64);
+        in->data.F32[0] = 0.7f;
+        in->data.F32[1] = 0.9f;
+        in->data.F32[2] = 0.3f;
+        in->data.F32[3] = 0.1f;
+        in->data.F32[4] = 0.5f;
+        in->data.F32[5] = 0.5f;
+        in->data.F32[6] = -2.0f;
+        out = psVectorSort(out, in);
+        skip_start(out == NULL,  7, "Skipping tests because psVectorSort() returned NULL.");
+        ok(out->data.F32[0] == -2.0f,"Smaller out vector: in[0]");
+        ok(out->data.F32[1] == 0.1f,"Smaller out vector: in[1]");
+        ok(out->data.F32[2] == 0.3f, "Smaller out vector: in[2]");
+        ok(out->data.F32[3] == 0.5f, "Smaller out vector: in[3]");
+        ok(out->data.F32[4] == 0.5f, "Smaller out vector: in[4]");
+        ok(out->data.F32[5] == 0.7f, "Smaller out vector: in[5]");
+        ok(out->data.F32[6] == 0.9f, "Smaller out vector: in[6]");
+        skip_end();
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Sort large input vector
+    {
+        #define N 1000000
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(N, PS_TYPE_S32);
+        for (int i = 0 ; i < N ; i++) {
+            in->data.S32[N-i-1] = i;
+        }
+        psVector *out = psVectorSort(NULL, in);
+        skip_start(out == NULL,  7, "Skipping tests because psVectorSort() returned NULL.");
+        bool errorFlag = false;
+        for (int i = 0 ; i < N ; i++) {
+            if (out->data.S32[i] != i) {
+                if (VERBOSE) {
+                    diag("Test error: out[%d] is %d, should be %d\n", i, out->data.S32[i], i);
+                }
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psVectorSort() correctly sorted a large input vector");
+        skip_end();
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVectorSortIndex.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVectorSortIndex.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tap_psVectorSortIndex.c	(revision 22322)
@@ -0,0 +1,209 @@
+/** @file  tst_psVectorSort_02.c
+ *
+ *  @brief Test driver for psVectorSort functions
+ *
+ *  This test driver contains the following tests for psVectorSort test point 2:
+ *    Attempt to sort with null input vector
+ *    Attempt to sort input vector with unallowed type
+ *    Sort input vector with zero elements
+ *    Sort input vector with one element
+ *    Sort vectors by index for all types: first with a NULL output vector, then not.
+ *    Sort with output vector which needs to be resized
+ *    Sort with output vector with different type
+ *
+ *  @author  Ross Harman, GLG, MHPCC
+ *
+ *  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-11-29 02:51:08 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+#define VERBOSE 0
+
+#define tstVectorSortIndexByType(datatype, boolOutNull, value) \
+{ \
+    psMemId id = psMemGetId(); \
+    psVector *out = NULL; \
+    psVector *in = psVectorAlloc(5, PS_TYPE_##datatype); \
+    in->data.datatype[0] = 7+value; \
+    in->data.datatype[1] = 9+value; \
+    in->data.datatype[2] = 5+value; \
+    in->data.datatype[3] = 1+value; \
+    in->data.datatype[4] = 5+value; \
+    if (boolOutNull) { \
+        out = NULL; \
+    } else { \
+        out = psVectorAlloc(5, PS_TYPE_##datatype); \
+    } \
+    psVector *tempVec = out; \
+    out = psVectorSortIndex(out, in); \
+    if (!boolOutNull) { \
+        ok(tempVec == out, "Return value equal to orignal output argument passed to function"); \
+    } \
+    skip_start(out == NULL, 6, "Skipping tests because psVectorSortIndex() returned NULL"); \
+    ok(out->type.type == PS_TYPE_S32, "Output vector is of type PS_TYPE_S32"); \
+    ok(out->n == 5, "psVectorSortIndex() returned correct size vector"); \
+    ok(out->data.U32[0] == 3, "index sort out[0] = %ld",out->data.U32[0]); \
+    ok(out->data.U32[1] == 2, "index sort out[1] = %ld",out->data.U32[1]); \
+    ok(out->data.U32[2] == 4, "index sort out[2] = %ld",out->data.U32[2]); \
+    ok(out->data.U32[3] == 0, "index sort out[3] = %ld",out->data.U32[3]); \
+    ok(out->data.U32[4] == 1, "index sort out[4] = %ld",out->data.U32[4]); \
+    skip_end(); \
+    psFree(in); \
+    psFree(out); \
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks"); \
+}\
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(196);
+
+
+    // Attempt to sort with null input vector
+    {
+        psMemId id = psMemGetId();
+        psVector *out = psVectorSortIndex(NULL, NULL);
+        ok(out == NULL, "psVectorSortIndex() return NULL with NULL input specified");
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Attempt to sort input vector with unallowed type
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(5, PS_TYPE_BOOL);
+        psVector *out = psVectorSortIndex(NULL, in);
+        ok(out == NULL, "psVectorSortIndex() returned NULL with unallowed input vector type");
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Sort input vector with zero elements
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(0, PS_TYPE_U8);
+        psVector *out = psVectorSortIndex(NULL, in);
+        ok(out != NULL, "psVectorSortIndex() returned non-NULL with size 0 input vector");
+        ok(out->n == 0, "psVectorSortIndex() returned correct size vector with size 0 input vector");
+        psFree(out);
+        psFree(in);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Sort input vector with one element
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(1, PS_TYPE_U8);
+        psVector *out = psVectorSortIndex(NULL, in);
+        ok(out != NULL, "psVectorSortIndex() returned non-NULL with size 0 input vector");
+        ok(out->n == 1, "psVectorSortIndex() returned correct size vector with size 0 input vector");
+        psFree(out);
+        psFree(in);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Sort vectors by index for all types: first with a NULL output vector, then not.
+    {
+        tstVectorSortIndexByType(S8, true, 0)
+        tstVectorSortIndexByType(U8, true, 1)
+        tstVectorSortIndexByType(S16, true, 2)
+        tstVectorSortIndexByType(U16, true, 3)
+        tstVectorSortIndexByType(S32, true, 4)
+        tstVectorSortIndexByType(U32, true, 5)
+        tstVectorSortIndexByType(S64, true, 6)
+        tstVectorSortIndexByType(U64, true, 7)
+        tstVectorSortIndexByType(F32, true, 8)
+        tstVectorSortIndexByType(F64, true, 9)
+        tstVectorSortIndexByType(S8, false, 0)
+        tstVectorSortIndexByType(U8, false, 1)
+        tstVectorSortIndexByType(S16, false, 2)
+        tstVectorSortIndexByType(U16, false, 3)
+        tstVectorSortIndexByType(S32, false, 4)
+        tstVectorSortIndexByType(U32, false, 5)
+        tstVectorSortIndexByType(S64, false, 6)
+        tstVectorSortIndexByType(U64, false, 7)
+        tstVectorSortIndexByType(F32, false, 8)
+        tstVectorSortIndexByType(F64, false, 9)
+    }
+
+
+    // Sort with output vector which needs to be resized
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(5, PS_TYPE_U8);
+        for(psS32 m=0; m<5; m++) {
+            in->data.U8[m]= 20-m;
+        }
+        psVector *out = psVectorAlloc(3,PS_TYPE_U32);
+        out = psVectorSortIndex(out,in);
+        ok(out->n == 5, "Did properly resize output vector...out->n=%d, in->n=%d", out->n, in->n);
+        ok(out->data.U32[0] == 4, "Did properly sort index out[0] = %d",out->data.U32[0]);
+        ok(out->data.U32[1] == 3, "Did properly sort index out[1] = %d",out->data.U32[1]);
+        ok(out->data.U32[2] == 2, "Did properly sort index out[2] = %d",out->data.U32[2]);
+        ok(out->data.U32[3] == 1, "Did properly sort index out[3] = %d",out->data.U32[3]);
+        ok(out->data.U32[4] == 0, "Did properly sort index out[4] = %d",out->data.U32[4]);
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Sort with output vector with different type
+    {
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(5, PS_TYPE_U8);
+        for(psS32 m=0; m<5; m++) {
+            in->data.U8[m]= 20-m;
+        }
+        psVector *out = psVectorAlloc(3,PS_TYPE_F64);
+        out = psVectorSortIndex(out,in);
+        ok(out->n == 5, "Did properly resize output vector...out->n=%d, in->n=%d", out->n, in->n);
+        ok(out->data.U32[0] == 4, "Did properly sort index out[0] = %d",out->data.U32[0]);
+        ok(out->data.U32[1] == 3, "Did properly sort index out[1] = %d",out->data.U32[1]);
+        ok(out->data.U32[2] == 2, "Did properly sort index out[2] = %d",out->data.U32[2]);
+        ok(out->data.U32[3] == 1, "Did properly sort index out[3] = %d",out->data.U32[3]);
+        ok(out->data.U32[4] == 0, "Did properly sort index out[4] = %d",out->data.U32[4]);
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Sort large input vector
+    {
+        #define N 1000000
+        psMemId id = psMemGetId();
+        psVector *in = psVectorAlloc(N, PS_TYPE_S32);
+        for (int i = 0 ; i < N ; i++) {
+            in->data.S32[N-i-1] = i;
+        }
+        psVector *out = psVectorSortIndex(NULL, in);
+        skip_start(out == NULL,  7, "Skipping tests because psVectorSort() returned NULL.");
+        bool errorFlag = false;
+        for (int i = 0 ; i < N ; i++) {
+            if (out->data.U32[i] != N-i-1) {
+                if (VERBOSE) {
+                    diag("Test error: out[%d] is %d, should be %d\n", i, out->data.U32[i], i);
+                }
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "psVectorSortIndex() correctly sorted a large input vector");
+        skip_end();
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tst_psImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tst_psImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tst_psImage.c	(revision 22322)
@@ -0,0 +1,583 @@
+/** @file  tst_psImage.c
+ *
+ *  @brief Contains the tests for psImage.[ch]
+ *
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-06-15 02:29:12 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "psType.h"
+
+static psS32 testImageAlloc(void);
+static psS32 testRegion(void);
+static psS32 testRegion2(void);
+static psS32 testRegion3(void);
+static psS32 testImageInit(void);
+static psS32 testImageSet(void);
+static psS32 testImageGet(void);
+
+testDescription tests[] = {
+                              {testImageAlloc,546,"psImageAlloc",0,false},
+                              {testImageAlloc,548,"psImageFree",0,true},
+                              {testRegion,790,"psRegionSet",0,false},
+                              {testRegion,791,"psRegionFromString",0,true},
+                              {testRegion2,792,"psRegionForImage",0,false},
+                              {testRegion3,793,"psRegionForSquare",0,false},
+                              {testImageInit,794,"psImageInit",0,false},
+                              {testImageSet,795,"psImageSet",0,false},
+                              {testImageGet,666,"psImageGet",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    return ! runTestSuite(stderr,"psImage",tests,argc,argv);
+}
+
+psS32 testImageAlloc(void)
+{
+    psImage* image = NULL;
+    psU32 sizes = 6;
+    psU32 numCols[] = {
+                          0,1,1,100,100,150
+                      };
+    psU32 numRows[] = {
+                          0,1,100,1,150,100
+                      };
+    psU32 types = 12;
+    psElemType type[] = { PS_TYPE_S8, PS_TYPE_S16, PS_TYPE_S32, PS_TYPE_S64,
+                          PS_TYPE_U8, PS_TYPE_U16, PS_TYPE_U32, PS_TYPE_U64,
+                          PS_TYPE_F32, PS_TYPE_F64, PS_TYPE_C32, PS_TYPE_C64};
+
+    psLogMsg(__func__,PS_LOG_INFO,"#546 - psImageAlloc shall allocate memory for a psImage structure");
+
+    for (psU32 t=0;t<types;t++) {
+        psLogMsg(__func__,PS_LOG_INFO,"Testing psImage with type %xh",type[t]);
+
+        for (psU32 i=0;i<sizes;i++) {
+
+            if (numRows[i] == 0 || numCols[i] == 0) {
+                psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+            }
+
+            image = psImageAlloc(numCols[i],numRows[i],type[t]);
+
+            if (image == NULL) {
+                if (numRows[i] == 0 || numCols[i] == 0) {
+                    continue;
+                }
+                psError(PS_ERR_UNKNOWN, true,"psImageAlloc returned NULL for type %x, size %dx%d.",
+                        type[t], numCols[i], numRows[i]);
+                psFree(image);
+                return 1;
+            }
+
+            if (image->type.dimen != PS_DIMEN_IMAGE || image->type.type != type[t]) {
+                psError(PS_ERR_UNKNOWN, true,"psImageAlloc allocated wrong dimen/type (%d/%d), should be "
+                        "PS_IMAGE_DIMEN/%d", image->type.dimen, image->type.type, type[t]);
+                psFree(image);
+                return 2;
+            }
+
+            if (image->numCols != numCols[i] || image->numRows != numRows[i]) {
+                psError(PS_ERR_UNKNOWN, true,"psImageAlloc allocated wrong size %dx%d, should be %dx%d (type = %d)",
+                        image->numCols, image->numRows, numCols[i], numRows[i],type[t]);
+                psFree(image);
+                return 3;
+            }
+
+            if (image->col0 != 0 || image->row0 != 0) {
+                psError(PS_ERR_UNKNOWN, true,"psImageAlloc returned row0/col0 of %d/%d.  Should be 0/0.",
+                        image->row0, image->row0);
+                psFree(image);
+                return 4;
+            }
+
+            if (image->parent != NULL) {
+                psError(PS_ERR_UNKNOWN, true,"psImageAlloc returned non-NULL parent");
+                psFree(image);
+                return 5;
+            }
+
+            if (image->children != NULL) {
+                psError(PS_ERR_UNKNOWN, true,"psImageAlloc returned non-NULL children array");
+                psFree(image);
+                return 7;
+            }
+
+            switch (type[t]) {
+            case PS_TYPE_U16: {
+                    psU32 rows = numRows[i];
+                    psU32 cols = numCols[i];
+
+                    for (psS32 r=0;r<rows;r++) {
+                        for (psS32 c=0;c<cols;c++) {
+                            image->data.U16[r][c] = 2*c+r;
+                        }
+                    }
+                    for (psS32 r=0;r<rows;r++) {
+                        for (psS32 c=0;c<cols;c++) {
+                            if (image->data.U16[r][c] != 2*c+r) {
+                                psError(PS_ERR_UNKNOWN, true,"Could not set all pixels in uint16 image at (%d,%d)",c,r);
+                                psFree(image);
+                                return 8;
+                            }
+                        }
+                    }
+                }
+                break;
+            case PS_TYPE_F32: {
+                    psU32 rows = numRows[i];
+                    psU32 cols = numCols[i];
+
+                    for (psS32 r=0;r<rows;r++) {
+                        for (psS32 c=0;c<cols;c++) {
+                            image->data.F32[r][c] = 2.0f*c+r;
+                        }
+                    }
+                    for (psS32 r=0;r<rows;r++) {
+                        for (psS32 c=0;c<cols;c++) {
+                            if (fabsf(image->data.F32[r][c] - (2.0f*c+r)) > FLT_EPSILON) {
+                                psError(PS_ERR_UNKNOWN, true,"Could not set all pixels in float image at (%d,%d)",c,r);
+                                psFree(image);
+                                return 8;
+                            }
+                        }
+                    }
+                }
+                break;
+            case PS_TYPE_F64: {
+                    psU32 rows = numRows[i];
+                    psU32 cols = numCols[i];
+
+                    for (psS32 r=0;r<rows;r++) {
+                        for (psS32 c=0;c<cols;c++) {
+                            image->data.F64[r][c] = 2.0f*c+r;
+                        }
+                    }
+                    for (psS32 r=0;r<rows;r++) {
+                        for (psS32 c=0;c<cols;c++) {
+                            if (fabs(image->data.F64[r][c] - (2.0f*c+r)) > DBL_EPSILON) {
+                                psError(PS_ERR_UNKNOWN, true,"Could not set all pixels in double image at (%d,%d)",c,r);
+                                psFree(image);
+                                return 8;
+                            }
+                        }
+                    }
+                }
+                break;
+            case PS_TYPE_C32: {
+                    psU32 rows = numRows[i];
+                    psU32 cols = numCols[i];
+
+                    for (psS32 r=0;r<rows;r++) {
+                        for (psS32 c=0;c<cols;c++) {
+                            image->data.C32[r][c] = r + I * c;
+                        }
+                    }
+                    for (psS32 r=0;r<rows;r++) {
+                        for (psS32 c=0;c<cols;c++) {
+                            if (fabsf(crealf(image->data.C32[r][c]) - r) > FLT_EPSILON ||
+                                    fabsf(cimagf(image->data.C32[r][c]) - c) > FLT_EPSILON ) {
+                                psError(PS_ERR_UNKNOWN, true,"Could not set all pixels in complex image at (%d,%d)",c,r);
+                                psFree(image);
+                                return 8;
+                            }
+                        }
+                    }
+                }
+                break;
+            default: {
+                    // ignore type and just use as byte bucket.
+                    psU32 rows = numRows[i];
+                    psU32 cols = numCols[i]*PSELEMTYPE_SIZEOF(type[t]);
+
+                    for (psS32 r=0;r<rows;r++) {
+                        for (psS32 c=0;c<cols;c++) {
+                            image->data.U8[r][c] = (uint8_t)(r + c);
+                        }
+                    }
+                    for (psS32 r=0;r<rows;r++) {
+                        for (psS32 c=0;c<cols;c++) {
+                            if (image->data.U8[r][c] != (uint8_t)(r + c)) {
+                                psError(PS_ERR_UNKNOWN, true,"Could not set all pixels in image (type=%d) at (%d,%d)",
+                                        type[t],c,r);
+                                psFree(image);
+                                return 8;
+                            }
+                        }
+                    }
+                }
+            }
+            psFree(image);
+        }
+    }
+
+    // #548: Verify no memory leaks or corruption are detected after a valid psImage structure with multiple
+    // children is freed.
+    image = psImageAlloc(100,100,PS_TYPE_F32);
+    psImageSubset(image,(psRegion) {
+                      50,0,70,20
+                  }
+                 );
+    psImageSubset(image,(psRegion) {
+                      70,20,90,40
+                  }
+                 );
+
+    psFree(image);
+
+    return 0;
+}
+
+static psS32 testRegion(void)
+{
+    int testNum = 0;
+
+
+    // Testpoint #790
+
+    psRegion region = psRegionSet(1,2,3,4);
+
+    testNum++;
+    if (region.x0 != 1 || region.x1 != 2 || region.y0 != 3 || region.y1 != 4) {
+        psError(PS_ERR_UNKNOWN, false,
+                "The region attributes are not set properly (%s)",
+                psRegionToString(region));
+        return testNum;
+    }
+
+    // Testpoint #791
+
+    region = psRegionFromString("[1:2,3:4]");
+
+    testNum++;
+    if (region.x0 != 0 || region.x1 != 2 || region.y0 != 2 || region.y1 != 4) {
+        psError(PS_ERR_UNKNOWN, false,
+                "The region attributes are not set properly (%s)",
+                psRegionToString(region));
+        return testNum;
+    }
+
+    region = psRegionFromString("[1:2,3:]");
+
+    testNum++;
+    if (! isnan(region.x0)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "psRegionFromString returned a non-NULL pointer given a malformed string.");
+        return testNum;
+    }
+
+    return 0;
+}
+
+//psRegionForImage//
+static psS32 testRegion2(void)
+{
+    psImage *in;
+    psRegion inReg;
+    psRegion out;
+    in = psImageAlloc(1, 1, PS_TYPE_S32);
+    inReg = psRegionSet(1, 2, 1, 2);
+    out = psRegionForImage(in,inReg);
+    psRegion inReg2;
+    psRegion out2;
+    inReg2 = psRegionSet(-1, 0, -2, -1);
+    out2 = psRegionForImage(in, inReg2);
+
+    if( out.x0 != 1 || out.x1 != 1 || out.y0 != 1 || out.y1 != 1 ) {
+        psError(PS_ERR_UNKNOWN, true, "Error:  Region For Image returned incorrect values.\n");
+    }
+    if( out2.x0 != 0 || out2.x1 != 1 || out2.y0 != 0 || out2.y1 != 0 ) {
+        psError(PS_ERR_UNKNOWN, true, "Error:  Region For Image returned incorrect values.\n");
+    }
+
+    psFree(in);
+    return 0;
+}
+
+//psRegionForSquare//
+static psS32 testRegion3(void)
+{
+    float X = 1;
+    float Y = 1;
+    float RAD = 1;
+    psRegion out;
+    out = psRegionForSquare(X, Y, RAD);
+    if (out.x0 != 0 || out.x1 != 3 || out.y0 != 0 || out.y1!= 3)
+        psError(PS_ERR_UNKNOWN, true, "Error:  Region For Square returned incorrect values.\n");
+    return 0;
+}
+
+//Initialize an image with a given value.//
+static psS32 testImageInit(void)
+{
+    psImage *in1 = NULL;
+    psImage *in2 = NULL;
+    psImage *in3 = NULL;
+    psImage *in4 = NULL;
+    int nRow = 1;
+    int nCol = 1;
+    in1 = psImageAlloc(nRow, nCol, PS_TYPE_U8);
+    if ( !psImageInit(in1, -1 ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "ImageInit failed.  U8 Case - 1x1\n");
+    }
+    nRow = 5;
+    in2 = psImageAlloc(nRow, nCol, PS_TYPE_F32);
+    if ( !psImageInit(in2, 3) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "ImageInit failed.  F32 Case - 5x1\n");
+    }
+    nCol = 5;
+    in3 = psImageAlloc(nRow, nCol, PS_TYPE_F64);
+    if ( !psImageInit(in3, 3.141) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "ImageInit failed.  F64 Case - 5x5\n");
+    }
+    in4 = psImageAlloc(nRow, nCol, PS_TYPE_S32);
+    if ( !psImageInit(in4, 3) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "ImageInit failed.  S32 Case - 5x5\n");
+    }
+    psFree(in1);
+    psFree(in2);
+    psFree(in3);
+    psFree(in4);
+    return 0;
+}
+
+static psS32 testImageSet(void)
+{
+    psImage *image = NULL;
+    image = psImageAlloc(5, 4, PS_TYPE_S32);
+
+    //Set the position of the subimage relative to the parent.
+    image->col0 = 10;
+    image->row0 = 10;
+    for (int i = 0; i < 4; i++) {
+        for (int j = 0; j < 5; j++) {
+            image->data.S32[i][j] = i+j;
+        }
+    }
+
+    //Attempt to set a position in a NULL psImage*
+    psImage *none = NULL;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psImageSet(none, 1, 1, 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageSet failed to return false when passed a NULL image.\n");
+        return 1;
+    }
+
+    //Attempt to set a position in a psImage* with negative numCols, numRows
+    none = psImageAlloc(2, 2, PS_TYPE_S32);
+    *(int*)&none->numCols = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psImageSet(none, 1, 1, 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageSet failed to return false when passed an image with negative numCols.\n");
+        return 2;
+    }
+    *(int*)&none->numCols = 2;
+    *(int*)&none->numRows = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psImageSet(none, 1, 1, 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageSet failed to return false when passed an image with negative numRows.\n");
+        return 3;
+    }
+
+    //Attempt to set a position in a psImage* with negative col0, row0
+    *(int*)&none->numRows = 2;
+    none->row0 = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psImageSet(none, 1, 1, 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageSet failed to return false when passed an image with negative col0.\n");
+        return 4;
+    }
+    none->row0 = 0;
+    none->col0 = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psImageSet(none, 1, 1, 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageSet failed to return false when passed an image with negative col0.\n");
+        return 5;
+    }
+    psFree(none);
+
+    //Try to set a position inside of the subimage.
+    if ( !psImageSet(image, 14, 12, 666) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageSet failed to return true when passed valid parameters.\n");
+        return 6;
+    }
+    if (image->data.S32[2][4] != 666) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageSet failed to set the data values correctly. value=%d\n", image->data.S32[2][4]);
+        return 7;
+    }
+    //Try to set a position inside of the subimage from the tail.
+    if ( !psImageSet(image, -1, -1, 666) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageSet failed to return true when passed valid parameters.\n");
+        return 8;
+    } else if  (image->data.S32[3][4] != 666) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageSet failed to set (from the tail) the data values correctly.\n");
+        printf("\n value at 14,14 is %d\n", image->data.S32[3][4]);
+        return 9;
+    }
+
+    //Try to set a position outside of the subimage but inside of the parent image.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psImageSet(image, 0, 0, 666) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSet failed to return false when passed an invalid location.\n");
+        return 10;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psImageSet(image, 9, 10, 666) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSet failed to return false when passed an invalid location.\n");
+        return 11;
+    }
+
+    //Try to set a position outside of the subimage.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psImageSet(image, 15, 14, 666) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSet failed to return false when passed an invalid location.\n");
+        return 12;
+    }
+    //Try to set a position outside of the subimage, indexing from the tail.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psImageSet(image, -6, -1, 666) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageSet failed to return false when passed an invalid location.\n");
+        return 13;
+    }
+
+
+    psFree(image);
+    return 0;
+}
+
+static psS32 testImageGet(void)
+{
+    psImage *image = NULL;
+    image = psImageAlloc(5, 3, PS_TYPE_S32);
+
+    //Set the position of the subimage relative to the parent.
+    image->col0 = 10;
+    image->row0 = 10;
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 5; j++) {
+            image->data.S32[i][j] = i+j;
+        }
+    }
+
+    //Attempt to get a position in a NULL psImage*
+    psImage *none = NULL;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( !isnan( psImageGet(none, 1, 1) ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageGet failed to return false when passed a NULL image.\n");
+        return 1;
+    }
+
+    //Attempt to get a position in a psImage* with negative numCols, numRows
+    none = psImageAlloc(2, 2, PS_TYPE_S32);
+    *(int*)&none->numCols = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( !isnan( psImageGet(none, 1, 1) ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageGet failed to return false when passed an image with negative numCols.\n");
+        return 2;
+    }
+    *(int*)&none->numCols = 2;
+    *(int*)&none->numRows = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( !isnan( psImageGet(none, 1, 1) ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageGet failed to return false when passed an image with negative numRows.\n");
+        return 3;
+    }
+
+    //Attempt to get a position in a psImage* with negative col0, row0
+    *(int*)&none->numRows = 2;
+    none->row0 = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( !isnan( psImageGet(none, 1, 1) ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageGet failed to return false when passed an image with negative col0.\n");
+        return 4;
+    }
+    none->row0 = 0;
+    none->col0 = -1;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( !isnan( psImageGet(none, 1, 1) ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageGet failed to return false when passed an image with negative col0.\n");
+        return 5;
+    }
+    psFree(none);
+
+    //Try to get a position inside of the subimage.
+    if ( psImageGet(image, 14, 12) != 6 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageGet failed to return the correct value.\n");
+        return 6;
+    }
+    //Try to get a position inside of the subimage from the tail.
+    if ( psImageGet(image, -1, -1) != 6 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psImageGet failed to return the correct value.\n");
+        return 8;
+    }
+
+    //Try to get a position outside of the subimage but inside of the parent image.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( !isnan( psImageGet(image, 1, 1) ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageGet failed to return NAN when passed an invalid location.\n");
+        return 10;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( !isnan( psImageGet(image, 9, 10) ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageGet failed to return NAN when passed an invalid location.\n");
+        return 11;
+    }
+
+    //Try to set a position outside of the subimage.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( !isnan( psImageGet(image, 15, 14) ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageGet failed to return NAN when passed an invalid location.\n");
+        return 12;
+    }
+    //Try to set a position outside of the subimage, indexing from the tail.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( !isnan( psImageGet(image, -6, -1) ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psImageGet failed to return NAN when passed an invalid location.\n");
+        return 13;
+    }
+
+
+    psFree(image);
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tst_psScalar.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tst_psScalar.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tst_psScalar.c	(revision 22322)
@@ -0,0 +1,138 @@
+/** @file  tst_psScalar.c
+ *
+ *  @brief Contains the tests for psScalar.[ch]
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.1 $
+ *           $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-07-13 02:47:00 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+static psS32 testScalarAlloc(void);
+static psS32 testScalarCopy(void);
+
+#define tstScalarAllocByType(datatype,value)                                                                    \
+scalar = psScalarAlloc(value,PS_TYPE_##datatype);                                                               \
+if(scalar == NULL) {                                                                                            \
+    psError(PS_ERR_UNKNOWN,true,"psScalarAlloc returned NULL.");                                                \
+    return 1+value;                                                                                             \
+} else {                                                                                                        \
+    if( (scalar->type.type != PS_TYPE_##datatype) && (scalar->data.datatype != value) ) {                       \
+        psError(PS_ERR_UNKNOWN,true,"psScalarAlloc created object with unexpected type and/or value");          \
+        return 2+value;                                                                                         \
+    }                                                                                                           \
+}                                                                                                               \
+psFree(scalar);
+
+#define tstScalarCopyByType(datatype,value)                                                                               \
+scalarOrig = psScalarAlloc(value,PS_TYPE_##datatype);                                                                     \
+scalarCopy = psScalarCopy(scalarOrig);                                                                                    \
+if(scalarCopy == NULL) {                                                                                                  \
+    psError(PS_ERR_UNKNOWN,true,"psScalarCopy returned NULL.");                                                           \
+    return 3+value;                                                                                                       \
+} else {                                                                                                                  \
+    if( (scalarCopy->type.type != scalarOrig->type.type) || (scalarCopy->data.datatype != scalarOrig->data.datatype) ) {  \
+        psError(PS_ERR_UNKNOWN,true,"psScalarCopy did not copy the original scalar by type and/or value");                \
+        return 4+value;                                                                                                   \
+    }                                                                                                                     \
+}                                                                                                                         \
+psFree(scalarCopy);                                                                                                       \
+psFree(scalarOrig);
+
+testDescription tests[] = {
+                              {testScalarAlloc,783,"psScalarAlloc",0,false},
+                              {testScalarCopy,784,"psScalarCopy",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    if ( ! runTestSuite(stderr,"psScalar",tests,argc,argv) ) {
+        psError(PS_ERR_UNKNOWN,true,"One or more tests failed");
+        return 1;
+    }
+    return 0;
+}
+
+psS32 testScalarAlloc(void)
+{
+    psScalar* scalar;
+
+    psLogMsg(__func__,PS_LOG_INFO,"psScalarAlloc shall create scalar data objects");
+
+    // Verify the proper allocation/deallocation of scalar objects of valid types
+    tstScalarAllocByType(S8,10);
+    tstScalarAllocByType(U8,12);
+    tstScalarAllocByType(S16,14);
+    tstScalarAllocByType(U16,16);
+    tstScalarAllocByType(S32,18);
+    tstScalarAllocByType(U32,20);
+    tstScalarAllocByType(S64,22);
+    tstScalarAllocByType(U64,24);
+    tstScalarAllocByType(F32,26);
+    tstScalarAllocByType(F64,28);
+    tstScalarAllocByType(C32,30);
+    tstScalarAllocByType(C64,32);
+
+    // Verify return is null for invalid scalar type
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    scalar = psScalarAlloc(true,PS_TYPE_BOOL);
+    if(scalar != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psScalarAlloc did not return null for invalid type");
+        return 5;
+    }
+
+    return 0;
+}
+
+psS32 testScalarCopy(void)
+{
+    psScalar*  scalarOrig;
+    psScalar*  scalarCopy;
+
+    psLogMsg(__func__,PS_LOG_INFO,"psScalarCopy shall copy scalar objects");
+
+    // Verify the proper copying of scalar objects for all valid types
+    tstScalarCopyByType(S8,100);
+    tstScalarCopyByType(U8,110);
+    tstScalarCopyByType(S16,120);
+    tstScalarCopyByType(U16,130);
+    tstScalarCopyByType(S32,140);
+    tstScalarCopyByType(U32,150);
+    tstScalarCopyByType(S64,160);
+    tstScalarCopyByType(U64,170);
+    tstScalarCopyByType(F32,180);
+    tstScalarCopyByType(F64,190);
+    tstScalarCopyByType(C32,200);
+    tstScalarCopyByType(C64,210);
+
+    // Verify the return is null for invalid scalar type in the original
+    scalarOrig = psScalarAlloc(0,PS_TYPE_S8);
+    scalarOrig->type.type = PS_TYPE_BOOL;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    scalarCopy = psScalarCopy(scalarOrig);
+    if(scalarCopy != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"psScalarCopy did not return NULL for invalid type");
+        return 6;
+    }
+    psFree(scalarOrig);
+
+    // Verify the return is null for null original scalar value
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message");
+    scalarCopy = psScalarCopy(NULL);
+    if(scalarCopy != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psScalarCopy did not return NULL for NULL argument");
+        return 7;
+    }
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tst_psVector.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tst_psVector.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/mathtypes/tst_psVector.c	(revision 22322)
@@ -0,0 +1,648 @@
+/** @file  tst_psVector.c
+ *
+ *  @brief Test driver for psVector integer functions
+ *
+ *  This test driver contains the following tests for psVector test point 1:
+ *     A)  Create S32 vector
+ *     B)  Add data to S32 vector
+ *     C)  Reallocate S32 vector bigger
+ *     D)  Reallocate S32 vector smaller
+ *     E)  Free S32 vector
+ *     F)  Attempt to create a S32 vector with zero size
+ *     G)  Attempt to realloc a null S32 vector
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.12 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-05-02 22:35:52 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+static psS32 testVectorAlloc(void);
+static psS32 testVectorRealloc(void);
+static psS32 testVectorExtend(void);
+static psS32 testVectorInit(void);
+static psS32 testVectorCreate(void);
+static psS32 testVectorCreateA(void);
+static psS32 testVectorCreateB(void);
+static psS32 testVectorGetSet(void);
+static psS32 testVectorCountPixelMask(void);
+static psS32 testVectorLength(void);
+static psS32 testVectorCopy(void);
+
+testDescription tests[] = {
+                              {testVectorAlloc,-1,"psVectorAlloc",0,false},
+                              {testVectorRealloc,-2,"psVectorRealloc",0,false},
+                              {testVectorExtend,-3,"psVectorExtend",0,false},
+                              {testVectorInit,-4,"psVectorInit",0,false},
+                              {testVectorCreate,-5,"psVectorCreate",0,false},
+                              {testVectorCreateA,-6,"psVectorCreateA",0,false},
+                              {testVectorCreateB,-7,"psVectorCreateB",0,false},
+                              {testVectorGetSet,-8,"psVectorGet/Set",0,false},
+                              {testVectorCountPixelMask,-9,"psVectorCountPixelMask",0,false},
+                              {testVectorLength,666,"psVectorLength",0,false},
+                              {testVectorCopy,667,"psVectorCopy",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+    psLogSetFormat("HLNM");
+
+    if ( ! runTestSuite(stderr,"psVector",tests,argc,argv) ) {
+        return 1;
+    }
+    return 0;
+}
+
+psS32 testVectorAlloc(void)
+{
+    psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+
+    if (psVec == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 1;
+    }
+    if (psVec->nalloc != 5) {
+        fprintf(stderr,"Vector size = %ld\n", psVec->nalloc);
+        return 1;
+    }
+    if (psVec->n != 0) {
+        fprintf(stderr,"Vector population = %ld\n", psVec->n);
+        return 2;
+    }
+
+    if (psVec->type.type != PS_TYPE_S32) {
+        fprintf(stderr,"Vector type = %d\n", psVec->type.type);
+        return 3;
+    }
+    if (psVec->type.dimen != PS_DIMEN_VECTOR) {
+        fprintf(stderr,"Vector dimen = %d\n", psVec->type.dimen);
+        return 4;
+    }
+
+    // Test B - Add data to integer vector
+    for(psS32 i = 0; i < 5; i++) {
+        psVec->data.S32[i] = i*10;
+    }
+
+    psVector* vecZero = psVectorAlloc(0, PS_TYPE_S32);
+    if(vecZero == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 5;
+    }
+    if (vecZero->nalloc != 0) {
+        fprintf(stderr,"Vector size = %ld\n", vecZero->nalloc);
+        return 6;
+    }
+    if (vecZero->n != vecZero->nalloc) {
+        fprintf(stderr,"Vector population = %ld\n", vecZero->n);
+        return 7;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO, "Following should be an error.");
+    psVector* vecBogus = psVectorAlloc(10, 0);
+    if (vecBogus != NULL) {
+        fprintf(stderr,"Huh!  bogus type generated a psVector?");
+        return 20;
+    }
+
+    psFree(psVec);
+    psFree(vecZero);
+
+    return 0;
+}
+
+psS32 testVectorRealloc(void)
+{
+    // create new psVector
+    psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+    if (psVec == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 30;
+    }
+    for(psS32 i = 0; i < 5; i++) {
+        psVec->data.S32[i] = i*10;
+        psVec->n++;
+    }
+
+    // Test C - Reallocate S32 vector bigger
+    psVec = psVectorRealloc(psVec,10);
+    if (psVec == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 1;
+    }
+    if (psVec->nalloc != 10) {
+        fprintf(stderr,"Vector size = %ld\n", psVec->nalloc);
+        return 1;
+    }
+    if (psVec->n != 5) {
+        fprintf(stderr,"Vector population = %ld\n", psVec->n);
+        return 2;
+    }
+
+    if (psVec->type.type != PS_TYPE_S32) {
+        fprintf(stderr,"Vector type = %d\n", psVec->type.type);
+        return 3;
+    }
+    if (psVec->type.dimen != PS_DIMEN_VECTOR) {
+        fprintf(stderr,"Vector dimen = %d\n", psVec->type.dimen);
+        return 4;
+    }
+
+    for(psS32 i = 5; i < 10; i++) {
+        psVec->data.S32[i] = i*10;
+        psVec->n++;
+    }
+
+    for(psS32 i = 0; i < 10; i++) {
+        if (psVec->data.S32[i] != i*10) {
+            fprintf(stderr,"Elem %d = %d, expected %d\n", i,
+                    psVec->data.S32[i], i*10);
+            return 5;
+        }
+    }
+
+    // Test D - Reallocate S32 vector smaller
+    psVec = psVectorRealloc(psVec,3);
+    if (psVec == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 9;
+    }
+    if (psVec->nalloc != 3) {
+        fprintf(stderr,"Vector size = %ld\n", psVec->nalloc);
+        return 10;
+    }
+    if (psVec->n != 3) {
+        fprintf(stderr,"Vector population = %ld\n", psVec->n);
+        return 11;
+    }
+
+    for(psS32 i = 0; i < 3; i++) {
+        if (psVec->data.S32[i] != i*10) {
+            fprintf(stderr,"Elem %d = %d, expected %d\n", i,
+                    psVec->data.S32[i], i*10);
+            return 12;
+        }
+    }
+
+    psVec = psVectorRealloc(psVec,0);
+    if (psVec == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 20;
+    }
+    if (psVec->nalloc != 0) {
+        fprintf(stderr,"Vector size = %ld\n", psVec->nalloc);
+        return 21;
+    }
+    if (psVec->n != 0) {
+        fprintf(stderr,"Vector population = %ld\n", psVec->n);
+        return 22;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    psVector* vecBogus = psVectorRealloc(NULL, 6);
+    if (vecBogus != NULL) {
+        fprintf(stderr,"Huh!  bogus type generated a psVector?");
+        return 25;
+    }
+
+    // Test E - Free S32 vector
+    psFree(psVec);
+
+    return 0;
+}
+
+psS32 testVectorExtend(void)
+{
+    // create new psVector
+    psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
+    if (psVec == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 1;
+    }
+
+    psVec->n = 0;
+
+    psVec = psVectorExtend(psVec, 0, 2);
+    if (psVec == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 2;
+    }
+    if (psVec->nalloc != 5) { // no growth should occur
+        fprintf(stderr,"Vector size = %ld\n", psVec->nalloc);
+        return 3;
+    }
+    if (psVec->n != 2) {
+        fprintf(stderr,"Vector population = %ld\n", psVec->n);
+        return 4;
+    }
+
+    psVec = psVectorExtend(psVec, 0, 2);
+    if (psVec == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 10;
+    }
+    if (psVec->nalloc != 15) { // growth should occur
+        fprintf(stderr,"Vector size = %ld\n", psVec->nalloc);
+        return 11;
+    }
+    if (psVec->n != 4) {
+        fprintf(stderr,"Vector population = %ld\n", psVec->n);
+        return 12;
+    }
+
+    psVec = psVectorExtend(psVec, 0, -2);
+    if (psVec == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 20;
+    }
+    if (psVec->nalloc != 15) { // no growth
+        fprintf(stderr,"Vector size = %ld\n", psVec->nalloc);
+        return 21;
+    }
+    if (psVec->n != 2) {
+        fprintf(stderr,"Vector population = %ld\n", psVec->n);
+        return 22;
+    }
+
+    psVec = psVectorExtend(psVec, 0, -20);
+    if (psVec == NULL) {
+        fprintf(stderr,"ERROR: Return is NULL\n");
+        return 30;
+    }
+    if (psVec->nalloc != 15) { // no growth
+        fprintf(stderr,"Vector size = %ld\n", psVec->nalloc);
+        return 31;
+    }
+    if (psVec->n != 0) {
+        fprintf(stderr,"Vector population = %ld\n", psVec->n);
+        return 32;
+    }
+
+    psFree(psVec);
+
+    return 0;
+}
+
+psS32 testVectorInit(void)
+{
+    psVector *in1 = NULL;
+    psVector *in2 = NULL;
+    psVector *in3 = NULL;
+    psVector *in4 = NULL;
+    psVector *in5 = NULL;
+    psVector *in6 = NULL;
+    psVector *in7 = NULL;
+    psVector *in8 = NULL;
+    psVector *in9 = NULL;
+    psVector *in10 = NULL;
+    psVector *in11 = NULL;
+    psVector *in12 = NULL;
+    int nalloc = 1;
+    in1 = psVectorAlloc(nalloc, PS_TYPE_U8);
+    in2 = psVectorAlloc(nalloc, PS_TYPE_U16);
+    in3 = psVectorAlloc(nalloc, PS_TYPE_U32);
+    in4 = psVectorAlloc(nalloc, PS_TYPE_U64);
+    in5 = psVectorAlloc(nalloc, PS_TYPE_S8);
+    in6 = psVectorAlloc(nalloc, PS_TYPE_S16);
+    in7 = psVectorAlloc(nalloc, PS_TYPE_S32);
+    in8 = psVectorAlloc(nalloc, PS_TYPE_S64);
+    in9 = psVectorAlloc(nalloc, PS_TYPE_F32);
+    in10 = psVectorAlloc(nalloc, PS_TYPE_F64);
+    in11 = psVectorAlloc(nalloc, PS_TYPE_C32);
+    in12 = psVectorAlloc(nalloc, PS_TYPE_C64);
+    psC32 vC32;
+    psC64 vC64;
+
+    if ( !psVectorInit(in1, 1 ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  U8 Case \n");
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psVectorInit(in2, PS_MAX_U64) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  U16 Case \n");
+    }
+    if ( !psVectorInit(in3, 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  U32 Case \n");
+    }
+    if ( !psVectorInit(in4, -1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  U64 Case \n");
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psVectorInit(in5, PS_MAX_S16 ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  S8 Case \n");
+    }
+    if ( !psVectorInit(in6, -100 ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  S16 Case \n");
+    }
+    if ( !psVectorInit(in7, 1 ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  S32 Case \n");
+    }
+    if ( !psVectorInit(in8, PS_MAX_U64 ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  S64 Case \n");
+    }
+    if ( !psVectorInit(in9, 1.1 ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  F32 Case \n");
+    }
+    if ( !psVectorInit(in10, 1.4 ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  F64 Case \n");
+    }
+
+    vC32 = 1.23 + 1.19I;
+    vC64 = 2.13 + 2.31I;
+    if ( !psVectorInit(in11, vC32 ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  C32 Case \n");
+    }
+    if ( !psVectorInit(in12, vC64 ) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "VectorInit failed.  C64 Case \n");
+    }
+
+    psFree(in1);
+    psFree(in2);
+    psFree(in3);
+    psFree(in4);
+    psFree(in5);
+    psFree(in6);
+    psFree(in7);
+    psFree(in8);
+    psFree(in9);
+    psFree(in10);
+    psFree(in11);
+    psFree(in12);
+    return 0;
+}
+
+psS32 testVectorCreate(void)
+{
+    psVector *test = NULL;
+    psVector *test2 = NULL;
+    psVector *input = NULL;
+    psVector *input2 = psVectorAlloc(5, PS_TYPE_F32);
+
+    test = psVectorCreate(input, 0.0, 10.0, 1.0, PS_TYPE_S32);
+    test2 = psVectorCreate(input2, 0.0, 5.0, 0.5, PS_TYPE_F32);
+
+    for (int i = 0; i < 10; i++) {
+        if (test->data.S32[i] != i) {
+            fprintf(stderr, "Vector data does not match. i = %d, data=%d\n",
+                    i, test->data.S32[i]);
+            return 1;
+        }
+        if (test2->data.F32[i] != i*0.5) {
+            fprintf(stderr, "Vector data does not match. i = %d, data=%f\n",
+                    i, test->data.F32[i]);
+            return 1;
+        }
+    }
+    psFree(test);
+    psFree(test2);
+    return 0;
+}
+psS32 testVectorCreateA(void)
+{
+    //
+    //  Test PS_TYPE_F64
+    //       PS_TYPE_S64
+    //
+    psVector *test = NULL;
+    psVector *test2 = NULL;
+    psVector *input = NULL;
+    psVector *input2 = psVectorAlloc(5, PS_TYPE_F64);
+
+    test = psVectorCreate(input, 0.0, 10.0, 1.0, PS_TYPE_S64);
+    test2 = psVectorCreate(input2, 0.0, 5.0, 0.5, PS_TYPE_F64);
+
+    for (int i = 0; i < 10; i++) {
+        if (test->data.S64[i] != i) {
+            fprintf(stderr, "Vector data does not match. i = %d, data=%ld\n",
+                    i, (long)test->data.S64[i]);
+            return 1;
+        }
+        if (test2->data.F64[i] != i*0.5) {
+            fprintf(stderr, "Vector data does not match. i = %d, data=%f\n",
+                    i, test->data.F64[i]);
+            return 1;
+        }
+    }
+    psFree(test);
+    psFree(test2);
+    return 0;
+}
+psS32 testVectorCreateB(void)
+{
+    //
+    //  Test PS_TYPE_U16
+    //       PS_TYPE_S16
+    //
+    psVector *test = NULL;
+    psVector *test2 = NULL;
+    psVector *input = NULL;
+    psVector *input2 = psVectorAlloc(5, PS_TYPE_U16);
+
+    test = psVectorCreate(input, 0.0, 10.0, 1.0, PS_TYPE_S16);
+    test2 = psVectorCreate(input2, 0.0, 20.0, 2.0, PS_TYPE_U16);
+
+    for (int i = 0; i < 10; i++) {
+        if (test->data.S16[i] != i) {
+            fprintf(stderr, "Vector data does not match. i = %d, data=%d\n",
+                    i, test->data.S16[i]);
+            return 1;
+        }
+        if (test2->data.U16[i] != i*2.0) {
+            fprintf(stderr, "Vector data does not match. i = %d, data=%d\n",
+                    i, test->data.U16[i]);
+            return 1;
+        }
+    }
+    psFree(test);
+    psFree(test2);
+    return 0;
+}
+psS32 testVectorGetSet(void)
+{
+    psVector *vec = NULL;
+    vec = psVectorAlloc(5, PS_TYPE_S32);
+    vec->n = 0;
+
+    if ( !psVectorSet(vec, 0, 10) ) {
+        fprintf(stderr, "VectorSet failed to set S32 at position 0\n");
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psVectorSet(vec, 10, 10) ) {
+        fprintf(stderr, "VectorSet Improperly set S32 at out of range position\n");
+    }
+    if ( !psVectorSet(vec, 1, 4) ) {
+        fprintf(stderr, "VectorSet Failed to set S32 at position 1\n");
+    }
+    if ( (psS32)psVectorGet(vec, 0) != 10 ) {
+        fprintf(stderr,
+                "VectorGet Failed to return the correct S32 from position 0\n");
+    }
+    if ( (psS32)psVectorGet(vec, -1) != 4 ) {
+        fprintf(stderr,
+                "VectorGet Failed to return the correct S32 from tail using -1\n");
+    }
+
+    psFree(vec);
+    return 0;
+}
+
+psS32 testVectorCountPixelMask(void)
+{
+    long numPix = 0;
+    long numPix2 = 0;
+    psVector *vec = NULL;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    numPix = psVectorCountPixelMask(vec, 1);
+    if (numPix != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psVectorCountPixelMask failed to return -1 for NULL Vector input.\n");
+        return 1;
+    }
+
+    numPix = 0;
+    vec = psVectorAlloc(5, PS_TYPE_S32);
+    psVector *vec2 = NULL;
+    vec2 = psVectorAlloc(5, PS_TYPE_U8);
+    vec->data.S32[0] = 0;
+    vec->data.S32[1] = 1;
+    vec->data.S32[2] = 0;
+    vec->data.S32[3] = 1;
+    vec->data.S32[4] = 0;
+    vec2->data.U8[0] = 0;
+    vec2->data.U8[1] = 1;
+    vec2->data.U8[2] = 0;
+    vec2->data.U8[3] = 1;
+    vec2->data.U8[4] = 0;
+    vec->n = 5;
+    vec2->n = 5;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    numPix = psVectorCountPixelMask(vec, 1);
+    numPix2 = psVectorCountPixelMask(vec2, 1);
+
+    if (numPix != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psVectorCountPixelMask failed to return -1 for wrong type of Vector input.\n");
+        return 2;
+    }
+    if (numPix2 == -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psVectorCountPixelMask returned -1 for correct Vector input\n");
+        return 3;
+    }
+    if (numPix2 != 2) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psVectorCountPixelMask returned incorrect pixel count %d\n", numPix2);
+        return 4;
+    }
+
+    psFree(vec);
+    psFree(vec2);
+    return 0;
+}
+
+psS32 testVectorLength( void )
+{
+    psVector *vector = psVectorAlloc(5, PS_TYPE_F32);
+
+    if (psVectorLength(vector) != 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psVectorLength failed to return the correct length of vector.\n");
+        return 1;
+    }
+    vector->n = 5;
+    if (psVectorLength(vector) != 5) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psVectorLength failed to return the correct length of vector.\n");
+        return 2;
+    }
+    vector->n++;
+    if (psVectorLength(vector) != 6) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psVectorLength failed to return the correct length of vector.\n");
+        return 3;
+    }
+    psFree(vector);
+
+    psVector *emptyVector = NULL;
+    psArray *array = psArrayAlloc(5);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psVectorLength(emptyVector) != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psVectorLength failed to return -1 for a NULL input vector.\n");
+        return 4;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psVectorLength((psVector*)array) != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psVectorLength failed to return -1 for an invalid input vector.\n");
+        return 5;
+    }
+    psFree(array);
+
+    return 0;
+}
+
+psS32 testVectorCopy(void)
+{
+
+    psVector *in = NULL;
+    psVector *copy = NULL;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    copy = psVectorCopy(copy, in, PS_TYPE_F32);
+    if (copy != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "psVectorCopy failed to return a NULL vector for NULL input.\n");
+        return 1;
+    }
+
+    in = psVectorAlloc(5, PS_TYPE_F32);
+    in->n = 5;
+    for (int i = 0; i < 5; i++) {
+        in->data.F32[i] = i;
+    }
+    //Try copy of different type
+    copy = psVectorCopy(copy, in, PS_TYPE_F64);
+    if (copy == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "psVectorCopy returned a NULL vector for correct input.\n");
+        return 2;
+    } else if (copy->data.F64[2] != 2.0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "psVectorCopy failed to return the correct data values.\n");
+        printf("\n copy->data.f64[2] = %lf, in->data.f32[2] = %f\n", copy->data.F64[2],
+               in->data.F32[2]);
+        return 3;
+    }
+
+    copy = psVectorRecycle(copy, in->n + 2, PS_TYPE_F64);
+    //Try copy of same type and non-NULL outVector of different type and size.
+    copy = psVectorCopy(copy, in, in->type.type);
+    if (copy == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "psVectorCopy returned a NULL vector for correct input.\n");
+        return 4;
+    } else if (copy->type.type != PS_TYPE_F32 || copy->data.F32[2] != 2.0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "psVectorCopy failed to return the correct data values.\n");
+        return 5;
+    }
+
+
+    psFree(in);
+    psFree(copy);
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/notes.txt	(revision 22322)
@@ -0,0 +1,13 @@
+
+test directories which are currently (2008.05.04) working:
+
+types : OK
+sys : OK
+mathtypes : OK, but tap_psImageInterpolate does not build
+math : many problems
+jpeg : no tests
+imageops : many problems
+fits : many problems
+fft : OK
+db : probably needs a working db installation
+astro : many problems
Index: /tags/sj_tags/sj_root_20080929/psLib/test/pstap/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/pstap/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/pstap/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+Makefile
+Makefile.in
+.deps
Index: /tags/sj_tags/sj_root_20080929/psLib/test/pstap/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/pstap/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/pstap/Makefile.am	(revision 22322)
@@ -0,0 +1,1 @@
+SUBDIRS = src
Index: /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+.deps
+.libs
+libpstap.la
+pstap.lo
Index: /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/Makefile.am	(revision 22322)
@@ -0,0 +1,12 @@
+AM_CPPFLAGS = $(SRCINC) -I$(top_srcdir)/test/tap/src $(PSLIB_CFLAGS)
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpslib.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(PSLIB_LIBS)
+
+TEST_LTLIBS = libpstap.la
+libpstap_la_SOURCES = pstap.c
+noinst_HEADERS = pstap.h
+
+noinst_LTLIBRARIES = $(TEST_LTLIBS)
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/pstap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/pstap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/pstap.c	(revision 22322)
@@ -0,0 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <tap.h>
+#include <pslib.h>
Index: /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/pstap.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/pstap.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/pstap/src/pstap.h	(revision 22322)
@@ -0,0 +1,145 @@
+#include <math.h>
+
+
+#include <pslib.h>
+
+#include "tap.h"
+
+#define done() ok(psMemCheckLeaks(0, NULL, stdout, false) == 0, "Memory Leaks"); \
+return exit_status()
+
+# define mem() ok(psMemCheckLeaks(psMemGetLastId(), NULL, stdout, false) == 0, "Memory Leaks")
+
+# define checkLeaks false
+
+# define checkMem() if(checkLeaks) mem()
+
+// write a comment which is counted as a test (and swallowed by prove)
+# define note(...)\
+{ \
+    fprintf(stdout, __VA_ARGS__); \
+    fprintf(stdout, "\n[%s:%d in %s]\n", __FILE__, __LINE__, __func__); \
+}
+
+# define noted(...) _gen_result(1, __func__, __FILE__, __LINE__, __VA_ARGS__);
+
+// use to test the value of a float
+# define is_float(VALUE, EXPECT, ...) \
+{ \
+    bool status = false; \
+    if (isnan(EXPECT)) { \
+        status = isnan(VALUE); \
+    } else { \
+        status = (fabsf(VALUE - EXPECT) < FLT_EPSILON); \
+    } \
+    ok(status, __VA_ARGS__); \
+    if (!status) { \
+        diag("         got: '%f'", VALUE); \
+        diag("    expected: '%f'", EXPECT); \
+    } \
+}
+
+
+// use to test the value of a float within a defined tolerance
+# define is_float_tol(VALUE,EXPECT,TOL,...)\
+{ \
+    bool status = false; \
+    if (isnan(EXPECT)) { \
+        status = isnan(VALUE); \
+    } else { \
+        status = (fabsf((VALUE) - (EXPECT)) < (TOL)); \
+    } \
+    ok(status, __VA_ARGS__); \
+    if (!status) { \
+        diag("         got: '%f'", VALUE); \
+        diag("    expected: '%f' +/- %f", EXPECT, TOL); \
+    } \
+}
+
+
+// use to test the value of a double
+# define is_double(VALUE,EXPECT,...)\
+{ \
+    bool status = false; \
+    if (isnan(EXPECT)) { \
+        status = isnan(VALUE); \
+    } else { \
+        status = (fabs(VALUE - EXPECT) < DBL_EPSILON); \
+    } \
+    ok(status, __VA_ARGS__); \
+    if (!status) { \
+        diag("         got: '%.10f'", VALUE); \
+        diag("    expected: '%.10f'", EXPECT); \
+    } \
+}
+
+
+// use to test the value of a double
+# define is_double_tol(VALUE,EXPECT,TOL, ...)\
+{ \
+    bool status = false; \
+    if (isnan(EXPECT)) { \
+        status = isnan(VALUE); \
+    } else { \
+        status = (fabs((VALUE) - (EXPECT)) < (TOL)); \
+    } \
+    ok(status, __VA_ARGS__); \
+    if (!status) { \
+        diag("         got: '%.10f'", VALUE); \
+        diag("    expected: '%.10f' +/- %.10f", EXPECT, TOL); \
+    } \
+}
+
+
+# define is_str(VALUE, EXPECT, ...) \
+{ \
+    int cmp = (strcmp(VALUE, EXPECT) == 0); \
+    ok(cmp, __VA_ARGS__); \
+    if (!cmp) { \
+        diag("         got: '%s'", VALUE); \
+        diag("    expected: '%s'", EXPECT); \
+    } \
+}
+
+
+# define is_strn(VALUE, EXPECT, N, ...)\
+{ \
+    int cmp = (strncmp(VALUE, EXPECT, N) == 0); \
+    ok(cmp, __VA_ARGS__); \
+    if (!cmp) { \
+        diag("         got: '%s'", VALUE); \
+        diag("    expected: '%s'", EXPECT); \
+    } \
+}
+
+
+# define is_int(VALUE, EXPECT, ...)\
+{ \
+    int cmp = (VALUE == EXPECT); \
+    ok(cmp, __VA_ARGS__); \
+    if (!cmp) { \
+        diag("         got: '%d'", VALUE); \
+        diag("    expected: '%d'", EXPECT); \
+    } \
+}
+
+
+# define is_long(VALUE, EXPECT, ...)\
+{ \
+    int cmp = (VALUE == EXPECT); \
+    ok(cmp, __VA_ARGS__); \
+    if (!cmp) { \
+        diag("         got: '%ld'", VALUE); \
+        diag("    expected: '%ld'", EXPECT); \
+    } \
+}
+
+# define is_bool(VALUE, EXPECT, ...)\
+{ \
+    int cmp = (VALUE == EXPECT); \
+    ok(cmp, __VA_ARGS__); \
+    if (!cmp) { \
+        diag("         got: '%s'", VALUE ? "true" : "false"); \
+        diag("    expected: '%s'", EXPECT ? "true" : "false"); \
+    } \
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/.cvsignore	(revision 22322)
@@ -0,0 +1,30 @@
+temp
+.deps
+.libs
+Makefile
+Makefile.in
+log.txt
+log2.txt
+tst_psAbort
+tst_psConfigure
+tst_psError
+tst_psLogMsg
+tst_psMemory
+tst_psString
+tst_psTrace
+tst_psTrace02_OUT
+test.fits
+core
+core.*
+tap_psStringSubstitute
+tst_psLine
+*.bb
+*.bbg
+*.da
+gmon.out
+tap_psConfigure
+tap_psError
+tap_psLine
+tap_psMemory
+tap_psString
+tap_psTrace
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/Makefile.am	(revision 22322)
@@ -0,0 +1,31 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSLIB_CFLAGS)
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpslib.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSLIB_LIBS)
+
+TEST_PROGS = \
+	tap_psString \
+	tap_psTrace \
+	tap_psError \
+	tap_psMemory \
+	tap_psLine \
+	tap_psConfigure \
+	tap_psStringSubstitute
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* log.txt log2.txt test.fits tst_psTrace02_OUT \
+	core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/table.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/table.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/table.fits	(revision 22322)
@@ -0,0 +1,1 @@
+END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tableF32.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tableF32.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tableF32.dat	(revision 22322)
@@ -0,0 +1,9 @@
+#  Table with valid types and index-values
+#
+#psF32     psU16    psU32     psU64    psS8    psS16    psS32    psS64    psU8   psF64
+-10.05    2        4         8        0       -2       -4       -8       5      -1.5
+ 1.009     4        8         16       -1      -4       -8       -16      0      -1.0
+23.45     6        12        24       -2      -6       -12      -24      5      -0.25
+3500.67   8        16        32       -3      -8       -16      -32      75     1.75
+   
+#
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psConfigure.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psConfigure.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psConfigure.c	(revision 22322)
@@ -0,0 +1,41 @@
+/** @file  tst_psConfigure.c
+ *
+ *  @brief Test driver for psconfigure functions
+ *
+ *  This test driver contains the following test points for psConfigure
+ *  functions.
+ *    1) Return current psLib version
+ *
+ *  Return:   Number of test points which failed
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-04-10 21:09:31 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib.h"
+#include "tap.h"
+#include "pstap.h"
+#include "string.h"
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    plan_tests(2);
+
+    // Simple test of psLibVersion()
+    // XX: Must somehow verify the output is correct.
+    {
+        psMemId id = psMemGetId();
+        char *stringVal = NULL;
+        stringVal = psLibVersion();
+        ok(stringVal != NULL && strlen(stringVal), "Version is cool");
+        psFree(stringVal);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psError.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psError.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psError.c	(revision 22322)
@@ -0,0 +1,382 @@
+/** @file  tst_psError.c
+ *
+ *  @brief Test driver for psError function
+ *
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.7 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2008-05-05 00:09:04 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "pslib.h"
+#include "tap.h"
+#include "pstap.h"
+
+// XXX in this file, several operations are only validated by printing an output to the screen.   
+// we should define an output file and compare the contents of the output file to expectations.
+// I've commented out these features for now
+
+# if (0)
+// Function used in testError02 to verify the psErrorStackPrintV function
+static void myErrorStackPrint(
+    FILE *fd,
+    const char *fmt,
+    ...)
+{
+    va_list ap;
+
+    // Test whether psErrorStackPrintV() accept a va_list for output variables
+    va_start(ap, fmt);
+    psErrorStackPrintV(fd, fmt, ap);
+    va_end(ap);
+}
+# endif
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(60);
+
+
+    //psError() tests
+    {
+        psS32  intval=1;
+        psS64 longval = 2;
+        float floatval = 3.01;
+        char  charval = 'E';
+        char *stringval = "E R R O R";
+
+        // Multiple type values placed in the error string
+        // XX: The output is never verified
+        {
+            psMemId id = psMemGetId();
+            psError(PS_ERR_UNKNOWN, true,
+                    "ALL TYPES intval = %d longval = %"PRId64 " floatval = %f charval = %c strval = %s",
+                    intval,longval,floatval,charval,stringval);
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+    
+
+        // String values in error message
+        // XX: The output is never verified
+        {
+            psMemId id = psMemGetId();
+            psError(PS_ERR_UNKNOWN, true, "NO VALUES");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Empty strings in error message
+        // XX: The output is never verified
+        {
+            psMemId id = psMemGetId();
+            psError(PS_ERR_UNKNOWN, true, " ");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Verify the return value of psErrorMsg is the psErrorCode passed
+        {
+            psMemId id = psMemGetId();
+            psErrorCode code=PS_ERR_BAD_PARAMETER_VALUE;
+            ok(psError(code, true, "Error code = %d", code) == code, "Failed return value verify.");
+            // psErrorStackPrint(stderr,"ERROR STACK PRINT Test1A");
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // test1B empty string in for name argument
+        {
+            psMemId id = psMemGetId();
+            psErrorCode code=PS_ERR_BAD_PARAMETER_VALUE;
+            ok(psError(code+1, true, "Error code = %d", code+1) == code+1,
+                 "Failed return with empty string.");
+            // psErrorStackPrint(stderr,"ERROR STACK PRINT Test1B");
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // test1D undefined code
+        {
+            psMemId id = psMemGetId();
+            ok(psError(-1, true, "Error code = %d", -1) == -1,
+                 "Failed return with undefined code.");
+            // psErrorStackPrint(stderr,"ERROR STACK PRINT Test1D");
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // test1E set psErrorMsg argument to false
+        {
+            psMemId id = psMemGetId();
+            psErrorCode code=PS_ERR_BAD_PARAMETER_VALUE;
+            ok(psError(code, false, "Error code = %d", code) == code,
+                "Failed return with false new arg.");
+            // psErrorStackPrint(stderr,"ERROR STACK PRINT Test1E");
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // test1F psErrorMsg with a error code less then PS_ERR_BASE(256)
+        {
+            psMemId id = psMemGetId();
+            ok(psError(9, true, "Errno code = %d", 9) == 9, "Error Code" );
+            // psErrorStackPrint(stderr,"ERROR STACK PRINT Test1F");
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+    }
+
+
+    //testError02()
+    {
+        // Generate error message and verify return value
+        {
+            psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+            psMemId id = psMemGetId();
+            ok(psError(code, true, "Error code = %d", code) == code,
+                "Failed return value verify.");
+            // myErrorStackPrint(stderr,"ERROR STACK PRINT Test%dA",2);
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+    }
+
+
+    //psErrorGet(), psErrorLast()
+    {
+        // Attempt to get last error message with an empty stack verify 
+        // psErr with code PS_ERR_NONE
+        {
+            psMemId id = psMemGetId();
+            psErrorClear();
+            psErr *last = psErrorLast();
+            ok(last != NULL, "psErrorLast() returned non-NULL");
+            skip_start(last == NULL, 1, "Skipping tests because psErrorLast() returned NULL");
+            ok(last->code == PS_ERR_NONE,
+              "psErrorLast did return PS_ERR_NONE for empty stack(%d)", last->code);
+            skip_end();
+            psFree(last);
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+        // Attempt to get specific error message with empty stack verify
+        // psErr with code PS_ERR_NONE
+        {
+            psMemId id = psMemGetId();
+            psErr *getErr= psErrorGet(2);
+            ok(getErr != NULL, "psErrorGet(2) returned non-NULL");
+            skip_start(getErr == NULL, 1, "Skipping tests because psErrorGet() returned NULL");
+            ok(getErr->code == PS_ERR_NONE,
+               "psErrorGet(2) did return PS_ERR_NONE for empty stack(%d)", getErr->code);
+            skip_end();
+            psFree(getErr);
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Attempt to get error message with invalid index and an empty stack
+        {
+            psMemId id = psMemGetId();
+            psErr *getErr= psErrorGet(-1);
+            ok(getErr != NULL, "psErrorGet(-1) returned non-NULL");
+            skip_start(getErr == NULL, 1, "Skipping tests because psErrorGet(-1) returned NULL");
+            ok(getErr->code == PS_ERR_NONE, "psErrorGet with invalid index/empty stack");
+            skip_end();
+            psFree(getErr);
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Generate three error messages, ensure they are correctly returned by psError()
+        {
+            psMemId id = psMemGetId();
+            psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+            ok(psError(code, true, "Error code = %d", code) ==  code,
+                "psError() returned correct error code (example 1)");
+            ok(psError((code+1), false, "Error code = %d", (code+1)) == (code+1),
+                "psError() returned correct error code (example 2)");
+            ok(psError((code+2), false, "Error code = %d", (code+2)) == (code+2),
+                "psError() returned correct error code (example 3)");
+    
+            psErr *last = psErrorLast();
+            psErr *getErr= psErrorGet(0);
+    
+            // Check that last and get with 0 index are equal
+            ok(last == getErr, "psErrorGet(0) equal to psErrorLast()");
+            psFree(last);
+
+            // Verify the last error message was returned
+            ok(getErr->code == (code+2), "psErrorLast() did retrieve last error");
+            psFree(getErr);
+            psErrorClear();
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Verify the middle error message can be retrieved
+        {
+            psMemId id = psMemGetId();
+            psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+            psError(code, true, "Error code = %d", code);
+            psError((code+1), false, "Error code = %d", (code+1));
+            psError((code+2), false, "Error code = %d", (code+2));
+            psErr *getErr= psErrorGet(1);
+            ok(getErr->code == (code+1), "psErrorGet() did not retrieve proper error");
+            psFree(getErr);
+            psErrorClear();
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+        // Verify the psErrorGet returns non-NULL PS_ERR_NONE if an invalid index
+        // is given with non-empty error stack
+        {
+            psMemId id = psMemGetId();
+            psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+            psError(code, true, "Error code = %d", code);
+            psError((code+1), false, "Error code = %d", (code+1));
+            psError((code+2), false, "Error code = %d", (code+2));
+            psErr *getErr= psErrorGet(-1);
+            ok(getErr->code == PS_ERR_NONE, "psErrorGet() did not return PS_ERR_NONE w/ invalid arg");
+            psFree(getErr);
+            psErrorClear();
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+    }
+
+
+    // psErrorClear()
+    {
+        // With an attemp error stack call psErrorClear
+        {
+            psMemId id = psMemGetId();
+            psErrorClear();
+            // Get the last error message and verify PS_ERR_NONE (empty stack)
+            psErr *lastAfterClear = psErrorLast();
+            ok(lastAfterClear->code == PS_ERR_NONE, "psErrorLast return expected.");
+            psFree(lastAfterClear);
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+
+        // Generate three error messages to have messages on error stack
+        {
+            psMemId id = psMemGetId();
+            psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+            ok(psError(code, true, "Error code = %d", code) == code,
+                "Failed return value verify.");
+            ok(psError((code+1), false, "Error code = %d", (code+1)) == (code+1),
+                "Failed return value verify.");
+            ok(psError((code+2), false, "Error code = %d", (code+2)) == (code+2),
+                "Failed return value verify.");
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+        // Get the last error message and verify it has the expected code
+        {
+            psMemId id = psMemGetId();
+            psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+            psErr *last = psErrorLast();
+            ok(last->code == (code+2), "psErrorLast return expected.");
+            psFree(last);
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+
+        // Clear the error stack
+        {
+            psMemId id = psMemGetId();
+            psErrorClear();
+            // Get the last error message after clear and verify is has PS_ERR_NONE code
+            psErr *lastAfterClear  = psErrorLast();
+            ok(lastAfterClear->code == PS_ERR_NONE, "psErrorLast return expected.");
+            psFree(lastAfterClear);
+            ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        }
+    }
+
+
+    // psErrorCodeString()
+    {
+        // Verify the return value of psErrorCodeString
+        // psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+        // Verify the return value of psErrorCodeString if code is negative
+        ok( psErrorCodeString(-1) == NULL, "error string with neg. code");
+    }
+
+
+    //testErrorRegister()
+    {
+        psS32 numErr = 4;
+        psErrorDescription errDesc[] = { {PS_ERR_N_ERR_CLASSES+1,"first"},
+                                         {PS_ERR_N_ERR_CLASSES+2,"second"},
+                                         {PS_ERR_N_ERR_CLASSES+3,"third"},
+                                         {PS_ERR_N_ERR_CLASSES+4,"fourth"} };
+        /*
+            1. invoke psErrorRegister with a n>1 array of psErrorDescriptions. Verify that:
+                a. Each error description given is retrievable with psErrorCodeString.
+        */
+        psErrorRegister(errDesc,numErr);
+
+        for (psS32 i = 0; i < numErr; i++) {
+            const char* desc = psErrorCodeString(PS_ERR_N_ERR_CLASSES+1+i);
+            ok(desc != NULL,
+                "psErrorCode didn't find registered error code.");
+
+            ok(strcmp(desc,errDesc[i].description) == 0,
+                "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.", desc,errDesc[i].description);
+        }
+
+        /*
+            2. invoke psErrorCodeString with a static/builtin psLib error code. Verify:
+                a. the result is correct.
+        */
+        const char* desc = psErrorCodeString(PS_ERR_N_ERR_CLASSES);
+        ok(desc != NULL, "psErrorCode didn't find static error code.");
+        ok(strcmp(desc,"error classes end marker") == 0,
+            "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.",
+            desc,"error classes end marker");
+
+        desc = psErrorCodeString(PS_ERR_NONE);
+        ok(desc != NULL,
+            "psErrorCode didn't find static error code.");
+        ok(strcmp(desc,"not an error") == 0,
+            "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.",
+            desc,"not an error");
+    
+        /*
+            3. invoke psErrorCodeString with an invalid code. Verify a NULL is returned.
+        */
+        desc = psErrorCodeString(PS_ERR_N_ERR_CLASSES+numErr+1);
+        ok(desc == NULL,
+            "psErrorCode didn't return a NULL with a bogus input code.");
+
+        /*
+            4. invoke psErrorRegister with a NULL psErrorDescription. Verify that:
+                a. the execution does not cease.
+                b. an appropriate error is generated.
+        */
+        // Following should be an error
+        psErrorClear();
+        psErrorRegister(NULL,1);
+        psErr* err = psErrorLast();
+        ok(err->code == PS_ERR_BAD_PARAMETER_NULL,
+        "psErrorCode didn't generate proper error code for NULL input.");
+
+        psFree(err);
+
+        /*
+            5. invoke psErrorRegister with nerror=0. Verify that no error occurs.
+        */
+        psErrorClear();
+        psErrorRegister(errDesc,0);
+        err = psErrorLast();
+        ok(err->code == PS_ERR_NONE,
+            "psErrorCode generated an error for nErrors = 0.");
+        psFree(err);
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psLine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psLine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psLine.c	(revision 22322)
@@ -0,0 +1,89 @@
+/** @file  tst_psLine.c
+ *
+ *  @brief Test driver for psLine functions
+ *
+ *  @author  dRob, MHPCC
+ *
+ *  @version $Revision: 1.4 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-04-10 21:09:31 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib.h"
+#include "tap.h"
+#include "pstap.h"
+#include "string.h"
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    plan_tests(16);
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+
+    //testLineAlloc()
+    {
+        psMemId id = psMemGetId();
+        psLine *lineline = NULL;
+        lineline = psLineAlloc(20);
+        ok(lineline->NLINE==20, "psLine set NLINE parameter during Allocation");
+        ok(lineline->Nline == 0, "psLine set Nline parameter during Allocation");
+        strncpy(lineline->line, "Hello World", 20);
+        ok(!strncmp(lineline->line, "Hello World", 20),
+            "psLine was stored a simple string!");
+        psFree(lineline);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // testLineInit()
+    {
+        psMemId id = psMemGetId();
+        psLine *line = NULL;
+        //Return false for NULL input
+        int okay = !psLineInit(line);
+        ok(okay, "psLineInit.  Expected false for NULL psLine input");
+        //Allocate a line and return true on Init
+        line = psLineAlloc(1);
+        okay = psLineInit(line);
+        ok(okay, "psLineInit.  Expected true for valid psLine input");
+        ok(line->NLINE == 1 && line->Nline == 0, "psLineInit returned line parameters.");
+        psFree(line);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testLineAdd()
+    {
+        psMemId id = psMemGetId();
+        psLine *line = NULL;
+        //Return false for NULL input
+        ok(!psLineAdd(line, "Hello World"),
+            "psLineAdd.  Expected false for NULL psLine input.");
+        //Allocate and return true for valid input.
+        line = psLineAlloc(20);
+        int okay = psLineAdd(line, "Hello %s", "World");
+        ok( okay, "psLineAdd.  Expected true for valid psLine input");
+        ok(line->NLINE == 20 && line->Nline == 11,
+            "psLineAdd failed to return the correct line parameters");
+        ok(!strncmp(line->line, "Hello World", 20),
+            "psLineAdd failed to store the correct line string.");
+        psFree(line);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testLineChk()
+    {
+        psMemId id = psMemGetId();
+        psLine *line = NULL;
+        //Return false for Null input line
+        ok(!psMemCheckLine(line),
+            "psMemCheckLine return false for NULL line input");
+        line = psLineAlloc(1);
+        ok(psMemCheckLine(line),
+            "psMemCheckLine return true for valid line input");
+        psFree(line);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psMemory.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psMemory.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psMemory.c	(revision 22322)
@@ -0,0 +1,621 @@
+/** @file  tst_psMemory.c
+*
+*  @brief Contains the tests for psMemory.[ch]
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2007-05-01 00:08:52 $
+*
+*  XXXX: Several tests fail with an Abort and are commented out.
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdlib.h>
+
+
+#include "pslib.h" // need to allow malloc for callback use
+#include "tap.h"
+#include "pstap.h"
+
+static psS32 problemCallbackCalled = 0;
+static psS32 allocCallbackCalled = 0;
+static psS32 freeCallbackCalled = 0;
+static psS32 exhaustedCallbackCalled = 0;
+
+psMemId memAllocCallback( const psMemBlock *ptr );
+psMemId memFreeCallback( const psMemBlock *ptr );
+psS32 memCheckTypes( void );
+void memProblemCallback( psMemBlock *ptr, const char *filename, unsigned int lineno );
+psPtr TPOutOfMemoryExhaustedCallback( size_t size );
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(46);
+
+    // TPFreeReferencedMemory()
+    {
+        psMemId id = psMemGetId();
+        psS32 *mem  = ( psS32* ) psAlloc( 100 * sizeof( psS32 ) );
+        psS32 ref = psMemGetRefCounter( mem );
+        ok(ref == 1, "buffer reference count %d.", ref );
+        skip_start ( ref != 1, 3, "buffer reference count %d.", ref );
+        psMemIncrRefCounter(mem);
+        psMemIncrRefCounter(mem);
+        psMemIncrRefCounter(mem);
+
+        ref = psMemGetRefCounter( mem );
+        ok(ref == 4, "buffer reference count was %d.", ref );
+        skip_start ( ref != 4, 2, "buffer reference count was %d.", ref );
+
+        psMemDecrRefCounter( mem );
+        psMemDecrRefCounter( mem );
+
+        ref = psMemGetRefCounter( mem );
+        ok(ref == 2, "Found buffer reference count to be %d.", ref );
+        skip_start ( ref != 2, 1, "Found buffer reference count to be %d.", ref );
+
+        psMemDecrRefCounter( mem );
+        ref = psMemGetRefCounter( mem );
+        ok(ref == 1, "Found buffer reference count to be %d.", ref );
+        skip_end();
+        skip_end();
+        skip_end();
+        psFree(mem);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Upon requesting more memory than is available, psalloc shall call
+    // the psMemExhaustedCallback.
+    // XXXX: Skipping TPOutOfMemory() because of test abort failure
+    if (0) {
+        psMemId id = psMemGetId();
+        psS32 *mem[ 100 ];
+        psMemExhaustedCallback cb;
+        for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+            mem[ lcv ] = NULL;
+        }
+        exhaustedCallbackCalled = 0;
+        cb = psMemExhaustedCallbackSet( TPOutOfMemoryExhaustedCallback );
+        #ifdef COMMENTED_OUT
+        // Don't include since intentionally aborts
+        for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+            mem[ lcv ] = ( psS32* ) psAlloc( SIZE_MAX/2 - 1000 );
+        }
+        psMemExhaustedCallbackSet( cb );
+        ok(exhaustedCallbackCalled != 0,
+             "Called psAlloc with HUGE memory requirement and survived!");
+        for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+            psFree( mem[ lcv ] );
+        }
+        #endif
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // Bug/Task #562 regression test.  Upon requesting more memory than is available,
+    // psRealloc shall call the psMemExhaustedCallback.
+    // XXXX: Skipping TPReallocOutOfMemory() because of test abort failure
+    if (0) {
+        psMemId id = psMemGetId();
+        psS32 *mem[ 100 ];
+        psMemExhaustedCallback cb;
+        for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+            mem[ lcv ] = NULL;
+        }
+        exhaustedCallbackCalled = 0;
+        cb = psMemExhaustedCallbackSet( TPOutOfMemoryExhaustedCallback );
+        for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+            mem[ lcv ] = ( psS32* ) psAlloc( 10 );
+        }
+        for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+            mem[ lcv ] = ( psS32* ) psRealloc( mem[ lcv ], SIZE_MAX/2 - 1000 );
+        }
+        psMemExhaustedCallbackSet( cb );
+        ok(exhaustedCallbackCalled != 0,
+             "Called psRealloc with HUGE memory requirement and survived in %s!", __func__ );
+        for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+            psFree( mem[ lcv ] );
+        }
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psAlloc shall allocate memory blocks writeable by caller.
+    {
+        psMemId id = psMemGetId();
+        const psS32 size = 100;
+        psS32 *mem = ( psS32* ) psAlloc( size * sizeof( psS32 ) );
+        ok(mem != NULL, "psAlloc returned non-NULL value" );
+        for ( psS32 index = 0;index < size;index++ ) {
+            mem[ index ] = index;
+        }
+        psS32 failed = 0;
+        for ( psS32 index = 0;index < size;index++ ) {
+            if ( mem[ index ] != index ) {
+                failed++;
+            }
+        }
+        ok(failed == 0, "mem legit" );
+        psFree( mem );
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psRealloc shall increase/decrease memory buffer while preserving contents
+    {
+        psMemId id = psMemGetId();
+        const psS32 initialSize = 100;
+        // allocate buffer with known values.
+        psS32 *mem1 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+        psS32 *mem2 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+        psS32 *mem3 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+        for ( psS32 lcv = 0;lcv < initialSize;lcv++ ) {
+            mem1[lcv] = mem2[lcv] = mem3[lcv] = lcv;
+        }
+        psMemCheckCorruption(stderr, false);
+        // realloc to 2x
+        mem1 = ( psS32* ) psRealloc( mem1, 2 * initialSize * sizeof( psS32 ) );
+        mem2 = ( psS32* ) psRealloc( mem2, 2 * initialSize * sizeof( psS32 ) );
+        mem3 = ( psS32* ) psRealloc( mem3, 2 * initialSize * sizeof( psS32 ) );
+        // check values of initial block
+        int error = 0;
+        for ( psS32 i = 0;i < initialSize;i++ ) {
+            if ( mem1[ i ] != i || mem2[ i ] != i || mem3[ i ] != i ) {
+                error = 1;
+                break;
+            }
+        }
+        ok(error==0, "Realloc preserve the contents with expanding buffer");
+        psMemCheckCorruption(stderr, false);
+        // realloc to 1/2 initial value.
+        mem1 = ( psS32* ) psRealloc( mem1, ( initialSize / 2 ) * sizeof( psS32 ) );
+        mem2 = ( psS32* ) psRealloc( mem2, ( initialSize / 2 ) * sizeof( psS32 ) );
+        mem3 = ( psS32* ) psRealloc( mem3, ( initialSize / 2 ) * sizeof( psS32 ) );
+        // check values of initial block
+        error = 0;
+        for ( psS32 i = 0;i < initialSize / 2;i++ ) {
+            if ( mem1[ i ] != i || mem2[ i ] != i || mem3[ i ] != i ) {
+                error = 1;
+                break;
+            }
+        }
+        ok(error==0, "Realloc preserved the contents with shrinking buffer");
+        psFree( mem1 );
+        psFree( mem2 );
+        psFree( mem3 );
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // TPallocCallback()
+    {
+        psMemId id = psMemGetId();
+        psS32 currentId = psMemGetId();
+        const psS32 initialSize = 100;
+        psS32 mark;
+        allocCallbackCalled = 0;
+        freeCallbackCalled = 0;
+        psMemAllocCallbackSet( memAllocCallback );
+        psMemFreeCallbackSet( memFreeCallback );
+        psMemAllocCallbackSetID( currentId + 1 );
+        psMemFreeCallbackSetID( currentId + 1 );
+        // allocate buffer with known values.
+        psS32 *mem1 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+        psS32 *mem2 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+        psS32 *mem3 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+        psFree(mem1);
+        psFree(mem2);
+        psFree(mem3);
+        ok(allocCallbackCalled == 2 && freeCallbackCalled == 2,
+            "alloc/free callbacks called the proper number of times" );
+        allocCallbackCalled = 0;
+        freeCallbackCalled = 0;
+        mark = psMemGetId();
+        mem1 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+        psMemAllocCallbackSetID( mark );
+        mem1 = ( psS32* ) psRealloc( mem1, initialSize * 2 * sizeof( psS32 ) );
+        psFree( mem1 );
+        ok(allocCallbackCalled == 2,
+             "realloc callbacks were called the proper number of times" );
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // TPcheckLeaks()
+    // XXXX: Skipping TPcheckLeaks() because of test abort failure
+    if (0) {
+        const psS32 numBuffers = 5;
+        psS32* buffers[ 5 ];
+        psS32 lcv;
+        psS32 currentId = psMemGetId();
+        psMemBlock** blks;
+        psS32 nLeaks = 0;
+        psS32 lineMark = 0;
+
+        for ( lcv = 0;lcv < numBuffers;lcv++ ) {
+            lineMark = __LINE__ + 1;
+            buffers[ lcv ] = psAlloc( sizeof( psS32 ) );
+        }
+        for ( lcv = 1;lcv < numBuffers;lcv++ ) {
+            psFree( buffers[ lcv ] );
+        }
+        nLeaks = psMemCheckLeaks( currentId, &blks, stderr, false );
+        ok(nLeaks == 1, "psMemCheckLeaks found %d leaks", nLeaks );
+        ok(blks[ 0 ] ->lineno == lineMark,
+             "psMemCheckLeaks found a leak other than the expected one (line %d vs %d)", lineMark, blks[ 0 ] ->lineno );
+        psFree( buffers[ 0 ] );
+        psFree( blks );
+        psMemCheckLeaks(currentId,NULL,stderr, false);
+        for ( lcv = 0;lcv < numBuffers;lcv++ ) {
+            lineMark = __LINE__ + 1;
+            buffers[ lcv ] = psAlloc( sizeof( psS32 ) );
+        }
+        for ( lcv = 0;lcv < numBuffers - 1;lcv++ ) {
+            psFree( buffers[ lcv ] );
+        }
+        nLeaks = psMemCheckLeaks( currentId, &blks, stderr, false );
+        ok(nLeaks == 1, "psMemCheckLeaks found %d leaks.", nLeaks );
+        ok(blks[ 0 ] ->lineno == lineMark, "psMemCheckLeaks found leaks");
+        psFree( buffers[ 4 ] );
+        psFree( blks );
+        for ( lcv = 0;lcv < numBuffers;lcv++ ) {
+            lineMark = __LINE__ + 1;
+            buffers[ lcv ] = psAlloc( sizeof( psS32 ) );
+        }
+        for ( lcv = 0;lcv < numBuffers;lcv++ ) {
+            if ( lcv % 2 == 0 ) {
+                psFree( buffers[ lcv ] );
+            }
+        }
+        nLeaks = psMemCheckLeaks( currentId, &blks, stderr, false );
+        ok(nLeaks == 2, "psMemCheckLeaks found %d leaks.", nLeaks);
+        ok(blks[ 0 ] ->lineno == lineMark,
+             "psMemCheckLeaks found a leak other than the expected." );
+        psFree(blks);
+        psFree(buffers[1]);
+        psFree(buffers[3]);
+    }
+
+
+    void TPmultipleFree( void );
+    // XXXX: Skipping TPmultipleFree() because of test abort failure
+    if (0) {
+        TPmultipleFree();
+    }
+
+    // memCheckTypes()
+    if (1) {
+        psMemId id = psMemGetId();
+        psArray *negative = psArrayAlloc(2);
+        psMetadata *neg = psMetadataAlloc();
+
+        psArray *array = psArrayAlloc(100);
+        int okay = psMemCheckType(PS_DATA_ARRAY,array);
+        if (!okay) {
+            psFree(array);
+        }
+        ok(okay, "psMemCheckArray in memCheckType");
+
+        ok(!psMemCheckType(PS_DATA_ARRAY, neg), "psMemCheckType with metadata input");
+        psFree(array);
+
+        psBitSet *bits;
+        bits = psBitSetAlloc(100);
+        okay = psMemCheckType(PS_DATA_BITSET, bits);
+        if (!okay )
+            psFree(bits);
+        ok(okay, "psMemCheckBitSet in memCheckType");
+        ok(!psMemCheckType(PS_DATA_BITSET, negative), "psMemCheckType on psArray");
+        psFree(bits);
+
+        psCube *cube;
+        cube = psCubeAlloc();
+        okay = psMemCheckType(PS_DATA_CUBE, cube);
+        if (!okay )
+            psFree(cube);
+        ok(okay, "psMemCheckCube in memCheckType");
+        psFree(cube);
+
+        psFits *fits;
+        fits = psFitsOpen("test.fits","w");
+        psImage* img = psImageAlloc(16,16,PS_TYPE_F32);
+        psFitsWriteImage(fits,NULL,img,1,NULL);
+        psFree(img);
+        okay = psMemCheckType(PS_DATA_FITS, fits);
+        if (!okay )
+            psFree(fits);
+        ok(okay, "psMemCheckFits in memCheckType");
+        psFitsClose(fits);
+
+        psHash *hash;
+        hash = psHashAlloc(100);
+        okay = psMemCheckType(PS_DATA_HASH, hash);
+        if (!okay )
+            psFree(hash);
+        ok(okay, "psMemCheckHash in memCheckType");
+        psFree(hash);
+
+        psHistogram *histogram;
+        histogram = psHistogramAlloc(1.1, 2.2, 2);
+        okay = psMemCheckType(PS_DATA_HISTOGRAM, histogram);
+        if (!okay )
+            psFree(histogram);
+        ok(okay, "psMemCheckHistogram in memCheckType");
+        psFree(histogram);
+
+        psImage *image;
+        image = psImageAlloc(5, 5, PS_TYPE_F32);
+        okay = psMemCheckType(PS_DATA_IMAGE, image);
+        if (!okay )
+            psFree(image);
+        ok(okay, "psMemCheckImage in memCheckType");
+        psFree(image);
+
+        psKernel *kernel;
+        kernel = psKernelAlloc(0, 1, 0, 1);
+        okay = psMemCheckType(PS_DATA_KERNEL, kernel);
+        if (!okay )
+            psFree(kernel);
+        ok(okay, "psMemCheckKernel in memCheckType");
+        psFree(kernel);
+
+        psList *list;
+        list = psListAlloc(NULL);
+        okay = psMemCheckType(PS_DATA_LIST, list);
+        if (!okay )
+            psFree(list);
+        ok(okay, "psMemCheckList in memCheckType");
+        psFree(list);
+
+        psLookupTable *lookup;
+        char *file = "tableF32.dat";
+        char *format = "\%f \%lf \%d \%ld";
+        lookup = psLookupTableAlloc(file, format, 10);
+        okay = psMemCheckType(PS_DATA_LOOKUPTABLE, lookup);
+        if (!okay )
+            psFree(lookup);
+        ok(okay, "psMemCheckLookupTable in memCheckType");
+        psFree(lookup);
+
+        psMetadata *metadata;
+        metadata = psMetadataAlloc();
+        okay = psMemCheckType(PS_DATA_METADATA, metadata);
+        if (!okay )
+            psFree(metadata);
+        ok(okay, "psMemCheckMetadata in memCheckType");
+        psFree(metadata);
+
+        psMetadataItem *metaItem;
+        metaItem = psMetadataItemAlloc("name", PS_DATA_S32, "COMMENT", 1);
+        okay = psMemCheckType(PS_DATA_METADATAITEM, metaItem);
+        if (!okay )
+            psFree(metaItem);
+        ok(okay, "psMemCheckMetadataItem in memCheckType");
+        psFree(metaItem);
+
+        psMinimization *min;
+        min = psMinimizationAlloc(3, 0.1);
+        okay = psMemCheckType(PS_DATA_MINIMIZATION, min);
+        if (!okay )
+            psFree(min);
+        ok(okay, "psMemCheckMinimization in memCheckType");
+        psFree(min);
+
+        psPixels *pixels;
+        pixels = psPixelsAlloc(100);
+        okay = psMemCheckType(PS_DATA_PIXELS, pixels);
+        if (!okay )
+            psFree(pixels);
+        ok(okay, "psMemCheckPixels in memCheckType");
+        psFree(pixels);
+
+        psPlane *plane;
+        plane = psPlaneAlloc();
+        okay = psMemCheckType(PS_DATA_PLANE, plane);
+        if (!okay )
+            psFree(plane);
+        ok(okay, "psMemCheckPlane in memCheckType.");
+        psFree(plane);
+
+        psPlaneDistort *planeDistort;
+        planeDistort = psPlaneDistortAlloc(1, 1, 1, 1);
+        okay =  psMemCheckType(PS_DATA_PLANEDISTORT, planeDistort);
+        if (!okay )
+            psFree(planeDistort);
+        ok(okay, "psMemCheckPlaneDistort in memCheckType.");
+        psFree(planeDistort);
+
+        psPlaneTransform *planeTransform;
+        planeTransform = psPlaneTransformAlloc(1, 1);
+        okay = psMemCheckType(PS_DATA_PLANETRANSFORM, planeTransform);
+        if (!okay )
+            psFree(planeTransform);
+        ok(okay, "psMemCheckPlaneTransform in memCheckType");
+        psFree(planeTransform);
+    
+        psPolynomial1D *poly1;
+        poly1 = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+        okay = psMemCheckType(PS_DATA_POLYNOMIAL1D, poly1);
+        if (!okay )
+            psFree(poly1);
+        ok(okay, "psMemCheckPolynomial1D in memCheckType");
+        psFree(poly1);
+    
+        psPolynomial2D *poly2;
+        poly2 = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 2, 1);
+        okay = psMemCheckType(PS_DATA_POLYNOMIAL2D, poly2);
+        if (!okay )
+            psFree(poly2);
+        ok(okay, "psMemCheckPolynomial2D in memCheckType");
+        psFree(poly2);
+    
+        psPolynomial3D *poly3;
+        poly3 = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, 2, 1, 1);
+        okay = psMemCheckType(PS_DATA_POLYNOMIAL3D, poly3);
+        if (!okay )
+            psFree(poly3);
+        ok(okay, "psMemCheckPolynomial3D in memCheckType");
+        psFree(poly3);
+    
+        psPolynomial4D *poly4;
+        poly4 = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 2, 1, 2, 1);
+        okay = psMemCheckType(PS_DATA_POLYNOMIAL4D, poly4);
+        if (!okay )
+            psFree(poly4);
+        ok(okay, "psMemCheckPolynomial4D in memCheckType");
+        psFree(poly4);
+    
+        psProjection *proj;
+        proj = psProjectionAlloc(1, 1, 2.1, 2.1, PS_PROJ_TAN);
+        okay = psMemCheckType(PS_DATA_PROJECTION, proj);
+        if (!okay )
+            psFree(proj);
+        ok(okay, "psMemCheckProjection in memCheckType.");
+        psFree(proj);
+    
+        psScalar *scalar;
+        psF64 f64 = 1.1;
+        scalar = psScalarAlloc(f64, PS_TYPE_F64);
+        okay = psMemCheckType(PS_DATA_SCALAR, scalar);
+        if (!okay )
+            psFree(scalar);
+        ok(okay, "psMemCheckScalar in memCheckType");
+        psFree(scalar);
+    
+        psSphere *sphere;
+        sphere = psSphereAlloc();
+        okay = psMemCheckType(PS_DATA_SPHERE, sphere);
+        if (!okay )
+            psFree(sphere);
+        ok(okay, "psMemCheckSphere in memCheckType");
+        psFree(sphere);
+    
+        psSphereRot *sphereRot;
+        sphereRot = psSphereRotAlloc(0, 0, 20);
+        okay = psMemCheckType(PS_DATA_SPHEREROT, sphereRot);
+        if (!okay )
+            psFree(sphereRot);
+        ok(okay, "psMemCheckSphereRot in memCheckType");
+        psFree(sphereRot);
+    
+        psSpline1D *spline;
+        spline = psSpline1DAlloc(2, 1, 0, 2);
+        okay = psMemCheckType(PS_DATA_SPLINE1D, spline);
+        if (!okay )
+            psFree(spline);
+        ok(okay, "psMemCheckSpline1D in memCheckType");
+        psFree(spline);
+    
+        psStats *stats;
+        stats = psStatsAlloc(PS_STAT_MAX);
+        okay = psMemCheckType(PS_DATA_STATS, stats);
+        if (!okay )
+            psFree(stats);
+        ok(okay, "psMemCheckStats in memCheckType");
+        psFree(stats);
+    
+        psTime *time;
+        time = psTimeAlloc(PS_TIME_UT1);
+        okay = psMemCheckType(PS_DATA_TIME, time);
+        if (!okay )
+            psFree(time);
+        ok(okay, "psMemCheckTime in memCheckType");
+        psFree(time);
+    
+        psVector *vector;
+        vector = psVectorAlloc(100, PS_TYPE_F32);
+        okay = psMemCheckType(PS_DATA_VECTOR, vector);
+        ok(okay, "psMemCheckVector in memCheckType");
+        psFree(vector);
+        psFree(negative);
+        psFree(neg);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
+
+#if 0
+void TPmemCorruption( void )
+{
+    diag("TPmemCorruption");
+
+    psS32 * buffer = NULL;
+    psS32 oldValue = 0;
+    psS32 corruptions = 0;
+    psMemProblemCallback cb;
+
+    buffer = psAlloc( sizeof( psS32 ) );
+
+    // cause memory corruption via buffer underflow
+    *buffer = 1;
+    buffer--;
+    oldValue = *buffer;
+    *buffer = 2;
+
+    problemCallbackCalled = 0;
+    cb = psMemProblemCallbackSet( memProblemCallback );
+
+    corruptions = psMemCheckCorruption( 0 );
+
+    // restore the memory problem callback
+    psMemProblemCallbackSet( cb );
+
+    // restore the value, 'uncorrupting' the buffer
+    *buffer = oldValue;
+    buffer++;
+
+    psFree( buffer );
+
+    ok(corruptions == 1,
+         "Expected one memory corruption but found %d", corruptions );
+    ok(problemCallbackCalled == 1, "The memProblemCallback was invoked" );
+}
+#endif
+
+
+void memProblemCallback( psMemBlock *ptr, const char *file, unsigned int lineno )
+{
+    problemCallbackCalled++;
+}
+
+
+psMemId memAllocCallback( const psMemBlock *ptr )
+{
+    allocCallbackCalled++;
+    return 1;
+}
+
+psMemId memFreeCallback( const psMemBlock *ptr )
+{
+    freeCallbackCalled++;
+    return 1;
+}
+
+psPtr TPOutOfMemoryExhaustedCallback( size_t size )
+{
+    exhaustedCallbackCalled++;
+    return NULL;
+}
+
+void TPmultipleFree( void )
+{
+    psPtr buffer = psAlloc( 1024 );
+    psPtr buffer2 = buffer;
+
+    psFree( buffer );
+    psFree( buffer2 );
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psString.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psString.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psString.c	(revision 22322)
@@ -0,0 +1,463 @@
+/** @file  tst_psString.c
+ *
+ * -*- mode: C; c-basic-indent: 4; tab-width: 8; indent-tabs-mode: nil -*-
+ * vim: set cindent ts=8 sw=4 expandtab:
+ *
+ *  @brief Test driver for psString functions
+ *
+ *  This test driver contains the following test points for psStringCopy
+ *  and psStringNCopy, and related string functions.
+ *    1) Verify string copy - psStringCopy
+ *    2) Verify empty string copy - psStringCopy
+ *    3) Verify string copy with length - psStringNCopy
+ *    4) Verify empty string copy with length - psStringNCopy
+ *    5) Copy string to larger string - psStringNCopy
+ *    6) Copy string with negative size - psStringNCopy
+ *    7) Verifiy creation of string literal - PS_STRING
+ *
+ *  Return:   Number of test points which failed
+ *
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.11 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2008-05-05 00:09:04 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <string.h>
+#include "pslib.h"
+#include "tap.h"
+#include "pstap.h"
+
+#define STR_0 "binky had a leeeetle lamb"
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(65);
+
+
+    //testStringCopy00()
+    {
+        psMemId id = psMemGetId();
+        char  stringval[20] = "E R R O R";
+        psS32   result = 0;
+        psS32   result1 = 0;
+        char  *strResult;
+
+        // Test point #1 Verify string copy - psStringCopy
+        strResult = psStringCopy(stringval);
+        // Perform string compare
+        result = strcmp(strResult, stringval);
+        // Modify original string
+        stringval[0]='G';
+        result1 = strcmp(strResult, stringval);
+        stringval[0]='E';
+        ok(( result == 0 ) && ( result1 != 0),
+             "Failed test point #1 strcmp result = %d expected 0",result);
+        // Free memory allocated
+        psFree(strResult);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testStringCopy01()
+    {
+        psMemId id = psMemGetId();
+        char  *emptyval = "";
+        psS32   result = 0;
+        char  *strResult;
+
+        // Test point #2 Verify empty string copy - psStringCopy
+        strResult = psStringCopy(emptyval);
+        // Perform string compare
+        result = strcmp(strResult, emptyval);
+        ok(result == 0,
+             "test point #2 strcmp result = %d expected 0",result);
+        // Free memory allocated
+        psFree(strResult);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testStringCopy02()
+    {
+        psMemId id = psMemGetId();
+        psS32   result = 0;
+        psS32   result1 = 0;
+        char  *strResult;
+        char  stringval1[20] = "e r r o r";
+        psS32   substringlen = 5;
+        char  *substringval = "e r r";
+
+        // Test point #3 Verify string copy with length - psStringNCopy
+        strResult = psStringNCopy(stringval1, substringlen);
+        // Perform string compare and get string length
+        result = strncmp(strResult, substringval, substringlen);
+        // Change original string
+        stringval1[0] = 'g';
+        result1 = strncmp(strResult, substringval, substringlen);
+        ok(( result == 0 ) && ( result1 == 0 ),
+             "Failed test point #3 strcmp result = %d expected 0",result);
+        psFree(strResult);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testStringCopy03()
+    {
+        psMemId id = psMemGetId();
+        psS32   result = 0;
+        psS32   result1 = 0;
+        char  *strResult;
+        char  *stringvalnocopy = "F A I L";
+
+        // Test point #4 Verify empty string copy with length - psStringNCopy
+        strResult = psStringNCopy(stringvalnocopy, 0);
+        // Perform string compare and get sting length
+        result = strcmp(strResult, stringvalnocopy);
+        result1 = strlen(strResult);
+        ok(result != 0,
+             "test point #4 strcmp result = %d didn't expected %d",result,0);
+        psFree(strResult);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testStringCopy04()
+    {
+        psMemId id = psMemGetId();
+        psS32   result = 0;
+        psS32   result1 = 0;
+        char  *strResult;
+        char  stringval[20] = "E R R O R";
+        psS32   increaseSize = 5;
+
+        // Test point #5 Copy string to larger string - psStringNCopy
+        strResult = psStringNCopy(stringval, (strlen(stringval) + increaseSize));
+        // Perform string compare and get string length
+        result = strcmp(strResult, stringval);
+        result1 = strlen(strResult);
+        // The strings should still compare
+        ok(result == 0 && result1 == strlen(stringval),
+             "test point #5 strcmp result = %d expected %d",result,0);
+        psFree(strResult);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+// XXX This test needs to be modified to check for maximum size
+//     This will require a mod to psStringNCopy source to check for maximum size
+//
+//psS32 testStringCopy05()
+//{
+//    char  *strResult;
+//    char  stringval[20] = "E R R O R";
+//    psS32   negativeSize = -5;
+//
+//    // Test point #6 Copy string with negative size - psStringNCopy
+//    strResult = psStringNCopy(stringval, negativeSize);
+//    if ( strResult != NULL ) {
+//        fprintf(stderr,"Failed test point #6 return value = %p expected NULL\n",
+//                strResult);
+//        return 1;
+//    }
+//    // Memory should not have been allocated
+//
+//    return 0;
+//}
+
+
+    // testStringCopy06()
+    {
+        psMemId id = psMemGetId();
+        char  *strResult;
+        char  stringval[20] = "E R R O R";
+        psS32   result = 0;
+
+        // Test point #7 Verify creation of string literal - PS_STRING
+        strResult = PS_STRING(E R R O R);
+        result = strcmp(strResult, stringval);
+        ok(result == 0,
+             "test point #7 strcmp result = %d expected %d",result,0);
+        // Memory should not have been allocated
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testStrAppend00()
+    {
+        psMemId id = psMemGetId();
+        char *str = psStringCopy("3.14159");
+        psStringAppend(&str, "%d%s", 2653589, "79323846");
+        // Test point: Verify string append
+        int result = strcmp(str, "3.14159265358979323846");
+        ok(result == 0, "Failed test point");
+        psFree(str);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+#if 0
+    // testStrAppend01()
+    {
+        psMemId id = psMemGetId();
+        char *str=NULL;
+        // test nonsensical invocations ...
+        ssize_t sz = psStringAppend(NULL, NULL);
+        ok(sz == 0, "Failed test point");
+        sz = psStringAppend(&str, NULL);
+        ok(sz == 0, "Failed test point");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+#endif
+
+    // testStrAppend02()
+    {
+        psMemId id = psMemGetId();
+        char *str=NULL;
+        // test string creation
+        psStringAppend(&str, "%s", "fubar");
+        int result = strcmp(str, "fubar");
+        ok(result == 0, "Failed test point");
+        psFree(str);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testStrAppend03()
+    {
+        psMemId id = psMemGetId();
+        char *str =psStringCopy(STR_0);
+        // test null-op
+        psStringAppend(&str, "%s", "");
+        is_str(str, STR_0, "Failed test point");
+        psFree(str);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testStrPrepend00()
+    {
+        psMemId id = psMemGetId();
+        char *str = psStringCopy("79323846");
+        psStringPrepend(&str, "%s%d","3.14159", 2653589 );
+        // Test point: Verify string append
+        int result = strcmp(str, "3.14159265358979323846");
+        ok(result == 0, "Failed test point");
+        psFree(str);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+#if 0
+    // testStrPrepend01()
+    {
+        psMemId id = psMemGetId();
+        char *str=NULL;
+        // test nonsensical invocations ...
+        ssize_t sz = psStringPrepend(NULL, NULL);
+        ok(sz == 0, "Failed test point");
+        sz = psStringPrepend(&str, NULL);
+        ok(sz == 0, "Failed test point");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+#endif
+
+    // testStrPrepend02()
+    {
+        psMemId id = psMemGetId();
+        char *str=NULL;
+        // test string creation
+        psStringPrepend(&str, "%s", "fubar");
+        int result = strcmp(str, "fubar");
+        ok(result == 0, "Failed test point");
+        psFree(str);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testStrPrepend03()
+    {
+        // test null-op
+        psMemId id = psMemGetId();
+        char *str =  psStringCopy(STR_0);
+        psStringPrepend(&str, "%s", "");
+        int result = strcmp(str, STR_0);
+        ok(result == 0, "test point str=[%s]", str);
+        psFree(str);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testStrSplit00()
+    {
+        psMemId id = psMemGetId();
+        psList *strList = NULL;
+        char str[35];
+        char split[5];
+        strncpy(str, "This is, a, test case, to check", 35);
+        strncpy(split, ",", 2);
+        psString psStr;
+        psString psSplit;
+        psStr = psStringCopy(str);
+        psSplit = psStringCopy(split);
+
+        //Return NULL for NULL inputs
+        strList = psStringSplit(NULL, NULL, true);
+        ok(!strList, "psStringSplit" );
+        psFree(strList);
+
+        strList = NULL;
+        //Return empty list for NULL string input
+        strList = psStringSplit(NULL, split, true);
+        ok(!psListLength(strList), "psListLength()" );
+        psFree(strList);
+
+        strList = NULL;
+        //Return NULL for NULL splitter input
+        strList = psStringSplit(str, NULL, true);
+        ok(!strList, "psStringSplit" );
+        psFree(strList);
+
+        strList = NULL;
+        //Return a psList* of psStrings
+        strList = psStringSplit(str, split, true);
+        ok(strList->n == 4,
+            "psStringSplit to return the correct number of strings");
+        ok(!strncmp((psString)(strList->head->data), "This is", 10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->data), " a", 10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->next->data), " test case",10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->next->next->data), " to check", 10),
+             "psStringSplit failed to return expected strings");
+
+        psFree(strList);
+        //Return correct psList when using (psString, char*)
+        strList = psStringSplit(psStr, split, true);
+        ok(strList->n == 4,
+            "psStringSplit to return the correct number of strings");
+        ok(!strncmp((psString)(strList->head->data), "This is", 10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->data), " a", 10),
+             "psStringSplit failed to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->next->data), " test case",10),
+             "psStringSplit failed to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->next->next->data), " to check", 10),
+             "psStringSplit to return expected strings");
+
+        psFree(strList);
+        //Return correct psList when using (char*, psString)
+        strList = psStringSplit(str, psSplit, true);
+        ok(strList->n == 4,
+            "psStringSplit to return the correct number of strings");
+        ok(!strncmp((psString)(strList->head->data), "This is", 10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->data), " a", 10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->next->data), " test case",10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->next->next->data), " to check", 10),
+             "psStringSplit to return expected strings");
+
+        psFree(strList);
+        //Return correct psList when using (psString, psString)
+        strList = psStringSplit(psStr, psSplit, true);
+        ok(strList->n == 4,
+            "psStringSplit to return the correct number of strings");
+        ok(!strncmp((psString)(strList->head->data), "This is", 10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->data), " a", 10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->next->data), " test case",10),
+             "psStringSplit  to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->next->next->data), " to check", 10),
+             "psStringSplit to return expected strings");
+
+        psFree(strList);
+        //Return correct psList output for string of zero length case
+        strncpy(str, "This is,, a,, test case,, to check", 35);
+        strList = psStringSplit(str, split, false);
+        ok(strList->n == 4,
+            "psStringSplit to return the correct number of strings");
+        ok(!strncmp((psString)(strList->head->data), "This is", 10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->data), " a", 10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->next->data), " test case",10),
+             "psStringSplit to return expected strings");
+        ok(!strncmp((psString)(strList->head->next->next->next->data), " to check", 10),
+             "psStringSplit to return expected strings");
+        psFree(strList);
+        psFree(psStr);
+        psFree(psSplit);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testNULLStrings()
+    {
+        psMemId id = psMemGetId();
+        psString nullTest = NULL;
+        psString output = NULL;
+        ssize_t outSize = 0;
+        char** nullDest = NULL;
+        char** test = NULL;
+        //psStringCopy should return NULL for NULL input string
+        // Following should generate error message
+        output = psStringCopy(nullTest);
+        ok(output == NULL, "psStringCopy to return NULL for NULL input string");
+
+        //psStringNCopy should return NULL for NULL input string
+        output = psStringNCopy(nullTest, 100);
+        ok(output == NULL, "psStringNCopy to return NULL for NULL input string");
+
+        //psStringAppend should return 0 for NULL input destination
+        outSize = psStringAppend(nullDest, "%s", "");
+        ok(outSize == 0, "psStringAppend to return 0 for NULL input destination");
+
+        //psStringAppend should return 0 for NULL input format
+        outSize = psStringAppend(test, nullTest);
+        ok(outSize == 0, "psStringAppend to return 0 for NULL input format");
+
+        //psStringPrepend should return 0 for NULL input destination
+        outSize = psStringPrepend(nullDest, " ");
+        ok(outSize == 0, "psStringPrepend to return 0 for NULL input destination");
+
+        //psStringPrepend should return 0 for NULL input format
+        outSize = psStringPrepend(test, nullTest);
+        ok(outSize == 0, "psStringPrepend to return 0 for NULL input format");
+
+        //psStringSplit should return empty list for NULL input string
+        psList *nullList = NULL;
+        nullList = psStringSplit(nullTest, ",", true);
+        ok(!psListLength(nullList), "psStringSplit to return NULL for NULL input string");
+        psFree(nullList);
+
+        nullList = NULL;
+        //psStringSplit should return NULL for NULL input splitter
+        nullList = psStringSplit("Hello World", nullTest, true);
+        ok(!nullList, "psStringSplit to return NULL for NULL input splitter");
+        psFree(nullList);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testStrCheck()
+    {
+        psMemId id = psMemGetId();
+        psString str = psStringAlloc(10);
+        strcpy(str, "Hello");
+        ok(psMemCheckString(str), "psString allocated!");
+        ok(psMemCheckType(PS_DATA_STRING, str), "psString allocated");
+        psFree(str);
+
+	// XXX EAM this function raises an abort since we are trying to test a non-psLib memory block
+        // char charStr[10];
+        // ok(!psMemCheckType(PS_DATA_STRING, charStr), "Input string is a psDataType");
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psStringSubstitute.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psStringSubstitute.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psStringSubstitute.c	(revision 22322)
@@ -0,0 +1,104 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+#define ORIGINAL "This is, a, test case, to check."
+#define CORRECTED "This is a test case to check."
+
+int main (void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(16);
+
+
+    // Return input for NULL key
+    {
+        psMemId id = psMemGetId();
+        psString input = psStringCopy(ORIGINAL);
+        psStringSubstitute(&input, ",", NULL);
+        ok(input && strcmp(input, ORIGINAL) == 0, "output = %s", input);
+        psFree(input);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return input for empty key
+    {
+        psMemId id = psMemGetId();
+        psString input = psStringCopy(ORIGINAL);
+        psStringSubstitute(&input, "XXX", "");
+        ok(input && strcmp(input, ORIGINAL) == 0, "output = %s", input);
+        psFree(input);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return corrected version for NULL replace
+    {
+        psMemId id = psMemGetId();
+        psString input = psStringCopy(ORIGINAL);
+        psStringSubstitute(&input, NULL, ",");
+        ok(input && strcmp(input, CORRECTED) == 0, "output = %s", input);
+        psFree(input);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return corrected version for empty replace
+    {
+        psMemId id = psMemGetId();
+        psString input = psStringCopy(ORIGINAL);
+        psStringSubstitute(&input, "", ",");
+        ok(input && strcmp(input, CORRECTED) == 0, "output = %s", input);
+        psFree(input);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return NULL for NULL input
+    {
+        psMemId id = psMemGetId();
+        int status = psStringSubstitute(NULL, "XXX", ",");
+        ok(status == 0, "status = %d", status);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return emptry string for empty input
+    {
+        psMemId id = psMemGetId();
+        psString input = psStringCopy("");
+        psStringSubstitute(&input, "XXX", ",");
+        ok(input && strcmp(input, "") == 0, "output = %s", input);
+        psFree(input);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Change commas to bangs
+    {
+        psMemId id = psMemGetId();
+        psString input = psStringCopy(ORIGINAL);
+        psStringSubstitute(&input, "!", ",");
+        ok(input && strcmp(input, "This is! a! test case! to check.") == 0, "output = %s", input);
+        psFree(input);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Long replacement text --- should allocate new space
+    {
+        psMemId id = psMemGetId();
+        psString input = psStringCopy(ORIGINAL);
+        psStringSubstitute(&input, "; This string is too long to fit in input(35 chars)", ".");
+        ok(input && strcmp(input, "This is, a, test case, to check; "
+                           "This string is too long to fit in input(35 chars)") == 0,
+           "output = %s", input);
+        psFree(input);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psTrace.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psTrace.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tap_psTrace.c	(revision 22322)
@@ -0,0 +1,302 @@
+/*****************************************************************************
+    This code will test whether trace levels can be set successfully.
+ 
+    XXX: For the last two testpoints, must verify that the results are
+    correct, and put that verification in the test as well.
+ *****************************************************************************/
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "pslib.h"
+#include "tap.h"
+#include "pstap.h"
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(54);
+
+# define DEBUG 1
+# if (DEBUG)
+    FILE *output = fopen ("/dev/null", "w");
+    int outFD = fileno (output);
+# else
+    int outFD = 2;
+# endif
+
+    // testTrace00()
+    {
+        psMemId id = psMemGetId();
+        psS32 lev = 0;
+        (void)psTraceSetDestination(outFD);
+        for (int i=0;i<10;i++) {
+            (void)psTraceSetLevel(".", i);
+            lev = psTraceGetLevel(".");
+            ok(lev == i, "trace level was %d, actual was %d", i, lev);
+        }
+
+        (void)psTraceSetLevel(".", 3);
+        for (int i=5;i<10;i++) {
+            (void)psTraceSetLevel(".NODE00", i);
+            lev = psTraceGetLevel(".NODE00");
+            ok (lev == i,"(.NODE00) expected trace level was %d, actual was %d",
+                i, lev);
+    
+            lev = psTraceGetLevel(".");
+            ok (lev == 3,
+                "expected trace level was %d, actual was %d", i, 3);
+        }
+    
+        (void)psTraceSetLevel(".NODE00.NODE01", 4);
+        for (int i=0;i<10;i++) {
+            (void)psTraceSetLevel(".NODE00.NODE01", i);
+            lev = psTraceGetLevel(".NODE00.NODE01");
+            ok (lev == i,
+                "(.NODE00.NODE01) expected trace level was %d, actual was %d",
+                i, lev);
+        }
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testTrace01()
+    {
+        psMemId id = psMemGetId();
+        (void)psTraceSetDestination(outFD);
+        (void)psTraceSetLevel(".A.B.C.D.E", 5);
+        psTrace(".A.C.D.C",1,"You should not see this");
+        psTrace(".A.B.C.D.E",2,"You should see this");
+        psTrace(".A.B.C.D.E.F",3,"You should see this too");
+        psTracePrintLevels();
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testTrace02()
+    {
+        psMemId id = psMemGetId();
+        psTraceReset();
+        (void)psTraceSetDestination(outFD);
+        (void)psTraceSetLevel(".A.B", 2);
+        (void)psTraceSetLevel(".A.B.C.D.E", 5);
+        psTracePrintLevels();
+        (void)psTraceSetLevel(".A.B", 10);
+        psTracePrintLevels();
+
+        ok (10 == psTraceGetLevel(".A.B.C"),
+            ".A.B.C did not dynamically inherit a trace level (%d)",
+            psTraceGetLevel(".A.B.C"));
+
+        ok (10 == psTraceGetLevel(".A.B.C.D"),
+            ".A.B.C.D did not dynamically inherit a trace level (%d)",
+            psTraceGetLevel(".A.B.C.D"));
+
+        ok (5 == psTraceGetLevel(".A.B.C.D.E"),
+            ".A.B.C.D.E did dynamically inherit a trace level (%d)",
+            psTraceGetLevel(".A.B.C.D.E"));
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testTrace03()
+    {
+        psMemId id = psMemGetId();
+        (void)psTraceSetDestination(outFD);
+
+        for (int i=0;i<10;i++) {
+            (void)psTraceSetLevel(".", i);
+            psTraceReset();
+            int lev = psTraceGetLevel(".");
+            ok (lev == PS_UNKNOWN_TRACE_LEVEL,
+                "expected trace level was %d, actual was %d",
+                PS_UNKNOWN_TRACE_LEVEL, lev);
+        }
+
+        (void)psTraceSetLevel(".", 5);
+        (void)psTraceSetLevel(".a", 4);
+        (void)psTraceSetLevel(".a.b", 3);
+        (void)psTraceSetLevel(".a.b.c", 2);
+        ok (!((5 != psTraceGetLevel(".")) ||
+              (4 != psTraceGetLevel(".a")) ||
+              (3 != psTraceGetLevel(".a.b")) ||
+              (2 != psTraceGetLevel(".a.b.c"))),
+            "trace successFlag = false;levels were not settable?");
+
+        psTraceReset();
+        ok (!((PS_UNKNOWN_TRACE_LEVEL != psTraceGetLevel(".")) ||
+              (PS_UNKNOWN_TRACE_LEVEL != psTraceGetLevel(".a")) ||
+              (PS_UNKNOWN_TRACE_LEVEL != psTraceGetLevel(".a.b")) ||
+              (PS_UNKNOWN_TRACE_LEVEL != psTraceGetLevel(".a.b.c"))),
+            "trace levels were not reset properly");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testTrace04()
+    {
+        psMemId id = psMemGetId();
+        int FD = creat("tst_psTrace02_OUT", 0666);
+        for (int nb = 0 ; nb<4;nb++) {
+            if (nb == 0)
+                (void)psTraceSetDestination(((outFD == 2) ? 1 : outFD));
+            if (nb == 1)
+                (void)psTraceSetDestination(((outFD == 2) ? 2 : outFD));
+            if (nb == 2)
+                (void)psTraceSetDestination(((outFD == 2) ? 0 : outFD));
+            if (nb == 3)
+                (void)psTraceSetDestination(FD);
+
+            (void)psTraceSetLevel(".", 4);
+            psTrace(".", 5, "(0) This message should not be displayed (%x)",
+                    0xbeefface);
+            (void)psTraceSetLevel(".", 7);
+            psTrace(".", 5, "(0) This message should be displayed (%x)",
+                    0xbeefface);
+
+            (void)psTraceSetLevel(".a", 4);
+            psTrace(".a", 5, "(1) This message should not be displayed (%x)",
+                    0xbeefface);
+            (void)psTraceSetLevel(".a", 7);
+            psTrace(".a", 5, "(1) This message should be displayed (%x)",
+                    0xbeefface);
+
+            (void)psTraceSetLevel(".a.b", 4);
+            psTrace(".a.b", 5, "(2) This message should not be displayed (%x)",
+                    0xbeefface);
+            (void)psTraceSetLevel(".a.b", 7);
+            psTrace(".a.b", 5, "(2) This message should be displayed (%x)",
+                    0xbeefface);
+            (void)psTraceSetLevel(".a.b.c", 12);
+            psTrace(".a.b.c", 11, "(3) This message should be displayed (%x)",
+                    0xbeefface);
+        }
+        close(FD);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testTrace05()
+    {
+        psMemId id = psMemGetId();
+        (void)psTraceSetDestination(outFD);
+        (void)psTraceSetLevel(".", 9);
+        (void)psTraceSetLevel(".a", 8);
+        (void)psTraceSetLevel(".b", 7);
+        (void)psTraceSetLevel(".c", 5);
+        (void)psTraceSetLevel(".a.a", 4);
+        (void)psTraceSetLevel(".a.b", 3);
+        (void)psTraceSetLevel(".b.a", 2);
+        (void)psTraceSetLevel(".b.b", 1);
+        (void)psTraceSetLevel(".c.a", 0);
+        (void)psTraceSetLevel(".c.b", 3);
+        (void)psTraceSetLevel(".c.c", 5);
+        psTracePrintLevels();
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testTrace05a()
+    {
+        psMemId id = psMemGetId();
+        (void)psTraceSetLevel(".", 9);
+        (void)psTraceSetLevel("a", 8);
+        (void)psTraceSetLevel("b", 7);
+        (void)psTraceSetLevel("c", 5);
+        (void)psTraceSetLevel("a.a", 4);
+        (void)psTraceSetLevel("a.b", 3);
+        (void)psTraceSetLevel("b.a", 2);
+        (void)psTraceSetLevel("b.b", 1);
+        (void)psTraceSetLevel("c.a", 0);
+        (void)psTraceSetLevel("c.b", 3);
+        (void)psTraceSetLevel("c.c", 5);
+        psTracePrintLevels();
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testTrace06()
+    {
+        psMemId id = psMemGetId();
+        (void)psTraceSetDestination(outFD);
+        (void)psTraceSetLevel(".", 9);
+        (void)psTraceSetLevel(".a", 8);
+        (void)psTraceSetLevel(".b", 7);
+        (void)psTraceSetLevel(".c", 5);
+        (void)psTraceSetLevel(".a.a", 4);
+        (void)psTraceSetLevel(".a.b", 3);
+        (void)psTraceSetLevel(".b.a", 2);
+        (void)psTraceSetLevel(".b.b", 1);
+        (void)psTraceSetLevel(".c.a", 0);
+        (void)psTraceSetLevel(".c.b", 3);
+        (void)psTraceSetLevel(".c.c", 5);
+        psTraceReset();
+
+        if ((psTraceGetLevel(".")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".a")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".b")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".c")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".a.a")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".a.b")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".b.a")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".b.b")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".c.a")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".c.b")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".c.c")!=PS_UNKNOWN_TRACE_LEVEL)) {
+            return 1;
+        }
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Ensure that the leading dot in the component names are optional.
+    // testTrace08()
+    {
+        psMemId id = psMemGetId();
+        psTraceReset();
+        (void)psTraceSetLevel(".", 9);
+        (void)psTraceSetLevel(".a", 8);
+        (void)psTraceSetLevel(".b", 7);
+        (void)psTraceSetLevel(".c", 5);
+        (void)psTraceSetLevel(".a.a", 4);
+        (void)psTraceSetLevel(".a.b", 3);
+        (void)psTraceSetLevel(".b.a", 2);
+        (void)psTraceSetLevel(".b.b", 1);
+        (void)psTraceSetLevel(".c.a", 0);
+        (void)psTraceSetLevel(".c.b", 3);
+        (void)psTraceSetLevel(".c.c", 5);
+        psTracePrintLevels();
+        if ((psTraceGetLevel(".")!=9) ||
+            (psTraceGetLevel("a")!=8) ||
+            (psTraceGetLevel("b")!=7) ||
+            (psTraceGetLevel("c")!=5) ||
+            (psTraceGetLevel("a.a")!=4) ||
+            (psTraceGetLevel("a.b")!=3) ||
+            (psTraceGetLevel("b.a")!=2) ||
+            (psTraceGetLevel("b.b")!=1) ||
+            (psTraceGetLevel("c.a")!=0) ||
+            (psTraceGetLevel("c.b")!=3) ||
+            (psTraceGetLevel("c.c")!=5)) {
+            printf("psTraceGetLevel(.) is %d", psTraceGetLevel("."));
+            printf("psTraceGetLevel(a) is %d", psTraceGetLevel("a"));
+            printf("psTraceGetLevel(b) is %d", psTraceGetLevel("b"));
+            printf("psTraceGetLevel(c) is %d", psTraceGetLevel("c"));
+            printf("psTraceGetLevel(a.a) is %d", psTraceGetLevel("a.a"));
+            printf("psTraceGetLevel(a.b) is %d", psTraceGetLevel("a.b"));
+            printf("psTraceGetLevel(b.a) is %d", psTraceGetLevel("b.a"));
+            printf("psTraceGetLevel(b.b) is %d", psTraceGetLevel("b.b"));
+            printf("psTraceGetLevel(c.a) is %d", psTraceGetLevel("c.a"));
+            printf("psTraceGetLevel(c.b) is %d", psTraceGetLevel("c.b"));
+            printf("psTraceGetLevel(c.c) is %d", psTraceGetLevel("c.c"));
+            return 1;
+        }
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+# if (DEBUG)
+    close (outFD);
+# endif
+
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psAbort.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psAbort.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psAbort.c	(revision 22322)
@@ -0,0 +1,76 @@
+/** @file  atst_psAbort_01.c
+ *
+ *  @brief Test driver for psAbort function
+ *
+ *  This test drivers contains the following test points for psAbort
+ *     1) Multiple type values in abort message
+ *
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.1 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-07-13 02:47:01 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testAbort00(void);
+static psS32 testAbort01(void);
+static psS32 testAbort02(void);
+
+testDescription tests[] = {
+                              {testAbort00, 0, "Multiple type values in abort message", -6, false},
+                              {testAbort01, 1, "String values in abort message", -6, false},
+                              {testAbort02, 2, "Empty strings in abort message", -6, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psAbort", tests, argc, argv ) );
+}
+
+static psS32 testAbort00(void)
+{
+    psS32   intval = 1;
+    psS64  longval = 2;
+    float floatval = 3.01;
+    char  charval = 'E';
+    char  *stringval = "E R R O R";
+
+    // Test point #1 Multiple type values placed in the error string
+    psAbort(__func__,
+            "ALL TYPES intval = %d longval = %lld floatval = %f charval = %c strval = %s",
+            intval, longval, floatval, charval, stringval );
+
+    // Program execution should have ended before this statement but if it
+    // does not return a zero since the expected return value of this test
+    // is a non-zero value
+    return 0;
+}
+
+static psS32 testAbort01(void)
+{
+    // Test point #2 String values in abort message
+    psAbort(PS_STRING(__LINE__), "NO_VALUES");
+
+    // Program execution should have ended before this statement but if it
+    // does not return a zero since the expected return value of this test
+    // is a non-zero value
+    return 0;
+}
+
+static psS32 testAbort02(void)
+{
+    // Test point #2 String values in abort message
+    psAbort("","");
+
+    // Program execution should have ended before this statement but if it
+    // does not return a zero since the expected return value of this test
+    // is a non-zero value
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psConfigure.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psConfigure.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psConfigure.c	(revision 22322)
@@ -0,0 +1,52 @@
+/** @file  tst_psConfigure.c
+ *
+ *  @brief Test driver for psconfigure functions
+ *
+ *  This test driver contains the following test points for psConfigure
+ *  functions.
+ *    1) Return current psLib version
+ *
+ *  Return:   Number of test points which failed
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-07-13 02:47:01 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+
+static psS32 psLibVersion00(void);
+
+
+testDescription tests[] = {
+                              {psLibVersion00, 0, "Return current psLib version", 0, false},
+                              {NULL}
+                          };
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    return(!runTestSuite( stderr, "psConfigure", tests, argc, argv));
+}
+
+
+static psS32 psLibVersion00(void)
+{
+    char *stringVal = NULL;
+
+    stringVal = psLibVersion();
+    psLogMsg(__func__,PS_LOG_INFO,"Current psLib version is: %s", stringVal);
+    psFree(stringVal);
+
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psError.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psError.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psError.c	(revision 22322)
@@ -0,0 +1,394 @@
+/** @file  tst_psError.c
+ *
+ *  @brief Test driver for psError function
+ *
+ *  This test driver contains the following test points for psError
+ *     testError00 - psError()                          (Testpoint #486)
+ *     testError01 - psErrorMsg(), psErrorStackPrint()  (Testpoint #725)
+ *     testError02 - psErrorStackPrintV()               (Testpoint #726)
+ *     testError03 - psErrorGet(), psErrorLast()        (Testpoint #727)
+ *     testError04 - psErrorClear()                     (Testpoint #728)
+ *     testError05 - psErrorCodeString()                (Testpoint #729)
+ *
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-02-27 23:56:12 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testError00(void);
+static psS32 testError01(void);
+static psS32 testError02(void);
+static psS32 testError03(void);
+static psS32 testError04(void);
+static psS32 testError05(void);
+static psS32 testErrorRegister(void);
+
+// Function used in testError02 to verify the psErrorStackPrintV function
+static void myErrorStackPrint(FILE *fd,
+                              const char *fmt,
+                              ...)
+{
+    va_list ap;
+
+    // Test whether psErrorStackPrintV() accept a va_list for output variables
+    va_start(ap, fmt);
+    psErrorStackPrintV(fd, fmt, ap);
+    va_end(ap);
+}
+
+testDescription tests[] = {
+                              {testError00, 486, "psError()", 0, false},
+                              {testError01, 725, "psErrorMsg(),psErrorStackPrint()", 0, false},
+                              {testError02, 726, "psErrorStackPrintV()", 0, false},
+                              {testError03, 727, "psErrorGet(),psErrorLast()", 0, false},
+                              {testError04, 728, "psErrorClear()", 0, false},
+                              {testError05, 729, "psErrorCodeString()", 0, false},
+                              {testErrorRegister, 751, "psErrorRegister()", 0, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+//    return ( !runTestSuite(stderr, "psError", tests, argc, argv) );
+    testError03();
+}
+
+static psS32 testError05(void)
+{
+    psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+
+    // Verify the return value of psErrorCodeString
+    psLogMsg("tst_psError05", PS_LOG_INFO, psErrorCodeString(code));
+
+    // Verify the return value of psErrorCodeString if code is negative
+    if( psErrorCodeString(-1) != NULL) {
+        psLogMsg("tst_psError05", PS_LOG_INFO, "Failed error string with neg. code");
+        return 40;
+    }
+
+    return 0;
+}
+
+static psS32 testError04(void)
+{
+    psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+    psErr *last = NULL;
+    psErr *lastAfterClear = NULL;
+
+    // With an attemp error stack call psErrorClear
+    psErrorClear();
+
+    // Get the last error message and verify PS_ERR_NONE (empty stack)
+    lastAfterClear = psErrorLast();
+    if(lastAfterClear->code != PS_ERR_NONE) {
+        psLogMsg("tst_psError05", PS_LOG_ERROR, "psErrorLast did not return expected.");
+        return 30;
+    }
+    psFree(lastAfterClear);
+
+    // Generate three error messages to have messages on error stack
+    if (psError(code, true, "Error code = %d", code) !=  code) {
+        psLogMsg("tst_psError04", PS_LOG_ERROR, "Failed return value verify.");
+        return 31;
+    }
+    if (psError((code+1), false, "Error code = %d", (code+1)) != (code+1)) {
+        psLogMsg("tst_psError04", PS_LOG_ERROR, "Failed return value verify.");
+        return 32;
+    }
+    if (psError((code+2), false, "Error code = %d", (code+2)) != (code+2)) {
+        psLogMsg("tst_psError04", PS_LOG_ERROR, "Failed return value verify.");
+        return 33;
+    }
+
+    // Get the last error message and verify it has the expected code
+    last = psErrorLast();
+    if(last->code != (code+2)) {
+        psLogMsg("tst_psError04", PS_LOG_ERROR, "psErrorLast did not return expected.");
+        return 34;
+    }
+    psFree(last);
+
+    // Clear the error stack
+    psErrorClear();
+
+    // Get the last error message after clear and verify is has PS_ERR_NONE code
+    lastAfterClear = psErrorLast();
+    if(lastAfterClear->code != PS_ERR_NONE) {
+        psLogMsg("tst_psError05", PS_LOG_ERROR, "psErrorLast did not return expected.");
+        return 35;
+    }
+    psFree(lastAfterClear);
+
+    return 0;
+}
+
+static psS32 testError03(void)
+{
+    psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+    psErr *last = NULL;
+    psErr *getErr = NULL;
+
+    // Attempt to get last error message with an empty stack verify psErr with code PS_ERR_NONE
+    last = psErrorLast();
+    if(last->code != PS_ERR_NONE) {
+        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorLast did return PS_ERR_NONE for empty stack");
+        return 20;
+    }
+    psFree(last);
+
+    // Attempt to get specific error message with empty stack verify psErr with code PS_ERR_NONE
+    getErr= psErrorGet(2);
+    if(getErr->code != PS_ERR_NONE) {
+        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorGet did not return PS_ERR_NONE  for empty stack");
+        return 21;
+    }
+    psFree(getErr);
+
+    // Attempt to get error message with invalid index and an empty stack
+    getErr= psErrorGet(-1);
+    if(getErr->code != PS_ERR_NONE) {
+        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorGet with invalid index/empty stack");
+        return 22;
+    }
+    psFree(getErr);
+
+    // Generate three error messages
+    if (psError(code, true, "Error code = %d", code) !=  code) {
+        psLogMsg("tst_psError03", PS_LOG_ERROR, "Failed return value verify.");
+        return 23;
+    }
+    if (psError((code+1), false, "Error code = %d", (code+1)) != (code+1)) {
+        psLogMsg("tst_psError03", PS_LOG_ERROR, "Failed return value verify.");
+        return 24;
+    }
+    if (psError((code+2), false, "Error code = %d", (code+2)) != (code+2)) {
+        psLogMsg("tst_psError03", PS_LOG_ERROR, "Failed return value verify.");
+        return 25;
+    }
+
+    last = psErrorLast();
+    getErr= psErrorGet(0);
+
+    // Check that last and get with 0 index are equal
+    if(last != getErr) {
+        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorGet(0) not equal to psErrorLast");
+        return 26;
+    }
+    psFree(last);
+
+    // Verify the last error message was returned
+    if ( getErr->code != (code+2) ) {
+        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorLast() did not retrieve last error");
+        return 27;
+    }
+    psFree(getErr);
+
+    // Verify the middle error message can be retrieved
+    getErr= psErrorGet(1);
+    if ( getErr->code != (code+1)) {
+        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorGet() did not retrieve proper error");
+        return 28;
+    }
+    psFree(getErr);
+
+    // Verify the psErrorGet returns NULL if an invalid index is given
+    getErr= psErrorGet(-1);
+    if ( getErr->code != PS_ERR_NONE ) {
+        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorGet() did not return PS_ERR_NONE w/ invalid arg");
+        return 29;
+    }
+    psFree(getErr);
+
+    return 0;
+}
+
+static psS32 testError02(void)
+{
+    psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
+
+    // Generate error message and verify return value
+    if (psError(code, true, "Error code = %d", code) != code ) {
+        psLogMsg("tst_psError02", PS_LOG_ERROR, "Failed return value verify.");
+        return 10;
+    }
+    myErrorStackPrint(stderr,"ERROR STACK PRINT Test%dA",2);
+
+    return 0;
+}
+
+static psS32 testError01(void)
+{
+    psErrorCode code=PS_ERR_BAD_PARAMETER_VALUE;
+
+    // Verify the return value of psErrorMsg is the psErrorCode passed
+    if ( psError(code, true, "Error code = %d", code) != code) {
+        psLogMsg("tst_psError01", PS_LOG_ERROR, "Failed return value verify.");
+        return 1;
+    }
+    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1A");
+
+    // test1B empty string in for name argument
+    if ( psError(code+1, true, "Error code = %d", code+1) != code+1) {
+        psLogMsg("tst_psError01", PS_LOG_ERROR, "Failed return with empty string.");
+        return 2;
+    }
+    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1B");
+
+    // test1D undefined code
+    if ( psError(-1, true, "Error code = %d", -1) != -1) {
+        psLogMsg("test_psError01", PS_LOG_ERROR, "Failed return with undefined code.");
+        return 4;
+    }
+    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1D");
+
+    // test1E set psErrorMsg argument to false
+    if( psError(code, false, "Error code = %d", code) != code) {
+        psLogMsg("test_psError01", PS_LOG_ERROR, "Failed return with false new arg.");
+        return 5;
+    }
+    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1E");
+
+    // test1F psErrorMsg with a error code less then PS_ERR_BASE(256)
+    if( psError(9, true, "Errno code = %d", 9) != 9) {
+        psLogMsg("test_psError01", PS_LOG_ERROR, "Failed return with errno code.");
+        return 6;
+    }
+    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1F");
+
+    return 0;
+}
+
+static psS32 testError00(void)
+{
+
+    psS32  intval=1;
+    psS64 longval = 2;
+    float floatval = 3.01;
+    char  charval = 'E';
+    char *stringval = "E R R O R";
+
+    // Test point #1 Multiple type values placed in the error string
+    psError(PS_ERR_UNKNOWN, true,
+            "ALL TYPES intval = %d longval = %lld floatval = %f charval = %c strval = %s",
+            intval,longval,floatval,charval,stringval);
+
+    // Test point #2 String values in error message
+    psError(PS_ERR_UNKNOWN, true, "NO VALUES");
+
+    // Test point #3 Empty strings in error message
+    psError(PS_ERR_UNKNOWN, true, "");
+
+    return 0;
+}
+
+static psS32 testErrorRegister(void)
+{
+
+    psS32 numErr = 4;
+    psErrorDescription errDesc[] = { {PS_ERR_N_ERR_CLASSES+1,"first"},
+                                     {PS_ERR_N_ERR_CLASSES+2,"second"},
+                                     {PS_ERR_N_ERR_CLASSES+3,"third"},
+                                     {PS_ERR_N_ERR_CLASSES+4,"fourth"} };
+    /*
+        1. invoke psErrorRegister with a n>1 array of psErrorDescriptions. Verify that:
+            a. Each error description given is retrievable with psErrorCodeString.
+    */
+    psErrorRegister(errDesc,numErr);
+
+    for (psS32 i = 0; i < numErr; i++) {
+        const char* desc = psErrorCodeString(PS_ERR_N_ERR_CLASSES+1+i);
+        if (desc == NULL) {
+            psLogMsg(__func__,PS_LOG_ERROR,
+                     "psErrorCode didn't find registered error code.");
+            return 1+i*10;
+        }
+        if (strcmp(desc,errDesc[i].description) != 0) {
+            psLogMsg(__func__,PS_LOG_ERROR,
+                     "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.",
+                     desc,errDesc[i].description);
+            return 2+i*10;
+        }
+    }
+
+    /*
+        2. invoke psErrorCodeString with a static/builtin psLib error code. Verify:
+            a. the result is correct.
+    */
+    const char* desc = psErrorCodeString(PS_ERR_N_ERR_CLASSES);
+    if (desc == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psErrorCode didn't find static error code.");
+        return 40;
+    }
+    if (strcmp(desc,"error classes end marker") != 0) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.",
+                 desc,"error classes end marker");
+        return 41;
+    }
+
+    desc = psErrorCodeString(PS_ERR_NONE);
+    if (desc == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psErrorCode didn't find static error code.");
+        return 42;
+    }
+    if (strcmp(desc,"not an error") != 0) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.",
+                 desc,"not an error");
+        return 43;
+    }
+
+    /*
+        3. invoke psErrorCodeString with an invalid code. Verify a NULL is returned.
+    */
+    desc = psErrorCodeString(PS_ERR_N_ERR_CLASSES+numErr+1);
+    if (desc != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psErrorCode didn't return a NULL with a bogus input code.");
+        return 44;
+    }
+
+    /*
+        4. invoke psErrorRegister with a NULL psErrorDescription. Verify that:
+            a. the execution does not cease.
+            b. an appropriate error is generated.
+    */
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    psErrorClear();
+    psErrorRegister(NULL,1);
+    psErr* err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psErrorCode didn't generate proper error code for NULL input.");
+        return 45;
+    }
+    psFree(err);
+
+    /*
+        5. invoke psErrorRegister with nerror=0. Verify that no error occurs.
+    */
+    psErrorClear();
+    psErrorRegister(errDesc,0);
+    err = psErrorLast();
+    if (err->code != PS_ERR_NONE) {
+        psLogMsg(__func__,PS_LOG_ERROR,
+                 "psErrorCode generated an error for nErrors = 0.");
+        return 46;
+    }
+    psFree(err);
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psLine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psLine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psLine.c	(revision 22322)
@@ -0,0 +1,142 @@
+/** @file  tst_psLine.c
+ *
+ *  @brief Test driver for psLine functions
+ *
+ *  @author  dRob, MHPCC
+ *
+ *  @version $Revision: 1.1 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-07-14 02:26:25 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testLineAlloc(void);
+static psS32 testLineInit(void);
+static psS32 testLineAdd(void);
+static psS32 testLineChk(void);
+
+testDescription tests[] = {
+                              {testLineAlloc, 0, "Verify Line Alloc/Free", 0, false},
+                              {testLineInit, 1, "Verify Line Init", 0, false},
+                              {testLineAdd, 2, "Verify Line Add", 0, false},
+                              {testLineChk, 3, "Verify Line MemCheck", 0, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psLine", tests, argc, argv ) );
+}
+
+psS32 testLineAlloc(void)
+{
+    psLine *lineline = NULL;
+    lineline = psLineAlloc(20);
+
+    if (lineline->NLINE != 20) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psLine set NLINE parameter incorrectly during Allocation!\n");
+        return 2;
+    }
+    if (lineline->Nline != 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psLine set Nline parameter incorrectly during Allocation!\n");
+        return 2;
+    }
+    strncpy(lineline->line, "Hello World", 20);
+    if (strncmp(lineline->line, "Hello World", 20)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psLine was unable to store a simple string!\n");
+        return 3;
+    }
+
+    psFree(lineline);
+
+    return 0;
+}
+
+psS32 testLineInit(void)
+{
+    psLine *line = NULL;
+    //Return false for NULL input
+    if (psLineInit(line) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psLineInit failed.  Expected false for NULL psLine input.\n");
+        return 1;
+    }
+    //Allocate a line and return true on Init
+    line = psLineAlloc(1);
+    if(!psLineInit(line) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psLineInit failed.  Expected true for valid psLine input.\n");
+        return 2;
+    }
+    if (line->NLINE != 1 || line->Nline != 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psLineInit failed to return the correct line parameters.\n");
+        return 3;
+    }
+
+    psFree(line);
+
+    return 0;
+}
+
+psS32 testLineAdd(void)
+{
+    psLine *line = NULL;
+    //Return false for NULL input
+    if (psLineAdd(line, "Hello World") ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psLineAdd failed.  Expected false for NULL psLine input.\n");
+        return 1;
+    }
+    //Allocate and return true for valid input.
+    line = psLineAlloc(20);
+    if (!psLineAdd(line, "Hello %s", "World") ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psLineAdd failed.  Expected true for valid psLine input.\n");
+        return 2;
+    }
+    if (line->NLINE != 20 || line->Nline != 11) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psLineAdd failed to return the correct line parameters.\n");
+        return 3;
+    }
+    if (strncmp(line->line, "Hello World", 20)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psLineAdd failed to store the correct line string.\n");
+        printf("\n  %s\n", line->line);
+        return 4;
+    }
+
+    psFree(line);
+
+    return 0;
+}
+
+psS32 testLineChk(void)
+{
+    psLine *line = NULL;
+    //Return false for Null input line
+    if (psMemCheckLine(line)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psMemCheckLine failed to return false for NULL line input.\n");
+        return 1;
+    }
+    line = psLineAlloc(1);
+    if (!psMemCheckLine(line)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psMemCheckLine failed to return true for valid line input.\n");
+        return 2;
+    }
+    psFree(line);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psLogMsg.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psLogMsg.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psLogMsg.c	(revision 22322)
@@ -0,0 +1,308 @@
+/*****************************************************************************
+    This code will test whether trace levels can be set successfully.
+    This code will test whether trace messages can be displayed with printf
+    style string.
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include <unistd.h>
+#include <fcntl.h>
+
+static psS32 testLogMsg00();
+static psS32 testLogMsg01();
+static psS32 testLogMsg02();
+static psS32 testLogMsg03();
+static psS32 testLogMsg04();
+static psS32 testLogMsg05();
+static psS32 testLogMsg06();
+
+testDescription tests[] = {
+                              {
+                                  testLogMsg00, 0, "default log levels, printf-style strings", 0, false
+                              },
+                              {
+                                  testLogMsg01, 1, "default log levels, psVLogMsg()", 0, false
+                              },
+                              {
+                                  testLogMsg02, 2, "psLogSet/GetLevel()", 0, false
+                              },
+                              {
+                                  testLogMsg03, 3, "psLogSetFormat()", 0, false
+                              },
+                              {
+                                  testLogMsg04, 4, "Output Format", 0, false
+                              },
+                              {
+                                  testLogMsg05, 5, "psLogSet/GetDestination()", 0, false
+                              },
+                              {
+                                  testLogMsg06, 6, "psMessageDestination()", 0, false
+                              },
+                              {
+                                  NULL
+                              }
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psLogMsg", tests, argc, argv ) );
+}
+
+
+static void myLogMsg(const char *name,
+                     psS32 level,
+                     const char *fmt,
+                     ...)
+{
+    va_list ap;
+
+    // Test whether psLogMsgV() accept a va_list for output variables.
+    va_start(ap, fmt);
+    psLogMsgV(name, level, fmt, ap);
+    va_end(ap);
+}
+
+static psS32 testLogMsg00()
+{
+    psS32 i = 0;
+
+    // Send a log messages for levels 0:9.  Only the first four messages
+    // should actually be displayed.
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d %f %s\n", i,
+                 (float) i, "beep beep");
+    }
+
+    return 0;
+}
+
+static psS32 testLogMsg01()
+{
+    psS32 i = 0;
+
+    // Send a log messages for levels 0:9.  Only the first four messages
+    // should actually be displayed.
+    for (i=0;i<10;i++) {
+        myLogMsg(__func__, i, "Hello World!  My level is %d %f %s\n", i,
+                 (float) i, "beep beep");
+    }
+
+    return 0;
+}
+
+static psS32 testLogMsg02()
+{
+
+    psLogSetLevel(9);
+    // Send a log messages for levels 0:9.
+    for (psS32 i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    psLogSetLevel(5);
+    psLogMsg(__func__, 6, "This should not be displayed (level %d)\n", 6);
+    psLogSetLevel(4);
+    psLogMsg(__func__, 4, "This should  be displayed (level %d)\n", 4);
+    psLogMsg(__func__, 4, "This should display level 4 logging -> level %d\n", psLogGetLevel() );
+
+    return 0;
+}
+
+static psS32 testLogMsg03()
+{
+    psS32 i;
+
+    fprintf(stderr,"------------- psLogSetFormat() -------------\n");
+    psLogSetFormat("");
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    fprintf(stderr,"------------- psLogSetFormat(NULL) -------------\n");
+    psLogSetFormat(NULL);
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    fprintf(stderr,"------------- psLogSetFormat(T) -------------\n");
+    psLogSetFormat("T");
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    fprintf(stderr,"------------- psLogSetFormat(H) -------------\n");
+    psLogSetFormat("H");
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    fprintf(stderr,"------------- psLogSetFormat(L) -------------\n");
+    psLogSetFormat("L");
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    fprintf(stderr,"------------- psLogSetFormat(N) -------------\n");
+    psLogSetFormat("N");
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    fprintf(stderr,"------------- psLogSetFormat(M) -------------\n");
+    psLogSetFormat("M");
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    fprintf(stderr,"------------- psLogSetFormat(THLNM) -------------\n");
+    psLogSetFormat("THLNM");
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    return 0;
+}
+
+
+psS32 testLogMsg04()
+{
+    psLogMsg("Under 15 chars", 0, "Hello World!\n");
+    psLogMsg("This string is more than 15 chars", 0, "Hello World!\n");
+    psLogMsg(__func__, 0, "Line #1\n");
+    psLogMsg(__func__, 0, "Line #2\n");
+    psLogMsg(__func__, 0, "Line #3");
+    psLogMsg(__func__, 0, "Line #4");
+
+    return 0;
+}
+
+psS32 testLogMsg05()
+{
+    psS32 i = 0;
+    //    FILE* file;
+    int fd;
+    //    char line[256];
+
+    printf("--------------- psLogSetDestination(PS_LOG_NONE) ----------------\n");
+    //    psLogSetDestination("none");
+    psLogSetDestination(0);
+    printf("    File Descriptor = %d \n", psLogGetDestination() );
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    printf("------------- psLogSetDestination(PS_LOG_TO_STDERR) -------------\n");
+    //    psLogSetDestination("dest:stderr");
+    psLogSetDestination(2);
+    printf("    File Descriptor = %d \n", psLogGetDestination() );
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    printf("------------- psLogSetDestination(PS_LOG_TO_STDOUT) -------------\n");
+    //    psLogSetDestination("dest:stdout");
+    psLogSetDestination(1);
+    printf("    File Descriptor = %d \n", psLogGetDestination() );
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    printf("--------------- psLogSetDestination(""file:log.txt"") ---------------\n");
+    fd = creat("log.txt", 0666);
+    //    psLogSetDestination("file:log.txt");
+    psLogSetDestination(fd);
+    printf("    File Descriptor = %d \n", psLogGetDestination() );
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+
+    //    psLogSetDestination("none");
+    FILE *file;
+    char line[257];
+    //    psLogSetDestination(-1);
+    psLogSetDestination(0);
+    printf("--------------------- The Contents of log.txt -------------------\n");
+    file = fopen("log.txt","r");
+    while ( fgets(line,256,file) != NULL ) {
+        printf("%s",line);
+    }
+    fclose(file);
+    close(fd);
+
+    int fd2 = creat("eva/log.txt", 0666);
+    printf("--------------- psLogSetDestination(""file:eva/log.txt"") ----------\n");
+    //    psLogSetDestination("file:/eva/log.txt");
+    psLogSetDestination(fd2);
+    for ( i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World! My level is %d\n", i);
+    }
+    close(fd2);
+
+    return 0;
+}
+
+psS32 testLogMsg06()
+{
+    psS32 i = 0;
+    //    FILE* file;
+    int fd;
+    //    char line[256];
+
+    printf("--------------- psMessageDestination(PS_LOG_NONE) ----------------\n");
+    psMessageDestination("none");
+    //    psLogSetDestination(0);
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+    printf("------------- psMessageDestination(PS_LOG_TO_STDERR) -------------\n");
+    psMessageDestination("stderr");
+    //    psLogSetDestination(2);
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d - stderr\n", i);
+    }
+
+    printf("------------- psMessageDestination(PS_LOG_TO_STDOUT) -------------\n");
+    psMessageDestination("stdout");
+    //    psLogSetDestination(1);
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d - stdout\n", i);
+    }
+
+    printf("--------------- psMessageDestination(""file:log2.txt"") ---------------\n");
+    fd = creat("log2.txt", 0666);
+    psMessageDestination("file:log2.txt");
+    //    psLogSetDestination(fd);
+    for (i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World!  My level is %d\n", i);
+    }
+
+
+    psMessageDestination("none");
+    FILE *file;
+    char line[257];
+    //    psMessageDestination(-1);
+    //    psLogSetDestination(0);
+    printf("--------------------- The Contents of log2.txt -------------------\n");
+    file = fopen("log2.txt","r");
+    while ( fgets(line,256,file) != NULL ) {
+        printf("%s",line);
+    }
+    fclose(file);
+    close(fd);
+
+    int fd2 = creat("eva/log2.txt", 0666);
+    printf("--------------- psMessageDestination(""file:eva/log.txt"") ----------\n");
+    psMessageDestination("file:eva/log2.txt");
+    //    psLogSetDestination(fd2);
+    for ( i=0;i<10;i++) {
+        psLogMsg(__func__, i, "Hello World! My level is %d\n", i);
+    }
+    close(fd2);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psMemory.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psMemory.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psMemory.c	(revision 22322)
@@ -0,0 +1,827 @@
+/** @file  tst_psMemory.c
+*
+*  @brief Contains the tests for psMemory.[ch]
+*
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-04-04 19:52:55 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdlib.h>
+
+
+#include "psTest.h"
+#include "pslib.h" // need to allow malloc for callback use
+
+static psS32 TPFreeReferencedMemory( void );
+static psS32 TPOutOfMemory( void );
+static psS32 TPReallocOutOfMemory( void );
+static psPtr TPOutOfMemoryExhaustedCallback( size_t size );
+static psS32 TPCheckBufferPositive( void );
+static psS32 TPrealloc( void );
+static psS32 TPallocCallback( void );
+static psMemId memAllocCallback( const psMemBlock *ptr );
+static psMemId memFreeCallback( const psMemBlock *ptr );
+static psS32 TPcheckLeaks( void );
+static psS32 TPmemCorruption( void );
+static psS32 TPmultipleFree( void );
+static psS32 memCheckTypes( void );
+void memProblemCallback( psMemBlock *ptr, const char *filename, unsigned int lineno );
+
+static psS32 problemCallbackCalled = 0;
+static psS32 allocCallbackCalled = 0;
+static psS32 freeCallbackCalled = 0;
+static psS32 exhaustedCallbackCalled = 0;
+
+testDescription tests[] = {
+                              {TPCheckBufferPositive, 449, "checkBufferPositive", 0, false},
+                              {TPOutOfMemory, 450, "outOfMemory", -6, false},
+                              {TPReallocOutOfMemory, 562, "reallocOutOfMemory", -6, false},
+                              {TPrealloc, 451, "psRealloc", 0, false},
+                              {TPallocCallback, 452, "allocCallback", 0, false},
+                              {TPallocCallback, 453, "allocCallback2", 0, true},
+                              {TPcheckLeaks, 454, "checkLeaks", 0, false},
+                              {TPmemCorruption, 455, "psMemCorruption", 0, false},
+                              {TPFreeReferencedMemory, 456, "freeReferencedMemory", 0, false},
+                              {TPmultipleFree, 699, "multipleFree", -6, false},
+                              {memCheckTypes, 700, "psMemCheckType", 0, false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psMemory", tests, argc, argv ) );
+}
+
+// Testpoint #449, psAlloc shall allocate memory blocks writeable by caller.
+psS32 TPCheckBufferPositive( void )
+{
+    psS32 * mem;
+    const psS32 size = 100;
+    psS32 failed = 0;
+
+    psLogMsg( __func__, PS_LOG_INFO, "psAlloc shall allocate memory blocks writeable by caller.\n" );
+
+    mem = ( psS32* ) psAlloc( size * sizeof( psS32 ) );
+    if ( mem == NULL ) {
+        psError(PS_ERR_UNKNOWN, true, "psAlloc returned a NULL value in %s!", __func__ );
+        return 1;
+    }
+
+    for ( psS32 index = 0;index < size;index++ ) {
+        mem[ index ] = index;
+    }
+
+    for ( psS32 index = 0;index < size;index++ ) {
+        if ( mem[ index ] != index ) {
+            failed++;
+        }
+    }
+
+    psFree( mem );
+
+    return failed;
+}
+
+psS32 TPFreeReferencedMemory( void )
+{
+    // create memory
+    psS32 * mem;
+    psS32 ref = 0;
+
+    psLogMsg( __func__, PS_LOG_INFO, "memory reference count shall be incrementable/decrementable" );
+
+    mem = ( psS32* ) psAlloc( 100 * sizeof( psS32 ) );
+
+    ref = psMemGetRefCounter( mem );
+    if ( ref != 1 ) {
+        psError(PS_ERR_UNKNOWN, true, "Expected to buffer reference count to be initially 1, but it was %d.", ref );
+        return 1;
+    }
+
+    psMemIncrRefCounter( mem );
+    psMemIncrRefCounter( mem );
+    psMemIncrRefCounter( mem );
+
+    ref = psMemGetRefCounter( mem );
+    if ( ref != 4 ) {
+        psError(PS_ERR_UNKNOWN, true, "Expected to find buffer reference count to be 4, but it was %d.", ref );
+        return 1;
+    }
+
+    psMemDecrRefCounter( mem );
+    psMemDecrRefCounter( mem );
+
+    ref = psMemGetRefCounter( mem );
+    if ( ref != 2 ) {
+        psError(PS_ERR_UNKNOWN, true, "Expected to find buffer reference count to be 2, but it was %d.", ref );
+        return 1;
+    }
+
+    psLogMsg( __func__, PS_LOG_INFO, "psFree shall be just decrement a multiple refererenced pointer." );
+
+    psMemDecrRefCounter( mem );
+
+    ref = psMemGetRefCounter( mem );
+    if ( ref != 1 ) {
+        psError(PS_ERR_UNKNOWN, true, "Expected to find buffer reference count to be 1, but it was %d.", ref );
+        return 1;
+    }
+
+    psFree( mem );
+
+    return 0;
+}
+
+// Bug/Task #562 regression test.  Upon requesting more memory than is available, psRealloc shall call
+// the psMemExhaustedCallback.
+psS32 TPReallocOutOfMemory( void )
+{
+    psS32 * mem[ 100 ];
+    psMemExhaustedCallback cb;
+
+    for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+        mem[ lcv ] = NULL;
+    }
+
+    psLogMsg( __func__, PS_LOG_INFO, "Upon requesting more memory than is available, psRealloc shall call "
+              "the psMemExhaustedCallback.\n" );
+
+    exhaustedCallbackCalled = 0;
+
+    cb = psMemExhaustedCallbackSet( TPOutOfMemoryExhaustedCallback );
+
+    for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+        mem[ lcv ] = ( psS32* ) psAlloc( 10 );
+    }
+
+    for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+        mem[ lcv ] = ( psS32* ) psRealloc( mem[ lcv ], SIZE_MAX/2 - 1000 );
+    }
+
+    psMemExhaustedCallbackSet( cb );
+
+    if ( exhaustedCallbackCalled == 0 ) {
+        psError(PS_ERR_UNKNOWN,true, "Called psRealloc with HUGE memory requirement and survived in %s!", __func__ );
+        return 1;
+    }
+
+    for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+        psFree( mem[ lcv ] );
+    }
+
+    return 0;
+}
+// Testpoint #450,  Upon requesting more memory than is available, psalloc shall call
+// the psMemExhaustedCallback.
+psS32 TPOutOfMemory( void )
+{
+    psS32 * mem[ 100 ];
+    psMemExhaustedCallback cb;
+
+    for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+        mem[ lcv ] = NULL;
+    }
+
+    psLogMsg( __func__, PS_LOG_INFO, "Upon requesting more memory than is available, psalloc shall call "
+              "the psMemExhaustedCallback.\n" );
+
+    exhaustedCallbackCalled = 0;
+
+    cb = psMemExhaustedCallbackSet( TPOutOfMemoryExhaustedCallback );
+
+    for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+        mem[ lcv ] = ( psS32* ) psAlloc( SIZE_MAX/2 - 1000 );
+    }
+
+    psMemExhaustedCallbackSet( cb );
+
+    if ( exhaustedCallbackCalled == 0 ) {
+        psError(PS_ERR_UNKNOWN,true, "Called psAlloc with HUGE memory requirement and survived in %s!", __func__ );
+        return 1;
+    }
+
+    for ( psS32 lcv = 0; lcv < 100; lcv++ ) {
+        psFree( mem[ lcv ] );
+    }
+
+    return 0;
+}
+
+// Testpoint #451,  psRealloc shall increase/decrease memory buffer while preserving contents
+psS32 TPrealloc( void )
+{
+    psS32 * mem1;
+    psS32* mem2;
+    psS32* mem3;
+    const psS32 initialSize = 100;
+
+    psLogMsg( __func__, PS_LOG_INFO, "psRealloc shall increase/decrease memory buffer while "
+              "preserving contents" );
+
+    // allocate buffer with known values.
+    mem1 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+    mem2 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+    mem3 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+    for ( psS32 lcv = 0;lcv < initialSize;lcv++ ) {
+        mem1[ lcv ] = mem2[ lcv ] = mem3[ lcv ] = lcv;
+    }
+
+    psMemCheckCorruption( 1 );
+    psLogMsg( __func__, PS_LOG_INFO, "Expanding memory buffer." );
+
+    // realloc to 2x
+    mem1 = ( psS32* ) psRealloc( mem1, 2 * initialSize * sizeof( psS32 ) );
+    mem2 = ( psS32* ) psRealloc( mem2, 2 * initialSize * sizeof( psS32 ) );
+    mem3 = ( psS32* ) psRealloc( mem3, 2 * initialSize * sizeof( psS32 ) );
+
+    // check values of initial block
+    for ( psS32 i = 0;i < initialSize;i++ ) {
+        if ( mem1[ i ] != i || mem2[ i ] != i || mem3[ i ] != i ) {
+            psError(PS_ERR_UNKNOWN,true, "Realloc didn't preserve the contents with expanding buffer in %s.",
+                    __func__ );
+            break;
+        }
+    }
+
+    psMemCheckCorruption( 1 );
+    psLogMsg( __func__, PS_LOG_INFO, "Shrinking memory buffer." );
+
+    // realloc to 1/2 initial value.
+    mem1 = ( psS32* ) psRealloc( mem1, ( initialSize / 2 ) * sizeof( psS32 ) );
+    mem2 = ( psS32* ) psRealloc( mem2, ( initialSize / 2 ) * sizeof( psS32 ) );
+    mem3 = ( psS32* ) psRealloc( mem3, ( initialSize / 2 ) * sizeof( psS32 ) );
+
+    // check values of initial block
+    for ( psS32 i = 0;i < initialSize / 2;i++ ) {
+        if ( mem1[ i ] != i || mem2[ i ] != i || mem3[ i ] != i ) {
+            psError(PS_ERR_UNKNOWN,true, "Realloc didn't preserve the contents with shrinking buffer in %s.",
+                    __func__ );
+            break;
+        }
+    }
+
+    psFree( mem1 );
+    psFree( mem2 );
+    psFree( mem3 );
+
+    return 0;
+}
+
+psS32 TPallocCallback( void )
+{
+    psS32 * mem1;
+    psS32* mem2;
+    psS32* mem3;
+    psS32 currentId = psMemGetId();
+    const psS32 initialSize = 100;
+    psS32 mark;
+
+    allocCallbackCalled = 0;
+    freeCallbackCalled = 0;
+    psMemAllocCallbackSet( memAllocCallback );
+    psMemFreeCallbackSet( memFreeCallback );
+
+    psMemAllocCallbackSetID( currentId + 1 );
+    psMemFreeCallbackSetID( currentId + 1 );
+
+    psLogMsg( __func__, PS_LOG_INFO, "call to psAlloc/psRealloc shall generate a callback if specified "
+              "memory ID is allocated." );
+
+    // allocate buffer with known values.
+    mem1 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+    mem2 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+    mem3 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+
+    psFree( mem1 );
+    psFree( mem2 );
+    psFree( mem3 );
+
+    if ( allocCallbackCalled != 2 || freeCallbackCalled != 2 ) {
+        psError(PS_ERR_UNKNOWN,true, "alloc/free callbacks were not called the proper number of times in %s",
+                __func__ );
+        return 1;
+    }
+
+    allocCallbackCalled = 0;
+    freeCallbackCalled = 0;
+
+    mark = psMemGetId();
+
+    mem1 = ( psS32* ) psAlloc( initialSize * sizeof( psS32 ) );
+
+    psMemAllocCallbackSetID( mark );
+
+    mem1 = ( psS32* ) psRealloc( mem1, initialSize * 2 * sizeof( psS32 ) );
+
+    psFree( mem1 );
+
+    if ( allocCallbackCalled != 2 ) {
+        psError(PS_ERR_UNKNOWN,true, "realloc callbacks were not called the proper number of times in %s",
+                __func__ );
+        return 1;
+    }
+
+    return 0;
+
+}
+
+psS32 TPcheckLeaks( void )
+{
+    const psS32 numBuffers = 5;
+    psS32* buffers[ 5 ];
+    psS32 lcv;
+    psS32 currentId = psMemGetId();
+    psMemBlock** blks;
+    psS32 nLeaks = 0;
+    psS32 lineMark = 0;
+
+    psLogMsg( __func__, PS_LOG_INFO, "psMemCheckLeaks shall return the number of blocks above an ID "
+              "that are still allocated" );
+
+    for ( lcv = 0;lcv < numBuffers;lcv++ ) {
+        lineMark = __LINE__ + 1;
+        buffers[ lcv ] = psAlloc( sizeof( psS32 ) );
+    }
+
+    for ( lcv = 1;lcv < numBuffers;lcv++ ) {
+        psFree( buffers[ lcv ] );
+    }
+
+    psLogMsg( __func__, PS_LOG_INFO, "following psMemCheckLeaks call should produce one instance." );
+
+    nLeaks = psMemCheckLeaks( currentId, &blks, stderr, false );
+
+    if ( nLeaks != 1 ) {
+        psError(PS_ERR_UNKNOWN,true, "psMemCheckLeaks should have found 1 leak, but found %d in %s.", nLeaks, __func__ );
+        return 1;
+    }
+
+    if ( blks[ 0 ] ->lineno != lineMark ) {
+        psError(PS_ERR_UNKNOWN,true, "psMemCheckLeaks found a leak other than the expected one (line %d vs %d) in %s.",
+                lineMark, blks[ 0 ] ->lineno, __func__ );
+        return 1;
+    }
+
+    psFree( buffers[ 0 ] );
+    psFree( blks );
+
+    psLogMsg( __func__, PS_LOG_INFO, "Testing psMemCheckLeaks again with a different leak location" );
+    psMemCheckLeaks(currentId,NULL,stderr, false);
+
+    for ( lcv = 0;lcv < numBuffers;lcv++ ) {
+        lineMark = __LINE__ + 1;
+        buffers[ lcv ] = psAlloc( sizeof( psS32 ) );
+    }
+
+    for ( lcv = 0;lcv < numBuffers - 1;lcv++ ) {
+        psFree( buffers[ lcv ] );
+    }
+
+    psLogMsg( __func__, PS_LOG_INFO, "following psMemCheckLeaks call should produce one error." );
+
+    nLeaks = psMemCheckLeaks( currentId, &blks, stderr, false );
+
+    if ( nLeaks != 1 ) {
+        psError(PS_ERR_UNKNOWN,true, "psMemCheckLeaks should have found 1 leak, but found %d in %s.", nLeaks, __func__ );
+        return 1;
+    }
+
+    if ( blks[ 0 ] ->lineno != lineMark ) {
+        psError(PS_ERR_UNKNOWN,true, "psMemCheckLeaks found a leak other than the expected one in %s.", __func__ );
+        return 1;
+    }
+
+    psFree( buffers[ 4 ] );
+    psFree( blks );
+
+    psLogMsg( __func__, PS_LOG_INFO, "Testing psMemCheckLeaks again with multiple leak locations." );
+
+    for ( lcv = 0;lcv < numBuffers;lcv++ ) {
+        lineMark = __LINE__ + 1;
+        buffers[ lcv ] = psAlloc( sizeof( psS32 ) );
+    }
+
+    for ( lcv = 0;lcv < numBuffers;lcv++ ) {
+        if ( lcv % 2 == 0 ) {
+            psFree( buffers[ lcv ] );
+        }
+    }
+
+    psLogMsg( __func__, PS_LOG_INFO, "following psMemCheckLeaks call should produce two errors." );
+
+    nLeaks = psMemCheckLeaks( currentId, &blks, stderr, false );
+
+    if ( nLeaks != 2 ) {
+        psError(PS_ERR_UNKNOWN,true, "psMemCheckLeaks should have found 1 leak, but found %d in %s.", nLeaks, __func__ );
+        return 1;
+    }
+
+    if ( blks[ 0 ] ->lineno != lineMark ) {
+        psError(PS_ERR_UNKNOWN,true, "psMemCheckLeaks found a leak other than the expected one in %s.", __func__ );
+        return 1;
+    }
+
+    psFree( blks );
+    psFree( buffers[ 1 ] );
+    psFree( buffers[ 3 ] );
+
+    return 0;
+}
+
+psS32 TPmemCorruption( void )
+{
+    psS32 * buffer = NULL;
+    psS32 oldValue = 0;
+    psS32 corruptions = 0;
+    psMemProblemCallback cb;
+
+    psLogMsg( __func__, PS_LOG_INFO, "psMemCheckCorruption shall detect memory corruptions" );
+
+    buffer = psAlloc( sizeof( psS32 ) );
+
+    // cause memory corruption via buffer underflow
+    *buffer = 1;
+    buffer--;
+    oldValue = *buffer;
+    *buffer = 2;
+
+    problemCallbackCalled = 0;
+    cb = psMemProblemCallbackSet( memProblemCallback );
+
+    psLogMsg( __func__, PS_LOG_INFO, "psMemCheckCorruption should output an error message and "
+              "memProblemCallback callback should be called." );
+
+    corruptions = psMemCheckCorruption( 0 );
+
+    // restore the memory problem callback
+    psMemProblemCallbackSet( cb );
+
+    // restore the value, 'uncorrupting' the buffer
+    *buffer = oldValue;
+    buffer++;
+
+    psFree( buffer );
+
+    if ( corruptions != 1 ) {
+        psError(PS_ERR_UNKNOWN,true, "Expected one memory corruption but found %d in %s.",
+                corruptions, __func__ );
+        return 1;
+    }
+
+    if ( problemCallbackCalled != 1 ) {
+        psError(PS_ERR_UNKNOWN,true, "The memProblemCallback was not invoked but should have been in %s",
+                __func__ );
+        return 1;
+    }
+
+    return 0;
+
+}
+
+void memProblemCallback( psMemBlock *ptr, const char *file, unsigned int lineno )
+{
+    psLogMsg( __func__, PS_LOG_INFO, "memory callback called for id %lu (%s:%d).",
+              ptr->id, file, lineno );
+    problemCallbackCalled++;
+    return ;
+}
+
+psMemId memAllocCallback( const psMemBlock *ptr )
+{
+    psLogMsg( __func__, PS_LOG_INFO, "block %lu was (re)allocated", ptr->id );
+    allocCallbackCalled++;
+    return 1;
+}
+
+psMemId memFreeCallback( const psMemBlock *ptr )
+{
+    psLogMsg( __func__, PS_LOG_INFO, "block %lu was freed", ptr->id );
+    freeCallbackCalled++;
+    return 1;
+}
+
+psPtr TPOutOfMemoryExhaustedCallback( size_t size )
+{
+    psLogMsg( __func__, PS_LOG_INFO, "Custom MemExhaustedCallback was invoked." );
+    exhaustedCallbackCalled++;
+    return NULL;
+}
+
+psS32 TPmultipleFree( void )
+{
+
+    psPtr buffer = psAlloc( 1024 );
+    psPtr buffer2 = buffer;
+
+    psFree( buffer );
+
+    psLogMsg( __func__, PS_LOG_INFO, "Next should abort due to multiple freeing." );
+    psFree( buffer2 );
+
+    psError(PS_ERR_UNKNOWN,true,
+            "Multiple psFree call survived");
+
+    return 0;
+}
+
+static psS32 memCheckTypes( void )
+{
+    psArray *negative;
+    negative = psArrayAlloc(2);
+    psMetadata *neg;
+    neg = psMetadataAlloc();
+
+    psArray *array;
+    array = psArrayAlloc(100);
+    if ( !psMemCheckType(PS_DATA_ARRAY, array) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckArray failed in memCheckType. \n");
+        psFree(array);
+        return 1;
+    }
+    psLogMsg( __func__, PS_LOG_INFO, "Next should error from invalid datatype." );
+    if ( !psMemCheckType(PS_DATA_ARRAY, neg) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckArray failed in memCheckType. \n");
+    }
+    psFree(array);
+
+    psBitSet *bits;
+    bits = psBitSetAlloc(100);
+    if ( !psMemCheckType(PS_DATA_BITSET, bits) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckBitSet failed in memCheckType. \n");
+        psFree(bits);
+        return 1;
+    }
+    psLogMsg( __func__, PS_LOG_INFO, "Next should error from invalid datatype." );
+    if ( !psMemCheckType(PS_DATA_BITSET, negative) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckBitSet failed in memCheckType. \n");
+    }
+    psFree(bits);
+
+    psCube *cube;
+    cube = psCubeAlloc();
+    if ( !psMemCheckType(PS_DATA_CUBE, cube) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckCube failed in memCheckType. \n");
+        psFree(cube);
+        return 1;
+    }
+    psFree(cube);
+
+    psFits *fits;
+    fits = psFitsOpen("test.fits","w");
+    psImage* img = psImageAlloc(16,16,PS_TYPE_F32);
+    psFitsWriteImage(fits,NULL,img,1,NULL);
+    psFree(img);
+    if ( !psMemCheckType(PS_DATA_FITS, fits) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckFits failed in memCheckType. \n");
+        psFree(fits);
+        return 1;
+    }
+    psFitsClose(fits);
+
+    psHash *hash;
+    hash = psHashAlloc(100);
+    if ( !psMemCheckType(PS_DATA_HASH, hash) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckHash failed in memCheckType. \n");
+        psFree(hash);
+        return 1;
+    }
+    psFree(hash);
+
+    psHistogram *histogram;
+    histogram = psHistogramAlloc(1.1, 2.2, 2);
+    if ( !psMemCheckType(PS_DATA_HISTOGRAM, histogram) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckHistogram failed in memCheckType. \n");
+        psFree(histogram);
+        return 1;
+    }
+    psFree(histogram);
+
+    psImage *image;
+    image = psImageAlloc(5, 5, PS_TYPE_F32);
+    if ( !psMemCheckType(PS_DATA_IMAGE, image) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckImage failed in memCheckType. \n");
+        psFree(image);
+        return 1;
+    }
+    psFree(image);
+
+    psKernel *kernel;
+    kernel = psKernelAlloc(0, 1, 0, 1);
+    if ( !psMemCheckType(PS_DATA_KERNEL, kernel) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckKernel failed in memCheckType. \n");
+        psFree(kernel);
+        return 1;
+    }
+    psFree(kernel);
+
+    psList *list;
+    list = psListAlloc(NULL);
+    if ( !psMemCheckType(PS_DATA_LIST, list) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckList failed in memCheckType. \n");
+        psFree(list);
+        return 1;
+    }
+    psFree(list);
+
+    psLookupTable *lookup;
+    char *file = "tableF32.dat";
+    char *format = "\%f \%lf \%d \%ld";
+    lookup = psLookupTableAlloc(file, format, 10);
+    if ( !psMemCheckType(PS_DATA_LOOKUPTABLE, lookup) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckLookupTable failed in memCheckType. \n");
+        psFree(lookup);
+        return 1;
+    }
+    psFree(lookup);
+
+    psMetadata *metadata;
+    metadata = psMetadataAlloc();
+    if ( !psMemCheckType(PS_DATA_METADATA, metadata) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckMetadata failed in memCheckType. \n");
+        psFree(metadata);
+        return 1;
+    }
+    psFree(metadata);
+
+    psMetadataItem *metaItem;
+    metaItem = psMetadataItemAlloc("name", PS_DATA_S32, "COMMENT", 1);
+    if ( !psMemCheckType(PS_DATA_METADATAITEM, metaItem) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckMetadataItem failed in memCheckType. \n");
+        psFree(metaItem);
+        return 1;
+    }
+    psFree(metaItem);
+
+    psMinimization *min;
+    min = psMinimizationAlloc(3, 0.1);
+    if ( !psMemCheckType(PS_DATA_MINIMIZATION, min) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckMinimization failed in memCheckType. \n");
+        psFree(min);
+        return 1;
+    }
+    psFree(min);
+
+    psPixels *pixels;
+    pixels = psPixelsAlloc(100);
+    if ( !psMemCheckType(PS_DATA_PIXELS, pixels) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckPixels failed in memCheckType. \n");
+        psFree(pixels);
+        return 1;
+    }
+    psFree(pixels);
+
+    psPlane *plane;
+    plane = psPlaneAlloc();
+    if ( !psMemCheckType(PS_DATA_PLANE, plane) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckPlane failed in memCheckType. \n");
+        psFree(plane);
+        return 1;
+    }
+    psFree(plane);
+
+    psPlaneDistort *planeDistort;
+    planeDistort = psPlaneDistortAlloc(1, 1, 1, 1);
+    if ( !psMemCheckType(PS_DATA_PLANEDISTORT, planeDistort) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckPlaneDistort failed in memCheckType. \n");
+        psFree(planeDistort);
+        return 1;
+    }
+    psFree(planeDistort);
+
+    psPlaneTransform *planeTransform;
+    planeTransform = psPlaneTransformAlloc(1, 1);
+    if ( !psMemCheckType(PS_DATA_PLANETRANSFORM, planeTransform) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckPlaneTransform failed in memCheckType. \n");
+        psFree(planeTransform);
+        return 1;
+    }
+    psFree(planeTransform);
+
+    psPolynomial1D *poly1;
+    poly1 = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    if ( !psMemCheckType(PS_DATA_POLYNOMIAL1D, poly1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckPolynomial1D failed in memCheckType. \n");
+        psFree(poly1);
+        return 1;
+    }
+    psFree(poly1);
+
+    psPolynomial2D *poly2;
+    poly2 = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 2, 1);
+    if ( !psMemCheckType(PS_DATA_POLYNOMIAL2D, poly2) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckPolynomial2D failed in memCheckType. \n");
+        psFree(poly2);
+        return 1;
+    }
+    psFree(poly2);
+
+    psPolynomial3D *poly3;
+    poly3 = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, 2, 1, 1);
+    if ( !psMemCheckType(PS_DATA_POLYNOMIAL3D, poly3) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckPolynomial3D failed in memCheckType. \n");
+        psFree(poly3);
+        return 1;
+    }
+    psFree(poly3);
+
+    psPolynomial4D *poly4;
+    poly4 = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 2, 1, 2, 1);
+    if ( !psMemCheckType(PS_DATA_POLYNOMIAL4D, poly4) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckPolynomial4D failed in memCheckType. \n");
+        psFree(poly4);
+        return 1;
+    }
+    psFree(poly4);
+
+    psProjection *proj;
+    proj = psProjectionAlloc(1, 1, 2.1, 2.1, PS_PROJ_TAN);
+    if ( !psMemCheckType(PS_DATA_PROJECTION, proj) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckProjection failed in memCheckType. \n");
+        psFree(proj);
+        return 1;
+    }
+    psFree(proj);
+
+    psScalar *scalar;
+    psC64 c64 = 1.1 + 7I;
+    scalar = psScalarAlloc(c64, PS_TYPE_F64);
+    if ( !psMemCheckType(PS_DATA_SCALAR, scalar) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckScalar failed in memCheckType. \n");
+        psFree(scalar);
+        return 1;
+    }
+    psFree(scalar);
+
+    psSphere *sphere;
+    sphere = psSphereAlloc();
+    if ( !psMemCheckType(PS_DATA_SPHERE, sphere) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckSphere failed in memCheckType. \n");
+        psFree(sphere);
+        return 1;
+    }
+    psFree(sphere);
+
+    psSphereRot *sphereRot;
+    sphereRot = psSphereRotAlloc(0, 0, 20);
+    if ( !psMemCheckType(PS_DATA_SPHEREROT, sphereRot) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckSphereRot failed in memCheckType. \n");
+        psFree(sphereRot);
+        return 1;
+    }
+    psFree(sphereRot);
+
+    psSpline1D *spline;
+    spline = psSpline1DAlloc(2, 1, 0, 2);
+    if ( !psMemCheckType(PS_DATA_SPLINE1D, spline) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckSpline1D failed in memCheckType. \n");
+        psFree(spline);
+        return 1;
+    }
+    psFree(spline);
+
+    psStats *stats;
+    stats = psStatsAlloc(PS_STAT_MAX);
+    if ( !psMemCheckType(PS_DATA_STATS, stats) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckStats failed in memCheckType. \n");
+        psFree(stats);
+        return 1;
+    }
+    psFree(stats);
+
+    psTime *time;
+    time = psTimeAlloc(PS_TIME_UT1);
+    if ( !psMemCheckType(PS_DATA_TIME, time) )  {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckTime failed in memCheckType. \n");
+        psFree(time);
+        return 1;
+    }
+    psFree(time);
+
+    psVector *vector;
+    vector = psVectorAlloc(100, PS_TYPE_F32);
+    if ( !psMemCheckType(PS_DATA_VECTOR, vector) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psMemCheckVector failed in memCheckType. \n");
+        psFree(vector);
+        return 1;
+    }
+    psFree(vector);
+
+
+    psFree(negative);
+    psFree(neg);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psString.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psString.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psString.c	(revision 22322)
@@ -0,0 +1,740 @@
+/** @file  tst_psString.c
+ *
+ * -*- mode: C; c-basic-indent: 4; tab-width: 8; indent-tabs-mode: nil -*-
+ * vim: set cindent ts=8 sw=4 expandtab:
+ *
+ *  @brief Test driver for psString functions
+ *
+ *  This test driver contains the following test points for psStringCopy
+ *  and psStringNCopy, and related string functions.
+ *    1) Verify string copy - psStringCopy
+ *    2) Verify empty string copy - psStringCopy
+ *    3) Verify string copy with length - psStringNCopy
+ *    4) Verify empty string copy with length - psStringNCopy
+ *    5) Copy string to larger string - psStringNCopy
+ *    6) Copy string with negative size - psStringNCopy
+ *    7) Verifiy creation of string literal - PS_STRING
+ *
+ *  Return:   Number of test points which failed
+ *
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.11 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-07-14 02:26:25 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+#define STR_0 "binky had a leeeetle lamb"
+
+static psS32 testStringCopy00(void);
+static psS32 testStringCopy01(void);
+static psS32 testStringCopy02(void);
+static psS32 testStringCopy03(void);
+static psS32 testStringCopy04(void);
+//static psS32 testStringCopy05(void);
+static psS32 testStringCopy06(void);
+
+static psS32 testStrAppend00(void);
+static psS32 testStrAppend01(void);
+static psS32 testStrAppend02(void);
+static psS32 testStrAppend03(void);
+
+static psS32 testStrPrepend00(void);
+static psS32 testStrPrepend01(void);
+static psS32 testStrPrepend02(void);
+static psS32 testStrPrepend03(void);
+
+static psS32 testStrSplit00(void);
+static psS32 testNULLStrings(void);
+static psS32 testStrCheck(void);
+
+testDescription tests[] = {
+                              {testStringCopy00, 0, "Verify string copy", 0, false},
+                              {testStringCopy01, 1, "Verify empty string copy", 0, false},
+                              {testStringCopy02, 2, "Verify string copy with length", 0, false},
+                              {testStringCopy03, 3, "Verify empty string copy with length", 0, false},
+                              {testStringCopy04, 4, "Copy string to larger string", 0, false},
+                              //                              {testStringCopy05, 5, "Copy string with negative size", 0, false},
+                              {testStringCopy06, 6, "Verify creation of string literal", 0, false},
+
+                              {testStrAppend00,  7, "Verify generic string append", 0, false},
+                              {testStrAppend01,  8, "Test append NULL handling", 0, false},
+                              {testStrAppend02,  9, "Verify append string creation", 0, false},
+                              {testStrAppend03, 10, "Test append null-op", 0, false},
+
+                              {testStrPrepend00,11, "Verify generic string prepend", 0, false},
+                              {testStrPrepend01,12, "Test prepend NULL handling", 0, false},
+                              {testStrPrepend02,13, "Verify prepend string creation", 0, false},
+                              {testStrPrepend03,14, "Test prepend null-op", 0, false},
+                              {testStrSplit00,15, "Test String Splitting", 0, false},
+                              {testNULLStrings,666, "Test NULL String Error Handling", 0, false},
+                              {testStrCheck,16, "Test String Allocation and MemCheck", 0, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psString", tests, argc, argv ) );
+}
+
+/*
+psS32 main( psS32 argc,
+          char * argv[] )
+{
+    psBool  tpResult = false;
+    char  stringval[20] = "E R R O R";
+    char  stringval1[20] = "e r r o r";
+    char  *stringvalnocopy = "F A I L";
+    char  *substringval = "e r r";
+    psS32   substringlen = 6;
+    char  *emptyval = "";
+    psS32   tpFails = 0;
+    char  *strResult;
+    psS32   increaseSize = 5;
+    psS32   negativeSize = -5;
+    psS32   result = 0;
+    psS32   result1 = 0;
+    psS32   memBlockAllocated = 0;
+    psMemBlock ***memBlockPtr = NULL;
+ 
+*/
+
+static psS32 testStringCopy00(void)
+{
+    char  stringval[20] = "E R R O R";
+    psS32   result = 0;
+    psS32   result1 = 0;
+    char  *strResult;
+
+    // Test point #1 Verify string copy - psStringCopy
+    strResult = psStringCopy(stringval);
+    // Perform string compare
+    result = strcmp(strResult, stringval);
+    // Modify original string
+    stringval[0]='G';
+    result1 = strcmp(strResult, stringval);
+    stringval[0]='E';
+    if ( ( result != 0 ) || ( result1 == 0) ) {
+        fprintf(stderr, "Failed test point #1 strcmp result = %d expected 0\n",result);
+        fprintf(stderr, "                               src = %s expected %s\n",
+                strResult, stringval);
+        fprintf(stderr, "             changed strcmp result = %d expected 1\n",result1);
+        fprintf(stderr, "             changed           src = %s expected %s\n",strResult,
+                stringval);
+        return 1;
+    }
+
+    // Free memory allocated
+    psFree(strResult);
+
+    return 0;
+}
+
+static psS32 testStringCopy01(void)
+{
+    char  *emptyval = "";
+    psS32   result = 0;
+    char  *strResult;
+
+    // Test point #2 Verify empty string copy - psStringCopy
+    strResult = psStringCopy(emptyval);
+    // Perform string compare
+    result = strcmp(strResult, emptyval);
+    if ( result != 0 ) {
+        fprintf(stderr,"Failed test point #2 strcmp result = %d expected 0\n",result);
+        fprintf(stderr,"                               src = %s expected %s\n",
+                strResult, emptyval);
+        return 1;
+    }
+
+    // Free memory allocated
+    psFree(strResult);
+
+    return 0;
+}
+
+static psS32 testStringCopy02(void)
+{
+    psS32   result = 0;
+    psS32   result1 = 0;
+    char  *strResult;
+    char  stringval1[20] = "e r r o r";
+    psS32   substringlen = 5;
+    char  *substringval = "e r r";
+
+    // Test point #3 Verify string copy with length - psStringNCopy
+    strResult = psStringNCopy(stringval1, substringlen);
+    // Perform string compare and get string length
+    result = strncmp(strResult, substringval, substringlen);
+    // Change original string
+    stringval1[0] = 'g';
+    result1 = strncmp(strResult, substringval, substringlen);
+    if ( ( result != 0 ) || ( result1 != 0 ) ) {
+        fprintf(stderr,"Failed test point #3 strcmp result = %d expected 0\n",result);
+        fprintf(stderr,"                               src = %s expected %s\n",
+                strResult, substringval);
+        fprintf(stderr,"             changed strcmp result = %d expected 0\n",result1);
+        fprintf(stderr,"                               src = %s expected %s\n",
+                strResult, substringval);
+        return 1;
+    }
+    // Free memory allocated
+    psFree(strResult);
+
+    return 0;
+}
+
+static psS32 testStringCopy03(void)
+{
+    psS32   result = 0;
+    psS32   result1 = 0;
+    char  *strResult;
+    char  *stringvalnocopy = "F A I L";
+
+    // Test point #4 Verify empty string copy with length - psStringNCopy
+    strResult = psStringNCopy(stringvalnocopy, 0);
+    // Perform string compare and get sting length
+    result = strcmp(strResult, stringvalnocopy);
+    result1 = strlen(strResult);
+    if ( result == 0 ) {
+        fprintf(stderr,"Failed test point #4 strcmp result = %d didn't expected %d\n",result,0);
+        fprintf(stderr,"                               src = %s didn't expected %s\n",
+                strResult, stringvalnocopy);
+        return 1;
+    }
+    // Free memory
+    psFree(strResult);
+
+    return 0;
+}
+
+static psS32 testStringCopy04(void)
+{
+    psS32   result = 0;
+    psS32   result1 = 0;
+    char  *strResult;
+    char  stringval[20] = "E R R O R";
+    psS32   increaseSize = 5;
+
+    // Test point #5 Copy string to larger string - psStringNCopy
+    strResult = psStringNCopy(stringval, (strlen(stringval) + increaseSize));
+    // Perform string compare and get string length
+    result = strcmp(strResult, stringval);
+    result1 = strlen(strResult);
+    // The strings should still compare
+    if ( ( result != 0 ) ||
+            ( result1 != strlen(stringval) ) ) {
+        fprintf(stderr,"Failed test point #5 strcmp result = %d expected %d\n",result,0);
+        fprintf(stderr,"                               src = %s expected %s\n",
+                strResult, stringval);
+        fprintf(stderr,"                     strlne result = %d expected %d\n",result1,
+                (psS32)strlen(stringval));
+        return 1;
+    }
+    // Free memory
+    psFree(strResult);
+
+    return 0;
+}
+
+// XXX This test needs to be modified to check for maximum size
+//     This will require a mod to psStringNCopy source to check for maximum size
+//
+//static psS32 testStringCopy05(void)
+//{
+//    char  *strResult;
+//    char  stringval[20] = "E R R O R";
+//    psS32   negativeSize = -5;
+//
+//    // Test point #6 Copy string with negative size - psStringNCopy
+//    strResult = psStringNCopy(stringval, negativeSize);
+//    if ( strResult != NULL ) {
+//        fprintf(stderr,"Failed test point #6 return value = %p expected NULL\n",
+//                strResult);
+//        return 1;
+//    }
+//    // Memory should not have been allocated
+//
+//    return 0;
+//}
+
+static psS32 testStringCopy06(void)
+{
+    char  *strResult;
+    char  stringval[20] = "E R R O R";
+    psS32   result = 0;
+
+    // Test point #7 Verify creation of string literal - PS_STRING
+    strResult = PS_STRING(E R R O R);
+    result = strcmp(strResult, stringval);
+    if ( result != 0 ) {
+        fprintf(stderr,"Failed test point #7 strcmp result = %d expected %d",result,0);
+        return 1;
+    }
+    // Memory should not have been allocated
+
+    return 0;
+}
+
+static psS32 testStrAppend00(void)
+{
+    char *str=NULL;
+    int result = 0;
+
+    str = psStringCopy("3.14159");
+
+    psStringAppend(&str, "%d%s", 2653589, "79323846");
+
+    // Test point: Verify string append
+    result = strcmp(str, "3.14159265358979323846");
+    if ( result != 0 ) {
+        fprintf(stderr,"%s","Failed test point\n");
+        return 1;
+    }
+
+    psFree(str);
+
+    return 0;
+}
+
+static psS32 testStrAppend01(void)
+{
+    ssize_t sz;
+    char *str=NULL;
+
+    // test nonsensical invocations ...
+    sz = psStringAppend(NULL, NULL);
+    if ( sz != 0 ) {
+        fprintf(stderr,"%s","Failed test point\n");
+        return 1;
+    }
+    sz = psStringAppend(&str, NULL);
+    if ( sz != 0 ) {
+        fprintf(stderr,"%s","Failed test point\n");
+        return 1;
+    }
+
+    return 0;
+}
+
+static psS32 testStrAppend02(void)
+{
+    char *str=NULL;
+    int result;
+
+    // test string creation
+    psStringAppend(&str, "%s", "fubar");
+    result = strcmp(str, "fubar");
+    if ( result != 0 ) {
+        fprintf(stderr,"%s","Failed test point\n");
+        return 1;
+    }
+
+    psFree(str);
+
+    return 0;
+}
+
+static psS32 testStrAppend03(void)
+{
+    char *str=NULL;
+    int result;
+
+    str = psStringCopy(STR_0);
+
+    // test null-op
+    psStringAppend(&str, "");
+    result = strcmp(str, STR_0);
+    if ( result != 0 ) {
+        fprintf(stderr,"Failed test point str=[%s]\n", str);
+        return 1;
+    }
+
+    psFree(str);
+
+    return 0;
+}
+
+static psS32 testStrPrepend00(void)
+{
+    char *str=NULL;
+    int result = 0;
+
+    str = psStringCopy("79323846");
+
+    psStringPrepend(&str, "%s%d","3.14159", 2653589 );
+
+    // Test point: Verify string append
+    result = strcmp(str, "3.14159265358979323846");
+    if ( result != 0 ) {
+        fprintf(stderr,"%s","Failed test point\n");
+        return 1;
+    }
+
+    psFree(str);
+
+    return 0;
+}
+
+static psS32 testStrPrepend01(void)
+{
+    ssize_t sz;
+    char *str=NULL;
+
+    // test nonsensical invocations ...
+    sz = psStringPrepend(NULL, NULL);
+    if ( sz != 0 ) {
+        fprintf(stderr,"%s","Failed test point\n");
+        return 1;
+    }
+    sz = psStringPrepend(&str, NULL);
+    if ( sz != 0 ) {
+        fprintf(stderr,"%s","Failed test point\n");
+        return 1;
+    }
+
+    return 0;
+}
+
+static psS32 testStrPrepend02(void)
+{
+    char *str=NULL;
+    int result;
+
+    // test string creation
+    psStringPrepend(&str, "%s", "fubar");
+    result = strcmp(str, "fubar");
+    if ( result != 0 ) {
+        fprintf(stderr,"%s","Failed test point\n");
+        return 1;
+    }
+
+    psFree(str);
+
+    return 0;
+}
+
+static psS32 testStrPrepend03(void)
+{
+    char *str=NULL;
+    int result;
+
+    str = psStringCopy(STR_0);
+
+    // test null-op
+    psStringPrepend(&str, "");
+    result = strcmp(str, STR_0);
+    if ( result != 0 ) {
+        fprintf(stderr,"Failed test point str=[%s]\n", str);
+        return 1;
+    }
+
+    psFree(str);
+
+    return 0;
+}
+
+static psS32 testStrSplit00(void)
+{
+    psList *strList = NULL;
+    char str[35];
+    char split[5];
+    strncpy(str, "This is, a, test case, to check.", 35);
+    strncpy(split, ",", 2);
+    psString psStr;
+    psString psSplit;
+    psStr = psStringCopy(str);
+    psSplit = psStringCopy(split);
+
+    //Return NULL for NULL inputs
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    strList = psStringSplit(NULL, NULL, true);
+    //    if (strList != NULL) {
+    if (strList) {
+        psFree(strList);
+        return 1;
+    }
+    psFree(strList);
+    strList = NULL;
+    //Return empty list for NULL string input
+    //psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    strList = psStringSplit(NULL, split, true);
+    //    if (strList != NULL) {
+    if ( psListLength(strList) ) {
+        psFree(strList);
+        return 2;
+    }
+    psFree(strList);
+    strList = NULL;
+    //Return NULL for NULL splitter input
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    strList = psStringSplit(str, NULL, true);
+    //    if (strList != NULL) {
+    if ( strList ) {
+        psFree(strList);
+        return 3;
+    }
+    psFree(strList);
+    strList = NULL;
+    //Return a psList* of psStrings
+    strList = psStringSplit(str, split, true);
+    if (strList->n != 4) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return the correct number of strings.\n");
+        return 4;
+    }
+    if ( strncmp((psString)(strList->head->data), "This is", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 5;
+    }
+    if ( strncmp((psString)(strList->head->next->data), " a", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 6;
+    }
+    if ( strncmp((psString)(strList->head->next->next->data), " test case", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 7;
+    }
+    if ( strncmp((psString)(strList->head->next->next->next->data), " to check.", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 8;
+    }
+    psFree(strList);
+    //Return correct psList when using (psString, char*)
+    strList = psStringSplit(psStr, split, true);
+    if (strList->n != 4) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return the correct number of strings.\n");
+        return 9;
+    }
+    if ( strncmp((psString)(strList->head->data), "This is", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 10;
+    }
+    if ( strncmp((psString)(strList->head->next->data), " a", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 11;
+    }
+    if ( strncmp((psString)(strList->head->next->next->data), " test case", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 12;
+    }
+    if ( strncmp((psString)(strList->head->next->next->next->data), " to check.", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 13;
+    }
+    psFree(strList);
+    //Return correct psList when using (char*, psString)
+    strList = psStringSplit(str, psSplit, true);
+    if (strList->n != 4) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return the correct number of strings.\n");
+        return 14;
+    }
+    if ( strncmp((psString)(strList->head->data), "This is", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 15;
+    }
+    if ( strncmp((psString)(strList->head->next->data), " a", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 16;
+    }
+    if ( strncmp((psString)(strList->head->next->next->data), " test case", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 17;
+    }
+    if ( strncmp((psString)(strList->head->next->next->next->data), " to check.", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 18;
+    }
+    psFree(strList);
+    //Return correct psList when using (psString, psString)
+    strList = psStringSplit(psStr, psSplit, true);
+    if (strList->n != 4) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return the correct number of strings.\n");
+        return 19;
+    }
+    if ( strncmp((psString)(strList->head->data), "This is", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 20;
+    }
+    if ( strncmp((psString)(strList->head->next->data), " a", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 21;
+    }
+    if ( strncmp((psString)(strList->head->next->next->data), " test case", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 22;
+    }
+    if ( strncmp((psString)(strList->head->next->next->next->data), " to check.", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 23;
+    }
+    psFree(strList);
+    //Return correct psList output for string of zero length case
+    strncpy(str, "This is,, a,, test case,, to check.", 35);
+    strList = psStringSplit(str, split, false);
+    if (strList->n != 4) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return the correct number of strings.\n");
+        return 24;
+    }
+    if ( strncmp((psString)(strList->head->data), "This is", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 25;
+    }
+    if ( strncmp((psString)(strList->head->next->data), " a", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 26;
+    }
+    if ( strncmp((psString)(strList->head->next->next->data), " test case", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 27;
+    }
+    if ( strncmp((psString)(strList->head->next->next->next->data), " to check.", 10) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psStringSplit failed to return expected strings.");
+        return 28;
+    }
+
+    psFree(strList);
+    psFree(psStr);
+    psFree(psSplit);
+    return 0;
+}
+
+static psS32 testNULLStrings(void)
+{
+    psString nullTest = NULL;
+    psString output = NULL;
+    ssize_t outSize = 0;
+    char** nullDest = NULL;
+    char** test;
+    //psStringCopy should return NULL for NULL input string
+    //    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    output = psStringCopy(nullTest);
+    if (output != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psStringCopy failed to return NULL for NULL input string.\n");
+        return 1;
+    }
+    //psStringNCopy should return NULL for NULL input string
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    output = psStringNCopy(nullTest, 100);
+    if (output != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psStringNCopy failed to return NULL for NULL input string.\n");
+        return 2;
+    }
+
+    //psStringAppend should return 0 for NULL input destination
+    outSize = psStringAppend(nullDest, "");
+    if (outSize != 0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psStringAppend failed to return 0 for NULL input destination.\n");
+        return 3;
+    }
+    //psStringAppend should return 0 for NULL input format
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    outSize = psStringAppend(test, nullTest);
+    if (outSize != 0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psStringAppend failed to return 0 for NULL input format.\n");
+        return 4;
+    }
+    //psStringPrepend should return 0 for NULL input destination
+    outSize = psStringPrepend(nullDest, " ");
+    if (outSize != 0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psStringPrepend failed to return 0 for NULL input destination.\n");
+        return 3;
+    }
+    //psStringPrepend should return 0 for NULL input format
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    outSize = psStringPrepend(test, nullTest);
+    if (outSize != 0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psStringPrepend failed to return 0 for NULL input format.\n");
+        return 4;
+    }
+    //psStringSplit should return empty list for NULL input string
+    psList *nullList = NULL;
+    // psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    nullList = psStringSplit(nullTest, ",", true);
+    //    if (nullList != NULL) {
+    if ( psListLength(nullList) ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psStringSplit failed to return NULL for NULL input string.\n");
+        return 5;
+    }
+    psFree(nullList);
+    nullList = NULL;
+    //psStringSplit should return NULL for NULL input splitter
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    nullList = psStringSplit("Hello World", nullTest, true);
+    //    if (nullList != NULL) {
+    if ( nullList ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psStringSplit failed to return NULL for NULL input splitter.\n");
+        return 6;
+    }
+    psFree(nullList);
+
+    return 0;
+}
+
+psS32 testStrCheck(void)
+{
+    psString str = NULL;
+    str = psStringAlloc(10);
+    strcpy(str, "Hello");
+    if (!psMemCheckString(str)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psString wasn't properly allocated!\n");
+        return 1;
+    }
+    if (!psMemCheckType(PS_DATA_STRING, str)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psString wasn't properly allocated!\n");
+        return 2;
+    }
+    psFree(str);
+
+    char charStr[10];
+    if (psMemCheckType(PS_DATA_STRING, charStr)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Input string is not a psDataType!!!  (Should have returned false)\n");
+        return 3;
+    }
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psTrace.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psTrace.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/sys/tst_psTrace.c	(revision 22322)
@@ -0,0 +1,381 @@
+/*****************************************************************************
+    This code will test whether trace levels can be set successfully.
+ 
+    XXX: For the last two testpoints, must verify that the results are
+    correct, and put that verification in the test as well.
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include <fcntl.h>
+#include <unistd.h>
+
+
+static psS32 testTrace00(void);
+static psS32 testTrace01(void);
+static psS32 testTrace02(void);
+static psS32 testTrace03(void);
+static psS32 testTrace04(void);
+static psS32 testTrace05(void);
+static psS32 testTrace05a(void);
+static psS32 testTrace06(void);
+static psS32 testTrace08(void);
+
+testDescription tests[] = {
+                              {testTrace00, 0, "psTraceSetLevel() and psTraceGetLevel()", 0, false},
+                              {testTrace01, 1, "psTraceSetLevel(): set multiple components in one call", 0, false},
+                              {testTrace02, 2, "psTraceSetLevel(): test static/dynamic inheritance", 0, false},
+                              {testTrace03, 3, "psTraceReset()", 0, false},
+                              {testTrace04, 4, "psTrace()", 0, false},
+                              {testTrace05, 5, "psTracePrintLevels()", 0, false},
+                              {testTrace05a, 5, "optional leading dot and psTracePrintLevels()", 0, false},
+                              {testTrace06, 6, "Testing psTraceReset", 0, false},
+                              {testTrace08, 8, "Testing ", 0, false},
+                              {NULL}
+                          };
+
+testDescription tests2[] = {
+                               {testTrace08, 8, "Testing ", 0, false},
+                           };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psTrace", tests, argc, argv ) );
+}
+
+static psS32 testTrace00(void)
+{
+    psS32 i;
+    psS32 lev = 0;
+
+    //    psTraceSetDestination(stderr);
+    psTraceSetDestination(2);
+
+    for (i=0;i<10;i++) {
+        (void)psTraceSetLevel(".", i);
+        lev = psTraceGetLevel(".");
+        if (lev != i) {
+            fprintf(stderr,"ERROR: (.) expected trace level was %d, actual was %d\n",
+                    i, lev);
+            return 1;
+        }
+    }
+    (void)psTraceSetLevel(".", 3);
+
+    for (i=5;i<10;i++) {
+        (void)psTraceSetLevel(".NODE00", i);
+        lev = psTraceGetLevel(".NODE00");
+        if (lev != i) {
+            fprintf(stderr,
+                    "ERROR: (.NODE00) expected trace level was %d, actual was %d\n",
+                    i, lev);
+            return 2;
+        }
+
+        lev = psTraceGetLevel(".");
+        if (lev != 3) {
+            fprintf(stderr,"ERROR: (.) expected trace level was %d, actual was %d\n",
+                    i, 3);
+            return 3;
+        }
+    }
+
+
+    (void)psTraceSetLevel(".NODE00.NODE01", 4);
+    for (i=0;i<10;i++) {
+        (void)psTraceSetLevel(".NODE00.NODE01", i);
+        lev = psTraceGetLevel(".NODE00.NODE01");
+        if (lev != i) {
+            fprintf(stderr,
+                    "ERROR: (.NODE00.NODE01) expected trace level was %d, actual was %d\n",
+                    i, lev);
+            return 4;
+        }
+    }
+
+    return 0;
+}
+
+static psS32 testTrace01(void)
+{
+    //    psTraceSetDestination(stderr);
+    psTraceSetDestination(2);
+    (void)psTraceSetLevel(".A.B.C.D.E", 5);
+
+    psTrace(".A.C.D.C",1,"You should not see this.\n");
+    psTrace(".A.B.C.D.E",2,"You should see this.\n");
+    psTrace(".A.B.C.D.E.F",3,"You should see this too.\n");
+
+    psTracePrintLevels();
+
+    return 0;
+}
+
+static psS32 testTrace02(void)
+{
+    psTraceReset();
+    //    psTraceSetDestination(stderr);
+    psTraceSetDestination(2);
+    psTraceSetLevel(".A.B", 2);
+    psTraceSetLevel(".A.B.C.D.E", 5);
+    psTracePrintLevels();
+    psTraceSetLevel(".A.B", 10);
+    psTracePrintLevels();
+
+    if (10 != psTraceGetLevel(".A.B.C")) {
+        fprintf(stderr,"ERROR: .A.B.C did not dynamically inherit a trace level (%d)\n",
+                psTraceGetLevel(".A.B.C"));
+        return 2;
+    }
+
+    if (10 != psTraceGetLevel(".A.B.C.D")) {
+        fprintf(stderr,"ERROR: .A.B.C.D did not dynamically inherit a trace level (%d)\n", psTraceGetLevel(".A.B.C.D"));
+        return 2;
+    }
+
+    if (5 != psTraceGetLevel(".A.B.C.D.E")) {
+        fprintf(stderr,"ERROR: .A.B.C.D.E did dynamically inherit a trace level (%d)\n", psTraceGetLevel(".A.B.C.D.E"));
+        return 2;
+    }
+
+    return 0;
+}
+
+static psS32 testTrace03(void)
+{
+    psS32 i = 0;
+    psS32 lev = 0;
+
+    //    psTraceSetDestination(stderr);
+    psTraceSetDestination(2);
+
+    for (i=0;i<10;i++) {
+        (void)psTraceSetLevel(".", i);
+        psTraceReset();
+
+        lev = psTraceGetLevel(".");
+        if (lev != PS_UNKNOWN_TRACE_LEVEL) {
+            fprintf(stderr,"ERROR: expected trace level was %d, actual was %d\n",
+                    PS_UNKNOWN_TRACE_LEVEL, lev);
+            return 1;
+        }
+    }
+    (void)psTraceSetLevel(".", 5);
+    (void)psTraceSetLevel(".a", 4);
+    (void)psTraceSetLevel(".a.b", 3);
+    (void)psTraceSetLevel(".a.b.c", 2);
+    if ((5 != psTraceGetLevel(".")) ||
+            (4 != psTraceGetLevel(".a")) ||
+            (3 != psTraceGetLevel(".a.b")) ||
+            (2 != psTraceGetLevel(".a.b.c"))) {
+        fprintf(stderr,"ERROR: trace successFlag = false;levels were not settable?\n");
+        return 2;
+    }
+
+    psTraceReset();
+    if ((PS_UNKNOWN_TRACE_LEVEL != psTraceGetLevel(".")) ||
+            (PS_UNKNOWN_TRACE_LEVEL != psTraceGetLevel(".a")) ||
+            (PS_UNKNOWN_TRACE_LEVEL != psTraceGetLevel(".a.b")) ||
+            (PS_UNKNOWN_TRACE_LEVEL != psTraceGetLevel(".a.b.c"))) {
+        fprintf(stderr,"ERROR: trace levels were not reset properly\n");
+        return 3;
+    }
+
+    return 0;
+}
+
+
+static psS32 testTrace04(void)
+{
+    int FD;
+    psS32 nb = 0;
+    FD = creat("tst_psTrace02_OUT", 0666);
+    //    printf("\nFD = %d\n", FD);
+    //    fp = fopen("tst_psTrace02_OUT", "w");
+    for (nb = 0 ; nb<4;nb++) {
+        if (nb == 0)
+            //            psTraceSetDestination(stdout);
+            psTraceSetDestination(1);
+        if (nb == 1)
+            //            psTraceSetDestination(stderr);
+            psTraceSetDestination(2);
+        if (nb == 2)
+            psTraceSetDestination(0); //NULL
+        if (nb == 3)
+            psTraceSetDestination(FD);
+
+        (void)psTraceSetLevel(".", 4);
+        psTrace(".", 5, "(0) This message should not be displayed (%x)\n",
+                0xbeefface);
+        (void)psTraceSetLevel(".", 7);
+        psTrace(".", 5, "(0) This message should be displayed (%x)\n",
+                0xbeefface);
+
+        (void)psTraceSetLevel(".a", 4);
+        psTrace(".a", 5, "(1) This message should not be displayed (%x)\n",
+                0xbeefface);
+        (void)psTraceSetLevel(".a", 7);
+        psTrace(".a", 5, "(1) This message should be displayed (%x)\n",
+                0xbeefface);
+
+
+        (void)psTraceSetLevel(".a.b", 4);
+        psTrace(".a.b", 5, "(2) This message should not be displayed (%x)\n",
+                0xbeefface);
+        (void)psTraceSetLevel(".a.b", 7);
+        psTrace(".a.b", 5, "(2) This message should be displayed (%x)\n",
+                0xbeefface);
+        (void)psTraceSetLevel(".a.b.c", 12);
+        psTrace(".a.b.c", 11, "(3) This message should be displayed (%x)\n",
+                0xbeefface);
+
+    }
+
+    close(FD);
+
+    return(0);
+}
+
+static psS32 testTrace05(void)
+{
+    //    psTraceSetDestination(stderr);
+    psTraceSetDestination(2);
+
+    (void)psTraceSetLevel(".", 9);
+
+    (void)psTraceSetLevel(".a", 8);
+    (void)psTraceSetLevel(".b", 7);
+    (void)psTraceSetLevel(".c", 5);
+
+    (void)psTraceSetLevel(".a.a", 4);
+    (void)psTraceSetLevel(".a.b", 3);
+
+    (void)psTraceSetLevel(".b.a", 2);
+    (void)psTraceSetLevel(".b.b", 1);
+
+    (void)psTraceSetLevel(".c.a", 0);
+    (void)psTraceSetLevel(".c.b", 3);
+    (void)psTraceSetLevel(".c.c", 5);
+
+    psTracePrintLevels();
+
+    return 0;
+
+}
+
+static psS32 testTrace05a(void)
+{
+    (void)psTraceSetLevel(".", 9);
+
+    (void)psTraceSetLevel("a", 8);
+    (void)psTraceSetLevel("b", 7);
+    (void)psTraceSetLevel("c", 5);
+
+    (void)psTraceSetLevel("a.a", 4);
+    (void)psTraceSetLevel("a.b", 3);
+
+    (void)psTraceSetLevel("b.a", 2);
+    (void)psTraceSetLevel("b.b", 1);
+
+    (void)psTraceSetLevel("c.a", 0);
+    (void)psTraceSetLevel("c.b", 3);
+    (void)psTraceSetLevel("c.c", 5);
+
+    psTracePrintLevels();
+
+    return 0;
+
+}
+
+static psS32 testTrace06(void)
+{
+    //    psTraceSetDestination(stderr);
+    psTraceSetDestination(2);
+
+    (void)psTraceSetLevel(".", 9);
+
+    (void)psTraceSetLevel(".a", 8);
+    (void)psTraceSetLevel(".b", 7);
+    (void)psTraceSetLevel(".c", 5);
+
+    (void)psTraceSetLevel(".a.a", 4);
+    (void)psTraceSetLevel(".a.b", 3);
+
+    (void)psTraceSetLevel(".b.a", 2);
+    (void)psTraceSetLevel(".b.b", 1);
+
+    (void)psTraceSetLevel(".c.a", 0);
+    (void)psTraceSetLevel(".c.b", 3);
+    (void)psTraceSetLevel(".c.c", 5);
+
+    psTraceReset();
+
+    if ((psTraceGetLevel(".")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".a")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".b")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".c")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".a.a")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".a.b")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".b.a")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".b.b")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".c.a")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".c.b")!=PS_UNKNOWN_TRACE_LEVEL) ||
+            (psTraceGetLevel(".c.c")!=PS_UNKNOWN_TRACE_LEVEL)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+// Ensure that the leading dot in the component names are optional.
+static psS32 testTrace08(void)
+{
+    psTraceReset();
+    (void)psTraceSetLevel(".", 9);
+
+    (void)psTraceSetLevel(".a", 8);
+    (void)psTraceSetLevel(".b", 7);
+    (void)psTraceSetLevel(".c", 5);
+
+    (void)psTraceSetLevel(".a.a", 4);
+    (void)psTraceSetLevel(".a.b", 3);
+
+    (void)psTraceSetLevel(".b.a", 2);
+    (void)psTraceSetLevel(".b.b", 1);
+
+    (void)psTraceSetLevel(".c.a", 0);
+    (void)psTraceSetLevel(".c.b", 3);
+    (void)psTraceSetLevel(".c.c", 5);
+
+    psTracePrintLevels();
+
+    if ((psTraceGetLevel(".")!=9) ||
+            (psTraceGetLevel("a")!=8) ||
+            (psTraceGetLevel("b")!=7) ||
+            (psTraceGetLevel("c")!=5) ||
+            (psTraceGetLevel("a.a")!=4) ||
+            (psTraceGetLevel("a.b")!=3) ||
+            (psTraceGetLevel("b.a")!=2) ||
+            (psTraceGetLevel("b.b")!=1) ||
+            (psTraceGetLevel("c.a")!=0) ||
+            (psTraceGetLevel("c.b")!=3) ||
+            (psTraceGetLevel("c.c")!=5)) {
+        printf("psTraceGetLevel(.) is %d\n", psTraceGetLevel("."));
+        printf("psTraceGetLevel(a) is %d\n", psTraceGetLevel("a"));
+        printf("psTraceGetLevel(b) is %d\n", psTraceGetLevel("b"));
+        printf("psTraceGetLevel(c) is %d\n", psTraceGetLevel("c"));
+        printf("psTraceGetLevel(a.a) is %d\n", psTraceGetLevel("a.a"));
+        printf("psTraceGetLevel(a.b) is %d\n", psTraceGetLevel("a.b"));
+        printf("psTraceGetLevel(b.a) is %d\n", psTraceGetLevel("b.a"));
+        printf("psTraceGetLevel(b.b) is %d\n", psTraceGetLevel("b.b"));
+        printf("psTraceGetLevel(c.a) is %d\n", psTraceGetLevel("c.a"));
+        printf("psTraceGetLevel(c.b) is %d\n", psTraceGetLevel("c.b"));
+        printf("psTraceGetLevel(c.c) is %d\n", psTraceGetLevel("c.c"));
+
+        return 1;
+    }
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/.cvsignore	(revision 22322)
@@ -0,0 +1,13 @@
+.in
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.log
+config.status
+configure
+libtool
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/INSTALL	(revision 22322)
@@ -0,0 +1,8 @@
+Quick Installation
+
+    ./configure
+    make
+    make check
+    make install
+
+Run "configure --help" for additional options.
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/LICENSE	(revision 22322)
@@ -0,0 +1,23 @@
+Copyright (c) 2004 Nik Clayton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/Makefile.am	(revision 22322)
@@ -0,0 +1,5 @@
+SUBDIRS  = src
+#SUBDIRS += tests
+
+prove:
+	prove -v -r
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/README	(revision 22322)
@@ -0,0 +1,11 @@
+NAME
+     tap -- write tests that implement the Test Anything Protocol
+
+SYNOPSIS
+     #include <tap.h>
+
+DESCRIPTION
+     The tap library provides functions for writing test scripts that produce
+     output consistent with the Test Anything Protocol.  A test harness that
+     parses this protocol can run these tests and produce useful reports indi-
+     cating their success or failure.
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/bootstrap.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/bootstrap.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/bootstrap.sh	(revision 22322)
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+set -x
+aclocal19 -I /usr/local/share/aclocal || aclocal || exit 1
+autoheader259 || autoheader || exit 1
+libtoolize15 -c -f || libtoolize -c -f || glibtoolize -c -f || exit 1
+automake19 -a -c || automake -a -c || exit 1
+autoconf259 || autoconf || exit 1
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/compile
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/compile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/compile	(revision 22322)
@@ -0,0 +1,142 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand `-c -o'.
+
+scriptversion=2004-10-12.08
+
+# Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+case $1 in
+  '')
+     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand `-c -o'.
+Remove `-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file `INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit 0
+    ;;
+  -v | --v*)
+    echo "compile $scriptversion"
+    exit 0
+    ;;
+esac
+
+ofile=
+cfile=
+eat=
+
+for arg
+do
+  if test -n "$eat"; then
+    eat=
+  else
+    case $1 in
+      -o)
+	# configure might choose to run compile as `compile cc -o foo foo.c'.
+	# So we strip `-o arg' only if arg is an object.
+	eat=1
+	case $2 in
+	  *.o | *.obj)
+	    ofile=$2
+	    ;;
+	  *)
+	    set x "$@" -o "$2"
+	    shift
+	    ;;
+	esac
+	;;
+      *.c)
+	cfile=$1
+	set x "$@" "$1"
+	shift
+	;;
+      *)
+	set x "$@" "$1"
+	shift
+	;;
+    esac
+  fi
+  shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+  # If no `-o' option was seen then we might have been invoked from a
+  # pattern rule where we don't need one.  That is ok -- this is a
+  # normal compilation that the losing compiler can handle.  If no
+  # `.c' file was seen then we are probably linking.  That is also
+  # ok.
+  exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use `[/.-]' here to ensure that we don't use the same name
+# that we are using for the .o file.  Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d
+while true; do
+  if mkdir "$lockdir" >/dev/null 2>&1; then
+    break
+  fi
+  sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+  mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+  mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/configure.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/configure.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/configure.in	(revision 22322)
@@ -0,0 +1,44 @@
+AC_INIT(tap, 1.01)
+AC_CONFIG_SRCDIR(src/tap.c)
+AM_INIT_AUTOMAKE([foreign])
+AC_CONFIG_HEADERS([src/config.h])
+AC_GNU_SOURCE
+AC_PROG_CC
+AC_PROG_LIBTOOL
+AC_PROG_INSTALL
+
+# Checks for libraries
+case "$host" in
+	*-*-*freebsd4*)
+		LDFLAGS="$LDFLAGS -pthread"
+		HAVE_LIBPTHREAD=1
+		;;
+	*)
+		AC_CHECK_LIB(pthread, main)
+		;;
+esac
+
+dnl build tests at the same time as the source code
+AC_ARG_ENABLE(tests,
+  [AS_HELP_STRING(--enable-tests,build tests at same time as source)],
+  [AC_MSG_RESULT(test building enabled)
+   tests=true],
+   [tests=false])
+AM_CONDITIONAL(BUILD_TESTS, test x$tests = xtrue)
+
+# Checks for header files
+AC_HEADER_STDC
+AC_CHECK_HEADERS([stdlib.h])
+AC_CHECK_HEADERS([pthread.h])
+
+# Checks for  typedefs, structures, and compiler characteristics.
+AC_C_CONST
+
+# Checks for library functions.
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS([atexit])
+
+AC_CONFIG_FILES([Makefile
+		 src/Makefile
+		])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/.cvsignore	(revision 22322)
@@ -0,0 +1,13 @@
+.deps
+.libs
+Makefile
+Makefile.in
+libtap.la
+tap.lo
+config.h
+config.h.in
+stamp-h1
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/Makefile.am	(revision 22322)
@@ -0,0 +1,9 @@
+TEST_LTLIBS = libtap.la
+libtap_la_SOURCES = tap.c tap.h
+noinst_HEADERS = tap.h
+
+noinst_LTLIBRARIES = $(TEST_LTLIBS)
+
+#man_MANS = tap.3
+EXTRA_DIST = $(man_MANS)
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/tap.3
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/tap.3	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/tap.3	(revision 22322)
@@ -0,0 +1,380 @@
+.Dd December 20, 2004
+.Os
+.Dt TAP 3
+.Sh NAME
+.Nm tap
+.Nd write tests that implement the Test Anything Protocol
+.Sh SYNOPSIS
+.In tap.h
+.Sh DESCRIPTION
+The
+.Nm
+library provides functions for writing test scripts that produce output
+consistent with the Test Anything Protocol.  A test harness that parses
+this protocol can run these tests and produce useful reports indicating
+their success or failure.
+.Ss PRINTF STRINGS
+In the descriptions that follow, for any function that takes as the
+last two parameters
+.Dq Fa char * , Fa ...
+it can be assumed that the
+.Fa char *
+is a
+.Fn printf
+-like format string, and the optional arguments are values to be placed
+in that string.
+.Ss TEST PLANS
+.Bl -tag -width indent
+.It Xo
+.Ft int
+.Fn plan_tests "unsigned int"
+.Xc
+.It Xo
+.Ft int
+.Fn plan_no_plan "void"
+.Xc
+.It Xo
+.Ft int
+.Fn plan_skip_all "char *" "..."
+.Xc
+.El
+.Pp
+You must first specify a test plan.  This indicates how many tests you
+intend to run, and allows the test harness to notice if any tests were
+missed, or if the test program exited prematurely.
+.Pp
+To do this, use
+.Fn plan_tests ,
+which returns the number of planned tests.  The function will cause
+your program to exit prematurely if you specify 0 tests.
+.Pp
+In some situations you may not know how many tests you will be running, or
+you are developing your test program, and do not want to update the
+.Fn plan_tests
+parameter every time you make a change.  For those situations use
+.Fn plan_no_plan .
+It returns 1, and indicates to the test harness that an indeterminate number
+of tests will be run.
+.Pp
+Both
+.Fn plan_tests
+and
+.Fn plan_no_plan
+will cause your test program to exit prematurely with a diagnostic
+message if they are called more than once.
+.Pp
+If your test program detects at run time that some required functionality
+is missing (for example, it relies on a database connection which is not
+present, or a particular configuration option that has not been included
+in the running kernel) use
+.Fn plan_skip_all ,
+passing as parameters a string to display indicating the reason for skipping
+the tests.
+.Ss SIMPLE TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft unsigned int
+.Fn ok "expression" "char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn ok1 "expression"
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn pass "char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn fail "char *" "..."
+.Xc
+.El
+.Pp
+Tests are implemented as expressions checked by calls to the
+.Fn ok
+and
+.Fn ok1
+macros.  In both cases
+.Fa expression
+should evaluate to true if the test succeeded.
+.Pp
+.Fn ok
+allows you to specify a name, or comment, describing the test which will
+be included in the output.
+.Fn ok1
+is for those times when the expression to be tested is self
+explanatory and does not need an associated comment.  In those cases
+the test expression becomes the comment.
+.Pp
+These four calls are equivalent:
+.Bd -literal -offset indent
+int i = 5;
+
+ok(i == 5, "i equals 5");      /* Overly verbose */
+ok(i == 5, "i equals %d", i);  /* Just to demonstrate printf-like
+                                  behaviour of the test name */
+ok(i == 5, "i == 5");          /* Needless repetition */
+ok1(i == 5);                   /* Just right */
+.Ed
+.Pp
+It is good practice to ensure that the test name describes the meaning
+behind the test rather than what you are testing.  Viz
+.Bd -literal -offset indent
+ok(db != NULL, "db is not NULL");            /* Not bad, but */
+ok(db != NULL, "Database conn. succeeded");  /* this is better */
+.Ed
+.Pp
+.Fn ok
+and
+.Fn ok1
+return 1 if the expression evaluated to true, and 0 if it evaluated to
+false.  This lets you chain calls from
+.Fn ok
+to
+.Fn diag
+to only produce diagnostic output if the test failed.  For example, this
+code will include diagnostic information about why the database connection
+failed, but only if the test failed.
+.Bd -literal -offset indent
+ok(db != NULL, "Database conn. succeeded") ||
+    diag("Database error code: %d", dberrno);
+.Ed
+.Pp
+You also have
+.Fn pass
+and
+.Fn fail .
+From the Test::More documentation:
+.Bd -literal -offset indent
+Sometimes you just want to say that the tests have passed.
+Usually the case is you've got some complicated condition
+that is difficult to wedge into an ok().  In this case,
+you can simply use pass() (to declare the test ok) or fail
+(for not ok).
+
+Use these very, very, very sparingly.
+.Ed
+.Pp
+These are synonyms for ok(1, ...) and ok(0, ...).
+.Ss SKIPPING TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft int
+.Fn skip "unsigned int" "char *" "..."
+.Xc
+.It Xo
+.Fn skip_start "expression" "unsigned int" "char *" "..."
+.Xc
+.It Xo
+.Fn skip_end
+.Xc
+.El
+.Pp
+Sets of tests can be skipped.  Ordinarily you would do this because
+the test can't be run in this particular testing environment.
+.Pp
+For example, suppose some tests should be run as root.  If the test is
+not being run as root then the tests should be skipped.  In this 
+implementation, skipped tests are flagged as being ok, with a special
+message indicating that they were skipped.  It is your responsibility
+to ensure that the number of tests skipped (the first parameter to
+.Fn skip )
+is correct for the number of tests to skip.
+.Pp
+One way of implementing this is with a
+.Dq do { } while(0);
+loop, or an
+.Dq if( ) { } else { }
+construct, to ensure that there are no additional side effects from the
+skipped tests.
+.Bd -literal -offset indent
+if(getuid() != 0) {
+        skip(1, "because test only works as root");
+} else {
+        ok(do_something_as_root() == 0, "Did something as root");
+}
+.Ed
+.Pp
+Two macros are provided to assist with this.  The previous example could
+be re-written as follows.
+.Bd -literal -offset indent
+skip_start(getuid() != 0, 1, "because test only works as root");
+
+ok(do_something_as_root() == 0, "Did something as root");
+
+skip_end();
+.Ed
+.Ss MARKING TESTS AS Dq TODO
+.Bl -tag -width indent
+.It Xo
+.Ft void
+.Fn todo_start "char *" "..."
+.Xc
+.It Xo
+.Ft void
+.Fn todo_end "void"
+.Xc
+.El
+.Pp
+Sets of tests can be flagged as being
+.Dq TODO .
+These are tests that you expect to fail, probably because you haven't
+fixed a bug, or finished a new feature yet.  These tests will still be
+run, but with additional output that indicates that they are expected
+to fail.  Should a test start to succeed unexpectedly, tools like
+.Xr prove 1
+will indicate this, and you can move the test out of the todo
+block.  This is much more useful than simply commenting out (or
+.Dq #ifdef 0 ... #endif )
+the tests.
+.Bd -literal -offset indent
+todo_start("dwim() not returning true yet");
+
+ok(dwim(), "Did what the user wanted");
+
+todo_end();
+.Ed
+.Pp
+Should
+.Fn dwim
+ever start succeeding you will know about it as soon as you run the
+tests.  Note that
+.Em unlike
+the
+.Fn skip_*
+family, additional code between
+.Fn todo_start
+and
+.Fn todo_end
+.Em is
+executed.
+.Ss SKIP vs. TODO
+From the Test::More documentation;
+.Bd -literal -offset indent
+If it's something the user might not be able to do, use SKIP.
+This includes optional modules that aren't installed, running
+under an OS that doesn't have some feature (like fork() or
+symlinks), or maybe you need an Internet connection and one
+isn't available.
+
+If it's something the programmer hasn't done yet, use TODO.
+This is for any code you haven't written yet, or bugs you have
+yet to fix, but want to put tests in your testing script 
+(always a good idea).
+.Ed
+.Ss DIAGNOSTIC OUTPUT
+.Bl -tag -width indent
+.It Xo
+.Fr unsigned int
+.Fn diag "char *" "..."
+.Xc
+.El
+.Pp
+If your tests need to produce diagnostic output, use
+.Fn diag .
+It ensures that the output will not be considered by the TAP test harness.
+.Fn diag
+adds the necessary trailing
+.Dq \en
+for you.
+.Bd -literal -offset indent
+diag("Expected return code 0, got return code %d", rcode);
+.Ed
+.Pp
+.Fn diag
+always returns 0.
+.Ss EXIT STATUS
+.Bl -tag -width indent
+.It Xo
+.Fr int
+.Fn exit_status void
+.Xc
+.El
+.Pp
+For maximum compatability your test program should return a particular
+exit code.  This is calculated by
+.Fn exit_status
+so it is sufficient to always return from
+.Fn main
+with either
+.Dq return exit_status();
+or
+.Dq exit(exit_status());
+as appropriate.
+.Sh ENVIRONMENT
+The following environment variables affect
+.Nm .
+.Bl -tag -width indent
+.It Ev HARNESS_ACTIVE
+Causes an extra
+.Dq \en
+to be printed before any diagnostic failure output generated by
+.Nm .
+This variable is normally set if tests are being run under Perl's
+Test::Harness.
+.El
+.Sh EXAMPLES
+The
+.Pa tests
+directory in the source distribution contains numerous tests of
+.Nm
+functionality, written using
+.Nm .
+Examine them for examples of how to construct test suites.
+.Sh COMPATABILITY
+.Nm
+strives to be compatible with the Perl Test::More and Test::Harness 
+modules.  The test suite verifies that
+.Nm
+is bug-for-bug compatible with their behaviour.  This is why some
+functions which would more naturally return nothing return constant
+values.
+.Pp
+If the
+.Lb libpthread
+is found at compile time,
+.Nm
+.Em should
+be thread safe.  Indications to the contrary (and test cases that expose
+incorrect behaviour) are very welcome.
+.Sh SEE ALSO
+.Xr Test::More 1 ,
+.Xr Test::Harness 1 ,
+.Xr prove 1
+.Sh STANDARDS
+.Nm
+requires a
+.St -isoC-99
+compiler.  Some of the
+.Nm
+functionality is implemented as variadic macros, and that functionality
+was not formally codified until C99.  Patches to use
+.Nm
+with earlier compilers that have their own implementation of variadic
+macros will be gratefully received.
+.Sh HISTORY
+.Nm
+was written to help improve the quality and coverage of the FreeBSD
+regression test suite, and released in the hope that others find it
+a useful tool to help improve the quality of their code.
+.Sh AUTHORS
+.An "Nik Clayton" Aq nik@ngo.org.uk ,
+.Aq nik@FreeBSD.org
+.Pp
+.Nm
+would not exist without the efforts of
+.An "Michael G Schwern" Aq schqern@pobox.com ,
+.An "Andy Lester" Aq andy@petdance.com ,
+and the countless others who have worked on the Perl QA programme.
+.Sh BUGS
+Ideally, running the tests would have no side effects on the behaviour
+of the application you are testing.  However, it is not always possible
+to avoid them.  The following side effects of using
+.Nm
+are known.
+.Bl -bullet -offset indent
+.It
+stdout is set to unbuffered mode after calling any of the
+.Fn plan_*
+functions.
+.El
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/tap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/tap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/tap.c	(revision 22322)
@@ -0,0 +1,430 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap.h"
+
+static int no_plan = 0;
+static int skip_all = 0;
+static int have_plan = 0;
+static unsigned int test_count = 0; /* Number of tests that have been run */
+static unsigned int e_tests = 0; /* Expected number of tests to run */
+static unsigned int failures = 0; /* Number of tests that failed */
+static char *todo_msg = NULL;
+static char *todo_msg_fixed = "libtap malloc issue";
+static int todo = 0;
+static int test_died = 0;
+
+/* Encapsulate the pthread code in a conditional.  In the absence of
+   libpthread the code does nothing */
+#ifdef HAVE_LIBPTHREAD
+#include <pthread.h>
+static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK pthread_mutex_lock(&M);
+# define UNLOCK pthread_mutex_unlock(&M);
+#else
+# define LOCK
+# define UNLOCK
+#endif
+
+static void _expected_tests(unsigned int);
+static void _tap_init(void);
+static void _cleanup(void);
+
+/*
+ * Generate a test result.
+ *
+ * ok -- boolean, indicates whether or not the test passed.
+ * test_name -- the name of the test, may be NULL
+ * test_comment -- a comment to print afterwards, may be NULL
+ */
+unsigned int
+_gen_result(int ok, const char *func, char *file, unsigned int line,
+            char *test_name, ...)
+{
+    va_list ap;
+    char *local_test_name = NULL;
+    char *c;
+    int name_is_digits;
+
+    LOCK;
+
+    test_count++;
+
+    /* Start by taking the test name and performing any printf()
+       expansions on it */
+    if(test_name != NULL) {
+        va_start(ap, test_name);
+        vasprintf(&local_test_name, test_name, ap);
+        va_end(ap);
+
+        /* Make sure the test name contains more than digits
+           and spaces.  Emit an error message and exit if it
+           does */
+        if(local_test_name) {
+            name_is_digits = 1;
+            for(c = local_test_name; *c != '\0'; c++) {
+                if(!isdigit(*c) && !isspace(*c)) {
+                    name_is_digits = 0;
+                    break;
+                }
+            }
+
+            if(name_is_digits) {
+                diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
+                diag("    Very confusing.");
+            }
+        }
+    }
+
+    if(!ok) {
+        printf("not ");
+        failures++;
+    }
+
+    printf("ok %d", test_count);
+
+    if(test_name != NULL) {
+        printf(" - ");
+
+        /* Print the test name, escaping any '#' characters it
+           might contain */
+        if(local_test_name != NULL) {
+            flockfile(stdout);
+            for(c = local_test_name; *c != '\0'; c++) {
+                if(*c == '#')
+                    fputc('\\', stdout);
+                fputc((int)*c, stdout);
+            }
+            funlockfile(stdout);
+        } else { /* vasprintf() failed, use a fixed message */
+            printf("%s", todo_msg_fixed);
+        }
+    }
+
+    /* If we're in a todo_start() block then flag the test as being
+       TODO.  todo_msg should contain the message to print at this
+       point.  If it's NULL then asprintf() failed, and we should
+       use the fixed message.
+
+       This is not counted as a failure, so decrement the counter if
+       the test failed. */
+    if(todo) {
+        printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
+        if(!ok)
+            failures--;
+    }
+
+    printf("\n");
+
+    if(!ok) {
+        if(getenv("HARNESS_ACTIVE") != NULL)
+            fputs("\n", stderr);
+
+        diag("    Failed %stest (%s:%s() at line %d)",
+             todo ? "(TODO) " : "", file, func, line);
+    }
+    free(local_test_name);
+
+    UNLOCK;
+
+    /* We only care (when testing) that ok is positive, but here we
+       specifically only want to return 1 or 0 */
+    return ok ? 1 : 0;
+}
+
+/*
+ * Initialise the TAP library.  Will only do so once, however many times it's
+ * called.
+ */
+void
+_tap_init(void)
+{
+    static int run_once = 0;
+
+    if(!run_once) {
+        atexit(_cleanup);
+
+        /* stdout needs to be unbuffered so that the output appears
+           in the same place relative to stderr output as it does 
+           with Test::Harness */
+        setbuf(stdout, 0);
+        run_once = 1;
+    }
+}
+
+/*
+ * Note that there's no plan.
+ */
+int
+plan_no_plan(void)
+{
+
+    LOCK;
+
+    _tap_init();
+
+    if(have_plan != 0) {
+        fprintf(stderr, "You tried to plan twice!\n");
+        test_died = 1;
+        UNLOCK;
+        exit(255);
+    }
+
+    have_plan = 1;
+    no_plan = 1;
+
+    UNLOCK;
+
+    return 1;
+}
+
+/*
+ * Note that the plan is to skip all tests
+ */
+int
+plan_skip_all(char *reason)
+{
+
+    LOCK;
+
+    _tap_init();
+
+    skip_all = 1;
+
+    printf("1..0");
+
+    if(reason != NULL)
+        printf(" # Skip %s", reason);
+
+    printf("\n");
+
+    UNLOCK;
+
+    exit(0);
+}
+
+/*
+ * Note the number of tests that will be run.
+ */
+int
+plan_tests(unsigned int tests)
+{
+
+    LOCK;
+
+    _tap_init();
+
+    if(have_plan != 0) {
+        fprintf(stderr, "You tried to plan twice!\n");
+        test_died = 1;
+        UNLOCK;
+        exit(255);
+    }
+
+    if(tests == 0) {
+        fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
+        test_died = 1;
+        UNLOCK;
+        exit(255);
+    }
+
+    have_plan = 1;
+
+    _expected_tests(tests);
+
+    UNLOCK;
+
+    return e_tests;
+}
+
+unsigned int
+diag(char *fmt, ...)
+{
+    va_list ap;
+
+    fputs("# ", stderr);
+
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+
+    fputs("\n", stderr);
+
+    return 0;
+}
+
+void
+_expected_tests(unsigned int tests)
+{
+
+    printf("1..%d\n", tests);
+    e_tests = tests;
+}
+
+int
+skip(unsigned int n, char *fmt, ...)
+{
+    va_list ap;
+    char *skip_msg;
+
+    LOCK;
+
+    va_start(ap, fmt);
+    asprintf(&skip_msg, fmt, ap);
+    va_end(ap);
+
+    while(n-- > 0) {
+        test_count++;
+        printf("ok %d # skip %s\n", test_count,
+               skip_msg != NULL ?
+               skip_msg : "libtap():malloc() failed");
+    }
+
+    free(skip_msg);
+
+    UNLOCK;
+
+    return 1;
+}
+
+void
+todo_start(char *fmt, ...)
+{
+    va_list ap;
+
+    LOCK;
+
+    va_start(ap, fmt);
+    vasprintf(&todo_msg, fmt, ap);
+    va_end(ap);
+
+    todo = 1;
+
+    UNLOCK;
+}
+
+void
+todo_end(void)
+{
+
+    LOCK;
+
+    todo = 0;
+    free(todo_msg);
+
+    UNLOCK;
+}
+
+int
+exit_status(void)
+{
+    int r;
+
+    LOCK;
+
+    /* If there's no plan, just return the number of failures */
+    if(no_plan || !have_plan) {
+        UNLOCK;
+        return failures;
+    }
+
+    /* Ran too many tests?  Return the number of tests that were run
+       that shouldn't have been */
+    if(e_tests < test_count) {
+        r = test_count - e_tests;
+        UNLOCK;
+        return r;
+    }
+
+    /* Return the number of tests that failed + the number of tests
+       that weren't run */
+    r = failures + e_tests - test_count;
+    UNLOCK;
+
+    return r;
+}
+
+/*
+ * Cleanup at the end of the run, produce any final output that might be
+ * required.
+ */
+void
+_cleanup(void)
+{
+
+    LOCK;
+
+    /* If plan_no_plan() wasn't called, and we don't have a plan,
+       and we're not skipping everything, then something happened
+       before we could produce any output */
+    if(!no_plan && !have_plan && !skip_all) {
+        diag("Looks like your test died before it could output anything.");
+        UNLOCK;
+        return;
+    }
+
+    if(test_died) {
+        diag("Looks like your test died just after %d.", test_count);
+        UNLOCK;
+        return;
+    }
+
+
+    /* No plan provided, but now we know how many tests were run, and can
+       print the header at the end */
+    if(!skip_all && (no_plan || !have_plan)) {
+        printf("1..%d\n", test_count);
+    }
+
+    if((have_plan && !no_plan) && e_tests < test_count) {
+        diag("Looks like you planned %d %s but ran %d extra.",
+             e_tests, e_tests == 1 ? "test" : "tests", test_count - e_tests);
+        UNLOCK;
+        return;
+    }
+
+    if((have_plan || !no_plan) && e_tests > test_count) {
+        diag("Looks like you planned %d %s but only ran %d.",
+             e_tests, e_tests == 1 ? "test" : "tests", test_count);
+        UNLOCK;
+        return;
+    }
+
+    if(failures)
+        diag("Looks like you failed %d %s of %d.",
+             failures, failures == 1 ? "test" : "tests", test_count);
+
+    UNLOCK;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/tap.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/tap.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/src/tap.h	(revision 22322)
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* '## __VA_ARGS__' is a gcc'ism. C99 doesn't allow the token pasting
+   and requires the caller to add the final comma if they've ommitted
+   the optional arguments */
+#ifdef __GNUC__
+# define ok(e, test, ...) ((e) ?					\
+			   _gen_result(1, __func__, __FILE__, __LINE__,	\
+				       test, ## __VA_ARGS__) :		\
+			   _gen_result(0, __func__, __FILE__, __LINE__,	\
+				       test, ## __VA_ARGS__))
+
+# define ok1(e) ((e) ?							\
+		 _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+		 _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(test, ...) ok(1, test, ## __VA_ARGS__);
+# define fail(test, ...) ok(0, test, ## __VA_ARGS__);
+
+# define skip_start(test, n, fmt, ...)			\
+	do {						\
+		if((test)) {				\
+			skip(n, fmt, ## __VA_ARGS__);	\
+			continue;			\
+		}
+#elif __STDC_VERSION__ >= 199901L /* __GNUC__ */
+# define ok(e, ...) ((e) ?						\
+		     _gen_result(1, __func__, __FILE__, __LINE__,	\
+				 __VA_ARGS__) :				\
+		     _gen_result(0, __func__, __FILE__, __LINE__,	\
+				 __VA_ARGS__))
+
+# define ok1(e) ((e) ?							\
+		 _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+		 _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(...) ok(1, __VA_ARGS__);
+# define fail(...) ok(0, __VA_ARGS__);
+
+# define skip_start(test, n, ...)			\
+	do {						\
+		if((test)) {				\
+			skip(n,  __VA_ARGS__);		\
+			continue;			\
+		}
+#else /* __STDC_VERSION__ */
+# error "Needs gcc or C99 compiler for variadic macros."
+#endif /* __STDC_VERSION__ */
+
+#define skip_end() } while(0);
+
+unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...);
+
+int plan_no_plan(void);
+int plan_skip_all(char *);
+int plan_tests(unsigned int);
+
+unsigned int diag(char *, ...);
+
+int skip(unsigned int, char *, ...);
+
+void todo_start(char *, ...);
+void todo_end(void);
+
+int exit_status(void);
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/Makefile.am	(revision 22322)
@@ -0,0 +1,7 @@
+SUBDIRS=	diag
+SUBDIRS+=	fail
+SUBDIRS+=	ok
+SUBDIRS+=	pass
+SUBDIRS+=	plan
+SUBDIRS+=	skip
+SUBDIRS+=	todo
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/README	(revision 22322)
@@ -0,0 +1,12 @@
+Most of the tests follow the same pattern.
+
+ * test.pl that uses Test::More, and demonstrates whatever functionality 
+   that we're trying to test.  This is the reference code.
+
+ * test.c, which tests the libtap reimplementation of the same functionality.
+
+ * test.t, which compiles the .c program, runs both test scripts, and then 
+   diffs their output to make sure it's identical.
+
+   Right now, test.t is identical in every directory.  This sucks somewhat.
+   It should either be a symlink to a common script
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	plan_tests(2);
+
+	rc = diag("A diagnostic message");
+	diag("Returned: %d", rc);
+
+	/* Make sure the failure is passed through */
+	ok(1, "test 1") || diag("ok() failed, and shouldn't");
+	ok(0, "test 2") || diag("ok() passed, and shouldn't");
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/test.pl	(revision 22322)
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+plan tests => 2;
+
+$rc = diag("A diagnostic message");
+diag("Returned: $rc");
+
+ok(1, 'test 1') or diag "ok() failed, and shouldn't";
+ok(0, 'test 2') or diag "ok() passed, and shouldn't";
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/diag/test.t	(revision 22322)
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+echo '1..7'
+
+perl $srcdir/test.pl 2>/dev/null >test.pl.out
+perlstatus=$?
+
+./test > test.c.out 2>&1
+cstatus=$?
+
+if grep "^# A diagnostic message$" test.c.out > /dev/null ; then
+    echo "ok 1 - found a diagnostic message"
+else
+    echo "not ok 1 - found a diagnostic message"
+    retval=1
+fi
+
+if grep "^# Returned: 0$" test.c.out > /dev/null ; then
+    echo "ok 2 - diag() expected return value" 
+else
+    echo "not ok 2 - diag() expected return value" 
+    retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 43)$" test.c.out > /dev/null ; then
+    echo "ok 3 - 'failed test at line' diag" 
+else
+    echo "not ok 3 - 'failed test at line' diag" 
+    retval=1
+fi
+
+if grep "^# ok() passed, and shouldn't$" test.c.out > /dev/null ; then
+    echo "ok 4 - expected diag"
+else
+    echo "ok 4 - expected diag"
+    retval=1
+fi
+
+if grep "^# Looks like you failed 1 test of 2.$" test.c.out > /dev/null ; then
+    echo "ok 5 - failed 1 test"
+ else
+    echo "ok 5 - failed 1 test"
+    retval=1
+fi
+
+sed -e '/^#/D' test.c.out > tmp
+mv tmp test.c.out
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 6 - TAP output is identical'
+else
+	retval=1
+	echo 'not ok 6 - TAP output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 7 - status code'
+else
+	retval=1
+	echo 'not ok 7 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/.cvsignore	(revision 22322)
@@ -0,0 +1,15 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(2);
+	diag("Returned: %d", rc);
+
+	rc = fail("test to fail");
+	diag("Returned: %d", rc);
+
+	rc = fail("test to fail %s", "with extra string");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 2;
+diag("Returned: " . sprintf('%d', $rc));
+
+$rc = fail('test to fail');
+diag("Returned: $rc");
+
+$rc = fail('test to fail with extra string');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/fail/test.t	(revision 22322)
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+echo '1..8'
+
+perl $srcdir/test.pl 2>/dev/null > test.pl.out
+perlstatus=$?
+
+./test > test.c.out 2>&1 
+cstatus=$?
+
+if grep "^# Returned: 2$" test.c.out >/dev/null ; then
+  echo "ok 1 - expected return value";
+else
+  echo "not ok 1 - expected return value";
+  retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 39)$" test.c.out >/dev/null ; then
+  echo "ok 2 - failed expected test";
+else
+  echo "not ok 2 - failed expected test";
+  retval=1
+fi
+
+if grep "^# Returned: 0$" test.c.out >/dev/null ; then
+  echo "ok 3 - expected return value";
+else
+  echo "not ok 3 - expected return value";
+  retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 42)$" test.c.out >/dev/null ; then
+  echo "ok 4 - failed expected test";
+else
+  echo "not ok 4 - failed expected test";
+  retval=1
+fi
+  
+if grep "^# Returned: 0$" test.c.out >/dev/null ; then
+  echo "ok 5 - expected return value";
+else
+  echo "not ok 5 - expected return value";
+  retval=1
+fi
+
+if grep "^# Looks like you failed 2 tests of 2.$" test.c.out >/dev/null ; then
+  echo "ok 6 - expected return value";
+else
+  echo "not ok 6 - expected return value";
+  retval=1
+fi
+
+sed -e '/^#/D' test.c.out > tmp
+mv tmp test.c.out
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 7 - output is identical'
+else
+	retval=1
+	echo 'not ok 7 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 8 - status code'
+else
+	retval=1
+	echo 'not ok 8 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS  =	ok
+SUBDIRS +=	ok-hash
+SUBDIRS +=	ok-numeric
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/test.c	(revision 22322)
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(4);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Test with no hash");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Test with one # hash");
+	diag("Returned: %d", rc);
+
+        rc = ok(1, "Test with # two # hashes");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Test with ## back to back hashes");
+	diag("Returned: %d", rc);
+	
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/test.pl	(revision 22322)
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 4;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1, 'Test with no hash');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with one # hash');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with # two # hashes');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with ## back to back hashes');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-hash/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(3);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "First test");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "1");
+	diag("Returned: %d", rc);
+
+	rc = ok(1, "Third test");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/test.pl	(revision 22322)
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 3;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1, 'First test');
+diag("Returned: $rc");
+
+$rc = ok(1, '1');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Third test');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok-numeric/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/test.c	(revision 22322)
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(5);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals 1");
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals %d", 1);
+	diag("Returned: %d", rc);
+
+	rc = ok1(1 == 1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 2, "1 equals 2");
+	diag("Returned: %d", rc);
+
+	rc = ok1(1 == 2);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/test.pl	(revision 22322)
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 1, '1 equals 1'); # Used for %d testing in test.c
+diag("Returned: $rc");
+
+$rc = ok(1 == 1, '1 == 1');	# Test ok1() passes when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 2, '1 equals 2');	# Test ok() fails when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 2, '1 == 2');	# Test ok1() fails when it should
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/ok/ok/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(2);
+	diag("Returned: %d", rc);
+
+	rc = pass("test to pass");
+	diag("Returned: %d", rc);
+
+	rc = pass("test to pass %s", "with extra string");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 2;
+diag("Returned: " . sprintf('%d', $rc));
+
+$rc = pass('test to pass');
+diag("Returned: $rc");
+
+$rc = pass('test to pass with extra string');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/pass/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/Makefile.am	(revision 22322)
@@ -0,0 +1,7 @@
+SUBDIRS  =	no-tests
+SUBDIRS +=	no_plan
+SUBDIRS +=	not-enough-tests
+SUBDIRS +=	too-many-plans
+SUBDIRS +=	too-many-tests
+SUBDIRS +=	sane
+SUBDIRS +=	skip_all
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(0);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 0;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no-tests/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_no_plan();
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+my $rc = 0;
+
+use Test::More;
+
+$rc = plan qw(no_plan);
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/no_plan/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/test.pl	(revision 22322)
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/not-enough-tests/test.t	(revision 22322)
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	# comment this out until we're exit-code compatible with Test::More
+	#retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/sane/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/plan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/plan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/plan.c	(revision 22322)
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap.h"
+
+/* Run pre-defined tests on the test library to make sure that the basic
+   functionality works, and it can be used to test itself afterwards */
+
+int
+main(int argc, char *argv[])
+{
+	plan_skip_all("No good reason");
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/test.c	(revision 22322)
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_skip_all("No good reason");
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/test.pl	(revision 22322)
@@ -0,0 +1,11 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan skip_all => "No good reason";
+diag("Returned: " . sprintf("%d", $rc));
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/skip_all/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = plan_tests(1);
+	diag("Returned: %d", rc);
+
+	rc = ok(0, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/test.pl	(revision 22322)
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = plan tests => 1;
+diag("Returned: $rc");
+
+$rc = ok(0);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-plans/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+
+	rc = plan_tests(5);
+	diag("Returned: %d", rc);
+
+	rc = ok(1, NULL);
+	diag("Returned: %d", rc);
+
+	rc = ok(0, NULL);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(0);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/plan/too-many-tests/test.t	(revision 22322)
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+    # we're not exit-status compatible with Test::More yet
+	#retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/test.c	(revision 22322)
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+	unsigned int side_effect = 0;
+
+	rc = plan_tests(4);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals 1");	/* Should always work */
+	diag("Returned: %d", rc);
+
+	do {
+		if(1) {
+			rc = skip(1, "Testing skipping");
+			continue;
+		}
+		
+		side_effect++;
+
+		ok(side_effect == 1, "side_effect checked out");
+
+	} while(0);
+
+	diag("Returned: %d", rc);
+
+	skip_start(1 == 1, 1, "Testing skipping #2");
+
+	side_effect++;
+	rc = ok(side_effect == 1, "side_effect checked out");
+	diag("Returned: %d", rc);
+
+	skip_end();
+
+	rc = ok(side_effect == 0, "side_effect is %d", side_effect);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/test.pl	(revision 22322)
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 4;
+diag("Returned: " . sprintf("%d", $rc));
+
+my $side_effect = 0;		# Check whether skipping has side effects
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+# Start skipping
+SKIP: {
+	$rc = skip "Testing skipping", 1;
+
+	$side_effect++;
+
+	$rc = ok($side_effect == 1, '$side_effect checked out');
+}
+
+diag("Returned: $rc");
+
+SKIP: {
+	$rc = skip "Testing skipping #2", 1;
+	diag("Returned: $rc");
+
+	$side_effect++;
+
+	$rc = ok($side_effect == 1, '$side_effect checked out');
+	diag("Returned: $rc");
+}
+
+$rc = ok($side_effect == 0, "side_effect is $side_effect");
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/skip/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/test.c	(revision 22322)
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+	unsigned int rc = 0;
+	unsigned int side_effect = 0;
+
+	rc = plan_tests(5);
+	diag("Returned: %d", rc);
+
+	rc = ok(1 == 1, "1 equals 1");	/* Should always work */
+	diag("Returned: %d", rc);
+
+	todo_start("For testing purposes");
+
+	side_effect++;
+
+	/* This test should fail */
+	rc = ok(side_effect == 0, "side_effect checked out");
+	diag("Returned: %d", rc);
+
+	/* This test should unexpectedly succeed */
+	rc = ok(side_effect == 1, "side_effect checked out");
+	diag("Returned: %d", rc);
+
+	todo_end();
+
+	todo_start("Testing printf() %s in todo_start()", "expansion");
+
+	rc = ok(0, "dummy test");
+	diag("Returned: %d", rc);
+
+	todo_end();
+
+	rc = ok(side_effect == 1, "side_effect is %d", side_effect);
+	diag("Returned: %d", rc);
+
+	return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/test.pl	(revision 22322)
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+my $side_effect = 0;		# Check whether TODO has side effects
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+# Start TODO tests
+TODO: {
+	local $TODO = 'For testing purposes';
+
+	$side_effect++;
+
+	# This test should fail
+	$rc = ok($side_effect == 0, 'side_effect checked out');
+	diag("Returned: $rc");
+
+	# This test should unexpectedly succeed
+	$rc = ok($side_effect == 1, 'side_effect checked out');
+	diag("Returned: $rc");
+}
+
+TODO: {
+	local $TODO = 'Testing printf() expansion in todo_start()';
+
+	$rc = ok(0, 'dummy test');
+	diag("Returned: $rc");
+}
+
+$rc = ok($side_effect == 1, "side_effect is $side_effect");
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/tap/tests/todo/test.t	(revision 22322)
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+# Test:;More prints diagnostic from TODO tests on stdout
+# http://rt.cpan.org/Ticket/Display.html?id=14982
+sed '/^#/D' test.pl.out > tmp
+mv tmp test.pl.out
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psLib/test/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/test.pl	(revision 22322)
@@ -0,0 +1,31 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: test.pl,v 1.2 2006-09-12 20:27:07 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all);
+
+use vars qw($VERSION);
+$VERSION = '0.01';
+
+use File::Find::Rule;
+use Cwd;
+
+my $rule = File::Find::Rule->new;
+# ignore .lib directories
+$rule->or($rule->new
+        ->directory
+        ->name('.libs')
+        ->prune
+        ->discard,
+        $rule->new
+    );
+$rule->name(qr/^tap_[^.]*$/)
+        ->maxdepth(2)
+        ->relative;
+
+my @test_files = $rule->in(getcwd());
+
+system("prove @test_files");
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/.cvsignore	(revision 22322)
@@ -0,0 +1,79 @@
+temp
+.deps
+.libs
+Makefile
+Makefile.in
+header_1.fits
+header_2.fits
+mdcfgwrt.out
+table10.dat
+table2.dat
+table3.dat
+tableF32.dat
+tableS32.dat
+tableU8.dat
+test.config
+test1.config
+test2.config
+test3.config
+test4.config
+test5.config
+tst_psArray
+tst_psBitSet
+tst_psHash00
+tst_psHash01
+tst_psHash02
+tst_psHash03
+tst_psHash04
+tst_psHash05
+tst_psList
+tst_psLookupTable_01
+tst_psMetadataIO
+tst_psMetadata_01
+tst_psMetadata_02
+tst_psMetadata_03
+tst_psMetadata_04
+tst_psMetadata_05
+tst_psMetadata_06
+tst_psMetadata_07
+tst_psPixels
+MDCopy.in
+MDCopy.out
+tst_psArguments
+mdcfgwrt.verified
+
+tap_psListIterator
+tap_psMetadataIterator
+tap_psMetadataConfigParse
+tap_psMetadataConfigParse.config
+*.bb
+*.bbg
+*.da
+gmon.out
+
+tap_psArguments_all
+tap_psArray_all
+tap_psBitSet_all
+tap_psHash_845
+tap_psHash_all
+tap_psList_all
+tap_psLookupTable_all
+tap_psMetadataConfigFormat
+tap_psMetadataConfigParse_time
+tap_psMetadataConfigPrint
+tap_psMetadataConfigRead
+tap_psMetadataConfigWrite
+tap_psMetadataConfig_input
+tap_psMetadataConfig_output
+tap_psMetadataItemCompare
+tap_psMetadataItemParse
+tap_psMetadata_copying
+tap_psMetadata_creating
+tap_psMetadata_manip
+tap_psMetadata_polynomials
+tap_psMetadata_printing
+tap_psPixels_all
+
+tap_psMetadataUpdate
+tap_psMetadataOverlay
+tap_psTree
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/Makefile.am	(revision 22322)
@@ -0,0 +1,112 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSLIB_CFLAGS)
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpslib.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSLIB_LIBS)
+AM_CFLAGS = -DXML_CONFIG_FILE="\"$(top_srcdir)/etc/pslib/psTime.xml\""
+
+TEST_PROGS = \
+	tap_psMetadataIterator \
+	tap_psListIterator \
+	tap_psMetadataConfigRead \
+	tap_psMetadataConfigParse_time \
+	tap_psMetadataItemCompare \
+	tap_psMetadataItemParse \
+	tap_psMetadataUpdate \
+	tap_psMetadataOverlay \
+	tap_psMetadata_printing \
+	tap_psMetadata_copying \
+	tap_psMetadata_creating \
+	tap_psMetadata_manip   \
+	tap_psMetadata_polynomials \
+	tap_psArray_all \
+	tap_psArguments_all \
+	tap_psPixels_all \
+	tap_psHash_all \
+	tap_psBitSet_all \
+	tap_psList_all \
+	tap_psLookupTable_all \
+	tap_psMetadataConfigWrite \
+	tap_psMetadataConfigPrint \
+	tap_psMetadataConfigParse \
+	tap_psMetadataConfigFormat \
+	tap_psMetadataConfig_input \
+	tap_psHash_845	\
+	tap_psTree
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+EXTRA_DIST = \
+	data/header_1.fits \
+	data/header_2.fits \
+	data/mdcfgwrt.verified \
+	data/test.config \
+	data/test1.config \
+	data/test2.config \
+	data/test3.config \
+	data/test4.config \
+	data/test5.config
+
+tmp_files = \
+	header_1.fits \
+	header_2.fits \
+	mdcfgwrt.verified \
+	test.config \
+	test1.config \
+	test2.config \
+	test3.config \
+	test4.config \
+	test5.config
+
+CLEANFILES = $(tmp_files) multi.fits table.fits temp/* MDCopy.in MDCopy.out mdcfgwrt.out \
+	core core.* *~ *.bb *.bbg *.da gmon.out
+
+tests: $(check_PROGRAMS)
+	$(top_srcdir)/test/test.pl
+
+# XXX what is the point of verbatim copying these files?
+
+%.dat: verified/%.dat
+	cp $? $@
+
+%.config: $(srcdir)/data/%.config
+	cp $? $@
+
+#test.config: $(srcdir)/data/test.config
+#	cp $? $@
+
+#test1.config: $(srcdir)/data/test1.config
+#	cp $? $@
+
+#test2.config: $(srcdir)/data/test2.config
+#	cp $? $@
+
+#test3.config: $(srcdir)/data/test3.config
+#	cp $? $@
+
+#test4.config: $(srcdir)/data/test4.config
+#	cp $? $@
+
+#test5.config: $(srcdir)/data/test5.config
+#	cp $? $@
+
+#header_1.fits: $(srcdir)/data/header_1.fits
+#	cp $? $@
+
+#header_2.fits: $(srcdir)/data/header_2.fits
+#	cp $? $@
+
+mdcfgwrt.verified: $(srcdir)/data/mdcfgwrt.verified
+	cp $? $@
+
+test: check
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/data/header_1.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/data/header_1.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/data/header_1.fits	(revision 22322)
@@ -0,0 +1,1 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                    8 / number of bits per data pixel                  NAXIS   =                    0 / number of data axes                            HISTORY File modified by user 'harman' with fv  on 2004-08-04T11:11:18          END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/data/header_2.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/data/header_2.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/data/header_2.fits	(revision 22322)
@@ -0,0 +1,1 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -64 / number of bits per data pixel                  NAXIS   =                    1 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -64 / number of bits per data pixel                  NAXIS   =                    1 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     BITPIX  =                  -64 / number of bits per data pixel                  EXTNAME = 'MY_DATA_1'                                                           HISTORY File modified by user 'harman' with fv  on 2004-08-04T13:14:45          HISTORY File modified by user 'harman' with fv  on 2004-08-04T13:15:29          HISTORY File modified by user 'harman' with fv  on 2004-08-04T13:21:18          HISTORY File modified by user 'harman' with fv  on 2004-08-04T13:21:46          END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -64 / number of bits per data pixel                  NAXIS   =                    1 / number of data axes                            NAXIS1  =                   64 / length of data axis 1                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     EXTNAME = 'MY_DATA_2'                                                           HISTORY File modified by user 'harman' with fv  on 2004-08-04T13:22:12          HISTORY File modified by user 'harman' with fv  on 2004-08-04T13:22:52          END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/data/mdcfgwrt.verified
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/data/mdcfgwrt.verified	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/data/mdcfgwrt.verified	(revision 22322)
@@ -0,0 +1,18 @@
+item1 BOOL  TRUE #I am a boolean 
+item2 S32  55  
+item3 F32  3.14  
+item4 F64  6.28  
+item5 STR  GNIRTS  #I am a string 
+@vector6 S32 1 2 3 4 5  #I am a vector 
+time01 PS_TIME_TAI  1000, 25, T  #I am time 
+
+metadata7  METADATA  
+   ITEM01 S32  666  
+   META NEW  METADATA  
+      @VECTORNEW S32 1 2 3 4 5  #Newest VECTOR 
+      cell STR  pStArRs  #I am a p-Star 
+   END   #I AM Newest METADATA
+   ITEM02 F32  666.6  #I AM FLOAT 
+   ITEM03 F64  666.666  #I AM DOUBLE 
+END   #I am a metadata
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test.config	(revision 22322)
@@ -0,0 +1,128 @@
+##########################################################################################################
+#  test.config
+#
+#  This is a sample metadata configuration file used to test the metadata configuration file parser. 
+#  It tests both positive and negative cases as described in the comments below.
+#
+#  author  Ross Harman, MHPCC
+#
+#  version $Revision: 1.1 $  $Name: not supported by cvs2svn $
+#  date  $Date: 2005-07-13 02:47:01 $
+#
+#  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+##########################################################################################################
+#
+#
+#
+########## Test 1 ##########
+# Lines beginning with # are comments. The line below, as well as those above are comments. They should be ignored by the parser.
+# I am a comment.
+#
+#
+#
+########## Test 2 ##########
+# The line below is a comment that begins with some whitespace. It should be ignored by the parser.
+      #        		Another comment
+#
+#
+#
+########## Test 3 ##########
+# The line below is completely blank except for a carriage return. It should be ignored by the parser.
+
+#
+#
+#
+########## Test 4 ##########
+# The line below consists of spaces and tabs (whitespace). It should be ignored by the parser.
+     	   			                    
+#
+#
+#
+########## Test 5 ##########
+# The line below consists of a variable name, type, value, and trailing comment. It should be read by the parser
+pi          F64         3.1415926535897932384626433832795029        # Definition of pi
+#
+#
+#
+########## Test 6 ##########
+# The line below consists of a variable name, type, and value, but without a trailing comment. It should be read by the parser.
+altitude          S32         10000   
+#
+#
+#
+########## Test 7 ##########
+# The lines below contain variations in name, type, value, trailing comment, and whitespace. They should all be read by the parser.
+   time F32 1234.5678 
+ myBool     BOOL            F           # F, f, 0, T, t, 1 are also acceptable
+title1  STR   Hello world #This is a comment for the string value
+    title2  STRING   Good bye world   #    STRING or STR may be used as the string type 
+#
+#
+#
+########## Test 8 ##########
+# The line below creates a vector. It should be read by the parser.
+@primes     S32     2, 3, 5, 7, 11, 13, 17   # These are prime numbers
+#
+#
+#
+########## Test 9 ##########
+# The line below creates a vector with commas mixed with spaces in the values. It should be read by the parser.
+@negatives  S32     -1,  -2 -3    -4, -5, -6,      -7
+#
+#
+#
+########## Test 10  ##########
+# The lines below should create a folder node with children that are strings. They should be read by the parser.
+comment *STR This
+comment *STR     is
+comment *STR         an
+comment *STR             ugly
+comment *STR                 comment
+comment *STR                         but
+comment *STR                             still
+comment *STR                                 valid
+#
+#
+#
+########## Test 11  ##########
+# The line below contains an incorrect value. It should produce an error.
+xPosition    F64          9876.54qqq32
+#
+#
+#
+########## Test 12  ##########
+# The line below contains no value. It should produce an error.
+yPosition    F64          
+#
+#
+#
+########## Test 13  ##########
+# The line below contains no type. It should produce an error.
+zPosition       99.999          
+#
+#
+#
+########## Test 14  ##########
+# The line below contains two * characters. It should produce an error.
+aPosition   **          
+#
+#
+#
+########## Test 15 ##########
+# The line below contains two @ characters. It should produce an error.
+@@bPosition     F64     22.33             
+#
+#
+#
+########## Test 16 ##########
+# The line below contains a ~ character used for special processing. It should produce an error.
+range     F64     140.0 ~             
+#
+#
+#
+########## Test 17 ##########
+# The variable name below is repeated. It should produce an error if the overwrite function argument is set to false.
+# If overwrite is true, then the last name and value will be stored and there will be no error.
+speed     F32     55.55
+speed     F32     66.66
+             
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test1.config	(revision 22322)
@@ -0,0 +1,57 @@
+Double	F64     1.23456789      # This is a comment
+Float     F32 0.98765#This is a comment too
+String   STR This is the string that forms the value #comment
+
+ # This is a comment line and is to be ignored
+boolean     BOOL    T # The value of 'boolean' is 'true'
+
+@primes U8  2,3 5 7,11,13 17 #   These are prime numbers
+
+comment MULTI # The rest of this line is ignored, but 'comment' is set to be non-unique
+comment STR This
+comment STR     is
+comment STR       a
+comment STR        non-unique
+comment STR                  key
+Float F64 1.23456 # This generates a warning, and, if 'overwrite' is 'false', is ignored
+
+boolean1  BOOL   F # The value of 'boolean' is 'false'
+
+@negprimes S8 -2, -3, -5, -7,-11,-13,-17,-19
+
+@vector1 U16  0, 1, 2, 4, 8
+@vector2 U32  0, 8, 16, 32, 64, 128
+@vector3 U64  0, 64, 256,
+
+@vector4 S16  -2 -1 0 1 2
+@vector5 S32  -4, -2, 0, 2, 4, 6
+@vector6 S64  -16, -4, 0, 4 16 36 64
+
+@vector7 F32  -1.03, 1.04, -1.05, 1.06
+@vector8 F64  -2.22, 2.21 -2.20, 2.19, -2.18
+
+TYPE	CELL	EXTNAME		BIASSEC		CHIP       #
+CELL.00	CELL	CCD00		BSEC-00		CHIP.00
+CELL.01	CELL	CCD01		BSEC-01		CHIP.00
+
+MYCELL  MULTI
+MYCELL    METADATA
+  EXTNAME   STR   CCD00
+  BIASSEC   STR   BSEC-00
+  CHIP      STR   CHIP.00
+  NCELL     S32   24
+END
+MYCELL S32 123 # A number
+
+cell  METADATA
+   foo   METADATA
+       bar     STR   BAZ
+       ping    STR   PONG
+   END
+
+   EXTNAME   STR CCD00
+   BIASSEC   STR BSEC-00
+   CHIP      STR CHIP.00
+   NCELL     S32 12
+END
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test2.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test2.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test2.config	(revision 22322)
@@ -0,0 +1,44 @@
+ # This should generate error message
+boolean     BOOL    X # The value of 'boolean' is 'true'
+
+value1    S32  # No value
+
+# Invalid vector entries
+@vector  U8
+@vector1 U8  x,y,z
+@vector2
+@vector3 F8 ,,,
+
+# Multiple MULTI definitions
+comment MULTI
+comment MULTI
+
+# Type CELL not defined
+MYCELL CELL CCD00 BSEC-00 CHIP.00
+
+# Invalid value entries
+value1   F64   aabb
+value2   S32   ccdd
+
+# Repeated vector character
+@@vector4 U8 1 2 3 4 #
+
+# Invalid TYPE line
+TYPE
+
+# Duplicate TYPE
+TYPE CELL EXTNAME BIASSEC CHIP
+TYPE CELL EXTNAME BIASSEC CHIP
+
+# END with no matching METADATA
+END
+
+# Duplicate item names within metadata type specifier
+TYPE NEWCELL VALUE1 VALUE1 #
+
+# Not enough values in specified type
+OURCELL  CELL CCD00
+
+# Type with no item
+TYPE NEWCELL1
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test3.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test3.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test3.config	(revision 22322)
@@ -0,0 +1,16 @@
+Double	F64     1.23456789      # This is a comment
+Float     F32 0.98765#This is a comment too
+String   STR This is the string that forms the value #comment
+
+ # This is a comment line and is to be ignored
+boolean     BOOL    T # The value of 'boolean' is 'true'
+
+@primes U8  2,3 5 7,11,13 17 #   These are prime numbers
+
+comment MULTI # The rest of this line is ignored, but 'comment' is set to be non-unique
+comment STR This
+comment STR     is
+comment STR       a
+comment STR        non-unique
+comment STR                  key
+Float F64 1.23456 # This generates a warning, and, if 'overwrite' is 'false', is ignored
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test4.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test4.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test4.config	(revision 22322)
@@ -0,0 +1,161 @@
+#
+# This file contains examples from SDR-14 appendix A
+#
+CELL1 METADATA
+  EXTNAME  STR  CCD00
+  BIASSEC  STR  BSEC-00
+  CHIP     STR  CHIP.00
+  NCELL    S32  24
+END
+
+CELL2 METADATA
+   FOO METADATA
+       BAR   STR  BAZ
+       PING  STR  PONG
+   END
+
+   EXTNAME   STR  CCD00
+   BIASSEC   STR  BSEC-00
+   CHIP      STR  CHIP.00
+   NCELL     S32  24
+END
+
+FOO1 METADATA
+    FOO2 METADATA
+        FOO3 METADATA
+            FOO4 METADATA
+                FOO5 METADATA
+                    FOO6 METADATA
+                        BAR    STR   BAZ
+                        PING   STR   PONG
+                    END
+                    BAR   STR  BAZ
+                    PING  STR  PONG
+                END
+                BAR   STR  BAZ
+                PING  STR  PONG
+            END
+            BAR   STR   BAZ
+            PING  STR   PONG
+        END
+        BAR   STR  BAZ
+        PING  STR  PONG
+    END
+    BAR   STR  BAZ
+    PING  STR  PONG
+END
+
+FOO2 METADATA
+    BAR   STR  BAZ
+    PING  STR  PONG
+    FOO3 METADATA
+        BAR  STR  BAZ
+        PING STR  PONG
+        FOO4 METADATA
+            BAR  STR  BAZ
+            PING STR  PONG
+            FOO5 METADATA
+                BAR  STR BAZ
+                PING STR PONG
+                FOO6 METADATA
+                    BAR  STR  BAZ
+                    PING STR  PONG
+                    FOO7  METADATA
+                        BAR  STR BAZ
+                        PING STR PONG
+                    END
+                END
+            END
+        END
+    END
+END
+
+FOO3 METADATA
+    BAR  STR  BAZ
+    FOO4 METADATA
+        BAR  STR  BAZ
+        FOO5 METADATA
+            BAR  STR  BAZ
+            FOO6 METADATA
+                BAR   STR  BAZ
+                FOO7 METADATA
+                    BAR   STR   BAZ
+                    FOO8 METADATA
+                        BAR   STR  BAZ
+                        PING  STR  PONG
+                    END
+                    PING  STR  PONG
+                END
+                PING STR  PONG
+            END
+            PING STR  PONG
+        END
+        PING STR PONG
+    END
+    PING STR PONG
+END
+
+FOO4 METADATA
+    FOO5 METADATA
+        BAR  STR  BAZ
+        PING STR  PONG
+    END
+    FOO6 METADATA
+        BAR  STR  BAZ
+        PING STR  PONG
+    END
+END
+
+TYPE    CELL    EXTNAME   BIASSEC   CHIP
+CELL.00  CELL  CCD00  BSEC-00 CHIP.00
+CELL.01  CELL  CCD01  BSEC-01 CHIP.00
+FOO5 METADATA
+    TYPE CELL EXTNAME BIASSEC
+    CELL.00 CELL CCD00 BSEC-00
+    CELL.01 CELL CCD01 BSEC-01
+    FOO6 METADATA
+        TYPE CELL EXTNAME
+        CELL.00  CELL  CCD00
+        CELL.01  CELL  CCD01
+    END
+END
+
+foo MULTI
+foo S32   -1
+foo STR   bar baz
+foo BOOL  T
+
+foo1 MULTI        # foo
+foo1 S32  -1      # bar
+foo1 STR  bar baz # baz
+foo1 BOOL T       #
+
+foo2 MULTI
+foo2 S32 -1
+foo2 STR  bar baz
+foo2 BOOL  T
+bar1 METADATA
+    foo MULTI
+    foo S32  -1
+    foo STR  bar baz
+    foo BOOL T
+END
+
+foo3 MULTI
+foo3 METADATA
+    bar   BOOL T
+END
+foo3 METADATA
+    bar  BOOL T
+END
+
+foo4 MULTI
+TYPE bar a b c
+foo4 bar x y z
+foo4 bar x y z
+
+TYPE bar1 a b c
+foo5 MULTI
+foo5 bar1 x y z
+foo5 bar1 x y z
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test5.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test5.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/data/test5.config	(revision 22322)
@@ -0,0 +1,32 @@
+#
+# Examples from SDR-14 Appendix A which cause failures
+#
+TYPE  CELL1  EXTNAME  BIASSEC  CHIP
+FOO1 METADATA
+    CELL.00 CELL  CCD00  BSEC-00  CHIP.00
+END
+
+FOO2  METADATA
+    TYPE CELL2 EXTNAME BIASSEC CHIP
+END
+CELL.00 CELL2 CCD00 BSEC-00 CHIP.00
+
+FOO3 METADATA
+    TYPE CELL3 EXTNAME BIASSEC CHIP
+END
+BAR METADATA
+    CELL.00 CELL3 CCD00 BSEC-00 CHIP.00
+END
+
+TYPE CELL4 EXTNAME BIASSEC CHIP
+CELL.00 CELL4 CCD00 BSEC-00
+CELL.01 CELL4 CCD01 BSEC-01 CHIP.00
+
+TYPE CELL5 EXTNAME BIASSEC CHIP
+TYPE CELL5 EXTNAME BIASSEC CHIP
+CELL.02 CELL5 CCD00 BSEC-00 CHIP.00
+CELL.03 CELL5 CCD01 BSEC-01 CHIP.00
+
+CELL.04 CELL6 CCD00 BSEC-00 CHIP.00
+CELL.05 CELL6 CCD01 BSEC-01 CHIP.00
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/execute_tap
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/execute_tap	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/execute_tap	(revision 22322)
@@ -0,0 +1,21 @@
+make test
+./tap_psArray_all
+./tap_psListIterator
+./tap_psMetadataConfigParse_time
+./tap_psMetadataConfigRead
+./tap_psMetadata_copying
+./tap_psMetadata_creating
+./tap_psMetadataItemCompare
+./tap_psMetadataItemParse
+./tap_psMetadataIterator
+./tap_psMetadata_manip
+./tap_psMetadata_printing
+./tap_psMetadata_polynomials
+./tap_psArguments_all
+./tap_psPixels_all
+./tap_psHash_all
+./tap_psBitSet_all
+./tap_psList_all
+./tap_psLookupTable_all
+./tap_psMetadataConfig_output
+./tap_psMetadataConfig_input
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/metaconf.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/metaconf.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/metaconf.in	(revision 22322)
@@ -0,0 +1,90 @@
+#
+# Good lines
+#
+
+item1            BOOL      T                # I am a boolean
+item2            S32       55               # I am int
+item3            F32       3.14
+item4            F64       6.28             # I am float
+item5            STR       GNIRTS           # I am a string
+item7            S64       555
+item8            S8        3
+itemu1           U16       2
+itemu2           U32       55
+itemu3           U8        1
+itemu4           U64       1634
+item9            BOOL      F                # I am a boolean2
+item10           S16       16
+@vector1  S8  1 2 3 4 5   # I am a vector
+@vector2  S16 1 2 3 4 5
+@vector3  S32 1 2 3 4 5
+@vector4  S64 1 2 3 4 5
+@vector5  U8  1 2 3 4 5   # I am a vector
+@vector6  U16 1 2 3 4 5
+@vector7  U32 1 2 3 4 5
+@vector8  U64 1 1
+@vector9  F32 1.0 2.3 3.1 4.5 5.0   # I am a vector
+@vector10 F64 1.0 2.0 3.0 4.0 5.0
+time01           TT        1970-01-01T00:16:39Z  # I am time
+time02           UTC       NULL     # I am time2
+
+f32 MULTI
+f32              S32       666              # f32_1
+f32              S32       665              # f32_2
+
+f32 METADATA
+    newi S32 151
+END
+
+metanew MULTI
+meta1 METADATA
+    itemS32   S32       666              # f32_1
+END
+
+metadata7  METADATA
+   ITEM01           S32       666
+   META_NEW  METADATA
+      @VECTORNEW S32 1 2 3 4 5   # Newest VECTOR
+      cell             STR       pStArRs          # I am a p-Star
+   END  # I am a metadata
+   ITEM02           F32       666.6            # I AM FLOAT
+   ITEM03           F64       666.666          # I AM DOUBLE
+END
+#item11           C32       16+2i            # I am Complex
+
+TYPE PS_TYPE_DD D
+DD2     PS_TYPE_DD   ""
+     TYPE  C32  3
+newC32      C32 7i+5
+
+#
+# Bad lines : 28 (-1 for the GOOD line in meta1) & 3 valid duplicates
+#
+item1            BOOL      F                # duplicate
+item8            S8        5                # duplicate
+item4-1          F64       6.28 1           # I am float
+item6            S32       10 1
+item6-1          S32       ~
+item7-1          S64       555 x
+item8-1          S8        5 1
+itemu1-1         U16       2 1
+itemu2-1         U32       55 1
+itemu3-1         U8        1 2
+itemu4-1         U64       1634 1
+item10-1         S16       16 16
+@vector1-1 S8 % #
+@@@vector12 F64 1.0 2.0 3.0 4.0 5.0
+@vector11 C64 1.0 2.0 3.0 4.0 5.0
+meta1 METADATA                              # duplicate
+    M2   S32       666  # this line is "GOOD" but parsed in the top level
+END
+newDD   PS_TYPE_DD
+TYPE            1
+TYPE
+TYPE         #  # 1
+     TYPE  C32  3
+     TYPE PS_TYPE_C32 2                 #
+newC32      PS_TYPE_C32 7i+5        # duplicate but with borken type
+ps1  S32  NAN
+f32 MULTI
+\n
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/table.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/table.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/table.dat	(revision 22322)
@@ -0,0 +1,9 @@
+#  Table with valid types and index-values
+#
+#psF32     psU16    psU32     psU64    psS8    psS16    psS32    psS64    psU8   psF64
+-10.05    2        4         8        0       -2       -4       -8       5      -1.5
+ 1.009     4        8         16       -1      -4       -8       -16      0      -1.0
+23.45     6        12        24       -2      -6       -12      -24      5      -0.25
+3500.67   8        16        32       -3      -8       -16      -32      75     1.75
+   
+#
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/table2.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/table2.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/table2.dat	(revision 22322)
@@ -0,0 +1,9 @@
+#  Table with valid types and index-values
+#
+#psF32     psU16    psU32     psU64    psS8    psS16    psS32    psS64    psU8   psF64
+-10.05    2        4         8        0       -2        1       -8       5      -1.5
+ 1e-20     4        8         16       -1      -4       1       -16      0      -1.0
+2e-20     6        12        24       -2      -6        1       -24      5      -0.25
+3500.67   8        16        32       -3      -8        1      -32      75     1.75
+
+#
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/table3.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/table3.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/table3.dat	(revision 22322)
@@ -0,0 +1,9 @@
+#
+# This file is not valid metdataconfig syntax
+#
+#psF32     psU16    psU32     psU64    psS8    psS16    psS32    psS64    psU8   psF64
+-10.05    2        4         8        0       -2       -4       -8       5      -1.5
+ 1.009     4        8         16       -1      -4       -8       -16      0      -1.0
+23.45     6
+
+#
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tableF32_err.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tableF32_err.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tableF32_err.dat	(revision 22322)
@@ -0,0 +1,9 @@
+#  Table with valid types and index-values
+#
+#psF32     psU16    psU32     psU64    psS8    psS16    psS32    psS64    psU8   psF64
+-10.05    2        4         8        0       -2       -4       -8       5      -1.5
+ k.009     4        8         16       -1      -4       -8       -16      0      -1.0
+23.45     6        12        24       -2      -6       -12      -24      5      -0.25
+3500.67   8        16        32       -3      -8       -16      -32      75     1.75
+   
+#
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tableF64_err.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tableF64_err.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tableF64_err.dat	(revision 22322)
@@ -0,0 +1,9 @@
+#  Table with valid types and index-values
+#
+#psF32     psU16    psU32     psU64    psS8    psS16    psS32    psS64    psU8   psF64
+-10.05    2        4         8        0       -2       -4       -8       5      -1.5  #
+ 1.009     4        8         16       -1      -4       -8       -16      0      -1.k
+23.45     6        12        24       -2      -6       -12      -24      5      -0.25
+3500.67   8        16        32       -3      -8       -16      -32      75     1.75
+
+#
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tableS32_err.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tableS32_err.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tableS32_err.dat	(revision 22322)
@@ -0,0 +1,9 @@
+#  Table with valid types and index-values
+#
+#psF32     psU16    psU32     psU64    psS8    psS16    psS32    psS64    psU8   psF64
+-10.05    2        4         8        0       -2       -4       -8       5      -1.5
+ 1.009     4        8         16       -1      -4       -k8       -16      0      -1.0
+23.45     6        12        24       -2      -6       -12      -24      5      -0.25
+3500.67   8        16        32       -3      -8       -16      -32      75     1.75
+
+#
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tableS64_err.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tableS64_err.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tableS64_err.dat	(revision 22322)
@@ -0,0 +1,9 @@
+#  Table with valid types and index-values
+#
+#psF32     psU16    psU32     psU64    psS8    psS16    psS32    psS64    psU8   psF64
+-10.05    2        4         8        0       -2       -4       -8       5      -1.5
+ 1.009     4        8         16       -1      -4       -8       -k6      0      -1.0
+23.45     6        12        24       -2      -6       -12      -24      5      -0.25
+3500.67   8        16        32       -3      -8       -16      -32      75     1.75
+
+#
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psArguments_all.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psArguments_all.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psArguments_all.c	(revision 22322)
@@ -0,0 +1,494 @@
+/**
+ *  C Implementation: tap_psArguments_all
+ *
+ * Description:  Tests for psArgumentVerbosity, psArgumentGet, psArgumentRemove,
+ *               psArgumentParse, psArgumentHelp, psLogArguments, psTraceArguments
+ *
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+// tests which send output to the screen are silent unless DEBUG = 1
+#define DEBUG 0
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(30);
+
+    // test the failure cases for psArgumentGet, psArgumentRemove
+    {
+        psMemId id = psMemGetId();
+
+	// define a simple fake argument list : this is not modified
+        char *argv[5];
+        argv[0] = "./program";
+        argv[1] = "-string";
+        argv[2] = "new";
+        argv[3] = "-float";
+        argv[4] = "6.66";
+        int argc = 5;
+
+        // Try to get an argument's position
+        int i = psArgumentGet(argc, argv, "-float");
+        {
+            ok( i == 3,
+                "psArgumentGet:         return correct location of input argument.");
+        }
+
+        // Return 0 for attempting to get from a NULL input argument.
+        {
+            ok( psArgumentGet(argc, NULL, "-float") == 0,
+                "psArgumentGet:         return 0 for a NULL input argument.");
+        }
+
+        // Return 0 for attempting to find a NULL string
+        {
+            ok( psArgumentGet(argc, argv, NULL) == 0,
+                "psArgumentGet:         return 0 for a NULL input string.");
+        }
+
+        // Return 0 for attempting to find an empty string
+        {
+            ok( psArgumentGet(argc, argv, "") == 0,
+                "psArgumentGet:         return 0 for an empty input string.");
+        }
+
+        // Return 0 for attempting to get an argument that doesn't match
+        {
+            ok( psArgumentGet(argc, argv, "-xxx") == 0,
+                "psArgumentGet:         return 0 for an empty input string.");
+        }
+
+        // Return false for attempting to remove argnum = 0
+        {
+            ok( !psArgumentRemove(0, &argc, argv),
+                "psArgumentRemove:      return false for argnum = 0.");
+        }
+
+        // Return false for attempting to remove NULL argc
+        {
+            ok( !psArgumentRemove(0, NULL, argv),
+                "psArgumentRemove:      return false for NULL argc.");
+        }
+
+        // simple argument definitions for comparison
+        psMetadata *args = psMetadataAlloc();
+        psMetadataAdd(args, PS_LIST_TAIL, "-string", PS_DATA_STRING, "Test String", "SomeString");
+        psMetadataAdd(args, PS_LIST_TAIL, "-float", PS_DATA_F32, "Test Float", 0.666);
+        psMetadataAdd(args, PS_LIST_TAIL, "-double", PS_DATA_F64, "Test Double", 0.666);
+
+        // psArgumentParse tests
+        // Return false for NULL input arguments
+        {
+            ok( !psArgumentParse(NULL, &argc, argv),
+                "psArgumentParse:       return false for NULL argument metadata input.");
+        }
+
+        // Return false for NULL input argc
+        {
+            ok( !psArgumentParse(args, NULL, argv),
+                "psArgumentParse:       return false for NULL argc input.");
+        }
+
+        // Return false for NULL input argv
+        {
+            ok( !psArgumentParse(args, &argc, NULL),
+                "psArgumentParse:       return false for NULL argv input.");
+        }
+
+        // Return false for argc = 0
+        {
+            int tempc = 0;
+            ok( !psArgumentParse(args, &tempc, argv),
+                "psArgumentParse:       return false for argc = 1 input.");
+        }
+
+        // Return false for string not found in metadata arguments
+        {
+	    // define a simple fake argument list : this is not modified
+	    char *argvBad[5];
+	    argvBad[0] = "./program";
+	    argvBad[1] = "-string";
+	    argvBad[2] = "new";
+	    argvBad[3] = "-test";
+	    argvBad[4] = "6.66";
+	    int argcBad = 5;
+
+            ok( !psArgumentParse(args, &argcBad, argvBad),
+                "psArgumentParse:      return false for argv containing unspecified input.");
+        }
+
+        // Check for Memory leaks
+        {
+            psFree(args);
+            checkMem();
+        }
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // define a valid argument input set and definition set
+    // Return true for valid case
+    {
+        psMemId id = psMemGetId();
+
+	// define the sample arguments
+	char *argv[20];
+	argv[0] = "./program";
+	argv[1] = "-string";
+	argv[2] = "new";
+	argv[3] = "-float";
+	argv[4] = "6.66";
+	argv[5] = "-double";
+	argv[6] = "0.666";
+	argv[7] = "-int";
+	argv[8] = "8";
+	argv[9] = "16";
+	argv[10] = "32";
+	argv[11] = "64";
+	argv[12] = "-U8";
+	argv[13] = "1";
+	argv[14] = "-U16";
+	argv[15] = "666";
+	argv[16] = "-U32";
+	argv[17] = "666";
+	argv[18] = "-Bool";
+	argv[19] = "true";
+	int argc = 20;
+
+	// setup the argument definition
+	psMetadata *args = psMetadataAlloc();
+
+	// three simple types
+	psMetadataAdd(args, PS_LIST_TAIL, "-string", PS_DATA_STRING, "Test String", "SomeString");
+	psMetadataAdd(args, PS_LIST_TAIL, "-float", PS_DATA_F32, "Test Float", 0.666);
+	psMetadataAdd(args, PS_LIST_TAIL, "-double", PS_DATA_F64, "Test Double", 0.666);
+
+	// a multiple argument option : -int 1 2 3 4
+	psMetadata *ints = psMetadataAlloc();
+	psMetadataAdd(ints, PS_LIST_TAIL, "int1", PS_DATA_S8, "Int1", 8);
+	psMetadataAdd(ints, PS_LIST_TAIL, "int2", PS_DATA_S16, "Int2", 16);
+	psMetadataAdd(ints, PS_LIST_TAIL, "int3", PS_DATA_S32, "Int3", 32);
+	psMetadataAdd(ints, PS_LIST_TAIL, "int4", PS_DATA_S64, "Int4", 64);
+	psMetadataAdd(args, PS_LIST_TAIL, "-int", PS_DATA_METADATA, "Integers", ints);
+	psFree(ints);                  // Drop reference
+
+	// a multiple option example : -multi 1 -multi 2
+	psMetadataAddS32(args, PS_LIST_TAIL, "-multi", 0, "666", 2);
+	psMetadataAddS32(args, PS_LIST_TAIL, "-multi", PS_META_DUPLICATE_OK, "hello kitty", 1);
+
+	// simple psLib types
+	psMetadataAddU8(args, PS_LIST_TAIL, "-U8", 0, "U8", 1);
+	psMetadataAddU16(args, PS_LIST_TAIL, "-U16", 0, "U16", 666);
+	psMetadataAddU32(args, PS_LIST_TAIL, "-U32", 0, "U32", 666);
+	psMetadataAddU64(args, PS_LIST_TAIL, "-U64", 0, "U64", 666);
+	psMetadataAddBool(args, PS_LIST_TAIL, "-Bool", 0, "boolean", true);
+
+	// attempt to parse the above arguments using the above argument definition
+	ok( psArgumentParse(args, &argc, argv), "psArgumentParse:      return true for valid inputs.");
+
+        // Check for Memory leaks
+	psFree(args);
+	checkMem();
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+
+    }
+
+    // other basic tests
+    { 
+        psMemId id = psMemGetId();
+
+	// Return false for inconsistent argc in metadata
+	{
+	    psMetadata *tempArg = psMetadataAlloc();
+	    psMetadata *mdtemp = psMetadataAlloc();
+	    psMetadataAdd(mdtemp, PS_LIST_TAIL, "int1", PS_DATA_S8, "Int1", 8);
+	    psMetadataAdd(mdtemp, PS_LIST_TAIL, "int2", PS_DATA_S16, "Int2", 16);
+	    psMetadataAdd(mdtemp, PS_LIST_TAIL, "int3", PS_DATA_S32, "Int3", 32);
+	    psMetadataAdd(tempArg, PS_LIST_TAIL, "-int", PS_DATA_METADATA, "Integers", mdtemp);
+	    psFree(mdtemp);
+
+	    int argc = 2;
+	    char *argv[2];
+	    argv[0] = "./program";
+	    argv[1] = "-string";
+	    ok( !psArgumentParse(tempArg, &argc, argv),
+		"psArgumentParse:      return false for inconsistent argc.");
+	    psFree(tempArg);
+	}
+
+	// setup the argument definition
+	psMetadata *args = psMetadataAlloc();
+
+	// three simple types
+	psMetadataAdd(args, PS_LIST_TAIL, "-string", PS_DATA_STRING, "Test String", "SomeString");
+	psMetadataAddS32(args, PS_LIST_TAIL, "-multi", 0, "666", 2);
+	psMetadataAddS32(args, PS_LIST_TAIL, "-multi", PS_META_DUPLICATE_OK, "hello kitty", 1);
+
+	// Return false for argc = 1
+	{
+	    int argc = 2;
+	    char *argv[2];
+	    argv[0] = "./program";
+	    argv[1] = "-string";
+	    ok( !psArgumentParse(args, &argc, argv),
+		"psArgumentParse:      return false for incomplete string syntax.");
+	}
+
+	// Return true for an unfound MULTI match
+	{
+	    int argc = 3;
+	    char *argv[3];
+	    argv[0] = "./program";
+	    argv[1] = "-multi";
+	    argv[2] = "2";
+	    ok( psArgumentParse(args, &argc, argv),
+		"psArgumentParse:      return true for MULTI.");
+	}
+
+	// Check for Memory leaks
+	{
+	    psFree(args);
+	    checkMem();
+	}
+	ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test psArgumentVerbosity()
+    {
+	psMemId id = psMemGetId();
+	// Return 2 (default) for NULL input argument
+	{
+	    int argc = 1;
+	    ok( psArgumentVerbosity(&argc, NULL) == 2,
+		"psArgumentVerbosity:  return 2 for NULL argument input.");
+	}
+
+	// Return 2 for NULL argc input
+	{
+	    char *argv[1];
+	    argv[0] = "./program";
+	    ok( psArgumentVerbosity(NULL, argv) == 2,
+		"psArgumentVerbosity:  return 2 for NULL argument input.");
+	}
+
+	// Return 3 for "-v" option
+	{
+	    int argc = 2;
+	    char *argv[2];
+	    argv[0] = "./program";
+	    argv[1] = "-v";
+	    ok( psArgumentVerbosity(&argc, argv) == 3,
+		"psArgumentVerbosity:  return 3 for '-v' argument input.");
+	}
+
+	// Return 4 for "-vv" option
+	{
+	    int argc = 2;
+	    char *argv[2];
+	    argv[0] = "./program";
+	    argv[1] = "-vv";
+	    ok( psArgumentVerbosity(&argc, argv) == 4,
+		"psArgumentVerbosity:  return 4 for '-vv' argument input.");
+	}
+
+	// Return 5 for "-vvv" option
+	{
+	    int argc = 2;
+	    char *argv[2];
+	    argv[0] = "./program";
+	    argv[1] = "-vvv";
+	    ok( psArgumentVerbosity(&argc, argv) == 5,
+		"psArgumentVerbosity:  return 5 for '-vvv' argument input.");
+	}
+
+	// These routines should return 5 since that was the last level set above with -vvv
+	// Return 5 for "-logfmt" option
+	{
+	    int argc = 2;
+	    char *argv[2];
+	    argv[0] = "./program";
+	    argv[1] = "-logfmt";
+	    int rc = psArgumentVerbosity(&argc, argv);
+	    ok(rc == 5, "psArgumentVerbosity:  return 5 for '-logfmt' argument input (was %d)", rc);
+	}
+
+	// Return 5 for "-logfmt" option with "H"
+	{
+	    int argc = 3;
+	    char *argv[3];
+	    argv[0] = "./program";
+	    argv[1] = "-logfmt";
+	    argv[2] = "H";
+	    int rc = psArgumentVerbosity(&argc, argv);
+	    ok(rc == 5, "psArgumentVerbosity:  return 5 for '-logfmt H' argument inputs (was %d)", rc);
+	}
+
+	// Return 5 for "-trace" option
+	{
+	    int argc = 2;
+	    char *argv[2];
+	    argv[0] = "./program";
+	    argv[1] = "-trace";
+	    int rc = psArgumentVerbosity(&argc, argv);
+	    ok(rc == 5, "psArgumentVerbosity:  return 5 for '-trace' argument input (was %d)", rc);
+	}
+
+	// Return 5 for "-trace 1 2" option
+	{
+	    int argc = 4;
+	    char *argv[4];
+	    argv[0] = "./program";
+	    argv[1] = "-trace";
+	    argv[2] = "1";
+	    argv[3] = "2";
+	    // XXX EAM : we are getting unneeded output because of the verbosity set above.
+	    int rc = psArgumentVerbosity(&argc, argv);
+	    ok(rc == 5, "psArgumentVerbosity:  return 5 for '-trace 1 2' argument inputs (was %d", rc);
+	}
+
+	// Return 5 for "-trace-levels" option
+	{
+	    // this function sends output to the screen or /dev/null (i
+	    # if (!DEBUG)
+	    FILE *f = fopen ("/dev/null", "w");
+	    int fd = fileno(f);
+	    psTraceSetDestination (fd);
+	    # endif
+
+	    int argc = 2;
+	    char *argv[2];
+	    argv[0] = "./program";
+	    argv[1] = "-trace-levels";
+	    int rc = psArgumentVerbosity(&argc, argv);
+	    ok(rc == 5, "psArgumentVerbosity:  return 5 for '-trace-levels' argument input (was %d)", rc);
+	}
+	ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
+
+/* XXX: Why is this commented out?
+   void testLogTraceArguments(void)
+   {
+   note("  >>>Test 3:  psLogArguments & psTraceArguments Fxns");
+ 
+   // Return 2 (default) for NULL arguments input
+   {
+   int argc = 4;
+   ok( psLogArguments(&argc, NULL) == 2,
+   "psLogArguments:       return 2 for NULL argument input.");
+   }
+   // Return 2 (default) for NULL argc input
+   {
+   char *argv[1];
+   argv[0] = "./program";
+   ok( psLogArguments(NULL, argv) == 2,
+   "psLogArguments:       return 2 for NULL argc input.");
+   }
+   // Return 3 for "-v" option
+   {
+   int argc = 2;
+   char *argv[2];
+   argv[0] = "./program";
+   argv[1] = "-v";
+   ok( psLogArguments(&argc, argv) == 3,
+   "psLogArguments:       return 3 for '-v' argument input.");
+   }
+   // Return 4 for "-vv" option
+   {
+   int argc = 2;
+   char *argv[2];
+   argv[0] = "./program";
+   argv[1] = "-vv";
+   ok( psLogArguments(&argc, argv) == 4,
+   "psLogArguments:       return 4 for '-vv' argument input.");
+   }
+   // Return 5 for "-vvv" option
+   {
+   int argc = 2;
+   char *argv[2];
+   argv[0] = "./program";
+   argv[1] = "-vvv";
+   ok( psLogArguments(&argc, argv) == 5,
+   "psLogArguments:       return 5 for '-vvv' argument input.");
+   }
+   // Return 2 for "-logfmt" option
+   {
+   int argc = 2;
+   char *argv[2];
+   argv[0] = "./program";
+   argv[1] = "-logfmt";
+   ok( psLogArguments(&argc, argv) == 2,
+   "psLogArguments:       return 2 for '-logfmt' argument input.");
+   }
+   // Return 2 for "-logfmt" option with "H"
+   {
+   int argc = 3;
+   char *argv[3];
+   argv[0] = "./program";
+   argv[1] = "-logfmt";
+   argv[2] = "H";
+   ok( psLogArguments(&argc, argv) == 2,
+   "psLogArguments:       return 2 for '-logfmt H' argument inputs.");
+   }
+ 
+ 
+   // psTraceArguments Tests
+   // Return 0 (default) for NULL arguments input
+   {
+   int argc = 4;
+   ok( psTraceArguments(&argc, NULL) == 0,
+   "psTraceArguments:     return 0 for NULL argument input.");
+   }
+   // Return 0 (default) for NULL argc input
+   {
+   char *argv[1];
+   argv[0] = "./program";
+   ok( psTraceArguments(NULL, argv) == 0,
+   "psTraceArguments:     return 0 for NULL argc input.");
+   }
+   // Return 0 for "-trace" option
+   {
+   int argc = 2;
+   char *argv[2];
+   argv[0] = "./program";
+   argv[1] = "-trace";
+   ok( psTraceArguments(&argc, argv) == 0,
+   "psTraceArguments:     return 2 for '-trace' argument input.");
+   }
+   // Return 2 for "-trace 1 2" option
+   {
+   int argc = 4;
+   char *argv[4];
+   argv[0] = "./program";
+   argv[1] = "-trace";
+   argv[2] = "1";
+   argv[3] = "2";
+   ok( psTraceArguments(&argc, argv) == 1,
+   "psTraceArguments:     return 2 for '-trace 1 2' argument inputs.");
+   }
+   // Return 2 for "-trace-levels" option
+   {
+   int argc = 2;
+   char *argv[2];
+   argv[0] = "./program";
+   argv[1] = "-trace-levels";
+   ok( psTraceArguments(&argc, argv) == 0,
+   "psTraceArguments:     return 2 for '-trace-levels' argument input.");
+   }
+ 
+   // Check for Memory leaks
+   {
+   checkMem();
+   }
+   }
+*/
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psArray_all.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psArray_all.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psArray_all.c	(revision 22322)
@@ -0,0 +1,303 @@
+/**
+ *  C Implementation: tap_psArray_creating
+ *
+ * Description:  Tests for psArrayAlloc, psArrayRealloc, psMemCheckArray,
+ *               psArrayElementsFree, psArrayLength
+ *
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(31);
+
+    // testArrayAllocs()
+    {
+        psMemId id = psMemGetId();
+        psArray *a = NULL;
+
+        //Tests for psArrayAlloc
+        //Return NULL on attempting to allocate a negative size array
+        {
+            a = psArrayAlloc(-2);
+            ok( a == NULL,
+                "psArrayAlloc:           return NULL for negative array-size input.");
+        }
+        //Return NULL for negative array size - psArrayAllocEmpty
+        {
+            a = psArrayAllocEmpty(-2);
+            ok( a == NULL,
+                "psArrayAllocEmpty:      return NULL for negative array-size input.");
+        }
+        //Return properly allocated psArray
+        {
+            a = psArrayAlloc(0);
+            ok( a != NULL && psMemCheckArray(a),
+                "psArrayAlloc:           return properly allocated psArray.");
+        }
+
+        //Tests for psArrayRealloc
+        //Now try to reallocate the psArray - bigger
+        {
+            a = psArrayRealloc(a, 2);
+            ok ( psArrayLength(a) == 0 && a->nalloc == 2,
+                 "psArrayRealloc:         return properly reallocated psArray.");
+        }
+        //Return NULL when trying to reallocate a NULL psArray
+        {
+            psArray *temp = NULL;
+            temp = psArrayRealloc(temp, 1);
+            ok( temp == NULL,
+                "psArrayRealloc:         return NULL for NULL input psArray.");
+        }
+        //Attempt to reallocate the psArray - smaller
+        {
+            psS32 *s32 = (psS32*)psAlloc(sizeof(psS32));
+            *s32 = 1;
+            psS32 *s32_2 = (psS32*)psAlloc(sizeof(psS32));
+            *s32_2 = 2;
+            skip_start(  !psArraySet(a, 0, s32) || !psArraySet(a, 1, s32), 1,
+                         "Skipping 1 tests because psArraySet failed");
+            a = psArrayRealloc(a, 1);
+            *s32_2 = *((psS32*)(a->data[0]));
+            ok( a->n == 1 && a->nalloc == 1 && *s32_2 == 1,
+                "psArrayRealloc:         return properly reallocated psArray.");
+            skip_end();
+            psFree(s32);
+            psFree(s32_2);
+        }
+        //Attempt to reallocate the psArray to negative size
+        {
+            a = psArrayRealloc(a, -1);
+            psS32 *s32 = (psS32*)psAlloc(sizeof(psS32));
+            *s32 = 2;
+            *s32 = *((psS32*)(a->data[0]));
+            ok( a->n == 1 && a->nalloc == 1 && *s32 == 1,
+                "psArrayRealloc:         return same psArray for negative-size input.");
+            psFree(s32);
+        }
+
+        //Attempt to free a NULL psArray
+        psArrayElementsFree(NULL);
+        //Check the length of a NULL array
+        {
+            psArray *emptyArray = NULL;
+            ok( psArrayLength(emptyArray) == -1,
+                "psArrayLength:          return -1 for NULL input psArray.");
+        }
+        psFree(a);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testArrayAddRemove()
+    {
+        psMemId id = psMemGetId();
+        psArray *a = psArrayAlloc(0);
+        psS32 *s1 = (psS32*)psAlloc(sizeof(psS32));
+        *s1 = 2;
+        psS32 *s2 = (psS32*)psAlloc(sizeof(psS32));
+        psS32 *s3 = (psS32*)psAlloc(sizeof(psS32));
+
+        //Tests for psArrayAdd
+        //Attempt to add element to NULL psArray.
+        {
+            psArray *temp = NULL;
+            temp = psArrayAdd(temp, 1, (psPtr)s1);
+            *s3 = *((psS32*)(temp->data[0]));
+            ok( temp->n == 1 && temp->nalloc == 1 && *s3 == 2,
+                "psArrayAdd:             return array with newly added data.");
+            psFree(temp);
+        }
+        //Attempt to add an element to empty array.
+        {
+            a = psArrayAdd(a, 1, (psPtr)s1);
+            *s2 = 666;
+            *s3 = 212;
+            a = psArrayAdd(a, 1, s2);
+            a = psArrayAdd(a, 1, s3);
+            *s2 = *((psS32*)(a->data[0]));
+            ok ( a->n == 3 && a->nalloc == 3 && *s2 == 2,
+                 "psArrayAdd:            return array with newly added data.");
+        }
+
+        //Tests for psArrayRemoveData
+        //Setup array with 3 elements to test remove function
+        //Return false for attempting to remove from a NULL array
+        {
+            psArray *temp = NULL;
+            ok( !psArrayRemoveData(temp, s2),
+                "psArrayRemoveData:     return false for NULL input psArray.");
+        }
+        //Return true for successful removal
+        {
+            ok( psArrayRemoveData(a, s2) && a->n == 2,
+                "psArrayRemoveData:     return true for successful removal.");
+        }
+
+        //Tests for psArrayRemoveIndex
+        //Return false for NULL array input
+        {
+            ok( !psArrayRemoveIndex(NULL, 0),
+                "psArrayRemoveIndex:    return false for NULL input psArray.");
+        }
+        //Return false for out-of-range index
+        {
+            ok( !psArrayRemoveIndex(a, 5),
+                "psArrayRemoveIndex:    return false for out-of-range location.");
+        }
+        //Return true for successful removal
+        {
+            ok( psArrayRemoveIndex(a, 1) && a->n == 1,
+                "psArrayRemoveIndex:    return true for successful removal.");
+        }
+
+        psFree(s1);
+        psFree(s2);
+        psFree(s3);
+        psFree(a);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testArraySetGet()
+    {
+        psMemId id = psMemGetId();
+        psArray *a = NULL;
+        psArray *b = NULL;
+        psS32 *s1 = (psS32*)psAlloc(sizeof(psS32));
+        *s1 = 2;
+        psS32 *s2 = (psS32*)psAlloc(sizeof(psS32));
+        *s2 = 1;
+        psS32 *s3 = (psS32*)psAlloc(sizeof(psS32));
+        *s3 = 3;
+
+        //Tests for psArraySet
+        //Return false for trying to set a NULL psArray
+        {
+            ok( !psArraySet(a, 0, (psPtr)s1),
+                "psArraySet:            return false for NULL input psArray.");
+        }
+        //Return false for trying to set an invalid position
+        a = psArrayAlloc(1);
+        {
+            ok( !psArraySet(a, 2, (psPtr)s1),
+                "psArraySet:            return false for invalid input position.");
+        }
+        //Return false for trying to set nalloc position
+        psArraySet(a, 0, (psPtr)s1);
+        {
+            ok( !psArraySet(a, a->nalloc, (psPtr)s2),
+                "psArraySet:            return false for out-of-range position.");
+        }
+        //Return false for an invalid position (-2 = out-of-range neg. index)
+        {
+            ok( !psArraySet(a, -2, (psPtr)s2),
+                "psArraySet:            return false for out-of-range negative position.");
+        }
+        //Return true for set to a->n position (< nalloc)
+        a = psArrayRealloc(a, 10);
+        {
+            ok( psArraySet(a, a->n, (psPtr)s2)  && a->n == 2,
+                "psArraySet:            return true for valid input position.");
+        }
+        //Return true for a negative index input
+        {
+            ok( psArraySet(a, -2, (psPtr)s3) && a->n == 2 &&
+                *((psS32*)(a->data[0])) == 3,
+                "psArraySet:            return true for valid negative input position.");
+        }
+
+        //Tests for psArrayGet
+        //Return NULL for NULL array input
+        psS32 *s4 = NULL;
+        {
+            s4 = (psS32*)psArrayGet(b, 0);
+            ok( s4 == NULL,
+                "psArrayGet:            return NULL for NULL input psArray.");
+        }
+        //Return NULL for an out-of-range index
+        {
+            s4 = (psS32*)psArrayGet(a, a->n);
+            ok( s4 == NULL,
+                "psArrayGet:            return NULL for out-of-range position.");
+        }
+        //Return NULL for an out-of-range negative index
+        {
+            s4 = (psS32*)psArrayGet(a, -1-a->n);
+            ok( s4 == NULL,
+                "psArrayGet:            return NULL for out-of-range negative position.");
+        }
+        //Return valid case
+        {
+            s4 = (psS32*)psArrayGet(a, 1);
+            ok( *s4 == 1,
+                "psArrayGet:            return correct value for valid position.");
+        }
+
+        psFree(a);
+        psFree(s1);
+        psFree(s2);
+        psFree(s3);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testArraySort()
+    {
+        psMemId id = psMemGetId();
+        psArray *a = NULL;
+        a = psArrayAlloc(3);
+        a->n = 0;
+        psS32 *s1 = (psS32*)psAlloc(sizeof(psS32));
+        *s1 = 2;
+        psS32 *s2 = (psS32*)psAlloc(sizeof(psS32));
+        *s2 = 1;
+        psS32 *s3 = (psS32*)psAlloc(sizeof(psS32));
+        *s3 = 3;
+        psArraySet(a, a->n, (psPtr)s1);
+        psArraySet(a, a->n, (psPtr)s2);
+        psArraySet(a, a->n, (psPtr)s3);
+        psS32 *s4 = (psS32*)psAlloc(sizeof(psS32));
+        psS32 *s5 = (psS32*)psAlloc(sizeof(psS32));
+        psS32 *s6 = (psS32*)psAlloc(sizeof(psS32));
+
+        //Tests for psArraySort
+        //Return NULL for attempt to sort NULL psArray input.
+        {
+            psArray *temp = NULL;
+            temp = psArraySort(temp, (psComparePtrFunc)psCompareDescendingS32Ptr);
+            ok( temp == NULL,
+                "psArraySort:           return NULL for NULL input psArray.");
+        }
+        //Return properly sorted psArray with descending psS32 elements - 3,2,1
+        {
+            a = psArraySort(a, (psComparePtrFunc)psCompareDescendingS32Ptr);
+            *s4 = *((psS32*)(a->data[0]));
+            *s5 = *((psS32*)(a->data[1]));
+            *s6 = *((psS32*)(a->data[2]));
+            ok( *s4 == 3 && *s5 == 2 && *s6 == 1,
+                "psArraySort:           return properly sorted psArray.");
+        }
+
+        psFree(s1);
+        psFree(s2);
+        psFree(s3);
+        psFree(s4);
+        psFree(s5);
+        psFree(s6);
+        psFree(a);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psBitSet_all.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psBitSet_all.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psBitSet_all.c	(revision 22322)
@@ -0,0 +1,279 @@
+/**
+ *  C Implementation: tap_psBitSet_all
+ *
+ * Description:  Tests for psBitSetAlloc, psBitSetSet, psMemCheckBitSet, psBitSetClear,
+ *               psBitSetTest, psBitSetOp, psBitSetNot, psBitSetToString
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(31);
+
+
+    // testBitSetBasics()
+    {
+        psMemId id = psMemGetId();
+        psBitSet *noBits = NULL;
+        psBitSet *bs = NULL;
+
+        //Return NULL for attempt to Allocate BitSet of negative size.
+        {
+            noBits = psBitSetAlloc(-1);
+            ok( noBits == NULL,
+                "psBitSetAlloc:         return NULL for negative BitSet-size input.");
+        }
+        //Return properly allocated 0-size psBitSet
+        {
+            bs = psBitSetAlloc(0);
+            ok( bs != NULL && psMemCheckBitSet(bs) && bs->n == 0,
+                "psBitSetAlloc:         return properly allocated psBitSet.");
+        }
+        //Return properly allocated psBitSet
+        {
+            psFree(bs);
+            bs = psBitSetAlloc(8);
+            ok( bs != NULL && psMemCheckBitSet(bs) && bs->n == 1,
+                "psBitSetAlloc:         return properly allocated psBitSet.");
+        }
+        //Make sure psMemCheckBitSet works correctly - return false
+        if (0) {
+            int j = 2;
+            ok( !psMemCheckBitSet(&j),
+                "psMemCheckBitSet:      return false for non-BitSet input.");
+        }
+
+        //BitSetSet Tests
+        //Return FALSE for NULL input psBitSet
+        {
+            bool rc = psBitSetSet(NULL, 0);
+            ok(rc == false,
+                "psBitSetSet:           return FALSE for NULL BitSet input.");
+        }
+        //Return FALSE for negative bit input
+        {
+            bool rc = psBitSetSet(bs, -1);
+            ok( rc == false,
+                "psBitSetSet:           return TRUE for negative bits input.");
+            noBits = NULL;
+        }
+        //Return FALSE for out-of-range bits
+        {
+            psFree(bs);
+            bs = psBitSetAlloc(8);
+            bool rc = psBitSetSet(bs, 8);
+            ok( rc == false,
+                "psBitSetSet:           return TRUE for out-of-range bits input.");
+            noBits = NULL;
+        }
+
+        //Return set BitSet for valid inputs
+        {
+            psBitSetSet(bs, 2);
+            ok( bs->bits[0] == 4,
+                "psBitSetSet:           return properly set BitSet for valid inputs.");
+        }
+
+        //BitSetClear Tests
+        //Return FALSE for NULL input psBitSet
+        {
+            bool rc = psBitSetClear(noBits, 0);
+            ok( rc == false,
+                "psBitSetClear:         return FALSE for NULL BitSet input.");
+        }
+        //Return FALSE for negative bit input
+        {
+            bool rc = psBitSetClear(bs, -1);
+            ok( rc == false,
+                "psBitSetClear:        return TRUE for negative bits input.");
+            noBits = NULL;
+        }
+        //Return FALSE for out-of-range bits
+        {
+            bool rc = psBitSetClear(bs, 8);
+            ok( rc == false,
+                "psBitSetClear:        return FALSE for out-of-range bits input.");
+            noBits = NULL;
+        }
+
+        //Return cleared BitSet for valid inputs
+        {
+            psBitSetClear(bs, 2);
+            ok( bs->bits[0] == 0,
+                "psBitSetClear:        return properly cleared BitSet for valid inputs.");
+        }
+
+        //BitSetTest Tests
+        //Return false for NULL input psBitSet
+        {
+            ok( !psBitSetTest(noBits, 0),
+                "psBitSetTest:         return false for NULL BitSet input.");
+        }
+        //Return false for negative bit input
+        {
+            ok( !psBitSetTest(bs, -1),
+                "psBitSetTest:         return false for negative bits input.");
+        }
+        //Return false for out-of-range bits
+        {
+            ok( !psBitSetTest(bs, 8),
+                "psBitSetTest:         return false for out-of-range bits input.");
+        }
+        //Return false for non-matching bit in BitSet
+        {
+            ok( !psBitSetTest(bs, 2),
+                "psBitSetTest:         return false for non-matching bit in BitSet.");
+        }
+        //Return false for non-matching bit in BitSet
+        {
+            psBitSetSet(bs, 2);
+            ok( psBitSetTest(bs, 2),
+                "psBitSetTest:         return true for matching bit in BitSet.");
+        }
+
+        psFree(bs);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testBitSetOps()
+    {
+        psMemId id = psMemGetId();
+        psBitSet *noBits = NULL;
+        psBitSet *bs = NULL;
+        bs = psBitSetAlloc(8);
+        psBitSetSet(bs, 2);  // 0000 0100 == 4
+        psBitSetSet(bs, 3);  // 0000 1100 == 12
+        psBitSetSet(bs, 4);  // 0001 1100 == 28
+        psBitSetSet(bs, 5);  // 0011 1100 == 60
+        psBitSetSet(bs, 6);  // 0111 1100 == 124
+        psBitSetSet(bs, 7);  // 1111 1100 == 252
+        psBitSet *out = NULL;
+
+        //psBitSetNot Tests
+        //Return NULL for NULL BitSet input
+        {
+            out = psBitSetNot(out, noBits);
+            ok( out == NULL,
+                "psBitSetNot:          return NULL for NULL BitSet input.");
+        }
+        //Return correct BitSet for valid BitSet input
+        {
+            out = psBitSetNot(out, bs);  //bs = 1111 1100  so out should = 0000 0011 = 3
+            ok( out->bits[0] == 3,
+                "psBitSetNot:          return correct BitSet for valid BitSet input.");
+        }
+
+        //psBitSetOp Tests   out = psBitSetOp(out, bs1, "op", bs2);
+        //Return NULL for NULL BitSet input
+        {
+            psFree(out);
+            out = NULL;
+            out = psBitSetOp(out, noBits, "op", noBits);
+            ok( out == NULL,
+                "psBitSetOp:           return NULL for NULL BitSet input.");
+        }
+        //Return NULL for NULL operator input
+        {
+            out = psBitSetOp(out, bs, NULL, noBits);
+            ok( out == NULL,
+                "psBitSetOp:           return NULL for NULL operator input.");
+        }
+        //Return NULL for invalid operator input
+        {
+            out = psBitSetOp(out, bs, "XAND", noBits);
+            ok( out == NULL,
+                "psBitSetOp:           return NULL for invalid operator input.");
+        }
+        //Return NULL for AND operator with NULL second BitSet input
+        {
+            out = psBitSetOp(out, bs, "AND", noBits);
+            ok( out == NULL,
+                "psBitSetOp:           return NULL for AND operator with NULL second BitSet input.");
+        }
+        //Return NULL for AND operator with BitSet inputs of differing size.
+        psBitSet *bs2 = psBitSetAlloc(16);
+        {
+            out = psBitSetOp(out, bs, "AND", bs2);
+            ok( out == NULL,
+                "psBitSetOp:           return NULL for AND operator with BitSet inputs of"
+                " differing size.");
+        }
+        psFree(bs);
+        bs = psBitSetAlloc(16);
+        psBitSetSet(bs, 1);     // 0000 0010 == 2
+        psBitSetSet(bs2, 2);   // 0000 0100 == 4
+        //Return correct psBitSet output for valid inputs with AND operator
+        {
+            out = psBitSetOp(out, bs, "AND", bs2);
+            ok( out->bits[0] == 0,
+                "psBitSetOp:           return correct psBitSet output for valid inputs"
+                " with AND operator.");
+        }
+        //Return correct psBitSet output for valid inputs with OR operator
+        {
+            out = psBitSetOp(out, bs, "OR", bs2);
+            ok( out->bits[0] == 6,
+                "psBitSetOp:           return correct psBitSet output for valid inputs"
+                " with OR operator.");
+        }
+        //Return correct psBitSet output for valid inputs with XOR operator
+        psBitSetSet(bs2, 1);     // 0000 0110 == 6
+        {
+            out = psBitSetOp(out, bs, "XOR", bs2);
+            ok( out->bits[0] == 4,
+                "psBitSetOp:           return correct psBitSet output for valid inputs"
+                " with XOR operator.");
+        }
+        //Return correct psBitSet output for valid inputs with NOT operator
+        {
+            psFree(out);
+            out = psBitSetAlloc(0);
+            psBitSetSet(bs, 2);  // 0000 0110 == 4
+            psBitSetSet(bs, 3);  // 0000 1110 == 12
+            psBitSetSet(bs, 4);  // 0001 1110 == 28
+            psBitSetSet(bs, 5);  // 0011 1110 == 60
+            psBitSetSet(bs, 6);  // 0111 1110 == 124
+            psBitSetSet(bs, 7);  // 1111 1110 == 252
+            out = psBitSetOp(out, bs, "NOT", bs2);
+            ok( out->bits[0] == 1,
+                "psBitSetOp:           return correct psBitSet output for valid inputs"
+                " with NOT operator.");
+        }
+
+        //psBitSetToString Tests
+        //Return NULL for NULL BitSet input
+        psString bitStr = NULL;
+        {
+            bitStr = psBitSetToString(noBits);
+            ok( bitStr == NULL,
+                "psBitSetToString:     return NULL for NULL BitSet input.");
+        }
+        //Return correct string for valid BitSet input
+        {
+            psFree(bs);
+            bs = psBitSetAlloc(8);
+            psBitSetSet(bs, 2);  // 0000 0100 == 4
+            bitStr = psBitSetToString(bs);
+            ok( !strncmp(bitStr, "00000100", 10),
+                "psBitSetToString:     return correct string for valid BitSet input.");
+        }
+
+        psFree(bitStr);
+        psFree(out);
+        psFree(bs);
+        psFree(bs2);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psHash_845.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psHash_845.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psHash_845.c	(revision 22322)
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include "pslib.h"
+#include "pstap.h"
+#define SIZE 128
+
+int main(int argc, char *argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(5);
+
+    // Very basic test: create a psHash, then add data to it.
+    // XXX: Remove this test, or mearge it with another file.
+    {
+        psMemId id = psMemGetId();
+        char *stuff1 = psAlloc(SIZE);       // Stuff to put on hash
+        char *stuff2 = psAlloc(SIZE);       // Stuff to put on hash
+
+        psHash *hash = psHashAlloc(16);     // Hash to test
+        ok(hash, "Hash allocated");
+        bool status1 = psHashAdd(hash, "stuff", stuff1);
+        ok(status1, "Added 1 to hash");
+        psFree(stuff1);                     // Drop reference
+        psMemId last = psMemGetId();        // Last memory I.D.
+        bool status2 = psHashAdd(hash, "stuff", stuff2);
+        ok(status2, "Added 2 to hash");
+        psFree(stuff2);                     // Drop reference
+        int numLeaks = psMemCheckLeaks(last, NULL, NULL, false); // Number of leaks
+        ok(numLeaks == 0, "No leaks.");
+        psFree(hash);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psHash_all.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psHash_all.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psHash_all.c	(revision 22322)
@@ -0,0 +1,252 @@
+/**
+ *  C Implementation: tap_psHash_all
+ *
+ * Description:  Tests for psHashAlloc, psHashAdd, psMemCheckHash, psHashLookup,
+ *               psHashRemove, psHashKeyList, psHashToArray
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+
+#include <pslib.h>
+#include <string.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+void testHashCreate(void);
+void testHashManip(void);
+void testHashConvert(void);
+
+int main(void)
+{
+    plan_tests(24);
+
+    note("Tests for psHash Functions");
+
+    testHashCreate();
+    testHashManip();
+    testHashConvert();
+
+    done();
+}
+
+void testHashCreate(void)
+{
+    note("  >>>Test 1:  psHash Creation Fxns");
+
+    psHash *h1 = NULL;
+    psHash *h2 = NULL;
+
+    //Return NULL on attempting to allocate a negative size hash
+    {
+        h1 = psHashAlloc(-2);
+        ok( h1 == NULL,
+            "psHashAlloc:          return NULL for negative array-size input.");
+    }
+    //Return properly allocated psHash of 0-length
+    {
+        h2 = psHashAlloc(0);
+        ok( h2 != NULL && psMemCheckHash(h2),//XXX: && h1->buckets == NULL,
+            "psHashAlloc:          return properly allocated psHash.");
+    }
+    //Return properly allocated psHash
+    {
+        h1 = psHashAlloc(3);
+        ok( h1 != NULL && psMemCheckHash(h1) && h1->buckets != NULL,
+            "psHashAlloc:          return properly allocated psHash.");
+    }
+    //Make sure psMemCheckHash works correctly - return false
+    if (0) {
+        int j = 2;
+        ok( !psMemCheckHash(&j),
+            "psMemCheckHash:       return false for non-Hash input.");
+    }
+    //Allocate a hashBucket by inserting a psHash entry - return true
+    psMetadataItem *itemBool = psMetadataItemAllocBool("itemBool", "", true);
+    {
+        ok( psHashAdd(h1, "h1-1", (psPtr)itemBool),
+            "psHashAlloc:          return properly allocated psHashBucket item.");
+    }
+    //Return false for trying to add to a NULL hash input
+    {
+        ok( !psHashAdd(NULL, "h1-2", (psPtr)itemBool),
+            "psHashAlloc:          return false for trying to add to a NULL hash input.");
+    }
+    //Return false for trying to add data with NULL key
+    {
+        ok( !psHashAdd(h1, NULL, (psPtr)itemBool),
+            "psHashAlloc:          return false for trying to add data with NULL key.");
+    }
+    //Return false for trying to add NULL data
+    {
+        ok( !psHashAdd(h1, "h1-2", NULL),
+            "psHashAlloc:          return false for trying to add NULL data.");
+    }
+    //Return false for trying to add data to empty hash
+    {
+        ok( !psHashAdd(h2, "h1-2", (psPtr)itemBool),
+            "psHashAlloc:          return false for trying to add data to empty table.");
+    }
+    //Return true for trying to add a duplicate hash entry - replaces
+    {
+        psHashAdd(h1, "h1-2", (psPtr)itemBool);
+        psHashAdd(h1, "h1-3", (psPtr)itemBool);
+        psHashAdd(h1, "h1-4", (psPtr)itemBool);
+        ok( psHashAdd(h1, "h1-3", (psPtr)itemBool),
+            "psHashAlloc:         return true when adding duplicate data entry.");
+    }
+
+
+    //Check for Memory leaks
+    {
+        psFree(itemBool);
+        psFree(h1);
+        psFree(h2);
+        checkMem();
+    }
+}
+
+void testHashManip(void)
+{
+    note("  >>>Test 2:  psHash Manipulation Fxns");
+
+    psHash *h1 = NULL;
+    h1 = psHashAlloc(2);
+    psMetadataItem *itemBool = psMetadataItemAllocBool("itemBool", "", true);
+    psMetadataItem *newBool = psMetadataItemAllocBool("newBool", "", true);
+    psHashAdd(h1, "h1-1", (psPtr)itemBool);
+    psHashAdd(h1, "h1-2", (psPtr)newBool);
+    psMetadataItem *data = NULL;
+
+    //psHashLookup Tests
+    //Return NULL for NULL hash input
+    {
+        data = (psMetadataItem*)psHashLookup(NULL, "h1-1");
+        ok( data == NULL,
+            "psHashLookup:        return NULL for NULL hash input.");
+    }
+    //Return NULL for NULL key input
+    {
+        data = (psMetadataItem*)psHashLookup(h1, NULL);
+        ok( data == NULL,
+            "psHashLookup:        return NULL for NULL key input.");
+    }
+    //Return NULL for no matching key
+    {
+        data = (psMetadataItem*)psHashLookup(h1, "h1-3");
+        ok( data == NULL,
+            "psHashLookup:        return NULL for invalid key input.");
+    }
+    //Return correct data for valid inputs
+    {
+        data = (psMetadataItem*)psHashLookup(h1, "h1-1");
+        ok( data != NULL && psMemCheckMetadataItem(data) && data->data.B,
+            "psHashLookup:        return correct data for valid inputs.");
+    }
+
+    //psHashRemove Tests
+    //Return false for NULL hash input
+    {
+        ok( !psHashRemove(NULL, "h1-1"),
+            "psHashRemove:        return false for NULL hash input.");
+    }
+    //Return false for NULL key input
+    {
+        ok( !psHashRemove(h1, NULL),
+            "psHashRemove:        return false for NULL key input.");
+    }
+    //Return false for no matching key
+    {
+        ok( !psHashRemove(h1, "h1-3"),
+            "psHashRemove:        return false for invalid key input.");
+    }
+    //Return true for properly removed hash entry
+    {
+        ok( psHashRemove(h1, "h1-1") && (psHashLookup(h1, "h1-1") == NULL),
+            "psHashRemove:        return true for properly removed hash entry.");
+    }
+
+    //Check for Memory leaks
+    {
+        psFree(h1);
+        psFree(itemBool);
+        psFree(newBool);
+        checkMem();
+    }
+}
+
+void testHashConvert(void)
+{
+    note("  >>>Test 3:  psHash Conversion/List Fxns");
+
+    psHash *h1 = NULL;
+    h1 = psHashAlloc(2);
+    psHash *h2 = psHashAlloc(0);
+    psMetadataItem *itemBool = psMetadataItemAllocBool("itemBool", "", true);
+    psMetadataItem *newBool = psMetadataItemAllocBool("newBool", "", true);
+    psHashAdd(h1, "h1-1", (psPtr)itemBool);
+    psHashAdd(h1, "h1-2", (psPtr)newBool);
+    psList *newList = NULL;
+    psArray *newArr = NULL;
+
+    //psHashKeyList Tests
+    //Return NULL for NULL hash input
+    {
+        newList = psHashKeyList(NULL);
+        ok( newList == NULL,
+            "psHashKeyList:       return NULL for NULL hash input.");
+    }
+    //Return empty list for empty hash input
+    {
+        newList = psHashKeyList(h2);
+        ok( newList != NULL && newList->n == 0,
+            "psHashKeyList:       return empty list for empty hash input.");
+        psFree(newList);
+        newList = NULL;
+    }
+    //Return correct list for valid, non-empty hash input
+    {
+        newList = psHashKeyList(h1);
+        ok( newList != NULL && !strncmp((char*)(newList->head->data), "h1-1", 10),
+            "psHashKeyList:       return correct list for valid hash input.");
+    }
+
+    //psHashToArray
+    //Return NULL for NULL hash input
+    {
+        newArr = psHashToArray(NULL);
+        ok( newArr == NULL,
+            "psHashToArray:       return NULL for NULL hash input.");
+    }
+    //Return empty array for empty hash input
+    {
+        newArr = psHashToArray(h2);
+        ok( newArr != NULL && newArr->n == 0,
+            "psHashToArray:       return empty array for empty hash input.");
+        psFree(newArr);
+        newArr = NULL;
+    }
+    //Return correct list for valid, non-empty hash input
+    {
+        newArr = psHashToArray(h1);
+        psMetadataItem *tempItem = (psMetadataItem*)psArrayGet(newArr, 0);
+        if (tempItem->type != PS_DATA_BOOL)
+            printf("\n not a bool - %s\n", tempItem->name);
+        ok( newArr != NULL,// && !strncmp(tempItem->name, "itemBool", 10),
+            "psHashToArray:       return correct array for valid hash input.");
+    }
+
+    //Check for Memory leaks
+    {
+        psFree(h1);
+        psFree(h2);
+        psFree(newList);
+        psFree(newArr);
+        psFree(itemBool);
+        psFree(newBool);
+        checkMem();
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psListIterator.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psListIterator.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psListIterator.c	(revision 22322)
@@ -0,0 +1,204 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define STRING1 "This is string #1"
+#define STRING2 "String number 2"
+#define STRING3 "3 number string"
+#define STRING4 "four number string"
+#define STRING5 "number five string"
+#define STRING6 "#6 string"
+#define STRING7 "#7 $7r!Ng"
+
+
+// Generate a dummy list
+static psList *listGenerate(void)
+{
+    psList *list = psListAlloc(NULL);
+
+    // Generate the strings
+    psString string1 = psStringCopy(STRING1);
+    psString string2 = psStringCopy(STRING2);
+    psString string3 = psStringCopy(STRING3);
+    psString string4 = psStringCopy(STRING4);
+    psString string5 = psStringCopy(STRING5);
+    psString string6 = psStringCopy(STRING6);
+    psString string7 = psStringCopy(STRING7);
+
+    // Add the strings to the list
+    psListAdd(list, PS_LIST_TAIL, string1);
+    psListAdd(list, PS_LIST_TAIL, string2);
+    psListAdd(list, PS_LIST_TAIL, string3);
+    psListAdd(list, PS_LIST_TAIL, string4);
+    psListAdd(list, PS_LIST_TAIL, string5);
+    psListAdd(list, PS_LIST_TAIL, string6);
+    psListAdd(list, PS_LIST_TAIL, string7);
+
+    // Drop references
+    psFree(string1);
+    psFree(string2);
+    psFree(string3);
+    psFree(string4);
+    psFree(string5);
+    psFree(string6);
+    psFree(string7);
+
+    return list;
+}
+
+// Function to perform iteration
+typedef psString (*iterFunc)(psListIterator *iter);
+
+
+// Test the iteration by iterating once and checking the name, comment, and data
+static void testIteration(iterFunc function, // Function to use to iterate
+                          psListIterator *iterator, // The iterator
+                          const char *string // Expected string
+                         )
+{
+    psString output = function(iterator);
+    ok(output, "output = %x", output);
+    skip_start(!output, 1, "Skipping test because iteration failed");
+    ok(strcmp(output, string) == 0, "output = %s", output);
+    skip_end();
+
+    return;
+}
+
+
+int main (void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(72);
+
+    note("psListIterator tests");
+    // Forwards
+    {
+        psMemId id = psMemGetId();
+        psList *list = listGenerate();
+        psListIterator *iter = psListIteratorAlloc(list, PS_LIST_HEAD, false);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 14, "Skipping 14 tests because psMetadataIteratorAlloc failed");
+
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING1);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING2);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING3);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING4);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING5);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING6);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING7);
+
+        skip_end();
+        psFree(iter);
+        psFree(list);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Backwards
+    {
+        psMemId id = psMemGetId();
+        psList *list = listGenerate();
+        psListIterator *iter = psListIteratorAlloc(list, PS_LIST_TAIL, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 14, "Skipping 14 tests because psListIteratorAlloc failed");
+
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING7);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING6);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING5);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING4);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING3);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING2);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING1);
+
+        skip_end();
+        psFree(iter);
+        psFree(list);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Jumping in halfway through (using allocator), forwards
+    {
+        psMemId id = psMemGetId();
+        psList *list = listGenerate();
+        psListIterator *iter = psListIteratorAlloc(list, 3, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 8, "Skipping 8 tests because psListIteratorAlloc failed");
+
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING4);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING5);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING6);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING7);
+
+        skip_end();
+        psFree(iter);
+        psFree(list);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Jumping in halfway through (using allocator), backwards
+    {
+        psMemId id = psMemGetId();
+        psList *list = listGenerate();
+        psListIterator *iter = psListIteratorAlloc(list, -4, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 8, "Skipping 8 tests because psListIteratorAlloc failed");
+
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING4);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING3);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING2);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING1);
+
+        skip_end();
+        psFree(iter);
+        psFree(list);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Jumping in halfway through (using set), forwards
+    {
+        psMemId id = psMemGetId();
+        psList *list = listGenerate();
+        psListIterator *iter = psListIteratorAlloc(list, PS_LIST_HEAD, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 8, "Skipping 8 tests because psListIteratorAlloc failed");
+        psListIteratorSet(iter, 3);
+
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING4);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING5);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING6);
+        testIteration((iterFunc)psListGetAndIncrement, iter, STRING7);
+
+        skip_end();
+        psFree(iter);
+        psFree(list);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Jumping in halfway through (using set), backwards
+    {
+        psMemId id = psMemGetId();
+        psList *list = listGenerate();
+        psListIterator *iter = psListIteratorAlloc(list, PS_LIST_HEAD, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 8, "Skipping 8 tests because psListIteratorAlloc failed");
+        psListIteratorSet(iter, -4);
+
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING4);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING3);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING2);
+        testIteration((iterFunc)psListGetAndDecrement, iter, STRING1);
+
+        skip_end();
+        psFree(iter);
+        psFree(list);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psList_all.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psList_all.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psList_all.c	(revision 22322)
@@ -0,0 +1,511 @@
+/**
+ *  C Implementation: tap_psList_all
+ *
+ * Description:  Tests for psListAlloc, psListAdd, psMemCheckList, psListAddAfter,
+ *               psListAddBefore, psListRemove, psListRemoveData, psListGet,
+ *               psListIteratorAlloc, psListIteratorSet, psListGetAndIncrement,
+ *               psListGetAndDecrement, psListToArray, psArrayToList, psListSort,
+ *               psListLength
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(61);
+
+    // Test 1:  psList Creation Fxns");
+    // testListCreate(void)
+    {
+        psMemId id = psMemGetId();
+        psList *noList = NULL;
+        psListIterator *noIter = NULL;
+        psList *list = psListAlloc(NULL);
+        psMetadata *md = psMetadataAlloc();
+        psListIterator *iter1 = NULL;
+        psListIterator *iter2 = NULL;
+
+        //Tests for psListAlloc & psMemCheckList
+        //Return empty list for NULL data input.
+        {
+            noList = psListAlloc(NULL);
+            ok( psListLength(noList) == 0 && psMemCheckList(noList),
+                "psListAlloc:             return empty list for NULL data input.");
+        }
+        //Return allocated list for non-NULL data input.
+        psFree(noList);
+        {
+            noList = psListAlloc(md);
+            ok( psListLength(noList) == 1,
+            "psListAlloc:             return correct list for non-NULL data input.");
+        }
+        //Make sure psMemCheckList works correctly - return false
+        if (0) {
+            int j = 2;
+            ok( !psMemCheckList(&j),
+                "psMemCheckList:          return false for non-List input.");
+        }
+
+        //Tests for ListIteratorAlloc
+        //Return NULL for attempt to allocate iterator with NULL list input
+        {
+            noIter = psListIteratorAlloc(NULL, 0, true);
+            ok( noIter == NULL,
+                "psListIteratorAlloc:     return false for NULL data input.");
+        }
+        //Attempt to Allocate a psListIterator with out-of-range location
+        {
+            iter1 = psListIteratorAlloc(noList, 10, false);
+            ok( iter1 == NULL,
+                "psListIteratorAlloc:     return NULL for out-of-range location.");
+        }
+        //Return valid iterator for valid inputs
+        {
+            iter1 = psListIteratorAlloc(noList, 0, true);
+            ok( iter1 != NULL,
+                "psListIteratorAlloc:     return valid iterator for valid inputs.");
+        }
+
+        //Tests For ListAdd Fxns
+        //Return false for NULL list input
+        {
+            ok( !psListAdd(NULL, 0, md),
+                "psListAdd:               return false for NULL list input.");
+        }
+        //Return false for NULL data input
+        {
+            ok( !psListAdd(list, 0, NULL),
+                "psListAdd:               return false for NULL data input.");
+        }
+        //Return false for list with NULL data
+        {
+            psFree(list->iterators->data[0]);
+            list->iterators->data[0] = NULL;
+            ok( !psListAdd(list, 0, md),
+                "psListAdd:               return false for list with NULL iterators.");
+            psFree(list);
+            list = psListAlloc(md);
+        }
+        //Return true for valid inputs - tail location
+        {
+            ok( psListAdd(list, 2, md),
+                "psListAdd:              return true for valid inputs - Tail Location.");
+        }
+        //Return true for valid inputs - head location
+        {
+            ok( psListAdd(list, 0, md),
+                "psListAdd:              return true for valid inputs - head Location.");
+        }
+
+        //psListAddAfter Tests
+        //psListAddAfter - Return false for NULL data input
+        {
+            ok( !psListAddAfter(iter1, NULL),
+                "psListAddAfter:         return false for NULL data input.");
+        }
+        //psListAddAfter - Return false for NULL iterator input
+        {
+            ok( !psListAddAfter(NULL, md),
+                "psListAddAfter:         return false for NULL iterator input.");
+        }
+        //psListAddAfter - Return false for non-mutable iterator input
+        {
+            iter1->mutable = false;
+            ok( !psListAddAfter(iter1, md),
+                "psListAddAfter:         return false for non-mutable iterator input.");
+            iter1->mutable = true;
+            psFree(iter1);
+        }
+        //psListAddAfter - Return false for iterator with NULL cursor
+        psList *newList = psListAlloc(md);
+        iter1 = psListIteratorAlloc(newList, 0, true);
+        psListElem *cursor = iter1->cursor;
+        {
+            iter1->cursor = NULL;
+            ok( !psListAddAfter(iter1, md),
+                "psListAddAfter:         return false for iterator with headptr but no cursor.");
+            iter1->cursor = cursor;
+        }
+        //Set the iterator to a middle element, then add after
+        {
+            iter2 = psListIteratorAlloc(list, 1, true);
+            ok( psListAddAfter(iter2, md),
+                "psListAddAfter:         return true for adding a list element to the middle.");
+        }
+        //Set the iterator to the head element, then add after a wrong index.  return true
+        {
+            if (!psListIteratorSet(iter2, PS_LIST_HEAD) )
+                printf("\nerror in set\n\n");
+            ((psListIterator*)(iter2->list->iterators->data[0]))->index = 10;
+            ok( psListAddAfter(iter2, md),
+                "psListAddAfter:         return true for adding a list element to the head.");
+        }
+        //Return true for adding to an empty list
+        psFree(noList);
+        noList = psListAlloc(NULL);
+        psListIterator *iter3 = psListIteratorAlloc(noList, 0, true);
+        {
+            ok( psListAddAfter(iter3, md),
+                "psListAddAfter:         return true for adding to an empty list.");
+        }
+
+        //psListAddBefore Tests
+        //psListAddBefore - Return false for NULL data input
+        {
+            ok( !psListAddBefore(iter1, NULL),
+                "psListAddBefore:        return false for NULL data input.");
+        }
+        //psListAddBefore - Return false for NULL iterator input
+        {
+            ok( !psListAddBefore(NULL, md),
+                "psListAddBefore:        return false for NULL iterator input.");
+        }
+        //psListAddBefore - Return false for non-mutable iterator input
+        {
+            iter1->mutable = false;
+            ok( !psListAddBefore(iter1, md),
+                "psListAddBefore:        return false for non-mutable iterator input.");
+            iter1->mutable = true;
+        }
+        {
+            cursor = iter1->cursor;
+            iter1->cursor = NULL;
+            ok( !psListAddBefore(iter1, md),
+                "psListAddBefore:        return false for iterator with headptr but no cursor.");
+            iter1->cursor = cursor;
+        }
+        //Add before a middle list element
+        {
+            psListIteratorSet(iter2, -2);
+            ok( psListAddBefore(iter2, md),
+                "psListAddBefore:        return true for adding a list element to the middle.");
+        }
+        //Set the iterator to the 2nd element, then add after a wrong index.  return true
+        {
+            skip_start(  !psListIteratorSet(iter2, 1), 1,
+                         "Skipping 1 tests because psListIteratorSet failed");
+            ((psListIterator*)(iter2->list->iterators->data[0]))->index = 10;
+            ok( psListAddBefore(iter2, md),
+                "psListAddBefore:        return true for adding a list element to the head.");
+            skip_end();
+        }
+        //Return true for adding to an empty list
+        {
+            psFree(noList);
+            noList = psListAlloc(NULL);
+            iter3 = psListIteratorAlloc(noList, 0, true);
+            psFree(iter3);
+            ok( psListAddBefore(iter3, md),
+                "psListAddBefore:        return true for adding to an empty list.");
+        }
+    
+        //Check the length of a NULL list
+        {
+            psList *emptyList = NULL;
+            ok( psListLength(emptyList) == -1,
+                "psListLength:           return -1 for NULL input psList.");
+        }
+    
+        psFree(iter2);
+        psFree(list);
+        psFree(iter1);
+        psFree(newList);
+        psFree(md);
+        psFree(noList);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testListManip()
+    // psList Manipulation Fxns");
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psList *emptyList = psListAlloc(NULL);
+        psList *list = psListAlloc(md);
+        psSphere *sphere = psSphereAlloc();
+        psCube *cube = psCubeAlloc();
+        psArray *array = psArrayAlloc(0);
+        psListAdd(list, PS_LIST_TAIL, sphere);
+        psListAdd(list, PS_LIST_TAIL, cube);
+        psListAdd(list, PS_LIST_TAIL, array);
+        psListIterator *emptyIter = psListIteratorAlloc(emptyList, 0, true);
+        psListIterator *iter1 = psListIteratorAlloc(list, PS_LIST_TAIL, true);
+    
+        //Tests for psListGet
+        //Return NULL for NULL list input
+        {
+            psMetadata *out = NULL;
+            out = (psMetadata*)psListGet(NULL, PS_LIST_HEAD);
+            ok( out == NULL,
+                "psListGet:              return NULL for NULL list input.");
+        }
+        //Return NULL for empty list
+        {
+            psMetadata *out = NULL;
+            out = (psMetadata*)psListGet(emptyList, PS_LIST_HEAD);
+            ok( out == NULL,
+                "psListGet:              return NULL for empty list.");
+        }
+        //Return NULL for out-of-range location
+        {
+            psMetadata *out = NULL;
+            out = (psMetadata*)psListGet(list, -10);
+            ok( out == NULL,
+                "psListGet:              return NULL for out-of-range location.");
+        }
+        //Return correct head item for valid inputs
+        {
+            psMetadata *out = NULL;
+            out = (psMetadata*)psListGet(list, PS_LIST_HEAD);
+            ok( out != NULL && psMemCheckMetadata(out),
+                "psListGet:              return correct head item for valid inputs.");
+        }
+    
+        //Tests for psListGetAndIncrement
+        //Return NULL for NULL iterator input
+        {
+            psMetadata *out = NULL;
+            out = (psMetadata*)psListGetAndIncrement(NULL);
+            ok( out == NULL,
+                "psListGetAndIncrement:  return NULL for NULL iterator input.");
+        }
+        //Return NULL for empty iterator with offend = false
+        {
+            psMetadata *out = NULL;
+            out = (psMetadata*)psListGetAndIncrement(emptyIter);
+            ok( out == NULL,
+                "psListGetAndIncrement:  return NULL for empty iterator input.");
+        }
+        //Return NULL for empty iterator with offend = true
+        {
+            emptyIter->offEnd = true;
+            psMetadata *out = NULL;
+            out = (psMetadata*)psListGetAndIncrement(emptyIter);
+            ok( out == NULL,
+                "psListGetAndIncrement:  return NULL for empty iterator input.");
+            emptyIter->offEnd = false;
+        }
+        //Return correct tail item for valid inputs
+        {
+            psArray *out = NULL;
+            out = (psArray*)psListGetAndIncrement(iter1);
+            ok( out != NULL && psMemCheckArray(out),
+                "psListGetAndIncrement:  return correct tail item for valid inputs.");
+        }
+    
+        //Tests for psListGetAndDecrement
+        //Return NULL for NULL iterator input
+        {
+            psMetadata *out = NULL;
+            out = (psMetadata*)psListGetAndDecrement(NULL);
+            ok( out == NULL,
+                "psListGetAndDecrement:  return NULL for NULL iterator input.");
+        }
+        //Return NULL for empty iterator with offend = false
+        {
+            psMetadata *out = NULL;
+            out = (psMetadata*)psListGetAndDecrement(emptyIter);
+            ok( out == NULL,
+                "psListGetAndDecrement:  return NULL for empty iterator input.");
+        }
+        //Return NULL for empty iterator with offend = true
+        {
+            emptyIter->offEnd = true;
+            psMetadata *out = NULL;
+            out = (psMetadata*)psListGetAndDecrement(emptyIter);
+            ok( out == NULL,
+                "psListGetAndDecrement:  return NULL for empty iterator input.");
+            emptyIter->offEnd = false;
+        }
+        //Return correct tail item for valid inputs
+        {
+            psArray *out = NULL;
+            skip_start(  !psListIteratorSet(iter1, PS_LIST_TAIL), 1,
+                         "Skipping 1 tests because psListIteratorSet failed");
+            out = (psArray*)psListGetAndDecrement(iter1);
+            ok( out != NULL && psMemCheckArray(out),
+                "psListGetAndDecrement:  return correct tail item for valid inputs.");
+            skip_end();
+        }
+    
+        //Tests for psListRemove
+        //Return false for NULL list input
+        {
+            ok( !psListRemove(NULL, PS_LIST_HEAD),
+                "psListRemove:           return false for NULL list input.");
+        }
+        //Return false for invalid location
+        {
+            ok( !psListRemove(list, -10),
+                "psListRemove:           return false for invalid location.");
+        }
+        //Return false for empty list
+        {
+            ok( !psListRemove(emptyList, PS_LIST_HEAD),
+                "psListRemove:           return false for remove from empty list.");
+        }
+        //Return true for remove from middle of list
+        {
+            ok( psListRemove(list, 2) && list->n == 3,
+                "psListRemove:           return true for remove from middle of list.");
+        }
+        //Return true for remove from head of list
+        {
+            ok( psListRemove(list, PS_LIST_HEAD) && list->n ==2,
+                "psListRemove:           return true for remove from head of list.");
+        }
+        //Return true for remove from tail of list
+        {
+            ok( psListRemove(list, PS_LIST_TAIL) && list->n == 1,
+                "psListRemove:           return true for remove from tail of list.");
+        }
+        //Tests for psListRemoveData
+        //Return false for NULL list input
+        {
+            ok( !psListRemoveData(NULL, md),
+                "psListRemoveData:       return false for NULL list input.");
+        }
+        //Return false for NULL data input
+        {
+            ok( !psListRemoveData(list, NULL),
+                "psListRemoveData:       return false for NULL data input.");
+        }
+        //Return false for non-matching data
+        {
+            ok( !psListRemoveData(list, md),
+                "psListRemoveData:       return false for non-matching data.");
+        }
+        //Return false for trying to remove from an empty list
+        {
+            ok( !psListRemoveData(emptyList, md),
+                "psListRemoveData:       return false for trying to remove from an empty list.");
+        }
+        //Return true for remove of valid data
+        psListAdd(list, PS_LIST_HEAD, cube);
+        psListAdd(list, PS_LIST_HEAD, array);
+        {
+            ok( psListRemoveData(list, sphere),
+                "psListRemoveData:       return true for remove of valid data.");
+        }
+    
+        psFree(md);
+        psFree(emptyList);
+        psFree(list);
+        psFree(sphere);
+        psFree(cube);
+        psFree(array);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testListConvertSort()
+    // psList Conversion and Sorting Fxns");
+    {
+        psMemId id = psMemGetId();
+        psList *emptyList = psListAlloc(NULL);
+        psS32 *s1 = (psS32*)psAlloc(sizeof(psS32));
+        *s1 = 2;
+        psS32 *s2 = (psS32*)psAlloc(sizeof(psS32));
+        *s2 = 3;
+        psS32 *s3 = (psS32*)psAlloc(sizeof(psS32));
+        *s3 = 1;
+        psList *list = psListAlloc(s1);
+        psListAdd(list, PS_LIST_TAIL, s2);
+        psListAdd(list, PS_LIST_TAIL, s3);
+        psArray *array = NULL;
+        psArray *emptyArray = psArrayAlloc(0);
+        psList *out = NULL;
+    
+        //Tests for psListToArray
+        //Return NULL for NULL list input
+        {
+            array = psListToArray(NULL);
+            ok( array == NULL,
+                "psListToArray:          return NULL for NULL list input.");
+        }
+        //Return empty array for empty list input
+        {
+            array = psListToArray(emptyList);
+            ok( array->n == 0 && psMemCheckArray(array),
+                "psListToArray:          return empty array for empty list input.");
+            psFree(array);
+            array = NULL;
+        }
+        //Return correct array for valid list input
+        {
+            array = psListToArray(list);
+            ok( array->n == 3 && *((psS32*)(array->data[0])) == 2,
+                "psListToArray:          return correct array for valid list input.");
+        }
+    
+        //Tests for psArrayToList
+        //Return NULL for NULL array input
+        {
+            out = psArrayToList(NULL);
+            ok( out == NULL,
+                "psArrayToList:          return NULL for NULL array input.");
+        }
+        //Return empty list for empty array input
+        {
+            out = psArrayToList(emptyArray);
+            ok( out->n == 0 && psMemCheckList(out),
+                "psArrayToList:          return empty list for empty array input.");
+            psFree(out);
+            out = NULL;
+        }
+        //Return correct list for valid array input
+        {
+            out = psArrayToList(array);
+            ok( out->n == 3 && *((psS32*)psListGet(out, PS_LIST_HEAD)) == 2,
+                "psArrayToList:          return correct list for valid array input.");
+        }
+    
+        //Tests for psListSort
+        //Return NULL for NULL list input
+        {
+            psList *none = NULL;
+            none = psListSort(none, (psComparePtrFunc)psCompareDescendingS32Ptr);
+            ok( none == NULL,
+                "psListSort:             return NULL for NULL list input.");
+        }
+        //Return empty list for empty list input
+        {
+            emptyList = psListSort(emptyList, (psComparePtrFunc)psCompareDescendingS32Ptr);
+            ok( emptyList != NULL,
+                "psListSort:             return empty list for empty list input.");
+        }
+        //Return properly sorted list for valid inputs
+        {
+            list = psListSort(list, (psComparePtrFunc)psCompareS32Ptr);
+            ok( *((psS32*)psListGet(list, 0)) == 1 &&  *((psS32*)psListGet(list, 1)) == 2,
+                "psListSort:             return properly sorted list for valid inputs.");
+        }
+        //Return unchanged list for NULL psComparePtrFunc
+        {
+            psList *tempList = NULL;
+            tempList = psListSort(list, NULL);
+            ok( tempList == NULL,
+                "psListSort:             return NULL for NULL psComparePtrFunc.");
+        }
+    
+        psFree(array);
+        psFree(out);
+        psFree(emptyArray);
+        psFree(emptyList);
+        psFree(list);
+        psFree(s1);
+        psFree(s2);
+        psFree(s3);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psLookupTable_all.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psLookupTable_all.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psLookupTable_all.c	(revision 22322)
@@ -0,0 +1,296 @@
+/**
+ *  C Implementation: tap_psLookupTable_all
+ *
+ * Description:  Tests for psLookupTableAlloc, psMemCheckLookupTable,
+ *               psVectorsReadFromFile, psLookupTableImport, psLookupTableRead,
+ *               psLookupTableInterpolate, psLookupTableInterpolateAll
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * XXX: Must add correct build directories.  This file reads a file in the
+ * types/ subdirectory and will not execute directly if not run from psLib/test
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(32);
+
+    // Tests for psLookupTable Functions
+
+
+    // testLookupTableAlloc()
+    // psLookupTableAlloc & psMemCheckLookupTable Fxns
+    {
+        psMemId id = psMemGetId();
+        psLookupTable *lt = NULL;
+        //Tests for psLookupTableAlloc
+        //Return NULL for NULL filename input
+        {
+            lt = psLookupTableAlloc(NULL, "\%f \%lf \%d \%ld", 10);
+            ok( lt == NULL,
+                "psLookupTableAlloc:               return NULL for NULL filename input.");
+        }
+        //Return NULL for NULL format input
+        {
+            lt = psLookupTableAlloc("table.dat", NULL, 10);
+            ok( lt == NULL,
+                "psLookupTableAlloc:               return NULL for NULL format input.");
+        }
+        //Return properly allocated lookupTable for valid inputs
+        {
+            lt = psLookupTableAlloc("table.dat", "\%f \%lf \%d \%ld", 10);
+            ok( lt != NULL && psMemCheckLookupTable(lt),
+                "psLookupTableAlloc:               "
+                "return properly allocated lookupTable for valid inputs.");
+        }
+
+        //Tests for psMemCheckArray
+        //Make sure psMemCheckArray works correctly - return false
+        if (0) {
+            int j = 2;
+            ok( !psMemCheckLookupTable(&j),
+                "psMemCheckLookupTable:            return false for non-LookupTable input.");
+        }
+
+        psFree(lt);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testLookupTableReadImport()
+    // psVectorsReadFromFile, psLookupTableImport, psLookupTableRead Fxns");
+    {
+        psMemId id = psMemGetId();
+        psArray *outVec = NULL;
+        psArray *vectors = NULL;
+        psLookupTable*  table1  = NULL;
+        long           numRows = 0;
+
+        //Tests for psVectorReadFromFile
+        // Attempt to read from NULL filename input
+        {
+            outVec = psVectorsReadFromFile(NULL,
+                                           "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf");
+            ok( outVec == NULL,
+                "psVectorsReadFromFile:            return NULL for NULL filename input.");
+        }
+        // Attempt to read from NULL format input
+        {
+            outVec = psVectorsReadFromFile("table.dat", NULL);
+            ok( outVec == NULL,
+                "psVectorsReadFromFile:            return NULL for NULL format input.");
+        }
+        // Attempt to read from invalid filename input
+        {
+            outVec = psVectorsReadFromFile("tableS32.dat",
+                                           "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf");
+            ok( outVec == NULL,
+                "psVectorsReadFromFile:            return NULL for invalid filename input.");
+        }
+        // Attempt to read from invalid format input
+        {
+            outVec = psVectorsReadFromFile("table.dat", "\%s");
+            ok( outVec == NULL,
+                "psVectorsReadFromFile:            return NULL for invalid format input.");
+        }
+        // Attempt to read from table containing invalid entry - F32
+        {
+            outVec = psVectorsReadFromFile("tableF32_err.dat",
+                                           "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf");
+            ok( outVec == NULL,
+                "psVectorsReadFromFile:            return NULL for table containing invalid entry.");
+        }
+        // Attempt to read from table containing invalid entry - F64
+        {
+            outVec = psVectorsReadFromFile("tableF64_err.dat",
+                                           "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf");
+            ok( outVec == NULL,
+                "psVectorsReadFromFile:           return NULL for table containing invalid entry.");
+        }
+        // Attempt to read from table containing invalid entry - S32
+        {
+            outVec = psVectorsReadFromFile("tableS32_err.dat",
+                                           "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf");
+            ok( outVec == NULL,
+                "psVectorsReadFromFile:           return NULL for table containing invalid entry.");
+        }
+        // Attempt to read from table containing invalid entry - S64
+        {
+            outVec = psVectorsReadFromFile("tableS64_err.dat",
+                                           "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf");
+            ok( outVec == NULL,
+                "psVectorsReadFromFile:           return NULL for table containing invalid entry.");
+        }
+        // Attempt to read from empty table
+        {
+            outVec = psVectorsReadFromFile("tableF32_2.dat",
+                                           "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf");
+            ok( outVec == NULL,
+                "psVectorsReadFromFile:           return NULL for empty table.");
+        }
+        // Attempt to read from file with partially constructed row
+        {
+            outVec = psVectorsReadFromFile("table3.dat",
+                                           "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf");
+            ok( outVec == NULL,
+                "psVectorsReadFromFile:           return NULL for table with partially"
+                " constructed row.");
+        }
+        // Attempt to read with valid inputs
+        {
+            outVec = psVectorsReadFromFile("table.dat",
+                                           "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf");
+            ok(outVec, "psVectorsReadFromFile:           read vectors from file");
+
+	    skip_start (!outVec, 2, "skipping tests using the invalid null array");
+	    ok(outVec->n == 9, "psVectorsReadFromFile:           read correct number of vectors");
+
+	    psVector *tmpVec = outVec->data[6];
+	    ok(tmpVec->data.S32[1] == -8, "psVectorsReadFromFile:           selected data element has correct value.");
+	    skip_end();
+        }
+
+        //Tests for psLookupTableRead
+        // Attempt to read table with NULL input table specified
+        numRows = psLookupTableRead(table1);
+        {
+            ok( numRows == 0,
+                "psLookupTableRead:               return NULL for NULL filename input.");
+        }
+        // Attempt to read table with bad filename specified
+        table1 = psLookupTableAlloc("psTable.dat", "\%f \%lf \%d \%ld", 10);
+        numRows = psLookupTableRead(table1);
+        {
+            ok( numRows == 0,
+                "psLookupTableRead:               return NULL for table with invalid filename.");
+        }
+
+        // Attempt to read valid table with wrong indexCol
+        psFree(table1);
+        table1 = psLookupTableAlloc("table.dat",
+                                "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf", 10);
+        numRows = psLookupTableRead(table1);
+        {
+            ok( numRows == 0,
+                "psLookupTableRead:               return correct number of rows for valid inputs.");
+        }
+        // Attempt to read valid table
+        psFree(table1);
+        table1 = psLookupTableAlloc("table.dat",
+                                    "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf", 0);
+        numRows = psLookupTableRead(table1);
+        {
+            ok( numRows == 4,
+                "psLookupTableRead:               return correct number of rows for valid inputs.");
+        }
+
+        //Tests for psLookupTableImport (remaining cases)
+        // Attempt to import table with negative indexCol
+        vectors = psVectorsReadFromFile(table1->filename, table1->format);
+        {
+            ok( !psLookupTableImport(table1, vectors, -1 ),
+                "psLookupTableImport:             return false for negative indexCol input.");
+        }
+        //Attempt to import table with unsorted array column
+        psFree(table1);
+        table1 = psLookupTableAlloc("table.dat",
+                                    "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf", 0);
+        {
+            ok( psLookupTableImport(table1, vectors, 7 ),
+                "psLookupTableImport:             return true for array with unsorted column");
+        }
+
+        psFree(vectors);
+        psFree(outVec);
+        psFree(table1);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testLookupTableInterpolates()
+    // psLookupTableInterpolate & psLookupTableInterpolateAll Fxns
+    {
+        psMemId id = psMemGetId();
+        psLookupTable *table1 = NULL;
+        psVector *vec = NULL;
+        // XXX: Remove the "types" path here
+        table1 = psLookupTableAlloc("table.dat",
+                                    "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf", 0);
+        psLookupTable *table2 = NULL;
+        // XXX: Remove the "types" path here
+        table2 = psLookupTableAlloc("table.dat",
+                                    "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf", 0);
+        psLookupTableRead(table2);
+    
+        //Tests for psLookupTableInterpolateAll
+        //Return NULL for NULL table input
+        {
+            vec = psLookupTableInterpolateAll(NULL, 0);
+            ok( vec == NULL,
+                "psLookupTableInterpolateAll:     return NULL for NULL table input.");
+        }
+        //Return NULL for NULL table values
+        {
+            vec = psLookupTableInterpolateAll(table1, 0);
+            ok( vec == NULL,
+                "psLookupTableInterpolateAll:     return NULL for NULL table values.");
+        }
+    
+        ok(table2->values != NULL, "table->values not NULL");
+	skip_start (!table2->values, 3, "skipping for failed table load");
+        //Return NULL for table with table->values->n == 0
+        {
+            long n = table2->values->n;
+            table2->values->n = 0;
+            vec = psLookupTableInterpolateAll(table2, 0);
+            ok( vec == NULL,
+                "psLookupTableInterpolateAll:     return NULL for table with no columns.");
+            table2->values->n = n;
+        }
+        //Return NULL for invalid index input
+        {
+            vec = psLookupTableInterpolateAll(table2, -10.5);
+            ok( vec == NULL,
+                "psLookupTableInterpolateAll:     return NULL for invalid index input.");
+        }
+        //Return correct vector output for valid inputs
+        {
+            vec = psLookupTableInterpolateAll(table2, 1);
+            skip_start(  vec == NULL, 1,
+                         "Skipping 1 tests because psLookupTableInterpolateAll failed");
+            is_double(vec->data.F64[0], 1.0,
+                      "psLookupTableInterpolateAll:     return correct output vector for valid inputs.");
+            skip_end();
+        }
+	skip_end();
+    
+        //Remaining tests for psLookupTableInterpolate
+        //Return NAN for invalid index - divide by zero error
+        psLookupTable *table3 = NULL;
+        table3 = psLookupTableAlloc("table2.dat",
+                                    "\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf", 0);
+        psLookupTableRead(table3);
+        {
+            double retVal;
+            retVal = psLookupTableInterpolate(table3, 1.5e-20, 0);
+            ok( isnan(retVal),
+                "psLookupTableInterpolateAll:     return NAN for invalid index input.");
+        }
+    
+        psFree(table3);
+        psFree(vec);
+        psFree(table2);
+        psFree(table1);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigFormat.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigFormat.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigFormat.c	(revision 22322)
@@ -0,0 +1,161 @@
+/**
+ *  C Implementation: tap_psMetadataConfigFormat.c
+ *
+ * Description:  Tests for psMetadataConfigFormat
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(18);
+
+    psMetadata *md = NULL;
+    psString out = NULL;
+    //psString psMetadataConfigFormat(psMetadata *md);
+    //Return NULL for NULL metadata input
+    {
+        psMemId id = psMemGetId();
+        out = psMetadataConfigFormat(md);
+        ok( !out, "psMetadataConfigFormat:         return NULL for NULL metadata input.");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for empty metadata input
+    {
+        md = psMetadataAlloc();
+        out = psMetadataConfigFormat(md);
+        ok( out, "psMetadataConfigFormat:         return valid string for empty metadata input.");
+	skip_start (!out, 1, "failed to build valid string");
+        ok( *out == 0, "psMetadataConfigFormat:         return empty string for empty metadata input.");
+	psFree (out);
+	skip_end();
+    }
+
+
+    //Return NULL for metadata with missing hash table
+    {
+        psMetadataAddS32(md, PS_LIST_HEAD, "new_S32", 0, "", 666);
+        psMetadataAddS32(md, PS_LIST_HEAD, "new_S32", PS_META_DUPLICATE_OK, "", 665);
+        psHash *temp = md->hash;
+        md->hash = NULL;
+        out = psMetadataConfigFormat(md);
+        ok( out == NULL,
+            "psMetadataConfigFormat:         return NULL for metadata with missing "
+            "hash table.");
+        md->hash = temp;
+    }
+
+
+    //Return NULL for metadata containing a psList
+    {
+        psList *list = psListAlloc(NULL);
+        psMetadataAddList(md, PS_LIST_HEAD, "new_list", 0, "", list);
+        out = psMetadataConfigFormat(md);
+        ok( out == NULL,
+            "psMetadataConfigFormat:         return NULL for metadata containing a psList.");
+        psFree(list);
+        psFree(md);
+    }
+
+
+    //Return NULL for attempting to format psTime with type = -1
+    {
+        psMemId id = psMemGetId();
+        md = psMetadataAlloc();
+        psTime *time = psTimeAlloc(PS_TIME_TT);
+        time->type = -1;
+        psMetadataAddTime(md, PS_LIST_TAIL, "time", 0, "", time);
+        out = psMetadataConfigFormat(md);
+        ok( out == NULL,
+            "psMetadataConfigFormat:         return NULL for metadata containing a "
+            "invalid time type.");
+        psFree(time);
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return valid metadata for attempting to format NULL psTime metadataItem
+    {
+        psMemId id = psMemGetId();
+        md = psMetadataAlloc();
+        psTime *time = psTimeAlloc(PS_TIME_TT);
+        psMetadataAddTime(md, PS_LIST_HEAD, "time", 0, "", time);
+        psMetadataItem *item = psMetadataGet(md, PS_LIST_HEAD);
+        psFree(item->data.V);
+        item->data.V = NULL;
+        out = psMetadataConfigFormat(md);
+        ok( out != NULL,
+            "psMetadataConfigFormat:         return valid metadata for metadata "
+            "containing a NULL time.");
+        char configTest[32];
+        strncpy(configTest, "time             TAI       NULL", 31);
+        is_strn(configTest, out, 31,
+                "psMetadataConfigFormat:         return correct output string.");
+        psFree(time);
+        psFree(out);
+        out = NULL;
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return valid metadata for attempting to format NULL psTime metadataItem
+    {
+        psMemId id = psMemGetId();
+        md = psMetadataAlloc();
+        psMetadataAddS32(md, PS_LIST_HEAD, "new_S32", 0, NULL, 666);
+        psMetadataAddS32(md, PS_LIST_HEAD, "new_S32", PS_META_DUPLICATE_OK, NULL, 665);
+        out = psMetadataConfigFormat(md);
+        ok( out != NULL,
+            "psMetadataConfigFormat:         return valid metadata for metadata "
+            "containing a NULL time.");
+        char configTest[62];
+        strncpy(configTest, "new_S32 MULTI\nnew_S32          S32       666", 61);
+        is_strn(configTest, out, strlen(configTest),
+                "psMetadataConfigFormat:         return correct output string.");
+        psFree(out);
+        out = NULL;
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return valid metadata for attempting to format NULL psTime metadataItem
+    {
+        psMemId id = psMemGetId();
+        md = psMetadataAlloc();
+        psMetadataAddS32(md, PS_LIST_HEAD, "f32", 0, "f32_1", 666);
+        psMetadataAddS32(md, PS_LIST_TAIL, "f32", PS_META_DUPLICATE_OK, "f32_3", 666);
+        psMetadataAddS32(md, PS_LIST_TAIL, "f32", PS_META_DUPLICATE_OK, "f32_2", 665);
+        out = psMetadataConfigFormat(md);
+        ok( out != NULL,
+            "psMetadataConfigFormat:         return valid metadata for metadata "
+            "containing a NULL time.");
+        char configTest[201];
+        strncpy(configTest,
+                "f32 MULTI"
+                "\nf32              S32       666              # f32_1"
+                "\nf32              S32       666              # f32_3"
+                "\nf32              S32       665              # f32_2", 200);
+        is_strn(configTest, out, strlen(configTest),
+                "psMetadataConfigFormat:         return correct output string.");
+        psFree(out);
+        out = NULL;
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    done();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigParse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigParse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigParse.c	(revision 22322)
@@ -0,0 +1,154 @@
+/**
+ * tap_psMetdataConfigParse.c
+ *
+ * Description:  Tests for psMetadataConfigParse
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ * Author: Joshua Hoblitt, University of Hawaii, 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(35);
+
+
+    // Return NULL for NULL string input
+    {
+        psMemId id = psMemGetId();
+        unsigned int nfail = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nfail, NULL, false);
+
+        ok(md == NULL, "return NULL for NULL string input.");
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return empty metadata for incorrect string input
+    {
+        psMemId id = psMemGetId();
+        char *str = "-10.05    2        4";
+        psMetadata *md = psMetadataConfigParse(NULL, NULL, str, false);
+
+        ok(md == NULL, "return NULL for 1 invalid line");
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return correct metadata for correct string input - S32
+    {
+        psMemId id = psMemGetId();
+        char *str = "new S32       666";
+        psMetadata *md = psMetadataConfigParse(NULL, NULL, str, false);
+        psMetadataItem *item = psMetadataGet(md, PS_LIST_HEAD);
+
+        ok(md != NULL, "return correct metadata for 1 valid line");
+        skip_start(item == NULL, 2,
+                     "Skipping 1 tests because metadata container is empty!");
+        ok(item->type == PS_TYPE_S32, "return correcot metdataItem type");
+        is_int(item->data.S32, 666, "return correct metadataItem data");
+        is_str(item->name, "new", "return correct metadataItem name");
+        is_str(item->comment, "", "return correct metadataItem comment");
+        skip_end();
+
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return correct metadata for correct string input - F64
+    {
+        psMemId id = psMemGetId();
+        char *str = "item4            F64       6.28";
+        psMetadata *md = psMetadataConfigParse(NULL, NULL, str, false);
+        psMetadataItem *item = psMetadataGet(md, PS_LIST_HEAD);
+
+        ok(md != NULL, "return correct metadata for valid string input.");
+        ok(item != NULL, "return correct metadata for valid string input.");
+        skip_start(item == NULL, 2,
+                "Skipping 1 tests because metadata container is empty!");
+        ok(item->type == PS_DATA_F64, "return correct metadataItem type");
+        is_float(6.28, item->data.F64, "return correct metadataItem data");
+        is_str(item->name, "item4", "return correct metadataItem name");
+        is_str(item->comment, "", "return correct metadataItem comment");
+        skip_end();
+
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return correct metadata for string with duplicate entry - S8
+    {
+        psMemId id = psMemGetId();
+        char *str = "item8            S8        3\nitem8            S8        5";
+        unsigned int nfail = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nfail, str, false);
+        psMetadataItem *item = psMetadataGet(md, PS_LIST_HEAD);
+
+        ok(md != NULL, "return metadata for 1 good & 1 duplicate (bad) line");
+        ok(item != NULL, "return non-empty metdata");
+        is_long(md->list->n, 1, "# of metdataItems");
+        is_int(nfail, 1, "number of parse failures");
+        skip_start(item == NULL, 2,
+                "Skipping 1 tests because metadata container is empty!");
+        ok(item->type == PS_TYPE_S8, "metdataItem type");
+        is_int(item->data.S8, 3, "metdataItem value");
+        is_str(item->name, "item8", "return correct metadataItem name");
+        is_str(item->comment, "", "return correct metadataItem comment");
+        skip_end();
+        psFree(md);
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return correct metadata for correct string input - Vector
+    {
+        psMemId id = psMemGetId();
+        char *str = "@vector6 U8 1 2 3 4 5   # I am a vector";
+        psMetadata *md = psMetadataConfigParse(NULL, NULL, str, true);
+        psMetadataItem *item = psMetadataGet(md, PS_LIST_HEAD);
+
+        ok(md != NULL, "return metadata for valid string input. (Vector)");
+        ok(item != NULL, "non-empty metadata");
+        skip_start(item == NULL, 2,
+                     "Skipping 1 tests because metadata container is empty!");
+        ok(item->type == PS_DATA_VECTOR, "metadataItem type");
+        is_int(((psVector*)(item->data.V))->data.U8[0], 1,
+                "return correct metadataItem data (Vector)");
+        is_str(item->name, "vector6",
+                "return correct metadataItem name (Vector)");
+        is_str(item->comment, "I am a vector", "return correct metadataItem comment");
+        skip_end();
+
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return empty metadata for incorrect string input
+    {
+        psMemId id = psMemGetId();
+        char *str = "END\n";
+        psMetadata *md = psMetadataConfigParse(NULL, NULL, str, false);
+
+        ok(md == NULL, "return NULL on 1 invalid line");
+
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigParse_time.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigParse_time.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigParse_time.c	(revision 22322)
@@ -0,0 +1,157 @@
+#include <stdio.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main (void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(39 + 1);
+
+
+    {
+        psMemId id = psMemGetId();
+        char *testStr = "time    UTC     2005-03-18T16:05:00Z\n";
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+        psTime *time1 = psMetadataLookupTime(NULL, md, "time");
+        psTime *time2 = psTimeFromISO("2005-03-18T16:05:00Z", PS_TIME_UTC);
+        double delta = psTimeDelta(time1, time2);
+        psFree(time2);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 0, "number of bad lines = %d", nBad); // One bad line from boolean
+        ok(md->list->n == 1, "number of items in metadata = %ld", md->list->n);
+        ok1(time1->type == PS_TIME_UTC);
+        ok(delta == 0.0, "reported time was off by %fs", delta);
+
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        char *testStr = "time    UT1     2005-03-18T16:05:00Z\n";
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+        psTime *time1 = psMetadataLookupTime(NULL, md, "time");
+        psTime *time2 = psTimeFromISO("2005-03-18T16:05:00Z", PS_TIME_UT1);
+        double delta = psTimeDelta(time1, time2);
+        psFree(time2);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 0, "number of bad lines = %d", nBad);
+        ok(md->list->n == 1, "number of items in metadata = %ld", md->list->n);
+        ok1(time1->type == PS_TIME_UT1);
+        ok(delta == 0.0, "reported time was off by %fs", delta);
+
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        char *testStr = "time    TAI     2005-03-18T16:05:00Z\n";
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+        psTime *time1 = psMetadataLookupTime(NULL, md, "time");
+        psTime *time2 = psTimeFromISO("2005-03-18T16:05:00Z", PS_TIME_TAI);
+        double delta = psTimeDelta(time1, time2);
+        psFree(time2);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 0, "number of bad lines = %d", nBad);
+        ok(md->list->n == 1, "number of items in metadata = %ld", md->list->n);
+        ok1(time1->type == PS_TIME_TAI);
+        ok(delta == 0.0, "reported time was off by %fs", delta);
+
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        char *testStr = "time    TT      2005-03-18T16:05:00Z";
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+        psTime *time1 = psMetadataLookupTime(NULL, md, "time");
+        psTime *time2 = psTimeFromISO("2005-03-18T16:05:00Z", PS_TIME_TT);
+        double delta = psTimeDelta(time1, time2);
+        psFree(time2);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 0, "number of bad lines = %d", nBad);
+        ok(md->list->n == 1, "number of items in metadata = %ld", md->list->n);
+        ok1(time1->type == PS_TIME_TT);
+        ok(delta == 0.0, "reported time was off by %fs", delta);
+
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        // missing Z
+        char *testStr = "broken      UTC     2005-03-18T16:05:00";
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+
+        ok(md, "returned metdata");
+        is_int(nBad, 0, "number of bad lines");
+        is_long(psListLength(md->list), 1, "number of items in metadata");
+
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psMemId id = psMemGetId();
+        // missing Z
+        char *testStr = "broken      UT1     2005-03-18T16:05:00";
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+
+        ok(md, "returned metdata");
+        is_int(nBad, 0, "number of bad lines");
+        is_long(psListLength(md->list), 1, "number of items in metadata");
+
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psMemId id = psMemGetId();
+        // missing Z
+        char *testStr = "broken      TAI     2005-03-18T16:05:00";
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+
+        ok(md, "returned metdata");
+        is_int(nBad, 0, "number of bad lines");
+        is_long(psListLength(md->list), 1, "number of items in metadata");
+
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+        // missing Z
+        char *testStr = "broken      TT      2005-03-18T16:05:00";
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+
+        ok(md, "returned metdata");
+        is_int(nBad, 0, "number of bad lines");
+        is_long(psListLength(md->list), 1, "number of items in metadata");
+
+        psFree(md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigPrint.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigPrint.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigPrint.c	(revision 22322)
@@ -0,0 +1,115 @@
+/**
+ *  C Implementation: tap_psMetadataConfig_output
+ *
+ * Description:  Tests for psMetadataConfigPrint 
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(11);
+
+
+    //Return false for NULL metadata input
+    {
+        psMemId id = psMemGetId();
+        FILE *openfile = fopen("mdcfg.prt", "w+");
+
+        ok(!psMetadataConfigPrint(openfile, NULL),
+           "return false for NULL metadata input.");
+
+        fclose(openfile);
+        remove("mdcfg.prt");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return true for empty metadata input (creates an empty file; is not an error)
+    {
+        psMemId id = psMemGetId();
+        FILE *openfile = fopen("mdcfg.prt", "w+");
+        psMetadata *md = psMetadataAlloc();
+
+        ok(psMetadataConfigPrint(openfile, md), "return true for empty metadata input.");
+
+        psFree(md);
+        fclose(openfile);
+        remove("mdcfg.prt");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return false for NULL file input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psMetadataAddS32(md, PS_LIST_HEAD, "new S32", 0, "", 666);
+
+        ok(!psMetadataConfigPrint(NULL, md),
+           "return false for NULL FILE* input.");
+
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return false for read-only file input
+    {
+        psMemId id = psMemGetId();
+        // create an empty file to open read only later
+        {
+            FILE *openfile = fopen("mdcfg.prt", "w");
+            fclose(openfile);
+        }
+
+        FILE *openfile = fopen("mdcfg.prt", "r");
+        psMetadata *md = psMetadataAlloc();
+        psMetadataAddS32(md, PS_LIST_HEAD, "new S32", 0, "", 666);
+
+        ok(!psMetadataConfigPrint(openfile, md),
+           "return false for read-only FILE* input.");
+
+        psFree(md);
+        fclose(openfile);
+        remove("mdcfg.prt");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return true for valid inputs
+    {
+        psMemId id = psMemGetId();
+        FILE *openfile = fopen("mdcfg.prt", "w+");
+        psMetadata *md = psMetadataAlloc();
+        psMetadataAddS32(md, PS_LIST_HEAD, "new S32", 0, "", 666);
+
+        ok(psMetadataConfigPrint(openfile, md),
+           "return true for valid inputs.");
+
+        psFree(md);
+        fclose(openfile);
+
+        char fileStr2[62];
+        memset(fileStr2, '\0', 62);
+        FILE *mdcfg = fopen("mdcfg.prt", "r");
+
+        fread(fileStr2, 1, 31, mdcfg);
+
+        is_strn(fileStr2, "\nnew S32          S32       666 ", 30,
+                "return correct output.");
+
+        fclose(mdcfg);
+        remove("mdcfg.prt");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigRead.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigRead.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigRead.c	(revision 22322)
@@ -0,0 +1,280 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <pslib.h>
+#include <string.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+#define FILENAME "tap_psMetadataConfigRead.config"
+
+#define STRING1 "# This is a comment that should be ignored\n"
+#define STRING2 "\t\t\tSomeString   STR    This is a string     # This is the comment    \n"
+#define STRING3 "      \t       vAlUes\t\t\t\tMETADATA\t\t\t\n"
+#define STRING3a "aFloat\tF32\t1.2345\t#A number with a decimal point\n"
+#define STRING3b "                                                anInt S32 98765\n"
+#define STRING3c "\t\t\t\t\t\t\t\t\t\t\t\t\tEND\n"
+#define STRING4 "Numbers\tMULTI\t\n"
+#define STRING5 "Numbers F32 1.23#Number one\n"
+#define STRING6 "        \t           \t                                \t              \n"
+#define STRING7 "Numbers\t\t\tSTR One point two three   #Number two\n"
+#define STRING8 "Numbers                                      BOOL 1#This is true\n"
+#define STRING9 "   \tboolean BOOL TRUEsomeTimesButFALSEatOthers#This should fail\n"
+#define STRING10 "SomeBoolean BOOL false\n"
+
+#define NAME2 "SomeString"
+#define TYPE2 PS_DATA_STRING
+#define MIX2 string
+#define VALUE2 "This is a string"
+#define COMMENT2 "This is the comment"
+
+#define NAME3 "vAlUes"
+#define TYPE3 PS_DATA_METADATA
+#define VALUE3 NULL                     // Not really, but serves as a placeholder
+#define COMMENT3 ""
+
+#define NAME3a "aFloat"
+#define TYPE3a PS_TYPE_F32
+#define MIX3a real
+#define VALUE3a 1.2345
+#define COMMENT3a "A number with a decimal point"
+
+#define NAME3b "anInt"
+#define TYPE3b PS_TYPE_S32
+#define MIX3b integer
+#define VALUE3b 98765
+#define COMMENT3b ""
+
+#define NAME5 "Numbers"
+#define TYPE5 PS_TYPE_F32
+#define MIX5 real
+#define VALUE5 1.23
+#define COMMENT5 "Number one"
+
+#define NAME7 "Numbers"
+#define TYPE7 PS_DATA_STRING
+#define MIX7 string
+#define VALUE7 "One point two three"
+#define COMMENT7 "Number two"
+
+#define NAME8 "Numbers"
+#define TYPE8 PS_TYPE_BOOL
+#define MIX8 boolean
+#define VALUE8 1
+#define COMMENT8 "This is true"
+
+#define NAME10 "SomeBoolean"
+#define TYPE10 PS_TYPE_BOOL
+#define MIX10 boolean
+#define VALUE10 0
+#define COMMENT10 ""
+
+#define MULTI_NAME "Numbers"
+
+#define BAD_NAME "boolean"
+
+typedef union {
+    const char *string;
+    float real;
+    int integer;
+    bool boolean;
+} mixedType;
+
+
+// Generate the metadata config file to be parsed
+static void generateMDConfig(void)
+{
+    FILE *fp = fopen(FILENAME, "w");    // Configuration file
+    fprintf(fp, "%s", STRING1);
+    fprintf(fp, "%s", STRING2);
+    fprintf(fp, "%s", STRING3);
+    fprintf(fp, "%s", STRING3a);
+    fprintf(fp, "%s", STRING3b);
+    fprintf(fp, "%s", STRING3c);
+    fprintf(fp, "%s", STRING4);
+    fprintf(fp, "%s", STRING5);
+    fprintf(fp, "%s", STRING6);
+    fprintf(fp, "%s", STRING7);
+    fprintf(fp, "%s", STRING8);
+    fprintf(fp, "%s", STRING9);
+    fprintf(fp, "%s", STRING10);
+    fclose(fp);
+}
+
+static void *testItem(const psMetadataItem *item, // Item to check
+                      const char *name, // Name of the item
+                      int type, // Type for item
+                      const char *comment, // Comment for the item
+                      mixedType value // Value for the item
+                     )
+{
+    psMemId id = psMemGetId();
+    skip_start(!item, 3, "Skipping 4 tests because !item");
+    ok(strcmp(item->name, name) == 0, "item->name = %s", item->name);
+    ok(strcmp(item->comment, comment) == 0, "item->comment = %s", item->comment);
+    ok(item->type == type, "item->type = %x", item->type);
+    skip_end();
+
+    if (item->type == PS_DATA_METADATA || item->type == PS_DATA_METADATA_MULTI) {
+        return item->data.V;
+    }
+
+    skip_start(item->type != type, 1, "Skipping 1 test because the item has incorrect type");
+    switch (item->type) {
+    case PS_DATA_STRING: {
+            ok(strcmp(item->data.V, value.string) == 0, "item->data.V = %s", item->data.V);
+            break;
+        }
+    case PS_TYPE_F32: {
+            ok(item->data.F32 == value.real, "item->data.F32 = %f", item->data.F32);
+            break;
+        }
+    case PS_TYPE_S32: {
+            ok(item->data.S32 == value.integer, "item->data.S32 = %d", item->data.S32);
+            break;
+        }
+    case PS_TYPE_BOOL: {
+            ok(item->data.B == value.boolean, "item->data.B = %d", item->data.B);
+            break;
+        }
+    default: {
+            ok(false, "Unknown item->type = %x", item->type);
+        }
+    }
+    skip_end();
+    ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    return NULL;
+}
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(62);
+
+    generateMDConfig();
+
+    unsigned int numBadLines = 0;            // Number of bad lines
+    psMetadata *md = psMetadataConfigRead(NULL, &numBadLines, FILENAME, false);
+    unlink (FILENAME);
+
+    ok(md, "md = %x", md);
+    is_int(numBadLines, 1, "number of bad lines"); // One bad line from boolean
+    skip_start(!md || numBadLines != 1, 0,
+               "Skipping 0 tests because psMetadataConfigRead failed.");
+    ok(psListLength(md->list) == 6, "size = %d", psListLength(md->list));
+
+    {
+        // Go through and make sure everything's there
+        psMetadataIterator *iterator = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL); // Iterator
+
+        {
+            psMetadataItem *item = psMetadataGetAndIncrement(iterator);
+            mixedType value;
+            value.MIX2 = VALUE2;
+            testItem(item, NAME2, TYPE2, COMMENT2, value);
+        }
+
+        {
+            psMetadataItem *item = psMetadataGetAndIncrement(iterator);
+            mixedType value;            // Needed only to check the other stuff
+            psMetadata *nested = testItem(item, NAME3, TYPE3, COMMENT3, value);
+            ok(psListLength(nested->list) == 2, "size = %d", psListLength(nested->list));
+            skip_start(psListLength(nested->list) != 2, 8, "Skipping 8 tests because wrong size");
+            psMetadataIterator *nestedIter = psMetadataIteratorAlloc(nested, PS_LIST_HEAD, NULL);
+
+            {
+                psMetadataItem *item = psMetadataGetAndIncrement(nestedIter);
+                mixedType value;
+                value.MIX3a = VALUE3a;
+                testItem(item, NAME3a, TYPE3a, COMMENT3a, value);
+            }
+
+            {
+                psMetadataItem *item = psMetadataGetAndIncrement(nestedIter);
+                mixedType value;
+                value.MIX3b = VALUE3b;
+                testItem(item, NAME3b, TYPE3b, COMMENT3b, value);
+            }
+
+            psFree(nestedIter);
+            skip_end();
+        }
+
+        {
+            psMetadataItem *item = psMetadataGetAndIncrement(iterator);
+            mixedType value;
+            value.MIX5 = VALUE5;
+            testItem(item, NAME5, TYPE5, COMMENT5, value);
+        }
+
+        {
+            psMetadataItem *item = psMetadataGetAndIncrement(iterator);
+            mixedType value;
+            value.MIX7 = VALUE7;
+            testItem(item, NAME7, TYPE7, COMMENT7, value);
+        }
+
+        {
+            psMetadataItem *item = psMetadataGetAndIncrement(iterator);
+            mixedType value;
+            value.MIX8 = VALUE8;
+            testItem(item, NAME8, TYPE8, COMMENT8, value);
+        }
+
+        {
+            psMetadataItem *item = psMetadataGetAndIncrement(iterator);
+            mixedType value;
+            value.MIX10 = VALUE10;
+            testItem(item, NAME10, TYPE10, COMMENT10, value);
+        }
+
+        psFree(iterator);
+    }
+
+    {
+        // Check that the correct item is MULTI
+        psMetadataItem *item = psMetadataLookup(md, MULTI_NAME);
+        mixedType value;                // Needed only so we can test other stuff
+        psList *multi = testItem(item, MULTI_NAME, PS_DATA_METADATA_MULTI, "", value);
+        ok(psListLength(multi) == 3, "length = %d", psListLength(multi));
+        skip_start(psListLength(multi) != 3, 12, "Skipping 12 tests because MULTI failed.");
+        psListIterator *multiIter = psListIteratorAlloc(multi, PS_LIST_HEAD, false); // Iterator
+
+        {
+            psMetadataItem *item = psListGetAndIncrement(multiIter);
+            mixedType value;
+            value.MIX5 = VALUE5;
+            testItem(item, NAME5, TYPE5, COMMENT5, value);
+        }
+
+        {
+            psMetadataItem *item = psListGetAndIncrement(multiIter);
+            mixedType value;
+            value.MIX7 = VALUE7;
+            testItem(item, NAME7, TYPE7, COMMENT7, value);
+        }
+
+        {
+            psMetadataItem *item = psListGetAndIncrement(multiIter);
+            mixedType value;
+            value.MIX8 = VALUE8;
+            testItem(item, NAME8, TYPE8, COMMENT8, value);
+        }
+        psFree(multiIter);
+        skip_end();
+    }
+
+    {
+        // Check that the bad one isn't there
+        psMetadataItem *item = psMetadataLookup(md, BAD_NAME);
+        ok(!item, "item = %x", item);
+    }
+
+    skip_end();
+
+    psFree(md);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigWrite.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigWrite.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfigWrite.c	(revision 22322)
@@ -0,0 +1,86 @@
+/**
+ *  C Implementation: tap_psMetadataConfigWrite
+ *
+ * Description:  Tests for psMetadataConfigWrite
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(11);
+
+
+    //Return false for NULL metadata input
+    {
+        psMemId id = psMemGetId();
+        ok( !psMetadataConfigWrite(NULL, "mdcfg.wrt"),
+            "return false for NULL metadata input.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return false for NULL filename input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        ok( !psMetadataConfigWrite(md, NULL),
+            "return false for NULL filename input.");
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return false for invalid filename input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        ok( !psMetadataConfigWrite(md, "."),
+            "return false for invalid filename input.");
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return true for empty metadata input (creates an empty file)
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        ok(psMetadataConfigWrite(md, "mdcfg.wrt"), "return false for empty metadata input.");
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return true for valid inputs
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psMetadataAddBool(md, PS_LIST_TAIL, "item1-1", 0, "I am a boolean", true);
+        ok( psMetadataConfigWrite(md, "mdcfg.wrt"),
+            "return true for valid inputs.");
+        char configTest[61];
+        char fileStr[62];
+        FILE *mdcfg = fopen("mdcfg.wrt", "r");
+        fgets(fileStr, 61, mdcfg);
+        strncpy(configTest,
+                "item1-1          BOOL      T                # I am a boolean", 60);
+        is_strn(configTest, fileStr, 60,
+                "return correct output.");
+        fclose(mdcfg);
+        remove
+            ("mdcfg.wrt");
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfig_input.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfig_input.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataConfig_input.c	(revision 22322)
@@ -0,0 +1,136 @@
+/**
+ * tap_psMetdataConfig_input.c
+ *
+ * Description:  Tests for psMetadataConfigRead
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ * Author: Joshua Hoblitt, University of Hawaii 2007
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(30);
+
+
+    // Return NULL for NULL filename input
+    {
+        psMemId id = psMemGetId();
+        unsigned int nfail = 0;
+        psMetadata *md = psMetadataConfigRead(NULL, &nfail, NULL, false);
+        ok(md == NULL, "return NULL for NULL filename input.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return NULL for invalid filename input
+    {
+        psMemId id = psMemGetId();
+        unsigned int nfail = 0;
+        psMetadata *md = psMetadataConfigRead(NULL, &nfail, ".", false);
+        ok(md == NULL, "return NULL for invalid filename input.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return NULL for missing file input
+    {
+        psMemId id = psMemGetId();
+        unsigned int nfail = 0;
+        psMetadata *md = psMetadataConfigRead(NULL, &nfail, "table4.dat", false);
+        ok(md == NULL, "return NULL for missing file input.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return NULL for read-only file with comments, bad syntax, and no good
+    // lines
+    {
+        psMemId id = psMemGetId();
+        unsigned int nfail = 0;
+        psMetadata *md = psMetadataConfigRead(NULL, &nfail, "table3.dat", false);
+        ok(md == NULL, "return NULL for all bad syntax");
+        is_int(nfail, 0, "correct nfail");
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return true for valid inputs - overwrite = false
+    {
+        psMemId id = psMemGetId();
+        unsigned int nfail = 0;
+        psMetadata *md = psMetadataConfigRead(NULL, &nfail, "metaconf.in", false);
+
+        ok(md != NULL, "return metadata for valid input");
+        is_int(nfail, 27, "return correct nFail count");
+
+        skip_start(md == NULL, 8,
+                     "Skipping 1 tests because metadata container is empty!");
+
+        // look at the first item
+        psMetadataItem *item = psMetadataGet(md, PS_LIST_HEAD);
+        // XXX shouldn't assume item isn't NULL
+        is_str(item->name, "item1", "metadataItem name");
+        ok(item->type == PS_DATA_BOOL, "metadataItem type");
+        is_bool(item->data.B, true, "metdataItem value");
+        is_str(item->comment, "I am a boolean", "metadataItem comment.");
+
+        // look at the last item
+        item = psMetadataGet(md, PS_LIST_TAIL);
+        // XXX shouldn't assume item isn't NULL
+        is_str(item->name, "M2", "metadataItem name");
+        ok(item->type == PS_DATA_S32, "metdataItem type");
+        is_int(item->data.S32, 666, "metdataItem value");
+        is_str(item->comment, "this line is \"GOOD\" but parsed in the top level", "metadataItem comment.");
+        skip_end();
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return true for valid inputs - overwrite = true
+    {
+        psMemId id = psMemGetId();
+        unsigned int nfail = 0;
+        psMetadata *md = psMetadataConfigRead(NULL, &nfail, "metaconf.in", true);
+
+        ok(md != NULL, "return metadata for valid input");
+        is_int(nfail, 23, "return correct nFail count");
+
+        skip_start(md == NULL, 7,
+                     "Skipping 1 tests because metadata container is empty!");
+
+        // look at the first item
+        psMetadataItem *item = psMetadataGet(md, PS_LIST_HEAD);
+        // XXX shouldn't assume item isn't NULL
+        // note that item1 gets added to the "tail" of the metdata was it's
+        // replaced
+        is_str(item->name, "item2", "metadataItem name");
+        ok(item->type == PS_DATA_S32, "metdataItem type");
+        is_bool(item->data.S32, 55, "metadataItem value");
+        is_str(item->comment, "I am int", "metadataItem value");
+
+        // look at the last item
+        item = psMetadataGet(md, PS_LIST_TAIL);
+        // XXX shouldn't assume item isn't NULL
+        // meta1 is from "the bad section"
+        is_str(item->name, "meta1", "metadataItem name");
+        ok(item->type == PS_DATA_METADATA, "metadataItem type");
+
+        is_str(item->comment, "duplicate", "metadataItem comment");
+        skip_end();
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataItemCompare.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataItemCompare.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataItemCompare.c	(revision 22322)
@@ -0,0 +1,198 @@
+/**
+ *  C Implementation: tap_psMetadataItemCompare
+ *
+ * Description:  Tests for psMetadataItemCompare
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(22);
+
+    // psMetadataItemCompare() tests
+    {
+        psMemId id = psMemGetId();
+        psMetadataItem *compare = NULL;
+        psMetadataItem *template = NULL;
+        psMetadataItem *itemCopy = NULL;
+        psMetadataItem *item = psMetadataItemAlloc("Snickers", PS_DATA_BOOL, "No Comment", true)
+                               ;
+    
+        //Return false for NULL compare psMetadataItem
+        {
+            ok( !psMetadataItemCompare(compare, item),
+                "psMetadataItemCompare:      return false for NULL compare input." );
+        }
+        //Return false for NULL template psMetadataItem
+        {
+            ok( !psMetadataItemCompare(item, template),
+                "psMetadataItemCompare:      return false for NULL template input." );
+        }
+    
+        //Return false for psMetadataItem's with different names
+        {
+            template = psMetadataItemAlloc("Milky Way", PS_DATA_BOOL, "No Comment", true)
+                       ;
+            compare = psMetadataItemAlloc("Snickers", PS_DATA_S32, "No Comment", 1);
+            ok( !psMetadataItemCompare(template, item),
+                "psMetadataItemCompare:      return false for input with different names.");
+        }
+
+        //Return true for an exact match (copied psMetadataItem)
+        {
+            itemCopy = psMetadataItemCopy(item);
+            ok( psMetadataItemCompare(itemCopy, item),
+                "psMetadataItemCompare:      return true for comparison of identical psMetadataItem's.");
+        }
+    
+        //Return true for psMetadataItem's with different types but otherwise identical
+        {
+            ok( psMetadataItemCompare(compare, item),
+                "psMetadataItemCompare:      return true for valid compare of different type.");
+        }
+    
+        //Return true for comparing Bool with F64
+        {
+            psFree(compare);
+            compare = psMetadataItemAlloc("Snickers", PS_DATA_F64, "No Comment", 1.0000);
+            ok( psMetadataItemCompare(compare, item),
+                "psMetadataItemCompare:      return false for comparison of F64 to bool." );
+        }
+
+        //Return true for comparing F64 with Bool
+        {
+            ok( psMetadataItemCompare(item, compare),
+                "psMetadataItemCompare:      return false for comparison of bool to F64." );
+        }
+    
+        //Return true for comparing S32 with F32 of same value
+        {
+            psFree(template);
+            template = psMetadataItemAlloc("Snickers", PS_DATA_S32, "No Comment", 1);
+            psFree(compare);
+            compare = psMetadataItemAlloc("Snickers", PS_DATA_F32, "No Comment", 1.00);
+            ok( psMetadataItemCompare(compare, template),
+                "psMetadataItemCompare:      return true for comparison of S32 to F32." );
+        }
+
+        //Return true for comparing F32 with S32 of same value
+        {
+            ok( psMetadataItemCompare(template, compare),
+                "psMetadataItemCompare:      return true for comparison of F32 to S32." );
+        }
+    
+        //Return true for comparing U32 with S16 of same value
+        {
+            psFree(template);
+            template = psMetadataItemAlloc("Snickers", PS_DATA_S16, "No Comment", 1);
+            psFree(compare);
+            compare = psMetadataItemAlloc("Snickers", PS_DATA_U32, "No Comment", 1);
+            ok( psMetadataItemCompare(compare, template),
+                "psMetadataItemCompare:     return true for comparison of S16 to U32." );
+        }
+
+        //Return true for comparing S16 with U32 of same value
+        {
+            ok( psMetadataItemCompare(template, compare),
+                "psMetadataItemCompare:     return true for comparison of U32 to S16." );
+        }
+    
+        //Return true for comparing U8 with U16 of same value
+        {
+            psFree(template);
+            template = psMetadataItemAlloc("Snickers", PS_DATA_U8, "No Comment", 1);
+            psFree(compare);
+            compare = psMetadataItemAlloc("Snickers", PS_DATA_U16, "No Comment", 1);
+            ok( psMetadataItemCompare(compare, template),
+                "psMetadataItemCompare:     return true for comparison of U8 to U16." );
+        }
+
+        //Return true for comparing U16 with U8 of same value
+        {
+            ok( psMetadataItemCompare(template, compare),
+                "psMetadataItemCompare:     return true for comparison of U16 to U8." );
+        }
+    
+        //Return true for comparing U8 with S8 of same value
+        {
+            psFree(compare);
+            compare = psMetadataItemAlloc("Snickers", PS_DATA_S8, "No Comment", 1);
+            ok( psMetadataItemCompare(compare, template),
+                "psMetadataItemCompare:     return true for comparison of U8 to S8." );
+        }
+
+        //Return true for comparing S8 with U8 of same value
+        {
+            ok( psMetadataItemCompare(template, compare),
+               "psMetadataItemCompare:     return true for comparison of S8 to U8." );
+        }
+    
+        //Return false for comparing U8 with S64 of same value
+        {
+            psFree(compare);
+            compare = psMetadataItemAlloc("Snickers", PS_DATA_S64, "No Comment", 1);
+            ok( !psMetadataItemCompare(compare, template),
+                "psMetadataItemCompare:     return false for comparison of U8 to S64*.  "
+                "*not yet implemented" );
+        }
+
+        //Return false for comparing S64 with U8 of same value
+        {
+            ok( !psMetadataItemCompare(template, compare),
+                "psMetadataItemCompare:     return false for comparison of S64* to U8.  "
+                "*not yet implemented" );
+        }
+    
+        //Return false for comparing string of different value
+        {
+            psFree(compare);
+            compare = psMetadataItemAlloc("Snickers", PS_DATA_STRING, "No Comment",
+                                          "Not Going Anywhere?");
+            psFree(template);
+            template = psMetadataItemAlloc("Snickers", PS_DATA_STRING, "No Comment", "For a While?");
+            ok( !psMetadataItemCompare(compare, template),
+                "psMetadataItemCompare:     return false for comparison of different psString's.");
+        }
+    
+        //Return true for comparing string of same value
+        {
+            psFree(template);
+            template = psMetadataItemAlloc("Snickers", PS_DATA_STRING, "No Comment",
+                                           "Not Going Anywhere?");
+            ok( psMetadataItemCompare(compare, template),
+                "psMetadataItemCompare:     return true for comparison of identical psString's.");
+        }
+    
+        //Return false for comparing string with bool
+        {
+            psFree(template);
+            template = psMetadataItemAlloc("Snickers", PS_DATA_BOOL, "No Comment", false);
+            ok( !psMetadataItemCompare(compare, template),
+                "psMetadataItemCompare:     return false for comparison of psString with bool.");
+        }
+
+        //Return false for comparing bool with string
+        {
+            psFree(template);
+            template = psMetadataItemAlloc("Snickers", PS_DATA_BOOL, "No Comment", false);
+            ok( !psMetadataItemCompare(template, compare),
+                "psMetadataItemCompare:     return false for comparison of psString with bool.");
+        }
+    
+        psFree(compare);
+        psFree(template);
+        psFree(itemCopy);
+        psFree(item);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataItemParse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataItemParse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataItemParse.c	(revision 22322)
@@ -0,0 +1,519 @@
+/**
+ *  C Implementation: tap_psMetadataItemParse
+ *
+ * Description:  Tests for psMetadataItemParse
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+void testItemParse(void);
+
+int main (void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(112);
+
+    // psMetadataItemParse() tests
+    {
+        psMemId id = psMemGetId();
+        psMetadataItem *itemBool = psMetadataItemAlloc("bool", PS_DATA_BOOL, "No Comment", true);
+        psMetadataItem *itemF32 = psMetadataItemAlloc("f32", PS_DATA_F32, "No Comment", 0.00);
+        psMetadataItem *itemF64 = psMetadataItemAlloc("f64", PS_DATA_F64, "No Comment", 0.50);
+        psMetadataItem *itemS8 = psMetadataItemAlloc("s8", PS_DATA_S8, "No Comment", 1);
+        psMetadataItem *itemS16 = psMetadataItemAlloc("s16", PS_DATA_S16, "No Comment", 0);
+        psMetadataItem *itemS32 = psMetadataItemAlloc("s32", PS_DATA_S32, "No Comment", 1);
+        psMetadataItem *itemU8 = psMetadataItemAlloc("u8", PS_DATA_U8, "No Comment", 2);
+        psMetadataItem *itemU16 = psMetadataItemAlloc("u16", PS_DATA_U16, "No Comment", 0);
+        psMetadataItem *itemU32 = psMetadataItemAlloc("u32", PS_DATA_U32, "No Comment", 1);
+        psMetadataItem *itemString1 = psMetadataItemAlloc("String", PS_DATA_STRING, "", "true");
+        psMetadataItem *itemString2 = psMetadataItemAlloc("String", PS_DATA_STRING, "", "1.666");
+        psMetadataItem *itemString11 = psMetadataItemAlloc("String", PS_DATA_STRING, "", "false");
+        psMetadataItem *itemUnsupported = psMetadataItemAlloc("s64", PS_DATA_S64, "", 1);
+
+        //TESTS:  Bool, F32, F64, S8, S16, S32, U8, U16, U32, String
+        //BOOL Test
+        {
+            //Return false for NULL item
+            ok ( !psMetadataItemParseBool(NULL),
+                 "psMetadataItemParse:      return false in BOOL for NULL psMetadataItem "
+                 "input." );
+            //Return false for non-supported type S64
+            ok ( !psMetadataItemParseBool(itemUnsupported),
+                 "psMetadataItemParse:      return false in BOOL for S64 (unsupported) "
+                 "psMetadataItem input." );
+            //Return true for string with "true"
+            ok ( psMetadataItemParseBool(itemString1),
+                 "psMetadataItemParse:      return true in BOOL for string-item with "
+                 "value 'true'." );
+            //Return false for string with "false"
+            ok ( !psMetadataItemParseBool(itemString11),
+                 "psMetadataItemParse:      return false in BOOL for string-item with "
+                 "value 'false'." );
+            //Return false for F32 value 0.0
+            ok ( !psMetadataItemParseBool(itemF32),
+                 "psMetadataItemParse:      return false in BOOL for F32-item with "
+                 "value '0.0'." );
+            //Return true for F64 value 0.5
+            ok ( psMetadataItemParseBool(itemF64),
+                 "psMetadataItemParse:      return true in BOOL for F64-item with "
+                 "value '0.5'." );
+            //Return true for S8 value 1
+            ok ( psMetadataItemParseBool(itemS8),
+                 "psMetadataItemParse:      return true in BOOL for S8-item with "
+                 "value '1'." );
+            //Return false for S16 value 0
+            ok ( !psMetadataItemParseBool(itemS16),
+                 "psMetadataItemParse:      return false in BOOL for S16-item with "
+                 "value '0'." );
+            //Return true for S32 value 1
+            ok ( psMetadataItemParseBool(itemS32),
+                 "psMetadataItemParse:      return true in BOOL for S32-item with "
+                 "value '1'." );
+            //Return true for U8 value 2
+            ok ( psMetadataItemParseBool(itemU8),
+                 "psMetadataItemParse:     return true in BOOL for U8-item with "
+                 "value '2'." );
+            //Return false for U16 value 0
+            ok ( !psMetadataItemParseBool(itemU16),
+                 "psMetadataItemParse:     return false in BOOL for U16-item with "
+                 "value '0'." );
+            //Return true for U32 value 1
+            ok ( psMetadataItemParseBool(itemU32),
+                 "psMetadataItemParse:     return true in BOOL for U32-item with "
+                 "value '1'." );
+        }
+
+        //F32 Test
+        {
+            //Return NaN for NULL item
+            ok ( isnan(psMetadataItemParseF32(NULL)),
+                 "psMetadataItemParse:     return NaN for NULL psMetadataItem input." );
+            //Return NaN for non-supported type S64
+            ok ( isnan(psMetadataItemParseF32(itemUnsupported)),
+                 "psMetadataItemParse:     return NaN in F32 for S64 (unsupported) "
+                 "psMetadataItem input." );
+            //Return 1.666 for string with "1.666"
+            is_float( psMetadataItemParseF32(itemString2), 1.666,
+                      "psMetadataItemParse:     return 1.666 in F32 for string-item with "
+                      "value '1.666'." );
+            //Return 0.0 for F32 value 0.0
+            is_float( psMetadataItemParseF32(itemF32), 0.0,
+                      "psMetadataItemParse:     return 0.0 in F32 for F32-item with "
+                      "value '0.0'." );
+            //Return 0.5 for F64 value 0.5
+            is_float( psMetadataItemParseF32(itemF64), 0.5,
+                      "psMetadataItemParse:     return 0.5 in F32 for F64-item with "
+                      "value '0.5'." );
+            //Return 1.0 for S8 value 1
+            is_float( psMetadataItemParseF32(itemS8), 1.0,
+                      "psMetadataItemParse:     return 1.0 in F32 for S8-item with "
+                      "value '1'." );
+            //Return 0.0 for S16 value 0
+            is_float( psMetadataItemParseF32(itemS16), 0.0,
+                      "psMetadataItemParse:     return 0.0 in F32 for S16-item with "
+                      "value '0'." );
+            //Return 1.0 for S32 value 1
+            is_float( psMetadataItemParseF32(itemS32), 1.0,
+                      "psMetadataItemParse:     return 1.0 in F32 for S32-item with "
+                      "value '1'." );
+            //Return 2.0 for U8 value 2
+            is_float( psMetadataItemParseF32(itemU8), 2.0,
+                      "psMetadataItemParse:     return 2.0 in F32 for U8-item with "
+                      "value '2'." );
+            //Return 0.0 for U16 value 0
+            is_float( psMetadataItemParseF32(itemU16), 0.0,
+                      "psMetadataItemParse:     return 0.0 in F32 for U16-item with "
+                      "value '0'." );
+            //Return 1.0 for U32 value 1
+            is_float( psMetadataItemParseF32(itemU32), 1.0,
+                      "psMetadataItemParse:     return 1.0 in F32 for U32-item with "
+                      "value '1'." );
+        }
+
+        //F64 Test
+        {
+            //Return NaN for NULL item
+            ok ( isnan(psMetadataItemParseF64(NULL)),
+                 "psMetadataItemParse:     return NaN for NULL psMetadataItem input." );
+            //Return NaN for non-supported type S64
+            ok ( isnan(psMetadataItemParseF64(itemUnsupported)),
+                 "psMetadataItemParse:     return NaN in F64 for S64 (unsupported) "
+                 "psMetadataItem input." );
+            //Return 1.666 for string with "1.666"
+            is_double_tol( psMetadataItemParseF64(itemString2), 1.666, 0.00001,
+                           "psMetadataItemParse:     return 1.666 in F64 for string-item with "
+                           "value '1.666'." );
+            //Return 0.0 for F32 value 0.0
+            is_double( psMetadataItemParseF64(itemF32), 0.0,
+                       "psMetadataItemParse:     return 0.0 in F64 for F32-item with "
+                       "value '0.0'." );
+            //Return 0.5 for F64 value 0.5
+            is_double( psMetadataItemParseF64(itemF64), 0.5,
+                       "psMetadataItemParse:     return 0.5 in F64 for F64-item with "
+                       "value '0.5'." );
+            //Return 1.0 for S8 value 1
+            is_double( psMetadataItemParseF64(itemS8), 1.0,
+                       "psMetadataItemParse:     return 1.0 in F64 for S8-item with "
+                       "value '1'." );
+            //Return 0.0 for S16 value 0
+            is_double( psMetadataItemParseF64(itemS16), 0.0,
+                       "psMetadataItemParse:     return 0.0 in F64 for S16-item with "
+                       "value '0'." );
+            //Return 1.0 for S32 value 1
+            is_double( psMetadataItemParseF64(itemS32), 1.0,
+                       "psMetadataItemParse:     return 1.0 in F64 for S32-item with "
+                       "value '1'." );
+            //Return 2.0 for U8 value 2
+            is_double( psMetadataItemParseF64(itemU8), 2.0,
+                       "psMetadataItemParse:     return 2.0 in F64 for U8-item with "
+                       "value '2'." );
+            //Return 0.0 for U16 value 0
+            is_double( psMetadataItemParseF64(itemU16), 0.0,
+                       "psMetadataItemParse:     return 0.0 in F64 for U16-item with "
+                       "value '0'." );
+            //Return 1.0 for U32 value 1
+            is_double( psMetadataItemParseF64(itemU32), 1.0,
+                       "psMetadataItemParse:     return 1.0 in F64 for U32-item with "
+                       "value '1'." );
+        }
+
+        //S8 Test
+        {
+            //Return 0 for NULL item
+            ok ( psMetadataItemParseS8(NULL) == 0,
+                 "psMetadataItemParse:     return 0 for NULL psMetadataItem input." );
+            //Return 0 for non-supported type S64
+            ok ( psMetadataItemParseS8(itemUnsupported) == 0,
+                 "psMetadataItemParse:     return 0 in S8 for S64 (unsupported) "
+                 "psMetadataItem input." );
+            //Return 2 for string with "1.666"
+            ok ( psMetadataItemParseS8(itemString2) == 1,
+                 "psMetadataItemParse:     return 1 in S8 for string-item with "
+                 "value '1.666'." );
+            //Return 0 for F32 value 0.0
+            ok ( psMetadataItemParseS8(itemF32) == 0,
+                 "psMetadataItemParse:     return 0 in S8 for F32-item with "
+                 "value '0.0'." );
+            //Return 1 for F64 value 0.5
+            ok ( psMetadataItemParseS8(itemF64) == 0,
+                 "psMetadataItemParse:     return 0 in S8 for F64-item with "
+                 "value '0.5'." );
+            //Return 1 for S8 value 1
+            ok ( psMetadataItemParseS8(itemS8) == 1,
+                 "psMetadataItemParse:     return 1 in S8 for S8-item with "
+                 "value '1'." );
+            //Return 0 for S16 value 0
+            ok ( psMetadataItemParseS8(itemS16) == 0,
+                 "psMetadataItemParse:     return 0 in S8 for S16-item with "
+                 "value '0'." );
+            //Return 1 for S32 value 1
+            ok ( psMetadataItemParseS8(itemS32) == 1,
+                 "psMetadataItemParse:     return 1 in S8 for S32-item with "
+                 "value '1'." );
+            //Return 2 for U8 value 2
+            ok ( psMetadataItemParseS8(itemU8) == 2,
+                 "psMetadataItemParse:     return 2 in S8 for U8-item with "
+                 "value '2'." );
+            //Return 0 for U16 value 0
+            ok ( psMetadataItemParseS8(itemU16) == 0,
+                 "psMetadataItemParse:     return 0 in S8 for U16-item with "
+                 "value '0'." );
+            //Return 1 for U32 value 1
+            ok ( psMetadataItemParseS8(itemU32) == 1,
+                 "psMetadataItemParse:     return 1 in S8 for U32-item with "
+                 "value '1'." );
+        }
+
+        //S16 Test
+        {
+            //Return 0 for NULL item
+            ok ( psMetadataItemParseS16(NULL) == 0,
+                 "psMetadataItemParse:     return 0 for NULL psMetadataItem input." );
+            //Return 0 for non-supported type S64
+            ok ( psMetadataItemParseS16(itemUnsupported) == 0,
+                 "psMetadataItemParse:     return 0 in S16 for S64 (unsupported) "
+                 "psMetadataItem input." );
+            //Return 2 for string with "1.666"
+            ok ( psMetadataItemParseS16(itemString2) == 1,
+                 "psMetadataItemParse:     return 1 in S16 for string-item with "
+                 "value '1.666'." );
+            //Return 0 for F32 value 0.0
+            ok ( psMetadataItemParseS16(itemF32) == 0,
+                 "psMetadataItemParse:     return 0 in S16 for F32-item with value '0.0'." );
+            //Return 1 for F64 value 0.5
+            ok ( psMetadataItemParseS16(itemF64) == 0,
+                 "psMetadataItemParse:     return 0 in S16 for F64-item with value '0.5'." );
+            //Return 1 for S8 value 1
+            ok ( psMetadataItemParseS16(itemS8) == 1,
+                 "psMetadataItemParse:     return 1 in S16 for S8-item with value '1'." );
+            //Return 0 for S16 value 0
+            ok ( psMetadataItemParseS16(itemS16) == 0,
+                 "psMetadataItemParse:     return 0 in S16 for S16-item with value '0'." );
+            //Return 1 for S32 value 1
+            ok ( psMetadataItemParseS16(itemS32) == 1,
+                 "psMetadataItemParse:     return 1 in S16 for S32-item with value '1'." );
+            //Return 2 for U8 value 2
+            ok ( psMetadataItemParseS16(itemU8) == 2,
+                 "psMetadataItemParse:     return 2 in S16 for U8-item with value '2'." );
+            //Return 0 for U16 value 0
+            ok ( psMetadataItemParseS16(itemU16) == 0,
+                 "psMetadataItemParse:     return 0 in S16 for U16-item with value '0'." );
+            //Return 1 for U32 value 1
+            ok ( psMetadataItemParseS16(itemU32) == 1,
+                 "psMetadataItemParse:     return 1 in S16 for U32-item with value '1'." );
+        }
+
+        //S32 Test
+        {
+            //Return 0 for NULL item
+            ok ( psMetadataItemParseS32(NULL) == 0,
+                 "psMetadataItemParse:     return 0 for NULL psMetadataItem input." );
+            //Return 0 for non-supported type S64
+            ok ( psMetadataItemParseS32(itemUnsupported) == 0,
+                 "psMetadataItemParse:     return 0 in S32 for S64 (unsupported) "
+                 "psMetadataItem input." );
+            //Return 2 for string with "1.666"
+            ok ( psMetadataItemParseS32(itemString2) == 1,
+                 "psMetadataItemParse:     return 1 in S32 for string-item with "
+                 "value '1.666'." );
+            //Return 0 for F32 value 0.0
+            ok ( psMetadataItemParseS32(itemF32) == 0,
+                 "psMetadataItemParse:     return 0 in S32 for F32-item with value '0.0'." );
+            //Return 1 for F64 value 0.5
+            ok ( psMetadataItemParseS32(itemF64) == 0,
+                 "psMetadataItemParse:     return 0 in S32 for F64-item with value '0.5'." );
+            //Return 1 for S8 value 1
+            ok ( psMetadataItemParseS32(itemS8) == 1,
+                 "psMetadataItemParse:     return 1 in S32 for S8-item with value '1'." );
+            //Return 0 for S16 value 0
+            ok ( psMetadataItemParseS32(itemS16) == 0,
+                 "psMetadataItemParse:     return 0 in S32 for S16-item with value '0'." );
+            //Return 1 for S32 value 1
+            ok ( psMetadataItemParseS32(itemS32) == 1,
+                 "psMetadataItemParse:     return 1 in S32 for S32-item with value '1'." );
+            //Return 2 for U8 value 2
+            ok ( psMetadataItemParseS32(itemU8) == 2,
+                 "psMetadataItemParse:     return 2 in S32 for U8-item with value '2'." );
+            //Return 0 for U16 value 0
+            ok ( psMetadataItemParseS32(itemU16) == 0,
+                 "psMetadataItemParse:     return 0 in S32 for U16-item with value '0'." );
+            //Return 1 for U32 value 1
+            ok ( psMetadataItemParseS32(itemU32) == 1,
+                 "psMetadataItemParse:     return 1 in S32 for U32-item with value '1'." );
+        }
+
+        //U8 Test
+        {
+            //Return 0 for NULL item
+            ok ( psMetadataItemParseU8(NULL) == 0,
+                 "psMetadataItemParse:     return 0 for NULL psMetadataItem input." );
+            //Return 0 for non-supported type S64
+            ok ( psMetadataItemParseU8(itemUnsupported) == 0,
+                 "psMetadataItemParse:     return 0 in U8 for S64 (unsupported) "
+                 "psMetadataItem input." );
+            //Return 2 for string with "1.666"
+            ok ( psMetadataItemParseU8(itemString2) == 1,
+                 "psMetadataItemParse:     return 1 in U8 for string-item with "
+                 "value '1.666'." );
+            //Return 0 for F32 value 0.0
+            ok ( psMetadataItemParseU8(itemF32) == 0,
+                 "psMetadataItemParse:     return 0 in U8 for F32-item with value '0.0'." );
+            //Return 1 for F64 value 0.5
+            ok ( psMetadataItemParseU8(itemF64) == 0,
+                 "psMetadataItemParse:     return 0 in U8 for F64-item with value '0.5'." );
+            //Return 1 for S8 value 1
+            ok ( psMetadataItemParseU8(itemS8) == 1,
+                 "psMetadataItemParse:     return 1 in U8 for S8-item with value '1'." );
+            //Return 0 for S16 value 0
+            ok ( psMetadataItemParseU8(itemS16) == 0,
+                 "psMetadataItemParse:     return 0 in U8 for S16-item with value '0'." );
+            //Return 1 for S32 value 1
+            ok ( psMetadataItemParseU8(itemS32) == 1,
+                 "psMetadataItemParse:     return 1 in U8 for S32-item with value '1'." );
+            //Return 2 for U8 value 2
+            ok ( psMetadataItemParseU8(itemU8) == 2,
+                 "psMetadataItemParse:     return 2 in U8 for U8-item with value '2'." );
+            //Return 0 for U16 value 0
+            ok ( psMetadataItemParseU8(itemU16) == 0,
+                 "psMetadataItemParse:     return 0 in U8 for U16-item with value '0'." );
+            //Return 1 for U32 value 1
+            ok ( psMetadataItemParseU8(itemU32) == 1,
+                 "psMetadataItemParse:     return 1 in U8 for U32-item with value '1'." );
+        }
+
+        //U16 Test
+        {
+            //Return 0 for NULL item
+            ok ( psMetadataItemParseU16(NULL) == 0,
+                 "psMetadataItemParse:     return 0 for NULL psMetadataItem input." );
+            //Return 0 for non-supported type S64
+            ok ( psMetadataItemParseU16(itemUnsupported) == 0,
+                 "psMetadataItemParse:     return 0 in U16 for S64 (unsupported) "
+                 "psMetadataItem input." );
+            //Return 2 for string with "1.666"
+            ok ( psMetadataItemParseU16(itemString2) == 1,
+                 "psMetadataItemParse:     return 1 in U16 for string-item with "
+                 "value '1.666'." );
+            //Return 0 for F32 value 0.0
+            ok ( psMetadataItemParseU16(itemF32) == 0,
+                 "psMetadataItemParse:     return 0 in U16 for F32-item with value '0.0'." );
+            //Return 1 for F64 value 0.5
+            ok ( psMetadataItemParseU16(itemF64) == 0,
+                 "psMetadataItemParse:     return 0 in U16 for F64-item with value '0.5'." );
+            //Return 1 for S8 value 1
+            ok ( psMetadataItemParseU16(itemS8) == 1,
+                 "psMetadataItemParse:     return 1 in U16 for S8-item with value '1'." );
+            //Return 0 for S16 value 0
+            ok ( psMetadataItemParseU16(itemS16) == 0,
+                 "psMetadataItemParse:     return 0 in S8 for S16-item with value '0'." );
+            //Return 1 for S32 value 1
+            ok ( psMetadataItemParseU16(itemS32) == 1,
+                 "psMetadataItemParse:     return 1 in U16 for S32-item with value '1'." );
+            //Return 2 for U8 value 2
+            ok ( psMetadataItemParseU16(itemU8) == 2,
+                 "psMetadataItemParse:     return 2 in U16 for U8-item with value '2'." );
+            //Return 0 for U16 value 0
+            ok ( psMetadataItemParseU16(itemU16) == 0,
+                 "psMetadataItemParse:     return 0 in U16 for U16-item with value '0'." );
+            //Return 1 for U32 value 1
+            ok ( psMetadataItemParseU16(itemU32) == 1,
+                 "psMetadataItemParse:     return 1 in U16 for U32-item with value '1'." );
+        }
+
+        //U32 Test
+        {
+            //Return 0 for NULL item
+            ok ( psMetadataItemParseU32(NULL) == 0,
+                 "psMetadataItemParse:     return 0 for NULL psMetadataItem input." );
+            //Return 0 for non-supported type S64
+            ok ( psMetadataItemParseU32(itemUnsupported) == 0,
+                 "psMetadataItemParse:     return 0 in U32 for S64 (unsupported) "
+                 "psMetadataItem input." );
+            //Return 2 for string with "1.666"
+            ok ( psMetadataItemParseU32(itemString2) == 1,
+                 "psMetadataItemParse:     return 1 in U32 for string-item with "
+                 "value '1.666'." );
+            //Return 0 for F32 value 0.0
+            ok ( psMetadataItemParseU32(itemF32) == 0,
+                 "psMetadataItemParse:     return 0 in U32 for F32-item with value '0.0'." );
+            //Return 1 for F64 value 0.5
+            ok ( psMetadataItemParseU32(itemF64) == 0,
+                 "psMetadataItemParse:     return 0 in U32 for F64-item with value '0.5'." );
+            //Return 1 for S8 value 1
+            ok ( psMetadataItemParseU32(itemS8) == 1,
+                 "psMetadataItemParse:     return 1 in U32 for S8-item with value '1'." );
+            //Return 0 for S16 value 0
+            ok ( psMetadataItemParseU32(itemS16) == 0,
+                 "psMetadataItemParse:     return 0 in U32 for S16-item with value '0'." );
+            //Return 1 for S32 value 1
+            ok ( psMetadataItemParseU32(itemS32) == 1,
+                 "psMetadataItemParse:     return 1 in U32 for S32-item with value '1'." );
+            //Return 2 for U8 value 2
+            ok ( psMetadataItemParseU32(itemU8) == 2,
+                 "psMetadataItemParse:     return 2 in U32 for U8-item with value '2'." );
+            //Return 0 for U16 value 0
+            ok ( psMetadataItemParseU32(itemU16) == 0,
+                 "psMetadataItemParse:     return 0 in U32 for U16-item with value '0'." );
+            //Return 1 for U32 value 1
+            ok ( psMetadataItemParseU32(itemU32) == 1,
+                 "psMetadataItemParse:    return 1 in U32 for U32-item with value '1'." );
+        }
+
+        //STRING Test
+        {
+            //Return NULL for NULL item
+            ok ( psMetadataItemParseString(NULL) == NULL,
+                 "psMetadataItemParse:    return NULL for NULL STRING psMetadataItem "
+                 "input." );
+            //Return NULL for non-supported type S64
+            ok ( psMetadataItemParseString(itemUnsupported) == NULL,
+                 "psMetadataItemParse:    return NULL in STRING for S64 (unsupported)"
+                 " psMetadataItem input." );
+            //Return "true" for STRING of value "true"
+            psString tempString = psMetadataItemParseString(itemString1);
+            is_strn( tempString, "true", 6,
+                     "psMetadataItemParse:    return 'true' in STRING for STRING with "
+                     "value 'true'." );
+            psFree(tempString);
+    
+            //Return 0.000000 for F32 of value 0.000000
+            tempString = psMetadataItemParseString(itemF32);
+            is_strn( tempString, "0.000000", 4,
+                     "psMetadataItemParse:    return '0.000000' in STRING for F32 with "
+                     "value '0.000000'.   (actual result=%s)", tempString);
+            psFree(tempString);
+    
+            //Return 0.500000 for F64 value 0.5
+            tempString = psMetadataItemParseString(itemF64);
+            is_strn( tempString, "0.500000", 10,
+                     "psMetadataItemParse:    return '0.500000' in STRING for F64 with "
+                     "value '0.5'.   (actual result=%s)", tempString);
+            psFree(tempString);
+    
+            //Return 1 for S8 value 1
+            tempString = psMetadataItemParseString(itemS8);
+            is_strn( tempString, "1", 3,
+                     "psMetadataItemParse:    return '1' in STRING for S8 with "
+                     "value '1'.   (actual result=%s)", tempString);
+            psFree(tempString);
+    
+            //Return 0 for S16 value 0
+            tempString = psMetadataItemParseString(itemS16);
+            is_strn( tempString, "0", 3,
+                     "psMetadataItemParse:    return '0' in STRING for S8 with "
+                     "value '0'.   (actual result=%s)", tempString);
+            psFree(tempString);
+    
+            //Return 1 for S32 value 1
+            tempString = psMetadataItemParseString(itemS32);
+            is_strn( tempString, "1", 3,
+                     "psMetadataItemParse:    return '1' in STRING for S8 with "
+                     "value '1'.   (actual result=%s)", tempString);
+            psFree(tempString);
+    
+            //Return 2 for U8 value 2
+            tempString = psMetadataItemParseString(itemU8);
+            is_strn( tempString, "2", 3,
+                     "psMetadataItemParse:    return '2' in STRING for S8 with "
+                     "value '2'.   (actual result=%s)", tempString);
+            psFree(tempString);
+    
+            //Return 0 for U16 value 0
+            tempString = psMetadataItemParseString(itemU16);
+            is_strn( tempString, "0", 3,
+                     "psMetadataItemParse:    return '0' in STRING for S8 with "
+                     "value '0'.   (actual result=%s)", tempString);
+            psFree(tempString);
+    
+            //Return 1 for U32 value 1
+            tempString = psMetadataItemParseString(itemU32);
+            is_strn( tempString, "1", 3,
+                     "psMetadataItemParse:    return '1' in STRING for S8 with "
+                     "value '1'.   (actual result=%s)", tempString);
+            psFree(tempString);
+        }
+
+        psFree(itemBool);
+        psFree(itemF32);
+        psFree(itemF64);
+        psFree(itemS8);
+        psFree(itemS16);
+        psFree(itemS32);
+        psFree(itemU8);
+        psFree(itemU16);
+        psFree(itemU32);
+        psFree(itemString1);
+        psFree(itemString2);
+        psFree(itemString11);
+        psFree(itemUnsupported);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataIterator.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataIterator.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataIterator.c	(revision 22322)
@@ -0,0 +1,347 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NAME1 "SIMPLE"
+#define COMMENT1 "Basic FITS?"
+#define FLAG1 0
+#define VALUE1 "TRUE"
+
+#define NAME2 "COMMENT"
+#define COMMENT2 "This is a    "
+#define FLAG2 PS_META_DUPLICATE_OK
+#define VALUE2 "   comment."
+
+#define NAME3 "FILENAME"
+#define COMMENT3 "File name"
+#define FLAG3 0
+#define VALUE3 "abcdef123456.fits"
+
+#define NAME4 "COMMENT"
+#define COMMENT4 "   this IS a   "
+#define FLAG4 PS_META_DUPLICATE_OK
+#define VALUE4 "comment    too!"
+
+#define NAME5 "DATE-OBS"
+#define COMMENT5 "Observing date, UTC"
+#define FLAG5 0
+#define VALUE5 "1978-11-29"
+
+#define NAME6 "COMMENT"
+#define COMMENT6 "    another"
+#define FLAG6 PS_META_DUPLICATE_OK
+#define VALUE6 "comment     "
+
+#define NAME7 "COMMENT"
+#define COMMENT7 "This is another comment"
+#define FLAG7 PS_META_DUPLICATE_OK
+#define VALUE7 "Hello world"
+
+#define MULTI_NAME "COMMENT"
+#define MULTI_NAME1 NAME2
+#define MULTI_NAME2 NAME4
+#define MULTI_NAME3 NAME6
+#define MULTI_NAME4 NAME7
+#define MULTI_COMMENT1 COMMENT2
+#define MULTI_COMMENT2 COMMENT4
+#define MULTI_COMMENT3 COMMENT6
+#define MULTI_COMMENT4 COMMENT7
+#define MULTI_VALUE1 VALUE2
+#define MULTI_VALUE2 VALUE4
+#define MULTI_VALUE3 VALUE6
+#define MULTI_VALUE4 VALUE7
+
+// Generate a dummy metadata
+static psMetadata *mdGenerate(void)
+{
+    psMetadata *md = psMetadataAlloc();
+
+    // Something like a FITS header
+    psMetadataAddStr(md, PS_LIST_TAIL, NAME1, FLAG1, COMMENT1, VALUE1);
+    psMetadataAddStr(md, PS_LIST_TAIL, NAME2, FLAG2, COMMENT2, VALUE2);
+    psMetadataAddStr(md, PS_LIST_TAIL, NAME3, FLAG3, COMMENT3, VALUE3);
+    psMetadataAddStr(md, PS_LIST_TAIL, NAME4, FLAG4, COMMENT4, VALUE4);
+    psMetadataAddStr(md, PS_LIST_TAIL, NAME5, FLAG5, COMMENT5, VALUE5);
+    psMetadataAddStr(md, PS_LIST_TAIL, NAME6, FLAG6, COMMENT6, VALUE6);
+    psMetadataAddStr(md, PS_LIST_TAIL, NAME7, FLAG7, COMMENT7, VALUE7);
+
+    return md;
+}
+
+// Function to perform iteration
+typedef psMetadataItem *(*iterFunc)(psMetadataIterator *iter);
+
+
+// Test the iteration by iterating once and checking the name, comment, and data
+static void testIteration(iterFunc function, // Function to use to iterate
+                          psMetadataIterator *iterator, // The iterator
+                          const char *name, // Name of the item
+                          const char *comment, // Comment for the item
+                          const char *value // Value for the item
+                         )
+{
+    psMetadataItem *item = function(iterator);
+    ok(item, "item = %x", item);
+    skip_start(!item, 4, "Skipping 4 tests because iteration failed");
+    ok(strcmp(item->name, name) == 0, "item->name = %s", item->name);
+    ok(strcmp(item->comment, comment) == 0, "item->comment = %s", item->comment);
+    ok(item->type == PS_DATA_STRING, "item->type = %x", item->type);
+    skip_start(item->type != PS_DATA_STRING, 1, "Skipping 1 test because the item has incorrect type");
+    ok(strcmp(item->data.V, value) == 0, "item->data.V = %s", item->data.V);
+    skip_end();
+    skip_end();
+
+    return;
+}
+
+
+int main (void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(254);
+
+    // psMetadataIterator tests
+    // No regular expressions, forwards
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 35, "Skipping 35 tests because psMetadataIteratorAlloc failed");
+
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME1, COMMENT1, VALUE1);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME2, COMMENT2, VALUE2);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME3, COMMENT3, VALUE3);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME4, COMMENT4, VALUE4);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME5, COMMENT5, VALUE5);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME6, COMMENT6, VALUE6);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME7, COMMENT7, VALUE7);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // No regular expressions, backwards
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_TAIL, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 35, "Skipping 35 tests because psMetadataIteratorAlloc failed");
+
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME7, COMMENT7, VALUE7);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME6, COMMENT6, VALUE6);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME5, COMMENT5, VALUE5);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME4, COMMENT4, VALUE4);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME3, COMMENT3, VALUE3);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME2, COMMENT2, VALUE2);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME1, COMMENT1, VALUE1);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // No regular expressions, jumping in halfway through (using allocator), forwards
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, 3, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 20, "Skipping 20 tests because psMetadataIteratorAlloc failed");
+
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME4, COMMENT4, VALUE4);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME5, COMMENT5, VALUE5);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME6, COMMENT6, VALUE6);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME7, COMMENT7, VALUE7);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //  No regular expressions, jumping in halfway through (using allocator), backwards
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, -4, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 15, "Skipping 20 tests because psMetadataIteratorAlloc failed");
+
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME4, COMMENT4, VALUE4);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME3, COMMENT3, VALUE3);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME2, COMMENT2, VALUE2);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME1, COMMENT1, VALUE1);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // No regular expressions, jumping in halfway through (using set), forwards");
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 20, "Skipping 20 tests because psMetadataIteratorAlloc failed");
+        psMetadataIteratorSet(iter, 3);
+
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME4, COMMENT4, VALUE4);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME5, COMMENT5, VALUE5);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME6, COMMENT6, VALUE6);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, NAME7, COMMENT7, VALUE7);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // No regular expressions, jumping in halfway through (using set), backwards");
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 15, "Skipping 20 tests because psMetadataIteratorAlloc failed");
+        psMetadataIteratorSet(iter, -4);
+
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME4, COMMENT4, VALUE4);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME3, COMMENT3, VALUE3);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME2, COMMENT2, VALUE2);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, NAME1, COMMENT1, VALUE1);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // With regular expressions, forwards");
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, "^" MULTI_NAME "$");
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 20, "Skipping 20 tests because psMetadataIteratorAlloc failed");
+
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, MULTI_NAME1, MULTI_COMMENT1, MULTI_VALUE1);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, MULTI_NAME2, MULTI_COMMENT2, MULTI_VALUE2);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, MULTI_NAME3, MULTI_COMMENT3, MULTI_VALUE3);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, MULTI_NAME4, MULTI_COMMENT4, MULTI_VALUE4);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // With regular expressions, backwards");
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_TAIL, "^" MULTI_NAME "$");
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 20, "Skipping 20 tests because psMetadataIteratorAlloc failed");
+
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, MULTI_NAME4, MULTI_COMMENT4, MULTI_VALUE4);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, MULTI_NAME3, MULTI_COMMENT3, MULTI_VALUE3);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, MULTI_NAME2, MULTI_COMMENT2, MULTI_VALUE2);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, MULTI_NAME1, MULTI_COMMENT1, MULTI_VALUE1);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // With regular expressions, jumping in halfway through (using allocator), forwards");
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, 2, "^" MULTI_NAME "$");
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 20, "Skipping 20 tests because psMetadataIteratorAlloc failed");
+
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, MULTI_NAME3, MULTI_COMMENT3, MULTI_VALUE3);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, MULTI_NAME4, MULTI_COMMENT4, MULTI_VALUE4);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // With regular expressions, jumping in halfway through (using allocator), backwards");
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, -3, "^" MULTI_NAME "$");
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 20, "Skipping 20 tests because psMetadataIteratorAlloc failed");
+
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, MULTI_NAME2, MULTI_COMMENT2, MULTI_VALUE2);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, MULTI_NAME1, MULTI_COMMENT1, MULTI_VALUE1);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // With regular expressions, jumping in halfway through (using set), forwards");
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, "^" MULTI_NAME "$");
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 20, "Skipping 20 tests because psMetadataIteratorAlloc failed");
+        psMetadataIteratorSet(iter, 2);
+
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, MULTI_NAME3, MULTI_COMMENT3, MULTI_VALUE3);
+        testIteration((iterFunc)psMetadataGetAndIncrement, iter, MULTI_NAME4, MULTI_COMMENT4, MULTI_VALUE4);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // With regular expressions, jumping in halfway through (using set), backwards");
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = mdGenerate();
+        psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, "^" MULTI_NAME "$");
+        ok(iter, "iter = %x", iter);
+        skip_start(!iter, 20, "Skipping 20 tests because psMetadataIteratorAlloc failed");
+        psMetadataIteratorSet(iter, -3);
+
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, MULTI_NAME2, MULTI_COMMENT2, MULTI_VALUE2);
+        testIteration((iterFunc)psMetadataGetAndDecrement, iter, MULTI_NAME1, MULTI_COMMENT1, MULTI_VALUE1);
+
+        skip_end();
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataOverlay.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataOverlay.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataOverlay.c	(revision 22322)
@@ -0,0 +1,290 @@
+/**
+*  C Implementation: tap_psMetadataOverlay
+*
+* Description: Tests for psMetadataOverlay
+*
+* Author: Eugene Magnier
+* Copyright: See COPYING file that comes with this distribution
+*
+*/
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define DEBUG 0
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(14);
+    // psMetadataOverlay Tests
+
+    // overlay two metadatas : output should contain union of all entries
+    {
+        psMemId id = psMemGetId();
+	psMetadata *in = psMetadataAlloc();
+	psMetadata *out = psMetadataAlloc();
+
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN1", 0, "", 1);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN2", 0, "", 2);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN3", 0, "", 3);
+
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT1", 0, "", 1);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT2", 0, "", 2);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT3", 0, "", 3);
+
+	bool status = psMetadataOverlay (out, in);
+	if (DEBUG) psMetadataPrint (stderr, out, 1);
+
+	ok (status, "psMetadataOverlay : overlay two MDs");
+	psFree (in);
+	psFree (out);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // overlay two metadatas, folder in output: output should contain union of all entries
+    {
+	psMemId id = psMemGetId();
+	psMetadata *in = psMetadataAlloc();
+	psMetadata *out = psMetadataAlloc();
+
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN1", 0, "", 1);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN2", 0, "", 2);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN3", 0, "", 3);
+
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT1", 0, "", 1);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT2", 0, "", 2);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT3", 0, "", 3);
+
+	psMetadata *sub = psMetadataAlloc();
+	psMetadataAddS32 (sub, PS_LIST_TAIL, "TEST.SUB1", 0, "", 1);
+	psMetadataAddS32 (sub, PS_LIST_TAIL, "TEST.SUB2", 0, "", 2);
+	psMetadataAddS32 (sub, PS_LIST_TAIL, "TEST.SUB3", 0, "", 3);
+
+	psMetadataAddMetadata (out, PS_LIST_TAIL, "SUB", 0, "", sub);
+
+	bool status = psMetadataOverlay (out, in);
+	if (DEBUG) psMetadataPrint (stderr, out, 1);
+
+	ok (status, "psMetadataOverlay : overlay two MDs");
+	psFree (in);
+	psFree (out);
+	psFree (sub);
+	ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // overlay two metadatas, folder in input: output should contain union of all entries
+    {
+	psMemId id = psMemGetId();
+	psMetadata *in = psMetadataAlloc();
+	psMetadata *out = psMetadataAlloc();
+
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN1", 0, "", 1);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN2", 0, "", 2);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN3", 0, "", 3);
+
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT1", 0, "", 1);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT2", 0, "", 2);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT3", 0, "", 3);
+
+	psMetadata *sub = psMetadataAlloc();
+	psMetadataAddS32 (sub, PS_LIST_TAIL, "TEST.SUB1", 0, "", 1);
+	psMetadataAddS32 (sub, PS_LIST_TAIL, "TEST.SUB2", 0, "", 2);
+	psMetadataAddS32 (sub, PS_LIST_TAIL, "TEST.SUB3", 0, "", 3);
+
+	psMetadataAddMetadata (in, PS_LIST_TAIL, "SUB", 0, "", sub);
+
+	bool status = psMetadataOverlay (out, in);
+	if (DEBUG) psMetadataPrint (stderr, out, 1);
+
+	ok (status, "psMetadataOverlay : overlay two MDs");
+	psFree (in);
+	psFree (out);
+	psFree (sub);
+	ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // overlay two metadatas, folder in input & output, no-matched elements
+    {
+	psMemId id = psMemGetId();
+	psMetadata *in = psMetadataAlloc();
+	psMetadata *out = psMetadataAlloc();
+
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN1", 0, "", 1);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN2", 0, "", 2);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN3", 0, "", 3);
+
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT1", 0, "", 1);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT2", 0, "", 2);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT3", 0, "", 3);
+
+	psMetadata *inSub = psMetadataAlloc();
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.INSUB1", 0, "", 1);
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.INSUB2", 0, "", 2);
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.INSUB3", 0, "", 3);
+	psMetadataAddMetadata (in, PS_LIST_TAIL, "SUB", 0, "", inSub);
+
+	psMetadata *outSub = psMetadataAlloc();
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.OUTSUB1", 0, "", 1);
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.OUTSUB2", 0, "", 2);
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.OUTSUB3", 0, "", 3);
+	psMetadataAddMetadata (out, PS_LIST_TAIL, "SUB", 0, "", outSub);
+
+	bool status = psMetadataOverlay (out, in);
+	if (DEBUG) psMetadataPrint (stderr, out, 1);
+
+	ok (status, "psMetadataOverlay : overlay two MDs");
+	psFree (in);
+	psFree (out);
+	psFree (inSub);
+	psFree (outSub);
+	ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // overlay two metadatas, folder and subfolders in input & output, no-matched elements
+    {
+	psMemId id = psMemGetId();
+	psMetadata *in = psMetadataAlloc();
+	psMetadata *out = psMetadataAlloc();
+
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN1", 0, "", 1);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN2", 0, "", 2);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN3", 0, "", 3);
+
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT1", 0, "", 1);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT2", 0, "", 2);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT3", 0, "", 3);
+
+	psMetadata *inSub = psMetadataAlloc();
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.INSUB1", 0, "", 1);
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.INSUB2", 0, "", 2);
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.INSUB3", 0, "", 3);
+
+	psMetadata *inSubSub = psMetadataAlloc();
+	psMetadataAddS32 (inSubSub, PS_LIST_TAIL, "TEST.INSUB1", 0, "", 1);
+	psMetadataAddS32 (inSubSub, PS_LIST_TAIL, "TEST.INSUB2", 0, "", 2);
+	psMetadataAddS32 (inSubSub, PS_LIST_TAIL, "TEST.INSUB3", 0, "", 3);
+	psMetadataAddMetadata (inSub, PS_LIST_TAIL, "SUB", 0, "", inSubSub);
+
+	psMetadataAddMetadata (in, PS_LIST_TAIL, "SUB", 0, "", inSub);
+
+	psMetadata *outSub = psMetadataAlloc();
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.OUTSUB1", 0, "", 1);
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.OUTSUB2", 0, "", 2);
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.OUTSUB3", 0, "", 3);
+
+	psMetadata *outSubSub = psMetadataAlloc();
+	psMetadataAddS32 (outSubSub, PS_LIST_TAIL, "TEST.OUTSUB1", 0, "", 1);
+	psMetadataAddS32 (outSubSub, PS_LIST_TAIL, "TEST.OUTSUB2", 0, "", 2);
+	psMetadataAddS32 (outSubSub, PS_LIST_TAIL, "TEST.OUTSUB3", 0, "", 3);
+	psMetadataAddMetadata (outSub, PS_LIST_TAIL, "SUB", 0, "", outSubSub);
+
+	psMetadataAddMetadata (out, PS_LIST_TAIL, "SUB", 0, "", outSub);
+
+	bool status = psMetadataOverlay (out, in);
+	if (DEBUG) psMetadataPrint (stderr, out, 1);
+
+	ok (status, "psMetadataOverlay : overlay two MDs");
+	psFree (in);
+	psFree (out);
+	psFree (inSub);
+	psFree (outSub);
+	psFree (inSubSub);
+	psFree (outSubSub);
+	ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // overlay two metadatas, folder in input & output, matched elements
+    {
+	psMemId id = psMemGetId();
+	psMetadata *in = psMetadataAlloc();
+	psMetadata *out = psMetadataAlloc();
+
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN1", 0, "", 1);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN2", 0, "", 2);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN3", 0, "", 3);
+
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT1", 0, "", 1);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT2", 0, "", 2);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT3", 0, "", 3);
+
+	psMetadata *inSub = psMetadataAlloc();
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.SUB1", 0, "", 1);
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.SUB2", 0, "", 2);
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.SUB3", 0, "", 3);
+	psMetadataAddMetadata (in, PS_LIST_TAIL, "SUB", 0, "", inSub);
+
+	psMetadata *outSub = psMetadataAlloc();
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.SUB1", 0, "", 4);
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.SUB2", 0, "", 5);
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.SUB3", 0, "", 6);
+	psMetadataAddMetadata (out, PS_LIST_TAIL, "SUB", 0, "", outSub);
+
+	bool status = psMetadataOverlay (out, in);
+	if (DEBUG) psMetadataPrint (stderr, out, 1);
+
+	ok (status, "psMetadataOverlay : overlay two MDs");
+	psFree (in);
+	psFree (out);
+	psFree (inSub);
+	psFree (outSub);
+	ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // overlay two metadatas, folder and subfolders in input & output, no-matched elements
+    {
+	psMemId id = psMemGetId();
+	psMetadata *in = psMetadataAlloc();
+	psMetadata *out = psMetadataAlloc();
+
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN1", 0, "", 1);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN2", 0, "", 2);
+	psMetadataAddS32 (in, PS_LIST_TAIL, "TEST.IN3", 0, "", 3);
+
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT1", 0, "", 1);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT2", 0, "", 2);
+	psMetadataAddS32 (out, PS_LIST_TAIL, "TEST.OUT3", 0, "", 3);
+
+	psMetadata *inSub = psMetadataAlloc();
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.SUB1", 0, "", 1);
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.SUB2", 0, "", 2);
+	psMetadataAddS32 (inSub, PS_LIST_TAIL, "TEST.SUB3", 0, "", 3);
+
+	psMetadata *inSubSub = psMetadataAlloc();
+	psMetadataAddS32 (inSubSub, PS_LIST_TAIL, "TEST.SUB1", 0, "", 1);
+	psMetadataAddS32 (inSubSub, PS_LIST_TAIL, "TEST.SUB2", 0, "", 2);
+	psMetadataAddS32 (inSubSub, PS_LIST_TAIL, "TEST.SUB3", 0, "", 3);
+	psMetadataAddMetadata (inSub, PS_LIST_TAIL, "SUB", 0, "", inSubSub);
+
+	psMetadataAddMetadata (in, PS_LIST_TAIL, "SUB", 0, "", inSub);
+
+	psMetadata *outSub = psMetadataAlloc();
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.SUB1", 0, "", 4);
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.SUB2", 0, "", 5);
+	psMetadataAddS32 (outSub, PS_LIST_TAIL, "TEST.SUB3", 0, "", 6);
+
+	psMetadata *outSubSub = psMetadataAlloc();
+	psMetadataAddS32 (outSubSub, PS_LIST_TAIL, "TEST.SUB1", 0, "", 4);
+	psMetadataAddS32 (outSubSub, PS_LIST_TAIL, "TEST.SUB2", 0, "", 5);
+	psMetadataAddS32 (outSubSub, PS_LIST_TAIL, "TEST.SUB3", 0, "", 6);
+	psMetadataAddMetadata (outSub, PS_LIST_TAIL, "SUB", 0, "", outSubSub);
+
+	psMetadataAddMetadata (out, PS_LIST_TAIL, "SUB", 0, "", outSub);
+
+	bool status = psMetadataOverlay (out, in);
+	if (DEBUG) psMetadataPrint (stderr, out, 1);
+
+	ok (status, "psMetadataOverlay : overlay two MDs");
+	psFree (in);
+	psFree (out);
+	psFree (inSub);
+	psFree (outSub);
+	psFree (inSubSub);
+	psFree (outSubSub);
+	ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataUpdate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataUpdate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadataUpdate.c	(revision 22322)
@@ -0,0 +1,137 @@
+/**
+*  C Implementation: tap_psMetadataUpdate
+*
+* Description: Tests for psMetadataUpdate, psMetadataItemAdd for PS_META_REQUIRE_ENTRY and
+* PS_META_REQUIRE_TYPE
+*
+* Author: Eugene Magnier
+* Copyright: See COPYING file that comes with this distribution
+*
+*/
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(16);
+    // psMetadataUpdate Tests
+
+
+    {
+        psMemId id = psMemGetId();
+	psMetadata *md = psMetadataAlloc();
+	psMetadataAddS32 (md, PS_LIST_TAIL, "TEST1", 0, "", 1);
+	bool status = psMetadataAddF32 (md, PS_LIST_TAIL, "TEST1", PS_META_REPLACE | PS_META_REQUIRE_ENTRY, "", 1);
+	ok (status, "PS_META_REQUIRE_ENTRY : psMetadataAdd should not fail if item already exists");
+	psFree (md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+	psMetadata *md = psMetadataAlloc();
+	psMetadataAddS32 (md, PS_LIST_TAIL, "TEST1", 0, "", 1);
+	bool status = psMetadataAddF32 (md, PS_LIST_TAIL, "TEST2", PS_META_REPLACE | PS_META_REQUIRE_ENTRY, "", 1);
+	ok (!status, "PS_META_REQUIRE_ENTRY : psMetadataAdd should fail if item does not already exist");
+	psFree (md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+	psMetadata *md = psMetadataAlloc();
+	psMetadataAddS32 (md, PS_LIST_TAIL, "TEST1", 0, "", 1);
+	bool status = psMetadataAddF32 (md, PS_LIST_TAIL, "TEST2", PS_META_REPLACE | PS_META_REQUIRE_TYPE, "", 1);
+	ok (status, "PS_META_REQUIRE_TYPE : psMetadataAdd should not fail if item does not already exist");
+	psFree (md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+	psMetadata *md = psMetadataAlloc();
+	psMetadataAddS32 (md, PS_LIST_TAIL, "TEST1", 0, "", 1);
+	bool status = psMetadataAddS32 (md, PS_LIST_TAIL, "TEST1", PS_META_REPLACE | PS_META_REQUIRE_TYPE, "", 1);
+	ok (status, "PS_META_REQUIRE_TYPE : psMetadataAdd should not fail if existing item has the same type");
+	psFree (md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+	psMetadata *md = psMetadataAlloc();
+	psMetadataAddS32 (md, PS_LIST_TAIL, "TEST1", 0, "", 1);
+	bool status = psMetadataAddF32 (md, PS_LIST_TAIL, "TEST1", PS_META_REPLACE | PS_META_REQUIRE_TYPE, "", 1);
+	ok (!status, "PS_META_REQUIRE_TYPE : psMetadataAdd should fail if existing item does not match type");
+	psFree (md);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+	psMetadata *input = psMetadataAlloc();
+	psMetadata *output = psMetadataAlloc();
+
+	psMetadataAddS32 (input, PS_LIST_TAIL, "TEST1", 0, "", 2);
+	psMetadataAddS32 (input, PS_LIST_TAIL, "TEST2", 0, "", 2);
+	psMetadataAddS32 (input, PS_LIST_TAIL, "TEST3", 0, "", 2);
+	psMetadataAddS32 (output, PS_LIST_TAIL, "TEST1", 0, "", 1);
+	psMetadataAddS32 (output, PS_LIST_TAIL, "TEST2", 0, "", 1);
+	psMetadataAddS32 (output, PS_LIST_TAIL, "TEST3", 0, "", 1);
+
+	bool status = psMetadataUpdate (output, input);
+	ok (status, "psMetadataUpdate should not fail if items all exist and match type");
+	psFree (input);
+	psFree (output);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+	psMetadata *input = psMetadataAlloc();
+	psMetadata *output = psMetadataAlloc();
+
+	psMetadataAddS32 (input, PS_LIST_TAIL, "TEST1", 0, "", 2);
+	psMetadataAddS32 (input, PS_LIST_TAIL, "TEST2", 0, "", 2);
+	psMetadataAddS32 (input, PS_LIST_TAIL, "TEST3", 0, "", 2);
+	psMetadataAddS32 (output, PS_LIST_TAIL, "TEST1", 0, "", 1);
+	psMetadataAddS32 (output, PS_LIST_TAIL, "TEST3", 0, "", 1);
+
+	bool status = psMetadataUpdate (output, input);
+	ok (!status, "psMetadataUpdate should fail if any input items do not exist in output");
+	psFree (input);
+	psFree (output);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    {
+        psMemId id = psMemGetId();
+	psMetadata *input = psMetadataAlloc();
+	psMetadata *output = psMetadataAlloc();
+
+	psMetadataAddS32 (input, PS_LIST_TAIL, "TEST1", 0, "", 2);
+	psMetadataAddS32 (input, PS_LIST_TAIL, "TEST2", 0, "", 2);
+	psMetadataAddS32 (input, PS_LIST_TAIL, "TEST3", 0, "", 2);
+
+	psMetadataAddS32 (output, PS_LIST_TAIL, "TEST1", 0, "", 1);
+	psMetadataAddF32 (output, PS_LIST_TAIL, "TEST2", 0, "", 1);
+	psMetadataAddS32 (output, PS_LIST_TAIL, "TEST3", 0, "", 1);
+
+	bool status = psMetadataUpdate (output, input);
+	ok (!status, "psMetadataUpdate should fail if any input items do not match type in output");
+	psFree (input);
+	psFree (output);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_copying.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_copying.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_copying.c	(revision 22322)
@@ -0,0 +1,480 @@
+/**
+ *  C Implementation: tap_psMetadata_copying
+ *
+ * Description:  Tests for psMetadataCopy, psMetadataItemCopy, & psMetadataItemSupplement.
+ *
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(41);
+
+
+    // testMetadataCopy()
+    {
+        psMemId id = psMemGetId();
+        psMetadata *in = NULL;
+        psMetadata *out = NULL;
+        psMetadataItem *tempItem = NULL;
+        psMetadataItem *outItem = NULL;
+        psMetadataItem *inItem = NULL;
+        psMetadata *outTest = NULL;
+    
+        //Return NULL for NULL psMetadata input
+        {
+            outTest = psMetadataCopy(out, NULL);
+            ok( outTest == NULL,
+                "psMetadataCopy:  return NULL for NULL input metadata.");
+        }
+        //Return allocated out for NULL psMetadata input (with allocated out)
+        {
+            out = psMetadataAlloc();
+            outTest = psMetadataCopy(out, NULL);
+            ok( outTest == NULL,
+                "psMetadataCopy:  return out for NULL input metadata.");
+            psFree(out);
+            out = NULL;
+        }
+        //Return NULL for attempt to copy item of unsupported data type - NULL out metadata
+        psMetadataItem *notype = NULL;
+        {
+            in = psMetadataAlloc();
+            psMetadataAddS32(in, PS_LIST_HEAD, "S32", 0, "", 1);
+            notype = psMetadataGet(in, PS_LIST_HEAD);
+            notype->type = -1;
+            outTest = psMetadataCopy(out, in);
+            ok( outTest == NULL,
+                "psMetadataCopy:  return NULL for metadata with item of bad type.");
+            psFree(out);
+            out = NULL;
+        }
+        //Return NULL for attempt to copy item of unsupported data type - allocated out metadata
+        {
+            out = psMetadataAlloc();
+            psMetadataAddS32(out, PS_LIST_TAIL, "S32", 0, "", 2);
+            outTest = psMetadataCopy(out, in);
+            ok( outTest == NULL,
+                "psMetadataCopy:  return NULL for metadata with item of bad type.");
+            notype->type = PS_DATA_S32;
+            psFree(out);
+            out = NULL;
+            psFree(in);
+            in = NULL;
+        }
+    
+        //Return NULL for NULL psMetadata input->list
+        in = psMetadataAlloc();
+        if (in->list != NULL) {
+            psFree(in->list);
+            in->list = NULL;
+        }
+        {
+            outTest = psMetadataCopy(out, in);
+            ok( outTest == NULL,
+                "psMetadataCopy:  return NULL for NULL metadata input->list.");
+        }
+        /*    //Return NULL for NULL psMetadata input->list (with allocated out)
+            {
+                out = psMetadataAlloc();
+                out = psMetadataCopy(out, in);
+                ok( out == NULL,
+                    "psMetadataCopy:  return NULL for NULL metadata input->list.");
+            }
+        */
+        //Return valid (but empty) metadata for NULL out input and empty input metadata.
+        in->list = psListAlloc(NULL);
+        psFree(in);
+        in = psMetadataAlloc();
+        if (out != NULL) {
+            psFree(out);
+            out = NULL;
+        }
+        outTest = psMetadataCopy(out, in);
+        tempItem = psMetadataGet(in, PS_LIST_HEAD);
+        {
+            ok( outTest != NULL && tempItem == NULL,
+                "psMetadataCopy:  return empty copy with no items for empty input metadata.");
+            psFree(out);
+            psFree(outTest);
+            out = NULL;
+            outTest = NULL;
+        }
+    
+        //Test case: multiCheckItem->type == PS_DATA_METADATA_MULTI
+        psMetadataAddS32(in, PS_LIST_HEAD, "S32", 0, "", 1);
+        psMetadataAddS32(in, PS_LIST_TAIL, "S32", PS_META_DUPLICATE_OK, "", 1);
+        outTest = psMetadataCopy(out, in);
+        tempItem = psMetadataGet(outTest, PS_LIST_HEAD);
+        {
+            ok( tempItem->type == PS_DATA_S32 && tempItem->data.S32 == 1,
+                "psMetadataCopy:  return valid copy for a PS_DATA_METADATA_MULTI entry.");
+            psFree(outTest);
+            outTest = NULL;
+        }
+    
+        //Test case:  !psMetadataAddItem(out, newItem, PS_LIST_TAIL, flag)
+        //XXX: doesn't seem to be easily reachable, certainly by the user...  Skipping.
+    
+        //Test case: simple valid test
+        psFree(in);
+        psFree(out);
+        out = NULL;
+        in = psMetadataAlloc();
+        psMetadataAddS32(in, PS_LIST_HEAD, "S32", 0, "", 666);
+        outTest = psMetadataCopy(out, in);
+        outItem = psMetadataGet(outTest, PS_LIST_HEAD);
+        inItem = psMetadataGet(in, PS_LIST_HEAD);
+        {
+            ok( outItem->type == PS_DATA_S32 && outItem->data.S32 == inItem->data.S32 &&
+                (strncmp(inItem->name, outItem->name, 10) == 0),
+                "psMetadataCopy:  return valid copy of metadata with 1 S32 entry.");
+        }
+        psFree(outTest);
+        psFree(in);
+        psFree(out);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testMetadataItemCopy()
+    {
+        psMemId id = psMemGetId();
+        //Return NULL for NULL input psMetadataItem
+        {
+            ok( psMetadataItemCopy(NULL) == NULL,
+                "psMetadataItemCopy:   return NULL for NULL input metadataItem.");
+        }
+    
+        //Test Simple types:  Bool, S8, S16, S32, S64, U8, U16, U32, U64, F32, F64, STRING
+        psMetadataItem *itemBool = psMetadataItemAlloc("itemBool", PS_DATA_BOOL, "", false);
+        psMetadataItem *itemS8  =  psMetadataItemAlloc("itemS8", PS_DATA_S8, "", 1);
+        psMetadataItem *itemS16 =  psMetadataItemAlloc("itemS16", PS_DATA_S16, "", 2);
+        psMetadataItem *itemS32 =  psMetadataItemAlloc("itemS32", PS_DATA_S32, "", 3);
+        psMetadataItem *itemS64 =  psMetadataItemAlloc("itemS64", PS_DATA_S64, "", (psS64)4);
+        psMetadataItem *itemU8  =  psMetadataItemAlloc("itemU8", PS_DATA_U8, "", 1);
+        psMetadataItem *itemU16 =  psMetadataItemAlloc("itemU16", PS_DATA_U16, "", 2);
+        psMetadataItem *itemU32 =  psMetadataItemAlloc("itemU32", PS_DATA_U32, "", 3);
+        psMetadataItem *itemU64 =  psMetadataItemAlloc("itemU64", PS_DATA_U64, "", 4);
+        psMetadataItem *itemF32 =  psMetadataItemAlloc("itemF32", PS_DATA_F32, "", 3.0);
+        psMetadataItem *itemF64 =  psMetadataItemAlloc("itemF64", PS_DATA_F64, "", 4.0);
+        psMetadataItem *itemString = psMetadataItemAlloc("itemString", PS_DATA_STRING, "", "I M STRiNG");
+        psMetadataItem *copy = NULL;
+        //Bool case
+        {
+            copy = psMetadataItemCopy(itemBool);
+            ok( (copy->type == PS_DATA_BOOL && (strncmp(copy->name, "itemBool", 10) == 0) &&
+                 !copy->data.B && strncmp(copy->comment, "", 2) == 0),
+                "psMetadataItemCopy:  return valid copy of BOOL item.");
+            psFree(copy);
+        }
+        //S8 case
+        {
+            copy = psMetadataItemCopy(itemS8);
+            ok( (copy->type == PS_DATA_S8 && (strncmp(copy->name, "itemS8", 10) == 0) &&
+                 copy->data.S8 == 1 && strncmp(copy->comment, "", 2) == 0),
+                "psMetadataItemCopy:  return valid copy of S8 item.");
+            psFree(copy);
+        }
+        //S16 case
+        {
+            copy = psMetadataItemCopy(itemS16);
+            ok( (copy->type == PS_DATA_S16 && (strncmp(copy->name, "itemS16", 10) == 0) &&
+                 copy->data.S16 == 2 && strncmp(copy->comment, "", 2) == 0),
+                "psMetadataItemCopy:  return valid copy of S16 item.");
+            psFree(copy);
+        }
+        //S32 case
+        {
+            copy = psMetadataItemCopy(itemS32);
+            ok( (copy->type == PS_DATA_S32 && (strncmp(copy->name, "itemS32", 10) == 0) &&
+                 copy->data.S32 == 3 && strncmp(copy->comment, "", 2) == 0),
+                "psMetadataItemCopy:  return valid copy of S32 item.");
+            psFree(copy);
+        }
+    
+        // S64 case
+        {
+            psMetadataItem *copy = psMetadataItemCopy(itemS64);
+    
+            ok(copy->type == PS_DATA_S64, "item type");
+            is_str(copy->name, "itemS64", "item name");
+            is_long(copy->data.S64, 4, "item data");
+            is_str(copy->comment, "", "item comment");
+    
+            psFree(copy);
+        }
+    
+        //U8 case
+        {
+            copy = psMetadataItemCopy(itemU8);
+            ok( (copy->type == PS_DATA_U8 && (strncmp(copy->name, "itemU8", 10) == 0) &&
+                 copy->data.U8 == 1 && strncmp(copy->comment, "", 2) == 0),
+                "psMetadataItemCopy:  return valid copy of U8 item.");
+            psFree(copy);
+        }
+        //U16 case
+        {
+            copy = psMetadataItemCopy(itemU16);
+            ok( (copy->type == PS_DATA_U16 && (strncmp(copy->name, "itemU16", 10) == 0) &&
+                 copy->data.U16 == 2 && strncmp(copy->comment, "", 2) == 0),
+                "psMetadataItemCopy:  return valid copy of U16 item.");
+            psFree(copy);
+        }
+        //U32 case
+        {
+            copy = psMetadataItemCopy(itemU32);
+            ok( (copy->type == PS_DATA_U32 && (strncmp(copy->name, "itemU32", 10) == 0) &&
+                 copy->data.U32 == 3 && strncmp(copy->comment, "", 2) == 0),
+                "psMetadataItemCopy:  return valid copy of U32 item.");
+            psFree(copy);
+        }
+        //U64 case
+        {
+            copy = psMetadataItemCopy(itemU64);
+            ok( (copy->type == PS_DATA_U64 && (strncmp(copy->name, "itemU64", 10) == 0) &&
+                 copy->data.U64 == 4 && strncmp(copy->comment, "", 2) == 0),
+                "psMetadataItemCopy:  return valid copy of U64 item.");
+            psFree(copy);
+        }
+        //F32 case
+        {
+            copy = psMetadataItemCopy(itemF32);
+            ok( (copy->type == PS_DATA_F32 && (strncmp(copy->name, "itemF32", 10) == 0) &&
+                 abs(copy->data.F32-3.0) < FLT_EPSILON && strncmp(copy->comment, "", 2) == 0),
+                "psMetadataItemCopy:  return valid copy of F32 item.");
+            psFree(copy);
+        }
+        //F64 case
+        {
+            copy = psMetadataItemCopy(itemF64);
+            ok( (copy->type == PS_DATA_F64 && (strncmp(copy->name, "itemF64", 10) == 0) &&
+                 abs(copy->data.F64-4.0) < DBL_EPSILON && strncmp(copy->comment, "", 2) == 0),
+                "psMetadataItemCopy:  return valid copy of F64 item.");
+            psFree(copy);
+        }
+        //STRING case
+        {
+            copy = psMetadataItemCopy(itemString);
+            ok( (copy->type == PS_DATA_STRING && (strncmp(copy->name, "itemString", 12) == 0) &&
+                 strncmp(copy->data.str,"I M STRiNG",12) == 0 && strncmp(copy->comment,"",2) == 0),
+                "psMetadataItemCopy:  return valid copy of STRING item.");
+            psFree(copy);
+        }
+    
+        //Test Remaining types:  Vector, Time, Metadata, Region
+        psVector *inV = psVectorAlloc(2, PS_TYPE_S32);
+        psVectorSet(inV, 0, 666);
+        psVectorSet(inV, 1, 667);
+        psMetadataItem *itemVector = psMetadataItemAlloc("itemVector", PS_DATA_VECTOR, "", inV);
+        psTime *teaTime = psTimeAlloc(PS_TIME_TAI);
+        teaTime->sec = 1;
+        teaTime->nsec = 0;
+        teaTime->leapsecond = true;
+        psMetadataItem *itemTime = psMetadataItemAlloc("itemTime", PS_DATA_TIME, "", teaTime);
+        psMetadata *md = psMetadataAlloc();
+        psMetadataAddS32(md, PS_LIST_HEAD, "mdS32", 0, "", 666);
+        psMetadataItem *itemMD = psMetadataItemAlloc("itemMD", PS_DATA_METADATA, "", md);
+        psRegion *reg = psRegionAlloc(0.0, 2.0, 1.0, 3.0);
+        psMetadataItem *itemRegion = psMetadataItemAlloc("itemRegion", PS_DATA_REGION, "", reg);
+        //Vector case
+        {
+            copy = psMetadataItemCopy(itemVector);
+            int int0 = ((psVector*)(copy->data.V))->data.S32[0];
+            int int1 = ((psVector*)(copy->data.V))->data.S32[1];
+            ok( (copy->type == PS_DATA_VECTOR && (strncmp(copy->name, "itemVector", 12) == 0)
+                 && int0 == 666 && int1 == 667 && strncmp(copy->comment,"",2) == 0),
+                "psMetadataItemCopy:  return valid copy of Vector item.");
+            psFree(copy);
+        }
+        //Vector case - invalid vector item
+        {
+            psFree(itemVector->data.V);
+            itemVector->data.V = NULL;
+            copy = psMetadataItemCopy(itemVector);
+            ok( copy == NULL, "psMetadataItemCopy:  return NULL for invalid vector item");
+            //        itemVector->data.V = psVectorAlloc(1, PS_TYPE_S32);
+        }
+        //Time case
+        {
+            copy = psMetadataItemCopy(itemTime);
+            int int0 = (int)((psTime*)(copy->data.V))->sec;
+            int int1 = (int)((psTime*)(copy->data.V))->nsec;
+            bool leap = ((psTime*)(copy->data.V))->leapsecond;
+            ok( (copy->type == PS_DATA_TIME && (strncmp(copy->name, "itemTime", 12) == 0)
+                 && int0 == 1 && int1 == 0 && strncmp(copy->comment,"",2) == 0) && leap,
+                "psMetadataItemCopy:  return valid copy of Time item.");
+            psFree(copy);
+        }
+        //Time case - invalid time item
+        {
+            psTime *t = psTimeAlloc(PS_TIME_TT);
+            t->type += 1;
+            psMetadataItem *itemTime2 = psMetadataItemAlloc("itemTime", PS_DATA_TIME, "", t);
+            copy = psMetadataItemCopy(itemTime2);
+            ok( copy != NULL && copy->data.V == NULL,
+                "psMetadataItemCopy:  return non-NULL item for invalid input time item");
+            psFree(t);
+            psFree(copy);
+            psFree(itemTime2);
+        }
+        //Time case - NULL time item
+        {
+            psFree(itemTime->data.V);
+            itemTime->data.V = NULL;
+            copy = psMetadataItemCopy(itemTime);
+            ok( copy != NULL && copy->data.V == NULL,
+                "psMetadataItemCopy:  return non-NULL item for NULL input time item");
+            //        (itemTime->data.V) = psTimeAlloc(PS_TIME_TAI);
+            psFree(copy);
+        }
+        //Metadata case
+        {
+            copy = psMetadataItemCopy(itemMD);
+            psMetadataItem *testItem = NULL;
+            testItem = psMetadataGet(((psMetadata*)(copy->data.V)), PS_LIST_HEAD);
+            if (testItem == NULL)
+                printf("\nError in psMetadataGet.\n");
+            ok( copy->type == PS_DATA_METADATA && strncmp(copy->name, "itemMD", 8) == 0
+                && (strncmp(copy->comment, "",2) == 0) && testItem->type == PS_DATA_S32
+                && (strncmp(testItem->name, "mdS32", 10) == 0) && testItem->data.S32 == 666
+                && (strncmp(testItem->comment,"",2) == 0),
+                "psMetadataItemCopy:  return valid copy of Metadata item.");
+            psFree(copy);
+        }
+        //Region case
+        {
+            copy = psMetadataItemCopy(itemRegion);
+            float x0 = ((psRegion*)(copy->data.V))->x0;
+            float x1 = ((psRegion*)(copy->data.V))->x1;
+            float y0 = ((psRegion*)(copy->data.V))->y0;
+            float y1 = ((psRegion*)(copy->data.V))->y1;
+            ok( (copy->type == PS_DATA_REGION && (strncmp(copy->name, "itemRegion", 12) == 0)
+                 && abs(x0-0.0) < FLT_EPSILON && abs(x1-2.0) < FLT_EPSILON
+                 && abs(y0-1.0) < FLT_EPSILON && abs(y1-3.0) < FLT_EPSILON
+                 && strncmp(copy->comment,"",2) == 0),
+                "psMetadataItemCopy:  return valid copy of Region item.");
+            psFree(copy);
+        }
+    
+        //Test Unsupported type - (sphere for example)
+        psSphere *sphere = psSphereAlloc();
+        sphere->r = 1.0;
+        sphere->d = 6.66;
+        psMetadataItem *itemSphere = psMetadataItemAlloc("itemSphere", PS_DATA_SPHERE, "", sphere);
+        psFree(sphere);
+        {
+            copy = psMetadataItemCopy(itemSphere);
+            float R = ((psSphere*)(copy->data.V))->r;
+            float D = ((psSphere*)(copy->data.V))->d;
+            ok( (copy->type == PS_DATA_SPHERE && (strncmp(copy->name, "itemSphere", 12) == 0) &&
+                 abs(R - 1.0) < FLT_EPSILON && abs(D - 6.66) < FLT_EPSILON &&
+                 strncmp(copy->comment,"",2) == 0),
+                "psMetadataItemCopy:  return valid copy of SPHERE item.");
+            psFree(copy);
+        }
+    
+        psFree(itemSphere);
+        psFree(inV);
+        psFree(teaTime);
+        psFree(md);
+        psFree(reg);
+        psFree(itemBool);
+        psFree(itemS8);
+        psFree(itemS16);
+        psFree(itemS32);
+        psFree(itemS64);
+        psFree(itemU8);
+        psFree(itemU16);
+        psFree(itemU32);
+        psFree(itemU64);
+        psFree(itemF32);
+        psFree(itemF64);
+        psFree(itemString);
+        psFree(itemVector);
+        psFree(itemTime);
+        psFree(itemMD);
+        psFree(itemRegion);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testMetadataItemTransfer()
+    {
+        psMemId id = psMemGetId();
+        psMetadata *out = psMetadataAlloc();
+        psMetadata *in = psMetadataAlloc();
+        psString key = psStringAlloc(30);
+        strncpy(key, "key", 4);
+    
+        //Return false for NULL in input
+        {
+            ok( !psMetadataItemSupplement(out, NULL, key),
+                "psMetadataItemSupplement:  return false for NULL in metadata.");
+        }
+        //Return false for NULL out input
+        {
+            ok( !psMetadataItemSupplement(NULL, in, key),
+                "psMetadataItemSupplement:  return false for NULL out metadata.");
+        }
+        //Return false for NULL key input
+        {
+            ok( !psMetadataItemSupplement(out, in, NULL),
+                "psMetadataItemSupplement:  return false for NULL key string.");
+        }
+        psMetadataAddS32(in, PS_LIST_HEAD, "key", PS_META_NO_REPLACE, "", 666);
+        psMetadataAddF32(out, PS_LIST_HEAD, "out", 0, "", 6.66);
+    
+        //Return true for valid inputs
+        {
+            bool transfered = psMetadataItemSupplement(out, in, key);
+            psMetadataItem *item = NULL;
+            if (transfered)
+                item = psMetadataGet(out, PS_LIST_TAIL);
+            ok(transfered && item->type == PS_DATA_S32 && strncmp(item->name, "key", 4) == 0
+               && item->data.S32 == 666 && strncmp(item->comment, "", 2) == 0,
+               "psMetadataItemSupplement:  return true for valid inputs.");
+        }
+        /*  Attempt here was to return false inside of ItemTransfer at psMetadataAddItem.
+        //Return false for duplicate entry with PS_META_DEFAULT (no duplicates)
+        {
+            psMetadataRemoveKey(out, key);
+            psMetadataItem *item = psMetadataGet(in, PS_LIST_HEAD);
+            psMetadataAddItem(out, item, PS_LIST_TAIL, PS_META_NO_REPLACE);
+            ok( !psMetadataItemSupplement(out, in, key),
+                "psMetadataItemSupplement:  return false for duplicate metadataItem.");
+        }
+        */
+        //Return false for invalid key
+        {
+            strncpy(key, "not MY key", 15);
+            ok( !psMetadataItemSupplement(out, in, key),
+                "psMetadataItemSupplement:  return false for invalid key.");
+        }
+        //Return false for invalid metadata
+        psMetadata *invalid = psMetadataAlloc();
+        psFree(invalid->hash);
+        invalid->hash = NULL;
+        {
+            ok( !psMetadataItemSupplement(invalid, in, "key"),
+                "psMetadataItemSupplement:  return false for invalid metadata.");
+        }
+        psFree(invalid);
+    
+        psFree(out);
+        psFree(in);
+        psFree(key);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_creating.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_creating.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_creating.c	(revision 22322)
@@ -0,0 +1,487 @@
+/**
+*  C Implementation: tap_psMetadata_creating
+*
+* Description:  Tests for psMetadataAlloc, psMetadataItemAlloc's (TYPEs),
+*               psMemCheckMetadata, psMemCheckMetadataItem, psMetadataAddItem,
+*               psMetadataIteratorAlloc
+*
+*
+* Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+*
+* Copyright: See COPYING file that comes with this distribution
+*
+*/
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(42);
+    // psMetadata & psMetadataItem Creation Functions
+
+
+    // testItemAllocs(void)
+    // psMetadataItemAlloc Fxns
+    {
+        psMemId id = psMemGetId();
+        psMetadataItem *itemStr = psMetadataItemAllocStr("itemStr", "", "itemStr");
+        psMetadataItem *itemF32 = psMetadataItemAllocF32("itemF32", "", 6.66);
+        psMetadataItem *itemF64 = psMetadataItemAllocF64("itemF64", "", 0.666);
+        psMetadataItem *itemS8 = psMetadataItemAllocS8("itemS8", "", 0);
+        psMetadataItem *itemS16 = psMetadataItemAllocS16("itemS16", "", 1);
+        psMetadataItem *itemS32 = psMetadataItemAllocS32("itemS32", "", 2);
+        psMetadataItem *itemS64 = psMetadataItemAllocS64("itemS64", "", 3);
+        psMetadataItem *itemU8 = psMetadataItemAllocU8("itemU8", "", 0);
+        psMetadataItem *itemU16 = psMetadataItemAllocU16("itemU16", "", 1);
+        psMetadataItem *itemU32 = psMetadataItemAllocU32("itemU32", "", 2);
+        psMetadataItem *itemU64 = psMetadataItemAllocU64("itemU64", "", 3);
+        psMetadataItem *itemBool = psMetadataItemAllocBool("itemBool", "", true);
+    
+        //First try to free a NULL item
+        psMetadataItem *nullItem = NULL;
+        psFree(nullItem);
+        //Verify correct allocation
+        //String
+        {
+            skip_start(itemStr == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocStr() failed");
+            ok(itemStr->type == PS_DATA_STRING
+               && !strncmp(itemStr->name, "itemStr", 10)
+               && !strncmp(itemStr->data.V, "itemStr", 10)
+               && !strncmp(itemStr->comment, "", 2),
+               "psMetadataItemAllocStr:    create valid string item.");
+            skip_end();
+        }
+        //F32
+        {
+            skip_start(itemF32 == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocF32() failed");
+            ok(itemF32->type == PS_DATA_F32
+               && !strncmp(itemF32->name, "itemF32", 10)
+               && abs(itemF32->data.F32 - 6.66) < FLT_EPSILON
+               && !strncmp(itemF32->comment, "", 2),
+               "psMetadataItemAllocF32:    create valid F32 item.");
+            skip_end();
+        }
+        //F64
+        {
+            skip_start(itemF64 == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocF64() failed");
+            ok(itemF64->type == PS_DATA_F64
+               && !strncmp(itemF64->name, "itemF64", 10)
+               && abs(itemF64->data.F64 - 0.666) < DBL_EPSILON
+               && !strncmp(itemF64->comment, "", 2),
+               "psMetadataItemAllocF64:    create valid F64 item.");
+            skip_end();
+        }
+        //S8
+        {
+            skip_start(itemS8 == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocS8() failed");
+            ok(itemS8->type == PS_DATA_S8
+               && !strncmp(itemS8->name, "itemS8", 10)
+               && itemS8->data.S8 == 0
+               && !strncmp(itemS8->comment, "", 2),
+               "psMetadataItemAllocS8:     create valid S8 item.");
+            skip_end();
+        }
+        //S16
+        {
+            skip_start(itemS16 == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocS16() failed");
+            ok(itemS16->type == PS_DATA_S16
+               && !strncmp(itemS16->name, "itemS16", 10)
+               && itemS16->data.S16 == 1
+               && !strncmp(itemS16->comment, "", 2),
+               "psMetadataItemAllocS16:    create valid S16 item.");
+            skip_end();
+        }
+        //S32
+        {
+            skip_start(itemS32 == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocS32() failed");
+            ok(itemS32->type == PS_DATA_S32
+               && !strncmp(itemS32->name, "itemS32", 10)
+               && itemS32->data.S32 == 2
+               && !strncmp(itemS32->comment, "", 2),
+               "psMetadataItemAllocS32:    create valid S32 item.");
+            skip_end();
+        }
+        //S64
+        {
+            skip_start(itemS64 == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocS64() failed");
+            ok(itemS64->type == PS_DATA_S64
+               && !strncmp(itemS64->name, "itemS64", 10)
+               && itemS64->data.S64 == 3
+               && !strncmp(itemS64->comment, "", 2),
+               "psMetadataItemAllocS64:    create valid S64 item.");
+            skip_end();
+        }
+        //U8
+        {
+            skip_start(itemU8 == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocU8() failed");
+            ok(itemU8->type == PS_DATA_U8
+               && !strncmp(itemU8->name, "itemU8", 10)
+               && itemU8->data.U8 == 0
+               && !strncmp(itemU8->comment, "", 2),
+               "psMetadataItemAllocU8:     create valid U8 item.");
+            skip_end();
+        }
+        //U16
+        {
+            skip_start(itemU16 == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocU16() failed");
+            ok(itemU16->type == PS_DATA_U16
+               && !strncmp(itemU16->name, "itemU16", 10)
+               && itemU16->data.U16 == 1
+               && !strncmp(itemU16->comment, "", 2),
+               "psMetadataItemAllocU16:    create valid U16 item.");
+            skip_end();
+        }
+        //U32
+        {
+            skip_start(itemU32 == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocU32() failed");
+            ok(itemU32->type == PS_DATA_U32
+               && !strncmp(itemU32->name, "itemU32", 10)
+               && itemU32->data.U32 == 2
+               && !strncmp(itemU32->comment, "", 2),
+               "psMetadataItemAllocU32:   create valid U32 item.");
+            skip_end();
+        }
+        //U64
+        {
+            skip_start(itemU64 == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocU64() failed");
+            ok(itemU64->type == PS_DATA_U64
+               && !strncmp(itemU64->name, "itemU64", 10)
+               && itemU64->data.U64 == 3
+               && !strncmp(itemU64->comment, "", 2),
+               "psMetadataItemAllocU64:   create valid U64 item.");
+            skip_end();
+        }
+        //Bool
+        {
+            skip_start(itemBool == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocBool() failed");
+            ok(itemBool->type == PS_DATA_BOOL
+               && !strncmp(itemBool->name, "itemBool", 10)
+               && itemBool->data.B
+               && !strncmp(itemBool->comment, "", 2),
+               "psMetadataItemAllocBool:  create valid Bool item.");
+            skip_end();
+        }
+    
+        //Allocate a pointer.  Example= psSphere
+        {
+            psSphere *s = psSphereAlloc();
+            s->r = 1.1;
+            s->d = 2.1;
+            psMetadataItem *itemPtr = psMetadataItemAllocPtr("itemPtr", PS_DATA_SPHERE, "", s);
+            skip_start(itemPtr == NULL, 1,
+                       "Skipping 1 tests because psMetadataItemAllocPtr() failed");
+            float r = ((psSphere*)(itemPtr->data.V))->r;
+            float d = ((psSphere*)(itemPtr->data.V))->d;
+            ok( itemPtr->type == PS_DATA_SPHERE
+                && !strncmp(itemPtr->name, "itemPtr", 10)
+                && abs(r-1.1) < FLT_EPSILON && abs(d-2.1) < FLT_EPSILON
+                && !strncmp(itemPtr->comment, "", 2),
+                "psMetadataItemAllocPtr:   create valid ptr (psSphere) item.");
+            skip_end();
+            psFree(s);
+            psFree(itemPtr);
+        }
+    
+        //Try to Allocate an unsupported type with AllocV
+        {
+            psMetadataItem *itemNULL = NULL;
+            psLine *line = psLineAlloc(2);
+            itemNULL = psMetadataItemAlloc("itemNULL", PS_DATA_LINE, NULL, line);
+            ok( itemNULL == NULL,
+                "psMetadataItemAlloc:      return NULL for unsupported type.");
+            psFree(line);
+        }
+    
+        //Now Allocate the item with Alloc and test MemCheck for it's type.
+        {
+            psMetadataItem *itemAllocV = psMetadataItemAlloc("itemAllocV", PS_DATA_S32, NULL, 4);
+            skip_start(itemAllocV == NULL, 2,
+                       "Skipping 2 tests because psMetadataItemAllocV() failed");
+            ok (itemAllocV->type == PS_DATA_S32
+                && !strncmp(itemAllocV->name, "itemAllocV", 10)
+                && itemAllocV->data.S32 == 4
+                && !strncmp(itemAllocV->comment, "", 2),
+                "psMetadataItemAlloc:      create valid S32 item.");
+            ok ( psMemCheckMetadataItem(itemAllocV),
+                 "psMemCheckMetadataItem:   return true for valid MetadataItem.");
+            skip_end();
+            psFree(itemAllocV);
+        }
+    
+        //Make sure MemCheckItem worked.  Try primitive type.  Expect false.
+	// XXX EAM : disabled
+        if (0) {
+            int j = 2;
+            ok( !psMemCheckMetadataItem(&j),
+                "psMemCheckMetadataItem:   return false for non-MetadataItem input.");
+        }
+    
+        psFree(itemStr);
+        psFree(itemF32);
+        psFree(itemF64);
+        psFree(itemS8);
+        psFree(itemS16);
+        psFree(itemS32);
+        psFree(itemS64);
+        psFree(itemU8);
+        psFree(itemU16);
+        psFree(itemU32);
+        psFree(itemU64);
+        psFree(itemBool);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psMetadataAlloc & psMetadataAdd Fxns");
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = NULL;
+        md = psMetadataAlloc();
+        psMetadata *md1 = psMetadataAlloc();
+        psMetadata *md2 = psMetadataAlloc();
+        //test MemCheck for a valid psMetadata
+        {
+            ok( psMemCheckMetadata(md),
+                "psMemCheckMetadata:       return true for valid Metadata.");
+        }
+        //Make sure MemCheck worked.  Try primitive type.  Expect false.
+	// XXX EAM : disabled
+        if (0) {
+            int j = 2;
+            ok( !psMemCheckMetadata(&j),
+                "psMemCheckMetadata:       return false for non-Metadata input.");
+        }
+        //Return false for attempt to add a psMetadataItem with no name.
+        {
+            ok( !psMetadataAdd(md1, PS_LIST_HEAD, NULL, PS_DATA_S32, "", 1),
+                "psMetadataAdd:            return false for adding item with no name.");
+        }
+        psFree(md1);
+        md1 = psMetadataAlloc();
+    
+        //Return false for NULL metadata input
+        psMetadataItem *item = psMetadataItemAllocBool("item", "", true);
+        {
+            ok( !psMetadataAddItem(NULL, item, PS_LIST_HEAD, PS_META_DEFAULT),
+                "psMetadataAddItem:        return false for NULL metadata input.");
+        }
+        //Return false for metadata with NULL hash
+        psFree(md1->hash);
+        md1->hash = NULL;
+        {
+            ok( !psMetadataAddItem(md1, item, PS_LIST_HEAD, PS_META_DEFAULT),
+                "psMetadataAddItem:        return false for metadata with no hash.");
+        }
+        psFree(md1);
+        //Return false for metadata with NULL list
+        psFree(md2->list);
+        md2->list = NULL;
+        {
+            ok( !psMetadataAddItem(md2, item, PS_LIST_HEAD, PS_META_DEFAULT),
+                "psMetadataAddItem:        return false for metadata with no list.");
+        }
+        psFree(md2);
+        //Return false for NULL psMetadataItem input
+        {
+            ok( !psMetadataAddItem(md, NULL, PS_LIST_HEAD, PS_META_DEFAULT),
+                "psMetadataAddItem:        return false for NULL item input.");
+        }
+        //Return false for psMetadataItem with NULL name
+        psFree(item->name);
+        item->name = NULL;
+        {
+            ok( !psMetadataAddItem(md, item, PS_LIST_HEAD, PS_META_DEFAULT),
+                "psMetadataAddItem:        return false for item with no name.");
+        }
+        psFree(item);
+    
+        psMetadata *itemMD = psMetadataAlloc();
+        psMetadataAddS32(itemMD, PS_LIST_HEAD, "s", PS_META_DUPLICATE_OK, "", 2);
+        psMetadataItem *item2 = psMetadataItemAllocS32("s", "", 2);
+    
+        //Return true for addition of multi
+        {
+            ok( psMetadataAddItem(md, item2, PS_LIST_TAIL, PS_META_DUPLICATE_OK),
+                "psMetadataAddItem:        return true for addition of PS_DATA_METADATA_MULTI.");
+        }
+        psFree(item2);
+        psFree(itemMD);
+    
+        //Existing entry with duplicate key found.
+        psMetadataAddS32(md, PS_LIST_HEAD, "S32", 0, "", 1);
+        item2 = psMetadataItemAllocS32("S32", "", 1);
+        //FLAG = PS_META_REPLACE
+        {
+            ok( psMetadataAddItem(md, item2, PS_LIST_TAIL, PS_META_REPLACE),
+                "psMetadataAddItem:        return true for PS_META_REPLACE flag.");
+        }
+        //FLAG = PS_META_DUPLICATE_OK
+        {
+            ok( psMetadataAddItem(md, item2, PS_LIST_TAIL, PS_META_DUPLICATE_OK),
+                "psMetadataAddItem:        return true for PS_META_DUPLICATE_OK flag.");
+        }
+        /*        SEE BELOW.  TEST CASE REDONE b/c item2 in md was already a MULTI
+        //FLAG = PS_META_NO_REPLACE
+        {
+            ok( psMetadataAddItem(md, item2, PS_LIST_TAIL, PS_META_NO_REPLACE),
+                "psMetadataAddItem:        return true for PS_META_NO_REPLACE flag.");
+        }
+        */
+        //FLAG = PS_META_DEFAULT
+        {
+            ok( psMetadataAddItem(md, item2, PS_LIST_TAIL, PS_META_DEFAULT),
+                "psMetadataAddItem:        return false for PS_META_DEFAULT flag.");
+        }
+    
+        //Return false for attempting to add an element twice with flag NO_REPLACE
+        {
+            psMetadata *norep = psMetadataAlloc();
+            psMetadataAddS32(norep, PS_LIST_HEAD, "S32", 0, "", 1);
+            psMetadataItem *item666 = psMetadataItemAllocS32("S32", "", 1);
+            ok( psMetadataAddItem(norep, item666, PS_LIST_TAIL, PS_META_NO_REPLACE),
+                "psMetadataAddItem:        return true for PS_META_NO_REPLACE flag.");
+            psFree(item666);
+            psFree(norep);
+        }
+        //Return false for bad list location
+        {
+            psMetadata *emptymeta = psMetadataAlloc();
+            psMetadataItem *item666 = psMetadataItemAllocS32("S32", "", 1);
+            ok( !psMetadataAddItem(emptymeta, item666, -100, PS_META_NO_REPLACE),
+                "psMetadataAddItem:        return false for invalid list location.");
+            psFree(item666);
+            psFree(emptymeta);
+        }
+        //Return false for trying to add to MULTI with broken list element.
+        {
+            psMetadata *broken_list = psMetadataAlloc();
+            psMetadataAddS32(broken_list, PS_LIST_HEAD, "S32", 0, "", 1);
+            psMetadataAddS32(broken_list, PS_LIST_TAIL, "S32", PS_META_DUPLICATE_OK, "", 1);
+            psMetadataItem *brokenptr = psMetadataLookup(broken_list, "S32");
+            if (brokenptr->type != PS_DATA_METADATA_MULTI)
+                printf("\nError, Error\n");
+            else
+            {
+                psFree(brokenptr->data.list);
+                brokenptr->data.list = NULL;
+            }
+            psMetadataItem *item666 = psMetadataItemAllocS32("S32", "", 1);
+            ok( !psMetadataAddItem(broken_list, item666, PS_LIST_TAIL, PS_META_DUPLICATE_OK),
+                "psMetadataAddItem:        return false for MULTI with NULL list.");
+            psFree(item666);
+            psFree(broken_list);
+        }
+    
+        //Attempt to add a MULTI
+        psFree(item2);
+        item2 = NULL;
+        psMetadata *xxx = psMetadataAlloc();
+        psMetadataAddS32(xxx, PS_LIST_TAIL, "new S32", 0, "", 1);
+        item2 = psMetadataItemAllocS32("new S32", "", 1);
+        psMetadataAddItem(xxx, item2, PS_LIST_TAIL, PS_META_DUPLICATE_OK);
+        psFree(item2);
+        item2 = psMetadataLookup(xxx, "new S32");
+        {
+            ok( psMetadataAddItem(md, item2, PS_LIST_TAIL, PS_META_DUPLICATE_OK),
+                "psMetadataAddItem:        return true for new MULTI item");
+        }
+        //Attempt to add a 2nd reference of a MULTI to the originating metadata container
+        {
+            ok( !psMetadataAddItem(xxx, item2, PS_LIST_TAIL, PS_META_NO_REPLACE),
+                "psMetadataAddItem:        return false for attempt to add 2nd reference"
+                " of a MULTI");
+        }
+        psFree(xxx);
+    
+        //No existing entries or duplicates found
+        item2 = psMetadataItemAllocS32("new", "", 2);
+        {
+            ok( psMetadataAddItem(md, item2, PS_LIST_TAIL, PS_META_DUPLICATE_OK),
+                "psMetadataAddItem:        return true for new item.");
+        }
+    
+        psFree(item2);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+    
+
+    // psMetadataIteratorAlloc
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psMetadata *md2 = NULL;
+        psMetadataAddS32(md, PS_LIST_HEAD, "S32_1", PS_META_NO_REPLACE, "", 1);
+        psMetadataAddS32(md, PS_LIST_TAIL, "S32_2", PS_META_DEFAULT, "", 2);
+    
+        psMetadataIterator *iter = NULL;
+        //Return NULL for NULL metadata input
+        {
+            iter = psMetadataIteratorAlloc(md2, PS_LIST_HEAD, NULL);
+            ok( iter == NULL,
+                "psMetadataIteratorAlloc:  return NULL for NULL metadata input.");
+        }
+        //Return NULL for metadata with no List
+        md2 = psMetadataAlloc();
+        psFree(md2->list);
+        md2->list = NULL;
+        {
+            iter = psMetadataIteratorAlloc(md2, PS_LIST_HEAD, NULL);
+            ok( iter == NULL,
+                "psMetadataIteratorAlloc:  return NULL for metadata with no list.");
+        }
+        psFree(md2);
+    
+        //Return newly allocated MetadataIterator, regex=NULL
+        {
+            iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
+            ok( iter != NULL,
+                "psMetadataIteratorAlloc:  return valid iterator for valid inputs, regex=NULL.");
+        }
+        //Return NULL for attempt to allocate an iterator with unfound regex.
+        psFree(iter);
+        iter = NULL;
+        {
+            iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, "IPP.machines.sky");
+            ok( iter == NULL,
+                "psMetadataIteratorAlloc:  return NULL for not-found regex input");
+        }
+        //Return NULL for invalid regex input.
+        {
+            iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, "IPP.machines.sky\\");
+            ok( iter == NULL,
+                "psMetadataIteratorAlloc:  return NULL for invalid regex input");
+        }
+        //Return properly allocated iterator for valid non-null regex input.
+        {
+            iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, "S32_");
+            ok( iter != NULL,
+                "psMetadataIteratorAlloc:  return valid iterator for valid regex input");
+        }
+    
+        //Check for Memory leaks
+        {
+            psFree(iter);
+            psFree(md);
+            checkMem();
+        }
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_manip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_manip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_manip.c	(revision 22322)
@@ -0,0 +1,460 @@
+/**
+ *  C Implementation: tap_psMetadata_manip
+ *
+ * Description:  Tests for psMetadataRemove's, psMetadataLookup's (TYPE),
+ *
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_WARN);
+    plan_tests(50);
+    // psMetadataLookup, Remove, and Iterator Fxns
+
+
+    // psMetadataLookup Functions
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psMetadataAddStr(md, PS_LIST_TAIL, "itemStr", 0, "I am a string", "GNIRTS");
+        psMetadataAddBool(md, PS_LIST_TAIL, "itemBool", 0, "I am a boolean", true);
+        psMetadataAddS8(md, PS_LIST_TAIL, "itemS8", 0, "I am S8", 6);
+        psMetadataAddS16(md, PS_LIST_TAIL, "itemS16", 0, "I am S16", -666);
+        psMetadataAddS32(md, PS_LIST_TAIL, "itemS32", 0, "I am a integer", 55);
+        psMetadataAddS64(md, PS_LIST_TAIL, "itemS64", 0, "I am S64", 666);
+        psMetadataAddU8(md, PS_LIST_TAIL, "itemU8", 0, "I am U8", 6);
+        psMetadataAddU16(md, PS_LIST_TAIL, "itemU16", 0, "I am U16", 666);
+        psMetadataAddU32(md, PS_LIST_TAIL, "itemU32", 0, "I am U32", 666);
+        psMetadataAddU64(md, PS_LIST_TAIL, "itemU64", 0, "I am U64", 666);
+        psMetadataAddF32(md, PS_LIST_TAIL, "itemF32", 0, NULL, 3.14);
+        psMetadataAddF64(md, PS_LIST_TAIL, "itemF64", 0, "", 6.28);
+        psSphere *sphere = psSphereAlloc();
+        sphere->r = 6.66;
+        sphere->d = 666.666;
+        psMetadataAddPtr(md, PS_LIST_HEAD, "ptr", PS_DATA_SPHERE, "", sphere);
+        //    psMetadataAddPtr(md, PS_LIST_TAIL, "ptr", PS_DATA_METADATA_MULTI, "", sphere);
+        psTime *time;
+        time = psTimeAlloc(PS_TIME_TAI);
+        time->sec = 1000;
+        time->nsec = 25;
+        time->leapsecond = true;
+        psMetadataAddTime(md, PS_LIST_TAIL, "time01", 0, "I am time", time);
+        psMetadata *newMD = psMetadataAlloc();
+        psMetadataAddS32(newMD, PS_LIST_TAIL, "1", 0, "", 666);
+        psMetadataAddMetadata(md, PS_LIST_TAIL, "metadata7", 0, "I am a metadata", newMD);
+    
+        //Lookup Standard number-types (valid cases)
+        //U8
+        {
+            bool status = false;
+            psU8 u8 = 0;
+            u8 = psMetadataLookupU8(&status, md, "itemU8");
+            ok( status && u8 == 6,
+                "psMetadataLookupU8:     return correct U8 value on lookup.");
+        }
+        //U16
+        {
+            bool status = false;
+            psU16 u16 = 0;
+            u16 = psMetadataLookupU16(&status, md, "itemU16");
+            ok( status && u16 == 666,
+                "psMetadataLookupU16:    return correct U16 value on lookup.");
+        }
+        //U32
+        {
+            bool status = false;
+            psU32 u32 = 0;
+            u32 = psMetadataLookupU32(&status, md, "itemU32");
+            ok( status && u32 == 666,
+                "psMetadataLookupU32:    return correct U32 value on lookup.");
+        }
+        //U64
+        {
+            bool status = false;
+            psU64 u64 = 0;
+            u64 = psMetadataLookupU64(&status, md, "itemU64");
+            ok( status && u64 == 666,
+                "psMetadataLookupU64:    return correct U64 value on lookup.");
+        }
+        //S8
+        {
+            bool status = false;
+            psS8 s8 = 0;
+            s8 = psMetadataLookupS8(&status, md, "itemS8");
+            ok( status && s8 == 6,
+                "psMetadataLookupS8:     return correct S8 value on lookup.");
+        }
+        //S16
+        {
+            bool status = false;
+            psS16 s16 = 0;
+            s16 = psMetadataLookupS16(&status, md, "itemS16");
+            ok( status && s16 == -666,
+                "psMetadataLookupS16:    return correct S16 value on lookup.");
+        }
+        //S32
+        {
+            bool status = false;
+            psS32 s32 = 0;
+            s32 = psMetadataLookupS32(&status, md, "itemS32");
+            ok( status && s32 == 55,
+                "psMetadataLookupS32:    return correct S32 value on lookup.");
+        }
+        //S64
+        {
+            bool status = false;
+            psS64 s64 = 0;
+            s64 = psMetadataLookupS64(&status, md, "itemS64");
+            ok( status && s64 == 666,
+                "psMetadataLookupS64:    return correct S64 value on lookup.");
+        }
+        //F32
+        {
+            bool status = false;
+            psF32 f32 = 0;
+            f32 = psMetadataLookupF32(&status, md, "itemF32");
+            ok( status && abs(f32 - 3.14) < FLT_EPSILON,
+                "psMetadataLookupF32:    return correct F32 value on lookup.");
+        }
+        //F64
+        {
+            bool status = false;
+            psF64 f64 = 0;
+            f64 = psMetadataLookupF64(&status, md, "itemF64");
+            ok( status && abs(f64 - 6.28) < DBL_EPSILON,
+                "psMetadataLookupF64:    return correct F64 value on lookup.");
+        }
+        //Bool
+        {
+            bool status = false;
+            bool stat = false;
+            stat = psMetadataLookupBool(&status, md, "itemBool");
+            ok( status && stat,
+                "psMetadataLookupBool:   return correct Bool value on lookup.");
+        }
+        //String
+        {
+            bool status = false;
+            psString str = NULL;
+            str = psMetadataLookupStr(&status, md, "itemStr");
+            ok( status && !strncmp(str, "GNIRTS", 8),
+                "psMetadataLookupStr:   return correct String value on lookup.");
+        }
+        //Pointer
+        {
+            bool status = false;
+            psSphere *sph = NULL;
+            sph = (psSphere*)(psMetadataLookupPtr(&status, md, "ptr"));
+            ok( status && sph != NULL && abs(sph->r-6.66) < FLT_EPSILON,
+                "psMetadataLookupPtr:   return correct pointer value on lookup.");
+        }
+        //Time
+        {
+            bool status = false;
+            psTime *t = NULL;
+            t = psMetadataLookupTime(&status, md, "time01");
+            ok( status && t->sec == 1000,
+                "psMetadataLookupTime:  return correct time value on lookup.");
+        }
+        //Metadata
+        {
+            bool status = false;
+            psMetadata *mdtemp = NULL;
+            mdtemp = psMetadataLookupMetadata(&status, md, "metadata7");
+            psMetadataItem *itemtemp = psMetadataGet(mdtemp, PS_LIST_HEAD);
+            ok( status && itemtemp->type == PS_DATA_S32 && itemtemp->data.S32 == 666,
+                "psMetadataLookupMD:    return correct metadata value on lookup.");
+        }
+    
+        //Try Negatives and non-standard lookup cases
+        //String  - invalid key name
+        {
+            bool status = false;
+            psString str = NULL;
+            str = psMetadataLookupStr(&status, md, "item");
+            ok( !status && str == NULL,
+                "psMetadataLookupStr:   return NULL for incorrect key.");
+            str = NULL;
+            str = psMetadataLookupStr(NULL, md, "item");
+            ok( str == NULL,
+                "psMetadataLookupStr:   return NULL for incorrect key (& NULL status).");
+        }
+        //String - wrong key->  lookup s8 data
+        {
+            bool status = false;
+            psString str = NULL;
+            str = psMetadataLookupStr(&status, md, "itemS8");
+            ok( !status && str == NULL,
+                "psMetadataLookupStr:   return NULL for incorrect key .");
+            str = NULL;
+            str = psMetadataLookupStr(NULL, md, "itemS8");
+            ok( str == NULL,
+                "psMetadataLookupStr:   return NULL for incorrect key (& NULL status).");
+        }
+        //Pointer - invalid key name
+        {
+            bool status = false;
+            psSphere *sph = NULL;
+            sph = psMetadataLookupPtr(&status, md, "item");
+            ok( !status && sph == NULL,
+                "psMetadataLookupPtr:   return NULL for incorrect key.");
+        }
+        //Pointer - wrong key -> lookup s8 data
+        {
+            bool status = false;
+            psSphere *sph = NULL;
+            sph = psMetadataLookupPtr(&status, md, "itemS8");
+            ok( !status && sph == NULL,
+                "psMetadataLookupPtr:   return NULL for incorrect key.");
+        }
+    
+        //Pointer**  - lookup a PS_DATA_METADATA_MULTI
+        {
+            psMetadata *metadata = psMetadataAlloc();
+            psMetadataItem *multi = psMetadataItemAlloc("ptr", PS_DATA_METADATA_MULTI, "", NULL);
+            psMetadataAddItem(metadata, multi, PS_LIST_HEAD, PS_META_DUPLICATE_OK);
+            psMetadataAddPtr(metadata, PS_LIST_TAIL, "ptr",
+                             PS_DATA_SPHERE | PS_META_DUPLICATE_OK, "", sphere);
+            psSphere *s = psSphereAlloc();
+            s->r = 0.666;
+            psMetadataAddPtr(metadata, PS_LIST_TAIL, "ptr", PS_DATA_SPHERE | PS_META_DUPLICATE_OK, "", s);
+            psFree(s);
+            psMetadataItem *metadataItem = NULL;
+            metadataItem = psMetadataLookup(metadata, "ptr");
+            bool status = false;
+            psSphere *sph = NULL;
+            sph = (psSphere*)(psMetadataLookupPtr(&status, metadata, "ptr"));
+            ok( status && sph != NULL && fabs(sph->r - 6.66) < FLT_EPSILON,
+                "psMetadataLookupPtr:   return correct value on lookup of MULTI item.");
+            psFree(metadata);
+            psFree(multi);
+        }
+    
+        //Metadata - invalid key name
+        {
+            bool status = false;
+            psMetadata *met = NULL;
+            met = psMetadataLookupMetadata(&status, md, "item");
+            ok( !status && met == NULL,
+                "psMetadataLookupMD:   return NULL for incorrect key.");
+            met = NULL;
+            met = psMetadataLookupMetadata(NULL, md, "item");
+            ok( met == NULL,
+                "psMetadataLookupMD:   return NULL for incorrect key (& NULL status).");
+        }
+        //Metadata - wrong key -> lookup s8 data
+        {
+            bool status = false;
+            psMetadata *met = NULL;
+            met = psMetadataLookupMetadata(&status, md, "itemS8");
+            ok( !status && met == NULL,
+                "psMetadataLookupMD:   return NULL for incorrect key (& NULL status).");
+            met = NULL;
+            met = psMetadataLookupMetadata(NULL, md, "itemS8");
+            ok( met == NULL,
+                "psMetadataLookupMD:   return NULL for incorrect key.");
+        }
+        //Time - invalid key name
+        {
+            bool status = false;
+            psTime *time = NULL;
+            time = psMetadataLookupTime(&status, md, "item");
+            ok( !status && time == NULL,
+                "psMetadataLookupTime:   return NULL for incorrect key.");
+            time = NULL;
+            time = psMetadataLookupTime(NULL, md, "item");
+            ok( time == NULL,
+                "psMetadataLookupTime:   return NULL for incorrect key (& NULL status).");
+        }
+        //Time - wrong key -> lookup s8 data
+        {
+            bool status = false;
+            psTime *time = NULL;
+            time = psMetadataLookupTime(&status, md, "itemS8");
+            ok( !status && time == NULL,
+                "psMetadataLookupTime:   return NULL for incorrect key.");
+            time = NULL;
+            time = psMetadataLookupTime(NULL, md, "itemS8");
+            ok( time == NULL,
+                "psMetadataLookupTime:   return NULL for incorrect key (& NULL status).");
+        }
+    
+        psFree(sphere);
+        psFree(time);
+        psFree(newMD);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psMetadataRemove Functions
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psMetadataAddStr(md, PS_LIST_TAIL, "itemStr", 0, "I am a string", "GNIRTS");
+        psMetadataAddBool(md, PS_LIST_TAIL, "itemBool", 0, "I am a boolean", true);
+        psMetadataAddS8(md, PS_LIST_TAIL, "itemS8", 0, "I am S8", 6);
+        psMetadataAddS16(md, PS_LIST_TAIL, "itemS16", 0, "I am S16", -666);
+        psMetadataAddS32(md, PS_LIST_TAIL, "itemS32", 0, "I am a integer", 55);
+        psMetadataAddS64(md, PS_LIST_TAIL, "itemS64", PS_META_DUPLICATE_OK, "I am S64", 666);
+        psMetadataAddU8(md, PS_LIST_TAIL, "itemU8", 0, "I am U8", 6);
+    
+        //psMetadataRemoveKey
+        {
+            bool status = psMetadataRemoveKey(md, "itemU8");
+            psMetadataItem *temp = psMetadataGet(md, PS_LIST_TAIL);
+            ok( status && temp->type == PS_DATA_S64,
+                "psMetadataRemoveKey:    return true for valid case.");
+        }
+        //psMetadataRemoveIndex
+        {
+            bool status = psMetadataRemoveIndex(md, PS_LIST_HEAD);
+            psMetadataItem *temp = psMetadataGet(md, PS_LIST_HEAD);
+            ok( status && temp->type == PS_DATA_BOOL,
+                "psMetadataRemoveIndex:  return true for valid case.");
+        }
+        //Attempt to remove an incorrect key - Return false
+        {
+            ok( !psMetadataRemoveKey(md, "BigDog"),
+                "psMetadataRemoveKey:    return false for incorrect key.");
+        }
+        //Attempt to remove an out-of-range index - Return false
+        {
+            ok( !psMetadataRemoveIndex(md, 20),
+                "psMetadataRemoveIndex:  return false for incorrect index.");
+        }
+        //Attempt to remove an item with NULL name
+        {
+            psMetadataItem *temp = psMetadataGet(md, PS_LIST_HEAD);
+            psFree(temp->name);
+            temp->name = NULL;
+            ok( !psMetadataRemoveIndex(md, PS_LIST_HEAD),
+                "psMetadataRemoveIndex:  return false for item with no name.");
+        }
+        //Attempt to remove an item with wrong hash table
+        {
+            psMetadata *md2 = psMetadataAlloc();
+            psMetadataAddBool(md2, PS_LIST_TAIL, "Bool", 0, "I am a boolean", true);
+            psMetadataAddS8(md2, PS_LIST_TAIL, "S8", 0, "I am S8", 6);
+            psMetadataAddS16(md2, PS_LIST_TAIL, "S16", 0, "I am S16", -666);
+            psMetadataAddS32(md2, PS_LIST_TAIL, "S32", 0, "I am a integer", 55);
+            psMetadataAddS64(md2, PS_LIST_TAIL, "S64", 0, "I am S64", 666);
+    
+            psFree(md2->hash);
+            md2->hash = NULL;
+            md2->hash = md->hash;
+            ok( !psMetadataRemoveIndex(md2, PS_LIST_HEAD),
+                "psMetadataRemoveIndex:  return false for item with bad table.");
+            md2->hash = NULL;
+            psFree(md2);
+        }
+        //Attempt to remove a METADATA_MULTI item by key
+        {
+            psMetadataAddS64(md, PS_LIST_TAIL, "itemS64", PS_META_DUPLICATE_OK, "I am S64", 667);
+            psMetadataAddS64(md, PS_LIST_TAIL, "itemS64", PS_META_DUPLICATE_OK, "I am S64", 668);
+            bool status = psMetadataRemoveKey(md, "itemS64");
+            psMetadataItem *temp = psMetadataGet(md, PS_LIST_TAIL);
+            ok( status && temp->type == PS_DATA_S32,
+                "psMetadataRemoveKey:    return true for valid METADATA_MULTI case.");
+    
+        }
+        //Attempt to remove a METADATA_MULTI item by index
+        {
+            psMetadataAddS64(md, PS_LIST_TAIL, "itemS64", PS_META_DUPLICATE_OK, "I am S64", 667);
+            psMetadataAddS64(md, PS_LIST_TAIL, "itemS64", PS_META_DUPLICATE_OK, "I am S64", 668);
+            bool status = psMetadataRemoveIndex(md, PS_LIST_TAIL);
+            psMetadataItem *temp = psMetadataGet(md, PS_LIST_TAIL);
+            ok( status && temp->type == PS_DATA_S64,
+                "psMetadataRemoveIndex:  return true for valid METADATA_MULTI case.");
+        }
+    
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+    
+
+    // psMetadataIterator Functions
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psMetadataAddS32(md, PS_LIST_HEAD, "S32_1", PS_META_NO_REPLACE, "", 1);
+        psMetadataAddS32(md, PS_LIST_TAIL, "S32_2", PS_META_NO_REPLACE, "", 2);
+        psMetadataAddS32(md, PS_LIST_TAIL, "S32_3", PS_META_NO_REPLACE, "", 3);
+    
+        psMetadataIterator *iter = NULL;
+    
+        //Return NULL in psMetadataGetAnd(In/De)crement for NULL iterator input
+        {
+            ok( psMetadataGetAndIncrement(NULL) == NULL,
+                "psMetadataGetAndIncrement:  return NULL for NULL iterator input.");
+            ok( psMetadataGetAndDecrement(NULL) == NULL,
+                "psMetadataGetAndDecrement:  return NULL for NULL iterator input.");
+        }
+        //Return NULL in psMetadataGetAnd(In/De)crement for iterator with no list iterator
+        psMetadataIterator *iter2 = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
+        psFree(iter2->iter);
+        iter2->iter = NULL;
+        {
+            ok( psMetadataGetAndIncrement(iter2) == NULL,
+                "psMetadataGetAndIncrement:  return NULL for iterator with no list iterator.");
+            ok( psMetadataGetAndDecrement(iter2) == NULL,
+                "psMetadataGetAndDecrement:  return NULL for iterator with no list iterator.");
+        }
+        psFree(iter2);
+    
+        //Return valid iterator for valid inputs, regex= NULL
+        psMetadataItem *item = NULL;
+        {
+            iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
+            ok( iter != NULL,
+                "psMetadataIteratorAlloc:    return valid iterator for valid inputs, regex=NULL.");
+            skip_start(iter == NULL, 4,
+                       "Skipping 4 tests because psMetadataIteratorAlloc failed");
+    
+            item = psMetadataGetAndIncrement(iter);
+            ok( item != NULL,
+                "psMetadataGetAndIncrement:  return valid item for valid iterator input.");
+            skip_start(item == NULL, 1,
+                       "Skipping 1 tests because psMetadataGetAndIncrement failed");
+            ok(item->type == PS_DATA_S32
+               && !strncmp(item->name, "S32_1", 7)
+               && item->data.S32 == 1
+               && !strncmp(item->comment, "", 2),
+               "psMetadataGetAndIncrement:  retrieve correct item from valid iterator.");
+            skip_end();
+    
+            item = psMetadataGetAndDecrement(iter);
+            ok( item != NULL,
+                "psMetadataGetAndDecrement:  return valid item for valid iterator input.");
+            skip_start(item == NULL, 1,
+                       "Skipping 1 tests because psMetadataGetAndDecrement failed");
+            ok(item->type == PS_DATA_S32
+               && !strncmp(item->name, "S32_2", 7)
+               && item->data.S32 == 2
+               && !strncmp(item->comment, "", 2),
+               "psMetadataGetAndDecrement:  retrieve correct item from valid iterator.");
+            skip_end();
+    
+            skip_end();
+    
+        }
+    
+        psFree(iter);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
+    
+    
+                
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_polynomials.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_polynomials.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_polynomials.c	(revision 22322)
@@ -0,0 +1,836 @@
+/**
+ *  C Implementation: tap_psMetadata_polynomials
+ *
+ * Description:  Tests for psPolynomial(2,3,4)D(to/from)MD
+ *
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include <string.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(88);
+    // psPolynomial(1D, 2D, 3D, & 4D)(to/from)Metadata tests
+
+
+    // psPolynomial1DtoMetadata & psPolynomial1DfromMetadata functions
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psPolynomial1D *p1d = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+        p1d->coeff[0] = 1.1;
+        p1d->coeff[1] = 2.2;
+        p1d->coeffErr[0] = 0.1;
+        p1d->coeffErr[1] = 0.2;
+
+        //psPolynomial1DtoMetadata
+        //Return a valid metadata containing a polynomial-metadata structure
+        {
+            note ("example of 1DtoMD using the names not the sequence");
+
+            ok( psPolynomial1DtoMetadata(md, p1d, "polyMD"),
+                "psPolynomial1DtoMetadata:      return true for valid inputs.");
+            psMetadata *polyMDtemp = psMetadataLookupMetadata(NULL, md, "polyMD");
+            skip_start( polyMDtemp == NULL , 1,
+                        "Skipping 1 tests because psPolynomial1DtoMetadata has errors");
+
+            bool status;
+            int nX = psMetadataLookupS32(&status, polyMDtemp, "NORDER_X");
+            ok(status,
+               "psPolynomial1DtoMetadata:      found NORDER_X");
+            ok(nX == 1,
+               "psPolynomial1DtoMetadata:      NORDER_X is 1");
+
+            double val;
+            val = psMetadataLookupF64(&status, polyMDtemp, "VAL_X00");
+            ok(status,
+               "psPolynomial1DtoMetadata:      found VAL_X00");
+            is_double(val, 1.1,
+                      "psPolynomial1DtoMetadata:      VAL_X00 is %lf", val);
+
+            val = psMetadataLookupF64(&status, polyMDtemp, "VAL_X01");
+            ok(status,
+               "psPolynomial1DtoMetadata:      found VAL_X01");
+            is_double(val, 2.2,
+                      "psPolynomial1DtoMetadata:      VAL_X01 is %lf", val);
+            skip_end();
+        }
+
+        //Return false for no-name polynomial
+        {
+            ok( !psPolynomial1DtoMetadata(md, p1d, " "),
+                "psPolynomial1DtoMetadata:      return false for no-name.");
+        }
+        //Return false for NULL-name polynomial
+#if 0 // Caught by compiler
+        {
+            ok( !psPolynomial1DtoMetadata(md, p1d, NULL),
+                "psPolynomial1DtoMetadata:      return false for NULL name input.");
+        }
+#endif
+        //Return false for NULL metadata input
+        {
+            ok( !psPolynomial1DtoMetadata(NULL, p1d, "polyMD"),
+                "psPolynomial1DtoMetadata:     return false for NULL metadata input.");
+        }
+        //Return false for NULL polynomial input
+        {
+            ok( !psPolynomial1DtoMetadata(md, NULL, "polyMD"),
+                "psPolynomial1DtoMetadata:     return false for NULL polynomial input.");
+        }
+        //Return true for polynomial with 1 element, a constant
+        {
+            psPolynomial1D *constPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 0);
+            constPoly->coeff[0] = 6.66;
+            ok( psPolynomial1DtoMetadata(md, constPoly, "polyMD"),
+                "psPolynomial1DtoMetadata:     return true for constant polynomial (1 element != 0).");
+            psFree(constPoly);
+        }
+        //Return false for non-ordinary polynomial
+        {
+            psPolynomial1D *p1d1 = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, 1);
+            p1d1->coeff[0] = 1.1;
+            ok( !psPolynomial1DtoMetadata(md, p1d1, "polyMD"),
+                "psPolynomial1DtoMetadata:     return false for chebyshev polynomial");
+            psFree(p1d1);
+        }
+
+        //psPolynomial1DfromMetadata Tests
+        //Return NULL for NULL metadata input.
+        {
+            psPolynomial1D *emptyPoly1D = NULL;
+            emptyPoly1D = psPolynomial1DfromMetadata(NULL);
+            ok( emptyPoly1D == NULL,
+                "psPolynomial1DfromMetadata:   return NULL for NULL metadata input.");
+        }
+        //Get a polynomial from Metadata.
+        psPolynomial1D *outPoly = NULL;
+        psMetadata *polyMDtemp = psMetadataLookupMetadata(NULL, md, "polyMD");
+        outPoly = psPolynomial1DfromMetadata(polyMDtemp);
+        {
+            skip_start( outPoly == NULL, 1,
+                        "Skipping 1 tests because psPolynomial1DfromMetadata has errors.");
+            ok( outPoly->type == PS_POLYNOMIAL_ORD && outPoly->nX == 1 &&
+                abs(outPoly->coeff[0] - 1.1) < DBL_EPSILON,
+                "psPolynomial1DfromMetadata:   return correct polynomial from metadata");
+            skip_end();
+        }
+        //Get a polynomial from Metadata with mask[0] == 1.
+        psFree(outPoly);
+        outPoly = NULL;
+        psPolynomial1D *maskPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 0);
+        maskPoly->coeff[0] = 6.66;
+        maskPoly->coeffMask[0] = 1;
+        psMetadata *newmd = psMetadataAlloc();
+        psPolynomial1DtoMetadata(newmd, maskPoly, "polyMD");
+        psMetadata *polyMask = psMetadataLookupMetadata(NULL, newmd, "polyMD");
+        outPoly = psPolynomial1DfromMetadata(polyMask);
+        {
+            ok( outPoly->type == PS_POLYNOMIAL_ORD && outPoly->nX == 0 &&
+                outPoly->coeffMask[0] == 1,
+                "psPolynomial1DfromMetadata:   return correct polynomial (w/mask) from metadata");
+        }
+        psFree(maskPoly);
+        psFree(newmd);
+        //Return NULL for wrong NELEMENTS
+        psMetadataItem *nElementItem = psMetadataGet(polyMDtemp, PS_LIST_TAIL);
+        nElementItem->data.S32 -= 1;
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial1DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial1DfromMetadata:   "
+                "return NULL for metadata-polynomial with wrong NELEMENTS");
+        }
+        //Return NULL for missing NELEMENTS
+        psMetadataRemoveIndex(polyMDtemp, PS_LIST_TAIL);
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial1DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial1DfromMetadata:   "
+                "return NULL for metadata-polynomial with no NELEMENTS");
+        }
+
+        //Return NULL for polynomial in metadata with no x-order
+        psMetadataRemoveKey(polyMDtemp, "NORDER_X");
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial1DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial1DfromMetadata:   "
+                "return NULL for metadata-polynomial with no x-order");
+        }
+
+        psFree(outPoly);
+        psFree(p1d);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psPolynomial2DtoMetadata & psPolynomial2DfromMetadata functions
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psPolynomial2D *p2d = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 1, 1);
+        p2d->coeff[0][0] = 1.1;
+        p2d->coeff[0][1] = 2.2;
+        p2d->coeff[1][0] = 3.3;
+        p2d->coeff[1][1] = 4.4;
+        p2d->coeffErr[0][0] = 0.1;
+        p2d->coeffErr[0][1] = 0.2;
+        p2d->coeffErr[1][0] = 0.3;
+        p2d->coeffErr[1][1] = 0.4;
+
+        //psPolynomial2DtoMetadata
+        //Return a valid metadata containing a polynomial-metadata structure
+        {
+            ok( psPolynomial2DtoMetadata(md, p2d, "polyMD"),
+                "psPolynomial2DtoMetadata:     return true for valid inputs.");
+
+            // psMetadataConfigWrite (md, "test.md");
+
+            //XXX        ok (0, "these tests are relying on the ORDER of the MD components not the NAMES");
+            //XXX        ok (0, "these seem like irrelevant tests");
+            psMetadata *polyMDtemp = psMetadataLookupMetadata(NULL, md, "polyMD");
+            skip_start( polyMDtemp == NULL , 1,
+                        "Skipping 1 tests because psPolynomial2DtoMetadata has errors");
+            psMetadataItem *polyItem = psMetadataGet(polyMDtemp, 0);
+            skip_start( polyItem == NULL, 2,
+                        "Skipping 2 tests because psPolynomial2DtoMetadata has errors in order elements");
+            ok( !strncmp(polyItem->name, "NORDER_X", 10) && polyItem->data.S32 == 1,
+                "psPolynomial2DtoMetadata:     return correct number of x orders.");
+            polyItem = psMetadataGet(polyMDtemp, 1);
+            ok( !strncmp(polyItem->name, "NORDER_Y", 10) && polyItem->data.S32 == 1,
+                "psPolynomial2DtoMetadata:     return correct number of y orders.");
+            skip_end();
+            polyItem = psMetadataGet(polyMDtemp, 2);
+            skip_start( polyItem == NULL, 2,
+                        "Skipping 2 tests because psPolynomial2DtoMetadata has errors in coeff elements");
+            ok( !strncmp(polyItem->name, "VAL_X00_Y00", 14) &&
+                abs(polyItem->data.F64-1.1) < DBL_EPSILON,
+                "psPolynomial2DtoMetadata:     return correct first element.");
+            polyItem = psMetadataGet(polyMDtemp, PS_LIST_TAIL-1);
+            ok( !strncmp(polyItem->name, "ERR_X01_Y01", 14) &&
+                abs(polyItem->data.F64-0.4) < DBL_EPSILON,
+                "psPolynomial2DtoMetadata:     return correct last element.");
+            skip_end();
+            skip_end();
+        }
+
+        //psPolynomial2DtoMetadata
+        //Return a valid metadata containing a polynomial-metadata structure
+        {
+            note ("example of 2DtoMD using the names not the sequence");
+
+            ok( psPolynomial2DtoMetadata(md, p2d, "polyMD"),
+                "psPolynomial2DtoMetadata:     return true for valid inputs.");
+
+            // psMetadataConfigWrite (md, "test.md");
+
+            // of the components.  these seem like irrelevant tests.
+            psMetadata *polyMDtemp = psMetadataLookupMetadata(NULL, md, "polyMD");
+            skip_start( polyMDtemp == NULL , 1,
+                        "Skipping 1 tests because psPolynomial2DtoMetadata has errors");
+
+            bool status;
+            int nX = psMetadataLookupS32(&status, polyMDtemp, "NORDER_X");
+            ok(status,
+               "psPolynomial2DtoMetadata:     found NORDER_X");
+            ok(nX == 1,
+               "psPolynomial2DtoMetadata:     NORDER_X is 1");
+
+            int nY = psMetadataLookupS32(&status, polyMDtemp, "NORDER_Y");
+            ok(status,
+               "psPolynomial2DtoMetadata:     found NORDER_Y");
+            ok(nY == 1,
+               "psPolynomial2DtoMetadata:     NORDER_Y is 1");
+
+            double val;
+            val = psMetadataLookupF64(&status, polyMDtemp, "VAL_X00_Y00");
+            ok(status,
+               "psPolynomial2DtoMetadata:     found VAL_X00_Y00");
+            is_double(val, 1.1,
+                      "psPolynomial2DtoMetadata:     VAL_X00_Y00 is %lf", val);
+
+            val = psMetadataLookupF64(&status, polyMDtemp, "VAL_X01_Y01");
+            ok(status,
+               "psPolynomial2DtoMetadata:     found VAL_X01_Y01");
+            is_double(val, 4.4,
+                      "psPolynomial2DtoMetadata:     VAL_X01_Y01 is %lf", val);
+
+            skip_end();
+        }
+
+        //Return false for no-name polynomial
+        {
+            ok( !psPolynomial2DtoMetadata(md, p2d, " "),
+                "psPolynomial2DtoMetadata:     return false for no-name.");
+        }
+        //Return false for NULL-name polynomial
+#if 0 // Caught by compiler
+        {
+            ok( !psPolynomial2DtoMetadata(md, p2d, NULL),
+                "psPolynomial2DtoMetadata:     return false for NULL name input.");
+        }
+#endif
+        //Return false for NULL metadata input
+        {
+            ok( !psPolynomial2DtoMetadata(NULL, p2d, "polyMD"),
+                "psPolynomial2DtoMetadata:     return false for NULL metadata input.");
+        }
+        //Return false for NULL polynomial input
+        {
+            ok( !psPolynomial2DtoMetadata(md, NULL, "polyMD"),
+                "psPolynomial2DtoMetadata:     return false for NULL polynomial input.");
+        }
+        //Return true for polynomial with 1 element, a constant
+        {
+            psPolynomial2D *constPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 0, 0);
+            constPoly->coeff[0][0] = 6.66;
+            ok( psPolynomial2DtoMetadata(md, constPoly, "polyMD"),
+                "psPolynomial2DtoMetadata:     return true for constant polynomial (1 element != 0).");
+            psFree(constPoly);
+        }
+        //Return false for non-ordinary polynomial
+        {
+            psPolynomial2D *p2d2 = psPolynomial2DAlloc(PS_POLYNOMIAL_CHEB, 1, 1);
+            p2d2->coeff[0][0] = 1.1;
+            ok( !psPolynomial2DtoMetadata(md, p2d2, "polyMD"),
+                "psPolynomial2DtoMetadata:     return false for chebyshev polynomial");
+            psFree(p2d2);
+        }
+
+        //psPolynomial2DfromMetadata Tests
+        //Return NULL for NULL metadata input.
+        {
+            psPolynomial2D *emptyPoly2D = NULL;
+            emptyPoly2D = psPolynomial2DfromMetadata(NULL);
+            ok( emptyPoly2D == NULL,
+                "psPolynomial2DfromMetadata:   return NULL for NULL metadata input.");
+        }
+        //Get a polynomial from Metadata.
+        psPolynomial2D *outPoly = NULL;
+        psMetadata *polyMDtemp = psMetadataLookupMetadata(NULL, md, "polyMD");
+        outPoly = psPolynomial2DfromMetadata(polyMDtemp);
+        {
+            skip_start( outPoly == NULL, 1,
+                        "Skipping 1 tests because psPolynomial2DfromMetadata has errors.");
+            ok( outPoly->type == PS_POLYNOMIAL_ORD && outPoly->nX == 1 &&
+                outPoly->nY == 1 && abs(outPoly->coeff[0][0] - 1.1) < DBL_EPSILON,
+                "psPolynomial2DfromMetadata:   return correct polynomial from metadata");
+            skip_end();
+        }
+        //Get a polynomial from Metadata with mask[0][0] == 1.
+        psFree(outPoly);
+        outPoly = NULL;
+        psPolynomial2D *maskPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 0, 0);
+        maskPoly->coeff[0][0] = 6.66;
+        maskPoly->coeffMask[0][0] = 1;
+        psMetadata *newmd = psMetadataAlloc();
+        psPolynomial2DtoMetadata(newmd, maskPoly, "polyMD");
+        psMetadata *polyMask = psMetadataLookupMetadata(NULL, newmd, "polyMD");
+        outPoly = psPolynomial2DfromMetadata(polyMask);
+        {
+            ok( outPoly->type == PS_POLYNOMIAL_ORD && outPoly->nX == 0 &&
+                outPoly->nY == 0 && outPoly->coeffMask[0][0] == 1,
+                "psPolynomial2DfromMetadata:   return correct polynomial (w/mask) from metadata");
+        }
+        psFree(maskPoly);
+        psFree(newmd);
+        //Return NULL for wrong NELEMENTS
+        psMetadataItem *nElementItem = psMetadataGet(polyMDtemp, PS_LIST_TAIL);
+        nElementItem->data.S32 -= 1;
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial2DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial2DfromMetadata:   "
+                "return NULL for metadata-polynomial with wrong NELEMENTS");
+        }
+        //Return NULL for missing NELEMENTS
+        psMetadataRemoveIndex(polyMDtemp, PS_LIST_TAIL);
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial2DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial2DfromMetadata:   "
+                "return NULL for metadata-polynomial with no NELEMENTS");
+        }
+
+        //Return NULL for polynomial in metadata with no y-order
+        psMetadataRemoveKey(polyMDtemp, "NORDER_Y");
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial2DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial2DfromMetadata:   "
+                "return NULL for metadata-polynomial with no y-order");
+        }
+        //Return NULL for polynomial in metadata with no x-order
+        psMetadataRemoveKey(polyMDtemp, "NORDER_X");
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial2DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial2DfromMetadata:   "
+                "return NULL for metadata-polynomial with no x-order");
+        }
+
+        psFree(outPoly);
+        psFree(p2d);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psPolynomial3DtoMetadata & psPolynomial3DfromMetadata functions
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psPolynomial3D *p3d = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, 1, 1, 1);
+        p3d->coeff[0][0][0] = 1.1;
+        p3d->coeff[1][0][0] = 2.2;
+        p3d->coeff[0][1][0] = 3.3;
+        p3d->coeff[0][0][1] = 4.4;
+        p3d->coeff[1][1][0] = 5.5;
+        p3d->coeff[1][0][1] = 6.6;
+        p3d->coeff[0][1][1] = 7.7;
+        p3d->coeff[1][1][1] = 8.8;
+        p3d->coeffErr[0][0][0] = 0.1;
+        p3d->coeffErr[1][0][0] = 0.2;
+        p3d->coeffErr[0][1][0] = 0.3;
+        p3d->coeffErr[0][0][1] = 0.4;
+        p3d->coeffErr[1][1][0] = 0.5;
+        p3d->coeffErr[1][0][1] = 0.6;
+        p3d->coeffErr[0][1][1] = 0.7;
+        p3d->coeffErr[1][1][1] = 0.8;
+
+        //psPolynomial3DtoMetadata
+        //Return a valid metadata containing a polynomial-metadata structure
+        {
+            ok( psPolynomial3DtoMetadata(md, p3d, "polyMD"),
+                "psPolynomial3DtoMetadata:     return true for valid inputs.");
+            psMetadata *polyMDtemp = psMetadataLookupMetadata(NULL, md, "polyMD");
+            skip_start( polyMDtemp == NULL , 1,
+                        "Skipping 1 tests because psPolynomial3DtoMetadata has errors");
+            psMetadataItem *polyItem = psMetadataGet(polyMDtemp, 0);
+            skip_start( polyItem == NULL, 2,
+                        "Skipping 3 tests because psPolynomial3DtoMetadata has errors in order elements");
+            ok( !strncmp(polyItem->name, "NORDER_X", 10) && polyItem->data.S32 == 1,
+                "psPolynomial3DtoMetadata:     return correct number of x orders.");
+            polyItem = psMetadataGet(polyMDtemp, 1);
+            ok( !strncmp(polyItem->name, "NORDER_Y", 10) && polyItem->data.S32 == 1,
+                "psPolynomial3DtoMetadata:     return correct number of y orders.");
+            polyItem = psMetadataGet(polyMDtemp, 2);
+            ok( !strncmp(polyItem->name, "NORDER_Z", 10) && polyItem->data.S32 == 1,
+                "psPolynomial3DtoMetadata:     return correct number of z orders.");
+            skip_end();
+            polyItem = psMetadataGet(polyMDtemp, 3);
+            skip_start( polyItem == NULL, 2,
+                        "Skipping 2 tests because psPolynomial3DtoMetadata has errors in coeff elements");
+            ok( !strncmp(polyItem->name, "VAL_X00_Y00_Z00", 14) &&
+                abs(polyItem->data.F64-1.1) < DBL_EPSILON,
+                "psPolynomial3DtoMetadata:     return correct first element.");
+            polyItem = psMetadataGet(polyMDtemp, PS_LIST_TAIL-1);
+            ok( !strncmp(polyItem->name, "ERR_X01_Y01_Z01", 14) &&
+                abs(polyItem->data.F64-0.8) < DBL_EPSILON,
+                "psPolynomial3DtoMetadata:     return correct last element.");
+            skip_end();
+            skip_end();
+        }
+        //Return false for no-name polynomial
+        {
+            ok( !psPolynomial3DtoMetadata(md, p3d, " "),
+                "psPolynomial3DtoMetadata:     return false for no-name.");
+        }
+        //Return false for NULL-name polynomial
+#if 0 // Caught by compiler
+        {
+            ok( !psPolynomial3DtoMetadata(md, p3d, NULL),
+                "psPolynomial3DtoMetadata:     return false for NULL name input.");
+        }
+#endif
+        //Return false for NULL metadata input
+        {
+            ok( !psPolynomial3DtoMetadata(NULL, p3d, "polyMD"),
+                "psPolynomial3DtoMetadata:     return false for NULL metadata input.");
+        }
+        //Return false for NULL polynomial input
+        {
+            ok( !psPolynomial3DtoMetadata(md, NULL, "polyMD"),
+                "psPolynomial3DtoMetadata:     return false for NULL polynomial input.");
+        }
+        /*    //Return false for empty polynomial
+            {
+                psPolynomial3D *emptyPoly = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, 0, 0, 0);
+                ok( !psPolynomial3DtoMetadata(md, emptyPoly, "polyMD"),
+                    "psPolynomial3DtoMetadata:     return false for empty polynomial input.");
+                psFree(emptyPoly);
+            }
+        */
+        //Return true for polynomial with 1 element, a constant
+        {
+            psPolynomial3D *constPoly = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, 0, 0, 0);
+            constPoly->coeff[0][0][0] = 6.66;
+            ok( psPolynomial3DtoMetadata(md, constPoly, "polyMD"),
+                "psPolynomial3DtoMetadata:     return true for constant polynomial (1 element != 0).");
+            psFree(constPoly);
+        }
+        //Return false for non-ordinary polynomial
+        {
+            psPolynomial3D *p3d2 = psPolynomial3DAlloc(PS_POLYNOMIAL_CHEB, 1, 1, 1);
+            p3d2->coeff[0][0][0] = 1.1;
+            ok( !psPolynomial3DtoMetadata(md, p3d2, "polyMD"),
+                "psPolynomial3DtoMetadata:     return false for chebyshev polynomial");
+            psFree(p3d2);
+        }
+
+        //psPolynomial3DfromMetadata Tests
+        //Return NULL for NULL metadata input.
+        {
+            psPolynomial3D *emptyPoly2D = NULL;
+            emptyPoly2D = psPolynomial3DfromMetadata(NULL);
+            ok( emptyPoly2D == NULL,
+                "psPolynomial3DfromMetadata:   return NULL for NULL metadata input.");
+        }
+        //Get a polynomial from Metadata.
+        psPolynomial3D *outPoly = NULL;
+        psMetadata *polyMDtemp = psMetadataLookupMetadata(NULL, md, "polyMD");
+        outPoly = psPolynomial3DfromMetadata(polyMDtemp);
+        {
+            skip_start( outPoly == NULL, 1,
+                        "Skipping 1 tests because psPolynomial3DfromMetadata has errors.");
+            ok( outPoly->type == PS_POLYNOMIAL_ORD && outPoly->nX == 1 &&
+                outPoly->nY == 1 && outPoly->nZ == 1 &&
+                abs(outPoly->coeff[0][0][0] - 1.1) < DBL_EPSILON,
+                "psPolynomial3DfromMetadata:   return correct polynomial from metadata");
+            skip_end();
+        }
+
+        //Get a polynomial from Metadata with mask[0][0][0] == 1.
+        psFree(outPoly);
+        outPoly = NULL;
+        psPolynomial3D *maskPoly = psPolynomial3DAlloc(PS_POLYNOMIAL_ORD, 0, 0, 0);
+        maskPoly->coeff[0][0][0] = 6.66;
+        maskPoly->coeffMask[0][0][0] = 1;
+        psMetadata *newmd = psMetadataAlloc();
+        psPolynomial3DtoMetadata(newmd, maskPoly, "polyMD");
+        psMetadata *polyMask = psMetadataLookupMetadata(NULL, newmd, "polyMD");
+        outPoly = psPolynomial3DfromMetadata(polyMask);
+        {
+            ok( outPoly->type == PS_POLYNOMIAL_ORD && outPoly->nX == 0 && outPoly->nY == 0
+                && outPoly->nZ == 0 && outPoly->coeffMask[0][0][0] == 1,
+                "psPolynomial3DfromMetadata:   return correct polynomial (w/mask) from metadata");
+        }
+        psFree(maskPoly);
+        psFree(newmd);
+        //Return NULL for wrong NELEMENTS
+        psMetadataItem *nElementItem = psMetadataGet(polyMDtemp, PS_LIST_TAIL);
+        nElementItem->data.S32 -= 1;
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial3DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial3DfromMetadata:   "
+                "return NULL for metadata-polynomial with wrong NELEMENTS");
+        }
+        //Return NULL for missing NELEMENTS
+        psMetadataRemoveIndex(polyMDtemp, PS_LIST_TAIL);
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial3DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial3DfromMetadata:   "
+                "return NULL for metadata-polynomial with no NELEMENTS");
+        }
+
+        //Return NULL for polynomial in metadata with no z-order
+        psMetadataRemoveKey(polyMDtemp, "NORDER_Z");
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial3DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial3DfromMetadata:   return NULL for metadata-polynomial with no z-order");
+        }
+        //Return NULL for polynomial in metadata with no y-order
+        psMetadataRemoveKey(polyMDtemp, "NORDER_Y");
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial3DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial3DfromMetadata:   return NULL for metadata-polynomial with no y-order");
+        }
+        //Return NULL for polynomial in metadata with no x-order
+        psMetadataRemoveKey(polyMDtemp, "NORDER_X");
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial3DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial3DfromMetadata:   return NULL for metadata-polynomial with no x-order");
+        }
+
+        psFree(outPoly);
+        psFree(p3d);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psPolynomial4DtoMetadata & psPolynomial4DfromMetadata functions
+    {
+        psMemId id = psMemGetId();
+        psMetadata *md = psMetadataAlloc();
+        psPolynomial4D *p4d = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 1, 1, 1, 1);
+        p4d->coeff[0][0][0][0] = 1.1;
+        p4d->coeff[0][0][0][1] = 2.2;
+        p4d->coeff[0][0][1][0] = 3.3;
+        p4d->coeff[0][1][0][0] = 4.4;
+        p4d->coeff[1][0][0][0] = 5.5;
+
+        p4d->coeff[0][0][1][1] = 6.6;
+        p4d->coeff[0][1][0][1] = 7.7;
+        p4d->coeff[1][0][0][1] = 8.8;
+        p4d->coeff[0][1][1][0] = 9.9;
+        p4d->coeff[1][0][1][0] = 10.10;
+        p4d->coeff[1][1][0][0] = 11.11;
+
+        p4d->coeff[0][1][1][1] = 12.12;
+        p4d->coeff[1][0][1][1] = 13.13;
+        p4d->coeff[1][1][0][1] = 14.14;
+        p4d->coeff[1][1][1][0] = 15.15;
+
+        p4d->coeff[1][1][1][1] = 16.16;
+
+        p4d->coeffErr[0][0][0][0] = 0.1;
+        p4d->coeffErr[0][0][0][1] = 0.2;
+        p4d->coeffErr[0][0][1][0] = 0.3;
+        p4d->coeffErr[0][1][0][0] = 0.4;
+        p4d->coeffErr[1][0][0][0] = 0.5;
+
+        p4d->coeffErr[0][0][1][1] = 0.6;
+        p4d->coeffErr[0][1][0][1] = 0.7;
+        p4d->coeffErr[1][0][0][1] = 0.8;
+        p4d->coeffErr[0][1][1][0] = 0.9;
+        p4d->coeffErr[1][0][1][0] = 0.10;
+        p4d->coeffErr[1][1][0][0] = 0.11;
+
+        p4d->coeffErr[0][1][1][1] = 0.12;
+        p4d->coeffErr[1][0][1][1] = 0.13;
+        p4d->coeffErr[1][1][0][1] = 0.14;
+        p4d->coeffErr[1][1][1][0] = 0.15;
+
+        p4d->coeffErr[1][1][1][1] = 0.16;
+
+        //psPolynomial4DtoMetadata
+        //Return a valid metadata containing a polynomial-metadata structure
+        {
+            ok( psPolynomial4DtoMetadata(md, p4d, "polyMD"),
+                "psPolynomial4DtoMetadata:     return true for valid inputs.");
+            psMetadata *polyMDtemp = psMetadataLookupMetadata(NULL, md, "polyMD");
+            skip_start( polyMDtemp == NULL , 1,
+                        "Skipping 1 tests because psPolynomial4DtoMetadata has errors");
+            psMetadataItem *polyItem = psMetadataGet(polyMDtemp, 0);
+            skip_start( polyItem == NULL, 2,
+                        "Skipping 4 tests because psPolynomial4DtoMetadata has errors in order elements");
+            ok( !strncmp(polyItem->name, "NORDER_X", 10) && polyItem->data.S32 == 1,
+                "psPolynomial4DtoMetadata:     return correct number of x orders.");
+            polyItem = psMetadataGet(polyMDtemp, 1);
+            ok( !strncmp(polyItem->name, "NORDER_Y", 10) && polyItem->data.S32 == 1,
+                "psPolynomial4DtoMetadata:     return correct number of y orders.");
+            polyItem = psMetadataGet(polyMDtemp, 2);
+            ok( !strncmp(polyItem->name, "NORDER_Z", 10) && polyItem->data.S32 == 1,
+                "psPolynomial4DtoMetadata:     return correct number of z orders.");
+            polyItem = psMetadataGet(polyMDtemp, 3);
+            ok( !strncmp(polyItem->name, "NORDER_T", 10) && polyItem->data.S32 == 1,
+                "psPolynomial4DtoMetadata:     return correct number of t orders.");
+            skip_end();
+            polyItem = psMetadataGet(polyMDtemp, 4);
+            skip_start( polyItem == NULL, 2,
+                        "Skipping 2 tests because psPolynomial4DtoMetadata has errors in coeff elements");
+            ok( !strncmp(polyItem->name, "VAL_X00_Y00_Z00_T00", 14) &&
+                abs(polyItem->data.F64-1.1) < DBL_EPSILON,
+                "psPolynomial4DtoMetadata:     return correct first element.");
+            polyItem = psMetadataGet(polyMDtemp, PS_LIST_TAIL-1);
+            ok( !strncmp(polyItem->name, "ERR_X01_Y01_Z01_T01", 14) &&
+                abs(polyItem->data.F64-0.16) < DBL_EPSILON,
+                "psPolynomial4DtoMetadata:     return correct last element.");
+            skip_end();
+            skip_end();
+        }
+        //Return false for no-name polynomial
+        {
+            ok( !psPolynomial4DtoMetadata(md, p4d, " "),
+                "psPolynomial4DtoMetadata:     return false for no-name.");
+        }
+        //Return false for NULL-name polynomial
+#if 0 // Caught by compiler
+        {
+            ok( !psPolynomial4DtoMetadata(md, p4d, NULL),
+                "psPolynomial4DtoMetadata:     return false for NULL name input.");
+        }
+#endif
+        //Return false for NULL metadata input
+        {
+            ok( !psPolynomial4DtoMetadata(NULL, p4d, "polyMD"),
+                "psPolynomial4DtoMetadata:     return false for NULL metadata input.");
+        }
+        //Return false for NULL polynomial input
+        {
+            ok( !psPolynomial4DtoMetadata(md, NULL, "polyMD"),
+                "psPolynomial4DtoMetadata:     return false for NULL polynomial input.");
+        }
+        /*    //Return false for empty polynomial
+            {
+                psPolynomial4D *emptyPoly = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 0, 0, 0, 0);
+                ok( !psPolynomial4DtoMetadata(md, emptyPoly, "polyMD"),
+                    "psPolynomial4DtoMetadata:     return false for empty polynomial input.");
+                psFree(emptyPoly);
+            }
+        */
+        //Return true for polynomial with 1 element, a constant
+        {
+            psPolynomial4D *constPoly = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 0, 0, 0, 0);
+            constPoly->coeff[0][0][0][0] = 6.66;
+            ok( psPolynomial4DtoMetadata(md, constPoly, "polyMD"),
+                "psPolynomial4DtoMetadata:     return true for constant polynomial (1 element != 0).");
+            psFree(constPoly);
+        }
+        //Return false for non-ordinary polynomial
+        {
+            psPolynomial4D *p4d2 = psPolynomial4DAlloc(PS_POLYNOMIAL_CHEB, 1, 1, 1, 1);
+            p4d2->coeff[0][0][0][0] = 1.1;
+            ok( !psPolynomial4DtoMetadata(md, p4d2, "polyMD"),
+                "psPolynomial4DtoMetadata:     return false for chebyshev polynomial");
+            psFree(p4d2);
+        }
+
+        //psPolynomial4DfromMetadata Tests
+        //Return NULL for NULL metadata input.
+        {
+            psPolynomial4D *emptyPoly2D = NULL;
+            emptyPoly2D = psPolynomial4DfromMetadata(NULL);
+            ok( emptyPoly2D == NULL,
+                "psPolynomial4DfromMetadata:   return NULL for NULL metadata input.");
+        }
+        //Get a polynomial from Metadata.
+        psPolynomial4D *outPoly = NULL;
+        psMetadata *polyMDtemp = psMetadataLookupMetadata(NULL, md, "polyMD");
+        outPoly = psPolynomial4DfromMetadata(polyMDtemp);
+        {
+            skip_start( outPoly == NULL, 1,
+                        "Skipping 1 tests because psPolynomial4DfromMetadata has errors.");
+            ok( outPoly->type == PS_POLYNOMIAL_ORD && outPoly->nX == 1 &&
+                outPoly->nY == 1 && outPoly->nZ == 1 && outPoly->nT == 1 &&
+                abs(outPoly->coeff[0][0][0][0] - 1.1) < DBL_EPSILON,
+                "psPolynomial4DfromMetadata:   return correct polynomial from metadata");
+            skip_end();
+        }
+        //Get a polynomial from Metadata with mask[0][0][0][0] == 1.
+        psFree(outPoly);
+        outPoly = NULL;
+        psPolynomial4D *maskPoly = psPolynomial4DAlloc(PS_POLYNOMIAL_ORD, 0, 0, 0, 0);
+        maskPoly->coeff[0][0][0][0] = 6.66;
+        maskPoly->coeffMask[0][0][0][0] = 1;
+        psMetadata *newmd = psMetadataAlloc();
+        psPolynomial4DtoMetadata(newmd, maskPoly, "polyMD");
+        psMetadata *polyMask = psMetadataLookupMetadata(NULL, newmd, "polyMD");
+        outPoly = psPolynomial4DfromMetadata(polyMask);
+        {
+            ok( outPoly->type == PS_POLYNOMIAL_ORD && outPoly->nX == 0 && outPoly->nY == 0
+                && outPoly->nZ == 0 && outPoly->nT == 0 && outPoly->coeffMask[0][0][0][0] == 1,
+                "psPolynomial4DfromMetadata:   return correct polynomial (w/mask) from metadata");
+        }
+        psFree(maskPoly);
+        psFree(newmd);
+        //Return NULL for wrong NELEMENTS
+        psMetadataItem *nElementItem = psMetadataGet(polyMDtemp, PS_LIST_TAIL);
+        nElementItem->data.S32 -= 1;
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial4DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial4DfromMetadata:   "
+                "return NULL for metadata-polynomial with wrong NELEMENTS");
+        }
+        //Return NULL for missing NELEMENTS
+        psMetadataRemoveIndex(polyMDtemp, PS_LIST_TAIL);
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial4DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial4DfromMetadata:   "
+                "return NULL for metadata-polynomial with no NELEMENTS");
+        }
+
+        //Return NULL for polynomial in metadata with no t-order
+        psMetadataRemoveKey(polyMDtemp, "NORDER_T");
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial4DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial4DfromMetadata:   return NULL for metadata-polynomial with no t-order");
+        }
+        //Return NULL for polynomial in metadata with no z-order
+        psMetadataRemoveKey(polyMDtemp, "NORDER_Z");
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial4DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial4DfromMetadata:   return NULL for metadata-polynomial with no z-order");
+        }
+        //Return NULL for polynomial in metadata with no y-order
+        psMetadataRemoveKey(polyMDtemp, "NORDER_Y");
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial4DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial4DfromMetadata:   return NULL for metadata-polynomial with no y-order");
+        }
+        //Return NULL for polynomial in metadata with no x-order
+        psMetadataRemoveKey(polyMDtemp, "NORDER_X");
+        psFree(outPoly);
+        outPoly = NULL;
+        outPoly = psPolynomial4DfromMetadata(polyMDtemp);
+        {
+            ok( outPoly == NULL,
+                "psPolynomial4DfromMetadata:   return NULL for metadata-polynomial with no x-order");
+        }
+
+        psFree(outPoly);
+        psFree(p4d);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
+
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_printing.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_printing.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psMetadata_printing.c	(revision 22322)
@@ -0,0 +1,388 @@
+/**
+ *  C Implementation: tap_psMetadata_printing
+ *
+ * Description:  Tests for psMetadataPrint and psMetadataItemPrint.
+ *
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <fcntl.h>
+#include <unistd.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+static psMetadata *setupMeta(void)
+{
+    psMetadata *md = NULL;
+    psMetadata *newMD = NULL;
+    int i = 0;
+    md = psMetadataAlloc();
+    newMD = psMetadataAlloc();
+
+    psMetadataAddStr(md, PS_LIST_TAIL, "item5", 0, "I am a string", "GNIRTS");
+    psMetadataAddBool(md, PS_LIST_TAIL, "item1", 0, "I am a boolean", true);
+    psMetadataAddBool(md, PS_LIST_TAIL, "item11", 0, "I am a boolean", false);
+    psMetadataAddS8(md, PS_LIST_TAIL, "item6", 0, "I am S8", 6);
+    psMetadataAddS16(md, PS_LIST_TAIL, "item7", 0, "I am S16", -666);
+    psMetadataAddS32(md, PS_LIST_TAIL, "item2", 0, "I am a integer", 55);
+    psMetadataAddS64(md, PS_LIST_TAIL, "helloS64", 0, "I am S64", 666);
+    psMetadataAddU8(md, PS_LIST_TAIL, "item8", 0, "I am U8", 6);
+    psMetadataAddU16(md, PS_LIST_TAIL, "item9", 0, "I am U16", 666);
+    psMetadataAddU32(md, PS_LIST_TAIL, "item10", 0, "I am U32", 666);
+    psMetadataAddU64(md, PS_LIST_TAIL, "item12", 0, "I am U64", 666);
+    psMetadataAddF32(md, PS_LIST_TAIL, "item3", 0, NULL, 3.14);
+    psMetadataAddF64(md, PS_LIST_TAIL, "item4", 0, "", 6.28);
+
+    psMetadataAddS32(newMD, PS_LIST_TAIL, "ITEM01", 0, NULL, 666);
+    psMetadata *newestMD = NULL;
+    newestMD = psMetadataAlloc();
+    psVector *vec = NULL;
+    vec = psVectorAlloc(60, PS_DATA_S32);
+    for (i = 0; i < 5; i++) {
+        vec->data.S32[i] = i+1;
+    }
+    vec->n = 5;
+    psMetadataAddVector(newestMD, PS_LIST_TAIL, "VECTORNEW", 0, "Newest VECTOR", vec);
+    psMetadataAddStr(newestMD, PS_LIST_TAIL, "cell", 0, "I am a p-Star string", "pStArRs");
+    psMetadataAddMetadata(newMD, PS_LIST_TAIL, "META NEW", 0, "I AM Newest METADATA", newestMD);
+    psMetadataAddF32(newMD, PS_LIST_TAIL, "ITEM02", 0, "I AM FLOAT", 666.6);
+    psMetadataAddF64(newMD, PS_LIST_TAIL, "ITEM03", 0, "I AM DOUBLE", 666.666);
+    psMetadataAddS64(newMD, PS_LIST_TAIL, "item666", 0, "I am S64", 666);
+
+    psMetadataAddMetadata(md, PS_LIST_TAIL, "metadata7", 0, "I am a metadata", newMD);
+    psRegion *region = psRegionAlloc(0.0, 2.0, 0.0, 2.0);
+    psMetadataAddPtr(md, PS_LIST_TAIL, "region", PS_DATA_REGION, "I am a region", region);
+    psList *list = psListAlloc(NULL);
+    psMetadataAddList(md, PS_LIST_TAIL, "list01", 0, "I am a list", list);
+
+    psTime *time;
+    time = psTimeAlloc(PS_TIME_TAI);
+    time->sec = 1000;
+    time->nsec = 25;
+    time->leapsecond = true;
+    psMetadataAddTime(md, PS_LIST_TAIL, "time01", 0, "I am time", time);
+
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector6", 0, "I am a vector", vec);
+    psFree(vec);
+    vec = psVectorAlloc(2, PS_DATA_U8);
+    vec->n = 2;
+    for (i = 0; i < 2; i++) {
+        vec->data.U8[i] = i+1;
+    }
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector7", 0, "I am a U8-vector", vec);
+    psFree(vec);
+    vec = psVectorAlloc(2, PS_DATA_U16);
+    vec->n = 2;
+    for (i = 0; i < 2; i++) {
+        vec->data.U16[i] = i+1;
+    }
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector8", 0, "I am a U16-vector", vec);
+    psFree(vec);
+    vec = psVectorAlloc(2, PS_DATA_U32);
+    vec->n = 2;
+    for (i = 0; i < 2; i++) {
+        vec->data.U32[i] = i+1;
+    }
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector9", 0, "I am a U32-vector", vec);
+    psFree(vec);
+    vec = psVectorAlloc(2, PS_DATA_U64);
+    vec->n = 2;
+    for (i = 0; i < 2; i++) {
+        vec->data.U64[i] = i+1;
+    }
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector10", 0, "I am a U64-vector", vec);
+    psFree(vec);
+    vec = psVectorAlloc(2, PS_DATA_S8);
+    vec->n = 2;
+    for (i = 0; i < 2; i++) {
+        vec->data.S8[i] = i+1;
+    }
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector11", 0, "I am a S8-vector", vec);
+    psFree(vec);
+    vec = psVectorAlloc(2, PS_DATA_S16);
+    vec->n = 2;
+    for (i = 0; i < 2; i++) {
+        vec->data.S16[i] = i+1;
+    }
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector12", 0, "I am a S16-vector", vec);
+    psFree(vec);
+    vec = psVectorAlloc(2, PS_DATA_S64);
+    vec->n = 2;
+    for (i = 0; i < 2; i++) {
+        vec->data.S64[i] = i+1;
+    }
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector13", 0, "I am a S64-vector", vec);
+    psFree(vec);
+    vec = psVectorAlloc(2, PS_DATA_F32);
+    vec->n = 2;
+    for (i = 0; i < 2; i++) {
+        vec->data.F32[i] = i+1;
+    }
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector14", 0, "I am a F32-vector", vec);
+    psFree(vec);
+    vec = psVectorAlloc(2, PS_DATA_F64);
+    vec->n = 2;
+    for (i = 0; i < 2; i++) {
+        vec->data.F64[i] = i+1;
+    }
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector15", 0, "I am a F64-vector", vec);
+
+    /*    psImage *image = psImageAlloc(1, 1, PS_TYPE_S32);
+        psImageSet(image, 0, 0, 1);
+        psMetadataAddImage(md, PS_LIST_TAIL, "image1", 0, "I am an Image", image);
+
+        psHash *hash = psHashAlloc(1);
+        psHashAdd(hash, "hash", image);
+        psMetadataAddHash(md, PS_LIST_TAIL, "hash1", 0, "I am a Hash", hash);
+
+        psLookupTable *lookup = psLookupTableAlloc("table2.dat", "%s", 0);
+        psMetadataAddLookupTable(md, PS_LIST_TAIL, "lookup", 0, "I am a LookupTable", lookup);
+
+        psArray *array = psArrayAlloc(1);
+        psArraySet(array, 0, image);
+        psMetadataAddArray(md, PS_LIST_TAIL, "array", 0, "I am an Array", array);
+
+        psFree(array);
+        psFree(lookup);
+        psFree(image);
+        psFree(hash);
+    */
+    psFree(region);
+    psFree(list);
+    psFree(time);
+    psFree(newestMD);
+    psFree(vec);
+    psFree(newMD);
+    return md;
+}
+
+
+int main(void)
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(30);
+
+
+    // psMetadataPrint
+    {
+        psMemId id = psMemGetId();
+        FILE *fd = fopen("psMetadataPrint.out", "w+");
+        FILE *fd_read = fopen("Makefile", "r");
+        psMetadata *md = NULL;
+        psMetadata *md2 = NULL;
+        md = setupMeta();
+        md2 = psMetadataAlloc();
+        psSphere *sphere = psSphereAlloc();
+        psMetadataAddPtr(md2, PS_LIST_HEAD, "ptr", PS_DATA_SPHERE, "", sphere);
+        psMetadataAddUnknown(md2, PS_LIST_TAIL, "unknown", 0, "", sphere);
+    
+        psImage *image = psImageAlloc(1, 1, PS_TYPE_S32);
+        psImageSet(image, 0, 0, 1);
+        psMetadataAddImage(md2, PS_LIST_TAIL, "image1", 0, "I am an Image", image);
+    
+        psHash *hash = psHashAlloc(1);
+        psHashAdd(hash, "hash", image);
+        psMetadataAddHash(md2, PS_LIST_TAIL, "hash1", 0, "I am a Hash", hash);
+    
+        psLookupTable *lookup = psLookupTableAlloc("table2.dat", "%s", 0);
+        psMetadataAddLookupTable(md2, PS_LIST_TAIL, "lookup", 0, "I am a LookupTable", lookup);
+    
+        psArray *array = psArrayAlloc(1);
+        psArraySet(array, 0, image);
+        psMetadataAddArray(md2, PS_LIST_TAIL, "array", 0, "I am an Array", array);
+    
+        psFree(array);
+        psFree(lookup);
+        psFree(image);
+        psFree(hash);
+    
+        //Return false for NULL psMetadata input
+        {
+            ok( !psMetadataPrint(NULL, NULL, 0),
+                "psMetadataPrint:            return false for NULL psMetadata input.");
+        }
+        //Return false for read-only file descriptor
+        {
+            ok ( !psMetadataPrint(fd_read, md, 0),
+                 "psMetadataPrint:            return false for read-only file descriptor.");
+        }
+        fclose(fd_read);
+        //Error and then return true for unprintable datatype (sphere) and DATA_UNKNOWN
+        {
+            ok ( !psMetadataPrint(NULL, md2, 0),
+                 "psMetadataPrint:            return false for metadata with unknown datatype.");
+        }
+        psFree(md2);
+    
+        //Valid case where fd is NULL -> stdout
+        md2 = psMetadataAlloc();
+        psMetadataAddStr(md2, PS_LIST_HEAD,
+                         "  >>Hello Wally", 0, "My name is Bubbles", "Pet Me");
+        {
+            ok ( psMetadataPrint(NULL, md2, 0),
+                 "psMetadataPrint:            return true for NULL file pointer.");
+        }
+        psFree(md2);
+        //Valid case where fd is "psMetadataPrint.out"
+        {
+            ok ( psMetadataPrint(fd, md, 0),
+                 "psMetadataPrint:            return true for file='psMetadataPrint.out'.");
+        }
+    
+        //Check for Memory Leaks before exit
+        fclose(fd);
+        remove("psMetadataPrint.out");
+        psFree(sphere);
+        psFree(md);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psMetadataItemPrint
+    {
+        psMemId id = psMemGetId();
+        FILE *fd;
+        fd = fopen("psMetadataItemPrint.out", "w");
+        psMetadataItem *item1 = psMetadataItemAlloc("bool", PS_DATA_BOOL, "No Comment", true);
+        psMetadataItem *item2 = psMetadataItemAlloc("f32", PS_DATA_F32, "No Comment", 0.66601);
+        psMetadataItem *item3 = psMetadataItemAlloc("string", PS_DATA_STRING, "", "Can't touch this");
+        psMetadataItem *item4 = psMetadataItemAlloc("U16", PS_DATA_U16, "", 1);
+        psMetadataItem *item5 = psMetadataItemAlloc("string", PS_DATA_U8, "", "a");
+    
+        //Return false for NULL file-pointer
+        {
+            ok( !psMetadataItemPrint(NULL, "%s", item1),
+                "psMetadataItemPrint:        return false for NULL FILE*.");
+        }
+        //Return false for NULL format paramter
+        {
+            ok( !psMetadataItemPrint(fd, NULL, item1),
+                "psMetadataItemPrint:        return false for NULL format.");
+        }
+        //Return false for NULL metadataItem
+        {
+            ok( !psMetadataItemPrint(fd, "%s", NULL),
+                "psMetadataItemPrint:        return false for NULL psMetadataItem.");
+        }
+        //Return false for incorrect format parameter
+        {
+            ok( !psMetadataItemPrint(fd, "sos \n", item1),
+                "psMetadataItemPrint:       return false for incorrect format.");
+        }
+        //Return false for type that doesn't match format (ie, %s for f32 value)
+        {
+            ok( !psMetadataItemPrint(fd, "%s", item2),
+                "psMetadataItemPrint:       return false for item that doesn't match format.");
+        }
+    
+        //Return false for incorrect format parameter
+        {
+            ok( !psMetadataItemPrint(fd, "%z", item1),
+                "psMetadataItemPrint:       return false for incorrect format.");
+        }
+    
+    
+        //Return true for format parameter containing additional modifiers (F32)
+        {
+            ok( psMetadataItemPrint(fd, "%.3f\n", item2),
+                "psMetadataItemPrint:       return true for format with .3 modifier.");
+        }
+        //Return true for valid string input
+        {
+            ok( psMetadataItemPrint(fd, "%s\n", item3),
+                "psMetadataItemPrint:       return true for valid string case w/s.");
+        }
+        //Return true for valid ptr input
+        {
+            ok( psMetadataItemPrint(fd, "%p\n", item3),
+                "psMetadataItemPrint:       return true for valid ptr case w/p.");
+        }
+    
+        //Return true for format = %d, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%d\n", item4),
+                "psMetadataItemPrint:       return true for valid case w/d.");
+        }
+        //Return true for format = %i, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%i\n", item4),
+                "psMetadataItemPrint:       return true for valid case w/i.");
+        }
+        //Return true for format = %c, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%c\n", item5),
+                "psMetadataItemPrint:       return true for valid case w/c.");
+        }
+        //Return true for format = %o, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%o\n", item4),
+                "psMetadataItemPrint:       return true for valid case w/d.");
+        }
+        //Return true for format = %u, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%u\n", item4),
+                "psMetadataItemPrint:       return true for valid case w/i.");
+        }
+        //Return true for format = %x, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%x\n", item5),
+                "psMetadataItemPrint:       return true for valid case w/c.");
+        }
+        //Return true for format = %X, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%X\n", item5),
+                "psMetadataItemPrint:       return true for valid case w/c.");
+        }
+    
+        //Return true for format = %e, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%.1e\n", item2),
+                "psMetadataItemPrint:       return true for valid case w/e.");
+        }
+        //Return true for format = %E, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%.2E\n", item2),
+                "psMetadataItemPrint:       return true for valid case w/E.");
+        }
+        //Return true for format = %F, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%.3F\n", item2),
+                "psMetadataItemPrint:       return true for valid case w/F.");
+        }
+        //Return true for format = %g, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%.3g\n", item2),
+                "psMetadataItemPrint:       return true for valid case w/g.");
+        }
+        //Return true for format = %G, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%.4G\n", item2),
+                "psMetadataItemPrint:       return true for valid case w/G.");
+        }
+        //Return true for format = %a, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%.4a\n", item2),
+                "psMetadataItemPrint:       return true for valid case w/a.");
+        }
+        //Return true for format = %A, type = int
+        {
+            ok( psMetadataItemPrint(fd, "%A\n", item2),
+                "psMetadataItemPrint:       return true for valid case w/A.");
+        }
+    
+        fclose(fd);
+        unlink ("psMetadataItemPrint.out");
+        psFree(item1);
+        psFree(item2);
+        psFree(item3);
+        psFree(item4);
+        psFree(item5);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psPixels_all.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psPixels_all.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psPixels_all.c	(revision 22322)
@@ -0,0 +1,400 @@
+/**
+ *  C Implementation: tap_psPixels_all
+ *
+ * Description:  Tests for psPixelsAlloc, psPixelsRealloc, psMemCheckPixels,
+ *               psPixelsCopy, psPixels(To/From)Mask, p_psPixelsAdd, psPixelsLength,
+ *               psPixels(Set/Get), psPixelsConcatenate
+ *
+ *
+ * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
+ *
+ * Copyright: See COPYING file that comes with this distribution
+ *
+ */
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+void testPixelsCreate(void);
+void testPixelsManip(void);
+
+int main(void)
+{
+    plan_tests(48);
+
+    //Tests for psPixels Functions
+    testPixelsCreate();
+    testPixelsManip();
+
+    done();
+}
+
+void testPixelsCreate(void)
+{
+    //Test 1:  psPixels Creation Fxns
+    //Return allocated psPixels of length 0 with NULL data.
+    psPixels* p0 = psPixelsAlloc(0);
+    {
+        ok(p0 != NULL && p0->n == 0 && p0->nalloc == 0,
+           "psPixelsAlloc:         return newly allocated psPixels of 0 length.");
+    }
+    //Return properly allocated psPixels
+    psPixels* p1 = psPixelsAlloc(1);
+    {
+        ok(p1 != NULL && p1->n == 1 && p1->data != NULL && p1->nalloc == 1,
+           "psPixelsAlloc:         return newly allocated psPixels of 1 length.");
+    }
+    //Make sure psMemCheckPixels works correctly - return true
+    {
+        ok(psMemCheckPixels(p1),
+            "psMemCheckPixels:      return true for psPixels input.");
+    }
+    //Make sure psMemCheckPixels works correctly - return false
+    // XXX EAM : disabled -- failing test causes segfault
+    if (0) {
+        int j = 2;
+        ok(!psMemCheckPixels(&j),
+            "psMemCheckPixels:      return false for non-psPixels input.");
+    }
+    //Return a 0-length allocated psPixels on Realloc with 0
+    {
+        psPixels *noPix = psPixelsAlloc(1);
+        noPix = psPixelsRealloc(noPix, 0);
+        ok(noPix->n == 0 && noPix->nalloc == 0,
+            "psPixelsRealloc:       return re-allocated psPixels of 0 length.");
+        psFree(noPix);
+    }
+    //Return a properly down-sized psPixels.
+    p0 = psPixelsAdd(p0, 1, 1.1, 1.2);
+    p0 = psPixelsAdd(p0, 1, 2.1, 2.2);
+    p0 = psPixelsAdd(p0, 1, 3.1, 3.2);
+    p0 = psPixelsAdd(p0, 0, 4.1, 4.2);
+    {
+        skip_start(  psPixelsLength(p0) != 4, 1,
+                     "Skipping 1 tests because psPixels input is of wrong size! =%ld", p0->n);
+        p0 = psPixelsRealloc(p0, 2);
+        ok(p0->n == 2 && fabs(p0->data[0].x - 1.1) < FLT_EPSILON && p0->nalloc == 2,
+            "psPixelsRealloc:       return properly down-sized psPixels.");
+        skip_end();
+    }
+    //Now see if we can create a valid psPixels copy of p0
+    {
+        psPixels *noPix = NULL;
+        noPix = psPixelsCopy(noPix, p0);
+        ok(noPix->n == 2 && fabs(noPix->data[0].x - 1.1) < FLT_EPSILON && noPix->nalloc == 2,
+            "psPixelsCopy:          return properly copied psPixels.");
+        psFree(noPix);
+    }
+    //Return NULL for attempting to create a psPixels copy of NULL input.
+    {
+        psPixels *noPix = NULL;
+        noPix = psPixelsCopy(noPix, NULL);
+        ok(noPix == NULL,
+            "psPixelsCopy:          return NULL for NULL psPixels input.");
+    }
+
+    //Check for Memory leaks
+    {
+        psFree(p1);
+        psFree(p0);
+        checkMem();
+    }
+}
+
+void testPixelsManip(void)
+{
+    //psPixels Manipulation Fxns
+    //Tests for psPixelsSet
+    psPixels *p0 = psPixelsAlloc(1);
+    psPixelCoord in;
+    in.x = 1.1;
+    in.y = 1.2;
+    //Return false for NULL pixels input
+    {
+        ok(!psPixelsSet(NULL, 0, in),
+            "psPixelsSet:          return false for NULL pixels input.");
+    }
+    //Return true for valid inputs
+    {
+        ok(psPixelsSet(p0, 0, in),
+            "psPixelsSet:          return true for valid inputs.");
+    }
+    //Return false for position == nalloc
+    {
+        ok(!psPixelsSet(p0, 1, in),
+            "psPixelsSet:          return false for position == nalloc.");
+    }
+    //Return false for out-of-range position
+    {
+        ok(!psPixelsSet(p0, 2, in),
+            "psPixelsSet:          return false for out-of-range position.");
+    }
+    //Return false for negative out-of-range position
+    {
+        ok(!psPixelsSet(p0, -2, in),
+            "psPixelsSet:          return false for negative out-of-range position.");
+    }
+    //Return true for valid negative position
+    {
+        ok(psPixelsSet(p0, -1, in),
+            "psPixelsSet:          return true for valid negative position.");
+    }
+
+    //Tests for psPixelsGet
+    psPixelCoord out;
+    //Return NAN's for NULL pixels input
+    {
+        out = psPixelsGet(NULL, 1);
+        ok(isnan(out.x) &&  isnan(out.y),
+            "psPixelsGet:          return NANs for NULL pixels input.");
+    }
+    //Return NAN's for out-of-range position
+    {
+        out = psPixelsGet(p0, 2);
+        ok(isnan(out.x) &&  isnan(out.y),
+            "psPixelsGet:          return NANs for out-of-range position.");
+    }
+    //Return NAN's for out-of-range negative position
+    {
+        out = psPixelsGet(p0, -2);
+        ok(isnan(out.x) &&  isnan(out.y),
+            "psPixelsGet:          return NANs for out-of-range negative position.");
+    }
+    //Return true for valid inputs
+    {
+        out = psPixelsGet(p0, 0);
+        ok(fabs(out.x - 1.1) < FLT_EPSILON &&  fabs(out.y - 1.2) < FLT_EPSILON,
+            "psPixelsGet:          return correct values for valid inputs.");
+    }
+    //Return true for valid negative position
+    {
+        out = psPixelsGet(p0, -1);
+        ok(fabs(out.x - 1.1) < FLT_EPSILON &&  fabs(out.y - 1.2) < FLT_EPSILON,
+            "psPixelsGet:          return correct values for valid negative position.");
+    }
+
+    //p_psPixelsPrint Tests
+    //Return true for valid inputs
+    {
+        FILE *newFD = fopen("psPixels.out", "w+");
+        ok(p_psPixelsPrint(newFD, p0, "PS-PIXELS"),
+             "p_psPixelsPrint:      return true for valid input.");
+        fflush(NULL);
+        fclose(newFD);
+    }
+    //Return false for invalid file
+    {
+        FILE *dummy = fopen("psPixels.out", "r");
+        ok(!p_psPixelsPrint(dummy, p0, "failed test"),
+             "p_psPixelsPrint:      return false for invalid file input.");
+        fclose(dummy);
+        remove
+            ("psPixels.out");
+    }
+    //Return false for NULL pixels and name inputs
+    {
+        ok(!p_psPixelsPrint(stdout, NULL, NULL),
+             "p_psPixelsPrint:      return false for NULL pixels and name inputs.");
+    }
+    //Return true for empty pixel data.
+    psPixels *p1 = psPixelsAlloc(0);
+    {
+        ok(p_psPixelsPrint(NULL, p1, "noPix"),
+             "p_psPixelsPrint:      return true for NULL file and empty pixel data inputs.");
+    }
+
+
+    //----------------------------------------------------------------------
+    //psPixelsToMask Tests
+    psImage *outImage = NULL;
+    psRegion region;
+    region.x0 = 1.0;
+    region.x1 = -2.0;
+    region.y0 = 1.0;
+    region.y1 = 5.0;
+    psMaskType maskVal = 1;
+    //Return NULL for NULL pixels input
+    {
+        psMemId id = psMemGetId();
+        outImage = psPixelsToMask(outImage, NULL, region, maskVal);
+        ok(outImage == NULL, "psPixelsToMask: return NULL for NULL pixels input.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for empty pixel data
+    {
+        psMemId id = psMemGetId();
+        outImage = psPixelsToMask(outImage, p1, region, maskVal);
+        ok(outImage == NULL, "psPixelsToMask: return NULL for NULL pixels data input.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for bad region input - negative value
+    {
+        psMemId id = psMemGetId();
+        outImage = psPixelsToMask(outImage, p0, region, maskVal);
+        ok(outImage == NULL, "psPixelsToMask: return NULL for negative value in region.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for bad region input - upper bound less than lower bound
+    region.x0 = 3.0;
+    region.x1 = 1.0;
+    {
+        psMemId id = psMemGetId();
+        outImage = psPixelsToMask(outImage, p0, region, maskVal);
+        ok(outImage == NULL, "psPixelsToMask: return NULL for bad region input.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return NULL for un-recyclable image - out->type.dimen != PS_DIMEN_IMAGE
+    region.x0 = 1.0;
+    region.x1 = 3.0;
+    outImage = psImageAlloc(1, 1, PS_TYPE_S32);
+    *(psDimen*)&(outImage->type.dimen) = PS_DIMEN_OTHER;
+    {
+        psMemId id = psMemGetId();
+        outImage = psPixelsToMask(outImage, p0, region, maskVal);
+        ok(outImage == NULL, "psPixelsToMask: return NULL for bad image input.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return valid image for valid inputs
+    //XXX: Musr check pixel values
+    p0 = psPixelsAdd(p0, 1, 2.1, 2.2);
+    p0 = psPixelsAdd(p0, 1, 3.1, 3.2);
+    p0 = psPixelsAdd(p0, 1, 1.0, 1.0);
+    {
+        psMemId id = psMemGetId();
+        outImage = psPixelsToMask(outImage, p0, region, maskVal);
+        ok(outImage != NULL, "psPixelsToMask: return valid image for valid input.");
+        psFree(outImage);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //----------------------------------------------------------------------
+    //psPixelsFromMask Tests
+    outImage = psImageAlloc(31, 99, PS_TYPE_MASK);
+    for (int i = 0; i < outImage->numCols; i++) {
+        for (int j = 0; j < outImage->numRows; j++) {
+            outImage->data.PS_TYPE_MASK_DATA[i][j] = 1;
+        }
+    }
+    //Return valid psPixels for valid inputs
+    //XXX: We never check output psPixels
+    {
+        psMemId id = psMemGetId();
+        psPixels *outPixels = NULL;
+        outPixels = psPixelsFromMask(outPixels, outImage, maskVal);
+        ok(outPixels != NULL, "psPixelsFromMask: return valid pixels for valid input.");
+        psFree(outPixels);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return NULL for NULL image input
+    {
+        psMemId id = psMemGetId();
+        psPixels *outPixels = NULL;
+        outPixels = psPixelsFromMask(outPixels, NULL, maskVal);
+        ok(outPixels == NULL, "psPixelsFromMask: return NULL for NULL image input.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Return NULL for image with wrong maskType
+    {
+        *(psElemType*)&(outImage->type.type) = PS_TYPE_U32;
+        psMemId id = psMemGetId();
+        psPixels *outPixels = NULL;
+        outPixels = psPixelsFromMask(outPixels, outImage, maskVal);
+        ok(outPixels == NULL, "psPixelsFromMask: return NULL for image with wrong maskType.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    //----------------------------------------------------------------------
+    // psPixelsConcatenate Tests
+    // Return NULL for NULL pixels input
+    {
+        psMemId id = psMemGetId();
+        psPixels *outPixels = NULL;
+        outPixels = psPixelsConcatenate(outPixels, NULL);
+        ok(outPixels == NULL, "psPixelsConcatenate: return NULL for NULL pixels input.");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Return copy of input pixels for NULL out pixels
+    {
+        psMemId id = psMemGetId();
+        psPixels *outPixels = NULL;
+        outPixels = psPixelsFromMask(outPixels, outImage, maskVal);
+        outPixels = psPixelsConcatenate(outPixels, p0);
+        ok(outPixels->n == 4 && fabs(outPixels->data[0].x - 1.1) < FLT_EPSILON &&
+           fabs(outPixels->data[0].y - 1.2) < FLT_EPSILON ,
+          "psPixelsConcatenate:  return copy of input pixels for NULL out input.");
+        psFree(outPixels);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //test psPixelsConcatenate()
+    //Return properly concatenated psPixels list
+    {
+        psMemId id = psMemGetId();
+        psPixels *outPixels = psPixelsAlloc(6);
+        in.x = 1.0;
+        in.y = 1.0;
+        psPixelsSet(outPixels, 0, in);  //1, 1
+        psPixelsSet(outPixels, 1, in);  //1, 1
+        in.y = 2.0;
+        psPixelsSet(outPixels, 2, in);  //1, 2
+        in.y = 1.0;
+        psPixelsSet(outPixels, 3, in);  //1, 1
+        in.x = 2.0;
+        psPixelsSet(outPixels, 4, in);  //2, 1
+        in.y = 2.0;
+        psPixelsSet(outPixels, 5, in);  //2, 2
+        psPixels *testPixels = psPixelsAlloc(7);
+        in.x = 1.0;
+        in.y = 1.0;
+        psPixelsSet(testPixels, 0, in);  //1, 1
+        in.y = 2.0;
+        psPixelsSet(testPixels, 1, in);  //1, 2
+        in.y = 1.0;
+        psPixelsSet(testPixels, 2, in);  //1, 1
+        in.x = 2.0;
+        psPixelsSet(testPixels, 3, in);  //2, 1
+        in.y = 2.0;
+        psPixelsSet(testPixels, 4, in);  //2, 2
+        in.x = 1.0;
+        psPixelsSet(testPixels, 5, in);  //1, 2
+        in.x = 5.0;
+        in.y = 3.0;
+        psPixelsSet(testPixels, 6, in);  //5, 3
+        outPixels = psPixelsConcatenate(outPixels, testPixels);
+        outPixels = psPixelsDuplicates(outPixels, outPixels);
+        // Should be 5 entries: (1,1) (2,1) (1,2) (2,2) (5,3)
+        ok(outPixels->n == 5, "psPixelsConcatenate:  return properly concatenate pixel list for valid inputs.");
+        psFree(testPixels);
+        psFree(outPixels);
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Check for Memory leaks
+    //XXX: Remove this, add memory checks to individual blocks
+    {
+        psFree(outImage);
+        psFree(p1);
+        psFree(p0);
+        checkMem();
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psTree.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psTree.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tap_psTree.c	(revision 22322)
@@ -0,0 +1,72 @@
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM 10000                       // Number of points
+
+int main(int argc, char *argv[])
+{
+    psLibInit(NULL);
+    plan_tests(6);
+
+    {
+        psMemId id = psMemGetId();
+
+        psVector *x = psVectorAlloc(NUM, PS_TYPE_F64);
+        psVector *y = psVectorAlloc(NUM, PS_TYPE_F64);
+
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        for (int i = 0; i < NUM; i++) {
+            x->data.F64[i] = 2.0 * psRandomUniform(rng) - 1.0;
+            y->data.F64[i] = 2.0 * psRandomUniform(rng) - 1.0;
+        }
+        psFree(rng);
+
+        psTree *tree = psTreePlant(2, 2, x, y);
+
+        ok(tree, "Tree planted");
+        skip_start(!tree, 4, "tree died");
+        {
+            //            psTreePrint(stderr, tree);
+
+            psVector *coords = psVectorAlloc(2, PS_TYPE_F64);
+            psVectorInit(coords, 0);
+
+            long closeIndex = psTreeNearest(tree, coords);
+            psFree(coords);
+            ok(closeIndex >= 0 && closeIndex < tree->numNodes, "found point: %ld", closeIndex);
+
+            long bestIndex = -1;
+            double bestDist = INFINITY;
+            for (int i = 0; i < NUM; i++) {
+                double dist = PS_SQR(x->data.F64[i]) + PS_SQR(y->data.F64[i]);
+                if (dist < bestDist) {
+                    bestIndex = i;
+                    bestDist = dist;
+                }
+            }
+            ok(bestIndex == closeIndex, "correct point: %ld vs %ld", closeIndex, bestIndex);
+
+            psVector *closest = psTreeCoords(NULL, tree, closeIndex);
+            ok(closest, "got coords: %lf,%lf", closest->data.F64[0], closest->data.F64[1]);
+            ok(closest->data.F64[0] == x->data.F64[bestIndex] &&
+               closest->data.F64[1] == y->data.F64[bestIndex],
+               "correst coords: %lf,%lf(%lf) vs %lf,%lf(%lf)",
+               closest->data.F64[0], closest->data.F64[1],
+               sqrt(PS_SQR(closest->data.F64[0]) + PS_SQR(closest->data.F64[1])),
+               x->data.F64[bestIndex], y->data.F64[bestIndex],
+               sqrt(PS_SQR(x->data.F64[bestIndex]) + PS_SQR(y->data.F64[bestIndex])));
+            psFree(closest);
+        }
+        skip_end();
+
+        psFree(tree);
+        psFree(x);
+        psFree(y);
+
+        ok(!psMemCheckLeaks(id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psLibFinalize();
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psArguments.c	(revision 22322)
@@ -0,0 +1,81 @@
+/** @file  tst_psArguments.c
+*
+*  @brief Test driver for psArguments functions
+*
+*  @author  David Robbins, MHPCC
+*
+*  @version $Revision: 1.4 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2006-09-13 02:20:15 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+
+#include "pslib.h"
+
+static psS32 testArgument(void);
+/*
+testDescription tests[] = {
+                              {testArgument, 666, "Test psArgument fxns", 0, false},
+                              {NULL}
+                          };
+*/
+int main(int argc, char* argv[])
+{
+    psLogSetLevel( PS_LOG_INFO );
+    testArgument();
+    //    if( !runTestSuite(stderr,"psArguments",tests,argc,argv)) {
+    //        return 1;
+    //    }
+    return 0;
+}
+
+psS32 testArgument(void)
+{
+    char *argv[6];
+    argv[0] = "./program";
+    argv[1] = "-string";
+    argv[2] = "new";
+    argv[3] = "-float";
+    argv[4] = "6.66";
+    argv[5] = "-vvv";
+    int argc = 6;
+
+    int i = psArgumentGet(argc, argv, "-float");
+    if ( i != 0 ) {
+        if ( !psArgumentRemove(i, &argc, argv) ) {
+            printf("\n Failed to remove float from argument list\n");
+        }
+    } else {
+        printf("\nFailed to find string in argument list\n");
+        return 1;
+    }
+    psArgumentRemove(i, &argc, argv);
+    printf("\n Argument %d has been removed", i);
+    int log = psArgumentVerbosity(&argc, argv);
+    printf("\nLog level = %d \n", log);
+
+    psMetadata *args = psMetadataAlloc();
+    psMetadataAdd(args, PS_LIST_TAIL, "-string", PS_DATA_STRING, "Test String", "SomeString");
+    psMetadataAdd(args, PS_LIST_TAIL, "-float", PS_DATA_F32, "Test Float", 0.0);
+    psMetadata *ints = psMetadataAlloc();
+    psMetadataAdd(ints, PS_LIST_TAIL, "int1", PS_DATA_S32, "Int1", 1);
+    psMetadataAdd(ints, PS_LIST_TAIL, "int2", PS_DATA_S32, "Int2", 2);
+    psMetadataAdd(args, PS_LIST_TAIL, "-int", PS_DATA_METADATA, "Integers", ints);
+    psFree(ints);                       // Drop reference
+
+    printf("\nThis Should print the Argument Help list.\n\n");
+    psArgumentHelp(args);
+
+    if ( !psArgumentParse(args, &argc, argv) || argc != 1 ) {
+        psArgumentHelp(args);
+        psFree(args);
+        return 2;
+    }
+
+    psMetadataPrint(NULL, args, 4);
+
+    psFree(args);
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psArray.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psArray.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psArray.c	(revision 22322)
@@ -0,0 +1,533 @@
+/** @file  tst_psArray.c
+ *
+ *  @brief Test driver for psArray integer functions
+ *
+ *  This test driver contains the following tests for psArray test point 1:
+ *     A)  Create void pointer array
+ *     B)  Add data to void pointer array
+ *     C)  Reallocate void pointer array bigger
+ *     D)  Reallocate void pointer array smaller
+ *     E)  Reallocate with null pointer
+ *     F)  Remove item from array
+ *     G)  Remove invalid item from array
+ *     H)  Remove item from null array
+ *     I)  Remove null item from array
+ *     J)  Free void pointer array
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.7 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-03-06 22:34:25 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <math.h>
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+typedef struct
+{
+    psS32 x;
+    float y;
+}
+testStruct;
+
+static int testStructCompare(const void **a, const void **b)
+{
+    testStruct* first = (testStruct*)*a;
+    testStruct* second = (testStruct*)*b;
+
+    if(first->x < second->x)
+        return -1;
+    else if (first->x > second->x)
+        return 1;
+    else
+        return 0;
+}
+
+static psS32 testArray( void );
+static psS32 testArray01( void );
+static psS32 testArrayAdd( void );
+static psS32 testArrayGetSet( void );
+static psS32 testArrayLength( void );
+
+testDescription tests[] = {
+                              {testArray, 665, "psArray", 0, false},
+                              {testArray01, 666, "psArray01", 0, false},
+                              {testArrayAdd, 667, "psArrayAdd", 0, false},
+                              {testArrayGetSet, 668, "psArrayGetSet", 0, false},
+                              {testArrayLength, 669, "psArrayLength", 0, false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psArray", tests, argc, argv ) );
+}
+
+
+psS32 testArray(void)
+{
+    // Create array of pointers
+    testStruct *mySt[10];
+    psArray *psArr1 = NULL;
+
+    // Test A - Create void pointer array
+    printPositiveTestHeader(stderr,"psArray", "Create void pointer array");
+    psArray *psArr = psArrayAlloc(5);
+    if (psArr->nalloc != 5) {
+        psError(PS_ERR_UNKNOWN, true,"psArray didn't have proper number of elements.");
+        return 1;
+    }
+    printFooter(stderr, "psArray", "Create void pointer array", true);
+
+
+    // Test B - Add data to void pointer array
+    printPositiveTestHeader(stderr, "psArray", "Add data to void pointer array");
+    for(psS32 i = 0; i < 5; i++) {
+        testStruct *ts = psAlloc(sizeof(testStruct));
+        ts->x = 10*i;
+        ts->y = 10.1*i;
+        mySt[i] = ts;
+        //        psArr->data[i] = ts;
+        if (!psArraySet(psArr, psArr->n, (psPtr)ts) ) {
+            return 6;
+        }
+        psMemIncrRefCounter(ts);
+        psFree(ts);
+    }
+
+    for(psS32 i = 0; i < 5; i++) {
+        testStruct *ts = (testStruct*)psArr->data[i];
+        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
+        if (fabsf(ts->x - 10*i) > 0.01f || fabsf(ts->y - 10.1*i) > 0.01f) {
+            psError(PS_ERR_UNKNOWN, true,"Couldn't properly get elements from array.");
+            return 2;
+        }
+    }
+    fprintf(stderr,"array size = %ld\n", psArr->nalloc);
+    if (psArr->nalloc != 5) {
+        psError(PS_ERR_UNKNOWN, true,"Array Size wrong");
+        return 3;
+    }
+    fprintf(stderr,"array population = %ld\n", psArr->n);
+    if (psArr->n != 5) {
+        psError(PS_ERR_UNKNOWN, true,"Array population wrong");
+        return 4;
+    }
+    printFooter(stderr, "psArray", "Add data to void pointer array", true);
+
+
+    // Test C - Reallocate void pointer array bigger
+    printPositiveTestHeader(stderr,"psArray", "Reallocate void pointer array bigger");
+    psArr = psArrayRealloc(psArr,10);
+    fprintf(stderr,"Adding more elements to void pointer array...\n");
+    for(psS32 i = 5; i < 10; i++) {
+        testStruct *ts = psAlloc(sizeof(testStruct));
+        ts->x = 10*i;
+        ts->y = 10.1*i;
+        mySt[i] = ts;
+        psArr->data[i] = ts;
+        psArr->n++;
+        psMemIncrRefCounter(ts);
+    }
+    for(psS32 i = 0; i < 10; i++) {
+        testStruct *ts = (testStruct*)psArr->data[i];
+        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
+        if (fabsf(ts->x - 10*i) > 0.01f || fabsf(ts->y - 10.1*i) > 0.01f) {
+            psError(PS_ERR_UNKNOWN, true,"Couldn't properly get elements from array.");
+            return 5;
+        }
+    }
+    fprintf(stderr,"array size = %ld\n", psArr->nalloc);
+    if (psArr->nalloc != 10) {
+        psError(PS_ERR_UNKNOWN, true,"Array Size wrong");
+        return 6;
+    }
+    fprintf(stderr,"array population = %ld\n", psArr->n);
+    if (psArr->n != 10) {
+        psError(PS_ERR_UNKNOWN, true,"Array Population wrong");
+        return 7;
+    }
+    printFooter(stderr, "psArray", "Reallocate void pointer array bigger", true);
+
+    // Test D - Reallocate void pointer array smaller
+    printPositiveTestHeader(stderr,"psArray","Reallocate void pointer array smaller");
+    psArr = psArrayRealloc(psArr,3);
+    for(psS32 i = 0; i < 3; i++) {
+        testStruct *ts = (testStruct*)psArr->data[i];
+        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
+        if (fabsf(ts->x - 10*i) > 0.01f || fabsf(ts->y - 10.1*i) > 0.01f) {
+            psError(PS_ERR_UNKNOWN, true,"Couldn't properly get elements from array.");
+            return 8;
+        }
+    }
+    fprintf(stderr,"array size = %ld\n", psArr->nalloc);
+    if (psArr->nalloc != 3) {
+        psError(PS_ERR_UNKNOWN, true,"Array Size wrong");
+        return 9;
+    }
+    fprintf(stderr,"array population = %ld\n", psArr->n);
+    if (psArr->n != 3) {
+        psError(PS_ERR_UNKNOWN, true,"Array Population wrong");
+        return 10;
+    }
+    printFooter(stderr, "psArray", "Reallocate integer void pointer smaller", true);
+
+    // Test E - Reallocate with a null
+    printNegativeTestHeader(stderr,"psArray","Reallocate with a null array",
+                            "Error message generator", 0 );
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
+    psArr1 = psArrayRealloc(NULL,5);
+    if ( psArr1 != NULL ) {
+        fprintf(stderr,"ERROR: Return is not NULL\n");
+    }
+    printFooter(stderr,"psArray","Reallocate with a null array",true);
+
+
+    // Test F - Remove item from array
+    printPositiveTestHeader(stderr, "psArray", "Remove valid item");
+    if( !psArrayRemove(psArr,mySt[0]) ) {
+        psError(PS_ERR_UNKNOWN,true,"Unable to remove valid item");
+        return 100;
+    }
+    fprintf(stderr,"Array size after removal = %ld\n",psArr->n);
+    if ( psArr->n != 2 ) {
+        psError(PS_ERR_UNKNOWN, true, "Items in array not decremented after removal");
+        return 101;
+    }
+    for(psS32 i = 0; i < psArr->n; i++) {
+        testStruct *ts = (testStruct*)psArr->data[i];
+        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
+        if(fabsf(ts->x - 10*(i+1)) > 0.01f || fabsf(ts->y - 10.1*(i+1)) > 0.01f) {
+            psError(PS_ERR_UNKNOWN, true, "Elements not as expected after remove.");
+            return 102;
+        }
+    }
+    printFooter(stderr,"psArray","Remove valid item", true);
+
+    // Test G - Remove invalid item from array
+    printPositiveTestHeader(stderr, "psArray", "Remove invalid item from array");
+    if( psArrayRemove(psArr,mySt[9]) ) {
+        psError(PS_ERR_UNKNOWN,true,"Removed invalid item from array");
+        return 103;
+    }
+    printFooter(stderr,"psArray","Remove invalid item from array",true);
+
+    // Test H - Remove item from null array
+    printPositiveTestHeader(stderr, "psArray", "Remove item from null array");
+    if ( psArrayRemove(NULL,mySt[1]) ) {
+        psError(PS_ERR_UNKNOWN,true,"Removed valid item from null array");
+        return 104;
+    }
+    printFooter(stderr,"psArray","Remove item from null array",true);
+
+    // Test I - Remove null item from array
+    printPositiveTestHeader(stderr, "psArray", "Remove null item from array");
+    if( psArrayRemove(psArr,NULL) ) {
+        psError(PS_ERR_UNKNOWN,true,"Remove null item from array",true);
+        return 105;
+    }
+    printFooter(stderr,"psArray","Remove null item from array",true);
+
+    // Test J - Free void pointer array
+    printPositiveTestHeader(stderr, "psArray", "Free void pointer array");
+    psFree(psArr);
+    for(psS32 i = 0; i < 10; i++) {
+        psFree(mySt[i]);
+    }
+
+    printFooter(stderr, "psArray" ,"Free void pointer array", true);
+
+    return 0;
+}
+
+psS32 testArray01(void)
+{
+    // Create array of pointers
+    testStruct *mySt[10];
+
+    // Test A - Create void pointer array
+    printPositiveTestHeader(stderr,"psArray", "Create void pointer array");
+    psArray *psArr = psArrayAlloc(10);
+    if (psArr->nalloc != 10) {
+        psError(PS_ERR_UNKNOWN, true,"psArray didn't have proper number of elements.");
+        return 1;
+    }
+    printFooter(stderr, "psArray", "Create void pointer array", true);
+
+    // Test B - Add data to void pointer array
+    printPositiveTestHeader(stderr, "psArray", "Add data to void pointer array");
+    for(psS32 i = 0; i < 10; i++) {
+        testStruct *ts = psAlloc(sizeof(testStruct));
+        ts->x = 10*(10-i);
+        ts->y = 10.1*(10-i);
+        mySt[i] = ts;
+        //        psArr->data[i] = ts;
+        if (!psArraySet(psArr, psArr->n, (psPtr)ts) ) {
+            return 6;
+        }
+        psMemIncrRefCounter(ts);
+        psFree(ts);
+    }
+
+    for(psS32 i = 0; i < 10; i++) {
+        testStruct *ts = (testStruct*)psArr->data[i];
+        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
+        if (fabsf(ts->x - 10*(10-i)) > 0.01f || fabsf(ts->y - 10.1*(10-i)) > 0.01f) {
+            psError(PS_ERR_UNKNOWN, true,"Couldn't properly get elements from array.");
+            return 2;
+        }
+    }
+    fprintf(stderr,"array size = %ld\n", psArr->nalloc);
+    if (psArr->nalloc != 10) {
+        psError(PS_ERR_UNKNOWN, true,"Array Size wrong");
+        return 3;
+    }
+    fprintf(stderr,"array population = %ld\n", psArr->n);
+    if (psArr->n != 10) {
+        psError(PS_ERR_UNKNOWN, true,"Array population wrong");
+        return 4;
+    }
+    printFooter(stderr, "psArray", "Add data to void pointer array", true);
+
+    // Test C - Sort data in array
+    printPositiveTestHeader(stderr,"psArray","Sort data in array");
+    psArr = psArraySort(psArr,testStructCompare);
+    for(psS32 i = 0; i < 10; i++) {
+        testStruct *ts = (testStruct*)psArr->data[i];
+        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
+        if (fabsf(ts->x - 10*(i+1)) > 0.01f || fabsf(ts->y - 10.1*(i+1)) > 0.01f) {
+            psError(PS_ERR_UNKNOWN, true,"Couldn't properly get elements from array.");
+            return 5;
+        }
+    }
+    printFooter(stderr,"psArray","Sort data in array",true);
+
+    // Test D - Attempt to sort null array
+    printPositiveTestHeader(stderr,"psArray","Attempt to sort array");
+    psArray* tempArr = psArraySort(NULL,testStructCompare);
+    if(tempArr != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Array sort did not return null when sorting null array");
+        return 6;
+    }
+    printFooter(stderr,"psArray","Attempt to sort array",true);
+
+    // Test E - Free void pointer array
+    printPositiveTestHeader(stderr, "psArray", "Free void pointer array");
+    psFree(psArr);
+    for(psS32 i = 0; i < 10; i++) {
+        psFree(mySt[i]);
+    }
+    if( psMemCheckLeaks(0, NULL, stderr, false) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Memory leaks detected.");
+        return 110;
+    }
+    psS32 nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        fprintf(stderr,"ERROR: Found %d bad memory blocks\n", nBad);
+        return 111;
+    }
+    printFooter(stderr, "psArray" ,"Free void pointer array", true);
+
+    return 0;
+}
+
+psS32 testArrayAdd( void )
+{
+    int subtest = 0;
+    float* data;
+    int nalloc = 5;
+    int n = 0;
+    int delta = 5;
+
+    // allocate the array.
+    psArray* arr = psArrayAlloc(nalloc);
+    arr->n = n;
+
+    // test arrayAdd until n == nalloc
+    while (n < nalloc) {
+        data = psAlloc(sizeof(float));
+        arr = psArrayAdd(arr, delta*2, data); // make delta unique versus next delta used.
+
+        subtest++;
+        if (psMemGetRefCounter(data) != 2) {
+            // in response of Bug #302
+            psError(PS_ERR_UNKNOWN, true,
+                    "psArrayAdd did not increment the data reference count.");
+            return subtest;
+        }
+
+        subtest++;
+        if (arr->nalloc != nalloc) {
+            psError(PS_ERR_UNKNOWN,true,
+                    "psArrayAdd expanded the psArray unnecessarily.  n=%d",
+                    n);
+            return subtest;
+        }
+
+        subtest++;
+        if (arr->n != ++n) {
+            psError(PS_ERR_UNKNOWN,true,
+                    "psArrayAdd did not increment the size of the psArray. n=%d",
+                    n);
+            return subtest;
+        }
+
+        subtest++;
+        if (arr->data[n-1] != data) {
+            psError(PS_ERR_UNKNOWN,true,
+                    "psArrayAdd didn't set the element to data. n=%d",
+                    n);
+            return subtest;
+        }
+
+        psFree(data);
+    }
+
+    // now try to add an element when the array is full.
+    data = psAlloc(sizeof(float));
+    arr = psArrayAdd(arr, delta, data);
+
+    // make sure the array was expanded
+    subtest++;
+    if (arr->nalloc != nalloc+delta) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psArrayAdd did not expand the psArray when it was already full."
+                " old nalloc=%d, nalloc=%d, delta=%d",
+                nalloc, arr->nalloc, delta);
+        return subtest;
+    }
+    nalloc = arr->nalloc;
+
+    subtest++;
+    if (arr->n != ++n) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psArrayAdd did not increment psArray.n by 1 after expanding it.");
+        return subtest;
+    }
+
+    subtest++;
+    if (arr->data[n-1] != data) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psArrayAdd didn't set the second element to data.");
+        return subtest;
+    }
+
+    psFree(data);
+
+    // make the array full again (operation tested already)
+    while (arr->n < arr->nalloc) {
+        data = psAlloc(sizeof(float));
+        arr = psArrayAdd(arr, 0, data);
+        psFree(data);
+    }
+    nalloc = arr->nalloc;
+    n = arr->n;
+
+    // now add to full array with delta = 0; verify that the array is
+    // expanded by 10
+    data = psAlloc(sizeof(float));
+    arr = psArrayAdd(arr, 0, data);
+    psFree(data);
+
+    subtest++;
+    if (arr->nalloc != nalloc+10) {
+        psError(PS_ERR_UNKNOWN,true,
+                "psArrayAdd did not expand the psArray by 10 when delta < 1."
+                " old nalloc=%d, nalloc=%d",
+                nalloc, arr->nalloc);
+        return subtest;
+    }
+
+    psFree(arr);
+
+    return 0;  // the value that indicates success is part of the testDescription
+}
+
+psS32 testArrayGetSet( void )
+{
+    psArray *test;
+    int *p1 = psAlloc(sizeof(int));
+    int *p2 = psAlloc(sizeof(int));
+    int *p3 = psAlloc(sizeof(int));
+    test = psArrayAlloc(5);
+    test->n = 0;
+    *p1 = 10;
+    *p2 = 4;
+    *p3 = 666;
+
+    if ( !psArraySet(test, 0, p1) )
+        fprintf(stderr, "ArraySet failed to set S32 at position 0\n");
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psArraySet(test, 10, p1) )
+        fprintf(stderr, "ArraySet Improperly set S32 at out of range position\n");
+    if ( !psArraySet(test, 1, p2) )
+        fprintf(stderr, "ArraySet Failed to set S32 at position 1\n");
+    if ( psArrayGet(test, 0) != p1 )
+        fprintf(stderr, "ArrayGet Failed to return the correct S32 from position 0\n");
+    if ( psArrayGet(test, -1) != p2)
+        fprintf(stderr, "ArrayGet Failed to return the correct S32 from tail using -1\n");
+    //    psFree(p1); // free ref from psArrayGet
+    //    psFree(p2); // free ref from psArrayGet
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psArraySet(test, -6, p3) )
+        fprintf(stderr, "ArraySet failed to fail using an out of range negative number\n");
+
+    psFree(test);
+    psFree(p1);
+    psFree(p2);
+    psFree(p3);
+
+    return 0;
+}
+
+psS32 testArrayLength( void )
+{
+    psArray *array = psArrayAlloc(5);
+
+    if (psArrayLength(array) != 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psArrayLength failed to return the correct length of array.\n");
+        return 1;
+    }
+    array->n = 5;
+    if (psArrayLength(array) != 5) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psArrayLength failed to return the correct length of array.\n");
+        return 2;
+    }
+    array->n++;
+    if (psArrayLength(array) != 6) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psArrayLength failed to return the correct length of array.\n");
+        return 3;
+    }
+    psFree(array);
+
+    psArray *emptyArray = NULL;
+    psVector *vector = psVectorAlloc(5, PS_TYPE_F32);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psArrayLength(emptyArray) != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psArrayLength failed to return -1 for a NULL input array.\n");
+        return 4;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psArrayLength((psArray*)vector) != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psArrayLength failed to return -1 for an invalid input array.\n");
+        return 5;
+    }
+    psFree(vector);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psBitSet.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psBitSet.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psBitSet.c	(revision 22322)
@@ -0,0 +1,681 @@
+/** @file  tst_psBitSet_01.c
+ *
+ *  @brief Test driver for psBitSet functions
+ *
+ *  This test driver contains the following tests for psBitSet test point 1:
+ *     A)  Create psBitSet
+ *     B)  Set bits
+ *     C)  Test bits
+ *     D)  Attempt to test negative bit
+ *     E)  Attempt to test bit to large
+ *     F)  Attempt to test bit in null BitSet
+ *     G)  Attempt to set negative bit
+ *     H)  Attempt to set bit to large
+ *     I)  Attempt to set bit in null BitSet
+ *     J)  Free psBitSet
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2007-02-07 23:52:54 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testBitSet01a(void);
+static psS32 testBitSet01b(void);
+static psS32 testBitSet01c(void);
+static psS32 testBitSet02(void);
+static psS32 testBitSet03(void);
+static psS32 testBitSet04(void);
+static psS32 testBitSet05(void);
+static psS32 testBitSet06(void);
+
+testDescription tests[] = {
+                              {testBitSet01a, 1, "psBitSetAlloc", 0, false},
+                              {testBitSet01b, 2, "psBitSetSet/psBitSetClear", 0, false},
+                              {testBitSet01c, 3, "psBitSetTest", 0, false},
+                              {testBitSet06, 4, "psBitSetOp", 0, false},
+                              {testBitSet02, 5, "psBitSetOp AND Operator", 0, false},
+                              {testBitSet03, 6, "psBitSetOp OR Operator", 0, false},
+                              {testBitSet04, 7, "psBitSetOp XOR Operator", 0, false},
+                              {testBitSet05, 8, "psBitSetNot", 0, false},
+
+                              {NULL}
+                          };
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psBitSet", tests, argc, argv ) );
+}
+
+psS32 testBitSet01a(void)
+{
+    psErr* err;
+
+    // Test A - Create psBitSet
+    fprintf(stderr,"Creating psBitSet with 24 bits...\n");
+    psBitSet* bs = psBitSetAlloc(24);
+    if (bs == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Failed to create a psBitSet");
+        return 1;
+    }
+    if (bs->n != 3) {
+        psError(PS_ERR_UNKNOWN,true,"Number of bytes for psBitSet incorrect (%ld vs 3)",
+                bs->n);
+        return 2;
+    }
+    if (bs->bits[0] != 0 || bs->bits[1] != 0 || bs->bits[2] != 0) {
+        psError(PS_ERR_UNKNOWN,true,"psBitSetAlloc didn't clean ou the bits by default (%x%x%x).",
+                bs->bits[2],bs->bits[1],bs->bits[0]);
+        return 3;
+    }
+    psFree(bs);
+
+    // Test A - Create psBitSet
+    fprintf(stderr,"Creating psBitSet with 25 bits...\n");
+    bs = psBitSetAlloc(25);
+    if (bs == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Failed to create a psBitSet with 25 bits");
+        return 4;
+    }
+    if (bs->n != 4) {
+        psError(PS_ERR_UNKNOWN,true,"Number of bytes for psBitSet incorrect (%ld vs 4)",bs->n);
+        return 5;
+    }
+    psFree(bs);
+
+    psErrorClear();
+    psLogMsg(__func__,PS_LOG_INFO,"Following is an error.");
+    bs = psBitSetAlloc(-4);
+    if (bs != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psBitSetAlloc returned something in case of a negative size");
+        return 6;
+    }
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psErrorStackPrint(stderr,"Error Stack:");
+        psError(PS_ERR_UNKNOWN,true,"psBitSetAlloc didn't generate expected error with size = -4");
+        return 7;
+    }
+    psFree(err);
+
+    return 0;
+}
+
+psS32 testBitSet01b(void)
+{
+    char *binOut = NULL;
+    psBitSet *tempBs = NULL;
+    psErr* err = NULL;
+
+    psBitSet* bs = psBitSetAlloc(24);
+
+    if (psBitSetTest(bs,0) ||
+            psBitSetTest(bs,2) ||
+            psBitSetTest(bs,23) ) {
+
+        psAbort("psBitSetAlloc failed to clear all bits at allocation.");
+    }
+
+    // Test B - Set bits
+    tempBs = bs;
+    fprintf(stderr,"Setting first bit...\n");
+    bs = psBitSetSet(bs, 0);
+    fprintf(stderr,"Setting third bit...\n");
+    bs = psBitSetSet(bs, 2);
+    fprintf(stderr,"Setting last bit...\n");
+    bs = psBitSetSet(bs, 23);
+    if(bs != tempBs) {
+        psAbort(                "Return pointer not equal to output argument pointer.");
+    }
+    if(bs->bits[0] != 0x05) {
+        psAbort(                "Unexpected value for first byte (%d vs 5).",
+                bs->bits[0]);
+    }
+
+
+    binOut = psBitSetToString(bs);
+    fprintf(stderr,"%s\n\n", binOut);
+    psFree(binOut);
+
+    // Test C - Test bits
+    if (! psBitSetTest(bs,0) ||
+            ! psBitSetTest(bs,2) ||
+            ! psBitSetTest(bs,23) ) {
+
+        psAbort("Failed to set a bit.");
+    }
+
+    fprintf(stderr,"Clearing first bit...\n");
+    bs = psBitSetClear(bs, 0);
+    fprintf(stderr,"Clearing third bit...\n");
+    bs = psBitSetClear(bs, 2);
+    fprintf(stderr,"Clearing last bit...\n");
+    bs = psBitSetClear(bs, 23);
+
+    binOut = psBitSetToString(bs);
+    fprintf(stderr,"%s\n\n", binOut);
+    psFree(binOut);
+
+
+    if (psBitSetTest(bs,0) ||
+            psBitSetTest(bs,2) ||
+            psBitSetTest(bs,23) ) {
+
+        psAbort("Failed to clear a bit.");
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
+    if(psBitSetClear(NULL,2) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psBitSetClear did not return NULL with NULL bitset");
+        return 20;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
+    if(psBitSetClear(bs,-3) != bs) {
+        psError(PS_ERR_UNKNOWN,true,"psBitSetClear did not return original bitset");
+        return 21;
+    }
+
+    psLogMsg("testBitSet01b",PS_LOG_INFO,"Following should be an error");
+    psErrorClear();
+    psBitSetSet(bs, -4);
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psErrorStackPrint(stderr,"Error Stack:");
+        psAbort("psBitSetSet(bs, -4) didn't generate expected error.\n");
+    }
+    psFree(err);
+
+    psLogMsg("testBitSet01b",PS_LOG_INFO,"Following should be an error");
+    psErrorClear();
+    psBitSetSet(bs, 200);
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psErrorStackPrint(stderr,"Error Stack:");
+        psAbort("psBitSetSet(bs, 200) didn't generate expected error.\n");
+    }
+    psFree(err);
+
+    psLogMsg("testBitSet01b",PS_LOG_INFO,"Following should be an error");
+    psErrorClear();
+    psBitSetSet(NULL, 0);
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psErrorStackPrint(stderr,"Error Stack:");
+        psAbort("psBitSetSet(NULL,0) didn't generate expected error.\n");
+    }
+    psFree(err);
+
+    psFree(bs);
+
+    return 0;
+}
+
+static psS32 testBitSet01c(void)
+{
+    psBitSet* bs = psBitSetAlloc(24);
+    psErr* err = NULL;
+
+    fprintf(stderr,"Setting first bit...\n");
+    bs = psBitSetSet(bs, 0);
+    fprintf(stderr,"Setting third bit...\n");
+    bs = psBitSetSet(bs, 2);
+    fprintf(stderr,"Setting last bit...\n");
+    bs = psBitSetSet(bs, 23);
+
+    if (! psBitSetTest(bs,0) ||
+            ! psBitSetTest(bs,2) ||
+            ! psBitSetTest(bs,23) ) {
+
+        psAbort("Set bits returned false.");
+    }
+
+    psLogMsg("testBitSet01c",PS_LOG_INFO,"Following should be an error");
+    psErrorClear();
+    if(psBitSetTest(bs, -4)) {
+        psAbort("psBitSetTest returned true with negative bit position.\n");
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psErrorStackPrint(stderr,"Error Stack:");
+        psAbort("psBitSetTest(bs, -4) didn't generate proper error.\n");
+    }
+    psFree(err);
+
+    psLogMsg("testBitSet01c",PS_LOG_INFO,"Following should be an error");
+    psErrorClear();
+    if(psBitSetTest(bs, 200)) {
+        psAbort("psBitSetTest returned true with too-large bit position.\n");
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psErrorStackPrint(stderr,"Error Stack:");
+        psAbort("psBitSetTest(bs, 200) didn't generate proper error.\n");
+    }
+    psFree(err);
+
+    psLogMsg("testBitSet01c",PS_LOG_INFO,"Following should be an error");
+    psErrorClear();
+    if (psBitSetTest(NULL, 0)) {
+        psAbort("psBitSetTest returned true with NULL psBitSet.\n");
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
+        psErrorStackPrint(stderr,"Error Stack:");
+        psAbort("psBitSetTest(NULL, 0) didn't generate proper error.\n");
+    }
+    psFree(err);
+
+    psFree(bs);
+
+    return 0;
+}
+
+psS32 testBitSet02()
+{
+    char *binOut1 = NULL;
+    char *binOut2 = NULL;
+    char *binOut3 = NULL;
+
+    psBitSet* bs1 = psBitSetAlloc(24);
+    psBitSet* bs2 = psBitSetAlloc(24);
+    psBitSet* and = psBitSetAlloc(24);
+    for(psS32 i=0; i<24; i++) {
+        if ((i & 2) == 0) {
+            bs1 = psBitSetSet(bs1, i);
+        }
+        if ((i & 1) == 0) {
+            bs2 = psBitSetSet(bs2, i);
+        }
+        if (((i & 1) == 0) && ((i & 2) == 0) ) {
+            and = psBitSetSet(and, i);
+        }
+    }
+    binOut1 = psBitSetToString(bs1);
+    binOut2 = psBitSetToString(bs2);
+    binOut3 = psBitSetToString(and);
+    fprintf(stderr,"psBitSetOp input is %s, %s.  Truth is %s.\n",
+            binOut1,binOut2,binOut3);
+    psFree(binOut1);
+    psFree(binOut2);
+    psFree(binOut3);
+
+    // Test B - Perform binary AND with psBitSets
+    psLogMsg(__func__,PS_LOG_INFO,"Perform binary AND with psBitSets");
+    psBitSet* outbs = psBitSetAlloc(24);
+    outbs = psBitSetOp(outbs, bs1, "AND", bs2);
+    if (outbs == NULL) {
+        psAbort("psBitSetOp returned a NULL result for AND operation");
+    }
+
+    for(psS32 i=0; i<24; i++) {
+        psBool truth = psBitSetTest(and,i);
+        psBool res = psBitSetTest(outbs,i);
+        if ( res != truth) {
+            binOut1 = psBitSetToString(bs1);
+            binOut2 = psBitSetToString(bs2);
+            binOut3 = psBitSetToString(outbs);
+            psAbort("psBitSetOp with AND operator failed.\nInput was %s, %s.  Output was %s",
+                    binOut1,binOut2,binOut3);
+        }
+    }
+    psFree(outbs);
+
+    // Test C - Perform binary AND and auto allocate output
+    psLogMsg(__func__,PS_LOG_INFO,"Perform binary AND and auto allocate output");
+    outbs = psBitSetOp(NULL, bs1, "AND", bs2);
+    if (outbs == NULL) {
+        psAbort("psBitSetOp failed to create a new psBitSet for the result");
+    }
+    for(psS32 i=0; i<24; i++) {
+        psBool truth = psBitSetTest(and,i);
+        psBool res = psBitSetTest(outbs,i);
+        if ( res != truth) {
+            binOut1 = psBitSetToString(bs1);
+            binOut2 = psBitSetToString(bs2);
+            binOut3 = psBitSetToString(outbs);
+            psAbort("psBitSetOp with AND operator failed.\nInput was %s, %s.  Output was %s",
+                    binOut1,binOut2,binOut3);
+        }
+    }
+    psFree(outbs);
+
+    psFree(bs1);
+    psFree(bs2);
+    psFree(and);
+
+    return 0;
+}
+
+static psS32 testBitSet03(void)
+{
+    char *binOut1 = NULL;
+    char *binOut2 = NULL;
+    char *binOut3 = NULL;
+
+    psBitSet* bs1 = psBitSetAlloc(24);
+    psBitSet* bs2 = psBitSetAlloc(24);
+    psBitSet* or = psBitSetAlloc(24);
+    for(psS32 i=0; i<24; i++) {
+        if ((i/2) % 2) {
+            bs1 = psBitSetSet(bs1, i);
+        }
+        if ((i) % 2) {
+            bs2 = psBitSetSet(bs2, i);
+        }
+        if ( ((i/2) % 2) || ((i) % 2) ) {
+            or = psBitSetSet(or, i);
+        }
+    }
+
+    binOut1 = psBitSetToString(bs1);
+    binOut2 = psBitSetToString(bs2);
+    binOut3 = psBitSetToString(or);
+    fprintf(stderr,"psBitSetOp input is %s, %s.  Truth is %s.\n",
+            binOut1,binOut2,binOut3);
+    psFree(binOut1);
+    psFree(binOut2);
+    psFree(binOut3);
+
+    // Test B - Perform binary AND with psBitSets
+    psLogMsg(__func__,PS_LOG_INFO,"Perform binary OR with psBitSets");
+    psBitSet* outbs = psBitSetAlloc(24);
+    outbs = psBitSetOp(outbs, bs1, "OR", bs2);
+    if (outbs == NULL) {
+        psAbort("psBitSetOp returned a NULL result for OR operation");
+    }
+
+    for(psS32 i=0; i<24; i++) {
+        psBool truth = psBitSetTest(or,i);
+        psBool res = psBitSetTest(outbs,i);
+        if ( res != truth) {
+            binOut1 = psBitSetToString(bs1);
+            binOut2 = psBitSetToString(bs2);
+            binOut3 = psBitSetToString(outbs);
+            psAbort("psBitSetOp with OR operator failed.\nInput was %s, %s.  Output was %s",
+                    binOut1,binOut2,binOut3);
+        }
+    }
+    psFree(outbs);
+
+    // Test C - Perform binary AND and auto allocate output
+    psLogMsg(__func__,PS_LOG_INFO,"Perform binary OR and auto allocate output");
+    outbs = psBitSetOp(NULL, bs1, "OR", bs2);
+    if (outbs == NULL) {
+        psAbort("psBitSetOp failed to create a new psBitSet for the result");
+    }
+    for(psS32 i=0; i<24; i++) {
+        psBool truth = psBitSetTest(or,i);
+        psBool res = psBitSetTest(outbs,i);
+        if ( res != truth) {
+            binOut1 = psBitSetToString(bs1);
+            binOut2 = psBitSetToString(bs2);
+            binOut3 = psBitSetToString(outbs);
+            psAbort("psBitSetOp with OR operator failed.\nInput was %s, %s.  Output was %s",
+                    binOut1,binOut2,binOut3);
+        }
+    }
+    psFree(outbs);
+    outbs = NULL;
+
+    psFree(bs1);
+    psFree(bs2);
+    psFree(or);
+
+    return 0;
+}
+
+static psS32 testBitSet04(void)
+{
+    char *binOut1 = NULL;
+    char *binOut2 = NULL;
+    char *binOut3 = NULL;
+
+    psBitSet* bs1 = psBitSetAlloc(24);
+    psBitSet* bs2 = psBitSetAlloc(24);
+    psBitSet* xor = psBitSetAlloc(24);
+    for(psS32 i=0; i<24; i++) {
+        if ((i/2) % 2) {
+            bs1 = psBitSetSet(bs1, i);
+        }
+        if ((i) % 2) {
+            bs2 = psBitSetSet(bs2, i);
+        }
+        if ( ((i/2) % 2) != ((i) % 2) ) {
+            xor = psBitSetSet(xor, i);
+        }
+    }
+
+    binOut1 = psBitSetToString(bs1);
+    binOut2 = psBitSetToString(bs2);
+    binOut3 = psBitSetToString(xor);
+    fprintf(stderr,"psBitSetOp input is %s, %s.  Truth is %s.\n",
+            binOut1,binOut2,binOut3);
+    psFree(binOut1);
+    psFree(binOut2);
+    psFree(binOut3);
+
+    // Test B - Perform binary AND with psBitSets
+    psLogMsg(__func__,PS_LOG_INFO,"Perform binary XOR with psBitSets");
+    psBitSet* outbs = psBitSetAlloc(24);
+    outbs = psBitSetOp(outbs, bs1, "XOR", bs2);
+    if (outbs == NULL) {
+        psAbort("psBitSetOp returned a NULL result for XOR operation");
+    }
+
+    for(psS32 i=0; i<24; i++) {
+        psBool truth = psBitSetTest(xor,i);
+        psBool res = psBitSetTest(outbs,i);
+        if ( res != truth) {
+            binOut1 = psBitSetToString(bs1);
+            binOut2 = psBitSetToString(bs2);
+            binOut3 = psBitSetToString(outbs);
+            psAbort("psBitSetOp with XOR operator failed.\nInput was %s, %s.  Output was %s",
+                    binOut1,binOut2,binOut3);
+        }
+    }
+    psFree(outbs);
+
+    // Test C - Perform binary AND and auto allocate output
+    psLogMsg(__func__,PS_LOG_INFO,"Perform binary XOR and auto allocate output");
+    outbs = psBitSetOp(NULL, bs1, "XOR", bs2);
+    if (outbs == NULL) {
+        psAbort("psBitSetOp failed to create a new psBitSet for the result");
+    }
+    for(psS32 i=0; i<24; i++) {
+        psBool truth = psBitSetTest(xor,i);
+        psBool res = psBitSetTest(outbs,i);
+        if ( res != truth) {
+            binOut1 = psBitSetToString(bs1);
+            binOut2 = psBitSetToString(bs2);
+            binOut3 = psBitSetToString(outbs);
+            psAbort("psBitSetOp with XOR operator failed.\nInput was %s, %s.  Output was %s",
+                    binOut1,binOut2,binOut3);
+        }
+    }
+    psFree(outbs);
+    outbs = NULL;
+
+    psFree(bs1);
+    psFree(bs2);
+    psFree(xor);
+
+    return 0;
+}
+
+static psS32 testBitSet05(void)
+{
+    char *binOut1 = NULL;
+    char *binOut2 = NULL;
+
+    psBitSet* bs1 = psBitSetAlloc(24);
+    psBitSet* not = psBitSetAlloc(24);
+    for(psS32 i=0; i<24; i++) {
+        if (i % 2) {
+            bs1 = psBitSetSet(bs1, i);
+        }
+        if (i % 2 == 0) {
+            not = psBitSetSet(not, i);
+        }
+    }
+
+    binOut1 = psBitSetToString(bs1);
+    binOut2 = psBitSetToString(not);
+    fprintf(stderr,"psBitSetOp input is %s.  Truth is %s.\n",
+            binOut1,binOut2);
+    psFree(binOut1);
+    psFree(binOut2);
+
+    // Test B - Perform binary AND with psBitSets
+    psLogMsg(__func__,PS_LOG_INFO,"Perform binary NOT with psBitSets");
+    psBitSet* outbs = psBitSetAlloc(24);
+    outbs = psBitSetNot(outbs, bs1);
+    if (outbs == NULL) {
+        psAbort("psBitSetOp returned a NULL result for NOT operation");
+    }
+
+    for(psS32 i=0; i<24; i++) {
+        psBool truth = psBitSetTest(not,i);
+        psBool res = psBitSetTest(outbs,i);
+        if ( res != truth) {
+            binOut1 = psBitSetToString(bs1);
+            binOut2 = psBitSetToString(outbs);
+            psAbort("psBitSetOp with NOT operator failed.\nInput was %s.  Output was %s",
+                    binOut1,binOut2);
+        }
+    }
+    psFree(outbs);
+
+    // Test C - Perform binary AND and auto allocate output
+    psLogMsg(__func__,PS_LOG_INFO,"Perform binary NOT and auto allocate output");
+    outbs = psBitSetNot(NULL, bs1);
+    if (outbs == NULL) {
+        psAbort("psBitSetOp failed to create a new psBitSet for the result");
+    }
+    for(psS32 i=0; i<24; i++) {
+        psBool truth = psBitSetTest(not,i);
+        psBool res = psBitSetTest(outbs,i);
+        if ( res != truth) {
+            binOut1 = psBitSetToString(bs1);
+            binOut2 = psBitSetToString(outbs);
+            psAbort("psBitSetOp with NOT operator failed.\nInput was %s.  Output was %s",
+                    binOut1,binOut2);
+        }
+    }
+    psFree(outbs);
+    outbs = NULL;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
+    if(psBitSetNot(NULL,NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psBitSetNot did not return NULL with NULL input");
+        return 30;
+    }
+
+    psFree(bs1);
+    psFree(not);
+
+    return 0;
+}
+
+static psS32 testBitSet06(void)
+{
+    psErr* err;
+
+    psBitSet* bs1 = psBitSetAlloc(24);
+    psBitSet* bs2 = psBitSetAlloc(40);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
+    psBitSet* outbs = psBitSetOp(NULL, bs1, "XOR", bs2);
+    if (outbs != NULL) {
+        psAbort("psBitSetOp did not return a NULL result when input sizes differ");
+    }
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_SIZE) {
+        psErrorStackPrint(stderr,"Error Stack:");
+        psAbort("psBitSetOp didn't generate expected error with operands' sizes differed.");
+    }
+    psFree(err);
+    psFree(bs1);
+    psFree(bs2);
+
+    bs1 = psBitSetAlloc(24);
+    bs2 = psBitSetAlloc(24);
+
+    outbs = psBitSetAlloc(40);
+    psBitSet* outbs2 = psBitSetOp(outbs, bs1, "XOR", bs2);
+    if (outbs2 == NULL) {
+        psAbort("psBitSetOp failed when input size and output size differed (a recoverable error).");
+    }
+    if (outbs2 != outbs) {
+        psAbort("psBitSetOp didn't reuse the given output struct.");
+    }
+    if (outbs2->n != bs1->n) {
+        psAbort("psBitSetOp did properly adjust the output psBitSet size.");
+    }
+
+    psErrorClear();
+    psLogMsg(__func__,PS_LOG_INFO,"Following is an error.");
+    outbs = psBitSetOp(outbs, bs1, "FOO", bs2);
+    if (outbs != NULL) {
+        psAbort("psBitSetOp returned something in case of a bogus operation.");
+    }
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psErrorStackPrint(stderr,"Error Stack:");
+        psAbort("psBitSetOp didn't generate expected error with bogus operator.");
+    }
+    psFree(err);
+
+
+    // try again, though give a valid output bitset -- should free the out upon error to avoid leak.
+    outbs = psBitSetAlloc(24);
+    psErrorClear();
+    psLogMsg(__func__,PS_LOG_INFO,"Following is an error.");
+    outbs = psBitSetOp(outbs, bs1, "FOO", bs2);
+
+    err = psErrorLast();
+    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
+        psErrorStackPrint(stderr,"Error Stack:");
+        psAbort("psBitSetOp didn't generate expected error with bogus operator.");
+    }
+    psFree(err);
+
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
+    if(psBitSetOp(outbs,NULL,"AND",bs2) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psBitSetOp did not return NULL with NULL input 1 bit set");
+        return 40;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
+    if(psBitSetOp(outbs,bs1,NULL,bs2) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psBitSetOp did not return NULL with NULL operator");
+        return 41;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
+    if(psBitSetOp(outbs,bs1,"AND",NULL) != NULL)  {
+        psError(PS_ERR_UNKNOWN,true,"psBitSetOp did not return NULL with NULL input 2 bit set");
+        return 42;
+    }
+
+    psFree(bs1);
+    psFree(bs2);
+    psFree(outbs);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash00.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash00.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash00.c	(revision 22322)
@@ -0,0 +1,64 @@
+/*****************************************************************************
+    This code will test whether a hash table can be allocated successfully,
+    then deallocated successfully.
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psHash.h"
+#define NUM_HASH_TABLE_BUCKETS 10
+psS32 main()
+{
+    psHash *myHashTable = NULL;
+    psS32 testStatus      = true;
+    psS32 i               = 0;
+    psS32 currentId = psMemGetId();
+    psS32 memLeaks        = 0;
+    printPositiveTestHeader(stdout,
+                            "psHash functions",
+                            "psHashAlloc()");
+
+    myHashTable = psHashAlloc(NUM_HASH_TABLE_BUCKETS);
+
+    if (myHashTable == NULL) {
+        fprintf(stderr, "%s: could not allocate a hash table.", __func__);
+        testStatus = false;
+    }
+
+    if (myHashTable->n != NUM_HASH_TABLE_BUCKETS) {
+        fprintf(stderr, "%s: myHashTable->nbucket not set properly.\n",
+                __func__);
+        testStatus = false;
+
+    }
+
+    if (myHashTable->buckets == NULL) {
+        fprintf(stderr, "%s: myHashTable->buckets is NULL.\n",
+                __func__);
+        testStatus = false;
+
+    }
+
+    for (i=0;i<NUM_HASH_TABLE_BUCKETS;i++) {
+        if (myHashTable->buckets[i] != NULL) {
+            fprintf(stderr, "%s: hash table bucket[%d] not equal to NULL.\n",
+                    __func__, i);
+            testStatus = false;
+        }
+    }
+
+    printFooter(stdout,
+                "psHash functions",
+                "psHashAlloc()",
+                testStatus);
+
+    psFree(myHashTable);
+
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    psMemCheckCorruption(1);
+
+    return (!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash01.c	(revision 22322)
@@ -0,0 +1,155 @@
+/*****************************************************************************
+    This code will test whether a hash table can be de-allocated successfully.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psHash.h"
+#define NUM_HASH_TABLE_BUCKETS 100
+psS32 imGlobal = 0;
+
+typedef struct
+{
+    char *name;
+}
+ID;
+
+static void IdFree(ID *id);
+
+static ID *IdAlloc(const char *name)
+{
+    ID *id = psAlloc(sizeof(ID));
+    psMemSetDeallocator(id,(psFreeFunc)IdFree);
+    id->name = psStringCopy(name);
+
+    return id;
+}
+
+static void IdFree(ID *id)
+{
+    imGlobal++;
+    psFree(id->name);
+}
+
+psS32 main()
+{
+    psHash *myHashTable = NULL;
+    psS32 testStatus      = true;
+    psS32 currentId = psMemGetId();
+    ID* id = NULL;
+    ID* replaceId = NULL;
+    psS32 memLeaks        = 0;
+    psBool  retVal = false;
+
+    // Allocate memory for Hash Table
+    printPositiveTestHeader(stdout,"psHash functions","psHashAlloc");
+    myHashTable = psHashAlloc(NUM_HASH_TABLE_BUCKETS);
+    if(myHashTable == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Unable to allocate psHash table");
+        return 10;
+    }
+    printFooter(stdout,"psHash functions","psHashAlloc",true);
+
+    // Add items to Hash table
+    id = IdAlloc("IDA");
+    printPositiveTestHeader(stdout,"psHash functions","psHashAdd");
+    retVal = psHashAdd(myHashTable, "ENTRY00", id);
+    psFree(id);
+    if( !retVal) {
+        psError(PS_ERR_UNKNOWN, true, "psHashAdd unable to add ENTRY00");
+        return 1;
+    }
+
+    id = IdAlloc("IDB");
+    retVal = psHashAdd(myHashTable, "ENTRY01", id);
+    psFree(id);
+    if (!retVal) {
+        psError(PS_ERR_UNKNOWN, true, "psHashAdd unable to add ENTRY01");
+        return 2;
+    }
+
+    id = IdAlloc("IDC");
+    retVal = psHashAdd(myHashTable, "ENTRY02", id);
+    psFree(id);
+    if(!retVal) {
+        psError(PS_ERR_UNKNOWN,true,"psHashAdd unable to add ENTRY02");
+        return 3;
+    }
+
+    id = IdAlloc("IDD");
+    retVal =psHashAdd(myHashTable, "ENTRY03", id);
+    psFree(id);
+    if(!retVal) {
+        psError(PS_ERR_UNKNOWN,true,"psHashAdd unable to add ENTRY03");
+        return 4;
+    }
+    printFooter(stdout,"psHash functions","psHashAdd",true);
+
+    // Replace item with same key
+    printPositiveTestHeader(stdout,"psHash replace item","psHashAdd");
+    id = IdAlloc("IDE");
+    retVal = psHashAdd(myHashTable,"ENTRY02",id);
+    if(!retVal) {
+        psError(PS_ERR_UNKNOWN,true,"psHashAdd unable to replace ENTRY02");
+        return 5;
+    }
+    replaceId = psHashLookup(myHashTable,"ENTRY02");
+    if(strcmp(replaceId->name,"IDE") != 0) {
+        psError(PS_ERR_UNKNOWN,true,"psHashAdd did not replace ENTRY02 with correct item");
+        return 6;
+    }
+    printFooter(stdout,"psHash replace item","psHashAdd",true);
+
+    // Add with NULL hash table specified
+    printNegativeTestHeader(stdout,"psHashAdd","NULL hash table","Hash table can not be NULL.",0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: psHashAdd with null table");
+    retVal = psHashAdd(NULL,"ENTRY04",id);
+    if(retVal) {
+        psError(PS_ERR_UNKNOWN,true,"psHashAdd added entry to NULL hash table.");
+        return 20;
+    }
+    printFooter(stdout,"psHashAdd","NULL hash table",true);
+
+    // Add with key to valid hash table NULL
+    printNegativeTestHeader(stdout,"psHashAdd","NULL key","Hash key can not be NULL.",0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: psHashAdd with null key");
+    retVal = psHashAdd(myHashTable,NULL,id);
+    if(retVal) {
+        psError(PS_ERR_UNKNOWN,true,"psHashAdd added entry to hash table with NULL key.");
+        return 21;
+    }
+    printFooter(stdout,"psHashAdd","NULL hash key",true);
+
+    // Add NULL hash data to valid table and key
+    printNegativeTestHeader(stdout,"psHashAdd","NULL hash data","Hash data can not be NULL.",0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: psHashAdd with null data");
+    retVal = psHashAdd(myHashTable,"ENTRY04",NULL);
+    if(retVal) {
+        psError(PS_ERR_UNKNOWN,true,"psHashAdd added entry to hash table with NULL data.");
+        return 22;
+    }
+    printFooter(stdout,"psHashAdd","NULL hash data",true);
+
+    // Free hash table
+    printPositiveTestHeader(stdout,"psHash functions","psHashFree");
+    psFree(id);
+
+    psFree(myHashTable);
+
+    if (imGlobal != 5) {
+        fprintf(stderr, "%s: only (%d/4) entries were freed",
+                __func__, imGlobal);
+        testStatus = false;
+    }
+
+    printFooter(stdout,"psHash functions","psHashFree()",testStatus);
+
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (memLeaks != 0) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    psMemCheckCorruption(1);
+
+    return (!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash02.c	(revision 22322)
@@ -0,0 +1,113 @@
+/*****************************************************************************
+    This code will test whether hash tables entries can be inserted correctly,
+    and retrieved correctly.
+ 
+    NOTE: Add code to test whether duplicates are handled correctly (use a
+    small hash table and lots of keys).
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psHash.h"
+#define NUM_HASH_TABLE_BUCKETS 100
+psS32 imGlobal = 0;
+
+typedef struct
+{
+    char *name;
+}
+ID;
+static void IdFree(ID *id);
+
+static ID *IdAlloc(const char *name)
+{
+    ID *id = psAlloc(sizeof(ID));
+    psMemSetDeallocator(id,(psFreeFunc)IdFree);
+    id->name = psStringCopy(name);
+
+    return id;
+}
+
+static void IdFree(ID *id)
+{
+    imGlobal++;
+    psFree(id->name);
+}
+
+psS32 main()
+{
+    psHash *myHashTable = NULL;
+    psS32 testStatus      = true;
+    psS32 i               = 0;
+    ID *id = NULL;
+    char *myKeys[] = {"ENTRY00", "ENTRY01", "ENTRY02", "ENTRY03", NULL
+                     };
+    char *myData[] = {"IDA", "IDB", "IDC", "IDD", NULL
+                     };
+    psS32 currentId = psMemGetId();
+    psS32 memLeaks        = 0;
+
+    printPositiveTestHeader(stdout,"psHash functions","psHashLookup");
+
+    myHashTable = psHashAlloc(NUM_HASH_TABLE_BUCKETS);
+    i = 0;
+    while (myKeys[i] != NULL) {
+        id = IdAlloc(myData[i]);
+        psHashAdd(myHashTable, myKeys[i], id);
+        psFree(id);
+        i++;
+    }
+
+    i = 0;
+    while (myKeys[i] != NULL) {
+        id = psHashLookup(myHashTable, myKeys[i]);
+        if (0 != strcmp(myData[i], id->name)) {
+            fprintf(stderr, "%s: Hash table entry for key %s was %s (should be %s).\n",
+                    __func__, myKeys[i], id->name, myData[i]);
+            return 1;
+        }
+        i++;
+    }
+    printFooter(stdout,"psHash functions","psHashLookup",true);
+
+    // Use an invalid key in the hash table: verify no item is returned
+    printNegativeTestHeader(stdout,"psHashLookup","Invalid key","Key is not found in the table",0);
+    id = psHashLookup(myHashTable, "BogusKey");
+    if (id != NULL) {
+        fprintf(stderr, "%s: Hash table entry for key %s was not NULL.\n",
+                __func__, "BogusKey");
+        return 2;
+    }
+    printFooter(stdout,"psHashLookup","Invalid key",true);
+
+    // Lookup with null table
+    printNegativeTestHeader(stdout,"psHashLookup","NULL table","Can not lookup with NULL table",0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: psHashLookup with null table");
+    id = psHashLookup(NULL,"ENTRY01");
+    if (id != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psHashLookup retrieved an entry from NULL hash table.");
+        return 3;
+    }
+    printFooter(stdout,"psHashLookup","NULL table",true);
+
+    // Lookup with null key
+    printNegativeTestHeader(stdout,"psHashLookup","NULL key","Can not lookup with NULL key.",0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: psHashLookup with null key");
+    id = psHashLookup(myHashTable,NULL);
+    if (id != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psHashLookup retrieved an entry with NULL key.");
+        return 4;
+    }
+    printFooter(stdout,"psHashLookup","NULL key",true);
+
+    psFree(myHashTable);
+
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (0 != memLeaks) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    psMemCheckCorruption(1);
+
+    return (!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash03.c	(revision 22322)
@@ -0,0 +1,142 @@
+/*****************************************************************************
+    This code will test whether hash tables entries can be removed correctly.
+ 
+    NOTE: Add code to test whether duplicates are handled correctly.
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psHash.h"
+#include "psMemory.h"
+#define NUM_HASH_TABLE_BUCKETS 100
+psS32 imGlobal = 0;
+psS32 currentId = 0;
+
+typedef struct
+{
+    char *name;
+}
+ID;
+static void IdFree(ID *id);
+
+static ID *IdAlloc(const char *name)
+{
+    ID *id = NULL;
+
+    id = psAlloc(sizeof(ID));
+    psMemSetDeallocator(id,(psFreeFunc)IdFree);
+
+    id->name = psStringCopy(name);
+
+    return id;
+}
+
+static void IdFree(ID *id)
+{
+    imGlobal++;
+    psFree(id->name);
+}
+
+psS32 main()
+{
+    psHash *myHashTable = NULL;
+    psS32 testStatus      = true;
+    psS32 i               = 0;
+    psS32 TotalKeys       = 0;
+    psBool retVal         = false;
+    ID *id = NULL;
+    ID *ids[4];
+    char *myKeys[] = {"ENTRY00", "ENTRY01", "ENTRY02", "ENTRY03", NULL
+                     };
+    char *myData[] = {"IDA", "IDB", "IDC", "IDD", NULL
+                     };
+    psS32 memLeaks        = 0;
+
+    currentId = psMemGetId();
+
+    printPositiveTestHeader(stdout,"psHash functions","psHashRemove");
+
+    // Add items to hash table
+    myHashTable = psHashAlloc(NUM_HASH_TABLE_BUCKETS);
+    i = 0;
+    while (myKeys[i] != NULL) {
+        ids[i] = IdAlloc(myData[i]);
+        psHashAdd(myHashTable, myKeys[i], ids[i]);
+        i++;
+    }
+    TotalKeys = i - 1;
+
+    // Verify items which were just add to hash table
+    i = TotalKeys;
+    while (i >= 0) {
+
+        id = psHashLookup(myHashTable, myKeys[i]);
+        if (0 != strcmp(myData[i], id->name)) {
+            fprintf(stderr, "%s: Hash table entry for key %s was %s (should be %s).\n",
+                    __func__, myKeys[i], id->name, myData[i]);
+            return 1;
+        }
+        i--;
+    }
+
+    // Remove each item from the table and verify item is no longer in the list
+    i = 0;
+    while (myKeys[i] != NULL) {
+        // The psHashRemove() procedure removes the entry from the hash
+        // table and deletes the data.
+        retVal = psHashRemove(myHashTable, myKeys[i]);
+        if (!retVal) {
+            fprintf(stderr,"%s: Hash table entry not removed.\n",__func__);
+            return 2;
+        }
+        id = psHashLookup(myHashTable, myKeys[i]);
+        if (id != NULL) {
+            fprintf(stderr, "%s: Hash table entry for key %s not removed.\n",
+                    __func__, "IDA");
+            return 3;
+        }
+        i++;
+    }
+    printFooter(stdout,"psHash functions","psHashRemove()",true);
+
+    // Use an invalid key in the hash table: verify false is returned
+    printNegativeTestHeader(stdout,"psHashRemove","Invalid key","Key is not found in table",0);
+    if (psHashRemove(myHashTable,"BogusKey") ) {
+        psError(PS_ERR_UNKNOWN,true,"psHashRemove removed an entry with a bogus key.");
+        return 4;
+    }
+    printFooter(stdout,"psHashRemove","Invalid key",true);
+
+    // Remove with null table
+    printNegativeTestHeader(stdout,"psHashRemove","NULL table","Can not remove with NULL table",0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: psHashRemove with null table");
+    retVal = psHashRemove(NULL,"ENTRY02");
+    if (retVal) {
+        psError(PS_ERR_UNKNOWN,true,"psHashRemove removed an entry from NULL hash table.");
+        return 5;
+    }
+    printFooter(stdout,"psHashRemove","NULL table",true);
+
+    // Remove with null key
+    printNegativeTestHeader(stdout,"psHashRemove","NULL key","Can not remove with NULL key",0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: psHashRemove with null key");
+    if (psHashRemove(myHashTable,NULL) ) {
+        psError(PS_ERR_UNKNOWN,true,"psHashRemove removed an entry from NULL key");
+        return 6;
+    }
+    printFooter(stdout,"psHashRemove","NULL key",true);
+
+    psFree(myHashTable);
+    for(i=0; i<(TotalKeys+1); i++) {
+        psFree(ids[i]);
+    }
+    memLeaks = psMemCheckLeaks(currentId,NULL,stdout,false);
+    if (memLeaks != 0) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    psMemCheckCorruption(true);
+
+    return (!testStatus);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash04.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash04.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash04.c	(revision 22322)
@@ -0,0 +1,84 @@
+/*****************************************************************************
+    This code will test whether the call psHashKeyList() function works.
+ *****************************************************************************/
+#include <stdio.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+#include "psHash.h"
+#define NUM_HASH_TABLE_BUCKETS 100
+psS32 imGlobal = 0;
+
+typedef struct
+{
+    char *name;
+}
+ID;
+static void IdFree(ID *id);
+
+static ID *IdAlloc(const char *name)
+{
+    ID *id = psAlloc(sizeof(ID));
+    psMemSetDeallocator(id,(psFreeFunc)IdFree);
+
+    id->name = psStringCopy(name);
+
+    return id;
+}
+
+static void IdFree(ID *id)
+{
+    imGlobal++;
+    psFree(id->name);
+}
+
+psS32 main()
+{
+    psHash *myHashTable = NULL;
+    psS32 testStatus      = true;
+    psS32 i               = 0;
+    char *myKeys[] = {"ENTRY00", "ENTRY01", "ENTRY02", "ENTRY03", NULL
+                     };
+    char *myData[] = {"IDA", "IDB", "IDC", "IDD", NULL
+                     };
+    psS32 currentId = psMemGetId();
+    psS32 memLeaks        = 0;
+    psList *myLinkList = NULL;
+    psListElem *tmp = NULL;
+    ID* id = NULL;
+
+    printPositiveTestHeader(stdout,"psHash functions","psHashKeyList()");
+    myHashTable = psHashAlloc(NUM_HASH_TABLE_BUCKETS);
+    i = 0;
+    while (myKeys[i] != NULL) {
+        id = IdAlloc(myData[i]);
+        psHashAdd(myHashTable, myKeys[i], id);
+        psFree(id);
+        i++;
+    }
+    myLinkList = psHashKeyList(myHashTable);
+    tmp = myLinkList->head;
+    while (tmp != NULL) {
+        printf("Linked List Entries: %s\n", (char *) tmp->data);
+        tmp = tmp->next;
+    }
+    printFooter(stdout,"psHash functions","psHashKeyList()",testStatus);
+    psFree(myLinkList);
+
+    printNegativeTestHeader(stdout,"psHashKeyList","NULL table","Can not lookup with NULL table",0);
+    myLinkList = psHashKeyList(NULL);
+    if(myLinkList != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psHashKeyList retrieved a key list from a NULL table.");
+        return 1;
+    }
+    printFooter(stdout,"psHashKeyList","NULL table",true);
+
+    psFree(myHashTable);
+
+    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
+    if (memLeaks != 0) {
+        psAbort("Memory Leaks! (%d leaks)", memLeaks);
+    }
+    psMemCheckCorruption(1);
+
+    return(!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash05.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash05.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psHash05.c	(revision 22322)
@@ -0,0 +1,172 @@
+/** @file  tst_psHash05.c
+*
+*  @brief Contains the tests for psHash.[ch]
+*
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-07-13 02:47:01 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+static psS32 hashToArray( void );
+
+testDescription tests[] = {
+                              {hashToArray, 789, "psHashToArray", 0, false},
+                              {NULL}
+                          };
+
+static void printIntArray(char* name, psArray* arr)
+{
+    if (arr == NULL) {
+        printf("%s = NULL\n",name);
+        return;
+    }
+
+    printf("%s = {",name);
+    for (int i = 0; i < arr->n; i++) {
+        if (arr->data[i] == NULL) {
+            printf("NULL");
+        } else {
+            printf("%d",*(int*)arr->data[i]);
+        }
+        if (i != arr->n-1) {
+            printf(",");
+        }
+    }
+    printf("}");
+}
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( ! runTestSuite( stderr, "psHash", tests, argc, argv ) );
+}
+
+psS32 hashToArray( void )
+{
+    int testNum = 0;
+    psArray* array;
+    #define BUCKETS 10
+
+    psHash* hash = psHashAlloc(BUCKETS);
+    char key[2] = "A";
+    bool found[BUCKETS];
+
+    for (int i = 0; i < BUCKETS; i++) {
+
+        array = psHashToArray(hash);
+
+        // return non-null?
+        testNum++;
+        if (array == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to create an array from a psHash of %d elements.",
+                    i);
+            return testNum;
+        }
+
+        // the size correct
+        testNum++;
+        if (array->n != i) {
+            printIntArray("array",array);
+            psError(PS_ERR_UNKNOWN, false,
+                    "psHashToArray created a psArray of %d elements from a psHash of %d elements.",
+                    array->n, i);
+            return testNum;
+        }
+
+        // the values correct?
+
+        // zero out the found boolean vector
+        for (int j = 0; j < i; j++) {
+            found[j] = false;
+        }
+
+        // check if all the items in array are valid
+        for (int k = 0; k < array->n; k++) {
+            int* item = array->data[k];
+
+            testNum++;
+            if (item == NULL) {
+                printIntArray("array",array);
+                psError(PS_ERR_UNKNOWN, true,
+                        "The array position %d was NULL.",
+                        k);
+                return testNum;
+            }
+
+            testNum++;
+            if (*item < 0 || *item >= BUCKETS) {
+                printIntArray("array",array);
+                psError(PS_ERR_UNKNOWN, true,
+                        "The array position %d was invalid (%d).",
+                        k,*item);
+                return testNum;
+            }
+
+            testNum++;
+            if (found[*item]) {
+                printIntArray("array",array);
+                psError(PS_ERR_UNKNOWN, true,
+                        "The array position %d was a duplicate (%d).",
+                        k,*item);
+                return testNum;
+            }
+
+            testNum++;
+            if (psMemGetRefCounter(item) != 2) {
+                printIntArray("array",array);
+                psError(PS_ERR_UNKNOWN, true,
+                        "The array position %d was not properly reference counted (%d).",
+                        k,psMemGetRefCounter(item));
+                return testNum;
+            }
+
+            found[*item] = true;
+        }
+
+        // check that all the items in psHash was found
+        for (int j = 0; j < i; j++) {
+            testNum++;
+            if (! found[j]) {
+                printIntArray("array",array);
+                psError(PS_ERR_UNKNOWN, true,
+                        "Item %d not found in array.",
+                        j);
+                return testNum;
+            }
+        }
+
+        psFree(array);
+
+        // add one element
+        int* value = psAlloc(sizeof(int));
+        *value = i;
+        psHashAdd(hash, key, value);
+        *key += 1; // increment the key value
+
+        psFree(value);
+    }
+
+    psFree(hash);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
+    array = psHashToArray(NULL);
+    testNum++;
+    if (array != NULL) {
+        printIntArray("array",array);
+        psError(PS_ERR_UNKNOWN, false,
+                "psHashToArray returned non-null psArray given a null psHash.");
+        return testNum;
+    }
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psList.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psList.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psList.c	(revision 22322)
@@ -0,0 +1,1320 @@
+/** @file  tst_psList.c
+ *
+ *  @brief Contains the tests for psList.[ch]
+ *
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-09-26 01:47:22 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+void printListInt(psList* list);
+
+
+static psS32 testListAlloc(void);
+static psS32 testListAdd(void);
+static psS32 testListGet(void);
+static psS32 testListRemove(void);
+static psS32 testListConvert(void);
+static psS32 testListIterator(void);
+static psS32 testListFree(void);
+static psS32 testListSort(void);
+static psS32 testListAddAfter(void);
+static psS32 testListAddBefore(void);
+static psS32 testListLength(void);
+
+testDescription tests[] = {
+                              {testListAlloc,487,"psListAlloc",0,false},
+                              {testListAdd,488,"psListAdd",0,false},
+                              {testListGet,489,"psListGet",0,false},
+                              {testListRemove,490,"psListRemove",0,false},
+                              {testListConvert,491,"psListConvert",0,false},
+                              {testListIterator,494,"psListIterator",0,false},
+                              {testListFree,627,"psListFree",0,false},
+                              {testListSort,624,"psListSort",0,false},
+                              {testListAddAfter,811,"psListAddAfter",0,false},
+                              {testListAddBefore,811,"psListAddBefore",0,false},
+                              {testListLength,666,"testListLength",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    if (! runTestSuite(stderr,"psList",tests,argc,argv) ) {
+        psError(PS_ERR_UNKNOWN,true,"One or more tests failed");
+        return 1;
+    }
+    return 0;
+}
+
+psS32 testListAlloc(void)
+{
+    psList* list;
+    psS32 ref;
+    float* data;
+
+    psLogMsg(__func__,PS_LOG_INFO,"psListAlloc shall create a psList with either 0 or 1 element.");
+
+    data = psAlloc(sizeof(float));
+
+    // if psListAlloc is invoked with a NULL parameter, it shall return an
+    // empty psList struct with head=tail=NULL and n=0. Otherwise, it shall
+    // return a psList of one element (head=tail=data, n=1).
+    // Test requirement SDR-167
+    list = psListAlloc(NULL);
+    if (list == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psListAlloc failed to return a list.");
+        return 1;
+    }
+    if (list->head != NULL || list->tail != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"head and/or tail was not NULL for empty list.");
+        return 2;
+    }
+    if (list->n != 0) {
+        psError(PS_ERR_UNKNOWN, true,"size of list wasn't zero for empty list.");
+        return 3;
+    }
+
+    psFree(list);
+
+    // Test requirement SDR-165
+    list = psListAlloc(data);
+    if (list == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psListAlloc failed to return a list.");
+        return 4;
+    }
+
+    if (list->head == NULL || list->tail == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"head and/or tail was NULL for one-element new list.");
+        return 5;
+    }
+
+    if (list->head->data != data || list->tail->data != data) {
+        psError(PS_ERR_UNKNOWN, true,"head and/or tail didn't point to data's node for one-element new list.");
+        return 6;
+    }
+
+    if (list->n != 1) {
+        psError(PS_ERR_UNKNOWN, true,"size of list wasn't correctly set for new, one-element list.");
+        return 7;
+    }
+
+    ref = psMemGetRefCounter(data);
+    if (ref != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psList didn't increment reference count of data (%d.",ref);
+        return 8;
+    }
+
+    psFree(list);
+
+    psFree(data);
+
+    return 0;
+}
+
+psS32 testListAddAfter(void)
+{
+    psList* list = NULL;
+    psS32*  data = NULL;
+    psS32*  data1 = NULL;
+    psS32*  data2 = NULL;
+    psListIterator *currentIterator = NULL;
+
+    data = psAlloc(sizeof(psS32));
+    *data = 1;
+    data1 = psAlloc(sizeof(psS32));
+    *data1 = 2;
+    data2 = psAlloc(sizeof(psS32));
+    *data2 = 3;
+
+    list = psListAlloc(data);
+    currentIterator = psListIteratorAlloc(list,PS_LIST_HEAD,true);
+
+    // Add data after HEAD and verify data
+    // Test requirement SDR-755
+    if(!psListAddAfter(currentIterator,data1)) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddAfter failed to add item");
+        return 1;
+    }
+    if(*(psS32*)list->head->next->data != 2) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddAfter did not add the item properly");
+        return 2;
+    }
+    // Test requirement SDR-175
+    if (psMemGetRefCounter(data1) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAddAfter didn't increment the data reference count.");
+        return 20;
+    }
+    psFree(currentIterator);
+
+    currentIterator = psListIteratorAlloc(list,PS_LIST_TAIL,true);
+
+    // Add data after TAIL and verify data
+    if(!psListAddAfter(currentIterator,data2)) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddAfter failed to add item");
+        return 3;
+    }
+    if(*(psS32*)list->tail->data != 3) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddAfter did not add the item properly");
+        return 4;
+    }
+    // Test requirement SDR-175
+    if (psMemGetRefCounter(data2) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAddAfter didn't increment the data reference count.");
+        return 40;
+    }
+
+    // Verify error message generated with data pointer is NULL
+    psLogMsg(__func__,PS_LOG_INFO,"NULL data pointer should generate error message");
+    if(psListAddAfter(currentIterator,NULL)) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddAfter should have generated error.");
+        return 5;
+    }
+
+    // Verify error message generated with iterator NULL
+    psLogMsg(__func__,PS_LOG_INFO,"NULL iterator should generate error message");
+    if(psListAddAfter(NULL,data2)) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddAfter should have generated error.");
+        return 6;
+    }
+
+    // Verify error message is generate with non-mutable iterator
+    psLogMsg(__func__,PS_LOG_INFO,"Non-mutable list should generate error message");
+    currentIterator->mutable = false;
+    if(psListAddAfter(currentIterator,data2)) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddAfter should have generated error for non-mutable list add");
+        return 7;
+    }
+    currentIterator->mutable = true;
+
+    psFree(data);
+    psFree(data1);
+    psFree(data2);
+    psFree(list);
+
+    return 0;
+}
+
+psS32 testListAddBefore(void)
+{
+    psList* list = NULL;
+    psS32*  data1 = NULL;
+    psS32*  data2 = NULL;
+    psListIterator *currentIterator = NULL;
+
+    data1 = psAlloc(sizeof(psS32));
+    *data1 = 2;
+    data2 = psAlloc(sizeof(psS32));
+    *data2 = 3;
+
+    list = psListAlloc(NULL);
+    currentIterator = psListIteratorAlloc(list,PS_LIST_HEAD,true);
+
+    // Add data before HEAD and verify data
+    // Test requirement SDR-756
+    if(!psListAddBefore(currentIterator,data1)) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddBefore failed to add item");
+        return 1;
+    }
+    if(*(psS32*)list->head->data != 2) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddBefore did not add the item properly");
+        return 2;
+    }
+    // Test requirement SDR-175
+    if (psMemGetRefCounter(data1) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAddBefore didn't increment the data reference count.");
+        return 20;
+    }
+    psFree(currentIterator);
+
+    currentIterator = psListIteratorAlloc(list,PS_LIST_TAIL,true);
+
+    // Add data after TAIL and verify data
+    if(!psListAddBefore(currentIterator,data2)) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddBefore failed to add item");
+        return 3;
+    }
+    if(*(psS32*)list->tail->prev->data != 3) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddBefore did not add the item properly");
+        return 4;
+    }
+    // Test requirement SDR-175
+    if (psMemGetRefCounter(data2) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAddBefore didn't increment the data reference count.");
+        return 40;
+    }
+
+    // Verify error message generated with data pointer is NULL
+    psLogMsg(__func__,PS_LOG_INFO,"NULL data pointer should generate error message");
+    if(psListAddBefore(currentIterator,NULL)) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddBefore should have generated error.");
+        return 5;
+    }
+
+    // Verify error message generated with iterator NULL
+    psLogMsg(__func__,PS_LOG_INFO,"NULL iterator should generate error message");
+    if(psListAddBefore(NULL,data2)) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddBefore should have generated error.");
+        return 6;
+    }
+
+    // Verify error message is generate with non-mutable iterator
+    psLogMsg(__func__,PS_LOG_INFO,"Non-mutable list should generate error message");
+    currentIterator->mutable = false;
+    if(psListAddBefore(currentIterator,data2)) {
+        psError(PS_ERR_UNKNOWN,true,"psListAddBefore should have generated error for non-mutable list add");
+        return 7;
+    }
+    currentIterator->mutable = true;
+
+    psFree(data1);
+    psFree(data2);
+    psFree(list);
+
+    return 0;
+}
+
+psS32 testListAdd(void)
+{
+    psList* list = NULL;
+    psS32* data = NULL;
+
+    psLogMsg(__func__,PS_LOG_INFO,"psListAdd shall add an element to list");
+
+    /*
+        psListAdd(list,data,where) should be tested in the instance where:
+
+        1. list is NULL (error)
+        2. data is NULL (error, list should not grow)
+        3. where is PS_LIST_HEAD or PS_LIST_TAIL
+        4. where is not PS_LIST_* but <0
+        5. where is >0
+    */
+
+    data = psAlloc(sizeof(psS32));
+    *data = 1;
+
+    //  1. list is NULL (error)
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for using NULL list.");
+    if (psListAdd(NULL,PS_LIST_HEAD,data)) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd was given a NULL list, but returned a true/success.");
+        return 1;
+    }
+
+    list = psListAlloc(data);
+    psFree(data);
+    if (list->n != 1) {
+        psError(PS_ERR_UNKNOWN, true,"psListAlloc didn't create a list properly.");
+        return 2;
+    }
+
+    //  2. data is NULL (error, list should not grow)
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error msg to add NULL data");
+    if (psListAdd(list, PS_LIST_HEAD,NULL)) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd successfully added a NULL data item?");
+        return 40;
+    }
+    if ( list->n != 1) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd with a NULL data element changed the list size or returned success.");
+        return 3;
+    }
+
+    //  3. where is PS_LIST_HEAD or PS_LIST_TAIL
+    data = psAlloc(sizeof(psS32));
+    *data = 2;
+    if ( ! psListAdd(list,PS_LIST_HEAD,data) ) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add a data item to head.");
+        return 21;
+    }
+    // Test requirement SDR-175
+    if (psMemGetRefCounter(data) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
+        return 20;
+    }
+    psFree(data);
+
+    // verify that the size incremented
+    if (list->n != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the size by one");
+        return 4;
+    }
+
+    // verify that the head is the inserted data item
+    if (*(psS32*)list->head->data != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd with PS_LIST_HEAD didn't insert at the head.");
+        return 5;
+    }
+
+    data = psAlloc(sizeof(psS32));
+    *data = 3;
+    if ( ! psListAdd(list,PS_LIST_TAIL,data) ) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add a data item to tail.");
+        return 21;
+    }
+    // Test requirement SDR-175
+    if (psMemGetRefCounter(data) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
+        return 22;
+    }
+    psFree(data);
+    // verify that the size incremented
+    if (list->n != 3) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the size by 1");
+        return 6;
+    }
+
+    // verify that the head is still the same
+    if (*(psS32*)list->head->data != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd with PS_LIST_TAIL modified the head.");
+        return 7;
+    }
+
+    // verify that the tail is the data item inserted
+    if (*(psS32*)list->tail->data != 3) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd with PS_LIST_TAIL didn't insert at the tail.");
+        return 8;
+    }
+
+    // 4. where is not PS_LIST_* but <0
+
+    data = psAlloc(sizeof(psS32));
+    *data = 4;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should error with invalid insert location");
+
+    if ( psListAdd(list,-10,data) ) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd successfully added data to a -10 position?");
+        return 30;
+    }
+    // Test requirement SDR-175
+    if (psMemGetRefCounter(data) != 1) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd incremented the data reference count.");
+        return 24;
+    }
+    psFree(data);
+    // verify that the size wasn't incremented
+    if (list->n != 3) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't insert to head when where was invalid.");
+        return 9;
+    }
+
+    // 5. where is >0
+    data = psAlloc(sizeof(psS32));
+    *data = 5;
+
+    if ( ! psListAdd(list,1,data) ) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add data to 1 position.");
+        return 30;
+    }
+    // Test requirement SDR-175
+    if (psMemGetRefCounter(data) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
+        return 25;
+    }
+    psFree(data);
+    // verify that the size incremented
+    if (list->n != 4 || *(psS32*)list->head->next->data != 5) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't insert to position #1.");
+        return 10;
+    }
+
+    data = psAlloc(sizeof(psS32));
+    *data = 6;
+    if ( ! psListAdd(list,3,data) ) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add data to 4 position.");
+        return 31;
+    }
+    // Test requirment SDR-175
+    if (psMemGetRefCounter(data) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
+        return 26;
+    }
+    psFree(data);
+    // verify that the size incremented
+    if (list->n != 5  || *(psS32 *)list->head->next->next->next->data != 6) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't insert to position #4.");
+        return 50;
+    }
+
+    data = psAlloc(sizeof(psS32));
+    *data = 7;
+    // Test requirment SDR-169
+    if ( ! psListAdd(list,-2,data) ) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add data to 2 position.");
+        return 32;
+    }
+
+    // Test requirment SDR-175
+    if (psMemGetRefCounter(data) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
+        return 28;
+    }
+    psFree(data);
+    // verify that the size incremented
+    if (list->n != 6  || *(psS32 *)list->tail->prev->prev->data != 7) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't insert to position #2.");
+        return 11;
+    }
+
+    // Test requirement SDR-757
+    data = psAlloc(sizeof(psS32));
+    *data = 8;
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be a warning.");
+
+    if ( ! psListAdd(list,9,data) ) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add data to a 9 position?");
+        return 30;
+    }
+    // Test requirment SDR-175
+    if (psMemGetRefCounter(data) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
+        return 29;
+    }
+    if(list->n != 7 || *(psS32 *)list->tail->data != 8) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN,true,"psListAdd didn't place added item at tail.");
+        return 12;
+    }
+
+    psFree(data);
+
+    psFree(list);
+
+    return 0;
+}
+
+void printListInt(psList* list)
+{
+    psS32* data = NULL;
+    psBool first = true;
+
+    psListIterator* iter = psListIteratorAlloc(list,PS_LIST_HEAD,true);
+
+    while ( (data=(psS32*)psListGetAndIncrement(iter)) != NULL ) {
+        if (!first) {
+            printf(", %d",*(psS32*)data);
+        } else {
+            printf("%d",*(psS32*)data);
+            first = false;
+        }
+    }
+
+    printf(".\n");
+}
+
+
+psS32 testListGet(void)
+{
+    psList* list = NULL;
+    psS32* data;
+
+    /*
+     psListGet(list,which) shall be tested with the following instances
+
+        1. list is NULL.
+        2. which>0 and which<list.n.
+        3. which>list.n.
+        4. which=PS_LIST_HEAD
+        5. which=PS_LIST_NEXT
+        6. which=PS_LIST_TAIL
+        7. which=PS_LIST_PREV
+        8. which<0 and not any PS_LIST_* values
+    */
+
+    //  1. list is NULL.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
+    if (psListGet(list,PS_LIST_HEAD) != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psListGet didn't return NULL given a NULL list.");
+        return 1;
+    };
+
+    // create a list
+    data = psAlloc(sizeof(psS32));
+    *data = 0;
+    list = psListAlloc(data);
+    psFree(data);
+
+    data = psAlloc(sizeof(psS32));
+    *data = 1;
+    psListAdd(list,PS_LIST_TAIL,data);
+    psFree(data);
+
+    data = psAlloc(sizeof(psS32));
+    *data = 2;
+    psListAdd(list,PS_LIST_TAIL,data);
+    psFree(data);
+
+    data = psAlloc(sizeof(psS32));
+    *data = 3;
+    psListAdd(list,PS_LIST_TAIL,data);
+    psFree(data);
+
+    //  2. which>0 and which<list.n.
+    data = (psS32*)psListGet(list,3);
+    if (data == NULL || *data != 3) {
+        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=3");
+        return 2;
+    }
+    data = (psS32*)psListGet(list,1);
+    if (data == NULL || *data != 1) {
+        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=1");
+        return 3;
+    }
+
+    //  3. which>=list.n.
+    data = (psS32*)psListGet(list,5);
+    if (data != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=5");
+        return 4;
+    }
+    data = (psS32*)psListGet(list,4);
+    if (data != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=4");
+        return 5;
+    }
+
+    //  4. which=PS_LIST_HEAD
+    data = (psS32*)psListGet(list,PS_LIST_HEAD);
+    if (data == NULL || *data != 0) {
+        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=PS_LIST_HEAD");
+        return 6;
+    }
+
+    //  6. which=PS_LIST_TAIL
+    data = (psS32*)psListGet(list,PS_LIST_TAIL);
+    if (data == NULL || *data != 3) {
+        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=PS_LIST_TAIL");
+        return 8;
+    }
+
+    data = (psS32*)psListGet(list,-2);
+    if (data == NULL || *data !=2) {
+        psError(PS_ERR_UNKNOWN,true,"psListGet failed with location=-2");
+        return 9;
+    }
+
+    psFree(list);
+
+    return 0;
+}
+
+psS32 testListRemove(void)
+{
+    psList* list = NULL;
+    psS32* data;
+    int items = 15;
+
+    /*
+        psListRemove(list,data,which) should be tested under the following conditions:
+
+        1. list is NULL
+        2. which is PS_LIST_HEAD (remove first element of list)
+        3. which is PS_LIST_TAIL (remove last element of list)
+        4. which is PS_LIST_NEXT (element right of cursor [which should be head,tail, and neither])
+        5. which is PS_LIST_PREV (element left of cursor [which should be head,tail, and neither])
+        6. which is PS_LIST_UNKNOWN and data=NULL (error)
+        7. which is PS_LIST_UNKNOWN and data=[head,tail,other list items]
+        8. which is PS_LIST_UNKNOWN and data!=NULL and data!=any element in list
+
+        In all conditions that are not an error, list.n shall be decremented and only the specified element
+        shall be removed. After each step, list.n should be checked to verify that it is the true
+        number of elements in list.
+    */
+
+    //  1. list is NULL.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
+    if (psListRemove(list,PS_LIST_HEAD)) {
+        psError(PS_ERR_UNKNOWN, true,"psListRemove didn't return false given a NULL list.");
+        return 1;
+    };
+
+    // create a list
+    list = psListAlloc(NULL);
+
+    for (psS32 lcv=0;lcv<items;lcv++) {
+        data = psAlloc(sizeof(psS32));
+        *data = lcv;
+        psListAdd(list,PS_LIST_TAIL,data);
+        psMemDecrRefCounter(data);
+    }
+
+
+    // 2. which is PS_LIST_HEAD (remove first element of list)
+    psS32* data1 = (psS32 *)psListGet(list,PS_LIST_HEAD);
+    psMemIncrRefCounter(data1);
+    // Test requirement SDR-172, SDR-173
+    if ( (! psListRemove(list,PS_LIST_HEAD)) ||
+            (psListGet(list,PS_LIST_HEAD) == data) ) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Failed to remove PS_LIST_HEAD");
+        return 1;
+    }
+
+    if (list->n != --items) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
+        return 1;
+    }
+    // Test requirement SDR-176
+    if (psMemGetRefCounter(data1) != 1) {
+        psError(PS_ERR_UNKNOWN, true,"psListRemove didn't decrement the data reference count.");
+        return 20;
+    }
+    psMemDecrRefCounter(data1);
+
+    // 3. which is PS_LIST_TAIL (remove last element of list)
+    data = psListGet(list,PS_LIST_TAIL);
+    // Test requirement SDR-173
+    if ( (! psListRemove(list,PS_LIST_TAIL)) ||
+            (psListGet(list,PS_LIST_TAIL) == data) ) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Failed to remove PS_LIST_TAIL");
+        return 1;
+    }
+
+    if (list->n != --items) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
+        return 1;
+    }
+
+    data = psListGet(list,-2);
+    // Test requirement SDR-173
+    if ( (! psListRemove(list,-2)) ||
+            (psListGet(list,-2) == data) ) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Failed to remove from location -2");
+        return 11;
+    }
+
+    if (list->n != --items) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
+        return 1;
+    }
+
+    // 6. psListRemoveData where data=NULL (error)
+    psLogMsg(__func__,PS_LOG_INFO,"Next message should be an error");
+    if (psListRemoveData(list,NULL)) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"removed something for PS_LIST_UNKNOWN with data=NULL");
+        return 1;
+    }
+
+    // 7. which is PS_LIST_UNKNOWN and data=[head,tail,other list items]
+    data = psListGet(list,PS_LIST_HEAD);
+    // Test requirement SDR-762
+    if ( (! psListRemoveData(list,data)) ||
+            (psListGet(list,PS_LIST_HEAD) == data) ) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Failed to remove PS_LIST_UNKNOWN @ PS_LIST_HEAD");
+        return 1;
+    }
+
+    if (list->n != --items) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
+        return 1;
+    }
+
+    psS32* data2 = psListGet(list,PS_LIST_TAIL);
+    psMemIncrRefCounter(data2);
+    if ( (!psListRemoveData(list,data2)) ||
+            (psListGet(list,PS_LIST_TAIL) == data2) ) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Failed to remove PS_LIST_UNKNOWN @ PS_LIST_TAIL");
+        return 1;
+    }
+
+    if (list->n != --items) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
+        return 1;
+    }
+    // Test requirement SDR-176
+    if (psMemGetRefCounter(data2) != 1) {
+        psError(PS_ERR_UNKNOWN, true,"psListRemoveData didn't decrement the data reference count.");
+        return 20;
+    }
+    psMemDecrRefCounter(data2);
+
+    data = psListGet(list,1);
+    if ( (! psListRemoveData(list,data))||
+            (psListGet(list,1) == data) ) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Failed to remove data @ which=1");
+        return 1;
+    }
+
+    if (list->n != --items) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
+        return 1;
+    }
+
+    // 8. data!=NULL and data!=any element in list
+    psLogMsg(__func__,PS_LOG_INFO,"Next message should be an error");
+    // Test requirement SDR-764
+    if ( psListRemoveData(list,data) ) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"removed something for PS_LIST_UNKNOWN with previously removed item");
+        return 1;
+    }
+
+    if (list->n != items) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"size not %d, as expected.",items);
+        return 1;
+    }
+
+    // clear out the list
+    while (items > 1) {
+        psListRemove(list,PS_LIST_HEAD);
+        items--;
+    }
+
+    data = psListGet(list,PS_LIST_HEAD);
+    if ( (! psListRemove(list,PS_LIST_HEAD)) ||
+            (psListGet(list,PS_LIST_HEAD) == data) ) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Failed to remove last element");
+        return 1;
+    }
+
+    if (list->n != 0) {
+        printListInt(list);
+        psError(PS_ERR_UNKNOWN, true,"Didn't remove all the elements.");
+        return 1;
+    }
+
+    psLogMsg(__func__,PS_LOG_INFO,"NULL list in psListRemoveData should generate error message.");
+    if(psListRemoveData(NULL,data)) {
+        psError(PS_ERR_UNKNOWN,true,"psListRemoveData should have generated an error with NULL list");
+        return 2;
+    }
+
+    psFree(list);
+
+    return 0;
+
+}
+
+psS32 testListConvert(void)
+{
+
+    psList* list = NULL;
+    psArray* arr = NULL;
+    psS32* data;
+
+    /*
+        array=psListToArray(list) shall take each element of the list, increment
+        their reference, and insert them into the returned fresh psPtr array.
+        The list shall not be changed, except for the element reference increment.
+    */
+
+    // test dlist -> array
+
+    // create a list
+    list = psListAlloc(NULL);
+    for (psS32 lcv=0;lcv<15;lcv++) {
+        data = psAlloc(sizeof(psS32));
+        *data = lcv;
+        psListAdd(list,PS_LIST_TAIL,data);
+        psMemDecrRefCounter(data);
+    }
+
+    arr = psListToArray(list);
+
+    if (arr->n != 15 || list->n != 15) {
+        psError(PS_ERR_UNKNOWN, true,"The created array didn't have the proper size");
+        return 1;
+    }
+    for (psS32 i=0;i<arr->n;i++) {
+        if (i != *(psS32*)arr->data[i]) {
+            psError(PS_ERR_UNKNOWN, true,"Element %d of array is incorrect (%d).",
+                    i,*(psS32*)arr->data[i]);
+            return 1;
+        }
+        if (i != *(psS32*)psListGet(list,i)) {
+            psError(PS_ERR_UNKNOWN, true,"Element %d of list is incorrect (%d).",
+                    i,*(psS32*)arr->data[i]);
+            return 1;
+        }
+        if (psMemGetRefCounter(arr->data[i]) != 2) {
+            psError(PS_ERR_UNKNOWN, true,"Element %d had wrong reference count (%ld).",
+                    i,psMemGetRefCounter(arr->data[i]));
+            return 1;
+        }
+    }
+
+    psFree(arr);
+    psFree(list);
+
+    // test array -> dlist
+
+    // create an array
+    arr = psArrayAlloc(15);
+    arr->n = arr->nalloc;
+    for (psS32 lcv=0;lcv<15;lcv++) {
+        data = psAlloc(sizeof(psS32));
+        *data = lcv;
+        arr->data[lcv] = data;
+    }
+
+    list = psArrayToList(arr);
+
+    if (arr->n != 15 || list->n != 15) {
+        psError(PS_ERR_UNKNOWN, true,"The created array didn't have the proper size");
+        return 1;
+    }
+    for (psS32 i=0;i<arr->n;i++) {
+        if (i != *(psS32*)arr->data[i]) {
+            psError(PS_ERR_UNKNOWN, true,"Element %d of array is incorrect (%d).",
+                    i,*(psS32*)arr->data[i]);
+            return 1;
+        }
+        if (i != *(psS32*)psListGet(list,i)) {
+            psError(PS_ERR_UNKNOWN, true,"Element %d of list is incorrect (%d).",
+                    i,*(psS32*)arr->data[i]);
+            return 1;
+        }
+        if (psMemGetRefCounter(arr->data[i]) != 2) {
+            psError(PS_ERR_UNKNOWN, true,"Element %d had wrong reference count (%ld).",
+                    i,psMemGetRefCounter(arr->data[i]));
+            return 1;
+        }
+    }
+
+    psFree(arr);
+    psFree(list);
+
+    // now, make sure if input array/list is NULL, output is NULL
+
+    arr = psListToArray(NULL);
+    if (arr != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psListToArray didn't return NULL when given NULL");
+        return 1;
+    }
+
+    list = psArrayToList(NULL);
+    if (list != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psArrayToList didn't return NULL when given NULL");
+        return 1;
+    }
+
+    // now, see what happens with a zero-size array/list
+    arr = psArrayAlloc(1);
+    arr->n = 0;
+    list = psArrayToList(arr);
+    if (list == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psArrayToList didn't create an empty list from an "
+                "empty array.");
+        return 1;
+    }
+    psFree(arr);
+    psFree(list);
+
+    list = psListAlloc(NULL);
+    arr = psListToArray(list);
+    if (arr == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psArrayToList didn't create an empty array from an "
+                "empty list.");
+        return 1;
+    }
+    psFree(arr);
+    psFree(list);
+
+    return 0;
+}
+
+psS32 testListIterator(void)
+{
+    psList* list = NULL;
+    psS32* data;
+
+    psLogMsg(__func__,PS_LOG_INFO," psListSetIterator/psListGetNext/psListGetPrev"
+             " shall move the list cursor to the specified location");
+
+    /*
+            psDlistSetIterator(list,where) shall:
+
+            1. output error message and do nothing if list=NULL
+            2. set list.cursor to list.head if where=PS_LIST_HEAD
+            3. set list.cursor to list.tail if where=PS_LIST_TAIL
+            4. set list.cursor to list.cursor->next if where=PS_LIST_NEXT
+            5. set list.cursor to list.cursor->prev if where=PS_LIST_PREV
+            6. leave list.cursor untouched if where=PS_LIST_UNKNOWN or PS_LIST_CURRENT
+
+            psDlistGetNext(list) shall be functionally equivalent to
+            psDlistSetIterator(list,PS_LIST_NEXT) but returns list.cursor.
+
+            psDlistGetPrev(list) shall be functionally equivalent to
+            psDlistSetIterator(list,PS_LIST_PREV) but returns list.cursor.
+    */
+
+    // create a list
+    list = psListAlloc(NULL);
+    for (psS32 lcv=0;lcv<15;lcv++) {
+        data = psAlloc(sizeof(psS32));
+        *data = lcv;
+        psListAdd(list,PS_LIST_TAIL,data);
+        psFree(data);
+    }
+
+    psListIterator* iter = psListIteratorAlloc(list,PS_LIST_HEAD,true);
+    if (iter == NULL) {
+        psError(PS_ERR_UNKNOWN, true,"Failed to make an iterator.");
+        return 22;
+    }
+
+    // 1. output error message and do nothing if iterator=NULL
+    psListIteratorSet(NULL,PS_LIST_HEAD);
+    if (psListIteratorSet(NULL,PS_LIST_HEAD)) {
+        psError(PS_ERR_UNKNOWN, true,"Success while setting position of a NULL iterator?");
+        return 23;
+    }
+
+    // Attempt to get next with NULL iterator
+    if ( psListGetAndIncrement(NULL) != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL when NULL iterator specified");
+        return 24;
+    }
+
+    // Attempt to get previous with NULL iterator
+    if( psListGetAndDecrement(NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL when NULL iterator specified");
+        return 25;
+    }
+
+    // 3. set list.cursor to list.tail if where=PS_LIST_TAIL
+
+    if (!psListIteratorSet(iter,PS_LIST_TAIL) ||
+            *(psS32*)iter->cursor->data != 14 ||
+            iter->index != 14) {
+        psError(PS_ERR_UNKNOWN, true,"Didn't successfully move cursor to tail.");
+        return 1;
+    }
+
+    // 2. set list.cursor to list.head if where=PS_LIST_HEAD
+    if (!psListIteratorSet(iter,PS_LIST_HEAD) ||
+            *(psS32*)iter->cursor->data != 0 ||
+            iter->index != 0) {
+        psError(PS_ERR_UNKNOWN, true,"Didn't successfully move cursor to head.");
+        return 2;
+    }
+
+    // test psListGetPrevious/Next
+    if (*(psS32*)psListGetAndIncrement(iter) != 0) {
+        psError(PS_ERR_UNKNOWN, true,"psListGetNext didn't move cursor to next.");
+        return 8;
+    }
+    if (*(psS32*)psListGetAndIncrement(iter) != 1) {
+        psError(PS_ERR_UNKNOWN, true,"psListGetNext didn't move cursor to next.");
+        return 9;
+    }
+    if (*(psS32*)psListGetAndIncrement(iter) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListGetNext didn't move cursor to next.");
+        return 10;
+    }
+
+    if (*(psS32*)psListGetAndDecrement(iter) != 3) {
+        psError(PS_ERR_UNKNOWN, true,"psListGetPrevious didn't move cursor to previous.");
+        return 11;
+    }
+    if (*(psS32*)psListGetAndDecrement(iter) != 2) {
+        psError(PS_ERR_UNKNOWN, true,"psListGetPrevious didn't move cursor to previous.");
+        return 12;
+    }
+    if (*(psS32*)psListGetAndDecrement(iter) != 1) {
+        psError(PS_ERR_UNKNOWN, true,"psListGetPrevious didn't move cursor to previous..");
+        return 13;
+    }
+    if (*(psS32*)psListGetAndDecrement(iter) != 0) {
+        psError(PS_ERR_UNKNOWN, true,"psListGetPrevious didn't move cursor to previous..");
+        return 14;
+    }
+    if (psListGetAndDecrement(iter) != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psListGetPrevious moved cursor beyond head.");
+        return 15;
+    }
+    if (psListGetAndIncrement(iter) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psListGetNext should return NULL when above head");
+        return 22;
+    }
+    if (*(psS32*)psListGetAndIncrement(iter) != 0 ) {
+        psError(PS_ERR_UNKNOWN,true,"psListGetNext didn't move cursor to next.");
+        return 23;
+    }
+
+    psListIteratorSet(iter,PS_LIST_TAIL); // works according to an above test
+    if (*(psS32*)psListGetAndIncrement(iter) != 14) {
+        psError(PS_ERR_UNKNOWN, true,"psListGetNext moved cursor beyond tail.");
+        return 16;
+    }
+    if ((psListGetAndIncrement(iter) != NULL) || (!iter->offEnd)) {
+        psError(PS_ERR_UNKNOWN, true,"psListGetNext moved cursor beyond tail.");
+        return 17;
+    }
+    if(psListGetAndDecrement(iter) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"psListGetPrevious did not return NULL when offEnd is true");
+        return 18;
+    }
+    if(iter->offEnd) {
+        psError(PS_ERR_UNKNOWN,true,"psListGetPrevious did not move back onto the list.");
+        return 19;
+    }
+    if(*(psS32*)psListGetAndDecrement(iter) != 14) {
+        psError(PS_ERR_UNKNOWN,true,"psListGetPrevious did not return correct value of tail.");
+        return 20;
+    }
+
+    psFree(list);
+
+    return 0;
+}
+
+psS32 testListFree(void)
+{
+    float* data[15];
+    psList* list;
+
+    /*
+    Call psDlistAlloc to create a doubly linked list.
+
+    Create several data items and add them to the list.
+
+    Verify the data item's reference counter are incremented after adding to the list.
+    (not really needed, as psListAdd is tested elsewhere)
+    */
+
+    list = psListAlloc(NULL);
+    for (psS32 lcv=0;lcv<15;lcv++) {
+        data[lcv] = psAlloc(sizeof(psS32));
+        *data[lcv] = lcv;
+        psListAdd(list,PS_LIST_TAIL,data[lcv]);
+        if (psMemGetRefCounter(data[lcv]) != 2) {
+            psError(PS_ERR_UNKNOWN, true,"Reference counter for data was not incremented");
+            return 1;
+        }
+    }
+
+    /*
+    Verify items are within the list.
+    (not needed, as psListAdd is tested elsewhere, but check anyway.)
+    */
+    if (list->n != 15) {
+        psError(PS_ERR_UNKNOWN, true,"List wasn't populated as expected?");
+        return 2;
+    }
+
+    /*
+    Call psDlistFree with NULL specified as the elemFree function.
+    */
+
+    psFree(list);
+
+    /*
+    Verify the list is deallocated but not the data items.
+    (accomplished by checking for memory leaks)
+
+    Verify the data item's reference counters are decremented by one compared to when added to the list.
+    (technically, this could be accomplished by checking memory leaks too, but I checked anyway)
+    */
+
+    for (psS32 i=0;i<15;i++) {
+        if (psMemGetRefCounter(data[i]) != 1) {
+            psError(PS_ERR_UNKNOWN, true,"pslistFree didn't decrement the data item's reference counter");
+            return 3;
+        }
+        psFree(data[i]);
+    }
+
+    return 0;
+}
+
+psS32 testListSort(void)
+{
+    psList* list;
+    psListIterator* iter;
+    float* fValue;
+    psU32* uValue;
+
+    list = psListAlloc(NULL);
+
+    for (psS32 lcv=0;lcv<15;lcv++) {
+        float* data = psAlloc(sizeof(psS32));
+        if (lcv < 7) {
+            *data = 13-lcv*2;
+        } else {
+            *data = lcv*2-12;
+        }
+        psListAdd(list,PS_LIST_TAIL,data);
+        psFree(data);
+
+    }
+
+    printf("original list = [");
+    iter = psListIteratorAlloc(list,PS_LIST_HEAD,true);
+    while( (fValue=psListGetAndIncrement(iter)) != NULL ) {
+        printf(" %.1f",*fValue);
+    }
+    printf(" ]\n");
+
+    list = psListSort(list,psCompareF32Ptr);
+
+    printf("sorted list = [");
+    psListIteratorSet(iter,PS_LIST_HEAD);
+    while( (fValue=psListGetAndIncrement(iter)) != NULL ) {
+        printf(" %.1f",*fValue);
+    }
+    printf(" ]\n");
+
+    psListIteratorSet(iter,PS_LIST_HEAD);
+    float* prevFValue = psListGetAndIncrement(iter);
+    while( (fValue=psListGetAndIncrement(iter)) != NULL ) {
+        if (*prevFValue > *fValue) {
+            psError(PS_ERR_UNKNOWN, true,"Hey, the list wasn't sorted!?");
+            return 1;
+        }
+    }
+
+    list = psListSort(list,psCompareDescendingF32Ptr);
+
+    printf("descending sort list = [");
+    psListIteratorSet(iter,PS_LIST_HEAD);
+    while( (fValue=psListGetAndIncrement(iter)) != NULL ) {
+        printf(" %.1f",*fValue);
+    }
+    printf(" ]\n");
+
+    psListIteratorSet(iter,PS_LIST_HEAD);
+    prevFValue = psListGetAndIncrement(iter);
+    while( (fValue=psListGetAndIncrement(iter)) != NULL ) {
+        if (*prevFValue < *fValue) {
+            psError(PS_ERR_UNKNOWN, true,"Hey, the list wasn't sorted!?");
+            return 2;
+        }
+    }
+
+    psFree(list);
+
+    /********** OK, now do the same thing with ints. *************/
+
+    list = psListAlloc(NULL);
+
+    for (psS32 lcv=0;lcv<15;lcv++) {
+        psU32* data = psAlloc(sizeof(psS32));
+        if (lcv < 7) {
+            *data = 13-lcv*2;
+        } else {
+            *data = lcv*2-12;
+        }
+        psListAdd(list,PS_LIST_TAIL,data);
+        psFree(data);
+
+    }
+
+    printf("original list = [");
+    iter = psListIteratorAlloc(list,PS_LIST_HEAD,true);
+    while( (uValue=psListGetAndIncrement(iter)) != NULL ) {
+        printf(" %d",*uValue);
+    }
+    printf(" ]\n");
+
+    list = psListSort(list,psCompareU32Ptr);
+
+    printf("sorted list = [");
+    psListIteratorSet(iter,PS_LIST_HEAD);
+    while( (uValue=psListGetAndIncrement(iter)) != NULL ) {
+        printf(" %d",*uValue);
+    }
+    printf(" ]\n");
+
+    psListIteratorSet(iter,PS_LIST_HEAD);
+    psU32* prevUValue = psListGetAndIncrement(iter);
+    while( (uValue=psListGetAndIncrement(iter)) != NULL ) {
+        if (*prevUValue > *uValue) {
+            psError(PS_ERR_UNKNOWN, true,"Hey, the list wasn't sorted!?");
+            return 3;
+        }
+    }
+
+    list = psListSort(list,psCompareDescendingU32Ptr);
+
+    printf("descending sort list = [");
+    psListIteratorSet(iter,PS_LIST_HEAD);
+    while( (uValue=psListGetAndIncrement(iter)) != NULL ) {
+        printf(" %d",*uValue);
+    }
+    printf(" ]\n");
+
+    psListIteratorSet(iter,PS_LIST_HEAD);
+    prevUValue = psListGetAndIncrement(iter);
+    while( (uValue=psListGetAndIncrement(iter)) != NULL ) {
+        if (*prevUValue < *uValue) {
+            psError(PS_ERR_UNKNOWN, true,"Hey, the list wasn't sorted!?");
+            return 4;
+        }
+    }
+
+    psFree(list);
+
+    return 0;
+}
+
+psS32 testListLength( void )
+{
+    psArray *temp = psArrayAlloc(5);
+    psList *list = psListAlloc(temp);
+
+    if (psListLength(list) != 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psListLength failed to return the correct length of list.\n");
+        return 1;
+    }
+    list->n = 5;
+    if (psListLength(list) != 5) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psListLength failed to return the correct length of list.\n");
+        return 2;
+    }
+    list->n++;
+    if (psListLength(list) != 6) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psListLength failed to return the correct length of list.\n");
+        return 3;
+    }
+    psFree(temp);
+    psFree(list);
+
+    psList *emptyList = NULL;
+    psVector *vector = psVectorAlloc(5, PS_TYPE_F32);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psListLength(emptyList) != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psListLength failed to return -1 for a NULL input list.\n");
+        return 4;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psListLength((psList*)vector) != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psListLength failed to return -1 for an invalid input list.\n");
+        return 5;
+    }
+    psFree(vector);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psLookupTable_01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psLookupTable_01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psLookupTable_01.c	(revision 22322)
@@ -0,0 +1,855 @@
+/** @file  tst_psLookupTable_01.c
+*
+*  @brief Test driver for psLookupTable functions
+*
+*  This test driver contains the following tests for psLookupTable functions:
+*     testLookupAlloc - test allocation and freeing of psLookupTable
+*     testLookupRead - test different files and types within files
+*                      being read correctly and some failure cases
+*     testLookupTableInterpolate - test the interpolation function
+*     testLookupTableInterpolateAll - test interpolation of all row values
+*
+*  @author  Ross Harman, MHPCC
+*
+*  @version $Revision: 1.5 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2006-06-13 23:55:34 $
+*
+*  Copyright 2004-5 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+
+#include "pslib_strict.h"
+#include "psTest.h"
+#include <string.h>
+
+static psS32 testLookupTableAlloc(void);
+static psS32 testVectorsReadFromFile(void);
+static psS32 testLookupTableImport(void);
+static psS32 testLookupTableRead(void);
+static psS32 testLookupTableInterpolate(void);
+static psS32 testLookupTableInterpolateAll(void);
+
+const psF64 errorTol_psF64 = 1.0e-4;
+const psF64 tableU8_validFrom = 0;
+const psF64 tableU8_validTo = 3;
+const psS32 tableU8_size = 4;
+const char tableU8_format[] = "\%d \%d \%d \%ld \%d \%d \%d \%ld \%f \%lf";
+const char tableU8_filename[] = VERIFIED_DIR "/tableU8.dat";
+const psS32 tableU8_indexCol = 0;
+const psS32 tableU8_index[] =
+    {
+        0,   1,    2,   3
+    };
+const psU16 tableU8_val1[]  =
+    {
+        2,   4,    6,   8
+    };
+const psU32 tableU8_val2[]  =
+    {
+        4,   8,   12,  16
+    };
+const psU64 tableU8_val3[]  =
+    {
+        8,  16,   24,  32
+    };
+const psS8  tableU8_val4[]  =
+    {
+        0,  -1,   -2,  -3
+    };
+const psS16 tableU8_val5[]  =
+    {
+        -2,  -4,   -6,  -8
+    };
+const psS32 tableU8_val6[]  =
+    {
+        -4,  -8,  -12, -16
+    };
+const psS64 tableU8_val7[]  =
+    {
+        -8, -16,  -24, -32
+    };
+const psF32 tableU8_val8[]  =
+    {
+        -0.5, 0.0,  0.5,0.75
+    };
+const psF64 tableU8_val9[]  =
+    {
+        -1.5,-1.0,-0.25,1.75
+    };
+
+const psF64 tableF32_validFrom = -10.05;
+const psF64 tableF32_validTo = 3500.67;
+const psS32 tableF32_size = 4;
+const psS32 tableF32_cols = 9;
+const char tableF32_filename[] = VERIFIED_DIR "/tableF32.dat";
+const char tableF32_format[] = "\%f \%d \%d \%ld \%d \%d \%d \%ld \%d \%lf";
+const char tableF32_indexCol = 0;
+const psElemType tableF32_colType[] =
+    {
+        PS_TYPE_F32, PS_TYPE_S32, PS_TYPE_S32, PS_TYPE_S64,
+        PS_TYPE_S32, PS_TYPE_S32, PS_TYPE_S32, PS_TYPE_S64,
+        PS_TYPE_F64
+    };
+const psF32 tableF32_index[] =
+    {
+        -10.05,1.009,23.45,3500.67
+    };
+const psS32 tableF32_col1[] =
+    {
+        2, 4, 6, 8
+    };
+const psS32 tableF32_col2[] =
+    {
+        4, 8, 12, 16
+    };
+const psS64 tableF32_col3[] =
+    {
+        8, 16, 24, 32
+    };
+const psS32 tableF32_col4[] =
+    {
+        0, -1, -2, -3
+    };
+const psS32 tableF32_col5[] =
+    {
+        -2, -4, -6, -8
+    };
+const psS32 tableF32_col6[] =
+    {
+        -4, -8, -12, -16
+    };
+const psS64 tableF32_col7[] =
+    {
+        -8, -16, -24, -32
+    };
+const psF64 tableF32_col8[] =
+    {
+        -1.5, -1.0, -0.25, 1.75
+    };
+
+
+const psF64 table10_validFrom = 1;
+const psF64 table10_validTo   = 10;
+const psS32 table10_size      = 10;
+const char table10_format[] = "\%d \%d \%d \%ld \%d \%d \%d \%ld \%f \%lf";
+const char table10_filename[] = VERIFIED_DIR "/table10.dat";
+const psS32 table10_indexCol = 0;
+const psS32  table10_index[]   =
+    {
+        1,   2,    3,   4,    5,    6,    7,    8,   9,    10
+    };
+const psS32 table10_val1[]    =
+    {
+        4,   6,    8,  10,   12,   14,   16,   18,   20,   22
+    };
+const psS32 table10_val2[]    =
+    {
+        8,  12,   16,  20,   24,   28,   32,   36,   40,   44
+    };
+const psS64 table10_val3[]    =
+    {
+        16,  24,   32,  64,  128,  256,  512, 1024, 2048, 4096
+    };
+const psS32  table10_val4[]    =
+    {
+        -1,  -2,   -3,  -4,   -5,   -6,   -7,   -8,   -9,  -10
+    };
+const psS32 table10_val5[]    =
+    {
+        -4,  -6,   -8, -10,  -12,  -14,  -16,  -18,  -20,  -22
+    };
+const psS32 table10_val6[]    =
+    {
+        -8, -12,  -16, -20,  -24,  -28,  -32,  -36,  -40,  -44
+    };
+const psS64 table10_val7[]    =
+    {
+        -16, -24,  -32, -64, -128, -256, -512,-1024,-2048,-4096
+    };
+const psF32 table10_val8[]    =
+    {
+        -2.25,-1.5,-0.75, 0.0, 0.75, 1.50, 2.25, 3.00, 3.75, 4.50
+    };
+const psF64 table10_val9[]    =
+    {
+        -2.67,0.66, 3.99,7.32,10.65,13.98,17.31,20.64,23.97,27.30
+    };
+const psF64 interpolVal1[]    =
+    {
+        5.25, 12.5, 25, 160, -5.25, -12.5, -25, -160, 0.9375, 11.4825
+    };
+const psF64 interpolVal2[]    =
+    {
+        5, 12, 24, 128, -5, -12, -24, -128, 0.75, 10.65
+    };
+const psF64 interpolVal3[]    =
+    {
+        4, 8, 16, -1, -4, -8, -16, -2.25, -2.67
+    };
+
+testDescription tests[] = {
+                              {testLookupTableAlloc,817,"psLookupTableAlloc",0,false},
+                              {testVectorsReadFromFile,999,"psVectorsReadFromFile",0,false},
+                              {testLookupTableImport,666,"psLookupTableImport",0,false},
+                              {testLookupTableRead,998,"psLookupTableRead",0,false},
+                              {testLookupTableInterpolate,997,"psLookupTableInterpolate",0,false},
+                              {testLookupTableInterpolateAll,996,"psLookupTableInterpolateAll",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    if ( ! runTestSuite(stderr,"psLookupTable",tests,argc,argv)) {
+        return 1;
+    }
+    return 0;
+}
+
+psS32 testLookupTableAlloc(void)
+{
+    psLookupTable*  table1 = NULL;
+
+    // Allocate lookup table with valid parameters
+    table1 = psLookupTableAlloc("tableF32.dat","\%f \%lf \%d \%ld",10);
+    if(table1 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Null lookup table generated from valid parameters");
+        return 1;
+    }
+    if(strcmp(table1->filename,"tableF32.dat") != 0) {
+        psError(PS_ERR_UNKNOWN,true,"File name not properly stored in psLookupTable structure.");
+        return 2;
+    }
+    if(strcmp(table1->format,"\%f \%lf \%d \%ld") != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Format string not properly storeed in psLookupTable structure.");
+        return 3;
+    }
+    if(table1->indexCol != 10) {
+        psError(PS_ERR_UNKNOWN,true,"Member indexCol not set properly");
+        return 3;
+    }
+    psFree(table1);
+
+    // Allocate lookup table with invalid filename
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid file name");
+    table1 = psLookupTableAlloc(NULL,"\%d",3);
+    if(table1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Null file name accepted by psLookupTableAlloc");
+        return 4;
+    }
+
+    // Allocate lookup table with invalid format
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid format string");
+    table1 = psLookupTableAlloc("tableF32.dat",NULL,3);
+    if(table1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Null format string accepted by psLookupTableAlloc");
+        return 5;
+    }
+
+    return 0;
+}
+
+psS32 testVectorsReadFromFile(void)
+{
+    psArray*   out        = NULL;
+    psVector*  tempVector = NULL;
+
+    // Read file and place into an array of vectors
+    out = psVectorsReadFromFile(VERIFIED_DIR "/tableF32.dat","\%f \%d \%d \%ld \%d \%d \%d \%ld \%*d \%lf");
+    if(out == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Unable to read file into array of vectors");
+        return 1;
+    }
+    // Verify the number of vectors in array is as expected
+    if(out->n != tableF32_cols) {
+        psError(PS_ERR_UNKNOWN,true,"Expected number of columns = %d not equal to actual = %d",
+                tableF32_cols, out->n);
+        return 2;
+    }
+    // Verify the number of entries in vectors is as expected
+    for(int i = 0; i < out->n; i++ ) {
+        tempVector = out->data[i];
+        if(tempVector->n != tableF32_size) {
+            psError(PS_ERR_UNKNOWN,true,"Col #%d Expected number of entries = %d  not equal to actual = %d",
+                    i,tableF32_size,tempVector->n);
+            return 3;
+        }
+    }
+    // Verify the vector types are as expected
+    for(int i = 0; i < out->n; i++) {
+        tempVector = out->data[i];
+        if(tempVector->type.type != tableF32_colType[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Col #%d Expected type = %d not equal to actual = %d",
+                    i,tableF32_colType[i],tempVector->type.type);
+            return 4;
+        }
+    }
+    // Verify the values in the vectors are as expected
+    tempVector = out->data[0];
+    for(int i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.F32[i] != tableF32_index[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Col #0 Vector element[%d] expected = %f not equal to actual = %f",
+                    i, tableF32_index[i],tempVector->data.F32[i]);
+            return 5 + i;
+        }
+    }
+    // Verify the values in the vectors are as expected
+    tempVector = out->data[1];
+    for(int i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S32[i] != tableF32_col1[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Col #1 Vector element[%d] expected = %d not equal to actual = %d",
+                    i, tableF32_col1[i],tempVector->data.S32[i]);
+            return 10 + i;
+        }
+    }
+    // Verify the values in the vectors are as expected
+    tempVector = out->data[2];
+    for(int i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S32[i] != tableF32_col2[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Col #2 Vector element[%d] expected = %d not equal to actual = %d",
+                    i, tableF32_col2[i],tempVector->data.S32[i]);
+            return 15 + i;
+        }
+    }
+    // Verify the values in the vectors are as expected
+    tempVector = out->data[3];
+    for(int i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S64[i] != tableF32_col3[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Col #3 Vector element[%d] expected = %ld not equal to actual = %ld",
+                    i, tableF32_col3[i],tempVector->data.S64[i]);
+            return 20 + i;
+        }
+    }
+    // Verify the values in the vectors are as expected
+    tempVector = out->data[4];
+    for(int i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S32[i] != tableF32_col4[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Col #4 Vector element[%d] expected = %d not equal to actual = %d",
+                    i, tableF32_col4[i],tempVector->data.S32[i]);
+            return 25 + i;
+        }
+    }
+    // Verify the values in the vectors are as expected
+    tempVector = out->data[5];
+    for(int i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S32[i] != tableF32_col5[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Col #5 Vector element[%d] expected = %d not equal to actual = %d",
+                    i, tableF32_col5[i],tempVector->data.S32[i]);
+            return 30 + i;
+        }
+    }
+    // Verify the values in the vectors are as expected
+    tempVector = out->data[6];
+    for(int i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S32[i] != tableF32_col6[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Col #6 Vector element[%d] expected = %d not equal to actual = %d",
+                    i, tableF32_col6[i],tempVector->data.S32[i]);
+            return 35 + i;
+        }
+    }
+    // Verify the values in the vectors are as expected
+    tempVector = out->data[7];
+    for(int i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S64[i] != tableF32_col7[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Col #7 Vector element[%d] expected = %ld not equal to actual = %ld",
+                    i, tableF32_col7[i],tempVector->data.S64[i]);
+            return 40 + i;
+        }
+    }
+    // Verify the values in the vectors are as expected
+    tempVector = out->data[8];
+    for(int i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.F64[i] != tableF32_col8[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Col #8 Vector element[%d] expected = %lf not equal to actual = %lf",
+                    i, tableF32_col8[i],tempVector->data.F64[i]);
+            return 45 + i;
+        }
+    }
+    psFree(out);
+
+    // Attempt to read vectors from valid file with invalid format
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL format");
+    out = psVectorsReadFromFile(VERIFIED_DIR "/tableF32.dat",NULL);
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with NULL format string");
+        return 50;
+    }
+
+    // Attempt to read vectors from invalid file with valid format
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL filename");
+    out = psVectorsReadFromFile(NULL,"%f %f");
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with NULL file name");
+        return 51;
+    }
+
+    // Attempt to read vectors from valid file with invalid format specifiers
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid format specifier");
+    out = psVectorsReadFromFile(VERIFIED_DIR "/tableF32.data","\%f \%c \%d");
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with invalid format specifier");
+        return 52;
+    }
+
+    // Attempt to read vectors from non-existant file
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for non-existant file");
+    out = psVectorsReadFromFile("nonexistant.dat","\%f \%d \%d");
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with non-existant file");
+        return 53;
+    }
+
+    // Attempt to read vectors from file with errors in the numbers
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for error is file");
+    out = psVectorsReadFromFile(VERIFIED_DIR "/tableF32_err.dat","\%f \%d \%d \%ld \%d \%d \%d \%ld \%d \%lf");
+    if(out != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with file with errors");
+        return 54;
+    }
+
+    return 0;
+}
+
+psS32 testLookupTableImport(void)
+{
+    psLookupTable*  outTable = NULL;
+    psLookupTable*  inTable  = NULL;
+    psArray*        vectors  = NULL;
+
+    // Attempt to import table with NULL table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL table argument");
+    outTable = psLookupTableImport(NULL,vectors,1);
+    if(outTable != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with NULL input table");
+        return 1;
+    }
+
+    // Allocate valid table, format string and index column
+    inTable = psLookupTableAlloc(table10_filename,table10_format,table10_indexCol);
+
+    // Attempt to import table with NULL vector argument
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL vectors");
+    outTable = psLookupTableImport(inTable,vectors,table10_indexCol);
+    if(outTable != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with NULL input vectors");
+        return 2;
+    }
+
+    // Allocate valid array
+    vectors = psArrayAlloc(10);
+
+    // Attempt to import table with invalid index column
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid index column");
+    outTable = psLookupTableImport(inTable,vectors,-1);
+    if(outTable != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL with invalid index column specified");
+        return 3;
+    }
+    psFree(vectors);
+
+    // Attempt to import file with file with unsorted index vector
+    // Read vectors from file
+    vectors = psVectorsReadFromFile(table10_filename,table10_format);
+    outTable = psLookupTableImport(inTable,vectors,table10_indexCol);
+    if(outTable != inTable) {
+        psError(PS_ERR_UNKNOWN,true,"Did not set proper return value");
+        return 4;
+    }
+    // Verify the index column vector
+    psVector* indexVector = outTable->index;
+    for(int i = 0; i < indexVector->n; i++) {
+        if(indexVector->data.S32[i] != table10_index[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Index value[%d] = %d is not as expected %d",
+                    i,indexVector->data.S32[i],table10_index[i]);
+            return i*4;
+        }
+    }
+    // Verify the value array vectors
+    psVector* valueVector = outTable->values->data[1];
+    for(int i = 0; i < valueVector->n; i++) {
+        if(valueVector->data.S32[i] != table10_val1[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value [%d] = %d is not as expected %d",
+                    i,valueVector->data.S32[i],table10_val1[i]);
+            return i*5;
+        }
+    }
+    valueVector = outTable->values->data[2];
+    for(int i = 0; i < valueVector->n; i++) {
+        if(valueVector->data.S32[i] != table10_val2[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value [%d] = %d is not as expected %d",
+                    i,valueVector->data.S32[i],table10_val2[i]);
+            return i*6;
+        }
+    }
+    valueVector = outTable->values->data[3];
+    for(int i = 0; i < valueVector->n; i++) {
+        if(valueVector->data.S64[i] != table10_val3[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value [%d] = %ld is not as expected %ld",
+                    i,valueVector->data.S64[i],table10_val3[i]);
+            return i*7;
+        }
+    }
+    valueVector = outTable->values->data[4];
+    for(int i = 0; i < valueVector->n; i++) {
+        if(valueVector->data.S32[i] != table10_val4[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value [%d] = %d is not as expected %d",
+                    i,valueVector->data.S32[i],table10_val4[i]);
+            return i*8;
+        }
+    }
+    valueVector = outTable->values->data[5];
+    for(int i = 0; i < valueVector->n; i++) {
+        if(valueVector->data.S32[i] != table10_val5[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value [%d] = %d is not as expected %d",
+                    i,valueVector->data.S32[i],table10_val5[i]);
+            return i*9;
+        }
+    }
+    valueVector = outTable->values->data[6];
+    for(int i = 0; i < valueVector->n; i++) {
+        if(valueVector->data.S32[i] != table10_val6[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value [%d] = %d is not as expected %d",
+                    i,valueVector->data.S32[i],table10_val6[i]);
+            return i*10;
+        }
+    }
+    valueVector = outTable->values->data[7];
+    for(int i = 0; i < valueVector->n; i++) {
+        if(valueVector->data.S64[i] != table10_val7[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value [%d] = %ld is not as expected %ld",
+                    i,valueVector->data.S64[i],table10_val7[i]);
+            return i*11;
+        }
+    }
+    valueVector = outTable->values->data[8];
+    for(int i = 0; i < valueVector->n; i++) {
+        if(valueVector->data.F32[i] != table10_val8[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value [%d] = %f is not as expected %f",
+                    i,valueVector->data.F32[i],table10_val8[i]);
+            return i*12;
+        }
+    }
+    valueVector = outTable->values->data[9];
+    for(int i = 0; i < valueVector->n; i++) {
+        if(valueVector->data.F64[i] != table10_val9[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value [%d] = %lf is not as expected %lf",
+                    i,valueVector->data.F64[i],table10_val9[i]);
+            return i*13;
+        }
+    }
+    // Verify the table members are set properly
+    if(outTable->indexCol != table10_indexCol) {
+        psError(PS_ERR_UNKNOWN,true,"Member indexCol = %d not as expected %d",
+                outTable->indexCol,table10_indexCol);
+        return 100;
+    }
+    if(outTable->validFrom != table10_validFrom) {
+        psError(PS_ERR_UNKNOWN,true,"Member validFrom = %d not as expected %d",
+                outTable->validFrom, table10_validFrom);
+        return 101;
+    }
+    if(outTable->validTo != table10_validTo) {
+        psError(PS_ERR_UNKNOWN,true,"Member validTo = %d not as expected %d",
+                outTable->validTo, table10_validTo);
+        return 102;
+    }
+    if(strcmp(outTable->filename,table10_filename) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Member filename = %d not as expected %s",
+                outTable->filename,table10_filename);
+        return 103;
+    }
+    if(strcmp(outTable->format,table10_format) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Member format = %d not as expected %s",
+                outTable->format,table10_format);
+        return 104;
+    }
+    psFree(vectors);
+    psFree(inTable);
+
+    // Attempt to import file with file with sorted index vector
+    // Read vectors from file
+    // Allocate valid table, format string and index column
+    inTable = psLookupTableAlloc(tableU8_filename,tableU8_format,tableU8_indexCol);
+    vectors = psVectorsReadFromFile(tableU8_filename,tableU8_format);
+    outTable = psLookupTableImport(inTable,vectors,tableU8_indexCol);
+    if(outTable != inTable) {
+        psError(PS_ERR_UNKNOWN,true,"Did not set proper return value");
+        return 4;
+    }
+    // Verify the index column vector
+    indexVector = outTable->index;
+    for(int i = 0; i < indexVector->n; i++) {
+        if(indexVector->data.S32[i] != tableU8_index[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Index value[%d] = %d is not as expected %d",
+                    i,indexVector->data.S32[i],tableU8_index[i]);
+        }
+    }
+    // Verify the table members are set properly
+    if(outTable->indexCol != tableU8_indexCol) {
+        psError(PS_ERR_UNKNOWN,true,"Member indexCol = %d not as expected %d",
+                outTable->indexCol,tableU8_indexCol);
+        return 100;
+    }
+    if(outTable->validFrom != tableU8_validFrom) {
+        psError(PS_ERR_UNKNOWN,true,"Member validFrom = %d not as expected %d",
+                outTable->validFrom, tableU8_validFrom);
+        return 101;
+    }
+    if(outTable->validTo != tableU8_validTo) {
+        psError(PS_ERR_UNKNOWN,true,"Member validTo = %d not as expected %d",
+                outTable->validTo, tableU8_validTo);
+        return 102;
+    }
+    if(strcmp(outTable->filename,tableU8_filename) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Member filename = %d not as expected %s",
+                outTable->filename,tableU8_filename);
+        return 103;
+    }
+    if(strcmp(outTable->format,tableU8_format) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Member format = %d not as expected %s",
+                outTable->format,tableU8_format);
+        return 104;
+    }
+    psFree(inTable);
+    psFree(vectors);
+
+    return 0;
+}
+
+psS32 testLookupTableRead(void)
+{
+    psLookupTable*  table1  = NULL;
+    long           numRows = 0;
+
+    // Attempt to read table with NULL input table specified
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL table");
+    numRows = psLookupTableRead(table1);
+    if(numRows != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return zero for NULL input table");
+        return 1;
+    }
+
+    // Set up valid table to read
+    table1 = psLookupTableAlloc(tableF32_filename,tableF32_format,tableF32_indexCol);
+    // Read table
+    numRows = psLookupTableRead(table1);
+    // Verify return value equals number of lines read
+    if(numRows != tableF32_size) {
+        psError(PS_ERR_UNKNOWN,true,"Return value %d not as expected %d",
+                numRows,tableF32_size);
+        return 1;
+    }
+    // Verify the members and values in table
+    if(fabs(table1->validFrom - tableF32_validFrom) > errorTol_psF64) {
+        psError(PS_ERR_UNKNOWN,true,"Member validFrom = %f not as expected %f",
+                table1->validFrom,tableF32_validFrom);
+        return 2;
+    }
+    if(fabs(table1->validTo - tableF32_validTo) > errorTol_psF64) {
+        psError(PS_ERR_UNKNOWN,true,"Member validTo = %f not as expected %f",
+                table1->validTo,tableF32_validTo);
+        return 3;
+    }
+    if(strcmp(table1->filename,tableF32_filename) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Member filename %s not as expected %s",
+                table1->filename,tableF32_filename);
+        return 4;
+    }
+    if(strcmp(table1->format,tableF32_format) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Member format %s not as expected %s",
+                table1->format,tableF32_format);
+        return 5;
+    }
+    if(table1->indexCol != tableF32_indexCol) {
+        psError(PS_ERR_UNKNOWN,true,"Member indexCol %d not as expected %d",
+                table1->indexCol,tableF32_indexCol);
+        return 6;
+    }
+    for(psS32 i = 0; i < table1->index->n; i++) {
+        if(fabs(table1->index->data.F32[i]-tableF32_index[i]) > errorTol_psF64) {
+            psError(PS_ERR_UNKNOWN,true,"Index column[%d] = %f not as expected %f",
+                    i,table1->index->data.F32[i],tableF32_index[i]);
+            return i*7;
+        }
+    }
+    psVector* tempVector = table1->values->data[1];
+    for(psS32 i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S32[i] != tableF32_col1[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value column[%d] = %d not as expected %d",
+                    i,tempVector->data.S32[i],tableF32_col1[i]);
+            return i*8;
+        }
+    }
+    tempVector = table1->values->data[2];
+    for(psS32 i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S32[i] != tableF32_col2[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value column[%d] = %d not as expected %d",
+                    i,tempVector->data.S32[i],tableF32_col2[i]);
+            return i*9;
+        }
+    }
+    tempVector = table1->values->data[3];
+    for(psS32 i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S64[i] != tableF32_col3[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value column[%d] = %ld not as expected %ld",
+                    i,tempVector->data.S64[i],tableF32_col3[i]);
+            return i*10;
+        }
+    }
+    tempVector = table1->values->data[4];
+    for(psS32 i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S32[i] != tableF32_col4[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value column[%d] = %d not as expected %d",
+                    i,tempVector->data.S32[i],tableF32_col4[i]);
+            return i*11;
+        }
+    }
+    tempVector = table1->values->data[5];
+    for(psS32 i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S32[i] != tableF32_col5[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value column[%d] = %d not as expected %d",
+                    i,tempVector->data.S32[i],tableF32_col5[i]);
+            return i*12;
+        }
+    }
+    tempVector = table1->values->data[6];
+    for(psS32 i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S32[i] != tableF32_col6[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value column[%d] = %d not as expected %d",
+                    i,tempVector->data.S32[i],tableF32_col6[i]);
+            return i*13;
+        }
+    }
+    tempVector = table1->values->data[7];
+    for(psS32 i = 0; i < tempVector->n; i++) {
+        if(tempVector->data.S64[i] != tableF32_col7[i]) {
+            psError(PS_ERR_UNKNOWN,true,"Value column[%d] = %ld not as expected %ld",
+                    i,tempVector->data.S64[i],tableF32_col7[i]);
+            return i*14;
+        }
+    }
+    psFree(table1);
+
+    // Set up invalid table to read
+    table1 = psLookupTableAlloc(tableF32_filename,tableF32_format,tableF32_indexCol);
+    table1->indexCol = -1;
+    // Read invalid table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message invalid table indexCol");
+    numRows = psLookupTableRead(table1);
+    // Verify the num of rows read is zero
+    if(numRows != 0) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return 0 for line read for invalid table");
+        return 15;
+    }
+    psFree(table1);
+
+    return 0;
+}
+
+psS32 testLookupTableInterpolate(void)
+{
+    psLookupTable*  table1 = NULL;
+    psF64            out1 = 0.0;
+
+    // Attempt to perform interpolation with NULL table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for invalid table");
+    out1 = psLookupTableInterpolate(table1,0,0);
+    if( !isnan(out1) ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NaN as expected");
+        return 1;
+    }
+
+    // Interpolate values within the list and verify return values
+    table1 = psLookupTableAlloc(table10_filename,table10_format,table10_indexCol);
+    psS32 numLines = psLookupTableRead(table1);
+    if(numLines != table10_size) {
+        psError(PS_ERR_UNKNOWN,true,"Line read %d not as expected %d",numLines,table10_size);
+        return 2;
+    }
+    for(psS32 i = 0; i < table1->values->n; i++ ) {
+        out1 = psLookupTableInterpolate(table1, 5.25, i);
+        if( fabs(out1-interpolVal1[i]) > errorTol_psF64) {
+            psError(PS_ERR_UNKNOWN,true,"Did not return expected value. %lg expected %lg",out1,interpolVal1[i]);
+            return 3*i;
+        }
+    }
+    for(psS32 i = 0; i < table1->values->n; i++ ) {
+        out1 = psLookupTableInterpolate(table1, 5.0, i);
+        if( fabs(out1-interpolVal2[i]) > errorTol_psF64) {
+            psError(PS_ERR_UNKNOWN,true,"Did not return expected value. %lg expected %lg",out1,interpolVal2[i]);
+            return 4*i;
+        }
+    }
+
+    // Interpolate value just below the lowest index value
+    out1 = psLookupTableInterpolate(table1,0,0);
+    if ( fabs(out1- NAN) > FLT_EPSILON ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN returned %lf",out1);
+        return 5;
+    }
+
+    // Interpolate value just above the highest index value
+    out1 = psLookupTableInterpolate(table1,11,0);
+    if ( fabs(out1-NAN) > FLT_EPSILON ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN returned %lf",out1);
+        return 7;
+    }
+
+    // Interpolate value with a column number greater than the table has
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error out of range.");
+    out1 = psLookupTableInterpolate(table1, 5, 100);
+    if ( fabs(out1-NAN) > FLT_EPSILON ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NAN returned %lf",out1);
+        return 9;
+    }
+    psFree(table1);
+
+    return 0;
+}
+
+psS32 testLookupTableInterpolateAll(void)
+{
+    psLookupTable*  table1 = NULL;
+    psVector*        interpValues;
+
+    // Interpolate values with NULL table
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL table");
+    interpValues = psLookupTableInterpolateAll(table1,5);
+    if(interpValues != NULL ) {
+        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for NULL table");
+        return 5;
+    }
+
+    // Interpolate values within the list and verify return values
+    table1 = psLookupTableAlloc(table10_filename,table10_format,table10_indexCol);
+    psS32 numLines = psLookupTableRead(table1);
+    if(numLines != table10_size) {
+        psError(PS_ERR_UNKNOWN,true,"Num lines read %d not as expected %d",numLines,table10_size);
+        return 6;
+    }
+    interpValues = psLookupTableInterpolateAll(table1,5.25);
+    if(interpValues == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Returned vector is NULL.");
+        return 1;
+    }
+    for(psS32 i = 0; i < table1->values->n; i++ ) {
+        if( fabs(interpValues->data.F64[i]-interpolVal1[i]) > errorTol_psF64) {
+            psError(PS_ERR_UNKNOWN,true,"Did not return expected value. %lg expected %lg",
+                    interpValues->data.F64[i],interpolVal1[i]);
+            return 2*i;
+        }
+    }
+    psFree(interpValues);
+
+    // Interpolate values with index outside table
+    interpValues = psLookupTableInterpolateAll(table1,0);
+    if(interpValues != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did return NULL");
+        return 4;
+    }
+    psFree(table1);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadataIO.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadataIO.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadataIO.c	(revision 22322)
@@ -0,0 +1,1021 @@
+/** @file  tst_psMetadataIO.c
+ *
+ *  @brief Test driver for psMetadataIO functions
+ *
+ *  This test driver contains the following tests for psMetadata:
+ *    Read config file with overwrite set true
+ *    Read config file with overwrite set false
+ *    Attempt to use null fileName argument
+ *    Attempt to open nonexistant file
+ *
+ *  @author  Ross Harman, MHPCC
+ *  @author  Robert DeSonia, MHPCC
+ *  @author  Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.7 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-06-07 01:39:17 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testMetadataParseConfig(void);
+static psS32 testMetadataParseConfig1(void);
+static psS32 testMetadataParseConfig2(void);
+static psS32 testMetadataParseConfig3(void);
+static psS32 testMetadataParseConfig4(void);
+
+testDescription tests[] = {
+                              {testMetadataParseConfig,0,"psMetadataConfigParse",0,false},
+                              {testMetadataParseConfig1,1,"psMetadataConfigParse1",0,false},
+                              {testMetadataParseConfig2,2,"psMetadataConfigParse2",0,false},
+                              {testMetadataParseConfig3,3,"psMetadataConfigParse3",0,false},
+                              {testMetadataParseConfig4,4,"psMetadataConfigParse4",0,false},
+                              {NULL}
+                          };
+
+static void writeMetadataItem(psMetadataItem* metadataIem, char* indentStr);
+static void writeMetadata(psMetadata* metadata, char* indentStr);
+static void writeMetadataList(psList *metadataItemList, char* indentStr);
+
+static psBool checkFailedLines(psS32 failedLines, psS32 expectedFailedLines, char* configFile);
+static psBool checkNumberOfItems(psMetadata* md, psS32 expectedItems, char* configFile);
+static psBool checkItemName(psMetadataItem* mdItem, char* expectedName, char* configFile);
+static psBool checkItemType(psMetadataItem* mdItem, psS32 expectedType, char* configFile);
+static psBool checkItemComment(psMetadataItem* mdItem, char* expectedComment, char* configFile);
+
+#define ERROR_TOL  0.0001
+
+const char testConfig5[] = "test5.config";
+const psS32 testConfig5Fails = 7;
+
+const char testConfig4[] = "test4.config";
+const psS32 testConfig4Fails = 0;
+
+const char testConfig3[] = "test3.config";
+const psS32 testConfig3Fails = 1;
+
+const char testConfig2[] = "test2.config";
+const psS32 testConfig2Fails = 17;
+
+// SDR-14 test config file #1
+const char  testConfig1[] = "test1.config";
+const psS32 testConfig1FailsOverwrite = 0;
+const psS32 testConfig1Fails = 1;
+const psS32 testConfig1Items = 25;
+const char* testConfig1KeyOverwrite[] =
+    {
+        "Double","String","boolean","primes","comment",
+        "comment","comment","comment","comment","Float",
+        "boolean1","negprimes","vector1","vector2","vector3",
+        "vector4","vector5","vector6","vector7","vector8",
+        "CELL.00", "CELL.01","MYCELL","MYCELL","cell"
+    };
+const char* testConfig1CommentOverwrite[] =
+    {
+        "This is a comment","comment","The value of 'boolean' is 'true'","These are prime numbers",
+        "","","","","",
+        "This generates a warning, and, if 'overwrite' is 'false', is ignored","The value of 'boolean' is 'false'",
+        "","","","","",
+        "","","","",
+        "","","","A number",""
+    };
+const psDataType testConfig1TypeOverwrite[] =
+    {
+        PS_DATA_F64, PS_DATA_STRING, PS_DATA_BOOL, PS_DATA_VECTOR, PS_DATA_STRING,
+        PS_DATA_STRING, PS_DATA_STRING, PS_DATA_STRING, PS_DATA_STRING,  PS_DATA_F64,
+        PS_DATA_BOOL, PS_DATA_VECTOR, PS_DATA_VECTOR, PS_DATA_VECTOR, PS_DATA_VECTOR,
+        PS_DATA_VECTOR, PS_DATA_VECTOR, PS_DATA_VECTOR, PS_DATA_VECTOR, PS_DATA_VECTOR,
+        PS_DATA_METADATA, PS_DATA_METADATA, PS_DATA_METADATA, PS_DATA_S32, PS_DATA_METADATA
+    };
+const psS32 testConfig1ValueS32Overwrite[] =
+    {
+        0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0,
+        0, 0, 0, 123, 0
+    };
+const psF32 testConfig1ValueF32Overwrite[] =
+    {
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0
+    };
+const psF64 testConfig1ValueF64Overwrite[] =
+    {
+        1.23456789, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 1.23456,
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0
+    };
+const psBool testConfig1ValueBoolOverwrite[] =
+    {
+        false, false, true, false, false,
+        false, false, false, false, false,
+        false, false, false, false, false,
+        false, false, false, false, false,
+        false, false, false, false, false
+    };
+const char* testConfig1ValueStrOverwrite[] =
+    {
+        "", "This is the string that forms the value","","","This",
+        "is","a","non-unique","key","",
+        "","","","","",
+        "","","","","",
+        "","","","",""
+    };
+const psS32 testConfig1ValueVecItemsU8 = 7;
+const psElemType testConfig1ValueVecTypeU8 = PS_TYPE_U8;
+const psU8 testConfig1ValueVecValueU8[] =
+    {
+        2, 3, 5, 7, 11, 13, 17
+    };
+const psS32 testConfig1ValueVecItemsS8 = 8;
+const psElemType testConfig1ValueVecTypeS8 = PS_TYPE_S8;
+const psS8 testConfig1ValueVecValueS8[] =
+    {
+        -2, -3, -5, -7, -11, -13, -17, -19
+    };
+const psS32 testConfig1ValueVecItemsU16 = 5;
+const psElemType testConfig1ValueVecTypeU16 = PS_TYPE_U16;
+const psU16 testConfig1ValueVecValueU16[] =
+    {
+        0, 1, 2, 4, 8
+    };
+const psS32 testConfig1ValueVecItemsU32 = 6;
+const psElemType testConfig1ValueVecTypeU32 = PS_TYPE_U32;
+const psU32 testConfig1ValueVecValueU32[] =
+    {
+        0, 8, 16, 32, 64, 128
+    };
+const psS32 testConfig1ValueVecItemsU64 = 3;
+const psElemType testConfig1ValueVecTypeU64 = PS_TYPE_U64;
+const psU64 testConfig1ValueVecValueU64[] =
+    {
+        0, 64, 256
+    };
+const psS32 testConfig1ValueVecItemsS16 = 5;
+const psElemType testConfig1ValueVecTypeS16 = PS_TYPE_S16;
+const psS16 testConfig1ValueVecValueS16[] =
+    {
+        -2, -1, 0, 1, 2
+    };
+const psS32 testConfig1ValueVecItemsS32 = 6;
+const psElemType testConfig1ValueVecTypeS32 = PS_TYPE_S32;
+const psS32 testConfig1ValueVecValueS32[] =
+    {
+        -4, -2, 0, 2, 4, 6
+    };
+const psS32 testConfig1ValueVecItemsS64 = 7;
+const psElemType testConfig1ValueVecTypeS64 = PS_TYPE_S64;
+const psS64 testConfig1ValueVecValueS64[] =
+    {
+        -16, -4, 0, 4, 16, 36, 64
+    };
+const psS32 testConfig1ValueVecItemsF32 = 4;
+const psElemType testConfig1ValueVecTypeF32 = PS_TYPE_F32;
+const psF32 testConfig1ValueVecValueF32[] =
+    {
+        -1.03, 1.04, -1.05, 1.06
+    };
+const psS32 testConfig1ValueVecItemsF64 = 5;
+const psElemType testConfig1ValueVecTypeF64 = PS_TYPE_F64;
+const psF64 testConfig1ValueVecValueF64[] =
+    {
+        -2.22, 2.21, -2.20, 2.19, -2.18
+    };
+const psS32 testConfig1ValueMetaItems1 = 3;
+const char* testConfig1ValueMetaNames1[] =
+    {
+        "EXTNAME","BIASSEC","CHIP"
+    };
+const psDataType testConfig1ValueMetaTypes1[] =
+    {
+        PS_DATA_STRING, PS_DATA_STRING, PS_DATA_STRING
+    };
+const char* testConfig1ValueMetaValue1[] =
+    {
+        "CCD00","BSEC-00","CHIP.00"
+    };
+const psS32 testConfig1ValueMetaItems2 = 3;
+const char* testConfig1ValueMetaNames2[] =
+    {
+        "EXTNAME","BIASSEC","CHIP"
+    };
+const psDataType testConfig1ValueMetaTypes2[] =
+    {
+        PS_DATA_STRING, PS_DATA_STRING, PS_DATA_STRING
+    };
+const char* testConfig1ValueMetaValue2[] =
+    {
+        "CCD01","BSEC-01","CHIP.00"
+    };
+const psS32 testConfig1ValueMetaItems3 = 4;
+const char* testConfig1ValueMetaNames3[] =
+    {
+        "EXTNAME","BIASSEC","CHIP","NCELL"
+    };
+const psDataType testConfig1ValueMetaTypes3[] =
+    {
+        PS_DATA_STRING, PS_DATA_STRING, PS_DATA_STRING, PS_DATA_S32
+    };
+const char* testConfig1ValueMetaValueStr3[] =
+    {
+        "CCD00","BSEC-00","CHIP.00",""
+    };
+const psS32 testConfig1ValueMetaValueS323[] =
+    {
+        0, 0, 0, 24
+    };
+
+static void writeMetadata(psMetadata* metadata, char* indentStr)
+{
+    writeMetadataList(metadata->list, indentStr);
+}
+
+static void writeMetadataList(psList* metadataItemList, char* indentStr)
+{
+    psMetadataItem* entryChild        = NULL;
+    psMetadataItem* searchChild       = NULL;
+    psArray*        nonUniqueKeyArray = NULL;
+    psBool          keyFound          = false;
+    char*           tempKey           = NULL;
+
+    // Allocate iterator for moving through metadata list
+    psListIterator* iter = psListIteratorAlloc(metadataItemList, PS_LIST_HEAD, true);
+    psListIterator* searchIter = psListIteratorAlloc(metadataItemList, PS_LIST_HEAD, true);
+
+    // Allocate array for nonUnique key names
+    nonUniqueKeyArray = psArrayAlloc(10);
+    nonUniqueKeyArray->n = 0;
+
+
+    // Loop through all items in the list
+    while ( (entryChild = psListGetAndIncrement(iter)) != NULL) {
+        // Search list for another entry with same key name
+        // Check if last entry
+        if(iter->index != metadataItemList->n) {
+            // Set search iterator to index
+            if(!psListIteratorSet(searchIter,iter->index) ) {
+                psError(PS_ERR_UNKNOWN,true,"Error searching list for multiple keys");
+                break;
+            }
+            keyFound = false;
+            while ( (searchChild = psListGetAndIncrement(searchIter)) != NULL) {
+                if(strcmp(entryChild->name,searchChild->name) == 0) {
+                    // Search nonUnique key array
+                    for(psS32 i = 0; i < nonUniqueKeyArray->n; i++) {
+                        if(strcmp(entryChild->name,nonUniqueKeyArray->data[i]) == 0) {
+                            keyFound = true;
+                        }
+                    }
+                    if(!keyFound) {
+                        // Add key to non-unique array
+                        tempKey = psStringCopy(entryChild->name);
+                        nonUniqueKeyArray = psArrayAdd(nonUniqueKeyArray,1,tempKey);
+                        psFree(tempKey);
+
+                        // Print MULTI line
+                        printf("%-25s MULTI\n",entryChild->name);
+
+                        // Break out of loop
+                        break;
+                    }
+                }
+            }
+        }
+        writeMetadataItem(entryChild,indentStr);
+    }
+
+    psFree(iter);
+    psFree(searchIter);
+    psFree(nonUniqueKeyArray);
+}
+
+char* vectorToConfigString(psVector* vector)
+{
+    psS32  maxLength = 256;
+
+    char* str = psAlloc(sizeof(char)*maxLength+1);
+
+    if (vector == NULL) {
+        snprintf(str,maxLength, "NULL");
+        return str;
+    }
+
+    int size = vector->n;
+
+    if (size == 0) {
+        snprintf(str,maxLength, " ");
+        return str;
+    }
+
+    char* tempStr = psAlloc(sizeof(char)*maxLength+1);
+    *str = '\0';
+    bool full = false;
+
+    #define APPEND_ELEMENTS_CASE(TYPE, NATIVE_TYPE, FORMAT) \
+case PS_TYPE_##TYPE: \
+    strcat(str,#TYPE); \
+    for(psS32 i = 0; i < (strlen(str)-6); i++) {  \
+        strcat(str," "); \
+    } \
+    for (lcv=0; lcv < size && ! full; lcv++) { \
+        snprintf(tempStr, maxLength, "%s" FORMAT, prefix, (NATIVE_TYPE) (vector->data.TYPE[lcv])); \
+        strncat(str,tempStr,maxLength); \
+        full = (strlen(str) > maxLength-2); \
+        prefix = ","; \
+    } \
+    break;
+
+    int lcv;
+    char* prefix = " ";
+    switch(vector->type.type) {
+        APPEND_ELEMENTS_CASE(S8,char,"%hd")
+        APPEND_ELEMENTS_CASE(S16,short int,"%hd")
+        APPEND_ELEMENTS_CASE(S32,int,"%d")
+        APPEND_ELEMENTS_CASE(S64,long,"%ld")
+        APPEND_ELEMENTS_CASE(U8,unsigned char,"%hu")
+        APPEND_ELEMENTS_CASE(U16,unsigned short,"%hu")
+        APPEND_ELEMENTS_CASE(U32,unsigned int, "%u")
+        APPEND_ELEMENTS_CASE(U64,unsigned long,"%lu")
+        APPEND_ELEMENTS_CASE(F32,double,"%g")
+        APPEND_ELEMENTS_CASE(F64,double,"%g")
+    default:
+        snprintf(str,maxLength,"INVALID TYPE");
+        break;
+    }
+
+    psFree(tempStr);
+
+    return str;
+}
+
+
+static void writeMetadataItem(psMetadataItem *metadataItem, char* indentStr)
+{
+    char*  vecStr;
+
+    switch(metadataItem->type) {
+    case PS_DATA_BOOL:
+        printf("%s%-25s BOOL  %40d",indentStr,metadataItem->name,metadataItem->data.B);
+        break;
+    case PS_DATA_S32:
+        printf("%s%-25s S32   %40d",indentStr,metadataItem->name,metadataItem->data.S32);
+        break;
+    case PS_DATA_F32:
+        printf("%s%-25s F32   %40f",indentStr,metadataItem->name,metadataItem->data.F32);
+        break;
+    case PS_DATA_F64:
+        printf("%s%-25s F64   %40lf",indentStr,metadataItem->name,metadataItem->data.F64);
+        break;
+    case PS_DATA_VECTOR:
+        vecStr = (char*)vectorToConfigString((psVector*)metadataItem->data.V);
+        printf("%s@%-24s %s",indentStr,metadataItem->name,vecStr);
+        psFree(vecStr);
+        break;
+    case PS_DATA_STRING:
+        printf("%s%-25s STR   %40s",indentStr,metadataItem->name,(char*)metadataItem->data.V);
+        break;
+    case PS_DATA_METADATA:
+        printf("%s%-25s METADATA\n",indentStr,metadataItem->name);
+        char nestedStr[256] = "    ";
+        strcat(nestedStr,indentStr);
+        writeMetadata((psMetadata*)metadataItem->data.md,nestedStr);
+        printf("%sEND",indentStr);
+        break;
+    default:
+        printf("#%s%-24s BAD TYPE=%d",indentStr,metadataItem->name,metadataItem->type);
+        break;
+    }
+    if(strlen(metadataItem->comment) > 0) {
+        printf("  # %-20s\n",metadataItem->comment);
+    } else {
+        printf("\n");
+    }
+}
+
+static psBool checkFailedLines(psS32 failedLines, psS32 expectedFailedLines, char* configFile)
+{
+    psBool     returnValue = true;
+
+    // Verify the expected number of failed lines
+    if(failedLines != expectedFailedLines) {
+        psError(PS_ERR_UNKNOWN,true,"File: %s : Number of failed lines = %d not as expected %d",
+                configFile,failedLines,expectedFailedLines);
+        returnValue = false;
+    }
+    return returnValue;
+}
+
+static psBool checkNumberOfItems(psMetadata* md, psS32 expectedItems, char* configFile)
+{
+    psBool     returnValue = true;
+
+    // Verify the number of items in metadata
+    if(md->list->n != expectedItems) {
+        psError(PS_ERR_UNKNOWN,true,"File: %s : Number of items = %d not as expected %d",
+                configFile,md->list->n, expectedItems);
+        returnValue = false;
+    }
+    return returnValue;
+}
+
+static psBool checkItemName(psMetadataItem* mdItem, char* expectedName, char* configFile)
+{
+    psBool    returnValue = true;
+
+    // Compare key names
+    if(strcmp(mdItem->name,expectedName) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"File: %s : Key name %s not as expected %s",
+                configFile,mdItem->name,expectedName);
+        returnValue = false;
+    }
+
+    return returnValue;
+}
+
+static psBool checkItemType(psMetadataItem* mdItem, psS32 expectedType, char* configFile)
+{
+    psBool     returnValue = true;
+
+    // Compare types
+    if(mdItem->type != expectedType) {
+        psError(PS_ERR_UNKNOWN,true,"File: %s : Type %d not as expected %d",
+                configFile,mdItem->type,expectedType);
+        returnValue = false;
+    }
+    return returnValue;
+}
+
+static psBool checkItemComment(psMetadataItem* mdItem, char* expectedComment, char* configFile)
+{
+    psBool    returnValue = true;
+
+    // Compare comments
+    if(strcmp(mdItem->comment,expectedComment) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"File: %s : Comment %s not as expected %s",
+                configFile,mdItem->comment,expectedComment);
+        returnValue = false;
+    }
+    return returnValue;
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    if( !runTestSuite(stderr,"psMetadataConfigParse",tests,argc,argv)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+psS32 testMetadataParseConfig(void)
+{
+    psMetadata*       metadata1      = NULL;
+    psMetadataItem*   entryChild     = NULL;
+    psU32             failedLines    = 0;
+    psListIterator*   iter           = NULL;
+    psListIterator*   mdIter         = NULL;
+    psMetadataItem*   mdChild        = NULL;
+    psS32             metaCounter    = 0;
+    psS32             mdCounter      = 0;
+
+    // Read config file test1.config with overwrite set true
+    metadata1 = psMetadataConfigParse(metadata1,&failedLines,testConfig1,true);
+    // Verify the expected number of failed lines
+    if(!checkFailedLines(failedLines,testConfig1FailsOverwrite,(char*)testConfig1)) {
+        return 1;
+    }
+    // Verify the number of items in metadata
+    if(!checkNumberOfItems(metadata1,testConfig1Items,(char*)testConfig1)) {
+        return 2;
+    }
+    // Verify metadata item quads
+    iter = psListIteratorAlloc(metadata1->list, PS_LIST_HEAD, true);
+    for(psS32 i = 0; i < testConfig1Items; i++) {
+        // Get list item
+        entryChild = psListGetAndIncrement(iter);
+
+        // Verify end of list not reached prematurely
+        if(entryChild == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"End of list encountered at %d not as expected %d",
+                    i,testConfig1Items);
+            return i*10+1;
+        }
+        // Verify name
+        if(!checkItemName(entryChild,(char*)testConfig1KeyOverwrite[i],(char*)testConfig1)) {
+            return i*10+2;
+        }
+        // Verify type
+        if(!checkItemType(entryChild,testConfig1TypeOverwrite[i],(char*)testConfig1)) {
+            return i*10+3;
+        }
+        // Verify comment
+        if(!checkItemComment(entryChild,(char*)testConfig1CommentOverwrite[i],(char*)testConfig1)) {
+            return i*10+4;
+        }
+        // Compare values
+        switch(entryChild->type) {
+        case PS_DATA_S32:
+            if(entryChild->data.S32 != testConfig1ValueS32Overwrite[i]) {
+                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %d not as expected %d",
+                        testConfig1,entryChild->data.S32, testConfig1ValueS32Overwrite[i]);
+                return i*10+5;
+            }
+            break;
+        case PS_DATA_F32:
+            if(fabs(entryChild->data.F32 - testConfig1ValueF32Overwrite[i]) > ERROR_TOL) {
+                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %f not as expected %f",
+                        testConfig1,entryChild->data.F32, testConfig1ValueF32Overwrite[i]);
+                return i*10+5;
+            }
+            break;
+        case PS_DATA_F64:
+            if(fabs(entryChild->data.F64 - testConfig1ValueF64Overwrite[i]) > ERROR_TOL) {
+                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %lf not as expected %lf",
+                        testConfig1,entryChild->data.F64, testConfig1ValueF64Overwrite[i]);
+                return i*10+5;
+            }
+            break;
+        case PS_DATA_BOOL:
+            if(entryChild->data.B != testConfig1ValueBoolOverwrite[i]) {
+                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %d not as expected %d",
+                        testConfig1,entryChild->data.B,testConfig1ValueBoolOverwrite[i]);
+                return i*10+5;
+            }
+            break;
+        case PS_DATA_STRING:
+            if(strcmp(entryChild->data.V,testConfig1ValueStrOverwrite[i]) != 0) {
+                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %s not as expected %s",
+                        testConfig1,entryChild->data.V,testConfig1ValueStrOverwrite[i]);
+                return i*10+5;
+            }
+            break;
+        case PS_DATA_VECTOR:
+            // Verify the correct number of entries
+            switch(((psVector*)(entryChild->data.V))->type.type) {
+            case PS_TYPE_U8:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU8) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU8);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU8) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU8);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsU8; j++) {
+                    if(((psVector*)entryChild->data.V)->data.U8[j] != testConfig1ValueVecValueU8[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.U8[j],
+                                testConfig1ValueVecValueU8[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_U16:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU16) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU16);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU16) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU16);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsU16; j++) {
+                    if(((psVector*)entryChild->data.V)->data.U16[j] != testConfig1ValueVecValueU16[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.U16[j],
+                                testConfig1ValueVecValueU16[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_U32:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU32);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU32);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsU32; j++) {
+                    if(((psVector*)entryChild->data.V)->data.U32[j] != testConfig1ValueVecValueU32[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.U32[j],
+                                testConfig1ValueVecValueU32[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_U64:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU64);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU64);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsU64; j++) {
+                    if(((psVector*)entryChild->data.V)->data.U64[j] != testConfig1ValueVecValueU64[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %ld not as expected %ld",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.U64[j],
+                                testConfig1ValueVecValueU64[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_S8:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS8) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS8);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS8) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS8);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsS8; j++) {
+                    if(((psVector*)entryChild->data.V)->data.S8[j] != testConfig1ValueVecValueS8[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.S8[j],
+                                testConfig1ValueVecValueS8[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_S16:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS16) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS16);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS16) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS16);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsS16; j++) {
+                    if(((psVector*)entryChild->data.V)->data.S16[j] != testConfig1ValueVecValueS16[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.S16[j],
+                                testConfig1ValueVecValueS16[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_S32:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS32);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS32);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsS32; j++) {
+                    if(((psVector*)entryChild->data.V)->data.S32[j] != testConfig1ValueVecValueS32[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.S32[j],
+                                testConfig1ValueVecValueS32[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_S64:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS64);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS64);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsS64; j++) {
+                    if(((psVector*)entryChild->data.V)->data.S64[j] != testConfig1ValueVecValueS64[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %ld not as expected %ld",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.S64[j],
+                                testConfig1ValueVecValueS64[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_F32:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsF32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsF32);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeF32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeF32);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsF32; j++) {
+                    if(fabs(((psVector*)entryChild->data.V)->data.F32[j]-testConfig1ValueVecValueF32[j])
+                            > ERROR_TOL) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %f not as expected %f",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.F32[j],
+                                testConfig1ValueVecValueF32[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_F64:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsF64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsF64);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeF64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeF64);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsF64; j++) {
+                    if(fabs(((psVector*)entryChild->data.V)->data.F64[j]-testConfig1ValueVecValueF64[j])
+                            > ERROR_TOL) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %lf not as expected %lf",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.F64[j],
+                                testConfig1ValueVecValueF64[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            default:
+                break;
+            }
+            break;
+        case PS_DATA_METADATA:
+            if(metaCounter == 0) {
+                // Check if number of items is as expected
+                if(entryChild->data.md->list->n != testConfig1ValueMetaItems1) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 items %d not as expected %d",
+                            testConfig1,entryChild->data.md->list->n,testConfig1ValueMetaItems1);
+                    return i*10+6;
+                }
+                // Loop through metadata items
+                mdIter = psListIteratorAlloc(entryChild->data.md->list, PS_LIST_HEAD, true);
+                mdCounter = 0;
+                while ( (mdChild = psListGetAndIncrement(mdIter)) != NULL) {
+                    if(strcmp(mdChild->name,testConfig1ValueMetaNames1[mdCounter]) != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 name[%d] %s not as expected %s",
+                                testConfig1,mdCounter,mdChild->name,testConfig1ValueMetaNames1[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    if(mdChild->type != testConfig1ValueMetaTypes1[mdCounter]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 type[%d] %d not as expected %d",
+                                testConfig1,mdCounter,mdChild->type,testConfig1ValueMetaTypes1[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    if(strcmp((char*)mdChild->data.V,testConfig1ValueMetaValue1[mdCounter]) != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 value[%d] %s not as expected %s",
+                                testConfig1,mdCounter,(char*)mdChild->data.V,testConfig1ValueMetaValue1[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    mdCounter++;
+                }
+                psFree(mdIter);
+            } else if(metaCounter == 1) {
+                // Check if number of items is as expected
+                if(entryChild->data.md->list->n != testConfig1ValueMetaItems2) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 items %d not as expected %d",
+                            testConfig1,entryChild->data.md->list->n,testConfig1ValueMetaItems2);
+                    return i*10+6;
+                }
+                // Loop through metadata items
+                mdIter = psListIteratorAlloc(entryChild->data.md->list, PS_LIST_HEAD, true);
+                mdCounter = 0;
+                while ( (mdChild = psListGetAndIncrement(mdIter)) != NULL) {
+                    if(strcmp(mdChild->name,testConfig1ValueMetaNames2[mdCounter]) != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 name[%d] %s not as expected %s",
+                                testConfig1,mdCounter,mdChild->name,testConfig1ValueMetaNames2[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    if(mdChild->type != testConfig1ValueMetaTypes2[mdCounter]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 type[%d] %d not as expected %d",
+                                testConfig1,mdCounter,mdChild->type,testConfig1ValueMetaTypes2[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    if(strcmp((char*)mdChild->data.V,testConfig1ValueMetaValue2[mdCounter]) != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 value[%d] %s not as expected %s",
+                                testConfig1,mdCounter,(char*)mdChild->data.V,testConfig1ValueMetaValue2[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    mdCounter++;
+                }
+                psFree(mdIter);
+            } else if(metaCounter == 2) {
+                // Check if number of items is as expected
+                if(entryChild->data.md->list->n != testConfig1ValueMetaItems3) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 items %d not as expected %d",
+                            testConfig1,entryChild->data.md->list->n,testConfig1ValueMetaItems3);
+                    return i*10+6;
+                }
+                // Loop through metadata items
+                mdIter = psListIteratorAlloc(entryChild->data.md->list, PS_LIST_HEAD, true);
+                mdCounter = 0;
+                while ( (mdChild = psListGetAndIncrement(mdIter)) != NULL) {
+                    if(strcmp(mdChild->name,testConfig1ValueMetaNames3[mdCounter]) != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 name[%d] %s not as expected %s",
+                                testConfig1,mdCounter,mdChild->name,testConfig1ValueMetaNames3[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    if(mdChild->type != testConfig1ValueMetaTypes3[mdCounter]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 type[%d] %d not as expected %d",
+                                testConfig1,mdCounter,mdChild->type,testConfig1ValueMetaTypes3[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    switch(mdChild->type) {
+                    case PS_DATA_STRING:
+                        if(strcmp((char*)mdChild->data.V,testConfig1ValueMetaValueStr3[mdCounter]) != 0) {
+                            psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 value[%d] %s not as expected %s",
+                                    testConfig1,mdCounter,(char*)mdChild->data.V,
+                                    testConfig1ValueMetaValueStr3[mdCounter]);
+                            return i*10+6*mdCounter;
+                        }
+                        break;
+                    case PS_DATA_S32:
+                        if(mdChild->data.S32 != testConfig1ValueMetaValueS323[mdCounter]) {
+                            psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 value[%d] %d not as expected %d",
+                                    testConfig1,mdCounter,mdChild->data.S32,
+                                    testConfig1ValueMetaValueS323[mdCounter]);
+                            return i*10+6*mdCounter;
+                        }
+                        break;
+                    default:
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 value[%d] unknown type",
+                                testConfig1,mdCounter);
+                        return i*10+6*mdCounter;
+                        break;
+                    }
+                    mdCounter++;
+                }
+                psFree(mdIter);
+            }
+            metaCounter++;
+            break;
+        default:
+            psError(PS_ERR_UNKNOWN,true,"Unexpected type %d encountered",entryChild->type);
+            return i*10+5;
+            break;
+        }
+    }
+
+    writeMetadata(metadata1,"");
+
+    psFree(metadata1);
+
+    return 0;
+}
+
+psS32 testMetadataParseConfig1(void)
+{
+    psMetadata*       metadata1      = NULL;
+    psU32             failedLines    = 0;
+
+    // Read config file test2.config with overwrite set true
+    // This file contains parse errors
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate parse error message");
+    metadata1 = psMetadataConfigParse(metadata1,&failedLines,testConfig2,true);
+    // Verify the expected number of failed lines
+    if(!checkFailedLines(failedLines,testConfig2Fails,(char*)testConfig2)) {
+        return 1;
+    }
+    // Verify return value is null
+    if(metadata1 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expected a NULL return for failed parse");
+        return 2;
+    }
+    psFree(metadata1);
+
+    // Attempt parse a non-existant file
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    metadata1 = psMetadataConfigParse(metadata1,&failedLines,"ab.config",true);
+    if(metadata1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected a NULL return for non-existant file");
+        return 3;
+    }
+
+    // Attempt parse with NULL failed lines ptr
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL failed lines arg");
+    metadata1 = psMetadataConfigParse(metadata1,NULL,testConfig2,true);
+    if(metadata1 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return for NULL failed lines argument");
+        return 4;
+    }
+    psFree(metadata1);
+    // Attempt parse with NULL file name
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL file name arg");
+    metadata1 = psMetadataConfigParse(metadata1,&failedLines,NULL,true);
+    if(metadata1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected a NULL return for NULL filename argument");
+        return 5;
+    }
+
+    psFree(metadata1);
+
+    return 0;
+}
+
+psS32 testMetadataParseConfig2(void)
+{
+    psMetadata*       metadata1      = NULL;
+    psU32             failedLines    = 0;
+
+    // Read config file test3.config with overwrite set false
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error of duplicate key names");
+    metadata1 = psMetadataConfigParse(metadata1,&failedLines,testConfig3,false);
+    // Verify the expected number of failed lines
+    if(!checkFailedLines(failedLines,testConfig3Fails,(char*)testConfig3)) {
+        return 1;
+    }
+    if(metadata1 == NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expected a NULL return for failed due to overwrite false");
+        return 2;
+    }
+    psFree(metadata1);
+
+    return 0;
+}
+
+psS32 testMetadataParseConfig3(void)
+{
+    psMetadata*       metadata1      = NULL;
+    psU32             failedLines    = 0;
+
+    // Read config file test4.config with overwrite set false
+    metadata1 = psMetadataConfigParse(metadata1,&failedLines,testConfig4,false);
+    // Verify the expected number of failed lines
+    if(!checkFailedLines(failedLines,testConfig4Fails,(char*)testConfig4)) {
+        return 1;
+    }
+    // Print out metadata items for comparison against STDERR verified file
+    writeMetadata(metadata1,"");
+
+    psFree(metadata1);
+
+    return 0;
+}
+
+psS32 testMetadataParseConfig4(void)
+{
+    psMetadata*       metadata1      = NULL;
+    psU32             failedLines    = 0;
+
+    // Read config file test4.config with overwrite set false
+    metadata1 = psMetadataConfigParse(metadata1,&failedLines,testConfig5,true);
+    // Verify the expected number of failed lines
+    if(!checkFailedLines(failedLines,testConfig5Fails,(char*)testConfig5)) {
+        return 1;
+    }
+    psFree(metadata1);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_01.c	(revision 22322)
@@ -0,0 +1,299 @@
+/** @file  tst_psMetadata_01.c
+*
+*  @brief Test driver for psMetadata functions
+*
+*  @author  dRob, MHPCC
+*
+*  @version $Revision: 1.12 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2006-08-09 00:06:26 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testMetadataItemCopy(void);
+static psS32 testMetadataItemTransfer(void);
+static psS32 testMetadataPrint(void);
+static psS32 testMetadataItemCompare(void);
+static psS32 testMetadataItemParse(void);
+
+testDescription tests[] = {
+                              {testMetadataItemCopy, 666, "psMetadataItemCopy()", 0, false},
+                              {testMetadataItemTransfer, 1, "psMetadataItemTransfer()", 0, false},
+                              {testMetadataPrint,667,"psMetadataPrint",0,false},
+                              {testMetadataItemCompare,669,"psMetadataItemCompare",0,false},
+                              {testMetadataItemParse,665,"psMetadataItemParse",0,false},
+                              {NULL}
+                          };
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    return ( !runTestSuite(stderr,"psMetadata_01",tests,argc,argv) );
+
+}
+
+psS32 testMetadataItemCopy(void)
+{
+    psMetadataItem *item = psMetadataItemAlloc("metaITEM", PS_DATA_S32, "no Comment");
+    item->data.S32 = 666;
+    psMetadataItem *item2 = NULL;
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    item2 = psMetadataItemCopy(NULL);
+    if (item2 != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psMetadataItemCopy failed to return NULL for NULL input psMetadataItem.\n");
+        return 1;
+    }
+
+    //Simple copy of psS32 data
+    item2 = psMetadataItemCopy(item);
+    if (item2 == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psMetadataItemCopy incorrectly returned NULL for valid input psMetadataItem.\n");
+        return 2;
+    }
+    if ( strncmp(item2->name, item->name, 10) || item2->type != item->type ||
+            item2->data.S32 != item->data.S32 || strncmp(item2->comment, item->comment, 15) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psMetadataItemCopy returned an incorrect psMetadataItem (S32 test).\n");
+        return 3;
+    }
+    psFree(item2);
+    psFree(item);
+    item2 = NULL;
+
+    //Copy of MetadataItem containing a psMetadata
+    psMetadata* metadata = psMetadataAlloc();
+    psMetadataAddS32(metadata, PS_LIST_TAIL, "item01", 0, "", 55);
+    psMetadataAddF32(metadata, PS_LIST_TAIL, "item02", 0, NULL, 3.14);
+    item = psMetadataItemAlloc("metaItem2", PS_DATA_METADATA, "newMeta", metadata);
+    item2 = psMetadataItemCopy(item);
+    if (item2 == NULL || item2->data.md == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psMetadataItemCopy returned either a NULL item or NULL metadata data\n");
+        return 4;
+    }
+    if ( strncmp(item2->name, item->name, 11) || item2->type != item->type ||
+            strncmp(item2->comment, item->comment, 15) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psMetadataItemCopy returned an incorrect psMetadataItem (Metadata test).\n");
+        return 5;
+    }
+    psMetadataItem *temp1 = psMetadataGet(item->data.md, PS_LIST_HEAD);
+    psMetadataItem *temp2 = psMetadataGet(item2->data.md, PS_LIST_HEAD);
+    if ( strncmp(temp2->name, temp1->name, 10) || temp2->type != temp1->type ||
+            temp2->data.S32 != temp1->data.S32 || strncmp(temp2->comment, temp1->comment, 15) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psMetadataItemCopy incorrectly returned non-matching metadata data.\n");
+        return 6;
+    }
+
+    psFree(item2);
+    psFree(metadata);
+    psFree(item);
+    return 0;
+}
+
+psS32 testMetadataItemTransfer(void)
+{
+
+    psMetadata *md = psMetadataAlloc();
+    psMetadata *out = NULL;
+    psMetadataAddS32(md, PS_LIST_TAIL, "metaITEM", 0, "no Comment", 666);
+
+    //attempt transfer to NULL 'out' metadata.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psMetadataItemTransfer(out, md, "metaITEM") ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psMetadataItemTransfer failed to return false for NULL 'out' metadata.\n");
+        return 1;
+    }
+    out = psMetadataAlloc();
+    //attempt to transfer using a NULL key.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psMetadataItemTransfer(out, md, NULL) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psMetadataItemTransfer failed to return false for NULL key input.\n");
+        return 2;
+    }
+    //attempt transfer to NULL 'in' metadata.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( psMetadataItemTransfer(out, NULL, "metaITEM") ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psMetadataItemTransfer failed to return false for NULL 'in' metadata.\n");
+        return 3;
+    }
+    //attempt to transfer using an allocated metadata and invalid key
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psMetadataItemTransfer(out, md, "metaITEM2") ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psMetadataItemTransfer failed to return false for invalid 'key'.\n");
+        return 4;
+    }
+    //attempt to transfer with all valid inputs.
+    psMetadataItem *item = NULL;
+    if ( !psMetadataItemTransfer(out, md, "metaITEM") ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psMetadataItemTransfer failed with valid inputs.\n");
+        return 7;
+    }
+    item = psMetadataGet(out, PS_LIST_HEAD);
+    if (item->data.S32 != 666 || strncmp(item->comment, "no Comment", 20)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "psMetadataItemTransfer failed to correctly transfer item data.\n");
+        return 8;
+    }
+
+    psFree(out);
+    psFree(md);
+
+    return 0;
+}
+
+static psMetadata *setupMeta2(void)
+{
+    psMetadata *md = NULL;
+    psVector *vec = NULL;
+    psMetadata *newMD = NULL;
+    psTime *time;
+    int i = 0;
+    md = psMetadataAlloc();
+    newMD = psMetadataAlloc();
+    vec = psVectorAlloc(60, PS_DATA_S32);
+    time = psTimeAlloc(PS_TIME_TAI);
+    for (i = 0; i < 5; i++) {
+        vec->data.S32[i] = i+1;
+    }
+    vec->n = 5;
+    time->sec = 1000;
+    time->nsec = 25;
+    time->leapsecond = true;
+    psMetadataAddBool(md, PS_LIST_TAIL, "item1", 0, "I am a boolean", true);
+    psMetadataAddS32(md, PS_LIST_TAIL, "item2", 0, "I am a integer", 55);
+    psMetadataAddF32(md, PS_LIST_TAIL, "item3", 0, NULL, 3.14);
+    psMetadataAddF64(md, PS_LIST_TAIL, "item4", 0, "", 6.28);
+    psMetadataAddStr(md, PS_LIST_TAIL, "item5", 0, "I am a string", "GNIRTS");
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector6", 0, "I am a vector", vec);
+    psMetadataAddTime(md, PS_LIST_TAIL, "time01", 0, "I am time", time);
+    psMetadataAddS8(md, PS_LIST_TAIL, "item6", 0, "I am S8", 6);
+    psMetadataAddS16(md, PS_LIST_TAIL, "item7", 0, "I am S16", -666);
+    psMetadataAddU8(md, PS_LIST_TAIL, "item8", 0, "I am U8", 6);
+    psMetadataAddU16(md, PS_LIST_TAIL, "item9", 0, "I am U16", 666);
+    psMetadataAddU32(md, PS_LIST_TAIL, "item10", 0, "I am U32", 666);
+    psMetadataAddS64(md, PS_LIST_TAIL, "item11", 0, "I am S64", 666);
+    psMetadataAddU64(md, PS_LIST_TAIL, "item12", 0, "I am U64", 666);
+
+    psMetadataAddS32(newMD, PS_LIST_TAIL, "ITEM01", 0, NULL, 666);
+    psMetadata *newestMD = NULL;
+    newestMD = psMetadataAlloc();
+    psMetadataAddVector(newestMD, PS_LIST_TAIL, "VECTORNEW", 0, "Newest VECTOR", vec);
+    psMetadataAddStr(newestMD, PS_LIST_TAIL, "cell", 0, "I am a p-Star", "pStArRs");
+    psMetadataAddMetadata(newMD, PS_LIST_TAIL, "META NEW", 0, "I AM Newest METADATA", newestMD);
+    psMetadataAddF32(newMD, PS_LIST_TAIL, "ITEM02", 0, "I AM FLOAT", 666.6);
+    psMetadataAddF64(newMD, PS_LIST_TAIL, "ITEM03", 0, "I AM DOUBLE", 666.666);
+
+    psMetadataAddMetadata(md, PS_LIST_TAIL, "metadata7", 0, "I am a metadata", newMD);
+    psFree(time);
+    psFree(newestMD);
+    psFree(vec);
+    psFree(newMD);
+    return md;
+}
+
+psS32 testMetadataPrint(void)
+{
+    psMetadata *meta = NULL;
+    meta = setupMeta2();
+    psMetadataPrint(NULL, meta, 0);
+    psFree(meta);
+    return 0;
+}
+
+psS32 testMetadataItemCompare(void)
+{
+    psMetadataItem *compare = NULL;
+    psMetadataItem *template = NULL;
+    psMetadataItem *itemCopy = NULL;
+    psMetadataItem *item = psMetadataItemAlloc("Snickers", PS_DATA_BOOL, "No Comment", true)
+                           ;
+    if (!item->data.B) {
+        return 1;
+    }
+
+    //compare = NULL
+    if ( psMetadataItemCompare(compare, item)) {
+        return 2;
+    }
+    //template = NULL
+    if ( psMetadataItemCompare(item, template)) {
+        return 3;
+    }
+
+    template = psMetadataItemAlloc("Milky Way", PS_DATA_BOOL, "No Comment", true)
+               ;
+    compare = psMetadataItemAlloc("Snickers", PS_DATA_S32, "No Comment", 1);
+    //template->name != item->name
+    if ( psMetadataItemCompare(template, item) ) {
+        return 4;
+    }
+    //compare->type != item->type
+    if ( psMetadataItemCompare(compare, item) ) {
+        printf("\ncompare == item b/c of type\n");
+        //        return 5;
+    }
+
+    //compare->type == F32  compare->data.F32 = 1.4
+    psFree(compare);
+    compare = psMetadataItemAlloc("Snickers", PS_DATA_F64, "No Comment", 1.00001);
+    if ( psMetadataItemCompare(compare, item) ) {
+        printf("\ncompare2 == item b/c of type\n");
+        //        return 5;
+    }
+    psFree(template)
+    ;
+    template = psMetadataItemAlloc("Snickers", PS_DATA_S32, "No Comment", 1)
+               ;
+    if ( psMetadataItemCompare(compare, template) ) {
+        printf("\ncompare3 == item b/c of type\n");
+        //        return 5;
+    }
+
+    psFree(template)
+    ;
+    template = psMetadataItemAlloc("Snickers", PS_DATA_F32, "No Comment", 1.00001)
+               ;
+    if ( psMetadataItemCompare(compare, template) ) {
+        printf("\ncompare4 == item b/c of type\n");
+        //        return 5;
+    }
+
+
+    //itemCopy == item
+    itemCopy = psMetadataItemCopy(item);
+    if ( !psMetadataItemCompare(itemCopy, item) ) {
+        return 7;
+    }
+
+    psFree(compare);
+    psFree(template)
+    ;
+    psFree(itemCopy);
+    psFree(item);
+
+    return 0;
+}
+
+psS32 testMetadataItemParse(void)
+{
+
+    return 0;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_02.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_02.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_02.c	(revision 22322)
@@ -0,0 +1,145 @@
+/** @file  tst_psMetadata_02.c
+ *
+ *  @brief Test driver for psMetadata functions
+ *
+ *  This test driver contains the following tests for psMetadata:
+ *     Test A - Allocate metadata items
+ *     Test B - Attempt to create metadata item with null name
+ *     Test C - Attempt to create metadata item with invalid type
+ *     Test D - Allocate metadata
+ *     Test E - Attempt to add metadata item to null metadata
+ *     Test F - Attempt to add null metadata item to metadata
+ *     Test G - Free psMetadata
+ *
+ *  @author  Ross Harman, MHPCC
+ *  @author  Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-09-26 21:13:33 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static void printMetadataItem(psMetadataItem *metadataItem)
+{
+    printf("Key Name: %8s  ", metadataItem->name);
+    printf("Key mdType: 0x%08x  ", metadataItem->type);
+
+    switch (metadataItem->type) {
+    case PS_DATA_METADATA_MULTI:
+        printf("Key Value: %17c", ' ');
+        break;
+    case PS_DATA_BOOL:
+        printf("Key Value: %15d  ", metadataItem->data.B);
+        break;
+    case PS_DATA_S32:
+        printf("Key Value: %15d  ", metadataItem->data.S32);
+        break;
+    case PS_DATA_F32:
+        printf("Key Value: %15.3f  ", metadataItem->data.F32);
+        break;
+    case PS_DATA_F64:
+        printf("Key Value: %15.3f  ", metadataItem->data.F64);
+        break;
+    case PS_DATA_STRING:
+        printf("Key Value: %15s  ", (char*)metadataItem->data.V);
+        break;
+    default:
+        printf("Bad type: %d ", metadataItem->type);
+    }
+    printf("Key Comment: %s\n", metadataItem->comment);
+}
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psMetadataItem *item1 = NULL;
+    psMetadata *metadata = NULL;
+    psMetadataItem *item2 = NULL;
+    psMetadataItem *item3 = NULL;
+    psMetadataItem *item4 = NULL;
+    psMetadataItem *item5 = NULL;
+    psMetadataItem *badItem = NULL;
+
+
+    // Test A - Allocate metadata items
+    printPositiveTestHeader(stdout, "psMetadata", "Test A - Allocate metadata items");
+    item1 = psMetadataItemAlloc("myItem1", PS_DATA_BOOL, "I am a boolean", true);
+    item2 = psMetadataItemAlloc("myItem2", PS_DATA_S32, "I am a signed integer", 111);
+    item3 = psMetadataItemAlloc("myItem3", PS_DATA_F32, "I am a single precision floating point", 222.222);
+    item4 = psMetadataItemAlloc("myItem4", PS_DATA_F64, "I am a double precision floating point", 333.333);
+    item5 = psMetadataItemAlloc("myItem5", PS_DATA_STRING, "I am a string", "HELLO WORLD");
+    printMetadataItem(item1);
+    printMetadataItem(item2);
+    printMetadataItem(item3);
+    printMetadataItem(item4);
+    printMetadataItem(item5);
+    printFooter(stdout, "psMetadata", "Test A - Allocate metadata items", true);
+
+
+    // Test B - Attempt to create metadata item with null name
+    printNegativeTestHeader(stdout,"psMetadata", "Test B - Attempt to create metadata item with null name",
+                            "Null value for name not allowed", 0);
+    psLogMsg(__func__,PS_LOG_INFO,"Following should produce error for null name.");
+    badItem = psMetadataItemAlloc(NULL, PS_DATA_STRING, "I am a string", "HELLO WORLD");
+    if (badItem != NULL) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataItemAlloc did not return null with null name item.");
+        return 10;
+    }
+    printFooter(stdout, "psMetadata", "Test B - Attempt to create metadata item with null name", true);
+
+
+    // Test C - Attempt to create metadata item with invalid type
+    printNegativeTestHeader(stdout,"psMetadata", "Test C - Attempt to create metadata item with invalid type",
+                            "Invalid psDataType: 6", 0);
+    badItem = psMetadataItemAlloc("badItem", -1, "I am bad", "Bad comment");
+    printFooter(stdout, "psMetadata", "Test C - Attempt to create metadata item with invalid type", true);
+
+
+    // Test D - Allocate metadata
+    printPositiveTestHeader(stdout, "psMetadata", "Test D - Allocate metadata");
+    metadata = psMetadataAlloc();
+    printFooter(stdout, "psMetadata", "Test D - Allocate metadata", true);
+
+
+    // Test E - Attempt add metadata item to null metadata
+    printNegativeTestHeader(stdout,"psMetadata", "Test E - Attempt to add metadata item to null metadata",
+                            "Null metadata collection not allowed", 0);
+    psMetadataAddItem(NULL, item1, PS_LIST_HEAD, PS_META_DEFAULT);
+    printFooter(stdout, "psMetadata", "Test E - Attempt to add metadata item to null metadata", true);
+
+
+    // Test F - Attempt add null metadata item to metadata
+    printNegativeTestHeader(stdout,"psMetadata", "Test F - Attempt to add null metadata item to metadata",
+                            "Null metadata item not allowed", 0);
+    psMetadataAddItem(metadata, NULL, PS_LIST_HEAD, PS_META_DEFAULT);
+    printFooter(stdout, "psMetadata", "Test F - Attempt to add null metadata item to metadata", true);
+
+
+    // Test G - Free psMetadata
+    printPositiveTestHeader(stdout, "psMetadata", "Test G - Free psMetadata");
+    psFree(item1);
+    psFree(item2);
+    psFree(item3);
+    psFree(item4);
+    psFree(item5);
+    psFree(badItem);
+    psFree(metadata);
+    if(psMemCheckLeaks(0, NULL, stdout,false) ) {
+        psError(PS_ERR_UNKNOWN,true,"Memory leaks detected.");
+        return 10;
+    }
+    psMemCheckCorruption(0);
+    psS32 nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+    }
+    printFooter(stdout, "psMetadata", "Test G - Free psMetadata", true);
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_03.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_03.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_03.c	(revision 22322)
@@ -0,0 +1,241 @@
+/** @file  tst_psMetadata_03.c
+*
+*  @brief Test driver for psMetadata functions
+*
+*  This test driver contains the following tests for psMetadata:
+*     Test A - Allocate metadata and items
+*     Test B - Remove items from metadata by name
+*     Test C - Remove items from metadata by index
+*     Test D - Attempt to use null metadata
+*     Test E - Attempt to remove non-existant metadata item by name
+*     Test F - Attempt to remove non-existant metadata item by index
+*     Test G - Attempt to add item to an invalid metadata structure
+*     Test H - Attempt to add item to an invalid metadata structure
+*     Test I - Attempt to add item with a null name
+*     Test J - Attempt to add item to an invalid metadata structure
+*     Test K - Free psMetadata
+*
+*  @author  Ross Harman, MHPCC
+*
+*  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2005-09-26 21:13:33 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static void printMetadataItem(psMetadataItem *metadataItem)
+{
+    printf("Key Name: %8s  ", metadataItem->name);
+    printf("Key mdType: 0x%08x  ", metadataItem->type);
+
+    switch (metadataItem->type) {
+    case PS_DATA_METADATA_MULTI:
+        printf("Key Value: %17c", ' ');
+        break;
+    case PS_DATA_BOOL:
+        printf("Key Value: %15d  ", metadataItem->data.B);
+        break;
+    case PS_DATA_S32:
+        printf("Key Value: %15d  ", metadataItem->data.S32);
+        break;
+    case PS_DATA_F32:
+        printf("Key Value: %15.3f  ", metadataItem->data.F32);
+        break;
+    case PS_DATA_F64:
+        printf("Key Value: %15.3f  ", metadataItem->data.F64);
+        break;
+    case PS_DATA_STRING:
+        printf("Key Value: %15s  ", (char*)metadataItem->data.V);
+        break;
+    default:
+        printf("Bad type: %d ", metadataItem->type);
+    }
+    printf("Key Comment: %s\n", metadataItem->comment);
+}
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psMetadataItem *item1 = NULL;
+    psMetadataItem *item2 = NULL;
+    psMetadataItem *item3 = NULL;
+    psMetadataItem *item4 = NULL;
+    psMetadataItem *errItem = NULL;
+    psMetadata *metadata = NULL;
+    psMetadata *errMetadata = NULL;
+    psHash *mdTable = NULL;
+    psList *mdList = NULL;
+    char *errName = NULL;
+
+    // Test A - Allocate metadata and items
+    printPositiveTestHeader(stdout, "psMetadata", "Test A - Allocate metadata items");
+    metadata = psMetadataAlloc();
+    item1 = psMetadataItemAlloc("myItem1", PS_DATA_BOOL, "I am a boolean", true);
+    item2 = psMetadataItemAlloc("myItem2", PS_DATA_S32, "I am a integer", 55);
+    item3 = psMetadataItemAlloc("myItem3", PS_DATA_BOOL, "I am a boolean", false);
+    item4 = psMetadataItemAlloc("myItem4", PS_DATA_S32, "I am a integer", 66);
+    errItem = psMetadataItemAlloc("errItem", PS_DATA_S32, "I am a integer", 99);
+    printMetadataItem(item1);
+    printMetadataItem(item2);
+    printMetadataItem(item3);
+    printMetadataItem(item4);
+    printFooter(stdout, "psMetadata", "Test A - Allocate metadata items", true);
+
+
+    // Test B - Remove items from metadata by name
+    printPositiveTestHeader(stdout, "psMetadata", "Test B - Remove items from metadata by name");
+    if(!psMetadataAddItem(metadata, item1, PS_LIST_HEAD, PS_META_DEFAULT)) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem did not return true when adding by name.");
+        return 10;
+    }
+    if(!psMetadataAdd(metadata,PS_LIST_HEAD,"myItem2", PS_DATA_S32, "I am S32 integer",77)) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem did not return true when adding by name.");
+        return 11;
+    }
+    if (!psMetadataRemove(metadata, 0, "myItem1")) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataRemove did not return true when removing by name.");
+        return 12;
+    }
+    if (!psMetadataRemove(metadata, 0, "myItem2")) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataRemove did not return true when removing by name.");
+        return 13;
+    }
+    printFooter(stdout, "psMetadata", "Test B - Remove items from metadata by name", true);
+
+
+    // Test C - Remove items from metadata by index
+    printPositiveTestHeader(stdout, "psMetadata", "Test C - Remove items from metadata by index");
+    if ( ! psMetadataAddItem(metadata, item3, PS_LIST_HEAD, PS_META_DEFAULT) ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem did not return true when adding by index.");
+        return 14;
+    }
+    if ( ! psMetadataAdd(metadata,PS_LIST_HEAD,"myItem4", PS_DATA_S32, "I am S32 integer",88) ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem did not return true when adding by index.");
+        return 15;
+    }
+    if( ! psMetadataRemove(metadata, 0, NULL) ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataRemove did not return true when removing by index.");
+        return 16;
+    }
+    if ( ! psMetadataRemove(metadata, 0, NULL ) ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataRemove did not return true when removing by index.");
+        return 17;
+    }
+    printFooter(stdout, "psMetadata", "Test C - Remove items from metadata by index", true);
+
+
+    // Test D - Attempt to use null metadata
+    printNegativeTestHeader(stdout,"psMetadata", "Test D - Attempt to use null metadata",
+                            "Null metadata collection not allowed", 0);
+    if ( psMetadataRemove(NULL, 0, NULL) ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataRemove did not return false when removing from null metadata.");
+        return 18;
+    }
+    printFooter(stdout, "psMetadata", "Test D - Attempt to use null metadata", true);
+
+
+    // Test E - Attempt to remove non-existant metadata item by name
+    printNegativeTestHeader(stdout,"psMetadata", "Test E - Attempt to remove non-existant metadata item by name",
+                            "Couldn't find metadata item. Name: AARGH", 0);
+    if( psMetadataRemove(metadata, 0, "AARGH") ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataRemove did not return false when removing non-existant item.");
+        return 19;
+    }
+    printFooter(stdout, "psMetadata", "Test E - Attempt to remove non-existant metadata item by name", true);
+
+
+    // Test F - Attempt to remove non-existant metadata item by index
+    printNegativeTestHeader(stdout,"psMetadata", "Test E - Attempt to remove non-existant metadata item by index",
+                            "Couldn't find metadata item in list. Index: 22", 0);
+    if( psMetadataRemove(metadata, 22, NULL) ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataRemove did not return false when removing non-existant item.");
+        return 20;
+    }
+    printFooter(stdout, "psMetadata", "Test E - Attempt to remove non-existant metadata item by index", true);
+
+
+    // Test G - Attempt to add item to an invalid metadata structure
+    printNegativeTestHeader(stdout,"psMetadata","Test G - Attempt to add item to metadata w/o hash table",
+                            "Couldn't add item to invalid metadata structure.",0);
+    errMetadata = psMetadataAlloc();
+    mdTable = errMetadata->hash;
+    errMetadata->hash = NULL;
+    if ( psMetadataAddItem(errMetadata, item3, PS_LIST_HEAD, PS_META_DEFAULT) ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem did not return false w/ invalid metadata struct w/o hash table.");
+        return 21;
+    }
+    if ( psMetadataRemove(errMetadata, PS_LIST_HEAD, "errItem") ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataRemove did not return false w/ invalid metadata struct w/o hash table.");
+        return 31;
+    }
+    errMetadata->hash = mdTable;
+    printFooter(stdout,"psMetadata","Test G - Attempt to add item to invalid metadata w/o hash table", true);
+
+    // Test H - Attempt to add item to an invalid metadata structure
+    printNegativeTestHeader(stdout,"psMetadata","Test H - Attempt to add item to metadata w/o link list",
+                            "Couldn't add item to invalid metadata structure.",0);
+    mdList = errMetadata->list;
+    errMetadata->list = NULL;
+    if ( psMetadataAddItem(errMetadata, item3, PS_LIST_HEAD, PS_META_DEFAULT) ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem did not return false w/ invalid metadata struct w/o link list.");
+        return 22;
+    }
+    if ( psMetadataRemove(errMetadata, PS_LIST_HEAD, "errItem") ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataRemove did not return false w/ invalid metadata struct w/o link list.");
+        return 32;
+    }
+    errMetadata->list = mdList;
+    printFooter(stdout,"psMetadata","Test H - Attempt to add item to invalid metadata w/o link list", true);
+
+    // Test I - Attempt to add item with a null name
+    printNegativeTestHeader(stdout,"psMetadata","Test I - Attempt to add item with null name",
+                            "Couldn't add item with null name.",0);
+    errName = errItem->name;
+    errItem->name = NULL;
+    if ( psMetadataAddItem(errMetadata, errItem, PS_LIST_HEAD, PS_META_DEFAULT) ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem did not return false with item with null name.");
+        return 23;
+    }
+    errItem->name = errName;
+    printFooter(stdout,"psMetadata","Test I - Attempt to add item with null name", true);
+
+    // Test J - Attempt to add item to an invalid metadata structure
+    printNegativeTestHeader(stdout,"psMetadata","Test J - Attempt to add item to metadata w/o hash table",
+                            "Couldn't add item to invalid metadata structure.",0);
+    mdTable = errMetadata->hash;
+    errMetadata->hash = NULL;
+    if (psMetadataAdd(errMetadata, PS_LIST_HEAD, "errItem", PS_DATA_S32, "Integer",22) ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem did not return false w/ invalid metadata struct w/o hash table.");
+        return 24;
+    }
+    errMetadata->hash = mdTable;
+    printFooter(stdout,"psMetadata","Test J - Attempt to add item to invalid metadata w/o hash table", true);
+
+    // Test K - Free psMetadata
+    printPositiveTestHeader(stdout, "psMetadata", "Test K - Free psMetadata");
+    psFree(metadata);
+    psFree(item1);
+    psFree(item2);
+    psFree(item3);
+    psFree(item4);
+    psFree(errItem);
+    psFree(errMetadata);
+    if( psMemCheckLeaks(0, NULL, stdout,false) != 0 ) {
+        psError(PS_ERR_UNKNOWN, true,"Memory leaks detected.");
+        return 25;
+    }
+    psMemCheckCorruption(0);
+    psS32 nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+    }
+    printFooter(stdout, "psMetadata", "Test K - Free psMetadata", true);
+
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_04.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_04.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_04.c	(revision 22322)
@@ -0,0 +1,332 @@
+/** @file  tst_psMetadata_04.c
+ *
+ *  @brief Test driver for psMetadata functions
+ *
+ *  This test driver contains the following tests for psMetadata:
+ *     Test A - Allocate metadata and items
+ *     Test B - Lookup metadata item by name
+ *     Test C - Attempt to use null metadata
+ *     Test D - Attempt to use null key
+ *     Test E - Attempt to lookup non-existant metadata item
+ *     Test F - Lookup metadata item by index
+ *     Test G - Lookup metadata item and return psS32 value
+ *     Test H - Lookup metadata item and return psF64 value
+ *     Test I - Lookup metadata item and return psVector pointer
+ *     Test J - Lookup metadata item and return psMetadata pointer
+ *     Test K - Lookup metadata item and return psString value
+ *     Test L - Attempt to use null metadata
+ *     Test M - Attempt to get non-existant metadata item
+ *     Test N - Attempt to look up with an invalid metadata object
+ *     Test O - Attempt get item  with an invalid metadata object
+ *     Test P - Attempt get value of non-existant metadata item
+ *     Test Q - Free psMetadata
+ *
+ *  @author  Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.11 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2006-02-03 00:12:02 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static void printMetadataItem(psMetadataItem *metadataItem)
+{
+    printf("Key Name: %8s  ", metadataItem->name);
+    printf("Key mdType: 0x%08x  ", metadataItem->type);
+
+    switch (metadataItem->type) {
+    case PS_DATA_METADATA_MULTI:
+        printf("Key Value: %17c", ' ');
+        break;
+    case PS_DATA_BOOL:
+        printf("Key Value: %15d  ", metadataItem->data.B);
+        break;
+    case PS_DATA_S32:
+        printf("Key Value: %15d  ", metadataItem->data.S32);
+        break;
+    case PS_DATA_F32:
+        printf("Key Value: %15.3f  ", metadataItem->data.F32);
+        break;
+    case PS_DATA_F64:
+        printf("Key Value: %15.3f  ", metadataItem->data.F64);
+        break;
+    case PS_DATA_STRING:
+        printf("Key Value: %15s  ", (char*)metadataItem->data.V);
+        break;
+    default:
+        printf("Bad type: %d ", metadataItem->type);
+    }
+    printf("Key Comment: %s\n", metadataItem->comment);
+}
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psBool status = 0;
+    psMetadataItem *item1 = NULL;
+    psMetadataItem *item2 = NULL;
+    psMetadataItem *item3 = NULL;
+    psMetadataItem *item4 = NULL;
+    psMetadataItem *item5 = NULL;
+    psMetadataItem *item6 = NULL;
+    psMetadataItem *item7 = NULL;
+    psMetadataItem *item8 = NULL;
+    psMetadataItem *item = NULL;
+    psMetadata *metadata = NULL;
+    psMetadata *newMD = NULL;
+    char string[50];
+    psHash* tmpTable = NULL;
+    psList* tmpList = NULL;
+
+
+    // Test A - Allocate metadata and items
+    printPositiveTestHeader(stdout, "psMetadata", "Test A - Allocate metadata items");
+    psVector *vec = psVectorAlloc(5, PS_TYPE_S32);
+    metadata = psMetadataAlloc();
+    newMD = psMetadataAlloc();
+    strncpy(string, "String was here", 50);
+    psMetadataAddF64(newMD, PS_LIST_HEAD, "yourItem1", 0, "I am a psF64", 4.14);
+    item1 = psMetadataItemAlloc("myItem1", PS_DATA_BOOL, "I am a boolean", true);
+    item2 = psMetadataItemAlloc("myItem2", PS_DATA_S32, "I am a integer", 55);
+    item3 = psMetadataItemAlloc("myItem3", PS_DATA_BOOL, "I am a boolean", false);
+    item4 = psMetadataItemAlloc("myItem4", PS_DATA_S32, "I am a integer", 66);
+    item5 = psMetadataItemAlloc("myItem5", PS_DATA_F64, "I am a double", 3.14);
+    item6 = psMetadataItemAlloc("myItem6", PS_DATA_VECTOR, "I am a vector", vec);
+    item7 = psMetadataItemAlloc("myItem7", PS_DATA_METADATA, "I am a metadata", newMD);
+    item8 = psMetadataItemAlloc("myItem8", PS_DATA_STRING, "I am a string", string);
+    printMetadataItem(item1);
+    printMetadataItem(item2);
+    printMetadataItem(item3);
+    printMetadataItem(item4);
+    printMetadataItem(item5);
+    psMetadataAddItem(metadata, item1, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMetadataAddItem(metadata, item2, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMetadataAddItem(metadata, item3, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMetadataAddItem(metadata, item4, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMetadataAddItem(metadata, item5, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMetadataAddItem(metadata, item6, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMetadataAddItem(metadata, item7, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMetadataAddItem(metadata, item8, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMemDecrRefCounter(vec); // vs. psFree, which now would set vec to NULL (want to keep it to comparison later)
+    printFooter(stdout, "psMetadata", "Test A - Allocate metadata items", true);
+
+
+    // Test B - Lookup metadata item by name
+    printPositiveTestHeader(stdout, "psMetadata", "Test B - Lookup metadata item by name");
+    item = psMetadataLookup(metadata, "myItem2");
+    printf("Found item named %s\n", item->name);
+    if(item == NULL) {
+        printf("ERROR: Item should not be null\n");
+        return 10;
+    }
+    printFooter(stdout, "psMetadata", "Test B - Lookup metadata item by name", true);
+
+
+    // Test C - Attempt to use null metadata
+    printNegativeTestHeader(stdout,"psMetadata", "Test C - Attempt to use null metadata",
+                            "Null metadata collection not allowed", 0);
+    if ( psMetadataLookup(NULL, "myItem2") != NULL ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataLookup did not return NULL with null metadata.");
+        return 11;
+    }
+    printFooter(stdout, "psMetadata", "Test C - Attempt to use null metadata", true);
+
+
+    // Test D - Attempt to use null key
+    printNegativeTestHeader(stdout,"psMetadata", "Test D - Attempt to use null key",
+                            "Null key name not allowed", 0);
+    if ( psMetadataLookup(metadata, NULL) != NULL ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadata did not return NULL with null key.");
+        return 12;
+    }
+    printFooter(stdout, "psMetadata", "Test D - Attempt to use null key", true);
+
+
+    // Test E - Attempt to lookup non-existant metadata item
+    printNegativeTestHeader(stdout,"psMetadata", "Test E - Attempt to lookup non-existant metadata item",
+                            "Couldn't find metadata item. Name: AARGH", 0);
+    if ( psMetadataLookup(metadata, "AARGH") != NULL ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadata did not return NULL with a non-existant item.");
+        return 13;
+    }
+    printFooter(stdout, "psMetadata", "Test E - Attempt to lookup non-existant metadata item", true);
+
+
+    // Test F - Lookup metadata item by index
+    printPositiveTestHeader(stdout, "psMetadata", "Test F - Lookup metadata item by index");
+    item = psMetadataGet(metadata, 0);
+    printf("Found item named %s\n", item->name);
+    if(item == NULL) {
+        printf("ERROR: Item should not be null\n");
+        return 14;
+    }
+    printFooter(stdout, "psMetadata", "Test F - Lookup metadata item by index", true);
+
+    // Test G - Lookup metadata item and return psS32 value
+    printPositiveTestHeader(stdout, "psMetadata", "Test G - Lookup metadata item and return psS32 value");
+    psS32 valueS32 = 0;
+    valueS32 = psMetadataLookupS32(&status, metadata, "myItem2");
+    if(valueS32 != 55) {
+        printf("ERROR: Bad value, %d, Expected 55\n", valueS32);
+        return 15;
+    } else if(! status) {
+        printf("ERROR: Bad status, %d\n", status);
+        return 15;
+    }
+    printFooter(stdout, "psMetadata", "Test G - Lookup metadata item and return psS32 value", true);
+
+    // Test H - Lookup metadata item and return psF64 value
+    printPositiveTestHeader(stdout, "psMetadata", "Test H - Lookup metadata item and return psF64 value");
+    psF64 valueF64 = 0.0;
+    valueF64 = psMetadataLookupF64(&status, metadata, "myItem5");
+    if(fabs(valueF64-3.14) > FLT_EPSILON) {
+        printf("ERROR: Bad value, %g, Expected 3.14\n", valueF64);
+        return 16;
+    } else if(! status) {
+        printf("ERROR: Bad status, %d\n", status);
+        return 16;
+    }
+    printFooter(stdout, "psMetadata", "Test H - Lookup metadata item and return psF64 value", true);
+
+    // Test I - Lookup metadata item and return psVector pointer
+    printPositiveTestHeader(stdout, "psMetadata", "Test I - Lookup metadata item and return psVector pointer");
+    psVector *valueVec = psMetadataLookupPtr(&status, metadata, "myItem6");
+    if(valueVec != vec) {
+        printf("ERROR: Bad vector pointer\n");
+        return 17;
+    } else if(! status) {
+        printf("ERROR: Bad status, %d\n", status);
+        return 17;
+    }
+    printFooter(stdout, "psMetadata", "Test I - Lookup metadata item and return psVector pointer", true);
+
+    // Test J - Lookup metadata item and return psMetadata pointer
+    printPositiveTestHeader(stdout, "psMetadata", "Test J - Lookup metadata item and return psMetadata pointer");
+    psMetadata *metaData = NULL;
+    metaData = psMetadataLookupMD(&status, metadata, "myItem7");
+    if(metaData != newMD) {
+        printf("ERROR: Bad metadata pointer\n");
+        return 25;
+    } else if(! status) {
+        printf("ERROR: Bad status, %d\n", status);
+        return 25;
+    }
+    printFooter(stdout, "psMetadata", "Test J - Lookup metadata item and return psMetadata pointer", true);
+
+    // Test K - Lookup metadata item and return psString value
+    printPositiveTestHeader(stdout, "psMetadata", "Test K - Lookup metadata item and return psString value");
+    psString newSTR;
+    newSTR = psMetadataLookupStr(&status, metadata, "myItem8");
+    if( strncmp(newSTR, string, 50) ) {
+        printf("ERROR: Bad string value \n");
+        return 26;
+    } else if(! status) {
+        printf("ERROR: Bad status, %d\n", status);
+        return 26;
+    }
+    printFooter(stdout, "psMetadata", "Test K - Lookup metadata item and return psString value", true);
+
+    // Test L - Attempt to use null metadata
+    printNegativeTestHeader(stdout,"psMetadata", "Test L - Attempt to use null metadata",
+                            "Null metadata collection not allowed", 0);
+    item = psMetadataGet(NULL, 0);
+    if ( item != NULL ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataGet did not return NULL with null metadata structure");
+        return 18;
+    }
+    printFooter(stdout, "psMetadata", "Test L - Attempt to use null metadata", true);
+
+
+    // Test M - Attempt to get non-existant metadata item
+    printNegativeTestHeader(stdout,"psMetadata", "Test M - Attempt to get non-existant metadata item",
+                            "Couldn't find metadata item with given index. Index: 22", 0);
+    if ( psMetadataGet(metadata, 22) != NULL ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataGet did not return NULL with non-existant metadata item");
+        return 19;
+    }
+    printFooter(stdout, "psMetadata", "Test M - Attempt to get non-existant metadata item", true);
+
+    // Test N - Attempt to look up with an invalid metadata object
+    printNegativeTestHeader(stdout,"psMetadata","Test N - Attemp to look up with an invalid metadata",
+                            "Lookup item with invalid metadata object.", 0 );
+    tmpTable = metadata->hash;
+    metadata->hash = NULL;
+    if ( psMetadataLookup(metadata,"myItem2") != NULL ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataLookup did not return null for invalid metadata object");
+        return 20;
+    }
+    metadata->hash = tmpTable;
+    printFooter(stdout, "psMetadata","Test N - Attempt to lookup an invalid metadata object.",true);
+
+    // Test O - Attempt get item with an invalid metadata object
+    printNegativeTestHeader(stdout,"psMetadata","Test O - Attempt to get item with an invalid metadata",
+                            "Get item with invalid metadata object.", 0 );
+    tmpList = metadata->list;
+    metadata->list = NULL;
+    if ( psMetadataGet(metadata,0) != NULL ) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataLookup did not return null for invalid metadata object");
+        return 21;
+    }
+    metadata->list = tmpList;
+    printFooter(stdout, "psMetadata","Test O - Attempt to get item with  invalid metadata object",true);
+
+    // Test P - Attempt get psS32 value of non-existant metadata item
+    printPositiveTestHeader(stdout, "psMetadata", "Test P - Attempt get psS32 value of non-existant metadata item");
+    valueS32 = psMetadataLookupS32(&status, metadata, "myItem22");
+    if(status) {
+        printf("ERROR: Bad status, %d, \n", status);
+        return 22;
+    }
+    printFooter(stdout, "psMetadata","Test P - Attempt get psS32 value of non-existant metadata item",true);
+
+    // Test P - Attempt get psF64 value of non-existant metadata item
+    printPositiveTestHeader(stdout, "psMetadata", "Test P - Attempt get psF64 value of non-existant metadata item");
+    valueF64 = psMetadataLookupF64(&status, metadata, "myItem22");
+    if(status) {
+        printf("ERROR: Bad status, %d, \n", status);
+        return 23;
+    }
+    printFooter(stdout, "psMetadata","Test P - Attempt get psF64 value of non-existant metadata item",true);
+
+    // Test P - Attempt get psVector value of non-existant metadata item
+    printPositiveTestHeader(stdout, "psMetadata", "Test P - Attempt get psVector value of non-existant metadata item");
+    valueVec = psMetadataLookupPtr(&status, metadata, "myItem22");
+    if(status) {
+        printf("ERROR: Bad status, %d, \n", status);
+        return 24;
+    }
+    printFooter(stdout, "psMetadata","Test P - Attempt get psVector value of non-existant metadata item",true);
+
+    // Test Q - Free psMetadata
+    printPositiveTestHeader(stdout, "psMetadata", "Test Q - Free psMetadata");
+    //    psFree(string);
+    psFree(newMD);
+    psFree(item1);
+    psFree(item2);
+    psFree(item3);
+    psFree(item4);
+    psFree(item5);
+    psFree(item6);
+    psFree(item7);
+    psFree(item8);
+    psFree(metadata);
+    if ( psMemCheckLeaks(0, NULL, stdout,false) != 0 ) {
+        psError(PS_ERR_UNKNOWN, true,"memory leaks detected.");
+        return 23;
+    }
+    psMemCheckCorruption(0);
+    psS32 nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+        return 24;
+    }
+    printFooter(stdout, "psMetadata", "Test Q - Free psMetadata", true);
+
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_05.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_05.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_05.c	(revision 22322)
@@ -0,0 +1,342 @@
+
+
+/** @file  tst_psMetadata_05.c
+*
+*  @brief Test driver for psMetadata functions
+*
+*  This test driver contains the following tests for psMetadata:
+*     Test A - Allocate metadata and items
+*     Test B - Set iterator at second index
+*     Test C - Get next item at index
+*     Test D - Get next item at index and string
+*     Test E - Get previous item at index
+*     Test F - Write metadata item to file
+*     Test G - Attempt to use null metadata with setIterator
+*     Test H - Attempt to use null metadata with getNext
+*     Test I - Attempt to use null metadata with getPrevious
+*     Test J - Attempt to use null file with itemPrint
+*     Test K - Attempt to use null format with itemPrint
+*     Test L - Attempt to use null item with itemPrint
+*     Test M - Free psMetadata
+*
+*  @author  Ross Harman, MHPCC
+*
+*  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2005-09-26 21:13:33 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static void printMetadataItem(psMetadataItem *metadataItem)
+{
+    printf("Key Name: %8s  ", metadataItem->name);
+    printf("Key mdType: 0x%08x  ", metadataItem->type);
+
+    switch (metadataItem->type) {
+    case PS_DATA_METADATA_MULTI:
+        printf("Key Value: %17c", ' ');
+        break;
+    case PS_DATA_BOOL:
+        printf("Key Value: %15d  ", metadataItem->data.B);
+        break;
+    case PS_DATA_S32:
+        printf("Key Value: %15d  ", metadataItem->data.S32);
+        break;
+    case PS_DATA_F32:
+        printf("Key Value: %15.3f  ", metadataItem->data.F32);
+        break;
+    case PS_DATA_F64:
+        printf("Key Value: %15.3f  ", metadataItem->data.F64);
+        break;
+    case PS_DATA_STRING:
+        printf("Key Value: %15s  ", (char*)metadataItem->data.V);
+        break;
+    default:
+        printf("Bad type: %d ", metadataItem->type);
+    }
+    printf("Key Comment: %s\n", metadataItem->comment);
+}
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psMetadataItem *item1 = NULL;
+    psMetadataItem *item2 = NULL;
+    psMetadataItem *item3 = NULL;
+    psMetadataItem *item4 = NULL;
+    psMetadataItem *item5 = NULL;
+    psMetadataItem *item6 = NULL;
+    psMetadataItem *item7 = NULL;
+    //    psMetadataItem *item8 = NULL;
+    psMetadata *metadata = NULL;
+    FILE *fd = NULL;
+
+    // Test A - Allocate metadata and items
+    printPositiveTestHeader(stdout, "psMetadata", "Test A - Allocate metadata items");
+    metadata = psMetadataAlloc();
+    item1 = psMetadataItemAlloc("myItem1", PS_DATA_BOOL, "I am a boolean", true);
+    item2 = psMetadataItemAlloc("myItem2", PS_DATA_S32, "I am a integer", 55);
+    item3 = psMetadataItemAlloc("myItem3", PS_DATA_BOOL, "I am a boolean", false);
+    item4 = psMetadataItemAlloc("myItem4", PS_DATA_S32, "I am a integer", 66);
+    item5 = psMetadataItemAlloc("myItem5", PS_DATA_F32, "I am a float", 3.14);
+    item6 = psMetadataItemAlloc("myItem6", PS_DATA_F64,"I am a double", 6.28);
+    item7 = psMetadataItemAlloc("myItem7", PS_DATA_STRING, "I am a string", "GNIRTS");
+    //    item8 = psMetadataItemAlloc("myItem8", PS_TYPE_PTR, PS_META_UNKNOWN, "I am unknown");
+    printMetadataItem(item1);
+    printMetadataItem(item2);
+    printMetadataItem(item3);
+    printMetadataItem(item4);
+    psMetadataAddItem(metadata, item1, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMetadataAddItem(metadata, item2, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMetadataAddItem(metadata, item3, PS_LIST_HEAD, PS_META_DEFAULT);
+    psMetadataAddItem(metadata, item4, PS_LIST_HEAD, PS_META_DEFAULT);
+    printFooter(stdout, "psMetadata", "Test A - Allocate metadata items", true);
+
+    /*
+        // Test B - Set iterator at second index
+        printPositiveTestHeader(stdout, "psMetadata", "Test B - Set iterator at second index");
+        if (!psMetadataSetIterator(metadata, 2)) {
+            psError(PS_ERR_UNKNOWN,true,"Failed to set iterator");
+            return 100;
+        }
+        printFooter(stdout, "psMetadata", "Test B - Set iterator at second index", true);
+
+
+        // Test C - Get next item at index
+        printPositiveTestHeader(stdout, "psMetadata", "Test C - Get next item at index");
+        item = psMetadataGetNext(metadata, NULL, 2);
+        if(item == NULL) {
+            printf("ERROR: Item should not be null\n");
+        } else {
+            printf("Found item named %s\n", item->name);
+        }
+        printFooter(stdout, "psMetadata", "Test C - Get next item at index", true);
+
+
+        // Test D - Get next item at index and string
+        printPositiveTestHeader(stdout, "psMetadata", "Test D - Get next item at index and string");
+        item = psMetadataGetNext(metadata, "myItem1", 1);
+        if(item == NULL) {
+            printf("ERROR: Item should not be null\n");
+        } else {
+            printf("Found item named %s\n", item->name);
+        }
+        printFooter(stdout, "psMetadata", "Test D - Get next item at index and string", true);
+
+
+        // Test E - Get previous item at index
+        printPositiveTestHeader(stdout, "psMetadata", "Test E - Get previous item at index");
+        item = psMetadataGetPrevious(metadata, NULL, 1);
+        if(item == NULL) {
+            printf("ERROR: Item should not be null\n");
+        } else {
+            printf("Found item named %s\n", item->name);
+        }
+        if( psMetadataGetPrevious(metadata,"myItem3",1) == NULL) {
+            psError(PS_ERR_UNKNOWN, true,"psMetadataGetPrevious did not return an item from metadata.");
+            return 45;
+        }
+        tmpList = metadata->list;
+        metadata->list = NULL;
+        if ( psMetadataGetPrevious(metadata,NULL,1) != NULL) {
+            psError(PS_ERR_UNKNOWN, true,"psMetadataGetPrevious did not return null for invalid metadata.");
+            return 50;
+        }
+        metadata->list = tmpList;
+        printFooter(stdout, "psMetadata", "Test E - Get previous item at index", true);
+
+    */
+    // Test F - Write metadata item to file
+    printPositiveTestHeader(stdout, "psMetadata", "Test F - Write metadata item to file");
+    fd = fopen("temp/tst_psMetadata05_OUT", "w");
+    if(fd == NULL) {
+        printf("ERROR: Couldn't open file for writing\n");
+    }
+
+    if (psMetadataItemPrint(fd, "S32 = %ld\n", item2) == false) {
+        printf("ERROR: Return type should be true\n");
+        return 10;
+    }
+
+    if (psMetadataItemPrint(fd, "S32 = %+06.1f\n", item2) == false) {
+        printf("ERROR: Return type should be true\n");
+        return 10;
+    }
+
+    if (psMetadataItemPrint(fd, "BOL = %ld\n", item3) == false) {
+        printf("ERROR: Return type should be true\n");
+        return 11;
+    }
+
+    if (psMetadataItemPrint(fd, "F32 = %g\n", item5) == false) {
+        printf("ERROR: Return type should be true\n");
+        return 12;
+    }
+
+    if (psMetadataItemPrint(fd, "F32 = % #i\n", item5) == false) {
+        printf("ERROR: Return type should be true\n");
+        return 12;
+    }
+
+    if (psMetadataItemPrint(fd, "F64 = %g\n", item6) == false) {
+        printf("ERROR: Return type should be true\n");
+        return 13;
+    }
+
+    if (psMetadataItemPrint(fd, "STR = %s\n", item7) == false) {
+        printf("ERROR: Return type should be true\n");
+        return 14;
+    }
+    //    psLogMsg(__func__,PS_LOG_INFO,"Attempt to print item of invalid type, should generate error message");
+    //    if (psMetadataItemPrint(fd, "UNK = \n", item8) == true) {
+    //        printf("ERROR: Return type should be false\n");
+    //        fclose(fd);
+    //        return 15;
+    //    }
+    fclose(fd);
+
+    fd = fopen("temp/tst_psMetadata05_OUT", "r");
+    if(fd == NULL) {
+        printf("ERROR: Couldn't open file for reading\n");
+    }
+
+    char line[256];
+    char truth1[] = "S32 = 55";
+    fgets(line, 256, fd);
+    if(strncmp(line, truth1, 8)) {
+        printf("ERROR: Data in file is not as expected. Value: %s. Should be: %s\n", line, truth1);
+    }
+
+    char truth1b[] = "S32 = +055.0";
+    fgets(line, 256, fd);
+    if(strncmp(line, truth1b, 12)) {
+        printf("ERROR: Data in file is not as expected. Value: %s. Should be: %s\n", line, truth1b);
+    }
+
+    char truth2[] = "BOL = 0";
+    fgets(line, 256, fd);
+    if(strncmp(line, truth2, 7)) {
+        printf("ERROR: Data in file is not as expected. Value: %s. Should be: %s\n", line, truth2);
+    }
+
+    char truth3[] = "F32 = 3.14";
+    fgets(line, 256, fd);
+    if(strncmp(line, truth3, 10)) {
+        printf("ERROR: Data in file is not as expected. Value: %s. Should be: %s\n", line, truth3);
+    }
+
+    char truth3b[] = "F32 =  3";
+    fgets(line, 256, fd);
+    if(strncmp(line, truth3b, 8)) {
+        printf("ERROR: Data in file is not as expected. Value: %s. Should be: %s\n", line, truth3b);
+    }
+
+    char truth4[] = "F64 = 6.28";
+    fgets(line, 256, fd);
+    if(strncmp(line, truth4, 10)) {
+        printf("ERROR: Data in file is not as expected. Value: %s. Should be: %s\n", line, truth4);
+    }
+
+    char truth5[] = "STR = GNIRTS";
+    fgets(line, 256, fd);
+    if(strncmp(line, truth5, 12)) {
+        printf("ERROR: Data in file is not as expected. Value: %s. Should be: %s\n", line, truth5);
+    }
+
+    fclose(fd);
+    printFooter(stdout, "psMetadata", "Test F - Write metadata item to file", true);
+    /*
+        // Test G - Attempt to use null metadata with setIterator
+        printNegativeTestHeader(stdout,"psMetadata", "Test G - Attempt to use null metadata with setIterator",
+                                "Null metadata collection not allowed", 0);
+        if( psMetadataSetIterator(NULL, 0)) {
+            psError(PS_ERR_UNKNOWN,true,"Set iterator did not detect invalid parameter.");
+            return 101;
+        }
+        tmpList = metadata->list;
+        metadata->list = NULL;
+        if( psMetadataSetIterator(metadata,0)) {
+            psError(PS_ERR_UNKNOWN,true,"Set iterator did not detect null list.");
+            return 102;
+        }
+        metadata->list = tmpList;
+        printFooter(stdout, "psMetadata", "Test G - Attempt to use null metadata with setIterator", true);
+
+
+        // Test H - Attempt to use null metadata with getNext
+        printNegativeTestHeader(stdout,"psMetadata", "Test H - Attempt to use null metadata with getNext",
+                                "Null metadata collection not allowed", 0);
+        if( psMetadataGetNext(NULL, "myItem2", 0) != NULL ) {
+            psError(PS_ERR_UNKNOWN, true,"psMetadataGetNext did not return null for null metadata.");
+            return 40;
+        }
+        tmpList = metadata->list;
+        metadata->list = NULL;
+        if ( psMetadataGetNext(metadata, "myItem2", 0) != NULL ) {
+            psError(PS_ERR_UNKNOWN, true,"psMetadataGetNext did not return null for invalid metadata.");
+            return 41;
+        }
+        metadata->list = tmpList;
+
+        printFooter(stdout, "psMetadata", "Test H - Attempt to use null metadata with getNext", true);
+
+
+        // Test I - Attempt to use null metadata with getPrevious
+        printNegativeTestHeader(stdout,"psMetadata", "Test I - Attempt to use null metadata with getPrevious",
+                                "Null metadata collection not allowed", 0);
+        psMetadataGetPrevious(NULL, "myItem2", 0);
+        printFooter(stdout, "psMetadata", "Test I - Attempt to use null metadata with getPrevious", true);
+
+    */
+    // Test J - Attempt to use null file with itemPrint
+    printNegativeTestHeader(stdout,"psMetadata", "Test J - Attempt to use null file with itemPrint",
+                            "Null file descriptor not allowed", 0);
+    psMetadataItemPrint(NULL, "Item value: %ld", item2);
+    printFooter(stdout, "psMetadata", "Test J - Attempt to use null file with itemPrint", true);
+
+
+    // Test K - Attempt to use null format with itemPrint
+    printNegativeTestHeader(stdout,"psMetadata", "Test K - Attempt to use null format with itemPrint",
+                            "Null format not allowed", 0);
+    psMetadataItemPrint(fd, NULL, item2);
+    printFooter(stdout, "psMetadata", "Test K - Attempt to use null format with itemPrint", true);
+
+
+    // Test L - Attempt to use null item with itemPrint
+    printNegativeTestHeader(stdout,"psMetadata", "Test L - Attempt to use null item with itemPrint",
+                            "Null metadata not allowed", 0);
+    psMetadataItemPrint(fd, "Item value: %ld", NULL);
+    printFooter(stdout, "psMetadata", "Test L - Attempt to use null item with itemPrint", true);
+
+
+    // Test M - Free psMetadata
+    printPositiveTestHeader(stdout, "psMetadata", "Test M - Free psMetadata");
+    psFree(metadata);
+    psFree(item1);
+    psFree(item2);
+    psFree(item3);
+    psFree(item4);
+    psFree(item5);
+    psFree(item6);
+    psFree(item7);
+    //    psFree(item8);
+    if ( psMemCheckLeaks(0, NULL, stdout, false) != 0 ) {
+        psError(PS_ERR_UNKNOWN, true,"Memory leaks detected.");
+        return 50;
+    }
+    psMemCheckCorruption(0);
+    psS32 nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+        return 51;
+    }
+    printFooter(stdout, "psMetadata", "Test M - Free psMetadata", true);
+
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_06.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_06.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_06.c	(revision 22322)
@@ -0,0 +1,166 @@
+
+/** @file  tst_psMetadata_06.c
+*
+*  @brief Test driver for psMetadata functions
+*
+*  This test driver contains the following tests for psMetadata:
+*     Test A - Allocate metadata and items
+*     Test B - Add leaf node on top of existing leaf node
+*     Test C - Add leaf node to existing folder node
+*     Test D - Add folder node on top of existing leaf node
+*     Test E - Free psMetadata
+*
+*  @author  Ross Harman, MHPCC
+*
+*  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2005-09-26 21:13:33 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+
+#include <string.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static void printMetadataItem(psMetadataItem *metadataItem)
+{
+    printf("Key Name: %8s  ", metadataItem->name);
+    printf("Key mdType: 0x%08x  ", metadataItem->type);
+
+    switch (metadataItem->type) {
+    case PS_DATA_METADATA_MULTI:
+        printf("Key Value: %17c", ' ');
+        break;
+    case PS_DATA_LIST:
+        printf("Key Value: %15s  ", "psList");
+        break;
+    case PS_DATA_BOOL:
+        printf("Key Value: %15d  ", metadataItem->data.B);
+        break;
+    case PS_DATA_S32:
+        printf("Key Value: %15d  ", metadataItem->data.S32);
+        break;
+    case PS_DATA_F32:
+        printf("Key Value: %15.3f  ", metadataItem->data.F32);
+        break;
+    case PS_DATA_F64:
+        printf("Key Value: %15.3f  ", metadataItem->data.F64);
+        break;
+    case PS_DATA_STRING:
+        printf("Key Value: %15s  ", (char*)metadataItem->data.V);
+        break;
+    default:
+        printf("Bad type: %d ", metadataItem->type);
+    }
+    printf("Key Comment: %s\n", metadataItem->comment);
+}
+
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psMetadataItem *item1a = NULL;
+    psMetadataItem *item1b = NULL;
+    psMetadataItem *item1c = NULL;
+    psMetadataItem *item2a = NULL;
+    psMetadataItem *item2b = NULL;
+    psMetadata *metadata = NULL;
+
+
+    // Test A - Allocate metadata and items
+    printPositiveTestHeader(stdout, "psMetadata", "Test A - Allocate metadata and items");
+    item1a = psMetadataItemAlloc("myItem1", PS_DATA_BOOL, "I am a boolean", true);
+    item1b = psMetadataItemAlloc("myItem1", PS_DATA_S32, "I am a signed integer", 111);
+    item1c = psMetadataItemAlloc("myItem1", PS_DATA_S32, "I am a signed integer", 222);
+    item2a = psMetadataItemAlloc("myItem2", PS_DATA_S32, "I am a signed integer", 333);
+    item2b = psMetadataItemAlloc("myItem2", PS_DATA_LIST, "I am a list", NULL);
+    metadata = psMetadataAlloc();
+    printMetadataItem(item1a);
+    printMetadataItem(item1b);
+    printMetadataItem(item1c);
+    printMetadataItem(item2a);
+    printMetadataItem(item2b);
+    printFooter(stdout, "psMetadata", "Test A - Allocate metadata and items", true);
+
+
+    // Test B - Add leaf node on top of existing leaf node
+    printPositiveTestHeader(stdout, "psMetadata", "Test B - replace an item in the metadata");
+    if(!psMetadataAddItem(metadata, item1a, PS_LIST_HEAD, PS_META_DEFAULT)) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem returned false for adding a node top.");
+        return 20;
+    }
+    psMetadataItem* tempItem = psMetadataLookup(metadata, item1a->name);
+    if (tempItem != item1a) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem didn't add the metadata entry.");
+        return 20;
+    }
+
+    if (!psMetadataAddItem(metadata, item1b, PS_LIST_HEAD, PS_META_REPLACE)) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem returned false for adding a node top.");
+        return 21;
+    }
+
+    tempItem = psMetadataLookup(metadata, item1a->name);
+    if (tempItem != item1b) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem didn't replace the metadata entry.");
+        return 21;
+    }
+
+    printFooter(stdout, "psMetadata", "Test B - replace an item in the metadata", true);
+
+
+    // Test C - Add leaf node to existing folder node
+    printPositiveTestHeader(stdout, "psMetadata", "Test C - add duplicate-key metadata item");
+    if (!psMetadataAddItem(metadata, item1c, PS_LIST_HEAD, PS_META_DUPLICATE_OK)) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem return false for adding a not to existing node.");
+        return 22;
+    }
+
+    tempItem = psMetadataLookup(metadata, item1a->name);
+    if (tempItem == NULL || tempItem->type != PS_DATA_METADATA_MULTI) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem didn't add additional metadata entry of same key.");
+        return 22;
+    }
+    printFooter(stdout, "psMetadata", "Test C - add duplicate-key metadata item", true);
+
+
+    // Test D - Add folder node on top of existing leaf node
+    printPositiveTestHeader(stdout, "psMetadata", "Test D - Add folder node on top of existing leaf node");
+    if (!psMetadataAddItem(metadata, item2a, PS_LIST_HEAD, PS_META_DEFAULT)) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem returned false for adding a node to existing leaf node.");
+        return 23;
+    }
+    if (!psMetadataAddItem(metadata, item2b, PS_LIST_HEAD, PS_META_DUPLICATE_OK)) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem returned false for adding a nod to existing leaf node.");
+        return 24;
+    }
+    printFooter(stdout, "psMetadata", "Test D - Add folder node on top of existing leaf node", true);
+    tempItem = psMetadataLookup(metadata, item1a->name);
+    if (tempItem == NULL || tempItem->type != PS_DATA_METADATA_MULTI) {
+        psError(PS_ERR_UNKNOWN, true,"psMetadataAddItem didn't add additional metadata entry of same key.");
+        return 24;
+    }
+
+
+    // Test E - Free psMetadata
+    printPositiveTestHeader(stdout, "psMetadata", "Test E - Free psMetadata");
+    psFree(item1a);
+    psFree(item1b);
+    psFree(item1c);
+    psFree(item2a);
+    psFree(item2b);
+    psFree(metadata);
+    if ( psMemCheckLeaks(0, NULL, stdout, false) != 0 ) {
+        psError(PS_ERR_UNKNOWN, true,"Memory leaks detected.");
+        return 25;
+    }
+    psMemCheckCorruption(0);
+    psS32 nBad = psMemCheckCorruption(0);
+    if(nBad) {
+        printf("ERROR: Found %d bad memory blocks\n", nBad);
+    }
+    printFooter(stdout, "psMetadata", "Test E - Free psMetadata", true);
+
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_07.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_07.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psMetadata_07.c	(revision 22322)
@@ -0,0 +1,362 @@
+/** @file  tst_psMetadata_07.c
+*
+*  @brief Test driver for psMetadataIterator functions
+*
+*  This test driver contains the following tests for psMetadata:
+*     Test A - Allocate a psMetadataIterator
+*     Test B - Set a psMetadataIterator
+*     Test C - Get and Increment a psMetadataIterator
+*     Test D - Get and Decrement a psMetadataIterator
+*     Test E - psMetadataConfigWrite (also tests psMetadataConfigFormat)
+*     Test F - psMetadataRemoveKey, RemoveIndex
+*     Test G - psMetadataAddPtr, psMetadataItemAllocPtr
+*     Test H - psMetadataCopy
+*     Test I - psArgument Functions
+*
+*  @author  David Robbins, MHPCC
+*
+*  @version $Revision: 1.22 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2006-05-03 01:10:09 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+#include "config.h"
+#include "pslib_strict.h"
+#include "psTest.h"
+//#include "psMetadata.h"
+
+static psS32 testMetaIter(void);
+static psS32 testMetaWrite(void);
+static psS32 testMetaRemove(void);
+static psS32 testMetaAddPtr(void);
+static psS32 testMetaCopy(void);
+
+testDescription tests[] = {
+                              {testMetaIter, 1, "Test psMetadataIterator fxns", 0, false},
+                              {testMetaWrite, 2, "Test psMetadataConfigWrite", 0, false},
+                              {testMetaRemove, 3, "Test psMetadataRemoveKey/Index", 0, false},
+                              {testMetaAddPtr, 4, "Test psMetadataAddPtr", 0, false},
+                              {testMetaCopy, 5, "Test psMetadataCopy", 0, false},
+                              {NULL}
+                          };
+
+static psMetadata *setupMeta(void);
+
+static psMetadata *setupMeta(void)
+{
+    psMetadata *md = NULL;
+    psVector *vec = NULL;
+    psMetadata *newMD = NULL;
+    psTime *time;
+    int i = 0;
+    md = psMetadataAlloc();
+    newMD = psMetadataAlloc();
+    vec = psVectorAlloc(60, PS_DATA_S32);
+    time = psTimeAlloc(PS_TIME_TAI);
+    for (i = 0; i < 5; i++) {
+        vec->data.S32[i] = i+1;
+    }
+    vec->n = 5;
+    time->sec = 1000;
+    time->nsec = 25;
+    time->leapsecond = true;
+    psMetadataAddBool(md, PS_LIST_TAIL, "item1", 0, "I am a boolean", true);
+    psMetadataAddS32(md, PS_LIST_TAIL, "item2", 0, "", 55);
+    psMetadataAddF32(md, PS_LIST_TAIL, "item3", 0, NULL, 3.14);
+    psMetadataAddF64(md, PS_LIST_TAIL, "item4", 0, "", 6.28);
+    psMetadataAddStr(md, PS_LIST_TAIL, "item5", 0, "I am a string", "GNIRTS");
+    psMetadataAddVector(md, PS_LIST_TAIL, "vector6", 0, "I am a vector", vec);
+    psMetadataAddTime(md, PS_LIST_TAIL, "time01", 0, "I am time", time);
+
+    psMetadataAddS32(newMD, PS_LIST_TAIL, "ITEM01", 0, NULL, 666);
+    psMetadata *newestMD = NULL;
+    newestMD = psMetadataAlloc();
+    psMetadataAddVector(newestMD, PS_LIST_TAIL, "VECTORNEW", 0, "Newest VECTOR", vec);
+    psMetadataAddStr(newestMD, PS_LIST_TAIL, "cell", 0, "I am a p-Star", "pStArRs");
+    psMetadataAddMetadata(newMD, PS_LIST_TAIL, "META NEW", 0, "I AM Newest METADATA", newestMD);
+    psMetadataAddF32(newMD, PS_LIST_TAIL, "ITEM02", 0, "I AM FLOAT", 666.6);
+    psMetadataAddF64(newMD, PS_LIST_TAIL, "ITEM03", 0, "I AM DOUBLE", 666.666);
+
+    psMetadataAddMetadata(md, PS_LIST_TAIL, "metadata7", 0, "I am a metadata", newMD);
+    psFree(time);
+    psFree(newestMD);
+    psFree(vec);
+    psFree(newMD);
+    return md;
+}
+
+int main(int argc, char* argv[])
+{
+    psLogSetLevel( PS_LOG_INFO );
+    if( !runTestSuite(stderr,"psMetadata_07",tests,argc,argv)) {
+        return 1;
+    }
+    return 0;
+}
+
+//Set a psMetadataIterator //
+static psS32 testMetaIter(void)
+{
+    psMetadata *metadata = NULL;
+    metadata = setupMeta();
+    psMetadataIterator *iter = NULL;
+    psMetadataItem *item1 = NULL;
+    psMetadataItem *item2 = NULL;
+    psMetadataItem *item3 = NULL;
+    printPositiveTestHeader(stdout, "psMetadata", "Test A - Allocate psMetadataIterator");
+    iter = psMetadataIteratorAlloc(metadata, PS_LIST_HEAD, NULL);
+    if (iter == NULL) {
+        fprintf(stderr, "Failed test point - Allocate a psMetadataIterator.  Iterator NULL. \n");
+    }
+    printFooter(stdout, "psMetadata", "Test A - Allocate psMetadataIterator", true);
+    printPositiveTestHeader(stdout, "psMetadata", "Test B - Set psMetadataIterator");
+    psMetadataIteratorSet(iter, 2);
+    item1 = psMetadataGetAndIncrement(iter);
+    if ( !strncmp(item1->name, "item2", 20) ) {
+        fprintf(stderr, "Failed test point - IteratorSet - Item names don't match. \n");
+    }
+    printFooter(stdout, "psMetadata", "Test B - Set psMetadataIterator", true);
+    printPositiveTestHeader(stdout, "psMetadata", "Test C - Get and Increment Iterator");
+    item2 = psMetadataGetAndDecrement(iter);
+    if ( !strncmp(item2->name, "item3", 20) ) {
+        fprintf(stderr, "Failed test point - GetAndIncrement - Item names don't match. \n");
+    }
+    printFooter(stdout, "psMetadata", "Test C - Get and Increment Iterator", true);
+    printPositiveTestHeader(stdout, "psMetadata", "Test D - Get and Decrement Iterator");
+    item3 = psMetadataGetAndDecrement(iter);
+    if ( !strncmp(item3->name, "item2", 20) ) {
+        fprintf(stderr, "Failed test point - GetAndDecrement - Item names don't match. \n");
+    }
+    printFooter(stdout, "psMetadata", "Test D - Get and Decrement Iterator", true);
+    psFree(iter);
+    psFree(metadata);
+    return 0;
+}
+
+static psS32 testMetaWrite(void)
+{
+    psMetadata *newMD = NULL;
+    //Return false for NULL input metadata
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psMetadataConfigWrite(newMD, "mdcfgwrt.out")) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psMetadataConfigWrite failed to return false for NULL input metadata.\n");
+        return 1;
+    }
+    newMD = setupMeta();
+    //Return false for NULL input filename
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psMetadataConfigWrite(newMD, NULL)) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psMetadataConfigWrite failed to return false for NULL input filename.\n");
+        return 2;
+    }
+    //Return false for bad input filename
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psMetadataConfigWrite(newMD, "temp")) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, false,
+                "psMetadataConfigWrite failed to return false for bad input filename.\n");
+        return 3;
+    }
+    //Return true for valid inputs
+    if ( !psMetadataConfigWrite(newMD, "mdcfgwrt.out") ) {
+        fprintf(stderr, "Failed test point E - Failed to write metadata to file. \n");
+        return 4;
+    }
+
+    //Make sure the output file matches the verified file output.
+    FILE *verified;
+    FILE *output;
+    verified = fopen("mdcfgwrt.verified", "r");
+    output = fopen("mdcfgwrt.out", "r");
+    char buffer1[100];
+    char buffer2[100];
+    while ( fscanf(verified, "%s", buffer1) != EOF ) {
+        if (fscanf(output, "%s", buffer2) == EOF) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, false,
+                    "Output file is smaller than the expected output file.\n");
+            return 5;
+        }
+        if (strncmp(buffer1, buffer2, 100) ) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "Output file contents do not match the expected output.\n");
+            return 6;
+        }
+    }
+    if (fscanf(output, "%s", buffer2) != EOF) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, false,
+                "Output file is larger than the expected output file.\n");
+        return 7;
+    }
+    fclose(verified);
+    fclose(output);
+
+    psFree(newMD);
+    return 0;
+}
+
+psS32 testMetaRemove(void)
+{
+    psMetadata *md = NULL;
+    md = setupMeta();
+    if ( !psMetadataRemoveKey(md, "item1") ) {
+        fprintf(stderr, "Failed to remove item1 from psMetadata.\n");
+        return 1;
+    }
+    if ( !psMetadataRemoveIndex(md, PS_LIST_HEAD) ) {
+        fprintf(stderr, "Failed to remove item2 from psMetadata.\n");
+        return 2;
+    }
+    psFree(md);
+    return 0;
+}
+
+psS32 testMetaAddPtr(void)
+{
+    bool status;
+    psVector *vecptr = NULL;
+    psVector *vecptr2 = NULL;
+    vecptr = psVectorAlloc(5, PS_TYPE_S32);
+    vecptr->data.S32[0] = 666;
+    psMetadata *md = NULL;
+    md = psMetadataAlloc();
+    psMetadataItem *item = NULL;
+    psMetadataItem *item2 = NULL;
+    item = psMetadataItemAllocPtr("itemptr", PS_DATA_STRING, "", "Devil String");
+    status = psMetadataAddPtr(md, PS_LIST_HEAD, "vecptr", PS_DATA_VECTOR,
+                              "this is a vector comment", vecptr);
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to add psPtr (vector) to metadata.\n");
+        return 1;
+    }
+    status = false;
+    vecptr2 = (psVector*)psMetadataLookupPtr(&status, md, "vecptr");
+    if (!status) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to retrieve psPtr (vector) from metadata.\n");
+        return 2;
+    }
+    if (vecptr2->data.S32[0] != 666) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Failed to retrieve correct psPtr (vector) from metadata.\n");
+        return 3;
+    }
+    status = false;
+    if (item == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to Alloc psPtr (string) metadata item .\n");
+        return 5;
+    }
+    if ( !psMetadataAddItem(md, item, PS_LIST_TAIL, 0) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to add psPtr (string) metadata item to metadata.\n");
+        return 6;
+    }
+    item2 = psMetadataGet(md, PS_LIST_TAIL);
+    if (item2 == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to retrieve psPtr (string) metadata item .\n");
+        return 7;
+    }
+    if ( strncmp((char*)item2->data.V, "Devil String", 20) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to retrieve correct psPtr (string) metadata item .\n");
+        printf("\n string = %s \n", (char*)item2->data.V);
+        return 8;
+    }
+
+    psMetadata *strtest = psMetadataAlloc();
+    //should return false for NULL input string
+    //    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if ( !psMetadataAddStr(strtest, PS_LIST_TAIL, "bar", 0, "baz", NULL) ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "strtest failed for NULL.\n");
+        return 9;
+    }
+    psFree(strtest);
+
+    psFree(item);
+    psFree(vecptr);
+    psFree(md);
+    return 0;
+
+}
+
+psS32 testMetaCopy(void)
+{
+    psMetadata *test = NULL;
+    psMetadata *empty = NULL;
+    psMetadata *md = setupMeta();
+
+    //Return NULL for NULL input Metadata
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    test = psMetadataCopy(test, empty);
+    if (test != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psMetadataCopy failed to return NULL for NULL input metadata.\n");
+        psFree(md);
+        return 1;
+    }
+
+    //Return a copy in of md in test
+    test = psMetadataCopy(NULL, md);
+    //Check if contents are the same
+    psMetadataItem *testItem = NULL;
+    psMetadataItem *mdItem = NULL;
+    psMetadataIterator *testIterator = NULL;
+    psMetadataIterator *mdIterator = NULL;
+    testIterator = psMetadataIteratorAlloc(test, PS_LIST_HEAD, NULL);
+    mdIterator = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
+    testItem = psMetadataGetAndIncrement(testIterator);
+    mdItem = psMetadataGetAndIncrement(mdIterator);
+    while (testItem != NULL || mdItem != NULL ) {
+        testItem = psMetadataGetAndIncrement(testIterator);
+        mdItem = psMetadataGetAndIncrement(mdIterator);
+    }
+
+    if (!psMetadataConfigWrite(md, "MDCopy.in") ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psMetadataConfigWrite failed to output original metadata contents.\n");
+        return 3;
+    }
+    if (!psMetadataConfigWrite(test, "MDCopy.out") ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psMetadataConfigWrite failed to output copied metadata contents.\n");
+        return 4;
+    }
+    FILE *verified;
+    FILE *output;
+    verified = fopen("MDCopy.in", "r");
+    output = fopen("MDCopy.out", "r");
+    char buffer1[100];
+    char buffer2[100];
+    while ( fscanf(verified, "%s", buffer1) != EOF ) {
+        if (fscanf(output, "%s", buffer2) == EOF) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, false,
+                    "Copied metadata is smaller than the original.\n");
+            return 5;
+        }
+        if (strncmp(buffer1, buffer2, 100) ) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "Copied metadata contents do not match original metadata contents.\n");
+            return 6;
+        }
+    }
+    if (fscanf(output, "%s", buffer2) != EOF) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, false,
+                "Copied metadata is larger than the original.\n");
+        return 7;
+    }
+    fclose(verified);
+    fclose(output);
+
+
+    psFree(testItem);
+    psFree(mdItem);
+    psFree(testIterator);
+    psFree(mdIterator);
+    psFree(test);
+    psFree(md);
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psPixels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psPixels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/types/tst_psPixels.c	(revision 22322)
@@ -0,0 +1,602 @@
+/** @file  tst_psPixels.c
+ *
+ *  @brief Contains the tests for psPixels.[ch]
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.5 $
+ *           $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-04-01 02:43:57 $
+ *
+ *  Copyright 2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psTest.h"
+#include "pslib_strict.h"
+
+static psS32 testPixelsAlloc(void);
+static psS32 testPixelsRealloc(void);
+static psS32 testPixelsCopy(void);
+static int testPixelsToMask(void);
+static int testPixelsFromMask(void);
+static int testPixelsConcatenate(void);
+static psS32 testPixelsGetSet(void);
+static psS32 testPixelsLength( void );
+
+testDescription tests[] = {
+                              {testPixelsAlloc,860,"psPixelsAlloc",0,false},
+                              {testPixelsRealloc,862,"psPixelsRealloc",0,false},
+                              {testPixelsCopy,863,"psPixelsCopy",0,false},
+                              {testPixelsToMask,864,"psPixelsToMask",0,false},
+                              {testPixelsFromMask,865,"psPixelsFromMask",0,false},
+                              {testPixelsConcatenate,866,"psPixelsConcatenate",0,false},
+                              {testPixelsGetSet,867,"psPixelsGet/Set",0,false},
+                              {testPixelsLength,666,"psPixelsLength",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+
+    if ( ! runTestSuite(stderr,"psPixels",tests,argc,argv) ) {
+        psError(PS_ERR_UNKNOWN,true,"One or more tests failed");
+        return 1;
+    }
+    return 0;
+}
+
+psS32 testPixelsAlloc(void)
+{
+
+    psPixels* p0 = psPixelsAlloc(0);
+
+    if (p0 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to allocate a zero-sized psPixels");
+        return 1;
+    }
+    if (p0->data != NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to set data to NULL for a zero-sized psPixels");
+        return 2;
+    }
+    if (p0->n != p0->nalloc) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to set n = 0");
+        return 3;
+    }
+    if (p0->nalloc != 0) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to set nalloc = 0");
+        return 4;
+    }
+
+    psPixels* p1 = psPixelsAlloc(1);
+
+    if (p1 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to allocate a one-sized psPixels");
+        return 11;
+    }
+    if (p1->data == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to allocate data for a one-sized psPixels");
+        return 12;
+    }
+    p1->data[0].x = 1;
+    p1->data[0].y = 2;
+    p1->n++;
+    if (p1->n != p1->nalloc) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to set n = %ld", p1->nalloc);
+        return 13;
+    }
+    if (p1->nalloc != 1) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to set nalloc = 1 (%ld)",
+                p1->nalloc);
+        return 14;
+    }
+
+    psPixels* p2 = psPixelsAlloc(10);
+
+    if (p2 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to allocate a one-sized psPixels");
+        return 11;
+    }
+    if (p2->data == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to allocate data for a one-sized psPixels");
+        return 12;
+    }
+    for (int i = 0; i < 10; i++) {
+        p2->data[i].x = i;
+        p2->data[i].y = 100+i;
+        p2->n++;
+    }
+
+    if (p2->n != p2->nalloc) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to set n = %ld", p2->nalloc);
+        return 13;
+    }
+    if (p2->nalloc != 10) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsAlloc failed to set nalloc = 1 (%ld)",
+                p2->nalloc);
+        return 14;
+    }
+
+
+    psFree(p2);
+    psFree(p1);
+    psFree(p0);
+
+    return 0;
+}
+
+psS32 testPixelsRealloc(void)
+{
+
+    // first, tests that reallocing a NULL just allocates
+    psPixels* p0 = psPixelsRealloc(NULL,10);
+    p0->n = 10;
+
+    if (p0 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to allocate a zero-sized psPixels");
+        return 1;
+    }
+    if (p0->data == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to set data to NULL for a zero-sized psPixels");
+        return 2;
+    }
+    if (p0->n != p0->nalloc) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to set n = 0");
+        return 3;
+    }
+    if (p0->nalloc != 10) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to set nalloc = 10");
+        return 4;
+    }
+    for (int i = 0; i < 10; i++) {
+        p0->data[i].x = i;
+        p0->data[i].y = 100+i;
+    }
+    p0->n = 10;
+
+    psPixels* p1 = psPixelsRealloc(p0, 20);
+    //    p1->n = 20;
+
+    if (p1 != p0) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to return a resized input psPixels");
+        return 11;
+    }
+    if (p1->data == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to set data to non-NULL");
+        return 12;
+    }
+    if (p1->n != 10) {
+        psError(PS_ERR_UNKNOWN, true,
+                "resizing up didn't preserve the size");
+        return 13;
+    }
+    if (p1->nalloc != 20) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to set nalloc = 0");
+        return 14;
+    }
+    for (int i = 0; i < 10; i++) {
+        if (p0->data[i].x != i || p0->data[i].y != 100+i) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "The value of pixel %d was not preserved (%f,%f) vs (%d,%d).",
+                    i, p0->data[i].x, p0->data[i].y, i, 100+i);
+            return 15;
+        }
+    }
+    for (int i = 10; i < 20; i++) {
+        p1->data[i].x = i;
+        p1->data[i].y = 100+i;
+    }
+    p1->n = 20;
+
+
+    psPixels* p2 = psPixelsRealloc(p1,5);
+    p2->n = 5;
+    if (p2 != p1) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc didn't return input psPixels.");
+        return 21;
+    }
+    if (p2->data == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to allocate data");
+        return 22;
+    }
+
+    if (p2->n != 5) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to set n=nalloc after shrinking");
+        return 23;
+    }
+    if (p2->nalloc != 5) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to set nalloc properly");
+        return 24;
+    }
+
+    psPixels* p3 = psPixelsRealloc(p2,0);
+
+    if (p3 != p2) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc didn't return input psPixels.");
+        return 31;
+    }
+    if (p3->data != NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to deallocate data when size=0");
+        return 32;
+    }
+
+    if (p3->n != 0) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to set n=nalloc after shrinking");
+        return 33;
+    }
+    if (p3->nalloc != 0) {
+        psError(PS_ERR_UNKNOWN, true,
+                "psPixelsRealloc failed to set nalloc properly");
+        return 34;
+    }
+
+    psFree(p3);
+
+    return 0;
+}
+
+psS32 testPixelsCopy(void)
+{
+    psPixels* in1 = psPixelsAlloc(10);
+
+    for (int i = 0; i < 10; i++) {
+        in1->data[i].x = i;
+        in1->data[i].y = 100+i;
+    }
+    in1->n = 10;
+
+    psPixels* out1 = psPixelsCopy(NULL, in1);
+    if (out1 == in1) {
+        psError(PS_ERR_UNKNOWN, true,
+                "output == input?");
+        return 1;
+    }
+    if (out1 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "output == NULL?");
+        return 2;
+    }
+
+    if (out1->n != in1->n) {
+        psError(PS_ERR_UNKNOWN, true,
+                "out->n != in->n");
+        return 3;
+    }
+    for (long i = 0; i < out1->n; i++) {
+        if (out1->data[i].x != in1->data[i].x || out1->data[i].y != in1->data[i].y) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "failed to copy the values correctly at index %ld", i);
+            return 4;
+        }
+    }
+
+    // now test what happens when copying a 0-length list.
+    psPixels* in2 = psPixelsAlloc(0);
+    psPixels* out2 = psPixelsCopy(out1, in2);
+    if (out2 != out1) {
+        psError(PS_ERR_UNKNOWN, true,
+                "failed to recycle.");
+        return 10;
+    }
+    if (out2->n != in2->n) {
+        psError(PS_ERR_UNKNOWN, true,
+                "failed to set size when copying 0-length pixel list.");
+        return 11;
+    }
+
+    // Attempt to copy from NULL input
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input");
+    out2 = psPixelsCopy(out2,NULL);
+    if (out2 != NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "Copying a NULL should create a NULL.");
+        return 20;
+    }
+
+    psFree(in1);
+    psFree(in2);
+
+    return 0;
+}
+
+int testPixelsToMask(void)
+{
+    // create a pixel list for
+    psPixels* pixels = psPixelsAlloc(10);
+    for (int i = 0; i < 10; i++) {
+        pixels->data[i].x = i;
+        pixels->data[i].y = i;
+    }
+    pixels->n = 10;
+
+    psImage* mask = psPixelsToMask(NULL,pixels, psRegionSet(0,10,0,10), 1);
+
+    if (mask == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "Resulting mask was null.");
+        return 1;
+    }
+    if (mask->type.type != PS_TYPE_MASK) {
+        psError(PS_ERR_UNKNOWN, true,
+                "mask type was not PS_TYPE_MASK.");
+        return 2;
+    }
+
+    for (int row=0;row<10;row++) {
+        psMaskType* rowData = mask->data.PS_TYPE_MASK_DATA[row];
+        for (int col=0;col<10;col++) {
+            if ( (col==row && rowData[col] != 1) ||
+                    (col!=row && rowData[col] != 0) ) {
+                psError(PS_ERR_UNKNOWN,true,
+                        "Mask has unexpected value, %d, at (%d,%d)",
+                        rowData[col], col, row);
+                return 10;
+            }
+        }
+    }
+
+    // test when input psPixels is NULL.
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for NULL pixels");
+    mask = psPixelsToMask(mask, NULL, psRegionSet(0,10,0,10), 1);
+    if (mask != NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "Resulting mask was not null though input psPixels was NULL.");
+        return 20;
+    }
+
+
+    // Test for invalid region
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for invalid range");
+    mask = psPixelsToMask(mask,pixels,psRegionSet(10,0,10,0),1);
+    if(mask != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL for invalid range");
+        return 21;
+    }
+    // Test for invalid region
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for invalid range");
+    mask = psPixelsToMask(mask,pixels,psRegionSet(0,-1,0,10),1);
+    if(mask != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL for invalid range");
+        return 22;
+    }
+
+    psFree(mask);
+    psFree(pixels);
+
+    return 0;
+}
+
+int testPixelsFromMask(void)
+{
+    const int numRows = 10;
+    const int numCols = 20;
+    psImage* mask = psImageAlloc(numCols,numRows,PS_TYPE_MASK);
+    for (int row=0;row<numRows;row++) {
+        for (int col=0;col<numCols;col++) {
+            mask->data.PS_TYPE_MASK_DATA[row][col] = (row*2 == col) ? 1 : 0;
+        }
+    }
+
+    psPixels* pixels = psPixelsFromMask(NULL, mask, 1);
+
+    if (pixels == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "resulting psPixels was NULL?");
+        return 1;
+    }
+
+    if (pixels->n != numRows) {
+        psError(PS_ERR_UNKNOWN, false,
+                "wrong number of pixels in list.  Got %ld, should be %d.",
+                pixels->n, numRows);
+        return 2;
+    }
+
+    for (long i = 0; i < pixels->n; i++) {
+        if (mask->data.PS_TYPE_MASK_DATA[(int)pixels->data[i].y][(int)pixels->data[i].x] != 1) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Item in psPixels list (%f,%f) didn't coorespond to a masked value in image",
+                    pixels->data[i].x, pixels->data[i].y);
+            return 3;
+        }
+    }
+
+    // Attempt to create pixels from NULL mask
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error for NULL mask");
+    pixels = psPixelsFromMask(pixels, NULL, 1);
+
+    if (pixels != NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "given a NULL image, got a non-NULL mask?");
+        return 4;
+    }
+
+    psFree(mask);
+
+    return 0;
+}
+
+int testPixelsConcatenate(void)
+{
+    // create a pixel list for
+    psPixels* pixels = psPixelsAlloc(10);
+    psPixels* pixels1 = psPixelsAlloc(10);
+    for (int i = 0; i < 10; i++) {
+        pixels->data[i].x = i;
+        pixels->data[i].y = i;
+        if (i%2) {
+            pixels1->data[i].x = -i;
+        } else {
+            pixels1->data[i].x = i;
+        }
+        pixels1->data[i].y = i;
+    }
+    pixels->n = 10;
+    pixels1->n = 10;
+    p_psPixelsPrint(stdout, pixels, "pixels");
+    p_psPixelsPrint(stdout, pixels1, "pixels1");
+
+    psPixels* pixels2 = psPixelsConcatenate(NULL, pixels);
+    p_psPixelsPrint(stdout, pixels2, "pixels2");
+    if (pixels2 == NULL) {
+        psError(PS_ERR_UNKNOWN, true,
+                "pixels2 == NULL?");
+        return 1;
+    }
+    if (pixels == pixels2) {
+        psError(PS_ERR_UNKNOWN, true,
+                "pixels == pixels2?  Should have made a copy.");
+        return 2;
+    }
+    if (pixels2->n != pixels->n) {
+        psError(PS_ERR_UNKNOWN, true,
+                "pixels2->n != pixels->n");
+        return 3;
+    }
+    for (long i = 0; i < pixels2->n; i++) {
+        if (pixels2->data[i].x != pixels->data[i].x || pixels2->data[i].y != pixels->data[i].y) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "failed to copy the values correctly at index %ld", i);
+            return 4;
+        }
+    }
+
+    // add pixels that are half unique.
+    psPixels* pixels3 = psPixelsConcatenate(pixels2, pixels1);
+    p_psPixelsPrint(stdout, pixels3, "pixels3");
+    if (pixels3 != pixels2) {
+        psError(PS_ERR_UNKNOWN, true,
+                "return expected to be the out parameter.");
+        return 10;
+    }
+    if (pixels3->n != 15) {
+        psError(PS_ERR_UNKNOWN, true,
+                "pixels3->n != 15, == %ld", pixels3->n);
+        return 11;
+    }
+
+    // Attempt to concatenate with NULL pixels
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL pixels");
+    if(psPixelsConcatenate(pixels2,NULL) != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Did not expect return to be non-NULL for NULL input pixels");
+        return 12;
+    }
+
+    psFree(pixels3);
+    psFree(pixels1);
+    psFree(pixels);
+
+    return 0;
+}
+
+psS32 testPixelsGetSet(void)
+{
+    psPixels *in = psPixelsAlloc(5);
+    in->n = 0;
+    psPixelCoord value;
+    psPixelCoord out;
+    value.x = 3;
+    value.y = 13;
+    if ( psPixelsSet(in, 2, value) ) {
+        psError(PS_ERR_LOCATION_INVALID, true, "psPixelSet set to Invalid location\n");
+        return 1;
+    }
+    if ( !psPixelsSet(in, 0, value) ) {
+        psError(PS_ERR_UNKNOWN, true, "psPixelsSet failed to set pixels at location 0\n");
+        return 2;
+    }
+    if ( !psPixelsSet(in, 1, value) ) {
+        psError(PS_ERR_UNKNOWN, true, "psPixelsSet failed to set pixels at location 0\n");
+        return 3;
+    }
+    out = psPixelsGet(in, 1);
+    if ( out.x != 3 || out.y != 13 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPixelsGet return incorrect values %f,%f",
+                out.x, out.y);
+        return 4;
+    }
+    value.x = 1;
+    value.y = 2;
+    if ( !psPixelsSet(in, 0, value) ) {
+        psError(PS_ERR_UNKNOWN, true, "psPixelsSet failed to set pixels at location 0\n");
+        return 5;
+    }
+    out = psPixelsGet(in, 0);
+    if (out.x != 1 || out.y != 2) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPixelsGet return incorrect values %f,%f",
+                out.x, out.y);
+        return 6;
+    }
+    psFree(in);
+    return 0;
+}
+
+psS32 testPixelsLength( void )
+{
+    psPixels *pixels = psPixelsAlloc(5);
+
+    if (psPixelsLength(pixels) != 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psPixelsLength failed to return the correct length of pixels.\n");
+        return 1;
+    }
+    pixels->n = 5;
+    if (psPixelsLength(pixels) != 5) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psPixelsLength failed to return the correct length of pixels.\n");
+        return 2;
+    }
+    pixels->n++;
+    if (psPixelsLength(pixels) != 6) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psPixelsLength failed to return the correct length of pixels.\n");
+        return 3;
+    }
+    psFree(pixels);
+
+    psPixels *emptyPixels = NULL;
+    psVector *vector = psVectorAlloc(5, PS_TYPE_F32);
+
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psPixelsLength(emptyPixels) != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psPixelsLength failed to return -1 for a NULL input pixels.\n");
+        return 4;
+    }
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    if (psPixelsLength((psPixels*)vector) != -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "psPixelsLength failed to return -1 for an invalid input pixels.\n");
+        return 5;
+    }
+    psFree(vector);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psLib/test/xml/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/xml/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/xml/.cvsignore	(revision 22322)
@@ -0,0 +1,15 @@
+temp
+.deps
+.libs
+Makefile
+Makefile.in
+psTest.xml
+psTest2.xml
+psTest3.xml
+psTest4.xml
+psTest5.xml
+tst_psXML
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psLib/test/xml/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/xml/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/xml/Makefile.am	(revision 22322)
@@ -0,0 +1,21 @@
+AM_CPPFLAGS = $(SRCINC) -I$(top_srcdir)/test/tap/src $(PSLIB_CFLAGS)
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpslib.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(PSLIB_LIBS)
+
+TEST_PROGS = 
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+EXTRA_DIST = psTime.xml psTime2.xml psTime3.xml psTime4.xml
+
+CLEANFILES = $(check_DATA) temp/* psTest5.xml psTest2.xml psTest.xml psTest3.xml psTest4.xml \
+	core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
Index: /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime.xml
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime.xml	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime.xml	(revision 22322)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<!-- This configuration file specifies values required for time calculations by psLib -->
+<metadata>
+
+    <!-- Directory for time tables -->
+    <item name = "psLib.time.S32.value" psType = "S32" value = "20" />
+    <item name = "psLib.time.F32.value" psType = "F32" value = "50324.42145" />
+    <item name = "psLib.time.F64.value" psType = "F64" value = "80112.48952" />
+    <item name = "psLib.time.BOOL.value" psType = "BOOL" value = "TRUE" />
+    <item name = "psLib.time.BOOL.value2" psType = "BOOL" value = "false" />
+    <item name = "psLib.time.BOOL.value3" psType = "BOOL" value = "t" />
+    <item name = "psLib.time.BOOL.value4" psType = "BOOL" value = "F" />
+    <time name = "psLib.TIME.Magazine" psType = "PS_TIME_UTC" value = "1000, 25, F" />
+    <vector name = "psLib.time.Vector.S32" psType = "S32" value = "-4, -2, 506"/>
+    <vector name = "psLib.time.Vector.F32" psType = "F32" value = "-0.4944, -0.00023, 53262.0"/>
+    <vector name = "psLib.time.Vector.F64" psType = "F64" value = "-0.4944, -0.00023, 53262.0"/>
+    <metadata name ="cell" psType="METADATA">
+      <item name = "EXTNAME" psType="STR" value = "CCD00" />
+      <item name = "BIASSEC" psType="STR" value = "BSEC-00" />
+    </metadata>
+    <item name = "psLib.time.tables.dir" psType = "STR" value = "../../data" />
+</metadata>
Index: /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime2.xml
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime2.xml	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime2.xml	(revision 22322)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<!-- This configuration file specifies values required for time calculations by psLib -->
+<metadata>
+
+    <!-- Directory for time tables -->
+    <item name = "psLib.time.S32.value" psType = "U32" value = "20" />
+    <item name = "psLib.time.F32.value" psType = "F32" value = "50324.42145" />
+    <item name = "psLib.time.F64.value" psType = "F64" value = "80112.48952" />
+    <item name = "psLib.time.BOOL.value" psType = "BOOL" value = "TRUE" />
+    <item name = "psLib.time.BOOL.value2" psType = "BOOL" value = "false" />
+    <item name = "psLib.time.BOOL.value3" psType = "BOOL" value = "t" />
+    <item name = "psLib.time.BOOL.value4" psType = "BOOL" value = "F" />
+    <vector name = "psLib.time.Vector.S32" psType = "S32" value = "-4, -2, 506"/>
+    <vector name = "psLib.time.Vector.F32" psType = "F32" value = "-0.4944, -0.00023, 53262.0"/>
+    <vector name = "psLib.time.Vector.F64" psType = "F64" value = "-0.4944, -0.00023, 53262.0"/>
+    <metadata name ="cell" psType="METADATA">
+      <item name = "EXTNAME" psType="STR" value = "CCD00" />
+      <item name = "BIASSEC" psType="STR" value = "BSEC-00" />
+    </metadata>
+    <item name = "psLib.time.tables.dir" psType = "STR" value = "../../data" />
+</metadata>
Index: /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime3.xml
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime3.xml	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime3.xml	(revision 22322)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<!-- This configuration file specifies values required for time calculations by psLib -->
+<metadata>
+
+    <!-- Directory for time tables -->
+    <item name = "psLib.time.S32.value" psType = "S32" value = "20" />
+    <item name = "psLib.time.F32.value" psType = "F32" value = "50324.42145" />
+    <item name = "psLib.time.F64.value" psType = "F64" value = "80112.48952" />
+    <item name = "psLib.time.BOOL.value" psType = "BOOL" value = "right" />
+    <item name = "psLib.time.BOOL.value2" psType = "BOOL" value = "false" />
+    <item name = "psLib.time.BOOL.value3" psType = "BOOL" value = "t" />
+    <item name = "psLib.time.BOOL.value4" psType = "BOOL" value = "F" />
+    <vector name = "psLib.time.Vector.S32" psType = "S32" value = "-4, -2, 506"/>
+    <vector name = "psLib.time.Vector.F32" psType = "F32" value = "-0.4944, -0.00023, 53262.0"/>
+    <vector name = "psLib.time.Vector.F64" psType = "F64" value = "-0.4944, -0.00023, 53262.0"/>
+    <metadata name ="cell" psType="METADATA">
+      <item name = "EXTNAME" psType="STR" value = "CCD00" />
+      <item name = "BIASSEC" psType="STR" value = "BSEC-00" />
+    </metadata>
+    <item name = "psLib.time.tables.dir" psType = "STR" value = "../../data" />
+</metadata>
Index: /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime4.xml
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime4.xml	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/xml/psTime4.xml	(revision 22322)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<!-- This configuration file specifies values required for time calculations by psLib -->
+<meta>
+
+    <!-- Directory for time tables -->
+    <item name = "psLib.time.S32.value" psType = "S32" value = "20" />
+    <item name = "psLib.time.F32.value" psType = "F32" value = "50324.42145" />
+    <item name = "psLib.time.F64.value" psType = "F64" value = "80112.48952" />
+    <item name = "psLib.time.BOOL.value" psType = "BOOL" value = "TRUE" />
+    <item name = "psLib.time.BOOL.value2" psType = "BOOL" value = "false" />
+    <item name = "psLib.time.BOOL.value3" psType = "BOOL" value = "t" />
+    <item name = "psLib.time.BOOL.value4" psType = "BOOL" value = "F" />
+    <vector name = "psLib.time.Vector.S32" psType = "S32" value = "-4, -2, 506"/>
+    <vector name = "psLib.time.Vector.F32" psType = "F32" value = "-0.4944, -0.00023, 53262.0"/>
+    <vector name = "psLib.time.Vector.F64" psType = "F64" value = "-0.4944, -0.00023, 53262.0"/>
+    <metadata name ="cell" psType="METADATA">
+      <item name = "EXTNAME" psType="STR" value = "CCD00" />
+      <item name = "BIASSEC" psType="STR" value = "BSEC-00" />
+    </metadata>
+    <item name = "psLib.time.tables.dir" psType = "STR" value = "../../data" />
+</meta>
Index: /tags/sj_tags/sj_root_20080929/psLib/test/xml/tap_psXML.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/xml/tap_psXML.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/xml/tap_psXML.c	(revision 22322)
@@ -0,0 +1,315 @@
+/** @file  tst_psXML.c
+*
+*  @brief Test driver for psXML functions
+*
+*  This test driver contains the following tests for psXML:
+*     Test 1 - Parse an XML file
+*     Test 2 - Parse an XML memory block
+*     Test 3 - Parse an XML file descriptor
+*     Test 4 - Convert an XML doc to Metadata
+*     Test 5 - Convert Metadata to an XML doc
+*     Test 6 - Write an XML doc to file
+*     Test 7 - Write an XML doc to memory block
+*     Test 8 - Write an XML doc to file descriptor
+*
+*  @author  Dave Robbins, MHPCC
+*
+* XXXX: This doesn't even compile
+* XXXX: There are no data tests, he simple prints data to STDOUT
+*
+*  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2007-05-01 00:08:52 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+//static void writeMetadata(psMetadata* metadata, char* indentStr);
+
+// XXX: What should SRCDIR be?
+#define SRCDIR
+const char testFile1[] = SRCDIR "/psTime.xml";
+const char testFile2[] = SRCDIR "/psTime2.xml";
+const char testFile3[] = SRCDIR "/psTime3.xml";
+const char testFile4[] = SRCDIR "/psTime4.xml";
+//static void printMetadata(psMetadata *in);
+static void printMetadataItem(psMetadataItem *metadataItem, char *spaces);
+static void printMetadata(psMetadata *metadata);
+static void printMetadataList(psList *metadataItemList, char* spaces);
+static void printMetadataTable(psHash *mdTable);
+
+static void printMetadata(psMetadata *metadata)
+{
+    printf("Contents of metadata list:\n");
+    printMetadataList(metadata->list, " ");
+    printf("\nContents of metadata table:\n");
+    printMetadataTable(metadata->hash);
+}
+
+static void printMetadataList(psList *metadataItemList, char* spaces)
+{
+    psMetadataItem *entryChild = NULL;
+
+    psListIterator* iter = psListIteratorAlloc(metadataItemList, PS_LIST_HEAD, true);
+
+    while ( (entryChild=psListGetAndIncrement(iter)) != NULL) {
+        printMetadataItem(entryChild, spaces);
+    }
+
+    psFree(iter);
+}
+
+static void printMetadataTable(psHash *mdTable)
+{
+    psS32 i;
+    psHashBucket* ptr = NULL;
+    for(i=0; i<mdTable->n; i++) {
+        ptr = mdTable->buckets[i];
+        while (ptr != NULL) {
+            printMetadataItem(ptr->data, " ");
+            ptr = ptr->next;
+        }
+    }
+}
+
+static void printMetadataItem(psMetadataItem *metadataItem, char *spaces)
+{
+    int i = 0;
+    printf("%sKey Name: %25s  ", spaces, metadataItem->name);
+    //    printf("Key mdType: %d  ", (int)metadataItem->type);
+    //    printf("Key mdType: 0x%08x  ", metadataItem->type);
+    //    if ( metadataItem->type == PS_DATA_S32 )
+    //        printf("Key mdType: S32  " );
+
+    switch (metadataItem->type) {
+    case PS_DATA_BOOL:
+        printf("Key Type:  BOOL     Key Value: %15d  ", metadataItem->data.B);
+        break;
+    case PS_DATA_S32:
+        printf("Key Type:  S32      Key Value: %15d  ", metadataItem->data.S32);
+        break;
+    case PS_DATA_F32:
+        printf("Key Type:  F32      Key Value: %15.3f  ", metadataItem->data.F32);
+        break;
+    case PS_DATA_F64:
+        printf("Key Type:  F64      Key Value: %15.3f  ", metadataItem->data.F64);
+        break;
+    case PS_DATA_METADATA:
+        printf("Key Type:  METADATA    ");
+        break;
+    default:
+        printf("Key type:  psPtr    ");
+    }
+    if ( !strncmp(metadataItem->name, "psLib.time.Vector.S32", 256) ) {
+        printf("Key Values:   ");
+        while ( (int)((psVector*)(metadataItem->data.V))->data.S32[i] != 0 ) {
+            printf("%d  ", (int)((psVector*)(metadataItem->data.V))->data.S32[i] );
+            i++;
+        }
+        printf("\n");
+    } else if ( !strncmp(metadataItem->name, "psLib.time.tables.dir", 256) ) {
+        printf("Key Value:   ");
+        printf("%s", (char*)metadataItem->data.V );
+        printf("\n");
+    } else if ( !strncmp(metadataItem->name, "psLib.TIME.Magazine", 256) ) {
+        printf("Key Value:   ");
+        printf("%ld, ", (long)((psTime*)(metadataItem->data.V))->sec );
+        printf("%u, ", ((psTime*)(metadataItem->data.V))->nsec );
+        if( ((psTime*)(metadataItem->data.V))->leapsecond )
+            printf("TRUE  ");
+        else
+            printf("FALSE  ");
+        if( ((psTime*)(metadataItem->data.V))->type == PS_TIME_UTC )
+            printf("PS_TIME_UTC ");
+        else if( ((psTime*)(metadataItem->data.V))->type == PS_TIME_TAI )
+            printf("PS_TIME_TAI ");
+        else if( ((psTime*)(metadataItem->data.V))->type == PS_TIME_UT1 )
+            printf("PS_TIME_UT1 ");
+        else if( ((psTime*)(metadataItem->data.V))->type == PS_TIME_TT )
+            printf("PS_TIME_TT ");
+        printf("\n");
+    } else
+        printf("Key Comment: %s\n", metadataItem->comment);
+
+    //    if(metadataItem->data.V && metadataItem->type==PS_META_MULTI) {
+    //    if(metadataItem->data.V) {
+    //        printMetadataList(metadataItem->data.V, "    ");
+    //    }
+}
+
+typedef xmlDocPtr psXMLDoc;
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(24);
+
+    //Parses an XML file into memory.  Stores as a psXMLDoc//
+    // testXMLInput00
+    {
+        psMemId id = psMemGetId();
+        //psXMLParseFile should return a psXMLDoc* of testFile1//
+        psXMLDoc *newXML = NULL;
+        newXML = psXMLParseFile(testFile1);
+        ok(newXML != NULL, "psXMLParseFile() returned non-NULL");
+        psFree(newXML);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    //Parses an XML file from memory.  Stores as a psXMLDoc//
+    // testXMLInput01
+    {
+        psMemId id = psMemGetId();
+        psXMLDoc *newXML = NULL;
+        newXML = psXMLParseFile(testFile1);
+        char buffer[2048];
+        ok(psXMLDocToMem(newXML, buffer), "psXMLDocToMem() returned non-NULL");
+        psXMLDoc *newDoc = NULL;
+        newDoc = psXMLParseMem(buffer, strlen(buffer) );
+        ok(newDoc != NULL, "psXMLParseMem() returned non-NULL");
+        psFree(newDoc);
+        psFree(newXML);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Parses an XML file from a file descriptor.  Stores as a psXMLDoc//
+    // testXMLInput02
+    {
+        psMemId id = psMemGetId();
+        psXMLDoc *newXML = NULL;
+        newXML = psXMLParseFile(testFile1);
+        ok(newXML != NULL, "psXMLParseFile () returned non-NULL");
+        int fd = creat("psTest5.xml", 0644);
+        ok(psXMLDocToFD(newXML, fd), "psXMLDocToFD() returned TRUE");
+
+        psXMLDoc *newDoc = NULL;
+        fd = open("psTest5.xml", O_RDWR, 0);
+        newDoc = psXMLParseFD(fd);
+        ok(newDoc != NULL, "psXMLParseFD () returned non-NULL");
+        close(fd);
+        psFree(newXML);
+        psFree(newDoc);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Converts an existing psXMLDoc into psMetadata//
+    // testXMLConvert00
+    {
+        psMemId id = psMemGetId();
+        psXMLDoc *newXML = NULL;
+        newXML = psXMLParseFile(testFile1);
+        psMetadata *metaData = NULL;
+        metaData = psXMLDocToMetadata(newXML);
+        if (metaData == NULL)
+        {
+            ok(false, "psXMLDocToMetadata() returned non-NULL");
+        } else
+        {
+            ok(true, "psXMLDocToMetadata() returned non-NULL");
+            printMetadata(metaData);
+        }
+        psFree(newXML);
+        psFree(metaData);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Converts existing psMetadata into a psXMLDoc//
+    // testXMLConvert01
+    {
+        psMemId id = psMemGetId();
+        psXMLDoc *newXML = NULL;
+        newXML = psXMLParseFile(testFile1);
+        psMetadata *metaData = NULL;
+        metaData = psXMLDocToMetadata(newXML);
+        ok(metaData != NULL, "psXMLDocToMetadata() returned non-NULL");
+        psXMLDoc *XML2 = NULL;
+        XML2 = psMetadataToXMLDoc(metaData);
+        ok(psXMLDocToFile(XML2, "psTest2.xml"), "psMetadataToXMLDoc() returned TRUE");
+        psFree(newXML);
+        psFree(XML2);
+        psFree(metaData);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Writes a psXMLDoc to File//
+    // testXMLOutput00
+    {
+        psMemId id = psMemGetId();
+        psXMLDoc *newXML = NULL;
+        newXML = psXMLParseFile(testFile1);
+        ok(psXMLDocToFile(newXML, "psTest.xml"), "psXMLParseFile() returned TRUE");
+        psFree(newXML);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Writes a psXMLDoc to Memory//
+    // testXMLOutput01
+    {
+        psMemId id = psMemGetId();
+        psXMLDoc *newXML = NULL;
+        newXML = psXMLParseFile(testFile1);
+        char buffer[2048];
+        FILE *file;
+        file = fopen("psTest3.xml", "w");
+        ok(psXMLDocToMem(newXML, buffer), "fopen() successful");
+        fprintf(file, "%s", buffer);
+        fclose(file);
+        psFree(newXML);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    //Writes a psXMLDoc to a file descriptor//
+    // testXMLOutput02
+    {
+        psMemId id = psMemGetId();
+        psXMLDoc *newXML = NULL;
+        newXML = psXMLParseFile(testFile1);
+        int fd = creat("psTest4.xml", 0666);
+        ok(!psXMLDocToFD(newXML, fd), "psXMLDocToFD() returned TRUE");
+        close(fd);
+        psFree(newXML);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // testXMLWrongInput
+    {
+        psMemId id = psMemGetId();
+        psXMLDoc *newXML = NULL;
+        newXML = psXMLParseFile(testFile2);
+        ok(newXML != NULL, "psXMLParseFile() returned non-NULL");
+        psMetadata *metaData = NULL;
+        metaData = psXMLDocToMetadata(newXML);
+        ok(metaData != NULL, "psXMLDocToMetadata() returned non-NULL");
+        psFree(newXML);
+        psFree(metaData);
+        psXMLDoc *newXML2 = NULL;
+        psMetadata *metaData2 = NULL;
+        newXML2 = psXMLParseFile(testFile3);
+        ok(newXML2 != NULL, "psXMLParseFile() returned non-NULL");
+        metaData2 = psXMLDocToMetadata(newXML2);
+        ok(metaData2 != NULL, "psXMLDocToMetadata() returned non-NULL");
+        psFree(newXML2);
+        psFree(metaData2);
+        psXMLDoc *newXML3 = NULL;
+        psMetadata *metaData3 = NULL;
+        newXML3 = psXMLParseFile(testFile4);
+        ok(newXML3 != NULL, "psXMLParseFile() returned non-NULL");
+        metaData3 = psXMLDocToMetadata(newXML3);
+        ok(metaData3 != NULL, "psXMLDocToMetadata() returned non-NULL");
+        psFree(newXML3);
+        psFree(metaData3);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/test/xml/tst_psXML.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/test/xml/tst_psXML.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/test/xml/tst_psXML.c	(revision 22322)
@@ -0,0 +1,357 @@
+/** @file  tst_psXML.c
+*
+*  @brief Test driver for psXML functions
+*
+*  This test driver contains the following tests for psXML:
+*     Test 1 - Parse an XML file
+*     Test 2 - Parse an XML memory block
+*     Test 3 - Parse an XML file descriptor
+*     Test 4 - Convert an XML doc to Metadata
+*     Test 5 - Convert Metadata to an XML doc
+*     Test 6 - Write an XML doc to file
+*     Test 7 - Write an XML doc to memory block
+*     Test 8 - Write an XML doc to file descriptor
+*
+*  @author  Dave Robbins, MHPCC
+*
+*  @version $Revision: 1.7 $  $Name: not supported by cvs2svn $
+*  @date  $Date: 2005-10-13 00:50:41 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*
+*/
+
+#include <string.h>
+#include "config.h"
+#include "pslib_strict.h"
+#include "psTest.h"
+#include <fcntl.h>
+#include <unistd.h>
+
+
+static psS32 testXMLInput00(void);
+static psS32 testXMLInput01(void);
+static psS32 testXMLInput02(void);
+static psS32 testXMLConvert00(void);
+static psS32 testXMLConvert01(void);
+static psS32 testXMLOutput00(void);
+static psS32 testXMLOutput01(void);
+static psS32 testXMLOutput02(void);
+static psS32 testXMLWrongInput(void);
+
+testDescription tests[] = {
+                              {testXMLInput00, 0, "Verify XML file parse", 0, false},
+                              {testXMLInput01, 1, "Verify XML memory parse", 0, false},
+                              {testXMLInput02, 2, "Verify XML file descriptor parse", 0, false},
+                              {testXMLConvert00, 3, "Verify XML to Metadata convert", 0, false},
+                              {testXMLConvert01, 4, "Verify Metadata to XML convert", 0, false},
+                              {testXMLOutput00, 5, "Verify XML file write", 0, false},
+                              {testXMLOutput01, 6, "Verify XML memory write", 0, false},
+                              {testXMLOutput02, 7, "Verify XML file descriptor write", 0, false},
+                              {testXMLWrongInput, 8, "Verify Incorrect XML Data", 0, false},
+                              {NULL}
+                          };
+
+//static void writeMetadata(psMetadata* metadata, char* indentStr);
+
+const char testFile1[] = SRCDIR "/psTime.xml";
+const char testFile2[] = SRCDIR "/psTime2.xml";
+const char testFile3[] = SRCDIR "/psTime3.xml";
+const char testFile4[] = SRCDIR "/psTime4.xml";
+//static void printMetadata(psMetadata *in);
+static void printMetadataItem(psMetadataItem *metadataItem, char *spaces);
+static void printMetadata(psMetadata *metadata);
+static void printMetadataList(psList *metadataItemList, char* spaces);
+static void printMetadataTable(psHash *mdTable);
+
+static void printMetadata(psMetadata *metadata)
+{
+    printf("Contents of metadata list:\n");
+    printMetadataList(metadata->list, " ");
+    printf("\nContents of metadata table:\n");
+    printMetadataTable(metadata->hash);
+}
+
+static void printMetadataList(psList *metadataItemList, char* spaces)
+{
+    psMetadataItem *entryChild = NULL;
+
+    psListIterator* iter = psListIteratorAlloc(metadataItemList, PS_LIST_HEAD, true);
+
+    while ( (entryChild=psListGetAndIncrement(iter)) != NULL) {
+        printMetadataItem(entryChild, spaces);
+    }
+
+    psFree(iter);
+}
+
+static void printMetadataTable(psHash *mdTable)
+{
+    psS32 i;
+    psHashBucket* ptr = NULL;
+    for(i=0; i<mdTable->n; i++) {
+        ptr = mdTable->buckets[i];
+        while (ptr != NULL) {
+            printMetadataItem(ptr->data, " ");
+            ptr = ptr->next;
+        }
+    }
+}
+
+static void printMetadataItem(psMetadataItem *metadataItem, char *spaces)
+{
+    int i = 0;
+    printf("%sKey Name: %25s  ", spaces, metadataItem->name);
+    //    printf("Key mdType: %d  ", (int)metadataItem->type);
+    //    printf("Key mdType: 0x%08x  ", metadataItem->type);
+    //    if ( metadataItem->type == PS_DATA_S32 )
+    //        printf("Key mdType: S32  " );
+
+    switch (metadataItem->type) {
+    case PS_DATA_BOOL:
+        printf("Key Type:  BOOL     Key Value: %15d  ", metadataItem->data.B);
+        break;
+    case PS_DATA_S32:
+        printf("Key Type:  S32      Key Value: %15d  ", metadataItem->data.S32);
+        break;
+    case PS_DATA_F32:
+        printf("Key Type:  F32      Key Value: %15.3f  ", metadataItem->data.F32);
+        break;
+    case PS_DATA_F64:
+        printf("Key Type:  F64      Key Value: %15.3f  ", metadataItem->data.F64);
+        break;
+    case PS_DATA_METADATA:
+        printf("Key Type:  METADATA    ");
+        break;
+    default:
+        printf("Key type:  psPtr    ");
+    }
+    if ( !strncmp(metadataItem->name, "psLib.time.Vector.S32", 256) ) {
+        printf("Key Values:   ");
+        while ( (int)((psVector*)(metadataItem->data.V))->data.S32[i] != 0 ) {
+            printf("%d  ", (int)((psVector*)(metadataItem->data.V))->data.S32[i] );
+            i++;
+        }
+        printf("\n");
+    } else if ( !strncmp(metadataItem->name, "psLib.time.tables.dir", 256) ) {
+        printf("Key Value:   ");
+        printf("%s", (char*)metadataItem->data.V );
+        printf("\n");
+    } else if ( !strncmp(metadataItem->name, "psLib.TIME.Magazine", 256) ) {
+        printf("Key Value:   ");
+        printf("%ld, ", (long)((psTime*)(metadataItem->data.V))->sec );
+        printf("%u, ", ((psTime*)(metadataItem->data.V))->nsec );
+        if( ((psTime*)(metadataItem->data.V))->leapsecond )
+            printf("TRUE  ");
+        else
+            printf("FALSE  ");
+        if( ((psTime*)(metadataItem->data.V))->type == PS_TIME_UTC )
+            printf("PS_TIME_UTC ");
+        else if( ((psTime*)(metadataItem->data.V))->type == PS_TIME_TAI )
+            printf("PS_TIME_TAI ");
+        else if( ((psTime*)(metadataItem->data.V))->type == PS_TIME_UT1 )
+            printf("PS_TIME_UT1 ");
+        else if( ((psTime*)(metadataItem->data.V))->type == PS_TIME_TT )
+            printf("PS_TIME_TT ");
+        printf("\n");
+    } else
+        printf("Key Comment: %s\n", metadataItem->comment);
+
+    //    if(metadataItem->data.V && metadataItem->type==PS_META_MULTI) {
+    //    if(metadataItem->data.V) {
+    //        printMetadataList(metadataItem->data.V, "    ");
+    //    }
+}
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+
+    if( !runTestSuite(stderr,"psXML",tests,argc,argv)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+//Parses an XML file into memory.  Stores as a psXMLDoc//
+psS32 testXMLInput00(void)
+{
+    //psXMLParseFile should return a psXMLDoc* of testFile1//
+    psXMLDoc *newXML = NULL;
+    newXML = psXMLParseFile(testFile1);
+    if (newXML == NULL ) {
+        fprintf(stderr, "Failed test point #1 xmlDocPtr is NULL \n");
+    }
+    psFree(newXML);
+    return 0;
+}
+
+//Parses an XML file from memory.  Stores as a psXMLDoc//
+psS32 testXMLInput01(void)
+{
+    psXMLDoc *newXML = NULL;
+    newXML = psXMLParseFile(testFile1);
+    char buffer[2048];
+    if (!psXMLDocToMem(newXML, buffer)) {
+        fprintf(stderr, "Failed test point #2 psXMLDocToMem not Working \n");
+    }
+    psXMLDoc *newDoc = NULL;
+    newDoc = psXMLParseMem(buffer, strlen(buffer) );
+    if (newDoc == NULL) {
+        fprintf(stderr, "Failed test point #2 xmlDocPtr is NULL \n");
+    }
+    psFree(newDoc);
+    psFree(newXML);
+    return 0;
+}
+
+//Parses an XML file from a file descriptor.  Stores as a psXMLDoc//
+psS32 testXMLInput02(void)
+{
+    psXMLDoc *newXML = NULL;
+    newXML = psXMLParseFile(testFile1);
+    if (newXML == NULL ) {
+        fprintf(stderr, "Failed test point #3 xmlDocPtr is NULL \n");
+    }
+    int fd = creat("psTest5.xml", 0666);
+
+    if ( !psXMLDocToFD(newXML, fd) ) {
+        fprintf(stderr, "Failed test point #3 psXMLDocToFD not Working \n");
+    }
+
+    psXMLDoc *newDoc = NULL;
+    fd = open("psTest5.xml", O_RDWR, 0);
+    newDoc = psXMLParseFD(fd);
+    if ( newDoc == NULL ) {
+        fprintf(stderr, "Failed test point #3 xmlDocPtr is NULL \n");
+    }
+
+    close(fd);
+    psFree(newXML);
+    psFree(newDoc);
+    return 0;
+}
+
+//Converts an existing psXMLDoc into psMetadata//
+psS32 testXMLConvert00(void)
+{
+    psXMLDoc *newXML = NULL;
+    newXML = psXMLParseFile(testFile1);
+    psMetadata *metaData = NULL;
+    metaData = psXMLDocToMetadata(newXML);
+    if (metaData == NULL) {
+        fprintf(stderr, "Failed test point #4 - Convert to Metadata \n");
+    } else {
+        printMetadata(metaData);
+    }
+    psFree(newXML);
+    psFree(metaData);
+    return 0;
+}
+
+//Converts existing psMetadata into a psXMLDoc//
+psS32 testXMLConvert01(void)
+{
+    psXMLDoc *newXML = NULL;
+    newXML = psXMLParseFile(testFile1);
+    psMetadata *metaData = NULL;
+    metaData = psXMLDocToMetadata(newXML);
+    if (metaData == NULL) {
+        fprintf(stderr, "Failed test point #5 - Convert from Metadata \n");
+    }
+    psXMLDoc *XML2 = NULL;
+    XML2 = psMetadataToXMLDoc(metaData);
+    if( !psXMLDocToFile(XML2, "psTest2.xml") ) {
+        fprintf(stderr, "Failed test point #5 - Write to File \n");
+    }
+    psFree(newXML);
+    psFree(XML2);
+    psFree(metaData);
+    return 0;
+}
+
+//Writes a psXMLDoc to File//
+psS32 testXMLOutput00(void)
+{
+    psXMLDoc *newXML = NULL;
+    newXML = psXMLParseFile(testFile1);
+    if( !psXMLDocToFile(newXML, "psTest.xml") ) {
+        fprintf(stderr, "Failed test point #6 - Write to File \n");
+    }
+    psFree(newXML);
+    return 0;
+}
+
+//Writes a psXMLDoc to Memory//
+psS32 testXMLOutput01(void)
+{
+    psXMLDoc *newXML = NULL;
+    newXML = psXMLParseFile(testFile1);
+    char buffer[2048];
+    FILE *file;
+    file = fopen("psTest3.xml", "w");
+    if( !psXMLDocToMem(newXML, buffer) ) {
+        fprintf(stderr, "Failed test point #7 - Write to Memory \n");
+    }
+    fprintf(file, "%s", buffer);
+    fclose(file);
+    psFree(newXML);
+    return 0;
+}
+
+//Writes a psXMLDoc to a file descriptor//
+psS32 testXMLOutput02(void)
+{
+    psXMLDoc *newXML = NULL;
+    newXML = psXMLParseFile(testFile1);
+    int fd = creat("psTest4.xml", 0666);
+
+    if( !psXMLDocToFD(newXML, fd) ) {
+        fprintf(stderr, "Failed test point #8 - Write to file descriptor \n");
+    }
+    close(fd);
+    psFree(newXML);
+    return 0;
+}
+
+psS32 testXMLWrongInput(void)
+{
+    psXMLDoc *newXML = NULL;
+    newXML = psXMLParseFile(testFile2);
+    if (newXML == NULL ) {
+        fprintf(stderr, "Failed to parse xml file.  Incorrect data\n");
+    }
+    psMetadata *metaData = NULL;
+    metaData = psXMLDocToMetadata(newXML);
+    if (metaData == NULL) {
+        fprintf(stderr, "Incorrect xml data for Metadata Conversion \n");
+    }
+    psFree(newXML);
+    psFree(metaData);
+    psXMLDoc *newXML2 = NULL;
+    psMetadata *metaData2 = NULL;
+    newXML2 = psXMLParseFile(testFile3);
+    if (newXML2 == NULL ) {
+        fprintf(stderr, "Failed to parse xml file.  Incorrect data\n");
+    }
+    metaData2 = psXMLDocToMetadata(newXML2);
+    if (metaData2 == NULL) {
+        fprintf(stderr, "Incorrect xml data for Metadata Conversion \n");
+    }
+    psFree(newXML2);
+    psFree(metaData2);
+    psXMLDoc *newXML3 = NULL;
+    psMetadata *metaData3 = NULL;
+    newXML3 = psXMLParseFile(testFile4);
+    if (newXML3 == NULL ) {
+        fprintf(stderr, "Failed to parse xml file.  Incorrect data\n");
+    }
+    metaData3 = psXMLDocToMetadata(newXML3);
+    if (metaData3 == NULL) {
+        fprintf(stderr, "Incorrect xml data for Metadata Conversion \n");
+    }
+    psFree(newXML3);
+    psFree(metaData3);
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/utils/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/utils/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/utils/.cvsignore	(revision 22322)
@@ -0,0 +1,5 @@
+Makefile
+Makefile.in
+.deps
+.libs
+psTableParse
Index: /tags/sj_tags/sj_root_20080929/psLib/utils/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/utils/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/utils/Makefile.am	(revision 22322)
@@ -0,0 +1,6 @@
+bin_SCRIPTS = psParsePrecessionTable psParseErrorCodes
+bin_PROGRAMS = psTableParse
+
+psTableParse_SOURCES = psTableParse.c
+
+EXTRA_DIST = psParsePrecessionTable psParseErrorCodes
Index: /tags/sj_tags/sj_root_20080929/psLib/utils/psParseErrorCodes
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/utils/psParseErrorCodes	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/utils/psParseErrorCodes	(revision 22322)
@@ -0,0 +1,84 @@
+#!/usr/bin/env perl
+
+# Provides functions for handling long command line options
+use Getopt::Long;
+
+my @ErrorCodes        = ();
+my @ErrorDescriptions = ();
+
+my $data = "psErrorCodes.dat";
+my $outdir = ".";
+
+# Assign variables based on the presence of command line options to the script
+GetOptions(
+    "data=s"  => \$data,
+    "outdir=s" => \$outdir,
+    "verbose" => \$verbose,
+    "help"    => \$help
+);
+
+if ($help) {
+    print "Usage: parseErrorCodes ", "[--data=dataFile] ", "[--help] ",
+        "[--verbose] filename\n\n";
+    exit(0);
+}
+
+print "Using data file '$data'\n" if $verbose;
+unless ( open( DATAFILE, "<", $data ) ) {
+    die "Can not open data file $data.";
+}
+
+print "Datafile:\n" if $verbose;
+while (<DATAFILE>) {
+    chop;
+    if (/^\s*\#/) {
+        print "C $_\n" if $verbose;
+    }
+    else {
+        if (/^\s*(\w+)\s+([\%\w].*)/) {
+            my $ErrorCode        = $1;
+            my $ErrorDescription = $2;
+            print "  $ErrorCode: '$ErrorDescription'\n" if $verbose;
+            push( @ErrorCodes,        $ErrorCode );
+            push( @ErrorDescriptions, $ErrorDescription );
+        } else {
+            print "I $_\n" if $verbose;
+        }
+    }
+}
+close(DATAFILE);
+
+my $found = $#ErrorCodes + 1;
+print "\nFound $found error codes.\n" if $verbose;
+
+foreach (@ARGV) {
+    my $filename = $_.".in";
+    m/\/?([\w\.]+)$/;
+    my $output = "$outdir/$1";
+
+    die "Failed to open input file '$filename'"
+      if !open( INFILE, "<", $filename );
+    die "Failed to open output file '$output'"
+      if !open( OUTFILE, ">", $output );
+
+    print "\nOutput File: $output\n" if $verbose;
+    while (<INFILE>) {
+        if (/\${ErrorCode}/ || /\${ErrorDescription}/ || /\${n}/) {
+            $line = $_;
+            for ( $n = 0 ; $n < $found ; $n++ ) {
+                $_ = $line;
+                s/\${ErrorCode}/$ErrorCodes[$n]/g;
+                s/\${ErrorDescription}/$ErrorDescriptions[$n]/g;
+                s/\${n}/$n/g;
+                print OUTFILE;
+                print if $verbose;
+            }
+        } else {
+            print OUTFILE;
+            print if $verbose;
+        }
+    }
+
+    close(INFILE);
+    close(OUTFILE);
+}
Index: /tags/sj_tags/sj_root_20080929/psLib/utils/psParsePrecessionTable
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/utils/psParsePrecessionTable	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/utils/psParsePrecessionTable	(revision 22322)
@@ -0,0 +1,149 @@
+#!/usr/bin/env perl
+
+# Provides functions for handling long command line options
+use Getopt::Long;
+
+# Assign variables based on the presence of command line options to the script
+GetOptions(
+    "verbose" => \$verbose,
+    "help"    => \$help
+);
+
+if ($help) {
+    print "Usage: psParsePrecessionTable ",
+        "[--help] ",
+        "[--verbose] ",
+        "tableFile.txt";
+    exit(0);
+}
+
+# Loop through the arguements passed to the script
+foreach (@ARGV) {
+    chomp;
+    $tableFile = $_;
+    s/\.txt$//;
+    $outputFile = $_ . ".dat";
+    $coeffFile = $_ . ".coeff";
+    print "Table output file is $outputFile\n",
+          "Coefficient output file is $coeffFile\n" if $verbose;
+
+    unless ( open( TABLEFILE, "<", $tableFile ) ) {
+        die "Can not open table file $tableFile.";
+    }
+
+    unless ( open( OUTPUTFILE, ">", $outputFile ) ) {
+        die "Can not create output file $outputFile.";
+    }
+
+    unless ( open( COEFFFILE, ">", $coeffFile ) ) {
+        die "Can not create coeff file $coeffFile.";
+    }
+
+    # Read Header and tranfer it to the output files
+    $found = 0;
+    while ($found < 2 && ($line=<TABLEFILE>)) {
+        print "# $line" if $verbose;
+        print COEFFFILE "# $line";
+        print OUTPUTFILE "# $line";
+        if ($line =~ /^----/) {
+            $found++;
+        }
+    }
+
+
+    die "Failed to parse the polynomials in $tableFile."
+        if &parsePolynomialCoefficients;
+
+    &parseTable;
+
+    close(TABLEFILE);
+    close(OUTPUTFILE);
+    close(COEFFFILE);
+}
+
+
+exit(0);
+
+sub parsePolynomialCoefficients {
+
+
+    print "Parsing table for polynomial values\n" if $verbose;
+
+    # find the polynomial part header
+    local $found = 0;
+    while (! $found && ($line=<TABLEFILE>)) {
+        if ($line =~ /^Polynomial part/) {
+            print COEFFFILE "# $line";
+            $found = 1;
+        }
+    }
+
+     if (! $found) {
+        print "Failed to find the coefficient section" if $verbose;
+        return 1;
+    }
+
+    print "Found proper polynomials section\n" if $verbose;
+
+    # find the line of coefficients
+    $found = 0;
+    while (! $found && ($line=<TABLEFILE>)) {
+        if ($line =~ /^\s*(\S+)\s+([\+-])\s*(\S+)\s+t\s+([\+-])\s*(\S+)\s+t\^2\s+([\+-])\s*(\S+)\s+t\^3\s+([\+-])\s*(\S+)\s+t\^4\s+([\+-])\s*(\S+)\s+t\^5/) {
+            $found = 1;
+            print COEFFFILE "$1 $2$3 $4$5 $6$7 $8$9 $10$11\n";
+            print "Coeff: $1 $2$3 $4$5 $6$7 $8$9 $10$11\n" if $verbose;
+        }
+        if ($line =~ /^\-\-\-\-\-\-\-/) {
+            $found = 2;
+        }
+    }
+
+    if ($found != 1) {
+        print "Failed to find the coefficients" if $verbose;
+        return 1;
+    }
+
+    return 0;
+}
+
+ sub parseTable {
+
+    print "Parsing table for non-polynomial values\n" if $verbose;
+
+    # find the non-polynomial part header
+    local $found = 0;
+    local $j = 0;
+    while (! $found && ($line=<TABLEFILE>)) {
+        if ($line =~ /^\s*j\s*=\s*(\d+)/) {
+            $j = $1;
+            print "Found data for j=$j\n" if $verbose;
+            $found = 1;
+        } else {
+            print OUTPUTFILE "# $line";
+        }
+    }
+
+     if (! $found) {
+        print "Failed to find the non-polynomial data" if $verbose;
+        return 1;
+    }
+
+    # find the line of coefficients
+    local $items = 0;
+    while ($line=<TABLEFILE>) {
+        if ($line =~ /^\s*j\s*=\s*(\d+)/) {
+            $j = $1;
+            print "Found data for j=$j\n" if $verbose;
+        } elsif ($line =~ /^\s*\d+\s(.+)/) {
+            print OUTPUTFILE "$j $1\n";
+            $items++;
+        }
+    }
+
+    print "Found $items data lines.\n" if $verbose;
+
+    return 0;
+}
+
+
+
Index: /tags/sj_tags/sj_root_20080929/psLib/utils/psTableParse.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psLib/utils/psTableParse.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psLib/utils/psTableParse.c	(revision 22322)
@@ -0,0 +1,381 @@
+/** @file  psTableParse.c
+ *
+ *  @brief Parses input data tables (finals2000A.data) and outputs files for specified format
+ *
+ *  @ingroup psTableParse
+ *
+ *  @author Dave Robbins, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-09-23 03:03:14 $
+ *
+ *  Copyright 2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+//#include "pslib.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+static FILE *output = NULL;
+static FILE *input = NULL;
+
+static void printHeader(char *filename);
+
+int main(int argc, char *argv[])
+{
+    if (argc < 2) {
+        printf("\n Insufficient arguments.  Use [-h] for help.\n");
+        return 0;
+    }
+    if (!strncmp(argv[1], "-h", 2) ) {
+        printf("\n Usage:  psTableParse inputTable.data outputTable.dat <Sections>.\n");
+        printf(" i.e.    psTableParse finals.data finals.dat 1 3 4 8\n");
+        printf("\n Sections:\n 1)MJD Date       2)X (Bull A)     3)Y (Bull A)");
+        printf("\n 4)X (Bull B)     5)Y (Bull B)     6)dX (Bull A)");
+        printf("\n 7)dY (Bull A)    8)dX (Bull B)    9)dY (Bull B)");
+        printf("\n 10)UT1-UTC (Bull A)       11)UT1-UTC (Bull B)");
+        printf("\n 12)Error in X (Bull A)    13)Error in Y (Bull A)");
+        printf("\n 14)Error in dX (Bull A)   15)Error in dY (Bull A)    ");
+        printf("\n 16)Error in UT1-UTC (Bull A)\n\n");
+        return 0;
+    }
+    input = fopen(argv[1], "r");
+    output = fopen(argv[2], "w");
+    if (input == NULL) {
+        printf("\nERROR.  Could not open input file.\n");
+        return 0;
+    }
+    if (output == NULL) {
+        printf("\nERROR.  Could not open or create output file.\n");
+        return 0;
+    }
+    char data2[200];
+    printHeader(argv[1]);
+    fprintf(output, "#  ");
+    for (int i = 3; i < argc; i++) {
+        if(!strncmp(argv[i], "1", 2)) {
+            fprintf(output, "MJD       ");
+        }
+        if(!strncmp(argv[i], "2", 2)) {
+            fprintf(output, " X (A)     ");
+        }
+        if(!strncmp(argv[i], "3", 2)) {
+            fprintf(output, " Y (A)     ");
+        }
+        if(!strncmp(argv[i], "4", 2)) {
+            fprintf(output, " X (B)     ");
+        }
+        if(!strncmp(argv[i], "5", 2)) {
+            fprintf(output, "  Y (B)     ");
+        }
+        if(!strncmp(argv[i], "6", 2)) {
+            fprintf(output, " dX (A)     ");
+        }
+        if(!strncmp(argv[i], "7", 2)) {
+            fprintf(output, " dY (A)     ");
+        }
+        if(!strncmp(argv[i], "8", 2)) {
+            fprintf(output, " dX (B)     ");
+        }
+        if(!strncmp(argv[i], "9", 2)) {
+            fprintf(output, " dY (B)     ");
+        }
+        if(!strncmp(argv[i], "10", 2)) {
+            fprintf(output, "UT1-UTC (A)  ");
+        }
+        if(!strncmp(argv[i], "11", 2)) {
+            fprintf(output, "UT1-UTC (B)  ");
+        }
+        if(!strncmp(argv[i], "12", 2)) {
+            fprintf(output, " xErr (A)  ");
+        }
+        if(!strncmp(argv[i], "13", 2)) {
+            fprintf(output, " yErr (A)  ");
+        }
+        if(!strncmp(argv[i], "14", 2)) {
+            fprintf(output, " dXErr (A) ");
+        }
+        if(!strncmp(argv[i], "15", 2)) {
+            fprintf(output, " dYErr (A) ");
+        }
+        if(!strncmp(argv[i], "16", 2)) {
+            fprintf(output, "UT1-UTC Err");
+        }
+    }
+    fprintf(output, "\n#\n#\n");
+    int i;
+    while ( fscanf(input, "%188c", data2) != EOF) {
+
+        for(int j = 3; j < argc; j++) {
+            if(!strncmp(argv[j], "1", 2) ) {
+                for (i = 7; i <= 14; i++) {
+                    fprintf(output, "%c", data2[i]);
+                }
+            }
+            if(!strncmp(argv[j], "2", 2) ) {
+                if (data2[23] == ' ') {
+                    fprintf(output, "   0.000 ");
+                } else {
+                    for (i = 18; i <= 26; i++) {
+                        if (i == 18 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 21;
+                        }
+                        if (i == 19) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "3", 2) ) {
+                if (data2[42] == ' ') {
+                    fprintf(output, "   0.000 ");
+                } else {
+                    for (i = 37; i <= 45; i++) {
+                        if (i == 37 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 40;
+                        }
+                        if (i == 38) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "4", 2) ) {
+                if (data2[139] == ' ') {
+                    fprintf(output, "    0.000 ");
+                    //                    i = 144;
+                } else {
+                    for (i = 134; i <= 143; i++) {
+                        if (i == 135 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 138;
+                        }
+                        if (i == 136) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "5", 2) ) {
+                if (data2[149] == ' ') {
+                    fprintf(output, "    0.000 ");
+                } else {
+                    for (i = 144; i <= 153; i++) {
+                        if (i == 145 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 148;
+                        }
+                        if (i == 146) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "6", 2) ) {
+                if (data2[102] == ' ') {
+                    fprintf(output, "   0.000 ");
+                } else {
+                    for (i = 97; i <= 105; i++) {
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "7", 2) ) {
+                if (data2[121] == ' ') {
+                    fprintf(output, "   0.000 ");
+                } else {
+                    for (i = 116; i <= 124; i++) {
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "8", 2) ) {
+                if (data2[170] == ' ') {
+                    fprintf(output, "    0.000 ");
+                } else {
+                    for (i = 165; i <= 174; i++) {
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "9", 2) ) {
+                if (data2[180] == ' ') {
+                    fprintf(output, "    0.000 ");
+                } else {
+                    for (i = 175; i <= 184; i++) {
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "10", 2) ) {
+                if (data2[63] == ' ') {
+                    fprintf(output, "    0.000 ");
+                } else {
+                    for (i = 58; i <= 67; i++) {
+                        if (i == 58 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 61;
+                        }
+                        if (i == 59) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "11", 2) ) {
+                if (data2[159] == ' ') {
+                    fprintf(output, "     0.000 ");
+                } else {
+                    for (i = 154; i <= 164; i++) {
+                        if (i == 155 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 158;
+                        }
+                        if (i == 156) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "12", 2) ) {
+                if (data2[32] == ' ') {
+                    fprintf(output, "    0.000 ");
+                } else {
+                    for (i = 27; i <= 35; i++) {
+                        if (i == 27 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 30;
+                        }
+                        if (i == 28) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "13", 2) ) {
+                if (data2[51] == ' ') {
+                    fprintf(output, "     0.000 ");
+                } else {
+                    for (i = 46; i <= 54; i++) {
+                        if (i == 46 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 49;
+                        }
+                        if (i == 47) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "14", 2) ) {
+                if (data2[111] == ' ') {
+                    fprintf(output, "    0.000 ");
+                } else {
+                    for (i = 106; i <= 114; i++) {
+                        if (i == 106 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 109;
+                        }
+                        if (i == 107) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "15", 2) ) {
+                if (data2[131] == ' ') {
+                    fprintf(output, "     0.000 ");
+                } else {
+                    for (i = 125; i <= 133; i++) {
+                        if (i == 125 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 128;
+                        }
+                        if (i == 126) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            if(!strncmp(argv[j], "16", 2) ) {
+                if (data2[73] == ' ') {
+                    fprintf(output, "     0.000 ");
+                } else {
+                    for (i = 68; i <= 77; i++) {
+                        if (i == 155 && data2[i+1] == '-' && data2[i+2] == '.') {
+                            fprintf(output, "-0.");
+                            i = 71;
+                        }
+                        if (i == 69) {
+                            if (data2[i] == ' ' && data2[i+1] == '.') {
+                                fprintf(output, "0");
+                                i++;
+                            }
+                        }
+                        fprintf(output, "%c", data2[i]);
+                    }
+                }
+            }
+            fprintf(output, "  ");
+        }
+        fprintf(output, "\n");
+    }
+
+    fclose(input);
+    fclose(output);
+    return 0;
+}
+
+void printHeader(char *filename)
+{
+    if (output == NULL) {
+        printf("\n Unable to access output file.  Output is NULL.\n");
+    } else {
+        fprintf(output, "#\n#  Stripped version of %s file\n#\n#\n", filename);
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/.cvsignore	(revision 22322)
@@ -0,0 +1,29 @@
+autom4te.cache
+Makefile
+config.log
+config.status
+libtool
+Makefile.in
+aclocal.m4
+configure
+Doxyfile
+DoxygenLog
+docs
+man
+psmodules-config
+psmodules.pc
+bin
+include
+lib
+config.guess
+config.sub
+depcomp
+install-sh
+ltmain.sh
+missing
+psmodule.kdevelop.pcs
+psmodule-*.tar.gz
+psmodule-*.tar.bz2
+compile
+ChangeLog
+psmodules-*.tar.*
Index: /tags/sj_tags/sj_root_20080929/psModules/AUTHORS
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/AUTHORS	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/AUTHORS	(revision 22322)
@@ -0,0 +1,1 @@
+Maui High Performance Computing Center, University of Hawai'i
Index: /tags/sj_tags/sj_root_20080929/psModules/COPYING
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/COPYING	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/COPYING	(revision 22322)
@@ -0,0 +1,6 @@
+(C) 2004 University of Hawai'i
+
+Permission to copy/distribute to be governed by:
+    Institute for Astronomy
+    2680 Woodlawn Drive
+    Honolulu, HI 96822-1897
Index: /tags/sj_tags/sj_root_20080929/psModules/Doxyfile.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/Doxyfile.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/Doxyfile.in	(revision 22322)
@@ -0,0 +1,1079 @@
+# Doxyfile 1.3.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "Pan-STARRS Module Library"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = @VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = @builddir@/docs
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
+# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese,
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH        =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP         = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
+# only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = DoxygenLog
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = src
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
+
+FILE_PATTERNS          = *.h *.dox
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = *_wrap.c
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.
+
+INPUT_FILTER           = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output dir.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = YES
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 10
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = YES
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = letter
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = times
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimised for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assigments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED             = DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = @PERL@
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similiar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes that 
+# lay further from the root node will be omitted. Note that setting this option to 
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also 
+# note that a graph may be further truncated if the graph's image dimensions are 
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
+
Index: /tags/sj_tags/sj_root_20080929/psModules/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/INSTALL	(revision 22322)
@@ -0,0 +1,167 @@
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes a while.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Type `make install' to install the programs and any data files and
+     documentation.
+
+  4. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
Index: /tags/sj_tags/sj_root_20080929/psModules/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/Makefile.am	(revision 22322)
@@ -0,0 +1,26 @@
+SUBDIRS = src test
+
+bin_SCRIPTS = psmodules-config
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= psmodules.pc
+
+EXTRA_DIST = \
+	Doxyfile.in \
+	psmodules-config.in \
+	psmodules.pc.in \
+	autogen.sh
+
+if HAVE_DOXYGEN
+install-data-hook: doxygen
+	-$(mkdir_p) $(mandir)/man3
+	chmod 0755 $(mandir)/man3
+	-cp $(top_builddir)/docs/man/man3/* $(mandir)/man3
+
+doxygen:
+	$(DOXYGEN)
+endif
+
+CLEANFILES = $(prefix)/docs/psmodules/* *~
+
+test: check
Index: /tags/sj_tags/sj_root_20080929/psModules/NEWS
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/NEWS	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/NEWS	(revision 22322)
@@ -0,0 +1,1 @@
+ 
Index: /tags/sj_tags/sj_root_20080929/psModules/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/README	(revision 22322)
@@ -0,0 +1,1 @@
+
Index: /tags/sj_tags/sj_root_20080929/psModules/TODO
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/TODO	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/TODO	(revision 22322)
@@ -0,0 +1,1 @@
+ 
Index: /tags/sj_tags/sj_root_20080929/psModules/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/autogen.sh	(revision 22322)
@@ -0,0 +1,114 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=psmodules
+TEST_TYPE=-f
+FILE=psmodules.pc.in
+
+DIE=0
+
+if [ "`which glibtoolize 2> /dev/null`" != "" ]
+ then LIBTOOLIZE=glibtoolize
+ else LIBTOOLIZE=libtoolize
+fi
+
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+# bypass taps bootstrap.sh
+cd ./test/tap
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/psModules/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/configure.ac	(revision 22322)
@@ -0,0 +1,348 @@
+AC_PREREQ(2.61)
+
+AC_INIT([psmodules],[1.1.0],[http://pan-starrs.ifa.hawaii.edu/bugzilla])
+AC_CONFIG_SRCDIR([psmodules.pc.in])
+
+dnl this enables the building of libtap
+AC_CONFIG_SUBDIRS([test/tap])
+
+AM_INIT_AUTOMAKE([1.7 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+PSMODULES_LT_VERSION="1:1:0"
+AC_SUBST(PSMODULES_LT_VERSION,$PSMODULES_LT_VERSION)
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_PROG_CC_C99
+AC_GNU_SOURCE
+AC_C_INLINE
+AC_C_CONST
+AC_PROG_INSTALL
+AM_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+AC_FUNC_FSEEKO
+
+AC_PREFIX_DEFAULT([`pwd`])
+
+dnl build tests at the same time as the source code
+AC_ARG_ENABLE(tests,
+  [AS_HELP_STRING(--enable-tests,build tests at same time as source)],
+  [AC_MSG_RESULT(test building enabled)
+   tests=true],
+   [tests=false])
+AM_CONDITIONAL(BUILD_TESTS, test x$tests = xtrue)
+
+dnl ------------------- PERL options ---------------------
+  AC_ARG_WITH(perl,
+    [AS_HELP_STRING(--with-perl=FILE,Specify location of PERL executable.)],
+    [AC_CHECK_PROG(PERL, $withval, $withval)],
+    [AC_CHECK_PROG(PERL, perl, `which perl`)])
+    if test "$PERL" == ""
+    then
+      AC_MSG_ERROR([PERL is required.  Use --with-perl to specify its install location.])
+    fi
+    AC_SUBST(PERL,$PERL)
+
+SRCPATH='${top_srcdir}/src'
+SRCDIRS="extras config concepts camera astrom detrend imcombine objects"
+# escape two escapes at this level so \\ gets passed to the shell and \ to perl
+SRCINC=`echo "${SRCDIRS=}" | ${PERL} -pe "s|(\w+)|-I\\\\${SRCPATH=}/\1|g"`
+SRCINC="-I${SRCPATH=} ${SRCINC=}"
+SRCSUBLIBS=`echo "${SRCDIRS=}" | ${PERL} -pe "s|(\w+)|\1/libpsmodules\1.la|g"`
+AC_SUBST(SRCSUBLIBS,${SRCSUBLIBS=})
+AC_SUBST(SRCINC,${SRCINC=})
+AC_SUBST([SRCDIRS],${SRCDIRS=})
+
+dnl doxygen -------------------------------------------------------------------
+dnl doxygen doc generation is very, very slow so we're turing it off by default
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_ENABLE(doxygen,
+  [AS_HELP_STRING(--enable-doxygen ,enable manpage generation)],
+  [AC_MSG_RESULT(doxygen enabled)
+    AC_PATH_PROG([DOXYGEN], [doxygen], [])
+  ],
+  [AC_MSG_RESULT([doxygen disabled])
+    doxygen=off
+  ]
+)
+AM_CONDITIONAL([HAVE_DOXYGEN], test -n "$DOXYGEN" -a "x$doxygen" != "xoff")
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------------------------------------------------
+
+AC_PATH_PROG([ERRORCODES], [psParseErrorCodes], [missing])
+if test "$ERRORCODES" = "missing" ; then
+  AC_MSG_ERROR([psParseErrorCodes is required])
+fi
+
+dnl ------------------ kapa,libkapa options -------------------------
+dnl -- libkapa implies the requirement for libpng, libjpeg as well --
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+dnl test for command-line options: use ohana-config if not supplied
+KAPA_CFLAGS_CONFIG="true"
+KAPA_LIBS_CONFIG="true"
+AC_ARG_WITH(kapa,
+[AS_HELP_STRING(--with-kapa=DIR,Specify location of libkapa)],
+[KAPA_CFLAGS="-I$withval/include" KAPA_LIBS="-L$withval/lib" 
+ KAPA_CFLAGS_CONFIG="false"       KAPA_LIBS_CONFIG="false"])
+AC_ARG_WITH(kapa-include,
+[AS_HELP_STRING(--with-kapa-include=DIR,Specify libkapa include directory.)],
+[KAPA_CFLAGS="-I$withval" KAPA_CFLAGS_CONFIG="false"])
+AC_ARG_WITH(kapa-lib,
+[AS_HELP_STRING(--with-kapa-lib=DIR,Specify libkapa library directory.)],
+[KAPA_LIBS="-L$withval" KAPA_LIBS_CONFIG="false"])
+
+echo "KAPA_CFLAGS_CONFIG: $KAPA_CFLAGS_CONFIG"
+echo "KAPA_LIBS_CONFIG: $KAPA_LIBS_CONFIG"
+echo "KAPA_CFLAGS: $KAPA_CFLAGS"
+echo "KAPA_LIBS: $KAPA_LIBS"
+
+dnl HAVE_KAPA is set to false if any of the tests fail
+HAVE_KAPA="true"
+AC_MSG_NOTICE([checking for libkapa])
+if test "$KAPA_CFLAGS_CONFIG" = "true" -o "$KAPA_LIBS_CONFIG" = "true"; then
+  AC_MSG_NOTICE([kapa info supplied by ohana-config])
+  KAPA_CONFIG=`which ohana-config`
+  AC_CHECK_FILE($KAPA_CONFIG,[],
+    [HAVE_KAPA="false"; AC_MSG_WARN([libkapa is not found: output plots disabled.  Obtain libkapa at http://kiawe.ifa.hawaii.edu/Elixir/Ohana or use --with-kapa to specify location])])
+  
+  echo "HAVE_KAPA: $HAVE_KAPA"
+  echo "KAPA_CFLAGS_CONFIG: $KAPA_CFLAGS_CONFIG"
+
+  if test "$HAVE_KAPA" = "true" -a "$KAPA_CFLAGS_CONFIG" = "true" ; then
+   AC_MSG_NOTICE([libkapa cflags info supplied by ohana-config])
+   AC_MSG_CHECKING([libkapa cflags])
+   KAPA_CFLAGS="`${KAPA_CONFIG} --cflags`"
+   AC_MSG_RESULT([${KAPA_CFLAGS}])
+  fi
+
+  if test "$HAVE_KAPA" = "true" -a "$KAPA_LIBS_CONFIG" = "true" ; then
+   AC_PATH_X
+   if test "$no_x" = "yes" ; then
+      AC_MSG_WARN([X11 not found: output plots using kapa disabled.  Use --x-includes and --x-libraries if required.])
+      HAVE_KAPA="false"
+   else
+      AC_MSG_NOTICE([libkapa ldflags info supplied by ohana-config])
+      AC_MSG_CHECKING([libkapa ldflags])
+      if test -n "$x_libraries" ; then
+            KAPA_LIBS="`${KAPA_CONFIG} --libs` -L$x_libraries -lX11"
+      else
+            KAPA_LIBS="`${KAPA_CONFIG} --libs` -lX11"
+      fi
+      if test -n "$x_includes" ; then
+            KAPA_CFLAGS="${KAPA_CFLAGS} -I$x_includes"
+      else
+            KAPA_CFLAGS="${KAPA_CFLAGS}"
+      fi
+      AC_MSG_RESULT([${KAPA_LIBS}])
+   fi
+  fi
+fi
+
+if test "$HAVE_KAPA" = "true" ; then
+ AC_MSG_NOTICE([libkapa supplied])
+ PSMODULES_CFLAGS="${PSMODULES_CFLAGS} ${KAPA_CFLAGS}"
+ PSMODULES_LIBS="${PSMODULES_LIBS} ${KAPA_LIBS}"
+else
+ AC_MSG_NOTICE([libkapa ignored])
+fi
+
+dnl HAVE_KAPA is set to false if any of the tests fail
+dnl HAVE_KAPA=true
+dnl AC_CHECK_HEADERS([kapa.h],
+dnl  [PSMODULES_CFLAGS="$PSMODULES_CFLAGS $KAPA_CFLAGS" AC_SUBST(KAPA_CFLAGS)],
+dnl  [HAVE_KAPA=false; AC_MSG_WARN([libkapa headers not found: output plots disabled.  Obtain libkapa at http://kiawe.ifa.hawaii.edu/Elixir/Ohana or use --with-kapa to specify location.])]
+dnl )
+dnl AC_CHECK_LIB(kapa,KapaInitGraph,
+dnl  [PSMODULES_LIBS="$PSMODULES_LIBS $JPEG_LDFLAGS -ljpeg"],  
+dnl  [HAVE_KAPA=false; AC_MSG_WARN([libkapa headers not found: output plots disabled.  Obtain libkapa at http://kiawe.ifa.hawaii.edu/Elixir/Ohana or use --with-kapa to specify location.])],[-lm]
+dnl )
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ libjpeg options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(jpeg,
+[AS_HELP_STRING(--with-jpeg=DIR,Specify location of libjpeg.)],
+[JPEG_CFLAGS="-I$withval/include"
+ JPEG_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(jpeg-include,
+[AS_HELP_STRING(--with-jpeg-include=DIR,Specify libjpeg include directory.)],
+[JPEG_CFLAGS="-I$withval"])
+AC_ARG_WITH(jpeg-lib,
+[AS_HELP_STRING(--with-jpeg-lib=DIR,Specify libjpeg library directory.)],
+[JPEG_LDFLAGS="-L$withval"])
+
+CFLAGS="${CFLAGS} ${JPEG_CFLAGS}"
+CPPFLAGS=${CFLAGS}
+LDFLAGS="${LDFLAGS} ${JPEG_LDFLAGS}"
+
+AC_CHECK_HEADERS([jpeglib.h],
+  [PSMODULES_CFLAGS="$PSMODULES_CFLAGS $JPEG_CFLAGS" AC_SUBST(JPEG_CFLAGS)],
+  [HAVE_KAPA=false; AC_MSG_WARN([libjpeg headers not found: output plots disabled.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
+)
+
+AC_CHECK_LIB(jpeg,jpeg_CreateCompress,
+  [PSMODULES_LIBS="$PSMODULES_LIBS $JPEG_LDFLAGS -ljpeg"],
+  [HAVE_KAPA=false; AC_MSG_WARN([libjpeg library not found: output plots disabled.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
+)
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ libpng options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(png,
+[AS_HELP_STRING(--with-png=DIR,Specify location of libpng.)],
+[PNG_CFLAGS="-I$withval/include"
+ PNG_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(png-include,
+[AS_HELP_STRING(--with-png-include=DIR,Specify libpng include directory.)],
+[PNG_CFLAGS="-I$withval"])
+AC_ARG_WITH(png-lib,
+[AS_HELP_STRING(--with-png-lib=DIR,Specify libpng library directory.)],
+[PNG_LDFLAGS="-L$withval"])
+
+CFLAGS="${CFLAGS} ${PNG_CFLAGS}"
+CPPFLAGS=${CFLAGS}
+LDFLAGS="${LDFLAGS} ${PNG_LDFLAGS}"
+
+AC_CHECK_HEADERS([png.h],
+  [PSMODULES_CFLAGS="$PSMODULES_CFLAGS $PNG_CFLAGS" AC_SUBST(PNG_CFLAGS)],
+  [HAVE_KAPA=false; AC_MSG_WARN([libpng headers not found: output plots disabled.  Obtain libpng from http://www.ijg.org/ or use --with-png to specify location.])]
+)
+
+AC_CHECK_LIB(png,png_init_io,
+  [PSMODULES_LIBS="$PSMODULES_LIBS $PNG_LDFLAGS -lpng"],
+  [HAVE_KAPA=false; AC_MSG_WARN([libpng library not found: output plots disabled.  Obtain libpng from http://www.ijg.org/ or use --with-png to specify location.])]
+)
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ use kapa or not? ---------------------
+
+if test "$HAVE_KAPA" == "true" ; then
+  AC_MSG_RESULT([including plotting functions])
+  AC_DEFINE([HAVE_KAPA],[1],[enable use of libkapa])
+else
+  AC_MSG_RESULT([skipping plotting functions])
+  AC_DEFINE([HAVE_KAPA],[0],[disable use of libkapa])
+fi
+
+dnl pslib ---------------------------------------------------------------------
+AC_ARG_WITH(pslib-config,
+[  --with-pslib-config=FILE  Specify location of psLib-config script],
+[PSLIB_CONFIG=$withval])
+
+if test -z ${PSLIB_CONFIG} ; then
+  PKG_CHECK_MODULES([PSLIB], [pslib >= 0.12.0])
+else
+  AC_CHECK_FILE($PSLIB_CONFIG,[],
+    [AC_MSG_ERROR([psLib is required.  If not in path, use --with-pslib-config to specify pslib-config script location.])])
+  AC_MSG_CHECKING([PSLIB_CFLAGS])
+  PSLIB_CFLAGS="`${PSLIB_CONFIG} --cflags`"
+  AC_MSG_RESULT([${PSLIB_CFLAGS}])
+  AC_MSG_CHECKING([PSLIB_LIBS])
+  PSLIB_LIBS="`${PSLIB_CONFIG} --libs`"
+  AC_MSG_RESULT([${PSLIB_LIBS}])
+fi
+
+PSMODULES_CFLAGS="${PSMODULES_CFLAGS=} ${PSLIB_CFLAGS}"
+PSMODULES_LIBS="${PSMODULES_LIBS=} ${PSLIB_LIBS}"
+
+dnl nebclient -----------------------------------------------------------------
+
+PKG_CHECK_MODULES([NEBCLIENT], [nebclient >= 0.0.2],
+    [AC_DEFINE([HAVE_NEBCLIENT], 1, [Define to 1 if libnebclient is avaiable])],    [AC_MSG_RESULT([no])]
+)
+
+PSMODULES_CFLAGS="${PSMODULES_CFLAGS=} ${NEBCLIENT_CFLAGS}"
+PSMODULES_LIBS="${PSMODULES_LIBS=} ${NEBCLIENT_LIBS}"
+
+echo "PSMODULES_CFLAGS: $PSMODULES_CFLAGS"
+echo "PSMODULE_LIBS: $PSMODULES_LIBS"
+
+dnl ------- enable -Werror after all of the probes have run ---------
+IPP_STDOPTS
+
+CFLAGS="${CFLAGS} -Wall -Werror"
+dnl enable POSIX/XSI and C99 compliance
+CFLAGS="${CFLAGS=} -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L"
+
+dnl ---------------- export variables --------------------
+
+
+AC_SUBST([PSMODULES_CFLAGS])
+AC_SUBST([PSMODULES_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  src/astrom/Makefile
+  src/camera/Makefile
+  src/config/Makefile
+  src/concepts/Makefile
+  src/detrend/Makefile
+  src/imcombine/Makefile
+  src/objects/Makefile
+  src/extras/Makefile
+  test/Makefile
+  test/astrom/Makefile
+  test/config/Makefile
+  test/camera/Makefile
+  test/concepts/Makefile
+  test/detrend/Makefile
+  test/extras/Makefile
+  test/imcombine/Makefile
+  test/objects/Makefile
+  test/pstap/Makefile
+  test/pstap/src/Makefile
+  Doxyfile
+  psmodules-config
+  psmodules.pc
+])
+
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/psModules/psmodules-config.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/psmodules-config.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/psmodules-config.in	(revision 22322)
@@ -0,0 +1,76 @@
+#! /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/@PACKAGE_NAME@
+
+usage()
+{
+    cat <<EOF
+Usage: pslib-config [OPTION]
+
+Known values for OPTION are:
+
+  --prefix		print psLib installation prefix
+  --libs		print library linking information
+  --cflags		print pre-processor and compiler flags
+  --help		display this help and exit
+  --version		output version information
+
+EOF
+
+    exit $1
+}
+
+if test $# -eq 0; then
+    usage 1
+fi
+
+cflags=false
+libs=false
+
+while test $# -gt 0; do
+    case "$1" in
+    -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+    *) optarg= ;;
+    esac
+
+    case "$1" in
+    --prefix=*)
+	prefix=$optarg
+	;;
+
+    --prefix)
+	echo $prefix
+	;;
+
+    --version)
+	echo @VERSION@
+	exit 0
+	;;
+
+    --help)
+	usage 0
+	;;
+
+    --cflags)
+       	echo -I${includedir} @PSMODULES_CFLAGS@
+       	;;
+
+    --libs)
+       	echo -L${libdir} -lpsmodules @PSMODULES_LIBS@
+       	;;
+
+    --deps)
+       	echo @LDFLAGS@
+       	;;
+    *)
+	usage
+	exit 1
+	;;
+    esac
+    shift
+done
+
+exit 0
Index: /tags/sj_tags/sj_root_20080929/psModules/psmodules.pc.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/psmodules.pc.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/psmodules.pc.in	(revision 22322)
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/@PACKAGE_NAME@
+
+Name: @PACKAGE_NAME@
+Description: Pan-STARRS Module Library
+Version: @VERSION@
+Requires: pslib
+Libs: -L${libdir} -lpsmodules
+Libs.private: @PSMODULES_LIBS@
+Cflags: -I${includedir} @PSMODULES_CFLAGS@
Index: /tags/sj_tags/sj_root_20080929/psModules/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+.deps
+.libs
+Makefile
+config.h
+stamp-h1
+Makefile.in
+config.h.in
+*.la
+*.lo
Index: /tags/sj_tags/sj_root_20080929/psModules/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+SUBDIRS = $(SRCDIRS)
+lib_LTLIBRARIES = libpsmodules.la
+
+libpsmodules_la_CPPFLAGS = $(SRCINC)
+libpsmodules_la_LIBADD = $(SRCSUBLIBS) $(PSMODULES_LIBS)
+libpsmodules_la_DEPENDENCIES = $(SRCSUBLIBS)
+libpsmodules_la_SOURCES = 
+libpsmodules_la_LDFLAGS = -version-info $(PSMODULES_LT_VERSION)
+
+pkginclude_HEADERS = \
+	psmodules.h
+
+CLEANFILES = *~
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/Makefile.am	(revision 22322)
@@ -0,0 +1,23 @@
+noinst_LTLIBRARIES = libpsmodulesastrom.la
+
+libpsmodulesastrom_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS) -I../pslib/
+libpsmodulesastrom_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulesastrom_la_SOURCES  = \
+	pmAstrometryObjects.c \
+	pmAstrometryRegions.c \
+	pmAstrometryDistortion.c \
+	pmAstrometryUtils.c \
+	pmAstrometryModel.c \
+	pmAstrometryRefstars.c \
+	pmAstrometryWCS.c
+
+pkginclude_HEADERS = \
+	pmAstrometryObjects.h \
+	pmAstrometryRegions.h \
+	pmAstrometryDistortion.h \
+	pmAstrometryUtils.h \
+	pmAstrometryModel.h \
+	pmAstrometryRefstars.h \
+	pmAstrometryWCS.h
+
+CLEANFILES = *~
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryDistortion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryDistortion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryDistortion.c	(revision 22322)
@@ -0,0 +1,352 @@
+/** @file  pmAstrometryDistortion.c
+*
+*  @brief This file defines the basic types for measuring the focal-plane distortion.
+*
+*  @ingroup AstroImage
+*
+*  @author EAM, IfA
+*
+*  @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2008-09-02 19:05:09 $
+*
+*  Copyright 2006 Institute for Astronomy, University of Hawaii
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAExtent.h"
+#include "pmAstrometryObjects.h"
+#include "pmAstrometryRegions.h"
+#include "pmAstrometryDistortion.h"
+#include "pmKapaPlots.h"
+
+static void pmAstromGradientFree (pmAstromGradient *grad)
+{
+
+    if (grad == NULL)
+        return;
+
+    return;
+}
+
+pmAstromGradient *pmAstromGradientAlloc ()
+{
+
+    pmAstromGradient *gradient = psAlloc (sizeof(pmAstromGradient));
+    psMemSetDeallocator(gradient, (psFreeFunc) pmAstromGradientFree);
+
+    return (gradient);
+}
+
+psArray *pmAstromMeasureGradients(psArray *gradients, psArray *rawstars, psArray *refstars, psArray *matches, psRegion *region, int Nx, int Ny)
+{
+
+    if (gradients == NULL) {
+        gradients = psArrayAllocEmpty (100);
+    }
+
+    // NOTE: region specifies the FP region in pixels covered by the chip (NOT in FP units)
+    // determine range
+    int DX = (region->x1 - region->x0) / Nx;
+    int DY = (region->y1 - region->y0) / Ny;
+
+    psPolynomial2D *local = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, 1, 1);
+    local->coeffMask[1][1] = PS_POLY_MASK_SET;
+
+    // measure gradient for fractional chip regions
+    for (int nx = 0; nx < Nx; nx++) {
+        for (int ny = 0; ny < Ny; ny++) {
+            int Xmin = nx*DX;
+            int Xmax = Xmin + DX;
+            int Ymin = ny*DY;
+            int Ymax = Ymin + DY;
+
+            psStats *stats = NULL;
+            psVector *mask = NULL;
+            pmAstromGradient *grad = NULL;
+
+            psVector *L  = psVectorAllocEmpty (100, PS_TYPE_F32);
+            psVector *M  = psVectorAllocEmpty (100, PS_TYPE_F32);
+            psVector *dP = psVectorAllocEmpty (100, PS_TYPE_F32);
+            psVector *dQ = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+            // XXX this is a bit inefficient: first sorting by X or Y could speed this up.
+            // XXX or assigning to a segment in a single pass first
+            // select the stars within this chip region
+            int Npts = 0;
+            for (int i = 0; i < matches->n; i++) {
+
+                pmAstromMatch *match = matches->data[i];
+
+                pmAstromObj *raw = rawstars->data[match->raw];
+
+                if (raw->chip->x < Xmin) continue;
+                if (raw->chip->x > Xmax) continue;
+                if (raw->chip->y < Ymin) continue;
+                if (raw->chip->y > Ymax) continue;
+
+                pmAstromObj *ref = refstars->data[match->ref];
+
+                L->data.F32[Npts] = raw->FP->x;
+                M->data.F32[Npts] = raw->FP->y;
+
+                // P,Q = L,M + terms of order epsilon.
+                // measuring the gradient constrains thos terms
+                dP->data.F32[Npts] = ref->TP->x - raw->FP->x;
+                dQ->data.F32[Npts] = ref->TP->y - raw->FP->y;
+
+                psVectorExtend (L, 100, 1);
+                psVectorExtend (M, 100, 1);
+                psVectorExtend (dP, 100, 1);
+                psVectorExtend (dQ, 100, 1);
+                Npts++;
+            }
+
+            psTrace ("psModules.astrom", 4, "Npts: %d (%d,%d) : (%d - %d),(%d - %d)\n", Npts, nx, ny, Xmin, Xmax, Ymin, Ymax);
+
+            if (Npts < 5)
+                goto skip;
+
+            // stats structure and mask for use in measuring the clipping statistic
+            // this analysis has too few data points to use the robust median method
+            stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+            mask = psVectorAlloc (Npts, PS_TYPE_MASK);
+            psVectorInit (mask, 0);
+
+            grad = pmAstromGradientAlloc ();
+
+	    // XXX psTraceSetLevel("psLib.math.psVectorClipFitPolynomial2D", 7);
+
+            // fit the collection of positions and offsets with a local 1st order gradient
+            // apply 3hi/3lo sigma clipping to the fitted data values
+            // the mask is used to mark the points which pass / fail the fit
+            if (!psVectorClipFitPolynomial2D (local, stats, mask, 0xff, dP, NULL, L, M)) {
+                goto skip;
+            }
+
+            grad->dTPdL.x = local->coeff[1][0];
+            grad->dTPdM.x = local->coeff[0][1];
+
+	    // XXX psTraceSetLevel("psLib.math.psVectorClipFitPolynomial2D", 0);
+
+            // fit the collection of positions and offsets with a local 1st order gradient
+            // apply 3hi/3lo sigma clipping to the fitted data values
+            // the mask is used to mark the points which pass / fail the fit
+            if (!psVectorClipFitPolynomial2D (local, stats, mask, 0xff, dQ, NULL, L, M)) {
+                goto skip;
+            }
+
+            grad->dTPdL.y = local->coeff[1][0];
+            grad->dTPdM.y = local->coeff[0][1];
+
+            // also measure the L and M median positions as a representative coordinate
+            psVectorStats (stats, L, NULL, NULL, 0);
+            grad->FP.x = stats->sampleMedian;
+
+            psVectorStats (stats, M, NULL, NULL, 0);
+            grad->FP.y = stats->sampleMedian;
+
+            psArrayAdd (gradients, 100, grad);
+
+skip:
+            psFree (grad);
+            psFree (stats);
+            psFree (mask);
+            psFree (L);
+            psFree (M);
+            psFree (dP);
+            psFree (dQ);
+        }
+    }
+    psFree (local);
+    return gradients;
+}
+
+bool pmAstromFitDistortion(pmFPA *fpa, psArray *gradients, double pixelScale)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_ARRAY_NON_NULL(gradients, false);
+
+    psPolynomial2D *localX = NULL;
+    psPolynomial2D *localY = NULL;
+
+    // assign the gradient elements to psVectors for fitting
+    psVector *dPdL = psVectorAlloc (gradients->n, PS_TYPE_F32);
+    psVector *dQdL = psVectorAlloc (gradients->n, PS_TYPE_F32);
+    psVector *dPdM = psVectorAlloc (gradients->n, PS_TYPE_F32);
+    psVector *dQdM = psVectorAlloc (gradients->n, PS_TYPE_F32);
+    psVector *L = psVectorAlloc (gradients->n, PS_TYPE_F32);
+    psVector *M = psVectorAlloc (gradients->n, PS_TYPE_F32);
+
+    for (int i = 0; i < gradients->n; i++) {
+
+        pmAstromGradient *grad = gradients->data[i];
+
+        dPdL->data.F32[i] = grad->dTPdL.x;
+        dQdL->data.F32[i] = grad->dTPdL.y;
+
+        dPdM->data.F32[i] = grad->dTPdM.x;
+        dQdM->data.F32[i] = grad->dTPdM.y;
+
+        L->data.F32[i] = grad->FP.x;
+        M->data.F32[i] = grad->FP.y;
+    }
+
+    // mask and stats structure for measuring the clipping statistic
+    // this analysis has too few data points to use the robust median method
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+    psVector *mask = psVectorAlloc (gradients->n, PS_TYPE_MASK);
+    psVectorInit (mask, 0);
+
+    // the order of the gradient fits need to be 1 less than the distortion term
+    // determine the gradient order(s) from the fpa->toTP structure
+    localX = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, fpa->toTPA->x->nX-1, fpa->toTPA->x->nY);
+    localY = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, fpa->toTPA->x->nX,   fpa->toTPA->x->nY-1);
+
+    // set masks based on fpa->toTPA
+    for (int i = 0; i <= fpa->toTPA->x->nX; i++) {
+        for (int j = 0; j <= fpa->toTPA->x->nY; j++) {
+            if ((i > 0) && (i <= fpa->toTPA->x->nX)) {
+                localX->coeffMask[i-1][j] = fpa->toTPA->x->coeffMask[i][j];
+            }
+            if ((j > 0) && (j <= fpa->toTPA->x->nY)) {
+                localY->coeffMask[i][j-1] = fpa->toTPA->x->coeffMask[i][j];
+            }
+        }
+    }
+
+    // fit the local gradients in each direction
+    if (!psVectorClipFitPolynomial2D (localX, stats, mask, 0xff, dPdL, NULL, L, M)) {
+        psLogMsg ("psastro", 3, "failed to fit x-dir gradient\n");
+        psFree (localX);
+        psFree (localY);
+        goto escape;
+    }
+
+    if (!psVectorClipFitPolynomial2D (localY, stats, mask, 0xff, dPdM, NULL, L, M)) {
+        psLogMsg ("psastro", 3, "failed to fit y-dir gradient\n");
+        psFree (localX);
+        psFree (localY);
+        goto escape;
+    }
+
+    // update fpa->toTP distortion terms
+    fpa->toTPA->x->coeff[0][0] = 0;
+    for (int i = 1; i <= fpa->toTPA->x->nX; i++) {
+        if (fpa->toTPA->x->coeffMask[i][0] & PS_POLY_MASK_SET) {
+            continue;
+        }
+        fpa->toTPA->x->coeff[i][0] = localX->coeff[i-1][0] / i;
+    }
+    for (int j = 1; j <= fpa->toTPA->x->nY; j++) {
+        if (fpa->toTPA->x->coeffMask[0][j] & PS_POLY_MASK_SET) {
+            continue;
+        }
+        fpa->toTPA->x->coeff[0][j] = localY->coeff[0][j-1] / j;
+    }
+    for (int i = 1; i <= fpa->toTPA->x->nX; i++) {
+        for (int j = 1; j <= fpa->toTPA->x->nY; j++) {
+            if (fpa->toTPA->x->coeffMask[i][j] & PS_POLY_MASK_SET) {
+                continue;
+            }
+            fpa->toTPA->x->coeff[i][j] = 0.5*(localX->coeff[i-1][j] / i + localY->coeff[i][j-1] / j);
+        }
+    }
+    fpa->toTPA->x->coeff[1][0] += 1.0;
+    psFree (localX);
+    psFree (localY);
+
+    // the order of the gradient fits need to be 1 less than the distortion term
+    // determine the gradient order(s) from the fpa->toTP structure
+    localX = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, fpa->toTPA->y->nX-1, fpa->toTPA->y->nY);
+    localY = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, fpa->toTPA->y->nX,   fpa->toTPA->y->nY-1);
+
+    // set masks based on fpa->toTP
+    for (int i = 0; i < fpa->toTPA->y->nX; i++) {
+        for (int j = 0; j < fpa->toTPA->y->nY; j++) {
+            if ((i > 0) && (i <= fpa->toTPA->y->nX)) {
+                localX->coeffMask[i-1][j] = fpa->toTPA->y->coeffMask[i][j];
+            }
+            if ((j > 0) && (j <= fpa->toTPA->y->nY)) {
+                localY->coeffMask[i][j-1] = fpa->toTPA->y->coeffMask[i][j];
+            }
+        }
+    }
+
+    // fit the local gradients in each direction
+    psVectorClipFitPolynomial2D (localX, stats, mask, 0xff, dQdL, NULL, L, M);
+    psVectorClipFitPolynomial2D (localY, stats, mask, 0xff, dQdM, NULL, L, M);
+
+    // update fpa->toTP distortion terms
+    fpa->toTPA->y->coeff[0][0] = 0;
+    for (int i = 1; i <= fpa->toTPA->y->nX; i++) {
+        if (fpa->toTPA->y->coeffMask[i][0] & PS_POLY_MASK_SET) {
+            continue;
+        }
+        fpa->toTPA->y->coeff[i][0] = localX->coeff[i-1][0] / i;
+    }
+    for (int j = 1; j <= fpa->toTPA->y->nY; j++) {
+        if (fpa->toTPA->y->coeffMask[0][j] & PS_POLY_MASK_SET) {
+            continue;
+        }
+        fpa->toTPA->y->coeff[0][j] = localY->coeff[0][j-1] / j;
+    }
+    for (int i = 1; i <= fpa->toTPA->y->nX; i++) {
+        for (int j = 1; j <= fpa->toTPA->y->nY; j++) {
+            if (fpa->toTPA->y->coeffMask[i][j] & PS_POLY_MASK_SET) {
+                continue;
+            }
+            fpa->toTPA->y->coeff[i][j] = 0.5*(localX->coeff[i-1][j] / i + localY->coeff[i][j-1] / j);
+        }
+    }
+    fpa->toTPA->y->coeff[0][1] += 1.0;
+    psFree (localX);
+    psFree (localY);
+
+    // free unneeded structures
+    psFree (dPdL);
+    psFree (dPdM);
+    psFree (dQdL);
+    psFree (dQdM);
+    psFree (L);
+    psFree (M);
+    psFree (stats);
+    psFree (mask);
+
+    // reset the fromTPA terms here. choose an appropriate region based on the dimensions of
+    // the complete FPA
+    psRegion *region = pmAstromFPAExtent (fpa);
+
+    psFree (fpa->fromTPA);
+    fpa->fromTPA = psPlaneTransformInvert(NULL, fpa->toTPA, *region, 50);
+    psFree (region);
+
+    if (fpa->fromTPA == NULL) {
+        psError (PS_ERR_UNKNOWN, false, "failed to invert fpa->toTPA\n");
+        return false;
+    }
+
+    return true;
+
+escape:
+    // free unneeded structures
+    psFree (dPdL);
+    psFree (dPdM);
+    psFree (dQdL);
+    psFree (dQdM);
+    psFree (L);
+    psFree (M);
+    psFree (stats);
+    psFree (mask);
+    return false;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryDistortion.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryDistortion.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryDistortion.h	(revision 22322)
@@ -0,0 +1,59 @@
+/* @file  pmAstrometryDistortion.h
+ * @brief This file defines the basic types for measuring and fitting the focal-plane distortion.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-18 22:07:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_ASTROMETRY_DISTORTION_H
+#define PM_ASTROMETRY_DISTORTION_H
+
+/// @addtogroup Astrometry
+/// @{
+
+/* The following data structure carries the information about the residual
+ * gradient of source positions in the tangent plane (pmAstromObj.TP) as a
+ * function of position in the focal plane (pmAstromObj.FP).
+ */
+typedef struct
+{
+    psPlane FP;
+    psPlane dTPdL;
+    psPlane dTPdM;
+}
+pmAstromGradient;
+
+pmAstromGradient *pmAstromGradientAlloc ();
+
+/* The following function determines the position residual, in the tangent
+ * plane, as a function of position in the focal plane, for a collection of raw
+ * measurements and matched reference stars. The configuration data must include
+ * the bin size over which the gradient is measured (keyword: ASTROM.GRAD.BOX).
+ * The function returns an array of pmAstromGradient structures, defined below.
+ */
+psArray *pmAstromMeasureGradients(
+    psArray *gradients,
+    psArray *rawstars,
+    psArray *refstars,
+    psArray *matches,
+    psRegion *region,
+    int Nx, int Ny
+);
+
+/* The gradient set measured above can be fitted with a pair of 2D
+ * polynomials. The resulting fits can then be related back to the implied
+ * polynomials which represent the distortion. The following function performs
+ * the fit and applies the result to the distortion transformation of the
+ * supplied pmFPA structure. The configuration variable supplies the polynomial
+ * order (keyword: ASTROM.DISTORT.ORDER).
+ */
+bool pmAstromFitDistortion(
+    pmFPA *fpa,
+    psArray *gradients,
+    double pixelScale);
+
+/// @}
+#endif // PM_ASTROMETRY_DISTORTION_H
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryModel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryModel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryModel.c	(revision 22322)
@@ -0,0 +1,763 @@
+/** @file  pmAstrometryModel.c
+ *
+ *  @brief Functions to read and write astrometric model
+ *
+ *  The generic model does not specify the location of the boresite on the sky, and it includes
+ *  a model for the rotator and motion of the boresite.
+ *
+ *  @ingroup AstroImage
+ *
+ *  @author EAM, IfA
+ *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-17 23:07:02 $
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include <unistd.h>   // for unlink
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmFPAExtent.h"
+#include "pmFPAfileFitsIO.h"
+#include "pmAstrometryWCS.h"
+#include "pmAstrometryUtils.h"
+#include "pmAstrometryRegions.h"
+#include "pmAstrometryModel.h"
+
+# define REQUIRE(TEST,MESSAGE){ if (!(TEST)) { psAbort (MESSAGE); }}
+
+/********************* CheckDataStatus functions *****************************/
+
+bool pmAstromModelCheckDataStatusForView (const pmFPAview *view, pmFPAfile *file) {
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        bool exists = pmAstromModelCheckDataStatusForFPA (fpa);
+        return exists;
+    }
+    if (view->chip >= fpa->chips->n) {
+        psError(PS_ERR_IO, true, "Requested chip == %d >= fpa->chips->n == %ld", view->chip, fpa->chips->n);
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        bool exists = pmAstromModelCheckDataStatusForChip (chip);
+        return exists;
+    }
+    if (view->cell >= chip->cells->n) {
+        psError(PS_ERR_IO, true, "Requested cell == %d >= chip->cells->n == %ld", view->cell, chip->cells->n);
+        return false;
+    }
+    psError(PS_ERR_IO, false, "Astrometry only valid at the chip level");
+    return false;
+}
+
+bool pmAstromModelCheckDataStatusForFPA (const pmFPA *fpa) {
+
+    if (!fpa->toTPA) return false;
+    if (!fpa->fromTPA) return false;
+    if (!fpa->toSky) return false;
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        if (!chip) continue;
+        if (pmAstromModelCheckDataStatusForChip (chip)) return true;
+    }
+    return false;
+}
+
+bool pmAstromModelCheckDataStatusForChip (const pmChip *chip) {
+
+    if (!chip->toFPA) return false;
+    if (!chip->fromFPA) return false;  // XXX not strictly needed?
+    return true;
+}
+
+/********************* Write Data functions *****************************/
+
+bool pmAstromModelWriteForView (const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    // write the full model in one pass: require the level to be FPA
+    if (view->chip != -1) {
+        psError(PS_ERR_IO, false, "Astrometry must be written at the FPA level");
+        return false;
+    }
+
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
+
+    if (!pmAstromModelWriteFPA(file, fpa)) {
+        psError(PS_ERR_IO, false, "Failed to write Astrometry for fpa");
+        psFree(fpa);
+        return false;
+    }
+
+    psFree(fpa);
+
+    return true;
+}
+
+// write out all chip-level Astrometry data for this FPA
+bool pmAstromModelWriteFPA (pmFPAfile *file, const pmFPA *fpa)
+{
+
+
+    if (!pmAstromModelWritePHU (file, fpa)) {
+        psError(PS_ERR_IO, false, "Failed to write PHU for Astrometry model");
+        return false;
+    }
+
+    if (!pmAstromModelWriteChips (file)) {
+        psError(PS_ERR_IO, false, "Failed to write Astrometry for chips");
+        return false;
+    }
+
+    if (!pmAstromModelWriteFP (file)) {
+        psError(PS_ERR_IO, false, "Failed to write Sky for Astrometry model");
+        return false;
+    }
+
+    if (!pmAstromModelWriteTP (file)) {
+        psError(PS_ERR_IO, false, "Failed to write Sky for Astrometry model");
+        return false;
+    }
+
+    if (!pmAstromModelWriteSky (file)) {
+        psError(PS_ERR_IO, false, "Failed to write Sky for Astrometry model");
+        return false;
+    }
+
+    return true;
+}
+
+bool pmAstromModelWritePHU (pmFPAfile *file, const pmFPA *fpa) {
+    // Need to have an FPA suitable for writing, so that the headers are all kosher
+
+    // output header data
+    psMetadata *outhead = psMetadataAlloc();
+
+    // use the FPA phu to generate the PHU header
+    pmHDU *phu = fpa->hdu;
+
+    // if there is no FPA PHU, this is a single header+image (extension-less) file. This could be
+    // the case for an input SPLIT set of files being written out as a MEF.  if there is a PHU,
+    // write it out as a 'blank'
+    if (phu) {
+        psMetadataCopy (outhead, phu->header);
+    } else {
+        pmConfigConformHeader (outhead, file->format);
+    }
+
+    psMetadataAddBool (outhead, PS_LIST_TAIL, "EXTEND", PS_META_REPLACE, "this file has extensions", true);
+    psFitsWriteBlank (file->fits, outhead, "");
+    file->wrote_phu = true;
+
+    psTrace ("pmFPAfile", 5, "wrote phu %s (type: %d)\n", file->filename, file->type);
+    psFree (outhead);
+
+    return true;
+}
+
+// fourth layer holds the chips
+bool pmAstromModelWriteChips (pmFPAfile *file) {
+
+    psMetadata *header = psMetadataAlloc();
+    psMetadataAddStr(header, PS_LIST_TAIL, "COORD",    PS_META_REPLACE, "name of this layer",   "CHIPS");
+    psMetadataAddStr(header, PS_LIST_TAIL, "PARENT",   PS_META_REPLACE, "next layer up",        "FOCAL_PLANE");
+    psMetadataAddStr(header, PS_LIST_TAIL, "BOUNDARY", PS_META_REPLACE, "validity region",      "RECTANGLE");
+    psMetadataAddStr(header, PS_LIST_TAIL, "TRANSFRM", PS_META_REPLACE, "mapping to parent",    "POLYNOMIAL");
+
+    psArray *model = psArrayAllocEmpty (1);
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    pmChip *chip = NULL;
+    while ((chip = pmFPAviewNextChip (view, file->fpa, 1)) != NULL) {
+
+        if (!chip->toFPA) continue;
+        assert (chip->toFPA->x);
+        assert (chip->toFPA->y);
+
+        psRegion *region = pmChipPixels (chip);
+
+        // set the chip name
+        char *chiprule = psStringCopy ("{CHIP.NAME}");
+        char *chipname = pmFPAfileNameFromRule (chiprule, file, view);
+
+        for (int i = 0; i <= chip->toFPA->x->nX; i++) {
+            for (int j = 0; j <= chip->toFPA->x->nY; j++) {
+                psMetadata *row = psMetadataAlloc ();
+
+                psMetadataAddStr(row,    PS_LIST_TAIL, "SEGMENT",  PS_META_REPLACE, "name of this segment", chipname);
+                psMetadataAddStr(row,    PS_LIST_TAIL, "PARENT",   PS_META_REPLACE, "next layer up",        "FOCAL_PLANE");
+                psMetadataAddF32(row,    PS_LIST_TAIL, "MINX",     PS_META_REPLACE, "range", region->x0);
+                psMetadataAddF32(row,    PS_LIST_TAIL, "MAXX",     PS_META_REPLACE, "range", region->x1);
+                psMetadataAddF32(row,    PS_LIST_TAIL, "MINY",     PS_META_REPLACE, "range", region->y0);
+                psMetadataAddF32(row,    PS_LIST_TAIL, "MAXY",     PS_META_REPLACE, "range", region->y1);
+
+                psMetadataAddS32(row,    PS_LIST_TAIL, "XORDER",   PS_META_REPLACE, "", i);
+                psMetadataAddS32(row,    PS_LIST_TAIL, "YORDER",   PS_META_REPLACE, "", j);
+                psMetadataAddS32(row,    PS_LIST_TAIL, "NXORDER",  PS_META_REPLACE, "", chip->toFPA->x->nX);
+                psMetadataAddS32(row,    PS_LIST_TAIL, "NYORDER",  PS_META_REPLACE, "", chip->toFPA->x->nY);
+                psMetadataAddF32(row,    PS_LIST_TAIL, "POLY_X",   PS_META_REPLACE, "", chip->toFPA->x->coeff[i][j]);
+                psMetadataAddF32(row,    PS_LIST_TAIL, "POLY_Y",   PS_META_REPLACE, "", chip->toFPA->y->coeff[i][j]);
+                psMetadataAddF32(row,    PS_LIST_TAIL, "ERROR_X",  PS_META_REPLACE, "", chip->toFPA->x->coeffErr[i][j]);
+                psMetadataAddF32(row,    PS_LIST_TAIL, "ERROR_Y",  PS_META_REPLACE, "", chip->toFPA->y->coeffErr[i][j]);
+                psMetadataAddU8 (row,    PS_LIST_TAIL, "MASK_X",   PS_META_REPLACE, "", chip->toFPA->x->coeffMask[i][j]);
+                psMetadataAddU8 (row,    PS_LIST_TAIL, "MASK_Y",   PS_META_REPLACE, "", chip->toFPA->y->coeffMask[i][j]);
+                psArrayAdd (model, 100, row);
+                psFree (row);
+            }
+        }
+        psFree (chiprule);
+        psFree (chipname);
+        psFree (region);
+    }
+
+    if (!psFitsWriteTable (file->fits, header, model, "CHIPS")) {
+        psError(PS_ERR_IO, false, "writing sky data\n");
+        psFree(model);
+        return false;
+    }
+
+    psFree (view);
+    psFree (model);
+    psFree (header);
+    return true;
+}
+
+// third layer is the focal plane
+bool pmAstromModelWriteFP (pmFPAfile *file) {
+
+    psMetadata *header = psMetadataAlloc();
+    psMetadataAddStr(header, PS_LIST_TAIL, "COORD",    PS_META_REPLACE, "name of this layer",   "FOCAL_PLANE");
+    psMetadataAddStr(header, PS_LIST_TAIL, "PARENT",   PS_META_REPLACE, "next layer up",        "TANGENT_PLANE");
+    psMetadataAddStr(header, PS_LIST_TAIL, "BOUNDARY", PS_META_REPLACE, "validity region",      "RECTANGLE");
+    psMetadataAddStr(header, PS_LIST_TAIL, "TRANSFRM", PS_META_REPLACE, "mapping to parent",    "POLYNOMIAL");
+
+    psArray *model = psArrayAllocEmpty (1);
+
+    // region over which the fromTPA projection is valid
+    psRegion *region = pmAstromFPInTP (file->fpa);
+
+    psPlaneTransform *toTPA   = file->fpa->toTPA;
+
+    for (int i = 0; i <= toTPA->x->nX; i++) {
+        for (int j = 0; j <= toTPA->x->nY; j++) {
+            psMetadata *row = psMetadataAlloc ();
+            psMetadataAddStr(row,    PS_LIST_TAIL, "SEGMENT",  PS_META_REPLACE, "name of this segment", "FOCAL_PLANE");
+            psMetadataAddStr(row,    PS_LIST_TAIL, "PARENT",   PS_META_REPLACE, "next layer up",        "TANGENT_PLANE");
+            psMetadataAddF32(row,    PS_LIST_TAIL, "MINX",     PS_META_REPLACE, "range", region->x0);
+            psMetadataAddF32(row,    PS_LIST_TAIL, "MAXX",     PS_META_REPLACE, "range", region->x1);
+            psMetadataAddF32(row,    PS_LIST_TAIL, "MINY",     PS_META_REPLACE, "range", region->y0);
+            psMetadataAddF32(row,    PS_LIST_TAIL, "MAXY",     PS_META_REPLACE, "range", region->y1);
+
+            psMetadataAddS32(row,    PS_LIST_TAIL, "XORDER",   PS_META_REPLACE, "", i);
+            psMetadataAddS32(row,    PS_LIST_TAIL, "YORDER",   PS_META_REPLACE, "", j);
+            psMetadataAddS32(row,    PS_LIST_TAIL, "NXORDER",  PS_META_REPLACE, "", toTPA->x->nX);
+            psMetadataAddS32(row,    PS_LIST_TAIL, "NYORDER",  PS_META_REPLACE, "", toTPA->x->nY);
+            psMetadataAddF32(row,    PS_LIST_TAIL, "POLY_X",   PS_META_REPLACE, "", toTPA->x->coeff[i][j]);
+            psMetadataAddF32(row,    PS_LIST_TAIL, "POLY_Y",   PS_META_REPLACE, "", toTPA->y->coeff[i][j]);
+            psMetadataAddF32(row,    PS_LIST_TAIL, "ERROR_X",  PS_META_REPLACE, "", toTPA->x->coeffErr[i][j]);
+            psMetadataAddF32(row,    PS_LIST_TAIL, "ERROR_Y",  PS_META_REPLACE, "", toTPA->y->coeffErr[i][j]);
+            psMetadataAddU8 (row,    PS_LIST_TAIL, "MASK_X",   PS_META_REPLACE, "", toTPA->x->coeffMask[i][j]);
+            psMetadataAddU8 (row,    PS_LIST_TAIL, "MASK_Y",   PS_META_REPLACE, "", toTPA->y->coeffMask[i][j]);
+
+            psArrayAdd (model, 100, row);
+            psFree (row);
+        }
+    }
+
+    if (!psFitsWriteTable (file->fits, header, model, "FP")) {
+        psError(PS_ERR_IO, false, "writing sky data\n");
+        psFree(model);
+        psFree (header);
+        psFree (region);
+        return false;
+    }
+
+    psFree (model);
+    psFree (header);
+    psFree (region);
+    return true;
+}
+
+// second layer is the tangent plane
+bool pmAstromModelWriteTP (pmFPAfile *file) {
+
+    bool status;
+
+    // get the boresite model parameters.  these track the position of the boresite
+    // as a function of the rotator angle
+    float Xo = psMetadataLookupF32 (&status, file->fpa->concepts, "FPA.BORE.X0");
+    float Yo = psMetadataLookupF32 (&status, file->fpa->concepts, "FPA.BORE.Y0");
+    float RX = psMetadataLookupF32 (&status, file->fpa->concepts, "FPA.BORE.RX");
+    float RY = psMetadataLookupF32 (&status, file->fpa->concepts, "FPA.BORE.RY");
+    float To = psMetadataLookupF32 (&status, file->fpa->concepts, "FPA.BORE.T0");
+    float Po = psMetadataLookupF32 (&status, file->fpa->concepts, "FPA.BORE.P0");
+
+    // the PosZero is the offset between the reported and actual POSANGLE values
+    float PosZero = psMetadataLookupF32 (&status, file->fpa->concepts, "FPA.POS_ZERO");  /// XXX be consistent with degrees v radians
+    char *refChip = psMetadataLookupStr (&status, file->fpa->concepts, "FPA.REF.CHIP");
+
+    psMetadata *header = psMetadataAlloc();
+    psMetadataAddStr(header, PS_LIST_TAIL, "COORD",    PS_META_REPLACE, "name of this layer",   "TANGENT_PLANE");
+    psMetadataAddStr(header, PS_LIST_TAIL, "PARENT",   PS_META_REPLACE, "next layer up",        "SKY");
+    psMetadataAddStr(header, PS_LIST_TAIL, "BOUNDARY", PS_META_REPLACE, "validity region",      "RECTANGLE");
+    psMetadataAddStr(header, PS_LIST_TAIL, "TRANSFRM", PS_META_REPLACE, "mapping to parent",    "PROJECTION");
+
+    psArray *model = psArrayAllocEmpty (1);
+    psMetadata *row = psMetadataAlloc ();
+    psMetadataAddStr(row,    PS_LIST_TAIL, "SEGMENT",  PS_META_REPLACE, "name of this segment", "TANGENT_PLANE");
+    psMetadataAddStr(row,    PS_LIST_TAIL, "PARENT",   PS_META_REPLACE, "next layer up",        "SKY");
+
+    psRegion *region = pmAstromFPAExtent (file->fpa);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "MINX",     PS_META_REPLACE, "range", region->x0);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "MAXX",     PS_META_REPLACE, "range", region->x1);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "MINY",     PS_META_REPLACE, "range", region->y0);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "MAXY",     PS_META_REPLACE, "range", region->y1);
+
+    psMetadataAddF32(row,    PS_LIST_TAIL, "XSCALE",   PS_META_REPLACE, "", file->fpa->toSky->Xs * PS_DEG_RAD);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "YSCALE",   PS_META_REPLACE, "", file->fpa->toSky->Ys * PS_DEG_RAD);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "BORE_X0",  PS_META_REPLACE, "boresite parameter", Xo);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "BORE_Y0",  PS_META_REPLACE, "boresite parameter", Yo);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "BORE_RX",  PS_META_REPLACE, "boresite parameter", RX);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "BORE_RY",  PS_META_REPLACE, "boresite parameter", RY);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "BORE_T0",  PS_META_REPLACE, "boresite parameter", To);
+    psMetadataAddF32(row,    PS_LIST_TAIL, "BORE_P0",  PS_META_REPLACE, "boresite parameter", Po);
+
+    psMetadataAddF32(row,    PS_LIST_TAIL, "POS_ZERO", PS_META_REPLACE, "POSANGLE offset (degrees)", PosZero);
+    psMetadataAddStr(row,    PS_LIST_TAIL, "REF_CHIP", PS_META_REPLACE, "reference chip for model", refChip);
+
+    psArrayAdd (model, 100, row);
+    psFree (row);
+
+    if (!psFitsWriteTable (file->fits, header, model, "TP")) {
+        psError(PS_ERR_IO, false, "writing sky data\n");
+        psFree (region);
+        psFree (model);
+        psFree (header);
+        return false;
+    }
+
+    psFree (region);
+    psFree (model);
+    psFree (header);
+    return (true);
+}
+
+// first layer is the sky
+bool pmAstromModelWriteSky (pmFPAfile *file) {
+
+    psMetadata *header = psMetadataAlloc();
+    psMetadataAddStr(header, PS_LIST_TAIL, "COORD",    PS_META_REPLACE, "name of this layer",   "SKY");
+    psMetadataAddStr(header, PS_LIST_TAIL, "PARENT",   PS_META_REPLACE, "next layer up",        "NONE");
+    psMetadataAddStr(header, PS_LIST_TAIL, "BOUNDARY", PS_META_REPLACE, "validity region",      "NONE");
+    psMetadataAddStr(header, PS_LIST_TAIL, "TRANSFRM", PS_META_REPLACE, "mapping to parent",    "NONE");
+
+    psArray *model = psArrayAllocEmpty (1);
+    psMetadata *row = psMetadataAlloc ();
+    psMetadataAddStr(row,    PS_LIST_TAIL, "SEGMENT",  PS_META_REPLACE, "name of this segment", "SKY");
+    psMetadataAddStr(row,    PS_LIST_TAIL, "PARENT",   PS_META_REPLACE, "next layer up",        "NONE");
+
+    psArrayAdd (model, 100, row);
+    psFree (row);
+
+    if (!psFitsWriteTable (file->fits, header, model, "SKY")) {
+        psError(PS_ERR_IO, false, "writing sky data\n");
+        psFree(model);
+        return false;
+    }
+
+    psFree (model);
+    psFree (header);
+    return (true);
+}
+
+/********************* Read Data functions *****************************/
+
+bool pmAstromModelReadForView (const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+    {
+
+        // write the full model in one pass: require the level to be FPA
+        if (view->chip != -1) {
+            psError(PS_ERR_IO, false, "Astrometry must be read at the FPA level");
+            return false;
+        }
+
+        if (!pmAstromModelReadFPA (file)) {
+            psError(PS_ERR_IO, false, "Failed to read Astrometry for fpa");
+            return false;
+        }
+        return true;
+    }
+
+// read out all chip-level Astrometry data for this FPA
+bool pmAstromModelReadFPA (pmFPAfile *file) {
+
+    if (!pmAstromModelReadPHU (file)) {
+        psError(PS_ERR_IO, false, "Failed to read PHU for Astrometry model");
+        return false;
+    }
+
+    if (!pmAstromModelReadChips (file)) {
+        psError(PS_ERR_IO, false, "Failed to read Astrometry for chips");
+        return false;
+    }
+
+    if (!pmAstromModelReadFP (file)) {
+        psError(PS_ERR_IO, false, "Failed to read Sky for Astrometry model");
+        return false;
+    }
+
+    // NOTE : TP must come after FP as it applies the POS, ROT boresite corrections to the
+    // transformation determined in FP
+    if (!pmAstromModelReadTP (file)) {
+        psError(PS_ERR_IO, false, "Failed to read Sky for Astrometry model");
+        return false;
+    }
+
+    if (!pmAstromModelReadSky (file)) {
+        psError(PS_ERR_IO, false, "Failed to read Sky for Astrometry model");
+        return false;
+    }
+
+    return true;
+}
+
+bool pmAstromModelReadPHU (pmFPAfile *file) {
+
+    // not necessary to read the PHU
+    return true;
+}
+
+int pmConceptsChipNumberFromName (pmFPA *fpa, char *name) {
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        if (!chip) continue;
+        char *thisone = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
+        if (!thisone) continue;
+        if (!strcmp (name, thisone)) return (i);
+    }
+    return -1;
+}
+
+pmChip *pmConceptsChipFromName (pmFPA *fpa, char *name) {
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        if (!chip) continue;
+        char *thisone = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
+        if (!thisone) continue;
+        if (!strcmp (name, thisone)) return (chip);
+    }
+    return NULL;
+}
+
+// first layer converts Chip to Focal Plane
+bool pmAstromModelReadChips (pmFPAfile *file) {
+
+    bool status;
+
+    // set FITS cursor
+    if (!psFitsMoveExtName (file->fits, "CHIPS")) {
+        psError(PS_ERR_IO, false, "missing CHIPS extension in astrometry model\n");
+        return false;
+    }
+
+    // free exising tranformations in prep for new alloc below
+    for (int i = 0; i < file->fpa->chips->n; i++) {
+        pmChip *chip = file->fpa->chips->data[i];
+        psFree (chip->toFPA);
+        chip->toFPA = NULL;
+    }
+
+    // load the header
+    psMetadata *header = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!header) psAbort("cannot read model header");
+
+    // load the full model in one shot
+    psArray *model = psFitsReadTable (file->fits);
+    if (!model) psAbort("cannot read model");
+    psLogMsg ("psModules.astrom", 4, "read %ld rows from FP\n", model->n);
+
+    // parse the model entries
+    for (int i = 0; i < model->n; i++) {
+        psMetadata *row = model->data[i];
+
+        // name of the chip for this row.
+        char *chipname = psMetadataLookupStr (&status, row, "SEGMENT");
+
+        // get chip from name
+        pmChip *chip = pmConceptsChipFromName (file->fpa, chipname);
+        REQUIRE (chip, "invalid chip name");
+
+        // define the toFPA transform if not already defined
+        int nX = psMetadataLookupS32(&status, row, "NXORDER"); REQUIRE (status, "missing NXORDER");
+        int nY = psMetadataLookupS32(&status, row, "NYORDER"); REQUIRE (status, "missing NYORDER");
+        if (chip->toFPA == NULL) {
+            chip->toFPA = psPlaneTransformAlloc(nX, nY);
+        } else {
+            REQUIRE (chip->toFPA->x->nX == nX, "mismatch in chip order");
+            REQUIRE (chip->toFPA->x->nY == nY, "mismatch in chip order");
+            REQUIRE (chip->toFPA->y->nX == nX, "mismatch in chip order");
+            REQUIRE (chip->toFPA->y->nY == nY, "mismatch in chip order");
+        }
+
+        int ix = psMetadataLookupS32(&status, row, "XORDER");  REQUIRE (status, "missing XORDER");
+        int iy = psMetadataLookupS32(&status, row, "YORDER");  REQUIRE (status, "missing YORDER");
+
+        chip->toFPA->x->coeff[ix][iy]    = psMetadataLookupF32(&status, row, "POLY_X");
+        chip->toFPA->y->coeff[ix][iy]    = psMetadataLookupF32(&status, row, "POLY_Y");
+        chip->toFPA->x->coeffErr[ix][iy] = psMetadataLookupF32(&status, row, "ERROR_X");
+        chip->toFPA->y->coeffErr[ix][iy] = psMetadataLookupF32(&status, row, "ERROR_Y");
+        chip->toFPA->x->coeffMask[ix][iy] = psMetadataLookupU8(&status, row, "MASK_X");
+        chip->toFPA->y->coeffMask[ix][iy] = psMetadataLookupU8(&status, row, "MASK_Y");
+    }
+
+    // convert the toFPA transfomations to fromFPA transformations
+    for (int i = 0; i < file->fpa->chips->n; i++) {
+        pmChip *chip = file->fpa->chips->data[i];
+        if (!chip->toFPA) continue;
+        psRegion *region = pmChipPixels (chip);
+        psFree (chip->fromFPA);
+        chip->fromFPA = psPlaneTransformInvert(NULL, chip->toFPA, *region, 50);
+        psFree (region);
+    }
+
+    psFree (model);
+    psFree (header);
+    return true;
+}
+
+// second layer converts Focal Plane to Tangent Plane (unrotated)
+bool pmAstromModelReadFP (pmFPAfile *file) {
+
+    bool status;
+
+    if (!psFitsMoveExtName (file->fits, "FP")) {
+        psError(PS_ERR_IO, false, "missing FP extension in astrometry model\n");
+        return false;
+    }
+
+    psMetadata *header = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!header) psAbort("cannot read model header");
+
+    // free the old
+    psFree (file->fpa->toTPA);
+    file->fpa->toTPA = NULL;
+
+    // read the complete model data at one shot
+    psArray *model = psFitsReadTable (file->fits);
+    psLogMsg ("psModules.astrom", 4, "read %ld rows from FP\n", model->n);
+
+    // parse the model
+    for (int i = 0; i < model->n; i++) {
+        psMetadata *row = model->data[i];
+
+        // there is only one transformation in this model; the order is defined in the header
+        int nX = psMetadataLookupS32(&status, row, "NXORDER"); REQUIRE (status, "missing NXORDER");
+        int nY = psMetadataLookupS32(&status, row, "NYORDER"); REQUIRE (status, "missing NYORDER");
+        if (file->fpa->toTPA == NULL) {
+            // allocate the new transformation
+            file->fpa->toTPA = psPlaneTransformAlloc(nX, nY);
+        } else {
+            REQUIRE (file->fpa->toTPA->x->nX == nX, "mismatch in chip order");
+            REQUIRE (file->fpa->toTPA->x->nY == nY, "mismatch in chip order");
+            REQUIRE (file->fpa->toTPA->y->nX == nX, "mismatch in chip order");
+            REQUIRE (file->fpa->toTPA->y->nY == nY, "mismatch in chip order");
+        }
+
+        int ix = psMetadataLookupS32(&status, row, "XORDER"); REQUIRE (status, "missing XORDER");
+        int iy = psMetadataLookupS32(&status, row, "YORDER"); REQUIRE (status, "missing YORDER");
+        file->fpa->toTPA->x->coeff[ix][iy]     = psMetadataLookupF32(&status, row, "POLY_X");  REQUIRE (status, "missing POLY_X");
+        file->fpa->toTPA->y->coeff[ix][iy]     = psMetadataLookupF32(&status, row, "POLY_Y");  REQUIRE (status, "missing POLY_Y");
+        file->fpa->toTPA->x->coeffErr[ix][iy]  = psMetadataLookupF32(&status, row, "ERROR_X"); REQUIRE (status, "missing ERROR_X");
+        file->fpa->toTPA->y->coeffErr[ix][iy]  = psMetadataLookupF32(&status, row, "ERROR_Y"); REQUIRE (status, "missing ERROR_Y");
+        file->fpa->toTPA->x->coeffMask[ix][iy] = psMetadataLookupU8 (&status, row, "MASK_X");  REQUIRE (status, "missing MASK_X");
+        file->fpa->toTPA->y->coeffMask[ix][iy] = psMetadataLookupU8 (&status, row, "MASK_Y");  REQUIRE (status, "missing MASK_Y");
+    }
+
+    psRegion *region = pmAstromFPAExtent (file->fpa);
+    psFree (file->fpa->fromTPA);
+    file->fpa->fromTPA = psPlaneTransformInvert(NULL, file->fpa->toTPA, *region, 50);
+
+    psFree (model);
+    psFree (header);
+    psFree (region);
+    return true;
+}
+
+# define TRANSFER(TO,FROM,NAME) { \
+    psMetadataItem *item = psMetadataLookup(FROM,NAME); \
+    if (!item) psAbort ("cannot find %s", NAME); \
+    psMetadataItem *newItem = psMetadataItemCopy(item); \
+    if (!psMetadataAddItem(TO, newItem, PS_LIST_TAIL, PS_META_REPLACE)) { \
+        psAbort ("cannot copy %s", NAME); \
+    } \
+    psFree (newItem); }
+
+// third layer applies boresite corrections and converts tangent plane to sky
+bool pmAstromModelReadTP (pmFPAfile *file) {
+
+    if (!psFitsMoveExtName (file->fits, "TP")) {
+        psError(PS_ERR_IO, false, "missing TP extension in astrometry model\n");
+        return false;
+    }
+
+    psMetadata *header = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!header) psAbort("cannot read model header");
+
+    psArray *model = psFitsReadTable (file->fits);
+    psLogMsg ("psModules.astrom", 4, "read %ld rows from TP\n", model->n);
+    if (model->n != 1) psAbort("invalid number of rows in TP model (%ld)", model->n);
+
+    psMetadata *row = model->data[0];
+
+    // move needed items to the concepts
+    TRANSFER (file->fpa->concepts, row, "XSCALE");
+    TRANSFER (file->fpa->concepts, row, "XSCALE");
+    TRANSFER (file->fpa->concepts, row, "YSCALE");
+    TRANSFER (file->fpa->concepts, row, "BORE_X0");
+    TRANSFER (file->fpa->concepts, row, "BORE_Y0");
+    TRANSFER (file->fpa->concepts, row, "BORE_RX");
+    TRANSFER (file->fpa->concepts, row, "BORE_RY");
+    TRANSFER (file->fpa->concepts, row, "BORE_T0");
+    TRANSFER (file->fpa->concepts, row, "BORE_P0");
+    TRANSFER (file->fpa->concepts, row, "POS_ZERO");
+    TRANSFER (file->fpa->concepts, row, "REF_CHIP");
+
+    psFree (model);
+    psFree (header);
+    return (true);
+}
+
+// first layer is the sky
+bool pmAstromModelReadSky (pmFPAfile *file) {
+
+    if (!psFitsMoveExtName (file->fits, "SKY")) {
+        psError(PS_ERR_IO, false, "missing SKY extension in astrometry model\n");
+        return false;
+    }
+
+    psMetadata *header = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!header) psAbort("cannot read model header");
+
+    psArray *model = psFitsReadTable (file->fits);
+    psLogMsg ("psModules.astrom", 4, "read %ld rows from SKY\n", model->n);
+    if (model->n != 1) psAbort("invalid number of rows in SKY model (%ld)", model->n);
+
+    // XXX not much information of interest in this table...
+
+    // generate a template projection for comparisons
+    psFree (file->fpa->toSky);
+    file->fpa->toSky = psProjectionAlloc (0.0, 0.0, PS_RAD_DEG/3600.0, PS_RAD_DEG/3600.0, PS_PROJ_DIS);
+
+    psFree (model);
+    psFree (header);
+    return (true);
+}
+
+// third layer applies boresite corrections and converts tangent plane to sky
+bool pmAstromModelSetTP (pmFPAfile *file, psMetadata *concepts) {
+
+    bool status;
+
+    // these externally supplied values are used to set the final transformation terms
+    double RA  = psMetadataLookupF64 (&status, concepts, "FPA.RA"); REQUIRE (status, "missing FPA.RA");
+    double DEC = psMetadataLookupF64 (&status, concepts, "FPA.DEC"); REQUIRE (status, "missing FPA.DEC");
+    double POS = PM_RAD_DEG * psMetadataLookupF64 (&status, concepts, "FPA.POSANGLE"); REQUIRE (status, "missing FPA.POSANGLE");
+
+    // get projection scale; center is supplied
+    float Xs = psMetadataLookupF32(&status, file->fpa->concepts, "XSCALE") * PM_RAD_DEG; REQUIRE (status, "missing XSCALE");
+    float Ys = psMetadataLookupF32(&status, file->fpa->concepts, "YSCALE") * PM_RAD_DEG; REQUIRE (status, "missing YSCALE");
+
+    // allocate a new toSky projection using the reported position
+    psFree (file->fpa->toSky);
+    file->fpa->toSky = psProjectionAlloc (RA, DEC, Xs, Ys, PS_PROJ_DIS);
+
+    // get boresite correction terms.  RX,RY,To,Po define an ellipse along which the boresite travels
+    double Xo = psMetadataLookupF32(&status, file->fpa->concepts, "BORE_X0"); REQUIRE (status, "missing ");
+    double Yo = psMetadataLookupF32(&status, file->fpa->concepts, "BORE_Y0"); REQUIRE (status, "missing ");
+    double RX = psMetadataLookupF32(&status, file->fpa->concepts, "BORE_RX"); REQUIRE (status, "missing ");
+    double RY = psMetadataLookupF32(&status, file->fpa->concepts, "BORE_RY"); REQUIRE (status, "missing ");
+    double To = psMetadataLookupF32(&status, file->fpa->concepts, "BORE_T0"); REQUIRE (status, "missing ");
+    double Po = psMetadataLookupF32(&status, file->fpa->concepts, "BORE_P0"); REQUIRE (status, "missing ");
+
+    // the true rotation of the instrument is POSANGLE - POS_ZERO
+    double PosZero = PM_RAD_DEG * psMetadataLookupF32(&status, file->fpa->concepts, "POS_ZERO"); REQUIRE (status, "missing POS_ZERO");
+    char *refChip  = psMetadataLookupStr(&status, file->fpa->concepts, "REF_CHIP"); REQUIRE (status, "missing REF_CHIP");
+
+    // XXX we've swapped the sign and parity of POS (add to model)
+    // apply true posangle = -(POS - POS_ZERO)
+    psLogMsg ("psModules.astrom", 4, "Position Angle: %f, Model Position Angle Zero Point: %f\n", POS, PosZero);
+    psPlaneTransform *fromTPA = psPlaneTransformRotate (NULL, file->fpa->fromTPA, (PosZero - POS));
+    psFree (file->fpa->fromTPA);
+    file->fpa->fromTPA = fromTPA;
+
+    psRegion *region = pmAstromFPAExtent (file->fpa);
+    psFree (file->fpa->toTPA);
+    file->fpa->toTPA = psPlaneTransformInvert(NULL, file->fpa->fromTPA, *region, 50);
+    psFree (region);
+
+    // current position of the nominal boresite in refChip coordinates
+    double X = Xo + RX*cos(POS - To)*cos(Po) + RY*sin(POS - To)*sin(Po);
+    double Y = Yo + RY*sin(POS - To)*cos(Po) - RX*cos(POS - To)*sin(Po);
+    psLogMsg ("psModules.astrom", 4, "Boresite coords on reference chip: %f, %f\n", X, Y);
+
+    // get reference chip from name
+    pmChip *chip = pmConceptsChipFromName (file->fpa, refChip);
+    if (!chip) psAbort ("invalid chip name for reference");
+
+    psPlane *boreCH = psPlaneAlloc();
+    psPlane *boreFP = psPlaneAlloc();
+    psPlane *boreTP = psPlaneAlloc();
+    psSphere *boreSky = psSphereAlloc();
+
+    // find the FP coord of the reported boresite location
+    boreCH->x = X;
+    boreCH->y = Y;
+    psPlaneTransformApply (boreFP, chip->toFPA, boreCH);
+
+    // find the true RA,DEC coord of the mirror of the reported boresite FP location
+    boreFP->x = -boreFP->x;
+    boreFP->y = -boreFP->y;
+    psPlaneTransformApply (boreTP, file->fpa->toTPA, boreFP);
+    psDeproject (boreSky, boreTP, file->fpa->toSky);
+
+    // modify the projection to account for offset between true and reported boresite
+    file->fpa->toSky->R = boreSky->r;
+    file->fpa->toSky->D = boreSky->d;
+
+    psTrace ("psModules.astrom", 5, "actual boresite coordinates: %lf, %lf\n", file->fpa->toSky->R*PS_DEG_RAD, file->fpa->toSky->D*PS_DEG_RAD);
+    psTrace ("psModules.astrom", 5, "plate scale used: %lf, %lf\n", file->fpa->toSky->Xs*PS_DEG_RAD*3600.0, file->fpa->toSky->Ys*PS_DEG_RAD*3600.0);
+
+    psFree (boreCH);
+    psFree (boreFP);
+    psFree (boreTP);
+    psFree (boreSky);
+
+    return (true);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryModel.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryModel.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryModel.h	(revision 22322)
@@ -0,0 +1,43 @@
+/* @file  pmAstrometryModel.h
+ * @brief Astrometry model I/O functions
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-07-17 22:38:15 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_ASTROMETRY_MODEL_H
+#define PM_ASTROMETRY_MODEL_H
+
+/// @addtogroup Astrometry
+/// @{
+
+bool pmAstromModelCheckDataStatusForView (const pmFPAview *view, pmFPAfile *file);
+bool pmAstromModelCheckDataStatusForFPA (const pmFPA *fpa);
+bool pmAstromModelCheckDataStatusForChip (const pmChip *chip);
+
+bool pmAstromModelWriteForView (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmAstromModelWriteFPA (pmFPAfile *file, const pmFPA *fpa);
+bool pmAstromModelWritePHU (pmFPAfile *file, const pmFPA *fpa);
+bool pmAstromModelWriteSky (pmFPAfile *file);
+bool pmAstromModelWriteTP (pmFPAfile *file);
+bool pmAstromModelWriteFP (pmFPAfile *file);
+bool pmAstromModelWriteChips (pmFPAfile *file);
+
+int pmConceptsChipNumberFromName (pmFPA *fpa, char *name);
+pmChip *pmConceptsChipFromName (pmFPA *fpa, char *name);
+
+bool pmAstromModelReadForView (const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmAstromModelReadFPA (pmFPAfile *file);
+bool pmAstromModelReadPHU (pmFPAfile *file);
+bool pmAstromModelReadChips (pmFPAfile *file);
+bool pmAstromModelReadFP (pmFPAfile *file);
+bool pmAstromModelReadTP (pmFPAfile *file);
+bool pmAstromModelReadSky (pmFPAfile *file);
+
+bool pmAstromModelSetTP (pmFPAfile *file, psMetadata *concepts);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryObjects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryObjects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryObjects.c	(revision 22322)
@@ -0,0 +1,929 @@
+/** @file  pmAstrometryObjects.c
+*
+*  @brief This file defines the basic types for matching objects
+*  based on their astrometry.
+*
+*  @ingroup AstroImage
+*
+*  @author EAM, IfA
+*
+*  @version $Revision: 1.39 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2008-04-11 07:42:05 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include <unistd.h>   // for unlink
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmAstrometryObjects.h"
+
+#define PM_ASTROMETRYOBJECTS_DEBUG 1
+/******************************************************************************
+pmAstromObjSortByMag(**a, **b): sort by mag (descending)
+
+Is this a private routine?
+Should we do the early asserts?
+ ******************************************************************************/
+int pmAstromObjSortByMag(
+    const void **a,
+    const void **b)
+{
+    if (PM_ASTROMETRYOBJECTS_DEBUG) {
+        PS_ASSERT_PTR_NON_NULL(a, 0);
+        PS_ASSERT_PTR_NON_NULL(*a, 0);
+        PS_ASSERT_PTR_NON_NULL(b, 0);
+        PS_ASSERT_PTR_NON_NULL(*b, 0);
+    }
+
+    pmAstromObj *A = *(pmAstromObj **)a;
+    pmAstromObj *B = *(pmAstromObj **)b;
+
+    psF32 diff = A->Mag - B->Mag;
+    if (diff > FLT_EPSILON) {
+        return (-1);
+    }
+
+    if (diff < FLT_EPSILON) {
+        return (+1);
+    }
+
+    return (0);
+}
+
+/************************************************************************************************************/
+/*
+ * Working routine to match two lists (where x[12] are sorted), given psVectors of their coordinates and the
+ * permutation used to sort in x
+ */
+static psArray *match_lists(const psVector *x1, const psVector *y1, // x/y coordinates of first set of objects
+                            const psVector *x2, const psVector *y2, // x/y   "    "    "  second "   "  "   "
+                            const psVector *sorted1, const psVector *sorted2, // mapping to original order
+                            const double RADIUS) // matching radius
+{
+    psArray *matches = psArrayAllocEmpty(x1->n);
+
+    const double RADIUS_SQR = PS_SQR(RADIUS);
+    double dX, dY, dR;
+
+    int jStart;
+    int i = 0, j = 0;
+    while (i < x1->n && j < x2->n) {
+        dX = x1->data.F64[i] - x2->data.F64[j];
+        if (dX <= -RADIUS) {
+            i++;
+            continue;
+        }
+        if (dX >= +RADIUS) {
+            j++;
+            continue;
+        }
+
+        jStart = j;
+        while (fabs(dX) < RADIUS && j < x2->n) {
+
+            dX = x1->data.F64[i] - x2->data.F64[j];
+            dY = y1->data.F64[i] - y2->data.F64[j];
+            dR = dX*dX + dY*dY;
+
+            if (dR > RADIUS_SQR) {
+                j++;
+                continue;
+            }
+
+            // got a match; add to output list
+            pmAstromMatch *match = pmAstromMatchAlloc (sorted1->data.S32[i], sorted2->data.S32[j]);
+            psArrayAdd (matches, 100, match);
+            psFree (match);
+
+            j++;
+        }
+        j = jStart;
+        i++;
+    }
+
+    return (matches);
+}
+
+/************************************************************************************************************/
+// macro to generate code for radius match function based on desired member
+// radius is in units of matching member (eg, pixels for chip, microns for FP, etc)
+#define MAKE_ASTROM_RADIUS(FUNC, MEMBER) \
+psArray *FUNC( \
+               const psArray *st1, \
+               const psArray *st2, \
+               double RADIUS) \
+{ \
+    PS_ASSERT_PTR_NON_NULL(st1, NULL); \
+    PS_ASSERT_PTR_NON_NULL(st2, NULL); \
+    \
+    assert(st1->n == 0 || pmAstromObjTest(st1->data[0])); \
+    assert(st2->n == 0 || pmAstromObjTest(st2->data[0])); \
+    \
+    /* sort both lists by X coord; st1 first */ \
+    psVector *x1 = psVectorAlloc(st1->n, PS_TYPE_F64); \
+    for (int i = 0; i < st1->n; i++) { \
+        x1->data.F64[i] = ((pmAstromObj *)st1->data[i])->MEMBER->x; \
+    } \
+    const psVector *sorted1 = psVectorSortIndex(NULL, x1); \
+    assert (sorted1->type.type == PS_TYPE_S32); \
+    \
+    psVector *y1 = psVectorAlloc(st1->n, PS_TYPE_F64); \
+    for (int i = 0; i < st1->n; i++) { \
+        x1->data.F64[i] = ((pmAstromObj *)st1->data[sorted1->data.S32[i]])->MEMBER->x; \
+        y1->data.F64[i] = ((pmAstromObj *)st1->data[sorted1->data.S32[i]])->MEMBER->y; \
+    } \
+    \
+    /* now st2 */ \
+    psVector *x2 = psVectorAlloc(st2->n, PS_TYPE_F64); \
+    for (int i = 0; i < st2->n; i++) { \
+        x2->data.F64[i] = ((pmAstromObj *)st2->data[i])->MEMBER->x; \
+    } \
+    const psVector *sorted2 = psVectorSortIndex(NULL, x2); \
+    \
+    psVector *y2 = psVectorAlloc(st2->n, PS_TYPE_F64); \
+    for (int i = 0; i < st2->n; i++) { \
+        x2->data.F64[i] = ((pmAstromObj *)st2->data[sorted2->data.S32[i]])->MEMBER->x; \
+        y2->data.F64[i] = ((pmAstromObj *)st2->data[sorted2->data.S32[i]])->MEMBER->y; \
+    } \
+    /* Do the work */ \
+    psArray *matches = match_lists(x1, y1, x2, y2, sorted1, sorted2, RADIUS); \
+    \
+    psFree((void *)sorted1); \
+    psFree((void *)sorted2); \
+    psFree(x1); \
+    psFree(y1); \
+    psFree(x2); \
+    psFree(y2); \
+    \
+    psLogMsg (__func__, 3, "radius match: %ld pairs (radius: %f)\n", matches->n, RADIUS); \
+    return (matches); \
+}
+
+/******************************************************************************/
+/*
+ * Match two lists of pmAstromObjs, based on the FP, TP, or chip coordinates
+ */
+MAKE_ASTROM_RADIUS(pmAstromRadiusMatch, FP)
+MAKE_ASTROM_RADIUS(pmAstromRadiusMatchFP, FP)
+MAKE_ASTROM_RADIUS(pmAstromRadiusMatchTP, TP)
+MAKE_ASTROM_RADIUS(pmAstromRadiusMatchChip, chip)
+
+/******************************************************************************
+pmAstromMatchFit(map, raw, ref, match, stats): take two matched star lists
+and fit a psPlaneTransform between them
+ ******************************************************************************/
+pmAstromFitResults *pmAstromMatchFit(
+    psPlaneTransform *map,
+    psArray *raw,
+    psArray *ref,
+    psArray *match,
+    psStats *stats)
+{
+    PS_ASSERT_PTR_NON_NULL(map, NULL);
+    PS_ASSERT_PTR_NON_NULL(raw, NULL);
+    PS_ASSERT_PTR_NON_NULL(ref, NULL);
+    PS_ASSERT_PTR_NON_NULL(match, NULL);
+    PS_ASSERT_PTR_NON_NULL(stats, NULL);
+
+    // reassign values for clip fit
+    // XXX set wt based on mag error?
+    psVector *X = psVectorAlloc (match->n, PS_TYPE_F32);
+    psVector *Y = psVectorAlloc (match->n, PS_TYPE_F32);
+    psVector *x = psVectorAlloc (match->n, PS_TYPE_F32);
+    psVector *y = psVectorAlloc (match->n, PS_TYPE_F32);
+    psVector *wt = psVectorAlloc (match->n, PS_TYPE_F32);
+    // take the matched stars, first fit
+    for (int i = 0; i < match->n; i++) {
+        pmAstromMatch *pair = match->data[i];
+        pmAstromObj *rawStar = raw->data[pair->raw];
+        pmAstromObj *refStar = ref->data[pair->ref];
+
+        X->data.F32[i] = rawStar->chip->x;
+        Y->data.F32[i] = rawStar->chip->y;
+
+        x->data.F32[i] = refStar->FP->x;
+        y->data.F32[i] = refStar->FP->y;
+
+        wt->data.F32[i] = 1.0;
+    }
+
+    // constant errors
+    psVector *mask = psVectorAlloc (match->n, PS_TYPE_U8);
+    psVectorInit (mask, 0);
+
+    // the stats options supplied are used to perform the clip fitting
+    pmAstromFitResults *results = pmAstromFitResultsAlloc();
+    results->xStats = psStatsAlloc (PS_STAT_NONE);
+    results->yStats = psStatsAlloc (PS_STAT_NONE);
+    *results->xStats = *stats;
+    *results->yStats = *stats;
+
+    int nIter = stats->clipIter;
+
+    results->xStats->clipIter = 1;
+    results->yStats->clipIter = 1;
+
+    // fit chip-to-FPA transformation
+    // we run 'clipIter' cycles clipping in each of x and y, with only one iteration each
+    // need to use the stats lookups functions to get the width and center
+    for (int i = 0; i < nIter; i++) {
+	if (!psVectorClipFitPolynomial2D (map->x, results->xStats, mask, 0xff, x, wt, X, Y)) {
+            psError(PS_ERR_UNKNOWN, false, "failure in clip-fitting for x\n");
+	    psFree (x);
+	    psFree (y);
+	    psFree (X);
+	    psFree (Y);
+	    psFree (wt);
+	    psFree (mask);
+
+	    return results;
+	}
+        psTrace ("psModules.astrom", 3, "x resid: %f +/- %f (%ld of %ld)\n", results->xStats->clippedMean, results->xStats->clippedStdev, results->xStats->clippedNvalues, x->n);
+
+        if (!psVectorClipFitPolynomial2D (map->y, results->yStats, mask, 0xff, y, wt, X, Y)) {
+            psError(PS_ERR_UNKNOWN, false, "failure in clip-fitting for y\n");
+	    psFree (x);
+	    psFree (y);
+	    psFree (X);
+	    psFree (Y);
+	    psFree (wt);
+	    psFree (mask);
+
+	    return results;
+	}
+        psTrace ("psModules.astrom", 3, "y resid: %f +/- %f (%ld of %ld)\n", results->yStats->clippedMean, results->yStats->clippedStdev, results->yStats->clippedNvalues, y->n);
+    }
+    results->xStats->clipIter = stats->clipIter;
+    results->yStats->clipIter = stats->clipIter;
+
+    psFree (x);
+    psFree (y);
+    psFree (X);
+    psFree (Y);
+    psFree (wt);
+    psFree (mask);
+
+    return (results);
+}
+
+
+/******************************************************************************
+pmAstromRotateObj(old, center, angle, angle): rotate & scale the focal-plane coordinates
+about the center coordinate angle specified in radians
+ ******************************************************************************/
+psArray *pmAstromRotateObj(
+    const psArray *old,
+    psPlane center,
+    double angle,
+    double scale)
+{
+    PS_ASSERT_PTR_NON_NULL(old, NULL);
+
+    double X, Y;
+    pmAstromObj *newObj;
+    const pmAstromObj *oldObj;
+
+    psArray *new = psArrayAlloc (old->n);
+    double cs = scale*cos(angle);
+    double sn = scale*sin(angle);
+    double xCenter = center.x;
+    double yCenter = center.y;
+
+    for (int i = 0; i < old->n; i++) {
+
+        oldObj = (pmAstromObj *)old->data[i];
+        newObj = pmAstromObjCopy (oldObj);
+
+        X = oldObj->FP->x - xCenter;
+        Y = oldObj->FP->y - yCenter;
+
+        newObj->FP->x = X*cs + Y*sn + xCenter;
+        newObj->FP->y = Y*cs - X*sn + yCenter;
+
+        new->data[i] = newObj;
+    }
+    return (new);
+}
+
+/******************************************************************************
+pmAstromStatsFree(stats)
+ ******************************************************************************/
+static void pmAstromStatsFree(pmAstromStats *stats)
+{
+    if (stats == NULL)
+        return;
+    return;
+}
+
+/******************************************************************************
+pmAstromStatsAlloc()
+ ******************************************************************************/
+pmAstromStats *pmAstromStatsAlloc(void)
+{
+    pmAstromStats *stats = psAlloc (sizeof(pmAstromStats));
+    psMemSetDeallocator (stats, (psFreeFunc)pmAstromStatsFree);
+
+    //    stats->center = {0, 0, 0, 0};
+    //    stats->offset = {0, 0, 0, 0};
+    stats->angle     = 0.0;
+    stats->scale     = 1.0;
+    stats->minMetric = 0.0;
+    stats->minVar    = 0.0;
+    stats->nMatch    = 0;
+    stats->nTest     = 0;
+    stats->nSigma    = 0;
+
+    return (stats);
+}
+
+/******************************************************************************
+pmAstromFitResultsFree(stats)
+ ******************************************************************************/
+static void pmAstromFitResultsFree(pmAstromFitResults *results)
+{
+    if (results == NULL)
+        return;
+    psFree (results->xStats);
+    psFree (results->yStats);
+    return;
+}
+
+/******************************************************************************
+pmAstromFitResultsAlloc()
+ ******************************************************************************/
+pmAstromFitResults *pmAstromFitResultsAlloc(void)
+{
+    pmAstromFitResults *results = psAlloc (sizeof(pmAstromFitResults));
+    psMemSetDeallocator (results, (psFreeFunc)pmAstromFitResultsFree);
+
+    results->xStats    = NULL;
+    results->yStats    = NULL;
+    results->nMatch    = 0;
+    results->nSigma    = 0;
+
+    return (results);
+}
+
+/******************************************************************************
+astromObjFree(obj)
+ ******************************************************************************/
+static void astromObjFree(pmAstromObj *obj)
+{
+    if (obj == NULL) {
+        return;
+    }
+
+    psFree(obj->pix);
+    psFree(obj->cell);
+    psFree(obj->chip);
+    psFree(obj->FP);
+    psFree(obj->TP);
+    psFree(obj->sky);
+
+    return;
+}
+
+
+/******************************************************************************
+pmAstromObjAlloc()
+ ******************************************************************************/
+pmAstromObj *pmAstromObjAlloc(void)
+{
+    pmAstromObj *obj = psAlloc (sizeof(pmAstromObj));
+    psMemSetDeallocator (obj, (psFreeFunc)astromObjFree);
+
+    obj->pix  = psPlaneAlloc();
+    obj->cell = psPlaneAlloc();
+    obj->chip = psPlaneAlloc();
+    obj->FP   = psPlaneAlloc();
+    obj->TP   = psPlaneAlloc();
+    obj->sky  = psSphereAlloc();
+    obj->Mag  = 0;
+    obj->dMag = 0;
+
+    return (obj);
+}
+
+bool pmAstromObjTest(const psPtr ptr)
+{
+    return (psMemGetDeallocator(ptr) == (psFreeFunc)astromObjFree);
+}
+
+
+
+/******************************************************************************
+pmAstromObjCopy(old)
+ ******************************************************************************/
+pmAstromObj *pmAstromObjCopy(const pmAstromObj *old)
+{
+    PS_ASSERT_PTR_NON_NULL(old, NULL);
+    pmAstromObj *obj = pmAstromObjAlloc();
+
+    *obj->pix  = *old->pix;
+    *obj->cell = *old->cell;
+    *obj->chip = *old->chip;
+    *obj->FP   = *old->FP;
+    *obj->TP   = *old->TP;
+    *obj->sky  = *old->sky;
+    obj->Mag   =  old->Mag;
+    obj->dMag  =  old->dMag;
+
+    return(obj);
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+static void pmAstromMatchFree (pmAstromMatch *match)
+{
+    if (match == NULL)
+        return;
+    return;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+pmAstromMatch *pmAstromMatchAlloc(
+    int raw,
+    int ref)
+{
+    pmAstromMatch *match = psAlloc (sizeof(pmAstromMatch));
+    psMemSetDeallocator(match, (psFreeFunc) pmAstromMatchFree);
+
+    match->raw = raw;
+    match->ref = ref;
+
+    return (match);
+}
+
+
+static double maxOffpix;                // maximum allowed offset between lists, in raw pixels
+static double Scale;                    // grid pixel scale static
+double Offset;                          // deltas to pixels
+/******************************************************************************
+AstromGridBin(*dx, *dy, dX, dY): local function to convert x,y coords to grid
+bins it requires the globals defined above.
+
+ ******************************************************************************/
+static bool AstromGridBin(
+    int *dx,
+    int *dy,
+    double dX,
+    double dY)
+{
+    if (PM_ASTROMETRYOBJECTS_DEBUG) {
+        PS_ASSERT_PTR_NON_NULL(dx, false);
+        PS_ASSERT_PTR_NON_NULL(dy, false);
+    }
+
+    if (!isfinite(dX)) return false;
+    if (!isfinite(dY)) return false;
+
+    if (fabs(dX) > maxOffpix) return false;
+    if (fabs(dY) > maxOffpix) return false;
+
+    *dx = dX / Scale + Offset;
+    *dy = dY / Scale + Offset;
+    return true;
+}
+
+
+/******************************************************************************
+pmAstromGridAngle(raw, ref, config): match the two lists using the binned
+delta-delta max.
+ ******************************************************************************/
+pmAstromStats *pmAstromGridAngle(
+    const psArray *raw,
+    const psArray *ref,
+    const psMetadata *config)
+{
+    PS_ASSERT_PTR_NON_NULL(raw, NULL);
+    PS_ASSERT_PTR_NON_NULL(ref, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    bool status;
+    int nPix;       // size of matching grid
+    int nPixHalf;   // half-size of matching grid
+    double dX, dY;  // offset between a possible matched pair
+    int iX, iY;     // corresponding grid bin
+
+    const pmAstromObj *ob1, *ob2; // short-cut pointers to the objects
+
+    pmAstromStats *stats = pmAstromStatsAlloc();    // output match statistics
+
+    // max allowed offset in either X or Y directions
+    double gridOffset = psMetadataLookupF32 (&status, config, "PSASTRO.GRID.OFFSET");
+
+    // sampling scale of the grid
+    double gridScale  = psMetadataLookupF32 (&status, config, "PSASTRO.GRID.SCALE");
+
+    // set the static scaling factors
+    nPixHalf = (int)(gridOffset / gridScale + 0.5);  // half-grid
+    nPix = 2*nPixHalf + 1;                           // full grid width
+
+    // these are globals used by p_pmAstromGridBin
+    maxOffpix = gridScale * (nPixHalf + 0.5);            // max offset from true center
+    Offset    = maxOffpix / gridScale;
+    Scale     = gridScale;
+
+    // images used as accumulators for the loop below
+    psImage *gridNP = psImageAlloc (nPix, nPix, PS_TYPE_U32);
+    psImage *gridDX = psImageAlloc (nPix, nPix, PS_TYPE_F32);
+    psImage *gridDY = psImageAlloc (nPix, nPix, PS_TYPE_F32);
+    psImage *gridD2 = psImageAlloc (nPix, nPix, PS_TYPE_F32);
+    psImageInit (gridNP, 0);
+    psImageInit (gridDX, 0);
+    psImageInit (gridDY, 0);
+    psImageInit (gridD2, 0);
+
+    // short-cut names for grid images
+    psU32 **NP = gridNP->data.U32;
+    psF32 **DX = gridDX->data.F32;
+    psF32 **DY = gridDY->data.F32;
+    psF32 **D2 = gridD2->data.F32;
+
+    // accumulate grids for focal plane (L,M) matches
+    for (int i = 0; i < raw->n; i++) {
+        ob1 = (pmAstromObj *)raw->data[i];
+        for (int j = 0; j < ref->n; j++) {
+            ob2 = (pmAstromObj *)ref->data[j];
+            dX = ob1->FP->x - ob2->FP->x;
+            dY = ob1->FP->y - ob2->FP->y;
+
+            // fprintf (f, "dX,dY: %8.2f %8.2f : %8.2f %8.2f : %8.2f %8.2f\n", dX, dY, ob1->FP->x, ob2->FP->x, ob1->FP->y, ob2->FP->y);
+            // find bin coordinates for this delta-delta
+            if (!AstromGridBin (&iX, &iY, dX, dY)) {
+                continue; // matched pair is too far offset
+            }
+
+            // accumulate bin stats
+            NP[iY][iX] ++;
+            DX[iY][iX] += dX;
+            DY[iY][iX] += dY;
+            D2[iY][iX] += PS_SQR(dX) + PS_SQR(dY);
+        }
+    }
+
+    // now assess the grid images
+    {
+        double minMetric = 1e10;
+        double minVar = 1e10;
+        int minX = -1;
+        int minY = -1;
+        double metric, var;
+
+        // find the max pixel
+        psStats *imStats = psStatsAlloc (PS_STAT_MAX | PS_STAT_MAX | PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+        if (!psImageStats(imStats, gridNP, NULL, 0)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to get image statistics.\n");
+            psFree(imStats);
+            psFree(gridNP);
+            psFree(gridDX);
+            psFree(gridDY);
+            psFree(gridD2);
+            psFree(stats);
+            return NULL;
+        }
+
+        # if 0
+        char line[16];
+        psFits *fits = psFitsOpen ("grid.image.fits", "w");
+        psFitsWriteImage (fits, NULL, gridNP, 0, NULL);
+        psFitsClose (fits);
+        fprintf (stderr, "wrote grid image, press return to continue\n");
+        fgets (line, 15, stdin);
+        # endif
+
+        // only check bins with at least 1/2 of max bin
+        // XXX requiring at least 3 matches in bin
+        int minNpts = PS_MAX (0.5*imStats->max, 5);
+        psTrace("psModule.astrom", 5, "minNpts: %d, min: %d, max: %d, median: %f, stdev: %f", minNpts, (int)(imStats->min), (int)(imStats->max), imStats->sampleMedian, imStats->sampleStdev);
+
+        // find the 'best' bin
+        for (int j = 0; j < gridNP->numRows; j++) {
+            for (int i = 0; i < gridNP->numCols; i++) {
+                if (NP[j][i] < minNpts) continue;
+
+                // this metric emphasizes a narrow peak with lots of sources over one with few.
+                var = fabs((D2[j][i]/NP[j][i]) - PS_SQR(DX[j][i]/NP[j][i]) - PS_SQR(DY[j][i]/NP[j][i]));
+                metric = var / PS_SQR(NP[j][i]) / PS_SQR(NP[j][i]);
+
+                // fprintf (stderr, "try : %f %f (%d pts, %f var, %f met)\n", DX[j][i]/NP[j][i], DY[j][i]/NP[j][i], NP[j][i], var, metric);
+
+                if (metric < minMetric) {
+                    minMetric = metric;
+                    minVar    = var;
+                    minX      = i;
+                    minY      = j;
+                }
+            }
+        }
+
+        // convert the bin to delta-delta
+        if ((minX < 0) || (minY < 0))
+        {
+            // no valid matches found
+            stats->offset.x   = 0;
+            stats->offset.y   = 0;
+            stats->minMetric  = minMetric;
+            stats->minVar     = minVar;
+            stats->nMatch     = 0;
+        } else
+        {
+            stats->offset.x  = DX[minY][minX] / NP[minY][minX];
+            stats->offset.y  = DY[minY][minX] / NP[minY][minX];
+            stats->minMetric = minMetric;
+            stats->minVar    = minVar;
+            stats->nMatch    = NP[minY][minX];
+        }
+
+        psFree (imStats);
+        // XXX EAM : This routine, and pmAstromGridMatch, need to handle failure cases better
+    }
+
+    // sort the NP values and choose
+    psVector *listNP = psVectorAlloc (nPix*nPix, PS_TYPE_U32);
+    int n = 0;
+    for (int i = 0; i < nPix; i++) {
+        for (int j = 0; j < nPix; j++) {
+            listNP->data.U32[n] = gridNP->data.U32[j][i];
+            n++;
+        }
+    }
+    psVector *sort = psVectorSort (NULL, listNP);
+    stats->nTest = sort->data.U32[sort->n - 5];
+    stats->nSigma = (stats->nMatch - stats->nTest) / sqrt(stats->nTest);
+    // XXX this needs a better analysis of the image histogram..
+    // fprintf (stderr, "sigma: nMatch: %d, nTest: %d, nTen: %d\n", stats->nMatch, stats->nTest, sort->data.U32[sort->n - 10]);
+
+    psFree (sort);
+    psFree (listNP);
+    psFree (gridNP);
+    psFree (gridDX);
+    psFree (gridDY);
+    psFree (gridD2);
+    return (stats);
+}
+
+
+
+/******************************************************************************
+pmAstromGridMatch(*raw, *ref, *config): match two star lists.
+ ******************************************************************************/
+
+pmAstromStats *pmAstromGridMatch(const psArray *raw,
+                                 const psArray *ref,
+                                 const psMetadata *config)
+{
+    PS_ASSERT_PTR_NON_NULL(raw, NULL);
+    PS_ASSERT_PTR_NON_NULL(ref, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    bool status;
+    double xMin, xMax, yMin, yMax;
+    const pmAstromObj *obj;
+    psArray *rot;
+
+    pmAstromStats *minStat = pmAstromStatsAlloc ();
+    pmAstromStats *newStat = NULL;
+
+    psPlane center;
+
+    // find center of the raw field (focal-plane coords)
+    xMin = yMin = +1e10;
+    xMax = yMax = -1e10;
+    for (int i = 0; i < raw->n; i++) {
+        obj = (pmAstromObj *)raw->data[i];
+        xMin = PS_MIN (obj->FP->x, xMin);
+        xMax = PS_MAX (obj->FP->x, xMax);
+        yMin = PS_MIN (obj->FP->y, yMin);
+        yMax = PS_MAX (obj->FP->y, yMax);
+    }
+    center.x = 0.5*(xMin + xMax);
+    center.y = 0.5*(yMin + yMax);
+
+    double minScale = psMetadataLookupF32 (&status, config, "PSASTRO.GRID.MIN.SCALE");
+    double maxScale = psMetadataLookupF32 (&status, config, "PSASTRO.GRID.MAX.SCALE");
+    double delScale = psMetadataLookupF32 (&status, config, "PSASTRO.GRID.DEL.SCALE");
+
+    double minAngle = PS_RAD_DEG*psMetadataLookupF32 (&status, config, "PSASTRO.GRID.MIN.ANGLE");
+    double maxAngle = PS_RAD_DEG*psMetadataLookupF32 (&status, config, "PSASTRO.GRID.MAX.ANGLE");
+    double delAngle = PS_RAD_DEG*psMetadataLookupF32 (&status, config, "PSASTRO.GRID.DEL.ANGLE");
+    double minSigma = psMetadataLookupF32 (&status, config, "PSASTRO.GRID.MIN.SIGMA");
+
+    minStat->minMetric = 1e10;
+    for (double scale = minScale; scale <= maxScale; scale += delScale) {
+	for (double angle = minAngle; angle <= maxAngle; angle += delAngle) {
+	    rot = pmAstromRotateObj (raw, center, angle, scale);
+
+# if 0
+	    FILE *f1 = fopen ("raw.dat", "w");
+	    for (int i = 0; i < rot->n; i++) {
+		pmAstromObj *obj = rot->data[i];
+		fprintf (f1, "%8.2f %8.2f   %6.2f\n", obj->FP->x, obj->FP->y, obj->Mag);
+	    }
+	    fclose (f1);
+	    FILE *f2 = fopen ("ref.dat", "w");
+	    for (int i = 0; i < ref->n; i++) {
+		pmAstromObj *obj = ref->data[i];
+		fprintf (f2, "%8.2f %8.2f   %6.2f\n", obj->FP->x, obj->FP->y, obj->Mag);
+	    }
+	    fclose (f2);
+	    fprintf (stderr, "type return");
+	    char c;
+	    fscanf (stdin, "%c", &c);
+# endif
+
+	    newStat = pmAstromGridAngle (rot, ref, config);
+	    newStat->angle  = angle;
+	    newStat->scale  = scale;
+	    newStat->center = center;
+
+	    if (isfinite(newStat->minMetric) && (newStat->minMetric > 0.0) && (newStat->minMetric < minStat->minMetric)) {
+		*minStat = *newStat;
+		psLogMsg ("psModule.astrom", 4, "grid test - offset: %7.2f,%7.2f @ %6.1f deg x %7.3f (%4d pts, %5.1f sig, %5.1f var, %6.3f log metric) *",
+			  minStat->offset.x, minStat->offset.y, PS_DEG_RAD*minStat->angle, minStat->scale, minStat->nMatch, minStat->nSigma, minStat->minVar, log10(minStat->minMetric));
+	    } else {
+		psLogMsg ("psModule.astrom", 4, "grid test - offset: %7.2f,%7.2f @ %6.1f deg x %7.3f (%4d pts, %5.1f sig, %5.1f var, %6.3f log metric)",
+			  newStat->offset.x, newStat->offset.y, PS_DEG_RAD*newStat->angle, newStat->scale, newStat->nMatch, newStat->nSigma, newStat->minVar, log10(newStat->minMetric));
+
+	    }
+	    psFree (newStat);
+	    psFree (rot);
+	}
+    }
+    psLogMsg ("psModule.astrom.grid.match", 4, "grid best - offset: %7.2f,%7.2f @ %6.1f deg x %7.3f (%4d pts, %5.1f sig, %5.1f var, %6.3f log metric)",
+              minStat->offset.x, minStat->offset.y, PS_DEG_RAD*minStat->angle, minStat->scale, minStat->nMatch, minStat->nSigma, minStat->minVar, log10(minStat->minMetric));
+
+    // I need to decide if a solution is likely to be a good solution or just a mis-match
+    // one posibility: how significant is the peak relative to the 4th or 5th most significant pixel?
+
+    if (minStat->nSigma < minSigma) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to find a valid match (%f sigma for best)", minStat->nSigma);
+        psFree (minStat);
+        return NULL;
+    }
+    return (minStat);
+}
+
+/******************************************************************************
+pmAstromGridTweak(*raw, *ref, *recipe, stats): improve match for two star lists.
+ ******************************************************************************/
+pmAstromStats *pmAstromGridTweak(
+    psArray *raw,
+    psArray *ref,
+    psMetadata *recipe,
+    pmAstromStats *stats)
+{
+    bool status;
+    pmAstromObj *ob1, *ob2;  // short-cut pointers to the objects
+    double dX, dY;   // offset between a possible matched pair
+    psArray *rot;
+    int nBin, xBin, yBin;
+
+    rot = pmAstromRotateObj (raw, stats->center, stats->angle, stats->scale);
+
+    // sampling scale of the grid
+    double tweakScale  = psMetadataLookupF32 (&status, recipe, "PSASTRO.TWEAK.SCALE");
+    double tweakRange  = psMetadataLookupF32 (&status, recipe, "PSASTRO.TWEAK.RANGE");
+    double tweakSmooth = psMetadataLookupF32 (&status, recipe, "PSASTRO.TWEAK.SMOOTH");
+    double tweakNsigma = psMetadataLookupF32 (&status, recipe, "PSASTRO.TWEAK.NSIGMA");
+
+    nBin = 2*tweakRange / tweakScale;
+    psVector *xHist = psVectorAlloc (nBin, PS_TYPE_F32);
+    psVector *yHist = psVectorAlloc (nBin, PS_TYPE_F32);
+    psVectorInit (xHist, 0);
+    psVectorInit (yHist, 0);
+
+    // accumulate grids for focal plane (L,M) matches
+    for (int i = 0; i < rot->n; i++) {
+        ob1 = (pmAstromObj *)rot->data[i];
+        for (int j = 0; j < ref->n; j++) {
+            ob2 = (pmAstromObj *)ref->data[j];
+            dX = ob1->FP->x - ob2->FP->x - stats->offset.x;
+            dY = ob1->FP->y - ob2->FP->y - stats->offset.y;
+
+            xBin = (dX + tweakRange) / tweakScale;
+            yBin = (dY + tweakRange) / tweakScale;
+
+            if (xBin < 0)
+                continue;
+            if (yBin < 0)
+                continue;
+            if (xBin >= nBin)
+                continue;
+            if (yBin >= nBin)
+                continue;
+
+            xHist->data.F32[xBin] += 1.0;
+            yHist->data.F32[yBin] += 1.0;
+        }
+    }
+
+    // smooth histgram vector with gaussian of 1sigma = radius
+    psVector *xHistNew = psVectorSmooth(NULL, xHist, tweakSmooth, tweakNsigma);
+    psVector *yHistNew = psVectorSmooth(NULL, yHist, tweakSmooth, tweakNsigma);
+    psFree(xHist);
+    psFree(yHist);
+    xHist = xHistNew;
+    yHist = yHistNew;
+
+    // select peak in x and in y
+    xBin = yBin = 0;
+    double xMax = 0;
+    double yMax = 0;
+    for (int i = 0; i < nBin; i++) {
+        if (xHist->data.F32[i] > xMax) {
+            xBin = i;
+            xMax = xHist->data.F32[i];
+        }
+        if (yHist->data.F32[i] > yMax) {
+            yBin = i;
+            yMax = yHist->data.F32[i];
+        }
+    }
+    double xPeak = xBin*tweakScale - tweakRange;
+    double yPeak = yBin*tweakScale - tweakRange;
+    psLogMsg (__func__, 3, "tweak peak by %f,%f\n", xPeak, yPeak);
+
+    // adjust offset by peak center
+    pmAstromStats *tweak = pmAstromStatsAlloc();
+    *tweak = *stats;
+    tweak->offset.x += xPeak;
+    tweak->offset.y += yPeak;
+
+    psFree (rot);
+    psFree (xHist);
+    psFree (yHist);
+
+    return tweak;
+}
+
+/******************************************************************************
+pmAstromGridApply(*map, stat): apply the measured FPA offset and rotation
+(stat) to the fpa astrom structures.
+ ******************************************************************************/
+psPlaneTransform *pmAstromGridApply(
+    psPlaneTransform *map,
+    pmAstromStats *stat)
+{
+    PS_ASSERT_PTR_NON_NULL(map, NULL);
+    PS_ASSERT_POLY_NON_NULL(map->x, NULL);
+    PS_ASSERT_POLY_NON_NULL(map->y, NULL);
+
+    double cs = stat->scale * cos (stat->angle);
+    double sn = stat->scale * sin (stat->angle);
+
+    double dx = (map->x->coeff[0][0] - stat->center.x);
+    double dy = (map->y->coeff[0][0] - stat->center.y);
+
+    // new offset
+    map->x->coeff[0][0] =  cs*dx + sn*dy - stat->offset.x + stat->center.x;
+    map->y->coeff[0][0] = -sn*dx + cs*dy - stat->offset.y + stat->center.y;
+
+    // original rotation matrix
+    double pc1_1 = map->x->coeff[1][0];
+    double pc1_2 = map->x->coeff[0][1];
+    double pc2_1 = map->y->coeff[1][0];
+    double pc2_2 = map->y->coeff[0][1];
+
+    // new rotation matrix
+    map->x->coeff[1][0] = +cs*pc1_1 + sn*pc2_1;
+    map->x->coeff[0][1] = +cs*pc1_2 + sn*pc2_2;
+    map->y->coeff[1][0] = -sn*pc1_1 + cs*pc2_1;
+    map->y->coeff[0][1] = -sn*pc1_2 + cs*pc2_2;
+
+    return (map);
+}
+
+/* Illustration of the grid bins
+   dX        bin
+   -35:-25 -> 0     bin = dX / Scale + Offset
+   -25:-15 -> 1     Scale = 10
+   -15:-05 -> 2     Offset = 3.5
+   -05:+05 -> 3     nPix = 3 (maxOffset = 35 = (nPix + 0.5)*dXix
+   +05:+15 -> 4     dPix = 10
+   +15:+25 -> 5
+   +25:+35 -> 6
+
+   maxOffsetRequest = 30
+   nPix = (int) (maxOffset / dPix + 0.5);
+   maxOffset = (nPix + 0.5)*Scale;
+*/
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryObjects.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryObjects.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryObjects.h	(revision 22322)
@@ -0,0 +1,348 @@
+/* @file  pmAstrometryObjects.h
+ * @brief basic matching of objects based on their astrometry.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-21 07:02:55 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PM_ASTROMETRY_OBJECTS_H
+#define PM_ASTROMETRY_OBJECTS_H
+
+/// @addtogroup Astrometry
+/// @{
+
+/*
+ *
+ * This structure specifies the coordinate of the detection in each of the
+ * four necessary coordinate frames: pix defines the position in the psReadout
+ * frame, FP defines the position in the Focal Plane frame, TP defines the
+ * position in the Tangent Plane frame, sky defines the position on the Celestial
+ * Sphere. In addition, a measurement of the brightness is given by the element
+ * Mag. Such a data structure should be used for both the raw and the reference
+ * stars. In astrometric processing, the raw detections will be projected using
+ * the best available information to each of these coordinate frames from the pix
+ * coordinates, while the reference detections will be projected to the other
+ * frames from the sky coordinates.
+ *
+ * XXX: There are more members here than in the SDRS.
+ *
+ */
+typedef struct
+{
+    psPlane *pix;   ///< the position in the pmReadout frame
+    psPlane *cell;   ///< the position in the pmCell frame
+    psPlane *chip;   ///< the position in the pmChip frame
+    psPlane *FP;   ///< the position in the pmFPA frame
+    psPlane *TP;   ///< the position in the tangent plane
+    psSphere *sky;        ///< the position on the Celestial Sphere.
+    double Mag;                         ///< object magnitude XXX what filter?
+    double dMag;                        ///< error on object magnitude
+}
+pmAstromObj;
+
+/*
+ *
+ * The pmAstromMatch structure defines the cross-correlation between two
+ * arrays. A single such data item specifies that item number pmAstromMatch.idx1
+ * in the first list corresponds to pmAstromMatch.idx2 in the second list.
+ *
+ */
+typedef struct
+{
+    int raw;                             ///< What is this?
+    int ref;                             ///< What is this?
+}
+pmAstromMatch;
+
+
+/*
+ *
+ * XXX: Not in SDRS.
+ *
+ */
+typedef struct
+{
+    psPlane center;                     ///<
+    psPlane offset;                     ///<
+    double  scale;                      ///<
+    double  angle;                      ///<
+    double  minMetric;                  ///<
+    double  minVar;                     ///<
+    int     nMatch;                     ///<
+    int     nTest;                      ///<
+    double  nSigma;                     ///<
+}
+pmAstromStats;
+
+typedef struct
+{
+    psStats *xStats;
+    psStats *yStats;
+    int     nMatch;                     ///<
+    double  nSigma;                     ///<
+}
+pmAstromFitResults;
+
+/*
+ *
+ * If the two sets of coordinates are expected to agree very well (ie, the current best-guess
+ * astrometric solution is quite close to reality), perform a match based on a simple radius
+ * test. The following functions accept two sets of pmAstromObj sources and determines the
+ * matched objects between the two lists using coordinates of the desired depth (depending on
+ * the function). The input and reference sources must have been projected to the desired depth
+ * (eg, for Focal Plane coordinates, to pmAstromObj.FP).  The specified radius must be in the
+ * units for the matching depth (chip: pixels, focal plane: microns, tangent plane:
+ * degrees. The output consists an array of pmAstromMatch values, defined above.
+ *
+ */
+psArray *pmAstromRadiusMatch(
+    const psArray *st1,
+    const psArray *st2,
+    double RADIUS
+);
+psArray *pmAstromRadiusMatchFP(
+    const psArray *st1,
+    const psArray *st2,
+    double RADIUS
+);
+psArray *pmAstromRadiusMatchTP(
+    const psArray *st1,
+    const psArray *st2,
+    double RADIUS
+);
+psArray *pmAstromRadiusMatchChip(
+    const psArray *st1,
+    const psArray *st2,
+    double RADIUS
+);
+
+
+pmAstromStats *pmAstromStatsAlloc(void);
+
+/*
+ *
+ * This function accepts an array of pmAstromObj objects and rotates them by
+ * the given angle about the given center coordinate pCenter,qCenter in the Focal
+ * Plane Array coordinates.
+ *
+ * XXX: This differs from the SDRS
+ *
+ */
+/* SDRS
+psArray *pmAstromRotateObj(
+    psArray *old,
+    double angle,
+    double pCenter,
+    double qCenter
+);
+*/
+psArray *pmAstromRotateObj(
+    const psArray *old,
+    psPlane center,
+    double angle,
+    double scale
+);
+
+
+/*
+ *
+ * If the two sets of coordinates are not known to agree well, but the
+ * relative scale and approximate relative rotation is known, then a much faster
+ * match can be found using pair-pair displacements. In such a case, the two
+ * lists can be considered as having the same coordinate system, with an unknown
+ * relative displacement. In this algorithm, all possible pair-wise differences
+ * between the source positions in the two lists are constructed and accumulated
+ * in a grid of possible offset values. The resulting grid is searched for a
+ * cluster representing the offset between the two input lists. This algorithm
+ * can only tolerate a small error in the relative scale or the relative rotation
+ * of the two coordinate lists. However, this process is naturally O(N2), and is
+ * thus advantageous over triangle matching in some circumstances. This process
+ * can be extended to allow a larger uncertainty in the relative rotation by
+ * allowing the procedure to scan over a range of rotations. We define the
+ * following function to apply this matching algorithm:
+ *
+ * XXX: In the SDRS, this function is a pointer.
+ *
+ */
+pmAstromStats *pmAstromGridMatch(
+    const psArray *st1,
+    const psArray *st2,
+    const psMetadata *config
+);
+
+/******************************************************************************
+pmAstromGridTweak(*raw, *ref, *recipe, stats): improve match for two star lists.
+ ******************************************************************************/
+pmAstromStats *pmAstromGridTweak(
+    psArray *raw,
+    psArray *ref,
+    psMetadata *recipe,
+    pmAstromStats *stats);
+
+/*
+ *
+ * The result of a pmAstromGridMatch may be used to modify the astrometry
+ * transformation information for a pmFPA image hierarchy structure. The result
+ * of pmAstromGridMatch defines the adjustments which should be made to the
+ * reference coordinate of the projection (pmFPA.projection.R,D) and the
+ * effective rotation of the Focal Plane.  The rotation implies modification of
+ * the linear terms of the pmFPA.toTangentPlane transformation. These two
+ * adjustments are made using the function:
+ *
+ * XXX: This function name is different in the SDRS.
+ *
+ */
+psPlaneTransform *pmAstromGridApply(
+    psPlaneTransform *map,
+    pmAstromStats *stat
+);
+
+
+/*
+ *
+ * This function is identical to pmAstromGridMatch, but is valid for only a
+ * single relative rotation. The input config information need not contain any of
+ * the GRID.*.ANGLE entries (they will be ignored).
+ *
+ * XXX: This function name is different in the SDRS.
+ *
+ */
+/* in pmAstromGrid.c */
+pmAstromStats *pmAstromGridAngle(
+    const psArray *st1,
+    const psArray *st2,
+    const psMetadata *config);
+
+
+
+/*
+ *
+ * This function accepts the raw and reference source lists and the list of
+ * matched entries. It uses the matched list to determine a polynomial
+ * transformation between the two coordinate systems. The fitting uses clipping
+ * to exclude outliers, likely representing poor matches. The config element must
+ * contain the information ASTROM.NSIGMA (specifying the number of sigma used in
+ * the clipping) and ASTROM.NCLIP (specifying the number of clipping iterations
+ * must be performed). The config element must also specify the order of the
+ * polynomial fit (keyword: ASTROM.ORDER). The result of this fit is a set of
+ * modifications of the components of the pmFPA.toTangentPlane transformation,
+ * and the modifications of the reference coordinate of the projection
+ * (pmFPA.projection.R,D) and the projection scale (pmFPA.projection.Xs,Ys). The
+ * modifications to pmFPA.toTangentPlane incorporate the rotation component of
+ * the linear terms and the higher-order terms of the polynomial fits.
+ *
+ * XXX: No prototype code.
+ *
+ */
+bool pmAstromFitFPA(
+    pmFPA *fpa,
+    psArray *st1,
+    psArray *st2,
+    psArray *match,
+    psMetadata *config
+);
+
+
+
+/*
+ *
+ * This function accepts the raw and reference source lists for a single chip
+ * and the list of matched entries. It uses the matched list to determine a
+ * polynomial transformation between the two coordinate systems. The fitting
+ * uses clipping to exclude outliers, likely representing poor matches. The
+ * config element must contain the information ASTROM.NSIGMA
+ *(specifying the number of sigma used in the clipping) and ASTROM.NCLIP
+ *(specifying the number of clipping iterations must be performed). The config
+ *element must also specify the order of the polynomial fit (keyword:
+ *ASTROM.ORDER).  The result of this fit is a set of modifications of the
+ *components of the pmChip.toFPA transformation.
+ *
+ * XXX: No prototype code.
+ *
+ */
+bool pmAstromFitChip(
+    pmFPA *fpa,
+    psArray *st1,
+    psArray *st2,
+    psArray *match,
+    psMetadata *config
+);
+
+
+/*******************************************************************************
+ The following functions and structs were in the prototype code, but not the
+ SDRS.
+ ******************************************************************************/
+/*
+ *
+ *
+ *
+ *
+ */
+
+pmAstromFitResults *pmAstromFitResultsAlloc(void);
+
+/*
+ *
+ * Allocates a pmAstromObj struct.
+ *
+ */
+pmAstromObj *pmAstromObjAlloc (void);
+/*
+ * Is a given pointer a pmAstromObj?
+ */
+bool pmAstromObjTest(const psPtr ptr);
+
+
+/*
+ *
+ * Copies a pmAstromObj struct.
+ *
+ */
+pmAstromObj *pmAstromObjCopy(
+    const pmAstromObj *old
+);
+
+
+
+/*
+ *
+ *
+ *
+ */
+pmAstromMatch *pmAstromMatchAlloc(
+    int i1,
+    int i2
+);
+
+
+
+
+/*
+ *
+ *
+ *
+ */
+pmAstromFitResults *pmAstromMatchFit(
+    psPlaneTransform *map,
+    psArray *raw,
+    psArray *ref,
+    psArray *match,
+    psStats *stats
+);
+
+/*
+ *
+ *
+ *
+ */
+int pmAstromObjSortByMag(
+    const void **a,
+    const void **b
+);
+
+/// @}
+#endif // PM_ASTROMETRY_OBJECTS_H
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRefstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRefstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRefstars.c	(revision 22322)
@@ -0,0 +1,299 @@
+/* @file  pmAstrometryRefstars.c
+ * @brief Functions to write (and read?) astrometric reference stars
+ *
+ * @ingroup AstroImage
+ *
+ * @author EAM, IfA
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-07-17 22:38:15 $
+ * Copyright 2008 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include <unistd.h>   // for unlink
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmFPAfileFitsIO.h"
+#include "pmAstrometryObjects.h"
+#include "pmAstrometryRefstars.h"
+
+/********************* CheckDataStatus functions *****************************/
+
+bool pmAstromRefstarsCheckDataStatusForView (const pmFPAview *view, pmFPAfile *file) {
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        bool exists = pmAstromRefstarsCheckDataStatusForFPA (fpa);
+        return exists;
+    }
+    if (view->chip >= fpa->chips->n) {
+        psError(PS_ERR_IO, true, "Requested chip == %d >= fpa->chips->n == %ld", view->chip, fpa->chips->n);
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        bool exists = pmAstromRefstarsCheckDataStatusForChip (chip);
+        return exists;
+    }
+    if (view->cell >= chip->cells->n) {
+        psError(PS_ERR_IO, true, "Requested cell == %d >= chip->cells->n == %ld", view->cell, chip->cells->n);
+        return false;
+    }
+    psError(PS_ERR_IO, false, "Astrometry only valid at the chip level");
+    return false;
+}
+
+// return true if data exists for any chip
+bool pmAstromRefstarsCheckDataStatusForFPA (const pmFPA *fpa) {
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        if (!chip) continue;
+        if (pmAstromRefstarsCheckDataStatusForChip (chip)) return true;
+    }
+    return false;
+}
+
+// return true if data exists for any cell
+bool pmAstromRefstarsCheckDataStatusForChip (const pmChip *chip) {
+
+    for (int i = 0; i < chip->cells->n; i++) {
+      pmCell *cell = chip->cells->data[i];
+        if (!cell) continue;
+        if (pmAstromRefstarsCheckDataStatusForCell (cell)) return true;
+    }
+    return false;
+}
+
+// return true if data exists for any readout
+bool pmAstromRefstarsCheckDataStatusForCell (const pmCell *cell) {
+
+    for (int i = 0; i < cell->readouts->n; i++) {
+      pmReadout *readout = cell->readouts->data[i];
+        if (!readout) continue;
+        if (pmAstromRefstarsCheckDataStatusForReadout (readout)) return true;
+    }
+    return false;
+}
+
+// check if refstars array exists
+bool pmAstromRefstarsCheckDataStatusForReadout (const pmReadout *readout) {
+
+  if (!readout->analysis) return false;
+
+  // select the raw objects for this readout
+  psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+  if (refstars == NULL) return false;
+
+  return true;
+}
+
+/********************* Write Data functions *****************************/
+
+bool pmAstromRefstarsWriteForView (const pmFPAview *view, pmFPAfile *file, const pmConfig *config) {
+
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        if (!pmAstromRefstarsWriteFPA (fpa, view, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write refstars for fpa");
+            return false;
+        }
+        return true;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        psError(PS_ERR_UNKNOWN, false, "Writing chip == %d (>= chips->n == %ld)", view->chip, fpa->chips->n);
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        if (!pmAstromRefstarsWriteChip (chip, view, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write refstars for chip");
+            return false;
+        }
+        return true;
+    }
+
+    psError(PS_ERR_IO, false, "Astrometry must be written at the FPA level");
+    return false;
+}
+
+bool pmAstromRefstarsWritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config) {
+
+    // output header data
+    psMetadata *outhead = psMetadataAlloc();
+
+    // use the FPA phu to generate the PHU header
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
+    pmHDU *phu = psMemIncrRefCounter(fpa->hdu);
+    psFree(fpa);
+
+    // if there is no FPA PHU, this is a single header+image (extension-less) file. This could be
+    // the case for an input SPLIT set of files being written out as a MEF.  if there is a PHU,
+    // write it out as a 'blank'
+    if (phu) {
+        psMetadataCopy (outhead, phu->header);
+    } else {
+        pmConfigConformHeader (outhead, file->format);
+    }
+    psFree(phu);
+
+    psMetadataAddBool (outhead, PS_LIST_TAIL, "EXTEND", PS_META_REPLACE, "this file has extensions", true);
+    psFitsWriteBlank (file->fits, outhead, "");
+    file->wrote_phu = true;
+
+    psTrace ("pmFPAfile", 5, "wrote phu %s (type: %d)\n", file->filename, file->type);
+    psFree (outhead);
+
+    return true;
+}
+
+// write out all chip-level Astrometry data for this FPA
+bool pmAstromRefstarsWriteFPA (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config) {
+
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fpa->chips, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc (view->nRows);
+    *thisView = *view;
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        thisView->chip = i;
+        if (!pmAstromRefstarsWriteChip (chip, thisView, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write %dth chip", i);
+            psFree (thisView);
+            return false;
+        }
+    }
+    psFree (thisView);
+    return true;
+}
+
+bool pmAstromRefstarsWriteChip (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config) {
+
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(chip->cells, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc (view->nRows);
+    *thisView = *view;
+
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i];
+        thisView->cell = i;
+        if (!pmAstromRefstarsWriteCell (cell, thisView, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write %dth cell", i);
+            psFree (thisView);
+            return false;
+        }
+    }
+    psFree (thisView);
+    return true;
+}
+
+// read in all readout-level Objects files for this cell
+bool pmAstromRefstarsWriteCell (pmCell *cell, const pmFPAview *view, pmFPAfile *file, const pmConfig *config) {
+
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(cell->readouts, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc (view->nRows);
+    *thisView = *view;
+
+    for (int i = 0; i < cell->readouts->n; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        thisView->readout = i;
+        if (!pmAstromRefstarsWriteReadout (readout, thisView, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write %dth readout", i);
+            psFree (thisView);
+            return false;
+        }
+    }
+    psFree (thisView);
+    return true;
+}
+
+// write out all refstars files for this readout
+bool pmAstromRefstarsWriteReadout (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, const pmConfig *config) {
+
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(readout->analysis, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    // select the raw objects for this readout
+    psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+    if (refstars == NULL) { return TRUE; }
+
+    psMetadata *header = psMetadataAlloc();
+
+    psArray *table = psArrayAllocEmpty (1);
+
+    // set the extname : we are really only allowed one entry per chip; check this here?
+    char *chiprule = psStringCopy ("{CHIP.NAME}");
+    char *chipname = pmFPAfileNameFromRule (chiprule, file, view);
+
+    for (int i = 0; i < refstars->n; i++) {
+      psMetadata *row = psMetadataAlloc ();
+
+      pmAstromObj *ref = refstars->data[i];
+
+      psMetadataAddF64(row,    PS_LIST_TAIL, "RA",      PS_META_REPLACE, "degrees", PS_DEG_RAD*ref->sky->r);
+      psMetadataAddF64(row,    PS_LIST_TAIL, "DEC",     PS_META_REPLACE, "degrees", PS_DEG_RAD*ref->sky->d);
+      psMetadataAddF32(row,    PS_LIST_TAIL, "TP_X",    PS_META_REPLACE, "microns", ref->TP->x);
+      psMetadataAddF32(row,    PS_LIST_TAIL, "TP_Y",    PS_META_REPLACE, "microns", ref->TP->y);
+      psMetadataAddF32(row,    PS_LIST_TAIL, "FP_X",    PS_META_REPLACE, "microns", ref->FP->x);
+      psMetadataAddF32(row,    PS_LIST_TAIL, "FP_Y",    PS_META_REPLACE, "microns", ref->FP->y);
+      psMetadataAddF32(row,    PS_LIST_TAIL, "CHIP_X",  PS_META_REPLACE, "microns", ref->chip->x);
+      psMetadataAddF32(row,    PS_LIST_TAIL, "CHIP_Y",  PS_META_REPLACE, "microns", ref->chip->y);
+      psMetadataAddF32(row,    PS_LIST_TAIL, "MAG",     PS_META_REPLACE, "microns", ref->Mag);
+      psMetadataAddF32(row,    PS_LIST_TAIL, "MAG_ERR", PS_META_REPLACE, "microns", ref->dMag);
+
+      psArrayAdd (table, 100, row);
+      psFree (row);
+    }
+    if (!psFitsWriteTable (file->fits, header, table, chipname)) {
+        psError(PS_ERR_IO, false, "writing refstars\n");
+        psFree (table);
+        psFree (header);
+        psFree (chiprule);
+        psFree (chipname);
+        return false;
+    }
+
+    psFree (chiprule);
+    psFree (chipname);
+    psFree (table);
+    psFree (header);
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRefstars.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRefstars.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRefstars.h	(revision 22322)
@@ -0,0 +1,34 @@
+/* @file  pmAstrometryRefstars.h
+ * @brief Functions to write (and read?) astrometric reference stars
+ *
+ * @ingroup AstroImage
+ *
+ * @author EAM, IfA
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-07-17 22:38:15 $
+ * Copyright 2008 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_ASTROMETRY_REFSTARS_H
+#define PM_ASTROMETRY_REFSTARS_H
+
+/// @addtogroup Astrometry
+/// @{
+
+bool pmAstromRefstarsCheckDataStatusForView (const pmFPAview *view, pmFPAfile *file);
+bool pmAstromRefstarsCheckDataStatusForFPA (const pmFPA *fpa);
+bool pmAstromRefstarsCheckDataStatusForChip (const pmChip *chip);
+bool pmAstromRefstarsCheckDataStatusForCell (const pmCell *cell);
+bool pmAstromRefstarsCheckDataStatusForReadout (const pmReadout *readout);
+
+bool pmAstromRefstarsWriteForView (const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmAstromRefstarsWritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+
+bool pmAstromRefstarsWriteFPA (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmAstromRefstarsWriteChip (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmAstromRefstarsWriteCell (pmCell *cell, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmAstromRefstarsWriteReadout (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRegions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRegions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRegions.c	(revision 22322)
@@ -0,0 +1,170 @@
+/** @file  pmAstrometryRegions.c
+ *  @brief functions to define astrometry regions on FPA images
+ *  @ingroup Astrometry
+ *
+ *  @author EAM, IfA
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-12-22 17:51:48 $
+ *
+ *  Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAExtent.h"
+#include "pmAstrometryRegions.h"
+
+// cell pixels corresponding to readout boundary
+psRegion *pmAstromReadoutInCell (pmReadout *readout) {
+
+    psRegion *region;
+    region = pmReadoutExtent (readout);
+    return (region);
+}
+
+// chip pixels corresponding to cell boundary
+psRegion *pmAstromCellInChip (pmCell *cell) {
+
+    psRegion *region;
+    region = pmCellExtent (cell);
+    return (region);
+}
+
+// FP pixels corresponding to chip boundary
+// since the chip may be rotated in the fpa, this region does not correspond 
+// exactly to the pixel grid of the chip
+psRegion *pmAstromChipInFP (pmChip *chip) {
+
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    // if we have not astrometry for this chip, just skip it silently (no error)
+    if (!chip->toFPA) return NULL;
+
+    // determine the bounding box of this chip in chip pixels
+    psRegion *chipExtent = pmChipPixels (chip);
+    if (!chipExtent) return NULL;
+
+    // apply chip-to-fpa astrometry to determine fpa coordinates 
+    psPlane *chPix = psPlaneAlloc ();
+    psPlane *fpPix = psPlaneAlloc ();
+    psRegion *chipRegion = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of chip
+
+    // determine the outer bounding box -- does not correspond to the same square region!
+    chPix->x = chipExtent->x0;
+    chPix->y = chipExtent->y0;
+    psPlaneTransformApply(fpPix, chip->toFPA, chPix); 
+    chipRegion->x0 = PS_MIN (chipRegion->x0, fpPix->x);
+    chipRegion->x1 = PS_MAX (chipRegion->x1, fpPix->x);
+    chipRegion->y0 = PS_MIN (chipRegion->y0, fpPix->y);
+    chipRegion->y1 = PS_MAX (chipRegion->y1, fpPix->y);
+
+    chPix->x = chipExtent->x1;
+    chPix->y = chipExtent->y0;
+    psPlaneTransformApply(fpPix, chip->toFPA, chPix); 
+    chipRegion->x0 = PS_MIN (chipRegion->x0, fpPix->x);
+    chipRegion->x1 = PS_MAX (chipRegion->x1, fpPix->x);
+    chipRegion->y0 = PS_MIN (chipRegion->y0, fpPix->y);
+    chipRegion->y1 = PS_MAX (chipRegion->y1, fpPix->y);
+
+    chPix->x = chipExtent->x1;
+    chPix->y = chipExtent->y1;
+    psPlaneTransformApply(fpPix, chip->toFPA, chPix); 
+    chipRegion->x0 = PS_MIN (chipRegion->x0, fpPix->x);
+    chipRegion->x1 = PS_MAX (chipRegion->x1, fpPix->x);
+    chipRegion->y0 = PS_MIN (chipRegion->y0, fpPix->y);
+    chipRegion->y1 = PS_MAX (chipRegion->y1, fpPix->y);
+
+    chPix->x = chipExtent->x0;
+    chPix->y = chipExtent->y1;
+    psPlaneTransformApply(fpPix, chip->toFPA, chPix); 
+    chipRegion->x0 = PS_MIN (chipRegion->x0, fpPix->x);
+    chipRegion->x1 = PS_MAX (chipRegion->x1, fpPix->x);
+    chipRegion->y0 = PS_MIN (chipRegion->y0, fpPix->y);
+    chipRegion->y1 = PS_MAX (chipRegion->y1, fpPix->y);
+
+    psFree (chPix);
+    psFree (fpPix);
+    psFree (chipExtent);
+    return (chipRegion);
+}
+
+// return FPA pixels included in all chips
+// this FPA grid has 0,0 at the mosaic center and is used for astrometric reference.
+psRegion *pmAstromFPAExtent(const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    psArray *chips = fpa->chips;       // Array of component chips
+    psRegion *fpaExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of fpa
+    for (long i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // Chip of interest
+        psRegion *chipExtent = pmAstromChipInFP(chip); // Extent of chip
+	if (!chipExtent) { continue; }
+        fpaExtent->x0 = PS_MIN(fpaExtent->x0, chipExtent->x0);
+        fpaExtent->x1 = PS_MAX(fpaExtent->x1, chipExtent->x1);
+        fpaExtent->y0 = PS_MIN(fpaExtent->y0, chipExtent->y0);
+        fpaExtent->y1 = PS_MAX(fpaExtent->y1, chipExtent->y1);
+        psFree(chipExtent);
+    }
+
+    return fpaExtent;
+}
+
+// TPA pixels corresponding to FPA boundary
+psRegion *pmAstromFPInTP (pmFPA *fpa) {
+
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa->toTPA, NULL);
+
+    psRegion *fpaExtent = pmAstromFPAExtent (fpa);
+    if (!fpaExtent) return NULL;
+
+    // apply fpa-to-tpa astrometry to determine tpa coordinates 
+    psPlane *fpPix = psPlaneAlloc ();
+    psPlane *tpPix = psPlaneAlloc ();
+    psRegion *fpaRegion = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of fpa
+
+    // determine the outer bounding box -- does not correspond to the same square region!
+    fpPix->x = fpaExtent->x0;
+    fpPix->y = fpaExtent->y0;
+    psPlaneTransformApply(tpPix, fpa->toTPA, fpPix); 
+    fpaRegion->x0 = PS_MIN (fpaRegion->x0, tpPix->x);
+    fpaRegion->x1 = PS_MAX (fpaRegion->x1, tpPix->x);
+    fpaRegion->y0 = PS_MIN (fpaRegion->y0, tpPix->y);
+    fpaRegion->y1 = PS_MAX (fpaRegion->y1, tpPix->y);
+
+    fpPix->x = fpaExtent->x1;
+    fpPix->y = fpaExtent->y0;
+    psPlaneTransformApply(tpPix, fpa->toTPA, fpPix); 
+    fpaRegion->x0 = PS_MIN (fpaRegion->x0, tpPix->x);
+    fpaRegion->x1 = PS_MAX (fpaRegion->x1, tpPix->x);
+    fpaRegion->y0 = PS_MIN (fpaRegion->y0, tpPix->y);
+    fpaRegion->y1 = PS_MAX (fpaRegion->y1, tpPix->y);
+
+    fpPix->x = fpaExtent->x1;
+    fpPix->y = fpaExtent->y1;
+    psPlaneTransformApply(tpPix, fpa->toTPA, fpPix); 
+    fpaRegion->x0 = PS_MIN (fpaRegion->x0, tpPix->x);
+    fpaRegion->x1 = PS_MAX (fpaRegion->x1, tpPix->x);
+    fpaRegion->y0 = PS_MIN (fpaRegion->y0, tpPix->y);
+    fpaRegion->y1 = PS_MAX (fpaRegion->y1, tpPix->y);
+
+    fpPix->x = fpaExtent->x0;
+    fpPix->y = fpaExtent->y1;
+    psPlaneTransformApply(tpPix, fpa->toTPA, fpPix); 
+    fpaRegion->x0 = PS_MIN (fpaRegion->x0, tpPix->x);
+    fpaRegion->x1 = PS_MAX (fpaRegion->x1, tpPix->x);
+    fpaRegion->y0 = PS_MIN (fpaRegion->y0, tpPix->y);
+    fpaRegion->y1 = PS_MAX (fpaRegion->y1, tpPix->y);
+
+    psFree (fpPix);
+    psFree (tpPix);
+    psFree (fpaExtent);
+    return (fpaRegion);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRegions.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRegions.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryRegions.h	(revision 22322)
@@ -0,0 +1,35 @@
+/* @file  pmAstrometryRegion.h
+ * @brief functions to detemine fpa,chip,etc boundaries from astrometry
+ *
+ * @author EAM, IfA
+ * @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-21 21:59:57 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_ASTROMETRY_REGIONS_H
+#define PM_ASTROMETRY_REGIONS_H
+
+/// @addtogroup Astrometry
+/// @{
+
+// cell pixels corresponding to readout boundary
+psRegion *pmAstromReadoutInCell (pmReadout *readout);
+
+// chip pixels corresponding to cell boundary
+psRegion *pmAstromCellInChip (pmCell *cell);
+
+// FP pixels corresponding to chip boundary
+// since the chip may be rotated in the fpa, this region does not correspond 
+// exactly to the pixel grid of the chip
+psRegion *pmAstromChipInFP (pmChip *chip);
+
+// return FPA pixels included in all chips
+// this FPA grid has 0,0 at the mosaic center and is used for astrometric reference.
+psRegion *pmAstromFPAExtent(const pmFPA *fpa);
+
+// chip pixels corresponding to cell boundary
+psRegion *pmAstromFPInTP (pmFPA *fpa);
+
+/// @}
+#endif // PM_ASTROMETRY_DISTORTION_H
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryUtils.c	(revision 22322)
@@ -0,0 +1,386 @@
+/** @file  pmAstrometryUtils.c
+ *
+ *  @brief utility functions for transform and distort functions
+ *
+ *  @ingroup Astrometry
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-12 01:05:24 $
+ *
+ *  Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmAstrometryUtils.h"
+
+// given a 2D transformation -- L(x,y),M(x,y) -- find the coordinates x,y
+// for which L,M = 0,0. tol is the allowed error on x,y.
+psPlane *psPlaneTransformGetCenter (psPlaneTransform *trans, double tol)
+{
+
+    // crpix1,2 = X,Y(crval1,2)
+    // start with linear solution for Xo,Yo:
+    double R  = (trans->x->coeff[1][0]*trans->y->coeff[0][1] - trans->x->coeff[0][1]*trans->y->coeff[1][0]);
+    double Xo = (trans->y->coeff[0][0]*trans->x->coeff[0][1] - trans->x->coeff[0][0]*trans->y->coeff[0][1])/R;
+    double Yo = (trans->x->coeff[0][0]*trans->y->coeff[1][0] - trans->y->coeff[0][0]*trans->x->coeff[1][0])/R;
+
+    // iterate to actual solution: requires small non-linear terms
+    if (trans->x->nX > 1) {
+        psPolynomial2D *XdX = psPolynomial2D_dX(NULL, trans->x);
+        psPolynomial2D *XdY = psPolynomial2D_dY(NULL, trans->x);
+
+        psPolynomial2D *YdX = psPolynomial2D_dX(NULL, trans->y);
+        psPolynomial2D *YdY = psPolynomial2D_dY(NULL, trans->y);
+
+        psImage *Alpha = psImageAlloc (2, 2, PS_DATA_F32);
+        psVector *Beta = psVectorAlloc (2, PS_DATA_F32);
+
+        /* this loop uses the Newton-Raphson method to solve for Xo,Yo
+        * it needs the high order terms to be small 
+        * Xo,Yo are in pixels;
+        */
+        double dPos = tol + 1;
+        for (int i = 0; (dPos > tol) && (i < 20); i++) {
+            // NOTE: order for Alpha is: [y][x]
+            Alpha->data.F32[0][0] = psPolynomial2DEval (XdX, Xo, Yo);
+            Alpha->data.F32[1][0] = psPolynomial2DEval (XdY, Xo, Yo);
+            Alpha->data.F32[0][1] = psPolynomial2DEval (YdX, Xo, Yo);
+            Alpha->data.F32[1][1] = psPolynomial2DEval (YdY, Xo, Yo);
+
+            Beta->data.F32[0] = psPolynomial2DEval (trans->x, Xo, Yo);
+            Beta->data.F32[1] = psPolynomial2DEval (trans->y, Xo, Yo);
+
+            if (!psMatrixGJSolveF32 (Alpha, Beta)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to solve for center.");
+		return NULL;
+	    }
+
+            Xo -= Beta->data.F32[0];
+            Yo -= Beta->data.F32[1];
+            dPos = hypot(Beta->data.F32[0], Beta->data.F32[1]);
+
+        }
+        psFree (Alpha);
+        psFree (Beta);
+        psFree (XdX);
+        psFree (XdY);
+        psFree (YdX);
+        psFree (YdY);
+
+	if (dPos > tol) {
+	    psError(PS_ERR_UNKNOWN, false, "Newton-Raphson method did not converge after 20 iterations (%f)\n", dPos);
+	    return NULL;
+	}
+    }
+    psPlane *center = psPlaneAlloc ();
+    center->x = Xo;
+    center->y = Yo;
+
+    return center;
+}
+
+// convert a transformation L(x,y) to L'(x-xo,y-yo)
+psPlaneTransform *psPlaneTransformSetCenter (psPlaneTransform *output, psPlaneTransform *input, double Xo, double Yo)
+{
+
+    // validate fit order
+
+    if (output == NULL) {
+        output = psPlaneTransformAlloc(input->x->nX, input->x->nY);
+    }
+
+    /* given two equivalent polynomial representations L(x,y) = \sum_i \sum_j A_{i,j} x^i y^j
+     * we can transform L(x,y) into L'(x-xo,y-yo) by taking the derivatives of both sides and 
+     * noting that the constant term in each is the coefficient in the case of L(x,y) and is the 
+     * value of L'(-xo,-yo) in the second case.
+     */
+
+    psPolynomial2D *tmp;
+
+    psPolynomial2D *xPx = psPolynomial2DCopy (NULL, input->x);
+    psPolynomial2D *yPx = psPolynomial2DCopy (NULL, input->y);
+
+    for (int i = 0; i <= input->x->nX; i++) {
+        psPolynomial2D *xPy = psPolynomial2DCopy (NULL, xPx);
+        psPolynomial2D *yPy = psPolynomial2DCopy (NULL, yPx);
+        for (int j = 0; j <= input->x->nY; j++) {
+            output->x->coeffMask[i][j] = input->x->coeffMask[i][j];
+            output->y->coeffMask[i][j] = input->y->coeffMask[i][j];
+            output->x->coeff[i][j] = (output->x->coeffMask[i][j] & PS_POLY_MASK_SET) ? 0 : psPolynomial2DEval (xPy, Xo, Yo) / tgamma(i+1) / tgamma(j+1);
+            output->y->coeff[i][j] = (output->y->coeffMask[i][j] & PS_POLY_MASK_SET) ? 0 : psPolynomial2DEval (yPy, Xo, Yo) / tgamma(i+1) / tgamma(j+1);
+
+            // take the next derivative wrt y, catch output (is NULL on last pass)
+            tmp = psPolynomial2D_dY(NULL, xPy);
+            psFree (xPy);
+            xPy = tmp;
+            tmp = psPolynomial2D_dY(NULL, yPy);
+            psFree (yPy);
+            yPy = tmp;
+        }
+        // take the next derivative wrt x, catch output (is NULL on last pass)
+        tmp = psPolynomial2D_dX(NULL, xPx);
+        psFree (xPx);
+        xPx = tmp;
+        tmp = psPolynomial2D_dX(NULL, yPx);
+        psFree (yPx);
+        yPx = tmp;
+    }
+    return output;
+}
+
+// rotate a transformation L(x,y) by theta
+psPlaneTransform *psPlaneTransformRotate (psPlaneTransform *output, psPlaneTransform *input, double theta)
+{
+    /* given the polynomial transformations:
+     *  L(x,y) = \sum_i \sum_j A_{i,j} x^i y^j and 
+     *  M(x,y) = \sum_i \sum_j B_{i,j} x^i y^j 
+     * we can rotate L,M to L',M' by applying the rotation matrix (c,s),(-s,c).
+     * the resulting terms of L and M are:
+     * A'_{i,j} = c A_{i,j} + s B_{i,j}
+     * B'_{i,j} = c B_{i,j} - s A_{i,j}
+     */
+
+    if (output == NULL) {
+        output = psPlaneTransformAlloc(input->x->nX, input->x->nY);
+    }
+
+    float cs = cos(theta);
+    float sn = sin(theta);
+
+    for (int i = 0; i <= input->x->nX; i++) {
+        for (int j = 0; j <= input->x->nY; j++) {
+	    // XXX what about inconsistent x and y masking?
+            output->x->coeffMask[i][j] = input->x->coeffMask[i][j];
+	    output->y->coeffMask[i][j] = input->y->coeffMask[i][j];
+	    if (output->x->coeffMask[i][j]) {
+		output->x->coeff[i][j] = 0.0;
+		output->y->coeff[i][j] = 0.0;
+	    } else {
+		output->x->coeff[i][j] = cs*input->x->coeff[i][j] + sn*input->y->coeff[i][j];
+		output->y->coeff[i][j] = cs*input->y->coeff[i][j] - sn*input->x->coeff[i][j];
+	    }
+        }
+    }
+    return output;
+}
+
+// construct a psPlaneTransform which is the identify transformation
+psPlaneTransform *psPlaneTransformIdentity (int order)
+{
+
+    psPlaneTransform *transform;
+
+    if (order < 1)
+        psAbort("invalid order");
+    if (order > 3)
+        psAbort("invalid order");
+
+    // all coeffs and masks initially set to 0
+    transform = psPlaneTransformAlloc (order, order);
+
+    for (int i = 0; i <= order; i++) {
+        for (int j = 0; j <= order; j++) {
+            if (i + j > order) {
+                transform->x->coeffMask [i][j] = PS_POLY_MASK_SET;
+                transform->y->coeffMask [i][j] = PS_POLY_MASK_SET;
+            }
+        }
+    }
+    transform->x->coeff[1][0] = 1;
+    transform->y->coeff[0][1] = 1;
+    transform->x->coeffMask[1][0] = PS_POLY_MASK_NONE;
+    transform->y->coeffMask[0][1] = PS_POLY_MASK_NONE;
+
+    return transform;
+}
+
+// check that the given psPlaneTransform is the identity * (Xs,Ys)
+bool psPlaneTransformIsDiagonal (psPlaneTransform *transform)
+{
+
+    int order;
+    bool status;
+
+    // we currently only support up to 3rd order polynomials
+    if (transform->x->nX < 1)
+        return false;
+    if (transform->x->nY < 1)
+        return false;
+    if (transform->y->nX < 1)
+        return false;
+    if (transform->y->nY < 1)
+        return false;
+
+    if (transform->x->nX != transform->x->nY)
+        return false;
+    if (transform->y->nX != transform->y->nY)
+        return false;
+
+    // these are not actually valid tests
+    if (transform->x->nX > 3)
+        return false;
+    if (transform->x->nY > 3)
+        return false;
+    if (transform->y->nX > 3)
+        return false;
+    if (transform->y->nY > 3)
+        return false;
+
+    status = true;
+    order = transform->x->nX;
+    for (int i = 0; i <= order; i++) {
+        for (int j = 0; j <= order; j++) {
+            if (i + j > order) {
+                // high-order cross terms must be masked (eg, x^3 y^2)
+                status &= (transform->x->coeffMask[i][j] & PS_POLY_MASK_SET);
+            } else {
+                status &= !(transform->x->coeffMask[i][j] & PS_POLY_MASK_SET);
+                if ((i == 1) && (i + j == 1)) {
+                    // linear, diagonal terms must be non-zero
+                    status &= (fabs(transform->x->coeff[i][j]) > FLT_EPSILON);
+                } else {
+                    // non-linear and off-diagonal terms must be 0 (eg, x^2, x y)
+                    status &= (fabs(transform->x->coeff[i][j]) < FLT_EPSILON);
+                }
+            }
+        }
+    }
+
+    order = transform->y->nX;
+    for (int i = 0; i <= order; i++) {
+        for (int j = 0; j <= order; j++) {
+            if (i + j > order) {
+                // high-order cross terms must be masked (eg, x^3 y^2)
+                status &= (transform->y->coeffMask[i][j] & PS_POLY_MASK_SET);
+            } else {
+                status &= !(transform->y->coeffMask[i][j] & PS_POLY_MASK_SET);
+                if ((j == 1) && (i + j == 1)) {
+                    // linear, diagonal terms must be 1.0
+                    status &= (fabs(transform->y->coeff[i][j]) > FLT_EPSILON);
+                } else {
+                    // non-linear and off-diagonal terms must be 0 (eg, x^2, x y)
+                    status &= (fabs(transform->y->coeff[i][j]) < FLT_EPSILON);
+                }
+            }
+        }
+    }
+    return status;
+}
+
+// construct a psPlaneDistort which is the identify transformation
+psPlaneDistort *psPlaneDistortIdentity (int order)
+{
+
+    psPlaneDistort *distort;
+
+    if (order < 1)
+        psAbort("invalid order");
+    if (order > 3)
+        psAbort("invalid order");
+
+    // all coeffs and masks initially set to 0
+    distort = psPlaneDistortAlloc (order, order, 0, 0);
+
+    for (int i = 0; i <= order; i++) {
+        for (int j = 0; j <= order; j++) {
+            if (i + j > order) {
+                distort->x->coeffMask [i][j][0][0] = PS_POLY_MASK_SET;
+                distort->y->coeffMask [i][j][0][0] = PS_POLY_MASK_SET;
+            }
+        }
+    }
+    distort->x->coeff[1][0][0][0] = 1;
+    distort->y->coeff[0][1][0][0] = 1;
+
+    return distort;
+}
+
+// check that the given psPlaneDistort is the identity * (Xs,Ys)
+bool psPlaneDistortIsDiagonal (psPlaneDistort *distort)
+{
+
+    int order;
+    bool status;
+
+    // we currently only support up to 3rd order polynomials
+    if (distort->x->nX < 1)
+        return false;
+    if (distort->x->nY < 1)
+        return false;
+    if (distort->y->nX < 1)
+        return false;
+    if (distort->y->nY < 1)
+        return false;
+
+    if (distort->x->nX > 3)
+        return false;
+    if (distort->x->nY > 3)
+        return false;
+    if (distort->y->nX > 3)
+        return false;
+    if (distort->y->nY > 3)
+        return false;
+
+    if (distort->x->nZ > 0)
+        return false;
+    if (distort->x->nT > 0)
+        return false;
+    if (distort->y->nZ > 0)
+        return false;
+    if (distort->y->nT > 0)
+        return false;
+
+    if (distort->x->nX != distort->x->nY)
+        return false;
+    if (distort->y->nX != distort->y->nY)
+        return false;
+
+    status = true;
+    order = distort->x->nX;
+    for (int i = 0; i <= order; i++) {
+        for (int j = 0; j <= order; j++) {
+            if (i + j > order) {
+                // high-order cross terms must be masked (eg, x^3 y^2)
+                status &= (distort->x->coeffMask[i][j][0][0] & PS_POLY_MASK_SET);
+            } else {
+                status &= !(distort->x->coeffMask[i][j][0][0] & PS_POLY_MASK_SET);
+                if ((i == 1) && (i + j == 1)) {
+                    // linear, diagonal terms must be 1.0
+                    status &= (fabs(distort->x->coeff[i][j][0][0]) > FLT_EPSILON);
+                } else {
+                    // non-linear and off-diagonal terms must be 0 (eg, x^2, x y)
+                    status &= (fabs(distort->x->coeff[i][j][0][0]) < FLT_EPSILON);
+                }
+            }
+        }
+    }
+
+    order = distort->y->nX;
+    for (int i = 0; i <= order; i++) {
+        for (int j = 0; j <= order; j++) {
+            if (i + j > order) {
+                // high-order cross terms must be masked (eg, x^3 y^2)
+                status &= (distort->y->coeffMask[i][j][0][0] & PS_POLY_MASK_SET);
+            } else {
+                status &= !(distort->y->coeffMask[i][j][0][0] & PS_POLY_MASK_SET);
+                if ((j == 1) && (i + j == 1)) {
+                    // linear, diagonal terms must be 1.0
+                    status &= (fabs(distort->y->coeff[i][j][0][0]) > FLT_EPSILON);
+                } else {
+                    // non-linear and off-diagonal terms must be 0 (eg, x^2, x y)
+                    status &= (fabs(distort->y->coeff[i][j][0][0]) < FLT_EPSILON);
+                }
+            }
+        }
+    }
+    return status;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryUtils.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryUtils.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryUtils.h	(revision 22322)
@@ -0,0 +1,28 @@
+/* @file  pmAstrometryUtils.h
+ * @brief utility functions for transform and distort functions
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-12-19 18:57:05 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_ASTROMETRY_UTILS_H
+#define PM_ASTROMETRY_UTILS_H
+
+/// @addtogroup Astrometry
+/// @{
+
+psPlane *psPlaneTransformGetCenter (psPlaneTransform *trans, double tol);
+psPlaneTransform *psPlaneTransformSetCenter (psPlaneTransform *output, psPlaneTransform *input, double Xo, double Yo);
+psPlaneTransform *psPlaneTransformRotate (psPlaneTransform *output, psPlaneTransform *input, double theta);
+
+psPlaneTransform *psPlaneTransformIdentity (int order);
+psPlaneDistort *psPlaneDistortIdentity (int order);
+
+bool psPlaneTransformIsDiagonal (psPlaneTransform *transform);
+bool psPlaneDistortIsDiagonal (psPlaneDistort *distort);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryWCS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryWCS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryWCS.c	(revision 22322)
@@ -0,0 +1,829 @@
+/** @file  pmAstrometryWCS.c
+ *
+ *  @brief functions to convert FITS WCS keywords to / from pmFPA structures
+ *
+ *  @ingroup Astrometry
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.29 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-12 01:05:59 $
+ *
+ *  Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <strings.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAExtent.h"
+#include "pmAstrometryWCS.h"
+#include "pmAstrometryUtils.h"
+#include "pmAstrometryRegions.h"
+
+// the following functions support coordinate transformations direcly related to the FITS WCS
+// keywords.  The FITS WCS allows for only a single level of transformation, thus it is not
+// appropriate for mosaic astrometry consisting of telescope distortion plus chip terms.
+// Below, we support the Elixir convention of using two connected FITS headers to define two
+// levels of coordinate transformation.  In the pmFPA structure, the projection, distortion,
+// and FPA-to-Chip transformations are carried independently.  NOTE: The FITS WCS keywords do
+// not represent a simple polynomial.  Instead, they have no constant term, and the coordinates
+// are corrected to a reference pixel before the polynomial transformation is applied.
+
+// interpret header WCS (only handles traditional WCS for the moment)
+// pixelScale is microns per pixel
+bool pmAstromReadWCS (pmFPA *fpa, pmChip *chip, const psMetadata *header, double pixelScale)
+{
+    pmAstromWCS *wcs = pmAstromWCSfromHeader (header);
+    if (!wcs) {
+        return false;
+    }
+
+    bool status = pmAstromWCStoFPA (fpa, chip, wcs, pixelScale);
+
+    psFree (wcs);
+    return status;
+}
+
+// convert toFPA / toSky components to pmAstromWCS
+// tolerance is convergence for inversion of non-linear terms in pixels
+bool pmAstromWriteWCS (psMetadata *header, const pmFPA *fpa, const pmChip *chip, double tol)
+{
+    pmAstromWCS *wcs = pmAstromWCSfromFPA(fpa, chip, tol);
+    if (!wcs) return false;
+
+    pmAstromWCStoHeader (header, wcs);
+
+    psFree (wcs);
+    return true;
+}
+
+// interpret chip header WCS as bilevel chip components
+bool pmAstromReadBilevelChip (pmChip *chip, const psMetadata *header)
+{
+    pmAstromWCS *wcs = pmAstromWCSfromHeader (header);
+    if (!wcs) {
+        return false;
+    }
+
+    bool status = pmAstromWCSBileveltoChip (chip, wcs);
+
+    psFree (wcs);
+    return status;
+}
+
+// convert toFPA / toSky components to traditional WCS
+// we require the header to have NAXIS1,NAXIS2, the field of the FPA 
+// the center of the TPA/Sky projection is 0.5*(NAXIS1,NAXIS2)
+bool pmAstromReadBilevelMosaic (pmFPA *fpa, const psMetadata *header)
+{
+    pmAstromWCS *wcs = pmAstromWCSfromHeader (header);
+    if (!wcs) {
+        psError(PS_ERR_UNKNOWN, false, "failure to determine WCS terms from header");
+        return false;
+    }
+
+    bool status1 = false;
+    bool status2 = false;
+    int Nx = psMetadataLookupS32 (&status1, header, "NAXIS1");
+    int Ny = psMetadataLookupS32 (&status2, header, "NAXIS2");
+
+    if (!status1 || !status2) {
+	Nx = psMetadataLookupS32 (&status1, header, "ZNAXIS1");
+	Ny = psMetadataLookupS32 (&status2, header, "ZNAXIS2");
+    }
+
+    if (!status1 || !status2) {
+	psFree (wcs);
+        psError(PS_ERR_UNKNOWN, false, "missing required FPA size in header");
+        return false;
+    }
+
+    psRegion region = psRegionSet (-0.5*Nx, +0.5*Nx, -0.5*Ny, +0.5*Ny);
+    bool status = pmAstromWCSBileveltoFPA (fpa, wcs, region);
+
+    psFree (wcs);
+    return status;
+}
+
+// convert chip->toFPA components to bilevel WCS
+bool pmAstromWriteBilevelChip (psMetadata *header, const pmChip *chip, double tol)
+{
+    pmAstromWCS *wcs = pmAstromWCSBilevelChipFromFPA (chip, tol);
+    if (!wcs) {
+        psError(PS_ERR_UNKNOWN, false, "failure to determine WCS terms from fpa");
+        return false;
+    }
+
+    pmAstromWCStoHeader (header, wcs);
+
+    psFree (wcs);
+    return true;
+}
+
+
+// convert fpa->toTPA, fpa->toSky components to bilevel WCS
+bool pmAstromWriteBilevelMosaic (psMetadata *header, const pmFPA *fpa, double tol)
+{
+    pmAstromWCS *wcs = pmAstromWCSBilevelMosaicFromFPA (fpa, tol);
+    if (!wcs) {
+        psError(PS_ERR_UNKNOWN, false, "failure to determine WCS terms from fpa");
+        return false;
+    }
+
+    // we need to specify the dimensions of the FPA
+    // if we have chips defined, we can do
+    psRegion *region = pmAstromFPAExtent (fpa);
+    int Nx = region->x1 - region->x0;
+    int Ny = region->y1 - region->y0;
+    psMetadataAddS32 (header, PS_LIST_TAIL, "NAXIS1", PS_META_REPLACE, "Mosaic Dimensions", Nx);
+    psMetadataAddS32 (header, PS_LIST_TAIL, "NAXIS2", PS_META_REPLACE, "Mosaic Dimensions", Ny);
+
+    pmAstromWCStoHeader (header, wcs);
+
+    psFree (region);
+    psFree (wcs);
+    return true;
+}
+
+// convert coordinates from chip to sky using a pmAstromWCS structure
+bool pmAstromWCStoSky (psSphere *sky, pmAstromWCS *wcs, psPlane *chip)
+{
+
+    if (chip == NULL)
+        return false;
+    if (sky == NULL)
+        return false;
+    if (wcs == NULL)
+        return false;
+
+    psPlane *Chip = psPlaneAlloc();
+    psPlane *FP = psPlaneAlloc();
+
+    Chip->x = chip->x - wcs->crpix1;
+    Chip->y = chip->y - wcs->crpix2;
+
+    psPlaneTransformApply (FP, wcs->trans, Chip);
+    psDeproject (sky, FP, wcs->toSky); // find the RA,DEC coord of the focal-plane coordinate
+
+    psFree (Chip);
+    psFree (FP);
+    return true;
+}
+
+// convert coordinates from sky to chip using a pmAstromWCS structure
+bool pmAstromWCStoChip (psPlane *chip, pmAstromWCS *wcs, psSphere *sky)
+{
+
+    if (chip == NULL)
+        return false;
+    if (sky == NULL)
+        return false;
+    if (wcs == NULL)
+        return false;
+
+    psError(PS_ERR_UNKNOWN, true, "not yet implemented: needs to invert the transformation");
+    return false;
+
+    psPlane *Chip = psPlaneAlloc();
+    psPlane *FP = psPlaneAlloc();
+
+    psProject (FP, sky, wcs->toSky); // find the RA,DEC coord of the focal-plane coordinate
+
+    // XXX I actually need the inverse of wcs->transform at this point
+    psPlaneTransformApply (Chip, wcs->trans, FP);
+
+    chip->x = Chip->x + wcs->crpix1;
+    chip->y = Chip->y + wcs->crpix2;
+
+    psFree (Chip);
+    psFree (FP);
+    return true;
+}
+
+// interpret header WCS keywords (valid for bilevel and traditional WCS)
+pmAstromWCS *pmAstromWCSfromHeader (const psMetadata *header)
+{
+    psProjectionType type;
+    bool status, pcKeys, cdKeys, isPoly;
+    char name[16]; // used to store FITS keyword below (always < 8, so 16 should be safe!)
+
+    // interpret header data, convert to crval(i), etc
+    char *ctype = psMetadataLookupPtr (&status, header, "CTYPE2");
+    if (!status) {
+        psLogMsg ("psastro", 5, "warning: no WCS metadata in header\n");
+        return NULL;
+    }
+
+    // determine projection type
+    // XXX there are two indications for higher-order terms: the type (DIS,WRP,PLY,ZPL) and
+    // the value of NPLYTERM.
+    type = psProjectTypeFromString (ctype);
+    if (type == PS_PROJ_NTYPE) {
+        psLogMsg ("psastro", 2, "warning: unknown projection type %s\n", ctype);
+        return NULL;
+    }
+
+    // what type of WCS keywords are available?
+    // XXX add check for CROTA2
+    int fitOrder = psMetadataLookupS32 (&isPoly, header, "NPLYTERM");
+    psMetadataLookupF64 (&pcKeys, header, "PC001001");
+    psMetadataLookupF64 (&cdKeys, header, "CD1_1");
+
+    if (cdKeys && pcKeys) {
+        // XXX make this an option
+        psLogMsg ("psastro", 5, "warning: both CDi_j and PC00i00j defined in headers, using PC00i00j terms\n");
+    }
+    if (!cdKeys && !pcKeys) {
+        psError(PS_ERR_UNKNOWN, true, "missing both CDi_j and PC00i00j WCS terms");
+        // XXX we could default here to RA, DEC, ROTANGLE
+        return NULL;
+    }
+    if (isPoly) {
+        if (!pcKeys) {
+            psError(PS_ERR_UNKNOWN, true, "polynomial terms defined, but missing PC00i00j WCS terms");
+            return NULL;
+        }
+        if (fitOrder == 0)
+            fitOrder = 1;
+        if ((fitOrder > 3) || (fitOrder < 1)) {
+            psError(PS_ERR_UNKNOWN, true, "NPLYTERM value undefined: %d", fitOrder);
+            return NULL;
+        }
+    } else {
+        fitOrder = 1;
+    }
+
+    pmAstromWCS *wcs = pmAstromWCSAlloc (fitOrder, fitOrder);
+
+    // construct a transformation from X,Y in pixels to L,M in pixels
+    // NOTE that the WCS keywords convert X,Y to degrees first (using cdelt1,2)
+    // and then define a transformation from degrees to degrees
+
+    wcs->crval1 = psMetadataLookupF64 (&status, header, "CRVAL1");
+    wcs->crval2 = psMetadataLookupF64 (&status, header, "CRVAL2");
+    wcs->crpix1 = psMetadataLookupF64 (&status, header, "CRPIX1");
+    wcs->crpix2 = psMetadataLookupF64 (&status, header, "CRPIX2");
+    wcs->toSky = psProjectionAlloc (wcs->crval1*PM_RAD_DEG, wcs->crval2*PM_RAD_DEG, PM_RAD_DEG, PM_RAD_DEG, type);
+    // XXX I think this is wrong for linear proj
+
+    // test the CDELTi varient
+    if (pcKeys) {
+        wcs->cdelt1 = psMetadataLookupF64 (&status, header, "CDELT1");
+        wcs->cdelt2 = psMetadataLookupF64 (&status, header, "CDELT2");
+
+        // test the CROTAi varient:
+        // XXX double check lambda..
+        double rotate = psMetadataLookupF64 (&status, header, "CROTA2");
+        if (status) {
+            wcs->trans->x->coeff[1][0] = +wcs->cdelt1 * cos(rotate*PM_RAD_DEG); // == PC1_1
+            wcs->trans->x->coeff[0][1] = -wcs->cdelt2 * sin(rotate*PM_RAD_DEG); // == PC1_2
+            wcs->trans->y->coeff[1][0] = +wcs->cdelt1 * sin(rotate*PM_RAD_DEG); // == PC2_1
+            wcs->trans->y->coeff[0][1] = +wcs->cdelt2 * cos(rotate*PM_RAD_DEG); // == PC2_2
+            return wcs;
+        }
+
+        // FITS WCS PCi,j has units of unity
+        // wcs->trans has units of degrees/pixel
+        wcs->trans->x->coeff[1][0] = wcs->cdelt1 * psMetadataLookupF64 (&status, header, "PC001001"); // == PC1_1
+        wcs->trans->x->coeff[0][1] = wcs->cdelt2 * psMetadataLookupF64 (&status, header, "PC001002"); // == PC1_2
+        wcs->trans->y->coeff[1][0] = wcs->cdelt1 * psMetadataLookupF64 (&status, header, "PC002001"); // == PC2_1
+        wcs->trans->y->coeff[0][1] = wcs->cdelt2 * psMetadataLookupF64 (&status, header, "PC002002"); // == PC2_2
+
+        if (isPoly) {
+            // Elixir-style polynomial terms
+            // XXX currently, Elixir/DVO cannot accept mixed orders
+            for (int i = 0; i <= fitOrder; i++) {
+                for (int j = 0; j <= fitOrder; j++) {
+                    if (i + j < 2)
+                        continue;
+                    if (i + j > fitOrder) {
+                        wcs->trans->x->coeffMask[i][j] = PS_POLY_MASK_SET;
+                        wcs->trans->y->coeffMask[i][j] = PS_POLY_MASK_SET;
+                        continue;
+                    }
+                    sprintf (name, "PCA1X%1dY%1d", i, j);
+                    wcs->trans->x->coeff[i][j] = pow(wcs->cdelt1, i) * pow(wcs->cdelt2, j) * psMetadataLookupF64 (&status, header, name);
+                    sprintf (name, "PCA2X%1dY%1d", i, j);
+                    wcs->trans->y->coeff[i][j] = pow(wcs->cdelt1, i) * pow(wcs->cdelt2, j) * psMetadataLookupF64 (&status, header, name);
+                }
+            }
+        }
+        return wcs;
+    }
+
+    // test the CDi_j varient
+    if (cdKeys) {
+        wcs->trans->x->coeff[1][0] = psMetadataLookupF64 (&status, header, "CD1_1"); // == PC1_1
+        wcs->trans->x->coeff[0][1] = psMetadataLookupF64 (&status, header, "CD1_2"); // == PC1_2
+        wcs->trans->y->coeff[1][0] = psMetadataLookupF64 (&status, header, "CD2_1"); // == PC2_1
+        wcs->trans->y->coeff[0][1] = psMetadataLookupF64 (&status, header, "CD2_2"); // == PC2_2
+        wcs->cdelt1 = hypot (wcs->trans->x->coeff[1][0], wcs->trans->x->coeff[0][1]);
+        wcs->cdelt2 = hypot (wcs->trans->y->coeff[1][0], wcs->trans->y->coeff[0][1]);
+        return wcs;
+    }
+    psLogMsg ("psastro", 2, "warning: missing rotation matrix?\n");
+    psFree (wcs);
+    return NULL;
+}
+
+// convert wcs transformations into header WCS keywords (only handles traditional WCS for the moment)
+// wcs->trans defines the transformation from pixels to degrees.
+// wcs->cdelt1,2 carries the original pixels scale.
+// XXX force PC00i00j to be normalized, or use cdelt1,2 to set the scale?
+// here I've chosen to force the rotation matrix to be normalized
+bool pmAstromWCStoHeader (psMetadata *header, const pmAstromWCS *wcs)
+{
+    char name[16]; // used to store FITS keyword below (always < 8, so 16 should be safe!)
+    char *type;
+
+    if (!wcs) return false;
+
+    type = psProjectTypeToString (wcs->toSky->type, "RA--");
+    psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE1", PS_META_REPLACE, "", type);
+    psFree (type);
+
+    type = psProjectTypeToString (wcs->toSky->type, "DEC-");
+    psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE2", PS_META_REPLACE, "", type);
+    psFree (type);
+
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CRVAL1", PS_META_REPLACE, "", wcs->toSky->R*PS_DEG_RAD);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CRVAL2", PS_META_REPLACE, "", wcs->toSky->D*PS_DEG_RAD);
+
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CRPIX1", PS_META_REPLACE, "", wcs->crpix1);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CRPIX2", PS_META_REPLACE, "", wcs->crpix2);
+
+    // XXX make it optional to write out CDi_j terms, or other versions
+    // apply CDELT1,2 (degrees / pixel) to yield PCi,j terms of order unity
+    double cdelt1 = wcs->cdelt1;
+    double cdelt2 = wcs->cdelt2;
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CDELT1", PS_META_REPLACE, "", cdelt1);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CDELT2", PS_META_REPLACE, "", cdelt2);
+
+    // test the PC00i00j varient:
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PC001001", PS_META_REPLACE, "", wcs->trans->x->coeff[1][0] / cdelt1); // == PC1_1
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PC001002", PS_META_REPLACE, "", wcs->trans->x->coeff[0][1] / cdelt2); // == PC1_2
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PC002001", PS_META_REPLACE, "", wcs->trans->y->coeff[1][0] / cdelt1); // == PC2_1
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PC002002", PS_META_REPLACE, "", wcs->trans->y->coeff[0][1] / cdelt2); // == PC2_2
+
+    // Elixir-style polynomial terms
+    // XXX currently, Elixir/DVO cannot accept mixed orders
+    // XXX need to respect the masks
+    // XXX is wcs->cdelt1,2 always consistent?
+    int fitOrder = wcs->trans->x->nX;
+    if (fitOrder > 1) {
+        for (int i = 0; i <= fitOrder; i++) {
+            for (int j = 0; j <= fitOrder; j++) {
+                if (i + j < 2)
+                    continue;
+                if (i + j > fitOrder)
+                    continue;
+                sprintf (name, "PCA1X%1dY%1d", i, j);
+                psMetadataAddF64 (header, PS_LIST_TAIL, name, PS_META_REPLACE, "", wcs->trans->x->coeff[i][j] / pow(cdelt1, i) / pow(cdelt2, j));
+                sprintf (name, "PCA2X%1dY%1d", i, j);
+                psMetadataAddF64 (header, PS_LIST_TAIL, name, PS_META_REPLACE, "", wcs->trans->y->coeff[i][j] / pow(cdelt1, i) / pow(cdelt2, j));
+            }
+        }
+        psMetadataAddS32 (header, PS_LIST_TAIL, "NPLYTERM", PS_META_REPLACE, "", fitOrder);
+    }
+
+    return true;
+}
+
+// interpret header WCS (only handles traditional WCS for the moment)
+// pixelScale is the pixel size in microns/pixel
+bool pmAstromWCStoFPA (pmFPA *fpa, pmChip *chip, const pmAstromWCS *wcs, double pixelScale)
+{
+    psPlaneTransform *toFPA;
+
+    // create transformation with 0,0 reference pixel and units of degrees/pixel
+    toFPA = psPlaneTransformSetCenter (NULL, wcs->trans, -wcs->crpix1, -wcs->crpix2);
+
+    // modify scale of toFPA to have units of microns/pixel
+    // cdelt1,2 has units of degree/pixel
+    for (int i = 0; i <= toFPA->x->nX; i++) {
+        for (int j = 0; j <= toFPA->x->nX; j++) {
+            toFPA->x->coeff[i][j] *= pixelScale/wcs->cdelt1;
+            toFPA->y->coeff[i][j] *= pixelScale/wcs->cdelt2;
+        }
+    }
+
+    // pdelt1,2 has units of degree/micron
+    double pdelt1 = wcs->cdelt1 / pixelScale;
+    double pdelt2 = wcs->cdelt2 / pixelScale;
+
+    // projection from TPA (linear microns) to SKY (radians)
+    psProjection *toSky = psProjectionAlloc (wcs->toSky->R, wcs->toSky->D, PM_RAD_DEG*pdelt1, PM_RAD_DEG*pdelt2, wcs->toSky->type);
+
+    if (fpa->toSky == NULL) {
+        fpa->toTPA = psPlaneTransformIdentity (1);
+        fpa->fromTPA = psPlaneTransformIdentity (1);
+        fpa->toSky = toSky;
+    } else {
+
+        // this section allows the loaded chip to be included in an fpa structure in which
+        // other chips have already been loaded (ie, the fpa->toTPA, fpa->toSky components have
+        // already been defined).  we have to adjust to match the existing transformation.
+
+        if (fpa->toTPA == NULL)
+            psAbort("projection defined, tangent-plane not defined");
+        if (fpa->fromTPA == NULL)
+            psAbort("projection defined, tangent-plane not defined");
+
+        // convert from pixels on this chip to pixels on reference chip
+        // rX has units of refpixels / pixel
+        double rX = toSky->Xs / fpa->toSky->Xs;
+        double rY = toSky->Ys / fpa->toSky->Ys;
+
+        for (int i = 0; i <= toFPA->x->nX; i++) {
+            for (int j = 0; j <= toFPA->x->nY; j++) {
+                toFPA->x->coeff[i][j] *= rX;
+                toFPA->y->coeff[i][j] *= rY;
+            }
+        }
+
+	// apply the exiting fromTPA transformation to make the new toFPA consistent with the toTPA layter
+	// XXX this only works if toTPA is at most a linear transformation
+        psPlaneTransform *toFPAnew = psPlaneTransformAlloc(toFPA->x->nX, toFPA->x->nY);
+	for (int i = 0; i <= toFPA->x->nX; i++) {
+	  for (int j = 0; j <= toFPA->x->nY; j++) {
+	    double f1 = toFPA->x->coeffMask[i][j] ? 0.0 : fpa->fromTPA->x->coeff[1][0]*toFPA->x->coeff[i][j];
+	    double f2 = toFPA->y->coeffMask[i][j] ? 0.0 : fpa->fromTPA->x->coeff[0][1]*toFPA->y->coeff[i][j];
+	    toFPAnew->x->coeff[i][j] = f1 + f2;
+
+	    double g1 = toFPA->x->coeffMask[i][j] ? 0.0 : fpa->fromTPA->y->coeff[1][0]*toFPA->x->coeff[i][j];
+	    double g2 = toFPA->y->coeffMask[i][j] ? 0.0 : fpa->fromTPA->y->coeff[0][1]*toFPA->y->coeff[i][j];
+	    toFPAnew->y->coeff[i][j] = g1 + g2;
+	  }
+	}
+	toFPAnew->x->coeff[0][0] += fpa->fromTPA->x->coeff[0][0];
+	toFPAnew->y->coeff[0][0] += fpa->fromTPA->y->coeff[0][0];
+
+	psFree (toFPA);
+	toFPA = toFPAnew;
+
+        // adjust reference pixel for new toSky reference coordinate
+        // find the FPA coordinate of 0,0 for this chip.
+        psPlane *fpOld = psPlaneAlloc();
+        psPlane *fpNew = psPlaneAlloc();
+        psPlane *tp = psPlaneAlloc();
+        psSphere *sky = psSphereAlloc();
+
+	sky->r = toSky->R;
+	sky->d = toSky->D;
+        psProject (tp, sky, fpa->toSky); // find the focal-plane coord of this RA,DEC coord using the ref chip projection
+        psPlaneTransformApply (fpOld, fpa->fromTPA, tp);
+
+	sky->r = fpa->toSky->R;
+	sky->d = fpa->toSky->D;
+        psProject (tp, sky, fpa->toSky); // find the focal-plane coord of this RA,DEC coord using the ref chip projection
+        psPlaneTransformApply (fpNew, fpa->fromTPA, tp);
+
+        toFPA->x->coeff[0][0] -= fpNew->x - fpOld->x;
+        toFPA->y->coeff[0][0] -= fpNew->y - fpOld->y;
+
+        psFree (sky);
+        psFree (tp);
+        psFree (fpOld);
+        psFree (fpNew);
+
+        psFree (toSky);
+    }
+
+    // free an existing toFPA structure
+    psFree (chip->toFPA);
+    chip->toFPA = toFPA;
+
+    // determine the inverse transformation: we need the chip pixels covered by this transform
+    psRegion *region = pmChipPixels (chip);
+
+    psFree (chip->fromFPA);
+    chip->fromFPA = psPlaneTransformInvert(NULL, chip->toFPA, *region, 50);
+    psFree (region);
+
+    // this can take a very long time...
+    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;
+
+    psTrace ("psastro", 5, "toFPA: %f %f  (%f,%f),(%f,%f)\n",
+             chip->toFPA->x->coeff[0][0], chip->toFPA->y->coeff[0][0],
+             chip->toFPA->x->coeff[1][0], chip->toFPA->x->coeff[0][1],
+             chip->toFPA->y->coeff[1][0], chip->toFPA->y->coeff[0][1]);
+
+    psTrace ("psastro", 5, "frFPA: %f %f  (%f,%f),(%f,%f)\n",
+             chip->fromFPA->x->coeff[0][0], chip->fromFPA->y->coeff[0][0],
+             chip->fromFPA->x->coeff[1][0], chip->fromFPA->x->coeff[0][1],
+             chip->fromFPA->y->coeff[1][0], chip->fromFPA->y->coeff[0][1]);
+
+    return true;
+}
+
+// convert a pmAstromWCS structure representing a bilevel chip into corresponding chip elements
+bool pmAstromWCSBileveltoChip (pmChip *chip, const pmAstromWCS *wcs)
+{
+    /* we convert wcs->trans to toFPA, which is different from wcs->trans in 3 important ways:
+     * 1) the output is in pixel (not degrees): divide by cdelt1,2 raised to an appropriate power
+     * 2) X,Y are applied directly, without an applied Xo,Yo offset
+     * 3) there is an allowed Lo,Mo term ([0][0] coefficients)
+     */
+
+    // create transformation with 0,0 reference pixel and units of microns/pixel
+    psFree (chip->toFPA);
+    chip->toFPA = psPlaneTransformSetCenter (NULL, wcs->trans, -wcs->crpix1, -wcs->crpix2);
+
+    // determine the inverse transformation: we need the chip pixels covered by this transform
+    psRegion *region = pmChipPixels (chip);
+
+    psFree (chip->fromFPA);
+    chip->fromFPA = psPlaneTransformInvert(NULL, chip->toFPA, *region, 50);
+    psFree (region);
+
+    return true;
+}
+
+// convert a pmAstromWCS structure representing a bilevel mosaic into corresponding fpa elements
+bool pmAstromWCSBileveltoFPA (pmFPA *fpa, const pmAstromWCS *wcs, psRegion region)
+{
+    // projection from TPA (microns) to SKY (radians)
+    // cdelt1,2 has units of degrees/micron
+    fpa->toSky = psProjectionAlloc (wcs->toSky->R, wcs->toSky->D, wcs->cdelt1*PM_RAD_DEG, wcs->cdelt2*PM_RAD_DEG, wcs->toSky->type);
+
+    // create transformation with 0,0 reference pixel
+    fpa->toTPA = psPlaneTransformSetCenter (NULL, wcs->trans, -wcs->crpix1, -wcs->crpix2);
+
+    // convert fpa->toTPA to units of unity (microns/micron)
+    for (int i = 0; i <= fpa->toTPA->x->nX; i++) {
+        for (int j = 0; j <= fpa->toTPA->x->nY; j++) {
+            fpa->toTPA->x->coeff[i][j] /= wcs->cdelt1;
+            fpa->toTPA->y->coeff[i][j] /= wcs->cdelt2;
+        }
+    }
+
+    // the transformation used the region to define the inversion grid
+    // the region defines the FPA pixels covered by the tranformation
+    psFree (fpa->fromTPA);
+    fpa->fromTPA = psPlaneTransformInvert(NULL, fpa->toTPA, region, 50);
+    return true;
+}
+
+// convert toFPA / toSky components to pmAstromWCS
+// tolerance is allowed error in center solution in pixels
+pmAstromWCS *pmAstromWCSfromFPA (const pmFPA *fpa, const pmChip *chip, double tol)
+{
+    // XXX require chip->toFPA->x->nX == chip->toFPA->x->nY
+    // XXX require chip->toFPA->y->nX == chip->toFPA->y->nY
+    // XXX require chip->toFPA->x->nX == chip->toFPA->y->nX
+    // XXX require chip->toFPA->nX == 1,2,3
+
+    // technically, we can have a plate scale here (fpa->toTPA:dx,dy != 1)
+    // XXX not really: toTPA needs to have unity scale for distortion fitting function
+    // if (!psPlaneTransformIsDiagonal (fpa->toTPA))
+    // psAbort("invalid TPA transformation");
+
+    // create a temporary transform which combines toTPA and toFPA.  allow toTPA to have 0th
+    // and 1st order terms
+    
+    // XXX require fpa->toTPA->x->nX == fpa->toTPA->x->nY
+    // XXX require fpa->toTPA->y->nX == fpa->toTPA->y->nY
+    // XXX require fpa->toTPA->x->nX == fpa->toTPA->y->nX
+    // XXX require fpa->toTPA->x->coeffMask[1][1]
+    // XXX require fpa->toTPA->y->coeffMask[1][1]
+    // XXX require fpa->toTPA->nX == 1
+
+    psPlaneTransform *toTPA = psPlaneTransformAlloc(chip->toFPA->x->nX, chip->toFPA->x->nY);
+
+    for (int i = 0; i <= toTPA->x->nX; i++) {
+	for (int j = 0; j <= toTPA->x->nY; j++) {
+	    double f1 = chip->toFPA->x->coeffMask[i][j] ? 0.0 : fpa->toTPA->x->coeff[1][0]*chip->toFPA->x->coeff[i][j];
+	    double f2 = chip->toFPA->y->coeffMask[i][j] ? 0.0 : fpa->toTPA->x->coeff[0][1]*chip->toFPA->y->coeff[i][j];
+	    toTPA->x->coeff[i][j] = f1 + f2;
+
+	    double g1 = chip->toFPA->x->coeffMask[i][j] ? 0.0 : fpa->toTPA->y->coeff[1][0]*chip->toFPA->x->coeff[i][j];
+	    double g2 = chip->toFPA->y->coeffMask[i][j] ? 0.0 : fpa->toTPA->y->coeff[0][1]*chip->toFPA->y->coeff[i][j];
+	    toTPA->y->coeff[i][j] = g1 + g2;
+	}
+    }
+    toTPA->x->coeff[0][0] += fpa->toTPA->x->coeff[0][0];
+    toTPA->y->coeff[0][0] += fpa->toTPA->y->coeff[0][0];
+
+    pmAstromWCS *wcs = pmAstromWCSAlloc(toTPA->x->nX, toTPA->x->nY);
+
+    // convert projection from FPA to SKY into wcs projection (degrees to radians)
+    wcs->toSky = psProjectionAlloc (fpa->toSky->R, fpa->toSky->D, PM_RAD_DEG, PM_RAD_DEG, fpa->toSky->type);
+    wcs->crval1 = fpa->toSky->R*PS_DEG_RAD;
+    wcs->crval2 = fpa->toSky->D*PS_DEG_RAD;
+
+    // given transformation, solve for coordinates which yields output coordinates of 0,0
+    psPlane *center = psPlaneTransformGetCenter (toTPA, tol);
+    if (!center) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to solve for TPA center.");
+	psFree (toTPA);
+	psFree (wcs);
+	return NULL;
+    }
+
+    // create wcs transform from toFPA, resulting transformation has units of microns/pixel
+    // adjust wcs transform to use center as reference coordinate
+    psPlaneTransformSetCenter (wcs->trans, toTPA, center->x, center->y);
+
+    // calculated center is crpix1,2
+    wcs->crpix1 = center->x;
+    wcs->crpix2 = center->y;
+    psFree (center);
+
+    // pdelt1,2 has units of degrees/micron
+    double pdelt1 = fpa->toSky->Xs * PS_DEG_RAD;
+    double pdelt2 = fpa->toSky->Ys * PS_DEG_RAD;
+
+    // convert wcs->trans to a matrix with units of degrees/pixel
+    for (int i = 0; i <= wcs->trans->x->nX; i++) {
+        for (int j = 0; j <= wcs->trans->x->nY; j++) {
+            wcs->trans->x->coeff[i][j] *= pdelt1;
+            wcs->trans->y->coeff[i][j] *= pdelt2;
+        }
+    }
+
+    // cdelt1,2 has units of degrees/pixel
+    wcs->cdelt1 = hypot (wcs->trans->x->coeff[1][0], wcs->trans->x->coeff[0][1]);
+    wcs->cdelt2 = hypot (wcs->trans->y->coeff[1][0], wcs->trans->y->coeff[0][1]);
+
+    psFree (toTPA);
+
+    return wcs;
+}
+
+/* the bilevel astrometry description consists of a polynomial warping from
+   chip coordinates to FPA coordinates (coords->ctype = LIN---WRP), followed
+   by a polynomial representation of the telescope distortion + the projection
+   (coords->ctype = RA---DIS).
+*/
+
+// convert the chip-level toFPA to a wcs polynomial transformation
+pmAstromWCS *pmAstromWCSBilevelChipFromFPA (const pmChip *chip, double tol)
+{
+    // XXX require chip->toFPA->x->nX == chip->toFPA->x->nY
+    // XXX require chip->toFPA->y->nX == chip->toFPA->y->nY
+    // XXX require chip->toFPA->x->nX == chip->toFPA->y->nX
+    // XXX require chip->toFPA->nX == 1,2,3
+
+    // convert chip->toFPA to wcs format (WRP)
+    pmAstromWCS *wcs = pmAstromWCSAlloc(chip->toFPA->x->nX, chip->toFPA->x->nY);
+
+    // Chip to FPA transformation is a Cartesian 'projection'
+    // reference pixel for FPA is 0.0, 0.0
+    wcs->toSky = psProjectionAlloc (0.0, 0.0, 1.0, 1.0, PS_PROJ_WRP);
+    wcs->crval1 = 0.0;
+    wcs->crval2 = 0.0;
+
+    // given transformation, solve for coordinates which yields output coordinates of 0,0
+    psPlane *center = psPlaneTransformGetCenter (chip->toFPA, tol);
+    if (!center) {
+    	psError(PS_ERR_UNKNOWN, false, "Unable to solve for TPA center.");
+    	psFree (wcs);
+    	return NULL;
+    }
+
+    // adjust wcs transform to use center as reference coordinate
+    // resulting transformation has units of microns/pixel
+    psPlaneTransformSetCenter (wcs->trans, chip->toFPA, center->x, center->y);
+
+    // calculated center is crpix1,2
+    wcs->crpix1 = center->x;
+    wcs->crpix2 = center->y;
+    psFree (center);
+
+    // output coordinates are in microns : CDELT1,2 has units of microns/pixel
+    wcs->cdelt1 = hypot (wcs->trans->x->coeff[1][0], wcs->trans->x->coeff[0][1]);
+    wcs->cdelt2 = hypot (wcs->trans->y->coeff[1][0], wcs->trans->y->coeff[0][1]);
+
+    return wcs;
+}
+
+// convert the fpa-level toTPA, toSky to a wcs polynomial transformation
+pmAstromWCS *pmAstromWCSBilevelMosaicFromFPA (const pmFPA *fpa, double tol)
+{
+    // XXX require fpa->toTPA->x->nX == fpa->toTPA->x->nY
+    // XXX require fpa->toTPA->y->nX == fpa->toTPA->y->nY
+    // XXX require fpa->toTPA->x->nX == fpa->toTPA->y->nX
+    // XXX require fpa->toTPA->nX == 1,2,3
+    // XXX require fpa->toSky->type == PS_PROJ_TAN
+
+    // convert fpa->toTPA + fpa->toSky to wcs format (DIS)
+    pmAstromWCS *wcs = pmAstromWCSAlloc(fpa->toTPA->x->nX, fpa->toTPA->x->nY);
+
+    // convert projection from TPA to SKY into wcs projection (degrees to radians)
+    wcs->toSky = psProjectionAlloc (fpa->toSky->R, fpa->toSky->D, PM_RAD_DEG, PM_RAD_DEG, PS_PROJ_DIS);
+    wcs->crval1 = fpa->toSky->R*PS_DEG_RAD;
+    wcs->crval2 = fpa->toSky->D*PS_DEG_RAD;
+
+    // given transformation, solve for coordinates which yields output coordinates of 0,0
+    psPlane *center = psPlaneTransformGetCenter (fpa->toTPA, tol);
+    if (!center) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to solve for TPA center.");
+	psFree (wcs);
+	return NULL;
+    }
+
+    // adjust wcs transform to use center as reference coordinate
+    // resulting transformation has units of unity (microns/micron)
+    psPlaneTransformSetCenter (wcs->trans, fpa->toTPA, center->x, center->y);
+
+    // calculated center is crpix1,2
+    wcs->crpix1 = center->x;
+    wcs->crpix2 = center->y;
+    psFree (center);
+
+    // pdelt1,2 has units of degrees/micron
+    double pdelt1 = fpa->toSky->Xs * PS_DEG_RAD;
+    double pdelt2 = fpa->toSky->Ys * PS_DEG_RAD;
+
+    // convert wcs->trans to units of degree/micron
+    for (int i = 0; i <= wcs->trans->x->nX; i++) {
+        for (int j = 0; j <= wcs->trans->x->nY; j++) {
+            wcs->trans->x->coeff[i][j] *= pdelt1;
+            wcs->trans->y->coeff[i][j] *= pdelt2;
+        }
+    }
+
+    // cdelt1,2 has units of degrees/micron
+    wcs->cdelt1 = hypot (wcs->trans->x->coeff[1][0], wcs->trans->x->coeff[0][1]);
+    wcs->cdelt2 = hypot (wcs->trans->y->coeff[1][0], wcs->trans->y->coeff[0][1]);
+
+    return wcs;
+}
+
+static void pmAstromWCSFree (pmAstromWCS *wcs)
+{
+
+    if (!wcs)
+        return;
+    psFree (wcs->trans);
+    psFree (wcs->toSky);
+}
+
+pmAstromWCS *pmAstromWCSAlloc (int nXorder, int nYorder)
+{
+
+    pmAstromWCS *wcs = (pmAstromWCS *) psAlloc(sizeof(pmAstromWCS));
+    psMemSetDeallocator(wcs, (psFreeFunc) pmAstromWCSFree);
+
+    wcs->trans = psPlaneTransformAlloc (nXorder, nYorder);
+    wcs->toSky = NULL;
+
+    memset (wcs->ctype1, 0, PM_ASTROM_WCS_TYPE_SIZE);
+    memset (wcs->ctype2, 0, PM_ASTROM_WCS_TYPE_SIZE);
+    return wcs;
+}
+
+/*****
+ 
+For mosaic astrometry, we need to have a starting set of projection terms in which the
+chip-to-FPA terms result in a fixed physical unit on the focal plane (eg, pixels or
+microns).  This set of projections, coupled with an identity toTPA (ie, no distortion) will
+result in substantial errors between the observed and predicted star positions on the focal
+plane: this is the measurement of the optical distortion in the camera.  At the same time,
+we need to carry around the transformations which allow us to make an accurate calculation
+of the position of the stars based on the input (per-chip) astrometry.  These
+transformations will allow us to match the raw and ref stars robustly.  To convert the
+per-chip astrometry (which may have been calculated with a different plate scale for each
+chip) to a collection of astrometry terms for chips in a single mosaic, we need to adjust
+the chip-to-FPA scaling (eg, pc11) to match the variations in the effective plate scale for
+each chip (eg, cdelt1).  Thus, we need to carry around both the
+ 
+*****/
+
+/* discussion of the coord transformations:
+   X,Y: coord on a chip in pixels
+   L,M: coord on the focal plane (pixels)
+   P,Q: coord in the tangent plane (microns or mm?)
+   R,D: coord on the sky 
+ 
+   this function creates WCS terms which convert directly from chip to sky.
+   this function requires a linear, unrotated toTPA distortion term
+   toTPA->x,y->coeff[1][0],[0][1] defines the detector scale (microns / pixel)
+   tpSky->Xs,Ys defines the plate scale (radians / micron)
+*/
+
+/* at this point, we have extracted from the header the WCS terms in the form of a polynomial,
+ * wcs->trans, which will convert X,Y in pixels to L,M in degrees.  we also have the following 
+ * elements defined:
+ * type (projection type)
+ * crval1,2 (in RA,DEC degrees)
+ * crpix1,2 
+ * cdelt1,2 (in degrees / pixel)
+ * pixelScale (microns / pixel)
+ * 
+ * now we convert wcs->trans to toFPA, which is different from wcs->trans in 3 important ways:
+ * 1) the output is in microns (not degrees): divide by cdelt1,2
+ * 2) X,Y are applied directly, without an applied Xo,Yo offset
+ * 3) there is an allowed Lo,Mo term ([0][0] coefficients)
+ */
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryWCS.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryWCS.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/astrom/pmAstrometryWCS.h	(revision 22322)
@@ -0,0 +1,77 @@
+/* @file  pmAstrometryDistortion.h
+ * @brief functions to convert FITS WCS keywords to / from pmFPA structures
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-21 22:00:49 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_ASTROMETRY_WCS_H
+#define PM_ASTROMETRY_WCS_H
+
+/// @addtogroup Astrometry
+/// @{
+
+#define PM_ASTROM_WCS_TYPE_SIZE 80
+typedef struct
+{
+    char ctype1[PM_ASTROM_WCS_TYPE_SIZE];
+    char ctype2[PM_ASTROM_WCS_TYPE_SIZE];
+    double crval1, crval2;
+    double crpix1, crpix2;
+    double cdelt1, cdelt2;
+    psProjection *toSky;
+    psPlaneTransform *trans;
+}
+pmAstromWCS;
+
+// support function for the pmAstromWCS representation
+pmAstromWCS *pmAstromWCSAlloc (int nXorder, int nYorder);
+bool pmAstromWCStoSky (psSphere *sky, pmAstromWCS *wcs, psPlane *chip);
+bool pmAstromWCStoChip (psPlane *chip, pmAstromWCS *wcs, psSphere *sky);
+
+// read and write the pmAstromWCS representation to the header
+bool pmAstromWCStoHeader (psMetadata *header, const pmAstromWCS *wcs);
+pmAstromWCS *pmAstromWCSfromHeader (const psMetadata *header);
+
+// convert from wcs terms to chip->toFPA, fpa->toSky,toTPA terms
+bool pmAstromWCSBileveltoChip (pmChip *chip, const pmAstromWCS *wcs);
+bool pmAstromWCSBileveltoFPA (pmFPA *fpa, const pmAstromWCS *wcs, psRegion region);
+
+// convert from chip->toFPA, fpa->toSky,toTPA terms to wcs terms
+pmAstromWCS *pmAstromWCSBilevelChipFromFPA (const pmChip *chip, double tol);
+pmAstromWCS *pmAstromWCSBilevelMosaicFromFPA (const pmFPA *fpa, double tol);
+
+// convert the pmAstromWCS representation to the FPA representation
+bool pmAstromWCStoFPA (pmFPA *fpa, pmChip *chip, const pmAstromWCS *wcs, double plateScale);
+pmAstromWCS *pmAstromWCSfromFPA (const pmFPA *fpa, const pmChip *chip, double tol);
+
+// read wcs terms from the supplied header into the fpa hierarchy components
+bool pmAstromReadWCS (pmFPA *fpa, pmChip *chip, const psMetadata *header, double plateScale);
+
+// write the wcs terms from the fpa hierarchy components into the supplied header
+// tol is the convergence tolerance for the non-linear solution to the reference pixel
+bool pmAstromWriteWCS (psMetadata *header, const pmFPA *fpa, const pmChip *chip, double tol);
+
+bool pmAstromReadBilevelChip (pmChip *chip, const psMetadata *header);
+bool pmAstromReadBilevelMosaic (pmFPA *fpa, const psMetadata *header);
+
+bool pmAstromWriteBilevelChip (psMetadata *header, const pmChip *chip, double tol);
+bool pmAstromWriteBilevelMosaic (psMetadata *header, const pmFPA *fpa, double tol);
+
+// move to pslib
+psPlaneDistort *psPlaneDistortIdentity (int order);
+bool psPlaneDistortIsDiagonal (psPlaneDistort *distort);
+
+# define PM_DEG_RAD 57.295779513082322
+# define PM_RAD_DEG  0.017453292519943
+
+/// @}
+#endif // PM_ASTROMETRY_WCS_H
+
+/*
+ * the wcs->trans component defines a polynomial which converts (x-crpix1),(y-crpix2) to
+ * L,M in degrees
+ */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/Makefile.am	(revision 22322)
@@ -0,0 +1,61 @@
+noinst_LTLIBRARIES = libpsmodulescamera.la
+
+libpsmodulescamera_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS)
+libpsmodulescamera_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulescamera_la_SOURCES  = \
+	pmFPA.c \
+	pmFPACalibration.c \
+	pmFPAConstruct.c \
+	pmFPACopy.c \
+	pmFPAHeader.c \
+	pmFPAMaskWeight.c \
+	pmFPAMosaic.c \
+	pmFPARead.c \
+	pmFPAUtils.c \
+	pmFPAWrite.c \
+	pmHDU.c \
+	pmHDUUtils.c \
+	pmHDUGenerate.c \
+	pmFPA_JPEG.c \
+	pmFPAview.c \
+	pmFPAfile.c \
+	pmFPAfileDefine.c \
+	pmFPAfileIO.c \
+	pmFPAfileFitsIO.c \
+	pmFPAFlags.c \
+	pmFPALevel.c \
+	pmFPAExtent.c \
+	pmCellSquish.c \
+	pmReadoutStack.c \
+	pmReadoutFake.c \
+	pmFPABin.c
+
+pkginclude_HEADERS = \
+	pmFPA.h \
+	pmFPACalibration.h \
+	pmFPAConstruct.h \
+	pmFPACopy.h \
+	pmFPAHeader.h \
+	pmFPAMaskWeight.h \
+	pmFPAMosaic.h \
+	pmFPARead.h \
+	pmFPAUtils.h \
+	pmFPAWrite.h \
+	pmHDU.h \
+	pmHDUUtils.h \
+	pmHDUGenerate.h \
+	pmFPA_JPEG.h \
+	pmFPAview.h \
+	pmFPAfile.h \
+	pmFPAfileDefine.h \
+	pmFPAfileIO.h \
+	pmFPAfileFitsIO.h \
+	pmFPAFlags.h \
+	pmFPALevel.h \
+	pmFPAExtent.h \
+	pmCellSquish.h \
+	pmReadoutStack.h \
+	pmReadoutFake.h \
+	pmFPABin.h
+
+CLEANFILES = *~
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmCellSquish.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmCellSquish.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmCellSquish.c	(revision 22322)
@@ -0,0 +1,176 @@
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmShifts.h"
+#include "pmCellSquish.h"
+
+// Comparing values to get ranges
+#define COMPARE_SMALLER(TARGET, SOURCE) if ((SOURCE) < (TARGET)) (TARGET) = (SOURCE);
+#define COMPARE_BIGGER(TARGET, SOURCE) if ((SOURCE) > (TARGET)) (TARGET) = (SOURCE);
+
+
+bool pmCellSquish(pmCell *cell, psMaskType maskVal, bool useShifts)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_ARRAY_NON_NULL(cell->readouts, false);
+    psArray *readouts = cell->readouts; // Array of readouts
+    long numReadouts = readouts->n; // Number of readouts
+
+    if (numReadouts <= 1) {
+        // We squished everything there was to squish
+        return true;
+    }
+
+    pmShifts *shifts = NULL;                   // Orthogonal transfer shifts
+    if (useShifts) {
+        bool mdok;                      // Status of MD lookup
+        shifts = psMetadataLookupPtr(&mdok, cell->analysis, PM_SHIFTS_TABLE_NAME);
+        if (!mdok || !shifts) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Squishing with shifts requested, but no shifts found.");
+            return false;
+        }
+        if (shifts->num != numReadouts) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Number of shifts (%ld) does not match number of readouts (%ld).",
+                    shifts->num, numReadouts);
+            return false;
+        }
+    }
+
+    // First pass to get the bounds, make sure everything is legit.
+    int xMin = 0, xMax = 0, yMin = 0, yMax = 0; // Bounds of the squish
+    bool valid = false;                 // Do we have a valid readout?
+    int col0 = 0, row0 = 0, numCols = 0, numRows = 0;// Window parameters for the readouts
+    int xShift = 0, yShift = 0;         // Shift due to orthogonal transfer, to be applied
+    if (useShifts && shifts->xyRelative) {
+        // Correct for final shift, to put in correct frame for the image that is read out
+        xShift = - shifts->x->data.S32[shifts->num - 1];
+        yShift = - shifts->y->data.S32[shifts->num - 1];
+    }
+    for (long i = 0; i < numReadouts; i++) {
+        // Add in the shift
+        if (useShifts) {
+            if (shifts->xyRelative) {
+                // Need to accumulate shift
+                xShift += shifts->x->data.S32[i];
+                yShift += shifts->y->data.S32[i];
+            } else {
+                // Correct for final shift, to put in correct frame for the image that is read out
+                xShift = shifts->x->data.S32[i] - shifts->x->data.S32[shifts->num - 1];
+                yShift = shifts->y->data.S32[i] - shifts->y->data.S32[shifts->num - 1];
+            }
+        }
+
+        pmReadout *readout = readouts->data[i]; // Readout of interest
+        if (!readout || !readout->image) {
+            continue;
+        }
+
+        if (!valid) {
+            valid = true;
+
+            col0 = readout->col0;
+            row0 = readout->row0;
+            numCols = readout->image->numCols;
+            numRows = readout->image->numRows;
+
+            if (useShifts) {
+                xMin = col0;
+                xMax = col0 + numCols;
+                yMin = row0;
+                yMax = row0 + numRows;
+            }
+        } else {
+            if (readout->col0 != col0 || readout->row0 != row0 ||
+                readout->image->numCols != numCols || readout->image->numRows != numRows) {
+                // Everything should have the same window because we've read it in from an image cube
+                psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                        "Readout window [%d:%d,%d:%d] doesn't match canonical window [%d:%d,%d:%d]",
+                        readout->col0, readout->col0 + readout->image->numCols,
+                        readout->row0, readout->row0 + readout->image->numRows,
+                        col0, col0 + numCols, row0, row0 + numRows);
+                return false;
+            }
+
+            if (useShifts) {
+                // If there is shifting, the actual window may change
+                int xMinTest = readout->col0 + xShift; // Minimum x value
+                int xMaxTest = readout->col0 + readout->image->numCols + xShift; // Maximum x value
+                int yMinTest = readout->row0 + yShift; // Minimum y value
+                int yMaxTest = readout->row0 + readout->image->numRows + yShift; // Maximum y value
+                COMPARE_SMALLER(xMin, xMinTest);
+                COMPARE_BIGGER(xMax, xMaxTest);
+                COMPARE_SMALLER(yMin, yMinTest);
+                COMPARE_BIGGER(yMax, yMaxTest);
+            }
+        }
+    }
+
+    if (useShifts) {
+        // Size of combined image, after shifts applied
+        numCols = xMax - xMin + 1;
+        numRows = yMax - yMin + 1;
+    }
+    psImage *squishImage = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Squished image
+    psImageInit(squishImage, 0.0);
+    psImage *squishMask = psImageAlloc(numCols, numRows, PS_TYPE_MASK); // Squished mask
+    psImageInit(squishMask, 0);
+
+    // Second pass to do the squish
+    xShift = yShift = 0;                // Reset the accumulated shifts
+    if (useShifts && shifts->xyRelative) {
+        // Correct for final shift, to put in correct frame for the image that is read out
+        xShift = - shifts->x->data.S32[shifts->num - 1];
+        yShift = - shifts->y->data.S32[shifts->num - 1];
+    }
+    for (long i = 0; i < numReadouts; i++) {
+        if (useShifts) {
+            if (shifts->xyRelative) {
+                // Need to accumulate shift
+                xShift += shifts->x->data.S32[i];
+                yShift += shifts->y->data.S32[i];
+            } else {
+                // Correct for final shift, to put in correct frame for the image that is read out
+                xShift = shifts->x->data.S32[i] - shifts->x->data.S32[shifts->num - 1];
+                yShift = shifts->y->data.S32[i] - shifts->y->data.S32[shifts->num - 1];
+            }
+        }
+
+        pmReadout *readout = readouts->data[i]; // Readout of interest
+        if (!readout || !readout->image) {
+            continue;
+        }
+
+        int xOffset = xMin - readout->col0; // Offset to squished readout in x
+        int yOffset = yMin - readout->row0; // Offset to squished readout in y
+        if (useShifts) {
+            xOffset += xShift;
+            yOffset += yShift;
+        }
+
+        psImage *image = readout->image; // The image of interest
+        psImage *mask = readout->mask; // The mask of interest
+        for (int y = 0; y < readout->image->numRows; y++) {
+            int ySquish = y + yOffset; // Position on squished readout in y
+            for (int x = 0; x < readout->image->numCols; x++) {
+                int xSquish = x + xOffset; // Position on squished readout in x
+                squishImage->data.F32[ySquish][xSquish] += image->data.F32[y][x];
+                if (mask) {
+                    squishMask->data.PS_TYPE_MASK_DATA[ySquish][xSquish] |= mask->data.U8[y][x] & maskVal;
+                }
+            }
+        }
+    }
+
+    pmCellFreeReadouts(cell);
+    pmReadout *squishRO = pmReadoutAlloc(cell); // New readout to hold squished image
+    squishRO->image = squishImage;
+    squishRO->mask = squishMask;
+    squishRO->row0 = yMin;
+    squishRO->col0 = xMin;
+    psFree(squishRO);               // Drop reference
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmCellSquish.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmCellSquish.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmCellSquish.h	(revision 22322)
@@ -0,0 +1,13 @@
+#ifndef PM_CELL_SQUISH_H
+#define PM_CELL_SQUISH_H
+
+/// Squish (combine all component readouts of) a cell
+///
+/// The component readouts are combined, optionally taking into account orthogonal transfer shifts (assumed to
+/// already have been read) and masks.
+bool pmCellSquish(pmCell *cell,         ///< Cell to have readouts combined
+                  psMaskType maskVal,   ///< Value to be masked
+                  bool useShifts        ///< Use the shifts when squishing?
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA.c	(revision 22322)
@@ -0,0 +1,470 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <strings.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmConcepts.h"
+#include "pmMaskBadPixels.h"
+
+static void readoutFree(pmReadout *readout)
+{
+    // if this readout has a parent, drop that instance
+    if (readout->parent) {
+        psTrace("psModules.camera", 9, "Removing readout %zd from cell %zd...\n",
+                (size_t)readout, (size_t)readout->parent);
+        psArray *readouts = readout->parent->readouts;
+        for (int i = 0; i < readouts->n; i++) {
+            if (readouts->data[i] == readout) {
+                readouts->data[i] = NULL;
+            }
+        }
+    }
+    psTrace("psModules.camera", 9, "Freeing readout %zd\n", (size_t) readout);
+
+    psFree(readout->image);
+    psFree(readout->mask);
+    psFree(readout->weight);
+    psFree(readout->analysis);
+    psFree(readout->bias);
+}
+
+static void cellFree(pmCell *cell)
+{
+
+    // if this cell has a parent, drop that instance
+    if (cell->parent) {
+        psTrace("psModules.camera", 9, "Removing cell %zd from chip %zd...\n",
+                (size_t)cell, (size_t)cell->parent);
+        psArray *cells = cell->parent->cells;
+        for (int i = 0; i < cells->n; i++) {
+            if (cells->data[i] == cell) {
+                cells->data[i] = NULL;
+            }
+        }
+    }
+    psTrace("psModules.camera", 9, "Freeing cell %zd\n", (size_t)cell);
+    pmCellFreeReadouts(cell);
+    psFree(cell->readouts);
+
+    psFree(cell->concepts);
+    psFree(cell->analysis);
+    psFree(cell->config);
+    psFree(cell->hdu);
+}
+
+static void chipFree(pmChip* chip)
+{
+    // if this chip has a parent, drop that instance
+    if (chip->parent) {
+        psTrace("psModules.camera", 9, "Removing chip %zd from fpa %zd...\n",
+                (size_t)chip, (size_t)chip->parent);
+        psArray *chips = chip->parent->chips;
+        for (int i = 0; i < chips->n; i++) {
+            if (chips->data[i] == chip) {
+                chips->data[i] = NULL;
+            }
+        }
+    }
+
+    psTrace("psModules.camera", 9, "Freeing chip %zd\n", (size_t)chip);
+    pmChipFreeCells(chip);
+    psFree(chip->cells);
+
+    psFree(chip->concepts);
+    psFree(chip->analysis);
+    psFree(chip->hdu);
+
+    psFree(chip->toFPA);
+    psFree(chip->fromFPA);
+}
+
+
+static void FPAFree(pmFPA *fpa)
+{
+    psTrace("psModules.camera", 9, "Freeing fpa %zd\n", (size_t)fpa);
+
+    // NULL the parent pointers
+    psArray *chips = fpa->chips;
+    for (int i = 0 ; i < chips->n ; i++) {
+        pmChip *tmpChip = chips->data[i];
+        if (! tmpChip) {
+            continue;
+        }
+        tmpChip->parent = NULL;
+    }
+    psFree(fpa->chips);
+    psFree(fpa->concepts);
+    psFree(fpa->analysis);
+    psFree((void *)fpa->camera);
+    psFree(fpa->hdu);
+
+    psFree(fpa->fromTPA);
+    psFree(fpa->toTPA);
+    psFree(fpa->toSky);
+}
+
+void pmCellFreeReadouts(pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell,);
+
+    //
+    // Set the parent to NULL in all cell->readouts before psFree(cell->readouts)
+    // in order to avoid memory reference counter problems.
+    //
+    psArray *readouts = cell->readouts;
+    for (psS32 i = 0 ; i < readouts->n ; i++) {
+        pmReadout *tmpReadout = readouts->data[i];
+        if (! tmpReadout) {
+            continue;
+        }
+        tmpReadout->parent = NULL;
+        psTrace("psModules.camera", 9, "Will now free readout %zd...\n", (size_t)tmpReadout);
+    }
+    cell->readouts = psArrayRealloc(cell->readouts, 0);
+    cell->readouts->n = 0;
+}
+
+
+void pmChipFreeCells(pmChip *chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chip,);
+
+    //
+    // Set the parent to NULL in all chip->cells before psFree(chip->cells)
+    // in order to avoid memory reference counter problems.
+    //
+    psArray *cells = chip->cells;
+    for (int i = 0 ; i < cells->n ; i++) {
+        pmCell *tmpCell = cells->data[i];
+        if (! tmpCell) {
+            continue;
+        }
+        tmpCell->parent = NULL;
+        pmCellFreeReadouts(tmpCell);// Drop all readouts the cell holds
+    }
+    chip->cells = psArrayRealloc(chip->cells, 0);
+    chip->cells->n = 0;
+}
+
+void pmReadoutFreeData (pmReadout *readout)
+{
+    if (!readout) {
+        return;
+    }
+
+    psFree(readout->image);
+    psFree(readout->mask);
+    psFree(readout->weight);
+    psFree(readout->bias);
+
+    psTrace("psModules.camera", 3, "Freeing readout data for %zd\n", (size_t) readout);
+
+    readout->image = NULL;
+    readout->weight = NULL;
+    readout->mask = NULL;
+
+    readout->bias = psListAlloc(NULL);
+
+    readout->col0 = 0;
+    readout->row0 = 0;
+
+    readout->thisImageScan = 0;
+    readout->thisMaskScan = 0;
+    readout->thisWeightScan = 0;
+
+    readout->lastImageScan = 0;
+    readout->lastMaskScan = 0;
+    readout->lastWeightScan = 0;
+}
+
+void pmCellFreeData(pmCell *cell)
+{
+    if (!cell) {
+        return;
+    }
+
+    for (int i = 0; i < cell->readouts->n; i++) {
+        pmReadoutFreeData(cell->readouts->data[i]);
+    }
+    if (cell->hdu) {
+        psFree(cell->hdu->images);
+        psFree(cell->hdu->weights);
+        psFree(cell->hdu->masks);
+        // psFree(cell->hdu->header);
+
+        cell->hdu->images = NULL;
+        cell->hdu->weights = NULL;
+        cell->hdu->masks = NULL;
+        // cell->hdu->header = NULL;
+    }
+}
+
+void pmChipFreeData(pmChip *chip)
+{
+    if (!chip) {
+        return;
+    }
+
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCellFreeData(chip->cells->data[i]);
+    }
+    if (chip->hdu) {
+        psFree(chip->hdu->images);
+        psFree(chip->hdu->weights);
+        psFree(chip->hdu->masks);
+        // psFree(chip->hdu->header);
+
+        chip->hdu->images = NULL;
+        chip->hdu->weights = NULL;
+        chip->hdu->masks = NULL;
+        // chip->hdu->header = NULL;
+    }
+}
+
+void pmFPAFreeData(pmFPA *fpa)
+{
+    if (!fpa) {
+        return;
+    }
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChipFreeData(fpa->chips->data[i]);
+    }
+    if (fpa->hdu) {
+        psFree(fpa->hdu->images);
+        psFree(fpa->hdu->weights);
+        psFree(fpa->hdu->masks);
+        // psFree(fpa->hdu->header);
+
+        fpa->hdu->images = NULL;
+        fpa->hdu->weights = NULL;
+        fpa->hdu->masks = NULL;
+        // fpa->hdu->header = NULL;
+    }
+}
+
+pmReadout *pmReadoutAlloc(pmCell *cell)
+{
+    pmReadout *tmpReadout = (pmReadout *)psAlloc(sizeof(pmReadout));
+    psMemSetDeallocator(tmpReadout, (psFreeFunc) readoutFree);
+
+    tmpReadout->image = NULL;
+    tmpReadout->mask = NULL;
+    tmpReadout->weight = NULL;
+    tmpReadout->bias = psListAlloc(NULL);
+    tmpReadout->analysis = psMetadataAlloc();
+    tmpReadout->parent = cell;
+    if (cell) {
+        cell->readouts = psArrayAdd(cell->readouts, 1, (psPtr) tmpReadout);
+    }
+
+    tmpReadout->process = true;            // All cells are processed by default
+    tmpReadout->file_exists = false;       // file not yet identified
+    tmpReadout->data_exists = false;       // data yet read in
+
+    tmpReadout->row0 = 0;
+    tmpReadout->col0 = 0;
+
+    tmpReadout->thisImageScan = 0;
+    tmpReadout->thisMaskScan = 0;
+    tmpReadout->thisWeightScan = 0;
+
+    tmpReadout->lastImageScan = 0;
+    tmpReadout->lastMaskScan = 0;
+    tmpReadout->lastWeightScan = 0;
+
+    tmpReadout->forceScan = false;
+
+    return(tmpReadout);
+}
+
+bool psMemCheckReadout(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) readoutFree);
+}
+
+
+pmCell *pmCellAlloc(
+    pmChip *chip,
+    const char *name)
+{
+    pmCell *tmpCell = (pmCell *) psAlloc(sizeof(pmCell));
+    psMemSetDeallocator(tmpCell, (psFreeFunc) cellFree);
+
+    tmpCell->config = NULL;
+    tmpCell->analysis = psMetadataAlloc();
+    tmpCell->readouts = psArrayAlloc(0);
+    tmpCell->parent = chip;
+    if (chip) {
+        chip->cells = psArrayAdd(chip->cells, 1, (psPtr) tmpCell);
+    }
+    tmpCell->hdu = NULL;
+    tmpCell->process = true;            // All cells are processed by default
+    tmpCell->file_exists = false;       // Not yet identified
+    tmpCell->data_exists = false;       // Not yet read in
+
+    tmpCell->concepts = psMetadataAlloc();
+    if (!psMetadataAddStr(tmpCell->concepts, PS_LIST_HEAD, "CELL.NAME", 0, NULL, name)) {
+        psErrorClear();
+        psWarning("Could not add CELL.NAME to metadata.");
+    }
+    tmpCell->conceptsRead = PM_CONCEPT_SOURCE_NONE;
+    // XXX does this work?  moved to conceptsRead... pmConceptsBlankCell(tmpCell);
+
+    return tmpCell;
+}
+
+bool psMemCheckCell(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) cellFree);
+}
+
+
+pmChip *pmChipAlloc(
+    pmFPA *fpa,
+    const char *name)
+{
+    pmChip *tmpChip = (pmChip*)psAlloc(sizeof(pmChip));
+    psMemSetDeallocator(tmpChip, (psFreeFunc) chipFree);
+
+    tmpChip->toFPA = NULL;
+    tmpChip->fromFPA = NULL;
+
+    tmpChip->analysis = psMetadataAlloc();
+    tmpChip->cells = psArrayAlloc(0);
+    tmpChip->parent = fpa;
+    if (fpa) {
+        psArrayAdd(fpa->chips, 1, (psPtr)tmpChip);
+    }
+    tmpChip->hdu = NULL;
+    tmpChip->process = true;            // Work on all chips, by default
+    tmpChip->file_exists = false;       // Not yet identified
+    tmpChip->data_exists = false;       // Not yet read in
+
+    tmpChip->concepts = psMetadataAlloc();
+    if (!psMetadataAddStr(tmpChip->concepts, PS_LIST_HEAD, "CHIP.NAME", 0, NULL, name)) {
+        psErrorClear();
+        psWarning("Could not add CHIP.NAME %s to concepts.", name);
+    }
+    tmpChip->conceptsRead = PM_CONCEPT_SOURCE_NONE;
+    // XXX does this work?  moved to conceptsRead... pmConceptsBlankChip(tmpChip);
+    return tmpChip;
+}
+
+bool psMemCheckChip(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) chipFree);
+}
+
+pmFPA *pmFPAAlloc(const psMetadata *camera, const char *cameraName)
+{
+    pmFPA *tmpFPA = (pmFPA *) psAlloc(sizeof(pmFPA));
+    psMemSetDeallocator(tmpFPA, (psFreeFunc) FPAFree);
+
+    tmpFPA->fromTPA = NULL;
+    tmpFPA->toTPA = NULL;
+    tmpFPA->toSky = NULL;
+
+    tmpFPA->analysis = psMetadataAlloc();
+    tmpFPA->camera = psMemIncrRefCounter((psPtr) camera);
+    tmpFPA->chips = psArrayAlloc(0);
+    tmpFPA->hdu = NULL;
+
+    tmpFPA->concepts = psMetadataAlloc();
+    if (!psMetadataAddStr(tmpFPA->concepts, PS_LIST_TAIL, "FPA.CAMERA", PS_META_REPLACE,
+                          "Camera name (according to configuration)", cameraName)) {
+        psErrorClear();
+        psWarning("Could not add FPA.CAMERA %s to concepts.", cameraName);
+    }
+    tmpFPA->conceptsRead = PM_CONCEPT_SOURCE_NONE;
+    // XXX does this work?  moved to conceptsRead... pmConceptsBlankFPA(tmpFPA);
+
+    // this may be somewhat pedantic, but it makes these things consistent
+    if (!psMetadataAddStr(tmpFPA->concepts, PS_LIST_TAIL, "FPA.NAME", PS_META_REPLACE,
+                          "name of FPA level", "fpa")) {
+        psErrorClear();
+        psWarning("Could not add FPA.NAME to concepts.");
+    }
+
+    return tmpFPA;
+}
+
+bool psMemCheckFPA(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) FPAFree);
+}
+
+
+// Check a cell to ensure that all component readouts have the parent pointer set correctly
+static bool cellCheckParents(pmCell *cell // Cell to check
+                            )
+{
+    PS_ASSERT_PTR_NON_NULL(cell, true);
+
+    bool flag = true;
+    for (long i = 0; i < cell->readouts->n ; i++) {
+        pmReadout *tmpReadout = (pmReadout *) cell->readouts->data[i];
+        if (!tmpReadout) {
+            continue;
+        }
+        if (tmpReadout->parent != cell) {
+            tmpReadout->parent = cell;
+            flag = false;
+        }
+    }
+    return flag;
+}
+
+// Check a chip to ensure that all component cells have the parent pointer set correctly
+static bool chipCheckParents(pmChip *chip // Chip to check
+                            )
+{
+    PS_ASSERT_PTR_NON_NULL(chip, true);
+
+    bool flag = true;
+    for (long i = 0; i < chip->cells->n ; i++) {
+        pmCell *tmpCell = (pmCell*)chip->cells->data[i];
+        if (!tmpCell) {
+            continue;
+        }
+        if (tmpCell->parent != chip) {
+            tmpCell->parent = chip;
+            flag = false;
+        }
+
+        flag &= cellCheckParents(tmpCell);
+    }
+    return flag;
+}
+
+psBool pmFPACheckParents(pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    bool flag = true;
+    for (long i = 0; i < fpa->chips->n ; i++) {
+        pmChip *tmpChip = (pmChip*)fpa->chips->data[i];
+        if (!tmpChip) {
+            continue;
+        }
+        if (tmpChip->parent != fpa) {
+            tmpChip->parent = fpa;
+            flag = false;
+        }
+
+        flag &= chipCheckParents(tmpChip);
+    }
+    return flag;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA.h	(revision 22322)
@@ -0,0 +1,243 @@
+/* @file pmFPA.h
+ * @brief Defines the focal plane hierarchy, along with functions for interacting with it
+ *
+ * @author George Gusciora, MHPCC
+ * @author Paul Price, IfA
+ * @author Eugene Magnier, IfA
+ *
+ * @version $Revision: 1.25 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-12 02:51:20 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_H
+#define PM_FPA_H
+
+#include <pslib.h>
+#include <pmHDU.h>
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+// Return chip position, given FPA position; calculations are all done in pixel units
+#define PM_FPA_TO_CHIP(pos, chip0, chipParity) \
+    (((pos) - (chip0))*(chipParity))
+
+// Return cell position, given chip position; calculations are all done in pixel units
+#define PM_CHIP_TO_CELL(pos, cell0, cellParity, binning) \
+    (((pos) - (cell0))*(cellParity)/(binning))
+
+// Return chip position, given a cell position; calculations are all done in pixel units
+#define PM_CELL_TO_CHIP(pos, cell0, cellParity, binning) \
+    ((pos)*(binning)*(cellParity) + (cell0))
+
+// Return FPA position, given a chip position; calculations are all done in pixel units
+#define PM_CHIP_TO_FPA(pos, chip0, chipParity) \
+    ((pos)*(chipParity) + (chip0))
+
+/// Focal plane array (the entirety of the camera)
+///
+/// The FPA is the top-level camera structure, and consists of one or more chips.  It also contains the
+/// concepts metadata appropriate to this level, a summary of analysis tasks that have been performed, the
+/// camera configuration information, any HDU that corresponds to this level for the file of interest, and
+/// astrometric transformations.  The astrometric transformations encode how to transform from the tangent
+/// plane to the sky, and back.
+typedef struct {
+    // Astrometric transformations
+    psPlaneTransform *fromTPA;  ///< Transformation from tangent plane to focal plane, or NULL
+    psPlaneTransform *toTPA;  ///< Transformation from focal plane to tangent plane, or NULL
+    psProjection *toSky;         ///< Projection from tangent plane to sky, or NULL
+    // Information
+    psMetadata *concepts;               ///< FPA-level concepts
+    unsigned int conceptsRead;          ///< Which concepts have been read; see pmConceptsSource
+    psMetadata *analysis;               ///< FPA-level analysis metadata
+    const psMetadata *camera;           ///< Camera configuration
+    psArray *chips;                     ///< The component chips
+    pmHDU *hdu;                         ///< FITS header data unit of interest, or NULL
+} pmFPA;
+
+/// A chip (contiguous detector element)
+///
+/// The chip is the mid-level camera structure, being part of an FPA, and consisting of one or more cells
+/// (e.g., a CCD).  It also contains the concepts metadata appropriate to this level, a summary of analysis
+/// tasks that have been performed, status flags, any HDU that corresponds to this level for the file of
+/// interest, and astrometric transformations.  The astrometric transformations provide transforms between the
+/// chip and FPA coordinates and back.
+typedef struct {
+    // Astrometric transformations
+    psPlaneTransform *toFPA;            ///< Transformation from chip to FPA coordinates, or NULL
+    psPlaneTransform *fromFPA;          ///< Transformation from FPA to chip coordinates, or NULL
+    // Information
+    psMetadata *concepts;               ///< Chip-level concepts
+    unsigned int conceptsRead;          ///< Which concepts have been read; see pmConceptsSource
+    psMetadata *analysis;               ///< Chip-level analysis metadata
+    psArray *cells;                     ///< The component cells
+    pmFPA *parent;                      ///< Parent FPA
+    bool process;                       ///< Do we bother about reading and working with this chip?
+    bool file_exists;                   ///< Does the file for this chip exist (read case only)?
+    bool data_exists;                   ///< Does the data for this chip exist (read case only)?
+    pmHDU *hdu;                         ///< FITS header data unit of interest,
+} pmChip;
+
+/// A cell (smallest logical unit)
+///
+/// A cell is the lowest-level camera structure, being part of a chip (e.g., an amplifier).  It may consist of
+/// one or more readouts, which are individual reads of the cell.  It also contains the concepts metadata
+/// appropriate to this level, the cell configuration information (for convenience) from the camera
+/// configuration, a summary of analysis tasks that have been performed, status flags, and any HDU that
+/// corresponds to this level for the file of interest
+
+/** Cell data structure
+ *
+ *  A cell consists of one or more readouts.  It also contains a pointer to the
+ *  cell's metadata, and its parent chip.  On the astrometry side, it also
+ *  contains coordinate transforms from the cell to chip, from the cell to
+ *  focal-plane, as well as a "quick and dirty" tranform from the cell to
+ *  sky coordinates.
+ *
+ */
+typedef struct {
+    psMetadata *concepts;               ///< Cell-level concepts
+    unsigned int conceptsRead;          ///< Which concepts have been read; see pmConceptsSource
+    psMetadata *config;                 ///< Cell configuration information (from CELLS in the camera config)
+    psMetadata *analysis;               ///< Cell-level analysis metadata
+    psArray *readouts;                  ///< The component readouts
+    pmChip *parent;                     ///< Parent chip
+    bool process;                       ///< Do we bother about reading and working with this cell?
+    bool file_exists;                   ///< Does the file for this cell exist (read case only)?
+    bool data_exists;                   ///< Does the data for this cell exist (read case only)?
+    pmHDU *hdu;                         ///< FITS header data unit of interest
+} pmCell;
+
+/// A readout (individual read of a cell)
+///
+/// A readout corresponds to an individual read of a cell (e.g., a single image as part of a video sequence,
+/// or one of multiple coadds).  It contains the actual pixels used in analysis (along with mask and weight
+/// maps).  When reading from a FITS file, the images are subimages (from CELL.TRIMSEC) of the pixels read
+/// from the appropriate HDU (at the FPA, chip or cell level).  The readout also contains a list of bias
+/// sections (prescans or overscans, or otherwise), a summary of analysis tasks that have been performed,
+/// status flags, and the offsets used for reading a FITS file incrementally.
+typedef struct {
+    int col0;                           ///< Column offset; non-zero if reading in columns incrementally
+    int row0;                           ///< Row offset; non-zero if reading in rows incrementally
+    psImage *image;                     ///< Imaging area of readout (corresponds to CELL.TRIMSEC region)
+    psImage *mask;                      ///< Mask of input image (corresponds to CELL.TRIMSEC region)
+    psImage *weight;                    ///< Weight of input image (corresponds to CELL.TRIMSEC region)
+    psList *bias;                       ///< List of bias (prescan/overscan) images
+    psMetadata *analysis;               ///< Readout-level analysis metadata
+    pmCell *parent;                     ///< Parent cell
+    bool process;                       ///< Do we bother about reading and working with this readout?
+    bool file_exists;                   ///< Does the file for this readout exist (read case only)?
+    bool data_exists;                   ///< Does the data for this readout exist (read case only)?
+    int thisImageScan;                  ///< start scan for next/current read
+    int lastImageScan;                  ///< start scan of the last read
+    int thisMaskScan;                   ///< start scan for next/current read
+    int lastMaskScan;                   ///< start scan of the last read
+    int thisWeightScan;                 ///< start scan for next/current read
+    int lastWeightScan;                 ///< start scan of the last read
+    bool forceScan;                     ///< Force pmFPARead to obey the above commanded this and last scans.
+} pmReadout;
+
+/// Free all readouts within a cell
+void pmCellFreeReadouts(pmCell *cell);    ///< Cell for which to free readouts
+
+/// Free all cells within a chip
+void pmChipFreeCells(pmChip *chip);       ///< Chip for which to free cells
+
+/// Free all data within a readout
+void pmReadoutFreeData(pmReadout *readout); ///< Readout for which to free data
+
+/// Free all data within a cell (all readouts as well as metadata)
+void pmCellFreeData(pmCell *cell);        ///< Cell for which to free data
+
+/// Free all data within a chip (all cells as well as metadata)
+void pmChipFreeData(pmChip *chip);        ///< Chip for which to free data
+
+/// Free all data within an FPA (all chips as well as metadata)
+void pmFPAFreeData(pmFPA *fpa);           ///< FPA for which to free data
+
+/// Allocate a readout associated with a cell
+pmReadout *pmReadoutAlloc(pmCell *cell);  ///< Parent cell, or NULL
+bool psMemCheckReadout(psPtr ptr);
+
+/// Allocate a cell associated with a chip
+///
+/// The name is used to set CELL.NAME within the concepts.
+pmCell *pmCellAlloc(pmChip *chip,       ///< Parent chip, or NULL
+                    const char *name);  ///< Name of cell, for CELL.NAME
+bool psMemCheckCell(psPtr ptr);
+
+
+/// Allocate a chip associated with an FPA
+///
+/// The name is used to set CHIP.NAME within the concepts
+pmChip *pmChipAlloc(pmFPA *fpa,         ///< Parent FPA, or NULL
+                    const char *name);  ///< Name of chip, for CHIP.NAME
+bool psMemCheckChip(psPtr ptr);
+
+
+/// Allocate an FPA
+pmFPA *pmFPAAlloc(const psMetadata *camera, ///< Camera configuration (to store in FPA)
+                  const char *cameraName ///< Name of camera (for FPA.CAMERA concept)
+    );
+bool psMemCheckFPA(psPtr ptr);
+
+/// Check parent links within an FPA
+///
+/// Iterates through the FPA to verify that the "parent" links in the chip, cell and readout are set
+/// correctly.  If there are any incorrect links, they are fixed, and the function returns false.
+bool pmFPACheckParents(pmFPA *fpa);     ///< FPA to check
+
+
+/// Assertions
+
+/// Check that the fundamentals of a readout are set
+#define PM_ASSERT_READOUT_NON_NULL(READOUT, RETVAL) { \
+    if (!(READOUT) || !(READOUT)->bias || !(READOUT)->analysis) { \
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Readout %s or one of its components is NULL.", #READOUT); \
+        return RETVAL; \
+    } \
+    int numCols = 0, numRows = 0; /* Size of readout images */ \
+    psImage *image = (READOUT)->image; /* Image pixels */ \
+    if (image) { \
+        PS_ASSERT_IMAGE_TYPE((READOUT)->image, PS_TYPE_F32, RETVAL); \
+        numCols = image->numCols; \
+        numRows = image->numRows; \
+    } \
+    psImage *mask = (READOUT)->mask; /* Mask pixels */ \
+    if (mask) { \
+        PS_ASSERT_IMAGE_NON_NULL((READOUT)->mask, RETVAL); \
+        if ((numCols != 0 || numRows != 0) && (mask->numCols != numCols || mask->numRows != numRows)) { \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Mask in readout %s has wrong size (%dx%d vs %dx%d)", \
+                    #READOUT, mask->numCols, mask->numRows, numCols, numRows); \
+            return RETVAL; \
+        } else { \
+            numCols = mask->numCols; \
+            numRows = mask->numRows; \
+        } \
+    } \
+    psImage *weight = (READOUT)->weight; /* Weight map pixels */ \
+    if (weight) { \
+        PS_ASSERT_IMAGE_NON_NULL((READOUT)->weight, RETVAL); \
+        if ((numCols != 0 || numRows != 0) && (weight->numCols != numCols || weight->numRows != numRows)) { \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Weight in readout %s has wrong size (%dx%d vs %dx%d)", \
+                    #READOUT, weight->numCols, weight->numRows, numCols, numRows); \
+            return RETVAL; \
+        } \
+    } \
+}
+
+/// Assert that a readout contains an image
+#define PM_ASSERT_READOUT_IMAGE(READOUT, RETVAL) \
+    PS_ASSERT_IMAGE_NON_NULL((READOUT)->image, RETVAL);
+
+/// Assert that a readout contains a mask
+#define PM_ASSERT_READOUT_MASK(READOUT, RETVAL) \
+    PS_ASSERT_IMAGE_NON_NULL((READOUT)->mask, RETVAL);
+
+/// Assert that a readout contains a weight map
+#define PM_ASSERT_READOUT_WEIGHT(READOUT, RETVAL) \
+    PS_ASSERT_IMAGE_NON_NULL((READOUT)->weight, RETVAL);
+
+/// @}
+#endif // #ifndef PM_FPA_H
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAAstrometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAAstrometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAAstrometry.c	(revision 22322)
@@ -0,0 +1,512 @@
+#include <stdio.h>
+#include "pslib.h"
+#include "pmFPAAstrometry.h"
+#include "pmFPA.h"
+
+
+/*****************************************************************************
+checkValidImageCoords(): this is a private function which simply determines if
+the supplied x,y coordinates are in the range for the supplied psImage.
+ 
+XXX: What about col0 and row0
+XXX: This should return a psBool.
+XXX: Macro this for speed.
+ *****************************************************************************/
+static psS32 checkValidImageCoords(
+    double x,
+    double y,
+    psImage* tmpImage)
+{
+    PS_ASSERT_IMAGE_NON_NULL(tmpImage, 0);
+
+    // The FLT_EPSILON is because -0.0 was failing this.
+    if (((x+FLT_EPSILON) < 0.0) || (x > (double)tmpImage->numCols) ||
+            ((y+FLT_EPSILON) < 0.0) || (y > (double)tmpImage->numRows)) {
+        return (0);
+    }
+
+    return (1);
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+pmCell* pmCellInFPA(
+    const psPlane* fpaCoord,
+    const pmFPA* FPA)
+{
+    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(FPA, NULL);
+
+    pmChip* tmpChip = NULL;
+    psPlane chipCoord;
+    pmCell* outCell = NULL;
+
+    // Determine which chip contains the fpaCoords.
+    tmpChip = pmChipInFPA(fpaCoord, FPA);
+    if (tmpChip == NULL) {
+        return(NULL);
+    }
+
+    // Convert to those chip coordinates.
+    psPlane *rc = pmCoordFPAToChip(&chipCoord, fpaCoord, tmpChip);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not determine Chip coords.\n");
+        return(NULL);
+    }
+
+    // Determine which cell contains those chip coordinates.
+    outCell = pmCellInChip(&chipCoord, tmpChip);
+    if (outCell == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not determine the cell.\n");
+        return(NULL);
+    }
+
+    return (outCell);
+}
+
+pmChip* pmChipInFPA(
+    const psPlane* fpaCoord,
+    const pmFPA* FPA)
+{
+    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(FPA, NULL);
+    PS_ASSERT_PTR_NON_NULL(FPA->chips, NULL);
+
+    psArray* chips = FPA->chips;
+    psS32 nChips = chips->n;
+    psPlane chipCoord;
+    pmCell *tmpCell = NULL;
+
+    //
+    // Loop through every chip in this FPA.  Convert the original FPA
+    // coordinates to chip coordinates for that chip.  Then, determine if any
+    // cells in that chip contain those chip coordinates.
+    // XXX: Depending on the number of chips, and their topology, there may be
+    // a much more efficient way of doing this.
+    //
+    for (psS32 i = 0; i < nChips; i++) {
+        pmChip* tmpChip = chips->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpChip, NULL);
+        PS_ASSERT_PTR_NON_NULL(tmpChip->fromFPA, NULL);
+
+        psPlaneTransformApply(&chipCoord, tmpChip->fromFPA, fpaCoord);
+
+        tmpCell = pmCellInChip(&chipCoord, tmpChip);
+        if (tmpCell != NULL) {
+            return(tmpChip);
+        }
+    }
+
+    psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not determine the chip.\n");
+    return (NULL);
+}
+
+
+pmCell* pmCellInChip(
+    const psPlane* chipCoord,
+    const pmChip* chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chipCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    psPlane cellCoord;
+    psArray* cells;
+
+    cells = chip->cells;
+    if (cells == NULL) {
+        return NULL;
+    }
+
+    //
+    // We loop over each cell in the chip.  We transform the chipCoord into
+    // a cellCoord for that cell and determine if that cellCoord is valid.
+    // If so, then we return that cell.
+    // XXX: Depending on the number of cells, and their topology, there may be
+    // a much more efficient way of doing this.
+    //
+    for (psS32 i = 0; i < cells->n; i++) {
+        pmCell* tmpCell = (pmCell* ) cells->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpCell, NULL);
+
+        psPlaneTransform *chipToCell = NULL;
+        if (true ==  p_psIsProjectionLinear(tmpCell->toChip)) {
+            chipToCell = p_psPlaneTransformLinearInvert(tmpCell->toChip);
+        } else {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: non-linear cell->chip transforms are not yet implemented.\n");
+            //chipToCell = psPlaneTransformInvert(NULL, tmpCell->toChip, NULL, -1);
+            chipToCell = NULL;
+        }
+        if (chipToCell == NULL) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not invert the Cell->toChip transform.\n");
+            return(NULL);
+        }
+        psArray* readouts = tmpCell->readouts;
+
+        if (readouts != NULL) {
+            for (psS32 j = 0; j < readouts->n; j++) {
+                pmReadout* tmpReadout = readouts->data[j];
+                PS_ASSERT_READOUT_NON_NULL(tmpReadout, NULL);
+
+                psPlaneTransformApply(&cellCoord,
+                                      chipToCell,
+                                      chipCoord);
+
+                if (checkValidImageCoords(cellCoord.x,
+                                          cellCoord.y,
+                                          tmpReadout->image)) {
+                    psFree(chipToCell);
+                    return (tmpCell);
+                }
+            }
+        }
+        psFree(chipToCell);
+    }
+
+    //psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not determine the cell.\n");
+    return (NULL);
+}
+
+
+psPlane* pmCoordCellToFPA(
+    psPlane* fpaCoord,
+    const psPlane* cellCoord,
+    const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    psPlane *rc = psPlaneTransformApply(fpaCoord, cell->toFPA, cellCoord);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform cell coords to FPA coords.\n");
+    }
+    return(rc);
+}
+
+
+psPlane* pmCoordChipToFPA(
+    psPlane* outCoord,
+    const psPlane* inCoord,
+    const pmChip* chip)
+{
+    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    psPlane *rc = psPlaneTransformApply(outCoord, chip->toFPA, inCoord);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform chip coords to FPA coords.\n");
+    }
+    return(rc);
+}
+
+
+psPlane* pmCoordFPAToChip(
+    psPlane* chipCoord,
+    const psPlane* fpaCoord,
+    const pmChip* chip)
+{
+    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip->fromFPA, NULL);
+
+    psPlane *rc = psPlaneTransformApply(chipCoord, chip->fromFPA, fpaCoord);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform FPA coords to Chip coords.\n");
+    }
+    return(rc);
+}
+
+psPlane* pmCoordCellToChip(
+    psPlane* outCoord,
+    const psPlane* inCoord,
+    const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    psPlane *rc = psPlaneTransformApply(outCoord, cell->toChip, inCoord);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform Cell coords to Chip coords.\n");
+    }
+    return(rc);
+}
+
+psPlane* pmCoordChipToCell(
+    psPlane* cellCoord,
+    const psPlane* chipCoord,
+    const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(chipCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+
+    pmCell *tmpCell = pmCellInChip(chipCoord, cell->parent);
+    if (tmpCell == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not determine the proper cell.\n");
+        return(NULL);
+    }
+
+    psPlaneTransform *tmpChipToCell = NULL;
+    PS_ASSERT_PTR_NON_NULL(tmpCell->toChip, NULL);
+    if (true ==  p_psIsProjectionLinear(tmpCell->toChip)) {
+        tmpChipToCell = p_psPlaneTransformLinearInvert(tmpCell->toChip);
+    } else {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: non-linear cell->chip transforms are not yet implemented.\n");
+        // XXX: tmpChipToCell = psPlaneTransformInvert(NULL, tmpCell->toChip, NULL, -1);
+        tmpChipToCell = NULL;
+    }
+    if (tmpChipToCell == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not invert the Cell->toChip transform.\n");
+        return(NULL);
+    }
+
+    psPlane *rc = psPlaneTransformApply(cellCoord, tmpChipToCell, chipCoord);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform Chip coords to Cell coords.\n");
+    }
+    psFree(tmpChipToCell);
+    return(rc);
+}
+
+psPlane* pmCoordFPAToTP(
+    psPlane* outCoord,
+    const psPlane* inCoord,
+    double color,
+    double magnitude,
+    const pmFPA* fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    psPlane *rc = psPlaneDistortApply(outCoord, fpa->toTangentPlane, inCoord, color, magnitude);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform FPA coords to tangent plane coords.\n");
+    }
+    return(rc);
+}
+
+psPlane* pmCoordTPToFPA(
+    psPlane* fpaCoord,
+    const psPlane* tpCoord,
+    double color,
+    double magnitude,
+    const pmFPA* fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(tpCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa->fromTangentPlane, NULL);
+
+    psPlane *rc = psPlaneDistortApply(fpaCoord, fpa->fromTangentPlane, tpCoord, color, magnitude);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform tangent plane coords to FPA coords.\n");
+    }
+    return(rc);
+}
+
+
+/*****************************************************************************
+XXXDeproject(outSphere, coord, projection): This private routine is a wrapper
+for p_psDeproject().  The reason: p_psDeproject() and p_psProject() combined
+do not seem to produce the original coordinates when they even though they
+should.  XXXDeproject() simply negates the ->r and ->d members of the output
+psSphere if the input ->y is larger than 0.0.  I don't know why it works.
+ 
+I'm guessing the p_psProject() and p_psDeproject() functions have bugs.
+ 
+XXX: It appears that p_psProject() and p_psDeproject() have been fixed.
+Remove this.
+ *****************************************************************************/
+psSphere* XXXDeproject(
+    psSphere *outSphere,
+    const psPlane* coord,
+    const psProjection* projection)
+{
+    psSphere *rc = p_psDeproject(outSphere, coord, projection);
+
+    if (coord->y >= 0.0) {
+        rc->d = -rc->d;
+        rc->r = -rc->r;
+    }
+
+    return(rc);
+}
+
+/*****************************************************************************
+  *****************************************************************************/
+psSphere* pmCoordTPToSky(
+    psSphere* outSphere,
+    const psPlane* tpCoord,
+    const psProjection *projection)
+{
+    PS_ASSERT_PTR_NON_NULL(tpCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(projection, NULL);
+
+    //    psSphere *rc = XXXDeproject(outSphere, tpCoord, projection);
+    psSphere *rc = p_psDeproject(outSphere, tpCoord, projection);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform tangent plane coords to sky coords.\n");
+    }
+    return(rc);
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+psPlane* pmCoordSkyToTP(
+    psPlane* tpCoord,
+    const psSphere* in,
+    const psProjection *projection)
+{
+    PS_ASSERT_PTR_NON_NULL(in, NULL);
+    PS_ASSERT_PTR_NON_NULL(projection, NULL);
+
+    psPlane *rc = p_psProject(tpCoord, in, projection);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform sky to tangent plane coords.\n");
+    }
+    return(rc);
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+psSphere* pmCoordCellToSky(
+    psSphere* skyCoord,
+    const psPlane* cellCoord,
+    double color,
+    double magnitude,
+    const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->toFPA, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->toTangentPlane, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->projection, NULL);
+    psPlane fpaCoord;
+    psPlane tpCoord;
+    psPlane *rc;
+    pmFPA* parFPA = (cell->parent)->parent;
+
+    // Convert the input cell coordinates to FPA coordinates.
+    rc = psPlaneTransformApply(&fpaCoord, cell->toFPA, cellCoord);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could transform cell coords to FPA coords.\n");
+        return(NULL);
+    }
+
+    // Convert the FPA coordinates to tangent plane Coordinates.
+    rc = psPlaneDistortApply(&tpCoord, parFPA->toTangentPlane, &fpaCoord, color, magnitude);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could transform FPA coords to tangent plane coords.\n");
+        return(NULL);
+    }
+
+    // Convert the tangent plane Coordinates to sky coordinates.
+    psSphere *rc2 = pmCoordTPToSky(skyCoord, &tpCoord, parFPA->projection);
+    if (rc2 == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform cell coords to sky coords.\n");
+    }
+
+    return(rc2);
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+psPlane* pmCoordSkyToCell(
+    psPlane* cellCoord,
+    const psSphere* skyCoord,
+    float color,
+    float magnitude,
+    const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(skyCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
+    pmChip *parChip = cell->parent;
+    pmFPA *parFPA = parChip->parent;
+    psPlane tpCoord;
+    psPlane fpaCoord;
+    psPlane chipCoord;
+    psPlane *rc;
+
+    // Convert the skyCoords to tangent plane coords.
+    rc = pmCoordSkyToTP(&tpCoord, skyCoord, parFPA->projection);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not determine tangent plane coords.\n");
+        return(NULL);
+    }
+
+    // Convert the tangent plane coords to FPA coords.
+    rc = pmCoordTPToFPA(&fpaCoord, &tpCoord, color, magnitude, parFPA);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not determine FPA coords.\n");
+        return(NULL);
+    }
+
+    // Convert the FPA coords to chip coords.
+    rc = pmCoordFPAToChip(&chipCoord, &fpaCoord, parChip);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not determine chip coords.\n");
+        return(NULL);
+    }
+
+    // Convert the chip coords to cell coords.
+    rc = pmCoordChipToCell(cellCoord, &chipCoord, cell);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not determine cell coords.\n");
+        return(NULL);
+    }
+
+    return (cellCoord);
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+psSphere* pmCoordCellToSkyQuick(
+    psSphere* outSphere,
+    const psPlane* cellCoord,
+    const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->toSky, NULL);
+    psPlane outPlane;
+    psPlane *rc;
+    rc = psPlaneTransformApply(&outPlane, cell->toSky, cellCoord);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could transform cell coords to sky coords.\n");
+        return(NULL);
+    }
+
+    psSphere *out = outSphere;
+    if (out == NULL) {
+        out = psSphereAlloc();
+    }
+    out->r = outPlane.y;
+    out->d = outPlane.x;
+
+    return(out);
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+psPlane* pmCoordSkyToCellQuick(
+    psPlane* cellCoord,
+    const psSphere* skyCoord,
+    const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(skyCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->toSky, NULL);
+    psPlane skyPlane;
+    skyPlane.y = skyCoord->r;
+    skyPlane.x = skyCoord->d;
+
+    psPlane *rc = psPlaneTransformApply(cellCoord, cell->toSky, &skyPlane);
+    if (rc == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not transform sky to cell coords.\n");
+    }
+    return(cellCoord);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAAstrometry.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAAstrometry.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAAstrometry.h	(revision 22322)
@@ -0,0 +1,188 @@
+#ifndef PM_FPA_ASTROMETRY_H
+#define PM_FPA_ASTROMETRY_H
+
+#include "pslib.h"
+#include "pmFPA.h"
+
+/** Find cooresponding cell for given FPA coordinate
+ *
+ *  @return pmCell*    the cell cooresponding to the coord in FPA
+ */
+pmCell* pmCellInFPA(
+    const psPlane* coord,              ///< the coordinate in FPA plane
+    const pmFPA* FPA                   ///< the FPA to search for the cell
+);
+
+
+/** Find cooresponding chip for given FPA coordinate
+ *
+ *  @return pmChip*    the chip cooresponding to coord
+ */
+pmChip* pmChipInFPA(
+    const psPlane* coord,              ///< the coordinate in FPA plane
+    const pmFPA* FPA                   ///< the FPA to search for the cell
+);
+
+
+/** Find cooresponding cell for given Chip coordinate
+ *
+ *  @return pmCell*    the cell cooresponding to coord
+ */
+pmCell* pmCellInChip(
+    const psPlane* coord,              ///< the coordinate in Chip plane
+    const pmChip* chip                 ///< the chip to search for the cell
+);
+
+
+/** Translate a cell coordinate into a chip coordinate
+ *
+ *  @return psPlane*    the resulting chip coordinate
+ */
+psPlane* pmCoordCellToChip(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within Cell
+    const pmCell* cell                 ///< the Cell in interest
+);
+
+
+/** Translate a chip coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* pmCoordChipToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within Chip
+    const pmChip* chip                 ///< the chip in interest
+);
+
+
+/** Translate a FPA coordinate into a Tangent Plane coordinate
+ *
+ *  @return psPlane*    the resulting Tangent Plane coordinate
+ */
+psPlane* pmCoordFPAToTP(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within FPA
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const pmFPA* fpa                   ///< the FPA in interest
+);
+
+
+/** Translate a Tangent Plane coordinate into a Sky coordinate
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* pmCoordTPToSky(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                ///< the coordinate within Tangent Plane
+    const psProjection *projection
+);
+
+/** Translate a cell coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* pmCoordCellToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    const pmCell* cell                 ///< the cell in interest
+);
+
+
+/** Translate a cell coordinate into a Sky coordinate
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* pmCoordCellToSky(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const pmCell* cell                 ///< the cell in interest
+);
+
+
+/** Translate a cell coordinate into a Sky coordinate using a 'quick and
+ *  dirty' method
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* pmCoordCellToSkyQuick(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    const pmCell* cell                 ///< the cell in interest
+);
+
+
+/** Translate a Sky coordinate into a Tangent Plane coordinate
+ *
+ *  @return psPlane*    the resulting Tangent Plane coordinate
+ */
+psPlane* pmCoordSkyToTP(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the sky coordinate
+    const psProjection *projection
+);
+
+/** Translate a Tangent Plane coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* pmCoordTPToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within tangent plane
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const pmFPA* fpa                   ///< the FPA of interest
+);
+
+
+/** Translate a FPA coordinate into a chip coordinate
+ *
+ *  @return psPlane*    the resulting chip coordinate
+ */
+psPlane* pmCoordFPAToChip(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the FPA coordinate
+    const pmChip* chip                 ///< the chip of interest
+);
+
+
+/** Translate a chip coordinate into a cell coordinate
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* pmCoordChipToCell(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the Chip coordinate
+    const pmCell* cell                 ///< the cell of interest
+);
+
+
+/** Translate a sky coordinate into a cell coordinate
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* pmCoordSkyToCell(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the Sky coordinate
+    float color,                       ///< Color of source
+    float magnitude,                   ///< Magnitude of source
+    const pmCell* cell                 ///< the cell of interest
+);
+
+
+/** Translate a sky coordinate into a cell coordinate using a 'quick and
+ *  dirty' method
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* pmCoordSkyToCellQuick(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the Sky coordinate
+    const pmCell* cell                 ///< the cell of interest
+);
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPABin.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPABin.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPABin.c	(revision 22322)
@@ -0,0 +1,83 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmFPA.h"
+#include "pmFPABin.h"
+
+bool pmReadoutRebin(pmReadout *out, const pmReadout *in, psMaskType maskVal, int xBin, int yBin)
+{
+    PM_ASSERT_READOUT_NON_NULL(out, false);
+    PM_ASSERT_READOUT_NON_NULL(in, false);
+
+    psImage *inImage = in->image, *inMask = in->mask; // Input image
+    int numColsIn = inImage->numCols, numRowsIn = inImage->numRows; // Size of input image
+
+    psImageBinning *binning = psImageBinningAlloc(); // Binning instructions
+    binning->nXbin = xBin;
+    binning->nYbin = yBin;
+    binning->nXfine = numColsIn;
+    binning->nYfine = numRowsIn;
+    binning->nXskip = 0;
+    binning->nYskip = 0;
+    psImageBinningSetRuffSize(binning, PS_IMAGE_BINNING_CENTER);
+
+    int numColsOut = binning->nXruff, numRowsOut = binning->nYruff; // Size of output image
+
+    // Output image
+    psImage *outImage = out->image = psImageRecycle(out->image,  numColsOut, numRowsOut, PS_TYPE_F32);
+
+    int xLast = numColsIn - 1, yLast = numRowsIn - 1; // Last index
+    int yStart = psImageBinningGetFineY(binning, 0); // Starting input y for binning
+    for (int yOut = 0; yOut < numRowsOut; yOut++) {
+        int yStop = psImageBinningGetFineY(binning, yOut + 1); // Stopping input y for binning
+        yStop = PS_MIN(yStop, yLast);
+        int xStart = psImageBinningGetFineX(binning, 0); // Starting input x for binning
+        for (int xOut = 0; xOut < numColsOut; xOut++) {
+            int xStop = psImageBinningGetFineX(binning, xOut + 1); // Stopping input x for binning
+            xStop = PS_MIN(xStop, xLast);
+
+            float sum = 0.0;            // Sum of pixels
+            int numPix = 0;             // Number of pixels
+            for (int y = yStart; y < yStop; y++) {
+                for (int x = xStart; x < xStop; x++) {
+                    if (inMask && (inMask->data.PS_TYPE_MASK_DATA[y][x] & maskVal)) {
+                        continue;
+                    }
+                    sum += inImage->data.F32[y][x];
+                    numPix++;
+                }
+            }
+
+            outImage->data.F32[yOut][xOut] = numPix > 0 ? sum / numPix : NAN;
+            xStart = xStop;
+        }
+        yStart = yStop;
+    }
+
+    psFree(binning);
+
+    out->data_exists = true;
+    if (out->parent) {
+        pmCell *outCell = out->parent;  // Output cell
+        outCell->data_exists = outCell->parent->data_exists = true;
+
+        if (in->parent) {
+            pmCell *inCell = in->parent;// Input cell
+            psAssert(outCell != inCell, "Can't copy in place!");
+            psFree(outCell->concepts);
+            outCell->concepts = psMetadataCopy(NULL, inCell->concepts);
+        }
+
+        // Update the concepts with the new values for the binning
+        psMetadataItem *binItem = psMetadataLookup(outCell->concepts, "CELL.XBIN");
+        binItem->data.S32 *= xBin;
+        binItem = psMetadataLookup(outCell->concepts, "CELL.YBIN");
+        binItem->data.S32 *= yBin;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPABin.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPABin.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPABin.h	(revision 22322)
@@ -0,0 +1,17 @@
+#ifndef PM_FPA_BIN_H
+#define PM_FPA_BIN_H
+
+#include <pslib.h>
+
+#include <pmFPA.h>
+
+/// Rebin a readout
+bool pmReadoutRebin(pmReadout *out,     ///< Output readout
+                    const pmReadout *in,///< Input readout
+                    psMaskType maskVal, ///< Value to mask
+                    int xBin, int yBin  ///< Binning factors in x and y
+    );
+
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACalibration.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACalibration.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACalibration.c	(revision 22322)
@@ -0,0 +1,63 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+
+#include "pmFPACalibration.h"
+
+float pmFPADarkNorm(const pmFPA *fpa, const pmFPAview *view, float expTime)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NAN);
+    PS_ASSERT_PTR_NON_NULL(view, NAN);
+
+    psMetadata *darkNorms = psMetadataLookupMetadata(NULL, fpa->camera, "DARK.NORM"); // Dark normalisations
+    if (!darkNorms) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find DARK.NORM in camera configuration.");
+        return NAN;
+    }
+
+    const char *key = psMetadataLookupStr(NULL, fpa->camera, "DARK.NORM.KEY"); // Key for normalisation
+    if (!key || strlen(key) == 0) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find DARK.NORM.KEY in camera configuration.");
+        return NAN;
+    }
+
+    psString keyResolved = pmFPANameFromRule(key, fpa, view); // Resolved version
+    if (!keyResolved || strlen(keyResolved) == 0) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to resolve DARK.NORM.KEY: %s", key);
+        return NAN;
+    }
+
+    psMetadata *polyMD = psMetadataLookupMetadata(NULL, darkNorms, keyResolved); // Metadata with polynomial
+    if (!polyMD) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find %s polynomial in the DARK.NORM metadata", keyResolved);
+        psFree(keyResolved);
+        return NAN;
+    }
+
+    psPolynomial1D *poly = psPolynomial1DfromMetadata(polyMD); // Polynomial
+    if (!poly) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine polynomial from %s in the DARK.NORM metadata",
+                keyResolved);
+        psFree(keyResolved);
+        return NAN;
+    }
+    psFree(keyResolved);
+
+    float value = psPolynomial1DEval(poly, expTime);
+
+    psFree(poly);
+
+    return value;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACalibration.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACalibration.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACalibration.h	(revision 22322)
@@ -0,0 +1,17 @@
+#ifndef PM_FPA_CALIBRATION_H
+#define PM_FPA_CALIBRATION_H
+
+/// Return the dark normalisation value
+///
+/// Unfortunately, dark current is not linear with the exposure time, but application of a polynomial
+/// correction to the exposure time should make it linear.  This function returns the appropriate value with
+/// which to normalise a dark frame.  The polynomial is obtained from DARK.NORM in the camera configuration.
+/// The specific polynomial metadata to use is provided by DARK.NORM.KEY, which is keyword expanded in the
+/// usual manner (e.g., try "{CHIP.NAME}").
+float pmFPADarkNorm(const pmFPA *fpa,   ///< FPA for which to get the normalisation
+                    const pmFPAview *view, ///< View to the FPA component of interest
+                    float expTime       ///< The nominal exposure time
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAConstruct.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAConstruct.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAConstruct.c	(revision 22322)
@@ -0,0 +1,1577 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <strings.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAFlags.h"
+#include "pmConcepts.h"
+#include "pmFPAConstruct.h"
+#include "pmFPAUtils.h"
+#include "pmHDUUtils.h"
+
+#define TABLE_OF_CONTENTS "CONTENTS"    // Name for camera format metadata containing the contents
+#define CHIP_TYPES "CHIPS"              // Name for camera format metadata containing the chip types
+#define CELL_TYPES "CELLS"              // Name for camera format metadata containing the cell types
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Read data for a particular cell from the camera format description
+static psMetadata *getCellData(const psMetadata *format, // The camera format description
+                               const char *cellName // The name of the cell
+                              )
+{
+    assert(format);
+    assert(cellName && strlen(cellName) > 0);
+
+    bool status = true;                 // Result of MD lookup
+    psMetadata *cells = psMetadataLookupMetadata(&status, format, CELL_TYPES); // The CELLS
+    if (!status || !cells) {
+        psError(PS_ERR_IO, true, "Unable to find %s in camera format.\n", CELL_TYPES);
+        return NULL;
+    }
+
+    psMetadata *cellData = psMetadataLookupMetadata(&status, cells, cellName); // The data for the particular cell
+    if (!status || !cellData) {
+        psWarning("Unable to find specs for cell %s: ignored\n", cellName);
+    }
+
+    return cellData;
+}
+
+// Parse a list of first:second:third pairs in a string
+static int parseContent(psArray **first, // Array of the first values
+                         psArray **second, // Array of the second values
+                         psArray **third, // Array of the third values
+                         const char *string // The string to parse
+                        )
+{
+    assert(string && strlen(string) > 0);
+    // Must populate 'first', 'second', 'third' in order.
+    assert(!second || first);
+    assert(!third || second);
+
+    int numArrays = third ? 3 : (second ? 2 : 1); // Number of arrays
+
+    psList *values = psStringSplit(string, " ,;", true); // List of the parts
+    int num = values->n; // number of parsed content elements
+
+    // extend the arrays if they exist, create new ones if they don't
+    if (first && !*first) {
+        *first = psArrayAllocEmpty(values->n);
+    }
+    if (second && !*second) {
+        *second = psArrayAllocEmpty(values->n);
+    }
+    if (third && !*third) {
+        *third = psArrayAllocEmpty(values->n);
+    }
+
+    psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false); // Iterator for values
+    psString value = NULL;               // "first:second:third" string
+    while ((value = psListGetAndIncrement(valuesIter))) {
+        psArray *fst = psStringSplitArray(value, ":", true); // First, second, third
+        switch (numArrays) {
+          case 3:
+            psArrayAdd(*third, 8, fst->data[2]);
+          case 2:
+            psArrayAdd(*second, 8, fst->data[1]);
+          case 1:
+            psArrayAdd(*first, 8, fst->data[0]);
+            break;
+        default:
+          psAbort("Should never get here.");
+        }
+        psFree(fst);
+    }
+    psFree(valuesIter);
+    psFree(values);
+
+    return num;
+}
+
+// Get the name of a PHU chip or cell from the header
+static psString phuNameFromHeader(const char *name, // The name to lookup: "CELL.NAME" or "CHIP.NAME"
+                                  const psMetadata *fileInfo, // FILE within the camera format description
+                                  const psMetadata *header // Primary header
+                                 )
+{
+    assert(name && strlen(name) > 0);
+    assert(fileInfo);
+    assert(header);
+
+    bool mdok = true;                   // Result of MD lookup
+    psString keyword = psMetadataLookupStr(&mdok, fileInfo, name);
+    if (!mdok || strlen(keyword) == 0) {
+        return false;
+    }
+    psMetadataItem *resultItem = psMetadataLookup(header, keyword);
+    if (!resultItem) {
+        psError(PS_ERR_IO, true, "Unable to find %s in primary header to identify %s.\n", keyword, name);
+        return NULL;
+    }
+    return psMetadataItemParseString(resultItem);
+}
+
+// Add an HDU to the FPA
+static bool addHDUtoFPA(pmFPA *fpa,     // FPA to which to add
+                        pmHDU *hdu      // HDU to be added
+                       )
+{
+    assert(fpa);
+    assert(hdu);
+
+    if (fpa->hdu) {
+        // Something's already here
+        if (fpa->hdu != hdu) {
+            psError(PS_ERR_IO, true, "Unable to add HDU since FPA already has one.\n");
+        }
+        return false;
+    }
+    fpa->hdu = psMemIncrRefCounter(hdu);
+    pmFPASetFileStatus(fpa, true);
+
+    return true;
+}
+
+// Add an HDU to the chip
+static bool addHDUtoChip(pmChip *chip,  // Chip to which to add
+                         pmHDU *hdu     // HDU to be added
+                        )
+{
+    assert(chip);
+    assert(hdu);
+
+    if (chip->hdu) {
+        // Something's already here
+        if (chip->hdu != hdu) {
+            psError(PS_ERR_IO, true, "Unable to add HDU since chip already has one.\n");
+        }
+        return false;
+    }
+    chip->hdu = psMemIncrRefCounter(hdu);
+    pmChipSetFileStatus(chip, true);
+
+    return true;
+}
+
+// Add an HDU to the cell
+static bool addHDUtoCell(pmCell *cell,  // Cell to which to add
+                         pmHDU *hdu     // HDU to be added
+                        )
+{
+    assert(cell);
+    assert(hdu);
+
+    if (cell->hdu) {
+        // Something's already here
+        if (cell->hdu != hdu) {
+            psError(PS_ERR_IO, true, "Unable to add HDU since cell already has one.\n");
+        }
+        return false;
+    }
+    cell->hdu = psMemIncrRefCounter(hdu);
+    pmCellSetFileStatus(cell, true);
+
+    return true;
+}
+
+
+// Looks up the particular content, based on the chip and cell
+static const char *getContent(const psMetadata *fileInfo, // The FILE from the camera format configuration
+                              const psMetadata *header, // The FITS header
+                              const psMetadata *contents // The CONTENTS from the camera format configuration
+                             )
+{
+    assert(fileInfo);
+    assert(contents);
+    assert(header);
+
+    const char *contentHeader = psMetadataLookupStr(NULL, fileInfo, "CONTENT"); // Keyword to get contents
+    if (!contentHeader || strlen(contentHeader) == 0) {
+        psError(PS_ERR_UNEXPECTED_NULL, false,
+                "Unable to find CONTENT in FILE within camera format configuration.\n");
+        return NULL;
+    }
+
+    psMetadataItem *contentKey = psMetadataLookup(header, contentHeader); // Key to CONTENTS menu
+    if (!contentKey) {
+        psError(PS_ERR_UNEXPECTED_NULL, false,
+                "Unable to find %s in header to determine file content.", contentHeader);
+        return NULL;
+    }
+
+    psString contentKeyStr = psMetadataItemParseString(contentKey); // Key, as a string
+
+    psTrace("psModules.camera", 5, "Looking up %s in the CONTENTS.\n", contentKeyStr);
+    const char *content = psMetadataLookupStr(NULL, contents, contentKeyStr);
+    if (!content || strlen(content) == 0) {
+        psError(PS_ERR_IO, false, "Unable to find %s in the CONTENTS.\n", contentKeyStr);
+        return NULL;
+    }
+
+    psFree(contentKeyStr);
+
+    return content;
+}
+
+
+// Given a list of contents, put the HDU in the correct place and plug in the cell configuration information
+static bool processContents(pmFPA *fpa,  // The FPA
+                            pmChip *chip, // The chip
+                            pmHDU *hdu,  // The HDU to be added
+                            pmFPALevel level, // The level at which to add the HDU
+                            psArray *chipNames, // The chip names
+                            psArray *cellNames, // The cell names
+                            psArray *cellTypes, // The cell types
+                            const psMetadata *format // Camera format configuration
+                            )
+{
+    assert(fpa);
+    assert(cellTypes);
+    long num = cellTypes->n;            // Number of entries to add
+    assert(chip || (chipNames && chipNames->n == num));
+    assert(cellNames && cellNames->n == num);
+    assert(format);
+
+    if (hdu && level == PM_FPA_LEVEL_FPA) {
+        if (!addHDUtoFPA(fpa, hdu)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to add HDU to FPA");
+            return false;
+        }
+    }
+    // Load fpa-related concepts
+    if (!pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_DEFAULTS, false, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read concepts from camera and defaults for fpa\n");
+        return false;
+    }
+
+    for (int i = 0; i < num; i++) {
+        psString cellType = cellTypes->data[i]; // The type of the cell
+
+        // Find the chip
+        pmChip *newChip;                // Chip of interest
+        if (chip) {
+            newChip = chip;
+        } else {
+            psString chipName = chipNames->data[i]; // The name of the chip
+            int chipNum = pmFPAFindChip(fpa, chipName); // The chip we're looking for
+            if (chipNum == -1) {
+                psError(PS_ERR_LOCATION_INVALID, false,
+                        "Unable to find chip %s in fpa --- ignored.\n", chipName);
+                return false;
+            }
+            newChip = fpa->chips->data[chipNum];
+        }
+
+        // Put in the HDU
+        if (hdu && level == PM_FPA_LEVEL_CHIP) {
+            addHDUtoChip(newChip, hdu);
+        }
+        // Load chip-related concepts
+        if (!pmConceptsReadChip(newChip, PM_CONCEPT_SOURCE_DEFAULTS, false, false, NULL)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read concepts from camera and defaults for chip\n");
+            return false;
+        }
+
+        // Find the cell
+        pmCell *newCell;                // Cell of interest
+        psString cellName = cellNames->data[i]; // The name of the cell
+        int cellNum = pmChipFindCell(newChip, cellName); // The cell we're looking for
+        if (cellNum == -1) {
+            psError(PS_ERR_LOCATION_INVALID, false, "Unable to find cell %s in chip --- ignored.\n",
+                    cellName);
+            return false;
+        }
+        newCell = newChip->cells->data[cellNum];
+
+        psMetadata *cellData = getCellData(format, cellType); // Data for this cell
+
+        if (hdu && level == PM_FPA_LEVEL_CELL) {
+            addHDUtoCell(newCell, hdu);
+        }
+
+        // Put in the cell data
+        if (newCell->config) {
+            psWarning("Overwriting cell data in chip\n");
+            psFree(newCell->config); // Make way!
+        }
+        newCell->config = psMemIncrRefCounter(cellData);
+        if (!pmConceptsReadCell(newCell, PM_CONCEPT_SOURCE_CELLS | PM_CONCEPT_SOURCE_DEFAULTS,
+                                false, NULL)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Unable to read concepts from camera and defaults for cell type %s", cellType);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+#if 0
+// Return the level at which EXTENSIONS go, from the FILE metadata within the camera format
+static pmFPALevel hduLevel(const psMetadata *format // The camera format configuration
+                          )
+{
+    assert(format);
+
+    bool mdok = true;                   // Status of MD lookup
+    psMetadata *file = psMetadataLookupMetadata(&mdok, format, "FILE"); // File information
+    if (!mdok || !file) {
+        psError(PS_ERR_IO, true, "Unable to find FILE information in camera format configuration.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+    const char *extType = psMetadataLookupStr(&mdok, file, "EXTENSIONS");
+    if (!mdok || !extType || strlen(extType) == 0) {
+        psError(PS_ERR_IO, true, "Unable to find EXTENSIONS in the FILE information in the camera format"
+                " configuration.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+
+    // Where do we stick in the HDUs?
+    pmFPALevel level = PM_FPA_LEVEL_NONE; // Level for HDU insertion
+    if (strcasecmp(extType, "CHIP") == 0) {
+        level = PM_FPA_LEVEL_CHIP;
+    } else if (strcasecmp(extType, "CELL") == 0) {
+        level = PM_FPA_LEVEL_CELL;
+    } else if (strcasecmp(extType, "NONE") != 0) {
+        psError(PS_ERR_IO, true, "EXTENSIONS is not CHIP or CELL or NONE.\n");
+    }
+
+    return level;
+}
+#endif
+
+// Find the chip of interest within the FPA
+static bool whichChip(int *chipNum, // Chip number, modified
+                      psString *chipType, // Type of chip, modified
+                      const pmFPA *fpa, // FPA holding chip of interest
+                      const char *content // Content consisting of chipName:chipType
+                      )
+{
+    assert(chipType);
+    assert(fpa);
+    assert(content);
+
+    psArray *chipNames = NULL;
+    psArray *chipTypes = NULL;
+    if (parseContent(&chipNames, &chipTypes, NULL, content) != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Unable to parse chipName:chipType in %s in camera format",
+                TABLE_OF_CONTENTS);
+        return false;
+    }
+
+    psString chipName = psMemIncrRefCounter(chipNames->data[0]); // Name of chip
+    *chipType = psMemIncrRefCounter(chipTypes->data[0]); // Type of chip
+    psFree(chipNames);
+    psFree(chipTypes);
+
+    psTrace("psModules.camera", 5, "This is chip %s\n", chipName);
+
+    // Get the chip
+    *chipNum = pmFPAFindChip(fpa, chipName); // Chip number
+    if (*chipNum == -1) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find chip %s in FPA.\n", chipName);
+        psFree(chipName);
+        return false;
+    }
+    psFree(chipName);
+
+    return true;
+}
+
+
+// Process a chip, using the cellName:cellType pair
+static bool processChip(const psMetadata *format, // Camera format
+                        const psMetadataItem *chipContents, // Contents of chip, cellName:cellType pairs (either in a string or a metadata)
+                        pmFPA *fpa, // FPA of interest
+                        pmChip *chip, // Chip of interest
+                        pmFPALevel level, // Level for HDU to go
+                        pmHDU *hdu      // HDU to add
+    )
+{
+    assert(format);
+    assert(chipContents);
+    assert(fpa);
+
+    psMetadata *chips = psMetadataLookupMetadata(NULL, format, CHIP_TYPES); // The chip types
+    if (!chips) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s in camera format.", CHIP_TYPES);
+        return false;
+    }
+
+    psArray *cellNames = NULL;      // Cell names
+    psArray *cellTypes = NULL;      // Cell types
+
+    int nParsed = 0;
+
+    switch (chipContents->type) {
+      case PS_DATA_STRING: {
+          nParsed = parseContent(&cellNames, &cellTypes, NULL, chipContents->data.str);
+          break;
+      }
+      case PS_DATA_METADATA: {
+          psMetadataIterator *iter = psMetadataIteratorAlloc(chipContents->data.md, PS_LIST_HEAD, NULL); // Iterator
+          psMetadataItem *item;           // Item from iteration
+          while ((item = psMetadataGetAndIncrement(iter))) {
+              if (item->type != PS_DATA_STRING) {
+                  psWarning ("Item %s in camera format chip table is not of type STR.", item->name);
+                  continue;
+              }
+              nParsed += parseContent(&cellNames, &cellTypes, NULL, item->data.str);
+          }
+          psFree (iter);
+          break;
+      }
+      default:
+        psWarning ("Item %s in camera format chip table is not of type STR.", chipContents->name);
+        break;
+    }
+
+    if (nParsed == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to parse chip contents (within %s in camera format) as cellName:cellType",
+                CHIP_TYPES);
+        psFree(cellNames);
+        psFree(cellTypes);
+        return false;
+    }
+
+    if (!processContents(fpa, chip, hdu, level, NULL, cellNames, cellTypes, format)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to set contents for chip from camera format.");
+        psFree(cellNames);
+        psFree(cellTypes);
+        return false;
+    }
+
+    psFree(cellNames);
+    psFree(cellTypes);
+
+    return true;
+}
+
+// Given a chip, find the corresponding type by searching through the contents, looking for a match to its
+// name
+psString findChipType(const pmChip *chip, // Chip of interest
+                      psMetadata *contents // Contents, from camera format
+                      )
+{
+    assert(chip);
+    assert(contents);
+
+    const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); // Name of chip
+    assert(chipName);
+
+    psString chipType = NULL;           // Type of chip
+    psMetadataIterator *iter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;           // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (item->type != PS_DATA_STRING) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Item %s within %s in camera format is not of type STR.", item->name, TABLE_OF_CONTENTS);
+            psFree(iter);
+            psFree(chipType);
+            return NULL;
+        }
+
+        psArray *chipNames = NULL;  // Chip names
+        psArray *chipTypes = NULL;  // Chip types
+        if (parseContent(&chipNames, &chipTypes, NULL, item->data.str) != 1) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "Unable to parse contents (within %s in camera format) as chipName:chipType",
+                    TABLE_OF_CONTENTS);
+            psFree(chipNames);
+            psFree(chipTypes);
+            psFree(iter);
+            psFree(chipType);
+            return NULL;
+        }
+
+        if (strcmp(chipName, chipNames->data[0]) == 0) {
+            if (chipType) {
+                if (strcmp(chipType, chipTypes->data[0]) != 0) {
+                    psError(PS_ERR_UNKNOWN, true,
+                            "Multiple instances of chip %s in contents, with differing chipType "
+                            "(%s vs %s)", chipName, chipType, (char*)chipTypes->data[0]);
+                    psFree(chipNames);
+                    psFree(chipTypes);
+                    psFree(iter);
+                    psFree(chipType);
+                    return NULL;
+                }
+            } else {
+                chipType = psMemIncrRefCounter(chipTypes->data[0]);
+            }
+        }
+        psFree(chipNames);
+        psFree(chipTypes);
+    }
+    psFree(iter);
+
+    if (!chipType) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to identify chip type for chip %s", chipName);
+        return NULL;
+    }
+
+    return chipType;
+}
+
+// PHU=FPA and EXTENSIONS=CHIP:
+// TABLE_OF_CONTENTS(METADATA) has a list of extensions, each with a chipName:chipType.
+// CHIP_TYPES(METADATA) has a list of chip types, each with cellName:cellType
+static bool addSource_FPA_CHIP(pmFPA *fpa, // FPA to which to add
+                               const psMetadata *format // The camera format
+                               )
+{
+    assert(fpa);
+    assert(format);
+
+    psMetadata *contents = psMetadataLookupMetadata(NULL, format, TABLE_OF_CONTENTS); // The contents
+    if (!contents) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s in camera format.", TABLE_OF_CONTENTS);
+        return false;
+    }
+
+    psMetadata *chips = psMetadataLookupMetadata(NULL, format, CHIP_TYPES); // The chip types
+    if (!chips) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s in camera format.", CHIP_TYPES);
+        return false;
+    }
+
+    // Iterate over all extensions
+    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(contentsIter))) {
+        if (item->type != PS_DATA_STRING) {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    "Type for %s (%x) in %s METADATA in camera format is not STR",
+                    item->name, item->type, TABLE_OF_CONTENTS);
+            psFree(contentsIter);
+            return false;
+        }
+
+        const char *extname = item->name; // Extension name
+        pmHDU *hdu = pmHDUAlloc(extname); // HDU for this extension
+        // Casting to avoid "warning: passing arg 1 of `p_psMemIncrRefCounter' discards qualifiers from
+        // pointer target type"
+        hdu->format = psMemIncrRefCounter((const psPtr)format);
+
+        // What's in the extension?  It's specified by chipName:chipType
+        // Assume that an extension contains only a single chip, instead of multiple chips
+        psString chipType = NULL;       // Type of chip
+        int chipNum = -1;               // Chip number
+        if (!whichChip(&chipNum, &chipType, fpa, item->data.str)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine chip from contents");
+            return false;
+        }
+        pmChip *chip = fpa->chips->data[chipNum]; // Chip of interest
+
+        const psMetadataItem *chipContents = psMetadataLookup(chips, chipType); // Contents of chip
+        psFree(chipType);
+        if (!chipContents) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find chip type %s in %s.",
+                    chipType, CHIP_TYPES);
+            psFree(hdu);
+            psFree(contentsIter);
+            return false;
+        }
+
+        if (!processChip(format, chipContents, fpa, chip, PM_FPA_LEVEL_CHIP, hdu)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to process chip %d\n", chipNum);
+            psFree(hdu);
+            psFree(contentsIter);
+            return false;
+        }
+
+        psFree(hdu);                    // Drop reference
+    }
+    psFree(contentsIter);
+
+    return true;
+}
+
+// PHU=FPA and EXTENSIONS=CELL:
+// TABLE_OF_CONTENTS(METADATA) has a list of extensions, each with a chipName:cellName:cellType.
+static bool addSource_FPA_CELL(pmFPA *fpa, // FPA to which to add
+                               const psMetadata *format // The camera format
+                               )
+{
+    assert(fpa);
+    assert(format);
+
+    psMetadata *contents = psMetadataLookupMetadata(NULL, format, TABLE_OF_CONTENTS); // The contents
+    if (!contents) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s in camera format.", TABLE_OF_CONTENTS);
+        return false;
+    }
+
+    // Iterate over all extensions
+    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(contentsIter))) {
+        if (item->type != PS_DATA_STRING) {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    "Type for %s (%x) in %s METADATA in camera format is not STR",
+                    item->name, item->type, TABLE_OF_CONTENTS);
+            psFree(contentsIter);
+            return false;
+        }
+
+        const char *extname = item->name; // Extension name
+        pmHDU *hdu = pmHDUAlloc(extname); // HDU for this extension
+        // Casting to avoid "warning: passing arg 1 of `p_psMemIncrRefCounter' discards qualifiers from
+        // pointer target type"
+        hdu->format = psMemIncrRefCounter((const psPtr)format);
+
+        // What's in the extension?  It's specified by (possibly multiple) chipName:cellName:cellType
+
+        psArray *chipNames = NULL;      // Chip names
+        psArray *cellNames = NULL;      // Cell names
+        psArray *cellTypes = NULL;      // Cell types
+        if (parseContent(&chipNames, &cellNames, &cellTypes, item->data.str) == 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "Unable to parse extension contents (within %s->%s in camera format) as "
+                    "chipName:cellName:cellType", TABLE_OF_CONTENTS, extname);
+            psFree(chipNames);
+            psFree(cellNames);
+            psFree(cellTypes);
+            psFree(hdu);
+            psFree(contentsIter);
+            return false;
+        }
+
+        if (!processContents(fpa, NULL, hdu, PM_FPA_LEVEL_CELL, chipNames, cellNames, cellTypes,
+                             format)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to set contents from camera format.");
+            psFree(chipNames);
+            psFree(cellNames);
+            psFree(cellTypes);
+            psFree(hdu);
+            psFree(contentsIter);
+            return false;
+        }
+
+        psFree(chipNames);
+        psFree(cellNames);
+        psFree(cellTypes);
+
+        psFree(hdu);                    // Drop reference
+    }
+    psFree(contentsIter);
+
+    return true;
+}
+
+// PHU=FPA and EXTENSIONS=NONE:
+// TABLE_OF_CONTENTS(STR) has a list of chipName:cellName:cellType.
+static bool addSource_FPA_NONE(pmFPA *fpa, // FPA to which to add
+                               const psMetadata *format // The camera format
+                               )
+{
+    assert(fpa);
+    assert(format);
+
+    psString contents = psMetadataLookupStr(NULL, format, TABLE_OF_CONTENTS); // The contents
+    if (!contents) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s in camera format.", TABLE_OF_CONTENTS);
+        return false;
+    }
+
+    // What's in the file?  It's specified by (possibly multiple) chipName:cellName:cellType
+
+    psArray *chipNames = NULL;          // Chip names
+    psArray *cellNames = NULL;          // Cell names
+    psArray *cellTypes = NULL;          // Cell types
+    if (parseContent(&chipNames, &cellNames, &cellTypes, contents) == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to parse contents (within %s in camera format) as chipName:cellName:cellType",
+                TABLE_OF_CONTENTS);
+        psFree(chipNames);
+        psFree(cellNames);
+        psFree(cellTypes);
+        return false;
+    }
+
+    if (!processContents(fpa, NULL, NULL, PM_FPA_LEVEL_NONE, chipNames, cellNames, cellTypes,
+                         format)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to set contents from camera format.");
+        psFree(chipNames);
+        psFree(cellNames);
+        psFree(cellTypes);
+        return false;
+    }
+
+    psFree(chipNames);
+    psFree(cellNames);
+    psFree(cellTypes);
+
+    return true;
+}
+
+
+// PHU=CHIP and EXTENSIONS=CELL:
+// TABLE_OF_CONTENTS(METADATA) has a menu of contents, each with a chipName:chipType.
+// CHIP_TYPES(METADATA) has a list of chip types(METADATA), each with extension(STR) with cellName:cellType
+static bool addSource_CHIP_CELL(pmFPAview *view, // View for PHU, modified
+                                pmFPA *fpa, // FPA to which to add
+                                pmChip *chip, // Known chip to which to add, or NULL
+                                const psMetadata *format, // The camera format
+                                pmHDU *phdu, // The Primary HDU
+                                bool install // Install the HDUs?
+                                )
+{
+    assert(view);
+    assert(fpa);
+    assert(format);
+    assert(phdu);
+
+    psMetadata *contents = psMetadataLookupMetadata(NULL, format, TABLE_OF_CONTENTS); // The contents
+    if (!contents) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s in camera format.", TABLE_OF_CONTENTS);
+        return false;
+    }
+
+    psMetadata *chips = psMetadataLookupMetadata(NULL, format, CHIP_TYPES); // The chip types
+    if (!chips) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s in camera format.", CHIP_TYPES);
+        return false;
+    }
+
+    psMetadata *fileInfo = psMetadataLookupMetadata(NULL, format, "FILE"); // The file information
+    if (!fileInfo) {
+        psError(PS_ERR_IO, false, "Unable to find FILE in the camera format configuration.\n");
+        return false;
+    }
+
+
+    psString chipType = NULL;           // Type of chip
+    if (chip) {
+        // We're given the chip (adding source from view)
+        // Need to identify the chip type, which we will do by traversing the contents
+        chipType = findChipType(chip, contents);
+    } else {
+        // We're given a header, from which to identify what chip we've got, and its type
+        const char *content = getContent(fileInfo, phdu->header, contents); // The contents of this chip
+        if (!content) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine content of file.");
+            return false;
+        }
+
+        int chipNum = -1;               // Chip number
+        if (!whichChip(&chipNum, &chipType, fpa, content)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine chip from contents");
+            return false;
+        }
+        chip = fpa->chips->data[chipNum]; // Chip of interest
+        view->chip = chipNum;
+    }
+
+    if (!install) {
+        // Everything below is about installing the HDUs
+        psFree(chipType);
+        return true;
+    }
+
+    if (!addHDUtoChip(chip, phdu)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to add HDU to chip\n");
+        psFree(chipType);
+        return false;
+    }
+
+    psMetadata *chipContents = psMetadataLookupMetadata(NULL, chips, chipType); // Contents of chip
+    psFree(chipType);
+    if (!chipContents) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find chip type %s in %s.",
+                chipType, CHIP_TYPES);
+        return false;
+    }
+
+    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(chipContents, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *contentItem;        // Content, from iteration
+    while ((contentItem = psMetadataGetAndIncrement(contentsIter))) {
+        pmHDU *hdu = pmHDUAlloc(contentItem->name); // HDU for this extension
+        // Casting to avoid "warning: passing arg 1 of `p_psMemIncrRefCounter' discards qualifiers from
+        // pointer target type"
+        hdu->format = psMemIncrRefCounter((const psPtr)format);
+
+        if (!processChip(format, contentItem, fpa, chip, PM_FPA_LEVEL_CELL, hdu)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to process chip\n");
+            psFree(hdu);
+            psFree(contentsIter);
+            return false;
+        }
+
+        psFree(hdu);                    // Drop reference
+    }
+    psFree(contentsIter);
+
+    if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_PHU, true, true, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read concepts for chip.");
+        return false;
+    }
+
+    return true;
+}
+
+// PHU=CHIP and EXTENSIONS=NONE:
+// TABLE_OF_CONTENTS(METADATA) has a menu of contents, each with a chipName:chipType.
+// CHIP_TYPES(METADATA) has a list of chip types, each with cellName:cellType
+static bool addSource_CHIP_NONE(pmFPAview *view, // View for PHU, modified
+                                pmFPA *fpa, // FPA to which to add
+                                pmChip *chip, // Known chip to which to add, or NULL
+                                const psMetadata *format, // The camera format
+                                pmHDU *phdu, // Primary HDU
+                                bool install // Install the HDUs?
+                                )
+{
+    assert(fpa);
+    assert(format);
+    assert(phdu);
+
+    psMetadata *contents = psMetadataLookupMetadata(NULL, format, TABLE_OF_CONTENTS); // The contents
+    if (!contents) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s in camera format.", TABLE_OF_CONTENTS);
+        return false;
+    }
+
+    psMetadata *chips = psMetadataLookupMetadata(NULL, format, CHIP_TYPES); // The chip types
+    if (!chips) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s in camera format.", CHIP_TYPES);
+        return false;
+    }
+
+    psMetadata *fileInfo = psMetadataLookupMetadata(NULL, format, "FILE"); // The file information
+    if (!fileInfo) {
+        psError(PS_ERR_IO, false, "Unable to find FILE in the camera format configuration.\n");
+        return false;
+    }
+
+    psString chipType = NULL;           // Type of chip
+    if (chip) {
+        // We're given the chip (adding source from view)
+        // Need to identify the chip type, which we will do by traversing the contents
+        chipType = findChipType(chip, contents);
+    } else {
+        const char *content = getContent(fileInfo, phdu->header, contents); // The chip type
+
+        int chipNum = -1;               // Chip number
+        if (!whichChip(&chipNum, &chipType, fpa, content)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine chip from contents");
+            return false;
+        }
+        chip = fpa->chips->data[chipNum]; // Chip of interest
+        view->chip = chipNum;
+    }
+
+    if (!install) {
+        // Everything below is about installing the HDU
+        psFree(chipType);
+        return true;
+    }
+
+    if (!addHDUtoChip(chip, phdu)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to add HDU to chip\n");
+        psFree(chipType);
+        return false;
+    }
+
+    // What's in the chip?
+    psMetadataItem *chipContents = psMetadataLookup(chips, chipType); // Contents of the chip
+    if (!chipContents) {
+        psError(PS_ERR_UNEXPECTED_NULL, false,
+                "Unable to find chip type %s in %s of camera format", chipType, CHIP_TYPES);
+        return false;
+    }
+    psFree(chipType);
+
+    if (!processChip(format, chipContents, fpa, chip, PM_FPA_LEVEL_NONE, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to process chip\n");
+        return false;
+    }
+
+    if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_PHU, true, true, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read concepts for chip.");
+        return false;
+    }
+
+    return true;
+}
+
+// PHU=CELL and EXTENSIONS=NONE:
+// TABLE_OF_CONTENTS(METADATA) has a menu of contents, each with a chipName:cellName:cellType
+static bool addSource_CELL_NONE(pmFPAview *view, // View for PHU, modified
+                                pmFPA *fpa, // FPA to which to add
+                                pmCell *cell, // Known cell to which to add, or NULL
+                                const psMetadata *format, // The camera format
+                                pmHDU *phdu, // The Primary HDU
+                                bool install // Install the HDUs?
+                                )
+{
+    assert(fpa);
+    assert(format);
+    assert(phdu);
+
+    psMetadata *contents = psMetadataLookupMetadata(NULL, format, TABLE_OF_CONTENTS); // The contents
+    if (!contents) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s in camera format.", TABLE_OF_CONTENTS);
+        return false;
+    }
+
+    psMetadata *fileInfo = psMetadataLookupMetadata(NULL, format, "FILE"); // The file information
+    if (!fileInfo) {
+        psError(PS_ERR_IO, false, "Unable to find FILE in the camera format configuration.\n");
+        return false;
+    }
+
+    psArray *chipNames = NULL;          // Chip names
+    psArray *cellNames = NULL;          // Cell names
+    psArray *cellTypes = NULL;          // Cell types
+    pmChip *chip = NULL;                // Chip of interest
+    if (cell) {
+        // We're given the chip and cell (adding source from view)
+        // Need to identify the cell type, which we will do by traversing the contents
+
+        chip = cell->parent;            // The chip of interest
+        psString cellType = NULL;       // Type of cell
+
+        // The below is very similar to findChipType(), but with modifications for finding the cellType
+        const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); // Name of chip
+        assert(chipName);
+        const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
+        assert(cellName);
+
+        psMetadataIterator *iter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *item;           // Item from iteration
+        while ((item = psMetadataGetAndIncrement(iter))) {
+            if (item->type != PS_DATA_STRING) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                        "Item %s within %s in camera format is not of type STR.",
+                        item->name, TABLE_OF_CONTENTS);
+                psFree(chipNames);
+                psFree(cellNames);
+                psFree(cellTypes);
+                psFree(iter);
+                psFree(cellType);
+                return false;
+            }
+
+            psArray *testChipNames = NULL; // Chip names
+            psArray *testCellNames = NULL; // Cell names
+            psArray *testCellTypes = NULL; // Cell types
+            if (parseContent(&testChipNames, &testCellTypes, &testCellTypes, item->data.str) != 1) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                        "Unable to parse contents (within %s in camera format) as chipName:cellName:cellType",
+                        TABLE_OF_CONTENTS);
+                psFree(chipNames);
+                psFree(cellNames);
+                psFree(cellTypes);
+                psFree(testChipNames);
+                psFree(testCellNames);
+                psFree(testCellTypes);
+                psFree(iter);
+                psFree(cellType);
+                return false;
+            }
+
+            if (strcmp(chipName, chipNames->data[0]) == 0 && strcmp(cellName, cellNames->data[0]) == 0) {
+                if (cellType) {
+                    if (strcmp(cellType, cellTypes->data[0]) != 0) {
+                        psError(PS_ERR_UNKNOWN, true,
+                                "Multiple instances of chip %s cell %s in contents, with differing cellType "
+                                "(%s vs %s)", chipName, cellName, cellType, (char*)cellTypes->data[0]);
+                        psFree(chipNames);
+                        psFree(cellNames);
+                        psFree(cellTypes);
+                        psFree(testChipNames);
+                        psFree(testCellNames);
+                        psFree(testCellTypes);
+                        psFree(iter);
+                        psFree(cellType);
+                        return false;
+                    }
+                } else {
+                    cellType = psMemIncrRefCounter(cellTypes->data[0]);
+                    chipNames = psMemIncrRefCounter(testChipNames);
+                    cellNames = psMemIncrRefCounter(testCellNames);
+                    cellTypes = psMemIncrRefCounter(testCellTypes);
+                }
+            }
+            psFree(testChipNames);
+            psFree(testCellNames);
+            psFree(testCellTypes);
+        }
+        psFree(iter);
+
+        if (!cellType) {
+            psError(PS_ERR_UNKNOWN, true, "Unable to identify cell type for chip %s cell %s",
+                    chipName, cellName);
+            psFree(chipNames);
+            psFree(cellNames);
+            psFree(cellTypes);
+            return false;
+        }
+
+        // We don't really care about the cell type here --- it's taken care of by processContents
+        psFree(cellType);
+
+    } else {
+        const char *content = getContent(fileInfo, phdu->header, contents); // Content of cell
+
+        if (parseContent(&chipNames, &cellNames, &cellTypes, content) != 1) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                    "Unable to parse cell contents (%s) as cellName:cellType", content);
+            psFree(chipNames);
+            psFree(cellNames);
+            psFree(cellTypes);
+            return false;
+        }
+
+        int chipNum = pmFPAFindChip(fpa, chipNames->data[0]); // Chip number
+        if (chipNum == -1) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to find chip %s referred to in contents",
+                    (char*)chipNames->data[0]);
+            psFree(chipNames);
+            psFree(cellNames);
+            psFree(cellTypes);
+            return false;
+        }
+        chip = fpa->chips->data[chipNum];
+
+        int cellNum = pmChipFindCell(chip, cellNames->data[0]); // Cell number
+        if (cellNum == -1) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to find cell %s referred to in contents",
+                    (char*)cellNames->data[0]);
+            psFree(chipNames);
+            psFree(cellNames);
+            psFree(cellTypes);
+            return false;
+        }
+        cell = chip->cells->data[cellNum];
+
+        view->chip = chipNum;
+        view->cell = cellNum;
+
+        psFree(chipNames);
+        psFree(cellNames);
+        psFree(cellTypes);
+    }
+
+    if (!install) {
+        // Everything below is about installing the HDU
+        psFree(chipNames);
+        psFree(cellNames);
+        psFree(cellTypes);
+        return true;
+    }
+
+    if (!processContents(fpa, NULL, phdu, PM_FPA_LEVEL_NONE, chipNames, cellNames, cellTypes, format)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to set contents for cell from camera format.");
+        psFree(chipNames);
+        psFree(cellNames);
+        psFree(cellTypes);
+        return false;
+    }
+
+    psFree(chipNames);
+    psFree(cellNames);
+    psFree(cellTypes);
+
+    if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_PHU, true, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read concepts for cell.");
+        return false;
+    }
+
+    return true;
+}
+
+
+// This is the engine for the pmFPAAddSourceFrom{Header,View} functions.
+// It uses the camera format configuration information to determine where HDUs go in the FPA.
+// It returns a view corresponding to the PHU
+static pmFPAview *addSource(pmFPA *fpa,       // The FPA
+                            const char *fpaObs, // The desired FPA observation name
+                            const pmFPAview *phuView, // The view corresponding to the PHU, or NULL
+                            const psMetadata *header, // The PHU header, or NULL
+                            const psMetadata *format, // Format of file
+                            bool install // Install the provided header in the location that we find?
+                           )
+{
+    assert(fpa);
+    assert(phuView || header);
+    assert(format);
+
+    bool mdok;                          // Status of MD lookup
+
+    // If FPA.OBS is already defined, new name must match it; otherwise, warn the user that something
+    // potentially bad is happening.
+    if (fpaObs && install) {
+        const char *currentName = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.OBS"); // Current name
+        if (mdok && currentName && strlen(currentName) > 0 && strcmp(currentName, fpaObs) != 0) {
+            psWarning("FPA.OBS for new source (%s) doesn't match FPA.OBS for current fpa (%s).",
+                      fpaObs, currentName);
+        }
+        psMetadataAddStr(fpa->concepts, PS_LIST_HEAD, "FPA.OBS", PS_META_REPLACE, "Observation identifier",
+                         fpaObs);
+    } else if (!psMetadataLookup(fpa->concepts, "FPA.OBS")) {
+        // Make sure there is an FPA.OBS
+        psMetadataAddStr(fpa->concepts, PS_LIST_HEAD, "FPA.OBS", 0, "Observation identifier", "UNKNOWN");
+    }
+
+    psMetadata *fileInfo = psMetadataLookupMetadata(&mdok, format, "FILE"); // The file information
+    if (!mdok || !fileInfo) {
+        psError(PS_ERR_IO, false, "Unable to find FILE in the camera format configuration.\n");
+        return NULL;
+    }
+
+    // At what level does the PHU go?
+    const char *phuType = psMetadataLookupStr(&mdok, fileInfo, "PHU"); // What is the PHU?
+    if (!mdok || strlen(phuType) == 0) {
+        psError(PS_ERR_IO, false, "Unable to find PHU in the format specification.\n");
+        return NULL;
+    }
+
+    // Prepare the PHU to be placed in the camera hierarchy
+    pmHDU *phdu = pmHDUAlloc(NULL);     // The primary header data unit
+    // Casting to psPtr to avoide "warning: passing arg 1 of `p_psMemIncrRefCounter' discards qualifiers from
+    // pointer target type"
+    phdu->header = psMemIncrRefCounter((const psPtr)header);
+    phdu->format = psMemIncrRefCounter((const psPtr)format);
+    pmFPAview *view = pmFPAviewAlloc(0); // View, to be returned
+    if (phuView) {
+        // Copy the view, for the case where we're given a view.
+        *view = *phuView;
+    }
+
+    // And at what level do the individual extensions go?
+    const char *extType = psMetadataLookupStr(&mdok, fileInfo, "EXTENSIONS"); // What's in the extns?
+    if (!mdok || strlen(extType) == 0) {
+        psError(PS_ERR_IO, false, "Unable to find EXTENSIONS in the format specification.\n");
+        psFree(view);
+        return NULL;
+    }
+
+    // Now, there are a few cases:
+
+    // Case    PHU     EXTENSIONS     Description
+    // ====    ===     ==========     ===========
+    // 1.      FPA     CHIP           CONTENTS(METADATA) has a list of extensions, each with chipName:chipType
+    //                                CHIPS(METADATA) has a list of chip types, each with cell:type
+    // 2.      FPA     CELL           CONTENTS(METADATA) has a list of extensions, each with chip:cell:type
+    //                                No need for CHIPS.
+    // 3.      FPA     NONE           CONTENTS(STRING) has a list of extensions, chip:cell:type
+    //                                No need for CHIPS
+    // 4.      CHIP    CELL           CONTENTS(METADATA) is a menu, each with a chipName:chipType
+    //                                CHIPS(METADATA) has a list of chip types(METADATA), containg a list of
+    //                                extensions.
+    // 5.      CHIP    NONE           CONTENTS(METADATA) is a menu, each with a chipName:chipType
+    //                                CHIPS(METADATA) has a list of chip types(STRING) with cell:type
+    // 6.      CELL    NONE           CONTENTS(METADATA) is a menu, each with a chipName:cellName:cellType.
+    //                                No need for CHIPS.
+
+
+    pmFPALevel phuLevel = pmFPALevelFromName(phuType); // Level for PHU
+    pmFPALevel extLevel = pmFPALevelFromName(extType); // Level for extensions
+
+    switch (phuLevel) {
+      case PM_FPA_LEVEL_FPA: {
+          // We don't have to work out where the PHU is --- there's only one FPA.
+          // 'view' already points to the FPA.
+          switch (extLevel) {
+            case PM_FPA_LEVEL_CHIP:
+              phdu->blankPHU = true;
+              if (install) {
+                  if (!addHDUtoFPA(fpa, phdu) || !addSource_FPA_CHIP(fpa, format) ||
+                      !pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_PHU,
+                                         true, NULL)) {
+                      psError(PS_ERR_UNKNOWN, false, "Unable to add source.");
+                      psFree(phdu);
+                      psFree(view);
+                      return NULL;
+                  }
+              }
+              psFree(phdu);
+              return view;
+            case PM_FPA_LEVEL_CELL:
+              if (install) {
+                  phdu->blankPHU = true;
+                  if (!addHDUtoFPA(fpa, phdu) || !addSource_FPA_CELL(fpa, format) ||
+                      !pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_PHU,
+                                         true, NULL)) {
+                      psError(PS_ERR_UNKNOWN, false, "Unable to add source.");
+                      psFree(phdu);
+                      psFree(view);
+                      return NULL;
+                  }
+              }
+              psFree(phdu);
+              return view;
+            case PM_FPA_LEVEL_NONE:
+              if (install) {
+                  phdu->blankPHU = false;
+                  if (!addHDUtoFPA(fpa, phdu) || !addSource_FPA_NONE(fpa, format) ||
+                      !pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_PHU,
+                                         true, NULL)) {
+                      psError(PS_ERR_UNKNOWN, false, "Unable to add source.");
+                      psFree(phdu);
+                      psFree(view);
+                      return NULL;
+                  }
+              }
+              psFree(phdu);
+             return view;
+            default:
+              psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                      "EXTENSIONS level (%s) incompatible with PHU level (FPA)", extType);
+              psFree(phdu);
+              psFree(view);
+              return NULL;
+          }
+          break;
+      }
+      case PM_FPA_LEVEL_CHIP: {
+          pmChip *chip = NULL;          // Appropriate chip, if the view is specified
+          if (phuView) {
+              chip = fpa->chips->data[phuView->chip];
+          }
+          switch (extLevel) {
+            case PM_FPA_LEVEL_CELL:
+              phdu->blankPHU = true;
+              if (!addSource_CHIP_CELL(view, fpa, chip, format, phdu, install)) {
+                  psError(PS_ERR_UNKNOWN, false, "Unable to add source.");
+                  psFree(phdu);
+                  psFree(view);
+                  return NULL;
+              }
+              psFree(phdu);
+              return view;
+            case PM_FPA_LEVEL_NONE:
+              phdu->blankPHU = false;
+              if (!addSource_CHIP_NONE(view, fpa, chip, format, phdu, install)) {
+                  psError(PS_ERR_UNKNOWN, false, "Unable to add source.");
+                  psFree(phdu);
+                  psFree(view);
+                  return NULL;
+              }
+              psFree(phdu);
+              return view;
+            default:
+              psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                      "EXTENSIONS level (%s) incompatible with PHU level (CHIP)", extType);
+              return NULL;
+          }
+          break;
+      }
+      case PM_FPA_LEVEL_CELL: {
+          if (extLevel != PM_FPA_LEVEL_NONE) {
+              psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                      "EXTENSIONS level (%s) incompatible with PHU level (CELL)", extType);
+              return NULL;
+          }
+          pmChip *chip = NULL;          // Appropriate chip, if the view is specified
+          pmCell *cell = NULL;          // Appropriate cell, if the view is specified
+          if (phuView) {
+              chip = fpa->chips->data[phuView->chip];
+              cell = chip->cells->data[phuView->cell];
+          }
+          phdu->blankPHU = false;
+          if (!addSource_CELL_NONE(view, fpa, cell, format, phdu, install)) {
+              psError(PS_ERR_UNKNOWN, false, "Unable to add source.");
+              psFree(phdu);
+              psFree(view);
+              return NULL;
+          }
+          psFree(phdu);
+          return view;
+          break;
+      }
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Bad PHU level: %s", phuType);
+        return NULL;
+    }
+
+    return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+pmFPA *pmFPAConstruct(const psMetadata *camera, const char *cameraName)
+{
+    PS_ASSERT_PTR_NON_NULL(camera, NULL);
+
+    pmFPA *fpa = pmFPAAlloc(camera, cameraName);    // The FPA to fill out
+
+    bool mdok = true;                   // Status from MD lookups
+    psMetadata *components = psMetadataLookupMetadata(&mdok, camera, "FPA"); // FPA components
+    if (!mdok || !components) {
+        psError(PS_ERR_IO, true, "Failed to lookup \"FPA\"");
+        psFree(fpa);
+        return NULL;
+    }
+    psMetadataIterator *componentsIter = psMetadataIteratorAlloc(components, PS_LIST_HEAD, NULL);
+    psMetadataItem *componentsItem = NULL; // Item from components
+    while ((componentsItem = psMetadataGetAndIncrement(componentsIter))) {
+        const char *chipName = componentsItem->name; // Name of the chip
+        if (componentsItem->type != PS_DATA_STRING) {
+            psWarning("Element %s in FPA within the camera configuration is not of "
+                     "type STR (type=%x) --- ignored.\n", chipName, componentsItem->type);
+            continue;
+        }
+
+        pmChip *chip = pmChipAlloc(fpa, chipName); // The chip
+        psList *cellNames = psStringSplit(componentsItem->data.V, " ,;", true); // List of cell names
+        psListIterator *cellNamesIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false); // Iterator
+
+        psString cellName = NULL;       // Name of cell
+        while ((cellName = psListGetAndIncrement(cellNamesIter))) {
+            pmCell *cell = pmCellAlloc(chip, cellName); // New cell
+            psFree(cell);               // Drop reference
+        }
+        psFree(chip);                   // Drop reference
+        psFree(cellNamesIter);
+        psFree(cellNames);
+    }
+    psFree(componentsIter);
+
+    return fpa;
+}
+
+bool pmFPAAddSourceFromFormat(pmFPA *fpa, const char *fpaObs, const psMetadata *format)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_METADATA_NON_NULL(format, false);
+
+    // Generate the correct structure
+    pmFPALevel phuLevel = pmFPAPHULevel(format); // Level at which PHU goes
+    pmFPAview *view = pmFPAviewAlloc(0);// View for current level
+    if (phuLevel == PM_FPA_LEVEL_FPA) {
+        if (!pmFPAAddSourceFromView(fpa, fpaObs, view, format)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
+            psFree(view);
+            return false;
+        }
+    } else {
+        pmChip *chip;                       // Chip from FPA
+        while ((chip = pmFPAviewNextChip(view, fpa, 1))) {
+            if (phuLevel == PM_FPA_LEVEL_CHIP) {
+                if (!pmFPAAddSourceFromView(fpa, fpaObs, view, format)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
+                    psFree(view);
+                    return false;
+                }
+            } else {
+                pmCell *cell;                   // Cell from chip
+                while ((cell = pmFPAviewNextCell(view, fpa, 1))) {
+                    if (phuLevel == PM_FPA_LEVEL_CELL) {
+                        if (!pmFPAAddSourceFromView(fpa, fpaObs, view, format)) {
+                            psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
+                            psFree(view);
+                            return false;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    psFree(view);
+
+    return true;
+}
+
+bool pmFPAAddSourceFromView(pmFPA *fpa, const char *fpaObs, const pmFPAview *phuView,
+                            const psMetadata *format)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(phuView, false);
+    PS_ASSERT_PTR_NON_NULL(format, false);
+
+    pmFPAview *view = addSource(fpa, fpaObs, phuView, NULL, format, true);
+    bool status = (view != NULL);
+    psFree(view);
+    return status;
+}
+
+pmFPAview *pmFPAAddSourceFromHeader(pmFPA *fpa, psMetadata *phu, const psMetadata *format)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+    PS_ASSERT_PTR_NON_NULL(phu, NULL);
+    PS_ASSERT_PTR_NON_NULL(format, NULL);
+
+    bool mdok = true;                   // Status from metadata lookups
+    psMetadata *fileInfo = psMetadataLookupMetadata(&mdok, format, "FILE"); // The file information
+    if (!mdok || !fileInfo) {
+        psError(PS_ERR_IO, false, "Unable to find FILE in the camera format configuration.\n");
+        return NULL;
+    }
+
+    // Check the name of the FPA
+    psString fpaObs = phuNameFromHeader("FPA.OBS", fileInfo, phu); // New observation name for the FPA
+    if (!fpaObs || strlen(fpaObs) == 0) {
+        psWarning("Unable to determine FPA.OBS: check for FPA.OBS in FILE in camera format");
+    }
+
+    pmFPAview *view = addSource(fpa, fpaObs, NULL, phu, format, true); // View of PHU, to return
+    psFree(fpaObs);
+
+    return view;
+}
+
+
+pmFPAview *pmFPAIdentifySourceFromHeader(pmFPA *fpa, psMetadata *phu, const psMetadata *format)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+    PS_ASSERT_PTR_NON_NULL(phu, NULL);
+    PS_ASSERT_PTR_NON_NULL(format, NULL);
+
+    return addSource(fpa, NULL, NULL, phu, format, false);
+}
+
+
+// Print spaces to indent
+#define INDENT(FILE, LEVEL) \
+{ \
+    for (int i = 0; i < (LEVEL); i++) { \
+        fprintf(FILE, " "); \
+    } \
+}
+
+void pmFPAPrint(FILE *fd, const pmFPA *fpa, bool header, bool concepts)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa,);
+
+    INDENT(fd, 1);
+    fprintf(fd, "FPA:\n");
+    if (fpa->hdu) {
+        pmHDUPrint(fd, fpa->hdu, 2, header);
+    }
+    if (concepts) {
+        psMetadataPrint(fd, fpa->concepts, 2);
+    }
+
+    psArray *chips = fpa->chips;        // Array of chips
+    // Iterate over the FPA
+    for (int i = 0; i < chips->n; i++) {
+        INDENT(fd, 3);
+        fprintf(fd, "Chip: %d\n", i);
+        pmChip *chip = chips->data[i]; // The chip
+        if (chip->hdu) {
+            pmHDUPrint(fd, chip->hdu, 4, header);
+        }
+        if (concepts) {
+            psMetadataPrint(fd, chip->concepts, 4);
+        }
+
+        // Iterate over the chip
+        psArray *cells = chip->cells;   // Array of cells
+        for (int j = 0; j < cells->n; j++) {
+            INDENT(fd, 5);
+            fprintf(fd, "Cell: %d\n", j);
+            pmCell *cell = cells->data[j]; // The cell
+            if (cell->hdu) {
+                pmHDUPrint(fd, cell->hdu, 6, header);
+            }
+            if (concepts) {
+                psMetadataPrint(fd, cell->concepts, 6);
+            }
+
+            psArray *readouts = cell->readouts; // Array of readouts
+            for (int k = 0; k < readouts->n; k++) {
+                pmReadout *readout = readouts->data[k]; // The readout
+                INDENT(fd, 6);
+                fprintf(fd, "Readout %d:\n", k);
+                INDENT(fd, 7);
+                fprintf(fd, "col0: %d\n", readout->col0);
+                INDENT(fd, 7);
+                fprintf(fd, "row0: %d\n", readout->row0);
+                psImage *image = readout->image; // The image
+                psImage *mask = readout->mask; // The mask
+                psImage *weight = readout->weight; // The weight
+                psList *bias = readout->bias; // The list of bias images
+                if (image) {
+                    INDENT(fd, 7);
+                    fprintf(fd, "Image: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 +
+                            image->numCols, image->row0, image->row0 + image->numRows, image->numCols,
+                            image->numRows);
+                }
+                if (bias) {
+                    psListIterator *biasIter = psListIteratorAlloc(bias, PS_LIST_HEAD, false); // Iterator
+                    psImage *biasImage = NULL; // Bias image from iteration
+                    while ((biasImage = psListGetAndIncrement(biasIter))) {
+                        INDENT(fd, 7);
+                        fprintf(fd, "Bias:  [%d:%d,%d:%d] (%dx%d)\n", biasImage->col0,
+                                biasImage->col0 + biasImage->numCols, biasImage->row0,
+                                biasImage->row0 + biasImage->numRows, biasImage->numCols, biasImage->numRows);
+                    }
+                    psFree(biasIter);
+                }
+                if (mask) {
+                    INDENT(fd, 7);
+                    fprintf(fd, "Mask: [%d:%d,%d:%d] (%dx%d)\n", mask->col0, mask->col0 +
+                            mask->numCols, mask->row0, mask->row0 + mask->numRows, mask->numCols,
+                            mask->numRows);
+                }
+                if (weight) {
+                    INDENT(fd, 7);
+                    fprintf(fd, "Weight: [%d:%d,%d:%d] (%dx%d)\n", weight->col0, weight->col0 +
+                            weight->numCols, weight->row0, weight->row0 + weight->numRows, weight->numCols,
+                            weight->numRows);
+                }
+            } // Iterating over cell
+        } // Iterating over chip
+    } // Iterating over FPA
+
+}
+
+
+pmFPALevel pmFPAPHULevel(const psMetadata *format)
+{
+    PS_ASSERT_METADATA_NON_NULL(format, PM_FPA_LEVEL_NONE);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *fileInfo = psMetadataLookupMetadata(&mdok, format, "FILE"); // Contents of FILE metadata
+    if (!mdok || !fileInfo) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find FILE in camera format configuration.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+    const char *phu = psMetadataLookupStr(&mdok, fileInfo, "PHU"); // PHU level
+    if (!mdok || !phu || strlen(phu) == 0) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find PHU in FILE in camera format configuration.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+
+    return pmFPALevelFromName(phu);
+}
+
+pmFPALevel pmFPAExtensionsLevel(const psMetadata *format)
+{
+    PS_ASSERT_METADATA_NON_NULL(format, PM_FPA_LEVEL_NONE);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *fileInfo = psMetadataLookupMetadata(&mdok, format, "FILE"); // Contents of FILE metadata
+    if (!mdok || !fileInfo) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find FILE in camera format configuration.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+
+    const char *extensions = psMetadataLookupStr(&mdok, fileInfo, "EXTENSIONS"); // EXTENSIONS level
+    if (!mdok || !extensions || strlen(extensions) == 0) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find EXTENSIONS in FILE in camera format configuration.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+
+    return pmFPALevelFromName(extensions);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAConstruct.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAConstruct.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAConstruct.h	(revision 22322)
@@ -0,0 +1,78 @@
+/* @file pmFPAConstruct.h
+ * @brief Functions to create an FPA, and add data sources to it.
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-05 01:31:33 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_CONSTRUCT_H
+#define PM_FPA_CONSTRUCT_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Construct an FPA instance on the basis of a camera configuration
+///
+/// This is the function that creates the FPA hierarchy on the basis of the camera configuration.  The "FPA"
+/// entry in the camera configuration specifies the chips (each of type STR) with their component cells listed
+/// as the corresponding values (whitespace separated).  The FPA hierarchy is created devoid of any
+/// input/output sources (i.e., HDUs).
+pmFPA *pmFPAConstruct(const psMetadata *camera, ///< The camera configuration
+                      const char *cameraName ///< Name of the camera (for FPA.CAMERA concept)
+                     );
+
+/// Add a source to the focal plane hierarchy, specified by a camera format
+///
+/// This is suitable for generating an output FPA given the desired format.
+bool pmFPAAddSourceFromFormat(pmFPA *fpa, ///< The FPA
+                              const char *fpaObs, ///< FPA.NAME for the source
+                              const psMetadata *format ///< Format of file
+    );
+
+/// Add an (input or output) source to the focal plane hierarchy, specified by a view
+///
+/// Given an FPA, add an HDU by specifying where it goes (i.e., by an FPAview).  The camera format
+/// configuration is required in order to describe how the FPA is laid out in terms of disk files.
+bool pmFPAAddSourceFromView(pmFPA *fpa,   ///< The FPA
+                            const char *fpaObs, ///< FPA.NAME for the source
+                            const pmFPAview *phuView, ///< The view, corresponding to the PHU
+                            const psMetadata *format ///< Format of file
+                           );
+
+/// Add an (input or output) source to the focal plane hierarchy, specified by a (primary) header
+///
+/// Given an FPA, add an HDU by specifying a primary header, which is used to determine the FITS file
+/// contents, and therefore the proper location for the HDU.  The camera format configuration is required in
+/// order to describe how the FPA is laid out in terms of disk files.
+pmFPAview *pmFPAAddSourceFromHeader(pmFPA *fpa, ///< The FPA
+                                    psMetadata *phu, ///< Primary header of file
+                                    const psMetadata *format ///< Format of file
+                                   );
+
+/// Identify a source in the focal plane hierarchy, specified by a (primary) header
+///
+/// This is the same as pmFPAAddSourceFromHeader, except the input is not added into the FPA hierarchy.
+/// This function serves only to identify where in the hierarchy it should go, not prepare for reading, etc.
+pmFPAview *pmFPAIdentifySourceFromHeader(pmFPA *fpa, psMetadata *phu, const psMetadata *format);
+
+/// Print a representation of the FPA, including its headers and concepts.
+///
+/// This function is intended for testing and development purposes.
+void pmFPAPrint(FILE *fd,               ///< File descriptor to which to print
+                const pmFPA *fpa,       ///< FPA to print
+                bool header,            ///< Print headers?
+                bool concepts           ///< Print concepts?
+               );
+
+/// Return the PHU level for an FPA, given the format
+pmFPALevel pmFPAPHULevel(const psMetadata *format);
+
+/// Return the Extensions level for an FPA, given the format
+pmFPALevel pmFPAExtensionsLevel(const psMetadata *format);
+
+/// @}
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACopy.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACopy.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACopy.c	(revision 22322)
@@ -0,0 +1,618 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <strings.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAUtils.h"
+#include "pmHDUUtils.h"
+#include "pmFPACopy.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions and macros
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Copy the value for a concept
+#define COPY_CONCEPT(TARGET, SOURCE, NAME, TYPE) { \
+    psMetadataItem *targetItem = psMetadataLookup(TARGET, NAME); \
+    psMetadataItem *sourceItem = psMetadataLookup(SOURCE, NAME); \
+    targetItem->data.TYPE = sourceItem->data.TYPE; }
+
+// Find the blank (image-less) PHU, given a cell.
+static pmHDU *findBlankPHU(const pmCell *cell // The cell for which to find the PHU
+                          )
+{
+    assert(cell);
+
+    if (cell->hdu && cell->hdu->blankPHU) {
+        return cell->hdu;
+    }
+    pmChip *chip = cell->parent;        // The parent chip
+    if (chip->hdu && chip->hdu->blankPHU) {
+        return chip->hdu;
+    }
+    pmFPA *fpa = chip->parent;  // The parent FPA
+    if (fpa->hdu && fpa->hdu->blankPHU) {
+        return fpa->hdu;
+    }
+
+    return NULL;
+}
+
+// copy one of the psImage components of the readout
+static void readoutCopyComponent(psImage **target, // Image to which to copy
+                                 const psImage *source, // Image from which to copy
+                                 psImageBinning *binning, // New binning
+                                 bool xFlip, bool yFlip, // Flip in x or y?
+                                 bool pixels // Copy the pixels?
+                                 )
+{
+    if (!source) return;
+
+    if (*target) {
+        psFree(*target);
+    }
+    if (pixels) {
+        *target = psImageFlip(NULL, source, xFlip, yFlip);
+        return;
+    }
+
+    // I have the fine image size, I know the binning factor, determine the ruff image size
+    binning->nXfine = source->numCols;
+    binning->nYfine = source->numRows;
+    psImageBinningSetRuffSize(binning, PS_IMAGE_BINNING_CENTER);
+    *target = psImageAlloc(binning->nXruff, binning->nYruff, source->type.type);
+    return;
+}
+
+// Update the output analysis metadata, adding stuff in the input
+//
+// This is probably very similar to psMetadataCopy, but we want to explicitly deal with arrays (especially
+// since astronomical sources live in them)
+static psMetadata *updateAnalysis(psMetadata *out, psMetadata *in)
+{
+    psAssert(in, "Require input");
+    if (!out) {
+        out = psMetadataAlloc();
+    }
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(in, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        psMetadataItem *original = psMetadataLookup(in, item->name); // Checking for MULTI
+        psMetadataItem *extant = psMetadataLookup(out, item->name); // Existing item?
+        if ((original && original->type == PS_DATA_METADATA_MULTI) ||
+            (extant && extant->type == PS_DATA_METADATA_MULTI)) {
+            psMetadataAddItem(out, item, PS_LIST_TAIL, PS_META_DUPLICATE_OK);
+            continue;
+        }
+
+        switch (item->type) {
+          case PS_DATA_ARRAY: {
+              // Concatenate arrays if they already exist
+              psMetadataItem *extant = psMetadataLookup(out, item->name); // Existing array?
+              if (extant && extant->type == PS_DATA_ARRAY) {
+                  psArray *new = item->data.V; // New array
+                  psArray *old = extant->data.V; // Old array
+                  long numNew = new->n, numOld = old->n; // Number of values in each
+                  extant->data.V = old = psArrayRealloc(old, old->n + numNew);
+                  for (long i = 0; i < numNew; i++) {
+                      old->data[numOld + i] = psMemIncrRefCounter(new->data[i]);
+                  }
+                  old->n = numOld + numNew;
+              } else {
+                  psMetadataAddItem(out, item, PS_LIST_TAIL, PS_META_REPLACE);
+              }
+              break;
+            default:
+              // If in doubt, there's not much we can do except replace
+              psMetadataAddItem(out, item, PS_LIST_TAIL, PS_META_REPLACE);
+              break;
+          }
+        }
+    }
+    psFree(iter);
+
+    return out;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static engine functions --- these do all the work.  Actually, cellCopy does all the work; the others
+// merely iterate on the higher-level components.
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Common engine for pmCellCopy and pmCellCopyStructure
+// Does the actual splitting/splicing that's required to copy an FPA to a different representation.
+static bool cellCopy(pmCell *target,     // The target cell
+                     const pmCell *source, // The source cell, to be copied
+                     bool pixels,        // Copy the pixels?
+                     int xBin, int yBin  // (Relative) binning factors in x and y
+                    )
+{
+    assert(target);
+    assert(source);
+    assert(xBin > 0 && yBin > 0);
+
+    if (!source->data_exists) {
+        // Copied everything that exists
+        return true;
+    }
+
+    // XXX this is a programming / config error
+    if (pixels && (xBin != 1 || yBin != 1)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to copy pixels if binning is set\n");
+        return false;
+    }
+
+    // the binning structure carries the information on how to rebin the images if needed
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXbin = xBin;
+    binning->nYbin = yBin;
+
+    psArray *sourceReadouts = source->readouts; // The source readouts
+    int numReadouts = sourceReadouts->n; // Number of readouts copied
+
+    // Need to check/change CELL.XPARITY and CELL.YPARITY
+    bool mdokS = true;                   // Status of MD lookup
+    bool mdokT = true;                   // Status of MD lookup
+    bool xFlip = false;                 // Switch parity in x?
+    bool yFlip = false;                 // Switch parity in y?
+
+    // enforce the following conditions:
+    // CELL.XPARITY is required for source
+    // CELL.XPARITY must be +/- 1
+    int xParitySource = psMetadataLookupS32(&mdokS, source->concepts, "CELL.XPARITY"); // Source parity
+    int xParityTarget = psMetadataLookupS32(&mdokT, target->concepts, "CELL.XPARITY"); // Target x parity
+    assert(mdokS && mdokT);
+    if (abs(xParitySource) != 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CELL.XPARITY is not set for source (%d)",
+                xParitySource);
+        psFree(binning);
+        return false;
+    } else {
+        // Use the source parity
+        COPY_CONCEPT(target->concepts, source->concepts, "CELL.XPARITY", S32);
+        xParityTarget = xParitySource;
+    }
+    if (xParityTarget != xParitySource) {
+        xFlip = true;
+    }
+
+    int yParityTarget = psMetadataLookupS32(&mdokT, target->concepts, "CELL.YPARITY"); // Target y parity
+    int yParitySource = psMetadataLookupS32(&mdokS, source->concepts, "CELL.YPARITY"); // Source parity
+    assert(mdokS && mdokT);
+    if (abs(yParitySource) != 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CELL.YPARITY is not set for source (%d)",
+                yParitySource);
+        psFree(binning);
+        return false;
+    } else {
+        // Use the source parity
+        COPY_CONCEPT(target->concepts, source->concepts, "CELL.YPARITY", S32);
+        yParityTarget = yParitySource;
+    }
+    if (yParityTarget != yParitySource) {
+        yFlip = true;
+    }
+    psTrace("psModules.camera", 3, "xFlip: %d; yFlip: %d\n", xFlip, yFlip);
+
+    // Perform deep copy of the images.  I would prefer *not* to do a deep copy, in the interests of speed (we
+    // still need to do another deep copy into the HDU for when we write out), but this is the only way I can
+    // think of to provide security against copying a cell and then unknowingly changing the source when
+    // manipulating the target.
+    for (int i = 0; i < numReadouts; i++) {
+        pmReadout *sourceReadout = sourceReadouts->data[i]; // The source readout
+        pmReadout *targetReadout = pmReadoutAlloc(target); // The target readout; this adds it to the cell
+
+        // Copy attributes
+        // XXX is this correct under binning?
+        targetReadout->col0 = sourceReadout->col0;
+        targetReadout->row0 = sourceReadout->row0;
+        targetReadout->process = sourceReadout->process;
+        targetReadout->file_exists = sourceReadout->file_exists;
+        targetReadout->data_exists = sourceReadout->data_exists;
+
+        // Copy all three image components (image, mask, weight)
+        readoutCopyComponent(&targetReadout->image,  sourceReadout->image,  binning, xFlip, yFlip, pixels);
+        readoutCopyComponent(&targetReadout->mask,   sourceReadout->mask,   binning, xFlip, yFlip, pixels);
+        readoutCopyComponent(&targetReadout->weight, sourceReadout->weight, binning, xFlip, yFlip, pixels);
+
+        // Copy bias
+        while (targetReadout->bias->n > 0) {
+            psListRemove(targetReadout->bias, PS_LIST_HEAD);
+        }
+
+        // Iterate over the biases
+        psListIterator *biasIter = psListIteratorAlloc(sourceReadout->bias, PS_LIST_HEAD, false);
+        psImage *bias = NULL;           // Bias image from iteration
+        while ((bias = psListGetAndIncrement(biasIter))) {
+            psImage *biasCopy = NULL;          // Copy of the bias
+            readoutCopyComponent (&biasCopy, bias, binning, xFlip, yFlip, pixels);
+            psListAdd(targetReadout->bias, PS_LIST_TAIL, biasCopy);
+            psFree(biasCopy);           // Drop reference
+        }
+        psFree(biasIter);
+
+        // Copy the analysis metadata
+        targetReadout->analysis = updateAnalysis(targetReadout->analysis, sourceReadout->analysis);
+
+        targetReadout->data_exists = true;
+        psFree(targetReadout);          // Drop reference
+    }
+
+    // Copy the remaining "concepts" over.  Don't copy the TRIMSEC or BIASSEC, since these will be created by
+    // pmHDUGenerate if they don't already exist in the target.  Don't copy the XPARITY or YPARITY, since
+    // we've used those to do the flips.  Don't copy the X0 and Y0 because they are updated below (and are
+    // dependent upon the flips we've done above).
+    psMetadataIterator *conceptsIter = psMetadataIteratorAlloc(source->concepts, PS_LIST_HEAD, NULL);
+    psMetadataItem *conceptItem = NULL; // Item from iteration
+    while ((conceptItem = psMetadataGetAndIncrement(conceptsIter))) {
+        psString name = conceptItem->name; // Name of concept
+        if (!strcmp(name, "CELL.TRIMSEC")) continue;
+        if (!strcmp(name, "CELL.BIASSEC")) continue;
+        if (!strcmp(name, "CELL.XPARITY")) continue;
+        if (!strcmp(name, "CELL.YPARITY")) continue;
+        if (!strcmp(name, "CELL.X0")) continue;
+        if (!strcmp(name, "CELL.Y0")) continue;
+
+        psMetadataItem *copy = psMetadataItemCopy(conceptItem); // Copy of the concept
+        psMetadataAddItem(target->concepts, copy, PS_LIST_TAIL, PS_META_REPLACE);
+        psFree(copy);               // Drop reference
+    }
+    psFree(conceptsIter);
+
+    // Need to update CELL.TRIMSEC and CELL.BIASSEC if we changed the binning and they exist already.
+    // XXX this code seems to be very similar to pmConceptsUpdate
+    if ((binning->nXbin != 1) || (binning->nYbin != 1)) {
+        bool mdok = false;
+        psRegion *trimsec = psMetadataLookupPtr(&mdok, target->concepts, "CELL.TRIMSEC"); // The trim section
+        if (mdok && trimsec && !psRegionIsNaN(*trimsec)) {
+            *trimsec = psImageBinningSetRuffRegion(binning, *trimsec);
+            // force integer pixels : truncate x0, roundup x1:
+            trimsec->x0 = (int)trimsec->x0;
+            if (trimsec->x1 > (int)trimsec->x1) {
+                trimsec->x1 = (int)trimsec->x1 + 1;
+            } else {
+                trimsec->x1 = (int)trimsec->x1;
+            }
+            trimsec->y0 = (int)trimsec->y0;
+            if (trimsec->y1 > (int)trimsec->y1) {
+                trimsec->y1 = (int)trimsec->y1 + 1;
+            } else {
+                trimsec->y1 = (int)trimsec->y1;
+            }
+        }
+        psList *biassecs = psMetadataLookupPtr(&mdok, target->concepts, "CELL.BIASSEC"); // The bias sections
+        if (mdok && biassecs && biassecs->n > 0) {
+            psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, true); // Iterator
+            psRegion *biassec = NULL;   // Bias section, from iteration
+            while ((biassec = psListGetAndIncrement(biassecsIter))) {
+                if (!psRegionIsNaN(*biassec)) {
+                    *biassec = psImageBinningSetRuffRegion(binning, *biassec);
+                    // force integer pixels : truncate x0, roundup x1:
+                    biassec->x0 = (int)biassec->x0;
+                    if (biassec->x1 > (int)biassec->x1) {
+                        biassec->x1 = (int)biassec->x1 + 1;
+                    } else {
+                        biassec->x1 = (int)biassec->x1;
+                    }
+                    biassec->y0 = (int)biassec->y0;
+                    if (biassec->y1 > (int)biassec->y1) {
+                        biassec->y1 = (int)biassec->y1 + 1;
+                    } else {
+                        biassec->y1 = (int)biassec->y1;
+                    }
+                }
+            }
+            psFree(biassecsIter);
+        }
+    }
+
+    // Need to update CELL.X0 and CELL.Y0 if we flipped
+    // XXX this section should probably use a common function consistent with psImageBinning
+    if (xFlip) {
+        int xZero = psMetadataLookupS32(NULL, source->concepts, "CELL.X0"); // CELL.X0 from source
+        int xParity = psMetadataLookupS32(NULL, source->concepts, "CELL.XPARITY"); // Parity in x
+        int sourceBin = psMetadataLookupS32(NULL, source->concepts, "CELL.XBIN"); // CELL.XBIN from source
+        int xSize = psMetadataLookupS32(NULL, source->concepts, "CELL.XSIZE"); // CELL.XSIZE of source
+
+        if (sourceBin == 0) {
+            // Don't know the binning; assume it is unity
+            sourceBin = binning->nXbin;
+        }
+
+        // XXX make sure this is consistent with the psImageBinning
+        psTrace("psModules.camera", 3, "CELL.X0: Before: %d After: %d\n", xZero, xZero + (xSize - 1) * xParity * sourceBin);
+        psTrace("psModules.camera", 9, "(xParity: %d xBin: %d numCols: %d)\n", xParity, sourceBin, xSize);
+
+        if (xParity == 0 || xSize == 0) {
+            psWarning("New CELL.X0 may be incorrect due to missing concepts (CELL.XPARITY, CELL.XSIZE)");
+        }
+
+        xZero += (xSize - 1) * xParity * sourceBin; // Change the parity on the X0 position
+        psMetadataItem *newItem = psMetadataLookup(target->concepts, "CELL.X0"); // CELL.X0 from target
+        newItem->data.S32 = xZero;
+    }
+    if (yFlip) {
+        int yZero = psMetadataLookupS32(NULL, source->concepts, "CELL.Y0"); // CELL.Y0 from source
+        int yParity = psMetadataLookupS32(NULL, source->concepts, "CELL.YPARITY"); // Parity in y
+        int sourceBin = psMetadataLookupS32(NULL, source->concepts, "CELL.YBIN"); // Binning in y
+        int ySize = psMetadataLookupS32(NULL, source->concepts, "CELL.YSIZE"); // CELL.YSIZE of source
+
+        if (sourceBin == 0) {
+            // Don't know the binning; assume it is unity
+            sourceBin = binning->nYbin;
+        }
+
+        psTrace("psModules.camera", 3, "CELL.Y0: Before: %d After: %d\n", yZero, yZero + (ySize - 1) * yParity * sourceBin);
+        psTrace("psModules.camera", 9, "(yParity: %d yBin: %d numRows: %d)\n", yParity, sourceBin, ySize);
+
+        if (yParity == 0 || ySize == 0) {
+            psWarning("New CELL.Y0 may be incorrect due to missing concepts "
+                      "(CELL.Y0, CELL.YPARITY, CELL.YBIN, CELL.YSIZE)");
+        }
+
+        yZero += (ySize - 1) * yParity * sourceBin; // Change the parity on the Y0 position
+        psMetadataItem *newItem = psMetadataLookup(target->concepts, "CELL.Y0"); // CELL.Y0 from target
+        newItem->data.S32 = yZero;
+    }
+
+    // Update the binning concepts
+    // XXX this should probably be done with a common function using psImageBinning
+    psMetadataItem *binItem = psMetadataLookup(target->concepts, "CELL.XBIN");
+    binItem->data.S32 *= xBin;
+    binItem = psMetadataLookup(target->concepts, "CELL.YBIN");
+    binItem->data.S32 *= yBin;
+
+    // Update the analysis metadata
+    target->analysis = updateAnalysis(target->analysis, source->analysis);
+
+    // Copy any headers
+    pmHDU *targetHDU = pmHDUFromCell(target); // The target HDU
+    if (targetHDU) {
+        pmHDU *sourceHDU = pmHDUFromCell(source); // The source HDU
+        if (!sourceHDU) {
+            psWarning("Unable to copy header: no source header.");
+        } else if (sourceHDU->header) {
+            targetHDU->header = psMetadataCopy(targetHDU->header, sourceHDU->header);
+        }
+    }
+
+    // Copy the PHU over as well, if required
+    pmHDU *targetPHU = findBlankPHU(target); // The target PHU
+    if (targetPHU && targetPHU != targetHDU) {
+        // pmHDU *sourcePHU = pmHDUGetHighest(source->parent->parent, source->parent, source); // A source HDU
+        pmHDU *sourcePHU = findBlankPHU(source); // The target PHU
+        if (sourcePHU && sourcePHU->header) {
+            targetPHU->header = psMetadataCopy(targetPHU->header, sourcePHU->header);
+        }
+    }
+
+    psFree (binning);
+    target->data_exists = true;
+    target->parent->data_exists = true;
+    return true;
+}
+
+// Common engine for pmChipCopy and pmChipCopyStructure
+// Iterate on the components
+static bool chipCopy(pmChip *target,          // The target chip
+                     const pmChip *source, // The source chip, to be copied
+                     bool pixels,             // Copy the pixels?
+                     int xBin, int yBin       // (Relative) binning factors in x and y
+                    )
+{
+    assert(target);
+    assert(source);
+    assert(xBin > 0);
+    assert(yBin > 0);
+
+    if (!source->data_exists) {
+        // Copied everything that exists
+        return true;
+    }
+
+    psArray *targetCells = target->cells; // The target cells
+    psArray *sourceCells = source->cells; // The source cells
+    if (targetCells->n != sourceCells->n) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Number of source cells (%ld) differs from the number of target cells (%ld)\n",
+                sourceCells->n, targetCells->n);
+        return false;
+    }
+
+    bool status = true;                 // Status of copy
+    for (int i = 0; i < targetCells->n; i++) {
+        pmCell *targetCell = targetCells->data[i]; // The target cell
+        const char *cellName = psMetadataLookupStr(NULL, targetCell->concepts, "CELL.NAME"); // Name of cell
+        int cellNum = pmChipFindCell(source, cellName); // Number of cell with that name
+        if (cellNum >= 0) {
+            pmCell *sourceCell = sourceCells->data[cellNum]; // The source cell
+            status &= cellCopy(targetCell, sourceCell, pixels, xBin, yBin);
+        }
+    }
+
+    // Update the analysis metadata
+    target->analysis = updateAnalysis(target->analysis, source->analysis);
+
+    // Update the concepts
+    psMetadataItem *chipName = psMemIncrRefCounter(psMetadataLookup(target->concepts, "CHIP.NAME"));
+    psMetadataCopy(target->concepts, source->concepts);
+    psMetadataAddItem(target->concepts, chipName, PS_LIST_TAIL, PS_META_REPLACE);
+    psFree(chipName);
+    psMetadataCopy(target->parent->concepts, source->parent->concepts);
+
+    target->data_exists = true;
+    return status;
+}
+
+// create a new pmChip with the data derived from the supplied chip
+pmChip *pmChipDuplicate(pmFPA *fpa, const pmChip *sourceChip)
+{
+    assert(sourceChip);
+
+    bool status;
+    char *chipName = psMetadataLookupStr(&status, sourceChip->concepts, "CHIP.NAME");
+    pmChip *targetChip = pmChipAlloc (NULL, chipName);
+    targetChip->parent = fpa;
+
+    psArray *sourceCells = sourceChip->cells; // The source cells
+
+    for (int i = 0; i < sourceCells->n; i++) {
+        pmCell *sourceCell = sourceCells->data[i]; // The sources cell
+        const char *cellName = psMetadataLookupStr(NULL, sourceCell->concepts, "CELL.NAME"); // Name of cell
+        // XXX are there other concepts I need to copy first?
+        pmCell *targetCell = pmCellAlloc (targetChip, cellName);
+        int xParityTarget = psMetadataLookupS32(&status, sourceCell->concepts, "CELL.XPARITY"); // Target x parity
+        psMetadataAddS32 (targetCell->concepts, PS_LIST_TAIL, "CELL.XPARITY", PS_META_REPLACE, "", xParityTarget);
+        int yParityTarget = psMetadataLookupS32(&status, sourceCell->concepts, "CELL.YPARITY"); // Target y parity
+        psMetadataAddS32 (targetCell->concepts, PS_LIST_TAIL, "CELL.YPARITY", PS_META_REPLACE, "", yParityTarget);
+        if (!cellCopy(targetCell, sourceCell, true, 1, 1)) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "failed to duplicate chip\n");
+            return NULL;
+        }
+        // update the attributes
+        targetCell->file_exists = sourceCell->file_exists;
+        targetCell->data_exists = sourceCell->data_exists;
+        targetCell->process     = sourceCell->process;
+    }
+
+    // Update the analysis metadata
+    targetChip->analysis = updateAnalysis(targetChip->analysis, sourceChip->analysis);
+
+    // Update the concepts
+    psMetadataCopy(targetChip->concepts, sourceChip->concepts);
+
+    // update the attributes
+    targetChip->file_exists = sourceChip->file_exists;
+    targetChip->data_exists = sourceChip->data_exists;
+    targetChip->process     = sourceChip->process;
+
+    return targetChip;
+}
+
+// Common engine for pmFPACopy and pmFPACopyStructure.
+// Iterate on the components
+static bool fpaCopy(pmFPA *target,      // The target FPA
+                    const pmFPA *source, // The source FPA, to be copied
+                    bool pixels,        // Copy the pixels?
+                    int xBin, int yBin  // (Relative) binning factors in x and y
+                   )
+{
+    assert(target);
+    assert(source);
+    assert(xBin > 0);
+    assert(yBin > 0);
+
+    psArray *targetChips = target->chips; // The target chips
+    psArray *sourceChips = source->chips; // The source chips
+    if (targetChips->n != sourceChips->n) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Number of source chips (%ld) differs from the number of target chips (%ld)\n",
+                sourceChips->n, targetChips->n);
+        return false;
+    }
+
+    bool status = true;                 // Status of copy
+    for (int i = 0; i < targetChips->n; i++) {
+        pmChip *targetChip = targetChips->data[i]; // The target chip
+        const char *chipName = psMetadataLookupStr(NULL, targetChip->concepts, "CHIP.NAME"); // Name of chip
+        int chipNum = pmFPAFindChip(source, chipName); // Number of chip with that name
+        if (chipNum >= 0) {
+            pmChip *sourceChip = sourceChips->data[chipNum]; // The source chip
+            status &= chipCopy(targetChip, sourceChip, pixels, xBin, yBin);
+        }
+    }
+
+    // Update the analysis metadata
+    target->analysis = updateAnalysis(target->analysis, source->analysis);
+
+    // Update the concepts
+    psMetadataCopy(target->concepts, source->concepts);
+
+    return status;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmFPACopy(pmFPA *target, const pmFPA *source)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    if (target == source) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Can't copy FPA onto itself.");
+        return false;
+    }
+    return fpaCopy(target, source, true, 1, 1);
+}
+
+bool pmChipCopy(pmChip *target, const pmChip *source)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    if (target == source) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Can't copy chip onto itself.");
+        return false;
+    }
+    return chipCopy(target, source, true, 1, 1);
+}
+
+bool pmCellCopy(pmCell *target, const pmCell *source)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    if (target == source) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Can't copy cell onto itself.");
+        return false;
+    }
+    return cellCopy(target, source, true, 1, 1);
+}
+
+
+bool pmFPACopyStructure(pmFPA *target, const pmFPA *source, int xBin, int yBin)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_INT_POSITIVE(xBin, false);
+    PS_ASSERT_INT_POSITIVE(yBin, false);
+    if (target == source) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Can't copy FPA onto itself.");
+        return false;
+    }
+    return fpaCopy(target, source, false, xBin, yBin);
+}
+
+bool pmChipCopyStructure(pmChip *target, const pmChip *source, int xBin, int yBin)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_INT_POSITIVE(xBin, false);
+    PS_ASSERT_INT_POSITIVE(yBin, false);
+    if (target == source) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Can't copy chip onto itself.");
+        return false;
+    }
+    return chipCopy(target, source, false, xBin, yBin);
+}
+
+bool pmCellCopyStructure(pmCell *target, const pmCell *source, int xBin, int yBin)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_INT_POSITIVE(xBin, false);
+    PS_ASSERT_INT_POSITIVE(yBin, false);
+    if (target == source) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Can't copy cell onto itself.");
+        return false;
+    }
+    return cellCopy(target, source, false, xBin, yBin);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACopy.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACopy.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPACopy.h	(revision 22322)
@@ -0,0 +1,78 @@
+/* @file pmFPACopy.h
+ * @brief Functions to copy FPA components.
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-04-12 02:51:44 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_COPY_H
+#define PM_FPA_COPY_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Copy an FPA and components, including the pixels, to a different representation of the same camera
+///
+/// This function is useful for converting between different representations of the same camera.  For example,
+/// between Megacam "RAW" (one amp per extension) and Megacam "SPLICED" formats (two amps = 1 chip per
+/// extension, spliced together).  Components are spliced together as necessary.
+bool pmFPACopy(pmFPA *target,           ///< The target FPA
+               const pmFPA *source      ///< The source FPA, to be copied
+              );
+
+/// Copy a chip and components, including the pixels, to a different representation of the same camera
+///
+/// This function is useful for converting between different representations of the same camera.  For example,
+/// between Megacam "RAW" (one amp per extension) and Megacam "SPLICED" formats (two amps = 1 chip per
+/// extension, spliced together).  Components are spliced together as necessary.
+bool pmChipCopy(pmChip *target,         ///< The target chip
+                const pmChip *source    ///< The source chip, to be copied
+               );
+
+/// Copy a cell and components, including the pixels, to a different representation of the same camera
+///
+/// This function is useful for converting between different representations of the same camera.  For example,
+/// between Megacam "RAW" (one amp per extension) and Megacam "SPLICED" formats (two amps = 1 chip per
+/// extension, spliced together).  Components are spliced together as necessary.
+bool pmCellCopy(pmCell *target,         ///< The target cell
+                const pmCell *source    ///< The source cell, to be copied
+               );
+
+
+/// Copy an FPA, but not the pixels, to a different representation of the same camera
+///
+/// This function the same as pmFPACopy, except that the pixels are not copied (though images of sufficient
+/// size are allocated in the target).  Changes the CELL.XBIN and CELL.YBIN according to the provided binning
+/// factors.
+bool pmFPACopyStructure(pmFPA *target,   ///< The target FPA
+                        const pmFPA *source, ///< The source FPA, to be copied
+                        int xBin, int yBin ///< Binning factors in x and y
+                       );
+
+/// Copy a chip, but not the pixels, to a different representation of the same camera
+///
+/// This function the same as pmChipCopy, except that the pixels are not copied (though images of sufficient
+/// size are allocated in the target).  Changes the CELL.XBIN and CELL.YBIN according to the provided binning
+/// factors.
+bool pmChipCopyStructure(pmChip *target, ///< The target chip
+                         const pmChip *source, ///< The source chip, to be copied
+                         int xBin, int yBin ///< Binning factors in x and y
+                        );
+
+/// Copy a cell, but not the pixels, to a different representation of the same camera
+///
+/// This function the same as pmCellCopy, except that the pixels are not copied (though images of sufficient
+/// size are allocated in the target).  Changes the CELL.XBIN and CELL.YBIN according to the provided binning
+/// factors.
+bool pmCellCopyStructure(pmCell *target, ///< The target cell
+                         const pmCell *source, ///< The source cell, to be copied
+                         int xBin, int yBin ///< Binning factors in x and y
+                        );
+
+pmChip *pmChipDuplicate(pmFPA *fpa, const pmChip *source);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAExtent.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAExtent.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAExtent.c	(revision 22322)
@@ -0,0 +1,174 @@
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+
+// return cell pixels bounding the readout
+psRegion *pmReadoutExtent(const pmReadout *readout)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, NULL);
+
+    psImage *image = readout->image;    // Image from which to get dimensions
+    if (!image) {
+        image = readout->mask;
+    }
+    if (!image) {
+        image = readout->weight;
+    }
+
+    int xSize = 0;
+    int ySize = 0;
+
+    if (!image) {
+	pmHDU *hdu = pmHDUFromReadout (readout);
+	if (hdu && hdu->header) {
+	    bool status;
+	    xSize = psMetadataLookupS32(&status, hdu->header, "NAXIS1");
+	    if (!status) {
+		xSize = psMetadataLookupS32(&status, hdu->header, "IMNAXIS1");
+		if (!status) return NULL;
+	    }		
+	    ySize = psMetadataLookupS32(&status, hdu->header, "NAXIS2");
+	    if (!status) {
+		ySize = psMetadataLookupS32(&status, hdu->header, "IMNAXIS2");
+		if (!status) return NULL;
+	    }		
+	} else {
+        // Don't have anything to base the true extent on, so have to give the hardwired value (largest possible extent)
+	    xSize = psMetadataLookupS32(NULL, readout->parent->concepts, "CELL.XSIZE");
+	    ySize = psMetadataLookupS32(NULL, readout->parent->concepts, "CELL.YSIZE");
+	}
+        return psRegionAlloc(0, xSize, 0, ySize);
+    }
+
+    // Get the offset to the CCD window
+    int xWindow = psMetadataLookupS32(NULL, readout->parent->concepts, "CELL.XWINDOW");
+    int yWindow = psMetadataLookupS32(NULL, readout->parent->concepts, "CELL.YWINDOW");
+    return psRegionAlloc(xWindow, xWindow + image->numCols,
+                         yWindow, yWindow + image->numRows);
+}
+
+// return chip pixels bounding the cell (all readouts)
+psRegion *pmCellExtent(const pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    psArray *readouts = cell->readouts; // Array of component readouts
+    psRegion *cellExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of cell
+    for (long i = 0; i < readouts->n; i++) {
+        pmReadout *readout = readouts->data[i]; // Readout of interest
+        psRegion *roExtent = pmReadoutExtent(readout); // Extent of readout
+        cellExtent->x0 = PS_MIN(cellExtent->x0, roExtent->x0);
+        cellExtent->x1 = PS_MAX(cellExtent->x1, roExtent->x1);
+        cellExtent->y0 = PS_MIN(cellExtent->y0, roExtent->y0);
+        cellExtent->y1 = PS_MAX(cellExtent->y1, roExtent->y1);
+        psFree(roExtent);
+    }
+
+    // Don't have anything to base the true extent on, so have to give the hardwired value (largest possible extent)
+    if (readouts->n == 0) {
+	int xSize = psMetadataLookupS32(NULL, cell->concepts, "CELL.XSIZE");
+	int ySize = psMetadataLookupS32(NULL, cell->concepts, "CELL.YSIZE");
+	cellExtent->x0 = 0;
+	cellExtent->x1 = xSize;
+	cellExtent->y0 = 0;
+	cellExtent->y1 = ySize;
+    }
+
+    bool mdok;                          // Status of MD lookup
+    int x0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0"); // Cell x offset
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find CELL.X0.\n");
+        psFree(cellExtent);
+        return NULL;
+    }
+    int y0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0"); // Cell y offset
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find CELL.Y0.\n");
+        psFree(cellExtent);
+        return NULL;
+    }
+
+    cellExtent->x0 += x0;
+    cellExtent->x1 += x0;
+    cellExtent->y0 += y0;
+    cellExtent->y1 += y0;
+
+    return cellExtent;
+}
+
+// return chip pixels included in all cells
+psRegion *pmChipPixels(const pmChip *chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    psArray *cells = chip->cells;       // Array of component cells
+    psRegion *chipExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of chip
+    for (long i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        psRegion *cellExtent = pmCellExtent(cell); // Extent of cell
+        chipExtent->x0 = PS_MIN(chipExtent->x0, cellExtent->x0);
+        chipExtent->x1 = PS_MAX(chipExtent->x1, cellExtent->x1);
+        chipExtent->y0 = PS_MIN(chipExtent->y0, cellExtent->y0);
+        chipExtent->y1 = PS_MAX(chipExtent->y1, cellExtent->y1);
+        psFree(cellExtent);
+    }
+
+    return chipExtent;
+}
+
+// return pixels in basic FPA grid bounded by chip
+// this FPA grid has 0,0 at the 0,0 corner of one chip, and is NOT the same
+// as the astrometry focal plane coordinate system
+psRegion *pmChipExtent(const pmChip *chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    psRegion *chipExtent = pmChipPixels(chip);
+    if (!chipExtent) return NULL;
+
+    bool mdok;                          // Status of MD lookup
+    int x0 = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.X0"); // Chip x offset
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find CHIP.X0.\n");
+        psFree(chipExtent);
+        return NULL;
+    }
+    int y0 = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.Y0"); // Chip y offset
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find CHIP.Y0.\n");
+        psFree(chipExtent);
+        return NULL;
+    }
+
+    chipExtent->x0 += x0;
+    chipExtent->x1 += x0;
+    chipExtent->y0 += y0;
+    chipExtent->y1 += y0;
+
+    return chipExtent;
+}
+
+// return FPA pixels included in all chips
+// this FPA grid has 0,0 at the 0,0 corner of one chip, and is NOT the same
+// as the astrometry focal plane coordinate system
+psRegion *pmFPAPixels(const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    psArray *chips = fpa->chips;       // Array of component chips
+    psRegion *fpaExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of fpa
+    for (long i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // Chip of interest
+        psRegion *chipExtent = pmChipExtent(chip); // Extent of chip
+        fpaExtent->x0 = PS_MIN(fpaExtent->x0, chipExtent->x0);
+        fpaExtent->x1 = PS_MAX(fpaExtent->x1, chipExtent->x1);
+        fpaExtent->y0 = PS_MIN(fpaExtent->y0, chipExtent->y0);
+        fpaExtent->y1 = PS_MAX(fpaExtent->y1, chipExtent->y1);
+        psFree(chipExtent);
+    }
+
+    return fpaExtent;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAExtent.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAExtent.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAExtent.h	(revision 22322)
@@ -0,0 +1,49 @@
+/* @file  pmPFAExtent.h
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-21 22:01:32 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_EXTENT_H
+#define PM_FPA_EXTENT_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Return the extent of a readout
+///
+/// The extent is determined from an image, if present, or the CELL.TRIMSEC otherwise.
+psRegion *pmReadoutExtent(const pmReadout *readout ///< The readout of interest
+                         );
+
+/// Return the extent of a cell
+///
+/// The extent is determined from the extent of the component readouts, plus CELL.X0,Y0
+psRegion *pmCellExtent(const pmCell *cell ///< The cell of interest
+                      );
+
+// return chip pixels included in all cells
+psRegion *pmChipPixels(const pmChip *chip);
+
+/// Return the extent of a chip
+///
+/// The extent is determined from the extent of the component cells, plus CHIP.X0,Y0
+psRegion *pmChipExtent(const pmChip *chip ///< The chip of interest
+                      );
+
+// return FPA pixels included in all chips
+// this FPA grid has 0,0 at the 0,0 corner of one chip, and is NOT the same
+// as the astrometry focal plane coordinate system
+psRegion *pmFPAPixels(const pmFPA *fpa);
+
+/// Return the extent of an FPA
+///
+/// The extent is determined from the extent of the component chips.
+psRegion *pmFPAExtent(const pmFPA *fpa ///< The FPA of interest
+                     );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAFlags.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAFlags.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAFlags.c	(revision 22322)
@@ -0,0 +1,341 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAFlags.h"
+
+
+/** functions to turn on/off the file_exists flag **/
+bool pmFPASetFileStatus(pmFPA *fpa, bool status)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        pmChipSetFileStatus (chip, status);
+    }
+    return true;
+}
+
+bool pmChipSetFileStatus(pmChip *chip, bool status)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+
+    chip->file_exists = status;
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i];
+        pmCellSetFileStatus (cell, status);
+    }
+    return true;
+}
+
+bool pmCellSetFileStatus(pmCell *cell, bool status)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+
+    cell->file_exists = status;
+    for (int i = 0; i < cell->readouts->n; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        readout->file_exists = status;
+    }
+    return true;
+}
+
+bool pmFPACheckFileStatus(const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        if (!pmChipCheckFileStatus(chip)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool pmChipCheckFileStatus(const pmChip *chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    if (!chip->file_exists) {
+        return false;
+    }
+
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i];
+        if (!pmCellCheckFileStatus(cell)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool pmCellCheckFileStatus(const pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    if (!cell->file_exists) {
+        return false;
+    }
+
+    for (int i = 0; i < cell->readouts->n; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        if (!readout->file_exists) {
+            return false;
+        }
+    }
+    return true;
+}
+
+/** functions to turn on/off the data_exists flag **/
+bool pmFPASetDataStatus(pmFPA *fpa, bool status)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        pmChipSetDataStatus (chip, status);
+    }
+    return true;
+}
+
+bool pmChipSetDataStatus(pmChip *chip, bool status)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+
+    chip->data_exists = status;
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i];
+        pmCellSetDataStatus (cell, status);
+    }
+    return true;
+}
+
+bool pmCellSetDataStatus (pmCell *cell, bool status)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+
+    cell->data_exists = status;
+    for (int i = 0; i < cell->readouts->n; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        readout->data_exists = status;
+    }
+    return true;
+}
+
+// pmFPA does not have its own data_exists flag; check its children
+bool pmFPACheckDataStatus (const pmFPA *fpa) {
+
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        if (chip == NULL) continue;
+        if (chip->data_exists) return true;
+    }
+    return false;
+}
+
+bool pmChipCheckDataStatus (const pmChip *chip) {
+
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+
+    return (chip->data_exists);
+}
+
+bool pmCellCheckDataStatus (const pmCell *cell) {
+
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+
+    return (cell->data_exists);
+}
+
+bool pmReadoutCheckDataStatus (const pmReadout *readout) {
+
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+
+    return (readout->data_exists);
+}
+
+bool pmFPAviewCheckDataStatus (const pmFPA *fpa, const pmFPAview *view) {
+
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    if (view->chip == -1) {
+        bool exists = pmFPACheckDataStatus (fpa);
+        return exists;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        psError(PS_ERR_IO, true, "Requested chip == %d >= fpa->chips->n == %ld", view->chip, fpa->chips->n);
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        bool exists = pmChipCheckDataStatus (chip);
+        return exists;
+    }
+
+    if (view->cell >= chip->cells->n) {
+        psError(PS_ERR_IO, true, "Requested cell == %d >= chip->cells->n == %ld", view->cell, chip->cells->n);
+        return false;
+    }
+    pmCell *cell = chip->cells->data[view->cell];
+
+    if (view->readout == -1) {
+        bool exists = pmCellCheckDataStatus (cell);
+        return exists;
+    }
+
+    if (view->readout >= cell->readouts->n) {
+        psError(PS_ERR_IO, true, "Requested readout == %d >= cell->readouds->n == %ld", view->readout, cell->readouts->n);
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[view->readout];
+
+    bool exists = pmReadoutCheckDataStatus (readout);
+    return exists;
+}
+
+// Set cells within a chip to be processed or not
+static bool setCellsProcess(const pmChip *chip, // Chip of interest
+                            bool process  // Process this chip?
+                           )
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+
+    psArray *cells = chip->cells;       // Component cells
+    if (! cells) {
+        return false;
+    }
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *tmpCell = cells->data[i]; // Cell of interest
+        if (tmpCell) {
+            tmpCell->process = process;
+        }
+    }
+
+    return true;
+}
+
+
+bool pmFPASelectChip(pmFPA *fpa, int chipNum, bool exclusive)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    psArray *chips = fpa->chips;        // Component chips
+    if ((chips == NULL) || (chipNum >= chips->n)) {
+        return(false);
+    }
+
+    for (int i = 0 ; i < chips->n ; i++) {
+        pmChip *tmpChip = (pmChip *) chips->data[i];
+        if (tmpChip == NULL) {
+            continue;
+        }
+        if (i == chipNum) {
+            tmpChip->process = true;
+            setCellsProcess(tmpChip, true);
+        } else {
+            if (exclusive) {
+                tmpChip->process = false;
+                setCellsProcess(tmpChip, false);
+            }
+        }
+
+    }
+
+    return true;
+}
+
+// XXX this function should probably be re-defined to merge with 'setCellsProcess'
+bool pmChipSelectCell(pmChip *chip, int cellNum, bool exclusive)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+
+    psArray *cells = chip->cells;       // Component cells
+    if (!cells || cellNum > cells->n) {
+        return false;
+    }
+
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];
+        if (!cell) {
+            continue;
+        }
+        if (i == cellNum) {
+            cell->process = true;
+        } else {
+            if (exclusive) {
+                cell->process = false;
+            }
+        }
+    }
+    return true;
+}
+
+
+int pmFPAExcludeChip(pmFPA *fpa, int chipNum)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, -1);
+
+    psArray *chips = fpa->chips;        // Component chips
+    if (chips == NULL) {
+        psWarning("WARNING: fpa->chips == NULL\n");
+        return(0);
+    }
+    if ((chipNum >= chips->n) || (NULL == (pmChip *) chips->data[chipNum])) {
+        psWarning("WARNING: the specified chip (%d) does not exist.\n", chipNum);
+        return(0);
+    }
+
+    int numChips = 0;                   // Number of chips to be processed
+    for (int i = 0 ; i < chips->n ; i++) {
+        pmChip *tmpChip = (pmChip *) chips->data[i]; // Chip of interest
+        if (tmpChip != NULL) {
+            if (i == chipNum) {
+                tmpChip->process = false;
+                setCellsProcess(tmpChip, false); // Wipe out the cell as well
+            } else if (tmpChip->process) {
+                numChips++;
+            }
+        }
+    }
+
+    return(numChips);
+}
+
+
+int pmChipExcludeCell(pmChip *chip, int cellNum)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, -1);
+
+    psArray *cells = chip->cells;       // The component cells
+    if (!cells || cellNum > cells->n) {
+        return 0;
+    }
+
+    int numCells = 0;                   // Number of cells to be processed
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];
+        if (!cell) {
+            continue;
+        }
+        if (i == cellNum) {
+            cell->process = false;
+        } else {
+            numCells++;
+        }
+    }
+
+    return numCells;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAFlags.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAFlags.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAFlags.h	(revision 22322)
@@ -0,0 +1,122 @@
+/*  @file pmFPAFlags.h
+ *  @brief Functions for setting and checking the status flags within the FPA hierarchy
+ * 
+ *  @author George Gusciora, MHPCC
+ *  @author Paul Price, IfA
+ *  @author Eugene Magnier, IfA
+ * 
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-05-03 20:04:01 $
+ *  Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_FLAGS_H
+#define PM_FPA_FLAGS_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+// Functions to turn on/off the file_exists flags
+
+/// Set the file_exists flag for an FPA and components
+bool pmFPASetFileStatus(pmFPA *fpa,     ///< FPA for which to set status
+                        bool status     ///< Status to set
+                       );
+
+/// Set the file_exists flag for a chip and components
+bool pmChipSetFileStatus(pmChip *chip,  ///< Chip for which to set status
+                         bool status    ///< Status to set
+                        );
+
+/// Set the file_exists flag for a cell and components
+bool pmCellSetFileStatus(pmCell *cell,  ///< Cell for which to set status
+                         bool status    ///< Status to set
+                        );
+
+// Functions to check the file_exists flags
+
+/// Return the file_exists status for an FPA and components
+bool pmFPACheckFileStatus(const pmFPA *fpa ///< FPA for which to check status
+                         );
+
+/// Return the file_exists status for a chip and components
+bool pmChipCheckFileStatus(const pmChip *chip ///< Chip for which to check status
+                          );
+
+/// Return the file_exists status for a chip and components
+bool pmCellCheckFileStatus(const pmCell *cell ///< Cell for which to check status
+                          );
+
+// Functions to turn on/off the data_exists flags
+
+/// Set the data_exists flag for an FPA and components
+bool pmFPASetDataStatus(pmFPA *fpa,     ///< FPA for which to set status
+                        bool status     ///< Status to set
+                       );
+
+/// Set the data_exists flag for a chip and components
+bool pmChipSetDataStatus(pmChip *chip,  ///< Chip for which to set status
+                         bool status    ///< Status to set
+                        );
+
+/// Set the data_exists flag for a cell and components
+bool pmCellSetDataStatus(pmCell *cell,  ///< Cell for which to set status
+                         bool status    ///< Status to set
+                        );
+
+
+// Functions the check the data_exists flags
+
+/// Check data_exists for the element of this fpa at this view
+bool pmFPAviewCheckDataStatus (const pmFPA *fpa, ///< FPA to check
+			       const pmFPAview *view ///< check for this view 
+  );
+
+/// Check data_exists for this fpa
+bool pmFPACheckDataStatus (const pmFPA *fpa ///< FPA to check
+  );
+
+/// Check data_exists for this chip
+bool pmChipCheckDataStatus (const pmChip *chip ///< Chip to check
+);
+
+/// Check data_exists for this cell
+bool pmCellCheckDataStatus (const pmCell *cell ///< Cell to check
+);
+
+/// Check data_exists for this readout
+bool pmReadoutCheckDataStatus (const pmReadout *readout ///< Readout to check
+);
+
+
+// Functions to set the process flags
+
+/// Select a chip within an FPA for processing
+///
+/// If exclusive is true, the specified chip is the only chip to be processed.  A negative value for chipNum
+/// is valid and, in combinations with exclusive, de-selects all chips.
+bool pmFPASelectChip(pmFPA *fpa,        ///< FPA containing the chip of interest
+                     int chipNum,       ///< Chip number to select
+                     bool exclusive     ///< Process this chip exclusive of the others?
+                    );
+
+/// Select a chip within a chip for processing
+///
+/// If exclusive is true, the specified cell is the only chip to be processed.  A negative value for cellNum
+/// is valid and, in combinations with exclusive, de-selects all chips.
+bool pmChipSelectCell(pmChip *chip,     ///< Chip containing the cell of interest
+                      int cellNum,      ///< Cell number to select
+                      bool exclusive    ///< Process this cell exclusive of the others?
+                     );
+
+/// Exclude a chip within an FPA from processing
+int pmFPAExcludeChip(pmFPA *fpa,        ///< FPA containing the chip of interest
+                     int chipNum        ///< Chip number to exclude
+                    );
+
+/// Exclude a cell within a chip from processing
+int pmChipExcludeCell(pmChip *chip,     ///< Chip containing the chip of interest
+                      int cellNum       ///< Cell number to exclude
+                     );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAHeader.c	(revision 22322)
@@ -0,0 +1,77 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmConcepts.h"
+#include "pmFPAHeader.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmCellReadHeader(pmCell *cell, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    if (!cell->hdu) {
+        return pmChipReadHeader(cell->parent, fits, config);
+    }
+    if (!pmHDUReadHeader(cell->hdu, fits)) {
+        psError(PS_ERR_IO, false, "Unable to read header for cell.\n");
+        return false;
+    }
+
+    return pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_DATABASE, false, config);
+}
+
+
+bool pmChipReadHeader(pmChip *chip, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    if (!chip->hdu) {
+        return pmFPAReadHeader(chip->parent, fits, config);
+    }
+    if (!pmHDUReadHeader(chip->hdu, fits)) {
+        psError(PS_ERR_IO, false, "Unable to read header for cell.\n");
+        return false;
+    }
+
+    if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_DATABASE, true, true, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read concepts for chip.\n");
+        return false;
+    }
+
+    return true;
+}
+
+
+bool pmFPAReadHeader(pmFPA *fpa, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    if (!fpa->hdu) {
+        return false;
+    }
+    if (!pmHDUReadHeader(fpa->hdu, fits)) {
+        psError(PS_ERR_IO, false, "Unable to read header for cell.\n");
+        return false;
+    }
+
+    if (!pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_DATABASE, true, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read concepts for FPA.\n");
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAHeader.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAHeader.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAHeader.h	(revision 22322)
@@ -0,0 +1,44 @@
+/*  @file pmFPAHeader.h
+ *  @brief Functions read FITS headers for FPA components
+ *
+ *  @author Paul Price, IfA
+ *
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-06-17 22:16:38 $
+ *  Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_HEADER_H
+#define PM_FPA_HEADER_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Read the FITS header (and ingest concepts) for an FPA, if it exists at this level
+///
+/// Returns false if there was a problem.  Returns true if it successfully read the header, or if the header
+/// was already there.  No iteration to lower levels is performed.
+bool pmFPAReadHeader(pmFPA *fpa,        ///< FPA for which to read header
+                     psFits *fits,       ///< FITS file handle
+                     pmConfig *config   ///< Configuration
+                    );
+
+/// Read the FITS header (and ingest concepts) for a chip, if it exists at this level
+///
+/// Returns false if there was a problem.  Returns true if it successfully read the header, or if the header
+/// was already there.  No iteration to lower levels is performed.
+bool pmChipReadHeader(pmChip *chip,     ///< Chip for which to read header
+                      psFits *fits,      ///< FITS file handle
+                     pmConfig *config   ///< Configuration
+                     );
+
+/// Read the FITS header (and ingest concepts) for a cell, if it exists at this level
+///
+/// Returns false if there was a problem.  Returns true if it successfully read the header, or if the header
+/// was already there.  No iteration to lower levels is performed.
+bool pmCellReadHeader(pmCell *cell,     ///< Cell for which to read header
+                      psFits *fits,      ///< FITS file handle
+                     pmConfig *config   ///< Configuration
+                     );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPALevel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPALevel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPALevel.c	(revision 22322)
@@ -0,0 +1,67 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <strings.h>
+#include <pslib.h>
+
+#include "pmFPALevel.h"
+
+static const char *nameNONE = "NONE";   ///< Name for PM_FPA_LEVEL_NONE
+static const char *nameFPA = "FPA";     ///< Name for PM_FPA_LEVEL_FPA
+static const char *nameCHIP = "CHIP";   ///< Name for PM_FPA_LEVEL_CHIP
+static const char *nameCELL = "CELL";   ///< Name for PM_FPA_LEVEL_CELL
+static const char *nameREADOUT = "READOUT"; ///< Name for PM_FPA_LEVEL_READOUT
+static const char *nameCHUNK = "CHUNK"; ///< Name for PM_FPA_LEVEL_CHUNK
+
+const char *pmFPALevelToName(pmFPALevel level)
+{
+    switch (level) {
+    case PM_FPA_LEVEL_NONE:
+        return nameNONE;
+    case PM_FPA_LEVEL_FPA:
+        return nameFPA;
+    case PM_FPA_LEVEL_CHIP:
+        return nameCHIP;
+    case PM_FPA_LEVEL_CELL:
+        return nameCELL;
+    case PM_FPA_LEVEL_READOUT:
+        return nameREADOUT;
+    case PM_FPA_LEVEL_CHUNK:
+        return nameCHUNK;
+    default:
+        psAbort("You can't get here; level = %d", level);
+    }
+    return NULL;
+}
+
+pmFPALevel pmFPALevelFromName(const char *name)
+{
+    if (name == NULL) {
+        return PM_FPA_LEVEL_NONE;
+    }
+    if (!strcasecmp(name, nameFPA)) {
+        return PM_FPA_LEVEL_FPA;
+    }
+    if (!strcasecmp(name, nameCHIP)) {
+        return PM_FPA_LEVEL_CHIP;
+    }
+    if (!strcasecmp(name, nameCELL)) {
+        return PM_FPA_LEVEL_CELL;
+    }
+    if (!strcasecmp(name, nameREADOUT)) {
+        return PM_FPA_LEVEL_READOUT;
+    }
+    if (!strcasecmp(name, nameCHUNK)) {
+        return PM_FPA_LEVEL_CHUNK;
+    }
+    if (!strcasecmp(name, nameNONE)) {
+        return PM_FPA_LEVEL_NONE;
+    }
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised FPA level name: %s", name);
+    return PM_FPA_LEVEL_NONE;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPALevel.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPALevel.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPALevel.h	(revision 22322)
@@ -0,0 +1,37 @@
+/* @file pmFPALevel.h
+ * @brief Defines enum and string representations for the FPA levels
+ *
+ * @author Eugene Magnier, IfA
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-02-07 00:10:08 $
+ * Copyright 2005-2008 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_LEVEL_H
+#define PM_FPA_LEVEL_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Specify the level of the FPA hierarchy
+typedef enum {
+    PM_FPA_LEVEL_NONE,                  ///< No particular level specified
+    PM_FPA_LEVEL_FPA,                   ///< Level corresponds to an FPA
+    PM_FPA_LEVEL_CHIP,                  ///< Level corresponds to a Chip
+    PM_FPA_LEVEL_CELL,                  ///< Level corresponds to a Cell
+    PM_FPA_LEVEL_READOUT,               ///< Level corresponds to a Readout
+    PM_FPA_LEVEL_CHUNK,                 ///< Level corresponds to a chunk
+} pmFPALevel;
+
+
+/// Return the string representation of the FPA level
+const char *pmFPALevelToName(pmFPALevel level ///< Level enum
+                            );
+
+/// Return the enum representation of the FPA level
+pmFPALevel pmFPALevelFromName(const char *name ///< Level name
+                             );
+/// @}
+#endif // #ifndef PM_FPA_LEVEL_H
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMaskWeight.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMaskWeight.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMaskWeight.c	(revision 22322)
@@ -0,0 +1,513 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "pmHDUGenerate.h"
+#include "pmFPAMaskWeight.h"
+
+#define PIXELS_BUFFER 100               // Size of buffer for allocating pixel lists
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static (private) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Create the parent mask images that reside in the HDU
+static void createParentMasks(pmHDU *hdu // The HDU for which to create
+                             )
+{
+    assert(hdu);
+    assert(hdu->images);
+
+    // Generate the parent mask images
+    psArray *images = hdu->images;      // Array of images
+    psArray *masks = hdu->masks;        // Array of masks
+    if (!masks) {
+        masks = psArrayAlloc(images->n);
+        hdu->masks = masks;
+    }
+
+    for (long i = 0; i < images->n; i++) {
+        psImage *image = images->data[i]; // The image for this readout
+        if (!image || masks->data[i]) {
+            continue;
+        }
+        masks->data[i] = psImageAlloc(image->numCols, image->numRows, PS_TYPE_U8);
+        psImageInit(masks->data[i], 0);
+    }
+
+    return;
+}
+
+// Create the parent weight images that reside in the HDU
+static void createParentWeights(pmHDU *hdu // The HDU for which to create
+                               )
+{
+    assert(hdu);
+    assert(hdu->images);
+
+    // Generate the parent mask images
+    psArray *images = hdu->images;      // Array of images
+    psArray *weights = hdu->weights;    // Array of weight images
+    if (!weights) {
+        weights = psArrayAlloc(images->n);
+        hdu->weights = weights;
+    }
+
+    for (long i = 0; i < images->n; i++) {
+        psImage *image = images->data[i]; // The image for this readout
+        if (!image || weights->data[i]) {
+            continue;
+        }
+        weights->data[i] = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
+    }
+
+    return;
+}
+
+// Identify a readout within the HDU, on the basis of the image pointer.
+// This is a little dirty, but hopefully should work....
+static long identifyReadout(pmHDU *hdu, // The HDU containing the readouts
+                            pmReadout *readout // The readout to be identified
+                           )
+{
+    assert(hdu);
+    assert(readout);
+
+    long index = -1;                    // Index of the readout
+    for (long i = 0; i < hdu->images->n && index == -1; i++) {
+        if (hdu->images->data[i] == readout->image->parent) {
+            index = i;
+        }
+    }
+
+    return index;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmReadoutSetMask(pmReadout *readout, psMaskType satMask, psMaskType badMask)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_IMAGE_NON_NULL(readout->image, false);
+
+    pmCell *cell = readout->parent;     // The parent cell
+    bool mdok = true;                   // Status of MD lookup
+
+    // Get the "concepts" of interest
+    float saturation = psMetadataLookupF32(&mdok, cell->concepts, "CELL.SATURATION"); // Saturation level
+    if (!mdok || isnan(saturation)) {
+        psError(PS_ERR_IO, true, "CELL.SATURATION is not set --- unable to set mask.\n");
+        return false;
+    }
+    float bad = psMetadataLookupF32(&mdok, cell->concepts, "CELL.BAD"); // Bad level
+    if (!mdok || isnan(bad)) {
+        psError(PS_ERR_IO, true, "CELL.BAD is not set --- unable to set mask.\n");
+        return false;
+    }
+    psTrace("psModules.camera", 5, "Saturation: %f, bad: %f\n", saturation, bad);
+
+
+    // Set up the mask
+    psImage *image = readout->image;    // The image pixels
+    if (!readout->mask) {
+        // Generate a (throwaway) mask image, if required
+        readout->mask = psImageAlloc(image->numCols, image->numRows, PS_TYPE_U8);
+    }
+    psImage *mask = readout->mask;      // The mask pixels
+    psImageInit(mask, 0);
+
+    // Dereference pointers for speed
+    psF32 **imageData = image->data.F32;// The image
+    psU8  **maskData  = mask->data.U8;  // The mask
+
+    for (int i = 0; i < image->numRows; i++) {
+        for (int j = 0; j < image->numCols; j++) {
+            if (imageData[i][j] >= saturation) {
+                maskData[i][j] |= satMask;
+            }
+            if (imageData[i][j] <= bad) {
+                maskData[i][j] |= badMask;
+            }
+            if (!isfinite(imageData[i][j])) {
+                maskData[i][j] |= badMask;
+            }
+        }
+    }
+
+    return true;
+}
+
+// XXX this function creates the mask pixels, or uses the existing mask
+// pixels.  currently, it will set mask bits if (value <= BAD) or (value >= SATURATION)
+// should we optionally ignore these tests?
+bool pmReadoutGenerateMask(pmReadout *readout, psMaskType satMask, psMaskType badMask)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+
+    pmCell *cell = readout->parent;     // The parent cell
+    bool mdok = true;                   // Status of MD lookup
+
+    // Create the mask image if required
+    if (!readout->mask) {
+        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+        if (!mdok || psRegionIsNaN(*trimsec)) {
+            psError(PS_ERR_IO, true, "CELL.TRIMSEC is not set --- unable to set mask.\n");
+            return false;
+        }
+
+        pmHDU *hdu = pmHDUFromCell(cell);   // The HDU containing the cell's pixels
+        PS_ASSERT_PTR_NON_NULL(hdu, false);
+        if (!hdu->images && !pmHDUGenerateForCell(cell)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate HDU for cell.\n");
+            return false;
+        }
+
+        createParentMasks(hdu);
+
+        // Need to identify which readout we're working with....
+        long index = identifyReadout(hdu, readout); // Index of the readout
+        if (index == -1) {
+            psError(PS_ERR_UNKNOWN, true, "Unable to identify readout image in HDU.\n");
+            return false;
+        }
+
+        psImage *mask = psImageSubset(hdu->masks->data[index], *trimsec); // The mask pixels
+        if (!mask) {
+            psString trimsecString = psRegionToString(*trimsec);
+            psError(PS_ERR_UNKNOWN, false, "Unable to set mask from HDU with trimsec: %s.\n", trimsecString);
+            psFree(trimsecString);
+            return false;
+        }
+        psImageInit(mask, 0);
+        assert (readout->mask == NULL); // or else this is a memory leak.
+        readout->mask = mask;
+    }
+
+    return pmReadoutSetMask(readout, satMask, badMask);
+}
+
+bool pmReadoutSetWeight(pmReadout *readout, bool poisson)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+
+    pmCell *cell = readout->parent;     // The parent cell
+    bool mdok = true;                   // Status of MD lookup
+
+    // Get the "concepts" of interest
+    float gain = psMetadataLookupF32(&mdok, cell->concepts, "CELL.GAIN"); // Cell gain
+    if (!mdok || isnan(gain)) {
+        psError(PS_ERR_IO, true, "CELL.GAIN is not set --- unable to set weight.\n");
+        return false;
+    }
+    float readnoise = psMetadataLookupF32(&mdok, cell->concepts, "CELL.READNOISE"); // Cell read noise
+    if (!mdok || isnan(readnoise)) {
+        psError(PS_ERR_IO, true, "CELL.READNOISE is not set --- unable to set weight.\n");
+        return false;
+    }
+
+    if (poisson) {
+        // Set weight image to the variance in ADU = f/g + rn^2
+        psImage *image = readout->image;    // The image pixels
+        readout->weight = (psImage*)psBinaryOp(readout->weight, image, "/", psScalarAlloc(gain, PS_TYPE_F32));
+
+        // a negative weight is non-sensical. if the image value drops below 1, the weight must be 1.
+        readout->weight = (psImage*)psUnaryOp(readout->weight, readout->weight, "abs");
+        readout->weight = (psImage*)psBinaryOp(readout->weight, readout->weight, "max",
+                                               psScalarAlloc(1, PS_TYPE_F32));
+    } else {
+        // Just use the read noise
+        if (!readout->weight) {
+            readout->weight = psImageAlloc(readout->image->numCols, readout->image->numRows, PS_TYPE_F32);
+        }
+        psImageInit(readout->weight, 0.0);
+    }
+
+    readout->weight = (psImage*)psBinaryOp(readout->weight, readout->weight, "+",
+                                           psScalarAlloc(readnoise*readnoise/gain/gain, PS_TYPE_F32));
+
+    return true;
+}
+
+// this function creates the weight pixels, or uses the existing weight pixels.  it will set
+// the noise pixel values only if the weight image is not supplied
+bool pmReadoutGenerateWeight(pmReadout *readout, bool poisson)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+
+    pmCell *cell = readout->parent;     // The parent cell
+    bool mdok = true;                   // Status of MD lookup
+
+    // Create the weight image if required
+    if (readout->weight)
+        return true;
+
+    psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+    if (!mdok || psRegionIsNaN(*trimsec)) {
+        psError(PS_ERR_IO, true, "CELL.TRIMSEC is not set --- unable to set weight.\n");
+        return false;
+    }
+
+    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU containing the cell's pixels
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    if (!hdu->images && !pmHDUGenerateForCell(cell)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate HDU for cell.\n");
+        return false;
+    }
+
+    createParentWeights(hdu);
+
+    // Need to identify which readout we're working with....
+    long index = identifyReadout(hdu, readout); // Index of the readout
+    if (index == -1) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to identify readout image in HDU.\n");
+        return false;
+    }
+
+    psImage *weight = psImageSubset(hdu->weights->data[index], *trimsec); // The weight pixels
+    if (!weight) {
+        psString trimsecString = psRegionToString(*trimsec);
+        psError(PS_ERR_UNKNOWN, false, "Unable to set weight from HDU with trimsec: %s.\n",
+                trimsecString);
+        psFree(trimsecString);
+        return false;
+    }
+    psImageInit(weight, 0);
+    readout->weight = weight;
+
+    return pmReadoutSetWeight(readout, poisson);
+}
+
+bool pmReadoutGenerateMaskWeight(pmReadout *readout, psMaskType satMask, psMaskType badMask, bool poisson)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+
+    bool success = true;                // Was everything successful?
+
+    success &= pmReadoutGenerateMask(readout, satMask, badMask);
+    success &= pmReadoutGenerateWeight(readout, poisson);
+
+    return success;
+}
+
+bool pmCellGenerateMaskWeight(pmCell *cell, psMaskType satMask, psMaskType badMask, bool poisson)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+
+    bool success = true;                // Was everything successful?
+    psArray *readouts = cell->readouts; // Array of readouts
+    for (int i = 0; i < readouts->n; i++) {
+        pmReadout *readout = readouts->data[i]; // The readout
+        success &= pmReadoutGenerateMaskWeight(readout, poisson, satMask, badMask);
+    }
+
+    return success;
+}
+
+
+bool pmReadoutWeightRenorm(const pmReadout *readout, psMaskType maskVal, psStatsOptions meanStat,
+                           psStatsOptions stdevStat, int width, psRandom *rng)
+{
+    PM_ASSERT_READOUT_NON_NULL(readout, false);
+    PM_ASSERT_READOUT_IMAGE(readout, false);
+    PM_ASSERT_READOUT_WEIGHT(readout, false);
+    PS_ASSERT_INT_POSITIVE(width, false);
+
+    if (!psMemIncrRefCounter(rng)) {
+        rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    }
+
+    psImage *image = readout->image, *mask = readout->mask, *weight = readout->weight; // Readout images
+    int numCols = image->numCols, numRows = image->numRows; // Size of images
+    int xNum = numCols / width + 1, yNum = numRows / width + 1; // Number of renormalisation regions
+    float xSize = numCols / (float)xNum, ySize = numRows / (float)yNum; // Size of renormalisation regions
+
+    psStats *meanStats = psStatsAlloc(meanStat), *stdevStats = psStatsAlloc(stdevStat); // Statistics
+    psVector *buffer = NULL;
+
+    for (int j = 0; j < yNum; j++) {
+        // Bounds in y
+        int yMin = j * ySize;
+        int yMax = (j + 1) * ySize;
+        for (int i = 0; i < xNum; i++) {
+            // Bounds in x
+            int xMin = i * xSize;
+            int xMax = (i + 1) * xSize;
+
+            psRegion region = psRegionSet(xMin, xMax, yMin, yMax); // Region of interest
+            psImage *subImage = psImageSubset(image, region); // Sub-image of the image pixels
+            psImage *subWeight = psImageSubset(weight, region); // Sub image of the weight pixels
+            psImage *subMask = mask ? psImageSubset(mask, region) : NULL; // Sub-image of the mask pixels
+
+            if (!psImageBackground(stdevStats, &buffer, subImage, subMask, maskVal, rng) ||
+                !psImageBackground(meanStats, &buffer, subWeight, subMask, maskVal, rng)) {
+                // Nothing we can do about it, but don't want to keel over and die, so do our best to flag it.
+                psString regionStr = psRegionToString(region); // String with region
+                psWarning("Unable to measure statistics over %s", regionStr);
+                psFree(regionStr);
+                psErrorClear();
+                psImageInit(subWeight, NAN);
+                if (subMask) {
+                    psImageInit(subMask, maskVal);
+                }
+            } else {
+                double meanVar = psStatsGetValue(meanStats, meanStat); // Mean of variance map
+                double stdev = psStatsGetValue(stdevStats, stdevStat); // Standard deviation of image
+                psTrace("psModules.camera", 3,
+                        "Region [%d:%d,%d:%d] has variance %lf, but mean of variance map is %lf\n",
+                        xMin, xMax, yMin, yMax, PS_SQR(stdev), meanVar);
+                psBinaryOp(subWeight, subWeight, "*", psScalarAlloc(PS_SQR(stdev) / meanVar, PS_TYPE_F32));
+            }
+
+            psFree(subImage);
+            psFree(subWeight);
+            psFree(subMask);
+        }
+    }
+    psFree(meanStats);
+    psFree(stdevStats);
+    psFree(rng);
+    psFree(buffer);
+
+    return true;
+}
+
+
+bool pmReadoutMaskNonfinite(pmReadout *readout, psMaskType maskVal)
+{
+    PM_ASSERT_READOUT_NON_NULL(readout, false);
+    PM_ASSERT_READOUT_IMAGE(readout, false);
+
+    psImage *image = readout->image;    // Readout's image
+    psImage *weight = readout->weight;  // Readout's weight
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+
+    if (!readout->mask) {
+        readout->mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+    }
+    psImage *mask = readout->mask;      // Readout's mask
+
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            if (!isfinite(image->data.F32[y][x]) || (weight && !isfinite(weight->data.F32[y][x]))) {
+                mask->data.PS_TYPE_MASK_DATA[y][x] |= maskVal;
+            }
+        }
+    }
+
+    return true;
+}
+
+
+
+bool pmReadoutMaskApply(pmReadout *readout, psMaskType maskVal)
+{
+    PM_ASSERT_READOUT_NON_NULL(readout, false);
+    PM_ASSERT_READOUT_IMAGE(readout, false);
+    PM_ASSERT_READOUT_MASK(readout, false);
+
+    int numCols = readout->image->numCols, numRows = readout->image->numRows; // Size of image
+    psMaskType **maskData = readout->mask->data.PS_TYPE_MASK_DATA; // Dereference mask
+    psF32 **imageData = readout->image->data.F32;// Dereference image
+    psF32 **weightData = readout->weight ? readout->weight->data.F32 : NULL; // Dereference weight map
+
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            if (maskData[y][x] & maskVal) {
+                imageData[y][x] = NAN;
+                if (weightData) {
+                    weightData[y][x] = NAN;
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+
+bool pmReadoutInterpolateBadPixels(pmReadout *readout, psMaskType maskVal, psImageInterpolateMode mode,
+                                   float poorFrac, psMaskType maskPoor, psMaskType maskBad)
+{
+    PM_ASSERT_READOUT_NON_NULL(readout, false);
+    PM_ASSERT_READOUT_IMAGE(readout, false);
+    PM_ASSERT_READOUT_MASK(readout, false);
+    if (!maskVal) {
+        return true;
+    }
+
+    psImage *image = readout->image;    // Image
+    psImage *mask = readout->mask;      // Mask
+    psImage *weight = readout->weight;  // Weight map
+
+    psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(mode, image, weight, mask, maskVal,
+                                                                       NAN, NAN, maskBad, maskPoor, poorFrac);
+    interp->shifting = false;           // Turn off "exact shifts" so we get proper interpolation
+
+    int numCols = mask->numCols, numRows = mask->numRows; // Size of image
+
+    psPixels *pixels = psPixelsAllocEmpty(PIXELS_BUFFER); // Pixels that have been interpolated
+    psVector *imagePix = psVectorAllocEmpty(PIXELS_BUFFER, PS_TYPE_F32); // Corresponding values for image
+    psVector *weightPix = psVectorAllocEmpty(PIXELS_BUFFER, PS_TYPE_F32); // Corresponding values for weight
+    psVector *maskPix = psVectorAllocEmpty(PIXELS_BUFFER, PS_TYPE_MASK); // Corresponding values for mask
+
+    long numBad = 0;                    // Number of bad pixels interpolated
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            if (mask->data.PS_TYPE_MASK_DATA[y][x] & maskVal) {
+                double imageValue, weightValue; // Image and weight value from interpolation
+                psMaskType maskValue = 0; // Mask value from interpolation
+                psImageInterpolateStatus status = psImageInterpolate(&imageValue, &weightValue, &maskValue,
+                                                                     x, y, interp);
+                if (status == PS_INTERPOLATE_STATUS_ERROR || status == PS_INTERPOLATE_STATUS_OFF) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to interpolate readout at %d,%d", x, y);
+                    psFree(interp);
+                    psFree(pixels);
+                    psFree(imagePix);
+                    psFree(weightPix);
+                    psFree(maskPix);
+                    return false;
+                }
+                if (status == PS_INTERPOLATE_STATUS_BAD) {
+                    // It's still bad: couldn't interpolate enough
+                    continue;
+                }
+
+                pixels = psPixelsAdd(pixels, PIXELS_BUFFER, x, y);
+                imagePix = psVectorExtend(imagePix, PIXELS_BUFFER, 1);
+                weightPix = psVectorExtend(weightPix, PIXELS_BUFFER, 1);
+                maskPix = psVectorExtend(maskPix, PIXELS_BUFFER, 1);
+                imagePix->data.F32[numBad] = imageValue;
+                weightPix->data.F32[numBad] = weightValue;
+                maskPix->data.PS_TYPE_MASK_DATA[numBad] = maskValue;
+                numBad++;
+            }
+        }
+    }
+
+    psFree(interp);
+
+    for (long i = 0; i < numBad; i++) {
+        int x = pixels->data[i].x, y = pixels->data[i].y; // Coordinates of pixel
+        image->data.F32[y][x] = imagePix->data.F32[i];
+        weight->data.F32[y][x] = weightPix->data.F32[i];
+        mask->data.PS_TYPE_MASK_DATA[y][x] = maskPix->data.PS_TYPE_MASK_DATA[i];
+    }
+
+    psFree(pixels);
+    psFree(imagePix);
+    psFree(weightPix);
+    psFree(maskPix);
+
+    psLogMsg("psModules.camera", PS_LOG_INFO, "Interpolated over %ld pixels", numBad);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMaskWeight.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMaskWeight.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMaskWeight.h	(revision 22322)
@@ -0,0 +1,137 @@
+/* @file pmFPAHeader.h
+ * @brief Functions read FITS headers for FPA components
+ *
+ * @author Paul Price, IfA
+ * @author Eugene Magnier, IfA
+ *
+ * @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-22 22:25:22 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_MASK_WEIGHT_H
+#define PM_FPA_MASK_WEIGHT_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+#if 0
+/// Pixel mask values
+typedef enum {
+    PM_MASK_CLEAR    = 0x00,            ///< The pixel is not masked
+    PM_MASK_BLANK    = 0x01,            ///< The pixel is blank or has no (valid) data
+    PM_MASK_FLAT     = 0x02,            ///< The pixel is non-positive in the flat-field
+    PM_MASK_DETECTOR = 0x02,            ///< The detector pixel is bad (e.g., bad column, charge trap)
+    PM_MASK_SAT      = 0x04,            ///< The pixel is saturated in the image of interest
+    PM_MASK_BAD      = 0x04,            ///< The pixel is low in the image of interest
+    PM_MASK_RANGE    = 0x04,            ///< The pixel is out of range in the image of interest
+    PM_MASK_CR       = 0x08,            ///< The pixel is probably a CR
+    PM_MASK_SPARE1   = 0x10,            ///< Spare mask value
+    PM_MASK_SPARE2   = 0x20,            ///< Spare mask value
+    PM_MASK_SUSPECT  = 0x40,            ///< The pixel is suspected of being bad, but may not be
+    PM_MASK_MARK     = 0x80,            ///< The pixel is marked as temporarily ignored
+} pmMaskValue;
+#define PM_MASK_MARK 0x80            ///< The pixel is marked as temporarily ignored
+#define PM_MASK_SAT  0x04            ///< The pixel is saturated in the image of interest
+#endif
+
+/// Set a temporary readout mask using CELL.SATURATION and CELL.BAD
+///
+/// Identifies pixels that are saturated (>= CELL.SATURATION) or bad (<= CELL.BAD).  The mask that is produced
+/// within the readout is temporary --- it is not added to the HDU.  This is intended for when the user is
+/// iterating using pmReadoutReadNext, in which case the HDU can't be generated.
+bool pmReadoutSetMask(pmReadout *readout, ///< Readout for which to set mask
+                      psMaskType satMask, ///< Mask value to give saturated pixels
+                      psMaskType badMask  ///< Mask value to give bad (low) pixels
+    );
+
+
+/// Set a temporary readout weight map using CELL.GAIN and CELL.READNOISE
+///
+/// Calculates weights (actually variances) for each pixel using photon statistics and the cell gain
+/// (CELL.GAIN) and read noise (CELL.READNOISE).  The weight map that is produced within the readout is
+/// temporary --- it is not added to the HDU.  This is intended for when the user is iterating using
+/// pmReadoutReadNext, in which case the HDU can't be generated.
+bool pmReadoutSetWeight(pmReadout *readout, ///< Readout for which to set weight
+                        bool poisson    ///< Use poisson weights (in addition to read noise)?
+                       );
+
+/// Generate a readout mask (suitable for output) using CELL.SATURATION and CELL.BAD
+///
+/// Identifies pixels that are saturated (>= CELL.SATURATION) or bad (<= CELL.BAD).  The mask that is produced
+/// is suitable for output (complete with HDU entry).  This is intended for most operations.
+bool pmReadoutGenerateMask(pmReadout *readout, ///< Readout for which to generate mask
+                           psMaskType sat, ///< Mask value to give saturated pixels
+                           psMaskType bad ///< Mask value to give bad (low) pixels
+    );
+
+/// Generate a weight map (suitable for output) using CELL.GAIN and CELL.READNOISE
+///
+/// Calculates weights (actually variances) for each pixel using photon statistics and the cell gain
+/// (CELL.GAIN) and read noise (CELL.READNOISE).  The weight map that is produced within the readout is
+/// suitable for output (complete with HDU entry).  This is intended for most operations.
+bool pmReadoutGenerateWeight(pmReadout *readout, ///< Readout for which to generate weight
+                             bool poisson    ///< Use poisson weights (in addition to read noise)?
+                            );
+
+/// Generate mask and weight map for a readout
+///
+/// Calls pmReadoutGenerateMask and pmReadoutGenerateWeight for the readout
+bool pmReadoutGenerateMaskWeight(pmReadout *readout, ///< Readout for which to generate mask and weights
+                                 psMaskType sat, ///< Mask value to give saturated pixels
+                                 psMaskType bad, ///< Mask value to give bad (low) pixels
+                                 bool poisson ///< Use poisson weights (in addition to read noise)?
+                                );
+
+/// Generate mask and weight maps for all readouts within a cell
+///
+/// Calls pmReadoutGenerateMaskWeight for each readout within the cell.
+bool pmCellGenerateMaskWeight(pmCell *cell, ///< Cell for which to generate mask and weights
+                              psMaskType sat, ///< Mask value to give saturated pixels
+                              psMaskType bad, ///< Mask value to give bad (low) pixels
+                              bool poisson ///< Use poisson weights (in addition to read noise)?
+                             );
+
+/// Renormalise the weight map to match the actual variance
+///
+/// The variance in the image is measured in patches, and the variance map is adjusted so that the mean for
+/// that patch corresponds.
+bool pmReadoutWeightRenorm(const pmReadout *readout, // Readout to normalise
+                           psMaskType maskVal, // Value to mask
+                           psStatsOptions meanStat, // Statistic to measure the mean (of the variance map)
+                           psStatsOptions stdevStat, // Statistic to measure the stdev (of the image)
+                           int width,   // Width of patch (pixels)
+                           psRandom *rng // Random number generator (for sub-sampling images)
+    );
+
+/// Explicitly mask non-finite pixels
+///
+/// Since unmasked non-finite pixels can occur (e.g., by out-of-range in quantisation), it is sometimes
+/// necessary to mask them explicitly.  Non-finite pixels in the image or weight have their mask OR-ed with
+/// the provided value.
+bool pmReadoutMaskNonfinite(pmReadout *readout, ///< Readout to mask
+                            psMaskType maskVal ///< Mask value to give non-finite pixels
+    );
+
+/// Apply a mask to the image and weight map
+///
+/// Unfortunately, image subtraction may result in a bi-modal image in masked areas, which can upset image
+/// statistics (very important for quantising images so that a product can be written out!).  This function
+/// sets masked areas to NAN in the image and weight.
+bool pmReadoutMaskApply(pmReadout *readout, ///< Readout to mask
+                        psMaskType maskVal ///< Mask value for which to apply mask
+    );
+
+/// Interpolate over bad pixels
+///
+/// Scan the mask image for bad pixels, and interpolate over them using the nominated options
+bool pmReadoutInterpolateBadPixels(pmReadout *readout, ///< Readout to work on
+                                   psMaskType maskVal, ///< Value to mask
+                                   psImageInterpolateMode mode, ///< Interpolation mode
+                                   float poorFrac, ///< Maximum bad fraction of kernel for "poor" status
+                                   psMaskType maskPoor, ///< Mask value to give poor pixels
+                                   psMaskType maskBad ///< Mask value to give bad pixels
+    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMosaic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMosaic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMosaic.c	(revision 22322)
@@ -0,0 +1,1359 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAFlags.h"
+#include "pmConceptsAverage.h"
+#include "pmHDUUtils.h"
+#include "pmConfig.h"
+#include "pmAstrometryWCS.h"
+#include "pmFPAExtent.h"
+
+#include "pmFPAMosaic.h"
+
+
+#define CELL_LIST_BUFFER 10             // Buffer size for cell lists
+
+#define BLANK_VALUE 0.0                 // Value for pixels that are blank in the mosaicked image (e.g., //
+                                        // between cells).
+                                        // XXX This should ultimately be set to NAN, but psphot doesn't like
+                                        // that (masking needs to be more thorough).
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static (private) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Do two regions overlap?
+#define REGIONS_OVERLAP(region1, region2) \
+((region1->x0 > region2->x0 && region1->x0 < region2->x1) || \
+ (region1->x1 > region2->x0 && region1->x1 < region2->x1) || \
+ (region1->y0 > region2->y0 && region1->y0 < region2->y1) || \
+ (region1->y1 > region2->y0 && region1->y1 < region2->y1))
+
+// Compare a value with a maximum and minimum
+#define COMPARE(value,min,max) \
+if ((value) < (min)) { \
+    (min) = (value); \
+} \
+if ((value) > (max)) { \
+    (max) = (value); \
+}
+
+// Update a concept to the assumed value
+#define FIX_CONCEPT(SOURCE, NAME, TYPE, VALUE) \
+psMetadataItem *item = psMetadataLookup(SOURCE, NAME); \
+item->data.TYPE = VALUE;
+
+// Get the bounds for an chip's pixels on the HDU
+static bool chipBounds(psRegion *bounds, // The bounds for the chip
+                       const pmChip *chip // The chip to examine for contiguity
+                      )
+{
+    assert(chip);
+
+    psArray *cells = chip->cells;       // The array of cells
+    bool mdok = true;                   // Status of MD lookup
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+        if (!mdok || !trimsec || psRegionIsNaN(*trimsec)) {
+            psError(PS_ERR_UNKNOWN, true, "CELL.TRIMSEC hasn't been set for cell %d.\n", i);
+            return false;
+        }
+
+        if (trimsec->x0 < bounds->x0) {
+            bounds->x0 = trimsec->x0;
+        }
+        if (trimsec->x1 > bounds->x1) {
+            bounds->x1 = trimsec->x1;
+        }
+        if (trimsec->y0 < bounds->y0) {
+            bounds->y0 = trimsec->y0;
+        }
+        if (trimsec->y1 > bounds->y1) {
+            bounds->y1 = trimsec->y1;
+        }
+    }
+
+    return true;
+}
+
+// Make sure the TRIMSEC doesn't overlap with the established image bounds
+static bool chipContiguousTrimsec(psRegion *bounds, // The bounds of the image, altered if primary==true
+                                  const pmChip *chip // The chip to examine for contiguity
+                                 )
+{
+    assert(bounds);
+    assert(chip);
+
+    psArray *cells = chip->cells;       // The array of cells
+    bool mdok = true;                   // Status of MD lookup
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+        if (!mdok || !trimsec || psRegionIsNaN(*trimsec)) {
+            psError(PS_ERR_UNKNOWN, true, "CELL.TRIMSEC hasn't been set for cell %d.\n", i);
+            return false;
+        }
+
+        if (REGIONS_OVERLAP(trimsec, bounds)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// Make sure the BIASSEC doesn't overlap with the established image bounds
+static bool chipContiguousBiassec(psRegion *bounds, // The bounds of the image, altered if primary==true
+                                  const pmChip *chip // The chip to examine for contiguity
+                                 )
+{
+    assert(bounds);
+    assert(chip);
+
+    // Check that the biases don't get in the way
+    psArray *cells = chip->cells;       // The array of cells
+    bool mdok = true;                   // Status of MD lookup
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
+        if (!mdok || !biassecs) {
+            psError(PS_ERR_UNKNOWN, true, "CELL.BIASSEC hasn't been set for cell %d.\n", i);
+            return false;
+        }
+        if (biassecs->n == 0) {
+            // No point allocating an iterator if there's nothing there to iterate on
+            continue;
+        }
+        psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
+        psRegion *biassec = NULL;       // Bias section from iteration
+        while ((biassec = psListGetAndIncrement(biassecsIter))) {
+            if (psRegionIsNaN(*biassec)) {
+                continue;
+            }
+            if (REGIONS_OVERLAP(biassec, bounds)) {
+                psFree(biassecsIter);
+                return false;
+            }
+        }
+        psFree(biassecsIter);
+    }
+
+    // If we've gotten this far, everything is fine.
+    return true;
+}
+
+// Are the pixels for the FPA contiguous on the HDU?
+// Work this out by examining all the CELL.TRIMSEC and CELL.BIASSEC regions for the component cells
+static bool fpaContiguous(psRegion *bounds, // The bounds of the image, returned
+                          const pmFPA *fpa // The FPA to examine for contiguity
+                         )
+{
+    assert(bounds);
+    assert(fpa);
+
+    *bounds = psRegionSet(INFINITY, 0, INFINITY, 0);
+
+    // Get the size of the pixels on the HDU
+    psArray *chips = fpa->chips;        // The array of chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // Chip of interest
+        if (!chipBounds(bounds, chip)) {
+            return false;
+        }
+    }
+
+    // Make sure the bias regions don't get in the way of the HDU
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // Chip of interest
+        if (!chipContiguousBiassec(bounds, chip)) {
+            return false;
+        }
+    }
+
+    // If we got through it all, they must all be contiguous
+    return true;
+}
+
+
+
+// Check a cell for niceness in the parity and binning
+static bool niceCellParityBinning(int *xBin, int *yBin, // Binning for cell, to be returned
+                                  const pmCell *cell // Cell to check for niceness
+                                 )
+{
+    assert(xBin);
+    assert(yBin);
+    assert(cell);
+
+    // A "nice" cell must have only a single readout
+    if (cell->readouts->n != 1) {
+        return false;
+    }
+
+    // A "nice" cell must have parity == 1
+    bool mdok = true;                   // Status of MD lookup
+    int xParity = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XPARITY"); // Parity in x
+    if (!mdok || xParity == 0) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.XPARITY hasn't been set for cell.\n");
+        return false;
+    }
+    if (xParity != 1) {
+        return false;
+    }
+    int yParity = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YPARITY"); // Parity in y
+    if (!mdok || yParity == 0) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.YPARITY hasn't been set for cell.\n");
+        return false;
+    }
+    if (yParity != 1) {
+        return false;
+    }
+
+    // A "nice" cell must have consistent binning
+    int xBinCell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XBIN"); // Binning in x
+    if (!mdok || xBin <= 0) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.XBIN hasn't been set for cell.\n");
+        return false;
+    }
+    int yBinCell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YBIN"); // Binning in y
+    if (!mdok || yBin <= 0) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.YBIN hasn't been set for cell.\n");
+        return false;
+    }
+    if (*xBin == 0 || *yBin == 0) {
+        *xBin = xBinCell;
+        *yBin = yBinCell;
+    } else if (xBinCell != *xBin || yBinCell != *yBin) {
+        return false;
+    }
+
+    return true;
+}
+
+
+// Check a cell for niceness in the boundaries
+static bool niceCellBounds(const pmCell *cell, // Cell to check for niceness
+                           const psRegion *imageBounds // Bounds of the image on the HDU
+                          )
+{
+    // A "nice" cell must have the (0,0) pixel at CELL.X0,CELL.Y0
+    bool mdok = true;                   // Status of MD lookup
+    int x0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0"); // Position of (0,0) on chip
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.X0 hasn't been set for cell.\n");
+        return false;
+    }
+    int y0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0"); // Position of (0,0) on chip
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.Y0 hasn't been set for cell.\n");
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[0]; // A representative readout
+    if (!readout) {
+        return false;                   // Nothing here
+    }
+    if (x0 != readout->col0 + readout->image->col0 - (int)imageBounds->x0 ||
+            y0 != readout->row0 + readout->image->row0 - (int)imageBounds->y0) {
+        psTrace("psModules.camera", 5, "CELL.X0,Y0 don't match: %d,%d vs %d,%d\n", x0, y0,
+                readout->col0 + readout->image->col0 - (int)imageBounds->x0,
+                readout->row0 + readout->image->row0 - (int)imageBounds->y0);
+        return false;
+    }
+
+    return true;
+}
+
+
+// Is the chip "nice"?  If so, return the region containing the chip pixels
+static psRegion *niceChip(int *xBinChip, int *yBinChip, // Binning for chip, to be returned
+                          const pmChip *chip // Chip to examine for "niceness".
+                         )
+{
+    assert(xBinChip);
+    assert(yBinChip);
+    assert(chip);
+
+    // Check that we've got the HDU in the chip or the FPA
+    if ((!chip->hdu || !chip->hdu->images) && (!chip->parent->hdu || !chip->parent->hdu->images)) {
+        return NULL;
+    }
+
+    // Check parity and binning for component cells
+    *xBinChip = 0;
+    *yBinChip = 0;
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i]; // The cell of interest
+        if (!niceCellParityBinning(xBinChip, yBinChip, cell)) {
+            return NULL;
+        }
+    }
+
+    // Now check that the pixels are all contiguous
+    psRegion *imageBounds = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Bound of image on HDU
+    if (!chipBounds(imageBounds, chip) || !chipContiguousBiassec(imageBounds, chip)) {
+        psTrace("psModules.camera", 5, "Image isn't contiguous.\n");
+        psFree(imageBounds);
+        return NULL;
+    }
+
+    psString region = psRegionToString(*imageBounds);
+    psTrace("psModules.camera", 7, "Image bounds: %s\n", region);
+    psFree(region);
+
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i]; // The cell of interest
+        if (!niceCellBounds(cell, imageBounds)) {
+            psFree(imageBounds);
+            return NULL;
+        }
+    }
+
+    // Need to check all the other chips if the HDU is in the FPA
+    pmFPA *fpa = chip->parent;          // The parent FPA
+    if (fpa->hdu && fpa->hdu->images) {
+        psArray *chips = fpa->chips;    // Array of chips
+        for (int i = 0; i < chips->n; i++) {
+            pmChip *testChip = chips->data[i]; // The chip of interest
+            if (testChip == chip) {
+                // Already done this one
+                continue;
+            }
+            if (!chipContiguousTrimsec(imageBounds, testChip) ||
+                    !chipContiguousBiassec(imageBounds, testChip)) {
+                psTrace("psModules.camera", 5, "Image isn't contiguous.\n");
+                psFree(imageBounds);
+                return NULL;
+            }
+        }
+    }
+
+    return imageBounds;
+}
+
+// Is the FPA "nice"?  If so, return the region containing the FPA pixels
+static psRegion *niceFPA(int *xBinFPA, int *yBinFPA, // Binning for FPA, to be returned
+                         const pmFPA *fpa  // FPA to examine for "niceness".
+                        )
+{
+    assert(xBinFPA);
+    assert(yBinFPA);
+    assert(fpa);
+
+    // Check that we've got the HDU in the chip or the FPA
+    if (!fpa->hdu || !fpa->hdu->images) {
+        return NULL;
+    }
+
+    // Check parity and binning for component cells
+    *xBinFPA = 0;
+    *yBinFPA = 0;
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i]; // The chip of interest
+        for (int j = 0; i < chip->cells->n; i++) {
+            pmCell *cell = chip->cells->data[j]; // The cell of interest
+            if (!niceCellParityBinning(xBinFPA, yBinFPA, cell)) {
+                return NULL;
+            }
+        }
+    }
+
+    // Now check that the pixels are all contiguous
+    psRegion *imageBounds = psRegionAlloc(0, 0, 0, 0); // Bound of image on HDU
+    if (!fpaContiguous(imageBounds, fpa)) {
+        psTrace("psModules.camera", 5, "Image isn't contiguous.\n");
+        psFree(imageBounds);
+        return NULL;
+    }
+
+    psString region = psRegionToString(*imageBounds);
+    psTrace("psModules.camera", 7, "Image bounds: %s\n", region);
+    psFree(region);
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i]; // The chip of interest
+        for (int j = 0; i < chip->cells->n; i++) {
+            pmCell *cell = chip->cells->data[j]; // The cell of interest
+            if (!niceCellBounds(cell, imageBounds)) {
+                psFree(imageBounds);
+                return NULL;
+            }
+        }
+    }
+
+    return imageBounds;
+}
+
+// supporting macros used by imageMosaic()
+// copy pixels without binning
+#define COPY_WITH_PARITY_DIFFERENCE(TYPE) \
+        case PS_TYPE_##TYPE: { \
+                for (int y = 0; y < image->numRows; y++) { \
+                    int yTarget =  yTargetBase + yParity * y; \
+                    for (int x = 0; x < image->numCols; x++) { \
+                        int xTarget = xTargetBase + xParity * x; \
+                        mosaic->data.TYPE[yTarget][xTarget] = image->data.TYPE[y][x]; \
+                    } \
+                } \
+            } \
+            break;
+
+// In case the original image is binned but the mosaic is not, we need to fill in the values in
+// the mosaic.  this operation should be replaced with a call to one of the functions defined
+// in psImageBinning
+#define FILL_IN(TYPE) \
+        case PS_TYPE_##TYPE: \
+            for (int y = 0; y < image->numRows; y++) { \
+                float yTargetBinBase = yTargetBase + yParity * yBinSource->data.S32[i] * y / yBinTarget; \
+                for (int x = 0; x < image->numCols; x++) { \
+                    float xTargetBinBase = xTargetBase + xParity * xBinSource->data.S32[i] * x / xBinTarget; \
+                    for (int j = 0; j < yBinSource->data.S32[i]; j++) { \
+                        int yTarget = (int)(yTargetBinBase + yParity * (float)j / (float)yBinTarget); \
+                        for (int k = 0; k < xBinSource->data.S32[i]; k++) { \
+                            int xTarget = (int)(xTargetBinBase + xParity * (float)k / (float)xBinTarget); \
+                            mosaic->data.TYPE[yTarget][xTarget] = image->data.TYPE[y][x]; \
+                        } \
+                    } \
+                } \
+            } \
+            break;
+
+// Mosaic multiple images, with flips, binning and offsets
+static psImage *imageMosaic(const psArray *source, // Images to splice in
+                            const psVector *xFlip, const psVector *yFlip, // Need to flip x and y?
+                            const psVector *xBinSource, // Binning in x of source images
+                            const psVector *yBinSource, // Binning in y of source images
+                            int xBinTarget, int yBinTarget, // Binning in x and y of target images
+                            const psVector *x0, const psVector *y0, // Offsets for source images on target
+                            double unexposed // Value for unexposed pixels
+                           )
+{
+    assert(source);
+    assert(xFlip && xFlip->type.type == PS_TYPE_U8);
+    assert(yFlip && yFlip->type.type == PS_TYPE_U8);
+    assert(xBinSource && xBinSource->type.type == PS_TYPE_S32);
+    assert(yBinSource && yBinSource->type.type == PS_TYPE_S32);
+    assert(x0 && x0->type.type == PS_TYPE_S32);
+    assert(y0 && y0->type.type == PS_TYPE_S32);
+    assert(xFlip->n == source->n);
+    assert(yFlip->n == source->n);
+    assert(xBinSource->n == source->n);
+    assert(yBinSource->n == source->n);
+    assert(x0->n == source->n);
+    assert(y0->n == source->n);
+
+    if (source->n == 0) {
+        return NULL;
+    }
+
+    // Get the maximum extent of the mosaic image
+    int xMin = +INT_MAX;
+    int xMax = -INT_MAX;
+    int yMin = +INT_MAX;
+    int yMax = -INT_MAX;
+    psElemType type = 0;
+    int numImages = 0;                  // Number of images
+    psTrace("psModules.camera", 3, "Mosaicking %ld cells.\n", source->n);
+    for (int i = 0; i < source->n; i++) {
+        psImage *image = source->data[i]; // The image of interest
+        if (!image) {
+            continue;
+        }
+        numImages++;
+
+        // Only implemented for F32 and U8 images so far.
+        assert(image->type.type == PS_TYPE_F32 || image->type.type == PS_TYPE_U8);
+        // All input types must be the same
+        if (type == 0) {
+            type = image->type.type;
+        }
+        assert(type == image->type.type);
+
+        // Size of cell in x and y
+        int xParity = xFlip->data.U8[i] ? -1 : 1;
+        int yParity = yFlip->data.U8[i] ? -1 : 1;
+        psTrace("psModules.camera", 5, "Extent of cell %d: %d -> %d , %d -> %d\n", i, x0->data.S32[i],
+                x0->data.S32[i] + xParity * xBinSource->data.S32[i] * image->numCols, y0->data.S32[i],
+                y0->data.S32[i] + yParity * yBinSource->data.S32[i] * image->numRows);
+
+        COMPARE(x0->data.S32[i], xMin, xMax);
+        COMPARE(y0->data.S32[i], yMin, yMax);
+        // Subtract the parity to get the inclusive limit (not exclusive)
+        COMPARE(x0->data.S32[i] + xParity * xBinSource->data.S32[i] * image->numCols - xParity, xMin, xMax);
+        COMPARE(y0->data.S32[i] + yParity * yBinSource->data.S32[i] * image->numRows - yParity, yMin, yMax);
+    }
+    if (numImages == 0) {
+        return NULL;
+    }
+
+    // Set up the image
+    // Since both upper and lower values are inclusive, we need to add one to the size
+    float xSize = (float)(xMax - xMin + 1) / (float)xBinTarget;
+    if (xSize - (int)xSize > 0) {
+        xSize += 1;
+    }
+    float ySize = (float)(yMax - yMin + 1) / (float)yBinTarget;
+    if (ySize - (int)ySize > 0) {
+        ySize += 1;
+    }
+
+    psTrace("psModules.camera", 3, "Spliced image will be %dx%d\n", (int)xSize, (int)ySize);
+    psImage *mosaic = psImageAlloc((int)xSize, (int)ySize, type); // The mosaic image
+    psImageInit(mosaic, unexposed);
+
+    // Next pass through the images to do the mosaicking
+    // XXX this function uses summing for the output: is this the right choice?
+    for (int i = 0; i < source->n; i++) {
+        psImage *image = source->data[i]; // The image of interest
+        if (!image) {
+            continue;
+        }
+        int xParity = xFlip->data.U8[i] ? -1 : 1; // Parity difference, in x
+        int yParity = yFlip->data.U8[i] ? -1 : 1; // Parity difference, in y
+        int xTargetBase = (x0->data.S32[i] - xMin) / xBinTarget; // The base x position in the target frame
+        int yTargetBase = (y0->data.S32[i] - yMin) / yBinTarget; // The base y position in the target frame
+
+        // in the first case, we are just copy a section pixel-by-pixel
+        if ((xBinSource->data.S32[i] == xBinTarget) &&
+            (yBinSource->data.S32[i] == yBinTarget) &&
+            (xFlip->data.U8[i] == 0) &&
+            (yFlip->data.U8[i] == 0)) {
+            // Let someone else do the hard work
+            psImageOverlaySection(mosaic, image, xTargetBase, yTargetBase, "=");
+            continue;
+        }
+
+        // in the second case, there's a difference with the parities, but we don't have to
+        // worry about binning
+        if (xBinSource->data.S32[i] == xBinTarget && yBinSource->data.S32[i] == yBinTarget) {
+            switch (type) {
+                COPY_WITH_PARITY_DIFFERENCE(F32);
+                COPY_WITH_PARITY_DIFFERENCE(U8);
+              default:
+                psAbort("Should never get here.\n");
+            }
+            continue;
+        }
+
+        // In the third case, the images are flipped and have different binnnig.
+        // We have to do all of the hard work ourselves
+        switch (type) {
+            FILL_IN(F32);
+            FILL_IN(U8);
+          default:
+            psAbort("Should never get here.\n");
+        }
+    } // Iterating over images
+
+    return mosaic;
+}
+
+// Add a cell and its various properties to the arrays
+static bool addCell(psArray *images,    // Array of images
+                    psArray *masks,     // Array of masks
+                    psArray *weights,   // Array of weights
+                    psVector *x0,       // Array of X0
+                    psVector *y0,       // Array of Y0
+                    psVector *xBin,     // Array of XBIN
+                    psVector *yBin,     // Array of YBIN
+                    psVector *xFlip,    // Array indicating whether x axis should be flipped
+                    psVector *yFlip,    // Array indicating whether y axis should be flipped
+                    const pmCell *cell, // Cell to add
+                    int *xBinMin,       // The minimum x binning, returned
+                    int *yBinMin,       // The minimum y binning, returned
+                    bool chipStuff,      // Worry about chip stuff as well?
+                    int x0Target, int y0Target, // Target x0 and y0 offsets
+                    int xParityTarget, int yParityTarget // Target parities
+                   )
+{
+    if (!cell) {
+        return false;
+    }
+
+    if (cell->readouts->n > 1) {
+        psWarning("Cell contains more than one readout (%ld) --- mosaicking only the first.\n",
+                  cell->readouts->n);
+    }
+
+    // Expand the arrays and vectors to handle new data
+    long index = images->n;               // The index to use
+    if (images->n == images->nalloc) {
+        images  = psArrayRealloc(images,  index + CELL_LIST_BUFFER);
+        masks   = psArrayRealloc(masks,   index + CELL_LIST_BUFFER);
+        weights = psArrayRealloc(weights, index + CELL_LIST_BUFFER);
+        x0    = psVectorRealloc(x0,    index+ CELL_LIST_BUFFER);
+        y0    = psVectorRealloc(y0,    index+ CELL_LIST_BUFFER);
+        xBin  = psVectorRealloc(xBin,  index+ CELL_LIST_BUFFER);
+        yBin  = psVectorRealloc(yBin,  index+ CELL_LIST_BUFFER);
+        xFlip = psVectorRealloc(xFlip, index+ CELL_LIST_BUFFER);
+        yFlip = psVectorRealloc(yFlip, index+ CELL_LIST_BUFFER);
+    }
+
+    images->n = index + 1;
+    masks->n = index + 1;
+    weights->n = index + 1;
+    x0->n = index + 1;
+    y0->n = index + 1;
+    xBin->n = index + 1;
+    yBin->n = index + 1;
+    xFlip->n = index + 1;
+    yFlip->n = index + 1;
+
+    bool mdok = true;                   // Status of MD lookup
+    bool good = true;                   // Is everything good?
+
+    // Offset of the cell on the chip
+    int x0Cell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0");
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.X0 for cell is not set.\n");
+        good = false;
+    }
+    int y0Cell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0");
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.Y0 for cell is not set.\n");
+        good = false;
+    }
+    psTrace("psModules.camera", 5, "Cell %ld: x0=%d y0=%d\n", index, x0Cell, y0Cell);
+
+    // Offset of the chip on the FPA
+    int x0Chip = 0, y0Chip = 0;
+    if (chipStuff) {
+        pmChip *chip = cell->parent;    // The parent chip
+        if (!chip) {
+            psError(PS_ERR_UNKNOWN, true, "Cell has no parent chip --- can't find CHIP.X0 and CHIP.Y0\n");
+            good = false;
+        }
+        x0Chip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.X0");
+        if (!mdok) {
+            psError(PS_ERR_UNKNOWN, true, "CHIP.X0 for chip is not set.\n");
+            good = false;
+        }
+        y0Chip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.Y0");
+        if (!mdok) {
+            psError(PS_ERR_UNKNOWN, true, "CHIP.Y0 for chip is not set.\n");
+            good = false;
+        }
+    }
+
+    // Binning
+    xBin->data.S32[index] = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XBIN");
+    if (!mdok || xBin->data.S32[index] == 0) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.XBIN for cell is not set.\n");
+        return false;
+    } else if (xBin->data.S32[index] < *xBinMin) {
+        *xBinMin = xBin->data.S32[index];
+    }
+    yBin->data.S32[index] = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YBIN");
+    if (!mdok || yBin->data.S32[index] == 0) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.YBIN for cell is not set.\n");
+        return false;
+    } else if (yBin->data.S32[index] < *yBinMin) {
+        *yBinMin = yBin->data.S32[index];
+    }
+
+    // Do we need to flip?
+    int xParityCell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XPARITY");
+    if (!mdok || (xParityCell != 1 && xParityCell != -1)) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.XPARITY for cell is not set.\n");
+        return false;
+    }
+    int yParityCell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YPARITY");
+    if (!mdok || (yParityCell != 1 && yParityCell != -1)) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.YPARITY for cell is not set.\n");
+        return false;
+    }
+
+    // Parity of the chip on the FPA
+    int xParityChip = 1, yParityChip = 1;
+    if (chipStuff) {
+        pmChip *chip = cell->parent;    // The parent chip
+        xParityChip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.XPARITY");
+        if (!mdok || (xParityChip != 1 && xParityChip != -1)) {
+            psError(PS_ERR_UNKNOWN, true, "CHIP.XPARITY for chip is not set.\n");
+            return false;
+        }
+        yParityChip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.YPARITY");
+        if (!mdok || (yParityChip != 1 && yParityChip != -1)) {
+            psError(PS_ERR_UNKNOWN, true, "CHIP.YPARITY for chip is not set.\n");
+            return false;
+        }
+    }
+
+    // Set the flips on the basis of the parity
+    // XXX if (level == CHIP) : only apply Cell parity
+    // XXX if (level == FPA) : apply Chip & Cell parity
+    if (xParityCell * xParityChip == xParityTarget) {
+        xFlip->data.U8[index] = 0;
+    } else {
+        xFlip->data.U8[index] = 1;
+    }
+    if (yParityCell * yParityChip == yParityTarget) {
+        yFlip->data.U8[index] = 0;
+    } else {
+        yFlip->data.U8[index] = 1;
+    }
+
+    x0->data.S32[index] = x0Chip + x0Cell - x0Target;
+    y0->data.S32[index] = y0Chip + y0Cell - y0Target;
+
+    // Add the readout to the array of images to be mosaicked
+    psArray *readouts = cell->readouts; // The array of readouts
+    pmReadout *readout = readouts->data[0]; // The only readout we'll bother with
+
+    // The images to put into the mosaic
+    images->data[index]  = psMemIncrRefCounter(readout->image);
+    weights->data[index] = psMemIncrRefCounter(readout->weight);
+    masks->data[index]   = psMemIncrRefCounter(readout->mask);
+
+    psTrace("psModules.camera", 9, "Added cell (%p) %ld: %d,%d; %d,%d, %d,%d.\n", cell, index,
+            x0->data.S32[index], y0->data.S32[index], xBin->data.S32[index], yBin->data.S32[index],
+            xFlip->data.U8[index], yFlip->data.U8[index]);
+
+    return true;
+}
+
+
+// Mosaic together the cells in a chip
+static bool chipMosaic(psImage **mosaicImage, // The mosaic image, to be returned
+                       psImage **mosaicMask, // The mosaic mask, to be returned
+                       psImage **mosaicWeights, // The mosaic weights, to be returned
+                       int *xBinChip, int *yBinChip, // The binning in x and y, to be returned
+                       const pmChip *chip, // Chip to mosaic
+                       const pmCell *targetCell, // Cell to which to mosaic
+                       psMaskType blank // Mask value to give blank pixels
+                      )
+{
+    assert(mosaicImage);
+    assert(mosaicMask);
+    assert(mosaicWeights);
+    assert(xBinChip);
+    assert(yBinChip);
+    assert(chip);
+    assert(targetCell);
+
+    psArray *images = psArrayAlloc(0); // Array of images that will be mosaicked
+    psArray *weights = psArrayAlloc(0); // Array of weight images to be mosaicked
+    psArray *masks = psArrayAlloc(0); // Array of mask images to be mosaicked
+    psVector *x0 = psVectorAlloc(0, PS_TYPE_S32); // Origin x coordinates
+    psVector *y0 = psVectorAlloc(0, PS_TYPE_S32); // Origin y coordinates
+    psVector *xBin = psVectorAlloc(0, PS_TYPE_S32); // Binning in x
+    psVector *yBin = psVectorAlloc(0, PS_TYPE_S32); // Binning in y
+    psVector *xFlip = psVectorAlloc(0, PS_TYPE_U8); // Flip in x?
+    psVector *yFlip = psVectorAlloc(0, PS_TYPE_U8); // Flip in y?
+
+    // Get the target characteristics
+    bool mdok = true;                   // Status of MD lookup
+    int x0Target = psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.X0");
+    if (!mdok) {
+        psWarning("CELL.X0 is not set for the target cell; assuming 0.\n");
+        FIX_CONCEPT(targetCell->concepts, "CELL.X0", S32, 0);
+    }
+    int y0Target = psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.Y0");
+    if (!mdok) {
+        psWarning("CELL.Y0 is not set for the target cell; assuming 0.\n");
+        FIX_CONCEPT(targetCell->concepts, "CELL.Y0", S32, 0);
+    }
+    int xParityTarget = psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.XPARITY");
+    if (!mdok || (xParityTarget != -1 && xParityTarget != 1)) {
+        psWarning("CELL.XPARITY is not set for the target cell; assuming 1.\n");
+        FIX_CONCEPT(targetCell->concepts, "CELL.XPARITY", S32, 1);
+        xParityTarget = 1;
+    }
+    int yParityTarget = psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.YPARITY");
+    if (!mdok || (yParityTarget != -1 && yParityTarget != 1)) {
+        psWarning("CELL.YPARITY is not set for the target cell; assuming 1.\n");
+        FIX_CONCEPT(targetCell->concepts, "CELL.YPARITY", S32, 1);
+        yParityTarget = 1;
+    }
+
+    // Binning for the mosaicked chip is the minimum binning allowed by the cells
+    *xBinChip = INT_MAX;
+    *yBinChip = INT_MAX;
+
+    // Set up the required inputs
+    bool allGood = true;                // Is everything good, well-behaved?
+    psArray *cells = chip->cells;       // The array of cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // The cell of interest
+        if (!cell || !cell->data_exists) {
+            continue;
+        }
+        allGood &= addCell(images, masks, weights, x0, y0, xBin, yBin, xFlip, yFlip,
+                           cell, xBinChip, yBinChip, false, x0Target, y0Target,
+                           xParityTarget, yParityTarget);
+    }
+
+    // Check to see if the target has a smaller binning in mind
+    int xBinTarget = psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.XBIN");
+    if (!mdok || xBinTarget == 0) {
+        // CELL.XBIN is not set for the target cell --- assume it's the same as the source
+        FIX_CONCEPT(targetCell->concepts, "CELL.XBIN", S32, *xBinChip);
+    } else {
+        *xBinChip = xBinTarget;
+    }
+    int yBinTarget = psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.YBIN");
+    if (!mdok || yBinTarget == 0) {
+        // CELL.YBIN is not set for the target cell --- assume it's the same as the source
+        FIX_CONCEPT(targetCell->concepts, "CELL.YBIN", S32, *yBinChip);
+    } else {
+        *yBinChip = yBinTarget;
+    }
+
+    // Mosaic the images together and we're done
+    if (allGood) {
+        *mosaicImage = imageMosaic(images, xFlip, yFlip, xBin, yBin, *xBinChip, *yBinChip, x0, y0, BLANK_VALUE);
+        *mosaicWeights = imageMosaic(weights, xFlip, yFlip, xBin, yBin, *xBinChip, *yBinChip, x0, y0, BLANK_VALUE);
+        *mosaicMask = imageMosaic(masks, xFlip, yFlip, xBin, yBin, *xBinChip, *yBinChip, x0, y0, blank);
+    }
+
+    // Clean up
+    psFree(images);
+    psFree(weights);
+    psFree(masks);
+    psFree(xFlip);
+    psFree(yFlip);
+    psFree(xBin);
+    psFree(yBin);
+    psFree(x0);
+    psFree(y0);
+
+    return allGood;
+}
+
+// Mosaic together the cells in a FPA
+static bool fpaMosaic(psImage **mosaicImage, // The mosaic image, to be returned
+                      psImage **mosaicMask, // The mosaic mask, to be returned
+                      psImage **mosaicWeights, // The mosaic weights, to be returned
+                      int *xBinFPA, int *yBinFPA, // The binning in x and y, to be returned
+                      const pmFPA *fpa,  // FPA to mosaic
+                      const pmChip *targetChip, // Chip to which to mosaic
+                      const pmCell *targetCell, // Cell to which to mosaic
+                      psMaskType blank  // Mask value to give blank pixels
+                     )
+{
+    assert(mosaicImage);
+    assert(mosaicMask);
+    assert(mosaicWeights);
+    assert(xBinFPA);
+    assert(yBinFPA);
+    assert(fpa);
+    assert(targetChip);
+    assert(targetCell);
+
+    psArray *images = psArrayAlloc(0); // Array of images that will be mosaicked
+    psArray *weights = psArrayAlloc(0); // Array of weight images to be mosaicked
+    psArray *masks = psArrayAlloc(0); // Array of mask images to be mosaicked
+    psVector *x0 = psVectorAlloc(0, PS_TYPE_S32); // Origin x coordinates
+    psVector *y0 = psVectorAlloc(0, PS_TYPE_S32); // Origin y coordinates
+    psVector *xBin = psVectorAlloc(0, PS_TYPE_S32); // Binning in x
+    psVector *yBin = psVectorAlloc(0, PS_TYPE_S32); // Binning in y
+    psVector *xFlip = psVectorAlloc(0, PS_TYPE_U8); // Flip in x?
+    psVector *yFlip = psVectorAlloc(0, PS_TYPE_U8); // Flip in y?
+
+    // Get the target characteristics
+    bool mdok = true;                   // Status of MD lookup
+    int x0Target = psMetadataLookupS32(&mdok, targetChip->concepts, "CHIP.X0");
+    if (!mdok) {
+        psWarning("CHIP.X0 is not set for the target chip; assuming 0.\n");
+        FIX_CONCEPT(targetChip->concepts, "CHIP.X0", S32, 0);
+    }
+    int y0Target = psMetadataLookupS32(&mdok, targetChip->concepts, "CHIP.Y0");
+    if (!mdok) {
+        psWarning("CHIP.Y0 is not set for the target chip; assuming 0.\n");
+        FIX_CONCEPT(targetChip->concepts, "CHIP.Y0", S32, 0);
+    }
+    x0Target += psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.X0");
+    if (!mdok) {
+        psWarning("CELL.X0 is not set for the target cell; assuming 0.\n");
+        FIX_CONCEPT(targetCell->concepts, "CELL.X0", S32, 0);
+    }
+    y0Target += psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.Y0");
+    if (!mdok) {
+        psWarning("CELL.Y0 is not set for the target cell; assuming 0.\n");
+        FIX_CONCEPT(targetCell->concepts, "CELL.Y0", S32, 0);
+    }
+    int xParityChipTarget = psMetadataLookupS32(&mdok, targetChip->concepts, "CHIP.XPARITY");
+    if (!mdok || (xParityChipTarget != -1 && xParityChipTarget != 1)) {
+        psWarning("CHIP.XPARITY is not set for the target chip; assuming 1.\n");
+        FIX_CONCEPT(targetChip->concepts, "CHIP.XPARITY", S32, 1);
+        xParityChipTarget = 1;
+    }
+    int yParityChipTarget = psMetadataLookupS32(&mdok, targetChip->concepts, "CHIP.YPARITY");
+    if (!mdok || (yParityChipTarget != -1 && yParityChipTarget != 1)) {
+        psWarning("CHIP.YPARITY is not set for the target chip; assuming 1.\n");
+        FIX_CONCEPT(targetChip->concepts, "CHIP.YPARITY", S32, 1);
+        yParityChipTarget = 1;
+    }
+    int xParityCellTarget = psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.XPARITY");
+    if (!mdok || (xParityCellTarget != -1 && xParityCellTarget != 1)) {
+        psWarning("CELL.XPARITY is not set for the target cell; assuming 1.\n");
+        FIX_CONCEPT(targetCell->concepts, "CELL.XPARITY", S32, 1);
+        xParityCellTarget = 1;
+    }
+    int yParityCellTarget = psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.YPARITY");
+    if (!mdok || (yParityCellTarget != -1 && yParityCellTarget != 1)) {
+        psWarning("CELL.YPARITY is not set for the target cell; assuming 1.\n");
+        FIX_CONCEPT(targetCell->concepts, "CELL.YPARITY", S32, 1);
+        yParityCellTarget = 1;
+    }
+    int xParityTarget = xParityChipTarget * xParityCellTarget;
+    int yParityTarget = yParityChipTarget * yParityCellTarget;
+
+    // Binning for the mosaicked chip is the minimum binning allowed by the cells
+    *xBinFPA = INT_MAX;
+    *yBinFPA = INT_MAX;
+
+    // Set up the required inputs
+    bool allGood = true;                // Is everything good, well-behaved?
+    psArray *chips = fpa->chips;        // Array of chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // The chip of interest
+        if (!chip || !chip->data_exists) {
+            continue;
+        }
+        psArray *cells = chip->cells;   // The array of cells
+        for (int j = 0; j < cells->n; j++) {
+            pmCell *cell = cells->data[j];  // The cell of interest
+            if (!cell || !cell->data_exists) {
+                continue;
+            }
+            allGood |= addCell(images, masks, weights, x0, y0, xBin, yBin, xFlip, yFlip,
+                               cell, xBinFPA, yBinFPA, true, x0Target, y0Target,
+                               xParityTarget, yParityTarget);
+        }
+    }
+
+    // Check to see if the target has a smaller binning in mind
+    int xBinTarget = psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.XBIN");
+    if (mdok && xBinTarget != 0) {
+        *xBinFPA = xBinTarget;
+    }
+    int yBinTarget = psMetadataLookupS32(&mdok, targetCell->concepts, "CELL.YBIN");
+    if (mdok && yBinTarget != 0) {
+        *yBinFPA = yBinTarget;
+    }
+
+    // Mosaic the images together and we're done
+    if (allGood) {
+        *mosaicImage = imageMosaic(images, xFlip, yFlip, xBin, yBin, *xBinFPA, *yBinFPA, x0, y0, BLANK_VALUE);
+        *mosaicWeights = imageMosaic(weights, xFlip, yFlip, xBin, yBin, *xBinFPA, *yBinFPA, x0, y0, BLANK_VALUE);
+        *mosaicMask = imageMosaic(masks, xFlip, yFlip, xBin, yBin, *xBinFPA, *yBinFPA, x0, y0, blank);
+    }
+
+    // Clean up
+    psFree(images);
+    psFree(weights);
+    psFree(masks);
+    psFree(xFlip);
+    psFree(yFlip);
+    psFree(xBin);
+    psFree(yBin);
+    psFree(x0);
+    psFree(y0);
+
+    return allGood;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Mosaic all the cells in a chip together.
+//
+// It is desirable to do this without using psImageOverlay (or similar) if it can be at all avoided (because
+// it's really really slow in that case).  There are therefore two cases:
+//
+// 1. The HDU is at the Chip or FPA level.  This is the fast case, and only works if the HDU is "nice", by
+// which I mean:
+//
+//    - the CELL.TRIMSECs are contiguous on the HDU image
+//    - the CELL.PARITYs are identically +1
+//    - the CELL.XBIN and CELL.YBIN are all identical
+//
+// Then we can just use psImageSubset to get the "mosaicked" chip.
+//
+//
+// 2. The HDU is at the cell level, or the above requirements are not met, in which case we mosaic the cells.
+// This is the slow case.  We need to:
+//
+//    - Throw away the bias regions
+//    - Convert all cells to common parity
+//    - Mosaic the cells into an HDU image using CELL.X0 and CELL.Y0
+//    - Update CELL.TRIMSECs
+//
+// Once the demands of case 1 have been met, or case 2 has been performed, then we can create a cell to hold
+// the mosaic image.
+
+bool pmChipMosaic(pmChip *target, const pmChip *source, bool deepCopy, psMaskType blank)
+{
+    // Target exists, and has only a single cell
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(target->cells, false);
+    if (target->cells->n != 1) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Target chip for mosaicking must contain a single cell.\n");
+        return false;
+    }
+    pmCell *targetCell = target->cells->data[0]; // The target cell
+    PS_ASSERT_PTR_NON_NULL(targetCell, false);
+    // Source exists
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+
+    psImage *mosaicImage   = NULL;      // The mosaic image
+    psImage *mosaicMask    = NULL;      // The mosaic mask
+    psImage *mosaicWeights = NULL;      // The mosaic weights
+
+    // Find the HDU
+    psRegion *chipRegion = NULL;        // Region on the HDU that corresponds to the chip
+    int xBin = 0, yBin = 0;             // Binning for the chip mosaic
+    if (!deepCopy && (chipRegion = niceChip(&xBin, &yBin, source))) {
+        // Case 1 --- we need only cut out the region
+        psTrace("psModules.camera", 1, "Case 1 mosaicking: simple cut-out.\n");
+        pmHDU *hdu = source->hdu;       // The HDU that has the pixels
+        if (!hdu || !hdu->images) {
+            hdu = source->parent->hdu;
+        }
+        // force limits to land on chip
+        psRegion bounds = psRegionForImage (hdu->images->data[0], *chipRegion);
+        mosaicImage = psImageSubset(hdu->images->data[0], bounds);
+        if (!mosaicImage) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to select image pixels.\n");
+            return false;
+        }
+        if (hdu->masks) {
+            mosaicMask = psImageSubset(hdu->masks->data[0], bounds);
+            if (!mosaicMask) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to select mask pixels.\n");
+                return false;
+            }
+        }
+        if (hdu->weights) {
+            mosaicWeights = psImageSubset(hdu->weights->data[0], bounds);
+            if (!mosaicWeights) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to select weight pixels.\n");
+                return false;
+            }
+        }
+    } else {
+        // Case 2 --- we need to mosaic by cut and paste
+        psTrace("psModules.camera", 1, "Case 2 mosaicking: cut and paste.\n");
+        if (!chipMosaic(&mosaicImage, &mosaicMask, &mosaicWeights, &xBin, &yBin, source, targetCell, blank)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to mosaic cells.\n");
+            return false;
+        }
+        chipRegion = psRegionAlloc(0, 0, 0, 0); // We've cut and paste, so there's no valid trimsec
+        *chipRegion = psRegionForImage (mosaicImage, *chipRegion);
+    }
+    psTrace("psModules.camera", 1, "xBin,yBin: %d,%d\n", xBin, yBin);
+
+    // Set the concepts for the target cell
+    psList *sourceCells = psArrayToList(source->cells); // List of cells
+    pmConceptsAverageCells(targetCell, sourceCells, chipRegion, NULL, false);
+    {
+        psMetadataItem *item = psMetadataLookup(targetCell->concepts, "CELL.X0");
+        item->data.S32 = 0;
+        item = psMetadataLookup(targetCell->concepts, "CELL.Y0");
+        item->data.S32 = 0;
+        item = psMetadataLookup(targetCell->concepts, "CELL.XBIN");
+        item->data.S32 = xBin;
+        item = psMetadataLookup(targetCell->concepts, "CELL.YBIN");
+        item->data.S32 = yBin;
+    }
+    psFree(sourceCells);
+    psFree(chipRegion);
+
+    // Copy the concepts
+    target->concepts = psMetadataCopy(target->concepts, source->concepts); // Chip level
+    target->parent->concepts = psMetadataCopy(target->parent->concepts, source->parent->concepts); // FPA lvl
+
+    // Now make a new readout to go in the target cell
+    pmReadout *newReadout = pmReadoutAlloc(targetCell); // New readout
+    newReadout->image  = mosaicImage;
+    newReadout->mask   = mosaicMask;
+    newReadout->weight = mosaicWeights;
+    psFree(newReadout);                 // Drop reference
+
+    // Data now exists in the targets
+    pmChipSetDataStatus(target, true);
+    pmCellSetDataStatus(targetCell, true);
+    newReadout->data_exists = true;
+
+    // Update the headers
+    pmHDU *sourceHDU = pmHDUFromChip(source); // The HDU for the source
+    pmHDU *targetHDU = pmHDUFromChip(target); // The HDU for the target
+    targetHDU->header = psMetadataCopy(targetHDU->header, sourceHDU->header);
+    pmHDU *targetPHU = pmHDUGetHighest(target->parent, target, NULL);
+    pmHDU *sourcePHU = pmHDUGetHighest(source->parent, source, NULL);
+
+    // Need to update NAXIS1, NAXIS2 in the target header, so that when we write a CMF, it has the correct
+    // extent.  I'm not convinced that this is the best way to do this, but it should be, at worst, harmless,
+    // since NAXIS[12] will get overwritten for an image with the proper dimensions.
+    psRegion *naxis = pmChipExtent(target);
+    psMetadataAddS32(targetHDU->header, PS_LIST_TAIL, "NAXIS1", PS_META_REPLACE, "Size in x",
+                     naxis->x1 - naxis->x0);
+    psMetadataAddS32(targetHDU->header, PS_LIST_TAIL, "NAXIS2", PS_META_REPLACE, "Size in y",
+                     naxis->y1 - naxis->y0);
+    psFree(naxis);
+
+
+    if (!targetPHU) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find HDU after mosaicking.\n");
+        return false;
+    }
+    if (!targetPHU->header) {
+        // if we don't yet have a header, copy this one.
+        // XXX do we need to create an empty one if the levels do not match??
+        if (true) {
+            targetPHU->header = psMetadataCopy(targetPHU->header, sourcePHU->header);
+        } else {
+            targetPHU->header = psMetadataAlloc();
+        }
+    }
+
+    if (!pmConfigConformHeader(targetPHU->header, targetPHU->format)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to conform header after mosaicking.\n");
+        return false;
+    }
+
+    // If the cells contain the headers, we need to apply the WCS terms from (one of?) the cells
+    int xParityCellTarget = psMetadataLookupS32(NULL, targetCell->concepts, "CELL.XPARITY");
+    if (xParityCellTarget != -1 && xParityCellTarget != 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CELL.XPARITY is not set for target.");
+        return false;
+    }
+    int xParityChipTarget = psMetadataLookupS32(NULL, target->concepts, "CHIP.XPARITY");
+    if (xParityChipTarget != -1 && xParityChipTarget != 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CHIP.XPARITY is not set for target.");
+        return false;
+    }
+    int xParityTarget = xParityCellTarget * xParityChipTarget; // Target parity in x
+
+    int yParityCellTarget = psMetadataLookupS32(NULL, targetCell->concepts, "CELL.YPARITY");
+    if (yParityCellTarget != -1 && yParityCellTarget != 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CELL.YPARITY is not set for target.");
+        return false;
+    }
+    int yParityChipTarget = psMetadataLookupS32(NULL, target->concepts, "CHIP.YPARITY");
+    if (yParityChipTarget != -1 && yParityChipTarget != 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CHIP.YPARITY is not set for target.");
+        return false;
+    }
+    int yParityTarget = yParityCellTarget * yParityChipTarget; // Target parity in y
+
+    for (int i = 0; i < source->cells->n; i++) {
+        pmCell *cell = source->cells->data[i];
+        if (!cell || !cell->hdu || !cell->hdu->header) {
+            continue;
+        }
+
+        pmAstromWCS *wcs = pmAstromWCSfromHeader(cell->hdu->header); // WCS terms for this cell
+        if (!wcs) {
+            psTrace("psModules.camera", 1, "Unable to read cell WCS to generate chip WCS --- ignored.");
+            continue;
+        }
+
+        int xBinCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.XBIN"); // Cell binning in x
+        if (xBinCell == 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CELL.XBIN is not set.");
+            return false;
+        }
+        int xParitySource = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY") *
+            psMetadataLookupS32(NULL, source->concepts, "CHIP.XPARITY"); // Source parity in x
+        if (xParitySource != -1 && xParitySource != 1) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CHIP.XPARITY or CELL.XPARITY is not set for source.");
+            return false;
+        }
+        bool xFlip = (xParitySource == xParityTarget ? false : true); // Flip the x sense of the WCS?
+        int x0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.X0"); // Cell offset in x
+
+        // Modify the wcs terms for the cell offset, binning, and parity
+        float xBinRatio = (float)xBinCell / (float)xBin;
+        if (xFlip) {
+            wcs->crpix1 = x0Cell - wcs->crpix1 * xBinRatio;
+            wcs->cdelt1 *= -1;
+        } else {
+            wcs->crpix1 = x0Cell + wcs->crpix1 * xBinRatio;
+        }
+        wcs->cdelt1 *= xBinRatio;
+
+        int yBinCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.YBIN"); // Cell binning in y
+        if (yBinCell == 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CELL.YBIN is not set.");
+            return false;
+        }
+        int yParitySource = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY") *
+            psMetadataLookupS32(NULL, cell->parent->concepts, "CHIP.YPARITY"); // Source parity in y
+        if (yParitySource != -1 && yParitySource != 1) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CHIP.YPARITY or CELL.YPARITY is not set for source.");
+            return false;
+        }
+        bool yFlip = (yParitySource == yParityTarget ? false : true); // Flip the y sense of the WCS?
+        int y0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0"); // Cell offset in y
+
+        float yBinRatio = (float)yBinCell / (float)yBin;
+        if (yFlip) {
+            wcs->crpix2 = y0Cell - wcs->crpix2 * yBinRatio;
+            wcs->cdelt2 *= -1;
+        } else {
+            wcs->crpix2 = y0Cell + wcs->crpix2 * yBinRatio;
+        }
+        wcs->cdelt2 *= yBinRatio;
+
+        if (!pmAstromWCStoHeader(targetHDU->header, wcs)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate chip WCS from cell WCS.");
+            psFree(wcs);
+            return false;
+        }
+        psFree(wcs);
+
+        // XXX rather than quitting at this point, we could save this wcs structure and compare
+        // its values to the equivalent version from one of the other cells.
+        break;
+    }
+
+    return true;
+}
+
+
+bool pmFPAMosaic(pmFPA *target, const pmFPA *source, bool deepCopy, psMaskType blank)
+{
+    // Target exists, and has only a single chip with single cell
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(target->chips, false);
+    if (target->chips->n != 1) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Target FPA for mosaicking must contain a single chip.\n");
+        return false;
+    }
+    pmChip *targetChip = target->chips->data[0]; // The target chip
+    PS_ASSERT_PTR_NON_NULL(targetChip, false);
+    PS_ASSERT_PTR_NON_NULL(targetChip->cells, false);
+    if (target->chips->n != 1) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Target FPA for mosaicking must contain a single cell.\n");
+        return false;
+    }
+    pmCell *targetCell = targetChip->cells->data[0]; // The target cell
+    PS_ASSERT_PTR_NON_NULL(targetCell, false);
+    // Source exists
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    psImage *mosaicImage   = NULL;      // The mosaic image
+    psImage *mosaicMask    = NULL;      // The mosaic mask
+    psImage *mosaicWeights = NULL;      // The mosaic weights
+
+    // Find the HDU
+    psRegion *fpaRegion = NULL;         // Region on the HDU that corresponds to the FPA
+    int xBin = 0, yBin = 0;             // Binning for the FPA mosaic
+    if (!deepCopy && (fpaRegion = niceFPA(&xBin, &yBin, source))) {
+        // Case 1 --- we need only cut out the region
+        psTrace("psModules.camera", 1, "Case 1 mosaicking: simple cut-out.\n");
+        pmHDU *hdu = source->hdu;         // The HDU that has the pixels
+        mosaicImage = psImageSubset(hdu->images->data[0], *fpaRegion);
+        if (hdu->masks) {
+            mosaicMask = psImageSubset(hdu->masks->data[0], *fpaRegion);
+        }
+        if (hdu->weights) {
+            mosaicWeights = psImageSubset(hdu->weights->data[0], *fpaRegion);
+        }
+    } else {
+        // Case 2 --- we need to mosaic by cut and paste
+        psTrace("psModules.camera", 1, "Case 2 mosaicking: cut and paste.\n");
+        if (!fpaMosaic(&mosaicImage, &mosaicMask, &mosaicWeights, &xBin, &yBin, source,
+                       targetChip, targetCell, blank)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to mosaic chips.\n");
+            return false;
+        }
+        fpaRegion = psRegionAlloc(NAN, NAN, NAN, NAN); // We've cut and paste, so there's no valid trimsec
+    }
+
+    // Set the concepts for the target cell, and add the mosaic in
+    // First we need a list of cells
+    psList *sourceCells = psListAlloc(NULL); // List of source cells
+    psArray *chips = source->chips;        // Array of chips
+    pmChip *firstSourceChip = NULL;     // The first chip in the source FPA; for headers
+    pmCell *firstSourceCell = NULL;     // The first cell in the source FPA; for headers
+    for (long i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // Chip of interest
+        if (!chip || !chip->data_exists) {
+            continue;
+        }
+        psArray *cells = chip->cells;
+        for (long j = 0; j < cells->n; j++) {
+            pmCell *cell = cells->data[j]; // Cell of interest
+            if (!cell || !cell->data_exists) {
+                continue;
+            }
+            psListAdd(sourceCells, PS_LIST_TAIL, cell);
+
+            // These are valid chip and cell to use for the header; grab the first such
+            if (!firstSourceCell && !firstSourceChip) {
+                firstSourceCell = cell;
+                firstSourceChip = chip;
+            }
+        }
+    }
+    pmConceptsAverageCells(targetCell, sourceCells, fpaRegion, NULL, false);
+    {
+        psMetadataItem *item = psMetadataLookup(targetCell->concepts, "CELL.X0");
+        item->data.S32 = 0;
+        item = psMetadataLookup(targetCell->concepts, "CELL.Y0");
+        item->data.S32 = 0;
+        item = psMetadataLookup(targetCell->concepts, "CELL.XBIN");
+        item->data.S32 = xBin;
+        item = psMetadataLookup(targetCell->concepts, "CELL.YBIN");
+        item->data.S32 = yBin;
+    }
+    psFree(sourceCells);
+    psFree(fpaRegion);
+
+    // Currently, there's nothing interesting in the chip concepts that needs to be updated.
+
+    // Copy the concepts for the target FPA
+    target->concepts = psMetadataCopy(target->concepts, source->concepts);
+
+    // Now make a new readout to go in the new cell
+    pmReadout *newReadout = pmReadoutAlloc(targetCell); // New readout
+    newReadout->image  = mosaicImage;
+    newReadout->mask   = mosaicMask;
+    newReadout->weight = mosaicWeights;
+    psFree(newReadout);                 // Drop reference
+
+    // Data now exists in the targets
+    pmChipSetDataStatus(targetChip, true);
+    pmCellSetDataStatus(targetCell, true);
+    newReadout->data_exists = true;
+
+    // Update the headers
+    pmHDU *sourceHDU = pmHDUGetHighest(source, firstSourceChip, firstSourceCell); // The HDU for the source
+    if (!sourceHDU) {
+        psWarning("Unable to find HDU in source FPA; unable to copy headers.\n");
+        return false;
+    }
+    pmHDU *targetHDU = pmHDUGetHighest(target, targetChip, targetCell); // The HDU for the target
+    if (!targetHDU) {
+        psWarning("Unable to find HDU in target FPA; unable to copy headers.\n");
+        return false;
+    }
+
+    if (sourceHDU->header) {
+        targetHDU->header = psMetadataCopy(targetHDU->header, sourceHDU->header);
+    } else if (!targetHDU->header) {
+        targetHDU->header = psMetadataAlloc();
+    }
+
+    if (!pmConfigConformHeader(targetHDU->header, targetHDU->format)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to conform header after mosaicking.\n");
+        return false;
+    }
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMosaic.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMosaic.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAMosaic.h	(revision 22322)
@@ -0,0 +1,41 @@
+/* @file pmFPAMosaic.h
+ * @brief Functions to mosaic FPA components into a single entity
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-06-20 02:22:26 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CHIP_MOSAIC_H
+#define PM_CHIP_MOSAIC_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Mosaic all cells within a chip
+///
+/// Mosaics all cells within the source into a single cell within the target (which must have only a single
+/// cell).  Cells are placed on the chip according to the CELL.X0 and CELL.Y0 offsets.  This is useful for
+/// getting an image of the chip on the sky.  The mosaicking is done so as to avoid performing a deep copy of
+/// the pixels, if possible.
+bool pmChipMosaic(pmChip *target,       ///< Target chip --- may contain only a single cell
+                  const pmChip *source, ///< Source chip whose cells will be mosaicked
+                  bool deepCopy,        ///< Require a deep copy (disregard 'nice' chip)
+                  psMaskType blank      ///< Mask value to give blank pixels
+    );
+
+/// Mosaic all cells within an FPA
+///
+/// Mosaics all cells within the source into a single chip with single cell within the target (which must have
+/// only a single chip with single cell).  Cells are placed on the FPA according to the CHIP.X0, CHIP.Y0,
+/// CELL.X0 and CELL.Y0 offsets.  This is useful for getting an image of the FPA on the sky.  The mosaicking
+/// is done so as to avoid performing a deep copy of the pixels, if possible.
+bool pmFPAMosaic(pmFPA *target, ///< Target FPA --- may contain only a single chip with a single cell
+                 const pmFPA *source,   ///< FPA whose chips and cells will be mosaicked
+                 bool deepCopy,         ///< Require a deep copy (disregard 'nice' chip)
+                 psMaskType blank       ///< Mask value to give blank pixels
+                );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPARead.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPARead.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPARead.c	(revision 22322)
@@ -0,0 +1,1269 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <strings.h>
+#include <assert.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAFlags.h"
+#include "pmHDUUtils.h"
+#include "pmConcepts.h"
+#include "pmFPAHeader.h"
+
+#include "pmFPARead.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Definitions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Specify what to read
+typedef enum {
+    FPA_READ_TYPE_IMAGE,                // Read image
+    FPA_READ_TYPE_MASK,                 // Read mask
+    FPA_READ_TYPE_WEIGHT,               // Read weight map
+    FPA_READ_TYPE_HEADER                // Read header
+} fpaReadType;
+
+// Desired type for pixels; the index corresponds to the fpaReadType, above.
+static psElemType pixelTypes[] = {
+    PS_TYPE_F32,
+    PS_TYPE_MASK,
+    PS_TYPE_F32,
+    0
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Get the "thisXXXScan" value in the readout for the appropriate image type
+static int readoutGetThisScan(pmReadout *readout, // Readout of interest
+                              fpaReadType type // Type of image
+    )
+{
+    switch (type) {
+      case FPA_READ_TYPE_IMAGE:
+        return readout->thisImageScan;
+      case FPA_READ_TYPE_MASK:
+        return readout->thisMaskScan;
+      case FPA_READ_TYPE_WEIGHT:
+        return readout->thisWeightScan;
+      default:
+        psAbort("Unknown read type: %x\n", type);
+    }
+}
+
+// Set the "thisXXXScan" value in the readout for the appropriate image type
+static int readoutSetThisScan(pmReadout *readout, // Readout of interest
+                              fpaReadType type, // Type of image
+                              int thisScan // Starting scan number
+    )
+{
+    switch (type) {
+      case FPA_READ_TYPE_IMAGE:
+        readout->thisImageScan = thisScan;
+        return readout->lastImageScan;
+      case FPA_READ_TYPE_MASK:
+        readout->thisMaskScan = thisScan;
+        return readout->lastMaskScan;
+      case FPA_READ_TYPE_WEIGHT:
+        readout->thisWeightScan = thisScan;
+        return readout->lastWeightScan;
+      default:
+        psAbort("Unknown read type: %x\n", type);
+    }
+    return false;
+}
+
+// Get the "lastXXXScan" value in the readout for the appropriate image type
+static int readoutGetLastScan(pmReadout *readout, // Readout of interest
+                              fpaReadType type // Type of image
+    )
+{
+    switch (type) {
+      case FPA_READ_TYPE_IMAGE:
+        return readout->lastImageScan;
+      case FPA_READ_TYPE_MASK:
+        return readout->lastMaskScan;
+      case FPA_READ_TYPE_WEIGHT:
+        return readout->lastWeightScan;
+      default:
+        psAbort("Unknown read type: %x\n", type);
+    }
+}
+
+// Set the "lastXXXScan" value in the readout for the appropriate image type
+static int readoutSetLastScan(pmReadout *readout, // Readout of interest
+                              fpaReadType type, // Type of image
+                              int lastScan // Last scan number
+    )
+{
+    switch (type) {
+      case FPA_READ_TYPE_IMAGE:
+        readout->lastImageScan = lastScan;
+        return readout->lastImageScan;
+      case FPA_READ_TYPE_MASK:
+        readout->lastMaskScan = lastScan;
+        return readout->lastMaskScan;
+      case FPA_READ_TYPE_WEIGHT:
+        readout->lastWeightScan = lastScan;
+        return readout->lastWeightScan;
+      default:
+        psAbort("Unknown read type: %x\n", type);
+    }
+    return false;
+}
+
+// Return pointer to appropriate image
+static psImage **readoutImageByType(pmReadout *readout, // Readout of interest
+                                    fpaReadType type // Type of image
+    )
+{
+    switch (type) {
+      case FPA_READ_TYPE_IMAGE:
+        return &readout->image;
+      case FPA_READ_TYPE_MASK:
+        return &readout->mask;
+      case FPA_READ_TYPE_WEIGHT:
+        return &readout->weight;
+      default:
+        psAbort("Unknown read type: %x\n", type);
+    }
+}
+
+// Determine number of readouts in the FITS file
+// In the process, reads the header and concepts
+static bool cellNumReadouts(pmCell *cell,    // Cell of interest
+                            psFits *fits,    // FITS file
+                            pmConfig *config // Configuration
+    )
+{
+    assert(cell);
+    assert(fits);
+
+    // Get the HDU and read the header
+    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
+    if (!hdu || hdu->blankPHU) {
+        psError(PS_ERR_IO, true, "Unable to find HDU");
+        return false;
+    }
+    if (!pmCellReadHeader(cell, fits, config)) {
+        psError(PS_ERR_IO, false, "Unable to read header for cell!\n");
+        return false;
+    }
+    if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS, true, NULL)) {
+        psError(PS_ERR_IO, false, "Failed to read concepts for cell.\n");
+        return false;
+    }
+
+    // Get the size of the third dimension
+    bool mdok;                          // Status of MD lookup
+    int naxis = psMetadataLookupS32(&mdok, hdu->header, "NAXIS"); // The number of axes
+    if (!mdok) {
+        psError(PS_ERR_IO, true, "Unable to find NAXIS in header for extension %s\n", hdu->extname);
+        return false;
+    }
+    if (naxis == 0) {
+        // No pixels to read
+        psError(PS_ERR_IO, true, "No pixels in extension %s.", hdu->extname);
+        return false;
+    }
+    if (naxis < 2 || naxis > 3) {
+        psError(PS_ERR_IO, true, "NAXIS in header of extension %s (= %d) is not valid.\n",
+                hdu->extname, naxis);
+        return false;
+    }
+    int naxis3;                     // Number of image planes
+    if (naxis == 3) {
+        naxis3 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS3");
+        if (!mdok) {
+            psError(PS_ERR_IO, true, "Unable to find NAXIS3 in header for extension %s\n", hdu->extname);
+            return false;
+        }
+    } else {
+        naxis3 = 1;
+    }
+
+    return naxis3;
+}
+
+// Does the current readout, with scans set for a new read, represent any real data, or is it beyond the end?
+// Requires that cellNumReadouts() has been called before (for header and concepts to have been read)
+// In the process, adjusts the TRIMSEC
+static bool readoutHaveMoreScans(int *start, // Start of scan
+                                 int *last, // Last possible scan (defined by TRIMSEC)
+                                 pmReadout *readout, // Readout of interest
+                                 int numScans, // Number of scans to read at a time
+                                 fpaReadType type, // Type of image
+                                 pmConfig *config // Configuration
+                                 )
+{
+    assert(start);
+    assert(last);
+    assert(readout);
+
+    if (!pmConceptsReadCell(readout->parent, PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_DATABASE,
+                            true, config)) {
+        psError(PS_ERR_IO, false, "Failed to read concepts for cell.");
+        return false;
+    }
+    // Header and concepts have been read by a call to cellNumReadouts(), so we can just assume they're there.
+
+    // Get the trim and bias sections
+    pmCell *cell = readout->parent;     // Parent cell
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    pmHDU *hdu = pmHDUFromCell(cell);   // HDU for data
+
+    bool mdok = true;                   // Status of MD lookup
+    psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim sections
+    if (!mdok || !trimsec || psRegionIsNaN(*trimsec)) {
+        psError(PS_ERR_IO, true, "CELL.TRIMSEC is not set.\n");
+        return false;
+    }
+    int readdir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
+    if (!mdok || readdir == 0 || (readdir != 1 && readdir != 2)) {
+        psError(PS_ERR_IO, true, "CELL.READDIR is not set to -1 or +1.\n");
+        return false;
+    }
+
+    // Rationalize trimsec against naxis1, naxis2:  valid range for trimsec is 1-Nx,1-Ny
+    if (trimsec->x1 < 1) {
+        int naxis1 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS1"); // The number of columns
+        if (!mdok) {
+            psError(PS_ERR_IO, true, "Unable to find NAXIS1 in header for extension %s\n", hdu->extname);
+            return false;
+        }
+        trimsec->x1 = naxis1 + trimsec->x1;
+    }
+    if (trimsec->y1 < 1) {
+        int naxis2 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS2"); // The number of columns
+        if (!mdok) {
+            psError(PS_ERR_IO, true, "Unable to find NAXIS2 in header for extension %s\n", hdu->extname);
+            return false;
+        }
+        trimsec->y1 = naxis2 + trimsec->y1;
+    }
+
+    *last = (readdir == 1) ? trimsec->y1 : trimsec->x1; // Maximum possible scan number
+
+    // Calculate the segment offset and upper limit
+    if (numScans == 0) {
+        // Read entire image.  In that case, we never call this funtion unless the data has not yet been read.
+        // thus, only if the delta is should we return false (ie, trimsec defines an empty region)
+        *start = (readdir == 1) ? trimsec->y0 : trimsec->x0;
+    } else if (readout->forceScan) {
+        // We're forced to read what we're told
+        *start = readoutGetThisScan(readout, type);
+    } else {
+        // Progressive scans
+        psImage *image = *readoutImageByType(readout, type); // Appropriate image from readout
+        *start = image ? readoutGetLastScan(readout, type) : 0;
+    }
+
+    return true;
+}
+
+static bool readoutMore(pmReadout *readout, // Readout of interest
+                        psFits *fits,    // FITS file
+                        int z,          // Plane number
+                        int numScans,   // Number of scans to read at a time
+                        fpaReadType type, // Type of image
+                        pmConfig *config// Configuration
+    )
+{
+    assert(readout);
+    assert(fits);
+
+    psImage *image = *readoutImageByType(readout, type);
+
+    // XXX this may not be the valid test in a multithread environment. consider a fileGroup of
+    // N readouts, but numScans set to 0.  only the first should report that it requires data,
+    // even if all readouts lack the image pointer.
+    if (!image) {
+        return true;
+    }
+
+    // If we have already read an image, this result implies we are done (no more to read)
+    if (numScans == 0) {
+        return false;
+    }
+
+    pmCell *cell = readout->parent;     // Parent cell
+    if (!cell) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find parent cell.");
+        return false;
+    }
+    int naxis3 = cellNumReadouts(cell, fits, config); // Number of planes
+    if (z >= naxis3) {
+        // No more to read
+        return false;
+    }
+
+    int start, last;                    // Start and last scans
+    if (!readoutHaveMoreScans(&start, &last, readout, numScans, type, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine readout properties.");
+        return false;
+    }
+
+    return start < last;
+}
+
+// Carve a readout from the image pixels
+static bool readoutCarve(pmReadout *readout, // Readout to be carved up
+                         psImage *image, // Image that will be carved
+                         const psRegion *trimsec, // Trim section
+                         const psList *biassecs, // Bias sections
+                         fpaReadType type // Type of image
+                        )
+{
+    assert(readout);
+    assert(image);
+    assert(trimsec);
+    assert(biassecs);
+
+    // The image corresponding to the trim region
+    if (psRegionIsNaN(*trimsec)) {
+        psString regionString = psRegionToString(*trimsec);
+        psError(PS_ERR_UNKNOWN, true, "Invalid trim section: %s\n", regionString);
+        psFree(regionString);
+        psFree(readout);
+        return false;
+    }
+    psRegion region = psRegionSet(PS_MAX(trimsec->x0 - readout->col0, 0), // x0
+                                  PS_MIN(trimsec->x1 - readout->col0, image->numCols), // x1
+                                  PS_MAX(trimsec->y0 - readout->row0, 0), // y0
+                                  PS_MIN(trimsec->y1 - readout->row0, image->numRows) // y1
+                                 );
+
+    // Place the image subset in the appropriate target location, freeing if needed
+    psImage **target = readoutImageByType(readout, type); // Target image
+    if (*target) {
+        psFree(*target);
+    }
+    *target = psImageSubset(image, region);
+
+    // Get the list of overscans: only for IMAGE types (no overscan for MASK and WEIGHT)
+    if (type == FPA_READ_TYPE_IMAGE) {
+        if (readout->bias->n != 0) {
+            // Make way!
+            psFree(readout->bias);
+            readout->bias = psListAlloc(NULL);
+        }
+        psListIterator *iter = psListIteratorAlloc((psList*)biassecs, PS_LIST_HEAD, false); // Iterator
+        psRegion *biassec = NULL;       // A BIASSEC region from the list
+        while ((biassec = psListGetAndIncrement(iter))) {
+            if (psRegionIsNaN(*biassec)) {
+                psString regionString = psRegionToString(*biassec);
+                psError(PS_ERR_IO, true, "Invalid bias section: %s\n", regionString);
+                psFree(regionString);
+                psFree(readout);
+                return false;
+            }
+            psRegion region = psRegionSet(PS_MAX(biassec->x0 - readout->col0, 0), // x0
+                                          PS_MIN(biassec->x1 - readout->col0, image->numCols), // x1
+                                          PS_MAX(biassec->y0 - readout->row0, 0), // y0
+                                          PS_MIN(biassec->y1 - readout->row0, image->numRows) // y1
+                );
+            psImage *overscan = psImageSubset(image, region);
+            psListAdd(readout->bias, PS_LIST_TAIL, overscan);
+            psFree(overscan);
+        }
+        psFree(iter);
+    }
+
+    return true;
+}
+
+// Read a component of a readout.  We read in only the rows from min to max for plane z, for
+// the full region requested.  if we request a range outside the region, we will pad to fill
+// out the edges of the region with 'bad' pixels.  The output image always has max-min rows.
+// The region represents the maximum bounds of the full image
+static psImage *readoutReadComponent(psImage *image, // Image into which to read
+                                     psFits *fits, // FITS file from which to read
+                                     const psRegion *fullImage, // full image region, read a subset
+                                     int readdir, // Read direction (1=rows, 2=cols)
+                                     int min,  // Minimum row/col number to read
+                                     int max,   // Maximum row/col number to read
+                                     int z,     // Image plane to read
+                                     float bad, // Bad value
+                                     psElemType type // Expected type for image
+    )
+{
+    assert(fits);
+    assert(fullImage);
+    assert((readdir == 1) || (readdir == 2));
+
+    int nRead = 0;                      // Number of scans read
+    int nScans = max - min;             // Number of scans desired
+    assert(nScans > 0);
+
+    psRegion toRead = *fullImage;  // full image region
+
+    int dX = 0, dY = 0;                 // Offset from image in FITS file to lower left corner of what's read
+    int nX = 0, nY = 0;                 // Size of region to read
+
+    if (readdir == 1) {
+        toRead.y0 = PS_MAX(toRead.y0, min);
+        toRead.y1 = PS_MIN(toRead.y1, max);
+        nRead = toRead.y1 - toRead.y0;
+        if (min < fullImage->y0) {
+            dY = toRead.y0;
+        }
+        nX = toRead.x1 - toRead.x0;
+        nY = nScans;
+    } else {
+        toRead.x0 = PS_MAX(toRead.x0, min);
+        toRead.x1 = PS_MIN(toRead.x1, max);
+        nRead = toRead.x1 - toRead.x0;
+        if (min < fullImage->x0) {
+            dX = toRead.x0;
+        }
+        nX = nScans;
+        nY = toRead.y1 - toRead.y0;
+    }
+
+    psTrace("psModules.camera", 5, "Reading section [%.0f:%.0f,%.0f:%.0f]\n",
+            toRead.x0, toRead.x1, toRead.y0, toRead.y1);
+    image = psFitsReadImageBuffer(image, fits, toRead, z); // Desired pixels
+    psTrace("psModules.camera", 7, "Image is %dx%d\n", image->numCols, image->numRows);
+
+    // Ensure the pixel type corresponds to what we desire
+    if (image->type.type != type) {
+        psImage *temp = psImageCopy(NULL, image, type);
+        psFree(image);
+        image = temp;
+    }
+
+    // Resize the image so that it matches the number of scans requested
+    // XXX this modification is not carried back up stream: it affects readout->row0,col0
+    //
+    // XXXXX Do we really want to do this???  Why???
+    if (nRead < nScans) {
+        // The region of interest is smaller than the number of pixels we want.
+        psTrace("psModules.camera", 5, "Resizing image to %d,%d\n", nX, nY);
+        psImage *temp = psImageAlloc(nX, nY, image->type.type);
+        psImageInit(temp, bad);
+        psImageOverlaySection(temp, image, dX, dY, "=");
+        psFree(image);
+        image = temp;
+    }
+
+    return image;
+}
+
+// Read a chunk of a readout (or the whole lot)
+static bool readoutReadChunk(pmReadout *readout, // Readout into which to read
+                             psFits *fits, // FITS file
+                             int z,     // Desired image plane
+                             int numScans, // Number of scans (row or col depends on CELL.READDIR); 0 for all
+                             int overlap, // Number of scans (row/col) to overlap between scans
+                             fpaReadType type, // Type of image
+                             pmConfig *config   // Configuration
+    )
+{
+    assert(readout);
+    assert(fits);
+    assert(z >= 0);
+    assert(numScans >= 0);
+    assert(overlap >= 0);
+
+    psImage **image = readoutImageByType(readout, type); // Pointer to the image of interest
+    if (*image && numScans == 0) {
+        psError(PS_ERR_UNKNOWN, true, "Already read entire image --- won't clobber.");
+        return false;
+    }
+
+    pmCell *cell = readout->parent;     // The parent cell
+    if (!cell) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find parent cell.");
+        return false;
+    }
+
+    int naxis3 = cellNumReadouts(cell, fits, config); // Number of image planes
+    if (z >= naxis3) {
+        psError(PS_ERR_IO, false, "Desired image plane (%d) exceeds available number (%d).",
+                z, naxis3);
+        return false;
+    }
+
+    int thisScan;                       // Starting scan for this read
+    int maxScan;                        // Maximum scan number
+    if (!readoutHaveMoreScans(&thisScan, &maxScan, readout, numScans, type, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine readout properties.");
+        return false;
+    }
+    if (thisScan >= maxScan) {
+        psError(PS_ERR_IO, true, "No more of the readout to read.");
+        return false;
+    }
+
+    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
+    assert(hdu && !hdu->blankPHU);      // Checked by cellNumReadouts()
+
+    bool mdok;                          // Status of MD lookup
+    int readdir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
+    if (!mdok || readdir == 0 || (readdir != 1 && readdir != 2)) {
+        psError(PS_ERR_IO, true, "CELL.READDIR is not set to -1 or +1.\n");
+        return false;
+    }
+
+    // XXX for IMAGE, we need the CELL.BAD value, but for MASK, we need the BAD mask value
+
+    float bad = 0;
+    if (type == FPA_READ_TYPE_MASK) {
+      bad = 1.0;
+    } else {
+      bad = psMetadataLookupF32(&mdok, cell->concepts, "CELL.BAD"); // Bad level
+    }
+
+    psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim sections
+    if (!mdok || !trimsec || psRegionIsNaN(*trimsec)) {
+        psError(PS_ERR_IO, true, "CELL.TRIMSEC is not set.\n");
+        return false;
+    }
+
+    // Check the third dimension
+    int naxis = psMetadataLookupS32(&mdok, hdu->header, "NAXIS"); // The number of axes
+    if (!mdok) {
+        psError(PS_ERR_IO, true, "Unable to find NAXIS in header for extension %s\n", hdu->extname);
+        return false;
+    }
+    if (naxis == 0) {
+        // No pixels to read
+        psError(PS_ERR_IO, true, "No pixels in extension %s.", hdu->extname);
+        return false;
+    }
+    if (naxis < 2 || naxis > 3) {
+        psError(PS_ERR_IO, true, "NAXIS in header of extension %s (= %d) is not valid.\n",
+                hdu->extname, naxis);
+        return false;
+    }
+
+    // Determine the number of scans to read
+    int lastScan = readoutSetLastScan(readout, type, thisScan + numScans);
+    if (thisScan == 0) {
+        overlap = 0;
+    }
+    thisScan -= overlap;
+    if (thisScan < 0) {
+        thisScan = 0;
+    }
+    readoutSetThisScan(readout, type, thisScan);
+
+    // Calculate limits, adjust readout->row0,col0
+    // XXX Should row0,col0 be adjusted, since they are used for astrometry???
+    if (readdir == 1) {
+        // Reading rows
+        readout->row0 = thisScan;
+        readout->col0 = trimsec->x0;
+        if (numScans == 0) {
+            numScans = trimsec->y1 - trimsec->y0;
+        }
+    } else {
+        // Reading cols
+        readout->col0 = thisScan;
+        readout->row0 = trimsec->y0;
+        if (numScans == 0) {
+            numScans = trimsec->x1 - trimsec->x0;
+        }
+    }
+
+    // Blow away existing data.
+    // Do this before returning, so that we're not returning data from a previous read
+    psFree(*image);
+    *image = NULL;
+    *image = readoutReadComponent(*image, fits, trimsec, readdir, thisScan, lastScan, z, bad, pixelTypes[type]);
+
+    // Read overscans only for "image" type --- weights and masks shouldn't record overscans
+    if (type == FPA_READ_TYPE_IMAGE) {
+        // Blow away existing data
+        while (readout->bias->n > 0) {
+            psListRemove(readout->bias, PS_LIST_HEAD);
+        }
+
+        // Get the new bias sections
+        psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
+        if (!mdok || !biassecs) {
+            psError(PS_ERR_IO, true, "CELL.BIASSEC is not set.\n");
+            return false;
+        }
+        psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
+        psRegion *biassec = NULL;           // Bias section from iteration
+        while ((biassec = psListGetAndIncrement(biassecsIter))) {
+            psImage *bias = readoutReadComponent(NULL, fits, biassec, readdir, thisScan, lastScan, z, bad, pixelTypes[type]); // The bias
+            psListAdd(readout->bias, PS_LIST_TAIL, bias);
+            psFree(bias);                   // Drop reference
+        }
+        psFree(biassecsIter);
+    }
+
+    return true;
+}
+
+// Read into an cell; this is the engine for pmCellRead, pmCellReadMask, pmCellReadWeight
+// Does most of the work for the reading --- reads the HDU, and portions the HDU into readouts.
+static bool cellRead(pmCell *cell,      // Cell into which to read
+                     psFits *fits,      // FITS file from which to read
+                     pmConfig *config,  // Configuration
+                     fpaReadType type   // Type to read
+                    )
+{
+    assert(cell);
+    assert(fits);
+
+    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
+    if (!hdu) {
+        return true;                    // We read everything we could
+    }
+
+    // check if we have read the desired data, read it if needed
+    bool (*hduReadFunc)(pmHDU*, psFits*) = NULL; // Function to use to read the HDU
+    void *dataPointer = NULL;           // pointer to location of desired data
+    switch (type) {
+      case FPA_READ_TYPE_IMAGE:
+        hduReadFunc = pmHDURead;
+        dataPointer = hdu->images;
+        break;
+      case FPA_READ_TYPE_HEADER:
+        hduReadFunc = pmHDUReadHeader;
+        dataPointer = hdu->header;
+        break;
+      case FPA_READ_TYPE_MASK:
+        hduReadFunc = pmHDUReadMask;
+        dataPointer = hdu->masks;
+        break;
+      case FPA_READ_TYPE_WEIGHT:
+        hduReadFunc = pmHDUReadWeight;
+        dataPointer = hdu->weights;
+        break;
+      default:
+        psAbort("Unknown read type: %x\n", type);
+    }
+
+    // do we have the data we want (image, header, or etc).
+    if (!dataPointer) {
+        // attempt to read in the desired data
+        if (!hduReadFunc(hdu, fits)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read HDU for cell.\n");
+            return false;
+        }
+    }
+
+    // load in the concept information for this cell
+    if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_DATABASE, true, config)) {
+        //psError(PS_ERR_UNKNOWN, false, "Failed to read concepts for cell");
+        //return false;
+        psWarning("Difficulty reading concepts for cell; attempting to proceed.");
+    }
+
+    // skip the image arrays completely for the header-only files
+    if (type == FPA_READ_TYPE_HEADER) {
+        pmCellSetDataStatus(cell, true);
+        return true;
+    }
+
+    // set up pointers for the different possible image arrays
+    psArray *imageArray = NULL; // Array of images in the HDU
+    psElemType imageType = pixelTypes[type]; // Expected type for image
+    switch (type) {
+      case FPA_READ_TYPE_IMAGE:
+        imageArray = hdu->images;
+        break;
+      case FPA_READ_TYPE_MASK:
+        imageArray = hdu->masks;
+        break;
+      case FPA_READ_TYPE_WEIGHT:
+        imageArray = hdu->weights;
+        break;
+      default:
+        psAbort("Unknown read type: %x\n", type);
+    }
+
+    // Having read the cell, we now have to cut it up
+    psRegion *trimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC");
+    psList *biassecs = psMetadataLookupPtr(NULL, cell->concepts, "CELL.BIASSEC");
+    if (psRegionIsNaN(*trimsec)) {
+        psError(PS_ERR_IO, false, "CELL.TRIMSEC is not set --- can't read cell.\n");
+        return false;
+    }
+
+    // Iterate over each of the image planes, converting type if necessary, and extracting the bits that
+    // matter (CELL.TRIMSEC, CELL.BIASSEC) into readouts with readoutCarve.
+    for (int i = 0; i < imageArray->n; i++) {
+        psImage *source = imageArray->data[i]; // Source image, from the i-th plane
+        PS_ASSERT_IMAGE_NON_NULL(source, false);
+
+        // Type conversion here to support the modules, which don't have multiple type support yet
+        if (source->type.type != imageType) {
+            psImage *temp = psImageCopy(NULL, source, imageType); // Temporary image
+            psFree(imageArray->data[i]);
+            imageArray->data[i] = temp;
+            source = temp;
+        }
+
+        pmReadout *readout;             // Readout into which to read
+        if (cell->readouts->n > i && cell->readouts->data[i]) {
+            readout = psMemIncrRefCounter(cell->readouts->data[i]);
+        } else {
+            readout = pmReadoutAlloc(cell);
+        }
+
+        if (!readoutCarve(readout, source, trimsec, biassecs, type)) {
+            psError(PS_ERR_UNEXPECTED_NULL, false,
+                    "Unable to carve readout into image and bias sections for %d the plane.", i);
+            return NULL;
+        }
+        psFree(readout);                // Drop reference
+    }
+
+    pmCellSetDataStatus(cell, true);
+    return true;
+}
+
+
+// Read into an chip; this is the engine for pmChipRead, pmChipReadMask, pmChipReadWeight
+// Iterates over component cells, reading each
+static bool chipRead(pmChip *chip,      // Chip into which to read
+                     psFits *fits,      // FITS file from which to read
+                     pmConfig *config,  // Configuration
+                     fpaReadType type   // Type to read
+                    )
+{
+    assert(chip);
+    assert(fits);
+
+    bool success = false;               // Were we able to read at least one HDU?
+    psArray *cells = chip->cells;       // Array of cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // The cell of interest
+        success |= cellRead(cell, fits, config, type);
+    }
+    if (success) {
+        if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_DATABASE,
+                                true, true, NULL)) {
+            psError(PS_ERR_IO, false, "Failed to read concepts for chip.\n");
+            return false;
+        }
+        // XXX probably could just use chip->data_exists
+        pmChipSetDataStatus(chip, true);
+    }
+
+    return success;
+}
+
+
+// Read into an FPA; this is the engine for pmFPARead, pmFPAReadMask, pmFPAReadWeight
+// Iterates over component chips, reading each
+static bool fpaRead(pmFPA *fpa,         // FPA into which to read
+                    psFits *fits,       // FITS file from which to read
+                    pmConfig *config,   // Configuration
+                    fpaReadType type    // Type to read
+                   )
+{
+    assert(fpa);
+    assert(fits);
+
+    bool success = false;               // Were we able to read at least one HDU?
+    psArray *chips = fpa->chips;        // Array of chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // The cell of interest
+        success |= chipRead(chip, fits, config, type);
+    }
+    if (success) {
+        if (!pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_DATABASE, true, NULL)) {
+            psError(PS_ERR_IO, false, "Failed to read concepts for FPA.\n");
+            return false;
+        }
+    } else {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read any chips in FPA");
+    }
+
+    return success;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Reading images
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// pmReadoutReadNext is maintained here (for now) to maintain backwards compatibility.
+// pmReadoutReadNext has been replaced by pmReadoutRead, pmReadoutReadChunk, pmReadoutMore
+bool pmReadoutReadNext(bool *status, pmReadout *readout, psFits *fits, int z, int numScans, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_INT_NONNEGATIVE(z, false);
+    PS_ASSERT_INT_NONNEGATIVE(numScans, false);
+
+    assert (numScans || !readout->image); // cannot have readout->image and !numScans
+
+    *status = false;
+
+    // Get the HDU and read the header
+    pmCell *cell = readout->parent;     // The parent cell
+
+    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
+    if (!hdu || hdu->blankPHU) {
+        // XXX is this an error condition?
+        *status = true;
+        return false;
+    }
+
+    if (!pmCellReadHeader(cell, fits, config)) {
+        psError(PS_ERR_IO, false, "Unable to read header for cell!\n");
+        return false;
+    }
+
+    // Make sure we have the information we need
+    if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
+                            PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_DATABASE, true, NULL)) {
+        psError(PS_ERR_IO, false, "Failed to read concepts for cell.\n");
+        return false;
+    }
+
+    // Get the trim and bias sections
+    bool mdok = true;                   // Status of MD lookup
+    psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim sections
+    if (!mdok || !trimsec || psRegionIsNaN(*trimsec)) {
+        psError(PS_ERR_IO, true, "CELL.TRIMSEC is not set.\n");
+        return false;
+    }
+    psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
+    if (!mdok || !biassecs) {
+        psError(PS_ERR_IO, true, "CELL.BIASSEC is not set.\n");
+        return false;
+    }
+    int readdir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
+    if (!mdok || readdir == 0 || (readdir != 1 && readdir != 2)) {
+        psError(PS_ERR_IO, true, "CELL.READDIR is not set to -1 or +1.\n");
+        return false;
+    }
+    float bad = psMetadataLookupF32(&mdok, cell->concepts, "CELL.BAD"); // Bad level
+    if (!mdok) {
+        psLogMsg(__func__, PS_LOG_WARN, "CELL.BAD is not set --- assuming zero.\n");
+        bad = 0.0;
+    }
+
+    // Check the third dimension
+    int naxis = psMetadataLookupS32(&mdok, hdu->header, "NAXIS"); // The number of axes
+    if (!mdok) {
+        psError(PS_ERR_IO, true, "Unable to find NAXIS in header for extension %s\n", hdu->extname);
+        return false;
+    }
+    if (naxis == 0) {
+        // No pixels to read, as for a PHU.
+        *status = true;
+        return false;
+    }
+    if (naxis < 2 || naxis > 3) {
+        psError(PS_ERR_IO, true, "NAXIS in header of extension %s (= %d) is not valid.\n",
+                hdu->extname, naxis);
+        return false;
+    }
+    int naxis3 = 1;                     // The number of image planes
+    if (naxis == 3) {
+        naxis3 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS3");
+        if (!mdok) {
+            psError(PS_ERR_IO, true, "Unable to find NAXIS3 in header for extension %s\n", hdu->extname);
+            return false;
+        }
+    }
+    if (z >= naxis3) {
+        // Nothing to see here.  Move along.
+        *status = true;
+        return false;
+    }
+
+    // Get the size of the image plane
+    int naxis1 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS1"); // The number of columns
+    if (!mdok) {
+        psError(PS_ERR_IO, true, "Unable to find NAXIS1 in header for extension %s\n", hdu->extname);
+        return false;
+    }
+    int naxis2 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS2"); // The number of columns
+    if (!mdok) {
+        psError(PS_ERR_IO, true, "Unable to find NAXIS2 in header for extension %s\n", hdu->extname);
+        return false;
+    }
+
+    // rationalize trimsec against naxis1, naxis2
+    // valid range for trimsec is 1-Nx,1-Ny
+    // if (trimsec->x0 == 0) trimsec->x0 = 1;
+    if (trimsec->x1 <  1)
+        trimsec->x1 = naxis1 + trimsec->x1;
+    // if (trimsec->y0 == 0) trimsec->y0 = 1;
+    if (trimsec->y1 <  1)
+        trimsec->y1 = naxis2 + trimsec->y1;
+
+    int maxSize;                        // Number of cols,rows in image
+    if (readdir == 1) {
+        maxSize = PS_MIN(naxis2, trimsec->y1 - trimsec->y0);
+    } else {
+        maxSize = PS_MIN(naxis1, trimsec->x1 - trimsec->x0);
+    }
+
+    int offset;                         // start of the segment
+    int upper;                          // end of the segment
+    int lastScan;                       // last possible scan
+
+    // Calculate the segment offset and upper limit, adjust readout->row0,col0
+    if (readdir == 1) {
+        // Reading rows
+        offset = (readout->image) ? readout->row0 + numScans : 0; // extend to next section or start at beginning?
+        offset = (numScans == 0) ? trimsec->x0 : offset; // full array ? read full trimsec : read section
+        readout->row0 = offset;
+        readout->col0 = trimsec->x0;
+        lastScan = trimsec->y1;
+    } else {
+        // Reading cols
+        offset = (readout->image) ? readout->col0 + numScans : 0;
+        offset = (numScans == 0) ? trimsec->y0 : offset; // full array ? read full trimsec : read section
+        readout->col0 = offset;
+        readout->row0 = trimsec->y0;
+        lastScan = trimsec->x1;
+    }
+    upper = offset + numScans;
+
+    // Blow away existing data.
+    // Do this before returning, so that we're not returning data from a previous read
+    psFree(readout->image);
+    readout->image = NULL;
+
+    while (readout->bias->n > 0) {
+        psListRemove(readout->bias, PS_LIST_HEAD);
+    }
+
+    if (offset >= lastScan) {
+        // We've read everything there is
+        psTrace("psModules.camera", 7, "Read everything.\n");
+        *status = true;
+        return false;
+    }
+
+    psTrace("psModules.camera", 7, "offset=%d, upper = %d, image is %dx%d, trimsec [%.0f:%.0f,%.0f:%.0f]\n",
+            offset, upper, naxis1, naxis2, trimsec->x0, trimsec->x1, trimsec->y0, trimsec->y1);
+
+    // Get the new the trim section
+    readout->image = readoutReadComponent(readout->image, fits, trimsec, readdir, offset, upper, z, bad,
+                                          PS_TYPE_F32); // The image
+
+    // Get the new bias sections
+    psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator for BIASSEC
+    psRegion *biassec = NULL;           // Bias section from iteration
+    while ((biassec = psListGetAndIncrement(biassecsIter))) {
+        psImage *bias = readoutReadComponent(NULL, fits, biassec, readdir, offset, upper, z, bad,
+                                             PS_TYPE_F32); // The bias
+        psListAdd(readout->bias, PS_LIST_TAIL, bias);
+        psFree(bias);                   // Drop reference
+    }
+    psFree(biassecsIter);
+
+    *status = true;
+    return true;
+}
+
+
+
+bool pmReadoutMore(pmReadout *readout, psFits *fits, int z, int numScans, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return readoutMore(readout, fits, z, numScans, FPA_READ_TYPE_IMAGE, config);
+}
+
+bool pmReadoutReadChunk(pmReadout *readout, psFits *fits, int z, int numScans, int overlap, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_INT_NONNEGATIVE(z, false);
+    PS_ASSERT_INT_NONNEGATIVE(numScans, false);
+
+    return readoutReadChunk(readout, fits, z, numScans, overlap, FPA_READ_TYPE_IMAGE, config);
+}
+
+bool pmReadoutRead(pmReadout *readout, psFits *fits, int z, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return readoutReadChunk(readout, fits, z, 0, 0, FPA_READ_TYPE_IMAGE, config);
+}
+
+int pmCellNumReadouts(pmCell *cell, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return cellNumReadouts(cell, fits, config);
+}
+
+bool pmCellRead(pmCell *cell, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return cellRead(cell, fits, config, FPA_READ_TYPE_IMAGE);
+}
+
+bool pmChipRead(pmChip *chip, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return chipRead(chip, fits, config, FPA_READ_TYPE_IMAGE);
+}
+
+bool pmFPARead(pmFPA *fpa, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return fpaRead(fpa, fits, config, FPA_READ_TYPE_IMAGE);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Reading the mask
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmReadoutMoreMask(pmReadout *readout, psFits *fits, int z, int numScans, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return readoutMore(readout, fits, z, numScans, FPA_READ_TYPE_MASK, config);
+}
+
+bool pmReadoutReadChunkMask(pmReadout *readout, psFits *fits, int z, int numScans, int overlap,
+                            pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_INT_NONNEGATIVE(z, false);
+    PS_ASSERT_INT_NONNEGATIVE(numScans, false);
+
+    return readoutReadChunk(readout, fits, z, numScans, overlap, FPA_READ_TYPE_MASK, config);
+}
+
+bool pmReadoutReadMask(pmReadout *readout, psFits *fits, int z, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return readoutReadChunk(readout, fits, z, 0, 0, FPA_READ_TYPE_MASK, config);
+}
+
+bool pmCellReadMask(pmCell *cell, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return cellRead(cell, fits, config, FPA_READ_TYPE_MASK);
+}
+
+bool pmChipReadMask(pmChip *chip, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return chipRead(chip, fits, config, FPA_READ_TYPE_MASK);
+}
+
+bool pmFPAReadMask(pmFPA *fpa, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return fpaRead(fpa, fits, config, FPA_READ_TYPE_MASK);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Reading the weight map
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmReadoutMoreWeight(pmReadout *readout, psFits *fits, int z, int numScans, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return readoutMore(readout, fits, z, numScans, FPA_READ_TYPE_WEIGHT, config);
+}
+
+bool pmReadoutReadChunkWeight(pmReadout *readout, psFits *fits, int z, int numScans, int overlap,
+                              pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_INT_NONNEGATIVE(z, false);
+    PS_ASSERT_INT_NONNEGATIVE(numScans, false);
+
+    return readoutReadChunk(readout, fits, z, numScans, overlap, FPA_READ_TYPE_WEIGHT, config);
+}
+
+bool pmReadoutReadWeight(pmReadout *readout, psFits *fits, int z, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return readoutReadChunk(readout, fits, z, 0, 0, FPA_READ_TYPE_WEIGHT, config);
+}
+
+bool pmCellReadWeight(pmCell *cell, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return cellRead(cell, fits, config, FPA_READ_TYPE_WEIGHT);
+}
+
+bool pmChipReadWeight(pmChip *chip, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return chipRead(chip, fits, config, FPA_READ_TYPE_WEIGHT);
+}
+
+bool pmFPAReadWeight(pmFPA *fpa, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return fpaRead(fpa, fits, config, FPA_READ_TYPE_WEIGHT);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Reading the image header
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmCellReadHeaderSet(pmCell *cell, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return cellRead(cell, fits, config, FPA_READ_TYPE_HEADER);
+}
+
+bool pmChipReadHeaderSet(pmChip *chip, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return chipRead(chip, fits, config, FPA_READ_TYPE_HEADER);
+}
+
+bool pmFPAReadHeaderSet(pmFPA *fpa, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return fpaRead(fpa, fits, config, FPA_READ_TYPE_HEADER);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Reading FITS tables
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+int pmCellReadTable(pmCell *cell, psFits *fits, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, 0);
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
+    PS_ASSERT_STRING_NON_EMPTY(name, 0);
+
+    const char *chipName = psMetadataLookupStr(NULL, cell->parent->concepts, "CHIP.NAME"); // Name of chip
+    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
+    psString extname = NULL;            // Extension name
+    psStringAppend(&extname, "%s_%s_%s", name, chipName, cellName);
+
+    // XXX Could do a table lookup from the camera format, in case the input file isn't laid out with
+    // NAME_CHIP_CELL extension names --- use these as keys, and the value as the proper extension name.
+    // Allow interpolation of concepts, e.g., "{CHIP.NAME}" --> "ccd13".
+
+    if (!psFitsMoveExtName(fits, extname)) {
+        psError(PS_ERR_IO, false, "Unable to move to extension %s\n", extname);
+        psFree(extname);
+        return 0;
+    }
+
+    psMetadata *header = psFitsReadHeader(NULL, fits); // The FITS header
+    if (!header) {
+        psError(PS_ERR_IO, false, "Unable to read header for extension %s\n", extname);
+        psFree(extname);
+        psFree(header);
+        return 0;
+    }
+
+    psString headerName = NULL;         // Name for header
+    psStringAppend(&headerName, "%s.HEADER", name);
+    if (!psMetadataAdd(cell->analysis, PS_LIST_TAIL, headerName, PS_DATA_METADATA,
+                       "FITS table header", header)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to add header from extension %s to analysis metadata "
+                "for chip %s, cell %s\n", extname, chipName, cellName);
+        psFree(headerName);
+        psFree(header);
+        psFree(extname);
+        return 0;
+    }
+    psFree(headerName);
+    psFree(header);
+
+    psArray *table = psFitsReadTable(fits); // The table
+    if (!psMetadataAdd(cell->analysis, PS_LIST_TAIL, name, PS_DATA_ARRAY, "FITS table", table)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to add table from extension %s to analysis metadata "
+                "for chip %s, cell %s\n", extname, chipName, cellName);
+        psFree(table);
+        psFree(extname);
+        return 0;
+    }
+
+    psFree(extname);
+    psFree(table);                      // Dropping reference
+
+    return 1;
+}
+
+
+int pmChipReadTable(pmChip *chip, psFits *fits, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, 0);
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
+    PS_ASSERT_STRING_NON_EMPTY(name, 0);
+
+    int numRead = 0;                    // Number of reads
+    psArray *cells = chip->cells;       // Array of cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        numRead += pmCellReadTable(cell, fits, name);
+    }
+
+    return numRead;
+}
+
+
+int pmFPAReadTable(pmFPA *fpa, psFits *fits, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, 0);
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
+    PS_ASSERT_STRING_NON_EMPTY(name, 0);
+
+    int numRead = 0;                    // Number of reads
+    psArray *chips = fpa->chips;        // Array of chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // Chip of interest
+        numRead += pmChipReadTable(chip, fits, name);
+    }
+
+    return numRead;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPARead.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPARead.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPARead.h	(revision 22322)
@@ -0,0 +1,248 @@
+/* @file pmFPARead.h
+ * @brief Functions to read FPA components from a FITS file
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-17 22:16:38 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_READ_H
+#define PM_FPA_READ_H
+
+#include <pmConfig.h>
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Check to see if there is more to read when reading a readout incrementally
+bool pmReadoutMore(pmReadout *readout,  ///< Readout of interest
+                   psFits *fits,        ///< FITS file from which to read
+                   int z,               ///< Readout number/plane; zero-offset indexing
+                   int numScans,        ///< Number of scans (rows/cols) to read
+                   pmConfig *config     ///< Configuration
+    );
+
+/// Read a chunk of a readout.
+///
+/// Allows reading the readout incrementally
+bool pmReadoutReadChunk(pmReadout *readout, ///< Readout of interest
+                        psFits *fits,   ///< FITS file from which to read
+                        int z,          ///< Readout number/plane; zero-offset indexing
+                        int numScans,   ///< Number of scans (rows/cols) to read
+                        int overlap,    ///< Overlap between consecutive reads
+                        pmConfig *config ///< Configuration
+    );
+
+/// Read the entire readout
+bool pmReadoutRead(pmReadout *readout,  ///< Readout of interest
+                   psFits *fits,        ///< FITS file from which to read
+                   int z,               ///< Readout number/plane; zero-offset indexing
+                   pmConfig *config     ///< Configuration
+    );
+
+/// Read a readout incrementally --- this is maintained temporarily only for backwards compatibility; it has
+/// been replaced by pmReadoutRead, pmReadoutReadChunk and pmReadoutMore.
+///
+/// Multiple calls to this function moves through a readout within a cell incrementally.  It is required to
+/// pass the readout previously acquired (or a newly-allocated one) in order to preserve state information
+/// (where the read is at).  Only a maximum of numRows rows are read at a time.  Returns true if pixels are
+/// read, and false otherwise.  To facilitate looping, no error is generated for reading a plane that doesn't
+/// exist.  Note that this function doesn't put pixels in the HDU.  It is therefore NOT COMPATIBLE with
+/// pmCellWrite, pmChipWrite, and pmFPAWrite (or any function that uses or creates an HDU for that matter).
+/// Use pmReadoutWriteNext to write the data that's read by this function.  This function is intended for
+/// reading in many readouts into memory at once (e.g., for stacking) where the input is not written out.
+bool pmReadoutReadNext(bool *status,    // non-error exit condition?
+                       pmReadout *readout, // Readout into which to read
+                       psFits *fits,    // FITS file from which to read
+                       int z,           // Readout number/plane; zero-offset indexing
+                       int numRows,      // The number of rows to read
+                       pmConfig *config
+                      );
+
+/// Return the number of readouts within a cell
+///
+/// This function is type-independent (doesn't matter if you are interested in the image/mask/weight).
+int pmCellNumReadouts(pmCell *cell, psFits *fits, pmConfig *config);
+
+/// Read an entire cell
+///
+/// Reads the appropriate HDU, ingests concepts from the header, and portions pixels into readouts.  Pixels
+/// are converted to F32.
+bool pmCellRead(pmCell *cell,           // Cell to read into
+                psFits *fits,           // FITS file from which to read
+                pmConfig *config        // Configuration
+               );
+
+/// Read a chip
+///
+/// Iterates over component cells, reading each with pmCellRead.
+bool pmChipRead(pmChip *chip,           // Chip to read into
+                psFits *fits,           // FITS file from which to read
+                pmConfig *config        // Configuration
+               );
+
+/// Read an FPA
+///
+/// Iterates over component chips, reading each with pmChipRead.
+bool pmFPARead(pmFPA *fpa,              // FPA to read into
+               psFits *fits,            // FITS file from which to read
+               pmConfig *config         // Configuration
+              );
+
+// Mask functions follow
+
+/// Check to see if there is more to read when reading a readout incrementally into the mask
+bool pmReadoutMoreMask(pmReadout *readout, ///< Readout of interest
+                       psFits *fits,    ///< FITS file from which to read
+                       int z,           ///< Readout number/plane; zero-offset indexing
+                       int numScans,    ///< Number of scans (rows/cols) to read
+                       pmConfig *config ///< Configuration
+    );
+
+/// Read a chunk of a readout into the mask
+///
+/// Allows reading the readout incrementally
+bool pmReadoutReadChunkMask(pmReadout *readout, ///< Readout of interest
+                            psFits *fits, ///< FITS file from which to read
+                            int z,      ///< Readout number/plane; zero-offset indexing
+                            int numScans, ///< Number of scans (rows/cols) to read
+                            int overlap, ///< Overlap between consecutive reads
+                            pmConfig *config ///< Configuration
+    );
+
+/// Read the entire readout into the mask
+bool pmReadoutReadMask(pmReadout *readout, ///< Readout of interest
+                       psFits *fits,    ///< FITS file from which to read
+                       int z,           ///< Readout number/plane; zero-offset indexing
+                       pmConfig *config ///< Configuration
+    );
+
+/// Read an entire cell into the mask
+///
+/// Same as pmCellRead, but reads into the mask element of the readouts.
+bool pmCellReadMask(pmCell *cell,       // Cell to read into
+                    psFits *fits,       // FITS file from which to read
+                    pmConfig *config    // Configuration
+                   );
+
+/// Read an entire chip into the mask
+///
+/// Same as pmChipRead, but reads into the mask element of the readouts.
+bool pmChipReadMask(pmChip *chip,       // Chip to read into
+                    psFits *fits,       // FITS file from which to read
+                    pmConfig *config    // Configuration
+                   );
+
+/// Read an entire FPA into the mask
+///
+/// Same as pmFPARead, but reads into the mask element of the readouts.
+bool pmFPAReadMask(pmFPA *fpa,          // FPA to read into
+                   psFits *fits,        // FITS file from which to read
+                   pmConfig *config     // Configuration
+                  );
+
+// Weight functions follow
+
+/// Check to see if there is more to read when reading a readout incrementally into the weight
+bool pmReadoutMoreWeight(pmReadout *readout, ///< Readout of interest
+                         psFits *fits,  ///< FITS file from which to read
+                         int z,         ///< Readout number/plane; zero-offset indexing
+                         int numScans,  ///< Number of scans (rows/cols) to read
+                         pmConfig *config ///< Configuration
+    );
+
+/// Read a chunk of a readout into the weight
+///
+/// Allows reading the readout incrementally
+bool pmReadoutReadChunkWeight(pmReadout *readout, ///< Readout of interest
+                              psFits *fits, ///< FITS file from which to read
+                              int z,    ///< Readout number/plane; zero-offset indexing
+                              int numScans, ///< Number of scans (rows/cols) to read
+                              int overlap, ///< Overlap between consecutive reads
+                              pmConfig *config ///< Configuration
+    );
+
+/// Read the entire readout into the weight
+bool pmReadoutReadWeight(pmReadout *readout, ///< Readout of interest
+                         psFits *fits,  ///< FITS file from which to read
+                         int z,         ///< Readout number/plane; zero-offset indexing
+                         pmConfig *config ///< Configuration
+    );
+
+/// Read an entire cell into the weight
+///
+/// Same as pmCellRead, but reads into the weight element of the readouts.
+bool pmCellReadWeight(pmCell *cell,     // Cell to read into
+                      psFits *fits,     // FITS file from which to read
+                      pmConfig *config  // Configuration
+                     );
+
+/// Read an entire chip into the weight
+///
+/// Same as pmChipRead, but reads into the weight element of the readouts.
+bool pmChipReadWeight(pmChip *chip,     // Chip to read into
+                      psFits *fits,     // FITS file from which to read
+                      pmConfig *config  // Configuration
+                     );
+
+/// Read an entire FPA into the weight
+///
+/// Same as pmFPARead, but reads into the weight element of the readouts.
+bool pmFPAReadWeight(pmFPA *fpa,        // FPA to read into
+                     psFits *fits,      // FITS file from which to read
+                     pmConfig *config   // Configuration
+                    );
+
+/// Read cell headers
+///
+/// Same as pmCellRead, but reads only the headers of the readouts.
+bool pmCellReadHeaderSet(pmCell *cell,  // Cell to read into
+                         psFits *fits,  // FITS file from which to read
+                         pmConfig *config // Configuration
+    );
+
+/// Read chip headers
+///
+/// Same as pmChipRead, but reads only the headers of the readouts.
+bool pmChipReadHeaderSet(pmChip *chip,  // Chip to read into
+                      psFits *fits,     // FITS file from which to read
+                      pmConfig *config  // Configuration
+                     );
+
+/// Read FPA headers
+///
+/// Same as pmFPARead, but reads only the headers of the readouts.
+bool pmFPAReadHeaderSet(pmFPA *fpa,     // FPA to read into
+                        psFits *fits,   // FITS file from which to read
+                        pmConfig *config // Configuration
+    );
+
+/// Read a FITS table into the cell
+///
+/// Given a name, which is combined with the chip and cell to identify the extension name ("NAME_CHIP_CELL"),
+/// read the FITS table into the cell analysis metadata (with key being the provided name).  The header is
+/// also read and included in the cell analysis metadata under "name.HEADER".
+int pmCellReadTable(pmCell *cell,       ///< Cell for which to read table
+                    psFits *fits,       ///< FITS file from which the table
+                    const char *name    ///< Specifies the extension name, and target in the analysis metadata
+                   );
+
+/// Read a FITS table into the component cells
+///
+/// Iterates over component cells, calling pmCellReadTable.
+int pmChipReadTable(pmChip *chip,       ///< Cell for which to read table
+                    psFits *fits,       ///< FITS file from which the table
+                    const char *name    ///< Specifies the extension name, and target in the analysis metadata
+                   );
+
+/// Read a FITS table into the component cells
+///
+/// Iterates over component chips, calling pmChipReadTable.
+int pmFPAReadTable(pmFPA *fpa,          ///< Cell for which to read table
+                   psFits *fits,        ///< FITS file from which the table
+                   const char *name     ///< Specifies the extension name, and target in the analysis metadata
+                  );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAUtils.c	(revision 22322)
@@ -0,0 +1,55 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAUtils.h"
+
+int pmFPAFindChip(const pmFPA *fpa, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, -1);
+    PS_ASSERT_PTR_NON_NULL(name, -1);
+    if (strlen(name) == 0) {
+        return -1;
+    }
+
+    psArray *chips = fpa->chips;        // Array of chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i]; // The chip of interest
+        psString testName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); // Name of this chip
+        if (strcmp(name, testName) == 0) {
+            return i;
+        }
+    }
+
+    psError(PS_ERR_IO, true, "Unable to find chip %s\n", name);
+    return -1;
+}
+
+
+int pmChipFindCell(const pmChip *chip, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, -1);
+    PS_ASSERT_PTR_NON_NULL(name, -1);
+    if (strlen(name) == 0) {
+        return -1;
+    }
+
+    psArray *cells = chip->cells;    // Array of cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i]; // The cell of interest
+        psString testName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of this cell
+        if (strcmp(name, testName) == 0) {
+            return i;
+        }
+    }
+
+    psError(PS_ERR_IO, true, "Unable to find cell %s\n", name);
+    return -1;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAUtils.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAUtils.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAUtils.h	(revision 22322)
@@ -0,0 +1,33 @@
+/* @file pmFPAUtils.h
+ * @brief Utility functions for FPAs
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-30 21:12:56 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_UTILS_H
+#define PM_FPA_UTILS_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Find a chip by name; return the index
+///
+/// Looks for a chip within the FPA with CHIP.NAME matching the provided name.  Returns the index of the chip,
+/// or -1 if it was not found.
+int pmFPAFindChip(const pmFPA *fpa,     ///< FPA in which to find the chip
+                  const char *name      ///< Name of the chip
+                 );
+
+/// Find a cell by name; return the index
+///
+/// Looks for a cell within the chip with CELL.NAME matching the provided name.  Returns the index of the
+/// cell, or -1 if it was not found.
+int pmChipFindCell(const pmChip *chip,  // Chip in which to find the cell
+                   const char *name     // Name of the cell
+                  );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAWrite.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAWrite.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAWrite.c	(revision 22322)
@@ -0,0 +1,511 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmDetrendDB.h"
+#include "pmFPAfile.h"
+#include "pmHDUUtils.h"
+#include "pmHDUGenerate.h"
+#include "pmConcepts.h"
+
+#include "pmFPAWrite.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Definitions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Specify what to read
+typedef enum {
+    FPA_WRITE_TYPE_IMAGE,               // Write image
+    FPA_WRITE_TYPE_MASK,                // Write mask
+    FPA_WRITE_TYPE_WEIGHT               // Write weight map
+} fpaWriteType;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static (private) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Return the appropriate image array for the given type
+static psArray **appropriateImageArray(pmHDU *hdu, // HDU containing the image arrays
+                                       fpaWriteType type // Type to write
+                                      )
+{
+    switch (type) {
+    case FPA_WRITE_TYPE_IMAGE:
+        return &hdu->images;
+    case FPA_WRITE_TYPE_MASK:
+        return &hdu->masks;
+    case FPA_WRITE_TYPE_WEIGHT:
+        return &hdu->weights;
+    default:
+        psAbort("Unknown write type: %x\n", type);
+    }
+    return NULL;
+}
+
+// Run the appropriate HDU write function
+static bool appropriateWriteFunc(pmHDU *hdu, // HDU to write
+                                 psFits *fits, // FITS file to which to write
+                                 const pmConfig *config, // Configuration
+                                 fpaWriteType type // Type to write
+                                )
+{
+    switch (type) {
+    case FPA_WRITE_TYPE_IMAGE:
+        return pmHDUWrite(hdu, fits, config);
+    case FPA_WRITE_TYPE_MASK:
+        return pmHDUWriteMask(hdu, fits, config);
+    case FPA_WRITE_TYPE_WEIGHT:
+        return pmHDUWriteWeight(hdu, fits, config);
+    default:
+        psAbort("Unknown write type: %x\n", type);
+    }
+    return false;
+}
+
+// Write a cell image/mask/weight
+static bool cellWrite(pmCell *cell,     // Cell to write
+                      psFits *fits,     // FITS file to which to write
+                      pmConfig *config, // Configuration
+                      bool blank,       // Write a blank PHU?
+                      fpaWriteType type // Type to write
+                     )
+{
+    assert(cell);
+    assert(fits);
+
+    psTrace ("pmFPAWrite", 5, "writing to Cell (%d)\n", blank);
+
+    pmHDU *hdu = cell->hdu;             // The HDU
+    if (!hdu || !cell->data_exists) {
+        return true;                    // We wrote every HDU that exists
+    }
+
+    psArray **imageArray = appropriateImageArray(hdu, type); // Array of images in the HDU
+
+    // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
+    // generate the HDU, but only copies the structure.
+    if (!blank && !hdu->blankPHU && !*imageArray && (!pmHDUGenerateForCell(cell) || !*imageArray)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate HDU for cell --- likely programming error.\n");
+        return false;
+    }
+
+    // We only write out a blank PHU if it's specifically requested.
+    bool writeBlank = blank && hdu->blankPHU && !*imageArray; // Write a blank PHU?
+    bool writeImage = !blank && !hdu->blankPHU && *imageArray; // Write an image?
+
+    if (writeBlank || writeImage) {
+
+        pmFPAUpdateNames(cell->parent->parent, cell->parent, cell);
+        pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
+                                 PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_DATABASE;
+        if (!pmConceptsWriteCell(cell, source, true, config)) {
+            psError(PS_ERR_IO, false, "Unable to write concepts for cell.\n");
+            return false;
+        }
+        if (!appropriateWriteFunc(hdu, fits, config, type)) {
+            psError(PS_ERR_IO, false, "Unable to write HDU for cell.\n");
+            return false;
+        }
+    }
+    // No lower levels to which to recurse
+
+    return true;
+}
+
+// Write a chip image/mask/weight
+static bool chipWrite(pmChip *chip,     // Chip to write
+                      psFits *fits,     // FITS file to which to write
+                      pmConfig *config, // Configuration
+                      bool blank,       // Write a blank PHU?
+                      bool recurse,     // Recurse to lower levels?
+                      fpaWriteType type // Type to write
+                     )
+{
+    assert(chip);
+    assert(fits);
+
+    pmHDU *hdu = chip->hdu;             // The HDU
+
+    psTrace ("pmFPAWrite", 5, "writing to Chip (%d, %d)\n", blank, recurse);
+
+    // If we have data at this level, try to write it out
+    if (hdu && chip->data_exists) {
+        psArray **imageArray = appropriateImageArray(hdu, type); // Array of images in HDU
+
+        // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
+        // generate the HDU, but only copies the structure.
+        if (!blank && !hdu->blankPHU && !*imageArray && (!pmHDUGenerateForChip(chip) || !*imageArray)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Unable to generate HDU for chip --- likely programming error.\n");
+            return false;
+        }
+
+        // We only write out a blank PHU if it's specifically requested.
+        bool writeBlank = blank && hdu->blankPHU && !*imageArray; // Write a blank HDU?
+        bool writeImage = !blank && !hdu->blankPHU && *imageArray; // Write an image?
+
+        if (writeBlank || writeImage) {
+            pmFPAUpdateNames(chip->parent, chip, NULL);
+            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)) {
+                psError(PS_ERR_IO, false, "Unable to write concepts for chip.\n");
+                return false;
+            }
+            if (!appropriateWriteFunc(hdu, fits, config, type)) {
+                psError(PS_ERR_IO, false, "Unable to write HDU for chip.\n");
+                return false;
+            }
+        }
+    }
+
+    // Recurse to lower level if specifically requested.
+    // XXX recursion implies blank == false (must be called on correct level?)
+    if (recurse) {
+        psArray *cells = chip->cells;       // Array of cells
+        for (int i = 0; i < cells->n; i++) {
+            pmCell *cell = cells->data[i];  // The cell of interest
+            if (!cellWrite(cell, fits, config, false, type)) {
+                psError(PS_ERR_IO, false, "Unable to write Chip.\n");
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+
+// Write an FPA image/mask/weight
+static bool fpaWrite(pmFPA *fpa,        // FPA to write
+                     psFits *fits,      // FITS file to which to write
+                     pmConfig *config,  // Configuration
+                     bool blank,        // Write a blank PHU?
+                     bool recurse,      // Recurse to lower levels?
+                     fpaWriteType type  // Type to write
+                    )
+{
+    assert(fpa);
+    assert(fits);
+
+    pmHDU *hdu = fpa->hdu;              // The HDU
+
+    psTrace ("pmFPAWrite", 5, "writing to FPA (%d, %d)\n", blank, recurse);
+
+    // If we have data at this level, try to write it out
+    if (hdu) {
+        psArray **imageArray = appropriateImageArray(hdu, type); // Array of images in HDU
+
+        // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
+        // generate the HDU, but only copies the structure.
+        if (!blank && !hdu->blankPHU && !*imageArray && (!pmHDUGenerateForFPA(fpa) || !*imageArray)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Unable to generate HDU for FPA --- likely programming error.\n");
+            return false;
+        }
+
+        // We only write out a blank PHU if it's specifically requested.
+        bool writeBlank = blank && hdu->blankPHU && !*imageArray; // Write a blank PHU?
+        bool writeImage = !blank && !hdu->blankPHU && *imageArray; // Write an image?
+
+        if (writeBlank || writeImage) {
+            pmFPAUpdateNames(fpa, NULL, NULL);
+            pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
+                                     PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_DATABASE;
+            if (!pmConceptsWriteFPA(fpa, source, true, config)) {
+                psError(PS_ERR_IO, false, "Unable to write concepts for FPA.\n");
+                return false;
+            }
+            if (!appropriateWriteFunc(hdu, fits, config, type))  {
+                psError(PS_ERR_IO, false, "Unable to write HDU for FPA.\n");
+                return false;
+            }
+        }
+    }
+
+    // Recurse to lower levels if requested
+    if (recurse) {
+        psArray *chips = fpa->chips;        // Array of chips
+        for (int i = 0; i < chips->n; i++) {
+            pmChip *chip = chips->data[i];  // The chip of interest
+            if (!chipWrite(chip, fits, config, false, true, type)) {
+                psError(PS_ERR_IO, false, "Unable to write FPA.\n");
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Update the FPA.OBS, CHIP.NAME and CELL.NAME in the FITS header, if required
+bool pmFPAUpdateNames(pmFPA *fpa, pmChip *chip, pmCell *cell)
+{
+    pmHDU *hdu = pmHDUGetHighest(fpa, chip, cell); // Highest HDU, i.e., the PHU
+    if (!hdu) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find HDU.\n");
+        return false;
+    }
+    if (!hdu->header) {
+        hdu->header = psMetadataAlloc();
+    }
+    bool mdok;                          // Status of MD lookup
+    psMetadata *fileData = psMetadataLookupMetadata(&mdok, hdu->format, "FILE"); // File information
+    if (!mdok || !fileData) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find FILE information in camera format.\n");
+        return false;
+    }
+    if (fpa) {
+        const char *fpaObsHdr = psMetadataLookupStr(&mdok, fileData, "FPA.OBS");
+        if (mdok && fpaObsHdr && strlen(fpaObsHdr) > 0) {
+            const char *fpaObs = psMetadataLookupStr(NULL, fpa->concepts, "FPA.OBS");
+            psMetadataAddStr(hdu->header, PS_LIST_TAIL, fpaObsHdr, PS_META_REPLACE,
+                             "Observation identifier", fpaObs);
+        }
+    }
+
+    if (fpa && !fpa->hdu && (chip || cell)) {
+        const char *rule = psMetadataLookupStr(NULL, fileData, "CONTENT.RULE"); // How to define the CONTENT
+        if (!rule) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to find CONTENT.RULE in FILE in camera format.");
+            return false;
+        }
+
+        pmFPAview *view = pmFPAviewGenerate(fpa, chip, cell, NULL); // View for fpa, chip, cell
+        psString content = pmFPANameFromRule(rule, fpa, view); // Content of this file, specified by the rule
+        psFree(view);
+
+        const char *contentKey = psMetadataLookupStr(NULL, fileData, "CONTENT"); // The CONTENT header keyword
+        if (!contentKey) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to find CONTENT in FILE in the camera format.");
+            psFree(content);
+            return false;
+        }
+
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, contentKey, PS_META_REPLACE, "Content of file", content);
+        psFree(content);                // Drop reference
+    }
+
+    return true;
+}
+
+bool pmReadoutWriteNext(pmReadout *readout, psFits *fits, int z)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    pmHDU *hdu = pmHDUFromReadout(readout); // The HDU to which to write
+    if (!hdu) {
+        psError(PS_ERR_IO, false, "Unable to find HDU for readout.\n");
+        return false;
+    }
+    psMetadata *header = hdu->header;   // The FITS header
+    if (!header) {
+        psError(PS_ERR_IO, true, "No FITS header available in the HDU.\n");
+        return false;
+    }
+
+    // We have to rely to a great extent on the FITS header, because otherwise we simply don't know how many
+    // image planes there are (NAXIS3) or the size of the original image (NAXIS1, NAXIS2).
+    bool mdok = true;                   // Status of MD lookup
+    int naxis1 = psMetadataLookupS32(&mdok, header, "NAXIS1"); // Number of columns
+    if (!mdok || naxis1 <= 0) {
+        psError(PS_ERR_IO, true, "Can't find NAXIS1 in header.\n");
+        return false;
+    }
+    int naxis2 = psMetadataLookupS32(&mdok, header, "NAXIS2"); // Number of rows
+    if (!mdok || naxis2 <= 0) {
+        psError(PS_ERR_IO, true, "Can't find NAXIS2 in header.\n");
+        return false;
+    }
+    int naxis3 = psMetadataLookupS32(&mdok, header, "NAXIS3"); // Number of image planes
+    if (!mdok || naxis3 <= 0) {
+        naxis3 = 1;
+    }
+    if (z >= naxis3) {
+        psError(PS_ERR_IO, true, "Specified a plane number (%d) greater than NAXIS3 allows.\n", z);
+        return false;
+    }
+
+    if (!hdu->images) {
+        psError(PS_ERR_IO, true, "No images allocated in HDU.\n");
+        return false;
+    }
+    psImage *image = readout->image;    // The image from the HDU to write
+    //    psImage *mask = readout->mask;        // Corresponding mask image
+    if (readout->row0 == 0 && readout->col0 == 0 && z == 0) {
+        // Then we can assume that nothing has been written to the FITS file for now
+        if (naxis1 == image->numCols && naxis2 == image->numRows) {
+            // We can write the whole lot at once
+            return psFitsWriteImage(fits, header, image, z, hdu->extname);
+        }
+        // Create a dummy image so we can write something larger than we actually have
+        psImage *dummy = psImageAlloc(naxis1, naxis2, image->type.type); // Dummy image
+        psImageInit(dummy, 0);
+        psImageOverlaySection(dummy, image, 0, 0, "=");
+        bool result = psFitsWriteImage(fits, header, dummy, z, hdu->extname);
+        psFree(dummy);
+        return result;
+    }
+
+    // We can simply update an existing HDU
+    if (hdu->blankPHU && !psFitsMoveExtNum(fits, 0, false)) {
+        psError(PS_ERR_IO, false, "Unable to move to PHU\n");
+        return false;
+    }
+    return psFitsUpdateImage(fits, image, readout->col0, readout->row0, z);
+}
+
+
+bool pmCellWrite(pmCell *cell, psFits *fits, pmConfig *config, bool blank)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return cellWrite(cell, fits, config, blank, FPA_WRITE_TYPE_IMAGE);
+}
+
+bool pmChipWrite(pmChip *chip, psFits *fits, pmConfig *config, bool blank, bool recurse)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return chipWrite(chip, fits, config, blank, recurse, FPA_WRITE_TYPE_IMAGE);
+}
+
+bool pmFPAWrite(pmFPA *fpa, psFits *fits, pmConfig *config, bool blank, bool recurse)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return fpaWrite(fpa, fits, config, blank, recurse, FPA_WRITE_TYPE_IMAGE);
+}
+
+
+bool pmCellWriteMask(pmCell *cell, psFits *fits, pmConfig *config, bool blank)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return cellWrite(cell, fits, config, blank, FPA_WRITE_TYPE_MASK);
+}
+
+bool pmChipWriteMask(pmChip *chip, psFits *fits, pmConfig *config, bool blank, bool recurse)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return chipWrite(chip, fits, config, blank, recurse, FPA_WRITE_TYPE_MASK);
+}
+
+bool pmFPAWriteMask(pmFPA *fpa, psFits *fits, pmConfig *config, bool blank, bool recurse)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return fpaWrite(fpa, fits, config, blank, recurse, FPA_WRITE_TYPE_MASK);
+}
+
+
+bool pmCellWriteWeight(pmCell *cell, psFits *fits, pmConfig *config, bool blank)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return cellWrite(cell, fits, config, blank, FPA_WRITE_TYPE_WEIGHT);
+}
+
+bool pmChipWriteWeight(pmChip *chip, psFits *fits, pmConfig *config, bool blank, bool recurse)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return chipWrite(chip, fits, config, blank, recurse, FPA_WRITE_TYPE_WEIGHT);
+}
+
+bool pmFPAWriteWeight(pmFPA *fpa, psFits *fits, pmConfig *config, bool blank, bool recurse)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return fpaWrite(fpa, fits, config, blank, recurse, FPA_WRITE_TYPE_WEIGHT);
+}
+
+
+int pmCellWriteTable(psFits *fits, const pmCell *cell, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, 0);
+    PS_ASSERT_PTR_NON_NULL(fits, 0);
+    PS_ASSERT_STRING_NON_EMPTY(name, 0);
+
+    const char *chipName = psMetadataLookupStr(NULL, cell->parent->concepts, "CHIP.NAME"); // Name of chip
+    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
+
+    psArray *table = psMetadataLookupPtr(NULL, cell->analysis, name); // The FITS table
+    if (!table) {
+        // We wrote everything we could find
+        return 0;
+    }
+
+    psString headerName = NULL;         // Name for header in analysis metadata
+    psStringAppend(&headerName, "%s.HEADER", name);
+    psMetadata *header = psMetadataLookupMetadata(NULL, cell->analysis, headerName); // The FITS header
+    psFree(headerName);
+
+    psString extname = NULL;            // Extension name
+    psStringAppend(&extname, "%s_%s_%s", name, chipName, cellName);
+
+    // XXX Could do a table lookup from the camera format, in case the input file isn't laid out with
+    // NAME_CHIP_CELL extension names --- use these as keys, and the value as the proper extension name.
+    // Allow interpolation of concepts, e.g., "{CHIP.NAME}" --> "ccd13".
+
+    if (!psFitsWriteTable(fits, header, table, extname)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to write table from chip %s, cell %s to extension %s\n",
+                chipName, cellName, extname);
+        psFree(extname);
+        return 0;
+    }
+
+    psFree(extname);
+    return 1;
+}
+
+
+int pmChipWriteTable(psFits *fits, const pmChip *chip, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, 0);
+    PS_ASSERT_PTR_NON_NULL(fits, 0);
+    PS_ASSERT_STRING_NON_EMPTY(name, 0);
+
+    int numWrite = 0;                    // Number of reads
+    psArray *cells = chip->cells;       // Array of cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        numWrite += pmCellWriteTable(fits, cell, name);
+    }
+
+    return numWrite;
+}
+
+
+int pmFPAWriteTable(psFits *fits, const pmFPA *fpa, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, 0);
+    PS_ASSERT_PTR_NON_NULL(fits, 0);
+    PS_ASSERT_STRING_NON_EMPTY(name, 0);
+
+    int numWrite = 0;                    // Number of reads
+    psArray *chips = fpa->chips;        // Array of chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // Chip of interest
+        numWrite += pmChipWriteTable(fits, chip, name);
+    }
+
+    return numWrite;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAWrite.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAWrite.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAWrite.h	(revision 22322)
@@ -0,0 +1,171 @@
+/* @file pmFPAWrite.h
+ * @brief Write FPA components to a FITS file
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-17 22:16:38 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_WRITE_H
+#define PM_FPA_WRITE_H
+
+#include <pmConfig.h>
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Write a readout incrementally
+///
+/// Writes a readout to a FITS file, perhaps incrementally (if it has been read in using pmReadoutReadNext).
+/// Relies on the FITS header to specify how many image planes there are, and the width and height.
+bool pmReadoutWriteNext(pmReadout *readout, ///< Readout to write
+                        psFits *fits,   ///<  FITS file to which to write
+                        int z           ///<  Image plane to write
+                       );
+
+/// Write a cell to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU pixels if required.  A blank (i.e., image-less header) is
+/// written only if specifically requested.  Writes the concepts to the various locations, and then the HDU to
+/// the FITS file.  This function should be called at the beginning of the output cell loop with blank=true in
+/// order to produce the correct file structure.
+bool pmCellWrite(pmCell *cell,          ///<  Cell to write
+                 psFits *fits,          ///<  FITS file to which to write
+                 pmConfig *config,      ///<  Configuration
+                 bool blank             ///<  Write a blank PHU?
+                );
+
+/// Write a chip to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU pixels if required.  A blank (i.e., image-less header) is
+/// written only if specifically requested.  Writes the concepts to the various locations, and then the HDU to
+/// the FITS file, optionally recursing to lower levels.  This function should be called at the beginning of
+/// the output chip loop with blank=true and recurse=false in order to produce the correct file structure.
+bool pmChipWrite(pmChip *chip,          ///<  Chip to write
+                 psFits *fits,          ///<  FITS file to which to write
+                 pmConfig *config,      ///<  Configuration
+                 bool blank,            ///<  Write a blank PHU?
+                 bool recurse           ///<  Recurse to lower levels?
+                );
+
+/// Write an FPA to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU pixels if required.  A blank (i.e., image-less header) is
+/// written only if specifically requested.  Writes the concepts to the various locations, and then the HDU to
+/// the FITS file, optionally recursing to lower levels.  This function should be called at the beginning of
+/// the output FPA loop with blank=true and recurse=false in order to produce the correct file structure.
+bool pmFPAWrite(pmFPA *fpa,             ///<  FPA to write
+                psFits *fits,           ///<  FITS file to which to write
+                pmConfig *config,       ///<  Configuration
+                bool blank,             ///<  Write a blank PHU?
+                bool recurse            ///<  Recurse to lower levels?
+               );
+
+/// Write a cell mask to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU mask pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU mask to the FITS file.  This function should be called at the beginning of the output cell loop
+/// with blank=true in order to produce the correct file structure.
+bool pmCellWriteMask(pmCell *cell,      ///<  Cell to write
+                     psFits *fits,      ///<  FITS file to which to write
+                     pmConfig *config,  ///<  Configuration
+                     bool blank         ///<  Write a blank PHU?
+                    );
+
+/// Write a chip mask to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU mask pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU mask to the FITS file, optionally recursing to lower levels.  This function should be called at
+/// the beginning of the output chip loop with blank=true and recurse=false in order to produce the correct
+/// file structure.
+bool pmChipWriteMask(pmChip *chip,      ///<  Chip to write
+                     psFits *fits,      ///<  FITS file to which to write
+                     pmConfig *config,  ///<  Configuration
+                     bool blank,        ///<  Write a blank PHU?
+                     bool recurse       ///<  Recurse to lower levels?
+                    );
+
+/// Write an FPA mask to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU mask pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU mask to the FITS file, optionally recursing to lower levels.  This function should be called at
+/// the beginning of the output FPA loop with blank=true and recurse=false in order to produce the correct
+/// file structure.
+bool pmFPAWriteMask(pmFPA *fpa,         ///<  FPA to write
+                    psFits *fits,       ///<  FITS file to which to write
+                    pmConfig *config,   ///<  Configuration
+                    bool blank,         ///<  Write a blank PHU?
+                    bool recurse        ///<  Recurse to lower levels?
+                   );
+
+/// Write a cell weight to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU weight pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU weight to the FITS file.  This function should be called at the beginning of the output cell loop
+/// with blank=true in order to produce the correct file structure.
+bool pmCellWriteWeight(pmCell *cell,    ///<  Cell to write
+                       psFits *fits,    ///<  FITS file to which to write
+                       pmConfig *config, ///<  Configuration
+                       bool blank       ///<  Write a blank PHU?
+                      );
+
+/// Write a chip weight to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU weight pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU weight to the FITS file, optionally recursing to lower levels.  This function should be called at
+/// the beginning of the output chip loop with blank=true and recurse=false in order to produce the correct
+/// file structure.
+bool pmChipWriteWeight(pmChip *chip,    ///<  Chip to write
+                       psFits *fits,    ///<  FITS file to which to write
+                       pmConfig *config, ///<  Configuration
+                       bool blank,      ///<  Write a blank PHU?
+                       bool recurse     ///<  Recurse to lower levels?
+                      );
+
+/// Write an FPA weight to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU weight pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU weight to the FITS file, optionally recursing to lower levels.  This function should be called at
+/// the beginning of the output FPA loop with blank=true and recurse=false in order to produce the correct
+/// file structure.
+bool pmFPAWriteWeight(pmFPA *fpa,       ///<  FPA to write
+                      psFits *fits,     ///<  FITS file to which to write
+                      pmConfig *config, ///<  Configuration
+                      bool blank,       ///<  Write a blank PHU?
+                      bool recurse      ///<  Recurse to lower levels?
+                     );
+
+
+/// Write a FITS table from the cell's analysis metadata.
+///
+/// The FITS table (a psArray of psMetadatas) from the cell's analysis metadata (under "name") is written to
+/// the FITS file, at an extension specified by the name, chip name and cell name ("NAME_CHIP_CELL").  If a
+/// header is present in the analysis metadata ("name.HEADER"), then it is written also.
+int pmCellWriteTable(psFits *fits,      ///< FITS file to which to write
+                     const pmCell *cell, ///< Cell containing FITS table in the analysis metadata
+                     const char *name   ///< Name for the table data, and the extension name
+                    );
+
+int pmChipWriteTable(psFits *fits,      ///< FITS file to which to write
+                     const pmChip *chip, ///< Chip containing cells with tables to write
+                     const char *name   ///< Name for the table data, and the extension name
+                    );
+
+int pmFPAWriteTable(psFits *fits,       ///< FITS file to which to write
+                    const pmFPA *fpa,   ///< FPA containing cells with tables to write
+                    const char *name    ///< Name for the table data, and the extension name
+                   );
+
+// XXX better name, please
+bool pmFPAUpdateNames(pmFPA *fpa, pmChip *chip, pmCell *cell);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_JPEG.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_JPEG.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_JPEG.c	(revision 22322)
@@ -0,0 +1,234 @@
+/** @file  pmFPA_JPEG.c
+ *
+ * This file contains functions to write JPEG images.
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.26 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-23 02:00:36 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmConfigMask.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmFPA_JPEG.h"
+
+
+bool pmFPAviewWriteJPEG(const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        pmFPAWriteJPEG (fpa, view, file, config);
+        return true;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        pmChipWriteJPEG (chip, view, file, config);
+        return true;
+    }
+
+    if (view->cell >= chip->cells->n) {
+        return false;
+    }
+    pmCell *cell = chip->cells->data[view->cell];
+
+    if (view->readout == -1) {
+        pmCellWriteJPEG (cell, view, file, config);
+        return true;
+    }
+
+    if (view->readout >= cell->readouts->n) {
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[view->readout];
+
+    pmReadoutWriteJPEG (readout, view, file, config);
+    return true;
+}
+
+// read in all chip-level JPEG files for this FPA
+bool pmFPAWriteJPEG (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+
+        pmChip *chip = fpa->chips->data[i];
+        pmChipWriteJPEG (chip, view, file, config);
+    }
+    return true;
+}
+
+// read in all cell-level JPEG files for this chip
+bool pmChipWriteJPEG (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    for (int i = 0; i < chip->cells->n; i++) {
+
+        pmCell *cell = chip->cells->data[i];
+        pmCellWriteJPEG (cell, view, file, config);
+    }
+    return true;
+}
+
+// read in all readout-level JPEG files for this cell
+bool pmCellWriteJPEG (pmCell *cell, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    for (int i = 0; i < cell->readouts->n; i++) {
+
+        pmReadout *readout = cell->readouts->data[i];
+        pmReadoutWriteJPEG (readout, view, file, config);
+    }
+    return true;
+}
+
+// write JPEG image for readout
+// note that this function will try as hard a possible to write out a jpeg.  it will not fail
+// just because the values are in a poor range.  it is more convenient to know you are getting
+// a jpeg which is weird than to fail to get the file at all.
+bool pmReadoutWriteJPEG (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    bool status;
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    if (file->type != PM_FPA_FILE_JPEG) {
+        psError(PS_ERR_UNKNOWN, true, "File is not of type JPEG");
+        return false;
+    }
+
+    psTrace("psModules.camera", 3, "writing jpeg for readout %p\n", readout);
+
+    psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, "JPEG");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find JPEG recipe");
+        return false;
+    }
+
+    psMetadata *options = psMetadataLookupMetadata(&status, recipe, file->name);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find options for file %s in JPEG recipe", file->name);
+        return false;
+    }
+
+    // measure the image statistics for scaling
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
+    stats->nSubsample = 10000;
+    psMaskType maskVal = pmConfigMaskGet("MASK.VALUE", config); // Value to mask
+    float mean = 0, delta = 0;          //
+    if (!psImageBackground(stats, NULL, readout->image, readout->mask, maskVal, rng)) {
+        psStats *statsAlt = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        stats->nSubsample = 10000;
+        if (!psImageBackground(stats, NULL, readout->image, readout->mask, maskVal, rng)) {
+            psLogMsg("psModules.jpeg", PS_LOG_WARN,
+                     "Unable to measure statistics for image; writing blank jpeg");
+            mean = 0;
+            delta = 1;
+        } else {
+            mean = statsAlt->sampleMean;
+            delta = 2.0 * statsAlt->sampleStdev;
+        }
+        psFree(statsAlt);
+    } else {
+        mean = stats->robustMedian;
+        delta = stats->robustUQ - stats->robustLQ;
+    }
+    psFree(rng);
+    psFree(stats);
+
+    char *colormapName = psMetadataLookupStr(NULL, options, "COLORMAP"); // Name of colour map
+    if (!colormapName) {
+        colormapName = "-greyscale";
+    }
+    char *mode = psMetadataLookupStr(NULL, options, "SCALE.MODE"); // Mode for scaling image
+    if (!mode) {
+        mode = "RANGE";
+    }
+    float fmin = psMetadataLookupF32(&status, options, "SCALE.MIN"); // Minimum value/faction for scaling
+    if (!status) {
+        fmin = -3.0;
+    }
+    float fmax = psMetadataLookupF32(&status, options, "SCALE.MAX"); // Maximum value/faction for scaling
+    if (!status) {
+        fmax = +6.0;
+    }
+
+    float min = 0, max = 0;             // Minimum and maximum for stretch
+
+    if (!strcasecmp(mode, "RANGE")) {
+        min = mean + fmin*delta;
+        max = mean + fmax*delta;
+    } else if (!strcasecmp(mode, "FRACTION")) {
+        min = fmin*mean;
+        max = fmax*mean;
+    } else if (!strcasecmp(mode, "VALUE")) {
+        min = fmin;
+        max = fmax;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised scaling mode: %s", mode);
+        return false;
+    }
+
+    if (!isfinite(min) || !isfinite(max)) {
+        psLogMsg("psModules.jpeg", PS_LOG_WARN,
+                 "The stretch parameters are not both finite --- writing blank jpeg");
+        min = 0;
+        max = 1;
+    }
+
+    psImageJpegColormap *map = psImageJpegColormapSet(NULL, colormapName);
+    if (!map) {
+        map = psImageJpegColormapSet(NULL, "-greyscale");
+    }
+
+    if (!psImageJpeg(map, readout->image, file->filename, min, max)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to write JPEG image");
+        psFree(map);
+        return false;
+    }
+
+    psFree(map);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_JPEG.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_JPEG.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_JPEG.h	(revision 22322)
@@ -0,0 +1,24 @@
+/* @file  pmFPAview.h
+ * @brief Tools to manipulate the FPA structure elements.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-04-14 03:22:47 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_JPEG_H
+#define PM_FPA_JPEG_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+bool pmFPAviewWriteJPEG (const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmFPAWriteJPEG (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmChipWriteJPEG (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmCellWriteJPEG (pmCell *cell, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmReadoutWriteJPEG (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_MANAPLOT.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_MANAPLOT.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_MANAPLOT.c	(revision 22322)
@@ -0,0 +1,176 @@
+/** @file  pmFPA_MANAPLOT.c
+ *
+ * This file contains functions to write MANAPLOT images.
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-06-10 20:58:28 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAfile.h"
+#include "pmFPAview.h"
+#include "pmFPA_MANAPLOT.h"
+
+
+bool pmFPAviewWriteMANAPLOT(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        pmFPAWriteMANAPLOT (fpa, view, file, config);
+        return true;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        pmChipWriteMANAPLOT (chip, view, file, config);
+        return true;
+    }
+
+    if (view->cell >= chip->cells->n) {
+        return false;
+    }
+    pmCell *cell = chip->cells->data[view->cell];
+
+    if (view->readout == -1) {
+        pmCellWriteMANAPLOT (cell, view, file, config);
+        return true;
+    }
+
+    if (view->readout >= cell->readouts->n) {
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[view->readout];
+
+    pmReadoutWriteMANAPLOT (readout, view, file, config);
+    return true;
+}
+
+// read in all chip-level MANAPLOT files for this FPA
+bool pmFPAWriteMANAPLOT (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+
+        pmChip *chip = fpa->chips->data[i];
+        pmChipWriteMANAPLOT (chip, view, file, config);
+    }
+    return true;
+}
+
+// read in all cell-level MANAPLOT files for this chip
+bool pmChipWriteMANAPLOT (pmChip *chip, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    for (int i = 0; i < chip->cells->n; i++) {
+
+        pmCell *cell = chip->cells->data[i];
+        pmCellWriteMANAPLOT (cell, view, file, config);
+    }
+    return true;
+}
+
+// read in all readout-level MANAPLOT files for this cell
+bool pmCellWriteMANAPLOT (pmCell *cell, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    for (int i = 0; i < cell->readouts->n; i++) {
+
+        pmReadout *readout = cell->readouts->data[i];
+        pmReadoutWriteMANAPLOT (readout, view, file, config);
+    }
+    return true;
+}
+
+// read in all readout-level Objects files for this cell
+bool pmReadoutWriteMANAPLOT (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    if (file->type != PM_FPA_FILE_MANAPLOT) {
+        psError(PS_ERR_UNKNOWN, true, "warning: type mismatch");
+        return false;
+    }
+
+    // XXX does not have to be a mana script...
+    // this function will call a mana script of the form:
+    // scriptname (input) (output)
+    // scriptname : extname
+    // input : filextra
+    // output : filerule
+
+    bool create = file->mode == PM_FPA_MODE_WRITE ? true : false;
+
+    psString tmpName;
+    tmpName = pmFPAfileNameFromRule (file->filextra, file, view);
+    psString input = pmConfigConvertFilename (tmpName, config, create, false);
+    psFree (tmpName);
+
+    tmpName = pmFPAfileNameFromRule (file->filerule, file, view);
+    psString output = pmConfigConvertFilename (tmpName, config, create, false);
+    psFree (tmpName);
+
+    tmpName = pmFPAfileNameFromRule (file->extname, file, view);
+    psString script = pmConfigConvertFilename (tmpName, config, create, false);
+    psFree (tmpName);
+
+    psString line = NULL;
+    psStringAppend (&line, "%s %s %s", script, input, output);
+
+    // capture the stdout and stderr?
+    // XXX use psPipe instead?
+    int status = system (line);
+    if (status == -1) {
+        psError(PS_ERR_UNKNOWN, true, "fork failure: %s", script);
+        return false;
+    }
+    if (WEXITSTATUS(status) != 0) {
+        psError(PS_ERR_UNKNOWN, true, "error running: %s", script);
+        return false;
+    }
+
+    psFree(line);
+    psFree(input);
+    psFree(output);
+    psFree(script);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_MANAPLOT.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_MANAPLOT.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPA_MANAPLOT.h	(revision 22322)
@@ -0,0 +1,24 @@
+/* @file  pmFPAview.h
+ * @brief Tools to manipulate the FPA structure elements.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-24 02:54:14 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_MANAPLOT_H
+#define PM_FPA_MANAPLOT_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+bool pmFPAviewWriteMANAPLOT (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmFPAWriteMANAPLOT (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmChipWriteMANAPLOT (pmChip *chip, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmCellWriteMANAPLOT (pmCell *cell, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmReadoutWriteMANAPLOT (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfile.c	(revision 22322)
@@ -0,0 +1,607 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmFPACopy.h"
+#include "pmConcepts.h"
+
+static void pmFPAfileFree(pmFPAfile *file)
+{
+    if (!file) {
+        return;
+    }
+
+    psTrace ("pmFPAfileFree", 5, "freeing %s\n", file->name);
+    psFree (file->fpa);
+    psFree (file->src);
+    psFree (file->readout);
+    psFree (file->names);
+
+    psFree (file->camera);
+    psFree (file->cameraName);
+    psFree (file->format);
+    psFree (file->formatName);
+    psFree (file->name);
+
+    if (file->fits != NULL) {
+        psFitsClose (file->fits);
+    }
+    psFree(file->compression);
+    psFree(file->options);
+
+    psFree (file->filerule);
+
+    psFree (file->filesrc);
+    psFree (file->detrend);
+
+    psFree (file->filename);
+    psFree (file->extname);
+
+    return;
+}
+
+pmFPAfile *pmFPAfileAlloc()
+{
+    pmFPAfile *file = psAlloc(sizeof(pmFPAfile));
+    psMemSetDeallocator(file, (psFreeFunc) pmFPAfileFree);
+
+    file->wrote_phu = false;
+    file->readout = NULL;
+    file->header = NULL;
+
+    file->fileLevel = PM_FPA_LEVEL_NONE;
+    file->dataLevel = PM_FPA_LEVEL_NONE;
+    file->freeLevel = PM_FPA_LEVEL_NONE;
+    file->mosaicLevel = PM_FPA_LEVEL_NONE;
+
+    file->type = PM_FPA_FILE_NONE;
+    file->mode = PM_FPA_MODE_NONE;
+    file->state = PM_FPA_STATE_CLOSED;
+
+    file->fpa = NULL;
+    file->fits = NULL;
+    file->compression = NULL;
+    file->options = NULL;
+    file->names = psMetadataAlloc();
+
+    file->camera = NULL;
+    file->cameraName = NULL;
+    file->format = NULL;
+    file->formatName = NULL;
+    file->name = NULL;
+
+    file->filerule = NULL;
+
+    file->filename = NULL;
+    file->extname  = NULL;
+
+    file->filesrc = NULL;
+    file->detrend = NULL;
+
+    file->xBin = 1;
+    file->yBin = 1;
+    file->src = NULL;
+
+    file->save = false;
+
+    return file;
+}
+
+// select the readout from the named pmFPAfile; if the named file does not exist,
+pmReadout *pmFPAfileThisReadout (psMetadata *files, const pmFPAview *view, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(files, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(name, false);
+    PS_ASSERT_INT_POSITIVE(strlen(name), false);
+
+    bool status;
+
+    pmFPAfile *file = psMetadataLookupPtr (&status, files, name);
+    if (file == NULL) {
+        return NULL;
+    }
+
+    // internal files have the readout as a separate element:
+    if (file->mode == PM_FPA_MODE_INTERNAL) {
+        return file->readout;
+    }
+
+    pmReadout *readout = pmFPAviewThisReadout (view, file->fpa);
+    return readout;
+}
+
+// select the cell from the named pmFPAfile; if the named file does not exist,
+pmCell *pmFPAfileThisCell (psMetadata *files, const pmFPAview *view, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(files, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(name, false);
+    PS_ASSERT_INT_POSITIVE(strlen(name), false);
+
+    bool status;
+
+    pmFPAfile *file = psMetadataLookupPtr (&status, files, name);
+    if (file == NULL) {
+        return NULL;
+    }
+
+    // internal files have the readout as a separate element:
+    if (file->mode == PM_FPA_MODE_INTERNAL) {
+        return NULL;
+    }
+
+    pmCell *cell = pmFPAviewThisCell(view, file->fpa);
+    return cell;
+}
+
+// select the readout from the named pmFPAfile; if the named file does not exist,
+pmChip *pmFPAfileThisChip (psMetadata *files, const pmFPAview *view, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(files, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(name, false);
+    PS_ASSERT_INT_POSITIVE(strlen(name), false);
+
+    bool status;
+
+    pmFPAfile *file = psMetadataLookupPtr (&status, files, name);
+    if (file == NULL) {
+        return NULL;
+    }
+
+    // internal files have the readout as a separate element:
+    if (file->mode == PM_FPA_MODE_INTERNAL) {
+        return NULL;
+    }
+
+    pmChip *chip = pmFPAviewThisChip (view, file->fpa);
+    return chip;
+}
+
+psString pmFPANameFromRule(const char *rule, const pmFPA *fpa, const pmFPAview *view)
+{
+    PS_ASSERT_STRING_NON_EMPTY(rule, NULL);
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    psString newName = NULL;            // New name, to be returned
+    newName = psStringCopy(rule);
+
+    if (strstr(newName, "{FPA.OBS}")) {
+        char *name = psMetadataLookupStr(NULL, fpa->concepts, "FPA.OBS");
+        if (name) {
+            psStringSubstitute(&newName, name, "{FPA.OBS}");
+        }
+    }
+    if (strstr(newName, "{FPA.NAME}")) {
+        char *name = psMetadataLookupStr(NULL, fpa->concepts, "FPA.NAME");
+        if (name) {
+            psStringSubstitute(&newName, name, "{FPA.NAME}");
+        }
+    }
+    if (strstr(newName, "{CHIP.NAME}")) {
+        pmChip *chip = pmFPAviewThisChip(view, fpa);
+        if (chip) {
+            char *name = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
+            if (name) {
+                psStringSubstitute(&newName, name, "{CHIP.NAME}");
+            }
+        }
+    }
+    if (strstr(newName, "{CHIP.ID}")) {
+        pmChip *chip = pmFPAviewThisChip(view, fpa);
+        if (chip) {
+            char *name = psMetadataLookupStr(NULL, chip->concepts, "CHIP.ID");
+            if (name) {
+                psStringSubstitute(&newName, name, "{CHIP.ID}");
+            }
+        }
+    }
+    if (strstr(newName, "{CHIP.N}")) {
+        char *name = NULL;
+        if (view->chip < 0) {
+            psStringAppend(&name, "XX");
+        } else {
+            psStringAppend(&name, "%02d", view->chip);
+        }
+        psStringSubstitute(&newName, name, "{CHIP.N}");
+        psFree(name);
+    }
+    if (strstr(newName, "{CHIP.NUM}")) {
+        char *name = NULL;
+        if (view->chip < 0) {
+            psStringAppend(&name, "XX");
+        } else {
+            int chipNum = view->chip;   // Number of chip
+            // Potential correction for fortran (unit-indexed) numbering
+            pmChip *chip = pmFPAviewThisChip(view, fpa); // Chip of interest
+            pmHDU *hdu = pmHDUFromChip(chip); // Corresponding HDU
+            bool mdok;                  // Status of MD lookup
+            psMetadata *formats = psMetadataLookupMetadata(&mdok, hdu->format, "FORMATS"); // Special formats
+            if (mdok && formats) {
+                const char *format = psMetadataLookupStr(&mdok, formats, "CHIP.NUM"); // Format for CHIP.NUM
+                if (mdok && format && strcmp(format, "FORTRAN") == 0) {
+                    chipNum++;
+                }
+            }
+            psStringAppend(&name, "%d", chipNum);
+        }
+        psStringSubstitute(&newName, name, "{CHIP.NUM}");
+        psFree(name);
+    }
+    if (strstr(newName, "{CELL.NAME}")) {
+        pmCell *cell = pmFPAviewThisCell(view, fpa);
+        if (cell) {
+            char *name = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME");
+            if (name) {
+                psStringSubstitute(&newName, name, "{CELL.NAME}");
+            }
+        }
+    }
+    if (strstr(newName, "{CELL.N}")) {
+        char *name = NULL;
+        if (view->cell < 0) {
+            psStringAppend(&name, "XX");
+        } else {
+            psStringAppend(&name, "%02d", view->cell);
+        }
+        psStringSubstitute(&newName, name, "{CELL.N}");
+    }
+    if (strstr(newName, "{CELL.NUM}")) {
+        char *name = NULL;
+        if (view->cell < 0) {
+            psStringAppend(&name, "XX");
+        } else {
+            int cellNum = view->cell;   // Number of cell
+            // Potential correction for fortran (unit-indexed) numbering
+            pmCell *cell = pmFPAviewThisCell(view, fpa); // Cell of interest
+            pmHDU *hdu = pmHDUFromCell(cell); // Corresponding HDU
+            bool mdok;                  // Status of MD lookup
+            psMetadata *formats = psMetadataLookupMetadata(&mdok, hdu->format, "FORMATS"); // Special formats
+            if (mdok && formats) {
+                const char *format = psMetadataLookupStr(&mdok, formats, "CELL.NUM"); // Format for CELL.NUM
+                if (mdok && format && strcmp(format, "FORTRAN") == 0) {
+                    cellNum++;
+                }
+            }
+            psStringAppend(&name, "%d", cellNum);
+        }
+        psStringSubstitute(&newName, name, "{CELL.NUM}");
+    }
+    if (strstr(newName, "{EXTNAME}")) {
+        pmHDU *hdu = pmFPAviewThisHDU(view, fpa);
+        if (hdu->extname && *hdu->extname) {
+            psStringSubstitute(&newName, hdu->extname, "{EXTNAME}");
+        }
+    }
+    if (strstr(newName, "{FILTER}") && fpa) {
+        char *name = psMetadataLookupStr(NULL, fpa->concepts, "FPA.FILTER");
+        if (name && *name) {
+            psStringSubstitute(&newName, name, "{FILTER}");
+        }
+    }
+    if (strstr(newName, "{FILTER.ID}") && fpa) {
+        char *name = psMetadataLookupStr(NULL, fpa->concepts, "FPA.FILTERID");
+        if (name && *name) {
+            psStringSubstitute(&newName, name, "{FILTER.ID}");
+        }
+    }
+    if (strstr(newName, "{CAMERA}") && fpa) {
+        char *name = psMetadataLookupStr(NULL, fpa->concepts, "FPA.INSTRUMENT");
+        if (name && *name) {
+            psStringSubstitute(&newName, name, "{CAMERA}");
+        }
+    }
+    if (strstr(newName, "{INSTRUMENT}") && fpa) {
+        char *name = psMetadataLookupStr(NULL, fpa->concepts, "FPA.INSTRUMENT");
+        if (name && *name) {
+            psStringSubstitute(&newName, name, "{INSTRUMENT}");
+        }
+    }
+    if (strstr(newName, "{DETECTOR}") && fpa) {
+        char *name = psMetadataLookupStr(NULL, fpa->concepts, "FPA.DETECTOR");
+        if (name && *name) {
+            psStringSubstitute(&newName, name, "{DETECTOR}");
+        }
+    }
+    if (strstr(newName, "{TELESCOPE}") && fpa) {
+        char *name = psMetadataLookupStr(NULL, fpa->concepts, "FPA.TELESCOPE");
+        if (name && *name) {
+            psStringSubstitute(&newName, name, "{TELESCOPE}");
+        }
+    }
+    return newName;
+}
+
+// select the rule from the camera configuration, perform substitutions as needed
+psString pmFPAfileNameFromRule(const char *rule, const pmFPAfile *file, const pmFPAview *view)
+{
+    PS_ASSERT_PTR_NON_NULL(rule, NULL);
+    PS_ASSERT_INT_POSITIVE(strlen(rule), NULL);
+    PS_ASSERT_PTR_NON_NULL(file, NULL);
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+
+    psString newRule = NULL;            // Rule to pass on to pmFPANameFromRule
+    newRule = psStringCopy(rule);
+
+    if (strstr(newRule, "{OUTPUT}") != NULL) {
+        char *name = psMetadataLookupStr(NULL, file->names, "OUTPUT");
+        if (name) {
+            psStringSubstitute(&newRule, name, "{OUTPUT}");
+        }
+    }
+
+    psString newName = pmFPANameFromRule(newRule, file->fpa, view); // New name, to be returned
+    psFree(newRule);
+
+    return newName;
+}
+
+// given an already-opened fits file, write the components corresponding
+// to the specified view
+bool pmFPAfileCopyView (pmFPA *out, pmFPA *in, const pmFPAview *view)
+{
+    PS_ASSERT_PTR_NON_NULL(out, false);
+    PS_ASSERT_PTR_NON_NULL(in, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    // pmFPAWrite takes care of all PHUs as needed
+    if (view->chip == -1) {
+        pmFPACopy (out, in);
+        return true;
+    }
+    if (view->chip >= in->chips->n) {
+        psError(PS_ERR_IO, true, "Requested chip == %d >= in->chips->n == %ld", view->chip, in->chips->n);
+        return false;
+    }
+    pmChip *inChip = in->chips->data[view->chip];
+    pmChip *outChip = out->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        pmChipCopy (outChip, inChip);
+        return true;
+    }
+    if (view->cell >= inChip->cells->n) {
+        psError(PS_ERR_IO, true, "Requested cell == %d>= inChip->cells->n == %ld",
+                view->cell, inChip->cells->n);
+        return false;
+    }
+    pmCell *inCell = inChip->cells->data[view->cell];
+    pmCell *outCell = outChip->cells->data[view->cell];
+
+    if (view->readout == -1) {
+        pmCellCopy (outCell, inCell);
+        return true;
+    }
+    psError(PS_ERR_UNKNOWN, true, "Returning false");
+    return false;
+
+    // XXX add readout / segment equivalents
+}
+
+// given an already-opened fits file, write the components corresponding
+// to the specified view
+bool pmFPAfileCopyStructureView (pmFPA *out, const pmFPA *in, int xBin, int yBin, const pmFPAview *view)
+{
+    bool status;
+    PS_ASSERT_PTR_NON_NULL(out, false);
+    PS_ASSERT_PTR_NON_NULL(in, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    // XXX this should be smarter (ie, only copy concepts from the current chips)
+    // but such a call is needed, so re-copy stuff rather than no copy
+    pmFPACopyConcepts (out, in);
+
+    // pmFPAWrite takes care of all PHUs as needed
+    if (view->chip == -1) {
+        status = pmFPACopyStructure (out, in, xBin, yBin);
+        return status;
+    }
+    if (view->chip >= in->chips->n) {
+        psError(PS_ERR_IO, true, "Requested chip == %d >= in->chips->n == %ld", view->chip, in->chips->n);
+        return false;
+    }
+    pmChip *inChip = in->chips->data[view->chip];
+    pmChip *outChip = out->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        status = pmChipCopyStructure (outChip, inChip, xBin, yBin);
+        return status;
+    }
+    if (view->cell >= inChip->cells->n) {
+        psError(PS_ERR_IO, true, "Requested cell == %d>= inChip->cells->n == %ld",
+                view->cell, inChip->cells->n);
+        return false;
+    }
+    pmCell *inCell = inChip->cells->data[view->cell];
+    pmCell *outCell = outChip->cells->data[view->cell];
+
+    status = pmCellCopyStructure (outCell, inCell, xBin, yBin);
+    return status;
+}
+
+pmFPAfileType pmFPAfileTypeFromString(const char *type)
+{
+    PS_ASSERT_STRING_NON_EMPTY(type, PM_FPA_FILE_NONE);
+
+    if (!strcasecmp(type, "SX"))     {
+        return PM_FPA_FILE_SX;
+    }
+    if (!strcasecmp(type, "OBJ"))     {
+        return PM_FPA_FILE_OBJ;
+    }
+    if (!strcasecmp(type, "CMP"))     {
+        return PM_FPA_FILE_CMP;
+    }
+    if (!strcasecmp(type, "CMF"))     {
+        return PM_FPA_FILE_CMF;
+    }
+    if (!strcasecmp(type, "WCS"))     {
+        return PM_FPA_FILE_WCS;
+    }
+    if (!strcasecmp(type, "RAW"))     {
+        return PM_FPA_FILE_RAW;
+    }
+    if (!strcasecmp(type, "IMAGE"))     {
+        return PM_FPA_FILE_IMAGE;
+    }
+    if (!strcasecmp(type, "PSF"))     {
+        return PM_FPA_FILE_PSF;
+    }
+    if (!strcasecmp(type, "JPEG"))     {
+        return PM_FPA_FILE_JPEG;
+    }
+    if (!strcasecmp(type, "KAPA"))     {
+        return PM_FPA_FILE_KAPA;
+    }
+    if (!strcasecmp(type, "MASK"))     {
+        return PM_FPA_FILE_MASK;
+    }
+    if (!strcasecmp(type, "WEIGHT"))     {
+        return PM_FPA_FILE_WEIGHT;
+    }
+    if (!strcasecmp(type, "FRINGE")) {
+        return PM_FPA_FILE_FRINGE;
+    }
+    if (!strcasecmp(type, "DARK"))     {
+        return PM_FPA_FILE_DARK;
+    }
+    if (!strcasecmp(type, "HEADER"))     {
+        return PM_FPA_FILE_HEADER;
+    }
+    // deprecate this?
+    if (!strcasecmp(type, "ASTROM"))     {
+        return PM_FPA_FILE_ASTROM_MODEL;
+    }
+    if (!strcasecmp(type, "ASTROM.MODEL"))     {
+        return PM_FPA_FILE_ASTROM_MODEL;
+    }
+    if (!strcasecmp(type, "ASTROM.REFSTARS"))     {
+        return PM_FPA_FILE_ASTROM_REFSTARS;
+    }
+    if (!strcasecmp(type, "SUBKERNEL"))     {
+        return PM_FPA_FILE_SUBKERNEL;
+    }
+
+    return PM_FPA_FILE_NONE;
+}
+
+const char *pmFPAfileStringFromType(pmFPAfileType type)
+{
+    switch (type) {
+      case PM_FPA_FILE_SX:
+        return ("SX");
+      case PM_FPA_FILE_OBJ:
+        return ("OBJ");
+      case PM_FPA_FILE_CMP:
+        return ("CMP");
+      case PM_FPA_FILE_CMF:
+        return ("CMF");
+      case PM_FPA_FILE_WCS:
+        return ("WCS");
+      case PM_FPA_FILE_RAW:
+        return ("RAW");
+      case PM_FPA_FILE_IMAGE:
+        return ("IMAGE");
+      case PM_FPA_FILE_PSF:
+        return ("PSF");
+      case PM_FPA_FILE_JPEG:
+        return ("JPEG");
+      case PM_FPA_FILE_KAPA:
+        return ("KAPA");
+      case PM_FPA_FILE_MASK:
+        return ("MASK");
+      case PM_FPA_FILE_WEIGHT:
+        return ("WEIGHT");
+      case PM_FPA_FILE_FRINGE:
+        return ("FRINGE");
+      case PM_FPA_FILE_DARK:
+        return("DARK");
+      case PM_FPA_FILE_HEADER:
+        return ("HEADER");
+      case PM_FPA_FILE_ASTROM_MODEL:
+        return ("ASTROM.MODEL");
+      case PM_FPA_FILE_ASTROM_REFSTARS:
+        return ("ASTROM.REFSTARS");
+      case PM_FPA_FILE_SUBKERNEL:
+        return ("SUBKERNEL");
+      default:
+        return ("NONE");
+    }
+    return ("NONE");
+}
+
+
+psArray *pmFPAfileSelect(psMetadata *files, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(files, NULL);
+
+    psList *list = psListAlloc(NULL);   // List of files selected
+
+    psString regex = NULL;              // Regular expression
+    if (name) {
+        if (!psMetadataLookup(files, name)) {
+            psFree (list);
+            return NULL;
+        }
+        psStringAppend(&regex, "^%s$", name);
+    }
+    psMetadataIterator *iter = psMetadataIteratorAlloc(files, PS_LIST_HEAD, regex); // Iterator
+    psFree(regex);
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        pmFPAfile *file = item->data.V; // File of iterest
+        psListAdd(list, PS_LIST_TAIL, file);
+    }
+    psFree(iter);
+
+    psArray *array = psListToArray(list); // Array generated from list
+    psFree(list);
+
+    return array;
+}
+
+pmFPAfile *pmFPAfileSelectSingle(psMetadata *files, const char *name, int num)
+{
+    PS_ASSERT_PTR_NON_NULL(files, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(num, NULL);
+
+    psString regex = NULL;              // Regular expression
+    if (name) {
+        if (!psMetadataLookup(files, name)) {
+            // No files
+            return NULL;
+        }
+        psStringAppend(&regex, "^%s$", name);
+    }
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(files, PS_LIST_HEAD, regex); // Iterator
+    psFree(regex);
+    psMetadataItem *item;               // Item from iteration
+    int i = 0;                          // Counter
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (i++ == num) {
+            psFree(iter);
+            return item->data.V;
+        }
+    }
+    psFree(iter);
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find instance %d of file %s", num, name);
+    return NULL;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfile.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfile.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfile.h	(revision 22322)
@@ -0,0 +1,159 @@
+/* @file  pmFPAview.h
+ * @brief Tools to manipulate the FPA structure elements.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.33 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-09-02 19:06:16 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_FILE_H
+#define PM_FPA_FILE_H
+
+#include <pslib.h>
+
+#include <pmFPALevel.h>
+#include <pmFPA.h>
+#include <pmFPAview.h>
+#include <pmDetrendDB.h>
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+typedef enum {
+    PM_FPA_BEFORE,
+    PM_FPA_AFTER,
+} pmFPAfilePlace;
+
+typedef enum {
+    PM_FPA_FILE_NONE,
+    PM_FPA_FILE_SX,
+    PM_FPA_FILE_OBJ,
+    PM_FPA_FILE_CMP,
+    PM_FPA_FILE_CMF,
+    PM_FPA_FILE_WCS,
+    PM_FPA_FILE_RAW,
+    PM_FPA_FILE_IMAGE,
+    PM_FPA_FILE_MASK,
+    PM_FPA_FILE_WEIGHT,
+    PM_FPA_FILE_FRINGE,
+    PM_FPA_FILE_DARK,
+    PM_FPA_FILE_PSF,
+    PM_FPA_FILE_JPEG,
+    PM_FPA_FILE_KAPA,
+    PM_FPA_FILE_HEADER,
+    PM_FPA_FILE_ASTROM_MODEL,
+    PM_FPA_FILE_ASTROM_REFSTARS,
+    PM_FPA_FILE_SUBKERNEL,
+} pmFPAfileType;
+
+typedef enum {
+    PM_FPA_MODE_NONE,
+    PM_FPA_MODE_READ,
+    PM_FPA_MODE_WRITE,
+    PM_FPA_MODE_INTERNAL,
+    PM_FPA_MODE_REFERENCE,
+} pmFPAfileMode;
+
+typedef enum {
+    PM_FPA_STATE_OPEN     = 0x01,
+    PM_FPA_STATE_CLOSED   = 0x02,
+    PM_FPA_STATE_INACTIVE = 0x04,
+} pmFPAfileState;
+
+typedef struct {
+    pmFPAfileMode mode;                 // is this file read, written, or only used internally?
+    pmFPAfileType type;                 // what type of data is read from / written to disk?
+    pmFPAfileState state;               // have we opened the file, etc?
+
+    pmFPALevel fileLevel;               // what level in the FPA hierarchy represents a unique file?
+    pmFPALevel dataLevel;               // at what level do we read/write the data segment? (request by user)
+    pmFPALevel freeLevel;               // at what level do we free the data segment? (set by program)
+    pmFPALevel mosaicLevel;             // at what level is the mosaic?
+
+    pmFPA *fpa;                         // for I/O files, we carry a pointer to the complete fpa
+    psFits *fits;                       // for I/O files of fits type (IMAGE, CMP, CMF) we carry a file handle
+    psFitsCompression *compression;     // Compression for FITS images
+    psFitsOptions *options;             // FITS I/O options
+
+    bool wrote_phu;                     // have we written a PHU for this file?
+    psMetadata *header;                 // pointer (view) to the current hdu header
+
+    pmReadout *readout;                 // for internal files, we only carry a single readout
+
+    psMetadata *names;                  // filenames supplied by the cmdline or detdb are saved here
+
+    char *filerule;                     // rule for constructing a filename when needed
+    char *filesrc;                      // rule to find file in pmFPAfile->names list
+
+    char *name;                         // the name of the rule (useful for debugging / tracing)
+    char *filename;                     // the current name of an active file
+    char *extname;                      // the current name of an active file extension
+
+    pmDetrendSelectResults *detrend;    // Detrend information, from pmDetrendSelect
+
+    bool save;                          // Should the file be saved?
+
+    // the following elements are used for WRITE-mode IMAGE-type pmFPAfiles to inform
+    // the creation of a new image based on an existing image
+    pmFPA *src;                         // if an output FPA, inherit from this FPA
+    int xBin;                           // desired binning in x direction
+    int yBin;                           // desired binning in y direction
+
+    psMetadata *camera;                 // Camera configuration
+    psString cameraName;                // Name of the camera
+    psMetadata *format;                 // Camera format
+    psString formatName;                // name of the camera format
+} pmFPAfile;
+
+// allocate an empty pmFPAfile structure
+pmFPAfile *pmFPAfileAlloc ();
+
+// select the readout from the named pmFPAfile; if the named file does not exist,
+pmReadout *pmFPAfileThisReadout (psMetadata *files, const pmFPAview *view, const char *name);
+
+// select the cell from the named pmFPAfile; if the named file does not exist,
+pmCell *pmFPAfileThisCell (psMetadata *files, const pmFPAview *view, const char *name);
+
+// select the chip from the named pmFPAfile; if the named file does not exist,
+pmChip *pmFPAfileThisChip (psMetadata *files, const pmFPAview *view, const char *name);
+
+// add the specified filename info (value) to the files of the given mode using the given reference name
+bool pmFPAfileAddFileNames (psMetadata *files, char *name, char *value, int mode);
+
+// convert the rule to a name based on the current view
+psString pmFPANameFromRule(const char *rule, const pmFPA *fpa, const pmFPAview *view);
+
+// convert the rule to a name based on the current view
+psString pmFPAfileNameFromRule(const char *rule, const pmFPAfile *file, const pmFPAview *view);
+
+bool pmFPAfileCopyView (pmFPA *out, pmFPA *in, const pmFPAview *view);
+
+bool pmFPAfileCopyStructureView (pmFPA *out, const pmFPA *in, int xBin, int yBin, const pmFPAview *view);
+
+// Return the file type enum from a string
+pmFPAfileType pmFPAfileTypeFromString(const char *type);
+
+// Return the file type as a string
+const char *pmFPAfileStringFromType(pmFPAfileType type);
+
+/// Select files with the same name from the list of files
+///
+/// Returns all files if name is NULL.
+psArray *pmFPAfileSelect(psMetadata *files, ///< All files
+                         const char *name ///< Name of file(s) to return, or NULL for all
+    );
+
+/// Select a specific instance of a file from the list of files
+///
+/// Returns the num-th instance of all files if name is NULL.
+pmFPAfile *pmFPAfileSelectSingle(psMetadata *files, ///< All files
+                                 const char *name, ///< Name of file
+                                 int num ///< Instance number of specific instance
+    );
+
+
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileDefine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileDefine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileDefine.c	(revision 22322)
@@ -0,0 +1,1430 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <pslib.h>
+
+#include "pmErrorCodes.h"
+#include "pmConfig.h"
+#include "pmConfigMask.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmFPAfile.h"
+#include "pmFPAConstruct.h"
+
+#include "pmConcepts.h"
+
+# define FPA_TEST_ASSERT(A){ \
+        assert(A->format == NULL); \
+        assert(A->formatName == NULL); \
+        assert(A->filerule == NULL); \
+        assert(A->filesrc == NULL); }
+
+// Parse an option from a metadata, returning the appropriate integer value
+static int parseOptionInt(const psMetadata *md, // Metadata containing the option
+                          const char *name, // Option name
+                          const char *source, // Description of source, for warning messages
+                          int defaultValue // Default value
+                          )
+{
+    psMetadataItem *item = psMetadataLookup(md, name); // Item with the value of interest
+    if (!item) {
+        psWarning("Unable to find value for %s in %s --- set to %d.", name, source, defaultValue);
+        return defaultValue;
+    }
+    int value = psMetadataItemParseS32(item); // Value of interst
+    return value;
+}
+
+// Parse an option from a metadata, returning the appropriate float value
+static float parseOptionFloat(const psMetadata *md, // Metadata containing the option
+                              const char *name, // Option name
+                              const char *source // Description of source, for warning messages
+                              )
+{
+    psMetadataItem *item = psMetadataLookup(md, name); // Item with the value of interest
+    if (!item) {
+        psWarning("Unable to find value for %s in %s", name, source);
+        return NAN;
+    }
+    int value = psMetadataItemParseF32(item); // Value of interst
+    return value;
+}
+
+// Parse an option from a metadata, returning the appropriate double value
+static double parseOptionDouble(const psMetadata *md, // Metadata containing the option
+                                const char *name, // Option name
+                                const char *source // Description of source, for warning messages
+                                )
+{
+    psMetadataItem *item = psMetadataLookup(md, name); // Item with the value of interest
+    if (!item) {
+        psWarning("Unable to find value for %s in %s", name, source);
+        return NAN;
+    }
+    int value = psMetadataItemParseF64(item); // Value of interst
+    return value;
+}
+
+
+// define an input-type pmFPAfile, bind to the optional fpa if supplied
+pmFPAfile *pmFPAfileDefineInput(const pmConfig *config, pmFPA *fpa, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->files, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->camera, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    bool status;
+    char *type;
+
+    const psMetadata *camera = (fpa ? fpa->camera : config->camera); // Camera configuration for this file
+    psMetadata *data = pmConfigFileRule(config, camera, name); // File rule
+    if (!data) {
+        psError(PS_ERR_IO, true, "Can't find file rule %s!", name);
+        return NULL;
+    }
+
+    pmFPAfile *file = pmFPAfileAlloc();
+
+    // save the name of this pmFPAfile
+    file->name = psStringCopy(name);
+
+    file->filerule = psMemIncrRefCounter(psMetadataLookupStr (&status, data, "FILENAME.RULE"));
+
+    type = psMetadataLookupStr(&status, data, "FILE.TYPE");
+    file->type = pmFPAfileTypeFromString(type);
+    if (file->type == PM_FPA_FILE_NONE) {
+        psError(PS_ERR_IO, true, "FILE.TYPE is not defined for %s\n", name);
+        psFree(file);
+        return NULL;
+    }
+
+    file->mode = PM_FPA_MODE_READ;
+    file->fileLevel = PM_FPA_LEVEL_NONE; // the fileLevel depends on the input data
+
+    file->dataLevel = pmFPALevelFromName(psMetadataLookupStr (&status, data, "DATA.LEVEL"));
+    if (file->dataLevel == PM_FPA_LEVEL_NONE) {
+        psError(PS_ERR_IO, true, "DATA.LEVEL is not set for %s\n", name);
+        psFree(file);
+        return NULL;
+    }
+    // default is to free the data after use (after written out)
+    // this can be overridden for pmFPAfiles used as carriers as well
+    file->freeLevel = file->dataLevel;
+
+    if (fpa) {
+        file->fpa = psMemIncrRefCounter(fpa);
+        file->camera = psMemIncrRefCounter((psMetadata *)fpa->camera);
+        file->cameraName = psMemIncrRefCounter(config->cameraName); // XXX Is this the correct thing to do?
+    } else {
+        file->camera = psMemIncrRefCounter(config->camera);
+        file->cameraName = psMemIncrRefCounter(config->cameraName);
+    }
+
+    // XXX ppImage and similar require the added file to be unique
+    // XXX ppFocus wants to override the selection with the new selection
+    // XXX require programs like ppFocus to remove existing files by hand
+    if (!psMetadataAddPtr(config->files, PS_LIST_TAIL, name,
+                          PS_DATA_UNKNOWN | PS_META_DUPLICATE_OK, "", file)) {
+        psError(PS_ERR_IO, false, "could not add %s to config files", name);
+        return NULL;
+    }
+    psFree(file);
+    return file;
+}
+
+// Define an output pmFPAfile
+pmFPAfile *pmFPAfileDefineOutputForFormat(const pmConfig *config, // Configuration
+                                          pmFPA *fpa, // Optional FPA to bind
+                                          const char *name, // Name of file rule
+                                          psString cameraName, // Name of camera configuration to use
+                                          psString formatName // Name of camera format to use
+    )
+{
+    bool status;
+
+    // Use the camera we were told to, the camera of the provided FPA, or default to the default camera
+    psMetadata *camera;                 // Camera configuration
+    if (!cameraName || strlen(cameraName) == 0) {
+        if (fpa && fpa->camera) {
+            camera = (psMetadata*)fpa->camera; // Casting away const, so I can put it in the file
+        } else {
+            camera = config->camera;
+            cameraName = config->cameraName;
+        }
+    } else {
+        bool mdok;                      // Status of MD lookup
+        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->system, "CAMERAS"); // Known cameras
+        if (!mdok || !cameras) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the system configuration.\n");
+            return NULL;
+        }
+        camera = psMetadataLookupMetadata(&mdok, cameras, cameraName); // Camera configuration of interest
+        if (!mdok || !camera) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find automatically generated "
+                    "camera configuration %s in system configuration.\n", cameraName);
+            return NULL;
+        }
+
+        if (fpa && fpa->camera && fpa->camera != camera) {
+            psAbort("Camera of bound FPA is not the requested camera --- there is an inconsistency!");
+        }
+    }
+
+    psMetadata *filerule = pmConfigFileRule(config, camera, name); // File rule
+    if (!filerule) {
+        psError(PS_ERR_IO, true, "Can't find file rule %s!", name);
+        return NULL;
+    }
+
+    pmFPAfile *file = pmFPAfileAlloc();
+
+    // save the name of this pmFPAfile
+    file->name = psStringCopy(name);
+
+    // this is the filename rule
+    file->filerule = psMemIncrRefCounter(psMetadataLookupStr(&status, filerule, "FILENAME.RULE"));
+
+    const char *type = psMetadataLookupStr(&status, filerule, "FILE.TYPE");
+    file->type = pmFPAfileTypeFromString(type);
+    if (file->type == PM_FPA_FILE_NONE) {
+        psError(PS_ERR_IO, true, "FILE.TYPE is not defined for %s\n", name);
+        psFree(file);
+        return NULL;
+    }
+
+    file->mode = PM_FPA_MODE_WRITE;
+    file->save = false;
+
+    file->camera = psMemIncrRefCounter(camera);
+    file->cameraName = psMemIncrRefCounter(cameraName);
+
+    // XXX this seems a bit of a hack: use the cameraName to determine the mosaic level...
+    # if (0)
+    if (cameraName) {
+        if (!strcmp(cameraName + strlen(cameraName) - 5, "-CHIP")) {
+            file->mosaicLevel = PM_FPA_LEVEL_CHIP;
+        }
+        if (!strcmp(cameraName + strlen(cameraName) - 5, "-FPA")) {
+            file->mosaicLevel = PM_FPA_LEVEL_FPA;
+        }
+    }
+    # endif
+
+    // Use the format we were told to, the format specified in the file rule, or default to the default format
+    if (!formatName || strlen(formatName) == 0) {
+        // select the format list from the selected camera
+        formatName = psMetadataLookupStr(&status, filerule, "FILE.FORMAT");
+        if (!formatName || strcmp(formatName, "NONE") == 0) {
+            // Try to get by with the default
+            formatName = config->formatName;
+        }
+    }
+    psMetadata *formats = psMetadataLookupMetadata(&status, file->camera, "FORMATS"); // List of formats
+    psMetadata *format = psMetadataLookupMetadata(&status, formats, formatName); // Camera format to use
+    if (!format) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find format %s for file %s.\n",
+                formatName, file->name);
+        psFree(file);
+        return NULL;
+    }
+    file->format = psMemIncrRefCounter(format);
+    file->formatName = psStringCopy(formatName);
+
+    if (fpa) {
+        file->fpa = psMemIncrRefCounter(fpa);
+    } else {
+        file->fpa = pmFPAConstruct(file->camera, file->cameraName);
+    }
+
+    // Get FITS output scheme
+    const char *fitsType = psMetadataLookupStr(&status, filerule, "FITS.TYPE"); // Name of FITS scheme to use
+    if (fitsType && strcasecmp(fitsType, "NONE") != 0) {
+
+        // load the FITSTYPE scheme for this file
+        psMetadata *scheme = pmConfigFitsType(config, camera, fitsType); // File rule
+        if (!scheme) {
+            // XXX change to a config error?
+            psWarning("Unable to find %s in FITS in camera configuration --- will use defaults.", fitsType);
+            goto FITS_OPTIONS_DONE;
+        }
+
+        psString source = NULL;     // Source of options
+        psStringAppend(&source, "%s in FITS in camera configuration", fitsType);
+
+        psFitsOptions *options = file->options = psFitsOptionsAlloc(); // FITS I/O options
+
+        // Custom floating-point
+        bool mdok;                      // Status of MD lookup
+        const char *floatName = psMetadataLookupStr(&mdok, scheme, "FLOAT"); // Name of custom float
+        if (mdok && floatName) {
+            psString fullName = NULL;   // Full name of custom floating-point
+            psStringAppend(&fullName, "FLOAT_%s", floatName);
+            options->floatType = psFitsFloatTypeFromString(fullName);
+            psFree(fullName);
+        }
+
+        options->bitpix = parseOptionInt(scheme, "BITPIX", source, 0); // Bits per pixel
+
+        // Scaling options
+        const char *scalingString = psMetadataLookupStr(&mdok, scheme, "SCALING"); // Scaling name
+        if (scalingString) {
+            options->scaling = psFitsScalingFromString(scalingString); // Scaling method
+
+            switch (options->scaling) {
+              case PS_FITS_SCALE_NONE:
+              case PS_FITS_SCALE_RANGE:
+                // No options required
+                break;
+              case PS_FITS_SCALE_STDEV_POSITIVE:
+              case PS_FITS_SCALE_STDEV_NEGATIVE:
+                options->stdevNum = parseOptionFloat(scheme, "STDEV.NUM", source); // Padding to edge
+                if (!isfinite(options->stdevNum)) {
+                    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Bad value for STDEV.NUM for %s", source);
+                    psFree(source);
+                    psFree(file);
+                    return NULL;
+                }
+                // Flow through
+              case PS_FITS_SCALE_STDEV_BOTH:
+                options->stdevBits = parseOptionInt(scheme, "STDEV.BITS", source, 0); // Bits for stdev
+                if (options->stdevBits <= 0) {
+                    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Bad value for STDEV.BITS (%d) for %s",
+                            options->stdevBits, source);
+                    psFree(source);
+                    psFree(file);
+                    return NULL;
+                }
+                break;
+              case PS_FITS_SCALE_MANUAL:
+                options->bscale = parseOptionDouble(scheme, "BSCALE", source); // Scaling
+                options->bzero = parseOptionDouble(scheme, "BZERO", source); // Zero point
+                break;
+              default:
+                psAbort("Should never get here.");
+            }
+        }
+
+        // Compression options
+        const char *compressString = psMetadataLookupStr(&mdok, scheme, "COMPRESSION"); // Compression type
+        if (mdok && compressString) {
+            psFitsCompressionType type = psFitsCompressionTypeFromString(compressString); // Compression
+            psVector *tile = psVectorAlloc(3, PS_TYPE_S32); // Tile sizes
+            tile->data.S32[0] = parseOptionInt(scheme, "TILE.X", source, 0); // Tiling in x
+            tile->data.S32[1] = parseOptionInt(scheme, "TILE.Y", source, 1); // Tiling in y
+            tile->data.S32[2] = parseOptionInt(scheme, "TILE.Z", source, 1); // Tiling in z
+            int noise = parseOptionInt(scheme, "NOISE", source, 16); // Noise bits
+            int hscale = 0, hsmooth = 0;// Scaling and smoothing for HCOMPRESS
+            if (type == PS_FITS_COMPRESS_HCOMPRESS) {
+                hscale = parseOptionInt(scheme, "HSCALE", source, 0);
+                hsmooth = parseOptionInt(scheme, "HSMOOTH", source, 0);
+            }
+
+            file->compression = psFitsCompressionAlloc(type, tile, noise, hscale, hsmooth);
+            psFree(tile);
+        }
+
+        psFree(source);
+    }
+ FITS_OPTIONS_DONE:
+
+    file->fileLevel = pmFPAPHULevel(format);
+    if (file->fileLevel == PM_FPA_LEVEL_NONE) {
+        psError(PS_ERR_IO, true, "Unable to determine file level for %s\n", name);
+        psFree(file);
+        return NULL;
+    }
+
+    file->dataLevel = pmFPALevelFromName(psMetadataLookupStr(&status, filerule, "DATA.LEVEL"));
+    if (file->dataLevel == PM_FPA_LEVEL_NONE) {
+        psError(PS_ERR_IO, true, "DATA.LEVEL is not set for %s\n", name);
+        psFree(file);
+        return NULL;
+    }
+    // default is to free the data after use (after written out)
+    // this can be overridden for pmFPAfiles used as carriers as well
+    file->freeLevel = file->dataLevel;
+    file->fileLevel = PS_MIN (file->fileLevel, file->dataLevel);
+
+    // XXX the file/data/free level must be consistent with the reference fpa (but since we
+    // don't have access to its pmFPAfile, we cannot enforce this here...
+
+    pmFPALevel extLevel = pmFPAExtensionsLevel(format); // Level for extensions
+    if (extLevel != PM_FPA_LEVEL_NONE) {
+        if (extLevel < file->dataLevel) {
+            psWarning("Level for extensions is higher than desired data level --- adjusting.\n");
+            file->dataLevel = extLevel;
+        }
+        if (extLevel < file->freeLevel) {
+            psWarning("Level for extensions is higher than desired free level --- adjusting.\n");
+            file->freeLevel = extLevel;
+        }
+    } else {
+        // if we do not have extensions in the file, we are forced to write out at the file level
+        file->dataLevel = file->fileLevel;
+        file->freeLevel = file->fileLevel;
+    }
+
+    psTrace ("psModules.camera", 5, "file: %s, format: %s, fileLevel: %s, extLevel: %s, dataLevel: %s, freeLevel: %s\n",
+             file->name, file->formatName, pmFPALevelToName (file->fileLevel), pmFPALevelToName(extLevel), pmFPALevelToName (file->dataLevel), pmFPALevelToName (file->freeLevel));
+
+    // add argument-supplied OUTPUT name to this file
+    char *outname = psMetadataLookupStr(&status, config->arguments, "OUTPUT");
+    psMetadataAddStr(file->names, PS_LIST_TAIL, "OUTPUT", PS_META_NO_REPLACE, "", outname);
+
+    // place the resulting file in the config system
+    psMetadataAddPtr (config->files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN, "", 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'
+}
+
+// define a pmFPAfile, bind to the optional fpa if supplied
+pmFPAfile *pmFPAfileDefineOutput(const pmConfig *config, pmFPA *fpa, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->files, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->camera, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    return pmFPAfileDefineOutputForFormat(config, fpa, name, NULL, NULL);
+}
+
+// define a pmFPAfile, bind to the optional file if supplied
+pmFPAfile *pmFPAfileDefineOutputFromFile(const pmConfig *config, pmFPAfile *file, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->files, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->camera, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    char *cameraName = NULL, *formatName = NULL; // Name of camera and format
+    pmFPA *fpa = NULL;                  // FPA for file
+    if (file) {
+        cameraName = file->cameraName;
+        formatName = file->formatName;
+        fpa = file->fpa;
+    }
+
+    return pmFPAfileDefineOutputForFormat(config, fpa, name, cameraName, formatName);
+}
+
+// search for argname on the config->argument list
+// construct an FPA based on the files in this list (must represent a single FPA)
+// built the association between the FPA elements (CHIP/CELL) and the files
+// define the pmFPAfile filename and bind it to this FPA
+// save the pmFPAfile on config->files
+// return the pmFPAfile (a view to the one saved on config->files)
+pmFPAfile *pmFPAfileDefineFromArgs(bool *success, pmConfig *config, const char *filename, const char *argname)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(argname, NULL);
+
+    bool status;
+    pmFPA *fpa = NULL;
+    psFits *fits = NULL;
+    pmFPAfile *file = NULL;
+    psMetadata *phu = NULL;
+    psMetadata *format = NULL;
+    psMetadata *camera = NULL;
+    psString formatName = NULL;
+
+    // use success to identify valid exit conditions (as opposed to 'argument not supplied')
+    if (success) {
+        *success = false;
+    }
+
+    // we search the argument data for the named fileset (argname)
+    psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname);
+    if (!status) {
+        if (success) {
+            *success = true;
+        }
+        return NULL;
+    }
+    if (infiles->n < 1) {
+        psError(PS_ERR_IO, false, "Found n == %ld files in %s in arguments\n", infiles->n, argname);
+        return NULL;
+    }
+
+    // this function is implicitly an INPUT operation: do not create the file
+    psString realName = pmConfigConvertFilename (infiles->data[0], config, false, false);
+    if (!realName) {
+        psError(PS_ERR_IO, false, "Failed to convert file name %s\n", (char *) infiles->data[0]);
+        return NULL;
+    }
+
+    // load the header of the first image
+    // EXTWORD (fits->extword) is not relevant to the PHU
+    fits = psFitsOpen (realName, "r");
+    if (!fits) {
+        psError(PS_ERR_IO, false, "Failed to open file %s\n", realName);
+        psFree (realName);
+        return NULL;
+    }
+    phu = psFitsReadHeader (NULL, fits);
+
+    if (!phu) {
+        psError(PS_ERR_IO, false, "Failed to read file header %s\n", realName);
+        psFree (realName);
+        return NULL;
+    }
+    psFitsClose(fits);
+
+    // Determine the current format from the header; Determine camera if not specified already.
+    // the returned pointers 'camera' and 'formatName' are allocated here
+    format = pmConfigCameraFormatFromHeader(&camera, &formatName, config, phu, true);
+    if (!format) {
+        psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", realName);
+        psFree(phu);
+        psFree(camera);
+        psFree(formatName);
+        psFree(realName);
+        return NULL;
+    }
+
+    // build the template fpa, set up the basic view
+    // XXX do we want this to be the baseCamera name or the metaCamera name?
+    fpa = pmFPAConstruct(camera, config->cameraName);
+    if (!fpa) {
+        psError(PS_ERR_IO, false, "Failed to construct FPA from %s", realName);
+        psFree(phu);
+        psFree(camera);
+        psFree(formatName);
+        psFree(realName);
+        psFree(format);
+        return NULL;
+    }
+    psFree (realName);
+    psFree (camera);
+
+    // load the given filerule (from config->camera) and bind it to the fpa
+    // the returned file is just a view to the entry on config->files
+    file = pmFPAfileDefineInput(config, fpa, filename);
+    if (!file) {
+        psError(PS_ERR_IO, false, "file %s not defined\n", filename);
+        psFree(phu);
+        psFree(formatName);
+        psFree(format);
+        psFree(fpa);
+        return NULL;
+    }
+    psFree (file->filerule); // this is set in pmFPAfileDefineInput
+    file->format = format;
+    file->formatName = formatName;
+    file->filerule = psStringCopy("@FILES");
+    file->filesrc = psStringCopy("{CHIP.NAME}.{CELL.NAME}");
+    // we use the above rules to identify these files in the file->names data
+
+    file->fileLevel = pmFPAPHULevel(format);
+    if (file->fileLevel == PM_FPA_LEVEL_NONE) {
+        psError(PS_ERR_IO, true, "Unable to determine file level for %s\n", file->name);
+        psFree(phu);
+        psFree(fpa);
+        return NULL;
+    }
+
+    if (file->type == PM_FPA_FILE_MASK) {
+        if (!pmConfigMaskReadHeader (config, phu)) {
+            psError(PS_ERR_IO, false, "error in mask bits");
+            return NULL;
+        }
+    }
+
+    // examine the list of input files and validate their cameras
+    // associated each filename with an element of the FPA
+    // save the association on file->names
+    for (int i = 0; i < infiles->n; i++) {
+        if (i > 0) {
+            // this function is implicitly an INPUT operation: do not create the file
+            psString realName = pmConfigConvertFilename (infiles->data[i], config, false, false);
+            if (!realName) {
+                psError(PS_ERR_IO, false, "Failed to convert file name %s", (char *) infiles->data[i]);
+                psFree(phu);
+                psFree(fpa);
+                return NULL;
+            }
+            // EXTWORD (fits->extword) is not relevant to the PHU
+            fits = psFitsOpen (realName, "r");
+            if (!fits) {
+                psError(PS_ERR_IO, false, "Failed to open file %s\n", realName);
+                psFree(realName);
+                psFree(phu);
+                psFree(fpa);
+                return NULL;
+            }
+            phu = psFitsReadHeader (NULL, fits);
+            if (!phu) {
+                psError(PS_ERR_IO, false, "Failed to read file header %s", realName);
+                psFree(realName);
+                psFitsClose(fits);
+                psFree(phu);
+                psFree(fpa);
+                return NULL;
+            }
+            bool valid = false;
+            if (!pmConfigValidateCameraFormat (&valid, format, phu)) {
+                psError(PS_ERR_UNKNOWN, false, "Error in config scripts\n");
+                psFree(realName);
+                psFitsClose(fits);
+                psFree(phu);
+                psFree(fpa);
+                return NULL;
+            }
+            if (!valid) {
+                psError(PS_ERR_IO, false, "file %s is not from the required camera", realName);
+                psFree(realName);
+                psFitsClose(fits);
+                psFree(phu);
+                psFree(fpa);
+                return NULL;
+            }
+            psFree(realName);
+            psFitsClose(fits);
+        }
+
+        // set the view to the corresponding entry for this phu
+        pmFPAview *view = pmFPAAddSourceFromHeader (fpa, phu, format);
+        if (!view) {
+            psError(PS_ERR_IO, false, "Unable to determine source for %s", file->name);
+            psFree(phu);
+            psFree(fpa);
+            return NULL;
+        }
+
+        // associate the filename with the FPA element
+        char *name = pmFPAfileNameFromRule(file->filesrc, file, view);
+
+        // save the name association in the pmFPAfile structure
+        psMetadataAddStr(file->names, PS_LIST_TAIL, name, 0, "", infiles->data[i]);
+
+        psFree(view);
+        psFree(name);
+        psFree(phu);
+    }
+    psFree(fpa);
+    if (success) {
+        *success = true;
+    }
+
+    return file;
+}
+
+// search for argname on the config->argument list
+// construct an FPA based on the files in this list (must represent a single FPA)
+// built the association between the FPA elements (CHIP/CELL) and the files
+// define the pmFPAfile filename and bind it to this FPA
+// save the pmFPAfile on config->files
+// return the pmFPAfile (a view to the one saved on config->files)
+pmFPAfile *pmFPAfileBindFromArgs (bool *success, pmFPAfile *input, pmConfig *config, const char *filename, const char *argname)
+{
+    PS_ASSERT_PTR_NON_NULL(input, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(argname, NULL);
+
+    bool status;
+    psFits *fits = NULL;
+    pmFPAfile *file = NULL;
+    psMetadata *phu = NULL;
+
+    // use success to identify valid exit conditions (as opposed to 'argument not supplied')
+    if (success) {
+        *success = false;
+    }
+
+    // we search the argument data for the named fileset (argname)
+    psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname);
+    if (!status) {
+        // this is not an error: this just means no matching argument was supplied
+        if (success) {
+            *success = true;
+        }
+        return NULL;
+    }
+    if (infiles->n < 1) {
+        psError(PS_ERR_IO, false, "Found n == %ld files in %s in arguments\n", infiles->n, argname);
+        return NULL;
+    }
+
+    // load the given filerule (from config->camera) and bind it to the fpa
+    // the returned file is just a view to the entry on config->files
+    file = pmFPAfileDefineInput (config, input->fpa, filename);
+    if (!file) {
+        psError(PS_ERR_IO, false, "file %s not defined\n", filename);
+        psFree(phu);
+        return NULL;
+    }
+
+    // set derived values
+    file->fileLevel = input->fileLevel;
+
+
+    // define the rule to identify these files in the file->names data
+    psFree (file->filerule);
+    psFree (file->filesrc);
+    file->filerule = psStringCopy ("@FILES");
+    file->filesrc = psStringCopy ("{CHIP.NAME}.{CELL.NAME}");
+
+    // examine the list of input files and validate their cameras
+    // associated each filename with an element of the FPA
+    // save the association on file->names
+    psMetadata *format = NULL;
+    for (int i = 0; i < infiles->n; i++) {
+        // this function is implicitly an INPUT operation: do not create the file
+        psString realName = pmConfigConvertFilename (infiles->data[i], config, false, false);
+        if (!realName) {
+            psError(PS_ERR_IO, false, "Failed to convert file name %s", (char *) infiles->data[i]);
+            return NULL;
+        }
+        // EXTWORD (fits->extword) is not relevant to the PHU
+        fits = psFitsOpen (realName, "r");
+        if (!fits) {
+            psError(PS_ERR_IO, false, "Failed to open file %s\n", realName);
+            psFree(realName);
+            return NULL;
+        }
+        phu = psFitsReadHeader (NULL, fits);
+        if (!phu) {
+            psError(PS_ERR_IO, false, "Failed to read file header %s", realName);
+            psFree(realName);
+            psFitsClose(fits);
+            return NULL;
+        }
+
+        if (!format) {
+            format = pmConfigCameraFormatFromHeader(NULL, NULL, config, phu, true);
+            if (!format) {
+                psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", realName);
+                psFree(phu);
+                psFree(realName);
+                psFitsClose(fits);
+                return NULL;
+            }
+        } else {
+            bool valid = false;
+            if (!pmConfigValidateCameraFormat(&valid, format, phu)) {
+                psError(PS_ERR_UNKNOWN, false, "Error in config scripts\n");
+                psFree(realName);
+                psFitsClose(fits);
+                return NULL;
+            }
+            if (!valid) {
+                psError(PS_ERR_IO, false, "specified data file %s does not match format of supplied INPUT\n",
+                        realName);
+                psFree(realName);
+                psFitsClose(fits);
+                return NULL;
+            }
+        }
+
+        psFree(realName);
+        psFitsClose(fits);
+
+        // set the view to the corresponding entry for this phu
+        pmFPAview *view = pmFPAIdentifySourceFromHeader (input->fpa, phu, format);
+        if (!view) {
+            psError(PS_ERR_IO, false, "Unable to determine source for %s", file->name);
+            psFree(phu);
+            return NULL;
+        }
+
+        // associate the filename with the FPA element
+        char *name = pmFPAfileNameFromRule(file->filesrc, file, view);
+
+        // save the name association in the pmFPAfile structure
+        psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[i]);
+
+        if ((i == 0) && (file->type == PM_FPA_FILE_MASK)) {
+            if (!pmConfigMaskReadHeader (config, phu)) {
+                psError(PS_ERR_IO, false, "error in mask bits");
+                return NULL;
+            }
+        }
+
+        psFree(view);
+        psFree(name);
+        psFree(phu);
+    }
+    file->format = format;
+    file->formatName = psStringCopy(config->formatName);
+
+    if (success) {
+        *success = true;
+    }
+    return file;
+}
+
+// search for argname on the config->argument list
+// construct an FPA based on the files in this list (each represents the same FPA)
+// built the association between the FPA elements (CHIP/CELL) and the files
+// define the pmFPAfile filenames and bind them to the FPAs
+// save the pmFPAfiles on config->files
+// return the pmFPAfiles (a view to the one saved on config->files)
+pmFPAfile *pmFPAfileDefineSingleFromArgs (bool *success, pmConfig *config, const char *filename,
+        const char *argname, int entry)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(argname, NULL);
+
+    bool status;
+    pmFPA *fpa = NULL;
+    psFits *fits = NULL;
+    pmFPAfile *file = NULL;
+    psMetadata *phu = NULL;
+    psMetadata *format = NULL;
+
+    if (success) {
+        *success = false;
+    }
+
+    // we search the argument data for the named fileset (argname)
+    psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname);
+    if (!status) {
+        psTrace("psModules.camera", 5, "Failed to find %s in argument list", argname);
+        if (success) {
+            *success = true;
+        }
+        return NULL;
+    }
+    if (infiles->n <= entry) {
+        psError(PS_ERR_IO, false, "only %ld files in %s in argument, entry %d requested\n",
+                infiles->n, argname, entry);
+        return NULL;
+    }
+
+    // examine the list of input files and validate their cameras
+    // associated each filename with an element of the FPA
+    // save the association on file->names
+    // EXTWORD (fits->extword) is not relevant to the PHU
+    fits = psFitsOpen (infiles->data[entry], "r");
+    phu = psFitsReadHeader (NULL, fits);
+    psFitsClose (fits);
+
+    // on first call to this function, config->camera is not set.
+    // later calls will give an error if the cameras do not match
+    psMetadata *camera = NULL;
+    psString formatName = NULL;
+    format = pmConfigCameraFormatFromHeader (&camera, &formatName, config, phu, true);
+    if (!format) {
+        psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", (char *)infiles->data[0]);
+        psFree(phu);
+        psFree(camera);
+        psFree(formatName);
+        return NULL;
+    }
+
+    // build the template fpa, set up the basic view
+    fpa = pmFPAConstruct (camera, config->cameraName);
+    if (!fpa) {
+        psError(PS_ERR_IO, false, "Failed to construct FPA from %s", (char *)infiles->data[0]);
+        psFree(phu);
+        psFree(camera);
+        psFree(formatName);
+        psFree(format);
+        return NULL;
+    }
+    psFree(camera);
+
+    // load the given filerule (from config->camera) and bind it to the fpa
+    // the returned file is just a view to the entry on config->files
+    // we need a variable name here... (but in filerule)
+    file = pmFPAfileDefineInput (config, fpa, filename);
+    if (!file) {
+        psError(PS_ERR_IO, false, "file %s not defined\n", filename);
+        psFree(phu);
+        psFree(fpa);
+        psFree(format);
+        return NULL;
+    }
+    FPA_TEST_ASSERT (file);
+    file->format = format;
+    file->formatName = formatName;
+    file->filerule = psStringCopy ("@FILES");
+    file->filesrc = psStringCopy ("{CHIP.NAME}.{CELL.NAME}");
+    // adjust the above rules to identify these files in the file->names data
+
+    file->fileLevel = pmFPAPHULevel(format);
+    if (file->fileLevel == PM_FPA_LEVEL_NONE) {
+        psError(PS_ERR_IO, true, "Unable to determine file level for %s\n", file->name);
+        psFree(phu);
+        psFree(fpa);
+        return NULL;
+    }
+
+    if (file->type == PM_FPA_FILE_MASK) {
+        if (!pmConfigMaskReadHeader (config, phu)) {
+            psError(PS_ERR_IO, false, "error in mask bits");
+            return NULL;
+        }
+    }
+
+    // set the view to the corresponding entry for this phu
+    pmFPAview *view = pmFPAAddSourceFromHeader (fpa, phu, format);
+    if (!view) {
+        psError(PS_ERR_IO, false, "Unable to determine source for %s", file->name);
+        psFree(phu);
+        psFree(fpa);
+        return NULL;
+    }
+
+    // associate the filename with the FPA element
+    char *name = pmFPAfileNameFromRule (file->filesrc, file, view);
+
+    // save the name association in the pmFPAfile structure
+    psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[entry]);
+
+    psFree(phu);
+    psFree(fpa);
+    psFree(view);
+    psFree(name);
+
+    if (success) {
+        *success = true;
+    }
+    return file;
+}
+
+// define the named pmFPAfile from the camera->config
+// only valid for pmFPAfile->mode = READ
+pmFPAfile *pmFPAfileDefineFromConf (bool *success, const pmConfig *config, const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+
+    if (success) {
+        *success = false;
+    }
+
+    // a camera config is needed (as source of file rule)
+    if (config->camera == NULL) {
+        psError(PS_ERR_IO, true, "camera is not defined");
+        return NULL;
+    }
+
+    // build the template fpa, set up the basic view
+    pmFPA *fpa = pmFPAConstruct(config->camera, config->cameraName);
+    if (!fpa) {
+        psError(PS_ERR_IO, false, "Failed to construct FPA for %s", filename);
+        return NULL;
+    }
+
+    // load the given filerule (from config->camera) and bind it to the fpa
+    // the returned file is just a view to the entry on config->files
+    pmFPAfile *file = pmFPAfileDefineInput(config, fpa, filename);
+    psFree (fpa);
+    if (!file) {
+        psError(PS_ERR_IO, false, "file %s not defined\n", filename);
+        return NULL;
+    }
+
+    // image names may not come from file->names
+    if (!strcasecmp(file->filerule, "@FILES")) {
+        psError(PS_ERR_IO, true, "supplied filerule uses illegal value @FILES");
+        // XXX remove the file from config->files
+        return NULL;
+    }
+
+    // image names may come from the detrend database
+    if (!strcasecmp(file->filerule, "@DETDB")) {
+        psTrace ("pmFPAfile", 5, "requiring use of detrend database source\n");
+        // don't free the file here: it is left on config->files
+        // to be used optionally by pmFPAfileDefineFromDetDB (or others)
+        if (success) {
+            *success = true;
+        }
+        return NULL;
+    }
+
+    // Prepend the global path to the file rule
+    // this function is implicitly an INPUT operation: do not create the file
+    psString tmpName = pmConfigConvertFilename(file->filerule, config, false, false);
+    psFree (file->filerule);
+    file->filerule = tmpName;
+
+    if (success) {
+        *success = true;
+    }
+
+    return file;
+}
+
+// construct an FPA based on the supplied config->camera
+// built the association between the FPA elements (CHIP/CELL) and the files
+// define the pmFPAfile filename and bind it to this FPA
+// save the pmFPAfile on config->files
+// return the pmFPAfile (a view to the one saved on config->files)
+pmFPAfile *pmFPAfileDefineFromDetDB (bool *success, const pmConfig *config, const char *filename,
+                                     pmFPA *input, pmDetrendType type)
+{
+    PS_ASSERT_PTR_NON_NULL(input, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->camera, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->files, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+
+    bool status;
+    pmFPA *fpa = NULL;
+    pmFPAfile *file = NULL;
+
+    if (success) {
+        *success = false;
+    }
+
+    // a camera config is needed (as source of file rule)
+    if (config->camera == NULL) {
+        psError(PS_ERR_IO, true, "camera is not defined");
+        return NULL;
+    }
+    // a camera config is needed (as source of file rule)
+    if (config->cameraName == NULL) {
+        psAbort("camera defined but not cameraName!");
+    }
+
+    // find or define a pmFPAfile with this name
+    file = psMetadataLookupPtr (NULL, config->files, filename);
+    if (!file) {
+        // build the template fpa, set up the basic view
+        fpa = pmFPAConstruct(config->camera, config->cameraName);
+        if (!fpa) {
+            psError(PS_ERR_IO, false, "Failed to construct FPA for %s", filename);
+            return NULL;
+        }
+        // load the given filerule (from config->camera) and bind it to the fpa
+        // the returned file is just a view to the entry on config->files
+        file = pmFPAfileDefineInput (config, fpa, filename);
+        if (!file) {
+            psError(PS_ERR_IO, false, "file %s not defined\n", filename);
+            psFree(fpa);
+            return NULL;
+        }
+    }
+
+    // we are constructing a detselect command of the form:
+    //   detselect -search -inst (camera) -type (type) -time (time) [others]
+    // camera, type, and time are derived from pmFPA *input, other options are
+    // added if specified for the particular detrend type by the DETREND.CONSTRAINTS
+    // note that the filter-dependent choices are set for ppImage in ppImageParseCamera
+    // XXX make all of the detrend constraints explicit in DETREND.CONSTRAINTS?
+
+    // Get the time from FPA.TIME
+    psTime *time = psMetadataLookupPtr(NULL, input->concepts, "FPA.TIME");
+    if (time->sec == 0 && time->nsec == 0) {
+        psLogMsg ("psModules.camera", PS_LOG_WARN, "FPA.TIME has not been set.\n");
+    }
+
+    // XXX careful about this: is this set correctly in the camera.config files?
+    char *cameraName = psMetadataLookupStr(NULL, input->concepts, "FPA.CAMERA");
+    pmDetrendSelectOptions *options = pmDetrendSelectOptionsAlloc(cameraName, *time, type);
+
+    // add additional constraints based on the type defined in the PPIMAGE recipe
+    // XXX use PPIMAGE or DETREND for the recipe name?
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, "PPIMAGE");
+    if (!status) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "PPIMAGE recipe not found.");
+        psFree(options);
+        psFree(fpa);
+        return false;
+    }
+    psMetadata *detConstraints = psMetadataLookupPtr (&status, recipe, "DETREND.CONSTRAINTS");
+    if (!status) {
+        psWarning("DETREND.CONSTRAINTS not found --- no constraints will be applied.");
+        goto DETREND_SELECT;
+    }
+
+    psString typeName = pmDetrendTypeToString (type);
+    psMetadata *constraints = psMetadataLookupPtr (&status, detConstraints, typeName);
+    if (!status) {
+        psWarning("DETREND.CONSTRAINTS for type %s not found --- no contraints will be applied.", typeName);
+        psFree(typeName);
+        goto DETREND_SELECT;
+    }
+    psFree(typeName);
+
+    // loop over the constraints and include in the detselect options
+    psMetadataIterator *iter = psMetadataIteratorAlloc (constraints, PS_LIST_HEAD, NULL);
+    psMetadataItem *item = NULL;
+    while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
+        if (item->type != PS_DATA_STRING) {
+            psWarning("Invalid type for DETREND.CONSTRAINT element %s --- ignoring constraint", item->name);
+            continue;
+        }
+        char *option  = item->name;     // item->name must correspond to a valid detselect option
+        char *concept = item->data.V;
+
+        // these items refer to the corresponding values for the input image
+        // (ie, -filter input:filter or -exptime input:exptime)
+        if (!strcasecmp (option, "filter")) {
+            options->filter = psMetadataLookupPtr (&status, input->concepts, concept);
+            psMemIncrRefCounter (options->filter);
+            if (!status)
+                psAbort("failed to find filter (concept %s)", concept);
+        } else if (!strcasecmp (option, "exptime")) {
+            options->exptime = psMetadataLookupF32 (&status, input->concepts, concept);
+            options->exptimeSet = true;
+            if (!status)
+                psAbort("exptime not found (concept %s)", concept);
+        } else if (!strcasecmp (option, "airmass")) {
+            options->airmass = psMetadataLookupF32 (&status, input->concepts, concept);
+            options->airmassSet = true;
+            if (!status)
+                psAbort("airmass not found (concept %s)", concept);
+        } else if (!strcasecmp (option, "dettemp")) {
+            options->dettemp = psMetadataLookupF32 (&status, input->concepts, concept);
+            options->dettempSet = true;
+            if (!status)
+                psAbort("dettemp not found (concept %s)", concept);
+        } else if (!strcasecmp (option, "twilight")) {
+            options->twilight = psMetadataLookupF32 (&status, input->concepts, concept);
+            options->twilightSet = true;
+            if (!status)
+                psAbort("twilight not found (concept %s)", concept);
+        }
+
+        // the version is applied literally
+        if (!strcasecmp (option, "version")) {
+            options->version = psMemIncrRefCounter (concept);
+        }
+        // we can override the detrend database dettype if desired
+        // ie, use DOMEFLAT for type FLAT
+        // the dettype string is applied literally
+        if (!strcasecmp (option, "dettype")) {
+            options->dettype = psMemIncrRefCounter (concept);
+        }
+    }
+    psFree(iter);
+
+DETREND_SELECT:
+    {
+        // search for existing detrend data (detID)
+        pmDetrendSelectResults *results = pmDetrendSelect (options, config);
+        if (!results) {
+            psError (PS_ERR_IO, false, "no matching detrend data");
+            return NULL;
+        }
+        file->detrend = results;
+        file->fileLevel = pmFPALevelFromName(results->level);
+        if (file->fileLevel == PM_FPA_LEVEL_NONE) {
+            psError (PS_ERR_IO, false, "invalid file level for selected detrend data");
+            return NULL;
+        }
+    }
+
+    psFree (options);
+
+    if (success) {
+        *success = true;
+    }
+    return file;
+}
+
+// create a new output pmFPAfile based on an existing FPA
+// only valid for pmFPAfile->mode == WRITE (or internal?)
+pmFPAfile *pmFPAfileDefineFromFPA (const pmConfig *config, pmFPA *src, int xBin, int yBin, const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(src, false);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+
+    pmFPA *fpa = pmFPAConstruct(src->camera, psMetadataLookupStr(NULL, src->concepts, "FPA.CAMERA"));
+    // XXX should this use DefineOutputForFormat?
+    pmFPAfile *file = pmFPAfileDefineOutput (config, fpa, filename);
+    if (!file) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
+        return NULL;
+    }
+    file->src = psMemIncrRefCounter(src); // inherit output elements from this source pmFPA
+    file->xBin = xBin;
+    file->yBin = yBin;
+    psFree (fpa);
+    return file;
+}
+
+pmFPAfile *pmFPAfileDefineFromFile(const pmConfig *config, pmFPAfile *src, int xBin, int yBin,
+                                   const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(src, false);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+
+    pmFPAfile *file = pmFPAfileDefineOutputForFormat(config, NULL, filename, src->cameraName,
+                                                     src->formatName);
+    file->src = psMemIncrRefCounter(src->fpa); // inherit output elements from this source pmFPA
+    file->xBin = xBin;
+    file->yBin = yBin;
+
+    // inherit the concepts from the src fpa:
+    pmFPACopyConcepts(file->fpa, file->src);
+
+    return file;
+}
+
+// create a new output pmFPAfile based on an existing FPA
+// only valid for pmFPAfile->mode == WRITE (or internal?)
+pmFPAfile *pmFPAfileDefineNewCamera (const pmConfig *config, const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+
+    pmFPAfile *file = pmFPAfileDefineOutput (config, NULL, filename);
+    if (!file) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
+        return NULL;
+    }
+    if (!file->camera) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "file %s does not define a new camera\n", filename);
+        return NULL;
+    }
+    file->fpa = pmFPAConstruct(file->camera, file->cameraName);
+
+    return file;
+}
+
+pmFPAfile *pmFPAfileDefineSkycell(const pmConfig *config, pmFPA *fpa, const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(config->cameraName, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(config->formatName, NULL);
+
+    pmFPAfile *file;                    // The new file
+
+    if (config->cameraName[0] == '_' &&
+        strcmp(config->cameraName + strlen(config->cameraName) - 8, "-SKYCELL") == 0) {
+        // The input camera is already a skycell
+        file = pmFPAfileDefineOutputForFormat(config, fpa, filename, config->cameraName, "SKYCELL");
+    } else {
+        psString cameraName = NULL;         // Name of the old camera configuration
+        if (config->cameraName[0] == '_' &&
+            strcmp(config->cameraName + strlen(config->cameraName) - 5, "-CHIP") == 0) {
+            cameraName = psStringNCopy(config->cameraName + 1, strlen(config->cameraName) - 6);
+        } else if (config->cameraName[0] == '_' &&
+                   strcmp(config->cameraName + strlen(config->cameraName) - 4 , "-FPA") == 0) {
+            cameraName = psStringNCopy(config->cameraName + 1, strlen(config->cameraName) - 5);
+        } else {
+            cameraName = psMemIncrRefCounter(config->cameraName);
+        }
+        psString newCameraName = NULL;  // Name of the new (automatically-generated) camera configuration
+        psStringAppend(&newCameraName, "_%s-SKYCELL", cameraName);
+        file = pmFPAfileDefineOutputForFormat(config, fpa, filename, newCameraName, "SKYCELL");
+        psFree(cameraName);
+        psFree(newCameraName);
+    }
+    if (!file) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
+        return NULL;
+    }
+
+    // Ensure everything is written out at the appropriate level
+    file->fileLevel = PM_FPA_LEVEL_FPA;
+    file->dataLevel = PM_FPA_LEVEL_FPA;
+    file->freeLevel = PM_FPA_LEVEL_FPA;
+
+    return file;
+}
+
+pmFPAfile *pmFPAfileDefineChipMosaic(const pmConfig *config, pmFPA *src, const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(src, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(config->cameraName, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(config->formatName, NULL);
+
+    pmFPAfile *file;                    // The new file
+    if (config->cameraName[0] == '_' &&
+        (strcmp(config->cameraName + strlen(config->cameraName) - 5, "-CHIP") == 0 ||
+         strcmp(config->cameraName + strlen(config->cameraName) - 8, "-SKYCELL") == 0)) {
+        // The input camera has already been mosaicked to this level
+        file = pmFPAfileDefineOutputForFormat(config, NULL, filename, config->cameraName, config->formatName);
+    } else {
+        psString cameraName = NULL; // Name of the new (automatically-generated) camera configuration
+        if (config->cameraName[0] == '_' &&
+            strcmp(config->cameraName + strlen(config->cameraName) - 4 , "-FPA") == 0) {
+            cameraName = psStringNCopy(config->cameraName + 1, strlen(config->cameraName) - 5);
+        } else {
+            cameraName = psMemIncrRefCounter(config->cameraName);
+        }
+        psString newCameraName = NULL;  // Name of the new (automatically-generated) camera configuration
+        psStringAppend(&newCameraName, "_%s-CHIP", cameraName);
+
+        // Find the correct camera configuration
+        file = pmFPAfileDefineOutputForFormat(config, NULL, filename, newCameraName, config->formatName);
+        psFree(newCameraName);
+        psFree(cameraName);
+    }
+    if (!file) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
+        return NULL;
+    }
+
+    file->src = psMemIncrRefCounter(src); // inherit output elements from this source pmFPA
+    if (src) {
+        if (!pmConceptsCopyFPA(file->fpa, src, true, false)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to copy concepts from source to new FPA");
+            return NULL;
+        }
+    }
+
+    file->mosaicLevel = PM_FPA_LEVEL_CHIP; // don't do any I/O on this at a lower level
+
+    return file;
+}
+
+pmFPAfile *pmFPAfileDefineFPAMosaic(const pmConfig *config, pmFPA *src, const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(src, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(config->cameraName, NULL);
+
+    pmFPAfile *file;                    // The new file
+    if (config->cameraName[0] == '_' &&
+        (strcmp(config->cameraName + strlen(config->cameraName) - 4 , "-FPA") == 0 ||
+         strcmp(config->cameraName + strlen(config->cameraName) - 8, "-SKYCELL") == 0)) {
+        // The input camera has already been mosaicked to this level
+        file = pmFPAfileDefineOutputForFormat(config, NULL, filename, config->cameraName, config->formatName);
+    } else {
+
+        psString original = NULL;       // Name of the original camera configuration
+        if (config->cameraName[0] == '_' &&
+            strcmp(config->cameraName + strlen(config->cameraName) - 5 , "-CHIP") == 0) {
+            // It's a chip mosaic; we need to get the original name
+            original = psStringNCopy(config->cameraName + 1, strlen(config->cameraName) - 6);
+        } else if (config->cameraName[0] == '_' &&
+            strcmp(config->cameraName + strlen(config->cameraName) - 8, "-SKYCELL") == 0) {
+            original = psStringNCopy(config->cameraName + 1, strlen(config->cameraName) - 9);
+        } else {
+            original = psMemIncrRefCounter(config->cameraName);
+        }
+        psString cameraName = NULL;
+        psStringAppend(&cameraName, "_%s-FPA", original);
+        psFree(original);
+
+        file = pmFPAfileDefineOutputForFormat(config, NULL, filename, cameraName, config->formatName);
+        psFree(cameraName);
+    }
+    if (!file) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
+        return NULL;
+    }
+
+    file->src = psMemIncrRefCounter(src); // inherit output elements from this source pmFPA
+    if (src) {
+        if (!pmConceptsCopyFPA(file->fpa, src, false, false)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to copy concepts from source to new FPA");
+            return NULL;
+        }
+    }
+
+    file->mosaicLevel = PM_FPA_LEVEL_FPA; // don't do any I/O on this at a lower level
+
+    return file;
+}
+
+// create a file with the given name, assign it type "INTERNAL", and supply it with an image
+// of the requested dimensions. (image only, mask and weight are ignored)
+pmReadout *pmFPAfileDefineInternal (psMetadata *files, const char *name, int Nx, int Ny, int type)
+{
+    PS_ASSERT_PTR_NON_NULL(files, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    pmReadout *readout = pmReadoutAlloc(NULL);
+    readout->image = psImageAlloc(Nx, Ny, type);
+
+    // I want an image from the
+    pmFPAfile *file = pmFPAfileAlloc();
+    file->mode = PM_FPA_MODE_INTERNAL;
+    file->name = psStringCopy (name);
+
+    file->readout = readout;
+    psMetadataAddPtr(files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN, "", file);
+    psFree(file);
+    // we free this copy of file, but 'files' still has a copy
+
+    return readout;
+}
+
+bool pmFPAfileDropInternal(psMetadata *files, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(files, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    bool status = false;
+
+    pmFPAfile *file = psMetadataLookupPtr(&status, files, name);
+    if (!status) {
+        psTrace("psModules.camera", 6, "Internal File %s not in file list", name);
+        return true;
+    }
+    if (file == NULL) {
+        psError(PS_ERR_IO, true, "file %s is NULL", name);
+        return false;
+    }
+    if (file->mode != PM_FPA_MODE_INTERNAL) {
+        psTrace("psModules.camera", 6, "FPA File %s not Internal, not dropping", name);
+        return true;
+    }
+
+    psTrace("psModules.camera", 6, "dropping Internal FPA File %s", name);
+    psMetadataRemoveKey(files, name);
+    return true;
+}
+
+// Select or construct the requested readout.  If the named entry does not exist, generate it based
+// on the specified fpa and binning.  We have 4 possibilities: (INTERNAL or I/O file) and (exists or
+// not).  This call is used after all user-requested pmFPAfiles have been generated.  A missing
+// pmFPAfile is being used internally.
+pmReadout *pmFPAGenerateReadout(const pmConfig *config, // configuration information
+                                const pmFPAview *view, // select background for this entry
+                                const char *name, // name of internal/external file
+                                const pmFPA *fpa, // use this fpa to generate
+                                const psImageBinning *binning) {
+  pmReadout *readout = NULL;
+
+  bool status = true;
+  pmFPAfile *file = psMetadataLookupPtr(&status, config->files, name);
+
+  // if the file does not exist, it is not being used as an I/O file: define an internal version
+  if (file == NULL) {
+    readout = pmFPAfileDefineInternal (config->files, name, binning->nXruff, binning->nYruff, PS_TYPE_F32);
+    return readout;
+  }
+
+  // if the mode is INTERNAL, it has been defined in a previous call.  XXX This seems to require
+  // that the readout have the same dimensions for all entries.
+  if (file->mode == PM_FPA_MODE_INTERNAL) {
+    readout = file->readout;
+    return readout;
+  }
+
+  // we are using this pmFPAfile as an I/O file: select readout or create
+  readout = pmFPAviewThisReadout (view, file->fpa);
+  if (readout == NULL) {
+    // readout does not yet exist: create from input
+    // XXX we have an inconsistency in this calculation here and in pmFPACopy
+    // XXX use the psImageBinning functions to set the output image size
+    if (binning == NULL) {
+      pmFPAfileCopyStructureView (file->fpa, fpa, 1, 1, view);
+      readout = pmFPAviewThisReadout (view, file->fpa);
+    } else {
+      pmFPAfileCopyStructureView (file->fpa, fpa, binning->nXbin, binning->nYbin, view);
+      readout = pmFPAviewThisReadout (view, file->fpa);
+      PS_ASSERT (binning->nXruff == readout->image->numCols, false);
+      PS_ASSERT (binning->nYruff == readout->image->numRows, false);
+    }
+  }
+
+  return readout;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileDefine.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileDefine.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileDefine.h	(revision 22322)
@@ -0,0 +1,156 @@
+/* @file  pmFPAview.h
+ * @brief Tools to manipulate the FPA structure elements.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-05-12 21:41:55 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+#ifndef PM_FPA_FILE_DEFINE_H
+#define PM_FPA_FILE_DEFINE_H
+
+// load the pmFPAfile information from the camera configuration data
+//
+// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+// reference count is held by the config->files metadata.  Multiple file rules of the same name are permitted
+// if multiple is true.
+pmFPAfile *pmFPAfileDefineInput (const pmConfig *config, pmFPA *fpa, const char *name);
+
+// load the pmFPAfile information from the camera configuration data
+//
+// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+// reference count is held by the config->files metadata.
+// Define an output pmFPAfile
+pmFPAfile *pmFPAfileDefineOutput(const pmConfig *config, // Configuration
+                                 pmFPA *fpa, // Optional FPA to bind
+                                 const char *name // Name of file rule
+    );
+
+/// Same as pmFPAfileDefineOutput, but binds to the fpa in the provided file
+pmFPAfile *pmFPAfileDefineOutputFromFile(const pmConfig *config, // Configuration
+                                         pmFPAfile *file, // File to bind FPAs, or NULL
+                                         const char *name // Name of file rule
+    );
+
+/// Define the FPA file using the provided camera and format names.
+///
+/// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+/// reference count is held by the config->files metadata.
+pmFPAfile *pmFPAfileDefineOutputForFormat(const pmConfig *config, // Configuration
+                                          pmFPA *fpa, // Optional FPA to bind
+                                          const char *name, // Name of file rule
+                                          psString cameraName, // Name of camera configuration to use
+                                          psString formatName // Name of camera format to use
+    );
+
+// look for the given argname on the argument list.  find the give filename from the file rules
+//
+// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+// reference count is held by the config->files metadata.
+pmFPAfile *pmFPAfileDefineFromArgs (bool *found, pmConfig *config, const char *filename, const char *argname);
+
+// look for the given argname on the argument list; bind the associated files to the specified
+// fpa.  these are, eg, mask or weight images.
+// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+// reference count is held by the config->files metadata.
+pmFPAfile *pmFPAfileBindFromArgs (bool *found, pmFPAfile *input, pmConfig *config, const char *filename, const char *argname);
+
+// look for the given argname on the argument list.  find the give filename from the file rules
+//
+// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+// reference count is held by the config->files metadata.
+pmFPAfile *pmFPAfileDefineFromConf (bool *found, const pmConfig *config, const char *filename);
+
+// look for the given argname on the argument list.  find the give filename from the file rules
+//
+// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+// reference count is held by the config->files metadata.
+pmFPAfile *pmFPAfileDefineFromDetDB (bool *found, const pmConfig *config, const char *filename,
+                                     pmFPA *input, pmDetrendType type);
+
+// create a new output pmFPAfile based on an existing FPA
+//
+// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+// reference count is held by the config->files metadata.
+pmFPAfile *pmFPAfileDefineFromFPA (const pmConfig *config, pmFPA *src, int xBin, int yBin, const char *filename);
+
+/// Same as pmFPAfileDefineFromFPA, except it uses an FPA file instead of an FPA
+///
+/// The new pmFPAfile is inserted into the config->files metadata, freed and returned; so that the user does
+/// not have to (and should not!) free the result.
+pmFPAfile *pmFPAfileDefineFromFile(const pmConfig *config, // Configuration
+                                   pmFPAfile *src, // Source file for this file
+                                   int xBin, int yBin, // Binning for this file
+                                   const char *filename // Name of file rule
+    );
+
+
+// create a new output pmFPAfile based on an existing FPA
+// only valid for pmFPAfile->mode == WRITE (or internal?)
+//
+// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+// reference count is held by the config->files metadata.
+pmFPAfile *pmFPAfileDefineNewCamera (const pmConfig *config, const char *filename);
+
+/// Create a new output pmFPAfile for a skycell of the default camera
+///
+/// The new pmFPAfile is inserted into the config->files metadata, freed and returned; so that the user does
+/// not have to (and should not!) free the result.
+pmFPAfile *pmFPAfileDefineSkycell(const pmConfig *config, ///< Configuration data
+                                  pmFPA *fpa, ///< FPA to which to bind
+                                  const char *filename ///< Output (root) filename
+    );
+
+
+/// Create a new output pmFPAfile based upon a chip mosaic of an existing FPA
+///
+/// The new pmFPAfile is inserted into the config->files metadata, freed and returned; so that the user does
+/// not have to (and should not!) free the result.
+pmFPAfile *pmFPAfileDefineChipMosaic(const pmConfig *config, ///< Configuration data
+                                     pmFPA *src, ///< Source FPA
+                                     const char *filename ///< Output (root) filename
+                                    );
+
+/// Create a new output pmFPAfile based upon an FPA mosaic of an existing FPA
+///
+/// The new pmFPAfile is inserted into the config->files metadata, freed and returned; so that the user does
+/// not have to (and should not!) free the result.
+pmFPAfile *pmFPAfileDefineFPAMosaic(const pmConfig *config, ///< Configuration data
+                                    pmFPA *src, ///< Source FPA
+                                    const char *filename ///< Output (root) filename
+                                   );
+
+// create a file with the given name, assign it type "INTERNAL", and supply it with an image
+// of the requested dimensions. (image only, mask and weight are ignored)
+///
+/// The new pmFPAfile is inserted into the config->files metadata, freed and returned; so that the user does
+/// not have to (and should not!) free the result.
+pmReadout *pmFPAfileDefineInternal(psMetadata *files, const char *name, int Nx, int Ny, int type);
+
+// delete the INTERNAL file of the given name (if it exists)
+bool pmFPAfileDropInternal(psMetadata *files, const char *name);
+
+// look for the given argname on the argument list.  find the give filename from the file rules
+//
+// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+// reference count is held by the config->files metadata.
+pmFPAfile *pmFPAfileDefineSingleFromArgs(bool *found, pmConfig *config, const char *filename,
+                                         const char *argname, int entry);
+
+// Select or construct the requested readout.  If the named entry does not exist, generate it based
+// on the specified fpa and binning.  We have 4 possibilities: (INTERNAL or I/O file) and (exists or
+// not).  This call is used after all user-requested pmFPAfiles have been generated.  A missing
+// pmFPAfile is being used internally.
+pmReadout *pmFPAGenerateReadout(const pmConfig *config, // configuration information
+                                const pmFPAview *view, // select background for this entry
+                                const char *name, // name of internal/external file
+                                const pmFPA *fpa, // use this fpa to generate
+                                const psImageBinning *binning);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileFitsIO.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileFitsIO.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileFitsIO.c	(revision 22322)
@@ -0,0 +1,625 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmConfigMask.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPARead.h"
+#include "pmFPAWrite.h"
+#include "pmFPAMaskWeight.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmFPAfileFitsIO.h"
+#include "pmFPACopy.h"
+#include "pmFPAConstruct.h"
+#include "pmDark.h"
+#include "pmConcepts.h"
+
+// Get a suitable FPA for the file; generate it if necessary
+static pmFPA *suitableFPA(const pmFPAfile *file, // File for which to get FPA
+                          const pmFPAview *view, // View at which to produce the FPA
+                          pmConfig *config, // Configuration (for concepts update)
+                          bool pixels   // Worry about copying pixels?
+    )
+{
+    psAssert(file, "It's supposed to be here");
+    psAssert(view, "It's supposed to be here");
+    psAssert(config, "It's supposed to be here");
+
+    if (!file->format) {                // Working with the same output format as input format
+        return psMemIncrRefCounter(file->fpa);
+    }
+
+    // May need to change format
+    pmFPALevel level = pmFPAviewLevel(view); // Level for the view
+    if (level == PM_FPA_LEVEL_NONE || level == PM_FPA_LEVEL_READOUT) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "This function shouldn't be called at the readout (or unknown) level.");
+        return NULL;
+    }
+
+    // Does the HDU of interest conform to the desired format?
+    pmHDU *hdu = pmFPAviewThisHDU(view, file->fpa); // The HDU of interest
+    if (hdu && hdu->format == file->format) {
+        // No work required
+        return psMemIncrRefCounter(file->fpa);
+    }
+
+    // Otherwise, we have to generate a copy with the correct format
+
+    pmFPAview *phuView = pmFPAviewAlloc(0); // View corresponding to the PHU
+    *phuView = *view;               // Copy contents
+    pmFPALevel phuLevel = pmFPAPHULevel(file->format); // Level for the PHU
+    switch (phuLevel) {
+      case PM_FPA_LEVEL_FPA:
+        phuView->chip = -1;
+        // Flow through
+      case PM_FPA_LEVEL_CHIP:
+        phuView->cell = -1;
+        // Flow through
+      case PM_FPA_LEVEL_CELL:
+        phuView->readout = -1;
+        break;
+      case PM_FPA_LEVEL_READOUT:
+      case PM_FPA_LEVEL_NONE:
+      default:
+        psAbort("Should never get here: bad phu level.\n");
+    }
+
+    pmFPA *nameSource = file->src; // Source of FPA.OBS
+    if (!nameSource) {
+        nameSource = file->fpa;
+    }
+    bool mdok;                  // Status of MD lookup
+    const char *fpaObs = psMetadataLookupStr(&mdok, nameSource->concepts, "FPA.OBS"); // Observation id
+
+    pmFPA *copy = pmFPAConstruct(file->camera, file->cameraName);  // FPA to return
+    if (!pmFPAAddSourceFromView(copy, fpaObs, phuView, file->format)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to insert HDU into FPA for writing.\n");
+        psFree(copy);
+        psFree(phuView);
+        return NULL;
+    }
+    psFree(phuView);
+
+    switch (level) {
+      case PM_FPA_LEVEL_FPA:
+        if ((pixels && !pmFPACopy(copy, file->fpa)) ||
+            (!pixels && !pmFPACopyStructure(copy, file->fpa, 1, 1))) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to copy FPA for format conversion.\n");
+            return NULL;
+        }
+        return copy;
+      case PM_FPA_LEVEL_CHIP: {
+          pmChip *chip = pmFPAviewThisChip(view, copy); // Chip of interest
+          pmChip *srcChip = pmFPAviewThisChip(view, file->fpa); // Source chip
+          if ((pixels && !pmChipCopy(chip, srcChip)) ||
+              (!pixels && !pmChipCopyStructure(chip, srcChip, 1, 1))) {
+              psError(PS_ERR_UNKNOWN, false, "Unable to copy chip for format conversion.\n");
+              return false;
+          }
+          return copy;
+      }
+      case PM_FPA_LEVEL_CELL: {
+          pmCell *cell = pmFPAviewThisCell(view, copy); // Cell of interest
+          pmCell *srcCell = pmFPAviewThisCell(view, file->fpa); // Source cell
+          if ((pixels && !pmCellCopy(cell, srcCell)) ||
+              (!pixels && !pmCellCopyStructure(cell, srcCell, 1, 1))) {
+              psError(PS_ERR_UNKNOWN, false, "Unable to copy cell for format conversion.\n");
+              return false;
+          }
+          return copy;
+      }
+      case PM_FPA_LEVEL_READOUT:
+      case PM_FPA_LEVEL_NONE:
+      default:
+        psAbort("Should never get here: bad phu level.\n");
+    }
+
+    // Unreachable
+    return NULL;
+}
+
+
+pmFPA *pmFPAfileSuitableFPA(const pmFPAfile *file, const pmFPAview *view, pmConfig *config, bool pixels)
+{
+    PS_ASSERT_PTR_NON_NULL(file, NULL);
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    pmFPA *fpa = suitableFPA(file, view, config, pixels); // A suitable FPA for writing
+    if (!fpa) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to produce suitable FPA.");
+        return NULL;
+    }
+
+    // Ensure headers and all are updated
+    // This is here so that the individual write functions (e.g., images, PSFs, sources, etc) don't have to
+    // take care of all this themselves (because they generally don't).
+    switch (file->type) {
+      case PM_FPA_FILE_IMAGE:
+      case PM_FPA_FILE_MASK:
+      case PM_FPA_FILE_WEIGHT:
+      case PM_FPA_FILE_HEADER:
+      case PM_FPA_FILE_FRINGE:
+      case PM_FPA_FILE_DARK:
+      case PM_FPA_FILE_CMP:
+      case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_PSF:
+      case PM_FPA_FILE_ASTROM_MODEL:
+      case PM_FPA_FILE_ASTROM_REFSTARS: {
+          pmHDU *hdu = pmFPAviewThisHDU(view, fpa);
+          if (hdu) {
+              if (!hdu->header) {
+                  hdu->header = psMetadataAlloc();
+              }
+              pmConfigConformHeader(hdu->header, file->format);
+
+              // whenever we write out a mask image, we should define the bits which represent mask concepts
+              if (file->type == PM_FPA_FILE_MASK) {
+                  assert (hdu->header);
+                  if (!pmConfigMaskWriteHeader(config, hdu->header)) {
+                      psError(PS_ERR_UNKNOWN, false,
+                              "failed to set the bitmask names in the PHU header for Image %s (%s)\n",
+                              file->filename, file->name);
+                      return false;
+                  }
+              }
+          }
+
+          pmChip *chip = pmFPAviewThisChip(view, fpa); // Chip of interest, or NULL
+          pmCell *cell = pmFPAviewThisCell(view, fpa); // Cell of interest, or NULL
+          if (!pmFPAUpdateNames(fpa, chip, cell)) {
+              psError(PS_ERR_UNKNOWN, false, "Unable to update names in header.");
+              return false;
+          }
+
+          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)) {
+                  psError(PS_ERR_IO, false, "Unable to write concepts for cell.\n");
+                  return false;
+              }
+          } else if (chip) {
+              if (!pmConceptsWriteChip(chip, sources, true, true, config)) {
+                  psError(PS_ERR_IO, false, "Unable to write concepts for chip.\n");
+                  return false;
+              }
+          } else if (!pmConceptsWriteFPA(fpa, sources, true, config)) {
+              psError(PS_ERR_IO, false, "Unable to write concepts for FPA.\n");
+              return false;
+          }
+          break;
+      }
+      default:
+        // No action
+        break;
+    }
+
+    return fpa;
+}
+
+// given an already-opened fits file, read the table corresponding to the specified view
+bool pmFPAviewReadFitsTable(const pmFPAview *view, pmFPAfile *file, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    pmFPA *fpa = file->fpa;             // FPA of interest
+    psFits *fits = file->fits;          // FITS file
+
+    if (view->chip == -1) {
+        return pmFPAReadTable(fpa, fits, name) > 0;
+    }
+
+    if (view->cell == -1) {
+        pmChip *chip = pmFPAviewThisChip(view, fpa); // Chip of interest
+        return pmChipReadTable(chip, fits, name) > 0;
+    }
+
+    pmCell *cell = pmFPAviewThisCell(view, fpa); // Cell of interest
+    return pmCellReadTable(cell, fits, name) > 0;
+}
+
+// given an already-opened fits file, write the table corresponding to the specified view
+bool pmFPAviewWriteFitsTable(const pmFPAview *view, pmFPAfile *file, const char *name, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // FPA of interest
+    psFits *fits = file->fits;          // FITS file
+
+    if (view->chip == -1) {
+        return pmFPAWriteTable(fits, fpa, name) > 0;
+    }
+
+    if (view->cell == -1) {
+        pmChip *chip = pmFPAviewThisChip(view, fpa); // Chip of interest
+        return pmChipWriteTable(fits, chip, name) > 0;
+    }
+
+    pmCell *cell = pmFPAviewThisCell(view, fpa); // Cell of interest
+    return pmCellWriteTable(fits, cell, name) > 0;
+}
+
+
+// given an already-opened fits file, read the components corresponding to the specified view
+static bool fpaViewReadFitsImage(const pmFPAview *view, // FPA view, specifying the level of interest
+                                 pmFPAfile *file, // FPA file of interest
+                                 pmConfig *config, // Configuration
+                                 bool (*fpaReadFunc)(pmFPA*, psFits*, pmConfig*), // Function to read FPA
+                                 bool (*chipReadFunc)(pmChip*, psFits*, pmConfig*), // Function to read chip
+                                 bool (*cellReadFunc)(pmCell*, psFits*, pmConfig*) // Function to read cell
+                                )
+{
+    assert(view);
+    assert(file);
+
+    pmFPA *fpa = file->fpa;             // FPA of interest
+    psFits *fits = file->fits;          // FITS file from which to read
+
+    if (view->chip == -1) {
+        return fpaReadFunc(fpa, fits, config);
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        psError(PS_ERR_IO, true, "Requested chip == %d >= fpa->chips->n == %ld", view->chip, fpa->chips->n);
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip]; // Chip of interest
+
+    if (view->cell == -1) {
+        return chipReadFunc(chip, fits, config);
+    }
+
+    if (view->cell >= chip->cells->n) {
+        psError(PS_ERR_IO, true, "Requested cell == %d >= chip->cells->n == %ld", view->cell, chip->cells->n);
+        return false;
+    }
+    pmCell *cell = chip->cells->data[view->cell]; // Cell of interest
+
+    if (view->readout == -1) {
+        return cellReadFunc(cell, fits, config);
+    }
+    psError(PS_ERR_UNKNOWN, true, "Bad view: %d,%d", view->chip, view->cell);
+    return false;
+
+    // XXX pmReadoutRead, pmReadoutReadSegement disabled for now
+    #if 0
+
+    if (view->readout >= cell->readouts->n) {
+        psError(PS_ERR_IO, true, "Requested readout == %d >= cell->readouts->n == %d",
+                view->readout, cell->readouts->n);
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[view->readout];
+
+    if (view->nRows == 0) {
+        pmReadoutRead (readout, fits, config);
+    } else {
+        pmReadoutReadSegment (readout, fits, view->nRows, view->iRows, NULL, NULL);
+    }
+    return true;
+    #endif
+}
+
+
+bool pmFPAviewReadFitsImage(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewReadFitsImage(view, file, config, pmFPARead, pmChipRead, pmCellRead);
+}
+
+bool pmFPAviewReadFitsMask(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewReadFitsImage(view, file, config, pmFPAReadMask, pmChipReadMask, pmCellReadMask);
+}
+
+bool pmFPAviewReadFitsWeight(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewReadFitsImage(view, file, config, pmFPAReadWeight, pmChipReadWeight, pmCellReadWeight);
+}
+
+bool pmFPAviewReadFitsDark(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewReadFitsImage(view, file, config, pmFPAReadDark, pmChipReadDark, pmCellReadDark);
+}
+
+bool pmFPAviewReadFitsHeaderSet(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewReadFitsImage(view, file, config, pmFPAReadHeaderSet, pmChipReadHeaderSet, pmCellReadHeaderSet);
+}
+
+// given an already-opened fits file, write the components corresponding
+// to the specified view. when the file was opened, pmFPA/Chip/CellWrite was
+// called on it with blank=true to write the (possible) blank PHU
+// do NOT call the functions below with blank=true or they will write
+// out data in an inconsistent fashion
+// the calls below should recurse down the element to write out all components.
+static bool fpaViewWriteFitsImage(const pmFPAview *view, // FPA view, specifying the level of interest
+                                  pmFPAfile *file, // FPA file of interest
+                                  pmConfig *config, // Configuration
+                                  bool (*fpaWriteFunc)(pmFPA*, psFits*, pmConfig*, bool, bool), // Func FPA
+                                  bool (*chipWriteFunc)(pmChip*, psFits*, pmConfig*, bool, bool),// Func chip
+                                  bool (*cellWriteFunc)(pmCell*, psFits*, pmConfig*, bool) // Func cell
+                                 )
+{
+    assert(view);
+    assert(file);
+
+    psFits *fits = file->fits;          // FITS file
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, true); // FPA to write
+
+    switch (pmFPAviewLevel(view)) {
+    case PM_FPA_LEVEL_FPA: {
+            bool success = fpaWriteFunc(fpa, fits, config, false, true);
+            psFree(fpa);
+            return success;
+        }
+    case PM_FPA_LEVEL_CHIP: {
+            pmChip *chip = pmFPAviewThisChip(view, fpa); // Chip of interest
+            bool success = chipWriteFunc(chip, fits, config, false, true);
+            psFree(fpa);
+            return success;
+        }
+    case PM_FPA_LEVEL_CELL: {
+            pmCell *cell = pmFPAviewThisCell(view, fpa); // Cell of interest
+            bool success = cellWriteFunc(cell, fits, config, false);
+            psFree(fpa);
+            return success;
+        }
+    case PM_FPA_LEVEL_READOUT:
+        #if 0 // XXX disable readout write for now
+
+        {
+            pmReadout *readout = pmFPAviewThisReadout(view, file->fpa); // Readout of interest
+            if (changeFormat)
+        {
+            // No copy function defined for readouts!
+            psError(PS_ERR_UNKNOWN, false, "Unable to copy readout for format conversion on write.\n");
+                return false;
+            }
+            if (view->nRows == 0)
+        {
+            return pmReadoutWrite(readout, fits, NULL, NULL);
+            } else
+            {
+                return pmReadoutWriteSegment(readout, fits, view->nRows, view->iRows, NULL, NULL);
+            }
+        }
+        #endif
+    case PM_FPA_LEVEL_NONE:
+    default:
+        psAbort("Should never reach here: invalid file level.");
+    }
+
+    return false;
+}
+
+bool pmFPAviewWriteFitsImage(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewWriteFitsImage(view, file, config, pmFPAWrite, pmChipWrite, pmCellWrite);
+}
+
+bool pmFPAviewWriteFitsMask(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewWriteFitsImage(view, file, config, pmFPAWriteMask, pmChipWriteMask, pmCellWriteMask);
+}
+
+bool pmFPAviewWriteFitsWeight(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewWriteFitsImage(view, file, config, pmFPAWriteWeight, pmChipWriteWeight, pmCellWriteWeight);
+}
+
+bool pmFPAviewWriteFitsDark(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewWriteFitsImage(view, file, config, pmFPAWriteDark, pmChipWriteDark, pmCellWriteDark);
+}
+
+// given an already-opened fits file, read the components corresponding
+// to the specified view
+bool pmFPAviewFreeData(const pmFPAview *view, pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        psTrace ("pmFPAfile", 5, "freeing fpa for %s\n", file->filename);
+        pmFPAFreeData (fpa);
+        // XXX drop me: file->fpa = NULL;
+        return true;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        psError(PS_ERR_IO, true, "Requested chip == %d >= fpa->chips->n == %ld", view->chip, fpa->chips->n);
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        psTrace ("pmFPAfile", 5, "freeing chip %d for %s\n", view->chip, file->filename);
+        pmChipFreeData (chip);
+        return true;
+    }
+
+    if (view->cell >= chip->cells->n) {
+        psError(PS_ERR_IO, true, "Requested cell == %d >= chip->cells->n == %ld", view->cell, chip->cells->n);
+        return false;
+    }
+    pmCell *cell = chip->cells->data[view->cell];
+
+    if (view->readout == -1) {
+        psTrace ("pmFPAfile", 5, "freeing cell %d for %s\n", view->cell, file->filename);
+        pmCellFreeData (cell);
+        return true;
+    }
+    psError(PS_ERR_UNKNOWN, true, "Returning false");
+    return false;
+
+    // XXX pmReadoutRead, pmReadoutReadSegement disabled for now
+    #if 0
+
+    if (view->readout >= cell->readouts->n) {
+        psError(PS_ERR_IO, true, "Requested readout == %d >= cell->readouts->n == %d",
+                view->readout, cell->readouts->n);
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[view->readout];
+
+    if (view->nRows == 0) {
+        pmReadoutRead (readout, fits, NULL);
+    } else {
+        pmReadoutReadSegment (readout, fits, view->nRows, view->iRows, NULL, NULL);
+    }
+    return true;
+    #endif
+}
+
+#if 0
+// Shouldn't need this --- when we want to free fringe data, we want to free the whole level, not just the
+// table.
+
+// Free the table within a cell
+static void freeTable(pmCell *cell,     // Cell of interest
+                      const char *name  // Name of table to free
+                     )
+{
+    assert(cell);
+    assert(name && strlen(name) > 0);
+
+    psString headerName = NULL;         // Name of header
+    psStringAppend(&headerName, "%s.HEADER", name);
+    if (psMetadataLookup(cell->analysis, headerName)) {
+        psMetadataRemoveKey(cell->analysis, headerName);
+    }
+    psFree(headerName);
+
+    if (psMetadataLookup(cell->analysis, name)) {
+        psMetadataRemoveKey(cell->analysis, name);
+    }
+
+    return;
+}
+
+// given a file, free the components corresponding to the specified view
+bool pmFPAviewFreeFitsTable (const pmFPAview *view, pmFPAfile *file, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        psArray *chips = fpa->chips;    // Array of chips
+        for (int i = 0; i < chips->n; i++) {
+            pmChip *chip = chips->data[i]; // Chip of interest
+            psArray *cells = chip->cells; // Array of cells
+            for (int j = 0; j < cells->n; j++) {
+                pmCell *cell = cells->data[j]; // Cell of interest
+                freeTable(cell, name);
+            }
+        }
+        return true;
+    }
+
+    if (view->cell == -1) {
+        pmChip *chip = pmFPAviewThisChip(view, fpa); // Chip of interest
+        psArray *cells = chip->cells;   // Array of cells
+        for (int i = 0; i < cells->n; i++) {
+            pmCell *cell = cells->data[i]; // Cell of interest
+            freeTable(cell, name);
+        }
+        return true;
+    }
+
+    pmCell *cell = pmFPAviewThisCell(view, fpa); // Cell of interest
+    freeTable(cell, name);
+    return true;
+}
+
+#endif
+
+bool pmFPAviewFitsWritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config) {
+
+    bool status = false;
+
+    if (file->mode != PM_FPA_MODE_WRITE) return true;
+    if (file->wrote_phu) return true;
+
+    // select or generate the desired fpa in the correct output format
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false);
+    pmHDU *phu = pmFPAviewThisHDU(view, fpa);
+    if (!phu || !phu->blankPHU) {
+        // No PHU to write!
+        psFree(fpa);
+        return true;
+    }
+
+    // whenever we write out a mask image, we should define the bits which represent mask concepts
+    if (file->type == PM_FPA_FILE_MASK) {
+        assert (phu->header);
+        if (!pmConfigMaskWriteHeader (config, phu->header)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to set the bitmask names in the PHU header for Image %s (%s)\n", file->filename, file->name);
+            return false;
+        }
+    }
+
+    switch (file->fileLevel) {
+      case PM_FPA_LEVEL_FPA:
+        status = pmFPAWrite(fpa, file->fits, config, true, false);
+        break;
+      case PM_FPA_LEVEL_CHIP: {
+          pmChip *chip = pmFPAviewThisChip(view, fpa);
+          status = pmChipWrite(chip, file->fits, config, true, false);
+          break;
+      }
+      case PM_FPA_LEVEL_CELL: {
+          pmCell *cell = pmFPAviewThisCell(view, fpa);
+          status = pmCellWrite(cell, file->fits, config, true);
+          break;
+      }
+      default:
+        psAbort("fileLevel not correctly set");
+        break;
+    }
+
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to write PHU for Image %s (%s)\n", file->filename, file->name);
+        return false;
+    }
+
+    psFree(fpa);
+    file->wrote_phu = true;
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileFitsIO.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileFitsIO.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileFitsIO.h	(revision 22322)
@@ -0,0 +1,106 @@
+/* @file  pmFPAview.h
+ * @brief Tools to manipulate the FPA structure elements.
+ *
+ * @author EAM, IfA
+ * @author PAP, IfA
+ *
+ * @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-07-17 22:38:15 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_FILE_FITS_IO_H
+#define PM_FPA_FILE_FITS_IO_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Read an image into the current view
+bool pmFPAviewReadFitsImage(const pmFPAview *view, ///< View specifying level of interest
+                            pmFPAfile *file, ///< FPA file into which to read
+                            pmConfig *config
+                           );
+
+/// Read a mask into the current view
+bool pmFPAviewReadFitsMask(const pmFPAview *view, ///< View specifying level of interest
+                           pmFPAfile *file, ///< FPA file into which to read
+                            pmConfig *config
+                          );
+/// Read a weight map into the current view
+bool pmFPAviewReadFitsWeight(const pmFPAview *view,  ///< View specifying level of interest
+                             pmFPAfile *file, ///< FPA file into which to read
+                            pmConfig *config
+                            );
+
+/// Read a dark into the current view
+bool pmFPAviewReadFitsDark(const pmFPAview *view,  ///< View specifying level of interest
+                           pmFPAfile *file, ///< FPA file into which to read
+                            pmConfig *config
+    );
+
+/// Read an image header into the current view
+bool pmFPAviewReadFitsHeaderSet(const pmFPAview *view,  ///< View specifying level of interest
+                                pmFPAfile *file, ///< FPA file into which to read
+                            pmConfig *config
+    );
+
+/// Write the image for the specified view
+bool pmFPAviewWriteFitsImage(const pmFPAview *view, ///< View specifying level of interest
+                             pmFPAfile *file, ///< FPA file to write
+                             pmConfig *config ///< Configuration
+                            );
+
+/// Write the mask for the specified view
+bool pmFPAviewWriteFitsMask(const pmFPAview *view, ///< View specifying level of interest
+                            pmFPAfile *file, ///< FPA file to write
+                            pmConfig *config ///< Configuration
+                           );
+
+/// Write the weight map for the specified view
+bool pmFPAviewWriteFitsWeight(const pmFPAview *view, ///< View specifying level of interest
+                              pmFPAfile *file, ///< FPA file to write
+                              pmConfig *config ///< Configuration
+                             );
+
+/// Write the dark for the specified view
+bool pmFPAviewWriteFitsDark(const pmFPAview *view, ///< View specifying level of interest
+                            pmFPAfile *file, ///< FPA file to write
+                            pmConfig *config ///< Configuration
+    );
+
+/// Write a PHU for a fits image if needed
+bool pmFPAviewFitsWritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+
+/// Free the data for the specified view
+bool pmFPAviewFreeData(const pmFPAview *view, ///< View specifying level of interest
+                       pmFPAfile *file  ///< FPA file to free data
+                      );
+
+/// Read a table into the current view
+bool pmFPAviewReadFitsTable(const pmFPAview *view, ///<  View specifying level of interest
+                            pmFPAfile *file, ///< FPA file into which to read
+                            const char *name ///< Name of table
+                           );
+
+/// Write the table for the specified view
+bool pmFPAviewWriteFitsTable(const pmFPAview *view, ///<  View specifying level of interest
+                             pmFPAfile *file, ///< FPA file to write
+                             const char *name, ///< Name of table
+                             pmConfig *config ///< Configuration
+                            );
+
+/// Produce a suitable FPA for writing, on the basis of the input FPAfile
+///
+/// A new FPA with a changed format is generated if required (file->format is set and file->camera is equal to
+/// the default, indicating a change in the format without changing the camera --- changes to the camera are
+/// handled using other systems --- see pmFPAfileDefineChipMosaic, pmFPAfileDefineFPAMosaic).  Otherwise the
+/// file->fpa is returned (incremented).
+pmFPA *pmFPAfileSuitableFPA(const pmFPAfile *file,///< File containing the fpa
+                            const pmFPAview *view, ///< View at which to produce the fpa
+                            pmConfig *config, ///< Configuration
+                            bool pixels ///< Worry about copying the pixels?
+                           );
+
+/// @}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileIO.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileIO.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileIO.c	(revision 22322)
@@ -0,0 +1,1001 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmConfigMask.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAMaskWeight.h"
+#include "pmFPAview.h"
+#include "pmFPAFlags.h"
+#include "pmFPAfile.h"
+#include "pmFPACopy.h"
+#include "pmFPARead.h"
+#include "pmFPAWrite.h"
+#include "pmFPAfileIO.h"
+#include "pmFPAfileFitsIO.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmSourceIO.h"
+#include "pmResiduals.h"
+#include "pmPSF_IO.h"
+#include "pmAstrometryModel.h"
+#include "pmAstrometryRefstars.h"
+#include "pmFPA_JPEG.h"
+#include "pmSourcePlots.h"
+#include "pmFPAConstruct.h"
+#include "pmSubtractionIO.h"
+#include "pmConcepts.h"
+
+// attempt create, read, write, close, or free pmFPAfiles available in files files are
+// automatically opened before they are read.  In the case of MEF files, the PHU is
+// read when the file is opened and written before the first extension is written.
+bool pmFPAfileIOChecks (pmConfig *config, const pmFPAview *view, pmFPAfilePlace place)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(config->files, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    psMetadata *files = config->files;
+
+    // attempt to perform all create, read, write, close operations
+    psMetadataItem *item = NULL;
+    psMetadataIterator *iter = psMetadataIteratorAlloc (files, PS_LIST_HEAD, NULL);
+    while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
+        pmFPAfile *file = item->data.V;
+
+        switch (place) {
+        case PM_FPA_BEFORE:
+            if (!pmFPAfileRead (file, view, config)) {
+                psError(PS_ERR_IO, false, "failed READ in FPA_BEFORE block for %s", file->name);
+                goto failure;
+            }
+            if (!pmFPAfileCreate(file, view, config)) {
+                psError(PS_ERR_IO, false, "failed CREATE in FPA_BEFORE block for %s", file->name);
+                goto failure;
+            }
+            break;
+        case PM_FPA_AFTER:
+            if (!pmFPAfileWrite (file, view, config)) {
+                psError(PS_ERR_IO, false, "failed WRITE in FPA_AFTER block for %s", file->name);
+                goto failure;
+            }
+            if (!pmFPAfileClose(file, view)) {
+                psError(PS_ERR_IO, false, "failed CLOSE in FPA_AFTER block for %s", file->name);
+                goto failure;
+            }
+            break;
+        default:
+            psAbort("You can't get here");
+        }
+    }
+
+    // attempt to free data that is no longer needed
+    psMetadataIteratorSet (iter, PS_LIST_HEAD);
+    while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
+        pmFPAfile *file = item->data.V;
+
+        switch (place) {
+        case PM_FPA_BEFORE:
+            break;
+        case PM_FPA_AFTER:
+            if (!pmFPAfileFreeData(file, view)) {
+                if (!psMetadataRemoveKey(files, file->name)) {
+                    psError(PS_ERR_IO, false, "failed to remove %s in FPA_AFTER block", file->name);
+                    goto failure;
+                }
+            }
+            break;
+        default:
+            psAbort("You can't get here");
+        }
+    }
+    psFree (iter);
+    return true;
+
+failure:
+    psFree (iter);
+    return false;
+}
+
+// read the file, if necessary and possible
+bool pmFPAfileRead(pmFPAfile *file, const pmFPAview *view, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    // an internal file should not be sent here (should not be left on config->files)
+    PS_ASSERT(file->mode != PM_FPA_MODE_INTERNAL, false);
+
+    // skip the following states
+    if (file->state & PM_FPA_STATE_INACTIVE) {
+        psTrace("psModules.camera", 6, "skip read for %s, file is inactive", file->name);
+        return true;
+    }
+    if (file->mode != PM_FPA_MODE_READ) {
+        psTrace("psModules.camera", 6, "skip read for %s, mode is not READ", file->name);
+        return true;
+    }
+
+    // get the current level
+    pmFPALevel level = pmFPAviewLevel (view);
+
+    // do we need to read this file? defer until we read the correct level
+    if (level != file->dataLevel) {
+        psTrace("psModules.camera", 6, "skip reading of %s at this level %s: dataLevel is %s",
+                file->name, pmFPALevelToName(level), pmFPALevelToName(file->dataLevel));
+        return true;
+    }
+
+    // do we need to open this file?
+    if (level >= file->fileLevel) {
+        // we are allowed to open a file at a level which is not the fileLevel, but we need to
+        // supply a view at the fileLevel for the file lookup functions below
+        pmFPAview *fileView = pmFPAviewForLevel (file->fileLevel, view);
+        if (!pmFPAfileOpen (file, fileView, config)) {
+            psError(PS_ERR_IO, false, "failed to open file %s (%s)", file->filename, file->name);
+            psFree (fileView);
+            return false;
+        }
+        psFree (fileView);
+    }
+
+    // We need to read it --- double-check it's open!
+    if (file->state == PM_FPA_STATE_CLOSED) {
+        psError(PS_ERR_IO, false, "failed to open file %s when attempting to read", file->name);
+        return false;
+    }
+
+    // select a reading method
+    bool status = true;
+    switch (file->type) {
+      case PM_FPA_FILE_IMAGE:
+        status = pmFPAviewReadFitsImage(view, file, config);
+        break;
+      case PM_FPA_FILE_MASK:
+        status = pmFPAviewReadFitsMask(view, file, config);
+        break;
+      case PM_FPA_FILE_WEIGHT:
+        status = pmFPAviewReadFitsWeight(view, file, config);
+        break;
+      case PM_FPA_FILE_HEADER:
+        status = pmFPAviewReadFitsHeaderSet(view, file, config);
+        break;
+      case PM_FPA_FILE_DARK:
+        status = pmFPAviewReadFitsDark(view, file, config);
+        break;
+      case PM_FPA_FILE_FRINGE:
+        status = pmFPAviewReadFitsImage(view, file, config);
+        if (status) {
+            if (!pmFPAviewReadFitsTable(view, file, "FRINGE")) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to read fringe data from %s.\n", file->filename);
+                return false;
+            }
+        }
+        break;
+      case PM_FPA_FILE_SUBKERNEL:
+        status = pmSubtractionReadKernels(view, file, config);
+        break;
+      case PM_FPA_FILE_SX:
+      case PM_FPA_FILE_RAW:
+      case PM_FPA_FILE_OBJ:
+      case PM_FPA_FILE_CMP:
+      case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_WCS:
+        status = pmFPAviewReadObjects (view, file, config);
+        break;
+      case PM_FPA_FILE_PSF:
+        status = pmPSFmodelReadForView (view, file, config);
+        break;
+      case PM_FPA_FILE_ASTROM_MODEL:
+        status = pmAstromModelReadForView (view, file, config);
+        break;
+      case PM_FPA_FILE_ASTROM_REFSTARS:
+      case PM_FPA_FILE_JPEG:
+      case PM_FPA_FILE_KAPA:
+        break;
+      default:
+        psError(PS_ERR_IO, true, "warning: type mismatch; saw type %d (%s)", file->type, file->name);
+        return false;
+    }
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to read %s (%s)\n", file->filename, file->name);
+        return false;
+    }
+    psTrace ("psModules.camera", 5, "read %s (%s) (%d:%d:%d)\n", file->filename, file->name, view->chip, view->cell, view->readout);
+    return true;
+}
+
+// create the data elements (headers, images) appropriate for this view
+bool pmFPAfileCreate (pmFPAfile *file, const pmFPAview *view, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    // these are not error conditions; these are state tests
+    if (file->state & PM_FPA_STATE_INACTIVE) {
+        psTrace("psModules.camera", 6, "skip create for inactive file %s", file->name);
+        return true;
+    }
+    if (file->mode != PM_FPA_MODE_WRITE) {
+        psTrace("psModules.camera", 6, "skip create for non-write file %s", file->name);
+        return true;
+    }
+
+    // an internal file should not be returned to here
+    PS_ASSERT(file->mode != PM_FPA_MODE_INTERNAL, false);
+
+    // get the current level
+    pmFPALevel level = pmFPAviewLevel (view);
+
+    // don't create the file if the src (FPA) is not defined
+    if (file->src == NULL) {
+        psTrace("psModules.camera", 6, "skip create for FPA without src FPA for %s", file->name);
+        return true;
+    }
+
+    // do we need to write this file?
+    if (level != file->fileLevel || file->mosaicLevel != PM_FPA_LEVEL_NONE) {
+        psTrace("psModules.camera", 6, "skip creation of %s at this level %s: fileLevel is %s",
+                file->name, pmFPALevelToName(level), pmFPALevelToName(file->fileLevel));
+        return true;
+    }
+
+    switch (file->type) {
+      case PM_FPA_FILE_IMAGE:
+      case PM_FPA_FILE_MASK:
+      case PM_FPA_FILE_WEIGHT:
+      case PM_FPA_FILE_FRINGE:
+      case PM_FPA_FILE_DARK: {
+          // create FPA structure component based on view
+          psMetadata *format = file->format; // Camera format configuration
+          if (!format) {
+              format = config->format;
+          }
+
+          pmFPA *nameSource = file->src; // Source of FPA.OBS
+          if (!nameSource) {
+              nameSource = file->fpa;
+          }
+          bool mdok;                  // Status of MD lookup
+          const char *fpaObs = psMetadataLookupStr(&mdok, nameSource->concepts, "FPA.OBS"); // Obs. id
+
+          pmFPAAddSourceFromView(file->fpa, fpaObs, view, format);
+          psTrace ("psModules.camera", 5, "created fpa data elements for %s (%s) (%d:%d:%d)\n",
+                   file->name, file->name, view->chip, view->cell, view->readout);
+          break;
+      }
+      case PM_FPA_FILE_HEADER:
+        psAbort ("Create not defined for HEADER");
+        break;
+      case PM_FPA_FILE_SUBKERNEL:
+      case PM_FPA_FILE_SX:
+      case PM_FPA_FILE_RAW:
+      case PM_FPA_FILE_OBJ:
+      case PM_FPA_FILE_CMP:
+      case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_WCS:
+      case PM_FPA_FILE_PSF:
+      case PM_FPA_FILE_ASTROM_MODEL:
+      case PM_FPA_FILE_ASTROM_REFSTARS:
+      case PM_FPA_FILE_JPEG:
+      case PM_FPA_FILE_KAPA:
+        break;
+
+      default:
+        psError(PS_ERR_IO, true, "Unsupported type for %s: %d", file->name, file->type);
+        return false;
+    }
+    return true;
+}
+
+bool pmFPAfileWrite(pmFPAfile *file, const pmFPAview *view, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    if (file->state & PM_FPA_STATE_INACTIVE) {
+        psTrace("psModules.camera", 6, "skip write for %s, files is inactive", file->name);
+        return true;
+    }
+
+    if (file->mode != PM_FPA_MODE_WRITE) {
+        psTrace("psModules.camera", 6, "skip write for %s, mode is not WRITE", file->name);
+        return true;
+    }
+
+    // an internal file should not be returned to here
+    if (file->mode == PM_FPA_MODE_INTERNAL) {
+        psError(PS_ERR_IO, true, "File is mode PM_FPA_MODE_INTERNAL");
+        return false;
+    }
+
+    if (!file->save) {
+        psTrace("psModules.camera", 6, "skip write for %s, save is FALSE", file->name);
+        return true;
+    }
+
+    // get the current level
+    pmFPALevel level = pmFPAviewLevel (view);
+
+    // the effective dataLevel (for mosaics, we have preserved the original dataLevel)
+    pmFPALevel dataLevel = file->dataLevel;
+    if (file->mosaicLevel != PM_FPA_LEVEL_NONE && file->mosaicLevel < dataLevel) {
+        dataLevel = file->mosaicLevel;
+    }
+
+    // do we need to write this file?
+    if (level != dataLevel) {
+        psTrace("psModules.camera", 6, "skip writing of %s at this level %s: dataLevel is %s",
+                file->name, pmFPALevelToName(level), pmFPALevelToName(dataLevel));
+        return true;
+    }
+
+    // do we have data to write at this level?
+    if (!pmFPAviewCheckDataStatus (file->fpa, view)) {
+        psTrace("psModules.camera", 6, "skip write for %s, no data for this entry", file->name);
+        return true;
+    }
+
+    // note that for CMF and PSF, the test above is not sufficient to determine if there
+    // is actually any data to write out.  this is because these types of output files
+    // have their data stored on the readout->analysis metadata structure of another
+    // (existing) fpa
+    if (file->type == PM_FPA_FILE_CMF) {
+        if (!pmFPAviewCheckDataStatusForSources (view, file)) {
+        psTrace("psModules.camera", 6, "skip write for %s, no data for this entry", file->name);
+        return true;
+      }
+    }
+    if (file->type == PM_FPA_FILE_PSF) {
+      if (!pmPSFmodelCheckDataStatusForView (view, file)) {
+        psTrace("psModules.camera", 6, "skip write for %s, no data for this entry", file->name);
+        return true;
+      }
+    }
+    if (file->type == PM_FPA_FILE_ASTROM_MODEL) {
+      if (!pmAstromModelCheckDataStatusForView (view, file)) {
+        psTrace("psModules.camera", 6, "skip write for %s, no data for this entry", file->name);
+        return true;
+      }
+    }
+    if (file->type == PM_FPA_FILE_ASTROM_REFSTARS) {
+      if (!pmAstromRefstarsCheckDataStatusForView (view, file)) {
+        psTrace("psModules.camera", 6, "skip write for %s, no data for this entry", file->name);
+        return true;
+      }
+    }
+
+    // open the file if not yet opened
+    // XXX do we need to test mosaicLevel?
+    if (level >= file->fileLevel) {
+        // we are allowed to open a file at a level which is not the fileLevel, but
+        // we need to supply view at the fileLevel for the file lookup functions below
+        pmFPAview *fileView = pmFPAviewForLevel (file->fileLevel, view);
+        if (!pmFPAfileOpen (file, fileView, config)) {
+            psError(PS_ERR_IO, false, "failed to open %s (%s)", file->filename, file->name);
+            psFree (fileView);
+            return false;
+        }
+
+        // do we need to write out a PHU?
+        if (!pmFPAfileWritePHU(file, fileView, config)) {
+            psError(PS_ERR_IO, false, "failed to write phu for %s (%s)", file->filename, file->name);
+            return false;
+        }
+
+        psFree (fileView);
+    }
+
+    if (file->compression) {
+        psTrace("psModules.camera", 7, "Setting compression for %s (%s)\n", file->filename, file->name);
+        if (!psFitsCompressionApply(file->fits, file->compression)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to set compression options for %s (%s) (%d:%d:%d)\n",
+                    file->filename, file->name, view->chip, view->cell, view->readout);
+            return false;
+        }
+    }
+
+    // IMPORTANT: If adding a FITS-based file, make sure your write function uses an FPA produced by
+    // pmFPAfileSuitableFPA.  This ensures the HDUs are at the correct level for your output format, and sets
+    // the headers correctly.
+
+    // select a writing method
+    bool status = true;
+    switch (file->type) {
+      case PM_FPA_FILE_IMAGE:
+        status = pmFPAviewWriteFitsImage(view, file, config);
+        break;
+      case PM_FPA_FILE_MASK:
+        status = pmFPAviewWriteFitsMask(view, file, config);
+        break;
+      case PM_FPA_FILE_WEIGHT:
+        status = pmFPAviewWriteFitsWeight(view, file, config);
+        break;
+      case PM_FPA_FILE_HEADER:
+        psAbort ("no HEADER write functions defined");
+        break;
+      case PM_FPA_FILE_DARK:
+        status = pmFPAviewWriteFitsDark(view, file, config);
+        break;
+      case PM_FPA_FILE_FRINGE:
+        status = pmFPAviewWriteFitsImage (view, file, config);
+        if (status) {
+            if (!pmFPAviewWriteFitsTable(view, file, "FRINGE", config)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to write fringe data from %s.\n", file->filename);
+                return false;
+            }
+        }
+        break;
+      case PM_FPA_FILE_SUBKERNEL:
+        status = pmSubtractionWriteKernels(view, file, config);
+        break;
+      case PM_FPA_FILE_SX:
+      case PM_FPA_FILE_RAW:
+      case PM_FPA_FILE_OBJ:
+      case PM_FPA_FILE_CMP:
+      case PM_FPA_FILE_CMF:
+        status = pmFPAviewWriteObjects (view, file, config);
+        break;
+
+      case PM_FPA_FILE_PSF:
+        status = pmPSFmodelWriteForView (view, file, config);
+        break;
+
+      case PM_FPA_FILE_ASTROM_MODEL:
+        status = pmAstromModelWriteForView (view, file, config);
+        break;
+
+      case PM_FPA_FILE_ASTROM_REFSTARS:
+        status = pmAstromRefstarsWriteForView (view, file, config);
+        break;
+
+      case PM_FPA_FILE_JPEG:
+        status = pmFPAviewWriteJPEG (view, file, config);
+        break;
+
+      case PM_FPA_FILE_KAPA:
+        status = pmFPAviewWriteSourcePlot (view, file, config);
+        break;
+
+      case PM_FPA_FILE_WCS:
+        psError(PS_ERR_IO, true, "cannot write type WCS (%s)", file->name);
+        return false;
+
+      default:
+        psError(PS_ERR_IO, true, "warning: type mismatch; saw type %d (%s)", file->type, file->name);
+        return false;
+    }
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to write %s (%s)\n", file->filename, file->name);
+        return false;
+    }
+    psTrace ("psModules.camera", 5, "wrote %s (%s) (%d:%d:%d)\n", file->filename, file->name, view->chip, view->cell, view->readout);
+    return true;
+}
+
+bool pmFPAfileClose (pmFPAfile *file, const pmFPAview *view)
+{
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    // an internal file should not be sent here (should not be left on config->files)
+    PS_ASSERT(file->mode != PM_FPA_MODE_INTERNAL, false);
+
+    // skip the following states
+    if (file->state & PM_FPA_STATE_INACTIVE) {
+        psTrace("psModules.camera", 6, "skip close for %s, files is inactive", file->name);
+        return true;
+    }
+    if (file->state == PM_FPA_STATE_CLOSED) {
+        psTrace("psModules.camera", 6, "skip close for %s, files is closed", file->name);
+        return true;
+    }
+
+    // is current level == open level?
+    pmFPALevel level = pmFPAviewLevel (view);
+    if (file->fileLevel != level) {
+        psTrace("psModules.camera", 6, "skip closing of %s at this level %s: fileLevel is %s",
+                file->name, pmFPALevelToName(level), pmFPALevelToName(file->fileLevel));
+        return true;
+    }
+
+    // check if we are actually open
+    bool status = true;
+    switch (file->type) {
+        // check the FITS types
+      case PM_FPA_FILE_IMAGE:
+      case PM_FPA_FILE_MASK:
+      case PM_FPA_FILE_WEIGHT:
+      case PM_FPA_FILE_HEADER:
+      case PM_FPA_FILE_FRINGE:
+      case PM_FPA_FILE_DARK:
+      case PM_FPA_FILE_SUBKERNEL:
+      case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_WCS:
+      case PM_FPA_FILE_PSF:
+      case PM_FPA_FILE_ASTROM_MODEL:
+      case PM_FPA_FILE_ASTROM_REFSTARS:
+        psTrace ("psModules.camera", 5, "closing %s (%s) (%d:%d:%d)\n", file->filename, file->name, view->chip, view->cell, view->readout);
+        status = psFitsClose (file->fits);
+        file->fits = NULL;
+        file->header = NULL;
+        file->state = PM_FPA_STATE_CLOSED;
+        file->wrote_phu = false;
+        break;
+
+        // ignore the TEXT types
+      case PM_FPA_FILE_SX:
+      case PM_FPA_FILE_RAW:
+      case PM_FPA_FILE_OBJ:
+      case PM_FPA_FILE_CMP:
+      case PM_FPA_FILE_JPEG:
+      case PM_FPA_FILE_KAPA:
+        break;
+
+      default:
+        psError(PS_ERR_IO, true, "type mismatch: %d (%s)", file->type, file->name);
+        return false;
+    }
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to close %s (%s) (%d:%d:%d)\n", file->filename, file->name, view->chip, view->cell, view->readout);
+        return false;
+    }
+    return true;
+}
+
+bool pmFPAfileFreeData(pmFPAfile *file, const pmFPAview *view)
+{
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    // an internal file should not be sent here (should not be left on config->files)
+    PS_ASSERT(file->mode != PM_FPA_MODE_INTERNAL, false);
+
+    if (file->state & PM_FPA_STATE_INACTIVE) {
+        psTrace("psModules.camera", 6, "skip free for %s, files is inactive", file->name);
+        return true;
+    }
+
+    // get the current level
+    pmFPALevel level = pmFPAviewLevel (view);
+
+    // do we need to free this file?
+    if (level != file->freeLevel) {
+        psTrace("psModules.camera", 6, "skip free of %s at this level %s: freeLevel is %s",
+                file->name, pmFPALevelToName(level), pmFPALevelToName(file->freeLevel));
+        return true;
+    }
+
+    bool status = true;
+    switch (file->type) {
+      case PM_FPA_FILE_IMAGE:
+      case PM_FPA_FILE_MASK:
+      case PM_FPA_FILE_WEIGHT:
+      case PM_FPA_FILE_HEADER:
+      case PM_FPA_FILE_FRINGE:
+      case PM_FPA_FILE_DARK:
+        status = pmFPAviewFreeData(view, file);
+        break;
+      case PM_FPA_FILE_SUBKERNEL:
+      case PM_FPA_FILE_SX:
+      case PM_FPA_FILE_RAW:
+      case PM_FPA_FILE_OBJ:
+      case PM_FPA_FILE_CMP:
+      case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_WCS:
+      case PM_FPA_FILE_PSF:
+      case PM_FPA_FILE_ASTROM_MODEL:
+      case PM_FPA_FILE_ASTROM_REFSTARS:
+        psTrace ("psModules.camera", 6, "NOT freeing %s (%s) : save for further analysis\n", file->filename, file->name);
+        return true;
+      case PM_FPA_FILE_JPEG:
+      case PM_FPA_FILE_KAPA:
+        psTrace ("psModules.camera", 5, "nothing to free for %s (%s)\n", file->filename, file->name);
+        return true;
+      default:
+        psError(PS_ERR_IO, true, "warning: type mismatch; saw type %d", file->type);
+        return false;
+    }
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to read %s (%s)\n", file->filename, file->name);
+        return false;
+    }
+    psTrace ("psModules.camera", 5, "freed %s (%s) (%d:%d:%d)\n", file->filename, file->name, view->chip, view->cell, view->readout);
+    return true;
+}
+
+// open file (if not already opened).
+// this function is only called only within pmFPAfileRead or pmFPAfileWrite.
+bool pmFPAfileOpen (pmFPAfile *file, const pmFPAview *view, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    bool status;
+    char *mode = NULL;
+    char *readMode = "r";
+    char *writeMode = "w";
+
+    if (file->state & PM_FPA_STATE_INACTIVE) {
+        psTrace("psModules.camera", 6, "skip open for %s, files is inactive", file->name);
+        return true;
+    }
+
+    if (file->state == PM_FPA_STATE_OPEN) {
+        return true;
+    }
+
+    // these are programming errors
+    PS_ASSERT(file->mode != PM_FPA_MODE_NONE, false);
+    PS_ASSERT(file->mode != PM_FPA_MODE_INTERNAL, false);
+
+    if (file->mode == PM_FPA_MODE_READ) {
+        mode = readMode;
+    }
+    if (file->mode == PM_FPA_MODE_WRITE) {
+        mode = writeMode;
+    }
+    if ((file->mode == PM_FPA_MODE_WRITE) && !file->save) {
+        psTrace("psModules.camera", 6, "skip open for %s, output file not requested", file->name);
+        return true;
+    }
+
+    // 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);
+    }
+
+    // apply filename mangling rules (file://, path://, neb://)
+    bool create = file->mode == PM_FPA_MODE_WRITE ? true : false;
+    psString tmpName = pmConfigConvertFilename (file->filename, config, create, false);
+    psFree (file->filename);
+    file->filename = tmpName;
+
+    switch (file->type) {
+        // open the FITS types:
+      case PM_FPA_FILE_IMAGE:
+      case PM_FPA_FILE_MASK:
+      case PM_FPA_FILE_WEIGHT:
+      case PM_FPA_FILE_HEADER:
+      case PM_FPA_FILE_FRINGE:
+      case PM_FPA_FILE_DARK:
+      case PM_FPA_FILE_SUBKERNEL:
+      case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_WCS:
+      case PM_FPA_FILE_PSF:
+      case PM_FPA_FILE_ASTROM_MODEL:
+      case PM_FPA_FILE_ASTROM_REFSTARS:
+        psTrace ("psModules.camera", 5, "opening %s (%s) (%d:%d:%d)\n",
+                 file->filename, file->name, view->chip, view->cell, view->readout);
+        file->fits = psFitsOpen (file->filename, mode);
+        if (file->fits == NULL) {
+            psError(PS_ERR_IO, false, "error opening file %s\n", file->filename);
+            return false;
+        }
+        file->state = PM_FPA_STATE_OPEN;
+
+        file->fits->options = psMemIncrRefCounter(file->options);
+
+        // in most cases, we have already open and read the phu and determined the format.
+        // in some cases, (eg DetDB images), we have only just determined the filename.
+        // we need to check the file format before we can work with the file
+        if (!file->format) {
+          psMetadata *phu = psFitsReadHeader (NULL, file->fits);
+          if (!phu) {
+            psError(PS_ERR_IO, false, "Failed to read file header %s\n", file->filename);
+            return false;
+          }
+
+          // XXX if we have a mask file, then we need to read the mask bit names
+          // defined for this file
+          if (file->type == PM_FPA_FILE_MASK) {
+            if (!pmConfigMaskReadHeader (config, phu)) {
+                psError(PS_ERR_IO, false, "error in mask bits");
+                return false;
+            }
+          }
+
+          // determine the current format from the header
+          // determine camera if not specified already
+          // XXX can I actually reach this with camera not specified??
+          file->format = pmConfigCameraFormatFromHeader (NULL, NULL, config, phu, true);
+          if (!file->format) {
+            psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", file->filename);
+            psFree(phu);
+            return false;
+          }
+          psFree(phu);
+        }
+
+        // if needed, set the optional EXTWORD field based on the camera value
+        psMetadata *fileMenu = psMetadataLookupMetadata (NULL, file->format, "FILE");
+        if (!fileMenu) {
+          psError (PS_ERR_IO, true, "FILE METADATA missing from camera format %s\n",
+                   config->formatName);
+          return false;
+        }
+        char *extword = psMetadataLookupStr (&status, fileMenu, "EXTWORD");
+        if (status) {
+          psFitsSetExtnameWord (file->fits, extword);
+        }
+
+        // XXX these are probably only needed for WRITE files
+        if (file->compression) {
+            psTrace("psModules.camera", 7, "Setting compression for %s (%s)\n", file->filename, file->name);
+            if (!psFitsCompressionApply(file->fits, file->compression)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to set compression options for %s (%s) (%d:%d:%d)\n",
+                        file->filename, file->name, view->chip, view->cell, view->readout);
+                return false;
+            }
+        }
+
+        // In some cases, we need to read the PHU after we've opened the file.  This happens for the images
+        // supplied by the detrend database, which are only identified here (pmConfigConvertFilename).
+        if (!pmFPAfileReadPHU (file, view, config)) {
+            psError (PS_ERR_IO, true, "error reading PHU for %s (%s) (%d:%d:%d)\n",
+                     file->filename, file->name, view->chip, view->cell, view->readout);
+            return false;
+        }
+        break;
+
+        // defer opening TEXT types:
+      case PM_FPA_FILE_SX:
+      case PM_FPA_FILE_OBJ:
+      case PM_FPA_FILE_CMP:
+      case PM_FPA_FILE_RAW:
+      case PM_FPA_FILE_JPEG:
+      case PM_FPA_FILE_KAPA:
+        psTrace ("psModules.camera", 5, "defer opening %s\n", file->filename);
+        break;
+
+      default:
+        psError(PS_ERR_IO, true, "type mismatch for %s : %d\n", file->filename, file->type);
+        return false;
+    }
+    return true;
+}
+
+// for this file and view, if we need to read a PHU, read it.  return true for any non-error
+// condition. this function should be called by pmFPAfileOpen.  this function is only called
+// for files for which the PHU is not already loaded (files passed via the db or files after
+// the first in a multifile dataset)
+bool pmFPAfileReadPHU (pmFPAfile *file, const pmFPAview *view, pmConfig *config)
+{
+    // required conditions
+    if (file->mode != PM_FPA_MODE_READ) return true;
+    if (file->state != PM_FPA_STATE_OPEN) psAbort ("pmFPAfileReadPHU called on unopened file");
+    if (file->fpa == NULL) psAbort ("pmFPAfileReadPHU called on file without an FPA");
+
+    // check if we need to read a PHU (if not, return true)
+    switch (file->fileLevel) {
+      case PM_FPA_LEVEL_FPA:
+        if (file->fpa->hdu) return true;
+        break;
+      case PM_FPA_LEVEL_CHIP: {
+          pmChip *chip = pmFPAviewThisChip(view, file->fpa);
+          if (!chip) psAbort ("inconsistent file/fpa: fileLevel is CHIP, view is FPA");
+          if (chip->hdu) return true;
+          break;
+      }
+      case PM_FPA_LEVEL_CELL: {
+          pmCell *cell = pmFPAviewThisCell(view, file->fpa);
+          if (!cell) psAbort ("inconsistent file/fpa: fileLevel is CELL, view is FPA");
+          if (cell->hdu) return true;
+          break;
+      }
+      case PM_FPA_LEVEL_NONE:
+        // Might get here immediately after opening a file selected from the detrend database.
+        break;
+      default:
+        psAbort("fileLevel not correctly set");
+        break;
+    }
+
+    // XXX do we need to advance to the first HDU?
+    psMetadata *phu = psFitsReadHeader (NULL, file->fits);
+    if (!file->format) {
+        // determine the format (camera is already known); do not load the recipe
+        file->format = pmConfigCameraFormatFromHeader (NULL, &file->formatName, config, phu, false);
+        if (!file->format) {
+            psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", file->filename);
+            psFree(phu);
+            return false;
+        }
+    } else {
+        bool valid;
+        if (!pmConfigValidateCameraFormat (&valid, file->format, phu)) {
+            psError (PS_ERR_UNKNOWN, false, "Error in camera configuration\n");
+            psFree (phu);
+            return false;
+        }
+        if (!valid) {
+            psError(PS_ERR_IO, false, "file %s is not from the required camera", file->filename);
+            psFree (phu);
+            return false;
+        }
+    }
+    pmFPAview *thisView = pmFPAAddSourceFromHeader (file->fpa, phu, file->format);
+    assert (thisView); // XXX we are having some trouble with input psf files not having the Cell and fpa names matching.
+    psFree (thisView);
+    psFree (phu);
+    // XXX we can check the output view to be sure it corresponds to our current view
+    return true;
+}
+
+// XXX this function is only called from pmFPAfileWrite
+// XXX for each data type, there should be a function which writes the PHU, if needed
+bool pmFPAfileWritePHU(pmFPAfile *file, const pmFPAview *view, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    if (file->wrote_phu) return true;
+
+    bool status = true;
+    switch (file->type) {
+      case PM_FPA_FILE_IMAGE:
+      case PM_FPA_FILE_MASK:
+      case PM_FPA_FILE_WEIGHT:
+      case PM_FPA_FILE_DARK:
+      case PM_FPA_FILE_FRINGE:
+        status = pmFPAviewFitsWritePHU (view, file, config);
+        break;
+      case PM_FPA_FILE_SUBKERNEL:
+        status = pmSubtractionWritePHU(view, file, config);
+      case PM_FPA_FILE_CMF:
+        status = pmSource_CMF_WritePHU (view, file, config);
+        break;
+      case PM_FPA_FILE_PSF:
+        status = pmPSFmodelWritePHU(view, file, config);
+        break;
+      case PM_FPA_FILE_ASTROM_REFSTARS:
+        status = pmAstromRefstarsWritePHU (view, file, config);
+        break;
+      case PM_FPA_FILE_ASTROM_MODEL:
+      case PM_FPA_FILE_SX:
+      case PM_FPA_FILE_RAW:
+      case PM_FPA_FILE_OBJ:
+      case PM_FPA_FILE_CMP:
+      case PM_FPA_FILE_WCS:
+      case PM_FPA_FILE_JPEG:
+      case PM_FPA_FILE_KAPA:
+        break;
+      default:
+        fprintf (stderr, "warning: type mismatch\n");
+        return false;
+    }
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to write PHU for %s (%s)\n", file->filename, file->name);
+        return false;
+    }
+    // XXX this is also being set in the individual functions.  choose one or the other
+    file->wrote_phu = true;
+    return true;
+}
+
+
+// set the state of the specified pmFPAfile(s) to active (state == true) or inactive
+// if name is NULL, set the state for all pmFPAfiles
+bool pmFPAfileActivate(psMetadata *files, bool state, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(files, false);
+
+    // return false if the requested file is not in the list (not an error, but informational)
+    psArray *selected = pmFPAfileSelect(files, name);
+    if (!selected) {
+        return false;
+    }
+    for (int i = 0; i < selected->n; i++) {
+        pmFPAfile *file = selected->data[i]; // File of interest
+        if (!file) {
+            continue;
+        }
+        if (state) {
+            file->state &= PS_NOT_U8(PM_FPA_STATE_INACTIVE);
+        } else {
+            file->state |= PM_FPA_STATE_INACTIVE;
+        }
+    }
+    psFree(selected);
+
+    return true;
+}
+
+
+pmFPAfile *pmFPAfileActivateSingle(psMetadata *files, bool state, const char *name, int num)
+{
+    PS_ASSERT_PTR_NON_NULL(files, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(num, NULL);
+
+    pmFPAfile *file = pmFPAfileSelectSingle(files, name, num);
+    if (!file) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to select instance %d of file %s", num, name);
+        return NULL;
+    }
+    if (state) {
+        file->state &= PS_NOT_U8(PM_FPA_STATE_INACTIVE);
+    } else {
+        file->state |= PM_FPA_STATE_INACTIVE;
+    }
+
+    return file;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileIO.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileIO.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAfileIO.h	(revision 22322)
@@ -0,0 +1,55 @@
+/* @file  pmFPAview.h
+ * @brief Tools to manipulate the FPA structure elements.
+ *
+ * @author EAM, IfA
+ * @author PAP, IfA
+ *
+ * @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-30 00:53:45 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_FILE_IO_H
+#define PM_FPA_FILE_IO_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+// open the real file corresponding to the given pmFPAfile appropriate to the current view
+bool pmFPAfileOpen (pmFPAfile *file, const pmFPAview *view, pmConfig *config);
+
+// read from the real file corresponding to the given pmFPAfile for the current view
+bool pmFPAfileRead (pmFPAfile *file, const pmFPAview *view, pmConfig *config);
+
+bool pmFPAfileCreate (pmFPAfile *file, const pmFPAview *view, const pmConfig *config);
+
+// write to the real file corresponding to the given pmFPAfile for the current view
+bool pmFPAfileWrite (pmFPAfile *file, const pmFPAview *view, pmConfig *config);
+
+// close the real file corresponding to the given pmFPAfile appropriate to the current view
+bool pmFPAfileClose (pmFPAfile *file, const pmFPAview *view);
+
+// free the data at this level
+bool pmFPAfileFreeData(pmFPAfile *file, const pmFPAview *view);
+
+// set the state of the specified pmFPAfile to active (state == true) or inactive
+// if name is NULL, set the state for all pmFPAfiles
+bool pmFPAfileActivate (psMetadata *files, bool state, const char *name);
+
+/// Set the state of a single pmFPAfile (in the case of multiple files with the same name)
+///
+/// Returns file activated
+pmFPAfile *pmFPAfileActivateSingle(psMetadata *files, ///< Files to activate
+                                   bool state, ///< State to set
+                                   const char *name, ///< Name of file to activate
+                                   int num    ///< Sequence numbner of file to activate
+    );
+
+// examine all pmFPAfiles listed in the files and perform the needed I/O operations (open,read,write,close)
+bool pmFPAfileIOChecks (pmConfig *config, const pmFPAview *view, pmFPAfilePlace place);
+
+bool pmFPAfileWritePHU(pmFPAfile *file, const pmFPAview *view, pmConfig *config);
+bool pmFPAfileReadPHU (pmFPAfile *file, const pmFPAview *view, pmConfig *config);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAview.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAview.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAview.c	(revision 22322)
@@ -0,0 +1,429 @@
+/** @file  pmFPAview.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-04 03:09:21 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmHDUUtils.h"
+#include "pmFPAview.h"
+
+static void pmFPAviewFree(pmFPAview *view)
+{
+    // No reason to keep this function, apart from the fact that it allows us to type the memBlock
+    return;
+}
+
+pmFPAview *pmFPAviewAlloc(int nRows)
+{
+    pmFPAview *view = psAlloc(sizeof(pmFPAview));
+    psMemSetDeallocator(view, (psFreeFunc) pmFPAviewFree);
+
+    view->nRows = nRows;
+    pmFPAviewReset(view);
+    return view;
+}
+
+bool psMemCheckFPAview(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmFPAviewFree);
+}
+
+
+bool pmFPAviewReset(pmFPAview *view)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    view->chip    = -1;
+    view->cell    = -1;
+    view->readout = -1;
+    view->iRows   =  0;
+    return true;
+}
+
+// return a view restricted to the level (must be >= the input level)
+pmFPAview *pmFPAviewForLevel(pmFPALevel level, const pmFPAview *input)
+{
+    PS_ASSERT_PTR_NON_NULL(input, NULL);
+
+    pmFPAview *output = pmFPAviewAlloc (input->nRows);
+    *output = *input;
+
+    switch (level) {
+      case PM_FPA_LEVEL_FPA:
+        output->chip = -1;
+      case PM_FPA_LEVEL_CHIP:
+        output->cell = -1;
+      case PM_FPA_LEVEL_CELL:
+        output->readout = -1;
+        break;
+      default:
+        break;
+    }
+    return output;
+}
+
+pmFPALevel pmFPAviewLevel(const pmFPAview *view)
+{
+    PS_ASSERT_PTR_NON_NULL(view, PM_FPA_LEVEL_NONE);
+
+    if (view->chip < 0) {
+        return PM_FPA_LEVEL_FPA;
+    }
+    if (view->cell < 0) {
+        return PM_FPA_LEVEL_CHIP;
+    }
+    if (view->readout < 0) {
+        return PM_FPA_LEVEL_CELL;
+    }
+    return PM_FPA_LEVEL_READOUT;
+}
+
+pmChip *pmFPAviewThisChip(const pmFPAview *view, const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa->chips, NULL);
+
+    if (view->chip < 0) {
+        return NULL;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        return NULL;
+    }
+
+    pmChip *chip = fpa->chips->data[view->chip];
+    return chip;
+}
+
+pmChip *pmFPAviewNextChip(pmFPAview *view, const pmFPA *fpa, int nStep)
+{
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    view->cell = -1;
+    view->readout = -1;
+    view->iRows = 0;
+
+    // if there are no available chips, return NULL
+    if (fpa->chips->n <= 0) {
+        view->chip = -1;
+        return NULL;
+    }
+
+    // clean up < -1 values
+    if (view->chip < -1) {
+        view->chip = -1;
+    }
+
+    // increment to the next chip
+    view->chip += nStep;
+
+    // if we are at the end of the stack, return NULL
+    if (view->chip >= fpa->chips->n) {
+        view->chip = -1;
+        return NULL;
+    }
+
+    // get the correct chip pointer
+    pmChip *chip = fpa->chips->data[view->chip];
+    return (chip);
+}
+
+pmCell *pmFPAviewThisCell(const pmFPAview *view, const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+    if (view->cell < 0) {
+        return NULL;
+    }
+    PS_ASSERT_PTR_NON_NULL(fpa->chips, NULL);
+
+    pmChip *chip = pmFPAviewThisChip (view, fpa);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    if (view->cell >= chip->cells->n) {
+        return NULL;
+    }
+
+    pmCell *cell = chip->cells->data[view->cell];
+    return cell;
+}
+
+pmCell *pmFPAviewNextCell (pmFPAview *view, const pmFPA *fpa, int nStep)
+{
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    pmChip *chip = pmFPAviewThisChip (view, fpa);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    view->readout = -1;
+    view->iRows = 0;
+
+    // if there are no available cells, return NULL
+    if (chip->cells->n <= 0) {
+        view->cell = -1;
+        return NULL;
+    }
+
+    // clean up < -1 values
+    if (view->cell < -1) {
+        view->cell = -1;
+    }
+
+    // increment to the next cell
+    view->cell += nStep;
+
+    // if we are at the end of the stack, return NULL
+    if (view->cell >= chip->cells->n) {
+        view->cell = -1;
+        return NULL;
+    }
+
+    // get the correct cell pointer
+    pmCell *cell = chip->cells->data[view->cell];
+    return (cell);
+}
+
+pmReadout *pmFPAviewThisReadout (const pmFPAview *view, const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    if (view->readout < 0) {
+        return NULL;
+    }
+
+    pmCell *cell = pmFPAviewThisCell (view, fpa);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->readouts, NULL);
+
+    if (view->readout >= cell->readouts->n) {
+        return NULL;
+    }
+
+    pmReadout *readout = cell->readouts->data[view->readout];
+    return readout;
+}
+
+pmReadout *pmFPAviewNextReadout (pmFPAview *view, const pmFPA *fpa, int nStep)
+{
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    pmCell *cell = pmFPAviewThisCell (view, fpa);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    view->iRows = 0;
+
+    // if there are no available cells, return NULL
+    if (cell->readouts->n <= 0) {
+        view->readout = -1;
+        return NULL;
+    }
+
+    // clean up < -1 values
+    if (view->readout < -1) {
+        view->readout = -1;
+    }
+
+    // increment to the next cell
+    view->readout += nStep;
+
+    // if we are at the end of the stack, return NULL
+    if (view->readout >= cell->readouts->n) {
+        view->readout = -1;
+        return NULL;
+    }
+
+    // get the correct cell pointer
+    pmReadout *readout = cell->readouts->data[view->readout];
+    return (readout);
+}
+
+pmHDU *pmFPAviewThisHDU(const pmFPAview *view, const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    // the HDU is attached to a cell, chip or fpa
+    // if this view has a -1 for the level which contains the hdu,
+    // there is no unambiguous HDU
+
+    if (view->chip < 0) {
+        return pmHDUFromFPA (fpa);
+    }
+    if (view->cell < 0) {
+        return pmHDUFromChip (pmFPAviewThisChip (view, fpa));
+    }
+    if (view->readout < 0) {
+        return pmHDUFromCell (pmFPAviewThisCell (view, fpa));
+    }
+    return pmHDUFromReadout (pmFPAviewThisReadout (view, fpa));
+}
+
+pmHDU *pmFPAviewThisPHU(const pmFPAview *view, const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    // select the HDU which corresponds to the PHU containing this view
+
+    pmHDU *hdu;
+    pmFPAview new;
+    pmChip *chip;
+    pmCell *cell;
+
+    new = *view;
+
+    if (view->chip < 0) {
+        hdu = pmHDUFromFPA (fpa);
+        if (!hdu)
+            return NULL;
+        if (hdu->blankPHU)
+            return hdu;
+        return NULL;
+    }
+    if (view->cell < 0) {
+        chip = pmFPAviewThisChip (view, fpa);
+        hdu  = pmHDUFromChip (chip);
+        if (!hdu)
+            return NULL;
+        if (hdu->blankPHU)
+            return hdu;
+        new.chip = -1;
+        hdu = pmFPAviewThisPHU (&new, fpa);
+        return hdu;
+    }
+    if (view->readout < 0) {
+        cell = pmFPAviewThisCell (view, fpa);
+        hdu  = pmHDUFromCell (cell);
+        if (!hdu) {
+            psAbort("a split readout is not covered by the current paradigm");
+        }
+        if (hdu->blankPHU)
+            return hdu;
+        new.cell = -1;
+        hdu = pmFPAviewThisPHU (&new, fpa);
+        return hdu;
+    }
+    return NULL;
+}
+
+pmFPAview *pmFPAviewGenerate(const pmFPA *fpa, const pmChip *chip, const pmCell *cell,
+                             const pmReadout *readout)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View to return
+
+    if (!chip) {
+        return view;
+    }
+
+    for (view->chip = 0; view->chip < fpa->chips->n && fpa->chips->data[view->chip] != chip; view->chip++);
+    if (view->chip == fpa->chips->n) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find chip %p in fpa.", chip);
+        psFree(view);
+        return NULL;
+    }
+
+    if (!cell) {
+        return view;
+    }
+
+    for (view->cell = 0; view->cell < chip->cells->n && chip->cells->data[view->cell] != cell; view->cell++);
+    if (view->cell == chip->cells->n) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find cell %p in chip.", cell);
+        psFree(view);
+        return NULL;
+    }
+
+    if (!readout) {
+        return view;
+    }
+
+    for (view->readout = 0;
+         view->readout < cell->readouts->n && cell->readouts->data[view->readout] != readout;
+         view->readout++);
+    if (view->readout == cell->readouts->n) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find readout %p in cell.", readout);
+        psFree(view);
+        return NULL;
+    }
+
+    return view;
+}
+
+pmFPAview *pmFPAviewTop(const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View to top
+
+    view->chip = -1;
+    view->cell = -1;
+    if (!fpa->hdu) {
+        int numChips = 0;                   // Number of active chips
+        psArray *chips = fpa->chips;        // Chips of interest
+        for (int i = 0; i < chips->n; i++) {
+            pmChip *chip = chips->data[i];  // Chip of interest
+            if (!chip) {
+                continue;
+            }
+            if (chip->hdu) {
+                numChips++;
+                view->chip = i;
+            } else {
+                int numCells = 0;               // Number of active cells
+                psArray *cells = chip->cells;   // Cells of interest
+                for (int j = 0; j < cells->n; j++) {
+                    pmCell *cell = cells->data[j]; // Cell of interest
+                    if (!cell) {
+                        continue;
+                    }
+                    if (cell->hdu) {
+                        numCells++;
+                        view->cell = j;
+                    }
+                }
+
+                if (numCells > 1) {
+                    if (numCells != cells->n) {
+                        psWarning("More than one, but not all cells are active.");
+                    }
+                    view->cell = -1;
+                }
+            }
+        }
+
+        if (numChips > 1) {
+            if (numChips != chips->n) {
+                psWarning("More than one, but not all chips are active.");
+            }
+            view->chip = -1;
+            view->cell = -1;
+        }
+    }
+
+    return view;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAview.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAview.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmFPAview.h	(revision 22322)
@@ -0,0 +1,131 @@
+/* @file pmFPA.h
+ * @brief Tools to manipulate the FPA structure elements.
+ *
+ * @author Eugene Magnier, IfA
+ *
+ * @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-25 22:05:58 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_VIEW_H
+#define PM_FPA_VIEW_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+
+/// Identifier for FPA components
+///
+/// This structure allows the identification of a single component of the focal plane hierarchy (or multiple,
+/// if we consider selecting all those components below the selected component).  Components are identified on
+/// the basis of their chip, cell, readout index.  An index of -1 means all components at that level.
+/// Additionally, since readouts may be read piecemeal, there are additional indices for these.
+typedef struct
+{
+    int chip;                           ///< Number of the chip, or -1 for all
+    int cell;                           ///< Number of the cell, or -1 for all
+    int readout;                        ///< Number of the readout, or -1 for all
+    int nRows;                          ///< Maximum number of rows per readout segment read, or 0 for all
+    int iRows;                          ///< Starting point for this read
+}
+pmFPAview;
+
+/// Allocator for pmFPAview
+pmFPAview *pmFPAviewAlloc(int nRows);   ///< Maximum number of rows per readout segment read, or 0 for all
+bool psMemCheckFPAview(psPtr ptr);
+
+/// Reset a view to select all components
+bool pmFPAviewReset(pmFPAview *view     ///< View to reset
+                   );
+
+// return a view restricted to the level (must be >= the input level)
+pmFPAview *pmFPAviewForLevel(pmFPALevel level, const pmFPAview *input);
+
+/// Determine the current view level
+///
+/// Returns the level appropriate for the view
+pmFPALevel pmFPAviewLevel(const pmFPAview *view ///< View to examine
+                         );
+
+// Lookups
+
+/// Return the currently selected chip for this view
+///
+/// Returns NULL if the selection is not specific or invalid
+pmChip *pmFPAviewThisChip(const pmFPAview *view, ///< Current view
+                          const pmFPA *fpa ///< FPA containing chip
+                         );
+
+/// Return the currently selected cell for this view
+///
+/// Returns NULL if the selection is not specific or invalid
+pmCell *pmFPAviewThisCell(const pmFPAview *view, ///< Current view
+                          const pmFPA *fpa ///< FPA containing cell
+                         );
+
+/// Return the currently selected readout for this view
+///
+/// Returns NULL if the selection is not specific or invalid
+pmReadout *pmFPAviewThisReadout(const pmFPAview *view, ///< Current view
+                                const pmFPA *fpa ///< FPA containing readout
+                               );
+
+// Incrementors
+
+/// Advance view to the next chip
+///
+/// Returns NULL if there is no next
+pmChip *pmFPAviewNextChip(pmFPAview *view, ///< Current view
+                          const pmFPA *fpa, ///< FPA containing chips
+                          int nStep     ///< Number of chips to increment
+                         );
+
+/// Advance view to the next cell
+///
+/// Returns NULL if there is no next
+pmCell *pmFPAviewNextCell(pmFPAview *view, ///< Current view
+                          const pmFPA *fpa, ///< FPA containing cells
+                          int nStep     ///< Number of cells to increment
+                         );
+
+/// Advance view to the next readout
+///
+/// Returns NULL if there is no next
+pmReadout *pmFPAviewNextReadout(pmFPAview *view, ///< Current view
+                                const pmFPA *fpa, ///< FPA containing readouts
+                                int nStep ///< Number of readouts to increment
+                               );
+
+/// Return the HDU corresponding to the current view
+///
+/// Uses the pmHDUFrom* functions, combined with the view.
+pmHDU *pmFPAviewThisHDU(const pmFPAview *view, ///< Current view
+                        const pmFPA *fpa ///< FPA for view
+                       );
+
+/// Return the blank Primary HDU corresponding to the current view, if any
+///
+/// Similar to pmFPAviewThisHDU, except returns NULL if no HDU is found, or the HDU is not a blank Primary HDU
+pmHDU *pmFPAviewThisPHU(const pmFPAview *view, ///< Current view
+                        const pmFPA *fpa ///< FPA for view
+                       );
+
+/// Generate a view, given a chip, cell, readout.
+///
+/// Uses the pointer value in the array of the parent to locate the child
+pmFPAview *pmFPAviewGenerate(const pmFPA *fpa, ///< FPA of interest
+                             const pmChip *chip, ///< Chip of interest, or NULL
+                             const pmCell *cell, ///< Cell of interest, or NULL
+                             const pmReadout *reaodut ///< Readout of interest, or NULL
+    );
+
+
+/// Determine the view suitable for the top level of the provided FPA
+pmFPAview *pmFPAviewTop(const pmFPA *fpa ///< FPA of interest
+    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDU.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDU.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDU.c	(revision 22322)
@@ -0,0 +1,244 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmConfigMask.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static (private) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Move to the appropriate extension in FITS file for HDU
+static bool hduMove(pmHDU *hdu,         // HDU with extname
+                    psFits *fits        // FITS file in which to move
+                   )
+{
+    // Deal with the PHU case
+    if (hdu->blankPHU || !hdu->extname) {
+        if (!psFitsMoveExtNum(fits, 0, false)) {
+            psError(PS_ERR_IO, false, "Unable to move to primary header!\n");
+            return false;
+        }
+        return true;
+    }
+
+    if (!psFitsMoveExtName(fits, hdu->extname)) {
+        psError(PS_ERR_IO, false, "Unable to move to extension %s\n", hdu->extname);
+        return false;
+    }
+
+    return true;
+}
+
+static void hduFree(pmHDU *hdu)
+{
+    psFree(hdu->extname);
+    psFree(hdu->format);
+    psFree(hdu->header);
+    psFree(hdu->images);
+    psFree(hdu->weights);
+    psFree(hdu->masks);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+pmHDU *pmHDUAlloc(const char *extname)
+{
+    pmHDU *hdu = psAlloc(sizeof(pmHDU));
+    psMemSetDeallocator(hdu, (psFreeFunc)hduFree);
+
+    if (!extname || strlen(extname) == 0) {
+        hdu->blankPHU = true;
+        hdu->extname = NULL;
+    } else {
+        hdu->blankPHU = false;
+        hdu->extname = psStringCopy(extname);
+    }
+    hdu->format  = NULL;
+    hdu->header  = NULL;
+    hdu->images  = NULL;
+    hdu->weights = NULL;
+    hdu->masks   = NULL;
+
+    return hdu;
+}
+
+bool psMemCheckHDU(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) hduFree);
+}
+
+
+bool pmHDUReadHeader(pmHDU *hdu, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    // Move to the appropriate extension
+    psTrace("psModules.camera", 5, "Moving to extension %s...\n", hdu->extname);
+    if (!hduMove(hdu, fits)) {
+        return false;
+    }
+
+    if (!hdu->header) {
+        psTrace("psModules.camera", 5, "Reading the header...\n");
+        hdu->header = psFitsReadHeader(hdu->header, fits);
+        if (! hdu->header) {
+            psError(PS_ERR_IO, false, "Unable to read header for extension %s\n", hdu->extname);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// Read an HDU from a FITS file
+// XXX: Add a region specifier?
+bool hduRead(pmHDU *hdu,                // HDU to write
+             psArray **images,          // Images into which to read
+             psFits *fits               // FITS file to read
+            )
+{
+    assert(hdu);
+    assert(images);
+    assert(fits);
+
+    // Read the header; includes the move
+    if (!pmHDUReadHeader(hdu, fits)) {
+        return false;
+    }
+
+    if (hdu->blankPHU) {
+        // Done already!
+        return true;
+    }
+
+    if (*images) {
+        psWarning("HDU %s has already been read --- overwriting.\n", hdu->extname);
+        psFree(*images);                // Blow away anything existing
+    }
+    psTrace("psModules.camera", 5, "Reading the pixels...\n");
+    *images = psFitsReadImageCube(fits, psRegionSet(0,0,0,0));
+    if (!*images) {
+        psError(PS_ERR_IO, false, "Unable to read pixels for extension %s\n", hdu->extname);
+        return false;
+    }
+    return true;
+}
+
+bool pmHDURead(pmHDU *hdu, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    return hduRead(hdu, &hdu->images, fits);
+}
+
+bool pmHDUReadMask(pmHDU *hdu, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    return hduRead(hdu, &hdu->masks, fits);
+}
+
+bool pmHDUReadWeight(pmHDU *hdu, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    return hduRead(hdu, &hdu->weights, fits);
+}
+
+// Write an HDU to a FITS file
+static bool hduWrite(pmHDU *hdu,        // HDU to write
+                     const psArray *images, // Images to write
+                     const psArray *masks, // Masks to use when writing
+                     psMaskType maskVal,// Value to mask
+                     psFits *fits       // FITS file to which to write
+                    )
+{
+    assert(hdu);
+    assert(fits);
+
+    psTrace("psModules.camera", 7, "Writing HDU %s\n", hdu->extname);
+
+    if (!images && !hdu->header) {
+        psWarning("Nothing to write for HDU %s\n", hdu->extname);
+        return false;
+    }
+
+    // Preserve the extension name, if it's the PHU
+    char *extname = hdu->extname;       // The name of the extension
+    if (!extname && hdu->header) {
+        bool mdok = true;               // Status of MD lookup
+        extname = psMetadataLookupStr(&mdok, hdu->header, "EXTNAME");
+        if (!mdok || !extname || strlen(extname) == 0) {
+            extname = "";
+        }
+    }
+
+    // Make sure it's recognisable as what it's supposed to be
+    if (!hdu->header) {
+        hdu->header = psMetadataAlloc();
+    }
+    if (!pmConfigConformHeader(hdu->header, hdu->format)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to conform header to format.\n");
+        return false;
+    }
+
+    // Only a header
+    if (!images && !psFitsWriteBlank(fits, hdu->header, extname)) {
+        psError(PS_ERR_IO, false, "Unable to write header for extension %s\n", extname);
+        return false;
+    }
+
+    if (images) {
+        psTrace("psModules.camera", 9, "Writing pixels for %s\n", hdu->extname);
+        if (!psFitsWriteImageCubeWithMask(fits, hdu->header, images, masks, maskVal, extname)) {
+            psError(PS_ERR_IO, false, "Unable to write image to extension %s\n", hdu->extname);
+            return false;
+        }
+    }
+    return true;
+}
+
+// XXX: Add a region specifier?
+bool pmHDUWrite(pmHDU *hdu, psFits *fits, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    psMaskType maskVal = pmConfigMaskGet("MASK.VALUE", config); // Value to mask
+    return hduWrite(hdu, hdu->images, hdu->masks, maskVal, fits);
+}
+
+bool pmHDUWriteMask(pmHDU *hdu, psFits *fits, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    // We don't supply a mask because we're writing the mask!
+    return hduWrite(hdu, hdu->masks, NULL, 0, fits);
+}
+
+bool pmHDUWriteWeight(pmHDU *hdu, psFits *fits, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    psMaskType maskVal = pmConfigMaskGet("MASK.VALUE", config); // Value to mask
+    return hduWrite(hdu, hdu->weights, hdu->masks, maskVal, fits);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDU.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDU.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDU.h	(revision 22322)
@@ -0,0 +1,88 @@
+/* @file pmHDU.h
+ * @brief Define a header data unit (from a FITS file), with functions to read and write
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-09-05 08:21:35 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_HDU_H
+#define PM_HDU_H
+
+#include <pslib.h>
+#include "pmConfig.h"
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// An instance of the FITS Header Data Unit
+///
+/// Of course, it is not an exact replica of a FITS HDU --- they have no mask and weight data, but these are
+/// stored here for convenience --- it keeps all the relevant data about the image in one place.
+typedef struct
+{
+    psString extname;                   ///< The extension name
+    bool blankPHU;                      ///< Is this a blank FITS Primary Header Unit, i.e., no data?
+    psMetadata *format;                 ///< The camera format
+    psMetadata *header;                 ///< The FITS header, or NULL if primary for FITS; or section info
+    psArray *images;                    ///< The pixel data
+    psArray *weights;                   ///< The pixel data
+    psArray *masks;                     ///< The pixel data
+}
+pmHDU;
+
+
+/// Allocator for pmHDU
+pmHDU *pmHDUAlloc(const char *extname);   ///< Extension name, or NULL for PHU
+bool psMemCheckHDU(psPtr ptr);
+
+/// Read the HDU header only
+///
+/// Moves to the appropriate extension
+bool pmHDUReadHeader(pmHDU *hdu,        ///< HDU for which to read header
+                     psFits *fits       ///< FITS file from which to read
+                    );
+
+/// Read the HDU header and pixels
+///
+/// Moves to the appropriate extension
+bool pmHDURead(pmHDU *hdu,              ///< HDU to read
+               psFits *fits             ///< FITS file to read from
+              );
+
+/// Read the HDU header and mask
+///
+/// Moves to the appropriate extension
+bool pmHDUReadMask(pmHDU *hdu,          ///< HDU to read
+                   psFits *fits         ///< FITS file to read from
+                  );
+
+/// Read the HDU header and weight map
+///
+/// Moves to the appropriate extension
+bool pmHDUReadWeight(pmHDU *hdu,        ///< HDU to read
+                     psFits *fits       ///< FITS file to read from
+    );
+
+/// Write the HDU header and pixels
+bool pmHDUWrite(pmHDU *hdu,             ///< HDU to write
+                psFits *fits,           ///< FITS file to write to
+                const pmConfig *config  ///< Configuration
+    );
+
+/// Write the HDU header and mask
+bool pmHDUWriteMask(pmHDU *hdu,         ///< HDU to write
+                    psFits *fits,       ///< FITS file to write to
+                    const pmConfig *config  ///< Configuration
+    );
+
+/// Write the HDU header and weight map
+bool pmHDUWriteWeight(pmHDU *hdu,       ///< HDU to write
+                      psFits *fits,     ///< FITS file to write to
+                      const pmConfig *config  ///< Configuration
+    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUGenerate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUGenerate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUGenerate.c	(revision 22322)
@@ -0,0 +1,711 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmHDUUtils.h"
+#include "pmConcepts.h"
+#include "pmConceptsStandard.h"
+#include "pmHDUGenerate.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Add cells in a chip to a list
+static bool addCellsFromChip(psList *list, // List of cells
+                             const pmChip *chip // The chip from which to add cells
+                            )
+{
+    assert(list);
+    assert(chip);
+
+    psArray *cells = chip->cells;       // Array of cells
+    bool result = true;                 // Result of adding cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // A cell
+        if (!cell->hdu) {               // Don't add cells that have their own HDU
+            result |= psListAdd(list, PS_LIST_TAIL, cell);
+        }
+    }
+
+    return result;
+}
+
+// Add cells in an FPA to a list
+static bool addCellsFromFPA(psList *list, // List of cells
+                            const pmFPA *fpa // The FPA from which to add cells
+                           )
+{
+    assert(list);
+    assert(fpa);
+
+    psArray *chips = fpa->chips;        // Array of chips
+    bool result = true;                 // Result of adding cells
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // A chip
+        if (! chip->hdu) {              // Don't add chips that have their own HDU
+            result |= addCellsFromChip(list, chip);
+        }
+    }
+
+    return result;
+}
+
+// Get the maximum extent of the HDU from the trimsec and biassecs
+static bool sizeHDU(int *xSize, int *ySize, // Size of HDU
+                    psList *cells       // List of cells
+                   )
+{
+    psListIterator *cellsIter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
+    pmCell *cell = NULL;                // The cell from iteration
+    bool mdok = true;                   // Status of MD lookup
+    *xSize = 0;
+    *ySize = 0;
+    while ((cell = psListGetAndIncrement(cellsIter))) {
+        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+        if (mdok && trimsec && !psRegionIsNaN(*trimsec)) {
+            *xSize = PS_MAX(trimsec->x1, *xSize);
+            *ySize = PS_MAX(trimsec->y1, *ySize);
+        } else {
+            psFree(cellsIter);
+            return false;
+        }
+        psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
+        if (mdok && biassecs) {
+            psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
+            psRegion *biassec = NULL;   // The bias section
+            while ((biassec = psListGetAndIncrement(biassecsIter))) {
+                if (!psRegionIsNaN(*biassec)) {
+                    *xSize = PS_MAX(biassec->x1, *xSize);
+                    *ySize = PS_MAX(biassec->y1, *ySize);
+                } else {
+                    psFree(biassecsIter);
+                    psFree(cellsIter);
+                    return false;
+                }
+            }
+            psFree(biassecsIter);
+        }
+    }
+    psFree(cellsIter);
+
+    return (*xSize != 0 && *ySize != 0);
+}
+
+
+static psRegion *sectionForImage(int *position, // Position on the output image, updated
+                                 const psImage *image, // Image containing the sizes and offsets
+                                 int readdir // Read direction, 1=rows, 2=cols
+                                )
+{
+    psRegion *region = NULL;            // The region to return
+    switch (readdir) {
+    case 1:                           // Read direction is rows
+        region = psRegionAlloc(*position, *position + image->numCols, image->row0,
+                               image->row0 + image->numRows);
+        *position += image->numCols;
+        break;
+    case 2:                           // Read direction is columns
+        region = psRegionAlloc(image->col0, image->col0 + image->numCols, *position,
+                               *position + image->numRows);
+        *position += image->numRows;
+        break;
+    default:
+        psAbort("Shouldn't ever get here!\n");
+    }
+
+    return region;
+}
+
+static bool doBiasSections(int *position, // Position on the output image, updated
+                           int *readdir,// Read direction for cells
+                           pmCell *cell // Cell
+                          )
+{
+    psMetadataItem *biassecItem = psMetadataLookup(cell->concepts, "CELL.BIASSEC"); // Bias sections
+    if (!biassecItem) {
+        psWarning("CELL.BIASSEC has not been initialised in cell --- ignored.\n");
+        return false;
+    }
+    psFree(biassecItem->data.V);        // Blow away the old list
+    psList *biassecs = psListAlloc(NULL);
+    biassecItem->data.V = biassecs;
+
+    bool mdok = true;                   // Status of MD lookup
+    int cellreaddir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
+    if (!mdok || (cellreaddir != 1 && cellreaddir != 2)) {
+        // Probably unnecessary, but just in case....
+        psWarning("CELL.READDIR is not set in cell --- ignored.\n");
+        return false;
+    }
+    if (*readdir == 0) {
+        *readdir = cellreaddir;
+    } else if (*readdir != cellreaddir) {
+        psWarning("CELL.READDIR does not match read direction for HDU --- ignored.\n");
+        return false;
+    }
+
+    pmReadout *readout = cell->readouts->data[0]; // The first readout, as representative
+    psList *biases = readout->bias; // The bias images from the readout
+
+    psListIterator *biasIter = psListIteratorAlloc(biases, PS_LIST_HEAD, true); // Iterator for biases
+    psImage *bias = NULL;       // Bias image from iteration
+    while ((bias = psListGetAndIncrement(biasIter))) {
+        // Construct a region
+        psRegion *biassec = sectionForImage(position, bias, *readdir);
+        psListAdd(biassecs, PS_LIST_TAIL, biassec);
+        psFree(biassec);        // Drop reference
+    }
+    psFree(biasIter);
+
+    return true;
+}
+
+// Attempt to read CELL.TRIMSEC and CELL.BIASSEC from the cell format if they are specified by VALUE
+static bool readTrimBias(psList *cells // List of cells below the HDU
+    )
+{
+    bool mdok;                          // Status of MD lookup
+    psListIterator *cellsIter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
+    pmCell *cell = NULL;                // Cell from iteration
+    bool allFixed = true;               // We're able to fix all TRIMSEC and BIASSEC
+    while ((cell = psListGetAndIncrement(cellsIter))) {
+        psMetadataItem *trimsecItem = psMetadataLookup(cell->concepts, "CELL.TRIMSEC"); // Item with trimsec
+        if (!trimsecItem || trimsecItem->type != PS_DATA_REGION) {
+            psWarning("CELL.TRIMSEC has not been initialised in cell --- ignored.\n");
+            return false;
+        }
+        psRegion *trimsec = trimsecItem->data.V; // Trim section
+        if (!trimsec || psRegionIsNaN(*trimsec)) {
+            const char *trimsecSource = psMetadataLookupStr(&mdok, cell->config, "CELL.TRIMSEC.SOURCE");
+            if (strcmp(trimsecSource, "VALUE") == 0) {
+
+                const char *trimsecStr = psMetadataLookupStr(&mdok, cell->config, "CELL.TRIMSEC");
+                if (!trimsec) {
+                    trimsec = trimsecItem->data.V = psRegionAlloc(NAN, NAN, NAN, NAN);
+                }
+                *trimsec = psRegionFromString(trimsecStr);
+            } else {
+                allFixed = false;
+            }
+        }
+
+        psMetadataItem *biassecItem = psMetadataLookup(cell->concepts, "CELL.BIASSEC"); // Bias sections
+        if (!biassecItem) {
+            psWarning("CELL.BIASSEC has not been initialised in cell --- ignored.\n");
+            return false;
+        }
+        psList *biassecs = biassecItem->data.V;
+        if (!biassecs || biassecs->n != 0) {
+            allFixed = false;
+            continue;
+        }
+
+        const char *biassecSource = psMetadataLookupStr(&mdok, cell->config, "CELL.BIASSEC.SOURCE");
+        if (biassecSource && strcmp(biassecSource, "VALUE") == 0) {
+            const char *biassecStr = psMetadataLookupStr(&mdok, cell->config, "CELL.BIASSEC");
+            psFree(biassecItem->data.V);
+            biassecItem->data.V = p_pmConceptParseRegions(biassecStr);
+        } else {
+            allFixed = false;
+        }
+    }
+    psFree(cellsIter);
+
+    return allFixed;
+}
+
+
+// Generate CELL.TRIMSEC and CELL.BIASSEC for the cells
+static bool generateTrimBias(psList *cells // List of cells below the HDU
+                            )
+{
+    pmCell *cell = NULL;                // Cell from iteration
+    int numCells = cells->n;            // Number of cells
+    int cellNum = 0;                    // The cell number
+    int position = 0;                   // Position on the image
+    bool mdok = true;                   // Status of MD lookup
+    int readdir = 0;                    // Read direction (1=rows, 2=cols)
+
+    // First run through to do the LHS biases
+    psListIterator *cellsIter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
+    bool done = false;                  // Done with iteration (due to being halfway through)?
+    while ((cell = psListGetAndIncrement(cellsIter)) && !done) {
+        if (cellNum <= numCells/2 - 1) {
+            doBiasSections(&position, &readdir, cell);
+            cellNum++;
+        } else {
+            done = true;
+        }
+    }
+
+    // Second run through to do the trim sections
+    psListIteratorSet(cellsIter, PS_LIST_HEAD);
+    while ((cell = psListGetAndIncrement(cellsIter))) {
+        psMetadataItem *trimsecItem = psMetadataLookup(cell->concepts, "CELL.TRIMSEC"); // Item with trimsec
+        if (!trimsecItem || trimsecItem->type != PS_DATA_REGION) {
+            psWarning("CELL.TRIMSEC has not been initialised in cell --- ignored.\n");
+            continue;
+        }
+        psRegion *trimsec = trimsecItem->data.V; // Trim section
+
+        int cellreaddir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
+        if (!mdok || (cellreaddir != 1 && cellreaddir != 2)) {
+            // Probably unnecessary, but just in case....
+            psWarning("CELL.READDIR is not set in cell --- ignored.\n");
+            continue;
+        }
+        if (readdir == 0 && mdok && cellreaddir != 0) {
+            readdir = cellreaddir;
+        } else if (readdir != cellreaddir) {
+            psWarning("CELL.READDIR for cells within the HDU do not match!\n");
+            cellreaddir = readdir;
+        }
+
+        pmReadout *readout = cell->readouts->data[0]; // The first readout, as representative
+        // The proper image, used to get the size
+        psImage *image = readout->image ? readout->image : (readout->mask ? readout->mask : readout->weight);
+        if (!image) {
+            continue;
+        }
+        if (readout->mask &&
+                (readout->mask->numCols != image->numCols || readout->mask->numRows != image->numRows)) {
+            psWarning("Image and mask have different sizes (%dx%d vs %dx%d)!\n",
+                     image->numCols, image->numRows, readout->mask->numCols, readout->mask->numRows);
+        }
+        if (readout->weight &&
+                (readout->weight->numCols != image->numCols || readout->weight->numRows != image->numRows)) {
+            psWarning("Image and weight have different sizes (%dx%d vs %dx%d)!\n",
+                     image->numCols, image->numRows, readout->weight->numCols, readout->weight->numRows);
+        }
+        // New reference
+        trimsec = sectionForImage(&position, image, cellreaddir);
+        psFree(trimsecItem->data.V);
+        trimsecItem->data.V = trimsec;
+    }
+
+    // A final run through to do the RHS biases
+    psListIteratorSet(cellsIter, cellNum);
+    while ((cell = psListGetAndIncrement(cellsIter))) {
+        doBiasSections(&position, &readdir, cell);
+    }
+
+    // Clean up
+    psFree(cellsIter);
+
+    return (position > 0);
+}
+
+// Check the type for a current image against a previous type
+static psElemType checkTypes(psElemType previous, // Previously defined type, or 0
+                             psElemType current // Current type
+                            )
+{
+    if (previous == 0) {
+        return current;
+    }
+
+    if (previous != current) {
+        psWarning("Images within the HDU are of different types (%x vs %x) --- promoting\n",
+                  previous, current);
+        return PS_MAX(previous, current);
+    }
+
+    return previous;
+}
+
+
+// Paste the source image into the target, according to the provided region.  The source is then updated to
+// reference the region within the target.
+static psImage *pasteImage(psImage *target, // Target image, into which the paste is made
+                           psImage *source,// Source image, from which the paste is made, and then changed
+                           psRegion *region // Image section into which to paste
+                          )
+{
+    if (source->numCols != region->x1 - region->x0 || source->numRows != region->y1 - region->y0) {
+        psString regionString = psRegionToString(*region);
+        psWarning("Image size (%dx%d) does not match region (%s).\n",
+                 source->numCols, source->numRows, regionString);
+        psFree(regionString);
+    }
+    psImageOverlaySection(target, source, region->x0, region->y0, "=");
+
+    // Reference the HDU version, so that subsequent changes will touch the HDU
+    return psImageSubset(target, *region);
+}
+
+
+// Generate the HDU, given a list of cells below that HDU.  This is the main engine function, that does all
+// the work.
+static bool generateHDU(pmHDU *hdu,     // HDU to generate
+                        psList *cells   // List of cells
+                       )
+{
+    // Check the number of readouts is consistent within the HDU
+    int numReadouts = -1;               // Number of readouts
+    psElemType imageType = 0;           // Type of readout images
+    psElemType maskType = 0;            // Type of readout masks
+    psElemType weightType = 0;          // Type of readout weights
+    {
+        psListIterator *iter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
+        pmCell *cell = NULL;                // The cell from iteration
+        while ((cell = psListGetAndIncrement(iter)))
+        {
+            psArray *readouts = cell->readouts;
+            if (numReadouts == -1) {
+                numReadouts = readouts->n;
+            } else if (readouts->n != numReadouts) {
+                psError(PS_ERR_IO, true, "Number of readouts doesn't match: %ld vs %d\n", readouts->n,
+                        numReadouts);
+                return false;
+            }
+            for (int i = 0; i < numReadouts; i++) {
+                pmReadout *readout = readouts->data[i]; // The readout
+                if (!readout) {
+                    continue;
+                }
+
+                if (!hdu->images && readout->image) {
+                    imageType = checkTypes(imageType, readout->image->type.type);
+                }
+                if (!hdu->masks && readout->mask) {
+                    maskType = checkTypes(maskType, readout->mask->type.type);
+                }
+                if (!hdu->weights && readout->weight) {
+                    weightType = checkTypes(weightType, readout->weight->type.type);
+                }
+            }
+        }
+        psFree(iter);
+    }
+    if (numReadouts == 0 || (imageType == 0 && maskType == 0 && weightType == 0)) {
+        // Nothing from which to create an HDU
+        psFree(cells);
+        psError(PS_ERR_IO, true, "Nothing from which to create an HDU\n");
+        return false;
+    }
+
+    // Get the size of the HDU, either from existing trimsec and biassec, or generate these and try again
+    int xSize = 0, ySize = 0;           // Size of HDU
+    if (!sizeHDU(&xSize, &ySize, cells) &&
+        !(readTrimBias(cells) && sizeHDU(&xSize, &ySize, cells)) &&
+        !(generateTrimBias(cells) && sizeHDU(&xSize, &ySize, cells))) {
+        psError(PS_ERR_IO, true, "Unable to determine size of HDU!\n");
+        return false;
+    }
+
+    // Generate the HDU
+    if (imageType) {
+        hdu->images = psArrayAlloc(numReadouts);
+        for (int i = 0; i < numReadouts; i++) {
+            psImage *image = psImageAlloc(xSize, ySize, imageType);
+            psImageInit(image, 0.0);
+            hdu->images->data[i] = image;
+        }
+    }
+    if (maskType) {
+        hdu->masks = psArrayAlloc(numReadouts);
+        for (int i = 0; i < numReadouts; i++) {
+            psImage *mask = psImageAlloc(xSize, ySize, maskType);
+            psImageInit(mask, 0);
+            hdu->masks->data[i] = mask;
+        }
+    }
+    if (weightType) {
+        hdu->weights = psArrayAlloc(numReadouts);
+        for (int i = 0; i < numReadouts; i++) {
+            psImage *weight = psImageAlloc(xSize, ySize, weightType);
+            psImageInit(weight, 0.0);
+            hdu->weights->data[i] = weight;
+        }
+    }
+
+    // Insert the pixels into the HDU
+    {
+        psListIterator *iter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
+        pmCell *cell = NULL;           // The cell from iteration
+        bool mdok = true;               // Result of MD lookup
+        while ((cell = psListGetAndIncrement(iter)))
+        {
+            psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+            if (!mdok || !trimsec) {
+                psAbort("Shouldn't ever get here --- CELL.TRIMSEC should have been set above.\n");
+            }
+            psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias secionts
+            if (!mdok || !biassecs) {
+                psAbort("Shouldn't ever get here --- CELL.BIASSEC should have been set above.\n");
+            }
+            psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
+
+            psArray *readouts = cell->readouts; // Array of readouts
+            psArray *hduImages = hdu->images; // Array of images in the HDU
+            psArray *hduMasks = hdu->masks; // Array of masks in the HDU
+            psArray *hduWeights = hdu->weights; // Array of weights in the HDU
+            for (int i = 0; i < readouts->n; i++) {
+                pmReadout *readout = readouts->data[i]; // The readout of interest
+                if (!readout) {
+                    continue;
+                }
+
+                if (readout->image) {
+                    psImage *new = pasteImage(hduImages->data[i], readout->image, trimsec);
+                    psFree(readout->image);
+                    readout->image = new;
+                }
+                if (readout->mask) {
+                    psImage *new = pasteImage(hduMasks->data[i], readout->mask, trimsec);
+                    psFree(readout->mask);
+                    readout->mask = new;
+                }
+                if (readout->weight) {
+                    psImage *new = pasteImage(hduWeights->data[i], readout->weight, trimsec);
+                    psFree(readout->weight);
+                    readout->weight = new;
+                }
+
+                if (biassecs->n != readout->bias->n) {
+                    psWarning("Number of bias sections (%ld) and number of biases (%ld) do not match.\n",
+                              biassecs->n, readout->bias->n);
+                }
+                psListIterator *biasIter = psListIteratorAlloc(readout->bias, PS_LIST_HEAD, false); // Iteratr
+                psImage *bias = NULL;   // Bias image, from iteration
+                psListIteratorSet(biassecsIter, PS_LIST_HEAD);
+                psRegion *biassec = NULL; // Bias region, from iteration
+                psList *newBias = psListAlloc(NULL); // New list of bias images
+                while ((bias = psListGetAndIncrement(biasIter)) &&
+                        (biassec = psListGetAndIncrement(biassecsIter))) {
+                    psImage *new = pasteImage(hduImages->data[i], bias, biassec);
+                    psListAdd(newBias, PS_LIST_TAIL, new);
+                    psFree(new);        // Drop reference
+                }
+                psFree(biasIter);
+
+                // Add on the new list of bias images
+                psFree(readout->bias);
+                readout->bias = newBias;
+            }
+            psFree(biassecsIter);
+        } // Iterating over cells within the HDU
+        psFree(iter);
+    }
+    psFree(cells);
+
+    return true;
+}
+
+// Return the level that an extension applies to
+static pmFPALevel extensionLevel(pmHDU *hdu // HDU to check
+                                )
+{
+    if (!hdu->format) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "HDU does not have a camera format.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+    bool mdok = true;                   // Status of MD lookup
+    psMetadata *file = psMetadataLookupMetadata(&mdok, hdu->format, "FILE"); // File information for camera format
+    if (!mdok || !file) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Can't file FILE information for camera format "
+                "configuration.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+    psString extensions = psMetadataLookupStr(&mdok, file, "EXTENSIONS"); // Where the HDUs are
+    if (!mdok || !extensions || strlen(extensions) == 0) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Can't find EXTENSIONS in the FILE information of the camera "
+                "format configuration.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+    if (strcasecmp(extensions, "CELL") == 0) {
+        return PM_FPA_LEVEL_CELL;
+    }
+    if (strcasecmp(extensions, "CHIP") == 0) {
+        return PM_FPA_LEVEL_CHIP;
+    }
+    if (strcasecmp(extensions, "FPA") == 0) {
+        return PM_FPA_LEVEL_FPA;
+    }
+    if (strcasecmp(extensions, "NONE") == 0) {
+        return PM_FPA_LEVEL_NONE;
+    }
+    // No idea what it is
+    psError(PS_ERR_IO, true, "EXTENSIONS (%s) in FILE information is not FPA, CHIP, CELL or NONE.\n",
+            extensions);
+    return PM_FPA_LEVEL_NONE;
+}
+
+// Generate HDU for cells belonging to a chip --- just an iterator
+static bool generateForCells(pmChip *chip // Chip for which to generate HDUs
+                            )
+{
+    psArray *cells = chip->cells;       // Array of cells
+    bool status = true;                 // Status of HDU generation
+    for (long i = 0; i < cells->n; i++) {
+        status |= pmHDUGenerateForCell(cells->data[i]);
+    }
+    return status;
+}
+
+// Generate HDU for chips belonging to an FPA --- just an iterator
+static bool generateForChips(pmFPA *fpa // FPA for which to generate HDUs
+                            )
+{
+    psArray *chips = fpa->chips;        // Array of chips
+    bool status = true;                 // Status of HDU generation
+    for (long i = 0; i < chips->n; i++) {
+        status |= pmHDUGenerateForChip(chips->data[i]);
+    }
+    return status;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmHDUGenerateForCell(pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+
+    // Get the HDU and a list of cells below it
+    pmHDU *hdu = pmHDUFromCell(cell); // The HDU in the cell
+    if (!hdu) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Can't find an HDU for cell.\n");
+        return false;
+    }
+    if (hdu->images && hdu->masks && hdu->weights) {
+        // It's already here!
+        return true;
+    }
+
+    pmFPALevel extLevel = extensionLevel(hdu);
+    switch (extLevel) {
+    case PM_FPA_LEVEL_NONE:
+    case PM_FPA_LEVEL_CELL: {
+            psList *cells = psListAlloc(NULL); // List of cells below the HDU
+
+            if (cell->hdu) {
+                psListAdd(cells, PS_LIST_TAIL, cell);
+            } else {
+                pmChip *chip = cell->parent;    // The parent chip
+                if (chip->hdu) {
+                    addCellsFromChip(cells, chip);
+                } else {
+                    pmFPA *fpa = chip->parent;  // The parent FPA
+                    if (fpa->hdu) {
+                        addCellsFromFPA(cells, fpa);
+                    }
+                }
+            }
+            if (cells->n == 0) {
+                // Nothing to do
+                return true;
+            }
+
+            return generateHDU(hdu, cells);
+        }
+    case PM_FPA_LEVEL_CHIP:
+    case PM_FPA_LEVEL_FPA:
+        return pmHDUGenerateForChip(cell->parent);
+    default:
+        psAbort("Shouldn't ever get here: check your camera format configuration.\n");
+    }
+    return false;
+}
+
+bool pmHDUGenerateForChip(pmChip *chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+
+    // Get the HDU and a list of cells below it
+    pmHDU *hdu = pmHDUFromChip(chip);   // The HDU in the chip
+    if (!hdu) {
+        // Nothing here; need to look further down
+        return generateForCells(chip);
+    }
+    if (hdu->images && hdu->masks && hdu->weights) {
+        // It's already here!
+        return true;
+    }
+
+    pmFPALevel extLevel = extensionLevel(hdu);
+    switch (extLevel) {
+    case PM_FPA_LEVEL_CELL:
+        // Work on lower levels
+        return generateForCells(chip);
+    case PM_FPA_LEVEL_NONE:
+    case PM_FPA_LEVEL_CHIP: {
+            // Work on this level
+            psList *cells = psListAlloc(NULL);  // List of cells below the HDU
+            if (chip->hdu) {
+                addCellsFromChip(cells, chip);
+            } else {
+                pmFPA *fpa = chip->parent;  // The parent FPA
+                if (fpa->hdu) {
+                    addCellsFromFPA(cells, fpa);
+                }
+            }
+            if (cells->n == 0) {
+                // Nothing to do
+                return true;
+            }
+
+            return generateHDU(hdu, cells);
+        }
+    case PM_FPA_LEVEL_FPA:
+        // Work on higher levels
+        return pmHDUGenerateForFPA(chip->parent);
+    default:
+        psAbort("Shouldn't ever get here: check your camera format configuration.\n");
+    }
+    return false;
+}
+
+
+bool pmHDUGenerateForFPA(pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    // Get the HDU and a list of cells below it
+    pmHDU *hdu = pmHDUFromFPA(fpa);     // The HDU in the FPA
+    if (!hdu) {
+        // Nothing here; need to look further down
+        return generateForChips(fpa);
+    }
+    if (hdu->images && hdu->masks && hdu->weights) {
+        // It's already here!
+        return true;
+    }
+
+    pmFPALevel extLevel = extensionLevel(hdu);
+    switch (extLevel) {
+    case PM_FPA_LEVEL_CELL:
+    case PM_FPA_LEVEL_CHIP:
+        // Work on lower levels
+        return generateForChips(fpa);
+    case PM_FPA_LEVEL_NONE:
+    case PM_FPA_LEVEL_FPA: {
+            // Work on this level
+            psList *cells = psListAlloc(NULL); // List of cells below the HDU
+            if (fpa->hdu) {
+                addCellsFromFPA(cells, fpa);
+            }
+            if (cells->n == 0) {
+                // Nothing to do
+                return true;
+            }
+
+            return generateHDU(hdu, cells);
+        }
+    default:
+        psAbort("Shouldn't ever get here: check your camera format configuration.\n");
+    }
+    return false;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUGenerate.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUGenerate.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUGenerate.h	(revision 22322)
@@ -0,0 +1,53 @@
+/* @file pmHDUGenerate.h
+ * @brief Generate HDU pixels from FPA components that have pixels
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-30 21:12:56 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_HDU_GENERATE_H
+#define PM_HDU_GENERATE_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Generate an HDU (with CELL.TRIMSEC, CELL.BIASSEC and pixels) for a cell with pixels
+///
+/// The write functions for the FPA hierarchy use pmHDUWrite, which assumes that the images in the readouts
+/// are subimages of the pixels in the HDU structure.  If this is not the case, the HDU pixels can be
+/// generated using some simple assumptions.  Splices the images and overscans together without regard for
+/// CELL.X0 and CELL.Y0 (for a proper mosaic, see pmFPAMosaic), though it should respect CELL.READDIR (so that
+/// the bias and trim sections match properly).  A warning may be generated after running this function if the
+/// bias and trim sections are specified in the camera format by default values rather than in the header.
+/// Failure of this function is often due to a bad camera format file.
+bool pmHDUGenerateForCell(pmCell *cell  ///< The cell for which to generate an HDU
+                         );
+
+/// Generate an HDU (with CELL.TRIMSEC, CELL.BIASSEC and pixels) for a cell with pixels
+///
+/// The write functions for the FPA hierarchy use pmHDUWrite, which assumes that the images in the readouts
+/// are subimages of the pixels in the HDU structure.  If this is not the case, the HDU pixels can be
+/// generated using some simple assumptions.  Splices the images and overscans together without regard for
+/// CELL.X0 and CELL.Y0 (for a proper mosaic, see pmFPAMosaic), though it should respect CELL.READDIR (so that
+/// the bias and trim sections match properly).  A warning may be generated after running this function if the
+/// bias and trim sections are specified in the camera format by default values rather than in the header.
+/// Failure of this function is often due to a bad camera format file.
+bool pmHDUGenerateForChip(pmChip *chip  ///< The chip for which to generate an HDU
+                         );
+
+// Generate an HDU (with CELL.TRIMSEC, CELL.BIASSEC and pixels) from an FPA with pixels
+///
+/// The write functions for the FPA hierarchy use pmHDUWrite, which assumes that the images in the readouts
+/// are subimages of the pixels in the HDU structure.  If this is not the case, the HDU pixels can be
+/// generated using some simple assumptions.  Splices the images and overscans together without regard for
+/// CELL.X0 and CELL.Y0 (for a proper mosaic, see pmFPAMosaic), though it should respect CELL.READDIR (so that
+/// the bias and trim sections match properly).  A warning may be generated after running this function if the
+/// bias and trim sections are specified in the camera format by default values rather than in the header.
+/// Failure of this function is often due to a bad camera format file.
+bool pmHDUGenerateForFPA(pmFPA *fpa     ///< The fpa for which to generate an HDU
+                        );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUUtils.c	(revision 22322)
@@ -0,0 +1,172 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+
+pmHDU *pmHDUGetFirst (const pmFPA *fpa) {
+
+    // XXX we probably should have an indicator in pmFPA about the depths.
+
+    if (!fpa) return NULL;
+    if (fpa->hdu) return fpa->hdu;
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+	pmChip *chip = fpa->chips->data[i];
+	if (!chip) continue;
+	if (chip->hdu) return chip->hdu;
+	if (!chip->cells) continue;
+	for (int j = 0; j < chip->cells->n; j++) {
+	    pmCell *cell = chip->cells->data[j];
+	    if (!cell) continue;
+	    if (cell->hdu) return cell->hdu;
+	}
+    }
+    return NULL;
+}
+
+pmHDU *pmHDUFromFPA(const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+    return fpa->hdu;
+}
+
+pmHDU *pmHDUFromChip(const pmChip *chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    pmHDU *hdu = chip->hdu;             // The HDU information
+    if (!hdu) {
+        hdu = pmHDUFromFPA(chip->parent); // Grab HDU info from the FPA
+    }
+
+    return hdu;
+}
+
+pmHDU *pmHDUFromCell(const pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    pmHDU *hdu = cell->hdu;             // The HDU information
+    if (!hdu) {
+        hdu = pmHDUFromChip(cell->parent); // Grab HDU info from the chip
+    }
+
+    return hdu;
+}
+
+pmHDU *pmHDUFromReadout(const pmReadout *readout)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, NULL);
+
+    pmCell *cell = readout->parent; // cell containing this readout;
+    pmHDU *hdu = pmHDUFromCell(cell);
+    return hdu;
+}
+
+// Get the lowest HDU
+pmHDU *pmHDUGetLowest(const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    pmHDU *hdu = NULL;          // The HDU that's at the lowest level
+    if (cell) {
+        hdu = pmHDUFromCell(cell);
+    } else if (chip) {
+        hdu = pmHDUFromChip(chip);
+    } else if (fpa) {
+        hdu = pmHDUFromFPA(fpa);
+    }
+
+    return hdu;
+}
+
+// Get the highest HDU
+pmHDU *pmHDUGetHighest(const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    pmHDU *hdu = NULL;          // The HDU that's at the highest level
+    if (fpa) {
+        hdu = pmHDUFromFPA(fpa);
+    }
+    if (!hdu && chip) {
+        hdu = pmHDUFromChip(chip);
+    }
+    if (!hdu && cell) {
+        hdu = pmHDUFromCell(cell);
+    }
+
+    return hdu;
+}
+
+// Print spaces to indent
+#define INDENT(FILE, LEVEL) \
+{ \
+    for (int i = 0; i < (LEVEL); i++) { \
+        fprintf(FILE, " "); \
+    } \
+}
+
+void pmHDUPrint(FILE *fd, const pmHDU *hdu, int level, bool header)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu,);
+
+    INDENT(fd, level);
+    if (hdu->blankPHU) {
+        fprintf(fd, "HDU: (PHU)\n");
+    } else {
+        fprintf(fd, "HDU: %s\n", hdu->extname);
+    }
+
+    INDENT(fd, level + 1);
+    fprintf(fd, "Format: %p\n", hdu->format);
+    if (header) {
+        INDENT(fd, level + 1);
+        if (hdu->header) {
+            fprintf(fd, "Header:\n");
+            psMetadataPrint(fd, hdu->header, level + 2);
+        } else {
+            fprintf(fd, "No header.\n");
+        }
+    }
+
+    INDENT(fd, level + 1);
+    if (hdu->images) {
+        fprintf(fd, "Images:\n");
+        for (long i = 0; i < hdu->images->n; i++) {
+            psImage *image = hdu->images->data[i]; // Image of interest
+            INDENT(fd, level + 2);
+            fprintf(fd, "%ld: %dx%d\n", i, image->numCols, image->numRows);
+        }
+    } else {
+        fprintf(fd, "NO images.\n");
+    }
+
+    INDENT(fd, level + 1);
+    if (hdu->masks) {
+        fprintf(fd, "Masks:\n");
+        for (long i = 0; i < hdu->masks->n; i++) {
+            psImage *mask = hdu->masks->data[i]; // Mask of interest
+            INDENT(fd, level + 2);
+            fprintf(fd, "%ld: %dx%d\n", i, mask->numCols, mask->numRows);
+        }
+    } else {
+        fprintf(fd, "NO masks.\n");
+    }
+
+    INDENT(fd, level + 1);
+    if (hdu->weights) {
+        fprintf(fd, "Weights:\n");
+        for (long i = 0; i < hdu->weights->n; i++) {
+            psImage *weight = hdu->weights->data[i]; // Weight image of interest
+            INDENT(fd, level + 2);
+            fprintf(fd, "%ld: %dx%d\n", i, weight->numCols, weight->numRows);
+        }
+    } else {
+        fprintf(fd, "NO weights.\n");
+    }
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUUtils.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUUtils.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmHDUUtils.h	(revision 22322)
@@ -0,0 +1,61 @@
+/* @file pmHDUUtils.h
+ * @brief Utility functions for working with an HDU
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-07-15 20:25:00 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_HDU_UTILS_H
+#define PM_HDU_UTILS_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Get the first HDU encountered in the hierarchy
+pmHDU *pmHDUGetFirst (const pmFPA *fpa);
+
+/// Get the lowest HDU in the hierarchy
+///
+/// The lowest HDU in the hierarchy will be the one with the actual pixels (if all levels are supplied).
+pmHDU *pmHDUGetLowest(const pmFPA *fpa, ///< The FPA
+                      const pmChip *chip, ///< The chip, or NULL
+                      const pmCell *cell ///< The cell, or NULL
+                     );
+
+/// Get the highest HDU in the hierarchy
+///
+/// The highest HDU in the hierarchy will be the PHU (might get NULL if not all levels are supplied)
+pmHDU *pmHDUGetHighest(const pmFPA *fpa, ///< The FPA
+                       const pmChip *chip, ///< The chip, or NULL
+                       const pmCell *cell ///< The cell, or NULL
+                      );
+
+/// Given an FPA, return the HDU (or NULL if all HDUs reside below the FPA)
+pmHDU *pmHDUFromFPA(const pmFPA *fpa    ///< FPA for which to find HDU
+                   );
+
+/// Given a chip, return the HDU (or NULL if it resides below the chip)
+pmHDU *pmHDUFromChip(const pmChip *chip ///< Chip for which to find HDU
+                    );
+
+/// Given a cell, return the HDU
+pmHDU *pmHDUFromCell(const pmCell *cell ///< Cell for which to find HDU
+                    );
+
+/// Given a readout, return the HDU
+pmHDU *pmHDUFromReadout(const pmReadout *readout ///< Readout for which to find HDU
+                       );
+
+/// Print details about an HDU
+///
+/// This is intended for testing or development use.
+void pmHDUPrint(FILE *fd,               ///< File descriptor to which to print
+                const pmHDU *hdu,       ///< HDU to print
+                int level,              ///< Level at which to print
+                bool header             ///< Print header?
+               );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutFake.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutFake.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutFake.c	(revision 22322)
@@ -0,0 +1,156 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmModelClass.h"
+#include "pmPeaks.h"
+#include "pmSource.h"
+#include "pmSourceUtils.h"
+#include "pmModelUtils.h"
+
+#include "pmReadoutFake.h"
+
+#define MODEL_TYPE "PS_MODEL_RGAUSS"    // Type of model to use
+#define MAX_AXIS_RATIO 20.0             // Maximum axis ratio for PSF model
+#define SOURCE_MASK (PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_CR_LIMIT) // Mask to apply to input sources
+
+
+// Given an object model, circularise it by setting the axes to be identical
+static bool circulariseModel(pmModel *model // Model to circularise
+    )
+{
+    assert(model);
+
+    psF32 *params = model->params->data.F32; // Model parameters
+    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO); // Ellipse axes
+    // Curiously, the minor axis can be larger than the major axis, so need to check.
+    if (axes.major >= axes.minor) {
+        axes.minor = axes.major;
+    } else {
+        axes.major = axes.minor;
+    }
+    return pmPSF_AxesToModel(params, axes);
+}
+
+bool pmReadoutFakeFromSources(pmReadout *readout, int numCols, int numRows, const psArray *sources,
+                              const psVector *xOffset, const psVector *yOffset, const pmPSF *psf,
+                              float minFlux, int radius, bool circularise)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_INT_LARGER_THAN(numCols, 0, false);
+    PS_ASSERT_INT_LARGER_THAN(numRows, 0, false);
+    PS_ASSERT_ARRAY_NON_NULL(sources, false);
+
+    if (xOffset || yOffset) {
+        PS_ASSERT_VECTOR_NON_NULL(xOffset, false);
+        PS_ASSERT_VECTOR_NON_NULL(yOffset, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(xOffset, yOffset, false);
+        PS_ASSERT_VECTOR_TYPE(xOffset, PS_TYPE_S32, false);
+        PS_ASSERT_VECTOR_TYPE_EQUAL(xOffset, yOffset, false);
+        if (xOffset->n != sources->n) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Number of offset vectors (%ld) and sources (%ld) doesn't match",
+                    xOffset->n, sources->n);
+            return false;
+        }
+    }
+    PS_ASSERT_PTR_NON_NULL(psf, false);
+    if (radius > 0 && isfinite(minFlux) && minFlux > 0.0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot define both minimum flux and fixed radius.");
+        return false;
+    }
+
+    readout->image = psImageRecycle(readout->image, numCols, numRows, PS_TYPE_F32);
+    psImageInit(readout->image, 0);
+
+    int numSources = sources->n;          // Number of stars
+
+    pmModel *fakeModel = pmModelFromPSFforXY(psf, (float)numCols / 2.0, (float)numRows / 2.0,
+                                             1.0); // Fake model, with central intensity of 1.0
+
+    float flux0 = fakeModel->modelFlux(fakeModel->params); // Flux for central intensity of 1.0
+
+    if (circularise && !circulariseModel(fakeModel)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
+        psFree(fakeModel);
+        return false;
+    }
+    psFree(fakeModel);
+
+    for (int i = 0; i < numSources; i++) {
+        pmSource *source = sources->data[i]; // Source of interest
+        if (source->mode & SOURCE_MASK) {
+            continue;
+        }
+        if (!isfinite(source->psfMag)) {
+            continue;
+        }
+        float x, y;                     // Coordinates of source
+        if (source->modelPSF) {
+            x = source->modelPSF->params->data.F32[PM_PAR_XPOS];
+            y = source->modelPSF->params->data.F32[PM_PAR_YPOS];
+        } else {
+            x = source->peak->xf;
+            y = source->peak->yf;
+        }
+
+        pmModel *fakeModel = pmModelFromPSFforXY(psf, x, y, powf(10.0, -0.4 * source->psfMag) / flux0);
+        if (!fakeModel) {
+            psErrorClear();
+            continue;
+        }
+        if (circularise && !circulariseModel(fakeModel)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
+            psFree(fakeModel);
+            return false;
+        }
+
+        psTrace("psModules.camera", 10, "Adding source at %f,%f with flux %f\n",
+                fakeModel->params->data.F32[PM_PAR_XPOS], fakeModel->params->data.F32[PM_PAR_YPOS],
+                fakeModel->params->data.F32[PM_PAR_I0]);
+
+        pmSource *fakeSource = pmSourceAlloc(); // Fake source to generate
+        fakeSource->peak = pmPeakAlloc(x, y, fakeModel->params->data.F32[PM_PAR_I0], PM_PEAK_LONE);
+        float fakeRadius = radius > 0 ? radius :
+            PS_MAX(1.0, fakeModel->modelRadius(fakeModel->params, minFlux)); // Radius of fake source
+
+        if (xOffset) {
+            if (!pmSourceDefinePixels(fakeSource, readout, x + xOffset->data.S32[i],
+                                      y + yOffset->data.S32[i], fakeRadius)) {
+                psErrorClear();
+                continue;
+            }
+            if (!pmModelAddWithOffset(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0,
+                                      - xOffset->data.S32[i], - yOffset->data.S32[i])) {
+                psErrorClear();
+                continue;
+            }
+        } else {
+            if (!pmSourceDefinePixels(fakeSource, readout, x, y, fakeRadius)) {
+                psErrorClear();
+                continue;
+            }
+            if (!pmModelAdd(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0)) {
+                psErrorClear();
+                continue;
+            }
+        }
+        psFree(fakeSource);
+        psFree(fakeModel);
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutFake.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutFake.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutFake.h	(revision 22322)
@@ -0,0 +1,26 @@
+#ifndef PM_READOUT_FAKE_H
+#define PM_READOUT_FAKE_H
+
+#include <pslib.h>
+#include <pmHDU.h>
+#include <pmFPA.h>
+
+#include <pmMoments.h>
+#include <pmResiduals.h>
+#include <pmGrowthCurve.h>
+#include <pmTrend2D.h>
+#include <pmPSF.h>
+
+/// Generate a fake readout from an array of sources
+bool pmReadoutFakeFromSources(pmReadout *readout, ///< Output readout, or NULL
+                              int numCols, int numRows, ///< Dimension of image
+                              const psArray *sources, ///< Array of pmSource
+                              const psVector *xOffset, ///< x offsets for sources (source -> img), or NULL
+                              const psVector *yOffset, ///< y offsets for sources (source -> img), or NULL
+                              const pmPSF *psf, ///< PSF for sources
+                              float minFlux, ///< Minimum flux to bother about; for setting source radius
+                              int radius, ///< Fixed radius for sources
+                              bool circularise ///< Circularise PSF model?
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutStack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutStack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutStack.c	(revision 22322)
@@ -0,0 +1,305 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmReadoutStack.h"
+
+// generate the specified image
+// XXX should it be an error for the image to exist?
+psImage *pmReadoutSetAnalysisImage(pmReadout *readout, // Readout containing image
+				   const char *name, // Name of image in analysis metadata
+				   int numCols, int numRows, // Expected size of image
+				   psElemType type, // Expected type of image
+				   double init // Initial value
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    psImage *image = psImageAlloc(numCols, numRows, type);
+
+    if (!psMetadataAddImage(readout->analysis, PS_LIST_TAIL, name, 0, "Analysis image from " __FILE__, image)) {
+	psAbort ("analysis image already exists");
+    }
+    psImageInit(image, init);
+
+    psFree (image); // we still have a view on readout->analysis
+    return image;
+}
+
+// retrieve the specified image
+// XXX not sure why this should call psMemIncrRefCounter
+psImage *pmReadoutGetAnalysisImage(pmReadout *readout, // Readout containing image
+				   const char *name       // Name of image in analysis metadata
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    bool mdok;                          // Status of MD lookup
+    psImage *image = psMetadataLookupPtr(&mdok, readout->analysis, name);
+    return image;
+}
+
+psImage *pmReadoutAnalysisImage(pmReadout *readout, // Readout containing image
+                                const char *name, // Name of image in analysis metadata
+                                int numCols, int numRows, // Expected size of image
+                                psElemType type, // Expected type of image
+                                double init // Initial value
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    bool mdok;                          // Status of MD lookup
+    psImage *image = psMetadataLookupPtr(&mdok, readout->analysis, name);
+    if (!image) {
+        image = psImageAlloc(numCols, numRows, type);
+        psMetadataAddImage(readout->analysis, PS_LIST_TAIL, name, 0, "Analysis image from " __FILE__, image);
+        psImageInit(image, init);
+        return image;
+    }
+    if (image->numCols != numCols || image->numRows != numRows) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Analysis image %s has incorrect size (%dx%d vs %dx%d)",
+                name, image->numCols, image->numRows, numCols, numRows);
+        return NULL;
+    }
+    if (image->type.type != type) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Analysis image %s has incorrect type (%x vs %x)",
+                name, image->type.type, type);
+        return NULL;
+    }
+    return psMemIncrRefCounter(image);
+}
+
+// XXX for the moment, use col0, row0, numCols, numRows supplied from the outside
+bool pmReadoutStackDefineOutput(pmReadout *readout, int col0, int row0, int numCols, int numRows, bool mask, bool weight, psMaskType blank)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+
+    // XXX is this an error?
+    if (readout->image) return false;
+    readout->col0 = col0;
+    readout->row0 = row0;
+
+    // allocate the images
+    readout->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImageInit(readout->image, NAN);
+
+    if (mask) {
+	// XXX is this an error?
+        if (readout->mask) return false;
+	readout->mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+	psImageInit(readout->mask, blank);
+    }
+
+    if (weight) {
+	// XXX is this an error?
+        if (readout->weight) return false;
+	readout->weight = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+	psImageInit(readout->weight, NAN);
+    }
+
+    return true;
+}
+
+bool pmReadoutStackSetOutputSize(int *col0, int *row0, int *numCols, int *numRows, const psArray *inputs)
+{
+    PS_ASSERT_ARRAY_NON_NULL(inputs, false);
+    PS_ASSERT_PTR_NON_NULL(col0, false);
+    PS_ASSERT_PTR_NON_NULL(row0, false);
+    PS_ASSERT_PTR_NON_NULL(numCols, false);
+    PS_ASSERT_PTR_NON_NULL(numRows, false);
+
+    // Step through each readout in the input image list to determine how big of an output
+    // image is needed to combine these input images.
+
+    int xMin = INT_MAX;
+    int yMin = INT_MAX;
+    int xMax = 0;
+    int yMax = 0;
+    int xSize = 0;
+    int ySize = 0;           // The size of the output image
+
+    bool valid = false;                 // Do we have a single valid input?
+    for (long i = 0; i < inputs->n; i++) {
+        pmReadout *readout = inputs->data[i]; // Readout of interest
+
+        if (!readout) continue;
+
+        // use the trimsec to define the max full range of the output pixels
+        pmCell *cell = readout->parent; // The parent cell
+        bool mdok = true;       // Status of MD lookup
+        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+        if (!mdok || !trimsec || psRegionIsNaN(*trimsec)) {
+            psWarning("CELL.TRIMSEC is not set for readout %ld --- ignored.\n", i);
+        } else {
+            xSize = PS_MAX(xSize, trimsec->x1 - trimsec->x0);
+            ySize = PS_MAX(ySize, trimsec->y1 - trimsec->y0);
+            xMin  = PS_MIN(xMin,  trimsec->x0);
+            xMax  = PS_MAX(xMax,  trimsec->x1);
+            yMin  = PS_MIN(yMin,  trimsec->y0);
+            yMax  = PS_MAX(yMax,  trimsec->y1);
+        }
+        valid = true;
+        psTrace("psModules.camera", 7, "Readout %ld: trimsec: %f,%f - %f,%f\n", i, trimsec->x0, trimsec->y0, trimsec->x1, trimsec->y1);
+    }
+
+    *col0 = xMin;
+    *row0 = yMin;
+    *numCols = xSize;
+    *numRows = ySize;
+
+    if (!valid) {
+        psError(PS_ERR_UNKNOWN, false, "No valid input readouts.");
+    }
+    return valid;
+}
+
+bool pmReadoutUpdateSize(pmReadout *readout, int minCols, int minRows,
+                         int numCols, int numRows, bool mask, bool weight,
+                         psMaskType blank)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+
+    if (readout->image) {
+        readout->col0 = PS_MIN(minCols, readout->col0);
+        readout->row0 = PS_MIN(minRows, readout->row0);
+    } else {
+        readout->col0 = minCols;
+        readout->row0 = minRows;
+    }
+
+    // (reAllocate the images
+    if (!readout->image) {
+        readout->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        psImageInit(readout->image, NAN);
+    }
+    if (readout->image->numCols < numCols || readout->image->numRows < numRows) {
+        // Generate the new output image by extending the current one, or making a whole new one
+        psImage *newImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        psImageInit(newImage, NAN);
+        psImageOverlaySection(newImage, readout->image, readout->col0, readout->row0, "=");
+        psFree(readout->image);
+        readout->image = newImage;
+    }
+
+    if (mask) {
+        if (!readout->mask) {
+            readout->mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+            psImageInit(readout->mask, blank);
+        }
+        if (readout->mask->numCols < numCols || readout->mask->numRows < numRows) {
+            psImage *newMask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+            psImageInit(newMask, blank);
+            psImageOverlaySection(newMask, readout->mask, readout->col0, readout->row0, "=");
+            psFree(readout->mask);
+            readout->mask = newMask;
+        }
+    }
+
+    if (weight) {
+        if (!readout->weight) {
+            readout->weight = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+            psImageInit(readout->weight, NAN);
+        }
+        if (readout->weight->numCols < numCols || readout->weight->numRows < numRows) {
+            psImage *newWeight = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+            psImageInit(newWeight, NAN);
+            psImageOverlaySection(newWeight, readout->weight, readout->col0, readout->row0, "=");
+            psFree(readout->weight);
+            readout->weight = newWeight;
+        }
+    }
+
+    return true;
+}
+
+bool pmReadoutStackValidate(int *minInputColsPtr, int *maxInputColsPtr, int *minInputRowsPtr,
+                            int *maxInputRowsPtr, int *numColsPtr, int *numRowsPtr,
+                            const psArray *inputs)
+{
+    PS_ASSERT_ARRAY_NON_NULL(inputs, false);
+    PS_ASSERT_PTR_NON_NULL(minInputColsPtr, false);
+    PS_ASSERT_PTR_NON_NULL(maxInputColsPtr, false);
+    PS_ASSERT_PTR_NON_NULL(minInputRowsPtr, false);
+    PS_ASSERT_PTR_NON_NULL(maxInputRowsPtr, false);
+    PS_ASSERT_PTR_NON_NULL(numColsPtr, false);
+    PS_ASSERT_PTR_NON_NULL(numRowsPtr, false);
+
+    // Step through each readout in the input image list to determine how big of an output image is needed to
+    // combine these input images.
+    int maxInputCols = 0;               // The largest input column value
+    int maxInputRows = 0;               // The largest input row value
+    int minInputCols = INT_MAX;         // The smallest input column value
+    int minInputRows = INT_MAX;         // The smallest input row value
+    int xSize = 0, ySize = 0;           // The size of the output image
+
+    int xMin = INT_MAX;
+    int yMin = INT_MAX;
+    int xMax = 0;
+    int yMax = 0;
+
+    bool valid = false;                 // Do we have a single valid input?
+    for (long i = 0; i < inputs->n; i++) {
+        pmReadout *readout = inputs->data[i]; // Readout of interest
+
+        if (!readout) {
+            continue;
+        }
+        if (!readout->image) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Input readout %ld has NULL image.\n", i);
+            return false;
+        }
+
+        // use the trimsec to define the max full range of the output pixels
+        pmCell *cell = readout->parent; // The parent cell
+        bool mdok = true;       // Status of MD lookup
+        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+        if (!mdok || !trimsec || psRegionIsNaN(*trimsec)) {
+            psWarning("CELL.TRIMSEC is not set for readout %ld --- ignored.\n", i);
+        } else {
+            xSize = PS_MAX(xSize, trimsec->x1 - trimsec->x0);
+            ySize = PS_MAX(ySize, trimsec->y1 - trimsec->y0);
+            xMin  = PS_MIN(xMin,  trimsec->x0);
+            xMax  = PS_MAX(xMax,  trimsec->x1);
+            yMin  = PS_MIN(yMin,  trimsec->y0);
+            yMax  = PS_MAX(yMax,  trimsec->y1);
+        }
+
+        valid = true;
+
+        // Range of pixels on output images
+        minInputCols = PS_MAX(xMin, PS_MIN(minInputCols, readout->col0));
+        maxInputCols = PS_MIN(xMax, PS_MAX(maxInputCols, readout->col0 + readout->image->numCols));
+        minInputRows = PS_MAX(yMin, PS_MIN(minInputRows, readout->row0));
+        maxInputRows = PS_MIN(yMax, PS_MAX(maxInputRows, readout->row0 + readout->image->numRows));
+
+        psTrace("psModules.camera", 7, "Readout %ld: offset %d,%d; size %dx%d\n", i, readout->col0, readout->row0, readout->image->numCols, readout->image->numRows);
+    }
+
+    if (minInputColsPtr) {
+        *minInputColsPtr = minInputCols;
+    }
+    if (maxInputColsPtr) {
+        *maxInputColsPtr = maxInputCols;
+    }
+    if (minInputRowsPtr) {
+        *minInputRowsPtr = minInputRows;
+    }
+    if (maxInputRowsPtr) {
+        *maxInputRowsPtr = maxInputRows;
+    }
+    if (numColsPtr) {
+        *numColsPtr = xSize;
+    }
+    if (numRowsPtr) {
+        *numRowsPtr = ySize;
+    }
+
+    return valid;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutStack.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutStack.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/camera/pmReadoutStack.h	(revision 22322)
@@ -0,0 +1,53 @@
+#ifndef PM_READOUT_STACK_H
+#define PM_READOUT_STACK_H
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#define PM_READOUT_STACK_ANALYSIS_COUNT "STACK.COUNT" // Name for count image in analysis metadata
+#define PM_READOUT_STACK_ANALYSIS_SIGMA "STACK.SIGMA" // Name for sigma image in analysis metadata
+
+/// Update an output readout (for a stack) with the correct col0,row0 and the image size
+bool pmReadoutUpdateSize(pmReadout *readout, ///< Readout which to update
+                         int minCols, int minRows, ///< Minimum coordinates
+                         int numCols, int numRows, ///< Size of images
+                         bool mask,     ///< Worry about the mask?
+                         bool weight,   ///< Worry about the weight?
+                         psMaskType blank ///< Mask value to give to blank pixels
+    );
+
+/// Determine how large an output image is needed to combine the input readouts
+bool pmReadoutStackValidate(int *minInputColsPtr, int *maxInputColsPtr, ///< Min and max size in x
+                            int *minInputRowsPtr, int *maxInputRowsPtr, ///< Min and max size in y
+                            int *numColsPtr, int *numRowsPtr, ///< Size of image
+                            const psArray *inputs ///< Array of pmReadouts
+    );
+
+psImage *pmReadoutSetAnalysisImage(pmReadout *readout, // Readout containing image
+				   const char *name, // Name of image in analysis metadata
+				   int numCols, int numRows, // Expected size of image
+				   psElemType type, // Expected type of image
+				   double init // Initial value
+    );
+
+// retrieve the specified image
+// XXX not sure why this should call psMemIncrRefCounter
+psImage *pmReadoutGetAnalysisImage(pmReadout *readout, // Readout containing image
+				   const char *name       // Name of image in analysis metadata
+    );
+
+
+/// Return an image from analysis metadata, produced while stacking
+psImage *pmReadoutAnalysisImage(pmReadout *readout, // Readout containing image
+                                const char *name, // Name of image in analysis metadata
+                                int numCols, int numRows, // Expected size of image
+                                psElemType type, // Expected type of image
+                                double init // Initial value
+    );
+
+// XXX for the moment, use col0, row0, numCols, numRows supplied from the outside
+bool pmReadoutStackDefineOutput(pmReadout *readout, int col0, int row0, int numCols, int numRows, bool mask, bool weight, psMaskType blank);
+
+bool pmReadoutStackSetOutputSize(int *col0, int *row0, int *numCols, int *numRows, const psArray *inputs);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/Makefile.am	(revision 22322)
@@ -0,0 +1,23 @@
+noinst_LTLIBRARIES = libpsmodulesconcepts.la
+
+libpsmodulesconcepts_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS)
+libpsmodulesconcepts_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulesconcepts_la_SOURCES  = \
+	pmConcepts.c \
+	pmConceptsAverage.c \
+	pmConceptsRead.c \
+	pmConceptsWrite.c \
+	pmConceptsStandard.c \
+	pmConceptsPhotcode.c \
+	pmConceptsUpdate.c
+
+pkginclude_HEADERS = \
+	pmConcepts.h \
+	pmConceptsAverage.h \
+	pmConceptsRead.h \
+	pmConceptsWrite.h \
+	pmConceptsStandard.h \
+	pmConceptsPhotcode.h \
+	pmConceptsUpdate.h
+
+CLEANFILES = *~
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConcepts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConcepts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConcepts.c	(revision 22322)
@@ -0,0 +1,953 @@
+// XXX *REALLY* need generic "concept update" and "concept read" functions that handles the type transparently
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <pslib.h>
+#include <string.h>
+
+#include "pmConfig.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmHDUUtils.h"
+#include "pmConcepts.h"
+#include "pmConceptsRead.h"
+#include "pmConceptsWrite.h"
+#include "pmConceptsStandard.h"
+#include "pmConceptsUpdate.h"
+
+static bool conceptsInitialised = false;// Have concepts been read?
+static psMetadata *conceptsFPA = NULL;  // Known concepts for FPA
+static psMetadata *conceptsChip = NULL; // Known concepts for chip
+static psMetadata *conceptsCell = NULL; // Known concepts for cell
+
+// Return the appropriate concepts metadata, given the level
+static psMetadata *conceptsFromLevel(pmFPALevel level)
+{
+    switch (level) {
+    case PM_FPA_LEVEL_FPA:
+        return conceptsFPA;
+    case PM_FPA_LEVEL_CHIP:
+        return conceptsChip;
+    case PM_FPA_LEVEL_CELL:
+        return conceptsCell;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Invalid concept level provided: %d\n", level);
+        return NULL;
+    }
+}
+
+// Free a concept
+static void conceptSpecFree(pmConceptSpec *spec)
+{
+    psFree(spec->blank);
+}
+
+pmConceptSpec *pmConceptSpecAlloc(psMetadataItem *blank, pmConceptParseFunc parse,
+                                  pmConceptFormatFunc format, bool required)
+{
+    pmConceptSpec *spec = psAlloc(sizeof(pmConceptSpec));
+    psMemSetDeallocator(spec, (psFreeFunc)conceptSpecFree);
+
+    spec->blank = psMemIncrRefCounter(blank);
+    spec->parse = parse;
+    spec->format = format;
+    spec->required = required;
+
+    return spec;
+}
+
+psList *pmConceptsList(pmFPALevel level)
+{
+    if (!conceptsInitialised) {
+        pmConceptsInit();
+    }
+
+    // Get the appropriate concepts
+    psMetadata *concepts = conceptsFromLevel(level); // Metadata of concepts specs
+    if (!concepts) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Invalid concept level provided: %d\n", level);
+        return NULL;
+    }
+
+    // Pull out the names
+    psList *list = psListAlloc(NULL);   // List of concepts' names
+    psMetadataIterator *iter = psMetadataIteratorAlloc(concepts, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        psListAdd(list, PS_LIST_TAIL, item->name);
+    }
+    psFree(iter);
+    return list;
+}
+
+bool pmConceptGetRequired(const char *name, pmFPALevel level)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+    if (!conceptsInitialised) {
+        pmConceptsInit();
+    }
+
+    psMetadata *concepts = conceptsFromLevel(level); // The metadata of known concepts
+
+    bool mdok;                          // Status of MD lookup
+    pmConceptSpec *spec = psMetadataLookupPtr(&mdok, concepts, name); // The specification
+    if (!spec) {
+        // Won't throw an error, because we can't distinguish an error from the desired result.
+        // However, that doesn't really matter, because if we can't find it, then it can't be required!
+        return false;
+    }
+
+    return spec->required;
+}
+
+bool pmConceptSetRequired(const char *name, pmFPALevel level, bool required)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    if (!conceptsInitialised) {
+        pmConceptsInit();
+    }
+
+    psMetadata *concepts = conceptsFromLevel(level); // The metadata of known concepts
+
+    bool mdok;                          // Status of MD lookup
+    pmConceptSpec *spec = psMetadataLookupPtr(&mdok, concepts, name); // The specification
+    if (!spec) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find defined concept %s in level %d.",
+                name, level);
+        return false;
+    }
+    spec->required = required;
+
+    return true;
+}
+
+bool pmConceptRegister(psMetadataItem *blank, pmConceptParseFunc parse,
+                        pmConceptFormatFunc format, bool required, pmFPALevel level)
+{
+    PS_ASSERT_PTR_NON_NULL(blank, false);
+
+    if (!conceptsInitialised) {
+        pmConceptsInit();
+    }
+
+    psMetadata *target = conceptsFromLevel(level); // The metadata of known concepts to write to
+    if (!target) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to register concept at invalid concept level.");
+        return false;
+    }
+
+    pmConceptSpec *spec = pmConceptSpecAlloc(blank, parse, format, required); // The concept specification
+    psMetadataAdd(target, PS_LIST_TAIL, blank->name, PS_DATA_UNKNOWN | PS_META_REPLACE,
+                  "Concepts specification", spec);
+    psFree(spec);                       // Drop reference
+
+    return true;
+}
+
+
+// Set all registered concepts to blank value for the specified level
+static bool conceptsBlank(psMetadata **specs, // One of the concepts specifications
+                          psMetadata *target // Place to install the concepts
+                         )
+{
+    assert(specs);
+    assert(target);
+
+    if (!conceptsInitialised) {
+        pmConceptsInit();
+    }
+    psMetadataIterator *specsIter = psMetadataIteratorAlloc(*specs, PS_LIST_HEAD, NULL); // Iterator on specs
+    psMetadataItem *specItem = NULL;    // Item from the specs metadata
+    while ((specItem = psMetadataGetAndIncrement(specsIter))) {
+        psTrace("psModules.concepts", 9, "Blanking %s...\n", specItem->name);
+        pmConceptSpec *spec = specItem->data.V; // The specification
+        psMetadataItem *blank = spec->blank; // The concept
+        psMetadataItem *copy = NULL;    // Copy of the blank concept
+        // Trap the lists, which can't be copied in the ordinary way without a warning
+        if (blank->type == PS_DATA_LIST) {
+            copy = psMetadataItemAllocPtr(blank->name, PS_DATA_LIST, blank->comment, blank->data.V);
+        } else {
+            copy = psMetadataItemCopy(blank);
+        }
+        if (!psMetadataAddItem(target, copy, PS_LIST_TAIL, PS_META_REPLACE)) {
+            psLogMsg(__func__, PS_LOG_WARN, "Unable to add blank version of concept %s\n", blank->name);
+        }
+        psFree(copy);                   // Drop reference
+    }
+    psFree(specsIter);
+
+    return true;
+}
+
+
+
+// Read all registered concepts for the specified level
+static bool conceptsRead(psMetadata **specs, // One of the concepts specifications
+                         pmFPA *fpa,    // The FPA
+                         pmChip *chip,  // The chip
+                         pmCell *cell, // The cell
+                         unsigned int *read,     // What's already been read
+                         pmConceptSource source, // The source of the concepts to read
+                         pmConfig *config, // Configuration
+                         psMetadata *target // Place into which to read the concepts
+                        )
+{
+    assert(specs);
+    assert(read);
+    assert(target);
+
+    if (!conceptsInitialised) {
+        pmConceptsInit();
+    }
+
+    // At least one HDU is required for the reading functions
+    pmHDU *hduLow = pmHDUGetLowest(fpa, chip, cell); // Lowest HDU.
+    if (!hduLow) {
+        // Can't do anything --- don't record any success, but don't return an error either
+        return true;
+    }
+    pmHDU *hduHigh = pmHDUGetHighest(fpa, chip, cell); // Highest HDU
+
+    if (cell && (cell->conceptsRead == PM_CONCEPT_SOURCE_NONE)) {
+        pmConceptsBlankCell(cell);
+        cell->conceptsRead = PM_CONCEPT_SOURCE_BLANK;
+    }
+    if (chip && (chip->conceptsRead == PM_CONCEPT_SOURCE_NONE)) {
+        pmConceptsBlankChip(chip);
+        chip->conceptsRead = PM_CONCEPT_SOURCE_BLANK;
+    }
+    if (fpa && (fpa->conceptsRead == PM_CONCEPT_SOURCE_NONE)) {
+        pmConceptsBlankFPA(fpa);
+        fpa->conceptsRead = PM_CONCEPT_SOURCE_BLANK;
+    }
+
+    bool success = true;                // Success in reading concepts?
+    if (source & PM_CONCEPT_SOURCE_CELLS && !(*read & PM_CONCEPT_SOURCE_CELLS) && cell) {
+        if (p_pmConceptsReadFromCells(target, *specs, cell)) {
+            *read |= PM_CONCEPT_SOURCE_CELLS;
+        } else {
+            psError(PS_ERR_UNKNOWN, false, "Error reading concepts from camera configuration.\n");
+            success = false;
+        }
+    }
+
+    if (source & PM_CONCEPT_SOURCE_DEFAULTS && !(*read & PM_CONCEPT_SOURCE_DEFAULTS)) {
+        if (p_pmConceptsReadFromDefaults(target, *specs, fpa, chip, cell)) {
+            *read |= PM_CONCEPT_SOURCE_DEFAULTS;
+        } else {
+            psError(PS_ERR_UNKNOWN, false, "Error reading concepts from defaults.\n");
+            success = false;
+        }
+    }
+
+    if (source & PM_CONCEPT_SOURCE_PHU && !(*read & PM_CONCEPT_SOURCE_PHU) && hduHigh->header) {
+        if (p_pmConceptsReadFromHeader(target, *specs, fpa, chip, cell)) {
+            *read |= PM_CONCEPT_SOURCE_PHU;
+        } else {
+            psError(PS_ERR_UNKNOWN, false, "Error reading concepts from PHU.\n");
+            success = false;
+        }
+    }
+
+    // If there are multiple HDUs, then it may be that one of them hasn't been read yet (hdu->header not set)
+    if (source & PM_CONCEPT_SOURCE_HEADER && !(*read & PM_CONCEPT_SOURCE_HEADER) &&
+        hduLow != hduHigh && hduLow->header) {
+        if (p_pmConceptsReadFromHeader(target, *specs, fpa, chip, cell)) {
+            *read |= PM_CONCEPT_SOURCE_HEADER;
+        } else {
+            psError(PS_ERR_UNKNOWN, false, "Error reading concepts from header.\n");
+            success = false;
+        }
+    }
+
+    #ifdef HAVE_PSDB
+    if (source & PM_CONCEPT_SOURCE_DATABASE && !(*read & PM_CONCEPT_SOURCE_DATABASE)) {
+        if (p_pmConceptsReadFromDatabase(target, *specs, fpa, chip, cell, config)) {
+            *read |= PM_CONCEPT_SOURCE_DATABASE;
+        } else {
+            psError(PS_ERR_UNKNOWN, false, "Error reading concepts from database.\n");
+            success = false;
+        }
+    }
+    #endif
+
+    pmConceptsUpdate(fpa, chip, cell);
+
+    return success;
+}
+
+// Write all registered concepts for the specified level
+static bool conceptsWrite(psMetadata **specs, // One of the concepts specifications
+                          const pmFPA *fpa,   // The FPA
+                          const pmChip *chip, // The chip
+                          const pmCell *cell, // The cell
+                          pmConceptSource source, // The source of the concepts to write
+                          pmConfig *config, // Configuration
+                          const psMetadata *concepts // The concepts to write out
+                         )
+{
+    assert(specs);
+    assert(concepts);
+
+    if (!conceptsInitialised) {
+        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);
+    }
+
+    return true;
+}
+
+
+bool pmConceptsRead(pmFPA *fpa, pmChip *chip, pmCell *cell, pmConceptSource source, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    bool success = conceptsRead(&conceptsFPA, fpa, chip, cell, &fpa->conceptsRead, source,
+                                config, fpa->concepts);
+    if (chip) {
+        success &= conceptsRead(&conceptsChip, fpa, chip, cell, &chip->conceptsRead, source,
+                                config, chip->concepts);
+    }
+    if (cell) {
+        success &= conceptsRead(&conceptsCell, fpa, chip, cell, &cell->conceptsRead, source,
+                                config, cell->concepts);
+    }
+
+    return success;
+}
+
+
+bool pmConceptsBlankFPA(pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    psTrace("psModules.concepts", 5, "Blanking FPA concepts: %p %p\n", conceptsFPA, fpa->concepts);
+    return conceptsBlank(&conceptsFPA, fpa->concepts);
+}
+
+
+bool pmConceptsReadFPA(pmFPA *fpa, pmConceptSource source, bool propagateDown, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    psTrace("psModules.concepts", 5, "Reading FPA concepts: %p %p\n", conceptsFPA, fpa->concepts);
+    bool success = conceptsRead(&conceptsFPA, fpa, NULL, NULL, &fpa->conceptsRead, source,
+                                config, fpa->concepts);
+    if (propagateDown) {
+        psArray *chips = fpa->chips;    // Array of chips
+        for (long i = 0; i < chips->n; i++) {
+            pmChip *chip = chips->data[i]; // Chip of interest
+            if (chip) {
+                success &= pmConceptsReadChip(chip, source, false, true, config);
+            }
+        }
+    }
+
+    return success;
+}
+
+bool pmConceptsWriteFPA(const pmFPA *fpa, pmConceptSource source, bool propagateDown, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    psTrace("psModules.concepts", 5, "Writing FPA concepts: %p %p\n", conceptsFPA, fpa->concepts);
+    bool success = conceptsWrite(&conceptsFPA, fpa, NULL, NULL, source, config, fpa->concepts);
+    if (propagateDown) {
+        psArray *chips = fpa->chips;        // Array of chips
+        for (long i = 0; i < chips->n; i++) {
+            pmChip *chip = chips->data[i];  // Chip of interest
+            if (chip && !chip->hdu) {
+                success &= pmConceptsWriteChip(chip, source, false, true, config);
+            }
+        }
+    }
+    return success;
+}
+
+bool pmConceptsBlankChip(pmChip *chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    psTrace("psModules.concepts", 5, "Blanking chip concepts: %p %p\n", conceptsChip, chip->concepts);
+    return conceptsBlank(&conceptsChip, chip->concepts);
+}
+
+bool pmConceptsReadChip(pmChip *chip, pmConceptSource source, bool propagateUp,
+                        bool propagateDown, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    psTrace("psModules.concepts", 5, "Reading chip concepts: %p %p\n", conceptsChip, chip->concepts);
+    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
+    bool success = conceptsRead(&conceptsChip, fpa, chip, NULL, &chip->conceptsRead, source, config,
+                                chip->concepts);
+    if (propagateUp) {
+        success &= conceptsRead(&conceptsFPA, fpa, chip, NULL, &fpa->conceptsRead, source,
+                                config, fpa->concepts);
+    }
+    if (propagateDown) {
+        psArray *cells = chip->cells;        // Array of cells
+        for (long i = 0; i < cells->n; i++) {
+            pmCell *cell = cells->data[i];  // Cell of interest
+            if (cell) {
+                success &= pmConceptsReadCell(cell, source, false, config);
+            }
+        }
+    }
+    return success;
+}
+
+bool pmConceptsWriteChip(const pmChip *chip, pmConceptSource source, bool propagateUp,
+                         bool propagateDown, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    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);
+    if (propagateUp && !fpa->hdu) {
+        success &= conceptsWrite(&conceptsFPA, fpa, chip, NULL, source, config, fpa->concepts);
+    }
+    if (propagateDown) {
+        psArray *cells = chip->cells;        // Array of cells
+        for (long i = 0; i < cells->n; i++) {
+            pmCell *cell = cells->data[i];  // Cell of interest
+            if (cell && !cell->hdu) {
+                success &= pmConceptsWriteCell(cell, source, false, config);
+            }
+        }
+    }
+    return success;
+}
+
+bool pmConceptsBlankCell(pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    psTrace("psModules.concepts", 5, "Blanking cell concepts: %p %p\n", conceptsCell, cell->concepts);
+    return conceptsBlank(&conceptsCell, cell->concepts);
+}
+
+bool pmConceptsReadCell(pmCell *cell, pmConceptSource source, bool propagateUp, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    psTrace("psModules.concepts", 5, "Reading cell concepts: %p %p\n", conceptsCell, cell->concepts);
+    pmChip *chip = cell->parent;        // Chip to which the cell belongs
+    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
+
+    bool success = conceptsRead(&conceptsCell, fpa, chip, cell, &cell->conceptsRead, source, config,
+                                cell->concepts);
+    if (propagateUp) {
+        success &= conceptsRead(&conceptsChip, fpa, chip, cell, &chip->conceptsRead, source, config,
+                                chip->concepts);
+        success &= conceptsRead(&conceptsFPA, fpa, chip, cell, &fpa->conceptsRead, source, config,
+                                fpa->concepts);
+    }
+
+    return success;
+}
+
+bool pmConceptsWriteCell(const pmCell *cell, pmConceptSource source, bool propagateUp, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    psTrace("psModules.concepts", 5, "Writing cell concepts: %p %p\n", conceptsCell, cell->concepts);
+    pmChip *chip = cell->parent;        // Chip to which the cell belongs
+    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
+
+    bool success = conceptsWrite(&conceptsCell, fpa, chip, cell, source, config, cell->concepts);
+    if (propagateUp) {
+        if (!chip->hdu) {
+            success &= conceptsWrite(&conceptsChip, fpa, chip, cell, source, config, chip->concepts);
+            if (!fpa->hdu) {
+                success &= conceptsWrite(&conceptsFPA, fpa, chip, cell, source, config, fpa->concepts);
+            }
+        }
+    }
+
+    return success;
+}
+
+// Register a concept
+#define CONCEPT_REGISTER_FUNCTION(TYPENAME, SUFFIX, DEFAULT) \
+static void conceptRegister##SUFFIX(const char *name, /* Name of concept */ \
+                                    const char *comment, /* Comment for concept */ \
+                                    pmConceptParseFunc parse, /* Parsing function */ \
+                                    pmConceptFormatFunc format, /* Formatting function */ \
+                                    bool required, /* Required concept? */ \
+                                    pmFPALevel level /* Level at which concept applies */ \
+    ) \
+{ \
+    psMetadataItem *item = psMetadataItemAlloc##TYPENAME(name, comment, DEFAULT); /* Item to add */ \
+    pmConceptRegister(item, parse, format, required, level); \
+    psFree(item); \
+    return; \
+}
+
+CONCEPT_REGISTER_FUNCTION(Str, Str, "");
+CONCEPT_REGISTER_FUNCTION(F32, F32, NAN);
+CONCEPT_REGISTER_FUNCTION(F64, F64, NAN);
+CONCEPT_REGISTER_FUNCTION(S32, Enum, -1); // For enums: set default to -1
+CONCEPT_REGISTER_FUNCTION(S32, S32, 0); // For values: set default to 0
+
+static void conceptRegisterTime(const char *name, /* Name of concept */ \
+                                const char *comment, /* Comment for concept */ \
+                                bool required, /* Required concept? */ \
+                                pmFPALevel level /* Level at which concept applies */ \
+    )
+{
+    psTime *time = psTimeAlloc(PS_TIME_TAI); // Blank time
+    // Not particularly distinguishing, but should be good enough
+    time->sec = 0;
+    time->nsec = 0;
+    psMetadataItem *item = psMetadataItemAlloc(name, PS_DATA_TIME, comment, time);
+    psFree(time);
+    pmConceptRegister(item, p_pmConceptParse_TIME, p_pmConceptFormat_TIME, required, level);
+    psFree(item);
+}
+
+bool pmConceptsInit(void)
+{
+    conceptsInitialised = true;
+
+    p_psMemAllocatePersistent(true);
+
+    bool init = false;                  // Did we initialise anything?
+
+    if (! conceptsFPA) {
+        conceptsFPA = psMetadataAlloc();
+        init = true;
+
+        // Install the standard concepts
+        conceptRegisterStr("FPA.TELESCOPE", "Telescope of origin", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.INSTRUMENT", "Instrument name (according to the instrument)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.DETECTOR", "Detector name", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.COMMENT", "Observation comment", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.FOCUS", "Telescope focus", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.AIRMASS", "Airmass at boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.FILTERID", "Filter used (parsed, abstract name)", p_pmConceptParse_FPA_FILTER, p_pmConceptFormat_FPA_FILTER, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.FILTER", "Filter used (instrument name)", false, NULL, NULL, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.POSANGLE", "Position angle of instrument", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.RADECSYS", "Celestial coordinate system", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF64("FPA.RA", "Right Ascension of boresight", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF64("FPA.DEC", "Declination of boresight", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF64("FPA.LONGITUDE", "West longitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF64("FPA.LATITUDE", "Latitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.ELEVATION", "Elevation of observatory (metres)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.OBSTYPE", "Type of observation", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.OBJECT", "Object of observation", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF64("FPA.ALT", "Altitude of boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF64("FPA.AZ", "Azimuth of boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterEnum("FPA.TIMESYS", "Time system", p_pmConceptParse_TIMESYS, p_pmConceptFormat_TIMESYS, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterTime("FPA.TIME", "Time of exposure", false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TEMP", "Temperature of focal plane", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M1X", "Primary Mirror X Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M1Y", "Primary Mirror Y Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M1Z", "Primary Mirror Z Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M1TIP", "Primary Mirror TIP", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M1TILT", "Primary Mirror TILT", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M2X", "Primary Mirror X Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M2Y", "Primary Mirror Y Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M2Z", "Primary Mirror Z Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M2TIP", "Primary Mirror TIP", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M2TILT", "Primary Mirror TILT", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.ENV.TEMP", "Environment: Temperature", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.ENV.HUMID", "Environment: Humidity", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.ENV.WIND", "Environment: Wind speed", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.ENV.DIR", "Environment: Wind direction", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.M1", "Telescope Temperatures: M1", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.M1CELL", "Telescope Temperatures: M1 cell", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.M2", "Telescope Temperatures: M2", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.SPIDER", "Telescope Temperatures: spider", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.TRUSS", "Telescope Temperatures: truss", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.EXTRA", "Telescope Temperatures: extra", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.PON.TIME", "Power On Time", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.EXPOSURE", "Exposure time (sec)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+    }
+    if (! conceptsChip) {
+        conceptsChip = psMetadataAlloc();
+        init = true;
+
+        // Install the standard concepts
+        conceptRegisterS32("CHIP.XPARITY", "Orientation in x compared to the rest of the FPA", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterS32("CHIP.YPARITY", "Orientation in y compared to the rest of the FPA", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterS32("CHIP.X0", "Position of (0,0) on the FPA",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterS32("CHIP.Y0", "Position of (0,0) on the FPA",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterS32("CHIP.XSIZE", "Size of chip (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterS32("CHIP.YSIZE", "Size of chip (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterF32("CHIP.TEMP", "Temperature of chip", NULL, NULL, false, PM_FPA_LEVEL_CHIP);
+        conceptRegisterStr("CHIP.ID", "Chip identifier", NULL, NULL, false, PM_FPA_LEVEL_CHIP);
+    }
+
+    if (! conceptsCell) {
+        conceptsCell = psMetadataAlloc();
+        init = true;
+
+        // Install the standard concepts
+        conceptRegisterF32("CELL.GAIN", "CCD gain (e/count)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterF32("CELL.READNOISE", "CCD read noise (e)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterF32("CELL.SATURATION", "Saturation level (counts)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterF32("CELL.BAD", "Bad level (counts)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.XPARITY", "Orientation in x compared to the rest of the chip", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.YPARITY", "Orientation in y compared to the rest of the chip", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.READDIR", "Read direction, rows=1, cols=2", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+
+        // These (CELL.EXPOSURE and CELL.DARKTIME) used to be READOUT.EXPOSURE and READOUT.DARKTIME, but that
+        // doesn't really make sense at the moment.  Maybe we need to add a "parent" link to the readouts.
+        // But then how are the exposure times REALLY derived?  They're not in the FITS headers, because a
+        // readout is a plane in a 3D image.  We'll have to dream up some additional suffix to specify these,
+        // but for now....
+        conceptRegisterF32("CELL.EXPOSURE", "Exposure time (sec)", NULL, NULL, false, PM_FPA_LEVEL_CELL);
+        conceptRegisterF32("CELL.DARKTIME", "Time since flush (sec)", NULL, NULL, false, PM_FPA_LEVEL_CELL);
+
+        conceptRegisterS32("CELL.XBIN", "Binning in x", p_pmConceptParse_CELL_Binning,p_pmConceptFormat_CELL_XBIN, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.YBIN", "Binning in y",p_pmConceptParse_CELL_Binning,p_pmConceptFormat_CELL_YBIN, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterEnum("CELL.TIMESYS", "Time system", p_pmConceptParse_TIMESYS,p_pmConceptFormat_TIMESYS, false, PM_FPA_LEVEL_CELL);
+        conceptRegisterTime("CELL.TIME", "Time of exposure", false, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.X0", "Position of (0,0) on the chip",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.Y0", "Position of (0,0) on the chip",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.XSIZE", "Size of cell (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.YSIZE", "Size of cell (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.XWINDOW", "Start of cell window (pixels)",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.YWINDOW", "Start of cell window (pixels)",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterF32("CELL.VARFACTOR", "Variance factor for conversion from large to small scales", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+
+        // CELL.TRIMSEC
+        {
+            psRegion *trimsec = psAlloc(sizeof(psRegion)); // Blank trimsec
+            trimsec->x0 = trimsec->y0 = trimsec->x1 = trimsec->y1 = NAN;
+            psMetadataItem *cellTrimsec = psMetadataItemAllocPtr("CELL.TRIMSEC", PS_DATA_REGION,
+                                          "Trim section", trimsec);
+            psFree(trimsec);
+            pmConceptRegister(cellTrimsec, p_pmConceptParse_CELL_TRIMSEC,p_pmConceptFormat_CELL_TRIMSEC, true, PM_FPA_LEVEL_CELL);
+            psFree(cellTrimsec);
+        }
+
+        // CELL.BIASSEC
+        {
+            psList *biassecs = psListAlloc(NULL); // Blank biassecs
+            psMetadataItem *cellBiassec = psMetadataItemAllocPtr("CELL.BIASSEC", PS_DATA_LIST,
+                                          "Bias sections", biassecs);
+            psFree(biassecs);
+            pmConceptRegister(cellBiassec, p_pmConceptParse_CELL_BIASSEC, p_pmConceptFormat_CELL_BIASSEC, true, PM_FPA_LEVEL_CELL);
+            psFree(cellBiassec);
+        }
+
+    }
+
+    p_psMemAllocatePersistent(false);
+
+    return init;
+}
+
+void pmConceptsDone(void)
+{
+    psFree(conceptsFPA);
+    conceptsFPA = NULL;
+    psFree(conceptsChip);
+    conceptsChip = NULL;
+    psFree(conceptsCell);
+    conceptsCell = NULL;
+
+    conceptsInitialised = false;
+}
+
+
+// List of concepts not to copy, for each level.
+// Must be NULL-terminated
+static const char *dontCopyConceptsFPA[] = { "FPA.OBS", "FPA.NAME", "FPA.CAMERA", 0 };
+static const char *dontCopyConceptsChip[] = { "CHIP.NAME", 0 };
+static const char *dontCopyConceptsCell[] = { "CELL.NAME", "CELL.TRIMSEC", "CELL.BIASSEC", 0 };
+
+// Copy concepts from a source container to a target container, avoiding certain entries
+static void copyConcepts(psMetadata *target, // Target metadata container
+                         psMetadata *source, // Source metadata container
+                         const char *dontCopyConcepts[] // Don't copy these concepts
+                         )
+{
+    assert(target);
+    assert(source);
+    assert(dontCopyConcepts);
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(source, PS_LIST_HEAD, NULL);
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        const char *name = item->name;  // Name of concept
+        bool copyOK = true;            // OK to copy
+        for (int i = 0; dontCopyConcepts[i] && copyOK; i++) {
+            if (!strcmp(name, dontCopyConcepts[i])) {
+                copyOK = false;
+            }
+        }
+        if (!copyOK) {
+            continue;
+        }
+        psMetadataItem *new = psMetadataItemCopy(item); // New item, a copy of the old
+        psMetadataAddItem(target, new, PS_LIST_TAIL, PS_META_REPLACE);
+        psFree(new);                    // Drop reference
+    }
+    psFree(iter);
+
+    return;
+}
+
+
+bool pmFPACopyConcepts(pmFPA *target, const pmFPA *source)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    // Copy FPA concepts
+    copyConcepts(target->concepts, source->concepts, dontCopyConceptsFPA);
+
+    // Copy chip concepts
+    psArray *targetChips = target->chips; // Chips in target
+    psArray *sourceChips = source->chips; // Chips in source
+    if (targetChips->n != sourceChips->n) {
+        psError(PS_ERR_IO, true, "Number of chips in target (%ld) and source (%ld) differ --- unable to copy "
+                "concepts.\n", targetChips->n, sourceChips->n);
+        return false;
+    }
+    for (int i = 0; i < targetChips->n; i++) {
+        pmChip *targetChip = targetChips->data[i]; // Target chip of interest
+        pmChip *sourceChip = sourceChips->data[i]; // Source chip of interest
+        if (! targetChip || ! sourceChip) {
+            continue;
+        }
+
+        copyConcepts(targetChip->concepts, sourceChip->concepts, dontCopyConceptsChip);
+
+        // Copy cell concepts
+        psArray *targetCells = targetChip->cells; // Cells in target
+        psArray *sourceCells = sourceChip->cells; // Cells in source
+        if (targetCells->n != sourceCells->n) {
+            psError(PS_ERR_IO, true, "Number of cells in target (%ld) and source (%ld) differ for chip %d ---"
+                    " unable to copy concepts.\n", targetCells->n, sourceCells->n, i);
+            return false;
+        }
+        for (int j = 0; j < targetCells->n; j++) {
+            pmCell *targetCell = targetCells->data[j]; // Target chip of interest
+            pmCell *sourceCell = sourceCells->data[j]; // Source chip of interest
+            if (! targetCell || ! sourceCell) {
+                continue;
+            }
+
+            copyConcepts(targetCell->concepts, sourceCell->concepts, dontCopyConceptsCell);
+        }
+    }
+
+    return true;
+}
+
+
+bool pmConceptsCopyFPA(pmFPA *target, const pmFPA *source, bool chips, bool cells)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    // Copy FPA concepts
+    copyConcepts(target->concepts, source->concepts, dontCopyConceptsFPA);
+
+    // Copy chip concepts
+    bool status = true;                 // Status of chips
+    if (chips) {
+        psArray *targetChips = target->chips; // Chips in target
+        psArray *sourceChips = source->chips; // Chips in source
+        if (targetChips->n != sourceChips->n) {
+            psError(PS_ERR_IO, true,
+                    "Number of chips in target (%ld) and source (%ld) differ --- unable to copy concepts.",
+                    targetChips->n, sourceChips->n);
+            return false;
+        }
+        for (int i = 0; i < targetChips->n; i++) {
+            pmChip *targetChip = targetChips->data[i]; // Target chip of interest
+            pmChip *sourceChip = sourceChips->data[i]; // Source chip of interest
+            if (! targetChip || ! sourceChip) {
+                continue;
+            }
+
+            status &= pmConceptsCopyChip(targetChip, sourceChip, cells);
+        }
+    }
+
+    return status;
+}
+
+bool pmConceptsCopyChip(pmChip *target, const pmChip *source, bool cells)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    // Copy chip concepts
+    copyConcepts(target->concepts, source->concepts, dontCopyConceptsChip);
+
+    // Copy cell concepts
+    bool status = true;                 // Status of cells
+    if (cells) {
+        psArray *targetCells = target->cells; // Cells in target
+        psArray *sourceCells = source->cells; // Cells in source
+        if (targetCells->n != sourceCells->n) {
+            psError(PS_ERR_IO, true,
+                    "Number of cells in target (%ld) and source (%ld) differ --- unable to copy concepts.",
+                    targetCells->n, sourceCells->n);
+            return false;
+        }
+        for (int j = 0; j < targetCells->n; j++) {
+            pmCell *targetCell = targetCells->data[j]; // Target chip of interest
+            pmCell *sourceCell = sourceCells->data[j]; // Source chip of interest
+            if (! targetCell || ! sourceCell) {
+                continue;
+            }
+
+            status &= pmConceptsCopyCell(targetCell, sourceCell);
+        }
+    }
+
+    return status;
+}
+
+
+bool pmConceptsCopyCell(pmCell *target, const pmCell *source)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    copyConcepts(target->concepts, source->concepts, dontCopyConceptsCell);
+
+    return true;
+}
+
+
+// Interpolate the concept.  Generalises the FPA/Chip/Cell
+#define CONCEPT_INTERPOLATE(SOURCE, NAME) \
+    if (strncmp(concept, NAME, strlen(NAME)) == 0) { \
+        if (!(SOURCE)) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot interpolate %s because %s not provided", \
+                    concept, NAME); \
+            psFree(string); \
+            return NULL; \
+        } \
+        psMetadataItem *item = psMetadataLookup((SOURCE)->concepts, concept); /* Item with concept */ \
+        if (!item) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Can't find concept %s in %s", concept, NAME); \
+            psFree(string); \
+            return NULL; \
+        } \
+        \
+        psString value = psMetadataItemParseString(item); /* Value of concept */ \
+        if (!value) { \
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to parse concept %s", concept); \
+            psFree(string); \
+            return NULL; \
+        } \
+        \
+        char replace[length + 2];       /* String to replace with value */ \
+        replace[0] = '{'; \
+        strcpy(replace + 1, concept); \
+        strcpy(replace + length, "}"); \
+        \
+        psTrace("psModules.concepts", 10, "Interpolating concept %s for %s", replace, value); \
+        \
+        if (!psStringSubstitute(&string, value, replace)) { \
+            psError(PS_ERR_UNKNOWN, false, "Unable to replace concept %s", concept); \
+            psFree(string); \
+            psFree(value); \
+            return NULL; \
+        } \
+        psFree(value); \
+        \
+        continue; \
+    }
+
+
+// XXX Could make the concept delimiters, currently '{' and '}', configurable
+psString pmConceptsInterpolate(const char *input,
+                               const pmFPA *fpa,
+                               const pmChip *chip,
+                               const pmCell *cell
+    )
+{
+    PS_ASSERT_STRING_NON_EMPTY(input, NULL);
+
+    psString string = psStringCopy(input); // Interpolated string, to return
+
+    char *start;                        // Start of a concept
+    while ((start = strchr(string, '{'))) {
+        char *stop = strchr(start, '}'); // End of a concept
+        int length = stop - start;      // Length of the concept name, including terminating \0
+        char concept[length];  // Name of concept
+        strncpy(concept, start + 1, length - 1);
+        concept[length - 1] = '\0';
+
+        psTrace("psModules.concepts", 7, "Interpolating concept %s", concept);
+
+        CONCEPT_INTERPOLATE(fpa,  "FPA");
+        CONCEPT_INTERPOLATE(chip, "CHIP");
+        CONCEPT_INTERPOLATE(cell, "CELL");
+
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised concept: %s", concept);
+        psFree(string);
+        return NULL;
+    }
+
+    return string;
+}
+
+
+psMetadataItem *p_pmConceptsDepend(const char *name, const psMetadata *menu, const psMetadata *source,
+                                   const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    psAssert(name && strlen(name) > 0, "Concept name is empty");
+    psAssert(menu, "Must have menu");
+    psAssert(source, "Must have source");
+
+    // Check for DEPEND
+    psString depend = NULL; // The CONCEPT.DEPEND
+    psStringAppend(&depend, "%s.DEPEND", name);
+    bool mdok;                          // Status of MD lookup
+    const char *dependConcept = psMetadataLookupStr(&mdok, source, depend); // The concept name
+    if (!mdok || !dependConcept || strlen(dependConcept) == 0) {
+        psError(PS_ERR_IO, true, "Unable to parse %s: couldn't find %s in DEFAULTS.\n", name, depend);
+        psFree(depend);
+        return NULL;
+    }
+    psFree(depend);
+    // Now look up the depend value
+    psMetadataItem *dependValue = NULL; // The value of the concept we're looking up
+    if (cell) {
+        dependValue = psMetadataLookup(cell->concepts, dependConcept);
+    }
+    if (chip && !dependValue) {
+        dependValue = psMetadataLookup(chip->concepts, dependConcept);
+    }
+    if (fpa && !dependValue) {
+        dependValue = psMetadataLookup(chip->concepts, dependConcept);
+    }
+    if (!dependValue) {
+        // Not an error --- it may be specified some other way
+        psTrace("psModules.concepts", 7, "Couldn't find DEPEND for %s", name);
+        return NULL;
+    }
+    if (dependValue->type != PS_DATA_STRING) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "%s is required to resolve %s in DEFAULTS, "
+                "but it is not of type STRING.\n", dependConcept, name);
+        return NULL;
+    }
+    const char *key = dependValue->data.V; // The key to the DEPEND menu
+    psTrace("psModules.concepts", 7, "%s.DEPEND resolves to %s....\n", name, key);
+
+    return psMetadataLookup(menu, key);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConcepts.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConcepts.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConcepts.h	(revision 22322)
@@ -0,0 +1,248 @@
+/* @file pmConcepts.h
+ * @brief Top-level functions for defining, registering, reading and writing concepts
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-30 00:53:45 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONCEPTS_H
+#define PM_CONCEPTS_H
+
+#include <pmConfig.h>
+
+/// @addtogroup Concepts Data Abstraction Concepts
+/// @{
+
+/// Source for concepts when reading and writing.
+///
+/// Since some sources become available at different times from others, we need to provide some specificity to
+/// reading and writing concepts (or we're forced to wait until everything's available, which we don't want to
+/// do).  Concepts may be read from or written to multiple sources at once by OR-ing them.
+typedef enum {
+    PM_CONCEPT_SOURCE_NONE     = 0x00,  ///< No concepts
+    PM_CONCEPT_SOURCE_BLANK    = 0x01,  ///< Blank concepts defined, but not read
+    PM_CONCEPT_SOURCE_CELLS    = 0x02,  ///< Concept comes from the camera information
+    PM_CONCEPT_SOURCE_DEFAULTS = 0x04,  ///< Concept comes from defaults
+    PM_CONCEPT_SOURCE_PHU      = 0x08,  ///< Concept comes from PHU
+    PM_CONCEPT_SOURCE_HEADER   = 0x10,  ///< Concept comes from FITS header
+    PM_CONCEPT_SOURCE_DATABASE = 0x20,  ///< Concept comes from database
+    PM_CONCEPT_SOURCE_ALL      = 0xfe   ///< All concepts (exclude BLANK)
+} pmConceptSource;
+
+/// Function to call to parse a concept once it has been read
+typedef psMetadataItem* (*pmConceptParseFunc)(const psMetadataItem *concept, ///< Concept to parse
+        const psMetadataItem *pattern, ///< Pattern for parsing
+                                              pmConceptSource source, ///< Source of concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                             );
+
+/// Function to call to format a concept for writing
+typedef psMetadataItem* (*pmConceptFormatFunc)(const psMetadataItem *concept, ///< Concept to format
+                                               pmConceptSource source, ///< Source of concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                              );
+
+/// A "concept" specification
+///
+/// Defines the name, default comment, blank value, and functions to parse (after reading) and format (before
+/// writing) the concept.
+typedef struct
+{
+    psMetadataItem *blank;              ///< Blank value of concept; also contains the name and comment
+    pmConceptParseFunc parse;           ///< Function to call to read the concept
+    pmConceptFormatFunc format;         ///< Function to call to write the concept
+    bool required;                      ///< Is concept required (throw an error on problems)?
+}
+pmConceptSpec;
+
+/// Allocator for pmConceptSpec
+pmConceptSpec *pmConceptSpecAlloc(psMetadataItem *blank, ///< Blank value; contains the name
+                                  pmConceptParseFunc parse, ///< Function to call to parse the concept
+                                  pmConceptFormatFunc format, ///< Function to call to format the concept
+                                  bool required ///< Is concept required?
+                                 );
+
+/// Get whether a particular concept is required
+bool pmConceptGetRequired(const char *name, ///< Name of concept
+                          pmFPALevel level ///< Level at which concept resides
+    );
+
+/// Set whether a particular concept is required
+bool pmConceptSetRequired(const char *name, ///< Name of concept
+                          pmFPALevel level, ///< Level at which concept resides
+                          bool required ///< Whether concept is required or not
+    );
+
+/// Register a new concept for parsing and formatting
+///
+/// Defines a new concept, based on the blank value (with name and default comment), and functions to parse
+/// and format the concept.  The new concept is registered at the specified level (FPA, chip or cell).  If the
+/// parse function is NULL, then a default parse function is used, which performs minimal parsing.  Similarly
+/// for the format function.
+bool pmConceptRegister(psMetadataItem *blank, ///< Blank value; contains the name and default comment
+                       pmConceptParseFunc parse, ///< Function to call to parse the concept, or NULL
+                       pmConceptFormatFunc format, ///< Function to call to format the concept, or NULL
+                       bool required,   ///< Is concept required?
+                       pmFPALevel level ///< Level at which to store concept in the FPA hierarchy
+                      );
+
+/// Get a list of defined concepts for a particular level.
+psList *pmConceptsList(pmFPALevel level);
+
+/// Read the concepts for the given set of fpa, chip, cell
+///
+/// Attempts to read as many concepts as possible from the specified source for the specified FPA, chip and
+/// cell.  That is, it will read chip- and cell-level concepts in addition to fpa-level concepts, if the chip
+/// and cell are provided.
+bool pmConceptsRead(pmFPA *fpa,         ///< FPA for which to read concepts
+                    pmChip *chip,       ///< Chip for which to read concepts, or NULL
+                    pmCell *cell,       ///< Cell for which to read concepts, or NULL
+                    pmConceptSource source, ///< The source of the concepts to read
+                    pmConfig *config    ///< Configuration
+                   );
+
+/// Set the concepts within the FPA to the blank value
+bool pmConceptsBlankFPA(pmFPA *fpa      ///< FPA for which to set blank concepts
+                       );
+
+/// Read concepts for an FPA; optionally, read concepts at all lower levels.
+///
+/// Once concepts should be available for reading at the FPA-level, this function attempts to read the
+/// concepts from the specified source.  It also allows concepts to be read at lower levels by iterating over
+/// the components.
+bool pmConceptsReadFPA(pmFPA *fpa,      ///< FPA for which to read concepts
+                       pmConceptSource source, ///< Source for concepts
+                       bool propagateDown, ///< Propagate to lower levels?
+                       pmConfig *config         ///< Configuration
+                      );
+
+/// Write concepts for an FPA; optionally, write concepts at all lower levels.
+///
+/// This function writes all concepts for the FPA to the specified "source".  It also allows concepts to be
+/// 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
+                       );
+
+/// Set the concepts within the chip to the blank value
+bool pmConceptsBlankChip(pmChip *chip   ///< FPA for which to set blank concepts
+                        );
+
+/// Read concepts for a chip; optionally, read concepts at the FPA and cell levels.
+///
+/// Once concepts should be available for reading at the FPA-level, this function attempts to read the
+/// concepts from the specified source.  It also allows concepts to be read at the fpa level (through the
+/// parent), and the cell level by iterating over the components.
+bool pmConceptsReadChip(pmChip *chip,   ///< Chip for which to read concepts
+                        pmConceptSource source, ///< Source for concepts
+                        bool propagateUp, ///< Propagate to higher levels?
+                        bool propagateDown, ///< Propagate to lower levels?
+                        pmConfig *config        ///< Configuration
+                       );
+
+/// Write concepts for a chip; optionally, write concepts at the FPA and cell levels.
+///
+/// This function writes all concepts for the chip to the specified "source".  It also allows concepts to be
+/// 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?
+                         pmConfig *config       ///< Configuration
+                        );
+
+/// Set the concepts within the cell to the blank value
+bool pmConceptsBlankCell(pmCell *cell   ///< Cell for which to set blank concepts
+                        );
+
+/// Read concepts for a cell; optionally, read concepts for the parents.
+///
+/// Once concepts should be available for reading at the FPA-level, this function attempts to read the
+/// concepts from the specified source.  It also allows concepts to be read at upper levels through the
+/// parents (note, it would not read concepts for all chips, but only the parent of this cell).
+bool pmConceptsReadCell(pmCell *cell,   ///< Cell for which to read concepts
+                        pmConceptSource source, ///< Source for concepts
+                        bool propagateUp, ///< Propagate to higher levels?
+                        pmConfig *config        ///< Configuration
+                       );
+
+/// Write concepts for a cell; optionally, write concepts for the parents.
+///
+/// This function writes all concepts for the chip to the specified "source".  It also allows concepts to be
+/// written for the upper levels through the parents (note, it would not write concepts for all chips, but
+/// 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
+                        );
+
+/// Initialise the concepts system.
+///
+/// Register the standard concepts, so that concepts may be read and written.  This function is called
+/// automatically the first time the concepts functions are used.
+bool pmConceptsInit(void);
+
+/// Signifies that the user is done with the concepts system.
+///
+/// Frees the registered concepts so there is no memory leak when the user checks "persistent" memory.
+void pmConceptsDone(void);
+
+/// Copy all the concepts within an FPA to another FPA
+///
+/// Iterates over all components of the FPA, and copies the concepts metadata from the source to the target.
+bool pmFPACopyConcepts(pmFPA *target,   ///< The target FPA
+                       const pmFPA *source    ///< The source FPA
+                      );
+
+/// Copy the concepts within an FPA to another FPA; optionally recurse to lower levels
+bool pmConceptsCopyFPA(pmFPA *target,   ///< Target FPA
+                       const pmFPA *source, ///< Source FPA
+                       bool chips,      ///< Recurse to chips level?
+                       bool cells       ///< Recurse to cells level?
+    );
+
+/// Copy the concepts within a chip to another chip; optionally recurse to lower level
+bool pmConceptsCopyChip(pmChip *target, ///< Target chip
+                        const pmChip *source, ///< Source chip
+                        bool cells      ///< Recurse to cells level?
+    );
+
+/// Copy the concepts within a cell to another cell
+bool pmConceptsCopyCell(pmCell *target, ///< Target cell
+                        const pmCell *source ///< Source cell
+    );
+
+/// Interpolate a concept name to the actual value
+///
+/// Concepts enclosed within braces {}, are replaced with the value of the concept
+psString pmConceptsInterpolate(const char *input, ///< Input string
+                               const pmFPA *fpa, ///< FPA with concept values, or NULL
+                               const pmChip *chip, ///< Chip with concept values, or NULL
+                               const pmCell *cell ///< Cell with concept values, or NULL
+    );
+
+/// Look up a dependency menu to get a concept's value
+///
+/// Returns a psMetadataItem with the concept value
+psMetadataItem *p_pmConceptsDepend(const char *name, ///< Name of concept for which to get dependent value
+                                   const psMetadata *menu, ///< Menu in which to look up key
+                                   const psMetadata *source, ///< Source metadata with CONCEPT.DEPEND
+                                   const pmFPA *fpa, ///< FPA for dependency
+                                   const pmChip *chip, ///< Chip for dependency
+                                   const pmCell *cell ///< Cell for dependency
+    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsAverage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsAverage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsAverage.c	(revision 22322)
@@ -0,0 +1,306 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <string.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmConcepts.h"
+#include "pmConceptsAverage.h"
+
+// Update a metadata entry directly
+#define MD_UPDATE(MD, NAME, TYPE, VALUE) \
+{ \
+    psMetadataItem *item = psMetadataLookup(MD, NAME); \
+    item->data.TYPE = VALUE; \
+}
+
+// Update a metadata string entry directly
+#define MD_UPDATE_STR(MD, NAME, VALUE) \
+{ \
+    psMetadataItem *item = psMetadataLookup(MD, NAME); \
+    psFree(item->data.str); \
+    item->data.str = psStringCopy(VALUE); \
+}
+
+bool pmConceptsAverageFPAs(pmFPA *target, psList *sources)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_INT_POSITIVE(sources->n, false);
+
+    double time      = 0.0;             // Time of observation
+    psTimeType timeSys = 0;             // Time system
+    char *filter     = NULL;            // Filter
+
+    int num = 0;                        // Number of FPAs
+    psListIterator *sourcesIter = psListIteratorAlloc(sources, PS_LIST_HEAD, false); // Iterator for sources
+    pmFPA *fpa = NULL;                  // Source FPA from iteration
+    while ((fpa = psListGetAndIncrement(sourcesIter))) {
+        if (!fpa) {
+            continue;
+        }
+
+        num++;
+
+        psTime *fpaTime = psMetadataLookupPtr(NULL, fpa->concepts, "FPA.TIME");
+        time       += psTimeToMJD(fpaTime);
+        if (num == 1) {
+            timeSys = psMetadataLookupS32(NULL, fpa->concepts, "FPA.TIMESYS");
+            filter = psMetadataLookupStr(NULL, fpa->concepts, "FPA.FILTER");
+        } else {
+            if (timeSys != psMetadataLookupS32(NULL, fpa->concepts, "FPA.TIMESYS")) {
+                psWarning("Differing FPA.TIMESYS in use: %d vs %d\n",
+                          timeSys, psMetadataLookupS32(NULL, fpa->concepts, "FPA.TIMESYS"));
+            }
+            if (strcmp(filter, psMetadataLookupStr(NULL, fpa->concepts, "FPA.FILTER")) != 0) {
+                psWarning("Differing FPA.FILTER in use: %s vs %s\n",
+                          filter, psMetadataLookupStr(NULL, fpa->concepts, "FPA.FILTER"));
+            }
+        }
+    }
+    psFree(sourcesIter);
+
+    time      /= (double)num;
+
+    MD_UPDATE(target->concepts, "FPA.TIMESYS", S32, timeSys);
+    MD_UPDATE_STR(target->concepts, "FPA.FILTER", filter);
+
+    // FPA.TIME needs special care
+    {
+        psMetadataItem *timeItem = psMetadataLookup(target->concepts, "FPA.TIME");
+        psFree(timeItem->data.V);
+        timeItem->data.V = psTimeFromMJD(time);
+    }
+
+    return true;
+}
+
+
+// Set a variety of concepts in a cell by averaging over several
+// XXX does not properly set XSIZE, YSIZE
+bool pmConceptsAverageCells(pmCell *target, psList *sources, psRegion *trimsec, psRegion *biassec, bool same)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_INT_POSITIVE(sources->n, false);
+
+    float gain       = 0.0;             // Gain
+    float readnoise  = 0.0;             // Read noise
+    float saturation = INFINITY;        // Saturation level
+    float bad        = -INFINITY;       // Bad level
+    float exposure   = 0.0;             // Exposure time
+    float darktime   = 0.0;             // Dark time
+    double time      = 0.0;             // Time of observation
+    psTimeType timeSys = 0;             // Time system
+    int readdir      = 0;               // Cell read direction
+    int xBin = 0, yBin = 0;             // Binning
+    int x0 = 0, y0 = 0;                 // Offset
+    int xParity = 0, yParity = 0;       // Parity
+
+    int nCells = 0;                     // Number of cells;
+    psListIterator *sourcesIter = psListIteratorAlloc(sources, PS_LIST_HEAD, false); // Iterator for sources
+    pmCell *cell = NULL;                // Source cell from iteration
+    while ((cell = psListGetAndIncrement(sourcesIter))) {
+        if (!cell) {
+            continue;
+        }
+
+        nCells++;
+        gain       += psMetadataLookupF32(NULL, cell->concepts, "CELL.GAIN");
+        readnoise  += psMetadataLookupF32(NULL, cell->concepts, "CELL.READNOISE");
+        exposure   += psMetadataLookupF32(NULL, cell->concepts, "CELL.EXPOSURE");
+        darktime   += psMetadataLookupF32(NULL, cell->concepts, "CELL.DARKTIME");
+        psTime *cellTime = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TIME");
+        time       += psTimeToMJD(cellTime);
+        if (nCells == 1) {
+            timeSys = psMetadataLookupS32(NULL, cell->concepts, "CELL.TIMESYS");
+            readdir = psMetadataLookupS32(NULL, cell->concepts, "CELL.READDIR");
+            xBin    = psMetadataLookupS32(NULL, cell->concepts, "CELL.XBIN");
+            yBin    = psMetadataLookupS32(NULL, cell->concepts, "CELL.YBIN");
+
+            if (same) {
+                // Only makes sense to update these if they are the same cell
+                x0 = psMetadataLookupS32(NULL, cell->concepts, "CELL.X0");
+                y0 = psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0");
+                xParity = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY");
+                yParity = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY");
+            }
+        } else {
+            if (timeSys != psMetadataLookupS32(NULL, cell->concepts, "CELL.TIMESYS")) {
+                psWarning("Differing CELL.TIMESYS in use: %d vs %d\n",
+                          timeSys, psMetadataLookupS32(NULL, cell->concepts, "CELL.TIMESYS"));
+            }
+            if (readdir != psMetadataLookupS32(NULL, cell->concepts, "CELL.READDIR")) {
+                psWarning("Differing CELL.READDIR in use: %d vs %d\n",
+                          readdir, psMetadataLookupS32(NULL, cell->concepts, "CELL.READDIR"));
+            }
+            if (xBin != psMetadataLookupS32(NULL, cell->concepts, "CELL.XBIN")) {
+                psWarning("Differing CELL.XBIN in use: %d vs %d\n",
+                          xBin, psMetadataLookupS32(NULL, cell->concepts, "CELL.XBIN"));
+            }
+            if (yBin != psMetadataLookupS32(NULL, cell->concepts, "CELL.YBIN")) {
+                psWarning("Differing CELL.YBIN in use: %d vs %d\n",
+                          yBin, psMetadataLookupS32(NULL, cell->concepts, "CELL.YBIN"));
+            }
+            if (same) {
+                if (x0 != psMetadataLookupS32(NULL, cell->concepts, "CELL.X0")) {
+                    psWarning("Differing CELL.X0 in use: %d vs %d\n",
+                              x0, psMetadataLookupS32(NULL, cell->concepts, "CELL.X0"));
+                }
+                if (y0 != psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0")) {
+                    psWarning("Differing CELL.Y0 in use: %d vs %d\n",
+                              y0, psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0"));
+                }
+                if (xParity != psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY")) {
+                    psWarning("Differing CELL.XPARITY in use: %d vs %d\n",
+                              xParity, psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY"));
+                }
+                if (yParity != psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY")) {
+                    psWarning("Differing CELL.YPARITY in use: %d vs %d\n",
+                              yParity, psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY"));
+                }
+            }
+        }
+
+        float cellSaturation = psMetadataLookupF32(NULL, cell->concepts, "CELL.SATURATION");
+        if (cellSaturation < saturation) {
+            saturation = cellSaturation;
+        }
+        float cellBad = psMetadataLookupF32(NULL, cell->concepts, "CELL.BAD");
+        if (cellBad > bad) {
+            bad = cellBad;
+        }
+    }
+    psFree(sourcesIter);
+
+    gain      /= (float)nCells;
+    readnoise /= (float)nCells;
+    exposure  /= (float)nCells;
+    darktime  /= (float)nCells;
+    time      /= (double)nCells;
+
+    MD_UPDATE(target->concepts, "CELL.GAIN", F32, gain);
+    MD_UPDATE(target->concepts, "CELL.READNOISE", F32, readnoise);
+    MD_UPDATE(target->concepts, "CELL.SATURATION", F32, saturation);
+    MD_UPDATE(target->concepts, "CELL.BAD", F32, bad);
+    MD_UPDATE(target->concepts, "CELL.EXPOSURE", F32, exposure);
+    MD_UPDATE(target->concepts, "CELL.DARKTIME", F32, darktime);
+    MD_UPDATE(target->concepts, "CELL.TIMESYS", S32, timeSys);
+    MD_UPDATE(target->concepts, "CELL.READDIR", S32, readdir);
+    MD_UPDATE(target->concepts, "CELL.XBIN", S32, xBin);
+    MD_UPDATE(target->concepts, "CELL.YBIN", S32, yBin);
+    if (same) {
+        MD_UPDATE(target->concepts, "CELL.X0", S32, x0);
+        MD_UPDATE(target->concepts, "CELL.Y0", S32, y0);
+        MD_UPDATE(target->concepts, "CELL.XPARITY", S32, xParity);
+        MD_UPDATE(target->concepts, "CELL.YPARITY", S32, yParity);
+    }
+
+    // CELL.TIME needs special care
+    {
+        psMetadataItem *timeItem = psMetadataLookup(target->concepts, "CELL.TIME");
+        psFree(timeItem->data.V);
+        timeItem->data.V = psTimeFromMJD(time);
+    }
+
+    // CELL.TRIMSEC needs special care
+    if (trimsec) {
+        psMetadataItem *trimsecItem = psMetadataLookup(target->concepts, "CELL.TRIMSEC");
+        psFree(trimsecItem->data.V);
+        trimsecItem->data.V = psMemIncrRefCounter(trimsec);
+    }
+
+    // CELL.BIASSEC needs special care
+    if (biassec) {
+        psMetadataItem *biassecItem = psMetadataLookup(target->concepts, "CELL.BIASSEC");
+        psFree(biassecItem->data.V);
+        biassecItem->data.V = psMemIncrRefCounter(biassec);
+    }
+
+    return true;
+}
+
+bool pmConceptsAverageChips(pmChip *target, psList *sources, bool same)
+{
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_INT_POSITIVE(sources->n, false);
+
+    float temp = 0.0;                   // Temperature
+    int x0 = 0, y0 = 0;                 // Offset
+    int xParity = 0, yParity = 0;       // Parity
+    int xSize = 0, ySize = 0;           // Size
+    psString id = NULL;                 // Identifier
+
+    int nChips = 0;                     // Number of chips;
+    psListIterator *sourcesIter = psListIteratorAlloc(sources, PS_LIST_HEAD, false); // Iterator for sources
+    pmChip *chip = NULL;                // Source chip from iteration
+    while ((chip = psListGetAndIncrement(sourcesIter))) {
+        if (!chip) {
+            continue;
+        }
+        temp += psMetadataLookupF32(NULL, chip->concepts, "CHIP.TEMP");
+        if (nChips == 0) {
+            xSize = psMetadataLookupS32(NULL, chip->concepts, "CHIP.XSIZE");
+            ySize = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YSIZE");
+            xParity = psMetadataLookupS32(NULL, chip->concepts, "CHIP.XPARITY");
+            yParity = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY");
+            x0 = psMetadataLookupS32(NULL, chip->concepts, "CHIP.X0");
+            y0 = psMetadataLookupS32(NULL, chip->concepts, "CHIP.Y0");
+            id = psMetadataLookupStr(NULL, chip->concepts, "CHIP.ID");
+        } else {
+            if (xSize != psMetadataLookupS32(NULL, chip->concepts, "CHIP.XSIZE")) {
+                psWarning("Differing CHIP.XSIZE in use: %d vs %d\n",
+                    xSize, psMetadataLookupS32(NULL, chip->concepts, "CHIP.XSIZE"));
+            }
+            if (ySize != psMetadataLookupS32(NULL, chip->concepts, "CHIP.YSIZE")) {
+                psWarning("Differing CHIP.YSIZE in use: %d vs %d\n",
+                    ySize, psMetadataLookupS32(NULL, chip->concepts, "CHIP.YSIZE"));
+            }
+            if (xParity != psMetadataLookupS32(NULL, chip->concepts, "CHIP.XPARITY")) {
+                psWarning("Differing CHIP.XPARITY in use: %d vs %d\n",
+                    xParity, psMetadataLookupS32(NULL, chip->concepts, "CHIP.XPARITY"));
+            }
+            if (yParity != psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY")) {
+                psWarning("Differing CHIP.YPARITY in use: %d vs %d\n",
+                    yParity, psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY"));
+            }
+            if (x0 != psMetadataLookupS32(NULL, chip->concepts, "CHIP.X0")) {
+                psWarning("Differing CHIP.X0 in use: %d vs %d\n",
+                    x0, psMetadataLookupS32(NULL, chip->concepts, "CHIP.X0"));
+            }
+            if (y0 != psMetadataLookupS32(NULL, chip->concepts, "CHIP.Y0")) {
+                psWarning("Differing CHIP.Y0 in use: %d vs %d\n",
+                    y0, psMetadataLookupS32(NULL, chip->concepts, "CHIP.Y0"));
+            }
+            psString newID = psMetadataLookupStr(NULL, chip->concepts, "CHIP.ID");
+            if (id && newID && strcmp(id, newID)) {
+                psWarning("Differing CHIP.ID in use: %s vs %s\n", id, newID);
+            }
+        }
+
+        nChips++;
+    }
+    psFree(sourcesIter);
+
+    temp /= (float)nChips;
+
+    MD_UPDATE(target->concepts, "CHIP.TEMP", F32, temp);
+    if (same) {
+        MD_UPDATE(target->concepts, "CHIP.X0", S32, x0);
+        MD_UPDATE(target->concepts, "CHIP.Y0", S32, y0);
+        MD_UPDATE(target->concepts, "CHIP.XSIZE", S32, xSize);
+        MD_UPDATE(target->concepts, "CHIP.YSIZE", S32, ySize);
+        MD_UPDATE(target->concepts, "CHIP.XPARITY", S32, xParity);
+        MD_UPDATE(target->concepts, "CHIP.YPARITY", S32, yParity);
+        MD_UPDATE_STR(target->concepts, "CHIP.ID", id);
+    }
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsAverage.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsAverage.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsAverage.h	(revision 22322)
@@ -0,0 +1,65 @@
+/* @file pmConceptsAverage.h
+ * @brief Average the values of multiple concepts
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-04-10 06:31:42 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONCEPTS_AVERAGE_H
+#define PM_CONCEPTS_AVERAGE_H
+
+/// @addtogroup Concepts Data Abstraction Concepts
+/// @{
+
+/// Set a variety of concepts in an FPA by averaging over several
+///
+/// This function averages the values of the following concepts:
+/// FPA.TIME
+/// And ensure the following concepts are consistent:
+/// FPA.FILTER
+/// FPA.TIMESYS
+/// The following concepts could be of interest to the user, but are not treated:
+/// FPA.EXPOSURE
+bool pmConceptsAverageFPAs(pmFPA *target,///< Target FPA
+                           psList *sources ///< List of source FPAs
+    );
+
+/// Set a variety of concepts in a cell by averaging over several
+///
+/// In some instances, we want to combine the values of a concept for several cells into a single concept for
+/// a single cell (e.g., when mosaicking multiple cells into a chip with one "cell").  This function averages
+/// the values of various concepts:
+/// - CELL.GAIN
+/// - CELL.READNOISE
+/// - CELL.EXPOSURE
+/// - CELL.DARKTIME
+/// - CELL.TIME
+/// For other concepts, it ensures the values are consistent:
+/// - CELL.READDIR
+/// - CELL.TIMESYS
+/// - CELL.X0, CELL.Y0
+/// - CELL.XPARITY, CELL.YPARITY
+/// And for others, it takes the "worst" possible value:
+/// - CELL.SATURATION
+/// - CELL.BAD
+/// These concepts are only handled if the cells are all the same cell (mosaicking vs stacking):
+/// - CELL.X0, CELL.Y0
+/// - CELL.XPARITY, CELL.YPARITY
+bool pmConceptsAverageCells(pmCell *target,///< Target cell
+                            psList *sources, ///< List of source cells
+                            psRegion *trimsec, ///< The new trim section
+                            psRegion *biassec, ///< The new bias section
+                            bool same   ///< Are the cells the same cell from different chips?
+                           );
+
+/// Set a variety of concepts in a chip by averaging over several 
+bool pmConceptsAverageChips(pmChip *target,///< Target chip
+                            psList *sources, ///< List of source chips
+                            bool same   ///< Are the chips the same chip from different exposures?
+                           );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsPhotcode.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsPhotcode.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsPhotcode.c	(revision 22322)
@@ -0,0 +1,41 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmConceptsPhotcode.h"
+
+psString pmConceptsPhotcodeForView(pmFPAfile *file, const pmFPAview *view)
+{
+    PS_ASSERT_PTR_NON_NULL(file, NULL);
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+
+    if (view->chip < -1) {
+        psError(PS_ERR_IO, true, "Photcodes undefined for FPA: defined by chip\n");
+        return NULL;
+    }
+
+    // select photcode rule from camera configuration
+    bool mdok;                          // Status of MD lookup
+    char *rule = psMetadataLookupStr(&mdok, file->camera, "PHOTCODE.RULE");
+    if (!mdok || !rule || strlen(rule) == 0) {
+        psError(PS_ERR_IO, true, "PHOTCODE.RULE not found in camera configuration.");
+        return NULL;
+    }
+
+    // convert rule to real photcode
+    psString photcode = pmFPAfileNameFromRule(rule, file, view);
+
+    return photcode;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsPhotcode.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsPhotcode.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsPhotcode.h	(revision 22322)
@@ -0,0 +1,25 @@
+/* @file  pmConceptsPhotcode.h
+ * @brief Generate a photcode from the concepts
+ *
+ * @author Eugene Magnier, IfA
+ *
+ * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-12 03:27:14 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONCEPTS_PHOTCODE_H
+#define PM_CONCEPTS_PHOTCODE_H
+
+/// @addtogroup Concepts Data Abstraction Concepts
+/// @{
+
+/// Return the photcode based on the PHOTCODE.RULE in the camera configuration
+///
+/// A photometry code ("photcode") is a string that represents the combination of filter and detector (chip).
+/// This functions generates a photcode for a particular chip within the FPA, based on the PHOTCODE.RULE in
+/// the camera configuration.  Interpolation using the usual syntax (e.g., "{CHIP.NAME}") is permitted.
+psString pmConceptsPhotcodeForView(pmFPAfile *file, const pmFPAview *view);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsRead.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsRead.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsRead.c	(revision 22322)
@@ -0,0 +1,466 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmHDUUtils.h"
+#include "pmConcepts.h"
+#include "pmConceptsRead.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// This function gets called for the really boring concepts --- where all you have to do is parse from a
+// header or database and you don't need to muck around with conversions.  There is no similar "formatPlain",
+// since the type is already known.
+static psMetadataItem *parsePlain(psMetadataItem *concept, // The concept to parse
+                                  const psMetadataItem *pattern // The concept pattern
+                                 )
+{
+    assert(concept);
+    assert(pattern);
+
+    switch (pattern->type) {
+    case PS_DATA_STRING: {
+            psString string = psMetadataItemParseString(concept); // Get the string, so I can free it after it
+            // goes on the MetadataItem
+            psMetadataItem *item = psMetadataItemAllocStr(pattern->name, pattern->comment, string);
+            psFree(string);
+            return item;
+        }
+    case PS_DATA_S32:
+        return psMetadataItemAllocS32(pattern->name, pattern->comment, psMetadataItemParseS32(concept));
+    case PS_DATA_F32:
+        return psMetadataItemAllocF32(pattern->name, pattern->comment, psMetadataItemParseF32(concept));
+    case PS_DATA_F64:
+        return psMetadataItemAllocF64(pattern->name, pattern->comment, psMetadataItemParseF64(concept));
+    default:
+        psWarning("Concept %s (%s) is not of a standard type (%x)\n",
+                 pattern->name, pattern->comment, pattern->type);
+        return NULL;
+    }
+}
+
+// Parse a single concept
+static bool conceptParse(pmConceptSpec *spec, // The concept specification
+                         psMetadataItem *concept, // The concept to parse
+                         pmConceptSource source, // The concept source
+                         psMetadata *cameraFormat, // The camera format
+                         psMetadata *target, // The target
+                         const pmFPA *fpa, // The FPA
+                         const pmChip *chip, // The chip
+                         const pmCell *cell // The cell
+                        )
+{
+    assert(spec);
+    assert(cameraFormat);
+    assert(target);
+
+    if (!concept) {
+        psError(PS_ERR_UNKNOWN, true, "Concept is NULL");
+        return false;
+    }
+
+    psMetadataItem *parsed = NULL;  // The parsed concept
+    if (spec->parse) {
+        parsed = spec->parse(concept, spec->blank, source, cameraFormat, fpa, chip, cell);
+    } else {
+        parsed = parsePlain(concept, spec->blank);
+    }
+    if (!parsed) {
+        if (spec->required) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to parse concept %s\n", spec->blank->name);
+            return false;
+        } else {
+            psWarning("Unable to parse concept %s, but concept not marked as required.\n", spec->blank->name);
+            psErrorClear();
+            return true; // XXX return?
+        }
+    }
+
+    psTrace ("psModules.concepts", 3, "parsing concept: %s\n", spec->blank->name);
+
+    // Plug the parsed concept into a new psMetadataItem, so each "concept" has its own version that can
+    // be altered without affecting the others.  Also, so that we maintain the template name and comment.
+    psMetadataItem *cleaned = NULL;     // Item that's been cleaned up --- correct name and comment
+    switch (spec->blank->type) {
+    case PS_DATA_STRING:
+        cleaned = psMetadataItemAllocStr(spec->blank->name, spec->blank->comment, parsed->data.V);
+        break;
+    case PS_DATA_S32:
+        cleaned = psMetadataItemAllocS32(spec->blank->name, spec->blank->comment, parsed->data.S32);
+        break;
+    case PS_DATA_F32:
+        cleaned = psMetadataItemAllocF32(spec->blank->name, spec->blank->comment, parsed->data.F32);
+        break;
+    case PS_DATA_F64:
+        cleaned = psMetadataItemAllocF64(spec->blank->name, spec->blank->comment, parsed->data.F64);
+        break;
+    default:
+        cleaned = psMetadataItemAlloc(spec->blank->name, parsed->type, spec->blank->comment,
+                                      parsed->data.V);
+    }
+    psFree(parsed);
+    psMetadataAddItem(target, cleaned, PS_LIST_TAIL, PS_META_REPLACE);
+    psFree(cleaned);                 // Drop reference
+    return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool p_pmConceptsReadFromCells(psMetadata *target, const psMetadata *specs, const pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(specs, false);
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    if (!cell) {
+        psError(PS_ERR_UNKNOWN, true, "cell is NULL");
+        return false;
+    }
+
+    pmHDU *hdu = pmHDUGetLowest(NULL, NULL, cell); // The HDU at the lowest level
+    if (!hdu) {
+        psError(PS_ERR_UNKNOWN, true, "Can't find HDU for cell");
+        return false;
+    }
+    psMetadata *cameraFormat = hdu->format; // The camera format
+    psMetadata *cellConfig = cell->config; // The camera configuration for this cell
+    psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *specItem = NULL;    // Item from the specs metadata
+    bool status = true;                 // Status of reading concepts
+    while ((specItem = psMetadataGetAndIncrement(specsIter))) {
+        pmConceptSpec *spec = specItem->data.V; // The specification
+        psString name = specItem->name; // The concept name
+        psMetadataItem *conceptItem = psMetadataLookup(cellConfig, name); // The concept, or NULL
+        psTrace("psModules.concepts", 10, "%s: %p\n", name, conceptItem);
+        if (conceptItem) {
+            if (conceptItem->type == PS_DATA_STRING) {
+                // Check the SOURCE
+                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
+                psFree(nameSource);
+                if (mdok && source && strlen(source) > 0 && strcasecmp(source, "VALUE") == 0) {
+                    if (!conceptParse(spec, conceptItem, PM_CONCEPT_SOURCE_CELLS,
+                                      cameraFormat, target, NULL, NULL, cell)) {
+                        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to parse concept %s from camera "
+                                "configuration\n", name);
+                        status = false;
+                    }
+                } else if (source && (strlen(source) == 0 || strcasecmp(source, "HEADER") != 0)) {
+                    // We leave "HEADER" to pmConceptsReadFromHeader
+                    psError(PS_ERR_IO, true, "%s isn't HEADER or VALUE --- can't read %s\n", source,
+                            name);
+                    continue;
+                }
+            } else {
+                // Another type --- should be OK
+                if (!conceptParse(spec, conceptItem, PM_CONCEPT_SOURCE_CELLS,
+                                  cameraFormat, target, NULL, NULL, cell)) {
+                    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to parse concept %s from camera "
+                            "configuration.\n", name);
+                    status = false;
+                }
+            }
+        }
+    }
+    psFree(specsIter);
+    return status;
+}
+
+psMetadataItem *p_pmConceptsReadSingleFromDefaults(const char *name, const psMetadata *defaults,
+                                                  const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+    PS_ASSERT_METADATA_NON_NULL(defaults, NULL);
+
+    psMetadataItem *item = psMetadataLookup(defaults, name); // The concept, or NULL
+    psTrace("psModules.concepts", 10, "%s: %p\n", name, item);
+    if (item && item->type == PS_DATA_METADATA) {
+        // This is a menu
+        psTrace("psModules.concepts", 5, "%s is of type METADATA.\n", name);
+        item = p_pmConceptsDepend(name, item->data.md, defaults, fpa, chip, cell);
+    }
+    return item;
+}
+
+bool p_pmConceptsReadFromDefaults(psMetadata *target, const psMetadata *specs,
+                                  const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(specs, false);
+    PS_ASSERT_PTR_NON_NULL(target, false);
+
+    psTrace("psModules.concepts", 3, "Reading concepts from defaults...\n");
+
+    pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
+    if (!hdu) {
+        // We read the defaults for all the HDUs we could find
+        return true;
+    }
+    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) {
+        psError(PS_ERR_IO, true, "Failed to find \"DEFAULTS\"");
+        return false;
+    }
+    psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *specItem = NULL;    // Item from the specs metadata
+    bool status = true;                 // Status of reading concepts
+    psErrorClear();   // we're going to declare all errors "old" => won't clear stack
+    while ((specItem = psMetadataGetAndIncrement(specsIter))) {
+        pmConceptSpec *spec = specItem->data.V; // The specification
+        psString name = specItem->name; // The concept name
+        psMetadataItem *conceptItem = p_pmConceptsReadSingleFromDefaults(name, defaults, fpa, chip, cell);
+        if (conceptItem && !conceptParse(spec, conceptItem, PM_CONCEPT_SOURCE_DEFAULTS,
+                                         cameraFormat, target, fpa, chip, cell)) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to parse concept %s from DEFAULTS.\n", name);
+            status = false;
+        }
+    }
+    psFree(specsIter);
+    return status;
+}
+
+
+bool p_pmConceptsReadFromHeader(psMetadata *target, const psMetadata *specs,
+                                const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(specs, false);
+    PS_ASSERT_PTR_NON_NULL(target, false);
+
+    pmHDU *hduLow = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
+    if (!hduLow) {
+        // We read the defaults for all the HDUs we could find
+        return true;
+    }
+    pmHDU *hduHigh = pmHDUGetHighest(fpa, chip, cell); // The HDU at the highest level
+    if (!hduHigh) {
+        psError(PS_ERR_UNKNOWN, true, "Can't find HDU at the highest level");
+        return false;
+    }
+    assert(hduLow->format == hduHigh->format); // Just in case....
+    psMetadata *cameraFormat = hduLow->format; // The camera format
+    bool mdok = true;                   // Status of MD lookup
+    psMetadata *transSpec = psMetadataLookupMetadata(&mdok, cameraFormat, "TRANSLATION"); // TRANSLATION spec
+    if (!mdok || !transSpec) {
+        psError(PS_ERR_IO, true, "Failed to find \"TRANSLATION\"");
+        return false;
+    }
+
+    psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *specItem = NULL;    // Item from the specs metadata
+    bool status = true;                 // Status of reading concepts
+    while ((specItem = psMetadataGetAndIncrement(specsIter))) {
+        pmConceptSpec *spec = specItem->data.V; // The specification
+        psString name = specItem->name; // The concept name
+        psMetadataItem *headerItem = NULL; // The value of the concept from the header
+
+        // First check the cell configuration
+        if (cell && cell->config) {
+            psMetadataItem *conceptItem = psMetadataLookup(cell->config, name); // The concept, or NULL
+            if (conceptItem) {
+                // Check the SOURCE
+                psString nameSource = NULL; // String with the concept name and ".SOURCE" added
+                psStringAppend(&nameSource, "%s.SOURCE", name);
+                psString source = psMetadataLookupStr(&mdok, cell->config, nameSource); // The source
+                psFree(nameSource);
+                if (mdok && strlen(source) && strcasecmp(source, "HEADER") == 0) {
+                    if (hduLow->header) {
+                        headerItem = psMetadataLookup(hduLow->header, conceptItem->data.V);
+                    }
+                    if (!headerItem && hduHigh != hduLow && hduHigh->header) {
+                        headerItem = psMetadataLookup(hduHigh->header, conceptItem->data.V);
+                    }
+                    psMemIncrRefCounter(headerItem);
+                }
+                // Leave the error handling to pmConceptsFromCamera, which should already have been called
+            }
+        }
+
+        if (!headerItem) {
+            psMetadataItem *formatItem = psMetadataLookup(transSpec, name); // Item with keyword
+            if (!formatItem) {
+                continue;
+            }
+            if (formatItem->type == PS_DATA_METADATA) {
+                // This is a menu
+                psTrace("psModules.concepts", 5, "%s is of type METADATA.\n", name);
+                formatItem = p_pmConceptsDepend(name, formatItem->data.md, transSpec, fpa, chip, cell);
+                if (!formatItem) {
+                    continue;
+                }
+            }
+            if (formatItem->type != PS_DATA_STRING) {
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for concept %s in TRANSLATION is not STR",
+                        name);
+                psFree(specsIter);
+                return false;
+            }
+            psString keywords = formatItem->data.str; // The FITS keywords
+
+            // In case there are multiple headers
+            psList *keys = psStringSplit(keywords, " ,;", true); // List of keywords
+            if (keys->n == 1) {
+                // Only one key --- proceed as usual
+                if (hduLow->header) {
+                    headerItem = psMetadataLookup(hduLow->header, keywords);
+                }
+                if (!headerItem && hduHigh != hduLow && hduHigh->header) {
+                    headerItem = psMetadataLookup(hduHigh->header, keywords);
+                }
+                psMemIncrRefCounter(headerItem);
+            } else {
+                psListIterator *keysIter = psListIteratorAlloc(keys, PS_LIST_HEAD, false); // Iterator
+                psString key = NULL; // Item from iteration
+                psList *values = psListAlloc(NULL); // List containing the values
+                while ((key = psListGetAndIncrement(keysIter))) {
+                    psMetadataItem *value = NULL;
+                    if (hduLow->header) {
+                        value = psMetadataLookup(hduLow->header, key);
+                    }
+                    if (!value && hduHigh != hduLow && hduHigh->header) {
+                        value = psMetadataLookup(hduHigh->header, key);
+                    }
+                    if (value) {
+                        psListAdd(values, PS_LIST_TAIL, value);
+                    } else {
+                        psWarning("Unable to find header %s --- assuming value is NULL", key);
+                    }
+                }
+                psFree(keysIter);
+                headerItem = psMetadataItemAlloc(name, PS_DATA_LIST, specItem->comment, values);
+                psFree(values);
+            }
+            psFree(keys);
+        }
+
+        // This will also clean up the name
+        if (headerItem) {
+            if (!conceptParse(spec, headerItem, PM_CONCEPT_SOURCE_HEADER,
+                              cameraFormat, target, fpa, chip, cell)) {
+                psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to parse concept %s from header.\n", name);
+                status = false;
+            }
+        }
+        psTrace("psModules.concepts", 10, "%s: %p\n", name, headerItem);
+        psFree(headerItem);
+    }
+    psFree(specsIter);
+    return status;
+}
+
+psMetadataItem *p_pmConceptsReadSingleFromDatabase(const char *name, const psMetadata *database,
+                                                   pmConfig *config, const pmFPA *fpa, const pmChip *chip,
+                                                   const pmCell *cell)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadataItem *item = psMetadataLookup(database, name); // Item to return
+    if (item && item->type == PS_DATA_METADATA) {
+        // This is a menu
+        psTrace("psModules.concepts", 5, "%s is of type METADATA.\n", name);
+        item = p_pmConceptsDepend(name, item->data.md, database, fpa, chip, cell);
+    }
+    if (!item) {
+        return NULL;
+    }
+    if (item->type != PS_DATA_STRING) {
+        psWarning("%s in DATABASE in camera format is not of type STR --- ignored.", name);
+        return NULL;
+    }
+
+#ifndef HAVE_PSDB
+    psError(PS_ERR_UNKNOWN, false,
+            "Cannot read concept: psModules was compiled without database support.");
+    return NULL;
+#else
+
+    psDB *db = pmConfigDB(config);      // Database handle
+    if (!db) {
+        psErrorClear();
+        psWarning("Unable to initialise database to write concepts.");
+        return NULL;
+    }
+
+    psString sql = pmConceptsInterpolate(item->data.str, fpa, chip, cell);
+    if (!p_psDBRunQuery(config->database, sql)) {
+        psWarning("Unable to query database for concept %s --- ignored.", name);
+        psFree(sql);
+        return NULL;
+    }
+    psFree(sql);
+
+    psArray *rows = p_psDBFetchResult(config->database); // Rows returned from the query
+    if (rows->n == 0) {
+        psWarning("No rows returned from database query for concept %s --- ignored.", name);
+        return NULL;
+    }
+    if (rows->n > 1) {
+        psWarning("Multiple rows returned from database query for concept %s --- using the first", name);
+    }
+    psMetadata *row = rows->data[0]; // First (and only) row
+    if (row->list->n > 1) {
+        psWarning("Multiple columns returned from database query for concept %s --- using the first", name);
+    }
+
+    return psMetadataGet(row, PS_LIST_HEAD); // Item of interest
+#endif // HAVE_PSDB
+}
+
+bool p_pmConceptsReadFromDatabase(psMetadata *target, const psMetadata *specs,
+                                  const pmFPA *fpa, const pmChip *chip, const pmCell *cell, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(specs, false);
+    PS_ASSERT_PTR_NON_NULL(target, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+#ifndef HAVE_PSDB
+    return true;
+#else
+    pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
+    if (!hdu) {
+        // We read the database for all the HDUs we could find
+        return true;
+    }
+    psMetadata *cameraFormat = hdu->format; // The camera format
+    bool mdok = true;                   // Status of MD lookup
+    psMetadata *dbSpec = psMetadataLookupMetadata(&mdok, cameraFormat, "DATABASE"); // The DATABASE spec
+    if (!mdok || !dbSpec) {
+        return true;
+    }
+
+    psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *specItem = NULL;    // Item from the specs metadata
+    bool status = true;                 // Status of reading concepts
+    while ((specItem = psMetadataGetAndIncrement(specsIter))) {
+        pmConceptSpec *spec = specItem->data.V; // The specification
+        psString name = specItem->name; // The concept name
+        psMetadataItem *conceptItem = p_pmConceptsReadSingleFromDatabase(name, dbSpec, config,
+                                                                         fpa, chip, cell);
+        if (conceptItem && !conceptParse(spec, conceptItem, PM_CONCEPT_SOURCE_DATABASE,
+                                         cameraFormat, target, fpa, chip, cell)) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to parse concept %s from database.\n", name);
+            status = false;
+        }
+    } // Iterating through the concept specifications
+    psFree(specsIter);
+
+    return status;
+#endif
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsRead.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsRead.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsRead.h	(revision 22322)
@@ -0,0 +1,82 @@
+/* @file  pmConceptsRead.h
+ * @brief Reading concepts from a variety of sources.
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-17 22:16:38 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONCEPTS_READ_H
+#define PM_CONCEPTS_READ_H
+
+/// Read concepts from the camera format file's CELLS.
+///
+/// Examines the CELLS metadata in the camera format file
+/// for the current type of cell, and sucks in the concepts defined there.
+/// This is a useful way of defining concepts that vary depending on the
+/// type of the cell.
+bool p_pmConceptsReadFromCells(psMetadata *target, ///< Place into which to read the concepts
+                               const psMetadata *specs, ///< The concept specifications
+                               const pmCell *cell ///< The cell
+                              );
+
+/// Read a single concept from the DEFAULTS in the camera format
+///
+/// The returned item is NOT parsed, but any interpolation for DEPEND is done.
+psMetadataItem *p_pmConceptsReadSingleFromDefaults(
+    const char *name,                   ///< Name of concept
+    const psMetadata *defaults,         ///< DEFAULTS specifications
+    const pmFPA *fpa,                   ///< The FPA
+    const pmChip *chip,                 ///< The chip, or NULL
+    const pmCell *cell                  ///< The cell, or NULL
+    );
+
+/// Read concepts from the DEFAULTS in the camera format file.
+///
+/// Examines the DEFAULTS metadata in the camera format file
+/// for concepts in the specs, and imports them into the target.
+bool p_pmConceptsReadFromDefaults(psMetadata *target, // Place into which to read the concepts
+                                  const psMetadata *specs, // The concept specifications
+                                  const pmFPA *fpa, // The FPA
+                                  const pmChip *chip, // The chip
+                                  const pmCell *cell // The cell
+                                 );
+
+/// Read concepts from the header TRANSLATION in the camera format file.
+///
+/// Examines the TRANSLATION metadata in the camera format file
+/// for concepts in the specs, and imports them into the target.
+bool p_pmConceptsReadFromHeader(psMetadata *target, // Place into which to read the concepts
+                                const psMetadata *specs, // The concept specifications
+                                const pmFPA *fpa, // The FPA
+                                const pmChip *chip, // The chip
+                                const pmCell *cell  // The cell
+                               );
+
+/// Read a single concept from the DATABASE specification in the camera format
+///
+/// The returned item is NOT parsed, but any interpolation for DEPEND is done.
+psMetadataItem *p_pmConceptsReadSingleFromDatabase(
+    const char *name,                   ///< Name of concept
+    const psMetadata *database,         ///< DATABASE specification
+    pmConfig *config,                   ///< Configuration
+    const pmFPA *fpa,                   ///< The FPA
+    const pmChip *chip,                 ///< The chip, or NULL
+    const pmCell *cell                  ///< The cell, or NULL
+    );
+
+/// Read concepts from the header DATABASE in the camera format file.
+///
+/// Examines the DATABASE metadata in the camera format file
+/// for concepts in the specs, and imports them into the target.
+bool p_pmConceptsReadFromDatabase(psMetadata *target, // Place into which to read the concepts
+                                  const psMetadata *specs, // The concept specifications
+                                  const pmFPA *fpa, // The FPA
+                                  const pmChip *chip, // The chip
+                                  const pmCell *cell,  // The cell
+                                  pmConfig *config // Configuration
+                                 );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsStandard.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsStandard.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsStandard.c	(revision 22322)
@@ -0,0 +1,1238 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <assert.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmConcepts.h"
+#include "pmConceptsRead.h"
+#include "pmConceptsWrite.h"
+#include "pmConceptsStandard.h"
+
+// The functions in this file are intended to be called solely within the psModules concepts code.  For this
+// reason, they use "assert" instead of the PS_ASSERT_WHATEVER functions --- if there's a problem, then
+// there's a BIG problem that affects all of the code.
+
+#define COMPARE_REGIONS(a,b) (((a)->x0 == (b)->x0 && \
+                               (a)->x1 == (b)->x1 && \
+                               (a)->y0 == (b)->y0 && \
+                               (a)->y1 == (b)->y1) ? true : false)
+
+#define TYPE_CASE(assign, item, TYPE) \
+case PS_TYPE_##TYPE: \
+assign = item->data.TYPE; \
+break;
+
+
+
+static double defaultCoordScaling(const psMetadataItem *pattern)
+{
+    if (strcmp(pattern->name, "FPA.RA") == 0 || strcmp(pattern->name, "FPA.LATITUDE") == 0) {
+        psWarning("Assuming format for %s is HOURS.\n", pattern->name);
+        return M_PI / 12.0;
+    }
+    if (strcmp(pattern->name, "FPA.DEC") == 0 || strcmp(pattern->name, "FPA.LONGITUDE") == 0) {
+        psWarning("Assuming format for %s is DEGREES.\n", pattern->name);
+        return M_PI / 180.0;
+    }
+    psAbort("Should never ever get here.\n");
+    return NAN;
+}
+
+// TELTEMPS : parse a list of the form 'X1 X2 X3 X4 X5 ...' : for now use median
+psMetadataItem *p_pmConceptParse_TELTEMPS(const psMetadataItem *concept,
+                                          const psMetadataItem *pattern,
+                                          pmConceptSource source,
+                                          const psMetadata *cameraFormat,
+                                          const pmFPA *fpa,
+                                          const pmChip *chip,
+                                          const pmCell *cell)
+{
+    assert(concept);
+    assert(pattern);
+
+    double value = NAN;
+    switch (concept->type) {
+      case PS_TYPE_F32:
+        value = concept->data.F32;
+        break;
+      case PS_TYPE_F64:
+        value = concept->data.F64;
+        break;
+      case PS_DATA_STRING: {
+          // parse the list of values into an array of substrings
+          psArray *strValues = psStringSplitArray (concept->data.V, " ,;", false);
+          assert (strValues);
+
+          // convert the substrings into a vector
+          psVector *fltValues = psVectorAlloc (strValues->n, PS_DATA_F32);
+          for (int i = 0; i < strValues->n; i++) {
+              fltValues->data.F32[i] = atof(strValues->data[i]);
+          }
+
+          // take the (for now) MEDIAN of the data
+          psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+          if (!psVectorStats (stats, fltValues, NULL, NULL, 0)) {
+              psAbort ("how can this stats function fail?");
+          }
+
+          value = stats->sampleMedian;
+          psFree (stats);
+          psFree (fltValues);
+          psFree (strValues);
+          break;
+      }
+
+      default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Invalid type for %s (%x)\n", pattern->name, concept->type);
+        return NULL;
+    }
+
+    psMetadataItem *item = psMetadataItemAllocF32(pattern->name, pattern->comment, value);
+    return (item);
+}
+
+// FPA.FILTER
+psMetadataItem *p_pmConceptParse_FPA_FILTER(const psMetadataItem *concept,
+                                            const psMetadataItem *pattern,
+                                            pmConceptSource source,
+                                            const psMetadata *cameraFormat,
+                                            const pmFPA *fpa,
+                                            const pmChip *chip,
+                                            const pmCell *cell)
+{
+    assert(concept);
+    assert(pattern);
+    assert(fpa);
+    assert(fpa->camera);
+
+    if (concept->type != PS_DATA_STRING) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not STR\n",
+                pattern->name, concept->type);
+        return NULL;
+    }
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *filters = psMetadataLookupMetadata(&mdok, fpa->camera, "FILTER.ID");
+    if (!mdok || !filters) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find FILTER.ID in camera configuration.\n");
+        return NULL;
+    }
+
+    // the metadata is in the format (internal) STR (external)
+    // do a reverse lookup to get the internal name
+    psMetadataIterator *iter = psMetadataIteratorAlloc(filters, PS_LIST_HEAD, NULL); // Iterator for filters
+    psMetadataItem *item;               // Item from iteration
+    char *name = NULL;                  // The winning name
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (item->type != PS_DATA_STRING) {
+            psWarning("Type for %s (%x) in FILTER.ID in camera configuration is not STR\n",
+                      item->name, item->type);
+            continue;
+        }
+        if (strcmp(item->data.str, concept->data.str) == 0) {
+            name = item->name;
+            break;
+        }
+    }
+    psFree(iter);
+    if (!name) {
+        psError(PS_ERR_UNEXPECTED_NULL, false,
+                "Unable to find any filter matching %s in FILTER.ID in camera configuration.\n",
+                concept->data.str);
+        return NULL;
+    }
+
+    return psMetadataItemAllocStr(pattern->name, pattern->comment, name);
+}
+
+psMetadataItem *p_pmConceptFormat_FPA_FILTER(const psMetadataItem *concept,
+                                             pmConceptSource source,
+                                             const psMetadata *cameraFormat,
+                                             const pmFPA *fpa,
+                                             const pmChip *chip,
+                                             const pmCell *cell)
+{
+    assert(concept);
+    assert(fpa);
+    assert(fpa->camera);
+
+    if (concept->type != PS_DATA_STRING) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not STR\n",
+                concept->name, concept->type);
+        return NULL;
+    }
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *filters = psMetadataLookupMetadata(&mdok, fpa->camera, "FILTER.ID");
+    if (!mdok || !filters) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find FILTER.ID in camera configuration.\n");
+        return NULL;
+    }
+
+    const char *key = concept->data.str;        // The name to look up
+    if (!key || strlen(key) == 0) {
+        return psMetadataItemAllocStr(concept->name, concept->comment, NULL);
+    }
+
+    // the metadata is in the format (internal) STR (external)
+    // find the first internal name that matches
+    psMetadataItem *item = psMetadataLookup (filters, key);
+    if (!item) {
+        psError(PS_ERR_UNEXPECTED_NULL, true,
+                "Unable to find %s in FILTER.ID in camera configuration.\n", key);
+        return NULL;
+    }
+
+    if (item->type == PS_DATA_STRING) {
+        return psMetadataItemAllocStr(concept->name, concept->comment, item->data.V);
+    }
+
+    if (item->type == PS_DATA_METADATA_MULTI) {
+        psMetadataItem *entry = psListGet (item->data.list, PS_LIST_HEAD);
+        if (!entry) {
+            psError(PS_ERR_UNEXPECTED_NULL, true,
+                    "List for %s in FILTER.ID in camera configuration is empty.\n", key);
+            return NULL;
+        }
+        return psMetadataItemAllocStr(concept->name, concept->comment, entry->data.V);
+    }
+
+    psError(PS_ERR_UNEXPECTED_NULL, true,
+            "Unable to find %s in FILTER.ID in camera configuration.\n", key);
+    return NULL;
+}
+
+// FPA.RA and FPA.DEC
+psMetadataItem *p_pmConceptParse_FPA_Coords(const psMetadataItem *concept,
+                                            const psMetadataItem *pattern,
+                                            pmConceptSource source,
+                                            const psMetadata *cameraFormat,
+                                            const pmFPA *fpa,
+                                            const pmChip *chip,
+                                            const pmCell *cell)
+{
+    assert(concept);
+    assert(pattern);
+    assert(cameraFormat);
+
+    double coords = NAN;                // The coordinates
+    switch (concept->type) {
+      case PS_TYPE_F32:
+        coords = concept->data.F32;
+        break;
+      case PS_TYPE_F64:
+        coords = concept->data.F64;
+        break;
+      case PS_DATA_STRING:
+        // Sexagesimal format
+        {
+            int big, medium;
+            float small;
+            // XXX: Upgrade path is to allow dd:mm.mmm
+            if (sscanf(concept->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
+                sscanf(concept->data.V, "%d %d %f", &big, &medium, &small) != 3)
+            {
+                psError(PS_ERR_UNKNOWN, true, "Cannot interpret %s: %s\n", pattern->name, concept->data.str);
+                break;
+            }
+            coords = abs(big) + (float)medium/60.0 + small/3600.0;
+            if (big < 0)
+            {
+                coords *= -1.0;
+            }
+        }
+        break;
+      default:
+        psError(PS_ERR_UNKNOWN, true, "%s concept is of an unexpected type: %x\n",
+                pattern->name, concept->type);
+        return NULL;
+    }
+
+    // How to interpret the coordinates
+    bool mdok = true;           // Status of MD lookup
+    psMetadata *formats = psMetadataLookupMetadata(&mdok, cameraFormat, "FORMATS");
+    if (mdok && formats) {
+        psString format = psMetadataLookupStr(&mdok, formats, pattern->name);
+        if (mdok && strlen(format) > 0) {
+            if (strcasecmp(format, "HOURS") == 0) {
+                coords *= M_PI / 12.0;
+            } else if (strcasecmp(format, "DEGREES") == 0) {
+                coords *= M_PI / 180.0;
+            } else if (strcasecmp(format, "RADIANS") == 0) {
+                // No action required
+            } else {
+                coords *= defaultCoordScaling(pattern);
+            }
+        } else {
+            coords *= defaultCoordScaling(pattern);
+        }
+    } else {
+        coords *= defaultCoordScaling(pattern);
+    }
+
+    return psMetadataItemAllocF64(pattern->name, pattern->comment, coords);
+}
+
+// FPA.RA and FPA.DEC
+psMetadataItem *p_pmConceptFormat_FPA_Coords(const psMetadataItem *concept,
+                                             pmConceptSource source,
+                                             const psMetadata *cameraFormat,
+                                             const pmFPA *fpa,
+                                             const pmChip *chip,
+                                             const pmCell *cell)
+{
+    assert(concept);
+    assert(cameraFormat);
+
+    double coords = concept->data.F64;  // The coordinates
+
+    if (!isfinite(coords)) {
+        return psMetadataItemAllocF32(concept->name, concept->comment, NAN);
+    }
+
+    // How to interpret the coordinates
+    bool mdok = true;                   // Status of MD lookup
+    psMetadata *formats = psMetadataLookupMetadata(&mdok,
+                                                   cameraFormat,
+                                                   "FORMATS");
+    if (mdok && formats) {
+        psString format = psMetadataLookupStr(&mdok,formats, concept->name);
+        if (mdok && strlen(format) > 0) {
+            if (strcasecmp(format, "HOURS") == 0) {
+                coords /= M_PI / 12.0;
+            } else if (strcasecmp(format, "DEGREES") == 0) {
+                coords /= M_PI / 180.0;
+            } else if (strcasecmp(format, "RADIANS") == 0) {
+                // No action required
+            } else {
+                coords /= defaultCoordScaling(concept);
+            }
+        } else {
+            coords /= defaultCoordScaling(concept);
+        }
+    } else {
+        coords /= defaultCoordScaling(concept);
+    }
+
+    // We choose to write sexagesimal format
+    int big, medium;                    // Degrees and minutes
+    float small;                        // Seconds
+    bool negative = (coords < 0);       // Are we working below zero?
+    coords = fabs(coords);
+    big = (int)abs(coords);
+    coords -= big;
+    medium = 60.0 * coords;
+    coords -= medium / 60.0;
+    small = 3600.0 * coords;
+    small = (float)((int)(small * 100.0)) / 100.0;
+    if (negative) {
+        big *= -1;
+    }
+    psString coordString = NULL;        // String with the coordinates in sexagesimal format
+    psStringAppend(&coordString, "%d:%02d:%05.2f", big, medium, small);
+    psMetadataItem *coordItem = psMetadataItemAllocStr(concept->name, concept->comment, coordString);
+    psFree(coordString);
+
+    return coordItem;
+}
+
+
+psMetadataItem *p_pmConceptParse_CELL_TRIMSEC(const psMetadataItem *concept,
+                                              const psMetadataItem *pattern,
+                                              pmConceptSource source,
+                                              const psMetadata *cameraFormat,
+                                              const pmFPA *fpa,
+                                              const pmChip *chip,
+                                              const pmCell *cell)
+{
+    assert(concept);
+    assert(cell);
+    assert(pattern);
+
+    int xParity = 0;
+    int yParity = 0;
+    psRegion *trimsec = psRegionAlloc(0, 0, 0, 0);
+
+    if (concept->type != PS_DATA_STRING) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.TRIMSEC after read is not of type STR (%x)\n", concept->type);
+        psFree(trimsec);
+        return NULL;
+    } else {
+        // allow for x and y flips in regions
+        *trimsec = psRegionAndParityFromString(&xParity, &yParity, concept->data.V);
+    }
+
+    // Need to correct for binning when CELL.TRIMSEC are specified immutably in the CELLS
+    if (source == PM_CONCEPT_SOURCE_CELLS) {
+        psMetadataAddBool(cell->concepts, PS_LIST_TAIL, "CELL.TRIMSEC.UPDATE", PS_META_REPLACE,
+                          "Need to update CELL.TRIMSEC when the binning is available.",
+                          true);
+    }
+
+    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_REGION, pattern->comment, trimsec);
+    psFree(trimsec);
+    return item;
+}
+
+
+psList *p_pmConceptParseRegions(const char *region)
+{
+    psList *list = psListAlloc(NULL);   // List of regions
+    if (!region || strlen(region) == 0) {
+        // Empty list
+        return list;
+    }
+
+    // a single BIASSEC is of the form [AAAA]
+    // we may have multiple BIASSEC entries separated by space, commas, or semicolons
+    int xParity = 0, yParity = 0;       // Parity of region
+    char *p = strchr (region, '[');
+    while (p) {
+        char *q = strchr (p, ']');
+        if (!q) {
+            break;
+        }
+        char *regionString = psStringAlloc(q - p + 2);
+        strncpy (regionString, p, q - p + 1);
+        regionString[q - p + 1] = 0;
+
+        psRegion *region = psAlloc(sizeof(psRegion)); // The region
+        *region = psRegionAndParityFromString(&xParity, &yParity, regionString);
+        psListAdd(list, PS_LIST_TAIL, region);
+        psFree(region);           // Drop reference
+        psFree(regionString);     // Drop reference
+
+        p = strchr (q, '[');
+    }
+
+    return list;
+}
+
+psMetadataItem *p_pmConceptParse_CELL_BIASSEC(const psMetadataItem *concept,
+                                              const psMetadataItem *pattern,
+                                              pmConceptSource source,
+                                              const psMetadata *cameraFormat,
+                                              const pmFPA *fpa,
+                                              const pmChip *chip,
+                                              const pmCell *cell)
+{
+    assert(concept);
+    assert(cell);
+    assert(pattern);
+
+    psList *biassecs = NULL; // List of bias sections
+
+    switch (concept->type) {
+      case PS_DATA_STRING: {
+          biassecs = p_pmConceptParseRegions(concept->data.V);
+          break;
+      }
+      case PS_DATA_LIST: {
+          biassecs = psListAlloc(NULL);
+          psList *regions = concept->data.V; // The list of regions
+          psListIterator *regionsIter = psListIteratorAlloc(regions, PS_LIST_HEAD, false); // Iterator
+          psMetadataItem *regionItem = NULL; // Item from list iteration
+          while ((regionItem = psListGetAndIncrement(regionsIter))) {
+              if (regionItem->type != PS_DATA_STRING) {
+                  psWarning("CELL.BIASSEC member is not of type STR --- ignored.\n");
+                  continue;
+              }
+              int xParity = 0;
+              int yParity = 0;
+              psRegion *region = psAlloc(sizeof(psRegion)); // The region
+              *region = psRegionAndParityFromString(&xParity, &yParity, regionItem->data.V);
+              psListAdd(biassecs, PS_LIST_TAIL, region);
+              psFree(region);           // Drop reference
+          }
+          psFree(regionsIter);
+          break;
+      }
+      default:
+        psError(PS_ERR_UNKNOWN, true, "CELL.BIASSEC after read is not of type STRING or LIST --- assuming "
+                "blank.\n");
+    }
+
+    // Need to correct for binning when CELL.BIASSEC are specified immutably in the CELLS
+    if (source == PM_CONCEPT_SOURCE_CELLS) {
+        psMetadataAddBool(cell->concepts, PS_LIST_TAIL, "CELL.BIASSEC.UPDATE", 0,
+                          "Need to update CELL.BIASSEC when the binning is available.",
+                          true);
+    }
+
+    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_LIST, pattern->comment, biassecs);
+    psFree(biassecs);               // Drop reference
+    return item;
+}
+
+// CELL.XBIN and CELL.YBIN
+psMetadataItem *p_pmConceptParse_CELL_Binning(const psMetadataItem *concept,
+                                              const psMetadataItem *pattern,
+                                              pmConceptSource source,
+                                              const psMetadata *cameraFormat,
+                                              const pmFPA *fpa,
+                                              const pmChip *chip,
+                                              const pmCell *cell)
+{
+    assert(concept);
+    assert(pattern);
+
+    int binning = 1;                    // Binning factor in x
+    switch (concept->type) {
+      case PS_DATA_STRING: {
+          psString binString = concept->data.V; // The string containing the binning
+          if ((strcmp(pattern->name, "CELL.XBIN") == 0 && sscanf(binString, "%d %*d", &binning) != 1 &&
+               sscanf(binString, "%d,%*d", &binning) != 1) ||
+              (strcmp(pattern->name, "CELL.YBIN") == 0 && sscanf(binString, "%*d %d", &binning) != 1 &&
+               sscanf(binString, "%*d,%d", &binning) != 1)) {
+              psError(PS_ERR_UNKNOWN, true, "Unable to parse string to get %s: %s\n", pattern->name, binString);
+          }
+          break;
+      }
+        TYPE_CASE(binning, concept, U8);
+        TYPE_CASE(binning, concept, U16);
+        TYPE_CASE(binning, concept, U32);
+        TYPE_CASE(binning, concept, S8);
+        TYPE_CASE(binning, concept, S16);
+        TYPE_CASE(binning, concept, S32);
+      default:
+        psError(PS_ERR_UNKNOWN, true, "Note sure how to parse %s of type %x --- assuming 1.\n", pattern->name,
+                concept->type);
+    }
+
+    return psMetadataItemAllocS32(pattern->name, pattern->comment, binning);
+}
+
+
+psMetadataItem *p_pmConceptParse_TIMESYS(const psMetadataItem *concept,
+                                         const psMetadataItem *pattern,
+                                         pmConceptSource source,
+                                         const psMetadata *cameraFormat,
+                                         const pmFPA *fpa,
+                                         const pmChip *chip,
+                                         const pmCell *cell)
+{
+    assert(concept);
+    assert(pattern);
+
+    psTimeType timeSys = PS_TIME_UTC;   // The time system
+    psString sys = concept->data.V;     // The time system string
+    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);
+    } else if (strcasecmp(sys, "TAI") == 0) {
+        timeSys = PS_TIME_TAI;
+    } else if (strcasecmp(sys, "UTC") == 0) {
+        timeSys = PS_TIME_UTC;
+    } else if (strcasecmp(sys, "UT1") == 0) {
+        timeSys = PS_TIME_UT1;
+    } else if (strcasecmp(sys, "TT") == 0) {
+        timeSys = PS_TIME_TT;
+    } else {
+        // XXX is this too low verbosity?
+        psLogMsg ("psModules.concepts", PS_LOG_DETAIL, "Can't interpret %s --- assuming UTC.\n", pattern->name);
+    }
+
+    return psMetadataItemAllocS32(pattern->name, pattern->comment, timeSys);
+}
+
+psMetadataItem *p_pmConceptParse_TIME(const psMetadataItem *concept,
+                                      const psMetadataItem *pattern,
+                                      pmConceptSource source,
+                                      const psMetadata *cameraFormat,
+                                      const pmFPA *fpa,
+                                      const pmChip *chip,
+                                      const pmCell *cell)
+{
+    assert(concept);
+    assert(cameraFormat);
+
+    // Need TIMESYS first
+    psString timesysName = psStringCopy(pattern->name); // e.g., "CELL.TIME" --> "CELL.TIMESYS"
+    psStringSubstitute(&timesysName, "TIMESYS", "TIME");
+    bool mdok = false;                  // Result of MD lookup
+    psTimeType timeSys = PS_TIME_UTC; // 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, pattern->name);
+        timeSys = PS_TIME_UTC;
+    }
+    psFree(timesysName);
+
+    // Work out how the time is represented
+    bool separateTime = false;
+    bool usaTime = false;               // Is the time specified in USA (MM-DD-YYYY) order?
+    bool backwardsTime = false;         // Is the time specified in backwards (DD-MM-YYYY) order?
+    bool yearFirst = false;            // Is the time specified in yearFirst (YYYY-MM-DD) order?
+    bool pre2000Time = false;           // Is the time specified pre-2000 (where the year only has two digits)
+    bool mjdTime = false;               // Is the time specified a MJD?
+    bool jdTime = false;                // Is the time specified a JD?
+
+    // Get format
+    psMetadata *formats = psMetadataLookupMetadata(&mdok, cameraFormat, "FORMATS");
+    if (!mdok || !formats) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find FORMATS in camera configuration.\n");
+        return NULL;
+    }
+
+    psString timeFormat = psMetadataLookupStr(&mdok, formats, pattern->name);
+    if (!mdok || strlen(timeFormat) == 0) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find %s in FORMATS.\n", pattern->name);
+        return NULL;
+    }
+
+    // Parse the time format
+    // why should more than one be allowed??
+    psList *timeFormats = psStringSplit(timeFormat, " ,;", false); // List of the format options
+    psListIterator *timeFormatsIter = psListIteratorAlloc(timeFormats, PS_LIST_HEAD, false); // Iter
+    while ((timeFormat = psListGetAndIncrement(timeFormatsIter))) {
+        if (strcasecmp(timeFormat, "SEPARATE") == 0) {
+            separateTime = true;
+        } else if (strcasecmp(timeFormat, "USA") == 0) {
+            usaTime = true;
+            backwardsTime = false;
+            yearFirst = false;
+            jdTime = false;
+            mjdTime = false;
+        } else if (strcasecmp(timeFormat, "BACKWARDS") == 0) {
+            backwardsTime = true;
+            usaTime = false;
+            yearFirst = false;
+            jdTime = false;
+            mjdTime = false;
+        } else if (strcasecmp(timeFormat, "YEAR.FIRST") == 0) {
+            yearFirst = true;
+            usaTime = false;
+            backwardsTime = false;
+            jdTime = false;
+            mjdTime = false;
+        } else if (strcasecmp(timeFormat, "PRE2000") == 0) {
+            pre2000Time = true;
+        } else if (strcasecmp(timeFormat, "MJD") == 0) {
+            mjdTime = true;
+            jdTime = false;
+            yearFirst = false;
+            backwardsTime = false;
+            usaTime = false;
+        } else if (strcasecmp(timeFormat, "JD") == 0) {
+            jdTime = true;
+            mjdTime = false;
+            yearFirst = false;
+            backwardsTime = false;
+            usaTime = false;
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "Unrecognised FORMATS option for %s: %s --- "
+                    "ignored.\n", pattern->name, timeFormat);
+            psFree(timeFormatsIter);
+            psFree(timeFormats);
+            return NULL;
+        }
+    }
+    psFree(timeFormatsIter);
+    psFree(timeFormats);
+
+    psTime *time = NULL;                // The time
+    switch (concept->type) {
+      case PS_DATA_LIST: {
+          if (!separateTime) {
+              psWarning ("DATE and TIME stored separately, but not specified in format\n");
+          }
+          // The date and time are stored separately
+          // Assume the date is first and the time second
+          psList *dateTime = concept->data.V; // The list containing items for date and time
+          if (psListLength(dateTime) != 2) {
+              psError(PS_ERR_BAD_PARAMETER_SIZE, false,
+                      "Unable to parse %s: date and time are not both available.", pattern->name);
+              return NULL;
+          }
+          psMetadataItem *dateItem = psListGet(dateTime, PS_LIST_HEAD); // Item containing the date
+          if (!dateItem) {
+              psError(PS_ERR_UNKNOWN, true, "Date is not found.\n");
+              return NULL;
+          }
+          if (dateItem->type != PS_DATA_STRING) {
+              psError(PS_ERR_UNKNOWN, true, "Date is not of type STR.\n");
+              return NULL;
+          }
+          psString dateString = dateItem->data.V; // The string with the date
+          int day = 0, month = 0, year = 0;
+          if (sscanf(dateString, "%d-%d-%d", &year, &month, &day) != 3 &&
+              sscanf(dateString, "%d/%d/%d", &year, &month, &day) != 3) {
+              psError(PS_ERR_UNKNOWN, true, "Unable to read date: %s\n", dateString);
+              return NULL;
+          }
+          if (backwardsTime) {
+              // Need to switch days and years
+              int temp = day;
+              day = year;
+              year = temp;
+          }
+          if (usaTime) {
+              // Need to switch everything around.... Yanks!
+              int temp = day;
+              day = month;
+              month = year;
+              year = temp;
+          }
+          if (year < 100) {
+              if (pre2000Time) {
+                  year += 1900;
+              } else {
+                  year += 2000;
+              }
+          }
+          sprintf(dateString,"%04d-%02d-%02d", year, month, day);
+
+          psMetadataItem *timeItem = psListGet(dateTime, PS_LIST_HEAD + 1); // Item containing the time
+          if (!timeItem) {
+              psError(PS_ERR_UNKNOWN, true, "Time is not found.\n");
+              return NULL;
+          }
+          psString timeString = NULL; // The string with the time
+          if (timeItem->type == PS_DATA_STRING) {
+              timeString = timeItem->data.V;
+          } else {
+              // Assume that time is specified in Second of Day (!)
+              double seconds = NAN;
+              switch (timeItem->type) {
+                  TYPE_CASE(seconds, timeItem, U8);
+                  TYPE_CASE(seconds, timeItem, U16);
+                  TYPE_CASE(seconds, timeItem, U32);
+                  TYPE_CASE(seconds, timeItem, S8);
+                  TYPE_CASE(seconds, timeItem, S16);
+                  TYPE_CASE(seconds, timeItem, S32);
+                  TYPE_CASE(seconds, timeItem, F32);
+                  TYPE_CASE(seconds, timeItem, F64);
+                default:
+                  psError(PS_ERR_UNKNOWN, true, "Time is not of an expected type: %x\n", timeItem->type);
+                  return NULL;
+              }
+              // Now print to timeString as "hh:mm:ss.ss"
+              int hours = seconds / 3600;
+              seconds -= (double)hours * 3600.0;
+              int minutes = seconds / 60;
+              seconds -= (double)minutes * 60.0;
+              psStringAppend(&timeString, "%02d:%02d:%02f", hours, minutes, seconds);
+          }
+          psString dateTimeString = NULL;
+          psStringAppend(&dateTimeString, "%sT%s", dateString, timeString);
+          time = psTimeFromISO(dateTimeString, timeSys);
+          psFree(dateTimeString);
+          break;
+      }
+      case PS_DATA_STRING: {
+          psString timeString = concept->data.V;   // String with the time
+          if (jdTime) {
+              double timeValue = strtod (timeString, NULL);
+              time = psTimeFromJD(timeValue);
+          } else if (mjdTime) {
+              double timeValue = strtod (timeString, NULL);
+              time = psTimeFromMJD(timeValue);
+          } else {
+              // It's ISO
+              time = psTimeFromISO(timeString, timeSys);
+          } // Interpreting the time string
+          break;
+      }
+      case PS_TYPE_F32: {
+          double timeValue = (double)concept->data.F32;
+          if (jdTime) {
+              time = psTimeFromJD(timeValue);
+          } else if (mjdTime) {
+              time = psTimeFromMJD(timeValue);
+          } else {
+              psError(PS_ERR_UNKNOWN, true, "Not sure how to parse %s (%f) --- trying JD\n",
+                      pattern->name, timeValue);
+              time = psTimeFromJD(timeValue);
+          }
+          break;
+      }
+      case PS_TYPE_F64: {
+          double timeValue = (double)concept->data.F64;
+          if (jdTime) {
+              time = psTimeFromJD(timeValue);
+          } else if (mjdTime) {
+              time = psTimeFromMJD(timeValue);
+          } else {
+              psError(PS_ERR_UNKNOWN, true, "Not sure how to parse %s (%f) --- trying JD\n",
+                      pattern->name, timeValue);
+              time = psTimeFromJD(timeValue);
+          }
+          break;
+      }
+      default:
+        psError(PS_ERR_UNKNOWN, true, "Unable to parse %s.\n", pattern->name);
+        return NULL;
+    }
+
+    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_TIME, pattern->comment, time);
+    psFree(time);                       // Drop reference
+    return item;
+}
+
+// Correct a position --- in case the user wants FORTRAN indexing (like the FITS standard...)
+static int fortranCorr(const psMetadata *cameraFormat, // The camera format description
+                       const char *name // Name of concept to check for FORTRAN indexing
+    )
+{
+    bool mdok = false;                  // Result of MD lookup
+    psMetadata *formats = psMetadataLookupMetadata(&mdok, cameraFormat, "FORMATS");
+    if (mdok && formats) {
+        psString format = psMetadataLookupStr(&mdok, formats, name);
+        if (mdok && strlen(format) > 0 && strcasecmp(format, "FORTRAN") == 0) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+psMetadataItem *p_pmConceptParse_Positions(const psMetadataItem *concept,
+                                           const psMetadataItem *pattern,
+                                           pmConceptSource source,
+                                           const psMetadata *cameraFormat,
+                                           const pmFPA *fpa,
+                                           const pmChip *chip,
+                                           const pmCell *cell)
+{
+    assert(concept);
+    assert(cameraFormat);
+
+    int offset = 0;                     // Offset of component (0,0) corner from the parent (0,0) corner
+
+    switch (concept->type) {
+        TYPE_CASE(offset, concept, U8);
+        TYPE_CASE(offset, concept, U16);
+        TYPE_CASE(offset, concept, U32);
+        TYPE_CASE(offset, concept, S8);
+        TYPE_CASE(offset, concept, S16);
+        TYPE_CASE(offset, concept, S32);
+#if 0
+
+      case PS_DATA_STRING: {
+          // Interpret as a region specifier [x0:x1,y0:y1]
+          int xParity = 0;
+          int yParity = 0;
+          psRegion region = psRegionAndParityFromString(&xParity, &yParity, concept->data.V);
+          if (strstr(pattern->name, ".X0")) {
+              offset = region.x0;
+          } else if (strstr(pattern->name, ".Y0")) {
+              offset = region.y0;
+          } else if (strstr(pattern->name, ".X1")) {
+              offset = region.x1;
+          } else if (strstr(pattern->name, ".Y1")) {
+              offset = region.y1;
+          } else {
+              psError(PS_ERR_UNKNOWN, true,
+                      "Unable to interpret %s because unable to determine if concept is X or Y.\n",
+                      pattern->name);
+              return NULL;
+          }
+          break;
+      }
+#endif
+      default:
+        if (concept->type == PS_TYPE_F32 && concept->data.F32 - (int)concept->data.F32 == 0) {
+            offset = concept->data.F32;
+        } else if (concept->type == PS_TYPE_F64 && concept->data.F64 - (int)concept->data.F64 == 0) {
+            offset = concept->data.F64;
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "Concept %s is not of integer type, as expected.\n", pattern->name);
+            return NULL;
+        }
+    }
+    offset -= fortranCorr(cameraFormat, pattern->name);
+    return psMetadataItemAllocS32(pattern->name, pattern->comment, offset);
+}
+
+
+psMetadataItem *p_pmConceptFormat_CELL_TRIMSEC(const psMetadataItem *concept,
+                                               pmConceptSource source,
+                                               const psMetadata *cameraFormat,
+                                               const pmFPA *fpa,
+                                               const pmChip *chip,
+                                               const pmCell *cell)
+{
+    assert(concept);
+
+    psRegion *trimsec = psMemIncrRefCounter(concept->data.V); // The trimsec region
+    if (!trimsec) {
+        return psMetadataItemAllocStr(concept->name, concept->comment, NULL);
+    }
+
+    // Correct trim section for binning if it's specified explicitly (i.e., immutably) in the CELLS.
+    if (source == PM_CONCEPT_SOURCE_CELLS) {
+        bool xStatus, yStatus;          // Status of MD lookups
+        int xBin = psMetadataLookupS32(&xStatus, cell->concepts, "CELL.XBIN");
+        int yBin = psMetadataLookupS32(&yStatus, cell->concepts, "CELL.YBIN");
+        if (!xStatus || !yStatus || xBin == 0 || yBin == 0) {
+            psWarning("Unable to find CELL.XBIN and CELL.YBIN to correct CELL.TRIMSEC.\n");
+            psFree(trimsec);            // Drop reference
+            return psMemIncrRefCounter((psPtr)concept); // Casting away "const" to increment
+        }
+        psRegion *newTrimsec = psRegionAlloc(trimsec->x0 * xBin, trimsec->x1 * xBin,
+                                             trimsec->y0 * yBin, trimsec->y1 * yBin); // Adjusted for binning
+        psFree(trimsec);
+        trimsec = newTrimsec;
+    }
+
+    psString trimsecString = psRegionToString(*trimsec);
+    psFree(trimsec);
+    psMetadataItem *formatted = psMetadataItemAllocStr(concept->name, concept->comment,
+                                                       trimsecString);
+    psFree(trimsecString);
+    return formatted;
+}
+
+psMetadataItem *p_pmConceptFormat_CELL_BIASSEC(const psMetadataItem *concept,
+                                               pmConceptSource source,
+                                               const psMetadata *cameraFormat,
+                                               const pmFPA *fpa,
+                                               const pmChip *chip,
+                                               const pmCell *cell)
+{
+    // Return a metadata item containing a list of metadata items of region strings
+    psList *biassecs = concept->data.V; // The biassecs region list
+    psList *new = psListAlloc(NULL);    // New list containing metadatas
+    if (biassecs) {
+        psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
+        psRegion *region = NULL;            // Region from iteration
+        while ((region = psListGetAndIncrement(biassecsIter))) {
+            // Correct bias section for binning if it's specified explicitly (i.e., immutably) in the CELLS.
+            if (source == PM_CONCEPT_SOURCE_CELLS) {
+                bool xStatus, yStatus;          // Status of MD lookups
+                int xBin = psMetadataLookupS32(&xStatus, cell->concepts, "CELL.XBIN");
+                int yBin = psMetadataLookupS32(&yStatus, cell->concepts, "CELL.YBIN");
+                if (!xStatus || !yStatus || xBin == 0 || yBin == 0) {
+                    psWarning("Unable to find CELL.XBIN and CELL.YBIN to correct CELL.BIASSEC.\n");
+                } else {
+                    psRegion *newTrimsec = psRegionAlloc(region->x0 * xBin, region->x1 * xBin,
+                                                         region->y0 * yBin, region->y1 * yBin);
+                    region = newTrimsec;
+                }
+            } else {
+                psMemIncrRefCounter(region);
+            }
+
+            psString regionString = psRegionToString(*region); // The string region "[x0:x1,y0:y1]"
+            psFree(region);
+            psMetadataItem *item = psMetadataItemAllocStr(concept->name, concept->comment, regionString);
+            psFree(regionString);           // Drop reference
+            psListAdd(new, PS_LIST_TAIL, item);
+            psFree(item);                   // Drop reference
+        }
+        psFree(biassecsIter);
+    }
+
+    psMetadataItem *formatted = psMetadataItemAllocPtr(concept->name, PS_DATA_LIST, concept->comment, new);
+    psFree(new);                        // Drop reference
+    return formatted;
+}
+
+// This function actually does both CELL.XBIN and CELL.YBIN if CELL.XBIN and CELL.YBIN are specified by the
+// same header.
+psMetadataItem *p_pmConceptFormat_CELL_XBIN(const psMetadataItem *concept,
+                                            pmConceptSource source,
+                                            const psMetadata *cameraFormat,
+                                            const pmFPA *fpa,
+                                            const pmChip *chip,
+                                            const pmCell *cell)
+{
+    assert(concept);
+
+    psMetadata *translation = psMetadataLookupMetadata(NULL, cameraFormat, "TRANSLATION");
+    bool xBinOK = true, yBinOK = true;  // Status of MD lookups
+    psString xKeyword = psMetadataLookupStr(&xBinOK, translation, "CELL.XBIN");
+    psString yKeyword = psMetadataLookupStr(&yBinOK, translation, "CELL.YBIN");
+    if (xBinOK && yBinOK && strlen(xKeyword) > 0 && strlen(yKeyword) > 0 &&
+        strcasecmp(xKeyword, yKeyword) == 0) {
+        psMetadataItem *yBinItem = psMetadataLookup(cell->concepts, "CELL.YBIN"); // Binning factor in y
+        psString binString = NULL;
+        psStringAppend(&binString, "%d %d", concept->data.S32, yBinItem->data.S32);
+        psMetadataItem *binItem = psMetadataItemAllocStr(concept->name, concept->comment, binString);
+        psFree(binString);
+        return binItem;
+    }
+
+    // Otherwise, there's no formatting required
+    return psMetadataItemCopy(concept);
+}
+
+// Only need to format if both if CELL.XBIN and CELL.YBIN are not specified by the same header.
+psMetadataItem *p_pmConceptFormat_CELL_YBIN(const psMetadataItem *concept,
+                                            pmConceptSource source,
+                                            const psMetadata *cameraFormat,
+                                            const pmFPA *fpa,
+                                            const pmChip *chip,
+                                            const pmCell *cell)
+{
+    assert(concept);
+
+    psMetadata *translation = psMetadataLookupMetadata(NULL, cameraFormat, "TRANSLATION");
+    bool xBinOK = true, yBinOK = true;  // Status of MD lookups
+    psString xKeyword = psMetadataLookupStr(&xBinOK, translation, "CELL.XBIN");
+    psString yKeyword = psMetadataLookupStr(&yBinOK, translation, "CELL.YBIN");
+    if (xBinOK && yBinOK && strlen(xKeyword) > 0 && strlen(yKeyword) > 0 &&
+        strcasecmp(xKeyword, yKeyword) == 0) {
+        // Censor this --- it's already done (though no harm if it's done twice
+        return NULL;
+    }
+
+    // No formatting required
+    return psMetadataItemCopy(concept);
+}
+
+
+psMetadataItem *p_pmConceptFormat_TIMESYS(const psMetadataItem *concept,
+                                          pmConceptSource source,
+                                          const psMetadata *cameraFormat,
+                                          const pmFPA *fpa,
+                                          const pmChip *chip,
+                                          const pmCell *cell)
+{
+    psString sys = NULL;            // String to store
+    switch (concept->data.S32) {
+      case PS_TIME_TAI:
+        sys = psStringCopy("TAI");
+        break;
+      case PS_TIME_UTC:
+        sys = psStringCopy("UTC");
+        break;
+      case PS_TIME_UT1:
+        sys = psStringCopy("UT1");
+        break;
+      case PS_TIME_TT:
+        sys = psStringCopy("TT");
+        break;
+      default:
+        sys = psStringCopy("Unknown");
+    }
+    psMetadataItem *newItem = psMetadataItemAllocStr(concept->name, concept->comment, sys);
+    psFree(sys);
+
+    return newItem;
+}
+
+psMetadataItem *p_pmConceptFormat_TIME(const psMetadataItem *concept,
+                                       pmConceptSource source,
+                                       const psMetadata *cameraFormat,
+                                       const pmFPA *fpa,
+                                       const pmChip *chip,
+                                       const pmCell *cell)
+{
+    psTime *time = concept->data.V;     // The time
+
+    // Work out the format
+    bool separateTime = false;          // Are the date and time stored separately?
+    bool pre2000Time = false;           // Is the year in pre-2000 format (two digits only)?
+    bool backwardsTime = false;         // Is the date stored backwards (DD-MM-YYYY)?
+    bool usaTime = false;               // Is the date stored in USA order (MM-DD-YYYY)?
+    bool jdTime = false;                // Is the date stored as a JD?
+    bool mjdTime = false;               // Is the date stored as a MJD?
+    bool yearFirst = false;
+
+    bool mdok = true;                   // Status of MD lookup
+    psMetadata *formats = psMetadataLookupMetadata(&mdok, cameraFormat, "FORMATS"); // The formats
+    if (mdok && formats) {
+        psString format = psMetadataLookupStr(&mdok, formats, concept->name); // The formats for eg, CELL.TIME
+        if (mdok && format && strlen(format) > 0) {
+            psList *formatList = psStringSplit(format, " ,;", false); // List of formats specified
+            psListIterator *formatListIter = psListIteratorAlloc(formatList, PS_LIST_HEAD, false); // Iterator
+            while ((format = psListGetAndIncrement(formatListIter))) {
+                if (strcasecmp(format, "SEPARATE") == 0) {
+                    separateTime = true;
+                } else if (strcasecmp(format, "USA") == 0) {
+                    usaTime = true;
+                    backwardsTime = false;
+                    jdTime = false;
+                    mjdTime = false;
+                } else if (strcasecmp(format, "BACKWARDS") == 0) {
+                    backwardsTime = true;
+                    usaTime = false;
+                    mjdTime = false;
+                    jdTime = false;
+                } else if (strcasecmp(format, "YEAR.FIRST") == 0) {
+                    yearFirst = true;
+                    usaTime = false;
+                    backwardsTime = false;
+                    jdTime = false;
+                    mjdTime = false;
+                } else if (strcasecmp(format, "PRE2000") == 0) {
+                    pre2000Time = true;
+                } else if (strcasecmp(format, "MJD") == 0) {
+                    mjdTime = true;
+                    usaTime = false;
+                    backwardsTime = false;
+                    jdTime = false;
+                    separateTime = false;
+                } else if (strcasecmp(format, "JD") == 0) {
+                    jdTime = true;
+                    usaTime = false;
+                    backwardsTime = false;
+                    mjdTime = false;
+                    separateTime = false;
+                } else {
+                    psWarning("Unrecognised FORMATS option for %s: %s --- "
+                              "ignored.\n", concept->name, format);
+                }
+            }
+            psFree(formatListIter);
+            psFree(formatList);
+        }
+    }
+
+    if (separateTime) {
+        // We're working with two separate headers --- construct a list with the date and time separately
+        psString dateTimeString = psTimeToISO(time); // String representation
+        psList *dateTime = psStringSplit(dateTimeString, "T", true);
+        psFree(dateTimeString);
+        psString dateString = psListGet(dateTime, PS_LIST_HEAD); // The date string
+        psString timeString = psListGet(dateTime, PS_LIST_TAIL); // The time string
+
+        // Need to format the strings....
+        // XXX: Couldn't be bothered doing these right now
+        if (pre2000Time) {
+            psError(PS_ERR_UNKNOWN, true, "Don't you realise it's the twenty-first century?\n");
+            return NULL;
+        }
+        if (backwardsTime) {
+            int day, month, year;
+            psTrace ("psModules.concepts", 5, "ISO time has year first, convert to DD-MM-YYYY");
+            sscanf (dateString, "%d-%d-%d", &year, &month, &day);
+            sprintf (dateString, "%02d-%02d-%04d", day, month, year);
+            // XXX fix this for str length
+        }
+        if (usaTime) {
+            int day, month, year;
+            psTrace ("psModules.concepts", 5, "ISO time has year first, convert to MM-DD-YYYY");
+            sscanf (dateString, "%d-%d-%d", &year, &month, &day);
+            sprintf (dateString, "%02d-%02d-%04d", month, day, year);
+            // XXX fix this for str length
+        }
+        if (yearFirst) {
+            psTrace ("psModules.concepts", 5, "ISO time has year first, no adjustment needed");
+        }
+
+        psMetadataItem *dateItem = psMetadataItemAllocStr(concept->name, "The date of observation",
+                                                          dateString);
+        psMetadataItem *timeItem = psMetadataItemAllocStr(concept->name, "The time of observation",
+                                                          timeString);
+
+        psListRemove(dateTime, PS_LIST_HEAD);
+        psListRemove(dateTime, PS_LIST_HEAD);
+
+        psListAdd(dateTime, PS_LIST_HEAD, dateItem);
+        psListAdd(dateTime, PS_LIST_TAIL, timeItem);
+
+        psMetadataItem *item = psMetadataItemAllocPtr(concept->name, PS_DATA_LIST, concept->comment,
+                                                      dateTime);
+        psFree (dateItem);
+        psFree (timeItem);
+        psFree (dateTime);
+        return item;
+    }
+    if (jdTime) {
+        double jd = psTimeToMJD(time);
+        return psMetadataItemAllocF64(concept->name, concept->comment, jd);
+    }
+    if (mjdTime) {
+        double mjd = psTimeToMJD(time);
+        return psMetadataItemAllocF64(concept->name, concept->comment, mjd);
+    }
+
+    // If we've gotten this far, so it's straight ISO.
+    psString dateTimeString = psTimeToISO(time); // String representation
+    psMetadataItem *item = psMetadataItemAllocStr(concept->name, concept->comment, dateTimeString);
+    psFree(dateTimeString);
+    return item;
+}
+
+
+psMetadataItem *p_pmConceptFormat_Positions(const psMetadataItem *concept,
+                                            pmConceptSource source,
+                                            const psMetadata *cameraFormat,
+                                            const pmFPA *fpa,
+                                            const pmChip *chip,
+                                            const pmCell *cell)
+{
+    assert(concept);
+    assert(cameraFormat);
+
+    if (concept->type != PS_TYPE_S32) {
+        psError(PS_ERR_UNKNOWN, true, "Concept %s is not of type S32, as expected.\n", concept->name);
+        return NULL;
+    }
+
+#if 0
+    // If both the X0 and Y0 positions are specified by the same header keyword, write both together, as part
+    // of the call for the X0 position.
+    // This is a bit of a kludge --- we're going to write it out as [x0:0,y0:0].  This means that we will be
+    // able to read it back in, but we've destroyed the x1 and y1 if it was present.
+    // We *could* attempt to read the header, parse the region, and only update the ones that we're trying
+    // to update.  Consider this an upgrade option later.
+    // Alternatively, we could add X1 and Y1 concepts, and write the whole lot out together.
+    // But until we care about X1 and Y1, it doesn't really matter --- if you want X1 and Y1, look at X0 and
+    // Y0 and add NXAIS1 and NAXIS2, respectively....
+    if (strstr(concept->name, ".X0")) {
+        psString companion = psStringCopy(concept->name); // Companion entry: ".Y" where this one has ".X"
+        psStringSubstitute(&companion, ".Y0", ".X0");
+
+        // Look both up in the camera format config
+        psMetadata *translation = psMetadataLookupMetadata(NULL, cameraFormat, "TRANSLATION");
+        bool xFound = true, yFound = true;  // Status of MD lookups
+        psString xKeyword = psMetadataLookupStr(&xFound, translation, concept->name);
+        psString yKeyword = psMetadataLookupStr(&yFound, translation, companion);
+        if (xFound && yFound && strlen(xKeyword) > 0 && strlen(yKeyword) > 0 &&
+            strcasecmp(xKeyword, yKeyword) == 0) {
+            psMetadataItem *yItem = psMetadataLookup(cell->concepts, companion); // Corresponding y value
+
+            int x = concept->data.S32 + fortranCorr(cameraFormat, concept->name); // x value
+            int y = yItem->data.S32 + fortranCorr(cameraFormat, companion); // y value
+
+            psRegion region = psRegionSet(x, x, y, y);
+            psString string = psRegionToString(region);
+            psMetadataItem *binItem = psMetadataItemAllocStr(concept->name, concept->comment, string);
+            psFree(string);
+            psFree(companion);
+            return binItem;
+        }
+        psFree(companion);
+    } else if (strstr(concept->name, ".Y0")) {
+        psString companion = psStringCopy(concept->name); // Companion entry: ".Y" where this one has ".X"
+        psStringSubstitute(&companion, ".X0", ".Y0");
+
+        // Look both up in the camera format config
+        psMetadata *translation = psMetadataLookupMetadata(NULL, cameraFormat, "TRANSLATION");
+        bool xFound = true, yFound = true;  // Status of MD lookups
+        psString xKeyword = psMetadataLookupStr(&xFound, translation, concept->name);
+        psString yKeyword = psMetadataLookupStr(&yFound, translation, companion);
+        psFree(companion);
+        if (xFound && yFound && strlen(xKeyword) > 0 && strlen(yKeyword) > 0 &&
+            strcasecmp(xKeyword, yKeyword) == 0) {
+            return NULL;                // We did it with the X; don't do anything.
+        }
+    }
+#endif
+
+    int offset = concept->data.S32;
+    offset += fortranCorr(cameraFormat, concept->name);
+    return psMetadataItemAllocS32(concept->name, concept->comment, offset);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsStandard.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsStandard.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsStandard.h	(revision 22322)
@@ -0,0 +1,191 @@
+/* @file  pmConceptsStandard.h
+ * @brief Private functions for parsing and formatting standard concepts.
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-12-08 03:15:37 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONCEPTS_STANDARD_H
+#define PM_CONCEPTS_STANDARD_H
+
+/// @addtogroup Concepts Data Abstraction Concepts
+/// @{
+
+/// Parse a list of region specifiers on a line
+psList *p_pmConceptParseRegions(const char *region ///< Regions, separated by whitespace
+    );
+
+// Parse the TELTEMPS concept : parse a list of the form 'X1 X2 X3 X4 X5 ...' : for now use median
+psMetadataItem *p_pmConceptParse_TELTEMPS(const psMetadataItem *concept,
+					  const psMetadataItem *pattern,
+					  pmConceptSource source,
+					  const psMetadata *cameraFormat,
+					  const pmFPA *fpa,
+					  const pmChip *chip,
+					  const pmCell *cell);
+
+/// Parse the FPA.FILTER concept to apply a lookup table
+psMetadataItem *p_pmConceptParse_FPA_FILTER(const psMetadataItem *concept, ///< Concept to parse
+        const psMetadataItem *pattern, ///< Pattern to use in parsing
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                           );
+
+/// Format the FPA.FILTER concept to (reverse-)apply a lookup table
+psMetadataItem *p_pmConceptFormat_FPA_FILTER(const psMetadataItem *concept, ///< Concept to format
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                            );
+
+/// Parse the coordinates concepts: FPA.RA and FPA.DEC
+psMetadataItem *p_pmConceptParse_FPA_Coords(const psMetadataItem *concept, ///< Concept to parse
+        const psMetadataItem *pattern, ///< Pattern to use in parsing
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                           );
+
+/// Format the coordinates concepts: FPA.RA and FPA.DEC
+psMetadataItem *p_pmConceptFormat_FPA_Coords(const psMetadataItem *concept, ///< Concept to format
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                            );
+
+/// Parse the CELL.TRIMSEC concept
+psMetadataItem *p_pmConceptParse_CELL_TRIMSEC(const psMetadataItem *concept, ///< Concept to parse
+        const psMetadataItem *pattern, ///< Pattern to use in parsing
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                             );
+
+/// Parse the CELL.BIASSEC concept
+psMetadataItem *p_pmConceptParse_CELL_BIASSEC(const psMetadataItem *concept, ///< Concept to parse
+        const psMetadataItem *pattern, ///< Pattern to use in parsing
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                             );
+
+/// Parse the cell binning concepts: CELL.XBIN, CELL.YBIN
+psMetadataItem *p_pmConceptParse_CELL_Binning(const psMetadataItem *concept, ///< Concept to parse
+        const psMetadataItem *pattern, ///< Pattern to use in parsing
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                             );
+
+/// Parse the time system concepts: FPA.TIMESYS and CELL.TIMESYS
+psMetadataItem *p_pmConceptParse_TIMESYS(const psMetadataItem *concept, ///< Concept to parse
+        const psMetadataItem *pattern, ///< Pattern to use in parsing
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                        );
+
+/// Parse the time concepts: FPA.TIME and CELL.TIME
+psMetadataItem *p_pmConceptParse_TIME(const psMetadataItem *concept, ///< Concept to parse
+                                      const psMetadataItem *pattern, ///< Pattern to use in parsing
+                                            pmConceptSource source, ///< Source for concept
+                                      const psMetadata *cameraFormat, ///< Camera format definition
+                                      const pmFPA *fpa, ///< FPA for concept, or NULL
+                                      const pmChip *chip, ///< Chip for concept, or NULL
+                                      const pmCell *cell ///< Cell for concept, or NULL
+                                     );
+
+/// Parse a cell position concept, e.g., CELL.X0
+psMetadataItem *p_pmConceptParse_Positions(const psMetadataItem *concept, ///< Concept to parse
+        const psMetadataItem *pattern, ///< Pattern to use in parsing
+                                            pmConceptSource source, ///< Source for concept
+       const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                          );
+
+/// Format the CELL.TRIMSEC concept
+psMetadataItem *p_pmConceptFormat_CELL_TRIMSEC(const psMetadataItem *concept, ///< Concept to format
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                              );
+
+/// Format the CELL.BIASSEC concept
+psMetadataItem *p_pmConceptFormat_CELL_BIASSEC(const psMetadataItem *concept, ///< Concept to format
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                              );
+
+/// Format the CELL.XBIN concept
+psMetadataItem *p_pmConceptFormat_CELL_XBIN(const psMetadataItem *concept, ///< Concept to format
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                           );
+
+/// Format the CELL.YBIN concept
+psMetadataItem *p_pmConceptFormat_CELL_YBIN(const psMetadataItem *concept, ///< Concept to format
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                           );
+
+/// Format the time system concepts: FPA.TIMESYS and CELL.TIMESYS
+psMetadataItem *p_pmConceptFormat_TIMESYS(const psMetadataItem *concept, ///< Concept to format
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                         );
+
+/// Format the time concepts: FPA.TIME and CELL.TIME
+psMetadataItem *p_pmConceptFormat_TIME(const psMetadataItem *concept, ///< Concept to format
+                                            pmConceptSource source, ///< Source for concept
+                                       const psMetadata *cameraFormat, ///< Camera format definition
+                                       const pmFPA *fpa, ///< FPA for concept, or NULL
+                                       const pmChip *chip, ///< Chip for concept, or NULL
+                                       const pmCell *cell ///< Cell for concept, or NULL
+                                      );
+
+/// Format a cell position concept, e.g., CELL.X0
+psMetadataItem *p_pmConceptFormat_Positions(const psMetadataItem *concept, ///< Concept to format
+                                            pmConceptSource source, ///< Source for concept
+        const psMetadata *cameraFormat, ///< Camera format definition
+        const pmFPA *fpa, ///< FPA for concept, or NULL
+        const pmChip *chip, ///< Chip for concept, or NULL
+        const pmCell *cell ///< Cell for concept, or NULL
+                                           );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsUpdate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsUpdate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsUpdate.c	(revision 22322)
@@ -0,0 +1,90 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmConceptsUpdate.h"
+
+bool pmConceptsUpdate(const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    if (fpa) {
+        // Check for FPA concepts updates
+    }
+
+    if (chip) {
+        // Check for chip concepts updates
+    }
+
+    if (cell) {
+        // Check for cell concepts updates
+
+	bool xStatus, yStatus; // Status of MD lookups
+	psImageBinning *binning = psImageBinningAlloc();
+	binning->nXbin = psMetadataLookupS32(&xStatus, cell->concepts, "CELL.XBIN");
+	binning->nYbin = psMetadataLookupS32(&yStatus, cell->concepts, "CELL.YBIN");
+	if (!xStatus || !yStatus) {
+	    // XXX should this be an error condition?
+	    psFree (binning);
+	    return true;
+	}
+	if (!binning->nXbin || !binning->nXbin) {
+	    // XXX should this be an error condition?
+	    psFree (binning);
+	    return true;
+	}
+
+        // CELL.TRIMSEC needs to be updated for the binning
+        if (psMetadataLookup(cell->concepts, "CELL.TRIMSEC.UPDATE")) {
+	    psRegion *trimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC"); // Trim section
+	    *trimsec = psImageBinningSetRuffRegion (binning, *trimsec);
+	    // force integer pixels : truncate x0, roundup x1:
+	    trimsec->x0 = (int)trimsec->x0;
+	    if (trimsec->x1 > (int)trimsec->x1) {
+		trimsec->x1 = (int)trimsec->x1 + 1;
+	    } else {
+		trimsec->x1 = (int)trimsec->x1;
+	    }		
+	    trimsec->y0 = (int)trimsec->y0;
+	    if (trimsec->y1 > (int)trimsec->y1) {
+		trimsec->y1 = (int)trimsec->y1 + 1;
+	    } else {
+		trimsec->y1 = (int)trimsec->y1;
+	    }		
+	    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC.UPDATE");
+        }
+
+        // CELL.BIASSEC needs to be updated for the binning
+        if (psMetadataLookup(cell->concepts, "CELL.BIASSEC.UPDATE")) {
+	    psList *biassecs = psMetadataLookupPtr(NULL, cell->concepts, "CELL.BIASSEC"); // Bias sections
+	    psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, true); // Iterator
+	    psRegion *biassec; // Bias region, from iteration
+	    while ((biassec = psListGetAndIncrement(biassecsIter))) {
+		*biassec = psImageBinningSetRuffRegion (binning, *biassec);
+		// force integer pixels : truncate x0, roundup x1:
+		biassec->x0 = (int)biassec->x0;
+		if (biassec->x1 > (int)biassec->x1) {
+		    biassec->x1 = (int)biassec->x1 + 1;
+		} else {
+		    biassec->x1 = (int)biassec->x1;
+		}		
+		biassec->y0 = (int)biassec->y0;
+		if (biassec->y1 > (int)biassec->y1) {
+		    biassec->y1 = (int)biassec->y1 + 1;
+		} else {
+		    biassec->y1 = (int)biassec->y1;
+		}		
+	    }
+	    psFree(biassecsIter);
+	    psMetadataRemoveKey(cell->concepts, "CELL.BIASSEC.UPDATE");
+        }
+	psFree (binning);
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsUpdate.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsUpdate.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsUpdate.h	(revision 22322)
@@ -0,0 +1,25 @@
+/* @file  pmConceptsUpdate.h
+ * @brief Function to update concepts.
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-30 21:12:56 $
+ * Copyright 2005-2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONCEPTS_UPDATE_H
+#define PM_CONCEPTS_UPDATE_H
+
+/// Check for concepts to update.
+///
+/// Updating concepts is necessary if one concept depends on the value of another.  In that case, a flag
+/// (e.g., CONCEPTNAME.UPDATE" in the concepts) can be set, and it can be updated once the required value is
+/// known.
+bool pmConceptsUpdate(const pmFPA *fpa,       ///< FPA for which to update concepts
+                      const pmChip *chip,     ///< Chip for which to update concepts
+                      const pmCell *cell      ///< Cell for which to update concepts
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsWrite.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsWrite.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsWrite.c	(revision 22322)
@@ -0,0 +1,487 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmHDUUtils.h"
+#include "pmConcepts.h"
+#include "pmConceptsRead.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static bool compareConcepts(const psMetadataItem *compare, // Item to compare
+                            const psMetadataItem *standard // Standard for comparison
+                           )
+{
+    // First order checks
+    if (! compare || ! standard) {
+        return false;
+    }
+    if (strcasecmp(compare->name, standard->name) != 0) {
+        return false;
+    }
+
+    // Special case: list
+    if (compare->type == PS_DATA_LIST) {
+        // "compare" contains a list of psMetadataItems
+        // "standard" likely contains just a string (but it might possibly be a list of strings)
+        psList *cList = compare->data.V; // The list from comparison item
+        psList *sList = NULL;         // The list from standard item
+        switch (standard->type) {
+        case PS_DATA_STRING:
+            sList = psStringSplit(standard->data.V, " ;", true);
+            break;
+        case PS_DATA_LIST:
+            sList = psMemIncrRefCounter(standard->data.V);
+            break;
+        default:
+            return false;
+        }
+        if (cList->n != sList->n) {
+            psFree(sList);
+            return false;
+        }
+        psVector *match = psVectorAlloc(cList->n, PS_TYPE_U8); // Array indicating which values match
+        psVectorInit(match, 0);
+        psListIterator *cIter = psListIteratorAlloc(cList, PS_LIST_HEAD, false); // compare iterator
+        psListIterator *sIter = psListIteratorAlloc(sList, PS_LIST_HEAD, false); // standard iterator
+        psMetadataItem *cItem = NULL; // Item from compare list
+        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);
+                psFree(cIter);
+                psFree(sIter);
+                psFree(match);
+                psFree(sList);
+                return false;
+            }
+            psString cString = cItem->data.V; // String from compare list
+            psListIteratorSet(sIter, PS_LIST_HEAD);
+            int index = 0;            // Index for list
+            bool found = false;       // Found a match?
+            for (psString sString = NULL; (sString = psListGetAndIncrement(sIter)) && !found; index++) {
+                if (strcasecmp(cString, sString) == 0) {
+                    match->data.U8[index]++;
+                    found = true;
+                }
+            }
+            if (! found) {
+                // Can give up immediately
+                psFree(cIter);
+                psFree(sIter);
+                psFree(match);
+                psFree(sList);
+                return false;
+            }
+        }
+        // Make sure we got 100% matches in both directions
+        bool allMatch = true;         // Did all of them match?
+        for (int i = 0; i < match->n && allMatch; i++) {
+            if (!match->data.U8[i]) {
+                allMatch = false;
+            }
+        }
+        psFree(cIter);
+        psFree(sIter);
+        psFree(sList);
+        psFree(match);
+        return allMatch;
+    }
+
+    return psMetadataItemCompare(compare, standard);
+
+}
+
+
+// Format a single concept
+static psMetadataItem *conceptFormat(const pmConceptSpec *spec, // The concept specification
+                                     const psMetadataItem *concept, // The concept to parse
+                                     pmConceptSource source, // The concept source
+                                     const psMetadata *cameraFormat, // The camera format
+                                     const pmFPA *fpa, // The FPA
+                                     const pmChip *chip, // The chip
+                                     const pmCell *cell // The cell
+                                    )
+{
+    assert(spec);
+    assert(cameraFormat);
+
+    if (concept) {
+        psMetadataItem *formatted = NULL;  // The formatted concept
+        if (spec->format) {
+            formatted = spec->format(concept, source, cameraFormat, fpa, chip, cell);
+        } else if (strcmp(concept->name, spec->blank->name) != 0) {
+            // Adjust so that the name is correct
+            formatted = psMetadataItemCopy(concept);
+            psFree(formatted->name);
+            formatted->name = psStringCopy(spec->blank->name);
+        } else {
+            // Can get away with merely incrementing the reference counter
+            formatted = psMemIncrRefCounter((const psPtr)concept);
+        }
+
+        return formatted;
+    }
+    return NULL;
+}
+
+// Write a single value to a header
+static bool writeSingleHeader(pmHDU *hdu, // HDU for which to add to the header
+                              const char *keyword, // Keyword to add
+                              const psMetadataItem *item // Item to add to the header; may be NULL
+                             )
+{
+    assert(hdu);
+    assert(keyword && strlen(keyword) > 0);
+
+    if (!hdu->header) {
+        hdu->header = psMetadataAlloc();
+    }
+    if (!item) {
+        psTrace("psModules.concepts", 9, "Writing header %s: <<<BLANK>>>\n", keyword);
+        // Assume it's a NULL string: it's most easily parsed.
+        return psMetadataAddStr(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, NULL, NULL);
+    }
+    switch (item->type) {
+    case PS_DATA_STRING:
+        psTrace("psModules.concepts", 9, "Writing header %s: %s\n", keyword, item->data.str);
+        return psMetadataAddStr(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
+                                item->data.V);
+    case PS_DATA_S32:
+        psTrace("psModules.concepts", 9, "Writing header %s: %d\n", keyword, item->data.S32);
+        return psMetadataAddS32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
+                                item->data.S32);
+    case PS_DATA_F32:
+        psTrace("psModules.concepts", 9, "Writing header %s: %f\n", keyword, item->data.F32);
+        return psMetadataAddF32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
+                                item->data.F32);
+    case PS_DATA_F64:
+        psTrace("psModules.concepts", 9, "Writing header %s: %f\n", keyword, item->data.F64);
+        return psMetadataAddF64(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
+                                item->data.F64);
+    case PS_DATA_REGION: {
+            psString region = psRegionToString(*(psRegion*)item->data.V);
+            psTrace("psModules.concepts", 9, "Writing header %s: %s\n", keyword, region);
+            bool result = psMetadataAddStr(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
+                                           region);
+            psFree(region);
+            return result;
+        }
+    default:
+        psWarning("Type of %s is not suitable for a FITS header --- not added.\n",
+                 item->name);
+        return false;
+    }
+}
+
+
+// Write potentially multiple values to a header
+static bool writeHeader(pmHDU *hdu,     // HDU for which to add to the header
+                        const char *keywords, // Keywords to add
+                        const psMetadataItem *item // Item to add to the header
+                       )
+{
+    assert(hdu);
+    assert(keywords);
+    assert(item);
+
+    bool status = true;                 // Status of writing headers, to be returned
+    if (item->type == PS_DATA_LIST) {
+        psList *values = item->data.V;  // List of outputs
+        psList *keys = psStringSplit(keywords, " ,;", true); // List of keywords
+        if (keys->n != values->n && values->n != 0) {
+            psError(PS_ERR_UNKNOWN, true, "Number of keywords (%ld) does not match number of "
+                    "values (%ld).\n", keys->n, values->n);
+            psFree(keys);
+            return false;
+        }
+        psListIterator *keysIter = psListIteratorAlloc(keys, PS_LIST_HEAD, false); // Iterator for keywords
+        psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false); // Iterator for values
+        psString key = NULL;            // Keyword from iteration
+        while ((key = psListGetAndIncrement(keysIter))) {
+            psMetadataItem *value = psListGetAndIncrement(valuesIter); // Value from iteration; may be NULL
+            status &= writeSingleHeader(hdu, key, value);
+        }
+        psFree(keysIter);
+        psFree(valuesIter);
+        psFree(keys);
+    } else {
+        status = writeSingleHeader(hdu, keywords, item);
+    }
+    return status;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+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);
+    if (!cell) {
+        return false;
+    }
+    if (!cell->config) {
+        return false;
+    }
+
+    pmHDU *hdu = pmHDUGetLowest(NULL, NULL, cell); // The HDU at the lowest level
+    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);
+    return true;
+}
+
+bool p_pmConceptsWriteToDefaults(const psMetadata *specs, const pmFPA *fpa, const pmChip *chip,
+                                 const pmCell *cell, 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);
+    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);
+    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
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsWrite.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsWrite.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/concepts/pmConceptsWrite.h	(revision 22322)
@@ -0,0 +1,61 @@
+/* @file  pmConceptsWrite.h
+ * @brief Writing concepts to a variety of sources.
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-17 22:16:38 $
+ * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONCEPTS_WRITE_H
+#define PM_CONCEPTS_WRITE_H
+
+/// @addtogroup Concepts Data Abstraction Concepts
+/// @{
+
+/// "Write" concepts 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
+/// not match.
+bool p_pmConceptsWriteToCells(const psMetadata *specs, ///< The concept specifications
+                              const pmCell *cell, ///< The cell
+                              const psMetadata *concepts ///< The concepts
+                             );
+
+/// "Write" concepts to (actually, check against) the camera format file's DEFAULTS.
+///
+/// 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
+                                );
+
+/// "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
+                                );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/.cvsignore	(revision 22322)
@@ -0,0 +1,8 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
+pmErrorCodes.c
+pmErrorCodes.h
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/Makefile.am	(revision 22322)
@@ -0,0 +1,35 @@
+noinst_LTLIBRARIES = libpsmodulesconfig.la
+
+libpsmodulesconfig_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS)
+libpsmodulesconfig_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulesconfig_la_SOURCES  = \
+	pmConfig.c \
+	pmConfigRecipes.c \
+	pmConfigCamera.c \
+	pmConfigCommand.c \
+	pmConfigMask.c \
+	pmConfigDump.c \
+	pmVersion.c \
+	pmErrorCodes.c
+
+pkginclude_HEADERS = \
+	pmConfig.h \
+	pmConfigRecipes.h \
+	pmConfigCamera.h \
+	pmConfigCommand.h \
+	pmConfigMask.h \
+	pmConfigDump.h \
+	pmVersion.h \
+	pmErrorCodes.h
+
+# Error codes.
+BUILT_SOURCES = pmErrorCodes.h pmErrorCodes.c
+CLEANFILES = *~ pmErrorCodes.h pmErrorCodes.c
+
+pmErrorCodes.h : pmErrorCodes.dat pmErrorCodes.h.in
+	$(ERRORCODES) --data=pmErrorCodes.dat --outdir=. pmErrorCodes.h
+
+pmErrorCodes.c : pmErrorCodes.dat pmErrorCodes.c.in pmErrorCodes.h
+	$(ERRORCODES) --data=pmErrorCodes.dat --outdir=. pmErrorCodes.c
+
+EXTRA_DIST = pmErrorCodes.h.in pmErrorCodes.dat pmErrorCodes.c.in
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/notes.txt	(revision 22322)
@@ -0,0 +1,56 @@
+
+* current recipe load sequence:
+
+** load the basic config information.
+   (unless camera is explicitly specified, camera-specific recipes are not loaded)
+
+** save the generic command-line options (-D ..) on config->arguments:OPTIONS:(recipe)
+
+** supplement recipe with specific command-line options (-isdark...) to config->arguments:OPTIONS:(recipe)
+
+** SYSTEM
+** load all files from config->complete:RECIPES to config->recipes
+   (loadRecipeSystem)
+
+** CAMERA
+** load the recipes for the specified camera from the camera-specific
+   config file to config->recipes (overlay existing metadata)
+
+** CL:arguments
+** load recipe files defined on command line in the form -recipe-file (recipe) (filename)
+   from config->arguments:RECIPES
+   
+** CL:symbols
+** load symbolic recipes defined on command line in the form -recipe (recipe) (symbolic name)
+   from config->recipeSymbols
+
+** CL:options
+** load values defined on command line in the form -D key value
+   from config->arguments:OPTIONS:(recipe)
+
+---
+
+what we really want:
+
+** SYSTEM
+** load all files from config->complete:RECIPES to config->recipes
+   (loadRecipeSystem)
+
+** CAMERA
+** load the recipes for the specified camera from the camera-specific
+   config file to config->recipesCamera
+
+** CL:arguments
+** load recipe files defined on command line in the form -recipe-file (recipe) (filename)
+   from config->arguments:RECIPES
+   
+** CL:symbols
+** load symbolic recipes defined on command line in the form -recipe (recipe) (symbolic name)
+   from config->recipeSymbols
+
+** CL:options
+** load values defined on command line in the form -D key value
+   from config->arguments:OPTIONS:(recipe)
+
+metadata = psMetadataConfigRead (recipes/NAME.config)
+metadata = 
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfig.c	(revision 22322)
@@ -0,0 +1,1751 @@
+/** @file  pmConfig.h
+ *
+ *  @author PAP (IfA)
+ *  @author EAM (IfA)
+ *
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <unistd.h>
+#include <libgen.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmErrorCodes.h"
+#include "pmFPALevel.h"
+#include "pmConfigRecipes.h"
+#include "pmConfigCamera.h"
+
+#ifdef HAVE_NEBCLIENT
+#include <nebclient.h>
+#endif // ifdef HAVE_NEBCLIENT
+
+#define IPPRC_ENV "IPPRC"        // Name of the environment variable containing the top-level config file
+#define IPPRC_FILE ".ipprc"      // Default top-level config file
+
+#define DEFAULT_LOG STDERR_FILENO       // Default file descriptor for log messages
+#define DEFAULT_TRACE STDERR_FILENO     // Default file descriptor for trace messages
+
+static bool readCameraConfig = true;    // Read the camera config on startup (with pmConfigRead)?
+static psArray *configPath = NULL;      // Search path for configuration files
+
+static bool checkPath(const char *filename, bool create, bool trunc);
+
+bool pmConfigReadParamsSet(bool newReadCameraConfig)
+{
+    bool oldReadCameraConfig = readCameraConfig;
+    readCameraConfig = newReadCameraConfig;
+    return oldReadCameraConfig;
+}
+
+static void configFree(pmConfig *config)
+{
+    psFree(config->user);
+    psFree(config->site);
+    psFree(config->system);
+    psFree(config->files);
+    psFree(config->camera);
+    psFree(config->cameraName);
+    psFree(config->format);
+    psFree(config->formatName);
+    psFree(config->recipes);
+    psFree(config->recipesCamera);
+    psFree(config->recipeSymbols);
+    psFree(config->arguments);
+    psFree(config->database);
+    psFree(config->program);
+
+    // Close log and trace files
+    if (config->logFD != STDOUT_FILENO && config->logFD != STDERR_FILENO) {
+        close(config->logFD);
+    }
+    if (config->traceFD != STDOUT_FILENO && config->traceFD != STDERR_FILENO) {
+        close(config->traceFD);
+    }
+
+    return;
+}
+
+// Check the end of a string for a word; return the length of the string without the ending word
+// Used to identify the camera base name (e.g., "MEGACAM" out of "_MEGACAM-CHIP")
+int checkEndForWord(const char *line,   // String to check for ending word
+                    const char *word    // Ending word to check for
+                    )
+{
+    int wlen = strlen(word);            // Length of word
+    int nlen = strlen(line);            // Length of line
+    if (nlen < wlen) {
+        return 0;
+    }
+
+    char *ptr = (char *)line + nlen - wlen; // Expected position of ending word
+    if (strcasecmp(ptr, word)) {
+        return 0;
+    }
+
+    return (nlen - wlen);
+}
+
+// the camera name is of the form: BASE, BASE_CHIP, or BASE_FPA.  pull out BASE:
+char *cameraBaseName(const char *name   // Name of meta-camera
+                     )
+{
+    char *answer;
+
+    int N = checkEndForWord (name, "-CHIP");
+    if (N && name[0] == '_') {
+        psString answer = psStringNCopy(name + 1, N - 1);
+        return answer;
+    }
+
+    N = checkEndForWord(name, "-FPA");
+    if (N && name[0] == '_') {
+        psString answer = psStringNCopy(name + 1, N - 1);
+        return answer;
+    }
+
+    N = checkEndForWord(name, "-SKYCELL");
+    if (N && name[0] == '_') {
+        psString answer = psStringNCopy(name + 1, N - 1);
+        return answer;
+    }
+
+    answer = psStringCopy(name);
+    return answer;
+}
+
+
+pmConfig *pmConfigAlloc()
+{
+    pmConfig *config = psAlloc(sizeof(pmConfig));
+    (void)psMemSetDeallocator(config, (psFreeFunc)configFree);
+
+    // Initialise
+    config->user = NULL;
+    config->site = NULL;
+    config->system = NULL;
+    config->camera = NULL;
+    config->cameraName = NULL;
+    config->format = NULL;
+    config->formatName = NULL;
+    config->recipes = psMetadataAlloc();
+    config->recipesRead = PM_RECIPE_SOURCE_NONE;
+    config->recipesCamera = psMetadataAlloc();
+    config->recipeSymbols = psMetadataAlloc();
+    config->arguments = psMetadataAlloc();
+    config->database = NULL;
+    config->defaultRecipe = NULL;
+    config->program = NULL;
+
+    config->traceFD = DEFAULT_TRACE;
+    config->logFD = DEFAULT_LOG;
+
+    // the file structure is used to carry pmFPAfiles
+    config->files = psMetadataAlloc ();
+    return config;
+}
+
+// Resolve environment variables within a directory name; returns the resolved directory string.
+// The returned string is likely a new pointer; the old pointer should be freed by psStringSubstitute.
+static psString resolveEnvVar(psString dir // Directory to check for environment variables
+                             )
+{
+    char *envStart;                     // Start of any environment variable
+    while ((envStart = strchr(dir, '$'))) {
+        char *envName = envStart + 1;   // Start of the environment variable name
+        if (envName[0] == '\0') {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Path %s contains a bad environment variable.\n", dir);
+            return NULL;
+        }
+        if (envName[0] == '{') {
+            envName++;
+            if (envName[0] == '\0') {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                        "Path %s contains a bad environment variable.\n", dir);
+                return NULL;
+            }
+        }
+        char *envStop = strpbrk(envStart, "}/"); // End of the environment variable
+        ssize_t nameLength = envStop ? envStop - envName : strlen(envName); // Length of the name
+        psString name = psStringNCopy(envName, nameLength); // The environment variable name
+        char *value = getenv(name);     // Value of the environment variable
+        psFree(name);
+        psString valueSlash = NULL;    // Value with appended slash
+        psStringAppend(&valueSlash, "%s/", value);
+
+        ssize_t envvarLength = envStop ? envStop - envStart : strlen(envStart); // Length, w/o '}'
+        psString envvar = psStringNCopy(envStart, envvarLength + 1);  // Environment variable, with $, {, }
+
+        psTrace("psModules.config", 7, "Replacing %s with %s in directory %s\n", envvar, valueSlash, dir);
+        psStringSubstitute(&dir, valueSlash, envvar);
+        psFree(envvar);
+        psFree(valueSlash);
+    }
+
+    return dir;
+}
+
+
+void pmConfigSet(const char *path)
+{
+    PS_ASSERT_STRING_NON_EMPTY(path,);
+
+    assert (configPath == NULL);
+    // XXX why was this being called?  pmConfigSet should only be called once...
+    // pmConfigDone();
+
+    psList *list = psStringSplit(path, ":", false);
+    configPath = psListToArray(list);
+    // Resolve environment variables
+    for (long i = 0; i < configPath->n; i++) {
+        configPath->data[i] = resolveEnvVar(configPath->data[i]);
+        psTrace("psModules.config", 4, "Path %ld: %s\n", i, (char*)configPath->data[i]);
+    }
+    psFree(list);
+}
+
+void pmConfigDone(void)
+{
+    if (configPath) {
+        psFree(configPath);
+    }
+    configPath = NULL;
+
+    return;
+}
+
+bool pmConfigFileRead(psMetadata **config, const char *name, const char *description)
+{
+    assert(config);
+    assert(name);
+    assert(description);
+
+    char *realName = NULL;
+    unsigned int numBadLines = 0;
+    struct stat filestat;
+
+    psTrace("psModules.config", 3, "Loading %s configuration from file %s\n",
+            description, name);
+
+    uid_t uid = getuid();
+    gid_t gid = getgid();
+
+    // we try: name, path[0]/name, path[1]/name, ...
+    // find the first existing entry in the path (starting with the bare name)
+    realName = psStringCopy (name);
+    psTrace ("psModules.config", 8, "trying %s\n", realName);
+
+    int status = stat (realName, &filestat);
+    if (status == 0) {
+        if ((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR)) {
+            goto found;
+        }
+        if ((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP)) {
+            goto found;
+        }
+        if (filestat.st_mode & S_IROTH) {
+            goto found;
+        }
+    }
+    psFree (realName);
+
+    if (configPath == NULL) {
+        psError(PS_ERR_IO, true, "Cannot find %s configuration file (%s) in path\n", description, name);
+        return false;
+    }
+
+    for (int i = 0; i < configPath->n; i++) {
+        realName = psStringCopy (configPath->data[i]);
+        psStringAppend (&realName, "/%s", name);
+        psTrace ("psModules.config", 8, "trying %s\n", realName);
+
+        status = stat (realName, &filestat);
+        if (status == 0) {
+            if ((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR)) {
+                goto found;
+            }
+            if ((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP)) {
+                goto found;
+            }
+            if (filestat.st_mode & S_IROTH) {
+                goto found;
+            }
+        }
+        psFree (realName);
+    }
+
+    psError(PS_ERR_IO, true, "Cannot find %s configuration file %s in path\n", description, name);
+    return false;
+
+found:
+    *config = psMetadataConfigRead(NULL, &numBadLines, realName, true);
+    if (numBadLines > 0) {
+        psError(PS_ERR_IO, false, "%d bad lines in %s configuration file (%s)",
+                numBadLines, description, realName);
+        psFree (realName);
+
+        return false;
+    }
+    if (!*config) {
+        psError(PS_ERR_IO, true, "Unable to read %s configuration from %s",
+                description, realName);
+        psFree (realName);
+        return false;
+    }
+
+    psFree (realName);
+    return true;
+}
+
+bool pmConfigFileIngest(psMetadataItem *item, const char *description)
+{
+    PS_ASSERT_METADATA_ITEM_NON_NULL(item, false);
+    PS_ASSERT_STRING_NON_EMPTY(description, false);
+
+    if (item->type == PS_DATA_METADATA) {
+        return true;                    // We've already read it
+    }
+    if (item->type != PS_DATA_STRING) {
+        psTrace("config", 2, "Element %s in %s metadata is not of type STR.\n",
+                item->name, description);
+        return false;
+    }
+
+    psTrace("config", 2, "Reading %s %s: %s\n", description, item->name, item->data.str);
+    psMetadata *new = NULL;         // New metadata
+    if (!pmConfigFileRead(&new, item->data.str, item->name)) {
+        psError(PM_ERR_CONFIG, false, "Trouble reading reading %s %s.\n",
+                description, item->name);
+        psFree(new);
+        return false;
+    }
+
+    // Muck around under the hood to replace the filename with the metadata; don't try this at home, kids
+    item->type = PS_DATA_METADATA;
+    psFree(item->data.str);
+    item->data.md = new;
+
+    return true;
+}
+
+// Read metadata config files in a metadata
+// The metadata contains file names, which will be replaced with the metadata that are in the files.
+static bool metadataReadFiles(psMetadata *source, // Source metadata
+                              const char *description // Description, for error messages
+                             )
+{
+    assert(source);
+    psMetadataIterator *iter = psMetadataIteratorAlloc(source, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (!pmConfigFileIngest(item, description)) {
+            psError(PM_ERR_CONFIG, false, "Unable to read %s %s.", description, item->name);
+            psFree(iter);
+            return false;
+        }
+    }
+    psFree(iter);
+
+    return true;
+}
+
+// Read the formats for a camera
+static bool cameraReadFormats(psMetadata *camera, // Camera for which to read the formats
+                              const char *name // Name of the camera, for error messages
+                             )
+{
+    assert(camera);
+    assert(name);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *formats = psMetadataLookupMetadata(&mdok, camera, "FORMATS"); // Formats
+    if (!mdok || !formats) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find FORMATS in camera configuration %s.\n", name);
+        return false;
+    }
+    if (!metadataReadFiles(formats, "camera format")) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read formats within camera configuration %s.\n", name);
+        return false;
+    }
+
+    return true;
+}
+
+// Read the calibrations for a camera
+static bool cameraReadCalibrations(psMetadata *camera, // Camera for which to read the formats
+                                   const char *cameraName // Name of the camera, for error messages
+    )
+{
+    assert(camera);
+    assert(cameraName);
+
+    psMetadataItem *darkNorm = psMetadataLookup(camera, "DARK.NORM"); // The dark normalisation calibration
+    if (darkNorm) {
+        if (!pmConfigFileIngest(darkNorm, "dark normalisation")) {
+            psWarning("Unable to ingest DARK.NORM in camera %s", cameraName);
+        }
+    } else {
+        // Add a dummy entry
+        psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1); // Dummy polynomial
+        poly->coeff[0] = 0.0;
+        poly->coeff[1] = 1.0;
+        psMetadata *polyMD = psMetadataAlloc(); // Container for the polynomial
+        (void)psPolynomial1DtoMetadata(polyMD, poly, "_DEFAULT"); // Metadata to insert
+        psFree(poly);
+        psMetadataAddMetadata(camera, PS_LIST_TAIL, "DARK.NORM", 0, "Dark normalisation polynomial",
+                              polyMD);
+        psMetadataAddStr(camera, PS_LIST_TAIL, "DARK.NORM.KEY", 0, "Key for dark normalisation", "_DEFAULT");
+        psFree(polyMD);
+    }
+
+    return true;
+}
+
+pmConfig *pmConfigRead(int *argc, char **argv, const char *defaultRecipe)
+{
+    PS_ASSERT_PTR_NON_NULL(argc, NULL);
+    PS_ASSERT_INT_POSITIVE(*argc, NULL);
+    PS_ASSERT_PTR_NON_NULL(argv, NULL);
+
+    pmConfig *config = pmConfigAlloc(); // The configuration, containing site, camera and recipes
+    config->program = psStringCopy(argv[0]);
+    config->defaultRecipe = defaultRecipe;
+
+    // The following section of code attempts to determine which file to use as the
+    // top-level the configuration file.  At the end of this code block, the configFile
+    // variable will contain the name of the configuration file.
+
+    char *configFile = NULL;
+    //
+    // First, try command line
+    //
+    psS32 argNum = psArgumentGet(*argc, argv, "-ipprc");
+    if (argNum != 0) {
+        // remove the "-ipprc" argument from argv, check and remove filename
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psWarning("-ipprc command-line switch provided without the required filename --- ignored.\n");
+        } else {
+            configFile = psStringCopy(argv[argNum]);
+            psArgumentRemove(argNum, argc, argv);
+        }
+    }
+    //
+    // Next, try environment variable
+    //
+    if (!configFile) {
+        configFile = getenv(IPPRC_ENV);
+        if (configFile) {
+            configFile = psStringCopy (configFile);
+        }
+    }
+
+    //
+    // Last chance is ~/.ipprc
+    //
+    if (!configFile) {
+        char *home = getenv("HOME");
+        configFile = psStringCopy(home);
+        psStringAppend(&configFile, "/%s", IPPRC_FILE);
+    }
+
+    // We have the configuration filename; now we read and parse the config
+    // file and store in psMetadata struct user.
+    // XXX move this section to pmConfigReadUser.c ?
+
+    if (!pmConfigFileRead(&config->user, configFile, "user")) {
+        psFree(config);
+        psFree(configFile);
+        return NULL;
+    }
+    psFree(configFile);
+
+    // XXX why was this being called here?  Is someone calling pmConfigRead multiple times?
+    // pmConfigDone();
+    assert (configPath == NULL);
+
+    // define the config-file search path (configPath).
+    psString path = psMetadataLookupStr(NULL, config->user, "PATH");
+    pmConfigSet (path);
+
+    // read the SITE file
+    psMetadataItem *siteItem = psMetadataLookup(config->user, "SITE");
+    if (!siteItem) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find SITE in user configuration.");
+        psFree(config);
+        return NULL;
+    }
+    if (!pmConfigFileIngest(siteItem, "site configuration")) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read site configuration");
+        psFree(config);
+        return NULL;
+    }
+    config->site = psMemIncrRefCounter(siteItem->data.md);
+
+    // load the SYSTEM file
+    psMetadataItem *systemItem = psMetadataLookup(config->user, "SYSTEM");
+    if (!systemItem) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find SYSTEM in user configuration.");
+        psFree(config);
+        return NULL;
+    }
+    if (!pmConfigFileIngest(systemItem, "system configuration")) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read system configuration");
+        psFree(config);
+        return NULL;
+    }
+    config->system = psMemIncrRefCounter(systemItem->data.md);
+
+    // Set LOG and TRACE options based on the user configuration.  These must be set AFTER
+    // the SITE and SYSTEM config files are read so path:// entries here can be resolved.
+    {
+        bool mdok = true;   // Status of MD lookup result
+
+        // Set logging level
+        int logLevel = psMetadataLookupS32(&mdok, config->user, "LOGLEVEL");
+        if (mdok && logLevel >= 0)
+        {
+            psTrace("psModules.config", 7, "Setting log level to %d\n", logLevel);
+            psLogSetLevel(logLevel);
+        }
+
+
+        // Set logging format
+        psString logFormat = psMetadataLookupStr(&mdok, config->user, "LOGFORMAT");
+        if (mdok && logFormat)
+        {
+            psTrace("psModules.config", 7, "Setting log format to %s\n", logFormat);
+            psLogSetFormat(logFormat);
+        }
+
+        // Set logging destination first from command line, second from user configuration
+        psString logDest = NULL;        // Logging destination
+        argNum = psArgumentGet(*argc, argv, "-log");
+        if (argNum > 0) {
+            psArgumentRemove(argNum, argc, argv);
+            if (argNum >= *argc) {
+                psWarning("-log command-line switch provided without the required log destination "
+                          "--- ignored.");
+            } else {
+                logDest = psStringCopy(argv[argNum]);
+                psArgumentRemove(argNum, argc, argv);
+            }
+        }
+        if (!logDest) {
+            logDest = psMemIncrRefCounter(psMetadataLookupStr(&mdok, config->user, "LOGDEST"));
+        }
+        if (logDest) {
+            psString resolved = pmConfigConvertFilename(logDest, config, true, false); // Resolved filename
+            if (!resolved || strlen(resolved) == 0) {
+                psWarning("Unable to resolve log destination: %s --- ignored", logDest);
+            } else {
+                config->logFD = psMessageDestination(resolved);
+            }
+            psFree(resolved);
+            psFree(logDest);
+        }
+        if (!psLogSetDestination(config->logFD)) {
+            psError(PS_ERR_IO, false, "Unable to set log destination to file number %d --- ignored",
+                    config->logFD);
+            psFree(config);
+            return NULL;
+        }
+
+        // Set trace levels
+        psMetadata *trace = psMetadataLookupMetadata(&mdok, config->user, "TRACE");
+        if (mdok && trace) {
+            psMetadataIterator *traceIter = psMetadataIteratorAlloc(trace, PS_LIST_HEAD, NULL); // Iterator
+            psMetadataItem *traceItem = NULL; // Item from MD iteration
+            while ((traceItem = psMetadataGetAndIncrement(traceIter))) {
+                if (traceItem->type != PS_DATA_S32) {
+                    psWarning("The level for trace component %s is not of type S32 (%x)\n",
+                             traceItem->name, traceItem->type);
+                    continue;
+                }
+                psTrace("psModules.config", 7, "Setting trace level for %s to %d\n",
+                        traceItem->name, traceItem->data.S32);
+                (void)psTraceSetLevel(traceItem->name, traceItem->data.S32);
+            }
+            psFree(traceIter);
+        }
+
+        // Set trace formats
+        psString traceFormat = psMetadataLookupStr(&mdok, config->user, "TRACEFORMAT");
+        if (mdok && traceFormat) {
+            psTrace("psModules.config", 7, "Setting trace format to %s\n", traceFormat);
+            (void)psTraceSetFormat(traceFormat);
+        }
+
+        // Set trace destinations
+        psString traceDest = NULL;      // Trace destination
+        argNum = psArgumentGet(*argc, argv, "-tracedest");
+        if (argNum > 0) {
+            psArgumentRemove(argNum, argc, argv);
+            if (argNum >= *argc) {
+                psWarning("-tracedest command-line switch provided without the required trace destination "
+                          "--- ignored.\n");
+            } else {
+                traceDest = psStringCopy(argv[argNum]);
+                psArgumentRemove(argNum, argc, argv);
+            }
+        }
+        if (!traceDest) {
+            traceDest = psMemIncrRefCounter(psMetadataLookupStr(&mdok, config->user, "TRACEDEST"));
+        }
+        if (traceDest) {
+            psString resolved = pmConfigConvertFilename(traceDest, config, true, false); // Resolved filename
+            if (!resolved || strlen(resolved) == 0) {
+                psWarning("Unable to resolve trace destination: %s --- ignored", traceDest);
+            } else {
+                config->traceFD = psMessageDestination(resolved);
+            }
+            psFree(resolved);
+            psFree(traceDest);
+        }
+        if (!psTraceSetDestination(config->traceFD)) {
+            psError(PS_ERR_IO, false, "Unable to set trace destination to file number %d --- ignored",
+                    config->traceFD);
+            psFree(config);
+            return NULL;
+        }
+
+        // Allow command line options to override defaults for logging.
+        // XXX: Is it appropriate to use the ArgVerbosity function for this?
+        //   A: it removes the options from the command line.
+        //   B: will the pmConfigRead function always be called on initialization.
+        //
+        psArgumentVerbosity(argc, argv);
+        // XXX: substitute the string for the default log level for "2".
+    }
+
+    // XXX read TIME from SITE (or system?)
+    {
+        bool mdok = true;
+
+        // Initialise the psLib time handling
+        // XXX is this still needed / desired?
+        psString timeName = psMetadataLookupStr(&mdok, config->system, "TIME");
+        if (mdok && timeName) {
+            psTrace("psModules.config", 7, "Initialising psTime with file %s\n", timeName);
+            psTimeInit(timeName);
+        }
+    }
+
+    // Next, we do a similar thing for the camera configuration file.  The
+    // file is read and parsed into psMetadata struct "camera".
+    argNum = psArgumentGet(*argc, argv, "-camera");
+    if (argNum > 0) {
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psWarning("-camera command-line switch provided without the required camera or filename --- "
+                      "ignored.\n");
+        } else {
+            bool mdok = true;           // Status of MD lookup
+            char *cameraName = argv[argNum]; // symbolic name of the camera
+
+            // look for the CAMERAS list in config->system
+            psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->system, "CAMERAS");
+            if (!cameras) {
+                psError(PS_ERR_IO, false, "Unable to find CAMERAS in site configuration.\n");
+                psFree(config);
+                return NULL;
+            }
+
+            // look for the symbolic camera name in the CAMERAS metadata
+            char *cameraFile = psMetadataLookupStr(&mdok, cameras, cameraName); // The filename
+            if (!cameraFile) {
+                psError(PS_ERR_IO, false, "%s is not listed in the site CAMERAS list\n", cameraName);
+                psFree(config);
+                return NULL;
+            }
+
+            // load this camera's configuration informatoin
+            if (!pmConfigFileRead(&config->camera, cameraFile, "camera")) {
+                psError(PM_ERR_CONFIG, false, "Problem reading %s", cameraName);
+                psFree(config);
+                return NULL;
+            }
+            // save the name for future uses
+            config->cameraName = psStringCopy (cameraName);
+
+            psArgumentRemove(argNum, argc, argv);
+
+            // Read in the formats
+            if (!cameraReadFormats(config->camera, cameraFile)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to read formats within camera configuration %s.\n",
+                        cameraFile);
+                psFree(config);
+                return NULL;
+            }
+
+            // Read in any camera-specific calibrations
+            if (!cameraReadCalibrations(config->camera, cameraName)) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "Unable to read calibrations within camera configuration %s.\n",
+                        cameraName);
+                psFree(config);
+                return NULL;
+            }
+
+            psMetadataAddMetadata(cameras, PS_LIST_HEAD, cameraName, PS_META_REPLACE,
+                                  "Camera specified on command line", config->camera);
+
+            if (!pmConfigCameraSkycellVersion(config->system, cameraName)) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "Unable to generate skycell versions of specified camera %s.\n",
+                        cameraName);
+                psFree(config);
+                return NULL;
+            }
+
+            if (!pmConfigCameraMosaickedVersions(config->system, cameraName)) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "Unable to generate mosaicked versions of specified camera %s.\n",
+                        cameraName);
+                psFree(config);
+                return NULL;
+            }
+        }
+    }
+
+    // Read the camera configurations, if not already defined, and not turned off
+    if (!config->camera && readCameraConfig) {
+        bool mdok;                      // Status of MD lookup
+        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->system, "CAMERAS"); // List of cameras
+        if (!mdok || !cameras) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the system configuration.\n");
+            return false;
+        }
+
+        if (!metadataReadFiles(cameras, "camera configuration")) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read cameras within system configuration.\n");
+            psFree(config);
+            return NULL;
+        }
+
+        // Now fill in the formats and calibrations
+        psMetadataIterator *iter = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *item;           // Item from iteration
+        while ((item = psMetadataGetAndIncrement(iter))) {
+            assert(item->type == PS_DATA_METADATA);
+            if (!cameraReadFormats(item->data.md, item->name)) {
+                psWarning("Unable to read formats for camera %s: removed.\n", item->name);
+                psErrorStackPrint(stderr, "errors from read failure\n");
+                psErrorClear();
+                psMetadataRemoveKey(cameras, item->name);
+                continue;
+            }
+            if (!cameraReadCalibrations(item->data.md, item->name)) {
+                psWarning("Unable to read calibrations for camera %s: removed.\n", item->name);
+                psErrorStackPrint(stderr, "errors from read failure\n");
+                psErrorClear();
+                psMetadataRemoveKey(cameras, item->name);
+                continue;
+            }
+        }
+        psFree(iter);
+
+        if (!pmConfigCameraSkycellVersionsAll(config->system)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate skycell versions of cameras.\n");
+            psFree(config);
+            return NULL;
+        }
+        if (!pmConfigCameraMosaickedVersionsAll(config->system)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate mosaicked versions of cameras.\n");
+            psFree(config);
+            return NULL;
+        }
+    }
+
+    // Load the recipes from the camera file, if appropriate
+    if(!pmConfigReadRecipes(config, PM_RECIPE_SOURCE_SYSTEM | PM_RECIPE_SOURCE_CAMERA)) {
+        psError(PS_ERR_IO, false, "Failed to read recipes from camera file");
+        psFree(config);
+        return NULL;
+    }
+
+    // load command-line options of the form -recipe NAME RECIPE
+    pmConfigLoadRecipeArguments(argc, argv, config);
+
+    // read in command-line options to specific recipe values
+    pmConfigLoadRecipeOptions(argc, argv, config, "-D");
+    pmConfigLoadRecipeOptions(argc, argv, config, "-Di");
+    pmConfigLoadRecipeOptions(argc, argv, config, "-Df");
+    pmConfigLoadRecipeOptions(argc, argv, config, "-Db");
+
+    // Look for command-line options for files to replace
+    while ((argNum = psArgumentGet(*argc, argv, "-F")) > 0) {
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum + 1 >= *argc) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Filerule switch (-F) provided without old and new filerule.");
+            psFree(config);
+            return NULL;
+        }
+
+        const char *old = argv[argNum]; // The old file, to be replaced
+        psArgumentRemove(argNum, argc, argv);
+        const char *new = argv[argNum]; // The new file, the replacement
+        psArgumentRemove(argNum, argc, argv);
+
+        psMetadata *cameras = psMetadataLookupMetadata(NULL, config->system, "CAMERAS"); // List of cameras
+        if (!cameras) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find CAMERAS in the site configuration.\n");
+            return false;
+        }
+
+        psMetadataIterator *camerasIter = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *cameraItem;     // Item from iteration
+        while ((cameraItem = psMetadataGetAndIncrement(camerasIter))) {
+            // Silently ignore problems --- they will be caught later, because if the user wants the nominated
+            // file and it's not available for that camera, then they will know.
+
+            if (cameraItem->type != PS_DATA_METADATA) {
+                psTrace("psModules.config", 2,
+                        "Entry %s in CAMERAS is not of type METADATA --- ignored.", cameraItem->name);
+                continue;
+            }
+            psMetadata *camera = cameraItem->data.md; // Camera configuration
+
+            psMetadata *newRule = pmConfigFileRule(config, camera, new); // The rule of interest
+            if (!newRule) {
+                psTrace("psModules.config", 2,
+                        "Unable to find filerule %s in camera %s --- ignored.", new, cameraItem->name);
+                continue;
+            }
+
+            // By calling pmConfigFileRule, we've assured that the FILERULES is now a metadata
+            psMetadata *filerules = psMetadataLookupMetadata(NULL, camera, "FILERULES"); // File rules
+            if (!filerules) {
+                psTrace("psModules.config", 2,
+                        "Can't find FILERULES of type METADATA in camera %s --- ignored.", cameraItem->name);
+                continue;
+            }
+
+            psMetadataAddMetadata(filerules, PS_LIST_TAIL, old, PS_META_REPLACE,
+                                  "Original replaced by -F option", newRule);
+        }
+        psFree(camerasIter);
+    }
+
+    // check for values that override DB* keywords
+    argNum = psArgumentGet(*argc, argv, "-dbserver");
+    if (argNum > 0) {
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psWarning("-dbserver command-line switch provided without the required server name --- ");
+        } else {
+            char *dbserver = argv[argNum]; // The camera configuration file to read
+            if (!psMetadataAddStr(config->user, PS_LIST_TAIL, "DBSERVER", PS_META_REPLACE,
+                                  NULL, dbserver)) {
+                psWarning("Failed to overwrite .ipprc DBSERVER value");
+            }
+
+            psArgumentRemove(argNum, argc, argv);
+        }
+    }
+
+    argNum = psArgumentGet(*argc, argv, "-dbname");
+    if (argNum > 0) {
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psWarning("-dbname command-line switch provided without the required database name");
+        } else {
+            char *dbname = argv[argNum]; // The camera configuration file to read
+            if (!psMetadataAddStr(config->user, PS_LIST_TAIL, "DBNAME", PS_META_REPLACE, NULL, dbname)) {
+                psWarning("Failed to overwrite .ipprc DBNAME value");
+            }
+
+            psArgumentRemove(argNum, argc, argv);
+        }
+    }
+
+    argNum = psArgumentGet(*argc, argv, "-dbuser");
+    if (argNum > 0) {
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psWarning("-dbuser command-line switch provided without the required database name");
+        } else {
+            char *dbuser = argv[argNum]; // The camera configuration file to read
+            if (!psMetadataAddStr(config->user, PS_LIST_TAIL, "DBUSER", PS_META_REPLACE, NULL, dbuser)) {
+                psWarning("Failed to overwrite .ipprc DBUSER value");
+            }
+
+            psArgumentRemove(argNum, argc, argv);
+        }
+    }
+
+    argNum = psArgumentGet(*argc, argv, "-dbpassword");
+    if (argNum > 0) {
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psWarning("-dbpassword command-line switch provided without the required password");
+        } else {
+            char *dbpassword = argv[argNum]; // The camera configuration file to read
+            if (!psMetadataAddStr(config->user, PS_LIST_TAIL, "DBPASSWORD", PS_META_REPLACE,
+                                  NULL, dbpassword)) {
+                psWarning("Failed to overwrite .ipprc DBPASSWORD value");
+            }
+
+            psArgumentRemove(argNum, argc, argv);
+        }
+    }
+
+    argNum = psArgumentGet(*argc, argv, "-dbport");
+    if (argNum > 0) {
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psWarning("-dbpport command-line switch provided without the required port number");
+        } else {
+            char *dbport = argv[argNum]; // The camera configuration file to read
+            if (!psMetadataAddS32(config->user, PS_LIST_TAIL, "DBPORT", PS_META_REPLACE, NULL,
+                                  (psS32)atoi(dbport))) {
+                psWarning("Failed to overwrite .ipprc DBPORT value");
+            }
+
+            psArgumentRemove(argNum, argc, argv);
+        }
+    }
+
+    psErrorClear();   // we may have failed to find some items in the metadata
+
+    return config;
+}
+
+
+// does this header match the specified camera format?  answer is supplied to 'valid' the
+// return value defines the error condition. error only on config errors
+bool pmConfigValidateCameraFormat(bool *valid, const psMetadata *cameraFormat, const psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(cameraFormat, false);
+    PS_ASSERT_PTR_NON_NULL(header, false);
+
+    // Read the rule for that camera format
+    bool mdStatus = true;
+
+    psMetadata *rule = psMetadataLookupMetadata(&mdStatus, cameraFormat, "RULE");
+    if (! mdStatus || ! rule) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read rule for camera.");
+        *valid = false;
+        return false;
+    }
+
+    // grab the metadata items in sequence by key so we get the MULTI entry
+    psList *keyList = psMetadataKeys (rule);
+    psArray *keys = psListToArray (keyList);
+    if (! keys) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read rule for camera.");
+        *valid = false;
+        return false;
+    }
+
+    *valid = true;
+    for (int i = 0; *valid && (i < keys->n); i++) {
+
+        // get the ruleItem for this key
+        psMetadataItem *ruleItem = psMetadataLookup(rule, keys->data[i]);
+
+        // Check for the existence of the rule in the header
+        psMetadataItem *headerItem = psMetadataLookup(header, ruleItem->name);
+        if (! headerItem) {
+            // rule item not found in header
+            psTrace("psModules.config.format", 5, "Can't find %s", ruleItem->name);
+            *valid = false;
+            continue;
+        }
+
+        // if the RULE type is a primitive type (int, float, etc) or string compare directly
+        if (PS_DATA_IS_PRIMITIVE (ruleItem->type) || (ruleItem->type == PS_DATA_STRING)) {
+            // Check to see if the rule works
+            if (!psMetadataItemCompare(headerItem, ruleItem)) {
+                psTrace("psModules.config.format", 5, "%s doesn't match.", ruleItem->name);
+                *valid = false;
+            }
+            continue;
+        }
+
+        // for MULTI, try each one & succeed if any match (valid = true is default state)
+        if (ruleItem->type == PS_DATA_METADATA_MULTI) {
+            bool found = false;
+            for (int j = 0; j < ruleItem->data.list->n; j++) {
+                psMetadataItem *entry = psListGet (ruleItem->data.list, j);
+                assert (entry);
+                if (psMetadataItemCompare(headerItem, entry)) {
+                    found = true;
+                    psTrace("psModules.config.format", 5, "%s in multi list matches.", ruleItem->name);
+                    break;
+                }
+            }
+            if (!found) {
+                *valid = false;
+                psTrace("psModules.config.format", 5, "%s doesn't match.", ruleItem->name);
+            }
+            continue;
+        }
+
+        psError(PS_ERR_UNKNOWN, false, "Invalid type for RULE %s.", ruleItem->name);
+        *valid = false;
+        psFree (keyList);
+        psFree (keys);
+        return false;
+    }
+
+    psFree (keyList);
+    psFree (keys);
+    return true;
+}
+
+// Given a camera and a header, see if any of the camera formats match the header
+// if so, return the winning format and the name of the winning format (both allocated here)
+static bool formatFromHeader(bool *status,
+                             psMetadata **format, // Format to return
+                             psString *name, // Name to return
+                             psMetadata *camera, // Camera configuration
+                             const psMetadata *header, // FITS header
+                             const char *cameraName // Name of camera
+                            )
+{
+    assert(format);
+    assert(camera);
+    assert(header);
+    assert(cameraName);
+    assert(*cameraName);
+
+    *status = true;                     // error status
+    bool result = false;                // Did we find the first match?
+
+    // Read the list of formats
+    bool mdok = true;                   // Status of MD lookup
+    psMetadata *formats = psMetadataLookupMetadata(&mdok, camera, "FORMATS"); // List of formats
+    if (!mdok || !formats) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find list of FORMATS in camera %s", cameraName);
+        *status = false;
+        return false;
+    }
+
+    if (!metadataReadFiles(formats, "camera format")) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read cameras formats within camera configuration.\n");
+        *status = false;
+        return false;
+    }
+
+    // Iterate over the formats
+    psMetadataIterator *formatsIter = psMetadataIteratorAlloc(formats, PS_LIST_HEAD, NULL);
+    psMetadataItem *formatsItem = NULL; // Item from formats
+    while ((formatsItem = psMetadataGetAndIncrement(formatsIter))) {
+        assert(formatsItem->type == PS_DATA_METADATA); // Since we have just read it in or deleted it
+        psMetadata *testFormat = formatsItem->data.md; // Format to test against
+
+        psTrace("psModules.config.format", 5, "trying format %s", formatsItem->name);
+
+        bool valid = false;
+        if (!pmConfigValidateCameraFormat(&valid, testFormat, header)) {
+            psError (PS_ERR_UNKNOWN, false, "Error in config scripts for camera %s, format %s\n",
+                     cameraName, formatsItem->name);
+            *status = false;
+            return false;
+        }
+        if (valid) {
+            if (!*format) {
+                psLogMsg("psModules.config.format", PS_LOG_INFO, "Camera %s, format %s matches header.\n",
+                         cameraName, formatsItem->name);
+                *format = psMemIncrRefCounter(testFormat);
+                *name = psStringCopy(formatsItem->name);
+                result = true;
+            } else {
+                psWarning("Camera %s, format %s also matches header --- ignored.\n",
+                         cameraName, formatsItem->name);
+            }
+        }
+    }
+    psFree(formatsIter);
+    *status = true;
+    return result;
+}
+
+// determine the camera format based on the keywords in the header.  If we have already chosen
+// a camera, then we select the formats only from that camera, and the meta-cameras for that
+// camera.  If we are discovering the camera (config->camera == NULL), then we also load the
+// recipe files for the camera.
+psMetadata *pmConfigCameraFormatFromHeader(psMetadata **camera, psString *formatName, pmConfig *config,
+                                           const psMetadata *header, bool readRecipes)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(header, NULL);
+
+    bool status = false;                // error status
+    psMetadata *format = NULL;          // The winning format
+    psString name = NULL;               // Name of the winning format
+
+    // If we don't know what sort of camera we have, we try all that we know
+    if (! config->camera) {
+        psAssert (!config->cameraName, "programming error: cameraName should be NULL if camera is undefined");
+        psAssert (!config->format,     "programming error: format should be NULL if camera is undefined");
+        psAssert (!config->formatName, "programming error: formatName should be NULL if camera is undefined");
+
+        bool mdok;                      // Metadata lookup status
+        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->system, "CAMERAS");
+        if (! mdok || !cameras) {
+            psError(PS_ERR_IO, true, "Unable to find CAMERAS in the configuration.");
+            return NULL;
+        }
+
+        if (!metadataReadFiles(cameras, "camera configuration")) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read cameras within site configuration.\n");
+            return NULL;
+        }
+
+        // Iterate over the cameras
+        psMetadataIterator *camerasIter = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL);
+        psMetadataItem *camerasItem = NULL; // Item from the metadata
+        while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
+            // Open the camera information
+            psTrace("psModules.config.format", 3, "Inspecting camera %s (%s)\n", camerasItem->name,
+                    camerasItem->comment);
+            assert(camerasItem->type == PS_DATA_METADATA); // It should be because we've read it in or deleted
+            psMetadata *testCamera = camerasItem->data.md; // Camera to test against what we've got:
+            if (formatFromHeader(&status, &format, &name, testCamera, header, camerasItem->name)) {
+                config->camera = psMemIncrRefCounter(testCamera);
+                config->cameraName = psStringCopy(camerasItem->name);
+                config->formatName = name;
+                config->format = format;
+                if (camera) {
+                    *camera = psMemIncrRefCounter(testCamera);  // view on value saved on config
+                }
+                if (formatName) {
+                    *formatName = psMemIncrRefCounter(name);    // view on value saved on config
+                }
+            } else {
+                if (!status) {
+                    psError(PS_ERR_IO, false, "Error reading camera config data for %s", camerasItem->name);
+                    return NULL;
+                }
+            }
+        }
+        psFree(camerasIter);
+
+        // Done looking at all cameras
+        if (!config->camera) {
+            psError(PS_ERR_IO, false, "Unable to find a camera that matches input FITS header!");
+            return NULL;
+        }
+
+        // Now we have the camera, we can read the recipes
+        if (readRecipes && !pmConfigReadRecipes(config, PM_RECIPE_SOURCE_CAMERA | PM_RECIPE_SOURCE_CL)) {
+            psError(PS_ERR_IO, false, "Error reading recipes from camera config for %s", config->cameraName);
+            return NULL;
+        }
+        return psMemIncrRefCounter(format); // a second copy, since the first copy sits on config->format
+    }
+
+    // we have a config with a specified camera.  However, the supplied header may not
+    // correspond to this mosaic level for the camera.  We need to try the CHIP and FPA mosaic
+    // versions as well as the base version
+
+    psAssert (config->cameraName, "programming error: cameraName should not be NULL if camera is defined");
+    psAssert (config->format,     "programming error: format should not be NULL if camera is defined");
+    psAssert (config->formatName, "programming error: formatName should not be NULL if camera is defined");
+
+    // the camera name is of the form: BASE, BASE_CHIP, or BASE_FPA.  pull out BASE:
+    char *baseName = cameraBaseName (config->cameraName);
+
+    bool found = false;
+
+    psMetadata *testCamera = NULL;
+    char *testName = NULL;
+
+    psMetadata *cameras = psMetadataLookupMetadata (NULL, config->system, "CAMERAS");
+    psAssert (cameras, "missing CAMERAS in complete metadata");
+
+    // try the FPA metaCamera
+    if (!found) {
+        testName = NULL;
+        psStringAppend (&testName, "_%s-FPA", baseName);
+
+        testCamera = psMetadataLookupMetadata (NULL, cameras, testName);
+        psAssert (testCamera, "missing %s in CAMERAS in complete metadata", testName);
+
+        bool status;
+        found = formatFromHeader(&status, &format, &name, testCamera, header, testName);
+        if (!found) psFree (testName);
+    }
+
+    // try the CHIP metaCamera
+    if (!found) {
+        testName = NULL;
+        psStringAppend (&testName, "_%s-CHIP", baseName);
+
+        testCamera = psMetadataLookupMetadata (NULL, cameras, testName);
+        psAssert (testCamera, "missing %s in CAMERAS in complete metadata", testName);
+
+        bool status;
+        found = formatFromHeader(&status, &format, &name, testCamera, header, testName);
+        if (!found) psFree (testName);
+    }
+
+    // try the SKYCELL metaCamera
+    if (!found) {
+        testName = NULL;
+        psStringAppend (&testName, "_%s-SKYCELL", baseName);
+
+        testCamera = psMetadataLookupMetadata (NULL, cameras, testName);
+        psAssert (testCamera, "missing %s in CAMERAS in complete metadata", testName);
+
+        bool status;
+        found = formatFromHeader(&status, &format, &name, testCamera, header, testName);
+        if (!found) psFree (testName);
+    }
+
+    // try the base name
+    if (!found) {
+        testName = psMemIncrRefCounter (baseName);
+
+        testCamera = psMetadataLookupMetadata (NULL, cameras, testName);
+        psAssert (testCamera, "missing %s in CAMERAS in complete metadata", testName);
+
+        bool status;
+        found = formatFromHeader(&status, &format, &name, testCamera, header, testName);
+    }
+
+    if (!found) {
+        psError(PS_ERR_IO, true, "Unable to find a format with the specified camera (%s) that matches the "
+                "given header.\n", baseName);
+        psFree (baseName);
+        return NULL;
+    }
+
+    psFree (name); // winning format name (for metaCamera) returned by formatFromHeader
+
+    psFree (baseName);
+    if (formatName) {
+        *formatName = testName;
+    } else {
+        psFree (testName);
+    }
+    if (camera) {
+        *camera = psMemIncrRefCounter(testCamera);
+    }
+    return format; // we do NOT need to incr ref counter: this is the only copy
+}
+
+// Return the requested camera configuration
+psMetadata *pmConfigCameraByName(pmConfig *config, const char *cameraName)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(cameraName, NULL);
+
+    psMetadata *cameras = psMetadataLookupMetadata(NULL, config->system, "CAMERAS");
+    if (!cameras) {
+        psError(PS_ERR_IO, true, "Unable to find CAMERAS in the configuration.");
+        return NULL;
+    }
+
+    psMetadataItem *item = psMetadataLookup(cameras, cameraName); // Item with camera of interest
+    if (!pmConfigFileIngest(item, "camera configuration")) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to ingest camera configuration.");
+        return NULL;
+    }
+
+    return psMemIncrRefCounter(item->data.md);
+}
+
+psMetadataItem *pmConfigUserSite(const pmConfig *config, const char *name, psDataType type)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    psMetadataItem *item = psMetadataLookup(config->user, name);
+    if (!item) {
+        item = psMetadataLookup(config->site, name);
+        if (!item) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Unable to find %s in user or site configuration", name);
+            return NULL;
+        }
+    }
+    if (item->type != type) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Type of %s (%x) in user/site configuration does not match expected (%x)",
+                name, item->type, type);
+        return NULL;
+    }
+
+    return item;
+}
+
+
+psDB *pmConfigDB(pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->user, NULL);
+
+#ifndef HAVE_PSDB
+
+    psError(PS_ERR_UNKNOWN, false,
+            "Cannot configure database: psModules was compiled without database support.");
+    return NULL;
+
+#else
+
+    if (config->database) {
+        return config->database;
+    }
+
+    // Connection details
+    psMetadataItem *server = pmConfigUserSite(config, "DBSERVER",   PS_DATA_STRING);
+    psMetadataItem *user   = pmConfigUserSite(config, "DBUSER",     PS_DATA_STRING);
+    psMetadataItem *pass   = pmConfigUserSite(config, "DBPASSWORD", PS_DATA_STRING);
+    psMetadataItem *name   = pmConfigUserSite(config, "DBNAME",     PS_DATA_STRING);
+    psMetadataItem *port   = pmConfigUserSite(config, "DBPORT",     PS_TYPE_S32);
+
+    if (!server || !user || !pass || !name) {
+        psWarning("Cannot find DBSERVER/DBUSER/DBPASSWORD/DBNAME in user or site configuration: "
+                  "unable to connect to database.");
+        psErrorClear();
+        return NULL;
+    }
+    if (!port) {
+        psTrace("psModules.config", 1, "Database port defaulting to 0");
+        psErrorClear();
+    }
+
+    if (strcasecmp(name->data.str, "XXX") == 0 || strcasecmp(name->data.str, "NONE") == 0) {
+        psTrace("psModules.config", 1, "Database initialisation skipped: database is %s.", name->data.str);
+        return NULL;
+    }
+
+    config->database = psDBInit(server->data.str, user->data.str, pass->data.str, name->data.str,
+                                port ? port->data.S32 : 0);
+    return config->database;
+
+#endif
+}
+
+
+bool pmConfigConformHeader(psMetadata *header, const psMetadata *format)
+{
+    PS_ASSERT_PTR_NON_NULL(header, false);
+    PS_ASSERT_PTR_NON_NULL(format, false);
+
+    bool mdok = true;                   // Status of MD lookup
+    psMetadata *rules = psMetadataLookupMetadata(&mdok, format, "RULE"); // How to identify this format
+    if (!mdok || !rules) {
+        psError(PS_ERR_IO, true, "Unable to find RULE in camera format.\n");
+        return false;
+    }
+
+    psMetadataIterator *rulesIter = psMetadataIteratorAlloc(rules, PS_LIST_HEAD, NULL); // Iterator for rules
+    psMetadataItem *rulesItem = NULL;   // Item from iteration
+    while ((rulesItem = psMetadataGetAndIncrement(rulesIter))) {
+        // this will insert each of the MULTI entries, writing over the previous copies
+        if (PS_DATA_IS_PRIMITIVE (rulesItem->type) || (rulesItem->type == PS_DATA_STRING)) {
+            psMetadataItem *newItem = psMetadataItemCopy(rulesItem); // Copy of item
+            psMetadataAddItem(header, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+            psFree(newItem);                // Drop reference
+            continue;
+        }
+
+        # if (0) // XXX not needed
+        // for MULTI entries, supply the first one
+        if (rulesItem->type == PS_DATA_METADATA_MULTI) {
+            psMetadataItem *entry = psListGet (rulesItem->data.list, PS_LIST_HEAD);
+            if (!entry) {
+                psError(PS_ERR_UNKNOWN, true, "No entries for MULTI RULE item.");
+                return false;
+            }
+            psMetadataItem *newItem = psMetadataItemCopy(entry); // Copy of item
+            psMetadataAddItem(header, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+            psFree(newItem);                // Drop reference
+            continue;
+        }
+        # endif
+
+        psError(PS_ERR_UNKNOWN, false, "Invalid type for RULE %s.", rulesItem->name);
+        return false;
+    }
+    psFree(rulesIter);
+
+    return true;
+}
+
+psArray *pmConfigFileSets(int *argc, char **argv, const char *file, const char *list)
+{
+    PS_ASSERT_PTR_NON_NULL(argc, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(*argc, NULL);
+    PS_ASSERT_PTR_NON_NULL(argv, NULL);
+
+    int Narg;                           // Argument number
+
+    // we load all input files onto a psArray, to be parsed later
+    psArray *input = psArrayAllocEmpty(16);
+
+    // load the list of filenames the supplied file
+    // maybe a comma-separated list of words
+    // each word may be a glob: "file*.fits"
+    if (file && strlen(file) > 0 && (Narg = psArgumentGet (*argc, argv, file))) {
+
+        // select the word after 'file' and split by comma
+        psArgumentRemove (Narg, argc, argv);
+        psArray *words = psStringSplitArray (argv[Narg], ",", true);
+        psArgumentRemove (Narg, argc, argv);
+
+        // parse the word as a glob
+        glob_t globList;
+        for (int i = 0; i < words->n; i++) {
+            globList.gl_offs = 0;
+            glob (words->data[i], 0, NULL, &globList);
+
+            // if the glob does not match, save the literal word:
+            // otherwise save all glob matches
+            if (globList.gl_pathc == 0) {
+                psArrayAdd (input, 16, words->data[i]);
+            } else {
+                for (int j = 0; j < globList.gl_pathc; j++) {
+                    char *filename = psStringCopy (globList.gl_pathv[j]);
+                    psArrayAdd (input, 16, filename);
+                    psFree (filename);
+                }
+            }
+        }
+        psFree (words);
+    }
+
+    // load the list from the supplied text file
+    if (list && strlen(list) > 0 && (Narg = psArgumentGet(*argc, argv, list))) {
+        psArgumentRemove (Narg, argc, argv);
+        FILE *f = fopen(argv[Narg], "r");
+        if (!f) {
+            psError(PS_ERR_IO, true, "Unable to open specified list file");
+            psFree(input);
+            return NULL;
+        }
+
+        // XXX Reading the list should be reimplemented using psSlurp
+
+        char line[1024]; // XXX limits the list lines to 1024 chars
+        while (fgets(line, 1024, f) != NULL) {
+            char word[1024];
+            int nItems = sscanf(line, "%s", word);
+            switch (nItems) {
+              case 0:
+                break;
+              case 1: {
+                  psString filename = psStringCopy(word);
+                  psArrayAdd(input, 16, filename);
+                  psFree(filename);
+                  break;
+              }
+              default:
+                // rigid format, no comments allowed?
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to parse file list: spaces detected.");
+                psFree(input);
+                fclose(f);
+                return NULL;
+            }
+        }
+        psArgumentRemove(Narg, argc, argv);
+        fclose(f);
+    }
+
+    return input;
+}
+
+// XXX this is a prime example of the failing of our error-handling system.  this function has
+// three possible outcomes: the argument was found, it was not found, or we raised an error.
+// returning only the bool does not distinguish failure to find the argument from a deeper
+// error.  requiring the calling function to both test the bool AND trap the error stack is
+// fragile: the error stack may not have been cleared, or they may not do both.  in some
+// places, we solve this by returning two types of boolean status values.  a better option
+// might be to return a psErrorCode value (as RHL proposed), which would be 0 on success and
+// any of several options on failure.
+
+bool pmConfigFileSetsMD(psMetadata *metadata, int *argc, char **argv, const char *name,
+                        const char *file, const char *list)
+{
+    PS_ASSERT_PTR_NON_NULL(metadata, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    psErrorClear();   // pmConfigFileSets may or may not call psError, so
+    // if files->n == 0 we'll want to call psError(..., false, ...)
+    psArray *files = pmConfigFileSets(argc, argv, file, list);
+    if (!files) {
+        psAbort("error parsing argument list");
+        psError(PS_ERR_IO, false, "error parsing argument list");
+        psFree (files);
+        return false;
+    }
+
+    // no files found: this is not really an error
+    if (files->n == 0) {
+        psFree (files);
+        return false;
+    }
+
+    psMetadataAddPtr(metadata, PS_LIST_TAIL, name,  PS_DATA_ARRAY, "", files);
+    psFree (files);
+    return true;
+}
+
+// convert the supplied name, create a new output psString
+psString pmConfigConvertFilename(const char *filename, const pmConfig *config, bool create, bool trunc)
+{
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    // strip file:// from front of name
+    if (!strncasecmp(filename, "file:", strlen("file:"))) {
+        psString newName = psStringCopy(filename);
+
+        char *point = newName + strlen("file:");
+        while (*point == '/')
+            point ++;
+        char *tmpName = NULL;
+        psStringAppend (&tmpName, "/%s", point);
+        psFree (newName);
+        newName = tmpName;
+
+        if (!checkPath(newName, create, trunc)) {
+            // let checkPath()'s psError() call float up
+            psFree (newName);
+            return NULL;
+        }
+
+        return newName;
+    }
+
+    // replace path://PATH with matched datapath
+    if (!strncasecmp(filename, "path://", strlen("path://"))) {
+        PS_ASSERT_METADATA_NON_NULL(config->site, NULL);
+
+        psString newName = psStringCopy(filename);
+
+        // filename should be of the form: path://PATH/rest/of/file
+        // replace PATH with matching name from config->site:DATAPATH
+        psMetadata *datapath = psMetadataLookupPtr (NULL, config->site, "DATAPATH");
+        if (datapath == NULL) {
+            psError(PS_ERR_UNKNOWN, true, "DATAPATH is not defined in config.site");
+            psFree (newName);
+            return NULL;
+        }
+
+        char *point = newName + strlen("path://");
+        char *mark = strchr (point, '/');
+        if (mark == NULL) {
+            psError(PS_ERR_UNKNOWN, true, "syntax error in PATH-style name %s", newName);
+            psFree (newName);
+            return false;
+        }
+
+        psString path = psStringNCopy (point, mark - point);
+        char *realpath = psMetadataLookupStr (NULL, datapath, path);
+        if (realpath == NULL) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "path (%s) not defined in config.site:DATAPATH for PATH-style name %s",
+                    path, newName);
+            psFree(newName);
+            psFree(path);
+            return false;
+        }
+        psFree(path);
+
+        char *tmpName = NULL;
+        psStringAppend(&tmpName, "%s/%s", realpath, mark + 1);
+        psFree(newName);
+        newName = tmpName;
+
+        if (!checkPath(newName, create, trunc)) {
+            // let checkPath()'s psError() call float up
+            psFree (newName);
+            return NULL;
+        }
+
+        return newName;
+    }
+
+    // substitute neb://name with matched nebulous name
+    if (!strncasecmp(filename, "neb://", strlen("neb://"))) {
+        #ifdef HAVE_NEBCLIENT
+
+        bool status = false;
+        psString neb_server = NULL;
+
+        // check the env first
+        neb_server = getenv("NEB_SERVER");
+
+        // if env isn't set, check the config system
+        if (!neb_server) {
+            neb_server = psMetadataLookupStr(&status, config->site, "NEB_SERVER");
+            if (!status) {
+                psError(PM_ERR_CONFIG, true, "failed to lookup config value for NEB_SERVER.");
+                return NULL;
+            }
+        }
+
+        if (!neb_server) {
+            psError(PM_ERR_CONFIG, true, "Could not determine nebulous server URI.");
+            return NULL;
+        }
+
+        nebServer *server = nebServerAlloc(neb_server);
+        if (!server) {
+            psError(PM_ERR_SYS, true, "failed to create a nebServer object: %s", nebErr(server));
+            nebServerFree(server);
+            return NULL;
+        }
+
+        char *nebfile = NULL;
+        if (!(nebfile = nebFind(server, filename))) {
+            // object does not exist
+            if (create) {
+                nebfile = nebCreate(server, filename, NULL, NULL);
+                if (!nebfile) {
+                    psError(PM_ERR_SYS, true, "failed to create a new nebulous key: %s", nebErr(server));
+                    nebServerFree(server);
+                    return NULL;
+                }
+            } else {
+                // if the object does not exist and create isn't set, then we
+                // should puke
+                psError(PM_ERR_SYS, true, "Unable to access file %s", filename);
+                nebServerFree(server);
+                return NULL;
+            }
+        }
+
+        // convert nebfile into a psString
+        psString path = psStringCopy(nebfile);
+        nebFree(nebfile);
+        nebServerFree(server);
+
+        if (trunc) {
+            if(truncate(path, 0) != 0) {
+                psError(PS_ERR_IO, true, "Failed to truncate Nebulous file %s (real name %s)\n", filename, path);
+                return NULL;
+            }
+        }
+
+        return path;
+
+        #else // ifdef HAVE_NEBCLIENT
+
+        psError(PM_ERR_PROG, true, "psModules was compiled without nebulous support.");
+        return NULL;
+        #endif // ifdef HAVE_NEBCLIENT
+
+    }
+
+    // if we go this far, do nothing
+    return psStringCopy(filename);
+}
+
+psMetadata *pmConfigFileRule(const pmConfig *config, const psMetadata *camera, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_METADATA_NON_NULL(camera, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    psMetadataItem *item = psMetadataLookup(camera, "FILERULES"); // Item with the file rule of interest
+    if (!item) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find FILERULES in the camera configuration.");
+        return NULL;
+    }
+
+    if (!pmConfigFileIngest(item, "file rules ")) {
+        psError(PM_ERR_CONFIG, false, "Unable to read file rules for camera.");
+        return NULL;
+    }
+
+    assert(item->type == PS_DATA_METADATA);
+    psMetadata *filerules = item->data.md; // File rules from the camera configuration
+
+    // select the name from the FILERULES
+    // check for alias name (type == STR, name is aliased name)
+    bool mdok;                          // Status of MD lookup
+    const char *realname = psMetadataLookupStr(&mdok, filerules, name); // Name of file rule to look up
+    if (!realname || strlen(realname) == 0) {
+        realname = name;
+    }
+
+    return psMetadataLookupMetadata(&mdok, filerules, realname);
+}
+
+psMetadata *pmConfigFitsType (const pmConfig *config, const psMetadata *camera, const char *fitsType)
+{
+    bool mdok;
+
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_METADATA_NON_NULL(camera, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(fitsType, NULL);
+
+    psMetadataItem *item = psMetadataLookup(camera, "FITSTYPES"); // Item with the file rule of interest
+    if (!item) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find FITSTYPES in the camera configuration.");
+        return NULL;
+    }
+
+    if (!pmConfigFileIngest(item, "FITS Types")) {
+        psError(PM_ERR_CONFIG, false, "Unable to read fits types for camera.");
+        return NULL;
+    }
+
+    assert(item->type == PS_DATA_METADATA);
+    psMetadata *fitstypes = item->data.md; // FITS Types from the camera configuration
+
+    // select the name from the FITSTYPES
+    psMetadata *scheme = psMetadataLookupMetadata(&mdok, fitstypes, fitsType);
+    if (!scheme) {
+        psWarning("Unable to find specified FITS Type %s in camera configuration.", fitsType);
+        return NULL;
+    }
+
+    return scheme;
+}
+
+static bool checkPath(const char *filename, bool create, bool trunc)
+{
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+
+    if (access(filename, R_OK) == 0) {
+        // file already exists
+        if (trunc) {
+            if(truncate(filename, 0) != 0) {
+                psError(PS_ERR_IO, true, "Failed to truncate file, %s\n", filename);
+                return false;
+            }
+        }
+    } else {
+        // file does not exist
+        if (create) {
+            int fd = open(filename, O_WRONLY|O_CREAT, 0666);
+            if (fd == 0) {
+                psError(PS_ERR_IO, true, "Failed to close file, %s\n", filename);
+                return false;
+            }
+            if (close(fd) != 0) {
+                psError(PS_ERR_IO, true, "Failed to close file, %s\n", filename);
+                return false;
+            }
+        } else {
+            // if the file does not exist and create isn't set, then we
+            // should puke
+            psError(PS_ERR_IO, true, "Unable to access file %s", filename);
+            return false;
+        }
+    }
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfig.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfig.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfig.h	(revision 22322)
@@ -0,0 +1,204 @@
+/*  @file pmConfig.h
+ *  @brief Configuration functions
+ *
+ *  @author Paul Price, IfA
+ *  @author Eugene Magnier, IfA
+ *
+ *  @version $Revision: 1.41 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-06 01:13:14 $
+ *  Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONFIG_H
+#define PM_CONFIG_H
+
+/// @addtogroup Config Configuration System
+/// @{
+
+/// Sources for recipes.
+///
+/// Defines what recipe sources have been read.  This allows us to read recipes from different sources as they
+/// become available.  For example, we may not have access to the camera configuration until we have read a
+/// FITS file.  We allow symbolic links, which means the user can specify on the command-line the name of a
+/// recipe that's defined elsewhere, instead of typing the entire filename.  This structure is private to
+/// psModules --- there is no need for the user to know about it.
+typedef enum {
+    PM_RECIPE_SOURCE_NONE        = 0x00, ///< None yet
+    PM_RECIPE_SOURCE_SYSTEM      = 0x01, ///< System configuration
+    PM_RECIPE_SOURCE_CAMERA      = 0x02, ///< Camera configuration
+    PM_RECIPE_SOURCE_CL          = 0x04, ///< Command-line
+    PM_RECIPE_SOURCE_SYMBOLIC    = 0x14, ///< Symbolic link, specified on command-line
+    PM_RECIPE_SOURCE_ALL         = 0xff  ///< All sources
+} pmRecipeSource;
+
+/// Configuration information
+///
+/// This structure stores the configuration information: user, site, system, camera and recipe configuration, the
+/// command-line arguments, the pmFPAfiles used, and the database handle.
+typedef struct {
+    psMetadata *user;                   ///< User configuration
+    psMetadata *site;                   ///< Site configuration
+    psMetadata *system;                 ///< System configuration
+    psMetadata *camera;                 ///< Camera specification
+    psString cameraName;                ///< Camera name
+    psMetadata *format;                 ///< Camera format description
+    psString formatName;                ///< Camera format name
+    psMetadata *recipes;                ///< Recipes for processing
+    psMetadata *recipesCamera;          ///< Recipes for processing
+    psMetadata *arguments;              ///< Processed command-line arguments
+    psMetadata *files;                  ///< pmFPAfiles used for analysis
+    psDB *database;                     ///< Database handle
+    const char *defaultRecipe;          ///< name of top-level recipe for this program
+    psString program;                   ///< Name of program
+    // Private members
+    pmRecipeSource recipesRead;         ///< Which recipe sources have been read
+    psMetadata *recipeSymbols;          ///< Where each recipe came from
+    int traceFD;                        ///< File descriptor for trace messages
+    int logFD;                          ///< File descriptor for log messages
+} pmConfig;
+
+/// Allocator for pmConfig
+pmConfig *pmConfigAlloc();
+
+/// Set static configuration information
+///
+/// The search path for the configuration files is a local static variable, set by this function.
+void pmConfigSet(const char *path ///< Search paths for configuration files; colon-delimited directories
+                );
+
+/// Free static memory used in the configuration system
+void pmConfigDone(void);
+
+/// Read configuration information from the command line.
+///
+/// pmConfigRead loads the user configuration (the file name is specified by "-ipprc FILE" on the
+/// command-line, the IPPRC environment variable, or it is $HOME/.ipprc).  The configuration search path is
+/// set. The camera configuration is loaded if it is specified on the command line ("-camera
+/// CAMERA_FILE"). Recipes specified on the command line ("-recipe RECIPE_NAME RECIPE_SOURCE") are also
+/// loaded.  These command-line arguments are removed from from the command-line, to simplify parsing.  The
+/// psLib log, trace and time setups are also performed if specified in the user configuration.
+pmConfig *pmConfigRead(int *argc,       ///< Number of command-line arguments
+                       char **argv, ///< Array of command-line arguments
+                       const char *defaultRecipe ///< name of top-level recipe for this program
+                      );
+
+/// Read a configuration file
+///
+/// Read a metadata configuration file into the supplied metadata.  Produce an error and
+/// return false if there's a problem.
+bool pmConfigFileRead(psMetadata **config, ///< Config to output
+                      const char *name, ///< Name of file
+                      const char *description ///< Description of file
+    );
+
+/// Ingest a configuration file
+///
+/// Ingest a metadata configuration file into the supplied metadata item, if required.  Produce an error and
+/// return false if there's a problem.
+bool pmConfigFileIngest(psMetadataItem *item, // Item into which to read file
+                        const char *description // Description, for error messages
+    );
+
+/// Validate a header against the camera format
+///
+/// Given a FITS header (the PHU header), check it against the RULE metadata contained within the camera
+/// format; return found = true if it matches. return false on serious errors
+bool pmConfigValidateCameraFormat(bool *valid,
+                                  const psMetadata *cameraFormat, ///< Camera format containing the RULE
+                                  const psMetadata *header // FITS header for the PHU
+                                 );
+
+/// Determine the camera format (and camera if unknown) from examining the header
+///
+/// Given a FITS header, check it against all known cameras (unless we already know which camera, from
+/// pmConfigRead) and all known formats for those cameras in order to identify which is appropriate.  The
+/// first matching format is accepted; further matches produce warnings.  The accepted camera is saved in the
+/// configuration.  The accepted format is returned.
+psMetadata *pmConfigCameraFormatFromHeader(psMetadata **camera, // selected camera (or meta-camera)
+                                           psString *formatName, // selected format name
+                                           pmConfig *config, ///< The configuration
+                                           const psMetadata *header, ///< The FITS header
+                                           bool readRecipes ///< optionally read the recipes as well as the format
+    );
+
+/// Return the camera configuration specified by name
+///
+/// Given a camera name, returns the camera configuration metadata.
+psMetadata *pmConfigCameraByName(pmConfig *config, ///< The configuration
+                                 const char *cameraName ///< The camera name header
+                                );
+
+/// Derive a value from the user or site configuration
+///
+/// The value in the user configuration takes precedence.  Returns NULL if the value isn't present in either,
+/// or has the wrong type.
+psMetadataItem *pmConfigUserSite(const pmConfig *config, // Configuration
+                                 const char *name, // Name of value
+                                 psDataType type // Expected type
+    );
+
+
+/// Setup the database
+///
+/// Initialise the database connection using the DBSERVER, DBNAME, DBUSER, DBPASSWORD values provided in the
+/// site configuration.  Stores the database handle in the configuration, and also returns it.
+psDB *pmConfigDB(pmConfig *config       ///< Configuration
+                );
+
+/// Make the supplied header conform to the nominated camera format.
+///
+/// Given a FITS header, make it conform to the RULE in the specified camera format.  This is useful for
+/// switching between formats, or generating fake data that must be recognised by
+/// pmConfigCameraFormatFromHeader.
+bool pmConfigConformHeader(psMetadata *header, ///< Header to conform
+                           const psMetadata *format ///< Camera format
+                          );
+
+/// Read the command-line for files (or a text file containing a list of files)
+///
+/// Given the 'file' and 'list' arguments (e.g., "-file" and "-list"), find the arguments associated with
+/// these words and interpret them as lists of files.  Return an array of the resulting filenames.
+psArray *pmConfigFileSets(int *argc,    ///< Number of arguments (I/O)
+                          char **argv,  ///< Array of arguments
+                          const char *file, ///< CL argument specifying a filename
+                          const char *list ///< CL argument specifying a text file with a list of filenames
+                         );
+
+/// Stuff associated files from the command-line into a metadata
+///
+/// Calls pmConfigFileSets to parse the command line for filenames (or a list which provides filenames), and
+/// stuffs the array of filenames into the metadata under "name".
+bool pmConfigFileSetsMD(psMetadata *metadata, ///< Metadata into which to stuff the array
+                        int *argc,    ///< Number of arguments (I/O)
+                        char **argv,  ///< Array of arguments
+                        const char *name, ///< Name for array in the metadata
+                        const char *file, ///< CL argument specifying a filename
+                        const char *list ///< CL argument specifying a text file with a list of filenames
+                       );
+
+/// Convert the supplied name, create a new output psString
+psString pmConfigConvertFilename(
+    const char *filename,               ///< file path/URI
+    const pmConfig *config,             ///< configuration
+    bool create,                        ///< create the file if it doesn't exist
+    bool trunc                          ///< truncate the file (if it exists)
+);
+
+/// Set whether all config parameters are read on startup
+bool pmConfigReadParamsSet(bool newReadCameraConfig // Desired mode for camera configuration reading
+                          );
+
+/// Get the file rule of interest
+///
+/// Look up the name of the set of file rules to use, get that set from the system configuration, and return the
+/// appropriate rule from the set.
+psMetadata *pmConfigFileRule(const pmConfig *config, ///< Configuration
+                             const psMetadata *camera, ///< Camera configuration of interest
+                             const char *name ///< Name of rule to read
+    );
+
+// look up the specified fitstype, interpolating the file if needed
+psMetadata *pmConfigFitsType (const pmConfig *config, const psMetadata *camera, const char *fitsType);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCamera.c	(revision 22322)
@@ -0,0 +1,919 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmVersion.h"
+#include "pmConcepts.h"
+#include "pmConfigCamera.h"
+
+#define TABLE_OF_CONTENTS "CONTENTS"    // Name for camera format metadata containing the contents
+#define CHIP_TYPES "CHIPS"              // Name for camera format metadata containing the chip types
+#define CELL_TYPES "CELLS"              // Name for camera format metadata containing the cell types
+
+// local helper functions defined below
+static void removeCellConceptsSources(psMetadata *source);
+static void removeChipConceptsSources(psMetadata *source);
+
+psString pmConfigCameraRootName(const char *name)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    if (name[0] != '_') {
+        // It's an original
+        return psStringCopy(name);
+    }
+
+    psString root = psStringCopy(name + 1); // Camera name
+    int length = strlen(name);                     // Length of camera name
+    if (strcmp(root + length - 9, "-SKYCELL") == 0) {
+        length -= 9;
+    } else if (strcmp(root + length - 6, "-CHIP") == 0) {
+        length -= 6;
+    } else if (strcmp(root + length - 5, "-FPA") == 0) {
+        length -= 5;
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised derivative camera: %s", name);
+        psFree(root);
+        return NULL;
+    }
+
+    // Truncate the string
+    root[length] = '\0';
+
+    return root;
+}
+
+psString pmConfigCameraSkycellName(const char *name)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    psString root = pmConfigCameraRootName(name); // Root name of camera
+    if (!root) {
+        return NULL;
+    }
+
+    psStringAppend(&root, "-SKYCELL");
+    psStringPrepend(&root, "_");
+
+    return root;
+}
+
+psString pmConfigCameraChipName(const char *name)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    psString root = pmConfigCameraRootName(name); // Root name of camera
+    if (!root) {
+        return NULL;
+    }
+
+    psStringAppend(&root, "-CHIP");
+    psStringPrepend(&root, "_");
+
+    return root;
+}
+
+psString pmConfigCameraFPAName(const char *name)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    psString root = pmConfigCameraRootName(name); // Root name of camera
+    if (!root) {
+        return NULL;
+    }
+
+    psStringAppend(&root, "-FPA");
+    psStringPrepend(&root, "_");
+
+    return root;
+}
+
+// Generate the skycell version of a named camera configuration
+bool pmConfigCameraSkycellVersion(psMetadata *system, // The system configuration
+                                  const char *name // Name of the un-mosaicked camera
+                                  )
+{
+    PS_ASSERT_METADATA_NON_NULL(system, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *cameras = psMetadataLookupMetadata(&mdok, system, "CAMERAS"); // List of cameras
+    if (!mdok || !cameras) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the system configuration.\n");
+        return false;
+    }
+    if (!pmConfigGenerateSkycellVersion(cameras, cameras, name, system)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to build skycell camera description for %s\n", name);
+        return false;
+    }
+    return true;
+}
+
+
+bool pmConfigCameraSkycellVersionsAll(psMetadata *system)
+{
+    PS_ASSERT_METADATA_NON_NULL(system, false);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *cameras = psMetadataLookupMetadata(&mdok, system, "CAMERAS"); // List of cameras
+    if (!mdok || !cameras) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the system configuration.\n");
+        return false;
+    }
+
+    psMetadataIterator *camerasIter = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *camerasItem = NULL; // Item from iteration
+    psMetadata *new = psMetadataAlloc();// New cameras to add
+    while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
+        assert(camerasItem->type == PS_DATA_METADATA); // Only metadata are allowed here!
+        if (!pmConfigGenerateSkycellVersion(cameras, new, camerasItem->name, system)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to build skycell camera description for %s\n",
+                    camerasItem->name);
+            return false;
+        }
+    }
+    psFree(camerasIter);
+
+    // Now put the new cameras at the top of the list of cameras, so they get recognised first
+    // Note: going from the top, and putting everything to the top as we get there, so that the last one on
+    // goes to the top.  This preserves the original order of the cameras, putting the skycell versions
+    // before the originals.
+    camerasIter = psMetadataIteratorAlloc(new, PS_LIST_HEAD, NULL); // Iterator
+    while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
+        psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, 0);
+    }
+    psFree(camerasIter);
+    psFree(new);
+
+    return true;
+}
+
+// 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 };
+const static char *skycellConceptsChip[] = { "CHIP.XPARITY", "CHIP.YPARITY", 0 };
+const static char *skycellConceptsFPA[] = { 0 };
+
+// What do we call the skycell concept in the FITS header?
+static const char *skycellConceptName(const char *name, // Name of concept
+                                      const char **concepts, // List of concepts NOT to update
+                                      const psMetadata *system // System configuration
+                                      )
+{
+    for (int i = 0; concepts[i]; i++) {
+        if (strcmp(name, concepts[i]) == 0) {
+            return NULL;
+        }
+    }
+
+    if (!system) {
+        return name;
+    }
+    bool mdok;                          // Status of MD lookup
+    psMetadata *skycells = psMetadataLookupMetadata(&mdok, system, "SKYCELLS"); // Skycell concept headers
+    if (!skycells) {
+        return name;
+    }
+    const char *keyword = psMetadataLookupStr(&mdok, skycells, name); // Keyword to use for this concept
+    if (!mdok || !keyword || strlen(keyword) == 0) {
+        return name;
+    }
+    return keyword;
+}
+
+
+// Generate a skycell version of a camera configuration
+bool pmConfigGenerateSkycellVersion(psMetadata *oldCameras, // Old list of camera configurations
+                                    psMetadata *newCameras, // New list of camera configurations
+                                    const char *name, // Name of original camera configuration
+                                    const psMetadata *system // System configuration
+                                    )
+{
+    assert(oldCameras);
+    assert(newCameras);
+    assert(name);
+
+    // See if the old one is there
+    psMetadata *camera = psMetadataLookupMetadata(NULL, oldCameras, name); // The camera configuration
+    if (!camera) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find camera to be skycelled in camera list.");
+        return false;
+    }
+
+    // See if the new one is already there
+    psString newName = NULL;       // Name of skycelled camera
+    psStringAppend(&newName, "_%s-SKYCELL", name);
+    if (psMetadataLookup(oldCameras, newName)) {
+        return true;
+    }
+
+    psMetadata *new = psMetadataCopy(NULL, camera); // Copy of the camera description
+
+    // Fix the FPA description to contain a single chip with single cell
+    {
+        psMetadata *fpa = psMetadataAlloc();// The FPA description
+        psMetadataAddStr(fpa, PS_LIST_HEAD, "SkyChip", 0, "Single chip with single cell", "SkyCell");
+        psMetadataAddMetadata(new, PS_LIST_TAIL, "FPA", PS_META_REPLACE, "Description of FPA hierarchy", fpa);
+        psFree(fpa);
+    }
+
+    // Clear out the formats, replace them with the One True Format
+    psMetadata *formats = psMetadataLookupMetadata(NULL, new, "FORMATS"); // The list of formats
+    if (!formats) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find FORMATS within camera configuration.");
+        psFree(new);
+        return false;
+    }
+    while (psListLength(formats->list) > 0) {
+        psMetadataRemoveIndex(formats, PS_LIST_HEAD);
+    }
+    psMetadata *format = psMetadataAlloc(); // The One True Format
+
+    {
+        psMetadata *rule = psMetadataAlloc(); // The RULE --- how to recognise the camera
+        psMetadataAddStr(rule, PS_LIST_TAIL, "PSCAMERA", 0, "Camera name", name);
+        psMetadataAddStr(rule, PS_LIST_TAIL, "PSFORMAT", 0, "Camera format", "SKYCELL");
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "RULE", 0, "How to recognise this type of file", rule);
+        psFree(rule);
+    }
+
+    {
+        psMetadata *file = psMetadataAlloc(); // The FILE --- how to read the data
+        psMetadataAddStr(file, PS_LIST_TAIL, "PHU", 0, "What level the FITS file represents", "FPA");
+        psMetadataAddStr(file, PS_LIST_TAIL, "EXTENSIONS", 0, "What level the extensions represent", "NONE");
+        psMetadataAddStr(file, PS_LIST_TAIL, "FPA.OBS", 0, "PHU keyword for unique identifier", "FPA.OBS");
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "FILE", 0, "How to read this type of file", file);
+        psFree(file);
+    }
+
+    psMetadataAddStr(format, PS_LIST_TAIL, "CONTENTS", 0, "What's in this type of file",
+                     "SkyChip:SkyCell:_skycell");
+
+    {
+        psMetadata *cells = psMetadataAlloc(); // The CELLS --- how to read the cells
+        psMetadata *skycell = psMetadataAlloc(); // How to read the skycell
+        psMetadataAddStr(skycell, PS_LIST_TAIL, "CELL.TRIMSEC", 0, "Trim section", "CELL.TRIMSEC");
+        psMetadataAddStr(skycell, PS_LIST_TAIL, "CELL.BIASSEC", 0, "Bias section", "CELL.BIASSEC");
+        psMetadataAddStr(skycell, PS_LIST_TAIL, "CELL.TRIMSEC.SOURCE", 0, "Source for trim section",
+                         "HEADER");
+        psMetadataAddStr(skycell, PS_LIST_TAIL, "CELL.BIASSEC.SOURCE", 0, "Source for bias section",
+                         "HEADER");
+        psMetadataAddMetadata(cells, PS_LIST_TAIL, "_skycell", 0, "Skycell specification", skycell);
+        psFree(skycell);
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "CELLS", 0, "How to read the cells", cells);
+        psFree(cells);
+    }
+
+    // Stuffing all concepts into the header, by their PS concept name (e.g., "FPA.AIRMASS").
+    // (HIERARCH will take care of the long names, implemented in psLib.)
+    // Some people may not like this, but it's quick and easy and will do for now.
+    // An alternative may be provided later.
+    {
+        psMetadata *translation = psMetadataAlloc(); // The TRANSLATION --- how to read the FITS headers
+
+        psList *concepts;               // List of concepts for each level
+        psListIterator *iter;           // Iterator for concepts
+        psString name;                  // Concept name, from iteration
+
+        concepts = pmConceptsList(PM_FPA_LEVEL_FPA); // FPA-level concepts
+        iter = psListIteratorAlloc(concepts, PS_LIST_HEAD, false);
+        while ((name = psListGetAndIncrement(iter))) {
+            const char *new = skycellConceptName(name, skycellConceptsFPA, system); // Name for skycell
+            if (new) {
+                psMetadataAddStr(translation, PS_LIST_TAIL, name, 0, NULL, new);
+            }
+        }
+        psFree(iter);
+        psFree(concepts);
+
+        concepts = pmConceptsList(PM_FPA_LEVEL_CHIP);
+        iter = psListIteratorAlloc(concepts, PS_LIST_HEAD, false);
+        while ((name = psListGetAndIncrement(iter))) {
+            const char *new = skycellConceptName(name, skycellConceptsChip, system); // Name for skycell
+            if (new) {
+                psMetadataAddStr(translation, PS_LIST_TAIL, name, 0, NULL, new);
+            }
+        }
+        psFree(iter);
+        psFree(concepts);
+
+        concepts = pmConceptsList(PM_FPA_LEVEL_CELL);
+        iter = psListIteratorAlloc(concepts, PS_LIST_HEAD, false);
+        while ((name = psListGetAndIncrement(iter))) {
+            const char *new = skycellConceptName(name, skycellConceptsCell, system); // Name for skycell
+            if (new) {
+                psMetadataAddStr(translation, PS_LIST_TAIL, name, 0, NULL, new);
+            }
+        }
+        psFree(iter);
+        psFree(concepts);
+
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "TRANSLATION", 0, "How to translate the FITS headers",
+                              translation);
+        psFree(translation);
+    }
+
+    {
+        psMetadata *defaults = psMetadataAlloc(); // Default values for concepts
+
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CELL.XPARITY", 0, NULL, 1);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CELL.YPARITY", 0, NULL, 1);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CHIP.XPARITY", 0, NULL, 1);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CHIP.YPARITY", 0, NULL, 1);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CELL.Y0", 0, NULL, 0);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CELL.X0", 0, NULL, 0);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CHIP.Y0", 0, NULL, 0);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CHIP.X0", 0, NULL, 0);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CELL.READDIR", 0, "Read direction (rows)", 1);
+
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "DEFAULTS", 0, "Default values for concepts", defaults);
+        psFree(defaults);
+
+    }
+
+    {
+        psMetadata *database = psMetadataAlloc(); // Database values for concepts
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "DATABASE", 0, "Database values for concepts", database);
+        psFree(database);
+    }
+
+    {
+        psMetadata *conceptFormats = psMetadataAlloc(); // Format peculiarities for various concepts
+        // These are the only essential formats
+        psMetadataAddStr(conceptFormats, PS_LIST_TAIL, "FPA.RA", 0, "Units for RA", "HOURS");
+        psMetadataAddStr(conceptFormats, PS_LIST_TAIL, "FPA.DEC", 0, "Units for RA", "DEGREES");
+        psMetadataAddStr(conceptFormats, PS_LIST_TAIL, "FPA.TIME", 0, "Format for time", "MJD");
+        psMetadataAddStr(conceptFormats, PS_LIST_TAIL, "CELL.TIME", 0, "Format for time", "MJD");
+
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "FORMATS", 0, "Formats for various concepts",
+                              conceptFormats);
+        psFree(conceptFormats);
+    }
+
+    psMetadataAddMetadata(formats, PS_LIST_TAIL, "SKYCELL", 0, "The One True Format for skycells", format);
+    psFree(format);
+
+    // New camera MUST go to the head of the metadata, so that it will be recognised first
+    // The old camera doesn't contain the PSCAMERA and PSFORMAT headers, so it will match anything!
+    psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE,
+                          "Automatically generated", new);
+    psFree(newName);
+    psFree(new);
+
+    return true;
+}
+
+// Generate the Chip and FPA mosaicked version of a named camera configuration
+bool pmConfigCameraMosaickedVersions(psMetadata *system, // The system configuration
+                                     const char *name // Name of the un-mosaicked camera
+                                    )
+{
+    PS_ASSERT_METADATA_NON_NULL(system, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *cameras = psMetadataLookupMetadata(&mdok, system, "CAMERAS"); // List of cameras
+    if (!mdok || !cameras) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the system configuration.\n");
+        return false;
+    }
+    if (!pmConfigGenerateMosaickedVersion(cameras, cameras, name, PM_FPA_LEVEL_CHIP)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to build Chip mosaic camera description for %s\n", name);
+        return false;
+    }
+    if (!pmConfigGenerateMosaickedVersion(cameras, cameras, name, PM_FPA_LEVEL_FPA)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to build FPA mosaic camera description for %s\n", name);
+        return false;
+    }
+    return true;
+}
+
+// the operation putting the new entries first is now implemented in pmConfigGenerateMosaickedVersion
+// Generate the Chip and FPA mosaicked version of a named camera configuration
+bool pmConfigCameraMosaickedVersionsAll(psMetadata *system)
+{
+    PS_ASSERT_METADATA_NON_NULL(system, false);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *cameras = psMetadataLookupMetadata(&mdok, system, "CAMERAS"); // List of cameras
+    if (!mdok || !cameras) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the system configuration.\n");
+        return false;
+    }
+
+    psMetadataIterator *camerasIter = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *camerasItem = NULL; // Item from iteration
+    psMetadata *new = psMetadataAlloc();// New cameras to add
+    while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
+        assert(camerasItem->type == PS_DATA_METADATA); // Only metadata are allowed here!
+        if (!pmConfigGenerateMosaickedVersion(cameras, new, camerasItem->name, PM_FPA_LEVEL_CHIP)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to build Chip mosaic camera description for %s\n",
+                    camerasItem->name);
+            return false;
+        }
+        if (!pmConfigGenerateMosaickedVersion(cameras, new, camerasItem->name, PM_FPA_LEVEL_FPA)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to build FPA mosaic camera description for %s\n",
+                    camerasItem->name);
+            return false;
+        }
+    }
+    psFree(camerasIter);
+
+    // Now put the new cameras at the top of the list of cameras, so they get recognised first
+    // Note: going from the top, and putting everything to the top as we get there, so that the last one on
+    // goes to the top.  This preserves the original order of the cameras, putting the mosaicked versions
+    // before the originals.
+    camerasIter = psMetadataIteratorAlloc(new, PS_LIST_HEAD, NULL); // Iterator
+    while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
+        psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, 0);
+    }
+    psFree(camerasIter);
+    psFree(new);
+
+    return true;
+}
+
+// Generate a mosaicked version of a camera configuration
+bool pmConfigGenerateMosaickedVersion(psMetadata *oldCameras, // Old list of camera configurations
+                                      psMetadata *newCameras, // New list of camera configurations
+                                      const char *name, // Name of original camera configuration
+                                      pmFPALevel mosaicLevel // Level to which we are mosaicking
+    )
+{
+    assert(oldCameras);
+    assert(newCameras);
+    assert(name);
+    assert(mosaicLevel == PM_FPA_LEVEL_CHIP || mosaicLevel == PM_FPA_LEVEL_FPA);
+
+    if (name[0] == '_') {
+        // It's already a mosaicked version of some sort
+        return true;
+    }
+
+    // See if the old one is there
+    psMetadata *camera = psMetadataLookupMetadata(NULL, oldCameras, name); // The camera configuration
+    if (!camera) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find camera to be mosaicked in camera list.");
+        return false;
+    }
+
+    // See if the new one is already there
+    psString newName = NULL;       // Name of mosaicked camera
+    psStringAppend(&newName, "_%s-%s", name, mosaicLevel == PM_FPA_LEVEL_CHIP ? "CHIP" : "FPA");
+    if (psMetadataLookup(oldCameras, newName)) {
+        return true;
+    }
+
+    psMetadata *new = psMetadataCopy(NULL, camera); // Copy of the camera description
+    bool mdok;                          // Status of MD lookups
+
+    // ** Fix up the contents of the FPA description to match the mosaicked camera **
+    // select the FPA description
+    psMetadata *fpa = psMetadataLookupMetadata(NULL, new, "FPA"); // FPA in the camera configuration
+    if (!fpa) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find FPA within camera configuration.");
+        psFree(new);
+        return false;
+    }
+    switch (mosaicLevel) {
+        // For CHIP mosaic, replace the contents of each chip with a single cell
+      case PM_FPA_LEVEL_CHIP: {
+          psMetadataIterator *fpaIter = psMetadataIteratorAlloc(fpa, PS_LIST_HEAD, NULL); // Iterator
+          psMetadataItem *fpaItem = NULL;     // Item from iteration
+          while ((fpaItem = psMetadataGetAndIncrement(fpaIter))) {
+              if (fpaItem->type != PS_DATA_STRING) {
+                  psError(PS_ERR_UNKNOWN, true,
+                          "Element %s within FPA in camera configuration is not of type STR.",
+                          fpaItem->name);
+                  psFree(new);
+                  return false;
+              }
+
+              psFree(fpaItem->data.str);
+              fpaItem->data.str = psStringCopy("MosaickedCell");
+              psFree(fpaItem->comment);
+              fpaItem->comment = psStringCopy("Mosaicked cell; automatically generated");
+          }
+          psFree(fpaIter);
+          break;
+      }
+        // For FPA mosaic, replace the contents of the FPA with a single chip containing a single cell
+      case PM_FPA_LEVEL_FPA: {
+          while (psListLength(fpa->list) > 0) {
+              psMetadataRemoveIndex(fpa, PS_LIST_TAIL);
+          }
+
+          psMetadataAddStr(fpa, PS_LIST_HEAD, "MosaickedChip", 0,
+                           "Mosaicked chip with mosaicked cell; automatically generated",
+                           "MosaickedCell");
+          break;
+      }
+    default:
+        psAbort("Should never get here.\n");
+    }
+
+    // ** Update the camera formats : add a new (mosaicked) format for each existing camera format **
+    // select the list of all camera formats
+    psMetadata *formats = psMetadataLookupMetadata(NULL, new, "FORMATS"); // FORMATS in the configuration
+    assert(formats);            // It had better be there --- we've already read them in
+    // loop over each of the formats
+    psMetadataIterator *formatsIter = psMetadataIteratorAlloc(formats, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *formatsItem = NULL; // Item from iteration
+    while ((formatsItem = psMetadataGetAndIncrement(formatsIter))) {
+        assert(formatsItem->type == PS_DATA_METADATA); // We should have read it by now!
+        psMetadata *format = formatsItem->data.md; // The camera format
+
+        // Add a new RULE which uniquely describes the mosaicked format.  this is needed so
+        // that when a mosaic is written to a FITS file, it can be recognised again when read.
+        psMetadata *rule = psMetadataLookupMetadata(NULL, format, "RULE"); // Way to identify format from PHU
+        if (!rule) {
+            // a camera format without a rule is not allowed.
+            psError(PS_ERR_UNKNOWN, false, "Camera format %s has no RULE", formatsItem->name);
+            return false;
+        }
+
+        // the new rule is supplemented by the mosaicLevel
+        switch (mosaicLevel) {
+        case PM_FPA_LEVEL_CHIP:
+            psMetadataAddStr(rule, PS_LIST_TAIL, "PSMOSAIC", 0, "Mosaicked level", "CHIP");
+            break;
+        case PM_FPA_LEVEL_FPA:
+            psMetadataAddStr(rule, PS_LIST_TAIL, "PSMOSAIC", 0, "Mosaicked level", "FPA");
+            break;
+        default:
+            psAbort("Should never get here.\n");
+        }
+
+        // Fix the FILE information: need to fix the levels for the PHU and EXTENSIONS.
+        // both of these elements are required in the format; we raise an error if they are not found
+        // If EXTENSIONS is NONE, then we need to change the CONTENT specifier to point to the chip name.
+        psMetadata *file = psMetadataLookupMetadata(NULL, format, "FILE"); // File information
+        if (!file) {
+            psError(PS_ERR_UNKNOWN, false, "Camera format %s has no FILE", formatsItem->name);
+            return false;
+        }
+        psMetadataItem *phuItem = psMetadataLookup(file, "PHU"); // PHU level
+        if (!phuItem || phuItem->type != PS_DATA_STRING) {
+            psError(PS_ERR_UNKNOWN, false, "Camera format %s is missing PHU in the FILE information", formatsItem->name);
+            return false;
+        }
+        psMetadataItem *extensionsItem = psMetadataLookup(file, "EXTENSIONS"); // Extensions level
+        if (!extensionsItem || extensionsItem->type != PS_DATA_STRING) {
+            psError(PS_ERR_UNKNOWN, false, "Camera format %s is missing EXTENSIONS in the FILE information", formatsItem->name);
+            return false;
+        }
+
+        // mosaicLevel == CHIP:
+        // Case    PHU     EXTENSIONS     Modifications
+        // ====    ===     ==========     ===========
+        // 1.      FPA     CHIP           NONE
+        // 2.      FPA     CELL           EXT->CHIP
+        // 3.      FPA     NONE           NONE
+        // 4.      CHIP    CELL           EXT->NONE
+        // 5.      CHIP    NONE           NONE
+        // 6.      CELL    NONE           PHU->CHIP
+        // possible outcomes:
+        //         FPA     CHIP
+        //         FPA     NONE
+        //         CHIP    NONE
+
+        // mosaicLevel == FPA:
+        // Case    PHU     EXTENSIONS     Modifications
+        // ====    ===     ==========     ===========
+        // 1.      FPA     CHIP           EXT->NONE
+        // 2.      FPA     CELL           EXT->NONE
+        // 3.      FPA     NONE           NONE
+        // 4.      CHIP    CELL           PHU->FPA, EXT->NONE
+        // 5.      CHIP    NONE           PHU->FPA
+        // 6.      CELL    NONE           PHU->FPA
+        // possible outcomes:
+        //         FPA     NONE
+
+        // modify the values of phuItem and extensionsItem
+        switch (mosaicLevel) {
+          case PM_FPA_LEVEL_CHIP:
+            if (!strcasecmp(phuItem->data.str, "FPA") && !strcasecmp(extensionsItem->data.str, "CHIP")) {
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "FPA") && !strcasecmp(extensionsItem->data.str, "CELL")) {
+                psFree(extensionsItem->data.str);
+                extensionsItem->data.str = psStringCopy("CHIP");
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "FPA") && !strcasecmp(extensionsItem->data.str, "NONE")) {
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "CHIP") && !strcasecmp(extensionsItem->data.str, "CELL")) {
+                psFree(extensionsItem->data.str);
+                extensionsItem->data.str = psStringCopy("NONE");
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "CHIP") && !strcasecmp(extensionsItem->data.str, "NONE")) {
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "CELL") && !strcasecmp(extensionsItem->data.str, "NONE")) {
+                psFree(phuItem->data.str);
+                phuItem->data.str = psStringCopy("CHIP");
+                break;
+            }
+            psAbort ("should not reach here");
+
+          case PM_FPA_LEVEL_FPA:
+            if (!strcasecmp(phuItem->data.str, "FPA") && !strcasecmp(extensionsItem->data.str, "CHIP")) {
+                psFree(extensionsItem->data.str);
+                extensionsItem->data.str = psStringCopy("NONE");
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "FPA") && !strcasecmp(extensionsItem->data.str, "CELL")) {
+                psFree(extensionsItem->data.str);
+                extensionsItem->data.str = psStringCopy("NONE");
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "FPA") && !strcasecmp(extensionsItem->data.str, "NONE")) {
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "CHIP") && !strcasecmp(extensionsItem->data.str, "CELL")) {
+                psFree(phuItem->data.str);
+                phuItem->data.str = psStringCopy("FPA");
+                psFree(extensionsItem->data.str);
+                extensionsItem->data.str = psStringCopy("NONE");
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "CHIP") && !strcasecmp(extensionsItem->data.str, "NONE")) {
+                psFree(phuItem->data.str);
+                phuItem->data.str = psStringCopy("FPA");
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "CELL") && !strcasecmp(extensionsItem->data.str, "NONE")) {
+                psFree(phuItem->data.str);
+                phuItem->data.str = psStringCopy("FPA");
+                break;
+            }
+            psAbort ("should not reach here");
+
+          default:
+            psAbort("Should never get here.\n");
+        }
+
+        // Fix up the CONTENTS to contain only the mosaicked cell for each chip
+        switch (mosaicLevel) {
+          case PM_FPA_LEVEL_FPA:
+            psMetadataAddStr(format, PS_LIST_TAIL, TABLE_OF_CONTENTS, PS_META_REPLACE, NULL,
+                             "MosaickedChip:MosaickedCell:_mosaic");
+            break;
+          case PM_FPA_LEVEL_CHIP:
+            if (!strcasecmp(phuItem->data.str, "FPA") && !strcasecmp(extensionsItem->data.str, "CHIP")) {
+                // ensure the value of CONTENT in the FILE section has the right value
+                psMetadataAddStr(file, PS_LIST_TAIL, "CONTENT", PS_META_REPLACE, "Key to CONTENTS menu",
+                                 "PS_CNTNT");
+                psMetadataAddStr(file, PS_LIST_TAIL, "CONTENT.RULE", PS_META_REPLACE,
+                                 "Rule to generate CONTENTS", "{CHIP.NAME}");
+
+                // List the chipName:chipType for each chip.
+                psMetadata *contents = psMetadataAlloc(); // List of contents, with chipName:chipType
+
+                // XXX this is using the fpaItem->name not the chipName
+                psMetadataIterator *fpaIter = psMetadataIteratorAlloc(fpa, PS_LIST_HEAD, NULL); // Iteratr
+                psMetadataItem *fpaItem;    // Item from iteration
+                while ((fpaItem = psMetadataGetAndIncrement(fpaIter))) {
+                    assert (fpaItem->type == PS_DATA_STRING);
+                    psString content = NULL; // Content to add
+                    psStringAppend(&content, "%s:_mosaicChip ", fpaItem->name);
+                    psMetadataAddStr(contents, PS_LIST_TAIL, fpaItem->name, 0, NULL, content);
+                    psFree(content);
+                }
+                psFree(fpaIter);
+                psMetadataAddMetadata(format, PS_LIST_TAIL, TABLE_OF_CONTENTS, PS_META_REPLACE,
+                                      "List of contents", contents);
+                psFree(contents);
+
+                psMetadata *chips = psMetadataAlloc(); // List of chip types, with cellName:cellType
+                psMetadataAddStr(chips, PS_LIST_TAIL, "_mosaicChip", 0, NULL,
+                                 "MosaickedCell:_mosaic");
+                psMetadataAddMetadata(format, PS_LIST_TAIL, CHIP_TYPES, PS_META_REPLACE,
+                                      "List of chip types", chips);
+                psFree(chips);
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "FPA") && !strcasecmp(extensionsItem->data.str, "NONE")) {
+                // List the contents on a single line
+                psString contentsLine = NULL; // Contents of the PHU
+                psMetadataIterator *fpaIter = psMetadataIteratorAlloc(fpa, PS_LIST_HEAD, NULL); // Iteratr
+                psMetadataItem *fpaItem;    // Item from iteration
+                while ((fpaItem = psMetadataGetAndIncrement(fpaIter))) {
+                    assert (fpaItem->type == PS_DATA_STRING);
+                    psStringAppend(&contentsLine, "%s:MosaickedCell:_mosaic ", fpaItem->name);
+                }
+                psFree(fpaIter);
+                psMetadataAddStr(format, PS_LIST_TAIL, TABLE_OF_CONTENTS, PS_META_REPLACE,
+                                 NULL, contentsLine);
+                psFree(contentsLine);
+                break;
+            }
+            if (!strcasecmp(phuItem->data.str, "CHIP") && !strcasecmp(extensionsItem->data.str, "NONE")) {
+                // XXX recode this to match the structure above (FPA/CHIP)?
+                // select and remove the old contents
+                psMetadata *contents = psMetadataLookupMetadata(NULL, format, TABLE_OF_CONTENTS); // File contents
+                if (!contents) {
+                    psError(PS_ERR_UNKNOWN, false, "Couldn't find %s in the camera format %s.\n",
+                            TABLE_OF_CONTENTS, formatsItem->name);
+                    return false;
+                }
+
+                // replace chip type with _mosaicChip
+                psMetadataIterator *contentIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL); // Iterator
+                psMetadataItem *contentItem;    // Item from iteration
+                while ((contentItem = psMetadataGetAndIncrement(contentIter))) {
+                    assert (contentItem->type == PS_DATA_STRING);
+                    char *ptr = strchr (contentItem->data.str, ':');
+                    assert (ptr);
+                    psString content = psStringNCopy (contentItem->data.str, ptr - contentItem->data.str);
+                    psStringAppend(&content, ":_mosaicChip ");
+                    psFree (contentItem->data.str);
+                    contentItem->data.str = content;
+                }
+                psFree(contentIter);
+
+                # if (0)
+                while (psListLength(contents->list) > 0) {
+                    psMetadataRemoveIndex(contents, PS_LIST_TAIL);
+                }
+
+                // update with the new contents
+                psMetadataIterator *fpaIter = psMetadataIteratorAlloc(fpa, PS_LIST_HEAD, NULL); // Iterator
+                psMetadataItem *fpaItem;    // Item from iteration
+                while ((fpaItem = psMetadataGetAndIncrement(fpaIter))) {
+                    assert (fpaItem->type == PS_DATA_STRING);
+                    psString content = NULL; // Content to add
+                    psStringAppend(&content, "%s:_mosaicChip ", fpaItem->name);
+                    psMetadataAddStr(contents, PS_LIST_TAIL, fpaItem->name, 0, NULL, content);
+                    psFree(content);
+                }
+                psFree(fpaIter);
+                # endif
+
+                psMetadata *chips = psMetadataAlloc(); // List of chip types, with cellName:cellType
+                psMetadataAddStr(chips, PS_LIST_TAIL, "_mosaicChip", 0, NULL,
+                                 "MosaickedCell:_mosaic");
+                psMetadataAddMetadata(format, PS_LIST_TAIL, CHIP_TYPES, PS_META_REPLACE,
+                                      "List of chip types", chips);
+                psFree(chips);
+                break;
+            }
+        default:
+            psAbort("Should never get here.\n");
+        }
+
+        // Fix the cell type
+        psMetadata *cells = psMetadataLookupMetadata(NULL, format, CELL_TYPES); // CELLS information
+        if (!cells) {
+            psError(PS_ERR_UNKNOWN, false, "Couldn't find CELLS of type METADATA in the camera format %s.\n", formatsItem->name);
+            return false;
+        }
+        psMetadata *cell = psMetadataAlloc(); // Cell information
+        psMetadataAddStr(cell, PS_LIST_TAIL, "CELL.TRIMSEC", 0, "Trim section", "TRIMSEC");
+        psMetadataAddStr(cell, PS_LIST_TAIL, "CELL.BIASSEC", 0, "Bias section", "BIASSEC");
+        psMetadataAddStr(cell, PS_LIST_TAIL, "CELL.TRIMSEC.SOURCE", 0, "Trim section source", "HEADER");
+        psMetadataAddStr(cell, PS_LIST_TAIL, "CELL.BIASSEC.SOURCE", 0, "Bias section source", "HEADER");
+        psMetadataAddMetadata(cells, PS_LIST_HEAD, "_mosaic", PS_META_REPLACE, "Mosaic cell information", cell);
+        psFree(cell);                   // Drop reference
+
+        // Update the concepts, so that they are all stored in the FITS headers, under headers of the same
+        // name as the concept
+        psMetadata *database = psMetadataLookupMetadata(&mdok, format, "DATABASE"); // DATABASE concepts
+        psMetadata *defaults = psMetadataLookupMetadata(&mdok, format, "DEFAULTS"); // DEFAULTS concepts
+        if (!mdok || !defaults) {
+            psWarning("Couldn't find DEFAULTS of type METADATA in the camera format %s.\n",
+                      formatsItem->name);
+            continue;
+        }
+        psMetadata *translation = psMetadataLookupMetadata(&mdok, format, "TRANSLATION"); // TRANSLATION info
+        if (!mdok || !translation) {
+            psWarning("Couldn't find TRANSLATION of type METADATA in the camera format %s.\n",
+                      formatsItem->name);
+            continue;
+        }
+
+        removeCellConceptsSources(translation);
+        removeCellConceptsSources(database);
+        removeCellConceptsSources(defaults);
+
+        psMetadata *conceptFormats = psMetadataLookupMetadata(&mdok, format, "FORMATS"); // Concepts formats
+
+        // Add in the positioning concepts
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CELL.XPARITY", 0, NULL, 1);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CELL.YPARITY", 0, NULL, 1);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CELL.X0",      0, NULL, 0);
+        psMetadataAddS32(defaults, PS_LIST_TAIL, "CELL.Y0",      0, NULL, 0);
+        if (conceptFormats) {
+            if (psMetadataLookup(conceptFormats, "CELL.X0")) {
+                psMetadataRemoveKey(conceptFormats, "CELL.X0");
+            }
+            if (psMetadataLookup(conceptFormats, "CELL.Y0")) {
+                psMetadataRemoveKey(conceptFormats, "CELL.Y0");
+            }
+        }
+
+        if (mosaicLevel == PM_FPA_LEVEL_FPA) {
+            removeChipConceptsSources(translation);
+            removeChipConceptsSources(database);
+            removeChipConceptsSources(defaults);
+            psMetadataAddS32(defaults, PS_LIST_TAIL, "CHIP.XPARITY", 0, NULL, 1);
+            psMetadataAddS32(defaults, PS_LIST_TAIL, "CHIP.YPARITY", 0, NULL, 1);
+            psMetadataAddS32(defaults, PS_LIST_TAIL, "CHIP.X0", 0, NULL, 0);
+            psMetadataAddS32(defaults, PS_LIST_TAIL, "CHIP.Y0", 0, NULL, 0);
+            if (conceptFormats) {
+                if (psMetadataLookup(conceptFormats, "CHIP.X0")) {
+                    psMetadataRemoveKey(conceptFormats, "CHIP.X0");
+                }
+                if (psMetadataLookup(conceptFormats, "CHIP.Y0")) {
+                    psMetadataRemoveKey(conceptFormats, "CHIP.Y0");
+                }
+            }
+        }
+
+    }
+    psFree(formatsIter);
+
+    // New camera MUST go to the head of the metadata, so that it will be recognised first
+    // The old camera doesn't contain the PSMOSAIC header, so it will match anything!
+    psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE,
+                          "Automatically generated", new);
+    psFree(newName);
+    psFree(new);
+
+    return true;
+}
+
+/*** Helper Functions ***/
+
+// Remove a concept from the list of sources.  Need to check to see if it exists first, to avoid a warning.
+static void removeConcept(psMetadata *source, // Source from which to remove concept
+                          const char *concept // Concept name to remove
+                         )
+{
+    assert(source);
+    assert(concept && strlen(concept) > 0);
+
+    if (psMetadataLookup(source, concept)) {
+        psMetadataRemoveKey(source, concept);
+    }
+
+    return;
+}
+
+// Remove certain concepts from the list of sources.  These concepts are important in the mosaicking process,
+// and are added explicitly to the defaults (elsewhere) so that the user can't get them wrong.
+static void removeCellConceptsSources(psMetadata *source // Source for concepts
+    )
+{
+    if (!source) {
+        return;
+    }
+
+    removeConcept(source, "CELL.BIASSEC");
+    removeConcept(source, "CELL.TRIMSEC");
+    removeConcept(source, "CELL.XPARITY");
+    removeConcept(source, "CELL.YPARITY");
+    removeConcept(source, "CELL.X0");
+    removeConcept(source, "CELL.Y0");
+
+    // For the sake of the defaults, include the .DEPEND
+    removeConcept(source, "CELL.XPARITY.DEPEND");
+    removeConcept(source, "CELL.YPARITY.DEPEND");
+    removeConcept(source, "CELL.X0.DEPEND");
+    removeConcept(source, "CELL.Y0.DEPEND");
+
+    return;
+}
+
+// Remove certain concepts from the list of sources.  These concepts are important in the mosaicking process,
+// and are added explicitly to the defaults (elsewhere) so that the user can't get them wrong.
+static void removeChipConceptsSources(psMetadata *source // Source for concepts
+    )
+{
+    if (!source) {
+        return;
+    }
+
+    removeConcept(source, "CHIP.XPARITY");
+    removeConcept(source, "CHIP.YPARITY");
+    removeConcept(source, "CHIP.X0");
+    removeConcept(source, "CHIP.Y0");
+
+    // For the sake of the defaults, include the .DEPEND
+    removeConcept(source, "CHIP.XPARITY.DEPEND");
+    removeConcept(source, "CHIP.YPARITY.DEPEND");
+    removeConcept(source, "CHIP.X0.DEPEND");
+    removeConcept(source, "CHIP.Y0.DEPEND");
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCamera.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCamera.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCamera.h	(revision 22322)
@@ -0,0 +1,70 @@
+/*  @file pmConfigCamera.h
+ *  @brief Camera Configuration functions
+ *
+ *  @author Paul Price, IfA
+ *  @author Eugene Magnier, IfA
+ *
+ *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-06 03:40:45 $
+ *  Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONFIG_CAMERA_H
+#define PM_CONFIG_CAMERA_H
+
+/// @addtogroup Config Configuration System
+/// @{
+
+// Return the name of the original ("root") camera
+//
+// The root name is the name of the camera before it was made into a derivative (e.g., skycell, chip, fpa).
+psString pmConfigCameraRootName(const char *name // Name of camera
+    );
+
+// Return the name of the Skycell derivative camera
+psString pmConfigCameraSkycellName(const char *name // Name of camera
+    );
+
+// Return the name of the Chip derivative camera
+psString pmConfigCameraChipName(const char *name // Name of camera
+    );
+
+// Return the name of the FPA derivative camera
+psString pmConfigCameraFPAName(const char *name // Name of camera
+    );
+
+// Generate a skycell version of a camera configuration
+bool pmConfigGenerateSkycellVersion(psMetadata *oldCameras, // Old list of camera configurations
+                                    psMetadata *newCameras, // New list of camera configurations
+                                    const char *name, // Name of original camera configuration
+                                    const psMetadata *site // The site configuration
+    );
+
+/// Generate the skycell version of a particular camera configuration
+bool pmConfigCameraSkycellVersion(psMetadata *site, // The site configuration
+                                  const char *name // Name of the un-mosaicked camera
+    );
+
+
+/// Generate skycell versions of all the camera configurations
+bool pmConfigCameraSkycellVersionsAll(psMetadata *site // Site configuration
+    );
+
+// Generate a mosaicked version of a camera configuration
+bool pmConfigGenerateMosaickedVersion(psMetadata *oldCameras, // Old list of camera configurations
+                                      psMetadata *newCameras, // New list of camera configurations
+                                      const char *name, // Name of original camera configuration
+                                      pmFPALevel mosaicLevel // Level to which we are mosaicking
+    );
+
+/// Generate the chip mosaicked version of a particular camera configuration
+bool pmConfigCameraMosaickedVersions(psMetadata *site, // Site configuration
+                                     const char *name // Name of the un-mosaicked camera
+    );
+
+/// Generate chip- and fpa-mosaicked versions of all the camera configurations
+bool pmConfigCameraMosaickedVersionsAll(psMetadata *site // Site configuration
+    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCommand.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCommand.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCommand.c	(revision 22322)
@@ -0,0 +1,51 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmConfig.h"
+#include "pmConfigCommand.h"
+
+bool pmConfigDatabaseCommand(psString *command, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(command, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // Connection details
+    psMetadataItem *server = pmConfigUserSite(config, "DBSERVER",   PS_DATA_STRING);
+    psMetadataItem *user   = pmConfigUserSite(config, "DBUSER",     PS_DATA_STRING);
+    psMetadataItem *pass   = pmConfigUserSite(config, "DBPASSWORD", PS_DATA_STRING);
+    psMetadataItem *name   = pmConfigUserSite(config, "DBNAME",     PS_DATA_STRING);
+
+    if (!server || !user || !pass || !name) {
+        psWarning("Cannot find DBSERVER/DBUSER/DBPASSWORD/DBNAME in user or site configuration: "
+                  "unable to connect to database.");
+        psErrorClear();
+        return NULL;
+    }
+
+    psStringAppend(command, " -dbserver %s -dbname %s -dbuser %s -dbpassword %s",
+                   server->data.str, name->data.str, user->data.str, pass->data.str);
+
+    return true;
+}
+
+
+bool pmConfigTraceCommand(psString *command)
+{
+    PS_ASSERT_PTR_NON_NULL(command, false);
+
+    psMetadata *levels = psTraceLevels(); // Metadata levels
+    psMetadataIterator *iter = psMetadataIteratorAlloc(levels, PS_LIST_HEAD, NULL); // Iterator for levels
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        assert(item->type == PS_TYPE_S32);
+        psStringAppend(command, " -trace %s %d", item->name, item->data.S32);
+    }
+    psFree(iter);
+    psFree(levels);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCommand.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCommand.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigCommand.h	(revision 22322)
@@ -0,0 +1,16 @@
+#ifndef PM_CONFIG_COMMAND_H
+#define PM_CONFIG_COMMAND_H
+
+/// Extend a command-line to include the necessary database flags
+///
+/// The command-line is extended with -dbserver, -dbname, -dbuser, -dbpassword
+bool pmConfigDatabaseCommand(psString *command, ///< Command to extend
+                             const pmConfig *config ///< Configuration
+                            );
+
+/// Extend a command-line to propagate the trace flags
+///
+/// The command-line is extended with -trace
+bool pmConfigTraceCommand(psString *command ///< Command to extend
+                         );
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigDump.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigDump.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigDump.c	(revision 22322)
@@ -0,0 +1,124 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmFPALevel.h"
+#include "pmFPA.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmConfigCamera.h"
+
+#include "pmConfigDump.h"
+
+// Cull entries in the metadata, ignoring the ones listed.
+static bool configCull(psMetadata *md,  // Configuration metadata from which to cull
+                       const psArray *list // List of items NOT to cull
+    )
+{
+    PS_ASSERT_METADATA_NON_NULL(md, false);
+    PS_ASSERT_ARRAY_NON_NULL(list, false);
+
+    psHash *keep = psHashAlloc(list->n); // Hash with strings to keep
+    for (int i = 0; i < keep->n; i++) {
+        psHashAdd(keep, list->data[i], list->data[i]);
+    }
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (!psHashLookup(keep, item->name)) {
+            psMetadataRemoveKey(md, item->name);
+        }
+    }
+    psFree(iter);
+
+    psFree(keep);
+
+    return true;
+}
+
+bool pmConfigRecipesCull(pmConfig *config, const char *save)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    if (!save || strlen(save) == 0) {
+        return true;
+    }
+
+    psArray *keep = psStringSplitArray(save, " ,;", false); // List of items to keep
+
+    bool result = configCull(config->recipes, keep); // Result of culling
+    psFree(keep);
+
+    return result;
+}
+
+bool pmConfigCamerasCull(pmConfig *config, const char *additional)
+{
+      PS_ASSERT_PTR_NON_NULL(config, false);
+
+      psMetadata *cameras = psMetadataLookupMetadata(NULL, config->system, "CAMERAS"); // Known cameras
+      if (!cameras) {
+          psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find CAMERAS in the system configuration.\n");
+          return NULL;
+      }
+
+      psArray *keep = NULL;             // List of cameras to keep
+      if (additional) {
+          keep = psStringSplitArray(additional, " ,;", false);
+      } else {
+          keep = psArrayAllocEmpty(1);
+      }
+      psArrayAdd(keep, 1, config->cameraName);
+
+      int numKeep = keep->n;            // Number of cameras to keep
+      for (int i = 0; i < numKeep; i++) {
+          psString orig = keep->data[i];// Original name
+          psString root = pmConfigCameraRootName(orig); // Camera root name
+          psString chip = pmConfigCameraChipName(config->cameraName); // Chip-mosaicked name
+          psString fpa  = pmConfigCameraFPAName(config->cameraName); // FPA-mosaicked name
+          psString sky  = pmConfigCameraSkycellName(config->cameraName); // Skycell name
+
+          // Just in case we weren't given the root name
+          psFree(keep->data[i]);
+          keep->data[i] = root;
+
+          psArrayAdd(keep, 1, chip);
+          psArrayAdd(keep, 1, fpa);
+          psArrayAdd(keep, 1, sky);
+
+          psFree(chip);
+          psFree(fpa);
+          psFree(sky);
+      }
+
+      bool result = configCull(cameras, keep); // Result of culling
+      psFree(keep);
+
+      return result;
+}
+
+
+bool pmConfigDump(const pmConfig *config, const pmFPA *source, const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_STRING_NON_EMPTY(filename, false);
+
+    psString resolved = pmConfigConvertFilename(filename, config, true, false); // Resolved filename
+
+    if (!psMetadataConfigWrite(config->user, resolved)) {
+        psError(PS_ERR_IO, false, "Unable to dump configuration to %s", filename);
+        psFree(resolved);
+        return false;
+    }
+
+    psFree(resolved);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigDump.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigDump.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigDump.h	(revision 22322)
@@ -0,0 +1,40 @@
+/*  @file pmConfigDump.h
+ *  @brief Configuration dumping function
+ *
+ *  @author Paul Price, IfA
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-05 22:41:58 $
+ *  Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONFIG_DUMP_H
+#define PM_CONFIG_DUMP_H
+
+#include <pmConfig.h>
+#include <pmFPA.h>
+
+/// @addtogroup Config Configuration System
+/// @{
+
+/// Cull recipes from the configuration file, apart from the ones listed
+bool pmConfigRecipesCull(pmConfig *config, ///< Configuration
+                         const char *save ///< List of recipes to save, comma-separated
+    );
+
+/// Cull cameras from the configuration file, apart from the one in use
+bool pmConfigCamerasCull(pmConfig *config, ///< Configuration
+                         const char *additional ///< List of additional cameras to save, comma-separated
+    );
+
+/// Dump the configuration to a file
+///
+bool pmConfigDump(const pmConfig *config, ///< Configuration to dump
+                  const pmFPA *source,    ///< Source FPA, defines the level for the file rule
+                  const char *filename    ///< Output file name
+    );
+
+
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigMask.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigMask.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigMask.c	(revision 22322)
@@ -0,0 +1,501 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfigMask.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static psMaskType maskGet(const psMetadata *source, // Source of masks
+                          const char *masks // Mask values to get
+                          )
+{
+    psMaskType mask = 0;                // Mask value, to return
+
+    psArray *names = psStringSplitArray(masks, " ,;", false); // Array of symbolic names
+    for (int i = 0; i < names->n; i++) {
+        const char *name = names->data[i]; // Symbolic name of interest
+        bool mdok;                      // Status of MD lookup
+        psMaskType value = psMetadataLookupU8(&mdok, source, name);
+        if (!mdok) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find mask value for %s", name);
+            psFree(names);
+            return 0;
+        }
+        mask |= value;
+    }
+    psFree(names);
+
+    return mask;
+}
+
+// maskSetValues examine named mask values and set the bits for maskValue and markValue.
+// Ensures that the below-named bad mask values are set, and calculates the mask value to catch them all
+// Ensure that the below-named other mask values are set (to 0x00 if necessary)
+
+// List of mask names for "bad" (i.e., mask me please) pixels
+static const char *badMaskNames[] = { "DETECTOR", // Something is wrong with the detector
+                                      "DARK", // Pixel doesn't dark-subtract properly
+                                      "FLAT", // Pixel doesn't flat-field properly
+                                      "BLANK", // Pixel doesn't contain valid data
+                                      "RANGE",// Pixel is out-of-range of linearity
+                                      "SAT",  // Pixel is saturated
+                                      // "LOW",  // Pixel is low
+                                      // "CONV", // Pixel is bad after convolution with a bad pixel
+                                      "BAD", // Pixel is low
+                                      "BAD.WARP", // Pixel is bad after convolution with a bad pixel
+                                      "CR",   // Pixel contains a cosmic ray
+                                      "GHOST",// Pixel contains an optical ghost
+                                      NULL // End marker
+};
+// Fallback names in case a bad mask name is not defined
+static const char *fallbackMaskNames[] = { NULL, // DETECTOR
+                                           "DETECTOR", // DARK
+                                           "DETECTOR", // FLAT
+                                           "DETECTOR", // BLANK
+                                           NULL, // RANGE
+                                           "RANGE", // SAT
+                                           "RANGE", // LOW
+                                           NULL, // CONV
+                                           NULL, // CR
+                                           NULL, // GHOST
+};
+// Default values in case a bad mask name and its fallback is not defined
+static const psMaskType defaultMask[] = { 0x00, // DETECTOR
+                                          0x00, // DARK
+                                          0x01, // FLAT
+                                          0x01, // BLANK
+                                          0x00, // RANGE
+                                          0x01, // SAT
+                                          0x01, // LOW
+                                          0x01, // CONV
+                                          0x00, // CR
+                                          0x00  // GHOST
+};
+// Other mask names to ensure exist; these shouldn't be combined in the MASK.VALUE
+static const char *otherMaskNames[] = { // "POOR", // Pixel is poor after convolution with a bad pixel
+                                        "POOR.WARP", // Pixel is poor after convolution with a bad pixel
+                                        NULL // End marker
+};
+
+static bool maskSetValues(psMaskType *outMaskValue, // Value of MASK.VALUE, returned
+                          psMaskType *outMarkValue, // Value of MARK.VALUE, returned
+                          psMetadata *source  // Source of mask bits
+                          )
+{
+    PS_ASSERT_METADATA_NON_NULL(source, false);
+
+    // Ensure all the bad mask names exist, and set the value to catch all bad pixels
+    psMaskType maskValue = 0;           // Value to mask to catch all the bad pixels
+    for (int i = 0; badMaskNames[i]; i++) {
+        const char *name = badMaskNames[i]; // Name for mask
+        const char *fallback = fallbackMaskNames[i]; // Fallback for mask
+
+        bool mdok;                      // Status of MD lookup
+        psMaskType value = psMetadataLookupU8(&mdok, source, name); // Value of mask
+        if (!value) {
+            if (fallback) {
+                value = psMetadataLookupU8(&mdok, source, fallback);
+            }
+            if (!value) {
+                value = defaultMask[i];
+            }
+            psMetadataAddU8(source, PS_LIST_TAIL, name, PS_META_REPLACE, NULL, value);
+        }
+        maskValue |= value;
+    }
+
+    // Ensure all the other mask names exist
+    for (int i = 0; otherMaskNames[i]; i++) {
+        const char *name = otherMaskNames[i]; // Name for mask
+        bool mdok;                      // Status of MD lookup
+        psMaskType value = psMetadataLookupU8(&mdok, source, name); // Value of mask
+        if (!value) {
+            psMetadataAddU8(source, PS_LIST_TAIL, name, PS_META_REPLACE, NULL, 0x00);
+        }
+    }
+
+    // search for an unset bit to use for MARK:
+    psMaskType markValue = 0x80;
+
+    int nBits = sizeof(psMaskType) * 8;
+    for (int i = 0; !markValue && (i < nBits); i++) {
+        if (maskValue & markValue) {
+            markValue >>= 1;
+        } else {
+            markValue = markValue;
+        }
+    }
+    if (!markValue) {
+        psError (PS_ERR_UNKNOWN, true, "Unable to define the MARK bit mask: all bits taken!");
+        return false;
+    }
+
+    // update the list with the results
+    psMetadataAddU8(source, PS_LIST_TAIL, "MASK.VALUE", PS_META_REPLACE, NULL, maskValue);
+    psMetadataAddU8(source, PS_LIST_TAIL, "MARK.VALUE", PS_META_REPLACE, NULL, markValue);
+
+    if (outMaskValue) {
+        *outMaskValue = maskValue;
+    }
+    if (outMarkValue) {
+        *outMarkValue = markValue;
+    }
+
+    return true;
+}
+
+// Remove from the header keywords starting with the provided string
+static int maskRemoveHeader(psMetadata *header, // Header from which to remove keywords
+                            const char *start // Remove keywords that start with this string
+                            )
+{
+    psString regex = NULL;              // Regular expression for keywords
+    psStringAppend(&regex, "^%s[0-9][0-9]", start);
+    psMetadataIterator *iter = psMetadataIteratorAlloc(header, PS_LIST_HEAD, regex); // Iterator
+    psFree(regex);
+    psMetadataItem *item;               // Item from iteration
+    int num = 0;                        // Number of items removed
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        psMetadataRemoveKey(header, item->name);
+        num++;
+    }
+    psFree(iter);
+    return num;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// FPA version of mask functions.  These are not ready to go yet.
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if 0
+bool pmFPAMaskSetValues(psMaskType *outMaskValue, psMaskType *outMarkValue, pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    return maskSetValues(outMaskValue, outMarkValue, fpa->masks);
+}
+
+psMaskType pmFPAMaskGet(const pmFPA *fpa, const char *masks, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, 0);
+    PS_ASSERT_STRING_NON_EMPTY(masks, 0);
+    PS_ASSERT_PTR_NON_NULL(config, 0);
+
+    if (fpa->masks) {
+        return maskGet(fpa->masks, masks);
+    }
+    return pmConfigMaskGet(masks, config);
+}
+
+bool pmFPAMaskSet(pmFPA *fpa, const char *maskName, psMaskType maskValue)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, 0);
+    PS_ASSERT_STRING_NON_EMPTY(maskName, false);
+
+    if (!fpa->masks) {
+        fpa->masks = psMetadataAlloc();
+    }
+    return psMetadataAddU8(fpa->masks, PS_LIST_TAIL, maskName, PS_META_REPLACE, NULL, maskValue);
+}
+
+bool pmFPAMaskReadHeader(pmFPA *fpa, const psMetadata *header, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    if (!fpa->masks) {
+        fpa->masks = psMetadataAlloc();
+    }
+
+    bool mdok;                          // Status of MD lookup
+    int numMask = psMetadataLookupS32(&mdok, header, "MSKNUM"); // Number of mask values in header
+    if (!mdok) {
+        if (psMetadataLookupBool(&mdok, config->camera, "MASK.FORCE")) {
+            psWarning("No mask values in header.  Assuming MASKS recipe is accurate because of MASK.FORCE");
+            numMask = 0;
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "Unable to find MSKNUM in header.");
+            return false;
+        }
+    }
+
+    char namekey[80];                   // Keyword name for symbolic name of mask entry
+    char valuekey[80];                  // Keyword name for value of mask entry
+    for (int i = 0; i < numMask; i++) {
+        snprintf(namekey,  64, "MSKNAM%02d", i);
+        snprintf(valuekey, 64, "MSKVAL%02d", i);
+
+        char *name = psMetadataLookupStr(&mdok, header, namekey);
+        if (!mdok || !name) {
+            psWarning("Unable to find header keyword %s when parsing mask", namekey);
+            continue;
+        }
+        psU8 bit = psMetadataLookupU8(&mdok, header, valuekey);
+        if (!mdok) {
+            psWarning("Unable to find header keyword %s when parsing mask", namekey);
+            continue;
+        }
+
+        // XXX validate that bit is a 2^n value?
+
+        psMetadataItem *item = psMetadataLookup(fpa->masks, name); // Item in recipe with current value
+        if (item) {
+            psAssert(item->type == PS_TYPE_MASK, "Mask entry %s is not of a mask type (%x)",
+                     name, item->type);
+            if (item->data.PS_TYPE_MASK_DATA != bit) {
+                psWarning("New mask entry %s doesn't match previously loaded entry: %x vs %x",
+                          name, bit, item->data.PS_TYPE_MASK_DATA);
+            }
+        } else {
+            psMetadataAddU8(fpa->masks, PS_LIST_TAIL, name, 0, NULL, bit);
+        }
+    }
+
+    // Now copy everything else from the recipe
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(recipe, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (item->type != PS_TYPE_MASK) {
+            psWarning("Recipe mask entry %s is not of a mask type (%x)", item->name, item->type);
+            continue;
+        }
+        if (!psMetadataLookup(fpa->masks, item->name)) {
+            psMetadataAddU8(fpa->masks, PS_LIST_TAIL, item->name, 0, item->comment,
+                            item->data.PS_TYPE_MASK_DATA);
+        }
+    }
+    psFree(iter);
+
+    return true;
+}
+
+
+bool pmFPAMaskWriteHeader(psMetadata *header, const pmFPA *fpa)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    maskRemoveHeader(header, "MSKNAM");
+    maskRemoveHeader(header, "MSKVAL");
+    if (psMetadataLookup(header, "MSKNUM")) {
+        psMetadataRemoveKey(header, "MSKNUM");
+    }
+
+    char namekey[80], valuekey[80];     // Mask name and mask value header keywords
+    int numMask = 0;                    // Number of mask entries
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(fpa->masks, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (item->type != PS_TYPE_MASK) {
+            psWarning("mask recipe entry %s is not of a mask type (%x)", item->name, item->type);
+            continue;
+        }
+
+        snprintf(namekey,  64, "MSKNAM%02d", numMask);
+        snprintf(valuekey, 64, "MSKVAL%02d", numMask);
+
+        psMetadataAddStr(header, PS_LIST_TAIL, namekey, 0, "Bitmask bit name", item->name);
+        psMetadataAddU8(header, PS_LIST_TAIL, valuekey, 0, "Bitmask bit value", item->data.PS_TYPE_MASK_DATA);
+        numMask++;
+    }
+    psFree(iter);
+
+    return psMetadataAddS32(header, PS_LIST_TAIL, "MSKNUM", 0, "Number of named mask entries", numMask);
+}
+
+#endif
+
+
+psMaskType pmConfigMaskGet(const char *masks, const pmConfig *config)
+{
+    psAssert(config, "Require configuration");
+    PS_ASSERT_STRING_NON_EMPTY(masks, 0);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return 0;
+    }
+    return maskGet(recipe, masks);
+}
+
+
+bool pmConfigMaskSet(const pmConfig *config, const char *maskName, psMaskType maskValue)
+{
+    psAssert(config, "Require configuration");
+    PS_ASSERT_STRING_NON_EMPTY(maskName, false);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    return psMetadataAddU8(recipe, PS_LIST_TAIL, maskName, PS_META_REPLACE, NULL, maskValue);
+}
+
+
+// replace the named masks in the recipe with values in the header:
+// replace only the names in the header in the recipe
+bool pmConfigMaskReadHeader(pmConfig *config, const psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    bool status = false;
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    // MASK.VALUE and MARK.VALUE aren't usually set in the recipe, but may be set in the header: create fake
+    // versions so that it won't complain later
+    if (!psMetadataLookup(recipe, "MASK.VALUE")) {
+        psMetadataAddU8(recipe, PS_LIST_TAIL, "MASK.VALUE", 0, "Bits to mask", 0);
+    }
+    if (!psMetadataLookup(recipe, "MARK.VALUE")) {
+        psMetadataAddU8(recipe, PS_LIST_TAIL, "MARK.VALUE", 0, "Bits for marking", 0);
+    }
+
+    int nMask = psMetadataLookupS32(&status, header, "MSKNUM");
+    if (!status) {
+        if (psMetadataLookupBool(&status, config->camera, "MASK.FORCE")) {
+            psWarning("No mask values in header.  Assuming MASKS recipe is accurate because of MASK.FORCE");
+            return true;
+        }
+        psError(PS_ERR_UNKNOWN, true, "Unable to find MSKNUM in header.");
+        return false;
+    }
+
+    char namekey[80];                   // Keyword name for symbolic name of mask entry
+    char valuekey[80];                  // Keyword name for value of mask entry
+    for (int i = 0; i < nMask; i++) {
+        snprintf(namekey,  64, "MSKNAM%02d", i);
+        snprintf(valuekey, 64, "MSKVAL%02d", i);
+
+        char *name = psMetadataLookupStr(&status, header, namekey);
+        if (!status || !name) {
+            psWarning("Unable to find header keyword %s when parsing mask", namekey);
+            continue;
+        }
+        psU8 bit = psMetadataLookupU8(&status, header, valuekey);
+        if (!status) {
+            psWarning("Unable to find header keyword %s when parsing mask", namekey);
+            continue;
+        }
+
+        // XXX validate that bit is a 2^n value?
+
+        psString nameAlready = NULL;    // Name of key with ".ALREADY" added
+        psStringAppend(&nameAlready, "%s.ALREADY", name);
+        bool already = psMetadataLookupBool(&status, recipe, nameAlready); // Already read this one?
+
+        psMetadataItem *item = psMetadataLookup(recipe, name); // Item in recipe with current value
+        if (item && item->type != PS_TYPE_MASK) {
+            psWarning("Mask recipe entry is not of a mask type (%x)", item->type);
+            item->type = PS_TYPE_MASK;
+        }
+
+        if (already) {
+            if (item && item->data.U8 != bit) {
+                psWarning("New mask recipe entry doesn't match previously loaded entry: %x vs %x",
+                          bit, item->data.U8);
+            }
+        } else {
+            psMetadataAddBool(recipe, PS_LIST_TAIL, nameAlready, 0, "Already read this mask value", true);
+        }
+
+        if (!item) {
+            psWarning("Mask recipe entry %s not in recipe\n", name);
+            psMetadataAddU8(recipe, PS_LIST_TAIL, name, 0, "Bitmask bit value", bit);
+        } else {
+            item->data.U8 = bit;
+        }
+
+        psFree(nameAlready);
+    }
+
+
+    return true;
+}
+
+
+
+// write the named mask bits to the header
+bool pmConfigMaskWriteHeader(const pmConfig *config, psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    maskRemoveHeader(header, "MSKNAM");
+    maskRemoveHeader(header, "MSKVAL");
+    if (psMetadataLookup(header, "MSKNUM")) {
+        psMetadataRemoveKey(header, "MSKNUM");
+    }
+
+    char namekey[80];
+    char valuekey[80];
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    int nMask = 0;
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(recipe, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (strcmp(item->name + strlen(item->name) - strlen(".ALREADY"), ".ALREADY") == 0) {
+            continue;
+        }
+
+        if (item->type != PS_DATA_U8) {
+            psWarning("mask recipe entry %s is not a bit value\n", item->name);
+            continue;
+        }
+
+        snprintf(namekey,  64, "MSKNAM%02d", nMask);
+        snprintf(valuekey, 64, "MSKVAL%02d", nMask);
+
+        psMetadataAddStr(header, PS_LIST_TAIL, namekey, 0, "Bitmask bit name", item->name);
+        psMetadataAddU8(header, PS_LIST_TAIL, valuekey, 0, "Bitmask bit value", item->data.U8);
+        nMask++;
+    }
+    psFree(iter);
+
+    psMetadataAddS32(header, PS_LIST_TAIL, "MSKNUM", 0, "Bitmask bit count", nMask);
+    return true;
+}
+
+
+bool pmConfigMaskSetBits(psMaskType *outMaskValue, psMaskType *outMarkValue, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    return maskSetValues(outMaskValue, outMarkValue, recipe);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigMask.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigMask.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigMask.h	(revision 22322)
@@ -0,0 +1,40 @@
+/*  @file pmConfigMask.h
+ *  @brief Mask configuration functions
+ *
+ *  @author Paul Price, IfA
+ *
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-07-17 20:37:20 $
+ *  Copyright 2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONFIG_MASK_H
+#define PM_CONFIG_MASK_H
+
+#include <pslib.h>
+#include <pmConfig.h>
+
+#define PM_MASKS_RECIPE "MASKS"
+
+/// @addtogroup Config Configuration System
+/// @{
+
+/// Return a mask value given a list of symbolic names
+///
+/// The mask values are derived from the MASKS recipe
+psMaskType pmConfigMaskGet(const char *masks, ///< List of symbolic names, space/comma delimited
+                           const pmConfig *config ///< Configuration
+    );
+
+bool pmConfigMaskSet(const pmConfig *config, const char *maskName, psMaskType maskValue);
+
+// replace the named masks in the recipe with values in the header:
+// replace only the names in the header in the recipe
+bool pmConfigMaskReadHeader(pmConfig *config, const psMetadata *header);
+
+// write the named mask bits to the header
+bool pmConfigMaskWriteHeader(const pmConfig *config, psMetadata *header);
+
+bool pmConfigMaskSetBits(psMaskType *outMaskValue, psMaskType *outMarkValue, const pmConfig *config);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigRecipes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigRecipes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigRecipes.c	(revision 22322)
@@ -0,0 +1,619 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <assert.h>
+#include <pslib.h>
+#include "pmConfig.h"
+#include "pmConfigRecipes.h"
+
+static bool loadRecipeSystem(bool *status, pmConfig *config, psMetadata *source);
+static bool loadRecipeCamera(bool *status, pmConfig *config, psMetadata *source);
+static bool loadRecipeSymbols(bool *status, pmConfig *config, pmRecipeSource source);
+static bool loadRecipeFromArguments(bool *status, pmConfig *config);
+static bool loadRecipeOptions(bool *status, pmConfig *config);
+static bool mergeRecipeCamera(bool *status, pmConfig *config);
+
+// use this function to select the options structure for the specified recipe
+// add additional command-line options to this metadata (before parsing the camera)
+psMetadata *pmConfigRecipeOptions (pmConfig *config, char *recipeName)
+{
+    bool success;
+
+    // select or create the OPTIONS folder
+    psMetadata *options = psMetadataLookupMetadata(&success, config->arguments, "OPTIONS");
+    if (!options) {
+        options = psMetadataAlloc ();
+        success = psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "OPTIONS",  PS_DATA_METADATA, "",
+                                    options);
+        assert (success); // type mismatch : OPTIONS already defined but wrong type
+        psFree (options); // drop extra reference
+    }
+
+    // look for the recipe defined in recipes
+    // if the recipe is already defined in config->arguments:OPTIONS, supplement
+    // save the recipe options onto config->arguments:RECIPES
+    psMetadata *recipe = psMetadataLookupMetadata(&success, options, recipeName);
+    if (!recipe) {
+        recipe = psMetadataAlloc();
+        success = psMetadataAddPtr(options, PS_LIST_TAIL, recipeName,  PS_DATA_METADATA, "", recipe);
+        assert (success); // type mismatch : OPTIONS already defined but wrong type
+        psFree (recipe);  // drop extra reference
+    }
+    return recipe;
+}
+
+// this function may be called several times.  it attempts to load the recipe data from one of three
+// locations: config->system, config->camera, and argv.  We cannot read the recipes from
+// config->camera until a camera has been read BUT, the argv recipes must override the camera and
+// system recipes.  This command strips the argv elements it uses from the argv list.
+bool pmConfigReadRecipes(pmConfig *config, pmRecipeSource source)
+{
+    bool status;
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    if (!config->recipes) {
+        config->recipes = psMetadataAlloc();
+    }
+
+    // Read the recipe file names from the system configuration and camera configuration
+    // It is an error for config->system:recipes not to exist.  all programs install their
+    // master recipe files in the system:recipe location when they are built.
+    psAssert(config->system, "base config data defined");
+    if (source & PM_RECIPE_SOURCE_SYSTEM) {
+        if (!loadRecipeSystem(&status, config, config->system)) {
+            psError(PS_ERR_IO, false, "Failed to read recipes from system config");
+            return false;
+        }
+        psTrace ("psModules.config", 3, "read recipes from system config");
+    }
+
+    // camera-specific recipes are not required : note the absence with a message
+    // camera-specific recipes may be read for a specified camera (in pmConfigRead) or
+    // for an identified camera (in pmConfigCameraFormatFromHeader).  the second
+    // set should not override the first set
+    if (config->camera && (source & PM_RECIPE_SOURCE_CAMERA) &&
+        !(config->recipesRead & PM_RECIPE_SOURCE_CAMERA)) {
+        if (!loadRecipeCamera(&status, config, config->camera)) {
+            psError(PS_ERR_IO, false, "Failed to read recipes from camera config");
+            return false;
+        }
+        if (status) {
+            psTrace ("psModules.config", 3, "read recipes from camera config");
+        } else {
+            psLogMsg ("psModules.config", PS_LOG_DETAIL, "no recipe supplied by camera config");
+        }
+    }
+
+    // merge camera and sytem recipes, apply recipes loaded into config->arguments based on command-line
+    // arguments
+    if (config->arguments && (source & PM_RECIPE_SOURCE_CL)) {
+
+        // update the system-level recipes with the symbolically-defined recipes
+        if (!loadRecipeSymbols(&status, config, PM_RECIPE_SOURCE_SYSTEM)) {
+            psError(PS_ERR_IO, false, "Failed to read recipes from symbolic references");
+            return false;
+        }
+        if (status) {
+            psTrace ("psModules.config", 3, "read recipes from symbolic references");
+        } else {
+            psLogMsg ("psModules.config", PS_LOG_DETAIL, "no recipe supplied by symbolic reference");
+        }
+
+        // merge the SYSTEM and CAMERA recipes
+        if (!mergeRecipeCamera(&status, config)) {
+            psError(PS_ERR_IO, false, "Failed to read recipes from symbolic references");
+            return false;
+        }
+        psLogMsg ("psModules.config", PS_LOG_DETAIL, "merged camera recipes with system recipes");
+
+        // load recipe-files specified on the command line
+        if (!loadRecipeFromArguments(&status, config)) {
+            psError(PS_ERR_IO, false, "Failed to read recipes from command-line arguments");
+            return false;
+        }
+        if (status) {
+            psTrace ("psModules.config", 3, "read recipes from command-line arguments");
+        } else {
+            psLogMsg ("psModules.config", PS_LOG_DETAIL, "no recipe supplied on command-line arguments");
+        }
+
+        // update the system-level recipes with the symbolically-defined recipes
+        if (!loadRecipeSymbols(&status, config, PM_RECIPE_SOURCE_CAMERA)) {
+            psError(PS_ERR_IO, false, "Failed to read recipes from symbolic references");
+            return false;
+        }
+        if (status) {
+            psTrace ("psModules.config", 3, "read recipes from symbolic references");
+        } else {
+            psLogMsg ("psModules.config", PS_LOG_DETAIL, "no recipe supplied by symbolic reference");
+        }
+
+        // override any specific values with values from the command line
+        if (!loadRecipeOptions(&status, config)) {
+            psError(PS_ERR_IO, false, "Failed to read recipes from symbolic references");
+            return false;
+        }
+        if (status) {
+            psTrace ("psModules.config", 3, "read recipes from command-line arguments");
+        } else {
+            psLogMsg ("psModules.config", PS_LOG_DETAIL, "no recipe supplied on command-line arguments");
+        }
+    }
+    return true;
+}
+
+// search for options of the form -D KEY VALUE or -D RECIPE:KEY VALUE
+bool pmConfigLoadRecipeOptions (int *argc, char **argv, pmConfig *config, char *flag)
+{
+    bool success;
+    int argNum;
+
+    // save the recipe options onto config->arguments:OPTIONS
+    // increment so we can free below (is a NOP if 'options' is NULL)
+    psMetadata *options = psMetadataLookupMetadata(&success, config->arguments, "OPTIONS");
+    if (!options) {
+        options = psMetadataAlloc();
+        success = psMetadataAddPtr(config->arguments, PS_LIST_TAIL, "OPTIONS",  PS_DATA_METADATA,
+                                   "Command-line options specified with -D", options);
+        assert (success); // type mismatch : OPTIONS already defined but wrong type
+        psFree (options); // drop extra reference
+    }
+
+    // -D key value (all added as string)
+    while ((argNum = psArgumentGet (*argc, argv, flag))) {
+        psArgumentRemove (argNum, argc, argv);
+
+        // do we have enough arguments?
+        if (argNum + 1 >= *argc) {
+            psError(PS_ERR_IO, true, "insufficient parameters for command-line argument -D");
+            return false;
+        }
+
+        // is a target recipe specified?
+        const char *recipeName = NULL;
+        char *key;
+        psArray *words = psStringSplitArray(argv[argNum], ":", false);
+        switch (words->n) {
+        case 1:
+            recipeName = config->defaultRecipe;
+            if (!config->defaultRecipe) {
+                psError(PS_ERR_IO, true,
+                        "syntax error in parameter: no default recipe available; must specify recipe");
+                return false;
+            }
+            key = words->data[0];
+            break;
+        case 2:
+            recipeName = words->data[0];
+            key = words->data[1];
+            break;
+        default:
+            psError(PS_ERR_IO, true, "syntax error in parameter");
+            return false;
+        }
+
+        // if this recipe is already defined in options, supplement
+        psMetadata *recipe = psMetadataLookupMetadata(&success, options, recipeName);
+        if (!recipe) {
+            recipe = psMetadataAlloc();
+            success = psMetadataAddPtr(options, PS_LIST_TAIL, recipeName,  PS_DATA_METADATA, "", recipe);
+            assert (success); // type mismatch : recipe already defined but wrong type
+            psFree (recipe); // drop extra reference
+        }
+
+        bool valid = false;
+        if (!strcmp (flag, "-D")) {
+            psMetadataAddStr (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", argv[argNum+1]);
+            valid = true;
+        }
+        if (!strcmp (flag, "-Di")) {
+            psMetadataAddS32 (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", atoi(argv[argNum+1]));
+            valid = true;
+        }
+        if (!strcmp (flag, "-Df")) {
+            psMetadataAddF32 (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", atof(argv[argNum+1]));
+            valid = true;
+        }
+        if (!strcmp (flag, "-Db")) {
+            if (!strcasecmp (argv[argNum+1], "true")) {
+                psMetadataAddBool (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", true);
+            } else {
+                psMetadataAddBool (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", false);
+            }
+            valid = true;
+        }
+        psFree (words);
+        assert (valid);  // flag may be: -D, -Df, -Di, -Db
+
+        psArgumentRemove (argNum, argc, argv);
+        psArgumentRemove (argNum, argc, argv);
+    }
+    return true;
+}
+
+// examine command-line arguments for -recipe RECIPE SYMBOLIC-NAME or -recipe-file RECIPE FILENAME
+// in the first case, the symbolic lookup is saved on config->recipeSymbols
+//   for later interpolation (pmConfigReadRecipes with option CL)
+// in the second case, read as metadata and save on config->arguments with name = KEY
+bool pmConfigLoadRecipeArguments (int *argc, char **argv, pmConfig *config)
+{
+    bool success;
+
+    psMetadata *recipes = psMetadataLookupMetadata(&success, config->arguments, "RECIPES");
+    if (!recipes) {
+        recipes = psMetadataAlloc();
+        success = psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "RECIPES",  PS_DATA_METADATA, "",
+                                    recipes);
+        assert (success);
+        psFree (recipes);
+    }
+
+    // Go through the command-line arguments
+    int argNum;                         // Argument number
+
+    // -recipe-file: read recipe from file
+    while ((argNum = psArgumentGet(*argc, argv, "-recipe-file")) > 0) {
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum + 1 >= *argc) {
+            psError(PS_ERR_IO, false,
+                    "-recipe command-line switch provided without the required recipe and filename\n");
+            return false;
+        }
+
+        psString recipeName = psStringCopy(argv[argNum]); // Name of the recipe
+        psArgumentRemove(argNum, argc, argv);
+        psString filename = psStringCopy(argv[argNum]); // Filename for the recipe
+        psArgumentRemove(argNum, argc, argv);
+
+        psMetadata *recipe = NULL;      // Recipe from file
+        if (!pmConfigFileRead(&recipe, filename, "recipe")) {
+            psError(PS_ERR_IO, "Error reading config file %s\n", filename);
+            psFree(recipeName);
+            psFree(filename);
+            return false;
+        }
+
+        psString comment = NULL;
+        psStringAppend(&comment, "Recipe added at command line from file %s", filename);
+        psMetadataAdd(recipes, PS_LIST_TAIL, recipeName, PS_DATA_METADATA | PS_META_REPLACE,
+                      comment, recipe);
+        psFree(comment);
+        psFree(recipe);                 // Drop reference
+        psFree(recipeName);
+        psFree(filename);
+    }
+
+    // -recipe: read recipe from symbolic link
+    while ((argNum = psArgumentGet(*argc, argv, "-recipe")) > 0) {
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum + 1 >= *argc) {
+            psError(PS_ERR_IO, false,
+                    "-recipe command-line switch provided without the required recipe and source\n");
+            return false;
+        }
+
+        char *recipeName = psStringCopy(argv[argNum]); // Name of the recipe
+        psArgumentRemove(argNum, argc, argv);
+        char *recipeSource = psStringCopy(argv[argNum]); // Source of the recipe
+        psArgumentRemove(argNum, argc, argv);
+
+        // Assume it's a symbolic reference to something that's not yet read in.
+        // it will be loaded later by pmConfigReadRecipes with option CL
+        psMetadataAddStr(config->recipeSymbols, PS_LIST_TAIL, recipeName, PS_META_REPLACE, NULL,
+                         recipeSource);
+
+        psTrace ("psModules.config", 3, "read recipe %s from %s", recipeName, recipeSource);
+        psFree(recipeName);
+        psFree(recipeSource);
+    }
+
+    return true;
+}
+
+// Load the recipe files for SYSTEM : REQUIRED
+static bool loadRecipeSystem(bool *status,
+                           pmConfig *config, // The configuration into which to read the recipes
+                           psMetadata *source // The source configuration, from which to read the filenames
+    )
+{
+    assert(status);
+    assert(config);
+    *status = false;
+
+    if (!source) {
+        psError(PS_ERR_IO, true,
+                "The system configuration has not been read --- cannot read recipes from this location.\n");
+        config->recipesRead &= ~PM_RECIPE_SOURCE_SYSTEM;
+        return false;
+    }
+
+    psMetadata *recipes = psMetadataLookupMetadata(NULL, source, "RECIPES"); // The list of recipes
+    if (!recipes) {
+        psError(PS_ERR_IO, false, "RECIPES not found in the system configuration\n");
+        return false;
+    }
+
+    // Copy contents of the filenames to config->recipes from the "RECIPES" metadata in the source.
+    // We could use psMetadataCopy for this, but it's better to check that everything's of the correct type.
+    // If it's not of the correct type, we can tell the user which file it's in, so they can find it easier.
+    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL;        // MD item containing the filename, from recipe iteration
+    while ((item = psMetadataGetAndIncrement(recipesIter))) {
+        if (!pmConfigFileIngest(item, "recipe")) {
+            psError(PS_ERR_IO, false, "Failed to read recipe %s listed in system configuration",
+                    item->name);
+            psFree(recipesIter);
+            return false;
+        }
+
+        // add the contents of this recipe file to config->recipes
+        psMetadataAdd(config->recipes, PS_LIST_TAIL, item->name, PS_DATA_METADATA | PS_META_REPLACE,
+                      item->comment, item->data.md);
+    }
+    psFree(recipesIter);
+    config->recipesRead |= PM_RECIPE_SOURCE_SYSTEM;
+
+    *status = true;
+    return true;
+}
+
+// Load the recipe files for a specific CAMERA.  these are saved on recipesCamera
+static bool loadRecipeCamera(bool *status, // status variable
+                             pmConfig *config, // The configuration into which to read the recipes
+                             psMetadata *source // The source configuration, from which to read the filenames
+    )
+{
+    bool success;
+
+    assert(status);
+    assert(config);
+    *status = false;
+
+    if (!source) {
+        psError(PS_ERR_IO, true,
+                "The camera configuration has not been read --- cannot read recipes from this location.\n");
+        config->recipesRead &= ~PM_RECIPE_SOURCE_CAMERA;
+        return false;
+    }
+
+    // it is not necessary to define any local recipes in the camera config; it this entry is missing,
+    // just return true
+    psMetadata *recipes = psMetadataLookupMetadata(&success, source, "RECIPES"); // The list of recipes
+    if (!recipes) {
+        psTrace ("psModules.config", 3, "RECIPES not found in the camera configuration\n");
+        return true;
+    }
+
+    // Copy contents of the filenames to config->recipes from the "RECIPES" metadata in the source.
+    // We could use psMetadataCopy for this, but it's better to check that everything's of the correct type.
+    // If it's not of the correct type, we can tell the user which file it's in, so they can find it easier.
+    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL;    // MD item containing the filename, from recipe iteration
+    while ((item = psMetadataGetAndIncrement(recipesIter))) {
+        if (!pmConfigFileIngest(item, "recipe")) {
+            psError(PS_ERR_IO, false, "Failed to read recipe %s listed in camera configuration",
+                    item->name);
+            return false;
+        }
+        const char *recipeName = item->name; // Name of the recipe
+        psMetadata *recipe = item->data.md; // The recipe
+
+        // the named recipe must exist at the system level
+        psMetadata *current = psMetadataLookupMetadata(NULL, config->recipes, recipeName);
+        if (!current) {
+            psError(PS_ERR_IO, false, "Failed to find recipe for %s in master recipe list", recipeName);
+            return false;
+        }
+
+        // add the contents of this recipe file to config->recipesCamera
+        psMetadataAdd(config->recipesCamera, PS_LIST_TAIL, recipeName, PS_DATA_METADATA | PS_META_REPLACE,
+                      item->comment, recipe);
+    }
+    psFree(recipesIter);
+    config->recipesRead |= PM_RECIPE_SOURCE_CAMERA;
+    *status = true;
+    return true;
+}
+
+// Merge the CAMERA recipes into the SYSTEM recipes
+static bool mergeRecipeCamera(bool *status, // status variable
+                             pmConfig *config // The configuration into which to read the recipes
+    )
+{
+    assert(status);
+    assert(config);
+    *status = false;
+
+    // Copy contents of config->recipesCamera to config->recipes
+    // We could use psMetadataCopy for this, but it's better to check that everything's of the correct type.
+    // If it's not of the correct type, we can tell the user which file it's in, so they can find it easier.
+    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(config->recipesCamera, PS_LIST_HEAD, NULL);
+    psMetadataItem *folderItem = NULL;    // MD item containing the filename, from recipe iteration
+    while ((folderItem = psMetadataGetAndIncrement(recipesIter))) {
+        char *recipeName = folderItem->name;
+        psMetadata *recipe = folderItem->data.md;
+
+        psTrace("psModules.config", 3, "merging %s from camera with system recipes.\n", recipeName);
+
+        // type mismatch is a serious error
+        if (folderItem->type != PS_DATA_METADATA) {
+            psAbort("%s not of type METADATA", recipeName);
+        }
+
+        // the select the named recipe from the system level
+        psMetadata *current = psMetadataLookupMetadata(NULL, config->recipes, recipeName);
+        psAssert (current, "Failed to find recipe for %s in system recipe list", recipeName);
+
+        // update the contents of this recipe from the one on config->recipesCamera
+        if (!psMetadataUpdate(current, recipe)) {
+            psError(PS_ERR_IO, false, "Failed to update recipe for %s from camera recipe", recipeName);
+            return false;
+        }
+    }
+    psFree(recipesIter);
+    *status = true;
+    return true;
+}
+
+// Load the recipes from config->arguments (CL)
+// Load the recipes: each time we load a specific recipe, it overrides the metadata
+// entries for an existing recipe metadata
+static bool loadRecipeFromArguments(bool *status,
+                                    pmConfig *config // The configuration into which to read the recipes
+    )
+{
+    assert(status);
+    assert(config);
+    *status = false;
+
+    if (!config->arguments) {
+        psTrace("psModules.config", 4, "no config->arguments metadata, nothing to read here");
+        return true;
+    }
+
+    psMetadata *recipes = psMetadataLookupMetadata(NULL, config->arguments, "RECIPES"); // The list of recipes
+    if (!recipes) {
+        psTrace("psModules.config", 4, "no RECIPES in config->arguments, nothing to read here");
+        return true;
+    }
+
+    // Copy the recipes from config->arguments:RECIPES to config->recipes.  supplement existing recipes.
+    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL;    // MD item containing the filename, from recipe iteration
+    while ((item = psMetadataGetAndIncrement(recipesIter))) {
+        // type mismatch is a serious error
+        if (item->type != PS_DATA_METADATA) {
+            psAbort("%s in config arguments RECIPES is not of type METADATA", item->name);
+        }
+        // increment the ref counter to protect the data
+
+        psMetadata *recipe = item->data.md; // Recipe of interest
+
+        // if this named recipe exists, supplement it
+        psMetadata *current = psMetadataLookupMetadata(NULL, config->recipes, item->name);
+        if (!current) {
+            psError(PS_ERR_IO, false, "Failed to find recipe for %s in master recipe list", item->name);
+            psFree(recipe);  // Drop reference
+            return false;
+        }
+        psTrace("psModules.config", 3, "Supplementing %s from arguments.\n", item->name);
+
+        if (!psMetadataUpdate (current, recipe)) {
+            psError(PS_ERR_IO, false, "Failed to update recipe for %s from camera recipe", item->name);
+            return false;
+        }
+    }
+    psFree(recipesIter);
+    *status = true;
+    return true;
+}
+
+// Load the recipes: each time we load a specific recipe, it overrides the metadata
+// entries for an existing recipe metadata
+// The configuration into which to read the recipes
+static bool loadRecipeSymbols(bool *status, pmConfig *config, pmRecipeSource source) {
+
+    bool found = false;
+
+    assert(status);
+    assert(config);
+    *status = false;
+
+    // check to see if any symbolic names need to be resolved
+    // each entry in recipeSymbols are of the form TARGET=SOURCE where TARGET is an existing
+    // recipe MD and REF is a MD to load over that recipe
+    psMetadataIterator *iter = psMetadataIteratorAlloc(config->recipeSymbols, PS_LIST_HEAD, NULL);
+    psMetadataItem *item = NULL;  // Item containing source, from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        assert(item->type == PS_DATA_STRING); // It should be this type: we put it in ourselves
+        const char *sourceName = item->data.str; // The name of the symbolic reference
+        const char *targetName = item->name;
+        psTrace("psModules.config", 3, "Supplementing %s from %s.\n", targetName, sourceName);
+
+        // the target recipe must exist; select it
+        psMetadata *targetMD = psMetadataLookupMetadata(&found, config->recipes, targetName);
+        if (!targetMD) {
+            psError(PS_ERR_IO, true, "Failed to find recipe for %s in master recipe list", targetName);
+            return false;
+        }
+
+        // search for sourceName in config->recipes (folder name is targetName)
+        psMetadata *folder = psMetadataLookupMetadata(&found, config->recipes, targetName);
+        psMetadata *sourceMD = psMetadataLookupMetadata(&found, folder, sourceName);
+
+        // if we find the desired symbolic name at this level, set the item comment to say "FOUND"
+        if (sourceMD) {
+          if (!psMetadataUpdate(targetMD, sourceMD)) {
+            psError(PS_ERR_IO, false, "Failed to update recipe for %s from camera recipe", targetName);
+            return false;
+          }
+          psStringAppend (&item->comment, "(FOUND)");
+        }
+
+        // if we have not found it by the camera level, we have a problem
+        if (source == PM_RECIPE_SOURCE_CAMERA) {
+          if (strstr (item->comment, "(FOUND)") == NULL) {
+            psError(PS_ERR_IO, false, "Selected symbolic name %s does not exist in recipes", sourceName);
+            return false;
+          }
+        }
+    }
+    psFree(iter);
+    *status = true;
+    return true;
+}
+
+// Load the recipe options
+// Load the recipes: each time we load a specific recipe, it overrides the metadata
+// entries for an existing recipe metadata
+static bool loadRecipeOptions(bool *status,
+                              pmConfig *config // The configuration into which to read the recipes
+    )
+{
+    bool found;
+    assert(status);
+    assert(config);
+    *status = false;
+
+    if (!config->arguments) {
+        psTrace("psModules.config", 4, "no config->arguments metadata, nothing to read here");
+        return true;
+    }
+
+    psMetadata *recipes = psMetadataLookupMetadata(&found, config->arguments, "OPTIONS"); // List of recipes
+    if (!recipes) {
+        psTrace("psModules.config", 4, "no OPTIONS in config->arguments, nothing to read here");
+        return true;
+    }
+
+    // Copy the recipes from config->arguments:OPTIONS to config->recipes.  supplement existing recipes.
+    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL;    // MD item containing the filename, from recipe iteration
+    while ((item = psMetadataGetAndIncrement(recipesIter))) {
+        char *recipeName = item->name;
+        psMetadata *recipe = item->data.md;
+
+        // type mismatch is a serious error
+        if (item->type != PS_DATA_METADATA) {
+            psAbort("%s in config arguments OPTIONS is not of type METADATA", recipeName);
+        }
+
+        // if this named recipe exists, supplement it
+        psMetadata *current = psMetadataLookupMetadata(NULL, config->recipes, recipeName);
+        if (!current) {
+            psError(PS_ERR_IO, false, "Selected recipe %s is not found in camera recipe", recipeName);
+            return false;
+        }
+        if (!psMetadataUpdate (current, recipe)) {
+            psError(PS_ERR_IO, false, "Failed to update recipe for %s from camera recipe", recipeName);
+            return false;
+        }
+    }
+    psFree(recipesIter);
+    *status = true;
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigRecipes.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigRecipes.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmConfigRecipes.h	(revision 22322)
@@ -0,0 +1,33 @@
+/*  @file pmConfigRecipes.h
+ *  @brief Configuration Recipe functions
+ * 
+ *  @author ?, MHPCC
+ *  @author Paul Price, IfA
+ *  @author Eugene Magnier, IfA
+ * 
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-04-19 02:10:12 $
+ *  Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_CONFIG_RECIPES_H
+#define PM_CONFIG_RECIPES_H
+
+/// @addtogroup Config Configuration System
+/// @{
+
+/// Read recipes
+///
+/// Attempt to read recipes from the sources that are available but have not already been read.  Having read a
+/// recipe, attempt to resolve symbolic links that were specified on the command line.
+bool pmConfigReadRecipes(pmConfig *config, ///< Configuration
+                         pmRecipeSource source ///< desired sources for recipes
+                        );
+
+
+bool pmConfigLoadRecipeArguments (int *argc, char **argv, pmConfig *config);
+bool pmConfigLoadRecipeOptions (int *argc, char **argv, pmConfig *config, char *flag);
+psMetadata *pmConfigRecipeOptions (pmConfig *config, char *recipe);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,25 @@
+/*
+ * The line
+    { PM_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+#include <pslib.h>
+#include "pmErrorCodes.h"
+
+void pmErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { PM_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PM_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = PM_ERR_NERROR - PM_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmErrorCodes.dat	(revision 22322)
@@ -0,0 +1,18 @@
+#
+# This file is used to generate pmErrorCodes.h
+#
+UNKNOWN			Unknown psModules error code
+PHOTOM			Problem in photometry
+PSF			Problem in PSF
+ASTROM			Problem in astrometry
+CAMERA			Problem in camera
+CONCEPTS		Problem in concepts
+IMCOMBINE		Problem in imcombine
+OBJECTS			Problem in objects
+SKY			Problem in sky
+# these errors correspond to standard exit conditions
+ARGUMENTS               Incorrect arguments
+SYS                     System error
+CONFIG                  Problem in configure files
+PROG                    Programming error
+DATA                    invalid data
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,18 @@
+#if !defined(PM_ERROR_CODES_H)
+#define PM_ERROR_CODES_H
+/*
+ * The line
+ *  PM_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    PM_ERR_BASE = 1200,
+    PM_ERR_${ErrorCode},
+    PM_ERR_NERROR
+} pmErrorCode;
+
+void pmErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmVersion.c	(revision 22322)
@@ -0,0 +1,28 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmVersion.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString psModulesVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString psModulesVersionLong(void)
+{
+    psString version = psModulesVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+
+    psFree(tag);
+    return version;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/config/pmVersion.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/config/pmVersion.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/config/pmVersion.h	(revision 22322)
@@ -0,0 +1,37 @@
+/*  @file pmVersion.h
+ *  @brief Version functions
+ * 
+ *  @author Paul Price, IfA
+ *  @author Eugene Magnier, IfA
+ * 
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-03-30 21:12:56 $
+ *  Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_VERSION_H
+#define PM_VERSION_H
+
+/// @addtogroup Config Configuration System
+/// @{
+
+/** Get current psModules version
+ *
+ *  Returns the current psModules version name as a string.
+ *
+ *  @return psString: String with version name.
+ */
+psString psModulesVersion(
+    void
+);
+
+/** Get current psModules version (full identification)
+ *
+ *  Returns the current psModules version name and other information identifying the compilation.
+ *
+ *  @return psString: String with identity.
+ */
+psString psModulesVersionLong(void);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/Makefile.am	(revision 22322)
@@ -0,0 +1,37 @@
+noinst_LTLIBRARIES = libpsmodulesdetrend.la
+
+libpsmodulesdetrend_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS)
+libpsmodulesdetrend_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulesdetrend_la_SOURCES  = \
+	pmFlatField.c \
+	pmFlatNormalize.c \
+	pmFringeStats.c \
+	pmMaskBadPixels.c \
+	pmNonLinear.c \
+	pmBias.c \
+	pmOverscan.c \
+	pmDetrendDB.c \
+	pmShutterCorrection.c \
+	pmDetrendThreads.c \
+	pmShifts.c \
+	pmDark.c
+
+#	pmSkySubtract.c
+
+pkginclude_HEADERS = \
+	pmFlatField.h \
+	pmFlatNormalize.h \
+	pmFringeStats.h \
+	pmMaskBadPixels.h \
+	pmNonLinear.h \
+	pmBias.h \
+	pmOverscan.h \
+	pmDetrendDB.h \
+	pmShutterCorrection.h \
+	pmDetrendThreads.h \
+	pmShifts.h \
+	pmDark.h
+
+#	pmSkySubtract.h
+
+CLEANFILES = *~
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmBias.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmBias.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmBias.c	(revision 22322)
@@ -0,0 +1,260 @@
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPACalibration.h"
+#include "pmDetrendThreads.h"
+
+#include "pmOverscan.h"
+#include "pmBias.h"
+
+bool pmBiasSubtractScan_Threaded(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+    pmReadout *in = job->args->data[0];
+    const pmReadout *sub = job->args->data[1];
+    float scale  = PS_SCALAR_VALUE(job->args->data[2],F32);
+    int xOffset  = PS_SCALAR_VALUE(job->args->data[3],S32);
+    int yOffset  = PS_SCALAR_VALUE(job->args->data[4],S32);
+    int rowStart = PS_SCALAR_VALUE(job->args->data[5],S32);
+    int rowStop  = PS_SCALAR_VALUE(job->args->data[6],S32);
+    return pmBiasSubtractScan(in, sub, scale, xOffset, yOffset, rowStart, rowStop);
+}
+
+bool pmBiasSubtractScan(pmReadout *in, const pmReadout *sub, float scale,
+                        int xOffset, int yOffset, int rowStart, int rowStop)
+{
+    psImage *inImage  = in->image;      // The input image
+    psImage *inMask   = in->mask;       // The input mask
+    const psImage *subImage = sub->image; // The image to be subtracted
+    const psImage *subMask  = sub->mask; // The mask for the subtraction image
+
+    if (scale == 1.0) {
+        for (int i = rowStart; i < rowStop; i++) {
+            for (int j = 0; j < inImage->numCols; j++) {
+                inImage->data.F32[i][j] -= subImage->data.F32[i+yOffset][j+xOffset];
+                if (inMask && subMask) {
+                    inMask->data.U8[i][j] |= subMask->data.U8[i+yOffset][j+xOffset];
+                }
+            }
+        }
+    } else {
+        for (int i = rowStart; i < rowStop; i++) {
+            for (int j = 0; j < inImage->numCols; j++) {
+                inImage->data.F32[i][j] -= subImage->data.F32[i+yOffset][j+xOffset] * scale;
+                if (inMask && subMask) {
+                    inMask->data.U8[i][j] |= subMask->data.U8[i+yOffset][j+xOffset];
+                }
+            }
+        }
+    }
+    return true;
+}
+
+// pmBiasSubtractFrame():
+// this routine will take as input a readout for the input image and a readout for the bias
+// image.  The bias image is subtracted in place from the input image.
+bool pmBiasSubtractFrame(pmReadout *in, // Input readout
+                         pmReadout *sub, // Readout to be subtracted from input
+                         float scale   // Scale to apply before subtracting
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(in, false);
+    PS_ASSERT_PTR_NON_NULL(in->image, false);
+    PS_ASSERT_IMAGE_TYPE(in->image, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(in->image, false);
+    PS_ASSERT_PTR_NON_NULL(sub, false);
+    PS_ASSERT_PTR_NON_NULL(sub->image, false);
+    PS_ASSERT_IMAGE_TYPE(sub->image, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(sub->image, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(in->image, sub->image, false);
+
+    psImage *inImage  = in->image;      // The input image
+    psImage *subImage = sub->image;     // The image to be subtracted
+
+    // Check parities
+    int xIpar = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.XPARITY");
+    int xSpar = psMetadataLookupS32(NULL, sub->parent->concepts, "CELL.XPARITY");
+    if (xIpar != xSpar) {
+        psError(PS_ERR_UNKNOWN, true, "images for subtraction do not have the same "
+                "CELL.XPARITY (%d vs %d).\n    pmSubtractBias must be upgraded to handle this situation\n",
+                xIpar, xSpar);
+        return false;
+    }
+
+    int yIpar = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.YPARITY");
+    int ySpar = psMetadataLookupS32(NULL, sub->parent->concepts, "CELL.YPARITY");
+    if (yIpar != ySpar) {
+        psError(PS_ERR_UNKNOWN, true, "images for subtraction do not have the same "
+                "CELL.YPARITY (%d vs %d).\n    pmSubtractBias must be upgraded to handle this situation\n",
+                xIpar, xSpar);
+        return false;
+    }
+
+    // Offsets of the cells
+    int x0in = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.X0");
+    int y0in = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.Y0");
+    int x0sub = psMetadataLookupS32(NULL, sub->parent->concepts, "CELL.X0");
+    int y0sub = psMetadataLookupS32(NULL, sub->parent->concepts, "CELL.Y0");
+
+    if ((inImage->numCols + x0in - x0sub) > subImage->numCols) {
+        psError(PS_ERR_UNKNOWN, true, "Image does not have enough columns for subtraction (%d vs %d).\n",
+                inImage->numCols + x0in - x0sub, subImage->numCols);
+        return false;
+    }
+    if ((inImage->numRows + y0in - y0sub) > subImage->numRows) {
+        psError(PS_ERR_UNKNOWN, true, "Image does not have enough rows for subtraction (%d vs %d).\n",
+                inImage->numRows + y0in - y0sub, subImage->numRows);
+        return false;
+    }
+
+    int xOffset = x0in - x0sub;
+    int yOffset = y0in - y0sub;
+
+    bool threaded = true;
+    int scanRows = pmDetrendGetScanRows();
+    if (scanRows == 0) {
+        threaded = false;
+        scanRows = inImage->numRows;
+    }
+
+    for (int rowStart = 0; rowStart < inImage->numRows; rowStart += scanRows) {
+        int rowStop = PS_MIN(rowStart + scanRows, inImage->numRows);
+
+        if (threaded) {
+            // allocate a job, construct the arguments for this job
+            psThreadJob *job = psThreadJobAlloc("PSMODULES_DETREND_BIAS");
+            psArrayAdd(job->args, 1, in);
+            psArrayAdd(job->args, 1, sub);
+            PS_ARRAY_ADD_SCALAR(job->args, scale, PS_TYPE_F32);
+            PS_ARRAY_ADD_SCALAR(job->args, xOffset, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, yOffset, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, rowStart, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, rowStop, PS_TYPE_S32);
+
+            if (!psThreadJobAddPending(job)) {
+                psFree(job);
+                return false;
+            }
+            psFree(job);
+        } else if (!pmBiasSubtractScan(in, sub, scale, xOffset, yOffset, rowStart, rowStop)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to apply bias correction.");
+            return false;
+        }
+    }
+
+    if (threaded) {
+        // wait here for the threaded jobs to finish
+        if (!psThreadPoolWait(true)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to apply bias correction.");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool pmBiasSubtract(pmReadout *in, pmOverscanOptions *overscanOpts,
+                    pmReadout *bias, pmReadout *dark, const pmFPAview *view)
+{
+    psTrace("psModules.detrend", 4,
+            "---- pmBiasSubtract() begin ----\n");
+
+    PS_ASSERT_PTR_NON_NULL(in, false);
+    PS_ASSERT_IMAGE_NON_NULL(in->image, false);
+    PS_ASSERT_IMAGE_TYPE(in->image, PS_TYPE_F32, false);
+    if (bias) {
+        PS_ASSERT_IMAGE_NON_NULL(bias->image, false);
+        PS_ASSERT_IMAGE_TYPE(bias->image, PS_TYPE_F32, false);
+    }
+    if (dark) {
+        psWarning("Dark processing is now available using pmDark --- perhaps you should use that instead?");
+        PS_ASSERT_PTR_NON_NULL(view, false);
+        PS_ASSERT_IMAGE_NON_NULL(dark->image, false);
+        PS_ASSERT_IMAGE_TYPE(dark->image, PS_TYPE_F32, false);
+    }
+
+    pmHDU *hdu = pmHDUFromReadout(in);  // HDU of interest
+
+    if (!pmOverscanSubtract (in, overscanOpts)) {
+        return false;
+    }
+
+    // Bias frame subtraction
+    if (bias) {
+        psVector *md5 = psImageMD5(bias->image); // md5 hash
+        psString md5string = psMD5toString(md5); // String
+        psFree(md5);
+        psStringPrepend(&md5string, "BIAS image MD5: ");
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                         md5string, "");
+        psFree(md5string);
+
+        if (!pmBiasSubtractFrame(in, bias, 1.0)) {
+            return false;
+        }
+    }
+
+    if (dark) {
+        // Get the scaling
+        float inTime = psMetadataLookupF32(NULL, in->parent->concepts, "CELL.DARKTIME");
+        float darkTime = psMetadataLookupF32(NULL, dark->parent->concepts, "CELL.DARKTIME");
+        if (isnan(inTime) || isnan(darkTime)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine dark scaling.");
+            return false;
+        }
+
+        float darkNorm = 1.0;
+        float inNorm = pmFPADarkNorm(in->parent->parent->parent, view, inTime);
+
+        // if we have a normalized dark exposure, we simply multiply the master by inNorm.  if
+        // we do not have a normalized exposure, we have to scale the master as well.  XXX do
+        // we need to explicitly identify the master as normalized?
+
+        if (darkTime != 1.0) {
+            darkNorm = pmFPADarkNorm(dark->parent->parent->parent, view, darkTime);
+        }
+
+        if (isnan(inNorm) || isnan(darkNorm)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine dark normalisations.");
+            return false;
+        }
+
+        float scale = inNorm / darkNorm;// Scaling to apply to dark exposure
+
+        psVector *md5 = psImageMD5(dark->image); // md5 hash
+        psString md5string = psMD5toString(md5); // String
+        psFree(md5);
+        psStringPrepend(&md5string, "DARK image (scale %.3f) MD5: ", scale);
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                         md5string, "");
+        psFree(md5string);
+
+        if (!pmBiasSubtractFrame(in, dark, scale)) {
+            return false;
+        }
+    }
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now, used for reporting
+    psString timeString = psTimeToISO(time); // String with time
+    psFree(time);
+    psStringPrepend(&timeString, "Overscan/bias/dark processing completed at ");
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                     timeString, "");
+    psFree(timeString);
+
+
+    return true;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmBias.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmBias.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmBias.h	(revision 22322)
@@ -0,0 +1,51 @@
+/* @file pmBias.h
+ * @brief Subtract the overscan, bias and dark
+ *
+ * @author George Gusciora, MHPCC
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-09-09 04:10:14 $
+ * Copyright 2004--2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_BIAS_H
+#define PM_BIAS_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+/// Subtract the overscan, bias and/or dark
+///
+/// Subtracts the overscan, as measured from the bias member of the input readout (if options are non-NULL),
+/// bias (if non-NULL) and dark (if non-NULL) scaled by the CELL.DARKTIME concept.
+bool pmBiasSubtract(pmReadout *in,      ///< Input readout, to be overscan/bias/dark corrected
+                    pmOverscanOptions *overscanOpts, ///< Options for overscan subtraction, or NULL
+                    pmReadout *bias, ///< Bias to subtract, or NULL
+                    pmReadout *dark, ///< Dark to scale and subtract, or NULL
+                    const pmFPAview *view ///< View for readout of interest
+                   );
+
+// pmBiasSubtractFrame
+// this routine will take as input a readout for the input image and a readout for the bias
+// image.  The bias image is subtracted in place from the input image.
+bool pmBiasSubtractFrame(pmReadout *in, // Input readout
+                         pmReadout *sub, // Readout to be subtracted from input
+                         float scale   // Scale to apply before subtracting
+    );
+
+/// Thread entry point for bias subtraction
+bool pmBiasSubtractScan_Threaded(psThreadJob *job ///< Job to execute
+    );
+
+/// Do bias subtraction for a scan
+bool pmBiasSubtractScan(
+    pmReadout *in,                      ///< Input image to correct
+    const pmReadout *sub,               ///< Bias+dark image to subtract
+    float scale,                        ///< Scale to apply
+    int xOffset, int yOffset,           ///< Offset between input and bias images
+    int rowStart, int rowStop           ///< Scan range
+    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDark.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDark.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDark.c	(revision 22322)
@@ -0,0 +1,893 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <string.h>
+
+#include "psPolynomialMD.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "pmFPARead.h"
+#include "pmFPAWrite.h"
+#include "pmReadoutStack.h"
+#include "pmDetrendThreads.h"
+
+#include "pmDark.h"
+
+#define PM_DARK_FITS_EXTNAME "PS_DARK"  // FITS extension name for ordinates table
+#define PM_DARK_FITS_NAME    "NAME"     // Column name for concept name in ordinates table
+#define PM_DARK_FITS_ORDER   "ORDER"    // Column name for polynomial order in ordinates table
+#define PM_DARK_FITS_SCALE   "SCALE"    // Column name for scaling option in ordinates table
+#define PM_DARK_FITS_MIN     "MIN"      // Column name for minimum value in ordinates table
+#define PM_DARK_FITS_MAX     "MAX"      // Column name for maximum value in ordinates table
+
+
+
+// Look up the value of an ordinate in a readout
+static bool ordinateLookup(float *value, // Value of ordinate, to return
+                           bool *inRange, // is value within min : max range?
+                           const char *name, // Name of ordinate (concept name)
+                           bool scale,  // Scale the value?
+                           float min, float max, // Minimum and maximum values for scaling
+                           const pmReadout *ro // Readout of interest
+                           )
+{
+    assert(value);
+    assert(name);
+    assert(ro);
+
+    *inRange = true;
+
+    pmCell *cell = ro->parent; // Parent cell
+    if (!cell) {
+        return false;
+    }
+    psMetadataItem *item = psMetadataLookup(cell->concepts, name);
+    if (!item) {
+        pmChip *chip = cell->parent; // Parent chip
+        if (!chip) {
+            return false;
+        }
+        item = psMetadataLookup(chip->concepts, name);
+        if (!item) {
+            pmFPA *fpa = chip->parent; // Parent FPA
+            if (!fpa) {
+                return false;
+            }
+            item = psMetadataLookup(fpa->concepts, name);
+            if (!item) {
+                psWarning("Unable to find concept %s in readout", name);
+                return false;
+            }
+        }
+    }
+
+    *value = psMetadataItemParseF32(item); // Value of interest
+    if (!isfinite(*value)) {
+        psWarning("Non-finite value (%f) of concept %s in readout", *value, name);
+        return false;
+    }
+    if (scale) {
+        if (*value < min || *value > max) {
+            psWarning("Value of concept %s (%f) outside range (%f:%f)", name, *value, min, max);
+            *inRange = false;
+        }
+        *value = 2.0 * (*value - min) / (max - min) - 1.0;
+    }
+
+    return true;
+}
+
+static void darkOrdinateFree(pmDarkOrdinate *ord)
+{
+    psFree(ord->name);
+    return;
+}
+
+pmDarkOrdinate *pmDarkOrdinateAlloc(const char *name, int order)
+{
+    pmDarkOrdinate *ord = psAlloc(sizeof(pmDarkOrdinate)); // Ordinate data, to return
+    psMemSetDeallocator(ord, (psFreeFunc)darkOrdinateFree);
+
+    ord->name = psStringCopy(name);
+    ord->order = order;
+    ord->scale = false;
+    ord->min = NAN;
+    ord->max = NAN;
+
+    return ord;
+}
+
+// this creates and saves: values, roMask, norm, orders, counts, sigma, and saves the on output->analysis
+bool pmDarkCombinePrepare(pmCell *output, const psArray *inputs, psArray *ordinates, const char *normConcept)
+{
+    psArray *values = psArrayAlloc(inputs->n);
+    psVector *roMask = psVectorAlloc(inputs->n, PS_TYPE_U8); // Mask for bad readouts
+    psVector *norm = normConcept ? psVectorAlloc(inputs->n, PS_TYPE_F32) : NULL; // Normalizations for each
+    psVector *orders = psVectorAlloc(ordinates->n, PS_TYPE_U8); // Orders for each concept
+
+    psVectorInit(roMask, 0);
+
+    bool inRange = false;
+    int numBadInputs = 0;               // Number of bad inputs
+
+    // build the 'norm' vector and the 'values' vectors, count the number of bad inputs
+    for (int i = 0; i < inputs->n; i++) {
+        values->data[i] = psVectorAlloc(ordinates->n, PS_TYPE_F32);
+        if (!norm) continue;
+
+        pmReadout *readout = inputs->data[i]; // Readout of interest
+        float normValue;            // Normalisation value
+        if (!ordinateLookup(&normValue, &inRange, normConcept, false, NAN, NAN, readout)) {
+            psWarning("Unable to find value of %s for readout %d", normConcept, i);
+            roMask->data.U8[i] = 0xff;
+            norm->data.F32[i] = NAN;
+            numBadInputs++;
+            continue;
+        }
+        if (normValue == 0.0) {
+            psWarning("Normalisation value (%s) for readout %d is zero", normConcept, i);
+            roMask->data.U8[i] = 0xff;
+            norm->data.F32[i] = NAN;
+            numBadInputs++;
+            continue;
+        }
+        norm->data.F32[i] = 1.0 / normValue;
+    }
+
+    // build the 'orders' vector and set the array of 'values'
+    for (int i = 0; i < ordinates->n; i++) {
+        pmDarkOrdinate *ord = ordinates->data[i]; // Ordinate information
+        if (ord->order <= 0) {
+            psError(PS_ERR_UNKNOWN, true, "Bad order for concept %s (%d) --- ignored", ord->name, ord->order);
+            psFree(values);
+            psFree(roMask);
+            psFree(orders);
+            psFree(norm);
+            return false;
+        }
+        orders->data.U8[i] = ord->order;
+
+        for (int j = 0; j < inputs->n; j++) {
+            psVector *val = values->data[j]; // Value vector for readout
+            if (roMask->data.U8[j]) {
+                val->data.F32[i] = NAN;
+                continue;
+            }
+
+            pmReadout *readout = inputs->data[j]; // Readout of interest
+            float value = NAN;          // Value of ordinate
+            if (!ordinateLookup(&value, &inRange, ord->name, ord->scale, ord->min, ord->max, readout)) {
+                roMask->data.U8[j] = 0xff;
+                val->data.F32[i] = NAN;
+                numBadInputs++;
+                continue;
+            }
+            if (!inRange) {
+                roMask->data.U8[j] = 0xff;
+                val->data.F32[i] = NAN;
+                numBadInputs++;
+                continue;
+            }
+            val->data.F32[i] = value;
+        }
+    }
+
+    if (psTraceGetLevel("psModules.detrend") > 9) {
+        for (int i = 0; i < inputs->n; i++) {
+            psVector *val = values->data[i];
+            (void) val; // avoid unused variable message when tracing is compiled out
+            for (int j = 0; j < ordinates->n; j++) {
+                psTrace("psModules.detrend", 9, "Image %d, ordinate %d: %f\n", i, j, val->data.F32[j]);
+            }
+        }
+    }
+
+    int numTerms = 1;                   // Number of terms in polynomial
+    for (int i = 0; i < orders->n; i++) {
+        numTerms += orders->data.U8[i];
+    }
+
+    if (numTerms > inputs->n - numBadInputs) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Insufficient inputs (%ld) to fit polynomial terms (%d).",
+                inputs->n - numBadInputs, numTerms);
+        psFree(values);
+        psFree(roMask);
+        psFree(norm);
+        return false;
+    }
+
+    // determine the output image size based on the input images
+    int row0, col0, numCols, numRows;
+    if (!pmReadoutStackSetOutputSize(&col0, &row0, &numCols, &numRows, inputs)) {
+        psError(PS_ERR_UNKNOWN, false, "problem setting output readout size.");
+        return false;
+    }
+
+    // the output is potentially a cube (depending on the dimensionality of the fit)
+    if (output->readouts->n != numTerms) {
+        output->readouts = psArrayRealloc(output->readouts, numTerms);
+    }
+
+    // generate the required output images based on the specified sizes
+    for (int i = 0; i < numTerms; i++) {
+        pmReadout *readout = output->readouts->data[i]; // Readout to update
+        if (!readout) {
+            readout = output->readouts->data[i] = pmReadoutAlloc(output);
+        }
+
+        pmReadoutStackDefineOutput(readout, col0, row0, numCols, numRows, false, false, 0);
+        psTrace("psModules.imcombine", 7, "Output minimum: %d,%d\n", col0, row0);
+    }
+
+    // these calls allocate and save the requested images on the output analysis metadata
+    psImage *counts = pmReadoutSetAnalysisImage(output->readouts->data[0], PM_READOUT_STACK_ANALYSIS_COUNT,
+                                                numCols, numRows, PS_TYPE_U16, 0);
+    if (!counts) {
+        return false;
+    }
+    psImage *sigma = pmReadoutSetAnalysisImage(output->readouts->data[0], PM_READOUT_STACK_ANALYSIS_SIGMA,
+                                               numCols, numRows, PS_TYPE_F32, NAN);
+    if (!sigma) {
+        return false;
+    }
+
+    psMetadataAddPtr(output->analysis, PS_LIST_TAIL, PM_DARK_ANALYSIS_ORDINATES,
+                     PS_DATA_ARRAY | PS_META_REPLACE, "Dark ordinates", ordinates);
+    psMetadataAddStr(output->analysis, PS_LIST_TAIL, PM_DARK_ANALYSIS_NORM, PS_META_REPLACE,
+                     "Dark normalisation", normConcept);
+
+    psMetadataAddPtr(output->analysis, PS_LIST_TAIL, "DARK.VALUES",  PS_DATA_ARRAY  | PS_META_REPLACE,
+                     "Dark values", values);
+    psMetadataAddPtr(output->analysis, PS_LIST_TAIL, "DARK.RO.MASK", PS_DATA_VECTOR | PS_META_REPLACE,
+                     "Dark Readout Mask", roMask);
+    psMetadataAddPtr(output->analysis, PS_LIST_TAIL, "DARK.NORM",    PS_DATA_VECTOR | PS_META_REPLACE,
+                     "Dark norm", norm);
+    psMetadataAddPtr(output->analysis, PS_LIST_TAIL, "DARK.ORDERS",  PS_DATA_VECTOR | PS_META_REPLACE,
+                     "Dark orders", orders);
+
+    for (int i = 0; i < numTerms; i++) {
+        pmReadout *readout = output->readouts->data[i]; // Readout to update
+        readout->data_exists = true;
+    }
+    output->data_exists = true;
+    output->parent->data_exists = true;
+
+    psFree(norm);
+    psFree(roMask);
+    psFree(orders);
+    psFree(values);
+
+    return true;
+}
+
+// do the combine work for this portion of the output (range is set by input data)
+bool pmDarkCombine(pmCell *output, const psArray *inputs, int iter, float rej, psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(output, false);
+    PS_ASSERT_PTR_NON_NULL(output->readouts, false);
+    PS_ASSERT_INT_NONNEGATIVE(output->readouts->n, false);
+    PS_ASSERT_ARRAY_NON_NULL(inputs, false);
+    PS_ASSERT_INT_NONNEGATIVE(iter, false);
+    PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
+
+    pthread_t id = pthread_self();
+    char name[64];
+    sprintf (name, "%x", (unsigned int) id);
+    psTimerStart (name);
+
+    bool mdok = false;
+
+    // retrieve the required parameter vectors
+    psArray *values  = psMetadataLookupPtr(&mdok, output->analysis, "DARK.VALUES");
+    psAssert(values, "values not supplied");
+    psVector *roMask = psMetadataLookupPtr(&mdok, output->analysis, "DARK.RO.MASK");
+    psAssert(roMask, "roMask not supplied");
+    psVector *orders = psMetadataLookupPtr(&mdok, output->analysis, "DARK.ORDERS");
+    psAssert(orders, "orders not supplied");
+
+    psPolynomialMD *poly = psPolynomialMDAlloc(orders); // Polynomial for fitting
+
+    // retrieve the norm vector, if supplied
+    psVector *norm       = psMetadataLookupPtr(&mdok, output->analysis, "DARK.NORM");
+
+    // retrieve the 'counts' and 'sigma' images
+    psImage *counts = pmReadoutGetAnalysisImage(output->readouts->data[0], PM_READOUT_STACK_ANALYSIS_COUNT);
+    if (!counts) {
+        return false;
+    }
+    psImage *sigma = pmReadoutGetAnalysisImage(output->readouts->data[0], PM_READOUT_STACK_ANALYSIS_SIGMA);
+    if (!sigma) {
+        return false;
+    }
+
+    int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine
+    int xSize, ySize;                   // Size of the output image
+    if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize,
+                                inputs)) {
+        psError(PS_ERR_UNKNOWN, false, "No valid input readouts.");
+        return false;
+    }
+
+    pmReadout *outReadout = output->readouts->data[0];
+
+    // Iterate over pixels, fitting polynomial
+    psVector *pixels = psVectorAlloc(inputs->n, PS_TYPE_F32); // Stack of pixels
+    psVector *mask   = psVectorAlloc(inputs->n, PS_TYPE_MASK); // Mask for stack
+    for (int i = minInputRows; i < maxInputRows; i++) {
+        int yOut = i - outReadout->row0; // y position on output readout
+
+#ifdef SHOW_BUSY
+        if (psTraceGetLevel("psModules.detrend") > 9) {
+            printf("Processing row %d\r", i);
+            fflush(stdout);
+        }
+#endif
+
+        for (int j = minInputCols; j < maxInputCols; j++) {
+            int xOut = j - outReadout->col0; // x position on output readout
+
+            psVectorInit(mask, 0);
+            for (int r = 0; r < inputs->n; r++) {
+                if (roMask->data.U8[r]) {
+                    mask->data.PS_TYPE_MASK_DATA[r] = 0xff;
+                    continue;
+                }
+                pmReadout *readout = inputs->data[r]; // Input readout
+                int yIn = i - readout->row0; // y position on input readout
+                int xIn = j - readout->col0; // x position on input readout
+
+                pixels->data.F32[r] = readout->image->data.F32[yIn][xIn];
+                if (norm) {
+                    pixels->data.F32[r] *= norm->data.F32[r];
+                }
+                if (readout->mask) {
+                    mask->data.PS_TYPE_MASK_DATA[r] = readout->mask->data.PS_TYPE_MASK_DATA[yIn][xIn];
+                }
+
+            }
+
+            if (!psPolynomialMDClipFit(poly, pixels, NULL, mask, maskVal, values, iter, rej)) {
+                psErrorClear();         // Nothing we can do about it
+                psVectorInit(poly->coeff, NAN);
+            }
+            for (int k = 0; k < poly->coeff->n; k++) {
+                pmReadout *ro = output->readouts->data[k]; // Readout of interest
+                ro->image->data.F32[yOut][xOut] = poly->coeff->data.F64[k];
+            }
+            counts->data.U16[yOut][xOut] = poly->numFit;
+            sigma->data.F32[yOut][xOut] = poly->stdevFit;
+        }
+    }
+
+    psFree(pixels);
+    psFree(mask);
+
+    return true;
+}
+
+bool pmDarkApplyScan_Threaded(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    pmReadout *readout     = job->args->data[0]; // Readout to correct
+    pmCell *dark           = job->args->data[1]; // Dark to apply
+    const psVector *orders = job->args->data[2]; // Polynomial orders for each ordinate
+    const psVector *values = job->args->data[3]; // Values for each ordinate
+
+    psMaskType bad = PS_SCALAR_VALUE(job->args->data[4], U8); // Mask value to give bad pixels
+    bool doNorm    = PS_SCALAR_VALUE(job->args->data[5], U8); // Normalise values?
+    float norm     = PS_SCALAR_VALUE(job->args->data[6], F32); // Value by which to normalise
+    int rowStart   = PS_SCALAR_VALUE(job->args->data[7], S32); // Starting row for scan
+    int rowStop    = PS_SCALAR_VALUE(job->args->data[8], S32); // Stopping row for scan
+
+    return pmDarkApplyScan(readout, dark, orders, values, bad, doNorm, norm, rowStart, rowStop);
+}
+
+bool pmDarkApplyScan(pmReadout *readout, const pmCell *dark, const psVector *orders, const psVector *values,
+                     psMaskType bad, bool doNorm, float norm, int rowStart, int rowStop)
+{
+    int numCols = readout->image->numCols;
+    int numTerms = dark->readouts->n;   // Number of polynomial terms
+
+    psPolynomialMD *poly = psPolynomialMDAlloc(orders); // Polynomial to apply
+
+    for (int y = rowStart; y < rowStop; y++) {
+        for (int x = 0; x < numCols; x++) {
+            for (int i = 0; i < numTerms; i++) {
+                pmReadout *ro = dark->readouts->data[i]; // Dark readout
+                poly->coeff->data.F64[i] = ro->image->data.F32[y][x];
+            }
+            float value = psPolynomialMDEval(poly, values); // Value of dark current
+            if (doNorm) {
+                value *= norm;
+            }
+            readout->image->data.F32[y][x] -= value;
+            if (readout->mask && !isfinite(value)) {
+                readout->mask->data.PS_TYPE_MASK_DATA[y][x] = bad;
+            }
+        }
+    }
+    psFree(poly);
+
+    return true;
+}
+
+bool pmDarkApply(pmReadout *readout, pmCell *dark, psMaskType bad)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(dark, false);
+    PS_ASSERT_IMAGE_NON_NULL(readout->image, false);
+    PS_ASSERT_IMAGE_TYPE(readout->image, PS_TYPE_F32, false);
+    int numCols = readout->image->numCols, numRows = readout->image->numRows; // Size of image
+    if (readout->mask) {
+        PS_ASSERT_IMAGE_NON_NULL(readout->mask, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(readout->mask, readout->image, false);
+        PS_ASSERT_IMAGE_TYPE(readout->mask, PS_TYPE_MASK, false);
+    }
+    int numTerms = dark->readouts->n;   // Number of polynomial terms
+    for (int i = 0; i < numTerms; i++) {
+        pmReadout *ro = dark->readouts->data[i]; // Dark readout
+        PS_ASSERT_PTR_NON_NULL(ro, false);
+        PS_ASSERT_IMAGE_NON_NULL(ro->image, false);
+        PS_ASSERT_IMAGE_SIZE(ro->image, numCols, numRows, false);
+        PS_ASSERT_IMAGE_TYPE(ro->image, PS_TYPE_F32, false);
+    }
+    psArray *ordinates = psMetadataLookupPtr(NULL, dark->analysis, PM_DARK_ANALYSIS_ORDINATES); // Ordinates
+    if (!ordinates) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find dark ordinates.");
+        return false;
+    }
+    bool mdok;                          // Status of MD lookup
+    psString normConcept = psMetadataLookupStr(&mdok, dark->analysis, PM_DARK_ANALYSIS_NORM); // Normalisation
+    bool inRange = false;
+
+    int numOrdinates = ordinates->n;    // Number of ordinates
+    psVector *values = psVectorAlloc(numOrdinates, PS_TYPE_F32); // Values of ordinates
+    for (int i = 0; i < numOrdinates; i++) {
+        pmDarkOrdinate *ord = ordinates->data[i]; // Ordinate of interest
+        float value = NAN;              // Value for ordinate
+        if (!ordinateLookup(&value, &inRange, ord->name, ord->scale, ord->min, ord->max, readout)) {
+            psError(PS_ERR_UNKNOWN, true, "Unable to find value for %s", ord->name);
+            psFree(values);
+            return false;
+        }
+        values->data.F32[i] = value;
+    }
+    float norm = NAN;                   // Normalisation value
+    bool doNorm = false;                // Do normalisation?
+    if (normConcept && strlen(normConcept) > 0) {
+        if (!ordinateLookup(&norm, &inRange, normConcept, false, NAN, NAN, readout)) {
+            psError(PS_ERR_UNKNOWN, true, "Unable to find value for %s", normConcept);
+            psFree(values);
+            return false;
+        }
+        doNorm = true;
+    }
+
+    psVector *orders = psVectorAlloc(numOrdinates, PS_TYPE_U8); // Order for each polynomial
+    for (int i = 0; i < numOrdinates; i++) {
+        pmDarkOrdinate *ord = ordinates->data[i]; // Ordinate information
+        if (ord->order <= 0) {
+            psError(PS_ERR_UNKNOWN, true, "Bad order for concept %s (%d) --- ignored", ord->name, ord->order);
+            psFree(values);
+            psFree(orders);
+            return false;
+        }
+        orders->data.U8[i] = ord->order;
+    }
+
+    // thread here by scan
+
+    bool threaded = true;
+    int scanRows = pmDetrendGetScanRows();
+    if (scanRows == 0) {
+        threaded = false;
+        scanRows = readout->image->numRows;
+    }
+
+    for (int rowStart = 0; rowStart < readout->image->numRows; rowStart += scanRows) {
+        int rowStop = PS_MIN(rowStart + scanRows, readout->image->numRows);
+
+        if (threaded) {
+            psThreadJob *job = psThreadJobAlloc("PSMODULES_DETREND_DARK");
+            psArrayAdd(job->args, 1, readout);
+            psArrayAdd(job->args, 1, dark);
+            psArrayAdd(job->args, 1, orders);
+            psArrayAdd(job->args, 1, values);
+            PS_ARRAY_ADD_SCALAR(job->args, bad, PS_TYPE_MASK);
+            PS_ARRAY_ADD_SCALAR(job->args, doNorm, PS_TYPE_U8);
+            PS_ARRAY_ADD_SCALAR(job->args, norm, PS_TYPE_F32);
+            PS_ARRAY_ADD_SCALAR(job->args, rowStart, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, rowStop, PS_TYPE_S32);
+
+            if (!psThreadJobAddPending (job)) {
+                psFree(job);
+                psFree(orders);
+                psFree(values);
+                return false;
+            }
+            psFree(job);
+        } else if (!pmDarkApplyScan(readout, dark, orders, values, bad, doNorm, norm, rowStart, rowStop)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to apply dark.");
+            psFree(orders);
+            psFree(values);
+            return false;
+        }
+    }
+
+    if (threaded) {
+        // wait here for the threaded jobs to finish
+        if (!psThreadPoolWait(true)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to apply dark.");
+            psFree(orders);
+            psFree(values);
+            return false;
+        }
+    }
+
+    psFree(orders);
+    psFree(values);
+
+    return true;
+}
+
+bool pmFPAWriteDark(pmFPA *fpa, psFits *fits, pmConfig *config, bool blank, bool recurse)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    if (!pmFPAWrite(fpa, fits, config, blank, recurse)) {
+        psError(PS_ERR_IO, false, "Unable to write FPA dark images");
+        return false;
+    }
+
+    pmHDU *hdu = fpa->hdu;
+    if (blank || !hdu) {
+        // No more to do
+        return true;
+    }
+
+    psArray *ordinates = NULL;      // Dark ordinates, to write
+    const char *normConcept = NULL;     // Normalisation concept
+    psArray *chips = fpa->chips;    // Component chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i]; // Chip of interest
+        psArray *cells = chip->cells; // Component cells
+        for (int j = 0; j < cells->n; j++) {
+            pmCell *cell = cells->data[j]; // Cell of interest
+            bool mdok;              // Status of MD lookup
+            psArray *newOrd = psMetadataLookupPtr(&mdok, cell->analysis, PM_DARK_ANALYSIS_ORDINATES); // Ords
+            if (!mdok) {
+                continue;
+            }
+            psString newNorm = psMetadataLookupPtr(&mdok, cell->analysis, PM_DARK_ANALYSIS_NORM); // Norm
+            if (ordinates) {
+                if (newOrd != ordinates) {
+                    psError(PS_ERR_UNKNOWN, true, "Dark ordinates differ across cells.");
+                    return false;
+                }
+                if ((normConcept && (!newNorm || strcmp(normConcept, newNorm) != 0)) ||
+                    (!normConcept && newNorm)) {
+                    psError(PS_ERR_UNKNOWN, true, "Dark normalisations differ across cells.");
+                    return false;
+                }
+            } else {
+                ordinates = newOrd;
+                normConcept = newNorm;
+            }
+        }
+    }
+
+    if (!ordinates) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find dark ordinates.");
+        return false;
+    }
+
+    return pmDarkWrite(fits, hdu->header, ordinates, normConcept);
+}
+
+bool pmChipWriteDark(pmChip *chip, psFits *fits, pmConfig *config, bool blank, bool recurse)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    if (!pmChipWrite(chip, fits, config, blank, recurse)) {
+        psError(PS_ERR_IO, false, "Unable to write chip dark images");
+        return false;
+    }
+
+    pmHDU *hdu = pmHDUFromChip(chip);
+    if (blank || !hdu) {
+        // No more to do
+        return true;
+    }
+
+    psArray *ordinates = NULL;          // Dark ordinates, to write
+    const char *normConcept = NULL;     // Normalisation concept
+    psArray *cells = chip->cells;       // Component cells
+    for (int j = 0; j < cells->n; j++) {
+        pmCell *cell = cells->data[j]; // Cell of interest
+        bool mdok;                      // Status of MD lookup
+        psArray *newOrd = psMetadataLookupPtr(&mdok, cell->analysis, PM_DARK_ANALYSIS_ORDINATES); // Ordinates
+        if (!mdok) {
+            continue;
+        }
+        psString newNorm = psMetadataLookupPtr(&mdok, cell->analysis, PM_DARK_ANALYSIS_NORM); // Normalisation
+        if (ordinates) {
+            if (newOrd != ordinates) {
+                psError(PS_ERR_UNKNOWN, true, "Dark ordinates differ across cells.");
+                return false;
+            }
+            if ((normConcept && (!newNorm || strcmp(normConcept, newNorm) != 0)) ||
+                (!normConcept && newNorm)) {
+                psError(PS_ERR_UNKNOWN, true, "Dark normalisations differ across cells.");
+                return false;
+            }
+        } else {
+            ordinates = newOrd;
+            normConcept = newNorm;
+        }
+    }
+
+    if (!ordinates) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find dark ordinates.");
+        return false;
+    }
+
+    return pmDarkWrite(fits, hdu->header, ordinates, normConcept);
+}
+
+bool pmCellWriteDark(pmCell *cell, psFits *fits, pmConfig *config, bool blank)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    // Allow the usual pmFPAWrite functions to handle the heavy lifting for the images
+    if (!pmCellWrite(cell, fits, config, blank)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to write dark cell.");
+        return false;
+    }
+
+    pmHDU *hdu = pmHDUFromCell(cell);
+    if (blank || !hdu) {
+        // No more to do
+        return true;
+    }
+
+    psArray *ordinates = psMetadataLookupPtr(NULL, cell->analysis, PM_DARK_ANALYSIS_ORDINATES);
+    if (!ordinates) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find dark ordinates.");
+        return false;
+    }
+    bool mdok;                          // Status of MD lookup
+    psString normConcept = psMetadataLookupPtr(&mdok, cell->analysis, PM_DARK_ANALYSIS_NORM); // Normalisation
+
+    return pmDarkWrite(fits, hdu->header, ordinates, normConcept);
+}
+
+bool pmDarkWrite(psFits *fits, psMetadata *header, const psArray *ordinates, const char *normConcept)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_ARRAY_NON_NULL(ordinates, false);
+
+    // Only write table once per FITS file
+    bool write = false;             // Write table?
+    if (psFitsGetSize(fits) <= 1) {
+        write = true;
+    } else {
+        psMetadata *headers = psFitsReadHeaderSet(NULL, fits); // FITS headers
+        if (!psMetadataLookup(headers, PM_DARK_FITS_EXTNAME)) {
+            write = true;
+        }
+        psFree(headers);
+    }
+    if (!write) {
+        return true;
+    }
+
+    if (!psMemIncrRefCounter((psMetadata*)header)) {
+        header = psMetadataAlloc();
+    }
+    psMetadataAddStr(header, PS_LIST_TAIL, PM_DARK_HEADER_NORM, PS_META_REPLACE,
+                     "Dark normalisation concept", normConcept);
+
+    if (ordinates->n > 0) {
+        // Format ordinates into FITS table
+        int numOrdinates = ordinates->n;// Number of ordinates
+        psArray *table = psArrayAlloc(numOrdinates); // FITS table, constructed from ordinates
+        for (int i = 0; i < ordinates->n; i++) {
+            pmDarkOrdinate *ord = ordinates->data[i]; // Ordinate of interest
+            psMetadata *row = psMetadataAlloc(); // FITS table row
+            psMetadataAddStr(row, PS_LIST_TAIL, PM_DARK_FITS_NAME, 0, "Concept name", ord->name);
+            psMetadataAddS32(row, PS_LIST_TAIL, PM_DARK_FITS_ORDER, 0, "Polynomial order", ord->order);
+            psMetadataAddBool(row, PS_LIST_TAIL, PM_DARK_FITS_SCALE, 0, "Scale values?", ord->scale);
+            psMetadataAddF32(row, PS_LIST_TAIL, PM_DARK_FITS_MIN, 0, "Minimum value", ord->min);
+            psMetadataAddF32(row, PS_LIST_TAIL, PM_DARK_FITS_MAX, 0, "Maximum value", ord->max);
+            table->data[i] = row;
+        }
+
+        if (!psFitsWriteTable(fits, header, table, PM_DARK_FITS_EXTNAME)) {
+            psError(PS_ERR_IO, false, "Unable to write dark ordinates.");
+            psFree(table);
+            psFree(header);
+            return false;
+        }
+        psFree(table);
+    } else {
+        // No ordinates to write to a table, so write to a blank header.
+        if (!psFitsWriteBlank(fits, header, PM_DARK_FITS_EXTNAME)) {
+            psError(PS_ERR_IO, false, "Unable to write dark header.");
+            psFree(header);
+            return false;
+        }
+    }
+    psFree(header);
+
+    return true;
+}
+
+
+bool pmFPAReadDark(pmFPA *fpa, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    if (!pmFPARead(fpa, fits, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read dark FPA.");
+        return false;
+    }
+
+    psString normConcept = NULL;        // Normalisation concept
+    psArray *ordinates = pmDarkRead(&normConcept, fits); // Dark ordinates
+    if (!ordinates) {
+        psError(PS_ERR_IO, false, "Unable to read dark ordinates.");
+        return false;
+    }
+
+    psArray *chips = fpa->chips;        // Component chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // Chip of interest
+        psArray *cells = chip->cells;   // Component cells
+        for (int j = 0; j < cells->n; j++) {
+            pmCell *cell = cells->data[j]; // Cell of interest
+            psMetadataAddPtr(cell->analysis, PS_LIST_TAIL, PM_DARK_ANALYSIS_ORDINATES,
+                             PS_DATA_ARRAY | PS_META_REPLACE, "Dark ordinates", ordinates);
+            psMetadataAddStr(cell->analysis, PS_LIST_TAIL, PM_DARK_ANALYSIS_NORM, PS_META_REPLACE,
+                             "Dark normalisation", normConcept);
+        }
+    }
+    psFree(ordinates);
+    psFree(normConcept);
+
+    return true;
+}
+
+bool pmChipReadDark(pmChip *chip, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    if (!pmChipRead(chip, fits, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read dark chip.");
+        return false;
+    }
+
+    psString normConcept = NULL;        // Normalisation concept
+    psArray *ordinates = pmDarkRead(&normConcept, fits); // Dark ordinates
+    if (!ordinates) {
+        psError(PS_ERR_IO, false, "Unable to read dark ordinates.");
+        return false;
+    }
+
+    psArray *cells = chip->cells;       // Component cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        psMetadataAddPtr(cell->analysis, PS_LIST_TAIL, PM_DARK_ANALYSIS_ORDINATES,
+                         PS_DATA_ARRAY | PS_META_REPLACE, "Dark ordinates", ordinates);
+        psMetadataAddStr(cell->analysis, PS_LIST_TAIL, PM_DARK_ANALYSIS_NORM, PS_META_REPLACE,
+                         "Dark normalisation", normConcept);
+    }
+    psFree(ordinates);
+    psFree(normConcept);
+
+    return true;
+}
+
+
+bool pmCellReadDark(pmCell *cell, psFits *fits, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    // Allow the usual pmFPARead functions to handle the heavy lifting for the images
+    if (!pmCellRead(cell, fits, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read dark cell.");
+        return false;
+    }
+
+    psString normConcept = NULL;        // Normalisation concept
+    psArray *ordinates = pmDarkRead(&normConcept, fits); // Dark ordinates
+    if (!ordinates) {
+        psError(PS_ERR_IO, false, "Unable to read dark ordinates.");
+        return false;
+    }
+    psMetadataAddPtr(cell->analysis, PS_LIST_TAIL, PM_DARK_ANALYSIS_ORDINATES,
+                     PS_DATA_ARRAY | PS_META_REPLACE, "Dark ordinates", ordinates);
+    psMetadataAddStr(cell->analysis, PS_LIST_TAIL, PM_DARK_ANALYSIS_NORM, PS_META_REPLACE,
+                     "Dark normalisation", normConcept);
+    psFree(ordinates);
+    psFree(normConcept);
+
+    return true;
+}
+
+psArray *pmDarkRead(psString *normConcept, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(normConcept, NULL);
+    PS_ASSERT_PTR_NULL(*normConcept, NULL);
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    if (!psFitsMoveExtName(fits, PM_DARK_FITS_EXTNAME)) {
+        psError(PS_ERR_IO, false, "Unable to find extension with dark ordinates table (%s).",
+                PM_DARK_FITS_EXTNAME);
+        return false;
+    }
+
+    psMetadata *header = psFitsReadHeader(NULL, fits); // Header
+    bool mdok;                          // Status of MD lookup
+    *normConcept = psMemIncrRefCounter(psMetadataLookupStr(&mdok, header, PM_DARK_HEADER_NORM));
+
+    psArray *ordinates = NULL;          // Dark ordinates to return
+
+    psFitsType type = psFitsGetExtType(fits); // Type of FITS extension
+    switch (type) {
+      case PS_FITS_TYPE_IMAGE: {
+          // Check that it's of zero size; otherwise we might have some conflict
+          int numCols = psMetadataLookupS32(&mdok, header, "NAXIS1");
+          int numRows = psMetadataLookupS32(&mdok, header, "NAXIS2");
+          if (numCols != 0 || numRows != 0) {
+              psError(PS_ERR_UNKNOWN, true, "Extension %s is not a DARK table.", PM_DARK_FITS_EXTNAME);
+              psFree(header);
+              return NULL;
+          }
+          // No ordinates fit --- only a constant term
+          ordinates = psArrayAlloc(0);
+          break;
+      }
+      case PS_FITS_TYPE_BINARY_TABLE:
+      case PS_FITS_TYPE_ASCII_TABLE: {
+          psArray *table = psFitsReadTable(fits); // FITS Table with ordinates
+          int numOrdinates = table->n;        // Number of ordinates
+          ordinates = psArrayAlloc(numOrdinates);
+
+          for (int i = 0; i < numOrdinates; i++) {
+              psMetadata *row = table->data[i]; // Row of interest
+              const char *name = psMetadataLookupStr(NULL, row, PM_DARK_FITS_NAME); // Concept name
+              int order = psMetadataLookupS32(NULL, row, PM_DARK_FITS_ORDER); // Polynomial order
+              if (!name || order <= 0) {
+                  psError(PS_ERR_UNKNOWN, false, "Bad value reading dark ordinates table.");
+                  psFree(table);
+                  psFree(ordinates);
+                  return false;
+              }
+              pmDarkOrdinate *ord = pmDarkOrdinateAlloc(name, order); // Ordinate data
+              ord->scale = psMetadataLookupBool(NULL, row, PM_DARK_FITS_SCALE);
+              ord->min = psMetadataLookupF32(NULL, row, PM_DARK_FITS_MIN);
+              ord->max = psMetadataLookupF32(NULL, row, PM_DARK_FITS_MAX);
+
+              ordinates->data[i] = ord;
+          }
+          psFree(table);
+          break;
+      }
+      default:
+        psError(PS_ERR_UNKNOWN, true, "Unrecognised FITS extension type.");
+        return NULL;
+    }
+    psFree(header);
+
+    return ordinates;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDark.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDark.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDark.h	(revision 22322)
@@ -0,0 +1,119 @@
+#ifndef PM_DARK_H
+#define PM_DARK_H
+
+#include <pslib.h>
+#include <pmHDU.h>
+#include <pmFPA.h>
+#include <pmConfig.h>
+
+#define PM_DARK_ANALYSIS_ORDINATES "DARK.ORDINATES" // Name for dark ordinates in the cell analysis metadata
+#define PM_DARK_ANALYSIS_NORM "DARK.NORM" // Name for dark normalisation concept in cell analysis metadata
+#define PM_DARK_HEADER_NORM "PSDRKNRM"  // Header keyword for dark normalisation concept
+
+// An ordinate for fitting darks
+typedef struct {
+    psString name;                      // Name of concept to fit
+    int order;                          // Polynomial order to fit
+    bool scale;                         // Rescale values?
+    float min, max;                     // Minimum and maximum values for rescaling
+} pmDarkOrdinate;
+
+// Allocator
+pmDarkOrdinate *pmDarkOrdinateAlloc(const char *name, // Name for ordinate
+                                    int order // Order for ordinate
+    );
+
+
+// Combine darks -- preparation step
+bool pmDarkCombinePrepare(pmCell *output,      // Output cell; readouts will be attached
+                          const psArray *inputs, // Input readouts for combination
+                          psArray *ordinates,  // Ordinates for fitting
+                          const char *normConcept // Concept name to use to divide input pixel values
+    );
+
+// Combine darks -- do the actual work
+bool pmDarkCombine(pmCell *output,      // Output cell; readouts will be attached
+                   const psArray *inputs, // Input readouts for combination
+                   int iter,            // Number of rejection iterations
+                   float rej,           // Rejection threshold (standard deviations)
+                   psMaskType maskVal   // Value to mask
+    );
+
+// Thread entry point for pmDarkApplyScan
+bool pmDarkApplyScan_Threaded(psThreadJob *job // Job to execute
+    );
+
+// Apply the dark correction to a scan
+bool pmDarkApplyScan(pmReadout *readout, // Readout to correct
+                     const pmCell *dark, // Dark to apply
+                     const psVector *orders, // Polynomial orders for each ordinate
+                     const psVector *values, // Values for each ordinate
+                     psMaskType bad,    // Value to give bad pixels
+                     bool doNorm,       // Normalise values?
+                     float norm,        // Value by which to normalise
+                     int rowStart, int rowStop // Scan range to work on
+    );
+
+// Apply dark
+bool pmDarkApply(pmReadout *readout,    // Readout to which to apply dark
+                 pmCell *dark,    // Dark to apply
+                 psMaskType bad         // Mask value to give bad pixels
+    );
+
+// I/O functions for darks
+
+// Write all darks within an FPA
+bool pmFPAWriteDark(pmFPA *fpa,         // FPA to write
+                    psFits *fits,       // FITS file to which to write
+                    pmConfig *config,   // Configuration
+                    bool blank,         // Write a blank only?
+                    bool recurse        // Recurse to lower levels?
+    );
+
+// Write all darks within a chip
+bool pmChipWriteDark(pmChip *chip,      // Chip to write
+                     psFits *fits,      // FITS file to which to write
+                     pmConfig *config,  // Configuration
+                     bool blank,        // Write a blank only?
+                     bool recurse       // Recurse to lower levels?
+    );
+
+// Write a dark to a FITS file
+bool pmCellWriteDark(pmCell *cell,      // Cell containing dark information
+                     psFits *fits,      // FITS file to which to write
+                     pmConfig *config,  // Configuration
+                     bool blank         // Write a blank only?
+    );
+
+// Read dark for all FPA from a FITS file
+bool pmFPAReadDark(pmFPA *fpa,          // FPA for which to read
+                   psFits *fits,        // FITS file to read
+                   pmConfig *config     // Configuration
+    );
+
+// Read dark for all chip from a FITS file
+bool pmChipReadDark(pmChip *chip,       // Chip for which to read
+                    psFits *fits,       // FITS file to read
+                    pmConfig *config    // Configuration
+    );
+
+// Read dark for a cell from a FITS file
+bool pmCellReadDark(pmCell *cell,       // Cell for which to read
+                    psFits *fits,       // FITS file to read
+                    pmConfig *config    // Configuration
+    );
+
+// Write dark table to FITS file
+bool pmDarkWrite(psFits *fits,          // FITS file to which to write
+                 psMetadata *header,    // Header to write
+                 const psArray *ordinates, // Dark ordinates to write
+                 const char *normConcept // Normalisation concept name
+    );
+
+// Read dark table from FITS file
+psArray *pmDarkRead(psString *normConcept, // Normalisation concept name
+                    psFits *fits        // FITS file to read
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendDB.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendDB.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendDB.c	(revision 22322)
@@ -0,0 +1,340 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmConfigCommand.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmConfigCamera.h"
+#include "pmDetrendDB.h"
+#include "psPipe.h"
+#include "psIOBuffer.h"
+
+// ************* detrend select functions **************
+
+//
+static void pmDetrendSelectOptionsFree (pmDetrendSelectOptions *options)
+{
+
+    if (!options)
+        return;
+
+    psFree (options->camera);
+    psFree (options->filter);
+    psFree (options->dettype);
+    psFree (options->version);
+
+    return;
+}
+
+// define basic options for a new detrend database query
+pmDetrendSelectOptions *pmDetrendSelectOptionsAlloc(const char *camera, psTime time, pmDetrendType type)
+{
+
+    pmDetrendSelectOptions *options = psAlloc(sizeof(pmDetrendSelectOptions));
+    psMemSetDeallocator(options, (psFreeFunc) pmDetrendSelectOptionsFree);
+
+    // basic options required by every query
+    options->camera = psStringCopy (camera);
+    options->time   = time;
+    options->type   = type;
+
+    // these other options depend on the type of detrend data
+    options->filter   = NULL;
+    options->version  = NULL;
+    options->dettype  = NULL;
+    options->exptime  = 0.0;
+    options->airmass  = 0.0;
+    options->dettemp  = 0.0;
+    options->twilight = 0.0;
+
+    options->exptimeSet  = false; // not selected
+    options->airmassSet  = false; // not selected
+    options->dettempSet  = false; // not selected
+    options->twilightSet = false; // not selected
+
+    return options;
+}
+
+static void pmDetrendSelectResultsFree (pmDetrendSelectResults *results)
+{
+
+    if (!results)
+        return;
+
+    psFree (results->detID);
+    psFree(results->level);
+
+    return;
+}
+
+pmDetrendSelectResults *pmDetrendSelectResultsAlloc ()
+{
+
+    pmDetrendSelectResults *results = psAlloc(sizeof(pmDetrendSelectResults));
+    psMemSetDeallocator(results, (psFreeFunc) pmDetrendSelectResultsFree);
+
+    results->detID = NULL;
+    results->level = NULL;
+
+    return results;
+}
+
+psString pmDetrendTypeToString (pmDetrendType type)
+{
+
+    # define DETREND_STRING_CASE(TTT) \
+case PM_DETREND_TYPE_##TTT: \
+    return psStringCopy (#TTT);
+
+    switch (type) {
+        DETREND_STRING_CASE (MASK);
+        DETREND_STRING_CASE (BIAS);
+        DETREND_STRING_CASE (DARK);
+        DETREND_STRING_CASE (FLAT);
+        DETREND_STRING_CASE (FLAT_CORRECTION);
+        DETREND_STRING_CASE (SHUTTER);
+        DETREND_STRING_CASE (FRINGE);
+        DETREND_STRING_CASE (ASTROM);
+    default:
+        return NULL;
+    }
+    return NULL;
+}
+
+// detselect -camera (camera) -time (time) -type (type) [others]
+// returns: (type) (class) (exp_flag) DONE
+pmDetrendSelectResults *pmDetrendSelect (const pmDetrendSelectOptions *options,
+        const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(options, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    int status, exit_status;
+    psString line = NULL;
+    psString time = psTimeToISO (&options->time);
+
+    char *type = NULL;
+    if (options->dettype) {
+        type = psMemIncrRefCounter (options->dettype);
+    } else {
+        type = pmDetrendTypeToString (options->type);
+    }
+    unsigned int nFail;
+
+    pmDetrendSelectResults *results = pmDetrendSelectResultsAlloc();
+    psString realCamera = pmConfigCameraRootName (options->camera);
+    psStringAppend(&line, "detselect -search -inst %s -det_type %s -time %s", realCamera, type, time);
+    psFree (realCamera);
+
+    if (options->filter) {
+        psStringAppend(&line, " -filter %s", options->filter);
+    }
+    if (options->version) {
+        psStringAppend(&line, " -version %s", options->version);
+    }
+    if (options->exptimeSet) {
+        psStringAppend(&line, " -exp_time %f", options->exptime);
+    }
+    if (options->airmassSet) {
+        psStringAppend(&line, " -airmass %f", options->airmass);
+    }
+    if (options->dettempSet) {
+        psStringAppend(&line, " -airmass %f", options->dettemp);
+    }
+    if (options->twilightSet) {
+        psStringAppend(&line, " -airmass %f", options->twilight);
+    }
+
+    psIOBuffer *buffer = NULL;
+    psPipe *pipe = NULL;
+    psMetadata *answer = NULL;
+
+    if (!pmConfigDatabaseCommand(&line, config)) {
+        psError (PS_ERR_IO, false, "error building detrend command %s", line);
+        goto failure;
+    }
+
+    if (!pmConfigTraceCommand(&line)) {
+        psError (PS_ERR_IO, false, "error building detrend command %s", line);
+        goto failure;
+    }
+
+    psTrace("psModules.detrend", 5, "running %s", line);
+
+    // use psPipe to exec the command, wait for response
+    buffer = psIOBufferAlloc (512);
+    pipe = psPipeOpen (line);
+    if (!pipe) {
+        psError (PS_ERR_IO, false, "error calling command %s", line);
+        goto failure;
+    }
+
+    status = psIOBufferReadEmpty (buffer, 2000, pipe->fd_stdout);
+    if (!status) {
+        psError (PS_ERR_IO, false, "detselect is not responding");
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect command:\n %s\n", line);
+        goto failure;
+    }
+    exit_status = psPipeClose (pipe);
+    if (exit_status) {
+        psError (PS_ERR_IO, false, "error running detselect");
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect command:\n %s\n", line);
+        goto failure;
+    }
+
+    psTrace("psModules.detrend", 5, "got answer: %s\n", buffer->data);
+
+    nFail = 0;
+    answer = psMetadataConfigParse (NULL, &nFail, buffer->data, false);
+    if (!answer) {
+        psError(PS_ERR_IO, false, "failed to parse response from detselect\n");
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect response (%d bytes):\n %s\n", buffer->n, buffer->data);
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect command:\n %s\n", line);
+        goto failure;
+    }
+
+    psMetadata *md = psMetadataLookupPtr (NULL, answer, "detExp");
+    if (!md) {
+        psError(PS_ERR_IO, false, "detselect response is missing 'detExp' Metadata\n");
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect response:\n %s\n", buffer->data);
+        goto failure;
+    }
+
+    bool mdstatus;
+    int detID = psMetadataLookupS32 (&mdstatus, md, "det_id");
+    if (!mdstatus) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find det_id in output from detselect.");
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect response:\n %s\n", buffer->data);
+        goto failure;
+    }
+    int iteration  = psMetadataLookupS32 (&mdstatus, md, "iteration");
+    if (!mdstatus) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find iteration in output from detselect.");
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect response:\n %s\n", buffer->data);
+        goto failure;
+    }
+    psString fileLevel = psMetadataLookupStr(&mdstatus, md, "filelevel");
+    if (!mdstatus || !fileLevel || strlen(fileLevel) == 0) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find filelevel in output from detselect.");
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect response:\n %s\n", buffer->data);
+        goto failure;
+    }
+
+    results->detID = NULL; // it should be NULL already from the Alloc above
+    psStringAppend (&results->detID, " -det_id %d -iteration %d ", detID, iteration);
+    results->level = psMemIncrRefCounter(fileLevel);
+
+    psTrace("psModules.detrend", 5, "generated detID %s\n", results->detID);
+
+    psFree (answer);
+    psFree (buffer);
+    psFree (pipe);
+    psFree (line);
+    psFree (time);
+    psFree (type);
+    return results;
+
+failure:
+    psFree (answer);
+    psFree (results);
+    psFree (pipe);
+    psFree (buffer);
+    psFree (line);
+    psFree (type);
+    psFree (time);
+    return NULL;
+}
+
+// ************* detrend file functions **************
+
+// detselect -select -detID (detID) -classID (classID)
+// returns: (detID) (classID) (filename) DONE
+char *pmDetrendFile (const char *detID, const char *classID, const pmConfig *config)
+{
+    unsigned int nFail;
+
+    PS_ASSERT_PTR_NON_NULL(detID, NULL);
+
+    bool status;
+    psString line = NULL;
+    psArray *array = NULL;
+
+    // generate the detselect command
+    psStringAppend (&line, "detselect -select %s", detID);
+    if (classID && strlen(classID) > 0) {
+        psStringAppend(&line, " -class_id %s", classID);
+    }
+    pmConfigDatabaseCommand(&line, config);
+    pmConfigTraceCommand(&line);
+    psTrace("psModules.detrend", 5, "running %s", line);
+
+    // use psPipe to exec the command, wait for response
+    psIOBuffer *buffer = psIOBufferAlloc (512);
+    psPipe *pipe = psPipeOpen (line);
+    if (!pipe) {
+        psError (PS_ERR_IO, false, "error calling command %s", line);
+        goto failure;
+    }
+
+    // timeout somewhat longer than 2sec.  this could still be too short....
+    status = psIOBufferReadEmpty (buffer, 2000, pipe->fd_stdout);
+    if (!status) {
+        psError (PS_ERR_IO, false, "detselect is not responding");
+        goto failure;
+    }
+    status = psPipeClose (pipe);
+    if (status) {
+        psError (PS_ERR_IO, false, "error running detselect");
+        goto failure;
+    }
+
+    psTrace("psModules.detrend", 5, "got answer: %s\n", buffer->data);
+
+    if (!buffer->n) {
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect response (%d bytes):\n %s\n", buffer->n, buffer->data);
+        psError(PS_ERR_IO, true, "no matching detrend data in database\n");
+        goto failure;
+    }
+
+    psMetadata *answer = psMetadataConfigParse (NULL, &nFail, buffer->data, false);
+    if (!answer) {
+        psError(PS_ERR_IO, false, "failed to parse response from detselect\n");
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect response (%d bytes):\n %s\n", buffer->n, buffer->data);
+        goto failure;
+    }
+    psMetadataItem *item = psMetadataLookup (answer, "detNormalizedImfile");
+    if ((item->type == PS_DATA_METADATA_MULTI) && (item->data.list->n > 1)) {
+        psError(PS_ERR_IO, false, "detselect returned too many files\n");
+        goto failure;
+    }
+
+    psMetadata *md = psMetadataLookupPtr (NULL, answer, "detNormalizedImfile");
+    if (!md) {
+        psError(PS_ERR_IO, false, "detselect response is missing 'detNormalizedImfile' Metadata\n");
+        psLogMsg ("psModules.detrend", PS_LOG_ERROR, "detselect response:\n %s\n", buffer->data);
+        goto failure;
+    }
+
+    char *result = psStringCopy (psMetadataLookupStr (NULL, md, "uri"));
+    psTrace("psModules.detrend", 5, "detrend file: %s\n", result);
+
+    psFree (answer);
+    psFree (pipe);
+    psFree (buffer);
+    psFree (line);
+    return result;
+
+failure:
+    psFree (array);
+    psFree (pipe);
+    psFree (buffer);
+    psFree (line);
+    return NULL;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendDB.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendDB.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendDB.h	(revision 22322)
@@ -0,0 +1,69 @@
+/* @file  pmDetrendDB.h
+ * @brief Tools to query the detrend database system
+ *
+ * the functions in here do not perform the detrend database queries directly.  all interfaces
+ * to the detrend database go through the external dettools functions.  this allows the modules
+ * and directly dependent program to be sufficiently independent of the database schema that it
+ * can be used with any properly defined detrend database tables.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-25 22:07:29 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_DETREND_DB_H
+#define PM_DETREND_DB_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+#include <pslib.h>
+
+#include "pmConfig.h"
+
+typedef enum {
+    PM_DETREND_TYPE_MASK,
+    PM_DETREND_TYPE_BIAS,
+    PM_DETREND_TYPE_DARK,
+    PM_DETREND_TYPE_FLAT,
+    PM_DETREND_TYPE_FLAT_CORRECTION,
+    PM_DETREND_TYPE_SHUTTER,
+    PM_DETREND_TYPE_FRINGE,
+    PM_DETREND_TYPE_BACKGROUND,
+    PM_DETREND_TYPE_ASTROM,
+} pmDetrendType;
+
+typedef struct {
+    char *camera;                       // name of camera
+    char *version;                      // optional version string
+    char *filter;                       // name of filter
+    char *dettype;                      // actual detrend type name
+    float exptime;                      // exposure time (for dark, maybe flat & fringe)
+    float airmass;                      // for fringe
+    float dettemp;                      // for fringe
+    float twilight;                     // hours (or seconds?) since/before nearest twilight
+    psTime time;                        // time of input data
+    pmDetrendType type;                 // type of detrend data
+
+    bool  exptimeSet;
+    bool  airmassSet;
+    bool  dettempSet;
+    bool  twilightSet;
+} pmDetrendSelectOptions;
+
+typedef struct {
+    char *detID;                        // identifier of detrend run
+    psString level;                     // level in FPA hierarchy of individual file
+} pmDetrendSelectResults;
+
+psString pmDetrendTypeToString (pmDetrendType type);
+
+pmDetrendSelectOptions *pmDetrendSelectOptionsAlloc(const char *camera, psTime time, pmDetrendType type);
+pmDetrendSelectResults *pmDetrendSelectResultsAlloc();
+pmDetrendSelectResults *pmDetrendSelect (const pmDetrendSelectOptions *options, const pmConfig *config);
+char *pmDetrendFile (const char *detID, const char *classID, const pmConfig *config);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendThreads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendThreads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendThreads.c	(revision 22322)
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <string.h>
+
+#include "psPolynomialMD.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+
+#include "pmOverscan.h"
+#include "pmBias.h"
+#include "pmDark.h"
+#include "pmShutterCorrection.h"
+#include "pmFlatField.h"
+#include "pmDetrendThreads.h"
+
+static int scanRows = 0;                // Number of rows to work on at once
+
+int pmDetrendGetScanRows(void)
+{
+    return scanRows;
+}
+
+bool pmDetrendSetThreadTasks (int newScanRows)
+{
+    psAssert(scanRows == 0, "programming error: program called pmDetrendSetThreadTasks twice");
+
+    PS_ASSERT_INT_POSITIVE(newScanRows, false);
+    scanRows = newScanRows;
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PSMODULES_DETREND_BIAS", 7);
+        task->function = &pmBiasSubtractScan_Threaded;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PSMODULES_DETREND_DARK", 9);
+        task->function = &pmDarkApplyScan_Threaded;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PSMODULES_DETREND_SHUTTER", 7);
+        task->function = &pmShutterCorrectionApplyScan_Threaded;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PSMODULES_DETREND_FLAT", 9);
+        task->function = &pmFlatFieldScan_Threaded;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendThreads.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendThreads.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmDetrendThreads.h	(revision 22322)
@@ -0,0 +1,23 @@
+/* @file pmDetrendThreads.h
+ * @brief theading functions related to detrends
+ * @author Eugene Magnier, IfA
+ *
+ * @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-05 01:24:47 $
+ * Copyright 2004-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_DETREND_THREADS_H
+#define PM_DETREND_THREADS_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+/// init the thread handler tasks for detrending
+bool pmDetrendSetThreadTasks (int newScanRows);
+
+/// get the requested number of scan rows per thread
+int pmDetrendGetScanRows ();
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatField.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatField.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatField.c	(revision 22322)
@@ -0,0 +1,185 @@
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "pmFPAMaskWeight.h"
+#include "pmFlatField.h"
+#include "pmDetrendThreads.h"
+
+bool pmFlatFieldScan_Threaded(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    psImage *inImage   = job->args->data[0]; // Input image
+    psImage *inMask    = job->args->data[1]; // Input mask
+    const psImage *flatImage = job->args->data[2]; // Flat-field image
+    const psImage *flatMask  = job->args->data[3]; // Flat-field mask
+
+    psMaskType badFlat = PS_SCALAR_VALUE(job->args->data[4],U8);
+    int xOffset        = PS_SCALAR_VALUE(job->args->data[5],S32);
+    int yOffset        = PS_SCALAR_VALUE(job->args->data[6],S32);
+    int rowStart       = PS_SCALAR_VALUE(job->args->data[7],S32);
+    int rowStop        = PS_SCALAR_VALUE(job->args->data[8],S32);
+    return pmFlatFieldScan(inImage, inMask, flatImage, flatMask, badFlat,
+                           xOffset, yOffset, rowStart, rowStop);
+}
+
+// Macro for all PS types
+#define FLAT_DIVISION_CASE(TYPE, SPECIAL)       \
+    case PS_TYPE_##TYPE:                        \
+    for (int j = rowStart; j < rowStop; j++) { \
+        for (int i = 0; i < inImage->numCols; i++) { \
+            ps##TYPE flatValue = flatImage->data.TYPE[j + yOffset][i + xOffset]; \
+            if (!isfinite(flatValue) || flatValue <= 0.0 || \
+                (flatMask && flatMask->data.U8[j + yOffset][i + xOffset])) { \
+                if (inMask) { \
+                    inMask->data.PS_TYPE_MASK_DATA[j][i] |= badFlat; \
+                } \
+                inImage->data.TYPE[j][i] = SPECIAL; \
+            } else { \
+                inImage->data.TYPE[j][i] /= flatValue; \
+            } \
+        } \
+    } \
+    break;
+
+bool pmFlatFieldScan(psImage *inImage, psImage *inMask, const psImage *flatImage, const psImage *flatMask,
+                     psMaskType badFlat, int xOffset, int yOffset, int rowStart, int rowStop)
+{
+    switch (inImage->type.type) {
+        FLAT_DIVISION_CASE(U8,  0);
+        FLAT_DIVISION_CASE(U16, 0);
+        FLAT_DIVISION_CASE(U32, 0);
+        FLAT_DIVISION_CASE(U64, 0);
+        FLAT_DIVISION_CASE(S8,  0);
+        FLAT_DIVISION_CASE(S16, 0);
+        FLAT_DIVISION_CASE(S32, 0);
+        FLAT_DIVISION_CASE(S64, 0);
+        FLAT_DIVISION_CASE(F32, NAN);
+        FLAT_DIVISION_CASE(F64, NAN);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unsupported type for input image: %x\n",
+                inImage->type.type);
+        return false;
+    }
+    return true;
+}
+
+bool pmFlatField(pmReadout *in, const pmReadout *flat, psMaskType badFlat)
+{
+    PS_ASSERT_PTR_NON_NULL(in, false);
+    PS_ASSERT_PTR_NON_NULL(in->image, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(in->image, false);
+    PS_ASSERT_PTR_NON_NULL(flat, false);
+    PS_ASSERT_PTR_NON_NULL(flat->image, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(flat->image, false);
+    if (in->mask) {
+        PS_ASSERT_IMAGE_TYPE(in->mask, PS_TYPE_MASK, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(in->mask, in->image, false);
+    }
+    PS_ASSERT_IMAGE_TYPE(flat->image, in->image->type.type, false);
+    if (flat->mask) {
+        PS_ASSERT_IMAGE_TYPE(flat->mask, PS_TYPE_MASK, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(flat->mask, flat->image, false);
+    }
+
+    psImage *inImage   = in->image;     // Input image
+    psImage *inMask    = in->mask;      // Mask for input image
+    psImage *flatImage = flat->image;   // Flat-field image
+    psImage *flatMask  = flat->mask;    // Mask for flat-field image
+
+    // Add flat-field MD5 to header
+    pmHDU *hdu = pmHDUFromReadout(in);  // HDU of interest
+    psVector *md5 = psImageMD5(flat->image); // md5 hash
+    psString md5string = psMD5toString(md5); // String
+    psFree(md5);
+    psStringPrepend(&md5string, "FLAT image MD5: ");
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                     md5string, "");
+    psFree(md5string);
+
+    // Check input image is not larger than flat image; mask is the same size as the input
+    if (inImage->numRows > flatImage->numRows || inImage->numCols > flatImage->numCols) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Input image (%dx%d) is larger than flat-field image "
+                "(%dx%d).\n", inImage->numCols, inImage->numRows, flatImage->numCols, flatImage->numRows);
+        return false;
+    }
+
+    // Offsets on the chip
+    int x0in = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.X0");
+    int y0in = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.Y0");
+    int x0flat = psMetadataLookupS32(NULL, flat->parent->concepts, "CELL.X0");
+    int y0flat = psMetadataLookupS32(NULL, flat->parent->concepts, "CELL.Y0");
+
+    // Determine offset based on image offset with chip offset: input frame to flat frame
+    int yOffset = in->row0 + y0in - flat->row0 - y0flat;
+    int xOffset = in->col0 + x0in - flat->col0 - x0flat;
+
+    // Check that offsets are within image limits
+    if (inImage->numRows + yOffset > flatImage->numRows ||
+            inImage->numCols + xOffset > flatImage->numCols) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Input image (%dx%d) with offsets (%d,%d) is larger than "
+                "flat-field image (%dx%d).\n", inImage->numCols, inImage->numRows, xOffset, yOffset,
+                flatImage->numCols, flatImage->numRows);
+        return false;
+    }
+
+    bool threaded = true;
+    int scanRows = pmDetrendGetScanRows();
+    if (scanRows == 0) {
+        threaded = false;
+        scanRows = inImage->numRows;
+    }
+
+    for (int rowStart = 0; rowStart < inImage->numRows; rowStart += scanRows) {
+      int rowStop = PS_MIN(rowStart + scanRows, inImage->numRows);
+
+      if (threaded) {
+          // allocate a job, construct the arguments for this job
+          psThreadJob *job = psThreadJobAlloc("PSMODULES_DETREND_FLAT");
+          psArrayAdd(job->args, 1, inImage);
+          psArrayAdd(job->args, 1, inMask);
+          psArrayAdd(job->args, 1, flatImage);
+          psArrayAdd(job->args, 1, flatMask);
+          PS_ARRAY_ADD_SCALAR(job->args, badFlat, PS_TYPE_U8);
+          PS_ARRAY_ADD_SCALAR(job->args, xOffset, PS_TYPE_S32);
+          PS_ARRAY_ADD_SCALAR(job->args, yOffset, PS_TYPE_S32);
+          PS_ARRAY_ADD_SCALAR(job->args, rowStart, PS_TYPE_S32);
+          PS_ARRAY_ADD_SCALAR(job->args, rowStop, PS_TYPE_S32);
+
+          if (!psThreadJobAddPending(job)) {
+              psFree(job);
+              return false;
+          }
+          psFree(job);
+      } else if (!pmFlatFieldScan(inImage, inMask, flatImage, flatMask, badFlat,
+                                  xOffset, yOffset, rowStart, rowStop)) {
+          psError(PS_ERR_UNKNOWN, false, "Unable to flat-field image.");
+          return false;
+      }
+    }
+
+    if (threaded) {
+        // wait here for the threaded jobs to finish
+        if (!psThreadPoolWait(true)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to flat-field image.");
+            return false;
+        }
+    }
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now, used for reporting
+    psString timeString = psTimeToISO(time); // String with time
+    psFree(time);
+    psStringPrepend(&timeString, "Flat-field processing completed at ");
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                     timeString, "");
+    psFree(timeString);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatField.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatField.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatField.h	(revision 22322)
@@ -0,0 +1,47 @@
+/* @file pmFlatField.h
+ * @brief Apply flat field calibration
+ *
+ * @author Ross Harman, MHPCC
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-09-09 04:10:14 $
+ * Copyright 2004-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FLAT_FIELD_H
+#define PM_FLAT_FIELD_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+/// Apply flat field calibration to a readout
+///
+/// This function applies the flat field calibration to the input readout.  Support is available for different
+/// image types, though the input and flat images must have the same type.  The relative offsets between the
+/// input and flat images is determined from the readout row0,col0 and the CELL.X0 and CELL.Y0 concepts.
+/// Normalisation of the flat is left as the responsibility of the caller.  Non-positive pixels in the flat
+/// are masked, if there is a mask present in the input readout.
+bool pmFlatField(pmReadout *in,         ///< Readout with input image
+                 const pmReadout *flat,  ///< Readout with flat image
+                 psMaskType badFlat     ///< Mask value to give bad flat pixels
+                );
+
+/// Thread entry point for flat-fielding
+bool pmFlatFieldScan_Threaded(psThreadJob *job ///< Job to exectute
+    );
+
+/// Flat-field a scan
+bool pmFlatFieldScan(
+    psImage *inImage,                   ///< Input image to correct
+    psImage *inMask,                    ///< Input mask image
+    const psImage *flatImage,           ///< Flat-field image
+    const psImage *flatMask,            ///< Flat-field mask
+    psMaskType badFlag,                 ///< Mask value to give bad pixels
+    int xOffset, int yOffset,           ///< Offset between input and flat-field
+    int rowStart, int rowStop           ///< Scan range
+    );
+
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatNormalize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatNormalize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatNormalize.c	(revision 22322)
@@ -0,0 +1,189 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+#include <pslib.h>
+
+#include "pmFlatNormalize.h"
+
+// I'm not sure that many many iterations are required, but rather suspect that the system converges within a
+// few with absolutely no trouble (it *is* over-constrained).  For this reason, I'm putting the maximum number
+// of iterations and tolerance as preset values.
+#define MAXITER 10                      // Maximum number of iterations
+#define TOLERANCE 1e-3                  // Minimum tolerance for convergance
+
+
+bool pmFlatNormalize(psVector **expFluxesPtr, psVector **chipGainsPtr, const psImage *bgMatrix)
+{
+    PS_ASSERT_PTR_NON_NULL(bgMatrix, false);
+    PS_ASSERT_IMAGE_NON_NULL(bgMatrix, false);
+
+    int numExps = bgMatrix->numRows; // Number of exposures
+    int numChips = bgMatrix->numCols; // Number of chips with which each exposure is made
+
+    psVector *expFluxes;                // Dereferenced version of expFluxesPtr
+    if (expFluxesPtr) {
+        if (*expFluxesPtr) {
+            PS_ASSERT_VECTOR_TYPE(*expFluxesPtr, PS_TYPE_F32, false);
+            PS_ASSERT_VECTOR_SIZE(*expFluxesPtr, (long)numExps, false);
+        } else {
+            *expFluxesPtr = psVectorAlloc(numExps, PS_TYPE_F32);
+        }
+        expFluxes = psMemIncrRefCounter(*expFluxesPtr);
+    } else {
+        expFluxes = psVectorAlloc(numExps, PS_TYPE_F32);
+    }
+
+    psVector *chipGains;                // Dereferenced version of chipGainsPtr
+    if (chipGainsPtr) {
+        if (*chipGainsPtr) {
+            PS_ASSERT_VECTOR_TYPE(*chipGainsPtr, PS_TYPE_F32, false);
+            PS_ASSERT_VECTOR_SIZE(*chipGainsPtr, (long)numChips, false);
+        } else {
+            *chipGainsPtr = psVectorAlloc(numChips, PS_TYPE_F32);
+            psVectorInit(*chipGainsPtr, 1.0);
+        }
+        chipGains = psMemIncrRefCounter(*chipGainsPtr);
+    } else {
+        chipGains = psVectorAlloc(numChips, PS_TYPE_F32);
+        psVectorInit(chipGains, 1.0);
+    }
+
+    // Take the logarithms
+    psImage *flux = psImageCopy(NULL, bgMatrix, PS_TYPE_F32); // Copy of the input flux levels matrix
+    psImage *fluxMask = psImageAlloc(numChips, numExps, PS_TYPE_U8); // Mask for bad measurements
+    psImageInit(fluxMask, 0);
+    psVector *gainMask = psVectorAlloc(numChips, PS_TYPE_U8); // Mask for bad gains
+    psVectorInit(gainMask, 0);
+    psVector *expMask = psVectorAlloc(numExps, PS_TYPE_U8); // Mask for bad exposures
+    psVectorInit(expMask, 0);
+    for (int i = 0; i < numChips; i++) {
+        // Note: the input gains are in e/ADU; we want to work with ADU/e (bg [ADU] = g [ADU/e] * f [e])
+        // Hence the minus sign
+        if (isfinite(chipGains->data.F32[i]) && chipGains->data.F32[i] > 0) {
+            chipGains->data.F32[i] = -logf(chipGains->data.F32[i]);
+        } else {
+            chipGains->data.F32[i] = 0.0; // Take a wild guess, gain ~ 1 e/ADU
+        }
+
+        for (int j = 0; j < numExps; j++) {
+            if (isfinite(flux->data.F32[j][i]) && flux->data.F32[j][i] > 0) {
+                flux->data.F32[j][i] = logf(flux->data.F32[j][i]);
+            } else {
+                // Blank out this measurement
+                fluxMask->data.U8[j][i] = 1;
+                flux->data.F32[j][i] = NAN;
+            }
+        }
+    }
+
+    // Not really sure that we need to iterate, but here we go anyway...
+
+    float diff = INFINITY;              // Difference from previous iteration
+    psVector *oldExpFluxes = NULL;      // The fluxes in the previous iteration
+    psVector *oldChipGains = NULL;      // Chip gains in the previous iteration
+    for (int iter = 0; iter < MAXITER && diff > TOLERANCE; iter++) {
+        // Improve on the exposure fluxes
+        int numFluxes = 0;              // Number of fluxes
+        for (int i = 0; i < numExps; i++) {
+            if (expMask->data.U8[i]) {
+                psTrace("psModules.detrend", 7, "Flux for exposure %d is masked.\n", i);
+                continue;
+            }
+            numFluxes++;
+            float sum = 0.0;            // Sum of F_ij - G_j
+            int number = 0;             // Number of chips contributing
+            for (int j = 0; j < numChips; j++) {
+                if (!gainMask->data.U8[j] && !fluxMask->data.U8[i][j]) {
+                    sum += flux->data.F32[i][j] - chipGains->data.F32[j];
+                    number++;
+                }
+            }
+            if (number > 0) {
+                expFluxes->data.F32[i] = sum / (float)number;
+            } else {
+                expMask->data.U8[i] = 1;
+                expFluxes->data.F32[i] = NAN;
+            }
+            psTrace("psModules.detrend", 7, "Flux for exposure %d is %lf\n", i, expf(expFluxes->data.F32[i]));
+        }
+
+        // Improve on the gains
+        float meanGain = 0.0;           // Mean gain
+        int numGains = 0;               // Number of gains
+        for (int i = 0; i < numChips; i++) {
+            if (gainMask->data.U8[i]) {
+                continue;
+            }
+            float sum = 0.0;           // Sum of F_ji - S_j
+            int number = 0;             // Numer of sources contributing
+            for (int j = 0; j < numExps; j++) {
+                if (!fluxMask->data.U8[j][i]) {
+                    sum += flux->data.F32[j][i] - expFluxes->data.F32[j];
+                    number++;
+                }
+            }
+            if (number > 0) {
+                chipGains->data.F32[i] = sum / (float)number;
+            } else {
+                gainMask->data.U8[i] = 1;
+                chipGains->data.F32[i] = NAN;
+            }
+            psTrace("psModules.detrend", 7, "Gain for chip %d is %lf\n", i, expf(-chipGains->data.F32[i]));
+            meanGain += expf(chipGains->data.F32[i]);
+            numGains++;
+        }
+
+        // Normalise the mean gain to unity, and measure the difference
+        meanGain /= (float)numGains;
+        meanGain = logf(meanGain);
+        if (iter > 0) {
+            diff = 0.0;
+            for (int i = 0; i < numChips; i++) {
+                if (gainMask->data.U8[i]) {
+                    continue;
+                }
+                chipGains->data.F32[i] -= meanGain;
+                diff += abs((chipGains->data.F32[i] - oldChipGains->data.F32[i]) / chipGains->data.F32[i]);
+            }
+            for (int i = 0; i < numExps; i++) {
+                if (expMask->data.U8[i]) {
+                    continue;
+                }
+                diff += abs((expFluxes->data.F32[i] - oldExpFluxes->data.F32[i]) / expFluxes->data.F32[i]);
+            }
+        }
+
+        psTrace("psModules.detrend", 2, "Iteration %d: difference is %e\n", iter, diff);
+
+        // Copy the new to the old
+        oldChipGains = psVectorCopy(oldChipGains, chipGains, PS_TYPE_F32);
+        oldExpFluxes = psVectorCopy(oldExpFluxes, expFluxes, PS_TYPE_F32);
+    }
+    psFree(flux);
+    psFree(fluxMask);
+    psFree(oldChipGains);
+    psFree(oldExpFluxes);
+
+    // Un-log the vectors
+    for (int i = 0; i < numChips; i++) {
+        if (!gainMask->data.U8[i]) {
+            chipGains->data.F32[i] = expf(chipGains->data.F32[i]);
+        }
+    }
+    for (int i = 0; i < numExps; i++) {
+        if (!expMask->data.U8[i]) {
+            expFluxes->data.F32[i] = expf(expFluxes->data.F32[i]);
+        }
+    }
+    psFree(gainMask);
+    psFree(expMask);
+
+    psFree(chipGains);
+    psFree(expFluxes);
+
+    return (diff < TOLERANCE); // Did we converge?
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatNormalize.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatNormalize.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFlatNormalize.h	(revision 22322)
@@ -0,0 +1,31 @@
+/* @file pmFlatNormalize.h
+ * @brief Normalize flat-field measurements
+ *
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-30 21:12:56 $
+ * Copyright 2004-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FLAT_NORMALIZE_H
+#define PM_FLAT_NORMALIZE_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+/// Normalize flat-field measurements
+///
+/// We have f_ij = g_i s_j where f_ij is the flux recorded for chip i and integration j, g_i is the gain for
+/// the i-th chip, s_j is the flux of the source in the j-th integration.  An initial guess for the chip gains
+/// might be helpful, but is not necessary.  The matrix of background measurements contains the background for
+/// the flat fields used in the combination, as a function of exposure (rows) and chip (columns).  The
+/// exposure fluxes and chip gains are modified upon return with the solved values.  Returns true if the
+/// solution converged.
+bool pmFlatNormalize(psVector **expFluxesPtr, ///< Flux in each exposure, or NULL; modified
+                     psVector **chipGainsPtr, ///< Initial guess of the chip gains or NULL; modified
+                     const psImage *bgMatrix ///< Background measurements: rows are exposures, cols are chips
+                    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFringeStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFringeStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFringeStats.c	(revision 22322)
@@ -0,0 +1,1071 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFringeStats.h"
+
+// Future optimisations for speed:
+//
+// 1. Clipping --- don't re-do the matrix setup again, but carry matrix and vector around, subtract
+// contributions from clipped data points.
+// 2. Faster psImageStats (use memcpy?)
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// pmFringeRegions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void fringeRegionsFree(pmFringeRegions *fringe)
+{
+    psFree(fringe->x);
+    psFree(fringe->y);
+    psFree(fringe->mask);
+    return;
+}
+
+pmFringeRegions *pmFringeRegionsAlloc(int nPts, int dX, int dY, int nX, int nY)
+{
+    pmFringeRegions *fringe = psAlloc(sizeof(pmFringeRegions));
+    (void)psMemSetDeallocator(fringe, (psFreeFunc)fringeRegionsFree);
+
+    fringe->x = NULL;
+    fringe->y = NULL;
+    fringe->mask = NULL;
+
+    fringe->nRequested = nPts;
+    fringe->nAccepted = 0;
+
+    fringe->dX = dX;
+    fringe->dY = dY;
+    fringe->nX = nX;
+    fringe->nY = nY;
+
+    return fringe;
+}
+
+bool pmFringeRegionsCreatePoints(pmFringeRegions *fringe, const psImage *image, psRandom *random)
+{
+    PS_ASSERT_PTR_NON_NULL(fringe, false);
+    PS_ASSERT_PTR_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(image, false);
+
+    double frnd;
+    // create fringe->nRequested
+
+    psRandom *rng;
+    if (random) {
+        rng = psMemIncrRefCounter(random);
+    } else {
+        rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    }
+
+    fringe->x = psVectorRecycle(fringe->x, fringe->nRequested, PS_TYPE_F32);
+    fringe->y = psVectorRecycle(fringe->y, fringe->nRequested, PS_TYPE_F32);
+    fringe->mask = psVectorRecycle(fringe->mask, fringe->nRequested, PS_TYPE_U8);
+    fringe->x->n = fringe->y->n = fringe->mask->n = fringe->nRequested;
+    psVectorInit(fringe->mask, 0);
+
+    int nX = image->numCols;
+    int nY = image->numRows;
+
+    psF32 *xPt = fringe->x->data.F32;
+    psF32 *yPt = fringe->y->data.F32;
+
+    int dX = fringe->dX;
+    int dY = fringe->dY;
+
+    // generate random points located within image bounds
+    for (int i = 0; i < fringe->nRequested; i++) {
+        frnd = psRandomUniform(rng);
+        xPt[i] = (nX - 2*dX)* frnd + dX;
+        frnd = psRandomUniform(rng);
+        yPt[i] = (nY - 2*dY)* frnd + dY;
+    }
+
+    psFree(rng);
+
+    return true;
+}
+
+bool pmFringeRegionsWriteFits(psFits *fits, psMetadata *header,
+                              const pmFringeRegions *regions, const char *extname)
+{
+    // Make sure the input is well-behaved
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(regions, false);
+    psVector *x = regions->x;           // The x positions
+    psVector *y = regions->y;           // The y positions
+    psVector *mask = regions->mask;     // The region mask
+    int numRows = regions->nRequested;  // Number of rows in the table
+    PS_ASSERT_INT_POSITIVE(numRows, false);
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F32, false);
+    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F32, false);
+    PS_ASSERT_VECTOR_SIZE(x, (long)numRows, false);
+    PS_ASSERT_VECTOR_SIZE(y, (long)numRows, false);
+    if (mask) {
+        PS_ASSERT_VECTOR_NON_NULL(mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+        PS_ASSERT_VECTOR_SIZE(mask, (long)numRows, false);
+    }
+
+    // We need to write:
+    // Scalars: dX, dY, nX, nY
+    // Vectors: x, y, mask
+
+    psMetadata *scalars = psMemIncrRefCounter(header); // Metadata to hold the scalars; will be the header
+    if (!scalars) {
+        scalars = psMetadataAlloc();
+    }
+    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGDX", PS_META_REPLACE, "Median box half-width",
+                     regions->dX);
+    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGDY", PS_META_REPLACE, "Median box half-height",
+                     regions->dY);
+    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGNX", PS_META_REPLACE, "Large-scale smoothing in x",
+                     regions->nX);
+    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGNY", PS_META_REPLACE, "Large-scale smoothing in y",
+                     regions->nY);
+
+    psArray *table = psArrayAlloc(numRows); // The table
+    // Translate the vectors into the required format for psFitsWriteTable()
+    for (long i = 0; i < numRows; i++) {
+        psMetadata *row = psMetadataAlloc();
+        psMetadataAddF32(row, PS_LIST_TAIL, "x", PS_META_REPLACE, "Fringe position in x", x->data.F32[i]);
+        psMetadataAddF32(row, PS_LIST_TAIL, "y", PS_META_REPLACE, "Fringe position in y", y->data.F32[i]);
+        psU8 maskValue = 0;
+        if (mask && mask->data.U8[i]) {
+            maskValue = 0xff;
+        }
+        psMetadataAddU8(row, PS_LIST_TAIL, "mask", PS_META_REPLACE, "Mask", maskValue);
+        table->data[i] = row;
+    }
+
+    bool success;                       // Success of operation
+    if (!(success = psFitsWriteTable(fits, scalars, table, extname))) {
+        psError(PS_ERR_IO, false, "Unable to write fringe data to extension %s\n", extname);
+    }
+    psFree(scalars);
+    psFree(table);
+
+    return success;
+}
+
+pmFringeRegions *pmFringeRegionsReadFits(psMetadata *header, const psFits *fits, const char *extname)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, NULL);
+
+    if (extname && strlen(extname) > 0) {
+        if (!psFitsMoveExtName(fits, extname)) {
+            psError(PS_ERR_IO, false, "Unable to move to extension %s\n", extname);
+            return NULL;
+        }
+    } else if (!psFitsMoveExtNum(fits, 0, false)) {
+        psError(PS_ERR_IO, false, "Unable to move to PHU\n");
+        return NULL;
+    }
+
+    psMetadata *headerCopy = psMemIncrRefCounter(header); // Copy of the header, or NULL
+
+    headerCopy = psFitsReadHeader(headerCopy, fits); // The FITS header
+    if (!headerCopy) {
+        psError(PS_ERR_IO, false, "Unable to read header for extension %s\n", extname);
+        psFree(header);
+        return NULL;
+    }
+
+    // Read the scalars from the header
+    #define READ_SCALAR(SCALAR, NAME) \
+    int SCALAR = psMetadataLookupS32(&mdok, headerCopy, NAME); \
+    if (!mdok || SCALAR <= 0) { \
+        psError(PS_ERR_IO, true, "Unable to find " NAME " in header of extension %s.\n", extname); \
+        psFree(headerCopy); \
+        return NULL; \
+    }
+
+    // Need to retrieve the scalars: dX, dY, nX, nY
+    bool mdok = true;                   // Status of MD lookup
+    READ_SCALAR(dX, "PSFRNGDX");
+    READ_SCALAR(dY, "PSFRNGDY");
+    READ_SCALAR(nX, "PSFRNGNX");
+    READ_SCALAR(nY, "PSFRNGNY");
+    psFree(headerCopy);
+
+    // Now the vectors: x, y, mask
+    psArray *table = psFitsReadTable(fits); // The table
+    long numRows = table->n;            // Number of rows
+
+    pmFringeRegions *regions = pmFringeRegionsAlloc(numRows, dX, dY, nX, nY); // The fringe regions
+    psVector *x = psVectorAlloc(numRows, PS_TYPE_F32); // x position
+    psVector *y = psVectorAlloc(numRows, PS_TYPE_F32); // y position
+    psVector *mask = psVectorAlloc(numRows, PS_TYPE_U8); // mask
+    regions->x = x;
+    regions->y = y;
+    regions->mask = mask;
+
+    #define READ_REGIONS_ROW(VECTOR, TYPE, NAME, DESCRIPTION) \
+    VECTOR->data.TYPE[i] = psMetadataLookup##TYPE(&mdok, row, NAME); \
+    if (!mdok) { \
+        psError(PS_ERR_IO, true, "Unable to find " #DESCRIPTION " .\n"); \
+        psFree(table); \
+        psFree(regions); \
+        return NULL; \
+    }
+
+    // Translate the table into vectors
+    for (long i = 0; i < numRows; i++) {
+        psMetadata *row = table->data[i]; // Table row
+        READ_REGIONS_ROW(x, F32, "x", "x position");
+        READ_REGIONS_ROW(y, F32, "y", "y position");
+        READ_REGIONS_ROW(mask, U8, "mask", "mask");
+    }
+    psFree(table);
+
+    return regions;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// pmFringeStats
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void fringeStatsFree(pmFringeStats *stats)
+{
+    psFree(stats->regions);
+    psFree(stats->f);
+    psFree(stats->df);
+}
+
+pmFringeStats *pmFringeStatsAlloc(pmFringeRegions *regions)
+{
+    PS_ASSERT_PTR_NON_NULL(regions, false);
+
+    pmFringeStats *stats = psAlloc(sizeof(pmFringeStats));
+    (void)psMemSetDeallocator(stats, (psFreeFunc)fringeStatsFree);
+
+    int numRegions = regions->nRequested; // Number of regions
+    stats->regions = psMemIncrRefCounter(regions);
+    stats->f = psVectorAlloc(numRegions, PS_TYPE_F32);
+    stats->df = psVectorAlloc(numRegions, PS_TYPE_F32);
+
+    return stats;
+}
+
+pmFringeStats *pmFringeStatsMeasure(pmFringeRegions *fringe, const pmReadout *readout, psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(fringe, NULL);
+    PS_ASSERT_PTR_NON_NULL(readout, NULL);
+    PS_ASSERT_PTR_NON_NULL(readout->image, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(readout->image, NULL);
+
+    if (!fringe->x || !fringe->y) {
+        // create the fringe vectors for this image
+        pmFringeRegionsCreatePoints(fringe, readout->image, NULL);
+    }
+
+    PS_ASSERT_PTR_NON_NULL(fringe->x, false);
+    PS_ASSERT_PTR_NON_NULL(fringe->y, false);
+
+    pmFringeStats *measurements = pmFringeStatsAlloc(fringe);
+
+    psF32 *xPt = fringe->x->data.F32;
+    psF32 *yPt = fringe->y->data.F32;
+    psF32 *fPt = measurements->f->data.F32;
+    psF32 *dfPt = measurements->df->data.F32;
+
+    int dX = fringe->dX;
+    int dY = fringe->dY;
+
+    psImage *image = readout->image;
+    psImage *mask  = readout->mask;
+
+    psStats *median = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN); // Median statistics only
+    psStats *medianSd = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV); // Median and SD
+
+    // Measure the sky in each smoothing box
+    psImage *sky = psImageAlloc(fringe->nX, fringe->nY, PS_TYPE_F32);
+    for (int i = 0; i < fringe->nY; i++) {
+        int y0 = image->row0 + (float)i * (float)image->numRows / (float)fringe->nY;
+        int y1 = image->row0 + (float)(i + 1) * (float)image->numRows / (float)fringe->nY;
+        for (int j = 0; j < fringe->nX; j++) {
+            int x0 = image->col0 + (float)j * (float)image->numCols / (float)fringe->nX;
+            int x1 = image->col0 + (float)(j + 1) * (float)image->numCols / (float)fringe->nX;
+            psRegion region = psRegionSet(x0, x1, y0, y1);
+            psImage *subImage = psImageSubset(image, region); // Subimage of the sky region
+            psImage *subMask = NULL;
+            if (mask) {
+                subMask = psImageSubset(mask, region); // Subimage of the sky region
+            }
+            psImageStats(median, subImage, subMask, maskVal);
+            sky->data.F32[i][j] = median->sampleMedian;
+            psFree(subImage);
+            psFree(subMask);
+        }
+    }
+
+    for (int i = 0; i < fringe->x->n; i++) {
+        psRegion region = psRegionSet(image->col0 + xPt[i] - dX,
+                                      image->col0 + xPt[i] + dX + 1,
+                                      image->row0 + yPt[i] - dY,
+                                      image->row0 + yPt[i] + dY + 1);
+        psImage *subImage = psImageSubset(image, region);
+        psImage *subMask = NULL;
+        if (mask) {
+            subMask = psImageSubset(mask, region);
+        }
+        psImageStats(medianSd, subImage, subMask, maskVal);
+        psFree(subImage);
+        psFree(subMask);
+
+        int xSky = xPt[i] / (float)image->numCols * (float)sky->numCols;
+        int ySky = yPt[i] / (float)image->numRows * (float)sky->numRows;
+
+        fPt[i] = medianSd->sampleMedian - sky->data.F32[ySky][xSky];
+        dfPt[i] = 1.0 / medianSd->sampleStdev;
+
+        psTrace("psModules.detrend", 7, "[%d:%d,%d:%d]: %f %f\n", (int)region.x0, (int)region.x1,
+                (int)region.y0, (int)region.y1, fPt[i], dfPt[i]);
+    }
+    psFree(sky);
+    psFree(median);
+    psFree(medianSd);
+
+    return measurements;
+}
+
+bool pmFringeStatsWriteFits(psFits *fits,
+                            psMetadata *header,
+                            const pmFringeStats *fringe,
+                            const char *extname
+                           )
+{
+    // Make sure the input is well-behaved
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(fringe, false);
+    pmFringeRegions *regions = fringe->regions; // The fringe regions
+    PS_ASSERT_PTR_NON_NULL(regions, false);
+    int numRows = regions->nRequested;  // Number of rows in the table
+    PS_ASSERT_INT_POSITIVE(numRows, false);
+    psVector *f = fringe->f;            // The fringe measurements
+    psVector *df = fringe->df;      // The fringe standard deviatiations
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+    PS_ASSERT_VECTOR_NON_NULL(df, false);
+    PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F32, false);
+    PS_ASSERT_VECTOR_TYPE(df, PS_TYPE_F32, false);
+    PS_ASSERT_VECTOR_SIZE(f, (long)numRows, false);
+    PS_ASSERT_VECTOR_SIZE(df, (long)numRows, false);
+
+    // We need to write:
+    // Vectors: f, df
+    psArray *table = psArrayAlloc(numRows); // The table
+    // Translate the vectors into the required format for psFitsWriteTable()
+    for (long i = 0; i < numRows; i++) {
+        psMetadata *row = psMetadataAlloc();
+        psMetadataAddF32(row, PS_LIST_TAIL, "f", PS_META_REPLACE, "Fringe measurement", f->data.F32[i]);
+        psMetadataAddF32(row, PS_LIST_TAIL, "df", PS_META_REPLACE, "Fringe stdev", df->data.F32[i]);
+        table->data[i] = row;
+    }
+
+    if (!psFitsWriteTable(fits, header, table, extname)) {
+        psError(PS_ERR_IO, false, "Unable to write fringe data to extension %s\n", extname);
+        psFree(table);
+        return false;
+    }
+
+    psFree(table);
+    return true;
+}
+
+pmFringeStats *pmFringeStatsReadFits(psMetadata *header, const psFits *fits, const char *extname,
+                                     pmFringeRegions *regions)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, NULL);
+    PS_ASSERT_PTR_NON_NULL(regions, NULL);
+    PS_ASSERT_INT_POSITIVE(regions->nRequested, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(regions->x, regions->y, NULL);
+    PS_ASSERT_VECTOR_SIZE(regions->x, (long)regions->nRequested, NULL);
+
+    if (extname && strlen(extname) > 0) {
+        if (!psFitsMoveExtName(fits, extname)) {
+            psError(PS_ERR_IO, false, "Unable to move to extension %s\n", extname);
+            return NULL;
+        }
+    } else if (!psFitsMoveExtNum(fits, 0, false)) {
+        psError(PS_ERR_IO, false, "Unable to move to PHU\n");
+        return NULL;
+    }
+
+    psMetadata *headerCopy = psMemIncrRefCounter(header); // Copy of the header, or NULL
+
+    headerCopy = psFitsReadHeader(headerCopy, fits); // The FITS header
+    if (!headerCopy) {
+        psError(PS_ERR_IO, false, "Unable to read header for extension %s\n", extname);
+        psFree(headerCopy);
+        return NULL;
+    }
+    psFree(headerCopy);
+
+    // Now the vectors: f, df
+    psArray *table = psFitsReadTable(fits); // The table
+    long numRows = table->n;            // Number of rows
+
+    pmFringeStats *fringes = pmFringeStatsAlloc(regions); // The fringe measurements
+    psVector *f = fringes->f;           // fringe measurement
+    psVector *df = fringes->df;         // fringe stdev
+
+    #define READ_STATS_ROW(VECTOR, TYPE, NAME, DESCRIPTION) \
+    VECTOR->data.TYPE[i] = psMetadataLookup##TYPE(&mdok, row, NAME); \
+    if (!mdok) { \
+        psError(PS_ERR_IO, true, "Unable to find " #DESCRIPTION " .\n"); \
+        psFree(table); \
+        psFree(fringes); \
+        return NULL; \
+    }
+
+    // Translate the table into vectors
+    bool mdok;                          // Status of MD lookup
+    for (long i = 0; i < numRows; i++) {
+        psMetadata *row = table->data[i]; // Table row
+        READ_STATS_ROW(f, F32, "f", "fringe measurement");
+        READ_STATS_ROW(df, F32, "df", "fringe standard deviation");
+    }
+    psFree(table);
+
+    return fringes;
+}
+
+
+pmFringeStats *pmFringeStatsConcatenate(const psArray *fringes, const psVector *x0, const psVector *y0)
+{
+    PS_ASSERT_PTR_NON_NULL(fringes, NULL);
+    PS_ASSERT_PTR_NON_NULL(fringes->data, NULL);
+    PS_ASSERT_INT_POSITIVE(fringes->n, NULL);
+    if (x0 && y0) {
+        PS_ASSERT_VECTOR_NON_NULL(x0, NULL);
+        PS_ASSERT_VECTOR_NON_NULL(y0, NULL);
+        PS_ASSERT_VECTOR_TYPE(x0, PS_TYPE_S32, NULL);
+        PS_ASSERT_VECTOR_TYPE(y0, PS_TYPE_S32, NULL);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(x0, y0, NULL);
+        PS_ASSERT_VECTOR_SIZE(x0, fringes->n, NULL);
+        PS_ASSERT_VECTOR_SIZE(y0, fringes->n, NULL);
+    }
+
+    // Get the measurement parameters, and check they are consistent
+    int numPoints = 0;                  // Number of fringe points
+    int dX = 0, dY = 0;                 // Half-width and -height of fringe boxes
+    int nX = 0, nY = 0;                 // Smoothing scales
+    for (long i = 0; i < fringes->n; i++) {
+        pmFringeStats *fringe = fringes->data[i]; // The fringe of interest
+        pmFringeRegions *regions = fringe->regions; // The fringe regions
+        if (numPoints == 0) {
+            dX = regions->dX;
+            dY = regions->dY;
+            nX = regions->nX;
+            nY = regions->nY;
+        } else if (regions->dX != dX || regions->dY != dY || regions->nX != nX || regions->nY != nY) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Fringe %ld has different parameters (%d,%d,%d,%d) "
+                    "from the first (%d,%d,%d,%d).\n", i,
+                    regions->dX, regions->dY, regions->nX, regions->nY, dX, dY, nX, nY);
+            return NULL;
+        }
+        int num = regions->nRequested;  // Number of fringe points
+        if (regions->x->n != num || regions->y->n != num || regions->mask->n != num ||
+                fringe->f->n != num || fringe->df->n != num) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Length of region (%ld,%ld,%ld) and fringe vectors "
+                    "do not match (%ld,%ld) with the official value (%d).\n", regions->x->n, regions->y->n,
+                    regions->mask->n, fringe->f->n, fringe->df->n, num);
+            return NULL;
+        }
+        numPoints += regions->nRequested;
+    }
+
+    pmFringeRegions *newRegions = pmFringeRegionsAlloc(numPoints, dX, dY, nX, nY); // The new list of regions
+    newRegions->x = psVectorAlloc(numPoints, PS_TYPE_F32);
+    newRegions->y = psVectorAlloc(numPoints, PS_TYPE_F32);
+    newRegions->mask = psVectorAlloc(numPoints, PS_TYPE_U8);
+    pmFringeStats *newStats = pmFringeStatsAlloc(newRegions); // The new list of statistics
+
+    long offset = 0;                    // Offset from start of the list
+    for (long i = 0; i < fringes->n; i++) {
+        pmFringeStats *fringe = fringes->data[i]; // The fringe of interest
+        pmFringeRegions *regions = fringe->regions; // The fringe regions
+        // Copy the data over
+        memcpy(&newRegions->x->data.F32[offset], regions->x->data.F32, regions->x->n * sizeof(psF32));
+        memcpy(&newRegions->y->data.F32[offset], regions->y->data.F32, regions->y->n * sizeof(psF32));
+        memcpy(&newRegions->mask->data.U8[offset], regions->mask->data.U8, regions->mask->n * sizeof(psU8));
+        memcpy(&newStats->f->data.F32[offset], fringe->f->data.F32, fringe->f->n * sizeof(psF32));
+        memcpy(&newStats->df->data.F32[offset], fringe->df->data.F32, fringe->df->n * sizeof(psF32));
+        if (x0 && y0) {
+            for (long j = offset; j < offset + regions->x->n; j++) {
+                newRegions->x->data.F32[j] += x0->data.S32[i];
+                newRegions->y->data.F32[j] += y0->data.S32[i];
+            }
+        }
+        offset += regions->nRequested;
+    }
+
+    psFree(newRegions);                 // Drop reference
+    return newStats;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// pmFringeIO
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmFringesFormat(pmCell *cell, psMetadata *header, const psArray *fringes)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_ARRAY_NON_NULL(fringes, false);
+
+    // Check the regions are all identical
+    pmFringeRegions *regions = ((pmFringeStats*)fringes->data[0])->regions; // First region
+    for (int i = 1; i < fringes->n; i++) {
+        pmFringeStats *stats = fringes->data[i];
+        if (stats->regions != regions) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Regions for fringe statistics are not identical.\n");
+            return false;
+        }
+    }
+
+    // Ensure the region is legit
+    psVector *x = regions->x;           // The x positions
+    psVector *y = regions->y;           // The y positions
+    psVector *mask = regions->mask;     // The region mask
+    int numRows = regions->nRequested;  // Number of rows in the table
+    PS_ASSERT_INT_POSITIVE(numRows, false);
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F32, false);
+    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F32, false);
+    PS_ASSERT_VECTOR_SIZE(x, (long)numRows, false);
+    PS_ASSERT_VECTOR_SIZE(y, (long)numRows, false);
+    if (mask) {
+        PS_ASSERT_VECTOR_NON_NULL(mask, false);
+        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, false);
+        PS_ASSERT_VECTOR_SIZE(mask, (long)numRows, false);
+    }
+
+    // We need to write:
+    // Scalars: dX, dY, nX, nY
+    // Vectors: x, y, mask, f, df
+
+    psMetadata *scalars = psMemIncrRefCounter(header); // Metadata to hold the scalars; will be the header
+    if (!scalars) {
+        scalars = psMetadataAlloc();
+    }
+    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGDX", PS_META_REPLACE, "Median box half-width",
+                     regions->dX);
+    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGDY", PS_META_REPLACE, "Median box half-height",
+                     regions->dY);
+    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGNX", PS_META_REPLACE, "Large-scale smoothing in x",
+                     regions->nX);
+    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGNY", PS_META_REPLACE, "Large-scale smoothing in y",
+                     regions->nY);
+    psMetadataAdd(cell->analysis, PS_LIST_TAIL, "FRINGE.HEADER", PS_DATA_METADATA,
+                  "Header for fringe data", scalars);
+    psFree(scalars);
+
+
+    psArray *table = psArrayAlloc(numRows); // The table
+    // Translate the vectors into the required format for psFitsWriteTable()
+    for (long i = 0; i < numRows; i++) {
+        psMetadata *row = psMetadataAlloc();
+        psMetadataAddF32(row, PS_LIST_TAIL, "x", PS_META_REPLACE, "Fringe position in x", x->data.F32[i]);
+        psMetadataAddF32(row, PS_LIST_TAIL, "y", PS_META_REPLACE, "Fringe position in y", y->data.F32[i]);
+        psU8 maskValue = 0;             // Mask value
+        if (mask && mask->data.U8[i]) {
+            maskValue = 0xff;
+        }
+
+        psVector *f = psVectorAlloc(fringes->n, PS_TYPE_F32); // Measurements for each fringe component
+        psVector *df = psVectorAlloc(fringes->n, PS_TYPE_F32); // Errors in measurements
+        for (long j = 0; j < fringes->n; j++) {
+            pmFringeStats *stats = fringes->data[j]; // Fringe statistics of interest
+            f->data.F32[j] = stats->f->data.F32[i];
+            df->data.F32[j] = stats->df->data.F32[i];
+            if (!isfinite(f->data.F32[j]) || !isfinite(df->data.F32[j])) {
+                maskValue = 0xff;
+            }
+        }
+        psMetadataAdd(row, PS_LIST_TAIL, "f", PS_DATA_VECTOR | PS_META_REPLACE, "Fringe measurements", f);
+        psMetadataAdd(row, PS_LIST_TAIL, "df", PS_DATA_VECTOR | PS_META_REPLACE, "Fringe errors", df);
+        // Drop references
+        psFree(f);
+        psFree(df);
+
+        psMetadataAddU8(row, PS_LIST_TAIL, "mask", PS_META_REPLACE, "Mask", maskValue);
+        table->data[i] = row;
+    }
+
+    psMetadataAdd(cell->analysis, PS_LIST_TAIL, "FRINGE", PS_DATA_ARRAY, "Fringe data", table);
+
+    return true;
+}
+
+
+psArray *pmFringesParse(pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *header = psMetadataLookupMetadata(&mdok, cell->analysis, "FRINGE.HEADER"); // Header
+    if (!mdok || !header) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find header for fringe data.\n");
+        return NULL;
+    }
+
+    psArray *table = psMetadataLookupPtr(NULL, cell->analysis, "FRINGE"); // FITS table
+    if (!table) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find table for fringe data.\n");
+        return NULL;
+    }
+
+
+    // Read the scalars from the header
+    #define READ_FRINGES_SCALAR(SCALAR, NAME) \
+    int SCALAR = psMetadataLookupS32(&mdok, header, NAME); \
+    if (!mdok || SCALAR <= 0) { \
+        psError(PS_ERR_IO, true, "Unable to find " NAME " in fringe header.\n"); \
+        return NULL; \
+    }
+
+    // Need to retrieve the scalars: dX, dY, nX, nY
+    READ_FRINGES_SCALAR(dX, "PSFRNGDX");
+    READ_FRINGES_SCALAR(dY, "PSFRNGDY");
+    READ_FRINGES_SCALAR(nX, "PSFRNGNX");
+    READ_FRINGES_SCALAR(nY, "PSFRNGNY");
+
+    // Now the vectors: x, y, mask, f, df
+    long numRows = table->n;            // Number of rows
+    pmFringeRegions *regions = pmFringeRegionsAlloc(numRows, dX, dY, nX, nY); // The fringe regions
+    psVector *x = psVectorAlloc(numRows, PS_TYPE_F32); // x position
+    psVector *y = psVectorAlloc(numRows, PS_TYPE_F32); // y position
+    psVector *mask = psVectorAlloc(numRows, PS_TYPE_U8); // mask
+    regions->x = x;
+    regions->y = y;
+    regions->mask = mask;
+    psArray *f = psArrayAlloc(numRows); // Array of fringe measurements
+    psArray *df = psArrayAlloc(numRows);// Array of errors
+    psArray *fringes = NULL; // Array of fringes, to return
+
+    #define READ_FRINGES_VECTOR_ROW(VECTOR, TYPE, NAME, DESCRIPTION) \
+    { \
+        VECTOR->data.TYPE[i] = psMetadataLookup##TYPE(&mdok, row, NAME); \
+        if (!mdok) { \
+            psError(PS_ERR_IO, true, "Unable to find " #DESCRIPTION " for row %ld.\n", i); \
+            goto READ_FRINGES_DONE; \
+        } \
+    }
+
+    // Some values may be either a vector or a value --- need to check
+    #define READ_FRINGES_ARRAY_ROW(ARRAY, TYPE, NAME, DESCRIPTION) \
+    { \
+        psMetadataItem *item = psMetadataLookup(row, NAME); \
+        if (!item) { \
+            psError(PS_ERR_IO, true, "Unable to find " #DESCRIPTION " for row %ld.\n", i); \
+            goto READ_FRINGES_DONE; \
+        } \
+        if (item->type == PS_DATA_VECTOR) { \
+            ARRAY->data[i] = psMemIncrRefCounter(item->data.V); \
+        } else if (item->type == PS_TYPE_##TYPE) { \
+            psVector *vector = psVectorAlloc(1, PS_TYPE_##TYPE); \
+            vector->data.TYPE[0] = item->data.TYPE; \
+            ARRAY->data[i] = vector; \
+        } else { \
+            psError(PS_ERR_IO, true, "Found " #DESCRIPTION " for row %ld, but it's of an " \
+                    "unsupported type (%x).\n", i, item->type); \
+            goto READ_FRINGES_DONE; \
+        } \
+    }
+
+    // Translate the table into vectors
+    for (long i = 0; i < numRows; i++) {
+        psMetadata *row = table->data[i]; // Table row
+        READ_FRINGES_VECTOR_ROW(x, F32, "x", "x position");
+        READ_FRINGES_VECTOR_ROW(y, F32, "y", "y position");
+        READ_FRINGES_VECTOR_ROW(mask, U8, "mask", "mask");
+        READ_FRINGES_ARRAY_ROW(f, F32, "f", "fringe measurement");
+        READ_FRINGES_ARRAY_ROW(df, F32, "df", "fringe error");
+    }
+
+    // Get f,df into pmFringeStats
+    long numFringes = ((psVector*)(f->data[0]))->n; // Number of fringe components
+    fringes = psArrayAlloc(numFringes);
+    for (int j = 0; j < numFringes; j++) {
+        fringes->data[j] = pmFringeStatsAlloc(regions);
+    }
+
+    for (long i = 0; i < numRows; i++) {
+        psVector *measurements = f->data[i]; // Vector of measurements
+        psVector *errors = df->data[i]; // Vector of errors
+        for (int j = 0; j < numFringes; j++) {
+            pmFringeStats *fringe = fringes->data[j];
+            fringe->f->data.F32[i] = measurements->data.F32[j];
+            fringe->df->data.F32[i] = errors->data.F32[j];
+        }
+    }
+
+READ_FRINGES_DONE:
+    psFree(regions);
+    psFree(f);
+    psFree(df);
+
+    return fringes;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// pmFringeScale
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void fringeScaleFree(pmFringeScale *scale)
+{
+    psFree(scale->coeff);
+    psFree(scale->coeffErr);
+    return;
+}
+
+pmFringeScale *pmFringeScaleAlloc(int nFringeFrames)
+{
+    pmFringeScale *scale = psAlloc(sizeof(pmFringeScale));
+    (void)psMemSetDeallocator(scale, (psFreeFunc)fringeScaleFree);
+
+    scale->nFringeFrames = nFringeFrames;
+    scale->coeff = psVectorAlloc(nFringeFrames + 1, PS_TYPE_F32);
+    scale->coeffErr = psVectorAlloc(nFringeFrames + 1, PS_TYPE_F32);
+
+    return scale;
+}
+
+// Determine the fringe scales through solving the least-squares problem
+static bool scaleMeasure(pmFringeScale *scale, // Scale to return
+                         pmFringeStats *science, // The fringe measurements for the science image
+                         psArray *fringes // Array of fringe measurements for the templates
+                        )
+{
+    assert(scale);
+    assert(science);
+    assert(fringes);
+    assert(scale->nFringeFrames == fringes->n);
+
+    psVector *mask = science->regions->mask; // The region mask
+
+    int numCoeffs = fringes->n + 1;     // Number of coefficients: scales for the templates plus a background
+    int numPoints = science->regions->nRequested; // Number of points (i.e., fringe measurements)
+
+    psImage *A = psImageAlloc(numCoeffs, numCoeffs, PS_TYPE_F64); // The least-squares matrix
+    psVector *B = psVectorAlloc(numCoeffs, PS_TYPE_F64); // The least-squares vector
+
+    // Generate the least-squares matrix and vector
+    for (int i = 0; i < numCoeffs; i++) {
+        psVector *fringe1 = NULL;       // A fringe measurement
+        if (i != 0) {
+            pmFringeStats *fringe = fringes->data[i - 1];
+            fringe1 = fringe->f;
+        }
+
+        // Fill in the upper part of the matrix
+        for (int j = i; j < numCoeffs; j++) {
+            psVector *fringe2 = NULL;   // Another fringe measurement
+            if (j != 0) {
+                pmFringeStats *fringe = fringes->data[j - 1];
+                fringe2 = fringe->f;
+            }
+
+            double matrix = 0.0;        // The matrix sum
+            for (int k = 0; k < numPoints; k++) {
+                if (!mask->data.U8[k]) {
+                    psF32 f1 = (fringe1) ? fringe1->data.F32[k] : 1.0; // Contribution from i fringe
+                    psF32 f2 = (fringe2) ? fringe2->data.F32[k] : 1.0; // Contribution from j fringe
+                    psF32 dsInv = science->df->data.F32[k]; // 1 / sigma
+                    matrix += f1 * f2 * dsInv * dsInv;
+                }
+            }
+            A->data.F64[i][j] = matrix;
+        }
+
+        // Use symmetry to fill in the lower part of the matrix
+        for (int j = 0; j < i; j++) {
+            A->data.F64[i][j] = A->data.F64[j][i];
+        }
+
+        double vector = 0.0;            // The vector sum
+        for (int k = 0; k < numPoints; k++) {
+            if (!mask->data.U8[k]) {
+                psF32 f1 = (fringe1) ? fringe1->data.F32[k] : 1.0; // Contribution from fringe 1
+                psF32 s = science->f->data.F32[k]; // Contribution from science measurement
+                psF32 dsInv = science->df->data.F32[k]; // 1 / sigma
+                vector += f1 * s * dsInv * dsInv;
+            }
+        }
+        B->data.F64[i] = vector;
+    }
+
+    if (psTraceGetLevel("psModules.detrend") >= 5) {
+        printf("From %d points:\n", numPoints);
+        for (int i = 0; i < numCoeffs; i++) {
+            for (int j = 0; j < numCoeffs; j++) {
+                printf("%.2e ", A->data.F64[i][j]);
+            }
+            printf("\n");
+        }
+    }
+
+    // Solve the least-squares equation
+    if (! psMatrixGJSolve(A, B)) {
+        psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.  Returning NULL.\n");
+        return false;
+    }
+
+    // Copy the results over
+    for (int i = 0; i < numCoeffs; i++) {
+        scale->coeff->data.F32[i] = B->data.F64[i];
+        scale->coeffErr->data.F32[i] = sqrt(A->data.F64[i][i]);
+    }
+
+    psFree(A);
+    psFree(B);
+
+    return true;
+}
+
+// Measure the fringe differences for each region
+static bool fringeScaleDiffs(psVector *diff, // Vector of differences
+                             pmFringeStats *science, // Science fringe measurements
+                             psArray *fringes, // Template fringe measurements
+                             pmFringeScale *scale // Fringe scales
+                            )
+{
+    assert(diff);
+    assert(diff->type.type == PS_TYPE_F32);
+    assert(science);
+    assert(fringes);
+    assert(scale);
+    assert(diff->n == science->regions->nRequested);
+    assert(fringes->n == scale->nFringeFrames);
+
+    psVector *mask = science->regions->mask; // The region mask
+
+    for (int i = 0; i < diff->n; i++) {
+        if (!mask->data.U8[i]) {
+            float difference = science->f->data.F32[i] - scale->coeff->data.F32[0];
+            for (int j = 0; j < fringes->n; j++) {
+                pmFringeStats *fringe = fringes->data[j]; // The fringe of interest
+                difference -= scale->coeff->data.F32[j + 1] * fringe->f->data.F32[i];
+            }
+            diff->data.F32[i] = difference * difference * science->df->data.F32[i] * science->df->data.F32[i];
+        }
+    }
+
+    return true;
+}
+
+// Clip regions based on the differences; return the number masked
+static int clipRegions(psVector *diffs, // Differences
+                       psVector *mask,  // Region mask
+                       float rej        // Rejection limit in standard deviations
+                      )
+{
+    assert(diffs);
+    assert(diffs->type.type == PS_TYPE_F32);
+    assert(mask);
+    assert(mask->type.type == PS_TYPE_U8);
+    assert(diffs->n == mask->n);
+
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_QUARTILE); // Statistics
+    psVectorStats(stats, diffs, NULL, mask, 1);
+    float middle = stats->sampleMedian; // The middle of the distribution
+    float thresh = rej * 0.74 * (stats->sampleUQ - stats->sampleLQ); // The rejection threshold
+    psFree(stats);
+
+    int numClipped = 0;                 // Number clipped
+    for (int i = 0; i < diffs->n; i++) {
+        psTrace("psModules.detrend", 10, "Region %d (%d): %f\n", i, mask->data.U8[i], diffs->data.F32[i]);
+        if (!mask->data.U8[i] && fabs(diffs->data.F32[i]) > middle + thresh) {
+            psTrace("psModules.detrend", 5, "Masking %d: %f\n", i, diffs->data.F32[i]);
+            mask->data.U8[i] = 1;
+            numClipped++;
+        }
+    }
+
+    return numClipped;
+}
+
+
+// XXX include the fringe error (fringe->df) in the fit?
+pmFringeScale *pmFringeScaleMeasure(pmFringeStats *science, psArray *fringes, float rej,
+                                    unsigned int nIter, float keepFrac)
+{
+    PS_ASSERT_PTR_NON_NULL(science, NULL);
+    PS_ASSERT_PTR_NON_NULL(fringes, NULL);
+    PS_ASSERT_INT_POSITIVE(fringes->n, NULL);
+    PS_ASSERT_INT_POSITIVE(nIter, NULL);
+
+    pmFringeRegions *regions = science->regions; // The fringe regions
+    int numRegions = regions->nRequested; // Number of regions
+
+    // Ensure we are dealing with the SAME fringe points for all the inputs.
+    // Otherwise, we're going to get crazy results.
+    for (long i = 0; i < numRegions; i++) {
+        float xScience = regions->x->data.F32[i]; // The x position for the science image
+        float yScience = regions->y->data.F32[i]; // The y position for the science image
+        for (long j = 0; j < fringes->n; j++) {
+            pmFringeStats *fringe = fringes->data[j]; // The fringe statistics from a fringe image
+            pmFringeRegions *fringeRegions = fringe->regions; // The fringe regions for that fringe image
+            if (fringeRegions->x->data.F32[i] != xScience ||
+                    fringeRegions->y->data.F32[i] != yScience) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Science and fringe measurement regions "
+                        "don't match.\n");
+                return NULL;
+            }
+        }
+    }
+
+    // Set up the mask
+    if (!regions->mask) {
+        regions->mask = psVectorAlloc(numRegions, PS_TYPE_U8);
+        psVectorInit(regions->mask, 0);
+    }
+    psVector *mask = regions->mask;     // The region mask
+    psStats *median = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN); // Median statistics
+    unsigned int numClipped = 0;        // Total number clipped
+    psVector *diff = psVectorAlloc(numRegions, PS_TYPE_F32); // The differences between obs. and pred.
+
+    pmFringeScale *scale = pmFringeScaleAlloc(fringes->n); // The fringe scales
+
+    // Get rid of bad data points
+    for (int i = 0; i < fringes->n; i++) {
+        pmFringeStats *fringe = fringes->data[i]; // The fringe of interest
+        for (int j = 0; j < numRegions; j++) {
+            if (!isfinite(fringe->f->data.F32[j])) {
+                mask->data.U8[j] = 1;
+                psTrace("psModules.detrend", 9, "Masking region %d because not finite in fringe %d.\n", j, i);
+            }
+        }
+    }
+
+# if (0)
+    // Write fringe data to file for a test
+    FILE *f = fopen ("fringe.dat", "w");
+    for (int j = 0; j < numRegions; j++) {
+	if (mask->data.U8[j]) continue;
+	fprintf (f, "%d %f %f ", j, science->f->data.F32[j], science->df->data.F32[j]);
+	for (int i = 0; i < fringes->n; i++) {
+	    pmFringeStats *fringe = fringes->data[i]; // The fringe of interest
+            fprintf (f, "%f  ", fringe->f->data.F32[j]);
+        }
+	fprintf (f, "\n");
+    }
+    fclose (f);
+# endif
+
+    // Get rid of the extreme outliers by assuming most of the points are somewhat clustered
+    psVectorStats(median, science->f, NULL, NULL, 0);
+    scale->coeff->data.F32[0] = median->sampleMedian;
+    for (int i = 0; i < fringes->n; i++) {
+        pmFringeStats *fringe = fringes->data[i]; // The fringe of interest
+        psVectorStats(median, fringe->f, NULL, NULL, 0);
+        scale->coeff->data.F32[0] -= median->sampleMedian;
+        scale->coeff->data.F32[i] = 0.0;
+    }
+    psFree(median);
+    fringeScaleDiffs(diff, science, fringes, scale);
+    numClipped = clipRegions(diff, mask, 3.0*rej);
+    psTrace("psModules.detrend", 4, "%d regions clipped in initial pass.\n", numClipped);
+
+    unsigned int iter = 0;              // Iteration number
+    unsigned int iterClip = 0;          // Number clipped in this iteration
+    do {
+        iter++;
+        scaleMeasure(scale, science, fringes); // The scales
+        psTrace("psModules.detrend", 1, "Fringe scales after iteration %d:\n", iter);
+        psTrace("psModules.detrend", 1, "Background: %f %f\n", scale->coeff->data.F32[0],
+                scale->coeffErr->data.F32[0]);
+        for (int i = 0; i < scale->nFringeFrames; i++) {
+            psTrace("psModules.detrend", 1, "%d: %f %f\n", i, scale->coeff->data.F32[i + 1],
+                    scale->coeffErr->data.F32[i + 1]);
+        }
+
+        fringeScaleDiffs(diff, science, fringes, scale);
+        iterClip = clipRegions(diff, mask, rej); // Number clipped
+        numClipped += iterClip;
+        psTrace("psModules.detrend", 9, "Clipped: %d\tFrac: %f\n", iterClip,
+                (float)numClipped/(float)numRegions);
+    } while (iterClip > 0 && iter < nIter && (float)numClipped/(float)numRegions <= 1.0 - keepFrac);
+    psFree(diff);
+
+    // A final iteration with the last clipping
+    scaleMeasure(scale, science, fringes);
+
+    return scale;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Fringe correction
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// XXX note that this modifies the input fringe images
+psImage *pmFringeCorrect(pmReadout *readout, pmFringeRegions *fringes, psArray *fringeImages,
+                         psArray *fringeStats, psMaskType maskVal, float rej,
+                         unsigned int nIter, float keepFrac)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, NULL);
+    PS_ASSERT_PTR_NON_NULL(readout->image, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(readout->image, NULL);
+    PS_ASSERT_PTR_NON_NULL(fringes, NULL);
+    PS_ASSERT_PTR_NON_NULL(fringeImages, NULL);
+    PS_ASSERT_PTR_NON_NULL(fringeStats, NULL);
+    PS_ASSERT_INT_EQUAL(fringeImages->n, fringeStats->n, NULL);
+    PS_ASSERT_INT_POSITIVE(nIter, NULL);
+
+    // measure the fringe stats for the science frame and solve for the scales
+    pmFringeStats *scienceStats = pmFringeStatsMeasure(fringes, readout, maskVal);
+
+    if (psTraceGetLevel("psModules.detrend") > 9) {
+        for (int i = 0; i < fringes->nRequested; i++) {
+            printf("%f", scienceStats->f->data.F32[i]);
+            for (int j = 0; j < fringeStats->n; j++) {
+                pmFringeStats *fringe = fringeStats->data[j];
+                printf("\t%f", fringe->f->data.F32[i]);
+            }
+            printf("\n");
+        }
+    }
+
+    pmFringeScale *scale = pmFringeScaleMeasure(scienceStats, fringeStats, rej, nIter, keepFrac);
+    psFree(scienceStats);
+
+    psTrace("psModules.detrend", 7, "Fringe solution:\n");
+    for (int i = 0; i < fringeImages->n + 1; i++) {
+        psTrace("psModules.detrend", 7, "%d: %f %f\n", i, scale->coeff->data.F32[i],
+                scale->coeffErr->data.F32[i]);
+    }
+
+    // build the fringe correction image
+    // XXX we could save data space by making the first image the output image
+    psImage *sumFringe = psImageAlloc(readout->image->numCols, readout->image->numRows, PS_TYPE_F32);
+    //psBinaryOp(sumFringe, sumFringe, "+", psScalarAlloc(scale->coeff->data.F32[0], PS_TYPE_F32));
+    for (int i = 0; i < fringeImages->n; i++) {
+
+        // rescale the fringe image
+        psBinaryOp(fringeImages->data[i], fringeImages->data[i], "*",
+                   psScalarAlloc(scale->coeff->data.F32[i+1], PS_TYPE_F32));
+
+        // sum together
+        sumFringe = (psImage*)psBinaryOp(sumFringe, sumFringe, "+", fringeImages->data[i]);
+    }
+    psFree(scale);
+
+    // subtract the resulting fringe frame
+    readout->image = (psImage*)psBinaryOp(readout->image, readout->image, "-", sumFringe);
+
+    return sumFringe;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFringeStats.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFringeStats.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmFringeStats.h	(revision 22322)
@@ -0,0 +1,214 @@
+/* @file pmFringeStats.h
+ * @brief Measure fringe statistics, and apply correction
+ *
+ * @author Eugene Magnier, IfA
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-24 02:54:15 $
+ * Copyright 2004-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FRINGE_STATS
+#define PM_FRINGE_STATS
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// pmFringeRegions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Fringe measurement regions.
+///
+/// Fringes are measured within a box of size dX,dY.  A large scale smoothing is performed by subtracting the
+/// background within large divisions of the image.  The coordinates of the fringe points and the mask may be
+/// NULL, which means that they will be generated when required.
+typedef struct
+{
+    int nRequested;                     // Number of fringe points selected
+    int nAccepted;                      // Number of fringe points not masked
+    int dX;                             // Median box half-width
+    int dY;                             // Median box half-height
+    int nX;                             // Number of large-scale smoothing divisions in x (col)
+    int nY;                             // Number of large-scale smoothing divisions in y (row)
+    psVector *x;                        // Fringe point coordinates (col), or NULL
+    psVector *y;                        // Fringe point coordinates (row), or NULL
+    psVector *mask;                     // Fringe point on/off mask, or NULL
+}
+pmFringeRegions;
+
+/// Allocate fringe regions
+pmFringeRegions *pmFringeRegionsAlloc (int nPts, ///< Number of fringe points to create
+                                       int dX, ///< Half-width of fringe boxes
+                                       int dY, ///< Half-height of fringe boxes
+                                       int nX, ///< Smoothing scale in x
+                                       int nY ///< Smoothing scale in y
+                                      );
+
+/// Generate the fringe points
+///
+/// Fringe points are generated randomly over the image.  No effort is made to avoid masked regions (indeed,
+/// the function knows nothing about masks).  If the random number generator is NULL, then a new one will be
+/// used.
+bool pmFringeRegionsCreatePoints(pmFringeRegions *fringe, ///< Fringe regions to generate
+                                 const psImage *image, ///< Image for the regions (defines the size)
+                                 psRandom *random ///< Random number generator, or NULL
+                                );
+
+/// Write the regions to a FITS file
+///
+/// The fringe regions are written to the FITS file, with the given extension name.  The header is
+/// supplemented with scalar values dX, dY, nX and nY (as PSFRNGDX, PSFRNGDY, PSFRNGNX, PSFRNGNY) from the
+/// fringe regions, while the fringe coordinates and mask are written as a FITS table (as x, y, mask).
+bool pmFringeRegionsWriteFits(psFits *fits, ///< Output FITS file
+                              psMetadata *header, ///< Additional headers to write, or NULL
+                              const pmFringeRegions *regions, ///< Regions to write
+                              const char *extname ///< Extension name, or NULL
+                             );
+
+/// Read the regions from a FITS file
+///
+/// The fringe regions are read from the FITS file, at the given extension name.  The scalars are retrieved
+/// from the header, while the table provides the fringe coordinates and mask.
+pmFringeRegions *pmFringeRegionsReadFits(psMetadata *header, ///< Header to read, or NULL
+        const psFits *fits, ///< Input FITS file
+        const char *extname ///< Extension name, or NULL
+                                        );
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// pmFringeStats
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Fringe measurements for a particular image
+///
+/// Measurements of the median and stdev are made at each of the fringe regions.
+typedef struct
+{
+    pmFringeRegions *regions;           ///< Fringe regions
+    psVector *f;                        ///< Fringe point median
+    psVector *df;                       ///< Fringe point stdev
+}
+pmFringeStats;
+
+/// Allocate fringe statistics
+pmFringeStats *pmFringeStatsAlloc(pmFringeRegions *regions // The fringe regions which will be measured
+                                 );
+
+/// Measure the fringe statistics for an image
+///
+/// Given an input image and fringe regions at which to measure, measures the median and stdev at each of the
+/// fringe points.  If the fringe points are undefined, they are generated.
+pmFringeStats *pmFringeStatsMeasure(pmFringeRegions *fringe, ///< Fringe regions at which to measure
+                                    const pmReadout *readout, ///< Readout for which to measure
+                                    psMaskType maskVal ///< Mask value for image
+                                   );
+
+/// Write the fringe stats for an image to a FITS table
+///
+/// The fringe measurements are written to the FITS file with the given extension name.  The median and stdev
+/// measurements are written as a FITS table (as f and df).
+bool pmFringeStatsWriteFits(psFits *fits, ///< FITS file to which to write
+                            psMetadata *header, ///< Additional headers to write, or NULL
+                            const pmFringeStats *fringe, ///< Fringe statistics to be written
+                            const char *extname ///< Extension name for table
+                           );
+
+/// Read the fringe stats for an image from a FITS table
+///
+/// The fringe measurements are read from the FITS file, at the given extension name.  The table provides the
+/// median and stdev measurements.  It is assumed that the fringe measurements correspond to the regions
+/// provided.
+pmFringeStats *pmFringeStatsReadFits(psMetadata *header, ///< Header to read, or NULL
+                                     const psFits *fits, ///< FITS file from which to read
+                                     const char *extname, ///< Extension name to read
+                                     pmFringeRegions *regions ///< Corresponding regions
+                                    );
+
+/// Concatenate the fringe stats for several readouts into a single fringe stats.
+///
+/// Each readout of each chip must be measured separately (so as to avoid any gaps between the cells, as in
+/// the case for GPC).  But the fit must be performed with all the readouts belonging to a chip (in order to
+/// get a secure measurement of the fringe amplitudes).  To do so, we need to concatenate the fringe
+/// measurements for each of the chip components.  This function generates a new pmFringeStats from
+/// concatenating those in the array.  The corresponding pmFringeRegions is also generated.
+pmFringeStats *pmFringeStatsConcatenate(const psArray *fringes, ///< Array of pmFringeStats for the readouts
+                                        const psVector *x0, ///< Offset in x for the readout
+                                        const psVector *y0 ///< Offset in y for the readout
+                                       );
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Input/output for multiple pmFringeStats
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Write an array of fringes measurements to a FITS table.
+///
+/// Writes an array of fringe measurements for a cell as a FITS table in the analysis metadata.  The array of
+/// fringe statistics must all use the same fringe regions (or there is no point in storing them all
+/// together).  The header is supplemented with scalar values dX, dY, nX and nY (as PSFRNGDX, PSFRNGDY,
+/// PSFRNGNX, PSFRNGNY) from the fringe regions, while the fringe coordinates and mask are written as a FITS
+/// table (as x, y, mask, f, df; f and df are vectors).
+bool pmFringesFormat(pmCell *cell,   ///< Cell for which to write
+                     psMetadata *header, ///< Header, or NULL
+                     const psArray *fringes ///< Array of pmFringeStats, all for the same pmFringeRegion
+                    );
+
+/// Parses an array of fringes measurements from a FITS table.
+///
+/// The fringes for the cell are read from the FITS table in the analysis metadata.  The table provides the
+/// region and the (possibly multiple) fringe statistics for that region.  The current extension is used if
+/// the extension name is not provided.
+psArray *pmFringesParse(pmCell *cell ///< Cell for which to read fringes
+                       );
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// pmFringeScale
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// The fringe correction solution
+typedef struct
+{
+    int nFringeFrames;                  ///< Number of fringe frames
+    psVector *coeff;                    ///< Fringe coefficients; size = nFringeFrames
+    psVector *coeffErr;                 ///< Error in fringe coefficients; size = nFringeFrames
+}
+pmFringeScale;
+
+/// Measure the scales for the fringe correction
+///
+/// Given a fringe measurement for a science image, and an array of template fringe measurements, this
+/// function measures the contribution of each of the templates to the input.  Rejection is performed on the
+/// fringe regions, to weed out stars etc.
+pmFringeScale *pmFringeScaleMeasure(pmFringeStats *science, ///< Fringe measurements from science image
+                                    psArray *fringes, ///< Array of fringe measurements from templates
+                                    float rej, ///< Rejection threshold (in standard deviations)
+                                    unsigned int nIter, ///< Maximum number of iterations
+                                    float keepFrac ///< Minimum fraction of regions to keep
+                                   );
+
+/// Allocate fringe scales
+pmFringeScale *pmFringeScaleAlloc(int nFringeFrames ///< Number of fringe frames
+                                 );
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Fringe correction
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Solve for and apply the fringe correction
+///
+/// This is a wrapper around each of the fringe correction components to measure the fringe points, solve for
+/// the fringe correction, and apply the fringe correction.  The input fringe images are modified (scaled by
+/// the solution coefficients in order to correct the science image).  Returns the summed fringe image.
+psImage *pmFringeCorrect(pmReadout *in, ///< Input science image
+                         pmFringeRegions *fringes, ///< The fringe regions used
+                         psArray *fringeImages, ///< Fringe template images to use in correction
+                         psArray *fringeStats, ///< Fringe stats (for templates) to use in correction
+                         psMaskType maskVal, ///< Value to mask for science image
+                         float rej,     ///< Rejection threshold, for pmFringeScaleMeasure
+                         unsigned int nIter, ///< Maximum number of iterations, for pmFringeScaleMeasure
+                         float keepFrac ///< Minimum fraction of regions to keep, for pmFringeScaleMeasure
+                        );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmMaskBadPixels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmMaskBadPixels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmMaskBadPixels.c	(revision 22322)
@@ -0,0 +1,271 @@
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <strings.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "pmFPAMaskWeight.h"
+#include "pmMaskBadPixels.h"
+
+bool pmMaskBadPixels(pmReadout *input, const pmReadout *mask, psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(input, false);
+    PS_ASSERT_PTR_NON_NULL(input->mask, false);
+    PS_ASSERT_IMAGE_TYPE(input->mask, PS_TYPE_MASK, false);
+
+    PS_ASSERT_PTR_NON_NULL(mask, false);
+    PS_ASSERT_PTR_NON_NULL(mask->mask, false);
+    PS_ASSERT_IMAGE_TYPE(mask->mask, PS_TYPE_MASK, false);
+
+    psImage *inMask = input->mask;
+    psImage *exMask = mask->mask;
+
+    // Add mask MD5 to header
+    pmHDU *hdu = pmHDUFromReadout(input);  // HDU of interest
+    psVector *md5 = psImageMD5(mask->mask); // md5 hash
+    psString md5string = psMD5toString(md5); // String
+    psFree(md5);
+    psStringPrepend(&md5string, "MASK image MD5: ");
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                     md5string, "");
+    psFree(md5string);
+
+    int rowMax = input->row0 + inMask->numRows;
+    int colMax = input->col0 + inMask->numCols;
+
+    if (mask->row0 > input->row0 || mask->col0 > input->col0 ||
+            mask->row0 + exMask->numRows < rowMax || mask->col0 + exMask->numCols < colMax) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Input image size exceeds that of mask image: (%d, %d) vs (%d, %d)",
+                inMask->numRows, inMask->numCols, exMask->numRows, exMask->numCols);
+        return false;
+    }
+
+    // Determine total offset based on image offset with chip offset
+    // XXX if we choose to correct for the readout location, apply input->col0,row0 here
+    int offCol = input->col0 - mask->col0;
+    int offRow = input->row0 - mask->row0;
+
+    // masks are both of type PS_TYPE_MASK
+    psMaskType **exVal = exMask->data.U8;
+    psMaskType **inVal = inMask->data.U8;
+
+    // apply exMask values
+    if (maskVal) {
+        // set raised pixels in exMask which are selected by maskVal
+        for (int j = 0; j < inMask->numRows; j++) {
+            int xJ = j - offRow;
+            for (int i = 0; i < inMask->numCols; i++) {
+                int xI = i - offCol;
+                inVal[j][i] |= (maskVal & exVal[xJ][xI]);
+            }
+        }
+    }
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now, used for reporting
+    psString timeString = psTimeToISO(time); // String with time
+    psFree(time);
+    psStringPrepend(&timeString, "Static mask (selecting %x) applied at ", maskVal);
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                     timeString, "");
+    psFree(timeString);
+
+    return true;
+}
+
+
+bool pmMaskFlagSuspectPixels(pmReadout *output, const pmReadout *readout, float median, float stdev,
+                             float rej, psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
+    PS_ASSERT_IMAGE_NON_NULL(readout->image, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(readout->image, false);
+    PS_ASSERT_IMAGE_TYPE(readout->image, PS_TYPE_F32, false);
+    if (readout->mask) {
+        PS_ASSERT_IMAGE_NON_EMPTY(readout->mask, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(readout->image, readout->mask, false);
+        PS_ASSERT_IMAGE_TYPE(readout->mask, PS_TYPE_MASK, false);
+    }
+    PS_ASSERT_PTR_NON_NULL(output, false);
+
+    bool mdok;                          // Status of MD lookup
+    psImage *suspect = psMetadataLookupPtr(&mdok, output->analysis, PM_MASK_ANALYSIS_SUSPECT); // Suspect img
+    if (suspect) {
+        PS_ASSERT_IMAGE_NON_EMPTY(suspect, false);
+        PS_ASSERT_IMAGE_TYPE(suspect, PS_TYPE_F32, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(readout->image, suspect, false);
+        psMemIncrRefCounter(suspect);
+    } else {
+        suspect = psImageAlloc(readout->image->numCols, readout->image->numRows, PS_TYPE_F32);
+        psImageInit(suspect, 0);
+        psMetadataAddImage(output->analysis, PS_LIST_TAIL, PM_MASK_ANALYSIS_SUSPECT, PS_META_REPLACE,
+                           "Suspect pixels", suspect);
+        psMetadataAddS32(output->analysis, PS_LIST_TAIL, PM_MASK_ANALYSIS_NUM, PS_META_REPLACE,
+                         "Number of input images", 0);
+    }
+
+    if (!isfinite(median) || !isfinite(stdev)) {
+        // If we get down here and the statistics are missing, then we should go and mask the entire image
+        psWarning("Missing statistics --- flagging entire image as suspect.");
+        return (psImage*)psBinaryOp(suspect, suspect, "+", psScalarAlloc(1.0, PS_TYPE_F32));
+    }
+
+    psImage *image = readout->image;    // Image of interest
+    psImage *mask = readout->mask;      // Corresponding mask
+
+    psTrace ("psModules.detrend", 3, "suspect: %f +/- %f\n", median, stdev);
+
+    // XXX this loop could result in pixels with suspect = 0.0 but no valid input pixels (all
+    // masked).  need to track the number of good as well as suspect pixels?
+    for (int y = 0; y < image->numRows; y++) {
+        for (int x = 0; x < image->numCols; x++) {
+            if (fabs((image->data.F32[y][x] - median) / stdev) < rej) continue;
+	    if (mask && (mask->data.PS_TYPE_MASK_DATA[y][x] & maskVal)) continue;
+	    suspect->data.F32[y][x] += 1.0;
+        }
+    }
+    psFree(suspect);                    // Drop reference
+
+    psMetadataItem *numItem = psMetadataLookup(output->analysis, PM_MASK_ANALYSIS_NUM); // Item with number
+    assert(numItem);
+    numItem->data.S32++;
+
+    return true;
+}
+
+// the maskVal supplied here is the value SET for this mask (ie, it is not used to avoid pixels)
+bool pmMaskIdentifyBadPixels(pmReadout *output, psMaskType maskVal, float thresh, pmMaskIdentifyMode mode)
+{
+    PS_ASSERT_PTR_NON_NULL(output, false);
+    psImage *suspects = psMetadataLookupPtr(NULL, output->analysis, PM_MASK_ANALYSIS_SUSPECT); // Suspect img
+    if (!suspects) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find image with suspected bad pixels.");
+        return false;
+    }
+    PS_ASSERT_IMAGE_NON_EMPTY(suspects, false);
+    PS_ASSERT_IMAGE_TYPE(suspects, PS_TYPE_F32, false);
+    if (output->mask) {
+        PS_ASSERT_IMAGE_NON_EMPTY(output->mask, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(output->mask, suspects, false);
+        PS_ASSERT_IMAGE_TYPE(output->mask, PS_TYPE_MASK, false);
+    } else {
+        output->mask = psImageAlloc(suspects->numCols, suspects->numRows, PS_TYPE_MASK);
+    }
+    int num = psMetadataLookupS32(NULL, output->analysis, PM_MASK_ANALYSIS_NUM); // Number of inputs
+    PS_ASSERT_INT_POSITIVE(num, false);
+
+    float limit = NAN;                  // Limit for masking
+    switch (mode) {
+      case PM_MASK_ID_VALUE:
+        limit = thresh;
+        break;
+
+      case PM_MASK_ID_FRACTION:
+        limit = thresh * num;
+        break;
+
+      case PM_MASK_ID_SIGMA: {
+        psStats *stats = psStatsAlloc(PS_STAT_CLIPPED_STDEV); // Statistics
+        stats->clipSigma = 5.0;
+        stats->clipIter = 1;
+        if (!psImageStats(stats, suspects, NULL, 0) || !isfinite(stats->clippedStdev)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to perform statistics.\n");
+            psFree(stats);
+            return NULL;
+        }
+        limit = thresh * stats->clippedStdev;
+        psTrace ("psModules.detrend", 3, "bad: %f -> %f\n", stats->clippedStdev, limit);
+        psFree(stats);
+        break;
+      }
+
+      case PM_MASK_ID_POISSON: {
+        psStats *stats = psStatsAlloc(PS_STAT_MAX); // Statistics
+        if (!psImageStats(stats, suspects, NULL, 0)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to perform statistics.\n");
+            psFree(stats);
+            return NULL;
+        }
+        psHistogram *histo = psHistogramAlloc(-0.5, stats->max + 0.5, stats->max + 1);
+        psFree(stats);
+        if (!psImageHistogram(histo, suspects, NULL, 0)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate histogram.\n");
+            psFree(histo);
+            return NULL;
+        }
+
+        // Find the mode.  Since this is a Poisson distribution (more or less), this should also be the mean
+        // and variance.
+        int max = 0;                    // Index of the mode
+        for (int i = 0; i < histo->nums->n; i++) {
+            if (histo->nums->data.F32[i] > histo->nums->data.F32[max]) {
+                max = i;
+            }
+        }
+
+        // Since the mode is most likely zero, we add one to get something realistic.  Then "thresh" is
+        // negative, so we subtract instead of add.
+        limit = max + 1.0 - thresh * sqrtf((float)max + 1.0);
+
+        psTrace ("psModules.detrend", 3, "bad: mode: %d, stdev: %f, limit: %f\n",
+                 max, sqrtf((float)max + 1.0), limit);
+        break;
+      }
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Invalid mask identify mode");
+        return NULL;
+    }
+
+    if (psTraceGetLevel("psModules.detrend") > 9) {
+        psStats *stats = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics
+        psImageStats(stats, suspects, NULL, 0);
+        psHistogram *histo = psHistogramAlloc(-0.5, stats->max + 0.5, stats->max + 1);
+        psImageHistogram(histo, suspects, NULL, 0);
+        for (int i = 0; i < histo->nums->n; i++) {
+            printf("%f --> %f : %f\n", histo->bounds->data.F32[i], histo->bounds->data.F32[i + 1],
+                   histo->nums->data.F32[i]);
+        }
+        psFree(stats);
+        psFree(histo);
+        printf("Threshold: %f\n", limit);
+    }
+
+    psTrace ("psModules.detrend", 3, "bad pixel threshold: %f", limit);
+
+    psImage *badpix = output->mask;     // Bad pixel mask
+    psImageInit(badpix, 0);
+
+    for (int y = 0; y < suspects->numRows; y++) {
+        for (int x = 0; x < suspects->numCols; x++) {
+            if (suspects->data.F32[y][x] >= limit) {
+                badpix->data.PS_TYPE_MASK_DATA[y][x] = maskVal;
+            }
+        }
+    }
+
+    return true;
+}
+
+pmMaskIdentifyMode pmMaskIdentifyModeFromString (const char *string) {
+
+    if (!strcasecmp(string, "VALUE")) {
+      return PM_MASK_ID_VALUE;
+    }
+    if (!strcasecmp(string, "FRACTION")) {
+      return PM_MASK_ID_FRACTION;
+    }
+    if (!strcasecmp(string, "SIGMA")) {
+      return PM_MASK_ID_SIGMA;
+    }
+    if (!strcasecmp(string, "POISSON")) {
+      return PM_MASK_ID_POISSON;
+    }
+    return PM_MASK_ID_NONE;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmMaskBadPixels.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmMaskBadPixels.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmMaskBadPixels.h	(revision 22322)
@@ -0,0 +1,71 @@
+/* @file pmMaskBadPixels.h
+ * @brief Mask bad pixels
+ *
+ * @author Ross Harman, MHPCC
+ * @author Eugene Magnier, IfA
+ *
+ * @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-03-29 03:10:17 $
+ * Copyright 2004 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_MASK_BAD_PIXELS_H
+#define PM_MASK_BAD_PIXELS_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+#define PM_MASK_ANALYSIS_SUSPECT "MASK.SUSPECT" // Readout analysis metadata keyword for suspect image
+#define PM_MASK_ANALYSIS_NUM "MASK.NUM" // Readout analysis metadata keyword for number of inputs
+
+
+typedef enum {
+  PM_MASK_ID_NONE,
+  PM_MASK_ID_VALUE,
+  PM_MASK_ID_FRACTION,
+  PM_MASK_ID_SIGMA,
+  PM_MASK_ID_POISSON,
+} pmMaskIdentifyMode;
+
+pmMaskIdentifyMode pmMaskIdentifyModeFromString (const char *string);
+
+/// Applies the bad pixel mask to the input
+///
+/// Pixels marked as bad within the mask are marked as bad within the input image's mask.  If maskVal is
+/// non-zero, all pixels in the mask have any of the same bits sets as maskVal shall have the corresponding
+/// bits raised.  If maskVal is zero, any zero pixels in the mask are OR-ed with PM_MASK_BAD.  Position
+/// offsets (such as due to trimming) between the input and mask are applied so that the same pixels are
+/// referred to.  The science readout must already have a supplied mask element (use eg. pmReadoutSetMask).
+/// The supplied mask image must be of MASK type
+bool pmMaskBadPixels(pmReadout *input,  ///< Input science image
+                     const pmReadout *mask, ///< Mask image to apply
+                     psMaskType maskVal ///< Mask value to apply
+                    );
+
+/// Find pixels outlying from the background, flagging suspect pixels
+///
+/// Pixels more than "rej" standard deviations from the background level (in flat-fielded,
+/// background-subtracted images) have the corresponding pixel in the "suspect pixels" image
+/// incremented.  After accumulating over a suitable sample of images, bad pixels should have a
+/// high value in the suspect pixels image, allowing them to be identified.  The suspect pixels
+/// image is of type S32.  The relevant median and standard deviation must be supplied in the
+/// readout->analysis metadata as READOUT.MEDIAN, READOUT.STDEVe
+bool pmMaskFlagSuspectPixels(pmReadout *output, ///< Output readout, optionally with suspect pixels image
+                             const pmReadout *readout, ///< Readout to inspect
+                             float median, ///< Image median
+                             float stdev, ///< Image standard deviation
+                             float rej, ///< Rejection threshold (standard deviations)
+                             psMaskType maskVal ///< Mask value for statistics
+    );
+
+/// Identify bad pixels from the suspect pixels image
+///
+/// Bad pixels are identified from the suspect pixels image (accumulated over a large number of images),
+/// according to the chosen mode.
+bool pmMaskIdentifyBadPixels(pmReadout *output, ///< Output readout, with suspect pixels imageOut
+                             psMaskType maskVal, ///< Value to set for bad pixels
+                             float thresh, ///< Threshold for bad pixel
+                             pmMaskIdentifyMode mode ///< Mode for identifying bad pixels
+    );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmNonLinear.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmNonLinear.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmNonLinear.c	(revision 22322)
@@ -0,0 +1,93 @@
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmNonLinear.h"
+
+pmReadout *pmNonLinearityPolynomial(pmReadout *inputReadout, const psPolynomial1D *input1DPoly)
+{
+    PS_ASSERT_PTR_NON_NULL(inputReadout, NULL);
+    PS_ASSERT_PTR_NON_NULL(inputReadout->image, NULL);
+    PS_ASSERT_IMAGE_TYPE(inputReadout->image, PS_TYPE_F32, NULL);
+    PS_ASSERT_PTR_NON_NULL(input1DPoly, NULL);
+
+    psImage *image = inputReadout->image; // Image to correct
+    for (int i = 0; i < image->numRows; i++) {
+        for (int j = 0; j < image->numCols; j++) {
+            image->data.F32[i][j] = psPolynomial1DEval(input1DPoly, image->data.F32[i][j]);
+        }
+    }
+    return inputReadout;
+}
+
+// set the bin closest to the corresponding value.  
+#define PS_BIN_FOR_VALUE(RESULT, VECTOR, VALUE) { \
+       	psVectorBinaryDisectResult result; \
+       	psScalar tmpScalar; \
+       	tmpScalar.type.type = PS_TYPE_F32; \
+	tmpScalar.data.F32 = (VALUE); \
+	RESULT = psVectorBinaryDisect (&result, VECTOR, &tmpScalar); \
+	switch (result) { \
+	  case PS_BINARY_DISECT_PASS: \
+            break; \
+	  case PS_BINARY_DISECT_OUTSIDE_RANGE: \
+            numPixels ++; \
+	    break; \
+	  case PS_BINARY_DISECT_INVALID_INPUT: \
+	  case PS_BINARY_DISECT_INVALID_TYPE: \
+	    psAbort ("programming error"); \
+	    break; \
+        } }
+
+pmReadout *pmNonLinearityLookup(pmReadout *inputReadout, const psVector *inFlux, const psVector *outFlux)
+{
+    PS_ASSERT_PTR_NON_NULL(inputReadout, NULL);
+    PS_ASSERT_PTR_NON_NULL(inputReadout->image, NULL);
+    PS_ASSERT_IMAGE_TYPE(inputReadout->image, PS_TYPE_F32, NULL);
+    PS_ASSERT_PTR_NON_NULL(inFlux, NULL);
+    if (inFlux->n < 2) {
+        psError(PS_ERR_UNKNOWN, true,
+                "pmNonLinearityLookup(): input vector less than 2 elements.  Returning inputReadout image.");
+        return(inputReadout);
+    }
+    PS_ASSERT_PTR_NON_NULL(outFlux,NULL);
+    psS32 tableSize = inFlux->n;
+    if (inFlux->n != outFlux->n) {
+        tableSize = PS_MIN(inFlux->n, outFlux->n);
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmNonLinear.c: pmNonLinearityLookup(): "
+                 "input vectors have different sizes (%ld, %ld)\n",
+                 inFlux->n, outFlux->n);
+    }
+    PS_ASSERT_VECTOR_TYPE(inFlux, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_TYPE(outFlux, PS_TYPE_F32, NULL);
+
+    psImage *image = inputReadout->image; // Input image
+    int numPixels = 0;                  // Number of pixels outside the range
+    int binNum;
+
+    for (int i = 0; i < image->numRows; i++) {
+        for (int j = 0; j < image->numCols; j++) {
+	    float value = image->data.F32[i][j];
+            PS_BIN_FOR_VALUE(binNum, inFlux, value);
+
+	    // Perform linear interpolation.
+	    // XXX this will result in non-sensical results if inFlux contains equal-value
+	    // bins.  either enforce d(inFlux)/d(binNum) > 0 or see psStats.c PS_BIN_INTERPOLATE
+	    float slope = 
+		(outFlux->data.F32[binNum + 1] - outFlux->data.F32[binNum]) /
+		(inFlux->data.F32[binNum + 1] - inFlux->data.F32[binNum]);
+	    image->data.F32[i][j] = slope*(value - inFlux->data.F32[binNum]) + outFlux->data.F32[binNum];
+        }
+    }
+    if (numPixels > 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmNonLinear.c: pmNonLinearityLookup(): %d pixels outside table.", numPixels);
+    }
+    return inputReadout;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmNonLinear.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmNonLinear.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmNonLinear.h	(revision 22322)
@@ -0,0 +1,35 @@
+/* @file pmNonLinear.h
+ * @brief Perform non-linear correction through polynomial or table lookup
+ *
+ * @author George Gusciora, MHPCC
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-30 21:12:56 $
+ * Copyright 2004 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_NON_LINEAR_H
+#define PM_NON_LINEAR_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+/// Correct non-linearity through polynomial
+///
+/// Applies a polynomial to the flux of each pixel in the input image to determine the corrected flux.
+pmReadout *pmNonLinearityPolynomial(pmReadout *in, ///< Input image, to correct
+                                    const psPolynomial1D *coeff ///< Polynomial for non-linearity correction
+                                   );
+
+/// Correct non-linearity through table lookup
+///
+/// For each pixel in the input image, performs linear interpolation on the table (from the two vectors) to
+/// determine the corrected flux.
+pmReadout *pmNonLinearityLookup(pmReadout *in, ///< Input image, to correct
+                                const psVector *inFlux, ///< Table column with input fluxes
+                                const psVector *outFlux ///< Table column with output fluxes
+                               );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmOverscan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmOverscan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmOverscan.c	(revision 22322)
@@ -0,0 +1,401 @@
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPACalibration.h"
+
+#include "pmOverscan.h"
+
+#define SMOOTH_NSIGMA 4.0               // Number of Gaussian sigma the smoothing kernel extends
+
+static void pmOverscanOptionsFree(pmOverscanOptions *options)
+{
+    psFree(options->stat);
+    psFree(options->poly);
+    psFree(options->spline);
+}
+
+pmOverscanOptions *pmOverscanOptionsAlloc(bool single, pmFit fitType, unsigned int order, psStats *stat,
+                                          int boxcar, float gauss)
+{
+    pmOverscanOptions *opts = psAlloc(sizeof(pmOverscanOptions));
+    psMemSetDeallocator(opts, (psFreeFunc)pmOverscanOptionsFree);
+
+    // Inputs
+    opts->single = single;
+    opts->constant = false;
+    opts->fitType = fitType;
+    opts->order = order;
+    opts->stat = psMemIncrRefCounter(stat);
+
+    // Smoothing
+    opts->boxcar = boxcar;
+    opts->gauss = gauss;
+
+    // Outputs
+    opts->poly = NULL;
+    opts->spline = NULL;
+
+    return opts;
+}
+
+// Produce an overscan vector from an array of pixels
+psVector *pmOverscanVector(float *chi2, // chi^2 from fit
+			   pmOverscanOptions *overscanOpts, // Overscan options
+			   const psArray *pixels, // Array of vectors containing the pixel values
+			   psStats *myStats // Statistic to use in reducing the overscan
+    )
+{
+    assert(overscanOpts);
+    assert(pixels);
+    assert(myStats);
+
+    psStatsOptions statistic = psStatsSingleOption(myStats->options); // Statistic to use
+    assert(statistic != 0);
+
+    // Reduce the overscans
+    psVector *reduced = psVectorAlloc(pixels->n, PS_TYPE_F32); // Overscan for each row
+    psVector *ordinate = psVectorAlloc(pixels->n, PS_TYPE_F32); // Ordinate
+    psVector *mask = psVectorAlloc(pixels->n, PS_TYPE_U8); // Mask for fitting
+
+    for (int i = 0; i < pixels->n; i++) {
+        psVector *values = pixels->data[i]; // Vector with overscan values
+        if (values->n > 0) {
+            mask->data.U8[i] = 0;
+            ordinate->data.F32[i] = 2.0*(float)i/(float)pixels->n - 1.0; // Scale to [-1,1]
+            psVectorStats(myStats, values, NULL, NULL, 0);
+            reduced->data.F32[i] = psStatsGetValue(myStats, statistic);
+        } else if (overscanOpts->fitType == PM_FIT_NONE) {
+            psError(PS_ERR_UNKNOWN, true, "The overscan is not supplied for all points on the "
+                    "image, and no fit is requested.\n");
+            return NULL;
+        } else {
+            // We'll fit this one out
+            mask->data.U8[i] = 1;
+        }
+    }
+
+    // Smooth the reduced vector
+    if (overscanOpts->boxcar > 0) {
+        psVector *smoothed = psVectorBoxcar(NULL, reduced, overscanOpts->boxcar); // Smoothed vector
+        psFree(reduced);
+        reduced = smoothed;
+    }
+    if (isfinite(overscanOpts->gauss) && overscanOpts->gauss > 0) {
+        if (overscanOpts->boxcar > 0) {
+            psWarning("Gaussian smoothing the boxcar smoothed overscan --- you asked for it.");
+        }
+        psVector *smoothed = psVectorSmooth(NULL, reduced, overscanOpts->gauss, SMOOTH_NSIGMA);
+        psFree(reduced);
+        reduced = smoothed;
+    }
+
+    // Fit the overscan, if required
+    psVector *fitted;                   // Fitted overscan values
+    switch (overscanOpts->fitType) {
+      case PM_FIT_NONE:
+        // No fitting --- that's easy.
+        fitted = psMemIncrRefCounter(reduced);
+        break;
+      case PM_FIT_POLY_ORD:
+        if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order ||
+                                   overscanOpts->poly->type != PS_POLYNOMIAL_ORD)) {
+            psFree(overscanOpts->poly);
+            overscanOpts->poly = NULL;
+        }
+        if (! overscanOpts->poly) {
+            overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, overscanOpts->order);
+        }
+        psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate);
+        fitted = psPolynomial1DEvalVector(overscanOpts->poly, ordinate);
+        break;
+      case PM_FIT_POLY_CHEBY:
+        if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order ||
+                                   overscanOpts->poly->type != PS_POLYNOMIAL_CHEB)) {
+            psFree(overscanOpts->poly);
+            overscanOpts->poly = NULL;
+        }
+        if (! overscanOpts->poly) {
+            overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, overscanOpts->order);
+        }
+        psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate);
+        fitted = psPolynomial1DEvalVector(overscanOpts->poly, ordinate);
+        break;
+      case PM_FIT_SPLINE:
+        // XXX I don't think psSpline1D is up to scratch yet --- it has no mask, and requires an
+        // input spline
+        overscanOpts->spline = psVectorFitSpline1D(reduced, ordinate);
+        fitted = psSpline1DEvalVector(overscanOpts->spline, ordinate);
+        break;
+      default:
+        psError(PS_ERR_UNKNOWN, true, "Unknown value for the fitting type: %d\n", overscanOpts->fitType);
+        return NULL;
+        break;
+    }
+
+    if (chi2) {
+        *chi2 = 0.0;                    // chi^2 (sort of)
+        for (int i = 0; i < reduced->n; i++) {
+            *chi2 += PS_SQR(fitted->data.F32[i] - reduced->data.F32[i]);
+        }
+    }
+
+    psFree(reduced);
+    psFree(ordinate);
+    psFree(mask);
+
+    return fitted;
+}
+
+bool pmOverscanUpdateHeader (pmHDU *hdu, pmOverscanOptions *overscanOpts, float chi2) {
+
+    psString comment = NULL;    // Comment to add
+
+    switch (overscanOpts->fitType) {
+      case PM_FIT_POLY_ORD:
+      case PM_FIT_POLY_CHEBY: {
+	  psStringAppend(&comment, "Overscan fit (chi2: %.2f): ", chi2);
+	  psPolynomial1D *poly = overscanOpts->poly; // The polynomial
+	  for (int i = 0; i < poly->nX; i++) {
+	      psStringAppend(&comment, "%.1f ", poly->coeff[i]);
+	  }
+	  psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
+	  psFree(comment);
+	  comment = NULL;
+
+	  // write metadata header value
+	  psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE,
+			   "Overscan value", poly->coeff[0]);
+	  psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE,
+			   "Overscan stdev", poly->coeffErr[0]);
+	  break;
+      }
+      case PM_FIT_SPLINE: {
+	  psSpline1D *spline = overscanOpts->spline; // The spline
+	  for (int i = 0; i < spline->n; i++) {
+	      psStringAppend(&comment, "Overscan fit (chi2: %.2f) %d:", chi2, i);
+	      psPolynomial1D *poly = spline->spline[i]; // i-th polynomial
+	      for (int j = 0; j < poly->nX; j++) {
+		  psStringAppend(&comment, "%.1f ", poly->coeff[i]);
+	      }
+	      psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+			       comment, "");
+	      psFree(comment);
+	      comment = NULL;
+	  }
+	  // write metadata header value
+	  psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE,
+			   "Overscan value", NAN);
+	  psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE,
+			   "Overscan stdev", NAN);
+	  break;
+      }
+      case PM_FIT_NONE:
+	break;
+      default:
+	psAbort("Should never get here!!!\n");
+    }
+    return true;
+}
+
+bool pmOverscanSubtract (pmReadout *input, pmOverscanOptions *overscanOpts) {
+
+    assert (input);
+
+    if (overscanOpts == NULL) return true; // no overscan subtraction requested
+
+    pmHDU *hdu = pmHDUFromReadout(input);  // HDU of interest
+    psImage *image = input->image;
+
+    // check for 'soft bias' (simple, fixed offset to be subtracted)
+    if (overscanOpts->constant) {
+	// write metadata header value
+	psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value",
+			 overscanOpts->value);
+	psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", NAN);
+
+	(void)psBinaryOp(input->image, input->image, "-", psScalarAlloc((float)overscanOpts->value, PS_TYPE_F32));
+
+	return true;
+    }
+
+    // we are performing a statitical analysis of the overscan region
+
+    // Check for an unallowable pmFit.
+    if (overscanOpts->fitType != PM_FIT_NONE && overscanOpts->fitType != PM_FIT_POLY_ORD &&
+	overscanOpts->fitType != PM_FIT_POLY_CHEBY && overscanOpts->fitType != PM_FIT_SPLINE) {
+	psError(PS_ERR_UNKNOWN, true, "Invalid fit type (%d).  Returning original image.\n",
+		overscanOpts->fitType);
+	return false;
+    }
+
+    psList *overscans = input->bias; // List of the overscan images
+
+    psStatsOptions statistic = psStatsSingleOption(overscanOpts->stat->options); // Statistic to use
+    if (statistic == 0) {
+	psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Multiple or no statistics options set: %p\n",
+		overscanOpts->stat);
+	return false;
+    }
+    psStats *stats = psStatsAlloc(statistic); // A new psStats, to avoid clobbering original
+
+    psString comment = NULL;    // Comment to add
+    psStringAppend(&comment, "Subtracting overscan (stat %x; type %x; order %d)",
+		   statistic, overscanOpts->fitType, overscanOpts->order);
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+		     comment, "");
+    psFree(comment);
+
+    // Reduce all overscan pixels to a single value
+    if (overscanOpts->single) {
+	psVector *pixels = psVectorAlloc(0, PS_TYPE_F32);
+	psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
+	psImage *overscan = NULL;   // Overscan image from iterator
+	while ((overscan = psListGetAndIncrement(iter))) {
+	    int index = pixels->n;  // Index
+	    pixels = psVectorRealloc(pixels, pixels->n + overscan->numRows * overscan->numCols);
+	    pixels->n += overscan->numRows * overscan->numCols;
+	    for (int i = 0; i < overscan->numRows; i++) {
+		memcpy(&pixels->data.F32[index], overscan->data.F32[i],
+		       overscan->numCols * sizeof(psF32));
+		index += overscan->numCols;
+	    }
+	}
+	psFree(iter);
+
+	(void)psVectorStats(stats, pixels, NULL, NULL, 0);
+	psFree(pixels);
+	double reduced = psStatsGetValue(stats, statistic); // Result of statistics
+
+	psString comment = NULL;    // Comment to add
+	psStringAppend(&comment, "Overscan value: %f", reduced);
+	psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
+	psFree(comment);
+
+	// write metadata header value
+	psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value",
+			 reduced);
+	psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", NAN);
+
+	(void)psBinaryOp(image, image, "-", psScalarAlloc((float)reduced, PS_TYPE_F32));
+	psFree(stats);
+	return true;
+    } 
+
+    // We are performing a row-by-row overscan subtraction
+    bool readRows = psMetadataLookupBool(NULL, input->parent->concepts, "CELL.READDIR"); // Read direction
+    float chi2 = NAN;           // chi^2 from fit
+
+    // adjust operation depending on the read direction : need to re-org pixels for columns
+    if (readRows) {
+	// The read direction is rows
+	psArray *pixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels
+	for (int i = 0; i < pixels->n; i++) {
+	    pixels->data[i] = psVectorAlloc(0, PS_TYPE_F32);
+	}
+
+	// Pull the pixels out into the vectors
+	psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
+	psImage *overscan = NULL; // Overscan image from iterator
+	while ((overscan = psListGetAndIncrement(iter))) {
+	    // the overscan and image might not be aligned.  pixels->data represents
+	    // the image row pixels.
+	    int diff = overscan->row0 - image->row0; // Offset between the two regions
+	    for (int i = PS_MAX(0,diff); i < PS_MIN(image->numRows, overscan->numRows + diff); i++) {
+		int j = i - diff;
+		// i is row on image
+		// j is row on overscan
+		psVector *values = pixels->data[i];
+		int index = values->n; // Index in the vector
+		values = psVectorRealloc(values, values->n + overscan->numCols);
+		values->n += overscan->numCols;
+		memcpy(&values->data.F32[index], overscan->data.F32[j],
+		       overscan->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
+		index += overscan->numCols;
+		pixels->data[i] = values; // Update the pointer in case it's moved
+	    }
+	}
+	psFree(iter);
+
+	// Reduce the overscans
+	psVector *reduced = pmOverscanVector(&chi2, overscanOpts, pixels, stats);
+	psFree(pixels);
+	if (! reduced) {
+	    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate overscan vector.\n");
+	    psFree(stats);
+	    return false;
+	}
+
+	// Subtract row by row
+	for (int i = 0; i < image->numRows; i++) {
+	    for (int j = 0; j < image->numCols; j++) {
+		image->data.F32[i][j] -= reduced->data.F32[i];
+	    }
+	}
+	psFree(reduced);
+    } else {
+	// The read direction is columns
+	psArray *pixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels
+	for (int i = 0; i < pixels->n; i++) {
+	    psVector *values = psVectorAlloc(0, PS_TYPE_F32);
+	    pixels->data[i] = values;
+	}
+
+	// Pull the pixels out into the vectors
+	psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
+	psImage *overscan = NULL; // Overscan image from iterator
+	while ((overscan = psListGetAndIncrement(iter))) {
+	    // the overscan and image might not be aligned.  pixels->data represents
+	    // the image row pixels.
+	    int diff = overscan->col0 - image->col0; // Offset between the two regions
+	    for (int i = PS_MAX(0,diff); i < PS_MIN(image->numCols, overscan->numCols + diff); i++) {
+		int iFixed = i - diff;
+		// i is column on image
+		// iFixed is column on overscan
+		psVector *values = pixels->data[i];
+		int index = values->n; // Index in the vector
+		values = psVectorRealloc(values, values->n + overscan->numRows);
+		for (int j = 0; j < overscan->numRows; j++) {
+		    values->data.F32[index++] = overscan->data.F32[j][iFixed];
+		}
+		values->n += overscan->numRows;
+		pixels->data[i] = values; // Update the pointer in case it's moved
+	    }
+	}
+	psFree(iter);
+
+	// Reduce the overscans
+	psVector *reduced = pmOverscanVector(&chi2, overscanOpts, pixels, stats);
+	psFree(pixels);
+	if (! reduced) {
+	    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate overscan vector.\n");
+	    psFree(stats);
+	    return false;
+	}
+
+	// Subtract column by column
+	for (int i = 0; i < image->numCols; i++) {
+	    for (int j = 0; j < image->numRows; j++) {
+		image->data.F32[j][i] -= reduced->data.F32[i];
+	    }
+	}
+	psFree(reduced);
+    }
+
+    pmOverscanUpdateHeader (hdu, overscanOpts, chi2);
+    psFree(stats);
+
+    return true;
+
+} // End of overscan subtraction
+    
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmOverscan.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmOverscan.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmOverscan.h	(revision 22322)
@@ -0,0 +1,72 @@
+/* @file pmOverscan.h
+ * @brief Functions to subtract the overscan, used by pmBiasSubtract
+ *
+ * @author George Gusciora, MHPCC
+ * @author Paul Price, IfA
+ * @author Eugene Magnier, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-08-15 20:21:18 $
+ * Copyright 2004--2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_OVERSCAN_H
+#define PM_OVERSCAN_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+/// Type of fit to perform
+typedef enum {
+    PM_FIT_NONE,                        ///< No fit
+    PM_FIT_POLY_ORD,                    ///< Fit ordinary polynomial
+    PM_FIT_POLY_CHEBY,                  ///< Fit Chebyshev polynomial
+    PM_FIT_SPLINE                       ///< Fit cubic splines
+} pmFit;
+
+/// Options for overscan subtraction
+///
+/// The overscan subtraction may be performed by reducing all overscan regions to a single value (e.g., if
+/// there is no structure); or the overscan may be fit perpendicular to the read direction (usually the
+/// columns) with a particular functional form; or a single value may be subtracted for each read/scan without
+/// fitting (if the structure defies characterisation).  In any case, statistics are required to reduce
+/// multiple values to a single value (either for the scan, or for the entire overscan regions).
+typedef struct
+{
+    // Inputs
+    bool single;                        ///< Reduce all overscan regions to a single value?
+    bool constant;			///< use a supplied constant value (do not measure region)
+    float value;			///< supplied value if needed (per above)
+    pmFit fitType;                      ///< Type of fit to overscan
+    unsigned int order;                 ///< Order of polynomial, or number of spline pieces
+    psStats *stat;                      ///< Statistic to use when reducing the minor direction
+    int boxcar;                         ///< Boxcar smoothing radius
+    float gauss;                        ///< Gaussian smoothing sigma
+    // Outputs
+    psPolynomial1D *poly;               ///< Result of polynomial fit
+    psSpline1D *spline;                 ///< Result of spline fit
+}
+pmOverscanOptions;
+
+/// Allocator for overscan options
+pmOverscanOptions *pmOverscanOptionsAlloc(bool single, ///< Reduce all overscan regions to a single value?
+                                          pmFit fitType, ///< Type of fit to overscan
+                                          unsigned int order, ///< Order of polynomial, or number of splines
+                                          psStats *stat, ///< Statistic to use
+                                          int boxcar, ///< Boxcar smoothing radius
+                                          float gauss ///< Gaussian smoothing sigma
+                                         );
+
+psVector *pmOverscanVector(float *chi2, // chi^2 from fit
+			   pmOverscanOptions *overscanOpts, // Overscan options
+			   const psArray *pixels, // Array of vectors containing the pixel values
+			   psStats *myStats // Statistic to use in reducing the overscan
+    );
+
+bool pmOverscanUpdateHeader (pmHDU *hdu, pmOverscanOptions *overscanOpts, float chi2);
+
+bool pmOverscanSubtract (pmReadout *input, pmOverscanOptions *overscanOpts);
+
+/// @}
+#endif
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShifts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShifts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShifts.c	(revision 22322)
@@ -0,0 +1,477 @@
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAUtils.h"
+#include "pmShifts.h"
+
+#define SHIFTS_BUFFER 100               // Buffer size for shifts
+#define TRACE "psModules.detrend"       // Trace facility
+#define FFT_SIZE 25                     // Size at which we use FFT instead of direct convolution
+
+// XXX To do:
+// * Make the table column names configurable, by having a SHIFTS metadata in the camera format, with entries
+//   specifying the column names.
+
+
+static void shiftsFree(pmShifts *shifts)
+{
+    psFree(shifts->x);
+    psFree(shifts->y);
+    psFree(shifts->t);
+    return;
+}
+
+pmShifts *pmShiftsAlloc(bool tRel, bool xyRel)
+{
+    pmShifts *shifts = psAlloc(sizeof(pmShifts));
+    psMemSetDeallocator(shifts, (psFreeFunc)shiftsFree);
+
+    shifts->x = psVectorAllocEmpty(SHIFTS_BUFFER, PS_TYPE_S32);
+    shifts->y = psVectorAllocEmpty(SHIFTS_BUFFER, PS_TYPE_S32);
+    shifts->t = psVectorAllocEmpty(SHIFTS_BUFFER, PS_TYPE_F32);
+    shifts->num = 0;
+
+    // Suitable defaults
+    shifts->tRelative = tRel;
+    shifts->xyRelative = xyRel;
+
+    return shifts;
+}
+
+// Look up the cell within a hash; supplement the hash with a new value if it doesn't exist
+static pmShifts *cellVectors(psHash *shifts, // Hash of shifts
+                             const char *cellName, // Key for hash
+                             bool tRel, bool xyRel // Are the shifts relative?
+    )
+{
+    assert(shifts);
+    assert(cellName);
+
+    // Find the appropriate cell
+    pmShifts *vectors = psHashLookup(shifts, cellName);
+    if (!vectors) {
+        vectors = pmShiftsAlloc(tRel, xyRel);
+        psHashAdd(shifts, cellName, vectors);
+        psFree(vectors);            // Drop reference
+    }
+    return vectors;
+}
+
+
+bool pmShiftsRead(const pmCell *cell, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+
+    bool mdok;                          // Status of MD lookup
+    pmShifts *check = psMetadataLookupPtr(&mdok, cell->analysis, PM_SHIFTS_TABLE_NAME); // Table, or NULL
+    if (check) {
+        psTrace(TRACE, 2, "Cell already has OT kernel present.\n");
+        return true;                    // No error
+    }
+    psTrace(TRACE, 2, "Reading FITS file for OT kernels.\n");
+
+    // Determine camera layout
+    pmChip *chip = cell->parent;        // The parent chip
+    pmFPA *fpa = chip->parent;          // The parent FPA
+    pmHDU *phu = NULL;                  // The primary header
+    pmFPALevel phuLevel = PM_FPA_LEVEL_NONE; // Level of the PHU
+    long numChips = 0;                  // Number of chips below the PHU; for setting efficient hash size
+    long numCells = 0;                  // Number of cells below the PHU; for setting efficient hash size
+    if (fpa->hdu) {
+        phu = fpa->hdu;
+        // Count the cells
+        psArray *chips = fpa->chips;    // Array of chips
+        numChips = chips->n;
+        for (int i = 0; i < chips->n; i++) {
+            pmChip *chip = chips->data[i]; // Chip of interest
+            numCells += chip->cells->n;
+        }
+        numCells = (float)numCells / (float)numChips + 0.5; // Average number of cells per chip
+        phuLevel = PM_FPA_LEVEL_FPA;
+    }
+    if (!phu && chip->hdu) {
+        phu = chip->hdu;
+        numChips = 1;
+        numCells = chip->cells->n;
+        phuLevel = PM_FPA_LEVEL_CHIP;
+    }
+    if (!phu && cell->hdu) {
+        phu = cell->hdu;
+        numChips = 0;
+        numCells = 1;
+        phuLevel = PM_FPA_LEVEL_CELL;
+    }
+    if (!phu || phuLevel == PM_FPA_LEVEL_NONE) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Can't find the PHU.\n");
+        return false;
+    }
+
+
+    // Find out what to read
+    psMetadata *shiftsInfo = psMetadataLookupMetadata(&mdok, phu->format, "SHIFTS"); // Shifts information
+    if (!mdok || !shiftsInfo) {
+        // We have read all the shifts that we have been told about --- which is none.
+        return true;
+    }
+    const char *shiftsExt = psMetadataLookupStr(&mdok, shiftsInfo, "EXTENSION"); // Extension name for shifts
+    if (!mdok || !shiftsExt) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't find EXTENSION name in SHIFTS information from camera format.");
+        return false;
+    }
+    const char *chipCol = NULL;         // Column name for chip
+    if (phuLevel == PM_FPA_LEVEL_FPA) {
+        chipCol = psMetadataLookupStr(&mdok, shiftsInfo, "CHIP");
+        if (!mdok || !chipCol) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Can't find CHIP column name in SHIFTS information from camera format.");
+            return false;
+        }
+    }
+    const char *cellCol = NULL;         // Column name for cell
+    if (phuLevel <= PM_FPA_LEVEL_CHIP) {
+        cellCol = psMetadataLookupStr(&mdok, shiftsInfo, "CELL");
+        if (!mdok || !chipCol) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Can't find CELL column name in SHIFTS information from camera format.");
+            return false;
+        }
+    }
+    const char *tCol = psMetadataLookupStr(&mdok, shiftsInfo, "T");
+    if (!mdok || !tCol) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't find T column name in SHIFTS information from camera format.");
+        return false;
+    }
+    const char *xCol = psMetadataLookupStr(&mdok, shiftsInfo, "X");
+    if (!mdok || !xCol) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't find X column name in SHIFTS information from camera format.");
+        return false;
+    }
+    const char *yCol = psMetadataLookupStr(&mdok, shiftsInfo, "Y");
+    if (!mdok || !yCol) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't find Y column name in SHIFTS information from camera format.");
+        return false;
+    }
+
+    bool tRel = psMetadataLookupBool(&mdok, shiftsInfo, "TRELATIVE");
+    if (!mdok) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't find TRELATIVE in SHIFTS information from camera format.");
+        return false;
+    };
+    bool xyRel = psMetadataLookupStr(&mdok, shiftsInfo, "XYRELATIVE");
+    if (!mdok) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't find XYRELATIVE in SHIFTS information from camera format.");
+        return false;
+    };
+
+    // Read the FITS file
+    int origExt = psFitsGetExtNum(fits); // Original extension number; to preserve position
+    if (!psFitsMoveExtName(fits, shiftsExt)) {
+        psError(PS_ERR_IO, false, "Unable to move to shifts extension %s", shiftsExt);
+        return false;
+    }
+    psArray *table = psFitsReadTable(fits); // The table of shifts
+    if (!table) {
+        psError(PS_ERR_IO, false, "Unable to read shifts table.\n");
+        return false;
+    }
+
+    // More sensible storage
+    pmShifts *singleShifts = NULL;      // Shifts for a single cell
+    psHash *multipleShifts = NULL;      // Shifts for multiple cells, stored by cell name
+    switch (phuLevel) {
+      case PM_FPA_LEVEL_FPA:
+        multipleShifts = psHashAlloc(2 * numChips);
+        break;
+      case PM_FPA_LEVEL_CHIP:
+        multipleShifts = psHashAlloc(2 * numCells);
+        break;
+      case PM_FPA_LEVEL_CELL:
+        singleShifts = pmShiftsAlloc(tRel, xyRel);
+        break;
+      default:
+        psAbort("Should never get here.\n");
+    }
+
+    // Pull values out of the table into something a bit more sensible
+    for (int i = 0; i < table->n; i++) {
+        psMetadata *row = table->data[i]; // The row of interest
+
+        const char *chipName = NULL;    // Name of chip
+        if (phuLevel == PM_FPA_LEVEL_FPA) {
+            // Only care about the chip name if there's more than one chip
+            chipName = psMetadataLookupStr(&mdok, row, chipCol);
+            if (!mdok || !chipName) {
+                psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find column %s in row %d of shifts table\n",
+                        chipCol, i);
+                psFree(multipleShifts);
+                psFree(table);
+                return false;
+            }
+        }
+        const char *cellName = NULL;    // Name of cell
+        if (phuLevel <= PM_FPA_LEVEL_CHIP) {
+            // Only care about the cell name if there's a chip
+            cellName = psMetadataLookupStr(&mdok, row, cellCol);
+            if (!mdok || !cellName) {
+                psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find column %s in row %d of shifts table\n",
+                        cellCol, i);
+                psFree(multipleShifts);
+                psFree(table);
+                return false;
+            }
+        }
+        float x = psMetadataLookupS32(&mdok, row, xCol); // Shift in x
+        if (!mdok) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find column %s in row %d of shifts table\n",
+                    xCol, i);
+            psFree(multipleShifts);
+            psFree(table);
+            return false;
+        }
+        float y = psMetadataLookupF32(&mdok, row, yCol); // Shift in y
+        if (!mdok) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find column %s in row %d of shifts table\n",
+                    yCol, i);
+            psFree(multipleShifts);
+            psFree(table);
+            return false;
+        }
+        float t = psMetadataLookupF32(&mdok, row, tCol); // Time of shift
+        if (!mdok) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find column %s in row %d of shifts table\n",
+                    tCol, i);
+            psFree(multipleShifts);
+            psFree(table);
+            return false;
+        }
+
+        pmShifts *shifts = NULL;        // Shifts for the cell of interest
+        switch (phuLevel) {
+          case PM_FPA_LEVEL_FPA: {
+              psHash *cells = psHashLookup(multipleShifts, chipName); // Hash of cells
+              if (!cells) {
+                  cells = psHashAlloc(numCells);
+                  psHashAdd(multipleShifts, chipName, cells);
+                  psFree(cells);          // Drop reference
+              }
+              shifts = cellVectors(cells, cellName, tRel, xyRel);
+              break;
+          }
+          case PM_FPA_LEVEL_CHIP:
+            shifts = cellVectors(multipleShifts, cellName, tRel, xyRel);
+            break;
+          case PM_FPA_LEVEL_CELL:
+            shifts = singleShifts;
+            break;
+          default:
+            psAbort("Should never get here.\n");
+        }
+
+        // Add the shift
+        psVectorExtend(shifts->x, 1, SHIFTS_BUFFER);
+        psVectorExtend(shifts->y, 1, SHIFTS_BUFFER);
+        psVectorExtend(shifts->t, 1, SHIFTS_BUFFER);
+        shifts->x->data.S32[shifts->num] = (int)x;
+        shifts->y->data.S32[shifts->num] = (int)y;
+        shifts->t->data.F32[shifts->num] = t;
+        shifts->num++;
+    }
+    psFree(table);
+
+    // Put the kernels into their own cells
+    if (phuLevel == PM_FPA_LEVEL_CELL) {
+        // Only a single cell
+        psMetadataAddPtr(cell->analysis, PS_LIST_TAIL, PM_SHIFTS_TABLE_NAME, PS_DATA_KERNEL,
+                         "Orthogonal transfer shifts", singleShifts);
+        psFree(singleShifts);
+        return true;
+    } else {
+        psList *names = psHashKeyList(multipleShifts); // List of hash keys (chip/cell names)
+        psListIterator *namesIter = psListIteratorAlloc(names, PS_LIST_HEAD, false); // Iterator for names
+        const char *name;               // Name, from iteration
+        while ((name = psListGetAndIncrement(namesIter))) {
+            switch (phuLevel) {
+              case PM_FPA_LEVEL_FPA: {
+                  int chipNum = pmFPAFindChip(fpa, name); // Number of chip of interest
+                  if (chipNum < 0) {
+                      psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find chip %s", name);
+                      psFree(namesIter);
+                      psFree(names);
+                      psFree(multipleShifts);
+                      return false;
+                  }
+                  pmChip *chip = fpa->chips->data[chipNum]; // Chip of interest
+
+                  // Loop over component cells
+                  psHash *cells = psHashLookup(multipleShifts, name); // Hash of cells
+                  psList *cellNames = psHashKeyList(multipleShifts); // List of hash keys (cell names)
+                  psListIterator *cellNamesIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false);
+                  const char *cellName;       // Cell name, from iteration
+                  while ((cellName = psListGetAndIncrement(cellNamesIter))) {
+                      int cellNum = pmChipFindCell(chip, cellName); // Number of cell of interest
+                      if (!cell) {
+                          psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find cell %s", cellName);
+                          psFree(cellNamesIter);
+                          psFree(cellNames);
+                          psFree(namesIter);
+                          psFree(names);
+                          psFree(multipleShifts);
+                          return false;
+                      }
+                      pmCell *cell = chip->cells->data[cellNum]; // Cell of interest
+                      if (psMetadataLookup(cell->analysis, PM_SHIFTS_TABLE_NAME)) {
+                          // Already has a shifts table, for some reason
+                          psWarning("Chip %s, cell %s already has a shifts table --- overwriting\n",
+                                    name, cellName);
+                      }
+
+                      pmShifts *vectors = psHashLookup(cells, cellName); // Shifts for the cell of interest
+                      psMetadataAddPtr(cell->analysis, PS_LIST_TAIL, PM_SHIFTS_TABLE_NAME,
+                                       PS_DATA_KERNEL | PS_META_REPLACE,
+                                       "Orthogonal transfer shifts", vectors);
+                  }
+                  psFree(cellNamesIter);
+                  psFree(cellNames);
+                  break;
+              }
+              case PM_FPA_LEVEL_CHIP: {
+                  int cellNum = pmChipFindCell(chip, name); // Number of cell of interest
+                  if (!cell) {
+                      psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find cell %s", name);
+                      psFree(namesIter);
+                      psFree(names);
+                      psFree(multipleShifts);
+                      return false;
+                  }
+                  pmCell *cell = chip->cells->data[cellNum]; // Cell of interest
+                  if (psMetadataLookup(cell->analysis, PM_SHIFTS_TABLE_NAME)) {
+                      // Already has a shifts table, for some reason
+                      psWarning("Cell %s already has a shifts table --- overwriting\n", name);
+                  }
+
+                  pmShifts *vectors = psHashLookup(multipleShifts, name); // Shifts for this cell
+                  psMetadataAddPtr(cell->analysis, PS_LIST_TAIL, PM_SHIFTS_TABLE_NAME,
+                                   PS_DATA_KERNEL | PS_META_REPLACE,
+                                   "Orthogonal transfer shifts", vectors);
+                  break;
+              }
+              default:
+                psAbort("Should never get here.\n");
+            }
+        }
+        psFree(namesIter);
+        psFree(names);
+        psFree(multipleShifts);
+    }
+
+    // Go back to the original position in the FITS file
+    return psFitsMoveExtNum(fits, origExt, false);
+}
+
+
+// Generate a kernel and stuff it on the cell metadata
+bool pmShiftsKernel(const pmCell *cell     // Cell to which the shifts belong
+    )
+{
+    PS_ASSERT_PTR(cell, false);
+
+    bool mdok;                          // Status of MD lookup
+    pmShifts *shifts = psMetadataLookupPtr(&mdok, cell->analysis, PM_SHIFTS_TABLE_NAME);
+    if (!shifts) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find shifts table for cell.\n");
+        return false;
+    }
+
+    psKernel *kernel = psKernelGenerate(shifts->t, shifts->x, shifts->y,
+                                        shifts->tRelative, shifts->xyRelative); // Shift kernel
+    if (!kernel) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate kernel from OT shifts");
+        return false;
+    }
+    psMetadataAddPtr(cell->analysis, PS_LIST_TAIL, PM_SHIFTS_KERNEL_NAME, PS_DATA_KERNEL,
+                     "Orthogonal transfer kernel, calculated from shifts", kernel);
+    psFree(kernel);
+
+    return true;
+}
+
+bool pmShiftsConvolve(pmReadout *detrend, const pmCell *source, psMaskType maskVal)
+{
+    PS_ASSERT_PTR(detrend, false);
+    PS_ASSERT_PTR(source, false);
+
+    bool mdok;                          // Status of MD lookup
+    psKernel *kernel = psMetadataLookupPtr(&mdok, source->analysis, PM_SHIFTS_KERNEL_NAME);
+    if (!kernel) {
+        // Maybe they just forgot to generate the kernel with pmShiftsKernel
+        if (psMetadataLookup(source->analysis, PM_SHIFTS_TABLE_NAME)) {
+            if (!pmShiftsKernel(source)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to generate shifts kernel.");
+                return false;
+            }
+            // Hopefully it's there now
+            kernel = psMetadataLookupPtr(&mdok, source->analysis, PM_SHIFTS_KERNEL_NAME);
+            if (!kernel) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to find shifts kernel.");
+                return false;
+            }
+        } else {
+            psError(PS_ERR_UNKNOWN, false, "Unable to find shifts kernel or shifts table.");
+            return false;
+        }
+    }
+
+    if (detrend->image) {
+#if 1
+        // Always do direct convolution (no fuss with edge effects)
+        psImage *convolved = psImageConvolveDirect(NULL, detrend->image, kernel);
+#else
+        // Kernel size-dependent convolution --- if it's big, use the FFT
+        int xSize = kernel->xMax - kernel->xMin; // Kernel size in x
+        int ySize = kernel->yMax - kernel->yMin; // Kernel size in y
+        psImage *convolved;
+        if (xSize * ySize < FFT_SIZE * FFT_SIZE) {
+            convolved = psImageConvolveDirect(NULL, detrend->image, kernel);
+        } else {
+            // This is a little dodgy --- making choices about parameters without the user's input
+            psStats *stats = psImageStats(PS_STAT_ROBUST_MEDIAN);
+            stats->nSubsample = 10000;
+            psImageBackground(stats, detrend->image, detrend->mask, maskVal, NULL);
+            convolved = psImageConvolveFFT(detrend->image, kernel, stats->robustMedian);
+        }
+#endif
+        if (!convolved) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to convolve detrend image.");
+            return false;
+        }
+        psFree(detrend->image);
+        detrend->image = convolved;
+    }
+
+    // Purposely ignoring the weight map --- don't care about the weight map for a detrend image
+
+    if (maskVal && detrend->mask && !psImageConvolveMaskDirect(detrend->mask, detrend->mask, maskVal, 0,
+                                                               kernel->xMin, kernel->xMax,
+                                                               kernel->yMin, kernel->yMax)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to convolve detrend mask.");
+        return false;
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShifts.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShifts.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShifts.h	(revision 22322)
@@ -0,0 +1,51 @@
+#ifndef PM_SHIFTS_H
+#define PM_SHIFTS_H
+
+#define PM_SHIFTS_TABLE_NAME "SHIFTS.TABLE" ///< Name for table on the analysis metadata
+#define PM_SHIFTS_KERNEL_NAME "SHIFTS.KERNEL" ///< Name for kernel on the analysis metadata
+
+/// Shifts due to orthogonal transfer
+typedef struct {
+    psVector *x;                        ///< Shifts in x
+    psVector *y;                        ///< Shifts in y
+    psVector *t;                        ///< Times of shifts
+    long num;                           ///< Number of values
+    bool tRelative;                     ///< Are the time values relative (durations)?
+    bool xyRelative;                    ///< Are the shift (x,y) values relative to the previous position?
+} pmShifts;
+
+/// Allocator for pmShifts
+pmShifts *pmShiftsAlloc(bool tRel,      ///< Are the time values relative (durations)?
+                        bool xyRel      ///< Are the shift (x,y) values relative to the previous position?
+                        );
+
+/// Read orthogonal transfer shifts table for a cell
+///
+/// Given a cell, this function searches for the orthogonal transfer shifts for this cell in the supplied FITS
+/// file.  The FITS extension containing the shifts table is specified by the SHIFTS keyword within the FILE
+/// information in the camera format.  If the extension is found, the shifts table is read and translated into
+/// kernels which are placed in the analysis metadata of each cell.  Note that this operation is performed on
+/// all cells within the FITS file (or at least, those contained within the shifts table), not just the cell
+/// provided --- if we have to read the whole table, we may as well translate the whole lot.  If a kernel is
+/// already present in the cell analysis metadata, the function returns true without doing any work.
+bool pmShiftsRead(const pmCell *cell,         ///< Cell for which to search for shifts
+                  psFits *fits          ///< FITS file in which to search for OT shifts extension
+                  );
+
+
+/// Generate a kernel for the cell from the orthogonal transfer shifts
+///
+/// The kernel is saved in the analysis metadata
+bool pmShiftsKernel(const pmCell *cell   ///< Cell for which to generate kernel
+                    );
+
+/// Convolve a detrend with the appropriate orthogonal transfer convolution kernel from a science exposure.
+///
+/// The kernel is generated (with pmShiftsKernel) if required.  The image and mask are convolved with the
+/// kernel (specified maskVal is smeared).  The weight map is not convolved.
+bool pmShiftsConvolve(pmReadout *detrend, ///< Detrend readout to convolve
+                      const pmCell *source, ///< Science exposure, containing a shifts kernel
+                      psMaskType maskVal ///< Mask value to smear
+                      );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShutterCorrection.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShutterCorrection.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShutterCorrection.c	(revision 22322)
@@ -0,0 +1,1167 @@
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <strings.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "psVectorBracket.h"
+#include "pmConceptsAverage.h"
+#include "pmReadoutStack.h"
+#include "pmDetrendThreads.h"
+
+#include "pmShutterCorrection.h"
+
+/// Measure shutter correction:
+///
+/// input  : collection of shutter correction exposures (pre-processed)
+/// output : a shutter correction image
+///
+/// The measurement could be performed on any focal-plane unit at a time. for GPC, the obvious scale is to
+/// measure the effect on the entire focal plane at once, with a single reference point in the field.  this is
+/// a little more complex than just measuring the effect for a single 2D image array.  the reference point and
+/// the detailed analysis points need to be defined for the entire hierarchy rather than just as coordinate
+/// pairs or regions.  a pmFPAview would be a natural element with which to define these points, but at the
+/// moment, the pmFPAview structure defines a band in the CCD, not a coordinate.  An option is to instead
+/// specify the reference locations as a pmFPAview coupled with a psRegion, though we need to be careful not
+/// to over-specify the pixels (ie, conflict between pmFPAview and psRegion).
+///
+/// At each point in an image with exposure time T, we measure f(k;T) = F(k;T) / F(0;T) where k is the
+/// coordinate of the point of interest, 0 is the reference coordinate, and F(k;T) is the measured number of
+/// counts at the point of interest in this image.  given a collection of f(k;T) values, we need to determine
+/// the model f(k;T) = A(k) (T + dTk) / (T + dTo) where dTk is the shutter error at the given position, dTo is
+/// the shutter error at the reference position, and A(k) is the scaling factor for the given position.
+///
+/// The process for generating a shutter correction is as follows:
+/// - for each image
+/// -- measure the reference point counts
+/// - for each analysis region:
+/// -- measure shutter parameters (dTo, dTk, A):
+/// --- for each image:
+/// ---- measure counts at the region
+/// ---- divide by the reference counts
+/// --- linear extrapolation to find f(inf) = A(k)
+/// --- linear extrapolation to find f(0) = A(k) dTk / dTo
+/// --- linear interpolation to find coordinate where f(dTo) = A (1 + dTk/dTo) / 2
+/// --- non-linear fit of T, f(T) to f(k;T) = A(k) (T + dTk) / (T + dTo)
+/// - use the collection of dTo values to choose a best value for dTo (median)
+/// - for each image pixel
+/// -- divide by the reference counts
+/// -- generate the vectors T, f(T)
+/// -- linear fit of T, f(T) to f(k;T) = A(k) (T + dTk) / (T + dTo) using dTo above
+/// -- save dTk, A(k) in output image pixels
+/// -- apply dTk, A(k) to measure residual images
+/// -- generate residual FITS/JPEG images
+
+
+#define MEASURE_SAMPLES 4               // Number of samples to make over the image.  This should only be
+                                        // changed with great caution, since assumptions on its value are in
+                                        // the code (see pmShutterCorrectionDataAlloc).
+
+
+static void pmShutterCorrectionFree(pmShutterCorrection *pars)
+{
+    // Nothing to free
+    return;
+}
+
+pmShutterCorrection *pmShutterCorrectionAlloc()
+{
+    pmShutterCorrection *corr = (pmShutterCorrection*)psAlloc(sizeof(pmShutterCorrection));
+    psMemSetDeallocator(corr, (psFreeFunc)pmShutterCorrectionFree);
+
+    corr->scale  = 0.0;
+    corr->offset = 0.0;
+    corr->offref = 0.0;
+    corr->num = 0;
+    corr->stdev = NAN;
+    corr->valid = true;
+
+    return corr;
+}
+
+pmShutterCorrection *pmShutterCorrectionGuess(const psVector *exptime, const psVector *counts)
+{
+    // NOTE: vectors must be sorted on input.  It is expensive to sort or check this here, but
+    // it is easy to arrange by sorting the images before generating these vectors.
+
+    PS_ASSERT_VECTOR_NON_NULL(exptime, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(counts, NULL);
+    PS_ASSERT_VECTOR_TYPE(exptime, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_TYPE(counts, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(exptime, counts, NULL);
+    if (exptime->n <= 2) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Require more than 2 exposures to guess shutter correction.\n");
+        return NULL;
+    }
+
+    long N = exptime->n;                // Number of exposures
+
+    // use interpolation to guess shutter correction parameters given a set of exposures times and normalized
+    // counts (divided by the reference counts for each image)
+
+    pmShutterCorrection *corr = pmShutterCorrectionAlloc(); // Shutter correction, to be returned
+    psPolynomial1D *line = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1); // Straight line, for extrapolation
+
+    // choose the highest exptime point as the guess for the scale:
+    // XXX we could examine the top 2 or 3 values and decide if we
+    // extended exptime enough or median clip.
+    corr->scale = counts->data.F32[N-1];
+
+    // fit a line to the lowest three points and extrapolate to 0.0
+    psVector *tmpX = psVectorAlloc(2, PS_TYPE_F32);
+    psVector *tmpY = psVectorAlloc(2, PS_TYPE_F32);
+
+    long index;
+
+    // Iterate only
+    for (index = 0; !isfinite(exptime->data.F32[index]) && index < N - 1; index++);
+
+    if (index == N - 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Not enough good values to guess shutter correction.\n");
+        goto GUESS_ERROR;
+    }
+    tmpX->data.F32[0] = exptime->data.F32[index];
+    tmpY->data.F32[0] = counts->data.F32[index];
+
+    for (index++;
+            (!isfinite(exptime->data.F32[index]) || exptime->data.F32[index] == exptime->data.F32[0]) &&
+            index < N; index++)
+        ; // Iterate only
+    if (index == N) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Exposure times are all identical --- cannot guess shutter correction.\n");
+        goto GUESS_ERROR;
+    }
+    tmpY->data.F32[1] = counts->data.F32[index];
+    tmpX->data.F32[1] = exptime->data.F32[index];
+
+    // fit a line and extrapolate the fit to 0.0
+    if (!psVectorFitPolynomial1D(line, NULL, 0, tmpY, NULL, tmpX)) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to fit for the time offset.\n");
+        goto GUESS_ERROR;
+    }
+    float ratio = psPolynomial1DEval(line, 0.0) / corr->scale;
+
+    // XXX we need a sanity check:
+    // if the mean value of the three points is higher than corr->scale,
+    // then the slope should be negative.
+    // if the mean value of the three points is lower than corr->scale,
+    // then the slope should be positive.
+
+    // find two points bracketing the value counts = A (1 + dTk/dTo) / 2 = corr->scale (1 + ratio) / 2
+    float value = corr->scale * (1 + ratio) / 2.0;
+
+    int Np;                             // Index of the value above (positive side)
+    if (ratio < 1.0) {
+        Np = psVectorBracket(counts, value, true);
+    } else {
+        Np = psVectorBracketDescend(counts, value, true);
+    }
+    int Nm = (Np == 0) ? 1 : Np - 1;    // Index of the value below (negative side)
+
+    tmpX->data.F32[0] = counts->data.F32[Nm];
+    tmpX->data.F32[1] = counts->data.F32[Np];
+    tmpY->data.F32[0] = exptime->data.F32[Nm];
+    tmpY->data.F32[1] = exptime->data.F32[Np];
+
+    // fit a line and extrapolate the fit to counts = A (1 + dTk/dTo) : exptime = dTo
+    if (!psVectorFitPolynomial1D (line, NULL, 0, tmpY, NULL, tmpX)) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to fit for the reference offset.\n");
+        goto GUESS_ERROR;
+    }
+    corr->offref = psPolynomial1DEval(line, value);
+    corr->offset = ratio * corr->offref;
+
+    psFree(line);
+    psFree(tmpX);
+    psFree(tmpY);
+
+    return corr;
+
+GUESS_ERROR:
+    psFree(tmpX);
+    psFree(tmpY);
+    psFree(line);
+    psFree(corr);
+    return NULL;
+}
+
+// linear fit to the counts and exptime, given a value for offref
+pmShutterCorrection *pmShutterCorrectionLinFit(const psVector *exptime, const psVector *counts,
+                                               const psVector *cntError, const psVector *mask, float offref,
+                                               int nIter, float rej, psMaskType maskVal)
+{
+    PS_ASSERT_VECTOR_NON_NULL(exptime, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(counts, NULL);
+    PS_ASSERT_VECTOR_TYPE(exptime, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_TYPE(counts, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(exptime, counts, NULL);
+    if (exptime->n <= 2) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Require more than 2 exposures to guess shutter correction.\n");
+        return NULL;
+    }
+    if (cntError) {
+        PS_ASSERT_VECTOR_TYPE(cntError, PS_TYPE_F32, NULL);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(counts, cntError, NULL);
+    }
+    PS_ASSERT_FLOAT_LARGER_THAN(offref, 0.0, NULL);
+
+    // this step is identical for all pixels: do it once and save?
+    psVector *x = psVectorAlloc(exptime->n, PS_TYPE_F32);
+    psVector *y = psVectorAlloc(exptime->n, PS_TYPE_F32);
+
+    for (long i = 0; i < exptime->n; i++) {
+        // Should be safe (if expensive) to stick NaNs in --- the fitter deals with them
+        float value = 1.0 / (exptime->data.F32[i] + offref);
+        x->data.F32[i] = exptime->data.F32[i] * value;
+        y->data.F32[i] = value;
+    }
+
+    psPolynomial2D *line = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, 1, 1);
+
+    // mask out the terms we will not fit
+    line->coeffMask[0][0] = PS_POLY_MASK_SET;
+    line->coeffMask[1][1] = PS_POLY_MASK_SET;
+    line->coeff[0][0] = 0;
+    line->coeff[1][1] = 0;
+
+    // the stats structure determines how the clipping statistic is measured
+    // too few points to use the robust analysis method
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+    stats->clipSigma = rej;
+    stats->clipIter = nIter;
+
+    if (!psVectorClipFitPolynomial2D(line, stats, mask, maskVal, counts, cntError, x, y)) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to fit shutter correction.\n");
+        psFree(stats);
+        psFree(x);
+        psFree(y);
+        psFree(line);
+        return NULL;
+    }
+
+    pmShutterCorrection *corr = pmShutterCorrectionAlloc();
+    corr->offref = offref;
+    corr->scale  = line->coeff[1][0];
+    corr->offset = line->coeff[0][1] / line->coeff[1][0];
+    corr->num = stats->clippedNvalues;
+    corr->stdev = stats->clippedStdev;
+
+    psFree(stats);
+    psFree(x);
+    psFree(y);
+    psFree(line);
+
+    return corr;
+}
+
+static psF32 pmShutterCorrectionModel(psVector *deriv, const psVector *params, const psVector *x)
+{
+    // This is in a tight loop, so we won't assert here.
+
+    psF32 A = params->data.F32[0];
+    psF32 p = x->data.F32[0] + params->data.F32[1];
+    psF32 q = 1.0 / (x->data.F32[0] + params->data.F32[2]);
+    psF32 f = A * p * q;
+
+    if (deriv) {
+        deriv->data.F32[0] = p * q;
+        deriv->data.F32[1] = A * q;
+        deriv->data.F32[2] = - f * q;
+    }
+    return f;
+}
+
+// non-linear fit to the counts and exptime, given a guess for the three parameters
+pmShutterCorrection *pmShutterCorrectionFullFit(const psVector *exptime, const psVector *counts,
+                                                const psVector *cntError, const pmShutterCorrection *guess)
+{
+    PS_ASSERT_VECTOR_NON_NULL(exptime, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(counts, NULL);
+    PS_ASSERT_VECTOR_TYPE(exptime, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_TYPE(counts, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(exptime, counts, NULL);
+    if (exptime->n <= 2) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Require more than 2 exposures to guess shutter correction.\n");
+        return NULL;
+    }
+    if (cntError) {
+        PS_ASSERT_VECTOR_TYPE(cntError, PS_TYPE_F32, NULL);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(counts, cntError, NULL);
+    }
+    PS_ASSERT_PTR_NON_NULL(guess, NULL);
+
+    psMinimization *minInfo = psMinimizationAlloc(15, 0.1); // Minimization information
+
+    psVector *params = psVectorAlloc (3, PS_TYPE_F32); // Fitting parameters
+    params->data.F32[0] = guess->scale;
+    params->data.F32[1] = guess->offset;
+    params->data.F32[2] = guess->offref;
+
+    // XXX for the moment, don't set any constraints
+    // psMinConstraint *constraint = psMinConstraintAlloc();
+    // constrain->checkLimits = pmShutterParamLimits;
+    // constrain->paramMask   = NULL;
+    psMinConstraint *constraint = NULL;   // Constraints on the minimization
+
+    // XXX ignore covariance matrix for the moment
+    // psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F64);
+    psImage *covar = NULL;              // Covariance matrix
+
+    // construct the coordinate and value entries (y is counts)
+    psArray *x = psArrayAlloc(exptime->n); // Coordinates
+
+    for (long i = 0; i < exptime->n; i++) {
+        psVector *coord = psVectorAlloc(1, PS_TYPE_F32);
+        coord->data.F32[0] = exptime->data.F32[i];
+        x->data[i] = coord;
+    }
+
+    if (!psMinimizeLMChi2(minInfo, covar, params, constraint, x, counts, cntError, pmShutterCorrectionModel)) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to fit for shutter correction.\n");
+        psFree(x);
+        psFree(minInfo);
+        psFree(params);
+        return NULL;
+    }
+
+    pmShutterCorrection *corr = pmShutterCorrectionAlloc(); // Shutter correction
+    corr->scale  = params->data.F32[0];
+    corr->offset = params->data.F32[1];
+    corr->offref = params->data.F32[2];
+
+    // apply the correction and measure the residual scatter
+    psVector *resid = psVectorAlloc (exptime->n, PS_TYPE_F32);
+    for (int i = 0; i < exptime->n; i++) {
+        float fitCounts = corr->scale * (exptime->data.F32[i] + corr->offset) / (exptime->data.F32[i] + corr->offref);
+        resid->data.F32[i] = counts->data.F32[i] - fitCounts;
+    }
+
+    psStats *rawStats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+    psStats *resStats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+    psVectorStats (rawStats, counts, NULL, NULL, 0);
+    psVectorStats (resStats, resid, NULL, NULL, 0);
+
+    // XXX temporary hard-wired minimum stdev improvement factor
+    psTrace("psModules.detrend", 3, "raw scatter %f vs res scatter %f\n", rawStats->sampleStdev, resStats->sampleStdev);
+    if (rawStats->sampleStdev / resStats->sampleStdev < 1.5) corr->valid = false;
+
+    psFree (rawStats);
+    psFree (resStats);
+    psFree (resid);
+
+    psFree(minInfo);
+    psFree(params);
+    psFree(x);
+
+    return corr;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmShutterCorrectionMeasure(pmReadout *output, const psArray *readouts, int size, psStatsOptions meanStat,
+                                psStatsOptions stdevStat, int nIter, float rej, psMaskType maskVal)
+{
+    PS_ASSERT_ARRAY_NON_NULL(readouts, NULL);
+    PS_ASSERT_ARRAY_NON_EMPTY(readouts, NULL);
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+
+    long num = readouts->n;             // Number of readouts
+    PS_ASSERT_INT_POSITIVE(nIter, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, NULL);
+
+    psArray *images = psArrayAlloc(num);// Array of images
+    psArray *masks = NULL; // Array of masks
+    psArray *weights = NULL; // Array of weights
+    psVector *exptimes = psVectorAlloc(num, PS_TYPE_F32); // Vector of exposure times
+
+    {
+        pmReadout *readout = readouts->data[0]; // Representative readout
+        if (readout->mask)
+        {
+            masks = psArrayAlloc(num);
+        }
+        if (readout->weight)
+        {
+            weights = psArrayAlloc(num);
+        }
+    }
+
+    // Check input sizes, generate first-pass statistics
+    psRegion refRegion;                 // Reference region
+    psVector *refs = psVectorAlloc(num, PS_TYPE_F32); // Reference measurements
+    psVectorInit(refs, 0);
+    psArray *regions = psArrayAlloc(MEASURE_SAMPLES); // Array of sample regions, made on each image
+    psImage *samplesMean = psImageAlloc(num, MEASURE_SAMPLES, PS_TYPE_F32); // Measurements for each file
+    psImage *samplesStdev = psImageAlloc(num, MEASURE_SAMPLES, PS_TYPE_F32); // Errors for each file
+    psStats *stats = psStatsAlloc(meanStat | stdevStat);
+    int numRows = 0, numCols = 0; // Size of images
+    for (long i = 0; i < images->n; i++) {
+        pmReadout *readout = readouts->data[i]; // Readout of interest
+        if (!readout) {
+            continue;
+        }
+
+        bool mdok;                      // Status of MD lookup
+        float exptime = psMetadataLookupF32(&mdok, readout->parent->concepts, "CELL.EXPOSURE"); // Exp. time
+        if (!mdok || !isfinite(exptime)) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure time for readout %ld is not set.\n", i);
+            goto MEASURE_ERROR;
+        }
+        exptimes->data.F32[i] = exptime;
+
+        psImage *image = readout->image; // Image of interest
+        if (!image) {
+            continue;
+        }
+        images->data[i] = psMemIncrRefCounter(image);
+        if (image->type.type != PS_TYPE_F32) {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Bad type for image: %x\n", image->type.type);
+            goto MEASURE_ERROR;
+        }
+        if (numRows == 0 || numCols == 0) {
+            numRows = image->numRows;
+            numCols = image->numCols;
+            // define the reference region : a box of size 'size' at the center
+            refRegion = psRegionForSquare(0.5 * numCols, 0.5 * numRows, size);
+            // Set up the sample regions : boxes of size 'size' at the 4 image corners
+            for (int j = 0; j < MEASURE_SAMPLES; j++) {
+                int x = (j % 2) ? size : image->numCols - size;
+                int y = (j > 1) ? size : image->numRows - size;
+                psRegion region = psRegionForSquare(x, y, size);
+                region = psRegionForImage(image, region);
+                regions->data[j] = psRegionAlloc(region.x0, region.x1, region.y0, region.y1);
+            }
+        } else if (numRows != image->numRows || numCols != image->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image sizes don't match: %dx%d vs %dx%d\n", image->numCols, image->numRows,
+                    numCols, numRows);
+            goto MEASURE_ERROR;
+        }
+        psImage *mask = readout->mask; // Mask of interest
+        if (mask) {
+            if (!masks) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Not all readouts have masks.\n");
+                goto MEASURE_ERROR;
+            }
+            masks->data[i] = psMemIncrRefCounter(mask);
+
+            if (mask->type.type != PS_TYPE_U8) {
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Bad type for mask: %x\n", mask->type.type);
+                goto MEASURE_ERROR;
+            }
+            if (mask->numRows != numRows || mask->numCols != numCols) {
+                psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                        "Mask sizes don't match: %dx%d vs %dx%d\n", mask->numCols, mask->numRows,
+                        numCols, numRows);
+                goto MEASURE_ERROR;
+            }
+        } else if (masks) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Not all readouts have masks.\n");
+            goto MEASURE_ERROR;
+        }
+
+        psImage *weight = readout->weight; // Weight map of interest
+        if (weight) {
+            if (!weights) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Not all readouts have weights.\n");
+                goto MEASURE_ERROR;
+            }
+            weights->data[i] = psMemIncrRefCounter(weight);
+
+            if (weight->type.type != PS_TYPE_F32) {
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Bad type for weights: %x\n", weight->type.type);
+                goto MEASURE_ERROR;
+            }
+            if (weight->numRows != numRows || weight->numCols != numCols) {
+                psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                        "Weight sizes don't match: %dx%d vs %dx%d\n", weight->numCols, weight->numRows,
+                        numCols, numRows);
+                goto MEASURE_ERROR;
+            }
+        } else if (weights) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Not all readouts have weights.\n");
+            goto MEASURE_ERROR;
+        }
+
+
+        // Measure statistics
+        if (!psImageStats(stats, image, mask, maskVal)) {
+            psWarning("Unable to measure reference statistics.\n");
+        }
+        refs->data.F32[i] = psStatsGetValue(stats, meanStat);
+        psTrace("psModules.detrend", 3, "Reference value for image %ld = %f\n", i, refs->data.F32[i]);
+        if (refs->data.F32[i] <= 0.0) {
+            psError(PS_ERR_UNKNOWN, true, "Measured non-positive reference value.\n");
+            goto MEASURE_ERROR;
+        }
+        refs->data.F32[i] = 1.0 / refs->data.F32[i];
+        for (int j = 0; j < MEASURE_SAMPLES; j++) {
+            psRegion *region = regions->data[j]; // Region of interest
+            psImage *subImage = psImageSubset(image, *region); // Sub-image
+            psImage *subMask = NULL;
+            if (mask) {
+                subMask = psImageSubset(mask, *region);
+            }
+            if (!psImageStats(stats, subImage, subMask, maskVal)) {
+                psString regionString = psRegionToString(*region);
+                psWarning("Unable to measure sample statistics at %s in image %ld.\n",
+                          regionString, i);
+                psFree(regionString);
+            }
+            psFree(subImage);
+            samplesMean->data.F32[j][i] = psStatsGetValue(stats, meanStat) * refs->data.F32[i];
+            samplesStdev->data.F32[j][i] = psStatsGetValue(stats, stdevStat) * refs->data.F32[i];
+            psTrace("psModules.detrend", 5, "Image %ld, sample %d: %f +/- %f\n", i, j,
+                    samplesMean->data.F32[j][i], samplesStdev->data.F32[j][i]);
+        }
+    }
+    psFree(regions);
+    psFree(stats);
+
+    float meanRef = 0.0;                // Mean reference offset
+    int numGood = 0;                    // Number of good measurements
+    psVector *counts = psVectorAlloc(num, PS_TYPE_F32); // Mean for each image
+    psVector *errors = psVectorAlloc(num, PS_TYPE_F32); // Stdev for each image
+    for (int i = 0; i < MEASURE_SAMPLES; i++) {
+        counts = psImageRow(counts, samplesMean, i);
+        errors = psImageRow(errors, samplesStdev, i);
+        pmShutterCorrection *guess = pmShutterCorrectionGuess(exptimes, counts); // Guess at correction
+        pmShutterCorrection *corr = pmShutterCorrectionFullFit(exptimes, counts, errors, guess); // Correct'n
+        if (!corr) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to measure shutter reference correction.\n");
+            psFree(guess);
+            psFree(counts);
+            psFree(errors);
+            goto MEASURE_ERROR;
+        }
+        psTrace("psModules.detrend", 5, "Sample reference value: %f\n", corr->offref);
+        if (isfinite(corr->offref)) {
+            meanRef += corr->offref;
+            numGood++;
+        }
+        psFree(corr);
+        psFree(guess);
+    }
+    psFree(samplesMean);
+    psFree(samplesStdev);
+
+    if (numGood == 0) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to measure mean reference offset.\n");
+        psFree(counts);
+        psFree(errors);
+        goto MEASURE_ERROR;
+    }
+    meanRef /= (float)numGood;
+    psTrace("psModules.detrend", 3, "Mean reference value: %f\n", meanRef);
+
+    // Check the weights
+    if (weights && nIter > 1) {
+        for (int i = 0; i < weights->n && nIter > 1; i++) {
+            psImage *weight = weights->data[i]; // Weight image
+            if (!weight) {
+                // We don't have weights, so no realistic errors: turn off iteration
+                if (nIter > 0) {
+                    psWarning("Not all images have weights --- turning iteration off.\n");
+                }
+                nIter = 1;
+            }
+        }
+    }
+
+    psImage *shutter = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Shutter correction image
+    psImage *pattern = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Illumination pattern
+    psVector *mask = psVectorAlloc(num, PS_TYPE_U8); // Mask for each image
+    psVectorInit(mask, 0);
+    psTrace("psModules.detrend", 2, "Performing linear fit on individual pixels...\n");
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            for (int i = 0; i < num; i++) {
+                psImage *image = images->data[i]; // Image of interest
+                counts->data.F32[i] = image->data.F32[y][x] * refs->data.F32[i];
+                psImage *maskImage;     // Mask image
+                if (masks && (maskImage = masks->data[i])) {
+                    mask->data.U8[i] = maskImage->data.U8[y][x];
+                }
+                psImage *weight;        // Weight image
+                if (weights && (weight = weights->data[i])) {
+                    errors->data.F32[i] = sqrtf(weight->data.F32[y][x]) * refs->data.F32[i];
+                } else {
+                    errors->data.F32[i] = sqrtf(image->data.F32[y][x]) * refs->data.F32[i];
+                }
+            }
+
+            pmShutterCorrection *corr = pmShutterCorrectionLinFit(exptimes, counts, errors, mask, meanRef,
+                                        nIter, rej, maskVal);
+            shutter->data.F32[y][x] = corr->offset;
+            pattern->data.F32[y][x] = corr->scale;
+            psFree(corr);
+        }
+    }
+    psFree(mask);
+    psFree(counts);
+    psFree(errors);
+    psFree(refs);
+
+    if (psTraceGetLevel("psModules.detrend") > 5) {
+        psFits *fits = psFitsOpen("pattern.fits", "w");
+        psFitsWriteImage(fits, NULL, pattern, 0, NULL);
+        psFitsClose(fits);
+    }
+    psFree(pattern);
+
+    output->image = shutter;
+
+    // Update the "concepts"
+    psList *inputCells = psListAlloc(NULL); // List of cells
+    for (long i = 0; i < readouts->n; i++) {
+        pmReadout *readout = readouts->data[i]; // Readout of interest
+        psListAdd(inputCells, PS_LIST_TAIL, readout->parent);
+    }
+    bool success = pmConceptsAverageCells(output->parent, inputCells, NULL, NULL, true);
+    psFree(inputCells);
+
+    // Correct the exposure times --- they don't make sense any more.
+    psMetadataItem *item = psMetadataLookup(output->parent->concepts, "CELL.EXPOSURE");
+    item->data.F32 = NAN;
+    item = psMetadataLookup(output->parent->concepts, "CELL.DARKTIME");
+    item->data.F32 = NAN;
+
+    return success;
+
+
+MEASURE_ERROR:
+    // Clean up after error
+    psFree(exptimes);
+    psFree(images);
+    psFree(masks);
+    psFree(weights);
+    psFree(refs);
+    psFree(regions);
+    psFree(stats);
+    psFree(samplesMean);
+    psFree(samplesStdev);
+    return false;
+}
+
+
+bool pmShutterCorrectionApplyScan_Threaded(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    psImage *image        = job->args->data[0];
+    const psImage *shutterImage = job->args->data[1];
+    psImage *mask         = job->args->data[2];
+
+    float exptime    = PS_SCALAR_VALUE(job->args->data[3],F32);
+    psMaskType blank = PS_SCALAR_VALUE(job->args->data[4],U8);
+    int rowStart     = PS_SCALAR_VALUE(job->args->data[5],S32);
+    int rowStop      = PS_SCALAR_VALUE(job->args->data[6],S32);
+    return pmShutterCorrectionApplyScan (image, shutterImage, mask, exptime, blank, rowStart, rowStop);
+}
+
+bool pmShutterCorrectionApplyScan(psImage *image, const psImage *shutterImage, psImage *mask, float exptime,
+                                  psMaskType blank, int rowStart, int rowStop)
+{
+    for (int y = rowStart; y < rowStop; y++) {
+        for (int x = 0; x < image->numCols; x++) {
+            if (mask && !isfinite(shutterImage->data.F32[y][x])) {
+                mask->data.PS_TYPE_MASK_DATA[y][x] |= blank;
+                image->data.F32[y][x] = NAN;
+                continue;
+            }
+            image->data.F32[y][x] *= exptime / (exptime + shutterImage->data.F32[y][x]);
+        }
+    }
+    return true;
+}
+
+bool pmShutterCorrectionApply(pmReadout *readout, const pmReadout *shutter, psMaskType blank)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(shutter, false);
+    PS_ASSERT_IMAGE_NON_NULL(readout->image, false);
+    PS_ASSERT_IMAGE_NON_NULL(shutter->image, false);
+    PS_ASSERT_IMAGE_TYPE(readout->image, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGE_TYPE(shutter->image, PS_TYPE_F32, false);
+
+    psRegion region = psRegionSet(readout->col0, readout->col0 + readout->image->numCols,
+                                  readout->row0, readout->row0 + readout->image->numRows); // Detector region
+
+    pmCell *cell = readout->parent;     // Parent cell
+    if (!cell) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                "Parent cell is NULL --- unable to determine exposure time.\n");
+        return false;
+    }
+    float exptime = psMetadataLookupF32(NULL, cell->concepts, "CELL.EXPOSURE"); // Exposure time
+    if (!isfinite(exptime)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Bad exposure time: %f.\n", exptime);
+        return false;
+    }
+
+    pmHDU *hdu = pmHDUFromCell(cell);// HDU  of interest
+
+    psVector *md5 = psImageMD5(shutter->image); // md5 hash
+    psString md5string = psMD5toString(md5); // String
+    psFree(md5);
+    psStringPrepend(&md5string, "Shutter image MD5: ");
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, md5string, "");
+    psFree(md5string);
+
+
+    psImage *shutterImage = psImageSubset(shutter->image, region); // Subimage with shutter
+    if (!shutterImage) {
+        psString regionString = psRegionToString(region);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Size mismatch: %s vs %dx%d\n",
+                regionString, shutter->image->numCols, shutter->image->numRows);
+        psFree(regionString);
+        psFree(shutterImage);
+        return false;
+    }
+    psImage *image = readout->image;    // Image to correct
+    psImage *mask = readout->mask;      // Corresponding mask
+
+    bool threaded = true;
+    int scanRows = pmDetrendGetScanRows();
+    if (scanRows == 0) {
+        threaded = false;
+        scanRows = image->numRows;
+    }
+
+    if (exptime <= 0.0) {
+        // In the extreme case that we have exptime <= 0.0, we correct the image to
+        // counts-per-second, rather than counts in the nominal exposure time
+        for (int y = 0; y < image->numRows; y++) {
+            for (int x = 0; x < image->numCols; x++) {
+                if (mask && !isfinite(shutterImage->data.F32[y][x])) {
+                    mask->data.PS_TYPE_MASK_DATA[y][x] |= blank;
+                    image->data.F32[y][x] = NAN;
+                    continue;
+                }
+                image->data.F32[y][x] *= 1.0 / (exptime + shutterImage->data.F32[y][x]);
+            }
+        }
+        psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.EXPOSURE", PS_META_REPLACE,
+                         "exposure time re-normalized to 1.0", 1.0); // Exposure time
+        psString line = NULL;
+        psStringAppend(&line, "extreme exposure time %f, re-normalized to 1.0", exptime);
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, line, "");
+        psFree(line);
+    } else {
+        for (int rowStart = 0; rowStart < image->numRows; rowStart += scanRows) {
+            int rowStop = PS_MIN (rowStart + scanRows, image->numRows);
+
+            if (threaded) {
+                // allocate a job, construct the arguments for this job
+                psThreadJob *job = psThreadJobAlloc("PSMODULES_DETREND_SHUTTER");
+                psArrayAdd(job->args, 1, image);
+                psArrayAdd(job->args, 1, shutterImage);
+                psArrayAdd(job->args, 1, mask);
+                PS_ARRAY_ADD_SCALAR(job->args, exptime, PS_TYPE_F32);
+                PS_ARRAY_ADD_SCALAR(job->args, blank, PS_TYPE_MASK);
+                PS_ARRAY_ADD_SCALAR(job->args, rowStart, PS_TYPE_S32);
+                PS_ARRAY_ADD_SCALAR(job->args, rowStop, PS_TYPE_S32);
+
+                if (!psThreadJobAddPending(job)) {
+                    psFree(job);
+                    return false;
+                }
+                psFree(job);
+            } else if (!pmShutterCorrectionApplyScan(image, shutterImage, mask, exptime, blank,
+                                                     rowStart, rowStop)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to apply shutter correction.");
+                psFree(shutterImage);
+                return false;
+            }
+        }
+        if (threaded) {
+            // wait here for the threaded jobs to finish
+            if (!psThreadPoolWait(true)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to apply shutter correction.");
+                psFree(shutterImage);
+                return false;
+            }
+        }
+    }
+    psFree(shutterImage);
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now, used for reporting
+    psString timeString = psTimeToISO(time); // String with time
+    psFree(time);
+    psStringPrepend(&timeString, "Shutter correction completed at ");
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                     timeString, "");
+    psFree(timeString);
+
+
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+#define IMAGES_BUFFER 10                // Allocate space for this many images at a time
+
+static void shutterCorrectionDataFree(pmShutterCorrectionData *data)
+{
+    psFree(data->regions);
+    psFree(data->mean);
+    psFree(data->stdev);
+
+    psFree(data->exptimes);
+    psFree(data->refs);
+
+    return;
+}
+
+pmShutterCorrectionData *pmShutterCorrectionDataAlloc(int numCols, int numRows, int size)
+{
+    pmShutterCorrectionData *data = psAlloc(sizeof(pmShutterCorrectionData));
+    psMemSetDeallocator(data, (psFreeFunc)shutterCorrectionDataFree);
+
+    data->num = 0;
+    data->numCols = 0;
+    data->numRows = 0;
+
+    data->regions = psArrayAlloc(MEASURE_SAMPLES);
+    for (int j = 0; j < MEASURE_SAMPLES; j++) {
+        int x = (j % 2) ? size : numCols - size - 1;
+        int y = (j > 1) ? size : numRows - size - 1;
+        psRegion region = psRegionForSquare(x, y, size);
+        data->regions->data[j] = psRegionAlloc(region.x0, region.x1, region.y0, region.y1);
+    }
+
+    data->mean = psArrayAlloc(MEASURE_SAMPLES);
+    data->stdev = psArrayAlloc(MEASURE_SAMPLES);
+    for (int i = 0; i < MEASURE_SAMPLES; i++) {
+        data->mean->data[i] = psVectorAllocEmpty(IMAGES_BUFFER, PS_TYPE_F32);
+        data->stdev->data[i] = psVectorAllocEmpty(IMAGES_BUFFER, PS_TYPE_F32);
+    }
+
+    data->exptimes = psVectorAllocEmpty(IMAGES_BUFFER, PS_TYPE_F32);
+    data->refs = psVectorAllocEmpty(IMAGES_BUFFER, PS_TYPE_F32);
+
+    return data;
+}
+
+bool pmShutterCorrectionAddReadout(pmShutterCorrectionData *data,
+                                   const pmReadout *readout, ///< Readout to add
+                                   psStatsOptions meanStat, ///< Statistic to use for mean
+                                   psStatsOptions stdevStat, ///< Statistic to use for stdev
+                                   psMaskType maskVal, ///< Mask value
+                                   psRandom *rng ///< Random number generator
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(data, NULL);
+    PS_ASSERT_PTR_NON_NULL(readout, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(readout->image, NULL);
+    PS_ASSERT_IMAGE_TYPE(readout->image, PS_TYPE_F32, NULL);
+    if (data->num == 0) {
+        data->numCols = readout->image->numCols;
+        data->numRows = readout->image->numRows;
+    } else {
+        PS_ASSERT_IMAGE_SIZE(readout->image, data->numCols, data->numRows, NULL);
+    }
+    if (readout->mask) {
+        PS_ASSERT_IMAGE_NON_NULL(readout->mask, NULL);
+        PS_ASSERT_IMAGE_TYPE(readout->mask, PS_TYPE_MASK, NULL);
+        PS_ASSERT_IMAGE_SIZE(readout->mask, data->numCols, data->numRows, NULL);
+    }
+    if (readout->weight) {
+        PS_ASSERT_IMAGE_NON_NULL(readout->weight, NULL);
+        PS_ASSERT_IMAGE_TYPE(readout->weight, PS_TYPE_F32, NULL);
+        PS_ASSERT_IMAGE_SIZE(readout->weight, data->numCols, data->numRows, NULL);
+    }
+
+    // Add the exposure time
+    float exptime = psMetadataLookupF32(NULL, readout->parent->concepts, "CELL.EXPOSURE"); // Exp. time
+    if (!isfinite(exptime)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure time is not set.");
+        return false;
+    }
+    data->exptimes->data.F32[data->exptimes->n] = exptime;
+    data->exptimes = psVectorExtend(data->exptimes, IMAGES_BUFFER, 1);
+
+    // Add the statistics
+
+    // Add the reference value
+    psStats *stats = psStatsAlloc(meanStat | stdevStat); // Statistics to apply
+    if (!rng) {
+        rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    } else {
+        psMemIncrRefCounter(rng);
+    }
+    if (!psImageBackground(stats, NULL, readout->image, readout->mask, maskVal, rng)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to measure reference statistics.\n");
+        psFree(stats);
+        psFree(rng);
+        return false;
+    }
+    psFree(rng);
+    float refValue = psStatsGetValue(stats, meanStat); // Reference value
+    psTrace("psModules.detrend", 3, "Reference value & exptime for shutter image : %f cnts %f sec\n", refValue, exptime);
+    if (refValue <= 0.0) {
+        psError(PS_ERR_UNKNOWN, true, "Measured non-positive reference value.\n");
+        psFree(stats);
+        return false;
+    }
+    refValue = 1.0 / refValue;
+    data->refs->data.F32[data->refs->n] = refValue;
+    data->refs = psVectorExtend(data->refs, IMAGES_BUFFER, 1);
+
+    // Add the region statistics
+    for (int j = 0; j < MEASURE_SAMPLES; j++) {
+        psRegion *region = data->regions->data[j]; // Region of interest
+        psRegion adjusted = *region;    // Adjusted region, compensating for offsets
+        adjusted.x0 += readout->image->col0;
+        adjusted.x1 += readout->image->col0;
+        adjusted.y0 += readout->image->row0;
+        adjusted.y1 += readout->image->row0;
+        psImage *subImage = psImageSubset(readout->image, adjusted); // Sub-image
+        psImage *subMask = NULL;        // Sub-image of mask
+        if (readout->mask) {
+            subMask = psImageSubset(readout->mask, adjusted);
+        }
+        if (!psImageStats(stats, subImage, subMask, maskVal)) {
+            psString regionString = psRegionToString(adjusted);
+            psWarning("Unable to measure sample statistics at %s in image.\n",
+                      regionString);
+            psFree(regionString);
+        }
+        psFree(subImage);
+        psFree(subMask);
+
+        psVector *mean = data->mean->data[j]; // Vector of means for this region
+        psVector *stdev = data->stdev->data[j]; // Vector of standard deviations for this region
+
+        mean->data.F32[mean->n] = psStatsGetValue(stats, meanStat) * refValue;
+        stdev->data.F32[stdev->n] = psStatsGetValue(stats, stdevStat) * refValue;
+
+        psTrace("psModules.detrend", 5, "input shutter image sample value %d: %f +/- %f  ->  %f +/- %f\n", j,
+                psStatsGetValue(stats, meanStat), psStatsGetValue(stats, stdevStat),
+                mean->data.F32[mean->n], stdev->data.F32[stdev->n]);
+
+        data->mean->data[j] = psVectorExtend(mean, IMAGES_BUFFER, 1);
+        data->stdev->data[j] = psVectorExtend(stdev, IMAGES_BUFFER, 1);
+    }
+
+    data->num++;
+
+    return true;
+}
+
+float pmShutterCorrectionReference(pmShutterCorrectionData *data)
+{
+    PS_ASSERT_PTR_NON_NULL(data, NAN);
+    PS_ASSERT_INT_POSITIVE(data->num, NAN);
+
+    // supply counts sorted by exptime
+
+    // generate the index for the exptimes vector
+    psVector *index = psVectorSortIndex (NULL, data->exptimes);
+    psVector *newtimes = psVectorAlloc (data->exptimes->n, PS_TYPE_F32);
+
+    // reshuffle exptimes to new sequence (this is only a local value)
+    for (int j = 0; j < newtimes->n; j++) {
+        newtimes->data.F32[j] = data->exptimes->data.F32[index->data.S32[j]];
+    }
+
+    float meanRef = 0.0;                // Mean reference offset
+    int numGood = 0;                    // Number of good measurements
+    for (int i = 0; i < MEASURE_SAMPLES; i++) {
+        psVector *newcounts = psVectorAlloc (data->exptimes->n, PS_TYPE_F32);
+        psVector *newerrors = psVectorAlloc (data->exptimes->n, PS_TYPE_F32);
+        psVector *counts = data->mean->data[i];
+        psVector *errors = data->stdev->data[i];
+
+        for (int j = 0; j < newcounts->n; j++) {
+            newcounts->data.F32[j] = counts->data.F32[index->data.S32[j]];
+            newerrors->data.F32[j] = errors->data.F32[index->data.S32[j]];
+        }
+
+        // use the sorted exptime, counts, and errors for the measurements
+        pmShutterCorrection *guess = pmShutterCorrectionGuess(newtimes, newcounts); // Guess at correction
+        psTrace("psModules.detrend", 5, "Shutter correction guess: scale: %f, offset: %f, offref: %f\n", guess->scale, guess->offset, guess->offref);
+
+        pmShutterCorrection *corr = pmShutterCorrectionFullFit(newtimes, newcounts, newerrors, guess); // The actual correction
+        psTrace("psModules.detrend", 5, "Shutter correction fit: scale: %f, offset: %f, offref: %f\n", corr->scale, corr->offset, corr->offref);
+
+        if (corr && isfinite(corr->offref) && corr->valid) {
+            psTrace("psModules.detrend", 5, "Sample reference value: %f\n", corr->offref);
+            meanRef += corr->offref;
+            numGood++;
+        }
+
+        psFree(corr);
+        psFree(guess);
+        psFree (newcounts);
+        psFree (newerrors);
+    }
+    psFree (newtimes);
+    psFree (index);
+
+    if (numGood == 0) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to measure mean reference offset.\n");
+        return false;
+    }
+    meanRef /= (float)numGood;
+    psTrace("psModules.detrend", 3, "Mean reference value: %f\n", meanRef);
+    return meanRef;
+}
+
+bool pmShutterCorrectionGeneratePrepare(pmReadout *shutter, pmReadout *pattern, const psArray *inputs,
+                                        psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(shutter, false);
+    PS_ASSERT_PTR_NON_NULL(pattern, false);
+    PS_ASSERT_ARRAY_NON_NULL(inputs, false);
+
+    // determine the output image size based on the input images
+    int row0, col0, numCols, numRows;
+    if (!pmReadoutStackSetOutputSize(&col0, &row0, &numCols, &numRows, inputs)) {
+        psError(PS_ERR_UNKNOWN, false, "problem setting output readout size.");
+        return false;
+    }
+
+    // generate the required output image based on the specified sizes
+    pmReadoutStackDefineOutput(shutter, col0, row0, numCols, numRows, false, false, maskVal);
+    if (pattern) {
+        pmReadoutStackDefineOutput(pattern, col0, row0, numCols, numRows, false, false, maskVal);
+    }
+
+    psImage *nums = pmReadoutSetAnalysisImage(shutter, PM_READOUT_STACK_ANALYSIS_COUNT, numCols, numRows,
+                                              PS_TYPE_U16, 0); // Image with number fitted per pixel
+    if (!nums) {
+        return false;
+    }
+    psImage *sigma = pmReadoutSetAnalysisImage(shutter, PM_READOUT_STACK_ANALYSIS_SIGMA, numCols, numRows,
+                                               PS_TYPE_F32, NAN); // Image with stdev per pixel
+    if (!sigma) {
+        return false;
+    }
+
+    // Update the "concepts"
+    psList *inputCells = psListAlloc(NULL); // List of cells
+    for (long i = 0; i < inputs->n; i++) {
+        pmReadout *readout = inputs->data[i]; // Readout of interest
+        psListAdd(inputCells, PS_LIST_TAIL, readout->parent);
+    }
+    bool success = pmConceptsAverageCells(shutter->parent, inputCells, NULL, NULL, true);
+    psFree(inputCells);
+
+    // Correct the exposure times --- they don't make sense any more.
+    psMetadataItem *item = psMetadataLookup(shutter->parent->concepts, "CELL.EXPOSURE");
+    item->data.F32 = NAN;
+    item = psMetadataLookup(shutter->parent->concepts, "CELL.DARKTIME");
+    item->data.F32 = NAN;
+
+    shutter->data_exists = true;
+    shutter->parent->data_exists = true;
+    shutter->parent->parent->data_exists = true;
+
+    pattern->data_exists = true;
+    if (pattern->parent) {
+        pattern->parent->data_exists = true;
+        if (pattern->parent->parent) {
+            pattern->parent->parent->data_exists = true;
+        }
+    }
+
+    return success;
+}
+
+bool pmShutterCorrectionGenerate(pmReadout *shutter, pmReadout *pattern, const psArray *inputs,
+                                 float reference, const pmShutterCorrectionData *data,
+                                 int nIter, float rej, psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(shutter, false);
+    PS_ASSERT_PTR_NON_NULL(pattern, false);
+    PS_ASSERT_ARRAY_NON_NULL(inputs, false);
+    PS_ASSERT_INT_EQUAL(data->num, inputs->n, false);
+    PS_ASSERT_INT_NONNEGATIVE(nIter, false);
+    PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
+
+    int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine
+    int xSize, ySize;                   // Size of the output image
+    if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize,
+                                inputs)) {
+        psError(PS_ERR_UNKNOWN, false, "No valid input readouts.");
+        return false;
+    }
+
+    psImage *nums = pmReadoutGetAnalysisImage(shutter, PM_READOUT_STACK_ANALYSIS_COUNT);
+    if (!nums) {
+        return false;
+    }
+    psImage *sigma = pmReadoutGetAnalysisImage(shutter, PM_READOUT_STACK_ANALYSIS_SIGMA);
+    if (!sigma) {
+        return false;
+    }
+
+    psImage *shutterImage = shutter->image; // Shutter correction image
+    psImage *patternImage = pattern->image; // Illumination pattern
+
+    int num = data->num;                // Number of images
+    psVector *counts = psVectorAlloc(num, PS_TYPE_F32); // Counts in each image
+    psVector *errors = psVectorAlloc(num, PS_TYPE_F32); // Counts in each image
+    psVector *mask = psVectorAlloc(num, PS_TYPE_MASK); // Mask for each image
+    psTrace("psModules.detrend", 2, "Performing linear fit on individual pixels...\n");
+    for (int i = minInputRows; i < maxInputRows; i++) {
+        int yOut = i - shutter->row0; // y position on output readout
+        for (int j = minInputCols; j < maxInputCols; j++) {
+            int xOut = j - shutter->col0; // x position on output readout
+
+            psVectorInit(mask, 0);
+            for (int r = 0; r < num; r++) {
+                pmReadout *readout = inputs->data[r]; // Readout of interest
+                int yIn = i - readout->row0; // y position on input readout
+                int xIn = j - readout->col0; // x position on input readout
+                psImage *image = readout->image; // Image of interest
+                float ref = data->refs->data.F32[r]; // (Inverse) reference value
+                counts->data.F32[r] = image->data.F32[yIn][xIn] * ref;
+                if (readout->mask) {
+                    mask->data.PS_TYPE_MASK_DATA[r] = readout->mask->data.PS_TYPE_MASK_DATA[yIn][xIn];
+                }
+                if (readout->weight) {
+                    errors->data.F32[r] = sqrtf(readout->weight->data.F32[yIn][xIn]) * ref;
+                } else {
+                    // XXX guess that the input data is Poisson distributed; if we go negative, force high
+                    errors->data.F32[r] = sqrtf(fabs(image->data.F32[yIn][xIn])) * ref;
+                }
+            }
+
+            pmShutterCorrection *corr = pmShutterCorrectionLinFit(data->exptimes, counts, errors, mask,
+                                                                  reference, nIter, rej, maskVal);
+            if (!corr) {
+                // Nothing we can do about it
+                psErrorClear();
+                shutterImage->data.F32[yOut][xOut] = NAN;
+                patternImage->data.F32[yOut][xOut] = NAN;
+                nums->data.U16[yOut][xOut] = 0;
+                sigma->data.F32[yOut][xOut] = NAN;
+                continue;
+            }
+            shutterImage->data.F32[yOut][xOut] = corr->offset;
+            patternImage->data.F32[yOut][xOut] = corr->scale;
+            nums->data.U16[yOut][xOut] = corr->num;
+            sigma->data.F32[yOut][xOut] = corr->stdev;
+            psFree(corr);
+        }
+    }
+    psFree(mask);
+    psFree(errors);
+    psFree(counts);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShutterCorrection.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShutterCorrection.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmShutterCorrection.h	(revision 22322)
@@ -0,0 +1,211 @@
+/* @file pmShutterCorrection.h
+ * @brief Functions to build and apply a shutter exposure-time correction.
+ *
+ * @author Eugene Magnier, IfA
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-09-09 04:10:14 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_SHUTTER_CORRECTION_H
+#define PM_SHUTTER_CORRECTION_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+/*  A mechanical shutter may not yield uniform exposure times as a function of position on the
+ *  detector.  The typical error consists of a constant exposure-time offset relative to the
+ *  requested value, ie exposure time is T_o + dT(x,y).  The exposure error, dT, may be
+ *  measured with the following scheme.  Obtain a set of exposures with different exposures
+ *  times taken of the same flat-field source; the source must be spatially stable between the
+ *  exposures, but need not have a stable amplitude.  For an illuminating flux of intensity
+ *  F(x,y) = F_o f(x,y), the signal recorded by any pixel in the detector is given by: S(t,x,y)
+ *  = F_o(t) f(x,y) (T_o + dT(x,y)) where F_o(t) is the (variable) overall intensity of the
+ *  illuminating source and f(x,y) is the spatial illumination pattern times the flat-field
+ *  response.  Choose a reference location in the image (eg, the detector center) and divide by
+ *  the value of that region (ie, mean or median):
+ *
+ *  s(t,x,y) = S(t,x,y) / S(t,0,0)
+ *  s(t,x,y) = F_o(t) f(x,y) (T_o + dT(x,y)) / F_o(t) f(0,0) (T_o + dT(0,0))
+ *  s(t,x,y) = f(x,y) (T_o + dT(x,y)) / f(0,0) (T_o + dT(0,0))
+ *
+ *  we can absorb the term f(0,0) into f(x,y) as we have no motivation for the scale of f(x,y)
+ *  -- a normalization for the flat-field is not specified here.  For any single pixel, over
+ *  the set of exposures, we thus need to solve for dT(x,y), dT(0,0), and f'(x,y) in the
+ *  equation: s(t,x,y) = f'(x,y) (T_o + dT(x,y)) / (T_o + dT(0,0))
+ *
+ *  we avoid directly fitting these values as the process would be a non-linear
+ *  least-squares problem for every pixel in the image, and thus very time
+ *  consuming.  There are linear options which may be used instead.
+ *  First, as T_o goes to a large value, s() approaches the value of f'(x,y).
+ *  Next, as T_o goes to a very small value, s() approaches the value of
+ *  f'(x,y)*dT(x,y)/dT(0,0).  Finally, when s() has the value of
+ *  f'(x,y)*(1 + dT(x,y)/dT(0,0))/2, T_o has the value of dT(0,0).  with data
+ *  points covering a reasonable dynamic range, we can solve for these three
+ *  values by interpolation and/or extrapolation.
+ *
+ *  To take the strategy one step further, we could use the above recipe to
+ *  obtain a guess for the three parameters and then apply non-linear fitting to
+ *  solve more accurately for the parameters.  If we limit this operation to a
+ *  handful of positions in the image (user defined, but the obvious choice would
+ *  be positions near the center, edges, and corners), then we may determine a
+ *  good value for dT(0,0).  Since there is only one dT(0,0) for the image, we
+ *  can apply the resulting measurement to the rest of the pixels in the image.
+ *  If dT(0,0) is not a free parameter, then the fitting process is linear in
+ *  terms of dT(x,y) and f'(x,y)
+ */
+
+/// Shutter correction parameters, applicable for a single pixel
+typedef struct {
+    double scale;                       ///< The normalisation for an exposure, A(k) or f'(x,y)
+    double offset;                      ///< The time offset, dTk
+    double offref;                      ///< The reference time offset, dTo
+    int num;                            ///< Number of points used
+    float stdev;                        ///< Standard deviation
+    bool valid;                         // is the fitted shutter correction valid (produce a significant improvement?)
+} pmShutterCorrection;
+
+/// Allocator for shutter correction parameters
+pmShutterCorrection *pmShutterCorrectionAlloc();
+
+/// Guess a shutter correction, based on plot of counts vs exposure time
+///
+/// This function is used before doing the full non-linear fit, to get parameters close to the true.  Assumes
+/// exptime vector is sorted (ascending order; longest is last) prior to input.
+pmShutterCorrection *pmShutterCorrectionGuess(
+    const psVector *exptime,            ///< Exposure times for each exposure
+    const psVector *counts              ///< Counts for each exposure
+    );
+
+/// Generate shutter correction based on a linear fit
+///
+/// Performs a linear fit to counts as a function of exposure time, with the reference time offset fixed (so
+/// that the system is linear).  Performs iterative clipping, if nIter > 1.
+pmShutterCorrection *pmShutterCorrectionLinFit(
+    const psVector *exptime,            ///< Exposure times for each exposure
+    const psVector *counts,             ///< Counts for each exposure
+    const psVector *cntError,           ///< Error in the counts
+    const psVector *mask,               ///< Mask for each exposure
+    float offref,                       ///< Reference time offset
+    int nIter,                          ///< Number of iterations
+    float rej,                          ///< Rejection threshold (sigma)
+    psMaskType maskVal                  ///< Mask value
+    );
+
+/// Generate shutter correction based on a full non-linear fit
+///
+/// Performs a full non-linear fit to counts as a function of exposure time.  The main purpose is to solve for
+/// the reference time offset, so that future fits may be performed using linear fitting with the reference
+/// time offset fixed.
+pmShutterCorrection *pmShutterCorrectionFullFit(
+    const psVector *exptime,            ///< Exposure times for each exposure
+    const psVector *counts,             ///< Counts for each exposure
+    const psVector *cntError,           ///< Error in the counts
+    const pmShutterCorrection *guess    ///< Initial guess
+    );
+
+/// Measure a shutter correction image from an array of images
+///
+/// Given an array of readouts (with known exposure times from the cell concepts), this function measures the
+/// shutter correction (our principal concern is for the time offset, rather than the normalisation) by
+/// measuring the reference time offset using the full non-linear fit for a small number of representative
+/// regions (middle and corners), and then using that to perform a linear fit to each pixel.
+bool pmShutterCorrectionMeasure(
+    pmReadout *output,                  ///< Output readout
+    const psArray *readouts,            ///< Array of readouts
+    int size,                           ///< Size of samples for statistics for non-linear fit
+    psStatsOptions meanStat,            ///< Statistic to use for mean
+    psStatsOptions stdevStat,           ///< Statistic to use for stdev
+    int nIter,                          ///< Number of iterations
+    float rej,                          ///< Rejection threshold (sigma)
+    psMaskType maskVal                  ///< Mask value
+    );
+
+/// Thread entry point for applying a shutter correction
+bool pmShutterCorrectionApplyScan_Threaded(
+    psThreadJob *job                    ///< Job to execute
+    );
+
+/// Apply the shutter correction to a scan
+bool pmShutterCorrectionApplyScan(
+    psImage *image,                     ///< Input image to correct
+    const psImage *shutterImage,        ///< Shutter correction image
+    psImage *mask,                      ///< Input mask image
+    float exptime,                      ///< Exposure time to which to correct
+    psMaskType blank,                   ///< Mask value to give blank pixels
+    int rowStart, int rowStop           ///< Range of scan
+    );
+
+/// Apply a shutter correction
+///
+/// Given a shutter correction (with dT for each pixel), applies this correction to an input image.
+bool pmShutterCorrectionApply(
+    pmReadout *readout,                 ///< Readout to which to apply shutter correction
+    const pmReadout *shutter,           ///< Shutter correction readout, with dT for each pixel
+    psMaskType blank                    ///< Value to give blank pixels
+    );
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Functions for doing the shutter correction piece-meal (don't have to read entire image stack into memory at
+// once).  A single read run through the stack is required, calling pmShutterCorrectionAddReadout on each.
+// Then pmShutterCorrectionReference provides the required reference shutter time, so that
+// pmShutterCorrectionGenerate can generate a shutter correction piece by piece as overlapping pixels from
+// each input are read in.
+
+
+/// Data for measuring the shutter correction
+typedef struct {
+    int num;                            ///< Number of images
+    int numCols, numRows;               ///< Size of images
+    psArray *regions;                   ///< Regions at which to measure statistics
+    psArray *mean;                      ///< Vector of means at each region
+    psArray *stdev;                     ///< Vector of standard deviations at each region
+    psVector *exptimes;                 ///< Exposure times for each image
+    psVector *refs;                     ///< Reference fluxes
+} pmShutterCorrectionData;
+
+/// Allocator for pmShutterCorrectionData
+pmShutterCorrectionData *pmShutterCorrectionDataAlloc(int numCols, int numRows, ///< Size of images
+                                                      int size ///< Size of regions
+    );
+
+/// Add a readout to the correction data
+///
+/// Performs statistics on the readout, recording the data
+bool pmShutterCorrectionAddReadout(
+    pmShutterCorrectionData *data,      ///< Correction data
+    const pmReadout *readout,           ///< Readout to add
+    psStatsOptions meanStat,            ///< Statistic to use for mean
+    psStatsOptions stdevStat,           ///< Statistic to use for stdev
+    psMaskType maskVal,                 ///< Mask value
+    psRandom *rng                       ///< Random number generator
+    );
+
+/// Calculate the reference shutter time from the correction data
+float pmShutterCorrectionReference(
+    pmShutterCorrectionData *data ///< Correction data
+    );
+
+/// Generate a shutter correction
+///
+/// Performs the linear fit to each pixel in the stack.
+bool pmShutterCorrectionGenerate(
+    pmReadout *shutter,                 ///< Shutter correction
+    pmReadout *pattern,                 ///< Background pattern (or NULL)
+    const psArray *inputs,              ///< Stack of input pmReadouts
+    float reference,                    ///< Reference shutter time (from pmShutterCorrectionRef)
+    const pmShutterCorrectionData *data, ///< Correction data
+    int nIter,                          ///< Number of iterations
+    float rej,                          ///< Rejection threshold (sigma)
+    psMaskType maskVal                  ///< Mask value
+    );
+
+// prepare outputs for shutter correction
+bool pmShutterCorrectionGeneratePrepare(pmReadout *shutter, pmReadout *pattern, const psArray *inputs,
+                                        psMaskType maskVal);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmSkySubtract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmSkySubtract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmSkySubtract.c	(revision 22322)
@@ -0,0 +1,738 @@
+/** @file  pmSubtractSky.c
+ *
+ *  This file will contain a module which will create a model of the
+ *  background sky and subtract that from the input image.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-04-04 22:42:48 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmSubtractSky.h"
+
+// XXX: Get rid of the.  Create pmUtils.h
+psImage *p_psDetermineTrimmedImage(
+    pmReadout *in
+);
+
+/******************************************************************************
+DetermineNumBits(data): This routine takes an enum psStatsOptions as an
+argument and returns the number of non-zero bits.
+
+XXX: This code is duplicated in the ReadoutCombine file.
+ *****************************************************************************/
+static psS32 DetermineNumBits(psStatsOptions data)
+{
+    psTrace("psModules.detrend", 4, "Calling DetermineNumBits(0x%x)\n", data);
+
+    psS32 i;
+    psU64 tmpData = data;
+    psS32 numBits = 0;
+
+    for (i=0;i<(8 * sizeof(psStatsOptions));i++) {
+        if (0x0001 & tmpData) {
+            numBits++;
+        }
+        tmpData = tmpData >> 1;
+    }
+
+    psTrace("psModules.detrend", 4,
+            "Calling DetermineNumBits(0x%x) -> %d\n", data, numBits);
+    return(numBits);
+}
+
+/******************************************************************************
+getHighestPriorityStatOption(statOptions): this routine takes as input a
+psStats->options with multiple options set and returns one with a single
+option set according to the precedence set in the SDRS.
+ *****************************************************************************/
+static psU64 getHighestPriorityStatOption(psU64 statOptions)
+{
+    psTrace("psModules.detrend", 4,
+            "Calling getHighestPriorityStatOption(0x%x)\n", statOptions);
+
+    if (statOptions & PS_STAT_SAMPLE_MEAN) {
+        return(PS_STAT_SAMPLE_MEAN);
+    } else if (statOptions & PS_STAT_SAMPLE_MEDIAN) {
+        return(PS_STAT_SAMPLE_MEDIAN);
+    } else if (statOptions & PS_STAT_CLIPPED_MEAN) {
+        return(PS_STAT_CLIPPED_MEAN);
+    } else if (statOptions & PS_STAT_FITTED_MEAN) {
+        return(PS_STAT_FITTED_MEAN);
+    } else if (statOptions & PS_STAT_ROBUST_MEDIAN) {
+        return(PS_STAT_ROBUST_MEDIAN);
+    }
+    psError(PS_ERR_UNKNOWN, true, "Unallowable option requested for statistically binning image pixels.\n");
+    return(-1);
+    // XXX
+    //else if (statOptions & PS_STAT_ROBUST_MODE) {
+    //    return(PS_STAT_ROBUST_MODE);
+    //}
+}
+
+/******************************************************************************
+psImage *binImage(origImage, binFactor, statOptions): This routine takes an
+input psImage and scales it smaller by a factor of binFactor.  The statistic
+used in combining input pixels is specified in statOptions.
+
+XXX: use static vectors for myStats, binVector and binMask.
+XXX: I coded this before I was aware of a psLib reBin function.  I don't
+use this function in this module.  I'm keeping it here in the event that
+requirements change and we might need a custom reBin function.
+ *****************************************************************************/
+#if 0
+static psImage *binImage(psImage *origImage,
+                         int binFactor,
+                         psStatsOptions statOptions)
+{
+    psTrace("psModules.detrend", 4, "Calling binImage(%d)\n", binFactor);
+
+    if (binFactor <= 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: binImage(): binFactor is %d\n", binFactor);
+        return(origImage);
+    }
+    if (binFactor == 1) {
+        return(origImage);
+    }
+
+    psVector *binVector = psVectorAlloc(binFactor * binFactor, PS_TYPE_F32);
+    psVector *binMask = psVectorAlloc(binFactor * binFactor, PS_TYPE_U8);
+    psStats *myStats = psStatsAlloc(statOptions);
+
+    for (psS32 row = 0; row < origImage->numRows ; row+=binFactor) {
+        for (psS32 col = 0; col < origImage->numCols ; col+=binFactor) {
+            psS32 count = 0;
+            for (psS32 binRow = 0; binRow <= binFactor ; binRow++) {
+                for (psS32 binCol = 0; binCol <= binFactor ; binCol++) {
+                    if (((row + binRow) < origImage->numRows) &&
+                            ((col + binCol) < origImage->numCols)) {
+                        binVector->data.F32[count] =
+                            origImage->data.F32[row + binRow][col + binCol];
+                        binMask->data.U8[count] = 0;
+                    } else {
+                        binVector->data.F32[count] = 0.0;
+                        binMask->data.U8[count] = 1;
+                    }
+                    count++;
+                }
+            }
+            psStats *rc1 = psVectorStats(myStats, binVector, NULL, binMask, 1);
+            if (rc1 == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
+                return(origImage);
+            }
+            psF64 statValue;
+            psBool rc = p_psGetStatValue(rc1, &statValue);
+
+            if (rc == true) {
+                origImage->data.F32[row][col] = (psF32) statValue;
+            } else {
+                origImage->data.F32[row][col] = 0.0;
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: pmSubtractSky(), binImage(): p_psGetStatValue() was FALSE\n");
+            }
+        }
+    }
+    psFree(binVector);
+    psFree(binMask);
+    psFree(myStats);
+
+    psTrace("psModules.detrend", 4, "Exiting binImage(%d)\n", binFactor);
+    return(origImage);
+}
+#endif
+
+/******************************************************************************
+CalculatePolyTerms(xOrder, yOrder): this routine will calculate the number of
+coefficients (or terms) in a 2-D polynomial of order (xOrder, yOrder).
+
+XXX: Use your brain and figure out the analytical expression.
+
+XXX: Why isn't it simply (xOrder+1) * (yOrder+1)?
+ *****************************************************************************/
+static psS32 CalculatePolyTerms(psS32 xOrder, psS32 yOrder)
+{
+    psTrace("psModules.detrend", 4,
+            "Calling CalculatePolyTerms(%d, %d)\n", xOrder, yOrder);
+
+    psS32 maxOrder = PS_MAX(xOrder, yOrder);
+    psS32 localPolyTerms = 0;
+    psS32 order = 0;
+    psS32 num=0;
+
+    for (order=0;order<=maxOrder;order++) {
+        for (num=0;num<=order;num++) {
+            if (((order-num) <= xOrder) && (num <= yOrder)) {
+                localPolyTerms++;
+            }
+        }
+    }
+    psTrace("psModules.detrend", 4,
+            "Exiting CalculatePolyTerms(%d, %d) -> %d\n", xOrder, yOrder, localPolyTerms);
+    return(localPolyTerms);
+
+    //    return((xOrder+1) * (yOrder+1));
+}
+
+/******************************************************************************
+buildPolyTerms(): this routine computes a 2-D array polyTerms[][] that holds
+terms for the polynomial that is used to model the sky background.  We use
+this array primarily for convenience in computations involving sky model
+polynomials.  It is defined as:
+    polyTerms[i][0] = the power to which X is raised in the i-th term of in an
+    poly-order sky background polynomial.
+
+    polyTerms[i][1] = the power to which Y is raised in the i-th term of in an
+    poly-order sky background polynomial.
+ *****************************************************************************/
+static psS32 **buildPolyTerms(psS32 xOrder, psS32 yOrder)
+{
+    psTrace("psModules.detrend", 4,
+            "Calling buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+
+    psS32 i=0;
+    psS32 order = 0;
+    psS32 num=0;
+    psS32 localPolyTerms = CalculatePolyTerms(xOrder, yOrder);
+    psS32 maxOrder = PS_MAX(xOrder, yOrder);
+
+    // Create the data structure which we hold the xy order of each coeff.
+    psS32 **polyTerms = (psS32 **) psAlloc(localPolyTerms * sizeof(psS32 *));
+    for (i=0; i < localPolyTerms ; i++) {
+        polyTerms[i] = (psS32 *) psAlloc(2 * sizeof(psS32));
+    }
+
+    i=0;
+    // This code segment loops through each term i in the polynomial and
+    // calculates the power to which x/y are raised in that i-th term.
+    // We first do the 0-order terms, then the 1-order terms, etc.
+    for (order=0;order<=maxOrder;order++) {
+        for (num=0;num<=order;num++) {
+            if (((order-num) <= xOrder) && (num <= yOrder)) {
+                polyTerms[i][0] = order-num;
+                polyTerms[i][1] = num;
+                i++;
+            }
+        }
+    }
+
+    if (psTraceGetLevel("psModules.detrend") >= 10) {
+        for (i=0; i < localPolyTerms ; i++) {
+            printf("x^%d * y^%d\n", polyTerms[i][0], polyTerms[i][1]);
+        }
+    }
+
+    psTrace("psModules.detrend", 4,
+            "Exiting buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+    return(polyTerms);
+}
+
+/******************************************************************************
+This procedure calculates various combinations of powers of x and y and stores
+them in the data structure p_psPolySums[][].  After it completes:
+
+    p_psPolySums[i][j] == x^i * y^j
+
+XXX: Use a psImage for the p_psPolySums data structure?
+XXX: p_psPolySums: should this be a global?  Did you get the storage classifier
+     and name correct?
+XXX: Use variable size arrays for p_psPolySums[][].
+XXX: Must initialize p_psPolySums[][]?
+ *****************************************************************************/
+#define PS_MAX_POLYNOMIAL_ORDER 20
+
+psF64 p_psPolySums[PS_MAX_POLYNOMIAL_ORDER+1][PS_MAX_POLYNOMIAL_ORDER+1];
+static void buildSums(psF64 x,
+                      psF64 y,
+                      psS32 xOrder,
+                      psS32 yOrder)
+{
+    psTrace("psModules.detrend", 4,
+            "Calling buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+
+    psS32 i = 0;
+    psS32 j = 0;
+    psF64 xSum = 0.0;
+    psF64 ySum = 0.0;
+
+    xSum = 1.0;
+    ySum = 1.0;
+    for(i=0;i<=xOrder;i++) {
+        ySum = xSum;
+        for(j=0;j<=yOrder;j++) {
+            p_psPolySums[i][j] = ySum;
+            ySum*= y;
+        }
+        xSum*= x;
+    }
+    psTrace("psModules.detrend", 4,
+            "Exiting buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+}
+
+/******************************************************************************
+ImageFitPolynomial(myPoly, dataImage, maskImage): this private routine takes
+an input image along with a mask and fits a polynomial to it.  The degree of
+the polynomial is specified by input parameter myPoly, and need not be
+symmetrical in orders of X and Y.  The polynomial must be type
+PS_POLYNOMIAL_ORD.  If there are not enough rows or columns in the input image
+for the order of the polynomial, then that order is reduced.  The algorithm
+used in this routine is based on that of the pilot project ADD, but is not
+documented anywhere.
+
+XXX: Different trace message facilities in use here.
+ *****************************************************************************/
+static psPolynomial2D *ImageFitPolynomial(
+    psPolynomial2D *myPoly,
+    psImage *dataImage,
+    psImage *maskImage)
+{
+    psTrace("psModules.detrend", 4,
+            "Calling ImageFitPolynomial()\n");
+    PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+    PS_ASSERT_POLY_TYPE(myPoly, PS_POLYNOMIAL_ORD, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(dataImage, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(dataImage, NULL);
+    PS_ASSERT_IMAGE_TYPE(dataImage, PS_TYPE_F32, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(maskImage, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(maskImage, NULL);
+    PS_ASSERT_IMAGE_TYPE(maskImage, PS_TYPE_U8, NULL);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(dataImage, maskImage, NULL);
+    psS32 oldPolyX = -1;
+    psS32 oldPolyY = -1;
+
+    // The matrix equations become singular if there are more powers of X
+    // in myPoly then there are rows of the image.  I think.  Similarly for
+    // powers of Y and columns.  So.  Here we reduce the complexity of the
+    // polynomial if there are not enough rows/columns in the input image.
+
+    if ((myPoly->nX + 1) > dataImage->numRows) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: ImageFitPolynomial(): Reducing polynomial complexity in x-dimension.\n");
+        oldPolyX = myPoly->nX;
+        myPoly->nX = dataImage->numRows - 1;
+    }
+    if ((myPoly->nY + 1) > dataImage->numCols) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: ImageFitPolynomial(): Reducing polynomial complexity in y-dimension.\n");
+        oldPolyY = myPoly->nY;
+        myPoly->nY = dataImage->numCols - 1;
+    }
+    psS32 i;
+    psS32 j;
+    psS32 x;
+    psS32 y;
+    psS32 aRow;
+    psS32 aCol;
+    psS32 **polyTerms = buildPolyTerms(myPoly->nX, myPoly->nY);
+    // We determine how many coefficients will be in the polynomial that we
+    // are fitting to this image.
+    psS32 localPolyTerms = CalculatePolyTerms(myPoly->nX, myPoly->nY);
+    psImage *A = psImageAlloc(localPolyTerms, localPolyTerms, PS_TYPE_F64);
+    psImage *Aout = psImageAlloc(localPolyTerms, localPolyTerms, PS_TYPE_F64);
+    psVector *B = psVectorAlloc(localPolyTerms, PS_TYPE_F64);
+    psVector *outPerm = NULL;
+
+    //
+    // Initialize A matrix and B vector.
+    //
+    psImageInit(A, 0.0);
+    psVectorInit(B, 0.0);
+
+    //
+    // We build the A matrix and B vector.
+    //
+    for (x=0;x<dataImage->numRows;x++) {
+        for (y=0;y<dataImage->numCols;y++) {
+            if (maskImage->data.U8[x][y] == 0) {
+                buildSums((psF64) x, (psF64) y, myPoly->nX, myPoly->nY);
+
+                /************************************************************
+                This code dervies from equation (7) of the pilot ADD.  However,
+                it is not exactly the same in that the order of the polynomial
+                may be different in X And Y.
+
+                Equation (7) from the pilot ADD describes 16 linear equations.
+                The i-th equation is simply the partial derivative of the
+                sky background polynomial (1) w.r.t. to the i-th term in
+                that polynomial.  The i-th equation is stored in row i of
+                matrix A[][] (matrix A[][] has origin (1,1), not (0,0)).  To
+                compute A[i][j] we simply multiply the j-th term of the Sky
+                Background Polynomial (SBP) by the i-th term of SBP.
+                ************************************************************/
+                for (aRow=0;aRow<localPolyTerms;aRow++) {
+                    for (aCol=0;aCol<localPolyTerms;aCol++) {
+                        A->data.F64[aRow][aCol]+=
+                            (p_psPolySums[ polyTerms[aCol][0] ][ polyTerms[aCol][1] ] *
+                             p_psPolySums[ polyTerms[aRow][0] ][ polyTerms[aRow][1] ]);
+                    }
+                }
+                // Build the B[] vector, which is the right-hand side of (7).
+                for (i=0;i<localPolyTerms;i++) {
+                    B->data.F64[i]+= dataImage->data.F32[x][y] *
+                                     p_psPolySums[ polyTerms[i][0] ][ polyTerms[i][1] ];
+                }
+            }
+        }
+    }
+
+    if (psTraceGetLevel(".psModule.pmSubtractSky.ImageFitPolynomial") >= 8) {
+        for (aRow=0;aRow<localPolyTerms;aRow++) {
+            for (aCol=0;aCol<localPolyTerms;aCol++) {
+                printf("A[%d][%d] is %f\n", aRow, aCol,
+                       A->data.F64[aRow][aCol]);
+            }
+        }
+
+        for (i=0;i<=localPolyTerms;i++) {
+            printf("B[%d] is %f\n", i, B->data.F64[i]);
+        }
+    }
+
+    //
+    // Solve the matrix equations for the polynomial coefficients C.
+    // XXX: How do we know if these matrix operations were successful?
+    //
+    Aout = psMatrixLUD(Aout, &outPerm, A);
+    PS_ASSERT_IMAGE_NON_NULL(Aout, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(Aout, NULL);
+    psVector *C = psVectorAlloc(localPolyTerms, PS_TYPE_F64);
+    psMatrixLUSolve(C, Aout, B, outPerm);
+
+    //
+    // Set the appropriate coefficients in the myPoly structure.
+    //
+    for (i=0;i<localPolyTerms;i++) {
+        myPoly->coeff[ polyTerms[i][0] ][ polyTerms[i][1] ] = C->data.F64[i];
+        psTrace("psModules.detrend", 6,
+                "myPoly->coeff[%d][%d] is %f\n", polyTerms[i][0], polyTerms[i][1], myPoly->coeff[ polyTerms[i][0] ][ polyTerms[i][1] ]);
+    }
+
+    //
+    // Free data structures that were allocated in this module.
+    //
+    for (i=0;i<localPolyTerms;i++) {
+        psFree(polyTerms[i]);
+    }
+    psFree(polyTerms);
+    psFree(A);
+    psFree(Aout);
+    psFree(B);
+    psFree(C);
+    psFree(outPerm);
+
+    //
+    // We restore the original size of the polynomial and set remaining
+    // coefficients to 0.0, if necessary.
+    //
+    // XXX: Verify this works after poly nOrder/nTerm change.
+    //
+    if (oldPolyX != -1) {
+        myPoly->nX = oldPolyX;
+        for (i=oldPolyX ; i < (1 + myPoly->nX) ; i++) {
+            for (j=0;j<(1 + myPoly->nY) ; j++) {
+                myPoly->coeff[i][j] = 0.0;
+            }
+        }
+    }
+    if (oldPolyY != -1) {
+        myPoly->nY = oldPolyY;
+        for (i=0 ; i < (1 + myPoly->nX) ; i++) {
+            for (j=oldPolyY;j < (1 + myPoly->nY) ; j++) {
+                myPoly->coeff[i][j] = 0.0;
+            }
+        }
+    }
+
+    psTrace("psModules.detrend", 4,
+            "Exiting ImageFitPolynomial()\n");
+    //    psTrace("psModules.detrend", 4,
+    //            "---- ImageFitPolynomial() end successfully ----\n");
+    return(myPoly);
+}
+
+
+/******************************************************************************
+pmReadout pmSubtractSky():
+
+XXX: use static vectors for myStats, and the binned image
+
+XXX: The SDR is silent about types.  PS_TYPE_F32 is implemented here.
+
+XXX: Sync the psTrace message facilities.
+ *****************************************************************************/
+pmReadout *pmSubtractSky(pmReadout *in,
+                         void *fitSpec,
+                         psFit fit,
+                         psS32 binFactor,
+                         psStats *stats,
+                         psF32 clipSD)
+{
+    PS_ASSERT_READOUT_NON_NULL(in, NULL);
+    PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
+    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
+    PS_WARN_PTR_NON_NULL(in->parent);
+    if (in->parent != NULL) {
+        PS_WARN_PTR_NON_NULL(in->parent->concepts);
+    }
+    psTrace("psModules.detrend", 4,
+            "---- pmSubtractSky() begin ----\n");
+
+    if ((fit != PM_FIT_NONE) &&
+            (fit != PM_FIT_POLYNOMIAL) &&
+            (fit != PM_FIT_SPLINE)) {
+        psError(PS_ERR_UNKNOWN, true, "psFit is unallowable (%d).  Returning in image.\n", fit);
+        return(in);
+    }
+
+    psStatsOptions statOptions = 0;
+
+    //
+    // Return the original input readout if the fit specs are poorly defined.
+    // No warning or error messages should be generated.
+    //
+    if ((fitSpec == NULL) ||
+            ((fit == PM_FIT_NONE) || (fit == PM_FIT_SPLINE))) {
+        //        psLogMsg(__func__, PS_LOG_WARN, "Fit specs are poorly defined.  Returning in image.\n");
+        return(in);
+    }
+
+    //
+    // Determine trimmed image from metadata.
+    //
+
+    psImage *trimmedImg = p_psDetermineTrimmedImage(in);
+    psImage *binnedImage = NULL;
+    psPolynomial2D *myPoly = NULL;
+    psImage *binnedMaskImage = NULL;
+    psU32 oldStatOptions = 0;
+
+    //
+    // Determine which statistic to use when binning pixels, if any.
+    //
+    if (stats != NULL) {
+        statOptions = stats->options;
+        if (1 < DetermineNumBits(statOptions)) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: Multiple statistical options have been requested.\n");
+            statOptions = getHighestPriorityStatOption(statOptions);
+            if (statOptions == -1) {
+                psError(PS_ERR_UNKNOWN, true, "Not allowable stats->option was specified.  Returning in image.\n");
+                return(in);
+            }
+            // Save old input "stats" parameter.
+            oldStatOptions = stats->options;
+            stats->options = statOptions;
+        }
+        if (0 == DetermineNumBits(statOptions)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractSky(): no stats->options was requested\n");
+        }
+    }
+
+    //
+    // Generate required warning messages.
+    //
+    if (binFactor <= 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractSky(): binFactor is %d\n", binFactor);
+    }
+    if (stats == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractSky(): input parameter stats is NULL\n");
+    }
+
+    //
+    // Bin the input image according to input parameters.
+    // Create a new binned image mask.
+    //
+    if ((binFactor <= 1) || (stats == NULL) || (0 == DetermineNumBits(statOptions))) {
+        // No binning is required here.  Simply create a copy of the image
+        // and a mask.
+        binnedImage = psImageCopy(binnedImage, trimmedImg, PS_TYPE_F32);
+        if (binnedImage == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageCopy() returned NULL.  Returning in image.\n");
+            return(in);
+        }
+
+        if (in->mask != NULL) {
+            binnedMaskImage = psImageCopy(binnedMaskImage, in->mask, PS_TYPE_U8);
+            if (binnedMaskImage == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psImageCopy() returned NULL.  Returning in image.\n");
+                psFree(binnedImage);
+                return(in);
+            }
+        } else {
+            binnedMaskImage = psImageAlloc(binnedImage->numCols,
+                                           binnedImage->numRows,
+                                           PS_TYPE_U8);
+            psImageInit(binnedMaskImage, 0);
+        }
+    } else {
+        binnedImage = psImageRebin(NULL, trimmedImg, in->mask, 0, binFactor, stats);
+        if (binnedImage == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageRebin() returned NULL.  Returning in image.\n");
+            return(in);
+        }
+        binnedMaskImage = psImageAlloc(binnedImage->numCols,
+                                       binnedImage->numRows,
+                                       PS_TYPE_U8);
+        psImageInit(binnedMaskImage, 0);
+    }
+    psTrace("psModules.detrend", 4,
+            "binnedImage size is (%d, %d)\n", binnedImage->numRows, binnedImage->numCols);
+
+    //
+    // Clip pixels that are outside the acceptable range.
+    //
+    if (clipSD <= 0.0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractSky(): clipSD is %f\n", clipSD);
+    } else {
+        // Determine the mean and standard deviation of the binned image.
+        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        if (!psImageStats(myStats, binnedImage, NULL, 0)) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Couldn't get statistics for image.\n");
+            return NULL;
+        }
+        psF64 binnedMean = myStats->sampleMean;
+        psF64 binnedStdev = myStats->sampleStdev;
+        psFree(myStats);
+        psTrace("psModules.detrend", 6,
+                "binned StDev is %f\n", binnedStdev);
+
+        // Clip all pixels which are more than clipSD sigmas from the mean.
+        psTrace("psModules.detrend", 6,
+                "clipSD is %f\n", clipSD);
+
+        for (psS32 row = 0; row < binnedImage->numRows ; row++) {
+            for (psS32 col = 0; col < binnedImage->numCols ; col++) {
+                if (fabs(binnedImage->data.F32[row][col] - binnedMean) >
+                        (clipSD * binnedStdev)) {
+                    binnedMaskImage->data.U8[row][col] = 1;
+                }
+            }
+        }
+    }
+
+    //
+    // Fit the polynomial to the binned image
+    //
+    if (fit == PM_FIT_POLYNOMIAL) {
+        myPoly = (psPolynomial2D *) fitSpec;
+        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+        PS_ASSERT_POLY_TYPE(myPoly, PS_POLYNOMIAL_ORD, NULL);
+
+        myPoly = ImageFitPolynomial(myPoly, binnedImage, binnedMaskImage);
+
+        if (myPoly != NULL) {
+            // Set the pixels in the binned image to that of the polynomial.
+            binnedImage = psImageEvalPolynomial(binnedImage, myPoly);
+            if (binnedImage == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psImageEvalPolynomial() returned NULL.  Returning in image.\n");
+                psFree(binnedMaskImage);
+                if (!((binFactor <= 1) || (stats == NULL))) {
+                    psFree(binnedImage);
+                }
+                if (oldStatOptions != 0) {
+                    stats->options = statOptions;
+                }
+                return(in);
+            }
+        } else {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractSky(): could not model sky with a polynomial.\n");
+            psFree(binnedMaskImage);
+            if (!((binFactor <= 1) || (stats == NULL))) {
+                psFree(binnedImage);
+            }
+            if (oldStatOptions != 0) {
+                stats->options = statOptions;
+            }
+            return(in);
+        }
+    } else {
+        // We shouldn't get here since we check this above.
+        psError(PS_ERR_UNKNOWN, true, "Unallowable fit type.  Returning in image.\n");
+        psFree(binnedMaskImage);
+        if (!((binFactor <= 1) || (stats == NULL))) {
+            psFree(binnedImage);
+        }
+        if (oldStatOptions != 0) {
+            stats->options = statOptions;
+        }
+        return(in);
+    }
+
+    //
+    //Subtract the polynomially fitted image from the original image
+    //
+    if (binFactor <= 1) {
+        // The binned image is the same size as the original image.
+        for (psS32 row = 0; row < trimmedImg->numRows ; row++) {
+            for (psS32 col = 0; col < trimmedImg->numCols ; col++) {
+                trimmedImg->data.F32[row][col]-= binnedImage->data.F32[row][col];
+            }
+        }
+    } else {
+
+        psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(PS_INTERPOLATE_BILINEAR,
+                                                                           binnedImage, NULL, NULL, 0,
+                                                                           0.0, 0.0, 0, 0, 0.0);
+
+        for (psS32 row = 0; row < trimmedImg->numRows ; row++) {
+            for (psS32 col = 0; col < trimmedImg->numCols ; col++) {
+                // We calculate the F32 value of the pixel coordinates in the
+                // binned image and then use a pixel interpolation routine to
+                // determine the value of the pixel at that location.
+                psF32 binRowF64 = ((psF32) row) / ((psF32) binFactor);
+                psF32 binColF64 = ((psF32) col) / ((psF32) binFactor);
+
+                // We add 0.5 to the pixel locations since the pixel
+                // interpolation routine defines the location of pixel
+                // (i, j) as (i+0.5, j+0.5).
+                binRowF64+= 0.5;
+                binColF64+= 0.5;
+
+                double binPixel;
+                if (!psImagePixelInterpolate(&binPixel, NULL, NULL, binColF64, binRowF64, interp)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+                    psFree(interp);
+                    psFree(binnedImage);
+                    return NULL;
+                }
+                trimmedImg->data.F32[row][col] -= binPixel;
+
+                psTrace("psModules.detrend", 8,
+                        "image[%d][%d] <--> binnedImage[%.2f][%.2f]: %lf\n",
+                        row, col, binRowF64-0.5, binColF64-0.5, binPixel);
+            }
+        }
+        psFree(interp);
+
+    }
+    psFree(binnedMaskImage);
+    psFree(binnedImage);
+    if (oldStatOptions != 0) {
+        stats->options = statOptions;
+    }
+
+    psTrace("psModules.detrend", 4,
+            "---- pmSubtractSky() exit successfully ----\n");
+    return(in);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmSkySubtract.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmSkySubtract.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/detrend/pmSkySubtract.h	(revision 22322)
@@ -0,0 +1,34 @@
+/* @file  pmSubtractSky.h
+ *
+ * This file will contain a module which will create a model of the
+ * background sky and subtract that from the input image.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-30 21:12:56 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PM_SUBTRACT_SKY_H
+#define PM_SUBTRACT_SKY_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+// XXX: this is pmFit in pmSubtractBias.c, named psFit here.
+typedef enum {
+    PM_FIT_NONE,                              ///< No fit
+    PM_FIT_POLYNOMIAL,                        ///< Fit polynomial
+    PM_FIT_SPLINE                             ///< Fit cubic splines
+} psFit;
+
+pmReadout *pmSubtractSky(pmReadout *in,
+                         void *fitSpec,
+                         psFit fit,
+                         int binFactor,
+                         psStats *stats,
+                         float clipSD);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/extras/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/extras/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/extras/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+.deps
+.libs
+Makefile
+Makefile.in
+libpsmodulesextras.la
+libpsmodulesextras_la-pmKapaPlots.lo
+libpsmodulesextras_la-psIOBuffer.lo
+libpsmodulesextras_la-psPipe.lo
+libpsmodulesextras_la-psVectorBracket.lo
Index: /tags/sj_tags/sj_root_20080929/psModules/src/extras/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/extras/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/extras/Makefile.am	(revision 22322)
@@ -0,0 +1,17 @@
+noinst_LTLIBRARIES = libpsmodulesextras.la
+
+libpsmodulesextras_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS) -I../pslib/
+libpsmodulesextras_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulesextras_la_SOURCES  = \
+	psVectorBracket.c \
+	psPipe.c \
+	psIOBuffer.c \
+	pmKapaPlots.c
+
+pkginclude_HEADERS = \
+	psVectorBracket.h \
+	psPipe.h \
+	psIOBuffer.h \
+	pmKapaPlots.h
+
+CLEANFILES = *~
Index: /tags/sj_tags/sj_root_20080929/psModules/src/extras/pmKapaPlots.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/extras/pmKapaPlots.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/extras/pmKapaPlots.c	(revision 22322)
@@ -0,0 +1,234 @@
+/** @file  pmAstrometryWCS.c
+ *
+ *  @brief functions to convert FITS WCS keywords to / from pmFPA structures
+ *
+ *  @ingroup Astrometry
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-06-20 02:20:07 $
+ *
+ *  Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+#include "pmKapaPlots.h"
+
+// the top portion of this file defines plotting functions which use kapa for plotting.
+// if kapa is not available, these functions are defined in the bottom portion as stubs
+// which perform NOP and return false (XXX should this be 'true' ??)
+
+# if (HAVE_KAPA)
+
+    // XXX not thread safe (perhaps not needed)
+    // to make this thread safe, we could check the thread ID and tie to it
+    static int kapa_fd = -1;
+
+int pmKapaOpen (bool showWindow)
+{
+    char kapa[64];
+
+    if (showWindow) {
+        strcpy (kapa, "kapa");
+    } else {
+        strcpy (kapa, "kapa -noX");
+    }
+
+    if (kapa_fd == -1) {
+        // kapa_fd = KapaOpen (kapa, "psphot");
+        kapa_fd = KapaOpenNamedSocket (kapa, "psphot");
+    }
+    return kapa_fd;
+}
+
+bool pmKapaClose ()
+{
+
+    if (kapa_fd == -1)
+        return true;
+    KapaClose (kapa_fd);
+    kapa_fd = -1;
+    return true;
+}
+
+bool pmKapaPlotVectorPair_AutoLimits_OpenGraph (int kapa, Graphdata *graphdata, psVector *xVec, psVector *yVec)
+{
+
+    // set limits based on data values
+    graphdata->xmin = +FLT_MAX;
+    graphdata->xmax = -FLT_MAX;
+    graphdata->ymin = +FLT_MAX;
+    graphdata->ymax = +FLT_MAX;
+    for (int i = 0; i < xVec->n; i++) {
+        graphdata->xmin = PS_MIN (graphdata->xmin, xVec->data.F32[i]);
+        graphdata->xmax = PS_MAX (graphdata->xmax, xVec->data.F32[i]);
+        graphdata->ymin = PS_MIN (graphdata->ymin, yVec->data.F32[i]);
+        graphdata->ymax = PS_MAX (graphdata->ymax, yVec->data.F32[i]);
+    }
+    // add 5% to range
+    float range;
+
+    range = graphdata->xmax - graphdata->xmin;
+    graphdata->xmax += 0.05*range;
+    graphdata->xmin -= 0.05*range;
+
+    range = graphdata->ymax - graphdata->ymin;
+    graphdata->ymax += 0.05*range;
+    graphdata->ymin -= 0.05*range;
+
+    KapaSetLimits (kapa, graphdata);
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, graphdata);
+
+    KapaPrepPlot (kapa, xVec->n, graphdata);
+    KapaPlotVector (kapa, xVec->n, xVec->data.F32, "x");
+    KapaPlotVector (kapa, yVec->n, yVec->data.F32, "y");
+    return true;
+}
+
+bool pmKapaPlotVectorPair (psVector *xVec, psVector *yVec)
+{
+
+    Graphdata graphdata;
+
+    int kapa = pmKapaOpen (true);
+    if (kapa == -1) {
+        psError(PS_ERR_UNKNOWN, true, "failure to open kapa");
+        return false;
+    }
+
+    if (xVec->n != yVec->n)
+        return false;
+
+    KapaInitGraph (&graphdata);
+    KapaClearPlots (kapa);
+
+    // set limits based on data values
+    graphdata.xmin = +FLT_MAX;
+    graphdata.xmax = -FLT_MAX;
+    graphdata.ymin = +FLT_MAX;
+    graphdata.ymax = -FLT_MAX;
+    for (int i = 0; i < xVec->n; i++) {
+        graphdata.xmin = PS_MIN (graphdata.xmin, xVec->data.F32[i]);
+        graphdata.xmax = PS_MAX (graphdata.xmax, xVec->data.F32[i]);
+        graphdata.ymin = PS_MIN (graphdata.ymin, yVec->data.F32[i]);
+        graphdata.ymax = PS_MAX (graphdata.ymax, yVec->data.F32[i]);
+    }
+    // add 5% to range
+    float range;
+
+    range = graphdata.xmax - graphdata.xmin;
+    graphdata.xmax += 0.05*range;
+    graphdata.xmin -= 0.05*range;
+
+    range = graphdata.ymax - graphdata.ymin;
+    graphdata.ymax += 0.05*range;
+    graphdata.ymin -= 0.05*range;
+
+    KapaSetLimits (kapa, &graphdata);
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, &graphdata);
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 0;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, xVec->n, &graphdata);
+    KapaPlotVector (kapa, xVec->n, xVec->data.F32, "x");
+    KapaPlotVector (kapa, yVec->n, yVec->data.F32, "y");
+
+    return true;
+}
+
+bool pmKapaPlotVectorTriple_AutoLimits_OpenGraph (int kapa, Graphdata *graphdata, psVector *xVec, psVector *yVec, psVector *zVec, bool increasing)
+{
+
+    // set limits based on data values
+    graphdata->xmin = +FLT_MAX;
+    graphdata->xmax = -FLT_MAX;
+    graphdata->ymin = +FLT_MAX;
+    graphdata->ymax = -FLT_MAX;
+    float zmin = +FLT_MAX;
+    float zmax = -FLT_MAX;
+    for (int i = 0; i < xVec->n; i++) {
+        graphdata->xmin = PS_MIN (graphdata->xmin, xVec->data.F32[i]);
+        graphdata->xmax = PS_MAX (graphdata->xmax, xVec->data.F32[i]);
+        graphdata->ymin = PS_MIN (graphdata->ymin, yVec->data.F32[i]);
+        graphdata->ymax = PS_MAX (graphdata->ymax, yVec->data.F32[i]);
+        zmin = PS_MIN (zmin, zVec->data.F32[i]);
+        zmax = PS_MAX (zmax, zVec->data.F32[i]);
+    }
+
+    // add 5% to range
+    float range;
+
+    psVector *zScale = psVectorAlloc (zVec->n, PS_DATA_F32);
+
+    range = zmax - zmin;
+    if (range == 0.0) {
+        psVectorInit (zScale, 1.0);
+    } else {
+        for (int i = 0; i < zVec->n; i++) {
+            if (increasing) {
+                zScale->data.F32[i] = PS_MIN (1.5, PS_MAX(0.05, 1.5*(zVec->data.F32[i] - zmin)/range));
+            } else {
+                zScale->data.F32[i] = PS_MIN (1.5, PS_MAX(0.05, 1.5*(zmax - zVec->data.F32[i])/range));
+            }
+        }
+    }
+
+    range = graphdata->xmax - graphdata->xmin;
+    graphdata->xmax += 0.05*range;
+    graphdata->xmin -= 0.05*range;
+
+    range = graphdata->ymax - graphdata->ymin;
+    graphdata->ymax += 0.05*range;
+    graphdata->ymin -= 0.05*range;
+
+    KapaSetLimits (kapa, graphdata);
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, graphdata);
+
+    // the point size will be scaled from the z vector
+    graphdata->size = -1;
+    KapaPrepPlot (kapa, xVec->n, graphdata);
+    KapaPlotVector (kapa, xVec->n, xVec->data.F32, "x");
+    KapaPlotVector (kapa, yVec->n, yVec->data.F32, "y");
+    KapaPlotVector (kapa, zVec->n, zScale->data.F32, "z");
+    psFree (zScale);
+    return true;
+}
+
+# else
+    # include "pmKapaPlots.h"
+
+    int pmKapaOpen (bool showWindow)
+{
+    return -1;
+}
+
+bool pmKapaClose ()
+{
+    return true;
+}
+
+bool pmKapaPlotVectorPair (psVector *xVec, psVector *yVec)
+{
+    return false;
+}
+
+bool pmKapaPlotVectorPair_AutoLimits_OpenGraph (int kapa, void *graphdata, psVector *xVec, psVector *yVec)
+{
+    return false;
+}
+
+bool pmKapaPlotVectorTriple_AutoLimits_OpenGraph (int kapa, void *graphdata, psVector *xVec, psVector *yVec, psVector *zVec, bool increasing)
+{
+    return false;
+}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/extras/pmKapaPlots.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/extras/pmKapaPlots.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/extras/pmKapaPlots.h	(revision 22322)
@@ -0,0 +1,35 @@
+/* @file  pmKapaPlots.h
+ * @brief functions to make plots with the external program 'kapa' 
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-30 21:12:56 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_KAPA_PLOTS_H
+#define PM_KAPA_PLOTS_H
+
+/// @addtogroup Extras Miscellaneous Funtions
+/// @{
+
+// move to psLib or psModules
+int pmKapaOpen (bool showWindow);
+bool pmKapaClose ();
+bool pmKapaPlotVectorPair (psVector *xVec, psVector *yVec);
+
+# if (HAVE_KAPA)
+# include <kapa.h>
+
+// yes, this is an absurd name...
+bool pmKapaPlotVectorPair_AutoLimits_OpenGraph (int kapa, Graphdata *graphdata, psVector *xVec, psVector *yVec);
+bool pmKapaPlotVectorTriple_AutoLimits_OpenGraph (int kapa, Graphdata *graphdata, psVector *xVec, psVector *yVec, psVector *zVec, bool increasing);
+# else
+
+bool pmKapaPlotVectorPair_AutoLimits_OpenGraph (int kapa, void *graphdata, psVector *xVec, psVector *yVec);
+bool pmKapaPlotVectorTriple_AutoLimits_OpenGraph (int kapa, void *graphdata, psVector *xVec, psVector *yVec, psVector *zVec, bool increasing);
+# endif
+
+/// @}
+#endif // PM_KAPA_PLOTS_H
Index: /tags/sj_tags/sj_root_20080929/psModules/src/extras/psIOBuffer.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/extras/psIOBuffer.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/extras/psIOBuffer.c	(revision 22322)
@@ -0,0 +1,113 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <pslib.h>
+#include "psIOBuffer.h"
+
+static void psIOBufferFree (psIOBuffer *buffer)
+{
+    if (buffer == NULL)
+        return;
+
+    psFree (buffer->data);
+    return;
+}
+
+psIOBuffer *psIOBufferAlloc (int nBuffer)
+{
+
+    psIOBuffer *buffer = (psIOBuffer *)psAlloc(sizeof(psIOBuffer));
+    psMemSetDeallocator(buffer, (psFreeFunc) psIOBufferFree);
+
+    buffer->data = (char *) psAlloc (nBuffer + 1);
+
+    buffer->nAlloc = nBuffer + 1;
+    buffer->nReset = nBuffer;
+    buffer->nBlock = nBuffer / 2;
+    buffer->n = 0;
+    return (buffer);
+}
+
+bool psIOBufferFlush (psIOBuffer *buffer)
+{
+
+    if (buffer == NULL)
+        return false;
+    buffer->n = 0;
+    buffer->nAlloc = buffer->nReset;
+    buffer->data = psRealloc (buffer->data, buffer->nAlloc);
+    memset(buffer->data, '\0', buffer->nAlloc);
+
+    return true;
+}
+
+int psIOBufferRead (psIOBuffer *buffer, int fd)
+{
+
+    int Nread, Nfree;
+
+    if (fd == 0) {
+        /* pipe is closed */
+        return (0);
+    }
+
+    Nfree = buffer->nAlloc - buffer->n - 1;
+
+    // extend the data block if needed
+    if (Nfree < buffer->nBlock) {
+        buffer->nAlloc += 2*buffer->nBlock + 1;
+        buffer->data = psRealloc (buffer->data, buffer->nAlloc);
+        Nfree = buffer->nAlloc - buffer->n;
+        memset(buffer->data + buffer->n, '\0', Nfree);
+    }
+
+    // attempt to read from the fd into the buffer
+    Nread = read (fd, &buffer->data[buffer->n], buffer->nBlock);
+
+    if (Nread >= 0) {
+        buffer->n += Nread;
+        buffer->data[buffer->n] = 0;
+        return (Nread);
+    }
+
+    // check on exit status (try again if waiting for non-blocking fd)
+    if (Nread == -1) {
+        switch (errno) {
+        case EAGAIN:
+        case EIO:
+            /** no data available in pipe **/
+            return (-1);
+        default:
+            /** error reading from pipe **/
+            psError (PS_ERR_IO, true, "error on psIOBufferRead");
+            return (-2);
+        }
+    }
+    return (Nread);
+}
+
+/* read until buffer is empty (Nmax retries) */
+int psIOBufferReadEmpty (psIOBuffer *buffer, int Nmax, int fd)
+{
+
+    int i, status;
+
+    status = -1;
+    for (i = 0; (status != 0) && (i < Nmax); i++) {
+        status = psIOBufferRead (buffer, fd);
+        if (status == -2)
+            return false;
+        if (status == -1)
+            usleep (10000);
+        if (status > 0)
+            i = 0;
+    }
+    if (status == -1)
+        return false;
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/extras/psIOBuffer.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/extras/psIOBuffer.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/extras/psIOBuffer.h	(revision 22322)
@@ -0,0 +1,34 @@
+/* @file  IOBuffer.h
+ * @brief input/output character buffer
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-24 02:54:15 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PS_IO_BUFFER_H
+#define PS_IO_BUFFER_H
+
+/// @addtogroup Extras Miscellaneous Funtions
+/// @{
+
+typedef struct
+{
+    char *data;
+    int nAlloc;    // current size of allocated buffer
+    int nReset;    // size to set buffer after flush
+    int nBlock;    // number of bytes to try to read at a time
+    int n;    // current size of filled data
+}
+psIOBuffer;
+
+// psIOBuffer functions
+psIOBuffer *psIOBufferAlloc (int nBuffer);
+bool psIOBufferFlush (psIOBuffer *buffer);
+int psIOBufferRead (psIOBuffer *buffer, int fd);
+int psIOBufferReadEmpty (psIOBuffer *buffer, int maxRetries, int fd);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/extras/psPipe.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/extras/psPipe.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/extras/psPipe.c	(revision 22322)
@@ -0,0 +1,210 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pslib.h>
+#include <psPipe.h>
+
+void closePipes (int *stdin_fd, int *stdout_fd, int *stderr_fd)
+{
+
+    if (stdin_fd[0]  != 0)
+        close (stdin_fd[0]);
+    if (stdin_fd[1]  != 0)
+        close (stdin_fd[0]);
+    if (stdout_fd[0] != 0)
+        close (stdout_fd[0]);
+    if (stdout_fd[1] != 0)
+        close (stdout_fd[1]);
+    if (stderr_fd[0] != 0)
+        close (stderr_fd[0]);
+    if (stderr_fd[1] != 0)
+        close (stderr_fd[1]);
+}
+
+static void psPipeFree (psPipe *pipe)
+{
+    return;
+}
+
+psPipe *psPipeAlloc ()
+{
+    psPipe *pipe = (psPipe *)psAlloc(sizeof(psPipe));
+    psMemSetDeallocator(pipe, (psFreeFunc) psPipeFree);
+
+    pipe->fd_stdin  = 0;
+    pipe->fd_stdout = 0;
+    pipe->fd_stderr = 0;
+    return (pipe);
+}
+
+psPipe *psPipeOpen (char *command)
+{
+
+    int stdin_fd[2], stdout_fd[2], stderr_fd[2], status;
+    pid_t pid;
+
+    memset(stdin_fd,  '\0', 2*sizeof(int));
+    memset(stdout_fd, '\0', 2*sizeof(int));
+    memset(stderr_fd, '\0', 2*sizeof(int));
+
+    if (pipe (stdin_fd)  < 0) {
+        psError (PS_ERR_UNKNOWN, true, "cannot create pipe file descriptor");
+        closePipes (stdin_fd, stdout_fd, stderr_fd);
+        return NULL;
+    }
+    if (pipe (stdout_fd) < 0) {
+        psError (PS_ERR_UNKNOWN, true, "cannot create pipe file descriptor");
+        closePipes (stdin_fd, stdout_fd, stderr_fd);
+        return NULL;
+    }
+    if (pipe (stderr_fd) < 0) {
+        psError (PS_ERR_UNKNOWN, true, "cannot create pipe file descriptor");
+        closePipes (stdin_fd, stdout_fd, stderr_fd);
+        return NULL;
+    }
+
+    psArray *cmd = psStringSplitArray (command, " ", false);
+    if (cmd->n <= 0) {
+        psError (PS_ERR_UNKNOWN, true, "empty command for pipe");
+        psFree (cmd);
+        closePipes (stdin_fd, stdout_fd, stderr_fd);
+        return NULL;
+    }
+
+    // create the command line array needed by execvp
+    char **argv = (char **)psAlloc((cmd->n+1)*sizeof(char *));
+    for (int i = 0; i < cmd->n; i++) {
+        argv[i] = cmd->data[i];
+    }
+    argv[cmd->n] = NULL;
+
+    pid = fork ();
+    if (!pid) { /* must be child process */
+        /* close the other ends of the pipes */
+        close (stdin_fd[1]);
+        close (stdout_fd[0]);
+        close (stderr_fd[0]);
+
+        /* tie our ends of the pipes to stdin, stdout, stderr */
+        dup2 (stdin_fd[0],  STDIN_FILENO);
+        dup2 (stdout_fd[1], STDOUT_FILENO);
+        dup2 (stderr_fd[1], STDERR_FILENO);
+
+        /* set all three unblocking */
+        setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+        setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+        setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+        status = execvp (argv[0], argv);
+
+        // this statement exits the child, not the parent, process
+        exit (1);
+    }
+    psFree (cmd);
+    psFree (argv);
+
+    if (pid == -1) {
+        psError (PS_ERR_UNKNOWN, true, "unable to create child process");
+        closePipes (stdin_fd, stdout_fd, stderr_fd);
+        return NULL;
+    }
+
+    /* close the other ends of the pipes */
+    close (stdin_fd[0]);
+    stdin_fd[0]  = 0;
+    close (stdout_fd[1]);
+    stdout_fd[1] = 0;
+    close (stderr_fd[1]);
+    stderr_fd[1] = 0;
+
+    /* make the pipes non-blocking */
+    fcntl (stdin_fd[1],  F_SETFL, O_NONBLOCK);
+    fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+    fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+
+    psPipe *pipe = psPipeAlloc();
+
+    pipe->pid    = pid;
+    pipe->fd_stdin  = stdin_fd[1];
+    pipe->fd_stdout = stdout_fd[0];
+    pipe->fd_stderr = stderr_fd[0];
+
+    return (pipe);
+}
+
+// this function returns the exit status of the called function
+// or a value > 255 on an error
+int psPipeClose (psPipe *pipe)
+{
+    int close_status;
+    int exit_status;
+    int wait_status;
+    int result;
+
+    PS_ASSERT_PTR_NON_NULL(pipe, false);
+
+    close_status = true;
+    if (close (pipe->fd_stdin) != 0) {
+        psError(PS_ERR_IO, true, "error closing the pipe stdin (pid %d, error %s)\n", pipe->pid, strerror(errno));
+        close_status = false;
+    }
+    if (close (pipe->fd_stdout) != 0) {
+        psError(PS_ERR_IO, true, "error closing the pipe stdout (pid %d, error %s)\n", pipe->pid, strerror(errno));
+        close_status = false;
+    }
+    if (close (pipe->fd_stderr) != 0) {
+        psError(PS_ERR_IO, true, "error closing the pipe sterr (pid %d, error %s)\n", pipe->pid, strerror(errno));
+        close_status = false;
+    }
+
+    // we expect the child process to have exited.
+    // wait for the exit condition, but no longer than 100ms
+    for (int i = 0; i < 10; i++) {
+        result = waitpid (pipe->pid, &wait_status, WNOHANG);
+        switch (result) {
+        case -1:   // error on waitpid
+            switch (errno) {
+            case ECHILD:
+                psError(PS_ERR_IO, true, "unknown PID, not a child process: %d\n", pipe->pid);
+                return 0x100;
+            default:
+                psAbort("unexpected response to waitpid: %d\n", result);
+            }
+            break;
+
+        case 0:   // child not yet exited
+            usleep (10000);
+            continue;
+
+        default:
+            if (result != pipe->pid) {
+                psAbort("waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pipe->pid);
+            }
+            if (WIFEXITED(wait_status)) {
+                exit_status = WEXITSTATUS(wait_status);
+                if (close_status) {
+                    return exit_status;
+                } else {
+                    return (0x100);
+                }
+            }
+            if (WIFSIGNALED(wait_status)) {
+                psError(PS_ERR_IO, true, "job %d exited on signal %d\n", pipe->pid, WTERMSIG(wait_status));
+                return (0x100 + WTERMSIG(wait_status));
+            }
+            if (WIFSTOPPED(wait_status)) {
+                psAbort("waitpid returns 'stopped' programming error\n");
+            }
+        }
+    }
+    psError(PS_ERR_IO, true, "child process pid %d did not exit\n", pipe->pid);
+    return 0x100;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/extras/psPipe.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/extras/psPipe.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/extras/psPipe.h	(revision 22322)
@@ -0,0 +1,33 @@
+/* @file  psPipe.h
+ * @brief 3-stream pipe 
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-24 02:54:15 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PS_PIPE_H
+#define PS_PIPE_H
+
+/// @addtogroup Extras Miscellaneous Funtions
+/// @{
+
+// move these to pslib??
+typedef struct
+{
+    int pid;
+    int fd_stdin;
+    int fd_stdout;
+    int fd_stderr;
+}
+psPipe;
+
+// psPipe functions
+psPipe *psPipeAlloc ();
+psPipe *psPipeOpen (char *command);
+int     psPipeClose (psPipe *pipe);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/extras/psVectorBracket.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/extras/psVectorBracket.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/extras/psVectorBracket.c	(revision 22322)
@@ -0,0 +1,137 @@
+/** @file  psVectorBracket.c
+ *
+ *  Vector Bracketing tools
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-12-10 18:27:26 $
+ *
+ *  Copyright 2006 Institute for Astronomy, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+#include "psVectorBracket.h"
+
+// return the last entry below or first entry above key value
+int psVectorBracket(const psVector *index, psF32 key, bool above)
+{
+
+    int N;
+    int Nlo = 0;
+    int Nhi = index->n;
+
+    if (above) {
+        while (Nhi - Nlo > 10) {
+            N = 0.5*(Nlo + Nhi);
+            if (index->data.F32[N] > key) {
+                Nhi = N;
+            } else {
+                Nlo = N - 1;
+            }
+        }
+        // at this point, index[Nhi] > key >= index[Nlo]
+        N = Nlo;
+        while ((index->data.F32[N] <= key) && (N < Nhi)) {
+            N++;
+        }
+        return (N);
+    }
+    while (Nhi - Nlo > 10) {
+        N = 0.5*(Nlo + Nhi);
+        if (index->data.F32[N] < key) {
+            Nlo = N;
+        } else {
+            Nhi = N + 1;
+        }
+    }
+    // at this point, index[Nhi] >= key > index[Nlo]
+    N = Nhi;
+    while ((index->data.F32[N] >= key) && (N > Nlo)) {
+        N--;
+    }
+    return (N);
+}
+
+// return the last entry below or first entry above key value (reverse sorted input)
+int psVectorBracketDescend(const psVector *index, psF32 key, bool above)
+{
+
+    int N;
+    int Nhi = 0;
+    int Nlo = index->n;
+
+    if (above) {
+        while (Nlo - Nhi > 10) {
+            N = 0.5*(Nhi + Nlo);
+            if (index->data.F32[N] < key) {
+                Nlo = N;
+            } else {
+                Nhi = N - 1;
+            }
+        }
+        // at this point, index[Nhi] >= key > index[Nlo]
+        N = Nhi;
+        while ((index->data.F32[N] >= key) && (N < Nlo)) {
+            N++;
+        }
+        return (N);
+    }
+    while (Nlo - Nhi > 10) {
+        N = 0.5*(Nhi + Nlo);
+        if (index->data.F32[N] > key) {
+            Nhi = N;
+        } else {
+            Nlo = N + 1;
+        }
+    }
+    // at this point, index[Nhi] > key >= index[Nlo]
+    N = Nlo;
+    while ((index->data.F32[N] <= key) && (N > Nhi)) {
+        N--;
+    }
+    return (N);
+}
+
+// search for the bins bounding key in index, interpolate the corresponding values
+psF32 psVectorInterpolate(const psVector *index, const psVector *value, psF32 key)
+{
+
+    int n0 = 0;
+    int n1 = 0;
+
+    // extrapolate at ends
+    if (key < index->data.F32[0]) {
+        n0 = 0;
+        n1 = 1;
+    }
+
+    // extrapolate at ends
+    if (key > index->data.F32[index->n-1]) {
+        n0 = index->n-2;
+        n1 = index->n-1;
+    }
+
+    if (n1 == 0) {
+        n0 = psVectorBracket (index, key, FALSE);
+        n1 = n0 + 1;
+    }
+
+    if (n0 == index->n-1) {
+        n1 = n0;
+        n0 = n1 - 1;
+    }
+
+    float dy = value->data.F32[n1] - value->data.F32[n0];
+    float dx = index->data.F32[n1] - index->data.F32[n0];
+    float dX = key - index->data.F32[n0];
+    float dY = dX * (dy/dx);
+    float result = value->data.F32[n0] + dY;
+    return result;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/extras/psVectorBracket.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/extras/psVectorBracket.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/extras/psVectorBracket.h	(revision 22322)
@@ -0,0 +1,22 @@
+/* @file  psVectorBracket.h
+ * @brief vector bracket functions
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-24 02:54:15 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+# ifndef PS_VECTOR_BRACKET_H
+# define PS_VECTOR_BRACKET_H
+
+/// @addtogroup Extras Miscellaneous Funtions
+/// @{
+
+int psVectorBracket(const psVector *index, psF32 key, bool above);
+int psVectorBracketDescend(const psVector *index, psF32 key, bool above);
+psF32 psVectorInterpolate(const psVector *index, const psVector *value, psF32 key);
+
+/// @}
+# endif /* PS_VECTOR_BRACKET_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/Makefile.am	(revision 22322)
@@ -0,0 +1,37 @@
+noinst_LTLIBRARIES = libpsmodulesimcombine.la
+
+libpsmodulesimcombine_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS)
+libpsmodulesimcombine_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+
+libpsmodulesimcombine_la_SOURCES = \
+	pmReadoutCombine.c	\
+	pmStack.c		\
+	pmStackReject.c		\
+	pmSubtraction.c		\
+	pmSubtractionEquation.c	\
+	pmSubtractionIO.c	\
+	pmSubtractionKernels.c	\
+	pmSubtractionMask.c	\
+	pmSubtractionMatch.c	\
+	pmSubtractionParams.c	\
+	pmSubtractionStamps.c	\
+	pmSubtractionThreads.c	\
+	pmPSFEnvelope.c
+
+pkginclude_HEADERS = \
+	pmImageCombine.h \
+	pmReadoutCombine.h	\
+	pmStack.h		\
+	pmStackReject.h		\
+	pmSubtraction.h		\
+	pmSubtractionEquation.h	\
+	pmSubtractionIO.h	\
+	pmSubtractionKernels.h	\
+	pmSubtractionMask.h	\
+	pmSubtractionMatch.h	\
+	pmSubtractionParams.h	\
+	pmSubtractionStamps.h	\
+	pmSubtractionThreads.h	\
+	pmPSFEnvelope.h
+
+CLEANFILES = *~
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/demo_psftool.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/demo_psftool.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/demo_psftool.c	(revision 22322)
@@ -0,0 +1,194 @@
+// This is a very quick and dirty test program for pmPSFEnvelope
+//
+// gcc -g demo_psftool.c -o demo_psftool `psmodules-config --cflags --libs` --std=gnu99 -Wall
+//
+// ./demo_psftool test MOPS.skycell.0198767.wrp*.psf
+//
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+// Add a single filename to the arguments as an array, so that it can be used with pmFPAfileBindFromArgs, etc
+static void fileList(const char *file, // The symbolic name for the file
+                     const char *name, // The name of the file
+                     const char *comment, // Description of the file
+                     pmConfig *config // Configuration
+    )
+{
+    psArray *files = psArrayAlloc(1); // Array with file names
+    files->data[0] = psStringCopy(name);
+    psMetadataAddArray(config->arguments, PS_LIST_TAIL, file, 0, comment, files);
+    psFree(files);
+    return;
+}
+
+
+
+int main(int argc, char *argv[])
+{
+    psLibInit(NULL);
+    pmConfig *config = pmConfigRead(&argc, argv, "PSF");
+    if (!config) {
+        psErrorStackPrint(stderr, "Error reading configuration.");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    psTraceSetLevel("psModules.imcombine", 5);
+    psTraceSetLevel("psModules.objects", 0);
+    psTraceSetLevel("psLib.math", 0);
+
+    pmModelClassInit();
+
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output", argv[1]);
+
+    psArray *files = psArrayAlloc(argc - 2);
+    for (int i = 2; i < argc; i++) {
+        psString name = NULL;           // Name of file list
+        psStringAppend(&name, "INPUT_%d", i);
+
+        fileList(name, argv[i], "Input PSF", config);
+
+        pmFPAfile *file = pmFPAfileDefineFromArgs(NULL, config, "PSPHOT.PSF.LOAD", name);
+        psFree(name);
+        if (!file) {
+            psErrorStackPrint(stderr, "Can't define PSF file from %s --> %s", name, argv[i]);
+            psFree(files);
+            psFree(config);
+            exit(PS_EXIT_SYS_ERROR);
+        }
+
+        files->data[i - 2] = psMemIncrRefCounter(file);
+    }
+
+    pmFPAfile *outFile = pmFPAfileDefineOutput(config, NULL, "PSPHOT.PSF.SAVE");
+    if (!outFile) {
+        psErrorStackPrint(stderr, "Can't define output PSF file");
+        psFree(files);
+        psFree(config);
+        exit(PS_EXIT_SYS_ERROR);
+    }
+    outFile->save = true;
+
+    // XXX This is a bit dodgy; should be more rigorous for a real system
+    {
+        pmFPAview *phuView = pmFPAviewAlloc(0);
+        phuView->chip = 0;
+        if (!pmFPAAddSourceFromView(outFile->fpa, "Envelope PSF", phuView, outFile->format)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to output.");
+            psFree(phuView);
+            return false;
+        }
+        psFree(phuView);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0);
+
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        psErrorStackPrint(stderr, "Problem in I/O");
+        psFree(view);
+        psFree(files);
+        psFree(config);
+        exit(PS_EXIT_SYS_ERROR);
+    }
+
+    pmChip *chip;
+    while ((chip = pmFPAviewNextChip(view, outFile->fpa, 1)) != NULL) {
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+            psErrorStackPrint(stderr, "Problem in I/O");
+            psFree(view);
+            psFree(files);
+            psFree(config);
+            exit(PS_EXIT_SYS_ERROR);
+        }
+
+#if 0
+        pmFPAfileActivate(config->files, "PSPHOT.PSF.SAVE", false);
+        pmCell *cell;
+        while ((cell = pmFPAviewNextCell(view, outFile->fpa, 1)) != NULL) {
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                psErrorStackPrint(stderr, "Problem in I/O");
+                psFree(view);
+                psFree(files);
+                psFree(config);
+                exit(PS_EXIT_SYS_ERROR);
+            }
+        }
+#endif
+
+        psArray *inputs = psArrayAlloc(files->n);
+        int numCols = 4501, numRows = 4751;
+        for (int i = 0; i < files->n; i++) {
+            pmFPAfile *file = files->data[i];
+            pmChip *chip = pmFPAviewThisChip(view, file->fpa);
+
+            pmPSF *psf = psMetadataLookupPtr(NULL, chip->analysis, "PSPHOT.PSF");
+            if (!psf) {
+                psErrorStackPrint(stderr, "Can't find PSF in file %d", i);
+                psFree(inputs);
+                psFree(files);
+                psFree(config);
+                exit(PS_EXIT_PROG_ERROR);
+            }
+
+#if 0
+            pmHDU *hdu = pmHDUGetLowest(file->fpa, chip, NULL);
+            int imaxis1 = psMetadataLookupS32(NULL, hdu->header, "IMAXIS1");
+            int imaxis2 = psMetadataLookupS32(NULL, hdu->header, "IMAXIS2");
+            if (imaxis1 == 0 || imaxis2 == 0) {
+                psErrorStackPrint(stderr, "Size of image %d can't be determined.", i);
+                psFree(inputs);
+                psFree(files);
+                psFree(config);
+                exit(PS_EXIT_SYS_ERROR);
+            }
+            if (numCols == 0 && numRows == 0) {
+                numCols = imaxis1;
+                numRows = imaxis2;
+            } else if (imaxis1 != numCols || imaxis2 != numRows) {
+                psErrorStackPrint(stderr, "Image %d differs in size: %dx%d vs %dx%d",
+                                  i, imaxis1, imaxis2, numCols, numRows);
+                psFree(inputs);
+                psFree(files);
+                psFree(config);
+                exit(PS_EXIT_SYS_ERROR);
+            }
+#endif
+
+            inputs->data[i] = psMemIncrRefCounter(psf);
+        }
+
+        pmPSF *psf = pmPSFEnvelope(numCols, numRows, inputs, 5, 20, "PS_MODEL_RGAUSS", 3, 3);
+        psFree(inputs);
+        if (!psf) {
+            psErrorStackPrint(stderr, "Can't generate envelope PSF.");
+            psFree(config);
+            exit(PS_EXIT_SYS_ERROR);
+        }
+
+        psMetadataAddPtr(chip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN, "Envelope PSF", psf);
+        psFree(psf);
+        chip->data_exists = true;
+
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            psErrorStackPrint(stderr, "Problem in I/O");
+            psFree(view);
+            psFree(files);
+            psFree(config);
+            exit(PS_EXIT_SYS_ERROR);
+            return false;
+        }
+    }
+
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        psErrorStackPrint(stderr, "Problem in I/O");
+        psFree(view);
+        psFree(files);
+        psFree(config);
+        exit(PS_EXIT_SYS_ERROR);
+    }
+
+    psFree(config);
+
+    exit(PS_EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmImageCombine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmImageCombine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmImageCombine.c	(revision 22322)
@@ -0,0 +1,683 @@
+/** @file  pmImageCombine.c
+ *
+ *  This file will perform image combination of several images of the
+ *  same field, produce a list of questionable pixels, then tag some
+ *  of those pixels as cosmic rays.
+ *
+ *  @author Paul Price, IfA (original prototype)
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-04-04 22:42:48 $
+ *
+ *  XXX: pmRejectPixels() has a known bug with the pmImageTransform() call.
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <config.h>
+#include <stdio.h>
+#include <math.h>
+#include "pslib.h"
+
+#define PIXEL_LIST_BUFFER 100           // Size of the pixel list buffer
+
+// Data structure for use as a buffer in combining pixels
+typedef struct
+{
+    psVector *pixels;                   // Pixel values
+    psVector *masks;                    // Pixel masks
+    psVector *errors;                   // Pixel errors
+    psStats *stats;                     // Statistics to use with combination
+}
+combineBuffer;
+
+void combineBufferFree(combineBuffer *buffer)
+{
+    psFree(buffer->pixels);
+    psFree(buffer->masks);
+    psFree(buffer->errors);
+    psFree(buffer->stats);
+}
+
+combineBuffer *combineBufferAlloc(long numImages // Number of images that will be combined
+                                 )
+{
+    combineBuffer *buffer = psAlloc(sizeof(combineBuffer));
+    psMemSetDeallocator(buffer, (psFreeFunc)combineBufferFree);
+
+    buffer->pixels = psVectorAlloc(numImages, PS_TYPE_F32);
+    buffer->masks = psVectorAlloc(numImages, PS_TYPE_MASK);
+    buffer->errors = psVectorAlloc(numImages, PS_TYPE_F32);
+    buffer->stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+
+    return buffer;
+}
+
+
+static bool combinePixels(psImage *combine, // Combined image, for output
+                          psArray *questionablePixels, // Array of rejection masks
+                          int x, int y, // Position in the images
+                          const psArray *images, // Array of input images
+                          const psArray *errors, // Array of input error images
+                          const psArray *masks, // Array of input masks
+                          psU32 maskVal, // Mask value
+                          psS32 numIter, // Number of rejection iterations
+                          psF32 sigmaClip, // Number of standard deviations at which to reject
+                          combineBuffer *buffer // Buffer for combination; to avoid multiple allocations
+                         )
+{
+    assert(combine);
+    assert(x >= 0 && x < combine->numCols);
+    assert(y >= 0 && y < combine->numRows);
+    assert(images);
+    int numImages = images->n;          // Number of images to combine
+    if (masks) {
+        assert(masks->n == numImages);
+    }
+    if (errors) {
+        assert(errors->n == numImages);
+    }
+    assert(numIter >= 0);
+    assert(sigmaClip > 0);
+    assert(!questionablePixels || (questionablePixels && questionablePixels->n == numImages));
+
+    if (buffer) {
+        psMemIncrRefCounter(buffer);
+    } else {
+        buffer = combineBufferAlloc(numImages);
+    }
+
+    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
+    psVector *pixelErrors = buffer->errors; // Errors for the pixel of interest
+    psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest
+    psStats *stats = buffer->stats;     // Statistics for combination
+
+    //
+    // Loop through each image, extract the pixel/mask/error data into psVectors.
+    //
+    if (!masks) {
+        psVectorInit(pixelMasks, 0);
+    }
+    if (!errors) {
+        pixelErrors = NULL;
+    }
+    for (int i = 0; i < numImages; i++) {
+        // Set the pixel data
+        psImage *image = images->data[i]; // Image of interest
+        pixelData->data.F32[i] = image->data.F32[y][x];
+        // Set the pixel mask data, if necessary
+        if (masks) {
+            psImage *mask = masks->data[i]; // Mask of interest
+            pixelMasks->data.U8[i] = mask->data.U8[y][x];
+        }        // Set the pixel error data, if necessary
+        if (errors) {
+            psImage *error = errors->data[i]; // Error image of interest
+            pixelErrors->data.F32[i] = error->data.F32[y][x];
+        }
+    }
+
+    //
+    // Iterate on the pixels, rejecting outliers
+    //
+    for (int iter = 0; iter < numIter; iter++) {
+        // Combine all the pixels, using the specified stat.
+        if (!psVectorStats(stats, pixelData, pixelErrors, pixelMasks, maskVal)) {
+            combine->data.F32[y][x] = NAN;
+            psFree(buffer);
+            return false;
+        }
+        float combinedPixel = stats->sampleMean; // Value of the combination
+
+        if (iter == 0) {
+            // Save the value produced with no rejection, since it may be useful later
+            // (if the rejection turns out to be unnecessary)
+            combine->data.F32[y][x] = combinedPixel;
+        }
+
+        //
+        // Reject all pixels that lie more that sigmaClip standard deviations from
+        // the combined pixel value.
+        //
+        int numRejects = 0;     // Number of rejections
+        float stdev = stats->sampleStdev;
+        for (int i = 0; i < numImages; i++) {
+            if (!(pixelMasks->data.U8[i] & maskVal) &&
+                    fabs(pixelData->data.F32[i] - combinedPixel) > sigmaClip * stdev) {
+                // Reject pixel as questionable
+                numRejects++;
+                pixelMasks->data.U8[i] = maskVal;
+                if (questionablePixels) {
+                    // Mark the pixel as questionable
+                    psPixels *qp = questionablePixels->data[i]; // Questionable pixels for this image
+                    int qpNum = qp->n; // Number of QPs in the image of interest
+                    if (qpNum >= qp->nalloc) {
+                        // Grow dynamically, if required
+                        qp = psPixelsRealloc(qp, qp->nalloc + PIXEL_LIST_BUFFER);
+                        questionablePixels->data[i] = qp;
+                    }
+                    qp->data[qpNum].x = x;
+                    qp->data[qpNum].y = y;
+                    qp->n++;
+                }
+            }
+        }
+
+        //
+        // If the number of rejected pixels is zero, then there's no point continuing the loop.
+        //
+        if (numRejects == 0) {
+            break;
+        }
+
+        //
+        // XXX: Is it possible to have all pixels rejected?  If so, we should exit the loop.
+        //
+    }
+
+    psFree(buffer);
+    return true;
+}
+
+psImage *pmCombineImages(
+    psImage *combine,                   ///< Combined image (output)
+    psArray **questionablePixels,       ///< Array of rejection masks
+    const psArray *images,              ///< Array of input images
+    const psArray *errors,              ///< Array of input error images
+    const psArray *masks,               ///< Array of input masks
+    psU32 maskVal,                      ///< Mask value
+    const psPixels *pixels,             ///< Pixels to combine
+    psS32 numIter,                      ///< Number of rejection iterations
+    psF32 sigmaClip                     ///< Number of standard deviations at which to reject
+)
+{
+    psTrace("psModules.imcombine", 3, "Calling pmCombineImages(%ld)\n", images->n);
+
+    PS_ASSERT_ARRAY_NON_NULL(images, NULL);
+    PS_ASSERT_INT_POSITIVE(images->n, NULL);
+    long numImages = images->n;          // Number of images
+    int numCols = ((psImage*)images->data[0])->numCols; // Size in x
+    int numRows = ((psImage*)images->data[0])->numRows; // Size in y
+
+    if (combine) {
+        PS_ASSERT_IMAGE_NON_NULL(combine, NULL);
+        PS_ASSERT_IMAGE_SIZE(combine, numCols, numRows, NULL);
+    }
+    if (questionablePixels && !*questionablePixels) {
+        PS_ASSERT_ARRAY_NON_NULL(*questionablePixels, NULL);
+        PS_ASSERT_ARRAY_SIZE(*questionablePixels, numImages, 0);
+    }
+    for (int i = 1; i < images->n; i++) {
+        psImage *image = images->data[i]; // Image of interest
+        PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+        PS_ASSERT_IMAGE_SIZE(image, numCols, numRows, NULL);
+    }
+    if (errors) {
+        PS_ASSERT_ARRAYS_SIZE_EQUAL(images, errors, NULL);
+        for (int i = 0; i < images->n; i++) {
+            psImage *error = errors->data[i];
+            PS_ASSERT_IMAGE_SIZE(error, numCols, numRows, NULL);
+            PS_ASSERT_IMAGE_TYPE(error, PS_TYPE_F32, NULL);
+        }
+    }
+    if (masks) {
+        PS_ASSERT_ARRAYS_SIZE_EQUAL(images, masks, NULL);
+        for (int i = 0; i < images->n; i++) {
+            psImage *mask  = masks->data[i];
+            PS_ASSERT_IMAGE_SIZE(mask, numCols, numRows, NULL);
+            PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, NULL);
+        }
+    }
+    PS_ASSERT_INT_POSITIVE(numIter, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN(sigmaClip, 0.0, NULL);
+
+    // Allocate and initialize the combined image, if necessary.
+    if (!combine) {
+        combine = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        if (pixels) {
+            // Set everything we're not working on to NAN
+            psImageInit(combine, NAN);
+        }
+    }
+
+    //
+    // Allocate the questionablePixels psArray, if necesssary, then create a psPixels
+    // struct for each image.
+    //
+    if (questionablePixels) {
+        if (*questionablePixels == NULL) {
+            *questionablePixels = psArrayAlloc(numImages);
+        } else if ((*questionablePixels)->n != numImages) {
+            *questionablePixels = psArrayRealloc(*questionablePixels, numImages);
+        }
+        for (int i = 0; i < numImages; i++) {
+            psFree((*questionablePixels)->data[i]);
+            (*questionablePixels)->data[i] = psPixelsAlloc(PIXEL_LIST_BUFFER);
+        }
+    }
+
+    combineBuffer *buffer = combineBufferAlloc(numImages); // Buffer for combination
+
+    if (pixels) {
+        // Only those specified pixels should be combined.
+
+        for (int p = 0; p < pixels->n; p++) {
+            int x = pixels->data[p].x; // Column of interest
+            int y = pixels->data[p].y; // Row of interest
+
+            if (!combinePixels(combine, questionablePixels ? *questionablePixels : NULL, x, y,
+                               images, errors, masks, maskVal, numIter, sigmaClip, buffer)) {
+                // Bad pixel --- no big deal
+                psErrorClear();
+            }
+        }
+    } else {
+        //
+        // We get here if there is a NULL list of pixels to combine.
+        // Therefore, we combine all pixels in all images.
+        //
+
+        //
+        // Loop over all pixels in all images, set the appropriate data, mask,
+        // error vectors, call psVectorStats(), and set the result in the
+        // combine image.
+        //
+        for (int y = 0; y < numRows; y++) {
+            for (int x = 0; x < numCols; x++) {
+                if (!combinePixels(combine, questionablePixels ? *questionablePixels : NULL, x, y,
+                                   images, errors, masks, maskVal, numIter, sigmaClip, buffer)) {
+                    // Bad pixel --- no big deal
+                    psErrorClear();
+                }
+            }
+        }
+    }
+
+    psFree(buffer);
+
+    psTrace("psModules.imcombine", 3, "Exiting pmCombineImages(%ld)\n", images->n);
+    return combine;
+}
+
+
+/******************************************************************************
+XXX: Directly from Paul Price
+ *****************************************************************************/
+static psF32 CalcGradient(
+    psImage *image,
+    psImage *imageMask,
+    psS32 x,
+    psS32 y
+)
+{
+    psTrace("psModules.imcombine", 4, "Calling CalcGradient(%d, %d)\n", x, y);
+    int num = 0;
+    psVector *pixels = psVectorAlloc(8, PS_TYPE_F32); // Array of pixels
+    psVector *mask = psVectorAlloc(8, PS_TYPE_U8); // Corresponding mask
+
+    // Get limits
+    int xMin = PS_MAX(x - 1, 0);
+    int xMax = PS_MIN(x + 1, image->numCols - 1);
+    int yMin = PS_MAX(y - 1, 0);
+    int yMax = PS_MIN(y + 1, image->numRows - 1);
+    if (imageMask != NULL) {
+        for (int j = yMin; j <= yMax; j++) {
+            for (int i = xMin; i <= xMax; i++) {
+                if ((i != x) && (j != y) && (0 == imageMask->data.U8[j][i])) {
+                    pixels->data.F32[num] = image->data.F32[j][i];
+                    mask->data.U8[num] = 0;
+                    num++;
+                } else {
+                    mask->data.U8[num] = 1;
+                }
+            }
+        }
+    } else {
+        //
+        // This code is simply the previous loop without the imageMask.
+        // XXX: Consider restructuring this.
+        //
+        for (int j = yMin; j <= yMax; j++) {
+            for (int i = xMin; i <= xMax; i++) {
+                if ((i != x) && (j != y)) {
+                    pixels->data.F32[num] = image->data.F32[j][i];
+                    mask->data.U8[num] = 0;
+                    num++;
+                } else {
+                    mask->data.U8[num] = 1;
+                }
+            }
+        }
+    }
+
+    pixels->n = num;
+    mask->n = num;
+
+    // Get the median
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    psVectorStats(stats, pixels, NULL, mask, 1);
+    float median = stats->sampleMedian;
+    psFree(stats);
+    psFree(pixels);
+    psFree(mask);
+
+    psTrace("psModules.imcombine", 4, "Exiting CalcGradient(%d, %d)\n", x, y);
+    return(median / image->data.F32[y][x]);
+}
+
+/******************************************************************************
+DetermineRegion(image, myOutToIn): for a psImage and a psPlaneTransform to that
+image, this routine determines the size of the input image which maps to that
+image, and returns the result in a psRegion struct.
+
+XXX: Basically, this routine is only guaranteed to work if the transform is
+linear.
+
+XXX: Shouldn't this functionality be part of psImageTransform()?
+ *****************************************************************************/
+static psRegion DetermineRegion(psImage *image,
+                                psPlaneTransform *myOutToIn)
+{
+    psTrace("psModules.imcombine", 4, "Calling DetermineRegion()\n");
+    psRegion myRegion;
+    myRegion.x0 = PS_MAX_F32;
+    myRegion.x1 = PS_MIN_F32;
+    myRegion.y0 = PS_MAX_F32;
+    myRegion.y1 = PS_MIN_F32;
+    psPlane in;
+    psPlane out;
+
+    in.x = 0.0;
+    in.y = 0.0;
+
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    in.x = (psF32) (image->numCols);
+    in.y = 0.0;
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    in.x = (psF32) (image->numCols);
+    ;
+    in.y = 0.0;
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    in.x = (psF32) (image->numCols);
+    in.y = (psF32) (image->numRows);
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    psTrace("psModules.imcombine", 4, "Exiting DetermineRegion()\n");
+    return(myRegion);
+}
+
+/******************************************************************************
+XXX: Don't we have a psLib function for this?
+ *****************************************************************************/
+static psImage *ImageConvertF32(psImage *image)
+{
+    psTrace("psModules.imcombine", 4, "Calling ImageConvertF32()\n");
+    psImage *imgF32 = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
+
+    for (psS32 i = 0 ; i < image->numRows ; i++) {
+        for (psS32 j = 0 ; j < image->numCols ; j++) {
+            imgF32->data.F32[i][j] = (psF32) image->data.U8[i][j];
+        }
+    }
+
+    psTrace("psModules.imcombine", 4, "Exiting ImageConvertF32()\n");
+    return(imgF32);
+}
+
+
+//
+// The following macros define how big the initial pixel list will be, and
+// how much it should be incremented when realloc'ed.
+//
+#define PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH 100
+#define PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH_INC 100
+/******************************************************************************
+pmRejectPixels(images, errors, inToOut, outToIn, rejThreshold,
+gradLimit)
+
+XXX: Optimization: we don't need to transform the entire mask image.
+XXX: The inToOut and outToIn transforms are confusing.  Verify that what
+     I think they mean syncs with PWP.
+ *****************************************************************************/
+psArray *pmRejectPixels(
+    const psArray *images,              ///< Array of input images
+    const psArray *masks,               ///< Array of input image masks
+    const psArray *errors,              ///< The pixels which were rejected in the combination
+    const psArray *inToOut,             ///< Transformation from input to output system
+    const psArray *outToIn,             ///< Transformation from output to input system
+    psF32 rejThreshold,                 ///< Rejection threshold
+    psF32 gradLimit                     ///< Gradient limit
+)
+{
+    psTrace("psModules.imcombine", 3, "Calling pmRejectPixels()\n");
+    PS_ASSERT_PTR_NON_NULL(images, NULL);
+    for (psS32 im = 0 ; im < images->n ; im++) {
+        psImage *tmpImage = (psImage *) images->data[im];
+        PS_ASSERT_IMAGE_NON_NULL(tmpImage, NULL);
+        PS_ASSERT_IMAGE_NON_EMPTY(tmpImage, NULL);
+        PS_ASSERT_IMAGE_TYPE(tmpImage, PS_TYPE_F32, NULL);
+        if (masks != NULL) {
+            PS_ASSERT_INT_EQUAL(images->n, masks->n, NULL);
+            psImage *tmpMask = (psImage *) masks->data[im];
+            PS_ASSERT_IMAGE_NON_NULL(tmpMask, NULL);
+            PS_ASSERT_IMAGE_NON_EMPTY(tmpMask, NULL);
+            PS_ASSERT_IMAGE_TYPE(tmpMask, PS_TYPE_F32, NULL);
+            PS_ASSERT_IMAGES_SIZE_EQUAL(tmpImage, tmpMask, NULL);
+        }
+        PS_ASSERT_IMAGES_SIZE_EQUAL(((psImage *) images->data[0]), tmpImage, NULL);
+    }
+    PS_ASSERT_PTR_NON_NULL(errors, NULL);
+    PS_ASSERT_PTR_NON_NULL(inToOut, NULL);
+    PS_ASSERT_PTR_NON_NULL(outToIn, NULL);
+    // Ensure that the psArray parameters have an element for each image.
+    psS32 numImages = images->n;
+    PS_ASSERT_INT_EQUAL(numImages, errors->n, NULL);
+    PS_ASSERT_INT_EQUAL(numImages, inToOut->n, NULL);
+    PS_ASSERT_INT_EQUAL(numImages, outToIn->n, NULL);
+
+    //
+    // Create the psArray of psPixelLists, one for each image, for rejected pixels.
+    //
+    psArray *rejects = psArrayAlloc(numImages);
+    for (psS32 im = 0 ; im < numImages ; im++) {
+        rejects->data[im] = (psPtr *) psPixelsAlloc(PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH);
+        ((psPixels *)(rejects->data[im]))->n = ((psPixels *)(rejects->data[im]))->nalloc;
+        psPixels *pixels = (psPixels *) rejects->data[im];
+        pixels->n = 0;
+    }
+    //
+    // rPtr is used to maintain a count of the questionable pixels for each image.
+    //
+    psVector *rPtr = psVectorAlloc(numImages, PS_TYPE_S32);
+    psVectorInit(rPtr, 0);
+
+    psS32 numCols = ((psImage *) images->data[0])->numCols;
+    psS32 numRows = ((psImage *) images->data[0])->numRows;
+    psRegion myRegion = psRegionSet(0, numCols-1, 0, numRows-1);
+    psU32 maskVal = 1;  // XXX: Is this appropriate?
+
+    psPlane *inCoords = psAlloc(sizeof(psPlane));
+    psPlane *outCoords = psAlloc(sizeof(psPlane));
+
+    for (psS32 im = 0 ; im < numImages ; im++) {
+        //
+        // Extract data from psArrays.
+        //
+        psPixels *pixelList = (psPixels *) errors->data[im];
+
+        psImage *currImage = (psImage *) images->data[im];
+        myRegion.x0 = 0;
+        myRegion.x1 = currImage->numCols;
+        myRegion.y0 = 0;
+        myRegion.y1 = currImage->numRows;
+        psPlaneTransform *myInToOut = (psPlaneTransform *) inToOut->data[im];
+        psPlaneTransform *myOutToIn = (psPlaneTransform *) outToIn->data[im];
+
+        //
+        // Create a psU8 mask image from the list of cosmic pixels.
+        //
+        psImage *maskImage = NULL;
+        maskImage = psPixelsToMask(maskImage, pixelList, myRegion, maskVal);
+        psImage *maskImageF32 = ImageConvertF32(maskImage);
+
+        //
+        // Transform that mask image into detector coordinate space
+        //
+        psRegion myRegionXForm = DetermineRegion(maskImageF32, myOutToIn);
+        psImage *transformedImage = psImageTransform(NULL, NULL, maskImageF32, NULL,
+                                    0, myOutToIn, myRegionXForm, NULL,
+                                    PS_INTERPOLATE_BILINEAR, 0);
+
+        //
+        // Loop over all cosmic pixels.  Transform their coords to detector space.
+        // If the value of the transformed mask is larger than rejThreshold, then
+        // this might be a cosmic ray pixel.  We then calculate the mean gradient
+        // in other images.
+        //
+
+        psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(PS_INTERPOLATE_BILINEAR,
+                                                                           transformedImage, NULL, NULL,
+                                                                           0, 0.0, 0.0, 0, 0, 0.0);
+
+        for (psS32 p = 0 ; p < pixelList->n ; p++) {
+            inCoords->x = 0.5 + (psF32) (pixelList->data[p]).x;
+            inCoords->y = 0.5 + (psF32) (pixelList->data[p]).y;
+            psPlaneTransformApply(outCoords, myInToOut, inCoords);
+            double maskVal;
+            if (!psImageInterpolate(&maskVal, NULL, NULL, outCoords->x, outCoords->y, interp)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+                psFree(interp);
+                psFree(maskImage);
+                psFree(maskImageF32);
+                psFree(transformedImage);
+                psFree(inCoords);
+                psFree(outCoords);
+                psFree(rejects);
+                return NULL;
+            }
+            if (maskVal > rejThreshold) {
+
+                // This is a possible cosmic array pixel.  We must calculate the gradient
+                // at this location in all input images.
+                psF32 meanGrads = 0.0;
+                psS32 numGrads = 0;
+                //
+                // Loop through all other images, calculate their mean gradient.
+                //
+                for (psS32 otherImg = 0 ; otherImg < numImages ; otherImg++) {
+                    if (im != otherImg) {
+                        // Map the outCoords to inCoords that for otherImg space.
+                        psImage *tmpMask = NULL;
+                        if (masks != NULL) {
+                            tmpMask = masks->data[otherImg];
+                        }
+                        psPlaneTransformApply(inCoords,
+                                              (psPlaneTransform * )outToIn->data[otherImg],
+                                              outCoords);
+                        psS32 xPix = (int)(inCoords->x + 0.5);
+                        psS32 yPix = (int)(inCoords->y + 0.5);
+                        if ((xPix >= 0) && (xPix <= ((psImage*)(images->data[otherImg]))->numCols - 1) &&
+                                (yPix >= 0) && (yPix <= ((psImage*)(images->data[otherImg]))->numRows - 1)) {
+                            meanGrads += CalcGradient(images->data[otherImg], tmpMask, xPix, yPix);
+                            numGrads++;
+                        }
+                    }
+                }
+                if (numGrads > 0) {
+                    meanGrads /= (psF32) numGrads;
+                } else {
+                    // XXX: my idea.  Verify with PWP:
+                    meanGrads = 1.0 + gradLimit;
+                }
+
+                // XXX: The SDRS and the prototype code differ significantly here:
+                // if (CalcGradient(inputs->data[i], pixelList->data.x, pixelList->data.y) < (gradLimit * meanGrads)) {
+                if (meanGrads < gradLimit) {
+                    //
+                    // Add this to the list of questionable pixels.  We must ensure that the
+                    // pixelList is large enough; if not, we realloc()
+                    //
+                    psS32 ptr = rPtr->data.S32[im];
+                    psPixels *pixelListPtr = (psPixels *) rejects->data[im];
+                    if (ptr >= pixelListPtr->nalloc) {
+                        rejects->data[im] = (psPtr *) psPixelsRealloc(((psPixels *) rejects->data[im]),
+                                            ((((psPixels *) rejects->data[im])->nalloc) + PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH_INC));
+                        // XXX: Can the realloc() fail?  Must we check for NULL?
+                    }
+
+                    ((psPixels *) rejects->data[im])->data[ptr].x = (pixelList->data[p]).x;
+                    ((psPixels *) rejects->data[im])->data[ptr].y = (pixelList->data[p]).y;
+                    (rPtr->data.S32[im])++;
+                    // XXX: this pixel ->n increment is wierd
+                    (((psPixels *) rejects->data[im])->n)++;
+                }
+            }
+        }
+
+        psFree(interp);
+        psFree(maskImage);
+        psFree(maskImageF32);
+        psFree(transformedImage);
+    }
+
+    psFree(inCoords);
+    psFree(outCoords);
+    psTrace("psModules.imcombine", 3, "Exiting pmRejectPixels()\n");
+    return(rejects);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmImageCombine.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmImageCombine.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmImageCombine.h	(revision 22322)
@@ -0,0 +1,44 @@
+/* @file  pmImageCombine.h
+ *
+ * This file will perform image combination of several images of the
+ * same field, produce a list of questionable pixels, then tag some
+ * of those pixels as cosmic rays.
+ *
+ * @author Paul Price, IfA (original prototype)
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-03-30 21:12:56 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PM_IMAGE_COMBINE_H
+#define PM_IMAGE_COMBINE_H
+
+/// @addtogroup imcombine Image Combinations
+/// @{
+
+psImage *pmCombineImages(
+    psImage *combine,                   ///< Combined image (output)
+    psArray **questionablePixels,       ///< Array of rejection masks
+    const psArray *images,              ///< Array of input images
+    const psArray *errors,              ///< Array of input error images
+    const psArray *masks,               ///< Array of input masks
+    psU32 maskVal,                      ///< Mask value
+    const psPixels *pixels,             ///< Pixels to combine
+    psS32 numIter,                      ///< Number of rejection iterations
+    psF32 sigmaClip                     ///< Number of standard deviations at which to reject
+);
+
+psArray *pmRejectPixels(
+    const psArray *images,              ///< Array of input images
+    const psArray *masks,               ///< Array of input image masks
+    const psArray *errors,              ///< The pixels which were rejected in the combination
+    const psArray *inToOut,             ///< Transformation from input to output system
+    const psArray *outToIn,             ///< Transformation from output to input system
+    psF32 rejThreshold,                 ///< Rejection threshold
+    psF32 gradLimit                     ///< Gradient limit
+);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmPSFEnvelope.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmPSFEnvelope.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmPSFEnvelope.c	(revision 22322)
@@ -0,0 +1,326 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmReadoutFake.h"
+
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmModelClass.h"
+#include "pmModelUtils.h"
+#include "pmPeaks.h"
+#include "pmSource.h"
+#include "pmSourceUtils.h"
+#include "pmSourceFitModel.h"
+#include "pmPSFtry.h"
+
+
+#include "pmPSFEnvelope.h"
+
+
+
+//#define TESTING                         // Enable test output
+#define PEAK_FLUX 1.0e4                 // Peak flux for each source
+#define SKY_VALUE 0.0e0                 // Sky value for fake image
+#define WEIGHT_VAL 3.0                  // Weighting for image
+#define WEIGHT_FACTOR 10.0              // Factor to multiply image by to get weighting
+#define PSF_STATS PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV // Statistics options for measuring PSF
+#define SOURCE_FIT_ITERATIONS 100       // Number of iterations for source fitting
+
+
+// XXX To do:
+//
+// * PSF variation when only a portion of the image is present (e.g., the edge of an FPA overlapping a
+// skycell) may mean a disastrously weird PSF in the missing regions.  To counter this, get a region of
+// validity for each PSF (perhaps from an associated mask, or have the user work it out), and taper the PSF
+// when outside this region (perhaps multiply the peak flux by a Gaussian whose arguments are the distance
+// from the valid region, and a width that the user supplies).
+
+
+// We deliberately do not include the calculation of and storing of residuals (data - model) for the PSF
+// model, because (1) there is no code in psModules to do this, and we're not going to implement it here; and
+// (2) this is intended to generate "nice" or "ideal" PSFs to feed into pmSubtraction (PSF matching code), so
+// any residuals will hopefully be dealt with by that.
+
+
+pmPSF *pmPSFEnvelope(int numCols, int numRows, // Size of original image
+                     const psArray *inputs, // Input PSF models
+                     int instances, // Number of instances per dimension
+                     int radius,        // Radius of each PSF
+                     const char *modelName,// Name of PSF model to use
+                     int xOrder, int yOrder // Order for PSF variation fit
+                     )
+{
+    PS_ASSERT_INT_POSITIVE(numCols, NULL);
+    PS_ASSERT_INT_POSITIVE(numRows, NULL);
+    PS_ASSERT_ARRAY_NON_NULL(inputs, NULL);
+    PS_ASSERT_INT_POSITIVE(instances, NULL);
+    PS_ASSERT_INT_POSITIVE(radius, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(modelName, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(xOrder, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(yOrder, NULL);
+
+    float xOrigSpacing = (float)(numCols - 2 * radius) / (float)(instances - 1); // Spacing between instances
+    float yOrigSpacing = (float)(numRows - 2 * radius) / (float)(instances - 1); // Spacing between instances
+    int fakeSpacing = 2 * radius + 1;   // Spacing between instances (x and y) in the fake image
+    int fakeSize = instances * fakeSpacing; // Size of fake image
+
+    // Generate list of fake sources (instances of the PSF)
+    int numFakes = PS_SQR(instances);   // Number of fake sources
+    psArray *fakes = psArrayAlloc(numFakes); // Fake sources
+    psVector *xOffset = psVectorAlloc(numFakes, PS_TYPE_S32); // X offset from fake position to image
+    psVector *yOffset = psVectorAlloc(numFakes, PS_TYPE_S32); // Y offset from fake position to image
+    for (int j = 0, index = 0; j < instances; j++) {
+        float yOrig = j * yOrigSpacing + radius; // Source position in original image
+        float yFake = j * fakeSpacing + radius; // Position in fake image
+        int dy = yFake - yOrig;         // Difference between fake and original position
+
+        for (int i = 0; i < instances; i++, index++) {
+            float xOrig = i * xOrigSpacing + radius; // Source position in original image
+            float xFake = i * fakeSpacing + radius; // Position in fake image
+            int dx = xFake - xOrig;     // Difference between fake and original position
+
+            pmSource *fake = pmSourceAlloc(); // Fake source
+            fake->peak = pmPeakAlloc(xFake - dx, yFake - dy, PEAK_FLUX, PM_PEAK_LONE);
+            fake->type = PM_SOURCE_TYPE_STAR;
+            fake->psfMag = -2.5 * log10(PEAK_FLUX);
+
+            psTrace("psModules.imcombine", 5, "Source %d: %.2f,%.2f\n",
+                    index, xOrig, yOrig);
+
+            fakes->data[index] = fake;
+            xOffset->data.S32[index] = dx;
+            yOffset->data.S32[index] = dy;
+        }
+    }
+
+    // Generate fake images with each PSF, and take the envelope
+    psImage *envelope = psImageAlloc(fakeSize, fakeSize, PS_TYPE_F32); // Image with envelope of PSFs
+    psImageInit(envelope, SKY_VALUE);
+    pmReadout *fakeRO = pmReadoutAlloc(NULL); // Fake readout
+    float maxRadius = 0.0;              // Maximum radius of sources
+    for (int i = 0; i < inputs->n; i++) {
+        pmPSF *psf = inputs->data[i];   // PSF of interest
+        pmResiduals *resid = psf->residuals;// PSF residuals
+        psf->residuals = NULL;
+        if (!pmReadoutFakeFromSources(fakeRO, fakeSize, fakeSize, fakes, xOffset, yOffset, psf,
+                                      NAN, radius, true)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate fake readout.");
+            psFree(envelope);
+            psFree(yOffset);
+            psFree(xOffset);
+            psFree(fakes);
+            psf->residuals = resid;
+            return NULL;
+        }
+        psf->residuals = resid;
+
+        // Need to renormalise sources so they all have the same peak.  You would think they do have the same
+        // peak already, but it seems that the residual map messes things up by adding extra flux
+        for (int j = 0; j < numFakes; j++) {
+            pmSource *source = fakes->data[j]; // Fake source
+            float x = source->peak->xf + xOffset->data.S32[j]; // x coordinate of source
+            float y = source->peak->yf + yOffset->data.S32[j]; // y coordinate of source
+
+            double flux = fakeRO->image->data.F32[(int)y][(int)x];
+            float norm = PEAK_FLUX / flux; // Normalisation for source
+            psRegion region = psRegionSet(x - radius, x + radius, y - radius, y + radius); // PSF region
+            psImage *subImage = psImageSubset(fakeRO->image, region); // Subimage of fake PSF
+            psImage *subEnv = psImageSubset(envelope, region); // Subimage of envelope
+            psBinaryOp(subImage, subImage, "*", psScalarAlloc(norm, PS_TYPE_F32));
+            psBinaryOp(subEnv, subEnv, "MAX", subImage);
+            psFree(subImage);
+            psFree(subEnv);
+
+            // Get the radius
+            pmModel *model = pmModelFromPSFforXY(psf, x, y, PEAK_FLUX); // Model for source
+            float srcRadius = model->modelRadius(model->params, PS_SQR(WEIGHT_VAL)); // Radius for source
+            if (srcRadius > maxRadius) {
+                maxRadius = srcRadius;
+            }
+            psFree(model);
+        }
+
+#ifdef TESTING
+        {
+            // Write out the PSF field
+            psString name = NULL;
+            psStringAppend(&name, "psf_field_%03d.fits", i);
+            psFits *fits = psFitsOpen(name, "w");
+            psFitsWriteImage(fits, NULL, fakeRO->image, 0, NULL);
+            psFitsClose(fits);
+            psFree(name);
+        }
+#endif
+
+    }
+    psFree(fakeRO);
+
+    if (maxRadius > radius) {
+        maxRadius = radius;
+    }
+
+#ifdef TESTING
+    {
+        // Write out the envelope
+        psFits *fits = psFitsOpen("psf_field_envelope.fits", "w");
+        psFitsWriteImage(fits, NULL, envelope, 0, NULL);
+        psFitsClose(fits);
+    }
+#endif
+
+    // Put the fake sources onto a full-size image
+    pmReadout *readout = pmReadoutAlloc(NULL); // Readout to contain envelope pixels
+    readout->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImageInit(readout->image, 0.0);
+    for (int i = 0; i < numFakes; i++) {
+        pmSource *source = fakes->data[i]; // Fake source
+        // Position of source on fake image
+        int xFake = source->peak->x + xOffset->data.S32[i];
+        int yFake = source->peak->y + yOffset->data.S32[i];
+        psRegion region = psRegionSet(xFake - radius, xFake + radius,
+                                      yFake - radius, yFake + radius); // PSF region
+        psImage *subImage = psImageSubset(envelope, region); // Subimage of fake PSF
+
+        // Position of source on "real" image
+        int x0 = source->peak->x - radius;
+        int y0 = source->peak->y - radius;
+
+        if (!psImageOverlaySection(readout->image, subImage, x0, y0, "=")) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to overlay PSF");
+            psFree(subImage);
+            psFree(readout);
+            psFree(xOffset);
+            psFree(yOffset);
+            psFree(fakes);
+            return NULL;
+        }
+        psFree(subImage);
+    }
+    psFree(xOffset);
+    psFree(yOffset);
+    psFree(envelope);
+
+    // XXX Setting the weight seems to be an art
+    // Can't set it too high so that pixels are rejected as insignificant
+    // Can't set it too low so that it's hard to get to the minimum
+    // Have also tried:
+    // *** readout->weight = (psImage*)psBinaryOp(NULL, readout->image, "*", readout->image);
+    // *** readout->weight = (psImage*)psBinaryOp(NULL, readout->image, "*", psScalarAlloc(WEIGHT_FACTOR, PS_TYPE_F32));
+    readout->weight = (psImage*)psBinaryOp(NULL, readout->image, "+", psScalarAlloc(WEIGHT_VAL, PS_TYPE_F32));
+    readout->mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+    psImageInit(readout->mask, 0);
+
+#ifdef TESTING
+    {
+        // Write out the envelope
+        psFits *fits = psFitsOpen("psf_field_full.fits", "w");
+        psFitsWriteImage(fits, NULL, readout->image, 0, NULL);
+        psFitsClose(fits);
+    }
+#endif
+
+    // Reset the sources to point to the new pixels, and measure the moments in preparation for PSF fitting
+    for (int i = 0; i < numFakes; i++) {
+        pmSource *source = fakes->data[i]; // Fake source
+        float x = source->peak->xf;     // x coordinates of source
+        float y = source->peak->yf;     // y coordinates of source
+
+        psFree(source->pixels);
+        psFree(source->weight);
+        psFree(source->maskView);
+        psFree(source->maskObj);
+        source->pixels = NULL;
+        source->weight = NULL;
+        source->maskView = NULL;
+        source->maskObj = NULL;
+
+        if (!pmSourceDefinePixels(source, readout, x, y, maxRadius)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define pixels for source.");
+            psFree(readout);
+            psFree(fakes);
+            return NULL;
+        }
+
+        if (!pmSourceMoments(source, maxRadius)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to measure moments for source.");
+            psFree(readout);
+            psFree(fakes);
+            return NULL;
+        }
+    }
+
+    // Don't assume Poisson errors
+    pmPSFOptions *options = pmPSFOptionsAlloc(); // Options for fitting a PSF
+    options->poissonErrorsPhotLMM = true;
+    options->poissonErrorsPhotLin = false;
+    options->poissonErrorsParams = true;
+    options->stats = psStatsAlloc(PSF_STATS);
+    options->radius = maxRadius;
+    options->psfTrendMode = PM_TREND_MAP;
+    options->psfTrendNx = xOrder;
+    options->psfTrendNy = yOrder;
+    options->psfFieldNx = numCols;
+    options->psfFieldNy = numRows;
+    options->psfFieldXo = 0;
+    options->psfFieldYo = 0;
+
+    pmSourceFitModelInit(SOURCE_FIT_ITERATIONS, 0.01, WEIGHT_VAL, true);
+
+    pmPSFtry *try = pmPSFtryModel(fakes, modelName, options, 0, 0xff);
+    psFree(options);
+    if (!try) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to fit PSF model to PSF envelope.");
+        psFree(readout);
+        psFree(fakes);
+        return NULL;
+    }
+
+    pmPSF *psf = psMemIncrRefCounter(try->psf); // Output PSF
+    psFree(try);
+
+#ifdef TESTING
+    {
+        // Need to translate peak flux --> integrated flux
+        pmModel *fakeModel = pmModelFromPSFforXY(psf, (float)numCols / 2.0, (float)numRows / 2.0,
+                                                 1.0); // Fake model, with central intensity of 1.0
+        float flux0 = fakeModel->modelFlux(fakeModel->params); // Flux for central intensity of 1.0
+        for (int i = 0; i < numFakes; i++) {
+            pmSource *source = fakes->data[i]; // Fake source
+            source->psfMag -= 2.5 * log10(flux0);
+        }
+
+        pmReadout *generated = pmReadoutAlloc(NULL); // Generated image
+        pmReadoutFakeFromSources(generated, numCols, numRows, fakes, NULL, NULL, psf, NAN, radius,
+                                 false);
+        {
+            psFits *fits = psFitsOpen("psf_field_model.fits", "w");
+            psFitsWriteImage(fits, NULL, generated->image, 0, NULL);
+            psFitsClose(fits);
+        }
+        psBinaryOp(generated->image, generated->image, "-", readout->image);
+        {
+            psFits *fits = psFitsOpen("psf_field_resid.fits", "w");
+            psFitsWriteImage(fits, NULL, generated->image, 0, NULL);
+            psFitsClose(fits);
+        }
+        psFree(generated);
+    }
+#endif
+
+    psFree(fakes);
+    psFree(readout);
+
+    return psf;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmPSFEnvelope.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmPSFEnvelope.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmPSFEnvelope.h	(revision 22322)
@@ -0,0 +1,22 @@
+#ifndef PM_PSF_ENVELOPE_H
+#define PM_PSF_ENVELOPE_H
+
+#include <pslib.h>
+#include <pmMoments.h>
+#include <pmResiduals.h>
+#include <pmGrowthCurve.h>
+#include <pmTrend2D.h>
+#include <pmPSF.h>
+
+/// Generate a PSF which is the envelope of an array of PSFs
+///
+/// Generates multiple instances of the PSFs (distributed over an image)
+pmPSF *pmPSFEnvelope(int numCols, int numRows, // Size of original image
+                     const psArray *inputs, // Input PSF models
+                     int instances,     // Number of instances per dimension
+                     int radius,        // Radius of each PSF
+                     const char *modelName, // Name of PSF model to use
+                     int xOrder, int yOrder // Order for PSF variation
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmReadoutCombine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmReadoutCombine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmReadoutCombine.c	(revision 22322)
@@ -0,0 +1,422 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "pmFPAMaskWeight.h"
+#include "pmConceptsAverage.h"
+#include "pmReadoutStack.h"
+
+#include "pmReadoutCombine.h"
+
+//#define SHOW_BUSY 1                   // Show that the function is busy
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Allocator for pmCombineParams
+pmCombineParams *pmCombineParamsAlloc(psStatsOptions combine)
+{
+    pmCombineParams *params = psAlloc(sizeof(pmCombineParams));
+
+    params->combine = combine;
+    params->maskVal = 0;
+    params->blank = 0;
+    params->nKeep = 0;
+    params->fracHigh = 0.0;
+    params->fracHigh = 0.0;
+    params->iter = 1;
+    params->rej = INFINITY;
+    params->weights = false;
+
+    return params;
+}
+
+// check the input parameters and set up the output images
+bool pmReadoutCombinePrepare(pmReadout *output, const psArray *inputs, const pmCombineParams *params)
+{
+    // Check inputs
+    PS_ASSERT_PTR_NON_NULL(output, false);
+    PS_ASSERT_ARRAY_NON_NULL(inputs, false);
+    PS_ASSERT_PTR_NON_NULL(params, false);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(params->fracLow, 0.0, 1.0, false);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(params->fracHigh, 0.0, 1.0, false);
+
+    // valid combintion statistic?
+    bool valid = false;
+    valid |= (params->combine == PS_STAT_SAMPLE_MEAN);
+    valid |= (params->combine == PS_STAT_SAMPLE_MEDIAN);
+    valid |= (params->combine == PS_STAT_ROBUST_MEDIAN);
+    valid |= (params->combine == PS_STAT_FITTED_MEAN);
+    valid |= (params->combine == PS_STAT_CLIPPED_MEAN);
+    if (!valid) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Combination method is not SAMPLE_MEAN, SAMPLE_MEDIAN, "
+                "ROBUST_MEDIAN, FITTED_MEAN or CLIPPED_MEAN.\n");
+        return false;
+    }
+
+    pmHDU *hdu = pmHDUFromReadout(output); // Output HDU
+    if (!hdu) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find HDU for readout.\n");
+        return false;
+    }
+
+    //  set the output header metadata
+    psString comment = NULL;        // Comment to add to header
+    psStringAppend(&comment, "Combining using statistic: %x", params->combine);
+    if (!hdu->header) {
+	hdu->header = psMetadataAlloc();
+    }
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
+    psFree(comment);
+
+    // note the clipping parameters, if used
+    if (params->combine == PS_STAT_CLIPPED_MEAN) {
+	psString comment = NULL;    // Comment to add to header
+	psStringAppend(&comment, "Combination clipping: %d iterations, rejection at %f sigma", params->iter, params->rej);
+	psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
+	psFree(comment);
+    }
+
+    // note the use of weights
+    if (params->weights) {
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK,
+                         "Using input weights to combine images", "");
+    }
+
+    // note the rejection fraction
+    float keepFrac = 1.0 - params->fracLow - params->fracHigh; // Fraction of pixels to keep
+    if (keepFrac != 1.0) {
+        psString comment = NULL;        // Comment to add to header
+        psStringAppend(&comment, "Min/max rejection: %f high, %f low, keep %d",
+                       params->fracHigh, params->fracLow, params->nKeep);
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
+        psFree(comment);
+    }
+
+    // note the mask value actually used
+    psMaskType maskVal = params->maskVal; // The mask value
+    if (maskVal) {
+        psString comment = NULL;        // Comment to add to header
+        psStringAppend(&comment, "Mask for combination: %x", maskVal);
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
+        psFree(comment);
+    }
+
+    // determine the output image size based on the input images
+    int row0, col0, numCols, numRows;
+    if (!pmReadoutStackSetOutputSize(&col0, &row0, &numCols, &numRows, inputs)) {
+        psError(PS_ERR_UNKNOWN, false, "problem setting output readout size.");
+        return false;
+    }
+
+    // generate the required output images based on the specified sizes
+    pmReadoutStackDefineOutput(output, col0, row0, numCols, numRows, true, params->weights, params->blank);
+    psTrace("psModules.imcombine", 7, "Output minimum: %d,%d\n", output->col0, output->row0);
+
+    // these calls allocate and save the requested images on the output analysis metadata
+    psImage *counts = pmReadoutSetAnalysisImage(output, PM_READOUT_STACK_ANALYSIS_COUNT, numCols, numRows, PS_TYPE_U16, 0);
+    if (!counts) {
+        return false;
+    }
+    psImage *sigma = pmReadoutSetAnalysisImage(output, PM_READOUT_STACK_ANALYSIS_SIGMA, numCols, numRows, PS_TYPE_F32, NAN);
+    if (!sigma) {
+        return false;
+    }
+
+    // Update the "concepts"
+    psList *inputCells = psListAlloc(NULL); // List of cells
+    for (long i = 0; i < inputs->n; i++) {
+        pmReadout *readout = inputs->data[i]; // Readout of interest
+        psListAdd(inputCells, PS_LIST_TAIL, readout->parent);
+    }
+    bool success = pmConceptsAverageCells(output->parent, inputCells, NULL, NULL, true);
+    psFree(inputCells);
+
+    // set these even though the values are not yet set
+    output->data_exists = true;
+    output->parent->data_exists = true;
+    output->parent->parent->data_exists = true;
+
+    return success;
+}
+
+// XXX: Maybe add support for S16 and S32 types.  Currently, only F32 supported.
+bool pmReadoutCombine(pmReadout *output, const psArray *inputs, const psVector *zero, const psVector *scale,
+                      const pmCombineParams *params)
+{
+    // Check inputs
+    PS_ASSERT_PTR_NON_NULL(output, false);
+    PS_ASSERT_ARRAY_NON_NULL(inputs, false);
+    PS_ASSERT_PTR_NON_NULL(params, false);
+    if (zero) {
+        PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, false);
+        PS_ASSERT_VECTOR_SIZE(zero, inputs->n, false);
+    }
+    if (scale) {
+        PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, false);
+        PS_ASSERT_VECTOR_SIZE(scale, inputs->n, false);
+    }
+    PS_ASSERT_FLOAT_WITHIN_RANGE(params->fracLow, 0.0, 1.0, false);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(params->fracHigh, 0.0, 1.0, false);
+
+    // does required/desired data exist?
+    for (int i = 0; i < inputs->n; i++) {
+        pmReadout *readout = inputs->data[i]; // Readout of interest
+        if (!readout->image) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Image data is missing for image %d.\n", i);
+            return false;
+        }
+        if (params->weights && !readout->weight) {
+            psError(PS_ERR_UNEXPECTED_NULL, true,
+                    "Rejection based on weights requested, but no weights supplied for image %d.\n", i);
+            return false;
+        }
+    }
+
+    pmHDU *hdu = pmHDUFromReadout(output); // Output HDU
+    if (!hdu) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find HDU for readout.\n");
+        return false;
+    }
+
+    // pthread_t id = pthread_self();
+    // char name[64];
+    // sprintf (name, "%x", (unsigned int) id);
+    // psTimerStart (name);
+
+    psStatsOptions combineStdev = 0; // Statistics option for weights
+    switch (params->combine) {
+      case PS_STAT_SAMPLE_MEAN:
+      case PS_STAT_SAMPLE_MEDIAN:
+        combineStdev = PS_STAT_SAMPLE_STDEV;
+        break;
+      case PS_STAT_ROBUST_MEDIAN:
+        combineStdev = PS_STAT_ROBUST_STDEV;
+        break;
+      case PS_STAT_FITTED_MEAN:
+        combineStdev = PS_STAT_FITTED_STDEV;
+        break;
+      case PS_STAT_CLIPPED_MEAN:
+        combineStdev = PS_STAT_CLIPPED_STDEV;
+        break;
+      default:
+        psAbort("Should never get here --- checked params->combine before.\n");
+    }
+
+    psStats *stats = psStatsAlloc(params->combine | combineStdev); // The statistics to use in the combination
+    if (params->combine == PS_STAT_CLIPPED_MEAN) {
+        stats->clipSigma = params->rej;
+        stats->clipIter = params->iter;
+    }
+
+    psImage *counts = pmReadoutGetAnalysisImage(output, PM_READOUT_STACK_ANALYSIS_COUNT);
+    if (!counts) {
+        return false;
+    }
+    psImage *sigma = pmReadoutGetAnalysisImage(output, PM_READOUT_STACK_ANALYSIS_SIGMA);
+    if (!sigma) {
+        return false;
+    }
+
+    stats->options |= combineStdev;
+
+    int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine
+    int xSize, ySize;                   // Size of the output image
+    if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize, inputs)) {
+        psError(PS_ERR_UNKNOWN, false, "No valid input readouts.");
+        return false;
+    }
+
+    // We loop through each pixel in the output image.  We loop through each input readout.  We determine if
+    // that output pixel is contained in the image from that readout.  If so, we save it in psVector pixels.
+    // If not, we set a mask for that element in pixels.  Then, we mask off pixels not between fracLow and
+    // fracHigh.  Then we call the vector stats routine on those pixels/mask.  Then we set the output pixel
+    // value to the result of the stats call.
+
+    psVector *pixels = psVectorAlloc(inputs->n, PS_TYPE_F32); // Stack of pixels
+    psF32 *pixelsData = pixels->data.F32; // Dereference pixels
+
+    psVector *mask   = psVectorAlloc(inputs->n, PS_TYPE_U8); // Mask for stack
+    psU8 *maskData = mask->data.U8;     // Dereference mask
+
+    psVector *weights = NULL;           // Stack of weights
+    psVector *errors = NULL;            // Stack of errors (sqrt of variance/weights), for psVectorStats
+    psF32 *weightsData = NULL;          // Dereference weights
+    if (params->weights) {
+        weights = psVectorAlloc(inputs->n, PS_TYPE_F32); // Stack of weights
+        weightsData = weights->data.F32;
+    }
+    psVector *index = NULL;             // The indices to sort the pixels
+
+    float keepFrac = 1.0 - params->fracLow - params->fracHigh; // Fraction of pixels to keep
+    psMaskType maskVal = params->maskVal; // The mask value
+
+    #ifndef PS_NO_TRACE
+    psTrace("psModules.imcombine", 3, "Iterating output: %d --> %d, %d --> %d\n",
+            minInputCols - output->col0, maxInputCols - output->col0,
+            minInputRows - output->row0, maxInputRows - output->row0);
+    if (psTraceGetLevel("psModules.imcombine") >= 3) {
+        for (int r = 0; r < inputs->n; r++) {
+            pmReadout *readout = inputs->data[r]; // Input readout
+            psTrace("psModules.imcombine", 3, "Iterating input %d: %d --> %d, %d --> %d\n", r,
+                    minInputCols - readout->col0, maxInputCols - readout->col0,
+                    minInputRows - readout->row0, maxInputRows - readout->row0);
+        }
+    }
+    #endif
+
+    // Dereference output products
+    psF32 **outputImage  = output->image->data.F32; // Output image
+    psU8  **outputMask   = output->mask->data.U8; // Output mask
+    psF32 **outputWeight = NULL; // Output weight map
+    if (output->weight) {
+        outputWeight = output->weight->data.F32;
+    }
+
+    psVector *invScale = NULL;          // Inverse scale; pre-calculated for efficiency
+    if (scale) {
+        invScale = (psVector*)psBinaryOp(NULL, psScalarAlloc(1.0, PS_TYPE_F32), "/", (const psPtr)scale);
+    }
+
+    for (int i = minInputRows; i < maxInputRows; i++) {
+        int yOut = i - output->row0; // y position on output readout
+
+        #ifdef SHOW_BUSY
+        if (psTraceGetLevel("psModules.imcombine") > 9) {
+            printf("Processing row %d\r", i);
+            fflush(stdout);
+        }
+        #endif
+
+        for (int j = minInputCols; j < maxInputCols; j++) {
+            int xOut = j - output->col0; // x position on output readout
+
+            int numValid = 0;           // Number of valid pixels in the stack
+            memset(maskData, 0, mask->n * sizeof(psU8)); // Reset the mask
+            for (int r = 0; r < inputs->n; r++) {
+                pmReadout *readout = inputs->data[r]; // Input readout
+                int yIn = i - readout->row0; // y position on input readout
+                int xIn = j - readout->col0; // x position on input readout
+                psImage *image = readout->image; // The readout image
+
+                pixelsData[r] = image->data.F32[yIn][xIn];
+                if (!isfinite(pixelsData[r])) {
+                    maskData[r] = 1;
+                    continue;
+                }
+
+                // Check mask
+                psImage *roMask = readout->mask; // The mask image
+                if (roMask && roMask->data.U8[yIn][xIn] & maskVal) {
+                    maskData[r] = 1;
+                    continue;
+                }
+
+                if (params->weights) {
+                    weightsData[r] = readout->weight->data.F32[yIn][xIn];
+                }
+
+                if (zero) {
+                    pixelsData[r] -= zero->data.F32[r];
+                }
+                if (scale) {
+                    pixelsData[r] *= invScale->data.F32[r];
+                    if (params->weights) {
+                        weightsData[r] *= invScale->data.F32[r] * invScale->data.F32[r];
+                    }
+                }
+
+                numValid++;
+            }
+
+            if (numValid == 0) {
+                outputMask[yOut][xOut] = params->blank;
+                outputImage[yOut][xOut] = NAN;
+                counts->data.U16[yOut][xOut] = 0;
+                sigma->data.F32[yOut][xOut] = NAN;
+                continue;
+            }
+
+            // Apply fracLow,fracHigh if there are enough pixels
+            if (numValid * keepFrac >= params->nKeep && keepFrac != 1.0) {
+                index = psVectorSortIndex(index, pixels);
+                int numLow = numValid * params->fracLow; // Number of low pixels to clip
+                int numHigh = numValid * params->fracHigh; // Number of high pixels to clip
+                // Low pixels
+                psS32 *indexData = index->data.S32; // Dereference index
+                for (int k = 0, numMasked = 0; numMasked < numLow && k < index->n; k++) {
+                    // Don't count the ones that are already masked
+                    if (!maskData[indexData[k]]) {
+                        maskData[indexData[k]] = 1;
+                        numMasked++;
+                        numValid--;
+                    }
+                }
+                // High pixels
+                for (int k = pixels->n - 1, numMasked = 0; numMasked < numHigh && k >= 0; k--) {
+                    // Don't count the ones that are already masked
+                    if (!maskData[indexData[k]]) {
+                        maskData[indexData[k]] = 1;
+                        numMasked++;
+                        numValid--;
+                    }
+                }
+            }
+            counts->data.U16[yOut][xOut] = numValid;
+
+            // XXXXX this step probably is very expensive : convert errors to variance everywhere?
+            if (params->weights) {
+                errors = (psVector*)psUnaryOp(errors, weights, "sqrt");
+            }
+
+            // Combination
+            if (!psVectorStats(stats, pixels, errors, mask, 1)) {
+                // Can't do much about it, but it's not worth worrying about
+                psErrorClear();
+                outputImage[yOut][xOut] = NAN;
+                outputMask[yOut][xOut] = params->blank;
+                if (params->weights) {
+                    outputWeight[yOut][xOut] = NAN;
+                }
+                sigma->data.F32[yOut][xOut] = NAN;
+            } else {
+                outputImage[yOut][xOut] = psStatsGetValue(stats, params->combine);
+                outputMask[yOut][xOut] = isfinite(outputImage[yOut][xOut]) ? 0 : params->blank;
+                if (params->weights) {
+                    float stdev = psStatsGetValue(stats, combineStdev);
+                    outputWeight[yOut][xOut] = PS_SQR(stdev); // Variance
+                    // XXXX this is not the correct formal error.
+                    // also, the weighted mean is not obviously the correct thing here
+                }
+                sigma->data.F32[yOut][xOut] = psStatsGetValue(stats, combineStdev);
+            }
+        }
+    }
+    #ifdef SHOW_BUSY
+    if (psTraceGetLevel("psModules.imcombine") > 9) {
+        printf("\n");
+    }
+    #endif
+
+    psFree(index);
+    psFree(pixels);
+    psFree(mask);
+    psFree(weights);
+    psFree(errors);
+    psFree(stats);
+    psFree(invScale);
+
+    // fprintf (stderr, "done with combine %x : %f sec\n", (unsigned int) id, psTimerMark (name));
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmReadoutCombine.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmReadoutCombine.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmReadoutCombine.h	(revision 22322)
@@ -0,0 +1,50 @@
+/* @file  pmReadoutCombine.h
+ * @brief Combine multiple readouts
+ *
+ * @author George Gusciora, MHPCC
+ * @author Paul Price, IfA
+ *
+ * @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:01:26 $
+ * Copyright 2004-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_READOUT_COMBINE_H
+#define PM_READOUT_COMBINE_H
+
+/// @addtogroup imcombine Image Combinations
+/// @{
+
+/// Combination parameters for pmReadoutCombine.
+///
+/// These values define how the combination is performed, and should not vary by detector, so that it can be
+/// re-used for multiple combinations.
+typedef struct {
+    psStatsOptions combine;             ///< Statistic to use when performing the combination
+    psMaskType maskVal;                 ///< Mask value
+    psMaskType blank;                   ///< Mask value to give blank (i.e., no data) pixels
+    int nKeep;                          ///< Mimimum number of pixels to keep
+    float fracHigh;                     ///< Fraction of high pixels to immediately throw
+    float fracLow;                      ///< Fraction of low pixels to immediately throw
+    int iter;                           ///< Number of iterations for clipping (for CLIPPED_MEAN only)
+    float rej;                          ///< Rejection threshould for clipping (for CLIPPED_MEAN only)
+    bool weights;                       ///< Use the supplied weights (instead of calculated stdev)?
+} pmCombineParams;
+
+// Allocator for pmCombineParams
+pmCombineParams *pmCombineParamsAlloc(psStatsOptions statsOptions ///< Statistic to use for combination
+                                     );
+
+// check the input parameters and set up the output images
+bool pmReadoutCombinePrepare(pmReadout *output, const psArray *inputs, const pmCombineParams *params);
+
+/// Combine multiple readouts, applying zero and scale, with optional minmax clipping
+bool pmReadoutCombine(pmReadout *output,///< Output readout; altered and returned
+                      const psArray *inputs,  ///< Array of input readouts (F32 image and weight, U8 mask)
+                      const psVector *zero, ///< Zero corrections to subtract from input, or NULL
+                      const psVector *scale, ///< Scale corrections to divide into input, or NULL
+                      const pmCombineParams *params ///< Combination parameters
+                     );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStack.c	(revision 22322)
@@ -0,0 +1,725 @@
+/** @file  pmStack.c
+ *
+ *  This file will perform image combination of several images of the
+ *  same field, produce a list of questionable pixels, then tag some
+ *  of those pixels as cosmic rays.
+ *
+ *  @author Paul Price, IfA
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.42 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-12 22:43:33 $
+ *  Copyright 2004-2007 Institute for Astronomy, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmReadoutStack.h"
+#include "pmConceptsAverage.h"
+
+#include "pmStack.h"
+
+#define PIXEL_LIST_BUFFER 100           // Number of entries to add to pixel list at a time
+#define PIXEL_MAP_BUFFER 2              // Number of entries to add to pixel map at a time
+#define VARIANCE_FACTORS                // Use variance factors when calculating the variances?
+#define NUM_DIRECT_STDEV 5              // For less than this number of values, measure stdev directly
+
+
+// Data structure for use as a buffer when combining pixels
+// Use of this structure means we don't have to do an allocation in the combination function for each pixel
+typedef struct {
+    psVector *pixels;                   // Pixel values
+    psVector *masks;                    // Pixel masks
+    psVector *variances;                // Pixel variances
+    psVector *weights;                  // Pixel weightings
+    psVector *sources;                  // Pixel sources (which image did they come from?)
+    psVector *sort;                     // Buffer for sorting (to get a robust estimator of the standard dev)
+} combineBuffer;
+
+static void combineBufferFree(combineBuffer *buffer)
+{
+    psFree(buffer->pixels);
+    psFree(buffer->masks);
+    psFree(buffer->variances);
+    psFree(buffer->weights);
+    psFree(buffer->sources);
+    psFree(buffer->sort);
+    return;
+}
+
+static combineBuffer *combineBufferAlloc(long numImages // Number of images that will be combined
+    )
+{
+    combineBuffer *buffer = psAlloc(sizeof(combineBuffer));
+    psMemSetDeallocator(buffer, (psFreeFunc)combineBufferFree);
+
+    buffer->pixels = psVectorAlloc(numImages, PS_TYPE_F32);
+    buffer->masks = psVectorAlloc(numImages, PS_TYPE_MASK);
+    buffer->variances = psVectorAlloc(numImages, PS_TYPE_F32);
+    buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32);
+    buffer->sources = psVectorAlloc(numImages, PS_TYPE_U16);
+    buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32);
+    return buffer;
+}
+
+
+// Deallocator for the stack data
+static void stackDataFree(pmStackData *data)
+{
+    psFree(data->readout);
+    psFree(data->reject);
+    psFree(data->inspect);
+    return;
+}
+
+
+// Determine a mean value and variance for the combination
+// Not using psVectorStats because it assumes that the "weights" are errors, and weights by 1/error^2
+static bool combinationMeanVariance(float *mean, // Mean value, to return
+                                    float *var, // Variance value, to return
+                                    const psVector *values, // Values to combine
+                                    const psVector *variances, // Pixel variances to combine
+                                    const psVector *weights // Weights to apply
+                                    )
+{
+    assert(mean);
+    assert(values && weights);
+    assert(values->n == weights->n);
+    assert((var && variances) || !var);
+    assert(!variances || variances->n == values->n);
+    assert(values->type.type == PS_TYPE_F32);
+    assert(!values || values->type.type == PS_TYPE_F32);
+    assert(weights->type.type == PS_TYPE_F32);
+
+    // We're not using the input pixel variances to generate a weighted average for the pixel flux (because
+    // that introduces systematic biases), so the variance of the output pixel value should simply be:
+    //     simga^2 = sum(weight_i^2 * sigma_i^2) / (sum(weight_i))^2
+    // This reduces, when the weights are all identically unity, to:
+    //     variance_combination = sum(variance_i) / N^2
+    // and if the variances are all equal:
+    //     variance_combination = variance_individual / N
+    // which makes sense --- the standard deviation of the combination is reduced by a factor of sqrt(N).
+
+    float sumValueWeight = 0.0;         // Sum of the value multiplied by the weight
+    float sumVarianceWeight = 0.0;     // Sum of the pixel variances multiplied by the global weights
+    float sumWeight = 0.0;              // Sum of the image weights
+    for (int i = 0; i < values->n; i++) {
+        sumValueWeight += values->data.F32[i] * weights->data.F32[i];
+        sumWeight += weights->data.F32[i];
+        if (variances) {
+            sumVarianceWeight += variances->data.F32[i] * PS_SQR(weights->data.F32[i]);
+        }
+    }
+
+    if (sumWeight <= 0) {
+        return false;
+    }
+
+    *mean = sumValueWeight / sumWeight;
+    if (var) {
+        *var = sumVarianceWeight / PS_SQR(sumWeight);
+    }
+    return true;
+}
+
+// Return the median and standard deviation for the pixels
+// Not using psVectorStats because it has additional allocations which slow things down
+static bool combinationMedianStdev(float *median, // Median value, to return
+                                   float *stdev, // Standard deviation value, to return
+                                   const psVector *values, // Values to combine
+                                   const psVector *masks, // Mask to apply
+                                   psVector *sortBuffer // Buffer for sorting
+                                   )
+{
+    assert(values);
+    assert(!masks || values->n == masks->n);
+    assert(values->type.type == PS_TYPE_F32);
+    assert(!masks || masks->type.type == PS_TYPE_MASK);
+    assert(sortBuffer && sortBuffer->nalloc >= values->n && sortBuffer->type.type == PS_TYPE_F32);
+
+    // Need to filter out clipped values
+    int num = 0;            // Number of valid values
+    for (int i = 0; i < values->n; i++) {
+        if (!masks || !masks->data.PS_TYPE_MASK_DATA[i]) {
+            sortBuffer->data.F32[num++] = values->data.F32[i];
+        }
+    }
+    sortBuffer->n = num;
+    if (!psVectorSortInPlace(sortBuffer)) {
+        return false;
+    }
+
+    if (num == 3) {
+        // Attempt to measure standard deviation with only three values (and one of those possibly corrupted)
+        *median = sortBuffer->data.F32[1];
+        if (stdev) {
+            float diff1 = sortBuffer->data.F32[0] - *median;
+            float diff2 = sortBuffer->data.F32[2] - *median;
+            // This factor of sqrt(2) might not be exact, but it's about right
+            *stdev = M_SQRT2 * PS_MIN(fabsf(diff1), fabsf(diff2));
+        }
+    } else {
+        *median = num % 2 ? sortBuffer->data.F32[num / 2] :
+            (sortBuffer->data.F32[num / 2 - 1] + sortBuffer->data.F32[num / 2]) / 2.0;
+        if (stdev) {
+            if (num <= NUM_DIRECT_STDEV) {
+                // If there are not many values, the direct standard deviation is better
+                double sum = 0.0;
+                for (int i = 0; i < num; i++) {
+                    sum += PS_SQR(sortBuffer->data.F32[i] - *median);
+                }
+                *stdev = sqrt(sum / (double)(num - 1));
+            } else {
+                // Standard deviation from the interquartile range
+                *stdev = 0.74 * (sortBuffer->data.F32[(int)(0.75 * num)] -
+                                 sortBuffer->data.F32[(int)(0.25 * num)]);
+            }
+        }
+    }
+
+    return true;
+}
+
+
+// Mark a pixel for inspection
+static inline void combineInspect(const psArray *inputs, // Stack data
+                                  int x, int y, // Pixel
+                                  int source // Source image index
+                                  )
+{
+    pmStackData *data = inputs->data[source]; // Stack data of interest
+    if (!data) {
+        psWarning("Can't find input data for source %d", source);
+        return;
+    }
+    data->inspect = psPixelsAdd(data->inspect, data->inspect->nalloc, x, y);
+    return;
+}
+
+// Given a stack of images, combine with optional rejection.
+// Pixels in the stack that are rejected are marked for subsequent inspection
+static void combinePixels(psImage *image, // Combined image, for output
+                          psImage *mask, // Combined mask, for output
+                          psImage *variance, // Combined variance map, for output
+                          const psArray *inputs, // Stack data
+                          const psVector *weights, // Global (single value) weights for data, or NULL
+                          const psVector *varFactors, // Variance factors for data
+                          const psVector *reject, // Indices of pixels to reject, or NULL
+                          int x, int y, // Coordinates of interest; frame of output image
+                          psMaskType maskVal, // Value to mask
+                          psMaskType bad, // Value to give bad pixels
+                          int numIter, // Number of rejection iterations
+                          float rej, // Number of standard deviations at which to reject
+                          bool useVariance, // Use variance for rejection when combining?
+                          bool safe,    // Combine safely?
+                          combineBuffer *buffer // Buffer for combination; to avoid multiple allocations
+                         )
+{
+    // Rudimentary error checking
+    assert(image);
+    assert(mask);
+    assert(inputs);
+    assert(numIter >= 0);
+    assert(buffer);
+    assert(varFactors);
+    assert((useVariance && variance) || !useVariance);
+
+    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
+    psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest
+    psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest
+    psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest
+    psVector *pixelSources = buffer->sources; // Sources for the pixel of interest
+    psVector *sort = buffer->sort;      // Sort buffer
+
+    // Extract the pixel and mask data
+    int num = 0;                        // Number of good images
+    for (int i = 0, j = 0; i < inputs->n; i++) {
+        // Check if this pixel has been rejected.  Assumes that the rejection pixel list is sorted --- it
+        // should be because of how pixelMapGenerate works
+        if (reject && reject->data.U16[j] == i) {
+            j++;
+            continue;
+        }
+
+        pmStackData *data = inputs->data[i]; // Stack data of interest
+        if (!data) {
+            continue;
+        }
+
+        int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
+        psImage *mask = data->readout->mask; // Mask of interest
+        if (mask->data.PS_TYPE_MASK_DATA[yIn][xIn] & maskVal) {
+            continue;
+        }
+
+        psImage *image = data->readout->image; // Image of interest
+        psImage *variance = data->readout->weight; // Variance ("weight") map of interest
+        pixelData->data.F32[num] = image->data.F32[yIn][xIn];
+        if (variance) {
+            pixelVariances->data.F32[num] = variance->data.F32[yIn][xIn];
+        }
+        pixelWeights->data.F32[num] = data->weight;
+        pixelSources->data.U16[num] = i;
+        num++;
+    }
+    pixelData->n = num;
+    if (variance) {
+        pixelVariances->n = num;
+    }
+    pixelWeights->n = num;
+    pixelSources->n = num;
+
+
+    // The sensible thing to do varies according to how many good pixels there are.
+    // Default option is that the pixel is bad
+    float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map
+    psMaskType maskValue = bad;         // Value for combined mask
+    switch (num) {
+      case 0:
+        // Nothing to combine: it's bad
+        break;
+      case 1: {
+          // Accept the single pixel unless we have to be safe
+          if (!safe) {
+              imageValue = pixelData->data.F32[0];
+              if (variance) {
+                  varianceValue = pixelVariances->data.F32[0];
+              }
+              maskValue = 0;
+          }
+          break;
+      }
+      case 2: {
+          // Accept the mean of the pixels only if we're going to reject based on the variance, or we're not
+          // playing safe
+          if (useVariance || !safe) {
+              float mean, var;   // Mean and variance from combination
+              if (combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
+                  imageValue = mean;
+                  varianceValue = var;
+                  maskValue = 0;
+              }
+          }
+          if (useVariance && numIter > 0) {
+              // Use variance to check that the two are consistent
+              float diff = pixelData->data.F32[0] - pixelData->data.F32[1];
+#ifdef VARIANCE_FACTORS
+              float sigma2 = pixelVariances->data.F32[0] * varFactors->data.F32[pixelSources->data.U16[0]] +
+                  pixelVariances->data.F32[1] * varFactors->data.F32[pixelSources->data.U16[1]];
+#else
+              float sigma2 = pixelVariances->data.F32[0] + pixelVariances->data.F32[1];
+#endif
+              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]);
+              }
+          }
+          break;
+      }
+      default: {
+          // Record the value derived with no clipping, because pixels rejected using the harsh clipping
+          // applied in the first pass might later be accepted.
+          float mean, var;           // Mean and variance of the combination
+          if (!combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
+              break;
+          }
+          imageValue = mean;
+          varianceValue = var;
+          maskValue = 0;
+
+          // Prepare for clipping iteration
+          if (numIter > 0) {
+              pixelMasks->n = num;
+              psVectorInit(pixelMasks, 0);
+              if (useVariance) {
+                  // Convert to rejection limits --- saves doing it later.
+                  // Using squared rejection limit because it's cheaper than sqrts
+                  float rej2 = PS_SQR(rej); // Rejection level squared
+                  for (int i = 0; i < num; i++) {
+#ifdef VARIANCE_FACTORS
+                      pixelVariances->data.F32[i] *= rej2 * varFactors->data.F32[pixelSources->data.U16[i]];
+#else
+                      pixelVariances->data.F32[i] *= rej2;
+#endif
+                  }
+              }
+          }
+
+          // The clipping that follows is solely to identify suspect pixels.
+          // These suspect pixels will be inspected in more detail by other functions.
+          int numClipped = INT_MAX;     // Number of pixels clipped per iteration
+          int totalClipped = 0;         // Total number of pixels clipped
+          for (int i = 0; i < numIter && numClipped > 0 && num - totalClipped > 2; i++) {
+              numClipped = 0;
+              float median, stdev;    // Median and stdev of the combination, for rejection
+
+              if (!combinationMedianStdev(&median, useVariance ? NULL : &stdev,
+                                          pixelData, pixelMasks, sort)) {
+                  psWarning("Bad median/stdev at %d,%d", x, y);
+                  break;
+              }
+
+              float limit = rej * stdev; // Rejection limit, when rejecting based on data properties
+
+// Mask a pixel for inspection
+#define MASK_PIXEL_FOR_INSPECTION() \
+    pixelMasks->data.PS_TYPE_MASK_DATA[j] = 0xff; \
+    combineInspect(inputs, x, y, pixelSources->data.U16[j]); \
+    numClipped++; \
+    totalClipped++;
+
+              for (int j = 0; j < num; j++) {
+                  if (pixelMasks->data.PS_TYPE_MASK_DATA[j]) {
+                      continue;
+                  }
+                  float diff = pixelData->data.F32[j] - median; // Difference from expected
+                  if (useVariance) {
+                      // Comparing squares --- cheaper than lots of sqrts
+                      // pixelVariances includes the variance factor and the rejection limit, from above
+                      if (PS_SQR(diff) > pixelVariances->data.F32[j]) {
+                          MASK_PIXEL_FOR_INSPECTION();
+                      }
+                  } else if (fabsf(diff) > limit) {
+                      MASK_PIXEL_FOR_INSPECTION();
+                  }
+              }
+          }
+          break;
+      }
+    }
+
+    image->data.F32[y][x] = imageValue;
+    mask->data.PS_TYPE_MASK_DATA[y][x] = maskValue;
+    if (variance) {
+        variance->data.F32[y][x] = varianceValue;
+    }
+
+    return;
+}
+
+
+// Ensure the input array of pmStackData is valid, and get some details out of it
+static bool validateInputData(bool *haveVariances, // Do we have variance maps in the sky images?
+                              bool *haveRejects, // Do we have lists of rejected pixels?
+                              int *num,    // Number of inputs
+                              int *numCols, int *numRows, // Size of (sky) images
+                              psArray *input // Input array of pmStackData to validate
+    )
+{
+    PS_ASSERT_ARRAY_NON_NULL(input, false);
+    *num = input->n;
+
+    pmStackData *data = NULL;           // First image off the rank, used as a template
+    for (int i = 0; !data && i < input->n; i++) {
+        data = input->data[i];
+    }
+    PS_ASSERT_PTR_NON_NULL(data, false);
+    assert(psMemGetDeallocator(data) == (psFreeFunc)stackDataFree); // Ensure it's the right type
+    *haveVariances = false;
+    PS_ASSERT_IMAGE_NON_NULL(data->readout->image, false);
+    PS_ASSERT_IMAGE_TYPE(data->readout->image, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGE_NON_NULL(data->readout->mask, false);
+    PS_ASSERT_IMAGE_TYPE(data->readout->mask, PS_TYPE_MASK, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(data->readout->image, data->readout->mask, false);
+    *numCols = data->readout->image->numCols;
+    *numRows = data->readout->image->numRows;
+    if (data->readout->weight) {
+        *haveVariances = true;
+        PS_ASSERT_IMAGE_NON_NULL(data->readout->weight, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(data->readout->image, data->readout->weight, false);
+        PS_ASSERT_IMAGE_TYPE(data->readout->weight, PS_TYPE_F32, false);
+    }
+    *haveRejects = (data->reject != NULL);
+
+    // Make sure the rest correspond with the first
+    for (int i = 1; i < *num; i++) {
+        pmStackData *data = input->data[i]; // Stack data for this input
+        if (!data) {
+            continue;
+        }
+        assert(psMemGetDeallocator(data) == (psFreeFunc)stackDataFree); // Ensure it's the right type
+        if (!data->readout) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "The readout is specified in some but not all inputs.");
+            return false;
+        }
+        if ((*haveRejects && !data->reject) || (data->reject && !*haveRejects)) {
+            psError(PS_ERR_UNEXPECTED_NULL, true,
+                    "The rejected pixels are specified in some but not all inputs.");
+            return false;
+        }
+        PS_ASSERT_IMAGE_NON_NULL(data->readout->image, false);
+        PS_ASSERT_IMAGE_NON_NULL(data->readout->mask, false);
+        PS_ASSERT_IMAGE_TYPE(data->readout->image, PS_TYPE_F32, false);
+        PS_ASSERT_IMAGE_TYPE(data->readout->mask, PS_TYPE_MASK, false);
+        PS_ASSERT_IMAGE_SIZE(data->readout->image, *numCols, *numRows, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(data->readout->image, data->readout->mask, false);
+        if (*haveVariances) {
+            PS_ASSERT_IMAGE_NON_NULL(data->readout->weight, false);
+            PS_ASSERT_IMAGES_SIZE_EQUAL(data->readout->image, data->readout->weight, false);
+            PS_ASSERT_IMAGE_TYPE(data->readout->weight, PS_TYPE_F32, false);
+        }
+    }
+
+    return true;
+}
+
+
+// Generate a "pixel map".
+//
+// A "pixel map" is an image-like structure containing a vector that contains the indices of images.  The idea
+// is to provide a reverse lookup for an array of pixel lists, so that the image for which a pixel is flagged
+// can be identified easily.
+static psArray *pixelMapGenerate(const psArray *input, // Data to stack
+                                 int minCols, int maxCols, int minRows, int maxRows // Bounds of interest
+    )
+{
+    int numCols = maxCols - minCols + 1, numRows = maxRows - minRows + 1; // Size of map
+
+    psArray *map = psArrayAlloc(numRows); // The pixel map
+    for (int y = 0; y < numRows; y++) {
+        map->data[y] = psArrayAlloc(numCols);
+    }
+
+    for (int i = 0; i < input->n; i++) {
+        pmStackData *data = input->data[i];
+        if (!data) {
+            continue;
+        }
+        assert(data->reject);
+        psPixels *pixels = data->reject; // The rejected pixels
+        for (int j = 0; j < pixels->n; j++) {
+            int x = pixels->data[j].x - minCols, y = pixels->data[j].y - minRows; // Coordinates of interest
+            if (x < 0 || x >= numCols || y < 0 || y >= numRows) {
+                continue;
+            }
+            psArray *columns = map->data[y]; // The columns for that row
+            psVector *images = columns->data[x]; // The images for that column
+            if (!images) {
+                images = columns->data[x] = psVectorAllocEmpty(PIXEL_MAP_BUFFER, PS_TYPE_U16);
+            }
+            int size = images->n;       // Element number at which to add
+            columns->data[x] = psVectorExtend(images, PIXEL_MAP_BUFFER, 1);
+            images->data.U16[size] = i;
+        }
+    }
+
+    return map;
+}
+
+// Query a "pixel map", by returning the list of image indices for a particular pixel.
+static psVector *pixelMapQuery(const psArray *map, // Pixel map
+                               int x0, int y0, // Offset into map
+                               int x, int y // Coordinates of interest
+    )
+{
+    // Adjust for offset
+    x -= x0;
+    y -= y0;
+
+    assert(y >= 0 && y < map->n);
+    psArray *colMap = map->data[y];     // Columns for that row
+    assert(x >= 0 && x < colMap->n);
+    return colMap->data[x];
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Constructor
+pmStackData *pmStackDataAlloc(pmReadout *readout, float weight, float addVariance)
+{
+    pmStackData *data = psAlloc(sizeof(pmStackData)); // Stack data, to return
+    psMemSetDeallocator(data, (psFreeFunc)stackDataFree);
+
+    data->readout = psMemIncrRefCounter(readout);
+    data->reject = NULL;
+    data->inspect = NULL;
+    data->weight = weight;
+    data->addVariance = addVariance;
+
+    return data;
+}
+
+/// Stack input images
+bool pmStackCombine(pmReadout *combined, psArray *input, psMaskType maskVal, psMaskType bad,
+                    int kernelSize, int numIter, float rej, bool entire, bool useVariance, bool safe)
+{
+    PS_ASSERT_PTR_NON_NULL(combined, false);
+    bool haveVariances;                 // Do we have the variance maps?
+    bool haveRejects;                   // Do we have lists of rejected pixels?
+    int num;                            // Number of inputs
+    int numCols, numRows;               // Size of (sky) images
+    if (!validateInputData(&haveVariances, &haveRejects, &num, &numCols, &numRows, input)) {
+        return false;
+    }
+    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
+    PS_ASSERT_INT_POSITIVE(bad, false);
+    PS_ASSERT_INT_NONNEGATIVE(numIter, false);
+    if (isnan(rej)) {
+        PS_ASSERT_INT_EQUAL(numIter, 0, false);
+    } else {
+        PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
+    }
+    if (haveRejects) {
+        // This is a subsequent combination, so expect that the image and mask already exist
+        PS_ASSERT_IMAGE_NON_NULL(combined->image, false);
+        PS_ASSERT_IMAGE_TYPE(combined->image, PS_TYPE_F32, false);
+        PS_ASSERT_IMAGE_NON_NULL(combined->mask, false);
+        PS_ASSERT_IMAGE_TYPE(combined->mask, PS_TYPE_MASK, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(combined->image, combined->mask, false);
+    }
+    if (useVariance && !haveVariances) {
+        psWarning("Unable to use variance in rejection if no variance maps supplied --- option turned off");
+        useVariance = false;
+    }
+
+    psVector *varFactors = psVectorAlloc(num, PS_TYPE_F32); // Variance factors for each image
+    psVector *weights = psVectorAlloc(num, PS_TYPE_F32); // Relative weighting for each image
+    psArray *stack = psArrayAlloc(num); // Stack of readouts
+    for (int i = 0; i < num; i++) {
+        pmStackData *data = input->data[i]; // Stack data for this input
+        if (!data) {
+            weights->data.F32[i] = 0.0;
+            continue;
+        }
+        weights->data.F32[i] = data->weight;
+        stack->data[i] = psMemIncrRefCounter(data->readout);
+        // Variance factor
+        float vf = psMetadataLookupF32(NULL, data->readout->parent->concepts, "CELL.VARFACTOR"); // Var factor
+        if (!isfinite(vf)) {
+            psWarning("Non-finite CELL.VARFACTOR for image %d --- setting to unity.", i);
+            vf = 1.0;
+        }
+        varFactors->data.F32[i] = vf;
+        if (isfinite(data->addVariance)) {
+            varFactors->data.F32[i] *= data->addVariance;
+        }
+        if (!haveRejects && !data->inspect) {
+            data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
+        }
+    }
+
+    int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine
+    int xSize, ySize;                   // Size of the output image
+    if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize,
+                                stack)) {
+        psError(PS_ERR_UNKNOWN, false, "Input stack is not valid.");
+        psFree(stack);
+        return false;
+    }
+    psFree(stack);
+    pmReadoutUpdateSize(combined, minInputCols, minInputRows, xSize, ySize, true, true, bad);
+    psTrace("psModules.imcombine", 1, "Have for combination [%d:%d,%d:%d] (%dx%d)\n",
+            minInputCols, maxInputCols, minInputRows, maxInputRows, xSize, ySize);
+
+    // Reduce combination area by the size of the kernel
+    minInputCols += kernelSize;
+    maxInputCols -= kernelSize;
+    minInputRows += kernelSize;
+    maxInputRows -= kernelSize;
+    psTrace("psModules.imcombine", 1, "Combining on [%d:%d,%d:%d]\n",
+            minInputCols, maxInputCols, minInputRows, maxInputRows);
+
+
+    // Buffer for combination
+    combineBuffer *buffer = combineBufferAlloc(num);
+
+    if (haveRejects) {
+        psImage *combinedImage = combined->image; // Combined image
+        psImage *combinedMask = combined->mask; // Combined mask
+        psImage *combinedVariance = combined->weight; // Combined variance map
+
+        psArray *pixelMap = pixelMapGenerate(input, minInputCols, maxInputCols,
+                                             minInputRows, maxInputRows); // Map of pixels to source
+        psPixels *pixels = NULL;            // Total list of pixels, with no duplicates
+        for (int i = 0; i < num; i++) {
+            pmStackData *data = input->data[i]; // Stacking data; contains the list of pixels
+            if (!data) {
+                continue;
+            }
+            pixels = psPixelsConcatenate(pixels, data->reject);
+        }
+        pixels = psPixelsDuplicates(pixels, pixels);
+
+        if (entire) {
+            // Combine entire image
+            for (int y = minInputRows; y < maxInputRows; y++) {
+                for (int x = minInputCols; x < maxInputCols; x++) {
+                    psVector *reject = pixelMapQuery(pixelMap, minInputCols, minInputRows,
+                                                     x, y); // Reject these images
+                    combinePixels(combinedImage, combinedMask, combinedVariance, input, weights, varFactors,
+                                  reject, x, y, maskVal, bad, numIter, rej, useVariance, safe, buffer);
+                }
+            }
+        } else {
+            // Only combine previously rejected pixels
+            for (int i = 0; i < pixels->n; i++) {
+                // Pixel coordinates are in the frame of the original image
+                int x = pixels->data[i].x, y = pixels->data[i].y; // Coordinates of interest
+                if (x < minInputCols || x >= maxInputCols || y < minInputRows || y >= maxInputRows) {
+                    continue;
+                }
+                psVector *reject = pixelMapQuery(pixelMap, minInputCols, minInputRows,
+                                                 x, y); // Reject these images
+                combinePixels(combinedImage, combinedMask, combinedVariance, input, weights, varFactors,
+                              reject, x, y, maskVal, bad, numIter, rej, useVariance, safe, buffer);
+            }
+        }
+        psFree(pixels);
+        psFree(pixelMap);
+    } else {
+        // Pull the products out, allocate if necessary
+        psImage *combinedImage = combined->image; // Combined image
+        if (!combinedImage) {
+            combined->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+            combinedImage = combined->image;
+        }
+        psImage *combinedMask = combined->mask; // Combined mask
+        if (!combinedMask) {
+            combined->mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+            combinedMask = combined->mask;
+        }
+
+        psImage *combinedVariance = combined->weight; // Combined variance map
+        if (haveVariances && !combinedVariance) {
+            combined->weight = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+            combinedVariance = combined->weight;
+        }
+
+        for (int y = minInputRows; y < maxInputRows; y++) {
+            for (int x = minInputCols; x < maxInputCols; x++) {
+                combinePixels(combinedImage, combinedMask, combinedVariance, input, weights, varFactors,
+                              NULL, x, y, maskVal, bad, numIter, rej, useVariance, safe, buffer);
+            }
+        }
+
+#ifndef PS_NO_TRACE
+        if (psTraceGetLevel("psModules.imcombine") >= 5) {
+            for (int i = 0; i < num; i++) {
+                pmStackData *data = input->data[i]; // Stack data for this input
+                if (!data || !data->inspect) {
+                    continue;
+                }
+                psTrace("psModules.imcombine", 5, "Image %d: %ld pixels to inspect.\n", i, data->inspect->n);
+            }
+        }
+#endif
+    }
+
+    psFree(weights);
+    psFree(buffer);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStack.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStack.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStack.h	(revision 22322)
@@ -0,0 +1,56 @@
+/* @file  pmStack.h
+ *
+ * This file will perform image combination of several images of the
+ * same field, produce a list of questionable pixels, then tag some
+ * of those pixels as defects.
+ *
+ * @author Paul Price, IfA
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-09-12 04:12:42 $
+ *
+ * Copyright 2004-2007 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_STACK_H
+#define PM_STACK_H
+
+#include <pslib.h>
+#include <pmHDU.h>
+#include <pmFPA.h>
+
+/// @addtogroup imcombine Image Combinations
+/// @{
+
+
+/// Container for input image
+typedef struct {
+    pmReadout *readout;                 ///< Warped readout (sky cell)
+    psPixels *reject;                   ///< Pixels to reject
+    psPixels *inspect;                  ///< Pixels to inspect
+    float weight;                       ///< Relative weighting for image
+    float addVariance;                  ///< Additional variance when rejecting
+} pmStackData;
+
+/// Constructor
+pmStackData *pmStackDataAlloc(pmReadout *readout, ///< Warped readout (sky cell)
+                              float weight, ///< Weight to apply
+                              float addVariance ///< Additional variance when rejecting
+    );
+
+/// Stack input images
+bool pmStackCombine(pmReadout *combined,///< Combined readout (output)
+                    psArray *input,     ///< Input array of pmStackData
+                    psMaskType maskVal, ///< Mask value of bad pixels
+                    psMaskType bad,     ///< Mask value to give rejected pixels
+                    int kernelSize,     ///< Half-size of the convolution kernel
+                    int numIter,        ///< Number of iterations
+                    float rej,          ///< Rejection limit (standard deviations)
+                    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)?
+    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStackReject.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStackReject.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStackReject.c	(revision 22322)
@@ -0,0 +1,311 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmSubtraction.h"
+#include "pmSubtractionThreads.h"
+#include "pmSubtractionKernels.h"
+
+#define PIXEL_LIST_BUFFER 100           // Number of pixels to add to list at a time
+
+//#define TESTING                         // Testing output
+
+// Mask values
+typedef enum {
+    PM_STACK_MASK_BAD      = 0x01,      // Bad pixel
+    PM_STACK_MASK_CONVOLVE = 0x02,      // Touching a bad pixel
+    PM_STACK_MASK_ALL      = 0xff,      // All mask bits
+} pmStackMask;
+
+static bool threaded = false;           // Running threaded?
+
+
+// Grow the rejection mask
+static inline bool stackRejectGrow(psImage *target,   // Target mask image (product)
+                                   psImage *source, // Source mask image (to be grown)
+                                   const pmSubtractionKernels *kernels, // Subtraction kernels
+                                   int numCols, int numRows, // Size of image
+                                   int xMin, int xMax, int yMin, int yMax, // Bounds of convolution
+                                   float poorFrac       // Fraction for "poor"
+    )
+{
+    int size = kernels->size;           // Half-size of convolution kernel
+    psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
+                                                              xMin + size + 1, yMin + size + 1); // Polynomial
+    int box = p_pmSubtractionBadRadius(NULL, kernels, polyValues, false, poorFrac); // Radius of bad box
+    psFree(polyValues);
+
+    if (box > 0) {
+        // Convolve a subimage, then stick it in the target
+        if (threaded) {
+            psMutexLock(source);
+        }
+        psImage *mask = psImageSubset(source, psRegionSet(xMin - box, xMax + box,
+                                                          yMin - box, yMax + box)); // Mask to convolve
+        if (threaded) {
+            psMutexUnlock(source);
+        }
+        psImage *convolved = psImageConvolveMask(NULL, mask, PM_STACK_MASK_BAD, PM_STACK_MASK_CONVOLVE,
+                                                 -box, box, -box, box); // Convolved mask
+        if (threaded) {
+            psMutexLock(source);
+        }
+        psFree(mask);
+        if (threaded) {
+            psMutexUnlock(source);
+        }
+
+        int numBytes = (xMax - xMin) * PSELEMTYPE_SIZEOF(PS_TYPE_MASK); // Number of bytes to copy
+        psAssert(convolved->numCols - 2 * box == xMax - xMin, "Bad number of columns");
+        psAssert(convolved->numRows - 2 * box == yMax - yMin, "Bad number of rows");
+
+        for (int yTarget = yMin, ySource = box; yTarget < yMax; yTarget++, ySource++) {
+            memcpy(&target->data.PS_TYPE_MASK_DATA[yTarget][xMin],
+                   &convolved->data.PS_TYPE_MASK_DATA[ySource][box], numBytes);
+        }
+        psFree(convolved);
+    }
+    return true;
+}
+
+// Thread entry for stackRejectGrow
+static bool stackRejectGrowThread(psThreadJob *job // Job to execute
+    )
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    psArray *args = job->args;          // Job arguments
+    psImage *target = args->data[0];    // Target mask image
+    psImage *source = args->data[1];    // Source mask image
+    const pmSubtractionKernels *kernels = args->data[2]; // Subtraction kernels
+    int numCols = PS_SCALAR_VALUE(args->data[3], S32); // Number of columns
+    int numRows = PS_SCALAR_VALUE(args->data[4], S32); // Number of rows
+    int xMin = PS_SCALAR_VALUE(args->data[5], S32); // Minimum x value
+    int xMax = PS_SCALAR_VALUE(args->data[6], S32); // Maximum x value
+    int yMin = PS_SCALAR_VALUE(args->data[7], S32); // Minimum y value
+    int yMax = PS_SCALAR_VALUE(args->data[8], S32); // Maximum y value
+    float poorFrac = PS_SCALAR_VALUE(args->data[9], F32); // Fraction for "poor"
+
+    return stackRejectGrow(target, source, kernels, numCols, numRows, xMin, xMax, yMin, yMax, poorFrac);
+}
+
+bool pmStackRejectThreadsInit(void)
+{
+    if (threaded) {
+        psAbort("Already running threaded.");
+    }
+    threaded = true;
+
+    if (!pmSubtractionThreaded()) {
+        pmSubtractionThreadsInit(NULL, NULL);
+    }
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PSMODULES_STACK_REJECT_GROW", 10);
+        task->function = &stackRejectGrowThread;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    return true;
+}
+
+
+psPixels *pmStackReject(const psPixels *in, int numCols, int numRows, float threshold, float poorFrac,
+                        const psArray *subRegions, const psArray *subKernels)
+{
+    PS_ASSERT_PIXELS_NON_NULL(in, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(threshold, 0.0, NULL);
+    PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(threshold, 1.0, NULL);
+    PS_ASSERT_ARRAY_NON_NULL(subRegions, NULL);
+    PS_ASSERT_ARRAY_NON_NULL(subKernels, NULL);
+    PS_ASSERT_ARRAYS_SIZE_EQUAL(subRegions, subKernels, NULL);
+
+    // Trivial case
+    if (in->n == 0) {
+        return psPixelsAllocEmpty(0);
+    }
+
+    // Check consistency of kernels
+    int numRegions = subRegions->n;     // Number of regions
+    int size = 0;                       // Size of kernel
+    for (int i = 0; i < numRegions; i++) {
+        pmSubtractionKernels *kernels = subKernels->data[i]; // Kernel of interest
+        if (size == 0) {
+            size = kernels->size;
+        } else if (kernels->size != size) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Kernel sizes are not identical: %d vs %d",
+                    size, kernels->size);
+            return NULL;
+        }
+    }
+
+    psImage *mask = psPixelsToMask(NULL, in, psRegionSet(0, numCols - 1, 0, numRows - 1), 1); // Mask
+    psImage *image = psImageCopy(NULL, mask, PS_TYPE_F32); // Floating-point version, so we can convolve
+    psFree(mask);
+
+    // Convolve the image with the kernel --- we're basically applying a matched filter and then thresholding
+    pmReadout *convRO = pmReadoutAlloc(NULL); // Readout with convolved image
+    pmReadout *inRO = pmReadoutAlloc(NULL); // Readout with input image
+    inRO->image = image;
+    if (threaded) {
+        psMutexInit(image);
+    }
+    for (int i = 0; i < numRegions; i++) {
+        psRegion *region = subRegions->data[i]; // Region of interest
+        pmSubtractionKernels *kernels = subKernels->data[i]; // Kernel of interest
+        if (!pmSubtractionConvolve(convRO, NULL, inRO, NULL, NULL, 0, 0, 1.0, region, kernels, false, true)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to convolve mask image in region %d.", i);
+            psFree(convRO);
+            psFree(inRO);
+            return NULL;
+        }
+
+        // Need to adjust the thresholding level for the normalisation of the kernel --- the application of
+        // the kernel may scale the unit level that we've inserted.
+
+        // Image of the kernel at the centre of the region
+        float xNorm = (region->x0 + 0.5 * (region->x1 - region->x0) - kernels->numCols/2.0) /
+            (float)kernels->numCols;
+        float yNorm = (region->y0 + 0.5 * (region->y1 - region->y0) - kernels->numRows/2.0) /
+            (float)kernels->numRows;
+        psImage *kernel = pmSubtractionKernelImage(kernels, xNorm, yNorm, false);
+        if (!kernel) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
+            psFree(convRO);
+            psFree(inRO);
+            return NULL;
+        }
+        float sum = 0.0;
+        for (int y = 0; y < kernel->numRows; y++) {
+            for (int x = 0; x < kernel->numCols; x++) {
+                sum += kernel->data.F32[y][x];
+            }
+        }
+        psFree(kernel);
+
+        // Range for normalisation
+        int xMin = PS_MAX(0, region->x0), xMax = PS_MIN(numCols - 1, region->x1);
+        int yMin = PS_MAX(0, region->y0), yMax = PS_MIN(numRows - 1, region->y1);
+        psTrace("psModules.imcombine", 2, "Normalising convolved mask image by %f over %d:%d,%d:%d\n",
+                sum, xMin, xMax, yMin, yMax);
+        sum = 1.0 / sum;
+        for (int y = yMin; y <= yMax; y++) {
+            for (int x = xMin; x <= xMax; x++) {
+                convRO->image->data.F32[y][x] *= sum;
+            }
+        }
+    }
+    if (threaded) {
+        psMutexDestroy(image);
+    }
+    psFree(inRO);
+    psImage *convolved = psMemIncrRefCounter(convRO->image);
+    psFree(convRO);
+
+#ifdef TESTING
+    {
+        static int seqNum = 0;          // Sequence number
+        psString name = NULL;           // Name of image
+        psStringAppend(&name, "inspect_conv_%02d.fits", seqNum);
+        seqNum++;
+        psFits *fits = psFitsOpen(name, "w"); // FITS file pointer
+        psFree(name);
+        psFitsWriteImage(fits, NULL, convolved, 0, NULL);
+        psFitsClose(fits);
+    }
+#endif
+
+    // Threshold the convolved image
+    psPixels *bad = psPixelsAllocEmpty(PIXEL_LIST_BUFFER); // List of pixels that should be masked
+    for (int y = size; y < convolved->numRows - size; y++) {
+        for (int x = size; x < convolved->numCols - size; x++) {
+            if (convolved->data.F32[y][x] > threshold) {
+                bad = psPixelsAdd(bad, bad->nalloc, x, y);
+            }
+        }
+    }
+
+    // Now, grow the mask to include everything that touches a bad pixel in the convolution
+    psImage *source = psPixelsToMask(NULL, bad, psRegionSet(0, numCols - 1, 0, numRows - 1),
+                                     PM_STACK_MASK_BAD); // Mask image to grow
+    psImage *target = psImageRecycle(convolved, numCols, numRows, PS_TYPE_MASK); // Grown image
+    psImageInit(target, 0);
+    if (threaded) {
+        psMutexInit(source);
+    }
+    for (int i = 0; i < subRegions->n; i++) {
+        psRegion *region = subRegions->data[i]; // Subtraction region
+        pmSubtractionKernels *kernels = subKernels->data[i]; // Subtraction kernel
+
+        int size = kernels->size;           // Half-size of kernel
+        int fullSize = 2 * size + 1;        // Full size of kernel
+
+        // Get region for convolution: [xMin:xMax,yMin:yMax]
+        int xMin = PS_MAX(region->x0, size), xMax = PS_MIN(region->x1, numCols - size);
+        int yMin = PS_MAX(region->y0, size), yMax = PS_MIN(region->y1, numRows - size);
+
+        for (int j = yMin; j < yMax; j += fullSize) {
+            int ySubMax = PS_MIN(j + fullSize, yMax); // Range for subregion of interest
+            for (int i = xMin; i < xMax; i += fullSize) {
+                int xSubMax = PS_MIN(i + fullSize, xMax); // Range for subregion of interest
+
+                if (threaded) {
+                    psThreadJob *job = psThreadJobAlloc("PSMODULES_STACK_REJECT_GROW"); // Job to execute
+                    psArray *args = job->args; // Job arguments
+                    psArrayAdd(args, 1, target);
+                    psMutexLock(source);
+                    psArrayAdd(args, 1, source);
+                    psMutexUnlock(source);
+                    psArrayAdd(args, 1, kernels);
+                    PS_ARRAY_ADD_SCALAR(args, numCols, PS_TYPE_S32);
+                    PS_ARRAY_ADD_SCALAR(args, numRows, PS_TYPE_S32);
+                    PS_ARRAY_ADD_SCALAR(args, i, PS_TYPE_S32);
+                    PS_ARRAY_ADD_SCALAR(args, xSubMax, PS_TYPE_S32);
+                    PS_ARRAY_ADD_SCALAR(args, j, PS_TYPE_S32);
+                    PS_ARRAY_ADD_SCALAR(args, ySubMax, PS_TYPE_S32);
+                    PS_ARRAY_ADD_SCALAR(args, poorFrac, PS_TYPE_F32);
+                    if (!psThreadJobAddPending(job)) {
+                        psFree(job);
+                        psFree(source);
+                        psFree(target);
+                        return false;
+                    }
+                    psFree(job);
+                } else if (!stackRejectGrow(target, source, kernels, numCols, numRows,
+                                            i, xSubMax, j, ySubMax, poorFrac)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to grow bad pixels.");
+                    psFree(source);
+                    psFree(target);
+                    return NULL;
+                }
+            }
+        }
+    }
+
+    if (!psThreadPoolWait(false)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to grow bad pixels.");
+        psFree(source);
+        psFree(target);
+        return NULL;
+    }
+
+    // Harvest the jobs
+    if (threaded) {
+        psThreadJob *job;                   // Job to destroy
+        while ((job = psThreadJobGetDone())) {
+            psFree(job);
+        }
+
+        psMutexDestroy(source);
+    }
+
+    psFree(source);
+    bad = psPixelsFromMask(bad, target, PM_STACK_MASK_ALL);
+
+    return bad;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStackReject.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStackReject.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmStackReject.h	(revision 22322)
@@ -0,0 +1,22 @@
+#ifndef PM_STACK_REJECT_H
+#define PM_STACK_REJECT_H
+
+#include <pslib.h>
+#include <pmSubtractionKernels.h>
+
+/// Given a list of pixels from the convolved image, find the corresponding (smaller subset of) pixels in the
+/// original image, and then convolve those to get the list of all pixels which should be rejected
+///
+/// We apply a matched filter to the corresponding mask image, and threshold to find the original pixels
+psPixels *pmStackReject(const psPixels *in, ///< List of pixels in the convolved image
+                        int numCols, int numRows, ///< Size of image of interest
+                        float threshold, ///< Threshold on convolved image, 0..1
+                        float poorFrac, ///< Fraction for "poor"
+                        const psArray *regions, ///< Array of image regions for image
+                        const psArray *kernels ///< Array of kernel parameters for each region
+    );
+
+/// Initialise threads for stack rejection
+bool pmStackRejectThreadsInit(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtraction.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtraction.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtraction.c	(revision 22322)
@@ -0,0 +1,1341 @@
+/** @file pmSubtraction.c
+ *
+ *  @author Paul Price, IfA
+ *  @author GLG, MHPCC
+ *
+ *  Copyright 2004-2007 Institute for Astronomy, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"                      // Required for pmFPA.h
+#include "pmFPA.h"
+#include "pmSubtractionStamps.h"
+#include "pmSubtractionEquation.h"
+#include "pmSubtractionThreads.h"
+
+#include "pmSubtraction.h"
+
+//#define TESTING
+
+#define PIXEL_LIST_BUFFER 100           // Number of entries to add to pixel list at a time
+#define MIN_SAMPLE_STATS    7           // Minimum number to use sample statistics; otherwise use quartiles
+
+#define SYS_ERROR 0.05                  // Relative error in derived convolution kernel pixels
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Private (file-static) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Generate the kernel to apply to the variance from the normal kernel
+static psKernel *varianceKernel(psKernel *out, // Output kernel
+                                psKernel *normalKernel // Normal kernel
+                                )
+{
+    // Kernel range
+    int xMin = normalKernel->xMin, xMax = normalKernel->xMax;
+    int yMin = normalKernel->yMin, yMax = normalKernel->yMax;
+
+    if (!out) {
+        out = psKernelAlloc(xMin, xMax, yMin, yMax);
+    }
+
+    // Take the square of the normal kernel
+    double sumNormal = 0.0, sumVariance = 0.0; // Sum of the normal and variance kernels
+    for (int v = yMin; v <= yMax; v++) {
+        for (int u = xMin; u <= xMax; u++) {
+            float value = normalKernel->kernel[v][u]; // Value of interest
+            float value2 = PS_SQR(value); // Value squared
+            sumNormal += value;
+            sumVariance += value2;
+            out->kernel[v][u] = value2;
+        }
+    }
+
+    // Normalise so that the sum of the variance kernel is the square of the sum of the normal kernel
+    // This is required to keep the relative scaling between the image and the weight map
+    psBinaryOp(out->image, out->image, "*", psScalarAlloc(PS_SQR(sumNormal) / sumVariance, PS_TYPE_F32));
+
+    return out;
+}
+
+// Generate an image of the solved kernel
+static psKernel *solvedKernel(psKernel *kernel, // Kernel, to return
+                              const pmSubtractionKernels *kernels, // Kernel basis functions
+                              const psImage *polyValues, // Spatial polynomial values
+                              bool wantDual // Want the dual (second) kernel?
+                              )
+{
+    assert(kernels);
+    assert(polyValues);
+
+    int numKernels = kernels->num;      // Number of kernel basis functions
+    int size = kernels->size;           // Kernel half-size
+    if (!kernel) {
+        kernel = psKernelAlloc(-size, size, -size, size);
+    }
+    psImageInit(kernel->image, 0.0);
+
+    for (int i = 0; i < numKernels; i++) {
+        double value = p_pmSubtractionSolutionCoeff(kernels, polyValues, i, wantDual); // Polynomial value
+
+        switch (kernels->type) {
+          case PM_SUBTRACTION_KERNEL_POIS: {
+              int u = kernels->u->data.S32[i]; // Offset in x
+              int v = kernels->v->data.S32[i]; // Offset in y
+              kernel->kernel[v][u] += value;
+              kernel->kernel[0][0] -= value;
+              break;
+          }
+          /* SPAM and FRIES use the same method */
+          case PM_SUBTRACTION_KERNEL_SPAM:
+          case PM_SUBTRACTION_KERNEL_FRIES: {
+              int uStart = kernels->u->data.S32[i];
+              int uStop = kernels->uStop->data.S32[i];
+              int vStart = kernels->v->data.S32[i];
+              int vStop = kernels->vStop->data.S32[i];
+
+              // Normalising sum of kernel component to unity
+              float norm = 1.0 / (float)((uStop - uStart + 1) * (vStop - vStart + 1));
+
+              for (int v = vStart; v <= vStop; v++) {
+                  for (int u = uStart; u <= uStop; u++) {
+                      kernel->kernel[v][u] += norm * value;
+                      kernel->kernel[0][0] -= value;
+                  }
+              }
+              break;
+          }
+          case PM_SUBTRACTION_KERNEL_GUNK: {
+              if (i < kernels->inner) {
+                  // Using pre-calculated function
+                  psKernel *preCalc = kernels->preCalc->data[i]; // Precalculated values
+                  // Iterating over the kernel
+                  for (int v = -size; v <= size; v++) {
+                      for (int u = -size; u <= size; u++) {
+                          kernel->kernel[v][u] += preCalc->kernel[v][u] * value;
+                          // Photometric scaling is built into the preCalc kernel --- no subtraction!
+                      }
+                  }
+              } else {
+                  // Using delta function
+                  int u = kernels->u->data.S32[i]; // Offset in x
+                  int v = kernels->v->data.S32[i]; // Offset in y
+                  kernel->kernel[v][u] += value;
+                  kernel->kernel[0][0] -= value;
+              }
+              break;
+          }
+          case PM_SUBTRACTION_KERNEL_ISIS: {
+              psArray *preCalc = kernels->preCalc->data[i]; // Precalculated values
+              psVector *xKernel = preCalc->data[0]; // Kernel in x
+              psVector *yKernel = preCalc->data[1]; // Kernel in y
+              // Iterating over the kernel
+              for (int y = 0, v = -size; v <= size; y++, v++) {
+                  for (int x = 0, u = -size; u <= size; x++, u++) {
+                      kernel->kernel[v][u] +=  value * xKernel->data.F32[x] * yKernel->data.F32[y];
+                  }
+              }
+              // Photometric scaling for even kernels only
+              if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
+                  kernel->kernel[0][0] -= value;
+              }
+              break;
+          }
+          case PM_SUBTRACTION_KERNEL_RINGS: {
+              psArray *preCalc = kernels->preCalc->data[i]; // Precalculated data
+              psVector *uCoords = preCalc->data[0]; // u coordinates
+              psVector *vCoords = preCalc->data[1]; // v coordinates
+              psVector *poly = preCalc->data[2]; // Polynomial values
+              int num = uCoords->n;     // Number of pixels
+
+              for (int j = 0; j < num; j++) {
+                  int u = uCoords->data.S32[j], v = vCoords->data.S32[j]; // Kernel coordinates
+                  kernel->kernel[v][u] += poly->data.F32[j] * value;
+              }
+              // Photometric scaling is built into the kernel --- no subtraction!
+              break;
+          }
+          default:
+            psAbort("Should never get here.");
+        }
+    }
+
+    // Put in the normalisation component
+    kernel->kernel[0][0] += (wantDual ? 1.0 : p_pmSubtractionSolutionNorm(kernels));
+
+    return kernel;
+}
+
+// Subtract the (0,0) element to preserve photometric scaling
+static void convolveSub(psKernel *convolved, // Convolved image
+                        const psKernel *image, // Image being convolved
+                        int footprint  // Size of region of interest
+                        )
+{
+    // Can't use psBinaryOp because the images are of different size
+    for (int y = -footprint; y <= footprint; y++) {
+        for (int x = -footprint; x <= footprint; x++) {
+            convolved->kernel[y][x] -= image->kernel[y][x];
+        }
+    }
+    return;
+}
+
+// Generate the convolution given some offset
+static psKernel *convolveOffset(const psKernel *image, // Image to convolve (a kernel for convenience)
+                                int u, int v, // Offset to apply
+                                int footprint // Size of region of interest
+                                )
+{
+    psKernel *convolved = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Convolved image
+    int numBytes = (2 * footprint + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes to copy
+    for (int y = -footprint; y <= footprint; y++) {
+        // Convolution with a delta function is just the value specified by the offset
+        memcpy(&convolved->kernel[y][-footprint], &image->kernel[y - v][-footprint - u], numBytes);
+    }
+    return convolved;
+}
+
+// Take a subset of an image
+static inline psImage *convolveSubsetAlloc(psImage *image, // Image to be convolved
+                                           psRegion region, // Region of interest (with border)
+                                           bool threaded // Are we running threaded?
+                                           )
+{
+    if (!image) {
+        return NULL;
+    }
+    if (threaded) {
+        psMutexLock(image);
+    }
+    psImage *subset = psImageSubset(image, region); // Subset image, to return
+    if (threaded) {
+        psMutexUnlock(image);
+    }
+    return subset;
+}
+
+// Free the subset of an image
+static inline void convolveSubsetFree(psImage *parent, // Parent image
+                                      psImage *child, // Child (subset) image
+                                      bool threaded // Are we running threaded?
+                                      )
+{
+    if (!child || !parent) {
+        return;
+    }
+    if (threaded) {
+        psMutexLock(parent);
+    }
+    psFree(child);
+    if (threaded) {
+        psMutexUnlock(parent);
+    }
+    return;
+}
+
+// Convolve an image using FFT
+static void convolveFFT(psImage *target,// Place the result in here
+                        psImage *image, // Image to convolve
+                        psImage *mask, // Mask image
+                        psMaskType maskVal, // Value to mask
+                        const psKernel *kernel, // Kernel by which to convolve
+                        psRegion region,// Region of interest
+                        float background, // Background to add
+                        int size        // Size of (square) kernel
+                        )
+{
+    psRegion border = psRegionSet(region.x0 - size, region.x1 + size,
+                                  region.y0 - size, region.y1 + size); // Add a border
+
+    bool threaded = pmSubtractionThreaded(); // Are we running threaded?
+
+    psImage *subImage = convolveSubsetAlloc(image, border, threaded); // Subimage to convolve
+    psImage *subMask = convolveSubsetAlloc(mask, border, threaded); // Subimage mask
+
+    psImage *convolved = psImageConvolveFFT(NULL, subImage, subMask, maskVal, kernel); // Convolution
+
+    convolveSubsetFree(image, subImage, threaded);
+    convolveSubsetFree(mask, subMask, threaded);
+
+    // Now, we have to stick it in where it belongs
+    int xMin = region.x0, xMax = region.x1, yMin = region.y0, yMax = region.y1; // Bounds of region
+    if (background != 0.0) {
+        for (int yTarget = yMin, ySource = size; yTarget < yMax; yTarget++, ySource++) {
+            for (int xTarget = xMin, xSource = size; xTarget < xMax; xTarget++, xSource++) {
+                target->data.F32[yTarget][xTarget] = convolved->data.F32[ySource][xSource] + background;
+            }
+        }
+    } else {
+        int numBytes = (xMax - xMin) * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes to copy
+        for (int yTarget = yMin, ySource = size; yTarget < yMax; yTarget++, ySource++) {
+            memcpy(&target->data.F32[yTarget][xMin], &convolved->data.F32[ySource][size], numBytes);
+        }
+    }
+    psFree(convolved);
+
+    return;
+}
+
+
+// Convolve an image using FFT
+static void convolveWeightFFT(psImage *target,// Place the result in here
+                              psImage *weight, // Weight map to convolve
+                              psImage *sys, // Systematic error image
+                              psImage *mask, // Mask image
+                              psMaskType maskVal, // Value to mask
+                              const psKernel *kernel, // Kernel by which to convolve
+                              psRegion region,// Region of interest
+                              int size        // Size of (square) kernel
+                              )
+{
+    psRegion border = psRegionSet(region.x0 - size, region.x1 + size,
+                                  region.y0 - size, region.y1 + size); // Add a border
+
+    bool threaded = pmSubtractionThreaded(); // Are we running threaded?
+
+    psImage *subWeight = convolveSubsetAlloc(weight, border, threaded); // Weight map
+    psImage *subSys = convolveSubsetAlloc(sys, border, threaded); // Systematic error image
+    psImage *subMask = convolveSubsetAlloc(mask, border, threaded); // Mask
+
+    // XXX Can trim this a little by combining the convolution: only have to take the FFT of the kernel once
+    psImage *convWeight = psImageConvolveFFT(NULL, subWeight, subMask, maskVal, kernel); // Convolved weight
+    psImage *convSys = subSys ? psImageConvolveFFT(NULL, subSys, subMask, maskVal, kernel) : NULL; // Conv sys
+
+    convolveSubsetFree(weight, subWeight, threaded);
+    convolveSubsetFree(sys, subSys, threaded);
+    convolveSubsetFree(mask, subMask, threaded);
+
+    // Now, we have to stick it in where it belongs
+    int xMin = region.x0, xMax = region.x1, yMin = region.y0, yMax = region.y1; // Bounds of region
+    if (convSys) {
+        for (int yTarget = yMin, ySource = size; yTarget < yMax; yTarget++, ySource++) {
+            for (int xTarget = xMin, xSource = size; xTarget < xMax; xTarget++, xSource++) {
+                target->data.F32[yTarget][xTarget] = convWeight->data.F32[ySource][xSource] +
+                    convSys->data.F32[ySource][xSource];
+            }
+        }
+    } else {
+        int numBytes = (xMax - xMin) * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes to copy
+        for (int yTarget = yMin, ySource = size; yTarget < yMax; yTarget++, ySource++) {
+            memcpy(&target->data.F32[yTarget][xMin], &convWeight->data.F32[ySource][size], numBytes);
+        }
+    }
+
+    psFree(convWeight);
+    psFree(convSys);
+
+    return;
+}
+
+
+// Convolve an image directly
+static void convolveDirect(psImage *target, // Put the result here
+                           const psImage *image, // Image to convolve
+                           const psKernel *kernel, // Kernel by which to convolve
+                           psRegion region,// Region of interest
+                           float background, // Background to add
+                           int size        // Size of (square) kernel
+                           )
+{
+    for (int y = region.y0; y < region.y1; y++) {
+        for (int x = region.x0; x < region.x1; x++) {
+            target->data.F32[y][x] = background;
+            for (int v = -size; v <= size; v++) {
+                for (int u = -size; u <= size; u++) {
+                    target->data.F32[y][x] += kernel->kernel[v][u] * image->data.F32[y - v][x - u];
+                }
+            }
+        }
+    }
+    return;
+}
+
+// Convolve a region of an image
+static inline void convolveRegion(psImage *convImage, // Convolved image (output)
+                                  psImage *convWeight, // Convolved weight map (output), or NULL
+                                  psImage *convMask, // Convolve mask (output), or NULL
+                                  psKernel **kernelImage, // Convolution kernel for the image
+                                  psKernel **kernelWeight, // Convolution kernel for the weight map, or NULL
+                                  psImage *image, // Image to convolve
+                                  psImage *weight, // Weight map to convolve, or NULL
+                                  psImage *sys, // Systematic error image, or NULL
+                                  psImage *subMask, // Subtraction mask
+                                  const pmSubtractionKernels *kernels, // Kernels
+                                  const psImage *polyValues, // Polynomial values
+                                  float background, // Background value to apply
+                                  psRegion region, // Region to convolve
+                                  psMaskType maskBad, // Value to give bad pixels
+                                  psMaskType maskPoor, // Value to give poor pixels
+                                  float poorFrac, // Fraction for "poor"
+                                  bool useFFT,  // Use FFT to convolve?
+                                  bool wantDual // Want the dual convolution?
+    )
+{
+    *kernelImage = solvedKernel(*kernelImage, kernels, polyValues, wantDual);
+    if (weight || subMask) {
+        *kernelWeight = varianceKernel(*kernelWeight, *kernelImage);
+    }
+
+    psMaskType subBad;                  // Bad pixels in subtraction mask
+    psMaskType subConvBad;              // Bad pixels in subtraction mask when convolving
+    psMaskType subConvPoor;             // Poor pixels in subtraction mask when convolving
+    if (kernels->mode == PM_SUBTRACTION_MODE_1 || (kernels->mode == PM_SUBTRACTION_MODE_DUAL && !wantDual)) {
+        subBad = PM_SUBTRACTION_MASK_BAD_1;
+        subConvBad = PM_SUBTRACTION_MASK_CONVOLVE_BAD_1;
+        subConvPoor = PM_SUBTRACTION_MASK_CONVOLVE_1;
+    } else {
+        subBad = PM_SUBTRACTION_MASK_BAD_2;
+        subConvBad = PM_SUBTRACTION_MASK_CONVOLVE_BAD_2;
+        subConvPoor = PM_SUBTRACTION_MASK_CONVOLVE_2;
+    }
+
+    // Convolve the image and weight
+    if (useFFT) {
+        // Use Fast Fourier Transform to do the convolution
+        // This provides a big speed-up for large kernels
+        convolveFFT(convImage, image, subMask, subBad, *kernelImage, region, background, kernels->size);
+        if (weight) {
+            convolveWeightFFT(convWeight, weight, sys, subMask, subBad, *kernelWeight, region, kernels->size);
+        }
+    } else {
+        // XXX Direct convolution doesn't account for bad pixels yet
+        convolveDirect(convImage, image, *kernelImage, region, background, kernels->size);
+        if (weight) {
+            convolveDirect(convWeight, weight, *kernelWeight, region, 0.0, kernels->size);
+        }
+    }
+
+    // Convolve the mask for bad pixels
+    if (subMask && convMask) {
+        int box = p_pmSubtractionBadRadius(*kernelImage, kernels, polyValues,
+                                           wantDual, poorFrac); // Size of bad box
+        if (box > 0) {
+            int colMin = region.x0, colMax = region.x1, rowMin = region.y0, rowMax = region.y1; // Bounds
+            bool threaded = pmSubtractionThreaded(); // Are we running threaded?
+
+            psRegion region = psRegionSet(colMin - box, colMax + box,
+                                          rowMin - box, rowMax + box); // Region to convolve
+
+            psImage *image = convolveSubsetAlloc(subMask, region, threaded); // Mask to convolve
+
+            psImage *convolved = psImageConvolveMask(NULL, image, subBad, subConvBad,
+                                                     -box, box, -box, box); // Convolved subtraction mask
+
+            convolveSubsetFree(subMask, image, threaded);
+
+            psAssert(convolved->numCols - 2 * box == colMax - colMin, "Bad number of columns");
+            psAssert(convolved->numRows - 2 * box == rowMax - rowMin, "Bad number of rows");
+
+            for (int yTarget = rowMin, ySource = box; yTarget < rowMax; yTarget++, ySource++) {
+                // Dereference images
+                psMaskType *target = &convMask->data.PS_TYPE_MASK_DATA[yTarget][colMin]; // Target values
+                psMaskType *source = &convolved->data.PS_TYPE_MASK_DATA[ySource][box]; // Source values
+                for (int xTarget = colMin; xTarget < colMax; xTarget++, target++, source++) {
+                    if (*source & subConvBad) {
+                        *target |= maskBad;
+                    } else if (*source & subConvPoor) {
+                        *target |= maskPoor;
+                    }
+                }
+            }
+
+            // No need to lock: we own this
+            psFree(convolved);
+        }
+    }
+
+    return;
+}
+
+// Generate an image that can be used to track systematic errors
+static psImage *subtractionSysErrImage(const psImage *image     // Image from which to make sys err image
+                                       )
+{
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+    psImage *sys = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Systematic error image
+
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            sys->data.F32[y][x] = PS_SQR(image->data.F32[y][x]) * PS_SQR(SYS_ERROR);
+        }
+    }
+
+    return sys;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Semi-public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+psKernel *p_pmSubtractionConvolveStampPrecalc(const psKernel *image, const psKernel *kernel)
+{
+    PS_ASSERT_KERNEL_NON_NULL(image, NULL);
+    PS_ASSERT_KERNEL_NON_NULL(kernel, NULL);
+
+    psImage *conv = psImageConvolveFFT(NULL, image->image, NULL, 0, kernel); // Convolved image
+    int x0 = - image->xMin, y0 = - image->yMin; // Position of centre of convolved image
+    psKernel *convolved = psKernelAllocFromImage(conv, x0, y0); // Kernel version
+    psFree(conv);
+    return convolved;
+}
+
+int p_pmSubtractionBadRadius(psKernel *preKernel, const pmSubtractionKernels *kernels,
+                             const psImage *polyValues, bool wantDual, float poorFrac)
+{
+    psKernel *kernel;                   // Kernel to use
+    if (!preKernel) {
+        kernel = solvedKernel(NULL, kernels, polyValues, wantDual);
+    } else {
+        kernel = psMemIncrRefCounter(preKernel);
+    }
+    PS_ASSERT_IMAGE_NON_NULL(polyValues, -1);
+
+    int xMin = kernel->xMin, xMax = kernel->xMax, yMin = kernel->yMin, yMax = kernel->yMax; // Bounds
+
+    // Determine the threshold between bad and poor
+    double sumKernel2 = 0.0;            // Sum of the kernel-squared
+    for (int y = yMin; y <= yMax; y++) {
+        for (int x = xMin; x <= xMax; x++) {
+            sumKernel2 += PS_SQR(kernel->kernel[y][x]);
+        }
+    }
+    float threshold = sumKernel2 * poorFrac; // Threshold between poor and bad
+
+    // Get bounds of threshold region
+    // Start with the entire kernel, and keep reducing the size of the box until it sum goes above threshold
+    int box = kernels->size;            // Size of box with bad pixels
+    for (double sumBox = 0.0; sumBox < threshold && box > 0; box--) {
+        for (int x = -box; x <= box; x++) {
+            sumBox += PS_SQR(kernel->kernel[-box][x]) + PS_SQR(kernel->kernel[box][x]);
+        }
+        for (int y = -box + 1; y <= box - 1; y++) {
+            // Note: not doing corners
+            sumBox += PS_SQR(kernel->kernel[y][-box]) + PS_SQR(kernel->kernel[y][box]);
+        }
+    }
+
+    psFree(kernel);
+
+    return box;
+}
+
+psImage *p_pmSubtractionPolynomialFromCoords(psImage *output, const pmSubtractionKernels *kernels,
+                                             int numCols, int numRows, int x, int y)
+{
+    assert(kernels);
+    assert(numCols > 0 && numRows > 0);
+
+    // Size to use when calculating normalised coordinates (different from actual size when convolving
+    // subimage)
+    int xNormSize = (kernels->numCols > 0 ? kernels->numCols : numCols);
+    int yNormSize = (kernels->numRows > 0 ? kernels->numRows : numRows);
+
+    // Normalised coordinates
+    float yNorm = 2.0 * (float)(y - yNormSize/2.0) / (float)yNormSize;
+    float xNorm = 2.0 * (float)(x - xNormSize/2.0) / (float)xNormSize;
+
+    return p_pmSubtractionPolynomial(output, kernels->spatialOrder, xNorm, yNorm);
+}
+
+psImage *p_pmSubtractionPolynomial(psImage *output, int spatialOrder, float x, float y)
+{
+    assert(spatialOrder >= 0);
+    assert(x >= -1 && x <= 1);
+    assert(y >= -1 && y <= 1);
+
+    output = psImageRecycle(output, spatialOrder + 1, spatialOrder + 1, PS_TYPE_F64);
+    output->data.F64[0][0] = 1.0;
+
+    double value = 1.0;
+    for (int i = 1; i <= spatialOrder; i++) {
+        value *= x;
+        output->data.F64[0][i] = value;
+    }
+
+    value = 1.0;
+    for (int j = 1; j <= spatialOrder; j++) {
+        value *= y;
+        output->data.F64[j][0] = value;
+    }
+
+    for (int j = 1; j <= spatialOrder; j++) {
+        for (int i = 1; i <= spatialOrder - j; i++) {
+            output->data.F64[j][i] = output->data.F64[j][0] * output->data.F64[0][i];
+        }
+    }
+
+    return output;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+// Convolve a stamp by a single kernel basis function
+static psKernel *convolveStampSingle(const pmSubtractionKernels *kernels, // Kernel basis functions
+                                     int index, // Kernel basis function index
+                                     const psKernel *image, // Image to convolve (a kernel for convenience)
+                                     int footprint // Size of region of interest
+    )
+{
+    switch (kernels->type) {
+      case PM_SUBTRACTION_KERNEL_POIS: {
+          int u = kernels->u->data.S32[index]; // Offset in x
+          int v = kernels->v->data.S32[index]; // Offset in y
+          psKernel *convolved = convolveOffset(image, u, v, footprint); // Convolved image
+          convolveSub(convolved, image, footprint);
+          return convolved;
+      }
+        // Method for SPAM and FRIES is the same
+      case PM_SUBTRACTION_KERNEL_SPAM:
+      case PM_SUBTRACTION_KERNEL_FRIES: {
+          psKernel *convolved = psKernelAlloc(-footprint, footprint,
+                                              -footprint, footprint); // Convolved image
+          int uStart = kernels->u->data.S32[index];
+          int uStop = kernels->uStop->data.S32[index];
+          int vStart = kernels->v->data.S32[index];
+          int vStop = kernels->vStop->data.S32[index];
+          float norm = 1.0 / (uStop - uStart + 1) * (vStop - vStart + 1); // Normalisation
+          for (int y = -footprint; y <= footprint; y++) {
+              for (int x = -footprint; x <= footprint; x++) {
+                  double sum = 0.0;
+                  for (int v = vStart; v <= vStop; v++) {
+                      for (int u = uStart; u <= uStop; u++) {
+                          sum += image->kernel[y - v][x - u];
+                      }
+                  }
+                  convolved->kernel[y][x] = norm * sum;
+              }
+          }
+          convolveSub(convolved, image, footprint);
+          return convolved;
+      }
+      case PM_SUBTRACTION_KERNEL_GUNK: {
+          if (index < kernels->inner) {
+              // Photometric scaling is already built in to the precalculated kernel
+              return p_pmSubtractionConvolveStampPrecalc(image, kernels->preCalc->data[index]);
+          }
+          // Using delta function
+          int u = kernels->u->data.S32[index]; // Offset in x
+          int v = kernels->v->data.S32[index]; // Offset in y
+          psKernel *convolved = convolveOffset(image, u, v, footprint); // Convolved image
+          convolveSub(convolved, image, footprint);
+          return convolved;
+      }
+      case PM_SUBTRACTION_KERNEL_ISIS: {
+          psArray *preCalc = kernels->preCalc->data[index]; // Precalculated values
+          psVector *xKernel = preCalc->data[0]; // Kernel in x
+          psVector *yKernel = preCalc->data[1]; // Kernel in y
+          int size = kernels->size;     // Size of kernel
+
+          // Convolve in x
+          // Need to convolve a bit more than the footprint, for the y convolution
+          int yMin = -size - footprint, yMax = size + footprint; // Range for y
+          psKernel *temp = psKernelAlloc(yMin, yMax,
+                                         -footprint, footprint); // Temporary convolution; NOTE: wrong way!
+          for (int y = yMin; y <= yMax; y++) {
+              for (int x = -footprint; x <= footprint; x++) {
+                  float value = 0.0;    // Value of convolved pixel
+                  int uMin = x - size, uMax = x + size; // Range for u
+                  psF32 *xKernelData = xKernel->data.F32; // Kernel values
+                  psF32 *imageData = &image->kernel[y][uMin]; // Image values
+                  for (int u = uMin; u <= uMax; u++, xKernelData++, imageData++) {
+                      value += *xKernelData * *imageData;
+                  }
+                  temp->kernel[x][y] = value; // NOTE: putting in wrong way!
+              }
+          }
+
+          // Convolve in y
+          psKernel *convolved = psKernelAlloc(-footprint, footprint, -footprint, footprint);// Convolved image
+          for (int x = -footprint; x <= footprint; x++) {
+              for (int y = -footprint; y <= footprint; y++) {
+                  float value = 0.0;    // Value of convolved pixel
+                  int vMin = y - size, vMax = y + size; // Range for v
+                  psF32 *yKernelData = yKernel->data.F32; // Kernel values
+                  psF32 *imageData = &temp->kernel[x][vMin]; // Image values; NOTE: wrong way!
+                  for (int v = vMin; v <= vMax; v++, yKernelData++, imageData++) {
+                      value += *yKernelData * *imageData;
+                  }
+                  convolved->kernel[y][x] = value;
+              }
+          }
+          psFree(temp);
+
+          // Photometric scaling for even kernels only
+          if (kernels->u->data.S32[index] % 2 == 0 && kernels->v->data.S32[index] % 2 == 0) {
+              convolveSub(convolved, image, footprint);
+          }
+          return convolved;
+      }
+      case PM_SUBTRACTION_KERNEL_RINGS: {
+          psKernel *convolved = psKernelAlloc(-footprint, footprint,
+                                              -footprint, footprint); // Convolved image
+          psArray *preCalc = kernels->preCalc->data[index]; // Precalculated data
+          psVector *uCoords = preCalc->data[0]; // u coordinates
+          psVector *vCoords = preCalc->data[1]; // v coordinates
+          psVector *poly = preCalc->data[2]; // Polynomial values
+          int num = uCoords->n;         // Number of pixels
+          psS32 *uData = uCoords->data.S32, *vData = vCoords->data.S32; // Dereference u,v coordinates
+          psF32 *polyData = poly->data.F32; // Dereference polynomial values
+          psF32 **imageData = image->kernel;  // Dereference image
+          psF32 **convData = convolved->kernel; // Dereference convolved image
+          for (int y = -footprint; y <= footprint; y++) {
+              for (int x = -footprint; x <= footprint; x++) {
+                  double sum = 0.0;             // Accumulated sum from convolution
+                  for (int j = 0; j < num; j++) {
+                      int u = uData[j], v = vData[j]; // Kernel coordinates
+                      sum += imageData[y - v][x - u] * polyData[j];
+                  }
+                  convData[y][x] = sum;
+                  // Photometric scaling is built into the kernel --- no subtraction!
+              }
+          }
+          return convolved;
+      }
+      default:
+        psAbort("Should never get here.");
+    }
+    return NULL;
+}
+
+// Convolve the stamp by each of the kernel basis functions
+static psArray *convolveStamp(psArray *convolutions, // The convolutions
+                              const psKernel *image, // Image to convolve
+                              const pmSubtractionKernels *kernels, // Kernel basis functions
+                              int footprint // Stamp half-size
+    )
+{
+    assert(image);
+    assert(kernels);
+    assert(footprint >= 0);
+
+    if (convolutions) {
+        // Already done
+        return convolutions;
+    }
+
+    int numKernels = kernels->num;      // Number of kernels
+    convolutions = psArrayAlloc(numKernels);
+
+    for (int i = 0; i < numKernels; i++) {
+        convolutions->data[i] = convolveStampSingle(kernels, i, image, footprint);
+    }
+
+    return convolutions;
+}
+
+
+bool pmSubtractionConvolveStamp(pmSubtractionStamp *stamp, const pmSubtractionKernels *kernels, int footprint)
+{
+    PS_ASSERT_PTR_NON_NULL(stamp, false);
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
+    PS_ASSERT_INT_NONNEGATIVE(footprint, false);
+
+    if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Stamp not marked for calculation.");
+        return false;
+    }
+
+    switch (kernels->mode) {
+      case PM_SUBTRACTION_MODE_1:
+        stamp->convolutions1 = convolveStamp(stamp->convolutions1, stamp->image1, kernels, footprint);
+        break;
+      case PM_SUBTRACTION_MODE_2:
+        stamp->convolutions2 = convolveStamp(stamp->convolutions2, stamp->image2, kernels, footprint);
+        break;
+      case PM_SUBTRACTION_MODE_UNSURE:
+      case PM_SUBTRACTION_MODE_DUAL:
+        stamp->convolutions1 = convolveStamp(stamp->convolutions1, stamp->image1, kernels, footprint);
+        stamp->convolutions2 = convolveStamp(stamp->convolutions2, stamp->image2, kernels, footprint);
+        break;
+      default:
+        psAbort("Unsupported subtraction mode: %x", kernels->mode);
+    }
+
+    return true;
+}
+
+
+
+
+int pmSubtractionRejectStamps(pmSubtractionKernels *kernels, pmSubtractionStampList *stamps,
+                              const psVector *deviations, psImage *subMask, float sigmaRej, int footprint)
+{
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
+    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, -1);
+    PS_ASSERT_VECTOR_NON_NULL(deviations, -1);
+    PS_ASSERT_VECTOR_TYPE(deviations, PS_TYPE_F32, -1);
+    PS_ASSERT_IMAGE_NON_EMPTY(subMask, -1);
+    PS_ASSERT_IMAGE_TYPE(subMask, PS_TYPE_MASK, -1);
+
+    // I used to measure the rms deviation about zero, and use that as the sigma against which to clip, but
+    // the distribution is actually something like a chi^2 or Student's t, both of which become Gaussian-like
+    // with large N.  Therefore, let's just treat this as a Gaussian distribution.
+
+    kernels->mean = NAN;
+    kernels->rms = NAN;
+    kernels->numStamps = -1;
+
+    int numStamps = 0;                  // Number of used stamps
+    psVector *mask = psVectorAlloc(stamps->num, PS_TYPE_MASK); // Mask, for statistics
+    psVectorInit(mask, 0);
+    for (int i = 0; i < stamps->num; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+        if (stamp->status != PM_SUBTRACTION_STAMP_USED) {
+            mask->data.PS_TYPE_MASK_DATA[i] = 0xff;
+            continue;
+        }
+        numStamps++;
+    }
+    psTrace("psModules.imcombine", 1, "Number of good stamps: %d\n", numStamps);
+
+    if (numStamps == 0) {
+        psError(PS_ERR_UNKNOWN, true, "No good stamps found.");
+        psFree(mask);
+        return -1;
+    }
+
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV |
+                                  PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_QUARTILE); // Statistics for deviatns
+    if (!psVectorStats(stats, deviations, NULL, mask, 0xff)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to measure statistics for deviations.");
+        psFree(stats);
+        psFree(mask);
+        return -1;
+    }
+    psFree(mask);
+
+    double mean, rms;                 // Mean and RMS of deviations
+    if (numStamps < MIN_SAMPLE_STATS) {
+        mean = stats->sampleMean;
+        rms = stats->sampleStdev;
+    } else {
+        mean = stats->sampleMedian;
+        rms = 0.74 * (stats->sampleUQ - stats->sampleLQ);
+    }
+    psFree(stats);
+
+    psTrace("psModules.imcombine", 1, "Mean: %f\n", mean);
+    psTrace("psModules.imcombine", 1, "RMS deviation: %f\n", rms);
+
+    kernels->mean = mean;
+    kernels->rms = rms;
+    kernels->numStamps = numStamps;
+
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Mean deviation from %d stamps: %lf +/- %lf",
+             numStamps, mean, rms);
+
+    if (!isfinite(sigmaRej) || sigmaRej <= 0.0) {
+        // User just wanted to calculate and record the deviation for posterity
+        return 0;
+    }
+
+    float limit = sigmaRej * rms; // Limit on maximum deviation
+    psTrace("psModules.imcombine", 1, "Deviation limit: %f\n", limit);
+
+    int numRejected = 0;                // Number of stamps rejected
+    int numGood = 0;                    // Number of good stamps
+    double newMean = 0.0;               // New mean
+    for (int i = 0; i < stamps->num; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+        if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
+            // Should we reject stars with low deviation?  Well, if this is really a Gaussian-like
+            // distribution and they're low, then we have the right to ask why.  Isn't it suspicious that
+            // they're anomalously low, compared to the rest of the population which (we hope) is indicative
+            // of normality?  Besides, the standard deviation is going to be blown up by stars that didn't
+            // subtract well, in which case very few (if any) stars will be legitimately rejected for being
+            // low.
+            if (fabsf(deviations->data.F32[i] - mean) > limit) {
+                // Mask out the stamp in the image so you it's not found again
+                psTrace("psModules.imcombine", 3, "Rejecting stamp %d (%d,%d)\n", i,
+                        (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
+                numRejected++;
+                for (int y = stamp->y - footprint; y <= stamp->y + footprint; y++) {
+                    for (int x = stamp->x - footprint; x <= stamp->x + footprint; x++) {
+                        subMask->data.PS_TYPE_MASK_DATA[y][x] |= PM_SUBTRACTION_MASK_REJ;
+                    }
+                }
+
+                // Set stamp for replacement
+                stamp->x = 0;
+                stamp->y = 0;
+                stamp->xNorm = NAN;
+                stamp->yNorm = NAN;
+                stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
+                // Recalculate convolutions
+                psFree(stamp->convolutions1);
+                psFree(stamp->convolutions2);
+                stamp->convolutions1 = stamp->convolutions2 = NULL;
+                psFree(stamp->image1);
+                psFree(stamp->image2);
+                psFree(stamp->weight);
+                stamp->image1 = stamp->image2 = stamp->weight = NULL;
+                psFree(stamp->matrix1);
+                psFree(stamp->matrix2);
+                psFree(stamp->matrixX);
+                stamp->matrix1 = stamp->matrix2 = stamp->matrixX = NULL;
+                psFree(stamp->vector1);
+                psFree(stamp->vector2);
+                stamp->vector1 = stamp->vector2 = NULL;
+            } else {
+                numGood++;
+                newMean += deviations->data.F32[i];
+            }
+        }
+    }
+    newMean /= numGood;
+
+    if (numRejected > 0) {
+        psLogMsg("psModules.imcombine", PS_LOG_INFO,
+                 "%d good stamps; %d rejected.\nMean deviation: %lf --> %lf\n",
+                 numGood, numRejected, mean, newMean);
+    } else {
+        psLogMsg("psModules.imcombine", PS_LOG_INFO,
+                 "%d good stamps; 0 rejected.\nMean deviation: %lf\n",
+                 numGood, mean);
+    }
+
+    return numRejected;
+}
+
+psImage *pmSubtractionKernelImage(const pmSubtractionKernels *kernels, float x, float y, bool wantDual)
+{
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NULL);
+    PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NULL);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(x, -1.0, 1.0, NULL);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(y, -1.0, 1.0, NULL);
+
+    // Precalulate polynomial values
+    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y);
+
+    // The appropriate kernel
+    psKernel *kernel = solvedKernel(NULL, kernels, polyValues, wantDual);
+
+    psFree(polyValues);
+
+    psImage *image = psMemIncrRefCounter(kernel->image); // Image of the kernel
+    psFree(kernel);
+
+    return image;
+}
+
+
+float pmSubtractionVarianceFactor(const pmSubtractionKernels *kernels, float x, float y, bool wantDual)
+{
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NAN);
+    PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NAN);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(x, -1.0, 1.0, NAN);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(y, -1.0, 1.0, NAN);
+
+    // Precalulate polynomial values
+    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y);
+
+    psKernel *kernel = solvedKernel(NULL, kernels, polyValues, wantDual); // The appropriate kernel
+    psFree(polyValues);
+
+    double sumKernel2 = 0.0;            // Sum of the kernel squared
+    double sumKernel = 0.0;             // Sum of the kernel
+    for (int y = kernel->yMin; y <= kernel->yMax; y++) {
+        for (int x = kernel->xMin; x <= kernel->xMax; x++) {
+            sumKernel += kernel->kernel[y][x];
+            sumKernel2 += PS_SQR(kernel->kernel[y][x]);
+        }
+    }
+
+    psFree(kernel);
+
+    return sumKernel2 / PS_SQR(sumKernel);
+}
+
+#if 0
+psArray *pmSubtractionKernelSolutions(const pmSubtractionKernels *kernels, float x, float y, bool wantDual)
+{
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NULL);
+    PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NULL);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(x, -1.0, 1.0, NULL);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(y, -1.0, 1.0, NULL);
+
+    psArray *images = psArrayAlloc(solution->n - 1); // Images of each kernel to return
+    psVector *fakeSolution = psVectorAlloc(solution->n, PS_TYPE_F64); // Fake solution vector
+    psVectorInit(fakeSolution, 0.0);
+
+    for (int i = 0; i < solution->n - 1; i++) {
+        fakeSolution->data.F64[i] = solution->data.F64[i];
+        images->data[i] = pmSubtractionKernelImage(kernels, x, y, wantDual);
+        fakeSolution->data.F64[i] = 0.0;
+    }
+
+    psFree(fakeSolution);
+
+    return images;
+}
+#endif
+
+
+// XXX Put kernelImage, kernelWeight and polyValues on thread-dependent data
+static bool subtractionConvolvePatch(int numCols, int numRows, // Size of image
+                                     int x0, int y0, // Offsets for image
+                                     pmReadout *out1, pmReadout *out2, // Output readouts
+                                     psImage *convMask, // Output convolved mask
+                                     const pmReadout *ro1, const pmReadout *ro2, // Input readouts
+                                     psImage *sys1, psImage *sys2, // Systematic error images
+                                     psImage *subMask, // Input subtraction mask
+                                     psMaskType maskBad, // Mask value to give bad pixels
+                                     psMaskType maskPoor, // Mask value to give poor pixels
+                                     float poorFrac, // Fraction for "poor"
+                                     const psRegion *region, // Patch to convolve
+                                     const pmSubtractionKernels *kernels, // Kernels
+                                     bool doBG, // Add in background when convolving?
+                                     bool useFFT // Use FFT to do the convolution?
+    )
+{
+    int size = kernels->size;           // Half-size of kernel
+    int xMin = region->x0, xMax = region->x1, yMin = region->y0, yMax = region->y1; // Bounds of patch
+
+    psKernel *kernelImage = NULL;       // Kernel for the images
+    psKernel *kernelWeight = NULL;      // Kernel for the weight maps
+
+    // Only generate polynomial values every kernel footprint, since we have already assumed
+    // (with the stamps) that it does not vary rapidly on this scale.
+    psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
+                                                              xMin + x0 + size + 1,
+                                                              yMin + y0 + size + 1);
+    float background = doBG ? p_pmSubtractionSolutionBackground(kernels, polyValues) : 0.0; // Background term
+
+    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+        convolveRegion(out1->image, out1->weight, convMask, &kernelImage, &kernelWeight,
+                       ro1->image, ro1->weight, sys1, subMask, kernels, polyValues, background, *region,
+                       maskBad, maskPoor, poorFrac, useFFT, false);
+    }
+    if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+        convolveRegion(out2->image, out2->weight, convMask, &kernelImage, &kernelWeight,
+                       ro2->image, ro2->weight, sys2, subMask, kernels, polyValues, background, *region,
+                       maskBad, maskPoor, poorFrac, useFFT, kernels->mode == PM_SUBTRACTION_MODE_DUAL);
+    }
+
+    psFree(kernelImage);
+    psFree(kernelWeight);
+    psFree(polyValues);
+
+    if ((kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) && ro1->mask) {
+        psMaskType **target = convMask->data.PS_TYPE_MASK_DATA; // Target mask
+        psMaskType **source = ro1->mask->data.PS_TYPE_MASK_DATA; // Source mask
+
+        for (int y = yMin; y < yMax; y++) {
+            for (int x = xMin; x < xMax; x++) {
+                target[y][x] |= source[y][x];
+            }
+        }
+    }
+    if ((kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) && ro2->mask) {
+        psMaskType **target = convMask->data.PS_TYPE_MASK_DATA; // Target mask
+        psMaskType **source = ro2->mask->data.PS_TYPE_MASK_DATA; // Source mask
+
+        for (int y = yMin; y < yMax; y++) {
+            for (int x = xMin; x < xMax; x++) {
+                target[y][x] |= source[y][x];
+            }
+        }
+    }
+
+    return true;
+}
+
+bool pmSubtractionConvolveThread(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    psArray *args = job->args;          // Arguments
+    int numCols = PS_SCALAR_VALUE(args->data[0], S32); // Number of columns
+    int numRows = PS_SCALAR_VALUE(args->data[1], S32); // Number of rows
+    int x0 = PS_SCALAR_VALUE(args->data[2], S32); // Offset in x
+    int y0 = PS_SCALAR_VALUE(args->data[3], S32); // Offset in x
+    pmReadout *out1 = args->data[4];    // Output readout 1
+    pmReadout *out2 = args->data[5];    // Output readout 2
+    psImage *convMask = args->data[6];  // Output convolved mask
+    const pmReadout *ro1 = args->data[7]; // Input readout 1
+    const pmReadout *ro2 = args->data[8]; // Input readout 2
+    psImage *sys1 = args->data[9]; // Systematic error image 1
+    psImage *sys2 = args->data[10]; // Systematic error image 2
+    psImage *subMask = args->data[11]; // Subtraction mask
+    psMaskType maskBad = PS_SCALAR_VALUE(args->data[12], U8); // Output mask value for bad pixels
+    psMaskType maskPoor = PS_SCALAR_VALUE(args->data[13], U8); // Output mask value for poor pixels
+    float poorFrac = PS_SCALAR_VALUE(args->data[14], F32); // Fraction for "poor"
+    const psRegion *region = args->data[15]; // Region to convolve
+    const pmSubtractionKernels *kernels = args->data[16]; // Kernels
+    bool doBG = PS_SCALAR_VALUE(args->data[17], U8); // Do background subtraction?
+    bool useFFT = PS_SCALAR_VALUE(args->data[18], U8); // Use FFT for convolution?
+
+    return subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2, sys1, sys2,
+                                    subMask, maskBad, maskPoor, poorFrac, region, kernels, doBG, useFFT);
+}
+
+bool pmSubtractionConvolve(pmReadout *out1, pmReadout *out2, const pmReadout *ro1, const pmReadout *ro2,
+                           psImage *subMask, psMaskType maskBad, psMaskType maskPoor, float poorFrac,
+                           const psRegion *region, const pmSubtractionKernels *kernels,
+                           bool doBG, bool useFFT)
+{
+    int numCols = 0, numRows = 0;       // Image dimensions
+    int x0 = 0, y0 = 0;                 // Image offset
+    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+        PM_ASSERT_READOUT_NON_NULL(out1, false);
+        PM_ASSERT_READOUT_NON_NULL(ro1, false);
+        PM_ASSERT_READOUT_IMAGE(ro1, false);
+        numCols = ro1->image->numCols;
+        numRows = ro1->image->numRows;
+        x0 = ro1->col0;
+        y0 = ro1->row0;
+    }
+    if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+        PM_ASSERT_READOUT_NON_NULL(out2, false);
+        PM_ASSERT_READOUT_NON_NULL(ro2, false);
+        PM_ASSERT_READOUT_IMAGE(ro2, false);
+        if (numCols == 0 && numRows == 0) {
+            numCols = ro2->image->numCols;
+            numRows = ro2->image->numRows;
+            x0 = ro2->col0;
+            y0 = ro2->row0;
+        }
+    }
+    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+        PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->image, ro2->image, false);
+    }
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
+    PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, false);
+    if (subMask) {
+        PS_ASSERT_IMAGE_NON_NULL(subMask, false);
+        PS_ASSERT_IMAGE_TYPE(subMask, PS_TYPE_MASK, false);
+        PS_ASSERT_IMAGE_SIZE(subMask, numCols, numRows, false);
+    }
+    if (region && psRegionIsNaN(*region)) {
+        psString string = psRegionToString(*region);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Input region (%s) contains NAN values", string);
+        psFree(string);
+        return false;
+    }
+
+    bool threaded = pmSubtractionThreaded(); // Running threaded?
+
+    // Outputs
+    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+        if (!out1->image) {
+            out1->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+            if (threaded) {
+                psMutexInit(out1->image);
+            }
+        }
+        if (ro1->weight) {
+            if (!out1->weight) {
+                out1->weight = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+                if (threaded) {
+                    psMutexInit(out1->weight);
+                }
+            }
+            psImageInit(out1->weight, 0.0);
+        }
+    }
+    if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+        if (!out2->image) {
+            out2->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+            if (threaded) {
+                psMutexInit(out2->image);
+            }
+        }
+        if (ro2->weight) {
+            if (!out2->weight) {
+                out2->weight = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+                if (threaded) {
+                    psMutexInit(out2->weight);
+                }
+            }
+            psImageInit(out2->weight, 0.0);
+        }
+    }
+    psImage *convMask = NULL;           // Convolved mask image (common to inputs 1 and 2)
+    if (subMask) {
+        if (threaded) {
+            psMutexInit(subMask);
+        }
+        if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+            if (!out1->mask) {
+                out1->mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+            }
+            convMask = out1->mask;
+        }
+        if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+            if (convMask) {
+                if (out2->mask) {
+                    psFree(out2->mask);
+                }
+                out2->mask = psMemIncrRefCounter(convMask);
+            } else {
+                if (!out2->mask) {
+                    out2->mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+                }
+                convMask = out2->mask;
+            }
+        }
+        psImageInit(convMask, 0);
+    }
+
+    psImage *sys1 = NULL, *sys2 = NULL; // Systematic error images
+    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+        sys1 = subtractionSysErrImage(ro1->image);
+        if (threaded) {
+            psMutexInit(sys1);
+        }
+    }
+    if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+        sys2 = subtractionSysErrImage(ro2->image);
+        if (threaded) {
+            psMutexInit(sys2);
+        }
+    }
+
+    int size = kernels->size;           // Half-size of kernel
+    int fullSize = 2 * size + 1;        // Full size of kernel
+
+    // Get region for convolution: [xMin:xMax,yMin:yMax]
+    int xMin = size, xMax = numCols - size;
+    int yMin = size, yMax = numRows - size;
+    if (region) {
+        xMin = PS_MAX(region->x0, xMin);
+        xMax = PS_MIN(region->x1, xMax);
+        yMin = PS_MAX(region->y0, yMin);
+        yMax = PS_MIN(region->y1, yMax);
+    }
+
+#if 0
+    // XXX Use thread-specific data to store these
+    psImage *polyValues = NULL;         // Pre-calculated polynomial values
+    psKernel *kernelImage = NULL;       // Kernel for the images
+    psKernel *kernelWeight = NULL;      // Kernel for the weight maps
+#endif
+
+    for (int j = yMin; j < yMax; j += fullSize) {
+        int ySubMax = PS_MIN(j + fullSize, yMax); // Range for subregion of interest
+        for (int i = xMin; i < xMax; i += fullSize) {
+            int xSubMax = PS_MIN(i + fullSize, xMax); // Range for subregion of interest
+
+            psRegion *subRegion = psRegionAlloc(i, xSubMax, j, ySubMax); // Bounds of subtraction
+            if (threaded) {
+                psThreadJob *job = psThreadJobAlloc("PSMODULES_SUBTRACTION_CONVOLVE");
+                psArray *args = job->args;
+                PS_ARRAY_ADD_SCALAR(args, numCols, PS_TYPE_S32);
+                PS_ARRAY_ADD_SCALAR(args, numRows, PS_TYPE_S32);
+                PS_ARRAY_ADD_SCALAR(args, x0, PS_TYPE_S32);
+                PS_ARRAY_ADD_SCALAR(args, y0, PS_TYPE_S32);
+                psArrayAdd(args, 1, out1);
+                psArrayAdd(args, 1, out2);
+                psArrayAdd(args, 1, convMask);
+                psArrayAdd(args, 1, (pmReadout*)ro1); // Casting away const
+                psArrayAdd(args, 1, (pmReadout*)ro2); // Casting away const
+                // Since adding to the array can impact the reference count, we need to lock
+                if (sys1) {
+                    psMutexLock(sys1);
+                }
+                psArrayAdd(args, 1, sys1);
+                if (sys1) {
+                    psMutexUnlock(sys1);
+                }
+                if (sys2) {
+                    psMutexLock(sys2);
+                }
+                psArrayAdd(args, 1, sys2);
+                if (sys2) {
+                    psMutexUnlock(sys2);
+                }
+                if (subMask) {
+                    psMutexLock(subMask);
+                }
+                psArrayAdd(args, 1, subMask);
+                if (subMask) {
+                    psMutexUnlock(subMask);
+                }
+                PS_ARRAY_ADD_SCALAR(args, maskBad, PS_TYPE_U8);
+                PS_ARRAY_ADD_SCALAR(args, maskPoor, PS_TYPE_U8);
+                PS_ARRAY_ADD_SCALAR(args, poorFrac, PS_TYPE_F32);
+                psArrayAdd(args, 1, subRegion);
+                psArrayAdd(args, 1, (pmSubtractionKernels*)kernels); // Casting away const
+                PS_ARRAY_ADD_SCALAR(args, doBG, PS_TYPE_U8);
+                PS_ARRAY_ADD_SCALAR(args, useFFT, PS_TYPE_U8);
+
+                if (!psThreadJobAddPending(job)) {
+                    psFree(job);
+                    return false;
+                }
+                psFree(job);
+            } else {
+                subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2,
+                                         sys1, sys2, subMask, maskBad, maskPoor, poorFrac, subRegion,
+                                         kernels, doBG, useFFT);
+            }
+            psFree(subRegion);
+        }
+    }
+
+    if (!psThreadPoolWait(false)) {
+        psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
+        return false;
+    }
+
+    // We don't rely on psThreadPoolWait to harvest the jobs because the job contains a reference to the
+    // subMask, which is being changed on a thread, and psThreadPoolWait doesn't know that it needs to be
+    // locked before freeing.  After psThreadPoolWait, however, the jobs are completed, the threads are idle,
+    // and so there's no need to lock the subMask when we're blowing away the jobs.
+    if (threaded) {
+        psThreadJob *job;               // Completed job
+        while ((job = psThreadJobGetDone())) {
+            psFree(job);
+        }
+
+        if (subMask) {
+            psMutexDestroy(subMask);
+        }
+        if (sys1) {
+            psMutexDestroy(sys1);
+        }
+        if (sys2) {
+            psMutexDestroy(sys2);
+        }
+    }
+
+    psFree(sys1);
+    psFree(sys2);
+
+    // Copy anything that wasn't convolved
+    switch (kernels->mode) {
+      case PM_SUBTRACTION_MODE_1:
+        if (out2) {
+            out2->image = psMemIncrRefCounter(ro2->image);
+            out2->weight = psMemIncrRefCounter(ro2->weight);
+            out2->mask = psMemIncrRefCounter(ro2->mask);
+        }
+        break;
+      case PM_SUBTRACTION_MODE_2:
+        if (out1) {
+            out1->image = psMemIncrRefCounter(ro1->image);
+            out1->weight = psMemIncrRefCounter(ro1->weight);
+            out1->mask = psMemIncrRefCounter(ro1->mask);
+        }
+        break;
+      case PM_SUBTRACTION_MODE_DUAL:
+        break;
+      default:
+        psAbort("Should never get here.");
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtraction.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtraction.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtraction.h	(revision 22322)
@@ -0,0 +1,145 @@
+/* @file pmSubtraction.h
+ *
+ * PSF-matched image subtraction, based on the Alard & Lupton (1998) and Alard (2000) methods.
+ *
+ * @author Paul Price, IfA
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.32 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-09-12 04:12:42 $
+ * Copyright 2004-207 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_SUBTRACTION_H
+#define PM_SUBTRACTION_H
+
+#include <pslib.h>
+
+#include <pmHDU.h>
+#include <pmFPA.h>
+#include <pmSubtractionKernels.h>
+#include <pmSubtractionStamps.h>
+
+/// @addtogroup imcombine Image Combinations
+/// @{
+
+/// Mask values for the subtraction mask
+typedef enum {
+    PM_SUBTRACTION_MASK_CLEAR          = 0x00, // No masking
+    PM_SUBTRACTION_MASK_BAD_1          = 0x01, // Image 1 is bad
+    PM_SUBTRACTION_MASK_BAD_2          = 0x02, // Image 2 is bad
+    PM_SUBTRACTION_MASK_CONVOLVE_1     = 0x04, // If image 1 is convolved, would be poor or bad
+    PM_SUBTRACTION_MASK_CONVOLVE_2     = 0x08, // If image 2 is convolved, would be poor or bad
+    PM_SUBTRACTION_MASK_CONVOLVE_BAD_1 = 0x10, // If image 1 is convolved, would be bad
+    PM_SUBTRACTION_MASK_CONVOLVE_BAD_2 = 0x20, // If image 2 is convolved, would be bad
+    PM_SUBTRACTION_MASK_BORDER         = 0x40, // Image border
+    PM_SUBTRACTION_MASK_REJ            = 0x80, // Previously tried as a stamp, and rejected
+} pmSubtractionMasks;
+
+
+/// Number of terms in a polynomial
+#define PM_SUBTRACTION_POLYTERMS(ORDER) (((ORDER) + 1) * ((ORDER) + 2) / 2)
+
+/// Set the indices for the normalisation and background terms
+#define PM_SUBTRACTION_INDICES(NORM,BG,KERNELS) { \
+    int numSpatial = PM_SUBTRACTION_POLYTERMS((KERNELS)->spatialOrder); /* Number of spatial terms */ \
+    NORM = (KERNELS)->num * numSpatial; \
+    BG = NORM + 1; \
+}
+
+/// Return the index for the start of the normalisation terms
+#define PM_SUBTRACTION_INDEX_NORM(KERNELS) \
+    ((KERNELS)->num * PM_SUBTRACTION_POLYTERMS((KERNELS)->spatialOrder))
+
+/// Return the index for the start of the background terms
+#define PM_SUBTRACTION_INDEX_BG(KERNELS) \
+    (((KERNELS)->num * PM_SUBTRACTION_POLYTERMS((KERNELS)->spatialOrder)) + 1)
+
+
+/// Convolve the reference stamp with the kernel components
+bool pmSubtractionConvolveStamp(pmSubtractionStamp *stamp, ///< Stamp to convolve
+                                const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                int footprint ///< Half-size of region over which to calculate equation
+    );
+
+/// Reject stamps
+int pmSubtractionRejectStamps(pmSubtractionKernels *kernels, ///< Kernel parameters to update
+                              pmSubtractionStampList *stamps, ///< Stamps
+                              const psVector *deviations, ///< Deviations for each stamp
+                              psImage *subMask, ///< Subtraction mask
+                              float sigmaRej, ///< Number of RMS deviations above zero at which to reject
+                              int footprint ///< Half-size of stamp
+    );
+
+/// Generate an image of the convolution kernel
+psImage *pmSubtractionKernelImage(const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                  float x, float y,///< Normalised position [-1,1] for which to generate image
+                                  bool wantDual ///< Calculate for the dual kernel?
+                                  );
+
+/// Return the variance factor for a kernel
+///
+/// The variance factor allows conversion from the large-scale variance (which is what is calculated by
+/// pmSubtractionConvolve) and the small-scale (pixel-to-pixel) variance.
+float pmSubtractionVarianceFactor(const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                  float x, float y, ///< Normalised position [-1,1]
+                                  bool wantDual ///< Calculate for the dual kernel?
+    );
+
+/// Generate images of the convolution kernel elements
+psArray *pmSubtractionKernelSolutions(const psVector *solution, ///< Solution vector
+                                      const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                      float x, float y ///< Normalised position [-1,1] for images
+    );
+
+/// Execute a thread job to convolve a patch of the image
+bool pmSubtractionConvolveThread(psThreadJob *job ///< Job to execute
+    );
+
+/// Convolve image in preparation for subtraction
+bool pmSubtractionConvolve(pmReadout *out1, ///< Output image 1
+                           pmReadout *out2, ///< Output image 2 (DUAL mode only)
+                           const pmReadout *ro1, // Input image 1
+                           const pmReadout *ro2, // Input image 2
+                           psImage *subMask, ///< Subtraction mask (or NULL)
+                           psMaskType maskBad, ///< Mask value to give bad pixels
+                           psMaskType maskPoor, ///< Mask value to give poor pixels
+                           float poorFrac, ///< Fraction for "poor"
+                           const psRegion *region, ///< Region to convolve (or NULL)
+                           const pmSubtractionKernels *kernels, ///< Kernel parameters
+                           bool doBG,   ///< Apply background term?
+                           bool useFFT  ///< Use Fast Fourier Transform for the convolution?
+    );
+
+/// Generate the convolution of an image, given a precalculated kernel
+///
+/// The 'image' is a kernel for convenience --- intended to be a stamp
+psKernel *p_pmSubtractionConvolveStampPrecalc(const psKernel *image, ///< Image to convolve
+                                              const psKernel *kernel ///< Kernel by which to convolve
+    );
+
+/// Given (normalised) coordinates (x,y), generate a matrix where the elements (i,j) are x^i * y^j
+psImage *p_pmSubtractionPolynomial(psImage *output, ///< Output matrix, or NULL
+                                   int spatialOrder, ///< Maximum spatial polynomial order
+                                   float x, float y ///< Normalised position of interest, [-1,1]
+    );
+
+/// Given pixel coordinates (x,y), generate a matrix where the elements (i,j) are x^i * y^j
+///
+/// Same as p_pmSubtractionPolynomial except that the normalisation is applied
+psImage *p_pmSubtractionPolynomialFromCoords(psImage *output, ///< Output matrix, or NULL
+                                             const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                             int numCols, int numRows, ///< Size of image of interest
+                                             int x, int y ///< Position of interest
+    );
+
+/// Return the radius from the centre of the convolution kernel that distinguishes "bad" and "poor" pixels
+int p_pmSubtractionBadRadius(psKernel *preKernel, ///< Pre-calculated convolution kernel
+                             const pmSubtractionKernels *kernels, ///< Kernel parameters
+                             const psImage *polyValues, ///< Polynomial values
+                             bool wantDual, ///< Calculate for the dual kernel?
+                             float poorFrac ///< Fraction for "poor"
+    );
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionEquation.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionEquation.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionEquation.c	(revision 22322)
@@ -0,0 +1,1152 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmSubtraction.h"
+#include "pmSubtractionKernels.h"
+#include "pmSubtractionStamps.h"
+#include "pmSubtractionThreads.h"
+
+#include "pmSubtractionEquation.h"
+
+
+//#define TESTING
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Private (file-static) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Calculate the sum over a stamp product
+static inline double calculateSumProduct(const psKernel *image1, // First image in multiplication
+                                         const psKernel *image2, // Second image in multiplication
+                                         const psKernel *weight, // Weight image
+                                         int footprint // (Half-)Size of stamp
+    )
+{
+    double sum = 0.0;                   // Sum of the image products
+    for (int y = - footprint; y <= footprint; y++) {
+        for (int x = - footprint; x <= footprint; x++) {
+            sum += image1->kernel[y][x] * image2->kernel[y][x] / 1.0; // weight->kernel[y][x];
+        }
+    }
+    return sum;
+}
+
+// Calculate a single element of the least-squares matrix, with the polynomial expansions in one direction
+static inline bool calculateMatrixElement1(psImage *matrix, // Matrix to calculate
+                                           int i, int j, // Coordinates of element
+                                           const psKernel *image1, // First image in multiplication
+                                           const psKernel *image2, // Second image in multiplication
+                                           const psKernel *weight, // Weight image
+                                           const psImage *polyValues, // Spatial polynomial values
+                                           int numKernels, // Number of kernel basis functions
+                                           int footprint, // (Half-)Size of stamp
+                                           int spatialOrder, // Maximum order of spatial variation
+                                           bool symmetric // Is the matrix symmetric?
+    )
+{
+    double sum = calculateSumProduct(image1, image2, weight, footprint); // Sum of the image products
+    if (!isfinite(sum)) {
+        return false;
+    }
+
+    // Generate the pseudo-convolutions from the spatial polynomial terms
+    for (int iyOrder = 0, iIndex = i; iyOrder <= spatialOrder; iyOrder++) {
+        for (int ixOrder = 0; ixOrder <= spatialOrder - iyOrder; ixOrder++, iIndex += numKernels) {
+            double convPoly = sum * polyValues->data.F64[iyOrder][ixOrder];
+
+            assert(iIndex < matrix->numRows && j < matrix->numCols);
+
+            matrix->data.F64[iIndex][j] = convPoly;
+            if (symmetric) {
+
+                assert(iIndex < matrix->numCols && j < matrix->numRows);
+
+                matrix->data.F64[j][iIndex] = convPoly;
+            }
+        }
+    }
+    return true;
+}
+
+// Calculate a single element of the least-squares matrix, with the polynomial expansions in both directions
+static inline bool calculateMatrixElement2(psImage *matrix, // Matrix to calculate
+                                           int i, int j, // Coordinates of element
+                                           const psKernel *image1, // First image in multiplication
+                                           const psKernel *image2, // Second image in multiplication
+                                           const psKernel *weight, // Weight image
+                                           const psImage *polyValues, // Spatial polynomial values
+                                           int numKernels, // Number of kernel basis functions
+                                           int footprint, // (Half-)Size of stamp
+                                           int spatialOrder, // Maximum order of spatial variation
+                                           bool symmetric // Is the matrix symmetric?
+    )
+{
+    double sum = calculateSumProduct(image1, image2, weight, footprint); // Sum of the image products
+    if (!isfinite(sum)) {
+        return false;
+    }
+
+    // Generate the pseudo-convolutions from the spatial polynomial terms
+    for (int iyOrder = 0, iIndex = i; iyOrder <= spatialOrder; iyOrder++) {
+        for (int ixOrder = 0; ixOrder <= spatialOrder - iyOrder; ixOrder++, iIndex += numKernels) {
+            double iPoly = polyValues->data.F64[iyOrder][ixOrder]; // Value of polynomial
+            for (int jyOrder = 0, jIndex = j; jyOrder <= spatialOrder; jyOrder++) {
+                for (int jxOrder = 0; jxOrder <= spatialOrder - jyOrder; jxOrder++, jIndex += numKernels) {
+                    double convPoly = sum * iPoly * polyValues->data.F64[jyOrder][jxOrder];
+
+                    assert(iIndex < matrix->numRows && jIndex < matrix->numCols);
+
+                    matrix->data.F64[iIndex][jIndex] = convPoly;
+                    if (symmetric) {
+
+                        assert(iIndex < matrix->numCols && jIndex < matrix->numRows);
+
+                        matrix->data.F64[jIndex][iIndex] = convPoly;
+                    }
+                }
+            }
+        }
+    }
+    return true;
+}
+
+// Calculate the square part of the matrix derived from multiplying convolutions
+static bool calculateMatrixSquare(psImage *matrix, // Matrix to calculate
+                                  const psArray *convolutions1, // Convolutions for element 1
+                                  const psArray *convolutions2, // Convolutions for element 2
+                                  const psKernel *weight, // Weight image
+                                  const psImage *polyValues, // Polynomial values
+                                  int numKernels, // Number of kernel basis functions
+                                  int spatialOrder, // Order of spatial variation
+                                  int footprint // Half-size of stamp
+                                  )
+{
+    bool symmetric = (convolutions1 == convolutions2 ? true : false); // Is matrix symmetric?
+
+    for (int i = 0; i < numKernels; i++) {
+        psKernel *iConv = convolutions1->data[i]; // Convolution for i-th element
+
+        for (int j = (symmetric ? i : 0); j < numKernels; j++) {
+            psKernel *jConv = convolutions2->data[j]; // Convolution for j-th element
+
+            if (!calculateMatrixElement2(matrix, i, j, iConv, jConv, weight, polyValues, numKernels,
+                                         footprint, spatialOrder, symmetric)) {
+                psTrace("psModules.imcombine", 2, "Bad sumCC at %d, %d", i, j);
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+// Calculate least-squares matrix and vector
+static bool calculateMatrix(psImage *matrix, // Matrix to calculate
+                            const pmSubtractionKernels *kernels, // Kernel components
+                            const psArray *convolutions, // Convolutions of source with kernels
+                            const psKernel *input, // Input stamp, or NULL
+                            const psKernel *weight, // Weight stamp
+                            const psImage *polyValues, // Spatial polynomial values
+                            int footprint, // (Half-)Size of stamp
+                            bool normAndBG // Calculate normalisation and background terms?
+    )
+{
+    int numKernels = kernels->num;      // Number of kernel components
+    int spatialOrder = kernels->spatialOrder; // Maximum order of spatial variation
+    int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variation terms
+    int bgOrder = kernels->bgOrder;     // Maximum order of background fit
+    int numBackground = normAndBG ? PM_SUBTRACTION_POLYTERMS(bgOrder) : 0; // Number of background terms
+    int numTerms = numKernels * numSpatial + (normAndBG ? 1 + numBackground : 0); // Total number of terms
+    assert(matrix);
+    assert(matrix->numCols == matrix->numRows);
+    assert(matrix->numCols == numTerms);
+    assert(convolutions && convolutions->n == numKernels);
+    assert(polyValues);
+    assert(!normAndBG || input);        // If we want the normalisation and BG, then we need the input image
+
+    // Square part of the matrix (convolution-convolution products)
+    if (!calculateMatrixSquare(matrix, convolutions, convolutions, weight, polyValues, numKernels,
+                               spatialOrder, footprint)) {
+        return false;
+    }
+
+    // XXX To support higher-order background model than simply constant, the below code needs to be updated.
+    if (normAndBG) {
+        int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
+        int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
+
+        for (int i = 0; i < numKernels; i++) {
+            psKernel *conv = convolutions->data[i]; // Convolution for i-th element
+
+            // Normalisation-convolution terms
+            if (!calculateMatrixElement1(matrix, i, normIndex, conv, input, weight, polyValues, numKernels,
+                                         footprint, spatialOrder, true)) {
+                psTrace("psModules.imcombine", 2, "Bad sumIC at %d", i);
+                return false;
+            }
+
+            // Background-convolution terms
+            double sumC = 0.0;          // Sum of the convolution
+            for (int y = - footprint; y <= footprint; y++) {
+                for (int x = - footprint; x <= footprint; x++) {
+                    sumC += conv->kernel[y][x] / 1.0; // weight->kernel[y][x];
+                }
+            }
+            if (!isfinite(sumC)) {
+                psTrace("psModules.imcombine", 2, "Bad sumC at %d", i);
+                return false;
+            }
+
+            for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) {
+                for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
+                    double value = sumC * polyValues->data.F64[yOrder][xOrder];
+                    matrix->data.F64[index][bgIndex] = value;
+                    matrix->data.F64[bgIndex][index] = value;
+                }
+            }
+        }
+
+        // Background only, normalisation only, and background-normalisation terms
+        double sum1 = 0.0;              // Sum of the weighting
+        double sumI = 0.0;              // Sum of the input
+        double sumII = 0.0;             // Sum of the input squared
+        for (int y = - footprint; y <= footprint; y++) {
+            for (int x = - footprint; x <= footprint; x++) {
+                double invNoise2 = 1.0 / 1.0; // weight->kernel[y][x];
+                double value = input->kernel[y][x] * invNoise2;
+                sumI += value;
+                sumII += value * input->kernel[y][x];
+                sum1 += invNoise2;
+            }
+        }
+        if (!isfinite(sumI)) {
+            psTrace("psModules.imcombine", 2, "Bad sumI detected");
+            return false;
+        }
+        if (!isfinite(sumII)) {
+            psTrace("psModules.imcombine", 2, "Bad sumII detected");
+            return false;
+        }
+        if (!isfinite(sum1)) {
+            psTrace("psModules.imcombine", 2, "Bad sum1 detected");
+            return false;
+        }
+        matrix->data.F64[normIndex][normIndex] = sumII;
+        matrix->data.F64[bgIndex][bgIndex] = sum1;
+        matrix->data.F64[normIndex][bgIndex] = sumI;
+        matrix->data.F64[bgIndex][normIndex] = sumI;
+    }
+
+    return true;
+}
+
+
+// Calculate least-squares matrix and vector
+static bool calculateVector(psVector *vector, // Vector to calculate, or NULL
+                            const pmSubtractionKernels *kernels, // Kernel components
+                            const psArray *convolutions, // Convolutions of source with kernels
+                            const psKernel *input, // Input stamp, or NULL if !normAndBG
+                            const psKernel *target, // Target stamp
+                            const psKernel *weight, // Weight stamp
+                            const psImage *polyValues, // Spatial polynomial values
+                            int footprint, // (Half-)Size of stamp
+                            bool normAndBG // Calculate normalisation and background terms?
+    )
+{
+    int numKernels = kernels->num;      // Number of kernel components
+    int spatialOrder = kernels->spatialOrder; // Maximum order of spatial variation
+    int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variation terms
+    int bgOrder = kernels->bgOrder;     // Maximum order of background fit
+    int numBackground = normAndBG ? PM_SUBTRACTION_POLYTERMS(bgOrder) : 0; // Number of background terms
+    int numTerms = numKernels * numSpatial + (normAndBG ? 1 + numBackground : 0); // Total number of terms
+    assert(vector && vector->n == numTerms);
+    assert(convolutions && convolutions->n == numKernels);
+    assert(target);
+    assert(polyValues);
+    assert(!normAndBG || input);       // If we want the normalisation and BG, then we need the input image
+
+    // Convolution terms
+    for (int i = 0; i < numKernels; i++) {
+        psKernel *conv = convolutions->data[i]; // Convolution for i-th element
+        double sumTC = 0.0;          // Sum of the target and convolution
+        for (int y = - footprint; y <= footprint; y++) {
+            for (int x = - footprint; x <= footprint; x++) {
+                sumTC += target->kernel[y][x] * conv->kernel[y][x] / 1.0; // weight->kernel[y][x];
+            }
+        }
+        if (!isfinite(sumTC)) {
+            psTrace("psModules.imcombine", 2, "Bad sumTC at %d", i);
+            return false;
+        }
+        for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) {
+            for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
+                vector->data.F64[index] = sumTC * polyValues->data.F64[yOrder][xOrder];
+            }
+        }
+    }
+
+    if (normAndBG) {
+        // Background terms
+        double sumT = 0.0;              // Sum of the target
+        double sumIT = 0.0;             // Sum of the input-target product
+        for (int y = - footprint; y <= footprint; y++) {
+            for (int x = - footprint; x <= footprint; x++) {
+                float value = target->kernel[y][x] / 1.0; // weight->kernel[y][x];
+                sumIT += value * input->kernel[y][x];
+                sumT += value;
+            }
+        }
+        if (!isfinite(sumT)) {
+            psTrace("psModules.imcombine", 2, "Bad sumI detected");
+            return false;
+        }
+        if (!isfinite(sumIT)) {
+            psTrace("psModules.imcombine", 2, "Bad sumIT detected");
+            return false;
+        }
+
+        int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation term
+        vector->data.F64[normIndex] = sumIT;
+        int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background term
+        vector->data.F64[bgIndex] = sumT;
+    }
+
+    return true;
+}
+
+
+
+// Calculate the cross-matrix, composed of convolutions of each image
+// Note that the cross-matrix is NOT square
+static bool calculateMatrixCross(psImage *matrix, // Matrix to calculate
+                                 const pmSubtractionKernels *kernels, // Kernel components
+                                 const psArray *convolutions1, // Convolutions of image 1
+                                 const psArray *convolutions2, // Convolutions of image 2
+                                 const psKernel *image1, // Image 1 stamp
+                                 const psKernel *weight, // Weight stamp
+                                 const psImage *polyValues, // Spatial polynomial values
+                                 int footprint // (Half-)Size of stamp
+                                 )
+{
+    assert(matrix);
+    int numKernels = kernels->num;      // Number of kernel components
+    int spatialOrder = kernels->spatialOrder; // Maximum order of spatial variation
+    int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial polynomial terms
+    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
+    int numCols = numKernels * numSpatial + 1 + numBackground; // Number of columns
+    int numRows = numKernels * numSpatial; // Number of rows
+    assert(matrix->numCols == numCols && matrix->numRows == numRows);
+    assert(convolutions1 && convolutions1->n == numKernels);
+    assert(convolutions2 && convolutions2->n == numKernels);
+
+    int normIndex, bgIndex;             // Indices in matrix for normalisation and background terms
+    PM_SUBTRACTION_INDICES(normIndex, bgIndex, kernels);
+
+    if (!calculateMatrixSquare(matrix, convolutions1, convolutions2, weight, polyValues, numKernels,
+                               spatialOrder, footprint)) {
+        return false;
+    }
+
+    for (int i = 0; i < numKernels; i++) {
+        // Normalisation
+        psKernel *conv = convolutions2->data[i]; // Convolution
+        if (!calculateMatrixElement1(matrix, i, normIndex, conv, image1, weight, polyValues, numKernels,
+                                     footprint, spatialOrder, false)) {
+            psTrace("psModules.imcombine", 2, "Bad sumIC at %d", i);
+            return false;
+        }
+
+        // Background
+        double sumC = 0.0;              // Sum of the weighting
+        for (int y = - footprint; y <= footprint; y++) {
+            for (int x = - footprint; x <= footprint; x++) {
+                sumC += conv->kernel[y][x] / 1.0; // weight->kernel[y][x];
+            }
+        }
+        if (!isfinite(sumC)) {
+            psTrace("psModules.imcombine", 2, "Bad sumC detected at %d", i);
+            return false;
+        }
+        for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) {
+            for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
+                matrix->data.F64[index][bgIndex] = sumC * polyValues->data.F64[yOrder][xOrder];
+            }
+        }
+    }
+
+    return true;
+}
+
+
+// Add in penalty term to least-squares vector
+static bool calculatePenalty(psVector *vector, // Vector to which to add in penalty term
+                             const pmSubtractionKernels *kernels // Kernel parameters
+    )
+{
+    if (kernels->penalty == 0.0) {
+        return true;
+    }
+
+    psVector *penalties = kernels->penalties; // Penalties for each kernel component
+    int spatialOrder = kernels->spatialOrder; // Order of spatial variations
+    int numKernels = kernels->num; // Number of kernel components
+    for (int i = 0; i < numKernels; i++) {
+        for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) {
+            for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
+                vector->data.F64[index] -= penalties->data.F32[i];
+            }
+        }
+    }
+
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Semi-public functions
+// XXX We might like to define these functions as "extern inline" but gcc currently doesn't handle this in c99
+// mode.  See http://gcc.gnu.org/ml/gcc/2006-11/msg00006.html
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Calculate the value of a polynomial, specified by coefficients and polynomial values
+double p_pmSubtractionCalculatePolynomial(const psVector *coeff, // Coefficients
+                                                 const psImage *polyValues, // Polynomial values
+                                                 int order, // Order of polynomials
+                                                 int index, // Index at which to begin
+                                                 int step // Step between subsequent indices
+                                                 )
+{
+    double sum = 0.0;                   // Value of the polynomial sum
+    for (int yOrder = 0; yOrder <= order; yOrder++) {
+        for (int xOrder = 0; xOrder <= order - yOrder; xOrder++, index += step) {
+
+            assert(index < coeff->n);
+
+            sum += coeff->data.F64[index] * polyValues->data.F64[yOrder][xOrder];
+        }
+    }
+    return sum;
+}
+
+double p_pmSubtractionSolutionCoeff(const pmSubtractionKernels *kernels, const psImage *polyValues,
+                                           int index, bool wantDual)
+{
+#if 0
+    // This is probably in a tight loop, so don't check inputs
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NAN);
+    PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NAN);
+    PS_ASSERT_IMAGE_NON_NULL(polyValues, NAN);
+    PS_ASSERT_INT_POSITIVE(index, NAN);
+#endif
+
+    psVector *solution = wantDual ? kernels->solution2 : kernels->solution1; // Solution vector
+    return p_pmSubtractionCalculatePolynomial(solution, polyValues, kernels->spatialOrder, index,
+                                              kernels->num);
+}
+
+double p_pmSubtractionSolutionNorm(const pmSubtractionKernels *kernels)
+{
+#if 0
+    // This is probably in a tight loop, so don't check inputs
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NAN);
+    PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NAN);
+    PS_ASSERT_IMAGE_NON_NULL(polyValues, NAN);
+#endif
+
+    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
+    return kernels->solution1->data.F64[normIndex];
+}
+
+double p_pmSubtractionSolutionBackground(const pmSubtractionKernels *kernels,
+                                                const psImage *polyValues)
+{
+#if 0
+    // This is probably in a tight loop, so don't check inputs
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NAN);
+    PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NAN);
+    PS_ASSERT_IMAGE_NON_NULL(polyValues, NAN);
+#endif
+
+    int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
+    return p_pmSubtractionCalculatePolynomial(kernels->solution1, polyValues, kernels->bgOrder, bgIndex, 1);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmSubtractionCalculateEquationThread(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    pmSubtractionStampList *stamps = job->args->data[0]; // List of stamps
+    const pmSubtractionKernels *kernels = job->args->data[1]; // Kernels
+    int index = PS_SCALAR_VALUE(job->args->data[2], S32); // Stamp index
+
+    return pmSubtractionCalculateEquationStamp(stamps, kernels, index);
+}
+
+bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels,
+                                         int index)
+{
+    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
+    PS_ASSERT_INT_NONNEGATIVE(index, false);
+    PS_ASSERT_INT_LESS_THAN(index, stamps->num, false);
+
+    int footprint = stamps->footprint;  // Half-size of stamps
+    int spatialOrder = kernels->spatialOrder; // Maximum order of spatial variation
+    int numKernels = kernels->num;      // Number of kernel basis functions
+    int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variations
+    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
+
+    // Total number of parameters to solve for: coefficient of each kernel basis function, multipled by the
+    // number of coefficients for the spatial polynomial, normalisation and a constant background offset.
+    int numParams = numKernels * numSpatial + 1 + numBackground;
+
+    pmSubtractionStamp *stamp = stamps->stamps->data[index]; // Stamp of interest
+    psAssert(stamp->status == PM_SUBTRACTION_STAMP_CALCULATE, "We only operate on stamps with this state.");
+
+    // Generate convolutions
+    if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to convolve stamp %d.", index);
+        return NULL;
+    }
+
+#ifdef TESTING
+    for (int j = 0; j < numKernels; j++) {
+        if (stamp->convolutions1) {
+            psString convName = NULL;
+            psStringAppend(&convName, "conv1_%03d_%03d.fits", index, j);
+            psFits *fits = psFitsOpen(convName, "w");
+            psFree(convName);
+            psKernel *conv = stamp->convolutions1->data[j];
+            psFitsWriteImage(fits, NULL, conv->image, 0, NULL);
+            psFitsClose(fits);
+        }
+
+        if (stamp->convolutions2) {
+            psString convName = NULL;
+            psStringAppend(&convName, "conv2_%03d_%03d.fits", index, j);
+            psFits *fits = psFitsOpen(convName, "w");
+            psFree(convName);
+            psKernel *conv = stamp->convolutions2->data[j];
+            psFitsWriteImage(fits, NULL, conv->image, 0, NULL);
+            psFitsClose(fits);
+        }
+    }
+#endif
+
+    psImage *polyValues = p_pmSubtractionPolynomial(NULL, spatialOrder,
+                                                    stamp->xNorm, stamp->yNorm); // Polynomial terms
+
+    bool new = stamp->vector1 ? false : true; // Is this a new run?
+    if (new) {
+        stamp->matrix1 = psImageAlloc(numParams, numParams, PS_TYPE_F64);
+        stamp->vector1 = psVectorAlloc(numParams, PS_TYPE_F64);
+    }
+#ifdef TESTING
+    psImageInit(stamp->matrix1, NAN);
+    psVectorInit(stamp->vector1, NAN);
+#endif
+
+    bool status;                    // Status of least-squares matrix/vector calculation
+    switch (kernels->mode) {
+      case PM_SUBTRACTION_MODE_1:
+        status = calculateMatrix(stamp->matrix1, kernels, stamp->convolutions1, stamp->image1,
+                                 stamp->weight, polyValues, footprint, true);
+        status &= calculateVector(stamp->vector1, kernels, stamp->convolutions1, stamp->image1,
+                                  stamp->image2, stamp->weight, polyValues, footprint, true);
+        break;
+      case PM_SUBTRACTION_MODE_2:
+        status = calculateMatrix(stamp->matrix1, kernels, stamp->convolutions2, stamp->image2,
+                                 stamp->weight, polyValues, footprint, true);
+        status &= calculateVector(stamp->vector1, kernels, stamp->convolutions2, stamp->image2,
+                                  stamp->image1, stamp->weight, polyValues, footprint, true);
+        break;
+      case PM_SUBTRACTION_MODE_DUAL:
+        if (new) {
+            stamp->matrix2 = psImageAlloc(numKernels * numSpatial, numKernels * numSpatial, PS_TYPE_F64);
+            stamp->matrixX = psImageAlloc(numParams, numKernels * numSpatial, PS_TYPE_F64);
+            stamp->vector2 = psVectorAlloc(numKernels * numSpatial, PS_TYPE_F64);
+        }
+#ifdef TESTING
+        psImageInit(stamp->matrix2, NAN);
+        psImageInit(stamp->matrixX, NAN);
+        psVectorInit(stamp->vector2, NAN);
+#endif
+        status  = calculateMatrix(stamp->matrix1, kernels, stamp->convolutions1, stamp->image1,
+                                  stamp->weight, polyValues, footprint, true);
+        status &= calculateMatrix(stamp->matrix2, kernels, stamp->convolutions2, NULL,
+                                  stamp->weight, polyValues, footprint, false);
+        status &= calculateMatrixCross(stamp->matrixX, kernels, stamp->convolutions1,
+                                       stamp->convolutions2, stamp->image1, stamp->weight, polyValues,
+                                       footprint);
+        status &= calculateVector(stamp->vector1, kernels, stamp->convolutions1, stamp->image1,
+                                  stamp->image2, stamp->weight, polyValues, footprint, true);
+        status &= calculateVector(stamp->vector2, kernels, stamp->convolutions2, NULL,
+                                  stamp->image2, stamp->weight, polyValues, footprint, false);
+        break;
+      default:
+        psAbort("Unsupported subtraction mode: %x", kernels->mode);
+    }
+
+    if (!status) {
+        stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
+        psWarning("Rejecting stamp %d (%d,%d) because of bad equation",
+                  index, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
+    } else {
+        stamp->status = PM_SUBTRACTION_STAMP_USED;
+    }
+
+#ifdef TESTING
+    if (psTraceGetLevel("psModules.imcombine.equation") >= 10) {
+        psString matrixName = NULL;
+        psStringAppend(&matrixName, "matrix1_%d.fits", index);
+        psFits *matrixFile = psFitsOpen(matrixName, "w");
+        psFree(matrixName);
+        psFitsWriteImage(matrixFile, NULL, stamp->matrix1, 0, NULL);
+        psFitsClose(matrixFile);
+
+        matrixName = NULL;
+        psStringAppend(&matrixName, "vector1_%d.fits", index);
+        psImage *dummy = psImageAlloc(stamp->vector1->n, 1, PS_TYPE_F64);
+        memcpy(dummy->data.F64[0], stamp->vector1->data.F64,
+               PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector1->n);
+        matrixFile = psFitsOpen(matrixName, "w");
+        psFree(matrixName);
+        psFitsWriteImage(matrixFile, NULL, dummy, 0, NULL);
+        psFree(dummy);
+        psFitsClose(matrixFile);
+
+        if (stamp->vector2) {
+            matrixName = NULL;
+            psStringAppend(&matrixName, "vector2_%d.fits", index);
+            dummy = psImageAlloc(stamp->vector2->n, 1, PS_TYPE_F64);
+            memcpy(dummy->data.F64[0], stamp->vector2->data.F64,
+                   PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector2->n);
+            matrixFile = psFitsOpen(matrixName, "w");
+            psFree(matrixName);
+            psFitsWriteImage(matrixFile, NULL, dummy, 0, NULL);
+            psFree(dummy);
+            psFitsClose(matrixFile);
+        }
+
+        if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+            matrixName = NULL;
+            psStringAppend(&matrixName, "matrix2_%d.fits", index);
+            matrixFile = psFitsOpen(matrixName, "w");
+            psFree(matrixName);
+            psFitsWriteImage(matrixFile, NULL, stamp->matrix2, 0, NULL);
+            psFitsClose(matrixFile);
+
+            matrixName = NULL;
+            psStringAppend(&matrixName, "matrixX_%d.fits", index);
+            matrixFile = psFitsOpen(matrixName, "w");
+            psFree(matrixName);
+            psFitsWriteImage(matrixFile, NULL, stamp->matrixX, 0, NULL);
+            psFitsClose(matrixFile);
+        }
+    }
+#endif
+
+    psFree(polyValues);
+
+    return true;
+}
+
+bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels)
+{
+    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
+
+    // We iterate over each stamp, allocate the matrix and vectors if
+    // necessary, and then calculate those matrix/vectors.
+    for (int i = 0; i < stamps->num; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+        if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE) {
+            continue;
+        }
+
+        if (pmSubtractionThreaded()) {
+            psThreadJob *job = psThreadJobAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION");
+            psArrayAdd(job->args, 1, stamps);
+            psArrayAdd(job->args, 1, (pmSubtractionKernels*)kernels); // Casting away const to put on array
+            PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
+            if (!psThreadJobAddPending(job)) {
+                psFree(job);
+                return false;
+            }
+            psFree(job);
+        } else {
+            pmSubtractionCalculateEquationStamp(stamps, kernels, i);
+        }
+    }
+
+    if (!psThreadPoolWait(true)) {
+        psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
+        return false;
+    }
+
+    return true;
+}
+
+bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps)
+{
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
+    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
+
+    // Check inputs
+    int numParams = -1;                // Number of parameters
+    int numParams2 = 0;                // Number of parameters for part solution (DUAL mode)
+    for (int i = 0; i < stamps->num; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+        PS_ASSERT_PTR_NON_NULL(stamp, false);
+        if (stamp->status != PM_SUBTRACTION_STAMP_USED) {
+            continue;
+        }
+
+        PS_ASSERT_VECTOR_NON_NULL(stamp->vector1, false);
+        if (numParams == -1) {
+            numParams = stamp->vector1->n;
+        }
+        PS_ASSERT_VECTOR_SIZE(stamp->vector1, (long)numParams, false);
+        PS_ASSERT_VECTOR_TYPE(stamp->vector1, PS_TYPE_F64, false);
+        PS_ASSERT_IMAGE_NON_NULL(stamp->matrix1, false);
+        PS_ASSERT_IMAGE_SIZE(stamp->matrix1, numParams, numParams, false);
+        PS_ASSERT_IMAGE_TYPE(stamp->matrix1, PS_TYPE_F64, false);
+
+        if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+            PS_ASSERT_IMAGE_NON_NULL(stamp->matrix2, false);
+            PS_ASSERT_IMAGE_NON_NULL(stamp->matrixX, false);
+            if (numParams2 == 0) {
+                numParams2 = stamp->matrix2->numCols;
+            }
+            PS_ASSERT_IMAGE_SIZE(stamp->matrix2, numParams2, numParams2, false);
+            PS_ASSERT_IMAGE_SIZE(stamp->matrixX, numParams, numParams2, false);
+            PS_ASSERT_IMAGE_TYPE(stamp->matrix2, PS_TYPE_F64, false);
+            PS_ASSERT_IMAGE_TYPE(stamp->matrixX, PS_TYPE_F64, false);
+            PS_ASSERT_VECTOR_NON_NULL(stamp->vector2, false);
+            PS_ASSERT_VECTOR_SIZE(stamp->vector2, (long)numParams2, false);
+            PS_ASSERT_VECTOR_TYPE(stamp->vector2, PS_TYPE_F64, false);
+        }
+    }
+    if (numParams == -1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "No suitable stamps found.");
+        return NULL;
+    }
+
+    if (kernels->mode != PM_SUBTRACTION_MODE_DUAL) {
+        // Accumulate the least-squares matricies and vectors
+        psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64); // Combined matrix
+        psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64); // Combined vector
+        psVectorInit(sumVector, 0.0);
+        psImageInit(sumMatrix, 0.0);
+        int numStamps = 0;              // Number of good stamps
+        for (int i = 0; i < stamps->num; i++) {
+            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+            if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
+                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix1);
+                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector1);
+                numStamps++;
+            }
+        }
+        calculatePenalty(sumVector, kernels);
+
+#ifdef TESTING
+        {
+            psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL);
+            psFits *fits = psFitsOpen("matrixInv.fits", "w");
+            psFitsWriteImage(fits, NULL, inverse, 0, NULL);
+            psFitsClose(fits);
+            psFree(inverse);
+        }
+        {
+            psImage *X = psMatrixInvert(NULL, sumMatrix, NULL);
+            psImage *Xt = psMatrixTranspose(NULL, X);
+            psImage *XtX = psMatrixMultiply(NULL, Xt, X);
+            psFits *fits = psFitsOpen("matrixErr.fits", "w");
+            psFitsWriteImage(fits, NULL, XtX, 0, NULL);
+            psFitsClose(fits);
+            psFree(X);
+            psFree(Xt);
+            psFree(XtX);
+        }
+#endif
+
+        psVector *permutation = NULL;       // Permutation vector, required for LU decomposition
+        psImage *luMatrix = psMatrixLUD(NULL, &permutation, sumMatrix);
+        psFree(sumMatrix);
+        if (!luMatrix) {
+            psError(PS_ERR_UNKNOWN, true, "LU Decomposition of least-squares matrix failed.\n");
+            psFree(sumVector);
+            psFree(luMatrix);
+            psFree(permutation);
+            return NULL;
+        }
+        kernels->solution1 = psMatrixLUSolve(kernels->solution1, luMatrix, sumVector, permutation);
+
+        psFree(sumVector);
+        psFree(luMatrix);
+        psFree(permutation);
+        if (!kernels->solution1) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to solve the least-squares system.\n");
+            return NULL;
+        }
+    } else {
+        // Dual convolution solution
+
+        // Accumulation of stamp matrices/vectors
+        psImage *sumMatrix1 = psImageAlloc(numParams, numParams, PS_TYPE_F64);
+        psImage *sumMatrix2 = psImageAlloc(numParams2, numParams2, PS_TYPE_F64);
+        psImage *sumMatrixX = psImageAlloc(numParams, numParams2, PS_TYPE_F64);
+        psVector *sumVector1 = psVectorAlloc(numParams, PS_TYPE_F64);
+        psVector *sumVector2 = psVectorAlloc(numParams, PS_TYPE_F64);
+        psImageInit(sumMatrix1, 0.0);
+        psImageInit(sumMatrix2, 0.0);
+        psImageInit(sumMatrixX, 0.0);
+        psVectorInit(sumVector1, 0.0);
+        psVectorInit(sumVector2, 0.0);
+
+        int numStamps = 0;              // Number of good stamps
+        for (int i = 0; i < stamps->num; i++) {
+            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+            if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
+                (void)psBinaryOp(sumMatrix1, sumMatrix1, "+", stamp->matrix1);
+                (void)psBinaryOp(sumMatrix2, sumMatrix2, "+", stamp->matrix2);
+                (void)psBinaryOp(sumMatrixX, sumMatrixX, "+", stamp->matrixX);
+                (void)psBinaryOp(sumVector1, sumVector1, "+", stamp->vector1);
+                (void)psBinaryOp(sumVector2, sumVector2, "+", stamp->vector2);
+                numStamps++;
+            }
+        }
+        calculatePenalty(sumVector1, kernels);
+        calculatePenalty(sumVector2, kernels);
+
+        // Pure matrix operations
+
+        // A * a = Ct * b + d
+        // C * a = B  * b + e
+        //
+        // a = (Ct * Bi * C - A)i (Ct * Bi * e - d)
+        // b = Bi * (C * a - e)
+        psVector *a = psVectorRecycle(kernels->solution1, numParams, PS_TYPE_F64);
+        psVector *b = psVectorRecycle(kernels->solution2, numParams2, PS_TYPE_F64);
+#ifdef TESTING
+        psVectorInit(a, NAN);
+        psVectorInit(b, NAN);
+#endif
+        psImage *A = sumMatrix1;
+        psImage *B = sumMatrix2;
+        psImage *C = sumMatrixX;
+        psVector *d = sumVector1;
+        psVector *e = sumVector2;
+
+        assert(a->n == numParams);
+        assert(b->n == numParams2);
+        assert(A->numRows == numParams && A->numCols == numParams);
+        assert(B->numRows == numParams2 && B->numCols == numParams2);
+        assert(C->numRows == numParams2 && C->numCols == numParams);
+        assert(d->n == numParams);
+        assert(e->n == numParams2);
+
+        psImage *Bi = psMatrixInvert(NULL, B, NULL);
+        assert(Bi->numRows == numParams2 && Bi->numCols == numParams2);
+        psImage *Ct = psMatrixTranspose(NULL, C);
+        assert(Ct->numRows == numParams && Ct->numCols == numParams2);
+
+        psImage *BiC = psMatrixMultiply(NULL, Bi, C);
+        assert(BiC->numRows == numParams2 && BiC->numCols == numParams);
+        psImage *CtBi = psMatrixMultiply(NULL, Ct, Bi);
+        assert(CtBi->numRows == numParams && CtBi->numCols == numParams2);
+
+        psImage *CtBiC = psMatrixMultiply(NULL, Ct, BiC);
+        assert(CtBiC->numRows == numParams && CtBiC->numCols == numParams);
+
+        psImage *F = (psImage*)psBinaryOp(NULL, CtBiC, "-", A);
+        assert(F->numRows == numParams && F->numCols == numParams);
+        float det = 0.0;
+        psImage *Fi = psMatrixInvert(NULL, F, &det);
+        assert(Fi->numRows == numParams && Fi->numCols == numParams);
+        psTrace("psModules.imcombine", 4, "Determinant of F: %f\n", det);
+
+        psVector *g = psVectorAlloc(numParams, PS_TYPE_F64);
+#ifdef TESTING
+        psVectorInit(g, NAN);
+#endif
+        assert(CtBi->numRows == numParams && CtBi->numCols == numParams2);
+        assert(e->n == numParams2);
+        assert(d->n == numParams);
+        for (int i = 0; i < numParams; i++) {
+            double value = 0.0;
+            for (int j = 0; j < numParams2; j++) {
+                value += CtBi->data.F64[i][j] * e->data.F64[j];
+            }
+            g->data.F64[i] = value - d->data.F64[i];
+        }
+
+        assert(Fi->numRows == numParams && Fi->numCols == numParams);
+        assert(g->n == numParams);
+        for (int i = 0; i < numParams; i++) {
+            double value = 0.0;
+            for (int j = 0; j < numParams; j++) {
+                value += Fi->data.F64[i][j] * g->data.F64[j];
+            }
+            a->data.F64[i] = value;
+        }
+
+        psVector *h = psVectorAlloc(numParams2, PS_TYPE_F64);
+#ifdef TESTING
+        psVectorInit(h, NAN);
+#endif
+        assert(C->numRows == numParams2 && C->numCols == numParams);
+        assert(a->n == numParams);
+        assert(e->n == numParams2);
+        for (int i = 0; i < numParams2; i++) {
+            double value = 0.0;
+            for (int j = 0; j < numParams; j++) {
+                value += C->data.F64[i][j] * a->data.F64[j];
+            }
+            h->data.F64[i] = value - e->data.F64[i];
+        }
+
+        assert(Bi->numRows == numParams2 && Bi->numCols == numParams2);
+        assert(h->n == numParams2);
+        for (int i = 0; i < numParams2; i++) {
+            double value = 0.0;
+            for (int j = 0; j < numParams2; j++) {
+                value += Bi->data.F64[i][j] * h->data.F64[j];
+            }
+            b->data.F64[i] = value;
+        }
+
+
+#if 0
+        for (int i = 0; i < numParams; i++) {
+            double aVal1 = 0.0, bVal1 = 0.0;
+            for (int j = 0; j < numParams2; j++) {
+                aVal1 += A->data.F64[i][j] * a->data.F64[j];
+                bVal1 += Ct->data.F64[i][j] * b->data.F64[j];
+            }
+            bVal1 += d->data.F64[i];
+            for (int j = numParams2; j < numParams; j++) {
+                aVal1 += A->data.F64[i][j] * a->data.F64[j];
+            }
+            printf("%d: %lf\n", i, aVal1 - bVal1);
+        }
+
+        for (int i = 0; i < numParams2; i++) {
+            double aVal2 = 0.0, bVal2 = 0.0;
+            for (int j = 0; j < numParams2; j++) {
+                aVal2 += C->data.F64[i][j] * a->data.F64[j];
+                bVal2 += B->data.F64[i][j] * b->data.F64[j];
+            }
+            bVal2 += e->data.F64[i];
+            for (int j = numParams2; j < numParams; j++) {
+                aVal2 += C->data.F64[i][j] * a->data.F64[j];
+            }
+            printf("%d: %lf\n", i, aVal2 - bVal2);
+        }
+#endif
+
+#ifdef TESTING
+        {
+            psFits *fits = psFitsOpen("sumMatrix1.fits", "w");
+            psFitsWriteImage(fits, NULL, sumMatrix1, 0, NULL);
+            psFitsClose(fits);
+        }
+        {
+            psFits *fits = psFitsOpen("sumMatrix2.fits", "w");
+            psFitsWriteImage(fits, NULL, sumMatrix2, 0, NULL);
+            psFitsClose(fits);
+        }
+        {
+            psFits *fits = psFitsOpen("sumMatrixX.fits", "w");
+            psFitsWriteImage(fits, NULL, sumMatrixX, 0, NULL);
+            psFitsClose(fits);
+        }
+        {
+            psFits *fits = psFitsOpen("sumFinverse.fits", "w");
+            psFitsWriteImage(fits, NULL, Fi, 0, NULL);
+            psFitsClose(fits);
+        }
+#endif
+
+        kernels->solution1 = a;
+        kernels->solution2 = b;
+
+        // XXXXX Free temporary matrices and vectors
+
+    }
+
+    if (psTraceGetLevel("psModules.imcombine") >= 7) {
+        for (int i = 0; i < kernels->solution1->n; i++) {
+            psTrace("psModules.imcombine", 7, "Solution 1 %d: %f\n", i, kernels->solution1->data.F64[i]);
+        }
+        if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
+            for (int i = 0; i < kernels->solution2->n; i++) {
+                psTrace("psModules.imcombine", 7, "Solution 2 %d: %f\n", i, kernels->solution2->data.F64[i]);
+            }
+        }
+     }
+
+    return true;
+}
+
+psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps,
+                                           const pmSubtractionKernels *kernels)
+{
+    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, NULL);
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NULL);
+    PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NULL);
+
+    psVector *deviations = psVectorAlloc(stamps->num, PS_TYPE_F32); // Mean deviation for stamps
+    int footprint = stamps->footprint; // Half-size of stamps
+    long numPixels = PS_SQR(2 * footprint + 1); // Number of pixels in footprint
+    double devNorm = 1.0 / (double)numPixels; // Normalisation for deviations
+    int numKernels = kernels->num;      // Number of kernels
+
+    psImage *polyValues = NULL;         // Polynomial values
+    psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
+
+    for (int i = 0; i < stamps->num; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // The stamp of interest
+        if (stamp->status != PM_SUBTRACTION_STAMP_USED) {
+            deviations->data.F32[i] = NAN;
+            continue;
+        }
+
+        // Calculate coefficients of the kernel basis functions
+        polyValues = p_pmSubtractionPolynomial(polyValues, kernels->spatialOrder, stamp->xNorm, stamp->yNorm);
+        double norm = p_pmSubtractionSolutionNorm(kernels); // Normalisation
+        double background = p_pmSubtractionSolutionBackground(kernels, polyValues);// Difference in background
+
+        // Calculate residuals
+        psKernel *weight = stamp->weight; // Weight postage stamp
+        psImageInit(residual->image, 0.0);
+        if (kernels->mode != PM_SUBTRACTION_MODE_DUAL) {
+            psKernel *target;           // Target postage stamp
+            psKernel *source;           // Source postage stamp
+            psArray *convolutions;      // Convolution postage stamps for each kernel basis function
+            switch (kernels->mode) {
+              case PM_SUBTRACTION_MODE_1:
+                target = stamp->image2;
+                source = stamp->image1;
+                convolutions = stamp->convolutions1;
+                break;
+              case PM_SUBTRACTION_MODE_2:
+                target = stamp->image1;
+                source = stamp->image2;
+                convolutions = stamp->convolutions2;
+                break;
+              default:
+                psAbort("Unsupported subtraction mode: %x", kernels->mode);
+            }
+
+            for (int j = 0; j < numKernels; j++) {
+                psKernel *convolution = convolutions->data[j]; // Convolution
+                double coefficient = p_pmSubtractionSolutionCoeff(kernels, polyValues, j,
+                                                                  false); // Coefficient
+                for (int y = - footprint; y <= footprint; y++) {
+                    for (int x = - footprint; x <= footprint; x++) {
+                        residual->kernel[y][x] -= convolution->kernel[y][x] * coefficient;
+                    }
+                }
+            }
+            for (int y = - footprint; y <= footprint; y++) {
+                for (int x = - footprint; x <= footprint; x++) {
+                    residual->kernel[y][x] += target->kernel[y][x] - background - source->kernel[y][x] * norm;
+                }
+            }
+        } else {
+            // Dual convolution
+            psArray *convolutions1 = stamp->convolutions1; // Convolutions of the first image
+            psArray *convolutions2 = stamp->convolutions2; // Convolutions of the second image
+            psKernel *image1 = stamp->image1; // The first image
+            psKernel *image2 = stamp->image2; // The second image
+
+            for (int j = 0; j < numKernels; j++) {
+                psKernel *conv1 = convolutions1->data[j]; // Convolution of first image
+                psKernel *conv2 = convolutions2->data[j]; // Convolution of second image
+                double coeff1 = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, false); // Coefficient 1
+                double coeff2 = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, true); // Coefficient 2
+
+                for (int y = - footprint; y <= footprint; y++) {
+                    for (int x = - footprint; x <= footprint; x++) {
+                        residual->kernel[y][x] += conv2->kernel[y][x] * coeff2 - conv1->kernel[y][x] * coeff1;
+                    }
+                }
+            }
+            for (int y = - footprint; y <= footprint; y++) {
+                for (int x = - footprint; x <= footprint; x++) {
+                    residual->kernel[y][x] += image2->kernel[y][x] - background - image1->kernel[y][x] * norm;
+                }
+            }
+        }
+
+        double deviation = 0.0;         // Sum of differences
+        for (int y = - footprint; y <= footprint; y++) {
+            for (int x = - footprint; x <= footprint; x++) {
+                double dev = PS_SQR(residual->kernel[y][x]) / weight->kernel[y][x];
+                deviation += dev;
+#ifdef TESTING
+                residual->kernel[y][x] = dev;
+#endif
+            }
+        }
+        deviations->data.F32[i] = devNorm * deviation;
+        psTrace("psModules.imcombine", 5, "Deviation for stamp %d (%d,%d): %f\n",
+                i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5), deviations->data.F32[i]);
+        if (!isfinite(deviations->data.F32[i])) {
+            stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
+            psTrace("psModules.imcombine", 5,
+                    "Rejecting stamp %d (%d,%d) because of non-finite deviation\n",
+                    i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
+            continue;
+        }
+
+#ifdef TESTING
+        {
+            psString filename = NULL;
+            psStringAppend(&filename, "resid_%03d.fits", i);
+            psFits *fits = psFitsOpen(filename, "w");
+            psFree(filename);
+            psFitsWriteImage(fits, NULL, residual->image, 0, NULL);
+            psFitsClose(fits);
+        }
+        if (stamp->image1) {
+            psString filename = NULL;
+            psStringAppend(&filename, "stamp_image1_%03d.fits", i);
+            psFits *fits = psFitsOpen(filename, "w");
+            psFree(filename);
+            psFitsWriteImage(fits, NULL, stamp->image1->image, 0, NULL);
+            psFitsClose(fits);
+        }
+        if (stamp->image2) {
+            psString filename = NULL;
+            psStringAppend(&filename, "stamp_image2_%03d.fits", i);
+            psFits *fits = psFitsOpen(filename, "w");
+            psFree(filename);
+            psFitsWriteImage(fits, NULL, stamp->image2->image, 0, NULL);
+            psFitsClose(fits);
+        }
+        if (stamp->weight) {
+            psString filename = NULL;
+            psStringAppend(&filename, "stamp_weight_%03d.fits", i);
+            psFits *fits = psFitsOpen(filename, "w");
+            psFree(filename);
+            psFitsWriteImage(fits, NULL, stamp->weight->image, 0, NULL);
+            psFitsClose(fits);
+        }
+#endif
+
+    }
+    psFree(residual);
+    psFree(polyValues);
+
+    return deviations;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionEquation.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionEquation.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionEquation.h	(revision 22322)
@@ -0,0 +1,56 @@
+#ifndef PM_SUBTRACTION_EQUATION_H
+#define PM_SUBTRACTION_EQUATION_H
+
+#include "pmSubtractionStamps.h"
+#include "pmSubtractionKernels.h"
+
+/// Execute a thread job to calculate the least-squares equation for a stamp
+bool pmSubtractionCalculateEquationThread(psThreadJob *job ///< Job to execute
+    );
+
+/// Calculate the least-squares equation to match the image quality for a single stamp
+bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, ///< Stamps
+                                         const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                         int index ///< Index of stamp
+                                    );
+
+/// Calculate the least-squares equation to match the image quality
+bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, ///< Stamps
+                                    const pmSubtractionKernels *kernels ///< Kernel parameters
+                                    );
+
+/// Solve the least-squares equation to match the image quality
+bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels, ///< Kernel parameters
+                                const pmSubtractionStampList *stamps ///< Stamps
+    );
+
+/// Calculate deviations
+psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps, ///< Stamps
+                                           const pmSubtractionKernels *kernels ///< Kernel parameters
+    );
+
+/// Calculate the value of a polynomial, specified by coefficients and polynomial values
+double p_pmSubtractionCalculatePolynomial(const psVector *coeff, ///< Coefficients
+                                          const psImage *polyValues, ///< Polynomial values
+                                          int order, ///< Order of polynomials
+                                          int index, ///< Index at which to begin
+                                          int step ///< Step between subsequent indices
+    );
+
+/// Return the specified coefficient in the solution
+double p_pmSubtractionSolutionCoeff(const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                    const psImage *polyValues, ///< Polynomial values
+                                    int index, ///< Coefficient index to calculate
+                                    bool wantDual ///< Calculate the coefficient for the dual solution?
+    );
+
+/// Return the normalisation in the solution
+double p_pmSubtractionSolutionNorm(const pmSubtractionKernels *kernels ///< Kernel parameters
+    );
+
+/// Return the background (difference) in the solution
+double p_pmSubtractionSolutionBackground(const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                         const psImage *polyValues ///< Polynomial values
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionIO.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionIO.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionIO.c	(revision 22322)
@@ -0,0 +1,581 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <string.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmFPAfileFitsIO.h"
+#include "pmConcepts.h"
+
+#include "pmSubtraction.h"
+#include "pmSubtractionKernels.h"
+#include "pmSubtractionMatch.h"
+
+#define ARRAY_BUFFER 16                 // Number to add to array at a time
+
+// Names of FITS table columns
+#define NAME_XMIN "XMIN"                // Region of applicability: minimum x value
+#define NAME_XMAX "XMAX"                // Region of applicability: maximum x value
+#define NAME_YMIN "YMIN"                // Region of applicability: minimum y value
+#define NAME_YMAX "YMAX"                // Region of applicability: maximum y value
+#define NAME_KERNEL "KERNEL"            // Kernel description
+#define NAME_TYPE "TYPE"                // Kernel type
+#define NAME_SIZE "SIZE"                // Kernel half-size
+#define NAME_INNER "INNER"              // Size of inner region (only applicable for some kernel types)
+#define NAME_SPATIAL "SPATIAL_ORDER"    // Order of spatial polynomial
+#define NAME_BG "BG_ORDER"              // Order of background polynomial
+#define NAME_MODE "MODE"                // Matching mode
+#define NAME_COLS "COLUMNS"             // Number of columns
+#define NAME_ROWS "ROWS"                // Number of rows
+#define NAME_SOL1 "SOLUTION_1"          // Solution for convolving image 1
+#define NAME_SOL2 "SOLUTION_2"          // Solution for convolving image 2
+#define NAME_MEAN "MEAN"                // Mean of chi^2 from stamps
+#define NAME_RMS  "RMS"                 // RMS of chi^2 from stamps
+#define NAME_NUMSTAMPS "NUMSTAMPS"      // Number of good stamps
+
+#define EXTNAME "SUBTRACTION_KERNEL"    // Extension name
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmReadoutWriteSubtractionKernels(pmReadout *ro, psFits *fits)
+{
+    PM_ASSERT_READOUT_NON_NULL(ro, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    // Extract the regions and solutions used in the image matching
+    psArray *regions = psArrayAllocEmpty(ARRAY_BUFFER); // Array of regions
+    {
+        psString regex = NULL;          // Regular expression
+        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_REGION);
+        psMetadataIterator *iter = psMetadataIteratorAlloc(ro->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);
+    }
+    if (regions->n == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "No subtraction regions found.");
+        psFree(regions);
+        return false;
+    }
+
+    psArray *kernels = psArrayAllocEmpty(ARRAY_BUFFER); // Array of kernels
+    {
+        psString regex = NULL;          // Regular expression
+        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
+        psMetadataIterator *iter = psMetadataIteratorAlloc(ro->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 = ro->image->numCols;
+            kernel->numRows = ro->image->numRows;
+
+            kernels = psArrayAdd(kernels, ARRAY_BUFFER, kernel);
+        }
+        psFree(iter);
+    }
+
+    if (regions->n != kernels->n) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Number of regions (%ld) and kernels (%ld) don't match.\n",
+                regions->n, kernels->n);
+        psFree(regions);
+        psFree(kernels);
+        return false;
+    }
+
+    // Format for writing a table
+    int num = regions->n;              // Number of regions and kernels
+    psArray *rows = psArrayAlloc(num); // Array of FITS table rows
+    for (int i = 0; i < num; i++) {
+        psMetadata *row = psMetadataAlloc(); // Row of interest
+        rows->data[i] = psMemIncrRefCounter(row);
+
+        psRegion *region = regions->data[i]; // Region of interest
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_XMIN,  0, "Applicability minimum x", region->x0);
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_XMAX,  0, "Applicability maximum x", region->x1);
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_YMIN,  0, "Applicability minimum y", region->y0);
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_YMAX,  0, "Applicability maximum y", region->y1);
+
+        pmSubtractionKernels *kernel = kernels->data[i]; // Kernel
+        psMetadataAddStr(row, PS_LIST_TAIL, NAME_KERNEL, 0, "Kernel description", kernel->description);
+
+#if 0
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_TYPE,  0, "Kernel type (enum)", kernel->type);
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_SIZE,  0, "Kernel half-size", kernel->size);
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_INNER, 0, "Size of inner region", kernel->inner);
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_SPATIAL, 0, "Polynomial order for spatial variations",
+                         kernel->spatialOrder);
+#endif
+
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_BG,  0, "Polynomial order for background fitting",
+                         kernel->bgOrder);
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_MODE,  0, "Matching mode (enum)", kernel->mode);
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_COLS,  0, "Number of columns", kernel->numCols);
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_ROWS,  0, "Number of rows", kernel->numRows);
+        if (kernel->mode == PM_SUBTRACTION_MODE_1 || kernel->mode == PM_SUBTRACTION_MODE_DUAL) {
+            psMetadataAddVector(row, PS_LIST_TAIL, NAME_SOL1, 0, "Solution vector 1", kernel->solution1);
+        }
+        if (kernel->mode == PM_SUBTRACTION_MODE_2 || kernel->mode == PM_SUBTRACTION_MODE_DUAL) {
+            psMetadataAddVector(row, PS_LIST_TAIL, NAME_SOL2, 0, "Solution vector 2", kernel->solution2);
+        }
+        psMetadataAddF32(row, PS_LIST_TAIL, NAME_MEAN,  0, "Mean of chi^2 from stamps", kernel->mean);
+        psMetadataAddF32(row, PS_LIST_TAIL, NAME_RMS,  0, "RMS of chi^2 from stamps", kernel->rms);
+        psMetadataAddS32(row, PS_LIST_TAIL, NAME_NUMSTAMPS,  0, "Number of good stamps", kernel->numStamps);
+    }
+    psFree(regions);
+    psFree(kernels);
+
+    psMetadata *header = psMetadataAlloc(); // Header for FITS file
+
+    // CVS tags, used to identify the version of this file (in case incompatibilities are introduced)
+    psString cvsFile = psStringCopy("$RCSfile: pmSubtractionIO.c,v $");
+    psString cvsRev  = psStringCopy("$Revision: 1.6 $");
+    psString cvsDate = psStringCopy("$Date: 2008-09-25 22:29:12 $");
+    psStringSubstitute(&cvsFile, NULL, "RCSfile: ");
+    psStringSubstitute(&cvsRev,  NULL, "Revision: ");
+    psStringSubstitute(&cvsDate, NULL, "Date: ");
+
+    psString version = NULL;            // Version information, for header
+    psStringAppend(&version, "%s %s %s", cvsFile, cvsRev, cvsDate);
+    psFree(cvsFile);
+    psFree(cvsRev);
+    psFree(cvsDate);
+    psStringSubstitute(&version, NULL, "$");
+    psMetadataAddStr(header, PS_LIST_TAIL, "PSVERSION", 0, "S/W version", version);
+    psFree(version);
+
+    if (!psFitsWriteTable(fits, header, rows, EXTNAME)) {
+        psError(PS_ERR_IO, false, "Unable to write subtraction kernel to FITS table.");
+        psFree(header);
+        psFree(rows);
+        return false;
+    }
+
+    psFree(header);
+    psFree(rows);
+
+    return true;
+}
+
+static bool pmCellWriteSubtractionKernels(pmCell *cell, const pmFPAview *view,
+                                          pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(cell->readouts, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc(view->nRows); // Copy of input view
+    *thisView = *view;
+
+    for (int i = 0; i < cell->readouts->n; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        thisView->readout = i;
+        if (!pmReadoutWriteSubtractionKernels(readout, file->fits)) {
+            psError(PS_ERR_IO, false, "Failed to write %dth readout", i);
+            psFree(thisView);
+            return false;
+        }
+    }
+    psFree(thisView);
+    return true;
+}
+
+static bool pmChipWriteSubtractionKernels(pmChip *chip, const pmFPAview *view,
+                                          pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(chip->cells, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc(view->nRows); // Copy of input view
+    *thisView = *view;
+
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i];
+        thisView->cell = i;
+        if (!pmCellWriteSubtractionKernels(cell, thisView, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write %dth cell", i);
+            psFree(thisView);
+            return false;
+        }
+    }
+    psFree(thisView);
+    return true;
+}
+
+static bool pmFPAWriteSubtractionKernels(pmFPA *fpa, const pmFPAview *view,
+                                         pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fpa->chips, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc(view->nRows); // Copy of input view
+    *thisView = *view;
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        thisView->chip = i;
+        if (!pmChipWriteSubtractionKernels(chip, thisView, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write %dth chip", i);
+            psFree(thisView);
+            return false;
+        }
+    }
+    psFree(thisView);
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+bool pmReadoutReadSubtractionKernels(pmReadout *ro, psFits *fits)
+{
+    PM_ASSERT_READOUT_NON_NULL(ro, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    if (!psFitsMoveExtName(fits, EXTNAME)) {
+        psError(PS_ERR_IO, false, "Unable to move to subtraction kernel table.");
+        return false;
+    }
+
+    psArray *table = psFitsReadTable(fits); // Table of interest
+    if (!table) {
+        psError(PS_ERR_IO, false, "Unable to read FITS table");
+        return false;
+    }
+
+    // Look up a column value for a row
+#define TABLE_LOOKUP(TYPE, SUFFIX, TARGET, NAME) \
+    TYPE TARGET; \
+    { \
+        bool mdok; \
+        TARGET = psMetadataLookup##SUFFIX(&mdok, row, NAME); \
+        if (!mdok) { \
+            psError(PS_ERR_UNKNOWN, false, "Unable to find column %s in subtraction kernel table.", NAME); \
+            psFree(table); \
+            return false; \
+        } \
+    }
+
+    for (int i = 0; i < table->n; i++) {
+        psMetadata *row = table->data[i]; // Table row
+
+        TABLE_LOOKUP(int, S32, xMin, NAME_XMIN);
+        TABLE_LOOKUP(int, S32, xMax, NAME_XMAX);
+        TABLE_LOOKUP(int, S32, yMin, NAME_YMIN);
+        TABLE_LOOKUP(int, S32, yMax, NAME_YMAX);
+
+        psRegion *region = psRegionAlloc(xMin, xMax, yMin, yMax); // Region of applicability
+        psMetadataAddPtr(ro->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_REGION, PS_DATA_REGION,
+                         "Subtraction region", region);
+        psFree(region);
+
+        TABLE_LOOKUP(const char *, Str, description, NAME_KERNEL);
+        TABLE_LOOKUP(pmSubtractionMode, S32, mode,    NAME_MODE);
+
+#if 0
+        TABLE_LOOKUP(int, S32, size,    NAME_SIZE);
+        TABLE_LOOKUP(int, S32, inner,   NAME_INNER);
+        TABLE_LOOKUP(int, S32, spatial, NAME_SPATIAL);
+#endif
+
+        TABLE_LOOKUP(int, S32, bg,      NAME_BG);
+        TABLE_LOOKUP(int, S32, numCols, NAME_COLS);
+        TABLE_LOOKUP(int, S32, numRows, NAME_ROWS);
+
+        TABLE_LOOKUP(float, F32, mean,      NAME_MEAN);
+        TABLE_LOOKUP(float, F32, rms,       NAME_RMS);
+        TABLE_LOOKUP(int,   S32, numStamps, NAME_NUMSTAMPS);
+
+        pmSubtractionKernels *kernels = pmSubtractionKernelsFromDescription(description, bg, mode);
+        kernels->numCols = numCols;
+        kernels->numRows = numRows;
+        kernels->mean = mean;
+        kernels->rms = rms;
+        kernels->numStamps = numStamps;
+
+        bool mdok;                      // Status of MD lookup
+        if (mode == PM_SUBTRACTION_MODE_1 || mode == PM_SUBTRACTION_MODE_DUAL) {
+            kernels->solution1 = psMemIncrRefCounter(psMetadataLookupPtr(&mdok, row, NAME_SOL1));
+            if (!mdok) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to find column %s in subtraction kernel table.",
+                        NAME_SOL1);
+                psFree(kernels);
+                psFree(table);
+                return false;
+            }
+        }
+        if (mode == PM_SUBTRACTION_MODE_2 || mode == PM_SUBTRACTION_MODE_DUAL) {
+            kernels->solution2 = psMemIncrRefCounter(psMetadataLookupPtr(&mdok, row, NAME_SOL2));
+            if (!mdok) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to find column %s in subtraction kernel table.",
+                        NAME_SOL2);
+                psFree(kernels);
+                psFree(table);
+                return false;
+            }
+        }
+
+        psMetadataAddPtr(ro->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_KERNEL, PS_DATA_UNKNOWN,
+                         "Subtraction kernels", kernels);
+        psFree(kernels);
+    }
+    psFree(table);
+    return true;
+}
+
+static bool pmCellReadSubtractionKernels(pmCell *cell, const pmFPAview *view,
+                                         pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(cell->readouts, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc(view->nRows); // Copy of input view
+    *thisView = *view;
+
+    // Create a readout if none exists
+    if (!cell->readouts || cell->readouts->n == 0) {
+        pmReadout *readout = pmReadoutAlloc(cell); // New readout
+        psFree(readout);                // Drop reference
+    }
+
+    cell->data_exists = false;
+    for (int i = 0; i < cell->readouts->n; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        thisView->readout = i;
+        pmReadoutReadSubtractionKernels(readout, file->fits);
+        if (!readout->data_exists) {
+            continue;
+        }
+
+        // load in the concept information for this cell
+        if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
+            psErrorClear();
+            psWarning("Difficulty reading concepts for cell; attempting to proceed.");
+        }
+        cell->data_exists = true;
+    }
+    psFree(thisView);
+
+    return true;
+}
+
+static bool pmChipReadSubtractionKernels(pmChip *chip, const pmFPAview *view,
+                                         pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(chip->cells, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc(view->nRows); // Copy of input view
+    *thisView = *view;
+
+    chip->data_exists = false;
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i];
+        thisView->cell = i;
+        pmCellReadSubtractionKernels(cell, thisView, file, config);
+        if (!cell->data_exists) {
+            continue;
+        }
+        chip->data_exists = true;
+    }
+    psFree(thisView);
+
+    if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, true, true, NULL)) {
+        psError(PS_ERR_IO, false, "Failed to read concepts for chip.\n");
+        return false;
+    }
+
+    return true;
+}
+
+static bool pmFPAReadSubtractionKernels(pmFPA *fpa, const pmFPAview *view,
+                                        pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa->chips, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc(view->nRows); // Copy of input view
+    *thisView = *view;
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        thisView->chip = i;
+        pmChipReadSubtractionKernels(chip, thisView, file, config);
+    }
+    psFree(thisView);
+
+    if (!pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
+        psError(PS_ERR_IO, false, "Failed to read concepts for fpa.\n");
+        return false;
+    }
+
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmSubtractionWriteKernels(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
+
+    if (view->chip == -1) {
+        if (!pmFPAWriteSubtractionKernels(fpa, view, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write subtraction kernels from fpa");
+            psFree(fpa);
+            return false;
+        }
+        psFree(fpa);
+        return true;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        psError(PS_ERR_UNKNOWN, false, "Writing chip == %d (>= chips->n == %ld)", view->chip, fpa->chips->n);
+        psFree(fpa);
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        if (!pmChipWriteSubtractionKernels(chip, view, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write objects from chip");
+            psFree(fpa);
+            return false;
+        }
+        psFree(fpa);
+        return true;
+    }
+
+    if (view->cell >= chip->cells->n) {
+        psError(PS_ERR_UNKNOWN, false, "Writing cell == %d (>= cells->n == %ld)",
+                view->cell, chip->cells->n);
+        psFree(fpa);
+        return false;
+    }
+    pmCell *cell = chip->cells->data[view->cell];
+
+    if (view->readout == -1) {
+        if (!pmCellWriteSubtractionKernels(cell, view, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write objects from cell");
+            psFree(fpa);
+            return false;
+        }
+        psFree(fpa);
+        return true;
+    }
+
+    if (view->readout >= cell->readouts->n) {
+        psError(PS_ERR_UNKNOWN, false, "Writing readout == %d (>= readouts->n == %ld)",
+                view->readout, cell->readouts->n);
+        psFree(fpa);
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[view->readout];
+
+    if (!pmReadoutWriteSubtractionKernels(readout, file->fits)) {
+        psError(PS_ERR_IO, false, "Failed to write objects from readout %d", view->readout);
+        psFree(fpa);
+        return false;
+    }
+
+    psFree(fpa);
+    return true;
+}
+
+bool pmSubtractionWritePHU(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    if (file->wrote_phu) {
+        return true;
+    }
+    if (file->fileLevel != PM_FPA_LEVEL_FPA) {
+        return true;
+    }
+    if (file->fpa->chips->n == 1) {
+        return true;
+    }
+
+    // find the FPA phu
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
+    pmHDU *phu = psMemIncrRefCounter(pmFPAviewThisPHU(view, fpa));
+    psFree(fpa);
+
+    // if there is no PHU, this is a single header+image (extension-less) file. This could be the case for an
+    // input SPLIT set of files being written out as a MEF.  if there is a PHU, write it out as a 'blank'
+    psMetadata *outhead = psMetadataAlloc();
+    if (phu) {
+        psMetadataCopy (outhead, phu->header);
+    }
+    psFree(phu);
+
+    pmConfigConformHeader(outhead, file->format);
+
+    psFitsWriteBlank(file->fits, outhead, "");
+    file->wrote_phu = true;
+
+    psTrace("pmFPAfile", 5, "wrote phu %s (type: %d)\n", file->filename, file->type);
+    psFree(outhead);
+
+    return true;
+}
+
+bool pmSubtractionReadKernels(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        pmFPAReadSubtractionKernels(fpa, view, file, config);
+        return true;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        pmChipReadSubtractionKernels(chip, view, file, config);
+        return true;
+    }
+
+    if (view->cell >= chip->cells->n) {
+        return false;
+    }
+    pmCell *cell = chip->cells->data[view->cell];
+
+    if (view->readout == -1) {
+        pmCellReadSubtractionKernels(cell, view, file, config);
+        return true;
+    }
+
+    if (view->readout >= cell->readouts->n) {
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[view->readout];
+
+    return pmReadoutReadSubtractionKernels(readout, file->fits);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionIO.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionIO.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionIO.h	(revision 22322)
@@ -0,0 +1,37 @@
+#ifndef PM_SUBTRACTION_IO_H
+#define PM_SUBTRACTION_IO_H
+
+#include <pslib.h>
+
+#include <pmHDU.h>
+#include <pmFPA.h>
+
+/// Write subtraction kernels within a readout to a FITS file
+bool pmReadoutWriteSubtractionKernels(
+    pmReadout *readout,                 ///< Readout for which to write subtraction kernels (in analysis MD)
+    psFits *fits                        ///< FITS file to which to write
+    );
+
+
+bool pmReadoutReadSubtractionKernels(
+    pmReadout *readout,                 ///< Readout for which to read subtraction kernels (into analysis MD)
+    psFits *fits                        ///< FITS file to which to write
+    );
+
+
+bool pmSubtractionReadKernels(const pmFPAview *view, ///< View into which to read
+                              pmFPAfile *file, ///< File from which to read
+                              pmConfig *config ///< Configuration
+                              );
+
+bool pmSubtractionWriteKernels(const pmFPAview *view, ///< View from which to write
+                               pmFPAfile *file, ///< File to which to write
+                               pmConfig *config ///< Configuration
+                               );
+
+bool pmSubtractionWritePHU(const pmFPAview *view, // View to PHU
+                           pmFPAfile *file, ///< File to which to write
+                           pmConfig *config ///< Configuration
+                           );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionKernels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionKernels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionKernels.c	(revision 22322)
@@ -0,0 +1,787 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+
+#include "pmSubtraction.h"
+#include "pmSubtractionKernels.h"
+
+#define RINGS_BUFFER 10                 // Buffer size for RINGS data
+
+
+// Free function for pmSubtractionKernels
+static void subtractionKernelsFree(pmSubtractionKernels *kernels)
+{
+    psFree(kernels->description);
+    psFree(kernels->u);
+    psFree(kernels->v);
+    psFree(kernels->widths);
+    psFree(kernels->uStop);
+    psFree(kernels->vStop);
+    psFree(kernels->preCalc);
+    psFree(kernels->penalties);
+    psFree(kernels->solution1);
+    psFree(kernels->solution2);
+}
+
+// Raise an integer to an integer power
+static inline long power(int value,     // Value
+                         int exp        // Exponent
+    )
+{
+    if (exp == 0) {
+        return 1.0;
+    }
+    long result = value;               // Result to return
+    for (int i = 2; i <= exp; i++) {
+        result *= value;
+    }
+    return result;
+}
+
+// Generate 1D convolution kernel for ISIS
+static psVector *subtractionKernelISIS(float sigma, // Gaussian width
+                                       int order, // Polynomial order
+                                       int size // Kernel half-size
+    )
+{
+    int fullSize = 2 * size + 1;        // Full size of kernel
+    psVector *kernel = psVectorAlloc(fullSize, PS_TYPE_F32); // Kernel to return
+
+    float expNorm = -0.5 / PS_SQR(sigma); // Normalisation for exponential
+    float norm = 1.0 / (M_2_PI * sqrtf(sigma)); // Normalisation for Gaussian
+    for (int i = 0, x = -size; x <= size; i++, x++) {
+        kernel->data.F32[i] = norm * power(x, order) * expf(expNorm * PS_SQR(x));
+    }
+
+    return kernel;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Semi-public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool p_pmSubtractionKernelsAddGrid(pmSubtractionKernels *kernels, int start, int size)
+{
+    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
+    PS_ASSERT_INT_NONNEGATIVE(start, false);
+    PS_ASSERT_INT_NONNEGATIVE(size, false);
+
+    int numNew = PS_SQR(2 * size + 1) - 1;  // Number of new kernel parameters to add
+
+    // Ensure the sizes match
+    kernels->widths = psVectorRealloc(kernels->widths, start + numNew);
+    kernels->u = psVectorRealloc(kernels->u, start + numNew);
+    kernels->v = psVectorRealloc(kernels->v, start + numNew);
+    kernels->preCalc = psArrayRealloc(kernels->preCalc, start + numNew);
+    kernels->penalties = psVectorRealloc(kernels->penalties, start + numNew);
+    kernels->inner = start;
+
+    // Generate a set of kernels for each (u,v)
+    for (int v = - size, index = start; v <= size; v++) {
+        for (int u = - size; u <= size; u++, index++) {
+            if (v == 0 && u == 0) {
+                // Skip normalisation component: added explicitly
+                index--;
+                continue;
+            }
+            kernels->widths->data.F32[index] = NAN;
+            kernels->u->data.S32[index] = u;
+            kernels->v->data.S32[index] = v;
+            kernels->preCalc->data[index] = NULL;
+            kernels->penalties->data.F32[index] = kernels->penalty * (PS_SQR(u) + PS_SQR(v));
+
+            psTrace("psModules.imcombine", 7, "Kernel %d: %d %d\n", index, u, v);
+        }
+    }
+
+    return true;
+}
+
+pmSubtractionKernels *p_pmSubtractionKernelsRawISIS(int size, int spatialOrder,
+                                                    const psVector *fwhms, const psVector *orders,
+                                                    float penalty, pmSubtractionMode mode)
+{
+    PS_ASSERT_VECTOR_NON_NULL(fwhms, NULL);
+    PS_ASSERT_VECTOR_TYPE(fwhms, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(orders, NULL);
+    PS_ASSERT_VECTOR_TYPE(orders, PS_TYPE_S32, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(fwhms, orders, NULL);
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
+
+    int numGaussians = fwhms->n;       // Number of Gaussians
+
+    int num = 0;                        // Number of basis functions
+    psString params = NULL;             // List of parameters
+    for (int i = 0; i < numGaussians; i++) {
+        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
+        psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
+        num += (gaussOrder + 1) * (gaussOrder + 2) / 2;
+    }
+
+    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_ISIS, size,
+                                                              spatialOrder, penalty, mode); // The kernels
+    psStringAppend(&kernels->description, "ISIS(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
+
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "ISIS kernel: %s,%d --> %d elements",
+             params, spatialOrder, num);
+    psFree(params);
+
+    // Set the kernel parameters
+    int fullSize = 2 * size + 1;        // Full size of kernels
+    for (int i = 0, index = 0; i < numGaussians; i++) {
+        float sigma = fwhms->data.F32[i] / (2.0 * sqrtf(2.0 * logf(2.0))); // Gaussian sigma
+        // Iterate over (u,v) order
+        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
+            for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
+                psArray *preCalc = psArrayAlloc(2); // Array to hold precalculated values
+                psVector *xKernel = preCalc->data[0] = subtractionKernelISIS(sigma, uOrder, size); // x Kernel
+                psVector *yKernel = preCalc->data[1] = subtractionKernelISIS(sigma, vOrder, size); // y Kernel
+
+                // Calculate moments
+                double sum = 0.0;       // Sum of kernel component, for normalisation
+                double moment = 0.0;    // Moment, for penalty
+                for (int v = -size, y = 0; v <= size; v++, y++) {
+                    for (int u = -size, x = 0; u <= size; u++, x++) {
+                        double value = xKernel->data.F32[x] * yKernel->data.F32[y]; // Value of kernel
+                        sum += value;
+                        moment += value * (PS_SQR(u) + PS_SQR(v));
+                    }
+                }
+
+                // Normalise sum of kernel component to unity for even functions
+                if (uOrder % 2 == 0 && vOrder % 2 == 0) {
+                    double sum = 0.0;   // Sum of kernel component
+                    for (int v = 0; v < fullSize; v++) {
+                        for (int u = 0; u < fullSize; u++) {
+                            sum += xKernel->data.F32[u] * yKernel->data.F32[v];
+                        }
+                    }
+                    sum = 1.0 / sum;
+                    psBinaryOp(xKernel, xKernel, "*", psScalarAlloc(sum, PS_TYPE_F32));
+                    psBinaryOp(yKernel, yKernel, "*", psScalarAlloc(sum, PS_TYPE_F32));
+                    moment *= sum;
+                }
+
+                kernels->widths->data.F32[index] = fwhms->data.F32[i];
+                kernels->u->data.S32[index] = uOrder;
+                kernels->v->data.S32[index] = vOrder;
+                if (kernels->preCalc->data[index]) {
+                    psFree(kernels->preCalc->data[index]);
+                }
+                kernels->preCalc->data[index] = preCalc;
+                kernels->penalties->data.F32[index] = kernels->penalty * fabsf(moment);
+
+                psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d %f\n", index,
+                        fwhms->data.F32[i], uOrder, vOrder, fabsf(moment));
+            }
+        }
+    }
+
+    return kernels;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+pmSubtractionKernels *pmSubtractionKernelsAlloc(int numBasisFunctions, pmSubtractionKernelsType type,
+                                                int size, int spatialOrder, float penalty,
+                                                pmSubtractionMode mode)
+{
+    pmSubtractionKernels *kernels = psAlloc(sizeof(pmSubtractionKernels)); // Kernels, to return
+    psMemSetDeallocator(kernels, (psFreeFunc)subtractionKernelsFree);
+
+    kernels->type = type;
+    kernels->description = NULL;
+    kernels->num = numBasisFunctions;
+    kernels->u = psVectorAlloc(numBasisFunctions, PS_TYPE_S32);
+    kernels->v = psVectorAlloc(numBasisFunctions, PS_TYPE_S32);
+    kernels->widths = psVectorAlloc(numBasisFunctions, PS_TYPE_F32);
+    kernels->preCalc = psArrayAlloc(numBasisFunctions);
+    kernels->penalty = penalty;
+    kernels->penalties = psVectorAlloc(numBasisFunctions, PS_TYPE_F32);
+    kernels->uStop = NULL;
+    kernels->vStop = NULL;
+    kernels->size = size;
+    kernels->inner = 0;
+    kernels->spatialOrder = spatialOrder;
+    kernels->bgOrder = 0;
+    kernels->mode = mode;
+    kernels->numCols = 0;
+    kernels->numRows = 0;
+    kernels->solution1 = NULL;
+    kernels->solution2 = NULL;
+
+    return kernels;
+}
+
+pmSubtractionKernels *pmSubtractionKernelsPOIS(int size, int spatialOrder, float penalty,
+                                               pmSubtractionMode mode)
+{
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
+
+    int num = PS_SQR(2 * size + 1) - 1; // Number of basis functions
+
+    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_POIS, size,
+                                                              spatialOrder, penalty, mode); // The kernels
+    psStringAppend(&kernels->description, "POIS(%d,%d,%.2e)", size, spatialOrder, penalty);
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "POIS kernel: %d,%d --> %d elements",
+             size, spatialOrder, num);
+
+    if (!p_pmSubtractionKernelsAddGrid(kernels, 0, size)) {
+        psAbort("Should never get here.");
+    }
+
+    return kernels;
+}
+
+
+pmSubtractionKernels *pmSubtractionKernelsISIS(int size, int spatialOrder,
+                                               const psVector *fwhms, const psVector *orders,
+                                               float penalty, pmSubtractionMode mode)
+{
+    pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders,
+                                                                  penalty, mode); // Kernels
+    if (!kernels) {
+        return NULL;
+    }
+
+    if (psTraceGetLevel("psModules.imcombine.kernel") >= 10) {
+        for (int i = 0; i < kernels->num; i++) {
+            psKernel *kernel = kernels->preCalc->data[i]; // Kernel of interest
+            psString kernelName = NULL;
+            psStringAppend(&kernelName, "kernel%03d.fits", i);
+            psFits *kernelFile = psFitsOpen(kernelName, "w");
+            psFree(kernelName);
+            psFitsWriteImage(kernelFile, NULL, kernel->image, 0, NULL);
+            psFitsClose(kernelFile);
+            double sum = 0.0;
+            for (int y = 0; y < kernel->image->numRows; y++) {
+                for (int x = 0; x < kernel->image->numCols; x++) {
+                    sum += kernel->image->data.F32[y][x];
+                }
+            }
+            psTrace("psModules.imcombine.kernel", 10, "Kernel %d sum: %le\n", i, sum);
+        }
+    }
+
+    return kernels;
+}
+
+pmSubtractionKernels *pmSubtractionKernelsSPAM(int size, int spatialOrder, int inner, int binning,
+                                               float penalty, pmSubtractionMode mode)
+{
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(inner, NULL);
+    PS_ASSERT_INT_LARGER_THAN(size, inner, NULL);
+    PS_ASSERT_INT_POSITIVE(binning, NULL);
+
+    // The outer region should be divisible by the "binning"; otherwise allocate remainder to the inner region
+    int numOuter = (size - inner) / binning; // Number of summed pixels in the outer region
+    int numInner = inner + (size - inner) % binning; // Number of pixels in the inner region
+    assert(numOuter * binning + numInner == size);
+    int numTotal = numOuter + numInner; // Total number of summed pixels
+
+    psTrace("psModules.imcombine", 3, "Inner: %d Outer: %d\n", numInner, numOuter);
+
+    int num = PS_SQR(2 * numTotal + 1) - 1; // Number of basis functions
+
+    psTrace("psModules.imcombine", 3, "Number of basis functions: %d\n", num);
+
+    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_SPAM, size,
+                                                              spatialOrder, penalty, mode); // The kernels
+    kernels->inner = inner;
+    psStringAppend(&kernels->description, "SPAM(%d,%d,%d,%d,%.2e)", size, inner, binning, spatialOrder,
+                   penalty);
+
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "SPAM kernel: %d,%d,%d,%d --> %d elements",
+             size, inner, binning, spatialOrder, num);
+
+    kernels->uStop = psVectorAlloc(num, PS_TYPE_S32);
+    kernels->vStop = psVectorAlloc(num, PS_TYPE_S32);
+
+    psVector *locations = psVectorAlloc(2 * numTotal + 1, PS_TYPE_S32); // Locations for each kernel element
+    psVector *widths = psVectorAlloc(2 * numTotal + 1, PS_TYPE_S32); // Widths for each kernel element
+    locations->data.S32[numTotal] = 0;
+    widths->data.S32[numTotal] = 0;
+    for (int i = 1; i <= numInner; i++) {
+        locations->data.S32[numTotal + i] = i;
+        widths->data.S32[numTotal + i] = 0;
+        locations->data.S32[numTotal - i] = - i;
+        widths->data.S32[numTotal - i] = 0;
+    }
+    for (int i = numInner + 1; i <= numTotal; i++) {
+        locations->data.S32[numTotal + i] = locations->data.S32[numTotal + i - 1] +
+            widths->data.S32[numTotal + i - 1] + 1;
+        widths->data.S32[numTotal + i] = binning - 1;
+        locations->data.S32[numTotal - i] = locations->data.S32[numTotal - i + 1] - binning;
+        widths->data.S32[numTotal - i] = binning - 1;
+    }
+
+    if (psTraceGetLevel("psModules.imcombine") >= 10) {
+        for (int i = 0; i < 2 * numTotal + 1; i++) {
+            psTrace("psModules.imcombine", 10, "%d: %d -> %d\n", i, locations->data.S32[i],
+                    locations->data.S32[i] + widths->data.S32[i]);
+        }
+    }
+
+    // Set the kernel parameters
+    for (int i = - numTotal, index = 0; i <= numTotal; i++) {
+        int u = locations->data.S32[numTotal + i]; // Location of pixel
+        int uStop = u + widths->data.S32[numTotal + i]; // Width of pixel
+
+        for (int j = - numTotal; j <= numTotal; j++, index++) {
+            if (i == 0 && j == 0) {
+                // Skip normalisation component: added explicitly
+                index--;
+                continue;
+            }
+            int v = locations->data.S32[numTotal + j]; // Location of pixel
+            int vStop = v + widths->data.S32[numTotal + j]; // Width of pixel
+
+            kernels->u->data.S32[index] = u;
+            kernels->v->data.S32[index] = v;
+            kernels->uStop->data.S32[index] = uStop;
+            kernels->vStop->data.S32[index] = vStop;
+
+            psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d %d\n", index,
+                    u, uStop, v, vStop);
+        }
+    }
+
+    psFree(locations);
+    psFree(widths);
+
+    psWarning("Kernel penalty for dual-convolution is not configured for SPAM kernels.");
+    psVectorInit(kernels->penalties, 0.0);
+
+    return kernels;
+}
+
+
+pmSubtractionKernels *pmSubtractionKernelsFRIES(int size, int spatialOrder, int inner, float penalty,
+                                                pmSubtractionMode mode)
+{
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(inner, NULL);
+    PS_ASSERT_INT_LARGER_THAN(size, inner, NULL);
+
+    int fibNum = 0;                     // Number of Fibonacci values
+    int fibLast = 1, fibTotal = 2;      // Fibonacci sequence
+    while (fibTotal < size - inner) {
+        int temp = fibTotal;
+        fibTotal += fibLast;
+        fibLast = temp;
+        fibNum++;
+    }
+
+    int numInner = inner;               // Number of pixels in the inner region
+    int numOuter = fibNum;              // Number of summed pixels in the outer region
+    int numTotal = numOuter + numInner; // Total number of summed pixels
+
+    psTrace("psModules.imcombine", 3, "Inner: %d Outer: %d\n", numInner, numOuter);
+
+    int num = PS_SQR(2 * numTotal + 1) - 1; // Number of basis functions
+
+    psTrace("psModules.imcombine", 3, "Number of basis functions: %d\n", num);
+
+    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_FRIES, size,
+                                                              spatialOrder, penalty, mode); // The kernels
+    kernels->inner = inner;
+    psStringAppend(&kernels->description, "FRIES(%d,%d,%d,%.2e)", size, inner, spatialOrder, penalty);
+
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "FRIES kernel: %d,%d,%d --> %d elements",
+             size, inner, spatialOrder, num);
+
+    kernels->uStop = psVectorAlloc(num, PS_TYPE_S32);
+    kernels->vStop = psVectorAlloc(num, PS_TYPE_S32);
+
+    psVector *start = psVectorAlloc(2 * numTotal + 1, PS_TYPE_S32);
+    psVector *stop = psVectorAlloc(2 * numTotal + 1, PS_TYPE_S32);
+    start->data.S32[numTotal] = 0;
+    stop->data.S32[numTotal] = 0;
+    for (int i = 1; i <= numInner; i++) {
+        start->data.S32[numTotal + i] = i;
+        stop->data.S32[numTotal + i] = i;
+        start->data.S32[numTotal - i] = -i;
+        stop->data.S32[numTotal - i] = -i;
+    }
+    for (int i = numInner + 1, fibLast = 1, fib = 2, temp; i <= numTotal;
+         i++, fib = (temp = fib) + fibLast, fibLast = temp) {
+        start->data.S32[numTotal + i] = stop->data.S32[numTotal + i - 1] + 1;
+        stop->data.S32[numTotal + i] = PS_MIN(start->data.S32[numTotal + i] + fib - 1, size);
+        start->data.S32[numTotal - i] = - stop->data.S32[numTotal + i];
+        stop->data.S32[numTotal - i] = - start->data.S32[numTotal + i];
+    }
+
+    if (psTraceGetLevel("psModules.imcombine") >= 10) {
+        for (int i = 0; i < 2 * numTotal + 1; i++) {
+            psTrace("psModules.imcombine", 10, "%d: %d -> %d\n", i, start->data.S32[i], stop->data.S32[i]);
+        }
+    }
+
+    // Set the kernel parameters
+    for (int i = - numTotal, index = 0; i <= numTotal; i++) {
+        int u = start->data.S32[numTotal + i]; // Location of pixel
+        int uStop = stop->data.S32[numTotal + i]; // Width of pixel
+        for (int j = - numTotal; j <= numTotal; j++, index++) {
+            if (i == 0 && j == 0) {
+                // Skip normalisation component: added explicitly
+                index--;
+                continue;
+            }
+            int v = start->data.S32[numTotal + j]; // Location of pixel
+            int vStop = stop->data.S32[numTotal + j]; // Width of pixel
+
+            kernels->u->data.S32[index] = u;
+            kernels->v->data.S32[index] = v;
+            kernels->uStop->data.S32[index] = uStop;
+            kernels->vStop->data.S32[index] = vStop;
+
+            psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d %d\n", index,
+                    u, uStop, v, vStop);
+        }
+    }
+
+    psFree(start);
+    psFree(stop);
+
+    psWarning("Kernel penalty for dual-convolution is not configured for FRIES kernels.");
+    psVectorInit(kernels->penalties, 0.0);
+
+    return kernels;
+}
+
+// Grid United with Normal Kernel
+pmSubtractionKernels *pmSubtractionKernelsGUNK(int size, int spatialOrder, const psVector *fwhms,
+                                               const psVector *orders, int inner, float penalty,
+                                               pmSubtractionMode mode)
+{
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(fwhms, NULL);
+    PS_ASSERT_VECTOR_TYPE(fwhms, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(orders, NULL);
+    PS_ASSERT_VECTOR_TYPE(orders, PS_TYPE_S32, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(fwhms, orders, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(inner, NULL);
+    PS_ASSERT_INT_LESS_THAN(inner, size, NULL);
+
+    // XXX GUNK doesn't seem to work --- doesn't add the POIS components, or at least, they're not noticed
+
+    pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders,
+                                                                  penalty, mode); // Kernels
+    psStringPrepend(&kernels->description, "GUNK=");
+    psStringAppend(&kernels->description, "+POIS(%d,%d)", inner, spatialOrder);
+
+    int numISIS = kernels->num;         // Number of ISIS kernels
+
+    if (!p_pmSubtractionKernelsAddGrid(kernels, numISIS, inner)) {
+        psAbort("Should never get here.");
+    }
+
+    return kernels;
+}
+
+// RINGS --- just what it says
+pmSubtractionKernels *pmSubtractionKernelsRINGS(int size, int spatialOrder, int inner, int ringsOrder,
+                                                float penalty, pmSubtractionMode mode)
+{
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(inner, NULL);
+    PS_ASSERT_INT_LESS_THAN_OR_EQUAL(inner, size, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(ringsOrder, NULL);
+
+    int fibNum = 0;                     // Number of Fibonacci values
+    {
+        int fibIndex = 1, fibIndexMinus1 = 0; // Fibonnacci parameters
+        int radius = inner;
+        while (radius + fibIndex < size) {
+            radius++;
+            int fibNew = fibIndex + fibIndexMinus1;
+            fibIndexMinus1 = fibIndex;
+            fibIndex = fibNew;
+            radius += fibIndex;
+            fibNum++;
+        }
+    }
+
+    int numInner = inner - 1;           // Number of pixels in the inner region
+    int numOuter = fibNum;              // Number of summed pixels in the outer region
+
+    int numRings = numOuter + numInner; // Number of rings (not including the central pixel)
+    int numPoly = PM_SUBTRACTION_POLYTERMS(ringsOrder); // Number of polynomial variants of each ring
+
+    int num = numRings * numPoly; // Total number of basis functions
+
+    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_RINGS, size,
+                                                              spatialOrder, penalty, mode); // The kernels
+    kernels->inner = inner;
+    psStringAppend(&kernels->description, "RINGS(%d,%d,%d,%d,%.2e)", size, inner, ringsOrder, spatialOrder,
+                   penalty);
+
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "RINGS kernel: %d,%d,%d,%d --> %d elements",
+             size, inner, ringsOrder, spatialOrder, num);
+
+    // Set the Gaussian kernel parameters
+    int fibIndex = 1, fibIndexMinus1 = 0; // Fibonnacci parameters
+    int radiusLast = 1;                 // Last radius
+    for (int i = 1, index = 0; i < numRings + 1; i++) {
+        float lower2;                   // Lower limit of radius^2
+        float upper2;                   // Upper limit of radius^2
+        if (i <= inner) {
+            // A ring every pixel width
+            float radius = i;
+            lower2 = PS_SQR(radius - 0.5);
+            upper2 = PS_SQR(radius + 0.5);
+            radiusLast = i;
+        } else {
+            // Rings Fibonacci distributed (2, 3, 5...)
+            int fibNew = fibIndex + fibIndexMinus1;
+            fibIndexMinus1 = fibIndex;
+            fibIndex = fibNew;
+
+            float radiusLower = radiusLast + 1;
+            radiusLast = radiusLower + fibIndex;
+            float radiusUpper = radiusLast;
+
+            lower2 = PS_SQR(radiusLower - 0.5);
+            upper2 = PS_SQR(radiusUpper + 0.5);
+        }
+
+        psTrace("psModules.imcombine", 8, "Radius limits: %f --> %f\n", sqrtf(lower2), sqrtf(upper2));
+
+        // Iterate over (u,v) order
+        for (int uOrder = 0; uOrder <= (i == 0 ? 0 : ringsOrder); uOrder++) {
+            for (int vOrder = 0; vOrder <= (i == 0 ? 0 : ringsOrder - uOrder); vOrder++, index++) {
+
+                psArray *data = psArrayAlloc(3); // Container for data
+                psVector *uCoords = data->data[0] = psVectorAllocEmpty(RINGS_BUFFER, PS_TYPE_S32); // u coords
+                psVector *vCoords = data->data[1] = psVectorAllocEmpty(RINGS_BUFFER, PS_TYPE_S32); // v coords
+                psVector *poly = data->data[2] = psVectorAllocEmpty(RINGS_BUFFER, PS_TYPE_F32); // Polynomial
+                double moment = 0.0;    // Moment, for penalty
+
+                if (i == 0) {
+                    // Central pixel is easy
+                    uCoords->data.S32[0] = vCoords->data.S32[0] = 0;
+                    poly->data.F32[0] = 1.0;
+                    uCoords->n = vCoords->n = poly->n = 1;
+                    radiusLast = 0;
+                    moment = 0.0;
+                } else {
+                    int j = 0;          // Index for data
+                    double norm = 0.0;  // Normalisation
+                    for (int v = -size; v <= size; v++) {
+                        int v2 = PS_SQR(v);   // Square of v
+                        float vPoly = powf(v/(float)size, vOrder); // Value of v^vOrder
+
+                        for (int u = -size; u <= size; u++) {
+                            int u2 = PS_SQR(u); // Square of u
+                            int distance2 = u2 + v2; // Distance from the centre
+                            if (distance2 > lower2 && distance2 < upper2) {
+                                float uPoly = powf(u/(float)size, uOrder); // Value of u^uOrder
+
+                                float polyVal = uPoly * vPoly; // Value of polynomial
+                                if (polyVal != 0) { // No point adding it otherwise
+                                    uCoords->data.S32[j] = u;
+                                    vCoords->data.S32[j] = v;
+                                    poly->data.F32[j] = polyVal;
+                                    norm += polyVal;
+                                    moment += polyVal * (PS_SQR(u) + PS_SQR(v));
+
+                                    psVectorExtend(uCoords, RINGS_BUFFER, 1);
+                                    psVectorExtend(vCoords, RINGS_BUFFER, 1);
+                                    psVectorExtend(poly, RINGS_BUFFER, 1);
+                                    psTrace("psModules.imcombine", 9, "u = %d, v = %d, poly = %f\n",
+                                            u, v, poly->data.F32[j]);
+                                    j++;
+                                }
+                            }
+                        }
+                    }
+                    // Normalise kernel component to unit sum
+                    if (uOrder % 2 == 0 && vOrder % 2 == 0) {
+                        psBinaryOp(poly, poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
+                        // Add subtraction of 0,0 component to preserve photometric scaling
+                        uCoords->data.S32[j] = 0;
+                        vCoords->data.S32[j] = 0;
+                        poly->data.F32[j] = -1.0;
+                        psVectorExtend(uCoords, RINGS_BUFFER, 1);
+                        psVectorExtend(vCoords, RINGS_BUFFER, 1);
+                        psVectorExtend(poly, RINGS_BUFFER, 1);
+                    } else {
+                        norm = powf(size, uOrder) * powf(size, vOrder);
+                        psBinaryOp(poly, poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
+                    }
+//                    moment /= norm;
+                }
+
+                psTrace("psModules.imcombine", 8, "%ld pixels in kernel\n", uCoords->n);
+
+                kernels->preCalc->data[index] = data;
+                kernels->u->data.S32[index] = uOrder;
+                kernels->v->data.S32[index] = vOrder;
+                kernels->penalties->data.F32[index] = kernels->penalty * fabsf(moment);
+
+                psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d\n", index,
+                        i, uOrder, vOrder);
+            }
+        }
+    }
+
+    return kernels;
+}
+
+pmSubtractionKernels *pmSubtractionKernelsGenerate(pmSubtractionKernelsType type, int size, int spatialOrder,
+                                                   const psVector *fwhms, const psVector *orders, int inner,
+                                                   int binning, int ringsOrder, float penalty,
+                                                   pmSubtractionMode mode)
+{
+    switch (type) {
+      case PM_SUBTRACTION_KERNEL_POIS:
+        return pmSubtractionKernelsPOIS(size, spatialOrder, penalty, mode);
+      case PM_SUBTRACTION_KERNEL_ISIS:
+        return pmSubtractionKernelsISIS(size, spatialOrder, fwhms, orders, penalty, mode);
+      case PM_SUBTRACTION_KERNEL_SPAM:
+        return pmSubtractionKernelsSPAM(size, spatialOrder, inner, binning, penalty, mode);
+      case PM_SUBTRACTION_KERNEL_FRIES:
+        return pmSubtractionKernelsFRIES(size, spatialOrder, inner, penalty, mode);
+      case PM_SUBTRACTION_KERNEL_GUNK:
+        return pmSubtractionKernelsGUNK(size, spatialOrder, fwhms, orders, inner, penalty, mode);
+      case PM_SUBTRACTION_KERNEL_RINGS:
+        return pmSubtractionKernelsRINGS(size, spatialOrder, inner, ringsOrder, penalty, mode);
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unknown kernel type: %x", type);
+        return NULL;
+    }
+}
+
+
+// Intermediate string parsing functions required because of different APIs for strtol and strtof
+static inline int parseStringInt(const char *string)
+{
+    return strtol(string, NULL, 10);
+}
+static inline float parseStringFloat(const char *string)
+{
+    return strtof(string, NULL);
+}
+
+
+// Parse a string of a number, up to some delimiter, and advance past the delimiter
+#define PARSE_STRING_NUMBER(TARGET, STRING, DELIM, PARSEFUNC) { \
+    char *start = STRING;               /* Start of string */ \
+    char *end = strchr(STRING, DELIM);  /* End of string */ \
+    if (!end) { \
+        psAbort("End of string encountered"); \
+    } \
+    int stringSize = end - STRING;      /* Size of string with value, NOT including \0 */ \
+    char value[stringSize + 1];         /* String to parse */ \
+    strncpy(value, start, stringSize); \
+    value[stringSize] = '\0'; \
+    TARGET = PARSEFUNC(value); \
+    STRING += stringSize + 1;           /* Advance past delimiter */ \
+}
+
+
+pmSubtractionKernels *pmSubtractionKernelsFromDescription(const char *description, int bgOrder,
+                                                          pmSubtractionMode mode)
+{
+    PS_ASSERT_STRING_NON_EMPTY(description, NULL);
+
+    if (bgOrder != 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Background order %d is not yet supported.", bgOrder);
+        return false;
+    }
+
+    pmSubtractionKernelsType type = PM_SUBTRACTION_KERNEL_NONE; // Type of kernel
+    int size = 0;                       // Half-size of kernel
+    int spatialOrder = 0;               // Order of spatial variations
+    const psVector *fwhms = NULL;       // FWHM of Gaussians
+    const psVector *orders = NULL;      // Polynomial order for each FWHM
+    int inner = 0;                      // Size of inner region
+    int binning = 0;                    // Binning to use
+    int ringsOrder = 0;                 // Polynomial order for rings
+    float penalty = 0.0;                // Penalty for wideness
+
+    if (strncmp(description, "ISIS", 4) == 0) {
+        // XXX Support for GUNK
+        if (strstr(description, "+POIS")) {
+            type = PM_SUBTRACTION_KERNEL_GUNK;
+            psAbort("Deciphering GUNK kernels (%s) is not currently supported.", description);
+        } else {
+            type = PM_SUBTRACTION_KERNEL_ISIS;
+            char *ptr = (char*)description + 5;    // Eat "ISIS("
+            PARSE_STRING_NUMBER(size, ptr, ',', parseStringInt);
+
+            // Count the number of Gaussians
+            int numGauss = 0;
+            for (char *string = ptr; string; string = strchr(string + 1, '(')) {
+                numGauss++;
+            }
+
+            fwhms = psVectorAlloc(numGauss, PS_TYPE_F32);
+            orders = psVectorAlloc(numGauss, PS_TYPE_S32);
+
+            for (int i = 0; i < numGauss; i++) {
+                ptr++;                  // Eat the '('
+                PARSE_STRING_NUMBER(fwhms->data.F32[i], ptr, ',', parseStringFloat); // Eat "1.234,"
+                PARSE_STRING_NUMBER(orders->data.S32[i], ptr, ')', parseStringInt); // Eat "3)"
+            }
+
+            ptr++;                      // Eat ','
+            PARSE_STRING_NUMBER(spatialOrder, ptr, ',', parseStringInt);
+            penalty = parseStringFloat(ptr);
+        }
+    } else if (strncmp(description, "RINGS", 5) == 0) {
+        type = PM_SUBTRACTION_KERNEL_RINGS;
+        char *ptr = (char*)description + 6;
+        PARSE_STRING_NUMBER(size, ptr, ',', parseStringInt);
+        PARSE_STRING_NUMBER(inner, ptr, ',', parseStringInt);
+        PARSE_STRING_NUMBER(ringsOrder, ptr, ',', parseStringInt);
+        PARSE_STRING_NUMBER(spatialOrder, ptr, ',', parseStringInt);
+        PARSE_STRING_NUMBER(penalty, ptr, ')', parseStringInt);
+    } else {
+        psAbort("Deciphering kernels other than ISIS and RINGS is not currently supported.");
+    }
+
+
+    return pmSubtractionKernelsGenerate(type, size, spatialOrder, fwhms, orders,
+                                        inner, binning, ringsOrder, penalty, mode);
+}
+
+
+pmSubtractionKernelsType pmSubtractionKernelsTypeFromString(const char *type)
+{
+    if (strcasecmp(type, "POIS") == 0) {
+        return PM_SUBTRACTION_KERNEL_POIS;
+    }
+    if (strcasecmp(type, "ISIS") == 0) {
+        return PM_SUBTRACTION_KERNEL_ISIS;
+    }
+    if (strcasecmp(type, "SPAM") == 0) {
+        return PM_SUBTRACTION_KERNEL_SPAM;
+    }
+    if (strcasecmp(type, "FRIES") == 0) {
+        return PM_SUBTRACTION_KERNEL_FRIES;
+    }
+    if (strcasecmp(type, "GUNK") == 0) {
+        return PM_SUBTRACTION_KERNEL_GUNK;
+    }
+    if (strcasecmp(type, "RINGS") == 0) {
+        return PM_SUBTRACTION_KERNEL_RINGS;
+    }
+
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unrecognised kernel type: %s", type);
+    return PM_SUBTRACTION_KERNEL_NONE;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionKernels.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionKernels.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionKernels.h	(revision 22322)
@@ -0,0 +1,205 @@
+#ifndef PM_SUBTRACTION_KERNELS_H
+#define PM_SUBTRACTION_KERNELS_H
+
+#include <pslib.h>
+
+/// Type of subtraction kernel
+typedef enum {
+    PM_SUBTRACTION_KERNEL_NONE,         ///< Nothing --- an error
+    PM_SUBTRACTION_KERNEL_POIS,         ///< Pan-STARRS Optimal Image Subtraction --- delta functions
+    PM_SUBTRACTION_KERNEL_ISIS,         ///< Traditional kernel --- gaussians modified by polynomials
+    PM_SUBTRACTION_KERNEL_SPAM,         ///< Summed Pixels for Advanced Matching --- summed delta functions
+    PM_SUBTRACTION_KERNEL_FRIES,        ///< Fibonacci Radius Increases Excellence of Subtraction
+    PM_SUBTRACTION_KERNEL_GUNK,         ///< Grid United with Normal Kernel --- POIS and ISIS hybrid
+    PM_SUBTRACTION_KERNEL_RINGS,        ///< Rings Instead of the Normal Gaussian Subtraction
+} pmSubtractionKernelsType;
+
+/// Modes --- specifies which image to convolve
+typedef enum {
+    PM_SUBTRACTION_MODE_ERR,            // Error in the mode
+    PM_SUBTRACTION_MODE_1,              // Convolve image 1
+    PM_SUBTRACTION_MODE_2,              // Convolve image 2
+    PM_SUBTRACTION_MODE_UNSURE,         // Not sure yet which image to convolve so try to satisfy both
+    PM_SUBTRACTION_MODE_DUAL,           // Dual convolution
+} pmSubtractionMode;
+
+/// Kernels specification
+typedef struct {
+    pmSubtractionKernelsType type;      ///< Type of kernels --- allowing the use of multiple kernels
+    psString description;               ///< Description of the kernel parameters
+    long num;                           ///< Number of kernel components (not including the spatial ones)
+    psVector *u, *v;                    ///< Offset (for POIS) or polynomial order (for ISIS)
+    psVector *widths;                   ///< Gaussian FWHMs (ISIS)
+    psVector *uStop, *vStop;            ///< Width of kernel element (SPAM,FRIES only)
+    psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (for ISIS)
+    float penalty;                      ///< Penalty for wideness
+    psVector *penalties;                ///< Penalty for each kernel component
+    int size;                           ///< The half-size of the kernel
+    int inner;                          ///< The size of an inner region
+    int spatialOrder;                   ///< The spatial order of the kernels
+    int bgOrder;                        ///< The order for the background fitting
+    pmSubtractionMode mode;             ///< Mode for subtraction
+    int numCols, numRows;               ///< Size of image (for normalisation), or zero to use image provided
+    psVector *solution1, *solution2;    ///< Solution for the PSF matching
+    // Quality information
+    float mean, rms;                    ///< Mean and RMS of chi^2 from stamps
+    int numStamps;                      ///< Number of good stamps
+} pmSubtractionKernels;
+
+// Assertion to check pmSubtractionKernels
+#define PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(KERNELS, RETURNVALUE) { \
+    PS_ASSERT_PTR_NON_NULL(KERNELS, RETURNVALUE); \
+    PS_ASSERT_STRING_NON_EMPTY((KERNELS)->description, RETURNVALUE); \
+    PS_ASSERT_INT_POSITIVE((KERNELS)->num, RETURNVALUE); \
+    PS_ASSERT_VECTOR_NON_NULL((KERNELS)->u, RETURNVALUE); \
+    PS_ASSERT_VECTOR_NON_NULL((KERNELS)->v, RETURNVALUE); \
+    PS_ASSERT_VECTOR_TYPE((KERNELS)->u, PS_TYPE_S32, RETURNVALUE); \
+    PS_ASSERT_VECTOR_TYPE((KERNELS)->v, PS_TYPE_S32, RETURNVALUE); \
+    PS_ASSERT_VECTOR_SIZE((KERNELS)->u, (KERNELS)->num, RETURNVALUE); \
+    PS_ASSERT_VECTOR_SIZE((KERNELS)->v, (KERNELS)->num, RETURNVALUE); \
+    if ((KERNELS)->type == PM_SUBTRACTION_KERNEL_ISIS) { \
+        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->widths, RETURNVALUE); \
+        PS_ASSERT_VECTOR_TYPE((KERNELS)->widths, PS_TYPE_F32, RETURNVALUE); \
+        PS_ASSERT_VECTOR_SIZE((KERNELS)->widths, (KERNELS)->num, RETURNVALUE); \
+    } \
+    if ((KERNELS)->uStop || (KERNELS)->vStop) { \
+        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->uStop, RETURNVALUE); \
+        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->vStop, RETURNVALUE); \
+        PS_ASSERT_VECTOR_TYPE((KERNELS)->uStop, PS_TYPE_S32, RETURNVALUE); \
+        PS_ASSERT_VECTOR_TYPE((KERNELS)->vStop, PS_TYPE_S32, RETURNVALUE); \
+        PS_ASSERT_VECTOR_SIZE((KERNELS)->uStop, (KERNELS)->num, RETURNVALUE); \
+        PS_ASSERT_VECTOR_SIZE((KERNELS)->vStop, (KERNELS)->num, RETURNVALUE); \
+    } \
+    if ((KERNELS)->preCalc) { \
+        PS_ASSERT_ARRAY_NON_NULL((KERNELS)->preCalc, RETURNVALUE); \
+        PS_ASSERT_ARRAY_SIZE((KERNELS)->preCalc, (KERNELS)->num, RETURNVALUE); \
+    } \
+    PS_ASSERT_INT_NONNEGATIVE((KERNELS)->size, RETURNVALUE); \
+    PS_ASSERT_INT_NONNEGATIVE((KERNELS)->inner, RETURNVALUE); \
+    PS_ASSERT_INT_NONNEGATIVE((KERNELS)->spatialOrder, RETURNVALUE); \
+    PS_ASSERT_INT_NONNEGATIVE((KERNELS)->bgOrder, RETURNVALUE); \
+}
+
+// Assertion to check that the solution is attached
+#define PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(KERNELS, RETURNVALUE) { \
+    PS_ASSERT_VECTOR_NON_NULL((KERNELS)->solution1, RETURNVALUE); \
+    PS_ASSERT_VECTOR_TYPE((KERNELS)->solution1, PS_TYPE_F64, RETURNVALUE); \
+    PS_ASSERT_VECTOR_SIZE((KERNELS)->solution1, \
+                          (KERNELS)->num * PM_SUBTRACTION_POLYTERMS((KERNELS)->spatialOrder) + 1 + \
+                              PM_SUBTRACTION_POLYTERMS((KERNELS)->bgOrder), \
+                          RETURNVALUE); \
+    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) { \
+        PS_ASSERT_VECTOR_NON_NULL(kernels->solution2, RETURNVALUE); \
+        PS_ASSERT_VECTOR_TYPE((KERNELS)->solution2, PS_TYPE_F64, RETURNVALUE); \
+        PS_ASSERT_VECTOR_SIZE((KERNELS)->solution2, \
+                              (KERNELS)->num * PM_SUBTRACTION_POLYTERMS((KERNELS)->spatialOrder), \
+                               RETURNVALUE); \
+    } \
+}
+
+/// Generate a delta-function grid for subtraction kernels (like the POIS kernel)
+bool p_pmSubtractionKernelsAddGrid(pmSubtractionKernels *kernels, ///< The subtraction kernels to append to
+                                   int start, ///< Index at which to start appending
+                                   int size ///< Half-size of the grid
+    );
+
+/// General allocator for pmSubtractionKernels
+///
+/// Unlike the functions for the specific kernel type, this function does not set up the basis functions, but
+/// merely allocates space for their storage.
+pmSubtractionKernels *pmSubtractionKernelsAlloc(int numBasisFunctions, ///< Number of basis functions
+                                                pmSubtractionKernelsType type, ///< Kernel type
+                                                int size, ///< Half-size of kernel
+                                                int spatialOrder, ///< Order of spatial variations
+                                                float penalty, ///< Penalty for wideness
+                                                pmSubtractionMode mode ///< Mode for subtraction
+    );
+
+/// Generate POIS kernels
+pmSubtractionKernels *pmSubtractionKernelsPOIS(int size, ///< Half-size of the kernel (in both dims)
+                                               int spatialOrder, ///< Order of spatial variations
+                                               float penalty, ///< Penalty for wideness
+                                               pmSubtractionMode mode ///< Mode for subtraction
+    );
+
+/// Generate ISIS kernels without the flux scaling built in
+pmSubtractionKernels *p_pmSubtractionKernelsRawISIS(int size, ///< Half-size of the kernel
+                                                    int spatialOrder, ///< Order of spatial variations
+                                                    const psVector *fwhms, ///< Gaussian FWHMs
+                                                    const psVector *orders, ///< Polynomial order of gaussians
+                                                    float penalty, ///< Penalty for wideness
+                                                    pmSubtractionMode mode ///< Mode for subtraction
+    );
+
+/// Generate ISIS kernels
+pmSubtractionKernels *pmSubtractionKernelsISIS(int size, ///< Half-size of the kernel
+                                               int spatialOrder, ///< Order of spatial variations
+                                               const psVector *fwhms, ///< Gaussian FWHMs
+                                               const psVector *orders, ///< Polynomial order of gaussians
+                                               float penalty, ///< Penalty for wideness
+                                               pmSubtractionMode mode ///< Mode for subtraction
+                                               );
+
+/// Generate SPAM kernels
+pmSubtractionKernels *pmSubtractionKernelsSPAM(int size, ///< Half-size of the kernel
+                                               int spatialOrder, ///< Order of spatial variations
+                                               int inner, ///< Inner radius to preserve unbinned
+                                               int binning, ///< Kernel binning factor
+                                               float penalty, ///< Penalty for wideness
+                                               pmSubtractionMode mode ///< Mode for subtraction
+    );
+
+/// Generate FRIES kernels
+pmSubtractionKernels *pmSubtractionKernelsFRIES(int size, ///< Half-size of the kernel
+                                                int spatialOrder, ///< Order of spatial variations
+                                                int inner, ///< Inner radius to preserve unbinned
+                                                float penalty, ///< Penalty for wideness
+                                                pmSubtractionMode mode ///< Mode for subtraction
+    );
+
+/// Generate GUNK kernels
+pmSubtractionKernels *pmSubtractionKernelsGUNK(int size, ///< Half-size of the kernel
+                                               int spatialOrder, ///< Order of spatial variations
+                                               const psVector *fwhms, ///< Gaussian FWHMs
+                                               const psVector *orders, ///< Polynomial order of gaussians
+                                               int inner, ///< Inner radius containing grid of delta functions
+                                               float penalty, ///< Penalty for wideness
+                                               pmSubtractionMode mode ///< Mode for subtraction
+    );
+
+/// Generate RINGS kernels
+pmSubtractionKernels *pmSubtractionKernelsRINGS(int size, ///< Half-size of the kernel
+                                                int spatialOrder, ///< Order of spatial variations
+                                                int inner, ///< Inner radius to preserve unbinned
+                                                int ringsOrder, ///< Polynomial order
+                                                float penalty, ///< Penalty for wideness
+                                                pmSubtractionMode mode ///< Mode for subtraction
+    );
+
+
+/// Generate a kernel of a specified type
+pmSubtractionKernels *pmSubtractionKernelsGenerate(pmSubtractionKernelsType type, ///< Kernel type
+                                                   int size, ///< Half-size of the kernel
+                                                   int spatialOrder, ///< Order of spatial variations
+                                                   const psVector *fwhms, ///< Gaussian FWHMs
+                                                   const psVector *orders, ///< Polynomial order of gaussians
+                                                   int inner, ///< Inner radius to preserve unbinned
+                                                   int binning, ///< Kernel binning factor
+                                                   int ringsOrder, ///< Polynomial order for RINGS
+                                                   float penalty, ///< Penalty for wideness
+                                                   pmSubtractionMode mode ///< Mode for subtraction
+    );
+
+/// Generate a kernel using the description
+pmSubtractionKernels *pmSubtractionKernelsFromDescription(
+    const char *description,            ///< Description of kernel
+    int bgOrder,                        ///< Polynomial order for background fitting
+    pmSubtractionMode mode              ///< Mode for subtraction
+    );
+
+/// Return the appropriate type from a string
+pmSubtractionKernelsType pmSubtractionKernelsTypeFromString(const char *string // String name for kernel type
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMask.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMask.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMask.c	(revision 22322)
@@ -0,0 +1,245 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmSubtraction.h"
+#include "pmSubtractionKernels.h"
+
+#include "pmSubtractionMask.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Private (file-static) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Mark a pixel as blank in the image, mask and weight
+static inline void markBlank(psImage *image, // Image to mark as blank
+                             psImage *mask, // Mask to mark as blank (or NULL)
+                             psImage *weight, // Weight map to mark as blank (or NULL)
+                             int x, int y, // Coordinates to mark blank
+                             psMaskType blank // Blank mask value
+    )
+{
+    image->data.F32[y][x] = NAN;
+    if (mask) {
+        mask->data.PS_TYPE_MASK_DATA[y][x] |= blank;
+    }
+    if (weight) {
+        weight->data.F32[y][x] = NAN;
+    }
+    return;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+psImage *pmSubtractionMask(const psImage *mask1, const psImage *mask2, psMaskType maskVal,
+                           int size, int footprint, float badFrac, bool useFFT)
+{
+    PS_ASSERT_IMAGE_NON_NULL(mask1, NULL);
+    PS_ASSERT_IMAGE_TYPE(mask1, PS_TYPE_MASK, NULL);
+    if (mask2) {
+        PS_ASSERT_IMAGE_NON_NULL(mask2, NULL);
+        PS_ASSERT_IMAGE_TYPE(mask2, PS_TYPE_MASK, NULL);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(mask2, mask1, NULL);
+    }
+    PS_ASSERT_INT_NONNEGATIVE(size, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(footprint, NULL);
+    if (isfinite(badFrac)) {
+        PS_ASSERT_FLOAT_LARGER_THAN(badFrac, 0.0, NULL);
+        PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(badFrac, 1.0, NULL);
+    }
+
+    int numCols = mask1->numCols, numRows = mask1->numRows; // Size of the images
+
+    // Dereference inputs for convenience
+    psMaskType **data1 = mask1->data.PS_TYPE_MASK_DATA;
+    psMaskType **data2 = NULL;
+    if (mask2) {
+        data2 = mask2->data.PS_TYPE_MASK_DATA;
+    }
+
+    // First, a pass through to determine the fraction of bad pixels
+    if (isfinite(badFrac) && badFrac != 1.0) {
+        int numBad = 0;                 // Number of bad pixels
+        for (int y = 0; y < numRows; y++) {
+            for (int x = 0; x < numCols; x++) {
+                if (data1[y][x] & maskVal) {
+                    numBad++;
+                    continue;
+                }
+                if (data2 && data2[y][x] & maskVal) {
+                    numBad++;
+                }
+            }
+        }
+        if (numBad > badFrac * numCols * numRows) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Fraction of bad pixels (%d/%d=%f) exceeds limit (%f)\n",
+                    numBad, numCols * numRows, (float)numBad/(float)(numCols * numRows), badFrac);
+            return NULL;
+        }
+    }
+
+    // Worried about the masks for bad pixels and bad stamps colliding, so make our own mask
+    psImage *mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK); // The global mask
+    psImageInit(mask, 0);
+    psMaskType **maskData = mask->data.PS_TYPE_MASK_DATA; // Dereference for convenience
+
+    // Block out a border around the edge of the image
+
+    // Bottom stripe
+    for (int y = 0; y < PS_MIN(size + footprint, numRows); y++) {
+        for (int x = 0; x < numCols; x++) {
+            maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
+        }
+    }
+    // Either side
+    for (int y = PS_MIN(size + footprint, numRows); y < numRows - size - footprint; y++) {
+        for (int x = 0; x < PS_MIN(size + footprint, numCols); x++) {
+            maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
+        }
+        for (int x = PS_MAX(numCols - size - footprint, 0); x < numCols; x++) {
+            maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
+        }
+    }
+    // Top stripe
+    for (int y = PS_MAX(numRows - size - footprint, 0); y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
+        }
+    }
+
+    // XXX Could do something smarter here --- we will get images that are predominantly masked (where the
+    // skycell isn't overlapped by a large fraction by the observation), so that convolving around every bad
+    // pixel is wasting time.  As a first cut, I've put in a check on the fraction of bad pixels, but we could
+    // imagine looking for the edge of big regions and convolving just at the edge.  As a second cut, allow
+    // use of FFT convolution.
+
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            if (data1[y][x] & maskVal) {
+                maskData[y][x] |= PM_SUBTRACTION_MASK_BAD_1;
+            }
+            if (data2 && data2[y][x] & maskVal) {
+                maskData[y][x] |= PM_SUBTRACTION_MASK_BAD_2;
+            }
+        }
+    }
+
+    // Block out the entire stamp footprint around bad input pixels.
+
+    // We want to block out with the CONVOLVE mask anything that would be bad if we convolved with a bad
+    // reference pixel (within 'size').  Then we want to block out with the FOOTPRINT mask everything within a
+    // footprint's distance of those (within 'footprint').
+
+    if (!psImageConvolveMask(mask, mask, PM_SUBTRACTION_MASK_BAD_1,
+                             PM_SUBTRACTION_MASK_CONVOLVE_1,
+                             -size, size, -size, size)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad pixels from mask 1.");
+        psFree(mask);
+        return NULL;
+    }
+    if (!psImageConvolveMask(mask, mask, PM_SUBTRACTION_MASK_BAD_2,
+                             PM_SUBTRACTION_MASK_CONVOLVE_2,
+                             -size, size, -size, size)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad pixels from mask 2.");
+        psFree(mask);
+        return NULL;
+    }
+
+    return mask;
+}
+
+
+bool pmSubtractionBorder(psImage *image, psImage *weight, psImage *mask,
+                         int size, psMaskType blank)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
+    if (mask) {
+        PS_ASSERT_IMAGE_NON_NULL(mask, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(mask, image, false);
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, false);
+    }
+    if (weight) {
+        PS_ASSERT_IMAGE_NON_NULL(weight, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(weight, image, false);
+        PS_ASSERT_IMAGE_TYPE(weight, PS_TYPE_F32, false);
+    }
+
+    int numCols = image->numCols, numRows = image->numRows; // Image dimensions
+
+    for (int y = size; y < numRows - size; y++) {
+        for (int x = 0; x < size; x++) {
+            markBlank(image, mask, weight, x, y, blank);
+        }
+        for (int x = numCols - size; x < numCols; x++) {
+            markBlank(image, mask, weight, x, y, blank);
+        }
+    }
+    for (int y = 0; y < size; y++) {
+        for (int x = 0; x < numCols; x++) {
+            markBlank(image, mask, weight, x, y, blank);
+        }
+    }
+    for (int y = numRows - size; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            markBlank(image, mask, weight, x, y, blank);
+        }
+    }
+
+    return true;
+}
+
+
+bool pmSubtractionMaskApply(psImage *image, psImage *weight, const psImage *mask, pmSubtractionMode mode)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
+    if (weight) {
+        PS_ASSERT_IMAGE_NON_NULL(weight, false);
+        PS_ASSERT_IMAGE_TYPE(weight, PS_TYPE_F32, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(weight, image, false);
+    }
+    PS_ASSERT_IMAGE_NON_NULL(mask, false);
+    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_MASK, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(mask, image, false);
+
+    bool maskVal = PM_SUBTRACTION_MASK_BORDER; // Value to mask
+    switch (mode) {
+      case PM_SUBTRACTION_MODE_1:
+        maskVal |= PM_SUBTRACTION_MASK_CONVOLVE_1;
+        break;
+      case PM_SUBTRACTION_MODE_2:
+        maskVal |= PM_SUBTRACTION_MASK_CONVOLVE_2;
+        break;
+      case PM_SUBTRACTION_MODE_DUAL:
+        maskVal |= PM_SUBTRACTION_MASK_CONVOLVE_2 | PM_SUBTRACTION_MASK_CONVOLVE_2;
+        break;
+      case PM_SUBTRACTION_MODE_ERR:
+      case PM_SUBTRACTION_MODE_UNSURE:
+      default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unsuppored subtraction mode: %x", mode);
+        return false;
+    }
+
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+    psMaskType **maskData = mask->data.PS_TYPE_MASK_DATA; // Dereference mask
+
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            if (maskData[y][x] & maskVal) {
+                image->data.F32[y][x] = NAN;
+                if (weight) {
+                    weight->data.F32[y][x] = NAN;
+                }
+            }
+        }
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMask.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMask.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMask.h	(revision 22322)
@@ -0,0 +1,36 @@
+#ifndef PM_SUBTRACTION_MASK_H
+#define PM_SUBTRACTION_MASK_H
+
+#include <pslib.h>
+
+/// Generate a mask for use in the subtraction process
+psImage *pmSubtractionMask(const psImage *refMask, ///< Mask for the reference image (will be convolved)
+                           const psImage *inMask, ///< Mask for the input image, or NULL
+                           psMaskType maskVal, ///< Value to mask out
+                           int size, ///< Half-size of the kernel (pmSubtractionKernels.size)
+                           int footprint, ///< Half-size of the kernel footprint
+                           float badFrac, ///< Maximum fraction of bad input pixels to accept
+                           bool useFFT  ///< Use FFT to do convolution?
+    );
+
+/// Mark the non-convolved part of the image as blank
+bool pmSubtractionBorder(psImage *image,///< Image
+                         psImage *weight, ///< Weight map (or NULL)
+                         psImage *mask, ///< Mask (or NULL)
+                         int size,      ///< Kernel half-size
+                         psMaskType blank ///< Mask value for blank regions
+    );
+
+/// Apply the subtraction mask to an image and weight.
+///
+/// Unfortunately, image subtraction may result in a bi-modal image in masked areas, which can upset image
+/// statistics (very important for quantising images so that a product can be written out!).  This function
+/// sets masked areas to NAN in the image and weight.
+bool pmSubtractionMaskApply(psImage *image, ///< Image to which to apply mask
+                            psImage *weight, ///< Weight map to which to apply mask (or NULL)
+                            const psImage *mask, ///< Subtraction mask
+                            pmSubtractionMode mode ///< Subtraction mode
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMatch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMatch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMatch.c	(revision 22322)
@@ -0,0 +1,811 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmSubtractionParams.h"
+#include "pmSubtractionKernels.h"
+#include "pmSubtractionStamps.h"
+#include "pmSubtractionEquation.h"
+#include "pmSubtraction.h"
+#include "pmSubtractionMask.h"
+#include "pmSubtractionThreads.h"
+#include "pmSubtractionMatch.h"
+
+
+#define KERNEL_MOSAIC 2                 // Half-number of kernel instances in the mosaic image
+#define BG_STAT PS_STAT_ROBUST_MEDIAN   // Statistic to use for background
+
+static bool useFFT = true;              // Do convolutions using FFT
+
+
+//#define TESTING
+//#define TESTING_MEMORY
+
+// Output memory usage information
+static void memCheck(const char *where)
+{
+#ifdef TESTING_MEMORY
+    psMemBlock **leaks = NULL;
+    int numLeaks = psMemCheckLeaks(0, &leaks, NULL, true);
+    size_t largestSize = 0;
+    psMemId largest = 0;
+    size_t totalSize = 0;
+    for (int i = 0; i < numLeaks; i++) {
+        psMemBlock *mb = leaks[i];
+        totalSize += mb->userMemorySize;
+        if (mb->userMemorySize > largestSize) {
+            largestSize = mb->userMemorySize;
+            largest = mb->id;
+        }
+    }
+    psFree(leaks);
+    fprintf(stderr, "%s:\n", where);
+    fprintf(stderr, "    Memory in use: %zd\n", totalSize);
+    fprintf(stderr, "    Largest block: %ld\n", largest);
+    fprintf(stderr, "    sbrk(): %zd\n", (size_t)sbrk(0));
+#endif
+    return;
+}
+
+
+static bool getStamps(pmSubtractionStampList **stamps, // Stamps to read
+                      const pmReadout *ro1, // Readout 1
+                      const pmReadout *ro2, // Readout 2
+                      const psImage *subMask, // Mask for subtraction, or NULL
+                      psImage *weight,  // Weight map
+                      const psRegion *region, // Region of interest, or NULL
+                      float threshold,  // Threshold for stamp finding
+                      float stampSpacing, // Spacing between stamps
+                      int size,         // Kernel half-size
+                      int footprint,     // Convolution footprint for stamps
+                      pmSubtractionMode mode // Mode for subtraction
+    )
+{
+    psTrace("psModules.imcombine", 3, "Finding stamps...\n");
+    *stamps = pmSubtractionStampsFind(*stamps, ro1->image, subMask, region, threshold, footprint,
+                                      stampSpacing, mode);
+    if (!*stamps) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find stamps.");
+        return false;
+    }
+
+    memCheck("  find stamps");
+
+    psTrace("psModules.imcombine", 3, "Extracting stamps...\n");
+    if (!pmSubtractionStampsExtract(*stamps, ro1->image, ro2 ? ro2->image : NULL, weight, size)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to extract stamps.");
+        return false;
+    }
+
+    memCheck("   extract stamps");
+
+    return true;
+}
+
+
+// Record the variance factor in a readout
+static bool recordVarianceFactor(pmReadout *ro, // Readout of interest
+                                 float varFactor, // Variance factor
+                                 const psRegion *region // Region of interest
+                                 )
+{
+    if (region) {
+        varFactor *= (region->x1 - region->x0 + 1) * (region->y1 - region->y0 + 1) /
+            (ro->image->numCols * ro->image->numRows);
+    }
+
+    psMetadataItem *vfItem = psMetadataLookup(ro->analysis, PM_SUBTRACTION_ANALYSIS_VARFACTOR);
+    if (vfItem) {
+        psAssert(vfItem->type == PS_TYPE_F32, "Should be the type we said.");
+        vfItem->data.F32 += varFactor;
+    } else {
+        psMetadataAddF32(ro->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_VARFACTOR, 0,
+                         "Variance factor weighted by the area", varFactor);
+    }
+
+    return true;
+}
+
+bool pmSubtractionMatch(pmReadout *conv1, pmReadout *conv2, const pmReadout *ro1, const pmReadout *ro2,
+                        int footprint, float regionSize, float stampSpacing, float threshold,
+                        const psArray *sources, const char *stampsName,
+                        pmSubtractionKernelsType type, int size, int spatialOrder,
+                        const psVector *isisWidths, const psVector *isisOrders,
+                        int inner, int ringsOrder, int binning, float penalty,
+                        bool optimum, const psVector *optFWHMs, int optOrder, float optThreshold,
+                        int iter, float rej, psMaskType maskVal, psMaskType maskBad, psMaskType maskPoor,
+                        float poorFrac, float badFrac, pmSubtractionMode subMode)
+{
+    if (subMode != PM_SUBTRACTION_MODE_2) {
+        PM_ASSERT_READOUT_NON_NULL(conv1, false);
+        if (conv1->image) {
+            psFree(conv1->image);
+            conv1->image = NULL;
+        }
+        if (conv1->mask) {
+            psFree(conv1->mask);
+            conv1->mask = NULL;
+        }
+        if (conv1->weight) {
+            psFree(conv1->weight);
+            conv1->weight = NULL;
+        }
+    }
+    if (subMode != PM_SUBTRACTION_MODE_1) {
+        PM_ASSERT_READOUT_NON_NULL(conv2, false);
+        if (conv2->image) {
+            psFree(conv2->image);
+            conv2->image = NULL;
+        }
+        if (conv2->mask) {
+            psFree(conv2->mask);
+            conv2->mask = NULL;
+        }
+        if (conv2->weight) {
+            psFree(conv2->weight);
+            conv2->weight = NULL;
+        }
+    }
+
+    PM_ASSERT_READOUT_NON_NULL(ro1, false);
+    PM_ASSERT_READOUT_NON_NULL(ro2, false);
+    PM_ASSERT_READOUT_IMAGE(ro1, false);
+    PM_ASSERT_READOUT_IMAGE(ro2, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->image, ro2->image, false);
+
+    PS_ASSERT_INT_NONNEGATIVE(footprint, false);
+    // regionSize can be just about anything (except maybe negative, but it can be NAN)
+    PS_ASSERT_FLOAT_LARGER_THAN(stampSpacing, 0.0, false);
+    // Don't care what threshold is
+    if (sources) {
+        PS_ASSERT_ARRAY_NON_NULL(sources, false);
+    }
+    // stampsName may be anything
+    // We'll check kernel type when we allocate the kernels
+    PS_ASSERT_INT_POSITIVE(size, false);
+    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, false);
+    if (isisWidths || isisOrders) {
+        PS_ASSERT_VECTOR_NON_NULL(isisWidths, false);
+        PS_ASSERT_VECTOR_TYPE(isisWidths, PS_TYPE_F32, false);
+        PS_ASSERT_VECTOR_NON_NULL(isisOrders, false);
+        PS_ASSERT_VECTOR_TYPE(isisOrders, PS_TYPE_S32, false);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(isisWidths, isisOrders, false);
+    }
+    PS_ASSERT_INT_NONNEGATIVE(inner, false);
+    PS_ASSERT_INT_NONNEGATIVE(ringsOrder, false);
+    PS_ASSERT_INT_POSITIVE(binning, false);
+    if (optimum) {
+        PS_ASSERT_VECTOR_NON_NULL(optFWHMs, false);
+        PS_ASSERT_INT_NONNEGATIVE(optOrder, false);
+        PS_ASSERT_FLOAT_LARGER_THAN(optThreshold, 0.0, false);
+        PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(optThreshold, 1.0, false);
+    }
+    PS_ASSERT_INT_POSITIVE(iter, false);
+    PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
+    // Don't care about maskVal
+    // Don't care about maskBad
+    // Don't care about maskPoor
+    PS_ASSERT_FLOAT_LARGER_THAN(poorFrac, 0.0, NULL);
+    PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(poorFrac, 1.0, NULL);
+    if (isfinite(badFrac)) {
+        PS_ASSERT_FLOAT_LARGER_THAN(badFrac, 0.0, NULL);
+        PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(badFrac, 1.0, NULL);
+    }
+
+    // If the stamp footprint is smaller than the kernel size, then we won't get much signal in the outer
+    // parts of the kernel, which can result in bad matching artifacts.
+    if (footprint < size) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Stamp footprint (%d) should be larger than or equal to the kernel size (%d)",
+                footprint, size);
+        return false;
+    }
+
+    // Where does our weight map come from?
+    // Getting the weight exactly right is not necessary --- it's just used for weighting.
+    psImage *weight = NULL;             // Weight image to use
+    if (ro1->weight && ro2->weight) {
+        weight = (psImage*)psBinaryOp(NULL, ro1->weight, "+", ro2->weight);
+    } else if (ro1->weight) {
+        weight = psMemIncrRefCounter(ro1->weight);
+    } else if (ro2->weight) {
+        weight = psMemIncrRefCounter(ro2->weight);
+    } else {
+        weight = (psImage*)psBinaryOp(NULL, ro1->image, "+", ro2->image);
+    }
+
+    // Putting important variable declarations here, since they are freed after a "goto" if there is an error.
+    psImage *subMask = NULL;            // Mask for subtraction
+    psRegion *region = NULL;            // Iso-kernel region
+    psString regionString = NULL;       // String for region
+    pmSubtractionStampList *stamps = NULL; // Stamps for matching PSF
+    pmSubtractionKernels *kernels = NULL; // Kernel basis functions
+
+    int numCols = ro1->image->numCols, numRows = ro1->image->numRows; // Image dimensions
+
+    memCheck("start");
+
+    subMask = pmSubtractionMask(ro1->mask, ro2 ? ro2->mask : NULL, maskVal, size, footprint,
+                                badFrac, useFFT);
+    if (!subMask) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate subtraction mask.");
+        psFree(weight);
+        return false;
+    }
+
+    memCheck("mask");
+
+    // Get region of interest
+    int xRegions = 1, yRegions = 1;     // Number of iso-kernel regions
+    float xRegionSize = 0, yRegionSize = 0; // Size of iso-kernel regions
+    if (isfinite(regionSize) && regionSize != 0.0) {
+        xRegions = numCols / regionSize + 1;
+        yRegions = numRows / regionSize + 1;
+        xRegionSize = (float)numCols / (float)xRegions;
+        yRegionSize = (float)numRows / (float)yRegions;
+        region = psRegionAlloc(NAN, NAN, NAN, NAN);
+    }
+
+    // Iterate over iso-kernel regions
+    for (int j = 0; j < yRegions; j++) {
+        for (int i = 0; i < xRegions; i++) {
+            psTrace("psModules.imcombine", 1, "Subtracting region %d of %d...\n",
+                    j * xRegions + i + 1, xRegions * yRegions);
+            if (region) {
+                *region = psRegionSet((int)(i * xRegionSize), (int)((i + 1) * xRegionSize),
+                                      (int)(j * yRegionSize), (int)((j + 1) * yRegionSize));
+                psFree(regionString);
+                regionString = psRegionToString(*region);
+                psTrace("psModules.imcombine", 3, "Iso-kernel region: %s out of %d,%d\n",
+                        regionString, numCols, numRows);
+            }
+
+            if (sources) {
+                stamps = pmSubtractionStampsSetFromSources(sources, subMask, region, footprint,
+                                                           stampSpacing, subMode);
+            } else if (stampsName && strlen(stampsName) > 0) {
+                stamps = pmSubtractionStampsSetFromFile(stampsName, ro1->image, subMask, region, footprint,
+                                                        stampSpacing, subMode);
+            }
+
+            // We get the stamps here; we will also attempt to get stamps at the first iteration, but it
+            // doesn't matter.
+            if (!getStamps(&stamps, ro1, ro2, subMask, weight, NULL, threshold, stampSpacing,
+                           size, footprint, subMode)) {
+                goto MATCH_ERROR;
+            }
+
+            if (subMode == PM_SUBTRACTION_MODE_UNSURE) {
+                // Get backgrounds
+                psStats *bgStats = psStatsAlloc(BG_STAT); // Statistics for background
+                psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+                psVector *buffer = NULL;// Buffer for stats
+                if (!psImageBackground(bgStats, &buffer, ro1->image, ro1->mask, maskVal, rng)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to measure background of image 1.");
+                    psFree(bgStats);
+                    psFree(rng);
+                    psFree(buffer);
+                    goto MATCH_ERROR;
+                }
+                float bg1 = psStatsGetValue(bgStats, BG_STAT); // Background for image 1
+                if (!psImageBackground(bgStats, &buffer, ro2->image, ro2->mask, maskVal, rng)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to measure background of image 2.");
+                    psFree(bgStats);
+                    psFree(rng);
+                    psFree(buffer);
+                    goto MATCH_ERROR;
+                }
+                float bg2 = psStatsGetValue(bgStats, BG_STAT); // Background for image 2
+                psFree(bgStats);
+                psFree(rng);
+                psFree(buffer);
+
+                pmSubtractionMode newMode = pmSubtractionOrder(stamps, bg1, bg2); // Subtraction mode to use
+                switch (newMode) {
+                  case PM_SUBTRACTION_MODE_1:
+                    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolving image 1 to match image 2.");
+                    break;
+                  case PM_SUBTRACTION_MODE_2:
+                    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolving image 2 to match image 1.");
+                    break;
+                  default:
+                    psError(PS_ERR_UNKNOWN, false, "Unable to determine subtraction order.");
+                    goto MATCH_ERROR;
+                }
+                subMode = newMode;
+            }
+
+            // Define kernel basis functions
+            if (optimum && (type == PM_SUBTRACTION_KERNEL_ISIS || type == PM_SUBTRACTION_KERNEL_GUNK)) {
+                kernels = pmSubtractionKernelsOptimumISIS(type, size, inner, spatialOrder, optFWHMs, optOrder,
+                                                          stamps, footprint, optThreshold, penalty, subMode);
+                if (!kernels) {
+                    psErrorClear();
+                    psWarning("Unable to derive optimum ISIS kernel --- switching to default.");
+                }
+            }
+            if (kernels == NULL) {
+                // Not an ISIS/GUNK kernel, or the optimum kernel search failed
+                kernels = pmSubtractionKernelsGenerate(type, size, spatialOrder, isisWidths, isisOrders,
+                                                       inner, binning, ringsOrder, penalty, subMode);
+            }
+
+            // Add analysis metadata
+            {
+                psRegion *subRegion;    // Region over which subtraction was performed
+                if (region) {
+                    subRegion = psMemIncrRefCounter(region);
+                } else {
+                    subRegion = psRegionAlloc(0, numCols, 0, numRows);
+                }
+
+                if (subMode == PM_SUBTRACTION_MODE_1 || subMode == PM_SUBTRACTION_MODE_DUAL) {
+                    psMetadataAddPtr(conv1->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_KERNEL,
+                                     PS_DATA_UNKNOWN | PS_META_DUPLICATE_OK, "Subtraction kernels", kernels);
+                    psMetadataAddS32(conv1->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_MODE,
+                                     PS_META_DUPLICATE_OK, "Subtraction kernels", subMode);
+                    psMetadataAddPtr(conv1->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_REGION,
+                                     PS_DATA_REGION | PS_META_DUPLICATE_OK,
+                                     "Region over which subtraction was performed", subRegion);
+                }
+                if (subMode == PM_SUBTRACTION_MODE_2 || subMode == PM_SUBTRACTION_MODE_DUAL) {
+                    psMetadataAddPtr(conv2->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_KERNEL,
+                                     PS_DATA_UNKNOWN | PS_META_DUPLICATE_OK, "Subtraction kernels", kernels);
+                    psMetadataAddS32(conv2->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_MODE,
+                                     PS_META_DUPLICATE_OK, "Subtraction kernels", subMode);
+                    psMetadataAddPtr(conv2->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_REGION,
+                                     PS_DATA_REGION | PS_META_DUPLICATE_OK,
+                                     "Region over which subtraction was performed", subRegion);
+                }
+                psFree(subRegion);
+            }
+
+            memCheck("kernels");
+
+            int numRejected = -1;       // Number of rejected stamps in each iteration
+            for (int k = 0; k < iter && numRejected != 0; k++) {
+                psLogMsg("psModules.imcombine", PS_LOG_INFO, "Iteration %d.", k);
+
+                if (!getStamps(&stamps, ro1, ro2, subMask, weight, region, threshold, stampSpacing,
+                               size, footprint, subMode)) {
+                    goto MATCH_ERROR;
+                }
+
+                psTrace("psModules.imcombine", 3, "Calculating equation...\n");
+                if (!pmSubtractionCalculateEquation(stamps, kernels)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
+                    goto MATCH_ERROR;
+                }
+
+                memCheck("  calculate equation");
+
+                psTrace("psModules.imcombine", 3, "Solving equation...\n");
+
+                if (!pmSubtractionSolveEquation(kernels, stamps)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
+                    goto MATCH_ERROR;
+                }
+
+                memCheck("  solve equation");
+
+                psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
+                if (!deviations) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
+                    goto MATCH_ERROR;
+                }
+
+                memCheck("   calculate deviations");
+
+                psTrace("psModules.imcombine", 3, "Rejecting stamps...\n");
+                numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, rej, footprint);
+                if (numRejected < 0) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to reject stamps.");
+                    psFree(deviations);
+                    goto MATCH_ERROR;
+                }
+                psFree(deviations);
+
+                memCheck("  reject stamps");
+            }
+
+            if (numRejected > 0) {
+                psTrace("psModules.imcombine", 3, "Solving equation...\n");
+                if (!pmSubtractionSolveEquation(kernels, stamps)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
+                    goto MATCH_ERROR;
+                }
+                psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
+                if (!deviations) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
+                    goto MATCH_ERROR;
+                }
+                pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, NAN, footprint);
+                psFree(deviations);
+            }
+            psFree(stamps);
+            stamps = NULL;
+
+            memCheck("solution");
+
+            {
+                psTrace("psModules.imcombine", 2, "Generating diagnostics...\n");
+                // Generate image with convolution kernels
+                int fullSize = 2 * size + 1 + 1;    // Full size of kernel
+                int imageSize = (2 * KERNEL_MOSAIC + 1) * fullSize;
+                psImage *convKernels = psImageAlloc((subMode == PM_SUBTRACTION_MODE_DUAL ? 2 : 1) *
+                                                    imageSize - 1 +
+                                                    (subMode == PM_SUBTRACTION_MODE_DUAL ? 4 : 0),
+                                                    imageSize - 1, PS_TYPE_F32);
+                psImageInit(convKernels, NAN);
+                for (int j = -KERNEL_MOSAIC; j <= KERNEL_MOSAIC; j++) {
+                    for (int i = -KERNEL_MOSAIC; i <= KERNEL_MOSAIC; i++) {
+                        psImage *kernel = pmSubtractionKernelImage(kernels, (float)i / (float)KERNEL_MOSAIC,
+                                                                   (float)j / (float)KERNEL_MOSAIC,
+                                                                   false); // Image of the kernel
+                        if (!kernel) {
+                            psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
+                            psFree(convKernels);
+                            goto MATCH_ERROR;
+                        }
+
+                        if (psImageOverlaySection(convKernels, kernel, (i + KERNEL_MOSAIC) * fullSize,
+                                                  (j + KERNEL_MOSAIC) * fullSize, "=") == 0) {
+                            psError(PS_ERR_UNKNOWN, false, "Unable to overlay kernel image.");
+                            psFree(kernel);
+                            psFree(convKernels);
+                            goto MATCH_ERROR;
+                        }
+                        psFree(kernel);
+
+                        if (subMode == PM_SUBTRACTION_MODE_DUAL) {
+                            kernel = pmSubtractionKernelImage(kernels, (float)i / (float)KERNEL_MOSAIC,
+                                                              (float)j / (float)KERNEL_MOSAIC,
+                                                              true); // Image of the kernel
+                            if (!kernel) {
+                                psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
+                                psFree(convKernels);
+                                goto MATCH_ERROR;
+                            }
+
+                            if (psImageOverlaySection(convKernels, kernel,
+                                                      (2 * KERNEL_MOSAIC + 1 + i + KERNEL_MOSAIC) * fullSize +
+                                                      4,
+                                                      (j + KERNEL_MOSAIC) * fullSize, "=") == 0) {
+                                psError(PS_ERR_UNKNOWN, false, "Unable to overlay kernel image.");
+                                psFree(kernel);
+                                psFree(convKernels);
+                                goto MATCH_ERROR;
+                            }
+                            psFree(kernel);
+                        }
+
+                    }
+                }
+
+                psString comment = NULL; // Comment for metadata
+                psStringAppend(&comment, "Subtraction kernel for region %s", regionString);
+                psMetadataAddImage(conv1->analysis, PS_LIST_TAIL, "SUBTRACTION.KERNEL.IMAGE",
+                                   PS_META_DUPLICATE_OK, comment, convKernels);
+                psFree(comment);
+                psFree(convKernels);
+            }
+
+#if 0
+            {
+                // Generate images of the kernel components
+                psMetadata *header = psMetadataAlloc(); // Header
+                for (int i = 0; i < solution->n; i++) {
+                    psString name = NULL;       // Header keyword
+                    psStringAppend(&name, "SOLN%04d", i);
+                    psMetadataAddF64(header, PS_LIST_TAIL, name, 0, NULL, solution->data.F64[i]);
+                    psFree(name);
+                }
+                psArray *kernelImages = pmSubtractionKernelSolutions(solution, kernels, 0.0, 0.0);
+                psFits *kernelFile = psFitsOpen("kernels.fits", "w");
+                (void)psFitsWriteImageCube(kernelFile, header, kernelImages, NULL);
+                psFitsClose(kernelFile);
+                psFree(kernelImages);
+                psFree(header);
+            }
+#endif
+
+            memCheck("diag outputs");
+
+            psTrace("psModules.imcombine", 2, "Convolving...\n");
+            if (!pmSubtractionConvolve(conv1, conv2, ro1, ro2, subMask, maskBad, maskPoor, poorFrac,
+                                       region, kernels, true, useFFT)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to convolve image.");
+                goto MATCH_ERROR;
+            }
+
+            // Set the variance factors
+            switch (subMode) {
+              case PM_SUBTRACTION_MODE_1: {
+                  recordVarianceFactor(conv1, pmSubtractionVarianceFactor(kernels, 0.5, 0.5, false),
+                                       region);
+                  break;
+              }
+              case PM_SUBTRACTION_MODE_2:
+                recordVarianceFactor(conv1, pmSubtractionVarianceFactor(kernels, 0.5, 0.5, false),
+                                     region);
+                break;
+              case PM_SUBTRACTION_MODE_DUAL:
+                recordVarianceFactor(conv1, pmSubtractionVarianceFactor(kernels, 0.5, 0.5, false),
+                                     region);
+                recordVarianceFactor(conv2, pmSubtractionVarianceFactor(kernels, 0.5, 0.5, true),
+                                     region);
+                break;
+              default:
+                psAbort("Should never reach here.");
+            }
+
+            psFree(kernels);
+            kernels = NULL;
+
+            // There is data in the readout now
+            if (subMode == PM_SUBTRACTION_MODE_1 || subMode == PM_SUBTRACTION_MODE_DUAL) {
+                conv1->data_exists = true;
+                if (conv1->parent) {
+                    conv1->parent->data_exists = true;
+                    conv1->parent->parent->data_exists = true;
+                }
+            }
+            if (subMode == PM_SUBTRACTION_MODE_2 || subMode == PM_SUBTRACTION_MODE_DUAL) {
+                conv2->data_exists = true;
+                if (conv2->parent) {
+                    conv2->parent->data_exists = true;
+                    conv2->parent->parent->data_exists = true;
+                }
+            }
+        }
+    }
+    psFree(region);
+    region = NULL;
+    psFree(regionString);
+    regionString = NULL;
+    psFree(subMask);
+    subMask = NULL;
+    psFree(weight);
+    weight = NULL;
+
+    if (!pmSubtractionBorder(conv1->image, conv1->weight, conv1->mask, size, maskBad)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to set border of convolved image.");
+        goto MATCH_ERROR;
+    }
+
+    memCheck("convolution");
+
+
+#ifdef TESTING
+    {
+        if (subMode == PM_SUBTRACTION_MODE_1 || subMode == PM_SUBTRACTION_MODE_DUAL) {
+            psFits *fits = psFitsOpen("convolved1.fits", "w");
+            psFitsWriteImage(fits, NULL, conv1->image, 0, NULL);
+            psFitsClose(fits);
+        }
+
+        if (subMode == PM_SUBTRACTION_MODE_2 || subMode == PM_SUBTRACTION_MODE_DUAL) {
+            psFits *fits = psFitsOpen("convolved2.fits", "w");
+            psFitsWriteImage(fits, NULL, conv2->image, 0, NULL);
+            psFitsClose(fits);
+        }
+    }
+#endif
+
+    return true;
+
+MATCH_ERROR:
+    psFree(region);
+    psFree(regionString);
+    psFree(subMask);
+    psFree(kernels);
+    psFree(stamps);
+    psFree(weight);
+    return false;
+}
+
+
+// Determine a rough width (integer value) of the star in the image
+// XXX Could improve this by using a user-provided list of floating-point widths (or an end point and
+// increment).
+static int subtractionOrderWidth(const psKernel *kernel, // Image
+                                 float bg, // Background in image
+                                 int size, // Maximum size
+                                 const psArray *models, // Buffer of models
+                                 const psVector *modelSums // Buffer of model sums
+    )
+{
+    assert(kernel);
+    assert(models);
+    assert(modelSums);
+
+    int xMin = -size, xMax = size; // Bounds in x
+    int yMin = -size, yMax = size; // Bounds in y
+
+    // Fit gaussians of varying widths to the image, record the chi^2
+    psVector *chi2 = psVectorAlloc(size, PS_TYPE_F32); // chi^2 as a function of radius
+    for (int sigma = 0; sigma < size; sigma++) {
+        double sumFG = 0.0; // Sum for calculating the normalisation of the Gaussian
+        psKernel *model = models->data[sigma]; // Model of interest
+        for (int y = yMin; y <= yMax; y++) {
+            for (int x = xMin; x <= xMax; x++) {
+                sumFG += model->kernel[y][x] * (kernel->kernel[y][x] - bg);
+            }
+        }
+        float norm = sumFG * modelSums->data.F64[sigma]; // Normalisation for Gaussian
+        double sumDev2 = 0.0;           // Sum of square deviations
+        for (int y = yMin; y <= yMax; y++) {
+            for (int x = xMin; x <= xMax; x++) {
+                float dev = kernel->kernel[y][x] - bg - norm * model->kernel[y][x]; // Deviation
+                sumDev2 += PS_SQR(dev);
+            }
+        }
+        chi2->data.F32[sigma] = sumDev2;
+    }
+
+    // Find the minimum chi^2
+    int bestIndex = -1;                 // Index of best chi^2
+    float bestChi2 = INFINITY;          // Best chi^2
+    for (int i = 0; i < size; i++) {
+        if (chi2->data.F32[i] < bestChi2) {
+            bestChi2 = chi2->data.F32[i];
+            bestIndex = i;
+        }
+    }
+    psFree(chi2);
+
+    return bestIndex + 1;
+}
+
+
+bool pmSubtractionOrderStamp(psVector *ratios, psVector *mask, const pmSubtractionStampList *stamps,
+                             const psArray *models, const psVector *modelSums,
+                             int index, float bg1, float bg2)
+{
+    PS_ASSERT_VECTOR_NON_NULL(ratios, false);
+    PS_ASSERT_VECTOR_NON_NULL(mask, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(ratios, mask, false);
+    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
+    PS_ASSERT_INT_NONNEGATIVE(index, false);
+    PS_ASSERT_INT_LESS_THAN(index, stamps->num, false);
+    PS_ASSERT_ARRAY_NON_NULL(models, false);
+    PS_ASSERT_VECTOR_NON_NULL(modelSums, false);
+    PS_ASSERT_VECTOR_SIZE(modelSums, models->n, false);
+
+    pmSubtractionStamp *stamp = stamps->stamps->data[index]; // Stamp of interest
+    psAssert(stamp->status == PM_SUBTRACTION_STAMP_CALCULATE || stamp->status == PM_SUBTRACTION_STAMP_USED,
+             "We checked this earlier.");
+
+    // Widths of stars
+    int width1 = subtractionOrderWidth(stamp->image1, bg1, stamps->footprint, models, modelSums);
+    int width2 = subtractionOrderWidth(stamp->image2, bg2, stamps->footprint, models, modelSums);
+
+    if (width1 == 0 || width2 == 0) {
+        ratios->data.F32[index] = NAN;
+        mask->data.PS_TYPE_MASK_DATA[index] = 0xff;
+    } else {
+        ratios->data.F32[index] = (float)width1 / (float)width2;
+        mask->data.PS_TYPE_MASK_DATA[index] = 0;
+    }
+
+    return true;
+}
+
+bool pmSubtractionOrderThread(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    psVector *ratios = job->args->data[0]; // Ratios of widths
+    psVector *mask = job->args->data[1]; // Mask for ratios
+    const pmSubtractionStampList *stamps = job->args->data[2]; // List of stamps
+    const psArray *models = job->args->data[3]; // Gaussian models
+    const psVector *modelSums = job->args->data[4]; // Gaussian model sums
+    int index = PS_SCALAR_VALUE(job->args->data[5], S32); // Stamp index
+    float bg1 = PS_SCALAR_VALUE(job->args->data[6], F32); // Background of image 1
+    float bg2 = PS_SCALAR_VALUE(job->args->data[7], F32); // Background of image 2
+
+    return pmSubtractionOrderStamp(ratios, mask, stamps, models, modelSums, index, bg1, bg2);
+}
+
+pmSubtractionMode pmSubtractionOrder(pmSubtractionStampList *stamps, float bg1, float bg2)
+{
+    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, PM_SUBTRACTION_MODE_ERR);
+
+    psVector *mask = psVectorAlloc(stamps->num, PS_TYPE_MASK); // Mask for stamps
+    psVector *ratios = psVectorAlloc(stamps->num, PS_TYPE_F32); // Ratios of widths
+
+    // Generate models
+    int size = stamps->footprint;       // Maximum size
+    psArray *models = psArrayAlloc(size); // Gaussian models
+    psVector *modelSums = psVectorAlloc(size, PS_TYPE_F64); // Gaussian model sums
+    for (int sigma = 0; sigma < size; sigma++) {
+        psKernel *model = psKernelAlloc(-size, size, -size, size); // Gaussian model
+        float invSigma2 = 1.0 / (float)PS_SQR(1 + sigma); // Inverse sigma squared
+        double sumGG = 0.0;         // Sum of square of Gaussian
+        for (int y = -size; y <= size; y++) {
+            int y2 = PS_SQR(y);     // y squared
+            for (int x = -size; x <= size; x++) {
+                float rad2 = PS_SQR(x) + y2; // Radius squared
+                float value = expf(-rad2 * invSigma2); // Model value
+                model->kernel[y][x] = value;
+                sumGG += PS_SQR(value);
+            }
+        }
+        models->data[sigma] = model;
+        modelSums->data.F64[sigma] = 1.0 / sumGG;
+    }
+
+    // Fit models to stamps
+    for (int i = 0; i < stamps->num; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+        if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE && stamp->status != PM_SUBTRACTION_STAMP_USED) {
+            mask->data.PS_TYPE_MASK_DATA[i] = 0xff;
+            continue;
+        }
+
+        if (pmSubtractionThreaded()) {
+            psThreadJob *job = psThreadJobAlloc("PSMODULES_SUBTRACTION_ORDER");
+            psArrayAdd(job->args, 1, ratios);
+            psArrayAdd(job->args, 1, mask);
+            psArrayAdd(job->args, 1, stamps);
+            psArrayAdd(job->args, 1, models);
+            psArrayAdd(job->args, 1, modelSums);
+            PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, bg1, PS_TYPE_F32);
+            PS_ARRAY_ADD_SCALAR(job->args, bg2, PS_TYPE_F32);
+            if (!psThreadJobAddPending(job)) {
+                psFree(job);
+                return false;
+            }
+            psFree(job);
+        } else {
+            if (!pmSubtractionOrderStamp(ratios, mask, stamps, models, modelSums, i, bg1, bg2)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to measure PSF width for stamp %d", i);
+                psFree(models);
+                psFree(modelSums);
+                psFree(ratios);
+                psFree(mask);
+                return false;
+            }
+        }
+    }
+
+    if (!psThreadPoolWait(true)) {
+        psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
+        psFree(models);
+        psFree(modelSums);
+        psFree(ratios);
+        psFree(mask);
+            return false;
+    }
+
+    psFree(models);
+    psFree(modelSums);
+
+    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
+    if (!psVectorStats(stats, ratios, NULL, mask, 0xff)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to calculate statistics for moments ratio.");
+        psFree(mask);
+        psFree(ratios);
+        psFree(stats);
+        return PM_SUBTRACTION_MODE_ERR;
+    }
+    psFree(ratios);
+    psFree(mask);
+
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Median width ratio: %lf", stats->robustMedian);
+    pmSubtractionMode mode = (stats->robustMedian <= 1.0 ? PM_SUBTRACTION_MODE_1 : PM_SUBTRACTION_MODE_2);
+    psFree(stats);
+
+    return mode;
+}
+
+
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMatch.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMatch.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionMatch.h	(revision 22322)
@@ -0,0 +1,76 @@
+#ifndef PM_SUBTRACTION_MATCH_H
+#define PM_SUBTRACTION_MATCH_H
+
+#include <pslib.h>
+
+#include <pmHDU.h>
+#include <pmFPA.h>
+#include <pmSubtractionKernels.h>
+#include <pmSubtractionStamps.h>
+
+// Names for things put on the readout analysis metadata
+#define PM_SUBTRACTION_ANALYSIS_KERNEL "SUBTRACTION.KERNEL" // Kernel used for convolving
+#define PM_SUBTRACTION_ANALYSIS_MODE "SUBTRACTION.MODE" // Subtraction mode
+#define PM_SUBTRACTION_ANALYSIS_REGION "SUBTRACTION.REGION" // Subtraction region
+#define PM_SUBTRACTION_ANALYSIS_VARFACTOR "SUBTRACTION.VARFACTOR"// Variance factor
+
+
+/// Match two images
+bool pmSubtractionMatch(pmReadout *conv1, ///< Output convolved data for image 1
+                        pmReadout *conv2, ///< Output convolved data for image 2
+                        const pmReadout *ro1, ///< Image 1
+                        const pmReadout *ro2, ///< Image 2
+                        // Stamp parameters
+                        int footprint,  ///< Stamp half-size
+                        float regionSize, ///< Typical size of iso-kernel regions
+                        float stampSpacing, ///< Typical spacing between stamps
+                        float threshold, ///< Threshold for stamps
+                        const psArray *sources, ///< Sources for stamps
+                        const char *stampsName, ///< Filename for stamps
+                        // Kernel parameters
+                        pmSubtractionKernelsType type, ///< Kernel type
+                        int size,       ///< Kernel half-size
+                        int order,      ///< Spatial polynomial order
+                        const psVector *widths, ///< ISIS Gaussian widths
+                        const psVector *orders, ///< ISIS Polynomial orders
+                        int inner,      ///< Inner radius for various kernel types
+                        int ringsOrder, ///< RINGS polynomial order
+                        int binning,    ///< SPAM kernel binning
+                        float penalty,  ///< Penalty for wideness
+                        bool optimum,   ///< Search for optimum ISIS kernel?
+                        const psVector *optFWHMs, ///< FWHMs for optimum search
+                        int optOrder,   ///< Maximum order for optimum search
+                        float optThreshold, ///< Threshold for optimum search (0..1)
+                        // Operational parameters
+                        int iter,       ///< Rejection iterations
+                        float rej,      ///< Rejection threshold
+                        psMaskType maskVal, ///< Value to mask for input
+                        psMaskType maskBad, ///< Mask for output bad pixels
+                        psMaskType maskPoor, ///< Mask for output poor pixels
+                        float poorFrac, ///< Fraction for "poor"
+                        float badFrac,   ///< Maximum fraction of bad input pixels to accept
+                        pmSubtractionMode mode ///< Mode of subtraction; may be modified
+    );
+
+/// Execute a thread job to measure the PSF width ratios
+bool pmSubtractionOrderThread(psThreadJob *job ///< Job to execute
+    );
+
+/// Measure the PSF width ratio for a single stamp
+bool pmSubtractionOrderStamp(psVector *ratios, ///< PSF width ratios
+                             psVector *mask, ///< Mask for PSF width ratios
+                             const pmSubtractionStampList *stamps, ///< List of stamps
+                             const psArray *models, ///< Pre-calculated gaussian models
+                             const psVector *modelSums, ///< Pre-calculated gaussian model sums
+                             int index, ///< Index of stamp
+                             float bg1, ///< Background for image 1
+                             float bg2  ///< Background for image 2
+    );
+
+/// Determine which image to convolve
+pmSubtractionMode pmSubtractionOrder(pmSubtractionStampList *stamps, ///< Stamps that have been extracted
+                                     float bg1, float bg2 ///< Background for each image
+    );
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionParams.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionParams.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionParams.c	(revision 22322)
@@ -0,0 +1,510 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <pslib.h>
+
+#include "pmSubtractionStamps.h"
+#include "pmSubtraction.h"
+#include "pmSubtractionParams.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if 0
+// Convolve the reference stamp by the kernel
+static psKernel *convolveStamp(const pmSubtractionStamp *stamp, // Stamp to be convolved
+                               const psKernel *kernel, // Kernel by which to convolve
+                               int footprint // Size of area to be convolved
+    )
+{
+    psKernel *convolution = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Result
+    psKernel *reference = stamp->reference; // Reference stamp, to be convolved
+
+    // Range of kernel
+    int uMin = kernel->xMin;
+    int uMax = kernel->xMax;
+    int vMin = kernel->yMin;
+    int vMax = kernel->yMax;
+
+    for (int y = -footprint; y <= footprint; y++) {
+        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
+        for (int x = -footprint; x <= footprint; x++, conv++) {
+            *conv = 0.0;
+
+            int xStart = x + uMin;      // Start index for convolution
+            for (int v = vMin; v <= vMax; v++) {
+                psF32 *ref = &reference->kernel[y + v][xStart]; // Dereference reference image
+                psF32 *krnl = &kernel->kernel[v][uMin]; // Dereference kernel in x
+                for (int u = uMin; u <= uMax; u++, ref++, krnl++) {
+                    *conv += *ref * *krnl;
+                }
+            }
+        }
+    }
+
+    return convolution;
+}
+#endif
+
+/// Select the appropriate convolution, given the kernel basis function and subtraction mode
+static inline psKernel *selectConvolution(const pmSubtractionStamp *stamp, // Stamp
+                                          int kernelIndex, // Index for kernel component
+                                          pmSubtractionMode mode // Mode of subtraction
+    )
+{
+    switch (mode) {
+      case PM_SUBTRACTION_MODE_1:
+        return stamp->convolutions1->data[kernelIndex];
+      case PM_SUBTRACTION_MODE_2:
+        return stamp->convolutions2->data[kernelIndex];
+      default:
+        psAbort("Unsupported subtraction mode: %x", mode);
+    }
+    return NULL;                        // Unreached
+}
+
+// Accumulate cross-term sums for a stamp
+static void accumulateCross(double *sumI, // Sum of I(x)/sigma(x)^2
+                            double *sumII, // Sum of I(x)^2/sigma(x)^2
+                            double *sumIC, // Sum of I(x)conv(x)/sigma(x)^2
+                            const pmSubtractionStamp *stamp, // Stamp with weight
+                            const psKernel *target, // Target stamp
+                            int kernelIndex, // Index for kernel component
+                            int footprint, // Size of region of interest
+                            pmSubtractionMode mode // Mode of subtraction
+    )
+{
+    psKernel *weight = stamp->weight;   // Weight, sigma(x)^2
+    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
+
+    for (int y = -footprint; y <= footprint; y++) {
+        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
+        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
+        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
+        for (int x = -footprint; x <= footprint; x++, in++, wt++, conv++) {
+            double temp = *in / *wt; // Temporary product
+            *sumI += temp;
+            *sumII += *in * temp;
+            *sumIC += *conv * temp;
+        }
+    }
+    return;
+}
+
+// Accumulate convolution sums for a stamp
+static void accumulateConvolutions(double *sumC, // Sum of conv(x)/sigma(x)^2
+                                   double *sumCC, // Sum of conv(x)^2/sigma(x)^2
+                                   const pmSubtractionStamp *stamp, // Stamp with input and weight
+                                   int kernelIndex, // Index for kernel component
+                                   int footprint, // Size of region of interest
+                                   pmSubtractionMode mode // Mode of subtraction
+    )
+{
+    psKernel *weight = stamp->weight;   // Weight, sigma(x)^2
+    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
+
+    for (int y = -footprint; y <= footprint; y++) {
+        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
+        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
+        for (int x = -footprint; x <= footprint; x++, wt++, conv++) {
+            double convNoise = *conv / *wt; // Temporary product
+            *sumC += convNoise;
+            *sumCC += *conv * convNoise;
+        }
+    }
+    return;
+}
+
+static double accumulateChi2(const psKernel *target, // Target stamp
+                             pmSubtractionStamp *stamp, // Stamp with weight
+                             int kernelIndex, // Index for kernel component
+                             double coeff, // Coefficient of convolution
+                             double bg,  // Background term
+                             int footprint, // Size of region of interest
+                             pmSubtractionMode mode // Mode of subtraction
+    )
+{
+    double chi2 = 0.0;
+    psKernel *weight = stamp->weight;   // Weight, sigma(x)^2
+    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
+
+    for (int y = -footprint; y <= footprint; y++) {
+        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
+        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
+        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
+        for (int x = -footprint; x <= footprint; x++, in++, wt++, conv++) {
+            chi2 += PS_SQR(*in - bg - coeff * *conv) / *wt;
+        }
+    }
+
+    return chi2;
+}
+
+// Return the initial value of chi^2
+static double initialChi2(const psKernel *target, // Target stamp
+                          const pmSubtractionStamp *stamp, // Stamp with weight
+                          int footprint, // Size of convolution
+                          pmSubtractionMode mode // Mode of subtraction
+    )
+{
+    psKernel *weight = stamp->weight;   // Weight map
+    psKernel *source;                   // Source stamp
+    switch (mode) {
+      case PM_SUBTRACTION_MODE_1:
+        source = stamp->image1;
+        break;
+      case PM_SUBTRACTION_MODE_2:
+        source = stamp->image2;
+        break;
+      default:
+        psAbort("Unsupported subtraction mode: %x", mode);
+    }
+
+    double chi2 = 0.0;                  // Chi^2
+    for (int y = -footprint; y <= footprint; y++) {
+        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
+        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
+        psF32 *ref = &source->kernel[y][-footprint]; // Derference reference
+        for (int x = -footprint; x <= footprint; x++, in++, wt++, ref++) {
+            float diff = *in - *ref;    // Temporary value
+            chi2 += PS_SQR(diff) / *wt;
+        }
+    }
+
+    return chi2;
+}
+
+// Subtract a convolution from the input
+static void subtractConvolution(psKernel *target, // Target stamp
+                                const pmSubtractionStamp *stamp, // Stamp with weight
+                                int kernelIndex, // Index for kernel component
+                                float coeff, // Coefficient of subtraction
+                                float bg, // Background term
+                                int footprint, // Size of region of interest
+                                pmSubtractionMode mode // Mode of subtraction
+    )
+{
+    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
+    for (int y = -footprint; y <= footprint; y++) {
+        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
+        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
+        for (int x = -footprint; x <= footprint; x++, in++, conv++) {
+            *in -= *conv * coeff + bg;
+        }
+    }
+
+    return;
+}
+
+
+pmSubtractionKernels *pmSubtractionKernelsOptimumISIS(pmSubtractionKernelsType type, int size, int inner,
+                                                      int spatialOrder, const psVector *fwhms, int maxOrder,
+                                                      const pmSubtractionStampList *stamps, int footprint,
+                                                      float tolerance, float penalty, pmSubtractionMode mode)
+{
+    if (type != PM_SUBTRACTION_KERNEL_ISIS && type != PM_SUBTRACTION_KERNEL_GUNK) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Invalid kernel type: %x\n", type);
+        return NULL;
+    }
+    PS_ASSERT_INT_NONNEGATIVE(size, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(inner, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(fwhms, NULL);
+    PS_ASSERT_VECTOR_TYPE(fwhms, PS_TYPE_F32, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(maxOrder, NULL);
+    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(footprint, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN(tolerance, 0.0, NULL);
+
+    // Generate the kernels to test
+    int numGaussians = fwhms->n;       // Number of Gaussians
+    int numKernels = numGaussians * (maxOrder + 1) * (maxOrder + 2) / 2; // Number of kernel components
+    psString params = NULL;             // Parameter, for description
+    for (int i = 0; i < numGaussians; i++) {
+        psStringAppend(&params, "%.2f,", fwhms->data.F32[i]);
+    }
+    params[strlen(params) - 1] = '\0';
+
+    psVector *orders = psVectorAlloc(numGaussians, PS_TYPE_S32); // Polynomial orders
+    psVectorInit(orders, maxOrder);
+    pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders,
+                                                                  penalty, mode); // Kernels
+    psFree(orders);
+    psFree(kernels->description);
+    kernels->description = NULL;
+    psStringAppend(&kernels->description, "OptISIS(%d,(%s),%d)", size, params, spatialOrder);
+    psFree(params);
+
+    // Need to save the stamp inputs --- we're changing the values!
+    int numStamps = stamps->num;        // Number of stamps
+    psArray *targets = psArrayAlloc(numStamps); // Deep copies of the targets
+    psVector *badStamps = psVectorAlloc(numStamps, PS_TYPE_U8); // Mark the bad stamps
+    psVectorInit(badStamps, 0);
+    for (int i = 0; i < numStamps; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+        if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE && stamp->status != PM_SUBTRACTION_STAMP_USED) {
+            badStamps->data.U8[i] = 0xff;
+            continue;
+        }
+        psKernel *target;               // Target image of interest
+        switch (mode) {
+          case PM_SUBTRACTION_MODE_1:
+            target = stamp->image2;
+            break;
+          case PM_SUBTRACTION_MODE_2:
+            target = stamp->image1;
+            break;
+          default:
+            psAbort("Unsupported subtraction mode: %x", mode);
+        }
+        psImage *copy = psImageCopy(NULL, target->image, PS_TYPE_F32); // Copy of the image
+        targets->data[i] = psKernelAllocFromImage(copy, size + footprint, size + footprint);
+        psFree(copy);                   // Drop reference
+    }
+
+    // Generate the convolutions, accumulate sums, and measure initial chi^2
+    double sum1 = 0.0;                  // sum of 1/sigma(x,y)^2
+    psVector *sumC = psVectorAlloc(numKernels, PS_TYPE_F64); // sum of R(x)*k(u)/sigma(x)^2
+    psVector *sumCC = psVectorAlloc(numKernels, PS_TYPE_F64); // sum of [R(x)*k(u)]^2/sigma(x)^2
+    psVectorInit(sumC, 0.0);
+    psVectorInit(sumCC, 0.0);
+    double lastChi2 = 0.0;              // Chi^2 from last iteration
+    int numPixels = 0;                  // Number of pixels contributing to chi^2
+    for (int i = 0; i < numStamps; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+        if (badStamps->data.U8[i]) {
+            continue;
+        }
+        if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to convolve stamp %d.", i);
+            psFree(targets);
+            psFree(kernels);
+            psFree(badStamps);
+            return NULL;
+        }
+
+        // This sum is invariant to the kernel
+        psKernel *weight = stamp->weight; // Weight map for stamp
+        for (int v = -footprint; v <= footprint; v++) {
+            psF32 *wt = &weight->kernel[v][-footprint]; // Dereference weight map
+            for (int u = -footprint; u <= footprint; u++, wt++) {
+                sum1 += 1.0 / *wt;
+            }
+        }
+        if (!isfinite(sum1)) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    "Sum of 1/sigma^2 is non-finite for stamp %d (%d,%d)\n",
+                    i, (int)stamp->x, (int)stamp->y);
+            psFree(targets);
+            psFree(kernels);
+            psFree(badStamps);
+            return NULL;
+        }
+
+        for (int j = 0; j < numKernels; j++) {
+            accumulateConvolutions(&sumC->data.F64[j], &sumCC->data.F64[j], stamp, j, footprint, mode);
+        }
+
+        lastChi2 += initialChi2(targets->data[i], stamp, footprint, mode);
+        numPixels += PS_SQR(2 * footprint + 1);
+    }
+    lastChi2 /= numPixels;
+
+    // Rank the kernel components
+    psVector *ranking = psVectorAlloc(numKernels, PS_TYPE_S32); // Ranking of the kernel components
+    psVectorInit(ranking, -1);
+    int cutIndex = -1;                  // Index at which to cut off kernels
+    for (int iter = 0; iter < numKernels; iter++) {
+        int bestIndex = -1;             // Index of best kernel component
+        double bestChi2 = INFINITY;     // Value of best chi^2
+        double bestCoeff = 0;           // Value of best coefficient
+        double bestBG = 0;              // Value of best background
+
+        for (int i = 0; i < numKernels; i++) {
+            if (ranking->data.S32[i] >= 0) {
+                continue;
+            }
+
+            double sumI = 0.0;          // sum of I(x)/sigma(x)^2
+            double sumII = 0.0;         // sum of I(x)^2/sigma(x)^2
+            double sumIC = 0.0;         // sum of I(x)C(x)/sigma(x)^2
+
+            for (int j = 0; j < numStamps; j++) {
+                if (badStamps->data.U8[j]) {
+                    continue;
+                }
+                pmSubtractionStamp *stamp = stamps->stamps->data[j]; // Stamp of interest
+                accumulateCross(&sumI, &sumII, &sumIC, stamp, targets->data[j], i, footprint, mode);
+            }
+
+            double invDet = 1.0 / (sum1 * sumCC->data.F64[i] - PS_SQR(sumC->data.F64[i])); // Determinant^-1
+            double coeff = invDet * (sum1 * sumIC - sumC->data.F64[i] * sumI); // Coefficient for kernel
+            double bg = invDet * (sumCC->data.F64[i] * sumI - sumC->data.F64[i] * sumIC); // Background
+
+            double chi2 = 0.0;          // Chi^2
+            for (int j = 0; j < numStamps; j++) {
+                if (badStamps->data.U8[j]) {
+                    continue;
+                }
+                pmSubtractionStamp *stamp = stamps->stamps->data[j]; // Stamp of interest
+                chi2 += accumulateChi2(targets->data[j], stamp, i, coeff, bg, footprint, mode);
+            }
+
+            if (chi2 < bestChi2) {
+                bestIndex = i;
+                bestCoeff = coeff;
+                bestChi2 = chi2;
+                bestBG = bg;
+            }
+
+            psTrace("psModules.imcombine", 8, "%d: %lf %lf %lf %lf %lf %lf\n", i, sum1, sumI, sumII, sumIC,
+                    sumC->data.F64[i], sumCC->data.F64[i]);
+            psTrace("psModules.imcombine", 6, "%d: %lf %lf %lf\n", i, coeff, bg, chi2);
+        }
+        bestChi2 /= numPixels;
+
+        if (bestIndex == -1) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to find best kernel component in round %d.", iter);
+            psFree(targets);
+            psFree(sumC);
+            psFree(sumCC);
+            psFree(ranking);
+            psFree(kernels);
+            psFree(badStamps);
+            return NULL;
+        }
+
+        // And the winner is....
+        ranking->data.S32[bestIndex] = iter;
+        // Remove its contribution, and don't include it in the future.
+        for (int j = 0; j < numStamps; j++) {
+            if (badStamps->data.U8[j]) {
+                continue;
+            }
+            pmSubtractionStamp *stamp = stamps->stamps->data[j]; // Stamp of interest
+            subtractConvolution(targets->data[j], stamp, bestIndex, bestCoeff, bestBG, footprint, mode);
+        }
+
+        double diff = lastChi2 - bestChi2; // Difference in chi^2 between iterations
+
+        psTrace("psModules.imcombine", 3, "The winner of round %d is %d (%f,%d,%d): %lf (%lf) --> %lf\n",
+                iter, bestIndex, kernels->widths->data.F32[bestIndex], kernels->u->data.S32[bestIndex],
+                kernels->v->data.S32[bestIndex], bestCoeff, diff, bestChi2);
+
+        if (fabsf(diff) < tolerance) {
+            cutIndex = iter;
+            break;
+        }
+
+        lastChi2 = bestChi2;
+    }
+    psFree(targets);
+    psFree(sumC);
+    psFree(sumCC);
+
+    if (cutIndex < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to converge to tolerance %g\n", tolerance);
+        psFree(ranking);
+        psFree(kernels);
+        psFree(badStamps);
+        return NULL;
+    }
+
+    int newSize = cutIndex + 1;         // Size of new kernel basis set
+    psTrace("psModules.imcombine", 2, "Accepting %d kernels.\n", newSize);
+    psVector *uNew = psVectorAlloc(newSize, PS_TYPE_S32);
+    psVector *vNew = psVectorAlloc(newSize, PS_TYPE_S32);
+    psVector *widthsNew = psVectorAlloc(newSize, PS_TYPE_F32);
+    psArray *preCalcNew = psArrayAlloc(newSize);
+    psArray *convNew = psArrayAlloc(numStamps);
+    for (int i = 0; i < numStamps; i++) {
+        if (badStamps->data.U8[i]) {
+            continue;
+        }
+        convNew->data[i] = psArrayAlloc(newSize);
+    }
+
+    for (int i = 0; i < numKernels; i++) {
+        int rank = ranking->data.S32[i]; // This kernel component's ranking
+        if (rank >= 0 && rank < newSize) {
+            uNew->data.S32[rank] = kernels->u->data.S32[i];
+            vNew->data.S32[rank] = kernels->v->data.S32[i];
+            widthsNew->data.F32[rank] = kernels->widths->data.F32[i];
+            preCalcNew->data[rank] = psMemIncrRefCounter(kernels->preCalc->data[i]);
+
+            for (int j = 0; j < numStamps; j++) {
+                if (badStamps->data.U8[j]) {
+                    continue;
+                }
+                pmSubtractionStamp *stamp = stamps->stamps->data[j]; // Stamp of interest
+                psArray *convolutions = convNew->data[j]; // Convolutions for this stamp
+                convolutions->data[rank] = psMemIncrRefCounter(selectConvolution(stamp, i, mode));
+            }
+        }
+    }
+    psFree(kernels->u);
+    psFree(kernels->v);
+    psFree(kernels->widths);
+    psFree(kernels->preCalc);
+    kernels->u = uNew;
+    kernels->v = vNew;
+    kernels->widths = widthsNew;
+    kernels->preCalc = preCalcNew;
+    kernels->num = newSize;
+
+    for (int i = 0; i < numStamps; i++) {
+        if (badStamps->data.U8[i]) {
+            continue;
+        }
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+        psFree(stamp->convolutions1);
+        psFree(stamp->convolutions2);
+        switch (mode) {
+          case PM_SUBTRACTION_MODE_1:
+            stamp->convolutions1 = convNew->data[i];
+            stamp->convolutions2 = NULL;
+            break;
+          case PM_SUBTRACTION_MODE_2:
+            stamp->convolutions1 = NULL;
+            stamp->convolutions2 = convNew->data[i];
+            break;
+          default:
+            psAbort("Unsupported subtraction mode: %x", mode);
+        }
+    }
+
+    psFree(badStamps);
+    psFree(ranking);
+
+    // Maintain photometric scaling
+    if (type == PM_SUBTRACTION_KERNEL_ISIS) {
+        psKernel *subtract = kernels->preCalc->data[0]; // Kernel to subtract from the rest
+        for (int i = 1; i < newSize; i++) {
+            if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
+                psKernel *kernel = kernels->preCalc->data[i]; // Kernel of interest
+                psBinaryOp(kernel->image, kernel->image, "-", subtract->image);
+            }
+        }
+    } else if (type == PM_SUBTRACTION_KERNEL_GUNK) {
+        psStringPrepend(&kernels->description, "GUNK=");
+        psStringAppend(&kernels->description, "+POIS(%d,%d)", inner, spatialOrder);
+
+        for (int i = 0; i < newSize; i++) {
+            if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
+                psKernel *kernel = kernels->preCalc->data[i]; // Kernel of interest
+                kernel->kernel[0][0] -= 1.0;
+            }
+        }
+
+        if (!p_pmSubtractionKernelsAddGrid(kernels, numGaussians, inner)) {
+            psAbort("Should never get here.");
+        }
+
+        kernels->type = PM_SUBTRACTION_KERNEL_GUNK;
+    }
+
+    return kernels;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionParams.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionParams.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionParams.h	(revision 22322)
@@ -0,0 +1,22 @@
+#ifndef PM_SUBTRACTION_PARAMS_H
+#define PM_SUBTRACTION_PARAMS_H
+
+#include <pslib.h>
+#include <pmSubtractionKernels.h>
+#include <pmSubtractionStamps.h>
+
+/// Generate a set of optimum kernels for ISIS (or GUNK)
+pmSubtractionKernels *pmSubtractionKernelsOptimumISIS(pmSubtractionKernelsType type, ///< Kernel type
+                                                      int size, ///< Half-size of kernel
+                                                      int inner, ///< Inner radius for GUNK
+                                                      int spatialOrder, ///< Spatial polynomial order
+                                                      const psVector *fwhms, ///< Gaussian FWHMs to try
+                                                      int maxOrder, ///< Maximum polynomial order
+                                                      const pmSubtractionStampList *stamps, ///< Stamps
+                                                      int footprint, ///< Convolution footprint for stamps
+                                                      float tolerance, ///< Maximum difference in chi^2
+                                                      float penalty, ///< Penalty for wideness
+                                                      pmSubtractionMode mode // Mode for subtraction
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionStamps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionStamps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionStamps.c	(revision 22322)
@@ -0,0 +1,655 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+// All these includes required to get stamps out of an array of pmSources
+#include "pmMoments.h"
+#include "pmPeaks.h"
+#include "pmResiduals.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+
+
+#include "pmSubtraction.h"
+#include "pmSubtractionStamps.h"
+
+#define STAMP_LIST_BUFFER 20            // Number of stamps to add to list at a time
+
+#define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_SATSTAR | PM_SOURCE_MODE_BLEND | \
+                     PM_SOURCE_MODE_BADPSF | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | \
+                     PM_SOURCE_MODE_CR_LIMIT) // Mask for bad sources
+#define SOURCE_FAINTEST 50.0            // Faintest magnitude to consider
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Private (file-static) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Free function for pmSubtractionStampList
+static void subtractionStampListFree(pmSubtractionStampList *list // Stamp list to free
+                                     )
+{
+    psFree(list->stamps);
+    psFree(list->regions);
+    psFree(list->x);
+    psFree(list->y);
+    psFree(list->flux);
+}
+
+// Free function for pmSubtractionStamp
+static void subtractionStampFree(pmSubtractionStamp *stamp // Stamp to free
+                                 )
+{
+    psFree(stamp->image1);
+    psFree(stamp->image2);
+    psFree(stamp->weight);
+    psFree(stamp->convolutions1);
+    psFree(stamp->convolutions2);
+
+    psFree(stamp->matrix1);
+    psFree(stamp->matrix2);
+    psFree(stamp->matrixX);
+    psFree(stamp->vector1);
+    psFree(stamp->vector2);
+
+}
+
+// Is this region OK?
+static bool checkStampRegion(int x, int y, // Coordinates of stamp
+                             const psRegion *region // Region of interest
+                             )
+{
+    if (!region) {
+        return true;
+    }
+    return (x < region->x0 || x > region->x1 || y < region->y0 || y > region->y1) ?
+        false : true;
+}
+
+// Is this position unmasked?
+static bool checkStampMask(int x, int y, // Coordinates of stamp
+                           const psImage *mask, // Mask
+                           pmSubtractionMode mode, // Mode for subtraction
+                           int footprint // Footprint to check for Bad Stuff
+                           )
+{
+    if (!mask) {
+        return true;
+    }
+
+    bool clean = true;                  // Is the footprint clean?
+    int numCols = mask->numCols, numRows = mask->numRows; // Size of image
+
+    // Check the footprint bounds
+    if (x < footprint || x >= numCols - footprint || y < footprint || y >= numRows - footprint) {
+        clean = false;
+    }
+
+    // Determine mask value
+    psMaskType maskVal = PM_SUBTRACTION_MASK_BORDER | PM_SUBTRACTION_MASK_BAD_1 | PM_SUBTRACTION_MASK_BAD_2;
+    switch (mode) {
+      case PM_SUBTRACTION_MODE_1:
+        maskVal |= PM_SUBTRACTION_MASK_CONVOLVE_1;
+        break;
+      case PM_SUBTRACTION_MODE_2:
+        maskVal |= PM_SUBTRACTION_MASK_CONVOLVE_2;
+        break;
+      case PM_SUBTRACTION_MODE_UNSURE:
+      case PM_SUBTRACTION_MODE_DUAL:
+        maskVal |= PM_SUBTRACTION_MASK_CONVOLVE_1 | PM_SUBTRACTION_MASK_CONVOLVE_2;
+        break;
+      default:
+        psAbort("Unsupported subtraction mode: %x", mode);
+    }
+
+    // Check the immediate pixel
+    if (clean && (mask->data.PS_TYPE_MASK_DATA[y][x] & (maskVal | PM_SUBTRACTION_MASK_REJ))) {
+        clean = false;
+    }
+
+    int xMin = PS_MAX(x - footprint, 0), xMax = PS_MIN(x + footprint, numCols - 1); // Bounds in x
+    int yMin = PS_MAX(y - footprint, 0), yMax = PS_MIN(y + footprint, numRows - 1); // Bounds in y
+
+    // Check the footprint
+    if (clean) {
+        for (int j = yMin; j <= yMax; j++) {
+            for (int i = xMin; i <= xMax; i++) {
+                if (mask->data.PS_TYPE_MASK_DATA[j][i] & maskVal) {
+                    clean = false;
+                    goto CHECK_STAMP_MASK_DONE;
+                }
+            }
+        }
+    }
+CHECK_STAMP_MASK_DONE:
+
+    if (!clean) {
+        // Mask the footprint, so we don't select something near it again
+        for (int j = yMin; j <= yMax; j++) {
+            for (int i = xMin; i <= xMax; i++) {
+                mask->data.PS_TYPE_MASK_DATA[j][i] |= PM_SUBTRACTION_MASK_REJ;
+            }
+        }
+    }
+
+    return clean;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+pmSubtractionStampList *pmSubtractionStampListAlloc(int numCols, int numRows, const psRegion *region,
+                                                    int footprint, float spacing)
+{
+    pmSubtractionStampList *list = psAlloc(sizeof(pmSubtractionStampList)); // Stamp list to return
+    psMemSetDeallocator(list, (psFreeFunc)subtractionStampListFree);
+
+    // Get region in which to find stamps: [xMin:xMax,yMin:yMax]
+    int xMin = 0, xMax = numCols, yMin = 0, yMax = numRows;
+    if (region) {
+        xMin = PS_MAX(region->x0, xMin);
+        xMax = PS_MIN(region->x1, xMax);
+        yMin = PS_MAX(region->y0, yMin);
+        yMax = PS_MIN(region->y1, yMax);
+    }
+    int xSize = xMax - xMin, ySize = yMax - yMin; // Size of region of interest
+    int xStamps = (float)xSize / spacing + 1, yStamps = (float)ySize / spacing + 1; // Number of stamps
+
+    list->num = xStamps * yStamps;
+    list->stamps = psArrayAlloc(list->num);
+    list->regions = psArrayAlloc(list->num);
+
+    for (int y = 0, index = 0; y < yStamps; y++) {
+        int yStart = yMin + y * ((float)ySize / (float)(yStamps)); // Subregion starts here
+        int yStop = yMin + (y + 1) * ((float)ySize / (float)(yStamps)) - 1; // Subregion stops here
+        assert(yStart >= yMin && yStop < yMax);
+
+        for (int x = 0; x < xStamps; x++, index++) {
+            int xStart = xMin + x * ((float)xSize / (float)(xStamps)); // Subregion starts here
+            int xStop = xMin + (x + 1) * ((float)xSize / (float)(xStamps)) - 1; // Subregion stops here
+            assert(xStart >= xMin && xStop < xMax);
+
+            list->stamps->data[index] = pmSubtractionStampAlloc();
+            psTrace("psModules.imcombine", 6, "Stamp region %d: [%d:%d,%d:%d]\n",
+                    index, xStart, xStop, yStart, yStop);
+            list->regions->data[index] = psRegionAlloc(xStart, xStop, yStart, yStop);
+        }
+    }
+
+    list->x = NULL;
+    list->y = NULL;
+    list->flux = NULL;
+    list->footprint = footprint;
+
+    return list;
+}
+
+pmSubtractionStamp *pmSubtractionStampAlloc(void)
+{
+    pmSubtractionStamp *stamp = psAlloc(sizeof(pmSubtractionStamp)); // Stamp to return
+    psMemSetDeallocator(stamp, (psFreeFunc)subtractionStampFree);
+
+    stamp->x = NAN;
+    stamp->y = NAN;
+    stamp->flux = NAN;
+    stamp->xNorm = NAN;
+    stamp->yNorm = NAN;
+    stamp->status = PM_SUBTRACTION_STAMP_INIT;
+
+    stamp->image1 = NULL;
+    stamp->image2 = NULL;
+    stamp->weight = NULL;
+    stamp->convolutions1 = NULL;
+    stamp->convolutions2 = NULL;
+
+    stamp->matrix1 = NULL;
+    stamp->matrix2 = NULL;
+    stamp->matrixX = NULL;
+    stamp->vector1 = NULL;
+    stamp->vector2 = NULL;
+
+    return stamp;
+}
+
+
+pmSubtractionStampList *pmSubtractionStampsFind(pmSubtractionStampList *stamps, const psImage *image,
+                                                const psImage *subMask, const psRegion *region,
+                                                float threshold, int footprint, float spacing,
+                                                pmSubtractionMode mode)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+    if (subMask) {
+        PS_ASSERT_IMAGE_NON_NULL(subMask, NULL);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(image, subMask, NULL);
+        PS_ASSERT_IMAGE_TYPE(subMask, PS_TYPE_MASK, NULL);
+    }
+    PS_ASSERT_INT_NONNEGATIVE(footprint, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN(spacing, 0.0, NULL);
+    if (region) {
+        if (psRegionIsNaN(*region)) {
+            psString string = psRegionToString(*region);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Input region (%s) contains NAN values", string);
+            psFree(string);
+            return false;
+        }
+        if (region->x0 < 0 || region->x1 > image->numCols ||
+            region->y0 < 0 || region->y1 > image->numRows) {
+            psString string = psRegionToString(*region);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Input region (%s) does not fit in image (%dx%d)",
+                    string, image->numCols, image->numRows);
+            psFree(string);
+            return false;
+        }
+    }
+
+    int numRows = image->numRows, numCols = image->numCols; // Size of image
+
+    if (!stamps) {
+        stamps = pmSubtractionStampListAlloc(numCols, numRows, region, footprint, spacing);
+    }
+
+    int numStamps = stamps->num;        // Number of stamp regions
+    int numFound = 0;                   // Number of stamps found
+    int numValid = 0;                   // Number of valid regions
+    for (int i = 0; i < numStamps; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+
+        // Only find a new stamp if we need to
+        if (stamp->status != PM_SUBTRACTION_STAMP_REJECTED && stamp->status != PM_SUBTRACTION_STAMP_INIT) {
+            continue;
+        }
+        numValid++;
+
+        float xStamp = 0, yStamp = 0;   // Coordinates of stamp
+        float fluxStamp = NAN;          // Flux of stamp
+        bool goodStamp = false;         // Found a good stamp?
+
+        // A couple different ways of finding stamps:
+        if (stamps->x && stamps->y) {
+            // Get the next stamp from the list
+            psVector *xList = stamps->x->data[i], *yList = stamps->y->data[i]; // Coordinate lists
+            psVector *fluxList = stamps->flux->data[i]; // List of stamp fluxes
+
+            // Take stamp off the top of the (sorted) list
+            if (xList->n > 0) {
+                int index = xList->n - 1; // Index of new stamp
+                xStamp = xList->data.F32[index];
+                yStamp = yList->data.F32[index];
+                fluxStamp = fluxList->data.F32[index];
+
+                // Chop off the top of the list
+                xList->n = index;
+                yList->n = index;
+                fluxList->n = index;
+
+                goodStamp = true;
+            }
+        } else {
+            // Use a simple method of automatically finding stamps --- take the highest pixel in the subregion
+            fluxStamp = threshold;
+            psRegion *subRegion = stamps->regions->data[i]; // Sub-region of interest
+            for (int y = subRegion->y0; y <= subRegion->y1; y++) {
+                for (int x = subRegion->x0; x <= subRegion->x1; x++) {
+                    if (checkStampMask(x, y, subMask, mode, footprint) && image->data.F32[y][x] > fluxStamp) {
+                        fluxStamp = image->data.F32[y][x];
+                        xStamp = x;
+                        yStamp = y;
+                        goodStamp = true;
+                    }
+                }
+            }
+        }
+
+        if (goodStamp) {
+            stamp->x = xStamp;
+            stamp->y = yStamp;
+            stamp->flux = fluxStamp;
+
+            // Reset the postage stamps since we're making a new stamp
+            psFree(stamp->image1);
+            psFree(stamp->image2);
+            psFree(stamp->weight);
+            psFree(stamp->convolutions1);
+            psFree(stamp->convolutions2);
+            stamp->image1 = stamp->image2 = stamp->weight = NULL;
+            stamp->convolutions1 = stamp->convolutions2 = NULL;
+
+            stamp->status = PM_SUBTRACTION_STAMP_FOUND;
+            numFound++;
+            psTrace("psModules.imcombine", 5, "Found stamp in subregion %d: %d,%d\n",
+                    i, (int)stamp->x, (int)stamp->y);
+        } else {
+            stamp->status = PM_SUBTRACTION_STAMP_NONE;
+        }
+    }
+
+    if (numValid > 0) {
+        psLogMsg("psModules.imcombine", PS_LOG_INFO, "Found %d stamps", numFound);
+    }
+
+    return stamps;
+}
+
+
+pmSubtractionStampList *pmSubtractionStampsSet(const psVector *x, const psVector *y, const psVector *flux,
+                                               const psImage *image, const psImage *subMask,
+                                               const psRegion *region, int footprint, float spacing,
+                                               pmSubtractionMode mode)
+
+{
+    PS_ASSERT_VECTOR_NON_NULL(x, NULL);
+    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(y, NULL);
+    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(y, x, NULL);
+    if (flux) {
+        PS_ASSERT_VECTOR_NON_NULL(flux, NULL);
+        PS_ASSERT_VECTOR_TYPE(flux, PS_TYPE_F32, NULL);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(flux, x, NULL);
+    } else {
+        PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    }
+    if (subMask) {
+        PS_ASSERT_IMAGE_NON_NULL(subMask, NULL);
+        PS_ASSERT_IMAGE_TYPE(subMask, PS_TYPE_MASK, NULL);
+        if (image) {
+            PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+            PS_ASSERT_IMAGES_SIZE_EQUAL(image, subMask, NULL);
+        }
+    }
+    PS_ASSERT_FLOAT_LARGER_THAN(spacing, 0.0, NULL);
+
+    int numStars = x->n;                // Number of stars
+    pmSubtractionStampList *stamps = pmSubtractionStampListAlloc(subMask->numCols, subMask->numRows,
+                                                                 region, footprint, spacing); // Stamp list
+    int numStamps = stamps->num;        // Number of stamps
+
+    // Initialise the lists
+    stamps->x = psArrayAlloc(numStamps);
+    stamps->y = psArrayAlloc(numStamps);
+    stamps->flux = psArrayAlloc(numStamps);
+    for (int i = 0; i < numStamps; i++) {
+        stamps->x->data[i] = psVectorAllocEmpty(STAMP_LIST_BUFFER, PS_TYPE_F32);
+        stamps->y->data[i] = psVectorAllocEmpty(STAMP_LIST_BUFFER, PS_TYPE_F32);
+        stamps->flux->data[i] = psVectorAllocEmpty(STAMP_LIST_BUFFER, PS_TYPE_F32);
+    }
+
+    // Put the stars into their appropriate subregions
+    for (int i = 0; i < numStars; i++) {
+        float xStamp = x->data.F32[i], yStamp = y->data.F32[i]; // Coordinates of stamp
+        int xPix = xStamp + 0.5, yPix = yStamp + 0.5; // Pixel coordinate of stamp
+        if (!checkStampRegion(xPix, yPix, region)) {
+            // It's not in the big region
+            psTrace("psModules.imcombine", 9, "Rejecting input stamp (%d,%d) because outside region",
+                    xPix, yPix);
+            continue;
+        }
+        if (!checkStampMask(xPix, yPix, subMask, mode, footprint)) {
+            // Not a good stamp
+            psTrace("psModules.imcombine", 9, "Rejecting input stamp (%d,%d) because bad mask",
+                    xPix, yPix);
+            continue;
+        }
+
+        bool found = false;
+        for (int j = 0; j < numStamps && !found; j++) {
+            psRegion *subRegion = stamps->regions->data[j]; // Subregion of interest
+            if (checkStampRegion(xPix, yPix, subRegion)) {
+                psVector *xList = stamps->x->data[j], *yList = stamps->y->data[j]; // Pixel lists
+                psVector *fluxList = stamps->flux->data[j]; // Flux list
+
+                int index = xList->n;   // Index of new stamp candidate
+
+                psVectorExtend(xList, STAMP_LIST_BUFFER, 1);
+                psVectorExtend(yList, STAMP_LIST_BUFFER, 1);
+                psVectorExtend(fluxList, STAMP_LIST_BUFFER, 1);
+
+                xList->data.F32[index] = xStamp;
+                yList->data.F32[index] = yStamp;
+
+                if (flux) {
+                    fluxList->data.F32[index] = flux->data.F32[i];
+                } else {
+                    fluxList->data.F32[index] = image->data.F32[yPix][xPix];
+                }
+
+                found = true;
+                psTrace("psModules.imcombine", 9, "Putting input stamp (%d,%d) into subregion %d",
+                        xPix, yPix, j);
+            }
+        }
+
+        if (!found) {
+            psTrace("psModules.imcombine", 9, "Unable to find subregion for stamp (%d,%d)",
+                    xPix, yPix);
+        }
+    }
+
+    // Sort the list by flux, with the brightest last
+    for (int i = 0; i < numStamps; i++) {
+        psVector *xList = stamps->x->data[i], *yList = stamps->y->data[i]; // Pixel lists
+        psVector *fluxList = stamps->flux->data[i]; // Flux list
+
+        psVector *indexes = psVectorSortIndex(NULL, fluxList); // Indices to sort flux
+        int num = indexes->n;           // Number of candidate stamps in this subregion
+
+        psVector *xSorted = psVectorAlloc(num, PS_TYPE_F32); // Sorted version of x list
+        psVector *ySorted = psVectorAlloc(num, PS_TYPE_F32); // Sorted version of y list
+        psVector *fluxSorted = psVectorAlloc(num, PS_TYPE_F32); // Sorted version of flux list
+        for (int j = 0; j < num; j++) {
+            int k = indexes->data.S32[j]; // Sorted index
+            xSorted->data.F32[j] = xList->data.F32[k];
+            ySorted->data.F32[j] = yList->data.F32[k];
+            fluxSorted->data.F32[j] = fluxList->data.F32[k];
+        }
+        psFree(indexes);
+
+        psFree(stamps->x->data[i]);
+        psFree(stamps->y->data[i]);
+        psFree(stamps->flux->data[i]);
+
+        stamps->x->data[i] = xSorted;
+        stamps->y->data[i] = ySorted;
+        stamps->flux->data[i] = fluxSorted;
+    }
+
+
+    return stamps;
+}
+
+
+bool pmSubtractionStampsExtract(pmSubtractionStampList *stamps, psImage *image1, psImage *image2,
+                                psImage *weight, int kernelSize)
+{
+    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
+    PS_ASSERT_IMAGE_NON_NULL(image1, false);
+    PS_ASSERT_IMAGE_TYPE(image1, PS_TYPE_F32, false);
+    if (image2) {
+        PS_ASSERT_IMAGE_NON_NULL(image2, false);
+        PS_ASSERT_IMAGES_SIZE_EQUAL(image2, image1, false);
+        PS_ASSERT_IMAGE_TYPE(image2, PS_TYPE_F32, false);
+    }
+    PS_ASSERT_IMAGE_NON_NULL(weight, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(weight, image1, false);
+    PS_ASSERT_IMAGE_TYPE(weight, PS_TYPE_F32, false);
+    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
+
+    int numCols = image1->numCols, numRows = image1->numRows; // Size of images
+    int size = kernelSize + stamps->footprint; // Size of postage stamps
+
+    for (int i = 0; i < stamps->num; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+        if (!stamp || stamp->status != PM_SUBTRACTION_STAMP_FOUND) {
+            continue;
+        }
+
+        if (isnan(stamp->xNorm)) {
+            stamp->xNorm = 2.0 * (stamp->x - (float)numCols/2.0) / (float)numCols;
+        }
+        if (isnan(stamp->yNorm)) {
+            stamp->yNorm = 2.0 * (stamp->y - (float)numRows/2.0) / (float)numRows;
+        }
+
+        int x = stamp->x + 0.5, y = stamp->y + 0.5; // Stamp coordinates
+        if (x < size || x > numCols - size || y < size || y > numRows - size) {
+            psError(PS_ERR_UNKNOWN, false, "Stamp %d (%d,%d) is within the image border.\n", i, x, y);
+            return false;
+        }
+
+        // Catch memory leaks --- these should have been freed and NULLed before
+        assert(stamp->image1 == NULL);
+        assert(stamp->image2 == NULL);
+        assert(stamp->weight == NULL);
+
+        psRegion region = psRegionSet(x - size, x + size + 1, y - size, y + size + 1); // Region of interest
+
+        psImage *sub1 = psImageSubset(image1, region); // Subimage with stamp
+        stamp->image1 = psKernelAllocFromImage(sub1, size, size);
+        psFree(sub1);                   // Drop reference
+
+        if (image2) {
+            psImage *sub2 = psImageSubset(image2, region); // Subimage with stamp
+            stamp->image2 = psKernelAllocFromImage(sub2, size, size);
+            psFree(sub2);               // Drop reference
+        }
+
+        psImage *wtSub = psImageSubset(weight, region); // Subimage with stamp
+        stamp->weight = psKernelAllocFromImage(wtSub, size, size);
+        psFree(wtSub);                  // Drop reference
+
+        stamp->status = PM_SUBTRACTION_STAMP_CALCULATE;
+    }
+
+    return true;
+}
+
+#if 0
+bool pmSubtractionStampsGenerate(pmSubtractionStampList *stamps, float fwhm, int kernelSize)
+{
+    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
+    PS_ASSERT_FLOAT_LARGER_THAN(fwhm, 0.0, false);
+    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
+
+    int size = kernelSize + stamps->footprint; // Size of postage stamps
+    int num = stamps->num;              // Number of stamps
+    float sigma = fwhm / (2.0 * sqrtf(2.0 * log(2.0))); // Gaussian sigma
+
+    for (int i = 0; i < num; i++) {
+        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
+        if (!(stamp->status & PM_SUBTRACTION_STAMP_CALCULATE)) {
+            continue;
+        }
+
+        float x = stamp->x, y = stamp->y; // Coordinates of stamp
+        float flux = stamp->flux; // Flux of star
+        if (!isfinite(flux)) {
+            psWarning("Unable to generate PSF for stamp %d --- bad flux.", i);
+            stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
+            continue;
+        }
+
+        float xStamp = x - (int)(x + 0.5); // x coordinate of star in stamp frame
+        float yStamp = y - (int)(y + 0.5); // y coordinate of star in stamp frame
+
+        psFree(stamp->image2);
+        stamp->image2 = psKernelAlloc(-size, size, -size, size);
+        psKernel *target = stamp->image2; // Target stamp
+
+        // Put in a Waussian, just for fun!
+        for (int v = -size; v <= size; v++) {
+            for (int u = -size; u <= size; u++) {
+                float z = (PS_SQR(u + xStamp) + PS_SQR(v + yStamp)) / (2.0 * PS_SQR(sigma));
+                target->kernel[v][u] = flux / sigma * 0.5 * M_2_SQRTPI * M_SQRT1_2 / (1.0 + z + PS_SQR(z));
+            }
+        }
+
+    }
+
+    return true;
+}
+#endif
+
+pmSubtractionStampList *pmSubtractionStampsSetFromSources(const psArray *sources, const psImage *subMask,
+                                                          const psRegion *region, int footprint,
+                                                          float spacing, pmSubtractionMode mode)
+{
+    PS_ASSERT_ARRAY_NON_NULL(sources, NULL);
+    // Let pmSubtractionStampsSet take care of the rest of the assertions
+
+    int numIn = sources->n;             // Number of sources in input list
+
+    psVector *x = psVectorAllocEmpty(numIn, PS_TYPE_F32); // x coordinates
+    psVector *y = psVectorAllocEmpty(numIn, PS_TYPE_F32); // y coordinates
+    psVector *flux = psVectorAllocEmpty(numIn, PS_TYPE_F32); // Fluxes
+
+    int numOut = 0;                     // Number of sources in output list
+    for (int i = 0; i < numIn; i++) {
+        pmSource *source = sources->data[i]; // Source of interest
+        if (!source || (source->mode & SOURCE_MASK) || !isfinite(source->psfMag) ||
+            source->psfMag > SOURCE_FAINTEST) {
+            continue;
+        }
+        if (source->modelPSF) {
+            x->data.F32[numOut] = source->modelPSF->params->data.F32[PM_PAR_XPOS];
+            y->data.F32[numOut] = source->modelPSF->params->data.F32[PM_PAR_YPOS];
+        } else {
+            x->data.F32[numOut] = source->peak->xf;
+            y->data.F32[numOut] = source->peak->yf;
+        }
+        flux->data.F32[numOut] = powf(10.0, -0.4 * source->psfMag);
+        numOut++;
+    }
+    x->n = numOut;
+    y->n = numOut;
+    flux->n = numOut;
+
+    pmSubtractionStampList *stamps = pmSubtractionStampsSet(x, y, flux, NULL, subMask, region,
+                                                            footprint, spacing, mode); // Stamps to return
+    psFree(x);
+    psFree(y);
+    psFree(flux);
+
+    if (!stamps) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to set stamps from sources.");
+    }
+
+    return stamps;
+}
+
+
+pmSubtractionStampList *pmSubtractionStampsSetFromFile(const char *filename, const psImage *image,
+                                                       const psImage *subMask, const psRegion *region,
+                                                       int footprint, float spacing, pmSubtractionMode mode)
+{
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    // Let pmSubtractionStampsSet take care of the rest of the assertions
+
+    psArray *data = psVectorsReadFromFile(filename, "%f %f");
+    if (!data) {
+        psError(PS_ERR_IO, false, "Unable to read stamps file %s", filename);
+        return NULL;
+    }
+    psVector *x = data->data[0], *y = data->data[1]; // Stamp positions
+
+    // Correct for IRAF/FITS (unit-offset) positions to C (zero-offset) positions
+    psBinaryOp(x, x, "-", psScalarAlloc(1.0, PS_TYPE_F32));
+    psBinaryOp(y, y, "-", psScalarAlloc(1.0, PS_TYPE_F32));
+
+    pmSubtractionStampList *stamps = pmSubtractionStampsSet(x, y, NULL, image, subMask, region, footprint,
+                                                            spacing, mode);
+    psFree(data);
+
+    return stamps;
+
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionStamps.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionStamps.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionStamps.h	(revision 22322)
@@ -0,0 +1,126 @@
+#ifndef PM_SUBTRACTION_STAMPS_H
+#define PM_SUBTRACTION_STAMPS_H
+
+#include <pslib.h>
+
+#include "pmSubtractionKernels.h"
+
+/// Status of stamp
+typedef enum {
+    PM_SUBTRACTION_STAMP_INIT,          ///< Initial state
+    PM_SUBTRACTION_STAMP_FOUND,         ///< Found a suitable source for this stamp
+    PM_SUBTRACTION_STAMP_CALCULATE,     ///< Calculate matrix and vector values for this stamp
+    PM_SUBTRACTION_STAMP_USED,          ///< Use this stamp
+    PM_SUBTRACTION_STAMP_REJECTED,      ///< This stamp has been rejected
+    PM_SUBTRACTION_STAMP_NONE           ///< No stamp in this region
+} pmSubtractionStampStatus;
+
+/// A list of stamps
+typedef struct {
+    long num;                           ///< Number of stamps
+    psArray *stamps;                    ///< The stamps
+    psArray *regions;                   ///< Regions for each stamp
+    psArray *x, *y;                     ///< Coordinates for possible stamps (or NULL)
+    psArray *flux;                      ///< Fluxes for possible stamps (or NULL)
+    int footprint;                      ///< Half-size of stamps
+} pmSubtractionStampList;
+
+/// Allocate a list of stamps
+pmSubtractionStampList *pmSubtractionStampListAlloc(int numCols, // Number of columns in image
+                                                    int numRows, // Number of rows in image
+                                                    const psRegion *region, // Region for stamps, or NULL
+                                                    int footprint, // Half-size of stamps
+                                                    float spacing // Rough average spacing between stamps
+    );
+
+/// Assertion for stamp list to be valid
+#define PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(LIST, RETURNVALUE) { \
+    PS_ASSERT_PTR_NON_NULL(LIST, RETURNVALUE); \
+    PS_ASSERT_ARRAY_NON_NULL((LIST)->stamps, RETURNVALUE); \
+    PS_ASSERT_ARRAY_NON_NULL((LIST)->regions, RETURNVALUE); \
+    PS_ASSERT_INT_POSITIVE((LIST)->num, RETURNVALUE); \
+    PS_ASSERT_ARRAY_SIZE((LIST)->stamps, (LIST)->num, RETURNVALUE); \
+    PS_ASSERT_ARRAY_SIZE((LIST)->regions, (LIST)->num, RETURNVALUE); \
+    PS_ASSERT_INT_NONNEGATIVE((LIST)->footprint, RETURNVALUE); \
+    if ((LIST)->x || (LIST)->y || (LIST)->flux) { \
+        PS_ASSERT_ARRAY_NON_NULL((LIST)->x, RETURNVALUE); \
+        PS_ASSERT_ARRAY_NON_NULL((LIST)->y, RETURNVALUE); \
+        PS_ASSERT_ARRAY_NON_NULL((LIST)->flux, RETURNVALUE); \
+        PS_ASSERT_ARRAY_SIZE((LIST)->x, (LIST)->num, RETURNVALUE); \
+        PS_ASSERT_ARRAY_SIZE((LIST)->y, (LIST)->num, RETURNVALUE); \
+        PS_ASSERT_ARRAY_SIZE((LIST)->flux, (LIST)->num, RETURNVALUE); \
+    } \
+}
+
+/// A stamp for image subtraction
+typedef struct {
+    float x, y;                         ///< Position
+    float flux;                         ///< Flux
+    float xNorm, yNorm;                 ///< Normalised position
+    psKernel *image1;                   ///< Reference image postage stamp
+    psKernel *image2;                   ///< Input image postage stamp
+    psKernel *weight;                   ///< Weight image postage stamp, or NULL
+    psArray *convolutions1;             ///< Convolutions of image 1 for each kernel component, or NULL
+    psArray *convolutions2;             ///< Convolutions of image 2 for each kernel component, or NULL
+    psImage *matrix1, *matrix2;         ///< Least-squares matrices for each image, or NULL
+    psImage *matrixX;                   ///< Cross-matrix (for mode DUAL), or NULL
+    psVector *vector1, *vector2;        ///< Least-squares vectors for each image, or NULL
+    pmSubtractionStampStatus status;    ///< Status of stamp
+} pmSubtractionStamp;
+
+/// Allocate a stamp
+pmSubtractionStamp *pmSubtractionStampAlloc(void);
+
+/// Find stamps on an image
+pmSubtractionStampList *pmSubtractionStampsFind(pmSubtractionStampList *stamps, ///< Output stamps, or NULL
+                                                const psImage *image, ///< Image for which to find stamps
+                                                const psImage *mask, ///< Mask, or NULL
+                                                const psRegion *region, ///< Region to search, or NULL
+                                                float threshold, ///< Threshold for stamps in the image
+                                                int footprint, ///< Half-size for stamps
+                                                float spacing, ///< Rough spacing for stamps
+                                                pmSubtractionMode mode ///< Mode for subtraction
+    );
+
+/// Set stamps based on a list of x,y
+pmSubtractionStampList *pmSubtractionStampsSet(const psVector *x, ///< x coordinates for each stamp
+                                               const psVector *y, ///< y coordinates for each stamp
+                                               const psVector *flux, ///< Flux for each stamp, or NULL
+                                               const psImage *image, ///< Image for flux of stamp
+                                               const psImage *mask, ///< Mask, or NULL
+                                               const psRegion *region, ///< Region to search, or NULL
+                                               int footprint, ///< Half-size for stamps
+                                               float spacing, ///< Rough spacing for stamps
+                                               pmSubtractionMode mode ///< Mode for subtraction
+    );
+
+/// Set stamps based on a list of sources
+pmSubtractionStampList *pmSubtractionStampsSetFromSources(
+    const psArray *sources,             ///< Sources for each stamp
+    const psImage *subMask,             ///< Mask, or NULL
+    const psRegion *region,             ///< Region to search, or NULL
+    int footprint,                      ///< Half-size for stamps
+    float spacing,                      ///< Rough spacing for stamps
+    pmSubtractionMode mode              ///< Mode for subtraction
+    );
+
+/// Set stamps based on values in a file
+pmSubtractionStampList *pmSubtractionStampsSetFromFile(
+    const char *filename,               ///< Filename of file containing x,y (or x,y,flux) on each line
+    const psImage *image,               ///< Image for flux of stamp
+    const psImage *subMask,             ///< Mask, or NULL
+    const psRegion *region,             ///< Region to search, or NULL
+    int footprint,                      ///< Half-size for stamps
+    float spacing,                      ///< Rough spacing for stamps
+    pmSubtractionMode mode              ///< Mode for subtraction
+    );
+
+/// Extract stamps from the images
+bool pmSubtractionStampsExtract(pmSubtractionStampList *stamps, ///< Stamps
+                                psImage *image1, ///< Reference image
+                                psImage *image2, ///< Input image (or NULL)
+                                psImage *weight, ///< Weight (variance) map
+                                int kernelSize ///< Kernel half-size
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionThreads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionThreads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionThreads.c	(revision 22322)
@@ -0,0 +1,102 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmSubtractionMatch.h"
+#include "pmSubtractionEquation.h"
+#include "pmSubtraction.h"
+
+bool threaded = false;                  // Run with threads?
+
+bool pmSubtractionThreaded(void)
+{
+    return threaded;
+}
+
+// Initialise a mutex in each of the input
+static void subtractionMutexInit(pmReadout *ro)
+{
+    if (!ro) {
+        return;
+    }
+
+    if (ro->image) {
+        psMutexInit(ro->image);
+    }
+    if (ro->weight) {
+        psMutexInit(ro->weight);
+    }
+
+    return;
+}
+
+static void subtractionMutexDestroy(pmReadout *ro)
+{
+    if (!ro) {
+        return;
+    }
+
+    if (ro->image) {
+        psMutexDestroy(ro->image);
+    }
+    if (ro->weight) {
+        psMutexDestroy(ro->weight);
+    }
+
+    return;
+}
+
+void pmSubtractionThreadsInit(pmReadout *in1, pmReadout *in2)
+{
+    if (threaded) {
+        psAbort("Already running threaded.");
+    }
+
+    threaded = true;
+
+    subtractionMutexInit(in1);
+    subtractionMutexInit(in2);
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PSMODULES_SUBTRACTION_ORDER", 8);
+        task->function = &pmSubtractionOrderThread;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION", 3);
+        task->function = &pmSubtractionCalculateEquationThread;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    {
+        psThreadTask *task = psThreadTaskAlloc("PSMODULES_SUBTRACTION_CONVOLVE", 19);
+        task->function = &pmSubtractionConvolveThread;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
+
+    return;
+}
+
+
+void pmSubtractionThreadsFinalize(pmReadout *in1, pmReadout *in2)
+{
+    if (!threaded) {
+        return;
+    }
+
+    threaded = false;
+    psThreadTaskRemove("PSMODULES_SUBTRACTION_ORDER");
+    psThreadTaskRemove("PSMODULES_SUBTRACTION_CALCULATE_EQUATION");
+    psThreadTaskRemove("PSMODULES_SUBTRACTION_CONVOLVE");
+
+    subtractionMutexDestroy(in1);
+    subtractionMutexDestroy(in1);
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionThreads.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionThreads.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/imcombine/pmSubtractionThreads.h	(revision 22322)
@@ -0,0 +1,19 @@
+#ifndef PM_SUBTRACTION_THREADS_H
+#define PM_SUBTRACTION_THREADS_H
+
+/// Return whether theads have been activated
+bool pmSubtractionThreaded(void);
+
+/// Set up threading for image matching
+///
+/// Sets up thread tasks, and initialises mutexes in readouts
+void pmSubtractionThreadsInit(pmReadout *in1, pmReadout *in2 // Input readouts
+    );
+
+
+/// Take down threading for image matching
+///
+/// Destroys thread tasks, and initialises mutexes in readouts
+void pmSubtractionThreadsFinalize(pmReadout *in1, pmReadout *in2);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/mainpage.dox
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/mainpage.dox	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/mainpage.dox	(revision 22322)
@@ -0,0 +1,127 @@
+/** @mainpage psModules Image Processing Library
+
+
+@section intro Introduction
+This library contains the Pan-STARRS Image Processing Pipeline (IPP) modules (psModules). These modules
+use the functionality of the Pan-STARRS Library (psLib) to perform more complex tasks associated with image
+processing. Modules were constructed to support each of the required processing stages and are listed according
+to the particular processing stage for which they will primarily be used. To preserve namespace, globally-visible
+structures and functions are prefixed with pm, an abbreviation for Pan-STARRS Modules.
+
+The capabilities provided by psModules are grouped into the following areas which are
+also reflected in the file system directory structure:
+ - Configuration
+ - Astrometry, Focal Plane
+ - Photometry
+ - Basic Image Detrending
+ - Object Detection and Classification
+ - Image Combination
+ - Image Subtraction
+
+The installed code for psModules consists of header files and a binary library.
+
+@section extinstall Required external Libraries
+
+Before building psLib from source, several external software libraries must
+be installed. These include:
+ - Pan-STARRS Library (psLib)
+     - Available from the Maui High Performance Computiong Center (MHPCC) at https://mhpcc.pan-starrs.org/code/releases
+     - Compatibility tested with rel5
+     - Note, psLib itself has many external library dependencies.
+ - Doxygen Documentation System
+     - Only needed if code documentation is to be made
+     - Available at http://www.doxygen.org
+     - Compatibility tested with version 1.3.6
+     - The optional companion package, GraphViz (a.k.a., the 'dot' command), is recommended to enable header-file dependency diagrams
+
+We recommend using the particular versions listed as compatibility tested, as
+that is the only versions of the external libraries tested to work well with psLib
+and psModules.  Though it is quite possible that later versions of the libraries
+listed will also work, care must be taken when upgrading these libraries to verify
+that its functionality is compatible with the tested version.
+
+@section install How to Build from Source
+
+Tested versions of psModules are put into a tar file and can be downloaded from:
+
+https://mhpcc.pan-starrs.org/code/releases
+
+If one has a login account on mhpcc.pan-starrs.org, direct CVS access is
+possible.  Example of the commands required for direct CVS retrieval are
+as follows:
+<pre>
+$ cvs -d:ext:USERNAME@mhpcc.pan-starrs.org:/data/panstarrs/cvsroot co -r RELEASEBRANCH psModule
+</pre>
+where:
+  - USERNAME is your login name on the server
+  - RELEASEBRANCH is the desired release branch, e.g. rel7.
+
+
+@section build How to Build and Test the psModules Library.
+
+The psModules library and associated tests are made via the GNU autoconf/automake system.
+
+The source should build using the configure script in the psModules directory.  The
+recommended steps are:
+<pre>
+$ cd psmodules
+$ ./configure
+$ make
+$ make check
+$ make install
+</pre>
+<i>Unless otherwise specified, the library is installed with PREFIX of the current directory.</i>
+
+If the code was retrieved from CVS, you will need to substitute 'autogen.sh' for 'configure' in
+above example.
+
+Other configuration options, such as location of external libraries, are also available.
+To get a list of options, type the following in the top psModules directory.
+<pre>
+$ configure --help
+</pre>
+A likely option needed is '--with-pslib-config', which specifies the location of
+the configuration script for psLib.  By default, configure searches for it using PATH, but that
+is not always sufficient.
+
+@section install How to Install
+
+To install the library using the prefix given in the configure step, execute in
+the top build directory:
+<pre>
+$ make install
+</pre>
+
+
+@section usage Building and Linking your code to the psModules library
+
+To assist the use of the library with your own code, a configuration tool is part
+of the psModules library package.  This tool, psmodules-config, is installed in the BIN
+directory, according to the options given to the configure script.
+
+The required CFLAG options for the compiler stage of code that uses psModules can be
+obtained via 'psmodules-config --cflags'.  This outputs the cc options that supplies
+include path(s) required to find the psModules headers.
+
+The required linking options, can be obtained via 'psmodules-config --libs'.  This
+outputs the ld options that supplies the library paths and files required to
+link to the psModules library.
+
+Note: psmodules-config usage above refers to the install locations of the library.  
+
+@section doc How to Create Code Documentation
+
+Both HTML and man page documentation may be generated from the inline
+documentation embedded in the code using the following commands:
+<pre>
+$ cd psmodules
+$ make docs
+</pre>
+<i>This places documentation in PREFIX/docs.</i>
+
+Also, a prebuilt set of code documentation for both the releases and last
+CVS snapshot can be found at:
+
+https://mhpcc.pan-starrs.org/docs/
+
+*/
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/Makefile.am	(revision 22322)
@@ -0,0 +1,81 @@
+noinst_LTLIBRARIES = libpsmodulesobjects.la
+
+libpsmodulesobjects_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS) -I../pslib/
+libpsmodulesobjects_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulesobjects_la_SOURCES  = \
+     pmDetections.c \
+     pmSpan.c \
+     pmFootprint.c \
+     pmFootprintArrayGrow.c \
+     pmFootprintArraysMerge.c \
+     pmFootprintAssignPeaks.c \
+     pmFootprintFind.c \
+     pmFootprintFindAtPoint.c \
+     pmFootprintIDs.c \
+     pmPeaks.c \
+     pmMoments.c \
+     pmModel.c \
+     pmModelClass.c \
+     pmModelUtils.c \
+     pmSource.c \
+     pmSourceExtendedPars.c \
+     pmSourceUtils.c \
+     pmSourceSky.c \
+     pmSourceContour.c \
+     pmSourceFitModel.c \
+     pmSourceFitSet.c \
+     pmSourcePhotometry.c \
+     pmSourceIO.c \
+     pmSourceIO_RAW.c \
+     pmSourceIO_OBJ.c \
+     pmSourceIO_SX.c \
+     pmSourceIO_CMP.c \
+     pmSourceIO_SMPDATA.c \
+     pmSourceIO_PS1_DEV_0.c \
+     pmSourceIO_PS1_DEV_1.c \
+     pmSourcePlots.c \
+     pmSourcePlotPSFModel.c \
+     pmSourcePlotMoments.c \
+     pmSourcePlotApResid.c \
+     pmResiduals.c \
+     pmPSF.c \
+     pmPSF_IO.c \
+     pmPSFtry.c \
+     pmTrend2D.c \
+     pmGrowthCurve.c
+
+EXTRA_DIST = \
+     models/pmModel_GAUSS.c \
+     models/pmModel_PGAUSS.c \
+     models/pmModel_QGAUSS.c \
+     models/pmModel_SGAUSS.c \
+     models/pmModel_RGAUSS.c \
+     models/pmModel_SERSIC.c
+
+pkginclude_HEADERS = \
+     pmDetections.h \
+     pmSpan.h \
+     pmFootprint.h \
+     pmPeaks.h \
+     pmMoments.h \
+     pmModel.h \
+     pmModelClass.h \
+     pmModelUtils.h \
+     pmSource.h \
+     pmSourceExtendedPars.h \
+     pmSourceUtils.h \
+     pmSourceSky.h \
+     pmSourceContour.h \
+     pmSourceFitModel.h \
+     pmSourceFitSet.h \
+     pmSourcePhotometry.h \
+     pmSourceIO.h \
+     pmSourcePlots.h \
+     pmResiduals.h \
+     pmPSF.h \
+     pmPSF_IO.h \
+     pmPSFtry.h \
+     pmTrend2D.h \
+     pmGrowthCurve.h
+
+CLEANFILES = *~
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_GAUSS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_GAUSS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_GAUSS.c	(revision 22322)
@@ -0,0 +1,396 @@
+/******************************************************************************
+ * this file defines the GAUSS source shape model.  Note that these model functions are loaded
+ * by pmModelGroup.c using 'include', and thus need no 'include' statements of their own.  The
+ * models use a psVector to represent the set of parameters, with the sequence used to specify
+ * the meaning of the parameter.  The meaning of the parameters may thus vary depending on the
+ * specifics of the model.  All models which are used a PSF representations share a few
+ * parameters, for which # define names are listed in pmModel.h:
+
+   pure Gaussian:
+   exp(-z)
+
+ * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+ * PM_PAR_I0 1    - central intensity
+ * PM_PAR_XPOS 2  - X center of object
+ * PM_PAR_YPOS 3  - Y center of object
+ * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) * SigmaX)
+ * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) * SigmaY)
+ * PM_PAR_SXY 6   - X*Y term of elliptical contour
+ *****************************************************************************/
+
+# define PM_MODEL_FUNC            pmModelFunc_GAUSS
+# define PM_MODEL_FLUX            pmModelFlux_GAUSS
+# define PM_MODEL_GUESS           pmModelGuess_GAUSS
+# define PM_MODEL_LIMITS          pmModelLimits_GAUSS
+# define PM_MODEL_RADIUS          pmModelRadius_GAUSS
+# define PM_MODEL_FROM_PSF        pmModelFromPSF_GAUSS
+# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_GAUSS
+# define PM_MODEL_FIT_STATUS      pmModelFitStatus_GAUSS
+
+// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
+psF32 PM_MODEL_FUNC(psVector *deriv,
+                    const psVector *params,
+                    const psVector *pixcoord)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = pixcoord->data.F32[0] - PAR[PM_PAR_XPOS];
+    psF32 Y  = pixcoord->data.F32[1] - PAR[PM_PAR_YPOS];
+    psF32 px = X / PAR[PM_PAR_SXX];
+    psF32 py = Y / PAR[PM_PAR_SYY];
+    psF32 z  = PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y;
+    assert (z >= 0.0);
+
+    psF32 r  = exp(-z);
+    psF32 q  = PAR[PM_PAR_I0]*r;
+    psF32 f  = q + PAR[PM_PAR_SKY];
+
+    if (deriv != NULL) {
+        psF32 *dPAR = deriv->data.F32;
+        dPAR[PM_PAR_SKY]  = +1.0;
+        dPAR[PM_PAR_I0]   = +r;
+        dPAR[PM_PAR_XPOS] = q*(2*px/PAR[PM_PAR_SXX] + Y*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_YPOS] = q*(2*py/PAR[PM_PAR_SYY] + X*PAR[PM_PAR_SXY]);
+        // the extra factor of 2 below is needed to avoid excessive swings
+        dPAR[PM_PAR_SXX]  = +4.0*q*px*px/PAR[PM_PAR_SXX];
+        dPAR[PM_PAR_SYY]  = +4.0*q*py*py/PAR[PM_PAR_SYY];
+        dPAR[PM_PAR_SXY]  = -q*X*Y;
+    }
+    return(f);
+}
+
+// define the parameter limits
+// AR_MAX is the maximum allowed axis ratio
+// AR_RATIO is ((1-R)/(1+R))^2 where R = AR_MAX^(-2)
+# define AR_MAX 20.0
+# define AR_RATIO 0.99
+
+bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
+{
+    float beta_lim = 0, params_min = 0, params_max = 0;
+    float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
+
+    // we need to calculate the limits for SXY specially
+    if (nParam == PM_PAR_SXY) {
+        f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
+        q1 = PS_MAX (0.0, q1);
+        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
+        // angle and let f2,f1 fight it out
+        q2  = 0.5*sqrt (q1);
+    }
+
+    switch (mode) {
+    case PS_MINIMIZE_BETA_LIMIT:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            beta_lim = 1000;
+            break;
+        case PM_PAR_I0:
+            beta_lim = 3e6;
+            break;
+        case PM_PAR_XPOS:
+            beta_lim = 5;
+            break;
+        case PM_PAR_YPOS:
+            beta_lim = 5;
+            break;
+        case PM_PAR_SXX:
+            beta_lim = 2.0;
+            break;
+        case PM_PAR_SYY:
+            beta_lim = 2.0;
+            break;
+        case PM_PAR_SXY:
+            beta_lim =  0.5*q2;
+            break;
+        default:
+            psAbort("invalid parameter %d for beta test", nParam);
+        }
+        if (fabs(beta[nParam]) > fabs(beta_lim)) {
+            beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
+            psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
+                     nParam, beta[nParam], beta_lim);
+            return false;
+        }
+        return true;
+    case PS_MINIMIZE_PARAM_MIN:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            params_min = -1000;
+            break;
+        case PM_PAR_I0:
+            params_min =   0.01;
+            break;
+        case PM_PAR_XPOS:
+            params_min =  -100;
+            break;
+        case PM_PAR_YPOS:
+            params_min =  -100;
+            break;
+        case PM_PAR_SXX:
+            params_min =   0.5;
+            break;
+        case PM_PAR_SYY:
+            params_min =   0.5;
+            break;
+        case PM_PAR_SXY:
+            params_min =   -q2;
+            break;
+        default:
+            psAbort("invalid parameter %d for param min test", nParam);
+        }
+        if (params[nParam] < params_min) {
+            params[nParam] = params_min;
+            psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
+                     nParam, params[nParam], params_min);
+            return false;
+        }
+        return true;
+    case PS_MINIMIZE_PARAM_MAX:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            params_max =   1e5;
+            break;
+        case PM_PAR_I0:
+            params_max =   1e8;
+            break;
+        case PM_PAR_XPOS:
+            params_max =   1e4;
+            break;
+        case PM_PAR_YPOS:
+            params_max =   1e4;
+            break;
+        case PM_PAR_SXX:
+            params_max =   100;
+            break;
+        case PM_PAR_SYY:
+            params_max =   100;
+            break;
+        case PM_PAR_SXY:
+            params_max =   +q2;
+            break;
+        default:
+            psAbort("invalid parameter %d for param max test", nParam);
+        }
+        if (params[nParam] > params_max) {
+            params[nParam] = params_max;
+            psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
+                     nParam, params[nParam], params_max);
+            return false;
+        }
+        return true;
+    default:
+        psAbort("invalid choice for limits");
+    }
+    psAbort("should not reach here");
+    return false;
+}
+
+// make an initial guess for parameters
+bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
+{
+    pmMoments *moments = source->moments;
+    psF32     *PAR  = model->params->data.F32;
+
+    psEllipseMoments emoments;
+    emoments.x2 = moments->Sx;
+    emoments.y2 = moments->Sy;
+    emoments.xy = moments->Sxy;
+
+    // force the axis ratio to be < 20.0
+    psEllipseAxes axes = psEllipseMomentsToAxes (emoments, 20.0);
+    psEllipseShape shape = psEllipseAxesToShape (axes);
+
+    PAR[PM_PAR_SKY]  = moments->Sky;
+    PAR[PM_PAR_I0]   = moments->Peak - moments->Sky;
+    PAR[PM_PAR_XPOS] = moments->x;
+    PAR[PM_PAR_YPOS] = moments->y;
+    PAR[PM_PAR_SXX] = PS_MAX(0.5, M_SQRT2*shape.sx);
+    PAR[PM_PAR_SYY] = PS_MAX(0.5, M_SQRT2*shape.sy);
+    PAR[PM_PAR_SXY] = shape.sxy;
+    return(true);
+}
+
+psF64 PM_MODEL_FLUX (const psVector *params)
+{
+
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    // Area is equivalent to 2 pi sigma^2
+    // axes ratio < 20
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+    psF64 Flux = params->data.F32[PM_PAR_I0] * Area;
+
+    return(Flux);
+}
+
+// return the radius which yields the requested flux
+// this function is never allowed to return <= 0
+psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
+{
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[PM_PAR_I0] <= 0)
+        return (1.0);
+    if (flux >= PAR[PM_PAR_I0])
+        return (1.0);
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 radius = axes.major * sqrt (2.0 * log(PAR[PM_PAR_I0] / flux));
+    return (radius);
+}
+
+// construct the PSF model from the FLT model and the psf
+bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
+{
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    // supply the model-fitted parameters, or copy from the input
+    for (int i = 0; i < psf->params->n; i++) {
+        if (psf->params->data[i] == NULL) {
+            out[i] = in[i];
+        } else {
+            pmTrend2D *trend = psf->params->data[i];
+            out[i] = pmTrend2DEval(trend, in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+        }
+    }
+
+    // the OLD 2D model for SXY actually fitted SXY / (SXX^-2 + SYY^-2); correct here
+    // out[PM_PAR_SXY] = pmPSF_SXYtoModel (out);
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    // XXX user-defined value for limit?
+    if (!pmPSF_FitToModel (out, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)",
+                in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, out, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, out, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)",
+                     in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+            modelPSF->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+// construct the PSF model from the FLT model and the psf
+// XXX is this sufficiently general do be a global function, not a pmModelClass function?
+bool PM_MODEL_PARAMS_FROM_PSF (pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io)
+{
+    psF32 *PAR = model->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = Io;
+    PAR[PM_PAR_XPOS] = Xo;
+    PAR[PM_PAR_YPOS] = Yo;
+
+    // supply the model-fitted parameters, or copy from the input
+    for (int i = 0; i < psf->params->n; i++) {
+        if (i == PM_PAR_SKY) continue;
+        if (i == PM_PAR_I0) continue;
+        if (i == PM_PAR_XPOS) continue;
+        if (i == PM_PAR_YPOS) continue;
+        pmTrend2D *trend = psf->params->data[i];
+        PAR[i] = pmTrend2DEval(trend, Xo, Yo);
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    // XXX user-defined value for limit?
+    if (!pmPSF_FitToModel (PAR, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, PAR, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, PAR, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)", Xo, Yo);
+            model->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+// check the status of the fitted model
+// this test is invalid if the parameters are derived
+// from the PSF model
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
+    dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    if (status)
+        return true;
+    return false;
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_PARAMS_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_PGAUSS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 22322)
@@ -0,0 +1,443 @@
+/******************************************************************************
+ * this file defines the PGAUSS source shape model.  Note that these model functions are loaded
+ * by pmModelGroup.c using 'include', and thus need no 'include' statements of their own.  The
+ * models use a psVector to represent the set of parameters, with the sequence used to specify
+ * the meaning of the parameter.  The meaning of the parameters may thus vary depending on the
+ * specifics of the model.  All models which are used a PSF representations share a few
+ * parameters, for which # define names are listed in pmModel.h:
+
+   Gaussian taylor expansion
+   1 / (1 + z + z^2/2 + z^3/6)
+
+ * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+ * PM_PAR_I0 1    - central intensity
+ * PM_PAR_XPOS 2  - X center of object
+ * PM_PAR_YPOS 3  - Y center of object
+ * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) * SigmaX)
+ * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) * SigmaY)
+ * PM_PAR_SXY 6   - X*Y term of elliptical contour
+ *****************************************************************************/
+
+# define PM_MODEL_FUNC            pmModelFunc_PGAUSS
+# define PM_MODEL_FLUX            pmModelFlux_PGAUSS
+# define PM_MODEL_GUESS           pmModelGuess_PGAUSS
+# define PM_MODEL_LIMITS          pmModelLimits_PGAUSS
+# define PM_MODEL_RADIUS          pmModelRadius_PGAUSS
+# define PM_MODEL_FROM_PSF        pmModelFromPSF_PGAUSS
+# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_PGAUSS
+# define PM_MODEL_FIT_STATUS      pmModelFitStatus_PGAUSS
+
+// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
+psF32 PM_MODEL_FUNC(psVector *deriv,
+                    const psVector *params,
+                    const psVector *pixcoord)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = pixcoord->data.F32[0] - PAR[PM_PAR_XPOS];
+    psF32 Y  = pixcoord->data.F32[1] - PAR[PM_PAR_YPOS];
+    psF32 px = X / PAR[PM_PAR_SXX];
+    psF32 py = Y / PAR[PM_PAR_SYY];
+    psF32 z  = PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y;
+    assert (z >= 0.0);
+
+    psF32 t  = 1 + z + z*z/2.0;
+    psF32 r  = 1.0 / (t + z*z*z/6.0); /* exp (-Z) */
+    psF32 f  = PAR[PM_PAR_I0]*r + PAR[PM_PAR_SKY];
+
+    if (deriv != NULL) {
+        psF32 *dPAR = deriv->data.F32;
+        psF32 q = PAR[PM_PAR_I0]*r*r*t;
+        dPAR[PM_PAR_SKY] = +1.0;
+        dPAR[PM_PAR_I0] = +r;
+        dPAR[PM_PAR_XPOS] = q*(2.0*px/PAR[PM_PAR_SXX] + Y*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_YPOS] = q*(2.0*py/PAR[PM_PAR_SYY] + X*PAR[PM_PAR_SXY]);
+        // the extra factor of 2 below is needed to avoid excessive swings
+        dPAR[PM_PAR_SXX] =  +4.0*q*px*px/PAR[PM_PAR_SXX];
+        dPAR[PM_PAR_SYY] =  +4.0*q*py*py/PAR[PM_PAR_SYY];
+        dPAR[PM_PAR_SXY] = -q*X*Y;
+    }
+    return(f);
+}
+
+// define the parameter limits
+// AR_MAX is the maximum allowed axis ratio
+// AR_RATIO is ((1-R)/(1+R))^2 where R = AR_MAX^(-2)
+# define AR_MAX 20.0
+# define AR_RATIO 0.99
+
+bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
+{
+    float beta_lim = 0, params_min = 0, params_max = 0;
+    float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
+
+    // we need to calculate the limits for SXY specially
+    if (nParam == PM_PAR_SXY) {
+        f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
+        q1 = PS_MAX (0.0, q1);
+        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
+        // angle and let f2,f1 fight it out
+        q2  = 0.5*sqrt (q1);
+    }
+
+    switch (mode) {
+    case PS_MINIMIZE_BETA_LIMIT:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            beta_lim = 1000;
+            break;
+        case PM_PAR_I0:
+            beta_lim = 3e6;
+            break;
+        case PM_PAR_XPOS:
+            beta_lim = 5;
+            break;
+        case PM_PAR_YPOS:
+            beta_lim = 5;
+            break;
+        case PM_PAR_SXX:
+            beta_lim = 2.0;
+            break;
+        case PM_PAR_SYY:
+            beta_lim = 2.0;
+            break;
+        case PM_PAR_SXY:
+            beta_lim =  0.5*q2;
+            break;
+        default:
+            psAbort("invalid parameter %d for beta test", nParam);
+        }
+        if (fabs(beta[nParam]) > fabs(beta_lim)) {
+            beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
+            psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
+                     nParam, beta[nParam], beta_lim);
+            return false;
+        }
+        return true;
+    case PS_MINIMIZE_PARAM_MIN:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            params_min = -1000;
+            break;
+        case PM_PAR_I0:
+            params_min =  0.01;
+            break;
+        case PM_PAR_XPOS:
+            params_min =  -100;
+            break;
+        case PM_PAR_YPOS:
+            params_min =  -100;
+            break;
+        case PM_PAR_SXX:
+            params_min =   0.5;
+            break;
+        case PM_PAR_SYY:
+            params_min =   0.5;
+            break;
+        case PM_PAR_SXY:
+            params_min =   -q2;
+            break;
+        default:
+            psAbort("invalid parameter %d for param min test", nParam);
+        }
+        if (params[nParam] < params_min) {
+            params[nParam] = params_min;
+            psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
+                     nParam, params[nParam], params_min);
+            return false;
+        }
+        return true;
+    case PS_MINIMIZE_PARAM_MAX:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            params_max =   1e5;
+            break;
+        case PM_PAR_I0:
+            params_max =   1e8;
+            break;
+        case PM_PAR_XPOS:
+            params_max =   1e4;
+            break;
+        case PM_PAR_YPOS:
+            params_max =   1e4;
+            break;
+        case PM_PAR_SXX:
+            params_max =   100;
+            break;
+        case PM_PAR_SYY:
+            params_max =   100;
+            break;
+        case PM_PAR_SXY:
+            params_max =   +q2;
+            break;
+        default:
+            psAbort("invalid parameter %d for param max test", nParam);
+        }
+        if (params[nParam] > params_max) {
+            params[nParam] = params_max;
+            psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
+                     nParam, params[nParam], params_max);
+            return false;
+        }
+        return true;
+    default:
+        psAbort("invalid choice for limits");
+    }
+    psAbort("should not reach here");
+    return false;
+}
+
+// make an initial guess for parameters
+bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
+{
+    pmMoments *moments = source->moments;
+    psF32     *PAR     = model->params->data.F32;
+
+    psEllipseMoments emoments;
+    emoments.x2 = moments->Sx;
+    emoments.y2 = moments->Sx;
+    emoments.xy = moments->Sxy;
+
+    psEllipseAxes axes = psEllipseMomentsToAxes (emoments, 20.0);
+    psEllipseShape shape = psEllipseAxesToShape (axes);
+
+    PAR[PM_PAR_SKY]  = moments->Sky;
+    PAR[PM_PAR_I0]   = moments->Peak - moments->Sky;
+    PAR[PM_PAR_XPOS] = moments->x; // XXX use peak->xf, peak->yf?
+    PAR[PM_PAR_YPOS] = moments->y;
+    PAR[PM_PAR_SXX] = PS_MAX(0.5, M_SQRT2*shape.sx);
+    PAR[PM_PAR_SYY] = PS_MAX(0.5, M_SQRT2*shape.sy);
+    PAR[PM_PAR_SXY] = shape.sxy;
+    return(true);
+}
+
+psF64 PM_MODEL_FLUX(const psVector *params)
+{
+    float norm, z;
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    // Area is equivalent to 2 pi sigma^2
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+
+    # define DZ 0.25
+
+    float f0 = 1.0;
+    float f1, f2;
+    for (z = DZ; z < 50; z += DZ) {
+        f1 = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
+        z += DZ;
+        f2 = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
+        norm += f0 + 4*f1 + f2;
+        f0 = f2;
+    }
+    norm *= DZ / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
+{
+    psF64 z, f;
+    int Nstep = 0;
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[PM_PAR_I0] <= 0)
+        return (1.0);
+    if (flux >= PAR[PM_PAR_I0])
+        return (1.0);
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    // this estimates the radius assuming f(z) is roughly exp(-z)
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 sigma = axes.major;
+
+    psF64 limit = flux / PAR[PM_PAR_I0];
+
+    // use the fact that f is monotonically decreasing
+    z = 0;
+    Nstep = 0;
+
+    // choose a z value guaranteed to be beyond our limit
+    float z0 = pow((1.0 / limit), (1.0 / 3.0));
+    float z1 = (1.0 / limit);
+    z1 = PS_MAX (z0, z1);
+    z0 = 0.0;
+
+    // perform a type of bisection to find the value
+    float f0 = 1.0 / (1 + z0 + z0*z0/2.0 + z0*z0*z0/6.0);
+    float f1 = 1.0 / (1 + z1 + z1*z1/2.0 + z1*z1*z1/6.0);
+    while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
+        z = 0.5*(z0 + z1);
+        f = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
+        if (f > limit) {
+            z0 = z;
+            f0 = f;
+        } else {
+            z1 = z;
+            f1 = f;
+        }
+        Nstep ++;
+    }
+    psF64 radius = sigma * sqrt (2.0 * z);
+
+    // psF64 radius = axes.major * sqrt (2.0 * log(PAR[PM_PAR_I0] / flux));
+
+    if (isnan(radius))
+        psAbort("error in code: radius is NaN");
+    if (radius < 0)
+        psAbort("error in code: radius is negative");
+
+    return (radius);
+}
+
+bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
+{
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    for (int i = 0; i < psf->params->n; i++) {
+        if (psf->params->data[i] == NULL) {
+            out[i] = in[i];
+        } else {
+            pmTrend2D *trend = psf->params->data[i];
+            out[i] = pmTrend2DEval(trend, in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+        }
+    }
+
+    // the OLD 2D model for SXY actually fitted SXY / (SXX^-2 + SYY^-2); correct here
+    // out[PM_PAR_SXY] = pmPSF_SXYtoModel (out);
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    if (!pmPSF_FitToModel (out, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)",
+                in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, out, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, out, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)",
+                     in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+            modelPSF->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+// construct the PSF model from the FLT model and the psf
+// XXX is this sufficiently general do be a global function, not a pmModelClass function?
+bool PM_MODEL_PARAMS_FROM_PSF (pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io)
+{
+    psF32 *PAR = model->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = Io;
+    PAR[PM_PAR_XPOS] = Xo;
+    PAR[PM_PAR_YPOS] = Yo;
+
+    // supply the model-fitted parameters, or copy from the input
+    for (int i = 0; i < psf->params->n; i++) {
+        if (i == PM_PAR_SKY) continue;
+        if (i == PM_PAR_I0) continue;
+        if (i == PM_PAR_XPOS) continue;
+        if (i == PM_PAR_YPOS) continue;
+        pmTrend2D *trend = psf->params->data[i];
+        PAR[i] = pmTrend2DEval(trend, Xo, Yo);
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    // XXX user-defined value for limit?
+    if (!pmPSF_FitToModel (PAR, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, PAR, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, PAR, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)", Xo, Yo);
+            model->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
+    dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    return status;
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_PARAMS_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_QGAUSS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 22322)
@@ -0,0 +1,474 @@
+/******************************************************************************
+ * this file defines the QGAUSS source shape model (XXX need a better name!).  Note that these
+ * model functions are loaded by pmModelGroup.c using 'include', and thus need no 'include'
+ * statements of their own.  The models use a psVector to represent the set of parameters, with
+ * the sequence used to specify the meaning of the parameter.  The meaning of the parameters
+ * may thus vary depending on the specifics of the model.  All models which are used a PSF
+ * representations share a few parameters, for which # define names are listed in pmModel.h:
+
+   power-law with fitted linear term
+   1 / (1 + kz + z^2.25)
+
+   * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+   * PM_PAR_I0 1    - central intensity
+   * PM_PAR_XPOS 2  - X center of object
+   * PM_PAR_YPOS 3  - Y center of object
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXY 6   - X*Y term of elliptical contour
+   * PM_PAR_7   7   - amplitude of the linear component (k)
+   *****************************************************************************/
+
+# define PM_MODEL_FUNC            pmModelFunc_QGAUSS
+# define PM_MODEL_FLUX            pmModelFlux_QGAUSS
+# define PM_MODEL_GUESS           pmModelGuess_QGAUSS
+# define PM_MODEL_LIMITS          pmModelLimits_QGAUSS
+# define PM_MODEL_RADIUS          pmModelRadius_QGAUSS
+# define PM_MODEL_FROM_PSF        pmModelFromPSF_QGAUSS
+# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_QGAUSS
+# define PM_MODEL_FIT_STATUS      pmModelFitStatus_QGAUSS
+
+psF32 PM_MODEL_FUNC (psVector *deriv,
+                     const psVector *params,
+                     const psVector *pixcoord)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = pixcoord->data.F32[0] - PAR[PM_PAR_XPOS];
+    psF32 Y  = pixcoord->data.F32[1] - PAR[PM_PAR_YPOS];
+    psF32 px = X / PAR[PM_PAR_SXX];
+    psF32 py = Y / PAR[PM_PAR_SYY];
+    psF32 z  = PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y;
+
+    // XXX if the elliptical contour is defined in valid way, this step should not be required.
+    // other models (like PGAUSS) don't use fractional powers, and thus do not have NaN values
+    // for negative values of z
+    // XXX use an assert here to force the elliptical parameters to be correctly determined
+    // if (z < 0) z = 0;
+    assert (z >= 0);
+
+    psF32 zp = pow(z,1.25);
+    psF32 r  = 1.0 / (1 + PAR[PM_PAR_7]*z + z*zp);
+
+    psF32 r1 = PAR[PM_PAR_I0]*r;
+    psF32 f  = r1 + PAR[PM_PAR_SKY];
+
+    if (deriv != NULL) {
+        psF32 *dPAR = deriv->data.F32;
+
+        // note difference from a pure gaussian: q = params->data.F32[PM_PAR_I0]*r
+        psF32 t = r1*r;
+        psF32 q = t*(PAR[PM_PAR_7] + 2.25*zp);
+
+        dPAR[PM_PAR_SKY]  = +1.0;
+        dPAR[PM_PAR_I0]   = +r;
+        dPAR[PM_PAR_XPOS] = q*(2.0*px/PAR[PM_PAR_SXX] + Y*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_YPOS] = q*(2.0*py/PAR[PM_PAR_SYY] + X*PAR[PM_PAR_SXY]);
+        // the extra factor of 2 below is needed to avoid excessive swings
+        dPAR[PM_PAR_SXX]  = +4.0*q*px*px/PAR[PM_PAR_SXX];
+        dPAR[PM_PAR_SYY]  = +4.0*q*py*py/PAR[PM_PAR_SYY];
+        dPAR[PM_PAR_SXY]  = -q*X*Y;
+        dPAR[PM_PAR_7]    = -t*z;
+    }
+    return(f);
+}
+
+// define the parameter limits
+// AR_MAX is the maximum allowed axis ratio
+// AR_RATIO is ((1-R)/(1+R))^2 where R = AR_MAX^(-2)
+# define AR_MAX 20.0
+# define AR_RATIO 0.99
+bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
+{
+    float beta_lim = 0, params_min = 0, params_max = 0;
+    float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
+
+    // we need to calculate the limits for SXY specially
+    if (nParam == PM_PAR_SXY) {
+        f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
+        q1 = (q1 < 0.0) ? 0.0 : q1;
+        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
+        // angle and let f2,f1 fight it out
+        q2  = 0.5*sqrt (q1);
+    }
+
+    switch (mode) {
+    case PS_MINIMIZE_BETA_LIMIT:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            beta_lim = 1000;
+            break;
+        case PM_PAR_I0:
+            beta_lim = 3e6;
+            break;
+        case PM_PAR_XPOS:
+            beta_lim = 5;
+            break;
+        case PM_PAR_YPOS:
+            beta_lim = 5;
+            break;
+        case PM_PAR_SXX:
+            beta_lim = 1.0;
+            break;
+        case PM_PAR_SYY:
+            beta_lim = 1.0;
+            break;
+        case PM_PAR_SXY:
+            beta_lim =  0.5*q2;
+            break;
+        case PM_PAR_7:
+            beta_lim = 2.0;
+            break;
+        default:
+            psAbort("invalid parameter %d for beta test", nParam);
+        }
+        if (fabs(beta[nParam]) > fabs(beta_lim)) {
+            beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
+            psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
+                     nParam, beta[nParam], beta_lim);
+            return false;
+        }
+        return true;
+    case PS_MINIMIZE_PARAM_MIN:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            params_min = -1000;
+            break;
+        case PM_PAR_I0:
+            params_min =   0.01;
+            break;
+        case PM_PAR_XPOS:
+            params_min =  -100;
+            break;
+        case PM_PAR_YPOS:
+            params_min =  -100;
+            break;
+        case PM_PAR_SXX:
+            params_min =   0.5;
+            break;
+        case PM_PAR_SYY:
+            params_min =   0.5;
+            break;
+        case PM_PAR_SXY:
+            params_min =  -q2;
+            break;
+        case PM_PAR_7:
+            params_min =   0.1;
+            break;
+        default:
+            psAbort("invalid parameter %d for param min test", nParam);
+        }
+        if (params[nParam] < params_min) {
+            params[nParam] = params_min;
+            psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
+                     nParam, params[nParam], params_min);
+            return false;
+        }
+        return true;
+    case PS_MINIMIZE_PARAM_MAX:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            params_max =   1e5;
+            break;
+        case PM_PAR_I0:
+            params_max =   1e8;
+            break;
+        case PM_PAR_XPOS:
+            params_max =   1e4;
+            break;
+        case PM_PAR_YPOS:
+            params_max =   1e4;
+            break;
+        case PM_PAR_SXX:
+            params_max =   100;
+            break;
+        case PM_PAR_SYY:
+            params_max =   100;
+            break;
+        case PM_PAR_SXY:
+            params_max =  +q2;
+            break;
+        case PM_PAR_7:
+            params_max =  20.0;
+            break;
+        default:
+            psAbort("invalid parameter %d for param max test", nParam);
+        }
+        if (params[nParam] > params_max) {
+            params[nParam] = params_max;
+            psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
+                     nParam, params[nParam], params_max);
+            return false;
+        }
+        return true;
+    default:
+        psAbort("invalid choice for limits");
+    }
+    psAbort("should not reach here");
+    return false;
+}
+
+
+// make an initial guess for parameters
+bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
+{
+    pmMoments *moments = source->moments;
+    pmPeak    *peak    = source->peak;
+    psF32     *PAR  = model->params->data.F32;
+
+    psEllipseMoments emoments;
+    emoments.x2 = moments->Sx;
+    emoments.y2 = moments->Sy;
+    emoments.xy = moments->Sxy;
+
+    // force the axis ratio to be < 20.0
+    psEllipseAxes axes = psEllipseMomentsToAxes (emoments, 20.0);
+
+    if (!isfinite(axes.major)) return false;
+    if (!isfinite(axes.minor)) return false;
+    if (!isfinite(axes.theta)) return false;
+
+    psEllipseShape shape = psEllipseAxesToShape (axes);
+
+    if (!isfinite(shape.sx))  return false;
+    if (!isfinite(shape.sy))  return false;
+    if (!isfinite(shape.sxy)) return false;
+
+    // XXX turn this off here for now PAR[PM_PAR_SKY]  = moments->Sky;
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = moments->Peak;
+    PAR[PM_PAR_XPOS] = peak->x;
+    PAR[PM_PAR_YPOS] = peak->y;
+    PAR[PM_PAR_SXX]  = PS_MAX(0.5, M_SQRT2*shape.sx);
+    PAR[PM_PAR_SYY]  = PS_MAX(0.5, M_SQRT2*shape.sy);
+    PAR[PM_PAR_SXY]  = shape.sxy;
+    PAR[PM_PAR_7]    = 1.0;
+
+    return(true);
+}
+
+psF64 PM_MODEL_FLUX (const psVector *params)
+{
+    float z, norm;
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    // Area is equivalent to 2 pi sigma^2
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+
+    # define DZ 0.25
+
+    float f0 = 1.0;
+    float f1, f2;
+    for (z = DZ; z < 50; z += DZ) {
+        f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
+        z += DZ;
+        f2 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
+        norm += f0 + 4*f1 + f2;
+        f0 = f2;
+    }
+    norm *= DZ / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
+{
+    psF64 z, f;
+    int Nstep = 0;
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[PM_PAR_I0] <= 0)
+        return (1.0);
+    if (flux >= PAR[PM_PAR_I0])
+        return (1.0);
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 sigma = axes.major;
+
+    psF64 limit = flux / PAR[PM_PAR_I0];
+
+    // use the fact that f is monotonically decreasing
+    z = 0;
+    Nstep = 0;
+
+    // choose a z value guaranteed to be beyond our limit
+    float z0 = pow((1.0 / limit), (1.0 / 2.25));
+    float z1 = (1.0 / limit) / PAR[PM_PAR_7];
+    z1 = PS_MAX (z0, z1);
+    z0 = 0.0;
+
+    // perform a type of bisection to find the value
+    float f0 = 1.0 / (1 + PAR[PM_PAR_7]*z0 + pow(z0, 2.25));
+    float f1 = 1.0 / (1 + PAR[PM_PAR_7]*z1 + pow(z1, 2.25));
+    while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
+        z = 0.5*(z0 + z1);
+        f = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
+        if (f > limit) {
+            z0 = z;
+            f0 = f;
+        } else {
+            z1 = z;
+            f1 = f;
+        }
+        Nstep ++;
+    }
+    psF64 radius = sigma * sqrt (2.0 * z);
+
+    if (isnan(radius))
+        psAbort("error in code: radius is NaN");
+
+    return (radius);
+}
+
+bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    for (int i = 0; i < psf->params->n; i++) {
+        if (psf->params->data[i] == NULL) {
+            out[i] = in[i];
+        } else {
+            pmTrend2D *trend = psf->params->data[i];
+            out[i] = pmTrend2DEval(trend, in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+        }
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    if (!pmPSF_FitToModel (out, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)",
+                in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS(PS_MINIMIZE_PARAM_MIN, i, out, NULL);
+        status &= PM_MODEL_LIMITS(PS_MINIMIZE_PARAM_MAX, i, out, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)",
+                     in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+            modelPSF->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+
+    return true;
+}
+
+// construct the PSF model from the FLT model and the psf
+// XXX is this sufficiently general do be a global function, not a pmModelClass function?
+bool PM_MODEL_PARAMS_FROM_PSF (pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io)
+{
+    psF32 *PAR = model->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = Io;
+    PAR[PM_PAR_XPOS] = Xo;
+    PAR[PM_PAR_YPOS] = Yo;
+
+    // supply the model-fitted parameters, or copy from the input
+    for (int i = 0; i < psf->params->n; i++) {
+        if (i == PM_PAR_SKY) continue;
+        if (i == PM_PAR_I0) continue;
+        if (i == PM_PAR_XPOS) continue;
+        if (i == PM_PAR_YPOS) continue;
+        pmTrend2D *trend = psf->params->data[i];
+        PAR[i] = pmTrend2DEval(trend, Xo, Yo);
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    // XXX user-defined value for limit?
+    if (!pmPSF_FitToModel (PAR, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, PAR, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, PAR, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)", Xo, Yo);
+            model->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
+    dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
+    dP = sqrt (dP);
+
+    status = true;
+//    status &= (dP < 0.5);
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    return status;
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_PARAMS_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_RGAUSS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 22322)
@@ -0,0 +1,466 @@
+/******************************************************************************
+ * this file defines the RGAUSS source shape model (XXX need a better name!).  Note that these
+ * model functions are loaded by pmModelGroup.c using 'include', and thus need no 'include'
+ * statements of their own.  The models use a psVector to represent the set of parameters, with
+ * the sequence used to specify the meaning of the parameter.  The meaning of the parameters
+ * may thus vary depending on the specifics of the model.  All models which are used a PSF
+ * representations share a few parameters, for which # define names are listed in pmModel.h:
+
+   power-law with fitted slope
+   1 / (1 + z + z^alpha)
+
+ * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+ * PM_PAR_I0 1    - central intensity
+ * PM_PAR_XPOS 2  - X center of object
+ * PM_PAR_YPOS 3  - Y center of object
+ * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
+ * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+ * PM_PAR_SXY 6   - X*Y term of elliptical contour
+ * PM_PAR_7   7   - power-law slope (alpha)
+ *****************************************************************************/
+
+# define PM_MODEL_FUNC            pmModelFunc_RGAUSS
+# define PM_MODEL_FLUX            pmModelFlux_RGAUSS
+# define PM_MODEL_GUESS           pmModelGuess_RGAUSS
+# define PM_MODEL_LIMITS          pmModelLimits_RGAUSS
+# define PM_MODEL_RADIUS          pmModelRadius_RGAUSS
+# define PM_MODEL_FROM_PSF        pmModelFromPSF_RGAUSS
+# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_RGAUSS
+# define PM_MODEL_FIT_STATUS      pmModelFitStatus_RGAUSS
+
+psF32 PM_MODEL_FUNC (psVector *deriv,
+                     const psVector *params,
+                     const psVector *pixcoord)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = pixcoord->data.F32[0] - PAR[PM_PAR_XPOS];
+    psF32 Y  = pixcoord->data.F32[1] - PAR[PM_PAR_YPOS];
+    psF32 px = X / PAR[PM_PAR_SXX];
+    psF32 py = Y / PAR[PM_PAR_SYY];
+    psF32 z  = PS_SQR(px) + PS_SQR(py) + X*Y*PAR[PM_PAR_SXY];
+
+    assert (z >= 0);
+
+    psF32 p  = pow(z, PAR[PM_PAR_7] - 1.0);
+    psF32 r  = 1.0 / (1 + z + z*p);
+    psF32 f  = PAR[PM_PAR_I0]*r + PAR[PM_PAR_SKY];
+
+    if (deriv != NULL) {
+        psF32 *dPAR = deriv->data.F32;
+
+        // note difference from a pure gaussian: q = params->data.F32[PM_PAR_I0]*r
+        psF32 t = PAR[PM_PAR_I0]*r*r;
+        psF32 q = t*(1 + PAR[PM_PAR_7]*p);
+
+        dPAR[PM_PAR_SKY] = +1.0;
+        dPAR[PM_PAR_I0] = +r;
+        dPAR[PM_PAR_XPOS] = q*(2.0*px/PAR[PM_PAR_SXX] + Y*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_YPOS] = q*(2.0*py/PAR[PM_PAR_SYY] + X*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_SXX] = +2.0*q*px*px/PAR[PM_PAR_SXX];
+        dPAR[PM_PAR_SYY] = +2.0*q*py*py/PAR[PM_PAR_SYY];
+        dPAR[PM_PAR_SXY] = -q*X*Y;
+
+        // this model derivative is undefined at z = 0.0, but is actually 0.0
+        dPAR[PM_PAR_7] = (z == 0.0) ? 0.0 : -5.0*t*log(z)*p*z;
+    }
+    return(f);
+}
+
+// define the parameter limits
+// AR_MAX is the maximum allowed axis ratio
+// AR_RATIO is ((1-R)/(1+R))^2 where R = AR_MAX^(-2)
+# define AR_MAX 20.0
+# define AR_RATIO 0.99
+bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
+{
+    float beta_lim = 0, params_min = 0, params_max = 0;
+    float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
+
+    // we need to calculate the limits for SXY specially
+    if (nParam == PM_PAR_SXY) {
+        f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
+        q1 = (q1 < 0.0) ? 0.0 : q1;
+        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
+        // angle and let f2,f1 fight it out
+        q2  = 0.5*sqrt (q1);
+    }
+
+    switch (mode) {
+    case PS_MINIMIZE_BETA_LIMIT:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            beta_lim = 1000;
+            break;
+        case PM_PAR_I0:
+            beta_lim = 3e6;
+            break;
+        case PM_PAR_XPOS:
+            beta_lim = 5;
+            break;
+        case PM_PAR_YPOS:
+            beta_lim = 5;
+            break;
+        case PM_PAR_SXX:
+            beta_lim = 0.5;
+            break;
+        case PM_PAR_SYY:
+            beta_lim = 0.5;
+            break;
+        case PM_PAR_SXY:
+            beta_lim =  0.5*q2;
+            break;
+        case PM_PAR_7:
+            beta_lim = 0.5;
+            break;
+        default:
+            psAbort("invalid parameter %d for beta test", nParam);
+        }
+        if (fabs(beta[nParam]) > fabs(beta_lim)) {
+            beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
+            psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
+                     nParam, beta[nParam], beta_lim);
+            return false;
+        }
+        return true;
+    case PS_MINIMIZE_PARAM_MIN:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            params_min = -1000;
+            break;
+        case PM_PAR_I0:
+            params_min =   0.01;
+            break;
+        case PM_PAR_XPOS:
+            params_min =  -100;
+            break;
+        case PM_PAR_YPOS:
+            params_min =  -100;
+            break;
+        case PM_PAR_SXX:
+            params_min =   0.5;
+            break;
+        case PM_PAR_SYY:
+            params_min =   0.5;
+            break;
+        case PM_PAR_SXY:
+            params_min =  -q2;
+            break;
+        case PM_PAR_7:
+            params_min =   1.25;
+            break;
+        default:
+            psAbort("invalid parameter %d for param min test", nParam);
+        }
+        if (params[nParam] < params_min) {
+            params[nParam] = params_min;
+            psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
+                     nParam, params[nParam], params_min);
+            return false;
+        }
+        return true;
+    case PS_MINIMIZE_PARAM_MAX:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            params_max =   1e5;
+            break;
+        case PM_PAR_I0:
+            params_max =   1e8;
+            break;
+        case PM_PAR_XPOS:
+            params_max =   1e4;
+            break;
+        case PM_PAR_YPOS:
+            params_max =   1e4;
+            break;
+        case PM_PAR_SXX:
+            params_max =   100;
+            break;
+        case PM_PAR_SYY:
+            params_max =   100;
+            break;
+        case PM_PAR_SXY:
+            params_max =  +q2;
+            break;
+        case PM_PAR_7:
+            params_max =  4.0;
+            break;
+        default:
+            psAbort("invalid parameter %d for param max test", nParam);
+        }
+        if (params[nParam] > params_max) {
+            params[nParam] = params_max;
+            psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
+                     nParam, params[nParam], params_max);
+            return false;
+        }
+        return true;
+    default:
+        psAbort("invalid choice for limits");
+    }
+    psAbort("should not reach here");
+    return false;
+}
+
+// make an initial guess for parameters
+bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
+{
+    pmMoments *moments = source->moments;
+    pmPeak    *peak    = source->peak;
+    psF32     *PAR  = model->params->data.F32;
+
+    psEllipseMoments emoments;
+    emoments.x2 = moments->Sx;
+    emoments.y2 = moments->Sy;
+    emoments.xy = moments->Sxy;
+
+    // force the axis ratio to be < 20.0
+    psEllipseAxes axes = psEllipseMomentsToAxes (emoments, 20.0);
+
+    if (!isfinite(axes.major)) return false;
+    if (!isfinite(axes.minor)) return false;
+    if (!isfinite(axes.theta)) return false;
+
+    psEllipseShape shape = psEllipseAxesToShape (axes);
+
+    if (!isfinite(shape.sx))  return false;
+    if (!isfinite(shape.sy))  return false;
+    if (!isfinite(shape.sxy)) return false;
+
+    PAR[PM_PAR_SKY]  = moments->Sky;
+    PAR[PM_PAR_I0]   = moments->Peak - moments->Sky;
+    PAR[PM_PAR_XPOS] = peak->x;
+    PAR[PM_PAR_YPOS] = peak->y;
+    PAR[PM_PAR_SXX]  = PS_MAX(0.5, M_SQRT2*shape.sx);
+    PAR[PM_PAR_SYY]  = PS_MAX(0.5, M_SQRT2*shape.sy);
+    PAR[PM_PAR_SXY]  = shape.sxy;
+    PAR[PM_PAR_7]    = 2.25;
+
+    return(true);
+}
+
+psF64 PM_MODEL_FLUX (const psVector *params)
+{
+    float norm, z;
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    // Area is equivalent to 2 pi sigma^2
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+
+    # define DZ 0.25
+
+    float f0 = 1.0;
+    float f1, f2;
+    for (z = DZ; z < 50; z += DZ) {
+        f1 = 1.0 / (1 + z + pow(z, PAR[PM_PAR_7]));
+        z += DZ;
+        f2 = 1.0 / (1 + z + pow(z, PAR[PM_PAR_7]));
+        norm += f0 + 4*f1 + f2;
+        f0 = f2;
+    }
+    norm *= DZ / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
+{
+    psF64 z, f;
+    int Nstep = 0;
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[PM_PAR_I0] <= 0)
+        return (1.0);
+    if (flux >= PAR[PM_PAR_I0])
+        return (1.0);
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 sigma = axes.major;
+
+    psF64 limit = flux / PAR[PM_PAR_I0];
+
+    // use the fact that f is monotonically decreasing
+    z = 0;
+    Nstep = 0;
+
+    // choose a z value guaranteed to be beyond our limit
+    float z0 = pow((1.0 / limit), (1.0 / PAR[PM_PAR_7]));
+    float z1 = (1.0 / limit);
+    z1 = PS_MAX (z0, z1);
+    z0 = 0.0;
+
+    // perform a type of bisection to find the value
+    float f0 = 1.0 / (1 + z0 + pow(z0, PAR[PM_PAR_7]));
+    float f1 = 1.0 / (1 + z1 + pow(z1, PAR[PM_PAR_7]));
+    while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
+        z = 0.5*(z0 + z1);
+        f = 1.0 / (1 + z + pow(z, PAR[PM_PAR_7]));
+        if (f > limit) {
+            z0 = z;
+            f0 = f;
+        } else {
+            z1 = z;
+            f1 = f;
+        }
+        Nstep ++;
+    }
+    psF64 radius = sigma * sqrt (2.0 * z);
+
+    if (isnan(radius))
+        psAbort("error in code: radius is NaN");
+
+    return (radius);
+}
+
+bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    for (int i = 0; i < psf->params->n; i++) {
+        if (psf->params->data[i] == NULL) {
+            out[i] = in[i];
+        } else {
+            pmTrend2D *trend = psf->params->data[i];
+            out[i] = pmTrend2DEval(trend, in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+        }
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    if (!pmPSF_FitToModel (out, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)",
+                in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS(PS_MINIMIZE_PARAM_MIN, i, out, NULL);
+        status &= PM_MODEL_LIMITS(PS_MINIMIZE_PARAM_MAX, i, out, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)",
+                     in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+            modelPSF->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+
+    return true;
+}
+
+// construct the PSF model from the FLT model and the psf
+// XXX is this sufficiently general do be a global function, not a pmModelClass function?
+bool PM_MODEL_PARAMS_FROM_PSF (pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io)
+{
+    psF32 *PAR = model->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = Io;
+    PAR[PM_PAR_XPOS] = Xo;
+    PAR[PM_PAR_YPOS] = Yo;
+
+    // supply the model-fitted parameters, or copy from the input
+    for (int i = 0; i < psf->params->n; i++) {
+        if (i == PM_PAR_SKY) continue;
+        if (i == PM_PAR_I0) continue;
+        if (i == PM_PAR_XPOS) continue;
+        if (i == PM_PAR_YPOS) continue;
+        pmTrend2D *trend = psf->params->data[i];
+        PAR[i] = pmTrend2DEval(trend, Xo, Yo);
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    // XXX user-defined value for limit?
+    if (!pmPSF_FitToModel (PAR, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, PAR, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, PAR, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)", Xo, Yo);
+            model->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
+    dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    return status;
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_PARAMS_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_SERSIC.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_SERSIC.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_SERSIC.c	(revision 22322)
@@ -0,0 +1,462 @@
+/******************************************************************************
+ * this file defines the SERSIC source shape model.  Note that these model functions are loaded
+ * by pmModelGroup.c using 'include', and thus need no 'include' statements of their own.  The
+ * models use a psVector to represent the set of parameters, with the sequence used to specify
+ * the meaning of the parameter.  The meaning of the parameters may thus vary depending on the
+ * specifics of the model.  All models which are used a PSF representations share a few
+ * parameters, for which # define names are listed in pmModel.h:
+
+   f = exp(-z^n)
+
+   * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+   * PM_PAR_I0 1    - central intensity
+   * PM_PAR_XPOS 2  - X center of object
+   * PM_PAR_YPOS 3  - Y center of object
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXY 6   - X*Y term of elliptical contour
+   * PM_PAR_7   7   - normalized sersic parameter
+
+   note that a standard sersic model uses exp(-K*(z^(1/n) - 1).  the additional elements (K,
+   the -1 offset) are absorbed in this model by the normalization, the exponent, and the
+   radial scale.  We fit the elements in this form, then re-normalize them on output.
+   *****************************************************************************/
+
+# define PM_MODEL_FUNC            pmModelFunc_SERSIC
+# define PM_MODEL_FLUX            pmModelFlux_SERSIC
+# define PM_MODEL_GUESS           pmModelGuess_SERSIC
+# define PM_MODEL_LIMITS          pmModelLimits_SERSIC
+# define PM_MODEL_RADIUS          pmModelRadius_SERSIC
+# define PM_MODEL_FROM_PSF        pmModelFromPSF_SERSIC
+# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_SERSIC
+# define PM_MODEL_FIT_STATUS      pmModelFitStatus_SERSIC
+
+psF32 PM_MODEL_FUNC (psVector *deriv,
+                     const psVector *params,
+                     const psVector *pixcoord)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = pixcoord->data.F32[0] - PAR[PM_PAR_XPOS];
+    psF32 Y  = pixcoord->data.F32[1] - PAR[PM_PAR_YPOS];
+    psF32 px = X / PAR[PM_PAR_SXX];
+    psF32 py = Y / PAR[PM_PAR_SYY];
+    psF32 z  = PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y;
+
+    // XXX if the elliptical contour is defined in valid way, this step should not be required.
+    // other models (like PGAUSS) don't use fractional powers, and thus do not have NaN values
+    // for negative values of z
+    // XXX use an assert here to force the elliptical parameters to be correctly determined
+    // if (z < 0) z = 0;
+    assert (z >= 0);
+
+    psF32 f2 = pow(z,PAR[PM_PAR_7]);
+    psF32 f1 = exp(-f2);
+    psF32 z0 = PAR[PM_PAR_I0]*f1;
+    psF32 f0 = PAR[PM_PAR_SKY] + z0;
+
+    assert (isfinite(f2));
+    assert (isfinite(f1));
+    assert (isfinite(z0));
+    assert (isfinite(f0));
+
+    if (deriv != NULL) {
+        psF32 *dPAR = deriv->data.F32;
+
+        // gradient is infinite for z = 0; saturate at z = 0.01
+        psF32 z1 = (z < 0.01) ? z0*PAR[PM_PAR_7]*pow(0.01,PAR[PM_PAR_7] - 1.0) : z0*PAR[PM_PAR_7]*pow(z,PAR[PM_PAR_7] - 1.0);
+
+        dPAR[PM_PAR_SKY]  = +1.0;
+        dPAR[PM_PAR_I0]   = +f1;
+        dPAR[PM_PAR_7]    = (z == 0.0) ? 0.0 : -z0*f2*log(z);
+
+        assert (isfinite(z1));
+        assert (isfinite(dPAR[PM_PAR_7]));
+
+        dPAR[PM_PAR_XPOS] = +1.0*z1*(2.0*px/PAR[PM_PAR_SXX] + Y*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_YPOS] = +1.0*z1*(2.0*py/PAR[PM_PAR_SYY] + X*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_SXX]  = +2.0*z1*px*px/PAR[PM_PAR_SXX];
+        dPAR[PM_PAR_SYY]  = +2.0*z1*py*py/PAR[PM_PAR_SYY];
+        dPAR[PM_PAR_SXY]  = -1.0*z1*X*Y;
+        dPAR[PM_PAR_SXY]  = -1.0*z1*X*Y;
+    }
+    return (f0);
+}
+
+// define the parameter limits
+// AR_MAX is the maximum allowed axis ratio
+// AR_RATIO is ((1-R)/(1+R))^2 where R = AR_MAX^(-2)
+# define AR_MAX 20.0
+# define AR_RATIO 0.99
+bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
+{
+    float beta_lim = 0, params_min = 0, params_max = 0;
+    float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
+
+    // we need to calculate the limits for SXY specially
+    if (nParam == PM_PAR_SXY) {
+        f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
+        q1 = (q1 < 0.0) ? 0.0 : q1;
+        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
+        // angle and let f2,f1 fight it out
+        q2  = 0.5*sqrt (q1);
+    }
+
+    switch (mode) {
+    case PS_MINIMIZE_BETA_LIMIT:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            beta_lim = 1000;
+            break;
+        case PM_PAR_I0:
+            beta_lim = 3e6;
+            break;
+        case PM_PAR_XPOS:
+            beta_lim = 5;
+            break;
+        case PM_PAR_YPOS:
+            beta_lim = 5;
+            break;
+        case PM_PAR_SXX:
+            beta_lim = 1.0;
+            break;
+        case PM_PAR_SYY:
+            beta_lim = 1.0;
+            break;
+        case PM_PAR_SXY:
+            beta_lim =  0.5*q2;
+            break;
+        case PM_PAR_7:
+            beta_lim = 2.0;
+            break;
+        default:
+            psAbort("invalid parameter %d for beta test", nParam);
+        }
+        if (fabs(beta[nParam]) > fabs(beta_lim)) {
+            beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
+            psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
+                     nParam, beta[nParam], beta_lim);
+            return false;
+        }
+        return true;
+    case PS_MINIMIZE_PARAM_MIN:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            params_min = -1000;
+            break;
+        case PM_PAR_I0:
+            params_min =     0.01;
+            break;
+        case PM_PAR_XPOS:
+            params_min =  -100;
+            break;
+        case PM_PAR_YPOS:
+            params_min =  -100;
+            break;
+        case PM_PAR_SXX:
+            params_min =   0.05;
+            break;
+        case PM_PAR_SYY:
+            params_min =   0.05;
+            break;
+        case PM_PAR_SXY:
+            params_min =  -q2;
+            break;
+        case PM_PAR_7:
+            params_min =   0.05;
+            break;
+        default:
+            psAbort("invalid parameter %d for param min test", nParam);
+        }
+        if (params[nParam] < params_min) {
+            params[nParam] = params_min;
+            psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
+                     nParam, params[nParam], params_min);
+            return false;
+        }
+        return true;
+    case PS_MINIMIZE_PARAM_MAX:
+        switch (nParam) {
+        case PM_PAR_SKY:
+            params_max =   1e5;
+            break;
+        case PM_PAR_I0:
+            params_max =   1e8;
+            break;
+        case PM_PAR_XPOS:
+            params_max =   1e4;
+            break;
+        case PM_PAR_YPOS:
+            params_max =   1e4;
+            break;
+        case PM_PAR_SXX:
+            params_max =   100;
+            break;
+        case PM_PAR_SYY:
+            params_max =   100;
+            break;
+        case PM_PAR_SXY:
+            params_max =  +q2;
+            break;
+        case PM_PAR_7:
+            params_max =   4.0;
+            break;
+        default:
+            psAbort("invalid parameter %d for param max test", nParam);
+        }
+        if (params[nParam] > params_max) {
+            params[nParam] = params_max;
+            psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
+                     nParam, params[nParam], params_max);
+            return false;
+        }
+        return true;
+    default:
+        psAbort("invalid choice for limits");
+    }
+    psAbort("should not reach here");
+    return false;
+}
+
+
+// make an initial guess for parameters
+bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
+{
+    pmMoments *moments = source->moments;
+    pmPeak    *peak    = source->peak;
+    psF32     *PAR  = model->params->data.F32;
+
+    psEllipseMoments emoments;
+    emoments.x2 = moments->Sx;
+    emoments.y2 = moments->Sy;
+    emoments.xy = moments->Sxy;
+
+    // force the axis ratio to be < 20.0
+    psEllipseAxes axes = psEllipseMomentsToAxes (emoments, 20.0);
+
+    if (!isfinite(axes.major)) return false;
+    if (!isfinite(axes.minor)) return false;
+    if (!isfinite(axes.theta)) return false;
+
+    psEllipseShape shape = psEllipseAxesToShape (axes);
+
+    if (!isfinite(shape.sx))  return false;
+    if (!isfinite(shape.sy))  return false;
+    if (!isfinite(shape.sxy)) return false;
+
+    // XXX PAR[PM_PAR_SKY]  = moments->Sky;
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = moments->Peak;
+    PAR[PM_PAR_XPOS] = peak->x;
+    PAR[PM_PAR_YPOS] = peak->y;
+    PAR[PM_PAR_SXX]  = PS_MAX(0.5, M_SQRT2*shape.sx);
+    PAR[PM_PAR_SYY]  = PS_MAX(0.5, M_SQRT2*shape.sy);
+    PAR[PM_PAR_SXY]  = shape.sxy;
+    PAR[PM_PAR_7]    = 0.5;
+
+    return(true);
+}
+
+psF64 PM_MODEL_FLUX (const psVector *params)
+{
+    float z, norm;
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    // Area is equivalent to 2 pi sigma^2
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+
+    # define DZ 0.25
+
+    float f0 = 1.0;
+    float f1, f2;
+    for (z = DZ; z < 50; z += DZ) {
+        f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
+        z += DZ;
+        f2 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
+        norm += f0 + 4*f1 + f2;
+        f0 = f2;
+    }
+    norm *= DZ / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
+{
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[PM_PAR_I0] <= 0)
+        return (1.0);
+    if (flux >= PAR[PM_PAR_I0])
+        return (1.0);
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 sigma = axes.major;
+
+    psF64 limit = flux / PAR[PM_PAR_I0];
+
+    psF64 z = pow (-log(limit), (1.0 / PAR[PM_PAR_7]));
+
+    psF64 radius = sigma * sqrt (2.0 * z);
+
+    if (isnan(radius))
+        psAbort("error in code: radius is NaN");
+
+    return (radius);
+}
+
+bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    for (int i = 0; i < psf->params->n; i++) {
+        if (psf->params->data[i] == NULL) {
+            out[i] = in[i];
+        } else {
+            pmTrend2D *trend = psf->params->data[i];
+            out[i] = pmTrend2DEval(trend, in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+        }
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    if (!pmPSF_FitToModel (out, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)",
+                in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS(PS_MINIMIZE_PARAM_MIN, i, out, NULL);
+        status &= PM_MODEL_LIMITS(PS_MINIMIZE_PARAM_MAX, i, out, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)",
+                     in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+            modelPSF->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+
+    return true;
+}
+
+// construct the PSF model from the FLT model and the psf
+// XXX is this sufficiently general do be a global function, not a pmModelClass function?
+bool PM_MODEL_PARAMS_FROM_PSF (pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io)
+{
+    psF32 *PAR = model->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = Io;
+    PAR[PM_PAR_XPOS] = Xo;
+    PAR[PM_PAR_YPOS] = Yo;
+
+    // supply the model-fitted parameters, or copy from the input
+    for (int i = 0; i < psf->params->n; i++) {
+        if (i == PM_PAR_SKY) continue;
+        if (i == PM_PAR_I0) continue;
+        if (i == PM_PAR_XPOS) continue;
+        if (i == PM_PAR_YPOS) continue;
+        pmTrend2D *trend = psf->params->data[i];
+        PAR[i] = pmTrend2DEval(trend, Xo, Yo);
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    // XXX user-defined value for limit?
+    if (!pmPSF_FitToModel (PAR, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, PAR, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, PAR, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)", Xo, Yo);
+            model->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
+    dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
+    dP = sqrt (dP);
+
+    status = true;
+//    status &= (dP < 0.5);
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    fprintf (stderr, "SERSIC status pars: dP: %f, I0: %f, S/N: %f\n",
+	     dP, PAR[PM_PAR_I0], (dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]));
+
+    return status;
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_PARAMS_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_SGAUSS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_SGAUSS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_SGAUSS.c	(revision 22322)
@@ -0,0 +1,386 @@
+/******************************************************************************
+ * this file defines the SGAUSS source shape model (XXX need a better name!).  Note that these
+ * model functions are loaded by pmModelGroup.c using 'include', and thus need no 'include'
+ * statements of their own.  The models use a psVector to represent the set of parameters, with
+ * the sequence used to specify the meaning of the parameter.  The meaning of the parameters
+ * may thus vary depending on the specifics of the model.  All models which are used a PSF
+ * representations share a few parameters, for which # define names are listed in pmModel.h:
+
+   power-law with fitted slope and outer tidal radius
+   1 / (1 + z^N + kz^4)
+
+   * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+   * PM_PAR_I0 1    - central intensity
+   * PM_PAR_XPOS 2  - X center of object
+   * PM_PAR_YPOS 3  - Y center of object
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXY 6   - X*Y term of elliptical contour
+   * PM_PAR_7   7   - slope of power-law component (N)
+   * PM_PAR_8   8   - amplitude of the tidal cutoff (k)
+   *****************************************************************************/
+
+/***
+    XXXX the model in this file needs to be tested more carefully.
+    the code for guessing the power-law slope based on the radial profile
+    is either too slow or does not work well.
+    fix up the code to follow conventions in the other model function files.
+***/
+
+XXX broken code
+
+# define PM_MODEL_FUNC       pmModelFunc_SGAUSS
+# define PM_MODEL_FLUX       pmModelFlux_SGAUSS
+# define PM_MODEL_GUESS      pmModelGuess_SGAUSS
+# define PM_MODEL_LIMITS     pmModelLimits_SGAUSS
+# define PM_MODEL_RADIUS     pmModelRadius_SGAUSS
+# define PM_MODEL_FROM_PSF   pmModelFromPSF_SGAUSS
+# define PM_MODEL_FIT_STATUS pmModelFitStatus_SGAUSS
+
+psF64 psImageEllipseContour (psEllipseAxes axes, double xc, double yc, psImage *image);
+
+psF32 PM_MODEL_FUNC (psVector *deriv,
+                     const psVector *params,
+                     const psVector *x)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = x->data.F32[0] - PAR[2];
+    psF32 Y  = x->data.F32[1] - PAR[3];
+    psF32 px = PAR[4]*X;
+    psF32 py = PAR[5]*Y;
+    psF32 z  = PS_MAX((0.5*PS_SQR(px) + 0.5*PS_SQR(py) + PAR[6]*X*Y), 1e-8);
+    // note that if z -> 0, dPAR[7] -> -inf
+    // also z^(PAR[7]-1) -> Inf
+
+    psF32 pr = z*PAR[8];
+    psF32 pr3 = pr*pr*pr;
+    psF32 p  = pow(z, PAR[7] - 1.0);
+    psF32 r  = 1.0 / (1 + z*p + pr*pr3);
+    psF32 f  = PAR[1]*r + PAR[0];
+
+    if (deriv != NULL) {
+        // note difference from a pure gaussian: q = params->data.F32[1]*r
+        psF32 t = PAR[1]*r*r;
+        psF32 q = t*(PAR[7]*p + 4*PAR[8]*pr3);
+
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*PAR[4] + PAR[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*PAR[5] + PAR[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -2*t*log(z)*z*p;
+        deriv->data.F32[8] = -2*t*4*z*pr3;
+    }
+    return(f);
+}
+
+bool PM_MODEL_LIMITS  (psVector **beta_lim, psVector **params_min, psVector **params_max)
+{
+
+    *beta_lim   = psVectorAlloc (9, PS_TYPE_F32);
+    *params_min = psVectorAlloc (9, PS_TYPE_F32);
+    *params_max = psVectorAlloc (9, PS_TYPE_F32);
+
+    beta_lim[0][0].data.F32[0] = 1000;
+    beta_lim[0][0].data.F32[1] = 10000;
+    beta_lim[0][0].data.F32[2] = 5;
+    beta_lim[0][0].data.F32[3] = 5;
+    beta_lim[0][0].data.F32[4] = 0.5;
+    beta_lim[0][0].data.F32[5] = 0.5;
+    beta_lim[0][0].data.F32[6] = 0.5;
+    beta_lim[0][0].data.F32[7] = 0.5;
+    beta_lim[0][0].data.F32[8] = 0.05;
+
+    params_min[0][0].data.F32[0] = -1000;
+    params_min[0][0].data.F32[1] = 0;
+    params_min[0][0].data.F32[2] = -100;
+    params_min[0][0].data.F32[3] = -100;
+    params_min[0][0].data.F32[4] = 0.01;
+    params_min[0][0].data.F32[5] = 0.01;
+    params_min[0][0].data.F32[6] = -5.0;
+    params_min[0][0].data.F32[7] = 0.5;
+    params_min[0][0].data.F32[8] = 0.001;
+
+    params_max[0][0].data.F32[0] = 1e5;
+    params_max[0][0].data.F32[1] = 1e6;
+    params_max[0][0].data.F32[2] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[3] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[4] = 2.0;
+    params_max[0][0].data.F32[5] = 2.0;
+    params_max[0][0].data.F32[6] = +3.0;
+    params_max[0][0].data.F32[7] = 5.0;
+    params_max[0][0].data.F32[8] = 0.5;
+
+    return (TRUE);
+}
+
+bool PM_MODEL_GUESS  (pmModel *model, pmSource *source)
+{
+
+    pmMoments *sMoments = source->moments;
+    pmPeak    *peak     = source->peak;
+    psF32     *params   = model->params->data.F32;
+
+    psEllipseAxes axes;
+    psEllipseShape shape;
+    psEllipseMoments moments;
+
+    moments.x2 = PS_SQR(sMoments->Sx);
+    moments.y2 = PS_SQR(sMoments->Sy);
+    moments.xy = sMoments->Sxy;
+
+    // solve the math to go from Moments To Shape
+    axes = psEllipseMomentsToAxes(moments);
+    shape = psEllipseAxesToShape(axes);
+
+    params[0] = sMoments->Sky;
+    params[1] = sMoments->Peak - sMoments->Sky;
+    params[2] = peak->x;
+    params[3] = peak->y;
+    params[4] = 1.0 / shape.sx;
+    params[5] = 1.0 / shape.sy;
+    params[6] = shape.sxy;
+
+    # if (0)
+
+        f1 = psImageEllipseContour (axes, peak->x, peak->y, source->pixels);
+    axes.major *= 2.0;
+    axes.minor *= 2.0;
+    f2 = psImageEllipseContour (axes, peak->x, peak->y, source->pixels);
+
+    if (f1 > f2) {
+        params[7] = PS_MIN (3.0, PS_MAX (0.5, log(2.0*(f1/f2) - 1.0) / log(2.0)));
+    } else {
+        params[7] = 0.5;
+    }
+    # endif
+
+    params[7] = 1.8;
+    params[8] = 0.1;
+
+
+    return(true);
+}
+
+psF64 PM_MODEL_FLUX (const psVector *params)
+{
+    float f, norm, z;
+
+    psF32 *PAR = params->data.F32;
+
+    psF64 A1   = PS_SQR(PAR[4]);
+    psF64 A2   = PS_SQR(PAR[5]);
+    psF64 A3   = PS_SQR(PAR[6]);
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - A3);
+    // Area is equivalent to 2 pi sigma^2
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+    for (z = 0.005; z < 50; z += 0.01) {
+        psF32 pr = PAR[8]*z;
+        f = 1.0 / (1 + pow(z, PAR[7]) + PS_SQR(PS_SQR(pr)));
+        norm += f;
+    }
+    norm *= 0.01;
+
+    psF64 Flux = PAR[1] * Area * norm;
+
+    return(Flux);
+}
+
+// XXX need to define the radius along the major axis
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 PM_MODEL_RADIUS   (const psVector *params, psF64 flux)
+{
+    psF64 r, z = 0.0, pr, f;
+    psF32 *PAR = params->data.F32;
+
+    psEllipseAxes axes;
+    psEllipseShape shape;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[1] <= 0)
+        return (1.0);
+    if (flux >= PAR[1])
+        return (1.0);
+
+    // convert Sx,Sy,Sxy to major/minor axes
+    shape.sx = 1.0 / PAR[4];
+    shape.sy = 1.0 / PAR[5];
+    shape.sxy = PAR[6];
+
+    axes = psEllipseShapeToAxes (shape);
+    psF64 dr = 1.0 / axes.major;
+    psF64 limit = flux / PAR[1];
+
+    // XXX : we can do this faster with an intelligent starting choice
+    for (r = 0.0; r < 20.0; r += dr) {
+        z = PS_SQR(r);
+        pr = PAR[8]*z;
+        f = 1.0 / (1 + pow(z, PAR[7]) + PS_SQR(PS_SQR(pr)));
+        if (f < limit)
+            break;
+    }
+    psF64 radius = 2.0 * axes.major * sqrt (z);
+    if (isnan(radius)) {
+        fprintf (stderr, "error in code\n");
+    }
+    return (radius);
+}
+
+bool PM_MODEL_FROM_PSF  (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 9; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        // XXX: Verify this (from EAM change)
+        //out[i] = Polynomial2DEval_EAM(poly, out[2], out[3]);
+        out[i] = psPolynomial2DEval(poly, out[2], out[3]);
+    }
+    return(true);
+}
+
+bool PM_MODEL_FIT_STATUS  (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+    psEllipseAxes axes;
+    psEllipseShape shape;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    shape.sx = 1.0 / PAR[4];
+    shape.sy = 1.0 / PAR[5];
+    shape.sxy = PAR[6];
+
+    axes = psEllipseShapeToAxes (shape);
+
+    dP = 0;
+    dP += PS_SQR(dPAR[4] / PAR[4]);
+    dP += PS_SQR(dPAR[5] / PAR[5]);
+    dP += PS_SQR(dPAR[7] / PAR[7]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[1] > 0);
+    status &= ((dPAR[1]/PAR[1]) < 0.5);
+    status &= (fabs(PAR[8]) < 0.5);
+    status &= (dPAR[8] < 0.1);
+    status &= (axes.major > 1.41);
+    status &= (axes.minor > 1.41);
+    status &= ((axes.major / axes.minor) < 5.0);
+    status &= (PAR[7] > 0.5);
+
+    if (status)
+        return true;
+    return false;
+}
+
+// measure the flux for the elliptical contour
+psF64 psImageEllipseContour (psEllipseAxes axes, double xc, double yc, psImage *image)
+{
+
+    double t, dt, ct, st, xo, yo, value;
+    int N, Nt, x, y;
+
+    // choose dt to uniformly divide contour, with ~1 pix spacing at most
+    dt = asin (1 / axes.minor);
+    Nt = (int)(2*M_PI / dt) + 1;
+    dt = 2*M_PI / Nt;
+
+    ct = cos(axes.theta);
+    st = sin(axes.theta);
+    xo = xc - image->col0;
+    yo = yc - image->row0;
+
+    psVector *contour = psVectorAlloc (Nt, PS_TYPE_F32);
+    contour->n = contour->nalloc;
+    for (t = 0, N = 0; (t < 2*M_PI) && (N < Nt); t += dt) {
+        x = ct*axes.major*cos(t) + st*axes.minor*sin(t) + xo;
+        y = ct*axes.minor*sin(t) + st*axes.major*cos(t) + yo;
+        value = p_psImageGetElementF64(image, x, y);
+        if (isfinite(value)) {
+            contour->data.F32[N] = value;
+            N++;
+        }
+    }
+    contour->n = N;
+    // accept every pixel: double counting is not so problematic here...
+
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+    psVectorStats (stats, contour, NULL, NULL, 0);
+    value = stats->sampleMedian;
+
+    psFree (stats);
+    psFree (contour);
+
+    return (value);
+}
+
+// XXX EAM : test version using flux contours to guess slope
+bool PM_MODEL_GUESS_HARD (pmModel *model, pmSource *source)
+{
+
+    pmMoments *sMoments = source->moments;
+    pmPeak    *peak     = source->peak;
+    psF32     *params   = model->params->data.F32;
+    float f1, f2;
+
+    psEllipseAxes axes;
+    psEllipseShape shape;
+    psEllipseMoments moments;
+
+    moments.x2 = PS_SQR(sMoments->Sx);
+    moments.y2 = PS_SQR(sMoments->Sy);
+    moments.xy = sMoments->Sxy;
+
+    // solve the math to go from Moments To Shape
+    axes = psEllipseMomentsToAxes(moments);
+    shape = psEllipseAxesToShape(axes);
+
+    params[0] = sMoments->Sky;
+    params[1] = sMoments->Peak - sMoments->Sky;
+    params[2] = peak->x;
+    params[3] = peak->y;
+    params[4] = 1.0 / shape.sx;
+    params[5] = 1.0 / shape.sy;
+    params[6] = shape.sxy;
+
+    f1 = psImageEllipseContour (axes, peak->x, peak->y, source->pixels);
+    axes.major *= 2.0;
+    axes.minor *= 2.0;
+    f2 = psImageEllipseContour (axes, peak->x, peak->y, source->pixels);
+
+    if (f1 > f2) {
+        params[7] = PS_MIN (3.0, PS_MAX (0.5, log(2.0*(f1/f2) - 1.0) / log(2.0)));
+    } else {
+        params[7] = 0.5;
+    }
+    params[8] = 0.1;
+
+    return(true);
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_TGAUSS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_TGAUSS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_TGAUSS.c	(revision 22322)
@@ -0,0 +1,202 @@
+/******************************************************************************
+ * this file defines the TGAUSS source shape model (XXX need a better name!).  Note that these
+ * model functions are loaded by pmModelGroup.c using 'include', and thus need no 'include'
+ * statements of their own.  The models use a psVector to represent the set of parameters, with
+ * the sequence used to specify the meaning of the parameter.  The meaning of the parameters
+ * may thus vary depending on the specifics of the model.  All models which are used a PSF
+ * representations share a few parameters, for which # define names are listed in pmModel.h:
+
+   power-law with fixed slope and fitted amplitude
+   1 / (1 + z + kz^2.2)
+
+   * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+   * PM_PAR_I0 1    - central intensity
+   * PM_PAR_XPOS 2  - X center of object
+   * PM_PAR_YPOS 3  - Y center of object
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXY 6   - X*Y term of elliptical contour
+   * PM_PAR_7   7   - amplitude of high-order component (k)
+   *****************************************************************************/
+
+/***
+    XXXX the model in this file needs to be tested more carefully.
+    fix up the code to follow conventions in the other model function files.
+***/
+
+XXX broken code
+
+# define PM_MODEL_FUNC       pmModelFunc_TGAUSS
+# define PM_MODEL_FLUX       pmModelFlux_TGAUSS
+# define PM_MODEL_GUESS      pmModelGuess_TGAUSS
+# define PM_MODEL_LIMITS     pmModelLimits_TGAUSS
+# define PM_MODEL_RADIUS     pmModelRadius_TGAUSS
+# define PM_MODEL_FROM_PSF   pmModelFromPSF_TGAUSS
+# define PM_MODEL_FIT_STATUS pmModelFitStatus_TGAUSS
+
+psF64 PS_MODEL_FUNC (psVector *deriv,
+                     const psVector *params,
+                     const psVector *x)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = x->data.F32[0] - PAR[2];
+    psF32 Y  = x->data.F32[1] - PAR[3];
+    psF32 px = PAR[4]*X;
+    psF32 py = PAR[5]*Y;
+    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + PAR[6]*X*Y;
+
+    psF32 p  = pow(z, 1.2);
+    psF32 r  = 1.0 / (1 + z + PAR[7]*z*p);
+    psF32 f  = PAR[1]*r + PAR[0];
+
+    if (deriv != NULL) {
+        // note difference from a pure gaussian: q = params->data.F32[1]*r
+        psF32 t = PAR[1]*r*r;
+        psF32 q = t*(1 + PAR[7]*2.2*p);
+
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*PAR[4] + PAR[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*PAR[5] + PAR[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -t*z*p;
+    }
+    return(f);
+}
+
+bool PM_MODEL_LIMITS  (psVector **beta_lim, psVector **params_min, psVector **params_max)
+{
+
+    *beta_lim   = psVectorAlloc (8, PS_TYPE_F32);
+    *params_min = psVectorAlloc (8, PS_TYPE_F32);
+    *params_max = psVectorAlloc (8, PS_TYPE_F32);
+
+    beta_lim[0][0].data.F32[PM_PAR_SKY] = 1000;
+    beta_lim[0][0].data.F32[PM_PAR_I0] = 3e6;
+    beta_lim[0][0].data.F32[PM_PAR_XPOS] = 5;
+    beta_lim[0][0].data.F32[PM_PAR_YPOS] = 5;
+    beta_lim[0][0].data.F32[PM_PAR_SXX] = 0.5;
+    beta_lim[0][0].data.F32[PM_PAR_SYY] = 0.5;
+    beta_lim[0][0].data.F32[PM_PAR_SXY] = 0.5;
+    beta_lim[0][0].data.F32[PM_PAR_7] = 0.5;
+
+    params_min[0][0].data.F32[PM_PAR_SKY] = -1000;
+    params_min[0][0].data.F32[PM_PAR_I0] = 0;
+    params_min[0][0].data.F32[PM_PAR_XPOS] = -100;
+    params_min[0][0].data.F32[PM_PAR_YPOS] = -100;
+    params_min[0][0].data.F32[PM_PAR_SXX] = 0.5;
+    params_min[0][0].data.F32[PM_PAR_SYY] = 0.5;
+    params_min[0][0].data.F32[PM_PAR_SXY] = -5.0;
+    params_min[0][0].data.F32[PM_PAR_7] = 0.1;
+
+    params_max[0][0].data.F32[PM_PAR_SKY] = 1e5;
+    params_max[0][0].data.F32[PM_PAR_I0] = 1e8;
+    params_max[0][0].data.F32[PM_PAR_XPOS] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[PM_PAR_YPOS] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[PM_PAR_SXX] = 100.0;
+    params_max[0][0].data.F32[PM_PAR_SYY] = 100.0;
+    params_max[0][0].data.F32[PM_PAR_SXY] = +5.0;
+    params_max[0][0].data.F32[PM_PAR_7] = 10.0;
+
+    return (TRUE);
+}
+
+bool PS_MODEL_GUESS  (psModel *model, psSource *source)
+{
+
+    psVector *params = model->params;
+
+    params->data.F32[0] = source->moments->Sky;
+    params->data.F32[1] = source->peak->counts - source->moments->Sky;
+    params->data.F32[2] = source->moments->x;
+    params->data.F32[3] = source->moments->y;
+    params->data.F32[4] = 1.0/source->moments->Sx;
+    params->data.F32[5] = 1.0/source->moments->Sy;
+    // params->data.F32[6] = source->moments->Sxy;
+    params->data.F32[6] = 0.0;
+    params->data.F32[7] = 5.0;
+    return(true);
+}
+
+psF64 PS_MODEL_FLUX (const psVector *params)
+{
+    psF64 A1   = 1 / PS_SQR(params->data.F32[4]);
+    psF64 A2   = 1 / PS_SQR(params->data.F32[5]);
+    psF64 A3   = params->data.F32[6];
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - PS_SQR(A3));
+    // Area is equivalent to 2 pi sigma^2
+
+    psF64 Flux = params->data.F32[1] * Area;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 PS_MODEL_RADIUS   (const psVector *params, psF64 flux)
+{
+    if (flux <= 0)
+        return (1.0);
+    if (params->data.F32[1] <= 0)
+        return (1.0);
+    if (flux >= params->data.F32[1])
+        return (1.0);
+
+    psF64 sigma  = sqrt(2.0) * hypot (1.0 / params->data.F32[4], 1.0 / params->data.F32[5]);
+    psF64 radius = sigma * sqrt (2.0 * log(params->data.F32[1] / flux));
+    if (isnan(radius)) {
+        fprintf (stderr, "error in code\n");
+    }
+    return (radius);
+}
+
+bool PS_MODEL_FROM_PSF  (psModel *modelPSF, psModel *modelFLT, const pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 8; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        out[i] = Polynomial2DEval (poly, out[2], out[3]);
+    }
+    return(true);
+}
+
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
+    dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    return status;
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_WAUSS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_WAUSS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_WAUSS.c	(revision 22322)
@@ -0,0 +1,198 @@
+/******************************************************************************
+ * this file defines the WAUSS source shape model (XXX need a better name!).  Note that these
+ * model functions are loaded by pmModelGroup.c using 'include', and thus need no 'include'
+ * statements of their own.  The models use a psVector to represent the set of parameters, with
+ * the sequence used to specify the meaning of the parameter.  The meaning of the parameters
+ * may thus vary depending on the specifics of the model.  All models which are used a PSF
+ * representations share a few parameters, for which # define names are listed in pmModel.h:
+
+   power-law with fitted linear term
+   1 / (1 + Az + Bz^2 + z^3/6)
+
+   * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+   * PM_PAR_I0 1    - central intensity
+   * PM_PAR_XPOS 2  - X center of object
+   * PM_PAR_YPOS 3  - Y center of object
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (SigmaX / sqrt(2))
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (SigmaY / sqrt(2))
+   * PM_PAR_SXY 6   - X*Y term of elliptical contour
+   * PM_PAR_7   7   - amplitude of the linear component (A)
+   * PM_PAR_8   8   - amplitude of the quadratic component (B)
+   *****************************************************************************/
+
+/***
+    XXXX the model in this file needs to be tested more carefully.
+    fix up the code to follow conventions in the other model function files.
+***/
+
+XXX broken code
+
+# define PM_MODEL_FUNC       pmModelFunc_WAUSS
+# define PM_MODEL_FLUX       pmModelFlux_WAUSS
+# define PM_MODEL_GUESS      pmModelGuess_WAUSS
+# define PM_MODEL_LIMITS     pmModelLimits_WAUSS
+# define PM_MODEL_RADIUS     pmModelRadius_WAUSS
+# define PM_MODEL_FROM_PSF   pmModelFromPSF_WAUSS
+# define PM_MODEL_FIT_STATUS pmModelFitStatus_WAUSS
+
+psF64 PS_MODEL_FUNC (psVector *deriv,
+                     const psVector *params,
+                     const psVector *x)
+{
+    psF32 X = x->data.F32[0] - params->data.F32[2];
+    psF32 Y = x->data.F32[1] - params->data.F32[2];
+    psF32 px = params->data.F32[4]*X;
+    psF32 py = params->data.F32[5]*Y;
+    psF32 z = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
+    psF32 t = 0.5*z*z*(1.0 + params->data.F32[8]*z/3.0);
+    psF32 r = 1.0 / (1.0 + z + params->data.F32[7]*t); /* exp (-Z) */
+    psF32 f = params->data.F32[1]*r + params->data.F32[0];
+
+    if (deriv != NULL) {
+        // note difference from gaussian: q = params->data.F32[1]*r
+        psF32 q = params->data.F32[1]*r*r*(1.0 + params->data.F32[7]*z*(1.0 + params->data.F32[8]*z/2.0));
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*params->data.F32[4] + params->data.F32[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*params->data.F32[5] + params->data.F32[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -100.0*params->data.F32[1]*r*r*t;
+        deriv->data.F32[8] = -100.0*params->data.F32[1]*r*r*params->data.F32[7]*(z*z*z)/6.0;
+        // The values of 100 dampen the swing of params->data.F32[7,8] */
+    }
+    return(f);
+}
+
+bool PM_MODEL_LIMITS  (psVector **beta_lim, psVector **params_min, psVector **params_max)
+{
+
+    *beta_lim   = psVectorAlloc (8, PS_TYPE_F32);
+    *params_min = psVectorAlloc (8, PS_TYPE_F32);
+    *params_max = psVectorAlloc (8, PS_TYPE_F32);
+
+    beta_lim[0][0].data.F32[PM_PAR_SKY] = 1000;
+    beta_lim[0][0].data.F32[PM_PAR_I0] = 3e6;
+    beta_lim[0][0].data.F32[PM_PAR_XPOS] = 5;
+    beta_lim[0][0].data.F32[PM_PAR_YPOS] = 5;
+    beta_lim[0][0].data.F32[PM_PAR_SXX] = 0.5;
+    beta_lim[0][0].data.F32[PM_PAR_SYY] = 0.5;
+    beta_lim[0][0].data.F32[PM_PAR_SXY] = 0.5;
+    beta_lim[0][0].data.F32[PM_PAR_7] = 0.5;
+
+    params_min[0][0].data.F32[PM_PAR_SKY] = -1000;
+    params_min[0][0].data.F32[PM_PAR_I0] = 0;
+    params_min[0][0].data.F32[PM_PAR_XPOS] = -100;
+    params_min[0][0].data.F32[PM_PAR_YPOS] = -100;
+    params_min[0][0].data.F32[PM_PAR_SXX] = 0.5;
+    params_min[0][0].data.F32[PM_PAR_SYY] = 0.5;
+    params_min[0][0].data.F32[PM_PAR_SXY] = -5.0;
+    params_min[0][0].data.F32[PM_PAR_7] = 0.1;
+
+    params_max[0][0].data.F32[PM_PAR_SKY] = 1e5;
+    params_max[0][0].data.F32[PM_PAR_I0] = 1e8;
+    params_max[0][0].data.F32[PM_PAR_XPOS] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[PM_PAR_YPOS] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[PM_PAR_SXX] = 100.0;
+    params_max[0][0].data.F32[PM_PAR_SYY] = 100.0;
+    params_max[0][0].data.F32[PM_PAR_SXY] = +5.0;
+    params_max[0][0].data.F32[PM_PAR_7] = 10.0;
+
+    return (TRUE);
+}
+
+bool PS_MODEL_GUESS  (psModel *model, psSource *source)
+{
+
+    psVector *params = model->params;
+
+    params->data.F32[0] = source->moments->Sky;
+    params->data.F32[1] = source->peak->counts - source->moments->Sky;
+    params->data.F32[2] = source->moments->x;
+    params->data.F32[3] = source->moments->y;
+    params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
+    params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
+    params->data.F32[6] = source->moments->Sxy;
+    // XXX: What are these?
+    // params->data.F32[7] = B2;
+    // params->data.F32[8] = B3;
+    return(true);
+}
+
+// this is probably wrong since it uses the gauss integral 2 pi sigma^2
+psF64 PS_MODEL_FLUX (const psVector *params)
+{
+    psF64 A1   = 1 / PS_SQR(params->data.F32[4]);
+    psF64 A2   = 1 / PS_SQR(params->data.F32[5]);
+    psF64 A3   = params->data.F32[6];
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - PS_SQR(A3));
+    // Area is equivalent to 2 pi sigma^2
+
+    psF64 Flux = params->data.F32[1] * Area;
+
+    return(Flux);
+}
+
+// return the radius which yields the requested flux
+psF64 PS_MODEL_RADIUS   (const psVector *params, psF64 flux)
+{
+    if (flux <= 0)
+        return (1.0);
+    if (params->data.F32[1] <= 0)
+        return (1.0);
+    if (flux >= params->data.F32[1] <= 0)
+        return (1.0);
+
+    psF64 sigma  = sqrt(2.0) * hypot (1.0 / params->data.F32[4], 1.0 / params->data.F32[5]);
+    psF64 radius = sigma * sqrt (2.0 * log(params->data.F32[1] / flux));
+    return (radius);
+}
+
+bool PS_MODEL_FROM_PSF  (psModel *modelPSF, psModel *modelFLT, const pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 9; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        out[i] = Polynomial2DEval (poly, out[2], out[3]);
+    }
+    return(true);
+}
+
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
+    dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    return status;
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_ZGAUSS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_ZGAUSS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/models/pmModel_ZGAUSS.c	(revision 22322)
@@ -0,0 +1,251 @@
+/******************************************************************************
+ * this file defines the ZGAUSS source shape model (XXX need a better name!).  Note that these
+ * model functions are loaded by pmModelGroup.c using 'include', and thus need no 'include'
+ * statements of their own.  The models use a psVector to represent the set of parameters, with
+ * the sequence used to specify the meaning of the parameter.  The meaning of the parameters
+ * may thus vary depending on the specifics of the model.  All models which are used a PSF
+ * representations share a few parameters, for which # define names are listed in pmModel.h:
+
+   power-law with fitted slope and tidal cutoff
+   1 / (1 + z^N + (Az)^4)
+
+   * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+   * PM_PAR_I0 1    - central intensity
+   * PM_PAR_XPOS 2  - X center of object
+   * PM_PAR_YPOS 3  - Y center of object
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXY 6   - X*Y term of elliptical contour
+   * PM_PAR_7   7   - slope of power-law component (N)
+   *****************************************************************************/
+
+/***
+    XXXX the model in this file needs to be tested more carefully.
+    fix up the code to follow conventions in the other model function files.
+***/
+
+XXX broken code
+
+# define PM_MODEL_FUNC       pmModelFunc_ZGAUSS
+# define PM_MODEL_FLUX       pmModelFlux_ZGAUSS
+# define PM_MODEL_GUESS      pmModelGuess_ZGAUSS
+# define PM_MODEL_LIMITS     pmModelLimits_ZGAUSS
+# define PM_MODEL_RADIUS     pmModelRadius_ZGAUSS
+# define PM_MODEL_FROM_PSF   pmModelFromPSF_ZGAUSS
+# define PM_MODEL_FIT_STATUS pmModelFitStatus_ZGAUSS
+
+# define PAR8 0.1
+
+psF64 PS_MODEL_FUNC (psVector *deriv,
+                     const psVector *params,
+                     const psVector *x)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = x->data.F32[0] - PAR[2];
+    psF32 Y  = x->data.F32[1] - PAR[3];
+    psF32 px = PAR[4]*X;
+    psF32 py = PAR[5]*Y;
+    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + PAR[6]*X*Y;
+
+    psF32 pr = PAR8*z;
+    psF32 p  = pow(z, PAR[7] - 1.0);
+    psF32 r  = 1.0 / (1 + z*p + SQ(SQ(pr)));
+    psF32 f  = PAR[1]*r + PAR[0];
+
+    if (deriv != NULL) {
+        // note difference from a pure gaussian: q = params->data.F32[1]*r
+        psF32 t = PAR[1]*r*r;
+        psF32 q = t*(PAR[7]*p + 4*PAR8*pr*pr*pr);
+
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*PAR[4] + PAR[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*PAR[5] + PAR[6]*X);
+        deriv->data.F32[4] = -q*px*X;
+        deriv->data.F32[5] = -q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -t*log(z)*z*p;
+    }
+    return(f);
+}
+
+bool PM_MODEL_LIMITS  (psVector **beta_lim, psVector **params_min, psVector **params_max)
+{
+
+    *beta_lim   = psVectorAlloc (8, PS_TYPE_F32);
+    *params_min = psVectorAlloc (8, PS_TYPE_F32);
+    *params_max = psVectorAlloc (8, PS_TYPE_F32);
+
+    beta_lim[0][0].data.F32[PM_PAR_SKY] = 1000;
+    beta_lim[0][0].data.F32[PM_PAR_I0] = 3e6;
+    beta_lim[0][0].data.F32[PM_PAR_XPOS] = 5;
+    beta_lim[0][0].data.F32[PM_PAR_YPOS] = 5;
+    beta_lim[0][0].data.F32[PM_PAR_SXX] = 0.5;
+    beta_lim[0][0].data.F32[PM_PAR_SYY] = 0.5;
+    beta_lim[0][0].data.F32[PM_PAR_SXY] = 0.5;
+    beta_lim[0][0].data.F32[PM_PAR_7] = 0.5;
+
+    params_min[0][0].data.F32[PM_PAR_SKY] = -1000;
+    params_min[0][0].data.F32[PM_PAR_I0] = 0;
+    params_min[0][0].data.F32[PM_PAR_XPOS] = -100;
+    params_min[0][0].data.F32[PM_PAR_YPOS] = -100;
+    params_min[0][0].data.F32[PM_PAR_SXX] = 0.5;
+    params_min[0][0].data.F32[PM_PAR_SYY] = 0.5;
+    params_min[0][0].data.F32[PM_PAR_SXY] = -5.0;
+    params_min[0][0].data.F32[PM_PAR_7] = 0.1;
+
+    params_max[0][0].data.F32[PM_PAR_SKY] = 1e5;
+    params_max[0][0].data.F32[PM_PAR_I0] = 1e8;
+    params_max[0][0].data.F32[PM_PAR_XPOS] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[PM_PAR_YPOS] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[PM_PAR_SXX] = 100.0;
+    params_max[0][0].data.F32[PM_PAR_SYY] = 100.0;
+    params_max[0][0].data.F32[PM_PAR_SXY] = +5.0;
+    params_max[0][0].data.F32[PM_PAR_7] = 10.0;
+
+    return (TRUE);
+}
+
+bool PS_MODEL_GUESS  (psModel *model, psSource *source)
+{
+
+    psVector *params = model->params;
+
+    psEllipseAxes axes;
+    psEllipseShape shape;
+    psEllipseMoments moments;
+
+    moments.x2 = PS_SQR(source->moments->Sx);
+    moments.y2 = PS_SQR(source->moments->Sy);
+    moments.xy = source->moments->Sxy;
+
+    axes = psEllipseMomentsToAxes(moments);
+    shape = psEllipseAxesToShape(axes);
+
+    params->data.F32[0] = source->moments->Sky;
+    params->data.F32[1] = source->peak->counts - source->moments->Sky;
+    params->data.F32[2] = source->moments->x;
+    params->data.F32[3] = source->moments->y;
+    params->data.F32[4] = 1.0 / shape.sx;
+    params->data.F32[5] = 1.0 / shape.sy;
+    params->data.F32[6] = shape.sxy;
+    params->data.F32[7] = 1.9;
+    return(true);
+}
+
+psF64 PS_MODEL_FLUX (const psVector *params)
+{
+    float f, norm, z;
+
+    psF32 *PAR = params->data.F32;
+
+    psF64 A1   = PS_SQR(PAR[4]);
+    psF64 A2   = PS_SQR(PAR[5]);
+    psF64 A3   = PS_SQR(PAR[6]);
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - A3);
+    // Area is equivalent to 2 pi sigma^2
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+    psF32 pr = PAR8*z;
+    for (z = 0.005; z < 50; z += 0.01) {
+        f = 1.0 / (1 + pow(z, PAR[7]) + SQ(SQ(pr)));
+        norm += f;
+    }
+    norm *= 0.01;
+
+    psF64 Flux = PAR[1] * Area * norm;
+
+    return(Flux);
+}
+
+// XXX need to define the radius along the major axis
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 PS_MODEL_RADIUS   (const psVector *params, psF64 flux)
+{
+    psF64 r, z, pr, f;
+    psF32 *PAR = params->data.F32;
+
+    psEllipseAxes axes;
+    psEllipseShape shape;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[1] <= 0)
+        return (1.0);
+    if (flux >= PAR[1])
+        return (1.0);
+
+    // convert Sx,Sy,Sxy to major/minor axes
+    shape.sx = 1.0 / PAR[4];
+    shape.sy = 1.0 / PAR[5];
+    shape.sxy = PAR[6];
+
+    axes = psEllipseShapeToAxes (shape);
+    psF64 dr = 1.0 / axes.major;
+    psF64 limit = flux / PAR[1];
+
+    // XXX : we can do this faster with an intelligent starting choice
+    for (r = 0.0; r < 20.0; r += dr) {
+        z = SQ(r);
+        pr = PAR8*z;
+        f = 1.0 / (1 + pow(z, PAR[7]) + SQ(SQ(pr)));
+        if (f < limit)
+            break;
+    }
+    psF64 radius = 2.0 * axes.major * sqrt (z);
+    if (isnan(radius)) {
+        fprintf (stderr, "error in code\n");
+    }
+    return (radius);
+}
+
+bool PS_MODEL_FROM_PSF  (psModel *modelPSF, psModel *modelFLT, const pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 8; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        out[i] = Polynomial2DEval (poly, out[2], out[3]);
+    }
+    return(true);
+}
+
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
+    dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    return status;
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmDetections.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmDetections.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmDetections.c	(revision 22322)
@@ -0,0 +1,44 @@
+/* @file  pmDetections.c
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:00:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmDetections.h"
+
+void pmDetectionsFree (pmDetections *detections) {
+
+  if (!detections) return;
+
+  psFree (detections->footprints);
+  psFree (detections->peaks);
+  psFree (detections->oldPeaks);
+  return;
+}
+
+// generate a pmDetections container with empty (allocated) footprints and peaks containers
+pmDetections *pmDetectionsAlloc() {
+
+    pmDetections *detections = (pmDetections *)psAlloc(sizeof(pmDetections));
+    psMemSetDeallocator(detections, (psFreeFunc) pmDetectionsFree);
+
+    detections->footprints = NULL;
+    detections->peaks      = NULL;
+    detections->oldPeaks   = NULL;
+    detections->last       = 0;
+
+    return (detections);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmDetections.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmDetections.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmDetections.h	(revision 22322)
@@ -0,0 +1,31 @@
+/* @file  pmDetections.h
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:00:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+# ifndef PM_DETECTIONS_H
+# define PM_DETECTIONS_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/** pmDetections structure
+ *
+ * A strcture to carry the combined footprint and peak information
+ *
+ */
+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
+  int last;
+} pmDetections;
+
+pmDetections *pmDetectionsAlloc ();
+
+/// @}
+# endif /* PM_DETECTIONS_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprint.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprint.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprint.c	(revision 22322)
@@ -0,0 +1,186 @@
+/* @file  pmFootprint.c
+ * low-level pmFootprint functions
+ *
+ * @author RHL, Princeton & IfA; EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:00:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmPeaks.h"
+#include "pmSpan.h"
+#include "pmFootprint.h"
+
+static void footprintFree(pmFootprint *tmp)
+{
+   if (!tmp) {
+        return;
+   }
+
+   psTrace("psModules.objects", 5, "---- begin ----\n");
+
+   psFree(tmp->spans);
+   psFree(tmp->peaks);
+
+   psTrace("psModules.objects", 5, "---- end ----\n");
+}
+
+/*
+ * pmFootprintAlloc(): Allocate the pmFootprint structure to NULL.
+ */
+pmFootprint *pmFootprintAlloc(int nspan, // number of spans expected in footprint
+			      const psImage *image) // region footprint lives in
+{
+    psTrace("psModules.objects", 5, "---- begin ----\n");
+
+    static int id = 1;
+    pmFootprint *footprint = (pmFootprint *)psAlloc(sizeof(pmFootprint));
+    *(int *)&footprint->id = id++;
+    psMemSetDeallocator(footprint, (psFreeFunc) footprintFree);
+
+    footprint->normalized = false;
+
+    assert(nspan >= 0);
+    footprint->npix = 0;
+    footprint->spans = psArrayAllocEmpty(nspan);
+    footprint->peaks = psArrayAlloc(0);
+
+    footprint->bbox.x0 = footprint->bbox.y0 = 0;
+    footprint->bbox.x1 = footprint->bbox.y1 = -1;
+
+    if (image == NULL) {
+	footprint->region.x0 = footprint->region.y0 = 0;
+	footprint->region.x1 = footprint->region.y1 = -1;
+    } else {
+	footprint->region.x0 = image->col0;
+	footprint->region.x1 = image->col0 + image->numCols - 1;
+	footprint->region.y0 = image->row0;
+	footprint->region.y1 = image->row0 + image->numRows - 1;
+    }
+
+    psTrace("psModules.objects", 5, "---- end ----\n");
+    return(footprint);
+}
+
+bool pmFootprintTest(const psPtr ptr) {
+    return (psMemGetDeallocator(ptr) == (psFreeFunc)footprintFree);
+}
+
+pmFootprint *pmFootprintNormalize(pmFootprint *fp) {
+    if (fp != NULL && !fp->normalized) {
+	fp->peaks = psArraySort(fp->peaks, pmPeakSortBySN);
+	fp->normalized = true;
+    }
+
+    return fp;
+}
+
+//
+// Add a span to a footprint, returning the new span
+//
+pmSpan *pmFootprintAddSpan(pmFootprint *fp,	// the footprint to add to
+			   const int y, // row to add
+			   int x0,      // range of
+			   int x1) {    //          columns
+
+    if (x1 < x0) {
+	int tmp = x0;
+	x0 = x1;
+	x1 = tmp;
+    }
+
+    pmSpan *sp = pmSpanAlloc(y, x0, x1);
+    psArrayAdd(fp->spans, 1, sp);
+    psFree(sp);
+    
+    fp->npix += x1 - x0 + 1;
+
+    if (fp->spans->n == 1) {
+	fp->bbox.x0 = x0;
+	fp->bbox.x1 = x1;
+	fp->bbox.y0 = y;
+	fp->bbox.y1 = y;
+    } else {
+	if (x0 < fp->bbox.x0) fp->bbox.x0 = x0;
+	if (x1 > fp->bbox.x1) fp->bbox.x1 = x1;
+	if (y < fp->bbox.y0) fp->bbox.y0 = y;
+	if (y > fp->bbox.y1) fp->bbox.y1 = y;
+    }
+
+    return sp;
+}
+
+void pmFootprintSetBBox(pmFootprint *fp) {
+    assert (fp != NULL);
+    if (fp->spans->n == 0) {
+	return;
+    }
+    pmSpan *sp = fp->spans->data[0];
+    int x0 = sp->x0;
+    int x1 = sp->x1;
+    int y0 = sp->y;
+    int y1 = sp->y;
+
+    for (int i = 1; i < fp->spans->n; i++) {
+	sp = fp->spans->data[i];
+	
+	if (sp->x0 < x0) x0 = sp->x0;
+	if (sp->x1 > x1) x1 = sp->x1;
+	if (sp->y < y0) y0 = sp->y;
+	if (sp->y > y1) y1 = sp->y;
+    }
+
+    fp->bbox.x0 = x0;
+    fp->bbox.x1 = x1;
+    fp->bbox.y0 = y0;
+    fp->bbox.y1 = y1;
+}
+
+int pmFootprintSetNpix(pmFootprint *fp) {
+   assert (fp != NULL);
+   int npix = 0;
+   for (int i = 0; i < fp->spans->n; i++) {
+       pmSpan *span = fp->spans->data[i];
+       npix += span->x1 - span->x0 + 1;
+   }
+   fp->npix = npix;
+
+   return npix;
+}
+
+/*
+ * Extract the peaks in a psArray of pmFootprints, returning a psArray of pmPeaks
+ */
+psArray *pmFootprintArrayToPeaks(const psArray *footprints) {
+   assert(footprints != NULL);
+   assert(footprints->n == 0 || pmFootprintTest(footprints->data[0]));
+
+   int npeak = 0;
+   for (int i = 0; i < footprints->n; i++) {
+      const pmFootprint *fp = footprints->data[i];
+      npeak += fp->peaks->n;
+   }
+
+   psArray *peaks = psArrayAllocEmpty(npeak);
+   
+   for (int i = 0; i < footprints->n; i++) {
+      const pmFootprint *fp = footprints->data[i];
+      for (int j = 0; j < fp->peaks->n; j++) {
+	 psArrayAdd(peaks, 1, fp->peaks->data[j]);
+      }
+   }
+
+   return peaks;
+}
+
+/************************************************************************************************************/
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprint.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprint.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprint.h	(revision 22322)
@@ -0,0 +1,60 @@
+/* @file  pmFootprint.h
+ *
+ * @author RHL, Princeton & IfA; EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:00:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FOOTPRINT_H
+#define PM_FOOTPRINT_H
+typedef struct {
+    const int id;                       //!< unique ID
+    int npix;                           //!< number of pixels in this pmFootprint
+    psArray *spans;                     //!< the pmSpans
+    psRegion bbox;                      //!< the pmFootprint's bounding box
+    psArray *peaks;                     //!< the peaks lying in this footprint
+    psRegion region;   //!< A region describing the psImage the footprints live in
+    bool normalized;                    //!< Are the spans sorted? 
+} pmFootprint;
+
+pmFootprint *pmFootprintAlloc(int nspan, const psImage *img);
+bool pmFootprintTest(const psPtr ptr);
+
+pmFootprint *pmFootprintNormalize(pmFootprint *fp);
+int pmFootprintSetNpix(pmFootprint *fp);
+void pmFootprintSetBBox(pmFootprint *fp);
+
+pmSpan *pmFootprintAddSpan(pmFootprint *fp,	// the footprint to add to
+			   const int y, // row to add
+			   int x0,      // range of
+			   int x1);    //          columns
+
+psArray *pmFootprintsFind(const psImage *img, const float threshold, const int npixMin);
+pmFootprint *pmFootprintsFindAtPoint(const psImage *img,
+                                    const float threshold,
+                                    const psArray *peaks,
+                                    int row, int col);
+
+psArray *pmFootprintArrayGrow(const psArray *footprints, int r);
+psArray *pmFootprintArraysMerge(const psArray *footprints1, const psArray *footprints2,
+                                const int includePeaks);
+
+psImage *pmSetFootprintArrayIDs(const psArray *footprints, const bool relativeIDs);
+psImage *pmSetFootprintID(psImage *idImage, const pmFootprint *fp, const int id);
+void pmSetFootprintArrayIDsForImage(psImage *idImage,
+				    const psArray *footprints, // the footprints to insert
+				    const bool relativeIDs); // show IDs starting at 0, not pmFootprint->id
+
+psErrorCode pmFootprintsAssignPeaks(psArray *footprints, const psArray *peaks);
+
+psErrorCode pmFootprintArrayCullPeaks(const psImage *img, const psImage *weight, psArray *footprints,
+                                 const float nsigma, const float threshold_min);
+psErrorCode pmFootprintCullPeaks(const psImage *img, const psImage *weight, pmFootprint *fp,
+                                 const float nsigma, const float threshold_min);
+
+psArray *pmFootprintArrayToPeaks(const psArray *footprints);
+
+/// @}
+# endif /* PM_FOOTPRINT_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintArrayGrow.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintArrayGrow.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintArrayGrow.c	(revision 22322)
@@ -0,0 +1,105 @@
+/* @file  pmFootprintArrayGrow.c
+ *
+ * @author RHL, Princeton & IfA; EAM, IfA
+ *
+ * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-05 01:51:57 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmSpan.h"
+#include "pmFootprint.h"
+
+# define USE_FFTS_TO_CONVOLVE 1
+
+/*
+ * Grow a psArray of pmFootprints isotropically by r pixels, returning a new psArray of new pmFootprints
+ */
+psArray *pmFootprintArrayGrow(const psArray *footprints, // footprints to grow
+                              int r) {  // how much to grow each footprint
+    assert (footprints->n == 0 || pmFootprintTest(footprints->data[0]));
+
+    psTimerStart ("grow");
+
+    if (footprints->n == 0) {           // we don't know the size of the footprint's region
+        return psArrayAlloc(0);
+    }
+    /*
+     * We'll insert the footprints into an image, then convolve with a disk,
+     * then extract a footprint from the result --- this is magically what we want.
+     */
+    psImage *idImage = pmSetFootprintArrayIDs(footprints, true);
+    psLogMsg ("psphot", PS_LOG_DETAIL, "set footprint array IDs: %f sec\n", psTimerMark ("grow"));
+
+#if 1
+    // Use a separable convolution: should be faster
+    idImage = (psImage*)psBinaryOp(idImage, idImage, "MIN", psScalarAlloc(1, PS_TYPE_S32));
+    psImage *grownIdImage = psImageCopy(NULL, idImage, PS_TYPE_MASK);
+    if (!psImageConvolveMask(grownIdImage, grownIdImage, 0x0f, 0xf0, -1, 1, -1, 1)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to grow mask.");
+        psFree(grownIdImage);
+        psFree(idImage);
+        return NULL;
+    }
+#else
+    if (r <= 0) {
+        r = 1;                          // r == 1 => no grow
+    }
+    psKernel *circle = psKernelAlloc(-r, r, -r, r);
+    assert (circle->image->numRows == 2*r + 1 && circle->image->numCols == circle->image->numRows);
+    for (int i = 0; i <= r; i++) {
+        for (int j = 0; j <= r; j++) {
+            if (i*i + j*j <= r*r) {
+                circle->kernel[i][j] =
+                    circle->kernel[i][-j] =
+                    circle->kernel[-i][j] =
+                    circle->kernel[-i][-j] = 1;
+            }
+        }
+    }
+
+# if (USE_FFTS_TO_CONVOLVE)
+    psImage *f32ImageIn = psImageCopy (NULL, idImage, PS_TYPE_F32);
+    psImage *f32ImageOut = psImageConvolveFFT(NULL, f32ImageIn, NULL, 0, circle);
+    psImage *grownIdImage = psImageCopy (NULL, f32ImageOut, PS_TYPE_S32);
+    psFree (f32ImageIn);
+    psFree (f32ImageOut);
+#else
+    psImage *grownIdImage = psImageConvolveDirect(NULL, idImage, circle); // Here's the actual grow step
+#endif // USE_FFTS_TO_CONVOLVE
+    psFree(circle);
+#endif // Don't bother at all
+
+    psFree(idImage);
+
+    psLogMsg ("psphot", PS_LOG_DETAIL, "convolved with grow disc: %f sec\n", psTimerMark ("grow"));
+
+    psArray *grown = pmFootprintsFind(grownIdImage, 0.5, 1); // and here we rebuild the grown footprints
+    psLogMsg ("psphot", PS_LOG_DETAIL, "found grown footprints: %f sec\n", psTimerMark ("grow"));
+
+    assert (grown != NULL);
+    psFree(grownIdImage);
+
+    /*
+     * Now assign the peaks appropriately.  We could do this more efficiently
+     * using grownIdImage (which we just freed), but this is easy and probably fast enough
+     */
+    psArray *peaks = pmFootprintArrayToPeaks(footprints);
+    pmFootprintsAssignPeaks(grown, peaks);
+    psFree(peaks);
+
+    psLogMsg ("psphot", PS_LOG_DETAIL, "finished grow: %f sec\n", psTimerMark ("grow"));
+
+    return grown;
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintArraysMerge.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintArraysMerge.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintArraysMerge.c	(revision 22322)
@@ -0,0 +1,87 @@
+/* @file  pmFootprintArraysMerge.c
+ *
+ * @author RHL, Princeton & IfA; EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:00:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmSpan.h"
+#include "pmFootprint.h"
+
+/*
+ * Merge together two psArrays of pmFootprints neither of which is damaged.
+ *
+ * The returned psArray may contain elements of the inital psArrays (with
+ * their reference counters suitable incremented)
+ */
+psArray *pmFootprintArraysMerge(const psArray *footprints1, // one set of footprints
+				const psArray *footprints2, // the other set
+				const int includePeaks) { // which peaks to set? 0x1 => footprints1, 0x2 => 2
+    assert (footprints1->n == 0 || pmFootprintTest(footprints1->data[0]));
+    assert (footprints2->n == 0 || pmFootprintTest(footprints2->data[0]));
+
+    if (footprints1->n == 0 || footprints2->n == 0) {		// nothing to do but put copies on merged
+	const psArray *old = (footprints1->n == 0) ? footprints2 : footprints1;
+
+	psArray *merged = psArrayAllocEmpty(old->n);
+	for (int i = 0; i < old->n; i++) {
+	    psArrayAdd(merged, 1, old->data[i]);
+	}
+	
+	return merged;
+    }
+    /*
+     * We have real work to do as some pmFootprints in footprints2 may overlap
+     * with footprints1
+     */
+    {
+	pmFootprint *fp1 = footprints1->data[0];
+	pmFootprint *fp2 = footprints2->data[0];
+	if (fp1->region.x0 != fp2->region.x0 ||
+	    fp1->region.x1 != fp2->region.x1 ||
+	    fp1->region.y0 != fp2->region.y0 ||
+	    fp1->region.y1 != fp2->region.y1) {
+	    psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+		    "The two pmFootprint arrays correspnond to different-sized regions");
+	    return NULL;
+	}
+    }
+    /*
+     * We'll insert first one set of footprints then the other into an image, then
+     * extract a footprint from the result --- this is magically what we want.
+     */
+    psImage *idImage = pmSetFootprintArrayIDs(footprints1, true);
+    pmSetFootprintArrayIDsForImage(idImage, footprints2, true);
+
+    psArray *merged = pmFootprintsFind(idImage, 0.5, 1);
+    assert (merged != NULL);
+    psFree(idImage);
+    /*
+     * Now assign the peaks appropriately.  We could do this more efficiently
+     * using idImage (which we just freed), but this is easy and probably fast enough
+     */ 
+    if (includePeaks & 0x1) {
+	psArray *peaks = pmFootprintArrayToPeaks(footprints1);
+	pmFootprintsAssignPeaks(merged, peaks);
+	psFree(peaks);
+    }
+
+    if (includePeaks & 0x2) {
+	psArray *peaks = pmFootprintArrayToPeaks(footprints2);
+	pmFootprintsAssignPeaks(merged, peaks);
+	psFree(peaks);
+    }
+    
+    return merged;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintAssignPeaks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintAssignPeaks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintAssignPeaks.c	(revision 22322)
@@ -0,0 +1,137 @@
+/* @file  pmFootprintAssignPeaks.c
+ *
+ * @author RHL, Princeton & IfA; EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:00:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmPeaks.h"
+#include "pmSpan.h"
+#include "pmFootprint.h"
+
+/*
+ * Given a psArray of pmFootprints and another of pmPeaks, assign the peaks to the
+ * footprints in which that fall; if they _don't_ fall in a footprint, add a suitable
+ * one to the list.
+ */
+psErrorCode
+pmFootprintsAssignPeaks(psArray *footprints,	// the pmFootprints
+			const psArray *peaks) { // the pmPeaks
+    assert (footprints != NULL);
+    assert (footprints->n == 0 || pmFootprintTest(footprints->data[0]));
+    assert (peaks != NULL);
+    assert (peaks->n == 0 || psMemCheckPeak(peaks->data[0]));
+    
+    if (footprints->n == 0) {
+	if (peaks->n > 0) {
+	    return psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Your list of footprints is empty");
+	}
+	return PS_ERR_NONE;
+    }
+    /*
+     * Create an image filled with the object IDs, and use it to assign pmPeaks to the
+     * objects
+     */
+    psImage *ids = pmSetFootprintArrayIDs(footprints, true);
+    assert (ids != NULL);
+    assert (ids->type.type == PS_TYPE_S32);
+    const int row0 = ids->row0;
+    const int col0 = ids->col0;
+    const int numRows = ids->numRows;
+    const int numCols = ids->numCols;
+
+    for (int i = 0; i < peaks->n; i++) {
+	pmPeak *peak = peaks->data[i];
+	const int x = peak->x - col0;
+	const int y = peak->y - row0;
+	
+	assert (x >= 0 && x < numCols && y >= 0 && y < numRows);
+	int id = ids->data.S32[y][x - col0];
+
+	if (id == 0) {			// peak isn't in a footprint, so make one for it
+	    pmFootprint *nfp = pmFootprintAlloc(1, ids);
+	    pmFootprintAddSpan(nfp, y, x, x);
+	    psArrayAdd(footprints, 1, nfp);
+	    psFree(nfp);
+	    id = footprints->n;
+	}
+
+	assert (id >= 1 && id <= footprints->n);
+	pmFootprint *fp = footprints->data[id - 1];
+	psArrayAdd(fp->peaks, 5, peak);
+	peak->footprint = (struct pmFootprint *) fp; // reference to containing footprint
+    }
+    
+    psFree(ids);
+
+    // Make sure that peaks within each footprint are sorted and unique
+    for (int i = 0; i < footprints->n; i++) {
+	
+	pmFootprint *fp = footprints->data[i];
+
+	// XXX are we allowed to have peak-less footprints??
+	if (!fp->peaks->n) continue;
+
+        fp->peaks = psArraySort(fp->peaks, pmPeakSortBySN);
+
+	// XXX EAM : the algorithm below should be much faster than using psArrayRemove if
+	// the number of peaks in the footprint is large, or if there are no duplicates.
+	// if we have a lot of small-number peak arrays with duplicates, this may be
+	// slower.
+
+	// track the number of good peaks in the footprint
+	int nGood = 1;
+
+	// check for duplicates
+	// on first pass, we set the index to NULL if peak is a duplicate
+	// XXX EAM : this can leave behind duplicates of the same S/N
+	// (if sorted list has A, B, A, B ...)
+	for (int j = 1; j < fp->peaks->n; j++) { 
+	    if (fp->peaks->data[j] == fp->peaks->data[nGood]) {
+		// everything on the array has its own mem reference; free and drop this one
+		psFree (fp->peaks->data[j]);
+		fp->peaks->data[j] = NULL;
+	    } else {
+		nGood ++;
+	    }
+	}
+
+	// no deleted peaks, go to next footprint
+	if (nGood == fp->peaks->n) continue;
+
+	int nKeep = 0;
+
+	psArray *goodPeaks = psArrayAlloc (nGood);
+	// on second pass, save the good peaks
+	for (int j = 0; j < fp->peaks->n; j++) { // check for duplicates
+	    if (fp->peaks->data[j] == NULL) continue;
+	    // transfer the data (NULL to avoid double free)
+	    // this is only slightly sleazy
+	    goodPeaks->data[nKeep] = fp->peaks->data[j];
+	    fp->peaks->data[j] = NULL;
+	    nKeep ++;
+	}
+	psAssert (nGood == nKeep, "mis-counted nKeep or nGood");
+
+	// free the old (now NULL-filled) array
+	psFree (fp->peaks);
+	fp->peaks = goodPeaks;
+    }
+
+    // (void)psArrayRemoveIndex(fp->peaks, j);
+
+
+    return PS_ERR_NONE;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintFind.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintFind.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintFind.c	(revision 22322)
@@ -0,0 +1,235 @@
+/* @file  pmFootprintFind.c
+ * find footprints in an image (fast on large scale images)
+ *
+ * @author RHL, Princeton & IfA; EAM, IfA
+ *
+ * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-05 02:36:25 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmSpan.h"
+#include "pmFootprint.h"
+
+// XXX EAM : why use WSPAN in here rather than pmSpan?
+typedef struct {                        /* run-length code for part of object*/
+   int id;                              /* ID for object */
+   int y;                               /* Row wherein WSPAN dwells */
+   int x0, x1;                          /* inclusive range of columns */
+} WSPAN;
+
+/*
+ * comparison function for qsort; sort by ID then row
+ */
+static int
+compar(const void *va, const void *vb)
+{
+   const WSPAN *a = va;
+   const WSPAN *b = vb;
+
+   if(a->id < b->id) {
+      return(-1);
+   } else if(a->id > b->id) {
+      return(1);
+   } else {
+      return(a->y - b->y);
+   }
+}
+
+/*
+ * Follow a chain of aliases, returning the final resolved value.
+ */
+static int
+resolve_alias(const int *aliases,       /* list of aliases */
+              int id)                   /* alias to look up */
+{
+   int resolved = id;                   /* resolved alias */
+
+   while(id != aliases[id]) {
+      resolved = id = aliases[id];
+   }
+
+   return(resolved);
+}
+
+/*
+ * Go through an image, finding sets of connected pixels above threshold
+ * and assembling them into pmFootprints;  the resulting set of objects
+ * is returned as a psArray
+ */
+psArray *
+pmFootprintsFind(const psImage *img,    // image to search
+                 const float threshold, // Threshold
+                 const int npixMin)     // minimum number of pixels in an acceptable pmFootprint
+{
+   int i0;                              /* initial value of i */
+   int id;                              /* object ID */
+   int in_span;                         /* object ID of current WSPAN */
+   int nspan = 0;                       /* number of spans */
+   int nobj = 0;                        /* number of objects found */
+   int x0 = 0;                          /* unpacked from a WSPAN */
+   int *tmp;                            /* used in swapping idc/idp */
+
+   assert(img != NULL);
+
+   psImage *floatImg = img->type.type == PS_TYPE_F32 ? psMemIncrRefCounter((psImage*)img) :
+       psImageCopy(NULL, img, PS_TYPE_F32); // Floating-point version of image; casting away const
+
+   const int row0 = img->row0;
+   const int col0 = img->col0;
+   const int numRows = img->numRows;
+   const int numCols = img->numCols;
+/*
+ * Storage for arrays that identify objects by ID. We want to be able to
+ * refer to idp[-1] and idp[numCols], hence the (numCols + 2)
+ */
+   int *id_s = psAlloc(2*(numCols + 2)*sizeof(int));
+   memset(id_s, '\0', 2*(numCols + 2)*sizeof(int)); assert(id_s[0] == 0);
+   int *idc = id_s + 1;                 // object IDs in current/
+   int *idp = idc + (numCols + 2);      //                       previous row
+
+   int size_aliases = 1 + numRows/20;   // size of aliases[] array
+   int *aliases = psAlloc(size_aliases*sizeof(int)); // aliases for object IDs
+
+   int size_spans = 1 + numRows/20;     // size of spans[] array
+   WSPAN *spans = psAlloc(size_spans*sizeof(WSPAN)); // row:x0,x1 for objects
+/*
+ * Go through image identifying objects
+ */
+   for (int i = 0; i < numRows; i++) {
+       int j;
+       tmp = idc; idc = idp; idp = tmp;  /* swap ID pointers */
+       memset(idc, '\0', numCols*sizeof(int));
+
+       in_span = 0;                      /* not in a span */
+       for (j = 0; j < numCols; j++) {
+           double pixVal = floatImg->data.F32[i][j]; // Value of pixel
+           if (pixVal < threshold) {
+               if (in_span) {
+                   if(nspan >= size_spans) {
+                       size_spans *= 2;
+                       spans = psRealloc(spans, size_spans*sizeof(WSPAN));
+                   }
+                   spans[nspan].id = in_span;
+                   spans[nspan].y = i;
+                   spans[nspan].x0 = x0;
+                   spans[nspan].x1 = j - 1;
+
+                   nspan++;
+
+                   in_span = 0;
+               }
+           } else {                       /* a pixel to fix */
+               if(idc[j - 1] != 0) {
+                   id = idc[j - 1];
+               } else if(idp[j - 1] != 0) {
+                   id = idp[j - 1];
+               } else if(idp[j] != 0) {
+                   id = idp[j];
+               } else if(idp[j + 1] != 0) {
+                   id = idp[j + 1];
+               } else {
+                   id = ++nobj;
+
+                   if(id >= size_aliases) {
+                       size_aliases *= 2;
+                       aliases = psRealloc(aliases, size_aliases*sizeof(int));
+                   }
+                   aliases[id] = id;
+               }
+
+               idc[j] = id;
+               if(!in_span) {
+                   x0 = j; in_span = id;
+               }
+               /*
+                * Do we need to merge ID numbers? If so, make suitable entries in aliases[]
+                */
+               if(idp[j + 1] != 0 && idp[j + 1] != id) {
+                   aliases[resolve_alias(aliases, idp[j + 1])] =
+                       resolve_alias(aliases, id);
+
+                   idc[j] = id = idp[j + 1];
+               }
+           }
+       }
+
+       if(in_span) {
+           if(nspan >= size_spans) {
+               size_spans *= 2;
+               spans = psRealloc(spans, size_spans*sizeof(WSPAN));
+           }
+
+           assert(nspan < size_spans);    /* we checked for space above */
+           spans[nspan].id = in_span;
+           spans[nspan].y = i;
+           spans[nspan].x0 = x0;
+           spans[nspan].x1 = j - 1;
+
+           nspan++;
+       }
+   }
+   psFree(floatImg);
+
+   psFree(id_s);
+   /*
+    * Resolve aliases; first alias chains, then the IDs in the spans
+    */
+   for (int i = 0; i < nspan; i++) {
+       spans[i].id = resolve_alias(aliases, spans[i].id);
+   }
+
+   psFree(aliases);
+   /*
+    * Sort spans by ID, so we can sweep through them once
+    * XXX replace with a psLib sort call
+    */
+   if(nspan > 0) {
+       qsort(spans, nspan, sizeof(WSPAN), compar);
+   }
+   /*
+    * Build pmFootprints from the spans
+    */
+   psArray *footprints = psArrayAlloc(nobj);
+   int n = 0;                   // number of pmFootprints
+
+   if(nspan > 0) {
+       id = spans[0].id;
+       i0 = 0;
+       for (int i = 0; i <= nspan; i++) {        /* nspan + 1 to catch last object */
+           if(i == nspan || spans[i].id != id) {
+               pmFootprint *fp = pmFootprintAlloc(i - i0, img);
+
+               for(; i0 < i; i0++) {
+                   pmFootprintAddSpan(fp, spans[i0].y + row0,
+                                      spans[i0].x0 + col0, spans[i0].x1 + col0);
+               }
+
+               if (fp->npix < npixMin) {
+                   psFree(fp);
+               } else {
+                   footprints->data[n++] = fp;
+               }
+           }
+
+           id = spans[i].id;
+       }
+   }
+
+   footprints = psArrayRealloc(footprints, n);
+   /*
+    * clean up
+    */
+   psFree(spans);
+
+   return footprints;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintFindAtPoint.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintFindAtPoint.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintFindAtPoint.c	(revision 22322)
@@ -0,0 +1,409 @@
+/* @file  pmFootprintFindAtPoint.c
+ * find footprints in a small image relative to a reference point
+ *
+ * @author RHL, Princeton & IfA; EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:00:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmPeaks.h"
+#include "pmSpan.h"
+#include "pmFootprint.h"
+
+/*
+ * A data structure to hold the starting point for a search for pixels above threshold,
+ * used by pmFootprintsFindAtPoint
+ *
+ * We don't want to find this span again --- it's already part of the footprint ---
+ * so we set appropriate mask bits
+ *
+ * EAM : these function were confusingly using "startspan" and "spartspan"  
+ * I've rationalized them all to 'startspan'
+ */
+
+//
+// An enum for what we should do with a Startspan
+//
+typedef enum {PM_SSPAN_DOWN = 0,	// scan down from this span
+	      PM_SSPAN_UP,		// scan up from this span
+	      PM_SSPAN_RESTART,		// restart scanning from this span
+	      PM_SSPAN_DONE		// this span is processed
+} PM_SSPAN_DIR;				// How to continue searching
+//
+// An enum for mask's pixel values.  We're looking for pixels that are above threshold, and
+// we keep extra book-keeping information in the PM_SSPAN_STOP plane.  It's simpler to be
+// able to check for 
+//
+enum {
+    PM_SSPAN_INITIAL = 0x0,		// initial state of pixels.
+    PM_SSPAN_DETECTED = 0x1,		// we've seen this pixel
+    PM_SSPAN_STOP = 0x2			// you may stop searching when you see this pixel
+};
+//
+// The struct that remembers how to [re-]start scanning the image for pixels
+//
+typedef struct {
+    const pmSpan *span;			// save the pixel range
+    PM_SSPAN_DIR direction;		// How to continue searching
+    bool stop;				// should we stop searching?
+} Startspan;
+
+static void startspanFree(Startspan *sspan) {
+    psFree((void *)sspan->span);
+}
+
+static Startspan *
+StartspanAlloc(const pmSpan *span,	// The span in question
+	       psImage *mask,		// Pixels that we've already detected
+	       const PM_SSPAN_DIR dir	// Should we continue searching towards the top of the image?
+    ) {
+    Startspan *sspan = psAlloc(sizeof(Startspan));
+    psMemSetDeallocator(sspan, (psFreeFunc)startspanFree);
+
+    sspan->span = psMemIncrRefCounter((void *)span);
+    sspan->direction = dir;
+    sspan->stop = false;
+    
+    if (mask != NULL) {			// remember that we've detected these pixels
+	psMaskType *mpix = &mask->data.PS_TYPE_MASK_DATA[span->y - mask->row0][span->x0 - mask->col0];
+
+	for (int i = 0; i <= span->x1 - span->x0; i++) {
+	    mpix[i] |= PM_SSPAN_DETECTED;
+	    if (mpix[i] & PM_SSPAN_STOP) {
+		sspan->stop = true;
+	    }
+	}
+    }
+    
+    return sspan;
+}
+
+//
+// Add a new Startspan to an array of Startspans.  Iff we see a stop bit, return true
+//
+static bool add_startspan(psArray *startspans, // the saved Startspans
+			  const pmSpan *sp, // the span in question
+			  psImage *mask, // mask of detected/stop pixels
+			  const PM_SSPAN_DIR dir) { // the desired direction to search
+    if (dir == PM_SSPAN_RESTART) {
+	if (add_startspan(startspans, sp, mask,  PM_SSPAN_UP) ||
+	    add_startspan(startspans, sp, NULL, PM_SSPAN_DOWN)) {
+	    return true;
+	}
+    } else {
+	Startspan *sspan = StartspanAlloc(sp, mask, dir);
+	if (sspan->stop) {		// we detected a stop bit
+	    psFree(sspan);		// don't allocate new span
+
+	    return true;
+	} else {
+	    psArrayAdd(startspans, 1, sspan);
+	    psFree(sspan);		// as it's now owned by startspans
+	}
+    }
+
+    return false;
+}
+
+/************************************************************************************************************/
+/*
+ * Search the image for pixels above threshold, starting at a single Startspan.
+ * We search the array looking for one to process; it'd be better to move the
+ * ones that we're done with to the end, but it probably isn't worth it for
+ * the anticipated uses of this routine.
+ *
+ * This is the guts of pmFootprintsFindAtPoint
+ */
+static bool do_startspan(pmFootprint *fp, // the footprint that we're building
+			 const psImage *img, // the psImage we're working on
+			 psImage *mask, // the associated masks
+			 const float threshold,	// Threshold
+			 psArray *startspans) {	// specify which span to process next
+    bool F32 = false;			// is this an F32 image?
+    if (img->type.type == PS_TYPE_F32) {
+	F32 = true;
+    } else if (img->type.type == PS_TYPE_S32) {
+	F32 = false;
+    } else {				// N.b. You can't trivially add more cases here; F32 is just a bool
+	psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);
+	return NULL;
+    }
+
+    psF32 *imgRowF32 = NULL;		// row pointer if F32
+    psS32 *imgRowS32 = NULL;		//  "   "   "  "  !F32
+    psMaskType *maskRow = NULL;		//  masks's row pointer
+    
+    const int row0 = img->row0;
+    const int col0 = img->col0;
+    const int numRows = img->numRows;
+    const int numCols = img->numCols;
+    
+    /********************************************************************************************************/
+    
+    Startspan *sspan = NULL;
+    for (int i = 0; i < startspans->n; i++) {
+	sspan = startspans->data[i];
+	if (sspan->direction != PM_SSPAN_DONE) {
+	    break;
+	}
+	if (sspan->stop) {
+	    break;
+	}
+    }
+    if (sspan == NULL || sspan->direction == PM_SSPAN_DONE) { // no more Startspans to process
+	return false;
+    }
+    if (sspan->stop) {			// they don't want any more spans processed
+	return false;
+    }
+    /*
+     * Work
+     */
+    const PM_SSPAN_DIR dir = sspan->direction;
+    /*
+     * Set initial span to the startspan
+     */
+    int x0 = sspan->span->x0 - col0, x1 = sspan->span->x1 - col0;
+    /*
+     * Go through image identifying objects
+     */
+    int nx0, nx1 = -1;			// new values of x0, x1
+    const int di = (dir == PM_SSPAN_UP) ? 1 : -1; // how much i changes to get to the next row
+    bool stop = false;			// should I stop searching for spans?
+
+    for (int i = sspan->span->y -row0 + di; i < numRows && i >= 0; i += di) {
+	imgRowF32 = img->data.F32[i];	// only one of
+	imgRowS32 = img->data.S32[i];	//      these is valid!
+	maskRow = mask->data.PS_TYPE_MASK_DATA[i];
+	//
+	// Search left from the pixel diagonally to the left of (i - di, x0). If there's
+	// a connected span there it may need to grow up and/or down, so push it onto
+	// the stack for later consideration
+	//
+	nx0 = -1;
+	for (int j = x0 - 1; j >= -1; j--) {
+	    double pixVal = (j < 0) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
+	    if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) {
+		if (j < x0 - 1) {	// we found some pixels above threshold
+		    nx0 = j + 1;
+		}
+		break;
+	    }
+	}
+
+	if (nx0 < 0) {			// no span to the left
+	    nx1 = x0 - 1;		// we're going to resume searching at nx1 + 1
+	} else {
+	    //
+	    // Search right in leftmost span
+	    //
+	    //nx1 = 0;			// make gcc happy
+	    for (int j = nx0 + 1; j <= numCols; j++) {
+		double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
+		if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) {
+		    nx1 = j - 1;
+		    break;
+		}
+	    }
+	    
+	    const pmSpan *sp = pmFootprintAddSpan(fp, i + row0, nx0 + col0, nx1 + col0);
+	    
+	    if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
+		stop = true;
+		break;
+	    }
+	}
+	//
+	// Now look for spans connected to the old span.  The first of these we'll
+	// simply process, but others will have to be deferred for later consideration.
+	//
+	// In fact, if the span overhangs to the right we'll have to defer the overhang
+	// until later too, as it too can grow in both directions
+	//
+	// Note that column numCols exists virtually, and always ends the last span; this
+	// is why we claim below that sx1 is always set
+	//
+	bool first = false;		// is this the first new span detected?
+	for (int j = nx1 + 1; j <= x1 + 1; j++) {
+	    double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
+	    if (!(maskRow[j] & PM_SSPAN_DETECTED) && pixVal >= threshold) {
+		int sx0 = j++;		// span that we're working on is sx0:sx1
+		int sx1 = -1;		// We know that if we got here, we'll also set sx1
+		for (; j <= numCols; j++) {
+		    double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
+		    if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) { // end of span
+			sx1 = j;
+			break;
+		    }
+		}
+		assert (sx1 >= 0);
+
+		const pmSpan *sp;
+		if (first) {
+		    if (sx1 <= x1) {
+			sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
+			if (add_startspan(startspans, sp, mask, PM_SSPAN_DONE)) {
+			    stop = true;
+			    break;
+			}
+		    } else {		// overhangs to right
+			sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, x1 + col0);
+			if (add_startspan(startspans, sp, mask, PM_SSPAN_DONE)) {
+			    stop = true;
+			    break;
+			}
+			sp = pmFootprintAddSpan(fp, i + row0, x1 + 1 + col0, sx1 + col0 - 1);
+			if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
+			    stop = true;
+			    break;
+			}
+		    }
+		    first = false;
+		} else {
+		    sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
+		    if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
+			stop = true;
+			break;
+		    }
+		}
+	    }
+	}
+
+	if (stop || first == false) {	// we're done
+	    break;
+	}
+
+	x0 = nx0; x1 = nx1;
+    }
+    /*
+     * Cleanup
+     */
+
+    sspan->direction = PM_SSPAN_DONE;
+    return stop ? false : true;
+}
+
+/*
+ * Go through an image, starting at (row, col) and assembling all the pixels
+ * that are connected to that point (in a chess kings-move sort of way) into
+ * a pmFootprint.
+ *
+ * This is much slower than pmFootprintsFind if you want to find lots of
+ * footprints, but if you only want a small region about a given point it
+ * can be much faster
+ *
+ * N.b. The returned pmFootprint is not in "normal form"; that is the pmSpans
+ * are not sorted by increasing y, x0, x1.  If this matters to you, call
+ * pmFootprintNormalize()
+ */
+pmFootprint *
+pmFootprintsFindAtPoint(const psImage *img,	// image to search
+		       const float threshold,	// Threshold
+		       const psArray *peaks, // array of peaks; finding one terminates search for footprint
+		       int row, int col) { // starting position (in img's parent's coordinate system)
+   assert(img != NULL);
+
+   bool F32 = false;			// is this an F32 image?
+   if (img->type.type == PS_TYPE_F32) {
+       F32 = true;
+   } else if (img->type.type == PS_TYPE_S32) {
+       F32 = false;
+   } else {				// N.b. You can't trivially add more cases here; F32 is just a bool
+       psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);
+       return NULL;
+   }
+   psF32 *imgRowF32 = NULL;		// row pointer if F32
+   psS32 *imgRowS32 = NULL;		//  "   "   "  "  !F32
+   
+   const int row0 = img->row0;
+   const int col0 = img->col0;
+   const int numRows = img->numRows;
+   const int numCols = img->numCols;
+/*
+ * Is point in image, and above threshold?
+ */
+   row -= row0; col -= col0;
+   if (row < 0 || row >= numRows ||
+       col < 0 || col >= numCols) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "row/col == (%d, %d) are out of bounds [%d--%d, %d--%d]",
+		row + row0, col + col0, row0, row0 + numRows - 1, col0, col0 + numCols - 1);
+       return NULL;
+   }
+
+   double pixVal = F32 ? img->data.F32[row][col] : img->data.S32[row][col];
+   if (pixVal < threshold) {
+       return pmFootprintAlloc(0, img);
+   }
+   
+   pmFootprint *fp = pmFootprintAlloc(1 + img->numRows/10, img);
+/*
+ * We need a mask for two purposes; to indicate which pixels are already detected,
+ * and to store the "stop" pixels --- those that, once reached, should stop us
+ * looking for the rest of the pmFootprint.  These are generally set from peaks.
+ */
+   psImage *mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
+   P_PSIMAGE_SET_ROW0(mask, row0);
+   P_PSIMAGE_SET_COL0(mask, col0);
+   psImageInit(mask, PM_SSPAN_INITIAL);
+   //
+   // Set stop bits from peaks list
+   //
+   assert (peaks == NULL || peaks->n == 0 || psMemCheckPeak(peaks->data[0]));
+   if (peaks != NULL) {
+       for (int i = 0; i < peaks->n; i++) {
+	   pmPeak *peak = peaks->data[i];
+	   mask->data.PS_TYPE_MASK_DATA[peak->y - mask->row0][peak->x - mask->col0] |= PM_SSPAN_STOP;
+       }
+   }
+/*
+ * Find starting span passing through (row, col)
+ */
+   psArray *startspans = psArrayAllocEmpty(1); // spans where we have to restart the search
+   
+   imgRowF32 = img->data.F32[row];	// only one of
+   imgRowS32 = img->data.S32[row];	//      these is valid!
+   psMaskType *maskRow = mask->data.PS_TYPE_MASK_DATA[row];
+   {
+       int i;
+       for (i = col; i >= 0; i--) {
+	   pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
+	   if ((maskRow[i] & PM_SSPAN_DETECTED) || pixVal < threshold) {
+	       break;
+	   }
+       }
+       int i0 = i;
+       for (i = col; i < numCols; i++) {
+	   pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
+	   if ((maskRow[i] & PM_SSPAN_DETECTED) || pixVal < threshold) {
+	       break;
+	   }
+       }
+       int i1 = i;
+       const pmSpan *sp = pmFootprintAddSpan(fp, row + row0, i0 + col0 + 1, i1 + col0 - 1);
+
+       (void)add_startspan(startspans, sp, mask, PM_SSPAN_RESTART);
+   }
+   /*
+    * Now workout from those Startspans, searching for pixels above threshold
+    */
+   while (do_startspan(fp, img, mask, threshold, startspans)) continue;
+   /*
+    * Cleanup
+    */
+   psFree(mask);
+   psFree(startspans);			// restores the image pixel
+
+   return fp;				// pmFootprint really
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintIDs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintIDs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmFootprintIDs.c	(revision 22322)
@@ -0,0 +1,121 @@
+/* @file  pmFootprintIDs.c
+ * functions to manipulate footprint IDs 
+ *
+ * @author RHL, Princeton & IfA; EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:00:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmSpan.h"
+#include "pmFootprint.h"
+
+/*
+ * Worker routine for the pmSetFootprintArrayIDs/pmSetFootprintID (and pmMergeFootprintArrays)
+ */
+static void
+set_footprint_id(psImage *idImage,	// the image to set
+		 const pmFootprint *fp, // the footprint to insert
+		 const int id) {	// the desired ID
+   const int col0 = fp->region.x0;
+   const int row0 = fp->region.y0;
+
+   for (int j = 0; j < fp->spans->n; j++) {
+       const pmSpan *span = fp->spans->data[j];
+       psS32 *imgRow = idImage->data.S32[span->y - row0];
+       for(int k = span->x0 - col0; k <= span->x1 - col0; k++) {
+	   imgRow[k] += id;
+       }
+   }
+}
+
+void pmSetFootprintArrayIDsForImage(psImage *idImage,
+				    const psArray *footprints, // the footprints to insert
+				    const bool relativeIDs) { // show IDs starting at 0, not pmFootprint->id
+    int id = 0;				// first index will be 1
+    for (int i = 0; i < footprints->n; i++) {
+	const pmFootprint *fp = footprints->data[i];
+	if (relativeIDs) {
+	    id++;
+	} else {
+	    id = fp->id;
+	}
+       
+	set_footprint_id(idImage, fp, id);
+    }
+}
+
+/*
+ * Set an image to the value of footprint's ID whever they may fall
+ */
+psImage *pmSetFootprintArrayIDs(const psArray *footprints, // the footprints to insert
+				const bool relativeIDs) { // show IDs starting at 1, not pmFootprint->id
+   assert (footprints != NULL);
+
+   if (footprints->n == 0) {
+       psError(PS_ERR_BAD_PARAMETER_SIZE, true, "You didn't provide any footprints");
+       return NULL;
+   }
+   const pmFootprint *fp = footprints->data[0];
+   assert(pmFootprintTest((const psPtr)fp));
+   const int numCols = fp->region.x1 - fp->region.x0 + 1;
+   const int numRows = fp->region.y1 - fp->region.y0 + 1;
+   const int col0 = fp->region.x0;
+   const int row0 = fp->region.y0;
+   assert (numCols >= 0 && numRows >= 0);
+   
+   psImage *idImage = psImageAlloc(numCols, numRows, PS_TYPE_S32);
+   P_PSIMAGE_SET_ROW0(idImage, row0);
+   P_PSIMAGE_SET_COL0(idImage, col0);
+   psImageInit(idImage, 0);
+   /*
+    * do the work
+    */
+   pmSetFootprintArrayIDsForImage(idImage, footprints, relativeIDs);
+
+   return idImage;
+   
+}
+
+/*
+ * Set an image to the value of footprint's ID whever they may fall
+ */
+psImage *pmSetFootprintID(psImage *idImage,
+			  const pmFootprint *fp, // the footprint to insert
+			  const int id) {	// the desired ID
+   assert(fp != NULL && pmFootprintTest((const psPtr)fp));
+   const int numCols = fp->region.x1 - fp->region.x0 + 1;
+   const int numRows = fp->region.y1 - fp->region.y0 + 1;
+   const int col0 = fp->region.x0;
+   const int row0 = fp->region.y0;
+   assert (numCols >= 0 && numRows >= 0);
+   
+   if (idImage == NULL) {
+       idImage = psImageAlloc(numCols, numRows, PS_TYPE_S32);
+   } else {
+       assert (idImage->numCols == numCols);
+       assert (idImage->numRows == numRows);
+       // XXX assert on type (S32)
+   }
+   P_PSIMAGE_SET_ROW0(idImage, row0);
+   P_PSIMAGE_SET_COL0(idImage, col0);
+   psImageInit(idImage, 0);
+   /*
+    * do the work
+    */
+   set_footprint_id(idImage, fp, id);
+
+   return idImage;
+   
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmGrowthCurve.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmGrowthCurve.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmGrowthCurve.c	(revision 22322)
@@ -0,0 +1,72 @@
+/** @file  pmGrowthCurve.c
+ *
+ *  Measure the curve-of-growth for sources
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-10-03 00:42:40 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "psVectorBracket.h"
+
+static void pmGrowthCurveFree (pmGrowthCurve *growth)
+{
+    if (growth == NULL)
+        return;
+
+    psFree (growth->radius);
+    psFree (growth->apMag);
+    return;
+}
+
+bool psMemCheckGrowthCurve(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc)pmGrowthCurveFree );
+}
+
+pmGrowthCurve *pmGrowthCurveAlloc (psF32 minRadius, psF32 maxRadius, psF32 refRadius)
+{
+
+    pmGrowthCurve *growth = psAlloc (sizeof(pmGrowthCurve));
+    psMemSetDeallocator(growth, (psFreeFunc) pmGrowthCurveFree);
+
+    // fractional pixel radii are not well defined; use integer steps
+    growth->radius = psVectorCreate (NULL, minRadius, maxRadius, 1.0, PS_TYPE_F32);
+    growth->apMag  = psVectorAlloc (growth->radius->n, PS_TYPE_F32);
+
+    // XXX may want to extend this to allow for a different refRadius;
+    growth->refRadius = refRadius;
+    growth->maxRadius = maxRadius;
+    growth->apLoss = 0.0;
+    growth->fitMag = 0.0;
+    return growth;
+}
+
+psF32 pmGrowthCurveCorrect(pmGrowthCurve *growth, psF32 radius)
+{
+    PS_ASSERT_PTR_NON_NULL(growth, NAN);
+    float apRad = psVectorInterpolate (growth->radius, growth->apMag, radius);
+    float apCor = growth->apRef - apRad;
+    return apCor;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmGrowthCurve.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmGrowthCurve.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmGrowthCurve.h	(revision 22322)
@@ -0,0 +1,35 @@
+/* @file  pmGrowthCurve.h
+ * @brief functions to manipulate the curve-of-growth data
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-10 01:09:20 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+# ifndef PM_GROWTH_CURVE_H
+# define PM_GROWTH_CURVE_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+typedef struct
+{
+    psVector *radius;
+    psVector *apMag;
+    psF32 refRadius;
+    psF32 maxRadius;
+    psF32 fitMag;
+    psF32 apRef;   // apMag[refRadius]
+    psF32 apLoss;  // fitMag - apRef
+}
+pmGrowthCurve;
+
+bool psMemCheckGrowthCurve(psPtr ptr);
+
+pmGrowthCurve *pmGrowthCurveAlloc (psF32 minRadius, psF32 maxRadius, psF32 refRadius);
+psF32 pmGrowthCurveCorrect (pmGrowthCurve *growth, psF32 radius);
+
+/// @}
+# endif /* PM_GROWTH_CURVE_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModel.c	(revision 22322)
@@ -0,0 +1,412 @@
+/** @file  pmModel.c
+ *
+ *  Functions to define and manipulate object models
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-01-02 20:36:36 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmModelClass.h"
+
+static void modelFree(pmModel *tmp)
+{
+    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+    psFree(tmp->params);
+    psFree(tmp->dparams);
+    psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+}
+
+/******************************************************************************
+pmModelAlloc(): Allocate the pmModel structure, along with its parameters,
+and initialize the type member.  Initialize the params to 0.0.
+*****************************************************************************/
+pmModel *pmModelAlloc(pmModelType type)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+
+    pmModelClass *class = pmModelClassSelect (type);
+    if (class == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return(NULL);
+    }
+
+    pmModel *tmp = (pmModel *) psAlloc(sizeof(pmModel));
+    psMemSetDeallocator(tmp, (psFreeFunc) modelFree);
+
+    tmp->type = type;
+    tmp->chisq = 0.0;
+    tmp->chisqNorm = 0.0;
+    tmp->nDOF  = 0;
+    tmp->nIter = 0;
+    tmp->radiusFit = 0;
+    tmp->flags = PM_MODEL_STATUS_NONE;
+    tmp->residuals = NULL;              // XXX should the model own this memory?
+
+    psS32 Nparams = pmModelClassParameterCount(type);
+    assert (Nparams);
+
+    tmp->params  = psVectorAlloc(Nparams, PS_TYPE_F32);
+    tmp->dparams = psVectorAlloc(Nparams, PS_TYPE_F32);
+    assert (tmp->params);
+    assert (tmp->dparams);
+
+    for (psS32 i = 0; i < tmp->params->n; i++) {
+        tmp->params->data.F32[i] = 0.0;
+        tmp->dparams->data.F32[i] = 0.0;
+    }
+
+    tmp->modelFunc          = class->modelFunc;
+    tmp->modelFlux          = class->modelFlux;
+    tmp->modelRadius        = class->modelRadius;
+    tmp->modelLimits        = class->modelLimits;
+    tmp->modelGuess         = class->modelGuess;
+    tmp->modelFromPSF       = class->modelFromPSF;
+    tmp->modelParamsFromPSF = class->modelParamsFromPSF;
+    tmp->modelFitStatus     = class->modelFitStatus;
+
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(tmp);
+}
+
+bool psMemCheckModel(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) modelFree);
+}
+
+// copy model to a new structure
+pmModel *pmModelCopy (pmModel *model)
+{
+    PS_ASSERT_PTR_NON_NULL(model, NULL);
+
+    pmModel *new = pmModelAlloc (model->type);
+
+    new->chisq     = model->chisq;
+    new->nDOF      = model->nDOF;
+    new->nIter     = model->nIter;
+    new->flags     = model->flags;
+    new->radiusFit = model->radiusFit;
+
+    for (int i = 0; i < new->params->n; i++) {
+        new->params->data.F32[i]  = model->params->data.F32[i];
+        new->dparams->data.F32[i] = model->dparams->data.F32[i];
+    }
+
+    // note that model->residuals is just a reference
+    new->residuals = model->residuals;
+
+    return (new);
+}
+
+/******************************************************************************
+    pmModelEval(source, level, row): evaluates the model function at the specified coords.
+
+    NOTE: The coords are in subImage source->pixel coords, not image coords.
+
+    XXX: Use static vectors for x (NO: needs to be thread safe)
+*****************************************************************************/
+psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(image, NAN);
+    PS_ASSERT_PTR_NON_NULL(model, NAN);
+    PS_ASSERT_PTR_NON_NULL(model->params, NAN);
+
+    // Allocate the x coordinate structure and convert row/col to image space.
+    //
+    psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+    x->data.F32[0] = (psF32) (col + image->col0);
+    x->data.F32[1] = (psF32) (row + image->row0);
+    psF32 tmpF;
+
+    tmpF = model->modelFunc (NULL, model->params, x);
+    psFree(x);
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(tmpF);
+}
+
+psF32 pmModelEvalWithOffset(pmModel *model, psImage *image, psS32 col, psS32 row, int dx, int dy)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(image, false);
+    PS_ASSERT_PTR_NON_NULL(model, false);
+    PS_ASSERT_PTR_NON_NULL(model->params, false);
+
+    // Allocate the x coordinate structure and convert row/col to image space.
+    //
+    psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+    x->data.F32[0] = (psF32) (col + image->col0 + dx);
+    x->data.F32[1] = (psF32) (row + image->row0 + dy);
+    psF32 tmpF;
+
+    tmpF = model->modelFunc (NULL, model->params, x);
+    psFree(x);
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(tmpF);
+}
+
+static bool AddOrSubModel(psImage *image,
+                          psImage *mask,
+                          pmModel *model,
+                          pmModelOpMode mode,
+                          bool add,
+                          psMaskType maskVal,
+                          int dx,
+                          int dy
+    )
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+
+    PS_ASSERT_PTR_NON_NULL(model, false);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
+
+    psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+    psVector *params = model->params;
+
+    psS32 imageCol;
+    psS32 imageRow;
+    psF32 pixelValue;
+
+    // save original values; restore before returning
+    // use the true source position for the residual model
+    // the PSF model has presumably already been set for this coordinate
+    float xPos    = params->data.F32[PM_PAR_XPOS];
+    float yPos    = params->data.F32[PM_PAR_YPOS];
+    float IoSave  = params->data.F32[PM_PAR_I0];
+    float skySave = params->data.F32[PM_PAR_SKY];
+
+    if (mode & PM_MODEL_OP_NORM) {
+        params->data.F32[PM_PAR_I0] = 1.0;
+    }
+    if (!(mode & PM_MODEL_OP_SKY)) {
+        params->data.F32[PM_PAR_SKY] = 0.0;
+    }
+    if (mode & PM_MODEL_OP_CENTER) {
+        params->data.F32[PM_PAR_XPOS] = image->col0 + 0.5*image->numCols;
+        params->data.F32[PM_PAR_YPOS] = image->row0 + 0.5*image->numRows;
+    }
+
+    // use these values for this realization
+    float xCenter  = params->data.F32[PM_PAR_XPOS];
+    float yCenter  = params->data.F32[PM_PAR_YPOS];
+    float Io       = params->data.F32[PM_PAR_I0];
+
+    int xBin = 1;
+    int yBin = 1;
+    float xResidCenter = 0.0;
+    float yResidCenter = 0.0;
+
+    psImage *myRo = NULL;
+    psImage *myRx = NULL;
+    psImage *myRy = NULL;
+    psImageInterpolateOptions *Ro = NULL;
+    psImageInterpolateOptions *Rx = NULL;
+    psImageInterpolateOptions *Ry = NULL;
+    if (model->residuals && (mode & (PM_MODEL_OP_RES0 | PM_MODEL_OP_RES1))) {
+        // if the residual image and object image don't match,
+        // supply an appropriately overlapped residual image
+        psImage *inRo = model->residuals->Ro;
+        psImage *inRx = model->residuals->Rx;
+        psImage *inRy = model->residuals->Ry;
+        xBin = model->residuals->xBin;
+        yBin = model->residuals->yBin;
+        xResidCenter = model->residuals->xCenter;
+        yResidCenter = model->residuals->yCenter;
+        if ((image->numCols != inRo->numCols) ||
+            (image->numRows != inRo->numRows)) {
+            myRo = psImageAlloc (image->numCols, image->numRows, PS_TYPE_F32);
+            myRx = psImageAlloc (image->numCols, image->numRows, PS_TYPE_F32);
+            myRy = psImageAlloc (image->numCols, image->numRows, PS_TYPE_F32);
+            // Difference between input and desired centres
+            int xDiff = (int)(inRo->numCols / 2) - (xPos - image->col0);
+            int yDiff = (int)(inRo->numRows / 2) - (yPos - image->row0);
+            xResidCenter -= xDiff;
+            yResidCenter -= yDiff;
+            for (int iy = 0; iy < myRo->numRows; iy++) {
+                int jy = iy + yDiff;
+                if ((jy < 0) || (jy >= inRo->numRows)) {
+                    for (int ix = 0; ix < myRo->numCols; ix++) {
+                        myRo->data.F32[iy][ix] = 0.0;
+                        myRx->data.F32[iy][ix] = 0.0;
+                        myRy->data.F32[iy][ix] = 0.0;
+                    }
+                    continue;
+                }
+                for (int ix = 0; ix < myRo->numCols; ix++) {
+                    int jx = ix + xDiff;
+                    if ((jx < 0) || (jx >= inRo->numCols)) {
+                        myRo->data.F32[iy][ix] = 0.0;
+                        myRx->data.F32[iy][ix] = 0.0;
+                        myRy->data.F32[iy][ix] = 0.0;
+                    } else {
+                        myRo->data.F32[iy][ix] = inRo->data.F32[jy][jx];
+                        myRx->data.F32[iy][ix] = inRx->data.F32[jy][jx];
+                        myRy->data.F32[iy][ix] = inRy->data.F32[jy][jx];
+                    }
+                }
+            }
+        } else {
+            myRo = psMemIncrRefCounter (inRo);
+            myRx = psMemIncrRefCounter (inRx);
+            myRy = psMemIncrRefCounter (inRy);
+        }
+
+        Ro = psImageInterpolateOptionsAlloc(PS_INTERPOLATE_BILINEAR, myRo, NULL, mask, 0, 0.0, 0.0, 1, 0, 0.0);
+        Rx = psImageInterpolateOptionsAlloc(PS_INTERPOLATE_BILINEAR, myRx, NULL, NULL, 0, 0.0, 0.0, 1, 0, 0.0);
+        Ry = psImageInterpolateOptionsAlloc(PS_INTERPOLATE_BILINEAR, myRy, NULL, NULL, 0, 0.0, 0.0, 1, 0, 0.0);
+
+    }
+
+    for (psS32 iy = 0; iy < image->numRows; iy++) {
+        for (psS32 ix = 0; ix < image->numCols; ix++) {
+            if ((mask != NULL) && (mask->data.U8[iy][ix] & maskVal))
+                continue;
+
+            // Convert i/j to image coord space:
+            // XXX should we use using 0.5 pixel offset?
+            imageCol = ix + image->col0 + dx;
+            imageRow = iy + image->row0 + dy;
+
+            x->data.F32[0] = (float) imageCol;
+            x->data.F32[1] = (float) imageRow;
+
+            pixelValue = 0.0;
+
+            // add in the desired components for this coordinate
+            if (mode & PM_MODEL_OP_FUNC) {
+                pixelValue += model->modelFunc (NULL, params, x);
+            }
+
+            // get the contribution from the residual model
+            if (Ro) {
+                // fractional image position
+                float ox = xBin*(imageCol + 0.5 - xCenter) + xResidCenter;
+                float oy = yBin*(imageRow + 0.5 - yCenter) + yResidCenter;
+
+                psU8 mflux = 0;
+                if (mode & PM_MODEL_OP_RES0) {
+                    double Fo = 0.0;
+                    psImageInterpolate (&Fo, NULL, &mflux, ox, oy, Ro);
+                    if (!mflux && isfinite(Fo)) {
+                        pixelValue += Io*Fo;
+                    }
+                }
+                // skip Rx,Ry if Ro is masked
+                if (!mflux && (mode & PM_MODEL_OP_RES1)) {
+                    double Fx = 0.0;
+                    double Fy = 0.0;
+                    psImageInterpolate (&Fx, NULL, &mflux, ox, oy, Rx);
+                    psImageInterpolate (&Fy, NULL, &mflux, ox, oy, Ry);
+                    if (!mflux && isfinite(Fx) && isfinite(Fy)) {
+                        pixelValue += Io*(xPos*Fx + yPos*Fy);
+                    }
+                }
+            }
+
+            // add or subtract the value
+            if (add) {
+                image->data.F32[iy][ix] += pixelValue;
+            } else {
+                image->data.F32[iy][ix] -= pixelValue;
+            }
+        }
+    }
+
+    // restore original values
+    params->data.F32[PM_PAR_I0]   = IoSave;
+    params->data.F32[PM_PAR_SKY]  = skySave;
+    params->data.F32[PM_PAR_XPOS] = xPos;
+    params->data.F32[PM_PAR_YPOS] = yPos;
+
+    psFree(x);
+    psFree(Ro);
+    psFree(Rx);
+    psFree(Ry);
+    psFree(myRo);
+    psFree(myRx);
+    psFree(myRy);
+    psTrace("psModules.objects", 3, "---- %s(true) end ----\n", __func__);
+    return(true);
+}
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmModelAdd(psImage *image,
+                psImage *mask,
+                pmModel *model,
+                pmModelOpMode mode,
+                psMaskType maskVal)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psBool rc = AddOrSubModel(image, mask, model, mode, true, maskVal, 0.0, 0.0);
+    psTrace("psModules.objects", 3, "---- %s(%d) end ----\n", __func__, rc);
+    return(rc);
+}
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmModelSub(psImage *image,
+                psImage *mask,
+                pmModel *model,
+                pmModelOpMode mode,
+                psMaskType maskVal)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psBool rc = AddOrSubModel(image, mask, model, mode, false, maskVal, 0.0, 0.0);
+    psTrace("psModules.objects", 3, "---- %s(%d) end ----\n", __func__, rc);
+    return(rc);
+}
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmModelAddWithOffset(psImage *image,
+                          psImage *mask,
+                          pmModel *model,
+                          pmModelOpMode mode,
+                          psMaskType maskVal,
+                          int dx,
+                          int dy)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psBool rc = AddOrSubModel(image, mask, model, mode, true, maskVal, dx, dy);
+    psTrace("psModules.objects", 3, "---- %s(%d) end ----\n", __func__, rc);
+    return(rc);
+}
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmModelSubWithOffset(psImage *image,
+                          psImage *mask,
+                          pmModel *model,
+                          pmModelOpMode mode,
+                          psMaskType maskVal,
+                          int dx,
+                          int dy)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psBool rc = AddOrSubModel(image, mask, model, mode, false, maskVal, dx, dy);
+    psTrace("psModules.objects", 3, "---- %s(%d) end ----\n", __func__, rc);
+    return(rc);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModel.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModel.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModel.h	(revision 22322)
@@ -0,0 +1,201 @@
+/* @file  pmModel.h
+ * @brief Functions to define and manipulate object models
+ *
+ * @author GLG, MHPCC
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-04-08 18:33:16 $
+ *
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_MODEL_H
+# define PM_MODEL_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/* pointers for the functions types below are supplied to each pmModel, and can be used by
+   the programmer without needing to know the model class */
+
+typedef enum {
+    PM_MODEL_STATUS_NONE         = 0x00, ///< model fit not yet attempted, no other info
+    PM_MODEL_STATUS_FITTED       = 0x01, ///< model fit completed
+    PM_MODEL_STATUS_NONCONVERGE  = 0x02, ///< model fit did not converge
+    PM_MODEL_STATUS_OFFIMAGE     = 0x04, ///< model fit drove out of range
+    PM_MODEL_STATUS_BADARGS      = 0x08, ///< model fit called with invalid args
+    PM_MODEL_STATUS_LIMITS       = 0x10  ///< model parameters hit limits
+} pmModelStatus;
+
+typedef enum {
+    PM_MODEL_OP_NONE    = 0x00,
+    PM_MODEL_OP_FUNC    = 0x01,
+    PM_MODEL_OP_RES0    = 0x02,
+    PM_MODEL_OP_RES1    = 0x04,
+    PM_MODEL_OP_FULL    = 0x07,
+    PM_MODEL_OP_SKY     = 0x08,
+    PM_MODEL_OP_CENTER  = 0x10,
+    PM_MODEL_OP_NORM    = 0x20,
+    PM_MODEL_OP_NOISE   = 0x40,
+} pmModelOpMode;
+
+typedef struct pmModel pmModel;
+typedef struct pmSource pmSource;
+
+//  This function is the model chi-square minimization function for this model.
+typedef psMinimizeLMChi2Func pmModelFunc;
+
+//  This function sets the parameter limits for this model.
+typedef psMinimizeLMLimitFunc pmModelLimits;
+
+// This function returns the integrated flux for the given model parameters.
+typedef psF64 (*pmModelFlux)(const psVector *params);
+
+// This function returns the radius at which the given model and parameters
+// achieves the given flux.
+typedef psF64 (*pmModelRadius)(const psVector *params, double flux);
+
+//  This function provides the model guess parameters based on the details of
+//  the given source.
+typedef bool (*pmModelGuessFunc)(pmModel *model, pmSource *source);
+
+//  This function constructs the PSF model for the given source based on the
+//  supplied psf and the EXT model for the object.
+typedef bool (*pmModelFromPSFFunc)(pmModel *modelPSF, pmModel *modelEXT, const pmPSF *psf);
+
+//  This function sets the model parameters based on the PSF for a given coordinate and central
+//  intensity
+typedef bool (*pmModelParamsFromPSF)(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
+
+//  This function returns the success / failure status of the given model fit
+typedef bool (*pmModelFitStatusFunc)(pmModel *model);
+
+/** pmModel data structure
+ *
+ * Every source may have two types of models: a PSF model and a EXT (extended-source)
+ * model. The PSF model represents the best fit of the image PSF to the specific
+ * object. In this case, the PSF-dependent parameters are specified for the
+ * object by the PSF, not by the fit. The EXT model represents the best fit of
+ * the given model to the object, with all shape parameters floating in the fit.
+ *
+ */
+struct pmModel {
+    pmModelType type;                   ///< Model to be used.
+    psVector *params;                   ///< Paramater values.
+    psVector *dparams;                  ///< Parameter errors.
+    float chisq;                        ///< Fit chi-squared.
+    float chisqNorm;                    ///< re-normalized fit chi-squared.
+    float mag;				///< integrated model magnitude 
+    float magErr;			///< integrated model magnitude error
+    int nDOF;                           ///< number of degrees of freedom
+    int nIter;                          ///< number of iterations to reach min
+    pmModelStatus flags;                ///< model status flags
+    float radiusFit;                    ///< fit radius actually used
+    pmResiduals *residuals;             ///< normalized PSF residuals
+
+    // functions for this model which depend on the model class
+    pmModelFunc          modelFunc;
+    pmModelFlux          modelFlux;
+    pmModelRadius        modelRadius;
+    pmModelLimits        modelLimits;
+    pmModelGuessFunc     modelGuess;
+    pmModelFromPSFFunc   modelFromPSF;
+    pmModelParamsFromPSF modelParamsFromPSF;
+    pmModelFitStatusFunc modelFitStatus;
+};
+
+/** Symbolic names for the elements of [d]params
+ * Note: these are #defines not enums as a given element of [d]params
+ * may/will correspond to different parameters in different contexts
+ */
+#define PM_PAR_SKY 0   ///< Sky
+#define PM_PAR_I0 1   ///< Central intensity
+#define PM_PAR_XPOS 2   ///< X centre of object
+#define PM_PAR_YPOS 3   ///< Y centre of object
+#define PM_PAR_SXX 4   ///< shape X^2 moment
+#define PM_PAR_SYY 5   ///< shape Y^2 moment
+#define PM_PAR_SXY 6   ///< shape XY moment
+#define PM_PAR_7 7   ///< ??? Unknown parameter
+#define PM_PAR_8 8   ///< ??? Unknown parameter
+
+/** pmModelAlloc()
+ *
+ */
+pmModel *pmModelAlloc(pmModelType type);
+bool psMemCheckModel(psPtr ptr);
+
+// copy model to a new structure
+pmModel *pmModelCopy (pmModel *model);
+
+psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row);
+psF32 pmModelEvalWithOffset(pmModel *model, psImage *image, psS32 col, psS32 row, int dx, int dy);
+
+/** pmModelAdd()
+ *
+ * Add the given source model flux to/from the provided image. The boolean
+ * option center selects if the source is re-centered to the image center or if
+ * it is placed at its centroid location. The boolean option sky selects if the
+ * background sky is applied (TRUE) or not. The pixel range in the target image
+ * is at most the pixel range specified by the source.pixels image. The success
+ * status is returned.
+ *
+ */
+bool pmModelAdd(
+    psImage *image,                     ///< The output image (float)
+    psImage *mask,                      ///< The image pixel mask (valid == 0)
+    pmModel *model,                     ///< The input pmModel
+    pmModelOpMode mode,                 ///< mode to control how the model is added into the image
+    psMaskType maskVal                  ///< Value to mask
+);
+
+/** pmModelSub()
+ *
+ * Subtract the given source model flux to/from the provided image. The
+ * boolean option center selects if the source is re-centered to the image center
+ * or if it is placed at its centroid location. The boolean option sky selects if
+ * the background sky is applied (TRUE) or not. The pixel range in the target
+ * image is at most the pixel range specified by the source.pixels image. The
+ * success status is returned.
+ *
+ */
+bool pmModelSub(
+    psImage *image,                     ///< The output image (float)
+    psImage *mask,                      ///< The image pixel mask (valid == 0)
+    pmModel *model,                     ///< The input pmModel
+    pmModelOpMode mode,                 ///< mode to control how the model is added into the image
+    psMaskType maskVal                  ///< Value to mask
+);
+
+bool pmModelAddWithOffset(psImage *image,
+                          psImage *mask,
+                          pmModel *model,
+                          pmModelOpMode mode,
+                          psMaskType maskVal,
+                          int dx,
+                          int dy);
+
+bool pmModelSubWithOffset(psImage *image,
+                          psImage *mask,
+                          pmModel *model,
+                          pmModelOpMode mode,
+                          psMaskType maskVal,
+                          int dx,
+                          int dy);
+
+/** pmModelFitStatus()
+ *
+ * This function wraps the call to the model-specific function returned by
+ * pmModelFitStatusFunc_GetFunction.  The model-specific function examines the
+ * model parameters, parameter errors, Chisq, S/N, and other parameters available
+ * from model to decide if the particular fit was successful or not.
+ *
+ * XXX: Must code this.
+ *
+ */
+bool pmModelFitStatus(
+    pmModel *model                      ///< Model to be used
+);
+
+/// @}
+# endif /* PM_MODEL_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelClass.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelClass.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelClass.c	(revision 22322)
@@ -0,0 +1,165 @@
+/** @file  pmModelClass.c
+ *
+ *  Functions to define and manipulate object model attributes
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-27 03:14:57 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmErrorCodes.h"
+
+// XXX shouldn't these be defined for us in pslib.h ???
+double hypot(double x, double y);
+double sqrt (double x);
+
+# include "models/pmModel_GAUSS.c"
+# include "models/pmModel_PGAUSS.c"
+# include "models/pmModel_QGAUSS.c"
+# include "models/pmModel_RGAUSS.c"
+# include "models/pmModel_SERSIC.c"
+
+static pmModelClass defaultModels[] = {
+    {"PS_MODEL_GAUSS",        7, pmModelFunc_GAUSS,   pmModelFlux_GAUSS,   pmModelRadius_GAUSS,   pmModelLimits_GAUSS,   pmModelGuess_GAUSS,  pmModelFromPSF_GAUSS,  pmModelParamsFromPSF_GAUSS,  pmModelFitStatus_GAUSS},
+    {"PS_MODEL_PGAUSS",       7, pmModelFunc_PGAUSS,  pmModelFlux_PGAUSS,  pmModelRadius_PGAUSS,  pmModelLimits_PGAUSS,  pmModelGuess_PGAUSS, pmModelFromPSF_PGAUSS, pmModelParamsFromPSF_PGAUSS, pmModelFitStatus_PGAUSS},
+    {"PS_MODEL_QGAUSS",       8, pmModelFunc_QGAUSS,  pmModelFlux_QGAUSS,  pmModelRadius_QGAUSS,  pmModelLimits_QGAUSS,  pmModelGuess_QGAUSS, pmModelFromPSF_QGAUSS, pmModelParamsFromPSF_QGAUSS, pmModelFitStatus_QGAUSS},
+    {"PS_MODEL_RGAUSS",       8, pmModelFunc_RGAUSS,  pmModelFlux_RGAUSS,  pmModelRadius_RGAUSS,  pmModelLimits_RGAUSS,  pmModelGuess_RGAUSS, pmModelFromPSF_RGAUSS, pmModelParamsFromPSF_RGAUSS, pmModelFitStatus_RGAUSS},
+    {"PS_MODEL_SERSIC",       8, pmModelFunc_SERSIC,  pmModelFlux_SERSIC,  pmModelRadius_SERSIC,  pmModelLimits_SERSIC,  pmModelGuess_SERSIC, pmModelFromPSF_SERSIC, pmModelParamsFromPSF_SERSIC, pmModelFitStatus_SERSIC}
+};
+
+static pmModelClass *models = NULL;
+static int Nmodels = 0;
+
+static void ModelClassFree (pmModelClass *modelClass)
+{
+    if (modelClass == NULL)
+        return;
+    return;
+}
+
+pmModelClass *pmModelClassAlloc (int nModels)
+{
+    pmModelClass *modelClass = (pmModelClass *) psAlloc (nModels * sizeof(pmModelClass));
+    psMemSetDeallocator(modelClass, (psFreeFunc) ModelClassFree);
+    return (modelClass);
+}
+
+bool psMemCheckModelClass(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) ModelClassFree);
+}
+
+void pmModelClassAdd (pmModelClass *model)
+{
+    if (models == NULL) {
+        pmModelClassInit();
+    }
+
+    Nmodels ++;
+    models = (pmModelClass *) psRealloc (models, Nmodels*sizeof(pmModelClass));
+    models[Nmodels-1] = model[0];
+    return;
+}
+
+bool pmModelClassInit (void)
+{
+    // if we do not need to init, return false;
+    if (models != NULL) {
+        return false;
+    }
+
+    int Nnew = sizeof (defaultModels) / sizeof (pmModelClass);
+
+    models = pmModelClassAlloc (Nnew);
+    for (int i = 0; i < Nnew; i++) {
+        models[i] = defaultModels[i];
+    }
+    Nmodels = Nnew;
+    return true;
+}
+
+pmModelClass *pmModelClassSelect (pmModelType type)
+{
+    if (models == NULL) {
+        pmModelClassInit();
+    }
+
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (&models[type]);
+}
+
+void pmModelClassCleanup (void)
+{
+    psFree (models);
+    models = NULL;
+    Nmodels = 0;
+    return;
+}
+
+psS32 pmModelClassParameterCount (pmModelType type)
+{
+    if (models == NULL) {
+        pmModelClassInit();
+    }
+
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (0);
+    }
+    return (models[type].nParams);
+}
+
+psS32 pmModelClassGetType (const char *name)
+{
+    if (models == NULL) {
+        pmModelClassInit();
+    }
+
+    for (int i = 0; i < Nmodels; i++) {
+        if (!strcmp(models[i].name, name)) {
+            return (i);
+        }
+    }
+    return (-1);
+}
+
+char *pmModelClassGetName (pmModelType type)
+{
+    if (models == NULL) {
+        pmModelClassInit();
+    }
+
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].name);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelClass.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelClass.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelClass.h	(revision 22322)
@@ -0,0 +1,76 @@
+/* @file  pmModelClass.h
+ *
+ * The object model function types are desined to allow for the flexible addition of new
+ * object models. Every object model, with parameters represented by pmModel, has an
+ * associated set of functions which provide necessary support operations.  A These
+ * functions allow the programmer to select the approriate function or property for a
+ * specific object model class.
+ *
+ * Every model instance belongs to a class of models, defined by the value of the
+ * pmModelType type entry. Various functions need access to information about each of the
+ * models. Some of this information varies from model to model, and may depend on the
+ * current parameter values or other data quantities. In order to keep the code from
+ * requiring the information about each model to be coded into the low-level fitting
+ * routines, we define a collection of functions which allow us to abstract this type of
+ * model-dependent information. These generic functions take the model type and return the
+ * corresponding function pointer for the specified model. Each model is defined by
+ * creating this collection of specific functions, and placing them in a single file for
+ * each model. We define the following structure to carry the collection of information
+ * about the models.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-27 03:14:57 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_MODEL_CLASS_H
+# define PM_MODEL_CLASS_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+typedef struct
+{
+    char *name;
+    int nParams;
+    pmModelFunc          modelFunc;
+    pmModelFlux          modelFlux;
+    pmModelRadius        modelRadius;
+    pmModelLimits        modelLimits;
+    pmModelGuessFunc     modelGuess;
+    pmModelFromPSFFunc   modelFromPSF;
+    pmModelParamsFromPSF modelParamsFromPSF;
+    pmModelFitStatusFunc modelFitStatus;
+} pmModelClass;
+
+// allocate a pmModelClass to hold nModels entries
+pmModelClass *pmModelClassAlloc (int nModels);
+
+//
+bool psMemCheckModelClass(psPtr ptr);
+
+// initialize the internal (static) model class with the default models
+bool pmModelClassInit (void);
+
+// free the internal (static) model class
+void pmModelClassCleanup (void);
+
+// add a new model class to the collection of model classes
+void pmModelClassAdd (pmModelClass *modelClass);
+
+// get the specified model class
+pmModelClass *pmModelClassSelect (pmModelType type);
+
+// This function returns the number of parameters used by the listed function.
+int pmModelClassParameterCount (pmModelType type);
+
+// This function returns the user-space model names for the specified model type.
+char *pmModelClassGetName (pmModelType type);
+
+// This function returns the internal model type code for the user-space model names.
+pmModelType pmModelClassGetType (const char *name);
+
+/// @}
+# endif /* PM_MODEL_CLASS_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelGroup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelGroup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelGroup.c	(revision 22322)
@@ -0,0 +1,202 @@
+/** @file  pmModelGroup.c
+ *
+ *  Functions to define and manipulate object model attributes
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-10 01:09:20 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmModel.h"
+#include "pmModelGroup.h"
+#include "pmErrorCodes.h"
+
+// XXX shouldn't these be defined for us in pslib.h ???
+double hypot(double x, double y);
+double sqrt (double x);
+
+# include "models/pmModel_GAUSS.c"
+# include "models/pmModel_PGAUSS.c"
+# include "models/pmModel_QGAUSS.c"
+# include "models/pmModel_RGAUSS.c"
+# include "models/pmModel_SERSIC.c"
+
+static pmModelGroup defaultModels[] = {
+                                          {"PS_MODEL_GAUSS",        7, pmModelFunc_GAUSS,   pmModelFlux_GAUSS,   pmModelRadius_GAUSS,   pmModelLimits_GAUSS,   pmModelGuess_GAUSS,  pmModelFromPSF_GAUSS,  pmModelFitStatus_GAUSS},
+                                          {"PS_MODEL_PGAUSS",       7, pmModelFunc_PGAUSS,  pmModelFlux_PGAUSS,  pmModelRadius_PGAUSS,  pmModelLimits_PGAUSS,  pmModelGuess_PGAUSS, pmModelFromPSF_PGAUSS, pmModelFitStatus_PGAUSS},
+                                          {"PS_MODEL_QGAUSS",       8, pmModelFunc_QGAUSS,  pmModelFlux_QGAUSS,  pmModelRadius_QGAUSS,  pmModelLimits_QGAUSS,  pmModelGuess_QGAUSS, pmModelFromPSF_QGAUSS, pmModelFitStatus_QGAUSS},
+                                          {"PS_MODEL_RGAUSS",       8, pmModelFunc_RGAUSS,  pmModelFlux_RGAUSS,  pmModelRadius_RGAUSS,  pmModelLimits_RGAUSS,  pmModelGuess_RGAUSS, pmModelFromPSF_RGAUSS, pmModelFitStatus_RGAUSS},
+                                          {"PS_MODEL_SERSIC",       8, pmModelFunc_SERSIC,  pmModelFlux_SERSIC,  pmModelRadius_SERSIC,  pmModelLimits_SERSIC,  pmModelGuess_SERSIC, pmModelFromPSF_SERSIC, pmModelFitStatus_SERSIC}
+                                      };
+
+static pmModelGroup *models = NULL;
+static int Nmodels = 0;
+
+static void ModelGroupFree (pmModelGroup *modelGroup)
+{
+
+    if (modelGroup == NULL)
+        return;
+    return;
+}
+
+pmModelGroup *pmModelGroupAlloc (int nModels)
+{
+
+    pmModelGroup *modelGroup = (pmModelGroup *) psAlloc (nModels * sizeof(pmModelGroup));
+    psMemSetDeallocator(modelGroup, (psFreeFunc) ModelGroupFree);
+    return (modelGroup);
+}
+
+bool psMemCheckModelGroup(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) ModelGroupFree);
+}
+
+void pmModelGroupAdd (pmModelGroup *model)
+{
+
+    if (models == NULL) {
+        pmModelGroupInit ();
+    }
+
+    Nmodels ++;
+    models = (pmModelGroup *) psRealloc (models, Nmodels*sizeof(pmModelGroup));
+    models[Nmodels-1] = model[0];
+    return;
+}
+
+bool pmModelGroupInit (void)
+{
+
+    // if we do not need to init, return false;
+    if (models != NULL)
+        return false;
+
+    int Nnew = sizeof (defaultModels) / sizeof (pmModelGroup);
+
+    models = pmModelGroupAlloc (Nnew);
+    for (int i = 0; i < Nnew; i++) {
+        models[i] = defaultModels[i];
+    }
+    Nmodels = Nnew;
+    return true;
+}
+
+void pmModelGroupCleanup (void)
+{
+    psFree (models);
+    models = NULL;
+    return;
+}
+
+pmModelFunc pmModelFunc_GetFunction (pmModelType type)
+{
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelFunc);
+}
+
+pmModelFlux pmModelFlux_GetFunction (pmModelType type)
+{
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelFlux);
+}
+
+pmModelRadius pmModelRadius_GetFunction (pmModelType type)
+{
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelRadius);
+}
+
+pmModelLimits pmModelLimits_GetFunction (pmModelType type)
+{
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelLimits);
+}
+
+pmModelGuessFunc pmModelGuessFunc_GetFunction (pmModelType type)
+{
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelGuessFunc);
+}
+
+pmModelFitStatusFunc pmModelFitStatusFunc_GetFunction (pmModelType type)
+{
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelFitStatusFunc);
+}
+
+pmModelFromPSFFunc pmModelFromPSFFunc_GetFunction (pmModelType type)
+{
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelFromPSFFunc);
+}
+
+psS32 pmModelParameterCount (pmModelType type)
+{
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (0);
+    }
+    return (models[type].nParams);
+}
+
+psS32 pmModelSetType (char *name)
+{
+    for (int i = 0; i < Nmodels; i++) {
+        if (!strcmp(models[i].name, name)) {
+            return (i);
+        }
+    }
+    return (-1);
+}
+
+char *pmModelGetType (pmModelType type)
+{
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].name);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelGroup.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelGroup.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelGroup.h	(revision 22322)
@@ -0,0 +1,201 @@
+/* @file  pmModelGroup.h
+ *
+ * The object model function types are desined to allow for the flexible addition of new object
+ * models. Every object model, with parameters represented by pmModel, has an associated set of
+ * functions which provide necessary support operations. A set of abstract functions allow the
+ * programmer to select the approriate function or property for a specific named object model.
+ *
+ * Every model instance belongs to a class of models, defined by the value of
+ * the pmModelType type entry. Various functions need access to information about
+ * each of the models. Some of this information varies from model to model, and
+ * may depend on the current parameter values or other data quantities. In order
+ * to keep the code from requiring the information about each model to be coded
+ * into the low-level fitting routines, we define a collection of functions which
+ * allow us to abstract this type of model-dependent information. These generic
+ * functions take the model type and return the corresponding function pointer
+ * for the specified model. Each model is defined by creating this collection of
+ * specific functions, and placing them in a single file for each model. We
+ * define the following structure to carry the collection of information about
+ * the models.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-10 01:09:20 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_MODEL_GROUP_H
+# define PM_MODEL_GROUP_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+//  This function is the model chi-square minimization function for this model.
+typedef psMinimizeLMChi2Func pmModelFunc;
+
+//  This function sets the parameter limits for this model.
+typedef psMinimizeLMLimitFunc pmModelLimits;
+
+// This function returns the integrated flux for the given model parameters.
+typedef psF64 (*pmModelFlux)(const psVector *params);
+
+// This function returns the radius at which the given model and parameters
+// achieves the given flux.
+typedef psF64 (*pmModelRadius)(const psVector *params, double flux);
+
+//  This function provides the model guess parameters based on the details of
+//  the given source.
+typedef bool (*pmModelGuessFunc)(pmModel *model, pmSource *source);
+
+//  This function constructs the PSF model for the given source based on the
+//  supplied psf and the EXT model for the object.
+typedef bool (*pmModelFromPSFFunc)(pmModel *modelPSF, pmModel *modelEXT, pmPSF *psf);
+
+//  This function sets the model parameters based on the PSF for a given coordinate and central
+//  intensity
+typedef bool (*pmModelParamsFromPSF)(pmModel *model, pmPSF *psf, float Xo, float Yo, float Io);
+
+//  This function returns the success / failure status of the given model fit
+typedef bool (*pmModelFitStatusFunc)(pmModel *model);
+
+typedef struct
+{
+    char *name;
+    int nParams;
+    pmModelFunc          modelFunc;
+    pmModelFlux          modelFlux;
+    pmModelRadius        modelRadius;
+    pmModelLimits        modelLimits;
+    pmModelGuessFunc     modelGuessFunc;
+    pmModelFromPSFFunc   modelFromPSFFunc;
+    pmModelParamsFromPSF modelParamsFromPSF;
+    pmModelFitStatusFunc modelFitStatusFunc;
+}
+pmModelGroup;
+
+// allocate a pmModelGroup to hold nModels entries
+pmModelGroup *pmModelGroupAlloc (int nModels);
+
+bool psMemCheckModelGroup(psPtr ptr);
+
+// initialize the internal (static) model group with the default models
+bool pmModelGroupInit (void);
+
+// free the internal (static) model group
+void pmModelGroupCleanup (void);
+
+// add a new model to the internal (static) model group
+void pmModelGroupAdd (pmModelGroup *model);
+
+/* This function returns the number of parameters used by the listed function.
+ */
+int pmModelParameterCount(
+    pmModelType type                    ///< Add comment.
+);
+
+
+/* This function returns the user-space model names for the specified model type.
+ */
+char *pmModelGetType(
+    pmModelType type                    ///< Add comment.
+);
+
+
+/**
+ * 
+ * This function returns the internal model type code for the user-space model names.
+ * 
+ */
+pmModelType pmModelSetType(
+    char *name                          ///< Add comment.
+);
+
+/**
+ * 
+ *  Each of the function types above has a corresponding function which returns
+ *  the function given the model type:
+ * 
+ */
+
+/**
+ * 
+ * pmModelFunc is the function used to determine the value of the model at a
+ * specific coordinate, and is the one used by psMinimizeLMChi2.
+ * 
+ */
+pmModelFunc          pmModelFunc_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelFlux returns the total integrated flux for the given input parameters.
+ * 
+ */
+pmModelFlux          pmModelFlux_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelRadius returns the scaling radius at which the flux of the model
+ * matches the specified flux. This presumes that the model is a function of an
+ * elliptical contour.
+ * 
+ */
+pmModelRadius        pmModelRadius_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelLimits sets the parameter limit vectors for the function.
+ * 
+ */
+pmModelLimits        pmModelLimits_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelGuessFunc generates an initial guess for the model based on the
+ * provided source statistics (moments and pixel values as needed).
+ * 
+ */
+pmModelGuessFunc     pmModelGuessFunc_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelFromPSFFunc takes as input a representation of the psf and a value
+ * for the model and fills in the PSF parameters of the model. The input
+ * primarily relies upon the centroid coordinates of the input model, though the
+ * normalization may potentially be used.
+ * 
+ */
+pmModelFromPSFFunc   pmModelFromPSFFunc_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelFitStatusFunc returns a true or false values based on the success or
+ * failure of a model fit.  The success is determined by quantities such as the
+ * chisq or the signal-to-noise.
+ * 
+ */
+pmModelFitStatusFunc pmModelFitStatusFunc_GetFunction (pmModelType type);
+
+
+/** pmSourceModelGuess()
+ *
+ * Convert available data to an initial guess for the given model. This
+ * function allocates a pmModel entry for the pmSource structure based on the
+ * provided model selection. The method of defining the model parameter guesses
+ * are specified for each model below. The guess values are placed in the model
+ * parameters. The function returns TRUE on success or FALSE on failure.
+ *
+ */
+pmModel *pmSourceModelGuess(
+    pmSource *source,   ///< The input pmSource
+    pmModelType model   ///< The type of model to be created.
+);
+
+/// @}
+# endif /* PM_MODEL_GROUP_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelUtils.c	(revision 22322)
@@ -0,0 +1,95 @@
+/** @file  pmModelUtils.c
+ *
+ *  Functions to manipulate object models
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-12-15 01:22:11 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmErrorCodes.h"
+#include "pmModelUtils.h"
+
+/*****************************************************************************
+pmModelFromPSF (*modelEXT, *psf):  use the model position parameters to
+construct a realization of the PSF model at the object coordinates
+ *****************************************************************************/
+pmModel *pmModelFromPSF (pmModel *modelEXT, const pmPSF *psf)
+{
+    PS_ASSERT_PTR_NON_NULL(psf, NULL);
+    PS_ASSERT_PTR_NON_NULL(modelEXT, NULL);
+
+    // allocate a new pmModel to hold the PSF version
+    pmModel *modelPSF = pmModelAlloc (psf->type);
+
+    // set model parameters for this source based on PSF information
+    if (!modelEXT->modelFromPSF (modelPSF, modelEXT, psf)) {
+        psError(PM_ERR_PSF, false, "Failed to set model params from PSF");
+        psFree(modelPSF);
+        return NULL;
+    }
+    // XXX note that model->residuals is just a reference
+    modelPSF->residuals = psf->residuals;
+
+    return (modelPSF);
+}
+
+// instantiate a model for the PSF at this location with peak flux
+// NOTE: psf and (Xo,Yo) are defined wrt chip coordinates
+pmModel *pmModelFromPSFforXY (const pmPSF *psf, float Xo, float Yo, float Io)
+{
+    PS_ASSERT_PTR_NON_NULL(psf, NULL);
+
+    // allocate a new pmModel to hold the PSF version
+    pmModel *modelPSF = pmModelAlloc (psf->type);
+
+    // set model parameters for this source based on PSF information
+    if (!modelPSF->modelParamsFromPSF (modelPSF, psf, Xo, Yo, Io)) {
+        psError(PM_ERR_PSF, false, "Failed to set model params from PSF");
+        psFree(modelPSF);
+        return NULL;
+    }
+
+    // XXX note that model->residuals is just a reference
+    modelPSF->residuals = psf->residuals;
+
+    return (modelPSF);
+}
+
+// set this model to have the requested flux
+bool pmModelSetFlux (pmModel *model, float flux) {
+    PS_ASSERT_PTR_NON_NULL(model, NULL);
+    PS_ASSERT_PTR_NON_NULL(model->params, NULL);
+
+    // set Io to be 1.0
+    model->params->data.F32[PM_PAR_I0] = 1.0;
+
+    // determine the normalized flux
+    float normFlux = model->modelFlux (model->params);
+    assert (isfinite(normFlux));
+    assert (normFlux > 0);
+
+    // set the desired normalization
+    model->params->data.F32[PM_PAR_I0] = flux / normFlux;
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelUtils.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelUtils.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmModelUtils.h	(revision 22322)
@@ -0,0 +1,47 @@
+/* @file  pmModelUtils.h
+ *
+ * Utility functions for working with pmSources
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-12-15 01:22:11 $
+ * Copyright 2007 IfA, University of Hawaii
+ */
+
+# ifndef PM_MODEL_UTILS_H
+# define PM_MODEL_UTILS_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/**
+ *
+ * This function constructs a pmModel instance based on the pmPSF description
+ * of the PSF. The input is a pmModel with at least the values of the centroid
+ * coordinates (possibly normalization if this is needed) defined. The values of
+ * the PSF-dependent parameters are specified for the specific realization based
+ * on the coordinates of the object.
+ *
+ */
+pmModel *pmModelFromPSF(
+    pmModel *model,                     ///< Add comment
+    const pmPSF *psf                    ///< Add comment
+);
+
+pmModel *pmModelFromPSFforXY (
+    const pmPSF *psf,
+    float Xo,
+    float Yo,
+    float Io
+    );
+
+bool pmModelSetFlux (
+    pmModel *model,
+    float flux
+    );
+
+
+
+/// @}
+# endif /* PM_MODEL_UTILS_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmMoments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmMoments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmMoments.c	(revision 22322)
@@ -0,0 +1,44 @@
+/** @file  pmMoments.c
+ *
+ *  Functions defining the pmMoments structure
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-09-15 09:49:01 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include "pmMoments.h"
+
+/******************************************************************************
+pmMomentsAlloc(): Allocate the pmMoments structure and initialize the members
+to zero.
+*****************************************************************************/
+pmMoments *pmMomentsAlloc()
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    pmMoments *tmp = (pmMoments *) psAlloc(sizeof(pmMoments));
+    tmp->x = 0.0;
+    tmp->y = 0.0;
+    tmp->Sx = 0.0;
+    tmp->Sy = 0.0;
+    tmp->Sxy = 0.0;
+    tmp->Sum = 0.0;
+    tmp->Peak = 0.0;
+    tmp->Sky = 0.0;
+    tmp->nPixels = 0;
+
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(tmp);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmMoments.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmMoments.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmMoments.h	(revision 22322)
@@ -0,0 +1,46 @@
+/* @file  pmMoments.h
+ * @brief Definitions of the moments structure
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-24 02:54:15 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_MOMENTS_H
+# define PM_MOMENTS_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/** pmMoments data structure
+ *
+ * One of the simplest measurements which can be made quickly for an object
+ * are the object moments. We specify a structure to carry the moment information
+ * for a specific source:
+ *
+ */
+typedef struct
+{
+    float x;     ///< X-coord of centroid.
+    float y;     ///< Y-coord of centroid.
+    float Sx;    ///< x-second moment.
+    float Sy;    ///< y-second moment.
+    float Sxy;   ///< xy cross moment.
+    float Sum;   ///< Pixel sum above sky (background).
+    float Peak;  ///< Peak counts above sky.
+    float Sky;   ///< Sky level (background).
+    float dSky;  ///< local Sky variance
+    float SN;    ///< approx signal-to-noise
+    int nPixels; ///< Number of pixels used.
+}
+pmMoments;
+
+/** pmMomentsAlloc()
+ *
+ */
+pmMoments *pmMomentsAlloc();
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmObjects.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmObjects.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmObjects.c	(revision 22322)
@@ -0,0 +1,25 @@
+/** @file  pmObjects.c
+ *
+ *  This file will ...
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA: significant modifications.
+ *
+ *  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-09-15 09:49:01 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmObjects.h"
+#include "pmModelGroup.h"
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmObjects.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmObjects.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmObjects.h	(revision 22322)
@@ -0,0 +1,68 @@
+/* @file  pmObjects.h
+ *
+ * The process of finding, measuring, and classifying astronomical sources on
+ * images is one of the critical tasks of the IPP or any astronomical software
+ * system. This file will define structures and functions related to the task
+ * of source detection and measurement. The elements defined in this section
+ * are generally low-level components which can be connected together to
+ * construct a complete object measurement suite.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-24 02:54:15 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PM_OBJECTS_H
+#define PM_OBJECTS_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+#include <stdio.h>
+#include <math.h>
+#include <pslib.h>
+
+/**
+ *
+ * This function converts the source classification into the closest available
+ * approximation to the Dophot classification scheme:
+ * XXX EAM : fix this to use current source classification scheme
+ *
+ * PM_SOURCE_DEFECT: 8
+ * PM_SOURCE_SATURATED: 8
+ * PM_SOURCE_SATSTAR: 10
+ * PM_SOURCE_PSFSTAR: 1
+ * PM_SOURCE_GOODSTAR: 1
+ * PM_SOURCE_POOR_FIT_PSF: 7
+ * PM_SOURCE_FAIL_FIT_PSF: 4
+ * PM_SOURCE_FAINTSTAR: 4
+ * PM_SOURCE_GALAXY: 2
+ * PM_SOURCE_FAINT_GALAXY: 2
+ * PM_SOURCE_DROP_GALAXY: 2
+ * PM_SOURCE_FAIL_FIT_GAL: 2
+ * PM_SOURCE_POOR_FIT_GAL: 2
+ * PM_SOURCE_OTHER: ?
+ *
+ */
+int pmSourceDophotType(
+    pmSource *source                    ///< Add comment.
+);
+
+
+/** pmSourceSextractType()
+ *
+ * This function converts the source classification into the closest available
+ * approximation to the Sextractor classification scheme. the correspondence is
+ * not yet defined (TBD) .
+ *
+ * XXX: Must code this.
+ *
+ */
+int pmSourceSextractType(
+    pmSource *source                    ///< Add comment.
+);
+
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF.c	(revision 22322)
@@ -0,0 +1,505 @@
+/** @file  pmPSF.c
+ *
+ * This file contains typedefs for the Point-Spread Function and prototypes
+ * for functions that calculate the PSF.
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.36 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-07-15 20:25:00 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+
+#include <strings.h>  // for strcasecmp
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmModelUtils.h"
+#include "pmSourcePhotometry.h"
+#include "pmFPAMaskWeight.h"
+#include "psVectorBracket.h"
+#include "pmErrorCodes.h"
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+static void pmPSFOptionsFree (pmPSFOptions *options) {
+
+    if (!options) return;
+
+    psFree (options->stats);
+    return;
+}
+
+pmPSFOptions *pmPSFOptionsAlloc () {
+
+    pmPSFOptions *options = (pmPSFOptions *) psAlloc(sizeof(pmPSFOptions));
+    psMemSetDeallocator(options, (psFreeFunc) pmPSFOptionsFree);
+
+    options->type          = 0;
+
+    options->stats         = NULL;
+
+    options->psfTrendMode  = PM_TREND_NONE;
+    options->psfTrendNx    = 0;
+    options->psfTrendNy    = 0;
+    options->psfFieldNx    = 0;
+    options->psfFieldNy    = 0;
+    options->psfFieldXo    = 0;
+    options->psfFieldYo    = 0;
+
+    options->poissonErrorsPhotLMM = true;
+    options->poissonErrorsPhotLin = false;
+    options->poissonErrorsParams  = true;
+
+    return options;
+}
+
+bool psMemCheckPSFOptions(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmPSFOptionsFree);
+}
+
+/*****************************************************************************
+pmPSFFree(psf): function to free a pmPSF structure
+ *****************************************************************************/
+static void pmPSFFree (pmPSF *psf)
+{
+    if (psf == NULL) {
+        return;
+    }
+
+    psFree (psf->ChiTrend);
+    psFree (psf->psfTrendStats);
+    psFree (psf->ApTrend);
+    psFree (psf->FluxScale);
+    psFree (psf->growth);
+    psFree (psf->params);
+    psFree (psf->residuals);
+    return;
+}
+
+/*****************************************************************************
+ pmPSFAlloc (type): allocate a pmPSF.
+
+ NOTE: PSF model parameters which are not modeled on an image are set to NULL
+ in psf->params.
+
+ These are normally:
+
+ X-center
+ Y-center
+ Sky background value
+ Object Normalization
+ *****************************************************************************/
+pmPSF *pmPSFAlloc (const pmPSFOptions *options)
+{
+    PS_ASSERT_PTR_NON_NULL(options, NULL);
+    int Nparams;
+
+    pmPSF *psf = (pmPSF *) psAlloc(sizeof(pmPSF));
+    psMemSetDeallocator(psf, (psFreeFunc) pmPSFFree);
+
+    psf->type     = options->type;
+    psf->chisq    = 0.0;
+    psf->ApResid  = 0.0;
+    psf->dApResid = 0.0;
+    psf->skyBias  = 0.0;
+    psf->skySat   = 0.0;
+    psf->nPSFstars  = 0;
+    psf->nApResid   = 0;
+
+    psf->poissonErrorsPhotLMM = options->poissonErrorsPhotLMM;
+    psf->poissonErrorsPhotLin = options->poissonErrorsPhotLin;
+    psf->poissonErrorsParams = options->poissonErrorsParams;
+
+    // the ApTrend components are (x, y).  It may be represented with a polynomial or with a
+    // psImageMap.  We set it initially to NULL. the user must allocate it before using.
+    // psf->ApTrend = pmTrend2DAlloc (PM_TREND_MAP, Nx, Ny, 1, 1, stats);
+    psf->ApTrend = NULL;
+
+    // the flux scale is the relationship between the integrated flux and the peak flux for a
+    // psf model.  this is a 2D function of position, and is modeled with pmTrend2D after the
+    // psf model is determined for an image.  until it is determined, the flux calculation
+    // integrates the sources
+    psf->FluxScale = NULL;
+
+    if (psf->poissonErrorsPhotLMM) {
+        psf->ChiTrend = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 1);
+    } else {
+        psf->ChiTrend = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 2);
+    }
+
+    // don't define a growth curve : user needs to choose radius bins
+    psf->growth = NULL;
+
+    // by default, we do not construct the residual image
+    psf->residuals = NULL;
+
+    Nparams = pmModelClassParameterCount (options->type);
+    if (!Nparams) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return(NULL);
+    }
+    psf->params = psArrayAlloc(Nparams);
+
+    // save the trend stats on the psf for use in pmPSFFromPSFtry
+    psf->psfTrendStats = psMemIncrRefCounter (options->stats);
+
+    // the psf parameters may have 2D variations represented as either a polynomial (ordinary
+    // or chebychev) or as an image map.  The size of the image map is determined by pmPSFtry
+    // by minimizing the scatter in the fitted data and the rms error in the complete image
+    // map.  In this case, the user-supplied options of psfTrendNx and psfTrendNy are the
+    // maximum value used for these axes.  For the polynomial terms, the order is not currently
+    // set, dynamically.  The value of psfTrendNx and psfTrendNy are used.
+
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXruff = options->psfTrendNx;
+    binning->nYruff = options->psfTrendNy;
+    binning->nXfine = options->psfFieldNx;
+    binning->nYfine = options->psfFieldNy;
+
+    // for polynomial representations, nXruff, nYruff are the order number, and may be 0.
+    // in this case, we cannot set the psImageBinning scale because 0 would be invalid.  these
+    // elements are only used by the image map representation
+    if (options->psfTrendMode == PM_TREND_MAP) {
+        psImageBinningSetScale (binning, PS_IMAGE_BINNING_CENTER);
+        psImageBinningSetSkipByOffset (binning, options->psfFieldXo, options->psfFieldYo);
+    }
+
+    // trendNx & trendNy are used in pmPSFtry as the max for these values
+    psf->psfTrendMode = options->psfTrendMode;
+    psf->trendNx      = options->psfTrendNx;
+    psf->trendNy      = options->psfTrendNy;
+    psf->fieldNx      = options->psfFieldNx;
+    psf->fieldNy      = options->psfFieldNy;
+    psf->fieldXo      = options->psfFieldXo;
+    psf->fieldYo      = options->psfFieldYo;
+
+    // define the parameter trends
+    if (options->psfTrendMode != PM_TREND_NONE) {
+        for (int i = 0; i < psf->params->n; i++) {
+            if (i == PM_PAR_SKY) continue;
+            if (i == PM_PAR_I0) continue;
+            if (i == PM_PAR_XPOS) continue;
+            if (i == PM_PAR_YPOS) continue;
+
+            psf->params->data[i] = pmTrend2DNoImageAlloc (options->psfTrendMode, binning, options->stats);
+        }
+    }
+    psFree (binning);
+    return psf;
+}
+
+bool psMemCheckPSF(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmPSFFree);
+}
+
+// the PSF models the \sigma_{xy} variation of the elliptical contour as a function of position in the image with a
+// polynomial.  an individual object has a contour of the form (x^2/2sx^2) + (y^2/2sy^2) + sxy*x*y
+// these are the values of the model->params.  the psf->params term for sxy is actually fitted
+// to sxy/(sxx^-2 + syy^-2)^2
+
+// XXX this is only an approximate solution.  A better solution would be to fit the second moment, Mxy. the
+// problem here is that converting from Mxy,SXX,SYY -> SXY is a third order problem:
+// Mxy = SXY * (SXX^-4 + SYY^-4 - 2 SXY ^2)
+
+// input: model->param, output: psf->param[PM_PAR_SXY]
+double pmPSF_SXYfromModel (psF32 *modelPar)
+{
+    PS_ASSERT_PTR_NON_NULL(modelPar, NAN);
+
+    double SXX = modelPar[PM_PAR_SXX];
+    double SYY = modelPar[PM_PAR_SYY];
+    double SXY = modelPar[PM_PAR_SXY];
+
+    double par = SXY / PS_SQR(1.0 / PS_SQR(SXX) + 1.0 / PS_SQR(SYY));
+    return (par);
+}
+
+// input: fitted psf->param, output: model->param[PM_PAR_SXY]
+double pmPSF_SXYtoModel (psF32 *fittedPar)
+{
+    PS_ASSERT_PTR_NON_NULL(fittedPar, NAN);
+
+    double SXX = fittedPar[PM_PAR_SXX];
+    double SYY = fittedPar[PM_PAR_SYY];
+    double fit = fittedPar[PM_PAR_SXY];
+
+    double SXY = fit * PS_SQR(1.0 / PS_SQR(SXX) + 1.0 / PS_SQR(SYY));
+
+    assert (!isnan(SXY));
+
+    return SXY;
+}
+
+// New Concept: the PSF modelling function fits the polarization terms e0, e1, e2:
+
+// convert the parameters used in the fitted source model
+// to the parameters used in the 2D PSF model
+bool pmPSF_FitToModel (psF32 *fittedPar, float minMinorAxis)
+{
+    PS_ASSERT_PTR_NON_NULL(fittedPar, false);
+
+    psEllipsePol pol;
+
+    pol.e0 = fittedPar[PM_PAR_E0];
+    pol.e1 = fittedPar[PM_PAR_E1];
+    pol.e2 = fittedPar[PM_PAR_E2];
+
+    psEllipseAxes axes;
+    if (psEllipsePolToAxes (pol, minMinorAxis, &axes) != PS_ERR_NONE) {
+        psError(PM_ERR_PSF, false, "Failed to convert e[012] (%g,%g,%g) to axes",
+                pol.e0, pol.e1, pol.e2);
+        return false;
+    }
+    psEllipseShape shape = psEllipseAxesToShape (axes);
+
+    fittedPar[PM_PAR_SXX] = shape.sx * M_SQRT2;
+    fittedPar[PM_PAR_SYY] = shape.sy * M_SQRT2;
+    fittedPar[PM_PAR_SXY] = shape.sxy;
+
+    return true;
+}
+
+// convert the PSF parameters used in the 2D PSF model fit into the
+// parameters used in the source model
+psEllipsePol pmPSF_ModelToFit (psF32 *modelPar)
+{
+    // must assert non-NULL input parameter
+    psEllipsePol pol;
+    pol.e0 = NAN;
+    pol.e1 = NAN;
+    pol.e2 = NAN;
+    PS_ASSERT_PTR_NON_NULL(modelPar, pol);
+
+    psEllipseShape shape;
+
+    shape.sx  = modelPar[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = modelPar[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = modelPar[PM_PAR_SXY];
+
+    pol = psEllipseShapeToPol (shape);
+
+    return pol;
+}
+
+// convert the parameters used in the fitted source model to the psEllipseAxes representation
+// (major,minor,theta)
+psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR)
+{
+    psEllipseShape shape;
+    psEllipseAxes axes;
+    axes.major = NAN;
+    axes.minor = NAN;
+    axes.theta = NAN;
+//   XXX: must assert non-NULL input parameter
+    PS_ASSERT_PTR_NON_NULL(modelPar, axes);
+
+    shape.sx  = modelPar[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = modelPar[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = modelPar[PM_PAR_SXY];
+
+    if ((shape.sx == 0) || (shape.sy == 0)) {
+        axes.major = 0.0;
+        axes.minor = 0.0;
+        axes.theta = 0.0;
+    } else {
+        // XXX this is not really consistent with the model fit range above
+        axes = psEllipseShapeToAxes (shape, maxAR);
+    }
+
+    return axes;
+}
+
+// convert the psEllipseAxes representation (major,minor,theta) to the parameters used in the
+// fitted source model
+bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes)
+{
+    PS_ASSERT_PTR_NON_NULL(modelPar, false);
+
+    if ((axes.major <= 0) || (axes.minor <= 0)) {
+        modelPar[PM_PAR_SXX] = 0.0;
+        modelPar[PM_PAR_SYY] = 0.0;
+        modelPar[PM_PAR_SXY] = 0.0;
+        return true;
+    }
+
+    psEllipseShape shape = psEllipseAxesToShape (axes);
+
+    modelPar[PM_PAR_SXX] = shape.sx * M_SQRT2;
+    modelPar[PM_PAR_SYY] = shape.sy * M_SQRT2;
+    modelPar[PM_PAR_SXY] = shape.sxy;
+
+    return true;
+}
+
+// generate a psf model of the requested type, with fixed shape
+pmPSF *pmPSFBuildSimple (char *typeName, float sxx, float syy, float sxy, ...)
+{
+
+    va_list ap;
+    va_start(ap, sxy);
+
+    pmPSFOptions *options = pmPSFOptionsAlloc ();
+    options->type = pmModelClassGetType (typeName);
+    options->psfTrendMode = PM_TREND_POLY_ORD;
+    options->psfTrendNx = 0;
+    options->psfTrendNy = 0;
+
+    pmPSF *psf = pmPSFAlloc (options);
+
+    psVector *par = psVectorAlloc (psf->params->n, PS_TYPE_F32);
+    par->data.F32[PM_PAR_SXX] = sxx;
+    par->data.F32[PM_PAR_SYY] = syy;
+    par->data.F32[PM_PAR_SXY] = sxy;
+
+    psEllipsePol pol = pmPSF_ModelToFit(par->data.F32);
+
+    pmTrend2D *trend = NULL;
+
+    // set the psf shape parameters
+    trend = psf->params->data[PM_PAR_E0];
+    trend->poly->coeff[0][0] = pol.e0;
+
+    trend = psf->params->data[PM_PAR_E1];
+    trend->poly->coeff[0][0] = pol.e1;
+
+    trend = psf->params->data[PM_PAR_E2];
+    trend->poly->coeff[0][0] = pol.e2;
+
+    for (int i = PM_PAR_SXY + 1; i < psf->params->n; i++) {
+        trend = psf->params->data[i];
+        trend->poly->coeff[0][0] = (psF32)va_arg(ap, psF64);
+    }
+    va_end(ap);
+
+    psFree (par);
+    psFree (options);
+    return psf;
+}
+
+// we generate the growth curve for the center of the image with the specified psf model
+bool pmGrowthCurveGenerate (pmReadout *readout, pmPSF *psf, bool ignore, psMaskType maskVal, psMaskType markVal)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(readout->image, false);
+
+    // bool status;
+    float xc, yc, dx, dy;
+    float fitMag, apMag;
+    float radius;
+
+    // create template model
+    pmModel *modelRef = pmModelAlloc(psf->type);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // use the center of the center pixel of the image
+    xc = 0.5*readout->image->numCols + readout->image->col0 + 0.5;
+    yc = 0.5*readout->image->numRows + readout->image->row0 + 0.5;
+    dx = psf->growth->maxRadius + 1;
+    dy = psf->growth->maxRadius + 1;
+
+    // assign the x and y coords to the image center
+    // create an object with center intensity of 1000
+    modelRef->params->data.F32[PM_PAR_SKY] = 0;
+    modelRef->params->data.F32[PM_PAR_I0] = 1000;
+    modelRef->params->data.F32[PM_PAR_XPOS] = xc;
+    modelRef->params->data.F32[PM_PAR_YPOS] = yc;
+
+    // create modelPSF from this model
+    pmModel *model = pmModelFromPSF (modelRef, psf);
+
+    // measure the fitMag for this model
+    pmSourcePhotometryModel (&fitMag, model);
+    psf->growth->fitMag = fitMag;
+
+    // generate working image for this source
+    psRegion region = {xc - dx, xc + dx, yc - dy, yc + dy};
+
+    // force region to stop at dimensions of image
+    region = psRegionForImage (readout->image, region);
+
+    // the view, image, and mask retain col0,row0
+    psImage *view = psImageSubset (readout->image, region);
+    psImage *image = psImageCopy (NULL, view, PS_TYPE_F32);
+    psImage *mask = psImageCopy (NULL, view, PS_TYPE_U8);
+
+    psImageInit (image, 0.0);
+    psImageInit (mask, 0);
+
+    // place the reference object in the image center
+    // no need to mask the source here
+    // XXX should we measure this for the analytical model only or the full model?
+    pmModelAdd (image, NULL, model, PM_MODEL_OP_FULL, maskVal);
+
+    // loop over a range of source fluxes
+    // no need to interpolate since we have forced the object center
+    // to 0.5, 0.5 above
+    bool completeGrowthCurve = true;            // do we have a complete curve of growth?
+    for (int i = 0; i < psf->growth->radius->n; i++) {
+
+        radius = psf->growth->radius->data.F32[i];
+
+        // mask the given aperture and measure the apMag
+        psImageKeepCircle (mask, xc, yc, radius, "OR", markVal);
+        if (!pmSourcePhotometryAper (&apMag, model, image, mask, maskVal)) {
+            psError(PM_ERR_PHOTOM, false, "Measuring apMag for radius == %g", radius);
+            psErrorStackPrint (stderr, "failure to get growth curve");
+            psErrorClear();
+            completeGrowthCurve = false;
+            break;
+        }
+        psImageKeepCircle (mask, xc, yc, radius, "AND", PS_NOT_U8(markVal));
+
+        // the 'ignore' mode is for testing
+        if (ignore) {
+            psf->growth->apMag->data.F32[i] = fitMag;
+        } else {
+            psf->growth->apMag->data.F32[i] = apMag;
+        }
+    }
+
+    if (completeGrowthCurve) {
+        psf->growth->apRef = psVectorInterpolate (psf->growth->radius, psf->growth->apMag, psf->growth->refRadius);
+        psf->growth->apLoss = psf->growth->fitMag - psf->growth->apRef;
+    } else {
+        psf->growth->apRef = NAN;
+        psf->growth->apLoss = 0;
+    }
+
+    psLogMsg ("psphot.growth", 4, "GrowthCurve : apLoss : %f\n", psf->growth->apLoss);
+
+    psFree (view);
+    psFree (image);
+    psFree (mask);
+    psFree (model);
+    psFree (modelRef);
+
+    return completeGrowthCurve ? true : false;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF.h	(revision 22322)
@@ -0,0 +1,108 @@
+/* @file  pmPSF.h
+ *
+ * This file contains typedefs for the Point-Spread Function and prototypes
+ * for functions that calculate the PSF.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.20 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-27 03:14:57 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_PSF_H
+# define PM_PSF_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+// type of model carried by the pmModel structure
+typedef int pmModelType;
+
+/** pmPSF data structure
+ *
+ * It is useful to generate a model to define the point-spread-function which
+ * describes the flux distribution for unresolved sources in an image. In
+ * general, the PSF varies with position in the image. We allow any of the source
+ * models defined for the pmModel to represent the PSF. For a given source model,
+ * the 2D spatial variation of all of the source parameters, except the first
+ * four PSF-independent parameters, are represented as polynomial, stored in a
+ * psArray. The other elements of the structure define the quality of the PSF
+ * determination.
+ *
+ */
+typedef struct
+{
+    pmModelType type;                   ///< PSF Model in use
+    psArray *params;                    ///< Model parameters (psPolynomial2D)
+    psStats *psfTrendStats;             ///< psf parameter trend clipping stats
+    pmTrend2DMode psfTrendMode;
+    psPolynomial1D *ChiTrend;           ///< Chisq vs flux fit (correction for systematic errors)
+    pmTrend2D *ApTrend;                 ///< ApResid vs (x,y)
+    pmTrend2D *FluxScale;               ///< Flux for PSF at (x,y) for normalization = 1.0
+    float ApResid;                      ///< apMag - psfMag (for PSF stars)
+    float dApResid;                     ///< scatter of ApResid
+    float skyBias;                      ///< implied residual sky offset from ApResid fit
+    float skySat;                       ///< roll-over of ApResid fit
+    float chisq;                        ///< PSF goodness statistic (unused??)
+    int nPSFstars;                      ///< number of stars used to measure PSF
+    int nApResid;                       ///< number of stars used to measure ApResid
+    int trendNx;
+    int trendNy;
+    int fieldNx;
+    int fieldNy;
+    int fieldXo;
+    int fieldYo;
+    bool poissonErrorsPhotLMM;          ///< use poission errors for non-linear model fitting
+    bool poissonErrorsPhotLin;          ///< use poission errors for linear model fitting
+    bool poissonErrorsParams;           ///< use poission errors for model parameter fitting
+    pmGrowthCurve *growth;              ///< apMag vs Radius
+    pmResiduals *residuals;             ///< normalized residual image (no spatial variation)
+}
+pmPSF;
+
+typedef struct {
+    pmModelType   type;
+    psStats      *stats;                // psfTrend clipping stats
+    pmTrend2DMode psfTrendMode;
+    int           psfTrendNx;
+    int           psfTrendNy;
+    int           psfFieldNx;
+    int           psfFieldNy;
+    int           psfFieldXo;
+    int           psfFieldYo;
+    bool          poissonErrorsPhotLMM; ///< use poission errors for non-linear model fitting
+    bool          poissonErrorsPhotLin; ///< use poission errors for linear model fitting
+    bool          poissonErrorsParams; ///< use poission errors for model parameter fitting
+    float         radius;
+} pmPSFOptions;
+
+# define PM_PAR_E0 PM_PAR_SXX
+# define PM_PAR_E1 PM_PAR_SYY
+# define PM_PAR_E2 PM_PAR_SXY
+
+/**
+ *
+ * Allocator for the pmPSF structure.
+ *
+ */
+
+pmPSF *pmPSFAlloc (const pmPSFOptions *options);
+bool psMemCheckPSF(psPtr ptr);
+pmPSFOptions *pmPSFOptionsAlloc();
+bool psMemCheckPSFOptions(psPtr ptr);
+
+double pmPSF_SXYfromModel (psF32 *modelPar);
+double pmPSF_SXYtoModel (psF32 *fittedPar);
+
+bool pmGrowthCurveGenerate (pmReadout *readout, pmPSF *psf, bool ignore, psMaskType maskVal, psMaskType mark);
+pmPSF *pmPSFBuildSimple (char *typeName, float sxx, float syy, float sxy, ...);
+
+bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes);
+bool pmPSF_FitToModel (psF32 *fittedPar, float minMinorAxis);
+
+psEllipsePol pmPSF_ModelToFit (psF32 *modelPar);
+psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF_IO.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF_IO.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF_IO.c	(revision 22322)
@@ -0,0 +1,820 @@
+/** @file  pmPSF_IO.c
+ *
+ * This file contains functions to read and write PSF models using the psMetadata Config file
+ * format.
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.34 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-07-17 22:38:15 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmFPAfileFitsIO.h"
+
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmPSF_IO.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourceIO.h"
+
+bool pmPSFmodelCheckDataStatusForView (const pmFPAview *view, const pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa->chips, false);
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        bool exists = pmPSFmodelCheckDataStatusForFPA (fpa);
+        return exists;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        psError(PS_ERR_IO, true, "Requested chip == %d >= fpa->chips->n == %ld", view->chip, fpa->chips->n);
+        return false;
+    }
+
+    pmChip *chip = fpa->chips->data[view->chip];
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(chip->cells, false);
+
+    if (view->cell == -1) {
+        bool exists = pmPSFmodelCheckDataStatusForChip (chip);
+        return exists;
+    }
+
+    if (view->cell >= chip->cells->n) {
+        psError(PS_ERR_IO, true, "Requested cell == %d >= chip->cells->n == %ld", view->cell, chip->cells->n);
+        return false;
+    }
+
+    psError(PS_ERR_IO, false, "PSF only valid at the chip level");
+    return false;
+}
+
+bool pmPSFmodelCheckDataStatusForFPA (const pmFPA *fpa) {
+
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fpa->chips, false);
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        if (!chip) continue;
+        if (pmPSFmodelCheckDataStatusForChip (chip)) return true;
+    }
+    return false;
+}
+
+bool pmPSFmodelCheckDataStatusForChip (const pmChip *chip) {
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+
+    bool status;
+
+    // select the psf of interest
+    pmPSF *psf = psMetadataLookupPtr(&status, chip->analysis, "PSPHOT.PSF");
+    return psf ? true : false;
+}
+
+bool pmPSFmodelWriteForView (const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa->chips, false);
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        if (!pmPSFmodelWriteFPA(fpa, view, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write PSF for fpa");
+            return false;
+        }
+        return true;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        if (!pmPSFmodelWriteChip (chip, view, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write PSF for chip");
+            return false;
+        }
+        return true;
+    }
+
+    psError(PS_ERR_IO, false, "PSF must be written at the chip level");
+    return false;
+}
+
+// read in all chip-level PSFmodel files for this FPA
+bool pmPSFmodelWriteFPA (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fpa->chips, false);
+    pmFPAview *thisView = pmFPAviewAlloc (view->nRows);
+    *thisView = *view;
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        thisView->chip = i;
+        if (!pmPSFmodelWriteChip (chip, thisView, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write PSF for %dth chip", i);
+            psFree(thisView);
+            return false;
+        }
+    }
+    psFree(thisView);
+    return true;
+}
+
+// read in all cell-level PSFmodel files for this chip
+bool pmPSFmodelWriteChip (pmChip *chip, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+
+    if (!pmPSFmodelWrite (chip->analysis, view, file, config)) {
+        psError(PS_ERR_IO, false, "Failed to write PSF for chip");
+        return false;
+    }
+    return true;
+}
+
+// for a pmPSF supplied on the analysis metadata, we write out
+// if needed:
+//   - a PHU blank header
+// - image header        : FITS Image NAXIS = 0
+// if (trendMode == MAP)
+//   - psf resid (+header) : FITS Image
+// else
+//   - psf table (+header) : FITS Table
+bool pmPSFmodelWrite (psMetadata *analysis, const pmFPAview *view,
+                      pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+    bool status;
+    char *headName, *tableName, *residName;
+
+    if (!analysis) return false;
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, "PSPHOT");
+    if (!recipe) {
+        psError(PS_ERR_UNKNOWN, false, "missing recipe %s", "PSPHOT");
+        return false;
+    }
+
+    // write a PHU? (only if input image is MEF)
+    // write a header? (only if this is the first readout for cell)
+    //   note that the file->header is set to track the last hdu->header written
+    // write the data? (always?)
+
+    // get the current header
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
+    pmHDU *hdu = psMemIncrRefCounter(pmFPAviewThisHDU(view, fpa));
+    psFree(fpa);
+    if (!hdu) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find HDU");
+        return false;
+    }
+
+    // if file does not yet have a PHU, attempt to write one to disk
+    // we only need a PHU if chips->n > 1 and file->fileLevel == FPA
+    // otherwise, the chip header fills the PHU location
+    // XXX this code could be placed in a 'pmPSF_WritePHU' function and called
+    // from pmFPAfileIO.c.
+
+    // define the EXTNAME values used for image header, table data, and residual image segments
+    {
+        // lookup the EXTNAME values used for table data and image header segments
+        char *rule = NULL;
+
+        // Menu of EXTNAME rules
+        psMetadata *menu = psMetadataLookupMetadata(&status, file->camera, "EXTNAME.RULES");
+        if (!menu) {
+            psError(PS_ERR_UNKNOWN, true, "missing EXTNAME.RULES in camera.config");
+            psFree(hdu);
+            return false;
+        }
+
+        // EXTNAME for image header
+        rule = psMetadataLookupStr(&status, menu, "PSF.HEAD");
+        if (!rule) {
+            psError(PS_ERR_UNKNOWN, false, "missing entry for PSF.HEAD in EXTNAME.RULES in camera.config");
+            psFree(hdu);
+            return false;
+        }
+        headName = pmFPAfileNameFromRule (rule, file, view);
+
+        // EXTNAME for table data
+        rule = psMetadataLookupStr(&status, menu, "PSF.TABLE");
+        if (!rule) {
+            psError(PS_ERR_UNKNOWN, false, "missing entry for PSF.TABLE in EXTNAME.RULES in camera.config");
+            psFree (headName);
+            psFree(hdu);
+            return false;
+        }
+        tableName = pmFPAfileNameFromRule (rule, file, view);
+
+        // EXTNAME for resid data
+        rule = psMetadataLookupStr(&status, menu, "PSF.RESID");
+        if (!rule) {
+            psError(PS_ERR_UNKNOWN, false, "missing entry for PSF.RESID in EXTNAME.RULES in camera.config");
+            psFree (headName);
+            psFree (tableName);
+            psFree(hdu);
+            return false;
+        }
+        residName = pmFPAfileNameFromRule (rule, file, view);
+    }
+
+    // write out the IMAGE header segment
+    // if this header block is new, write it to disk
+    if (hdu->header != file->header) {
+        // add EXTNAME, EXTHEAD, EXTTYPE to header
+        psMetadataAddStr (hdu->header, PS_LIST_TAIL, "EXTTABLE", PS_META_REPLACE, "name of table extension", tableName);
+        psMetadataAddStr (hdu->header, PS_LIST_TAIL, "EXTRESID", PS_META_REPLACE, "name of resid extension", residName);
+        psMetadataAddStr (hdu->header, PS_LIST_TAIL, "EXTTYPE", PS_META_REPLACE, "extension type", "IMAGE");
+        if (!file->wrote_phu) {
+            // this hdu->header acts as the PHU: set EXTEND to be true
+            psMetadataAddBool (hdu->header, PS_LIST_TAIL, "EXTEND", PS_META_REPLACE, "this file has extensions", true);
+            file->wrote_phu = true;
+        }
+
+        psFitsWriteBlank (file->fits, hdu->header, headName);
+        psTrace ("pmFPAfile", 5, "wrote ext head %s (type: %d)\n", file->filename, file->type);
+        file->header = hdu->header;
+        psFree (headName);
+    }
+    psFree(hdu);
+
+    // select the psf of interest
+    pmPSF *psf = psMetadataLookupPtr (&status, analysis, "PSPHOT.PSF");
+    if (!psf) {
+        psError(PS_ERR_UNKNOWN, true, "missing PSF for this analysis metadata");
+        psFree (tableName);
+        psFree (residName);
+        return false;
+    }
+
+    // write the PSF model parameters in a FITS table
+    {
+        // we need to write a header for the table,
+        psMetadata *header = psMetadataAlloc();
+
+        char *modelName = pmModelClassGetName (psf->type);
+        psMetadataAddStr (header, PS_LIST_TAIL, "PSF_NAME", 0, "PSF model name", modelName);
+
+        psMetadataAddBool (header, PS_LIST_TAIL, "ERR_LMM",  0, "Use Poisson errors in fits?", psf->poissonErrorsPhotLMM);
+        psMetadataAddBool (header, PS_LIST_TAIL, "ERR_LIN",  0, "Use Poisson errors in fits?", psf->poissonErrorsPhotLin);
+        psMetadataAddBool (header, PS_LIST_TAIL, "ERR_PAR",  0, "Use Poisson errors in fits?", psf->poissonErrorsParams);
+
+        int nPar = pmModelClassParameterCount (psf->type)    ;
+        psMetadataAdd (header, PS_LIST_TAIL, "PSF_NPAR", PS_DATA_S32, "PSF model parameter count", nPar);
+
+        psMetadataAddS32 (header, PS_LIST_TAIL, "IMAXIS1", 0, "Image X Size", psf->fieldNx);
+        psMetadataAddS32 (header, PS_LIST_TAIL, "IMAXIS2", 0, "Image Y Size", psf->fieldNy);
+        psMetadataAddS32 (header, PS_LIST_TAIL, "IMREF1",  0, "Image X Ref",  psf->fieldXo);
+        psMetadataAddS32 (header, PS_LIST_TAIL, "IMREF2",  0, "Image Y Ref",  psf->fieldYo);
+
+        // extract PSF Clump info
+        pmPSFClump psfClump;
+
+        psfClump.X  = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.X");   assert (status);
+        psfClump.Y  = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.Y");   assert (status);
+        psfClump.dX = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.DX");  assert (status);
+        psfClump.dY = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.DY");  assert (status);
+
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PSF_CLX", 0, "psf clump center", psfClump.X);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PSF_CLY", 0, "psf clump center", psfClump.Y);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PSF_CLDX", 0, "psf clump size", psfClump.dX);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PSF_CLDY", 0, "psf clump size", psfClump.dY);
+
+        // save the dimensions of each parameter
+        for (int i = 0; i < nPar; i++) {
+            char name[9];
+            int nX, nY;
+
+            pmTrend2D *trend = psf->params->data[i];
+            if (trend == NULL) continue;
+
+            if (trend->mode == PM_TREND_MAP) {
+              nX = trend->map->map->numCols;
+              nY = trend->map->map->numRows;
+            } else {
+              nX = trend->poly->nX;
+              nY = trend->poly->nY;
+            }
+            snprintf (name, 9, "PAR%02d_NX", i);
+            psMetadataAddS32 (header, PS_LIST_TAIL, name, 0, "", nX);
+            snprintf (name, 9, "PAR%02d_NY", i);
+            psMetadataAddS32 (header, PS_LIST_TAIL, name, 0, "", nY);
+            snprintf (name, 9, "PAR%02d_MD", i);
+            char *modeName = pmTrend2DModeToString (trend->mode);
+            psMetadataAddStr (header, PS_LIST_TAIL, name, 0, "", modeName);
+            psFree (modeName);
+        }
+
+        // other required information describing the PSF
+        psMetadataAddF32 (header, PS_LIST_TAIL, "AP_RESID", 0, "aperture residual", psf->ApResid);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "AP_ERROR", 0, "aperture residual scatter", psf->dApResid);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CHISQ",    0, "chi-square for fit", psf->chisq);
+        psMetadataAddS32 (header, PS_LIST_TAIL, "NSTARS",   0, "number of stars used to measure PSF", psf->nPSFstars);
+
+        // XXX can we drop this now?
+        psMetadataAddF32 (header, PS_LIST_TAIL, "SKY_BIAS", PS_DATA_F32, "sky bias level", psf->skyBias);
+
+        // build a FITS table of the PSF parameters
+        psArray *psfTable = psArrayAllocEmpty (100);
+        for (int i = 0; i < nPar; i++) {
+            pmTrend2D *trend = psf->params->data[i];
+            if (trend == NULL) continue; // skip unset parameters (eg, XPOS)
+
+            if (trend->mode == PM_TREND_MAP) {
+                // write the image components into a table: this is needed because they may each be a different size
+                psImageMap *map = trend->map;
+                for (int ix = 0; ix < map->map->numCols; ix++) {
+                    for (int iy = 0; iy < map->map->numRows; iy++) {
+                        psMetadata *row = psMetadataAlloc ();
+                        psMetadataAddS32 (row, PS_LIST_TAIL, "MODEL_TERM", 0, "", i);
+                        psMetadataAddS32 (row, PS_LIST_TAIL, "X_POWER",    0, "", ix);
+                        psMetadataAddS32 (row, PS_LIST_TAIL, "Y_POWER",    0, "", iy);
+                        psMetadataAddF32 (row, PS_LIST_TAIL, "VALUE",      0, "", map->map->data.F32[iy][ix]);
+                        psMetadataAddF32 (row, PS_LIST_TAIL, "ERROR",      0, "", map->error->data.F32[iy][ix]);
+                        psMetadataAddU8  (row, PS_LIST_TAIL, "MASK",       0, "", 0); // no cells are masked
+
+                        psArrayAdd (psfTable, 100, row);
+                        psFree (row);
+                    }
+                }
+            } else {
+                // write the polynomial components into a table
+                psPolynomial2D *poly = trend->poly;
+                for (int ix = 0; ix <= poly->nX; ix++) {
+                    for (int iy = 0; iy <= poly->nY; iy++) {
+                        psMetadata *row = psMetadataAlloc ();
+                        psMetadataAddS32 (row, PS_LIST_TAIL, "MODEL_TERM", 0, "", i);
+                        psMetadataAddS32 (row, PS_LIST_TAIL, "X_POWER",    0, "", ix);
+                        psMetadataAddS32 (row, PS_LIST_TAIL, "Y_POWER",    0, "", iy);
+                        psMetadataAddF32 (row, PS_LIST_TAIL, "VALUE",      0, "", poly->coeff[ix][iy]);
+                        psMetadataAddF32 (row, PS_LIST_TAIL, "ERROR",      0, "", poly->coeffErr[ix][iy]);
+                        psMetadataAddU8  (row, PS_LIST_TAIL, "MASK",       0, "", poly->coeffMask[ix][iy]);
+
+                        psArrayAdd (psfTable, 100, row);
+                        psFree (row);
+                    }
+                }
+            }
+        }
+
+        // write an empty FITS segment if we have no PSF information
+        if (psfTable->n == 0) {
+            // XXX this is probably an error (if we have a PSF, how do we have no data?)
+            psFitsWriteBlank (file->fits, header, tableName);
+        } else {
+            psTrace ("pmFPAfile", 5, "writing psf data %s\n", tableName);
+            if (!psFitsWriteTable (file->fits, header, psfTable, tableName)) {
+                psError(PS_ERR_IO, false, "writing psf table data %s\n", tableName);
+                psFree (tableName);
+                psFree (residName);
+                psFree (psfTable);
+                psFree (header);
+                return false;
+            }
+        }
+        psFree (tableName);
+        psFree (psfTable);
+        psFree (header);
+    }
+
+    // write the residual images (3D)
+    {
+        psMetadata *header = psMetadataAlloc ();
+        if (psf->residuals == NULL) {
+            // set some header keywords to make it clear there are no residuals?
+            psFitsWriteBlank (file->fits, header, residName);
+            psFree (residName);
+            psFree (header);
+            return true;
+        }
+
+        psMetadataAddS32 (header, PS_LIST_TAIL, "XBIN",    0, "", psf->residuals->xBin);
+        psMetadataAddS32 (header, PS_LIST_TAIL, "YBIN",    0, "", psf->residuals->yBin);
+        psMetadataAddS32 (header, PS_LIST_TAIL, "XCENTER", 0, "", psf->residuals->xCenter);
+        psMetadataAddS32 (header, PS_LIST_TAIL, "YCENTER", 0, "", psf->residuals->yCenter);
+
+        // write the residuals as three planes of the image
+        // this call creates an extension with NAXIS3 = 3
+        if (psf->residuals->Rx) {
+            // this call creates an extension with NAXIS3 = 3
+            psArray *images = psArrayAllocEmpty (3);
+            psArrayAdd (images, 1, psf->residuals->Ro);
+            psArrayAdd (images, 1, psf->residuals->Rx);
+            psArrayAdd (images, 1, psf->residuals->Ry);
+
+            psFitsWriteImageCube (file->fits, header, images, residName);
+            psFree (images);
+        } else {
+            // this call creates an extension with NAXIS3 = 1
+            psFitsWriteImage(file->fits, header, psf->residuals->Ro, 0, residName);
+        }
+        psFree (residName);
+        psFree (header);
+    }
+
+    return true;
+
+    // XXX save the growth curve
+    // XXX save ApTrend (as image?)
+    // XXX write the ApTrend with the same API as will be used for the PSF parameters above
+
+# if (0)
+    // build a FITS table of the fit to the Aperture Residuals
+    psArray *apresTable = psArrayAllocEmpty (100);
+    psPolynomial4D *poly = psf->ApTrend;
+    for (int ix = 0; ix < poly->nX; ix++) {
+        for (int iy = 0; iy < poly->nY; iy++) {
+
+            row = psMetadataAlloc ();
+            psMetadataAddS32 (row, PS_LIST_TAIL, "X_POWER",    0, "", ix);
+            psMetadataAddS32 (row, PS_LIST_TAIL, "Y_POWER",    0, "", iy);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "VALUE",      0, "", poly->coeff[ix][iy]);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "ERROR",      0, "", poly->coeffErr[ix][iy]);
+            psMetadataAddU8  (row, PS_LIST_TAIL, "MASK",       0, "", poly->mask[ix][iy]);
+
+            psArrayAdd (psfTable, 100, row);
+            psFree (row);
+        }
+    }
+# endif
+}
+
+// if this file needs to have a PHU written out, write one
+bool pmPSFmodelWritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+    // not needed if already written
+    if (file->wrote_phu) return true;
+
+    // not needed if not FPA
+    // XXX this prevents us from defining a SPLIT/MEF CMF file...
+    if (file->fileLevel != PM_FPA_LEVEL_FPA) return true;
+
+    // not needed if only one chip
+    if (file->fpa->chips->n == 1) return true;
+
+
+    // find the FPA phu
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
+    pmHDU *phu = psMemIncrRefCounter(pmFPAviewThisPHU(view, fpa));
+    psFree(fpa);
+
+    // if there is no PHU, this is a single header+image (extension-less) file. This could be
+    // the case for an input SPLIT set of files being written out as a MEF.  if there is a PHU,
+    // write it out as a 'blank'
+    psMetadata *outhead = psMetadataAlloc();
+    if (phu) {
+        psMetadataCopy (outhead, phu->header);
+    } else {
+        pmConfigConformHeader (outhead, file->format);
+    }
+    psFree(phu);
+
+    psMetadataAddBool (outhead, PS_LIST_TAIL, "EXTEND", PS_META_REPLACE, "this file has extensions", true);
+    psFitsWriteBlank (file->fits, outhead, "");
+    file->wrote_phu = true;
+
+    psTrace ("pmFPAfile", 5, "wrote phu %s (type: %d)\n", file->filename, file->type);
+    psFree (outhead);
+
+    return true;
+}
+
+bool pmPSFmodelReadForView (const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        return pmPSFmodelReadFPA(fpa, view, file, config);
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        psAbort("Programming error: view does not apply to FPA.");
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        return pmPSFmodelReadChip(chip, view, file, config);
+    }
+
+    psError(PS_ERR_IO, false, "PSF must be read at the chip level");
+    return false;
+}
+
+// read in all chip-level PSFmodel files for this FPA
+bool pmPSFmodelReadFPA (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+
+    bool success = true;                // Was everything successful?
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        success &= pmPSFmodelReadChip(chip, view, file, config);
+    }
+    return success;
+}
+
+// read in all cell-level PSFmodel files for this chip
+bool pmPSFmodelReadChip (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+
+    if (!pmPSFmodelRead (chip->analysis, view, file, config)) {
+        psError(PS_ERR_IO, false, "Failed to write PSF for chip");
+        return false;
+    }
+    return true;
+}
+
+// for each Readout (ie, analysed image), we write out: header + table with PSF model parameters,
+// and header + image for the PSF residual images
+bool pmPSFmodelRead (psMetadata *analysis, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+
+    bool status;
+    char *rule = NULL;
+    psMetadata *header = NULL;
+
+    psTrace ("psModules.objects", 5, "read psf model for %s\n", file->filename);
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, "PSPHOT");
+    if (!recipe) {
+        psError(PS_ERR_UNKNOWN, false, "missing recipe %s", "PSPHOT");
+        return false;
+    }
+
+    // Menu of EXTNAME rules
+    psMetadata *menu = psMetadataLookupMetadata(&status, file->camera, "EXTNAME.RULES");
+    if (!menu) {
+        psError(PS_ERR_UNKNOWN, true, "missing EXTNAME.RULES in camera.config");
+        return false;
+    }
+    // EXTNAME for table data
+    rule = psMetadataLookupStr(&status, menu, "PSF.TABLE");
+    if (!rule) {
+        psError(PS_ERR_UNKNOWN, true, "missing entry for PSF.TABLE in EXTNAME.RULES in camera.config");
+        return false;
+    }
+    char *tableName = pmFPAfileNameFromRule (rule, file, view);
+    // EXTNAME for residual images
+    rule = psMetadataLookupStr(&status, menu, "PSF.RESID");
+    if (!rule) {
+        psError(PS_ERR_UNKNOWN, true, "missing entry for PSF.RESID in EXTNAME.RULES in camera.config");
+        return false;
+    }
+    char *imageName = pmFPAfileNameFromRule (rule, file, view);
+
+    // move fits pointer to table and read header
+    // advance to the table data extension
+    // since we have read the IMAGE header, the TABLE header should exist
+    if (!psFitsMoveExtName (file->fits, tableName)) {
+        psAbort("cannot find data extension %s in %s", tableName, file->filename);
+    }
+
+    // load the PSF model table header
+    header = psFitsReadHeader (NULL, file->fits);
+    if (!header) psAbort("cannot read table header");
+
+    pmPSFOptions *options = pmPSFOptionsAlloc();
+
+    // load the PSF model parameters from the FITS table
+    char *modelName = psMetadataLookupStr (&status, header, "PSF_NAME");
+    options->type = pmModelClassGetType (modelName);
+    if (options->type == -1) {
+        psError(PS_ERR_UNKNOWN, true, "invalid model name %s in psf file %s", modelName, file->filename);
+        return false;
+    }
+
+    // psf clump data
+    pmPSFClump psfClump;
+
+    psfClump.X  = psMetadataLookupF32 (&status, header, "PSF_CLX" );  assert(status);
+    psfClump.Y  = psMetadataLookupF32 (&status, header, "PSF_CLY" );  assert(status);
+    psfClump.dX = psMetadataLookupF32 (&status, header, "PSF_CLDX");  assert(status);
+    psfClump.dY = psMetadataLookupF32 (&status, header, "PSF_CLDY");  assert(status);
+
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.X" , PS_META_REPLACE, "psf clump center", psfClump.X);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.Y" , PS_META_REPLACE, "psf clump center", psfClump.Y);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump size",   psfClump.dX);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump size",   psfClump.dY);
+
+    options->poissonErrorsPhotLMM = psMetadataLookupBool (&status, header, "ERR_LMM");
+    options->poissonErrorsPhotLin = psMetadataLookupBool (&status, header, "ERR_LIN");
+    options->poissonErrorsParams  = psMetadataLookupBool (&status, header, "ERR_PAR");
+
+    options->psfFieldNx = psMetadataLookupS32 (&status, header, "IMAXIS1");
+    options->psfFieldNy = psMetadataLookupS32 (&status, header, "IMAXIS2");
+    options->psfFieldXo = psMetadataLookupS32 (&status, header, "IMREF1");
+    options->psfFieldYo = psMetadataLookupS32 (&status, header, "IMREF2");
+
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXfine = options->psfFieldNx;
+    binning->nYfine = options->psfFieldNy;
+
+    // we determine the PSF parameter polynomials from the MD-defined polynomials
+    pmPSF *psf = pmPSFAlloc (options);
+
+    // check the number of expected parameters
+    int nPar = psMetadataLookupS32 (&status, header, "PSF_NPAR");
+    if (nPar != pmModelClassParameterCount (psf->type))
+        psAbort("mismatch model par count");
+
+    // load the trend mode and dimensions of each parameter
+    for (int i = 0; i < nPar; i++) {
+        char name[9];
+        snprintf (name, 9, "PAR%02d_NX", i);
+        binning->nXruff = psMetadataLookupS32 (&status, header, name);
+        if (!status) continue;          // not all parameters are defined
+
+        snprintf (name, 9, "PAR%02d_NY", i);
+        binning->nYruff = psMetadataLookupS32 (&status, header, name);
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, true, "inconsistent PSF header: NX defined for PAR %d, but not NY", i);
+            return false;
+        }
+
+        snprintf (name, 9, "PAR%02d_MD", i);
+        char *modeName = psMetadataLookupStr (&status, header, name);
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, true, "inconsistent PSF header: NX & NY defined for PAR %d, but not MD", i);
+            return false;
+        }
+        pmTrend2DMode psfTrendMode = pmTrend2DModeFromString (modeName);
+        if (psfTrendMode == PM_TREND_NONE) {
+            psfTrendMode = PM_TREND_POLY_ORD;
+        }
+
+        // XXX Attempting to guard against failing assertions on nXruff and nYruff in psImageBinningSetScale.
+        // This replicates code in psphotCheckStarDistribution, where these values are generated.  Not sure
+        // it's correct, though.
+        if (psfTrendMode != PM_TREND_MAP) {
+            binning->nXruff++;
+            binning->nYruff++;
+        }
+
+        psImageBinningSetScale (binning, PS_IMAGE_BINNING_CENTER);
+        psImageBinningSetSkipByOffset (binning, options->psfFieldXo, options->psfFieldYo);
+        psf->params->data[i] = pmTrend2DNoImageAlloc (psfTrendMode, binning, NULL);
+    }
+    psFree (binning);
+    psFree(options);
+
+    // other required information describing the PSF
+    psf->ApResid   = psMetadataLookupF32 (&status, header, "AP_RESID");
+    psf->dApResid  = psMetadataLookupF32 (&status, header, "AP_ERROR");
+    psf->chisq     = psMetadataLookupF32 (&status, header, "CHISQ");
+    psf->nPSFstars = psMetadataLookupS32 (&status, header, "NSTARS");
+
+    // XXX can we drop this now?
+    psf->skyBias   = psMetadataLookupF32 (&status, header, "SKY_BIAS");
+
+    // read the raw table data
+    psArray *table = psFitsReadTable (file->fits);
+
+    // fill in the matching psf->params entries
+    for (int i = 0; i < table->n; i++) {
+        psMetadata *row = table->data[i];
+        int iPar = psMetadataLookupS32 (&status, row, "MODEL_TERM");
+        int xPow = psMetadataLookupS32 (&status, row, "X_POWER");
+        int yPow = psMetadataLookupS32 (&status, row, "Y_POWER");
+
+        pmTrend2D *trend = psf->params->data[iPar];
+        if (trend == NULL) {
+            psError(PS_ERR_UNKNOWN, true, "parameter %d not available", iPar);
+            return false;
+        }
+
+        if (trend->mode == PM_TREND_MAP) {
+            psImageMap *map = trend->map;
+            assert (map);
+            assert (map->map);
+            assert (map->error);
+            assert (xPow >= 0);
+            assert (yPow >= 0);
+            assert (xPow < map->map->numCols);
+            assert (yPow < map->map->numRows);
+            map->map->data.F32[yPow][xPow]    = psMetadataLookupF32 (&status, row, "VALUE");
+            map->error->data.F32[yPow][xPow]  = psMetadataLookupF32 (&status, row, "ERROR");
+        } else {
+            psPolynomial2D *poly = trend->poly;
+            assert (poly);
+            assert (xPow >= 0);
+            assert (yPow >= 0);
+            assert (xPow <= poly->nX);
+            assert (yPow <= poly->nY);
+            poly->coeff[xPow][yPow]     = psMetadataLookupF32 (&status, row, "VALUE");
+            poly->coeffErr[xPow][yPow]  = psMetadataLookupF32 (&status, row, "ERROR");
+            poly->coeffMask[xPow][yPow] = psMetadataLookupU8  (&status, row, "MASK");
+        }
+    }
+    psFree (header);
+    psFree (table);
+
+    // move fits pointer to residual image and read header
+    // advance to the table data extension
+    // since we have read the IMAGE header, the TABLE header should exist
+    if (!psFitsMoveExtName (file->fits, imageName)) {
+        psAbort("cannot find data extension %s in %s", imageName, file->filename);
+    }
+
+    header = psFitsReadHeader (NULL, file->fits);
+    int Naxis = psMetadataLookupS32 (&status, header, "NAXIS");
+    if (Naxis != 0) {
+
+        int Nx = psMetadataLookupS32 (&status, header, "NAXIS1");
+        int Ny = psMetadataLookupS32 (&status, header, "NAXIS2");
+        int Nz = psMetadataLookupS32 (&status, header, "NAXIS3");
+
+        int xBin  = psMetadataLookupS32 (&status, header, "XBIN");
+        int yBin  = psMetadataLookupS32 (&status, header, "YBIN");
+
+        int xSize = Nx / xBin;
+        int ySize = Ny / yBin;
+
+        psf->residuals = pmResidualsAlloc (xSize, ySize, xBin, yBin);
+
+        psf->residuals->xCenter = psMetadataLookupS32 (&status, header, "XCENTER");
+        psf->residuals->yCenter = psMetadataLookupS32 (&status, header, "YCENTER");
+
+        psRegion fullImage = {0, 0, 0, 0};
+        psFitsReadImageBuffer(psf->residuals->Ro, file->fits, fullImage, 0); // Desired pixels
+        if (Nz > 1) {
+            assert (Nz == 3);
+            psFitsReadImageBuffer(psf->residuals->Rx, file->fits, fullImage, 1); // Desired pixels
+            psFitsReadImageBuffer(psf->residuals->Ry, file->fits, fullImage, 2); // Desired pixels
+        }
+    }
+
+    psMetadataAdd (analysis, PS_LIST_TAIL, "PSPHOT.PSF",     PS_DATA_UNKNOWN,  "psphot psf", psf);
+    psFree (psf);
+
+    psFree (tableName);
+    psFree (imageName);
+    psFree (header);
+
+    return true;
+}
+
+// XXX pmPSF to/from Metadata functions were defined for 1.22 and earlier, but were dropped
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF_IO.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF_IO.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSF_IO.h	(revision 22322)
@@ -0,0 +1,36 @@
+/* @file  pmPSF.h
+ *
+ * This file contains typedefs for the Point-Spread Function and prototypes
+ * for functions that calculate the PSF.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-07-17 22:38:15 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_PSF_IO_H
+# define PM_PSF_IO_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+bool pmPSFmodelWriteForView (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmPSFmodelWriteFPA (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmPSFmodelWriteChip (pmChip *chip, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmPSFmodelWrite (psMetadata *analysis, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+
+bool pmPSFmodelWritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+
+bool pmPSFmodelReadForView (const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmPSFmodelReadFPA (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmPSFmodelReadChip (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmPSFmodelRead (psMetadata *analysis, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+
+bool pmPSFmodelCheckDataStatusForView (const pmFPAview *view, const pmFPAfile *file);
+bool pmPSFmodelCheckDataStatusForFPA (const pmFPA *fpa);
+bool pmPSFmodelCheckDataStatusForChip (const pmChip *chip);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSFtoMetadata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSFtoMetadata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSFtoMetadata.c	(revision 22322)
@@ -0,0 +1,91 @@
+
+// create a psMetadata representation (human-readable) of a psf model
+psMetadata *pmPSFtoMetadata (psMetadata *metadata, pmPSF *psf)
+{
+
+    if (metadata == NULL) {
+        metadata = psMetadataAlloc ();
+    }
+
+    char *modelName = pmModelClassGetName (psf->type);
+    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_MODEL_NAME", PS_DATA_STRING, "PSF model name", modelName);
+
+    int nPar = pmModelClassParameterCount (psf->type)    ;
+    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_MODEL_NPAR", PS_DATA_S32, "PSF model parameter count", nPar);
+
+    for (int i = 0; i < nPar; i++) {
+        psPolynomial2D *poly = psf->params->data[i];
+        if (poly == NULL)
+            continue;
+        psPolynomial2DtoMetadata (metadata, poly, "PSF_PAR%02d", i);
+    }
+
+    // XXX fix this
+    psWarning ("APTREND is currently missing");
+    // psPolynomial4DtoMetadata (metadata, psf->ApTrend, "APTREND");
+
+    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_AP_RESID", PS_DATA_F32, "aperture residual", psf->ApResid);
+    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_dAP_RESID", PS_DATA_F32, "aperture residual scatter", psf->dApResid);
+    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_SKY_BIAS", PS_DATA_F32, "sky bias level", psf->skyBias);
+
+    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_CHISQ", PS_DATA_F32, "chi-square for fit", psf->chisq);
+    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_NSTARS", PS_DATA_S32, "number of stars used to measure PSF", psf->nPSFstars);
+    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_POISSON_ERRORS", PS_DATA_BOOL, "Poisson errors for fits", psf->poissonErrors);
+
+    return metadata;
+}
+
+// parse a psMetadata representation (human-readable) of a psf model
+pmPSF *pmPSFfromMetadata (psMetadata *metadata)
+{
+
+    bool status;
+    char keyword[80];
+
+    char *modelName = psMetadataLookupPtr (&status, metadata, "PSF_MODEL_NAME");
+    pmModelType type = pmModelClassGetType (modelName);
+
+    bool poissonErrors = psMetadataLookupPtr (&status, metadata, "PSF_POISSON_ERRORS");
+    if (!status)
+        poissonErrors = true;
+
+    // we determine the PSF parameter polynomials from the MD-defined polynomials
+    pmPSF *psf = pmPSFAlloc (type, poissonErrors, NULL);
+
+    int nPar = psMetadataLookupS32 (&status, metadata, "PSF_MODEL_NPAR");
+    if (nPar != pmModelClassParameterCount (psf->type))
+        psAbort("mismatch model par count");
+
+    // un-fitted terms, not in the Metadata, are left NULL
+    // XXX add a double-check of the expected number?
+    for (int i = 0; i < nPar; i++) {
+        sprintf (keyword, "PSF_PAR%02d", i);
+        psMetadata *folder = psMetadataLookupPtr (&status, metadata, keyword);
+        if (!status)
+            continue;
+        psPolynomial2D *poly = psPolynomial2DfromMetadata (folder);
+        psFree (psf->params->data[i]);
+        psf->params->data[i] = poly;
+    }
+
+    // load the APTREND data
+    // XXX fix this to work with pmTrend2D
+    psWarning ("APTREND is not being read");
+    # if (0)
+    sprintf (keyword, "APTREND");
+    psMetadata *folder = psMetadataLookupPtr (&status, metadata, keyword);
+    psPolynomial4D *poly = psPolynomial4DfromMetadata (folder);
+    psFree (psf->ApTrend);
+    psf->ApTrend = poly;
+    # endif
+
+    psf->ApResid = psMetadataLookupF32 (&status, metadata, "PSF_AP_RESID");
+    psf->dApResid = psMetadataLookupF32 (&status, metadata, "PSF_dAP_RESID");
+    psf->skyBias = psMetadataLookupF32 (&status, metadata, "PSF_SKY_BIAS");
+
+    psf->chisq = psMetadataLookupF32 (&status, metadata, "PSF_CHISQ");
+    psf->nPSFstars = psMetadataLookupS32 (&status, metadata, "PSF_NSTARS");
+
+    psFree (metadata);
+    return (psf);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSFtry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSFtry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSFtry.c	(revision 22322)
@@ -0,0 +1,861 @@
+/** @file  pmPSFtry.c
+ *
+ *  XXX: need description of file purpose
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.61 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-11 00:38:23 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmSourceUtils.h"
+#include "pmPSFtry.h"
+#include "pmModelClass.h"
+#include "pmModelUtils.h"
+#include "pmSourceFitModel.h"
+#include "pmSourcePhotometry.h"
+
+// ********  pmPSFtry functions  **************************************************
+// * pmPSFtry holds a single pmPSF model test, with the input sources, the freely
+// * fitted version of the model, the pmPSF fit to the fitted model parameters,
+// * and the PSF fits to the source. It also includes the statistics from the
+// * fits, both the individual sources, and the collection
+
+// free a pmPSFtry structure
+static void pmPSFtryFree (pmPSFtry *test)
+{
+    if (test == NULL) return;
+
+    psFree (test->psf);
+    psFree (test->sources);
+    psFree (test->metric);
+    psFree (test->metricErr);
+    psFree (test->fitMag);
+    psFree (test->mask);
+    return;
+}
+
+// allocate a pmPSFtry based on the desired sources and the model (identified by name)
+pmPSFtry *pmPSFtryAlloc (const psArray *sources, const pmPSFOptions *options)
+{
+    pmPSFtry *test = (pmPSFtry *) psAlloc(sizeof(pmPSFtry));
+    psMemSetDeallocator(test, (psFreeFunc) pmPSFtryFree);
+
+    test->psf       = pmPSFAlloc (options);
+    test->metric    = psVectorAlloc (sources->n, PS_TYPE_F32);
+    test->metricErr = psVectorAlloc (sources->n, PS_TYPE_F32);
+    test->fitMag    = psVectorAlloc (sources->n, PS_TYPE_F32);
+    test->mask      = psVectorAlloc (sources->n, PS_TYPE_U8);
+
+    psVectorInit (test->mask,        0);
+    psVectorInit (test->metric,    0.0);
+    psVectorInit (test->metricErr, 0.0);
+    psVectorInit (test->fitMag,    0.0);
+
+    test->sources   = psArrayAlloc (sources->n);
+
+    for (int i = 0; i < sources->n; i++) {
+        test->sources->data[i] = pmSourceCopy (sources->data[i]);
+    }
+
+    return (test);
+}
+
+bool psMemCheckPSFtry(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmPSFtryFree);
+}
+
+
+// build a pmPSFtry for the given model:
+// - fit each source with the free-floating model
+// - construct the pmPSF from the collection of models
+// - fit each source with the PSF-parameter models
+// - measure the pmPSF quality metric (dApResid)
+
+// sources used in for pmPSFtry may be masked by the analysis
+// mask values indicate the reason the source was rejected:
+
+// generate a pmPSFtry with a copy of the test PSF sources
+pmPSFtry *pmPSFtryModel (const psArray *sources, const char *modelName, pmPSFOptions *options, psMaskType maskVal, psMaskType markVal)
+{
+    bool status;
+    int Next = 0;
+    int Npsf = 0;
+
+    // validate the requested model name
+    options->type = pmModelClassGetType (modelName);
+    if (options->type == -1) {
+        psError (PS_ERR_UNKNOWN, true, "invalid model name %s", modelName);
+        return NULL;
+    }
+
+    pmPSFtry *psfTry = pmPSFtryAlloc (sources, options);
+    if (psfTry == NULL) {
+        psError (PS_ERR_UNKNOWN, false, "failed to allocate psf model");
+        return NULL;
+    }
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // stage 1:  fit an EXT model to all candidates PSF sources
+    psTimerStart ("fit");
+    for (int i = 0; i < psfTry->sources->n; i++) {
+
+        pmSource *source = psfTry->sources->data[i];
+        source->modelEXT = pmSourceModelGuess (source, psfTry->psf->type);
+        if (source->modelEXT == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "failed to build model");
+            psFree(psfTry);
+            return NULL;
+        }
+
+        // set object mask to define valid pixels
+        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "OR", markVal);
+
+        // fit model as EXT, not PSF
+        status = pmSourceFitModel (source, source->modelEXT, PM_SOURCE_FIT_EXT, maskVal);
+
+        // clear object mask to define valid pixels
+        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "AND", PS_NOT_U8(markVal));
+
+        // exclude the poor fits
+        if (!status) {
+            psfTry->mask->data.U8[i] = PSFTRY_MASK_EXT_FAIL;
+            psTrace ("psModules.objects", 4, "masking %d (%d,%d) : status is poor\n", i, source->peak->x, source->peak->y);
+            continue;
+        }
+        Next ++;
+    }
+    psLogMsg ("psphot.psftry", 4, "fit ext:   %f sec for %d of %ld sources\n", psTimerMark ("fit"), Next, sources->n);
+    psTrace ("psphot.psftry", 3, "keeping %d of %ld PSF candidates (EXT)\n", Next, sources->n);
+
+    if (Next == 0) {
+        psError(PS_ERR_UNKNOWN, true, "No sources with good extended fits from which to determine PSF.");
+        psFree(psfTry);
+        return NULL;
+    }
+
+    // stage 2: construct a psf (pmPSF) from this collection of model fits
+    if (!pmPSFFromPSFtry (psfTry)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to construct a psf model from collection of sources");
+        psFree(psfTry);
+        return NULL;
+    }
+
+    // stage 3: refit with fixed shape parameters
+    psTimerStart ("fit");
+    for (int i = 0; i < psfTry->sources->n; i++) {
+
+        pmSource *source = psfTry->sources->data[i];
+
+        // masked for: bad model fit, outlier in parameters
+        if (psfTry->mask->data.U8[i] & PSFTRY_MASK_ALL) {
+            psTrace ("psModules.objects", 4, "dropping %d (%d,%d) : source is masked\n", i, source->peak->x, source->peak->y);
+            continue;
+        }
+
+        // set shape for this model based on PSF
+        source->modelPSF = pmModelFromPSF (source->modelEXT, psfTry->psf);
+        if (source->modelPSF == NULL) {
+            psfTry->mask->data.U8[i] = PSFTRY_MASK_BAD_MODEL;
+            abort();
+            continue;
+        }
+        source->modelPSF->radiusFit = options->radius;
+
+        // set object mask to define valid pixels
+        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "OR", markVal);
+
+        // fit the PSF model to the source
+        status = pmSourceFitModel (source, source->modelPSF, PM_SOURCE_FIT_PSF, maskVal);
+
+        // skip poor fits
+        if (!status) {
+            psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "AND", PS_NOT_U8(markVal));
+            psfTry->mask->data.U8[i] = PSFTRY_MASK_PSF_FAIL;
+            psTrace ("psModules.objects", 4, "dropping %d (%d,%d) : failed PSF fit\n", i, source->peak->x, source->peak->y);
+            continue;
+        }
+
+        status = pmSourceMagnitudes (source, psfTry->psf, PM_SOURCE_PHOT_INTERP, maskVal);
+        if (!status || isnan(source->apMag)) {
+            psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "AND", PS_NOT_U8(markVal));
+            psfTry->mask->data.U8[i] = PSFTRY_MASK_BAD_PHOT;
+            psTrace ("psModules.objects", 4, "dropping %d (%d,%d) : poor photometry\n", i, source->peak->x, source->peak->y);
+            continue;
+        }
+
+        // clear object mask to define valid pixels
+        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "AND", PS_NOT_U8(markVal));
+
+        psfTry->fitMag->data.F32[i] = source->psfMag;
+        psfTry->metric->data.F32[i] = source->apMag - source->psfMag;
+        psfTry->metricErr->data.F32[i] = source->errMag;
+
+        psTrace ("psphot.psftry", 6, "keeping source %d (%d) of %ld\n", i, Npsf, psfTry->sources->n);
+        Npsf ++;
+    }
+    psfTry->psf->nPSFstars = Npsf;
+
+    psLogMsg ("psphot.psftry", 4, "fit psf:   %f sec for %d of %ld sources\n", psTimerMark ("fit"), Npsf, sources->n);
+    psTrace ("psphot.psftry", 3, "keeping %d of %ld PSF candidates (PSF)\n", Npsf, sources->n);
+
+    if (Npsf == 0) {
+        psError(PS_ERR_UNKNOWN, true, "No sources with good PSF fits after model is built.");
+        psFree(psfTry);
+        return NULL;
+    }
+
+    // measure the chi-square trend as a function of flux (PAR[PM_PAR_I0])
+    // this should be linear for Poisson errors and quadratic for constant sky errors
+    psVector *flux  = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
+    psVector *chisq = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
+    psVector *mask  = psVectorAlloc (psfTry->sources->n, PS_TYPE_MASK);
+
+    // generate the x and y vectors, and mask missing models
+    for (int i = 0; i < psfTry->sources->n; i++) {
+        pmSource *source = psfTry->sources->data[i];
+        if (source->modelPSF == NULL) {
+            flux->data.F32[i] = 0.0;
+            chisq->data.F32[i] = 0.0;
+            mask->data.U8[i] = 0xff;
+        } else {
+            flux->data.F32[i] = source->modelPSF->params->data.F32[PM_PAR_I0];
+            chisq->data.F32[i] = source->modelPSF->chisq / source->modelPSF->nDOF;
+            mask->data.U8[i] = 0;
+        }
+    }
+
+    // use 3hi/3lo sigma clipping on the chisq fit
+    psStats *stats = options->stats;
+
+    // linear clipped fit of chisq trend vs flux
+    bool result = psVectorClipFitPolynomial1D(psfTry->psf->ChiTrend, options->stats, mask,
+                                              0xff, chisq, NULL, flux);
+    psStatsOptions meanStat = psStatsMeanOption(options->stats->options); // Statistic for mean
+    psStatsOptions stdevStat = psStatsStdevOption(options->stats->options); // Statistic for stdev
+
+    psLogMsg ("pmPSFtry", 4, "chisq vs flux fit: %f +/- %f\n",
+              psStatsGetValue(stats, meanStat), psStatsGetValue(stats, stdevStat));
+
+    psFree(flux);
+    psFree(mask);
+    psFree(chisq);
+
+    if (!result) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to fit psf->ChiTrend");
+        psFree(psfTry);
+        return NULL;
+    }
+
+    for (int i = 0; i < psfTry->psf->ChiTrend->nX + 1; i++) {
+        psLogMsg ("pmPSFtry", 4, "chisq vs flux fit term %d: %f +/- %f\n", i,
+                  psfTry->psf->ChiTrend->coeff[i]*pow(10000, i),
+                  psfTry->psf->ChiTrend->coeffErr[i]*pow(10000,i));
+    }
+
+    // XXX this function wants aperture radius for pmSourcePhotometry
+    if (!pmPSFtryMetric (psfTry, options)) {
+        psError(PS_ERR_UNKNOWN, false, "Attempt to fit PSF with model %s failed.", modelName);
+        psFree (psfTry);
+        return NULL;
+    }
+
+    psLogMsg ("psphot.pspsf", 3, "try model %s, ap-fit: %f +/- %f : sky bias: %f\n",
+              modelName, psfTry->psf->ApResid, psfTry->psf->dApResid, psfTry->psf->skyBias);
+
+    return (psfTry);
+}
+
+bool pmPSFtryMetric (pmPSFtry *psfTry, pmPSFOptions *options)
+{
+    PS_ASSERT_PTR_NON_NULL(psfTry, false);
+    PS_ASSERT_PTR_NON_NULL(options, false);
+    PS_ASSERT_PTR_NON_NULL(psfTry->sources, false);
+
+    float RADIUS = options->radius;
+
+    // the measured (aperture - fit) magnitudes (dA == psfTry->metric)
+    //   depend on both the true ap-fit (dAo) and the bias in the sky measurement:
+    //     dA = dAo + dsky/flux
+    //   where flux is the flux of the star
+    // we fit this trend to find the infinite flux aperture correction (dAo),
+    //   the nominal sky bias (dsky), and the error on dAo
+    // the values of dA are contaminated by stars with close neighbors in the aperture
+    //   we use an outlier rejection to avoid this bias
+
+    // r2rflux = radius^2 * ten(0.4*fitMag);
+    psVector *r2rflux = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
+
+    for (int i = 0; i < psfTry->sources->n; i++) {
+        if (psfTry->mask->data.U8[i] & PSFTRY_MASK_ALL)
+            continue;
+        r2rflux->data.F32[i] = PS_SQR(RADIUS) * pow(10.0, 0.4*psfTry->fitMag->data.F32[i]);
+    }
+
+    // XXX test dump of aperture residual data
+    if (psTraceGetLevel("psModules.objects") >= 5) {
+        FILE *f = fopen ("apresid.dat", "w");
+        for (int i = 0; i < psfTry->sources->n; i++) {
+            int keep = (psfTry->mask->data.U8[i] & PSFTRY_MASK_ALL);
+
+            pmSource *source = psfTry->sources->data[i];
+            float x = source->peak->x;
+            float y = source->peak->y;
+
+            fprintf (f, "%d  %d, %f %f %f  %f %f %f \n",
+                     i, keep, x, y,
+                     psfTry->fitMag->data.F32[i],
+                     r2rflux->data.F32[i],
+                     psfTry->metric->data.F32[i],
+                     psfTry->metricErr->data.F32[i]);
+        }
+        fclose (f);
+    }
+
+    // This analysis of the apResid statistics is only approximate.  The fitted magnitudes
+    // measured at this point (in the PSF fit) use Poisson errors, and are thus biased as a
+    // function of magnitude.  We re-measure the apResid statistics later in psphot using the
+    // linear, constant-error fitting.  Do not reject outliers with excessive vigor here.
+
+    // fit ApTrend only to r2rflux, ignore x,y,flux variations for now
+    // linear clipped fit of ApResid to r2rflux
+    psPolynomial1D *poly = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 1);
+    poly->coeffMask[1] = PS_POLY_MASK_SET; // fit only a constant offset (no SKYBIAS)
+
+    // XXX replace this with a psVectorStats call?  since we are not fitting the trend
+    bool result = psVectorClipFitPolynomial1D(poly, options->stats, psfTry->mask, PSFTRY_MASK_ALL,
+                                              psfTry->metric, psfTry->metricErr, r2rflux);
+    if (!result) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to fit clipped poly");
+
+        psFree(poly);
+        psFree(r2rflux);
+
+        return false;
+    }
+    psStatsOptions stdevStat = psStatsStdevOption(options->stats->options); // Statistic for stdev
+    psLogMsg ("pmPSFtryMetric", 4, "apresid: %f +/- %f; from statistics of %ld psf stars\n", poly->coeff[0],
+              psStatsGetValue(options->stats, stdevStat), psfTry->sources->n);
+
+
+
+    // XXX test dump of fitted model (dump when tracing?)
+    if (psTraceGetLevel("psModules.objects") >= 4) {
+        FILE *f = fopen ("resid.dat", "w");
+        psVector *apfit = psPolynomial1DEvalVector (poly, r2rflux);
+        for (int i = 0; i < psfTry->sources->n; i++) {
+            int keep = (psfTry->mask->data.U8[i] & PSFTRY_MASK_ALL);
+
+            pmSource *source = psfTry->sources->data[i];
+            float x = source->peak->x;
+            float y = source->peak->y;
+
+            fprintf (f, "%d  %d, %f %f %f  %f %f %f  %f\n",
+                     i, keep, x, y,
+                     psfTry->fitMag->data.F32[i],
+                     r2rflux->data.F32[i],
+                     psfTry->metric->data.F32[i],
+                     psfTry->metricErr->data.F32[i],
+                     apfit->data.F32[i]);
+        }
+        fclose (f);
+        psFree (apfit);
+    }
+
+    // XXX drop the skyBias value, or include above??
+    psfTry->psf->skyBias  = poly->coeff[1];
+    psfTry->psf->ApResid  = poly->coeff[0];
+    psfTry->psf->dApResid = psStatsGetValue(options->stats, stdevStat);
+
+    psFree (r2rflux);
+    psFree (poly);
+
+    return true;
+}
+
+/*
+  (aprMag' - fitMag) = rflux*skyBias + ApTrend(x,y)
+  (aprMag - rflux*skyBias) - fitMag = ApTrend(x,y)
+  (aprMag - rflux*skyBias) = fitMag + ApTrend(x,y)
+*/
+
+/*****************************************************************************
+pmPSFFromPSFtry (psfTry): build a PSF model from a collection of
+source->modelEXT entries.  The PSF ignores the first 4 (independent) model
+parameters and constructs a polynomial fit to the remaining as a function of
+image coordinate.
+    input: psfTry with fitted source->modelEXT collection, pre-allocated psf
+Note: some of the array entries may be NULL (failed fits); ignore them.
+ *****************************************************************************/
+bool pmPSFFromPSFtry (pmPSFtry *psfTry)
+{
+    PS_ASSERT_PTR_NON_NULL(psfTry, false);
+    PS_ASSERT_PTR_NON_NULL(psfTry->sources, false);
+
+    pmPSF *psf = psfTry->psf;
+
+    // construct the fit vectors from the collection of objects
+    psVector *x  = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
+    psVector *y  = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
+    psVector *z  = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
+
+    // construct the x,y terms
+    for (int i = 0; i < psfTry->sources->n; i++) {
+        pmSource *source = psfTry->sources->data[i];
+        if (source->modelEXT == NULL)
+            continue;
+
+        x->data.F32[i] = source->modelEXT->params->data.F32[PM_PAR_XPOS];
+        y->data.F32[i] = source->modelEXT->params->data.F32[PM_PAR_YPOS];
+    }
+
+    if (!pmPSFFitShapeParams (psf, psfTry->sources, x, y, psfTry->mask)) {
+        psFree(x);
+        psFree(y);
+        psFree(z);
+        return false;
+    }
+
+    // skip the unfitted parameters (X, Y, Io, Sky) and the shape parameters (SXX, SYY, SXY)
+    // apply the values of Nx, Ny determined above for E0,E1,E2 to the remaining parameters
+    for (int i = 0; i < psf->params->n; i++) {
+        switch (i) {
+          case PM_PAR_SKY:
+          case PM_PAR_I0:
+          case PM_PAR_XPOS:
+          case PM_PAR_YPOS:
+          case PM_PAR_SXX:
+          case PM_PAR_SYY:
+          case PM_PAR_SXY:
+            continue;
+          default:
+            break;
+        }
+
+        // select the per-object fitted data for this parameter
+        for (int j = 0; j < psfTry->sources->n; j++) {
+            pmSource *source = psfTry->sources->data[j];
+            if (source->modelEXT == NULL) continue;
+            z->data.F32[j] = source->modelEXT->params->data.F32[i];
+        }
+
+        psImageBinning *binning = psImageBinningAlloc();
+        binning->nXruff = psf->trendNx;
+        binning->nYruff = psf->trendNy;
+        binning->nXfine = psf->fieldNx;
+        binning->nYfine = psf->fieldNy;
+
+        if (psf->psfTrendMode == PM_TREND_MAP) {
+            psImageBinningSetScale (binning, PS_IMAGE_BINNING_CENTER);
+            psImageBinningSetSkipByOffset (binning, psf->fieldXo, psf->fieldYo);
+        }
+
+        // free existing trend, re-alloc
+        psFree (psf->params->data[i]);
+        psf->params->data[i] = pmTrend2DNoImageAlloc (psf->psfTrendMode, binning, psf->psfTrendStats);
+        psFree (binning);
+
+        // fit the collection of measured parameters to the PSF 2D model
+        // the mask is carried from previous steps and updated with this operation
+        // the weight is either the flux error or NULL, depending on 'psf->poissonErrorParams'
+        if (!pmTrend2DFit (psf->params->data[i], psfTry->mask, 0xff, x, y, z, NULL)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to build psf model for parameter %d", i);
+            psFree(x);
+            psFree(y);
+            psFree(z);
+            return false;
+        }
+    }
+
+    // test dump of star parameters vs position (compare with fitted values)
+    if (psTraceGetLevel("psModules.objects") >= 4) {
+        FILE *f = fopen ("params.dat", "w");
+
+        for (int j = 0; j < psfTry->sources->n; j++) {
+            pmSource *source = psfTry->sources->data[j];
+            if (source == NULL) continue;
+            if (source->modelEXT == NULL) continue;
+
+            pmModel *modelPSF = pmModelFromPSF (source->modelEXT, psf);
+
+            fprintf (f, "%f %f : ", source->modelEXT->params->data.F32[PM_PAR_XPOS], source->modelEXT->params->data.F32[PM_PAR_YPOS]);
+
+            for (int i = 0; i < psf->params->n; i++) {
+                if (psf->params->data[i] == NULL) continue;
+                fprintf (f, "%f %f : ", source->modelEXT->params->data.F32[i], modelPSF->params->data.F32[i]);
+            }
+            fprintf (f, "%f %d\n", source->modelEXT->chisq, source->modelEXT->nIter);
+
+            psFree(modelPSF);
+        }
+        fclose (f);
+    }
+
+    psFree (x);
+    psFree (y);
+    psFree (z);
+    return true;
+}
+
+
+bool pmPSFFitShapeParams (pmPSF *psf, psArray *sources, psVector *x, psVector *y, psVector *srcMask) {
+
+    // we are doing a robust fit.  after each pass, we drop points which are more deviant than
+    // three sigma.  mask is currently updated for each pass.
+
+    // The shape parameters (SXX, SXY, SYY) are strongly coupled.  We have to handle them very
+    // carefully.  First, we convert them to the Ellipse Polarization terms (E0, E1, E2) for
+    // each source and fit this set of parameters.  These values are less tightly coupled, but
+    // are still inter-related.  The fitted values do a good job of constraining the major axis
+    // and the position angle, but the minor axis is weakly measured.  When we apply the PSF
+    // model to construct a source model, we convert the fitted values of E0,E1,E2 to the shape
+    // parameters, with the constraint that the minor axis must be greater than a minimum
+    // threshold.
+
+    // convert the measured source shape paramters to polarization terms
+    psVector *e0   = psVectorAlloc (sources->n, PS_TYPE_F32);
+    psVector *e1   = psVectorAlloc (sources->n, PS_TYPE_F32);
+    psVector *e2   = psVectorAlloc (sources->n, PS_TYPE_F32);
+    psVector *mag  = psVectorAlloc (sources->n, PS_TYPE_F32);
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+        if (source->modelEXT == NULL) continue;
+        // XXX I am relying on the fact that none of the masked sources
+        // have modelEXT set here.  perhaps use the value of psfTry->mask instead?
+
+        psEllipsePol pol = pmPSF_ModelToFit (source->modelEXT->params->data.F32);
+
+        e0->data.F32[i] = pol.e0;
+        e1->data.F32[i] = pol.e1;
+        e2->data.F32[i] = pol.e2;
+
+        float flux = source->modelEXT->params->data.F32[PM_PAR_I0];
+        mag->data.F32[i] = (flux > 0.0) ? -2.5*log(flux) : -100.0;
+    }
+
+    if (psf->psfTrendMode == PM_TREND_MAP) {
+        float errorFloor = 0.0;
+        float errorTotal = 0.0;
+        float errorTotalMin = FLT_MAX;
+        int entryMin = -1;
+
+        psVector *dz = NULL;
+        psVector *mask = psVectorAlloc (sources->n, PS_TYPE_U8);
+
+        // check the fit residuals and increase Nx,Ny until the error is minimized
+        // pmPSFParamTrend increases the number along the longer of x or y
+        for (int i = 1; i < PS_MAX (psf->trendNx, psf->trendNy); i++) {
+
+            psVectorInit (mask, 0);
+            if (!pmPSFFitShapeParamsMap (psf, i, &errorFloor, &errorTotal, mask, x, y, mag, e0, e1, e2, dz)) {
+                break;
+            }
+
+            // we do not have a good model for the error distributions on e0, e1, e2.
+            // use the bright end scatter from the constant fit to set the scale for the
+            // versions with 2D variation.  potentially scale by poisson errors...
+            if (i == 1) {
+                // if (psf->poissonErrorsParams) do something else..
+                dz = psVectorAlloc (sources->n, PS_TYPE_F32);
+                for (int i = 0; i < sources->n; i++) {
+                    dz->data.F32[i] = errorFloor;
+                }
+            }
+
+            // store the resulting errorTotal values and the scales, redo the best
+            if (errorTotal < errorTotalMin) {
+                errorTotalMin = errorTotal;
+                entryMin = i;
+            }
+        }
+        if (entryMin == -1) {
+            psError (PS_ERR_UNKNOWN, true, "failed to find image map for shape params");
+            return false;
+        }
+
+        // XXX supply the resulting mask values back to srcMask
+        psVectorInit (mask, 0);
+        if (!pmPSFFitShapeParamsMap (psf, entryMin, &errorFloor, &errorTotal, mask, x, y, mag, e0, e1, e2, dz)) {
+            psAbort ("failed pmPSFFitShapeParamsMap on second pass?");
+        }
+
+        pmTrend2D *trend = psf->params->data[PM_PAR_E0];
+        psf->trendNx = trend->map->map->numCols;
+        psf->trendNy = trend->map->map->numRows;
+
+        psFree (mask);
+        psFree (dz);
+    } else {
+
+        // XXX iterate Nx, Ny based on scatter?
+        // XXX we force the x & y order to be the same
+        // modify the order to correspond to the actual number of matched stars:
+        int order = PS_MAX (psf->trendNx, psf->trendNy);
+        if ((sources->n < 15) && (order >= 3)) order = 2;
+        if ((sources->n < 11) && (order >= 2)) order = 1;
+        if ((sources->n <  8) && (order >= 1)) order = 0;
+        if ((sources->n <  3)) {
+            psError (PS_ERR_UNKNOWN, true, "failed to fit polynomial to shape params");
+            return false;
+        }
+        psf->trendNx = order;
+        psf->trendNy = order;
+
+        // we run 'clipIter' cycles clipping in each of x and y, with only one iteration each.
+        // This way, the parameters masked by one of the fits will be applied to the others
+        for (int i = 0; i < psf->psfTrendStats->clipIter; i++) {
+
+            // XXX we are using the same stats structure on each pass: do we need to re-init it?
+            bool status = true;
+            status &= pmTrend2DFit (psf->params->data[PM_PAR_E0], srcMask, 0xff, x, y, e0, NULL);
+            psTrace ("psModules.pmPSFtry", 4, "clipped E0 : keeping %ld of %ld\n", psf->psfTrendStats->clippedNvalues, e0->n);
+            status &= pmTrend2DFit (psf->params->data[PM_PAR_E1], srcMask, 0xff, x, y, e1, NULL);
+            psTrace ("psModules.pmPSFtry", 4, "clipped E1 : keeping %ld of %ld\n", psf->psfTrendStats->clippedNvalues, e1->n);
+            status &= pmTrend2DFit (psf->params->data[PM_PAR_E2], srcMask, 0xff, x, y, e2, NULL);
+            psTrace ("psModules.pmPSFtry", 4, "clipped E2 : keeping %ld of %ld\n", psf->psfTrendStats->clippedNvalues, e2->n);
+
+            if (!status) {
+                psError (PS_ERR_UNKNOWN, true, "failed to fit polynomial to shape params");
+                return false;
+            }
+        }
+    }
+
+    // test dump of the psf parameters
+    if (psTraceGetLevel("psModules.objects") >= 4) {
+        FILE *f = fopen ("pol.dat", "w");
+        fprintf (f, "# x y  :  e0obs e1obs e2obs  : e0fit e1fit e2fit : mask\n");
+        for (int i = 0; i < e0->n; i++) {
+            fprintf (f, "%f %f  :  %f %f %f  : %f %f %f  : %d\n",
+                     x->data.F32[i], y->data.F32[i],
+                     e0->data.F32[i], e1->data.F32[i], e2->data.F32[i],
+                     pmTrend2DEval (psf->params->data[PM_PAR_E0], x->data.F32[i], y->data.F32[i]),
+                     pmTrend2DEval (psf->params->data[PM_PAR_E1], x->data.F32[i], y->data.F32[i]),
+                     pmTrend2DEval (psf->params->data[PM_PAR_E2], x->data.F32[i], y->data.F32[i]),
+                     srcMask->data.U8[i]);
+        }
+        fclose (f);
+    }
+
+    psFree (e0);
+    psFree (e1);
+    psFree (e2);
+    psFree (mag);
+    return true;
+}
+
+// fit the shape variations as a psImageMap for the given scale factor
+bool pmPSFFitShapeParamsMap (pmPSF *psf, int scale, float *errorFloor, float *errorTotal, psVector *mask, psVector *x, psVector *y, psVector *mag, psVector *e0obs, psVector *e1obs, psVector *e2obs, psVector *dz) {
+
+    int Nx, Ny;
+
+    // set the map scale to match the aspect ratio
+    if (psf->fieldNx > psf->fieldNy) {
+        Nx = scale;
+        Ny = PS_MAX (1, Nx * (psf->fieldNy / psf->fieldNx));
+    } else {
+        Ny = scale;
+        Nx = PS_MAX (1, Ny * (psf->fieldNx / psf->fieldNy));
+    }
+
+    // do we have enough sources for this fine of a grid?
+    if (x->n < 3*Nx*Ny) {
+        return false;
+    }
+
+    // the mask marks the values not used to calculate the ApTrend
+    psVectorInit (mask, 0);
+
+    // XXX check this against the exising type
+    pmTrend2DMode psfTrendMode = PM_TREND_MAP;
+
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXruff = Nx;
+    binning->nYruff = Ny;
+    binning->nXfine = psf->fieldNx;
+    binning->nYfine = psf->fieldNy;
+    psImageBinningSetScale (binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkipByOffset (binning, psf->fieldXo, psf->fieldYo);
+
+    psFree (psf->params->data[PM_PAR_E0]);
+    psFree (psf->params->data[PM_PAR_E1]);
+    psFree (psf->params->data[PM_PAR_E2]);
+
+    int nIter = psf->psfTrendStats->clipIter;
+    psf->psfTrendStats->clipIter = 1;
+    psf->params->data[PM_PAR_E0] = pmTrend2DNoImageAlloc (psfTrendMode, binning, psf->psfTrendStats);
+    psf->params->data[PM_PAR_E1] = pmTrend2DNoImageAlloc (psfTrendMode, binning, psf->psfTrendStats);
+    psf->params->data[PM_PAR_E2] = pmTrend2DNoImageAlloc (psfTrendMode, binning, psf->psfTrendStats);
+    psFree (binning);
+
+    // we run 'clipIter' cycles clipping in each of x and y, with only one iteration each.
+    // This way, the parameters masked by one of the fits will be applied to the others
+    for (int i = 0; i < nIter; i++) {
+        // XXX we are using the same stats structure on each pass: do we need to re-init it?
+        pmTrend2DFit (psf->params->data[PM_PAR_E0], mask, 0xff, x, y, e0obs, dz);
+        psTrace ("psModules.objects", 5, "clipped E0 : keeping %ld of %ld\n", psf->psfTrendStats->clippedNvalues, e0obs->n);
+        pmTrend2DFit (psf->params->data[PM_PAR_E1], mask, 0xff, x, y, e1obs, dz);
+        psTrace ("psModules.objects", 5, "clipped E1 : keeping %ld of %ld\n", psf->psfTrendStats->clippedNvalues, e1obs->n);
+        pmTrend2DFit (psf->params->data[PM_PAR_E2], mask, 0xff, x, y, e2obs, dz);
+        psTrace ("psModules.objects", 5, "clipped E2 : keeping %ld of %ld\n", psf->psfTrendStats->clippedNvalues, e2obs->n);
+    }
+    psf->psfTrendStats->clipIter = nIter; // restore default setting
+
+    // construct the fitted values and the residuals
+    psVector *e0fit = pmTrend2DEvalVector (psf->params->data[PM_PAR_E0], x, y);
+    psVector *e1fit = pmTrend2DEvalVector (psf->params->data[PM_PAR_E1], x, y);
+    psVector *e2fit = pmTrend2DEvalVector (psf->params->data[PM_PAR_E2], x, y);
+
+    psVector *e0res = (psVector *) psBinaryOp (NULL, (void *) e0obs, "-", (void *) e0fit);
+    psVector *e1res = (psVector *) psBinaryOp (NULL, (void *) e1obs, "-", (void *) e1fit);
+    psVector *e2res = (psVector *) psBinaryOp (NULL, (void *) e2obs, "-", (void *) e2fit);
+
+    // measure errors on the map pixels
+    pmTrend2D *trend;
+
+    trend = psf->params->data[PM_PAR_E0];
+    float mapErrorSum = 0.0;
+    float mapError = 0.0;
+    for (int iy = 0; iy < trend->map->error->numRows; iy++) {
+        for (int ix = 0; ix < trend->map->error->numCols; ix++) {
+            mapError += PS_SQR (trend->map->error->data.F32[iy][ix]);
+        }
+    }
+    psTrace ("psModules.objects", 4, "E0 error: %f\n", sqrt(mapError));
+    mapErrorSum += mapError;
+
+    trend = psf->params->data[PM_PAR_E1];
+    mapError = 0.0;
+    for (int iy = 0; iy < trend->map->error->numRows; iy++) {
+        for (int ix = 0; ix < trend->map->error->numCols; ix++) {
+            mapError += PS_SQR (trend->map->error->data.F32[iy][ix]);
+        }
+    }
+    psTrace ("psModules.objects", 4, "E1 error: %f\n", sqrt(mapError));
+    mapErrorSum += mapError;
+
+    trend = psf->params->data[PM_PAR_E2];
+    mapError = 0.0;
+    for (int iy = 0; iy < trend->map->error->numRows; iy++) {
+        for (int ix = 0; ix < trend->map->error->numCols; ix++) {
+            mapError += PS_SQR (trend->map->error->data.F32[iy][ix]);
+        }
+    }
+    psTrace ("psModules.objects", 4, "E2 error: %f\n", sqrt(mapError));
+    mapErrorSum += mapError;
+
+    psTrace ("psModules.objects", PS_LOG_INFO, "total map error: %f\n", sqrt(mapErrorSum));
+
+    // measure systematic errorFloor & systematic / photon scale factor
+    // XXX this is a bit arbitrary, but it forces ~3 stars from the bright bin per spatial bin
+    int nGroup = PS_MAX (3*Nx*Ny, 10);
+    pmPSFShapeParamsErrors (errorFloor, mag, e0res, e1res, e2res, mask, nGroup,
+                            psStatsStdevOption(psf->psfTrendStats->options));
+
+    *errorTotal = sqrt(PS_SQR(*errorFloor) + mapErrorSum);
+
+    psLogMsg ("psphot.psftry", PS_LOG_INFO, "result of %d x %d grid (%d stars per bin)\n", Nx, Ny, nGroup);
+    psLogMsg ("psphot.psftry", PS_LOG_INFO, "systematic scatter floor: %f, error map total: %f\n", *errorFloor, *errorTotal);
+
+    psFree (e0fit);
+    psFree (e1fit);
+    psFree (e2fit);
+
+    psFree (e0res);
+    psFree (e1res);
+    psFree (e2res);
+
+    return true;
+}
+
+// calculate the minimum scatter of the parameters
+bool pmPSFShapeParamsErrors(float *errorFloor, psVector *mag, psVector *e0res, psVector *e1res,
+                            psVector *e2res, psVector *mask, int nGroup, psStatsOptions stdevOpt)
+{
+
+    psStats *statsS = psStatsAlloc(stdevOpt);
+
+    // measure the trend in bins with 10 values each; if < 10 total, use them all
+    int nBin = PS_MAX (mag->n / nGroup, 1);
+
+    // use mag to group parameters in sequence
+    psVector *index = psVectorSortIndex (NULL, mag);
+
+    // subset vectors for mag and dap values within the given range
+    psVector *dE0subset = psVectorAllocEmpty (nGroup, PS_TYPE_F32);
+    psVector *dE1subset = psVectorAllocEmpty (nGroup, PS_TYPE_F32);
+    psVector *dE2subset = psVectorAllocEmpty (nGroup, PS_TYPE_F32);
+    psVector *mkSubset  = psVectorAllocEmpty (nGroup, PS_TYPE_U8);
+
+    int n = 0;
+    float min = INFINITY;               // Minimum error
+    for (int i = 0; i < nBin; i++) {
+        int j;
+        for (j = 0; (j < nGroup) && (n < mag->n); j++, n++) {
+            int N = index->data.U32[n];
+            dE0subset->data.F32[j] = e0res->data.F32[N];
+            dE1subset->data.F32[j] = e1res->data.F32[N];
+            dE2subset->data.F32[j] = e2res->data.F32[N];
+
+            mkSubset->data.U8[j]   = mask->data.U8[N];
+        }
+        dE0subset->n = j;
+        dE1subset->n = j;
+        dE2subset->n = j;
+        mkSubset->n = j;
+
+        // calculate the root-mean-square of E0, E1, E2
+        float dEsquare = 0.0;
+        psStatsInit (statsS);
+        psVectorStats (statsS, dE0subset, NULL, mkSubset, 0xff);
+        dEsquare += PS_SQR(psStatsGetValue(statsS, stdevOpt));
+
+        psStatsInit (statsS);
+        psVectorStats (statsS, dE1subset, NULL, mkSubset, 0xff);
+        dEsquare += PS_SQR(psStatsGetValue(statsS, stdevOpt));
+
+        psStatsInit (statsS);
+        psVectorStats (statsS, dE2subset, NULL, mkSubset, 0xff);
+        dEsquare += PS_SQR(psStatsGetValue(statsS, stdevOpt));
+
+        if (isfinite(dEsquare)) {
+            float err = sqrtf(dEsquare);
+            if (err < min) {
+                min = err;
+            }
+        }
+    }
+    psFree (dE0subset);
+    psFree (dE1subset);
+    psFree (dE2subset);
+    psFree (mkSubset);
+
+    psFree(index);
+
+    psFree(statsS);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSFtry.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSFtry.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPSFtry.h	(revision 22322)
@@ -0,0 +1,134 @@
+/* @file  pmPSFtry.h
+ *
+ * This file contains code that allows the user to try to fit several
+ * PSF models to an image.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-12-15 01:23:18 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_PSF_TRY_H
+# define PM_PSF_TRY_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/**
+ *
+ * This structure contains a pointer to the collection of sources which will
+ * be used to test the PSF model form. It lists the pmModelType type of model
+ * being tests, and contains an element to store the resulting psf
+ * representation. In addition, this structure carries the complete collection of
+ * EXT (floating parameter) and PSF (fixed parameter) model fits to each of the
+ * sources modelEXT and modelPSF. It also contains a mask which is set by the
+ * model fitting and psf fitting steps. For each model, the value of the quality
+ * metric is stored in the vector metric and the fitted instrumental magnitude is
+ * stored in fitMag. The quality metric for the PSF model is the aperture
+ * magnitude minus the fitted magnitude for each source. This collection of
+ * aperture residuals is examined in the analysis process, and a linear trend of
+ * the residual with the inverse object flux (ie, 100:4his structure contains a
+ * pointer to the collection of sources which will be used to test the PSF model
+ * form. It lists the pmModelType type of modmag) is fitted. The result of this
+ * fit is a measured sky bias (systematic error in the sky measured by the fits),
+ * an effective infinite-magnitude aperture correction (ApResid), and the scatter
+ * of the aperture correction for the ensemble of PSF stars (dApResid). The
+ * ultimate metric to intercompare multiple types of PSF models is the value of
+ * the aperture correction scatter.
+ *
+ * XXX: There are many more members in the SDRS then in the prototype code.
+ * I stuck with the prototype code.
+ *
+ *
+ */
+typedef struct
+{
+    pmPSF      *psf;                    ///< Add comment.
+    psArray    *sources;                ///< pointers to the original sources
+    psVector   *mask;                   ///< Add comment.
+    psVector   *metric;                 ///< Add comment.
+    psVector   *metricErr;              ///< Add comment.
+    psVector   *fitMag;                 ///< Add comment.
+}
+pmPSFtry;
+
+
+/** pmPSFtryMaskValues
+ *
+ * The following datatype defines the masks used by the pmPSFtry analysis to
+ * identify sources which should or should not be included in the analysis.
+ *
+ */
+typedef enum {
+    PSFTRY_MASK_CLEAR    = 0x00,        ///< Add comment.
+    PSFTRY_MASK_OUTLIER  = 0x01,        ///< 1: outlier in psf polynomial fit (provided by psPolynomials)
+    PSFTRY_MASK_EXT_FAIL = 0x02,        ///< 2: ext model failed to converge
+    PSFTRY_MASK_PSF_FAIL = 0x04,        ///< 3: psf model failed to converge
+    PSFTRY_MASK_BAD_PHOT = 0x08,        ///< 4: invalid source photometry
+    PSFTRY_MASK_BAD_MODEL= 0x10,        ///< 5: could not build PSF from EXT (!??)
+    PSFTRY_MASK_ALL      = 0x1f,        ///< Add comment.
+} pmPSFtryMaskValues;
+
+
+/** pmPSFtryAlloc()
+ *
+ * Allocate a pmPSFtry data structure.
+ *
+ */
+
+pmPSFtry *pmPSFtryAlloc (const psArray *sources, const pmPSFOptions *options);
+bool psMemCheckPSFtry(psPtr ptr);
+
+/** pmPSFtryModel()
+ *
+ * This function takes the input collection of sources and performs a complete
+ * analysis to determine a PSF model of the given type (specified by model name).
+ * The result is a pmPSFtry with the results of the analysis.
+ *
+ */
+pmPSFtry *pmPSFtryModel (const psArray *sources, const char *modelName, pmPSFOptions *options, psMaskType maskVal, psMaskType mark);
+
+/** pmPSFtryMetric()
+ *
+ * This function is used to measure the PSF model metric for the set of
+ * results contained in the pmPSFtry structure.
+ *
+ */
+bool pmPSFtryMetric(
+    pmPSFtry *psfTry,                  ///< Add comment.
+    pmPSFOptions *options              ///< PSF fitting options
+);
+
+/** pmPSFtryMetric_Alt()
+ *
+ * This function is used to measure the PSF model metric for the set of
+ * results contained in the pmPSFtry structure (alternative implementation).
+ *
+ */
+bool pmPSFtryMetric_Alt(
+    pmPSFtry *try,                      ///< Add comment.
+    float RADIUS                        ///< Add comment.
+);
+
+/**
+ *
+ * This function takes a collection of pmModel fitted models from across a
+ * single image and builds a pmPSF representation of the PSF. The input array of
+ * model fits may consist of entries to be ignored (noted by a non-zero mask
+ * entry). The analysis of the models fits a 2D polynomial for each parameter to
+ * the collection of model parameters as a function of position (and
+ * normalization?). In this process, some of the input models may be marked as
+ * outliers and excluded from the fit. These elements will be marked with a
+ * specific mask value (1 == PSFTRY_MASK_OUTLIER).
+ *
+ */
+bool pmPSFFromPSFtry (pmPSFtry *psfTry);
+
+bool pmPSFFitShapeParams (pmPSF *psf, psArray *sources, psVector *x, psVector *y, psVector *srcMask);
+bool pmPSFFitShapeParamsMap (pmPSF *psf, int scale, float *errorFloor, float *errorTotal, psVector *mask, psVector *x, psVector *y, psVector *mag, psVector *e0obs, psVector *e1obs, psVector *e2obs, psVector *dz);
+bool pmPSFShapeParamsErrors (float *errorFloor, psVector *mag, psVector *e0res, psVector *e1res, psVector *e2res, psVector *mask, int nGroup, psStatsOptions stdevOpt);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPeaks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPeaks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPeaks.c	(revision 22322)
@@ -0,0 +1,569 @@
+/** @file  pmPeaks.c
+ *
+ *  This file defines functions to detect and manipulate peaks in images
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA: significant modifications.
+ *
+ *  @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-08 19:41:56 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmPeaks.h"
+
+/******************************************************************************
+AddPeak(): A private function which allocates a psArray, if the peaks
+argument is NULL, otherwise it adds the peak to that array.
+XXX EAM : row,col now refer to image coords, NOT parent (since this is private) 
+XXX EAM : now also calculates fractional peak positions from 3x3 bicube region
+*****************************************************************************/
+static psArray *AddPeak(psArray *peaks,
+                        const psImage *image,
+                        psS32 row,
+                        psS32 col,
+                        pmPeakType type)
+{
+    psTrace("psModules.objects", 5, "---- begin ----\n");
+
+    if (peaks == NULL) {
+        peaks = psArrayAllocEmpty(100);
+    }
+
+    // the peak position is in parent coordinates
+    pmPeak *peak = pmPeakAlloc(col + image->col0, row + image->row0, image->data.F32[row][col], type);
+
+    // measure fractional peak position using the 3x3 bicube fit
+
+    // ix,iy must land on image with 1 pixel border
+    int ix = PS_MAX (PS_MIN (col, image->numCols - 2), 1);
+    int iy = PS_MAX (PS_MIN (row, image->numRows - 2), 1);
+
+    // calculate peak position relative to ix,iy
+    // XXX these functions need to take a mask, weight, and calculate the errors
+    psPolynomial2D *bicube = psImageBicubeFit (image, ix + image->col0, iy + image->row0);
+    psPlane min = psImageBicubeMin (bicube);
+    psFree (bicube);
+
+    // if min point is too deviant, use the peak value
+    if ((fabs(min.x) < 1.5) && (fabs(min.y) < 1.5)) {
+        peak->xf = min.x + ix + image->col0;
+        peak->yf = min.y + iy + image->row0;
+	peak->dx = 0.0;
+	peak->dy = 0.0;
+    } else {
+        peak->xf = ix;
+        peak->yf = iy;
+	peak->dx = 1.0;
+	peak->dy = 1.0;
+    }
+
+    psArrayAdd(peaks, 100, peak);
+    psFree (peak);
+
+    psTrace("psModules.objects", 5, "---- end ----\n");
+    return(peaks);
+}
+
+/******************************************************************************
+getRowVectorFromImage(): a private function which simply returns a
+psVector containing the specified row of data from the psImage.
+ 
+XXX: Is there a better way to do this?
+XXX EAM: does this really need to alloc a new vector???
+*****************************************************************************/
+static psVector *getRowVectorFromImage(psImage *image,
+                                       psU32 row)
+{
+    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+
+    psVector *tmpVector = psVectorAlloc(image->numCols, PS_TYPE_F32);
+    for (psU32 col = 0; col < image->numCols ; col++) {
+        tmpVector->data.F32[col] = image->data.F32[row][col];
+    }
+    psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+    return(tmpVector);
+}
+
+/******************************************************************************
+isItInThisRegion(): a private function which simply returns a
+boolean denoting if specified coordinate is in the region.
+XXX: Macro this.
+*****************************************************************************/
+static bool isItInThisRegion(const psRegion valid,
+                             psS32 x,
+                             psS32 y)
+{
+    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+    if ((x >= valid.x0) &&
+            (x <= valid.x1) &&
+            (y >= valid.y0) &&
+            (y <= valid.y1)) {
+        psTrace("psModules.objects", 4, "---- %s(true) end ----\n", __func__);
+        return(true);
+    }
+    psTrace("psModules.objects", 4, "---- %s(false) end ----\n", __func__);
+    return(false);
+}
+
+/******************************************************************************
+pmPeakAlloc(): Allocate the pmPeak data structure and set appropriate members.
+*****************************************************************************/
+static void peakFree(pmPeak *tmp)
+{} //
+
+pmPeak *pmPeakAlloc(psS32 x,
+                    psS32 y,
+                    psF32 value,
+                    pmPeakType type)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    static int id = 1;
+    pmPeak *tmp = (pmPeak *) psAlloc(sizeof(pmPeak));
+    *(int *)&tmp->id = id++;
+    tmp->x = x;
+    tmp->y = y;
+    tmp->value = value;
+    tmp->flux = 0;
+    tmp->SN = 0;
+    tmp->xf = x;
+    tmp->yf = y;
+    tmp->assigned = false;
+    tmp->type = type;
+    tmp->footprint = NULL;
+
+    psMemSetDeallocator(tmp, (psFreeFunc) peakFree);
+
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(tmp);
+}
+
+bool psMemCheckPeak(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) peakFree);
+}
+
+
+// psSort comparison function for peaks
+// XXX: Add error-checking for NULL args
+int pmPeaksCompareAscend (const void **a, const void **b)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    pmPeak *A = *(pmPeak **)a;
+    pmPeak *B = *(pmPeak **)b;
+
+    psF32 diff;
+
+    diff = A->value - B->value;
+    if (diff < FLT_EPSILON) {
+        psTrace("psModules.objects", 3, "---- %s(-1) end ----\n", __func__);
+        return (-1);
+    } else if (diff > FLT_EPSILON) {
+        psTrace("psModules.objects", 3, "---- %s(+1) end ----\n", __func__);
+        return (+1);
+    }
+    psTrace("psModules.objects", 3, "---- %s(0) end ----\n", __func__);
+    return (0);
+}
+
+// psSort comparison function for peaks
+// XXX: Add error-checking for NULL args
+int pmPeaksCompareDescend (const void **a, const void **b)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    pmPeak *A = *(pmPeak **)a;
+    pmPeak *B = *(pmPeak **)b;
+
+    psF32 diff;
+
+    diff = A->value - B->value;
+    if (diff < FLT_EPSILON) {
+        psTrace("psModules.objects", 3, "---- %s(+1) end ----\n", __func__);
+        return (+1);
+    } else if (diff > FLT_EPSILON) {
+        psTrace("psModules.objects", 3, "---- %s(-1) end ----\n", __func__);
+        return (-1);
+    }
+    psTrace("psModules.objects", 3, "---- %s(0) end ----\n", __func__);
+    return (0);
+}
+
+// sort by SN (descending)
+int pmPeakSortBySN (const void **a, const void **b)
+{
+    pmPeak *A = *(pmPeak **)a;
+    pmPeak *B = *(pmPeak **)b;
+
+    psF32 fA = A->SN;
+    psF32 fB = B->SN;
+    if (isnan (fA)) fA = 0;
+    if (isnan (fB)) fB = 0;
+
+    psF32 diff = fA - fB;
+    if (diff > FLT_EPSILON) return (-1);
+    if (diff < FLT_EPSILON) return (+1);
+    return (0);
+}
+
+// sort by Y (ascending)
+int pmPeakSortByY (const void **a, const void **b)
+{
+    pmPeak *A = *(pmPeak **)a;
+    pmPeak *B = *(pmPeak **)b;
+
+    psF32 fA = A->y;
+    psF32 fB = B->y;
+
+    psF32 diff = fA - fB;
+    if (diff > FLT_EPSILON) return (+1);
+    if (diff < FLT_EPSILON) return (-1);
+    return (0);
+}
+
+/******************************************************************************
+pmPeaksInVector(vector, threshold): Find all local peaks in the given vector
+above the given threshold.  Returns a vector of type PS_TYPE_U32 containing
+the location (x value) of all peaks.
+ 
+XXX: What types should be supported?  Only F32 is implemented.
+ 
+XXX: We currently step through the input vector twice; once to determine the
+size of the output vector, then to set the values of the output vector.
+Depending upon actual use, this may need to be optimized.
+*****************************************************************************/
+psVector *pmPeaksInVector(const psVector *vector,
+			 psF32 threshold)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_VECTOR_NON_NULL(vector, NULL);
+    PS_ASSERT_VECTOR_NON_EMPTY(vector, NULL);
+    PS_ASSERT_VECTOR_TYPE(vector, PS_TYPE_F32, NULL);
+    int count = 0;
+    int n = vector->n;
+
+    //
+    // Special case: the input vector has a single element.
+    //
+    if (n == 1) {
+        psVector *tmpVector = NULL;
+        if (vector->data.F32[0] > threshold) {
+            tmpVector = psVectorAlloc(1, PS_TYPE_U32);
+            tmpVector->data.U32[0] = 0;
+        } else {
+            tmpVector = psVectorAlloc(0, PS_TYPE_U32);
+        }
+        psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+        return(tmpVector);
+    }
+
+    //
+    // Determine if first pixel is a peak
+    //
+    if ((vector->data.F32[0] > vector->data.F32[1]) &&
+            (vector->data.F32[0] > threshold)) {
+        count++;
+    }
+
+    //
+    // Determine if interior pixels are peaks
+    //
+    for (psU32 i = 1; i < n-1 ; i++) {
+        if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
+                (vector->data.F32[i] >= vector->data.F32[i+1]) &&
+                (vector->data.F32[i] > threshold)) {
+            count++;
+        }
+    }
+
+    //
+    // Determine if last pixel is a peak
+    //
+    if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
+            (vector->data.F32[n-1] > threshold)) {
+        count++;
+    }
+
+    //
+    // We know how many peaks exist, so we now allocate a psVector to store
+    // those peaks.
+    //
+    psVector *tmpVector = psVectorAlloc(count, PS_TYPE_U32);
+    count = 0;
+
+    //
+    // Determine if first pixel is a peak
+    //
+    if ((vector->data.F32[0] > vector->data.F32[1]) &&
+            (vector->data.F32[0] > threshold)) {
+        tmpVector->data.U32[count++] = 0;
+    }
+
+    //
+    // Determine if interior pixels are peaks
+    //
+    for (psU32 i = 1; i < (n-1) ; i++) {
+        if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
+                (vector->data.F32[i] >= vector->data.F32[i+1]) &&
+                (vector->data.F32[i] > threshold)) {
+            tmpVector->data.U32[count++] = i;
+        }
+    }
+
+    //
+    // Determine if last pixel is a peak
+    //
+    if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
+            (vector->data.F32[n-1] > threshold)) {
+        tmpVector->data.U32[count++] = n-1;
+    }
+
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(tmpVector);
+}
+
+
+/******************************************************************************
+pmPeaksInImage(image, threshold): Find all local peaks in the given psImage
+above the given threshold.  Returns a psArray containing location (x/y value)
+of all peaks.
+ 
+XXX: I'm not convinced the peak type definition in the SDRS is mutually
+exclusive.  Some peaks can have multiple types.  Edges for sure.  Also, a
+digonal line with the same value at each point will have a peak for every
+point on that line.
+ 
+XXX: This does not work if image has either a single row, or a single column.
+ 
+The peak is returned in the image parent coordinates
+
+*****************************************************************************/
+psArray *pmPeaksInImage(const psImage *image, psF32 threshold)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+    if ((image->numRows == 1) || (image->numCols == 1)) {
+        psError(PS_ERR_UNKNOWN, true, "Currently, input image must have at least 2 rows and 2 columns.");
+        psTrace("psModules.objects", 3, "---- %s(NULL) end ----\n", __func__);
+        return(NULL);
+    }
+    psVector *tmpRow = NULL;
+    psU32 col = 0;
+    psU32 row = 0;
+    psArray *list = NULL;
+
+    // Find peaks in row 0 only.
+    row = 0;
+    tmpRow = getRowVectorFromImage((psImage *) image, row);
+    psVector *row1 = pmPeaksInVector(tmpRow, threshold);
+    // pmPeaksInVector returns coords in the vector, not corrected for col0
+
+    for (psU32 i = 0 ; i < row1->n ; i++ ) {
+        col = row1->data.U32[i];
+        // is pixel (0,0) is a peak?
+        if (col == 0) {
+            if ( (image->data.F32[row][col] >  image->data.F32[row][col+1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row+1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+
+                if (image->data.F32[row][col] > threshold) {
+                    list = AddPeak(list, image, row, col, PM_PEAK_EDGE);
+                }
+            }
+        } else if (col < (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col+1]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row+1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = AddPeak(list, image, row, col, PM_PEAK_EDGE);
+                }
+            }
+
+        } else if (col == (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
+                    (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col-1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = AddPeak(list, image, row, col, PM_PEAK_EDGE);
+                }
+            }
+
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
+        }
+    }
+    psFree (tmpRow);
+    psFree (row1);
+
+    //
+    // Exit if this image has a single row.
+    //
+    if (image->numRows == 1) {
+        psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+        return(list);
+    }
+
+    //
+    // Find peaks in interior rows only.
+    //
+    for (row = 1 ; row < (image->numRows - 1) ; row++) {
+        tmpRow = getRowVectorFromImage((psImage *) image, row);
+        row1 = pmPeaksInVector(tmpRow, threshold);
+
+        // Step through all local peaks in this row.
+        for (psU32 i = 0 ; i < row1->n ; i++ ) {
+            pmPeakType myType = PM_PEAK_UNDEF;
+            col = row1->data.U32[i];
+
+            if (col == 0) {
+                // If col==0, then we can not read col-1 pixels
+                if ((image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+                    myType = PM_PEAK_EDGE;
+                    list = AddPeak(list, image, row, col, myType);
+                }
+            } else if (col < (image->numCols - 1)) {
+                // This is an interior pixel
+                if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                        (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                        (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+                    if (image->data.F32[row][col] > threshold) {
+                        if ((image->data.F32[row][col] > image->data.F32[row-1][col-1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
+                                (image->data.F32[row][col] > image->data.F32[row-1][col+1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row][col+1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row+1][col-1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
+                                (image->data.F32[row][col] > image->data.F32[row+1][col+1])) {
+                            myType = PM_PEAK_LONE;
+                        }
+
+                        if ((image->data.F32[row][col] == image->data.F32[row-1][col-1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row-1][col]) ||
+                                (image->data.F32[row][col] == image->data.F32[row-1][col+1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row][col-1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row][col+1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row+1][col-1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row+1][col]) ||
+                                (image->data.F32[row][col] == image->data.F32[row+1][col+1])) {
+                            myType = PM_PEAK_FLAT;
+                        }
+
+                        list = AddPeak(list, image, row, col, myType);
+
+                    }
+                }
+            } else if (col == (image->numCols - 1)) {
+                // If col==numCols - 1, then we can not read col+1 pixels
+                if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                        (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                        (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col])) {
+                    myType = PM_PEAK_EDGE;
+                    list = AddPeak(list, image, row, col, myType);
+                }
+            } else {
+                psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
+            }
+
+        }
+        psFree (tmpRow);
+        psFree (row1);
+    }
+
+    //
+    // Find peaks in the last row only.
+    //
+    row = image->numRows - 1;
+    tmpRow = getRowVectorFromImage((psImage *) image, row);
+    row1 = pmPeaksInVector(tmpRow, threshold);
+    for (psU32 i = 0 ; i < row1->n ; i++ ) {
+        col = row1->data.U32[i];
+        if (col == 0) {
+            if ( (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col+1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = AddPeak(list, image, row, col, PM_PEAK_EDGE);
+                }
+            }
+        } else if (col < (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col-1]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row][col+1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = AddPeak(list, image, row, col, PM_PEAK_EDGE);
+                }
+            }
+
+        } else if (col == (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col-1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = AddPeak(list, image, row, col, PM_PEAK_EDGE);
+                }
+            }
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
+        }
+    }
+    psFree (tmpRow);
+    psFree (row1);
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(list);
+}
+
+// return a new array of peaks which are in the valid region and below threshold
+// XXX this function is unused and probably could be dropped
+psArray *pmPeaksSubset(
+    psArray *peaks,
+    psF32 maxValue,
+    const psRegion valid)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(peaks, NULL);
+
+    psArray *output = psArrayAllocEmpty (200);
+
+    psTrace ("psModules.objects", 3, "list size is %ld\n", peaks->n);
+
+    for (int i = 0; i < peaks->n; i++) {
+        pmPeak *tmpPeak = (pmPeak *) peaks->data[i];
+        if (tmpPeak->value > maxValue)
+            continue;
+        if (isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))
+            continue;
+        psArrayAdd (output, 200, tmpPeak);
+    }
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(output);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPeaks.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPeaks.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmPeaks.h	(revision 22322)
@@ -0,0 +1,145 @@
+/* @file  pmPeaks.h
+ *
+ * The process of finding, measuring, and classifying astronomical sources on
+ * images is one of the critical tasks of the IPP or any astronomical software
+ * system. This file will define structures and functions related to the task
+ * of source detection and measurement. The elements defined in this section
+ * are generally low-level components which can be connected together to
+ * construct a complete object measurement suite.
+ *
+ * @author GLG, MHPCC
+ *
+ * @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-08 19:41:56 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_PEAKS_H
+# define PM_PEAKS_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/** pmPeakType
+ *
+ *  A peak pixel may have several features which may be determined when the
+ *  peak is found or measured. These are specified by the pmPeakType enum.
+ *  PM_PEAK_LONE represents a single pixel which is higher than its 8 immediate
+ *  neighbors.  The PM_PEAK_EDGE represents a peak pixel which touching the image
+ *  edge. The PM_PEAK_FLAT represents a peak pixel which has more than a specific
+ *  number of neighbors at the same value, within some tolarence:
+ *
+ */
+typedef enum {
+    PM_PEAK_LONE,                       ///< Isolated peak.
+    PM_PEAK_EDGE,                       ///< Peak on edge.
+    PM_PEAK_FLAT,                       ///< Peak has equal-value neighbors.
+    PM_PEAK_UNDEF                       ///< Undefined.
+} pmPeakType;
+
+
+/** pmPeak data structure
+ *
+ *  A source has the capacity for several types of measurements. The
+ *  simplest measurement of a source is the location and flux of the peak pixel
+ *  associated with the source:
+ *
+ */
+typedef struct
+{
+    const int id;   ///< Unique ID for object
+    int x;                              ///< X-coordinate of peak pixel.
+    int y;                              ///< Y-coordinate of peak pixel.
+    float xf;                           ///< bicube fit to peak coord (x)
+    float yf;                           ///< bicube fit to peak coord (y)
+    float dx;                           ///< bicube fit error on peak coord (x)
+    float dy;                           ///< bicube fit error on peak coord (y)
+    float value;                        ///< level in detection image
+    float flux;                         ///< level in unsmoothed sci image
+    float SN;                           ///< S/N implied by detection level
+    bool assigned;                      ///< is peak assigned to a source?
+    pmPeakType type;			///< Description of peak.
+    struct pmFootprint *footprint;	///< reference to containing footprint
+}
+pmPeak;
+
+
+/** pmPeakAlloc()
+ *
+ *  @return pmPeak*    newly allocated pmPeak with all internal pointers set to NULL
+ */
+pmPeak *pmPeakAlloc(
+    int x,    ///< Row-coordinate in image space
+    int y,    ///< Col-coordinate in image space
+    float counts,   ///< The value of the peak pixel
+    pmPeakType type   ///< The type of peak pixel
+);
+
+bool psMemCheckPeak(psPtr ptr);
+
+/** pmPeaksInVector()
+ *
+ * Find all local peaks in the given vector above the given threshold. A peak
+ * is defined as any element with a value greater than its two neighbors and with
+ * a value above the threshold. Two types of special cases must be addressed.
+ * Equal value elements: If an element has the same value as the following
+ * element, it is not considered a peak. If an element has the same value as the
+ * preceding element (but not the following), then it is considered a peak. Note
+ * that this rule (arbitrarily) identifies flat regions by their trailing edge.
+ * Edge cases: At start of the vector, the element must be higher than its
+ * neighbor. At the end of the vector, the element must be higher or equal to its
+ * neighbor. These two rules again places the peak associated with a flat region
+ * which touches the image edge at the image edge. The result of this function is
+ * a vector containing the coordinates (element number) of the detected peaks
+ * (type psU32).
+ *
+ */
+psVector *pmPeaksInVector(
+    const psVector *vector,  ///< The input vector (float)
+    float threshold   ///< Threshold above which to find a peak
+);
+
+
+/** pmPeaksInImage()
+ *
+ * Find all local peaks in the given image above the given threshold. This
+ * function should find all row peaks using pmFindVectorPeaks, then test each row
+ * peak and exclude peaks which are not local peaks. A peak is a local peak if it
+ * has a higher value than all 8 neighbors. If the peak has the same value as its
+ * +y neighbor or +x neighbor, it is NOT a local peak. If any other neighbors
+ * have an equal value, the peak is considered a valid peak. Note two points:
+ * first, the +x neighbor condition is already enforced by pmFindVectorPeaks.
+ * Second, these rules have the effect of making flat-topped regions have single
+ * peaks at the (+x,+y) corner. When selecting the peaks, their type must also be
+ * set. The result of this function is an array of pmPeak entries.
+ *
+ */
+psArray *pmPeaksInImage(
+    const psImage *image,  ///< The input image where peaks will be found (float)
+    float threshold   ///< Threshold above which to find a peak
+);
+
+
+/** pmPeaksSubset()
+ *
+ * Create a new peaks array, removing certain types of peaks from the input
+ * array of peaks based on the given criteria. Peaks should be eliminated if they
+ * have a peak value above the given maximum value limit or if the fall outside
+ * the valid region.  The result of the function is a new array with a reduced
+ * number of peaks.
+ *
+ */
+psArray *pmPeaksSubset(
+    psArray *peaks,                     ///< Add comment.
+    float maxvalue,                     ///< Add comment.
+    const psRegion valid                ///< Add comment.
+);
+
+int pmPeaksCompareAscend (const void **a, const void **b);
+int pmPeaksCompareDescend (const void **a, const void **b);
+
+int pmPeakSortBySN (const void **a, const void **b);
+int pmPeakSortByY (const void **a, const void **b);
+
+/// @}
+# endif /* PM_PEAKS_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmResiduals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmResiduals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmResiduals.c	(revision 22322)
@@ -0,0 +1,59 @@
+/** @file pmResiduals.c
+ *
+ * Functions to manipulate the residual tables (data - model).
+ *
+ * @author EAM, IfA
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-10 01:09:20 $
+ * Copyright 2004 IfA, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+#include "pmResiduals.h"
+
+static void pmResidualsFree (pmResiduals *resid) {
+
+    if (resid == NULL) return;
+
+    psFree (resid->Ro);
+    psFree (resid->Rx);
+    psFree (resid->Ry);
+    psFree (resid->weight);
+    psFree (resid->mask);
+    return;
+}
+
+pmResiduals *pmResidualsAlloc (int xSize, int ySize, int xBin, int yBin) {
+
+    pmResiduals *resid = (pmResiduals *) psAlloc(sizeof(pmResiduals));
+    psMemSetDeallocator(resid, (psFreeFunc) pmResidualsFree);
+
+    int nX = xSize * xBin;
+    int nY = ySize * yBin;
+
+    nX = (nX % 2) ? nX : nX + 1;
+    nY = (nY % 2) ? nY : nY + 1;
+
+    resid->Ro  = psImageAlloc (nX, nY, PS_TYPE_F32);
+    resid->Rx  = psImageAlloc (nX, nY, PS_TYPE_F32);
+    resid->Ry  = psImageAlloc (nX, nY, PS_TYPE_F32);
+    resid->weight = psImageAlloc (nX, nY, PS_TYPE_F32);
+    resid->mask   = psImageAlloc (nX, nY, PS_TYPE_U8);
+
+    resid->xBin = xBin;
+    resid->yBin = yBin;
+    resid->xCenter = 0.5*(nX - 1); 
+    resid->yCenter = 0.5*(nY - 1);
+    return resid;
+}
+
+bool psMemCheckResiduals(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmResidualsFree);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmResiduals.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmResiduals.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmResiduals.h	(revision 22322)
@@ -0,0 +1,34 @@
+/** @file pmResiduals.h
+ *
+ * Functions to manipulate the residual tables (data - model).
+ *
+ * @author EAM, IfA
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-10 01:09:20 $
+ * Copyright 2004 IfA, University of Hawaii
+ */
+
+# ifndef PM_RESIDUALS_H
+# define PM_RESIDUALS_H
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/** residual tables for sources 
+ */
+typedef struct {
+    psImage *Ro;
+    psImage *Rx;
+    psImage *Ry;
+    psImage *weight;
+    psImage *mask;
+    int xBin;
+    int yBin;
+    int xCenter;
+    int yCenter;
+} pmResiduals;
+
+pmResiduals *pmResidualsAlloc (int xSize, int ySize, int xBin, int yBin);
+bool psMemCheckResiduals(psPtr ptr);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSource.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSource.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSource.c	(revision 22322)
@@ -0,0 +1,1047 @@
+/** @file  pmSource.c
+ *
+ *  Functions to define and manipulate sources on images
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA: significant modifications.
+ *
+ *  @version $Revision: 1.55 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-07-15 20:25:00 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+
+static void sourceFree(pmSource *tmp)
+{
+    if (!tmp)
+        return;
+
+    psTrace("psModules.objects", 5, "---- begin ----\n");
+    psFree(tmp->peak);
+    psFree(tmp->pixels);
+    psFree(tmp->weight);
+    psFree(tmp->maskObj);
+    psFree(tmp->maskView);
+    psFree(tmp->modelFlux);
+    psFree(tmp->psfFlux);
+    psFree(tmp->moments);
+    psFree(tmp->modelPSF);
+    psFree(tmp->modelEXT);
+    psFree(tmp->modelFits);
+    psFree(tmp->extpars);
+    psFree(tmp->blends);
+    psTrace("psModules.objects", 5, "---- end ----\n");
+}
+
+// free only the pixel data associated with this source
+void pmSourceFreePixels(pmSource *source)
+{
+
+    if (!source)
+        return;
+
+    psFree (source->pixels);
+    psFree (source->weight);
+    psFree (source->maskObj);
+    psFree (source->maskView);
+    psFree (source->modelFlux);
+    psFree (source->psfFlux);
+
+    source->pixels = NULL;
+    source->weight = NULL;
+    source->maskObj = NULL;
+    source->maskView = NULL;
+    source->modelFlux = NULL;
+    source->psfFlux = NULL;
+    return;
+}
+
+bool psMemCheckSource(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) sourceFree);
+}
+
+/******************************************************************************
+pmSourceAlloc(): Allocate the pmSource structure and initialize its members
+to NULL.
+*****************************************************************************/
+
+pmSource *pmSourceAlloc()
+{
+    psTrace("psModules.objects", 5, "---- begin ----\n");
+    static int id = 1;
+    pmSource *source = (pmSource *) psAlloc(sizeof(pmSource));
+    *(int *)&source->id = id++;
+    source->seq = -1;
+    source->peak = NULL;
+    source->pixels = NULL;
+    source->weight = NULL;
+    source->maskObj = NULL;
+    source->maskView = NULL;
+    source->modelFlux = NULL;
+    source->psfFlux = NULL;
+    source->moments = NULL;
+    source->blends = NULL;
+    source->modelPSF = NULL;
+    source->modelEXT = NULL;
+    source->modelFits = NULL;
+    source->type = PM_SOURCE_TYPE_UNKNOWN;
+    source->mode = PM_SOURCE_MODE_DEFAULT;
+    source->extpars = NULL;
+    source->region = psRegionSet(NAN, NAN, NAN, NAN);
+    psMemSetDeallocator(source, (psFreeFunc) sourceFree);
+
+    // default values are NAN
+    source->psfMag = NAN;
+    source->extMag = NAN;
+    source->errMag = NAN;
+    source->apMag  = NAN;
+    source->sky    = NAN;
+    source->skyErr = NAN;
+    source->pixWeight = NAN;
+
+    source->psfChisq = NAN;
+    source->crNsigma = NAN;
+    source->extNsigma = NAN;
+
+    psTrace("psModules.objects", 5, "---- end ----\n");
+    return(source);
+}
+
+/******************************************************************************
+pmSourceCopy(): copy the pmSource structure and contents
+XXX : are we OK with incrementing the ID?
+*****************************************************************************/
+pmSource *pmSourceCopy(pmSource *in)
+{
+    if (in == NULL) {
+        return(NULL);
+    }
+    // this copy is used to allow multiple fit attempts on the
+    // same object.  the pixels, weight, and mask arrays all point to
+    // the same original subarrays.  the peak and moments point at
+    // the original values.
+    pmSource *source = pmSourceAlloc ();
+
+    // this is actually the same peak as the original, is this the correct way to handle this?
+    if (in->peak != NULL) {
+        source->peak = pmPeakAlloc (in->peak->x, in->peak->y, in->peak->value, in->peak->type);
+        source->peak->xf = in->peak->xf;
+        source->peak->yf = in->peak->yf;
+        source->peak->flux = in->peak->flux;
+        source->peak->SN = in->peak->SN;
+    }
+
+    // copy the values in the moments structure
+    if (in->moments != NULL) {
+        source->moments  =  pmMomentsAlloc();
+        *source->moments = *in->moments;
+    }
+
+    // These images are all views to the parent.
+    // We want a new view, but pointing at the same pixels.
+    source->pixels   = psImageCopyView(NULL, in->pixels);
+    source->weight   = psImageCopyView(NULL, in->weight);
+    source->maskView = in->maskView ? psImageCopyView(NULL, in->maskView) : NULL;
+
+    // the maskObj is a unique mask array; create a new mask image
+    source->maskObj = in->maskObj ? psImageCopy (NULL, in->maskObj, PS_TYPE_MASK) : NULL;
+
+    source->type = in->type;
+    source->mode = in->mode;
+
+    return(source);
+}
+
+// x,y are defined in the parent image coords of readout->image
+bool pmSourceDefinePixels(pmSource *mySource,
+                          const pmReadout *readout,
+                          psF32 x,
+                          psF32 y,
+                          psF32 Radius)
+{
+    PS_ASSERT_PTR_NON_NULL(mySource, false);
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(readout->image, false);
+    PS_ASSERT_INT_POSITIVE(Radius, false);
+
+    psRegion srcRegion;
+
+    // Grab a subimage of the original image of size (2 * outerRadius).
+    srcRegion = psRegionForSquare (x, y, Radius);
+    srcRegion = psRegionForImage (readout->image, srcRegion);
+
+    // these images are subset images of the equivalent parents
+    mySource->pixels = psImageSubset(readout->image, srcRegion);
+    if (readout->weight) {
+        mySource->weight = psImageSubset(readout->weight, srcRegion);
+    }
+    if (readout->mask) {
+        mySource->maskView = psImageSubset(readout->mask,  srcRegion);
+        // the object mask is a copy, and used to define the source pixels
+        mySource->maskObj = psImageCopy(NULL, mySource->maskView, PS_TYPE_MASK);
+    }
+    mySource->region   = srcRegion;
+
+    return true;
+}
+
+bool pmSourceRedefinePixels(pmSource *mySource,
+                            const pmReadout *readout,
+                            psF32 x,
+                            psF32 y,
+                            psF32 Radius)
+{
+    PS_ASSERT_PTR_NON_NULL(mySource, false);
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(readout->image, false);
+    PS_ASSERT_INT_POSITIVE(Radius, false);
+
+    bool extend;
+    psRegion newRegion;
+
+    // check to see if new region is completely contained within old region
+    newRegion = psRegionForSquare (x, y, Radius);
+    newRegion = psRegionForImage (readout->image, newRegion);
+
+    extend = false;
+    extend |= (int)(newRegion.x0) < (int)(mySource->region.x0);
+    extend |= (int)(newRegion.x1) > (int)(mySource->region.x1);
+    extend |= (int)(newRegion.y0) < (int)(mySource->region.y0);
+    extend |= (int)(newRegion.y1) > (int)(mySource->region.y1);
+
+    extend |= (mySource->pixels == NULL);
+    extend |= (mySource->weight == NULL);
+    extend |= (mySource->maskObj == NULL);
+    extend |= (mySource->maskView == NULL);
+
+    // extend = true;
+    if (extend) {
+        // re-create the subimage
+        psFree (mySource->pixels);
+        psFree (mySource->weight);
+        psFree (mySource->maskView);
+
+        mySource->pixels   = psImageSubset(readout->image,  newRegion);
+        mySource->weight   = psImageSubset(readout->weight, newRegion);
+        mySource->maskView = psImageSubset(readout->mask,   newRegion);
+        mySource->region   = newRegion;
+
+        // re-copy the main mask pixels.  NOTE: the user will need to reset the object mask
+        // pixels (eg, with psImageKeepCircle)
+        mySource->maskObj = psImageCopy (mySource->maskObj, mySource->maskView, PS_TYPE_MASK);
+
+        // drop the old modelFlux pixels and force the user to re-create
+        psFree (mySource->modelFlux);
+        mySource->modelFlux = NULL;
+
+        // drop the old psfFlux pixels and force the user to re-create
+        psFree (mySource->psfFlux);
+        mySource->psfFlux = NULL;
+    }
+    return extend;
+}
+
+/******************************************************************************
+    pmSourcePSFClump(source, recipe): Find the likely PSF clump in the
+    sigma-x, sigma-y plane. return 0,0 clump in case of error.
+*****************************************************************************/
+
+// XXX EAM include a S/N cutoff in selecting the sources?
+// XXX this function should probably accept the values, not a recipe. wrap with a
+// psphot-specific function which applies the recipe values
+pmPSFClump pmSourcePSFClump(psArray *sources, psMetadata *recipe)
+{
+    psTrace("psModules.objects", 5, "---- begin ----\n");
+
+    # define NPIX 10
+    # define SCALE 0.1
+
+    psArray *peaks  = NULL;
+    pmPSFClump errorClump = {-1.0, -1.0, 0.0, 0.0};
+    pmPSFClump emptyClump = {+0.0, +0.0, 0.0, 0.0};
+    pmPSFClump psfClump;
+
+    PS_ASSERT_PTR_NON_NULL(sources, errorClump);
+    PS_ASSERT_PTR_NON_NULL(recipe, errorClump);
+
+    bool status = true;
+    float PSF_CLUMP_SN_LIM = psMetadataLookupF32 (&status, recipe, "PSF_CLUMP_SN_LIM");
+    if (!status) {
+        PSF_CLUMP_SN_LIM = 0;
+    }
+
+    // find the sigmaX, sigmaY clump
+    {
+        psStats *stats  = NULL;
+        psImage *splane = NULL;
+        int binX, binY;
+        bool status;
+
+        psF32 SX_MAX = psMetadataLookupF32 (&status, recipe, "MOMENTS_SX_MAX");
+        if (!status)
+            SX_MAX = 10.0;
+        psF32 SY_MAX = psMetadataLookupF32 (&status, recipe, "MOMENTS_SY_MAX");
+        if (!status)
+            SY_MAX = 10.0;
+        psF32 AR_MAX = psMetadataLookupF32 (&status, recipe, "MOMENTS_AR_MAX");
+        if (!status)
+            AR_MAX =  3.0;
+        psF32 AR_MIN = 1.0 / AR_MAX;
+
+        // construct a sigma-plane image
+        splane = psImageAlloc (SX_MAX/SCALE, SY_MAX/SCALE, PS_TYPE_F32);
+        psImageInit(splane, 0);  // psImageAlloc doesn't zero the data
+
+        // place the sources in the sigma-plane image (ignore 0,0 values?)
+        int nValid = 0;
+        for (psS32 i = 0 ; i < sources->n ; i++)
+        {
+            pmSource *tmpSrc = (pmSource *) sources->data[i];
+            if (tmpSrc == NULL)
+                continue;
+            if (tmpSrc->moments == NULL)
+                continue;
+            if (tmpSrc->moments->SN < PSF_CLUMP_SN_LIM)
+                continue;
+
+            // Sx,Sy are limited at 0.  a peak at 0,0 is artificial
+            if (fabs(tmpSrc->moments->Sx) < 0.05)
+                continue;
+            if (fabs(tmpSrc->moments->Sy) < 0.05)
+                continue;
+            if ((tmpSrc->moments->Sx / tmpSrc->moments->Sy) > AR_MAX)
+                continue;
+            if ((tmpSrc->moments->Sx / tmpSrc->moments->Sy) < AR_MIN)
+                continue;
+            if (tmpSrc->mode & PM_SOURCE_MODE_BLEND)
+                continue;
+
+            // for the moment, force splane dimensions to be 10x10 image pix
+            binX = tmpSrc->moments->Sx/SCALE;
+            if (binX < 0)
+                continue;
+            if (binX >= splane->numCols)
+                continue;
+
+            binY = tmpSrc->moments->Sy/SCALE;
+            if (binY < 0)
+                continue;
+            if (binY >= splane->numRows)
+                continue;
+
+            splane->data.F32[binY][binX] += 1.0;
+            nValid ++;
+        }
+
+        // find the peak in this image
+        stats = psStatsAlloc (PS_STAT_MAX);
+        if (!psImageStats (stats, splane, NULL, 0)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to get image statistics.\n");
+            psFree(stats);
+            psFree(splane);
+            return emptyClump;
+        }
+        peaks = pmPeaksInImage (splane, stats[0].max / 2);
+        psTrace ("psModules.objects", 2, "clump threshold is %f\n", stats[0].max/2);
+
+        const bool keep_psf_clump = psMetadataLookupBool(NULL, recipe, "KEEP_PSF_CLUMP");
+        if (keep_psf_clump)
+        {
+            psMetadataAdd(recipe, PS_LIST_TAIL,
+                          "PSF_CLUMP", PS_DATA_IMAGE, "Image of PSF coefficients", splane);
+        }
+        psFree (splane);
+        psFree (stats);
+
+        // if we failed to find a valid peak, return the empty clump (failure signal)
+        if (peaks == NULL)
+        {
+            psLogMsg ("psphot", 3, "failed to find a peak in the PSF clump image\n");
+            if (nValid == 0) {
+                psLogMsg ("psphot", 3, "no valid sources kept for PSF search\n");
+            } else {
+                psLogMsg ("psphot", 3, "no significant peak\n");
+            }
+            return (emptyClump);
+        }
+    }
+
+    // measure statistics on Sx, Sy if Sx, Sy within range of clump
+    {
+        pmPeak *clump;
+        psF32 minSx, maxSx;
+        psF32 minSy, maxSy;
+        psVector *tmpSx = NULL;
+        psVector *tmpSy = NULL;
+        psStats *stats  = NULL;
+
+        // select the single highest peak
+        psArraySort (peaks, pmPeaksCompareDescend);
+        clump = peaks->data[0];
+        psTrace ("psModules.objects", 2, "clump is at %d, %d (%f)\n", clump->x, clump->y, clump->value);
+
+        // define section window for clump
+        minSx = clump->x * SCALE - 0.2;
+        maxSx = clump->x * SCALE + 0.2;
+        minSy = clump->y * SCALE - 0.2;
+        maxSy = clump->y * SCALE + 0.2;
+
+        tmpSx = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+        tmpSy = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+
+        // create vectors with Sx, Sy values in window
+        // clip sources based on S/N
+        for (psS32 i = 0 ; i < sources->n ; i++)
+        {
+            pmSource *tmpSrc = (pmSource *) sources->data[i];
+
+            if (tmpSrc == NULL)
+                continue;
+            if (tmpSrc->moments == NULL)
+                continue;
+            if (tmpSrc->moments->SN < PSF_CLUMP_SN_LIM)
+                continue;
+
+            if (tmpSrc->moments->Sx < minSx)
+                continue;
+            if (tmpSrc->moments->Sx > maxSx)
+                continue;
+            if (tmpSrc->moments->Sy < minSy)
+                continue;
+            if (tmpSrc->moments->Sy > maxSy)
+                continue;
+            tmpSx->data.F32[tmpSx->n] = tmpSrc->moments->Sx;
+            tmpSy->data.F32[tmpSy->n] = tmpSrc->moments->Sy;
+            tmpSx->n++;
+            tmpSy->n++;
+            if (tmpSx->n == tmpSx->nalloc) {
+                psVectorRealloc (tmpSx, tmpSx->nalloc + 100);
+                psVectorRealloc (tmpSy, tmpSy->nalloc + 100);
+            }
+        }
+
+        // measures stats of Sx, Sy
+        stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+
+        psVectorStats (stats, tmpSx, NULL, NULL, 0);
+        psfClump.X  = stats->clippedMean;
+        psfClump.dX = stats->clippedStdev;
+
+        psVectorStats (stats, tmpSy, NULL, NULL, 0);
+        psfClump.Y  = stats->clippedMean;
+        psfClump.dY = stats->clippedStdev;
+
+        psTrace ("psModules.objects", 2, "clump  X,  Y: %f, %f\n", psfClump.X, psfClump.Y);
+        psTrace ("psModules.objects", 2, "clump DX, DY: %f, %f\n", psfClump.dX, psfClump.dY);
+
+        psFree (stats);
+        psFree (peaks);
+        psFree (tmpSx);
+        psFree (tmpSy);
+    }
+
+    psTrace("psModules.objects", 5, "---- end ----\n");
+    return (psfClump);
+}
+
+/******************************************************************************
+    pmSourceRoughClass(source, recipe): make a guess at the source
+    classification.
+    XXX: How can this function ever return FALSE?
+*****************************************************************************/
+
+bool pmSourceRoughClass(psArray *sources, psMetadata *recipe, pmPSFClump clump, psMaskType maskSat)
+{
+    psTrace("psModules.objects", 5, "---- begin ----");
+
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(recipe, false);
+
+    int Nsat     = 0;
+    int Next     = 0;
+    int Nstar    = 0;
+    int Npsf     = 0;
+    int Ncr      = 0;
+    int Nsatstar = 0;
+    psRegion inner;
+
+    // report stats on S/N values for star-like objects
+    psVector *starsn_peaks = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+    psVector *starsn_moments = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+
+    // get basic parameters, or set defaults
+    bool status;
+    float PSF_SN_LIM = psMetadataLookupF32 (&status, recipe, "PSF_SN_LIM");
+    if (!status)
+        PSF_SN_LIM = 20.0;
+    float PSF_CLUMP_NSIGMA = psMetadataLookupF32 (&status, recipe, "PSF_CLUMP_NSIGMA");
+    if (!status)
+        PSF_CLUMP_NSIGMA = 1.5;
+    float INNER_RADIUS = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
+
+    // XXX allow clump size to be scaled relative to sigmas?
+    // make rough IDs based on clumpX,Y,DX,DY
+    for (psS32 i = 0 ; i < sources->n ; i++) {
+
+        pmSource *source = (pmSource *) sources->data[i];
+
+        source->peak->type = 0;
+
+        // we are basically classifying by moments; use the default if not found
+        if (!source->moments) {
+            source->type = PM_SOURCE_TYPE_STAR;
+            Nstar++;
+            continue;
+        }
+
+        psF32 sigX = source->moments->Sx;
+        psF32 sigY = source->moments->Sy;
+
+        // XXX EAM : can we use the value of SATURATE if mask is NULL?
+        inner = psRegionForSquare (source->peak->x, source->peak->y, 2);
+        inner = psRegionForImage (source->maskView, inner);
+        int Nsatpix = psImageCountPixelMask (source->maskView, inner, maskSat);
+
+        // saturated star (size consistent with PSF or larger)
+        // Nsigma should be user-configured parameter
+        bool big = (sigX > (clump.X - clump.dX)) && (sigY > (clump.Y - clump.dY));
+        big = true;
+        if ((Nsatpix > 1) && big) {
+            source->type = PM_SOURCE_TYPE_STAR;
+            source->mode |= PM_SOURCE_MODE_SATSTAR;
+            // recalculate moments here with larger box?
+            pmSourceMoments (source, INNER_RADIUS);
+            Nsatstar ++;
+            continue;
+        }
+
+        // saturated object (not a star, eg bleed trails, hot pixels)
+        if (Nsatpix > 1) {
+            source->type = PM_SOURCE_TYPE_SATURATED;
+            source->mode |= PM_SOURCE_MODE_SATURATED;
+            Nsat ++;
+            continue;
+        }
+
+        // likely defect (too small to be stellar) (push out to 3 sigma)
+        // low S/N objects which are small are probably stellar
+        // only set candidate defects if
+        // XXX these limits are quite arbitrary
+        if ((sigX < 0.05) || (sigY < 0.05)) {
+            source->type = PM_SOURCE_TYPE_DEFECT;
+            source->mode |= PM_SOURCE_MODE_DEFECT;
+            Ncr ++;
+            continue;
+        }
+
+        // likely unsaturated extended source (too large to be stellar)
+        if ((sigX > (clump.X + 3*clump.dX)) || (sigY > (clump.Y + 3*clump.dY))) {
+            source->type = PM_SOURCE_TYPE_EXTENDED;
+            Next ++;
+            continue;
+        }
+
+        // the rest are probable stellar objects
+        starsn_moments->data.F32[starsn_moments->n] = source->moments->SN;
+        starsn_moments->n ++;
+        starsn_peaks->data.F32[starsn_peaks->n] = source->peak->SN;
+        starsn_peaks->n ++;
+        Nstar ++;
+
+        // PSF star (within 1.5 sigma of clump center, S/N > limit)
+        psF32 radius = hypot ((sigX-clump.X)/clump.dX, (sigY-clump.Y)/clump.dY);
+        if ((source->moments->SN > PSF_SN_LIM) && (radius < PSF_CLUMP_NSIGMA)) {
+            source->type = PM_SOURCE_TYPE_STAR;
+            source->mode |= PM_SOURCE_MODE_PSFSTAR;
+            Npsf ++;
+            continue;
+        }
+
+        // random type of star
+        source->type = PM_SOURCE_TYPE_STAR;
+    }
+
+    psStats *stats = NULL;
+    stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
+    if (!psVectorStats (stats, starsn_moments, NULL, NULL, 0)) {
+        // Don't care about this error
+        psErrorClear();
+    }
+    psLogMsg ("pmObjects", 3, "SN range (moments): %f - %f\n", stats[0].min, stats[0].max);
+    psFree (starsn_moments);
+    psFree (stats);
+
+    stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
+    if (!psVectorStats (stats, starsn_peaks, NULL, NULL, 0)) {
+        // Don't care about this error
+        psErrorClear();
+    }
+    psLogMsg ("pmObjects", 3, "SN range (peaks)  : %f - %f\n", stats[0].min, stats[0].max);
+    psFree (starsn_peaks);
+    psFree (stats);
+
+    psTrace ("psModules.objects", 2, "Nstar:    %3d\n", Nstar);
+    psTrace ("psModules.objects", 2, "Npsf:     %3d\n", Npsf);
+    psTrace ("psModules.objects", 2, "Next:     %3d\n", Next);
+    psTrace ("psModules.objects", 2, "Nsatstar: %3d\n", Nsatstar);
+    psTrace ("psModules.objects", 2, "Nsat:     %3d\n", Nsat);
+    psTrace ("psModules.objects", 2, "Ncr:      %3d\n", Ncr);
+
+    psTrace("psModules.objects", 5, "---- end ----\n");
+    return true;
+}
+
+/******************************************************************************
+pmSourceMoments(source, radius): this function takes a subImage defined in the
+pmSource data structure, along with the peak location, and determines the
+various moments associated with that peak.
+
+Requires the following to have been created:
+    pmSource
+    pmSource->peak
+    pmSource->pixels
+    pmSource->weight
+    pmSource->mask
+
+XXX: The peak calculations are done in image coords, not subImage coords.
+
+XXX EAM : this version clips input pixels on S/N
+XXX EAM : this version returns false for several reasons
+*****************************************************************************/
+# define VALID_RADIUS(X,Y,RAD2) (((RAD2) >= (PS_SQR(X) + PS_SQR(Y))) ? 1 : 0)
+
+bool pmSourceMoments(pmSource *source,
+                     psF32 radius)
+{
+    psTrace("psModules.objects", 5, "---- begin ----\n");
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
+
+    //
+    // XXX: Verify the setting for sky if source->moments == NULL.
+    //
+    psF32 sky = 0.0;
+    if (source->moments == NULL) {
+        source->moments = pmMomentsAlloc();
+    } else {
+        sky = source->moments->Sky;
+    }
+
+    //
+    // Sum = SUM (z - sky)
+    // X1  = SUM (x - xc)*(z - sky)
+    // X2  = SUM (x - xc)^2 * (z - sky)
+    // XY  = SUM (x - xc)*(y - yc)*(z - sky)
+    //
+    psF32 peakPixel = -PS_MAX_F32;
+    psS32 numPixels = 0;
+    psF32 Sum = 0.0;
+    psF32 Var = 0.0;
+    psF32 X1 = 0.0;
+    psF32 Y1 = 0.0;
+    psF32 X2 = 0.0;
+    psF32 Y2 = 0.0;
+    psF32 XY = 0.0;
+    psF32 x  = 0;
+    psF32 y  = 0;
+    psF32 R2 = PS_SQR(radius);
+
+    psF32 xPeak = source->peak->x;
+    psF32 yPeak = source->peak->y;
+    psF32 xOff = source->pixels->col0 - source->peak->x;
+    psF32 yOff = source->pixels->row0 - source->peak->y;
+
+    // XXX why do I get different results for these two methods of finding Sx?
+    // XXX Sx, Sy would be better measured if we clip pixels close to sky
+    // XXX Sx, Sy can still be imaginary, so we probably need to keep Sx^2?
+    // We loop through all pixels in this subimage (source->pixels), and for each
+    // pixel that is not masked, AND within the radius of the peak pixel, we
+    // proceed with the moments calculation.  need to do two loops for a
+    // numerically stable result.  first loop: get the sums.
+    // XXX EAM : mask == 0 is valid
+
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+
+        psF32 *vPix = source->pixels->data.F32[row];
+        psF32 *vWgt = source->weight->data.F32[row];
+        psU8  *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.U8[row];
+
+        for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
+            if (vMsk) {
+                if (*vMsk) {
+                    vMsk++;
+                    psTrace("psModules.objects", 10, "Ignoring pixel %d,%d due to mask: %d\n",
+                            col, row, (int)*vMsk);
+                    continue;
+                }
+                vMsk++;
+            }
+	    if (isnan(*vPix)) continue;
+
+            psF32 xDiff = col + xOff;
+            psF32 yDiff = row + yOff;
+
+            // radius is just a function of (xDiff, yDiff)
+            if (!VALID_RADIUS(xDiff, yDiff, R2)) {
+#if 1
+                psTrace("psModules.objects", 10, "Ignoring pixel %d,%d due to position: %f %f\n",
+                        col, row, xDiff, yDiff);
+#endif
+                continue;
+            }
+
+            psF32 pDiff = *vPix - sky;
+            psF32 wDiff = *vWgt;
+
+            // XXX EAM : check for valid S/N in pixel
+            // XXX EAM : should this limit be user-defined?
+            if (PS_SQR(pDiff) < wDiff) {
+#if 1
+                psTrace("psModules.objects", 10, "Ignoring pixel %d,%d due to insignificance: %f, %f\n",
+                        col, row, pDiff, wDiff);
+#endif
+                continue;
+            }
+
+            Var += wDiff;
+            Sum += pDiff;
+
+            psF32 xWght = xDiff * pDiff;
+            psF32 yWght = yDiff * pDiff;
+
+            X1  += xWght;
+            Y1  += yWght;
+
+            XY  += xDiff * yWght;
+            X2  += xDiff * xWght;
+            Y2  += yDiff * yWght;
+
+            peakPixel = PS_MAX (*vPix, peakPixel);
+            numPixels++;
+        }
+    }
+
+    // if we have less than (1/4) of the possible pixels, force a retry
+    // XXX EAM - the limit is a bit arbitrary.  make it user defined?
+    if ((numPixels < 0.75*R2) || (Sum <= 0)) {
+        psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n",
+                 numPixels, (int)(0.75*R2), Sum);
+        psTrace("psModules.objects", 5, "---- end (false) ----\n");
+        return (false);
+    }
+
+    psTrace ("psModules.objects", 4, "sky: %f  Sum: %f  X1: %f  Y1: %f  X2: %f  Y2: %f  XY: %f  Npix: %d\n",
+             sky, Sum, X1, Y1, X2, Y2, XY, numPixels);
+
+    //
+    // first moment X  = X1/Sum + xc
+    // second moment X = sqrt (X2/Sum - (X1/Sum)^2)
+    // Sxy             = XY / Sum
+    //
+    x = X1/Sum;
+    y = Y1/Sum;
+    if ((fabs(x) > radius) || (fabs(y) > radius)) {
+        psTrace ("psModules.objects", 3, "large centroid swing; invalid peak %d, %d\n",
+                 source->peak->x, source->peak->y);
+        psTrace("psModules.objects", 5, "---- end(false)  ----\n");
+        return (false);
+    }
+
+    source->moments->x = x + xPeak;
+    source->moments->y = y + yPeak;
+
+    // XXX EAM : Sxy needs to have x*y subtracted
+    source->moments->Sxy = XY/Sum - x*y;
+    source->moments->Sum = Sum;
+    source->moments->SN  = Sum / sqrt(Var);
+    source->moments->Peak = peakPixel;
+    source->moments->nPixels = numPixels;
+
+    // XXX EAM : these values can be negative, so we need to limit the range
+    // XXX EAM : make the use of this consistent: should this be the second moment or sqrt?
+    // source->moments->Sx = sqrt(PS_MAX(X2/Sum - PS_SQR(x), 0));
+    // source->moments->Sy = sqrt(PS_MAX(Y2/Sum - PS_SQR(y), 0));
+    source->moments->Sx = PS_MAX(X2/Sum - PS_SQR(x), 0);
+    source->moments->Sy = PS_MAX(Y2/Sum - PS_SQR(y), 0);
+
+    psTrace ("psModules.objects", 4,
+             "sky: %f  Sum: %f  x: %f  y: %f  Sx: %f  Sy: %f  Sxy: %f\n",
+             sky, Sum, source->moments->x, source->moments->y,
+             source->moments->Sx, source->moments->Sy, source->moments->Sxy);
+
+    psTrace("psModules.objects", 5, "---- end ----\n");
+    return(true);
+}
+
+// construct a realization of the source model
+bool pmSourceCacheModel (pmSource *source, psMaskType maskVal) {
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    // select appropriate model
+    pmModel *model = pmSourceGetModel (NULL, source);
+    if (model == NULL) return false;  // model must be defined
+
+    // if we already have a cached image, re-use that memory
+    source->modelFlux = psImageCopy (source->modelFlux, source->pixels, PS_TYPE_F32);
+    psImageInit (source->modelFlux, 0.0);
+
+    // in some places (psphotEnsemble), we need a normalized version
+    // in others, we just want the model.  which is more commonly used?
+    // modelFlux always has unity normalization (I0 = 1.0)
+    pmModelAdd (source->modelFlux, source->maskObj, model, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM, maskVal);
+    return true;
+}
+
+// construct a realization of the source model
+// XXX this function should optionally save an existing psf image from modelFlux
+bool pmSourceCachePSF (pmSource *source, psMaskType maskVal) {
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    // select appropriate model
+    if (source->modelPSF == NULL) return false;  // model must be defined
+
+    // if we already have a cached image, re-use that memory
+    source->psfFlux = psImageCopy (source->psfFlux, source->pixels, PS_TYPE_F32);
+    psImageInit (source->psfFlux, 0.0);
+
+    // in some places (psphotEnsemble), we need a normalized version
+    // in others, we just want the model.  which is more commonly used?
+    // psfFlux always has unity normalization (I0 = 1.0)
+    pmModelAdd (source->psfFlux, source->maskObj, source->modelPSF, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM, maskVal);
+    return true;
+}
+
+// should we call pmSourceCacheModel if it does not exist?
+bool pmSourceOp (pmSource *source, pmModelOpMode mode, bool add, psMaskType maskVal, int dx, int dy)
+{
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    bool status;
+
+    if (add) {
+        psTrace ("psphot", 3, "replacing object at %f,%f\n", source->peak->xf, source->peak->yf);
+    } else {
+        psTrace ("psphot", 3, "removing object at %f,%f\n", source->peak->xf, source->peak->yf);
+    }
+
+    pmModel *model = pmSourceGetModel (NULL, source);
+    if (model == NULL) return false;  // model must be defined
+
+    if (source->modelFlux) {
+        // add in the pixels from the modelFlux image
+        int dX = source->modelFlux->col0 - source->pixels->col0;
+        int dY = source->modelFlux->row0 - source->pixels->row0;
+        assert (dX >= 0);
+        assert (dY >= 0);
+        assert (dX + source->modelFlux->numCols <= source->pixels->numCols);
+        assert (dY + source->modelFlux->numRows <= source->pixels->numRows);
+
+        // modelFlux has unity normalization
+        float Io = model->params->data.F32[PM_PAR_I0];
+        if (mode & PM_MODEL_OP_NORM) {
+            Io = 1.0;
+        }
+
+        psU8 **mask = NULL;
+        if (source->maskObj) {
+            mask = source->maskObj->data.U8;
+        }
+
+        psF32 **target = source->pixels->data.F32;
+        if (mode & PM_MODEL_OP_NOISE) {
+	    // XXX need to scale by the gain...
+            target = source->weight->data.F32;
+        }
+
+        // XXX need to respect the source and model masks?
+        for (int iy = 0; iy < source->modelFlux->numRows; iy++) {
+            int oy = iy + dY;
+            for (int ix = 0; ix < source->modelFlux->numCols; ix++) {
+                int ox = ix + dX;
+                if (mask && (mask[iy][ix] & maskVal)) continue;
+                float value = Io*source->modelFlux->data.F32[iy][ix];
+                if (add) {
+                    target[oy][ox] += value;
+                } else {
+                    target[oy][ox] -= value;
+                }
+            }
+        }
+        return true;
+    }
+
+    psImage *target = source->pixels;
+    if (mode & PM_MODEL_OP_NOISE) {
+        target = source->weight;
+    }
+
+    if (add) {
+        status = pmModelAddWithOffset (target, source->maskObj, model, PM_MODEL_OP_FULL, maskVal, dx, dy);
+    } else {
+        status = pmModelSubWithOffset (target, source->maskObj, model, PM_MODEL_OP_FULL, maskVal, dx, dy);
+    }
+
+    return true;
+}
+
+bool pmSourceAdd (pmSource *source, pmModelOpMode mode, psMaskType maskVal) {
+    return pmSourceOp (source, mode, true, maskVal, 0, 0);
+}
+
+bool pmSourceSub (pmSource *source, pmModelOpMode mode, psMaskType maskVal) {
+    return pmSourceOp (source, mode, false, maskVal, 0, 0);
+}
+
+bool pmSourceAddWithOffset (pmSource *source, pmModelOpMode mode, psMaskType maskVal, int dx, int dy) {
+    return pmSourceOp (source, mode, true, maskVal, dx, dy);
+}
+
+bool pmSourceSubWithOffset (pmSource *source, pmModelOpMode mode, psMaskType maskVal, int dx, int dy) {
+    return pmSourceOp (source, mode, false, maskVal, dx, dy);
+}
+
+// given a source, which model is currently appropriate?
+// choose PSF or EXT based on source->type, but fall back on PSF
+// if the EXT model is NULL
+pmModel *pmSourceGetModel (bool *isPSF, const pmSource *source)
+{
+    PS_ASSERT_PTR_NON_NULL(source, NULL);
+
+    pmModel *model;
+
+    if (isPSF) {
+        *isPSF = false;
+    }
+
+    switch (source->type) {
+      case PM_SOURCE_TYPE_STAR:
+        model = source->modelPSF;
+        if (model == NULL)
+            return NULL;
+        if (isPSF) {
+            *isPSF = true;
+        }
+        return model;
+
+	// the 'best' extended model is saved in source->modelEXT (may be a pointer to one of
+	// the elements of source->modelFits)
+      case PM_SOURCE_TYPE_EXTENDED:
+	model = source->modelEXT;
+        if (!model && source->modelPSF) {
+	    // XXX raise an error or warning here?
+            if (isPSF) {
+                *isPSF = true;
+            }
+            return source->modelPSF;
+        }
+        return (model);
+        break;
+
+      default:
+        return NULL;
+    }
+    return NULL;
+}
+
+// sort by SN (descending)
+int pmSourceSortBySN (const void **a, const void **b)
+{
+    pmSource *A = *(pmSource **)a;
+    pmSource *B = *(pmSource **)b;
+
+    psF32 fA = (A->peak == NULL) ? 0 : A->peak->SN;
+    psF32 fB = (B->peak == NULL) ? 0 : B->peak->SN;
+    if (isnan (fA)) fA = 0;
+    if (isnan (fB)) fB = 0;
+
+    psF32 diff = fA - fB;
+    if (diff > FLT_EPSILON) return (-1);
+    if (diff < FLT_EPSILON) return (+1);
+    return (0);
+}
+
+// sort by Y (ascending)
+int pmSourceSortByY (const void **a, const void **b)
+{
+    pmSource *A = *(pmSource **)a;
+    pmSource *B = *(pmSource **)b;
+
+    psF32 fA = (A->peak == NULL) ? 0 : A->peak->y;
+    psF32 fB = (B->peak == NULL) ? 0 : B->peak->y;
+
+    psF32 diff = fA - fB;
+    if (diff > FLT_EPSILON) return (+1);
+    if (diff < FLT_EPSILON) return (-1);
+    return (0);
+}
+
+pmSourceMode pmSourceModeFromString (const char *name) {
+  if (!strcasecmp (name, "DEFAULT"   )) return PM_SOURCE_MODE_DEFAULT;
+  if (!strcasecmp (name, "PSFMODEL"  )) return PM_SOURCE_MODE_PSFMODEL;
+  if (!strcasecmp (name, "EXTMODEL"  )) return PM_SOURCE_MODE_EXTMODEL;
+  if (!strcasecmp (name, "FITTED"    )) return PM_SOURCE_MODE_FITTED;
+  if (!strcasecmp (name, "FAIL"      )) return PM_SOURCE_MODE_FAIL;
+  if (!strcasecmp (name, "POOR"      )) return PM_SOURCE_MODE_POOR;
+  if (!strcasecmp (name, "PAIR"      )) return PM_SOURCE_MODE_PAIR;
+  if (!strcasecmp (name, "PSFSTAR"   )) return PM_SOURCE_MODE_PSFSTAR;
+  if (!strcasecmp (name, "SATSTAR"   )) return PM_SOURCE_MODE_SATSTAR;
+  if (!strcasecmp (name, "BLEND"     )) return PM_SOURCE_MODE_BLEND;
+  if (!strcasecmp (name, "EXTERNAL"  )) return PM_SOURCE_MODE_EXTERNAL;
+  if (!strcasecmp (name, "BADPSF"    )) return PM_SOURCE_MODE_BADPSF;
+  if (!strcasecmp (name, "DEFECT"    )) return PM_SOURCE_MODE_DEFECT;
+  if (!strcasecmp (name, "SATURATED" )) return PM_SOURCE_MODE_SATURATED;
+  if (!strcasecmp (name, "CRLIMIT"   )) return PM_SOURCE_MODE_CR_LIMIT;
+  if (!strcasecmp (name, "EXTLIMIT"  )) return PM_SOURCE_MODE_EXT_LIMIT;
+  if (!strcasecmp (name, "SUBTRACTED")) return PM_SOURCE_MODE_SUBTRACTED;
+  return PM_SOURCE_MODE_DEFAULT;
+}
+
+char *pmSourceModeToString (const pmSourceMode mode) {
+  switch (mode) {
+    case PM_SOURCE_MODE_DEFAULT    : return psStringCopy ("DEFAULT"   );
+    case PM_SOURCE_MODE_PSFMODEL   : return psStringCopy ("PSFMODEL"  );
+    case PM_SOURCE_MODE_EXTMODEL   : return psStringCopy ("EXTMODEL"  );
+    case PM_SOURCE_MODE_FITTED     : return psStringCopy ("FITTED"    );
+    case PM_SOURCE_MODE_FAIL       : return psStringCopy ("FAIL"      );
+    case PM_SOURCE_MODE_POOR       : return psStringCopy ("POOR"      );
+    case PM_SOURCE_MODE_PAIR       : return psStringCopy ("PAIR"      );
+    case PM_SOURCE_MODE_PSFSTAR    : return psStringCopy ("PSFSTAR"   );
+    case PM_SOURCE_MODE_SATSTAR    : return psStringCopy ("SATSTAR"   );
+    case PM_SOURCE_MODE_BLEND      : return psStringCopy ("BLEND"     );
+    case PM_SOURCE_MODE_EXTERNAL   : return psStringCopy ("EXTERNAL"  );
+    case PM_SOURCE_MODE_BADPSF     : return psStringCopy ("BADPSF"    );
+    case PM_SOURCE_MODE_DEFECT     : return psStringCopy ("DEFECT"    );
+    case PM_SOURCE_MODE_SATURATED  : return psStringCopy ("SATURATED" );
+    case PM_SOURCE_MODE_CR_LIMIT   : return psStringCopy ("CRLIMIT"   );
+    case PM_SOURCE_MODE_EXT_LIMIT  : return psStringCopy ("EXTLIMIT"  );
+    case PM_SOURCE_MODE_SUBTRACTED : return psStringCopy ("SUBTRACTED");
+    default:
+      return NULL;
+  }
+  return NULL;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSource.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSource.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSource.h	(revision 22322)
@@ -0,0 +1,243 @@
+/* @file  pmSource.h
+ *
+ * @author EAM, IfA; GLG, MHPCC
+ *
+ * @version $Revision: 1.24 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-04-08 18:35:38 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_SOURCE_H
+# define PM_SOURCE_H
+
+# include "pmSourceExtendedPars.h"
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/** pmSourceType enumeration
+ *
+ * A given source may be identified as most-likely to be one of several source
+ * types. The pmSource entry pmSourceType defines the current best-guess for this
+ * source.
+ *
+ */
+typedef enum {
+    PM_SOURCE_TYPE_UNKNOWN,		///< not yet classified
+    PM_SOURCE_TYPE_DEFECT,		///< a cosmic-ray
+    PM_SOURCE_TYPE_SATURATED,		///< random saturated pixels (eg, bleed trails)
+    PM_SOURCE_TYPE_STAR,		///< a good-quality star (subtracted model is PSF)
+    PM_SOURCE_TYPE_EXTENDED,		///< an extended object (eg, galaxy) (subtracted model is EXT)
+} pmSourceType;
+
+typedef enum {
+    PM_SOURCE_MODE_DEFAULT    = 0x0000, ///<
+    PM_SOURCE_MODE_PSFMODEL   = 0x0001, ///< Source fitted with a psf model (linear or non-linear)
+    PM_SOURCE_MODE_EXTMODEL   = 0x0002, ///< Source fitted with an extended-source model
+    PM_SOURCE_MODE_FITTED     = 0x0004, ///< Source fitted with non-linear model (PSF or EXT; good or bad)
+    PM_SOURCE_MODE_FAIL       = 0x0008, ///< Fit (non-linear) failed (non-converge, off-edge, run to zero)
+    PM_SOURCE_MODE_POOR       = 0x0010, ///< Fit succeeds, but low-SN, high-Chisq, or large (for PSF -- drop?)
+    PM_SOURCE_MODE_PAIR       = 0x0020, ///< Source fitted with a double psf
+    PM_SOURCE_MODE_PSFSTAR    = 0x0040, ///< Source used to define PSF model
+    PM_SOURCE_MODE_SATSTAR    = 0x0080, ///< Source model peak is above saturation
+    PM_SOURCE_MODE_BLEND      = 0x0100, ///< Source is a blend with other sourcers
+    PM_SOURCE_MODE_EXTERNAL   = 0x0200, ///< Source based on supplied input position
+    PM_SOURCE_MODE_BADPSF     = 0x0400, ///< Failed to get good estimate of object's PSF
+    PM_SOURCE_MODE_DEFECT     = 0x0800, ///< Source is thought to be a defect
+    PM_SOURCE_MODE_SATURATED  = 0x1000, ///< Source is thought to be saturated pixels (bleed trail)
+    PM_SOURCE_MODE_CR_LIMIT   = 0x2000, ///< Source has crNsigma above limit
+    PM_SOURCE_MODE_EXT_LIMIT  = 0x4000, ///< Source has extNsigma above limit
+    PM_SOURCE_MODE_SUBTRACTED = 0x8000, ///< XXX this flag is actually only used internally (move)
+} pmSourceMode;
+
+/** pmSource data structure
+ *
+ *  This source has the capacity for several types of measurements. The
+ *  simplest measurement of a source is the location and flux of the peak pixel
+ *  associated with the source:
+ *
+ *  XXX do I have to re-organize this (again!) to allow an arbitrary set of extended model fits??
+ *  XXX put the Mag and Err inside the pmModel?
+ *  XXX keep the modelEXT or add to the psArray 
+ *  
+ *
+ */
+struct pmSource {
+    const int id;                       ///< Unique ID for object
+    int seq;				///< ID for output (generated on write)
+    pmPeak  *peak;                      ///< Description of peak pixel.
+    psImage *pixels;                    ///< Rectangular region including object pixels.
+    psImage *weight;                    ///< Image variance.
+    psImage *maskObj;                   ///< unique mask for this object which marks included pixels associated with objects.
+    psImage *maskView;                  ///< view into global image mask for this object region
+    psImage *modelFlux;                 ///< cached copy of the best model for this source
+    psImage *psfFlux;                   ///< cached copy of the psf model for this source
+    pmMoments *moments;                 ///< Basic moments measured for the object.
+    pmModel *modelPSF;                  ///< PSF Model fit (parameters and type)
+    pmModel *modelEXT;                  ///< EXT Model fit used for subtraction (parameters and type)
+    psArray *modelFits;			///< collection of extended source models (best == modelEXT)
+    pmSourceType type;                  ///< Best identification of object.
+    pmSourceMode mode;                  ///< analysis flags set for object.
+    psArray *blends;			///< collection of sources thought to be confused with object
+    float psfMag;                       ///< calculated from flux in modelPSF
+    float extMag;                       ///< calculated from flux in modelEXT
+    float errMag;                       ///< error in psfMag OR extMag (depending on type)
+    float apMag;                        ///< apMag corresponding to psfMag or extMag (depending on type)
+    float pixWeight;                    ///< model-weighted coverage of valid pixels
+    float psfChisq;			///< probability of PSF
+    float crNsigma;                     ///< Nsigma deviation from PSF to CR
+    float extNsigma;                    ///< Nsigma deviation from PSF to EXT
+    float sky, skyErr;                  ///< The sky and its error at the center of the object
+    psRegion region;                    ///< area on image covered by selected pixels
+    pmSourceExtendedPars *extpars;      ///< extended source parameters
+};
+
+/** pmPSFClump data structure
+ *
+ * A collection of object moment measurements can be used to determine
+ * approximate object classes. The key to this analysis is the location and
+ * statistics (in the second-moment plane,
+ *
+ */
+typedef struct
+{
+    float X;
+    float dX;
+    float Y;
+    float dY;
+}
+pmPSFClump;
+
+
+/** pmSourceAlloc()
+ *
+ */
+pmSource  *pmSourceAlloc();
+
+/** pmSourceCopy()
+ *
+ */
+
+bool psMemCheckSource(psPtr ptr);
+
+pmSource  *pmSourceCopy(pmSource *source);
+
+// free just the pixels for a source, keeping derived data
+void pmSourceFreePixels(pmSource *source);
+
+/** pmSourceDefinePixels()
+ *
+ * Define psImage subarrays for the source located at coordinates x,y on the
+ * image set defined by readout. The pixels defined by this operation consist of
+ * a square window (of full width 2Radius+1) centered on the pixel which contains
+ * the given coordinate, in the frame of the readout. The window is defined to
+ * have limits which are valid within the boundary of the readout image, thus if
+ * the radius would fall outside the image pixels, the subimage is truncated to
+ * only consist of valid pixels. If readout->mask or readout->weight are not
+ * NULL, matching subimages are defined for those images as well. This function
+ * fails if no valid pixels can be defined (x or y less than Radius, for
+ * example). This function should be used to define a region of interest around a
+ * source, including both source and sky pixels.
+ *
+ */
+bool pmSourceDefinePixels(
+    pmSource *mySource,                 ///< source to be re-defined
+    const pmReadout *readout,  ///< base the source on this readout
+    psF32 x,                            ///< center coords of source
+    psF32 y,                            ///< center coords of source
+    psF32 Radius                        ///< size of box on source
+);
+
+bool pmSourceRedefinePixels (
+    pmSource *mySource,   ///< source to be re-defined
+    const pmReadout *readout,   ///< base the source on this readout
+    psF32 x,     ///< center coords of source
+    psF32 y,      ///< center coords of source
+    psF32 Radius   ///< size of box on source
+);
+
+/** pmSourcePSFClump()
+ *
+ * We use the source moments to make an initial, approximate source
+ * classification, and as part of the information needed to build a PSF model for
+ * the image. As long as the PSF shape does not vary excessively across the
+ * image, the sources which are represented by a PSF (the start) will have very
+ * similar second moments. The function pmSourcePSFClump searches a collection of
+ * sources with measured moments for a group with moments which are all very
+ * similar. The function returns a pmPSFClump structure, representing the
+ * centroid and size of the clump in the sigma_x, sigma_y second-moment plane.
+ *
+ * The goal is to identify and characterize the stellar clump within the
+ * sigma_x, sigma_y second-moment plane.  To do this, an image is constructed to
+ * represent this plane.  The units of sigma_x and sigma_y are in image pixels. A
+ * pixel in this analysis image represents 0.1 pixels in the input image. The
+ * dimensions of the image need only be 10 pixels. The peak pixel in this image
+ * (above a threshold of half of the image maximum) is found. The coordinates of
+ * this peak pixel represent the 2D mode of the sigma_x, sigma_y distribution.
+ * The sources with sigma_x, sigma_y within 0.2 pixels of this value are then
+ *  * used to calculate the median and standard deviation of the sigma_x, sigma_y
+ * values. These resulting values are returned via the pmPSFClump structure.
+ *
+ * The return value indicates the success (TRUE) of the operation.
+ *
+ * XXX: Limit the S/N of the candidate sources (part of Metadata)? (TBD).
+ * XXX: Save the clump parameters on the Metadata (TBD)
+ *
+ */
+pmPSFClump pmSourcePSFClump(
+    psArray *source,                    ///< The input pmSource
+    psMetadata *metadata                ///< Contains classification parameters
+);
+
+/** pmSourceRoughClass()
+ *
+ * Based on the specified data values, make a guess at the source
+ * classification. The sources are provides as a psArray of pmSource entries.
+ * Definable parameters needed to make the classification are provided to the
+ * routine with the psMetadata structure. The rules (in SDRS) refer to values which
+ * can be extracted from the metadata using the given keywords. Except as noted,
+ * the data type for these parameters are psF32.
+ *
+ */
+bool pmSourceRoughClass(
+    psArray *source,                    ///< The input pmSource
+    psMetadata *metadata,               ///< Contains classification parameters
+    pmPSFClump clump,                   ///< Statistics about the PSF clump
+    psMaskType maskSat                  ///< Mask value for saturated pixels
+);
+
+
+/** pmSourceMoments()
+ *
+ * Measure source moments for the given source, using the value of
+ * source.moments.sky provided as the local background value and the peak
+ * coordinates as the initial source location. The resulting moment values are
+ * applied to the source.moments entry, and the source is returned. The moments
+ * are measured within the given circular radius of the source.peak coordinates.
+ * The return value indicates the success (TRUE) of the operation.
+ *
+ */
+bool pmSourceMoments(
+    pmSource *source,   ///< The input pmSource for which moments will be computed
+    float radius   ///< Use a circle of pixels around the peak
+);
+
+pmModel *pmSourceGetModel (bool *isPSF, const pmSource *source);
+
+bool pmSourceAdd (pmSource *source, pmModelOpMode mode, psMaskType maskVal);
+bool pmSourceSub (pmSource *source, pmModelOpMode mode, psMaskType maskVal);
+bool pmSourceAddWithOffset (pmSource *source, pmModelOpMode mode, psMaskType maskVal, int dx, int dy);
+bool pmSourceSubWithOffset (pmSource *source, pmModelOpMode mode, psMaskType maskVal, int dx, int dy);
+
+bool pmSourceOp (pmSource *source, pmModelOpMode mode, bool add, psMaskType maskVal, int dx, int dy);
+bool pmSourceCacheModel (pmSource *source, psMaskType maskVal);
+bool pmSourceCachePSF (pmSource *source, psMaskType maskVal);
+
+int             pmSourceSortBySN (const void **a, const void **b);
+int             pmSourceSortByY (const void **a, const void **b);
+
+pmSourceMode pmSourceModeFromString (const char *name);
+char *pmSourceModeToString (const pmSourceMode mode);
+
+/// @}
+# endif /* PM_SOURCE_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceContour.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceContour.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceContour.c	(revision 22322)
@@ -0,0 +1,459 @@
+/** @file  pmSourceContour.c
+ *
+ *  Functions to measure the local sky and sky variance for sources on images
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA: significant modifications.
+ *
+ *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-01-02 20:39:04 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "pslib.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmSourceContour.h"
+
+/******************************************************************************
+findValue(source, level, row, col, dir): a private function which determines
+the column coordinate of the model function which has the value "level".  If
+dir equals 0, then you loop leftwards from the peak pixel, otherwise,
+rightwards.
+ 
+XXX: reverse order of row,col args?
+ 
+XXX: Input row/col are in image coords.
+ 
+XXX: The result is returned in image coords.
+*****************************************************************************/
+# define LEFT false
+# define RIGHT true
+
+// return the first coordinate at or below the threshold in the requested direction
+static int findContourNeg(
+    psImage *image,
+    float threshold,
+    int x,
+    int y,
+    bool right)
+{
+
+    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+
+    // We define variables incr and lastColumn so that we can use the same loop
+    // whether we are stepping leftwards, or rightwards.
+
+    int incr;
+    int subCol;
+    int lastColumn;
+    if (right) {
+        incr = 1;
+        lastColumn = image->numCols - 1;
+    } else {
+        incr = -1;
+        lastColumn = 0;
+    }
+
+    subCol = x;
+
+    while (subCol != lastColumn) {
+        float value = image->data.F32[y][subCol];
+        if (value <= threshold) {
+            psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+            return (subCol);
+        }
+        subCol += incr;
+    }
+    psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+    return (lastColumn);
+}
+
+// return the last coordinate at or below the threshold in the requested direction
+static int findContourPos(
+    psImage *image,
+    float threshold,
+    int x,
+    int y,
+    bool right,
+    int xEnd)
+{
+
+    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+
+    // We define variables incr and lastColumn so that we can use the same loop
+    // whether we are stepping leftwards, or rightwards.
+
+    int incr;
+    int subCol;
+    int lastColumn;
+    if (right) {
+        incr = 1;
+        lastColumn = PS_MIN (image->numCols - 1, xEnd);
+    } else {
+        incr = -1;
+        lastColumn = PS_MAX (0, xEnd);
+    }
+
+    subCol = x;
+    while (subCol != lastColumn) {
+        float value = image->data.F32[y][subCol];
+        if (value >= threshold) {
+            psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+            if (subCol == x) {
+                return (subCol);
+            }
+            return (subCol);
+        }
+        subCol += incr;
+    }
+    psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+    return (lastColumn);
+}
+
+/******************************************************************************
+findValue(source, level, row, col, dir): a private function which determines
+the column coordinate of the model function which has the value "level".  If
+dir equals 0, then you loop leftwards from the peak pixel, otherwise,
+rightwards.
+ 
+XXX: reverse order of row,col args?
+ 
+XXX: Input row/col are in image coords.
+ 
+XXX: The result is returned in image coords.
+*****************************************************************************/
+static psF32 findValue(pmSource *source,
+                       psF32 level,
+                       psU32 row,
+                       psU32 col,
+                       psU32 dir)
+{
+    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+    //
+    // Convert coords to subImage space.
+    //
+    psU32 subRow = row - source->pixels->row0;
+    psU32 subCol = col - source->pixels->col0;
+
+    // Ensure that the starting column is allowable.
+    if (!((0 <= subCol) && (subCol < source->pixels->numCols))) {
+        psError(PS_ERR_UNKNOWN, true, "Starting column outside subImage range");
+        psTrace("psModules.objects", 4, "---- %s(NAN) end ----\n", __func__);
+        return(NAN);
+    }
+    if (!((0 <= subRow) && (subRow < source->pixels->numRows))) {
+        psTrace("psModules.objects", 4, "---- %s(NAN) end ----\n", __func__);
+        psError(PS_ERR_UNKNOWN, true, "Starting row outside subImage range");
+        return(NAN);
+    }
+
+    // XXX EAM : i changed this to match pmModelEval above, but see
+    // XXX EAM   the note below in pmSourceContour
+    psF32 oldValue = pmModelEval(source->modelEXT, source->pixels, subCol, subRow);
+    if (oldValue == level) {
+        psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+        return(((psF32) (subCol + source->pixels->col0)));
+    }
+
+    //
+    // We define variables incr and lastColumn so that we can use the same loop
+    // whether we are stepping leftwards, or rightwards.
+    //
+    psS32 incr;
+    psS32 lastColumn;
+    if (dir == 0) {
+        incr = -1;
+        lastColumn = -1;
+    } else {
+        incr = 1;
+        lastColumn = source->pixels->numCols;
+    }
+    subCol+=incr;
+
+    while (subCol != lastColumn) {
+        psF32 newValue = pmModelEval(source->modelEXT, source->pixels, subCol, subRow);
+        if (oldValue == level) {
+            psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+            return((psF32) (subCol + source->pixels->col0));
+        }
+
+        if ((newValue <= level) && (level <= oldValue)) {
+            // This is simple linear interpolation.
+            psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+            return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - newValue) / (oldValue - newValue)) );
+        }
+
+        if ((oldValue <= level) && (level <= newValue)) {
+            // This is simple linear interpolation.
+            psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+            return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - oldValue) / (newValue - oldValue)) );
+        }
+
+        subCol+=incr;
+    }
+
+    psTrace("psModules.objects", 4, "---- %s(NAN) end ----\n", __func__);
+    return(NAN);
+}
+
+/******************************************************************************
+new implementation of source contour function
+*****************************************************************************/
+psArray *pmSourceContour (psImage *image, int xc, int yc, float threshold)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(image, NULL);
+
+    int xR, yR, x0, x1, x0s, x1s;
+    int x = xc - image->col0;
+    int y = yc - image->row0;
+
+    // Ensure that the starting column is allowable.
+    if (x < 0) {
+        return NULL;
+    }
+    if (y < 0) {
+        return NULL;
+    }
+    if (x >= image->numCols) {
+        return NULL;
+    }
+    if (y >= image->numRows) {
+        return NULL;
+    }
+
+    // the requested point must be within the contour
+    if (image->data.F32[y][x] < threshold) {
+        return NULL;
+    }
+
+    // Allocate data for x/y pairs.
+    psVector *xVec = psVectorAllocEmpty(100, PS_TYPE_F32);
+    psVector *yVec = psVectorAllocEmpty(100, PS_TYPE_F32);
+
+    // First row: find the left and right end-points
+    int Npt = 0;
+
+    x0 = findContourNeg (image, threshold, x, y, LEFT);
+    x1 = findContourNeg (image, threshold, x, y, RIGHT);
+    xVec->data.F32[Npt + 0] = image->col0 + x0;
+    xVec->data.F32[Npt + 1] = image->col0 + x1;
+    yVec->data.F32[Npt + 0] = image->row0 + y;
+    yVec->data.F32[Npt + 1] = image->row0 + y;
+    Npt += 2;
+
+    x0s = x0;
+    x1s = x1;
+
+    // look for contour outline above row
+    xR = x0s;
+    yR = y + 1;
+    while (yR < image->numRows) {
+        if (image->data.F32[yR][xR] < threshold) {
+            x0 = findContourPos (image, threshold, xR, yR, RIGHT, x1);
+            if (x0 == x1) {
+                // fprintf (stderr, "top: %d (%d - %d)\n", yR, xR, x1);
+                goto pt1;
+            }
+            x1 = findContourNeg (image, threshold, x0, yR, RIGHT);
+            x0--;
+        } else {
+            x0 = findContourNeg (image, threshold, xR, yR, LEFT);
+            x1 = findContourNeg (image, threshold, xR, yR, RIGHT);
+        }
+        // fprintf (stderr, "pos: %d (%d - %d)\n", yR, x0, x1);
+
+        xVec->data.F32[Npt + 0] = image->col0 + x0;
+        xVec->data.F32[Npt + 1] = image->col0 + x1;
+        yVec->data.F32[Npt + 0] = image->row0 + yR;
+        yVec->data.F32[Npt + 1] = image->row0 + yR;
+        Npt += 2;
+
+        if (Npt >= xVec->nalloc - 1) {
+            psVectorRealloc (xVec, xVec->nalloc + 100);
+            psVectorRealloc (yVec, yVec->nalloc + 100);
+        }
+        yR ++;
+        xR = x0;
+    }
+
+pt1:
+    // look for contour outline below row
+    xR = x0s;
+    x1 = x1s;
+    yR = y - 1;
+    while (yR >= 0) {
+        if (image->data.F32[yR][xR] < threshold) {
+            x0 = findContourPos (image, threshold, xR, yR, RIGHT, x1);
+            if (x0 == x1) {
+                // fprintf (stderr, "top: %d (%d - %d)\n", yR, xR, x1);
+                goto pt2;
+            }
+            x1 = findContourNeg (image, threshold, x0, yR, RIGHT);
+            x0--;
+        } else {
+            x0 = findContourNeg (image, threshold, xR, yR, LEFT);
+            x1 = findContourNeg (image, threshold, xR, yR, RIGHT);
+        }
+        // fprintf (stderr, "neg: %d (%d - %d)\n", yR, x0, x1);
+
+        xVec->data.F32[Npt + 0] = image->col0 + x0;
+        xVec->data.F32[Npt + 1] = image->col0 + x1;
+        yVec->data.F32[Npt + 0] = image->row0 + yR;
+        yVec->data.F32[Npt + 1] = image->row0 + yR;
+        Npt += 2;
+
+        if (Npt >= xVec->nalloc - 1) {
+            psVectorRealloc (xVec, xVec->nalloc + 100);
+            psVectorRealloc (yVec, yVec->nalloc + 100);
+        }
+        yR --;
+    }
+pt2:
+    xVec->n = Npt;
+    yVec->n = Npt;
+
+    // fprintf (stderr, "done\n");
+    psArray *tmpArray = psArrayAlloc(2);
+
+    tmpArray->data[0] = (psPtr *) xVec;
+    tmpArray->data[1] = (psPtr *) yVec;
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(tmpArray);
+}
+
+/******************************************************************************
+    pmSourceContour(src, img, level, mode): For an input subImage, and model, this
+    routine returns a psArray of coordinates that evaluate to the specified level.
+ 
+    XXX: Probably should remove the "image" argument.
+    XXX: What type should the output coordinate vectors consist of?  col,row?
+    XXX: Why a pmArray output?
+    XXX: doex x,y correspond with col,row or row/col?
+    XXX: What is mode?
+    XXX: The top, bottom of the contour is not correctly determined.
+    XXX EAM : this function is using the model for the contour, but it should
+              be using only the image counts
+*****************************************************************************/
+psArray *pmSourceContour_Crude(pmSource *source,
+                               const psImage *image,
+                               psF32 level)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(image, false);
+    PS_ASSERT_PTR_NON_NULL(source->moments, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(source->modelEXT, false);
+    // XXX EAM : what is the purpose of modelPSF/modelEXT?
+
+    //
+    // Allocate data for x/y pairs.
+    //
+    psVector *xVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
+    psVector *yVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
+
+    //
+    // Start at the row with peak pixel, then decrement.
+    //
+    psS32 col = source->peak->x;
+    for (psS32 row = source->peak->y; row>= 0 ; row--) {
+        // XXX: yVec contain no real information.  Do we really need it?
+        yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
+        yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
+
+        // Starting at peak pixel, search leftwards for the column intercept.
+        psF32 leftIntercept = findValue(source, level, row, col, 0);
+        if (isnan(leftIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            psTrace("psModules.objects", 3, "---- %s(NULL) end ----\n", __func__);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
+
+        // Starting at peak pixel, search rightwards for the column intercept.
+
+        psF32 rightIntercept = findValue(source, level, row, col, 1);
+        if (isnan(rightIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            psTrace("psModules.objects", 3, "---- %s(NULL) end ----\n", __func__);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        psTrace("psModules.objects", 4, "The intercepts are (%.2f, %.2f)\n", leftIntercept, rightIntercept);
+        xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
+
+        // Set starting column for next row
+        col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
+    }
+    //
+    // Start at the row (+1) with peak pixel, then increment.
+    //
+    col = source->peak->x;
+    for (psS32 row = 1 + source->peak->y; row < source->pixels->numRows ; row++) {
+        // XXX: yVec contain no real information.  Do we really need it?
+        yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
+        yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
+
+        // Starting at peak pixel, search leftwards for the column intercept.
+        psF32 leftIntercept = findValue(source, level, row, col, 0);
+        if (isnan(leftIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            psTrace("psModules.objects", 3, "---- %s(NULL) end ----\n", __func__);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
+
+        // Starting at peak pixel, search rightwards for the column intercept.
+        psF32 rightIntercept = findValue(source, level, row, col, 1);
+        if (isnan(rightIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            psTrace("psModules.objects", 3, "---- %s(NULL) end ----\n", __func__);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
+
+        // Set starting column for next row
+        col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
+    }
+
+    //
+    // Allocate an array for result, store coord vectors there.
+    //
+    psArray *tmpArray = psArrayAlloc(2);
+
+    tmpArray->data[0] = (psPtr *) yVec;
+    tmpArray->data[1] = (psPtr *) xVec;
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(tmpArray);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceContour.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceContour.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceContour.h	(revision 22322)
@@ -0,0 +1,39 @@
+/* @file  pmSourceContour.h
+ *
+ * @author EAM, IfA; GLG, MHPCC
+ *
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-01-24 02:54:15 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_SOURCE_CONTOUR_H
+# define PM_SOURCE_CONTOUR_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+psArray *pmSourceContour (psImage *image, int xc, int yc, float threshold);
+
+
+/** pmSourceContour()
+ *
+ * Find points in a contour for the given source at the given level. If type
+ * is PM_CONTOUR_CRUDE, the contour is found by starting at the source peak,
+ * running along each pixel row until the level is crossed, then interpolating to
+ * the level coordinate for that row. This is done for each row, with the
+ * starting point determined by the midpoint of the previous row, until the
+ * starting point has a value below the contour level. The returned contour
+ * consists of two vectors giving the x and y coordinates of the contour levels.
+ * This function may be used as part of the model guess inputs.  Other contour
+ * types may be specified in the future for more refined contours (TBD)
+ *
+ */
+psArray *pmSourceContour_Crude(
+    pmSource *source,   ///< The input pmSource
+    const psImage *image,  ///< The input image (float) (this arg should be removed)
+    float level   ///< The level of the contour
+);
+
+/// @}
+# endif /* PM_SOURCE_PHOTOMETRY_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceExtendedPars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceExtendedPars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceExtendedPars.c	(revision 22322)
@@ -0,0 +1,198 @@
+/** @file  pmSourceExtendedPars.c
+ *
+ *  Functions to define and manipulate sources on images
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA: significant modifications.
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-12-22 00:31:41 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+
+static void pmSourceExtendedParsFree (pmSourceExtendedPars *pars) {
+    if (!pars) return;
+
+    psFree(pars->profile);
+    psFree(pars->annuli);
+    psFree(pars->isophot);
+    psFree(pars->petrosian);
+    psFree(pars->kron);
+    return;
+}
+
+pmSourceExtendedPars *pmSourceExtendedParsAlloc () {
+    pmSourceExtendedPars *pars = (pmSourceExtendedPars *) psAlloc(sizeof(pmSourceExtendedPars));
+    psMemSetDeallocator(pars, (psFreeFunc) pmSourceExtendedParsFree);
+
+    pars->profile = NULL;
+    pars->annuli = NULL;
+    pars->isophot = NULL;
+    pars->petrosian = NULL;
+    pars->kron = NULL;
+
+    return pars;
+}
+
+bool psMemCheckSourceExtendedPars(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceExtendedParsFree);
+}
+
+
+static void pmSourceRadialProfileFree (pmSourceRadialProfile *profile) {
+    if (!profile) return;
+
+    psFree(profile->radius);
+    psFree(profile->flux);
+    psFree(profile->weight);
+    return;
+}
+
+pmSourceRadialProfile *pmSourceRadialProfileAlloc () {
+
+    pmSourceRadialProfile *profile = (pmSourceRadialProfile *) psAlloc(sizeof(pmSourceRadialProfile));
+    psMemSetDeallocator(profile, (psFreeFunc) pmSourceRadialProfileFree);
+
+    profile->radius = NULL;
+    profile->flux = NULL;
+    profile->weight = NULL;
+
+    return profile;
+}
+
+bool psMemCheckSourceRadialProfile(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceRadialProfileFree);
+}
+
+
+static void pmSourceIsophotalValuesFree (pmSourceIsophotalValues *isophot) {
+    if (!isophot) return;
+    return;
+}
+
+pmSourceIsophotalValues *pmSourceIsophotalValuesAlloc () {
+
+    pmSourceIsophotalValues *isophot = (pmSourceIsophotalValues *) psAlloc(sizeof(pmSourceIsophotalValues));
+    psMemSetDeallocator(isophot, (psFreeFunc) pmSourceIsophotalValuesFree);
+
+    isophot->mag = 0.0;
+    isophot->magErr = 0.0;
+    isophot->rad = 0.0;
+    isophot->radErr = 0.0;
+
+    return isophot;
+}
+
+
+bool psMemCheckSourceIsophotalValues(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceIsophotalValuesFree);
+}
+
+
+static void pmSourcePetrosianValuesFree (pmSourcePetrosianValues *petrosian) {
+    if (!petrosian) return;
+    return;
+}
+
+pmSourcePetrosianValues *pmSourcePetrosianValuesAlloc () {
+
+    pmSourcePetrosianValues *petrosian = (pmSourcePetrosianValues *) psAlloc(sizeof(pmSourcePetrosianValues));
+    psMemSetDeallocator(petrosian, (psFreeFunc) pmSourcePetrosianValuesFree);
+
+    petrosian->mag = 0.0;
+    petrosian->magErr = 0.0;
+    petrosian->rad = 0.0;
+    petrosian->radErr = 0.0;
+
+    return petrosian;
+}
+
+
+bool psMemCheckSourcePetrosianValues(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourcePetrosianValuesFree);
+}
+
+static void pmSourceKronValuesFree (pmSourceKronValues *kron) {
+    if (!kron) return;
+    return;
+}
+
+pmSourceKronValues *pmSourceKronValuesAlloc () {
+
+    pmSourceKronValues *kron = (pmSourceKronValues *) psAlloc(sizeof(pmSourceKronValues));
+    psMemSetDeallocator(kron, (psFreeFunc) pmSourceKronValuesFree);
+
+    kron->mag = 0.0;
+    kron->magErr = 0.0;
+    kron->rad = 0.0;
+    kron->radErr = 0.0;
+
+    return kron;
+}
+
+
+bool psMemCheckSourceKronValues(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceKronValuesFree);
+}
+
+
+static void pmSourceAnnuliFree (pmSourceAnnuli *annuli) {
+    if (!annuli) return;
+
+    psFree (annuli->flux);
+    psFree (annuli->fluxErr);
+    psFree (annuli->fluxVar);
+
+    return;
+}
+
+pmSourceAnnuli *pmSourceAnnuliAlloc () {
+
+    pmSourceAnnuli *annuli = (pmSourceAnnuli *) psAlloc(sizeof(pmSourceAnnuli));
+    psMemSetDeallocator(annuli, (psFreeFunc) pmSourceAnnuliFree);
+
+    annuli->flux = NULL;
+    annuli->fluxErr = NULL;
+    annuli->fluxVar = NULL;
+
+    return annuli;
+}
+
+
+bool psMemCheckSourceAnnuli(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceAnnuliFree);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceExtendedPars.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceExtendedPars.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceExtendedPars.h	(revision 22322)
@@ -0,0 +1,71 @@
+/* @file  pmSourceExtendedPars.h
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-01-02 20:39:04 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_SOURCE_EXTENDED_PARS_H
+# define PM_SOURCE_EXTENDED_PARS_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+typedef struct {
+  psVector *radius;
+  psVector *flux;
+  psVector *weight;
+} pmSourceRadialProfile;
+
+typedef struct {
+  psVector *flux;
+  psVector *fluxErr;
+  psVector *fluxVar;
+} pmSourceAnnuli;
+
+typedef struct {
+  float mag;
+  float magErr;
+  float rad;
+  float radErr;
+} pmSourceIsophotalValues;
+
+typedef struct {
+  float mag;
+  float magErr;
+  float rad;
+  float radErr;
+} pmSourcePetrosianValues;
+
+typedef struct {
+  float mag;
+  float magErr;
+  float rad;
+  float radErr;
+} pmSourceKronValues;
+
+typedef struct {
+  pmSourceRadialProfile   *profile;
+  pmSourceAnnuli          *annuli;
+  pmSourceIsophotalValues *isophot;
+  pmSourcePetrosianValues *petrosian;
+  pmSourceKronValues      *kron;
+} pmSourceExtendedPars;
+
+pmSourceExtendedPars *pmSourceExtendedParsAlloc ();
+bool psMemCheckSourceExtendedPars(psPtr ptr);
+pmSourceRadialProfile *pmSourceRadialProfileAlloc ();
+bool psMemCheckSourceRadialProfile(psPtr ptr);
+pmSourceIsophotalValues *pmSourceIsophotalValuesAlloc ();
+bool psMemCheckSourceIsophotalValues(psPtr ptr);
+pmSourcePetrosianValues *pmSourcePetrosianValuesAlloc ();
+bool psMemCheckSourcePetrosianValues(psPtr ptr);
+pmSourceKronValues *pmSourceKronValuesAlloc ();
+bool psMemCheckSourceKronValues(psPtr ptr);
+pmSourceAnnuli *pmSourceAnnuliAlloc ();
+bool psMemCheckSourceAnnuli(psPtr ptr);
+
+/// @}
+# endif /* PM_SOURCE_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitModel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitModel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitModel.c	(revision 22322)
@@ -0,0 +1,233 @@
+/** @file  pmSourceFitModel.c
+ *
+ *  fit single source models to image pixels
+ *
+ *  @author EAM, IfA
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.27 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-09-28 00:38:56 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourceFitModel.h"
+
+// save as static values so they may be set externally
+static psF32 PM_SOURCE_FIT_MODEL_NUM_ITERATIONS = 15;
+static psF32 PM_SOURCE_FIT_MODEL_TOLERANCE = 0.1;
+static psF32 PM_SOURCE_FIT_MODEL_WEIGHT = 1.0;
+static bool  PM_SOURCE_FIT_MODEL_PIX_WEIGHTS = true;
+
+bool pmSourceFitModelInit (float nIter, float tol, float weight, bool poissonErrors)
+{
+
+    PM_SOURCE_FIT_MODEL_NUM_ITERATIONS = nIter;
+    PM_SOURCE_FIT_MODEL_TOLERANCE = tol;
+    PM_SOURCE_FIT_MODEL_WEIGHT = weight;
+    PM_SOURCE_FIT_MODEL_PIX_WEIGHTS = poissonErrors;
+
+    return true;
+}
+
+bool pmSourceFitModel (pmSource *source,
+                       pmModel *model,
+                       pmSourceFitMode mode,
+                       psMaskType maskVal)
+{
+    psTrace("psModules.objects", 5, "---- %s begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(source->maskObj, false);
+    PS_ASSERT_PTR_NON_NULL(source->weight, false);
+
+    psBool fitStatus = true;
+    psBool onPic     = true;
+    psBool rc        = true;
+
+    // maximum number of valid pixels
+    psS32 nPix = source->pixels->numRows * source->pixels->numCols;
+
+    // arrays to hold the data to be fitted
+    psArray *x = psArrayAllocEmpty(nPix);
+    psVector *y = psVectorAllocEmpty(nPix, PS_TYPE_F32);
+    psVector *yErr = psVectorAllocEmpty(nPix, PS_TYPE_F32);
+
+    // fill in the coordinate and value entries
+    nPix = 0;
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            // skip masked points
+            if (source->maskObj->data.U8[i][j] & maskVal) {
+                continue;
+            }
+            // skip zero-weight points
+            if (source->weight->data.F32[i][j] == 0) {
+                continue;
+            }
+            // skip nan values in image
+            if (!isfinite(source->pixels->data.F32[i][j])) {
+                continue;
+            }
+
+            psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+
+            // Convert i/j to image space:
+            coord->data.F32[0] = (psF32) (j + source->pixels->col0);
+            coord->data.F32[1] = (psF32) (i + source->pixels->row0);
+            x->data[nPix] = (psPtr *) coord;
+            y->data.F32[nPix] = source->pixels->data.F32[i][j];
+
+            // psMinimizeLMChi2 takes wt = 1/dY^2.  suggestion from RHL is to use the local sky
+            // as weight to avoid the bias from systematic errors here we would just use the
+            // source sky variance
+            if (PM_SOURCE_FIT_MODEL_PIX_WEIGHTS) {
+                yErr->data.F32[nPix] = 1.0 / source->weight->data.F32[i][j];
+            } else {
+                yErr->data.F32[nPix] = 1.0 / PM_SOURCE_FIT_MODEL_WEIGHT;
+            }
+            nPix++;
+        }
+    }
+    x->n = nPix;
+    y->n = nPix;
+    yErr->n = nPix;
+
+    psVector *params = model->params;
+    psVector *dparams = model->dparams;
+
+    // create the minimization constraints
+    psMinConstraint *constraint = psMinConstraintAlloc();
+    constraint->paramMask = psVectorAlloc (params->n, PS_TYPE_U8);
+    constraint->checkLimits = model->modelLimits;
+
+    // set parameter mask based on fitting mode
+    int nParams = 0;
+    switch (mode) {
+    case PM_SOURCE_FIT_NORM:
+        // NORM-only model fits only source normalization (Io)
+        nParams = 1;
+        psVectorInit (constraint->paramMask, 1);
+        constraint->paramMask->data.U8[PM_PAR_I0] = 0;
+        break;
+    case PM_SOURCE_FIT_PSF:
+        // PSF model only fits x,y,Io
+        nParams = 3;
+        psVectorInit (constraint->paramMask, 1);
+        constraint->paramMask->data.U8[PM_PAR_I0] = 0;
+        constraint->paramMask->data.U8[PM_PAR_XPOS] = 0;
+        constraint->paramMask->data.U8[PM_PAR_YPOS] = 0;
+        break;
+    case PM_SOURCE_FIT_EXT:
+        // EXT model fits all params (except sky)
+        nParams = params->n - 1;
+        psVectorInit (constraint->paramMask, 0);
+        constraint->paramMask->data.U8[PM_PAR_SKY] = 1;
+        break;
+    default:
+        psAbort("invalid fitting mode");
+    }
+    // force the floating parameters to fall within the contraint ranges
+    for (int i = 0; i < params->n; i++) {
+        model->modelLimits (PS_MINIMIZE_PARAM_MIN, i, params->data.F32, NULL);
+        model->modelLimits (PS_MINIMIZE_PARAM_MAX, i, params->data.F32, NULL);
+    }
+
+    if (nPix <  nParams + 1) {
+        psTrace ("psModules.objects", 4, "insufficient valid pixels\n");
+        model->flags |= PM_MODEL_STATUS_BADARGS;
+        psFree (x);
+        psFree (y);
+        psFree (yErr);
+        psFree (constraint);
+        return(false);
+    }
+
+    psMinimization *myMin = psMinimizationAlloc (PM_SOURCE_FIT_MODEL_NUM_ITERATIONS, PM_SOURCE_FIT_MODEL_TOLERANCE);
+
+    psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
+
+    fitStatus = psMinimizeLMChi2(myMin, covar, params, constraint, x, y, yErr, model->modelFunc);
+    for (int i = 0; i < dparams->n; i++) {
+        if (psTraceGetLevel("psModules.objects") >= 4) {
+            fprintf (stderr, "%f ", params->data.F32[i]);
+        }
+        if ((constraint->paramMask != NULL) && constraint->paramMask->data.U8[i])
+            continue;
+        dparams->data.F32[i] = sqrt(covar->data.F32[i][i]);
+    }
+    psTrace ("psModules.objects", 4, "niter: %d, chisq: %f", myMin->iter, myMin->value);
+
+    // save the resulting chisq, nDOF, nIter
+    model->chisq = myMin->value;
+    model->nIter = myMin->iter;
+    model->nDOF  = y->n - nParams;
+    model->flags |= PM_MODEL_STATUS_FITTED;
+    if (!fitStatus) model->flags |= PM_MODEL_STATUS_NONCONVERGE;
+
+    // get the Gauss-Newton distance for fixed model parameters
+    // hold the fitted parameters fixed; mask sky which is not fitted at all
+    if (constraint->paramMask != NULL) {
+        psVector *delta = psVectorAlloc (params->n, PS_TYPE_F32);
+        psVector *altmask = psVectorAlloc (params->n, PS_TYPE_U8);
+        altmask->data.U8[0] = 1;
+        for (int i = 1; i < dparams->n; i++) {
+            altmask->data.U8[i] = (constraint->paramMask->data.U8[i]) ? 0 : 1;
+        }
+        psMinimizeGaussNewtonDelta(delta, params, altmask, x, y, yErr, model->modelFunc);
+
+        for (int i = 0; i < dparams->n; i++) {
+            if (!constraint->paramMask->data.U8[i])
+                continue;
+            // note that delta is the value *subtracted* from the parameter
+            // to get the new guess.  for dparams to represent the direction
+            // of motion, we need to take -delta
+            dparams->data.F32[i] = -delta->data.F32[i];
+        }
+        psFree (delta);
+        psFree (altmask);
+    }
+
+    // models can go insane: reject these
+    onPic &= (params->data.F32[PM_PAR_XPOS] >= source->pixels->col0);
+    onPic &= (params->data.F32[PM_PAR_XPOS] <  source->pixels->col0 + source->pixels->numCols);
+    onPic &= (params->data.F32[PM_PAR_YPOS] >= source->pixels->row0);
+    onPic &= (params->data.F32[PM_PAR_YPOS] <  source->pixels->row0 + source->pixels->numRows);
+    if (!onPic) {
+        model->flags |= PM_MODEL_STATUS_OFFIMAGE;
+    }
+
+    source->mode |= PM_SOURCE_MODE_FITTED;
+
+    psFree(x);
+    psFree(y);
+    psFree(yErr);
+    psFree(myMin);
+    psFree(covar);
+    psFree(constraint);
+
+    rc = (onPic && fitStatus);
+    psTrace("psModules.objects", 5, "---- %s(%d) end ----\n", __func__, rc);
+    return(rc);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitModel.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitModel.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitModel.h	(revision 22322)
@@ -0,0 +1,78 @@
+/* @file  pmSourceFitModel.h
+ *
+ * @author EAM, IfA; GLG, MHPCC
+ *
+ * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-06-20 02:22:26 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_SOURCE_FIT_MODEL_H
+# define PM_SOURCE_FIT_MODEL_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+typedef enum {
+    PM_SOURCE_FIT_NORM,
+    PM_SOURCE_FIT_PSF,
+    PM_SOURCE_FIT_EXT,
+    PM_SOURCE_FIT_PSF_AND_SKY,
+    PM_SOURCE_FIT_EXT_AND_SKY
+} pmSourceFitMode;
+
+bool pmSourceFitModelInit(
+    float nIter,   ///< max number of allowed iterations
+    float tol,      ///< convergence criterion
+    float weight,      ///< use this weight for constant-weight fits
+    bool poissonErrors   // use poisson errors for fits?
+);
+
+/** pmSourceFitModel()
+ *
+ * Fit the requested model to the specified source. The starting guess for the model is given
+ * by the input source.model parameter values. The pixels of interest are specified by the
+ * source.pixels and source.mask entries. This function calls psMinimizeLMChi2() on the image
+ * data. The function returns TRUE on success or FALSE on failure.
+ *
+ */
+bool pmSourceFitModel(
+    pmSource *source,   ///< The input pmSource
+    pmModel *model,   ///< model to be fitted
+    pmSourceFitMode mode,  ///< define parameters to be fitted
+    psMaskType maskVal                  ///< Value to mask
+);
+
+
+// initialize data for a group of object models
+bool pmSourceFitSetInit (pmModelType type);
+
+// clear data for a group of object models
+void pmSourceFitSetClear (void);
+
+// function used to set limits for a group of models
+bool pmSourceFitSet_CheckLimits (psMinConstraintMode mode, int nParam, float *params, float *betas);
+
+// function used to fit a group of object models
+psF32 pmSourceFitSet_Function(psVector *deriv,
+                              const psVector *params,
+                              const psVector *x);
+
+/** pmSourceFitSet()
+ *
+ * Fit the requested model to the specified source. The starting guess for the model is given
+ * by the input source.model parameter values. The pixels of interest are specified by the
+ * source.pixels and source.mask entries. This function calls psMinimizeLMChi2() on the image
+ * data. The function returns TRUE on success or FALSE on failure.
+ *
+ */
+bool pmSourceFitSet(
+    pmSource *source,   ///< The input pmSource
+    psArray *modelSet,   ///< model to be fitted
+    pmSourceFitMode mode,  ///< define parameters to be fitted
+    psMaskType maskVal                  ///< Vale to mask
+
+);
+
+/// @}
+# endif /* PM_SOURCE_FIT_MODEL_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitSet.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitSet.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitSet.c	(revision 22322)
@@ -0,0 +1,495 @@
+/** @file  pmSourceFitModel.c
+ *
+ *  fit single source models to image pixels
+ *
+ *  @author EAM, IfA
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-01-02 20:39:04 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourceFitModel.h"
+#include "pmSourceFitSet.h"
+
+// save as static values so they may be set externally
+static psF32 PM_SOURCE_FIT_MODEL_NUM_ITERATIONS = 15;
+static psF32 PM_SOURCE_FIT_MODEL_TOLERANCE = 0.1;
+static psF32 PM_SOURCE_FIT_MODEL_WEIGHT = 1.0;
+static bool  PM_SOURCE_FIT_MODEL_PIX_WEIGHTS = true;
+
+/********************* Source Model Set Functions ***************************/
+
+static pmSourceFitSetData *thisSet = NULL;
+
+static void pmSourceFitSetDataFree (pmSourceFitSetData *set) {
+    if (!set) return;
+
+    psFree (set->modelSet);
+    psFree (set->paramSet);
+    psFree (set->derivSet);
+    return;
+}
+
+pmSourceFitSetData *pmSourceFitSetDataAlloc (psArray *modelSet) {
+    PS_ASSERT_PTR_NON_NULL(modelSet, NULL);
+
+    pmSourceFitSetData *set = (pmSourceFitSetData *) psAlloc(sizeof(pmSourceFitSetData));
+    psMemSetDeallocator(set, (psFreeFunc) pmSourceFitSetDataFree);
+
+    set->modelSet = psMemIncrRefCounter (modelSet);
+    set->paramSet = psArrayAlloc (modelSet->n);
+    set->derivSet = psArrayAlloc (modelSet->n);
+    set->nParamSet = 0;
+
+    for (int i = 0; i < modelSet->n; i++) {
+        pmModel *model = modelSet->data[i];
+
+        int nParams = pmModelClassParameterCount (model->type);
+
+        set->paramSet->data[i] = psVectorCopy (NULL, model->params, PS_DATA_F32);
+        set->derivSet->data[i] = psVectorAlloc (nParams, PS_DATA_F32);
+        psVectorInit (set->derivSet->data[i], 0.0);
+
+        set->nParamSet += nParams;
+    }
+
+    return set;
+}
+
+bool psMemCheckSourceFitSetData(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceFitSetDataFree);
+}
+
+
+// this function is called with the full set of parameters and the beta values in a single vector
+bool pmSourceFitSetCheckLimits (psMinConstraintMode mode, int nParam, float *params,
+                                float *betas)
+{
+    PS_ASSERT_PTR_NON_NULL(thisSet, false);
+
+    // nParam is the parameter in the full sequence.  determine which single model this comes from
+    int nModel = -1;
+    int nParamOne = -1;
+    int nParamBase = 0;
+    for (int i = 0; i < thisSet->modelSet->n; i++) {
+        psVector *param = thisSet->paramSet->data[i];
+        if ((nParamBase <= nParam) && (nParam < nParamBase + param->n)) {
+            nModel = i;
+            nParamOne = nParam - nParamBase;
+            break;
+        }
+        nParamBase += param->n;
+    }
+    assert (nModel > -1);
+
+    pmModel *model = thisSet->modelSet->data[nModel];
+
+    // pass the single model function a pointer to the start of that model's sequence
+    float *paramOne = params + nParamBase;
+    float *betaOne = betas + nParamBase;
+    bool status = model->modelLimits (mode, nParamOne, paramOne, betaOne);
+    return status;
+}
+
+// merge parameters from FitSet models into single param and deriv vectors
+bool pmSourceFitSetJoin (psVector *deriv, psVector *param, pmSourceFitSetData *set)
+{
+    PS_ASSERT_PTR_NON_NULL(set, false);
+    PS_ASSERT_PTR_NON_NULL(set->paramSet, false);
+    PS_ASSERT_PTR_NON_NULL(set->derivSet, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(set->paramSet, set->derivSet, false);
+    int n = 0;
+    int sum = 0;
+    for (int i = 0; i < set->paramSet->n; i++) {
+        sum+= set->paramSet->n;
+    }
+
+    // Must assert that the deriv and param psVectors are large enough, or else
+    // a seg fault occurs.
+    // XXX: Put the correct error call in here:
+    if (0) {
+        if (deriv->n < sum || param->n < sum) {
+            PS_ASSERT_PTR_NON_NULL(0, false);
+        }
+    }
+
+    for (int i = 0; i < set->paramSet->n; i++) {
+
+        psVector *paramOne = set->paramSet->data[i];
+        psVector *derivOne = set->derivSet->data[i];
+
+	// one or the other (param or deriv) must be set
+	assert ((deriv != NULL) || (param != NULL));
+
+	// if we are setting derive, derivOne and paramOne must be same length
+        assert ((deriv == NULL) || (paramOne->n == derivOne->n));
+	
+        for (int j = 0; j < paramOne->n; j++, n++) {
+	    if (param) {
+		param->data.F32[n] = paramOne->data.F32[j];
+	    }
+            if (deriv) {
+                deriv->data.F32[n] = derivOne->data.F32[j];
+            }
+        }
+    }
+    return true;
+}
+
+// distribute parameters from single param and deriv vectors into FitSet models
+bool pmSourceFitSetSplit (pmSourceFitSetData *set, const psVector *deriv, const psVector *param) 
+{
+    PS_ASSERT_VECTOR_NON_NULL(param, false);
+    PS_ASSERT_PTR_NON_NULL(set, false);
+    PS_ASSERT_PTR_NON_NULL(set->paramSet, false);
+    PS_ASSERT_PTR_NON_NULL(set->derivSet, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(set->paramSet, set->derivSet, false);
+
+    int n = 0;
+    for (int i = 0; i < set->paramSet->n; i++) {
+
+        psVector *paramOne = set->paramSet->data[i];
+        psVector *derivOne = set->derivSet->data[i];
+        assert ((deriv == NULL) || (paramOne->n == derivOne->n));
+
+        for (int j = 0; j < paramOne->n; j++, n++) {
+            paramOne->data.F32[j] = param->data.F32[n];
+            if (deriv) {
+                derivOne->data.F32[j] = deriv->data.F32[n];
+            }
+        }
+    }
+    return true;
+}
+
+bool pmSourceFitSetValues (pmSourceFitSetData *set, const psVector *dparam, 
+                           const psVector *param, pmSource *source,
+                           psMinimization *myMin, int nPix, bool fitStatus)
+{
+    PS_ASSERT_PTR_NON_NULL(set, false);
+    PS_ASSERT_PTR_NON_NULL(set->paramSet, false);
+    PS_ASSERT_VECTOR_NON_NULL(dparam, false);
+    PS_ASSERT_VECTOR_NON_NULL(param, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(myMin, false);
+
+    bool onPic = true;
+
+    int n = 0;
+    for (int i = 0; i < set->paramSet->n; i++) {
+
+        pmModel *model = set->modelSet->data[i];
+
+        for (int j = 0; j < model->params->n; j++, n++) {
+            if (psTraceGetLevel("psModules.objects") >= 4) {
+                fprintf (stderr, "%f ", param->data.F32[n]);
+            }
+            model->params->data.F32[j] = param->data.F32[n];
+            model->dparams->data.F32[j] = dparam->data.F32[n];
+        }
+        psTrace ("psModules.objects", 4, " src %d", i);
+
+        // save the resulting chisq, nDOF, nIter
+        // these are not unique for any one source
+        model->chisq = myMin->value;
+        model->nIter = myMin->iter;
+        model->nDOF  = nPix - model->params->n;
+
+        // set the model success or failure status
+        model->flags |= PM_MODEL_STATUS_FITTED;
+        if (!fitStatus) model->flags |= PM_MODEL_STATUS_NONCONVERGE;
+
+        // models can go insane: reject these
+        onPic &= (model->params->data.F32[PM_PAR_XPOS] >= source->pixels->col0);
+        onPic &= (model->params->data.F32[PM_PAR_XPOS] <  source->pixels->col0 + source->pixels->numCols);
+        onPic &= (model->params->data.F32[PM_PAR_XPOS] >= source->pixels->row0);
+        onPic &= (model->params->data.F32[PM_PAR_XPOS] <  source->pixels->row0 + source->pixels->numRows);
+        if (!onPic) model->flags |= PM_MODEL_STATUS_OFFIMAGE;
+    }
+    return true;
+}
+
+// generic psMinLMM-style function for fitting: split the parameters across the models, call
+// each model function one-at-a-time, the join the derivatives for on-going evaluation
+psF32 pmSourceFitSetFunction(psVector *deriv, const psVector *param, const psVector *x)
+{
+    PS_ASSERT_PTR_NON_NULL(thisSet, NAN);
+    float chisqSum = 0.0;
+    float chisqOne = 0.0;
+    pmSourceFitSetSplit (thisSet, deriv, param);
+
+    for (int i = 0; i < thisSet->modelSet->n; i++) {
+
+        pmModel *model = thisSet->modelSet->data[i];
+
+        psVector *paramOne = thisSet->paramSet->data[i];
+        psVector *derivOne = thisSet->derivSet->data[i];
+
+        chisqOne = model->modelFunc (derivOne, paramOne, x);
+        chisqSum += chisqOne;
+    }
+    pmSourceFitSetJoin (deriv, NULL, thisSet);
+
+    return (chisqSum);
+}
+
+// XXX allow the mode to be a function of the object (eg, S/N)
+bool pmSourceFitSetMasks (psMinConstraint *constraint, pmSourceFitSetData *set,
+                          pmSourceFitMode mode)
+{
+    PS_ASSERT_PTR_NON_NULL(set, false);
+    PS_ASSERT_PTR_NON_NULL(constraint, false);
+
+    // unmask everyone
+    psVectorInit (constraint->paramMask, 0);
+
+    int n = 0;
+    for (int i = 0; i < set->paramSet->n; i++) {
+        psVector *paramOne = set->paramSet->data[i];
+
+        switch (mode) {
+          case PM_SOURCE_FIT_NORM:
+            // mask all but Xo,Yo,Io
+            for (int j = 0; j < paramOne->n; j++) {
+                if (j == PM_PAR_I0) continue;
+                constraint->paramMask->data.U8[n + j] = 1;
+            }
+            break;
+          case PM_SOURCE_FIT_PSF:
+            // mask all but Xo,Yo,Io
+            for (int j = 0; j < paramOne->n; j++) {
+                if (j == PM_PAR_XPOS) continue;
+                if (j == PM_PAR_YPOS) continue;
+                if (j == PM_PAR_I0) continue;
+                constraint->paramMask->data.U8[n + j] = 1;
+            }
+            break;
+          case PM_SOURCE_FIT_EXT:
+            // EXT model fits all params (except sky)
+            constraint->paramMask->data.U8[n + PM_PAR_SKY] = 1;
+            break;
+          default:
+            psAbort("invalid fitting mode");
+        }
+        n += paramOne->n;
+    }
+    return true;
+}
+
+bool pmSourcePrintModelSet (FILE *file, psArray *modelSet) {
+
+    for (int i = 0; i < modelSet->n; i++) {
+	pmModel *model = modelSet->data[i];
+	int nParams = pmModelClassParameterCount (model->type);
+	for (int j = 0; j < nParams; j++) {
+	    fprintf (file, "%d %d  : %f %f\n", i, j, model->params->data.F32[j], model->dparams->data.F32[j]);
+	}
+    }
+    return true;
+}
+
+bool pmSourceFitSetPrint (FILE *file, pmSourceFitSetData *set) {
+
+    for (int i = 0; i < set->paramSet->n; i++) {
+        psVector *paramOne = set->paramSet->data[i];
+        psVector *derivOne = set->derivSet->data[i];
+        for (int j = 0; j < paramOne->n; j++) {
+	    fprintf (file, "%d %d  : %f %f\n", i, j, paramOne->data.F32[j], derivOne->data.F32[j]);
+        }
+    }
+    return true;
+}
+
+bool pmSourceFitSet (pmSource *source,
+                     psArray *modelSet,
+                     pmSourceFitMode mode,
+                     psMaskType maskVal)
+{
+    psTrace("psModules.objects", 3, "---- %s begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(source->maskObj, false);
+    PS_ASSERT_PTR_NON_NULL(source->weight, false);
+
+    bool fitStatus = true;
+    bool onPic     = true;
+
+    // maximum number of valid pixels
+    int nPix = source->pixels->numRows * source->pixels->numCols;
+
+    // construct the coordinate and value entries
+    psArray *x = psArrayAllocEmpty(nPix);
+    psVector *y = psVectorAllocEmpty(nPix, PS_TYPE_F32);
+    psVector *yErr = psVectorAllocEmpty(nPix, PS_TYPE_F32);
+
+    // fill in the coordinate and value entries
+    nPix = 0;
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            // skip masked points
+            if (source->maskObj->data.U8[i][j] & maskVal) {
+                continue;
+            }
+            // skip zero-weight points
+            if (source->weight->data.F32[i][j] == 0) {
+                continue;
+            }
+            // skip nan values in image
+            if (!isfinite(source->pixels->data.F32[i][j])) {
+                continue;
+            }
+
+            psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+
+            // Convert i/j to image space:
+            coord->data.F32[0] = (psF32) (j + source->pixels->col0);
+            coord->data.F32[1] = (psF32) (i + source->pixels->row0);
+            x->data[nPix] = (psPtr *) coord;
+            y->data.F32[nPix] = source->pixels->data.F32[i][j];
+
+            // psMinimizeLMChi2 takes wt = 1/dY^2.  suggestion from RHL is to use the local sky
+            // as weight to avoid the bias from systematic errors here we would just use the
+            // source sky variance
+            if (PM_SOURCE_FIT_MODEL_PIX_WEIGHTS) {
+                yErr->data.F32[nPix] = 1.0 / source->weight->data.F32[i][j];
+            } else {
+                yErr->data.F32[nPix] = 1.0 / PM_SOURCE_FIT_MODEL_WEIGHT;
+            }
+            nPix++;
+        }
+    }
+    x->n = nPix;
+    y->n = nPix;
+    yErr->n = nPix;
+
+    // create the FitSet and set the initial parameter guesses
+    thisSet = pmSourceFitSetDataAlloc (modelSet);
+
+    // define param and deriv vectors for complete set of parameters
+    psVector *params = psVectorAlloc (thisSet->nParamSet, PS_TYPE_F32);
+
+    // set the param and deriv vectors based on the curent values
+    pmSourceFitSetJoin (NULL, params, thisSet);
+
+    // create the minimization constraints
+    psMinConstraint *constraint = psMinConstraintAlloc();
+    constraint->paramMask = psVectorAlloc (thisSet->nParamSet, PS_TYPE_U8);
+    constraint->checkLimits = pmSourceFitSetCheckLimits;
+
+    pmSourceFitSetMasks (constraint, thisSet, mode);
+
+    // force the floating parameters to fall within the contraint ranges
+    for (int i = 0; i < params->n; i++) {
+        pmSourceFitSetCheckLimits (PS_MINIMIZE_PARAM_MIN, i, params->data.F32, NULL);
+        pmSourceFitSetCheckLimits (PS_MINIMIZE_PARAM_MAX, i, params->data.F32, NULL);
+    }
+
+    if (psTraceGetLevel("psModules.objects") >= 5) {
+        for (int i = 0; i < params->n; i++) {
+            fprintf (stderr, "%d %f %d\n", i, params->data.F32[i], constraint->paramMask->data.U8[i]);
+        }
+    }
+
+    if (nPix <  thisSet->nParamSet + 1) {
+        psTrace (__func__, 4, "insufficient valid pixels\n");
+        psTrace("psModules.objects", 3, "---- %s() end : fail pixels ----\n", __func__);
+        for (int i = 0; i < modelSet->n; i++) {
+            pmModel *model = modelSet->data[i];
+            model->flags |= PM_MODEL_STATUS_BADARGS;
+        }
+        psFree (x);
+        psFree (y);
+        psFree (yErr);
+        psFree (params);
+        psFree(constraint);
+        psFree (thisSet);
+        thisSet = NULL;
+        return(false);
+    }
+
+    psMinimization *myMin = psMinimizationAlloc (PM_SOURCE_FIT_MODEL_NUM_ITERATIONS, PM_SOURCE_FIT_MODEL_TOLERANCE);
+
+    psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
+
+    fitStatus = psMinimizeLMChi2(myMin, covar, params, constraint, x, y, yErr, pmSourceFitSetFunction);
+    if (!fitStatus) {
+        psTrace("psModules.objects", 4, "Failed to fit model (%ld components)\n", modelSet->n);
+    }
+
+    // parameter errors from the covariance matrix
+    psVector *dparams = psVectorAlloc (thisSet->nParamSet, PS_TYPE_F32);
+    for (int i = 0; i < dparams->n; i++) {
+        if ((constraint->paramMask != NULL) && constraint->paramMask->data.U8[i])
+            continue;
+        dparams->data.F32[i] = sqrt(covar->data.F32[i][i]);
+    }
+
+    // get the Gauss-Newton distance for fixed model parameters
+    if (constraint->paramMask != NULL) {
+        psVector *delta = psVectorAlloc (params->n, PS_TYPE_F32);
+        psVector *altmask = psVectorAlloc (params->n, PS_TYPE_U8);
+        altmask->data.U8[0] = 1;
+        for (int i = 1; i < dparams->n; i++) {
+            altmask->data.U8[i] = (constraint->paramMask->data.U8[i]) ? 0 : 1;
+        }
+        psMinimizeGaussNewtonDelta(delta, params, altmask, x, y, yErr, pmSourceFitSetFunction);
+
+        for (int i = 0; i < dparams->n; i++) {
+            if (!constraint->paramMask->data.U8[i])
+                continue;
+            // note that delta is the value *subtracted* from the parameter
+            // to get the new guess.  for dparams to represent the direction
+            // of motion, we need to take -delta
+            dparams->data.F32[i] = -delta->data.F32[i];
+        }
+        psFree (delta);
+        psFree (altmask);
+    }
+
+    pmSourceFitSetValues (thisSet, dparams, params, source, myMin, y->n, fitStatus);
+    psTrace ("psModules.objects", 5, "onPic: %d, fitStatus: %d, nIter: %d, chisq: %f, nPix: %ld\n", onPic, fitStatus, myMin->iter, myMin->value, y->n);
+
+    source->mode |= PM_SOURCE_MODE_FITTED;
+
+    psFree(x);
+    psFree(y);
+    psFree(yErr);
+    psFree(myMin);
+    psFree(covar);
+    psFree(constraint);
+    psFree(params);
+    psFree(dparams);
+    psFree(thisSet);
+
+    thisSet = NULL;
+
+    bool rc = (onPic && fitStatus);
+    psTrace("psModules.objects", 5, "---- %s end (%d) ----\n", __func__, rc);
+    return(rc);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitSet.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitSet.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceFitSet.h	(revision 22322)
@@ -0,0 +1,57 @@
+/* @file  pmSourceFitSet.h
+ *
+ * @author EAM, IfA; GLG, MHPCC
+ *
+ * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-10 01:09:20 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_SOURCE_FIT_SET_H
+# define PM_SOURCE_FIT_SET_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+typedef struct {
+    psArray *modelSet;
+    psArray *paramSet;
+    psArray *derivSet;
+    int nParamSet;
+} pmSourceFitSetData;
+
+// initialize data for a group of object models
+pmSourceFitSetData *pmSourceFitSetDataAlloc (psArray *modelSet);
+bool psMemCheckSourceFitSetData(psPtr ptr);
+
+// function used to set limits for a group of models
+bool pmSourceFitSetCheckLimits (psMinConstraintMode mode, int nParam, float *params, float *betas);
+
+bool pmSourceFitSetJoin (psVector *deriv, psVector *param, pmSourceFitSetData *set);
+bool pmSourceFitSetSplit (pmSourceFitSetData *set, const psVector *deriv, const psVector *param);
+bool pmSourceFitSetValues (pmSourceFitSetData *set, const psVector *dparam, const psVector *param, pmSource *source, psMinimization *myMin, int nPix, bool fitStatus);
+
+psF32 pmSourceFitSetFunction(psVector *deriv, const psVector *param, const psVector *x);
+bool pmSourceFitSetMasks (psMinConstraint *constraint, pmSourceFitSetData *set, pmSourceFitMode mode);
+
+/** pmSourceFitSet()
+ *
+ * Fit the requested model to the specified source. The starting guess for the model is given
+ * by the input source.model parameter values. The pixels of interest are specified by the
+ * source.pixels and source.mask entries. This function calls psMinimizeLMChi2() on the image
+ * data. The function returns TRUE on success or FALSE on failure.
+ *
+ */
+bool pmSourceFitSet(
+    pmSource *source,   ///< The input pmSource
+    psArray *modelSet,   ///< model to be fitted
+    pmSourceFitMode mode,  ///< define parameters to be fitted
+    psMaskType maskVal                  ///< Vale to mask
+
+);
+
+bool pmSourcePrintModelSet (FILE *file, psArray *modelSet);
+bool pmSourceFitSetPrint (FILE *file, pmSourceFitSetData *set);
+
+/// @}
+# endif /* PM_SOURCE_FIT_MODEL_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO.c	(revision 22322)
@@ -0,0 +1,1029 @@
+/** @file  pmSourceIO.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.65 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-09-02 19:06:17 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>            /* for strn?casecmp */
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmFPAfileFitsIO.h"
+#include "pmConcepts.h"
+
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourceIO.h"
+
+#define BLANK_HEADERS "BLANK.HEADERS"   // Name of metadata in camera configuration containing header names
+                                        // for putting values into a blank PHU
+
+// translations between psphot object types and dophot object types
+int pmSourceGetDophotType (pmSource *source)
+{
+    PS_ASSERT_PTR_NON_NULL(source, -1);
+
+    switch (source->type) {
+
+      case PM_SOURCE_TYPE_DEFECT:
+      case PM_SOURCE_TYPE_SATURATED:
+        return (8);
+
+      case PM_SOURCE_TYPE_STAR:
+        if (source->mode & PM_SOURCE_MODE_SATSTAR)
+            return (10);
+        if (source->mode & PM_SOURCE_MODE_POOR)
+            return (7);
+        if (source->mode & PM_SOURCE_MODE_FAIL)
+            return (4);
+        return (1);
+
+      case PM_SOURCE_TYPE_EXTENDED:
+        return (2);
+
+      default:
+        return (0);
+    }
+    return (0);
+}
+
+// translations between psphot object types and dophot object types
+bool pmSourceSetDophotType (pmSource *source, int type)
+{
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    if (type == 4) {
+        source->mode |= PM_SOURCE_MODE_FAIL;
+    }
+    if (type == 7) {
+        source->mode |= PM_SOURCE_MODE_POOR;
+    }
+    if (type == 10) {
+        source->mode |= PM_SOURCE_MODE_SATSTAR;
+    }
+
+    switch (type) {
+      case 1:
+      case 4:
+      case 7:
+      case 10:
+        source->type = PM_SOURCE_TYPE_STAR;
+        return true;
+      case 2:
+        source->type = PM_SOURCE_TYPE_EXTENDED;
+        return true;
+      case 8:
+        source->type = PM_SOURCE_TYPE_DEFECT;
+        return true;
+      default:
+        return false;
+    }
+    return false;
+}
+
+// Given a FITS file pointer, write the table of object data
+bool pmFPAviewWriteObjects (const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
+
+    if (view->chip == -1) {
+        if (!pmFPAWriteObjects (fpa, view, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write objects from fpa");
+            psFree(fpa);
+            return false;
+        }
+        psFree(fpa);
+        return true;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        psError(PS_ERR_UNKNOWN, false, "Writing chip == %d (>= chips->n == %ld)", view->chip, fpa->chips->n);
+        psFree(fpa);
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        if (!pmChipWriteObjects (chip, view, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write objects from chip");
+            psFree(fpa);
+            return false;
+        }
+        psFree(fpa);
+        return true;
+    }
+
+    if (view->cell >= chip->cells->n) {
+        psError(PS_ERR_UNKNOWN, false, "Writing cell == %d (>= cells->n == %ld)",
+                view->cell, chip->cells->n);
+        psFree(fpa);
+        return false;
+    }
+    pmCell *cell = chip->cells->data[view->cell];
+
+    if (view->readout == -1) {
+        if (!pmCellWriteObjects (cell, view, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write objects from cell");
+            psFree(fpa);
+            return false;
+        }
+        psFree(fpa);
+        return true;
+    }
+
+    if (view->readout >= cell->readouts->n) {
+        psError(PS_ERR_UNKNOWN, false, "Writing readout == %d (>= readouts->n == %ld)",
+                view->readout, cell->readouts->n);
+        psFree(fpa);
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[view->readout];
+
+    if (!pmReadoutWriteObjects (readout, view, file, config)) {
+        psError(PS_ERR_IO, false, "Failed to write objects from readout %d", view->readout);
+        psFree(fpa);
+        return false;
+    }
+
+    psFree(fpa);
+    return true;
+}
+
+// read in all chip-level Objects files for this FPA
+bool pmFPAWriteObjects (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fpa->chips, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc (view->nRows);
+    *thisView = *view;
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        thisView->chip = i;
+        if (!pmChipWriteObjects (chip, thisView, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write %dth chip", i);
+            psFree (thisView);
+            return false;
+        }
+    }
+    psFree (thisView);
+    return true;
+}
+
+// read in all cell-level Objects files for this chip
+bool pmChipWriteObjects (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(chip->cells, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc (view->nRows);
+    *thisView = *view;
+
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i];
+        thisView->cell = i;
+        if (!pmCellWriteObjects (cell, thisView, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write %dth cell", i);
+            psFree (thisView);
+            return false;
+        }
+    }
+    psFree (thisView);
+    return true;
+}
+
+// read in all readout-level Objects files for this cell
+bool pmCellWriteObjects (pmCell *cell, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(cell->readouts, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc (view->nRows);
+    *thisView = *view;
+
+    for (int i = 0; i < cell->readouts->n; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        thisView->readout = i;
+        if (!pmReadoutWriteObjects (readout, thisView, file, config)) {
+            psError(PS_ERR_IO, false, "Failed to write %dth readout", i);
+            psFree (thisView);
+            return false;
+        }
+    }
+    psFree (thisView);
+    return true;
+}
+
+// write out all readout-level Objects files for this cell
+bool pmReadoutWriteObjects (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    pmCell *cell = readout->parent;
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    pmChip *chip = cell->parent;
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    pmFPA *fpa = chip->parent;
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    bool status;
+    pmHDU *hdu;
+    psMetadata *updates;
+    psMetadata *outhead;
+
+    char *exttype  = NULL;
+    char *dataname = NULL;
+    char *xsrcname = NULL;
+    char *xfitname = NULL;
+    char *headname = NULL;
+
+    // if sources is NULL, write out an empty table
+    // input / output sources are stored on the readout->analysis as "PSPHOT.SOURCES" -- a better name might be something like PM_SOURCE_DATA
+    psArray *sources = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.SOURCES");
+    if (!sources) {
+        sources = psArrayAlloc(0);
+        psMetadataAddArray(readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_META_REPLACE, "Blank array of sources", sources);
+        psFree(sources); // Held onto by the metadata, so we can continue to use
+    }
+
+    switch (file->type) {
+      case PM_FPA_FILE_RAW:
+        pmSourcesWriteRAW (sources, file->filename);
+        break;
+
+      case PM_FPA_FILE_OBJ:
+        pmSourcesWriteOBJ (sources, file->filename);
+        break;
+
+      case PM_FPA_FILE_SX:
+        pmSourcesWriteSX (sources, file->filename);
+        break;
+
+      case PM_FPA_FILE_CMP:
+        // a SPLIT format : only one header and object table per file
+        hdu = pmFPAviewThisHDU (view, fpa);
+        if (!hdu) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find HDU to write sources.");
+            return false;
+        }
+
+        // copy the header to an output header, add the output header data
+        outhead = psMetadataCopy (NULL, hdu->header);
+
+        // copy over the entries saved by PSPHOT
+        updates = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.HEADER");
+        if (updates) {
+            psMetadataCopy (outhead, updates);
+        }
+
+        // copy over the entries saved by PSASTRO
+        updates = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.HEADER");
+        if (updates) {
+            psMetadataCopy (outhead, updates);
+        }
+
+        bool status = pmSourcesWriteCMP (sources, file->filename, outhead);
+        psFree (outhead);
+
+        if (!status) {
+            psError(PS_ERR_IO, false, "Failed to write CMP file\n");
+            return false;
+        }
+        break;
+
+      case PM_FPA_FILE_CMF:
+        // write a header? (only if this is the first readout for cell)
+        //   note that the file->header is set to track the last hdu->header written
+        // write the data? (always?)
+
+        // get the current header
+        hdu = pmFPAviewThisHDU (view, fpa);
+        if (!hdu) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find HDU to write sources.");
+            return false;
+        }
+
+        // determine the output table format
+        psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, "PSPHOT");
+        if (!status) {
+          psError(PS_ERR_UNKNOWN, true, "missing recipe PSPHOT in config data");
+          return false;
+        }
+
+        // if this is not TRUE, the output files only contain the psf measurements.
+        bool XSRC_OUTPUT = psMetadataLookupBool(&status, recipe, "EXTENDED_SOURCE_ANALYSIS");
+        bool XFIT_OUTPUT = psMetadataLookupBool(&status, recipe, "EXTENDED_SOURCE_FITS");
+
+        // define the EXTNAME values for the different data segments:
+        {
+            // lookup the EXTNAME values used for table data and image header segments
+            char *rule = NULL;
+
+            // Menu of EXTNAME rules
+            psMetadata *menu = psMetadataLookupMetadata(&status, file->camera, "EXTNAME.RULES");
+            if (!menu) {
+                psError(PS_ERR_UNKNOWN, true, "missing EXTNAME.RULES in camera.config");
+                return false;
+            }
+
+            // EXTNAME for image header
+            rule = psMetadataLookupStr(&status, menu, "CMF.HEAD");
+            if (!rule) {
+                psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.HEAD in EXTNAME.RULES in camera.config");
+                return false;
+            }
+            headname = pmFPAfileNameFromRule (rule, file, view);
+
+            // EXTNAME for table data
+            rule = psMetadataLookupStr(&status, menu, "CMF.DATA");
+            if (!rule) {
+                psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.DATA in EXTNAME.RULES in camera.config");
+                return false;
+            }
+            dataname = pmFPAfileNameFromRule (rule, file, view);
+
+            if (XSRC_OUTPUT) {
+              // EXTNAME for extended source data table
+              rule = psMetadataLookupStr(&status, menu, "CMF.XSRC");
+              if (!rule) {
+                psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.XSRC in EXTNAME.RULES in camera.config");
+                return false;
+              }
+              xsrcname = pmFPAfileNameFromRule (rule, file, view);
+            }
+            if (XFIT_OUTPUT) {
+              // EXTNAME for extended source data table
+              rule = psMetadataLookupStr(&status, menu, "CMF.XFIT");
+              if (!rule) {
+                psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.XFIT in EXTNAME.RULES in camera.config");
+                return false;
+              }
+              xfitname = pmFPAfileNameFromRule (rule, file, view);
+            }
+        }
+
+        // write out the IMAGE header segment (only for the first readout of the cell)
+        {
+            // this header block is new, write it to disk
+            if (hdu->header != file->header) {
+                // add EXTNAME, EXTHEAD, EXTTYPE to header
+                psMetadataAddStr (hdu->header, PS_LIST_TAIL, "EXTDATA", PS_META_REPLACE, "name of table extension", dataname);
+                psMetadataAddStr (hdu->header, PS_LIST_TAIL, "EXTTYPE", PS_META_REPLACE, "extension type", "IMAGE");
+                if (!file->wrote_phu) {
+                    // this hdu->header acts as the PHU: set EXTEND to be true
+                    psMetadataAddBool (hdu->header, PS_LIST_TAIL, "EXTEND", PS_META_REPLACE, "this file has extensions", true);
+                    file->wrote_phu = true;
+                }
+
+                // save psphot and psastro metadata in the image and table headers
+                updates = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.HEADER");
+                if (updates) {
+                    psMetadataCopy (hdu->header, updates);
+                }
+                updates = psMetadataLookupPtr (&status, readout->analysis, "PSASTRO.HEADER");
+                if (updates) {
+                    psMetadataCopy (hdu->header, updates);
+                }
+
+                pmConfigConformHeader(hdu->header, file->format);
+
+		// psFitsWriteBlank strips out the NAXISn keywords, forcing CFITSIO to take care of them
+		// save NAXIS1,NAXIS2 as IMNAXIS1,IMNAXIS2
+		int Nx = psMetadataLookupS32 (&status, hdu->header, "IMNAXIS1");
+		if (!status) {
+		    Nx = psMetadataLookupS32 (&status, hdu->header, "NAXIS1");
+		    if (status) {
+			psMetadataAddS32 (hdu->header, PS_LIST_TAIL, "IMNAXIS1", PS_META_REPLACE, "original image size (x-dir)", Nx);
+		    } else {
+			fprintf (stderr, "header is missing NAXIS1!");
+		    }
+		}
+
+		int Ny = psMetadataLookupS32 (&status, hdu->header, "IMNAXIS2");
+		if (!status) {
+		    Ny = psMetadataLookupS32 (&status, hdu->header, "NAXIS2");
+		    if (status) {
+			psMetadataAddS32 (hdu->header, PS_LIST_TAIL, "IMNAXIS2", PS_META_REPLACE, "original image size (y-dir)", Ny);
+		    } else {
+			fprintf (stderr, "header is missing NAXIS2!");
+		    }
+		}
+
+                psFitsWriteBlank (file->fits, hdu->header, headname);
+                psTrace ("pmFPAfile", 5, "wrote ext head %s (type: %d)\n", file->filename, file->type);
+                file->header = hdu->header;
+            }
+        }
+
+        // write out the TABLE data segment
+        {
+            // create a header to hold the output data
+            outhead = psMetadataAlloc ();
+
+            exttype = psMemIncrRefCounter (psMetadataLookupStr(&status, recipe, "OUTPUT.FORMAT"));
+            if (!exttype) {
+                exttype = psStringCopy ("SMPDATA");
+            }
+
+            // write the links to the image header
+            psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTHEAD", PS_META_REPLACE, "name of image extension w/", headname);
+            psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTTYPE", PS_META_REPLACE, "extension type", exttype);
+            psFree (exttype);
+
+            // if we request XSRC output, add the XSRC name to this header
+            if (xsrcname) {
+              psMetadataAddStr (outhead, PS_LIST_TAIL, "XSRCNAME", PS_META_REPLACE, "name of XSRC table extension", xsrcname);
+            }
+            if (xfitname) {
+              psMetadataAddStr (outhead, PS_LIST_TAIL, "XFITNAME", PS_META_REPLACE, "name of XFIT table extension", xfitname);
+            }
+
+            // XXX these are case-sensitive since the EXTYPE is case-sensitive
+            status = false;
+            if (!strcmp (exttype, "SMPDATA")) {
+                status = pmSourcesWrite_SMPDATA (file->fits, sources, file->header, outhead, dataname);
+            }
+            if (!strcmp (exttype, "PS1_DEV_0")) {
+                status = pmSourcesWrite_PS1_DEV_0 (file->fits, sources, file->header, outhead, dataname);
+            }
+            if (!strcmp (exttype, "PS1_DEV_1")) {
+                status = pmSourcesWrite_PS1_DEV_1 (file->fits, sources, file->header, outhead, dataname, xsrcname);
+            }
+            if (xsrcname) {
+              if (!strcmp (exttype, "PS1_DEV_1")) {
+                status = pmSourcesWrite_PS1_DEV_1_XSRC (file->fits, sources, xsrcname, recipe);
+              }
+            }
+            if (xfitname) {
+              if (!strcmp (exttype, "PS1_DEV_1")) {
+                status = pmSourcesWrite_PS1_DEV_1_XFIT (file->fits, sources, xfitname);
+              }
+            }
+            if (!status) {
+                psError(PS_ERR_IO, false, "writing CMF data to %s with format %s\n", file->filename, exttype);
+                psFree (headname);
+                psFree (dataname);
+                psFree (xsrcname);
+                psFree (xfitname);
+                psFree (outhead);
+                return false;
+            }
+        }
+
+        psTrace ("pmFPAfile", 5, "wrote ext data %s (type: %d)\n", file->filename, file->type);
+
+        psFree (headname);
+        psFree (dataname);
+        psFree (xsrcname);
+        psFree (xfitname);
+        psFree (outhead);
+        break;
+
+      default:
+        fprintf (stderr, "warning: type mismatch\n");
+        break;
+    }
+    return true;
+}
+// a MEF CMF file has: PHU, CELL-HEAD, TABLE, CELL-HEAD, TABLE, TABLE, TABLE...
+
+// if this file needs to have a PHU written out, write one
+bool pmSource_CMF_WritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    bool status;
+
+    // not needed if already written
+    if (file->wrote_phu) return true;
+
+    // not needed if not FPA
+    // XXX this prevents us from defining a SPLIT/MEF CMF file...
+    if (file->fileLevel != PM_FPA_LEVEL_FPA) return true;
+
+    // not needed if only one chip
+    if (file->fpa->chips->n == 1) return true;
+
+
+    // find the FPA phu
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
+    pmHDU *phu = psMemIncrRefCounter(pmFPAviewThisPHU(view, fpa));
+
+    // if there is no PHU, this is a single header+image (extension-less) file. This could be
+    // the case for an input SPLIT set of files being written out as a MEF.  if there is a PHU,
+    // write it out as a 'blank'
+    psMetadata *outhead = psMetadataAlloc();
+    if (phu) {
+        psMetadataCopy (outhead, phu->header);
+    }
+    psFree(phu);
+
+    pmConfigConformHeader (outhead, file->format);
+
+    // We need to get some FPA-level concepts in there
+    // This is a hack, not very pretty.  But then, so is writing the FPA in this manner without
+    // using the pmFPAMosaic functions....
+    // XXX why are these not correctly inserted by pmConfigConformHeader??
+
+    // Because these concepts are not part of the "RULE" in the camera format, pmConfigConformHeader only adds
+    // what is required by the "RULE".
+    // Though we can configure Ohana to look at particular header keywords, it doesn't like having the
+    // important values in "HIERARCH FPA.TIME", etc, as is done for skycells, so we stoop to its level,
+    // putting in additional header keywords that it can understand.
+
+    psMetadata *headers = psMetadataLookupMetadata(NULL, fpa->camera, BLANK_HEADERS); // Header names
+    if (!headers) {
+        psError(PS_ERR_UNEXPECTED_NULL, false,
+                "Unable to find %s metadata within camera configuration", BLANK_HEADERS);
+        psFree(outhead);
+        psFree(fpa);
+        return false;
+    }
+
+    {
+        const char *mjdName = psMetadataLookupStr(NULL, headers, "FPA.TIME"); // Header name
+        if (!mjdName || strlen(mjdName) == 0) {
+            psError(PS_ERR_UNEXPECTED_NULL, false,
+                    "Unable to find FPA.TIME in %s within camera configuration.", BLANK_HEADERS);
+            psFree(outhead);
+            psFree(fpa);
+            return false;
+        }
+        psTime *time = psMetadataLookupTime(NULL, fpa->concepts, "FPA.TIME"); // Time of observation
+        double mjd = psTimeToMJD(time); // The MJD of observation
+        psMetadataAddF64(outhead, PS_LIST_TAIL, mjdName, PS_META_REPLACE,
+                         "Time of observation", mjd);
+    }
+
+    {
+        const char *expName = psMetadataLookupStr(NULL, headers, "FPA.EXPOSURE"); // Header name
+        if (!expName || strlen(expName) == 0) {
+            psError(PS_ERR_UNEXPECTED_NULL, false,
+                    "Unable to find FPA.EXPOSURE in %s within camera configuration.",
+                    BLANK_HEADERS);
+            psFree(outhead);
+            psFree(fpa);
+            return false;
+        }
+        float exptime = psMetadataLookupF32(NULL, fpa->concepts, "FPA.EXPOSURE"); // Exposure time
+        psMetadataAddF32(outhead, PS_LIST_TAIL, expName, PS_META_REPLACE,
+                         "Exposure time (sec)", exptime);
+    }
+
+    {
+        const char *amName = psMetadataLookupStr(NULL, headers, "FPA.AIRMASS"); // Header name
+        if (!amName || strlen(amName) == 0) {
+            psError(PS_ERR_UNEXPECTED_NULL, false,
+                    "Unable to find FPA.AIRMASS in %s within camera configuration.",
+                    BLANK_HEADERS);
+            psFree(outhead);
+            psFree(fpa);
+            return false;
+        }
+        float airmass = psMetadataLookupF32(NULL, fpa->concepts, "FPA.AIRMASS"); // Airmass
+        psMetadataAddF32(outhead, PS_LIST_TAIL, amName, PS_META_REPLACE,
+                         "Observation airmass", airmass);
+    }
+
+    psMetadata *fileData = psMetadataLookupMetadata(NULL, file->format, "FILE"); // File information
+    const char *fpaNameHdr = psMetadataLookupStr(NULL, fileData, "FPA.OBS");
+    if (fpaNameHdr && strlen(fpaNameHdr) > 0) {
+        const char *fpaName = psMetadataLookupStr(NULL, fpa->concepts, "FPA.OBS");
+        psMetadataAddStr(outhead, PS_LIST_TAIL, fpaNameHdr, PS_META_REPLACE,
+                         "FPA observation identifier", fpaName);
+    }
+
+    // if we have mosaic-level astrometry information, add it here:
+    psMetadata *updates = psMetadataLookupPtr (&status, fpa->analysis, "PSASTRO.HEADER");
+    if (updates) {
+        psMetadataCopy (outhead, updates);
+    }
+    psFree(fpa);
+
+    psMetadataAddBool (outhead, PS_LIST_TAIL, "EXTEND", PS_META_REPLACE, "this file has extensions", true);
+    psFitsWriteBlank (file->fits, outhead, "");
+    file->wrote_phu = true;
+
+    psTrace ("pmFPAfile", 5, "wrote phu %s (type: %d)\n", file->filename, file->type);
+    psFree (outhead);
+
+    return true;
+}
+
+// Given a FITS file pointer, read the table of object data
+// XXX add in error handling
+bool pmFPAviewReadObjects (const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        pmFPAReadObjects (fpa, view, file, config);
+        return true;
+    }
+
+    if (view->chip >= fpa->chips->n) {
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        pmChipReadObjects (chip, view, file, config);
+        return true;
+    }
+
+    if (view->cell >= chip->cells->n) {
+        return false;
+    }
+    pmCell *cell = chip->cells->data[view->cell];
+
+    if (view->readout == -1) {
+        pmCellReadObjects (cell, view, file, config);
+        return true;
+    }
+
+    if (view->readout >= cell->readouts->n) {
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[view->readout];
+
+    pmReadoutReadObjects (readout, view, file, config);
+    return true;
+}
+
+// read in all chip-level Objects files for this FPA
+bool pmFPAReadObjects (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa->chips, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc (view->nRows);
+    *thisView = *view;
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        thisView->chip = i;
+        pmChipReadObjects (chip, thisView, file, config);
+    }
+    psFree (thisView);
+
+    if (!pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
+        psError(PS_ERR_IO, false, "Failed to read concepts for fpa.\n");
+        return false;
+    }
+
+    return true;
+}
+
+// read in all cell-level Objects files for this chip
+bool pmChipReadObjects (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(chip->cells, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc (view->nRows);
+    *thisView = *view;
+
+    chip->data_exists = false;
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i];
+        thisView->cell = i;
+        pmCellReadObjects (cell, thisView, file, config);
+        if (!cell->data_exists) continue;
+        chip->data_exists = true;
+    }
+    psFree (thisView);
+
+    if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, true, true, NULL)) {
+        psError(PS_ERR_IO, false, "Failed to read concepts for chip.\n");
+        return false;
+    }
+
+    return true;
+}
+
+// read in all readout-level Objects files for this cell
+bool pmCellReadObjects (pmCell *cell, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(cell->readouts, false);
+
+    pmFPAview *thisView = pmFPAviewAlloc (view->nRows);
+    *thisView = *view;
+
+    // multiple readout mode is not yet defined for CMP or CMF files
+    // if they have not been allocated, allocate a single readout
+    if (!cell->readouts || !cell->readouts->n) {
+        pmReadout *readout = pmReadoutAlloc (cell);
+        psFree (readout);
+    }
+
+    cell->data_exists = false;
+    for (int i = 0; i < cell->readouts->n; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        thisView->readout = i;
+        pmReadoutReadObjects (readout, thisView, file, config);
+        if (!readout->data_exists) {
+            continue;
+        }
+
+        // load in the concept information for this cell
+        if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
+            //psError(PS_ERR_UNKNOWN, false, "Failed to read concepts for cell");
+            //return false;
+            psWarning("Difficulty reading concepts for cell; attempting to proceed.");
+        }
+        cell->data_exists = true;
+    }
+    psFree (thisView);
+
+    if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
+        //psError(PS_ERR_UNKNOWN, false, "Failed to read concepts for cell");
+        //return false;
+        psWarning("Difficulty reading concepts for cell; attempting to proceed.");
+    }
+
+    return true;
+}
+
+// read in all readout-level Objects files for this cell
+bool pmReadoutReadObjects (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    bool status;
+    psArray *sources = NULL;
+    pmHDU *hdu;
+
+    switch (file->type) {
+      case PM_FPA_FILE_OBJ:
+        psError(PS_ERR_UNKNOWN, true, "OBJ is not supported as an input object format");
+        return false;
+
+      case PM_FPA_FILE_SX:
+        psError(PS_ERR_UNKNOWN, true, "SX is not supported as an input object format");
+        return false;
+
+      case PM_FPA_FILE_CMP:
+        // a SPLIT format : only one header and object table per file
+
+        // read in header, if not yet loaded
+        hdu = pmFPAviewThisHDU (view, file->fpa);
+#if 0
+        if (file->filename)
+            psFree (file->filename);
+        file->filename = pmFPAfileNameFromRule (file->filerule, file, view);
+#endif
+
+        // indirect filenames
+        if (!strcasecmp (file->filename, "@FILES")) {
+            psFree (file->filename);
+            char *filesrc = pmFPAfileNameFromRule (file->filesrc, file, view);
+            file->filename = psMetadataLookupStr (&status, file->names, filesrc);
+            psFree (filesrc);
+            if (file->filename == NULL) return false;
+            // psMetadataLookupStr just returns a view, file->filename must be protected
+            psMemIncrRefCounter (file->filename);
+        }
+
+        // read the PHU from this file
+        file->fits = psFitsOpen (file->filename, "r");
+        if (hdu->header != NULL) {
+            psFree (hdu->header);
+        }
+        hdu->header = psFitsReadHeader (NULL, file->fits);
+        psFitsClose (file->fits);
+        file->fits = NULL;
+
+        sources = pmSourcesReadCMP (file->filename, hdu->header);
+        break;
+
+      case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_WCS:  // "WCS" is CMF without detected objects
+        // read in header, if not yet loaded
+        hdu = pmFPAviewThisHDU (view, file->fpa);
+
+        // lookup the EXTNAME values used for table data and image header segments
+        char *rule = NULL;
+        // Menu of EXTNAME rules
+        psMetadata *menu = psMetadataLookupMetadata(&status, file->camera, "EXTNAME.RULES");
+        if (!menu) {
+            psError(PS_ERR_UNKNOWN, true, "missing EXTNAME.RULES in camera.config");
+            return false;
+        }
+        // EXTNAME for image header
+        rule = psMetadataLookupStr(&status, menu, "CMF.HEAD");
+        if (!rule) {
+            psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.HEAD in EXTNAME.RULES in camera.config");
+            return false;
+        }
+        char *headname = pmFPAfileNameFromRule (rule, file, view);
+        // EXTNAME for table data
+        rule = psMetadataLookupStr(&status, menu, "CMF.DATA");
+        if (!rule) {
+            psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.DATA in EXTNAME.RULES in camera.config");
+            return false;
+        }
+        char *dataname = pmFPAfileNameFromRule (rule, file, view);
+
+        // advance to the IMAGE HEADER extension
+        if (hdu->header == NULL) {
+            // if the IMAGE header does not exist, we have no data for this view
+            if (!psFitsMoveExtName (file->fits, headname)) {
+                readout->data_exists = false;
+                psFree (headname);
+                psFree (dataname);
+                return true;
+            }
+            hdu->header = psFitsReadHeader (NULL, file->fits);
+        }
+
+        // we need to find the corresponding table EXTNAME.
+        // first check the header
+        char *extdata = psMetadataLookupStr (NULL, hdu->header, "EXTDATA");
+        if (extdata) {
+            // if EXTDATA is defined in the header, use that value for 'dataname'
+            psFree (dataname);
+            dataname = psMemIncrRefCounter (extdata);
+        }
+
+        // advance to the table data extension
+        // since we have read the IMAGE header, the TABLE header should exist
+        if (!psFitsMoveExtName (file->fits, dataname)) {
+            psAbort("cannot find data extension %s in %s", dataname, file->filename);
+        }
+
+        psMetadata *tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
+        if (!tableHeader) psAbort("cannot read table header");
+
+        char *exttype = psMetadataLookupStr (NULL, tableHeader, "EXTTYPE");
+        if (!exttype) psAbort("cannot read table type");
+
+        // XXX these are case-sensitive since the EXTYPE is case-sensitive
+	if (file->type == PM_FPA_FILE_CMF) {
+	    if (!strcmp (exttype, "SMPDATA")) {
+		sources = pmSourcesRead_SMPDATA (file->fits, hdu->header);
+	    }
+	    if (!strcmp (exttype, "PS1_DEV_0")) {
+		sources = pmSourcesRead_PS1_DEV_0 (file->fits, hdu->header);
+	    }
+	    if (!strcmp (exttype, "PS1_DEV_1")) {
+		sources = pmSourcesRead_PS1_DEV_1 (file->fits, hdu->header);
+	    }
+	}
+
+        psTrace("psModules.objects", 6, "read CMF table from %s : %s : %s", file->filename, headname, dataname);
+        psFree (headname);
+        psFree (dataname);
+        psFree (tableHeader);
+        break;
+
+      default:
+        fprintf (stderr, "warning: type mismatch\n");
+        break;
+    }
+    readout->data_exists = true;
+    status = psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY, "input sources", sources);
+    psFree (sources);
+    return true;
+}
+
+bool pmFPAviewCheckDataStatusForSources (const pmFPAview *view, const pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(file->fpa, false);
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        bool exists = pmFPACheckDataStatusForSources (fpa);
+        return exists;
+    }
+    if (view->chip >= fpa->chips->n) {
+        psError(PS_ERR_IO, true, "Requested chip == %d >= fpa->chips->n == %ld", view->chip, fpa->chips->n);
+        return false;
+    }
+    pmChip *chip = fpa->chips->data[view->chip];
+
+    if (view->cell == -1) {
+        bool exists = pmChipCheckDataStatusForSources (chip);
+        return exists;
+    }
+    if (view->cell >= chip->cells->n) {
+        psError(PS_ERR_IO, true, "Requested cell == %d >= chip->cells->n == %ld", view->cell, chip->cells->n);
+        return false;
+    }
+    pmCell *cell = chip->cells->data[view->cell];
+
+    if (view->readout == -1) {
+        bool exists = pmCellCheckDataStatusForSources (cell);
+        return exists;
+    }
+
+    if (view->readout >= cell->readouts->n) {
+        psError(PS_ERR_IO, true, "Requested readout == %d >= cell->readouds->n == %ld", view->readout, cell->readouts->n);
+        return false;
+    }
+    pmReadout *readout = cell->readouts->data[view->readout];
+
+    bool exists = pmReadoutCheckDataStatusForSources (readout);
+    return exists;
+}
+
+bool pmFPACheckDataStatusForSources (const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fpa->chips, false);
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        if (!chip) continue;
+        if (pmChipCheckDataStatusForSources (chip)) return true;
+    }
+    return false;
+}
+
+bool pmChipCheckDataStatusForSources (const pmChip *chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(chip->cells, false);
+
+    for (int i = 0; i < chip->cells->n; i++) {
+        pmCell *cell = chip->cells->data[i];
+        if (!cell) continue;
+        if (pmCellCheckDataStatusForSources (cell)) return true;
+    }
+    return false;
+}
+
+bool pmCellCheckDataStatusForSources (const pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(cell->readouts, false);
+
+    for (int i = 0; i < cell->readouts->n; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        if (!readout) continue;
+        if (pmReadoutCheckDataStatusForSources (readout)) return true;
+    }
+    return false;
+}
+
+bool pmReadoutCheckDataStatusForSources (const pmReadout *readout)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+
+    bool status;
+
+    // select the psf of interest
+    pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.SOURCES");
+    if (!psf) return false;
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO.h	(revision 22322)
@@ -0,0 +1,65 @@
+/* @file  pmSourceIO.h
+ * @brief functions to read and write object files
+ *
+ * @author EAM, IfA; GLG, MHPCC
+ *
+ * @version $Revision: 1.18 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-07-17 22:38:15 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+# ifndef PM_SOURCE_IO_H
+# define PM_SOURCE_IO_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+int pmSourceGetDophotType (pmSource *source);
+bool pmSourceSetDophotType (pmSource *source, int type);
+
+bool pmSourcesWriteRAW (psArray *sources, char *filename);
+bool pmSourcesWriteOBJ (psArray *sources, char *filename);
+bool pmSourcesWriteSX (psArray *sources, char *filename);
+bool pmSourcesWriteCMP (psArray *sources, char *filename, psMetadata *header);
+
+bool pmSourcesWrite_SMPDATA (psFits *fits, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
+bool pmSourcesWrite_PS1_DEV_0 (psFits *fits, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
+bool pmSourcesWrite_PS1_DEV_1 (psFits *fits, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, char *xsrcname);
+bool pmSourcesWrite_PS1_DEV_1_XSRC (psFits *fits, psArray *sources, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_PS1_DEV_1_XFIT (psFits *fits, psArray *sources, char *extname);
+
+bool pmSource_CMF_WritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+
+psArray *pmSourcesReadCMP (char *filename, psMetadata *header);
+
+psArray *pmSourcesRead_SMPDATA (psFits *fits, psMetadata *header);
+psArray *pmSourcesRead_PS1_DEV_0 (psFits *fits, psMetadata *header);
+psArray *pmSourcesRead_PS1_DEV_1 (psFits *fits, psMetadata *header);
+
+bool pmSourcesWritePSFs (psArray *sources, char *filename);
+bool pmSourcesWriteEXTs (psArray *sources, char *filename, bool require);
+bool pmSourcesWriteNULLs (psArray *sources, char *filename);
+bool pmMomentsWriteText (psArray *sources, char *filename);
+bool pmPeaksWriteText (psArray *peaks, char *filename);
+
+bool pmFPAviewReadObjects (const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmFPAReadObjects (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmChipReadObjects (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmCellReadObjects (pmCell *cell, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmReadoutReadObjects (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+
+bool pmFPAviewWriteObjects (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmFPAWriteObjects (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmChipWriteObjects (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmCellWriteObjects (pmCell *cell, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmReadoutWriteObjects (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+
+bool pmFPAviewCheckDataStatusForSources (const pmFPAview *view, const pmFPAfile *file);
+bool pmFPACheckDataStatusForSources (const pmFPA *fpa);
+bool pmChipCheckDataStatusForSources (const pmChip *chip);
+bool pmCellCheckDataStatusForSources (const pmCell *cell);
+bool pmReadoutCheckDataStatusForSources (const pmReadout *readout);
+
+/// @}
+# endif /* PM_SOURCE_IO_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_CMP.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_CMP.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_CMP.c	(revision 22322)
@@ -0,0 +1,302 @@
+/** @file  pmSourceIO.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.33 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-01 18:33:01 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourceIO.h"
+
+// XXX make sure in and out have consistent zero-point adjustments
+// XXX make sure the angle in correctly translated to/from degrees
+// XXX we lose all information from the 'type' field
+
+// XXX update this file is we convert to PAR[4] : SigmaX*sqrt(2) (not 1/SigmaX)
+
+// elixir-style pseudo FITS table (header + ascii list)
+bool pmSourcesWriteCMP (psArray *sources, char *filename, psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+    PS_ASSERT_PTR_NON_NULL(header, false);
+
+    int i, type;
+    // psMetadataItem *mdi;
+    psF32 *PAR, *dPAR;
+    float lsky = 0;
+    bool status;
+    psEllipseAxes axes;
+
+    // find config information for output header
+    float ZERO_POINT = psMetadataLookupF32 (&status, header, "ZERO_PT");
+    if (!status) {
+        ZERO_POINT = 25.0;
+    }
+
+    // MEF elements have XTENSION, not SIMPLE: remove this (replace with SIMPLE)
+    psMetadataLookupStr (&status, header, "XTENSION");
+    if (status) {
+        psMetadataRemoveKey (header, "XTENSION");
+    }
+
+    // create file, write-out header
+    psMetadataAddS32 (header, PS_LIST_HEAD, "NAXIS", PS_META_REPLACE, "head data only", 0);
+    psMetadataAddBool (header, PS_LIST_HEAD, "SIMPLE", PS_META_REPLACE, "CMP file, not simple", false);
+
+    psFits *fits = psFitsOpen (filename, "w");
+    if (fits == NULL) {
+        psError(PS_ERR_IO, false, "can't open output file for write %s\n", filename);
+        return false;
+    }
+    // XXX what is the EXTNAME??
+    if (!psFitsWriteBlank(fits, header, "")) {
+        psError(PS_ERR_IO, false, "Writing header to %s\n", filename);
+        (void)psFitsClose(fits);
+        return false;
+    }
+    if (!psFitsClose(fits)) {
+        const psErrorCode code = psErrorCodeLast();
+
+        if (code == PS_ERR_BAD_FITS) {
+            psErrorClear();
+        } else {
+            psError(PS_ERR_IO, false, "Closing %s\n", filename);
+            return false;
+        }
+    }
+
+    // re-open, add data to end of file
+    FILE *f = fopen (filename, "a+");
+    if (f == NULL) {
+        psLogMsg ("WriteSourceOBJ", 3, "can't reopen output file for append %s\n", filename);
+        psError(PS_ERR_IO, false, "can't open output file for output %s\n", filename);
+        return false;
+    }
+
+    fseeko(f, 0, SEEK_END);
+
+    psLine *line = psLineAlloc (67);  // 66 is imclean-defined line length
+
+    // write sources with models first
+    for (i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+
+        // no difference between PSF and non-PSF model
+        pmModel *model = pmSourceGetModel (NULL, source);
+        if (model == NULL)
+            continue;
+
+        PAR = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+
+        type = pmSourceGetDophotType (source);
+        lsky = (source->sky < 1.0) ? 0.0 : log10(source->sky);
+
+        axes = pmPSF_ModelToAxes (PAR, 20.0);
+
+        float errMag = isfinite(source->errMag) ? source->errMag : 999;
+
+        psLineInit (line);
+        psLineAdd (line, "%6.1f ",  PAR[PM_PAR_XPOS]);
+        psLineAdd (line, "%6.1f ",  PAR[PM_PAR_YPOS]);
+        psLineAdd (line, "%6.3f ",  PS_MIN (99.0, source->psfMag + ZERO_POINT));
+        psLineAdd (line, "%03d ",   PS_MIN (999, (int)errMag));
+        psLineAdd (line, "%2d ",    type);
+        psLineAdd (line, "%3.1f ",  lsky);
+        psLineAdd (line, "%6.3f ",  PS_MIN (99.0, source->extMag + ZERO_POINT));
+        psLineAdd (line, "%6.3f ",  PS_MIN (99.0, source->apMag  + ZERO_POINT));
+        psLineAdd (line, "%6.2f ",  axes.major);
+        psLineAdd (line, "%6.2f ",  axes.minor);
+        psLineAdd (line, "%5.1f\n", axes.theta);
+        if (fwrite(line->line, 1, line->Nline, f) < line->Nline) {
+            psError(PS_ERR_IO, true, "Unable to write CMP sources file (%s)", filename);
+            fclose(f);
+            psFree(line);
+            return false;
+        }
+    }
+    fclose (f);
+    psFree (line);
+    return true;
+}
+
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+// elixir-style pseudo FITS table (header + ascii list)
+psArray *pmSourcesReadCMP (char *filename, psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+    PS_ASSERT_PTR_NON_NULL(header, false);
+
+    bool status;
+    int Ninstar;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+
+    // define PSF model type
+    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
+
+    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
+    if (PSF_NAME != NULL) {
+        modelType = pmModelClassGetType (PSF_NAME);
+    }
+
+    // find config information for output header
+    float ZERO_POINT = psMetadataLookupF32 (&status, header, "ZERO_PT");
+    if (!status)
+        ZERO_POINT = 25.0;
+
+    // how many lines in the header?
+    long nLines = header->list->n;
+    off_t nBytes = nLines * 80;
+    if (nBytes % 2880) {
+        off_t nBlock = 1 + (off_t)(nBytes / 2880);
+        nBytes = nBlock * 2880;
+    }
+
+    // re-open, seek to end of header
+    FILE *f = fopen (filename, "r");
+    if (f == NULL) {
+        psLogMsg ("pmSourcesReadCMP", 3, "can't open output file for input %s\n", filename);
+        return NULL;
+    }
+
+    fseeko(f, nBytes, SEEK_SET);
+
+    // prepare array to store data
+    int nStars = psMetadataLookupS32 (&status, header, "NSTARS");
+    psArray *sources = psArrayAlloc (nStars);
+    sources->n = 0;
+
+    // we have fixed bytes / line : use that info
+    // XXX use the min of nStars and BLOCK?
+    char *buffer = psAlloc (BYTES_STAR*PS_MIN(nStars, BLOCK));
+
+    int Nextra = 0;
+    while (true) {
+        /* load next data block */
+        // XXX fix the use of two vars with different case -JH
+        off_t Nbytes = BYTES_STAR * BLOCK - Nextra;
+        off_t nbytes = fread (&buffer[Nextra], 1, Nbytes, f);
+        if (nbytes == 0) {
+            goto done_load;
+        }
+        nbytes += Nextra;
+
+        /* check line-by-line integrity */
+        char *c  = buffer;
+        char *c2 = NULL;
+        while (c < buffer + nbytes) {
+            for (c2 = c; *c2 == '\n'; c2++)
+                ;
+            if (c2 > c) { /* extra return chars */
+                memmove (c, c2, (int)(buffer + nbytes - c2));
+                int Nskip = c2 - c;
+                nbytes -= Nskip;
+                memset(buffer + nbytes, '\0', Nskip);
+                psLogMsg (__func__, 4, "deleted %d extra return chars\n", Nskip);
+            }
+            c2 = strchr (c, '\n');
+            if (c2 == (char *) NULL) {
+                goto done_check;
+            }
+            c2++;
+            if ((c2 - c) != BYTES_STAR) { /* bad line, delete it */
+                memmove (c, c2, (int)(buffer + nbytes - c2));
+                int Nskip = c2 - c;
+                nbytes -= Nskip;
+                memset(buffer + nbytes, '\0', Nskip);
+                psLogMsg (__func__, 4, "deleted line, %d extra chars\n", Nskip);
+            } else {
+                c = c2;
+            }
+        }
+done_check:
+
+        /* extract data for stars */
+        Ninstar = nbytes / BYTES_STAR;
+        Nextra = nbytes % BYTES_STAR;
+        for (int j = 0; j < Ninstar; j++) {
+            psString line = psStringNCopy (&buffer[j*BYTES_STAR], BYTES_STAR);
+
+            psArray *array = psStringSplitArray (line, " ", false);
+
+            // XXX this is a bit cheap: I don't even attempt to interpret the
+            // sextractor / dophot analysis to distinguish stars and galaxies
+            // your milage may vary...
+            pmSource *source = pmSourceAlloc ();
+            source->modelPSF = pmModelAlloc (modelType);
+            source->type = PM_SOURCE_TYPE_STAR;
+
+            PAR = source->modelPSF->params->data.F32;
+            dPAR = source->modelPSF->dparams->data.F32;
+
+            PAR[PM_PAR_SKY] = pow (atof (array->data[5]), 10.0);
+            PAR[PM_PAR_XPOS] = atof (array->data[0]);
+            PAR[PM_PAR_YPOS] = atof (array->data[1]);
+            source->psfMag = atof (array->data[2]);
+            source->extMag = atof (array->data[6]);
+            source->errMag = atof (array->data[3]) / 1000.0;
+            source->apMag  = atof (array->data[7]);
+            axes.major     = atof (array->data[8]);
+            axes.minor     = atof (array->data[9]);
+            axes.theta  = atof (array->data[10]);
+
+            if (!isfinite(axes.major))
+                goto skip_source;
+            if (!isfinite(axes.minor))
+                goto skip_source;
+            if (!isfinite(axes.theta))
+                goto skip_source;
+
+            pmPSF_AxesToModel (PAR, axes);
+
+            psArrayAdd (sources, 100, source);
+
+skip_source:
+            psFree (line);
+            psFree (array);
+            psFree (source);
+
+        }
+    }
+done_load:
+
+    // XXX if sources->n != nStars, give an error?
+    psFree (buffer);
+
+    fclose (f);
+    return (sources);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_OBJ.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_OBJ.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_OBJ.c	(revision 22322)
@@ -0,0 +1,108 @@
+/** @file  pmSourceIO.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.18 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-01 01:05:39 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourceIO.h"
+
+// dophot-style output list with fixed line width
+bool pmSourcesWriteOBJ (psArray *sources, char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+
+    int type;
+    psF32 *PAR, *dPAR;
+    float dmag, apResid;
+    psEllipseAxes axes;
+
+    psTimerStart ("string");
+
+    psLine *line = psLineAlloc (104);  // 104 is dophot-defined line length
+
+    FILE *f = fopen (filename, "w");
+    if (f == NULL) {
+        psLogMsg (__func__, 3, "can't open output file for output %s\n", filename);
+        return false;
+    }
+
+    // write sources with models
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+
+        // no difference between PSF and non-PSF model
+        pmModel *model = pmSourceGetModel (NULL, source);
+        if (model == NULL)
+            continue;
+
+        PAR = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+
+        dmag = dPAR[PM_PAR_I0] / PAR[PM_PAR_I0];
+        type = pmSourceGetDophotType (source);
+        if ((source->apMag < 99.0) && (source->psfMag < 99.0)) {
+            apResid = source->apMag - source->psfMag;
+        } else {
+            apResid = 0.0;
+        }
+
+        axes = pmPSF_ModelToAxes (PAR, 20.0);
+
+        psLineInit (line);
+        psLineAdd (line, "%3d",   type);
+        psLineAdd (line, "%8.2f", PAR[PM_PAR_XPOS]);
+        psLineAdd (line, "%8.2f", PAR[PM_PAR_YPOS]);
+        psLineAdd (line, "%8.3f", source->psfMag);
+        psLineAdd (line, "%6.3f", dmag);
+        psLineAdd (line, "%9.2f", source->sky);
+        psLineAdd (line, "%9.3f", axes.major);
+        psLineAdd (line, "%9.3f", axes.minor);
+        psLineAdd (line, "%7.2f", axes.theta);
+        psLineAdd (line, "%8.3f", source->extMag);
+        psLineAdd (line, "%8.3f", source->apMag);
+        psLineAdd (line, "%8.2f\n", apResid);
+        if (fwrite (line->line, 1, line->Nline, f) < line->Nline) {
+            psError(PS_ERR_IO, true, "Unable to write OBJ sources file (%s)", filename);
+            fclose(f);
+            psFree(line);
+            return false;
+        }
+    }
+    fclose (f);
+    psFree (line);
+    fprintf (stderr, "%f seconds for %d objects\n", psTimerMark ("string"), (int)sources->n);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_PS1_DEV_0.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_PS1_DEV_0.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_PS1_DEV_0.c	(revision 22322)
@@ -0,0 +1,224 @@
+/** @file  pmSourceIO.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-01-02 20:39:04 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourceIO.h"
+
+// panstars-style FITS table output (header + table in 1st extension)
+// this format consists of a header derived from the image header
+// followed by a zero-size matrix, followed by the table data
+
+// this output format is valid for psphot analysis of an image, and does not include calibrated
+// values derived in the DVO database.
+// XXX how do I generate the source tables which I need to send to PSPS?
+// XXX: input parameter imageHeader is never used.
+bool pmSourcesWrite_PS1_DEV_0 (psFits *fits, psArray *sources, psMetadata *imageHeader,
+                               psMetadata *tableHeader, char *extname)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(extname, false);
+
+    psArray *table;
+    psMetadata *row;
+    int i;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+    psF32 xPos, yPos;
+    psF32 xErr, yErr;
+
+    table = psArrayAllocEmpty (sources->n);
+
+    // we write out all sources, regardless of quality.  the source flags tell us the state
+    for (i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+
+        // no difference between PSF and non-PSF model
+        pmModel *model = pmSourceGetModel(NULL, source);
+
+        if (model != NULL) {
+            PAR = model->params->data.F32;
+            dPAR = model->dparams->data.F32;
+            xPos = PAR[PM_PAR_XPOS];
+            yPos = PAR[PM_PAR_YPOS];
+            xErr = dPAR[PM_PAR_XPOS];
+            yErr = dPAR[PM_PAR_YPOS];
+
+            axes = pmPSF_ModelToAxes (PAR, 20.0);
+        } else {
+            // XXX: This code seg faults if source->peak is NULL.
+            xPos = source->peak->xf;
+            yPos = source->peak->yf;
+            xErr = 0.0; // XXX a better choice, please
+            yErr = 0.0; // XXX a better choice, please
+            axes.major = 0.0;
+            axes.minor = 0.0;
+            axes.theta = 0.0;
+        }
+
+        float peakMag = (source->peak->flux > 0) ? -2.5*log10(source->peak->flux) : NAN;
+        psS16 nImageOverlap = 1;
+        psS32 ID = 0; // XXX need to figure out how to generate this
+
+        row = psMetadataAlloc ();
+        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
+        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             ID);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF",            PS_DATA_F32, "PSF x coordinate",                           xPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF",            PS_DATA_F32, "PSF y coordinate",                           yPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF_SIG",        PS_DATA_F32, "Sigma in PSF x coordinate",                  xErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF_SIG",        PS_DATA_F32, "Sigma in PSF y coordinate",                  yErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG",     PS_DATA_F32, "PSF fit instrumental magnitude",             PS_MIN (99.0, source->psfMag));
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG_SIG", PS_DATA_F32, "Sigma of PSF instrumental magnitude",        PS_MIN (99.0, source->errMag));
+        psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           PS_MIN (99.0, peakMag));
+        psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
+        psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
+        // XXX this is called STAR_GALAXY_SEP in the ICD; PSF_PROB is better
+        // XXX need to set this value in psphotEvalPSF
+        // XXX can I use the 2d polynomial peak fit to constrain this for the low-sn sources?
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_PROBABILITY",  PS_DATA_F32,  "Probability of PSF-ness",                   NAN);
+        // XXX these should be major and minor, not 'x' and 'y'
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_WIDTH_X",      PS_DATA_F32, "PSF width in x coordinate",                  axes.major);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_WIDTH_Y",      PS_DATA_F32, "PSF width in y coordinate",                  axes.minor);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeight);
+        // XXX not sure how to get this : need to load Nimages with weight
+        psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
+        psMetadataAdd (row, PS_LIST_TAIL, "DUMMY",            PS_DATA_U16, "padding", 0);
+
+        // XXX these calibrated values are not supplied by psphot analysis
+        // psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF",           PS_DATA_F64, "RA from PSF fit",                         RA);
+        // psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F64, "DEC from PSF fit",                        DEC);
+        // psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF_SIG",       PS_DATA_F32, "Sigma of PSF fit RA",                     dRA);
+        // psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF_SIG",      PS_DATA_F32, "Sigma of PSF fit DEC",                    dDEC);
+        // psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "Calibrated magnitude",                    calMag);
+        // psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_sIG",  PS_DATA_F32, "Sigma of calibrated magnitude",           calMagErr);
+
+        psArrayAdd (table, 100, row);
+        psFree (row);
+    }
+
+    if (table->n == 0) {
+        psFitsWriteBlank (fits, tableHeader, extname);
+        psFree (table);
+        return true;
+    }
+
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable (fits, tableHeader, table, extname)) {
+        psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
+        psFree(table);
+        return false;
+    }
+
+    psFree (table);
+    return true;
+}
+
+// read in a readout from the fits file
+psArray *pmSourcesRead_PS1_DEV_0 (psFits *fits, psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(header, false);
+
+    bool status;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+
+    // define PSF model type
+    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
+
+    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
+    if (PSF_NAME != NULL) {
+        modelType = pmModelClassGetType (PSF_NAME);
+    }
+
+    psArray *table = psFitsReadTable (fits);
+    // validate a single row of the table (must match SMP)
+    // XXX: The following seg-faults if table returns NULL, so I added the ASSERT
+    PS_ASSERT_PTR_NON_NULL(table, NULL);
+    psArray *sources = psArrayAlloc (table->n);
+
+    // convert the table to the pmSource entries
+    // XXX need to chooose PSF vs EXT, based on type?
+    for (int i = 0; i < table->n; i++) {
+        pmSource *source = pmSourceAlloc ();
+        pmModel *model = pmModelAlloc (modelType);
+        source->modelPSF  = model;
+        source->type = PM_SOURCE_TYPE_STAR;
+
+        // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
+        PAR = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+
+        psMetadata *row = table->data[i];
+
+        PAR[PM_PAR_XPOS]  = psMetadataLookupF32 (&status, row, "X_PSF");
+        PAR[PM_PAR_YPOS]  = psMetadataLookupF32 (&status, row, "Y_PSF");
+        dPAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PSF_SIG");
+        dPAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PSF_SIG");
+        axes.major        = psMetadataLookupF32 (&status, row, "PSF_WIDTH_X");
+        axes.minor        = psMetadataLookupF32 (&status, row, "PSF_WIDTH_Y");
+        axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
+
+        PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
+        dPAR[PM_PAR_SKY]  = psMetadataLookupF32 (&status, row, "SKY_SIGMA");
+        source->sky = PAR[PM_PAR_SKY];
+        source->skyErr = dPAR[PM_PAR_SKY];
+
+        // XXX use these to determine PAR[PM_PAR_I0]?
+        source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
+        source->errMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
+
+        pmPSF_AxesToModel (PAR, axes);
+
+        float lflux = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
+        float flux = (isfinite(lflux)) ? pow(10.0, -0.4*lflux) : NAN;
+        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], flux, PM_PEAK_LONE);
+        source->peak->flux = flux;
+
+        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
+
+        // XXX other values saved but not loaded?
+        // psMetadataLookupS64 (&status, row, "IPP_IDET");
+        // psMetadataLookupF32 (&status, row, "PSF_PROBABILITY");
+        // psMetadataLookupF32 (&status, row, "N_FRAMES");
+
+        sources->data[i] = source;
+    }
+    psFree (table);
+    return (sources);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_PS1_DEV_1.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_PS1_DEV_1.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_PS1_DEV_1.c	(revision 22322)
@@ -0,0 +1,544 @@
+/** @file  pmSourceIO.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-04-08 18:35:38 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourceIO.h"
+
+// panstarrs-style FITS table output (header + table in 1st extension)
+// this format consists of a header derived from the image header
+// followed by a zero-size matrix, followed by the table data
+
+// this output format is valid for psphot analysis of an image, and does not include calibrated
+// values derived in the DVO database.
+// XXX how do I generate the source tables which I need to send to PSPS?
+
+bool pmSourcesWrite_PS1_DEV_1 (psFits *fits, psArray *sources, psMetadata *imageHeader,
+                               psMetadata *tableHeader, char *extname, char *xsrcname)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(extname, false);
+
+    psArray *table;
+    psMetadata *row;
+    int i;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+    psF32 xPos, yPos;
+    psF32 xErr, yErr;
+
+    // let's write these out in S/N order
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    table = psArrayAllocEmpty (sources->n);
+
+    // we write out all sources, regardless of quality.  the source flags tell us the state
+    for (i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+        source->seq = i;
+
+        // no difference between PSF and non-PSF model
+        pmModel *model = pmSourceGetModel (NULL, source);
+
+        if (model != NULL) {
+            PAR = model->params->data.F32;
+            dPAR = model->dparams->data.F32;
+            xPos = PAR[PM_PAR_XPOS];
+            yPos = PAR[PM_PAR_YPOS];
+            xErr = dPAR[PM_PAR_XPOS];
+            yErr = dPAR[PM_PAR_YPOS];
+
+            axes = pmPSF_ModelToAxes (PAR, 20.0);
+        } else {
+            xPos = source->peak->xf;
+            yPos = source->peak->yf;
+            xErr = source->peak->dx;
+            yErr = source->peak->dy;
+            axes.major = 0.0;
+            axes.minor = 0.0;
+            axes.theta = 0.0;
+        }
+
+        float peakMag = (source->peak->flux > 0) ? -2.5*log10(source->peak->flux) : NAN;
+        psS16 nImageOverlap = 1;
+
+        row = psMetadataAlloc ();
+        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
+        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF",            PS_DATA_F32, "PSF x coordinate",                           xPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF",            PS_DATA_F32, "PSF y coordinate",                           yPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF_SIG",        PS_DATA_F32, "Sigma in PSF x coordinate",                  xErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF_SIG",        PS_DATA_F32, "Sigma in PSF y coordinate",                  yErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG",     PS_DATA_F32, "PSF fit instrumental magnitude",             PS_MIN (99.0, source->psfMag));
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG_SIG", PS_DATA_F32, "Sigma of PSF instrumental magnitude",        PS_MIN (99.0, source->errMag));
+        psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           PS_MIN (99.0, peakMag));
+        psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
+        psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_CHISQ",        PS_DATA_F32,  "Chisq of PSF-fit to top 9 pixels",          source->psfChisq);
+        psMetadataAdd (row, PS_LIST_TAIL, "CR_NSIGMA",        PS_DATA_F32,  "Nsigma deviations from PSF to CF",          source->crNsigma);
+        psMetadataAdd (row, PS_LIST_TAIL, "EXT_NSIGMA",       PS_DATA_F32,  "Nsigma deviations from PSF to EXT",         source->extNsigma);
+
+        // XXX these should be major and minor, not 'x' and 'y'
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_WIDTH_X",      PS_DATA_F32, "PSF width in x coordinate",                  axes.major);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_WIDTH_Y",      PS_DATA_F32, "PSF width in y coordinate",                  axes.minor);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeight);
+
+        // XXX not sure how to get this : need to load Nimages with weight?
+        psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
+        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U16, "psphot analysis flags",                      source->mode);
+
+        // XXX these calibrated values are not supplied by psphot analysis
+        // psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF",           PS_DATA_F64, "RA from PSF fit",                         RA);
+        // psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F64, "DEC from PSF fit",                        DEC);
+        // psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF_SIG",       PS_DATA_F32, "Sigma of PSF fit RA",                     dRA);
+        // psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF_SIG",      PS_DATA_F32, "Sigma of PSF fit DEC",                    dDEC);
+        // psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "Calibrated magnitude",                    calMag);
+        // psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_sIG",  PS_DATA_F32, "Sigma of calibrated magnitude",           calMagErr);
+
+        psArrayAdd (table, 100, row);
+        psFree (row);
+    }
+
+    if (table->n == 0) {
+        psFitsWriteBlank (fits, tableHeader, extname);
+        psFree (table);
+        return true;
+    }
+
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable (fits, tableHeader, table, extname)) {
+        psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
+        psFree(table);
+        return false;
+    }
+    psFree (table);
+
+    return true;
+}
+
+// read in a readout from the fits file
+psArray *pmSourcesRead_PS1_DEV_1 (psFits *fits, psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(header, false);
+
+    bool status;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+
+    // define PSF model type
+    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
+
+    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
+    if (PSF_NAME != NULL) {
+        modelType = pmModelClassGetType (PSF_NAME);
+    }
+    assert (modelType > -1);
+
+    // XXX need to look up the XSRCNAME entries
+
+    // validate a single row of the table (must match SMP)
+
+    // XXX test return values
+
+    // XXX we have a memory problem, which is illustrated here: if I allocate the sources array
+    // (line 1) before freeing the table, the data is not really freed (but not a leak?)  if I
+    // allocate the sources array *after* freeing the table, the data is actually freed
+    // psArray *fooSources = psArrayAllocEmpty (10000);
+    // psFree (table);
+    // return (fooSources);
+
+
+    // We get the size of the table, and allocate the array of sources first because the table is large and
+    // ephemeral --- when the table gets blown away, whatever is allocated after the table is read.  In fact,
+    // it's better to read the table row by row.
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+    psArray *sources = psArrayAlloc(numSources); // Array of sources, to return
+
+    // convert the table to the pmSource entries
+    // XXX need to chooose PSF vs EXT, based on type?
+    for (int i = 0; i < numSources; i++) {
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+
+        pmSource *source = pmSourceAlloc ();
+        pmModel *model = pmModelAlloc (modelType);
+        source->modelPSF  = model;
+        source->type = PM_SOURCE_TYPE_STAR;
+
+        // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
+        PAR = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+
+        PAR[PM_PAR_XPOS]  = psMetadataLookupF32 (&status, row, "X_PSF");
+        PAR[PM_PAR_YPOS]  = psMetadataLookupF32 (&status, row, "Y_PSF");
+        dPAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PSF_SIG");
+        dPAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PSF_SIG");
+        axes.major        = psMetadataLookupF32 (&status, row, "PSF_WIDTH_X");
+        axes.minor        = psMetadataLookupF32 (&status, row, "PSF_WIDTH_Y");
+        axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
+
+        PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
+        dPAR[PM_PAR_SKY]  = psMetadataLookupF32 (&status, row, "SKY_SIGMA");
+        source->sky = PAR[PM_PAR_SKY];
+        source->skyErr = dPAR[PM_PAR_SKY];
+
+        // XXX use these to determine PAR[PM_PAR_I0]?
+        source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
+        source->errMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
+
+        pmPSF_AxesToModel (PAR, axes);
+
+        float lflux = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
+        float flux = (isfinite(lflux)) ? pow(10.0, -0.4*lflux) : NAN;
+        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], flux, PM_PEAK_LONE);
+        source->peak->flux = flux;
+
+        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
+
+	// note that some older versions used PSF_PROBABILITY: this was not well defined.
+        source->psfChisq  = psMetadataLookupF32 (&status, row, "PSF_CHISQ");
+        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
+        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
+
+        source->mode      = psMetadataLookupU16 (&status, row, "FLAGS");
+        assert (status);
+
+        // XXX other values saved but not loaded?
+        // psMetadataLookupS64 (&status, row, "IPP_IDET");
+        // psMetadataLookupF32 (&status, row, "N_FRAMES");
+
+        sources->data[i] = source;
+        psFree(row);
+    }
+
+    return sources;
+}
+
+bool pmSourcesWrite_PS1_DEV_1_XSRC (psFits *fits, psArray *sources, char *extname, psMetadata *recipe)
+{
+
+    bool status;
+    psArray *table;
+    psMetadata *row;
+    psF32 *PAR, *dPAR;
+    psF32 xPos, yPos;
+    psF32 xErr, yErr;
+
+    // create a header to hold the output data
+    psMetadata *outhead = psMetadataAlloc ();
+
+    // write the links to the image header
+    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
+
+    // let's write these out in S/N order
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    table = psArrayAllocEmpty (sources->n);
+
+    // which extended source analyses should we perform?
+    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
+    bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
+    bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
+    bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
+
+    psVector *radialBinsLower = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
+    psVector *radialBinsUpper = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
+    assert (radialBinsLower->n == radialBinsUpper->n);
+
+    // we write out all sources, regardless of quality.  the source flags tell us the state
+    for (int i = 0; i < sources->n; i++) {
+	// skip source if it is not a ext sourc
+	// XXX we have two places that extended source parameters are measured:
+	// psphotExtendedSources, which measures the aperture-like parameters and (potentially) the psf-convolved extended source models,
+	// psphotFitEXT, which does the simple extended source model fit (not psf-convolved)
+	// should we require both?
+
+	pmSource *source = sources->data[i];
+
+	// skip sources without measurements
+	if (source->extpars == NULL) continue;
+
+	// we require a PSF model fit (ignore the real crud)
+	pmModel *model = source->modelPSF;
+	if (model == NULL) continue;
+
+	// XXX I need to split the extended models from the extended aperture measurements
+	PAR = model->params->data.F32;
+	dPAR = model->dparams->data.F32;
+	xPos = PAR[PM_PAR_XPOS];
+	yPos = PAR[PM_PAR_YPOS];
+	xErr = dPAR[PM_PAR_XPOS];
+	yErr = dPAR[PM_PAR_YPOS];
+
+        row = psMetadataAlloc ();
+
+        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
+        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT",            PS_DATA_F32, "EXT model x coordinate",                     xPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT",            PS_DATA_F32, "EXT model y coordinate",                     yPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT_SIG",        PS_DATA_F32, "Sigma in EXT x coordinate",                  xErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
+
+	// Petrosian measurements
+	// XXX insert header data: petrosian ref radius, flux ratio
+	if (doPetrosian) {
+	    pmSourcePetrosianValues *petrosian = source->extpars->petrosian;
+	    if (petrosian) {
+		psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       petrosian->mag);
+		psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", petrosian->magErr);
+		psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          petrosian->rad);
+		psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    petrosian->radErr);
+	    } else {
+		psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
+		psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", NAN);
+		psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
+		psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    NAN);
+	    }
+	} 
+
+	// Kron measurements
+	if (doKron) {
+	    pmSourceKronValues *kron = source->extpars->kron;
+	    if (kron) {
+		psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       kron->mag);
+		psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", kron->magErr);
+		psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          kron->rad);
+		psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    kron->radErr);
+	    } else {
+		psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       NAN);
+		psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", NAN);
+		psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          NAN);
+		psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    NAN);
+	    }
+	}
+
+	// Isophot measurements
+	// XXX insert header data: isophotal level
+	if (doIsophotal) {
+	    pmSourceIsophotalValues *isophot = source->extpars->isophot;
+	    if (isophot) {
+		psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       isophot->mag);
+		psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", isophot->magErr);
+		psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          isophot->rad);
+		psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    isophot->radErr);
+	    } else {
+		psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       NAN);
+		psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", NAN);
+		psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          NAN);
+		psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    NAN);
+	    }
+	}
+
+	// Flux Annuli
+	if (doAnnuli) {
+	    pmSourceAnnuli *annuli = source->extpars->annuli;
+	    if (annuli) {
+		psVector *fluxVal = annuli->flux;
+		psVector *fluxErr = annuli->fluxErr;
+		psVector *fluxVar = annuli->fluxVar;
+
+		for (int j = 0; j < fluxVal->n; j++) {
+		    char name[32];
+		    sprintf (name, "FLUX_VAL_R_%02d", j);
+		    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", fluxVal->data.F32[j]);
+		    sprintf (name, "FLUX_ERR_R_%02d", j);
+		    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", fluxErr->data.F32[j]);
+		    sprintf (name, "FLUX_VAR_R_%02d", j);
+		    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", fluxVar->data.F32[j]);
+		} 
+	    } else {
+		for (int j = 0; j < radialBinsLower->n; j++) {
+		    char name[32];
+		    sprintf (name, "FLUX_VAL_R_%02d", j);
+		    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", NAN);
+		    sprintf (name, "FLUX_ERR_R_%02d", j);
+		    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", NAN);
+		    sprintf (name, "FLUX_VAR_R_%02d", j);
+		    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", NAN);
+		} 
+	    }
+	}
+
+	psArrayAdd (table, 100, row);
+	psFree (row);
+    }
+
+    if (table->n == 0) {
+	psFitsWriteBlank (fits, outhead, extname);
+	psFree (outhead);
+	psFree (table);
+	return true;
+    }
+
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable (fits, outhead, table, extname)) {
+	psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
+	psFree (outhead);
+	psFree(table);
+	return false;
+    }
+    psFree (outhead);
+    psFree (table);
+
+    return true;
+}
+
+bool pmSourcesWrite_PS1_DEV_1_XFIT (psFits *fits, psArray *sources, char *extname)
+{
+
+    psArray *table;
+    psMetadata *row;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+    psF32 xPos, yPos;
+    psF32 xErr, yErr;
+    char name[64];
+
+    // create a header to hold the output data
+    psMetadata *outhead = psMetadataAlloc ();
+
+    // write the links to the image header
+    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
+
+    // let's write these out in S/N order
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // we are writing one row per model; we need to write out same number of columns for each row: find the max Nparams
+    int nParamMax = 0;
+    for (int i = 0; i < sources->n; i++) {
+	pmSource *source = sources->data[i];
+	if (source->modelFits == NULL) continue;
+	for (int j = 0; j < source->modelFits->n; j++) {
+	    pmModel *model = source->modelFits->data[j];
+	    assert (model);
+	    nParamMax = PS_MAX (nParamMax, model->params->n);
+	}
+    }
+
+    table = psArrayAllocEmpty (sources->n);
+
+    // we write out all sources, regardless of quality.  the source flags tell us the state
+    for (int i = 0; i < sources->n; i++) {
+
+	pmSource *source = sources->data[i];
+
+	// XXX if no model fits are saved, write out modelEXT?
+	if (source->modelFits == NULL) continue;
+
+	// We have multiple sources : need to flag the one used to subtract the light (the 'best' model)
+	for (int j = 0; j < source->modelFits->n; j++) {
+
+	    // choose the convolved EXT model, if available, otherwise the simple one
+	    pmModel *model = source->modelFits->data[j];
+	    assert (model);
+
+	    PAR = model->params->data.F32;
+	    dPAR = model->dparams->data.F32;
+	    xPos = PAR[PM_PAR_XPOS];
+	    yPos = PAR[PM_PAR_YPOS];
+	    xErr = dPAR[PM_PAR_XPOS];
+	    yErr = dPAR[PM_PAR_YPOS];
+
+	    axes = pmPSF_ModelToAxes (PAR, 20.0);
+
+	    row = psMetadataAlloc ();
+
+	    // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
+	    psMetadataAddU32 (row, PS_LIST_TAIL, "IPP_IDET",         0, "IPP detection identifier index",             source->seq);
+	    psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT",            0, "EXT model x coordinate",                     xPos);
+	    psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT",            0, "EXT model y coordinate",                     yPos);
+	    psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT_SIG",        0, "Sigma in EXT x coordinate",                  xErr);
+	    psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT_SIG",        0, "Sigma in EXT y coordinate",                  yErr);
+	    psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
+	    psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
+
+	    psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
+	    psMetadataAddStr (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "name of model",                              pmModelClassGetName (model->type));
+
+	    // XXX these should be major and minor, not 'x' and 'y'
+	    psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MAJ",    0, "EXT width in x coordinate",                  axes.major);
+	    psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MIN",    0, "EXT width in y coordinate",                  axes.minor);
+	    psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_THETA",        0, "EXT orientation angle",                      axes.theta);
+
+	    // write out the other generic parameters
+	    for (int k = 0; k < nParamMax; k++) {
+		if (k == PM_PAR_I0) continue;
+		if (k == PM_PAR_SKY) continue;
+		if (k == PM_PAR_XPOS) continue;
+		if (k == PM_PAR_YPOS) continue;
+		if (k == PM_PAR_SXX) continue;
+		if (k == PM_PAR_SXY) continue;
+		if (k == PM_PAR_SYY) continue;
+
+		snprintf (name, 64, "EXT_PAR_%02d", k);
+
+		if (k < model->params->n) {
+		    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "", model->params->data.F32[k]);
+		} else {
+		    psMetadataAddF32 (row, PS_LIST_TAIL, name, PS_DATA_F32, "", NAN);
+		}
+	    }
+
+	    // XXX other parameters which may be set.
+	    // XXX flag / value to define the model
+	    // XXX write out the model type, fit status flags
+
+	    psArrayAdd (table, 100, row);
+	    psFree (row);
+	}
+    }
+
+    if (table->n == 0) {
+	psFitsWriteBlank (fits, outhead, extname);
+	psFree (outhead);
+	psFree (table);
+	return true;
+    }
+
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable (fits, outhead, table, extname)) {
+	psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
+	psFree (outhead);
+	psFree(table);
+	return false;
+    }
+    psFree (outhead);
+    psFree (table);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_RAW.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_RAW.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_RAW.c	(revision 22322)
@@ -0,0 +1,293 @@
+/** @file  pmSourceIO.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.18 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-10 01:09:20 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourcePhotometry.h"
+#include "pmSourceIO.h"
+
+/***** Text Output Methods *****/
+bool pmSourcesWriteRAW (psArray *sources, char *filename)
+{
+
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+
+    char *name = (char *) psAlloc (strlen(filename) + 10);
+
+    sprintf (name, "%s.psf.dat", filename);
+    pmSourcesWritePSFs (sources, name);
+
+    sprintf (name, "%s.ext.dat", filename);
+    pmSourcesWriteEXTs (sources, name, true);
+
+    sprintf (name, "%s.nul.dat", filename);
+    pmSourcesWriteNULLs (sources, name);
+
+    sprintf (name, "%s.mnt.dat", filename);
+    pmMomentsWriteText (sources, name);
+
+    psFree (name);
+    return true;
+}
+
+// write the PSF sources to an output file
+bool pmSourcesWritePSFs (psArray *sources, char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+
+    double dPos;
+    int i, j;
+    FILE *f;
+    psF32 *PAR, *dPAR;
+    pmModel  *model;
+
+    f = fopen (filename, "w");
+    if (f == NULL) {
+        psLogMsg (__func__, 3, "can't open output file for PSF sources: %s\n", filename);
+        return false;
+    }
+
+    // write sources with models first
+    for (i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+        if (source->type != PM_SOURCE_TYPE_STAR)
+            continue;
+        model = source->modelPSF;
+        if (model == NULL)
+            continue;
+
+        PAR  = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+
+        // dPos is positional error, dMag is mag error
+        dPos = hypot (dPAR[PM_PAR_XPOS], dPAR[PM_PAR_YPOS]);
+
+        fprintf (f, "%7.1f %7.1f  %7.1f %8.4f  %7.4f %7.4f  ",
+                 PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], source->sky,
+                 source->psfMag, source->errMag, dPos);
+
+        for (j = 4; j < model->params->n; j++) {
+            fprintf (f, "%9.6f ", PAR[j]);
+        }
+        fprintf (f, " : ");
+        for (j = 4; j < model->params->n; j++) {
+            fprintf (f, "%9.6f ", dPAR[j]);
+        }
+
+        float logChi = ((model[0].chisq == 0.0) || (model[0].nDOF == 0)) ? NAN : log10(model[0].chisq/model[0].nDOF);
+        float logChiNorm = ((model[0].chisqNorm == 0.0) || (model[0].nDOF == 0)) ? NAN : log10(model[0].chisqNorm/model[0].nDOF);
+
+        fprintf (f, ": %8.4f %2d %#5x %7.3f %7.3f  %7.1f %7.2f %4.2f %4d %2d\n",
+                 source[0].apMag, source[0].type, source[0].mode,
+                 logChi, logChiNorm,
+                 source[0].peak->SN,
+                 model[0].radiusFit,
+                 source[0].pixWeight,
+                 model[0].nDOF,
+                 model[0].nIter);
+    }
+    fclose (f);
+    return true;
+}
+
+// dump the sources to an output file
+bool pmSourcesWriteEXTs (psArray *sources, char *filename, bool requireEXT)
+{
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+
+    double dPos;
+    int i, j;
+    FILE *f;
+    psF32 *PAR, *dPAR;
+    pmModel  *model;
+
+    f = fopen (filename, "w");
+    if (f == NULL) {
+        psLogMsg ("pmModelWriteEXTs", 3, "can't open output file for EXT sources: %s\n", filename);
+        return false;
+    }
+
+    // write sources with models first
+    for (i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+
+        if (requireEXT && (source->type != PM_SOURCE_TYPE_EXTENDED))
+            continue;
+
+        model = source->modelEXT;
+        if (model == NULL)
+            continue;
+
+        PAR  = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+
+        // dPos is shape error
+        // XXX these are hardwired for SGAUSS
+        dPos = hypot ((dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]), (dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]));
+
+        fprintf (f, "%7.1f %7.1f  %7.1f %8.4f  %7.4f %7.4f  ",
+                 PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], source->sky,
+                 source->extMag, source->errMag, dPos);
+
+        for (j = 4; j < model->params->n; j++) {
+            fprintf (f, "%9.6f ", PAR[j]);
+        }
+        fprintf (f, " : ");
+        for (j = 4; j < model->params->n; j++) {
+            fprintf (f, "%9.6f ", dPAR[j]);
+        }
+        fprintf (f, ": %7.4f  %2d %#5x %7.3f %7.3f  %7.1f %7.2f %4.2f %4d %2d\n",
+                 source->apMag,
+                 source[0].type, source[0].mode,
+                 log10(model[0].chisq/model[0].nDOF),
+                 log10(model[0].chisqNorm/model[0].nDOF),
+                 source[0].peak->SN,
+                 model[0].radiusFit,
+                 source[0].pixWeight,
+                 model[0].nDOF,
+                 model[0].nIter);
+    }
+    fclose (f);
+    return true;
+}
+
+// dump the sources to an output file
+bool pmSourcesWriteNULLs (psArray *sources, char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+
+    int i;
+    FILE *f;
+    pmMoments *moment = NULL;
+    pmSource *source = NULL;
+
+    f = fopen (filename, "w");
+    if (f == NULL) {
+        psLogMsg ("DumpObjects", 3, "can't open output file for NULL sources: %s\n", filename);
+        return false;
+    }
+
+    pmMoments *empty = pmMomentsAlloc ();
+
+    // write sources with models first
+    for (i = 0; i < sources->n; i++) {
+        source = sources->data[i];
+
+        // skip these sources (in PSF or EXT)
+        if (source->type == PM_SOURCE_TYPE_STAR)
+            continue;
+        if (source->type == PM_SOURCE_TYPE_EXTENDED)
+            continue;
+
+        if (source->moments == NULL) {
+            moment = empty;
+        } else {
+            moment = source->moments;
+        }
+
+        fprintf (f, "%5d %5d  %7.1f  %7.1f %7.1f  %6.3f %6.3f  %8.1f %7.1f %7.1f %7.1f  %4d %2d\n",
+                 source->peak->x, source->peak->y, source->peak->value,
+                 moment->x, moment->y,
+                 moment->Sx, moment->Sy,
+                 moment->Sum, moment->Peak,
+                 moment->Sky, moment->SN,
+                 moment->nPixels, source->type);
+    }
+    fclose (f);
+    psFree (empty);
+    return true;
+}
+
+// write the moments to an output file
+bool pmMomentsWriteText (psArray *sources, char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+
+    int i;
+    FILE *f;
+    pmSource *source = NULL;
+
+    f = fopen (filename, "w");
+    if (f == NULL) {
+        psLogMsg ("pmMomentsWriteText", 3, "can't open output file for moments: %s\n", filename);
+        return false;
+    }
+
+    for (i = 0; i < sources->n; i++) {
+        source = sources->data[i];
+        if (source->moments == NULL)
+            continue;
+        fprintf (f, "%5d %5d  %7.1f  %7.1f %7.1f  %6.3f %6.3f  %10.1f %7.1f %7.1f %7.1f  %4d %2d %#5x\n",
+                 source->peak->x, source->peak->y, source->peak->value,
+                 source->moments->x, source->moments->y,
+                 source->moments->Sx, source->moments->Sy,
+                 source->moments->Sum, source->moments->Peak,
+                 source->moments->Sky, source->moments->SN,
+                 source->moments->nPixels, source->type, source->mode);
+    }
+    fclose (f);
+    return true;
+}
+
+// write the peaks to an output file
+bool pmPeaksWriteText (psArray *peaks, char *filename)
+{
+
+    int i;
+    FILE *f;
+
+    f = fopen (filename, "w");
+    if (f == NULL) {
+        psLogMsg ("pmPeaksWriteText", 3, "can't open output file for peaks%s\n", filename);
+        return false;
+    }
+
+    for (i = 0; i < peaks->n; i++) {
+        pmPeak *peak = peaks->data[i];
+        if (peak == NULL)
+            continue;
+        fprintf (f, "%5d %5d  %7.1f %7.2f %7.2f %7.2f\n",
+                 peak->x, peak->y, peak->value, peak->SN, peak->xf, peak->yf);
+    }
+    fclose (f);
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_SMPDATA.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_SMPDATA.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_SMPDATA.c	(revision 22322)
@@ -0,0 +1,202 @@
+/** @file  pmSourceIO.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-10 01:09:20 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourceIO.h"
+
+// elixir-style FITS table output (header + table in 1st extension)
+// this format consists of a header derived from the image header
+// followed by a zero-size matrix, followed by the table data
+// XXX: input parameter imageHeader is never used
+bool pmSourcesWrite_SMPDATA (psFits *fits, psArray *sources, psMetadata *imageHeader,
+                             psMetadata *tableHeader, char *extname)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(extname, false);
+
+    psArray *table;
+    psMetadata *row;
+    int i;
+    psF32 *PAR, *dPAR;
+    bool status;
+    psEllipseAxes axes;
+    psF32 xPos, yPos;
+
+    // find config information for output header
+    float ZERO_POINT = psMetadataLookupF32 (&status, imageHeader, "ZERO_PT");
+    if (!status)
+        ZERO_POINT = 25.0;
+
+    float lsky = 0;
+    int type = 0;
+
+    table = psArrayAllocEmpty (sources->n);
+
+    for (i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+
+	// no difference between PSF and non-PSF model
+        pmModel *model = pmSourceGetModel (NULL, source);
+        if (model != NULL) {
+	    PAR = model->params->data.F32;
+	    dPAR = model->dparams->data.F32;
+	    xPos = PAR[PM_PAR_XPOS];
+	    yPos = PAR[PM_PAR_YPOS];
+
+	    type = pmSourceGetDophotType (source);
+	    lsky = (source->sky < 1.0) ? 0.0 : log10(source->sky);
+
+	    axes = pmPSF_ModelToAxes (PAR, 20.0);
+
+	} else {
+	    xPos = source->peak->xf;
+	    yPos = source->peak->yf;
+	    axes.major = 0.0;
+	    axes.minor = 0.0;
+	    axes.theta = 0.0;
+	}
+
+        row = psMetadataAlloc ();
+        psMetadataAdd (row, PS_LIST_TAIL, "X_PIX",   PS_DATA_F32, "", xPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_PIX",   PS_DATA_F32, "", yPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "MAG_RAW", PS_DATA_F32, "", PS_MIN (99.0, source->psfMag + ZERO_POINT));
+        psMetadataAdd (row, PS_LIST_TAIL, "MAG_ERR", PS_DATA_F32, "", PS_MIN (999, 1000*source->errMag));
+        psMetadataAdd (row, PS_LIST_TAIL, "MAG_GAL", PS_DATA_F32, "", PS_MIN (99.0, source->extMag + ZERO_POINT));
+        psMetadataAdd (row, PS_LIST_TAIL, "MAG_AP",  PS_DATA_F32, "", PS_MIN (99.0, source->apMag + ZERO_POINT));
+        psMetadataAdd (row, PS_LIST_TAIL, "LOG_SKY", PS_DATA_F32, "", lsky);
+        psMetadataAdd (row, PS_LIST_TAIL, "FWHM_X",  PS_DATA_F32, "", axes.major);
+        psMetadataAdd (row, PS_LIST_TAIL, "FWHM_Y",  PS_DATA_F32, "", axes.minor);
+        psMetadataAdd (row, PS_LIST_TAIL, "THETA",   PS_DATA_F32, "", axes.theta);
+        psMetadataAdd (row, PS_LIST_TAIL, "DOPHOT",  PS_DATA_U8,  "", type);
+        psMetadataAdd (row, PS_LIST_TAIL, "WEIGHT",  PS_DATA_U8,  "", PS_MIN (255, PS_MAX(0, 255*source->pixWeight)));
+        psMetadataAdd (row, PS_LIST_TAIL, "DUMMY",   PS_DATA_U16, "", 0);
+
+        psArrayAdd (table, 100, row);
+        psFree (row);
+    }
+
+    if (table->n == 0) {
+        psFitsWriteBlank (fits, tableHeader, extname);
+        psFree (table);
+        return true;
+    }
+
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable (fits, tableHeader, table, extname)) {
+        psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
+        psFree(table);
+        return false;
+    }
+
+    psFree (table);
+    return true;
+}
+
+// read in a readout from the fits file
+psArray *pmSourcesRead_SMPDATA (psFits *fits, psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(header, false);
+
+    bool status;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+    float lsky;
+
+    // define PSF model type
+    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
+
+    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
+    if (PSF_NAME != NULL) {
+        modelType = pmModelClassGetType (PSF_NAME);
+    }
+
+    // find config information for output header
+    float ZERO_POINT = psMetadataLookupF32 (&status, header, "ZERO_PT");
+    if (!status)
+        ZERO_POINT = 25.0;
+
+    psArray *table = psFitsReadTable (fits);
+    // validate a single row of the table (must match SMP)
+
+    psArray *sources = psArrayAlloc (table->n);
+
+    // convert the table to the pmSource entries
+    // XXX need to chooose PSF vs EXT, based on type?
+    for (int i = 0; i < table->n; i++) {
+        pmSource *source = pmSourceAlloc ();
+        pmModel *model = pmModelAlloc (modelType);
+        source->modelPSF  = model;
+        source->type = PM_SOURCE_TYPE_STAR;
+
+        PAR = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+
+        psMetadata *row = table->data[i];
+
+        lsky             = psMetadataLookupF32 (&status, row, "LOG_SKY");
+        PAR[PM_PAR_SKY]  = pow(10.0, lsky);
+        source->sky    = PAR[PM_PAR_SKY];
+
+        PAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PIX");
+        PAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PIX");
+        axes.major       = psMetadataLookupF32 (&status, row, "FWHM_X");
+        axes.minor       = psMetadataLookupF32 (&status, row, "FWHM_Y");
+        axes.theta       = psMetadataLookupF32 (&status, row, "THETA");
+
+	pmPSF_AxesToModel (PAR, axes);
+
+
+        source->psfMag = psMetadataLookupF32 (&status, row, "MAG_RAW") - ZERO_POINT;
+        source->extMag = psMetadataLookupF32 (&status, row, "MAG_GAL") - ZERO_POINT;
+        source->errMag = psMetadataLookupF32 (&status, row, "MAG_ERR") * 0.001;
+        source->apMag  = psMetadataLookupF32 (&status, row, "MAG_AP")  - ZERO_POINT;
+
+        source->pixWeight = psMetadataLookupU8 (&status, row, "WEIGHT")/255.0;
+        int dophot = psMetadataLookupU8 (&status, row, "DOPHOT");
+	pmSourceSetDophotType (source, dophot);
+
+	double Area = 2.0 * M_PI * axes.major * axes.minor;
+	double peakFlux = source->psfMag / Area;
+
+	source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
+        sources->data[i] = source;
+    }
+    psFree (table);
+    return (sources);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_SX.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_SX.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceIO_SX.c	(revision 22322)
@@ -0,0 +1,100 @@
+/** @file  pmSourceIO.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-08-01 18:33:14 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourceIO.h"
+
+// elixir-mode / sextractor-style output list with fixed line width
+bool pmSourcesWriteSX (psArray *sources, char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(filename, false);
+
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+
+    psLine *line = psLineAlloc (110);  // 110 is sextractor line length
+
+    FILE *f = fopen (filename, "w");
+    if (f == NULL) {
+        psLogMsg (__func__, 3, "can't open output file for output %s\n", filename);
+        return false;
+    }
+
+    // write sources with models
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+
+        // no difference between PSF and non-PSF model
+        pmModel *model = pmSourceGetModel (NULL, source);
+        if (model == NULL)
+            continue;
+
+        PAR = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+
+        // pmSourceSextractType (source, &type, &flags);
+
+        axes = pmPSF_ModelToAxes (PAR, 20.0);
+
+        psLineInit (line);
+        psLineAdd (line, "%5.2f",  0.0); // should be type
+        psLineAdd (line, "%11.3f", PAR[PM_PAR_XPOS]);
+        psLineAdd (line, "%11.3f", PAR[PM_PAR_YPOS]);
+        psLineAdd (line, "%9.4f",  source->psfMag);
+        psLineAdd (line, "%9.4f",  source->errMag);
+        psLineAdd (line, "%13.4f", source->sky);
+        psLineAdd (line, "%9.2f",  axes.major);
+        psLineAdd (line, "%9.2f",  axes.minor);
+        psLineAdd (line, "%6.1f",  axes.theta);
+        psLineAdd (line, "%9.4f",  source->extMag);
+        psLineAdd (line, "%9.4f",  source->apMag);
+        psLineAdd (line, "%4d\n",  0); // should be flags
+        if (fwrite(line->line, 1, line->Nline, f) < line->Nline) {
+            psError(PS_ERR_IO, true, "Unable to write SX sources file (%s)", filename);
+            fclose(f);
+            psFree(line);
+            return false;
+        }
+    }
+    fclose (f);
+    psFree (line);
+    return true;
+}
+
+// XXX need to fix the FWHM / shape stuff,
+// XXX make sure we are using the correct mags, etc
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePhotometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePhotometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePhotometry.c	(revision 22322)
@@ -0,0 +1,809 @@
+/** @file  pmSourcePhotometry.c
+ *
+ *  @author EAM, IfA; GLG, MHPCC
+ *
+ *  @version $Revision: 1.43 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-07-15 20:25:00 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmErrorCodes.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmModelClass.h"
+#include "pmSourcePhotometry.h"
+
+# define DO_SKY 0
+
+static float AP_MIN_SN = 0.0;
+
+bool pmSourceMagnitudesInit (psMetadata *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    bool status;
+
+    float limit = psMetadataLookupF32 (&status, config, "AP_MIN_SN");
+    if (status) {
+        AP_MIN_SN = limit;
+    }
+    return true;
+}
+
+/**
+    this function is used to calculate the three defined source magnitudes:
+    - apMag  : only if S/N > AP_MIN_SN
+             : is optionally corrected for curve-of-growth if:
+        - the source is a STAR (PSF)
+        - the option is selected (mode & PM_SOURCE_PHOT_GROWTH)
+    - psfMag : all sources with non-NULL modelPSF
+             : is optionally corrected for aperture residual if:
+        - the source is a STAR (PSF)
+        - the option is selected (mode & PM_SOURCE_PHOT_APCORR)
+
+    - extMag : all sources with non-NULL modelEXT
+**/
+
+// XXX masked region should be (optionally) elliptical
+bool pmSourceMagnitudes (pmSource *source, pmPSF *psf, pmSourcePhotometryMode mode, psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(psf, false);
+
+    int status = false;
+    bool isPSF;
+    float x, y;
+    float rflux;
+    float SN;
+    pmModel *model;
+
+    source->psfMag = NAN;
+    source->extMag = NAN;
+    source->errMag = NAN;
+    source->apMag  = NAN;
+
+    // we must have a valid model
+    model = pmSourceGetModel (&isPSF, source);
+    if (model == NULL) {
+        psTrace ("psModules.objects", 3, "fail mag : no valid model");
+        return false;
+    }
+
+    if (model->dparams->data.F32[PM_PAR_I0] > 0) {
+        SN = model->params->data.F32[PM_PAR_I0] / model->dparams->data.F32[PM_PAR_I0];
+        source->errMag = 1.0 / SN;
+    } else {
+        SN = NAN;
+        source->errMag = NAN;
+    }
+    x = model->params->data.F32[PM_PAR_XPOS];
+    y = model->params->data.F32[PM_PAR_YPOS];
+
+    // measure PSF model photometry
+    if (psf->FluxScale) {
+        // the source peak pixel is guaranteed to be on the image, and only minimally different from the source center
+        double fluxScale = pmTrend2DEval (psf->FluxScale, (float)source->peak->x, (float)source->peak->y);
+        if (!isfinite(fluxScale)) {
+            // XXX objects on the edge can be slightly outside -- if we get an
+            // error, use the full fit.
+            psErrorClear();
+            status = pmSourcePhotometryModel (&source->psfMag, source->modelPSF);
+        } else {
+            if (isfinite(fluxScale) && (fluxScale > 0.0)) {
+                source->psfMag = -2.5*log10(fluxScale * source->modelPSF->params->data.F32[PM_PAR_I0]);
+            } else {
+                source->psfMag = NAN;
+            }
+        }
+    } else {
+        status = pmSourcePhotometryModel (&source->psfMag, source->modelPSF);
+    }
+
+    // if we have a collection of model fits, one of them is a pointer to modelEXT?
+    // XXX not guaranteed
+    if (source->modelFits) {
+      for (int i = 0; i < source->modelFits->n; i++) {
+	pmModel *model = source->modelFits->data[i];
+	status = pmSourcePhotometryModel (&model->mag, model);
+      }
+      if (source->modelEXT) {
+	source->extMag = source->modelEXT->mag;
+      }
+    } else {
+      if (source->modelEXT) {
+	status = pmSourcePhotometryModel (&source->extMag, source->modelEXT);
+      }
+    }
+
+    // for PSFs, correct both apMag and psfMag to same system, consistent with infinite flux star in aperture RADIUS
+    if ((mode & PM_SOURCE_PHOT_APCORR) && isPSF && psf && psf->ApTrend) {
+        // the source peak pixel is guaranteed to be on the image, and only minimally different from the source center
+        double apTrend = pmTrend2DEval (psf->ApTrend, (float)source->peak->x, (float)source->peak->y);
+        source->psfMag += apTrend;
+    }
+
+    if (!isfinite(SN) || (SN < AP_MIN_SN)) {
+        psTrace ("psModules.objects", 3, "fail mag : bad SN: %f (limit: %f)", SN, AP_MIN_SN);
+        return false;
+    }
+
+    // replace source flux
+    // XXX need to be certain which model and size of mask for prior subtraction
+    // XXX full model or just analytical?
+    // XXX use pmSourceAdd instead?
+    if (source->mode & PM_SOURCE_MODE_SUBTRACTED) {
+        pmModelAdd (source->pixels, source->maskObj, model, PM_MODEL_OP_FULL, maskVal);
+    }
+
+    // if we are measuring aperture photometry and applying the growth correction,
+    // we need to shift the flux in the selected pixels (but not the mask)
+    // psImageShift ();
+    psImage *flux = NULL, *mask = NULL; // Star flux and mask images, to photometer
+    if (mode & PM_SOURCE_PHOT_INTERP) {
+        float dx = 0.5 - x + (int)x;
+        float dy = 0.5 - y + (int)y;
+        x += dx;
+        y += dy;
+
+        if (!psImageShiftMask(&flux, &mask, source->pixels, source->maskObj, maskVal, dx, dy,
+                              NAN, 0xff, PS_INTERPOLATE_BICUBE)) {
+            // Not much we can do about it
+            psErrorClear();
+            psTrace ("psModules.objects", 3, "fail shift");
+            return false;
+        }
+
+        // XXX this is test code to verify the shift is doing the right thing (seems to be)
+        # if (0)
+            // measure centroid of unshifted gaussian (should be 16.0,16.0)
+        {
+            psImage *image = source->pixels;
+            float xo = 0.0;
+            float yo = 0.0;
+            float xo2 = 0.0;
+            float yo2 = 0.0;
+            float no = 0.0;
+            for (int j = 0; j < image->numRows; j++)
+            {
+                for (int i = 0; i < image->numCols; i++) {
+                    xo += i*image->data.F32[j][i];
+                    yo += j*image->data.F32[j][i];
+                    xo2 += i*i*image->data.F32[j][i];
+                    yo2 += j*j*image->data.F32[j][i];
+                    no += image->data.F32[j][i];
+                }
+            }
+            xo /= no;
+            yo /= no;
+            xo2 = sqrt (xo2/no - xo*xo);
+            yo2 = sqrt (yo2/no - yo*yo);
+            fprintf (stderr, "pre-shift centroid: %f,%f, sigma: %f,%f: flux: %f\n", xo, yo, xo2, yo2, no);
+        }
+
+        // measure centroid of unshifted gaussian (should be 16.0,16.0)
+        {
+            psImage *image = flux;
+            float xo = 0.0;
+            float yo = 0.0;
+            float xo2 = 0.0;
+            float yo2 = 0.0;
+            float no = 0.0;
+            for (int j = 0; j < image->numRows; j++)
+            {
+                for (int i = 0; i < image->numCols; i++) {
+                    xo += i*image->data.F32[j][i];
+                    yo += j*image->data.F32[j][i];
+                    xo2 += i*i*image->data.F32[j][i];
+                    yo2 += j*j*image->data.F32[j][i];
+                    no += image->data.F32[j][i];
+                }
+            }
+            xo /= no;
+            yo /= no;
+            xo2 = sqrt (xo2/no - xo*xo);
+            yo2 = sqrt (yo2/no - yo*yo);
+            fprintf (stderr, "pre-shift centroid: %f,%f, sigma: %f,%f: flux: %f\n", xo, yo, xo2, yo2, no);
+        }
+        # endif
+
+    } else {
+        flux = source->pixels;
+        mask = source->maskObj;
+    }
+
+    // measure the weight of included pixels
+    // XXX is this supposed to use the weight or the flux?
+    if (mode & PM_SOURCE_PHOT_WEIGHT) {
+        pmSourcePixelWeight (&source->pixWeight, model, source->pixels, source->maskObj, maskVal);
+    }
+
+    // measure object aperture photometry
+    status = pmSourcePhotometryAper  (&source->apMag, model, flux, mask, maskVal);
+    if (!status) {
+        psTrace ("psModules.objects", 3, "fail mag : bad Ap Mag");
+    }
+
+    // for PSFs, correct both apMag and psfMag to same system, consistent with infinite flux star in aperture RADIUS
+    // if the aper mag is NAN, the flux < 0.  this can happen for sources near the
+    // detection limits (esp near bright neighbors)
+    if (isfinite (source->apMag) && isPSF && psf) {
+        if (psf->growth && (mode & PM_SOURCE_PHOT_GROWTH)) {
+            source->apMag += pmGrowthCurveCorrect (psf->growth, model->radiusFit);
+        }
+        if (mode & PM_SOURCE_PHOT_APCORR) {
+	    rflux   = pow (10.0, 0.4*source->psfMag);
+	    source->apMag -= PS_SQR(model->radiusFit)*rflux * psf->skyBias + psf->skySat / rflux;
+        }
+    }
+    if (mode & PM_SOURCE_PHOT_INTERP) {
+        psFree(flux);
+        psFree(mask);
+    }
+
+    // if source was originally subtracted, re-subtract object, leave local sky
+    // XXX replace with pmSourceSub...
+    if (source->mode & PM_SOURCE_MODE_SUBTRACTED) {
+        pmModelSub (source->pixels, source->maskObj, model, PM_MODEL_OP_FULL, maskVal);
+    }
+
+    return status;
+}
+
+/*
+aprMag' - fitMag = flux*skySat + r^2*rflux*skyBias + ApTrend(x,y)
+(aprMag - flux*skySat - r^2*rflux*skyBias) - fitMAg = ApTrend(x,y)
+(aprMag - flux*skySat - r^2*rflux*skyBias) = fitMAg + ApTrend(x,y)
+
+*/
+
+// return source model magnitude
+bool pmSourcePhotometryModel (float *fitMag, pmModel *model)
+{
+    PS_ASSERT_PTR_NON_NULL(fitMag, false);
+    if (model == NULL) {
+        return false;
+    }
+
+    float fitSum = 0;
+    *fitMag = NAN;
+
+    // measure fitMag
+    fitSum = model->modelFlux (model->params);
+    if (fitSum <= 0)
+        return false;
+    if (!isfinite(fitSum))
+        return false;
+    *fitMag = -2.5*log10(fitSum);
+
+    return (true);
+}
+
+// return source aperture magnitude
+bool pmSourcePhotometryAper (float *apMag, pmModel *model, psImage *image, psImage *mask, psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(apMag, false);
+    PS_ASSERT_PTR_NON_NULL(image, false);
+    PS_ASSERT_PTR_NON_NULL(mask, false);
+    PS_ASSERT_PTR_NON_NULL(model, false);
+
+    float apSum = 0;
+    float sky = 0;
+    *apMag = NAN;
+
+    if (DO_SKY) {
+        sky = model->params->data.F32[PM_PAR_SKY];
+    } else {
+        sky = 0;
+    }
+
+    psF32 **imData = image->data.F32;
+    psU8 **mkData = mask->data.U8;
+
+    // measure apMag
+    for (int ix = 0; ix < image->numCols; ix++) {
+        for (int iy = 0; iy < image->numRows; iy++) {
+            if (mkData[iy][ix] & maskVal)
+                continue;
+            apSum += imData[iy][ix] - sky;
+        }
+    }
+    if (apSum <= 0) {
+	*apMag = NAN;
+        return true;
+    }
+
+    *apMag = -2.5*log10(apSum);
+    return true;
+}
+
+// return source aperture magnitude
+bool pmSourcePixelWeight (float *pixWeight, pmModel *model, psImage *image, psImage *mask, psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(pixWeight, false);
+    PS_ASSERT_PTR_NON_NULL(image, false);
+    PS_ASSERT_PTR_NON_NULL(mask, false);
+    PS_ASSERT_PTR_NON_NULL(model, false);
+
+    float modelSum = 0;
+    float validSum = 0;
+    float sky = 0;
+    float value;
+
+    int Xo, Yo, dP;
+    int dX, DX, NX;
+    int dY, DY, NY;
+
+    *pixWeight = 0.0;
+
+    // we only care about the value of the object model, not the local sky
+    if (DO_SKY) {
+        sky = model->params->data.F32[PM_PAR_SKY];
+    } else {
+        sky = 0;
+    }
+
+    // the model function returns the source flux at a position
+    psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+
+    psVector *params = model->params;
+
+    Xo = params->data.F32[PM_PAR_XPOS];
+    Yo = params->data.F32[PM_PAR_YPOS];
+
+    dX = Xo - image->col0;
+    dP = image->numCols - dX;
+    DX = PS_MAX(dX, dP);
+    NX = image->numCols;
+
+    dY = Yo - image->row0;
+    dP = image->numRows - dY;
+    DY = PS_MAX(dY, dP);
+    NY = image->numRows;
+
+    // measure modelSum and validSum
+    // XXX this does not work for sources near the edge: we need to measure for
+    // a full square region
+    for (int ix = -DX; ix < DX + 1; ix++) {
+        int mx = ix + dX;
+        for (int iy = -DY; iy < DY + 1; iy++) {
+            int my = iy + dY;
+
+            coord->data.F32[0] = (psF32) (ix + Xo);
+            coord->data.F32[1] = (psF32) (iy + Yo);
+
+            // for the full model, add all points
+            value = model->modelFunc (NULL, params, coord) - sky;
+            modelSum += value;
+
+            // include count only the unmasked pixels within the image area
+            if (mx < 0)
+                continue;
+            if (my < 0)
+                continue;
+            if (mx >= NX)
+                continue;
+            if (my >= NY)
+                continue;
+            if (mask->data.U8[my][mx] & maskVal)
+                continue;
+
+            validSum += value;
+        }
+    }
+    psFree (coord);
+
+    if (validSum <= 0)
+        return false;
+
+    *pixWeight = validSum / modelSum;
+    return (true);
+}
+
+# if (0)
+double pmSourceCrossProduct (const pmSource *Mi,
+                             const pmSource *Mj,
+                             const bool unweighted_sum) // should the cross product be weighted?
+{
+    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
+    PS_ASSERT_PTR_NON_NULL(Mj, NAN);
+
+    int Xs, Xe, Ys, Ye;
+    int xi, xj, yi, yj;
+    int xIs, xJs, yIs, yJs;
+    int xIe, yIe;
+    double flux, wt;
+
+    const psImage *Pi = Mi->pixels;
+    assert (Pi != NULL);
+    const psImage *Pj = Mj->pixels;
+    assert (Pj != NULL);
+
+    const psImage *Wi = Mi->weight;
+    if (!unweighted_sum) {
+        assert (Wi != NULL);
+    }
+
+    const psImage *Ti = Mi->maskObj;
+    assert (Ti != NULL);
+    const psImage *Tj = Mj->maskObj;
+    assert (Tj != NULL);
+
+    Xs = PS_MAX (Pi->col0, Pj->col0);
+    Xe = PS_MIN (Pi->col0 + Pi->numCols, Pj->col0 + Pj->numCols);
+
+    Ys = PS_MAX (Pi->row0, Pj->row0);
+    Ye = PS_MIN (Pi->row0 + Pi->numRows, Pj->row0 + Pj->numRows);
+
+    xIs = Xs - Pi->col0;
+    xJs = Xs - Pj->col0;
+    yIs = Ys - Pi->row0;
+    yJs = Ys - Pj->row0;
+
+    xIe = Xe - Pi->col0;
+    yIe = Ye - Pi->row0;
+
+    // note that this is addressing the same image pixels,
+    // though only if both are source not model images
+    flux = 0;
+    for (yi = yIs, yj = yJs; yi < yIe; yi++, yj++) {
+        for (xi = xIs, xj = xJs; xi < xIe; xi++, xj++) {
+            if (Ti->data.U8[yi][xi])
+                continue;
+            if (Tj->data.U8[yj][xj])
+                continue;
+
+            if (unweighted_sum) {
+                flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]);
+            } else {
+                wt = Wi->data.F32[yi][xi];
+                if (wt > 0) {
+                    flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]) / wt;
+                }
+            }
+        }
+    }
+    return flux;
+}
+
+double pmSourceCrossWeight(const pmSource *Mi,
+                           const pmSource *Mj,
+                           const bool unweighted_sum) // should the cross product be weighted?
+{
+    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
+    PS_ASSERT_PTR_NON_NULL(Mj, NAN);
+
+    int Xs, Xe, Ys, Ye;
+    int xi, xj, yi, yj;
+    int xIs, xJs, yIs, yJs;
+    int xIe, yIe;
+    double flux, wt;
+
+    const psImage *Pi = Mi->pixels;
+    assert (Pi != NULL);
+    const psImage *Pj = Mj->pixels;
+    assert (Pj != NULL);
+
+    const psImage *Wi = Mi->weight;
+    if (!unweighted_sum) {
+        assert (Wi != NULL);
+    }
+
+    const psImage *Ti = Mi->maskObj;
+    assert (Ti != NULL);
+    const psImage *Tj = Mj->maskObj;
+    assert (Tj != NULL);
+
+    Xs = PS_MAX (Pi->col0, Pj->col0);
+    Xe = PS_MIN (Pi->col0 + Pi->numCols, Pj->col0 + Pj->numCols);
+
+    Ys = PS_MAX (Pi->row0, Pj->row0);
+    Ye = PS_MIN (Pi->row0 + Pi->numRows, Pj->row0 + Pj->numRows);
+
+    xIs = Xs - Pi->col0;
+    xJs = Xs - Pj->col0;
+    yIs = Ys - Pi->row0;
+    yJs = Ys - Pj->row0;
+
+    xIe = Xe - Pi->col0;
+    yIe = Ye - Pi->row0;
+
+    // note that this is addressing the same image pixels,
+    // though only if both are source not model images
+    flux = 0;
+    for (yi = yIs, yj = yJs; yi < yIe; yi++, yj++) {
+        for (xi = xIs, xj = xJs; xi < xIe; xi++, xj++) {
+            if (Ti->data.U8[yi][xi])
+                continue;
+            if (Tj->data.U8[yj][xj])
+                continue;
+
+            if (unweighted_sum) {
+                flux++;
+            } else {
+                wt = Wi->data.F32[yi][xi];
+                if (wt > 0) {
+                    flux += 1.0 / wt;
+                }
+            }
+        }
+    }
+    return flux;
+}
+
+double pmSourceWeight(const pmSource *Mi,
+                      int term,
+                      const bool unweighted_sum) // should the cross product be weighted?
+{
+    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
+    double flux = 0, wt = 0, factor = 0;
+
+    const psImage *Pi = Mi->pixels;
+    assert (Pi != NULL);
+    const psImage *Wi = Mi->weight;
+    if (!unweighted_sum) {
+        assert (Wi != NULL);
+    }
+    const psImage *Ti = Mi->maskObj;
+    assert (Ti != NULL);
+
+    // note that this is addressing the same image pixels,
+    // though only if both are source not model images
+    for (int yi = 0; yi < Pi->numRows; yi++) {
+        for (int xi = 0; xi < Pi->numCols; xi++) {
+            if (Ti->data.U8[yi][xi])
+                continue;
+            if (!unweighted_sum) {
+                wt = Wi->data.F32[yi][xi];
+                if (wt == 0)
+                    continue;
+            }
+
+            switch (term) {
+            case 0:
+                factor = 1;
+                break;
+            case 1:
+                factor = xi + Pi->col0;
+                break;
+            case 2:
+                factor = yi + Pi->row0;
+                break;
+            default:
+                psAbort("invalid term for pmSourceWeight");
+            }
+
+            if (unweighted_sum) {
+                flux += (factor * Pi->data.F32[yi][xi]);
+            } else {
+                flux += (factor * Pi->data.F32[yi][xi]) / wt;
+            }
+            // fprintf (stderr, "Pi: %f, flux: %f\n", Pi->data.F32[yi][xi], flux);
+        }
+    }
+    return flux;
+}
+# endif
+
+bool pmSourceChisq (pmModel *model, psImage *image, psImage *mask, psImage *weight,
+                    psMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(model, false);
+    PS_ASSERT_PTR_NON_NULL(image, false);
+    PS_ASSERT_PTR_NON_NULL(mask, false);
+    PS_ASSERT_PTR_NON_NULL(weight, false);
+
+    double dC = 0.0;
+    int Npix = 0;
+    for (int j = 0; j < image->numRows; j++) {
+        for (int i = 0; i < image->numCols; i++) {
+            if (mask->data.U8[j][i] & maskVal)
+                continue;
+            if (weight->data.F32[j][i] <= 0)
+                continue;
+            dC += PS_SQR (image->data.F32[j][i]) / weight->data.F32[j][i];
+            Npix ++;
+        }
+    }
+    model->nDOF = Npix - 1;
+    model->chisq = dC;
+
+    return (true);
+}
+
+
+double pmSourceModelWeight(const pmSource *Mi,
+                      int term,
+                      const bool unweighted_sum) // should the cross product be weighted?
+{
+    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
+    double flux = 0, wt = 0, factor = 0;
+
+    const psImage *Pi = Mi->modelFlux;
+    assert (Pi != NULL);
+    const psImage *Wi = Mi->weight;
+    if (!unweighted_sum) {
+        assert (Wi != NULL);
+    }
+    const psImage *Ti = Mi->maskObj;
+    assert (Ti != NULL);
+
+    for (int yi = 0; yi < Pi->numRows; yi++) {
+        for (int xi = 0; xi < Pi->numCols; xi++) {
+            if (Ti->data.U8[yi][xi])
+                continue;
+            if (!unweighted_sum) {
+                wt = Wi->data.F32[yi][xi];
+                if (wt == 0)
+                    continue;
+            }
+
+            switch (term) {
+            case 0:
+                factor = 1;
+                break;
+            case 1:
+                factor = xi + Pi->col0;
+                break;
+            case 2:
+                factor = yi + Pi->row0;
+                break;
+            default:
+                psAbort("invalid term for pmSourceWeight");
+            }
+
+            if (unweighted_sum) {
+                flux += (factor * Pi->data.F32[yi][xi]);
+            } else {
+                flux += (factor * Pi->data.F32[yi][xi]) / wt;
+            }
+        }
+    }
+    return flux;
+}
+
+double pmSourceModelDotModel (const pmSource *Mi,
+                              const pmSource *Mj,
+                              const bool unweighted_sum) // should the cross product be weighted?
+{
+    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
+    PS_ASSERT_PTR_NON_NULL(Mj, NAN);
+    int Xs, Xe, Ys, Ye;
+    int xi, xj, yi, yj;
+    int xIs, xJs, yIs, yJs;
+    int xIe, yIe;
+    double flux, wt;
+
+    const psImage *Pi = Mi->modelFlux;
+    assert (Pi != NULL);
+    const psImage *Pj = Mj->modelFlux;
+    assert (Pj != NULL);
+
+    const psImage *Wi = Mi->weight;
+    if (!unweighted_sum) {
+        assert (Wi != NULL);
+    }
+
+    const psImage *Ti = Mi->maskObj;
+    assert (Ti != NULL);
+    const psImage *Tj = Mj->maskObj;
+    assert (Tj != NULL);
+
+    Xs = PS_MAX (Pi->col0, Pj->col0);
+    Xe = PS_MIN (Pi->col0 + Pi->numCols, Pj->col0 + Pj->numCols);
+
+    Ys = PS_MAX (Pi->row0, Pj->row0);
+    Ye = PS_MIN (Pi->row0 + Pi->numRows, Pj->row0 + Pj->numRows);
+
+    xIs = Xs - Pi->col0;
+    xJs = Xs - Pj->col0;
+    yIs = Ys - Pi->row0;
+    yJs = Ys - Pj->row0;
+
+    xIe = Xe - Pi->col0;
+    yIe = Ye - Pi->row0;
+
+    // note that weight is addressing the same image pixels
+    flux = 0;
+    for (yi = yIs, yj = yJs; yi < yIe; yi++, yj++) {
+        for (xi = xIs, xj = xJs; xi < xIe; xi++, xj++) {
+            if (Ti->data.U8[yi][xi])
+                continue;
+            if (Tj->data.U8[yj][xj])
+                continue;
+
+            // XXX skip the nonsense weight pixels?
+            if (unweighted_sum) {
+                flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]);
+            } else {
+                wt = Wi->data.F32[yi][xi];
+                if (wt > 0) {
+                    flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]) / wt;
+                }
+            }
+        }
+    }
+    return flux;
+}
+
+double pmSourceDataDotModel (const pmSource *Mi,
+                             const pmSource *Mj,
+                             const bool unweighted_sum) // should the cross product be weighted?
+{
+    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
+    PS_ASSERT_PTR_NON_NULL(Mj, NAN);
+    int Xs, Xe, Ys, Ye;
+    int xi, xj, yi, yj;
+    int xIs, xJs, yIs, yJs;
+    int xIe, yIe;
+    double flux, wt;
+
+    const psImage *Pi = Mi->pixels;
+    assert (Pi != NULL);
+    const psImage *Pj = Mj->modelFlux;
+    assert (Pj != NULL);
+
+    const psImage *Wi = Mi->weight;
+    if (!unweighted_sum) {
+        assert (Wi != NULL);
+    }
+
+    const psImage *Ti = Mi->maskObj;
+    assert (Ti != NULL);
+    const psImage *Tj = Mj->maskObj;
+    assert (Tj != NULL);
+
+    Xs = PS_MAX (Pi->col0, Pj->col0);
+    Xe = PS_MIN (Pi->col0 + Pi->numCols, Pj->col0 + Pj->numCols);
+
+    Ys = PS_MAX (Pi->row0, Pj->row0);
+    Ye = PS_MIN (Pi->row0 + Pi->numRows, Pj->row0 + Pj->numRows);
+
+    xIs = Xs - Pi->col0;
+    xJs = Xs - Pj->col0;
+    yIs = Ys - Pi->row0;
+    yJs = Ys - Pj->row0;
+
+    xIe = Xe - Pi->col0;
+    yIe = Ye - Pi->row0;
+
+    // note that weight is addressing the same image pixels,
+    flux = 0;
+    for (yi = yIs, yj = yJs; yi < yIe; yi++, yj++) {
+        for (xi = xIs, xj = xJs; xi < xIe; xi++, xj++) {
+            if (Ti->data.U8[yi][xi])
+                continue;
+            if (Tj->data.U8[yj][xj])
+                continue;
+
+            // XXX skip the nonsense weight pixels?
+            if (unweighted_sum) {
+                flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]);
+            } else {
+                wt = Wi->data.F32[yi][xi];
+                if (wt > 0) {
+                    flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]) / wt;
+                }
+            }
+        }
+    }
+    return flux;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePhotometry.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePhotometry.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePhotometry.h	(revision 22322)
@@ -0,0 +1,67 @@
+/* @file  pmSourcePhotometry.h
+ * @brief functions to measure source photometry
+ *
+ * @author EAM, IfA; GLG, MHPCC
+ *
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-07-15 20:25:00 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_SOURCE_PHOTOMETRY_H
+# define PM_SOURCE_PHOTOMETRY_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/**
+ *
+ * The function returns both the magnitude of the fit, defined as -2.5log(flux),
+ * where the flux is integrated under the model, theoretically from a radius of 0
+ * to infinity. In practice, we integrate the model beyond 50sigma.  The aperture magnitude is
+ * defined as -2.5log(flux) , where the flux is summed for all pixels which are
+ * not excluded by the aperture mask. The model flux is calculated by calling the
+ * model-specific function provided by pmModelFlux_GetFunction.
+ *
+ * XXX: must code this.
+ *
+ */
+
+typedef enum {
+    PM_SOURCE_PHOT_NONE   = 0x0000,
+    PM_SOURCE_PHOT_GROWTH = 0x0001,
+    PM_SOURCE_PHOT_APCORR = 0x0002,
+    PM_SOURCE_PHOT_WEIGHT = 0x0004,
+    PM_SOURCE_PHOT_INTERP = 0x0008,
+} pmSourcePhotometryMode;
+
+bool pmSourcePhotometryModel(
+    float *fitMag,                      ///< integrated fit magnitude
+    pmModel *model                      ///< model used for photometry
+);
+
+bool pmSourcePhotometryAper(
+    float   *apMag,                     ///< aperture flux magnitude
+    pmModel *model,                     ///< model used for photometry
+    psImage *image,                     ///< image pixels to be used
+    psImage *mask,                      ///< mask of pixels to ignore
+    psMaskType maskVal                  ///< Value to mask
+);
+
+bool pmSourceMagnitudesInit (psMetadata *config);
+bool pmSourceMagnitudes (pmSource *source, pmPSF *psf, pmSourcePhotometryMode mode, psMaskType maskVal);
+bool pmSourcePixelWeight (float *pixWeight, pmModel *model, psImage *image, psImage *mask, psMaskType maskVal);
+bool pmSourceChisq (pmModel *model, psImage *image, psImage *mask, psImage *weight, psMaskType maskVal);
+
+
+double pmSourceDataDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum);
+double pmSourceModelDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum);
+double pmSourceModelWeight(const pmSource *Mi, int term, const bool unweighted_sum);
+
+// retire these:
+// double pmSourceCrossProduct(const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum);
+// double pmSourceCrossWeight(const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum);
+// double pmSourceWeight(const pmSource *Mi, int term, const bool unweighted_sum);
+
+/// @}
+# endif /* PM_SOURCE_PHOTOMETRY_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlotApResid.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlotApResid.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlotApResid.c	(revision 22322)
@@ -0,0 +1,163 @@
+/** @file  pmSourcePlot.c
+ *
+ *  Plot the Aperture Mag - Fitted Mag residuals
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-10 01:09:20 $
+ *  Copyright 2006 IfA, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmSourcePlots.h"
+#include "pmKapaPlots.h"
+
+// this variable is defined in psmodules.h if ohana-config is found
+# if (HAVE_KAPA)
+    # include <kapa.h>
+
+// plot the sx, sy, sxy as vector field,
+// plot the PSF measured sx, sy, sxy as vector field
+// pull the sources from the config / file?
+bool pmSourcePlotApResid (const pmFPAview *view, pmFPAfile *file, const pmConfig *config,
+                          pmSourcePlotLayout *layout)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(layout, false);
+
+    Graphdata graphdata;
+    KapaSection section;
+
+    psLogMsg ("psphot", 3, "creating ap mag - psf mag plot");
+
+    // find the currently selected readout
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
+
+    psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
+    if (sources == NULL)
+        return false;
+
+    int kapa = pmKapaOpen (true);
+    if (kapa == -1) {
+        psError(PS_ERR_UNKNOWN, true, "failure to open kapa");
+        return false;
+    }
+
+    int DX = 1000;
+    float dx = DX / layout->nX;
+    float dy = dx * layout->aspectRatio;
+    int DY = dy * layout->nY;
+
+    // XXX make the aspect-ratio match the image
+    if (layout->i == 0) {
+        KapaResize (kapa, DX, DY);
+    }
+
+    KapaClearPlots (kapa);
+    KapaInitGraph (&graphdata);
+    section.dx = dx / DX;
+    section.dy = dy / DY;
+    section.x = layout->iX * section.dx;
+    section.y = layout->iY * section.dy;
+    section.name = NULL;
+    psStringAppend (&section.name, "a%d", layout->i);
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    psVector *x = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+    psVector *y = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+
+    graphdata.xmin = +32.0;
+    graphdata.xmax = -32.0;
+    graphdata.ymin = +32.0;
+    graphdata.ymax = -32.0;
+
+    // construct the plot vectors
+    int n = 0;
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+	if (!source) continue;
+        if (source->type != PM_SOURCE_TYPE_STAR) continue;
+	if (!isfinite (source->apMag)) continue;
+	if (!isfinite (source->psfMag)) continue;
+
+        x->data.F32[n] = source->psfMag;
+        y->data.F32[n] = source->apMag - source->psfMag;
+        graphdata.xmin = PS_MIN(graphdata.xmin, x->data.F32[n]);
+        graphdata.xmax = PS_MAX(graphdata.xmax, x->data.F32[n]);
+        graphdata.ymin = PS_MIN(graphdata.ymin, y->data.F32[n]);
+        graphdata.ymax = PS_MAX(graphdata.ymax, y->data.F32[n]);
+
+        n++;
+    }
+    x->n = y->n = n;
+
+    float range;
+    range = graphdata.xmax - graphdata.xmin;
+    graphdata.xmax += 0.05*range;
+    graphdata.xmin -= 0.05*range;
+    range = graphdata.ymax - graphdata.ymin;
+    graphdata.ymax += 0.05*range;
+    graphdata.ymin -= 0.05*range;
+
+    // XXX set the plot range to match the image
+    KapaSetLimits (kapa, &graphdata);
+
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, &graphdata);
+    KapaSendLabel (kapa, "PSF Mag", KAPA_LABEL_XM);
+    KapaSendLabel (kapa, "Ap Mag - PSF Mag", KAPA_LABEL_YM);
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 2;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, n, &graphdata);
+    KapaPlotVector (kapa, n, x->data.F32, "x");
+    KapaPlotVector (kapa, n, y->data.F32, "y");
+
+    if (layout->i == layout->nTotal - 1) {
+        psLogMsg ("psphot", 3, "saving plot to %s", file->filename);
+        KapaPNG (kapa, file->filename);
+	KapaClearPlots (kapa);
+    }
+
+    psFree (x);
+    psFree (y);
+    return true;
+}
+
+# else
+
+bool pmSourcePlotApResid (const pmFPAview *view, pmFPAfile *file, const pmConfig *config, pmSourcePlotLayout *layout)
+{
+    psLogMsg ("psphot", 3, "skipping ap-mag resid plot");
+    return true;
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlotMoments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlotMoments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlotMoments.c	(revision 22322)
@@ -0,0 +1,176 @@
+/** @file  pmSourcePlot.c
+ *
+ * This file contains functions to write source plots
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-07-15 20:25:00 $
+ *
+ *  Copyright 2006 IfA, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmSourcePlots.h"
+#include "pmKapaPlots.h"
+
+// this variable is defined in psmodules.h if ohana-config is found
+# if (HAVE_KAPA)
+# include <kapa.h>
+
+// plot the sx, sy moments plane (faint and bright sources)
+bool pmSourcePlotMoments (const pmFPAview *view, pmFPAfile *file, const pmConfig *config,
+                          pmSourcePlotLayout *layout)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(layout, false);
+
+    Graphdata graphdata;
+    KapaSection section;
+
+    psLogMsg ("psphot", 3, "creating moments plot");
+
+    // find the currently selected readout
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
+
+    psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
+    if (sources == NULL)
+        return false;
+
+    int kapa = pmKapaOpen (false);
+    if (kapa == -1) {
+        psError(PS_ERR_UNKNOWN, true, "failure to open kapa");
+        return false;
+    }
+
+    // moments plot subplots are square
+    int DX = 1000;
+    int dx = DX / layout->nX;
+    int dy = dx;
+    int DY = dy * layout->nY;
+
+    // XXX make the aspect-ratio match the image
+    if (layout->i == 0) {
+        KapaResize (kapa, DX, DY);
+    }
+
+    KapaClearPlots (kapa);
+    KapaInitGraph (&graphdata);
+    section.dx = dx / (float) DX;
+    section.dy = dy / (float) DY;
+    section.x = layout->iX * section.dx;
+    section.y = layout->iY * section.dy;
+    section.name = NULL;
+    psStringAppend (&section.name, "a%d", layout->i);
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    // examine sources to set data range
+    graphdata.xmin = -0.05;
+    graphdata.ymin = -0.05;
+    graphdata.xmax = +4.05;
+    graphdata.ymax = +4.05;
+    KapaSetLimits (kapa, &graphdata);
+
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, &graphdata);
+    KapaSendLabel (kapa, "&ss&h_x| (pixels)", KAPA_LABEL_XM);
+    KapaSendLabel (kapa, "&ss&h_y| (pixels)", KAPA_LABEL_YM);
+
+    psVector *xBright = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+    psVector *yBright = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+    psVector *xFaint  = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+    psVector *yFaint  = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+
+    // construct the vectors
+    int nB = 0;
+    int nF = 0;
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+        if (source->moments == NULL)
+            continue;
+
+        xFaint->data.F32[nF] = source->moments->Sx;
+        yFaint->data.F32[nF] = source->moments->Sy;
+        nF++;
+
+        // XXX make this a user-defined cutoff
+        if (source->moments->SN < 50)
+            continue;
+
+        xBright->data.F32[nB] = source->moments->Sx;
+        yBright->data.F32[nB] = source->moments->Sy;
+        nB++;
+    }
+    xFaint->n = nF;
+    yFaint->n = nF;
+
+    xBright->n = nB;
+    yBright->n = nB;
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 0;
+    graphdata.size = 0.3;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, nF, &graphdata);
+    KapaPlotVector (kapa, nF, xFaint->data.F32, "x");
+    KapaPlotVector (kapa, nF, yFaint->data.F32, "y");
+
+    graphdata.color = KapaColorByName ("red");
+    graphdata.ptype = 2;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, nB, &graphdata);
+    KapaPlotVector (kapa, nB, xBright->data.F32, "x");
+    KapaPlotVector (kapa, nB, yBright->data.F32, "y");
+
+    if (layout->i == layout->nTotal - 1) {
+        psLogMsg ("psphot", 3, "saving plot to %s", file->filename);
+        KapaPNG (kapa, file->filename);
+	KapaClearPlots (kapa);
+    }
+
+    psFree (xBright);
+    psFree (yBright);
+    psFree (xFaint);
+    psFree (yFaint);
+
+    return true;
+}
+
+
+# else
+
+    bool pmSourcePlotMoments (const pmFPAview *view, pmFPAfile *file, const pmConfig *config, pmSourcePlotLayout *layout)
+{
+    psLogMsg ("psphot", 3, "skipping moments plot");
+    return true;
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlotPSFModel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlotPSFModel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlotPSFModel.c	(revision 22322)
@@ -0,0 +1,237 @@
+/** @file  pmSourcePlot.c
+ *
+ * This file contains functions to write source plots
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-10 01:09:20 $
+ *
+ *  Copyright 2006 IfA, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmGrowthCurve.h"
+#include "pmResiduals.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmSourcePlots.h"
+#include "pmKapaPlots.h"
+
+// this variable is defined in psmodules.h if ohana-config is found
+# if (HAVE_KAPA)
+# include <kapa.h>
+
+// plot the sx, sy, sxy as vector field,
+// plot the PSF measured sx, sy, sxy as vector field
+// pull the sources from the config / file?
+bool pmSourcePlotPSFModel (const pmFPAview *view, pmFPAfile *file, const pmConfig *config,
+                           pmSourcePlotLayout *layout)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(layout, false);
+
+    Graphdata graphdata;
+    KapaSection section;
+
+    psLogMsg ("psphot", 3, "creating psf model plot");
+
+    // find the currently selected readout
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
+
+    psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
+    if (sources == NULL)
+        return false;
+
+    int kapa = pmKapaOpen (false);
+    if (kapa == -1) {
+        psError(PS_ERR_UNKNOWN, true, "failure to open kapa");
+        return false;
+    }
+
+    int DX = 1000;
+    float dx = DX / layout->nX;
+    float dy = dx * layout->aspectRatio;
+    int DY = dy * layout->nY;
+
+    // XXX make the aspect-ratio match the image
+    if (layout->i == 0) {
+        KapaResize (kapa, DX, DY);
+    }
+    
+    KapaClearPlots (kapa);
+    KapaInitGraph (&graphdata);
+    section.dx = dx / DX;
+    section.dy = dy / DY;
+    section.x = layout->iX * section.dx;
+    section.y = layout->iY * section.dy;
+    section.name = NULL;
+    psStringAppend (&section.name, "a%d", layout->i);
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    psVector *xMNT = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+    psVector *yMNT = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+    psVector *xPSF = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+    psVector *yPSF = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+    psVector *xMIN = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+    psVector *yMIN = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+
+    // construct the plot vectors
+    int nMNT = 0;
+    int nPSF = 0;
+    int nMIN = 0;
+    dx = 0;
+    dy = 0;
+    float scale = 10;
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+        if (source->moments == NULL)
+            continue;
+        if (source->moments->SN < 25)
+            continue;
+        if (source->type != PM_SOURCE_TYPE_STAR)
+            continue;
+
+        pmModel *model = source->modelPSF;
+        if (model == NULL)
+            continue;
+
+        psF32 *PAR = model->params->data.F32;
+
+        psEllipseMoments moments;
+        moments.x2 = source->moments->Sx;
+        moments.y2 = source->moments->Sy;
+        moments.xy = source->moments->Sxy;
+
+        // force the axis ratio to be < 20.0
+        psEllipseAxes axes_mnt = psEllipseMomentsToAxes (moments, 20.0);
+        psEllipseAxes axes_psf = pmPSF_ModelToAxes (PAR, 20.0);
+
+        // moments major axis
+        dx = scale*axes_mnt.major*cos(axes_mnt.theta);
+        dy = scale*axes_mnt.major*sin(axes_mnt.theta);
+        xMNT->data.F32[nMNT] = PAR[PM_PAR_XPOS] - dx;
+        yMNT->data.F32[nMNT] = PAR[PM_PAR_YPOS] - dy;
+        nMNT++;
+        xMNT->data.F32[nMNT] = PAR[PM_PAR_XPOS] + dx;
+        yMNT->data.F32[nMNT] = PAR[PM_PAR_YPOS] + dy;
+        nMNT++;
+
+        // psf major axis
+        dx = scale*axes_psf.major*cos(axes_psf.theta);
+        dy = scale*axes_psf.major*sin(axes_psf.theta);
+        xPSF->data.F32[nPSF] = PAR[PM_PAR_XPOS] - dx;
+        yPSF->data.F32[nPSF] = PAR[PM_PAR_YPOS] - dy;
+        nPSF++;
+        xPSF->data.F32[nPSF] = PAR[PM_PAR_XPOS] + dx;
+        yPSF->data.F32[nPSF] = PAR[PM_PAR_YPOS] + dy;
+        nPSF++;
+
+        // minor axis (to show size)
+        dy = +scale*axes_psf.minor*cos(axes_psf.theta);
+        dx = -scale*axes_psf.minor*sin(axes_psf.theta);
+        xMIN->data.F32[nMIN] = PAR[PM_PAR_XPOS] - dx;
+        yMIN->data.F32[nMIN] = PAR[PM_PAR_YPOS] - dy;
+        nMIN++;
+        xMIN->data.F32[nMIN] = PAR[PM_PAR_XPOS] + dx;
+        yMIN->data.F32[nMIN] = PAR[PM_PAR_YPOS] + dy;
+        nMIN++;
+
+        graphdata.xmin = PS_MIN(graphdata.xmin, PAR[PM_PAR_XPOS]);
+        graphdata.xmax = PS_MAX(graphdata.xmax, PAR[PM_PAR_XPOS]);
+        graphdata.ymin = PS_MIN(graphdata.ymin, PAR[PM_PAR_YPOS]);
+        graphdata.ymax = PS_MAX(graphdata.ymax, PAR[PM_PAR_YPOS]);
+    }
+    xMNT->n = yMNT->n = nMNT;
+    xPSF->n = yPSF->n = nPSF;
+    xMIN->n = yMIN->n = nMIN;
+
+    float range;
+    range = graphdata.xmax - graphdata.xmin;
+    graphdata.xmax += 0.05*range;
+    graphdata.xmin -= 0.05*range;
+    range = graphdata.ymax - graphdata.ymin;
+    graphdata.ymax += 0.05*range;
+    graphdata.ymin -= 0.05*range;
+
+    // XXX set the plot range to match the image
+    KapaSetLimits (kapa, &graphdata);
+
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, &graphdata);
+    KapaSendLabel (kapa, "x (pixels)", KAPA_LABEL_XM);
+    KapaSendLabel (kapa, "y (pixels)", KAPA_LABEL_YM);
+    KapaSendLabel (kapa, "vector is major axis (scaled by 20) : black are moments, blue are psf model, red is psf minor axis", KAPA_LABEL_XP);
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 100;
+    graphdata.size = 0.3;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, nMNT, &graphdata);
+    KapaPlotVector (kapa, nMNT, xMNT->data.F32, "x");
+    KapaPlotVector (kapa, nMNT, yMNT->data.F32, "y");
+
+    graphdata.color = KapaColorByName ("blue");
+    graphdata.ptype = 100;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, nPSF, &graphdata);
+    KapaPlotVector (kapa, nPSF, xPSF->data.F32, "x");
+    KapaPlotVector (kapa, nPSF, yPSF->data.F32, "y");
+
+    graphdata.color = KapaColorByName ("red");
+    graphdata.ptype = 100;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, nMIN, &graphdata);
+    KapaPlotVector (kapa, nMIN, xMIN->data.F32, "x");
+    KapaPlotVector (kapa, nMIN, yMIN->data.F32, "y");
+
+    if (layout->i == layout->nTotal - 1) {
+        psLogMsg ("psphot", 3, "saving plot to %s", file->filename);
+        KapaPNG (kapa, file->filename);
+	KapaClearPlots (kapa);
+    }
+
+    psFree (xMNT);
+    psFree (yMNT);
+    psFree (xPSF);
+    psFree (yPSF);
+    psFree (xMIN);
+    psFree (yMIN);
+
+    return true;
+}
+
+# else
+
+    bool pmSourcePlotPSFModel (const pmFPAview *view, pmFPAfile *file, const pmConfig *config, pmSourcePlotLayout *layout)
+{
+    psLogMsg ("psphot", 3, "skipping psf model plot");
+    return true;
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlots.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlots.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlots.c	(revision 22322)
@@ -0,0 +1,178 @@
+/** @file  pmSourcePlot.c
+ *
+ * This file contains functions to write source plots
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-10 01:09:20 $
+ *
+ *  Copyright 2006 IfA, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmSourcePlots.h"
+
+// this function is called for the specific plotting program at the fileLevel
+// fileLevel must be >= chip
+bool pmFPAviewWriteSourcePlot(const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    if ((view->readout != -1) || (view->cell != -1)) {
+        psError(PS_ERR_UNKNOWN, true, "source plots must have fileLevel >= chip");
+        return false;
+    }
+
+    pmFPA *fpa = file->fpa;
+
+    if (view->chip == -1) {
+        pmFPAWriteSourcePlot (fpa, view, file, config, NULL);
+        return true;
+    }
+
+    pmChipWriteSourcePlot (fpa, view, file, config, NULL);
+    return true;
+}
+
+// read in all chip-level SourcePlot files for this FPA
+bool pmFPAWriteSourcePlot (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config, pmSourcePlotLayout *layout)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    // if the layout is not defined, this level is the top level being plotted
+    // determine the plot layout
+    if (!layout) {
+        layout = pmSourcePlotLayoutAlloc ();
+        // XXX temporary hardwired values for essence?
+        layout->nX = 4;
+        layout->nY = 2;
+        layout->aspectRatio = 1.0;
+        // count the number of chips and their layout (xrange, yrange, axis ratio)
+        for (int i = 0; i < fpa->chips->n; i++) {
+            pmChip *chip = fpa->chips->data[i];
+            if (chip->data_exists) {
+                layout->nTotal ++;
+            }
+        }
+    }
+
+    pmFPAview *chipView = pmFPAviewAlloc(0);
+
+    while (pmFPAviewNextChip (chipView, fpa, 1) != NULL) {
+        pmChipWriteSourcePlot (fpa, chipView, file, config, layout);
+        layout->i ++;
+        layout->iX ++;
+        if (layout->iX == layout->nX) {
+            layout->iY++;
+            layout->iX = 0;
+        }
+    }
+    psFree (chipView);
+    psFree (layout);
+    return true;
+}
+
+// read in all cell-level SourcePlot files for this chip
+bool pmChipWriteSourcePlot (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config, pmSourcePlotLayout *layout)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    // if the layout is not defined, this level is the top level being plotted
+    // determine the plot layout (always single chip)
+    if (!layout) {
+        layout = pmSourcePlotLayoutAlloc ();
+        layout->nTotal = 1;
+        layout->nX = 1;
+        layout->nY = 1;
+        layout->aspectRatio = 1.0;
+    } else {
+        psMemIncrRefCounter (layout);
+    }
+
+    pmFPAview *newView = pmFPAviewAlloc(0);
+    newView->chip = view->chip;
+
+    while (pmFPAviewNextCell (newView, fpa, 1) != NULL) {
+        while (pmFPAviewNextReadout (newView, fpa, 1) != NULL) {
+
+            if (!strcmp (file->name, "SOURCE.PLOT.PSFMODEL")) {
+                pmSourcePlotPSFModel (newView, file, config, layout);
+            }
+            if (!strcmp (file->name, "SOURCE.PLOT.MOMENTS")) {
+                pmSourcePlotMoments (newView, file, config, layout);
+            }
+            if (!strcmp (file->name, "SOURCE.PLOT.APRESID")) {
+                pmSourcePlotApResid (newView, file, config, layout);
+            }
+        }
+    }
+    psFree (newView);
+    psFree (layout);
+    return true;
+}
+
+static void pmSourcePlotLayoutFree(pmSourcePlotLayout *layout)
+{
+    return;
+}
+
+pmSourcePlotLayout *pmSourcePlotLayoutAlloc()
+{
+    pmSourcePlotLayout *layout = (pmSourcePlotLayout *)psAlloc(sizeof(pmSourcePlotLayout));
+    psMemSetDeallocator(layout, (psFreeFunc)pmSourcePlotLayoutFree );
+
+    layout->nX = layout->nY = layout->nTotal = 0;
+    layout->iX = layout->iY = layout->i  =  0;
+    layout->aspectRatio = 1.0;
+    return (layout);
+}
+
+bool psMemCheckSourcePlotLayout(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourcePlotLayoutFree);
+}
+
+
+/* we have three types of plots to produce (maybe more?)
+ * for each plot, we need to supply the following information:
+ *     - what are overall dimensions?
+ *     - what is the relative position of this element?
+ *     - what are the dimensions of this element?
+ *     - create a new page for this element?
+
+ * the answers to these questions come from slightly different locations
+ * for the different types of plots:
+
+ *  1) focal-plane layout based plots:
+ *     - dimensions depend on fileLevel
+
+ *  2) multi-row plots:
+ *     - dimensions depend on total number of rows
+
+ *  2) multi-page plots:
+ *     - dimensions depend on total number of rows
+ */
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlots.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlots.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourcePlots.h	(revision 22322)
@@ -0,0 +1,42 @@
+/* @file  pmSourcePlots.h
+ * @brief functions to create plots illustrating source properties
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-10 01:09:20 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_SOURCE_PLOTS_H
+#define PM_SOURCE_PLOTS_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+typedef struct
+{
+    int nX;
+    int nY;
+    int nTotal;
+    int iX;
+    int iY;
+    int i;
+    float aspectRatio;
+}
+pmSourcePlotLayout;
+
+// typedef bool (*pmSourcePlotFunction)(pmConfig *config, pmFPAview *view, pmSourcePlotLayout *layout);
+
+pmSourcePlotLayout *pmSourcePlotLayoutAlloc();
+bool psMemCheckSourcePlotLayout(psPtr ptr);
+
+bool pmFPAviewWriteSourcePlot(const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmFPAWriteSourcePlot (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config, pmSourcePlotLayout *layout);
+bool pmChipWriteSourcePlot (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config, pmSourcePlotLayout *layout);
+bool pmSourcePlotPSFModel (const pmFPAview *view, pmFPAfile *file, const pmConfig *config, pmSourcePlotLayout *layout);
+bool pmSourcePlotMoments (const pmFPAview *view, pmFPAfile *file, const pmConfig *config, pmSourcePlotLayout *layout);
+bool pmSourcePlotApResid (const pmFPAview *view, pmFPAfile *file, const pmConfig *config, pmSourcePlotLayout *layout);
+
+/// @}
+#endif // PM_SOURCE_PLOTS_H
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceSky.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceSky.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceSky.c	(revision 22322)
@@ -0,0 +1,156 @@
+/** @file  pmSourceSky.c
+ *
+ *  Functions to measure the local sky and sky variance for sources on images
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA: significant modifications.
+ *
+ *  @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-07-15 20:25:00 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmSourceSky.h"
+
+/******************************************************************************
+pmSource *pmSourceLocalSky(source, statsOptions, Radius): this
+routine creates a new pmSource.moments element if needed and sets pmSource.pmMoments.sky
+
+The sky value is set from the pixels in the square annulus surrounding the
+peak pixel.
+
+The source.pixels and source.mask must already exist
+
+This function modifies the source mask; it should only be called before the object aperture is defined
+*****************************************************************************/
+
+bool pmSourceLocalSky(
+    pmSource *source,
+    psStatsOptions statsOptions,
+    psF32 Radius,
+    psMaskType maskVal,
+    psMaskType markVal)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_IMAGE_NON_NULL(source->pixels, false);
+    PS_ASSERT_IMAGE_NON_NULL(source->maskObj, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_INT_POSITIVE(Radius, false);
+
+    psStatsOptions statistic = psStatsSingleOption(statsOptions);
+    if (statistic == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Multiple or no statistics specified: %x\n", statsOptions);
+        return NULL;
+    }
+
+    psImage *image = source->pixels;
+    psImage *mask  = source->maskObj;
+    pmPeak *peak  = source->peak;
+    psRegion srcRegion;
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    srcRegion = psRegionForSquare(peak->x, peak->y, Radius);
+    srcRegion = psRegionForImage(mask, srcRegion);
+
+    psImageMaskRegion(mask, srcRegion, "OR", markVal);
+    psStats *myStats = psStatsAlloc(statsOptions);
+    if (!psImageStats(myStats, image, mask, maskVal)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to get image statistics.\n");
+        psFree(myStats);
+        return false;
+    }
+    psImageMaskRegion(mask, srcRegion, "AND", PS_NOT_U8(markVal));
+    double value = psStatsGetValue(myStats, statistic);
+    psFree(myStats);
+
+    if (isnan(value)) {
+        psTrace("psModules.objects", 3, "---- %s(false) end ----\n", __func__);
+        return(false);
+    }
+    if (source->moments == NULL) {
+        source->moments = pmMomentsAlloc();
+    }
+    source->moments->Sky = value;
+    psTrace("psModules.objects", 3, "---- %s(true) end ----\n", __func__);
+    return (true);
+}
+
+// A complementary function to pmSourceLocalSky: calculate the local median variance
+bool pmSourceLocalSkyVariance(
+    pmSource *source,
+    psStatsOptions statsOptions,
+    psF32 Radius,
+    psMaskType maskVal,
+    psMaskType markVal
+)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_IMAGE_NON_NULL(source->weight, false);
+    PS_ASSERT_IMAGE_NON_NULL(source->maskObj, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_INT_POSITIVE(Radius, false);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    psStatsOptions statistic = psStatsSingleOption(statsOptions);
+    if (statistic == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Multiple or no statistics specified: %x\n", statsOptions);
+        return NULL;
+    }
+
+    psImage *image = source->weight;
+    psImage *mask  = source->maskObj;
+    pmPeak *peak  = source->peak;
+    psRegion srcRegion;
+
+    srcRegion = psRegionForSquare(peak->x, peak->y, Radius);
+    srcRegion = psRegionForImage(mask, srcRegion);
+
+    psImageMaskRegion(mask, srcRegion, "OR", markVal);
+    psStats *myStats = psStatsAlloc(statsOptions);
+    if (!psImageStats(myStats, image, mask, maskVal)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to get image statistics.\n");
+        psFree(myStats);
+        return false;
+    }
+    psImageMaskRegion(mask, srcRegion, "AND", PS_NOT_U8(markVal));
+    double value = psStatsGetValue(myStats, statistic);
+    psFree(myStats);
+
+    if (isnan(value)) {
+        psTrace("psModules.objects", 3, "---- %s(false) end ----\n", __func__);
+        return(false);
+    }
+    if (source->moments == NULL) {
+        source->moments = pmMomentsAlloc();
+    }
+    source->moments->dSky = value;
+    psTrace("psModules.objects", 3, "---- %s(true) end ----\n", __func__);
+    return (true);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceSky.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceSky.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceSky.h	(revision 22322)
@@ -0,0 +1,47 @@
+/* @file  pmSourceSky.h
+ * @author EAM, IfA; GLG, MHPCC
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-06-20 02:22:26 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_SOURCE_SKY_H
+# define PM_SOURCE_SKY_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/** pmSourceLocalSky()
+ *
+ * Measure the local sky in the vicinity of the given source. The Radius
+ * defines the square aperture in which the moments will be measured. This
+ * function assumes the source pixels have been defined, and that the value of
+ * Radius here is smaller than the value of Radius used to define the pixels. The
+ * annular region not contained within the radius defined here is used to measure
+ * the local background in the vicinity of the source. The local background
+ * measurement uses the specified statistic passed in via the statsOptions entry.
+ * This function allocates the pmMoments structure. The resulting sky is used to
+ * set the value of the pmMoments.sky element of the provided pmSource structure.
+ *
+ */
+bool pmSourceLocalSky(
+    pmSource *source,   ///< The input image (float)
+    psStatsOptions statsOptions, ///< The statistic used in calculating the background sky
+    float Radius,   ///< The inner radius of the square annulus to exclude
+    psMaskType maskVal,                 ///< Value to mask
+    psMaskType mark                     ///< Mask value for marking
+);
+
+
+// A complementary function to pmSourceLocalSky: calculate the local sky variance
+bool pmSourceLocalSkyVariance(
+    pmSource *source,   ///< The input image (float)
+    psStatsOptions statsOptions, ///< The statistic used in calculating the background sky
+    float Radius,   ///< The inner radius of the square annulus to exclude
+    psMaskType maskVal,                 ///< Value to mask
+    psMaskType mark                     ///< Mask value for marking
+);
+
+/// @}
+# endif /* PM_SOURCE_PHOTOMETRY_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceUtils.c	(revision 22322)
@@ -0,0 +1,117 @@
+/** @file  pmSource.c
+ *
+ *  Functions to define and manipulate sources on images
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA: significant modifications.
+ *
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-11-10 01:09:20 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmTrend2D.h"
+#include "pmPSF.h"
+#include "pmModel.h"
+#include "pmSource.h"
+#include "pmSourceUtils.h"
+
+/******************************************************************************
+    pmSourceModelGuess(source, model): This function allocates a new
+    pmModel structure based on the given modelType specified in the argument list.
+    The corresponding pmModelGuess function is returned, and used to
+    supply the values of the params array in the pmModel structure.
+ 
+    XXX: Many parameters are based on the src->moments structure, which is in
+    image, not subImage coords.  Therefore, the calls to the model evaluation
+    functions will be in image, not subImage coords.  Remember this.
+*****************************************************************************/
+pmModel *pmSourceModelGuess(pmSource *source,
+                            pmModelType modelType)
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, NULL);
+    PS_ASSERT_PTR_NON_NULL(source->moments, NULL);
+    PS_ASSERT_PTR_NON_NULL(source->peak, NULL);
+
+    pmModel *model = pmModelAlloc(modelType);
+
+    if (!model->modelGuess(model, source)) {
+	psFree (model);
+	return NULL;
+    }
+
+    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    return(model);
+}
+
+pmSource *pmSourceFromModel (pmModel *model, pmReadout *readout, float radius, 
+                             pmSourceType type)
+{
+    PS_ASSERT_PTR_NON_NULL(model, NULL);
+    PS_ASSERT_PTR_NON_NULL(readout, NULL);
+
+    pmSource *source = pmSourceAlloc ();
+
+    // use the model centroid for the peak
+    switch (type) {
+      case PM_SOURCE_TYPE_STAR:
+	source->modelPSF = model;
+	break;
+      case PM_SOURCE_TYPE_EXTENDED:
+	source->modelEXT = model;
+	break;
+      default:
+	psAbort ("invalid source type");
+    }
+    source->type = type;
+
+    pmCell *cell = readout->parent;
+
+    float Io    = model->params->data.F32[PM_PAR_I0];
+    float xChip = model->params->data.F32[PM_PAR_XPOS];
+    float yChip = model->params->data.F32[PM_PAR_YPOS];
+
+    source->peak = pmPeakAlloc (xChip, yChip, Io, PM_PEAK_LONE);
+
+    int x0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.X0");
+    int y0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0");
+    int xParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY");
+    int yParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY");
+
+    // XXX fix the binning : currently not selected from concepts
+    // int xBin = psMetadataLookupS32(NULL, cell->concepts, "CELL.XBIN"); // Binning in x and y
+    // int yBin = psMetadataLookupS32(NULL, cell->concepts, "CELL.YBIN"); // Binning in x and y
+    int xBin = 1;
+    int yBin = 1;
+
+    // Position on the cell 
+    float xCell = PM_CHIP_TO_CELL(xChip, x0Cell, xParityCell, xBin);
+    float yCell = PM_CHIP_TO_CELL(yChip, y0Cell, yParityCell, yBin);
+
+    // Position on the readout
+    // float xReadout = CELL_TO_READOUT(xCell, x0Readout);
+    // float yReadout = CELL_TO_READOUT(yCell, y0Readout);
+    
+    pmSourceDefinePixels (source, readout, xCell, yCell, radius);
+
+    return (source);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceUtils.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceUtils.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSourceUtils.h	(revision 22322)
@@ -0,0 +1,40 @@
+/* @file  pmSourceUtils.h
+ *
+ * Utility functions for working with pmSources
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-08-24 00:11:02 $
+ * Copyright 2007 IfA, University of Hawaii
+ */
+
+# ifndef PM_SOURCE_UTILS_H
+# define PM_SOURCE_UTILS_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/** pmSourceModelGuess()
+ *
+ * Convert available data to an initial guess for the given model. This
+ * function allocates a pmModel entry for the pmSource structure based on the
+ * provided model selection. The method of defining the model parameter guesses
+ * are specified for each model below. The guess values are placed in the model
+ * parameters. The function returns TRUE on success or FALSE on failure.
+ *
+ */
+pmModel *pmSourceModelGuess(
+    pmSource *source,   ///< The input pmSource
+    pmModelType model   ///< The type of model to be created.
+);
+
+pmSource *pmSourceFromModel (
+  pmModel *model, 
+  pmReadout *readout, 
+  float radius,
+  pmSourceType type
+  );
+
+/// @}
+# endif /* PM_MODEL_CLASS_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSpan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSpan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSpan.c	(revision 22322)
@@ -0,0 +1,73 @@
+/* @file  pmSpan.c
+ *
+ * @author RHL, Princeton & IfA; EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:00:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmSpan.h"
+
+/*** functions to manipulate image spans ***/
+
+static void spanFree(pmSpan *tmp) {
+    return;
+}
+
+/*
+ * pmSpanAlloc()
+ */
+pmSpan *pmSpanAlloc(int y, int x0, int x1)
+{
+    pmSpan *span = (pmSpan *)psAlloc(sizeof(pmSpan));
+    psMemSetDeallocator(span, (psFreeFunc) spanFree);
+
+    span->y = y;
+    span->x0 = x0;
+    span->x1 = x1;
+
+    return(span);
+}
+
+bool pmSpanTest(const psPtr ptr)
+{
+    return (psMemGetDeallocator(ptr) == (psFreeFunc)spanFree);
+}
+
+//
+// Sort pmSpans by y, then x0, then x1
+//
+int pmSpanSortByYX(const void **a, const void **b) {
+    const pmSpan *sa = *(const pmSpan **)a;
+    const pmSpan *sb = *(const pmSpan **)b;
+
+    if (sa->y < sb->y) {
+	return -1;
+    } else if (sa->y == sb->y) {
+	if (sa->x0 < sb->x0) {
+	    return -1;
+	} else if (sa->x0 == sb->x0) {
+	    if (sa->x1 < sb->x1) {
+		return -1;
+	    } else if (sa->x1 == sb->x1) {
+		return 0;
+	    } else {
+		return 1;
+	    }
+	} else {
+	    return 1;
+	}
+    } else {
+	return 1;
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSpan.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSpan.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmSpan.h	(revision 22322)
@@ -0,0 +1,28 @@
+/* @file  pmSpan.h
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-01 00:00:17 $
+ * Copyright 2006 Institute for Astronomy, University of Hawaii
+ */
+
+# ifndef PM_SPAN_H
+# define PM_SPAN_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+// Describe a segment of an image
+typedef struct {
+    int y;                              //!< Row that span's in
+    int x0;                             //!< Starting column (inclusive)
+    int x1;                             //!< Ending column (inclusive)
+} pmSpan;
+
+pmSpan *pmSpanAlloc(int y, int x1, int x2);
+bool pmSpanTest(const psPtr ptr);
+int pmSpanSortByYX (const void **a, const void **b);
+
+/// @}
+# endif /* PM_SPAN_H */
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmTrend2D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmTrend2D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmTrend2D.c	(revision 22322)
@@ -0,0 +1,300 @@
+/** @file  pmTrend2D.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-01-15 02:47:51 $
+ *  Copyright 2004 Institute for Astronomy, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include <strings.h>
+# include <pslib.h>
+# include "pmTrend2D.h"
+
+static void pmTrend2DFree (pmTrend2D *trend) {
+
+    if (trend == NULL) 
+        return;
+
+    psFree (trend->stats);
+    psFree (trend->poly);
+    psFree (trend->map);
+    return;
+}
+
+pmTrend2D *pmTrend2DAlloc (pmTrend2DMode mode, psImage *image, int nXtrend, int nYtrend, psStats *stats)
+{
+    PS_ASSERT_PTR_NON_NULL(stats, NULL);
+    if (mode == PM_TREND_MAP) {
+        PS_ASSERT_PTR_NON_NULL(image, NULL);
+    }
+
+    pmTrend2D *trend = (pmTrend2D *) psAlloc(sizeof(pmTrend2D));
+    psMemSetDeallocator(trend, (psFreeFunc) pmTrend2DFree);
+
+    trend->map = NULL;
+    trend->poly = NULL;
+    trend->stats = psMemIncrRefCounter (stats);
+    trend->mode = mode;
+	
+    switch (mode) {
+      case PM_TREND_POLY_ORD:
+	trend->poly = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, nXtrend, nYtrend);
+	// set masking somehow
+ 	for (int nx = 0; nx < trend->poly->nX + 1; nx++) {
+	    for (int ny = 0; ny < trend->poly->nY + 1; ny++) {
+		if (nx + ny >= PS_MAX (trend->poly->nX, trend->poly->nY) + 1) {
+		    trend->poly->coeffMask[nx][ny] = PS_POLY_MASK_SET;
+		} else {
+		    trend->poly->coeffMask[nx][ny] = PS_POLY_MASK_NONE;
+		}
+	    }
+	}
+	break;
+
+      case PM_TREND_POLY_CHEB:
+	trend->poly = psPolynomial2DAlloc (PS_POLYNOMIAL_CHEB, nXtrend, nYtrend);
+	break;
+
+      case PM_TREND_MAP: {
+	  // binning defines the map scale relationship
+	  psImageBinning *binning = psImageBinningAlloc();
+	  binning->nXruff = nXtrend;
+	  binning->nYruff = nYtrend;
+	  binning->nXfine = image->numCols;
+	  binning->nYfine = image->numRows;
+
+	  trend->map = psImageMapAlloc (image, binning, stats);
+	  psFree (binning);
+	  break;
+      }
+      // XXX: Put a more graceful error here.
+      default:
+	psAbort ("error");
+    }
+    return (trend);
+}
+
+bool psMemCheckTrend2D(psPtr ptr)
+{
+    PS_ASSERT_PTR(ptr, false);
+    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmTrend2DFree);
+}
+
+pmTrend2D *pmTrend2DNoImageAlloc (pmTrend2DMode mode, psImageBinning *binning, psStats *stats)
+{
+    if (mode == PM_TREND_MAP) {
+        PS_ASSERT_PTR_NON_NULL(binning, NULL);
+        PS_ASSERT_PTR_NON_NULL(stats, NULL);
+    }
+    pmTrend2D *trend = (pmTrend2D *) psAlloc(sizeof(pmTrend2D));
+    psMemSetDeallocator(trend, (psFreeFunc) pmTrend2DFree);
+
+    trend->map = NULL;
+    trend->poly = NULL;
+    trend->stats = psMemIncrRefCounter (stats);
+    trend->mode = mode;
+	
+    switch (mode) {
+      case PM_TREND_POLY_ORD:
+	trend->poly = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, binning->nXruff, binning->nYruff);
+	// set masking somehow
+ 	for (int nx = 0; nx < trend->poly->nX + 1; nx++) {
+	    for (int ny = 0; ny < trend->poly->nY + 1; ny++) {
+		if (nx + ny >= PS_MAX (trend->poly->nX, trend->poly->nY) + 1) {
+		    trend->poly->coeffMask[nx][ny] = PS_POLY_MASK_SET;
+		} else {
+		    trend->poly->coeffMask[nx][ny] = PS_POLY_MASK_NONE;
+		}
+	    }
+	}
+	break;
+
+      case PM_TREND_POLY_CHEB:
+	trend->poly = psPolynomial2DAlloc (PS_POLYNOMIAL_CHEB, binning->nXruff, binning->nYruff);
+	break;
+
+      case PM_TREND_MAP: {
+	  // binning defines the map scale relationship
+	  trend->map = psImageMapNoImageAlloc (binning, stats);
+	  break;
+      }
+
+      default:
+	psAbort ("error");
+    }
+    return (trend);
+}
+
+pmTrend2D *pmTrend2DFieldAlloc (pmTrend2DMode mode, int nXfield, int nYfield, int nXtrend, int nYtrend, psStats *stats)
+{
+    PS_ASSERT_PTR_NON_NULL(stats, NULL);
+    pmTrend2D *trend = (pmTrend2D *) psAlloc(sizeof(pmTrend2D));
+    psMemSetDeallocator(trend, (psFreeFunc) pmTrend2DFree);
+
+    trend->map = NULL;
+    trend->poly = NULL;
+    trend->stats = psMemIncrRefCounter (stats);
+    trend->mode = mode;
+	
+    switch (mode) {
+      case PM_TREND_POLY_ORD:
+	trend->poly = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, nXtrend, nYtrend);
+	// set masking somehow
+ 	for (int nx = 0; nx < trend->poly->nX + 1; nx++) {
+	    for (int ny = 0; ny < trend->poly->nY + 1; ny++) {
+		if (nx + ny >= PS_MAX (trend->poly->nX, trend->poly->nY) + 1) {
+		    trend->poly->coeffMask[nx][ny] = PS_POLY_MASK_SET;
+		} else {
+		    trend->poly->coeffMask[nx][ny] = PS_POLY_MASK_NONE;
+		}
+	    }
+	}
+	break;
+
+      case PM_TREND_POLY_CHEB:
+	trend->poly = psPolynomial2DAlloc (PS_POLYNOMIAL_CHEB, nXtrend, nYtrend);
+	break;
+
+      case PM_TREND_MAP: {
+	  // binning defines the map scale relationship
+	  psImageBinning *binning = psImageBinningAlloc();
+	  binning->nXfine = nXfield;
+	  binning->nYfine = nYfield;
+	  binning->nXruff = nXtrend;
+	  binning->nYruff = nYtrend;
+
+	  trend->map = psImageMapAlloc (NULL, binning, stats);
+	  psFree (binning);
+	  break;
+      }
+
+      default:
+        // XXX: Put a more graceful error here.
+	psAbort ("error");
+    }
+    return (trend);
+}
+
+bool pmTrend2DFit (pmTrend2D *trend, psVector *mask, psMaskType maskVal, psVector *x,
+                   psVector *y, psVector *f, psVector *df)
+{
+    PS_ASSERT_PTR_NON_NULL(trend, false);
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    PS_ASSERT_VECTOR_NON_NULL(f, false);
+
+    bool status;
+    switch (trend->mode) {
+      case PM_TREND_POLY_ORD:
+      case PM_TREND_POLY_CHEB:
+        status = psVectorClipFitPolynomial2D (trend->poly, trend->stats, mask, maskVal, f, df, x, y);
+	// we can use the API here which adjusts the polynomial order based on the number
+	// of points in the image, and potentially based on the fractional range of the
+	// data?
+	break;
+
+      case PM_TREND_MAP:
+	// XXX supply fraction from trend elements
+	// XXX need to add the API which adjusts the scale
+	status = psImageMapClipFit (trend->map, trend->stats, mask, maskVal, x, y, f, df);
+	break;
+
+      default:
+	psAbort ("error");
+    }
+    return status;
+}
+
+double pmTrend2DEval (pmTrend2D *trend, float x, float y)
+{
+    if (!trend) return 0.0;
+
+    double result;
+    switch (trend->mode) {
+      case PM_TREND_POLY_ORD:
+      case PM_TREND_POLY_CHEB:
+	result = psPolynomial2DEval (trend->poly, x, y);
+	break;
+
+      case PM_TREND_MAP:
+	result = psImageMapEval (trend->map, x, y);
+	break;
+
+      default:
+	psAbort ("error");
+    }
+    return result;
+}
+
+psVector *pmTrend2DEvalVector (pmTrend2D *trend, psVector *x, psVector *y)
+{
+    PS_ASSERT_PTR_NON_NULL(trend, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(x, false);
+    PS_ASSERT_VECTOR_NON_NULL(y, false);
+    psVector *result;
+
+    switch (trend->mode) {
+      case PM_TREND_POLY_ORD:
+      case PM_TREND_POLY_CHEB:
+	result = psPolynomial2DEvalVector (trend->poly, x, y);
+	break;
+
+      case PM_TREND_MAP:
+	result = psImageMapEvalVector (trend->map, x, y);
+	break;
+
+      default:
+	psAbort ("error");
+    }
+    return result;
+}
+
+psString pmTrend2DModeToString (pmTrend2DMode mode) {
+    
+    psString name;
+
+    switch (mode) {
+      case PM_TREND_NONE:
+	name = psStringCopy ("NONE");
+	break;
+      case PM_TREND_POLY_ORD:
+	name = psStringCopy ("POLY_ORD");
+	break;
+      case PM_TREND_POLY_CHEB:
+	name = psStringCopy ("POLY_CHEB");
+	break;
+      case PM_TREND_MAP:
+	name = psStringCopy ("MAP");
+	break;
+      default:
+        psError (PS_ERR_UNKNOWN, true, "Unknown pmTrend2D mode\n");
+	psAbort ("invalid mode %d", mode);
+    }
+    return name;
+}
+
+pmTrend2DMode pmTrend2DModeFromString (psString name) {
+
+    if (!name) return PM_TREND_NONE;
+
+    if (!strcasecmp (name, "NONE")) {
+	return PM_TREND_NONE;
+    }
+    if (!strcasecmp (name, "POLY_ORD")) {
+	return PM_TREND_POLY_ORD;
+    }
+    if (!strcasecmp (name, "POLY_CHEB")) {
+	return PM_TREND_POLY_CHEB;
+    }
+    if (!strcasecmp (name, "MAP")) {
+	return PM_TREND_MAP;
+    }
+    psError (PS_ERR_UNKNOWN, true, "Unknown pmTrend2D mode %s\n", name);
+    return PM_TREND_NONE;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmTrend2D.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmTrend2D.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/objects/pmTrend2D.h	(revision 22322)
@@ -0,0 +1,55 @@
+/* @file  pmTrend2D.h
+ *
+ * functions to represent ways of modeling a 2D trend
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-12-15 01:23:18 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_TREND_2D_H
+# define PM_TREND_2D_H
+
+#include <pslib.h>
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+typedef enum {
+    PM_TREND_NONE,
+    PM_TREND_POLY_ORD,
+    PM_TREND_POLY_CHEB,
+    PM_TREND_MAP,
+} pmTrend2DMode;
+
+typedef struct {
+    psPolynomial2D *poly;
+    psImageMap *map;
+    psStats *stats;                     // Statistics for clipped fitting
+    psStatsOptions singleMean, singleStdev; // Staistics for mean and stdev when there's a single pixel
+    pmTrend2DMode mode; // POLY_ORD, POLY_CHEB, MAP
+} pmTrend2D;
+
+// allocate a pmTrend2D structure tied to an image dimensions.  nXtrend,nYtrend is the order for the polynomials, max number of grid cells for
+// psImageMap
+pmTrend2D *pmTrend2DAlloc (pmTrend2DMode mode, psImage *image, int nXtrend, int nYtrend, psStats *stats);
+
+bool psMemCheckTrend2D(psPtr ptr);
+
+pmTrend2D *pmTrend2DNoImageAlloc (pmTrend2DMode mode, psImageBinning *binning, psStats *stats);
+
+// allocate a pmTrend2D tied to an abstract field with size nXfield,nYfield
+pmTrend2D *pmTrend2DFieldAlloc (pmTrend2DMode mode, int nXfield, int nYfield, int nXtrend, int nYtrend, psStats *stats);
+
+bool pmTrend2DFit (pmTrend2D *trend, psVector *mask, psMaskType maskVal, psVector *x, psVector *y, psVector *f, psVector *df);
+
+double pmTrend2DEval (pmTrend2D *trend, float x, float y);
+psVector *pmTrend2DEvalVector (pmTrend2D *trend, psVector *x, psVector *y);
+
+psString pmTrend2DModeToString (pmTrend2DMode mode);
+pmTrend2DMode pmTrend2DModeFromString (psString name);
+
+/// @}
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/psmodules.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/psmodules.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/psmodules.h	(revision 22322)
@@ -0,0 +1,127 @@
+#ifndef PS_MODULES_H
+#define PS_MODULES_H
+
+#include <pslib.h>
+
+// the following headers are from psModule:extras
+#include <psPipe.h>
+#include <psIOBuffer.h>
+#include <psVectorBracket.h>
+#include <pmKapaPlots.h>
+
+// XXX the following headers define constructs needed by the elements below
+#include <pmConfig.h>
+#include <pmDetrendDB.h>
+#include <pmDetrendThreads.h>
+#include <pmHDU.h>
+#include <pmFPA.h>
+#include <pmFPALevel.h>
+#include <pmFPAview.h>
+#include <pmFPAfile.h>
+
+// the following headers are from psModule:config
+#include <pmErrorCodes.h>
+#include <pmConfigRecipes.h>
+#include <pmConfigCamera.h>
+#include <pmConfigCommand.h>
+#include <pmConfigMask.h>
+#include <pmConfigDump.h>
+#include <pmVersion.h>
+
+// the following headers are from psModule:concepts
+#include <pmConcepts.h>
+#include <pmConceptsRead.h>
+#include <pmConceptsStandard.h>
+#include <pmConceptsWrite.h>
+#include <pmConceptsPhotcode.h>
+#include <pmConceptsAverage.h>
+#include <pmConceptsUpdate.h>
+
+// the following headers are from psModule:camera
+#include <pmHDUUtils.h>
+#include <pmHDUGenerate.h>
+#include <pmFPAFlags.h>
+#include <pmFPAfileDefine.h>
+#include <pmFPAfileFitsIO.h>
+#include <pmFPAfileIO.h>
+#include <pmFPARead.h>
+#include <pmFPAConstruct.h>
+#include <pmFPACopy.h>
+#include <pmFPAHeader.h>
+#include <pmFPAMaskWeight.h>
+#include <pmFPAMosaic.h>
+#include <pmFPARead.h>
+#include <pmFPAWrite.h>
+#include <pmFPA_JPEG.h>
+#include <pmFPAExtent.h>
+#include <pmFPACalibration.h>
+#include <pmReadoutStack.h>
+#include <pmFPAUtils.h>
+#include <pmCellSquish.h>
+#include <pmFPABin.h>
+
+// the following headers are from psModule:detrend
+#include <pmFlatField.h>
+#include <pmFlatNormalize.h>
+#include <pmFringeStats.h>
+#include <pmMaskBadPixels.h>
+#include <pmNonLinear.h>
+#include <pmOverscan.h>
+#include <pmBias.h>
+#include <pmShutterCorrection.h>
+// #include <pmSkySubtract.h>
+#include <pmDark.h>
+
+// the following headers are from psModule:astrom
+#include <pmAstrometryWCS.h>
+#include <pmAstrometryUtils.h>
+#include <pmAstrometryRegions.h>
+#include <pmAstrometryObjects.h>
+#include <pmAstrometryModel.h>
+#include <pmAstrometryRefstars.h>
+#include <pmAstrometryDistortion.h>
+
+// the following headers are from psModule:imcombine
+#include <pmStack.h>
+#include <pmStackReject.h>
+#include <pmSubtraction.h>
+#include <pmSubtractionThreads.h>
+#include <pmSubtractionStamps.h>
+#include <pmSubtractionKernels.h>
+#include <pmSubtractionMatch.h>
+#include <pmSubtractionIO.h>
+#include <pmSubtractionParams.h>
+#include <pmSubtractionMask.h>
+#include <pmSubtractionEquation.h>
+#include <pmReadoutCombine.h>
+
+// the following headers are from psModule:objects
+#include <pmPeaks.h>
+#include <pmSpan.h>
+#include <pmFootprint.h>
+#include <pmDetections.h>
+#include <pmMoments.h>
+#include <pmResiduals.h>
+#include <pmGrowthCurve.h>
+#include <pmTrend2D.h>
+#include <pmPSF.h>
+#include <pmModel.h>
+#include <pmSource.h>
+#include <pmSourceUtils.h>
+#include <pmSourceIO.h>
+#include <pmSourceSky.h>
+#include <pmSourceFitModel.h>
+#include <pmSourceFitSet.h>
+#include <pmSourceContour.h>
+#include <pmSourcePlots.h>
+#include <pmPSF_IO.h>
+#include <pmPSFtry.h>
+#include <pmModelClass.h>
+#include <pmModelUtils.h>
+#include <pmSourcePhotometry.h>
+
+// The following headers are from random locations, here because they cross bounds
+#include <pmReadoutFake.h>
+#include <pmPSFEnvelope.h>
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmSkyCell.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmSkyCell.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmSkyCell.h	(revision 22322)
@@ -0,0 +1,18 @@
+#ifndef PM_SKY_CELL_H
+#define PM_SKY_CELL_H
+
+/// Structure representing a sky cell
+typedef struct
+{
+    psImage *image;                     // Image; type F32
+    psImage *mask;                      // Mask image; type MASK_TYPE
+    psImage *weight;                    // Weight map; type F32
+    psPlaneTransform *toTPA;            // Transformation from pixels to tangent plane
+    psPlaneTransform *fromTPA;          // Transformation from tangent plane to pixels
+    psProjection *toSky;                // Projection to the sky
+}
+pmSkyCell;
+
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmWarp.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmWarp.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmWarp.h	(revision 22322)
@@ -0,0 +1,24 @@
+#ifndef PM_WARP_MAP_H
+#define PM_WARP_MAP_H
+
+// a single pswarpMap converts coordinates from one image to a second image
+// the linear model is only valid over a limited range of pixels
+typedef struct
+{
+    double Xo, Xx, Xy;
+    double Yo, Yx, Yy;
+    int xo;
+    int yo;
+}
+pswarpMap;
+
+// the pswarpMapGrid carries a collection of pswarpMag structures representing the
+// local value of the pswarpMap at different locations in the image.
+typedef struct
+{
+    pswarpMap ***maps;
+    int nXpts, nYpts;                   // number of x,y samples in the grid
+    int nXpix, nYpix;                   // x,y spacing in src image pixels of grid samples
+    int xMin,  yMin;                    // coordinate of first grid sample
+}
+pswarpMapGrid;
Index: /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmWarpMap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmWarpMap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmWarpMap.c	(revision 22322)
@@ -0,0 +1,241 @@
+#include <stdio.h>
+#include <math.h>
+#include <pslib.h>
+
+#include "pmWarpMap.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Allocators and deallocators
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void warpMapFree(pswarpMap *map)
+{
+    // No operations required; this function exists merely to identify a pmWarpMap
+    return;
+}
+
+pswarpMap *pswarpMapAlloc(void)
+{
+    pswarpMap *map = (pswarpMap *) psAlloc (sizeof(pswarpMap));
+    psMemSetDeallocator(map, (psFreeFunc)warpMapFree);
+
+    map->Xo = map->Xx = map->Xy = NAN;
+    map->Yo = map->Yx = map->Yy = NAN;
+    map->xo = map->yo = NAN;
+
+    return map;
+}
+
+
+static void warpMapGridFree(pswarpMapGrid *grid)
+{
+    if (!grid->maps) {
+        // Nothing to free
+        return;
+    }
+
+    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]);
+    }
+    psFree (grid->maps);
+    return;
+}
+
+pswarpMapGrid *pswarpMapGridAlloc(int nXpts, int nYpts)
+{
+    PS_ASSERT_INT_POSITIVE(nXpts, NULL);
+    PS_ASSERT_INT_POSITIVE(nYpts, NULL);
+
+    pswarpMapGrid *grid = (pswarpMapGrid *) psAlloc(sizeof(pswarpMapGrid));
+    psMemSetDeallocator(grid, (psFreeFunc)warpMapGridFree);
+
+    grid->maps = psAlloc(nXpts*sizeof(void **));
+    for (int i = 0; i < nXpts; i++) {
+        grid->maps[i] = psAlloc(nYpts*sizeof(void *));
+        for (int j = 0; j < nYpts; j++) {
+            grid->maps[i][j] = pswarpMapAlloc();
+        }
+    }
+    grid->nXpts = nXpts;
+    grid->nYpts = nYpts;
+
+    grid->nXpix = 0;
+    grid->nYpix = 0;
+
+    return grid;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Operations
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// XXX for the moment, ignore readout->cell->chip offsets
+pswarpMapGrid *pswarpMapGridFromImage(pmSkyCell *dest, const pmReadout *src, int nXpix, int nYpix)
+{
+    PS_ASSERT_PTR_NON_NULL(dest, NULL);
+    PS_ASSERT_PTR_NON_NULL(src, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(src->image, NULL);
+    PS_ASSERT_INT_NON_NEGATIVE(nXpix, NULL);
+    PS_ASSERT_INT_NON_NEGATIVE(nYpix, NULL);
+
+    // split the difference of the remainder
+    int xMin = 0.5 * (src->image->numCols % nXpix); // Minimum in x
+    int yMin = 0.5 * (src->image->numRows % nYpix); // Minimum in y
+
+    int nXpts = src->image->numCols / nXpix; // Number of points in x
+    if (src->image->numCols % nXpix == 0) {
+        nXpts++;
+    }
+    int nYpts = src->image->numRows / nYpix; // Number of points in y
+    if (src->image->numRows % nYpix == 0) {
+        nYpts++;
+    }
+
+    pmWarpMapGrid *grid = pmWarpMapGridAlloc(nXpts, nYpts);
+    grid->nXpix = nXpix;
+    grid->nYpix = nYpix;
+    grid->xMin = xMin;
+    grid->yMin = yMin;
+
+    for (int ni = 0, i = xMin; ni < nXpts; i += nXpix, ni++) {
+        for (int nj = 0, j = yMin; nj < nYpts; j += nYpix, nj++) {
+            pmWarpMapSetLocalModel(grid->maps[ni][nj], dest, src, i, j);
+        }
+    }
+
+    return grid;
+}
+
+bool pmWarpMapGridSetGrid(const pmWarpMapGrid *grid, int ix, int iy, int *gridX, int *gridY)
+{
+    *gridX = (ix - grid->xMin + 0.5*grid->nXpix) / grid->nXpix;
+    *gridY = (iy - grid->yMin + 0.5*grid->nYpix) / grid->nYpix;
+
+    return true;
+}
+
+bool pmWarpMapGridNextGrid(const pmWarpMapGrid *grid, int gridX, int gridY, int *nextX, int *nextY)
+{
+    *nextX = gridX*grid->nXpix + grid->xMin + 0.5*grid->nXpix;
+    *nextY = gridY*grid->nYpix + grid->yMin + 0.5*grid->nYpix;
+
+    return true;
+}
+
+// measure the max error accumulated in appling one grid point to its neighbors
+double pmWarpMapGridMaxError(const pmWarpMapGrid *grid)
+{
+    double maxError = 0;                // Maximum error
+
+    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
+            double xRaw, yRaw;          // Raw position
+            double xRef, yRef;          // Reference position
+
+            pmWarpMapApply(&xRaw, &yRaw, grid->maps[i][j], grid->maps[i][j]->xo + grid->nXpix,
+                           grid->maps[i][j]->yo);
+            pmWarpMapApply(&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;
+}
+
+bool pmWarpMapApply(double *outX, double *outY, const pmWarpMap *map, double inX, double inY)
+{
+    *outX = map->Xo + map->Xx*inX + map->Xy*inY;
+    *outY = map->Yo + map->Yx*inX + map->Yy*inY;
+
+    return true;
+}
+
+bool pmWarpMapSetLocalModel(pmWarpMap *map, const pmSkyCell *dest, const pmReadout *src, int ix, int iy)
+{
+    PS_ASSERT_PTR_NON_NULL(map, false);
+    PS_ASSERT_PTR_NON_NULL(dest, false);
+    PS_ASSERT_PTR_NON_NULL(dest->toSky, false);
+    PS_ASSERT_PTR_NON_NULL(dest->fromTPA, false);
+    PS_ASSERT_PTR_NON_NULL(src, false);
+    pmCell *cell = src->parent;         // Parent cell of source
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    pmChip *chip = cell->parent;        // Parent chip of source
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(chip->toFPA, false);
+    pmFPA *fpa = chip->parent;          // Parent FPA of source
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fpa->toTPA, false);
+    PS_ASSERT_PTR_NON_NULL(fpa->toSky, false);
+    PS_ASSERT_INT_NON_NEGATIVE(ix, false);
+    PS_ASSERT_INT_NON_NEGATIVE(iy, false);
+
+    // These will get allocated in the course of the calculations; may as well allocate them here
+    // XXX save these as static for speed?
+    // Alternatively, pass in a buffer containing these pre-allocated.
+    psPlane *offset = psPlaneAlloc();   // Position offset from the main position of interest (ix,iy)
+    psPlane *FP = psPlaneAlloc();       // Position in the focal plane
+    psPlane *TP = psPlaneAlloc();       // Position in the tangent plane
+    psSphere *sky = psSphereAlloc();    // Position on the sky
+
+    // XXX need to include readout->cell->chip offsets
+    // --> Look up CELL.X0, CELL.Y0 in concepts
+    // --> Use readout->col0, readout->row0
+
+    // V(0,0) position
+    offset->x = ix;
+    offset->y = iy;
+    psPlaneTransformApply(FP, chip->toFPA, offset);
+    psPlaneTransformApply (TP, fpa->toTPA, FP);
+    psDeproject(sky, TP, fpa->toSky);
+    psProject(TP, sky, dest->toSky);
+    psPlane *V00 = psPlaneTransformApply(NULL, dest->fromTPA, TP); // Coordinates at the position of interest
+
+    // V(1,0) position
+    offset->x = ix + 1;
+    offset->y = iy;
+    psPlaneTransformApply(FP, chipSrc->toFPA, offset);
+    psPlaneTransformApply(TP, fpaSrc->toTPA, FP);
+    psDeproject(sky, TP, fpaSrc->toSky);
+    psProject(TP, sky, dest->toSky);
+    psPlane *V10 = psPlaneTransformApply(NULL, dest->fromTPA, TP); // Coordinates at position + (1,0)
+
+    // V(0,1) position
+    offset->x = ix;
+    offset->y = iy + 1;
+    psPlaneTransformApply(FP, chipSrc->toFPA, offset);
+    psPlaneTransformApply(TP, fpaSrc->toTPA, FP);
+    psDeproject(sky, TP, fpaSrc->toSky);
+    psProject(TP, sky, fpaDest->toSky);
+    psPlane *V01 = psPlaneTransformApply(NULL, dest->fromTPA, TP); // Coordinates at position + (0,1)
+
+    psFree(offset);
+    psFree(FP);
+    psFree(TP);
+    psFree(sky);
+
+    // Generate local linear transformation
+    map->Xx = V10->x - V00->x;
+    map->Xy = V01->x - V00->x;
+    map->Xo = V00->x - map->Xx * ix - map->Xy * iy;
+
+    map->Yx = V10->y - V00->y;
+    map->Yy = V01->y - V00->y;
+    map->Yo = V00->y - map->Yx * ix - map->Yy * iy;
+
+    map->xo = ix;
+    map->yo = iy;
+
+    psFree(V00);
+    psFree(V10);
+    psFree(V01);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmWarpMap.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmWarpMap.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/src/sky/pmWarpMap.h	(revision 22322)
@@ -0,0 +1,88 @@
+/*  @file pmWarpMap.h
+ *  @brief Warp Map Manipulation functions
+ * 
+ *  @author Paul Price, IfA
+ *  @author Eugene Magnier, IfA
+ * 
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-01-24 01:05:41 $
+ *  Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_WARP_MAP_H
+#define PM_WARP_MAP_H
+
+/// @addtogroup Sky
+/// @{
+
+// a single pmWarpMap converts coordinates from one image to a second image
+// the linear model is only valid over a limited range of pixels
+typedef struct
+{
+    double Xo, Xx, Xy;                  // Transformation coefficients for x
+    double Yo, Yx, Yy;                  // Transformation coefficients for y
+    int xo, yo;                         // Origin
+}
+pmWarpMap;
+
+// the pmWarpMapGrid carries a collection of pmWarpMag structures representing the
+// local value of the pmWarpMap at different locations in the image.
+typedef struct
+{
+    pmWarpMap ***maps;                  // 2D image of pointers to maps
+    int nXpts, nYpts;                   // number of x,y samples in the grid
+    int nXpix, nYpix;                   // x,y spacing in src image pixels of grid samples
+    int xMin,  yMin;                    // coordinate of first grid sample
+}
+pmWarpMapGrid;
+
+/// Allocator
+pmWarpMap *pmWarpMapAlloc(void);
+
+/// Allocator
+pmWarpMapGrid *pmWarpMapGridAlloc(int Nx, // Number of grid points in x
+                                  int Ny // Number of grid points in y
+                                 );
+
+
+// Operations follow
+
+
+// construct a grid with superpixel spacing of nXpix, nYpix
+pmWarpMapGrid *pmWarpMapGridFromImage(pmSkyCell *dest, // Destination sky cell
+                                      const pmReadout *src, // Source readout
+                                      int nXpix, // Number of pixels in x
+                                      int nYpix // Number of pixels in y
+                                     );
+
+bool pmWarpMapGridSetGrid(pmWarpMapGrid *grid,
+                          int ix, int iy,
+                          int *gridX,
+                          int *gridY
+                         );
+
+bool pmWarpMapGridNextGrid(pmWarpMapGrid *grid,
+                           int gridX,
+                           int gridY,
+                           int *nextX,
+                           int *nextY
+                          );
+
+// measure the max error accumulated in appling one grid point to its neighbors
+double pmWarpMapGridMaxError(const pmWarpMapGrid *grid
+                            );
+
+bool pmWarpMapApply(double *outX,
+                    double *outY,
+                    const pmWarpMap *map,
+                    double inX,
+                    double inY);
+
+// determine the map for the given pixel from src to dest. pixel is in src coords
+bool pmWarpMapSetLocalModel(pmWarpMap *map,
+                            const pmSkyCell *dest,
+                            const pmReadout *src,
+                            int ix, int iy
+                           );
+/// @}
+#endif
Index: /tags/sj_tags/sj_root_20080929/psModules/templates/c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/templates/c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/templates/c	(revision 22322)
@@ -0,0 +1,11 @@
+/** @file  filename.c
+ *
+ *  @brief Contains the definitions for ...
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1.1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-09-10 19:01:33 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
Index: /tags/sj_tags/sj_root_20080929/psModules/templates/h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/templates/h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/templates/h	(revision 22322)
@@ -0,0 +1,13 @@
+/** @file  filename.h
+ *
+ *  @brief Contains the definitions for ...
+ *
+ *  Detailed Description goes here.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1.1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-09-10 19:01:33 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
Index: /tags/sj_tags/sj_root_20080929/psModules/test/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/.cvsignore	(revision 22322)
@@ -0,0 +1,15 @@
+.deps
+.libs
+Makefile
+tst_pmFlatField
+tst_pmMaskBadPixels
+tst_pmNonLinear
+tst_pmSubtractBias
+temp
+Makefile.in
+tst_pmObjects01
+tst_pmReadoutCombine
+tst_pmSubtractSky
+tst_pmImageSubtract
+tst_pmImageCombine
+tst_pmAstrometry
Index: /tags/sj_tags/sj_root_20080929/psModules/test/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/Makefile.am	(revision 22322)
@@ -0,0 +1,9 @@
+SUBDIRS = tap pstap $(SRCDIRS)
+
+TESTS = test.pl
+
+EXTRA_DIST = test.pl
+
+CLEANFILES = core core.* *~
+
+test: check
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/.cvsignore	(revision 22322)
@@ -0,0 +1,12 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmAstrometry
+tst_pmAstrometry01
+tap_pmAstrometryWCS
+tap_pmAstrometryWCS_DVO
+tap_pmAstrometryWCS_DVO2
+tap_pmAstrometryWCS_DVO3
+tap_pmAstrometryWCS_DVO4
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/Makefile.am	(revision 22322)
@@ -0,0 +1,30 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSMODULES_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpsmodules.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSMODULES_LIBS)
+
+TEST_PROGS = \
+	tap_pmAstrometryWCS \
+	tap_pmAstrometryWCS_DVO \
+	tap_pmAstrometryWCS_DVO2 \
+	tap_pmAstrometryWCS_DVO3 \
+	tap_pmAstrometryWCS_DVO4
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
+	$(top_srcdir)/test/test.pl
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometry.c	(revision 22322)
@@ -0,0 +1,318 @@
+    /** @file  tst_pmAstrometry.c
+     *
+ *  @brief Contains the tests: pmAstrometry.[ch].  The pmxxxAlloc()
+ *  and psFree() functionality are used here.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  XXX: Untested: pmFPACheckParents()
+ *  XXX: Create the pmHDU alloc/free function, test them here
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-06-19 18:28:38 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define CHIP_ALLOC_NAME "ChipName"
+#define CELL_ALLOC_NAME "CellName"
+#define MISC_NUM 32
+#define MISC_NAME "META00"
+#define MISC_NAME2 "META01"
+#define NUM_BIAS_DATA 10
+#define TEST_NUM_ROWS 32
+#define TEST_NUM_COLS 32
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.  We do this to ensure that the data is
+later being psFree()'ed correctly.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    fpa->hdu = pmHDUAlloc(NULL);
+    return(fpa);
+}
+
+
+/******************************************************************************
+testFPAAlloc()
+    1: We ensure that pmFPAAlloc() properly allocates a pmFPA struct.
+    2: We populate the members with real data to ensure they are being
+       free'ed correctly.
+ *****************************************************************************/
+void testFPAAlloc(void)
+{
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = pmFPAAlloc(camera);
+        ok(fpa != NULL, "pmFPAAlloc() returned a non-NULL");
+        ok(fpa->fromTPA == NULL, "pmFPAAlloc() set ->fromTPA to NULL");
+        ok(fpa->toTPA == NULL, "pmFPAAlloc() set ->toTPA to NULL");
+        ok(fpa->toSky == NULL, "pmFPAAlloc() set ->toSky to NULL");
+        ok(fpa->concepts != NULL &&
+           psMemCheckMetadata(fpa->concepts), "pmFPAAlloc() set ->concepts correctly");
+        ok(fpa->conceptsRead == PM_CONCEPT_SOURCE_NONE, "pmFPAAlloc() set ->conceptsRead correctly");
+        ok(fpa->analysis != NULL &&
+           psMemCheckMetadata(fpa->analysis), "pmFPAAlloc() set ->analysis correctly");
+        ok(fpa->camera == camera, "pmFPAAlloc() set ->camera correctly");
+        ok(fpa->chips != NULL &&
+           psMemCheckArray(fpa->chips), "pmFPAAlloc() set ->chips correctly");
+        ok(fpa->hdu == NULL, "pmFPAAlloc() set ->hdu to NULL");
+        ok(fpa->wrote_phu == false, "pmFPAAlloc() set ->wrote_phu to FALSE");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Populate the pmFPA struct with real data to ensure they were
+    // psFree()'ed correctly.
+    {
+        psMemId id = psMemGetId();
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.  We do this to ensure that the data is
+later being psFree()'ed correctly.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    chip->hdu = pmHDUAlloc(NULL);
+    return(chip);
+}
+
+void testChipAlloc(void)
+{
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+        ok(chip != NULL, "pmChipAlloc() returned non-NULL");
+        ok(chip->toFPA == NULL, "pmChipAlloc() set chip->toChip to NULL");
+        ok(chip->fromFPA == NULL, "pmChipAlloc() set chip->fromChip to NULL");
+        ok(chip->concepts != NULL &&
+           psMemCheckMetadata(chip->concepts), "pmChipAlloc() set ->concepts correctly");
+        ok(chip->conceptsRead == PM_CONCEPT_SOURCE_NONE, "pmCellAlloc() set ->conceptsRead correctly");
+        ok(chip->analysis != NULL &&
+           psMemCheckMetadata(chip->analysis), "pmChipAlloc() set ->analysis correctly");
+        ok(chip->cells != NULL &&
+           psMemCheckArray(chip->cells), "pmChipAlloc() set ->cells correctly");
+        ok(chip->parent == fpa, "pmChipAlloc() set ->parent correctly");
+        ok(chip->process == true, "pmChipAlloc() set ->process correctly");
+        ok(chip->file_exists == false, "pmChipAlloc() set ->file_exists correctly");
+        ok(chip->data_exists == false, "pmChipAlloc() set ->data_exists correctly");
+        ok(chip->hdu == NULL, "pmChipAlloc() set ->hdu to NULL");
+        ok(chip->wrote_phu == false, "pmChipAlloc() set ->wrote_phu correctly");
+        psFree(camera);
+        psFree(fpa);
+        psFree(chip);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Populate the pmChip struct with real data to ensure they were
+    // psFree()'ed correctly.
+    {
+        psMemId id = psMemGetId();
+        pmChip *chip = generateSimpleChip(NULL);
+        psFree(chip);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.  We do this to ensure that the data is
+later being psFree()'ed correctly.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmFPA *fpa, pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    cell->hdu = pmHDUAlloc(NULL);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(cell);
+}
+
+void testCellAlloc(void)
+{
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+        pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+        ok(cell != NULL, "pmCellAlloc returned non-NULL");
+        ok(cell->concepts != NULL &&
+           psMemCheckMetadata(cell->concepts), "pmCellpAlloc() set ->concepts correctly");
+        ok(cell->conceptsRead == PM_CONCEPT_SOURCE_NONE, "pmCellAlloc() set ->conceptsRead correctly");
+        ok(cell->config == NULL, "pmCellpAlloc() set ->config to NULL");
+        ok(cell->analysis != NULL &&
+           psMemCheckMetadata(cell->analysis), "pmCellAlloc() set ->analysis correctly");
+        ok(cell->readouts != NULL &&
+           psMemCheckArray(cell->readouts), "pmCellAlloc() set ->readouts correctly");
+        ok(cell->parent == chip, "pmCellAlloc() set ->parent correctly");
+        ok(cell->process == true, "pmCellAlloc() set ->process correctly");
+        ok(cell->file_exists == false, "pmCellAlloc() set ->file_exists correctly");
+        ok(cell->data_exists == false, "pmCellAlloc() set ->data_exists correctly");
+        ok(cell->hdu == NULL, "pmCellAlloc() set ->hdu to NULL");
+        ok(cell->wrote_phu == false, "pmCellAlloc() set ->wrote_phu correctly");
+        psFree(camera);
+        psFree(fpa);
+        psFree(chip);
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Populate the pmCell struct with real data to ensure they were
+    // psFree()'ed correctly.
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL, NULL);
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test pmCellFreeReadouts()
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL, NULL);
+        pmCellFreeReadouts(cell);
+        ok(cell->readouts->n == 0, "pmCellFreeReadouts() correctly set cell->readouts->n to 0");
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (pmCellFreeReadouts)");
+    }
+
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.  We do this to ensure that the data is
+later being psFree()'ed correctly.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+
+void testReadoutAlloc(void)
+{
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+        pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+        pmReadout *readout = pmReadoutAlloc(cell);
+        ok(readout != NULL, "pmReadoutAlloc() returned non-NULL");
+        ok(readout->col0 == 0, "pmReadoutAlloc() set ->col0 correctly");
+        ok(readout->row0 == 0, "pmReadoutAlloc() set ->row0 correctly");
+        ok(readout->image == NULL, "pmReadoutAlloc() set ->image correctly");
+        ok(readout->mask == NULL, "pmReadoutAlloc() set ->mask correctly");
+        ok(readout->weight == NULL, "pmReadoutAlloc() set ->weight correctly");
+        ok(readout->bias != NULL &&
+           psMemCheckList(readout->bias), "pmReadoutAlloc() set ->bias correctly");
+        ok(readout->analysis != NULL &&
+           psMemCheckMetadata(readout->analysis), "pmReadoutAlloc() set ->analysis correctly");
+        ok(readout->parent == cell, "pmReadoutAlloc() set ->parent correctly");
+        ok(readout->process == true, "pmReadoutAlloc() set ->process correctly");
+        ok(readout->file_exists == false, "pmReadoutAlloc() set ->file_exists correctly");
+        ok(readout->data_exists == false, "pmReadoutAlloc() set ->data_exists correctly");
+        psFree(camera);
+        psFree(fpa);
+        psFree(chip);
+        psFree(cell);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Populate the pmReadout struct with real data to ensure they were
+    // psFree()'ed correctly.
+    {
+        psMemId id = psMemGetId();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        psFree(readout);
+        // XXX: The pmReadout->bias list is not being free'ed correctly.
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+/*
+    {
+        psMemId id = psMemGetId();
+        pmReadout *readout = pmReadoutAlloc(NULL);
+        readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+        readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+            psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+            psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        }
+        psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+        return(readout);
+    }
+*/
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(56);
+
+    testFPAAlloc();
+    testChipAlloc();
+    testCellAlloc();
+    testReadoutAlloc();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS.c	(revision 22322)
@@ -0,0 +1,151 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int main (void)
+{
+    plan_tests(33);
+
+    note("pmAstrometryWCS tests");
+
+    {
+        note("test pmAstromReadWCS");
+        psMemId id = psMemGetId();
+
+        // construct a header with a simple set of WCS values
+        // convert to pmFPA components and check
+
+        psMetadata *header = psMetadataAlloc();
+
+        psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE1", PS_META_REPLACE, "", "RA---TAN");
+        psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE2", PS_META_REPLACE, "", "DEC--TAN");
+
+        // center coords (R,D)
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CRVAL1", PS_META_REPLACE, "", 0.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CRVAL2", PS_META_REPLACE, "", 0.0);
+
+        // center coords (X,Y)
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CRPIX1", PS_META_REPLACE, "", 0.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CRPIX2", PS_META_REPLACE, "", 0.0);
+
+        // degrees per pixel
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CDELT1",  PS_META_REPLACE, "", 1.0/3600.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CDELT2",  PS_META_REPLACE, "", 1.0/3600.0);
+
+        // rotation matrix
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PC001001", PS_META_REPLACE, "", 1.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PC001002", PS_META_REPLACE, "", 0.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PC002001", PS_META_REPLACE, "", 0.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PC002002", PS_META_REPLACE, "", 1.0);
+
+        pmFPA *fpa = pmFPAAlloc (NULL);
+        pmChip *chip = pmChipAlloc (NULL, "test");
+
+        // toFPA carries pixel scale (microns per pixel)
+        // toSky carries plate scale (radians per micron)
+        bool status = pmAstromReadWCS (fpa, chip, header, 10.0);
+        ok (status, "converted WCS keywords to FPA astrometry");
+        skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+        ok(fpa->toSky->type == PS_PROJ_TAN, "correct projection (TAN)");
+
+        // make these tests double
+        ok_float(fpa->toSky->R*PS_DEG_RAD, 0.0, "projection center RA %f", fpa->toSky->R*PS_DEG_RAD);
+        ok_float(fpa->toSky->R*PS_DEG_RAD, 0.0, "projection center DEC %f", fpa->toSky->R*PS_DEG_RAD);
+
+        ok_float(fpa->toSky->Xs, PM_RAD_DEG/3600.0/10.0, "projection X scale %f", fpa->toSky->Xs);
+        ok_float(fpa->toSky->Ys, PM_RAD_DEG/3600.0/10.0, "projection X scale %f", fpa->toSky->Ys);
+
+        ok_float(fpa->toTPA->x->coeff[1][0], 1.0, "TP scale (unity): %f", fpa->toTPA->x->coeff[1][0]);
+        ok_float(fpa->toTPA->y->coeff[0][1], 1.0, "TP scale (unity): %f", fpa->toTPA->x->coeff[1][0]);
+
+        ok_float(chip->toFPA->x->coeff[0][0], 0.0, "ref pixel X: %f", chip->toFPA->x->coeff[0][0]);
+        ok_float(chip->toFPA->y->coeff[0][0], 0.0, "ref pixel Y: %f", chip->toFPA->y->coeff[0][0]);
+
+        ok_float(chip->toFPA->x->coeff[1][0], 10.0, "CD1_1: %f", chip->toFPA->x->coeff[1][0]);
+        ok_float(chip->toFPA->x->coeff[0][1], 0.0, "CD1_2: %f", chip->toFPA->x->coeff[0][1]);
+        ok_float(chip->toFPA->y->coeff[1][0], 0.0, "CD2_1: %f", chip->toFPA->y->coeff[1][0]);
+        ok_float(chip->toFPA->y->coeff[0][1], 10.0, "CD2_2: %f", chip->toFPA->y->coeff[0][1]);
+
+        psFree (fpa);
+        psFree (chip);
+        psFree (header);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+    }
+
+    {
+        note("test pmAstromReadWCS");
+        psMemId id = psMemGetId();
+
+        // construct a header with a simple set of WCS values
+        // convert to pmFPA components and check
+
+        psMetadata *header = psMetadataAlloc();
+
+        psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE1", PS_META_REPLACE, "", "RA---TAN");
+        psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE2", PS_META_REPLACE, "", "DEC--TAN");
+
+        // center coords (R,D)
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CRVAL1", PS_META_REPLACE, "", 0.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CRVAL2", PS_META_REPLACE, "", 0.0);
+
+        // center coords (X,Y)
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CRPIX1", PS_META_REPLACE, "", 10.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CRPIX2", PS_META_REPLACE, "", 10.0);
+
+        // degrees per pixel
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CDELT1",  PS_META_REPLACE, "", 1.0/3600.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "CDELT2",  PS_META_REPLACE, "", 1.0/3600.0);
+
+        // rotation matrix
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PC001001", PS_META_REPLACE, "", 1.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PC001002", PS_META_REPLACE, "", 0.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PC002001", PS_META_REPLACE, "", 0.0);
+        psMetadataAddF32 (header, PS_LIST_TAIL, "PC002002", PS_META_REPLACE, "", 1.0);
+
+        pmFPA *fpa = pmFPAAlloc (NULL);
+        pmChip *chip = pmChipAlloc (NULL, "test");
+
+        // toFPA carries pixel scale (pixels per micron)
+        // toTPA carries plate scale (microns per arcsecond)
+        bool status = pmAstromReadWCS (fpa, chip, header, 10.0);
+        ok (status, "converted WCS keywords to FPA astrometry");
+        skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+        ok(fpa->toSky->type == PS_PROJ_TAN, "correct projection (TAN)");
+
+        // make these tests double
+        ok_float(fpa->toSky->R*PS_DEG_RAD, 0.0, "projection center RA %f", fpa->toSky->R*PS_DEG_RAD);
+        ok_float(fpa->toSky->R*PS_DEG_RAD, 0.0, "projection center DEC %f", fpa->toSky->R*PS_DEG_RAD);
+
+        ok_float(fpa->toSky->Xs, PM_RAD_DEG/3600.0/10.0, "projection X scale %f", fpa->toSky->Xs);
+        ok_float(fpa->toSky->Ys, PM_RAD_DEG/3600.0/10.0, "projection X scale %f", fpa->toSky->Ys);
+
+        ok_float(fpa->toTPA->x->coeff[1][0], 1.0, "TP scale (unity): %f", fpa->toTPA->x->coeff[1][0]);
+        ok_float(fpa->toTPA->y->coeff[0][1], 1.0, "TP scale (unity): %f", fpa->toTPA->x->coeff[1][0]);
+
+        ok_float(chip->toFPA->x->coeff[0][0], -100.0, "ref pixel X: %f", chip->toFPA->x->coeff[0][0]);
+        ok_float(chip->toFPA->y->coeff[0][0], -100.0, "ref pixel Y: %f", chip->toFPA->y->coeff[0][0]);
+
+        ok_float(chip->toFPA->x->coeff[1][0], 10.0, "CD1_1: %f", chip->toFPA->x->coeff[1][0]);
+        ok_float(chip->toFPA->x->coeff[0][1], 0.0, "CD1_2: %f", chip->toFPA->x->coeff[0][1]);
+        ok_float(chip->toFPA->y->coeff[1][0], 0.0, "CD2_1: %f", chip->toFPA->y->coeff[1][0]);
+        ok_float(chip->toFPA->y->coeff[0][1], 10.0, "CD2_2: %f", chip->toFPA->y->coeff[0][1]);
+
+        psFree (fpa);
+        psFree (chip);
+        psFree (header);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+    }
+
+    return exit_status();
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO.c	(revision 22322)
@@ -0,0 +1,528 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+# if (HAVE_KAPA)
+    # include "dvo.h"
+
+    psMetadata *WriteCoordsToHeader (Coords *coords);
+void test1(); // basic TAN projection,
+void test2(); // small rotation
+void test3(); // 2nd order term
+
+void test1in(); // basic TAN projection,
+void test2in(); // small rotation
+void test3in(); // 2nd order term
+
+void testA(); // alloc test
+void testB(); // alloc test
+
+int main (void)
+{
+    plan_tests(992);
+
+    note("pmAstrometryWCS tests compared with DVO coords routines");
+    note("this file tests pmAstromWCS <-> Header representations");
+
+    test1in();
+    test2in();
+    test3in();
+
+    test1();
+    test2();
+    test3();
+    return exit_status();
+}
+
+void test3in()
+{
+    note("test pmAstromWCStoHeader");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+    coords.polyterms[0][0] = 0.01; // L vs X^2
+    coords.polyterms[2][1] = 0.01; // M vs Y^2
+
+    psMetadata *header1 = WriteCoordsToHeader(&coords);
+    pmAstromWCS *wcs1 = pmAstromWCSfromHeader(header1);
+    skip_start (wcs1 == NULL, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psMetadata *header2 = psMetadataAlloc();
+    pmAstromWCStoHeader(header2, wcs1);
+    pmAstromWCS *wcs2 = pmAstromWCSfromHeader(header2);
+
+    ok (wcs2 != NULL, "converted WCS keywords to WCS astrometry");
+    skip_start (wcs2 == NULL, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psSphere *sky1 = psSphereAlloc();
+    psSphere *sky2 = psSphereAlloc();
+    psPlane *chip = psPlaneAlloc();
+
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            chip->x = x;
+            chip->y = y;
+            pmAstromWCStoSky (sky1, wcs1, chip);
+            pmAstromWCStoSky (sky2, wcs2, chip);
+            while (sky1->r > 2*M_PI)
+                sky1->r -= 2*M_PI;
+            while (sky1->r <      0)
+                sky1->r += 2*M_PI;
+            while (sky2->r > 2*M_PI)
+                sky2->r -= 2*M_PI;
+            while (sky2->r <      0)
+                sky2->r += 2*M_PI;
+
+            ok_float(sky1->r*PS_DEG_RAD, sky2->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", sky1->r*PS_DEG_RAD, sky2->r*PS_DEG_RAD, sky1->r*PS_DEG_RAD - sky2->r*PS_DEG_RAD);
+            ok_float(sky1->d*PS_DEG_RAD, sky2->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", sky1->d*PS_DEG_RAD, sky2->d*PS_DEG_RAD, sky1->d*PS_DEG_RAD - sky2->d*PS_DEG_RAD);
+        }
+    }
+    psFree (sky1);
+    psFree (sky2);
+    psFree (chip);
+
+    skip_end();
+    psFree (wcs2);
+    psFree (header2);
+
+    skip_end();
+    psFree (wcs1);
+    psFree (header1);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test2in()
+{
+    note("test pmAstromWCStoHeader");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 0.9;
+    coords.pc1_2  = -0.1;
+    coords.pc2_1  = 0.1;
+    coords.pc2_2  = 0.9;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header1 = WriteCoordsToHeader(&coords);
+    pmAstromWCS *wcs1 = pmAstromWCSfromHeader(header1);
+    skip_start (wcs1 == NULL, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psMetadata *header2 = psMetadataAlloc();
+    pmAstromWCStoHeader(header2, wcs1);
+    pmAstromWCS *wcs2 = pmAstromWCSfromHeader(header2);
+
+    ok (wcs2 != NULL, "converted WCS keywords to WCS astrometry");
+    skip_start (wcs2 == NULL, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psSphere *sky1 = psSphereAlloc();
+    psSphere *sky2 = psSphereAlloc();
+    psPlane *chip = psPlaneAlloc();
+
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            chip->x = x;
+            chip->y = y;
+            pmAstromWCStoSky (sky1, wcs1, chip);
+            pmAstromWCStoSky (sky2, wcs2, chip);
+            while (sky1->r > 2*M_PI)
+                sky1->r -= 2*M_PI;
+            while (sky1->r <      0)
+                sky1->r += 2*M_PI;
+            while (sky2->r > 2*M_PI)
+                sky2->r -= 2*M_PI;
+            while (sky2->r <      0)
+                sky2->r += 2*M_PI;
+
+            ok_float(sky1->r*PS_DEG_RAD, sky2->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", sky1->r*PS_DEG_RAD, sky2->r*PS_DEG_RAD, sky1->r*PS_DEG_RAD - sky2->r*PS_DEG_RAD);
+            ok_float(sky1->d*PS_DEG_RAD, sky2->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", sky1->d*PS_DEG_RAD, sky2->d*PS_DEG_RAD, sky1->d*PS_DEG_RAD - sky2->d*PS_DEG_RAD);
+        }
+    }
+    psFree (sky1);
+    psFree (sky2);
+    psFree (chip);
+
+    skip_end();
+    psFree (wcs2);
+    psFree (header2);
+
+    skip_end();
+    psFree (wcs1);
+    psFree (header1);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test1in()
+{
+    note("test pmAstromWCStoHeader");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header1 = WriteCoordsToHeader(&coords);
+    pmAstromWCS *wcs1 = pmAstromWCSfromHeader(header1);
+    skip_start (wcs1 == NULL, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psMetadata *header2 = psMetadataAlloc();
+    pmAstromWCStoHeader(header2, wcs1);
+    pmAstromWCS *wcs2 = pmAstromWCSfromHeader(header2);
+
+    ok (wcs2 != NULL, "converted WCS keywords to WCS astrometry");
+    skip_start (wcs2 == NULL, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psSphere *sky1 = psSphereAlloc();
+    psSphere *sky2 = psSphereAlloc();
+    psPlane *chip = psPlaneAlloc();
+
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            chip->x = x;
+            chip->y = y;
+            pmAstromWCStoSky (sky1, wcs1, chip);
+            pmAstromWCStoSky (sky2, wcs2, chip);
+            while (sky1->r > 2*M_PI)
+                sky1->r -= 2*M_PI;
+            while (sky1->r <      0)
+                sky1->r += 2*M_PI;
+            while (sky2->r > 2*M_PI)
+                sky2->r -= 2*M_PI;
+            while (sky2->r <      0)
+                sky2->r += 2*M_PI;
+
+            ok_float(sky1->r*PS_DEG_RAD, sky2->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", sky1->r*PS_DEG_RAD, sky2->r*PS_DEG_RAD, sky1->r*PS_DEG_RAD - sky2->r*PS_DEG_RAD);
+            ok_float(sky1->d*PS_DEG_RAD, sky2->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", sky1->d*PS_DEG_RAD, sky2->d*PS_DEG_RAD, sky2->d*PS_DEG_RAD - sky2->d*PS_DEG_RAD);
+        }
+    }
+    psFree (sky1);
+    psFree (sky2);
+    psFree (chip);
+
+    skip_end();
+    psFree (wcs2);
+    psFree (header2);
+
+    skip_end();
+    psFree (wcs1);
+    psFree (header1);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test3 ()
+{
+    note("test pmAstromWCSfromHeader ");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+    coords.polyterms[0][0] = 0.01; // L vs X^2
+    coords.polyterms[2][1] = 0.01; // M vs Y^2
+
+    psMetadata *header = WriteCoordsToHeader (&coords);
+    pmAstromWCS *wcs = pmAstromWCSfromHeader (header);
+
+    ok (wcs != NULL, "converted WCS keywords to WCS astrometry");
+    skip_start (wcs == NULL, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psSphere *sky = psSphereAlloc();
+    psPlane *chip = psPlaneAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+            chip->x = x;
+            chip->y = y;
+            pmAstromWCStoSky (sky, wcs, chip);
+            while (sky->r > 2*M_PI)
+                sky->r -= 2*M_PI;
+            while (sky->r <      0)
+                sky->r += 2*M_PI;
+
+            ok_float(rDVO, sky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", rDVO, sky->r*PS_DEG_RAD, rDVO - sky->r*PS_DEG_RAD);
+            ok_float(dDVO, sky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", dDVO, sky->d*PS_DEG_RAD, dDVO - sky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (sky);
+    psFree (chip);
+
+    skip_end();
+    psFree (wcs);
+    psFree (header);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test2 ()
+{
+    note("test pmAstromWCSfromHeader");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 0.9;
+    coords.pc1_2  = -0.1;
+    coords.pc2_1  = 0.1;
+    coords.pc2_2  = 0.9;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header = WriteCoordsToHeader (&coords);
+    pmAstromWCS *wcs = pmAstromWCSfromHeader (header);
+
+    ok (wcs != NULL, "converted WCS keywords to WCS astrometry");
+    skip_start (wcs == NULL, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psSphere *sky = psSphereAlloc();
+    psPlane *chip = psPlaneAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+            chip->x = x;
+            chip->y = y;
+            pmAstromWCStoSky (sky, wcs, chip);
+            while (sky->r > 2*M_PI)
+                sky->r -= 2*M_PI;
+            while (sky->r <      0)
+                sky->r += 2*M_PI;
+
+            ok_float(rDVO, sky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", rDVO, sky->r*PS_DEG_RAD, rDVO - sky->r*PS_DEG_RAD);
+            ok_float(dDVO, sky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", dDVO, sky->d*PS_DEG_RAD, dDVO - sky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (sky);
+    psFree (chip);
+
+    skip_end();
+    psFree (wcs);
+    psFree (header);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test1()
+{
+    note("test pmAstromWCSfromHeader");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header = WriteCoordsToHeader (&coords);
+    pmAstromWCS *wcs = pmAstromWCSfromHeader (header);
+
+    ok (wcs != NULL, "converted WCS keywords to WCS astrometry");
+    skip_start (wcs == NULL, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psSphere *sky = psSphereAlloc();
+    psPlane *chip = psPlaneAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+            chip->x = x;
+            chip->y = y;
+            pmAstromWCStoSky (sky, wcs, chip);
+            while (sky->r > 2*M_PI)
+                sky->r -= 2*M_PI;
+            while (sky->r <      0)
+                sky->r += 2*M_PI;
+
+            ok_float(rDVO, sky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", rDVO, sky->r*PS_DEG_RAD, rDVO - sky->r*PS_DEG_RAD);
+            ok_float(dDVO, sky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", dDVO, sky->d*PS_DEG_RAD, dDVO - sky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (sky);
+    psFree (chip);
+
+    skip_end();
+    psFree (wcs);
+    psFree (header);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void testA()
+{
+    note("test coord allocs");
+    psMemId id = psMemGetId();
+
+    psPlane *chip = psPlaneAlloc();
+    psFree (chip);
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void testB()
+{
+    note("test coord allocs");
+    psMemId id = psMemGetId();
+
+    psSphere *sky = psSphereAlloc();
+    psFree (sky);
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+psMetadata *WriteCoordsToHeader (Coords *coords)
+{
+
+    char name[16];
+
+    // construct a header using coords as the input
+    psMetadata *header = psMetadataAlloc();
+
+    sprintf (name, "RA--%s", &coords[0].ctype[4]);
+    psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE1", PS_META_REPLACE, "", name);
+    sprintf (name, "DEC-%s", &coords[0].ctype[4]);
+    psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE2", PS_META_REPLACE, "", name);
+
+    // center coords (R,D)
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRVAL1", PS_META_REPLACE, "", coords[0].crval1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRVAL2", PS_META_REPLACE, "", coords[0].crval2);
+
+    // center coords (X,Y)
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRPIX1", PS_META_REPLACE, "", coords[0].crpix1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRPIX2", PS_META_REPLACE, "", coords[0].crpix2);
+
+    // degrees per pixel
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CDELT1",  PS_META_REPLACE, "", coords[0].cdelt1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CDELT2",  PS_META_REPLACE, "", coords[0].cdelt2);
+
+    // rotation matrix
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC001001", PS_META_REPLACE, "", coords[0].pc1_1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC001002", PS_META_REPLACE, "", coords[0].pc1_2);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC002001", PS_META_REPLACE, "", coords[0].pc2_1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC002002", PS_META_REPLACE, "", coords[0].pc2_2);
+
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X2Y0", PS_META_REPLACE, "", coords[0].polyterms[0][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X1Y1", PS_META_REPLACE, "", coords[0].polyterms[1][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X0Y2", PS_META_REPLACE, "", coords[0].polyterms[2][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X2Y0", PS_META_REPLACE, "", coords[0].polyterms[0][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X1Y1", PS_META_REPLACE, "", coords[0].polyterms[1][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X0Y2", PS_META_REPLACE, "", coords[0].polyterms[2][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X3Y0", PS_META_REPLACE, "", coords[0].polyterms[3][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X2Y1", PS_META_REPLACE, "", coords[0].polyterms[4][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X1Y2", PS_META_REPLACE, "", coords[0].polyterms[5][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X0Y3", PS_META_REPLACE, "", coords[0].polyterms[6][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X3Y0", PS_META_REPLACE, "", coords[0].polyterms[3][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X2Y1", PS_META_REPLACE, "", coords[0].polyterms[4][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X1Y2", PS_META_REPLACE, "", coords[0].polyterms[5][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X0Y3", PS_META_REPLACE, "", coords[0].polyterms[6][1]);
+
+    psMetadataAddS32 (header, PS_LIST_TAIL, "NPLYTERM", PS_META_REPLACE, "", coords[0].Npolyterms);
+
+    return header;
+}
+
+# else
+
+    int main (void)
+{
+    plan_tests(2);
+
+    ok(true, "Skipping tests: (libdvo not available)");
+    note("pmAstrometryWCS tests compared with DVO coords routines : SKIPPED (libdvo not available)");
+
+    return exit_status();
+}
+
+# endif
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO2.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO2.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO2.c	(revision 22322)
@@ -0,0 +1,630 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+# if (HAVE_KAPA)
+    # include "dvo.h"
+
+    psMetadata *WriteCoordsToHeader (Coords *coords);
+void test1(); // basic TAN projection,
+void test2(); // small rotation
+void test3(); // 2nd order term
+void test1x(); // basic TAN projection with central offset
+void test2x(); // small rotation with central offset
+void test3x(); // 2nd order term with central offset
+void test3inv(); // 2nd order term with central offset
+
+int main (void)
+{
+    plan_tests(1483);
+
+    note("pmAstromReadWCS tests compared with DVO coords routines");
+
+    test1();
+    test2();
+    test3();
+    test1x();
+    test2x();
+    test3x();
+    test3inv();
+
+    return exit_status();
+}
+
+void test1()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header, PM_RAD_DEG*10.0/3600.0);
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane *onChip = psPlaneAlloc();
+    psPlane *onFPA = psPlaneAlloc();
+    psPlane *onTPA = psPlaneAlloc();
+    psSphere *onSky = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            ok_float(rDVO, onSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", rDVO, onSky->r*PS_DEG_RAD, rDVO - onSky->r*PS_DEG_RAD);
+            ok_float(dDVO, onSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", dDVO, onSky->d*PS_DEG_RAD, dDVO - onSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test2()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 0.9;
+    coords.pc1_2  = 0.1;
+    coords.pc2_1  =-0.1;
+    coords.pc2_2  = 0.9;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header, PM_RAD_DEG*10.0/3600.0);
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane *onChip = psPlaneAlloc();
+    psPlane *onFPA = psPlaneAlloc();
+    psPlane *onTPA = psPlaneAlloc();
+    psSphere *onSky = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            ok_float(rDVO, onSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", rDVO, onSky->r*PS_DEG_RAD, rDVO - onSky->r*PS_DEG_RAD);
+            ok_float(dDVO, onSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", dDVO, onSky->d*PS_DEG_RAD, dDVO - onSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test3()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+    coords.polyterms[0][0] = 0.01; // L vs X^2
+    coords.polyterms[2][1] = 0.01; // M vs Y^2
+
+
+    psMetadata *header = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header, PM_RAD_DEG*10.0/3600.0);
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane *onChip = psPlaneAlloc();
+    psPlane *onFPA = psPlaneAlloc();
+    psPlane *onTPA = psPlaneAlloc();
+    psSphere *onSky = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            ok_float(rDVO, onSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", rDVO, onSky->r*PS_DEG_RAD, rDVO - onSky->r*PS_DEG_RAD);
+            ok_float(dDVO, onSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", dDVO, onSky->d*PS_DEG_RAD, dDVO - onSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test1x()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = +50.0;
+    coords.crpix2 = -20.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header, PM_RAD_DEG*10.0/3600.0);
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane *onChip = psPlaneAlloc();
+    psPlane *onFPA = psPlaneAlloc();
+    psPlane *onTPA = psPlaneAlloc();
+    psSphere *onSky = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            ok_float(rDVO, onSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", rDVO, onSky->r*PS_DEG_RAD, rDVO - onSky->r*PS_DEG_RAD);
+            ok_float(dDVO, onSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", dDVO, onSky->d*PS_DEG_RAD, dDVO - onSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test2x()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = +50.0;
+    coords.crpix2 = -20.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 0.9;
+    coords.pc1_2  = 0.1;
+    coords.pc2_1  =-0.1;
+    coords.pc2_2  = 0.9;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header, PM_RAD_DEG*10.0/3600.0);
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane *onChip = psPlaneAlloc();
+    psPlane *onFPA = psPlaneAlloc();
+    psPlane *onTPA = psPlaneAlloc();
+    psSphere *onSky = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            ok_float(rDVO, onSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", rDVO, onSky->r*PS_DEG_RAD, rDVO - onSky->r*PS_DEG_RAD);
+            ok_float(dDVO, onSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", dDVO, onSky->d*PS_DEG_RAD, dDVO - onSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test3x()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = +50.0;
+    coords.crpix2 = -20.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+    coords.polyterms[0][0] = 0.01; // L vs X^2
+    coords.polyterms[2][1] = 0.01; // M vs Y^2
+
+
+    psMetadata *header = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header, PM_RAD_DEG*10.0/3600.0);
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane *onChip = psPlaneAlloc();
+    psPlane *onFPA = psPlaneAlloc();
+    psPlane *onTPA = psPlaneAlloc();
+    psSphere *onSky = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            ok_float(rDVO, onSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", rDVO, onSky->r*PS_DEG_RAD, rDVO - onSky->r*PS_DEG_RAD);
+            ok_float(dDVO, onSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", dDVO, onSky->d*PS_DEG_RAD, dDVO - onSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test3inv()
+{
+    note("test the inversion of the non-linear polynomial for toFPA -> fromFPA in pmAstromReadWCS");
+    note("note that the tolerance for these tests are rather loose");
+    note("a 2nd order polynomial is not a great approximate to 1 over a 2nd order polynomial");
+    note("unless the non-linear terms are quite small");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = +50.0;
+    coords.crpix2 = -20.0;
+    coords.cdelt1 = 1.0/3600;
+    coords.cdelt2 = 1.0/3600;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+    coords.polyterms[0][0] = 0.01; // L vs X^2
+    coords.polyterms[2][1] = 0.01; // M vs Y^2
+
+
+    psMetadata *header = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header, PM_RAD_DEG*10.0/3600.0);
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane *aChip  = psPlaneAlloc();
+    psPlane *aFPA   = psPlaneAlloc();
+    psPlane *aTPA   = psPlaneAlloc();
+    psPlane *bChip  = psPlaneAlloc();
+    psPlane *bFPA   = psPlaneAlloc();
+    psPlane *bTPA   = psPlaneAlloc();
+    psSphere *onSky = psSphereAlloc();
+
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            // convert up to sky
+            aChip->x = x;
+            aChip->y = y;
+
+            psPlaneTransformApply (aFPA, chip->toFPA, aChip);
+            psPlaneTransformApply (aTPA, fpa->toTPA, aFPA);
+            psDeproject (onSky, aTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            psProject (bTPA, onSky, fpa->toSky);
+            psPlaneTransformApply (bFPA, fpa->fromTPA, bTPA);
+            psPlaneTransformApply (bChip, chip->fromFPA, bFPA);
+
+            // calculate appropriate tol values as f(x,y)
+            ok_float_tol(aChip->x, bChip->x, 1.0, "coordinate match: %f vs %f (delta = %f)", aChip->x, bChip->x, aChip->x - bChip->x);
+            ok_float_tol(aChip->y, bChip->y, 1.0, "coordinate match: %f vs %f (delta = %f)", aChip->y, bChip->y, aChip->y - bChip->y);
+
+            ok_float(aFPA->x, bFPA->x, "coordinate match: %f vs %f (delta = %f)", aFPA->x, bFPA->x, aFPA->x - bFPA->x);
+            ok_float(aFPA->y, bFPA->y, "coordinate match: %f vs %f (delta = %f)", aFPA->y, bFPA->y, aFPA->y - bFPA->y);
+
+            // in this example, TPA coordinates are 10 arcsec/mm; the tol. below represent 1nano-arcsec
+            ok_float_tol(aTPA->x, bTPA->x, 1e-10, "coordinate match: %f vs %f (delta = %g)", aTPA->x, bTPA->x, aTPA->x - bTPA->x);
+            ok_float_tol(aTPA->y, bTPA->y, 1e-10, "coordinate match: %f vs %f (delta = %g)", aTPA->y, bTPA->y, aTPA->y - bTPA->y);
+        }
+    }
+    psFree (onSky);
+    psFree (aTPA);
+    psFree (aFPA);
+    psFree (aChip);
+    psFree (bTPA);
+    psFree (bFPA);
+    psFree (bChip);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+psMetadata *WriteCoordsToHeader (Coords *coords)
+{
+
+    char name[16];
+
+    // construct a header using coords as the input
+    psMetadata *header = psMetadataAlloc();
+
+    sprintf (name, "RA--%s", &coords[0].ctype[4]);
+    psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE1", PS_META_REPLACE, "", name);
+    sprintf (name, "DEC-%s", &coords[0].ctype[4]);
+    psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE2", PS_META_REPLACE, "", name);
+
+    // center coords (R,D)
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRVAL1", PS_META_REPLACE, "", coords[0].crval1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRVAL2", PS_META_REPLACE, "", coords[0].crval2);
+
+    // center coords (X,Y)
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRPIX1", PS_META_REPLACE, "", coords[0].crpix1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRPIX2", PS_META_REPLACE, "", coords[0].crpix2);
+
+    // degrees per pixel
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CDELT1",  PS_META_REPLACE, "", coords[0].cdelt1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CDELT2",  PS_META_REPLACE, "", coords[0].cdelt2);
+
+    // rotation matrix
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC001001", PS_META_REPLACE, "", coords[0].pc1_1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC001002", PS_META_REPLACE, "", coords[0].pc1_2);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC002001", PS_META_REPLACE, "", coords[0].pc2_1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC002002", PS_META_REPLACE, "", coords[0].pc2_2);
+
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X2Y0", PS_META_REPLACE, "", coords[0].polyterms[0][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X1Y1", PS_META_REPLACE, "", coords[0].polyterms[1][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X0Y2", PS_META_REPLACE, "", coords[0].polyterms[2][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X2Y0", PS_META_REPLACE, "", coords[0].polyterms[0][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X1Y1", PS_META_REPLACE, "", coords[0].polyterms[1][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X0Y2", PS_META_REPLACE, "", coords[0].polyterms[2][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X3Y0", PS_META_REPLACE, "", coords[0].polyterms[3][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X2Y1", PS_META_REPLACE, "", coords[0].polyterms[4][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X1Y2", PS_META_REPLACE, "", coords[0].polyterms[5][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X0Y3", PS_META_REPLACE, "", coords[0].polyterms[6][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X3Y0", PS_META_REPLACE, "", coords[0].polyterms[3][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X2Y1", PS_META_REPLACE, "", coords[0].polyterms[4][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X1Y2", PS_META_REPLACE, "", coords[0].polyterms[5][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X0Y3", PS_META_REPLACE, "", coords[0].polyterms[6][1]);
+
+    psMetadataAddS32 (header, PS_LIST_TAIL, "NPLYTERM", PS_META_REPLACE, "", coords[0].Npolyterms);
+
+    return header;
+}
+
+# else
+
+    int main (void)
+{
+    plan_tests(2);
+
+    ok(true, "Skipping tests: (libdvo not available)");
+    note("pmAstrometryWCS tests compared with DVO coords routines : SKIPPED (libdvo not available)");
+
+    return exit_status();
+}
+
+# endif
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO3.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO3.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO3.c	(revision 22322)
@@ -0,0 +1,655 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+# if (HAVE_KAPA)
+    # include "dvo.h"
+
+    psMetadata *WriteCoordsToHeader (Coords *coords);
+void test1(); // basic TAN projection,
+void test2(); // small rotation
+void test3(); // 2nd order term
+void test1x(); // basic TAN projection with central offset
+void test2x(); // small rotation with central offset
+void test3x(); // 2nd order term with central offset
+
+int main (void)
+{
+    plan_tests(991);
+
+    note("pmAstromWriteWCS tests compared with DVO coords routines");
+
+    test1();
+    test2();
+    test3();
+    test1x();
+    test2x();
+    test3x();
+
+    return exit_status();
+}
+
+void test1()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600.0;
+    coords.cdelt2 = 1.0/3600.0;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header1 = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header1, 10.0);
+
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psMetadata *header2 = psMetadataAlloc();
+    status = pmAstromWriteWCS (header2, fpa, chip, 0.001);
+    pmAstromWCS *wcs = pmAstromWCSfromHeader(header2);
+
+    psPlane  *aChip = psPlaneAlloc();
+    psPlane  *aFPA = psPlaneAlloc();
+    psPlane  *aTPA = psPlaneAlloc();
+    psSphere *aSky = psSphereAlloc();
+
+    psPlane  *bChip = psPlaneAlloc();
+    psSphere *bSky = psSphereAlloc();
+
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            aChip->x = x;
+            aChip->y = y;
+            bChip->x = x;
+            bChip->y = y;
+
+            psPlaneTransformApply (aFPA, chip->toFPA, aChip);
+            psPlaneTransformApply (aTPA, fpa->toTPA, aFPA);
+            psDeproject (aSky, aTPA, fpa->toSky);
+
+            pmAstromWCStoSky (bSky, wcs, bChip);
+
+            while (aSky->r > 2*M_PI)
+                aSky->r -= 2*M_PI;
+            while (aSky->r <      0)
+                aSky->r += 2*M_PI;
+            while (bSky->r > 2*M_PI)
+                bSky->r -= 2*M_PI;
+            while (bSky->r <      0)
+                bSky->r += 2*M_PI;
+
+            ok_float(aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, aSky->r*PS_DEG_RAD - bSky->r*PS_DEG_RAD);
+            ok_float(aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, aSky->d*PS_DEG_RAD - bSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (aSky);
+    psFree (aTPA);
+    psFree (aFPA);
+    psFree (aChip);
+
+    psFree (bSky);
+    psFree (bChip);
+
+    psFree (wcs);
+    psFree (header2);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header1);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test2()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600.0;
+    coords.cdelt2 = 1.0/3600.0;
+    coords.pc1_1  = 0.9;
+    coords.pc1_2  = 0.1;
+    coords.pc2_1  = -0.1;
+    coords.pc2_2  = 0.9;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header1 = WriteCoordsToHeader (&coords);
+
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header1, 10.0);
+
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psMetadata *header2 = psMetadataAlloc();
+    status = pmAstromWriteWCS (header2, fpa, chip, 0.001);
+
+    pmAstromWCS *wcs = pmAstromWCSfromHeader(header2);
+
+    psPlane  *aChip = psPlaneAlloc();
+    psPlane  *aFPA = psPlaneAlloc();
+    psPlane  *aTPA = psPlaneAlloc();
+    psSphere *aSky = psSphereAlloc();
+
+    psPlane  *bChip = psPlaneAlloc();
+    psSphere *bSky = psSphereAlloc();
+
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            aChip->x = x;
+            aChip->y = y;
+            bChip->x = x;
+            bChip->y = y;
+
+            psPlaneTransformApply (aFPA, chip->toFPA, aChip);
+            psPlaneTransformApply (aTPA, fpa->toTPA, aFPA);
+            psDeproject (aSky, aTPA, fpa->toSky);
+
+            pmAstromWCStoSky (bSky, wcs, bChip);
+
+            while (aSky->r > 2*M_PI)
+                aSky->r -= 2*M_PI;
+            while (aSky->r <      0)
+                aSky->r += 2*M_PI;
+            while (bSky->r > 2*M_PI)
+                bSky->r -= 2*M_PI;
+            while (bSky->r <      0)
+                bSky->r += 2*M_PI;
+
+            ok_float(aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, aSky->r*PS_DEG_RAD - bSky->r*PS_DEG_RAD);
+            ok_float(aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, aSky->d*PS_DEG_RAD - bSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (aSky);
+    psFree (aTPA);
+    psFree (aFPA);
+    psFree (aChip);
+
+    psFree (bSky);
+    psFree (bChip);
+
+    psFree (wcs);
+    psFree (header2);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header1);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test3()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 1.0/3600.0;
+    coords.cdelt2 = 1.0/3600.0;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+    coords.polyterms[0][0] = 0.01; // L vs X^2
+    coords.polyterms[2][1] = 0.01; // M vs Y^2
+
+    psMetadata *header1 = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header1, 10.0);
+
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psMetadata *header2 = psMetadataAlloc();
+    status = pmAstromWriteWCS (header2, fpa, chip, 0.001);
+
+    pmAstromWCS *wcs = pmAstromWCSfromHeader(header2);
+
+    psPlane  *aChip = psPlaneAlloc();
+    psPlane  *aFPA = psPlaneAlloc();
+    psPlane  *aTPA = psPlaneAlloc();
+    psSphere *aSky = psSphereAlloc();
+
+    psPlane  *bChip = psPlaneAlloc();
+    psSphere *bSky = psSphereAlloc();
+
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            aChip->x = x;
+            aChip->y = y;
+            bChip->x = x;
+            bChip->y = y;
+
+            psPlaneTransformApply (aFPA, chip->toFPA, aChip);
+            psPlaneTransformApply (aTPA, fpa->toTPA, aFPA);
+            psDeproject (aSky, aTPA, fpa->toSky);
+
+            pmAstromWCStoSky (bSky, wcs, bChip);
+
+            while (aSky->r > 2*M_PI)
+                aSky->r -= 2*M_PI;
+            while (aSky->r <      0)
+                aSky->r += 2*M_PI;
+            while (bSky->r > 2*M_PI)
+                bSky->r -= 2*M_PI;
+            while (bSky->r <      0)
+                bSky->r += 2*M_PI;
+
+            ok_float(aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, aSky->r*PS_DEG_RAD - bSky->r*PS_DEG_RAD);
+            ok_float(aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, aSky->d*PS_DEG_RAD - bSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (aSky);
+    psFree (aTPA);
+    psFree (aFPA);
+    psFree (aChip);
+
+    psFree (bSky);
+    psFree (bChip);
+
+    psFree (wcs);
+    psFree (header2);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header1);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test1x()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 20.0;
+    coords.crpix2 = 50.0;
+    coords.cdelt1 = 1.0/3600.0;
+    coords.cdelt2 = 1.0/3600.0;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header1 = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header1, 10.0);
+
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psMetadata *header2 = psMetadataAlloc();
+    status = pmAstromWriteWCS (header2, fpa, chip, 0.001);
+    pmAstromWCS *wcs = pmAstromWCSfromHeader(header2);
+
+    psPlane  *aChip = psPlaneAlloc();
+    psPlane  *aFPA = psPlaneAlloc();
+    psPlane  *aTPA = psPlaneAlloc();
+    psSphere *aSky = psSphereAlloc();
+
+    psPlane  *bChip = psPlaneAlloc();
+    psSphere *bSky = psSphereAlloc();
+
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            aChip->x = x;
+            aChip->y = y;
+            bChip->x = x;
+            bChip->y = y;
+
+            psPlaneTransformApply (aFPA, chip->toFPA, aChip);
+            psPlaneTransformApply (aTPA, fpa->toTPA, aFPA);
+            psDeproject (aSky, aTPA, fpa->toSky);
+
+            pmAstromWCStoSky (bSky, wcs, bChip);
+
+            while (aSky->r > 2*M_PI)
+                aSky->r -= 2*M_PI;
+            while (aSky->r <      0)
+                aSky->r += 2*M_PI;
+            while (bSky->r > 2*M_PI)
+                bSky->r -= 2*M_PI;
+            while (bSky->r <      0)
+                bSky->r += 2*M_PI;
+
+            ok_float(aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, aSky->r*PS_DEG_RAD - bSky->r*PS_DEG_RAD);
+            ok_float(aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, aSky->d*PS_DEG_RAD - bSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (aSky);
+    psFree (aTPA);
+    psFree (aFPA);
+    psFree (aChip);
+
+    psFree (bSky);
+    psFree (bChip);
+
+    psFree (wcs);
+    psFree (header2);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header1);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test2x()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 50.0;
+    coords.crpix2 = -20.0;
+    coords.cdelt1 = 1.0/3600.0;
+    coords.cdelt2 = 1.0/3600.0;
+    coords.pc1_1  = 0.9;
+    coords.pc1_2  = 0.1;
+    coords.pc2_1  = -0.1;
+    coords.pc2_2  = 0.9;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    psMetadata *header1 = WriteCoordsToHeader (&coords);
+
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header1, 10.0);
+
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psMetadata *header2 = psMetadataAlloc();
+    status = pmAstromWriteWCS (header2, fpa, chip, 0.001);
+
+    pmAstromWCS *wcs = pmAstromWCSfromHeader(header2);
+
+    psPlane  *aChip = psPlaneAlloc();
+    psPlane  *aFPA = psPlaneAlloc();
+    psPlane  *aTPA = psPlaneAlloc();
+    psSphere *aSky = psSphereAlloc();
+
+    psPlane  *bChip = psPlaneAlloc();
+    psSphere *bSky = psSphereAlloc();
+
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            aChip->x = x;
+            aChip->y = y;
+            bChip->x = x;
+            bChip->y = y;
+
+            psPlaneTransformApply (aFPA, chip->toFPA, aChip);
+            psPlaneTransformApply (aTPA, fpa->toTPA, aFPA);
+            psDeproject (aSky, aTPA, fpa->toSky);
+
+            pmAstromWCStoSky (bSky, wcs, bChip);
+
+            while (aSky->r > 2*M_PI)
+                aSky->r -= 2*M_PI;
+            while (aSky->r <      0)
+                aSky->r += 2*M_PI;
+            while (bSky->r > 2*M_PI)
+                bSky->r -= 2*M_PI;
+            while (bSky->r <      0)
+                bSky->r += 2*M_PI;
+
+            ok_float(aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, aSky->r*PS_DEG_RAD - bSky->r*PS_DEG_RAD);
+            ok_float(aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, "coordinate match: %f vs %f (delta = %f)", aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, aSky->d*PS_DEG_RAD - bSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (aSky);
+    psFree (aTPA);
+    psFree (aFPA);
+    psFree (aChip);
+
+    psFree (bSky);
+    psFree (bChip);
+
+    psFree (wcs);
+    psFree (header2);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header1);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+void test3x()
+{
+    note("test pmAstromReadWCS");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style coordinate system
+    Coords coords;
+    strcpy (coords.ctype, "RA---TAN");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 20.0;
+    coords.crpix2 = 50.0;
+    coords.cdelt1 = 1.0/3600.0;
+    coords.cdelt2 = 1.0/3600.0;
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+    coords.polyterms[0][0] = 0.01; // L vs X^2
+    coords.polyterms[2][1] = 0.01; // M vs Y^2
+
+    psMetadata *header1 = WriteCoordsToHeader (&coords);
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadWCS (fpa, chip, header1, 10.0);
+
+    ok (status, "converted WCS keywords to WCS astrometry");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psMetadata *header2 = psMetadataAlloc();
+    status = pmAstromWriteWCS (header2, fpa, chip, 0.00001);
+
+    pmAstromWCS *wcs = pmAstromWCSfromHeader(header2);
+
+    psPlane  *aChip = psPlaneAlloc();
+    psPlane  *aFPA = psPlaneAlloc();
+    psPlane  *aTPA = psPlaneAlloc();
+    psSphere *aSky = psSphereAlloc();
+
+    psPlane  *bChip = psPlaneAlloc();
+    psSphere *bSky = psSphereAlloc();
+
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            aChip->x = x;
+            aChip->y = y;
+            bChip->x = x;
+            bChip->y = y;
+
+            psPlaneTransformApply (aFPA, chip->toFPA, aChip);
+            psPlaneTransformApply (aTPA, fpa->toTPA, aFPA);
+            psDeproject (aSky, aTPA, fpa->toSky);
+
+            pmAstromWCStoSky (bSky, wcs, bChip);
+
+            while (aSky->r > 2*M_PI)
+                aSky->r -= 2*M_PI;
+            while (aSky->r <      0)
+                aSky->r += 2*M_PI;
+            while (bSky->r > 2*M_PI)
+                bSky->r -= 2*M_PI;
+            while (bSky->r <      0)
+                bSky->r += 2*M_PI;
+
+            // XXX we are getting round-off errors as a result of the wcs transformation
+            // having terms in units of pix/degree. for now require 10mas on this
+            ok_float_tol(aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, 0.01/3600.0, "coordinate match: %f vs %f (delta = %f)", aSky->r*PS_DEG_RAD, bSky->r*PS_DEG_RAD, aSky->r*PS_DEG_RAD - bSky->r*PS_DEG_RAD);
+            ok_float_tol(aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, 0.01/3600.0, "coordinate match: %f vs %f (delta = %f)", aSky->d*PS_DEG_RAD, bSky->d*PS_DEG_RAD, aSky->d*PS_DEG_RAD - bSky->d*PS_DEG_RAD);
+        }
+    }
+    psFree (aSky);
+    psFree (aTPA);
+    psFree (aFPA);
+    psFree (aChip);
+
+    psFree (bSky);
+    psFree (bChip);
+
+    psFree (wcs);
+    psFree (header2);
+
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+    psFree (header1);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+psMetadata *WriteCoordsToHeader (Coords *coords)
+{
+
+    char name[16];
+
+    // construct a header using coords as the input
+    psMetadata *header = psMetadataAlloc();
+
+    sprintf (name, "RA--%s", &coords[0].ctype[4]);
+    psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE1", PS_META_REPLACE, "", name);
+    sprintf (name, "DEC-%s", &coords[0].ctype[4]);
+    psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE2", PS_META_REPLACE, "", name);
+
+    // center coords (R,D)
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CRVAL1", PS_META_REPLACE, "", coords[0].crval1);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CRVAL2", PS_META_REPLACE, "", coords[0].crval2);
+
+    // center coords (X,Y)
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CRPIX1", PS_META_REPLACE, "", coords[0].crpix1);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CRPIX2", PS_META_REPLACE, "", coords[0].crpix2);
+
+    // degrees per pixel
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CDELT1",  PS_META_REPLACE, "", coords[0].cdelt1);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "CDELT2",  PS_META_REPLACE, "", coords[0].cdelt2);
+
+    // rotation matrix
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PC001001", PS_META_REPLACE, "", coords[0].pc1_1);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PC001002", PS_META_REPLACE, "", coords[0].pc1_2);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PC002001", PS_META_REPLACE, "", coords[0].pc2_1);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PC002002", PS_META_REPLACE, "", coords[0].pc2_2);
+
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA1X2Y0", PS_META_REPLACE, "", coords[0].polyterms[0][0]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA1X1Y1", PS_META_REPLACE, "", coords[0].polyterms[1][0]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA1X0Y2", PS_META_REPLACE, "", coords[0].polyterms[2][0]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA2X2Y0", PS_META_REPLACE, "", coords[0].polyterms[0][1]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA2X1Y1", PS_META_REPLACE, "", coords[0].polyterms[1][1]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA2X0Y2", PS_META_REPLACE, "", coords[0].polyterms[2][1]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA1X3Y0", PS_META_REPLACE, "", coords[0].polyterms[3][0]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA1X2Y1", PS_META_REPLACE, "", coords[0].polyterms[4][0]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA1X1Y2", PS_META_REPLACE, "", coords[0].polyterms[5][0]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA1X0Y3", PS_META_REPLACE, "", coords[0].polyterms[6][0]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA2X3Y0", PS_META_REPLACE, "", coords[0].polyterms[3][1]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA2X2Y1", PS_META_REPLACE, "", coords[0].polyterms[4][1]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA2X1Y2", PS_META_REPLACE, "", coords[0].polyterms[5][1]);
+    psMetadataAddF64 (header, PS_LIST_TAIL, "PCA2X0Y3", PS_META_REPLACE, "", coords[0].polyterms[6][1]);
+
+    psMetadataAddS32 (header, PS_LIST_TAIL, "NPLYTERM", PS_META_REPLACE, "", coords[0].Npolyterms);
+
+    return header;
+}
+
+# else
+
+    int main (void)
+{
+    plan_tests(2);
+
+    ok(true, "Skipping tests: (libdvo not available)");
+    note("pmAstrometryWCS tests compared with DVO coords routines : SKIPPED (libdvo not available)");
+
+    return exit_status();
+}
+
+# endif
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO4.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO4.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmAstrometryWCS_DVO4.c	(revision 22322)
@@ -0,0 +1,956 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+// HAVA_KAPA checks for Ohana libraries, needed for the Coords structure
+# if (HAVE_KAPA)
+# include "dvo.h"
+
+psMetadata *WriteCoordsToHeader (Coords *coords);
+void test1(); // basic WRP+DIS projections,
+void test2(); // small rotation
+void test3(); // 2nd order terms in WRP
+void test4(); // small rotation in WRP and DIS
+void test5(); // 2nd order terms in WRP and DIS
+void test1x(); // basic WRP+DIS projections with central offset
+void test2x(); // small rotation with central offset
+void test3x(); // 2nd order term in WRP with central offset
+
+int main (void)
+{
+    plan_tests(1329);
+
+    note("pmAstromWriteWCS tests compared with DVO coords routines");
+
+    test1();
+    test2();
+    test3();
+    test4();
+    test5();
+    test1x();
+    test2x();
+    test3x();
+
+    return exit_status();
+}
+
+// create a fake chip-level mosaic header
+void test1()
+{
+    note("test pmAstrom Read,Write BilevelChip");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style mosaic coordinate system
+    // chip-level data (chip -> fpa)
+    Coords coords;
+    strcpy (coords.ctype, "RA---WRP");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 10.0; // microns per pixel
+    coords.cdelt2 = 10.0; // microns per pixel
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    // mosaic-level data (fpa->sky)
+    Coords mosaic;
+    strcpy (mosaic.ctype, "RA---DIS");
+    mosaic.crval1 = 0.0;
+    mosaic.crval2 = 0.0;
+    mosaic.crpix1 = 0.0;
+    mosaic.crpix2 = 0.0;
+    mosaic.cdelt1 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.cdelt2 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.pc1_1  = 1.0;
+    mosaic.pc1_2  = 0.0;
+    mosaic.pc2_1  = 0.0;
+    mosaic.pc2_2  = 1.0;
+    mosaic.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        mosaic.polyterms[i][0] = 0.0;
+        mosaic.polyterms[i][1] = 0.0;
+    }
+    RegisterMosaic (&mosaic);
+
+    psMetadata *headerChp = WriteCoordsToHeader (&coords);
+    psMetadata *headerMos = WriteCoordsToHeader (&mosaic);
+
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadBilevelChip (chip, headerChp);
+    ok (status, "read bilevel chip header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    bool status = pmAstromReadBilevelMosaic (fpa, headerMos);
+    ok (status, "read bilevel fpa header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane  *onChip = psPlaneAlloc();
+    psPlane  *onFPA  = psPlaneAlloc();
+    psPlane  *onTPA  = psPlaneAlloc();
+    psSphere *onSky  = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            // fprintf (stderr, "fpa x: %f vs %f : %f\n", rDVO, onFPA->x, rDVO - onFPA->x);
+            // fprintf (stderr, "fpa y: %f vs %f : %f\n", dDVO, onFPA->y, dDVO - onFPA->y);
+
+            ok_float(onSky->r*PM_DEG_RAD, rDVO, "coordinate match: %f vs %f (delta = %f)", onSky->r*PM_DEG_RAD, rDVO, 3600.0*(onSky->r*PM_DEG_RAD - rDVO));
+            ok_float(onSky->d*PM_DEG_RAD, dDVO, "coordinate match: %f vs %f (delta = %f)", onSky->d*PM_DEG_RAD, dDVO, 3600.0*(onSky->d*PM_DEG_RAD - dDVO));
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+
+    psFree (headerMos);
+    psFree (headerChp);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+// create a fake chip-level mosaic header
+void test2()
+{
+    note("test pmAstrom Read,Write BilevelChip");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style mosaic coordinate system
+    // chip-level data (chip -> fpa)
+    Coords coords;
+    strcpy (coords.ctype, "RA---WRP");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 10.0; // microns per pixel
+    coords.cdelt2 = 10.0; // microns per pixel
+    coords.pc1_1  = 0.9;
+    coords.pc1_2  = 0.1;
+    coords.pc2_1  =-0.1;
+    coords.pc2_2  = 0.9;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    // mosaic-level data (fpa->sky)
+    Coords mosaic;
+    strcpy (mosaic.ctype, "RA---DIS");
+    mosaic.crval1 = 0.0;
+    mosaic.crval2 = 0.0;
+    mosaic.crpix1 = 0.0;
+    mosaic.crpix2 = 0.0;
+    mosaic.cdelt1 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.cdelt2 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.pc1_1  = 1.0;
+    mosaic.pc1_2  = 0.0;
+    mosaic.pc2_1  = 0.0;
+    mosaic.pc2_2  = 1.0;
+    mosaic.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        mosaic.polyterms[i][0] = 0.0;
+        mosaic.polyterms[i][1] = 0.0;
+    }
+    RegisterMosaic (&mosaic);
+
+    psMetadata *headerChp = WriteCoordsToHeader (&coords);
+    psMetadata *headerMos = WriteCoordsToHeader (&mosaic);
+
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadBilevelChip (chip, headerChp);
+    ok (status, "read bilevel chip header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    bool status = pmAstromReadBilevelMosaic (fpa, headerMos);
+    ok (status, "read bilevel fpa header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane  *onChip = psPlaneAlloc();
+    psPlane  *onFPA  = psPlaneAlloc();
+    psPlane  *onTPA  = psPlaneAlloc();
+    psSphere *onSky  = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            // fprintf (stderr, "fpa x: %f vs %f : %f\n", rDVO, onFPA->x, rDVO - onFPA->x);
+            // fprintf (stderr, "fpa y: %f vs %f : %f\n", dDVO, onFPA->y, dDVO - onFPA->y);
+
+            ok_float(onSky->r*PS_DEG_RAD, rDVO, "coordinate match: %f vs %f (delta = %f)", onSky->r*PS_DEG_RAD, rDVO, 3600.0*(onSky->r*PS_DEG_RAD - rDVO));
+            ok_float(onSky->d*PS_DEG_RAD, dDVO, "coordinate match: %f vs %f (delta = %f)", onSky->d*PS_DEG_RAD, dDVO, 3600.0*(onSky->d*PS_DEG_RAD - dDVO));
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+
+    psFree (headerMos);
+    psFree (headerChp);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+// create a fake chip-level mosaic header
+void test3()
+{
+    note("test pmAstrom Read,Write BilevelChip");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style mosaic coordinate system
+    // chip-level data (chip -> fpa)
+    Coords coords;
+    strcpy (coords.ctype, "RA---WRP");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 10.0; // microns per pixel
+    coords.cdelt2 = 10.0; // microns per pixel
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+    coords.polyterms[0][0] = 0.01; // L vs X^2
+    coords.polyterms[2][1] = 0.01; // M vs Y^2
+
+    // mosaic-level data (fpa->sky)
+    Coords mosaic;
+    strcpy (mosaic.ctype, "RA---DIS");
+    mosaic.crval1 = 0.0;
+    mosaic.crval2 = 0.0;
+    mosaic.crpix1 = 0.0;
+    mosaic.crpix2 = 0.0;
+    mosaic.cdelt1 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.cdelt2 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.pc1_1  = 1.0;
+    mosaic.pc1_2  = 0.0;
+    mosaic.pc2_1  = 0.0;
+    mosaic.pc2_2  = 1.0;
+    mosaic.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        mosaic.polyterms[i][0] = 0.0;
+        mosaic.polyterms[i][1] = 0.0;
+    }
+    RegisterMosaic (&mosaic);
+
+    psMetadata *headerChp = WriteCoordsToHeader (&coords);
+    psMetadata *headerMos = WriteCoordsToHeader (&mosaic);
+
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadBilevelChip (chip, headerChp);
+    ok (status, "read bilevel chip header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    bool status = pmAstromReadBilevelMosaic (fpa, headerMos);
+    ok (status, "read bilevel fpa header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane  *onChip = psPlaneAlloc();
+    psPlane  *onFPA  = psPlaneAlloc();
+    psPlane  *onTPA  = psPlaneAlloc();
+    psSphere *onSky  = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            // fprintf (stderr, "fpa x: %f vs %f : %f\n", rDVO, onFPA->x, rDVO - onFPA->x);
+            // fprintf (stderr, "fpa y: %f vs %f : %f\n", dDVO, onFPA->y, dDVO - onFPA->y);
+
+            ok_float(onSky->r*PM_DEG_RAD, rDVO, "coordinate match: %f vs %f (delta = %f)", onSky->r*PM_DEG_RAD, rDVO, 3600.0*(onSky->r*PM_DEG_RAD - rDVO));
+            ok_float(onSky->d*PM_DEG_RAD, dDVO, "coordinate match: %f vs %f (delta = %f)", onSky->d*PM_DEG_RAD, dDVO, 3600.0*(onSky->d*PM_DEG_RAD - dDVO));
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+
+    psFree (headerMos);
+    psFree (headerChp);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+// create a fake chip-level mosaic header
+void test4()
+{
+    note("test pmAstrom Read,Write BilevelChip");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style mosaic coordinate system
+    // chip-level data (chip -> fpa)
+    Coords coords;
+    strcpy (coords.ctype, "RA---WRP");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 10.0; // microns per pixel
+    coords.cdelt2 = 10.0; // microns per pixel
+    coords.pc1_1  = 0.9;
+    coords.pc1_2  = 0.1;
+    coords.pc2_1  =-0.1;
+    coords.pc2_2  = 0.9;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    // mosaic-level data (fpa->sky)
+    Coords mosaic;
+    strcpy (mosaic.ctype, "RA---DIS");
+    mosaic.crval1 = 0.0;
+    mosaic.crval2 = 0.0;
+    mosaic.crpix1 = 0.0;
+    mosaic.crpix2 = 0.0;
+    mosaic.cdelt1 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.cdelt2 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.pc1_1  = 0.9;
+    mosaic.pc1_2  =-0.1;
+    mosaic.pc2_1  = 0.1;
+    mosaic.pc2_2  = 0.9;
+    mosaic.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        mosaic.polyterms[i][0] = 0.0;
+        mosaic.polyterms[i][1] = 0.0;
+    }
+    RegisterMosaic (&mosaic);
+
+    psMetadata *headerChp = WriteCoordsToHeader (&coords);
+    psMetadata *headerMos = WriteCoordsToHeader (&mosaic);
+
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadBilevelChip (chip, headerChp);
+    ok (status, "read bilevel chip header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    bool status = pmAstromReadBilevelMosaic (fpa, headerMos);
+    ok (status, "read bilevel fpa header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane  *onChip = psPlaneAlloc();
+    psPlane  *onFPA  = psPlaneAlloc();
+    psPlane  *onTPA  = psPlaneAlloc();
+    psSphere *onSky  = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            // fprintf (stderr, "fpa x: %f vs %f : %f\n", rDVO, onFPA->x, rDVO - onFPA->x);
+            // fprintf (stderr, "fpa y: %f vs %f : %f\n", dDVO, onFPA->y, dDVO - onFPA->y);
+
+            ok_float(onSky->r*PM_DEG_RAD, rDVO, "coordinate match: %f vs %f (delta = %f)", onSky->r*PM_DEG_RAD, rDVO, 3600.0*(onSky->r*PM_DEG_RAD - rDVO));
+            ok_float(onSky->d*PM_DEG_RAD, dDVO, "coordinate match: %f vs %f (delta = %f)", onSky->d*PM_DEG_RAD, dDVO, 3600.0*(onSky->d*PM_DEG_RAD - dDVO));
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+
+    psFree (headerMos);
+    psFree (headerChp);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+// create a fake chip-level mosaic header
+void test5()
+{
+    note("test pmAstrom Read,Write BilevelChip");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style mosaic coordinate system
+    // chip-level data (chip -> fpa)
+    Coords coords;
+    strcpy (coords.ctype, "RA---WRP");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = 0.0;
+    coords.crpix2 = 0.0;
+    coords.cdelt1 = 10.0; // microns per pixel
+    coords.cdelt2 = 10.0; // microns per pixel
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+    coords.polyterms[0][0] = 0.01; // L vs X^2
+    coords.polyterms[2][1] = 0.01; // M vs Y^2
+
+    // mosaic-level data (fpa->sky)
+    Coords mosaic;
+    strcpy (mosaic.ctype, "RA---DIS");
+    mosaic.crval1 = 0.0;
+    mosaic.crval2 = 0.0;
+    mosaic.crpix1 = 0.0;
+    mosaic.crpix2 = 0.0;
+    mosaic.cdelt1 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.cdelt2 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.pc1_1  = 1.0;
+    mosaic.pc1_2  = 0.0;
+    mosaic.pc2_1  = 0.0;
+    mosaic.pc2_2  = 1.0;
+    mosaic.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        mosaic.polyterms[i][0] = 0.0;
+        mosaic.polyterms[i][1] = 0.0;
+    }
+    RegisterMosaic (&mosaic);
+    mosaic.polyterms[0][0] = 0.01; // L vs X^2
+    mosaic.polyterms[2][1] = 0.01; // M vs Y^2
+
+    psMetadata *headerChp = WriteCoordsToHeader (&coords);
+    psMetadata *headerMos = WriteCoordsToHeader (&mosaic);
+
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadBilevelChip (chip, headerChp);
+    ok (status, "read bilevel chip header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    bool status = pmAstromReadBilevelMosaic (fpa, headerMos);
+    ok (status, "read bilevel fpa header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane  *onChip = psPlaneAlloc();
+    psPlane  *onFPA  = psPlaneAlloc();
+    psPlane  *onTPA  = psPlaneAlloc();
+    psSphere *onSky  = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            // fprintf (stderr, "fpa x: %f vs %f : %f\n", rDVO, onFPA->x, rDVO - onFPA->x);
+            // fprintf (stderr, "fpa y: %f vs %f : %f\n", dDVO, onFPA->y, dDVO - onFPA->y);
+
+            ok_float(onSky->r*PM_DEG_RAD, rDVO, "coordinate match: %f vs %f (delta = %f)", onSky->r*PM_DEG_RAD, rDVO, 3600.0*(onSky->r*PM_DEG_RAD - rDVO));
+            ok_float(onSky->d*PM_DEG_RAD, dDVO, "coordinate match: %f vs %f (delta = %f)", onSky->d*PM_DEG_RAD, dDVO, 3600.0*(onSky->d*PM_DEG_RAD - dDVO));
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+
+    psFree (headerMos);
+    psFree (headerChp);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+// create a fake chip-level mosaic header
+void test1x()
+{
+    note("test pmAstrom Read,Write BilevelChip");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style mosaic coordinate system
+    // chip-level data (chip -> fpa)
+    Coords coords;
+    strcpy (coords.ctype, "RA---WRP");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = +50.0;
+    coords.crpix2 = -20.0;
+    coords.cdelt1 = 10.0; // microns per pixel
+    coords.cdelt2 = 10.0; // microns per pixel
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    // mosaic-level data (fpa->sky)
+    Coords mosaic;
+    strcpy (mosaic.ctype, "RA---DIS");
+    mosaic.crval1 = 0.0;
+    mosaic.crval2 = 0.0;
+    mosaic.crpix1 = 0.0;
+    mosaic.crpix2 = 0.0;
+    mosaic.cdelt1 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.cdelt2 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.pc1_1  = 1.0;
+    mosaic.pc1_2  = 0.0;
+    mosaic.pc2_1  = 0.0;
+    mosaic.pc2_2  = 1.0;
+    mosaic.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        mosaic.polyterms[i][0] = 0.0;
+        mosaic.polyterms[i][1] = 0.0;
+    }
+    RegisterMosaic (&mosaic);
+
+    psMetadata *headerChp = WriteCoordsToHeader (&coords);
+    psMetadata *headerMos = WriteCoordsToHeader (&mosaic);
+
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadBilevelChip (chip, headerChp);
+    ok (status, "read bilevel chip header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    bool status = pmAstromReadBilevelMosaic (fpa, headerMos);
+    ok (status, "read bilevel fpa header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane  *onChip = psPlaneAlloc();
+    psPlane  *onFPA  = psPlaneAlloc();
+    psPlane  *onTPA  = psPlaneAlloc();
+    psSphere *onSky  = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            // fprintf (stderr, "fpa x: %f vs %f : %f\n", rDVO, onFPA->x, rDVO - onFPA->x);
+            // fprintf (stderr, "fpa y: %f vs %f : %f\n", dDVO, onFPA->y, dDVO - onFPA->y);
+
+            ok_float(onSky->r*PM_DEG_RAD, rDVO, "coordinate match: %f vs %f (delta = %f)", onSky->r*PM_DEG_RAD, rDVO, 3600.0*(onSky->r*PM_DEG_RAD - rDVO));
+            ok_float(onSky->d*PM_DEG_RAD, dDVO, "coordinate match: %f vs %f (delta = %f)", onSky->d*PM_DEG_RAD, dDVO, 3600.0*(onSky->d*PM_DEG_RAD - dDVO));
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+
+    psFree (headerMos);
+    psFree (headerChp);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+// create a fake chip-level mosaic header
+void test2x()
+{
+    note("test pmAstrom Read,Write BilevelChip");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style mosaic coordinate system
+    // chip-level data (chip -> fpa)
+    Coords coords;
+    strcpy (coords.ctype, "RA---WRP");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = +50.0;
+    coords.crpix2 = -20.0;
+    coords.cdelt1 = 10.0; // microns per pixel
+    coords.cdelt2 = 10.0; // microns per pixel
+    coords.pc1_1  = 0.9;
+    coords.pc1_2  = 0.1;
+    coords.pc2_1  =-0.1;
+    coords.pc2_2  = 0.9;
+    coords.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+
+    // mosaic-level data (fpa->sky)
+    Coords mosaic;
+    strcpy (mosaic.ctype, "RA---DIS");
+    mosaic.crval1 = 0.0;
+    mosaic.crval2 = 0.0;
+    mosaic.crpix1 = 0.0;
+    mosaic.crpix2 = 0.0;
+    mosaic.cdelt1 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.cdelt2 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.pc1_1  = 1.0;
+    mosaic.pc1_2  = 0.0;
+    mosaic.pc2_1  = 0.0;
+    mosaic.pc2_2  = 1.0;
+    mosaic.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        mosaic.polyterms[i][0] = 0.0;
+        mosaic.polyterms[i][1] = 0.0;
+    }
+    RegisterMosaic (&mosaic);
+
+    psMetadata *headerChp = WriteCoordsToHeader (&coords);
+    psMetadata *headerMos = WriteCoordsToHeader (&mosaic);
+
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadBilevelChip (chip, headerChp);
+    ok (status, "read bilevel chip header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    bool status = pmAstromReadBilevelMosaic (fpa, headerMos);
+    ok (status, "read bilevel fpa header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane  *onChip = psPlaneAlloc();
+    psPlane  *onFPA  = psPlaneAlloc();
+    psPlane  *onTPA  = psPlaneAlloc();
+    psSphere *onSky  = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            // fprintf (stderr, "fpa x: %f vs %f : %f\n", rDVO, onFPA->x, rDVO - onFPA->x);
+            // fprintf (stderr, "fpa y: %f vs %f : %f\n", dDVO, onFPA->y, dDVO - onFPA->y);
+
+            ok_float(onSky->r*PM_DEG_RAD, rDVO, "coordinate match: %f vs %f (delta = %f)", onSky->r*PM_DEG_RAD, rDVO, 3600.0*(onSky->r*PM_DEG_RAD - rDVO));
+            ok_float(onSky->d*PM_DEG_RAD, dDVO, "coordinate match: %f vs %f (delta = %f)", onSky->d*PM_DEG_RAD, dDVO, 3600.0*(onSky->d*PM_DEG_RAD - dDVO));
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+
+    psFree (headerMos);
+    psFree (headerChp);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+// create a fake chip-level mosaic header
+void test3x()
+{
+    note("test pmAstrom Read,Write BilevelChip");
+    psMemId id = psMemGetId();
+
+    // build a DVO-style mosaic coordinate system
+    // chip-level data (chip -> fpa)
+    Coords coords;
+    strcpy (coords.ctype, "RA---WRP");
+    coords.crval1 = 0.0;
+    coords.crval2 = 0.0;
+    coords.crpix1 = +50.0;
+    coords.crpix2 = -20.0;
+    coords.cdelt1 = 10.0; // microns per pixel
+    coords.cdelt2 = 10.0; // microns per pixel
+    coords.pc1_1  = 1.0;
+    coords.pc1_2  = 0.0;
+    coords.pc2_1  = 0.0;
+    coords.pc2_2  = 1.0;
+    coords.Npolyterms = 2;
+    for (int i = 0; i < 7; i++) {
+        coords.polyterms[i][0] = 0.0;
+        coords.polyterms[i][1] = 0.0;
+    }
+    coords.polyterms[0][0] = 0.01; // L vs X^2
+    coords.polyterms[2][1] = 0.01; // M vs Y^2
+
+    // mosaic-level data (fpa->sky)
+    Coords mosaic;
+    strcpy (mosaic.ctype, "RA---DIS");
+    mosaic.crval1 = 0.0;
+    mosaic.crval2 = 0.0;
+    mosaic.crpix1 = 0.0;
+    mosaic.crpix2 = 0.0;
+    mosaic.cdelt1 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.cdelt2 = 1.0 / 10.0 / 3600.0; // degrees per micron
+    mosaic.pc1_1  = 1.0;
+    mosaic.pc1_2  = 0.0;
+    mosaic.pc2_1  = 0.0;
+    mosaic.pc2_2  = 1.0;
+    mosaic.Npolyterms = 0;
+    for (int i = 0; i < 7; i++) {
+        mosaic.polyterms[i][0] = 0.0;
+        mosaic.polyterms[i][1] = 0.0;
+    }
+    RegisterMosaic (&mosaic);
+
+    psMetadata *headerChp = WriteCoordsToHeader (&coords);
+    psMetadata *headerMos = WriteCoordsToHeader (&mosaic);
+
+    pmFPA *fpa = pmFPAAlloc (NULL);
+    pmChip *chip = pmChipAlloc (fpa, NULL);
+
+    bool status = pmAstromReadBilevelChip (chip, headerChp);
+    ok (status, "read bilevel chip header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    bool status = pmAstromReadBilevelMosaic (fpa, headerMos);
+    ok (status, "read bilevel fpa header");
+    skip_start (!status, 1, "*** WCS Conversion FAILS *** : skipping related tests");
+
+    psPlane  *onChip = psPlaneAlloc();
+    psPlane  *onFPA  = psPlaneAlloc();
+    psPlane  *onTPA  = psPlaneAlloc();
+    psSphere *onSky  = psSphereAlloc();
+
+    double rDVO, dDVO;
+    for (double x = -2000; x <= +2000; x+= 500.0) {
+        for (double y = -2000; y <= +2000; y+= 500.0) {
+            XY_to_RD (&rDVO, &dDVO, x, y, &coords);
+
+            onChip->x = x;
+            onChip->y = y;
+
+            psPlaneTransformApply (onFPA, chip->toFPA, onChip);
+            psPlaneTransformApply (onTPA, fpa->toTPA, onFPA);
+            psDeproject (onSky, onTPA, fpa->toSky);
+
+            while (onSky->r > 2*M_PI)
+                onSky->r -= 2*M_PI;
+            while (onSky->r <      0)
+                onSky->r += 2*M_PI;
+
+            // fprintf (stderr, "fpa x: %f vs %f : %f\n", rDVO, onFPA->x, rDVO - onFPA->x);
+            // fprintf (stderr, "fpa y: %f vs %f : %f\n", dDVO, onFPA->y, dDVO - onFPA->y);
+
+            ok_float(onSky->r*PM_DEG_RAD, rDVO, "coordinate match: %f vs %f (delta = %f)", onSky->r*PM_DEG_RAD, rDVO, 3600.0*(onSky->r*PM_DEG_RAD - rDVO));
+            ok_float(onSky->d*PM_DEG_RAD, dDVO, "coordinate match: %f vs %f (delta = %f)", onSky->d*PM_DEG_RAD, dDVO, 3600.0*(onSky->d*PM_DEG_RAD - dDVO));
+        }
+    }
+    psFree (onSky);
+    psFree (onTPA);
+    psFree (onFPA);
+    psFree (onChip);
+
+    skip_end();
+    skip_end();
+    psFree (fpa);
+    psFree (chip);
+
+    psFree (headerMos);
+    psFree (headerChp);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+}
+
+psMetadata *WriteCoordsToHeader (Coords *coords)
+{
+
+    char name[16];
+
+    // construct a header using coords as the input
+    psMetadata *header = psMetadataAlloc();
+
+    sprintf (name, "RA--%s", &coords[0].ctype[4]);
+    psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE1", PS_META_REPLACE, "", name);
+    sprintf (name, "DEC-%s", &coords[0].ctype[4]);
+    psMetadataAddStr (header, PS_LIST_TAIL, "CTYPE2", PS_META_REPLACE, "", name);
+
+    // center coords (R,D)
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRVAL1", PS_META_REPLACE, "", coords[0].crval1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRVAL2", PS_META_REPLACE, "", coords[0].crval2);
+
+    // center coords (X,Y)
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRPIX1", PS_META_REPLACE, "", coords[0].crpix1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CRPIX2", PS_META_REPLACE, "", coords[0].crpix2);
+
+    // degrees per pixel
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CDELT1",  PS_META_REPLACE, "", coords[0].cdelt1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "CDELT2",  PS_META_REPLACE, "", coords[0].cdelt2);
+
+    // rotation matrix
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC001001", PS_META_REPLACE, "", coords[0].pc1_1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC001002", PS_META_REPLACE, "", coords[0].pc1_2);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC002001", PS_META_REPLACE, "", coords[0].pc2_1);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PC002002", PS_META_REPLACE, "", coords[0].pc2_2);
+
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X2Y0", PS_META_REPLACE, "", coords[0].polyterms[0][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X1Y1", PS_META_REPLACE, "", coords[0].polyterms[1][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X0Y2", PS_META_REPLACE, "", coords[0].polyterms[2][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X2Y0", PS_META_REPLACE, "", coords[0].polyterms[0][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X1Y1", PS_META_REPLACE, "", coords[0].polyterms[1][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X0Y2", PS_META_REPLACE, "", coords[0].polyterms[2][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X3Y0", PS_META_REPLACE, "", coords[0].polyterms[3][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X2Y1", PS_META_REPLACE, "", coords[0].polyterms[4][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X1Y2", PS_META_REPLACE, "", coords[0].polyterms[5][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA1X0Y3", PS_META_REPLACE, "", coords[0].polyterms[6][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X3Y0", PS_META_REPLACE, "", coords[0].polyterms[3][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X2Y1", PS_META_REPLACE, "", coords[0].polyterms[4][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X1Y2", PS_META_REPLACE, "", coords[0].polyterms[5][1]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "PCA2X0Y3", PS_META_REPLACE, "", coords[0].polyterms[6][1]);
+
+    psMetadataAddS32 (header, PS_LIST_TAIL, "NPLYTERM", PS_META_REPLACE, "", coords[0].Npolyterms);
+
+    return header;
+}
+
+# else
+
+    int main (void)
+{
+    plan_tests(2);
+
+    ok(true, "Skipping tests: (libdvo not available)");
+    note("pmAstrometryWCS tests compared with DVO coords routines : SKIPPED (libdvo not available)");
+
+    return exit_status();
+}
+
+# endif
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmHDU.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmHDU.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tap_pmHDU.c	(revision 22322)
@@ -0,0 +1,272 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+//#include <unistd.h>
+
+#define GENIMAGE(img,c,r,TYP, valueFcn) \
+img = psImageAlloc(c,r,PS_TYPE_##TYP); \
+for (psU32 row=0;row<r;row++) { \
+    ps##TYP* imgRow = img->data.TYP[row]; \
+    for (psU32 col=0;col<c;col++) { \
+        imgRow[col] = (ps##TYP)(valueFcn); \
+    } \
+}
+const char* fitsFilename = "tmp.fits";
+const char* fitsFilename2 = "tmp2.fits";
+
+bool createFitsFile(void)
+{
+    psFits* fitsFile = psFitsOpen(fitsFilename, "w");
+
+    if (fitsFile == NULL) {
+        diag("Could not create 'multi' FITS file");
+        return false;
+    }
+
+    psImage* image = psImageAlloc(16, 16, PS_TYPE_F32);
+
+    char extname[80];
+    for (int lcv = 0; lcv < 8; lcv++) {
+        snprintf(extname, 80, "ext-%d", lcv);
+
+        psMetadata* header = psMetadataAlloc();
+
+        psMetadataAdd(header, PS_LIST_TAIL, "MYINT",
+                      PS_DATA_S32,
+                      "psS32 Item", (psS32)lcv);
+
+        psMetadataAdd(header, PS_LIST_TAIL, "MYFLT",
+                      PS_DATA_F32,
+                      "psF32 Item", (float)(1.0f/(float)(1+lcv)));
+
+        psMetadataAdd(header, PS_LIST_TAIL, "MYDBL",
+                      PS_DATA_F64,
+                      "psF64 Item", (double)(1.0/(double)(1+lcv)));
+
+        psMetadataAdd(header, PS_LIST_TAIL, "MYBOOL",
+                      PS_DATA_BOOL,
+                      "psBool Item",
+                      (lcv%2 == 0));
+
+        psMetadataAdd(header, PS_LIST_TAIL, "MYSTR",
+                      PS_DATA_STRING,
+                      "String Item",
+                      extname);
+
+        // set the pixels in the image
+        psImageInit(image, (float)lcv);
+        if (!psFitsWriteImage(fitsFile, header, image, 0, extname)) {
+            diag("Could not write image");
+        }
+        psFree(header);
+    }
+    psFree(image);
+    psFree(fitsFile);
+
+    return true;
+}
+
+
+bool createFitsFile2(void)
+{
+    psFits* fitsFile = psFitsOpen(fitsFilename2, "w");
+
+    if (fitsFile == NULL) {
+        diag("Could not create 'multi' FITS file");
+        return false;
+    }
+
+    psImage* image = psImageAlloc(16, 16, PS_TYPE_F32);
+
+    char extname[80];
+    for (int lcv = 0; lcv < 8; lcv++) {
+        snprintf(extname, 80, "ext-%d", lcv);
+        pmHDU *hdu = pmHDUAlloc(extname);
+        hdu->header = psMetadataAlloc();
+
+        psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYINT",
+                      PS_DATA_S32,
+                      "psS32 Item", (psS32)lcv);
+
+        psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYFLT",
+                      PS_DATA_F32,
+                      "psF32 Item", (float)(1.0f/(float)(1+lcv)));
+
+        psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYDBL",
+                      PS_DATA_F64,
+                      "psF64 Item", (double)(1.0/(double)(1+lcv)));
+
+        psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYBOOL",
+                      PS_DATA_BOOL,
+                      "psBool Item",
+                      (lcv%2 == 0));
+
+        psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYSTR",
+                      PS_DATA_STRING,
+                      "String Item",
+                      extname);
+
+        // set the pixels in the image
+        psImageInit(image, (float)lcv);
+        if(!pmHDUWrite(hdu, fitsFile)) {
+            diag("Could not write header");
+            psErrorStackPrint(stdout, "THIS");
+
+exit(0);
+        }
+        if (!psFitsWriteImage(fitsFile, NULL, image, 0, extname)) {
+            diag("Could not write image");
+        }
+
+        psFree(hdu);
+    }
+    psFree(image);
+    psFree(fitsFile);
+
+    return true;
+}
+
+
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(18);
+      psError(PS_ERR_IO, true, "HEY: ERROR");
+
+    psHistogram *myHist = psHistogramAlloc(10, 5, 1);
+    psFree(myHist);
+
+    // Test pmHDUAlloc()
+    // Use a NULL extname
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc(NULL);
+        ok(hdu, "pmHDUAlloc(NULL) returned non-NULL");
+        skip_start(!hdu, 7, "Skipping tests because pmHDUAlloc(NULL) returned NULL");
+        ok(hdu->blankPHU == true, "pmHDUAlloc(NULL) set hdu->blankPHU correctly");
+        ok(hdu->extname == NULL, "pmHDUAlloc(NULL) set hdu->extname correctly");
+        ok(hdu->format == NULL, "pmHDUAlloc(NULL) set hdu->format correctly");
+        ok(hdu->header == NULL, "pmHDUAlloc(NULL) set hdu->header correctly");
+        ok(hdu->images == NULL, "pmHDUAlloc(NULL) set hdu->images correctly");
+        ok(hdu->weights == NULL, "pmHDUAlloc(NULL) set hdu->weights correctly");
+        ok(hdu->masks == NULL, "pmHDUAlloc(NULL) set hdu->masks correctly");
+        psFree(hdu);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Use a non-NULL extname
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc("ext-0");
+        ok(hdu, "pmHDUAlloc(extname) returned non-NULL");
+        skip_start(!hdu, 7, "Skipping tests because pmHDUAlloc(extname) returned NULL");
+        ok(hdu->blankPHU == false, "pmHDUAlloc(extname) set hdu->blankPHU correctly");
+        ok(0 == strcmp(hdu->extname, "ext-0"), "pmHDUAlloc(extname) set hdu->extname correctly");
+        ok(hdu->format == NULL, "pmHDUAlloc(extname) set hdu->format correctly");
+        ok(hdu->header == NULL, "pmHDUAlloc(extname) set hdu->header correctly");
+        ok(hdu->images == NULL, "pmHDUAlloc(extname) set hdu->images correctly");
+        ok(hdu->weights == NULL, "pmHDUAlloc(extname) set hdu->weights correctly");
+        ok(hdu->masks == NULL, "pmHDUAlloc(extname) set hdu->masks correctly");
+        psFree(hdu);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // tst_psFitsReadHeader()
+    {
+        ok(createFitsFile2(), "Created test FITS file");
+        psFits* fits = psFitsOpen(fitsFilename2,"r");
+        ok(fits != NULL, "psFitsOpen returned non-NULL on existing file");
+        int numHDUs = psFitsGetSize(fits);
+        ok(numHDUs == 8, "The test FITS file has %d HDUs", numHDUs);
+
+        for (int hdunum = 0; hdunum < numHDUs; hdunum++)
+        {
+            psMemId id = psMemGetId();
+            char extname[80];
+            snprintf(extname,80,"ext-%d",hdunum);
+            psFitsMoveExtNum(fits, hdunum, false);
+            pmHDU *hdu = pmHDUAlloc(extname);
+            bool rc = pmHDUReadHeader(hdu, fits);
+            ok(rc == true, "pmHDUReadHeader() returned TRUE");
+            ok(hdu->header != NULL &&
+               psMemCheckMetadata(hdu->header), "pmHDUReadHeader() correctly returned the hdu->header member");
+
+            // check for the extra metadata items
+            psS32 intItem = psMetadataLookupS32(NULL, hdu->header, "MYINT");
+            psF32 fltItem = psMetadataLookupF32(NULL, hdu->header, "MYFLT");
+            psF64 dblItem = psMetadataLookupF64(NULL, hdu->header, "MYDBL");
+            psMetadataItem* boolItem = psMetadataLookup(hdu->header, "MYBOOL");
+            psString strItem = psMetadataLookupStr(NULL, hdu->header, "MYSTR");
+
+            ok(intItem == hdunum, "Retrieved psS32 metadata item from file");
+            ok(fabsf(fltItem - 1.0f/(float)(1+hdunum)) <= FLT_EPSILON,
+               "Retrieved psF32 metadata item from file.  Got %f vs %f",
+               fltItem,1.0f/(float)(1+hdunum));
+            ok(abs(dblItem - 1.0/(double)(1+hdunum)) <= DBL_EPSILON,
+               "Retrieved psF64 metadata item from file.  Got %g vs %g",
+               dblItem, 1.0/(double)(1+hdunum));
+            ok(boolItem != NULL && boolItem->type == PS_DATA_BOOL,
+               "Retrieved psBool metadata item from file");
+            ok(strItem != NULL && strncmp(strItem,extname,strlen(extname)) == 0,
+               "Retrieved string metadata item from file.  Got '%s' vs '%s' (%d)",
+               strItem,extname,strlen(extname));
+            psFree(hdu);
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (header %d)", hdunum);
+        }
+
+
+        // Call pmHDUReadHeader() with NULL pmHDU input.  Should returne false.
+        {
+            psMemId id = psMemGetId();
+//            pmHDU *hdu = pmHDUAlloc(NULL);
+            bool rc = pmHDUReadHeader(NULL, fits);
+            ok(rc == false, "pmHDUReadHeader() returned FALSE with NULL hdu input");
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        // Call pmHDUReadHeader() with NULL pmFits input.  Should returne false.
+        {
+            psMemId id = psMemGetId();
+            pmHDU *hdu = pmHDUAlloc(NULL);
+            bool rc = pmHDUReadHeader(hdu, NULL);
+            ok(rc == false, "pmHDUReadHeader() returned FALSE with NULL pmFits input");
+            psFree(hdu);
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        }
+
+        psFree(fits);
+    }
+
+
+/* HERE
+    // tst_psFitsWriteHeader()
+    {
+        psMemId id = psMemGetId();
+        ok(createFitsFile2(), "Created 'multi' FITS file");
+
+        psMetadata* header   = psMetadataAlloc();
+        psFits*     fitsFile = psFitsOpen(fitsFilename,"a+");
+
+        // Test psFitsReadWrite generates files from psFitsWriteImage which 
+        // calls psFitsWriteHeader so these additional tests check for error conditions
+        // Attempt call function with NULL metadata
+        ok(!psFitsWriteHeader(fitsFile, NULL), "Expected return of true for NULL metadata pointer");
+        psFree(fitsFile);
+
+        // Attempt to call function with NULL fits
+        ok(!psFitsWriteHeader(NULL, header), "Expected return of true for NULL fits file pointer");
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+HERE*/
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tst_pmAstrometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tst_pmAstrometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tst_pmAstrometry.c	(revision 22322)
@@ -0,0 +1,615 @@
+/** @file  tst_pmAstrometry.c
+ *
+ *  @brief Contains the tests: pmAstrometry.[ch].  The pmxxxAlloc()
+ *  and psFree() functionality are used here.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  XXX: Untested: pmFPACheckParents()
+ *  XXX: Create the pmHDU alloc/free function, test them here
+ *
+ *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-01-26 21:10:51 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "config.h"
+#include <math.h>
+#include <string.h>
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "pmAstrometry.h"
+
+static psS32 testFPAAlloc(void);
+static psS32 testChipAlloc(void);
+static psS32 testCellAlloc(void);
+static psS32 testReadoutAlloc(void);
+
+testDescription tests[] = {
+                              {testFPAAlloc,739,"pmFPAAlloc",0,false},
+                              {testChipAlloc,740,"pmChipAlloc",0,false},
+                              {testCellAlloc,741,"pmCellAlloc",0,false},
+                              {testReadoutAlloc,742,"pmReadoutAlloc",0,false},
+                              {NULL}
+                          };
+
+#define CHIP_ALLOC_NAME "ChipName"
+#define CELL_ALLOC_NAME "CellName"
+#define MISC_NUM 32
+#define MISC_NAME "META00"
+#define MISC_NAME2 "META01"
+#define NUM_BIAS_DATA 10
+#define TEST_NUM_ROWS 32
+#define TEST_NUM_COLS 32
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.  We do this to ensure that the data is
+later being psFree()'ed correctly.
+ *****************************************************************************/
+pmFPA *generateSimpleFPA()
+{
+    psBool rc;
+    pmFPA* fpa = pmFPAAlloc(psMetadataAlloc());
+
+    if (fpa == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc() returned a NULL.");
+        return(NULL);
+    }
+
+    //
+    // Test and create camera metadata.
+    //
+    if (fpa->camera == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: fpa->camera is NULL.");
+        psFree(fpa);
+        return(NULL);
+    } else {
+        rc = psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+        if (rc == false) {
+            psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: could not add metadata to fpa->camera.");
+            psFree(fpa);
+            return(NULL);
+        }
+        psS32 tmpS32 = psMetadataLookupS32(&rc, fpa->camera, MISC_NAME);
+        if ((rc == false) || (tmpS32 != MISC_NUM)) {
+            psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: could not read metadata from fpa->camera.");
+            psFree(fpa);
+            return(NULL);
+        }
+    }
+
+    //
+    // Create various transforms and projections.
+    //
+    fpa->fromTangentPlane = PS_CREATE_4D_IDENTITY_PLANE_DISTORT();
+    fpa->toTangentPlane = PS_CREATE_4D_IDENTITY_PLANE_DISTORT();
+    fpa->projection = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+
+    //
+    // Ensure fpa concepts metadata was allocated properly.
+    //
+    if (fpa->concepts == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc did not set fpa->concepts.");
+        psFree(fpa);
+        return(NULL);
+    } else {
+        rc = psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+        if (rc == false) {
+            psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: could not add data to fpa->concepts.");
+            psFree(fpa);
+            return(NULL);
+        }
+        psS32 tmpS32 = psMetadataLookupS32(&rc, fpa->concepts, MISC_NAME);
+        if ((rc == false) || (tmpS32 != MISC_NUM)) {
+            psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: could not read metadata from fpa->concepts.");
+            psFree(fpa);
+            return(NULL);
+        }
+    }
+
+    //
+    // Create ->analysis metadata.
+    //
+    fpa->analysis = psMetadataAlloc();
+    rc = psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    //
+    // We test the fpa->chips array later.
+    //
+
+    //
+    // How to test the p_pmHDU *hdu member?
+    //
+
+    //
+    // Create ->phu metadata.
+    //
+    fpa->phu = psMetadataAlloc();
+    rc = psMetadataAddS32(fpa->phu, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    return(fpa);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+    psLogSetFormat("HLNM");
+
+    return ! runTestSuite(stderr,"pmAstrometry",tests,argc,argv);
+}
+
+/******************************************************************************
+testFPAAlloc()
+    1: We ensure that pmFPAAlloc() properly allocates a pmFPA struct.
+    2: We populate the members with real data to ensure they are being
+       free'ed correctly.
+ *****************************************************************************/
+static psS32 testFPAAlloc(void)
+{
+    psMetadata *camera = psMetadataAlloc();
+    pmFPA* fpa = pmFPAAlloc(camera);
+
+    if (fpa == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc() returned a NULL.");
+        return 1;
+    }
+
+    if (fpa->fromTangentPlane != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc did not set ->fromTangentPlane to NULL.");
+        return 2;
+    }
+
+    if (fpa->toTangentPlane != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc did not set ->toTangentPlane to NULL.");
+        return 3;
+    }
+    if (fpa->projection != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc did not set ->projection to NULL.");
+        return 4;
+    }
+
+    if (fpa->concepts == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc did not set ->concepts.");
+        return 5;
+    }
+
+    if (fpa->analysis != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc did not set ->analysis to NULL.");
+        return 6;
+    }
+
+    if (fpa->camera != camera) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc did not set ->camera.");
+        return 7;
+    }
+
+    if (fpa->chips == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc did not set ->chips.");
+        return 8;
+    }
+
+    if (fpa->hdu != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc did not set ->hdu to NULL.");
+        return 9;
+    }
+    psFree(fpa);
+
+    //
+    // Populate the pmFPA struct with real data to ensure they were
+    // psFree()'ed correctly.
+    //
+    fpa = generateSimpleFPA();
+    if (fpa == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: generateSimpleFPA() returned NULL.");
+        return(15);
+    }
+    psFree(fpa);
+
+    return(0);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.  We do this to ensure that the data is
+later being psFree()'ed correctly.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    psBool rc;
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    if (chip == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmChipAlloc returned a NULL.");
+        return(NULL);
+    }
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    //
+    // We already ensured that chip->concepts was working properly.
+    //
+
+    //
+    // Create ->analysis metadata.
+    //
+    chip->analysis = psMetadataAlloc();
+    rc = psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    //
+    // We test the chip->cells array later.
+    //
+
+    //
+    // How to test the p_pmHDU *hdu member?
+    //
+
+    return(chip);
+}
+
+static psS32 testChipAlloc(void)
+{
+    pmFPA* fpa = generateSimpleFPA();
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    if (chip == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmChipAlloc returned a NULL.");
+        return 1;
+    }
+
+    if (chip->col0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: chip->col0 set improperly.\n");
+        return 5;
+    }
+
+    if (chip->row0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: chip->row0 set improperly.\n");
+        return 6;
+    }
+
+    if (chip->toFPA != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: chip->toChip set improperly.\n");
+        return 7;
+    }
+
+    if (chip->fromFPA != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: chip->toFPA set improperly.\n");
+        return 8;
+    }
+
+    if (chip->concepts == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: chip->concepts set improperly.\n");
+        return 21;
+    } else {
+        psMetadataItem *tmpMeta = psMetadataLookup(chip->concepts, "CHIP.NAME");
+        if (0 != strcmp((char *) tmpMeta->data.V, CHIP_ALLOC_NAME)) {
+            psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: The metadata was set improperly.\n");
+            return (32);
+        }
+        // XXX: Code a test to ensure the metadata has the correct type
+    }
+
+    if (chip->analysis != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: chip->analysis set improperly.\n");
+        return 10;
+    }
+
+    if (chip->cells == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: chip->cells set improperly.\n");
+        return 22;
+    }
+
+    if (chip->parent != fpa) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: chip->parent set improperly.\n");
+        return 23;
+    }
+
+    if (chip->valid != false) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: chip->valid set improperly.\n");
+        return 24;
+    }
+
+    if (chip->hdu != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: chip->hdu set improperly.\n");
+        return 25;
+    }
+    psFree(fpa);
+
+    //
+    // Populate the pmChip struct with real data to ensure they were
+    // psFree()'ed correctly.
+    //
+    fpa = generateSimpleFPA();
+    chip = generateSimpleChip(fpa);
+    psFree(fpa);
+
+    return(0);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.  We do this to ensure that the data is
+later being psFree()'ed correctly.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmFPA *fpa, pmChip *chip)
+{
+    psBool rc;
+    pmCell *cell = pmCellAlloc(chip, (psMetadata *) fpa->camera, CELL_ALLOC_NAME);
+    if (cell == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmCellAlloc returned a NULL.");
+        return(NULL);
+    }
+    cell->toChip = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    cell->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    cell->toSky = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    //
+    // We already ensured that cell->concepts was working properly.
+    //
+
+    //
+    // Test camera metadata.
+    //
+    if (cell->camera == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->camera is NULL.");
+        psFree(fpa);
+        return(NULL);
+    } else {
+        rc = psMetadataAddS32((psMetadata *) cell->camera, PS_LIST_HEAD, MISC_NAME2, 0, NULL, MISC_NUM);
+        if (rc == false) {
+            psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: could not add metadata to cell->camera.");
+            psFree(fpa);
+            return(NULL);
+        }
+        psS32 tmpS32 = psMetadataLookupS32(&rc, cell->camera, MISC_NAME2);
+        if ((rc == false) || (tmpS32 != MISC_NUM)) {
+            psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: could not read metadata from cell->camera.");
+            psFree(fpa);
+            return(NULL);
+        }
+    }
+
+    //
+    // Create ->analysis metadata.
+    //
+    cell->analysis = psMetadataAlloc();
+    rc = psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    //
+    // We test the cell->readouts array later.
+    //
+
+    //
+    // How to test the p_pmHDU *hdu member?
+    //
+
+    return(cell);
+}
+
+static psS32 testCellAlloc(void)
+{
+    pmFPA* fpa = generateSimpleFPA();
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    pmCell *cell = pmCellAlloc(chip, (psMetadata *) fpa->camera, CELL_ALLOC_NAME);
+    if (cell == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmCellAlloc returned a NULL.n");
+        return 3;
+    }
+
+    if (cell->col0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->col0 set improperly.\n");
+        return 5;
+    }
+
+    if (cell->row0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->row0 set improperly.\n");
+        return 6;
+    }
+
+    if (cell->toChip != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->toChip set improperly.\n");
+        return 7;
+    }
+
+    if (cell->toFPA != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->toFPA set improperly.\n");
+        return 8;
+    }
+
+    if (cell->toSky != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->toSky set improperly.\n");
+        return 9;
+    }
+
+    if (cell->concepts == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->concepts set improperly.\n");
+        return 21;
+    } else {
+        psMetadataItem *tmpMeta = psMetadataLookup(cell->concepts, "CELL.NAME");
+        if (0 != strcmp((char *) tmpMeta->data.V, CELL_ALLOC_NAME)) {
+            psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: The metadata was set improperly.\n");
+            return (32);
+        }
+        // XXX: Code a test to ensure the metadata has the correct type
+    }
+
+    if (cell->camera != fpa->camera) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->camera set improperly.\n");
+        return 20;
+    }
+
+    if (cell->analysis != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->analysis set improperly.\n");
+        return 10;
+    }
+
+    if (cell->readouts == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->readouts set improperly.\n");
+        return 22;
+    }
+
+    if (cell->parent != chip) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->parent set improperly.\n");
+        return 23;
+    }
+
+    if (cell->valid != false) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->valid set improperly.\n");
+        return 24;
+    }
+
+    if (cell->hdu != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: cell->hdu set improperly.\n");
+        return 27;
+    }
+    psFree(fpa);
+
+    //
+    // Populate the pmCell struct with real data to ensure they were
+    // psFree()'ed correctly.
+    //
+    fpa = generateSimpleFPA();
+    chip = generateSimpleChip(fpa);
+    cell = generateSimpleCell(fpa, chip);
+    psFree(fpa);
+
+    return(0);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.  We do this to ensure that the data is
+later being psFree()'ed correctly.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmFPA *fpa, pmChip *chip, pmCell *cell)
+{
+    psBool rc;
+    pmReadout *readout = pmReadoutAlloc(cell);
+    if (readout == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmReadoutAlloc returned a NULL.");
+        return(NULL);
+    }
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+
+    //
+    // Create a psList of bias data.
+    //
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        if (readout->bias == NULL) {
+            readout->bias = psListAlloc(tmpImage);
+        } else {
+            psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        }
+    }
+
+    //
+    // Test readout->analysis metadata.
+    //
+    if (readout->analysis == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: readout->analysis is NULL.");
+        psFree(fpa);
+        return(NULL);
+    } else {
+        rc = psMetadataAddS32((psMetadata *) readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+        if (rc == false) {
+            psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: could not add metadata to readout->analysis.");
+            psFree(fpa);
+            return(NULL);
+        }
+    }
+
+    return(readout);
+}
+
+
+static psS32 testReadoutAlloc(void)
+{
+    pmFPA* fpa = generateSimpleFPA();
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    pmCell *cell = pmCellAlloc(chip, (psMetadata *) fpa->camera, CELL_ALLOC_NAME);
+    pmReadout *readout = pmReadoutAlloc(cell);
+    if (readout == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmReadoutAlloc returned a NULL.\n");
+        return 4;
+    }
+
+    if (readout->col0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: pmReadout->col0 set improperly.\n");
+        return 5;
+    }
+
+    if (readout->row0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: pmReadout->row0 set improperly.\n");
+        return 6;
+    }
+
+    if (readout->colBins != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: pmReadout->colBins set improperly.\n");
+        return 7;
+    }
+
+    if (readout->rowBins != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: pmReadout->colBins set improperly.\n");
+        return 8;
+    }
+
+    if (readout->image != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: pmReadout->image set improperly.\n");
+        return 10;
+    }
+
+    if (readout->mask != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: pmReadout->mask set improperly.\n");
+        return 12;
+    }
+
+    if (readout->weight != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: pmReadout->weight set improperly.\n");
+        return 14;
+    }
+
+    if (readout->bias != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: pmReadout->bias set improperly.\n");
+        return 16;
+    }
+
+    if (readout->analysis == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: pmReadout->analysis set improperly.\n");
+        return 18;
+    }
+
+    if (readout->parent != cell) {
+        psLogMsg(__func__, PS_LOG_ERROR, "TEST ERROR: pmReadout->parent set improperly.\n");
+        return 20;
+    }
+    psFree(fpa);
+
+    //
+    // Populate the pmReadout struct with real data to ensure they were
+    // psFree()'ed correctly.
+    //
+    fpa = generateSimpleFPA();
+    chip = generateSimpleChip(fpa);
+    cell = generateSimpleCell(fpa, chip);
+    readout = generateSimpleReadout(fpa, chip, cell);
+    psFree(fpa);
+
+    return(0);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tst_pmAstrometry01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tst_pmAstrometry01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/astrom/tst_pmAstrometry01.c	(revision 22322)
@@ -0,0 +1,675 @@
+/** @file  tst_psAstrometry01.c
+*
+*  @brief This code will test the pmFPA hierarchy transform code in psAstrometry.[ch]
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-03-06 22:53:47 $
+*
+* XXX: Add tests were the coordinate does not transform to any legitimate cell
+* or chip, or FPA, or whatever.
+*
+* XXX: For each function, add tests for bad input parameters, as well as failed transforms.
+*
+* XXX: Must test pmFPASelectChip() and pmFPAExcludeChip().
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include "config.h"
+#include <math.h>
+#include <string.h>
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "pmAstrometry.h"
+static psS32 test3( void );
+static psS32 test4( void );
+static psS32 test5( void );
+
+testDescription tests[] = {
+                              {test3, 666, "pmAstrometry focal plane transformations", 0, false},
+                              {test4, 667, "pmCheckParents()", 0, false},
+                              {test5, 668, "pmFPASelectChip() and pmFPAExcludeChip()", 0, false},
+                              {NULL}
+                          };
+
+psS32 currentId = 0;
+psS32 memLeaks = 0;
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetLevel( PS_LOG_INFO );
+    psLogSetFormat("HLNM");
+
+    return ( ! runTestSuite( stderr, "psAstrometry", tests, argc, argv ) );
+}
+
+#define PS_PERCENT_COMPARE(X, Y, PERCENT_FRACTION) (fabs((Y)-(X))/fabs(X) < (PERCENT_FRACTION))
+#define VERBOSE 0
+#define NUM_READOUTS 1
+#define READOUT_NUM_ROWS 10
+#define READOUT_NUM_COLS 10
+
+#define NUM_CELLS 4
+#define CELL_GAP 2
+#define CELL_WIDTH READOUT_NUM_COLS
+#define CELL_HEIGHT READOUT_NUM_ROWS
+#define CELL_MIN_X 0
+#define CELL_MAX_X CELL_HEIGHT
+#define CELL_MIN_Y 0
+#define CELL_MAX_Y CELL_WIDTH
+
+#define NUM_CHIPS 2
+#define CHIP_GAP 2
+#define CHIP_MIN_X 0
+#define CHIP_MAX_X CELL_HEIGHT
+#define CHIP_MIN_Y 0
+#define CHIP_MAX_Y ((NUM_CELLS * CELL_WIDTH) + ((NUM_CELLS) * CELL_GAP))
+#define CHIP_WIDTH CHIP_MAX_Y
+#define CHIP_HEIGHT CHIP_MAX_X
+
+#define NUM_FPAS 1
+#define FPA_MIN_X 0
+#define FPA_MAX_X CHIP_HEIGHT
+#define FPA_MIN_Y 0
+#define FPA_MAX_Y (((NUM_CHIPS) * CHIP_WIDTH) + (((NUM_CHIPS)-1) * CHIP_GAP))
+
+#define PROJECTION_SCALE_X 1.0
+#define PROJECTION_SCALE_Y 1.0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+genSystem(): This routine will create a system of FPAs/Chips/Cells/Readouts.  For
+simplicity, an FPA is defined as a linear array of chips, and a chip is
+defined as a linear array of cells, both in the y (cols) direction.  The
+transforms between the various layers take into account the cell/chip and
+the boundaries between each.
+ *****************************************************************************/
+pmFPA *genSystem()
+{
+    //
+    // Create top pmFPA structure.
+    //
+    const psMetadata *camera = psMetadataAlloc();
+    pmFPA* myFPA = pmFPAAlloc(camera);
+    if (myFPA == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc() returned a NULL.\n");
+        return NULL;
+    }
+    myFPA->fromTangentPlane = PS_CREATE_4D_IDENTITY_PLANE_DISTORT();
+    myFPA->toTangentPlane = PS_CREATE_4D_IDENTITY_PLANE_DISTORT();
+    myFPA->projection = psProjectionAlloc(0.0,0.0,PROJECTION_SCALE_X,PROJECTION_SCALE_X,PS_PROJ_TAN);
+
+    myFPA->chips = psArrayRealloc(myFPA->chips, NUM_CHIPS);
+    for (psS32 chipID=0 ; chipID<NUM_CHIPS ; chipID++) {
+        pmChip *myChip = pmChipAlloc(myFPA, "ChipName");
+        if (myChip == NULL) {
+            psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmChipAlloc() returned a NULL.\n");
+            return NULL;
+        }
+        myFPA->chips->data[chipID] = (psPtr *) myChip;
+        myChip->row0 = 0;
+        myChip->col0 = chipID * (CHIP_WIDTH + CHIP_GAP);
+
+        // We create the transforms between the chip and FPA.  The
+        // transform is a simple identity transform.
+        myChip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+        myChip->toFPA->y->coeff[0][0] = (psF64) myChip->col0;
+        myChip->toFPA->y->coeff[1][1] = 0.0;
+        myChip->toFPA->x->coeff[0][0] = (psF64) myChip->row0;
+        myChip->toFPA->x->coeff[1][1] = 0.0;
+
+        myChip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+        myChip->fromFPA->y->coeff[0][0] = (psF64) (- myChip->col0);
+        myChip->fromFPA->y->coeff[1][1] = 0.0;
+        myChip->fromFPA->x->coeff[0][0] = (psF64) (- myChip->row0);
+        myChip->fromFPA->x->coeff[1][1] = 0.0;
+
+        myChip->cells = psArrayRealloc(myChip->cells, NUM_CELLS);
+        for (psS32 cellID=0 ; cellID<NUM_CELLS ; cellID++) {
+            pmCell *myCell = pmCellAlloc(myChip, NULL, "CellName");
+            if (myCell == NULL) {
+                psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmCellAlloc() returned a NULL.\n");
+                return NULL;
+            }
+            myChip->cells->data[cellID] = (psPtr *) myCell;
+            myCell->row0 = 0;
+            myCell->col0 = cellID * (CELL_WIDTH + CELL_GAP);
+            myCell->toChip = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+            myCell->toChip->y->coeff[0][0] = (psF64) myCell->col0;
+            myCell->toChip->y->coeff[1][1] = 0.0;
+            myCell->toChip->x->coeff[0][0] = (psF64) myCell->row0;
+            myCell->toChip->x->coeff[1][1] = 0.0;
+
+            myCell->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+            myCell->toFPA->y->coeff[0][0] = (psF64) (myCell->col0 + myChip->col0);
+            myCell->toFPA->y->coeff[1][1] = 0.0;
+            myCell->toFPA->x->coeff[0][0] = (psF64) (myCell->row0 + myChip->row0);
+            myCell->toFPA->x->coeff[1][1] = 0.0;
+
+            myCell->toSky = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+            myCell->toSky->y->coeff[0][0] = myCell->toFPA->y->coeff[0][0];
+            myCell->toSky->y->coeff[1][1] = 0.0;
+            myCell->toSky->x->coeff[0][0] = myCell->toFPA->x->coeff[0][0];
+            myCell->toSky->x->coeff[1][1] = 0.0;
+
+            myCell->readouts = psArrayRealloc(myCell->readouts, NUM_READOUTS);
+            for (psS32 readoutID=0 ; readoutID<NUM_READOUTS ; readoutID++) {
+                pmReadout *myReadout = pmReadoutAlloc(myCell);
+                if (myReadout == NULL) {
+                    psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmReadoutAlloc() returned a NULL.\n");
+                    return NULL;
+                }
+                myCell->readouts->data[readoutID] = (psPtr *) myReadout;
+                myReadout->image = psImageAlloc(READOUT_NUM_COLS, READOUT_NUM_ROWS, PS_TYPE_F32);
+                for (psS32 row=0;row<READOUT_NUM_ROWS;row++) {
+                    for(psS32 col=0;col<READOUT_NUM_COLS;col++) {
+                        myReadout->image->data.F32[row][col] = (psF32) ((chipID * (CHIP_WIDTH + CHIP_GAP)) +
+                                                               (cellID * (CELL_WIDTH + CELL_GAP)));
+                    }
+                }
+                myReadout->row0 = myCell->row0;
+                myReadout->col0 = myCell->col0;
+                myReadout->rowBins = 0;
+                myReadout->colBins = 0;
+            }
+            if (VERBOSE) {
+                printf("\n\n\n\nFor chip %d cell %d the cell->toFPA transform is:\n", chipID, cellID);
+                PS_PRINT_PLANE_TRANSFORM(myCell->toFPA);
+                printf("\n\n\n\nFor chip %d cell %d the cell->toChip transform is:\n", chipID, cellID);
+                PS_PRINT_PLANE_TRANSFORM(myCell->toChip);
+                printf("\n\n\n\nFor chip %d cell %d the cell->toSky transform is:\n", chipID, cellID);
+                PS_PRINT_PLANE_TRANSFORM(myCell->toSky);
+            }
+        }
+        if (VERBOSE) {
+            printf("\n\n\n\nFor chip %d the chip->toFPA transform is:\n", chipID);
+            PS_PRINT_PLANE_TRANSFORM(myChip->toFPA);
+            printf("\n\n\n\nFor chip %d the chip->fromFPA transform is:\n", chipID);
+            PS_PRINT_PLANE_TRANSFORM(myChip->fromFPA);
+        }
+    }
+
+    return(myFPA);
+}
+
+
+/******************************************************************************
+This routine tests many Astrometry functions.  It loops through all valid
+pixels of all cells of all chips and computes the corresponding (x,y)
+coordinates in the FPA plane.  It then calls the pmChipInFPA() then
+pmCellInFPA() with the FPA coordinate and determines the chip/cell that that
+coordinate corresponds to.  Following that it does a variety of tests on the
+various functions that tharnsform coordinates within the pmFPA hierarchy.
+ 
+List of tested functions:
+    pmCellInFPA()  yes
+    pmChipInFPA()  yes
+    pmCellInChip()  yes
+ 
+    pmCoordCellToFPA()  yes
+    pmCoordChipToFPA()  yes
+    pmCoordFPAToChip()  yes
+    pmCoordCellToChip()  yes
+    pmCoordChipToCell()  yes
+ 
+    pmCoordFPAToTP()  yes
+    pmCoordTPToFPA()  yes
+ 
+    pmCoordTPToSky()  yes
+    pmCoordSkyToTP()  yes
+    pmCoordSkyToCell()  yes
+    pmCoordCellToSky()  yes
+    pmCoordCellToSkyQuick() yes
+    pmCoordSkyToCellQuick() yes
+ *****************************************************************************/
+psS32 test3( void )
+{
+    psS32 x;
+    psS32 y;
+    psPlane fpaCoord;
+    pmFPA *myFPA = genSystem();
+    pmCell *myCell = NULL;
+    psPlane chipCoord;
+    psPlane cellCoord;
+    psPlane testCoord;
+    psSphere *skyCoord = psSphereAlloc();
+    // XXX: This code causes a seg fault.
+    //    psSphere skyTmp;
+    //    psMemCheckType(PS_DATA_SPHERE, &skyTmp);
+    psPlane tpCoord;
+    psS32 testStatus = 0;
+
+    //
+    // I'm not convinced that the p_psProject() and p_psDeproject() functions work
+    // correctly.  If we project a set of coordinates over a wide range of (R, D)
+    // values, then deproject them, the original (R, D) values are only produced
+    // when D is larger than 0.  This code demonstrates that.  I also created tests
+    // that currently fail in tst_psCoord01.c.  I have a workaround in the function
+    // XXXDeproject() in pmAstrometry.c.
+    //
+    if (0) {
+        // This loop goes from (R, D) -> (X, Y) -> (R, D)
+        psPlane planeCoord01;
+        psSphere skyCoord01;
+        psSphere skyCoord02;
+        #define DEG_INC 15.0
+
+        for (psF32 R = -90.0 ; R <= 90.0 ; R+= DEG_INC) {
+            for (psF32 D = -90.0 ; D <= 90.0 ; D+= DEG_INC) {
+                if ((fabs(R) != 90.0) && (fabs(D) != 90.0)) {
+                    skyCoord01.r = DEG_TO_RAD(R);
+                    skyCoord01.d = DEG_TO_RAD(D);
+                    p_psProject(&planeCoord01, &skyCoord01, myFPA->projection);
+                    p_psDeproject(&skyCoord02, &planeCoord01, myFPA->projection);
+                    printf("(%.2fr %.2fd) (%.2fr %.2fd) -> (%.2f %.2f) -> (%.2fr %.2fd)", R, D,
+                           skyCoord01.r, skyCoord01.d,
+                           planeCoord01.x, planeCoord01.y,
+                           skyCoord02.r, skyCoord02.d);
+                    if ((fabs(skyCoord01.r - skyCoord02.r) < FLT_EPSILON) &&
+                            (fabs(skyCoord01.d - skyCoord02.d) < FLT_EPSILON)) {
+                        printf(": CORRECT\n");
+                    } else {
+                        printf(": WRONG\n");
+                    }
+                }
+            }
+        }
+        psFree(myFPA);
+        return(0);
+    }
+    if (0) {
+        // This loop goes from (X, Y) -> (R, D) -> (X, Y)
+        #define SPACE_INC 4.0
+        for(testCoord.x=-CELL_HEIGHT;testCoord.x<=CELL_HEIGHT;testCoord.x+=SPACE_INC)
+        {
+            for (testCoord.y=-CELL_WIDTH;testCoord.y<=CELL_WIDTH;testCoord.y+=SPACE_INC) {
+                psPlane planeCoord01;
+                psPlane planeCoord02;
+                psSphere skyCoord01;
+                psSphere skyCoord02;
+                p_psDeproject(&skyCoord01, &testCoord, myFPA->projection);
+                p_psProject(&planeCoord01, &skyCoord01, myFPA->projection);
+                p_psDeproject(&skyCoord02, &planeCoord01, myFPA->projection);
+                p_psProject(&planeCoord02, &skyCoord02, myFPA->projection);
+                printf("Plane: (%.2f %.2f) -> (%.2fr %.2fd) -> (%.2f %.2f)\n",
+                       testCoord.x, testCoord.y,
+                       skyCoord01.r, skyCoord01.d,
+                       planeCoord01.x, planeCoord01.y);
+                /*
+                                printf("Plane: (%.2f %.2f) -> (%.2f %.2f) -> (%.2f %.2f)\n",
+                                        testCoord.x, testCoord.y, planeCoord01.x, planeCoord01.y,
+                                        planeCoord02.x, planeCoord02.y);
+                                printf("Sphere: (%.2f %.2f) -> (%.2f %.2f)\n",
+                                        skyCoord01.r, skyCoord01.d, skyCoord02.r, skyCoord02.d);
+                                printf("Plane: (%.2f %.2f) -> (%.2fd %.2fr) -> (%.2f %.2f) -> (%.2fd %.2fr) -> (%.2f %.2f)\n",
+                                        testCoord.x, testCoord.y,
+                                        skyCoord01.r, skyCoord01.d,
+                                        planeCoord01.x, planeCoord01.y,
+                                        skyCoord02.r, skyCoord02.d,
+                                        planeCoord02.x, planeCoord02.y);
+                */
+            }
+        }
+        psFree(myFPA);
+        return(0);
+    }
+    //
+    // We iterate through all cells on all chips on the fpa.  We determine
+    // the expected fpaCcoord.
+    //
+
+    for (psS32 chip=0;chip<NUM_CHIPS;chip++) {
+        for (psS32 cell=0;cell<NUM_CELLS;cell++) {
+            for(x=0;x<CELL_HEIGHT;x++) {
+                for (y=0;y<CELL_WIDTH;y++) {
+                    fpaCoord.x = (psF64) x;
+                    fpaCoord.y = (psF64) (y + (chip * (CHIP_WIDTH + CHIP_GAP)) +
+                                          (cell * (CELL_WIDTH + CELL_GAP)));
+                    if (VERBOSE) {
+                        printf("------------------ (%.2f, %.2f) ------------------\n", fpaCoord.x, fpaCoord.y);
+                        printf("(chip, cell, x, y) is (%d, %d, %d, %d)\n", chip, cell, x, y);
+                    }
+                    pmChip* tmpChip = pmChipInFPA(&fpaCoord, myFPA);
+                    myCell = pmCellInFPA(&fpaCoord, myFPA);
+
+                    if ((myCell == NULL) || (tmpChip == NULL)) {
+                        if (tmpChip == NULL) {
+                            printf("TEST ERROR: pmChipInFPA() returned NULL\n");
+                            testStatus = 1;
+                        } else if (myCell == NULL) {
+                            printf("TEST ERROR: pmCellInFPA(): returned NULL\n");
+                            testStatus = 1;
+                        }
+                    } else {
+                        pmCoordFPAToChip(&chipCoord, &fpaCoord, tmpChip);
+                        pmCoordChipToCell(&cellCoord, &chipCoord, myCell);
+
+                        if (x != (psS32) cellCoord.x) {
+                            printf("TEST ERROR: pmCoordFPAToChip()->pmCoordChipToCell(): x coord was %d (%f), should be %d\n", (psS32) cellCoord.x, cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (y != (psS32) cellCoord.y) {
+                            printf("TEST ERROR: pmCoordFPAToChip()->pmCoordChipToCell(): y coord was %d (%f), should be %d\n", (psS32) cellCoord.y, cellCoord.y, y);
+                            testStatus = 1;
+                        }
+
+                        pmCoordCellToChip(&testCoord, &cellCoord, myCell);
+                        if (fabs(testCoord.x - chipCoord.x) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordCellToChip() x coord was %.2f, should be %d\n", cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (fabs(testCoord.y - chipCoord.y) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordCellToChip() y coord was %.2f, should be %d\n", cellCoord.y, y);
+                            testStatus = 1;
+                        }
+
+                        pmCell *myCell2 = pmCellInChip(&chipCoord, tmpChip);
+                        if (myCell2 != myCell) {
+                            printf("TEST ERROR: pmCellInChip() != pmCellInChip(pmChipInFPA()) (%p %p)\n", myCell2, myCell);
+                            testStatus = 1;
+                        }
+
+                        pmCoordChipToFPA(&testCoord, &chipCoord, tmpChip);
+                        if (fabs(testCoord.x - x) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordChipToFPA() x coord was %.2f, should be %d\n", cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (fabs(testCoord.y - fpaCoord.y) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordChipToFPA() y coord was %.2f, should be %.2f\n", cellCoord.y, fpaCoord.y);
+                            testStatus = 1;
+                        }
+
+                        pmCoordFPAToTP(&testCoord, &fpaCoord, 0.0, 0.0, myFPA);
+                        if (fabs(testCoord.x - fpaCoord.x) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordFPAToTP() x coord was %.2f, should be %d\n", cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (fabs(testCoord.y - fpaCoord.y) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordFPAToTP() y coord was %.2f, should be %d\n", cellCoord.y, y);
+                            testStatus = 1;
+                        }
+
+                        //
+                        // Test pmCoordTPToSky() -> pmCoordSkyToTP()
+                        //
+                        if (1) {
+                            psSphere *rc = pmCoordTPToSky(skyCoord, &testCoord, myFPA->projection);
+                            if (rc == NULL) {
+                                printf("pmCoordTPToSky() failed.\n");
+                            } else {
+                                psPlane *rc = pmCoordSkyToTP(&tpCoord, skyCoord, myFPA->projection);
+                                if (rc == NULL) {
+                                    printf("pmCoordSkyToTP() failed.\n");
+                                } else {
+                                    if (fabs(testCoord.x - tpCoord.x) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordTPToSky()/pmCoordSkyToTP() x coord was %.2f, should be %.2f\n", tpCoord.x, testCoord.x);
+                                        testStatus = 1;
+                                    }
+                                    if (fabs(testCoord.y - tpCoord.y) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordTPToSky()/pmCoordSkyToTP() y coord was %.2f, should be %.2f\n", tpCoord.y, testCoord.y);
+                                        testStatus = 1;
+                                    }
+                                    if (VERBOSE) {
+                                        printf("(%.2f %.2f) -> (%.2f %.2f) -> (%.2f %.2f)\n", testCoord.x, testCoord.y, skyCoord->r, skyCoord->d, tpCoord.x, tpCoord.y);
+                                    }
+                                }
+                            }
+                        }
+
+                        //
+                        // Test pmCoordCellToSky() -> pmCoordSkyToCell()
+                        //
+                        if (1) {
+                            psPlane tmpCellCoord;
+                            psSphere *rc = pmCoordCellToSky(skyCoord, &cellCoord, 0.0, 0.0, myCell);
+                            if (rc == NULL) {
+                                printf("pmCoordCellToSky() failed.\n");
+                            } else {
+                                psPlane *rc = pmCoordSkyToCell(&tmpCellCoord, skyCoord, 0.0, 0.0, myCell);
+                                if (rc == NULL) {
+                                    printf("pmCoordSkyToCell() failed.\n");
+                                } else {
+                                    if (fabs(cellCoord.x - tmpCellCoord.x) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordCellToSky()/pmCoordSkyToCell() x coord was %.2f, should be %.2f\n", tmpCellCoord.x, cellCoord.x);
+                                        testStatus = 1;
+                                    }
+                                    if (fabs(cellCoord.y - tmpCellCoord.y) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordCellToSky()/pmCoordSkyToCell() y coord was %.2f, should be %.2f\n", tmpCellCoord.y, cellCoord.y);
+                                        testStatus = 1;
+                                    }
+                                    if (VERBOSE) {
+                                        printf("(%.2f %.2f) -> (%.2f %.2f) -> (%.2f %.2f)\n", testCoord.x, testCoord.y, skyCoord->r, skyCoord->d, tmpCellCoord.x, tmpCellCoord.y);
+                                    }
+                                }
+                            }
+                        }
+
+                        //
+                        // Test pmCoordCellToSkyQuick() -> pmCoordSkyToCellQuick()
+                        // I'm not sure how to test this in a system with chip and cell gaps.
+                        // There's no way to create an accurate polynomial transform from
+                        // a cell to the sky where there are cell, or chip gaps.
+                        //
+                        if ((NUM_CHIPS == 1) && (NUM_CELLS == 1)) {
+                            psPlane tmpCellCoord;
+                            psSphere *rc = pmCoordCellToSkyQuick(skyCoord, &cellCoord, myCell);
+                            if (rc == NULL) {
+                                printf("pmCoordCellToSkyQuick() failed.\n");
+                            } else {
+                                psPlane *rc = pmCoordSkyToCellQuick(&tmpCellCoord, skyCoord, myCell);
+                                if (rc == NULL) {
+                                    printf("pmCoordSkyToCellQuick() failed.\n");
+                                } else {
+                                    if (fabs(cellCoord.x - tmpCellCoord.x) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordCellToSky()/pmCoordSkyToCell() x coord was %.2f, should be %.2f\n", tmpCellCoord.x, cellCoord.x);
+                                        testStatus = 1;
+                                    }
+                                    if (fabs(cellCoord.y - tmpCellCoord.y) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordCellToSky()/pmCoordSkyToCell() y coord was %.2f, should be %.2f\n", tmpCellCoord.y, cellCoord.y);
+                                        testStatus = 1;
+                                    }
+                                    if (VERBOSE) {
+                                        printf("(%.2f %.2f) -> (%.2f %.2f) -> (%.2f %.2f)\n", testCoord.x, testCoord.y, skyCoord->r, skyCoord->d, tmpCellCoord.x, tmpCellCoord.y);
+                                    }
+                                }
+                            }
+                        }
+
+                        pmCoordCellToFPA(&testCoord, &cellCoord, myCell);
+                        if (fabs(testCoord.x - fpaCoord.x) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordCellToFPA() x coord was %.2f, should be %d\n", cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (fabs(testCoord.y - fpaCoord.y) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordCellToFPA() y coord was %.2f, should be %d\n", cellCoord.y, y);
+                            testStatus = 1;
+                        }
+
+                        pmCoordTPToFPA(&testCoord, &fpaCoord, 0.0, 0.0, myFPA);
+                        if (fabs(testCoord.x - fpaCoord.x) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordTPToFPA() x coord was %.2f, should be %d\n", cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (fabs(testCoord.y - fpaCoord.y) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordTPToFPA() y coord was %.2f, should be %d\n", cellCoord.y, y);
+                            testStatus = 1;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    psFree(myFPA);
+    psFree(skyCoord);
+
+    return(testStatus);
+}
+
+/******************************************************************************
+test4(): This routine wil test the pmFPACheckParents() function.  We generate
+an pmFPA hierarchy, then set the parents of each readout/cell/chip to NULL,
+then call pmFPACheckParents() to restore them, then we ensure they were
+restored.
+ *****************************************************************************/
+psS32 test4( void )
+{
+    psS32 testStatus = 0;
+
+    //
+    // Generate a pmFPA hierarchy.
+    //
+    pmFPA *tmpFPA = genSystem();
+
+    //
+    // We set the parents of each readout/cell/chip to NULL.
+    //
+    for (psS32 chipID = 0; chipID < tmpFPA->chips->n ; chipID++) {
+        pmChip *tmpChip = (pmChip *) tmpFPA->chips->data[chipID];
+        tmpChip->parent = NULL;
+
+        for (psS32 cellID = 0; cellID < tmpChip->cells->n ; cellID++) {
+            pmCell *tmpCell = (pmCell *) tmpChip->cells->data[cellID];
+            tmpCell->parent = NULL;
+
+            for (psS32 readoutID = 0; readoutID < tmpCell->readouts->n ; readoutID++) {
+                pmReadout *tmpReadout = (pmReadout *) tmpCell->readouts->data[readoutID];
+                tmpReadout->parent = NULL;
+            }
+        }
+    }
+
+    //
+    // Ensure that pmFPACheckParents() returned FALSE.
+    //
+    psBool rc = pmFPACheckParents(tmpFPA);
+    if (rc != false) {
+        printf("TEST ERROR: pmCheckParents() returned TRUE.\n");
+        testStatus = 1;
+    }
+
+    //
+    // Ensure that the parent members are right.
+    //
+    for (psS32 chipID = 0; chipID < tmpFPA->chips->n ; chipID++) {
+        pmChip *tmpChip = (pmChip *) tmpFPA->chips->data[chipID];
+        if (tmpChip->parent != tmpFPA) {
+            printf("TEST ERROR: pmCheckParents() did not restore Chip->parent.\n");
+            testStatus = 2;
+        }
+
+        for (psS32 cellID = 0; cellID < tmpChip->cells->n ; cellID++) {
+            pmCell *tmpCell = (pmCell *) tmpChip->cells->data[cellID];
+            if (tmpCell->parent != tmpChip) {
+                printf("TEST ERROR: pmCheckParents() did not restore Cell->parent.\n");
+                testStatus = 3;
+            }
+
+            for (psS32 readoutID = 0; readoutID < tmpCell->readouts->n ; readoutID++) {
+                pmReadout *tmpReadout = (pmReadout *) tmpCell->readouts->data[readoutID];
+                if (tmpReadout->parent != tmpCell) {
+                    printf("TEST ERROR: pmCheckParents() did not restore Readout->parent.\n");
+                    testStatus = 4;
+                }
+            }
+        }
+    }
+
+    psFree(tmpFPA);
+    return(testStatus);
+}
+
+/******************************************************************************
+test5(): This routine wil test the pmFPASelectChip() and pmFPAExcludeChip()
+functions.  We generate an pmFPA hierarchy, then set the ->valid members with
+those routines, then verify.
+ *****************************************************************************/
+psS32 test5( void )
+{
+    psS32 testStatus = 0;
+    pmChip *tmpChip = NULL;
+
+    //
+    // Generate a pmFPA hierarchy.
+    //
+    pmFPA *tmpFPA = genSystem();
+
+    //
+    // We test the ->valid member for each chip.
+    //
+    for (psS32 i = 0 ; i < tmpFPA->chips->n ; i++) {
+        tmpChip = (pmChip *) tmpFPA->chips->data[i];
+        if ((tmpChip == NULL) || (tmpChip->valid != false)) {
+            printf("TEST ERROR: Could not properly generate an FPA hierarchy.\n");
+            testStatus = 1;
+        }
+    }
+
+    //
+    // Exclude chip number 0, include all others, then test return value
+    //
+    psS32 numChips = pmFPAExcludeChip(tmpFPA, 0);
+    if (numChips != (NUM_CHIPS-1)) {
+        printf("TEST ERROR: pmFPAExcludeChip() did not return the correct number of chips.\n");
+        testStatus = 2;
+    }
+
+    //
+    // We test the ->valid member for each chip.
+    //
+    tmpChip = (pmChip *) tmpFPA->chips->data[0];
+    if (tmpChip->valid != false) {
+        printf("TEST ERROR: pmFPAExcludeChip() did not set the proper chip->valid to FALSE.\n");
+        testStatus = 3;
+    }
+    for (psS32 i = 1 ; i < tmpFPA->chips->n ; i++) {
+        pmChip *tmpChip = (pmChip *) tmpFPA->chips->data[i];
+        if (tmpChip->valid != true) {
+            printf("TEST ERROR: pmFPAExcludeChip() did not set the proper chip->valids to FALSE.\n");
+            testStatus = 4;
+        }
+    }
+
+
+    //
+    // Include chip number 0, exclude all others, then test return value
+    //
+    psBool tmpBool = pmFPASelectChip(tmpFPA, 0);
+    if (tmpBool != true) {
+        printf("TEST ERROR: pmFPASelectChip() returned FALSE.\n");
+        testStatus = 5;
+    }
+
+    //
+    // We test the ->valid member for each chip.
+    //
+    tmpChip = (pmChip *) tmpFPA->chips->data[0];
+    if (tmpChip->valid != true) {
+        printf("TEST ERROR: pmFPASelectChip() did not set the proper chip->valid to FALSE.\n");
+        testStatus = 6;
+    }
+    for (psS32 i = 1 ; i < tmpFPA->chips->n ; i++) {
+        pmChip *tmpChip = (pmChip *) tmpFPA->chips->data[i];
+        if (tmpChip->valid != false) {
+            printf("TEST ERROR: pmFPASelectChip() did not set the proper chip->valids to FALSE.\n");
+            testStatus = 7;
+        }
+    }
+
+    psFree(tmpFPA);
+    return(testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmAstrometry
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/Makefile.am	(revision 22322)
@@ -0,0 +1,41 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSMODULES_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpsmodules.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSMODULES_LIBS)
+
+TEST_PROGS = \
+	tap_pmFPA \
+	tap_pmFPAReadWrite \
+	tap_pmHDU \
+	tap_pmHDUUtils \
+	tap_pmFPALevel \
+	tap_pmFPAFlags \
+	tap_pmFPAExtent \
+	tap_pmFPAUtils \
+	tap_pmFPAConstruct \
+	tap_pmFPAView \
+	tap_pmFPAHeader \
+	tap_pmFPAMaskW \
+	tap_pmFPACellSquish \
+	tap_pmReadoutStack \
+	tap_pmReadoutFake \
+	tap_pmFPACopy
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
+	$(top_srcdir)/test/test.pl
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/SampleIPPConfig
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/SampleIPPConfig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/SampleIPPConfig	(revision 22322)
@@ -0,0 +1,45 @@
+### Example .ipprc file
+    PATH            STR     .
+    DATAPATH	str	datapath
+
+### Database configuration
+    DBSERVER	STR	localhost
+    DBUSER		STR	test
+    DBPASSWORD	STR	""
+    DBNAME          STR     test
+    DBPORT		S32	0
+
+### Setups for each camera system
+    CAMERAS		METADATA
+	CAMERA0		STR	camera0/camera.config
+	CAMERA1		STR	camera1/camera.config
+    END
+
+### Setups for psLib
+    TIME		STR	time.config
+    LOGLEVEL	S32	3
+    LOGFORMAT	STR	HLNM
+    LOGDEST	STR	STDOUT
+
+### Default trace logging initializations
+    TRACE		METADATA
+	dummyTraceFacility01	S32	1
+	dummyTraceFacility02	S32	2
+    END
+
+### Predefined recipe files
+    RECIPES         METADATA                # Site-level recipes
+        R00		STR	recipes/R00.config
+        R01		STR	recipes/R01.config
+        R02		STR	recipes/R02.config
+        R03		STR	recipes/R03.config
+    END
+
+### Misc arbitraty metadata
+    ARBITRARY_STRING_S32	S32	20
+    ARBITRARY_STRING_F32	F32	21.0
+    ARBITRARY_STRING_F64	F64	22.0
+    ARBITRARY_STRING_STR	STR	19.0
+    ARBITRARY_STRING_BOOL_T	BOOL	true
+    ARBITRARY_STRING_BOOL_F	BOOL	false
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/camera.config	(revision 22322)
@@ -0,0 +1,24 @@
+FORMATS         METADATA
+        C0_FM0     STR     camera0/format0.config
+        C0_FM1     STR     camera0/format1.config
+END
+
+RECIPES         METADATA
+        C0_RECIPE0          STR     camera0/recipe0.config
+        C0_RECIPE1          STR     camera0/recipe1.config
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION     METADATA
+        CELL.TRIMSEC            STR     TRIMSEC
+        CELL.BIASSEC            STR     BIASSEC
+        CELL.TRIMSEC0            STR     TRIMSEC
+        CELL.BIASSEC1            STR     BIASSEC
+END
+
+
+FPA     METADATA
+        ccd00   STR     LeftAmp RightAmp
+        ccd01   STR     LeftAmp RightAmp
+END
+ID      STR     CAMERA0
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/format0.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/format0.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/format0.config	(revision 22322)
@@ -0,0 +1,31 @@
+RULE    METADATA
+        F0_KEY0        STR     string20
+        F0_KEY1        STR     string21
+        F0_KEY2        S32     20
+END
+
+FILE    METADATA
+        PHU             STR     FPA
+        EXTENSIONS      STR     CELL
+        FPA.NAME        STR     EXPNUM
+END
+
+DEFAULTS        METADATA
+        F0_DEF0           STR     DefString20
+        F0_DEF1           STR     DefString21
+        F0_DEF2           F32     21.0
+        F0_DEF3           S32     22
+END
+
+CELLS   METADATA
+        left    METADATA        # Left amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+        right   METADATA        # Right amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+END
+
+ID	STR	camera0/format0.config
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/format1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/format1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/format1.config	(revision 22322)
@@ -0,0 +1,29 @@
+RULE    METADATA
+        F1_KEY0        STR     string30
+        F1_KEY1        STR     string31
+        F1_KEY2        S32     30
+END
+
+FILE    METADATA
+        PHU             STR     FPA
+        EXTENSIONS      STR     CELL
+        FPA.NAME        STR     EXPNUM
+END
+
+DEFAULTS        METADATA
+        F1_DEF0           STR     DefString30
+        F1_DEF1           STR     DefString31
+        F1_DEF2           F32     31.0
+        F1_DEF3           S32     32
+END
+
+CELLS   METADATA
+        left    METADATA        # Left amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+        right   METADATA        # Right amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/recipe0.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/recipe0.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/recipe0.config	(revision 22322)
@@ -0,0 +1,4 @@
+R0_KEY0		STR	RecipeString0
+R0_KEY1		STR	RecipeString1
+R0_KEY2		S32	2
+R0_KEY3		F32	3
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/recipe1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/recipe1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera0/recipe1.config	(revision 22322)
@@ -0,0 +1,4 @@
+R1_KEY0		STR	RecipeString10
+R1_KEY1		STR	RecipeString11
+R1_KEY2		S32	12
+R1_KEY3		F32	13
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/camera.config	(revision 22322)
@@ -0,0 +1,20 @@
+FORMATS         METADATA
+        C1_FM0     STR     camera1/format0.config
+        C1_FM1     STR     camera1/format1.config
+END
+
+RECIPES         METADATA
+        C1_RECIPE0          STR     camera1/recipe0.config
+        C1_RECIPE1          STR     camera1/recipe1.config
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION     METADATA
+        CELL.TRIMSEC            STR     TRIMSEC
+        CELL.BIASSEC            STR     BIASSEC
+END
+
+FPA     METADATA
+        ccd00   STR     LeftAmp RightAmp
+        ccd01   STR     LeftAmp RightAmp
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/format0.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/format0.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/format0.config	(revision 22322)
@@ -0,0 +1,29 @@
+RULE    METADATA
+        F0_KEY0        STR     string40
+        F0_KEY1        STR     string41
+        F0_KEY2        S32     420
+END
+
+FILE    METADATA
+        PHU             STR     FPA
+        EXTENSIONS      STR     CELL
+        FPA.NAME        STR     EXPNUM
+END
+
+DEFAULTS        METADATA
+        F0_DEF0           STR     DefString40
+        F0_DEF1           STR     DefString41
+        F0_DEF2           F32     41.0
+        F0_DEF3           S32     44
+END
+
+CELLS   METADATA
+        left    METADATA        # Left amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+        right   METADATA        # Right amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/format1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/format1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/format1.config	(revision 22322)
@@ -0,0 +1,29 @@
+RULE    METADATA
+        F1_KEY0        STR     string80
+        F1_KEY1        STR     string81
+        F1_KEY2        S32     80
+END
+
+FILE    METADATA
+        PHU             STR     FPA
+        EXTENSIONS      STR     CELL
+        FPA.NAME        STR     EXPNUM
+END
+
+DEFAULTS        METADATA
+        F1_DEF0           STR     DefString80
+        F1_DEF1           STR     DefString81
+        F1_DEF2           F32     81.0
+        F1_DEF3           S32     82
+END
+
+CELLS   METADATA
+        left    METADATA        # Left amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+        right   METADATA        # Right amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/recipe0.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/recipe0.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/recipe0.config	(revision 22322)
@@ -0,0 +1,4 @@
+R0_KEY0		STR	RecipeString120
+R0_KEY1		STR	RecipeString121
+R0_KEY2		S32	122
+R0_KEY3		F32	123
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/recipe1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/recipe1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/camera1/recipe1.config	(revision 22322)
@@ -0,0 +1,4 @@
+R1_KEY0		STR	RecipeString230
+R1_KEY1		STR	RecipeString2311
+R1_KEY2		S32	232
+R1_KEY3		F32	233
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/path2/SampleIPPConfig2
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/path2/SampleIPPConfig2	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/path2/SampleIPPConfig2	(revision 22322)
@@ -0,0 +1,32 @@
+### Example .ipprc file
+
+### Database configuration
+DBSERVER	STR	ippdb.ifa.hawaii.edu	# Database host name (for psDBInit)
+DBUSER		STR	ipp			# Database user name (for psDBInit)
+DBPASSWORD	STR	password		# Database password (for psDBInit)
+
+### Setups for each camera system
+CAMERAS		METADATA
+	MEGACAM_RAW	STR	megacam_raw.config
+	MEGACAM_SPLICE	STR	megacam_splice.config
+	GPC1_RAW	STR	gpc1_raw.config
+	LRIS_BLUE	STR	lris_blue.config
+	LRIS_RED	STR	lris_red.config
+END
+
+### psLib setup
+#TIME		STR	/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/etc/pslib/psTime.config	# Time configuration file
+LOGLEVEL	S32	3			# Logging level; 3=INFO
+LOGFORMAT	STR	HLNM			# Log format
+LOGDEST	STR	STDOUT				# Log destination
+TRACE		METADATA			# Trace levels
+	dummyTraceFunc01	S32	1
+	dummyTraceFunc02	S32	2
+END
+ARBITRARY_STRING_S32	S32	20
+ARBITRARY_STRING_F32	F32	21.0
+ARBITRARY_STRING_F64	F64	22.0
+ARBITRARY_STRING_STR	STR	19.0
+ARBITRARY_STRING_BOOL_T	BOOL	true
+ARBITRARY_STRING_BOOL_F	BOOL	false
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/sampleFitsFile.fits
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/sampleFitsFile.fits	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/data/sampleFitsFile.fits	(revision 22322)
@@ -0,0 +1,1 @@
+SIMPLE  =                    T / file does conform to FITS standard             BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   16 / length of data axis 1                          NAXIS2  =                   16 / length of data axis 2                          EXTEND  =                    T / FITS dataset may contain extensions            COMMENT   FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H MYINT   =                    0 / psS32 Item                                     MYFLT   =                   1. / psF32 Item                                     MYDBL   =                   1. / psF64 Item                                     MYBOOL  =                    T / psBool Item                                    MYSTR   = 'ext-0   '           / String Item                                    EXTNAME = 'ext-0   '                                                            END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   16 / length of data axis 1                          NAXIS2  =                   16 / length of data axis 2                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     MYINT   =                    1 / psS32 Item                                     MYFLT   =                  0.5 / psF32 Item                                     MYDBL   =                  0.5 / psF64 Item                                     MYBOOL  =                    F / psBool Item                                    MYSTR   = 'ext-1   '           / String Item                                    EXTNAME = 'ext-1   '                                                            END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   16 / length of data axis 1                          NAXIS2  =                   16 / length of data axis 2                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     MYINT   =                    2 / psS32 Item                                     MYFLT   =            0.3333333 / psF32 Item                                     MYDBL   =    0.333333333333333 / psF64 Item                                     MYBOOL  =                    T / psBool Item                                    MYSTR   = 'ext-2   '           / String Item                                    EXTNAME = 'ext-2   '                                                            END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   16 / length of data axis 1                          NAXIS2  =                   16 / length of data axis 2                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     MYINT   =                    3 / psS32 Item                                     MYFLT   =                 0.25 / psF32 Item                                     MYDBL   =                 0.25 / psF64 Item                                     MYBOOL  =                    F / psBool Item                                    MYSTR   = 'ext-3   '           / String Item                                    EXTNAME = 'ext-3   '                                                            END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   16 / length of data axis 1                          NAXIS2  =                   16 / length of data axis 2                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     MYINT   =                    4 / psS32 Item                                     MYFLT   =                  0.2 / psF32 Item                                     MYDBL   =                  0.2 / psF64 Item                                     MYBOOL  =                    T / psBool Item                                    MYSTR   = 'ext-4   '           / String Item                                    EXTNAME = 'ext-4   '                                                            END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   16 / length of data axis 1                          NAXIS2  =                   16 / length of data axis 2                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     MYINT   =                    5 / psS32 Item                                     MYFLT   =            0.1666667 / psF32 Item                                     MYDBL   =    0.166666666666667 / psF64 Item                                     MYBOOL  =                    F / psBool Item                                    MYSTR   = 'ext-5   '           / String Item                                    EXTNAME = 'ext-5   '                                                            END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @   @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   16 / length of data axis 1                          NAXIS2  =                   16 / length of data axis 2                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     MYINT   =                    6 / psS32 Item                                     MYFLT   =            0.1428571 / psF32 Item                                     MYDBL   =    0.142857142857143 / psF64 Item                                     MYBOOL  =                    T / psBool Item                                    MYSTR   = 'ext-6   '           / String Item                                    EXTNAME = 'ext-6   '                                                            END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À  @À                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  XTENSION= 'IMAGE   '           / IMAGE extension                                BITPIX  =                  -32 / number of bits per data pixel                  NAXIS   =                    2 / number of data axes                            NAXIS1  =                   16 / length of data axis 1                          NAXIS2  =                   16 / length of data axis 2                          PCOUNT  =                    0 / required keyword; must = 0                     GCOUNT  =                    1 / required keyword; must = 1                     MYINT   =                    7 / psS32 Item                                     MYFLT   =                0.125 / psF32 Item                                     MYDBL   =                0.125 / psF64 Item                                     MYBOOL  =                    F / psBool Item                                    MYSTR   = 'ext-7   '           / String Item                                    EXTNAME = 'ext-7   '                                                            END                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à  @à                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmAstrometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmAstrometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmAstrometry.c	(revision 22322)
@@ -0,0 +1,667 @@
+/** @file  tst_psAstrometry01.c
+*
+* XXX: The source code that is tested here is in the pmAstrometry.c and
+* pmAstrometry.h files.  However, those files are not included in the
+* current automake configuration.  These tests are therefore, not
+* runnable.  We include them in this distribution for the future.
+*
+* XXX: These tests need to be converted to tap format.
+*
+* XXX: Add tests were the coordinate does not transform to any legitimate cell
+* or chip, or FPA, or whatever.
+*
+* XXX: For each function, add tests for bad input parameters, as well as failed transforms.
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define PS_PERCENT_COMPARE(X, Y, PERCENT_FRACTION) (fabs((Y)-(X))/fabs(X) < (PERCENT_FRACTION))
+#define VERBOSE 0
+#define NUM_READOUTS 1
+#define READOUT_NUM_ROWS 10
+#define READOUT_NUM_COLS 10
+
+#define NUM_CELLS 4
+#define CELL_GAP 2
+#define CELL_WIDTH READOUT_NUM_COLS
+#define CELL_HEIGHT READOUT_NUM_ROWS
+#define CELL_MIN_X 0
+#define CELL_MAX_X CELL_HEIGHT
+#define CELL_MIN_Y 0
+#define CELL_MAX_Y CELL_WIDTH
+
+#define NUM_CHIPS 2
+#define CHIP_GAP 2
+#define CHIP_MIN_X 0
+#define CHIP_MAX_X CELL_HEIGHT
+#define CHIP_MIN_Y 0
+#define CHIP_MAX_Y ((NUM_CELLS * CELL_WIDTH) + ((NUM_CELLS) * CELL_GAP))
+#define CHIP_WIDTH CHIP_MAX_Y
+#define CHIP_HEIGHT CHIP_MAX_X
+
+#define NUM_FPAS 1
+#define FPA_MIN_X 0
+#define FPA_MAX_X CHIP_HEIGHT
+#define FPA_MIN_Y 0
+#define FPA_MAX_Y (((NUM_CHIPS) * CHIP_WIDTH) + (((NUM_CHIPS)-1) * CHIP_GAP))
+
+#define PROJECTION_SCALE_X 1.0
+#define PROJECTION_SCALE_Y 1.0
+
+psS32 currentId = 0;
+psS32 memLeaks = 0;
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+genSystem(): This routine will create a system of FPAs/Chips/Cells/Readouts.  For
+simplicity, an FPA is defined as a linear array of chips, and a chip is
+defined as a linear array of cells, both in the y (cols) direction.  The
+transforms between the various layers take into account the cell/chip and
+the boundaries between each.
+ *****************************************************************************/
+pmFPA *genSystem()
+{
+    //
+    // Create top pmFPA structure.
+    //
+    const psMetadata *camera = psMetadataAlloc();
+    pmFPA* myFPA = pmFPAAlloc(camera);
+    if (myFPA == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc() returned a NULL.\n");
+        return NULL;
+    }
+    myFPA->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    myFPA->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    myFPA->projection = psProjectionAlloc(0.0,0.0,PROJECTION_SCALE_X,PROJECTION_SCALE_X,PS_PROJ_TAN);
+
+    myFPA->chips = psArrayRealloc(myFPA->chips, NUM_CHIPS);
+    for (psS32 chipID=0 ; chipID<NUM_CHIPS ; chipID++) {
+        pmChip *myChip = pmChipAlloc(myFPA, "ChipName");
+        if (myChip == NULL) {
+            psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmChipAlloc() returned a NULL.\n");
+            return NULL;
+        }
+        myFPA->chips->data[chipID] = (psPtr *) myChip;
+        int myChipRow0 = 0;
+        int myChipCol0 = chipID * (CHIP_WIDTH + CHIP_GAP);
+
+        // We create the transforms between the chip and FPA.  The
+        // transform is a simple identity transform.
+        myChip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+        myChip->toFPA->y->coeff[0][0] = (psF64) myChipCol0;
+        myChip->toFPA->y->coeff[1][1] = 0.0;
+        myChip->toFPA->x->coeff[0][0] = (psF64) myChipRow0;
+        myChip->toFPA->x->coeff[1][1] = 0.0;
+
+        myChip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+        myChip->fromFPA->y->coeff[0][0] = (psF64) (- myChipCol0);
+        myChip->fromFPA->y->coeff[1][1] = 0.0;
+        myChip->fromFPA->x->coeff[0][0] = (psF64) (- myChipRow0);
+        myChip->fromFPA->x->coeff[1][1] = 0.0;
+
+        myChip->cells = psArrayRealloc(myChip->cells, NUM_CELLS);
+        for (psS32 cellID=0 ; cellID<NUM_CELLS ; cellID++) {
+            pmCell *myCell = pmCellAlloc(myChip, NULL, "CellName");
+            if (myCell == NULL) {
+                psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmCellAlloc() returned a NULL.\n");
+                return NULL;
+            }
+            myChip->cells->data[cellID] = (psPtr *) myCell;
+            int myCellRow0 = 0;
+            int myCellCol0 = cellID * (CELL_WIDTH + CELL_GAP);
+            myCell->toChip = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+            myCell->toChip->y->coeff[0][0] = (psF64) myCellCol0;
+            myCell->toChip->y->coeff[1][1] = 0.0;
+            myCell->toChip->x->coeff[0][0] = (psF64) myCellRow0;
+            myCell->toChip->x->coeff[1][1] = 0.0;
+
+            myCell->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+            myCell->toFPA->y->coeff[0][0] = (psF64) (myCellCol0 + myChipCol0);
+            myCell->toFPA->y->coeff[1][1] = 0.0;
+            myCell->toFPA->x->coeff[0][0] = (psF64) (myCellRow0 + myChipRow0);
+            myCell->toFPA->x->coeff[1][1] = 0.0;
+
+            myCell->toSky = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+            myCell->toSky->y->coeff[0][0] = myCell->toFPA->y->coeff[0][0];
+            myCell->toSky->y->coeff[1][1] = 0.0;
+            myCell->toSky->x->coeff[0][0] = myCell->toFPA->x->coeff[0][0];
+            myCell->toSky->x->coeff[1][1] = 0.0;
+
+            myCell->readouts = psArrayRealloc(myCell->readouts, NUM_READOUTS);
+            for (psS32 readoutID=0 ; readoutID<NUM_READOUTS ; readoutID++) {
+                pmReadout *myReadout = pmReadoutAlloc(myCell);
+                if (myReadout == NULL) {
+                    psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmReadoutAlloc() returned a NULL.\n");
+                    return NULL;
+                }
+                myCell->readouts->data[readoutID] = (psPtr *) myReadout;
+                myReadout->image = psImageAlloc(READOUT_NUM_COLS, READOUT_NUM_ROWS, PS_TYPE_F32);
+                for (psS32 row=0;row<READOUT_NUM_ROWS;row++) {
+                    for(psS32 col=0;col<READOUT_NUM_COLS;col++) {
+                        myReadout->image->data.F32[row][col] = (psF32) ((chipID * (CHIP_WIDTH + CHIP_GAP)) +
+                                                               (cellID * (CELL_WIDTH + CELL_GAP)));
+                    }
+                }
+                int myReadoutRow0 = myCellRow0;
+                int myReadoutCol0 = myCellCol0;
+                myReadout->rowBins = 0;
+                myReadout->colBins = 0;
+            }
+            if (VERBOSE) {
+                printf("\n\n\n\nFor chip %d cell %d the cell->toFPA transform is:\n", chipID, cellID);
+                PS_PRINT_PLANE_TRANSFORM(myCell->toFPA);
+                printf("\n\n\n\nFor chip %d cell %d the cell->toChip transform is:\n", chipID, cellID);
+                PS_PRINT_PLANE_TRANSFORM(myCell->toChip);
+                printf("\n\n\n\nFor chip %d cell %d the cell->toSky transform is:\n", chipID, cellID);
+                PS_PRINT_PLANE_TRANSFORM(myCell->toSky);
+            }
+        }
+        if (VERBOSE) {
+            printf("\n\n\n\nFor chip %d the chip->toFPA transform is:\n", chipID);
+            PS_PRINT_PLANE_TRANSFORM(myChip->toFPA);
+            printf("\n\n\n\nFor chip %d the chip->fromFPA transform is:\n", chipID);
+            PS_PRINT_PLANE_TRANSFORM(myChip->fromFPA);
+        }
+    }
+
+    return(myFPA);
+}
+
+
+/******************************************************************************
+This routine tests many Astrometry functions.  It loops through all valid
+pixels of all cells of all chips and computes the corresponding (x,y)
+coordinates in the FPA plane.  It then calls the pmChipInFPA() then
+pmCellInFPA() with the FPA coordinate and determines the chip/cell that that
+coordinate corresponds to.  Following that it does a variety of tests on the
+various functions that tharnsform coordinates within the pmFPA hierarchy.
+ 
+List of tested functions:
+    pmCellInFPA()  yes
+    pmChipInFPA()  yes
+    pmCellInChip()  yes
+ 
+    pmCoordCellToFPA()  yes
+    pmCoordChipToFPA()  yes
+    pmCoordFPAToChip()  yes
+    pmCoordCellToChip()  yes
+    pmCoordChipToCell()  yes
+ 
+    pmCoordFPAToTP()  yes
+    pmCoordTPToFPA()  yes
+ 
+    pmCoordTPToSky()  yes
+    pmCoordSkyToTP()  yes
+    pmCoordSkyToCell()  yes
+    pmCoordCellToSky()  yes
+    pmCoordCellToSkyQuick() yes
+    pmCoordSkyToCellQuick() yes
+ *****************************************************************************/
+psS32 test3( void )
+{
+    psS32 x;
+    psS32 y;
+    psPlane fpaCoord;
+    pmFPA *myFPA = genSystem();
+    pmCell *myCell = NULL;
+    psPlane chipCoord;
+    psPlane cellCoord;
+    psPlane testCoord;
+    psSphere *skyCoord = psSphereAlloc();
+    // XXX: This code causes a seg fault.
+    //    psSphere skyTmp;
+    //    psMemCheckType(PS_DATA_SPHERE, &skyTmp);
+    psPlane tpCoord;
+    psS32 testStatus = 0;
+
+    //
+    // I'm not convinced that the p_psProject() and p_psDeproject() functions work
+    // correctly.  If we project a set of coordinates over a wide range of (R, D)
+    // values, then deproject them, the original (R, D) values are only produced
+    // when D is larger than 0.  This code demonstrates that.  I also created tests
+    // that currently fail in tst_psCoord01.c.  I have a workaround in the function
+    // XXXDeproject() in pmAstrometry.c.
+    //
+    if (0) {
+        // This loop goes from (R, D) -> (X, Y) -> (R, D)
+        psPlane planeCoord01;
+        psSphere skyCoord01;
+        psSphere skyCoord02;
+        #define DEG_INC 15.0
+
+        for (psF32 R = -90.0 ; R <= 90.0 ; R+= DEG_INC) {
+            for (psF32 D = -90.0 ; D <= 90.0 ; D+= DEG_INC) {
+                if ((fabs(R) != 90.0) && (fabs(D) != 90.0)) {
+                    skyCoord01.r = DEG_TO_RAD(R);
+                    skyCoord01.d = DEG_TO_RAD(D);
+                    p_psProject(&planeCoord01, &skyCoord01, myFPA->projection);
+                    p_psDeproject(&skyCoord02, &planeCoord01, myFPA->projection);
+                    printf("(%.2fr %.2fd) (%.2fr %.2fd) -> (%.2f %.2f) -> (%.2fr %.2fd)", R, D,
+                           skyCoord01.r, skyCoord01.d,
+                           planeCoord01.x, planeCoord01.y,
+                           skyCoord02.r, skyCoord02.d);
+                    if ((fabs(skyCoord01.r - skyCoord02.r) < FLT_EPSILON) &&
+                            (fabs(skyCoord01.d - skyCoord02.d) < FLT_EPSILON)) {
+                        printf(": CORRECT\n");
+                    } else {
+                        printf(": WRONG\n");
+                    }
+                }
+            }
+        }
+        psFree(myFPA);
+        return(0);
+    }
+    if (0) {
+        // This loop goes from (X, Y) -> (R, D) -> (X, Y)
+        #define SPACE_INC 4.0
+        for(testCoord.x=-CELL_HEIGHT;testCoord.x<=CELL_HEIGHT;testCoord.x+=SPACE_INC)
+        {
+            for (testCoord.y=-CELL_WIDTH;testCoord.y<=CELL_WIDTH;testCoord.y+=SPACE_INC) {
+                psPlane planeCoord01;
+                psPlane planeCoord02;
+                psSphere skyCoord01;
+                psSphere skyCoord02;
+                p_psDeproject(&skyCoord01, &testCoord, myFPA->projection);
+                p_psProject(&planeCoord01, &skyCoord01, myFPA->projection);
+                p_psDeproject(&skyCoord02, &planeCoord01, myFPA->projection);
+                p_psProject(&planeCoord02, &skyCoord02, myFPA->projection);
+                printf("Plane: (%.2f %.2f) -> (%.2fr %.2fd) -> (%.2f %.2f)\n",
+                       testCoord.x, testCoord.y,
+                       skyCoord01.r, skyCoord01.d,
+                       planeCoord01.x, planeCoord01.y);
+                /*
+                                printf("Plane: (%.2f %.2f) -> (%.2f %.2f) -> (%.2f %.2f)\n",
+                                        testCoord.x, testCoord.y, planeCoord01.x, planeCoord01.y,
+                                        planeCoord02.x, planeCoord02.y);
+                                printf("Sphere: (%.2f %.2f) -> (%.2f %.2f)\n",
+                                        skyCoord01.r, skyCoord01.d, skyCoord02.r, skyCoord02.d);
+                                printf("Plane: (%.2f %.2f) -> (%.2fd %.2fr) -> (%.2f %.2f) -> (%.2fd %.2fr) -> (%.2f %.2f)\n",
+                                        testCoord.x, testCoord.y,
+                                        skyCoord01.r, skyCoord01.d,
+                                        planeCoord01.x, planeCoord01.y,
+                                        skyCoord02.r, skyCoord02.d,
+                                        planeCoord02.x, planeCoord02.y);
+                */
+            }
+        }
+        psFree(myFPA);
+        return(0);
+    }
+    //
+    // We iterate through all cells on all chips on the fpa.  We determine
+    // the expected fpaCcoord.
+    //
+
+    for (psS32 chip=0;chip<NUM_CHIPS;chip++) {
+        for (psS32 cell=0;cell<NUM_CELLS;cell++) {
+            for(x=0;x<CELL_HEIGHT;x++) {
+                for (y=0;y<CELL_WIDTH;y++) {
+                    fpaCoord.x = (psF64) x;
+                    fpaCoord.y = (psF64) (y + (chip * (CHIP_WIDTH + CHIP_GAP)) +
+                                          (cell * (CELL_WIDTH + CELL_GAP)));
+                    if (VERBOSE) {
+                        printf("------------------ (%.2f, %.2f) ------------------\n", fpaCoord.x, fpaCoord.y);
+                        printf("(chip, cell, x, y) is (%d, %d, %d, %d)\n", chip, cell, x, y);
+                    }
+                    pmChip* tmpChip = pmChipInFPA(&fpaCoord, myFPA);
+                    myCell = pmCellInFPA(&fpaCoord, myFPA);
+
+                    if ((myCell == NULL) || (tmpChip == NULL)) {
+                        if (tmpChip == NULL) {
+                            printf("TEST ERROR: pmChipInFPA() returned NULL\n");
+                            testStatus = 1;
+                        } else if (myCell == NULL) {
+                            printf("TEST ERROR: pmCellInFPA(): returned NULL\n");
+                            testStatus = 1;
+                        }
+                    } else {
+                        pmCoordFPAToChip(&chipCoord, &fpaCoord, tmpChip);
+                        pmCoordChipToCell(&cellCoord, &chipCoord, myCell);
+
+                        if (x != (psS32) cellCoord.x) {
+                            printf("TEST ERROR: pmCoordFPAToChip()->pmCoordChipToCell(): x coord was %d (%f), should be %d\n", (psS32) cellCoord.x, cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (y != (psS32) cellCoord.y) {
+                            printf("TEST ERROR: pmCoordFPAToChip()->pmCoordChipToCell(): y coord was %d (%f), should be %d\n", (psS32) cellCoord.y, cellCoord.y, y);
+                            testStatus = 1;
+                        }
+
+                        pmCoordCellToChip(&testCoord, &cellCoord, myCell);
+                        if (fabs(testCoord.x - chipCoord.x) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordCellToChip() x coord was %.2f, should be %d\n", cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (fabs(testCoord.y - chipCoord.y) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordCellToChip() y coord was %.2f, should be %d\n", cellCoord.y, y);
+                            testStatus = 1;
+                        }
+
+                        pmCell *myCell2 = pmCellInChip(&chipCoord, tmpChip);
+                        if (myCell2 != myCell) {
+                            printf("TEST ERROR: pmCellInChip() != pmCellInChip(pmChipInFPA()) (%p %p)\n", myCell2, myCell);
+                            testStatus = 1;
+                        }
+
+                        pmCoordChipToFPA(&testCoord, &chipCoord, tmpChip);
+                        if (fabs(testCoord.x - x) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordChipToFPA() x coord was %.2f, should be %d\n", cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (fabs(testCoord.y - fpaCoord.y) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordChipToFPA() y coord was %.2f, should be %.2f\n", cellCoord.y, fpaCoord.y);
+                            testStatus = 1;
+                        }
+
+                        pmCoordFPAToTP(&testCoord, &fpaCoord, 0.0, 0.0, myFPA);
+                        if (fabs(testCoord.x - fpaCoord.x) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordFPAToTP() x coord was %.2f, should be %d\n", cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (fabs(testCoord.y - fpaCoord.y) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordFPAToTP() y coord was %.2f, should be %d\n", cellCoord.y, y);
+                            testStatus = 1;
+                        }
+
+                        //
+                        // Test pmCoordTPToSky() -> pmCoordSkyToTP()
+                        //
+                        if (1) {
+                            psSphere *rc = pmCoordTPToSky(skyCoord, &testCoord, myFPA->projection);
+                            if (rc == NULL) {
+                                printf("pmCoordTPToSky() failed.\n");
+                            } else {
+                                psPlane *rc = pmCoordSkyToTP(&tpCoord, skyCoord, myFPA->projection);
+                                if (rc == NULL) {
+                                    printf("pmCoordSkyToTP() failed.\n");
+                                } else {
+                                    if (fabs(testCoord.x - tpCoord.x) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordTPToSky()/pmCoordSkyToTP() x coord was %.2f, should be %.2f\n", tpCoord.x, testCoord.x);
+                                        testStatus = 1;
+                                    }
+                                    if (fabs(testCoord.y - tpCoord.y) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordTPToSky()/pmCoordSkyToTP() y coord was %.2f, should be %.2f\n", tpCoord.y, testCoord.y);
+                                        testStatus = 1;
+                                    }
+                                    if (VERBOSE) {
+                                        printf("(%.2f %.2f) -> (%.2f %.2f) -> (%.2f %.2f)\n", testCoord.x, testCoord.y, skyCoord->r, skyCoord->d, tpCoord.x, tpCoord.y);
+                                    }
+                                }
+                            }
+                        }
+
+                        //
+                        // Test pmCoordCellToSky() -> pmCoordSkyToCell()
+                        //
+                        if (1) {
+                            psPlane tmpCellCoord;
+                            psSphere *rc = pmCoordCellToSky(skyCoord, &cellCoord, 0.0, 0.0, myCell);
+                            if (rc == NULL) {
+                                printf("pmCoordCellToSky() failed.\n");
+                            } else {
+                                psPlane *rc = pmCoordSkyToCell(&tmpCellCoord, skyCoord, 0.0, 0.0, myCell);
+                                if (rc == NULL) {
+                                    printf("pmCoordSkyToCell() failed.\n");
+                                } else {
+                                    if (fabs(cellCoord.x - tmpCellCoord.x) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordCellToSky()/pmCoordSkyToCell() x coord was %.2f, should be %.2f\n", tmpCellCoord.x, cellCoord.x);
+                                        testStatus = 1;
+                                    }
+                                    if (fabs(cellCoord.y - tmpCellCoord.y) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordCellToSky()/pmCoordSkyToCell() y coord was %.2f, should be %.2f\n", tmpCellCoord.y, cellCoord.y);
+                                        testStatus = 1;
+                                    }
+                                    if (VERBOSE) {
+                                        printf("(%.2f %.2f) -> (%.2f %.2f) -> (%.2f %.2f)\n", testCoord.x, testCoord.y, skyCoord->r, skyCoord->d, tmpCellCoord.x, tmpCellCoord.y);
+                                    }
+                                }
+                            }
+                        }
+
+                        //
+                        // Test pmCoordCellToSkyQuick() -> pmCoordSkyToCellQuick()
+                        // I'm not sure how to test this in a system with chip and cell gaps.
+                        // There's no way to create an accurate polynomial transform from
+                        // a cell to the sky where there are cell, or chip gaps.
+                        //
+                        if ((NUM_CHIPS == 1) && (NUM_CELLS == 1)) {
+                            psPlane tmpCellCoord;
+                            psSphere *rc = pmCoordCellToSkyQuick(skyCoord, &cellCoord, myCell);
+                            if (rc == NULL) {
+                                printf("pmCoordCellToSkyQuick() failed.\n");
+                            } else {
+                                psPlane *rc = pmCoordSkyToCellQuick(&tmpCellCoord, skyCoord, myCell);
+                                if (rc == NULL) {
+                                    printf("pmCoordSkyToCellQuick() failed.\n");
+                                } else {
+                                    if (fabs(cellCoord.x - tmpCellCoord.x) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordCellToSky()/pmCoordSkyToCell() x coord was %.2f, should be %.2f\n", tmpCellCoord.x, cellCoord.x);
+                                        testStatus = 1;
+                                    }
+                                    if (fabs(cellCoord.y - tmpCellCoord.y) > FLT_EPSILON) {
+                                        printf("TEST ERROR: pmCoordCellToSky()/pmCoordSkyToCell() y coord was %.2f, should be %.2f\n", tmpCellCoord.y, cellCoord.y);
+                                        testStatus = 1;
+                                    }
+                                    if (VERBOSE) {
+                                        printf("(%.2f %.2f) -> (%.2f %.2f) -> (%.2f %.2f)\n", testCoord.x, testCoord.y, skyCoord->r, skyCoord->d, tmpCellCoord.x, tmpCellCoord.y);
+                                    }
+                                }
+                            }
+                        }
+
+                        pmCoordCellToFPA(&testCoord, &cellCoord, myCell);
+                        if (fabs(testCoord.x - fpaCoord.x) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordCellToFPA() x coord was %.2f, should be %d\n", cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (fabs(testCoord.y - fpaCoord.y) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordCellToFPA() y coord was %.2f, should be %d\n", cellCoord.y, y);
+                            testStatus = 1;
+                        }
+
+                        pmCoordTPToFPA(&testCoord, &fpaCoord, 0.0, 0.0, myFPA);
+                        if (fabs(testCoord.x - fpaCoord.x) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordTPToFPA() x coord was %.2f, should be %d\n", cellCoord.x, x);
+                            testStatus = 1;
+                        }
+                        if (fabs(testCoord.y - fpaCoord.y) > FLT_EPSILON) {
+                            printf("TEST ERROR: pmCoordTPToFPA() y coord was %.2f, should be %d\n", cellCoord.y, y);
+                            testStatus = 1;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    psFree(myFPA);
+    psFree(skyCoord);
+
+    return(testStatus);
+}
+
+/******************************************************************************
+test4(): This routine wil test the pmFPACheckParents() function.  We generate
+an pmFPA hierarchy, then set the parents of each readout/cell/chip to NULL,
+then call pmFPACheckParents() to restore them, then we ensure they were
+restored.
+ *****************************************************************************/
+psS32 test4( void )
+{
+    psS32 testStatus = 0;
+
+    //
+    // Generate a pmFPA hierarchy.
+    //
+    pmFPA *tmpFPA = genSystem();
+
+    //
+    // We set the parents of each readout/cell/chip to NULL.
+    //
+    for (psS32 chipID = 0; chipID < tmpFPA->chips->n ; chipID++) {
+        pmChip *tmpChip = (pmChip *) tmpFPA->chips->data[chipID];
+        tmpChip->parent = NULL;
+
+        for (psS32 cellID = 0; cellID < tmpChip->cells->n ; cellID++) {
+            pmCell *tmpCell = (pmCell *) tmpChip->cells->data[cellID];
+            tmpCell->parent = NULL;
+
+            for (psS32 readoutID = 0; readoutID < tmpCell->readouts->n ; readoutID++) {
+                pmReadout *tmpReadout = (pmReadout *) tmpCell->readouts->data[readoutID];
+                tmpReadout->parent = NULL;
+            }
+        }
+    }
+
+    //
+    // Ensure that pmFPACheckParents() returned FALSE.
+    //
+    psBool rc = pmFPACheckParents(tmpFPA);
+    if (rc != false) {
+        printf("TEST ERROR: pmCheckParents() returned TRUE.\n");
+        testStatus = 1;
+    }
+
+    //
+    // Ensure that the parent members are right.
+    //
+    for (psS32 chipID = 0; chipID < tmpFPA->chips->n ; chipID++) {
+        pmChip *tmpChip = (pmChip *) tmpFPA->chips->data[chipID];
+        if (tmpChip->parent != tmpFPA) {
+            printf("TEST ERROR: pmCheckParents() did not restore Chip->parent.\n");
+            testStatus = 2;
+        }
+
+        for (psS32 cellID = 0; cellID < tmpChip->cells->n ; cellID++) {
+            pmCell *tmpCell = (pmCell *) tmpChip->cells->data[cellID];
+            if (tmpCell->parent != tmpChip) {
+                printf("TEST ERROR: pmCheckParents() did not restore Cell->parent.\n");
+                testStatus = 3;
+            }
+
+            for (psS32 readoutID = 0; readoutID < tmpCell->readouts->n ; readoutID++) {
+                pmReadout *tmpReadout = (pmReadout *) tmpCell->readouts->data[readoutID];
+                if (tmpReadout->parent != tmpCell) {
+                    printf("TEST ERROR: pmCheckParents() did not restore Readout->parent.\n");
+                    testStatus = 4;
+                }
+            }
+        }
+    }
+
+    psFree(tmpFPA);
+    return(testStatus);
+}
+
+/******************************************************************************
+test5(): This routine wil test the pmFPASelectChip() and pmFPAExcludeChip()
+functions.  We generate an pmFPA hierarchy, then set the ->valid members with
+those routines, then verify.
+ *****************************************************************************/
+psS32 test5( void )
+{
+    psS32 testStatus = 0;
+    pmChip *tmpChip = NULL;
+
+    //
+    // Generate a pmFPA hierarchy.
+    //
+    pmFPA *tmpFPA = genSystem();
+
+    //
+    // We test the ->valid member for each chip.
+    //
+    for (psS32 i = 0 ; i < tmpFPA->chips->n ; i++) {
+        tmpChip = (pmChip *) tmpFPA->chips->data[i];
+        if ((tmpChip == NULL) || (tmpChip->valid != false)) {
+            printf("TEST ERROR: Could not properly generate an FPA hierarchy.\n");
+            testStatus = 1;
+        }
+    }
+
+    //
+    // Exclude chip number 0, include all others, then test return value
+    //
+    psS32 numChips = pmFPAExcludeChip(tmpFPA, 0);
+    if (numChips != (NUM_CHIPS-1)) {
+        printf("TEST ERROR: pmFPAExcludeChip() did not return the correct number of chips.\n");
+        testStatus = 2;
+    }
+
+    //
+    // We test the ->valid member for each chip.
+    //
+    tmpChip = (pmChip *) tmpFPA->chips->data[0];
+    if (tmpChip->valid != false) {
+        printf("TEST ERROR: pmFPAExcludeChip() did not set the proper chip->valid to FALSE.\n");
+        testStatus = 3;
+    }
+    for (psS32 i = 1 ; i < tmpFPA->chips->n ; i++) {
+        pmChip *tmpChip = (pmChip *) tmpFPA->chips->data[i];
+        if (tmpChip->valid != true) {
+            printf("TEST ERROR: pmFPAExcludeChip() did not set the proper chip->valids to FALSE.\n");
+            testStatus = 4;
+        }
+    }
+
+
+    //
+    // Include chip number 0, exclude all others, then test return value
+    //
+    psBool tmpBool = pmFPASelectChip(tmpFPA, 0);
+    if (tmpBool != true) {
+        printf("TEST ERROR: pmFPASelectChip() returned FALSE.\n");
+        testStatus = 5;
+    }
+
+    //
+    // We test the ->valid member for each chip.
+    //
+    tmpChip = (pmChip *) tmpFPA->chips->data[0];
+    if (tmpChip->valid != true) {
+        printf("TEST ERROR: pmFPASelectChip() did not set the proper chip->valid to FALSE.\n");
+        testStatus = 6;
+    }
+    for (psS32 i = 1 ; i < tmpFPA->chips->n ; i++) {
+        pmChip *tmpChip = (pmChip *) tmpFPA->chips->data[i];
+        if (tmpChip->valid != false) {
+            printf("TEST ERROR: pmFPASelectChip() did not set the proper chip->valids to FALSE.\n");
+            testStatus = 7;
+        }
+    }
+
+    psFree(tmpFPA);
+    return(testStatus);
+}
+
+psS32 main( psS32 argc, char* argv[] )
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(92);
+
+    test3();
+    test4();
+    test5();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPA.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPA.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPA.c	(revision 22322)
@@ -0,0 +1,492 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+*/
+
+#define CHIP_ALLOC_NAME "ChipName"
+#define CELL_ALLOC_NAME "CellName"
+#define MISC_NUM 32
+#define MISC_NAME "META00"
+#define MISC_NAME2 "META01"
+#define NUM_BIAS_DATA 10
+#define TEST_NUM_ROWS 32
+#define TEST_NUM_COLS 32
+#define NUM_READOUTS	4
+#define NUM_CELLS	6
+#define NUM_CHIPS	8
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    cell->hdu = pmHDUAlloc(NULL);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    chip->hdu = pmHDUAlloc(NULL);
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    fpa->hdu = pmHDUAlloc(NULL);
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+
+    return(fpa);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(92);
+
+    // ------------------------------------------------------------------------
+    // pmFPAAlloc() tests
+    // Call pmFPAAlloc() with NULL pmCamera input.
+    {
+        psMemId id = psMemGetId();
+        pmFPA* fpa = pmFPAAlloc(NULL);
+        ok(fpa != NULL && psMemCheckFPA(fpa), "pmFPAAlloc() returned a non-NULL pmFPA with a NULL pmCamera input");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call pmFPAAlloc() with acceptable input parameters.
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = pmFPAAlloc(camera);
+        ok(fpa != NULL, "pmFPAAlloc() returned a non-NULL");
+        ok(fpa->fromTPA == NULL, "pmFPAAlloc() set ->fromTPA to NULL");
+        ok(fpa->toTPA == NULL, "pmFPAAlloc() set ->toTPA to NULL");
+        ok(fpa->toSky == NULL, "pmFPAAlloc() set ->toSky to NULL");
+        ok(fpa->concepts != NULL &&
+           psMemCheckMetadata(fpa->concepts), "pmFPAAlloc() set ->concepts correctly");
+        ok(fpa->conceptsRead == PM_CONCEPT_SOURCE_NONE, "pmFPAAlloc() set ->conceptsRead correctly");
+        ok(fpa->analysis != NULL &&
+           psMemCheckMetadata(fpa->analysis), "pmFPAAlloc() set ->analysis correctly");
+        ok(fpa->camera == camera, "pmFPAAlloc() set ->camera correctly");
+        ok(fpa->chips != NULL &&
+           psMemCheckArray(fpa->chips), "pmFPAAlloc() set ->chips correctly");
+        ok(fpa->hdu == NULL, "pmFPAAlloc() set ->hdu to NULL");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Populate the pmFPA struct with real data to ensure they were psFree()'ed correctly.
+    {
+        psMemId id = psMemGetId();
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (Populate the pmFPA struct with real data)");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmFPAFreeData() tests
+    // Call pmFPAFreeData() with NULL pmFPA input parameter
+    {
+        psMemId id = psMemGetId();
+        pmFPAFreeData(NULL);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (Call pmFPAFreeData() with NULL pmFPA input parameter)");
+    }
+
+
+    // Call pmFPAFreeData() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        fpa->hdu->images = psArrayAlloc(10);
+        fpa->hdu->weights = psArrayAlloc(10);
+        fpa->hdu->masks = psArrayAlloc(10);
+        pmFPAFreeData(fpa);
+        ok(fpa->hdu->images == NULL, "pmFPAFreeData() correctly set fpa->hdu->images to NULL");
+        ok(fpa->hdu->weights == NULL, "pmFPAFreeData() correctly set fpa->hdu->weights to NULL");
+        ok(fpa->hdu->masks == NULL, "pmFPAFreeData() correctly set fpa->hdu->masks to NULL");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmChipAlloc() tests
+    // Call pmChipAlloc() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        pmChip *chip = pmChipAlloc(NULL, CHIP_ALLOC_NAME);
+        ok(chip != NULL, "pmChipAlloc() returned non-NULL with NULL pmFPA input parameter");
+        psFree(chip);
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        chip = pmChipAlloc(fpa, NULL);
+        ok(chip != NULL, "pmChipAlloc() returned non-NULL with NULL chip name input parameter");
+        psFree(fpa);
+        psFree(chip);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmChipAlloc() tests
+    // XXX: Add tests for NULL inputs.
+    {
+        psMemId id = psMemGetId();
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+        ok(chip != NULL, "pmChipAlloc() returned non-NULL");
+        ok(chip->toFPA == NULL, "pmChipAlloc() set chip->toChip to NULL");
+        ok(chip->fromFPA == NULL, "pmChipAlloc() set chip->fromChip to NULL");
+        ok(chip->concepts != NULL &&
+           psMemCheckMetadata(chip->concepts), "pmChipAlloc() set ->concepts correctly");
+        ok(chip->conceptsRead == PM_CONCEPT_SOURCE_NONE, "pmCellAlloc() set ->conceptsRead correctly");
+        ok(chip->analysis != NULL &&
+           psMemCheckMetadata(chip->analysis), "pmChipAlloc() set ->analysis correctly");
+        ok(chip->cells != NULL &&
+           psMemCheckArray(chip->cells), "pmChipAlloc() set ->cells correctly");
+        ok(chip->parent == fpa, "pmChipAlloc() set ->parent correctly");
+        ok(chip->process == true, "pmChipAlloc() set ->process correctly");
+        ok(chip->file_exists == false, "pmChipAlloc() set ->file_exists correctly");
+        ok(chip->data_exists == false, "pmChipAlloc() set ->data_exists correctly");
+        ok(chip->hdu == NULL, "pmChipAlloc() set ->hdu to NULL");
+        psFree(fpa);
+        psFree(chip);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Populate the pmChip struct with real data to ensure they were free'ed correctly.
+    {
+        psMemId id = psMemGetId();
+        pmChip *chip = generateSimpleChip(NULL);
+        psFree(chip);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (Populate the pmChip struct with real data)");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmChipFreeData() tests
+    // Call pmChipFreeData() with NULL pmFPA input parameter
+    {
+        psMemId id = psMemGetId();
+        pmChipFreeData(NULL);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (Call pmChipFreeData() with NULL pmFPA input parameter)");
+    }
+
+
+    // Call pmChipFreeData() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmChip* chip = generateSimpleChip(NULL);
+        chip->hdu->images = psArrayAlloc(10);
+        chip->hdu->weights = psArrayAlloc(10);
+        chip->hdu->masks = psArrayAlloc(10);
+        pmChipFreeData(chip);
+        ok(chip->hdu->images == NULL, "pmChipFreeData() correctly set chip->hdu->images to NULL");
+        ok(chip->hdu->weights == NULL, "pmChipFreeData() correctly set chip->hdu->weights to NULL");
+        ok(chip->hdu->masks == NULL, "pmChipFreeData() correctly set chip->hdu->masks to NULL");
+        psFree(chip);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmChipFreeCells() tests
+    // Call pmChipFreeCells() with NULL pmFPA input parameter
+    {
+        psMemId id = psMemGetId();
+        pmChipFreeCells(NULL);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (Call pmChipFreeCells() with NULL pmFPA input parameter)");
+    }
+
+
+    // Call pmChipFreeCells() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmChip* chip = generateSimpleChip(NULL);
+        pmChipFreeCells(chip);
+        ok(chip->cells->n == 0, "pmChipFreeCells() free'ed chip->cells correctly");
+        psFree(chip);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmCellAlloc() tests
+    // Call pmCellAlloc() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = pmCellAlloc(NULL, CELL_ALLOC_NAME);
+        ok(cell != NULL, "pmCellAlloc returned non-NULL with NULL pmChip input parameter");
+        psFree(cell);
+
+        pmChip *chip = pmChipAlloc(NULL, NULL);
+        cell = pmCellAlloc(chip, NULL);
+        ok(cell != NULL, "pmCellAlloc returned non-NULL with NULL cell name input parameter");
+        psFree(chip);
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmCellAlloc() tests
+    // Call pmCellAlloc() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+        pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+        ok(cell != NULL, "pmCellAlloc returned non-NULL");
+        ok(cell->concepts != NULL &&
+           psMemCheckMetadata(cell->concepts), "pmCellpAlloc() set ->concepts correctly");
+        ok(cell->conceptsRead == PM_CONCEPT_SOURCE_NONE, "pmCellAlloc() set ->conceptsRead correctly");
+        ok(cell->config == NULL, "pmCellpAlloc() set ->config to NULL");
+        ok(cell->analysis != NULL &&
+           psMemCheckMetadata(cell->analysis), "pmCellAlloc() set ->analysis correctly");
+        ok(cell->readouts != NULL &&
+           psMemCheckArray(cell->readouts), "pmCellAlloc() set ->readouts correctly");
+        ok(cell->parent == chip, "pmCellAlloc() set ->parent correctly");
+        ok(cell->process == true, "pmCellAlloc() set ->process correctly");
+        ok(cell->file_exists == false, "pmCellAlloc() set ->file_exists correctly");
+        ok(cell->data_exists == false, "pmCellAlloc() set ->data_exists correctly");
+        ok(cell->hdu == NULL, "pmCellAlloc() set ->hdu to NULL");
+        psFree(camera);
+        psFree(fpa);
+        psFree(chip);
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Populate the pmCell struct with real data to ensure they were
+    // psFree()'ed correctly.
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (Populate the pmCell struct with real data)");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmCellFreeData() tests
+    // Call pmCellFreeData() with NULL pmFPA input parameter
+    {
+        psMemId id = psMemGetId();
+        pmCellFreeData(NULL);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (Call pmCellFreeData() with NULL pmFPA input parameter)");
+    }
+
+
+    // Call pmCellFreeData() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        cell->hdu->images = psArrayAlloc(10);
+        cell->hdu->weights = psArrayAlloc(10);
+        cell->hdu->masks = psArrayAlloc(10);
+        pmCellFreeData(cell);
+        ok(cell->hdu->images == NULL, "pmCellFreeData() correctly set cell->hdu->images to NULL");
+        ok(cell->hdu->weights == NULL, "pmCellFreeData() correctly set cell->hdu->weights to NULL");
+        ok(cell->hdu->masks == NULL, "pmCellFreeData() correctly set cell->hdu->masks to NULL");
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // test pmCellFreeReadouts()
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmCellFreeReadouts(cell);
+        ok(cell->readouts->n == 0, "pmCellFreeReadouts() correctly set cell->readouts->n to 0");
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (pmCellFreeReadouts)");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmReadoutAlloc() tests
+    // Call pmReadoutAlloc() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        pmReadout *readout = pmReadoutAlloc(NULL);
+        ok(readout != NULL, "pmReadoutAlloc() returned non-NULL with NULL pmCell input parameter");
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmReadoutAlloc() with acceptable parameters
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+        pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+        pmReadout *readout = pmReadoutAlloc(cell);
+        ok(readout != NULL, "pmReadoutAlloc() returned non-NULL");
+        ok(readout->col0 == 0, "pmReadoutAlloc() set ->col0 correctly");
+        ok(readout->row0 == 0, "pmReadoutAlloc() set ->row0 correctly");
+        ok(readout->image == NULL, "pmReadoutAlloc() set ->image correctly");
+        ok(readout->mask == NULL, "pmReadoutAlloc() set ->mask correctly");
+        ok(readout->weight == NULL, "pmReadoutAlloc() set ->weight correctly");
+        ok(readout->bias != NULL &&
+           psMemCheckList(readout->bias), "pmReadoutAlloc() set ->bias correctly");
+        ok(readout->analysis != NULL &&
+           psMemCheckMetadata(readout->analysis), "pmReadoutAlloc() set ->analysis correctly");
+        ok(readout->parent == cell, "pmReadoutAlloc() set ->parent correctly");
+        ok(readout->process == true, "pmReadoutAlloc() set ->process correctly");
+        ok(readout->file_exists == false, "pmReadoutAlloc() set ->file_exists correctly");
+        ok(readout->data_exists == false, "pmReadoutAlloc() set ->data_exists correctly");
+        psFree(camera);
+        psFree(fpa);
+        psFree(chip);
+        psFree(cell);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Populate the pmReadout struct with real data to ensure they were
+    // psFree()'ed correctly.
+    {
+        psMemId id = psMemGetId();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        psFree(readout);
+        // XXX: The pmReadout->bias list is not being free'ed correctly.
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmReadoutFreeData() tests
+    // Call pmReadoutFreeData() with NULL pmFPA input parameter
+    {
+        psMemId id = psMemGetId();
+        pmReadoutFreeData(NULL);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks (Call pmReadoutFreeData() with NULL pmFPA input parameter)");
+    }
+
+
+    // Call pmReadoutFreeData() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        pmReadoutFreeData(readout);
+        ok(readout->image == NULL, "pmReadoutFreeData() correctly set readout->image to NULL");
+        ok(readout->weight == NULL, "pmReadoutFreeData() correctly set readout->weight to NULL");
+        ok(readout->mask == NULL, "pmReadoutFreeData() correctly set readout->mask to NULL");
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmFPACheckParents() tests
+    // psBool pmFPACheckParents(pmFPA *fpa)
+    // Call pmFPACheckParents() with NULL pmFPA input parameter
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmFPACheckParents(NULL);
+        ok(rc == false, "pmFPACheckParents() returned FALSE with NULL pmFPA input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPACheckParents() tests
+    // Call pmFPACheckParents() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        bool rc = pmFPACheckParents(fpa);
+        ok(rc == true, "pmFPACheckParents() returned FALSE with acceptable input parameters");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPACellSquish.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPACellSquish.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPACellSquish.c	(revision 22322)
@@ -0,0 +1,193 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+// XXX: Use better name for the temporary FITS file
+// XXX: The code to generate and free the FPA hierarchy was copied from
+// tap-pmFPA.c.  EIther include it directly, or library, or something.
+// Also, get rid of the manual free functions and use psFree() once
+// it correctly frees child members
+// XXX: For the genSimpleFPA() code, add IDs to each function so that
+// the values set in each chip-?cell-?hdu-?image are unique
+// XXX: For the genSimpleFPA() code, write masks and weights as well
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+//    psRegion *region = psRegionAlloc(0.0, TEST_NUM_COLS-1, 0.0, TEST_NUM_ROWS-1);
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(4);
+
+
+    // ----------------------------------------------------------------------
+    // pmCellSquish() tests
+    // bool pmCellSquish(pmCell *cell, psMaskType maskVal, bool useShifts)
+    {
+        psMemId id = psMemGetId();
+        ok(!pmCellSquish(NULL, 0, false), "pmCellSquish(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // ----------------------------------------------------------------------
+    // pmCellSquish() tests
+    // bool pmCellSquish(pmCell *cell, psMaskType maskVal, bool useShifts)
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(pmCellSquish(cell, 0, false), "pmCellSquish(NULL) returned NULL");
+        psFree(camera);
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAConstruct.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAConstruct.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAConstruct.c	(revision 22322)
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(9);
+
+    // ----------------------------------------------------------------------
+    // pmFPAConstruct() tests
+    // pmFPA *pmFPAConstruct(const psMetadata *camera)
+    // test will NULL camera input param
+    {
+        psMemId id = psMemGetId();
+        pmFPA* fpa = pmFPAConstruct(NULL);
+        ok(fpa == NULL, "pmFPAConstruct() NULL will NULL camera input param");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // pmFPA *pmFPAConstruct(const psMetadata *camera)
+    // test will acceptable data
+    // XXX: The memory leak code is put outside the call to pmConfigFileRead() because of
+    // a memory leak in pmConfigFileRead().
+    {
+        psMetadata *camera = psMetadataAlloc();
+        bool rc = pmConfigFileRead(&camera, "dataFiles/camera0/camera.config", "CAMERA 0 config file");
+        if (!rc) {
+             rc = pmConfigFileRead(&camera, "../dataFiles/camera0/camera.config", "CAMERA 0 config file");
+	}
+
+        // Generate the pmFPA heirarchy
+        psMemId id = psMemGetId();
+        ok(rc, "Succesfully read camera format file");
+        pmFPA* fpa = pmFPAConstruct(camera);
+        ok(fpa != NULL, "pmFPAConstruct() returned non-NULL");
+        if (VERBOSE) {
+            pmFPAPrint(stdout, fpa, true, true);
+	}
+        bool errorFlag = false;
+        ok(fpa->chips->n == 2, "pmFPAConstruct() set fpa->chips->n (%d)", fpa->chips->n);
+        for (int chipID = 0 ; chipID < fpa->chips->n ; chipID++) {
+            pmChip *chip = fpa->chips->data[chipID];
+            ok(chip->cells->n == 2, "pmFPAConstruct() set chip->cells->n (%d)", chip->cells->n);
+            for (int cellID = 0 ; cellID < chip->cells->n ; cellID++) {
+                pmCell *cell = chip->cells->data[cellID];
+                for (int readoutID = 0 ; readoutID < cell->readouts->n ; readoutID++) {
+                    pmReadout *readout = cell->readouts->data[readoutID];
+                    readout = readout;
+		}
+	    }
+	}
+        ok(!errorFlag, "pmFPAConstruct() read the pmFPA structure correctly");
+
+
+
+
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+
+        psFree(camera);
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPACopy.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPACopy.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPACopy.c	(revision 22322)
@@ -0,0 +1,601 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define CHIP_ALLOC_NAME		"ChipName"
+#define CELL_ALLOC_NAME		"CellName"
+#define MISC_NUM_SOURCE		32
+#define MISC_NUM_TARGET		16
+#define MISC_NAME_SOURCE	"META00_SOURCE"
+#define MISC_NAME_TARGET	"META00_TARGET"
+#define NUM_BIAS_DATA		10
+#define SOURCE_NUM_ROWS		16
+#define SOURCE_NUM_COLS		8
+#define TARGET_NUM_ROWS		15
+#define TARGET_NUM_COLS		5
+#define NUM_READOUTS		4
+#define NUM_CELLS		6
+#define NUM_CHIPS		8
+#define SOURCE_BASE		10
+#define BASE_INC		20
+#define TARGET_BASE		(SOURCE_BASE + BASE_INC)
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadoutSource(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(SOURCE_NUM_COLS, SOURCE_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(SOURCE_NUM_COLS, SOURCE_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(SOURCE_NUM_COLS, SOURCE_NUM_ROWS, PS_TYPE_F32);
+    for (int i = 0 ; i < SOURCE_NUM_ROWS ; i++) {
+        for (int j = 0 ; j < SOURCE_NUM_COLS ; j++) {
+            readout->image->data.F32[i][j] = (float) (i + j + SOURCE_BASE);
+            readout->mask->data.U8[i][j] = (psU8) (i + j + SOURCE_BASE);
+            readout->weight->data.F32[i][j] = (float) (i + j + SOURCE_BASE);
+	}
+    }
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(SOURCE_NUM_COLS, SOURCE_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME_SOURCE, 0, NULL, MISC_NUM_SOURCE);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCellSource(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME_SOURCE, 0, NULL, MISC_NUM_SOURCE);
+    cell->hdu = pmHDUAlloc(NULL);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME_SOURCE, 0, NULL, MISC_NUM_SOURCE);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, "CELL.XPARITY", PS_META_REPLACE, NULL, 1);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, "CELL.YPARITY", PS_META_REPLACE, NULL, 1);
+
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadoutSource(cell));
+    }
+    cell->data_exists = true;
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChipSource(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME_SOURCE, 0, NULL, MISC_NUM_SOURCE);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME_SOURCE, 0, NULL, MISC_NUM_SOURCE);
+    chip->hdu = pmHDUAlloc(NULL);
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCellSource(chip));
+    }
+    chip->data_exists = true;
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPASource(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME_SOURCE, 0, NULL, MISC_NUM_SOURCE);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME_SOURCE, 0, NULL, MISC_NUM_SOURCE);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME_SOURCE, 0, NULL, MISC_NUM_SOURCE);
+    fpa->hdu = pmHDUAlloc(NULL);
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChipSource(fpa));
+    }
+
+    return(fpa);
+}
+
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadoutTarget(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TARGET_NUM_COLS, TARGET_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TARGET_NUM_COLS, TARGET_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TARGET_NUM_COLS, TARGET_NUM_ROWS, PS_TYPE_F32);
+    for (int i = 0 ; i < TARGET_NUM_ROWS ; i++) {
+        for (int j = 0 ; j < TARGET_NUM_COLS ; j++) {
+            readout->image->data.F32[i][j] = (float) (i + j + TARGET_BASE);
+            readout->mask->data.U8[i][j] = (psU8) (i + j + TARGET_BASE);
+            readout->weight->data.F32[i][j] = (float) (i + j + TARGET_BASE);
+	}
+    }
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TARGET_NUM_COLS, TARGET_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME_TARGET, 0, NULL, MISC_NUM_TARGET);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCellTarget(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME_TARGET, 0, NULL, MISC_NUM_TARGET);
+    cell->hdu = pmHDUAlloc(NULL);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME_TARGET, 0, NULL, MISC_NUM_TARGET);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, "CELL.XPARITY", PS_META_REPLACE, NULL, 1);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, "CELL.YPARITY", PS_META_REPLACE, NULL, 1);
+    psArrayRealloc(cell->readouts, 0);
+    for (int i = 0 ; i < 0 ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadoutTarget(cell));
+    }
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChipTarget(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME_TARGET, 0, NULL, MISC_NUM_TARGET);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME_TARGET, 0, NULL, MISC_NUM_TARGET);
+    chip->hdu = pmHDUAlloc(NULL);
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCellTarget(chip));
+    }
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPATarget(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME_TARGET, 0, NULL, MISC_NUM_TARGET);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME_TARGET, 0, NULL, MISC_NUM_TARGET);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME_TARGET, 0, NULL, MISC_NUM_TARGET);
+    fpa->hdu = pmHDUAlloc(NULL);
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChipTarget(fpa));
+    }
+
+    return(fpa);
+}
+
+bool testCellCopy(pmCell* cellTarget, pmCell *cellSource) {
+    bool errorFlag = false;
+    for (int readoutID = 0 ; readoutID < cellTarget->readouts->n ; readoutID++) {
+        pmReadout *readoutTarget = cellTarget->readouts->data[readoutID];
+        pmReadout *readoutSource = cellSource->readouts->data[readoutID];
+
+        psImage *imageTarget = readoutTarget->image;
+        psImage *imageSource = readoutSource->image;
+        for (int i = 0 ; i < SOURCE_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < SOURCE_NUM_COLS ; j++) {
+                if (imageSource->data.F32[i][j] != imageTarget->data.F32[i][j]) {
+                    diag("ERROR: target readout[%d] image[%d][%d] is %.2f, should be %.2f",
+                          readoutID, i, j, imageTarget->data.F32[i][j], imageSource->data.F32[i][j]);
+                    errorFlag = true;
+		}
+	    }
+	}
+        if (errorFlag) {
+            diag("ERROR: pmCellCopy() did not set the data for readout %d, image correctly", readoutID);
+	}
+
+        psImage *maskTarget = readoutTarget->mask;
+        psImage *maskSource = readoutSource->mask;
+        for (int i = 0 ; i < SOURCE_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < SOURCE_NUM_COLS ; j++) {
+                if (maskTarget->data.U8[i][j] != maskSource->data.U8[i][j]) {
+                    diag("ERROR: target readout[%d] mask[%d][%d] is %d, should be %d",
+                          readoutID, i, j, maskTarget->data.U8[i][j], maskSource->data.U8[i][j]);
+                    errorFlag = true;
+		}
+	    }
+	}
+        if (errorFlag) {
+            diag("ERROR: pmCellCopy() did not set the data for readout %d, mask correctly", readoutID);
+	}
+
+        psImage *weightTarget = readoutTarget->weight;
+        psImage *weightSource = readoutSource->weight;
+        for (int i = 0 ; i < SOURCE_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < SOURCE_NUM_COLS ; j++) {
+                if (weightTarget->data.F32[i][j] != weightSource->data.F32[i][j]) {
+                    diag("ERROR: target readout[%d] weight[%d][%d] is %.2f, should be %.2f",
+                          readoutID, i, j, weightTarget->data.F32[i][j], weightSource->data.F32[i][j]);
+                    errorFlag = true;
+		}
+	    }
+	}
+        if (errorFlag) {
+            diag("ERROR: pmCellCopy() did not set the data for readout %d, weight correctly", readoutID);
+	}
+    }
+    return(errorFlag);
+}
+
+
+bool testChipCopy(pmChip* chipTarget, pmChip *chipSource) {
+    bool errorFlag = false;
+    for (int cellID = 0 ; cellID < chipSource->cells->n ; cellID++) {
+        pmCell *cellTarget = chipTarget->cells->data[cellID];
+        pmCell *cellSource = chipSource->cells->data[cellID];
+        errorFlag|= testCellCopy(cellTarget, cellSource);
+    }
+    return(errorFlag);
+}
+
+bool testFPACopy(pmFPA* fpaTarget, pmFPA *fpaSource) {
+    bool errorFlag = false;
+    for (int chipID = 0 ; chipID < fpaSource->chips->n ; chipID++) {
+        pmChip *chipTarget = fpaTarget->chips->data[chipID];
+        pmChip *chipSource = fpaSource->chips->data[chipID];
+        errorFlag|= testChipCopy(chipTarget, chipSource);
+    }
+    return(errorFlag);
+}
+
+
+bool testCellCopyStructure(pmCell* cellTarget, pmCell *cellSource) {
+    bool errorFlag = false;
+    for (int readoutID = 0 ; readoutID < cellTarget->readouts->n ; readoutID++) {
+        pmReadout *readoutTarget = cellTarget->readouts->data[readoutID];
+        if (readoutTarget->image->numRows != SOURCE_NUM_ROWS ||
+            readoutTarget->image->numCols != SOURCE_NUM_COLS) {
+            diag("ERROR: readoutTarget->image size is (%d by %d)\n", readoutTarget->image->numRows, 
+                  readoutTarget->image->numCols);
+            errorFlag = true;
+	}
+
+        if (readoutTarget->mask->numRows != SOURCE_NUM_ROWS ||
+            readoutTarget->mask->numCols != SOURCE_NUM_COLS) {
+            diag("ERROR: readoutTarget->mask size is (%d by %d)\n", readoutTarget->mask->numRows, 
+                  readoutTarget->mask->numCols);
+            errorFlag = true;
+	}
+        if (readoutTarget->weight->numRows != SOURCE_NUM_ROWS ||
+            readoutTarget->weight->numCols != SOURCE_NUM_COLS) {
+            diag("ERROR: readoutTarget->weight size is (%d by %d)\n", readoutTarget->weight->numRows, 
+                  readoutTarget->weight    ->numCols);
+            errorFlag = true;
+	}
+    }
+    return(errorFlag);
+}
+
+
+bool testChipCopyStructure(pmChip* chipTarget, pmChip *chipSource) {
+    bool errorFlag = false;
+    for (int cellID = 0 ; cellID < chipSource->cells->n ; cellID++) {
+        pmCell *cellTarget = chipTarget->cells->data[cellID];
+        pmCell *cellSource = chipSource->cells->data[cellID];
+        errorFlag|= testCellCopyStructure(cellTarget, cellSource);
+    }
+    return(errorFlag);
+}
+
+bool testFPACopyStructure(pmFPA* fpaTarget, pmFPA *fpaSource) {
+    bool errorFlag = false;
+    for (int chipID = 0 ; chipID < fpaSource->chips->n ; chipID++) {
+        pmChip *chipTarget = fpaTarget->chips->data[chipID];
+        pmChip *chipSource = fpaSource->chips->data[chipID];
+        errorFlag|= testChipCopyStructure(chipTarget, chipSource);
+    }
+    return(errorFlag);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(92);
+
+
+    // ------------------------------------------------------------------------
+    // pmFPACopy() tests
+    // Call pmFPACopy() with bad input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmFPA* fpaSource = generateSimpleFPASource(NULL);
+        pmFPA* fpaTarget = generateSimpleFPATarget(NULL);
+        bool rc = pmFPACopy(NULL, fpaSource);
+        ok(rc == FALSE, "pmFPACopy() returned FALSE with NULL target pmFPA input parameter");
+        rc = pmFPACopy(fpaTarget, NULL);
+        ok(rc == FALSE, "pmFPACopy() returned FALSE with NULL source pmFPA input parameter");
+        psFree(fpaSource);
+        psFree(fpaTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmFPACopy() with acceptable input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmFPA* fpaSource = generateSimpleFPASource(NULL);
+        pmFPA* fpaTarget = generateSimpleFPATarget(NULL);
+        bool rc = pmFPACopy(fpaTarget, fpaSource);
+        ok(rc == true, "pmFPACopy() returned TRUE with acceptable input parameters");
+        int tmpS32 = psMetadataLookupS32(&rc, fpaTarget->concepts, MISC_NAME_TARGET);
+        ok(rc, "psMetadataLookupStr(NULL, fpaTarget->concepts, MISC_NAME_TARGET) was successful");
+        ok(tmpS32 == MISC_NUM_TARGET, "pmFPACopy() copied the source FPA concepts correctly");
+
+        tmpS32 = psMetadataLookupS32(&rc, fpaTarget->concepts, MISC_NAME_SOURCE);
+        ok(rc, "psMetadataLookupStr(NULL, fpaTarget->concepts, MISC_NAME_SOURCE) was successful");
+        ok(tmpS32 == MISC_NUM_SOURCE, "pmFPACopy() copied the source FPA concepts correctly");
+        ok(!testFPACopy(fpaTarget, fpaSource), "pmFPACopy() set the pmReadout data correctly");
+
+        psFree(fpaSource);
+        psFree(fpaTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmChipCopy() tests
+    // Call pmChipCopy() with bad input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmChip* chipSource = generateSimpleChipSource(NULL);
+        pmChip* chipTarget = generateSimpleChipTarget(NULL);
+        bool rc = pmChipCopy(NULL, chipSource);
+        ok(rc == FALSE, "pmChipCopy() returned FALSE with NULL target pmChip input parameter");
+        rc = pmChipCopy(chipTarget, NULL);
+        ok(rc == FALSE, "pmChipCopy() returned FALSE with NULL source pmChip input parameter");
+        psFree(chipSource);
+        psFree(chipTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmChipCopy() with acceptable input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmFPA *fpaSource = generateSimpleFPASource(NULL);
+        pmFPA *fpaTarget = generateSimpleFPATarget(NULL);
+        pmChip* chipSource = generateSimpleChipSource(fpaSource);
+        pmChip* chipTarget = generateSimpleChipTarget(fpaTarget);
+        bool rc = pmChipCopy(chipTarget, chipSource);
+        ok(rc == true, "pmChipCopy() returned TRUE with acceptable input parameters");
+        ok(!testChipCopy(chipTarget, chipSource), "pmChipCopy() set the pmReadout data correctly");
+        psFree(chipSource);
+        psFree(chipTarget);
+        psFree(fpaSource);
+        psFree(fpaTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmCellCopy() tests
+    // Call pmCellCopy() with bad input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmCell* cellSource = generateSimpleCellSource(NULL);
+        pmCell* cellTarget = generateSimpleCellTarget(NULL);
+        bool rc = pmCellCopy(NULL, cellSource);
+        ok(rc == FALSE, "pmCellCopy() returned FALSE with NULL target pmCell input parameter");
+        rc = pmCellCopy(cellTarget, NULL);
+        ok(rc == FALSE, "pmCellCopy() returned FALSE with NULL source pmCell input parameter");
+        psFree(cellSource);
+        psFree(cellTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // Call pmCellCopy() with acceptable input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmChip *parentSource = generateSimpleChipSource(NULL);
+        pmChip *parentTarget = generateSimpleChipSource(NULL);
+        pmCell* cellSource = generateSimpleCellSource(parentSource);
+        pmCell* cellTarget = generateSimpleCellTarget(parentTarget);
+        bool rc = pmCellCopy(cellTarget, cellSource);
+        ok(rc == true, "pmCellCopy() returned TRUE with NULL target pmCell input parameter");
+        ok(!testCellCopy(cellTarget, cellSource), "pmChipCopy() set the pmReadout data correctly");
+        psFree(cellSource);
+        psFree(cellTarget);
+        psFree(parentSource);
+        psFree(parentTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmFPACopyStructure() tests
+    // bool pmFPACopyStructure(pmFPA *target, const pmFPA *source, int xBin, int yBin)
+    // Call pmFPACopyStructure() with bad input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmFPA* fpaSource = generateSimpleFPASource(NULL);
+        pmFPA* fpaTarget = generateSimpleFPATarget(NULL);
+        bool rc = pmFPACopyStructure(NULL, fpaSource, 1.0, 1.0);
+        ok(rc == FALSE, "pmFPACopyStructure() returned FALSE with NULL target pmFPA input parameter");
+        rc = pmFPACopyStructure(fpaTarget, NULL, 1.0, 1.0);
+        ok(rc == FALSE, "pmFPACopyStructure() returned FALSE with NULL source pmFPA input parameter");
+        rc = pmFPACopyStructure(fpaTarget, fpaSource, 0.0, 1.0);
+        ok(rc == FALSE, "pmFPACopyStructure() returned FALSE with non-positive xBin input parameter");
+        rc = pmFPACopyStructure(fpaTarget, fpaSource, 1.0, 0.0);
+        ok(rc == FALSE, "pmFPACopyStructure() returned FALSE with non-positive yBin input parameter");
+        psFree(fpaSource);
+        psFree(fpaTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmFPACopyStructure() with acceptable input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmFPA* fpaSource = generateSimpleFPASource(NULL);
+        pmFPA* fpaTarget = generateSimpleFPATarget(NULL);
+        bool rc = pmFPACopyStructure(fpaTarget, fpaSource, 1.0, 1.0);
+        ok(rc == true, "pmFPACopyStructure() returned TRUE with acceptable input parameters");
+        ok(!testFPACopyStructure(fpaTarget, fpaSource), "pmFPACopyStructure() set the pmReadout data correctly");
+        int tmpS32 = psMetadataLookupS32(&rc, fpaTarget->concepts, MISC_NAME_TARGET);
+        ok(tmpS32 == MISC_NUM_TARGET, "pmFPACopy() copied the source FPA concepts correctly");
+        psFree(fpaSource);
+        psFree(fpaTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmChipCopyStructure() tests
+    // bool pmChipCopyStructure(pmChip *target, const pmChip *source, int xBin, int yBin)
+    // Call pmChipCopyStructure() with bad input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmChip* chipSource = generateSimpleChipSource(NULL);
+        pmChip* chipTarget = generateSimpleChipTarget(NULL);
+        bool rc = pmChipCopyStructure(NULL, chipSource, 1.0, 1.0);
+        ok(rc == FALSE, "pmChipCopyStructure() returned FALSE with NULL target pmChip input parameter");
+        rc = pmChipCopyStructure(chipTarget, NULL, 1.0, 1.0);
+        ok(rc == FALSE, "pmChipCopyStructure() returned FALSE with NULL source pmChip input parameter");
+        rc = pmChipCopyStructure(chipTarget, chipSource, 0.0, 1.0);
+        ok(rc == FALSE, "pmChipCopyStructure() returned FALSE with non-positive xBin input parameter");
+        rc = pmChipCopyStructure(chipTarget, chipSource, 1.0, 0.0);
+        ok(rc == FALSE, "pmChipCopyStructure() returned FALSE with non-positive yBin input parameter");
+        psFree(chipSource);
+        psFree(chipTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmChipCopyStructure() with acceptable parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmFPA *fpaSource = generateSimpleFPASource(NULL);
+        pmFPA *fpaTarget = generateSimpleFPATarget(NULL);
+        pmChip* chipSource = generateSimpleChipSource(fpaSource);
+        pmChip* chipTarget = generateSimpleChipTarget(fpaTarget);
+        bool rc = pmChipCopyStructure(chipTarget, chipSource, 1.0, 1.0);
+        ok(rc == true, "pmChipCopyStructure() returned TRUE with acceptable input parameters");
+        ok(!testChipCopyStructure(chipTarget, chipSource), "pmChipCopyStructure() set the pmReadout data correctly");
+        psFree(chipSource);
+        psFree(chipTarget);
+        psFree(fpaSource);
+        psFree(fpaTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmCellCopyStructure() tests
+    // bool pmCellCopyStructure(pmCell *target, const pmCell *source, int xBin, int yBin)
+    // Call pmCellCopyStructure() with bad input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmCell* cellSource = generateSimpleCellSource(NULL);
+        pmCell* cellTarget = generateSimpleCellTarget(NULL);
+        bool rc = pmCellCopyStructure(NULL, cellSource, 1.0, 1.0);
+        ok(rc == FALSE, "pmCellCopyStructure() returned FALSE with NULL target pmCell input parameter");
+        rc = pmCellCopyStructure(cellTarget, NULL, 1.0, 1.0);
+        ok(rc == FALSE, "pmCellCopyStructure() returned FALSE with NULL source pmCell input parameter");
+        rc = pmCellCopyStructure(cellTarget, cellSource, 0.0, 1.0);
+        ok(rc == FALSE, "pmCellCopyStructure() returned FALSE with non-positive xBin input parameter");
+        rc = pmCellCopyStructure(cellTarget, cellSource, 1.0, 0.0);
+        ok(rc == FALSE, "pmCellCopyStructure() returned FALSE with non-positive yBin input parameter");
+        psFree(cellSource);
+        psFree(cellTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmCellCopyStructure() with acceptable input parameters.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmChip *parentSource = generateSimpleChipSource(NULL);
+        pmChip *parentTarget = generateSimpleChipSource(NULL);
+        pmCell* cellSource = generateSimpleCellSource(parentSource);
+        pmCell* cellTarget = generateSimpleCellTarget(parentTarget);
+        bool rc = pmCellCopyStructure(cellTarget, cellSource, 1.0, 1.0);
+        ok(rc == true, "pmCellCopyStructure() returned TRUE with NULL target pmCell input parameter");
+        ok(!testCellCopyStructure(cellTarget, cellSource), "pmCellCopyStructure() set the pmReadout data correctly");
+        psFree(cellSource);
+        psFree(cellTarget);
+        psFree(parentSource);
+        psFree(parentTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmChipDuplicate() tests
+    // pmChip *pmChipDuplicate(pmFPA *fpa, const pmChip *source);
+    // Call pmChipDuplicate() with bad input parameters.
+    if (0) {
+        psMemId id = psMemGetId();
+        pmChip *chipSource = generateSimpleChipSource(NULL);
+        pmChip *chipTarget = pmChipDuplicate(NULL, NULL);
+        ok(chipTarget == NULL, "pmChipDuplicate() returned NULL with NULL pmChip input parameter");
+        psFree(chipSource);
+        psFree(chipTarget);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAExtent.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAExtent.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAExtent.c	(revision 22322)
@@ -0,0 +1,425 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+*/
+
+// XXX: For the genSimpleFPA() code, add IDs to each function so that
+// the values set in each chip-?cell-?hdu-?image are unique
+// XXX: For the genSimpleFPA() code, write masks and weights as well
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    bool rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "../camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+        }
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+
+    // XXX: Add code to initialize chip pmConcepts
+
+
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(25);
+
+
+    // ----------------------------------------------------------------------
+    // pmReadoutExtent() tests: NULL input
+    {
+        psMemId id = psMemGetId();
+        ok(NULL == pmReadoutExtent(NULL), "pmReadoutExtent(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // pmReadoutExtent() tests: acceptable inputs
+    // XXX: We should probably test when the images are NULL, and use different size
+    // images in each readout.
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        bool errorFlag = false;
+        for (int chipID = 0 ; chipID < fpa->chips->n ; chipID++) {
+            pmChip *chip = fpa->chips->data[chipID];
+            for (int cellID = 0 ; cellID < chip->cells->n ; cellID++) {
+                pmCell *cell = chip->cells->data[cellID];
+                for (int readoutID = 0 ; readoutID < cell->readouts->n ; readoutID++) {
+                    pmReadout *readout = cell->readouts->data[readoutID];
+                    psRegion *region = pmReadoutExtent(readout);
+                    int xWindow = psMetadataLookupS32(NULL, readout->parent->concepts, "CELL.XWINDOW");
+                    int yWindow = psMetadataLookupS32(NULL, readout->parent->concepts, "CELL.YWINDOW");
+                    if (!region || 
+                         region->x0 != xWindow ||
+                         region->x1 != xWindow + readout->image->numCols ||
+                         region->y0 != yWindow ||
+                         region->y1 != yWindow + readout->image->numRows) {
+                        diag("ERROR: pmReadoutExtent() did not set the psRegion correctly for chip/cell/readout (%d/%d/%d)\n", chipID, cellID, readoutID);
+                        errorFlag = true;
+		    }
+                    psFree(region);
+		}
+	    }
+	}
+        ok(!errorFlag, "pmReadoutExtent() passed all tests");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmCellExtent() tests: NULL input
+    {
+        psMemId id = psMemGetId();
+        ok(NULL == pmCellExtent(NULL), "pmCellExtent(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmCellExtent() tests: acceptable inputs
+    // XX: We should probably set different region sizes to better test the min/max code
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        bool errorFlag = false;
+        for (int chipID = 0 ; chipID < fpa->chips->n ; chipID++) {
+            pmChip *chip = fpa->chips->data[chipID];
+            for (int cellID = 0 ; cellID < chip->cells->n ; cellID++) {
+                pmCell *cell = chip->cells->data[cellID];
+                // Determine the actual extent
+                psRegion *cellExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of cell
+                for (int readoutID = 0 ; readoutID < cell->readouts->n ; readoutID++) {
+                    pmReadout *readout = cell->readouts->data[readoutID];
+                    psRegion *roExtent = pmReadoutExtent(readout); // Extent of readout
+                    cellExtent->x0 = PS_MIN(cellExtent->x0, roExtent->x0);
+                    cellExtent->x1 = PS_MAX(cellExtent->x1, roExtent->x1);
+                    cellExtent->y0 = PS_MIN(cellExtent->y0, roExtent->y0);
+                    cellExtent->y1 = PS_MAX(cellExtent->y1, roExtent->y1);
+                    psFree(roExtent);
+		}
+                bool mdok;
+                int x0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0"); // Cell x offset
+                int y0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0"); // Cell y offset
+                cellExtent->x0 += x0;
+                cellExtent->x1 += x0;
+                cellExtent->y0 += y0;
+                cellExtent->y1 += y0;
+    
+                psRegion *tstExtent = pmCellExtent(cell);
+                if (!cell ||
+                     tstExtent->x0 != cellExtent->x0 ||
+                     tstExtent->x1 != cellExtent->x1 ||
+                     tstExtent->y0 != cellExtent->y0 ||
+                     tstExtent->y1 != cellExtent->y1) {
+                     diag("ERROR: psRegion set incorrectly for chip/cell (%d/%d)", chipID, cellID);
+                     errorFlag = true;
+		}
+                psFree(tstExtent);
+                psFree(cellExtent);
+	    }
+	}
+        ok(!errorFlag, "pmCellExtent() passed all tests");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ----------------------------------------------------------------------
+    // pmChipExtent() tests: NULL input
+    {
+        psMemId id = psMemGetId();
+        ok(NULL == pmChipExtent(NULL), "pmChipExtent(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmChipExtent() tests: acceptable inputs
+    // XX: We should probably set different region sizes to better test the min/max code
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        bool errorFlag = false;
+        for (int chipID = 0 ; chipID < fpa->chips->n ; chipID++) {
+            pmChip *chip = fpa->chips->data[chipID];
+            psRegion *chipExtent = pmChipPixels(chip);
+            bool rc;
+            int x0 = psMetadataLookupS32(&rc, chip->concepts, "CHIP.X0"); // Chip x offset
+            int y0 = psMetadataLookupS32(&rc, chip->concepts, "CHIP.Y0"); // Chip y offset
+            chipExtent->x0 += x0;
+            chipExtent->x1 += x0;
+            chipExtent->y0 += y0;
+            chipExtent->y1 += y0;
+            psRegion *tstExtent = pmChipExtent(chip);
+            if (!chip ||
+                tstExtent->x0 != chipExtent->x0 ||
+                tstExtent->x1 != chipExtent->x1 ||
+                tstExtent->y0 != chipExtent->y0 ||
+                tstExtent->y1 != chipExtent->y1) {
+                diag("ERROR: psRegion set incorrectly for chip (%d)", chipID);
+                errorFlag = true;
+	    }
+            psFree(tstExtent);
+            psFree(chipExtent);
+	}
+        ok(!errorFlag, "pmChipExtent() passed all tests");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmChipPixels() tests: NULL input
+    {
+        psMemId id = psMemGetId();
+        ok(NULL == pmChipPixels(NULL), "pmChipPixels(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmChipPixels() tests: acceptable inputs
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+
+        bool errorFlag = false;
+        for (int chipID = 0 ; chipID < fpa->chips->n ; chipID++) {
+            pmChip *chip = fpa->chips->data[chipID];
+            // Determine the actual pixels
+            psRegion *actualExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0);
+            for (int cellID = 0 ; cellID < chip->cells->n ; cellID++) {
+                pmCell *cell = chip->cells->data[cellID];
+                psRegion *cellExtent = pmCellExtent(cell);
+                actualExtent->x0 = PS_MIN(actualExtent->x0, cellExtent->x0);
+                actualExtent->x1 = PS_MAX(actualExtent->x1, cellExtent->x1);
+                actualExtent->y0 = PS_MIN(actualExtent->y0, cellExtent->y0);
+                actualExtent->y1 = PS_MAX(actualExtent->y1, cellExtent->y1);
+                psFree(cellExtent);
+	    }
+
+            // Now test if pmChipPixels() determines the same pixels
+            psRegion *tstExtent = pmChipPixels(chip);
+            if (!tstExtent ||
+                 tstExtent->x0 != actualExtent->x0 ||
+                 tstExtent->x1 != actualExtent->x1 ||
+                 tstExtent->y0 != actualExtent->y0 ||
+                 tstExtent->y1 != actualExtent->y1) {
+                diag("ERROR: pixels set incorrectly for chip %d", chipID);
+                errorFlag = true;
+	    }
+
+            // Free temp memory
+            psFree(tstExtent);
+            psFree(actualExtent);
+	}
+        ok(!errorFlag, "pmChipPixels() passed all tests");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAPixels() tests: NULL input
+    {
+        psMemId id = psMemGetId();
+        ok(NULL == pmFPAPixels(NULL), "pmFPAPixels(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAPixels() tests: acceptable inputs
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+
+        psRegion *actualExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of fpa
+        for (int chipID = 0 ; chipID < fpa->chips->n ; chipID++) {
+            pmChip *chip = fpa->chips->data[chipID];
+            psRegion *chipExtent = pmChipExtent(chip); // Extent of chip
+            actualExtent->x0 = PS_MIN(actualExtent->x0, chipExtent->x0);
+            actualExtent->x1 = PS_MAX(actualExtent->x1, chipExtent->x1);
+            actualExtent->y0 = PS_MIN(actualExtent->y0, chipExtent->y0);
+            actualExtent->y1 = PS_MAX(actualExtent->y1, chipExtent->y1);
+            psFree(chipExtent);
+        }
+
+        bool errorFlag = false;
+        psRegion *tstExtent = pmFPAPixels(fpa);
+        if (!tstExtent ||
+             tstExtent->x0 != actualExtent->x0 ||
+             tstExtent->x1 != actualExtent->x1 ||
+             tstExtent->y0 != actualExtent->y0 ||
+             tstExtent->y1 != actualExtent->y1) {
+            diag("ERROR: pmFPAPixels() set the pixels incorrectly");
+            errorFlag = true;
+	}
+        ok(!errorFlag, "pmFPAPixels() set the pixels psRegion correctly");
+
+        psFree(tstExtent);
+        psFree(actualExtent);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAFlags.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAFlags.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAFlags.c	(revision 22322)
@@ -0,0 +1,1021 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS
+    TESTED:
+        pmFPASetFileStatus()
+        pmChipSetFileStatus()
+        pmCellSetFileStatus()
+
+        pmFPACheckFileStatus()
+        pmChipCheckFileStatus()
+        pmCellCheckFileStatus()
+
+        pmFPASetDataStatus()
+        pmChipSetDataStatus()
+        pmCellSetDataStatus()
+
+        pmFPACheckDataStatus()
+        pmChipCheckDataStatus()
+        pmCellCheckDataStatus()
+        pmReadoutCheckDataStatus()
+    MUST TEST:
+        pmFPAviewCheckDataStatus()
+        pmFPASelectChip()
+        pmChipSelectCell()
+        pmFPAExcludeChip()
+        pmChipExcludeCell()
+*/
+
+// XXX: For the genSimpleFPA() code, add IDs to each function so that
+// the values set in each chip-?cell-?hdu-?image are unique
+// XXX: For the genSimpleFPA() code, write masks and weights as well
+// XXX: Add in the associated CheckStatus tests
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    bool rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+        }
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+
+    // XXX: Add code to initialize chip pmConcepts
+
+
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+
+    // XXX: Eventually, when you finish the pmConcepts tests, add full concept
+    // reading code from wherever.
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+void SetCellFileExists(pmCell *cell) {
+    cell->file_exists = true;
+    for (int i = 0 ; i < cell->readouts->n ; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        readout->file_exists = true;
+    }
+}
+
+void SetChipFileExists(pmChip *chip) {
+    chip->file_exists = true;
+    for (int i = 0 ; i < chip->cells->n ; i++) {
+        pmCell *cell = chip->cells->data[i];
+        cell->file_exists = true;
+        SetCellFileExists(cell);
+    }
+}
+
+void SetFPAFileExists(pmFPA *fpa) {
+    for (int i = 0 ; i < fpa->chips->n ; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        chip->file_exists = true;
+        SetChipFileExists(chip);
+    }
+}
+
+void SetReadoutDataExists(pmReadout *readout) {
+    readout->data_exists = true;
+}
+
+void SetCellDataExists(pmCell *cell) {
+    cell->data_exists = true;
+    for (int i = 0 ; i < cell->readouts->n ; i++) {
+        pmReadout *readout = cell->readouts->data[i];
+        readout->data_exists = true;
+    }
+}
+
+void SetChipDataExists(pmChip *chip) {
+    chip->data_exists = true;
+    for (int i = 0 ; i < chip->cells->n ; i++) {
+        pmCell *cell = chip->cells->data[i];
+        cell->data_exists = true;
+        SetCellDataExists(cell);
+    }
+}
+
+void SetFPADataExists(pmFPA *fpa) {
+    for (int i = 0 ; i < fpa->chips->n ; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        chip->data_exists = true;
+        SetChipDataExists(chip);
+    }
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(107);
+
+
+    // ----------------------------------------------------------------------
+    // pmFPASetFileStatus() tests: verify with NULL pmFPA param
+    // bool pmFPASetFileStatus(pmFPA *fpa, bool status)
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmFPASetFileStatus(NULL, false);
+        ok(!rc, "pmFPASetFileStatus() returned FALSE with NULL pmFPA param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPASetFileStatus() tests: verify with acceptable data
+    // bool pmFPASetFileStatus(pmFPA *fpa, bool status)
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        // First, set all flags to FALSE
+        bool correctStatus = false;
+        bool rc = pmFPASetFileStatus(fpa, correctStatus);
+        ok(rc, "pmFPASetFileStatus() returned successfully with acceptable input params");
+        bool errorFlag = false;
+        for (int k = 0 ; k < fpa->chips->n ; k++) {
+            pmChip *chip = fpa->chips->data[k];
+            for (int j = 0 ; j < chip->cells->n ; j++) {
+                pmCell *cell = chip->cells->data[j];
+                if (cell->file_exists != correctStatus) {
+                    diag("TEST ERROR: pmFPASetFileStatus() failed to set file status for chip %d cell %d\n", k, j);
+                    errorFlag = true;
+                }
+    
+                for (int i = 0; i < cell->readouts->n; i++) {
+                    pmReadout *readout = cell->readouts->data[i];
+                    if (readout->file_exists != correctStatus) {
+                        diag("TEST ERROR: pmFPASetFileStatus() failed to set file status for chip %d cell %d readout %d\n", k, j, i);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+        ok(!errorFlag, "pmFPASetFileStatus() set file status in all cells to FALSE");
+
+        // Second, set all flags to TRUE
+        correctStatus = true;
+        rc = pmFPASetFileStatus(fpa, correctStatus);
+        ok(rc, "pmFPASetFileStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        for (int k = 0 ; k < fpa->chips->n ; k++) {
+            pmChip *chip = fpa->chips->data[k];
+            for (int j = 0 ; j < chip->cells->n ; j++) {
+                pmCell *cell = chip->cells->data[j];
+                if (cell->file_exists != correctStatus) {
+                    diag("TEST ERROR: pmFPASetFileStatus() failed to set file status for chip %d cell %d\n", k, j);
+                    errorFlag = true;
+                }
+    
+                for (int i = 0; i < cell->readouts->n; i++) {
+                    pmReadout *readout = cell->readouts->data[i];
+                    if (readout->file_exists != correctStatus) {
+                        diag("TEST ERROR: pmFPASetFileStatus() failed to set file status for chip %d cell %d readout %d\n", k, j, i);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+        ok(!errorFlag, "pmFPASetFileStatus() set file status in all cells to TRUE");
+
+        // Third, set all flags to FALSE
+        correctStatus = false;
+        rc = pmFPASetFileStatus(fpa, correctStatus);
+        ok(rc, "pmFPASetFileStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        for (int k = 0 ; k < fpa->chips->n ; k++) {
+            pmChip *chip = fpa->chips->data[k];
+            for (int j = 0 ; j < chip->cells->n ; j++) {
+                pmCell *cell = chip->cells->data[j];
+                if (cell->file_exists != correctStatus) {
+                    diag("TEST ERROR: pmFPASetFileStatus() failed to set file status for chip %d cell %d\n", k, j);
+                    errorFlag = true;
+                }
+    
+                for (int i = 0; i < cell->readouts->n; i++) {
+                    pmReadout *readout = cell->readouts->data[i];
+                    if (readout->file_exists != correctStatus) {
+                        diag("TEST ERROR: pmFPASetFileStatus() failed to set file status for chip %d cell %d readout %d\n", k, j, i);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+        ok(!errorFlag, "pmFPASetFileStatus() set file status in all cells to FALSE");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmChipSetFileStatus() tests: verify with NULL pmChip param
+    // bool pmChipSetFileStatus(pmChip *chip, bool status)
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmChipSetFileStatus(NULL, false);
+        ok(!rc, "pmChipSetFileStatus() returned FALSE with NULL pmChip param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmChipSetFileStatus() tests: verify with acceptable data
+    // bool pmChipSetFileStatus(pmChip *chip, bool status)
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        // First, set all flags to FALSE
+        bool correctStatus = false;
+        bool rc = pmChipSetFileStatus(chip, correctStatus);
+        ok(rc, "pmChipSetFileStatus() returned successfully with acceptable input params");
+        bool errorFlag = false;
+        if (chip->file_exists != correctStatus) {
+            diag("TEST ERROR: pmChipSetFileStatus() failed to set file status for chip param");
+            errorFlag = true;
+        }
+        for (int j = 0 ; j < chip->cells->n ; j++) {
+            pmCell *cell = chip->cells->data[j];
+            if (cell->file_exists != correctStatus) {
+                diag("TEST ERROR: pmChipSetFileStatus() failed to set file status for cell %d\n", j);
+                errorFlag = true;
+            }
+
+            for (int i = 0; i < cell->readouts->n; i++) {
+                pmReadout *readout = cell->readouts->data[i];
+                if (readout->file_exists != correctStatus) {
+                    diag("TEST ERROR: pmChipSetFileStatus() failed to set file status for cell %d readout %d\n", j, i);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmChipSetFileStatus() set file status in all cells to FALSE");
+
+        // Second, set all flags to TRUE
+        correctStatus = true;
+        rc = pmChipSetFileStatus(chip, correctStatus);
+        ok(rc, "pmChipSetFileStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        if (chip->file_exists != correctStatus) {
+            diag("TEST ERROR: pmChipSetFileStatus() failed to set file status for chip param");
+            errorFlag = true;
+        }
+        for (int j = 0 ; j < chip->cells->n ; j++) {
+            pmCell *cell = chip->cells->data[j];
+            if (cell->file_exists != correctStatus) {
+                diag("TEST ERROR: pmChipSetFileStatus() failed to set file status for cell %d\n", j);
+                errorFlag = true;
+            }
+
+            for (int i = 0; i < cell->readouts->n; i++) {
+                pmReadout *readout = cell->readouts->data[i];
+                if (readout->file_exists != correctStatus) {
+                    diag("TEST ERROR: pmChipSetFileStatus() failed to set file status for cell %d readout %d\n", j, i);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmChipSetFileStatus() set file status in all cells to TRUE");
+
+        // Third, set all flags to FALSE
+        correctStatus = false;
+        rc = pmChipSetFileStatus(chip, correctStatus);
+        ok(rc, "pmChipSetFileStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        if (chip->file_exists != correctStatus) {
+            diag("TEST ERROR: pmChipSetFileStatus() failed to set file status for chip param");
+            errorFlag = true;
+        }
+        for (int j = 0 ; j < chip->cells->n ; j++) {
+            pmCell *cell = chip->cells->data[j];
+            if (cell->file_exists != correctStatus) {
+                diag("TEST ERROR: pmChipSetFileStatus() failed to set file status for cell %d\n", j);
+                errorFlag = true;
+            }
+
+            for (int i = 0; i < cell->readouts->n; i++) {
+                pmReadout *readout = cell->readouts->data[i];
+                if (readout->file_exists != correctStatus) {
+                    diag("TEST ERROR: pmChipSetFileStatus() failed to set file status for cell %d readout %d\n", j, i);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmChipSetFileStatus() set file status in all cells to FALSE");
+
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmCellSetFileStatus() tests: verify with NULL pmCell param
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmCellSetFileStatus(NULL, false);
+        ok(!rc, "pmCellSetFileStatus() returned FALSE with NULL pmCell param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmCellSetFileStatus() tests: verify with acceptable data
+    // bool pmCellSetFileStatus(pmCell *cell, bool status)
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        // First, set all flags to FALSE
+        bool correctStatus = false;
+        bool rc = pmCellSetFileStatus(cell, correctStatus);
+        ok(rc, "pmCellSetFileStatus() returned successfully with acceptable input params");
+        bool errorFlag = false;
+        if (cell->file_exists != correctStatus) {
+            diag("TEST ERROR: pmCellSetFileStatus() failed to set file status for cell param\n");
+            errorFlag = true;
+        }
+        for (int i = 0; i < cell->readouts->n; i++) {
+
+            pmReadout *readout = cell->readouts->data[i];
+            if (readout->file_exists != correctStatus) {
+                diag("TEST ERROR: pmCellSetFileStatus() failed to set file status for cell %d\n", i);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "pmCellSetFileStatus() set file status in all cells to FALSE");
+
+        // Second, set all flags to TRUE
+        correctStatus = true;
+        rc = pmCellSetFileStatus(cell, correctStatus);
+        ok(rc, "pmCellSetFileStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        if (cell->file_exists != correctStatus) {
+            diag("TEST ERROR: pmCellSetFileStatus() failed to set file status for cell param\n");
+            errorFlag = true;
+        }
+        for (int i = 0; i < cell->readouts->n; i++) {
+            pmReadout *readout = cell->readouts->data[i];
+            if (readout->file_exists != correctStatus) {
+                diag("TEST ERROR: pmCellSetFileStatus() failed to set file status for cell %d\n", i);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "pmCellSetFileStatus() set file status in all cells to TRUE");
+
+        // Third, set all flags to FALSE
+        correctStatus = false;
+        rc = pmCellSetFileStatus(cell, correctStatus);
+        ok(rc, "pmCellSetFileStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        if (cell->file_exists != correctStatus) {
+            diag("TEST ERROR: pmCellSetFileStatus() failed to set file status for cell param\n");
+            errorFlag = true;
+        }
+        for (int i = 0; i < cell->readouts->n; i++) {
+            pmReadout *readout = cell->readouts->data[i];
+            if (readout->file_exists != correctStatus) {
+                diag("TEST ERROR: pmCellSetFileStatus() failed to set file status for readout %d\n", i);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "pmCellSetFileStatus() set file status in all cells to FALSE");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPACheckFileStatus() tests
+    // bool pmFPACheckFileStatus(const pmFPA *fpa)
+    // Call with NULL pmFPA input parameter
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmFPACheckFileStatus(NULL);
+        ok(rc == false, "pmFPACheckFileStatus() returned FALSE with NULL pmFPA input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmFPA *fpa = generateSimpleFPA(NULL);
+        bool rc = pmFPACheckFileStatus(fpa);
+        ok(rc == false, "pmFPACheckFileStatus() returned FALSE with NULL pmFPA input parameter");
+        SetFPAFileExists(fpa);
+        rc = pmFPACheckFileStatus(fpa);
+        ok(rc == true, "pmFPACheckFileStatus() returned TRUE with NULL pmFPA input parameter");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmChipCheckFileStatus() tests
+    // Call with NULL pmChip input parameter
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmChipCheckFileStatus(NULL);
+        ok(rc == false, "pmChipCheckFileStatus() returned FALSE with NULL pmChip input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call with acceptable input parameter
+    {
+        psMemId id = psMemGetId();
+        pmChip *chip = generateSimpleChip(NULL);
+        bool rc = pmChipCheckFileStatus(chip);
+        ok(rc == false, "pmChipCheckFileStatus() returned FALSE with NULL pmChip input parameter");
+        SetChipFileExists(chip);
+        rc = pmChipCheckFileStatus(chip);
+        ok(rc == true, "pmChipCheckFileStatus() returned TRUE with NULL pmChip input parameter");
+        psFree(chip);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmCellCheckFileStatus() tests
+    // bool pmCellCheckFileStatus(const pmCell *cell)
+    // Call with NULL pmCell input parameter
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmCellCheckFileStatus(NULL);
+        ok(rc == false, "pmCellCheckFileStatus() returned FALSE with NULL pmCell input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        bool rc = pmCellCheckFileStatus(cell);
+        ok(rc == false, "pmCellCheckFileStatus() returned FALSE with acceptable input parameters");
+        SetCellFileExists(cell);
+        rc = pmCellCheckFileStatus(cell);
+        ok(rc == true, "pmCellCheckFileStatus() returned TRUE with acceptable input parameters");
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPASetDataStatus() tests: verify with NULL pmFPA param
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmFPASetDataStatus(NULL, false);
+        ok(!rc, "pmFPASetDataStatus() returned FALSE with NULL pmFPA param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPASetDataStatus() tests: verify with acceptable data
+    // bool pmFPASetDataStatus(pmFPA *fpa, bool status)
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        // First, set all flags to FALSE
+        bool correctStatus = false;
+        bool rc = pmFPASetDataStatus(fpa, correctStatus);
+        ok(rc, "pmFPASetDataStatus() returned successfully with acceptable input params");
+        bool errorFlag = false;
+        for (int k = 0 ; k < fpa->chips->n ; k++) {
+            pmChip *chip = fpa->chips->data[k];
+            for (int j = 0 ; j < chip->cells->n ; j++) {
+                pmCell *cell = chip->cells->data[j];
+                if (cell->data_exists != correctStatus) {
+                    diag("TEST ERROR: pmFPASetDataStatus() failed to set file status for chip %d cell %d\n", k, j);
+                    errorFlag = true;
+                }
+    
+                for (int i = 0; i < cell->readouts->n; i++) {
+                    pmReadout *readout = cell->readouts->data[i];
+                    if (readout->data_exists != correctStatus) {
+                        diag("TEST ERROR: pmFPASetDataStatus() failed to set file status for chip %d cell %d readout %d\n", k, j, i);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+        ok(!errorFlag, "pmFPASetDataStatus() set file status in all cells to FALSE");
+
+        // Second, set all flags to TRUE
+        correctStatus = true;
+        rc = pmFPASetDataStatus(fpa, correctStatus);
+        ok(rc, "pmFPASetDataStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        for (int k = 0 ; k < fpa->chips->n ; k++) {
+            pmChip *chip = fpa->chips->data[k];
+            for (int j = 0 ; j < chip->cells->n ; j++) {
+                pmCell *cell = chip->cells->data[j];
+                if (cell->data_exists != correctStatus) {
+                    diag("TEST ERROR: pmFPASetDataStatus() failed to set file status for chip %d cell %d\n", k, j);
+                    errorFlag = true;
+                }
+    
+                for (int i = 0; i < cell->readouts->n; i++) {
+                    pmReadout *readout = cell->readouts->data[i];
+                    if (readout->data_exists != correctStatus) {
+                        diag("TEST ERROR: pmFPASetDataStatus() failed to set file status for chip %d cell %d readout %d\n", k, j, i);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+        ok(!errorFlag, "pmFPASetDataStatus() set file status in all cells to TRUE");
+
+        // Third, set all flags to FALSE
+        correctStatus = false;
+        rc = pmFPASetDataStatus(fpa, correctStatus);
+        ok(rc, "pmFPASetDataStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        for (int k = 0 ; k < fpa->chips->n ; k++) {
+            pmChip *chip = fpa->chips->data[k];
+            for (int j = 0 ; j < chip->cells->n ; j++) {
+                pmCell *cell = chip->cells->data[j];
+                if (cell->data_exists != correctStatus) {
+                    diag("TEST ERROR: pmFPASetDataStatus() failed to set file status for chip %d cell %d\n", k, j);
+                    errorFlag = true;
+                }
+    
+                for (int i = 0; i < cell->readouts->n; i++) {
+                    pmReadout *readout = cell->readouts->data[i];
+                    if (readout->data_exists != correctStatus) {
+                        diag("TEST ERROR: pmFPASetDataStatus() failed to set file status for chip %d cell %d readout %d\n", k, j, i);
+                        errorFlag = true;
+                    }
+                }
+            }
+        }
+        ok(!errorFlag, "pmFPASetDataStatus() set file status in all cells to FALSE");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmChipSetDataStatus() tests: verify with NULL pmChip param
+    // bool pmChipSetDataStatus(pmChip *chip, bool status)
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmChipSetDataStatus(NULL, false);
+        ok(!rc, "pmChipSetDataStatus() returned FALSE with NULL pmChip param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmChipSetDataStatus() tests: verify with acceptable data
+    // bool pmChipSetDataStatus(pmChip *chip, bool status)
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        // First, set all flags to FALSE
+        bool correctStatus = false;
+        bool rc = pmChipSetDataStatus(chip, correctStatus);
+        ok(rc, "pmChipSetDataStatus() returned successfully with acceptable input params");
+        bool errorFlag = false;
+        if (chip->data_exists != correctStatus) {
+            diag("TEST ERROR (a): pmChipSetDataStatus() failed to set file status for chip param\n");
+            errorFlag = true;
+        }
+        for (int j = 0 ; j < chip->cells->n ; j++) {
+            pmCell *cell = chip->cells->data[j];
+            if (cell->data_exists != correctStatus) {
+                diag("TEST ERROR (b): pmChipSetDataStatus() failed to set file status for cell %d\n", j);
+                errorFlag = true;
+            }
+
+            for (int i = 0; i < cell->readouts->n; i++) {
+                pmReadout *readout = cell->readouts->data[i];
+                if (readout->data_exists != correctStatus) {
+                    diag("TEST ERROR (c): pmChipSetDataStatus() failed to set file status for cell %d readout %d\n", j, i);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmChipSetDataStatus() set data status in all cells to FALSE");
+
+        // Second, set all flags to TRUE
+        correctStatus = true;
+        rc = pmChipSetDataStatus(chip, correctStatus);
+        ok(rc, "pmChipSetDataStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        if (chip->data_exists != correctStatus) {
+            diag("TEST ERROR (a): pmChipSetDataStatus() failed to set file status for chip param\n");
+            errorFlag = true;
+        }
+        for (int j = 0 ; j < chip->cells->n ; j++) {
+            pmCell *cell = chip->cells->data[j];
+            if (cell->data_exists != correctStatus) {
+                diag("TEST ERROR (b): pmChipSetDataStatus() failed to set file status for cell %d\n", j);
+                errorFlag = true;
+            }
+
+            for (int i = 0; i < cell->readouts->n; i++) {
+                pmReadout *readout = cell->readouts->data[i];
+                if (readout->data_exists != correctStatus) {
+                    diag("TEST ERROR (c): pmChipSetDataStatus() failed to set file status for cell %d readout %d\n", j, i);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmChipSetDataStatus() set data status in all cells to TRUE");
+
+        // ThirdSecond, set all flags to FALSE
+        correctStatus = false;
+        rc = pmChipSetDataStatus(chip, correctStatus);
+        ok(rc, "pmChipSetDataStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        if (chip->data_exists != correctStatus) {
+            diag("TEST ERROR (a): pmChipSetDataStatus() failed to set file status for chip param\n");
+            errorFlag = true;
+        }
+        for (int j = 0 ; j < chip->cells->n ; j++) {
+            pmCell *cell = chip->cells->data[j];
+            if (cell->data_exists != correctStatus) {
+                diag("TEST ERROR (b): pmChipSetDataStatus() failed to set file status for cell %d\n", j);
+                errorFlag = true;
+            }
+
+            for (int i = 0; i < cell->readouts->n; i++) {
+                pmReadout *readout = cell->readouts->data[i];
+                if (readout->data_exists != correctStatus) {
+                    diag("TEST ERROR (c): pmChipSetDataStatus() failed to set file status for cell %d readout %d\n", j, i);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmChipSetDataStatus() set data status in all cells to FALSE");
+
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmCellSetDataStatus() tests: verify with NULL pmCell param
+    // bool pmCellSetDataStatus(pmCell *cell, bool status)
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmCellSetDataStatus(NULL, false);
+        ok(!rc, "pmCellSetDataStatus() returned FALSE with NULL pmCell param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmCellSetDataStatus() tests: verify with acceptable data
+    // bool pmCellSetDataStatus(pmCell *cell, bool status)
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        // First, set all flags to FALSE
+        bool correctStatus = false;
+        bool rc = pmCellSetDataStatus(cell, correctStatus);
+        ok(rc, "pmCellSetDataStatus() returned successfully with acceptable input params");
+        bool errorFlag = false;
+        if (cell->data_exists != correctStatus) {
+            diag("TEST ERROR: pmCellSetDataStatus() failed to set file status for cell param\n");
+            errorFlag = true;
+        }
+        for (int i = 0; i < cell->readouts->n; i++) {
+            pmReadout *readout = cell->readouts->data[i];
+            if (readout->data_exists != correctStatus) {
+                diag("TEST ERROR: pmCellSetDataStatus() failed to set file status for cell %d\n", i);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "pmCellSetDataStatus() set data status in all cells to FALSE");
+
+        // Second, set all flags to TRUE
+        correctStatus = true;
+        rc = pmCellSetDataStatus(cell, correctStatus);
+        ok(rc, "pmCellSetDataStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        if (cell->data_exists != correctStatus) {
+            diag("TEST ERROR: pmCellSetDataStatus() failed to set file status for cell param\n");
+            errorFlag = true;
+        }
+        for (int i = 0; i < cell->readouts->n; i++) {
+            pmReadout *readout = cell->readouts->data[i];
+            if (readout->data_exists != correctStatus) {
+                diag("TEST ERROR: pmCellSetDataStatus() failed to set file status for cell %d\n", i);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "pmCellSetDataStatus() set data status in all cells to TRUE");
+
+        // Third, set all flags to FALSE
+        correctStatus = false;
+        rc = pmCellSetDataStatus(cell, correctStatus);
+        ok(rc, "pmCellSetDataStatus() returned successfully with acceptable input params");
+        errorFlag = false;
+        if (cell->data_exists != correctStatus) {
+            diag("TEST ERROR: pmCellSetDataStatus() failed to set file status for cell param\n");
+            errorFlag = true;
+        }
+        for (int i = 0; i < cell->readouts->n; i++) {
+            pmReadout *readout = cell->readouts->data[i];
+            if (readout->data_exists != correctStatus) {
+                diag("TEST ERROR: pmCellSetDataStatus() failed to set file status for readout %d\n", i);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "pmCellSetDataStatus() set data status in all cells to FALSE");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPACheckDataStatus() tests
+    // bool pmFPACheckDataStatus(const pmFPA *fpa)
+    // Call with NULL pmFPA input parameter
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmFPACheckDataStatus(NULL);
+        ok(rc == false, "pmFPACheckDataStatus() returned FALSE with NULL pmFPA input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmFPA *fpa = generateSimpleFPA(NULL);
+        bool rc = pmFPACheckDataStatus(fpa);
+        ok(rc == false, "pmFPACheckDataStatus() returned FALSE with NULL pmFPA input parameter");
+        SetFPADataExists(fpa);
+        rc = pmFPACheckDataStatus(fpa);
+        ok(rc == true, "pmFPACheckDataStatus() returned TRUE with NULL pmFPA input parameter");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmChipCheckDataStatus() tests
+    // Call with NULL pmChip input parameter
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmChipCheckDataStatus(NULL);
+        ok(rc == false, "pmChipCheckDataStatus() returned FALSE with NULL pmChip input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call with acceptable input parameter
+    {
+        psMemId id = psMemGetId();
+        pmChip *chip = generateSimpleChip(NULL);
+        bool rc = pmChipCheckDataStatus(chip);
+        ok(rc == false, "pmChipCheckDataStatus() returned FALSE with NULL pmChip input parameter");
+        SetChipDataExists(chip);
+        rc = pmChipCheckDataStatus(chip);
+        ok(rc == true, "pmChipCheckDataStatus() returned TRUE with NULL pmChip input parameter");
+        psFree(chip);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmCellCheckDataStatus() tests
+    // bool pmCellCheckDataStatus(const pmCell *cell)
+    // Call with NULL pmCell input parameter
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmCellCheckDataStatus(NULL);
+        ok(rc == false, "pmCellCheckDataStatus() returned FALSE with NULL pmCell input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        bool rc = pmCellCheckDataStatus(cell);
+        ok(rc == false, "pmCellCheckDataStatus() returned FALSE with acceptable input parameters");
+        SetCellDataExists(cell);
+        rc = pmCellCheckDataStatus(cell);
+        ok(rc == true, "pmCellCheckDataStatus() returned TRUE with acceptable input parameters");
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmReadoutCheckDataStatus() tests
+    // bool pmReadoutCheckDataStatus(const pmReadout *readout)
+    // Call with NULL pmReadout input parameter
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmReadoutCheckDataStatus(NULL);
+        ok(rc == false, "pmReadoutCheckDataStatus() returned FALSE with NULL pmReadout input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        bool rc = pmReadoutCheckDataStatus(readout);
+        ok(rc == false, "pmReadoutCheckDataStatus() returned FALSE with acceptable input parameters");
+        SetReadoutDataExists(readout);
+        rc = pmReadoutCheckDataStatus(readout);
+        ok(rc == true, "pmReadoutCheckDataStatus() returned TRUE with acceptable input parameters");
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAHeader.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAHeader.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAHeader.c	(revision 22322)
@@ -0,0 +1,449 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+*/
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+char *fitsFilename = "tmp.fits";
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip, int cellID)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    char extname[80];
+    snprintf(extname,80, "ext-%d", cellID);
+    cell->hdu = pmHDUAlloc(extname);
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa, int chipID)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    if (0) {
+        char extname[80];
+        snprintf(extname,80, "ext-%d", chipID);
+        chip->hdu = pmHDUAlloc(extname);
+    }
+
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip, i));
+    }
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa, i));
+    }
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(75);
+
+
+    // ----------------------------------------------------------------------
+    // pmCellReadHeader() tests: NULL input fits file
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(!pmCellReadHeader(cell, NULL), "pmCellReadHeader(cell, NULL) returned FALSE");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmCellReadHeader() tests: NULL input pmCell
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(fitsFilename, "w");
+        ok(fitsFileW != NULL, "psFitsOpen() opened the FITS file");
+        ok(!pmCellReadHeader(NULL, fitsFileW), "pmCellReadHeader(NULL, fitsFile) returned FALSE");
+        psFree(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmCellReadHeader() tests: acceptable data
+    {
+        psMemId id = psMemGetId();
+        // Create a FITS file for this test
+        psFits* fitsFileW = psFitsOpen(fitsFilename, "w");
+        ok(fitsFileW != NULL, "psFitsOpen() opened the FITS file");
+        char extname[80];
+        for (int lcv = 0; lcv < NUM_HDUS; lcv++) {
+            snprintf(extname, 80, "ext-%d", lcv);
+            pmHDU *hdu = pmHDUAlloc(extname);
+            hdu->header = psMetadataAlloc();
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYINT", PS_DATA_S32,
+                         "psS32 Item", (psS32)lcv);
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYFLT", PS_DATA_F32,
+                         "psF32 Item", (float)(1.0f/(float)(1+lcv)));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYDBL", PS_DATA_F64,
+                         "psF64 Item", (double)(1.0/(double)(1+lcv)));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYBOOL", PS_DATA_BOOL,
+                         "psBool Item", (lcv%2 == 0));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYSTR", PS_DATA_STRING,
+                         "String Item", extname);
+            bool rc = pmConfigFileRead(&hdu->format, "../dataFiles/camera0/format0.config", "Camera 0 Config Format");
+            if (!rc) {
+                rc = pmConfigFileRead(&hdu->format, "dataFiles/camera0/format0.config", "Camera 0 Config Format");
+	    }
+            ok(rc == true, "pmConfigFileRead() was successful");
+            rc = pmHDUWrite(hdu, fitsFileW);
+            ok(rc == true, "pmHDUWrite() successfully wrote the header");
+            psFree(hdu);
+        }
+        psFitsClose(fitsFileW);
+
+        // Now, open that FITS file, and create an pmFPA hierarchy
+        psFits* fitsFileR = psFitsOpen(fitsFilename, "r");
+        ok(fitsFileR != NULL, "psFitsOpen returned non-NULL on existing file");
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        ok(pmCellReadHeader(cell, fitsFileR), "pmCellReadHeader() returned TRUE with acceptable data");
+
+        // XXX: It's not clear if we should test if the HDU and pmConcepts actually
+        // get rid, since pmCellReadHeader() simply calls functions that are tested
+        // elsewhere.  However, if we should test it, test it here.
+
+        psFree(fpa);
+        psFree(camera);
+        psFree(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmChipReadHeader() tests: NULL input fits file
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(!pmChipReadHeader(chip, NULL), "pmChipReadHeader(chip, NULL) returned FALSE");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmChipReadHeader() tests: NULL input pmCell
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(fitsFilename, "w");
+        ok(fitsFileW != NULL, "psFitsOpen() opened the FITS file");
+        ok(!pmChipReadHeader(NULL, fitsFileW), "pmChipReadHeader(NULL, fitsFile) returned FALSE");
+        psFree(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmChipReadHeader() tests: acceptable data
+    {
+        psMemId id = psMemGetId();
+
+        // Create a FITS file for this test
+        psFits* fitsFileW = psFitsOpen(fitsFilename, "w");
+        ok(fitsFileW != NULL, "psFitsOpen() opened the FITS file");
+        char extname[80];
+        for (int lcv = 0; lcv < NUM_HDUS; lcv++) {
+            snprintf(extname, 80, "ext-%d", lcv);
+            pmHDU *hdu = pmHDUAlloc(extname);
+            hdu->header = psMetadataAlloc();
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYINT", PS_DATA_S32,
+                         "psS32 Item", (psS32)lcv);
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYFLT", PS_DATA_F32,
+                         "psF32 Item", (float)(1.0f/(float)(1+lcv)));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYDBL", PS_DATA_F64,
+                         "psF64 Item", (double)(1.0/(double)(1+lcv)));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYBOOL", PS_DATA_BOOL,
+                         "psBool Item", (lcv%2 == 0));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYSTR", PS_DATA_STRING,
+                         "String Item", extname);
+            bool rc = pmConfigFileRead(&hdu->format, "../dataFiles/camera0/format0.config", "Camera 0 Config Format");
+            if (!rc) {
+               rc = pmConfigFileRead(&hdu->format, "dataFiles/camera0/format0.config", "Camera 0 Config Format");
+	    }
+            ok(rc == true, "pmConfigFileRead() was successful");
+            rc = pmHDUWrite(hdu, fitsFileW);
+            ok(rc == true, "pmHDUWrite() successfully wrote the header");
+            psFree(hdu);
+        }
+        psFitsClose(fitsFileW);
+
+        // Now, open that FITS file, and create an pmFPA hierarchy
+        psFits* fitsFileR = psFitsOpen(fitsFilename, "r");
+        ok(fitsFileR != NULL, "psFitsOpen returned non-NULL on existing file");
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        // Set the correct extension names for the chips (if we put this in the
+        // generateChip() code, then psMOdules aborts.
+        for (int chipID = 0 ; chipID < fpa->chips->n ; chipID++) {
+            pmChip *chip = fpa->chips->data[chipID];
+            char extname[80];
+            snprintf(extname,80, "ext-%d", chipID);
+            chip->hdu = pmHDUAlloc(extname);
+	}
+
+        ok(pmChipReadHeader(chip, fitsFileR), "pmChipReadHeader() returned TRUE with acceptable data");
+
+
+        // XXX: It's not clear if we should test if the HDU and pmConcepts actually
+        // get rid, since pmCellReadHeader() simply calls functions that are tested
+        // elsewhere.  However, if we should test it, test it here.
+
+        psFree(fpa);
+        psFree(camera);
+        psFree(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAReadHeader() tests: NULL input fits file
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(!pmFPAReadHeader(fpa, NULL), "pmFPAReadHeader(fpa, NULL) returned FALSE");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAReadHeader() tests: NULL input pmCell
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(fitsFilename, "w");
+        ok(fitsFileW != NULL, "psFitsOpen() opened the FITS file");
+        ok(!pmFPAReadHeader(NULL, fitsFileW), "pmFPAReadHeader(NULL, fitsFile) returned FALSE");
+        psFree(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAReadHeader() tests: acceptable data
+    {
+        psMemId id = psMemGetId();
+
+        // Create a FITS file for this test
+        psFits* fitsFileW = psFitsOpen(fitsFilename, "w");
+        ok(fitsFileW != NULL, "psFitsOpen() opened the FITS file");
+        char extname[80];
+        for (int lcv = 0; lcv < NUM_HDUS; lcv++) {
+            snprintf(extname, 80, "ext-%d", lcv);
+            pmHDU *hdu = pmHDUAlloc(extname);
+            hdu->header = psMetadataAlloc();
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYINT", PS_DATA_S32,
+                         "psS32 Item", (psS32)lcv);
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYFLT", PS_DATA_F32,
+                         "psF32 Item", (float)(1.0f/(float)(1+lcv)));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYDBL", PS_DATA_F64,
+                         "psF64 Item", (double)(1.0/(double)(1+lcv)));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYBOOL", PS_DATA_BOOL,
+                         "psBool Item", (lcv%2 == 0));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYSTR", PS_DATA_STRING,
+                         "String Item", extname);
+            bool rc = pmConfigFileRead(&hdu->format, "../dataFiles/camera0/format0.config", "Camera 0 Config Format");
+            if (!rc) {
+                rc = pmConfigFileRead(&hdu->format, "dataFiles/camera0/format0.config", "Camera 0 Config Format");
+	    }
+            ok(rc == true, "pmConfigFileRead() was successful");
+            rc = pmHDUWrite(hdu, fitsFileW);
+            ok(rc == true, "pmHDUWrite() successfully wrote the header");
+            psFree(hdu);
+        }
+        psFitsClose(fitsFileW);
+
+        // Now, open that FITS file, and create an pmFPA hierarchy
+        psFits* fitsFileR = psFitsOpen(fitsFilename, "r");
+        ok(fitsFileR != NULL, "psFitsOpen returned non-NULL on existing file");
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        // Set the correct extension names for the chips (if we put this in the
+        // generateChip() code, then psMOdules aborts.
+        for (int chipID = 0 ; chipID < fpa->chips->n ; chipID++) {
+            pmChip *chip = fpa->chips->data[chipID];
+            char extname[80];
+            snprintf(extname,80, "ext-%d", chipID);
+            chip->hdu = pmHDUAlloc(extname);
+	}
+        snprintf(extname,80, "ext-%d", 0);
+        fpa->hdu = pmHDUAlloc(extname);
+
+        ok(pmFPAReadHeader(fpa, fitsFileR), "pmFPAReadHeader() returned TRUE with acceptable data");
+
+        // XXX: It's not clear if we should test if the HDU and pmConcepts actually
+        // get rid, since pmCellReadHeader() simply calls functions that are tested
+        // elsewhere.  However, if we should test it, test it here.
+
+        psFree(fpa);
+        psFree(camera);
+        psFree(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPALevel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPALevel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPALevel.c	(revision 22322)
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+    XXX: Add tests for bad input parameters.
+*/
+
+#define	ERR_TRACE_LEVEL		0
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(14);
+
+    // ----------------------------------------------------------------------
+    // pmFPALevelToName(): tests
+    // const char *pmFPALevelToName(pmFPALevel level)
+    {
+        psMemId id = psMemGetId();
+        char *str = (char *) pmFPALevelToName(PM_FPA_LEVEL_NONE);
+        ok(!strcmp(str, "NONE"), "pmFPALevelToName(PM_FPA_LEVEL_NONE)");
+
+        str = (char *) pmFPALevelToName(PM_FPA_LEVEL_FPA);
+        ok(!strcmp(str, "FPA"), "pmFPALevelToName(PM_FPA_LEVEL_FPA)");
+
+        str = (char *) pmFPALevelToName(PM_FPA_LEVEL_CHIP);
+        ok(!strcmp(str, "CHIP"), "pmFPALevelToName(PM_FPA_LEVEL_CHIP)");
+
+        str = (char *) pmFPALevelToName(PM_FPA_LEVEL_CELL);
+        ok(!strcmp(str, "CELL"), "pmFPALevelToName(PM_FPA_LEVEL_CELL)");
+
+        str = (char *) pmFPALevelToName(PM_FPA_LEVEL_READOUT);
+        ok(!strcmp(str, "READOUT"), "pmFPALevelToName(PM_FPA_LEVEL_READOUT)");
+
+        // XXX: We avoid this because pmFPALevelToName() aborts
+        if (0) {
+            str = (char *) pmFPALevelToName(-1);
+            ok(str == NULL, "pmFPALevelToName(-1)");
+	}
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPALevelFromName(): tests
+    {
+        psMemId id = psMemGetId();
+        pmFPALevel lev = pmFPALevelFromName("NONE");
+        ok(lev == PM_FPA_LEVEL_NONE, "pmFPALevelToName(NONE)");
+
+        lev = pmFPALevelFromName("FPA");
+        ok(lev == PM_FPA_LEVEL_FPA, "pmFPALevelToName(FPA)");
+
+        lev = pmFPALevelFromName("CHIP");
+        ok(lev == PM_FPA_LEVEL_CHIP, "pmFPALevelToName(CHIP)");
+
+        lev = pmFPALevelFromName("CELL");
+        ok(lev == PM_FPA_LEVEL_CELL, "pmFPALevelToName(CELL)");
+
+        lev = pmFPALevelFromName("READOUT");
+        ok(lev == PM_FPA_LEVEL_READOUT, "pmFPALevelToName(READOUT)");
+
+        lev = pmFPALevelFromName("BOGUS");
+        ok(lev == PM_FPA_LEVEL_NONE, "pmFPALevelToName(BOGUS)");
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAMaskW.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAMaskW.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAMaskW.c	(revision 22322)
@@ -0,0 +1,426 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+// XXX: Use better name for the temporary FITS file
+// XXX: For the genSimpleFPA() code, add IDs to each function so that
+// the values set in each chip-?cell-?hdu-?image are unique
+// XXX: For the genSimpleFPA() code, write masks and weights as well
+// XXX: Must add tests for pmReadoutGenerateWeight()
+// XXX: We don't test pmReadoutGenerateMaskWeight() and pmCellGenerateMaskWeight()
+// because they are simply calls to the above tested functions
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define SATURATION_LEVEL	10000.0
+#define BAD_LEVEL		100.0
+#define SATURATION_MASK		1
+#define BAD_MASK		2
+#define CELL_GAIN		1.0
+#define CELL_READNOISE		2.0
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (int i = 0 ; i < TEST_NUM_ROWS ; i++) {
+        for (int j = 0 ; j < TEST_NUM_COLS ; j++) {
+            readout->image->data.F32[i][j] = 32.0;
+            readout->mask->data.U8[i][j] = 0;
+            readout->weight->data.F32[i][j] = 1.0;
+	}
+    }
+
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(18);
+
+    // ----------------------------------------------------------------------
+    // pmReadoutSetMask() tests: NULL inputs
+    // bool pmReadoutSetMask(pmReadout *readout, psMaskType satMask, psMaskType badMask)
+    if (1) {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        pmReadout *readout = cell->readouts->data[0];
+        bool rc;
+
+        // Set readout == NULL, ensure pmReadoutSetMask() returnes FALSE, with no seg faults, memory leaks
+        rc = pmReadoutSetMask(NULL, SATURATION_MASK, BAD_MASK);
+        ok(!rc, "pmReadoutSetMask(NULL, SATURATION_MASK, BAD_MASK) returned FALSE with null pmReadout input");
+
+        // Set readout->image, ensure pmReadoutSetMask() returnes FALSE, with no seg faults, memory leaks
+        psImage *saveImg = readout->image;
+        readout->image = NULL;
+        rc = pmReadoutSetMask(readout, SATURATION_MASK, BAD_MASK);
+        ok(!rc, "pmReadoutSetMask(readout, SATURATION_MASK, BAD_MASK) returned FALSE with null pmReadout->image input");
+        readout->image = saveImg;
+
+        // Set pixels in the upper-left quadrant to values [10000:11000] range
+        // Set pixels in the upper-right quadrant to values [0:1000] range
+        // Set pixels in the lower-left quadrant to values [100000:1000000] range
+        // Set pixels in the lower-right quadrant to values [100000:1000000] range
+
+        for (int i = 0 ; i < readout->image->numRows ; i++) {
+            for (int j = 0 ; j < readout->image->numCols ; j++) {
+                if (i < readout->image->numRows/2) {
+                    if (j < readout->image->numCols/2) {
+                        readout->image->data.F32[i][j] = 10000.0 + (float) (i + j);
+		    } else {
+                        readout->image->data.F32[i][j] = (float) (i + j);
+		    }
+		} else {
+                    readout->image->data.F32[i][j] = 100000.0 + (float) (i + j);
+		}
+	    }
+	}
+
+        // Set the acceptable pixel range to [100.0 : 20000.0]
+        rc = psMetadataAddF32(readout->parent->concepts, PS_LIST_HEAD, "CELL.SATURATION", PS_META_REPLACE, NULL, 20000.0);
+        rc|= psMetadataAddF32(readout->parent->concepts, PS_LIST_HEAD, "CELL.BAD", PS_META_REPLACE, NULL, 100.0);
+        ok(rc, "Set pixel range in cell->concepts successfully");
+
+        // Call pmReadoutSetMask() and then verify that the mask data was set correctly
+        rc = pmReadoutSetMask(readout, SATURATION_MASK, BAD_MASK);
+        ok(rc, "pmReadoutSetMask(readout, SATURATION_MASK, BAD_MASK) returned TRUE with acceptable input data");
+        bool errorFlag = false;
+        for (int i = 0 ; i < readout->image->numRows ; i++) {
+            for (int j = 0 ; j < readout->image->numCols ; j++) {
+                if (i < readout->image->numRows/2) {
+                    if (j < readout->image->numCols/2) {
+                        if(readout->mask->data.U8[i][j] != 0) {
+                            if (VERBOSE) {
+                                diag("TEST ERROR: mask[%d][%d] is %d, should be 0\n",
+                                      i, j, readout->mask->data.U8[i][j]);
+			    }
+                            errorFlag = true;
+			}
+		    } else {
+                        if(readout->mask->data.U8[i][j] != BAD_MASK) {
+                            if (VERBOSE) {
+                                diag("TEST ERROR: mask[%d][%d] is %d, should be %d\n",
+                                      i, j, readout->mask->data.U8[i][j], BAD_MASK);
+			    }
+                            errorFlag = true;
+			}
+		    }
+		} else {
+                    if(readout->mask->data.U8[i][j] != SATURATION_MASK) {
+                        if (VERBOSE) {
+                            diag("TEST ERROR: mask[%d][%d] is %d, should be %d\n",
+                                  i, j, readout->mask->data.U8[i][j], SATURATION_MASK);
+                        }
+                        errorFlag = true;
+		    }
+		}
+	    }
+	}
+        ok(!errorFlag, "pmReadoutSetMask() set the mask values correctly");
+        psFree(fpa);    
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ----------------------------------------------------------------------
+    // pmReadoutGenerateMask() tests: NULL inputs
+    // bool pmReadoutGenerateMask(pmReadout *readout, psMaskType satMask, psMaskType badMask)
+    // XXX: This test is a duplicate of the above pmReadoutSetMask() test since the actual
+    // code is almost the same.  Must test with the readout->mask == NULL.
+    if (1) {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        pmReadout *readout = cell->readouts->data[0];
+        bool rc;
+
+        // Set readout == NULL, ensure pmReadoutGenerateMask() returnes FALSE, with no seg faults, memory leaks
+        rc = pmReadoutGenerateMask(NULL, SATURATION_MASK, BAD_MASK);
+        ok(!rc, "pmReadoutGenerateMask(NULL, SATURATION_MASK, BAD_MASK) returned FALSE with null pmReadout input");
+
+        // Set pixels in the upper-left quadrant to values [10000:11000] range
+        // Set pixels in the upper-right quadrant to values [0:1000] range
+        // Set pixels in the lower-left quadrant to values [100000:1000000] range
+        // Set pixels in the lower-right quadrant to values [100000:1000000] range
+
+        for (int i = 0 ; i < readout->image->numRows ; i++) {
+            for (int j = 0 ; j < readout->image->numCols ; j++) {
+                if (i < readout->image->numRows/2) {
+                    if (j < readout->image->numCols/2) {
+                        readout->image->data.F32[i][j] = 10000.0 + (float) (i + j);
+		    } else {
+                        readout->image->data.F32[i][j] = (float) (i + j);
+		    }
+		} else {
+                    readout->image->data.F32[i][j] = 100000.0 + (float) (i + j);
+		}
+	    }
+	}
+
+        // Set the acceptable pixel range to [100.0 : 20000.0]
+        rc = psMetadataAddF32(readout->parent->concepts, PS_LIST_HEAD, "CELL.SATURATION", PS_META_REPLACE, NULL, 20000.0);
+        rc|= psMetadataAddF32(readout->parent->concepts, PS_LIST_HEAD, "CELL.BAD", PS_META_REPLACE, NULL, 100.0);
+        ok(rc, "Set pixel range in cell->concepts successfully");
+
+        // Call pmReadoutGenerateMask() and then verify that the mask data was set correctly
+        rc = pmReadoutGenerateMask(readout, SATURATION_MASK, BAD_MASK);
+        ok(rc, "pmReadoutGenerateMask(readout, SATURATION_MASK, BAD_MASK) returned TRUE with acceptable input data");
+        bool errorFlag = false;
+        for (int i = 0 ; i < readout->image->numRows ; i++) {
+            for (int j = 0 ; j < readout->image->numCols ; j++) {
+                if (i < readout->image->numRows/2) {
+                    if (j < readout->image->numCols/2) {
+                        if(readout->mask->data.U8[i][j] != 0) {
+                            if (VERBOSE) {
+                                diag("TEST ERROR: mask[%d][%d] is %d, should be 0\n",
+                                      i, j, readout->mask->data.U8[i][j]);
+			    }
+                            errorFlag = true;
+			}
+		    } else {
+                        if(readout->mask->data.U8[i][j] != BAD_MASK) {
+                            if (VERBOSE) {
+                                diag("TEST ERROR: mask[%d][%d] is %d, should be %d\n",
+                                      i, j, readout->mask->data.U8[i][j], BAD_MASK);
+                                errorFlag = true;
+			    }
+			}
+		    }
+		} else {
+                    if(readout->mask->data.U8[i][j] != SATURATION_MASK) {
+                        if (VERBOSE) {
+                            diag("TEST ERROR: mask[%d][%d] is %d, should be %d\n",
+                                  i, j, readout->mask->data.U8[i][j], SATURATION_MASK);
+                        }
+                        errorFlag = true;
+		    }
+		}
+	    }
+	}
+        ok(!errorFlag, "pmReadoutGenerateMask() set the mask values correctly");
+
+        psFree(fpa);    
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ----------------------------------------------------------------------
+    // pmReadoutSetWeight() tests: NULL inputs
+    // bool pmReadoutSetWeight(pmReadout *readout, bool poisson)
+    if (1) {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        pmReadout *readout = cell->readouts->data[0];
+        bool rc;
+
+        // Set readout == NULL, ensure pmReadoutSetWeight() returnes FALSE, with no seg faults, memory leaks
+        rc = pmReadoutSetWeight(NULL, false);
+        ok(!rc, "pmReadoutSetWeight(NULL, false) returned FALSE with null pmReadout input");
+
+
+        // Set the acceptable pixel range to [100.0 : 20000.0]
+        rc = psMetadataAddF32(readout->parent->concepts, PS_LIST_HEAD, "CELL.GAIN", PS_META_REPLACE, NULL, CELL_GAIN);
+        rc|= psMetadataAddF32(readout->parent->concepts, PS_LIST_HEAD, "CELL.READNOISE", PS_META_REPLACE, NULL, CELL_READNOISE);
+        ok(rc, "Set GAIN and READNOISE in cell->concepts successfully");
+
+        // Call pmReadoutSetWeight() and then verify that the mask data was set correctly
+        rc = pmReadoutSetWeight(readout, false);
+        ok(rc, "pmReadoutSetWeight(readout, false) returned TRUE with acceptable input data");
+        bool errorFlag = false;
+        for (int i = 0 ; i < readout->weight->numRows ; i++) {
+            for (int j = 0 ; j < readout->weight->numCols ; j++) {
+                psF32 exp = CELL_READNOISE * CELL_READNOISE / CELL_GAIN / CELL_GAIN;
+                if(abs(readout->weight->data.F32[i][j] - exp) > 1e-4) {
+                    if (VERBOSE) {
+                        diag("TEST ERROR: weight[%d][%d] is %.2f, should be %.2f\n",
+                              i, j, readout->weight->data.F32[i][j], exp);
+		    }
+                    errorFlag = true;
+		}
+	    }
+	}
+        ok(!errorFlag, "pmReadoutSetWeight() set the weight values correctly (non-Poisson)");
+
+        for (int i = 0 ; i < readout->image->numRows ; i++) {
+            for (int j = 0 ; j < readout->image->numCols ; j++) {
+               readout->image->data.F32[i][j] = 100.0 + (float) (i + j);
+	    }
+	}
+        // Call pmReadoutSetWeight() and then verify that the mask data was set correctly
+        rc = pmReadoutSetWeight(readout, true);
+        ok(rc, "pmReadoutSetWeight(readout, true) returned TRUE with acceptable input data");
+        errorFlag = false;
+        for (int i = 0 ; i < readout->weight->numRows ; i++) {
+            for (int j = 0 ; j < readout->weight->numCols ; j++) {
+                psF32 exp = abs(readout->image->data.F32[i][j] / CELL_GAIN); 
+                if (exp < 1.0) exp = 1.0;
+                exp+= CELL_READNOISE * CELL_READNOISE / CELL_GAIN / CELL_GAIN;
+                if(abs(readout->weight->data.F32[i][j] - exp) > 1e-4) {
+                    if (VERBOSE) {
+                        diag("TEST ERROR: weight[%d][%d] is %.2f, should be %.2f\n",
+                              i, j, readout->weight->data.F32[i][j], exp);
+		    }
+                    errorFlag = true;
+		}
+	    }
+	}
+
+        ok(!errorFlag, "pmReadoutSetWeight() set the weight values correctly (Poisson)");
+        psFree(fpa);    
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAReadWrite.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAReadWrite.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAReadWrite.c	(revision 22322)
@@ -0,0 +1,1218 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+// XXX: Use better name for the temporary FITS file
+// XXX: The code to generate and free the FPA hierarchy was copied from
+// tap-pmFPA.c.  EIther include it directly, or library, or something.
+// Also, get rid of the manual free functions and use psFree() once
+// it correctly frees child members
+// XXX: For the genSimpleFPA() code, add IDs to each function so that
+// the values set in each chip-?cell-?hdu-?image are unique
+
+#define CHIP_ALLOC_NAME		"ChipName"
+#define CELL_ALLOC_NAME		"CellName"
+#define MISC_NUM		32
+#define MISC_NAME		"META00"
+#define MISC_NAME2		"META01"
+#define NUM_BIAS_DATA		10
+#define TEST_NUM_ROWS		4
+#define TEST_NUM_COLS		4
+#define NUM_READOUTS		3
+#define NUM_CELLS		10
+#define NUM_CHIPS		8
+#define NUM_HDUS		5
+#define BASE_IMAGE		10
+#define BASE_MASK		40
+#define BASE_WEIGHT		70
+#define VERBOSE			0
+#define ERR_TRACE_LEVEL		10
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+
+    // XXX: Add code to initialize chip pmConcepts
+
+
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(22);
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+    // pmCellWrite(): tests
+    // Verify pmCellWrite() with NULL pmCell arg
+    if (0) {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp00", "w");
+        ok(!pmCellWrite(NULL, fitsFileW, NULL, false), "pmCellWrite() returned FALSE with NULL pmCell input");
+        psFitsClose(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellWrite() with NULL pmCell arg
+    // XXXX: Big problem: Without the next code, everything else fails.  Why?
+    if (0) {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp00", "w");
+        ok(!pmCellWrite(NULL, fitsFileW, NULL, false), "pmCellWrite() returned FALSE with NULL pmCell input");
+        psFitsClose(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellWrite() with NULL pmFits arg
+    if (0) {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(!pmCellWrite(cell, NULL, NULL, false), "pmCellWrite() returned FALSE with NULL psFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellWrite() with acceptable input params
+    // We first write a FITS file with the pmCellWrite(), then we read it and verify.
+    // First call pmCellWrite()
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp00", "w");
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        //  Use pmCellWrite() to write image data to the FITS file
+        bool rc = pmCellWrite(cell, fitsFileW, NULL, false);
+        ok(rc, "pmCellWrite() returned TRUE");
+
+        //  Close the FITS file, free memory
+        psFitsClose(fitsFileW);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // ----------------------------------------------------------------------
+    // pmCellRead() tests 
+    // Verify pmCellRead() with NULL pmCell param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(!pmCellRead(NULL, fitsFileR, NULL), "pmCellRead() returned FALSE with NULL pmCell param");
+        psFitsClose(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellRead() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(!pmCellRead(cell, NULL, NULL), "pmCellRead() returned FALSE with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellRead() with acceptable data (using the FITS file created above)
+    {
+        psMemId id = psMemGetId();
+        bool rc;
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        // Free the existing cell hdu image data (so we can verify that pmCellRead() actually reads the data
+        psFree(cell->hdu->images);
+        cell->hdu->images = NULL;
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+
+        rc = pmCellRead(cell, fitsFileR, NULL);
+        ok(rc, "pmCellRead() returned TRUE");
+        skip_start(!rc, 1, "Skipping tests because pmCellRead returned NULL");
+        for (int k = 0 ; k < cell->hdu->images->n ; k++) {
+            bool errorFlag = false;
+            psImage *img = cell->hdu->images->data[k];
+            for (int i = 0 ; i < img->numRows ; i++) {
+                for (int j = 0 ; j < img->numCols ; j++) {
+                    if (((float) (BASE_IMAGE+k)) != img->data.F32[i][j]) {
+                        diag("TEST ERROR: img[%d][%d] is %.2f, should be %.2f\n", i, j,
+                              img->data.F32[i][j], ((float) (BASE_IMAGE+k)));
+                        errorFlag = true;
+		    }
+		}
+	    }
+            ok(!errorFlag, "pmCellWrite()/pmCellRead() properly set the image data (image %d)", k);
+        }
+        skip_end();
+        psFitsClose(fitsFileR);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+    // pmCellWriteWeight(): tests
+    // Verify pmCellWriteWeight() with NULL pmCell arg
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp00", "w");
+        ok(!pmCellWriteWeight(NULL, fitsFileW, NULL, false), "pmCellWriteWeight() returned FALSE with NULL pmCell input");
+        psFitsClose(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellWriteWeight() with NULL pmFits arg
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(!pmCellWriteWeight(cell, NULL, NULL, false), "pmCellWriteWeight() returned FALSE with NULL psFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellWriteWeight() with acceptable input params
+    // We first write a FITS file with the pmCellWriteWeight(), then we read it and verify.
+    // First call pmCellWriteWeight()
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp00", "w");
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        //  Use pmCellWriteWeight() to write weight data to the FITS file
+        bool rc = pmCellWriteWeight(cell, fitsFileW, NULL, false);
+        ok(rc, "pmCellWriteWeight() returned TRUE");
+
+        //  Close the FITS file, free memory
+        psFitsClose(fitsFileW);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmCellReadWeight() tests 
+    // Verify pmCellReadWeight() with NULL pmCell param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(!pmCellReadWeight(NULL, fitsFileR, NULL), "pmCellReadWeight() returned FALSE with NULL pmCell param");
+        psFitsClose(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellReadWeight() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(!pmCellReadWeight(cell, NULL, NULL), "pmCellReadWeight() returned FALSE with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellReadWeight() with acceptable data (using the FITS file created above)
+    {
+        psMemId id = psMemGetId();
+        bool rc;
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        // Free the existing cell hdu weight data (so we can verify that pmCellReadWeight() actually reads the data
+        psFree(cell->hdu->weights);
+        cell->hdu->weights = NULL;
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+
+        rc = pmCellReadWeight(cell, fitsFileR, NULL);
+        ok(rc, "pmCellReadWeight() returned TRUE");
+        for (int k = 0 ; k < cell->hdu->weights->n ; k++) {
+            bool errorFlag = false;
+            psImage *msk = cell->hdu->weights->data[k];
+            for (int i = 0 ; i < msk->numRows ; i++) {
+                for (int j = 0 ; j < msk->numCols ; j++) {
+                    if (((float) (BASE_WEIGHT+k)) != msk->data.F32[i][j]) {
+                        diag("TEST ERROR: msk[%d][%d] is %.2f, should be %.2f\n", i, j,
+                              msk->data.F32[i][j], ((float) (BASE_WEIGHT+k)));
+                        errorFlag = true;
+		    }
+		}
+	    }
+            ok(!errorFlag, "pmCellWriteWeight()/pmCellReadWeight() properly set the weight data (image %d)", k);
+        }
+        psFitsClose(fitsFileR);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+    // pmCellWriteMask(): tests
+    // Verify pmCellWriteMask() with NULL pmCell arg
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp00", "w");
+        ok(!pmCellWriteMask(NULL, fitsFileW, NULL, false), "pmCellWriteMask() returned FALSE with NULL pmCell input");
+        psFitsClose(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellWriteMask() with NULL pmFits arg
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(!pmCellWriteMask(cell, NULL, NULL, false), "pmCellWriteMask() returned FALSE with NULL psFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellWriteMask() with acceptable input params
+    // We first write a FITS file with the pmCellWriteMask(), then we read it and verify.
+    // First call pmCellWriteMask()
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp00", "w");
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(cell != NULL, "Allocated a pmCell successfully");
+
+        //  Use pmCellWriteMask() to write mask data to the FITS file
+        bool rc = pmCellWriteMask(cell, fitsFileW, NULL, false);
+        ok(rc, "pmCellWriteMask() returned TRUE");
+
+        //  Close the FITS file, free memory
+        psFitsClose(fitsFileW);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmCellReadMask() tests 
+    // Verify pmCellReadMask() with NULL pmCell param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(!pmCellReadMask(NULL, fitsFileR, NULL), "pmCellReadMask() returned FALSE with NULL pmCell param");
+        psFitsClose(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellReadMask() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(!pmCellReadMask(cell, NULL, NULL), "pmCellReadMask() returned FALSE with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmCellReadMask() with acceptable data (using the FITS file created above)
+    {
+        psMemId id = psMemGetId();
+        bool rc;
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        // Free the existing cell hdu mask data (so we can verify that pmCellReadMask() actually reads the data
+        psFree(cell->hdu->masks);
+        cell->hdu->masks = NULL;
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+
+        rc = pmCellReadMask(cell, fitsFileR, NULL);
+        ok(rc, "pmCellReadMask() returned TRUE");
+        for (int k = 0 ; k < cell->hdu->masks->n ; k++) {
+            bool errorFlag = false;
+            psImage *msk = cell->hdu->masks->data[k];
+            for (int i = 0 ; i < msk->numRows ; i++) {
+                for (int j = 0 ; j < msk->numCols ; j++) {
+                    if (0) {
+                        if (((float) (BASE_WEIGHT+k)) != msk->data.F32[i][j]) {
+                            diag("TEST ERROR: msk[%d][%d] is %.2f, should be %.2f\n", i, j,
+                                  msk->data.F32[i][j], ((float) (BASE_WEIGHT+k)));
+                            errorFlag = true;
+                        }
+		    }
+                    if (1) {
+                        if (((BASE_MASK+k)) != msk->data.U8[i][j]) {
+                            diag("TEST ERROR: msk[%d][%d] is %.2f, should be %.2f\n", i, j,
+                                  msk->data.F32[i][j], ((float) (BASE_MASK+k)));
+                            errorFlag = true;
+                        }
+		    }
+    		}
+	    }
+            ok(!errorFlag, "pmCellWriteMask()/pmCellReadMask() properly set the mask data (image %d)", k);
+        }
+        psFitsClose(fitsFileR);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+    // pmChipWrite() tests
+    // Verify pmChipWrite() with NULL pmChip param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp01", "w");
+        ok(!pmChipWrite(NULL, fitsFileW, NULL, false, true), "pmChipWrite() returned NULL with NULL pmChip param");
+        psFitsClose(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmChipWrite() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(!pmChipWrite(chip, NULL, NULL, false, true), "pmChipWrite() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmChipWrite() with acceptable data
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp01", "w");
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        ok(chip != NULL, "Allocated a pmChip successfully");
+
+        //  Use pmChipWrite() to write image data to the FITS file
+        bool rc = pmChipWrite(chip, fitsFileW, NULL, false, true);
+        ok(rc, "pmChipWrite() returned TRUE");
+
+        //  Close the FITS file, free memory
+        psFitsClose(fitsFileW);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmChipRead() tests
+    // Verify pmChipRead() with NULL pmChip param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(!pmChipRead(NULL, fitsFileR, NULL), "pmChipRead() returned NULL with NULL pmChip param");
+        psFitsClose(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmChipRead() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        ok(!pmChipRead(chip, NULL, NULL), "pmChipRead() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmChipRead() with acceptable input data
+    {
+        psMemId id = psMemGetId();
+        bool rc;
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        // Free the cells for chip 0 so we can verify that pmChipRead() actually reads the data from file
+        for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+            pmCell *cell = (pmCell *) chip->cells->data[chipID];
+            psFree(cell);
+            cell = NULL;
+	}
+
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        rc = pmChipRead(chip, fitsFileR, NULL);
+        ok(rc, "pmChipRead() returned TRUE");
+        bool errorFlag = false;
+        // XXX: chipID should be cellID
+        for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+            if (VERBOSE) diag("Reading cell %d\n", chipID);
+            pmCell *cell = (pmCell *) chip->cells->data[chipID];
+            for (int k = 0 ; k < cell->hdu->images->n ; k++) {
+                if (VERBOSE) diag("NOTE: image %d\n", k);
+                psImage *img = cell->hdu->images->data[k];
+                for (int i = 0 ; i < img->numRows ; i++) {
+                    for (int j = 0 ; j < img->numCols ; j++) {
+                        if (((float) (BASE_IMAGE+k)) != img->data.F32[i][j]) {
+                            diag("TEST ERROR: img[%d][%d] is %.2f, should be %.2f\n", i, j,
+                                  img->data.F32[i][j], ((float) (BASE_IMAGE+k)));
+                            errorFlag = true;
+			}
+		    }
+		}
+	    }
+            ok(!errorFlag, "pmChipWrite()/pmChipRead() properly set the image data (cell %d)", chipID);
+	}
+
+        psFitsClose(fitsFileR);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmChipWriteWeight() tests
+    // Verify pmChipWriteWeight() with NULL pmChip param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp01", "w");
+        ok(!pmChipWriteWeight(NULL, fitsFileW, NULL, false, true), "pmChipWriteWeight() returned NULL with NULL pmChip param");
+        psFitsClose(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmChipWriteWeight() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(!pmChipWriteWeight(chip, NULL, NULL, false, true), "pmChipWriteWeight() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmChipWriteWeight() with acceptable data
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp01", "w");
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        ok(chip != NULL, "Allocated a pmChip successfully");
+
+        //  Use pmChipWriteWeight() to write image data to the FITS file
+        bool rc = pmChipWriteWeight(chip, fitsFileW, NULL, false, true);
+        ok(rc, "pmChipWriteWeight() returned TRUE");
+
+        //  Close the FITS file, free memory
+        psFitsClose(fitsFileW);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmChipReadMask() tests
+    // Verify pmChipReadMask() with NULL pmChip param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(!pmChipReadMask(NULL, fitsFileR, NULL), "pmChipReadMask() returned NULL with NULL pmChip param");
+        psFitsClose(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmChipReadMask() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        ok(!pmChipReadMask(chip, NULL, NULL), "pmChipReadMask() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmChipReadMask() with acceptable input data
+    {
+        psMemId id = psMemGetId();
+        bool rc;
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        // Free the cells for chip 0 so we can verify that pmChipReadMask() actually reads the data from file
+        for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+            pmCell *cell = (pmCell *) chip->cells->data[chipID];
+            psFree(cell);
+            cell = NULL;
+	}
+
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        rc = pmChipReadMask(chip, fitsFileR, NULL);
+        ok(rc, "pmChipReadMask() returned TRUE");
+        bool errorFlag = false;
+        // XXX: chipID should be cellID
+        for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+            if (VERBOSE) diag("Reading cell %d\n", chipID);
+            pmCell *cell = (pmCell *) chip->cells->data[chipID];
+            for (int k = 0 ; k < cell->hdu->weights->n ; k++) {
+                if (VERBOSE) diag("NOTE: image %d\n", k);
+                psImage *wgt = cell->hdu->weights->data[k];
+                for (int i = 0 ; i < wgt->numRows ; i++) {
+                    for (int j = 0 ; j < wgt->numCols ; j++) {
+                        if (((float) (BASE_WEIGHT+k)) != wgt->data.F32[i][j]) {
+                            diag("TEST ERROR: wgt[%d][%d] is %.2f, should be %.2f\n", i, j,
+                                  wgt->data.F32[i][j], ((float) (BASE_WEIGHT+k)));
+                            errorFlag = true;
+			}
+		    }
+		}
+	    }
+            ok(!errorFlag, "pmChipWriteWeight()/pmChipReadWeight() properly set the weight data (cell %d)", chipID);
+	}
+
+        psFitsClose(fitsFileR);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmChipReadMask() tests
+    // Verify pmChipReadMask() with NULL pmChip param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(!pmChipReadMask(NULL, fitsFileR, NULL), "pmChipReadMask() returned NULL with NULL pmChip param");
+        psFitsClose(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmChipReadMask() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        ok(!pmChipReadMask(chip, NULL, NULL), "pmChipReadMask() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmChipReadMask() with acceptable input data
+    {
+        psMemId id = psMemGetId();
+        bool rc;
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        // Free the cells for chip 0 so we can verify that pmChipReadMask() actually reads the data from file
+        for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+            pmCell *cell = (pmCell *) chip->cells->data[chipID];
+            psFree(cell);
+            cell = NULL;
+	}
+
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        rc = pmChipReadMask(chip, fitsFileR, NULL);
+        ok(rc, "pmChipReadMask() returned TRUE");
+        bool errorFlag = false;
+        // XXX: chipID should be cellID
+        for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+            if (VERBOSE) diag("Reading cell %d\n", chipID);
+            pmCell *cell = (pmCell *) chip->cells->data[chipID];
+            for (int k = 0 ; k < cell->hdu->masks->n ; k++) {
+                if (VERBOSE) diag("NOTE: image %d\n", k);
+                psImage *msk = cell->hdu->masks->data[k];
+                for (int i = 0 ; i < msk->numRows ; i++) {
+                    for (int j = 0 ; j < msk->numCols ; j++) {
+                        if (((BASE_MASK+k)) != msk->data.U8[i][j]) {
+                            diag("TEST ERROR: msk[%d][%d] is %.2f, should be %.2f\n", i, j,
+                                  msk->data.F32[i][j], ((float) (BASE_MASK+k)));
+                            errorFlag = true;
+			}
+		    }
+		}
+	    }
+            ok(!errorFlag, "pmChipWriteMask()/pmChipReadMask() properly set the mask data (cell %d)", chipID);
+	}
+
+        psFitsClose(fitsFileR);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+    // pmFPAWrite() tests
+    // pmFPAWrite(pmFPA *fpa, psFits *fits, psDB *db, bool blank, bool recurse)
+    // Verify pmFPAWrite() with NULL pmFPA param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp01", "w");
+        ok(!pmFPAWrite(NULL, fitsFileW, NULL, false, true), "pmFPAWrite() returned NULL with NULL pmFPA param");
+        psFitsClose(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPAWrite() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(!pmFPAWrite(fpa, NULL, NULL, false, true), "pmFPAWrite() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPAWrite() with acceptable data
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp01", "w");
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        bool rc = pmFPAWrite(fpa, fitsFileW, NULL, false, true);
+        ok(rc, "pmFPAWrite() returned TRUE");
+        //  Close the FITS file, free memory
+        psFitsClose(fitsFileW);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPARead() tests
+    // Verify pmFPARead() with NULL pmFPA param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(!pmFPARead(NULL, fitsFileR, NULL), "pmFPARead() returned NULL with NULL pmFPA param");
+        psFitsClose(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPARead() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(!pmFPARead(fpa, NULL, NULL), "pmFPARead() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPARead() with acceptable input data
+    {
+        psMemId id = psMemGetId();
+        bool rc;
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        // Free the cells for chip 0 so we can verify that pmFPARead() actually reads the data from file
+        if (0) {
+            for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+                pmCell *cell = (pmCell *) chip->cells->data[chipID];
+                psFree(cell);
+                cell = NULL;
+            }
+	}
+
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        rc = pmFPARead(fpa, fitsFileR, NULL);
+        ok(rc, "pmFPARead() returned TRUE");
+        bool errorFlag = false;
+        // XXX: fpaID should be chipID
+        // XXX: chipID should be cellID
+        for (int fpaID = 0 ; fpaID < fpa->chips->n ; fpaID++) {
+            pmChip *chip = fpa->chips->data[fpaID];
+            for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+                if (VERBOSE) diag("Reading cell %d\n", chipID);
+                pmCell *cell = (pmCell *) chip->cells->data[chipID];
+                for (int k = 0 ; k < cell->hdu->images->n ; k++) {
+                    if (VERBOSE) diag("NOTE: image %d\n", k);
+                    psImage *img = cell->hdu->images->data[k];
+                    for (int i = 0 ; i < img->numRows ; i++) {
+                        for (int j = 0 ; j < img->numCols ; j++) {
+                            if (((float) (BASE_IMAGE+k)) != img->data.F32[i][j]) {
+                                diag("TEST ERROR: img[%d][%d] is %.2f, should be %.2f\n", i, j,
+                                      img->data.F32[i][j], ((float) (BASE_IMAGE+k)));
+                                errorFlag = true;
+        			}
+        		    }
+        		}
+        	    }
+                ok(!errorFlag, "pmFPAWrite()/pmFPARead() properly set the image data (chip %d, cell %d)", fpaID, chipID);
+    	    }
+	}
+
+        psFitsClose(fitsFileR);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+    // pmFPAWriteWeight() tests
+    // pmFPAWriteWeight(pmFPA *fpa, psFits *fits, psDB *db, bool blank, bool recurse)
+    // Verify pmFPAWriteWeight() with NULL pmFPA param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp01", "w");
+        ok(!pmFPAWriteWeight(NULL, fitsFileW, NULL, false, true), "pmFPAWriteWeight() returned NULL with NULL pmFPA param");
+        psFitsClose(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPAWriteWeight() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(!pmFPAWriteWeight(fpa, NULL, NULL, false, true), "pmFPAWriteWeight() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPAWriteWeight() with acceptable data
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp01", "w");
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        bool rc = pmFPAWriteWeight(fpa, fitsFileW, NULL, false, true);
+        ok(rc, "pmFPAWriteWeight() returned TRUE");
+        //  Close the FITS file, free memory
+        psFitsClose(fitsFileW);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAReadWeight() tests
+    // Verify pmFPAReadWeight() with NULL pmFPA param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(!pmFPARead(NULL, fitsFileR, NULL), "pmFPAReadWeight() returned NULL with NULL pmFPA param");
+        psFitsClose(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPAReadWeight() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(!pmFPARead(fpa, NULL, NULL), "pmFPAReadWeight() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPAReadWeight() with acceptable input data
+    {
+        psMemId id = psMemGetId();
+        bool rc;
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        // Free the cells for chip 0 so we can verify that pmFPAReadWeight() actually reads the data from file
+        if (0) {
+            for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+                pmCell *cell = (pmCell *) chip->cells->data[chipID];
+                psFree(cell);
+                cell = NULL;
+            }
+	}
+
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        rc = pmFPARead(fpa, fitsFileR, NULL);
+        ok(rc, "pmFPAReadWeight() returned TRUE");
+        bool errorFlag = false;
+        // XXX: fpaID should be chipID
+        // XXX: chipID should be cellID
+        for (int fpaID = 0 ; fpaID < fpa->chips->n ; fpaID++) {
+            pmChip *chip = fpa->chips->data[fpaID];
+            for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+                if (VERBOSE) diag("Reading cell %d\n", chipID);
+                pmCell *cell = (pmCell *) chip->cells->data[chipID];
+                for (int k = 0 ; k < cell->hdu->weights->n ; k++) {
+                    if (VERBOSE) diag("NOTE: image %d\n", k);
+                    psImage *wgt = cell->hdu->weights->data[k];
+                    for (int i = 0 ; i < wgt->numRows ; i++) {
+                        for (int j = 0 ; j < wgt->numCols ; j++) {
+                            if (((float) (BASE_WEIGHT+k)) != wgt->data.F32[i][j]) {
+                                diag("TEST ERROR: wgt[%d][%d] is %.2f, should be %.2f\n", i, j,
+                                      wgt->data.F32[i][j], ((float) (BASE_WEIGHT+k)));
+                                errorFlag = true;
+        			}
+        		    }
+        		}
+        	    }
+                ok(!errorFlag, "pmFPAWriteWeight()/pmFPAReadWeight() properly set the image data (chip %d, cell %d)", fpaID, chipID);
+    	    }
+	}
+
+        psFitsClose(fitsFileR);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+    // pmFPAWriteMask() tests
+    // pmFPAWriteMask(pmFPA *fpa, psFits *fits, psDB *db, bool blank, bool recurse)
+    // Verify pmFPAWriteMask() with NULL pmFPA param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp01", "w");
+        ok(!pmFPAWriteMask(NULL, fitsFileW, NULL, false, true), "pmFPAWriteMask() returned NULL with NULL pmFPA param");
+        psFitsClose(fitsFileW);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPAWriteMask() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(!pmFPAWriteMask(fpa, NULL, NULL, false, true), "pmFPAWriteMask() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPAWriteMask() with acceptable data
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(".tmp01", "w");
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        bool rc = pmFPAWriteMask(fpa, fitsFileW, NULL, false, true);
+        ok(rc, "pmFPAWriteMask() returned TRUE");
+        //  Close the FITS file, free memory
+        psFitsClose(fitsFileW);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAReadMask() tests
+    // Verify pmFPAReadMask() with NULL pmFPA param
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(!pmFPARead(NULL, fitsFileR, NULL), "pmFPAReadMask() returned NULL with NULL pmFPA param");
+        psFitsClose(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPAReadMask() with NULL pmFits param
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        ok(!pmFPARead(fpa, NULL, NULL), "pmFPAReadMask() returned NULL with NULL pmFits param");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Verify pmFPAReadMask() with acceptable input data
+    {
+        psMemId id = psMemGetId();
+        bool rc;
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        // Free the cells for chip 0 so we can verify that pmFPAReadMask() actually reads the data from file
+        if (0) {
+            for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+                pmCell *cell = (pmCell *) chip->cells->data[chipID];
+                psFree(cell);
+                cell = NULL;
+            }
+	}
+
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        rc = pmFPARead(fpa, fitsFileR, NULL);
+        ok(rc, "pmFPAReadMask() returned TRUE");
+        bool errorFlag = false;
+        // XXX: fpaID should be chipID
+        // XXX: chipID should be cellID
+        for (int fpaID = 0 ; fpaID < fpa->chips->n ; fpaID++) {
+            pmChip *chip = fpa->chips->data[fpaID];
+            for (int chipID = 0 ; chipID < chip->cells->n ; chipID++) {
+                if (VERBOSE) diag("Reading cell %d\n", chipID);
+                pmCell *cell = (pmCell *) chip->cells->data[chipID];
+                for (int k = 0 ; k < cell->hdu->masks->n ; k++) {
+                    if (VERBOSE) diag("NOTE: image %d\n", k);
+                    psImage *msk = cell->hdu->masks->data[k];
+                    for (int i = 0 ; i < msk->numRows ; i++) {
+                        for (int j = 0 ; j < msk->numCols ; j++) {
+                            if (((BASE_MASK+k)) != msk->data.U8[i][j]) {
+                                diag("TEST ERROR: msk[%d][%d] is %.2f, should be %.2f\n", i, j,
+                                      msk->data.F32[i][j], ((float) (BASE_MASK+k)));
+                                errorFlag = true;
+        			}
+        		    }
+        		}
+        	    }
+                ok(!errorFlag, "pmFPAWriteMask()/pmFPAReadMask() properly set the image data (chip %d, cell %d)", fpaID, chipID);
+    	    }
+	}
+
+        psFitsClose(fitsFileR);
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAUtils.c	(revision 22322)
@@ -0,0 +1,295 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+*/
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(40);
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAFindChip() tests
+    // Call pmFPAFindChip() with NULL pmFPA input parameter
+    {
+        psMemId id = psMemGetId();
+        int rc = pmFPAFindChip(NULL, "chip-0");
+        ok(-1 == rc, "pmFPAFindChip() returns -1 with NULL pmFPA input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmFPAFindChip() with bogus chip name
+    {
+        psMemId id = psMemGetId();
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        int rc = pmFPAFindChip(fpa, "bogus");
+        ok(-1 == rc, "pmFPAFindChip() returns -1 with bogus chip name");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmFPAFindChip() with NULL chip name
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        int rc = pmFPAFindChip(fpa, NULL);
+        ok(-1 == rc, "pmFPAFindChip() returns -1 with NULL name input param");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmFPAFindChip() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        // Set unique chip names
+        for (int chipID = 0 ; chipID < fpa->chips->n ; chipID++) {
+            pmChip *chip = fpa->chips->data[chipID];
+            char tmpName[100];
+            sprintf(tmpName, "chip-%d", chipID);
+            psMetadataAddStr(chip->concepts, PS_LIST_TAIL, "CHIP.NAME", PS_META_REPLACE, NULL, tmpName);
+	}
+
+        // Verify that pmFPAFindChip() finds those chips correctly
+        for (int chipID = 0 ; chipID < fpa->chips->n ; chipID++) {
+            char tmpName[100];
+            sprintf(tmpName, "chip-%d", chipID);
+            int rc = pmFPAFindChip(fpa, tmpName);
+            ok(rc == chipID, "pmFPAFindChip() correctly found chip %s (%d)", tmpName, rc);
+	}
+
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ----------------------------------------------------------------------
+    // pmChipFindCell() tests
+    // Call pmChipFindCell() with bogus cell name
+    {
+        psMemId id = psMemGetId();
+        int rc = pmChipFindCell(NULL, "cell-0");
+        ok(-1 == rc, "pmChipFindCell() returns -1 with NULL pmChip input param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call pmChipFindCell() with bogus cell name
+    {
+        psMemId id = psMemGetId();
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        pmChip *chip = fpa->chips->data[0];
+        int rc = pmChipFindCell(chip, "bogus");
+        ok(-1 == rc, "pmChipFindCell() returns -1 with bogus cell name");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmChipFindCell() with NULL cell name
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmChip heirarchy
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        int rc = pmChipFindCell(chip, NULL);
+        ok(-1 == rc, "pmChipFindCell() returns -1 with NULL name input param");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmChipFindCell() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmChip heirarchy
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        // Set unique cell names
+        for (int cellID = 0 ; cellID < chip->cells->n ; cellID++) {
+            pmCell *cell = chip->cells->data[cellID];
+            char tmpName[100];
+            sprintf(tmpName, "cell-%d", cellID);
+            psMetadataAddStr(cell->concepts, PS_LIST_TAIL, "CELL.NAME", PS_META_REPLACE, NULL, tmpName);
+	}
+
+        // Verify that pmChipFindCell() finds those cells correctly
+        for (int cellID = 0 ; cellID < chip->cells->n ; cellID++) {
+            char tmpName[100];
+            sprintf(tmpName, "cell-%d", cellID);
+            int rc = pmChipFindCell(chip, tmpName);
+            ok(rc == cellID, "pmChipFindCell() correctly found cell %s (%d)", tmpName, rc);
+	}
+
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAView.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAView.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmFPAView.c	(revision 22322)
@@ -0,0 +1,1023 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+*/
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+    cell->hdu->blankPHU = true;
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    chip->hdu = pmHDUAlloc("chipExtName");
+    bool rc = pmConfigFileRead(&chip->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&chip->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleChip())");
+        }
+    }
+    chip->hdu->blankPHU = true;
+
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    fpa->hdu = pmHDUAlloc("fpaExtName");
+    bool rc = pmConfigFileRead(&fpa->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&fpa->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleFPA())");
+        }
+    }
+    fpa->hdu->blankPHU = true;
+
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(174);
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAViewAlloc() tests
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(tmpView != NULL && psMemCheckFPAview(tmpView), "pmFPAviewAlloc() returned non-NULL");
+        ok(tmpView->nRows == 32, "pmFPAviewAlloc() set ->nRows properly");
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAViewReset() tests: NULL input
+    {
+        psMemId id = psMemGetId();
+        ok(!pmFPAviewReset(NULL), "pmFPAviewReset() returned FALSE with NULL input");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAViewReset() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(tmpView != NULL, "pmFPAviewAlloc() returned non-NULL");
+        tmpView->chip = 16;
+        tmpView->cell = 16;
+        tmpView->readout = 16;
+        tmpView->iRows = 16;
+        ok(pmFPAviewReset(tmpView), "pmFPAviewReset() returned TRUE");
+        ok(tmpView->chip == -1, "pmFPAviewReset() set tmpView->chip to -1");
+        ok(tmpView->cell == -1, "pmFPAviewReset() set tmpView->cell to -1");
+        ok(tmpView->readout == -1, "pmFPAviewReset() set tmpView->readout to 0");
+        ok(tmpView->iRows == 0, "pmFPAviewReset() set tmpView->iRows to 0");
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAViewForLevel() tests: NULL input
+    // pmFPAview *pmFPAviewForLevel(pmFPALevel level, const pmFPAview *input)
+
+    {
+        psMemId id = psMemGetId();
+        ok(!pmFPAviewForLevel(PM_FPA_LEVEL_FPA, NULL), "pmFPAviewForLevel() returned FALSE with NULL input");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAViewForLevel() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(tmpView != NULL, "pmFPAviewAlloc() returned non-NULL");
+        tmpView->chip = 16;
+        tmpView->cell = 16;
+        tmpView->readout = 16;
+        tmpView->iRows = 16;
+
+        pmFPAview *newView = pmFPAviewForLevel(PM_FPA_LEVEL_FPA, tmpView);
+        ok(newView, "pmFPAviewReset(PM_FPA_LEVEL_FPA) returned non-NULL");
+        ok(newView->chip == -1, "pmFPAviewReset() set tmpView->chip to -1");
+        psFree(newView);
+
+        newView = pmFPAviewForLevel(PM_FPA_LEVEL_CHIP, tmpView);
+        ok(newView, "pmFPAviewReset(PM_FPA_LEVEL_CHIP) returned non-NULL");
+        ok(newView->cell == -1, "pmFPAviewReset() set tmpView->cell to -1");
+        psFree(newView);
+
+        newView = pmFPAviewForLevel(PM_FPA_LEVEL_CELL, tmpView);
+        ok(newView, "pmFPAviewReset(PM_FPA_LEVEL_CELL) returned non-NULL");
+        ok(newView->readout == -1, "pmFPAviewReset() set tmpView->readout to 0");
+        psFree(newView);
+
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAViewLevel() tests: NULL input
+    // pmFPALevel pmFPAviewLevel(const pmFPAview *view)
+    {
+        psMemId id = psMemGetId();
+        ok(!pmFPAviewLevel(NULL), "pmFPAviewLevel() returned NULL with NULL input");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAViewLevel() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(tmpView != NULL, "pmFPAviewAlloc() returned non-NULL");
+        tmpView->chip = 16;
+        tmpView->cell = 16;
+        tmpView->readout = 16;
+        tmpView->iRows = 16;
+
+        tmpView->chip = -1;
+        ok(PM_FPA_LEVEL_FPA == pmFPAviewLevel(tmpView), "pmFPAviewReset() returned PM_FPA_LEVEL_FPA");
+        tmpView->chip = 16;
+
+        tmpView->cell = -1;
+        ok(PM_FPA_LEVEL_CHIP == pmFPAviewLevel(tmpView), "pmFPAviewReset() returned PM_FPA_LEVEL_CHIP");
+        tmpView->cell = 16;
+
+        tmpView->readout = -1;
+        ok(PM_FPA_LEVEL_CELL == pmFPAviewLevel(tmpView), "pmFPAviewReset() returned PM_FPA_LEVEL_CELL");
+        tmpView->readout = 16;
+
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAviewThisChip() tests: NULL view input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(NULL == pmFPAviewThisChip(NULL, fpa), "pmFPAviewThisChip(NULL, fpa) returned NULL");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewThisChip() tests: NULL pmFPA input
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewThisChip(tmpView, NULL), "pmFPAviewThisChip(pmFPAView, NULL) returned NULL");
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewThisChip() tests: NULL fpa->chips input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        psArray *chips = fpa->chips;
+        fpa->chips = NULL;
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewThisChip(tmpView, fpa), "pmFPAviewThisChip(pmFPAView, fpa) returned NULL view input");
+        fpa->chips = chips;
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // pmFPAviewThisChip() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+
+        // Test with tmpView->chip too low
+        tmpView->chip = -1;
+        ok(NULL == pmFPAviewThisChip(tmpView, fpa), "pmFPAviewThisChip(pmFPAView, fpa) returned NULL with pmFPAView->chip == -1");
+
+        // Test with tmpView->chip too high
+        tmpView->chip = fpa->chips->n;
+        ok(NULL == pmFPAviewThisChip(tmpView, fpa), "pmFPAviewThisChip(pmFPAView, fpa) returned NULL with pmFPAView->chip larger than the number of chips");
+    
+        // Test with various tmpView->chip 
+        bool errorFlag = false;
+        for (int i = 0 ; i < NUM_CHIPS ; i++) {
+            tmpView->chip = i;
+            pmChip *tmpChip = pmFPAviewThisChip(tmpView, fpa);
+            if (!tmpChip || tmpChip != fpa->chips->data[i]) {
+                diag("ERROR: pmFPAviewThisChip() returned the incorrect chip (%d)", i);
+                errorFlag = true;
+	    }
+	}
+        ok(!errorFlag, "pmFPAviewThisChip() returned the correct pmChip for all tests");
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAviewNextChip() tests: NULL view input
+    // pmChip *pmFPAviewNextChip(pmFPAview *view, const pmFPA *fpa, int nStep)
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(NULL == pmFPAviewNextChip(NULL, fpa, 0), "pmFPAviewNextChip(NULL, fpa, 0) returned NULL");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewNextChip() tests: NULL pmFPA input
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewNextChip(tmpView, NULL, 0), "pmFPAviewNextChip(pmFPAView, NULL, 0) returned NULL");
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewNextChip() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+
+        // Test with tmpView->chip too low
+        // XXX: Source code is wrong: must guard against input tmpView->chip = -1
+        tmpView->chip = -1;
+        ok(fpa->chips->data[0] == pmFPAviewNextChip(tmpView, fpa, 1), "pmFPAviewNextChip(pmFPAView, fpa, 1) returned NULL with pmFPAView->chip == -1");
+
+        // Test with tmpView->chip too high
+        tmpView->chip = fpa->chips->n;
+        ok(NULL == pmFPAviewNextChip(tmpView, fpa, 0), "pmFPAviewNextChip(pmFPAView, fpa, 0) returned NULL with pmFPAView->chip larger than the number of chips");
+    
+        // Test with various tmpView->chip, step = 0
+        bool errorFlag = false;
+        for (int i = 0 ; i < NUM_CHIPS ; i++) {
+            tmpView->chip = i;
+            pmChip *tmpChip = pmFPAviewNextChip(tmpView, fpa, 0);
+            if (!tmpChip || tmpChip != fpa->chips->data[i]) {
+                diag("ERROR: pmFPAviewNextChip() returned the incorrect chip (%d)", i);
+                errorFlag = true;
+	    }
+	}
+        ok(!errorFlag, "pmFPAviewNextChip() returned the correct pmChip for all tests (step = 0)");
+
+        // Test with various tmpView->chip, step = 1
+        errorFlag = false;
+        for (int i = 0 ; i < NUM_CHIPS-1 ; i++) {
+            tmpView->chip = i;
+            pmChip *tmpChip = pmFPAviewNextChip(tmpView, fpa, 1);
+            if (!tmpChip || tmpChip != fpa->chips->data[i+1]) {
+                diag("ERROR: pmFPAviewNextChip() returned the incorrect chip (%d)", i);
+                errorFlag = true;
+	    }
+	}
+        ok(!errorFlag, "pmFPAviewNextChip() returned the correct pmChip for all tests (step = 1)");
+
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAviewThisCell() tests: NULL view input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(NULL == pmFPAviewThisCell(NULL, fpa), "pmFPAviewThisCell(NULL, fpa) returned NULL");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewThisCell() tests: NULL pmFPA input
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewThisCell(tmpView, NULL), "pmFPAviewThisCell(pmFPAView, NULL) returned NULL");
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewThisCell() tests: NULL fpa->chips input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        psArray *chips = fpa->chips;
+        fpa->chips = NULL;
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewThisCell(tmpView, fpa), "pmFPAviewThisCell(pmFPAView, fpa) returned NULL view input");
+        fpa->chips = chips;
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // pmFPAviewThisCell() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+
+        // Test with tmpView->chip too low
+        tmpView->chip = -1;
+        ok(NULL == pmFPAviewThisCell(tmpView, fpa), "pmFPAviewThisCell(pmFPAView, fpa) returned NULL with pmFPAView->chip == -1");
+
+        // Test with tmpView->chip too high
+        tmpView->chip = fpa->chips->n;
+        ok(NULL == pmFPAviewThisCell(tmpView, fpa), "pmFPAviewThisCell(pmFPAView, fpa) returned NULL with pmFPAView->chip larger than the number of chips");
+    
+        // Test with various tmpView->chip 
+        bool errorFlag = false;
+        for (int i = 0 ; i < NUM_CHIPS ; i++) {
+            pmChip *chip = fpa->chips->data[i];
+            for (int j = 0 ; j < NUM_CELLS ; j++) {
+                tmpView->chip = i;
+                tmpView->cell = j;
+                pmCell *tmpCell = pmFPAviewThisCell(tmpView, fpa);
+                if (!tmpCell || tmpCell != chip->cells->data[j]) {
+                    diag("ERROR: pmFPAviewThisCell() returned the incorrect chip/cell (%d, %d)", i, j);
+                    errorFlag = true;
+		}
+
+    	    }
+	}
+        ok(!errorFlag, "pmFPAviewThisCell() returned the correct pmCell for all tests");
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAviewNextCell() tests: NULL view input
+    // pmChip *pmFPAviewNextCell(pmFPAview *view, const pmFPA *fpa, int nStep)
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(NULL == pmFPAviewNextCell(NULL, fpa, 0), "pmFPAviewNextCell(NULL, fpa, 0) returned NULL");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewNextCell() tests: NULL pmFPA input
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewNextCell(tmpView, NULL, 0), "pmFPAviewNextCell(pmFPAView, NULL, 0) returned NULL");
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewNextCell() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+
+        // Test with tmpView->chip too low
+        tmpView->chip = -1;
+        ok(NULL == pmFPAviewNextCell(tmpView, fpa, 0), "pmFPAviewNextCell(pmFPAView, fpa) returned NULL with pmFPAView->chip == -1");
+
+        // Test with tmpView->chip too high
+        tmpView->chip = fpa->chips->n;
+        ok(NULL == pmFPAviewNextCell(tmpView, fpa, 0), "pmFPAviewNextCell(pmFPAView, fpa) returned NULL with pmFPAView->chip larger than the number of chips");
+    
+        // Test with various tmpView->chip (step = 0)
+        bool errorFlag = false;
+        for (int i = 0 ; i < NUM_CHIPS ; i++) {
+            pmChip *chip = fpa->chips->data[i];
+            for (int j = 0 ; j < NUM_CELLS ; j++) {
+                tmpView->chip = i;
+                tmpView->cell = j;
+                pmCell *tmpCell = pmFPAviewNextCell(tmpView, fpa, 0);
+                if (!tmpCell || tmpCell != chip->cells->data[j]) {
+                    diag("ERROR: pmFPAviewNextCell() returned the incorrect chip/cell (%d, %d)", i, j);
+                    errorFlag = true;
+		}
+
+    	    }
+	}
+        ok(!errorFlag, "pmFPAviewNextCell() returned the correct pmCell for all tests (step = 0)");
+
+        // Test with various tmpView->chip (step = 1)
+        errorFlag = false;
+        for (int i = 0 ; i < NUM_CHIPS ; i++) {
+            pmChip *chip = fpa->chips->data[i];
+            for (int j = 0 ; j < NUM_CELLS-1 ; j++) {
+                tmpView->chip = i;
+                tmpView->cell = j;
+                pmCell *tmpCell = pmFPAviewNextCell(tmpView, fpa, 1);
+                if (!tmpCell || tmpCell != chip->cells->data[j+1]) {
+                    diag("ERROR: pmFPAviewNextCell() returned the incorrect chip/cell (%d, %d)", i, j);
+                    errorFlag = true;
+		}
+
+    	    }
+	}
+        ok(!errorFlag, "pmFPAviewNextCell() returned the correct pmCell for all tests (step = 1)");
+
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAviewThisReadout() tests: NULL view input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(NULL == pmFPAviewThisReadout(NULL, fpa), "pmFPAviewThisReadout(NULL, fpa) returned NULL");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewThisReadout() tests: NULL pmFPA input
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewThisReadout(tmpView, NULL), "pmFPAviewThisReadout(pmFPAView, NULL) returned NULL");
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewThisReadout() tests: NULL fpa->chips input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        psArray *chips = fpa->chips;
+        fpa->chips = NULL;
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewThisReadout(tmpView, fpa), "pmFPAviewThisReadout(pmFPAView, fpa) returned NULL view input");
+        fpa->chips = chips;
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // pmFPAviewThisReadout() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+
+        // Test with tmpView->chip too low
+        tmpView->chip = -1;
+        ok(NULL == pmFPAviewThisReadout(tmpView, fpa), "pmFPAviewThisReadout(pmFPAView, fpa) returned NULL with pmFPAView->chip == -1");
+
+        // Test with tmpView->chip too high
+        tmpView->chip = fpa->chips->n;
+        ok(NULL == pmFPAviewThisReadout(tmpView, fpa), "pmFPAviewThisReadout(pmFPAView, fpa) returned NULL with pmFPAView->chip larger than the number of chips");
+    
+        // Test with various tmpView->chip 
+        bool errorFlag = false;
+        for (int i = 0 ; i < NUM_CHIPS ; i++) {
+            pmChip *chip = fpa->chips->data[i];
+            for (int j = 0 ; j < NUM_CELLS ; j++) {
+                pmCell *cell = chip->cells->data[j];
+                for (int k = 0 ; k < NUM_READOUTS ; k++) {
+                    tmpView->chip = i;
+                    tmpView->cell = j;
+                    tmpView->readout = k;
+                    pmReadout *tmpReadout = pmFPAviewThisReadout(tmpView, fpa);
+                    if (!tmpReadout || tmpReadout != cell->readouts->data[k]) {
+                        diag("ERROR: pmFPAviewThisReadout() returned the incorrect chip/cell/readout (%d, %d, %d)", i, j, k);
+                        errorFlag = true;
+		    }
+		}
+    	    }
+	}
+        ok(!errorFlag, "pmFPAviewThisReadout() returned the correct pmCell for all tests");
+
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAviewNextReadout() tests: NULL view input
+    // pmChip *pmFPAviewNextReadout(pmFPAview *view, const pmFPA *fpa, int nStep)
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(NULL == pmFPAviewNextReadout(NULL, fpa, 0), "pmFPAviewNextReadout(NULL, fpa, 0) returned NULL");
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewNextReadout() tests: NULL pmFPA input
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewNextReadout(tmpView, NULL, 0), "pmFPAviewNextReadout(pmFPAView, NULL, 0) returned NULL");
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // pmFPAviewNextReadout() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+
+        // Test with tmpView->chip too low
+        tmpView->chip = -1;
+        ok(NULL == pmFPAviewNextReadout(tmpView, fpa, 0), "pmFPAviewNextReadout(pmFPAView, fpa) returned NULL with pmFPAView->chip == -1");
+
+        // Test with tmpView->chip too high
+        tmpView->chip = fpa->chips->n;
+        ok(NULL == pmFPAviewNextReadout(tmpView, fpa, 0), "pmFPAviewNextReadout(pmFPAView, fpa) returned NULL with pmFPAView->chip larger than the number of chips");
+    
+        // Test with various tmpView->chip (step = 0)
+        bool errorFlag = false;
+        for (int i = 0 ; i < NUM_CHIPS ; i++) {
+            pmChip *chip = fpa->chips->data[i];
+            for (int j = 0 ; j < NUM_CELLS ; j++) {
+                pmCell *cell = chip->cells->data[j];
+                for (int k = 0 ; k < NUM_READOUTS ; k++) {
+                    tmpView->chip = i;
+                    tmpView->cell = j;
+                    tmpView->readout = k;
+                    pmReadout *tmpReadout = pmFPAviewNextReadout(tmpView, fpa, 0);
+                    if (!tmpReadout || tmpReadout != cell->readouts->data[k]) {
+                        diag("ERROR: pmFPAviewNextReadout() returned the incorrect chip/cell/readout (%d, %d, %d)", i, j, k);
+                        errorFlag = true;
+		    }
+		}
+    	    }
+	}
+        ok(!errorFlag, "pmFPAviewNextReadout() returned the correct pmCell for all tests (step = 0)");
+
+        // Test with various tmpView->chip (step = 1)
+        errorFlag = false;
+        for (int i = 0 ; i < NUM_CHIPS ; i++) {
+            pmChip *chip = fpa->chips->data[i];
+            for (int j = 0 ; j < NUM_CELLS ; j++) {
+                pmCell *cell = chip->cells->data[j];
+                for (int k = 0 ; k < NUM_READOUTS-1 ; k++) {
+                    tmpView->chip = i;
+                    tmpView->cell = j;
+                    tmpView->readout = k;
+                    pmReadout *tmpReadout = pmFPAviewNextReadout(tmpView, fpa, 1);
+                    if (!tmpReadout || tmpReadout != cell->readouts->data[k+1]) {
+                        diag("ERROR: pmFPAviewNextReadout() returned the incorrect chip/cell/readout (%d, %d, %d)", i, j, k);
+                        errorFlag = true;
+		    }
+		}
+    	    }
+	}
+        ok(!errorFlag, "pmFPAviewNextReadout() returned the correct pmCell for all tests (step = 1)");
+
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAviewThisHDU() tests: NULL view input
+    // pmHDU *pmFPAviewThisHDU(const pmFPAview *view, const pmFPA *fpa)
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(NULL == pmFPAviewThisHDU(NULL, fpa), "pmFPAviewThisHDU(NULL, fpa) returned NULL");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // pmFPAviewThisHDU() tests: NULL pmFPA input
+    // pmHDU *pmFPAviewThisHDU(const pmFPAview *view, const pmFPA *fpa)
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewThisHDU(tmpView, NULL), "pmFPAviewThisHDU(pmFPAView, NULL) returned NULL");
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewThisHDU() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+
+        tmpView->chip = -1;
+        pmHDU *hdu = pmFPAviewThisHDU(tmpView, fpa);
+        ok(hdu, "pmFPAviewThisHDU() returned non-NULL with tmpView->chip = -1");
+        ok(hdu == pmHDUFromFPA(fpa), "pmFPAviewThisHDU() returned the correct HDU");
+        tmpView->chip = NUM_CHIPS/2;
+
+        tmpView->cell = -1;
+        hdu = pmFPAviewThisHDU(tmpView, fpa);
+        ok(hdu, "pmFPAviewThisHDU() returned non-NULL with tmpView->cell = -1");
+        ok(hdu == pmHDUFromChip(pmFPAviewThisChip(tmpView, fpa)), "pmFPAviewThisHDU() returned the correct HDU");
+        tmpView->cell = NUM_CELLS/2;
+
+        tmpView->readout = -1;
+        hdu = pmFPAviewThisHDU(tmpView, fpa);
+        ok(hdu, "pmFPAviewThisHDU() returned non-NULL with tmpView->readout = -1");
+        ok(hdu == pmHDUFromCell(pmFPAviewThisCell(tmpView, fpa)), "pmFPAviewThisHDU() returned the correct HDU");
+        tmpView->readout = NUM_READOUTS/2;
+
+        hdu = pmFPAviewThisHDU(tmpView, fpa);
+        ok(hdu, "pmFPAviewThisHDU() returned non-NULL");
+        ok(hdu == pmHDUFromReadout(pmFPAviewThisReadout(tmpView, fpa)), "pmFPAviewThisHDU() returned the correct HDU");
+
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAviewThisPHU() tests: NULL view input
+    // pmHDU *pmFPAviewThisPHU(const pmFPAview *view, const pmFPA *fpa)
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(NULL == pmFPAviewThisPHU(NULL, fpa), "pmFPAviewThisPHU(NULL, fpa) returned NULL");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // pmFPAviewThisPHU() tests: NULL pmFPA input
+    // pmHDU *pmFPAviewThisPHU(const pmFPAview *view, const pmFPA *fpa)
+    {
+        psMemId id = psMemGetId();
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+        ok(NULL == pmFPAviewThisPHU(tmpView, NULL), "pmFPAviewThisPHU(pmFPAView, NULL) returned NULL");
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewThisPHU() tests: acceptable input
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmFPAview *tmpView = pmFPAviewAlloc(32);
+
+        tmpView->chip = -1;
+        pmHDU *hdu = pmFPAviewThisPHU(tmpView, fpa);
+        ok(hdu, "pmFPAviewThisPHU() returned non-NULL with tmpView->chip = -1");
+        ok(hdu == pmHDUFromFPA(fpa), "pmFPAviewThisPHU() returned the correct HDU");
+        tmpView->chip = NUM_CHIPS/2;
+
+        tmpView->cell = -1;
+        hdu = pmFPAviewThisPHU(tmpView, fpa);
+        ok(hdu, "pmFPAviewThisPHU() returned non-NULL with tmpView->cell = -1");
+        ok(hdu == pmHDUFromChip(pmFPAviewThisChip(tmpView, fpa)), "pmFPAviewThisPHU() returned the correct HDU");
+        tmpView->cell = NUM_CELLS/2;
+
+        tmpView->readout = -1;
+        hdu = pmFPAviewThisPHU(tmpView, fpa);
+        ok(hdu, "pmFPAviewThisPHU() returned non-NULL with tmpView->readout = -1");
+        ok(hdu == pmHDUFromCell(pmFPAviewThisCell(tmpView, fpa)), "pmFPAviewThisPHU() returned the correct HDU");
+        tmpView->readout = NUM_READOUTS/2;
+
+        hdu = pmFPAviewThisPHU(tmpView, fpa);
+        ok(hdu == NULL, "pmFPAviewThisPHU() returned NULL");
+
+        psFree(fpa);
+        psFree(camera);
+        psFree(tmpView);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmFPAviewGenerate() tests: NULL pmFPA input
+    {
+        psMemId id = psMemGetId();
+        int chipID = NUM_CHIPS/2;
+        int cellID = NUM_CELLS/2;
+        int readoutID = NUM_READOUTS/2;
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        pmChip *chip = fpa->chips->data[chipID];
+        pmCell *cell = chip->cells->data[cellID];
+        pmReadout *readout = cell->readouts->data[readoutID];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(readout != NULL, "Allocated a pmReadout successfully");
+        pmFPAview *view = pmFPAviewGenerate(NULL, chip, cell, readout);
+        ok(view == NULL, "pmFPAviewGenerate(NULL, chip, cell, readout) returned NULL");
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmFPAviewGenerate() tests: acceptable input parameters
+    // XX: Add more input combinations.
+    {
+        psMemId id = psMemGetId();
+        int chipID = NUM_CHIPS/2;
+        int cellID = NUM_CELLS/2;
+        int readoutID = NUM_READOUTS/2;
+        pmFPA* fpa = generateSimpleFPA(NULL);
+        pmChip *chip = fpa->chips->data[chipID];
+        pmCell *cell = chip->cells->data[cellID];
+        pmReadout *readout = cell->readouts->data[readoutID];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(readout != NULL, "Allocated a pmReadout successfully");
+        pmFPAview *view = pmFPAviewGenerate(fpa, chip, cell, readout);
+        ok(view != NULL && psMemCheckFPAview(view), "pmFPAviewGenerate() returned an pmFPAviw with acceptable input parameters");
+        ok(view->chip == chipID, "pmFPAviewGenerate() set pmFPAview->chip correctly");
+        ok(view->cell == cellID, "pmFPAviewGenerate() set pmFPAview->cell correctly");
+        ok(view->readout == readoutID, "pmFPAviewGenerate() set pmFPAview->readout correctly");
+        psFree(view);
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmHDU.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmHDU.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmHDU.c	(revision 22322)
@@ -0,0 +1,571 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+*/
+
+#define NUM_HDUS 8
+char *fitsFilename = "tmp.fits";
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", 0);
+    plan_tests(159);
+
+
+    // ----------------------------------------------------------------------
+    // pmHDUAlloc() tests
+    // Test pmHDUAlloc() with a NULL extname
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc(NULL);
+        ok(hdu, "pmHDUAlloc(NULL) returned non-NULL");
+        skip_start(!hdu, 7, "Skipping tests because pmHDUAlloc(NULL) returned NULL");
+        ok(hdu->blankPHU == true, "pmHDUAlloc(NULL) set hdu->blankPHU correctly");
+        ok(hdu->extname == NULL, "pmHDUAlloc(NULL) set hdu->extname correctly");
+        ok(hdu->format == NULL, "pmHDUAlloc(NULL) set hdu->format correctly");
+        ok(hdu->header == NULL, "pmHDUAlloc(NULL) set hdu->header correctly");
+        ok(hdu->images == NULL, "pmHDUAlloc(NULL) set hdu->images correctly");
+        ok(hdu->weights == NULL, "pmHDUAlloc(NULL) set hdu->weights correctly");
+        ok(hdu->masks == NULL, "pmHDUAlloc(NULL) set hdu->masks correctly");
+        psFree(hdu);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDUAlloc() with a non-NULL extname
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc("ext-0");
+        ok(hdu, "pmHDUAlloc(extname) returned non-NULL");
+        skip_start(!hdu, 7, "Skipping tests because pmHDUAlloc(extname) returned NULL");
+        ok(hdu->blankPHU == false, "pmHDUAlloc(extname) set hdu->blankPHU correctly");
+        ok(0 == strcmp(hdu->extname, "ext-0"), "pmHDUAlloc(extname) set hdu->extname correctly");
+        ok(hdu->format == NULL, "pmHDUAlloc(extname) set hdu->format correctly");
+        ok(hdu->header == NULL, "pmHDUAlloc(extname) set hdu->header correctly");
+        ok(hdu->images == NULL, "pmHDUAlloc(extname) set hdu->images correctly");
+        ok(hdu->weights == NULL, "pmHDUAlloc(extname) set hdu->weights correctly");
+        ok(hdu->masks == NULL, "pmHDUAlloc(extname) set hdu->masks correctly");
+        psFree(hdu);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmHDUWrite(), pmHDURead tests
+    // Test pmHDUWrite() with NULL pmHDU input argument
+    {
+        psMemId id = psMemGetId();
+        // Create simple FITS file
+        psFits* fitsFile = psFitsOpen(".tmp00", "w");
+        ok(fitsFile != NULL, "psFitsOpen() was successful");
+        psMetadata* header = psMetadataAlloc();
+        psImage* image = psImageAlloc(16, 16, PS_TYPE_F32);
+        ok(psFitsWriteImage(fitsFile, header, image, 0, "extname"), "psFitsWriteImage() successful");
+        psFree(header);
+        psFree(image);
+        bool rc = pmHDUWrite(NULL, fitsFile);
+        ok(rc == false, "pmHDUWrite() returned FALSE with NULL psHDU as input");
+        psFitsClose(fitsFile);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDUWrite() with NULL psFits input argument
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc("extname");
+        hdu->header = psMetadataAlloc();
+        psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYINT", PS_DATA_S32, "psS32 Item", 0);
+        bool rc = pmConfigFileRead(&hdu->format, "../dataFiles/camera0/format0.config", "Camera 0 Config Format");
+        if (!rc) {
+            rc = pmConfigFileRead(&hdu->format, "dataFiles/camera0/format0.config", "Camera 0 Config Format");
+	}
+        ok(rc == true, "pmConfigFileRead() was successful");
+        rc = pmHDUWrite(hdu, NULL);
+        ok(rc == false, "pmHDUWrite() returned FALSE with NULL psFits file as input");
+        psFree(hdu);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDURead() with NULL pmHDU input argument
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen("../dataFiles/sampleFitsFile.fits", "r");
+        if (!fitsFile) {
+            fitsFile = psFitsOpen("dataFiles/sampleFitsFile.fits", "r");
+	}
+        ok(fitsFile != NULL, "psFitsOpen() was successful");
+        bool rc = pmHDURead(NULL, fitsFile);
+        ok(rc == false, "pmHDURead() returned FALSE with NULL psHDU as input");
+        psFitsClose(fitsFile);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDURead() with NULL pmHDU input argument
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc("extname");
+        hdu->header = psMetadataAlloc();
+        bool rc = pmHDURead(hdu, NULL);
+        ok(rc == false, "pmHDURead() returned FALSE with NULL psFits file as input");
+        psFree(hdu);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // 1. Create a simple FITS file
+    // 2. Create a simple pmHDU header
+    // 3. Use pmHDUWrite() to write that header to the FITS file
+    // 4. Close the FITS file
+    // 5. Read that fits file from disk
+    // 6. Create another pmHDU
+    // 7. Call pmHDURead()which will read the FITS data into hdu->images
+    // 8. Verify that hdu->images was set correctly.
+    // 
+    // XXX: Add code to delete .tmp00
+    // XXX: Should we try with multiple images in the psArray hdu->images?
+    #define BASE 20
+    {
+        psMemId id = psMemGetId();
+        // 1. Create a simple FITS file
+        psFits* fitsFileW = psFitsOpen(".tmp00", "w");
+        ok(fitsFileW != NULL, "psFitsOpen() was successful");
+        // 2. Create a simple pmHDU header
+        //    Allocate and format hdu->header
+        //    Set hdu->images
+        pmHDU *hdu = pmHDUAlloc("extname");
+        bool rc = pmConfigFileRead(&hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            rc = pmConfigFileRead(&hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+	}
+        ok(rc, "pmConfigFileRead() was successful");
+        hdu->images = psArrayAlloc(NUM_HDUS);
+        for (int k = 0 ; k < NUM_HDUS ; k++) {
+            hdu->images->data[k] = psImageAlloc(4, 4, PS_TYPE_F32);
+            psImageInit(hdu->images->data[k], (float) (BASE+k));
+	}
+        // 3. Use pmHDUWrite() to write that header to the FITS file
+        rc = pmHDUWrite(hdu, fitsFileW);
+        ok(rc == true, "pmHDUWrite() returned TRUE");
+        // 4. Close the FITS file, free memory
+        psFree(hdu);
+        psFitsClose(fitsFileW);
+
+        // Now, try to read that FITS file from disk.
+        // 5. Read that fits file from disk
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(fitsFileR != NULL, "psFitsOpen() was successful");
+        // 6. Create another pmHDU
+        hdu = pmHDUAlloc("extname");
+        hdu->images = NULL;
+        // 7. Call pmHDURead()which will read the FITS data into hdu->images
+        rc = pmHDURead(hdu, fitsFileR);
+        ok(rc == true, "pmHDURead() returned TRUE");
+        ok(hdu->images != NULL, "pmHDURead() created hdu->images");
+        // 8. Verify that hdu->images was set correctly.
+        bool errorFlag = false;
+        for (int k = 0 ; k < NUM_HDUS ; k++) {
+            psImage *img = (psImage *) hdu->images->data[k];
+            for (psS32 i = 0 ; i < img->numRows ; i++) {
+                for (psS32 j = 0 ; j < img->numCols ; j++) {
+                    if (((float) k+BASE) != img->data.F32[i][j]) {
+                        errorFlag = true;
+                        diag("TEST ERROR (image %d): img[%d][%d] is %f, should be %f\n",
+                              k, i, j, img->data.F32[i][j], ((float)k+BASE));
+        	    }
+            	}
+	    }
+	}
+        ok(!errorFlag, "pmHDURead() correctly returned the image data");
+        psFitsClose(fitsFileR);
+        psFree(hdu);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmHDUWriteWeight(), pmHDUReadWeight() tests
+    // Test pmHDUWriteWeight() with NULL pmHDU input argument
+    {
+        psMemId id = psMemGetId();
+        // Create simple FITS file
+        psFits* fitsFile = psFitsOpen(".tmp00", "w");
+        ok(fitsFile != NULL, "psFitsOpen() was successful");
+        psMetadata* header = psMetadataAlloc();
+        psImage* image = psImageAlloc(16, 16, PS_TYPE_F32);
+        ok(psFitsWriteImage(fitsFile, header, image, 0, "extname"), "psFitsWriteImage() successful");
+        psFree(header);
+        psFree(image);
+        bool rc = pmHDUWriteWeight(NULL, fitsFile);
+        ok(rc == false, "pmHDUWriteWeight() returned FALSE with NULL psHDU as input");
+        psFitsClose(fitsFile);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDUWriteWeight() with NULL psFits input argument
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc("extname");
+        hdu->header = psMetadataAlloc();
+        psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYINT", PS_DATA_S32, "psS32 Item", 0);
+        bool rc = pmConfigFileRead(&hdu->format, "../dataFiles/camera0/format0.config", "Camera 0 Config Format");
+        if (!rc) {
+            rc = pmConfigFileRead(&hdu->format, "dataFiles/camera0/format0.config", "Camera 0 Config Format");
+	}
+        ok(rc == true, "pmConfigFileRead() was successful");
+        rc = pmHDUWriteWeight(hdu, NULL);
+        ok(rc == false, "pmHDUWriteWeight    () returned FALSE with NULL psFits file as input");
+        psFree(hdu);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDUReadWeight() with NULL pmHDU input argument
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen("../dataFiles/sampleFitsFile.fits", "r");
+        if (!fitsFile) {
+            fitsFile = psFitsOpen("dataFiles/sampleFitsFile.fits", "r");
+	}
+        ok(fitsFile != NULL, "psFitsOpen() was successful");
+        bool rc = pmHDUReadWeight(NULL, fitsFile);
+        ok(rc == false, "pmHDUReadWeight() returned FALSE with NULL psHDU as input");
+        psFitsClose(fitsFile);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDUReadWeight() with NULL pmHDU input argument
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc("extname");
+        hdu->header = psMetadataAlloc();
+        bool rc = pmHDUReadWeight(hdu, NULL);
+        ok(rc == false, "pmHDUReadWeight    () returned FALSE with NULL psFits file as input");
+        psFree(hdu);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // 1. Create a simple FITS file
+    // 2. Create a simple pmHDU header
+    // 3. Use pmHDUWriteWeight() to write that header to the FITS file
+    // 4. Close the FITS file
+    // 5. Read that fits file from disk
+    // 6. Create another pmHDU
+    // 7. Call pmHDUReadWeight()which will read the FITS data into hdu->images
+    // 8. Verify that hdu->images was set correctly.
+    // 
+    // XXX: Add code to delete .tmp00
+    // XXX: Should we try with multiple images in the psArray hdu->images?
+    #define BASE 20
+    {
+        psMemId id = psMemGetId();
+        // 1. Create a simple FITS file
+        psFits* fitsFileW = psFitsOpen(".tmp00", "w");
+        ok(fitsFileW != NULL, "psFitsOpen() was successful");
+        // 2. Create a simple pmHDU header
+        //    Allocate and format hdu->header
+        //    Set hdu->images
+        pmHDU *hdu = pmHDUAlloc("extname");
+        bool rc = pmConfigFileRead(&hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            rc = pmConfigFileRead(&hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+	}
+        ok(rc, "pmConfigFileRead() was successful");
+        hdu->weights = psArrayAlloc(NUM_HDUS);
+        for (int k = 0 ; k < NUM_HDUS ; k++) {
+            hdu->weights->data[k] = psImageAlloc(4, 4, PS_TYPE_F32);
+            psImageInit(hdu->weights->data[k], (float) (BASE+k));
+	}
+        // 3. Use pmHDUWriteWeight() to write that header to the FITS file
+        rc = pmHDUWriteWeight(hdu, fitsFileW);
+        ok(rc == true, "pmHDUWriteWeight() returned TRUE");
+        // 4. Close the FITS file, free memory
+        psFree(hdu);
+        psFitsClose(fitsFileW);
+
+        // Now, try to read that FITS file from disk.
+        // 5. Read that fits file from disk
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(fitsFileR != NULL, "psFitsOpen() was successful");
+        // 6. Create another pmHDU
+        hdu = pmHDUAlloc("extname");
+        hdu->weights = NULL;
+        // 7. Call pmHDUReadWeight()which will read the FITS data into hdu->weights
+        rc = pmHDUReadWeight(hdu, fitsFileR);
+        ok(rc == true, "pmHDUReadWeight() returned TRUE");
+        ok(hdu->weights != NULL, "pmHDUReadWeight() created hdu->weights");
+        // 8. Verify that hdu->weights was set correctly.
+        bool errorFlag = false;
+        for (int k = 0 ; k < NUM_HDUS ; k++) {
+            psImage *img = (psImage *) hdu->weights->data[k];
+            for (psS32 i = 0 ; i < img->numRows ; i++) {
+                for (psS32 j = 0 ; j < img->numCols ; j++) {
+                    if (((float) k+BASE) != img->data.F32[i][j]) {
+                        errorFlag = true;
+                        diag("TEST ERROR (image %d): img[%d][%d] is %f, should be %f\n",
+                              k, i, j, img->data.F32[i][j], ((float)k+BASE));
+        	    }
+            	}
+	    }
+	}
+        ok(!errorFlag, "pmHDUReadWeight() correctly returned the image data");
+        psFitsClose(fitsFileR);
+        psFree(hdu);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmHDUWriteMask(), pmHDUReadMask() tests
+    // Test pmHDUWriteMask() with NULL pmHDU input argument
+    {
+        psMemId id = psMemGetId();
+        // Create simple FITS file
+        psFits* fitsFile = psFitsOpen(".tmp00", "w");
+        ok(fitsFile != NULL, "psFitsOpen() was successful");
+        psMetadata* header = psMetadataAlloc();
+        psImage* image = psImageAlloc(16, 16, PS_TYPE_F32);
+        ok(psFitsWriteImage(fitsFile, header, image, 0, "extname"), "psFitsWriteImage() successful");
+        psFree(header);
+        psFree(image);
+        bool rc = pmHDUWriteMask(NULL, fitsFile);
+        ok(rc == false, "pmHDUWriteMask() returned FALSE with NULL psHDU as input");
+        psFitsClose(fitsFile);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDUWriteMask() with NULL psFits input argument
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc("extname");
+        hdu->header = psMetadataAlloc();
+        psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYINT", PS_DATA_S32, "psS32 Item", 0);
+        bool rc = pmConfigFileRead(&hdu->format, "dataFiles/camera0/format0.config", "Camera 0 Config Format");
+        if (!rc) {
+             pmConfigFileRead(&hdu->format, "../dataFiles/camera0/format0.config", "Camera 0 Config Format");
+	}
+        ok(rc == true, "pmConfigFileRead() was successful");
+        rc = pmHDUWriteMask(hdu, NULL);
+        ok(rc == false, "pmHDUWriteMask() returned FALSE with NULL psFits file as input");
+        psFree(hdu);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDUReadMask() with NULL pmHDU input argument
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen("../dataFiles/sampleFitsFile.fits", "r");
+        if (!fitsFile) {
+            fitsFile = psFitsOpen("dataFiles/sampleFitsFile.fits", "r");
+	}
+        ok(fitsFile != NULL, "psFitsOpen() was successful");
+        bool rc = pmHDUReadMask(NULL, fitsFile);
+        ok(rc == false, "pmHDUReadMask() returned FALSE with NULL psHDU as input");
+        psFitsClose(fitsFile);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDUReadMask() with NULL pmHDU input argument
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc("extname");
+        hdu->header = psMetadataAlloc();
+        bool rc = pmHDUReadMask(hdu, NULL);
+        ok(rc == false, "pmHDUReadMask() returned FALSE with NULL psFits file as input");
+        psFree(hdu);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // 1. Create a simple FITS file
+    // 2. Create a simple pmHDU header
+    // 3. Use pmHDUWriteMask() to write that header to the FITS file
+    // 4. Close the FITS file
+    // 5. Read that fits file from disk
+    // 6. Create another pmHDU
+    // 7. Call pmHDUReadMask()which will read the FITS data into hdu->images
+    // 8. Verify that hdu->images was set correctly.
+    // 
+    // XXX: Add code to delete .tmp00
+    // XXX: Should we try with multiple images in the psArray hdu->images?
+    #define BASE 20
+    {
+        psMemId id = psMemGetId();
+        // 1. Create a simple FITS file
+        psFits* fitsFileW = psFitsOpen(".tmp00", "w");
+        ok(fitsFileW != NULL, "psFitsOpen() was successful");
+        // 2. Create a simple pmHDU header
+        //    Allocate and format hdu->header
+        //    Set hdu->images
+        pmHDU *hdu = pmHDUAlloc("extname");
+        bool rc = pmConfigFileRead(&hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            rc = pmConfigFileRead(&hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+	}
+        ok(rc, "pmConfigFileRead() was successful");
+        hdu->masks = psArrayAlloc(NUM_HDUS);
+        for (int k = 0 ; k < NUM_HDUS ; k++) {
+            hdu->masks->data[k] = psImageAlloc(4, 4, PS_TYPE_F32);
+            psImageInit(hdu->masks->data[k], (float) (BASE+k));
+	}
+        // 3. Use pmHDUWriteMask() to write that header to the FITS file
+        rc = pmHDUWriteMask(hdu, fitsFileW);
+        ok(rc == true, "pmHDUWriteMask() returned TRUE");
+        // 4. Close the FITS file, free memory
+        psFree(hdu);
+        psFitsClose(fitsFileW);
+
+        // Now, try to read that FITS file from disk.
+        // 5. Read that fits file from disk
+        psFits* fitsFileR = psFitsOpen(".tmp00", "r");
+        ok(fitsFileR != NULL, "psFitsOpen() was successful");
+        // 6. Create another pmHDU
+        hdu = pmHDUAlloc("extname");
+        hdu->masks = NULL;
+        // 7. Call pmHDUReadMask()which will read the FITS data into hdu->masks
+        rc = pmHDUReadMask(hdu, fitsFileR);
+        ok(rc == true, "pmHDUReadMask() returned TRUE");
+        ok(hdu->masks != NULL, "pmHDUReadMask() created hdu->masks");
+        // 8. Verify that hdu->masks was set correctly.
+        bool errorFlag = false;
+        for (int k = 0 ; k < NUM_HDUS ; k++) {
+            psImage *img = (psImage *) hdu->masks->data[k];
+            for (psS32 i = 0 ; i < img->numRows ; i++) {
+                for (psS32 j = 0 ; j < img->numCols ; j++) {
+                    if (((float) k+BASE) != img->data.F32[i][j]) {
+                        errorFlag = true;
+                        diag("TEST ERROR (image %d): img[%d][%d] is %f, should be %f\n",
+                              k, i, j, img->data.F32[i][j], ((float)k+BASE));
+        	    }
+            	}
+	    }
+	}
+        ok(!errorFlag, "pmHDUReadMask() correctly returned the image data");
+        psFitsClose(fitsFileR);
+        psFree(hdu);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmHDUReadHeader(), pmHDUWriteHeader() tests
+    // Test pmHDUReadHeader() with NULL pmHDU input argument
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen("../dataFiles/sampleFitsFile.fits", "r");
+        if (!fitsFile) {
+            fitsFile = psFitsOpen("dataFiles/sampleFitsFile.fits", "r");
+	}
+        ok(fitsFile != NULL, "psFitsOpen() was successful");
+        bool rc = pmHDUReadHeader(NULL, fitsFile);
+        ok(rc == false, "pmHDUReadHeader() returned FALSE with NULL psHDU as input");
+        psFitsClose(fitsFile);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDUReadHeader() with NULL psFits input argument
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUAlloc("extname");
+        hdu->header = psMetadataAlloc();
+        bool rc = pmHDUReadHeader(hdu, NULL);
+        ok(rc == false, "pmHDUReadHeader() returned FALSE with NULL psFits file as input");
+        psFree(hdu);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmHDUReadHeader() and pmHDUWriteHeader()
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFileW = psFitsOpen(fitsFilename, "w");
+        ok(fitsFileW != NULL, "psFitsOpen() opened the FITS file");
+        char extname[80];
+        for (int lcv = 0; lcv < NUM_HDUS; lcv++) {
+            snprintf(extname, 80, "ext-%d", lcv);
+            pmHDU *hdu = pmHDUAlloc(extname);
+            hdu->header = psMetadataAlloc();
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYINT", PS_DATA_S32,
+                         "psS32 Item", (psS32)lcv);
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYFLT", PS_DATA_F32,
+                         "psF32 Item", (float)(1.0f/(float)(1+lcv)));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYDBL", PS_DATA_F64,
+                         "psF64 Item", (double)(1.0/(double)(1+lcv)));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYBOOL", PS_DATA_BOOL,
+                         "psBool Item", (lcv%2 == 0));
+            psMetadataAdd(hdu->header, PS_LIST_TAIL, "MYSTR", PS_DATA_STRING,
+                         "psStr Item", extname);
+
+            bool rc = pmConfigFileRead(&hdu->format, "../dataFiles/camera0/format0.config", "Camera 0 Config Format");
+            if (!rc) {
+                rc = pmConfigFileRead(&hdu->format, "dataFiles/camera0/format0.config", "Camera 0 Config Format");
+	    }
+            ok(rc == true, "pmConfigFileRead() was successful");
+            rc = pmHDUWrite(hdu, fitsFileW);
+            ok(rc == true, "pmHDUWrite() successfully wrote the header");
+            psFree(hdu);
+        }
+        psFitsClose(fitsFileW);
+
+        psFits* fitsFileR = psFitsOpen(fitsFilename, "r");
+        ok(fitsFileR != NULL, "psFitsOpen returned non-NULL on existing file");
+        int numHDUs = psFitsGetSize(fitsFileR);
+        ok(numHDUs == NUM_HDUS, "The test FITS file has %d HDUs", numHDUs);
+
+        for (int hdunum = 0; hdunum < NUM_HDUS; hdunum++)
+        {
+            char extname[80];
+            snprintf(extname,80, "ext-%d", hdunum);
+            psFitsMoveExtNum(fitsFileR, hdunum, false);
+            pmHDU *hdu = pmHDUAlloc(extname);
+            bool rc = pmHDUReadHeader(hdu, fitsFileR);
+            ok(rc == true, "pmHDUReadHeader() returned TRUE");
+            ok(hdu->header != NULL && psMemCheckMetadata(hdu->header),
+              "pmHDUReadHeader() correctly returned the hdu->header member");
+
+            psS32 intItem = psMetadataLookupS32(NULL, hdu->header, "MYINT");
+            ok(intItem == hdunum, "Retrieved psS32 metadata item from file");
+
+            psF32 fltItem = psMetadataLookupF32(NULL, hdu->header, "MYFLT");
+            ok(fabsf(fltItem - 1.0f/(float)(1+hdunum)) <= FLT_EPSILON,
+               "Retrieved psF32 metadata item from file.  Got %f vs %f",
+               fltItem,1.0f/(float)(1+hdunum));
+
+            psF64 dblItem = psMetadataLookupF64(NULL, hdu->header, "MYDBL");
+            ok(abs(dblItem - 1.0/(double)(1+hdunum)) <= DBL_EPSILON,
+               "Retrieved psF64 metadata item from file.  Got %g vs %g",
+               dblItem, 1.0/(double)(1+hdunum));
+
+            psMetadataItem* boolItem = psMetadataLookup(hdu->header, "MYBOOL");
+            ok(boolItem != NULL && boolItem->type == PS_DATA_BOOL,
+               "Retrieved psBool metadata item from file");
+
+            psString strItem = psMetadataLookupStr(NULL, hdu->header, "MYSTR");
+            ok(strItem != NULL && strncmp(strItem,extname,strlen(extname)) == 0,
+               "Retrieved string metadata item from file.  Got '%s' vs '%s' (%d)",
+               strItem,extname,strlen(extname));
+
+            psFree(hdu);
+        }
+        psFitsClose(fitsFileR);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmHDUUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmHDUUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmHDUUtils.c	(revision 22322)
@@ -0,0 +1,480 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+*/
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    chip->hdu = pmHDUAlloc("chipExtName");
+
+    bool rc = pmConfigFileRead(&chip->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&chip->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleChip())");
+	}
+    }
+
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    fpa->hdu = pmHDUAlloc("fpaExtName");
+    bool rc = pmConfigFileRead(&fpa->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&fpa->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleFPA())");
+	}
+    }
+
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(73);
+
+
+    // ----------------------------------------------------------------------
+    // pmHDUFromFPA() tests
+    // Call pmHDUFromFPA() with NULL input params
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUFromFPA(NULL);
+        ok(hdu == NULL, "pmHDUFromFPA(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUFromFPA() with acceptable input params
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmHDU *hdu = pmHDUFromFPA(fpa);
+        ok(hdu == fpa->hdu, "pmHDUFromFPA(NULL) returned the correct pmHDU of an pmFPA struct");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmHDUFromChip() tests
+    // Call pmHDUFromChip() with NULL input params
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUFromChip(NULL);
+        ok(hdu == NULL, "pmHDUFromChip(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUFromChip() with acceptable input params
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmHDU *hdu = pmHDUFromChip(chip);
+        ok(hdu == chip->hdu, "pmHDUFromChip() returned the correct pmHDU of an pmChip struct");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUFromChip() with acceptable input params
+    // Set chip->hdu to NULL, verify chip->parent->hdu is returned
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        psFree(chip->hdu);
+        chip->hdu = NULL;
+        pmHDU *hdu = pmHDUFromChip(chip);
+        ok(hdu == chip->parent->hdu, "pmHDUFromChip() returned the correct pmHDU of an pmChip struct");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmHDUFromCell() tests
+    // Call pmHDUFromCell() with NULL input params
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUFromCell(NULL);
+        ok(hdu == NULL, "pmHDUFromCell(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUFromCell() with acceptable input params
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmHDU *hdu = pmHDUFromCell(cell);
+        ok(hdu == cell->hdu, "pmHDUFromCell() returned the correct pmHDU of an pmCell struct");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUFromCell() with acceptable input params
+    // Set cell->hdu to NULL, verify cell->parent->hdu is returned
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        psFree(cell->hdu);
+        cell->hdu = NULL;
+        pmHDU *hdu = pmHDUFromCell(cell);
+        ok(hdu == cell->parent->hdu, "pmHDUFromCell() returned the correct pmHDU of an pmCell struct");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmHDUFromReadout() tests
+    // Call pmHDUFromReadout() with NULL input params
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUFromReadout(NULL);
+        ok(hdu == NULL, "pmHDUFromReadout(NULL) returned NULL");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUFromReadout() with acceptable input params
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        pmReadout *readout = cell->readouts->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        ok(readout != NULL, "Allocated a pmReadout successfully");
+        pmHDU *hdu = pmHDUFromReadout(readout);
+        ok(hdu == readout->parent->hdu, "pmHDUFromReadout() returned the correct pmHDU of an pmReadout struct");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmHDUGetLowest() tests
+    // Call pmHDUGetLowest() with all NULL inputs
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUGetLowest(NULL, NULL, NULL);
+        ok(hdu == NULL, "pmHDUFromReadout(NULL, NULL, NULL)");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUGetLowest() with all acceptable inputs
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell);
+        ok(hdu == cell->hdu, "pmHDUGetLowest(fpa, chip, cell)");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUGetLowest() with all (fpa, chip, NULL) inputs
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmHDU *hdu = pmHDUGetLowest(fpa, chip, NULL);
+        ok(hdu == chip->hdu, "pmHDUGetLowest(fpa, chip, NULL)");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUGetLowest() with all (fpa, NULL, NULL) inputs
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmHDU *hdu = pmHDUGetLowest(fpa, NULL, NULL);
+        ok(hdu == fpa->hdu, "pmHDUGetLowest(fpa, NULL, NULL)");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    //pmHDUGetHighest() tests
+    // Call pmHDUGetHighest() with all NULL inputs
+    {
+        psMemId id = psMemGetId();
+        pmHDU *hdu = pmHDUGetHighest(NULL, NULL, NULL);
+        ok(hdu == NULL, "pmHDUFromReadout(NULL, NULL, NULL)");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUGetHighest() with all acceptable inputs
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmHDU *hdu = pmHDUGetHighest(fpa, chip, cell);
+        ok(hdu == fpa->hdu, "pmHDUGetHighest(fpa, chip, cell)");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUGetHighest() with (NULL, chip, cell) inputs
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmHDU *hdu = pmHDUGetHighest(NULL, chip, cell);
+        ok(hdu == chip->hdu, "pmHDUGetHighest(NULL, chip, cell)");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmHDUGetHighest() with (NULL, NULL, cell) inputs
+    {
+        psMemId id = psMemGetId();
+        // Generate the pmFPA heirarchy
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA* fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        ok(fpa != NULL, "Allocated a pmFPA successfully");
+        ok(chip != NULL, "Allocated a pmChip successfully");
+        ok(cell != NULL, "Allocated a pmCell successfully");
+        pmHDU *hdu = pmHDUGetHighest(NULL, NULL, cell);
+        ok(hdu == cell->hdu, "pmHDUGetHighest(NULL, NULL, cell)");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmReadoutFake.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmReadoutFake.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmReadoutFake.c	(revision 22322)
@@ -0,0 +1,177 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    Only one function in the source code: pmReadoutFakeFromSources()
+
+    pmReadoutFakeFromSources() is only tested with bad input parameters.
+    Tests must be written to exercise it with legitimate input sources, and
+    verify the output.
+*/
+
+#define MISC_NUM                32
+#define MISC_NAME               "META00"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           5
+#define TEST_NUM_COLS           8
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define NUM_SOURCES		5
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    psImageInit(readout->image, 1.0);
+    psImageInit(readout->mask, 2);
+    psImageInit(readout->weight, 3.0);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(11);
+
+    // ------------------------------------------------------------------------
+    // pmReadoutFakeFromSources() tests
+    // bool pmReadoutFakeFromSources(pmReadout *readout, int numCols, int numRows, const psArray *sources,
+    //                               const psVector *xOffset, const psVector *yOffset, const pmPSF *psf,
+    //                               float minFlux, int radius, bool circularise)
+    //
+    // Call pmReadoutFakeFromSources() with bad input parameters.
+    {
+        psMemId id = psMemGetId();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        psVector *xOffset = psVectorAlloc(NUM_SOURCES, PS_TYPE_S32);
+        psVector *yOffset = psVectorAlloc(NUM_SOURCES, PS_TYPE_S32);
+        psVector *xOffsetBig = psVectorAlloc(NUM_SOURCES*2, PS_TYPE_S32);
+        psVector *xOffsetF32 = psVectorAlloc(NUM_SOURCES, PS_TYPE_F32);
+        psVector *yOffsetF32 = psVectorAlloc(NUM_SOURCES, PS_TYPE_F32);
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0; i < sources->n ; i++) {
+            sources->data[i] = pmSourceAlloc();
+        }
+        pmPSFOptions *psfOptions = pmPSFOptionsAlloc();
+        psfOptions->psfTrendNx = 1;
+        psfOptions->psfTrendNy = 2;
+        psfOptions->psfFieldNx = 3;
+        psfOptions->psfFieldNy = 4;
+        psfOptions->psfFieldXo = 5;
+        psfOptions->psfFieldYo = 6;
+        pmModelClassInit();
+        psfOptions->type = pmModelClassGetType("PS_MODEL_GAUSS");
+        pmPSF *psf = pmPSFAlloc(psfOptions);
+
+        // NULL pmReadout input parameter
+        bool rc = pmReadoutFakeFromSources(NULL, TEST_NUM_COLS, TEST_NUM_ROWS, sources,
+                                           xOffset, yOffset, psf, 0.0, 1.0, false);
+        ok(rc == false, "pmReadoutFakeFromSources() returned FALSE with pmReadout input parameter");
+
+        // Non-positive numCols input parameter
+        rc = pmReadoutFakeFromSources(readout, 0, TEST_NUM_ROWS, sources,
+                                           xOffset, yOffset, psf, 0.0, 1.0, false);
+        ok(rc == false, "pmReadoutFakeFromSources() returned FALSE with Non-positive numCols input parameter");
+
+        // Non-positive numRows input parameter
+        rc = pmReadoutFakeFromSources(readout, TEST_NUM_COLS, 0, sources,
+                                           xOffset, yOffset, psf, 0.0, 1.0, false);
+        ok(rc == false, "pmReadoutFakeFromSources() returned FALSE with Non-positive numRow input parameter");
+
+        // NULL pmSource input parameter
+        rc = pmReadoutFakeFromSources(readout, TEST_NUM_COLS, TEST_NUM_ROWS, NULL,
+                                           xOffset, yOffset, psf, 0.0, 1.0, false);
+        ok(rc == false, "pmReadoutFakeFromSources() returned FALSE with pmSource input parameter");
+
+        // NULL pmPSF input parameter
+        rc = pmReadoutFakeFromSources(readout, TEST_NUM_COLS, TEST_NUM_ROWS, sources,
+                                           xOffset, yOffset, NULL, 0.0, 1.0, false);
+        ok(rc == false, "pmReadoutFakeFromSources() returned FALSE with input parameter");
+
+        // NULL incorrect type xOffset input parameter
+        rc = pmReadoutFakeFromSources(readout, TEST_NUM_COLS, TEST_NUM_ROWS, sources,
+                                           xOffsetF32, yOffset, psf, 0.0, 1.0, false);
+        ok(rc == false, "pmReadoutFakeFromSources() returned FALSE with incorrect type xOffset input parameter");
+
+        // NULL incorrect type yOffset input parameter
+        rc = pmReadoutFakeFromSources(readout, TEST_NUM_COLS, TEST_NUM_ROWS, sources,
+                                           xOffset, yOffsetF32, psf, 0.0, 1.0, false);
+        ok(rc == false, "pmReadoutFakeFromSources() returned FALSE with incorrect type yOffset input parameter");
+
+        // NULL incorrect size xOffset input parameter
+        rc = pmReadoutFakeFromSources(readout, TEST_NUM_COLS, TEST_NUM_ROWS, sources,
+                                           xOffsetBig, yOffset, psf, 0.0, 1.0, false);
+        ok(rc == false, "pmReadoutFakeFromSources() returned FALSE with incorrect size xOffset input parameter");
+
+        psFree(readout);
+        psFree(xOffset);
+        psFree(yOffset);
+        psFree(xOffsetBig);
+        psFree(xOffsetF32);
+        psFree(yOffsetF32);
+        psFree(sources);
+        psFree(psfOptions);
+        psFree(psf);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmReadoutFakeFromSources() with acceptable input parameters.
+    {
+        psMemId id = psMemGetId();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        psVector *xOffset = psVectorAlloc(NUM_SOURCES, PS_TYPE_S32);
+        psVector *yOffset = psVectorAlloc(NUM_SOURCES, PS_TYPE_S32);
+        psVector *xOffsetF32 = psVectorAlloc(NUM_SOURCES, PS_TYPE_F32);
+        psVector *yOffsetF32 = psVectorAlloc(NUM_SOURCES, PS_TYPE_F32);
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0; i < sources->n ; i++) {
+            sources->data[i] = pmSourceAlloc();
+        }
+        pmPSFOptions *psfOptions = pmPSFOptionsAlloc();
+        psfOptions->psfTrendNx = 1;
+        psfOptions->psfTrendNy = 2;
+        psfOptions->psfFieldNx = 3;
+        psfOptions->psfFieldNy = 4;
+        psfOptions->psfFieldXo = 5;
+        psfOptions->psfFieldYo = 6;
+        pmModelClassInit();
+        psfOptions->type = pmModelClassGetType("PS_MODEL_GAUSS");
+        pmPSF *psf = pmPSFAlloc(psfOptions);
+
+        bool rc = pmReadoutFakeFromSources(readout, TEST_NUM_COLS, TEST_NUM_ROWS, sources,
+                                           xOffset, yOffset, psf, 0.0, 1.0, false);
+        ok(rc == true, "pmReadoutFakeFromSources() returned TRUE with acceptable input parameters");
+
+        psFree(readout);
+        psFree(xOffset);
+        psFree(yOffset);
+        psFree(xOffsetF32);
+        psFree(yOffsetF32);
+        psFree(sources);
+        psFree(psfOptions);
+        psFree(psf);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmReadoutStack.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmReadoutStack.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/camera/tap_pmReadoutStack.c	(revision 22322)
@@ -0,0 +1,250 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+*/
+
+#define CELL_ALLOC_NAME		"CellName"
+#define MISC_NUM		32
+#define MISC_NAME		"META00"
+#define NUM_BIAS_DATA		10
+#define TEST_NUM_ROWS		5
+#define TEST_NUM_COLS		8
+#define NUM_INPUTS		10
+#define NUM_READOUTS		4
+#define VERBOSE			0
+#define ERR_TRACE_LEVEL		10
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    psImageInit(readout->image, 1.0);
+    psImageInit(readout->mask, 2);
+    psImageInit(readout->weight, 3.0);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(int ID)
+{
+    pmCell *cell = pmCellAlloc(NULL, CELL_ALLOC_NAME);
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    cell->hdu = pmHDUAlloc(NULL);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psRegion *region = psRegionAlloc(0.0, (float) (10 + ID), 0.0, (float) (20 + ID));
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    return(cell);
+}
+
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(29);
+
+    // ------------------------------------------------------------------------
+    // pmReadoutUpdateSize() tests
+    // Call pmReadoutUpdateSize() with NULL pmReadout input parameter.
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmReadoutUpdateSize(NULL, 0, 0, TEST_NUM_COLS, TEST_NUM_ROWS, false);
+        ok(rc == false, "pmReadoutUpdateSize() returned FALSE with NULL pmReadout input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmReadoutUpdateSize() with acceptable input parameters (mask == false).
+    {
+        psMemId id = psMemGetId();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        bool rc = pmReadoutUpdateSize(readout, 0, 0, 2*TEST_NUM_COLS, 2*TEST_NUM_ROWS, false);
+        ok(rc == true, "pmReadoutUpdateSize() returned TRUE with acceptable input parameters");
+        ok(readout->image->numCols == (2*TEST_NUM_COLS) &&
+           readout->image->numRows == (2*TEST_NUM_ROWS), "pmReadoutUpdateSize() generated the correct size pmReadout->image");
+        bool errorFlag = false;
+        for (int i = 0 ; i < readout->image->numRows ; i++) {
+            for (int j = 0 ; j < readout->image->numCols ; j++) {
+                psF32 correctF32;
+                if (i < TEST_NUM_ROWS && j < TEST_NUM_COLS) {
+                   correctF32 = 1.0;
+                } else {
+                   correctF32 = 0.0;
+                }
+                if (readout->image->data.F32[i][j] != correctF32) {
+                    diag("ERROR: readout->image[%d][%d] is %.2f, should be %.2f", i, j, readout->image->data.F32[i][j], correctF32);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmReadoutUpdateSize() initialized pmReadout->image to zero");
+        ok(readout->mask->numCols == (TEST_NUM_COLS) &&
+           readout->mask->numRows == (TEST_NUM_ROWS), "pmReadoutUpdateSize() generated the correct size pmReadout->mask");
+
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmReadoutUpdateSize() with acceptable input parameters (mask == true).
+    {
+        psMemId id = psMemGetId();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        bool rc = pmReadoutUpdateSize(readout, 0, 0, 2*TEST_NUM_COLS, 2*TEST_NUM_ROWS, true);
+        ok(rc == true, "pmReadoutUpdateSize() returned TRUE with acceptable input parameters");
+        ok(readout->image->numCols == (2*TEST_NUM_COLS) &&
+           readout->image->numRows == (2*TEST_NUM_ROWS), "pmReadoutUpdateSize() generated the correct size pmReadout->image");
+        bool errorFlag = false;
+        for (int i = 0 ; i < readout->image->numRows ; i++) {
+            for (int j = 0 ; j < readout->image->numCols ; j++) {
+                psF32 correctF32;
+                if (i < TEST_NUM_ROWS && j < TEST_NUM_COLS) {
+                   correctF32 = 1.0;
+                } else {
+                   correctF32 = 0.0;
+                }
+                if (readout->image->data.F32[i][j] != correctF32) {
+                    diag("ERROR: readout->image[%d][%d] is %.2f, should be %.2f", i, j, readout->image->data.F32[i][j], correctF32);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmReadoutUpdateSize() initialized pmReadout->image to zero");
+        ok(readout->mask->numCols == (2*TEST_NUM_COLS) &&
+           readout->mask->numRows == (2*TEST_NUM_ROWS), "pmReadoutUpdateSize() generated the correct size pmReadout->mask");
+        errorFlag = false;
+        for (int i = 0 ; i < readout->mask->numRows ; i++) {
+            for (int j = 0 ; j < readout->mask->numCols ; j++) {
+                psF32 correctU8;
+                if (i < TEST_NUM_ROWS && j < TEST_NUM_COLS) {
+                   correctU8 = 2;
+                } else {
+                   correctU8 = 0;
+                }
+                if (readout->mask->data.U8[i][j] != correctU8) {
+                    diag("ERROR: readout->mask[%d][%d] is %d, should be %d", i, j, readout->mask->data.U8[i][j], correctU8);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmReadoutUpdateSize() initialized pmReadout->mask to zero");
+
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ------------------------------------------------------------------------
+    // pmReadoutStackValidate() tests
+    // Call pmReadoutStackValidate() with bad input parameters.
+    {
+        psMemId id = psMemGetId();
+        int minInputCols, maxInputCols, minInputRows, maxInputRows, numCols, numRows;
+        minInputCols = maxInputCols = minInputRows = maxInputRows = numCols = numRows = 0;
+        bool rc = pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows,
+                                         &numCols, &numRows, NULL);
+        psArray *inputs = psArrayAlloc(NUM_INPUTS);
+        for (int i = 0 ; i < NUM_INPUTS ; i++) {
+            inputs->data[i] = (psPtr *) generateSimpleReadout(NULL);
+        }
+
+        // NULL psArray input parameter
+        rc = pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows,
+                                   &numCols, &numRows, NULL);
+        ok(rc == false, "pmReadoutStackValidate() returned FALSE with NULL pmArray input parameter");
+
+        // NULL minInputColsPtr
+        rc = pmReadoutStackValidate(NULL, &maxInputCols, &minInputRows, &maxInputRows,
+                                    &numCols, &numRows, inputs);
+        ok(rc == false, "pmReadoutStackValidate() returned FALSE with NULL minInputColsPtr input parameter");
+
+        // NULL maxInputColsPtr
+        rc = pmReadoutStackValidate(&minInputCols, NULL, &minInputRows, &maxInputRows,
+                                    &numCols, &numRows, inputs);
+        ok(rc == false, "pmReadoutStackValidate() returned FALSE with NULL maxInputColsPtr input parameter");
+
+        // NULL minInputRowsPtr
+        rc = pmReadoutStackValidate(&minInputCols, &maxInputCols, NULL, &maxInputRows,
+                                    &numCols, &numRows, inputs);
+        ok(rc == false, "pmReadoutStackValidate() returned FALSE with NULL minInputRowsPtr input parameter");
+
+        // NULL maxInputRowsPtr
+        rc = pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, NULL,
+                                    &numCols, &numRows, inputs);
+        ok(rc == false, "pmReadoutStackValidate() returned FALSE with NULL maxInputRowsPtr input parameter");
+
+        // NULL numColsPtr
+        rc = pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows,
+                                    NULL, &numRows, inputs);
+        ok(rc == false, "pmReadoutStackValidate() returned FALSE with NULL numColsPtr input parameter");
+
+        // NULL numRowsPtr
+        rc = pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows,
+                                    &numCols, NULL, inputs);
+        ok(rc == false, "pmReadoutStackValidate() returned FALSE with NULL numRowsPtr input parameter");
+
+        psFree(inputs);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmReadoutStackValidate() with acceptable input parameters.
+    {
+        psMemId id = psMemGetId();
+        int minInputCols, maxInputCols, minInputRows, maxInputRows, numCols, numRows;
+        minInputCols = maxInputCols = minInputRows = maxInputRows = numCols = numRows = 0;
+        pmCell *cells[NUM_INPUTS];
+        psArray *inputs = psArrayAlloc(NUM_INPUTS);
+        for (int i = 0 ; i < NUM_INPUTS ; i++) {
+            cells[i] = generateSimpleCell(i);
+            inputs->data[i] = (psPtr *) generateSimpleReadout(cells[i]);
+        }
+        bool rc = pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows,
+                                   &numCols, &numRows, inputs);
+        ok(rc == true, "pmReadoutStackValidate() returned TRUE with acceptable input parameters");
+        ok(minInputCols == 0, "pmReadoutStackValidate() set minInputCols correctly");
+        ok(maxInputCols == TEST_NUM_COLS, "pmReadoutStackValidate() set maxInputCols correctly");
+        ok(minInputRows == 0, "pmReadoutStackValidate() set minInputRows correctly");
+        ok(maxInputRows == TEST_NUM_ROWS, "pmReadoutStackValidate() set maxInputRows correctly");
+        ok(numCols == (10 + NUM_INPUTS - 1), "pmReadoutStackValidate() set numCols correctly");
+        ok(numRows == (20 + NUM_INPUTS - 1), "pmReadoutStackValidate() set numRows correctly");
+
+        for (int i = 0 ; i < NUM_INPUTS ; i++) {
+            psFree(cells[i]);
+        }
+        psFree(inputs);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/concepts/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/concepts/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/concepts/.cvsignore	(revision 22322)
@@ -0,0 +1,4 @@
+.deps
+.libs
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/psModules/test/concepts/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/concepts/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/concepts/Makefile.am	(revision 22322)
@@ -0,0 +1,29 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSMODULES_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpsmodules.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSMODULES_LIBS)
+
+TEST_PROGS = \
+	tap_pmConcepts \
+	tap_pmConceptsUpdate \
+	tap_pmConceptsPhotcode \
+	tap_pmConceptsAverage
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
+	$(top_srcdir)/test/test.pl
Index: /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConcepts.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConcepts.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConcepts.c	(revision 22322)
@@ -0,0 +1,499 @@
+    /** @file tst_pmConcepts.c
+ *
+ *  @brief Contains the tests for pmConcepts.c:
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-01-02 20:49:56 $
+*   pmConceptSpecAlloc()
+    pmConceptsList()
+*   pmConceptGetRequired()
+*   pmConceptSetRequired()
+    pmConceptRegister()
+    pmConceptsRead()
+*   pmConceptsBlankFPA()
+    pmConceptsReadFPA()
+    pmConceptsWriteFPA()
+*   pmConceptsBlankChip()
+    pmConceptsReadChip()
+    pmConceptsWriteChip()
+*   pmConceptsBlankCell()
+    pmConceptsReadCell()
+    pmConceptsWriteCell()
+*   pmConceptsInit()
+*   pmConceptsDone()
+    pmFPACopyConcepts()
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psMetadataItem *dummyConceptParser(
+    const psMetadataItem *concept,
+    const psMetadataItem *pattern,
+    pmConceptSource source,
+    const psMetadata *cameraFormat,
+    const pmFPA *fpa,
+    const pmChip *chip,
+    const pmCell *cell)
+{
+    if (concept == NULL ||
+        pattern == NULL ||
+        source == PM_CONCEPT_SOURCE_NONE ||
+        cameraFormat == NULL ||
+        fpa == NULL ||
+        chip == NULL ||
+        cell == NULL) {
+        printf("dummyConceptParser() args are NULL\n");
+    }
+    return(NULL);
+}
+
+// FPA.RA and FPA.DEC
+psMetadataItem *dummyConceptFormatter(
+    const psMetadataItem *concept,
+    pmConceptSource source,
+    const psMetadata *cameraFormat,
+    const pmFPA *fpa,
+    const pmChip *chip,
+    const pmCell *cell)
+{
+    if (concept == NULL ||
+        source == PM_CONCEPT_SOURCE_NONE ||
+        cameraFormat == NULL ||
+        fpa == NULL ||
+        chip == NULL ||
+        cell == NULL) {
+        printf("dummyConceptFormatter() args are NULL\n");
+    }
+    return(NULL);
+}
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel(".", ERR_TRACE_LEVEL);
+    plan_tests(126);
+    
+    // --------------------------------------------------------------------
+    // Tests for pmConceptSpecAlloc()
+    // Acceptable input parameters.
+    {
+        psMemId id = psMemGetId();
+        psMetadataItem *blank = psMetadataItemAlloc("myItem1", PS_DATA_BOOL, "I am a boolean", true);
+        pmConceptSpec *tmp = pmConceptSpecAlloc(blank, dummyConceptParser,
+            dummyConceptFormatter, true);
+        ok(tmp != NULL, "pmConceptSpecAlloc() returned non-NULL");
+        skip_start(tmp == NULL, 4, "Skipping tests because pmConceptSpecAlloc() returned NULL");
+        ok(tmp->blank == blank, "pmConceptSpecAlloc() set the ->blank member correctly");
+        ok(tmp->parse == dummyConceptParser, "pmConceptSpecAlloc() set the ->parse member correctly");
+        ok(tmp->format == dummyConceptFormatter, "pmConceptSpecAlloc() set the ->format member correctly");
+        ok(tmp->required == true, "pmConceptSpecAlloc() set the ->required member correctly");
+        skip_end();
+        psFree(tmp);
+        psFree(blank);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // NULL input parameters.
+    {
+        psMemId id = psMemGetId();
+        pmConceptSpec *tmp = pmConceptSpecAlloc(NULL, NULL, NULL, false);
+        ok(tmp != NULL, "pmConceptSpecAlloc() returned non-NULL with NULL inputs");
+        psFree(tmp);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------------
+    // Tests for pmConceptGetRequired() and pmConceptSetRequired()
+    // Acceptable input parameters.
+    // We get the "required" for FPA.TELESCOPE, ensure that it is false
+    // then set it to true, then ensure that it is true.
+    {
+        psMemId id = psMemGetId();
+        bool tmpBool = pmConceptGetRequired("FPA.TELESCOPE", PM_FPA_LEVEL_FPA);
+        ok (tmpBool == false, "pmConceptGetRequired() returned true for FPA.TELESCOPE");
+        tmpBool = pmConceptSetRequired("FPA.TELESCOPE", PM_FPA_LEVEL_FPA, true);
+        ok (tmpBool == true, "pmConceptSetRequired() returned true for FPA.TELESCOPE");
+        tmpBool = pmConceptGetRequired("FPA.TELESCOPE", PM_FPA_LEVEL_FPA);
+        ok (tmpBool == true, "pmConceptGetRequired() returned true for FPA.TELESCOPE");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test pmConceptGetRequired() with a few incorrect concept names
+    // and/or levels.
+    {
+        psMemId id = psMemGetId();
+        bool tmpBool = pmConceptGetRequired("FPA.TELESCOPE", PM_FPA_LEVEL_CHIP);
+        ok (tmpBool == false, "pmConceptGetRequired() returned false for FPA.TELESCOPE with wrong level (CHIP)");
+        tmpBool = pmConceptGetRequired("FPA.TELESCOPE", PM_FPA_LEVEL_CELL);
+        ok (tmpBool == false, "pmConceptGetRequired() returned false for FPA.TELESCOPE with wrong level (CELL)");
+        tmpBool = pmConceptGetRequired("FPA.BADCONCEPTNAME", PM_FPA_LEVEL_FPA);
+        ok (tmpBool == false, "pmConceptGetRequired() returned false for FPA.BADCONCEPTNAME");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------------
+    // Tests for pmConceptsInit(), pmConceptsDone()
+    // We determine if pmConceptsInit() was successful by looking at the
+    // "required" for the CHIP.XPARITY concept at level PM_FPA_LEVEL_CHIP
+    {
+        psMemId id = psMemGetId();
+        pmConceptsDone();
+        // pmConceptsInit() should return TRUE after pmConceptsDone() is called
+        ok(true == pmConceptsInit(), "pmConceptsInit() returned TRUE");
+
+        // FPA concepts
+        ok(false == pmConceptGetRequired("FPA.TELESCOPE", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.TELESCOPE at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.INSTRUMENT", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.INSTRUMENT at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.DETECTOR", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.DETECTOR at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.CAMERA", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.CAMERA at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.FOCUS", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.FOCUS at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.AIRMASS", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.AIRMASS at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.FILTERID", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.FILTERID at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.FILTER", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.FILTER at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.POSANGLE", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.POSANGLE at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.RADECSYS", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.RADECSYS at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.RA", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.RA at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.DEC", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.DEC at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.OBSTYPE", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.OBSTYPE at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.OBJECT", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.OBJECT at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.ALT", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.ALT at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.AZ", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.AZ at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.TIMESYS", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.TIMESYS at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.TIME", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.TIME at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.TEMP", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.TEMP at level: FPA");
+        ok(false == pmConceptGetRequired("FPA.EXPOSURE", PM_FPA_LEVEL_FPA),
+          "pmConceptGetRequired() returned false for FPA.EXPOSURE at level: FPA");
+
+        // Chip concepts
+        ok(true == pmConceptGetRequired("CHIP.XPARITY", PM_FPA_LEVEL_CHIP),
+          "pmConceptGetRequired() returned true for CHIP.XPARITY at level: CHIP");
+        ok(true == pmConceptGetRequired("CHIP.YPARITY", PM_FPA_LEVEL_CHIP),
+          "pmConceptGetRequired() returned true for CHIP.YPARITY at level: CHIP");
+        ok(true == pmConceptGetRequired("CHIP.X0", PM_FPA_LEVEL_CHIP),
+          "pmConceptGetRequired() returned true for CHIP.X0 at level: CHIP");
+        ok(true == pmConceptGetRequired("CHIP.Y0", PM_FPA_LEVEL_CHIP),
+          "pmConceptGetRequired() returned true for CHIP.Y0 at level: CHIP");
+        ok(true == pmConceptGetRequired("CHIP.XSIZE", PM_FPA_LEVEL_CHIP),
+          "pmConceptGetRequired() returned true for CHIP.XSIZE at level: CHIP");
+        ok(true == pmConceptGetRequired("CHIP.YSIZE", PM_FPA_LEVEL_CHIP),
+          "pmConceptGetRequired() returned true for CHIP.YSIZE at level: CHIP");
+        ok(false == pmConceptGetRequired("CHIP.TEMP", PM_FPA_LEVEL_CHIP),
+          "pmConceptGetRequired() returned true for CHIP.TEMP at level: CHIP");
+
+        // Cell concepts
+        ok(true == pmConceptGetRequired("CELL.GAIN", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.GAIN at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.READNOISE", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.READNOISE at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.SATURATION", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.SATURATION at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.BAD", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.BAD at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.XPARITY", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.XPARITY at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.YPARITY", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.YPARITY at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.READDIR", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.READDIR at level: CELL");
+        ok(false == pmConceptGetRequired("CELL.EXPOSURE", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned false for CELL.EXPOSURE at level: CELL");
+        ok(false == pmConceptGetRequired("CELL.DARKTIME", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned false for CELL.DARKTIME at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.TRIMSEC", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.TRIMSEC at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.BIASSEC", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.BIASSEC at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.XBIN", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.XBIN at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.YBIN", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.YBIN at level: CELL");
+        ok(false == pmConceptGetRequired("CELL.TIMESYS", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned false for CELL.TIMESYS at level: CELL");
+        ok(false == pmConceptGetRequired("CELL.TIME", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned false for CELL.TIME at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.X0", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.X0 at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.Y0", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.Y0 at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.XSIZE", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.XSIZE at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.YSIZE", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.YSIZE at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.XWINDOW", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.XWINDOW at level: CELL");
+        ok(true == pmConceptGetRequired("CELL.YWINDOW", PM_FPA_LEVEL_CELL),
+          "pmConceptGetRequired() returned true for CELL.YWINDOW at level: CELL");
+
+        // The 2nd pmConceptsInit() should return FALSE after pmConceptsInit() is called
+        ok(false == pmConceptsInit(), "pmConceptsInit() returned FALSE");
+
+        pmConceptsDone();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // --------------------------------------------------------------------
+    // Tests for pmConceptsBlankFPA()
+    // Verify error with NULL input.
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmConceptsBlankFPA(NULL);
+        ok(rc == false, "pmConceptsBlankFPA() returned FALSE with NULL input");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // -----------------------------------------------------------------------------
+    // Tests for pmConceptsBlankFPA()
+    // Call with valid data.  We test by ensuring the first 5 metadata items were
+    // added to fpa->concepts.
+    {
+        psMemId id = psMemGetId();
+        bool mdok;
+        char *tmpStr;
+        psF32 tmpF32;
+        psF64 tmpF64;
+        psS32 tmpS32;
+        pmFPA *fpa = pmFPAAlloc(NULL);
+        bool rc = false;
+
+        // First junk items to fpa->concepts so that we know they are later blanked.
+        rc|= psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.TELESCOPE", PS_META_REPLACE, "", "JUNK");
+        rc|= psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.INSTRUMENT", PS_META_REPLACE, "", "JUNK");
+        rc|= psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.DETECTOR", PS_META_REPLACE, "", "JUNK");
+        rc|= psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.CAMERA", PS_META_REPLACE, "", "JUNK");
+        rc|= psMetadataAddF32(fpa->concepts, PS_LIST_TAIL, "FPA.FOCUS", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddF32(fpa->concepts, PS_LIST_TAIL, "FPA.AIRMASS", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.FILTERID", PS_META_REPLACE, "", "JUNK");
+        rc|= psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.FILTER", PS_META_REPLACE, "", "JUNK");
+        rc|= psMetadataAddF32(fpa->concepts, PS_LIST_TAIL, "FPA.POSANGLE", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.RADECSYS", PS_META_REPLACE, "", "JUNK");
+        rc|= psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "FPA.RA", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "FPA.DEC", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.OBSTYPE", PS_META_REPLACE, "", "JUNK");
+        rc|= psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.OBJECT", PS_META_REPLACE, "", "JUNK");
+        rc|= psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "FPA.ALT", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "FPA.AZ", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddS32(fpa->concepts, PS_LIST_TAIL, "FPA.TIMESYS", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddF32(fpa->concepts, PS_LIST_TAIL, "FPA.TEMP", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddF32(fpa->concepts, PS_LIST_TAIL, "FPA.EXPOSURE", PS_META_REPLACE, "", 22.0);
+        ok(rc, "Set dummy data in fpa->concepts");
+
+        rc = pmConceptsBlankFPA(fpa);
+        ok(rc == true, "pmConceptsBlankFPA() returned TRUE with valid input data");
+        tmpStr = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.TELESCOPE");
+        ok(mdok && !strcmp(tmpStr, ""), "FPA.TELESCOPE was cleared (%s)", tmpStr);
+        tmpStr = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.INSTRUMENT");
+        ok(mdok && !strcmp(tmpStr, ""), "FPA.INSTRUMENT was cleared (%s)", tmpStr);
+        tmpStr = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.DETECTOR");
+        ok(mdok && !strcmp(tmpStr, ""), "FPA.DETECTOR was cleared (%s)", tmpStr);
+        tmpStr = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.CAMERA");
+        ok(mdok && !strcmp(tmpStr, ""), "FPA.CAMERA was cleared (%s)", tmpStr);
+        tmpF32 = psMetadataLookupF32(&mdok, fpa->concepts, "FPA.FOCUS");
+        ok(mdok && isnan(tmpF32), "FPA.FOCUS was cleared (%f)", tmpF32);
+        tmpF32 = psMetadataLookupF32(&mdok, fpa->concepts, "FPA.AIRMASS");
+        ok(mdok && isnan(tmpF32), "FPA.AIRMASS was cleared (%f)", tmpF32);
+        tmpStr = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.FILTERID");
+        ok(mdok && !strcmp(tmpStr, ""), "FPA.FILTERID was cleared (%s)", tmpStr);
+        tmpStr = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.FILTER");
+        ok(mdok && !strcmp(tmpStr, ""), "FPA.FILTER was cleared (%s)", tmpStr);
+        tmpF32 = psMetadataLookupF32(&mdok, fpa->concepts, "FPA.POSANGLE");
+        ok(mdok && isnan(tmpF32), "FPA.POSANGLE was cleared (%f)", tmpF32);
+        tmpStr = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.RADECSYS");
+        ok(mdok && !strcmp(tmpStr, ""), "FPA.RADECSYS was cleared (%s)", tmpStr);
+        tmpF64 = psMetadataLookupF64(&mdok, fpa->concepts, "FPA.RA");
+        ok(mdok && isnan(tmpF64), "FPA.RA was cleared (%f)", tmpF64);
+        tmpF64 = psMetadataLookupF64(&mdok, fpa->concepts, "FPA.DEC");
+        ok(mdok && isnan(tmpF64), "FPA.DEC was cleared (%f)", tmpF64);
+        tmpStr = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.OBSTYPE");
+        ok(mdok && !strcmp(tmpStr, ""), "FPA.OBSTYPE was cleared (%s)", tmpStr);
+        tmpStr = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.OBJECT");
+        ok(mdok && !strcmp(tmpStr, ""), "FPA.OBJECT was cleared (%s)", tmpStr);
+        tmpF64 = psMetadataLookupF64(&mdok, fpa->concepts, "FPA.ALT");
+        ok(mdok && isnan(tmpF64), "FPA.ALT was cleared (%f)", tmpF64);
+        tmpF64 = psMetadataLookupF64(&mdok, fpa->concepts, "FPA.AZ");
+        ok(mdok && isnan(tmpF64), "FPA.AZ was cleared (%f)", tmpF64);
+        tmpS32 = psMetadataLookupS32(&mdok, fpa->concepts, "FPA.TIMESYS");
+        ok(mdok && -1 == tmpS32, "FPA.TIMESYS was cleared (%d)", tmpS32);
+        // XXX: Add code to make sure it was cleared.
+        psMetadataItem *tmpMI = psMetadataLookup(fpa->concepts, "FPA.TIME");
+        ok(tmpMI != NULL, "FPA.TIME was cleared");
+        tmpF32 = psMetadataLookupF32(&mdok, fpa->concepts, "FPA.TEMP");
+        ok(mdok && isnan(tmpF32), "FPA.TEMP was cleared (%f)", tmpF32);
+        tmpF32 = psMetadataLookupF32(&mdok, fpa->concepts, "FPA.TEMP");
+        ok(mdok && isnan(tmpF32), "FPA.TEMP was cleared (%f)", tmpF32);
+
+        psFree(fpa);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // -----------------------------------------------------------------------------
+    // Tests for pmConceptsBlankChip()
+    // Call with valid data.  We test by ensuring the first 5 metadata items were
+    // added to chip->concepts.
+    {
+        psMemId id = psMemGetId();
+        bool mdok;
+        psF32 tmpF32;
+        psS32 tmpS32;
+        pmChip *chip = pmChipAlloc(NULL, NULL);
+        bool rc = false;
+
+        // First junk items to chip->concepts so that we know they are later blanked.
+        rc|= psMetadataAddS32(chip->concepts, PS_LIST_TAIL, "CHIP.XPARITY", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(chip->concepts, PS_LIST_TAIL, "CHIP.YPARITY", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(chip->concepts, PS_LIST_TAIL, "CHIP.X0", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(chip->concepts, PS_LIST_TAIL, "CHIP.Y0", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(chip->concepts, PS_LIST_TAIL, "CHIP.XSIZE", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(chip->concepts, PS_LIST_TAIL, "CHIP.YSIZE", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddF32(chip->concepts, PS_LIST_TAIL, "CHIP.TEMP", PS_META_REPLACE, "", 22.0);
+        ok(rc, "Set dummy data in chip->concepts");
+
+        rc = pmConceptsBlankChip(chip);
+        tmpS32 = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.XPARITY");
+        ok(mdok && 0 == tmpS32, "CHIP.XPARITY was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.YPARITY");
+        ok(mdok && 0 == tmpS32, "CHIP.YPARITY was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.X0");
+        ok(mdok && 0 == tmpS32, "CHIP.X0 was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.Y0");
+        ok(mdok && 0 == tmpS32, "CHIP.Y0 was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.XSIZE");
+        ok(mdok && 0 == tmpS32, "CHIP.XSIZE was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.YSIZE");
+        ok(mdok && 0 == tmpS32, "CHIP.YSIZE was cleared (%d)", tmpS32);
+        tmpF32 = psMetadataLookupF32(&mdok, chip->concepts, "CHIP.TEMP");
+        ok(mdok && isnan(tmpF32), "CHIP.TEMP was cleared (%d)", tmpF32);
+
+        psFree(chip);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // -----------------------------------------------------------------------------
+    // Tests for pmConceptsBlankCell()
+    // Call with valid data.  We test by ensuring the first 5 metadata items were
+    // added to cell->concepts.
+    {
+        psMemId id = psMemGetId();
+        bool mdok;
+        psF32 tmpF32;
+        psS32 tmpS32;
+        pmCell *cell = pmCellAlloc(NULL, NULL);
+        bool rc = false;
+
+        // First junk items to cell->concepts so that we know they are later blanked.
+        rc|= psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.GAIN", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.READNOISE", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.XPARITY", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.YPARITY", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.READDIR", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.SATURATION", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.BAD", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.EXPOSURE", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.DARKTIME", PS_META_REPLACE, "", 22.0);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.XBIN", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.YBIN", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.TIMESYS", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.X0", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.Y0", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.XSIZE", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.YSIZE", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.XWINDOW", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.YWINDOW", PS_META_REPLACE, "", 22);
+        rc|= psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.", PS_META_REPLACE, "", 22);
+
+
+        ok(rc, "Set dummy data in cell->concepts");
+
+        rc = pmConceptsBlankCell(cell);
+        ok(rc == true, "pmConceptsBlankCELL() returned TRUE with valid input data");
+
+        tmpF32 = psMetadataLookupF32(&mdok, cell->concepts, "CELL.GAIN");
+        ok(mdok && isnan(tmpF32), "CELL.GAIN was cleared (%f)", tmpF32);
+        tmpF32 = psMetadataLookupF32(&mdok, cell->concepts, "CELL.READNOISE");
+        ok(mdok && isnan(tmpF32), "CELL.READNOISE was cleared (%f)", tmpF32);
+        tmpF32 = psMetadataLookupF32(&mdok, cell->concepts, "CELL.SATURATION");
+        ok(mdok && isnan(tmpF32), "CELL.SATURATION was cleared (%f)", tmpF32);
+        tmpF32 = psMetadataLookupF32(&mdok, cell->concepts, "CELL.BAD");
+        ok(mdok && isnan(tmpF32), "CELL.BAD was cleared (%f)", tmpF32);
+
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XPARITY");
+        ok(mdok && 0 == tmpS32, "CELL.XPARITY was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YPARITY");
+        ok(mdok && 0 == tmpS32, "CELL.YPARITY was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR");
+        ok(mdok && 0 == tmpS32, "CELL.READDIR was cleared (%d)", tmpS32);
+        tmpF32 = psMetadataLookupF32(&mdok, cell->concepts, "CELL.EXPOSURE");
+        ok(mdok && isnan(tmpF32), "CELL.EXPOSURE was cleared (%f)", tmpF32);
+        tmpF32 = psMetadataLookupF32(&mdok, cell->concepts, "CELL.DARKTIME");
+        ok(mdok && isnan(tmpF32), "CELL.DARKTIME was cleared (%f)", tmpF32);
+        // XXX: Add code to make sure it was cleared.
+        psMetadataItem *tmpMI = psMetadataLookup(cell->concepts, "CELL.TRIMSEC");
+        ok(tmpMI != NULL, "CELL.TRIMSEC was cleared");
+        tmpMI = psMetadataLookup(cell->concepts, "CELL.BIASSEC");
+        ok(tmpMI != NULL, "CELL.BIASSEC was cleared");
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XBIN");
+        ok(mdok && 0 == tmpS32, "CELL.XBIN was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YBIN");
+        ok(mdok && 0 == tmpS32, "CELL.YBIN was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.TIMESYS");
+        ok(mdok && -1 == tmpS32, "CELL.TIMESYS was cleared (%d)", tmpS32);
+        // XXX: Add code to make sure it was cleared.
+        tmpMI = psMetadataLookup(cell->concepts, "CELL.TIME");
+        ok(tmpMI != NULL, "CELL.TIME was cleared");
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0");
+        ok(mdok && 0 == tmpS32, "CELL.X0 was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0");
+        ok(mdok && 0 == tmpS32, "CELL.Y0 was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XSIZE");
+        ok(mdok && 0 == tmpS32, "CELL.XSIZE was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YSIZE");
+        ok(mdok && 0 == tmpS32, "CELL.YSIZE was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XWINDOW");
+        ok(mdok && 0 == tmpS32, "CELL.XWINDOW was cleared (%d)", tmpS32);
+        tmpS32 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YWINDOW");
+        ok(mdok && 0 == tmpS32, "CELL.YWINDOW was cleared (%d)", tmpS32);
+
+        psFree(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConceptsAverage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConceptsAverage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConceptsAverage.c	(revision 22322)
@@ -0,0 +1,443 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+// XXX: Use better name for the temporary FITS file
+// XXX: The code to generate and free the FPA hierarchy was copied from
+// tap-pmFPA.c.  EIther include it directly, or library, or something.
+// Also, get rid of the manual free functions and use psFree() once
+// it correctly frees child members
+// XXX: For the genSimpleFPA() code, add IDs to each function so that
+// the values set in each chip-?cell-?hdu-?image are unique
+// XXX: For the genSimpleFPA() code, write masks and weights as well
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_FPAS		4
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+//    psRegion *region = psRegionAlloc(0.0, TEST_NUM_COLS-1, 0.0, TEST_NUM_ROWS-1);
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(50);
+
+    // ----------------------------------------------------------------------
+    // pmConceptsAverageFPAs() tests: NULL input pmFPA *target
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *targetFPA = generateSimpleFPA(camera);
+        pmFPA *sourceFPA[NUM_FPAS];
+
+        sourceFPA[0] = generateSimpleFPA(camera);
+        psList *sources = psListAlloc(sourceFPA[0]);
+        for (int fpaID = 1 ; fpaID < NUM_FPAS ; fpaID++) {
+            sourceFPA[fpaID] = generateSimpleFPA(camera);
+            bool rc = psListAdd(sources, PS_LIST_HEAD, sourceFPA[fpaID]);
+            ok(rc, "Successfully added FPA %d to list", fpaID);
+	}
+        ok(!pmConceptsAverageFPAs(NULL, sources), "pmConceptsAverage(NULL, sources) returned FALSE");
+
+        for (int fpaID = 0 ; fpaID < NUM_FPAS ; fpaID++) {
+            psFree(sourceFPA[fpaID]);
+	}
+        psFree(sources);
+        psFree(targetFPA);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmConceptsAverageFPAs() tests: NULL input psList *sources
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *targetFPA = generateSimpleFPA(camera);
+        pmFPA *sourceFPA[NUM_FPAS];
+
+        sourceFPA[0] = generateSimpleFPA(camera);
+        psList *sources = psListAlloc(sourceFPA[0]);
+        for (int fpaID = 1 ; fpaID < NUM_FPAS ; fpaID++) {
+            sourceFPA[fpaID] = generateSimpleFPA(camera);
+            bool rc = psListAdd(sources, PS_LIST_HEAD, sourceFPA[fpaID]);
+            ok(rc, "Successfully added FPA %d to list", fpaID);
+	}
+        ok(!pmConceptsAverageFPAs(targetFPA, NULL), "pmConceptsAverage(NULL, sources) returned FALSE");
+
+        for (int fpaID = 0 ; fpaID < NUM_FPAS ; fpaID++) {
+            psFree(sourceFPA[fpaID]);
+	}
+        psFree(sources);
+        psFree(targetFPA);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmConceptsAverageFPAs() tests: acceptable inputs
+    // XXX: There's a memory leak somewhere in this test, not sure where.
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *targetFPA = generateSimpleFPA(camera);
+        pmFPA *sourceFPA[NUM_FPAS];
+        psMetadata *cameras[NUM_FPAS];
+
+        // Ensure that the FPA.TIME average is computed correctly
+        psList *sources = NULL;
+        psF64 actualTime = 0.0;
+        for (int fpaID = 0 ; fpaID < NUM_FPAS ; fpaID++) {
+            cameras[fpaID] = psMetadataAlloc();
+            sourceFPA[fpaID] = generateSimpleFPA(cameras[fpaID]);
+            psTime *fpaTime = psMetadataLookupPtr(NULL, (sourceFPA[fpaID])->concepts, "FPA.TIME");
+            // Add a small value to the psTime so that we can test/ensure that pmConceptsAverageFPAs()
+            // is actually calculating an average.
+            fpaTime->sec += (double) (fpaID * 1000);
+            actualTime+= psTimeToMJD(fpaTime);
+            if (0 == fpaID) {
+                sources = psListAlloc(sourceFPA[fpaID]);
+	    } else {
+                bool rc = psListAdd(sources, PS_LIST_HEAD, sourceFPA[fpaID]);
+                ok(rc, "Successfully added FPA %d to list", fpaID);
+	    }
+	}
+        // XXX: The memory leak occurs during the following single call
+        ok(pmConceptsAverageFPAs(targetFPA, sources), "pmConceptsAverage(targetFPA, sources) returned TRUE");
+        actualTime/= (float) NUM_FPAS;
+        psTime *fpaTime = psMetadataLookupPtr(NULL, targetFPA->concepts, "FPA.TIME");
+        ok(abs(actualTime - psTimeToMJD(fpaTime)) < 1e-4, "pmConceptsAverageFPAs() calculated the average time correctly");
+
+        // Replace the FPA.TIMESYS with a non-conforming value, verify that pmConceptsAverageFPAs() returns an error
+
+        psTimeType timeSys = psMetadataLookupS32(NULL, sourceFPA[0]->concepts, "FPA.TIMESYS");
+        psMetadataAddS32(sourceFPA[NUM_FPAS-1]->concepts, PS_LIST_HEAD, "FPA.TIMESYS", PS_META_REPLACE, NULL, timeSys+10);
+        ok(!pmConceptsAverageFPAs(targetFPA, sources), "pmConceptsAverage(NULL, sources) returned FALSE with nonequal FPA.TIMESYS metadata");
+        psMetadataAddS32(sourceFPA[NUM_FPAS-1]->concepts, PS_LIST_HEAD, "FPA.TIMESYS", PS_META_REPLACE, NULL, timeSys);
+        ok(pmConceptsAverageFPAs(targetFPA, sources), "pmConceptsAverage(NULL, sources) returned TRUE with equal FPA.TIMESYS metadata");
+
+
+        // Replace the FPA.TIMESYS with a non-conforming value, verify that pmConceptsAverageFPAs() returnes an error        
+        psString filter = psMetadataLookupStr(NULL, sourceFPA[0]->concepts, "FPA.FILTER");
+        psMetadataAddStr(sourceFPA[NUM_FPAS-1]->concepts, PS_LIST_HEAD, "FPA.FILTER", PS_META_REPLACE, NULL, "BOGUS STRING");
+        ok(!pmConceptsAverageFPAs(targetFPA, sources), "pmConceptsAverage(NULL, sources) returned FALSE with nonequal FPA.FILTER metadata");
+        psMetadataAddStr(sourceFPA[NUM_FPAS-1]->concepts, PS_LIST_HEAD, "FPA.FILTER", PS_META_REPLACE, NULL, filter);
+        ok(pmConceptsAverageFPAs(targetFPA, sources), "pmConceptsAverage(NULL, sources) returned TRUE with equal FPA.FILTER metadata");
+
+        // Free data, check for memory leaks
+        for (int fpaID = 0 ; fpaID < NUM_FPAS ; fpaID++) {
+            psFree(sourceFPA[fpaID]);
+            psFree(cameras[fpaID]);
+	}
+        psFree(sources);
+//        psFree(filter);
+        psFree(targetFPA);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // ----------------------------------------------------------------------
+    // pmConceptsAverageCells() tests: NULL input pmFPA *target
+    // bool pmConceptsAverageCells(pmCell *target, psList *sources, psRegion *trimsec, psRegion *biassec, bool same)
+    {
+        psMemId id = psMemGetId();
+        psMetadata *tgtCamera = psMetadataAlloc();
+        pmFPA *tgtFPA = generateSimpleFPA(tgtCamera);
+        pmChip *tgtChip = tgtFPA->chips->data[0];
+        pmCell *tgtCell = tgtChip->cells->data[0];
+        psMetadata *srcCamera = psMetadataAlloc();
+        pmFPA *srcFPA = generateSimpleFPA(srcCamera);
+        pmChip *srcChip = srcFPA->chips->data[0];
+
+        psList *sources = NULL;
+        psF32 tstGain = 0.0;
+        psF32 tstReadnoise = 0.0;
+        psF32 tstExposure = 0.0;
+        psF32 tstDarktime = 0.0;
+        psF32 tstSaturation = 0.0;
+        psF32 tstBad = 0.0;
+        
+        for (int cellID = 0 ; cellID < srcChip->cells->n ; cellID++) {
+            pmCell *cell = srcChip->cells->data[cellID];
+            // Set the various concepts which we will test later
+            psMetadataAddF32(cell->concepts, PS_LIST_HEAD, "CELL.GAIN", PS_META_REPLACE, NULL, 0.0 + (float) cellID);
+            tstGain+= 0.0 + (float) cellID;
+            psMetadataAddF32(cell->concepts, PS_LIST_HEAD, "CELL.READNOISE", PS_META_REPLACE, NULL, 10.0 + (float) cellID);
+            tstReadnoise+= 10.0 + (float) cellID;
+            psMetadataAddF32(cell->concepts, PS_LIST_HEAD, "CELL.EXPOSURE", PS_META_REPLACE, NULL, 20.0 + (float) cellID);
+            tstExposure+= 20.0 + (float) cellID;
+            psMetadataAddF32(cell->concepts, PS_LIST_HEAD, "CELL.DARKTIME", PS_META_REPLACE, NULL, 30.0 + (float) cellID);
+            tstDarktime+= 30.0 + (float) cellID;
+            psMetadataAddF32(cell->concepts, PS_LIST_HEAD, "CELL.SATURATION", PS_META_REPLACE, NULL, 40.0 + (float) cellID);
+            if (cellID == 0)
+                tstSaturation = 40.0 + (float) cellID;
+            psMetadataAddF32(cell->concepts, PS_LIST_HEAD, "CELL.BAD", PS_META_REPLACE, NULL, 50.0 + (float) cellID);
+            if (cellID == (srcChip->cells->n - 1))
+                tstBad = 50.0 + (float) cellID;
+            if (cellID == 0) {
+                sources = psListAlloc(srcChip->cells->data[cellID]);
+	    } else {
+                bool rc = psListAdd(sources, PS_LIST_HEAD, srcChip->cells->data[cellID]);
+                ok(rc, "Successfully added cell %d to list", cellID);
+	    }
+	}
+        tstGain /= (psF64) srcChip->cells->n;
+        tstReadnoise /= (psF64) srcChip->cells->n;
+        tstExposure /= (psF64) srcChip->cells->n;
+        tstDarktime /= (psF64) srcChip->cells->n;
+
+        psRegion *trimsec = psRegionAlloc(0, 1, 2, 3);
+        psRegion *biassec = psRegionAlloc(4, 5, 6, 7);
+
+        // Ensure pmConceptsAverageCells() returns NULL with NULL tgtCell input
+        bool rc = pmConceptsAverageCells(NULL, sources, trimsec, biassec, false);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with NULL sources input");
+
+        // Ensure pmConceptsAverageCells() returns NULL with NULL sources input
+        rc = pmConceptsAverageCells(tgtCell, NULL, trimsec, biassec, false);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with NULL targetCell input");
+
+        // Ensure pmConceptsAverageCells() returns NULL with sources->n = 0
+        sources->n = 0;
+        rc = pmConceptsAverageCells(tgtCell, sources, trimsec, biassec, false);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with NULL sources->n = 0");
+        sources->n = NUM_CELLS;
+
+        // Call pmConceptsAverageCells() with acceptable input data
+        rc = pmConceptsAverageCells(tgtCell, sources, trimsec, biassec, false);
+        ok(rc, "pmConceptsAverageCells() returned TRUE with acceptable input data");
+        psF32 tmpF32;
+        tmpF32 = psMetadataLookupF32(NULL, tgtCell->concepts, "CELL.GAIN");
+        ok(abs(tmpF32 - tstGain) < 1e-4, "pmConceptsAverageCells() calculated the average CELL.GAIN correctly");
+        tmpF32 = psMetadataLookupF32(NULL, tgtCell->concepts, "CELL.READNOISE");
+        ok(abs(tmpF32 - tstReadnoise) < 1e-4, "pmConceptsAverageCells() calculated the average CELL.READNOISE correctly");
+        tmpF32 = psMetadataLookupF32(NULL, tgtCell->concepts, "CELL.EXPOSURE");
+        ok(abs(tmpF32 - tstExposure) < 1e-4, "pmConceptsAverageCells() calculated the average CELL.EXPOSURE correctly");
+        tmpF32 = psMetadataLookupF32(NULL, tgtCell->concepts, "CELL.DARKTIME");
+        ok(abs(tmpF32 - tstDarktime) < 1e-4, "pmConceptsAverageCells() calculated the average CELL.DARKTIME correctly");
+        tmpF32 = psMetadataLookupF32(NULL, tgtCell->concepts, "CELL.SATURATION");
+        ok(abs(tmpF32 - tstSaturation) < 1e-4, "pmConceptsAverageCells() calculated the average CELL.SATURATION correctly (%f %f)", tmpF32, tstSaturation);
+        tmpF32 = psMetadataLookupF32(NULL, tgtCell->concepts, "CELL.BAD");
+        ok(abs(tmpF32 - tstBad) < 1e-4, "pmConceptsAverageCells() calculated the average CELL.BAD correctly");
+        psRegion *tstTrimsec = psMetadataLookupPtr(NULL, tgtCell->concepts, "CELL.TRIMSEC");
+        ok(tstTrimsec->x0 == 0.0 && tstTrimsec->x1 == 1.0 && tstTrimsec->y0 == 2.0 && tstTrimsec->y1 == 3.0,
+           "pmConceptsAverageCells() set the CELL>TRIMSEC region correctly");
+        psRegion *tstBiassec = psMetadataLookupPtr(NULL, tgtCell->concepts, "CELL.BIASSEC");
+        ok(tstBiassec->x0 == 4.0 && tstBiassec->x1 == 5.0 && tstBiassec->y0 == 6.0 && tstBiassec->y1 == 7.0,
+           "pmConceptsAverageCells() set the CELL>TRIMSEC region correctly");
+
+        psS32 tmpS32;
+        pmCell *srcCell = srcChip->cells->data[NUM_CELLS - 1];
+
+        // Set the CELL.TIMESYS metadata unequal, verify that error occurs
+        rc = psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.TIMESYS", PS_META_REPLACE, NULL, 232);
+        tmpS32 = psMetadataLookupS32(&rc, srcCell->concepts, "CELL.TIMESYS");
+        rc = pmConceptsAverageCells(tgtCell, sources, trimsec, biassec, false);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with non equal CELL.TIMESYS metadata");
+        psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.TIMESYS", PS_META_REPLACE, NULL, tmpS32);
+
+        // Set the CELL.READDIR metadata unequal, verify that error occurs
+        rc = psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.READDIR", PS_META_REPLACE, NULL, 232);
+        tmpS32 = psMetadataLookupS32(&rc, srcCell->concepts, "CELL.READDIR");
+        rc = pmConceptsAverageCells(tgtCell, sources, trimsec, biassec, false);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with non equal CELL.READDIR metadata");
+        psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.READDIR", PS_META_REPLACE, NULL, tmpS32);
+
+        // Set the CELL.XBIN metadata unequal, verify that error occurs
+        rc = psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.XBIN", PS_META_REPLACE, NULL, 232);
+        tmpS32 = psMetadataLookupS32(&rc, srcCell->concepts, "CELL.XBIN");
+        rc = pmConceptsAverageCells(tgtCell, sources, trimsec, biassec, false);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with non equal CELL.XBIN metadata");
+        psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.XBIN", PS_META_REPLACE, NULL, tmpS32);
+
+        // Set the CELL.YBIN metadata unequal, verify that error occurs
+        rc = psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.YBIN", PS_META_REPLACE, NULL, 232);
+        tmpS32 = psMetadataLookupS32(&rc, srcCell->concepts, "CELL.YBIN");
+        rc = pmConceptsAverageCells(tgtCell, sources, trimsec, biassec, false);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with non equal CELL.YBIN metadata");
+        psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.YBIN", PS_META_REPLACE, NULL, tmpS32);
+
+        // Set the CELL.X0 metadata unequal, verify that error occurs
+        rc = psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.X0", PS_META_REPLACE, NULL, 232);
+        tmpS32 = psMetadataLookupS32(&rc, srcCell->concepts, "CELL.X0");
+        rc = pmConceptsAverageCells(tgtCell, sources, trimsec, biassec, true);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with non equal CELL.X0 metadata (same = true)");
+        psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.X0", PS_META_REPLACE, NULL, tmpS32);
+
+        // Set the CELL.Y0 metadata unequal, verify that error occurs
+        rc = psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.Y0", PS_META_REPLACE, NULL, 232);
+        tmpS32 = psMetadataLookupS32(&rc, srcCell->concepts, "CELL.Y0");
+        rc = pmConceptsAverageCells(tgtCell, sources, trimsec, biassec, true);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with non equal CELL.Y0 metadata (same = true)");
+        psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.Y0", PS_META_REPLACE, NULL, tmpS32);
+
+        // Set the CELL.X0 metadata unequal, verify that no error occurs with same==false
+        rc = psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.X0", PS_META_REPLACE, NULL, 232);
+        tmpS32 = psMetadataLookupS32(&rc, srcCell->concepts, "CELL.X0");
+        rc = pmConceptsAverageCells(tgtCell, sources, trimsec, biassec, false);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with non equal CELL.X0 metadata (same = false)");
+        psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.X0", PS_META_REPLACE, NULL, tmpS32);
+
+        // Set the CELL.Y0 metadata unequal, verify that no error occurs with same==false
+        rc = psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.Y0", PS_META_REPLACE, NULL, 232);
+        tmpS32 = psMetadataLookupS32(&rc, srcCell->concepts, "CELL.Y0");
+        rc = pmConceptsAverageCells(tgtCell, sources, trimsec, biassec, false);
+        ok(!rc, "pmConceptsAverageCells() returned FALSE with non equal CELL.Y0 metadata (same = false)");
+        psMetadataAddS32(srcCell->concepts, PS_LIST_HEAD, "CELL.Y0", PS_META_REPLACE, NULL, tmpS32);
+
+        psFree(tgtFPA);
+        psFree(srcFPA);
+        psFree(tgtCamera);
+        psFree(srcCamera);
+        psFree(biassec);
+        psFree(trimsec);
+        psFree(sources);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConceptsPhotcode.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConceptsPhotcode.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConceptsPhotcode.c	(revision 22322)
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(25);
+
+
+    // ----------------------------------------------------------------------
+    // pmConceptsPhotcodeForView() tests: NULL pmConfig input
+    // psString pmConceptsPhotcodeForView(pmConfig *config, pmFPAfile *file, const pmFPAview *view)
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config =pmConfigAlloc();
+        pmFPAfile *file = pmFPAfileAlloc();
+        pmFPAview *view = pmFPAviewAlloc(32);
+        ok(NULL == pmConceptsPhotcodeForView(NULL, file, view),
+          "pmConceptsPhotcodeForView(NULL, file, view) returned NULL");
+        psFree(config);
+        psFree(file);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmConceptsPhotcodeForView() tests: NULL pmConfig input
+    // psString pmConceptsPhotcodeForView(pmConfig *config, pmFPAfile *file, const pmFPAview *view)
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config =pmConfigAlloc();
+        pmFPAfile *file = pmFPAfileAlloc();
+        pmFPAview *view = pmFPAviewAlloc(32);
+        ok(NULL == pmConceptsPhotcodeForView(NULL, file, view),
+          "pmConceptsPhotcodeForView(NULL, file, view) returned NULL");
+        psFree(config);
+        psFree(file);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmConceptsPhotcodeForView() tests: NULL pmFPAfile input
+    // psString pmConceptsPhotcodeForView(pmConfig *config, pmFPAfile *file, const pmFPAview *view)
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config =pmConfigAlloc();
+        pmFPAfile *file = pmFPAfileAlloc();
+        pmFPAview *view = pmFPAviewAlloc(32);
+        ok(NULL == pmConceptsPhotcodeForView(config, NULL, view),
+          "pmConceptsPhotcodeForView(config, NULL, view) returned NULL");
+        psFree(config);
+        psFree(file);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmConceptsPhotcodeForView() tests: NULL pmFPAview input
+    // psString pmConceptsPhotcodeForView(pmConfig *config, pmFPAfile *file, const pmFPAview *view)
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config =pmConfigAlloc();
+        pmFPAfile *file = pmFPAfileAlloc();
+        pmFPAview *view = pmFPAviewAlloc(32);
+        ok(NULL == pmConceptsPhotcodeForView(config, file, NULL),
+          "pmConceptsPhotcodeForView(config, file, NULL) returned NULL");
+        psFree(config);
+        psFree(file);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmConceptsPhotcodeForView() tests: acceptable inputs
+    // psString pmConceptsPhotcodeForView(pmConfig *config, pmFPAfile *file, const pmFPAview *view)
+    {
+        psMemId id = psMemGetId();
+        psString str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+//        str[2] = "../dataFiles/SampleIPPConfig";
+        str[2] = "../config/data/SampleIPPConfig";
+        psS32 argc = 3;
+        pmConfig *config = pmConfigRead(&argc, str, "RecipeName");
+        ok(config, "pmConfigRead() returned non-NULL");
+        pmFPAfile *file = pmFPAfileAlloc();
+        // XXX: Insert code to read a pmFPAfile correctly
+        pmFPAview *view = pmFPAviewAlloc(0);
+
+        skip_start(!config, 2, "Skipping tests because pmConfigRead() failed");        
+        bool rc;
+        psMetadata *recipe  = psMetadataLookupPtr(&rc, config->recipes, "PPIMAGE");
+        char *rule = psMetadataLookupStr(&rc, recipe, "PHOTCODE.RULE");
+        psString goodPhotcode = pmFPAfileNameFromRule(rule, file, view);
+
+        psString testPhotcode = pmConceptsPhotcodeForView(config, file, view);
+        ok(testPhotcode, "pmConceptsPhotcodeForView(config, file, view) returned non-NULL");
+        ok(!strcmp(goodPhotcode, testPhotcode), "pmConceptsPhotcodeForView() produced the correct string");
+        skip_end();
+        psFree(config);
+        psFree(file);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConceptsUpdate.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConceptsUpdate.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/concepts/tap_pmConceptsUpdate.c	(revision 22322)
@@ -0,0 +1,248 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+// XXX: Use better name for the temporary FITS file
+// XXX: The code to generate and free the FPA hierarchy was copied from
+// tap-pmFPA.c.  EIther include it directly, or library, or something.
+// Also, get rid of the manual free functions and use psFree() once
+// it correctly frees child members
+// XXX: For the genSimpleFPA() code, add IDs to each function so that
+// the values set in each chip-?cell-?hdu-?image are unique
+// XXX: For the genSimpleFPA() code, write masks and weights as well
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+
+    // XXX: Add code to initialize chip pmConcepts
+
+
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(7);
+
+
+    // ----------------------------------------------------------------------
+    // pmConceptsUpdate() tests: NULL inputs
+    // bool pmConceptsUpdate(const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+    {
+        psMemId id = psMemGetId();
+        ok(pmConceptsUpdate(NULL, NULL, NULL), "pmConceptsUpdate(NULL, NULL, NULL) returned TRUE");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    
+    // pmConceptsUpdate() tests: acceptable inputs
+    // XXX: Must add tests for the BIASSEC concepts
+    {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        pmCell *cell = chip->cells->data[0];
+        bool rc = pmConceptsUpdate(NULL, NULL, cell);
+
+        // Calculate expected myTrimsec
+        bool xStatus, yStatus; // Status of MD lookups
+        psImageBinning *binning = psImageBinningAlloc();
+        binning->nXbin = psMetadataLookupS32(&xStatus, cell->concepts, "CELL.XBIN");
+        binning->nYbin = psMetadataLookupS32(&yStatus, cell->concepts, "CELL.YBIN");
+        psRegion *goodTrimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC");
+        double goodTrimsecX0 = goodTrimsec->x0;
+        double goodTrimsecX1 = goodTrimsec->x1;
+        double goodTrimsecY0 = goodTrimsec->y0;
+        double goodTrimsecY1 = goodTrimsec->y1;
+        goodTrimsecX0/= binning->nXbin;
+        goodTrimsecX1/= binning->nXbin;
+        goodTrimsecY0/= binning->nYbin;
+        goodTrimsecY1/= binning->nYbin;
+        goodTrimsecX0 = (int) goodTrimsec->x0;
+        if (goodTrimsec->x1 > (int)goodTrimsec->x1) {
+            goodTrimsecX1 = (int)goodTrimsec->x1 + 1;
+         } else {
+            goodTrimsecX1 = (int)goodTrimsec->x1;
+	 }
+        goodTrimsecY0 = (int)goodTrimsec->y0;
+        if (goodTrimsec->y1 > (int)goodTrimsec->y1) {
+            goodTrimsecY1 = (int)goodTrimsec->y1 + 1;
+	} else {
+            goodTrimsecY1 = (int)goodTrimsec->y1;
+	}
+        psFree(binning);
+
+        // Add CELL.TRIMSEC.UPDATE concept to cell->concepts
+        psMetadataAddS32(cell->concepts, PS_LIST_HEAD, "CELL.TRIMSEC.UPDATE", 0, NULL, 32);
+        psS32 num = psMetadataLookupS32(&rc, cell->concepts, "CELL.TRIMSEC.UPDATE");
+        ok(rc, "Successfully added CELL.TRIMSEC.UPDATE to cell->concepts (%d)", num);
+
+        // Add CELL.BIASSEC.UPDATE concept to cell->concepts
+        psMetadataAddS32(cell->concepts, PS_LIST_HEAD, "CELL.BIASSEC.UPDATE", 0, NULL, 32);
+        num = psMetadataLookupS32(&rc, cell->concepts, "CELL.BIASSEC.UPDATE");
+        ok(rc, "Successfully added CELL.BIASSEC.UPDATE to cell->concepts (%d)", num);
+
+        // Call pmConceptsUpdate to update the CELL.TRIMSEC concept
+        ok(rc, "pmConceptsUpdate(NULL, NULL, pmCell) returned TRUE");
+
+        psRegion *testTrimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC"); // Trim section
+        ok(testTrimsec->x1 == goodTrimsecX1 && testTrimsec->y1 == goodTrimsecY1 &&
+           testTrimsec->x0 == goodTrimsecX0 && testTrimsec->y0 == goodTrimsecY0,
+           "pmConceptsUpdate() updated CELL.TRIMSEC correctly (%f %f)",
+            testTrimsec->x1, testTrimsec->y1);
+
+        // XXX: This fails but the problem might be with the test code
+        if (0) {
+            num = psMetadataLookupS32(&rc, cell->concepts, "CELL.TRIMSEC.UPDATE");
+            ok(!rc, "pmConceptsUpdate() removed CELL.TRIMSEC.UPDATE to cell->concepts (%d)", num);
+        }
+
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/.cvsignore	(revision 22322)
@@ -0,0 +1,7 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmConfig
+SampleIPPConfig
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/Makefile.am	(revision 22322)
@@ -0,0 +1,35 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSMODULES_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpsmodules.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSMODULES_LIBS)
+
+TEST_PROGS = \
+	tap_pmConfig \
+	tap_pmConfigCommand \
+	tap_pmConfigMask \
+	tap_pmErrorCodes \
+	tap_pmVersion
+
+check_DATA =
+
+
+EXTRA_DIST = data
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
+	$(top_srcdir)/test/test.pl
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/SampleIPPConfig
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/SampleIPPConfig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/SampleIPPConfig	(revision 22322)
@@ -0,0 +1,74 @@
+### Example .ipprc file
+    PATH            STR     .
+    DATAPATH	str	datapath
+
+PATH            STR     .
+
+### Database configuration
+DBSERVER	STR	localhost		# Database host name (for psDBInit)
+DBUSER		STR	test			# Database user name (for psDBInit)
+DBPASSWORD	STR	DB-PW			# Database password (for psDBInit)
+DBNAME          STR     test                    # ????
+DBPORT		S32	0
+
+
+### Setups for each camera system
+CAMERAS		METADATA
+	CAMERA0		STR	camera0/camera.config
+	CAMERA1		STR	camera1/camera.config
+END
+
+### psLib setup
+#TIME		STR	/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/etc/pslib/psTime.config	# Time configuration file
+LOGLEVEL	S32	3			# Logging level; 3=INFO
+LOGFORMAT	STR	HLNM			# Log format
+LOGDEST	STR	STDOUT				# Log destination
+TRACE		METADATA			# Trace levels
+	dummyTraceFunc01	S32	1
+	dummyTraceFunc02	S32	2
+END
+ARBITRARY_STRING_S32	S32	20
+ARBITRARY_STRING_F32	F32	21.0
+ARBITRARY_STRING_F64	F64	22.0
+ARBITRARY_STRING_STR	STR	19.0
+ARBITRARY_STRING_BOOL_T	BOOL	true
+ARBITRARY_STRING_BOOL_F	BOOL	false
+=======
+    CAMERAS		METADATA
+	CAMERA0		STR	camera0/camera.config
+	CAMERA1		STR	camera1/camera.config
+    END
+
+### Setups for psLib
+    TIME		STR	time.config
+    LOGLEVEL	S32	3
+    LOGFORMAT	STR	HLNM
+    LOGDEST	STR	STDOUT
+
+### Default trace logging initializations
+    TRACE		METADATA
+	dummyTraceFacility01	S32	1
+	dummyTraceFacility02	S32	2
+    END
+
+### Predefined recipe files
+    RECIPES         METADATA                # Site-level recipes
+        R00		STR	recipes/R00.config
+        R01		STR	recipes/R01.config
+        R02		STR	recipes/R02.config
+        R03		STR	recipes/R03.config
+    END
+
+### Misc arbitraty metadata
+    ARBITRARY_STRING_S32	S32	20
+    ARBITRARY_STRING_F32	F32	21.0
+    ARBITRARY_STRING_F64	F64	22.0
+    ARBITRARY_STRING_STR	STR	19.0
+    ARBITRARY_STRING_BOOL_T	BOOL	true
+    ARBITRARY_STRING_BOOL_F	BOOL	false
+
+RECIPES         METADATA
+	MASKS			STR	recipes_masks.config
+        JUNK_RECIPE_00		STR	recipe_file00
+        JUNK_RECIPE_01		STR	recipe_file01
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/basicConfig
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/basicConfig	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/basicConfig	(revision 22322)
@@ -0,0 +1,25 @@
+
+TEST MULTI
+TEST STR FOO
+TEST STR BAR
+
+PATH              STR     .
+
+# load the site-specific information from here
+SITE              STR     site.config
+
+# load the system configuration information from here
+SYSTEM            STR     system.config
+
+### psLib setup
+LOGLEVEL	S32	5			# Logging level; 3=INFO
+LOGFORMAT	STR	M			# Log format
+LOGDEST		STR	STDERR			# Log destination
+TRACEDEST	STR	STDERR			# Trace destination
+
+# place default trace lines here
+TRACE		METADATA			# Trace levels
+  err		S32	10
+# psLib.db      S32	10
+END
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/camera.config	(revision 22322)
@@ -0,0 +1,15 @@
+FORMATS         METADATA
+        C0_FM0     STR     camera0/format0.config
+        C0_FM1     STR     camera0/format1.config
+END
+
+RECIPES         METADATA
+        C0_RECIPE0          STR     camera0/recipe0.config
+        C0_RECIPE1          STR     camera0/recipe1.config
+END
+
+FPA     METADATA
+        ccd00   STR     LeftAmp RightAmp
+        ccd01   STR     LeftAmp RightAmp
+END
+ID      STR     CAMERA0
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/format0.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/format0.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/format0.config	(revision 22322)
@@ -0,0 +1,36 @@
+RULE    METADATA
+        F0_KEY0        STR     string20
+        F0_KEY1        STR     string21
+        F0_KEY2        S32     20
+END
+
+FILE    METADATA
+        PHU             STR     FPA
+        EXTENSIONS      STR     CELL
+        FPA.NAME        STR     EXPNUM
+END
+
+DEFAULTS        METADATA
+        F0_DEF0           STR     DefString20
+        F0_DEF1           STR     DefString21
+        F0_DEF2           F32     21.0
+        F0_DEF3           S32     22
+END
+
+CELLS   METADATA
+        left    METADATA        # Left amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+        right   METADATA        # Right amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+END
+
+ID	STR	camera0/format0.config
+
+TRANSLATION     METADATA
+        FPA.TELESCOPE   STR     TELESCOP
+        FPA.INSTRUMENT  STR     INSTRUME
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/format1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/format1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/format1.config	(revision 22322)
@@ -0,0 +1,34 @@
+RULE    METADATA
+        F1_KEY0        STR     string30
+        F1_KEY1        STR     string31
+        F1_KEY2        S32     30
+END
+
+FILE    METADATA
+        PHU             STR     FPA
+        EXTENSIONS      STR     CELL
+        FPA.NAME        STR     EXPNUM
+END
+
+DEFAULTS        METADATA
+        F1_DEF0           STR     DefString30
+        F1_DEF1           STR     DefString31
+        F1_DEF2           F32     31.0
+        F1_DEF3           S32     32
+END
+
+CELLS   METADATA
+        left    METADATA        # Left amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+        right   METADATA        # Right amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+END
+
+TRANSLATION     METADATA
+        FPA.TELESCOPE   STR     TELESCOP
+        FPA.INSTRUMENT  STR     INSTRUME
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/recipe0.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/recipe0.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/recipe0.config	(revision 22322)
@@ -0,0 +1,4 @@
+R0_KEY0		STR	RecipeString0
+R0_KEY1		STR	RecipeString1
+R0_KEY2		S32	2
+R0_KEY3		F32	3
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/recipe1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/recipe1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera0/recipe1.config	(revision 22322)
@@ -0,0 +1,4 @@
+R1_KEY0		STR	RecipeString10
+R1_KEY1		STR	RecipeString11
+R1_KEY2		S32	12
+R1_KEY3		F32	13
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/camera.config	(revision 22322)
@@ -0,0 +1,14 @@
+FORMATS         METADATA
+        C1_FM0     STR     camera1/format0.config
+        C1_FM1     STR     camera1/format1.config
+END
+
+RECIPES         METADATA
+        C1_RECIPE0          STR     camera1/recipe0.config
+        C1_RECIPE1          STR     camera1/recipe1.config
+END
+
+FPA     METADATA
+        ccd00   STR     LeftAmp RightAmp
+        ccd01   STR     LeftAmp RightAmp
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/format0.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/format0.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/format0.config	(revision 22322)
@@ -0,0 +1,34 @@
+RULE    METADATA
+        F0_KEY0        STR     string40
+        F0_KEY1        STR     string41
+        F0_KEY2        S32     420
+END
+
+FILE    METADATA
+        PHU             STR     FPA
+        EXTENSIONS      STR     CELL
+        FPA.NAME        STR     EXPNUM
+END
+
+DEFAULTS        METADATA
+        F0_DEF0           STR     DefString40
+        F0_DEF1           STR     DefString41
+        F0_DEF2           F32     41.0
+        F0_DEF3           S32     44
+END
+
+CELLS   METADATA
+        left    METADATA        # Left amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+        right   METADATA        # Right amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+END
+
+TRANSLATION     METADATA
+        FPA.TELESCOPE   STR     TELESCOP
+        FPA.INSTRUMENT  STR     INSTRUME
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/format1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/format1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/format1.config	(revision 22322)
@@ -0,0 +1,34 @@
+RULE    METADATA
+        F1_KEY0        STR     string80
+        F1_KEY1        STR     string81
+        F1_KEY2        S32     80
+END
+
+FILE    METADATA
+        PHU             STR     FPA
+        EXTENSIONS      STR     CELL
+        FPA.NAME        STR     EXPNUM
+END
+
+DEFAULTS        METADATA
+        F1_DEF0           STR     DefString80
+        F1_DEF1           STR     DefString81
+        F1_DEF2           F32     81.0
+        F1_DEF3           S32     82
+END
+
+CELLS   METADATA
+        left    METADATA        # Left amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+        right   METADATA        # Right amplifier
+                CELL.BIASSEC.SOURCE     STR     HEADER
+                CELL.TRIMSEC.SOURCE     STR     HEADER
+        END
+END
+
+TRANSLATION     METADATA
+        FPA.TELESCOPE   STR     TELESCOP
+        FPA.INSTRUMENT  STR     INSTRUME
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/recipe0.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/recipe0.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/recipe0.config	(revision 22322)
@@ -0,0 +1,4 @@
+R0_KEY0		STR	RecipeString120
+R0_KEY1		STR	RecipeString121
+R0_KEY2		S32	122
+R0_KEY3		F32	123
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/recipe1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/recipe1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/camera1/recipe1.config	(revision 22322)
@@ -0,0 +1,4 @@
+R1_KEY0		STR	RecipeString230
+R1_KEY1		STR	RecipeString2311
+R1_KEY2		S32	232
+R1_KEY3		F32	233
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/path2/SampleIPPConfig2
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/path2/SampleIPPConfig2	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/path2/SampleIPPConfig2	(revision 22322)
@@ -0,0 +1,32 @@
+### Example .ipprc file
+
+### Database configuration
+DBSERVER	STR	ippdb.ifa.hawaii.edu	# Database host name (for psDBInit)
+DBUSER		STR	ipp			# Database user name (for psDBInit)
+DBPASSWORD	STR	password		# Database password (for psDBInit)
+
+### Setups for each camera system
+CAMERAS		METADATA
+	MEGACAM_RAW	STR	megacam_raw.config
+	MEGACAM_SPLICE	STR	megacam_splice.config
+	GPC1_RAW	STR	gpc1_raw.config
+	LRIS_BLUE	STR	lris_blue.config
+	LRIS_RED	STR	lris_red.config
+END
+
+### psLib setup
+#TIME		STR	/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/etc/pslib/psTime.config	# Time configuration file
+LOGLEVEL	S32	3			# Logging level; 3=INFO
+LOGFORMAT	STR	HLNM			# Log format
+LOGDEST	STR	STDOUT				# Log destination
+TRACE		METADATA			# Trace levels
+	dummyTraceFunc01	S32	1
+	dummyTraceFunc02	S32	2
+END
+ARBITRARY_STRING_S32	S32	20
+ARBITRARY_STRING_F32	F32	21.0
+ARBITRARY_STRING_F64	F64	22.0
+ARBITRARY_STRING_STR	STR	19.0
+ARBITRARY_STRING_BOOL_T	BOOL	true
+ARBITRARY_STRING_BOOL_F	BOOL	false
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipe_file00
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipe_file00	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipe_file00	(revision 22322)
@@ -0,0 +1,2 @@
+recipe00		BOOL    TRUE
+recipe01		BOOL    FALSE
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipe_file01
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipe_file01	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipe_file01	(revision 22322)
@@ -0,0 +1,2 @@
+recipe00		BOOL    FALSE
+recipe01		BOOL    TRUE
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipes/recipe1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipes/recipe1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipes/recipe1.config	(revision 22322)
@@ -0,0 +1,12 @@
+
+KEY1	STR	VALUE1
+KEY2	STR	VALUE2
+
+RECIPE1_ALT METADATA
+  KEY2  STR     VALUE2_ALT
+END
+
+KEY3 MULTI 
+KEY3 STR V1
+KEY3 STR V2
+KEY3 STR V3
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipes_masks.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipes_masks.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/recipes_masks.config	(revision 22322)
@@ -0,0 +1,10 @@
+### Recipe specifying values for various mask concepts
+BLANK           U8      0x01            # The pixel is blank or has no (valid) data
+FLAT            U8      0x02            # The pixel is non-positive in the flat-field
+DETECTOR        U8      0x02            # The detector pixel is bad (e.g., bad column, charge trap)
+SAT             U8      0x04            # The pixel is saturated in the image of interest
+BAD             U8      0x04            # The pixel is low in the image of interest
+RANGE           U8      0x04            # The pixel is out of range in the image of interest
+CR              U8      0x08            # The pixel is probably a CR
+SUSPECT         U8      0x40            # The pixel is suspected of being bad, but may not be
+MARK            U8      0x80            # The pixel is marked as temporarily ignored
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/site.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/site.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/site.config	(revision 22322)
@@ -0,0 +1,32 @@
+
+# place your data directories here and refer to as path://PATH/remainder
+DATAPATH	METADATA
+	TEST1	STR	dataTest1
+	TEST2	STR	dataTest2
+END
+
+# List of tessellations, and their DVO CATDIR
+TESSELLATIONS	METADATA
+	TESS1		STR	path://TEST1/skycells/
+	TESS2		STR	path://TEST2/skycells/
+END
+
+# dvo databases used for output
+DVO.CATDIRS	METADATA
+	CATDIR1		STR	path://TEST1/catdir/
+END
+
+# dvo databases used for psastro reference
+PSASTRO.CATDIRS	METADATA
+	CATDIR1		STR	path://TEST1/catdir/
+END
+
+# nebulous server
+NEB_SERVER	STR	http://alala:80/nebulous	# Nebulous server
+
+# Database configuration
+DBSERVER	STR	ipp000			# Database host name (for psDBInit)
+DBNAME		STR	XXX			# Database name (for psDBInit)
+DBUSER		STR	XXX			# Database user name (for psDBInit)
+DBPASSWORD	STR	XXX			# Database password (for psDBInit)
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/system.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/system.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/system.config	(revision 22322)
@@ -0,0 +1,24 @@
+## system-wide options : these are concepts not specific to any camera or recipe
+
+### Setups for each camera system
+CAMERAS		METADATA
+	TEST1			STR	testCamera1/camera.config
+END
+
+### camera names as expected by DVO
+DVO.CAMERAS		METADATA
+	TEST1			STR	testCamera1
+END
+
+# Header keywords for skycell concepts; required because DVO doesn't read HIERARCH
+SKYCELLS	METADATA
+	FPA.TIME	STR	MJD-OBS
+	CELL.TIME	STR	MJD-OBS
+	FPA.EXPOSURE	STR	EXPTIME
+	CELL.EXPOSURE	STR	EXPTIME
+	FPA.AIRMASS	STR	AIRMASS
+END
+
+RECIPES		METADATA		# Site-level recipes
+	RECIPE1		STR		recipes/recipe1.config  # Simple recipe
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/testCamera1/camera.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/testCamera1/camera.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/testCamera1/camera.config	(revision 22322)
@@ -0,0 +1,12 @@
+
+RECIPES	METADATA	# Site-level recipes
+	RECIPE1	STR	testCamera1/recipe1.config  # Simple recipe
+END
+
+FORMATS METADATA
+  	RAW  	STR	testCamera1/format.config
+END
+
+FPA	METADATA
+	chip	STR	cell
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/testCamera1/format.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/testCamera1/format.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/testCamera1/format.config	(revision 22322)
@@ -0,0 +1,53 @@
+# sample MEF format with HDU = cell
+
+# How to identify this type
+RULE	METADATA
+	TELESCOP	STR	TestTelescope
+	DETECTOR	STR	TestDetector
+	NAXIS           S32     0
+	EXTEND		BOOL	T
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	CELL	# The extensions represent cells
+	FPA.NAME	STR	EXPNUM	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	METADATA
+	# Extension name, chip name:type
+	amp		STR	chip:LeftAmp:left
+END
+
+# Specify the cell data
+CELLS	METADATA
+	left	METADATA	# Left amplifier
+		CELL.BIASSEC.SOURCE	STR	HEADER
+		CELL.TRIMSEC.SOURCE	STR	HEADER
+		CELL.BIASSEC		STR	BIASSEC
+		CELL.TRIMSEC		STR	DATASEC
+		CELL.XPARITY		S32	1 # We could have specified this as a DEFAULT, but this works
+		CELL.X0			S32	1
+		CELL.Y0			S32	1
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	FPA.AIRMASS		STR	AIRMASS
+	FPA.FILTERID		STR	FILTER
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TELESCOPE		STR	CFHT
+	FPA.INSTRUMENT		STR	MEGACAM
+END
+
+# Where there might be some ambiguity, specify the format
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+END
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/data/testCamera1/recipe1.config
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/data/testCamera1/recipe1.config	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/data/testCamera1/recipe1.config	(revision 22322)
@@ -0,0 +1,10 @@
+
+KEY1	STR	VALUE1_CAMERA
+# KEY2	STR	VALUE2_CAMERA
+
+RECIPE1_ALT METADATA
+  KEY1  STR     VALUE1_CAMERA_ALT
+END
+
+KEY3 MULTI RESET
+KEY3 STR V4
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmConfig.c	(revision 22322)
@@ -0,0 +1,1284 @@
+/** @file tst_pmConfig.c
+ *
+ *  @brief Contains the tests for pmConfig.c:
+ *
+ * This code will test the pmConfig() routine.
+*       pmConfigReadParamsSet
+*       pmConfigAlloc		
+*       pmConfigSet
+*       pmConfigDone
+*       pmConfigFileRead
+*        pmConfigValidateCameraFormat
+*        pmConfigCameraFormatFromHeader
+*        pmConfigCameraByName
+?        pmConfigDB
+*        pmConfigConformHeader
+-        pmConfigFileSets
+-        pmConfigFileSetsMD
+*        pmConfigConvertFilename
+*        pmConfigRead
+ * XXXX: Must determine what to do with NULL arguments, then test it.
+ *
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-06-10 20:58:28 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+#define ERR_TRACE_LEVEL         10
+#define	VERBOSE			0
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(181);
+    
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Tests for pmConfigReadParamsSet()
+    {
+        psMemId id = psMemGetId();
+        bool oldReadCameraConfig = pmConfigReadParamsSet(false);
+        ok(oldReadCameraConfig == true, "pmConfigReadParamsSet() returned old value correctly");
+        oldReadCameraConfig = pmConfigReadParamsSet(true);
+        ok(oldReadCameraConfig == false, "pmConfigReadParamsSet() returned old value correctly");
+        oldReadCameraConfig = pmConfigReadParamsSet(true);
+        ok(oldReadCameraConfig == true, "pmConfigReadParamsSet() returned new value correctly");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    
+    
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Tests for pmConfigAlloc()
+    // XXX: Add a MemCheckConfig() function to verify the return type
+    {
+        psMemId id = psMemGetId();
+
+        pmConfig *myConfig = pmConfigAlloc();
+        ok(myConfig != NULL, "pmConfigAlloc() returned non-NULL");
+        skip_start(myConfig == NULL, 10, "skipping tests because pmConfigAlloc() returned NULL");
+        ok(myConfig->site == NULL, "pmConfigAlloc() initialized pmConfig->site properly");
+        ok(myConfig->camera == NULL, "pmConfigAlloc() initialized pmConfig->camera properly");
+        ok(myConfig->cameraName == NULL, "pmConfigAlloc() initialized pmConfig->cameraName properly");
+        ok(myConfig->format == NULL, "pmConfigAlloc() initialized pmConfig->format properly");
+        ok(myConfig->formatName == NULL, "pmConfigAlloc() initialized pmConfig->formatName properly");
+        ok(myConfig->recipes != NULL && psMemCheckMetadata(myConfig->recipes),
+            "pmConfigAlloc() initialized pmConfig->recipes properly");
+        ok(myConfig->recipesRead == PM_RECIPE_SOURCE_NONE, "pmConfigAlloc() initialized pmConfig->recipesRead properly");
+        ok(myConfig->recipeSymbols != NULL && psMemCheckMetadata(myConfig->recipeSymbols),
+            "pmConfigAlloc() initialized pmConfig->recipesSymbols properly");
+        ok(myConfig->arguments != NULL && psMemCheckMetadata(myConfig->arguments),
+            "pmConfigAlloc() initialized pmConfig->arguments properly");
+        ok(myConfig->database == NULL, "pmConfigAlloc() initialized pmConfig->database properly");
+        ok(myConfig->defaultRecipe == NULL, "pmConfigAlloc() initialized pmConfig->defaultRecipe properly");
+
+        skip_end();
+        psFree(myConfig);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    
+    
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigSet()
+    // Test with NULL input
+    // XX: Test with empty string?
+    {
+        psMemId id = psMemGetId();
+        pmConfigSet(NULL);
+        ok(true, "called pmConfigSet() with NULL input pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigSet(NULL) created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    
+    
+    // Test with acceptable config file name, with a simple path
+    {
+        pmConfigDone();
+        psMemId id = psMemGetId();
+        psMetadata *config = NULL;
+        bool rc = pmConfigFileRead(&config, "SampleIPPConfig2", "DESCRIPTION");
+        ok(rc == false, "pmConfigFileRead() returned FALSE before calling pmConfigSet()");
+        pmConfigSet("../dataFiles/path2:dataFiles/path2");
+        rc = pmConfigFileRead(&config, "SampleIPPConfig2", "DESCRIPTION");
+        ok(rc == true, "pmConfigFileRead() returned TRUE after calling pmConfigSet() (test 1)");
+        psFree(config);
+        pmConfigDone();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with acceptable config file name, with a compound path
+    {
+        pmConfigDone();
+        psMemId id = psMemGetId();
+        psMetadata *config = NULL;
+        bool rc = pmConfigFileRead(&config, "SampleIPPConfig2", "DESCRIPTION");
+        ok(rc == false, "pmConfigFileRead() returned FALSE before calling pmConfigSet()");
+        pmConfigSet("junk:../dataFiles/path2:dataFiles/path2:data/path5");
+        rc = pmConfigFileRead(&config, "SampleIPPConfig2", "DESCRIPTION");
+        ok(rc == true, "pmConfigFileRead() returned TRUE after calling pmConfigSet() (test 2)");
+        psFree(config);
+        pmConfigDone();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigDone(): ensure it frees memory allocated by pmConfigSet();
+    // This also ensures that pmConfigSet() psFrees memory between calls
+    {
+        psMemId id = psMemGetId();
+        pmConfigSet("junk01");
+        pmConfigSet("junk:../dataFiles/path2:dataFiles/path2:data/path5");
+        pmConfigSet("junk02");
+        pmConfigDone();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks: pmConfigDone()");
+    }
+
+
+
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigFileRead()
+    // Test with NULL config pointer
+    // XXX: There's a memory leak if the first argument points to data that's
+    // already allocated (pmConfigFileRead() frees the first argument)
+    {
+        psMemId id = psMemGetId();
+        psString name = "foo1";
+        psString desc = "foo2";
+        bool rc = pmConfigFileRead(NULL, name, desc);
+        ok(rc == false, "pmConfigFileRead() returned FALSE with NULL config pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigFileRead() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with NULL name pointer
+    // XXX: Test with empty name string?
+    {
+        psMemId id = psMemGetId();
+        psMetadata *config = psMetadataAlloc();
+        psString desc = "foo2";
+        bool rc = pmConfigFileRead(&config, NULL, desc);
+        ok(rc == false, "pmConfigFileRead() returned FALSE with NULL name pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigFileRead() created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with NULL desc pointer
+    // XXX: Test with empty desc string and otherwise acceptable inputs.
+    // That generated errors elsewhere.
+    {
+        psMemId id = psMemGetId();
+        psMetadata *config = psMetadataAlloc();
+        psString name = "foo1";
+        bool rc = pmConfigFileRead(&config, name, NULL);
+        ok(rc == false, "pmConfigFileRead() returned FALSE with NULL desc pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigFileRead() created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with acceptable config file name, no paths
+    {
+        psMemId id = psMemGetId();
+        psMetadata *config = NULL;
+        psString name = "../dataFiles/SampleIPPConfig";
+        psString name2 = "dataFiles/SampleIPPConfig";
+        psString desc = "DESCRIPTION";
+        // First try to read data from ../dataFiles, then try dataFiles.
+        bool rc = pmConfigFileRead(&config, name, desc);
+        if (!rc) {
+            rc = pmConfigFileRead(&config, name2, desc);
+	}
+        ok(rc == true, "pmConfigFileRead() returned TRUE with acceptable inputs");
+
+        // Test the several arbitrary config file strings.
+        psS32 tmpInt = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_S32");
+        ok(rc == true && tmpInt == 20, "pmConfigFileRead() properly set metadata (S32)");
+        psF32 tmpFloat = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_F32");
+        ok(rc == true && tmpFloat == 21.0, "pmConfigFileRead() properly set metadata (F32)");
+        psF64 tmpDub = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_F64");
+        ok(rc == true && tmpDub == 22.0, "pmConfigFileRead() properly set metadata (F64)");
+        psBool tmpBool = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_BOOL_T");
+        ok(rc == true && tmpBool == true, "pmConfigFileRead() properly set metadata (bool)");
+        tmpBool = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_BOOL_F");
+        ok(rc == true && tmpBool == false, "pmConfigFileRead() properly set metadata (bool)");
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with acceptable config file name, no paths
+    {
+        psMemId id = psMemGetId();
+        psMetadata *config = NULL;
+        psString name = "../dataFiles/SampleIPPConfig";
+        psString name2 = "dataFiles/SampleIPPConfig";
+        psString desc = "DESCRIPTION";
+        bool rc = pmConfigFileRead(&config, name, desc);
+        if (!rc) {
+            rc = pmConfigFileRead(&config, name2, desc);
+	}
+        ok(rc == true, "pmConfigFileRead() returned TRUE with acceptable inputs");
+
+        // Test the several arbitrary config file strings.
+        psS32 tmpInt = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_S32");
+        ok(rc == true && tmpInt == 20, "pmConfigFileRead() properly set metadata (S32)");
+        psF32 tmpFloat = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_F32");
+        ok(rc == true && tmpFloat == 21.0, "pmConfigFileRead() properly set metadata (F32)");
+        psF64 tmpDub = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_F64");
+        ok(rc == true && tmpDub == 22.0, "pmConfigFileRead() properly set metadata (F64)");
+        psBool tmpBool = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_BOOL_T");
+        ok(rc == true && tmpBool == true, "pmConfigFileRead() properly set metadata (bool)");
+        tmpBool = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_BOOL_F");
+        ok(rc == true && tmpBool == false, "pmConfigFileRead() properly set metadata (bool)");
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with acceptable config file name, no paths
+    {
+        psMemId id = psMemGetId();
+        psMetadata *config = NULL;
+        psString name = "../dataFiles/SampleIPPConfig";
+        psString name2 = "dataFiles/SampleIPPConfig";
+        psString desc = "DESCRIPTION";
+        bool rc = pmConfigFileRead(&config, name, desc);
+        if (!rc) {
+            rc = pmConfigFileRead(&config, name2, desc);
+	}
+        ok(rc == true, "pmConfigFileRead() returned TRUE with acceptable inputs");
+
+        // Test the several arbitrary config file strings.
+        psS32 tmpInt = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_S32");
+        ok(rc == true && tmpInt == 20, "pmConfigFileRead() properly set metadata (S32)");
+        psF32 tmpFloat = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_F32");
+        ok(rc == true && tmpFloat == 21.0, "pmConfigFileRead() properly set metadata (F32)");
+        psF64 tmpDub = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_F64");
+        ok(rc == true && tmpDub == 22.0, "pmConfigFileRead() properly set metadata (F64)");
+        psBool tmpBool = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_BOOL_T");
+        ok(rc == true && tmpBool == true, "pmConfigFileRead() properly set metadata (bool)");
+        tmpBool = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_BOOL_F");
+        ok(rc == true && tmpBool == false, "pmConfigFileRead() properly set metadata (bool)");
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with acceptable config file name, with path
+    // Note: This also tests the pmConfigSet() function.
+    {
+        psMemId id = psMemGetId();
+        psMetadata *config = NULL;
+        psString name = "SampleIPPConfig2";
+        psString desc = "DESCRIPTION";
+        pmConfigSet("../dataFiles/path2:dataFiles/path2");
+        bool rc = pmConfigFileRead(&config, name, desc);
+        ok(rc == true, "pmConfigFileRead() returned TRUE using paths");
+        // Test the several arbitrary config file strings.
+        psS32 tmpInt = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_S32");
+        ok(rc == true && tmpInt == 20, "pmConfigFileRead() properly set metadata (S32)");
+        psF32 tmpFloat = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_F32");
+        ok(rc == true && tmpFloat == 21.0, "pmConfigFileRead() properly set metadata (F32)");
+        psF64 tmpDub = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_F64");
+        ok(rc == true && tmpDub == 22.0, "pmConfigFileRead() properly set metadata (F64)");
+        psBool tmpBool = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_BOOL_T");
+        ok(rc == true && tmpBool == true, "pmConfigFileRead() properly set metadata (bool)");
+        tmpBool = psMetadataLookupS32(&rc, config, "ARBITRARY_STRING_BOOL_F");
+        ok(rc == true && tmpBool == false, "pmConfigFileRead() properly set metadata (bool)");
+        psFree(config);
+        pmConfigDone();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigValidateCameraFormat()
+    // Test with NULL valid pointer
+    // XXX: This is commented out because of a seg-fault
+    if (0) {
+        psMemId id = psMemGetId();
+        psMetadata *cameraFormat = psMetadataAlloc();
+        psMetadata *header = psMetadataAlloc();
+        bool rc = pmConfigValidateCameraFormat(NULL, cameraFormat, header);
+        ok(rc == false, "pmConfigValidateCameraFormat() returned FALSE with NULL valid pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigValidateCameraFormat() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(cameraFormat);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with NULL cameraFormat pointer
+    {
+        psMemId id = psMemGetId();
+        bool valid;
+        psMetadata *cameraFormat = psMetadataAlloc();
+        psMetadata *header = psMetadataAlloc();
+        bool rc = pmConfigValidateCameraFormat(&valid, NULL, header);
+        ok(rc == false, "pmConfigValidateCameraFormat() returned FALSE with NULL valid pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigValidateCameraFormat() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(cameraFormat);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with NULL header pointer
+    {
+        psMemId id = psMemGetId();
+        bool valid;
+        psMetadata *cameraFormat = psMetadataAlloc();
+        psMetadata *header = psMetadataAlloc();
+        bool rc = pmConfigValidateCameraFormat(&valid, cameraFormat, NULL);
+        ok(rc == false, "pmConfigValidateCameraFormat() returned FALSE with NULL valid pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigValidateCameraFormat() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(cameraFormat);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with acceptable input
+    // psTraceSetLevel("psModules.config", 10);
+    {
+        psMemId id = psMemGetId();
+        bool valid;
+        psMetadata *header = psMetadataAlloc();
+        psMetadata *camera = psMetadataAlloc();
+        // Test with unitialized camera metadata
+        bool rc = pmConfigValidateCameraFormat(&valid, camera, header);
+        ok(rc == false && valid == false, "pmConfigValidateCameraFormat() returned FALSE with uninitialized psMetadata");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_UNKNOWN == tmpErr->code,
+          "pmConfigValidateCameraFormat() created the PS_ERR_UNKNOWN error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(camera);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with acceptable input
+    // psTraceSetLevel("psModules.config", 10);
+    // Add a test that sets the metadata value incorrectly
+    {
+        psMemId id = psMemGetId();
+        bool valid;
+        psMetadata *header = psMetadataAlloc();
+        psMetadata *camera = NULL;
+        bool rc = pmConfigFileRead(&camera, "../dataFiles/camera0/format0.config", "Camera 0 Config File");
+        if (!rc) {
+            rc = pmConfigFileRead(&camera, "dataFiles/camera0/format0.config", "Camera 0 Config File");
+	}
+        ok(rc == true, "pmConfigFileRead() read ../dataFiles/camera0/format0.config");
+        psMetadataAddStr(header, PS_LIST_TAIL, "F0_KEY0", 0, "", "string20");
+        rc = pmConfigValidateCameraFormat(&valid, camera, header);
+        ok(rc == true, "pmConfigValidateCameraFormat() returned FALSE with acceptable input, wrong values");
+        ok(valid == false, "pmConfigValidateCameraFormat() rejected header correctly");
+        psMetadataAddStr(header, PS_LIST_TAIL, "F0_KEY1", 0, "", "string21");
+        rc = pmConfigValidateCameraFormat(&valid, camera, header);
+        ok(rc == true, "pmConfigValidateCameraFormat() returned FALSE with acceptable input, wrong values");
+        ok(valid == false, "pmConfigValidateCameraFormat() rejected header correctly");
+        psMetadataAddS32(header, PS_LIST_TAIL, "F0_KEY2", 0, "", 20);
+        rc = pmConfigValidateCameraFormat(&valid, camera, header);
+        ok(rc == true, "pmConfigValidateCameraFormat() returned TRUE with acceptable input, correct values");
+        ok(valid == true, "pmConfigValidateCameraFormat() accepted header correctly");
+        psFree(camera);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigCameraFormatFromHeader()
+    //
+    // Given a FITS header, check it against all known cameras (unless we
+    // already know which camera, from pmConfigRead) and all known formats
+    // for those cameras in order to identify which is appropriate.
+    //
+    // psMetadata *pmConfigCameraFormatFromHeader(
+    //    pmConfig *config,
+    //    const psMetadata *header,
+    //    bool readRecipes)
+    // Test with NULL config pointer
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config = pmConfigAlloc();
+        psMetadata *header = psMetadataAlloc();
+        bool readRecipes = false;
+        psMetadata *camera = pmConfigCameraFormatFromHeader(NULL, header, readRecipes);
+        ok(camera == NULL, "pmConfigCameraFormatFromHeader() returned NULL with NULL config pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigCameraFormatFromHeader() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(config);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with NULL header pointer
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config = pmConfigAlloc();
+        psMetadata *header = psMetadataAlloc();
+        bool readRecipes = false;
+        psMetadata *camera = pmConfigCameraFormatFromHeader(config, NULL, readRecipes);
+        ok(camera == NULL, "pmConfigCameraFormatFromHeader() returned NULL with NULL header pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigCameraFormatFromHeader() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(config);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with acceptable inputs
+    if (1) {
+        psMemId id = psMemGetId();
+        pmConfig *config = pmConfigAlloc();
+        psMetadata *header = psMetadataAlloc();
+        bool readRecipes = false;
+        // Load a sample config file
+        bool rc = pmConfigFileRead(&config->site, "../dataFiles/SampleIPPConfig", "DESCRIPTION");
+        if (!rc) {
+            rc = pmConfigFileRead(&config->site, "dataFiles/SampleIPPConfig", "DESCRIPTION");
+	}
+        ok(rc == true && !config, "pmConfigFileRead() was successful");
+        // Set metadata tags for a simulated FITS header
+        psMetadataAddStr(header, PS_LIST_TAIL, "F0_KEY0", 0, "", "string20");
+        psMetadataAddStr(header, PS_LIST_TAIL, "F0_KEY1", 0, "", "string21");
+        psMetadataAddS32(header, PS_LIST_TAIL, "F0_KEY2", 0, "", 20);
+        psMetadata *camera = pmConfigCameraFormatFromHeader(config, header, readRecipes);
+
+        ok(camera != NULL,
+          "pmConfigCameraFormatFromHeader() returned non-NULL with acceptable inputs");
+        ok(camera != NULL && psMemCheckMetadata(camera),
+          "pmConfigCameraFormatFromHeader() returned non-NULL and correct type with acceptable inputs");
+        psFree(config);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigCameraByName()
+    //    psMetadata *pmConfigCameraByName(pmConfig *config, const char *cameraName)
+    // Test with NULL config pointer
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config = pmConfigAlloc();
+        psString cameraName = "CameraName";
+        psMetadata *camera = pmConfigCameraByName(NULL, cameraName);
+        ok(camera == NULL, "pmConfigCameraByName() returned NULL with NULL config pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigCameraByName() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with NULL camera Name
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config = pmConfigAlloc();
+        // psString cameraName = "CameraName";
+        psMetadata *camera = pmConfigCameraByName(config, NULL);
+        ok(camera == NULL, "pmConfigCameraByName() returned NULL with NULL camera name");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigCameraByName() created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    psTraceSetLevel("err", 10);
+    // Test with acceptable data
+    if (1) {
+        psMemId id = psMemGetId();
+        pmConfigSet("data");
+        pmConfig *config = pmConfigAlloc();
+        bool rc = pmConfigFileRead(&config->site, "../dataFiles/SampleIPPConfig", "DESCRIPTION");
+        if (!rc) {
+            rc = pmConfigFileRead(&config->site, "dataFiles/SampleIPPConfig", "DESCRIPTION");
+	}
+        ok(rc == true, "pmConfigFileRead() was successful");
+        psString cameraName = "CAMERA0";
+        psMetadata *camera = pmConfigCameraByName(config, cameraName);
+        ok(camera != NULL && psMemCheckMetadata(camera),
+          "pmConfigCameraByName() returned non-NULL with acceptable");
+        char *tmpStr = psMetadataLookupStr(&rc, camera, "ID");
+        ok(tmpStr != NULL && !strcmp(tmpStr, "CAMERA0"), "pmConfigCameraByName() returned non-NULL(CAMERA0)");
+        pmConfigDone();
+        psFree(config);
+        psFree(tmpStr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigDB()
+    // psDB *pmConfigDB(pmConfig *config)
+    // Test with NULL config pointer
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config = pmConfigAlloc();
+        psDB *db = pmConfigDB(NULL);
+        ok(db == NULL, "pmConfigDB() returned NULL with NULL config pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigDB()() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with NULL config->site pointer
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config = pmConfigAlloc();
+        config->site = NULL;
+        psDB *db = pmConfigDB(config);
+        ok(db == NULL, "pmConfigDB() returned NULL with NULL config->site pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigDB()() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with acceptable data
+    // XXX: Not tested.
+    if (1) {
+        psMemId id = psMemGetId();
+        pmConfig *config = pmConfigAlloc();
+        config->site = psMetadataAlloc();
+        bool rc = pmConfigFileRead(&config->site, "../dataFiles/SampleIPPConfig", "DESCRIPTION");
+        if (!rc) {
+            rc = pmConfigFileRead(&config->site, "dataFiles/SampleIPPConfig", "DESCRIPTION");
+	}
+        ok(rc == true, "pmConfigFileRead() was successful");
+        skip_start(rc == false, 1, "Skipping pmConfigDB() tests because we could not read config file");
+        psDB *db = pmConfigDB(config);
+        ok(db != NULL, "pmConfigDB() returned non NULL with acceptable data");
+        psFree(db);
+        skip_end();
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigConformHeader()
+    // Test with NULL header pointer
+    {
+        psMemId id = psMemGetId();
+        psMetadata *header = psMetadataAlloc();
+        psMetadata *format = psMetadataAlloc();
+        bool rc = pmConfigConformHeader(NULL, (const psMetadata *) format);
+        ok(rc == false, "pmConfigConformHeader() returned FALSE with NULL header pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigConformHeader() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(header);
+        psFree(format);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with NULL format pointer
+    {
+        psMemId id = psMemGetId();
+        psMetadata *header = psMetadataAlloc();
+        psMetadata *format = psMetadataAlloc();
+        bool rc = pmConfigConformHeader(header, NULL);
+        ok(rc == false, "pmConfigConformHeader() returned FALSE with NULL header pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigConformHeader() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(header);
+        psFree(format);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Test with acceptable data
+    {
+        psMemId id = psMemGetId();
+        bool valid;
+        psMetadata *header = psMetadataAlloc();
+        psMetadata *format = psMetadataAlloc();
+        bool rc = pmConfigFileRead(&format, "../dataFiles/camera0/format0.config", "Camera 0 Config File");
+        if (!rc) {
+            rc = pmConfigFileRead(&format, "dataFiles/camera0/format0.config", "Camera 0 Config File");
+	}
+        ok(rc == true, "pmConfigFileRead() read camera format correctly");
+
+        // First ensure that the header is not accepted
+        rc = pmConfigValidateCameraFormat(&valid, format, header);
+        ok(rc == true && valid == false, "pmConfigValidateCameraFormat() rejected header with uninitialized psMetadata");
+
+        // Now call pmConfigConformHeader() and ensure that the header is accepted
+        rc = pmConfigConformHeader(header, format);
+        ok(rc == true, "pmConfigConformHeader() returned TRUE");
+        rc = pmConfigValidateCameraFormat(&valid, format, header);
+        ok(rc == true, "pmConfigValidateCameraFormat() returned TRUE");
+        ok(valid == true, "pmConfigValidateCameraFormat() validated camera format");
+        psFree(header);
+        psFree(format);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigFileSets()
+    // test with NULL argc pointer
+    {
+        psMemId id = psMemGetId();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        char *filename = "FILENAME";
+        char *list = "LIST";
+        psArray *array = pmConfigFileSets(NULL, str, filename, list);
+        if (!array) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            array = pmConfigFileSets(NULL, str, filename, list);
+	}
+        ok(array == NULL, "pmConfigFileSets() returned NULL with NULL argc pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigFileSets() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with negative argc pointer
+    {
+        psMemId id = psMemGetId();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int myArgc = -1;
+        char *filename = "FILENAME";
+        char *list = "LIST";
+        psArray *array = pmConfigFileSets(&myArgc, str, filename, list);
+        if (!array) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            array = pmConfigFileSets(&myArgc, str, filename, list);
+	}
+        ok(array == NULL, "pmConfigFileSets() returned NULL with negative argc pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigFileSets() created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with NULL argv pointer
+    {
+        psMemId id = psMemGetId();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int myArgc = 3;
+        char *filename = "FILENAME";
+        char *list = "LIST";
+        psArray *array = pmConfigFileSets(&myArgc, NULL, filename, list);
+        ok(array == NULL, "pmConfigFileSets() returned NULL with NULL argv pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigFileSets() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with NULL filename pointer
+    {
+        psMemId id = psMemGetId();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int myArgc = 3;
+        char *list = "LIST";
+        psArray *array = pmConfigFileSets(&myArgc, str, NULL, list);
+        if (!array) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            array = pmConfigFileSets(&myArgc, str, NULL, list);
+	}
+        ok(array == NULL, "pmConfigFileSets() returned NULL with NULL filename pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigFileSets() created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with NULL list pointer
+    {
+        psMemId id = psMemGetId();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int myArgc = 3;
+        char *filename = "FILENAME";
+        psArray *array = pmConfigFileSets(&myArgc, str, filename, NULL);
+        if (!array) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            array = pmConfigFileSets(&myArgc, str, filename, NULL);
+	}
+        ok(array == NULL, "pmConfigFileSets() returned NULL with NULL list pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigFileSets() created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with acceptable data
+    if (0) {
+        psMemId id = psMemGetId();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int myArgc = 3;
+        char *filename = "FILENAME";
+        char *list = "LIST";
+        psArray *array = pmConfigFileSets(&myArgc, str, filename, list);
+        if (!array) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            array = pmConfigFileSets(&myArgc, str, filename, list);
+	}
+        ok(array != NULL && psMemCheckArray(array),
+          "pmConfigFileSets() returned non-NULL with acceptable input data");
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigFileSetsMD()
+    // bool pmConfigFileSetsMD(psMetadata *metadata, int *argc, char **argv, const char *name,
+    //                         const char *file, const char *list)
+    // XX: Should we test/check for bad argv/argc params?
+    // test with NULL metadata pointer
+    {
+        psMemId id = psMemGetId();
+        psMetadata *metadata = psMetadataAlloc();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int myArgc = 3;
+        char *name = "NAME";
+        char *file = "FILENAME";
+        char *list = "LIST";
+        bool rc = pmConfigFileSetsMD(NULL, &myArgc, str, name, file, list);
+        if (!rc) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            rc = pmConfigFileSetsMD(NULL, &myArgc, str, name, file, list);
+	}
+        ok(rc == false, "pmConfigFileSetsMD() returned FALSE with NULL metadata pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigFileSetsMD() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psFree(metadata);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with NULL name pointer
+    {
+        psMemId id = psMemGetId();
+        psMetadata *metadata = psMetadataAlloc();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int myArgc = 3;
+        char *file = "FILENAME";
+        char *list = "LIST";
+        bool rc = pmConfigFileSetsMD(metadata, &myArgc, str, NULL, file, list);
+        if (!rc) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            rc = pmConfigFileSetsMD(metadata, &myArgc, str, NULL, file, list);
+	}
+        ok(rc == false, "pmConfigFileSetsMD() returned FALSE with NULL name pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigFileSetsMD() created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psFree(metadata);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with NULL file pointer
+    {
+        psMemId id = psMemGetId();
+        psMetadata *metadata = psMetadataAlloc();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int myArgc = 3;
+        char *name = "NAME";
+        char *list = "LIST";
+        bool rc = pmConfigFileSetsMD(metadata, &myArgc, str, name, NULL, list);
+        if (!rc) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            rc = pmConfigFileSetsMD(metadata, &myArgc, str, name, NULL, list);
+	}
+        ok(rc == false, "pmConfigFileSetsMD() returned FALSE with NULL file pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigFileSetsMD() created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psFree(metadata);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with NULL list pointer
+    {
+        psMemId id = psMemGetId();
+        psMetadata *metadata = psMetadataAlloc();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int myArgc = 3;
+        char *name = "NAME";
+        char *file = "FILENAME";
+        bool rc = pmConfigFileSetsMD(metadata, &myArgc, str, name, file, NULL);
+        if (!rc) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            rc = pmConfigFileSetsMD(metadata, &myArgc, str, name, file, NULL);
+	}
+        ok(rc == false, "pmConfigFileSetsMD() returned FALSE with NULL list pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigFileSetsMD() created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psFree(metadata);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with acceptable input data
+    if (0) {
+        psMemId id = psMemGetId();
+        psMetadata *metadata = psMetadataAlloc();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int myArgc = 3;
+        char *name = "NAME";
+        char *file = "FILENAME";
+        char *list = "LIST";
+        bool rc = pmConfigFileSetsMD(metadata, &myArgc, str, name, file, list);
+        if (!rc) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            rc = pmConfigFileSetsMD(metadata, &myArgc, str, name, file, list);
+	}
+        ok(rc == true, "pmConfigFileSetsMD() returned TRUE with acceptable input data");
+        psFree(metadata);
+        psErrorClear();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigConvertFilename()
+    // convert the supplied name, create a new output psString
+    // psString pmConfigConvertFilename(const char *filename, const pmConfig *config,
+    //                                  bool create)
+    // test with NULL filename pointer
+    {
+        psMemId id = psMemGetId();
+        pmConfig *config = pmConfigAlloc();
+        bool create = false;
+        bool rc = pmConfigConvertFilename(NULL, (const pmConfig *) config, create, false);
+        ok(rc == false, "pmConfigConvertFilename() returned FALSE with NULL filename pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_VALUE == tmpErr->code,
+          "pmConfigConvertFilename() created the PS_ERR_BAD_PARAMETER_VALUE error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with NULL config pointer
+    {
+        psMemId id = psMemGetId();
+        char *name = "NAME";
+        pmConfig *config = pmConfigAlloc();
+        bool create = false;
+        bool rc = pmConfigConvertFilename(name, NULL, create, false);
+        ok(rc == false, "pmConfigConvertFilename() returned FALSE with NULL config pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigConvertFilename() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test with acceptable input data
+    // First call with create==FALSE, and a filename that does not exist.
+    // Should return NULL.  Then set create==TRUE, and call; should return
+    // "JUNK".  Then call again with create==FALSE; should return "JUNK"
+    if (1) {
+        psMemId id = psMemGetId();
+        char *filename = "file:///////JUNK";
+        pmConfig *config = pmConfigAlloc();
+        bool rc = pmConfigFileRead(&config->site, "../dataFiles/SampleIPPConfig", "DESCRIPTION");
+        if (!rc) {
+            rc = pmConfigFileRead(&config->site, "dataFiles/SampleIPPConfig", "DESCRIPTION");
+	}
+        ok(rc == true, "pmConfigFileRead() was successful");
+        bool create = false;
+        psString tmpStr = pmConfigConvertFilename(filename, (const pmConfig *) config, create, false);
+        ok(NULL == tmpStr, "pmConfigConvertFilename() returned NULL with create==FALSE");
+        create = true;
+        tmpStr = pmConfigConvertFilename(filename, (const pmConfig *) config, create, false);
+        ok(tmpStr != NULL, "pmConfigConvertFilename() returned non-NULL with create==TRUE");
+        ok(!strcmp(tmpStr, "/JUNK"), "pmConfigConvertFilename() returned correct filename (%s)", tmpStr);
+        psFree(tmpStr);
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
+    // Test pmConfigRead()
+    // Test with NULL argc pointer
+    {
+        psMemId id = psMemGetId();
+        psString str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        pmConfig *myConfig = pmConfigRead(NULL, str, "RecipeName");
+        if (!myConfig) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            myConfig = pmConfigRead(NULL, str, "RecipeName");
+	}
+        ok(myConfig == NULL, "pmConfigRead() returned NULL with NULL argc pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigValidateCameraFormat() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(myConfig);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with NULL argv pointer
+    {
+        psMemId id = psMemGetId();
+        pmConfig *myConfig = pmConfigRead(&argc, NULL, "RecipeName");
+        ok(myConfig == NULL, "pmConfigRead() returned NULL with NULL argv pointer");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigValidateCameraFormat() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(myConfig);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test with NULL recipe string (should not cause error)
+    // XXX: Not working, debug
+    if (0) {
+        psMemId id = psMemGetId();
+        char *str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        int numArgs = 3;
+        pmConfig *myConfig = pmConfigRead(&numArgs, str, NULL);
+        if (!myConfig) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            myConfig = pmConfigRead(&numArgs, str, NULL);
+	}
+        ok(myConfig != NULL, "pmConfigRead() returned non-NULL with NULL recipe");
+        psErr *tmpErr = psErrorLast();
+        ok(PS_ERR_BAD_PARAMETER_NULL == tmpErr->code,
+          "pmConfigValidateCameraFormat() created the PS_ERR_BAD_PARAMETER_NULL error");
+        psFree(tmpErr);
+        psErrorClear();
+        psFree(myConfig);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmConfigRead() with acceptable data.
+    if (1) {
+        psMemId id = psMemGetId();
+        bool testStatus = true;
+        psMetadata *site = psMetadataAlloc();
+        psMetadata *camera = psMetadataAlloc();
+        psMetadata *recipe = psMetadataAlloc();
+        psString str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        psS32 argc = 3;
+        printf("----------------------------------------------------------------\n");
+        printf("Calling pmConfigRead() with acceptable arguments.\n");
+        bool rc;
+        pmConfig *myConfig = pmConfigRead(&argc, str, "RecipeName");
+        if (!myConfig) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            myConfig = pmConfigRead(&argc, str, "RecipeName");
+	}
+        ok(myConfig, "pmConfigRead() returned non-NULL");
+        if (myConfig == NULL) {
+            diag("TEST ERROR: pmConfigRead() returned NULL\n");
+            testStatus = false;
+        } else {
+            //
+            // Ensure that the various trace and logging values are set properly
+            //
+            if (1 != psTraceGetLevel("dummyTraceFunc01")) {
+                diag("TEST ERROR: failed to properly set tracelevel for dummyTraceFunc01\n");
+                diag("    tracelevel was %d, should be 1.\n", psTraceGetLevel("dummyTraceFunc01"));
+                testStatus = false;
+            }
+
+            if (2 != psTraceGetLevel("dummyTraceFunc02")) {
+                diag("TEST ERROR: failed to properly set tracelevel for dummyTraceFunc02\n");
+                diag("    tracelevel was %d, should be 2.\n", psTraceGetLevel("dummyTraceFunc02"));
+                testStatus = false;
+            }
+
+            if (1 != psLogGetDestination()) {
+                diag("TEST ERROR: failed to properly set log destination.\n");
+                diag("    it was %d, should be 1\n", psLogGetDestination());
+                testStatus = false;
+            }
+
+            if (3 != psLogGetLevel()) {
+                diag("TEST ERROR: failed to properly set log level.\n");
+                diag("    it was %d, should be 3\n", psLogGetLevel());
+                testStatus = false;
+            }
+
+            //
+            // Test the several arbitrary config file strings.
+            //
+            psS32 tmpInt = psMetadataLookupS32(&rc, site, "ARBITRARY_STRING_S32");
+            if ((rc == false) || (tmpInt != 20)) {
+                diag("TEST ERROR: failed to properly set metadata integer ARBITRARY_STRING_S32.\n");
+                testStatus = false;
+            }
+
+            psF32 tmpFloat = psMetadataLookupS32(&rc, site, "ARBITRARY_STRING_F32");
+            if ((rc == false) || (tmpFloat != 21.0)) {
+                diag("TEST ERROR: failed to properly set metadata float ARBITRARY_STRING_F32.\n");
+                testStatus = false;
+            }
+
+            psF64 tmpDub = psMetadataLookupS32(&rc, site, "ARBITRARY_STRING_F64");
+            if ((rc == false) || (tmpDub != 22.0)) {
+                diag("TEST ERROR: failed to properly set metadata double ARBITRARY_STRING_F64.\n");
+                testStatus = false;
+            }
+
+            psBool tmpBool;
+            tmpBool = psMetadataLookupS32(&rc, site, "ARBITRARY_STRING_BOOL_T");
+            if ((rc == false) || (tmpBool != true)) {
+                diag("TEST ERROR: failed to properly set metadata double ARBITRARY_STRING_BOOL_T.\n");
+                testStatus = false;
+            }
+            tmpBool = psMetadataLookupS32(&rc, site, "ARBITRARY_STRING_BOOL_F");
+            if ((rc == false) || (tmpBool != false)) {
+                diag("TEST ERROR: failed to properly set metadata double ARBITRARY_STRING_BOOL_F.\n");
+                testStatus = false;
+            }
+
+            //
+            // Test the database camera metadata keywords.
+            //
+            psMetadata *tmpMeta = psMetadataLookupMetadata(&rc, site, "CAMERAS");
+            if (rc == false) {
+                diag("TEST ERROR: failed to properly set metadata metadata CAMERAS.\n");
+                testStatus = false;
+            } else {
+                psString tmpStr;
+                tmpStr = psMetadataLookupStr(&rc, tmpMeta, "MEGACAM_RAW");
+                if ((rc == false) || (0 != strcmp(tmpStr, "megacam_raw.config"))) {
+                    diag("TEST ERROR: failed to properly set metadata metadata CAMERAS->MEGACAM_RAW.\n");
+                    testStatus = false;
+                }
+
+                tmpStr = psMetadataLookupStr(&rc, tmpMeta, "MEGACAM_SPLICE");
+                if ((rc == false) || (0 != strcmp(tmpStr, "megacam_splice.config"))) {
+                    diag("TEST ERROR: failed to properly set metadata metadata CAMERAS->MEGACAM_SPLICE.\n");
+                    testStatus = false;
+                }
+
+                tmpStr = psMetadataLookupStr(&rc, tmpMeta, "GPC1_RAW");
+                if ((rc == false) || (0 != strcmp(tmpStr, "gpc1_raw.config"))) {
+                    diag("TEST ERROR: failed to properly set metadata metadata CAMERAS->GPC1_RAW.\n");
+                    testStatus = false;
+                }
+
+                tmpStr = psMetadataLookupStr(&rc, tmpMeta, "LRIS_BLUE");
+                if ((rc == false) || (0 != strcmp(tmpStr, "lris_blue.config"))) {
+                    diag("TEST ERROR: failed to properly set metadata metadata CAMERAS->LRIS_BLUE.\n");
+                    testStatus = false;
+                }
+
+                tmpStr = psMetadataLookupStr(&rc, tmpMeta, "LRIS_RED");
+                if ((rc == false) || (0 != strcmp(tmpStr, "lris_red.config"))) {
+                    diag("TEST ERROR: failed to properly set metadata metadata CAMERAS->LRIS_RED.\n");
+                    testStatus = false;
+                }
+            }
+
+            //
+            // Test the database metadata keywords.  This is somewhat redundant since
+            // we already test metadat strings.
+            //
+            psString tmpStr;
+            tmpStr = psMetadataLookupStr(&rc, site, "DBSERVER");
+            if ((rc == false) || (0 != strcmp(tmpStr, "ippdb.ifa.hawaii.edu"))) {
+                diag("TEST ERROR: failed to properly set metadata string DBSERVER.\n");
+                testStatus = false;
+            }
+
+            tmpStr = psMetadataLookupStr(&rc, site, "DBUSER");
+            if ((rc == false) || (0 != strcmp(tmpStr, "ipp"))) {
+                diag("TEST ERROR: failed to properly set metadata string DBUSER.\n");
+                testStatus = false;
+            }
+
+            tmpStr = psMetadataLookupStr(&rc, site, "DBPASSWORD");
+            if ((rc == false) || (0 != strcmp(tmpStr, "password"))) {
+                diag("TEST ERROR: failed to properly set metadata string DBPASSWORD.\n");
+                testStatus = false;
+            }
+        }
+        psFree(site);
+        psFree(camera);
+        psFree(recipe);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmConfigCommand.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmConfigCommand.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmConfigCommand.c	(revision 22322)
@@ -0,0 +1,155 @@
+/** @file tst_pmConfigCommand.c
+ *
+ *  @brief Contains the tests for pmConfigCommand.c:
+ *
+ * This code will test the pmConfigCommand() routine.
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-01-02 20:49:09 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+#define ERR_TRACE_LEVEL         0
+#define VERBOSE			0
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(14);
+
+
+    // --------------------------------------------------------------------
+    // pmConfigDatabaseCommand() tests
+    // Test pmConfigDatabaseCommand() with NULL command input param
+    // XXX: I think the memory leak is in pmConfigRead()
+    {
+        psMemId id = psMemGetId();
+        psString str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        psS32 argc = 3;
+        pmConfig *config = pmConfigRead(&argc, str, "RecipeName");
+        if (!config) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            config = pmConfigRead(&argc, str, "RecipeName");
+	}
+        ok(config != NULL, "pmConfigRead() successful");
+        ok(!pmConfigDatabaseCommand(NULL, config), "pmConfigDatabaseCommand() returned NULL with NULL command input param");
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmConfigDatabaseCommand() with NULL pmConfig input param
+    // XXX: I think the memory leak is in pmConfigRead()
+    {
+        psMemId id = psMemGetId();
+        psString testCmd = psStringCopy("my command");
+        ok(!pmConfigDatabaseCommand(&testCmd, NULL), "pmConfigDatabaseCommand() returned NULL with NULL command input param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmConfigDatabaseCommand() with command arg generated by other than psStringDup()
+    // XXX: This seg-faults because pmConfigDatabaseCommand(), or whichever function,
+    // does not verify that the command was allocated as a psString.
+    if (0) {
+        psMemId id = psMemGetId();
+        psString str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        psS32 argc = 3;
+        pmConfig *config = pmConfigRead(&argc, str, "RecipeName");
+        if (!config) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            config = pmConfigRead(&argc, str, "RecipeName");
+	}
+        ok(config != NULL, "pmConfigRead() successful");
+        char *testCmd = "command";
+        ok(!pmConfigDatabaseCommand(&testCmd, config), "pmConfigDatabaseCommand() returned NULL with NULL command input param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmConfigDatabaseCommand() with acceptable input params
+    // XXX: I think the memory leak is in pmConfigRead()
+    {
+        psMemId id = psMemGetId();
+        psString str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "../dataFiles/SampleIPPConfig";
+        psS32 argc = 3;
+        pmConfig *config = pmConfigRead(&argc, str, "RecipeName");
+        if (!config) {
+            str[2] = "dataFiles/SampleIPPConfig";
+            config = pmConfigRead(&argc, str, "RecipeName");
+	}
+        ok(config != NULL, "pmConfigRead() successful");
+        psString testCmd = psStringCopy("my command");
+        psString verifyCmd = psStringCopy(testCmd);
+        // Generate the verify test string
+        {
+            bool mdok;
+            psString dbserver = psMetadataLookupStr(&mdok, config->site, "DBSERVER");
+            psString dbname = psMetadataLookupStr(&mdok, config->site, "DBNAME");
+            psString dbuser = psMetadataLookupStr(&mdok, config->site, "DBUSER");
+            psString dbpassword = psMetadataLookupStr(&mdok, config->site, "DBPASSWORD");
+            psStringAppend(&verifyCmd, " -dbserver %s -dbname %s -dbuser %s -dbpassword %s",
+                           dbserver, dbname, dbuser, dbpassword);
+	}
+
+        ok(pmConfigDatabaseCommand(&testCmd, config), "pmConfigDatabaseCommand() returned non-NULL with acceptable input params");
+        ok(!strcmp(testCmd, verifyCmd), "pmConfigDatabaseCommand() generated the correct command string");
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------------
+    // pmConfigTraceCommand() tests
+    // Test pmConfigTraceCommand() with NULL command input param
+    {
+        psMemId id = psMemGetId();
+        ok(!pmConfigTraceCommand(NULL), "pmConfigTraceCommand() returned NULL with NULL command input param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmConfigTraceCommand() with acceptable input command input param
+    {
+        psMemId id = psMemGetId();
+        psTraceSetLevel("dummyTraceLevel", 0);
+        psString testCmd = psStringCopy("my command");
+        psString verifyCmd = psStringCopy(testCmd);
+        // Generate the verify test string
+        {
+            psMetadata *levels = psTraceLevels();
+            psMetadataIterator *iter = psMetadataIteratorAlloc(levels, PS_LIST_HEAD, NULL);
+            psMetadataItem *item;
+            while ((item = psMetadataGetAndIncrement(iter))) {
+                assert(item->type == PS_TYPE_S32);
+                psStringAppend(&verifyCmd, " -trace %s %d", item->name, item->data.S32);
+            }
+            psFree(iter);
+            psFree(levels);
+	}
+        ok(pmConfigTraceCommand(&testCmd), "pmConfigTraceCommand() returned non-NULL with acceptable input param");
+        ok(!strcmp(testCmd, verifyCmd), "pmConfigTraceCommand() generated the correct command string");
+        psFree(testCmd);
+        psFree(verifyCmd);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+    
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmConfigMask.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmConfigMask.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmConfigMask.c	(revision 22322)
@@ -0,0 +1,89 @@
+/** @file tst_pmConfigMask.c
+ *
+ *  @brief Contains the tests for pmConfigMask.c:
+ *
+ * This code will test the pmConfigMask() routine.
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2008-01-02 20:49:10 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+#define ERR_TRACE_LEVEL         0
+#define VERBOSE			0
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(7);
+
+
+    // --------------------------------------------------------------------
+    // pmConfigMask() tests
+    // Test pmConfigMask() with NULL masks input param
+    // XXX: I think the memory leak is in pmConfigRead()
+    {
+        psMemId id = psMemGetId();
+        psString str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "dataFiles/SampleIPPConfig";
+        psS32 argc = 3;
+        pmConfig *config = pmConfigRead(&argc, str, "RecipeName");
+        if (!config) {
+            str[2] = "../dataFiles/SampleIPPConfig";
+            config = pmConfigRead(&argc, str, "RecipeName");
+	}
+        ok(config != NULL, "pmConfigRead() successful");
+        ok(!pmConfigMask(NULL, config), "pmConfigMask() returned NULL with NULL masks input param");
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmConfigMask() with NULL pmConfig input param
+    // XXX: This fails on current CVS code because there is no assert in pmConfigMask() for a null pmConfig param
+    if (0) {
+        psMemId id = psMemGetId();
+        char *masks = "Mask0 Mask1 Mask2";
+        ok(!pmConfigMask(masks, NULL), "pmConfigMask() returned NULL with NULL pmConfig input param");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmConfigMask() with acceptable input params
+    // XXX: I think the memory leak is in pmConfigRead()
+    {
+        psMemId id = psMemGetId();
+        // See file ../dataFiles/recipes_masks.config (
+        char *masks = "DETECTOR RANGE";
+        psMaskType correctMask = 0x02 | 0x04;
+        psString str[3];
+        str[0] = "ARGS:";
+        str[1] = "-site";
+        str[2] = "dataFiles/SampleIPPConfig";
+        psS32 argc = 3;
+        pmConfig *config = pmConfigRead(&argc, str, "RecipeName");
+        if (!config) {
+            str[2] = "../dataFiles/SampleIPPConfig";
+            config = pmConfigRead(&argc, str, "RecipeName");
+	}
+        ok(config != NULL, "pmConfigRead() successful");
+        skip_start(config == NULL, 2, "Skipping tests because pmConfigRead() failed");
+        psMaskType mask = pmConfigMask(masks, config);
+        ok(mask, "pmConfigMask returned non-zero with acceptable input params");
+        ok(mask == correctMask, "pmConfigMask() generated the correct output mask (%x).  Should be (%x)", mask, correctMask);
+        skip_end();
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmErrorCodes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmErrorCodes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmErrorCodes.c	(revision 22322)
@@ -0,0 +1,98 @@
+/** @file tst_pmErrorCodes.c
+ *
+ *  @brief Contains the tests for pmErrorCodes.c:
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-09-18 18:58:58 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+#define ERR_TRACE_LEVEL         0
+#define VERBOSE			0
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(16);
+
+
+    // --------------------------------------------------------------------
+    // pmErrorRegister() tests
+    // Test pmErrorRegister() with currently coded error types
+    {
+        psMemId id = psMemGetId();
+        pmErrorRegister();
+        char *errStr;
+        errStr = (char *) psErrorCodeString(PM_ERR_BASE);
+        ok(!strcmp("First value we use; lower values belong to psLib", errStr),
+           "psErrorCodeString(PM_ERR_BASE)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_UNKNOWN);
+        ok(!strcmp("Unknown psModules error code", errStr),
+           "psErrorCodeString(PM_ERR_UNKNOWN)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_PHOTOM);
+        ok(!strcmp("Problem in photometry", errStr),
+           "psErrorCodeString(PM_ERR_PHOTOM)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_PSF);
+        ok(!strcmp("Problem in PSF", errStr),
+           "psErrorCodeString(PM_ERR_PSF)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_ASTROM);
+        ok(!strcmp("Problem in astrometry", errStr),
+           "psErrorCodeString(PM_ERR_ASTROM)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_CAMERA);
+        ok(!strcmp("Problem in camera", errStr),
+           "psErrorCodeString(PM_ERR_CAMERA)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_CONCEPTS);
+        ok(!strcmp("Problem in concepts", errStr),
+           "psErrorCodeString(PM_ERR_CONCEPTS)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_IMCOMBINE);
+        ok(!strcmp("Problem in imcombine", errStr),
+           "psErrorCodeString(PM_ERR_IMCOMBINE)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_OBJECTS);
+        ok(!strcmp("Problem in objects", errStr),
+           "psErrorCodeString(PM_ERR_OBJECTS)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_SKY);
+        ok(!strcmp("Problem in sky", errStr),
+           "psErrorCodeString(PM_ERR_SKY)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_ARGUMENTS);
+        ok(!strcmp("Incorrect arguments", errStr),
+           "psErrorCodeString(PM_ERR_ARGUMENTS)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_SYS);
+        ok(!strcmp("System error", errStr),
+           "psErrorCodeString(PM_ERR_SYS)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_CONFIG);
+        ok(!strcmp("Problem in configure files", errStr),
+           "psErrorCodeString(PM_ERR_CONFIG)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_PROG);
+        ok(!strcmp("Programming error", errStr),
+           "psErrorCodeString(PM_ERR_PROG)");
+
+        errStr = (char *) psErrorCodeString(PM_ERR_DATA);
+        ok(!strcmp("invalid data", errStr),
+           "psErrorCodeString(PM_ERR_DATA)");
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/tap_pmVersion.c	(revision 22322)
@@ -0,0 +1,58 @@
+/** @file tst_pmVersion.c
+ *
+ *  @brief Contains the tests for pmVersion.c:
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-09-18 18:59:00 $
+ *
+ *  XX: The output version string for psModulesVersionLong() is not verified.
+ *  Not sure how to do this since this psModulesVersionLong() produces the
+ *  time that the source code, not the test code, was compiled.
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+#include "config.h"
+#define ERR_TRACE_LEVEL         0
+#define VERBOSE			0
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(4);
+
+
+    // --------------------------------------------------------------------
+    // psModulesVersion() tests
+    // XX: The output string is not verified
+    {
+        psMemId id = psMemGetId();
+        psString tstStr = NULL;
+        psStringAppend(&tstStr, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+        psString str = psModulesVersion();
+        ok(!strcmp(str, tstStr), "psModulesVersion()");
+        psFree(str);
+        psFree(tstStr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // --------------------------------------------------------------------
+    // psModulesVersionLong() tests
+    // XX: The output string is not verified
+    {
+        psMemId id = psMemGetId();
+        psString str = psModulesVersionLong();
+        ok(str, "psModulesVersionLong()");
+        psFree(str);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/config/tst_pmConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/config/tst_pmConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/config/tst_pmConfig.c	(revision 22322)
@@ -0,0 +1,277 @@
+/** @file tst_pmConfig.c
+ *
+ *  @brief Contains the tests for pmConfig.c:
+ *
+ * test00: This code will test the pmConfig() routine.
+ *
+ *  @author GLG, MHPCC
+ *
+ * XXX: Untested:
+ * pmConfigValidateCamera()
+ * pmConfigCameraFromHeader()
+ * pmConfigRecipeFromCamera()
+ * pmConfigDB()
+ *
+ * XXXX: Must determine what to do with NULL arguments, then test it.
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-10-13 21:15:45 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include "psTest.h"
+#include "pslib.h"
+#include "pmConfig.h"
+
+static int test00(void);
+testDescription tests[] = {
+                              {test00, 000, "pmConfig:", true, false},
+                              {NULL}
+                          };
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    //
+    // We include the function names here in psTraceSetLevel() commands for
+    // debugging convenience.  There is no guarantee that this list of functions
+    // is complete.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("readConfig", 0);
+    psTraceSetLevel("pmConfigRead", 0);
+    psTraceSetLevel("pmConfigValidateCamera", 0);
+    psTraceSetLevel("pmConfigCameraFromHeader", 0);
+    psTraceSetLevel("pmConfigRecipeFromCamera", 0);
+    psTraceSetLevel("pmConfigDB", 0);
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+/******************************************************************************
+test00():
+XXX: untested:
+    TIME:
+    LOGFORMAT: tested implicitly via the stdout files.
+    Command line arguments for logging shall override config file defaults.
+ *****************************************************************************/
+int test00( void )
+{
+    psTraceSetLevel(".", 0);
+    bool testStatus = true;
+    psBool rc = 0;
+    psString str[3];
+    str[0] = "ARGS:";
+    str[1] = "-site";
+    str[2] = "SampleIPPConfig";
+
+    psMetadata *site = psMetadataAlloc();
+    psMetadata *camera = psMetadataAlloc();
+    psMetadata *recipe = psMetadataAlloc();
+    psS32 argc = 3;
+
+
+    psMetadata *nullMD = NULL;
+    if (0) {
+        printf("----------------------------------------------------------------\n");
+        printf("Calling pmConfigRead() with NULL site pointer.  Should generate ERROR, return false.\n");
+        rc = pmConfigRead(NULL, &camera, &recipe, &argc, str, "RecipeName");
+        if (rc == true) {
+            printf("TEST ERROR: pmConfigRead() returned true\n");
+            testStatus = false;
+        }
+
+        printf("----------------------------------------------------------------\n");
+        printf("Calling pmConfigRead() with NULL *site pointer.  Should generate ERROR, return false.\n");
+        rc = pmConfigRead(&nullMD, &camera, &recipe, &argc, str, "RecipeName");
+        if (rc == true) {
+            printf("TEST ERROR: pmConfigRead() returned true\n");
+            testStatus = false;
+        }
+
+        printf("----------------------------------------------------------------\n");
+        printf("Calling pmConfigRead() with NULL camera pointer.  Should generate ERROR, return false.\n");
+        rc = pmConfigRead(&site, NULL, &recipe, &argc, str, "RecipeName");
+        if (rc == true) {
+            printf("TEST ERROR: pmConfigRead() returned true\n");
+            testStatus = false;
+        }
+
+        printf("----------------------------------------------------------------\n");
+        printf("Calling pmConfigRead() with NULL *camera pointer.  Should generate ERROR, return false.\n");
+        rc = pmConfigRead(&site, &nullMD, &recipe, &argc, str, "RecipeName");
+        if (rc == true) {
+            printf("TEST ERROR: pmConfigRead() returned true\n");
+            testStatus = false;
+        }
+
+        printf("----------------------------------------------------------------\n");
+        printf("Calling pmConfigRead() with NULL recipe pointer.  Should generate ERROR, return false.\n");
+        rc = pmConfigRead(&site, &camera, NULL, &argc, str, "RecipeName");
+        if (rc == true) {
+            printf("TEST ERROR: pmConfigRead() returned true\n");
+            testStatus = false;
+        }
+
+        printf("----------------------------------------------------------------\n");
+        printf("Calling pmConfigRead() with NULL *recipe pointer.  Should generate ERROR, return false.\n");
+        rc = pmConfigRead(&site, &camera, &nullMD, &argc, str, "RecipeName");
+        if (rc == true) {
+            printf("TEST ERROR: pmConfigRead() returned true\n");
+            testStatus = false;
+        }
+
+        printf("----------------------------------------------------------------\n");
+        printf("Calling pmConfigRead() with NULL argv pointer.  Should generate ERROR, return false.\n");
+        rc = pmConfigRead(&site, &camera, &recipe, &argc, NULL, "RecipeName");
+        if (rc == true) {
+            printf("TEST ERROR: pmConfigRead() returned true\n");
+            testStatus = false;
+        }
+
+        printf("----------------------------------------------------------------\n");
+        printf("Calling pmConfigRead() with negative argc.  Should generate ERROR, return false.\n");
+        psS32 tmpArgc = -1;
+        rc = pmConfigRead(&site, &camera, &recipe, &tmpArgc, str, "RecipeName");
+        if (rc == true) {
+            printf("TEST ERROR: pmConfigRead() returned true\n");
+            testStatus = false;
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmConfigRead() with acceptable arguments.\n");
+
+    rc = pmConfigRead(&site, &camera, &recipe, &argc, str, "RecipeName");
+    if (rc == false) {
+        printf("TEST ERROR: pmConfigRead() returned False\n");
+        testStatus = false;
+    } else {
+        //
+        // Ensure that the various trace and logging values are set properly
+        //
+        if (1 != psTraceGetLevel("dummyTraceFunc01")) {
+            printf("TEST ERROR: failed to properly set tracelevel for dummyTraceFunc01\n");
+            printf("    tracelevel was %d, should be 1.\n", psTraceGetLevel("dummyTraceFunc01"));
+            testStatus = false;
+        }
+
+        if (2 != psTraceGetLevel("dummyTraceFunc02")) {
+            printf("TEST ERROR: failed to properly set tracelevel for dummyTraceFunc02\n");
+            printf("    tracelevel was %d, should be 2.\n", psTraceGetLevel("dummyTraceFunc02"));
+            testStatus = false;
+        }
+
+        if (1 != psLogGetDestination()) {
+            printf("TEST ERROR: failed to properly set log destination.\n");
+            printf("    it was %d, should be 1\n", psLogGetDestination());
+            testStatus = false;
+        }
+
+        if (3 != psLogGetLevel()) {
+            printf("TEST ERROR: failed to properly set log level.\n");
+            printf("    it was %d, should be 3\n", psLogGetLevel());
+            testStatus = false;
+        }
+
+        //
+        // Test the several arbitrary config file strings.
+        //
+        psS32 tmpInt = psMetadataLookupS32(&rc, site, "ARBITRARY_STRING_S32");
+        if ((rc == false) || (tmpInt != 20)) {
+            printf("TEST ERROR: failed to properly set metadata integer ARBITRARY_STRING_S32.\n");
+            testStatus = false;
+        }
+
+        psF32 tmpFloat = psMetadataLookupS32(&rc, site, "ARBITRARY_STRING_F32");
+        if ((rc == false) || (tmpFloat != 21.0)) {
+            printf("TEST ERROR: failed to properly set metadata float ARBITRARY_STRING_F32.\n");
+            testStatus = false;
+        }
+
+        psF64 tmpDub = psMetadataLookupS32(&rc, site, "ARBITRARY_STRING_F64");
+        if ((rc == false) || (tmpDub != 22.0)) {
+            printf("TEST ERROR: failed to properly set metadata double ARBITRARY_STRING_F64.\n");
+            testStatus = false;
+        }
+
+        psBool tmpBool;
+        tmpBool = psMetadataLookupS32(&rc, site, "ARBITRARY_STRING_BOOL_T");
+        if ((rc == false) || (tmpBool != true)) {
+            printf("TEST ERROR: failed to properly set metadata double ARBITRARY_STRING_BOOL_T.\n");
+            testStatus = false;
+        }
+        tmpBool = psMetadataLookupS32(&rc, site, "ARBITRARY_STRING_BOOL_F");
+        if ((rc == false) || (tmpBool != false)) {
+            printf("TEST ERROR: failed to properly set metadata double ARBITRARY_STRING_BOOL_F.\n");
+            testStatus = false;
+        }
+
+        //
+        // Test the database camera metadata keywords.
+        //
+        psMetadata *tmpMeta = psMetadataLookupMetadata(&rc, site, "CAMERAS");
+        if (rc == false) {
+            printf("TEST ERROR: failed to properly set metadata metadata CAMERAS.\n");
+            testStatus = false;
+        } else {
+            psString tmpStr;
+            tmpStr = psMetadataLookupStr(&rc, tmpMeta, "MEGACAM_RAW");
+            if ((rc == false) || (0 != strcmp(tmpStr, "megacam_raw.config"))) {
+                printf("TEST ERROR: failed to properly set metadata metadata CAMERAS->MEGACAM_RAW.\n");
+                testStatus = false;
+            }
+
+            tmpStr = psMetadataLookupStr(&rc, tmpMeta, "MEGACAM_SPLICE");
+            if ((rc == false) || (0 != strcmp(tmpStr, "megacam_splice.config"))) {
+                printf("TEST ERROR: failed to properly set metadata metadata CAMERAS->MEGACAM_SPLICE.\n");
+                testStatus = false;
+            }
+
+            tmpStr = psMetadataLookupStr(&rc, tmpMeta, "GPC1_RAW");
+            if ((rc == false) || (0 != strcmp(tmpStr, "gpc1_raw.config"))) {
+                printf("TEST ERROR: failed to properly set metadata metadata CAMERAS->GPC1_RAW.\n");
+                testStatus = false;
+            }
+
+            tmpStr = psMetadataLookupStr(&rc, tmpMeta, "LRIS_BLUE");
+            if ((rc == false) || (0 != strcmp(tmpStr, "lris_blue.config"))) {
+                printf("TEST ERROR: failed to properly set metadata metadata CAMERAS->LRIS_BLUE.\n");
+                testStatus = false;
+            }
+
+            tmpStr = psMetadataLookupStr(&rc, tmpMeta, "LRIS_RED");
+            if ((rc == false) || (0 != strcmp(tmpStr, "lris_red.config"))) {
+                printf("TEST ERROR: failed to properly set metadata metadata CAMERAS->LRIS_RED.\n");
+                testStatus = false;
+            }
+        }
+
+        //
+        // Test the database metadata keywords.  This is somewhat redundant since
+        // we already test metadat strings.
+        //
+        psString tmpStr;
+        tmpStr = psMetadataLookupStr(&rc, site, "DBSERVER");
+        if ((rc == false) || (0 != strcmp(tmpStr, "ippdb.ifa.hawaii.edu"))) {
+            printf("TEST ERROR: failed to properly set metadata string DBSERVER.\n");
+            testStatus = false;
+        }
+
+        tmpStr = psMetadataLookupStr(&rc, site, "DBUSER");
+        if ((rc == false) || (0 != strcmp(tmpStr, "ipp"))) {
+            printf("TEST ERROR: failed to properly set metadata string DBUSER.\n");
+            testStatus = false;
+        }
+
+        tmpStr = psMetadataLookupStr(&rc, site, "DBPASSWORD");
+        if ((rc == false) || (0 != strcmp(tmpStr, "password"))) {
+            printf("TEST ERROR: failed to properly set metadata string DBPASSWORD.\n");
+            testStatus = false;
+        }
+    }
+
+    psFree(site);
+    psFree(camera);
+    psFree(recipe);
+    return(testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmFlatField
+tst_pmMaskBadPixels
+tst_pmNonLinear
+.tmp_tst_pmNonLinearLookupFile
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/Makefile.am	(revision 22322)
@@ -0,0 +1,26 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSMODULES_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpsmodules.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSMODULES_LIBS)
+
+TEST_PROGS = \
+	tap_pmShutterCorrection
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
+	$(top_srcdir)/test/test.pl
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmBias.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmBias.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmBias.c	(revision 22322)
@@ -0,0 +1,529 @@
+/** @file tap_pmBias.c
+ *
+ * XXX: pmBiasSubtractFrame(): Must add tests for CELL.X0, CELL.Y0 stuff:
+ * XXX: pmBiasSubtractFrame(): Must add tests with scale != 1.0
+ * XXX: pmBiasSubtract(): must test with acceptable data
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define BADFLAT			0
+#define VERBOSE                 1
+#define ERR_TRACE_LEVEL         0
+#define TEST_NUM_ROWS		8
+#define TEST_NUM_COLS		8
+#define NUM_BIAS_DATA		2
+#define NUM_HDUS		8
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define CELL_ALLOC_NAME        "CellName"
+#define NUM_READOUTS            3
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (int i=0;i<TEST_NUM_ROWS;i++) {
+        for (int j=0;j<TEST_NUM_COLS;j++) {
+            readout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = generateSimpleReadout(cell);
+    }
+
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../camera/data/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+//    psRegion *region = psRegionAlloc(0.0, TEST_NUM_COLS-1, 0.0, TEST_NUM_ROWS-1);
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+void myFreeCell(pmCell *cell)
+{
+    for (int k = 0 ; k < cell->readouts->n ; k++) {
+        psFree(cell->readouts->data[k]);
+    }
+    psFree(cell);
+}
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(1);
+
+
+    // ----------------------------------------------------------------------
+    // pmBiasSubtractFrame() tests
+    // bool pmBiasSubtractFrame(pmReadout *in, pmReadout *sub, float scale)
+    // Call pmBiasSubtractFrame() with NULL pmReadout input
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *sub = generateSimpleReadout(cell);
+
+        ok(!pmBiasSubtractFrame(NULL, sub, 1.0), "pmBiasSubtractFrame(NULL, sub, 1.0) returned FALSE");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // bool pmBiasSubtractFrame(pmReadout *in, pmReadout *sub, float scale)
+    // Call pmBiasSubtractFrame() with NULL pmReadout input->image
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->image);
+        in->image = NULL;
+        pmReadout *sub = generateSimpleReadout(cell);
+
+        ok(!pmBiasSubtractFrame(in, sub, 1.0), "pmBiasSubtractFrame() returned FALSE with NULL pmReadout input->image");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtractFrame() with empty pmReadout input->image
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->image);
+        in->image = psImageAlloc(0, 0, PS_TYPE_F32);
+        pmReadout *sub = generateSimpleReadout(cell);
+
+        ok(!pmBiasSubtractFrame(in, sub, 1.0), "pmBiasSubtractFrame() returned FALSE with empty input->image");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtractFrame() with bad type for pmReadout input->image
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->image);
+        in->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmReadout *sub = generateSimpleReadout(cell);
+
+        ok(!pmBiasSubtractFrame(in, sub, 1.0), "pmBiasSubtractFrame() returned FALSE with bad type for pmReadout input->image");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtractFrame() with NULL pmReadout sub
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *sub = generateSimpleReadout(cell);
+
+        ok(!pmBiasSubtractFrame(in, NULL, 1.0), "pmBiasSubtractFrame(in, NULL, 1.0) returned FALSE");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtractFrame() with NULL pmReadout sub->image
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *sub = generateSimpleReadout(cell);
+        psFree(sub->image);
+        sub->image = NULL;
+
+        ok(!pmBiasSubtractFrame(in, sub, 1.0), "pmBiasSubtractFrame() returned FALSE with NULL pmReadout sub->image");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtractFrame() with empty pmReadout sub->image
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *sub = generateSimpleReadout(cell);
+        psFree(sub->image);
+        sub->image = psImageAlloc(0, 0, PS_TYPE_F32);
+
+        ok(!pmBiasSubtractFrame(in, sub, 1.0), "pmBiasSubtractFrame() returned FALSE with NULL pmReadout sub->image");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtractFrame() with bad type for pmReadout sub->image
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *sub = generateSimpleReadout(cell);
+        psFree(sub->image);
+        sub->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+
+        ok(!pmBiasSubtractFrame(in, sub, 1.0), "pmBiasSubtractFrame() returned FALSE with bad type for pmReadout sub->image");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtractFrame() with differing input image sizes
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *sub = generateSimpleReadout(cell);
+        psFree(sub->image);
+        sub->image = psImageAlloc(TEST_NUM_COLS/2, TEST_NUM_ROWS/2, PS_TYPE_S32);
+
+        ok(!pmBiasSubtractFrame(in, sub, 1.0), "pmBiasSubtractFrame() returned FALSE with with differing input image sizes");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtractFrame() with differing image X parity
+    {
+        psMemId id = psMemGetId();
+        pmCell *cellIn = generateSimpleCell(NULL);
+        pmCell *cellSub = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cellIn);
+        pmReadout *sub = generateSimpleReadout(cellSub);
+
+        // Set the X parity differently for each pmReadout's parent cell
+        psMetadataAddS32(in->parent->concepts, PS_LIST_TAIL, "CELL.XPARITY", PS_META_REPLACE, "", -1);
+        psMetadataAddS32(sub->parent->concepts, PS_LIST_TAIL, "CELL.XPARITY", PS_META_REPLACE, "", 1);
+        ok(!pmBiasSubtractFrame(in, sub, 1.0), "pmBiasSubtractFrame() returned FALSE with differing image X parity");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cellIn);
+        myFreeCell(cellSub);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtractFrame() with differing image Y parity
+    {
+        psMemId id = psMemGetId();
+        pmCell *cellIn = generateSimpleCell(NULL);
+        pmCell *cellSub = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cellIn);
+        pmReadout *sub = generateSimpleReadout(cellSub);
+
+        // Set the Y parity differently for each pmReadout's parent cell
+        psMetadataAddS32(in->parent->concepts, PS_LIST_TAIL, "CELL.YPARITY", PS_META_REPLACE, "", -1);
+        psMetadataAddS32(sub->parent->concepts, PS_LIST_TAIL, "CELL.YPARITY", PS_META_REPLACE, "", 1);
+        ok(!pmBiasSubtractFrame(in, sub, 1.0), "pmBiasSubtractFrame() returned FALSE with differing image Y parity");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cellIn);
+        myFreeCell(cellSub);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtractFrame() with differing image Y parity
+    {
+        psMemId id = psMemGetId();
+        pmCell *cellIn = generateSimpleCell(NULL);
+        pmCell *cellSub = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cellIn);
+        pmReadout *sub = generateSimpleReadout(cellSub);
+        for (int i = 0 ; i < in->image->numRows ; i++) {
+            for (int j = 0 ; j < in->image->numCols ; j++) {
+                in->image->data.F32[i][j] = (float) (i + j + 10);
+	    }
+	}
+
+        for (int i = 0 ; i < sub->image->numRows ; i++) {
+            for (int j = 0 ; j < sub->image->numCols ; j++) {
+                sub->image->data.F32[i][j] = 10.0;
+	    }
+	}
+
+        ok(pmBiasSubtractFrame(in, sub, 1.0), "pmBiasSubtractFrame() returned TRUE");
+        bool errorFlag = false;
+        for (int i = 0 ; i < in->image->numRows ; i++) {
+            for (int j = 0 ; j < in->image->numCols ; j++) {
+                if (in->image->data.F32[i][j] != (float) (i + j)) {
+                    if (VERBOSE) diag("ERROR: image[%d][%d] is %.2f, should be %.2f\n",
+                        i, j, in->image->data.F32[i][j], (float) (i + j));
+                    errorFlag = true;
+		}
+	    }
+	}
+
+        ok(!errorFlag, "pmBiasSubtractFrame() set the pixels correctly");
+        psFree(in);
+        psFree(sub);
+        myFreeCell(cellIn);
+        myFreeCell(cellSub);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmBiasSubtract() tests
+    // bool pmBiasSubtract(pmReadout *in, pmOverscanOptions *overscanOpts,
+    //               const pmReadout *bias, const pmReadout *dark, const pmFPAview *view)
+    // Call pmBiasSubtract() with NULL pmReadout input
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *bias = generateSimpleReadout(cell);
+        pmReadout *dark = generateSimpleReadout(cell);
+        pmFPAview *view = pmFPAviewAlloc(TEST_NUM_ROWS);
+        // XXX: Must set the following:
+        pmOverscanOptions *overscanOpts = NULL;
+
+        ok(!pmBiasSubtract(NULL, overscanOpts, bias, dark, view), "pmBiasSubtract() returned FALSE with NULL input pmReadout: in");
+        psFree(in);
+        psFree(bias);
+        psFree(dark);
+        myFreeCell(cell);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtract() with NULL pmReadout input->image
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->image);
+        in->image = NULL;
+        pmReadout *bias = generateSimpleReadout(cell);
+        pmReadout *dark = generateSimpleReadout(cell);
+        pmFPAview *view = pmFPAviewAlloc(TEST_NUM_ROWS);
+        // XXX: Must set the following:
+        pmOverscanOptions *overscanOpts = NULL;
+
+        ok(!pmBiasSubtract(in, overscanOpts, bias, dark, view), "pmBiasSubtract() returned FALSE with NULL input image");
+        psFree(in);
+        psFree(bias);
+        psFree(dark);
+        myFreeCell(cell);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtract() with wrong type for pmReadout input->image
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->image);
+        in->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmReadout *bias = generateSimpleReadout(cell);
+        pmReadout *dark = generateSimpleReadout(cell);
+        pmFPAview *view = pmFPAviewAlloc(TEST_NUM_ROWS);
+        // XXX: Must set the following:
+        pmOverscanOptions *overscanOpts = NULL;
+
+        ok(!pmBiasSubtract(in, overscanOpts, bias, dark, view), "pmBiasSubtract() returned FALSE with wrong type for pmReadout input->image");
+        psFree(in);
+        psFree(bias);
+        psFree(dark);
+        myFreeCell(cell);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtract() with NULL image for input pmReadout bias
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *bias = generateSimpleReadout(cell);
+        psFree(bias->image);
+        bias->image = NULL;
+        pmReadout *dark = generateSimpleReadout(cell);
+        pmFPAview *view = pmFPAviewAlloc(TEST_NUM_ROWS);
+        // XXX: Must set the following:
+        pmOverscanOptions *overscanOpts = NULL;
+
+        ok(!pmBiasSubtract(in, overscanOpts, bias, dark, view), "pmBiasSubtract() returned FALSE with NULL image for input pmReadout bias");
+        psFree(in);
+        psFree(bias);
+        psFree(dark);
+        myFreeCell(cell);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtract() with wrong type image for input pmReadout bias
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *bias = generateSimpleReadout(cell);
+        psFree(bias->image);
+        bias->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmReadout *dark = generateSimpleReadout(cell);
+        pmFPAview *view = pmFPAviewAlloc(TEST_NUM_ROWS);
+        // XXX: Must set the following:
+        pmOverscanOptions *overscanOpts = NULL;
+
+        ok(!pmBiasSubtract(in, overscanOpts, bias, dark, view), "pmBiasSubtract() returned FALSE with wrong type image for input pmReadout bias");
+        psFree(in);
+        psFree(bias);
+        psFree(dark);
+        myFreeCell(cell);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtract() with NULL image for input pmReadout dark
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *bias = generateSimpleReadout(cell);
+        pmReadout *dark = generateSimpleReadout(cell);
+        psFree(dark->image);
+        dark->image = NULL;
+        pmFPAview *view = pmFPAviewAlloc(TEST_NUM_ROWS);
+        // XXX: Must set the following:
+        pmOverscanOptions *overscanOpts = NULL;
+
+        ok(!pmBiasSubtract(in, overscanOpts, bias, dark, view), "pmBiasSubtract() returned FALSE with wrong type image for input pmReadout bias");
+        psFree(in);
+        psFree(bias);
+        psFree(dark);
+        myFreeCell(cell);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtract() with wrong type image for input pmReadout dark
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *bias = generateSimpleReadout(cell);
+        pmReadout *dark = generateSimpleReadout(cell);
+        psFree(dark->image);
+        dark->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmFPAview *view = pmFPAviewAlloc(TEST_NUM_ROWS);
+        // XXX: Must set the following:
+        pmOverscanOptions *overscanOpts = NULL;
+
+        ok(!pmBiasSubtract(in, overscanOpts, bias, dark, view), "pmBiasSubtract() returned FALSE with wrong type image for input pmReadout dark");
+        psFree(in);
+        psFree(bias);
+        psFree(dark);
+        myFreeCell(cell);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmBiasSubtract() with NULL pmView (while pmReadout dark non-NULL)
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *bias = generateSimpleReadout(cell);
+        pmReadout *dark = generateSimpleReadout(cell);
+        pmFPAview *view = pmFPAviewAlloc(TEST_NUM_ROWS);
+        // XXX: Must set the following:
+        pmOverscanOptions *overscanOpts = NULL;
+
+        ok(!pmBiasSubtract(in, overscanOpts, bias, dark, NULL), "pmBiasSubtract() returned FALSE with NULL pmView (while pmReadout dark non-NULL)");
+        psFree(in);
+        psFree(bias);
+        psFree(dark);
+        myFreeCell(cell);
+        psFree(view);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmFlatField.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmFlatField.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmFlatField.c	(revision 22322)
@@ -0,0 +1,348 @@
+/** @file tap_pmFlatField.c
+ *
+ * XXX: Added a mask argument to pmFlatField().  Must add tests.  For now, all
+ * masks are NULL.
+ *
+ *  XXX: I added the CELL.TRIMSEC region code but there are not tests for it.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+#define BADFLAT			0
+#define VERBOSE                 1
+#define ERR_TRACE_LEVEL         10
+#define TEST_NUM_ROWS		8
+#define TEST_NUM_COLS		8
+#define NUM_BIAS_DATA		2
+#define NUM_HDUS		8
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define CELL_ALLOC_NAME        "CellName"
+#define NUM_READOUTS            3
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (int i=0;i<TEST_NUM_ROWS;i++) {
+        for (int j=0;j<TEST_NUM_COLS;j++) {
+            readout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = generateSimpleReadout(cell);
+    }
+
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../camera/data/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+//    psRegion *region = psRegionAlloc(0.0, TEST_NUM_COLS-1, 0.0, TEST_NUM_ROWS-1);
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+void myFreeCell(pmCell *cell)
+{
+    for (int k = 0 ; k < cell->readouts->n ; k++) {
+        psFree(cell->readouts->data[k]);
+    }
+    psFree(cell);
+}
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(28);
+
+
+    // bool pmFlatField(pmReadout *in, pmReadout *flat, psMaskType badFlat)
+    // Test pmFlatField() with NULL input psReadout
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        ok(!pmFlatField(NULL, flat, 0), "pmFlatField(NULL, flat, 0) returned FALSE");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // bool pmFlatField(pmReadout *in, pmReadout *flat, psMaskType badFlat)
+    // Test pmFlatField() with NULL input psReadout->image
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        psFree(in->image);
+        in->image = NULL;
+        pmReadout *flat = generateSimpleReadout(NULL);
+        ok(!pmFlatField(in, flat, 0), "pmFlatField(in, flat, 0) returned FALSE with NULL input psReadout->image");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // bool pmFlatField(pmReadout *in, pmReadout *flat, psMaskType badFlat)
+    // Test pmFlatField() with NULL input psReadout->image
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        psFree(in->image);
+        in->image = psImageAlloc(0, 0, PS_TYPE_F32);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        ok(!pmFlatField(in, flat, 0), "pmFlatField(in, flat, 0) returned FALSE with empty input psReadout->image");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // bool pmFlatField(pmReadout *in, pmReadout *flat, psMaskType badFlat)
+    // Test pmFlatField() with NULL input flat
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        ok(!pmFlatField(in, NULL, 0), "pmFlatField(in, NULL, 0) returned FALSE");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // bool pmFlatField(pmReadout *in, pmReadout *flat, psMaskType badFlat)
+    // Test pmFlatField() with NULL input flat->image
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        psFree(flat->image);
+        flat->image = NULL;
+        ok(!pmFlatField(in, NULL, 0), "pmFlatField(in, flat, 0) returned FALSE with NULL input flat->image");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // bool pmFlatField(pmReadout *in, pmReadout *flat, psMaskType badFlat)
+    // Test pmFlatField() with NULL input flat->image
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        psFree(flat->image);
+        flat->image = psImageAlloc(0, 0, PS_TYPE_F32);
+        ok(!pmFlatField(in, NULL, 0), "pmFlatField(in, flat, 0) returned FALSE with empty input flat->image");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // bool pmFlatField(pmReadout *in, pmReadout *flat, psMaskType badFlat)
+    // Test pmFlatField() with differing types of flat/in image
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        psFree(flat->image);
+        flat->image = psImageAlloc(0, 0, PS_TYPE_F64);
+        ok(!pmFlatField(in, NULL, 0), "pmFlatField(in, flat, 0) returned FALSE with differing types of flat/in image");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmFlatField() with input image larger than flat image
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        psFree(flat->image);
+        flat->image = psImageAlloc(TEST_NUM_ROWS/2, TEST_NUM_COLS/2, PS_TYPE_F32);
+        ok(!pmFlatField(in, flat, 1), "pmFlatField(in, flat, 0) returned FALSE with input image larger than flat image");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmFlatField() with input image and flat image of differing types
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        psFree(flat->image);
+        flat->image = psImageAlloc(TEST_NUM_ROWS, TEST_NUM_COLS, PS_TYPE_F64);
+        ok(!pmFlatField(in, flat, 1), "pmFlatField(in, flat, 0) returned FALSE with input image and flat image of differing types");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmFlatField() with input image mask smaller than input image
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        psFree(in->mask);
+        in->mask = psImageAlloc(TEST_NUM_ROWS/2, TEST_NUM_COLS/2, PS_TYPE_MASK);
+        ok(!pmFlatField(in, flat, 1), "pmFlatField(in, flat, 0) returned FALSE with input image mask smaller than input image");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmFlatField() with input image mask of incorrect type
+    {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        psFree(in->mask);
+        in->mask = psImageAlloc(TEST_NUM_ROWS, TEST_NUM_COLS, PS_TYPE_F32);
+        ok(!pmFlatField(in, flat, 1), "pmFlatField(in, flat, 0) returned FALSE with input image mask of incorrect type");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmFlatField() with offset greater than input image
+    // XXX: Must rewrite with offsets coming from metadata
+    if (0) {
+        psMemId id = psMemGetId();
+        pmReadout *in = generateSimpleReadout(NULL);
+        pmReadout *flat = generateSimpleReadout(NULL);
+        *(int*)&in->col0 = 50;
+        *(int*)&in->row0 = 50;
+        ok(!pmFlatField(in, flat, 1), "pmFlatField(in, flat, 0) returned FALSE with offset greater than input image");
+        psFree(in);
+        psFree(flat);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmFlatField() with acceptable input data
+    // Set the flat field to 1
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *flat = generateSimpleReadout(cell);
+        psImageInit(in->image, 6.0);
+        psImageInit(in->mask, 0);
+        psImageInit(flat->image, 2.0);
+        ok(pmFlatField(in, flat, 1), "pmFlatField(in, flat, 0) returned TRUE with acceptable input data");
+        bool errorFlag = false;
+        for (int i = 0 ; i < in->image->numRows ; i++) {
+            for (int j = 0 ; j < in->image->numCols ; j++) {
+                if (in->image->data.F32[i][j] != 3.0) {
+                    if (VERBOSE) diag("ERROR: image[%d][%d] is %.2f, should be 3.0", i, j,
+                        in->image->data.F32[i][j]);
+		}
+                if (in->mask->data.PS_TYPE_MASK_DATA[i][j] != 0) {
+                    if (VERBOSE) diag("ERROR: mask[%d][%d] is %d, should be 0", i, j,
+                        in->image->data.PS_TYPE_MASK_DATA[i][j]);
+		}
+	    }
+	}
+        ok(!errorFlag, "pmFlatField() set the image data correctly");
+
+        psFree(in);
+        psFree(flat);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmFlatField() with acceptable input data
+    // Set the flat field to -1
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *flat = generateSimpleReadout(cell);
+        psImageInit(in->image, 6.0);
+        psImageInit(in->mask, 0);
+        psImageInit(flat->image, -1.0);
+        ok(pmFlatField(in, flat, 1), "pmFlatField(in, flat, 0) returned TRUE with acceptable input data");
+        bool errorFlag = false;
+        for (int i = 0 ; i < in->image->numRows ; i++) {
+            for (int j = 0 ; j < in->image->numCols ; j++) {
+                if (!isnan(in->image->data.F32[i][j])) {
+                    if (VERBOSE) diag("ERROR: image[%d][%d] is %.2f, should be NAN", i, j,
+                        in->image->data.F32[i][j]);
+		}
+                if (in->mask->data.PS_TYPE_MASK_DATA[i][j] != 1) {
+                    if (VERBOSE) diag("ERROR: mask[%d][%d] is %d, should be 0", i, j,
+                        in->image->data.PS_TYPE_MASK_DATA[i][j]);
+		}
+	    }
+	}
+        ok(!errorFlag, "pmFlatField() set the image data correctly");
+
+        psFree(in);
+        psFree(flat);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmMaskBadPixels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmMaskBadPixels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmMaskBadPixels.c	(revision 22322)
@@ -0,0 +1,520 @@
+/** @file tap_pmMaskBadPixels.c
+ *
+ * XXX: must test pmMaskFlagSuspectPixels() with acceptable input data
+ * XXX: must test pmMaskIdentifyBadPixels() with acceptable input data
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define BADFLAT			0
+#define VERBOSE                 1
+#define ERR_TRACE_LEVEL         0
+#define TEST_NUM_ROWS		8
+#define TEST_NUM_COLS		8
+#define NUM_BIAS_DATA		2
+#define NUM_HDUS		8
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define CELL_ALLOC_NAME        "CellName"
+#define NUM_READOUTS            3
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (int i=0;i<TEST_NUM_ROWS;i++) {
+        for (int j=0;j<TEST_NUM_COLS;j++) {
+            readout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = generateSimpleReadout(cell);
+    }
+
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../camera/data/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+//    psRegion *region = psRegionAlloc(0.0, TEST_NUM_COLS-1, 0.0, TEST_NUM_ROWS-1);
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+void myFreeCell(pmCell *cell)
+{
+    for (int k = 0 ; k < cell->readouts->n ; k++) {
+        psFree(cell->readouts->data[k]);
+    }
+    psFree(cell);
+}
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(1);
+
+
+    // ----------------------------------------------------------------------
+    // pmMaskBadPixels() tests
+    // Call pmMaskBadPixels() with NULL pmReadout input
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *mask = generateSimpleReadout(cell);
+
+        ok(!pmMaskBadPixels(NULL, mask, 0), "pmMaskBadPixels(NULL, mask, 0) returned FALSE");
+        psFree(in);
+        psFree(mask);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskBadPixels() with NULL pmReadout input->mask
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->mask);
+        in->mask = NULL;
+        pmReadout *mask = generateSimpleReadout(cell);
+
+        ok(!pmMaskBadPixels(in, mask, 0), "pmMaskBadPixels() returned FALSE with NULL pmReadout input->mask");
+        psFree(in);
+        psFree(mask);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskBadPixels() with incorrect type for input->mask
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->mask);
+        in->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        pmReadout *mask = generateSimpleReadout(cell);
+
+        ok(!pmMaskBadPixels(in, mask, 0), "pmMaskBadPixels() returned FALSE with NULL pmReadout input->mask");
+        psFree(in);
+        psFree(mask);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskBadPixels() with NULL pmReadout mask
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *mask = generateSimpleReadout(cell);
+
+        ok(!pmMaskBadPixels(in, NULL, 0), "pmMaskBadPixels(in, NULL, 0) returned FALSE");
+        psFree(in);
+        psFree(mask);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskBadPixels() with NULL pmReadout mask->mask
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *mask = generateSimpleReadout(cell);
+        psFree(mask->mask);
+        mask->mask = NULL;
+
+        ok(!pmMaskBadPixels(in, mask, 0), "pmMaskBadPixels() returned FALSE with NULL pmReadout mask->mask");
+        psFree(in);
+        psFree(mask);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskBadPixels() with incorrect type for mask->mask
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *mask = generateSimpleReadout(cell);
+        psFree(mask->mask);
+        mask->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+
+        ok(!pmMaskBadPixels(in, mask, 0), "pmMaskBadPixels() returned FALSE with incorrect type for mask->mask");
+        psFree(in);
+        psFree(mask);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskBadPixels() with input mask larger than mask->mask
+    {
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *mask = generateSimpleReadout(cell);
+        psFree(mask->mask);
+        mask->mask = psImageAlloc(TEST_NUM_COLS/2, TEST_NUM_ROWS/2, PS_TYPE_MASK);
+
+        ok(!pmMaskBadPixels(in, mask, 0), "pmMaskBadPixels() returned FALSE with input mask larger than mask->mask");
+        psFree(in);
+        psFree(mask);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskBadPixels() with acceptable input data
+    {
+        #define MASK_VAL 1
+        psMemId id = psMemGetId();
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        pmReadout *mask = generateSimpleReadout(cell);
+        for (int i = 0 ; i < in->mask->numRows ; i++) {
+            for (int j = 0 ; j < in->mask->numCols ; j++) {
+                in->mask->data.PS_TYPE_MASK_DATA[i][j] = 0;
+                mask->mask->data.PS_TYPE_MASK_DATA[i][j] = 0;
+                if (i >= in->mask->numRows/2 && j >= in->mask->numCols/2) {
+                    mask->mask->data.PS_TYPE_MASK_DATA[i][j] = MASK_VAL;
+		}
+	    }
+	}
+
+        ok(pmMaskBadPixels(in, mask, MASK_VAL), "pmMaskBadPixels() returned TRUE with acceptable input data");
+        bool errorFlag = false;     
+        for (int i = 0 ; i < in->mask->numRows ; i++) {
+            for (int j = 0 ; j < in->mask->numCols ; j++) {
+                psU8 expect;
+                if (i >= in->mask->numRows/2 && j >= in->mask->numCols/2) {
+                    expect = MASK_VAL;
+		} else {
+                    expect = 0;
+		}
+                if (in->mask->data.PS_TYPE_MASK_DATA[i][j] != expect) {
+                    if (VERBOSE) diag("ERROR: in->mask[%d][%d] is %d, should be %d",
+                                       i, j, in->mask->data.PS_TYPE_MASK_DATA[i][j], expect);
+                    errorFlag = true;
+		}
+	    }
+	}
+        ok(!errorFlag, "pmMaskBadPixels() set the input mask correctly");
+
+        psFree(in);
+        psFree(mask);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+
+    // ----------------------------------------------------------------------
+    // pmMaskFlagSuspectPixels() tests
+    // Call pmMaskFlagSuspectPixels() with NULL pmReadout input
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+
+        ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with NULL input pmReadout");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with NULL pmReadout->image
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->image);
+        in->image = NULL;
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with NULL input pmReadout->image");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with empty pmReadout->image
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->image);
+        in->image = psImageAlloc(0, 0, PS_TYPE_F32);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with empty input pmReadout->image");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with incorrect type for pmReadout->image
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->image);
+        in->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with incorrect type for pmReadout->image");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with empty in->mask
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->mask);
+        in->mask = psImageAlloc(0, 0, PS_TYPE_MASK);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with incorrect type for pmReadout->image");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with bad size for in->mask
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->mask);
+        in->mask = psImageAlloc(TEST_NUM_COLS/2, TEST_NUM_ROWS/2, PS_TYPE_MASK);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with bad size for in->mask");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with bad type for in->mask
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psFree(in->mask);
+        in->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with bad type for in->mask");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with empty out image
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(0, 0, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with empty out image");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with incorrect type for out image
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with incorrect type for out image");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with incorrect size for out image
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS/2, TEST_NUM_ROWS/2, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with incorrect size for out image");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with rej input <= 0.0
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+
+        ok(!pmMaskFlagSuspectPixels(out, in, 0.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with rej input <= 0.0");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskFlagSuspectPixels() with frac input outside range [0.0:1.0]
+    {
+        psMemId id = psMemGetId();
+        psImage *out = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *in = generateSimpleReadout(cell);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+
+        ok(!pmMaskFlagSuspectPixels(out, in, 1.0, 1, -1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with frac input < 0.0");
+        ok(!pmMaskFlagSuspectPixels(out, in, 1.0, 1, 2.0, rng), "pmMaskFlagSuspectPixels() returned NULL with frac input > 1.0");
+        psFree(rng);
+        psFree(out);
+        psFree(in);
+        myFreeCell(cell);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+
+    // ----------------------------------------------------------------------
+    // pmMaskIdentifyBadPixels() tests
+    // Call pmMaskIdentifyBadPixels() with NULL input image
+    {
+        psMemId id = psMemGetId();
+        psImage *suspects = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
+        ok(!pmMaskIdentifyBadPixels(NULL, 1.0, 0), "pmMaskIdentifyBadPixels() returned NULL with NULL input image");
+        psFree(suspects);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskIdentifyBadPixels() with empty input image
+    {
+        psMemId id = psMemGetId();
+        psImage *suspects = psImageAlloc(0, 0, PS_TYPE_S32);
+        ok(!pmMaskIdentifyBadPixels(NULL, 1.0, 0), "pmMaskIdentifyBadPixels() returned NULL with empty input image");
+        psFree(suspects);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmMaskIdentifyBadPixels() with incorrect type for input image
+    {
+        psMemId id = psMemGetId();
+        psImage *suspects = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        ok(!pmMaskIdentifyBadPixels(NULL, 1.0, 0), "pmMaskIdentifyBadPixels() returned NULL with incorrect type for input image");
+        psFree(suspects);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmNonLinear.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmNonLinear.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmNonLinear.c	(revision 22322)
@@ -0,0 +1,237 @@
+/* @file tap_pmNonLinear.c
+ *
+ *  XXX: Add tests 
+ *      Input psVectors and psImages have incorrect type
+ *      Input psVectors are wrong size
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define TEST_NUM_ROWS 8
+#define TEST_NUM_COLS 8
+#define NUM_BIAS_DATA 2
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (int i=0;i<TEST_NUM_ROWS;i++) {
+        for (int j=0;j<TEST_NUM_COLS;j++) {
+            readout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(72);
+
+
+    // ----------------------------------------------------------------------
+    // pmNonLinearityPolynomial() tests
+    // Call pmNonLinearityPolynomial() with NULL input readout.
+    {
+        psMemId id = psMemGetId();
+        psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+        myPoly->coeff[1] = 1.0;
+        pmReadout *rc = pmNonLinearityPolynomial(NULL, myPoly);
+        ok(!rc, "pmNonLinearityPolynomial() returned NULL with a NULL input pmReadout");
+        psFree(myPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    
+     
+    // Call pmNonLinearityPolynomial() with NULL input readout->image.
+    {
+        psMemId id = psMemGetId();
+        pmReadout *myReadout = generateSimpleReadout(NULL);
+        psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+        myPoly->coeff[1] = 1.0;
+        psImage *tmpImage = myReadout->image;
+        myReadout->image = NULL;
+        pmReadout *rc = pmNonLinearityPolynomial(myReadout, myPoly);
+        ok(!rc, "pmNonLinearityPolynomial() returned NULL with a NULL input pmReadout");
+        myReadout->image = tmpImage;
+        psFree(myReadout);
+        psFree(myPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    
+     
+    // Call pmNonLinearityPolynomial() with NULL polynomial.
+    {
+        psMemId id = psMemGetId();
+        pmReadout *myReadout = generateSimpleReadout(NULL);
+        pmReadout *rc = pmNonLinearityPolynomial(myReadout, NULL);
+        ok(!rc, "pmNonLinearityPolynomial() returned NULL with a NULL input psPolynomial");
+        psFree(myReadout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    
+     
+    // Call pmNonLinearityPolynomial() with acceptable input data
+    { 
+        psMemId id = psMemGetId();
+        pmReadout *myReadout = generateSimpleReadout(NULL);
+        psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+        myPoly->coeff[1] = 1.0;
+        myReadout = pmNonLinearityPolynomial(myReadout, myPoly);
+        bool errorFlag = false;
+        for (int i=0;i<TEST_NUM_ROWS;i++) {
+            for (int j=0;j<TEST_NUM_COLS;j++) {
+                float expect = psPolynomial1DEval(myPoly, (float) (i + j));
+                float actual = myReadout->image->data.F32[i][j];
+                if (FLT_EPSILON < fabs(expect - actual)) {
+                    if (VERBOSE) diag("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmNonLinearityPolynomial() set the image data correctly");
+    
+        psFree(myReadout);
+        psFree(myPoly);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmNonLinearityLookup() tests
+    // Call pmNonLinearityLookup() with NULL input pmReadout.
+    {
+        psMemId id = psMemGetId();
+        psVector *inVec = psVectorAlloc(PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3, PS_TYPE_F32);
+        psVector *outVec = psVectorAlloc(PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3, PS_TYPE_F32);
+        for (psS32 i=0;i<PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3;i++) {
+            inVec->data.F32[i] = (float) i;
+            outVec->data.F32[i] = (float) (2 * i);
+        }
+        pmReadout *rc = pmNonLinearityLookup(NULL, inVec, outVec);
+        ok(!rc, "pmNonLinearityLookup() returned NULL with NULL input pmReadout");
+        psFree(inVec);
+        psFree(outVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmNonLinearityLookup() with NULL input pmReadout->image
+    {
+        psMemId id = psMemGetId();
+        pmReadout *myReadout = generateSimpleReadout(NULL);
+        psImage *tmpImage = myReadout->image;
+        myReadout->image = NULL;
+        psVector *inVec = psVectorAlloc(PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3, PS_TYPE_F32);
+        psVector *outVec = psVectorAlloc(PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3, PS_TYPE_F32);
+        for (psS32 i=0;i<PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3;i++) {
+            inVec->data.F32[i] = (float) i;
+            outVec->data.F32[i] = (float) (2 * i);
+        }
+        pmReadout *rc = pmNonLinearityLookup(myReadout, inVec, outVec);
+        ok(!rc, "pmNonLinearityLookup() returned NULL with NULL input pmReadout->image");
+        myReadout->image = tmpImage;
+        psFree(myReadout);
+        psFree(inVec);
+        psFree(outVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmNonLinearityLookup() with NULL input inFlux
+    {
+        psMemId id = psMemGetId();
+        pmReadout *myReadout = generateSimpleReadout(NULL);
+        psVector *inVec = psVectorAlloc(PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3, PS_TYPE_F32);
+        psVector *outVec = psVectorAlloc(PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3, PS_TYPE_F32);
+        for (psS32 i=0;i<PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3;i++) {
+            inVec->data.F32[i] = (float) i;
+            outVec->data.F32[i] = (float) (2 * i);
+        }
+        pmReadout *rc = pmNonLinearityLookup(myReadout, NULL, outVec);
+        ok(!rc, "pmNonLinearityLookup() returned NULL with input inFlux");
+        psFree(myReadout);
+        psFree(inVec);
+        psFree(outVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call pmNonLinearityLookup() with NULL input outFlux
+    {
+        psMemId id = psMemGetId();
+        pmReadout *myReadout = generateSimpleReadout(NULL);
+        psVector *inVec = psVectorAlloc(PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3, PS_TYPE_F32);
+        psVector *outVec = psVectorAlloc(PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3, PS_TYPE_F32);
+        for (psS32 i=0;i<PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3;i++) {
+            inVec->data.F32[i] = (float) i;
+            outVec->data.F32[i] = (float) (2 * i);
+        }
+        pmReadout *rc = pmNonLinearityLookup(myReadout, inVec, NULL);
+        ok(!rc, "pmNonLinearityLookup() returned NULL with input outFlux");
+        psFree(myReadout);
+        psFree(inVec);
+        psFree(outVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmNonLinearityLookup() with acceptable input data
+    {
+        psMemId id = psMemGetId();
+        pmReadout *myReadout = generateSimpleReadout(NULL);
+        psVector *inVec = psVectorAlloc(PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3, PS_TYPE_F32);
+        psVector *outVec = psVectorAlloc(PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3, PS_TYPE_F32);
+        for (psS32 i=0;i<PS_MAX(TEST_NUM_COLS, TEST_NUM_ROWS)*3;i++) {
+            inVec->data.F32[i] = (float) i;
+            outVec->data.F32[i] = (float) (2 * i);
+        }
+        {
+            myReadout = pmNonLinearityLookup(myReadout, inVec, outVec);
+            ok(myReadout, "pmNonLinearityLookup() returned non-NULL with acceptable input data");
+            bool errorFlag = false;
+            if (myReadout) {
+                for (int i=0;i<TEST_NUM_ROWS;i++) {
+                    for (int j=0;j<TEST_NUM_COLS;j++) {
+                        float expect = (float) (2 * (i + j));
+                        float actual = myReadout->image->data.F32[i][j];
+                        if (FLT_EPSILON < fabs(expect - actual)) {
+                            if (VERBOSE) diag("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                            errorFlag = true;
+                        }
+                    }
+                }
+                ok(!errorFlag, "pmNonLinearityLookup() set the image data correctly");
+	    }
+	}
+        psFree(myReadout);
+        psFree(inVec);
+        psFree(outVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmShutterCorrection.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmShutterCorrection.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tap_pmShutterCorrection.c	(revision 22322)
@@ -0,0 +1,430 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+
+int main (void)
+{
+    plan_tests(46);
+
+    diag("pmShutterCorrection tests");
+
+    // test allocation, free of pmShutterCorrPars
+    diag("pmShutterCorrParsAlloc tests");
+    {
+        psMemId id = psMemGetId();
+        pmShutterCorrection *pars = pmShutterCorrectionAlloc ();
+
+        ok(pars != NULL, "pmShutterCorrPars successfully allocated");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrParsAlloc() failed");
+        skip_end();
+
+        psFree(pars);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test parameter guess (linearly spaced exptimes, TK/TO < 1)
+    diag("pmShutterCorrectionGuess tests : coarse linear-spaced exptimes");
+    {
+        psMemId id = psMemGetId();
+
+        int NPTS = 10;
+        float AK = 5.0;
+        float TK = 0.1;
+        float TO = 0.2;
+        psVector *exptime = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *counts  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        exptime->n = counts->n = NPTS;
+
+        for (int i = 0; i < exptime->n; i++) {
+            exptime->data.F32[i] = i*0.25;
+            counts->data.F32[i] = AK*(exptime->data.F32[i] + TK) / (exptime->data.F32[i] + TO);
+        }
+
+        pmShutterCorrection *pars = pmShutterCorrectionGuess (exptime, counts);
+
+        ok(pars != NULL, "pmShutterCorrPars successfully allocated");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrParsAlloc() failed");
+
+        // with coarse linearly-spaced times large compared to TO and TK,
+        // we can't expect very accurate guesses.  the exptime guess is fairly good because
+        // the largest exptime is much longer than TK or TO
+        ok(fabs(pars->scale  - AK) < 0.5, "scale guess is close enough (got %f vs %f)",  pars->scale, AK);
+        ok(fabs(pars->offset - TK) < 0.5, "offset guess is close enough (got %f vs %f)", pars->offset, TK);
+        ok(fabs(pars->offref - TO) < 0.5, "offref guess is close enough (got %f vs %f)", pars->offref, TO);
+        skip_end();
+
+        psFree(pars);
+        psFree(exptime);
+        psFree(counts);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test parameter guess (linearly spaced exptimes, TK/TO < 1)
+    diag("pmShutterCorrectionGuess tests : fine linear-spaced exptimes");
+    {
+        psMemId id = psMemGetId();
+
+        int NPTS = 20;
+        float AK = 5.0;
+        float TK = 0.1;
+        float TO = 0.2;
+        psVector *exptime = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *counts  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        exptime->n = counts->n = NPTS;
+
+        for (int i = 0; i < exptime->n; i++) {
+            exptime->data.F32[i] = i*0.1;
+            counts->data.F32[i] = AK*(exptime->data.F32[i] + TK) / (exptime->data.F32[i] + TO);
+        }
+
+        pmShutterCorrection *pars = pmShutterCorrectionGuess (exptime, counts);
+
+        ok(pars != NULL, "pmShutterCorrection successfully allocated");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrectionAlloc() failed");
+
+        // with fine linearly-spaced times large compared to TO and TK,
+        // we get a good guess to TK and TO, but since the largest exptime is not
+        // many times larger than TO, we don't do very well with the AK
+        ok(fabs(pars->scale  - AK) < 0.5, "scale guess is close enough (got %f vs %f)",  pars->scale, AK);
+        ok(fabs(pars->offset - TK) < 0.05, "offset guess is close enough (got %f vs %f)", pars->offset, TK);
+        ok(fabs(pars->offref - TO) < 0.05, "offref guess is close enough (got %f vs %f)", pars->offref, TO);
+        skip_end();
+
+        psFree(pars);
+        psFree(exptime);
+        psFree(counts);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test parameter guess (log spaced exptimes, TK/TO < 1)
+    diag("pmShutterCorrectionGuess tests : log-spaced exptimes");
+    {
+        psMemId id = psMemGetId();
+
+        int NPTS = 40;
+        float AK = 5.0;
+        float TK = 0.1;
+        float TO = 0.2;
+        psVector *exptime = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *counts  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        exptime->n = counts->n = NPTS;
+
+        for (int i = 0; i < exptime->n; i++) {
+            exptime->data.F32[i] = pow(10.0, -2 + i*0.1);
+            counts->data.F32[i] = AK*(exptime->data.F32[i] + TK) / (exptime->data.F32[i] + TO);
+        }
+
+        pmShutterCorrection *pars = pmShutterCorrectionGuess (exptime, counts);
+
+        // with fine log-spaced times well-sampling TO and TK,
+        // we can expect accurate guesses
+        ok(pars != NULL, "pmShutterCorrectionsuccessfully allocated");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrectionAlloc() failed");
+        ok(fabs(pars->scale  - AK) < 0.01, "scale guess is close enough (got %f vs %f)",  pars->scale, AK);
+        ok(fabs(pars->offset - TK) < 0.01, "offset guess is close enough (got %f vs %f)", pars->offset, TK);
+        ok(fabs(pars->offref - TO) < 0.01, "offref guess is close enough (got %f vs %f)", pars->offref, TO);
+        skip_end();
+
+        psFree(pars);
+        psFree(exptime);
+        psFree(counts);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // test parameter guess (linearly spaced exptimes, TK/TO > 1)
+    diag("pmShutterCorrectionGuess tests : coarse linear-spaced exptimes, TK/TO > 1");
+    {
+        psMemId id = psMemGetId();
+
+        int NPTS = 10;
+        float AK = 5.0;
+        float TK = 0.2;
+        float TO = 0.1;
+        psVector *exptime = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *counts  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        exptime->n = counts->n = NPTS;
+
+        for (int i = 0; i < exptime->n; i++) {
+            exptime->data.F32[i] = i*0.25;
+            counts->data.F32[i] = AK*(exptime->data.F32[i] + TK) / (exptime->data.F32[i] + TO);
+        }
+
+        pmShutterCorrection *pars = pmShutterCorrectionGuess (exptime, counts);
+
+        ok(pars != NULL, "pmShutterCorrection successfully allocated");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrectionAlloc() failed");
+
+        // with coarse linearly-spaced times large compared to TO and TK,
+        // we can't expect very accurate guesses.  the exptime guess is fairly good because
+        // the largest exptime is much longer than TK or TO
+        ok(fabs(pars->scale  - AK) < 0.5, "scale guess is close enough (got %f vs %f)",  pars->scale, AK);
+        ok(fabs(pars->offset - TK) < 0.5, "offset guess is close enough (got %f vs %f)", pars->offset, TK);
+        ok(fabs(pars->offref - TO) < 0.5, "offref guess is close enough (got %f vs %f)", pars->offref, TO);
+        skip_end();
+
+        psFree(pars);
+        psFree(exptime);
+        psFree(counts);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test parameter guess (linearly spaced exptimes, TK/TO > 1)
+    diag("pmShutterCorrectionGuess tests : fine linear-spaced exptimes, TK/TO > 1");
+    {
+        psMemId id = psMemGetId();
+
+        int NPTS = 20;
+        float AK = 5.0;
+        float TK = 0.2;
+        float TO = 0.1;
+        psVector *exptime = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *counts  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        exptime->n = counts->n = NPTS;
+
+        for (int i = 0; i < exptime->n; i++) {
+            exptime->data.F32[i] = i*0.1;
+            counts->data.F32[i] = AK*(exptime->data.F32[i] + TK) / (exptime->data.F32[i] + TO);
+        }
+
+        pmShutterCorrection *pars = pmShutterCorrectionGuess (exptime, counts);
+
+        ok(pars != NULL, "pmShutterCorrection successfully allocated");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrectionsAlloc() failed");
+
+        // with fine linearly-spaced times large compared to TO and TK,
+        // we get a good guess to TK and TO, but since the largest exptime is not
+        // many times larger than TO, we don't do very well with the AK
+        ok(fabs(pars->scale  - AK) < 0.5, "scale guess is close enough (got %f vs %f)",  pars->scale, AK);
+        ok(fabs(pars->offset - TK) < 0.05, "offset guess is close enough (got %f vs %f)", pars->offset, TK);
+        ok(fabs(pars->offref - TO) < 0.05, "offref guess is close enough (got %f vs %f)", pars->offref, TO);
+        skip_end();
+
+        psFree(pars);
+        psFree(exptime);
+        psFree(counts);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test parameter guess (log spaced exptimes, TK/TO > 1)
+    diag("pmShutterCorrectionGuess tests : log-spaced exptimes, TK/TO > 1");
+    {
+        psMemId id = psMemGetId();
+
+        int NPTS = 40;
+        float AK = 5.0;
+        float TK = 0.2;
+        float TO = 0.1;
+        psVector *exptime = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *counts  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        exptime->n = counts->n = NPTS;
+
+        for (int i = 0; i < exptime->n; i++) {
+            exptime->data.F32[i] = pow(10.0, -2 + i*0.1);
+            counts->data.F32[i] = AK*(exptime->data.F32[i] + TK) / (exptime->data.F32[i] + TO);
+        }
+
+        pmShutterCorrection *pars = pmShutterCorrectionGuess (exptime, counts);
+
+        // with fine log-spaced times well-sampling TO and TK,
+        // we can expect accurate guesses
+        ok(pars != NULL, "pmShutterCorrection successfully allocated");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrectionAlloc() failed");
+        ok(fabs(pars->scale  - AK) < 0.01, "scale guess is close enough (got %f vs %f)",  pars->scale, AK);
+        ok(fabs(pars->offset - TK) < 0.01, "offset guess is close enough (got %f vs %f)", pars->offset, TK);
+        ok(fabs(pars->offref - TO) < 0.01, "offref guess is close enough (got %f vs %f)", pars->offref, TO);
+        skip_end();
+
+        psFree(pars);
+        psFree(exptime);
+        psFree(counts);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test non-linear fitting
+    diag("pmShutterCorrectionFullFit tests : linear-spaced exptimes");
+    {
+        psMemId id = psMemGetId();
+
+        int NPTS = 20;
+        float FL = 10000.0;
+        float AK = 5.0;
+        float TK = 0.2;
+        float TO = 0.1;
+        psVector *exptime = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *counts  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *cntErr  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        exptime->n = counts->n = cntErr->n = NPTS;
+
+        for (int i = 0; i < exptime->n; i++) {
+            exptime->data.F32[i] = i*0.1;
+            counts->data.F32[i] = AK*(exptime->data.F32[i] + TK) / (exptime->data.F32[i] + TO);
+            cntErr->data.F32[i] = AK*sqrt(FL*(exptime->data.F32[i] + TK)) / (exptime->data.F32[i] + TO);
+        }
+
+        pmShutterCorrection *guess = pmShutterCorrectionGuess (exptime, counts);
+        skip_start(guess == NULL, 0, "Skipping tests because pmShutterCorrectionGuess() failed");
+        pmShutterCorrection *pars = pmShutterCorrectionFullFit (exptime, counts, cntErr, guess);
+
+        // with fine log-spaced times well-sampling TO and TK,
+        // we can expect accurate guesses
+        ok(pars != NULL, "pmShutterCorrection successfully allocated by FullFit");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrectionsAlloc() failed");
+        ok(fabs(pars->scale  - AK) < 0.01, "scale fit is close enough (got %f vs %f)",  pars->scale, AK);
+        ok(fabs(pars->offset - TK) < 0.01, "offset fit is close enough (got %f vs %f)", pars->offset, TK);
+        ok(fabs(pars->offref - TO) < 0.01, "offref fit is close enough (got %f vs %f)", pars->offref, TO);
+        skip_end();
+
+        psFree(pars);
+        skip_end();
+
+        psFree(guess);
+        psFree(exptime);
+        psFree(counts);
+        psFree(cntErr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test non-linear fitting
+    diag("pmShutterCorrectionFullFit tests : log-spaced exptimes");
+    {
+        psMemId id = psMemGetId();
+
+        int NPTS = 40;
+        float FL = 10000.0;
+        float AK = 1.0;
+        float TK = 0.2;
+        float TO = 0.1;
+        psVector *exptime = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *counts  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *cntErr  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        exptime->n = counts->n = cntErr->n = NPTS;
+
+        for (int i = 0; i < exptime->n; i++) {
+            exptime->data.F32[i] = pow(10.0, -2 + i*0.1);
+            counts->data.F32[i] = AK*(exptime->data.F32[i] + TK) / (exptime->data.F32[i] + TO);
+            cntErr->data.F32[i] = AK*sqrt(FL*(exptime->data.F32[i] + TK)) / (exptime->data.F32[i] + TO);
+        }
+
+        pmShutterCorrection*guess = pmShutterCorrectionGuess (exptime, counts);
+        skip_start(guess == NULL, 0, "Skipping tests because pmShutterCorrectionGuess() failed");
+        pmShutterCorrection *pars = pmShutterCorrectionFullFit (exptime, counts, cntErr, guess);
+
+        // with fine log-spaced times well-sampling TO and TK,
+        // we can expect accurate guesses
+        ok(pars != NULL, "pmShutterCorrection successfully allocated by FullFit");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrectionAlloc() failed");
+        ok(fabs(pars->scale  - AK) < 0.01, "scale fit is close enough (got %f vs %f)",  pars->scale, AK);
+        ok(fabs(pars->offset - TK) < 0.01, "offset fit is close enough (got %f vs %f)", pars->offset, TK);
+        ok(fabs(pars->offref - TO) < 0.01, "offref fit is close enough (got %f vs %f)", pars->offref, TO);
+        skip_end();
+
+        psFree(pars);
+        skip_end();
+
+        psFree(guess);
+        psFree(exptime);
+        psFree(counts);
+        psFree(cntErr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // XXX should add tests with the input counts scattered with GaussDev...
+
+    // test linear fitting
+    diag("pmShutterCorrectionLinFit tests : linear-spaced exptimes");
+    {
+        psMemId id = psMemGetId();
+
+        int NPTS = 20;
+        float FL = 10000.0;
+        float AK = 5.0;
+        float TK = 0.2;
+        float TO = 0.1;
+        psVector *exptime = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *counts  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *cntErr  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        exptime->n = counts->n = cntErr->n = NPTS;
+
+        for (int i = 0; i < exptime->n; i++) {
+            exptime->data.F32[i] = i*0.1;
+            counts->data.F32[i] = AK*(exptime->data.F32[i] + TK) / (exptime->data.F32[i] + TO);
+            cntErr->data.F32[i] = AK*sqrt(FL*(exptime->data.F32[i] + TK)) / (exptime->data.F32[i] + TO);
+        }
+
+        pmShutterCorrection*guess = pmShutterCorrectionGuess (exptime, counts);
+        skip_start(guess == NULL, 0, "Skipping tests because pmShutterCorrectionGuess() failed");
+        pmShutterCorrection *full = pmShutterCorrectionFullFit (exptime, counts, cntErr, guess);
+        pmShutterCorrection *pars = pmShutterCorrectionLinFit (exptime, counts, cntErr, NULL, full->offref, 1, 5, 0);
+
+        // with fine log-spaced times well-sampling TO and TK,
+        // we can expect accurate guesses
+        ok(pars != NULL, "pmShutterCorrection successfully allocated by FullFit");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrectionAlloc() failed");
+        ok(fabs(pars->scale  - AK) < 0.01, "scale fit is close enough (got %f vs %f)",  pars->scale, AK);
+        ok(fabs(pars->offset - TK) < 0.01, "offset fit is close enough (got %f vs %f)", pars->offset, TK);
+        ok(fabs(pars->offref - TO) < 0.01, "offref fit is close enough (got %f vs %f)", pars->offref, TO);
+        skip_end();
+
+        psFree(pars);
+        psFree(full);
+        skip_end();
+
+        psFree(guess);
+        psFree(exptime);
+        psFree(counts);
+        psFree(cntErr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test linear fitting
+    diag("pmShutterCorrectionLinFit tests : log-spaced exptimes");
+    {
+        psMemId id = psMemGetId();
+
+        int NPTS = 40;
+        float FL = 10000.0;
+        float AK = 1.0;
+        float TK = 0.2;
+        float TO = 0.1;
+        psVector *exptime = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *counts  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        psVector *cntErr  = psVectorAlloc (NPTS, PS_TYPE_F32);
+        exptime->n = counts->n = cntErr->n = NPTS;
+
+        for (int i = 0; i < exptime->n; i++) {
+            exptime->data.F32[i] = pow(10.0, -2 + i*0.1);
+            counts->data.F32[i] = AK*(exptime->data.F32[i] + TK) / (exptime->data.F32[i] + TO);
+            cntErr->data.F32[i] = AK*sqrt(FL*(exptime->data.F32[i] + TK)) / (exptime->data.F32[i] + TO);
+        }
+
+        pmShutterCorrection *guess = pmShutterCorrectionGuess (exptime, counts);
+        skip_start(guess == NULL, 0, "Skipping tests because pmShutterCorrectionGuess() failed");
+        pmShutterCorrection *full = pmShutterCorrectionFullFit (exptime, counts, cntErr, guess);
+        pmShutterCorrection *pars = pmShutterCorrectionLinFit (exptime, counts, cntErr, NULL, full->offref, 1, 5, 0);
+
+        // with fine log-spaced times well-sampling TO and TK,
+        // we can expect accurate guesses
+        ok(pars != NULL, "pmShutterCorrection successfully allocated by FullFit");
+        skip_start(pars == NULL, 0, "Skipping tests because pmShutterCorrectionAlloc() failed");
+        ok(fabs(pars->scale  - AK) < 0.01, "scale fit is close enough (got %f vs %f)",  pars->scale, AK);
+        ok(fabs(pars->offset - TK) < 0.01, "offset fit is close enough (got %f vs %f)", pars->offset, TK);
+        ok(fabs(pars->offref - TO) < 0.01, "offref fit is close enough (got %f vs %f)", pars->offref, TO);
+        skip_end();
+
+        psFree(pars);
+        psFree(full);
+        skip_end();
+
+        psFree(guess);
+        psFree(exptime);
+        psFree(counts);
+        psFree(cntErr);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmFlatField.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmFlatField.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmFlatField.c	(revision 22322)
@@ -0,0 +1,290 @@
+/** @file tst_pmFlatField.c
+ *
+ *  @brief Contains the tests for pmFlatField.c:
+ *
+ *    Test A - Divide input image by flat image
+ *    Test B - Mask flat image data
+ *    Test C - Mask flat image data starting with non-null mask
+ *    Test E - Attempt to use null input image
+ *    Test F - Attempt tp use null flat image
+ *    Test G - Attempt to use input image bigger than flat image
+ *    Test H - Attempt to use input image mask bigger than flat image
+ *    Test I - Attempt to use offset greater than input image
+ *    Test J - Attempt to use complex input image
+ *    Test K - Attempt to use complex flat image
+ *    Test L - Attempt to use non-equal input and flat image types
+ *    Test M - Attempt to use non-mask type mask image
+ *
+ * XXX: Added a mask argument to pmFlatField().  Must add tests.  For now, all
+ * masks are NULL.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  XXX: I added the CELL.TRIMSEC region code but there are not tests for it.
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+
+#include "psTest.h"
+#include "pslib.h"
+#include "pmFlatField.h"
+
+
+#define PRINT_MATRIX(IMAGE,TYPE,STRING)                                                                      \
+printf(STRING);                                                                                              \
+printf("\n");                                                                                                \
+for(int i=IMAGE->numRows-1; i>-1; i--) {                                                                     \
+    for(int j=0; j<IMAGE->numCols; j++) {                                                                    \
+        if(PS_IS_PSELEMTYPE_COMPLEX(IMAGE->type.type)) {                                                     \
+            printf("%f+%fi ", creal(IMAGE->data.TYPE[i][j]), cimag(IMAGE->data.TYPE[i][j]));                 \
+        } else if(PS_IS_PSELEMTYPE_INT(IMAGE->type.type)) {                                                  \
+            printf("%d ", (int)IMAGE->data.TYPE[i][j]);                                                      \
+        } else {                                                                                             \
+            printf("%f ", (double)IMAGE->data.TYPE[i][j]);                                                   \
+        }                                                                                                    \
+    }                                                                                                        \
+    printf("\n");                                                                                            \
+}                                                                                                            \
+printf("\n");
+
+
+#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS)                                                    \
+psImage *NAME = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE);                                          \
+for(int i=0; i<NAME->numRows; i++) {                                                                         \
+    for(int j=0; j<NAME->numCols; j++) {                                                                     \
+        NAME->data.TYPE[i][j] = VALUE;                                                                       \
+    }                                                                                                        \
+}
+
+
+static int testFlatField(void);
+
+
+testDescription tests[] = {
+                              {testFlatField, 753, "pmFlatField", 0, false},
+                              {NULL}
+                          };
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+
+int testFlatField( void )
+{
+    // Test A - Divide input image by flat image
+    printPositiveTestHeader(stdout, "pmFlatField", "Test A - Divide input image by flat image");
+    CREATE_AND_SET_IMAGE(inImage,F64,6.0,3,3)
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    inReadout->image = inImage;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+    CREATE_AND_SET_IMAGE(inMask, U8, 0, 3,3);
+    inReadout->mask = inMask;
+    PRINT_MATRIX((inReadout->mask),U8,"Input mask:");
+    PRINT_MATRIX(inImage,F64,"Input image:");
+
+    CREATE_AND_SET_IMAGE(flatImage1,F64,2.0,3,3)
+    pmReadout *flatReadout = pmReadoutAlloc(NULL);
+    flatReadout->row0 = 0;
+    flatReadout->col0 = 0;
+    flatReadout->image = flatImage1;
+    PRINT_MATRIX(flatImage1,F64,"Flat image:");
+
+    if ( !pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test A - Returned false should be true");
+        return 1;
+    }
+    PRINT_MATRIX(inImage,F64, "Resulting image:");
+    printFooter(stdout, "pmFlatField", "Test A - Divide input image by flat image", true);
+    printf("\n\n\n");
+
+
+    // Test B - Mask flat image data
+    printPositiveTestHeader(stdout, "pmFlatField", "Test B - Mask flat image data");
+    PRINT_MATRIX(inImage, F64, "Input image:");
+    CREATE_AND_SET_IMAGE(flatImage2,F64,0.0,3,3)
+    PRINT_MATRIX(flatImage2, F64, "Flat image:");
+    flatReadout->image = flatImage2;
+    if ( !pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test B - Returned false should be true");
+        return 2;
+    }
+    PRINT_MATRIX(inReadout->mask, PS_TYPE_MASK_DATA, "Resulting mask:");
+    PRINT_MATRIX(inImage,F64,"Resulting image:");
+    printFooter(stdout, "pmFlatField", "Test B - Mask flat image data", true);
+    printf("\n\n\n");
+
+
+    // Test C - Mask flat image data starting with non-null mask
+    printPositiveTestHeader(stdout, "pmFlatField", "Test C - Mask flat image data starting with non-null mask");
+    PRINT_MATRIX(inImage, F64, "Input image:");
+    flatImage2->data.F64[0][0] = 3.0;
+    flatImage2->data.F64[0][1] = -3.0;
+    PRINT_MATRIX(flatImage2, F64, "Flat image:");
+    CREATE_AND_SET_IMAGE(mask1,U8,0,3,3);
+    psFree(inReadout->mask);
+    inReadout->mask = mask1;
+    if ( !pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test C - Returned false should be true");
+        return 3;
+    }
+    PRINT_MATRIX(flatImage2, F64, "Flat image out:");
+    PRINT_MATRIX(inReadout->mask, PS_TYPE_MASK_DATA,"Resulting mask:");
+    PRINT_MATRIX(inImage,F64,"Resulting image:");
+    printFooter(stdout, "pmFlatField", "Test C - Mask flat image data starting with non-null mask", true);
+    printf("\n\n\n");
+
+
+    // Test D - Attempt to use null flat readout
+    printNegativeTestHeader(stdout,"pmFlatField", "Test D - Attempt to use null flat readout",
+                            "Null not allowed for flat readout", 0);
+    if( pmFlatField(inReadout, NULL) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test D - Returned true should be false");
+        return 4;
+    }
+    printFooter(stdout, "pmFlatField", "Test D - Attempt to use null flat readout", true);
+    printf("\n\n\n");
+
+
+    // Test E - Attempt to use null input image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test E - Attempt to use null input image",
+                            "Null not allowed for input image", 0);
+    psImage *temp = inReadout->image;
+    inReadout->image = NULL;
+    if ( pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test E - Returned true should be false" );
+        return 5;
+    }
+    inReadout->image = temp    ;
+    printFooter(stdout, "pmFlatField", "Test E - Attempt to use null input image", true);
+    printf("\n\n\n");
+
+
+    // Test F - Attempt tp use null flat image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test F - Attempt tp use null flat image",
+                            "Null not allowed for flat image", 0);
+    temp = flatReadout->image;
+    flatReadout->image = NULL;
+    if ( pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test F - Returned true should be false" );
+        return 6;
+    }
+    flatReadout->image = temp;
+    printFooter(stdout, "pmFlatField", "Test F - Attempt tp use null flat image", true);
+    printf("\n\n\n");
+
+
+    // Test G - Attempt to use input image bigger than flat image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test G - Attempt to use input image bigger than flat image",
+                            "Input image size exceeds that of flat image", 0);
+    CREATE_AND_SET_IMAGE(smallFlat,F64,0.0,2,2);
+    temp = flatReadout->image;
+    flatReadout->image = smallFlat;
+    if ( pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test G - Returned true should be false");
+        return 7;
+    }
+    flatReadout->image = temp;
+    printFooter(stdout, "pmFlatField", "Test G - Attempt to use input image bigger than flat image", true);
+    printf("\n\n\n");
+
+    // Test H - Attempt to use input image mask bigger than flat image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test H - Attempt to use input image mask bigger than flat image",
+                            "Input image mask size exceeds that of flat image", 0);
+    CREATE_AND_SET_IMAGE(largeMask,F64,0.0,5,5);
+    inReadout->mask = largeMask;
+    if ( pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test H - Returned true should be false");
+        return 8;
+    }
+    printFooter(stdout, "pmFlatField", "Test H - Attempt to use input image mask bigger than flat image", true);
+    printf("\n\n\n");
+    inReadout->mask = mask1;
+
+    // Test I - Attempt to use offset greater than input image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test I - Attempt to use offset greater than input image",
+                            "Total offset >= input image size", 0);
+    *(int*)&inReadout->col0 = 50;
+    *(int*)&inReadout->row0 = 50;
+    if ( pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test I - Returned true should be false");
+        return 9;
+    }
+    *(int*)&inReadout->col0 = 0;
+    *(int*)&inReadout->row0 = 0;
+    printFooter(stdout, "pmFlatField", "Test I - Attempt to use offset greater than input image", true);
+    printf("\n\n\n");
+
+
+    // Test J - Attempt to use complex input image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test J - Attempt to use complex input image",
+                            "Complex types not allowed for input image", 0);
+    *(psElemType* ) & inReadout->image->type.type = PS_TYPE_C64;
+    if ( pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test J - Returned true should be false");
+        return 10;
+    }
+    *(psElemType* ) & inReadout->image->type.type = PS_TYPE_F64;
+    printFooter(stdout, "pmFlatField", "Test J - Attempt to use complex input image", true);
+    printf("\n\n\n");
+
+
+    // Test K - Attempt to use complex flat image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test K - Attempt to use complex flat image",
+                            "Complex types not allowed for flat image", 0);
+    *(psElemType* ) & flatReadout->image->type.type = PS_TYPE_C64;
+    if ( pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test K - Returned ture should be false");
+        return 11;
+    }
+    *(psElemType* ) & flatReadout->image->type.type = PS_TYPE_F64;
+    printFooter(stdout, "pmFlatField", "Test K - Attempt to use complex flat image", true);
+    printf("\n\n\n");
+
+
+    // Test L - Attempt to use non-equal input and flat image types
+    printNegativeTestHeader(stdout,"pmFlatField", "Test L - Attempt to use non-equal input and flat image types",
+                            "Input and flat image types differ", 0);
+    *(psElemType* ) & flatReadout->image->type.type = PS_TYPE_F32;
+    if ( pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test L - Returned true should be false");
+        return 12;
+    }
+    *(psElemType* ) & flatReadout->image->type.type = PS_TYPE_F64;
+    printFooter(stdout, "pmFlatField", "Test L - Attempt to use non-equal input and flat image types", true);
+    printf("\n\n\n");
+
+
+    // Test M - Attempt to use non-mask type mask image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test M - Attempt to use non-mask type mask image",
+                            "Mask must be PS_TYPE_MASK type", 0);
+    *(psElemType* ) & inReadout->mask->type.type = PS_TYPE_F32;
+    if ( pmFlatField(inReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test M - Returned true should be false");
+        return 13;
+    }
+    *(psElemType* ) & inReadout->mask->type.type = PS_TYPE_MASK;
+    printFooter(stdout, "pmFlatField", "Test M - Attempt to use non-mask type mask image", true);
+    printf("\n\n\n");
+
+
+    // Free memory
+    psFree(inReadout);
+    psFree(flatReadout);
+    //psFree(inImage);
+    psFree(flatImage1);
+    //psFree(flatImage1);
+    psFree(smallFlat);
+    psFree(largeMask);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmMaskBadPixels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmMaskBadPixels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmMaskBadPixels.c	(revision 22322)
@@ -0,0 +1,381 @@
+/** @file tst_pmMaskBadPixels.c
+ *
+ *  @brief Contains the tests for pmMaskBadPixels:
+ *
+ *    Test A - Create mask based on maskVal argument
+ *    Test B - Create mask based on saturation argument
+ *    Test C - Create mask based on growVal and grow arguments
+ *    Test D - Auto Create mask based on maskVal argument
+ *    Test E - Attempt to use null mask
+ *    Test F - Attempt tp use null input image
+ *    Test G - Attempt to use input image bigger than mask
+ *    Test H - Attempt to use input image mask bigger than mask
+ *    Test I - Attempt to use offset greater than input image
+ *    Test J - Attempt to use complex input image
+ *    Test K - Attempt to use non-mask type mask image
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+
+#include "psTest.h"
+#include "pslib.h"
+#include "pmMaskBadPixels.h"
+
+
+#define PRINT_MATRIX(IMAGE,TYPE,STRING)                                                                      \
+printf(STRING);                                                                                              \
+printf("\n");                                                                                                \
+for(int i=(IMAGE)->numRows-1; i>-1; i--) {                                                                     \
+    for(int j=0; j<(IMAGE)->numCols; j++) {                                                                    \
+        if(PS_IS_PSELEMTYPE_COMPLEX((IMAGE)->type.type)) {                                                     \
+            printf("%f+%fi ", creal((IMAGE)->data.TYPE[i][j]), cimag((IMAGE)->data.TYPE[i][j]));                 \
+        } else if(PS_IS_PSELEMTYPE_INT((IMAGE)->type.type)) {                                                  \
+            printf("%d", (int)(IMAGE)->data.TYPE[i][j]);                                                       \
+        } else {                                                                                             \
+            printf("%f", (double)(IMAGE)->data.TYPE[i][j]);                                                    \
+        }                                                                                                    \
+    }                                                                                                        \
+    printf("\n");                                                                                            \
+}                                                                                                            \
+printf("\n");
+
+
+#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS)                                                    \
+(NAME) = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE);                                             \
+for(int i=0; i<(NAME)->numRows; i++) {                                                                         \
+    for(int j=0; j<(NAME)->numCols; j++) {                                                                     \
+        (NAME)->data.TYPE[i][j] = VALUE;                                                                       \
+    }                                                                                                        \
+}
+
+static int testMaskBadPixels1(void);
+static int testMaskBadPixels2(void);
+static int testMaskBadPixels3(void);
+static int testMaskBadPixels4(void);
+static int testMaskBadPixels5(void);
+static int testMaskBadPixels6(void);
+static int testMaskBadPixels7(void);
+static int testMaskBadPixels8(void);
+static int testMaskBadPixels9(void);
+static int testMaskBadPixels10(void);
+static int testMaskBadPixels11(void);
+
+
+testDescription tests[] = {
+                              {testMaskBadPixels1, 885, "pmMaskBadPixels - Create mask based on maskVal argument", 0, false},
+                              {testMaskBadPixels2, 885, "pmMaskBadPixels - Create mask based on saturation argument", 0, false},
+                              {testMaskBadPixels3, 885, "pmMaskBadPixels - Create mask based on growVal and grow arguments", 0, false},
+                              {testMaskBadPixels4, 885, "pmMaskBadPixels - Auto create mask based on maskVal argument", 0, false},
+                              {testMaskBadPixels5, 885, "pmMaskBadPixels - Attempt to use null mask", 0, false},
+                              {testMaskBadPixels6, 885, "pmMaskBadPixels - Attempt tp use null input image", 0, false},
+                              {testMaskBadPixels7, 885, "pmMaskBadPixels - Attempt to use input image bigger than mask", 0, false},
+                              {testMaskBadPixels8, 885, "pmMaskBadPixels - Attempt to use input image mask bigger than mask", 0, false},
+                              {testMaskBadPixels9, 885, "pmMaskBadPixels - Attempt to use offset greater than input image", 0, false},
+                              {testMaskBadPixels10, 885, "pmMaskBadPixels - Attempt to use complex input image", 0, false},
+                              {testMaskBadPixels11, 885, "pmMaskBadPixels - Attempt to use non-mask type mask image", 0, false        },
+                              {NULL}
+                          };
+
+/*
+    #define PS_TYPE_MASK PS_TYPE_U8
+    #define PS_TYPE_MASK_DATA U8
+    #define PS_TYPE_MASK_NAME "psU8"
+*/
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+
+#define NUM_ROWS 50
+#define NUM_COLS 50
+#define DEFAULT_IMAGE_VAL 0.0
+#define DEFAULT_MASK_VAL 0
+#define MASK_VAL 1
+#define SAT_VAL  100.0
+#define GROW_VAL 1
+#define GROW_RAD 10
+int testMaskBadPixels1( void )
+{
+    //
+    // Test A - Create mask based on maskVal argument
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+    maskReadout->image->data.PS_TYPE_MASK_DATA[NUM_ROWS/2][NUM_COLS/2]=1;
+    maskReadout->image->row0 = 0;
+    maskReadout->image->col0 = 0;
+
+    PRINT_MATRIX(maskReadout->image, U8, "Data mask:");
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+    PRINT_MATRIX(inReadout->mask, PS_TYPE_MASK_DATA, "Resulting mask:");
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels2( void )
+{
+    //
+    // Test B - Create mask based on saturation argument
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+    inReadout->image->data.F32[NUM_ROWS/2][NUM_COLS/2] = 150.0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+
+    //PS_IMAGE_PRINT_F32(inReadout->image);
+    PRINT_MATRIX(maskReadout->image, U8, "Data mask:");
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+    PRINT_MATRIX(inReadout->mask, U8, "Resulting mask:");
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels3( void )
+{
+    //
+    // Test C - Create mask based on growVal and grow arguments
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+    inReadout->image->data.F32[NUM_ROWS/2][NUM_COLS/2]=GROW_VAL;
+    inReadout->image->data.F32[NUM_ROWS/4][NUM_COLS/4]=GROW_VAL;
+    inReadout->image->data.F32[NUM_ROWS/4][NUM_COLS-(NUM_COLS/4)]=GROW_VAL;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS);
+
+    PRINT_MATRIX(maskReadout->image, U8, "Data mask:");
+    //PS_IMAGE_PRINT_F32(inReadout->image);
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+    PRINT_MATRIX(inReadout->mask, U8, "Resulting mask:");
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels4( void )
+{
+    //
+    // Test D - Auto Create mask based on maskVal argument
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+    maskReadout->image->data.PS_TYPE_MASK_DATA[NUM_ROWS/2][NUM_COLS/2]=1;
+
+    PRINT_MATRIX(maskReadout->image, U8, "Data mask:");
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+    PRINT_MATRIX(inReadout->mask, U8, "Resulting mask:");
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels5( void )
+{
+    //
+    // Test E - Attempt to use null mask
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmMaskBadPixels(inReadout, NULL, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+    psFree(inReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels6( void )
+{
+    //
+    // Test F - Attempt tp use null input image
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels7( void )
+{
+    //
+    // Test G - Attempt to use input image bigger than mask
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS+10, NUM_COLS+10);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels8( void )
+{
+    //
+    // Test H - Attempt to use mask bigger than image
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS+10, NUM_COLS+10)
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels9( void )
+{
+    //
+    // Test I - Attempt to use offset greater than input image
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+    *(int*)&inReadout->image->col0 = 150;
+    *(int*)&inReadout->image->row0 = 150;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels10( void )
+{
+    //
+    // Test J - Attempt to use complex input image
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, C64, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels11( void )
+{
+    //
+    // Test K - Attempt to use mask image with wrong data type.
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, F64, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmNonLinear.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmNonLinear.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmNonLinear.c	(revision 22322)
@@ -0,0 +1,268 @@
+/* @file tst_pmNonLinear.c
+ *
+ *  @brief Contains the tests for pmNonLinear.c:
+ *
+ * test00: This code will create a simple polynomial, and call
+ * pmNonLinearityPolynomial() for a variety of image sizes [(1, 1), (1,
+ * N), (N, 1), (N, N)].  
+ *
+ * test01: This code will create simple table lookup vectors, and call
+ * pmNonLinearityPolynomial() for a variety of image sizes [(1, 1), (1,
+ * N), (N, 1), (N, N)].  
+ *
+ * test02, test03: This code tests the functions with various unallowable
+ * input parameters (NULLS) and incorrect vector sizes.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  XXX: Add tests in which the lookup file has incorrect number of entries,
+ *  and where the data is outside the pmReadout range.
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-01-26 21:10:51 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psTest.h"
+#include "pslib.h"
+#include "pmNonLinear.h"
+static int test00(void);
+static int test01(void);
+static int test02(void);
+static int test03(void);
+testDescription tests[] = {
+                              {test00, 000, "pmNonLinearityPolynomial", true, false},
+                              {test01, 000, "pmNonLinearityLookup", true, false},
+                              {test02, 000, "pmNonLinearityPolynomial(): error/warning conditions", true, false},
+                              {test03, 000, "pmNonLinearityLookup(): error/warning conditions", true, false},
+                              {NULL}
+                          };
+
+#define NUM_ROWS 8
+#define NUM_COLS 8
+#define LOOKUP_FILENAME ".tmp_tst_pmNonLinearLookupFile"
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    //
+    // We generate a lookup file for future tests.  We should probably remove
+    // it when we're done.
+    //
+    FILE *fp = fopen(LOOKUP_FILENAME, "w");
+    ;
+    for (psS32 i=0;i<PS_MAX(NUM_COLS, NUM_ROWS)*3;i++) {
+        fprintf(fp, "%f %f\n", (float) i, (float) (2 * i));
+    }
+    fclose(fp);
+
+    //    system("rm LOOKUP_FILENAME");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+int doNonLinearityPolynomialTest(int numCols, int numRows)
+{
+    int i;
+    int j;
+    float actual;
+    float expect;
+    int testStatus = true;
+    psImage *myImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = myImage;
+    psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    myPoly->coeff[1] = 1.0;
+
+    printPositiveTestHeader(stdout, "pmNonLinear", "doNonLinearityPolynomialTest");
+
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+
+    myReadout = pmNonLinearityPolynomial(myReadout, myPoly);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            expect = psPolynomial1DEval(myPoly, (float) (i + j));
+            actual = myReadout->image->data.F32[i][j];
+            if (FLT_EPSILON < fabs(expect - actual)) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                testStatus = false;
+            }
+        }
+    }
+
+
+    psFree(myReadout);
+    psFree(myPoly);
+    printFooter(stdout, "pmNonLinear", "doNonLinearityPolynomialTest", true);
+    return(testStatus);
+}
+
+int test00( void )
+{
+    int testStatus = 0;
+
+    testStatus |= doNonLinearityPolynomialTest(1, 1);
+    testStatus |= doNonLinearityPolynomialTest(NUM_COLS, 1);
+    testStatus |= doNonLinearityPolynomialTest(1, NUM_ROWS);
+    testStatus |= doNonLinearityPolynomialTest(NUM_COLS, NUM_ROWS);
+
+    return(testStatus);
+}
+
+int doNonLinearityLookupTest(int numCols, int numRows)
+{
+    int i;
+    int j;
+    float actual;
+    float expect;
+    int testStatus = true;
+    psImage *myImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = myImage;
+
+    printPositiveTestHeader(stdout, "pmNonLinear", "doNonLinearityLookupTest");
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+
+    myReadout = pmNonLinearityLookup(myReadout, LOOKUP_FILENAME);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            expect = (float) (2 * (i + j));
+            actual = myReadout->image->data.F32[i][j];
+            if (FLT_EPSILON < fabs(expect - actual)) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                testStatus = false;
+            }
+        }
+    }
+
+    psFree(myReadout);
+    printFooter(stdout, "pmNonLinear", "doNonLinearityLookupTest", true);
+    return(testStatus);
+}
+
+int test01( void )
+{
+    int testStatus = 0;
+
+    testStatus |= doNonLinearityLookupTest(1, 1);
+    testStatus |= doNonLinearityLookupTest(NUM_COLS, 1);
+    testStatus |= doNonLinearityLookupTest(1, NUM_ROWS);
+    testStatus |= doNonLinearityLookupTest (NUM_COLS, NUM_ROWS);
+
+    return(testStatus);
+}
+
+int test02( void )
+{
+    int i;
+    int j;
+    int testStatus = true;
+    psImage *myImage = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    pmReadout *rc = NULL;
+    myReadout->image = myImage;
+    psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    myPoly->coeff[1] = 1.0;
+
+    printPositiveTestHeader(stdout, "pmNonLinear", "Testing bad input parameter conditions.");
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityPolynomial() with NULL input readout.  Should generate error, return NULL.\n");
+    rc = pmNonLinearityPolynomial(NULL, myPoly);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityPolynomial() with NULL input readout->image.  Should generate error, return NULL.\n");
+    psImage *tmpImage = myReadout->image;
+    myReadout->image = NULL;
+    rc = pmNonLinearityPolynomial(myReadout, myPoly);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+    myReadout->image = tmpImage;
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityPolynomial() with NULL polynomial.  Should generate error, return NULL.\n");
+    rc = pmNonLinearityPolynomial(myReadout, NULL);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+
+    psFree(myReadout);
+    psFree(myPoly);
+    return(testStatus);
+}
+
+
+int test03Init(pmReadout *myReadout)
+{
+    for (psS32 i=0;i<NUM_ROWS;i++) {
+        for (psS32 j=0;j<NUM_COLS;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+    return(0);
+}
+
+int test03()
+{
+    int testStatus = true;
+    psImage *myImage = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    pmReadout *rc = NULL;
+    myReadout->image = myImage;
+
+    test03Init(myReadout);
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with NULL input pmReadout.  Should generate error, return NULL.\n");
+    rc = pmNonLinearityLookup(NULL, LOOKUP_FILENAME);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with NULL input pmReadout->image.  Should generate error, return NULL.\n");
+    psImage *tmpImage = myReadout->image;
+    myReadout->image = NULL;
+    rc = pmNonLinearityLookup(myReadout, LOOKUP_FILENAME);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+    myReadout->image = tmpImage;
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with non-existent lookup file.\n");
+    rc = pmNonLinearityLookup(myReadout, "I_DONT_EXIST");
+    if (rc == NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
+        testStatus = false;
+    }
+
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with one pixels outside inFlux range.  Should generate warnings.\n");
+
+    psFree(myReadout);
+
+    printFooter(stdout, "pmNonLinear", "Testing bad input parameter conditions.", true);
+    return(testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmSubtractBias.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmSubtractBias.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmSubtractBias.c	(revision 22322)
@@ -0,0 +1,1129 @@
+/** @file tst_pmSubtractBias.c
+ *
+ *  @brief Contains the tests for pmSubtractBias.c:
+ *
+ * test00a: This code will subtract full bias frames from the input image.
+ * XXX: Must test:
+ *  Various image offsets.
+ *  Various image size combinations.
+ *  Various data types for the bias and input images.
+ *  Ensure code works when CELL.TRIMSEC is not set.
+ * test00b: This code will subtract full dark frames from the input image.
+ * XXX: Must test:
+ *  Various image offsets.
+ *  Various image size combinations.
+ *  Various data types for the bias and input images.
+ *  Code properly determines CELL.DARKTIME from cell metadata.
+ *  Ensure code works when CELL.DARKTIME is not set.
+ *  Ensure code works when CELL.TRIMSEC is not set.
+ *  test03: Calculate a row overscan vector and subtract it from each
+ *  row in the input image.
+ * test05:
+ *
+ *  @author GLG, MHPCC
+ *
+ *  XXX: Memory leaks are not being detected.
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-05-25 22:02:23 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psTest.h"
+#include "pslib.h"
+#include "pmSubtractBias.h"
+static int test00a(void);
+static int test00b(void);
+//static int test01(void);
+//static int test02(void);
+//static int test03(void);
+//static int test04(void);
+static int test05(void);
+testDescription tests[] = {
+                              {test00a, 000, "doSubtractBiasFullFrame", 0, true},
+                              {test00b, 000, "doSubtractDarkFullFrame", 0, true},
+                              //                              {test01, 000, "pmSubtractBias", 0, true},
+                              //                              {test02, 000, "pmSubtractBias", 0, true},
+                              //                              {test03, 000, "pmSubtractBias", 0, true},
+                              //                              {test04, 000, "pmSubtractBias", 0, true},
+                              {test05, 000, "pmSubtractBias", 0, false},
+                              {NULL}
+                          };
+
+psS32 currentId = 0;
+psS32 memLeaks = 0;             // XXX: remove
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("spline1DFree", 0);
+    psTraceSetLevel("calculateSecondDerivs", 0);
+    psTraceSetLevel("vectorBinDisectF32", 0);
+    psTraceSetLevel("vectorBinDisectF64", 0);
+    psTraceSetLevel("p_psVectorBinDisect", 0);
+    psTraceSetLevel("psSpline1DAlloc", 0);
+    psTraceSetLevel("psVectorFitSpline1D", 0);
+    psTraceSetLevel("psSpline1DEval", 0);
+    psTraceSetLevel("psSpline1DEvalVector", 0);
+
+    psS32 currentId = psMemGetId(); // XXX: remove
+    psS32 memLeaks = 0;             // XXX: remove
+    if (0) {
+        PRINT_MEMLEAKS(0);
+    }
+
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+#define NUM_ROWS 8
+#define NUM_COLS 8
+#define MAX_HEADER_MSG_LENGTH 1000
+#define POLYNOMIAL_FIT_ORDER 2
+#define NUM_OVERSCANS 2
+/******************************************************************************
+doSubtractBiasFullFrame(): a sample pmReadout as well as a bias image are
+created and the bias image is subtracted from the pmReadout.
+ *****************************************************************************/
+int doSubtractBiasFullFrame(int numCols, int numRows)
+{
+    int i;
+    int j;
+    float actual;
+    float expect;
+    int testStatus = 0;
+    psImage *tmpImage1 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *tmpImage2 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    pmReadout *myBias = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImage1;
+    myBias->image = tmpImage2;
+
+    char *HeaderMessageStr = (char *) psAlloc(MAX_HEADER_MSG_LENGTH);
+    sprintf(HeaderMessageStr, "doSubtractBiasFullFrame(%d, %d)", numRows, numCols);
+    printPositiveTestHeader(stdout, "pmSubtractBias", HeaderMessageStr);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+            myBias->image->data.F32[i][j] = 1.0;
+        }
+    }
+
+    myReadout = pmSubtractBias(myReadout, NULL, PM_FIT_NONE, false,
+                               NULL, 0, myBias, NULL);
+
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            expect = ((float) (i + j)) - 1.0;
+            actual = myReadout->image->data.F32[i][j];
+            if (FLT_EPSILON < fabs(expect - actual)) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                testStatus = 1;
+            }
+        }
+    }
+
+
+    psFree(myReadout);
+    psFree(myBias);
+    printFooter(stdout, "pmSubtractBias", HeaderMessageStr, true);
+    psFree(HeaderMessageStr);
+    return(testStatus);
+}
+
+
+int test00a( void )
+{
+    int testStatus = 0;
+
+    testStatus |= doSubtractBiasFullFrame(1, 1);
+    testStatus |= doSubtractBiasFullFrame(NUM_COLS, 1);
+    testStatus |= doSubtractBiasFullFrame(1, NUM_ROWS);
+    testStatus |= doSubtractBiasFullFrame(NUM_COLS, NUM_ROWS);
+    return(testStatus);
+}
+
+
+/******************************************************************************
+doSubtractDarkFullFrame(): a sample pmReadout as well as a dark image are
+created and the dark image is subtracted from the pmReadout.
+ *****************************************************************************/
+int doSubtractDarkFullFrame(int numCols, int numRows)
+{
+    int i;
+    int j;
+    float actual;
+    float expect;
+    int testStatus = 0;
+    psImage *tmpImage1 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *tmpImage2 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    pmReadout *myDark = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImage1;
+    myDark->image = tmpImage2;
+
+    char *HeaderMessageStr = (char *) psAlloc(MAX_HEADER_MSG_LENGTH);
+    sprintf(HeaderMessageStr, "doSubtractDarkFullFrame(%d, %d)", numRows, numCols);
+    printPositiveTestHeader(stdout, "pmSubtractBias", HeaderMessageStr);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+            myDark->image->data.F32[i][j] = 1.0;
+        }
+    }
+
+    myReadout = pmSubtractBias(myReadout, NULL, PM_FIT_NONE, false,
+                               NULL, 0, NULL, myDark);
+
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            expect = ((float) (i + j)) - 1.0;
+            actual = myReadout->image->data.F32[i][j];
+            if (FLT_EPSILON < fabs(expect - actual)) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                testStatus = 1;
+            }
+        }
+    }
+
+
+    psFree(myReadout);
+    psFree(myDark);
+    printFooter(stdout, "pmSubtractBias", HeaderMessageStr, true);
+    psFree(HeaderMessageStr);
+    return(testStatus);
+}
+
+
+int test00b( void )
+{
+    int testStatus = 0;
+
+    testStatus |= doSubtractDarkFullFrame(1, 1);
+    testStatus |= doSubtractDarkFullFrame(NUM_COLS, 1);
+    testStatus |= doSubtractDarkFullFrame(1, NUM_ROWS);
+    testStatus |= doSubtractDarkFullFrame(NUM_COLS, NUM_ROWS);
+    return(testStatus);
+}
+
+
+/*
+int doSubtractOverscansTestInputCases(int numCols, int numRows)
+{
+    int i;
+    int j;
+    int testStatus = 0;
+    psImage *tmpImage1 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *tmpImage2 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *tmpImage3 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *tmpImage4 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *tmpImage2Short = psImageAlloc(numCols-1, numRows-1, PS_TYPE_F32);
+    psImage *tmpImage3Short = psImageAlloc(numCols-1, numRows-1, PS_TYPE_F32);
+    psImage *tmpImage4Short = psImageAlloc(numCols-1, numRows-1, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImage1;
+    pmReadout *rc = NULL;
+    psList *list;
+    psList *listShort;
+    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psImage *tmpImage5 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    pmReadout *myBias = pmReadoutAlloc(NULL);
+    myBias->image = tmpImage5;
+    printPositiveTestHeader(stdout, "pmSubtractBias", "Testing input parameter error conditions");
+ 
+    psImage *tmpImage5ShortRows = psImageAlloc(numCols, numRows-1, PS_TYPE_F32);
+    pmReadout *myBiasShortRows = pmReadoutAlloc(NULL);
+    myBiasShortRows->image = tmpImage5ShortRows;
+    psImage *tmpImage5ShortCols = psImageAlloc(numCols-1, numRows, PS_TYPE_F32);
+    pmReadout *myBiasShortCols = pmReadoutAlloc(NULL);
+    myBiasShortCols->image = tmpImage5ShortCols;
+ 
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+            tmpImage2->data.F32[i][j] = 3.0;
+            tmpImage3->data.F32[i][j] = 4.0;
+            tmpImage4->data.F32[i][j] = 5.0;
+            myBias->image->data.F32[i][j] = 1.0;
+        }
+    }
+    list = psListAlloc(tmpImage2);
+    psListAdd(list, PS_LIST_HEAD, tmpImage3);
+    psListAdd(list, PS_LIST_HEAD, tmpImage4);
+ 
+    for (i=0;i<numRows-1;i++) {
+        for (j=0;j<numCols-1;j++) {
+            tmpImage2Short->data.F32[i][j] = 3.0;
+            tmpImage3Short->data.F32[i][j] = 4.0;
+            tmpImage4Short->data.F32[i][j] = 5.0;
+        }
+    }
+    listShort = psListAlloc(tmpImage2Short);
+    psListAdd(listShort, PS_LIST_HEAD, tmpImage3Short);
+    psListAdd(listShort, PS_LIST_HEAD, tmpImage4Short);
+    for (i=0;i<numRows-1;i++) {
+        for (j=0;j<numCols;j++) {
+            myBiasShortRows->image->data.F32[i][j] = 1.0;
+        }
+    }
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols-1;j++) {
+            myBiasShortCols->image->data.F32[i][j] = 1.0;
+        }
+    }
+ 
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_ALL.  Should generate error.\n");
+    rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_ALL, stat, 0, PM_FIT_NONE, NULL);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+    }
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_ROWS.  Should generate error.\n");
+    rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_ROWS, stat, 0, PM_FIT_NONE, NULL);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_COLUMNS.  Should generate error.\n");
+    rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_COLUMNS, stat, 0, PM_FIT_NONE, NULL);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with non-NULL overscan list and PM_OVERSCAN_NONE.  Should generate warning.\n");
+    rc = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_NONE, stat, 0, PM_FIT_NONE, myBias);
+ 
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            psF32 expect = ((float) (i + j)) - 1.0;
+            psF32 actual = rc->image->data.F32[i][j];
+            if (FLT_EPSILON < fabs(expect - actual)) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                testStatus = 1;
+            }
+ 
+            // Restore myReadout for next test.
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+ 
+    // XXX: This does not seem to be a requirement.
+    if (0) {
+        printf("------------------------------------------------------------------\n");
+        printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_NONE.  Should generate warning.\n");
+        rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, stat,
+                            0, PM_FIT_NONE, myBias);
+ 
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                psF32 expect = ((float) (i + j)) - 1.0;
+                psF32 actual = rc->image->data.F32[i][j];
+                if (FLT_EPSILON < fabs(expect - actual)) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                    testStatus = 1;
+                }
+ 
+                // Restore myReadout for next test.
+                myReadout->image->data.F32[i][j] = (float) (i + j);
+            }
+        }
+    }
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with PM_OVERSCAN_NONE and PM_FIT_POLYNOMIAL.  Should generate Warning.\n");
+    rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, stat, 0, PM_FIT_POLYNOMIAL, myBias);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            psF32 expect = ((float) (i + j)) - 1.0;
+            psF32 actual = rc->image->data.F32[i][j];
+            if (FLT_EPSILON < fabs(expect - actual)) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                testStatus = 1;
+            }
+ 
+            // Restore myReadout for next test.
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+ 
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with PM_OVERSCAN_ALL and PM_FIT_SPLINE.  Should generate Warning.\n");
+    rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, stat, 0, PM_FIT_SPLINE, myBias);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+ 
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            psF32 expect = ((float) (i + j)) - 1.0;
+            psF32 actual = rc->image->data.F32[i][j];
+            if (FLT_EPSILON < fabs(expect - actual)) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                testStatus = 1;
+            }
+ 
+            // Restore myReadout for next test.
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+ 
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with multiple stats->options.  Should generate Warning.\n");
+    stat->options|= PS_STAT_SAMPLE_MEDIAN;
+    myReadout = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_ALL, stat,
+                               0, PM_FIT_NONE, NULL);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            psF32 expect = ((float) (i + j)) - 12.0;
+            psF32 actual = rc->image->data.F32[i][j];
+            if (FLT_EPSILON < fabs(expect - actual)) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                testStatus = 1;
+            }
+        }
+    }
+    stat->options = PS_STAT_SAMPLE_MEAN;
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() undersize overscans (PM_OVERSCAN_ROWS).  Should generate Warning.\n");
+    rc = pmSubtractBias(myReadout, NULL, listShort, PM_OVERSCAN_ROWS, stat,
+                        0, PM_FIT_NONE, NULL);
+    if (0) {
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                psF32 expect = ((float) (i + j)) - 12.0;
+                psF32 actual = rc->image->data.F32[i][j];
+                if (FLT_EPSILON < fabs(expect - actual)) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                    testStatus = 1;
+                }
+            }
+        }
+    }
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() undersize overscans (PM_OVERSCAN_COLUMNS).  Should generate Warning.\n");
+    rc = pmSubtractBias(myReadout, NULL, listShort, PM_OVERSCAN_COLUMNS, stat,
+                        0, PM_FIT_NONE, NULL);
+    if (0) {
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                psF32 expect = ((float) (i + j)) - 12.0;
+                psF32 actual = rc->image->data.F32[i][j];
+                if (FLT_EPSILON < fabs(expect - actual)) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                    testStatus = 1;
+                }
+            }
+        }
+    }
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() undersize bias image (short rows).  Should generate Error.\n");
+    myReadout = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, NULL,
+                               0, PM_FIT_NONE, myBiasShortRows);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+ 
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() undersize bias image (short columns).  Should generate Error.\n");
+    myReadout = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, NULL,
+                               0, PM_FIT_NONE, myBiasShortCols);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with bogus PM_FIT.  Should generate Error.\n");
+    myReadout = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_ROWS, stat,
+                               0, 54321, NULL);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+ 
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with bogus overScanAxis.  Should generate Error.\n");
+    myReadout = pmSubtractBias(myReadout, NULL, list, 54321, stat,
+                               0, PM_FIT_NONE, NULL);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+ 
+    if (0) {
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                psF32 expect = ((float) (i + j)) - 12.0;
+                psF32 actual = rc->image->data.F32[i][j];
+                if (FLT_EPSILON < fabs(expect - actual)) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                    testStatus = 1;
+                }
+            }
+        }
+    }
+ 
+    printf("------------------------------------------------------------------\n");
+    psFree(myReadout);
+    psFree(tmpImage2);
+    psFree(tmpImage3);
+    psFree(tmpImage4);
+    psFree(tmpImage2Short);
+    psFree(tmpImage3Short);
+    psFree(tmpImage4Short);
+    psFree(myBias);
+    psFree(myBiasShortRows);
+    psFree(myBiasShortCols);
+    psFree(stat);
+    psFree(list);
+    psFree(listShort);
+ 
+    printFooter(stdout, "pmSubtractBias", "Testing input parameter error conditions", true);
+    return(testStatus);
+}
+ 
+int test04( void )
+{
+    int testStatus = 0;
+ 
+    testStatus |= doSubtractOverscansTestInputCases(NUM_COLS, NUM_ROWS);
+    return(testStatus);
+}
+*/
+
+void PS_POLY1D_PRINT(
+    psPolynomial1D *poly)
+{
+    printf("-------------- PS_POLY1D_PRINT() --------------\n");
+    printf("poly->nX is %d\n", poly->nX);
+    for (psS32 i = 0 ; i < (1 + poly->nX) ; i++) {
+        printf("poly->coeff[%d] is %f\n", i, poly->coeff[i]);
+    }
+}
+
+void PS_PRINT_SPLINE2(psSpline1D *mySpline)
+{
+    printf("-------------- PS_PRINT_SPLINE2() --------------\n");
+    if (mySpline != NULL) {
+        printf("mySpline->n is %d\n", mySpline->n);
+        for (psS32 i = 0 ; i < mySpline->n ; i++) {
+            if (mySpline->spline[i] != NULL) {
+                PS_POLY1D_PRINT(mySpline->spline[i]);
+            }
+        }
+        if (mySpline->knots != NULL) {
+            PS_VECTOR_PRINT_F32(mySpline->knots);
+        }
+    } else {
+        printf("NULL\n");
+    }
+    printf("-------------- PS_PRINT_SPLINE2() DONE --------------\n");
+}
+
+
+
+
+
+
+/******************************************************************************
+doSubtractOverscansGeneric(): This is a general version of the
+bias subtraction tests which allows the various parameters to be specified
+as arguments.
+ *****************************************************************************/
+int doSubtractOverscansGeneric(
+    int imageNumCols,
+    int imageNumRows,
+    int overscanNumCols,
+    int overscanNumRows,
+    int numOverscans,
+    pmOverscanAxis overscanaxis,
+    pmFit fit,
+    psS32 nBin)
+{
+    int i;
+    int j;
+    float actual;
+    float expect;
+    int testStatus = 0;
+
+    printPositiveTestHeader(stdout, "pmSubtractBias", "PUT COMMENT HERE");
+    printf("---- doSubtractOverscansGeneric() ----\n");
+    printf("    Image size: %d by %d\n", imageNumRows, imageNumCols);
+    printf("    Overscan size: %d by %d\n", overscanNumRows, overscanNumCols);
+    printf("    Total Overscans: %d\n", numOverscans);
+    printf("    Binning factor: %d\n", nBin);
+    if (overscanaxis == PM_OVERSCAN_ROWS)
+        printf("    Overscan axis: PM_OVERSCAN_ROWS\n");
+    if (overscanaxis == PM_OVERSCAN_COLUMNS)
+        printf("    Overscan axis: PM_OVERSCAN_COLUMNS\n");
+    if (overscanaxis == PM_OVERSCAN_ALL)
+        printf("    Overscan axis: PM_OVERSCAN_ALL\n");
+    if (overscanaxis == PM_OVERSCAN_NONE)
+        printf("    Overscan axis: PM_OVERSCAN_NONE\n");
+    if (fit == PM_FIT_NONE)
+        printf("    Fit type: PM_FIT_NONE\n");
+    if (fit == PM_FIT_POLYNOMIAL)
+        printf("    Fit type: PM_FIT_POLYNOMIAL\n");
+    if (fit == PM_FIT_SPLINE)
+        printf("    Fit type: PM_FIT_SPLINE\n");
+
+    //
+    // Create and initialize input image, FPA hierarchy.
+    //
+    const psMetadata *camera = psMetadataAlloc();
+    pmFPA* fpa = pmFPAAlloc(camera);
+
+    if (fpa == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc returned a NULL.\n");
+        return 1;
+    }
+
+    pmChip *chip = pmChipAlloc(fpa, "ChipName");
+    if (chip == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmChipAlloc returned a NULL.\n");
+        return 2;
+    }
+
+    pmCell *cell = pmCellAlloc(chip, (psMetadata *) camera, "CellName");
+    if (cell == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmCellAlloc returned a NULL.\n");
+        return 3;
+    }
+
+    pmReadout *myReadout = pmReadoutAlloc(cell);
+    myReadout->image = psImageAlloc(imageNumCols, imageNumRows, PS_TYPE_F32);
+    for (i=0;i<myReadout->image->numRows;i++) {
+        for (j=0;j<myReadout->image->numCols;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+
+    //
+    // Set overscan axis in the metadata.
+    //
+    psBool rc = false;
+    if (overscanaxis == PM_OVERSCAN_ROWS) {
+        rc = psMetadataAddS32(myReadout->parent->concepts, PS_LIST_HEAD, "CELL.READDIR", 0, NULL, 1);
+    } else if (overscanaxis == PM_OVERSCAN_COLUMNS) {
+        rc = psMetadataAddS32(myReadout->parent->concepts, PS_LIST_HEAD, "CELL.READDIR", 0, NULL, 2);
+    } else if (overscanaxis == PM_OVERSCAN_ALL) {
+        rc = psMetadataAddS32(myReadout->parent->concepts, PS_LIST_HEAD, "CELL.READDIR", 0, NULL, 3);
+    } else if (overscanaxis == PM_OVERSCAN_NONE) {
+        rc = psMetadataAddS32(myReadout->parent->concepts, PS_LIST_HEAD, "CELL.READDIR", 0, NULL, 0);
+    }
+    if (rc == false) {
+        printf("TEST ERROR: Could not set CELL.READDIR metadata.\n");
+        testStatus = 1;
+    }
+
+    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, POLYNOMIAL_FIT_ORDER);
+    psSpline1D *mySpline = NULL;
+
+
+    if (0) {
+        if (overscanNumRows <= 0) {
+            overscanNumRows = 1;
+        }
+        if (overscanNumCols <= 0) {
+            overscanNumCols = 1;
+        }
+    }
+    psF32 oAverage = 0.0;
+    myReadout->bias = NULL;
+    for (psS32 i = 0 ; i < numOverscans ; i++) {
+        psImage *tmpImage = psImageAlloc(overscanNumCols, overscanNumRows, PS_TYPE_F32);
+        psF32 oValue = (float) (i + 3);
+        PS_IMAGE_SET_F32(tmpImage, oValue);
+        oAverage += oValue;
+        if (myReadout->bias == NULL) {
+            myReadout->bias = psListAlloc(tmpImage);
+        } else {
+            psListAdd(myReadout->bias, PS_LIST_HEAD, tmpImage);
+        }
+    }
+    oAverage/= (psF32) numOverscans;
+    if (0) {
+        if (fit == PM_FIT_NONE) {
+            myReadout = pmSubtractBias(myReadout, NULL, PM_FIT_NONE, overscanaxis,
+                                       stat, nBin, NULL, NULL);
+        } else if (fit == PM_FIT_POLYNOMIAL) {
+            myReadout = pmSubtractBias(myReadout, myPoly, PM_FIT_POLYNOMIAL, overscanaxis,
+                                       stat, nBin, NULL, NULL);
+        } else if (fit == PM_FIT_SPLINE) {
+            //        mySpline = psSpline1DAlloc();
+            myReadout = pmSubtractBias(myReadout, mySpline, PM_FIT_SPLINE, overscanaxis,
+                                       stat, nBin, NULL, NULL);
+        }
+        if (myReadout == NULL ) {
+            printf("TEST ERROR: pmSubtractBias() returned NULL.\n");
+            testStatus = 1;
+        } else {
+            for (i=0;i<imageNumRows;i++) {
+                for (j=0;j<imageNumCols;j++) {
+                    expect = ((float) (i + j)) - oAverage;
+                    actual = myReadout->image->data.F32[i][j];
+                    if (FLT_EPSILON < fabs(expect - actual)) {
+                        printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                        testStatus = 1;
+                    } else {
+                        //printf("GOOD: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                    }
+                }
+            }
+        }
+    }
+
+    // HEY
+    psFree(fpa);
+    psFree(stat);
+    psFree(myPoly);
+    psFree(mySpline);
+
+    printFooter(stdout, "pmSubtractBias", "Column Overscans", true);
+    return(testStatus);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+test05a() The following combinations are tested here:
+ Overscan images are same size, no fit, bin factor is 1.
+ Overscan images are same size, no fit, bin factor is 2.
+ *****************************************************************************/
+int test05a(
+    psS32 imageNumCols,
+    psS32 imageNumRows,
+    psS32 overscanNumCols,
+    psS32 overscanNumRows)
+{
+    int testStatus = 0;
+
+    // imageNumCols, imageNumRows, overscanNumCols, overscanNumRows,
+    // overscanaxis, fit, nBin
+
+    //
+    // Overscan images are same size, no fit, bin factor is 1.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_NONE, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_NONE, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_NONE, 1);
+
+    //
+    // Overscan images are same size, no fit, bin factor is 2.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_NONE, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_NONE, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_NONE, 2);
+
+    return(testStatus);
+}
+
+
+/******************************************************************************
+test05b() The following combinations are tested here:
+ Overscan images are too small, spline fit, bin factor is 1.
+ Overscan images are too small, spline fit, bin factor is 2.
+ Overscan images are same size, spline fit, bin factor is 1.
+ Overscan images are same size, spline fit, bin factor is 2.
+ Overscan images are too big,   spline fit, bin factor is 1.
+ Overscan images are too big,   spline fit, bin factor is 2.
+ A single overscan image of the same size, spline fit, bin factor is 1.
+ 
+ Overscan images are too small, polynomial fit, bin factor is 1.
+ Overscan images are too small, polynomial fit, bin factor is 2.
+ Overscan images are same size, polynomial fit, bin factor is 1.
+ Overscan images are same size, polynomial fit, bin factor is 2.
+ Overscan images are too big,   polynomial fit, bin factor is 1.
+ Overscan images are too big,   polynomial fit, bin factor is 2.
+ A single overscan image of the same size, polynomial fit, bin factor is 1.
+ 
+XXX: Must add M-by-N image size tests.
+ *****************************************************************************/
+int test05b(
+    psS32 imageNumCols,
+    psS32 imageNumRows,
+    psS32 overscanNumCols,
+    psS32 overscanNumRows)
+{
+    int testStatus = 0;
+
+    //
+    // Overscan images are too small, spline fit, bin factor is 1.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 1);
+
+    //
+    // Overscan images are too small, spline fit, bin factor is 2.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 2);
+
+
+    //
+    // Overscan images are same size, spline fit, bin factor is 1.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 1);
+
+    //
+    // Overscan images are same size, spline fit, bin factor is 2.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 2);
+
+    //
+    // Overscan images are too big, spline fit, bin factor is 1.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 1);
+
+    //
+    // Overscan images are too big, spline fit, bin factor is 2.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2 , NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 2);
+
+
+    //
+    // A single overscan image of the same size, spline fit, bin factor is 1.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 1);
+
+
+    //
+    // Overscan images are too small, polynomial fit, bin factor is 1.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    //
+    // Overscan images are too small, polynomial fit, bin factor is 2.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+
+    //
+    // Overscan images are same size, polynomial fit, bin factor is 1.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    //
+    // Overscan images are same size, polynomial fit, bin factor is 2.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    //
+    // Overscan images are too big, polynomial fit, bin factor is 1.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    //
+    // Overscan images are too big, polynomial fit, bin factor is 2.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    //
+    // A single overscan image of the same size, polynomial fit, bin factor is 1.
+    //
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractOverscansGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    return(testStatus);
+}
+
+
+
+#define LOW_COLS 3
+#define LOW_ROWS 3
+
+/******************************************************************************
+test05(): See test05a() and test05b().
+ 
+We run the tests in test05b() starting with all possible combinations of
+sizes.
+ 
+XXX: Must add M-by-N image size tests.
+ *****************************************************************************/
+int test05()
+{
+    int testStatus = 0;
+    //    testStatus = test05a(NUM_COLS, NUM_ROWS, NUM_COLS, NUM_ROWS);
+
+    //    testStatus|= test05b(LOW_COLS, LOW_ROWS, LOW_COLS, LOW_ROWS);
+    //    testStatus|= test05b(LOW_COLS, LOW_ROWS, LOW_COLS, NUM_ROWS);
+    //    testStatus|= test05b(LOW_COLS, LOW_ROWS, NUM_COLS, LOW_ROWS);
+    //    testStatus|= test05b(LOW_COLS, LOW_ROWS, NUM_COLS, NUM_ROWS);
+
+    //    testStatus|= test05b(LOW_COLS, NUM_ROWS, LOW_COLS, LOW_ROWS);
+    //    testStatus|= test05b(LOW_COLS, NUM_ROWS, LOW_COLS, NUM_ROWS);
+    //    testStatus|= test05b(LOW_COLS, NUM_ROWS, NUM_COLS, LOW_ROWS);
+    //    testStatus|= test05b(LOW_COLS, NUM_ROWS, NUM_COLS, NUM_ROWS);
+
+    //    testStatus|= test05b(NUM_COLS, LOW_ROWS, LOW_COLS, LOW_ROWS);
+    //    testStatus|= test05b(NUM_COLS, LOW_ROWS, LOW_COLS, NUM_ROWS);
+    //    testStatus|= test05b(NUM_COLS, LOW_ROWS, NUM_COLS, LOW_ROWS);
+    //    testStatus|= test05b(NUM_COLS, LOW_ROWS, NUM_COLS, NUM_ROWS);
+
+    //    testStatus|= test05b(NUM_COLS, NUM_ROWS, LOW_COLS, LOW_ROWS);
+    //    testStatus|= test05b(NUM_COLS, NUM_ROWS, LOW_COLS, NUM_ROWS);
+    //    testStatus|= test05b(NUM_COLS, NUM_ROWS, NUM_COLS, LOW_ROWS);
+    testStatus|= test05b(NUM_COLS, NUM_ROWS, NUM_COLS, NUM_ROWS);
+
+
+    return(testStatus);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmSubtractSky.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmSubtractSky.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/detrend/tst_pmSubtractSky.c	(revision 22322)
@@ -0,0 +1,375 @@
+/** @file tst_pmSubtractSky.c
+ *
+ *  @brief Contains the tests for pmSubtractSky.c:
+ *
+ * test00: This code will test the pmSubtractSky routine.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-05-25 22:02:23 $
+ *
+ *  XXX: I added the CELL.TRIMSEC region code but there are not tests for it.
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include "psTest.h"
+#include "pslib.h"
+#include "pmSubtractSky.h"
+#define NUM_ROWS 512
+#define NUM_COLS 512
+#define POLY_X_ORDER 3
+#define POLY_Y_ORDER 3
+#define ERROR_TOLERANCE 1.0
+#define OBJECT_INTENSITY 2000.0
+static int test00(void);
+static int test01(void);
+testDescription tests[] = {
+                              {test00, 000, "pmSubtractSky", 0, false},
+                              {test01, 000, "pmSubtractSky: warning, error messages", 0, false},
+                              {NULL}
+                          };
+
+float func(int i, int j)
+{
+    return((float) (i + j));
+}
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+    //    test00();
+}
+
+/******************************************************************************
+ *****************************************************************************/
+int doSubtractSkySimple(int numCols, int numRows, int binFactor)
+{
+    int i;
+    int j;
+    int testStatus = 0;
+    psImage *tmpImageF32 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    //    pmReadout *myReadout = pmReadoutAlloc(numCols, numRows, tmpImageF32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImageF32;
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psPolynomial2D *myPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, POLY_X_ORDER, POLY_Y_ORDER);
+
+    printPositiveTestHeader(stdout, "pmSubtractSky", "doSubtractSkySimple");
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = func(i, j);
+        }
+    }
+
+    myReadout = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL,
+                              binFactor, myStats, 10.0);
+    if (myReadout == NULL) {
+        printf("TEST ERROR: pmSubtractSky() returned NULL.\n");
+        testStatus = 1;
+    } else {
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                if (ERROR_TOLERANCE < fabs(myReadout->image->data.F32[i][j])) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j, myReadout->image->data.F32[i][j]);
+                    testStatus = 1;
+                }
+            }
+        }
+    }
+    psFree(myReadout);
+    psFree(myStats);
+    psFree(myPoly);
+    printFooter(stdout, "pmSubtractSky", "doSubtractSkySimple", true);
+    return(testStatus);
+}
+
+/******************************************************************************
+ *****************************************************************************/
+int doSubtractSkyWithObjects(int numCols, int numRows, int binFactor)
+{
+    int i;
+    int j;
+    int testStatus = 0;
+    psImage *tmpImageF32 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    //    pmReadout *myReadout = pmReadoutAlloc(numCols, numRows, tmpImageF32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImageF32;
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psPolynomial2D *myPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, POLY_X_ORDER, POLY_Y_ORDER);
+    psImage *trueImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psF32 errorTolerance = ERROR_TOLERANCE * ((psF32) binFactor);
+
+    printPositiveTestHeader(stdout, "pmSubtractSky", "doSubtractSkyWithObjects");
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = func(i, j);
+        }
+    }
+    // We insert a few bright spots in the image.
+    myReadout->image->data.F32[NUM_ROWS/4][NUM_COLS/4]+= OBJECT_INTENSITY;
+    myReadout->image->data.F32[NUM_ROWS/4][3*NUM_COLS/4]+= OBJECT_INTENSITY;
+    myReadout->image->data.F32[3*NUM_ROWS/4][NUM_COLS/4]+= OBJECT_INTENSITY;
+    myReadout->image->data.F32[3*NUM_ROWS/4][3*NUM_COLS/4]+= OBJECT_INTENSITY;
+    PS_IMAGE_SET_F32(trueImage, 0.0);
+    trueImage->data.F32[NUM_ROWS/4][NUM_COLS/4]+= OBJECT_INTENSITY;
+    trueImage->data.F32[NUM_ROWS/4][3*NUM_COLS/4]+= OBJECT_INTENSITY;
+    trueImage->data.F32[3*NUM_ROWS/4][NUM_COLS/4]+= OBJECT_INTENSITY;
+    trueImage->data.F32[3*NUM_ROWS/4][3*NUM_COLS/4]+= OBJECT_INTENSITY;
+
+    myReadout = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL,
+                              binFactor, myStats, 2.0);
+    if (myReadout == NULL) {
+        printf("TEST ERROR: pmSubtractSky() returned NULL.\n");
+        testStatus = 1;
+    } else {
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                if (errorTolerance < fabs(myReadout->image->data.F32[i][j] - trueImage->data.F32[i][j])) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j,
+                           myReadout->image->data.F32[i][j], trueImage->data.F32[i][j]);
+                    testStatus = 1;
+                }
+            }
+        }
+    }
+    psFree(myReadout);
+    psFree(myStats);
+    psFree(myPoly);
+    psFree(trueImage);
+    printFooter(stdout, "pmSubtractSky", "doSubtractSkyWithObjects", true);
+    return(testStatus);
+}
+
+
+int test00( void )
+{
+    int testStatus = 0;
+
+    psTraceSetLevel(".", 0);
+
+    // Bin Factor == 1
+    printf("doSubtractSkySimple(1, 1, 1)\n");
+    testStatus |= doSubtractSkySimple(1, 1, 1);
+    printf("doSubtractSkySimple(NUM_COLS, 1, 1)\n");
+    testStatus |= doSubtractSkySimple(NUM_COLS, 1, 1);
+
+    printf("doSubtractSkySimple(1, NUM_ROWS, 1)\n");
+    testStatus |= doSubtractSkySimple(1, NUM_ROWS, 1);
+    printf("doSubtractSkySimple(NUM_COLS, NUM_ROWS, 1)\n");
+    testStatus |= doSubtractSkySimple(NUM_COLS, NUM_ROWS, 1);
+
+    // Bin Factor == 2
+    printf("doSubtractSkySimple(1, 1, 2)\n");
+    testStatus |= doSubtractSkySimple(1, 1, 2);
+    printf("doSubtractSkySimple(NUM_COLS, 1, 2)\n");
+    testStatus |= doSubtractSkySimple(NUM_COLS, 1, 2);
+    printf("doSubtractSkySimple(1, NUM_ROWS, 2)\n");
+    testStatus |= doSubtractSkySimple(1, NUM_ROWS, 2);
+    printf("doSubtractSkySimple(NUM_COLS, NUM_ROWS, 2)\n");
+    testStatus |= doSubtractSkySimple(NUM_COLS, NUM_ROWS, 2);
+
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 1)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 1);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 2)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 2);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 4)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 8);
+
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 8)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 8);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 16)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 16);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 32)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 32);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 64)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 64);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 128)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 128);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 256)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 256);
+
+    return(testStatus);
+}
+
+#define NUM_ROWS_SMALL 16
+#define NUM_COLS_SMALL 16
+int test01( void )
+{
+    int testStatus = 0;
+    psS32 i;
+    psS32 j;
+    psImage *tmpImageF32 = psImageAlloc(NUM_COLS_SMALL, NUM_ROWS_SMALL, PS_TYPE_F32);
+    psImage *tmpImageF64 = psImageAlloc(NUM_COLS_SMALL, NUM_ROWS_SMALL, PS_TYPE_F64);
+    //    pmReadout *myReadout = pmReadoutAlloc(NUM_COLS_SMALL, NUM_ROWS_SMALL, tmpImageF32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImageF32;
+    pmReadout *rc = NULL;
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psPolynomial2D *myPoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, POLY_X_ORDER, POLY_Y_ORDER);
+
+    printPositiveTestHeader(stdout, "pmSubtractSky", "Testing bad input parameter conditions.");
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            myReadout->image->data.F32[i][j] = func(i, j);
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with NULL pmReadout.  Should error.\n\n");
+    rc = pmSubtractSky(NULL, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmSubtractSky() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with NULL pmReadout->image.  Should error.\n\n");
+    myReadout->image = NULL;
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmSubtractSky() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+    myReadout->image = tmpImageF32;
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with PS_TYPE_F64 pmReadout->image.  Should error.\n\n");
+    myReadout->image = tmpImageF64;
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmSubtractSky() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+    myReadout->image = tmpImageF32;
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with NULL fitSpec.  Should return image, no ERROR, no WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, NULL, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractSky() returned something other than pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with PM_FIT_NONE fit.  Should return image, no ERROR, no WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_NONE, 1, myStats, 2.0);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractSky() returned something other than pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with PM_FIT_SPLINE fit.  Should return image, no ERROR, no WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_SPLINE, 1, myStats, 2.0);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractSky() returned something other than pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with NULL myStats.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, NULL, 2.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    psU64 oldOptions = myStats->options;
+    myStats->options = 0;
+    printf("Calling pmSubtractSky() with no myStats->options specified.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+    myStats->options = oldOptions;
+
+    printf("----------------------------------------------------------------\n");
+    oldOptions = myStats->options;
+    myStats->options = PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN;
+    printf("Calling pmSubtractSky() with multiple myStats->options specified.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+    myStats->options = oldOptions;
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with 0 binFactor.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 0, myStats, 2.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with -1 binFactor.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, -1, myStats, 2.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with -1.0 clipSD.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, -1.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with bogus psFit.  Should generate Error.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, 54321, 1, myStats, -1.0);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractSky() returned something other than pmReadout\n");
+        testStatus = false;
+    }
+
+
+    //    myReadout = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL,
+    //                              1, myStats, 2.0);
+
+
+    printf("----------------------------------------------------------------\n");
+    psFree(myReadout);
+    psFree(myStats);
+    psFree(myPoly);
+    psFree(tmpImageF64);
+    printFooter(stdout, "pmSubtractSky", "Testing bad input parameter conditions.", true);
+    return(testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/extras/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/extras/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/extras/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/psModules/test/extras/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/extras/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/extras/Makefile.am	(revision 22322)
@@ -0,0 +1,25 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSMODULES_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpsmodules.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSMODULES_LIBS)
+
+TEST_PROGS =
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
+	$(top_srcdir)/test/test.pl
Index: /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/.cvsignore	(revision 22322)
@@ -0,0 +1,8 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmImageCombine
+tst_pmReadoutCombine
+tap_pmImageCombine
Index: /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/Makefile.am	(revision 22322)
@@ -0,0 +1,28 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSMODULES_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpsmodules.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSMODULES_LIBS)
+
+TEST_PROGS =
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
+	$(top_srcdir)/test/test.pl
+
+# Removed
+#	tap_pmImageCombine
Index: /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tap_pmImageCombine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tap_pmImageCombine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tap_pmImageCombine.c	(revision 22322)
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+
+#define NUM 10
+#define SIZE 100
+
+psArray *generate_images(void)
+{
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 12345);   // Random Number Generator
+
+    // Generate images
+    psArray *images = psArrayAlloc(NUM);// Array of images
+    for (int i = 0; i < NUM; i++) {
+        psImage *image = psImageAlloc(SIZE, SIZE, PS_TYPE_F32); // Image i
+        for (int y = 0; y < SIZE; y++) {
+            for (int x = 0; x < SIZE; x++) {
+                image->data.F32[y][x] = psRandomGaussian(rng);
+            }
+        }
+        images->data[i] = image;
+    }
+    psFree(rng);
+
+    return images;
+}
+
+int main(int argc, char *argv[])
+{
+    plan_tests(6);
+
+    diag("Image combination tests");
+
+    // Basic combination
+    {
+        psArray *images = generate_images();
+        psImage *combined = pmCombineImages(NULL, NULL, images, NULL, NULL, 0, NULL, 1, 3.0);
+        ok(combined, "Combined image generated");
+        skip_start(!combined, 5, "Combination failed.");
+
+        ok(combined->type.type == PS_TYPE_F32, "Correct type");
+        ok(combined->numCols == SIZE && combined->numRows == SIZE, "Correct size");
+        int discrepant = 0;             // Number of discrepant pixels
+        for (int y = 0; y < SIZE; y++)
+        {
+            for (int x = 0; x < SIZE; x++) {
+                if (fabsf(combined->data.F32[y][x]) > 3.0 / sqrt(NUM)) {
+                    discrepant++;
+                }
+            }
+        }
+        ok(discrepant <= 30, "%d discrepant pixels", discrepant); // Should have 99.7% of 100x100 pixels OK
+
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        psImageStats(stats, combined, NULL, 0);
+        ok(stats->sampleMean < 3.0 / sqrt(NUM * SIZE * SIZE), "Sample mean: %e", stats->sampleMean);
+        ok(fabs(stats->sampleStdev - 1.0 / sqrt(NUM)) < 1.0e-3, "Sample stdev: %e",
+           stats->sampleStdev);
+        psFree(stats);
+
+        skip_end();
+        psFree(combined);
+        psFree(images);
+    }
+
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tst_pmImageCombine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tst_pmImageCombine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tst_pmImageCombine.c	(revision 22322)
@@ -0,0 +1,365 @@
+/** @file tst_pmImageCombine.c
+ *
+ *  @brief Contains the tests for pmImageCombine.c:
+ *
+ *  test00: This code will test the various functions in the pmImageCombine.c file.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  XXX: Must verify the results internally.  Don't use stdout file.
+ *  XXX: Must test masks with pmRejectPixels()
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-03-04 01:01:34 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include "psTest.h"
+#include "pslib.h"
+#include "pmImageCombine.h"
+static int test00(void);
+testDescription tests[] = {
+                              {test00, 000, "pmCombineImages()", true, false},
+                              {NULL}
+                          };
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+psF32 genRanFloat(psF32 low,
+                  psF32 high)
+{
+    psF32 ran1 = (((psF32) (random() % 10000)) / 10000.0);
+    return(low + (ran1 * (high - low)));
+}
+
+psF32 genRanInt(psS32 low,
+                psS32 high)
+{
+    psF32 ran1 = (((psF32) (random() % 10000)) / 10000.0);
+    return((psS32) (low + (ran1 * (high - low))));
+}
+
+#define TST00_EXPANSION_FACTOR_X 1.0
+#define TST00_EXPANSION_FACTOR_Y 1.0
+#define TST00_OFFSET_X 0.0
+#define TST00_OFFSET_Y 0.0
+#define TST00_NUM_PIXELS 10
+#define TST00_MASK_VALUE 1
+#define TST00_NUM_ITERATIONS 4
+#define TST00_SIGMA_CLIP 1.0
+#define TST00_REJECTION_THRESHOLD 0.01
+#define TST00_GRADIENT_LIMIT 10.0
+/*******************************************************************************
+NOTE: This function returns FALSE if there were no errors.
+ ******************************************************************************/
+psBool testCombineImages(psS32 numRows,
+                         psS32 numCols,
+                         psS32 numImages)
+{
+    printf("Testing pmCombineImages(%d, %d, %d)\n", numRows, numCols, numImages);
+    bool testStatus = false;
+
+    psArray *images = psArrayAlloc(numImages);
+    psArray *errors = psArrayAlloc(numImages);
+    psArray *masks = psArrayAlloc(numImages);
+    images->n = images->nalloc;
+    errors->n = errors->nalloc;
+    masks->n = masks->nalloc;
+    for (psS32 i = 0 ; i < numImages ; i++) {
+        images->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        errors->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        masks->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_U8);
+        psImage *image = (psImage *) images->data[i];
+        psImage *error = (psImage *) errors->data[i];
+        psImage *mask = (psImage *) masks->data[i];
+        PS_IMAGE_SET_F32(image, 0.0);
+        PS_IMAGE_SET_F32(error, 1.0);
+        PS_IMAGE_SET_U8(mask, 0);
+
+        for (psS32 row = 0 ; row < numRows ; row++) {
+            for (psS32 col = 0 ; col < numCols ; col++) {
+                // Scale row/col to [-1.0:1.0]
+                psF32 rowScaled = ((psF32) (row - (numRows/2))) / ((psF32) (numRows/2));
+                psF32 colScaled = ((psF32) (col - (numCols/2))) / ((psF32) (numCols/2));
+                image->data.F32[row][col] = PS_SQR((2.0 - rowScaled) + (2.0 - colScaled)) + genRanFloat(0.0, 0.5);
+            }
+        }
+    }
+
+    //
+    // Same as above except the numImages is wrong
+    //
+    psArray *imagesLong = psArrayAlloc(numImages+1);
+    psArray *errorsLong = psArrayAlloc(numImages+1);
+    psArray *masksLong = psArrayAlloc(numImages+1);
+    imagesLong->n = imagesLong->nalloc;
+    errorsLong->n = errorsLong->nalloc;
+    masksLong->n = masksLong->nalloc;
+    for (psS32 i = 0 ; i < numImages+1 ; i++) {
+        imagesLong->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        errorsLong->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        masksLong->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_U8);
+        psImage *image = (psImage *) imagesLong->data[i];
+        psImage *error = (psImage *) errorsLong->data[i];
+        psImage *mask = (psImage *) masksLong->data[i];
+        PS_IMAGE_SET_F32(image, 0.0);
+        PS_IMAGE_SET_F32(error, 1.0);
+        PS_IMAGE_SET_U8(mask, 0);
+
+        for (psS32 row = 0 ; row < numRows ; row++) {
+            for (psS32 col = 0 ; col < numCols ; col++) {
+                // Scale row/col to [-1.0:1.0]
+                psF32 rowScaled = ((psF32) (row - (numRows/2))) / ((psF32) (numRows/2));
+                psF32 colScaled = ((psF32) (col - (numCols/2))) / ((psF32) (numCols/2));
+                image->data.F32[row][col] = PS_SQR((2.0 - rowScaled) + (2.0 - colScaled)) + genRanFloat(0.0, 0.5);
+            }
+        }
+    }
+
+    //
+    // Same as above except the type is wrong
+    //
+    psArray *imagesBadType = psArrayAlloc(numImages);
+    psArray *errorsBadType = psArrayAlloc(numImages);
+    psArray *masksBadType = psArrayAlloc(numImages);
+    imagesBadType->n = imagesBadType->nalloc;
+    errorsBadType->n = errorsBadType->nalloc;
+    masksBadType->n = masksBadType->nalloc;
+    for (psS32 i = 0 ; i < numImages ; i++) {
+        imagesBadType->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F64);
+        errorsBadType->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F64);
+        masksBadType->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_S8);
+        psImage *image = (psImage *) imagesBadType->data[i];
+        psImage *error = (psImage *) errorsBadType->data[i];
+        psImage *mask = (psImage *) masksBadType    ->data[i];
+        PS_IMAGE_SET_F32(image, 0.0);
+        PS_IMAGE_SET_F32(error, 1.0);
+        PS_IMAGE_SET_U8(mask, 0);
+
+        for (psS32 row = 0 ; row < numRows ; row++) {
+            for (psS32 col = 0 ; col < numCols ; col++) {
+                // Scale row/col to [-1.0:1.0]
+                psF32 rowScaled = ((psF32) (row - (numRows/2))) / ((psF32) (numRows/2));
+                psF32 colScaled = ((psF32) (col - (numCols/2))) / ((psF32) (numCols/2));
+                image->data.F32[row][col] = PS_SQR((2.0 - rowScaled) + (2.0 - colScaled)) + genRanFloat(0.0, 0.5);
+            }
+        }
+    }
+
+    psPixels *pixels = psPixelsAlloc(TST00_NUM_PIXELS);
+    pixels->n = pixels->nalloc;
+    for (psS32 p = 0 ; p < TST00_NUM_PIXELS ; p++) {
+        psS32 col =  genRanInt(0, numCols);
+        psS32 row =  genRanInt(0, numRows);
+        pixels->data[p].x = (psF32) col;
+        pixels->data[p].y = (psF32) row;
+        psS32 im = genRanInt(0, numImages-1);
+        psImage *image = (psImage *) images->data[im];
+        image->data.F32[row][col] += 100.0;
+        printf("Generating a bad pixel in image (%d) at (%d, %d)\n", im, row, col);
+    }
+
+    psArray *questionablePixels = NULL;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a NULL images.  Should generate error, return NULL.\n");
+    psImage *outImg = pmCombineImages(NULL, &questionablePixels, NULL, errors,
+                                      masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                                      TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a long images.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, imagesLong, errors,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a bad type images.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, imagesBadType, errors,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a long errors.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errorsLong,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a bad type errors.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errorsBadType,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a long masks.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errors,
+                             masksLong, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a bad type masks.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errors,
+                             masksBadType, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a NULL stats.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errors,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, NULL);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with acceptable data.  Should generate a psImage.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errors,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg == NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a NULL psImage.\n");
+        testStatus = true;
+    }
+    if (0) {
+        for (psS32 p = 0 ; p < TST00_NUM_PIXELS ; p++) {
+            psS32 col = (psS32) (pixels->data[p]).x;
+            psS32 row = (psS32) (pixels->data[p]).y;
+            printf("------------------------------------------\n");
+            printf("Pixel (%d, %d) in combined image is %f\n", row, col, outImg->data.F32[row][col]);
+            for (psS32 i = 0 ; i < numImages ; i++) {
+                psImage *image = (psImage *) images->data[i];
+                printf("Pixel (after combine) (%d, %d) in image (%d) is %f\n", row, col, i, image->data.F32[row][col]);
+            }
+        }
+    }
+
+    if (questionablePixels->n != numImages) {
+        printf("TEST ERROR: pmCombineImages(): questionablePixels->n was %ld, should have been %d\n",
+               questionablePixels->n, numImages);
+        testStatus = true;
+    } else {
+        // XXX: We should internally verify this with the pixels list, not merely use the stdout.
+        for (psS32 i = 0 ; i < questionablePixels->n ; i++) {
+            psPixels *myPixels = (psPixels *) questionablePixels->data[i];
+            for (psS32 p = 0 ; p < myPixels->n ; p++) {
+                printf("Image %d, questionable pixel %d is (%f %f)\n",
+                       i, p, myPixels->data[p].y, myPixels->data[p].x);
+            }
+        }
+    }
+
+    psArray *expandTransforms = psArrayAlloc(numImages);
+    psArray *contractTransforms = psArrayAlloc(numImages);
+    expandTransforms->n = expandTransforms->nalloc;
+    contractTransforms->n = contractTransforms->nalloc;
+    for (psS32 im = 0 ; im < numImages ; im++) {
+        psPlaneTransform *ptExpand = psPlaneTransformAlloc(2, 2);
+        ptExpand->x->coeff[0][0] = TST00_OFFSET_X;
+        ptExpand->x->coeff[1][0] = TST00_EXPANSION_FACTOR_X;
+        ptExpand->y->coeff[0][0] = TST00_OFFSET_Y;
+        ptExpand->y->coeff[0][1] = TST00_EXPANSION_FACTOR_Y;
+        expandTransforms->data[im] = (psPtr *) ptExpand;
+
+        psPlaneTransform *ptContract = psPlaneTransformAlloc(2, 2);
+        ptContract->x->coeff[0][0] = -TST00_OFFSET_X;
+        ptContract->x->coeff[1][0] = 1.0 / TST00_EXPANSION_FACTOR_X;
+        ptContract->y->coeff[0][0] = -TST00_OFFSET_Y;
+        ptContract->y->coeff[0][1] = 1.0 / TST00_EXPANSION_FACTOR_Y;
+        contractTransforms->data[im] = (psPtr *) ptContract;
+    }
+
+    //-------------------------------------------------------------------------
+    //
+    // XXX: psRejectPixels() has known bugs.  Specifically, in the psImageTransform() call.
+    // We exclude this from our tests.
+    //
+    printf("\n\n\nCalling pmRejectPixels() with acceptable data.  Should generate a psArray.\n");
+    psArray *pixelRejects = pmRejectPixels(images, NULL, questionablePixels, expandTransforms,
+                                           contractTransforms, TST00_REJECTION_THRESHOLD,
+                                           TST00_GRADIENT_LIMIT);
+    if (pixelRejects == NULL) {
+        printf("TEST ERROR: pmRejectPixels() returned a NULL psArray.\n");
+        testStatus = true;
+    } else {
+        // XXX: We should internally verify this with the pixels list, not merely use the stdout.
+        for (psS32 i = 0 ; i < pixelRejects->n ; i++) {
+            psPixels *myPixels = (psPixels *) pixelRejects->data[i];
+            printf("tst_pmImageCombine.c: Image %d had %ld rejects.\n", i, myPixels->n);
+
+            for (psS32 p = 0 ; p < myPixels->n ; p++) {
+                printf("Image %d, rejected pixel %d is (%f %f)\n", i, p,
+                       myPixels->data[p].y, myPixels->data[p].x);
+            }
+        }
+        psFree(pixelRejects);
+    }
+
+    psFree(images);
+    psFree(errors);
+    psFree(masks);
+    psFree(imagesLong);
+    psFree(errorsLong);
+    psFree(masksLong);
+    psFree(imagesBadType);
+    psFree(errorsBadType);
+    psFree(masksBadType);
+    psFree(pixels);
+    psFree(stats);
+
+    return(testStatus);
+}
+
+/*******************************************************************************
+NOTE: This function returns TRUE if there were no errors.
+ ******************************************************************************/
+int test00( void )
+{
+    bool testStatus = false;
+
+    testStatus|= testCombineImages(10, 10, 5);
+
+    return(!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tst_pmImageSubtract.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tst_pmImageSubtract.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tst_pmImageSubtract.c	(revision 22322)
@@ -0,0 +1,847 @@
+/** @file tst_pmImageSubtract.c
+ *
+ *  @brief Contains the tests for pmImageSubtract.c:
+ *
+ *  test00: This code will test the various functions in pmObjects.c
+ *
+ *  @author GLG, MHPCC
+ *
+ *  XXX: Most test simply ensure that the functions can be called with allowable
+ *  data.  More work need to be done to verify the results.
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-05-25 22:02:46 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include "psTest.h"
+#include "pslib.h"
+#include "pmImageSubtract.h"
+#define ERROR_TOLERANCE 1.0
+static int test00(void);
+static int test01(void);
+static int test02(void);
+static int test03(void);
+testDescription tests[] = {
+                              {test00, 000, "pmSubtractionKernelsAllocPOIS()", true, false},
+                              {test01, 000, "pmSubtractionKernelsAllocISIS()", true, false},
+                              {test02, 000, "pmSubtractionFindStamps()", true, false},
+                              {test03, 000, "pmSubtractionCalculateEquation()", true, false},
+                              {NULL}
+                          };
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+
+/*******************************************************************************
+NOTE: This function returns FALSE if there were no errors.
+ 
+XXX: Call with various unallowable input parameters.
+ 
+XXX: Untested: we don't loop through the (u, v, xOrder, yOrder) psVectors and
+ensure that each value is set correctly.
+ ******************************************************************************/
+psBool testPOISAlloc(psS32 size,
+                     psS32 SpatialOrder)
+{
+    printf("Testing pmSubtractionKernelsAllocPOIS(%d, %d)\n", size, SpatialOrder);
+
+    bool testStatus = false;
+    psS32 nBasisFunctions = (2 * size + 1) * (2 * size + 1) * (SpatialOrder + 1) * (SpatialOrder + 2) / 2;
+
+    psSubtractionKernels *kernels = pmSubtractionKernelsAllocPOIS(size, SpatialOrder);
+    if (kernels == NULL) {
+        printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() returned a NULL psSubtractionKernels.\n");
+        testStatus = true;
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated the wrong kernels->type.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->u == NULL) ||
+                (kernels->u->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a NULL ->u member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a incorrect length ->u member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->v == NULL) ||
+                (kernels->v->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a NULL ->v member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a incorrect length ->v member.\n");
+            testStatus = true;
+        }
+
+        if (kernels->sigma != NULL) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a non-NULL ->sigma member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->xOrder == NULL) ||
+                (kernels->xOrder->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a NULL ->xOrder member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a incorrect length ->xOrder member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->yOrder == NULL) ||
+                (kernels->yOrder->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a NULL ->yOrder member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a incorrect length ->yOrder member.\n");
+            testStatus = true;
+        }
+
+        if (kernels->subIndex != 0) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a non-zero ->subIndex member (%d).\n", kernels->subIndex);
+            testStatus = true;
+        }
+
+        psS32 i = kernels->subIndex;
+        if ((kernels->u->data.F32[i] != 0) ||
+                (kernels->v->data.F32[i] != 0)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS(): the ->subIndex member points to a kernel with (%f, %f) (u, v) basis function.\n",
+                   kernels->u->data.F32[i], kernels->v->data.F32[i]);
+            testStatus = true;
+        }
+
+        if (kernels->preCalc != NULL) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a non-NULL ->preCalc member.\n");
+            testStatus = true;
+        }
+
+        if (kernels->size != size) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated an incorrect ->size member (%d).\n", kernels->size);
+            testStatus = true;
+        }
+
+        if (kernels->spatialOrder != SpatialOrder) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated an incorrect ->spatialOrder member (%d).\n", kernels->spatialOrder);
+            testStatus = true;
+        }
+    }
+    psFree(kernels);
+    return(testStatus);
+}
+
+/*******************************************************************************
+NOTE: This function returns TRUE if there were no errors.
+ ******************************************************************************/
+int test00( void )
+{
+    bool testStatus = false;
+
+    testStatus|= testPOISAlloc(1, 1);
+    testStatus|= testPOISAlloc(2, 3);
+    testStatus|= testPOISAlloc(3, 4);
+
+    return(!testStatus);
+}
+
+/*******************************************************************************
+NOTE: This function returns FALSE if there were no errors.
+ 
+XXX: Call with various unallowable input parameters.
+ 
+XXX: Untested: we don't loop through the (u, v, xOrder, yOrder) psVectors and
+ensure that each value is set correctly.  We don't ensure that he preCalc
+psImages are set correctly.
+ ******************************************************************************/
+psBool testISISAlloc(psS32 sigmaLength,
+                     psS32 orderLength,
+                     psS32 size,
+                     psS32 SpatialOrder)
+{
+    printf("Testing pmSubtractionKernelsAllocISIS(%d, %d, %d, %d)\n",
+           sigmaLength, orderLength, size, SpatialOrder);
+
+    psVector *sigmas = psVectorAlloc(sigmaLength, PS_TYPE_F32);
+    sigmas->n = sigmas->nalloc;
+    for (psS32 i = 0 ; i < sigmas->n ; i++) {
+        sigmas->data.F32[i] = 1.0 + (psF32) i;
+    }
+    psVector *orders = psVectorAlloc(orderLength, PS_TYPE_S32);
+    orders->n = orders->nalloc;
+    for (psS32 i = 0 ; i < orders->n ; i++) {
+        orders->data.S32[i] = i + 2;
+    }
+
+    bool testStatus = false;
+    psS32 numSigmas = sigmas->n;
+    psS32 nBasisFunctions = 0;
+    for (psS32 s = 0 ; s < numSigmas ; s++) {
+        for (psS32 o = 0 ; o < orders->n ; o++) {
+            nBasisFunctions+= ((orders->data.S32[o] + 1) * (orders->data.S32[o] + 2) / 2);
+        }
+    }
+    nBasisFunctions*= ((SpatialOrder + 1) * (SpatialOrder + 2) / 2);
+
+    psSubtractionKernels *kernels = pmSubtractionKernelsAllocISIS(sigmas, orders, size, SpatialOrder);
+    if (kernels == NULL) {
+        printf("TEST ERROR: pmSubtractionKernelsAllocISIS() returned a NULL psSubtractionKernels.\n");
+        testStatus = true;
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_ISIS) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated the wrong kernels->type.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->u == NULL) ||
+                (kernels->u->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->u member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->u member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->v == NULL) ||
+                (kernels->v->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->v member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->v member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->sigma == NULL) ||
+                (kernels->sigma->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->sigma member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->sigma member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->xOrder == NULL) ||
+                (kernels->xOrder->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->xOrder member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->xOrder member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->yOrder == NULL) ||
+                (kernels->yOrder->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->yOrder member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->yOrder member.\n");
+            testStatus = true;
+        }
+
+        if (kernels->subIndex != 0) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a non-zero ->subIndex member (%d).\n", kernels->subIndex);
+            testStatus = true;
+        }
+
+        //
+        // Ensure that kernels->subIndex points to the correct kernel.
+        //
+        psS32 i = kernels->subIndex;
+        if ((kernels->u->data.F32[i] != 0.0) ||
+                (kernels->v->data.F32[i] != 0.0) ||
+                (kernels->xOrder->data.F32[i] != 0.0) ||
+                (kernels->yOrder->data.F32[i] != 0.0) ||
+                (kernels->sigma->data.F32[i] != sigmas->data.F32[0])) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS(): the ->subIndex member points to the wrong kernel.\n");
+            printf("TEST ERROR: (u, v, xOrder, yOrder, sigma) is (%f, %f, %f, %f, %f).\n",
+                   kernels->u->data.F32[i], kernels->v->data.F32[i],
+                   kernels->xOrder->data.F32[i], kernels->yOrder->data.F32[i],
+                   kernels->sigma->data.F32[i]);
+            testStatus = true;
+        }
+
+        //
+        // Ensure that the preCalc images are allocated correctly.
+        //
+        if ((kernels->preCalc == NULL) ||
+                (kernels->preCalc->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->preCalc member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->preCalc member.\n");
+            testStatus = true;
+        } else {
+            for (psS32 k = 0 ; k < kernels->u->n ; k++) {
+                psImage *kerImg = (psImage *) kernels->preCalc->data[k];
+                if (kerImg == NULL) {
+                    printf("TEST ERROR: the %d-th kernel preCalc image is NULL.\n", k);
+                    testStatus = true;
+                } else {
+                    if (kerImg->type.type != PS_TYPE_F32) {
+                        printf("TEST ERROR: preCalc image %d had ioncorrect type.\n", k);
+                        testStatus = true;
+                    }
+                    if ((kerImg->numRows != (1 + (2 * size))) ||
+                            (kerImg->numCols != (1 + (2 * size)))) {
+                        printf("TEST ERROR: preCalc image %d had incorrect size (%d, %d).\n", k,
+                               kerImg->numRows, kerImg->numCols);
+                        testStatus = true;
+                    }
+                }
+            }
+        }
+
+        if (kernels->size != size) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated an incorrect ->size member (%d).\n", kernels->size);
+            testStatus = true;
+        }
+
+        if (kernels->spatialOrder != SpatialOrder) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated an incorrect ->spatialOrder member (%d).\n", kernels->spatialOrder);
+            testStatus = true;
+        }
+    }
+    psFree(sigmas);
+
+    psFree(kernels->u);
+    psFree(kernels->v);
+    psFree(kernels->sigma);
+    psFree(kernels->xOrder);
+    psFree(kernels->yOrder);
+    psFree(kernels->preCalc);
+    psFree(kernels);
+    psFree(orders);
+    return(testStatus);
+}
+
+/*******************************************************************************
+NOTE: This function returns TRUE if there were no errors.
+ ******************************************************************************/
+int test01( void )
+{
+    bool testStatus = false;
+
+    /*
+        testStatus|= testISISAlloc(1, 1, 1, 1);
+        testStatus|= testISISAlloc(2, 2, 2, 2);
+        testStatus|= testISISAlloc(2, 3, 4, 5);
+        testStatus|= testISISAlloc(3, 4, 5, 6);
+    */
+
+    return(!testStatus);
+}
+
+
+/*******************************************************************************
+NOTE: This function returns FALSE if there were no errors.
+ 
+XXX: Can we test to ensure that no stamps overlap?
+ 
+XXX: Test stamp alloc/dealloc functions.
+ ******************************************************************************/
+#define TST02_THRESHOLD 3.0
+#define TST02_MASK_VAL 1
+psBool testFindStamps(psS32 numCols,
+                      psS32 numRows,
+                      psS32 xNum,
+                      psS32 yNum,
+                      psS32 border)
+{
+    printf("Testing pmSubtractionFindStamps(%d, %d, %d, %d, %d)\n",
+           numCols, numRows, xNum, yNum, border);
+    bool testStatus = false;
+
+    // Create a test image and set a single pixel in the center of each stamp.
+    psImage *tstImg = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    int numStamps = 0;
+    PS_IMAGE_SET_F32(tstImg, 0.0);
+    for (psS32 j = 0; j < yNum; j++) {
+        for (psS32 i = 0; i < xNum; i++) {
+            psS32 yMin = border + j * (numRows - 2.0 * border) / yNum;
+            psS32 yMax = PS_MIN(numRows-1, (border + (j + 1) * (numRows - 2.0 * border) / yNum) - 1);
+            psS32 xMin = border + i * (numCols - 2.0 * border) / xNum;
+            psS32 xMax = PS_MIN(numCols-1, (border + (i + 1) * (numCols - 2.0 * border) / xNum) - 1);
+
+            tstImg->data.F32[(yMax+yMin)/2][(xMax+xMin)/2] = TST02_THRESHOLD + (psF32) (i + j);
+            numStamps++;
+        }
+    }
+    psImage *tmpMask= psImageAlloc(numCols, numRows, PS_TYPE_U8);
+    PS_IMAGE_SET_U8(tstImg, 0);
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a NULL psImage.  Should generate error, return NULL.\n");
+    psArray *stamps = pmSubtractionFindStamps(NULL, NULL, tmpMask, TST02_MASK_VAL,
+                      TST02_THRESHOLD, xNum, yNum,
+                      border);
+    if (stamps != NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        psFree(stamps);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a non-positive xNum.  Should generate error, return NULL.\n");
+    stamps = pmSubtractionFindStamps(NULL, tstImg, tmpMask, TST02_MASK_VAL,
+                                     TST02_THRESHOLD, 0, yNum,
+                                     border);
+    if (stamps != NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        psFree(stamps);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a non-positive yNum.  Should generate error, return NULL.\n");
+    stamps = pmSubtractionFindStamps(NULL, tstImg, tmpMask, TST02_MASK_VAL,
+                                     TST02_THRESHOLD, xNum, 0,
+                                     border);
+    if (stamps != NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        psFree(stamps);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a non-positive border.  Should generate error, return NULL.\n");
+    stamps = pmSubtractionFindStamps(NULL, tstImg, tmpMask, TST02_MASK_VAL,
+                                     TST02_THRESHOLD, xNum, yNum,
+                                     0);
+    if (stamps != NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        psFree(stamps);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with acceptable input parameters, non-NULL mask.\n");
+    stamps = pmSubtractionFindStamps(NULL, tstImg, tmpMask, TST02_MASK_VAL,
+                                     TST02_THRESHOLD, xNum, yNum,
+                                     border);
+    if (stamps == NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        testStatus = true;
+    } else {
+        if (stamps->n != numStamps) {
+            printf("TEST ERROR: %ld stamps were found, %d were expected.\n",
+                   stamps->n, numStamps);
+            testStatus = true;
+        }
+        for (psS32 s = 0 ; s < stamps->n ; s++) {
+            pmStamp *stamp = (pmStamp *) stamps->data[s];
+            psS32 row = stamp->y;
+            psS32 col = stamp->x;
+            // printf("Stamp %d at (%d, %d) has a value of %f\n", s, row, col, tstImg->data.F32[row][col]);
+            if (tstImg->data.F32[row][col] < TST02_THRESHOLD) {
+                if (stamp->status != PM_STAMP_NONE) {
+                    printf("TEST ERROR: stamp %d had peak value %f (below theshold) and the status was not set to PM_STAMP_NONE.\n",
+                           s, tstImg->data.F32[row][col]);
+                    testStatus = true;
+                }
+            } else {
+                if (stamp->status != PM_STAMP_RECALC) {
+                    printf("TEST ERROR: stamp %d had peak value %f (above theshold) and the status was not set to PM_STAMP_RECALC.\n",
+                           s, tstImg->data.F32[row][col]);
+                    testStatus = true;
+                }
+            }
+        }
+    }
+    psFree(stamps);
+
+    //-------------------------------------------------------------------------
+    printf("Calling with acceptable input parameters, NULL mask.\n");
+    stamps = pmSubtractionFindStamps(NULL, tstImg, NULL, TST02_MASK_VAL,
+                                     TST02_THRESHOLD, xNum, yNum,
+                                     border);
+    if (stamps == NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        testStatus = true;
+    } else {
+        if (stamps->n != numStamps) {
+            printf("TEST ERROR: %ld stamps were found, %d were expected.\n",
+                   stamps->n, numStamps);
+            testStatus = true;
+        }
+        for (psS32 s = 0 ; s < stamps->n ; s++) {
+            pmStamp *stamp = (pmStamp *) stamps->data[s];
+            psS32 row = stamp->y;
+            psS32 col = stamp->x;
+            //printf("Stamp %d at (%d, %d) has a value of %f\n", s, row, col, tstImg->data.F32[row][col]);
+            if (tstImg->data.F32[row][col] < TST02_THRESHOLD) {
+                if (stamp->status != PM_STAMP_NONE) {
+                    printf("TEST ERROR: stamp %d had peak value %f (below theshold) and the status was not set to PM_STAMP_NONE.\n",
+                           s, tstImg->data.F32[row][col]);
+                    testStatus = true;
+                }
+            } else {
+                if (stamp->status != PM_STAMP_RECALC) {
+                    printf("TEST ERROR: stamp %d had peak value %f (above theshold) and the status was not set to PM_STAMP_RECALC.\n",
+                           s, tstImg->data.F32[row][col]);
+                    testStatus = true;
+                }
+            }
+        }
+    }
+
+    psFree(tstImg);
+    psFree(tmpMask);
+    psFree(stamps);
+
+    return(testStatus);
+}
+
+
+
+/*******************************************************************************
+NOTE: This function returns TRUE if there were no errors.
+ ******************************************************************************/
+int test02( void )
+{
+    bool testStatus = false;
+
+    testStatus|= testFindStamps(100, 100, 2, 2, 2);
+    testStatus|= testFindStamps(100, 100, 10, 10, 2);
+
+    return(!testStatus);
+}
+
+
+psF32 genRanFloat(psF32 low,
+                  psF32 high)
+{
+    psF32 ran1 = (((psF32) (random() % 10000)) / 10000.0);
+    return(low + (ran1 * (high - low)));
+}
+
+//
+// XXX: POIS kernels are producing NANs if the image size is 20 or less.
+//
+// XXX: ISIS kernels are producing NANS if the TST03_ORDER_LENGTH is 2 or larger.
+//
+#define TST03_THRESHOLD  3.0
+#define TST03_MASK_VAL  1
+#define TST03_KERNEL_SIZE 2
+#define TST03_SPATIAL_ORDER 2
+#define TST03_ORDER_LENGTH 1
+#define TST03_SIGMA_LENGTH 1
+#define TST03_PSF_MAX  10.0
+#define TST03_BG 0.0
+#define TST03_IMAGE_SIZE 25
+#define TST03_NUM_COLS  TST03_IMAGE_SIZE
+#define TST03_NUM_ROWS  TST03_IMAGE_SIZE
+#define TST03_NUM_STAMPS 2
+#define TST03_NUM_STAMPS_COLS TST03_NUM_STAMPS
+#define TST03_NUM_STAMPS_ROWS TST03_NUM_STAMPS
+#define TST03_BORDER  TST03_KERNEL_SIZE
+//#define TST03_FOOTPRINT (((TST03_IMAGE_SIZE - (2 * TST03_BORDER)) / TST03_NUM_STAMPS) - TST03_KERNEL_SIZE)
+#define TST03_FOOTPRINT  4
+#define TST03_PSF_WIDTH  (TST03_FOOTPRINT/2 - 1)
+
+/*******************************************************************************
+This routine generates an object in the center of the stamp defined by the
+(xMin, xMax) and (yMin, yMax) boundary.
+ ******************************************************************************/
+psBool genObject(psImage *tstImg,
+                 psImage *refImg,
+                 psS32 xMin,
+                 psS32 xMax,
+                 psS32 yMin,
+                 psS32 yMax)
+{
+    if (((1 + 2 * TST03_PSF_WIDTH) > (xMax - xMin)) ||
+            ((1 + 2 * TST03_PSF_WIDTH) > (yMax - yMin))) {
+        printf("INCORRECT TEST CONFIGURATION: TST03_PSF_WIDTH is too big.\n");
+        printf("TST03_PSF_WIDTH is %d: (xMin - xMax) is %d\n", TST03_PSF_WIDTH, (xMax - xMin));
+        return(FALSE);
+    }
+    //
+    // This code basically creates a peak at the center of the stamp with
+    // a height of TST03_PSF_MAX
+    //
+    psS32 xCenter = (xMax + xMin) / 2;
+    psS32 yCenter = (yMax + yMin) / 2;
+    psF32 subImageWidth = 1.0 + sqrtf(PS_SQR(((psF32) ((yMax-yMin)/2))) + PS_SQR(((psF32) ((xMax-xMin)/2))));
+    for (psS32 y = yMin ; y <= yMax ; y++) {
+        for (psS32 x = xMin ; x <= xMax ; x++) {
+            psF32 dist = sqrtf(PS_SQR((psF32) (y - yCenter)) + PS_SQR((psF32) (x - xCenter)));
+            psF32 pixel = TST03_PSF_MAX * PS_SQR(((psF32) (subImageWidth - dist)) / ((psF32) subImageWidth));
+            if (pixel < 0.0) {
+                pixel = 0.0;
+            }
+            refImg->data.F32[y][x] = pixel + TST03_BG;
+            tstImg->data.F32[y][x] = pixel + TST03_BG;
+            // Add some noise for the test image.
+            tstImg->data.F32[y][x]+= genRanFloat(0.0, 1.0);
+        }
+    }
+
+    return(TRUE);
+}
+
+/*******************************************************************************
+NOTE: This function returns FALSE if there were no errors.
+ 
+XXX: We should use a larger variety of input parameter configurations.
+ 
+I test the following functions here (since they linearly rely on a set of data
+structures that the previoues ones generate):
+    pmSubtractionCalculateEquation()
+    pmSubtractionSolveEquation()
+    pmSubtractionRejectStamps()
+    pmSubtractionKernelImage()
+ ******************************************************************************/
+psBool testSubCalcEqu(psS32 numCols,
+                      psS32 numRows,
+                      psS32 xNum,
+                      psS32 yNum,
+                      psS32 border,
+                      pmSubtractionKernelsType KernelType)
+{
+    printf("Testing pmSubtractionCalculateEquation(): \n");
+    printf("    image size is (%d, %d)\n", numRows, numCols);
+    printf("    num stamps is (%d, %d).  Border is %d\n", yNum, xNum, border);
+    if (KernelType == PM_SUBTRACTION_KERNEL_POIS) {
+        printf("   kernel type is PM_SUBTRACTION_KERNEL_POIS.\n");
+    } else if (KernelType == PM_SUBTRACTION_KERNEL_ISIS) {
+        printf("   kernel type is PM_SUBTRACTION_KERNEL_ISIS.\n");
+    }
+    bool testStatus = false;
+
+    // Create a test image and set a single pixel in the center of each stamp.
+    psImage *tstImg = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *refImg = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *maskImg = psImageAlloc(numCols, numRows, PS_TYPE_U8);
+    PS_IMAGE_SET_F32(tstImg, 0.0);
+    PS_IMAGE_SET_F32(maskImg, 0);
+    for (psS32 j = 0; j < yNum; j++) {
+        for (psS32 i = 0; i < xNum; i++) {
+            psS32 yMin = border + j * (numRows - 2.0 * border) / yNum;
+            psS32 yMax = PS_MIN(numRows-1, (border + (j + 1) * (numRows - 2.0 * border) / yNum) - 1);
+            psS32 xMin = border + i * (numCols - 2.0 * border) / xNum;
+            psS32 xMax = PS_MIN(numCols-1, (border + (i + 1) * (numCols - 2.0 * border) / xNum) - 1);
+
+            genObject(tstImg, refImg, xMin, xMax, yMin, yMax);
+        }
+    }
+    printf("Generating stamps...\n");
+    psArray *stamps = pmSubtractionFindStamps(NULL, tstImg, NULL, TST03_MASK_VAL,
+                      TST03_THRESHOLD, xNum, yNum,
+                      border);
+
+    //
+    // PsVectors sigmas and orders are for ISIS kernels only.
+    //
+    psVector *sigmas = psVectorAlloc(TST03_SIGMA_LENGTH, PS_TYPE_F32);
+    sigmas->n = sigmas->nalloc;
+    for (psS32 i = 0 ; i < sigmas->n ; i++) {
+        sigmas->data.F32[i] = 1.0 + (psF32) i;
+    }
+    psVector *orders = psVectorAlloc(TST03_ORDER_LENGTH, PS_TYPE_S32);
+    orders->n = orders->nalloc;
+    for (psS32 i = 0 ; i < orders->n ; i++) {
+        orders->data.S32[i] = i + 2;
+    }
+
+    //
+    // Create the subtraction kernels
+    //
+    printf("Generating kernel basis functions...\n");
+    psSubtractionKernels *myKernels = NULL;
+    if (KernelType == PM_SUBTRACTION_KERNEL_POIS) {
+        myKernels = pmSubtractionKernelsAllocPOIS(TST03_KERNEL_SIZE, TST03_SPATIAL_ORDER);
+    } else if (KernelType == PM_SUBTRACTION_KERNEL_ISIS) {
+        myKernels = pmSubtractionKernelsAllocISIS(sigmas, orders, TST03_KERNEL_SIZE, TST03_SPATIAL_ORDER);
+    }
+
+    if ((stamps == NULL) ||
+            (myKernels == NULL)) {
+        printf("TEST ERROR: stamps or myKernels is NULL.\n");
+        testStatus = true;
+    } else {
+        //-------------------------------------------------------------------------
+        printf("Calling with a NULL psArray stamps.  Should generate error, return FALSE.\n");
+        psBool rc = pmSubtractionCalculateEquation(NULL, refImg, tstImg, myKernels, TST03_FOOTPRINT);
+        if (rc == TRUE) {
+            printf("TEST ERROR: pmSubtractionCalculateEquation() returned TRUE.\n");
+            testStatus = true;
+        }
+
+        //-------------------------------------------------------------------------
+        printf("Calling with a NULL reference images.  Should generate error, return FALSE.\n");
+        rc = pmSubtractionCalculateEquation(stamps, NULL, tstImg, myKernels, TST03_FOOTPRINT);
+        if (rc == TRUE) {
+            printf("TEST ERROR: pmSubtractionCalculateEquation() returned TRUE.\n");
+            testStatus = true;
+        }
+
+        //-------------------------------------------------------------------------
+        printf("Calling with a NULL input images.  Should generate error, return FALSE.\n");
+        rc = pmSubtractionCalculateEquation(stamps, refImg, NULL, myKernels, TST03_FOOTPRINT);
+        if (rc == TRUE) {
+            printf("TEST ERROR: pmSubtractionCalculateEquation() returned TRUE.\n");
+            testStatus = true;
+        }
+
+        //-------------------------------------------------------------------------
+        printf("Calling with a NULL kernel basis functions.  Should generate error, return FALSE.\n");
+        rc = pmSubtractionCalculateEquation(stamps, refImg, tstImg, NULL, TST03_FOOTPRINT);
+        if (rc == TRUE) {
+            printf("TEST ERROR: pmSubtractionCalculateEquation() returned TRUE.\n");
+            testStatus = true;
+        }
+
+        //-------------------------------------------------------------------------
+        printf("Calling with acceptable input parameters.  Should return TRUE.\n");
+
+        rc = pmSubtractionCalculateEquation(stamps, refImg, tstImg, myKernels, TST03_FOOTPRINT);
+        if (rc != TRUE) {
+            printf("TEST ERROR: pmSubtractionCalculateEquation() returned FALSE.\n");
+            testStatus = true;
+        } else {
+
+            if (0) {
+                for (psS32 s = 0 ; s < stamps->n ; s++) {
+                    printf("********************************* Stamp %d *********************************\n", s);
+                    pmStamp *stamp = (pmStamp *) stamps->data[s];
+                    if (stamp->vector != NULL) {
+                        PS_VECTOR_PRINT_F64(stamp->vector);
+                    }
+                    if (stamp->matrix != NULL) {
+                        printf("Stamp matrix size is (%d, %d)\n", stamp->matrix->numRows, stamp->matrix->numCols);
+                        PS_IMAGE_PRINT_F64(stamp->matrix);
+                    }
+                }
+            }
+
+
+            //-------------------------------------------------------------------------
+            printf("Calling pmSubtractionSolveEquation() with a NULL stamp argument.  Should generate error, return FALSE.\n");
+            psVector *solution = pmSubtractionSolveEquation(NULL, NULL);
+            if (solution != NULL) {
+                printf("TEST ERROR: pmSubtractionSolveEquation() returned non-NULL.\n");
+                testStatus = true;
+            }
+
+            //-------------------------------------------------------------------------
+            printf("Calling pmSubtractionSolveEquation() with acceptable input parameters.  Should return non-NULL.\n");
+            solution = pmSubtractionSolveEquation(NULL, stamps);
+            if (solution == NULL) {
+                printf("TEST ERROR: pmSubtractionSolveEquation() returned NULL.\n");
+                testStatus = true;
+            } else {
+                printf("The solution vector is:\n");
+                for (psS32 i = 0 ; i < solution->n ; i++) {
+                    printf("(%.2f) ", solution->data.F64[i]);
+                }
+                printf("\n");
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionRejectStamps() with acceptable input parameters.  Should return TRUE.\n");
+                rc = pmSubtractionRejectStamps(stamps, maskImg, 0xff, TST03_FOOTPRINT, 1.0, refImg,
+                                               tstImg, solution, myKernels);
+                if (rc != TRUE) {
+                    printf("TEST ERROR: pmSubtractionRejectStamps() returned FALSE.\n");
+                    testStatus = true;
+                } else {}
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() with NULL solution.  Should generate error, return NULL.\n");
+                psImage *kernelImg = pmSubtractionKernelImage(NULL, NULL, myKernels, 0.0, 0.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() with NULL kernels.  Should generate error, return NULL.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, NULL, 0.0, 0.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() unallowable x value.  Should generate error, return NULL.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, myKernels, -2.0, 0.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() unallowable x value.  Should generate error, return NULL.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, myKernels, 2.0, 0.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() unallowable y value.  Should generate error, return NULL.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, myKernels, 0.0, -2.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() unallowable y value.  Should generate error, return NULL.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, myKernels, 0.0, 2.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() with acceptable input parameters.  Should return a psImage.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, myKernels, 0.5, 0.5);
+                if (kernelImg == NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned NULL.\n");
+                    testStatus = true;
+                } else {
+                    for (psS32 row = 0 ; row < kernelImg->numRows; row++) {
+                        for (psS32 col = 0 ; col < kernelImg->numCols; col++) {
+                            printf("%f ", kernelImg->data.F32[row][col]);
+                        }
+                        printf("\n");
+                    }
+                }
+                free(kernelImg);
+
+                psFree(solution);
+            }
+        }
+    }
+    psFree(tstImg);
+    psFree(refImg);
+    psFree(stamps);
+    psFree(myKernels);
+    psFree(orders);
+    psFree(sigmas);
+
+    return(testStatus);
+}
+
+/*******************************************************************************
+NOTE: This function returns TRUE if there were no errors.
+ ******************************************************************************/
+int test03( void )
+{
+    bool testStatus = false;
+
+
+    srand(1995);
+    if (1)
+        testStatus|= testSubCalcEqu(TST03_NUM_COLS,
+                                    TST03_NUM_ROWS,
+                                    TST03_NUM_STAMPS_COLS,
+                                    TST03_NUM_STAMPS_ROWS,
+                                    TST03_BORDER,
+                                    PM_SUBTRACTION_KERNEL_POIS);
+
+
+    srand(1995);
+    if (1)
+        testStatus|= testSubCalcEqu(TST03_NUM_COLS,
+                                    TST03_NUM_ROWS,
+                                    TST03_NUM_STAMPS_COLS,
+                                    TST03_NUM_STAMPS_ROWS,
+                                    TST03_BORDER,
+                                    PM_SUBTRACTION_KERNEL_ISIS);
+
+
+
+    return(!testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tst_pmReadoutCombine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tst_pmReadoutCombine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/imcombine/tst_pmReadoutCombine.c	(revision 22322)
@@ -0,0 +1,462 @@
+/** @file tst_pmReadoutCombine.c
+ *
+ *  test00() This routine will test the basic functionality.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-03-04 01:01:34 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: Untested:
+ * S16, S32 types
+ * Multiple input readouts with varying sizes and offsets.
+ * params->fracLow and params->fracHigh
+ * params->nKeep
+ * (gain > 0.0) && (readnoise >= 0.0) (applyZeroScale == true)
+ * (gain > 0.0) && (readnoise >= 0.0) (applyZeroScale == false)
+ *
+ */
+
+#include "psTest.h"
+#include "pslib.h"
+#include "pmReadoutCombine.h"
+static int test00(void);
+static int test01(void);
+testDescription tests[] = {
+                              {test00, 000, "pmSubtractBias(): Basic readout combines with no image overlap", true, false},
+                              {test01, 000, "pmSubtractBias(): input parameter error conditions", true, false},
+                              {NULL}
+                          };
+
+#define NUM_READOUTS  10
+#define INPUT_NUM_ROWS 20
+#define INPUT_NUM_COLS 20
+#define VEC_ZERO 1.0
+#define VEC_SCALE 2.0
+
+psS32 VerifyTheOutput(psImage *output, psF32 expect)
+{
+    bool testStatus = true;
+
+    for (psS32 i = 0 ; i < output->numRows ; i++) {
+        for (psS32 j = 0 ; j < output->numCols ; j++) {
+            if (output->data.F32[i][j] != expect) {
+                printf("TEST ERROR: output[%d][%d] is %.2f, should be %f\n", i, j, output->data.F32[i][j], expect);
+                testStatus = false;
+            }
+        }
+    }
+    return(testStatus);
+}
+
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+/******************************************************************************
+simpleCombineNoOverlap(): this routine creates a list of NUM_READOUTS input
+readouts and calls pmReadoutCombine().
+ *****************************************************************************/
+int simpleCombineNoOverlap(psS32 numInputCols, psS32 numInputRows)
+{
+    int i;
+    int r;
+    psList *list = NULL;
+    int baseRowsReadout[NUM_READOUTS];
+    int baseColsReadout[NUM_READOUTS];
+    int baseRows[NUM_READOUTS];
+    int baseCols[NUM_READOUTS];
+    int numRows[NUM_READOUTS];
+    int numCols[NUM_READOUTS];
+    int minOutRow = 10000;
+    int minOutCol = 10000;
+    int maxOutRow = -1;
+    int maxOutCol = -1;
+    psImage *output = NULL;
+    psCombineParams *params = (psCombineParams *) psAlloc(sizeof(psCombineParams));
+    psVector *zero = psVectorAlloc(NUM_READOUTS, PS_TYPE_F32);
+    psVector *scale = psVectorAlloc(NUM_READOUTS, PS_TYPE_F32);
+    zero->n = zero->nalloc;
+    scale->n = scale->nalloc;
+    printPositiveTestHeader(stdout, "pmReadoutCombine", "simpleCombineNoOverlap");
+
+    for (i=0;i<NUM_READOUTS;i++) {
+        zero->data.F32[i] = VEC_ZERO;
+    }
+    for (i=0;i<NUM_READOUTS;i++) {
+        scale->data.F32[i] = VEC_SCALE;
+    }
+
+    params->stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    params->maskVal = 1;
+    params->fracLow = 0.0;
+    params->fracHigh = 10000.0;
+    params->nKeep = 0;
+
+    //
+    // Create a psList of psReadouts.  The pixels in readout r will all have the
+    // value r.
+    //
+    for (r=0;r<NUM_READOUTS;r++) {
+        baseRowsReadout[r] = r + 40;
+        baseColsReadout[r] = r + 42;
+        baseRows[r] = r;
+        baseCols[r] = r+2;
+        numRows[r] = 4 + (2 * r);
+        numCols[r] = 8 + (2 * r);
+
+        baseRowsReadout[r] = 0;
+        baseColsReadout[r] = 0;
+        baseRows[r] = 0;
+        baseCols[r] = 0;
+        numRows[r] = numInputRows;
+        numCols[r] = numInputCols;
+
+        psImage *tmpImage = psImageAlloc(numCols[r], numRows[r], PS_TYPE_F32);
+        PS_IMAGE_SET_F32(tmpImage, ((float) r));
+        *(int *) (& (tmpImage->row0)) = baseRows[r];
+        *(int *) (& (tmpImage->col0)) = baseCols[r];
+        pmReadout *tmpReadout = pmReadoutAlloc(NULL);
+        tmpReadout->row0 = 0;
+        tmpReadout->col0 = 0;
+        tmpReadout->image = tmpImage;
+
+        minOutRow = PS_MIN(minOutRow, (baseRowsReadout[r] + baseRows[r]));
+        minOutCol = PS_MIN(minOutCol, (baseColsReadout[r] + baseCols[r]));
+        maxOutRow = PS_MAX(maxOutRow, (baseRowsReadout[r] + baseRows[r] + numRows[r]));
+        maxOutCol = PS_MAX(maxOutCol, (baseColsReadout[r] + baseCols[r] + numCols[r]));
+
+        if (r == 0) {
+            list = psListAlloc(tmpReadout);
+        } else {
+            psListAdd(list, PS_LIST_HEAD, tmpReadout);
+        }
+    }
+    printf("tst_pmReadoutCombine(): (minOutRow, minOutCol) to (maxOutRow, maxOutCol) is (%d, %d) (%d, %d)\n",
+           minOutRow, minOutCol, maxOutRow, maxOutCol);
+
+    output = pmReadoutCombine(output, list, params, zero, scale, true, 0.0, 0.0);
+    psF32 NR = (psF32) NUM_READOUTS;
+    psF32 expectedPixel = ((NR/2.0) * (VEC_ZERO + (VEC_ZERO + VEC_SCALE * (NR - 1)))) / NR;
+
+    int testStatus = VerifyTheOutput(output, expectedPixel);
+
+    psFree(params->stats);
+    psFree(params);
+    psFree(output);
+    psFree(zero);
+    psFree(scale);
+
+    psListElem *tmpInput = (psListElem *) list->head;
+    while (NULL != tmpInput) {
+        pmReadout *tmpReadout = (pmReadout *) tmpInput->data;
+        psFree(tmpReadout);
+        tmpInput = tmpInput->next;
+    }
+    psFree(list);
+
+    printFooter(stdout, "pmReadoutCombine", "simpleCombineNoOverlap", true);
+    return(testStatus);
+}
+
+int test00( void )
+{
+    int testStatus = 0;
+
+    testStatus |= simpleCombineNoOverlap(1, 1);
+    testStatus |= simpleCombineNoOverlap(INPUT_NUM_COLS, 1);
+    testStatus |= simpleCombineNoOverlap(1, INPUT_NUM_ROWS);
+    testStatus |= simpleCombineNoOverlap(INPUT_NUM_COLS, INPUT_NUM_ROWS);
+
+    return(testStatus);
+}
+
+/******************************************************************************
+test01(): we simply call pmReadoutCombine() with a variety of erroneous input
+parameter combinations and verify that it behaves properly.
+ *****************************************************************************/
+int test01()
+{
+    int testStatus = true;
+    int i;
+    int r;
+    psList *list = NULL;
+    int baseRowsReadout[NUM_READOUTS];
+    int baseColsReadout[NUM_READOUTS];
+    int baseRows[NUM_READOUTS];
+    int baseCols[NUM_READOUTS];
+    int numRows[NUM_READOUTS];
+    int numCols[NUM_READOUTS];
+    int minOutRow = 10000;
+    int minOutCol = 10000;
+    int maxOutRow = -1;
+    int maxOutCol = -1;
+    psImage *output = NULL;
+    psImage *rc = NULL;
+    psCombineParams *params = (psCombineParams *) psAlloc(sizeof(psCombineParams));
+    psVector *zero = psVectorAlloc(NUM_READOUTS, PS_TYPE_F32);
+    psVector *zeroHalf = psVectorAlloc(NUM_READOUTS/2, PS_TYPE_F32);
+    psVector *zeroBig = psVectorAlloc(NUM_READOUTS+1, PS_TYPE_F32);
+    psVector *zeroF64 = psVectorAlloc(NUM_READOUTS, PS_TYPE_F64);
+    psVector *scale = psVectorAlloc(NUM_READOUTS, PS_TYPE_F32);
+    psVector *scaleHalf = psVectorAlloc(NUM_READOUTS/2, PS_TYPE_F32);
+    psVector *scaleBig = psVectorAlloc(NUM_READOUTS*2, PS_TYPE_F32);
+    psVector *scaleF64 = psVectorAlloc(NUM_READOUTS, PS_TYPE_F64);
+    zero->n = zero->nalloc;
+    zeroHalf->n = zeroHalf->nalloc;
+    zeroBig->n = zeroBig->nalloc;
+    zeroF64->n = zeroF64->nalloc;
+    scale->n = scale->nalloc;
+    scaleHalf->n = scaleHalf->nalloc;
+    scaleBig->n = scaleBig->nalloc;
+    scaleF64->n = scaleF64->nalloc;
+    for (i=0;i<NUM_READOUTS;i++) {
+        zero->data.F32[i] = 3.0;
+        zeroBig->data.F32[i] = 3.0;
+        zero->data.F32[i] = VEC_ZERO;
+        zeroBig->data.F32[i] = VEC_ZERO;
+    }
+    for (i=0;i<NUM_READOUTS;i++) {
+        scale->data.F32[i] = 6.0;
+        scaleBig->data.F32[i] = 6.0;
+        scale->data.F32[i] = VEC_SCALE;
+        scaleBig->data.F32[i] = VEC_SCALE;
+    }
+    printPositiveTestHeader(stdout, "pmReadoutCombine", "Testing bad input parameter conditions");
+
+    params->stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    params->maskVal = 1;
+    params->fracLow = 0.0;
+    params->fracHigh = 10000.0;
+    params->nKeep = 0;
+
+    for (r=0;r<NUM_READOUTS;r++) {
+        baseRowsReadout[r] = 0;
+        baseColsReadout[r] = 0;
+        baseRows[r] = 0;
+        baseCols[r] = 0;
+        numRows[r] = INPUT_NUM_ROWS;
+        numCols[r] = INPUT_NUM_COLS;
+
+        psImage *tmpImage = psImageAlloc(numCols[r], numRows[r], PS_TYPE_F32);
+        PS_IMAGE_SET_F32(tmpImage, ((float) r));
+        *(int *) (& (tmpImage->row0)) = baseRows[r];
+        *(int *) (& (tmpImage->col0)) = baseCols[r];
+        pmReadout *tmpReadout = pmReadoutAlloc(NULL);
+        tmpReadout->row0 = 0;
+        tmpReadout->col0 = 0;
+        tmpReadout->image = tmpImage;
+        minOutRow = PS_MIN(minOutRow, (baseRowsReadout[r] + baseRows[r]));
+        minOutCol = PS_MIN(minOutCol, (baseColsReadout[r] + baseCols[r]));
+        maxOutRow = PS_MAX(maxOutRow, (baseRowsReadout[r] + baseRows[r] + numRows[r]));
+        maxOutCol = PS_MAX(maxOutCol, (baseColsReadout[r] + baseCols[r] + numCols[r]));
+
+        if (r == 0) {
+            list = psListAlloc(tmpReadout);
+        } else {
+            psListAdd(list, PS_LIST_HEAD, tmpReadout);
+        }
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with NULL zero vector.\n");
+    rc = pmReadoutCombine(NULL, list, params, NULL, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        psF32 NR = (psF32) NUM_READOUTS;
+        psF32 expectedPixel = ((NR/2.0) * (0.0 + (0.0 + VEC_SCALE * (NR - 1)))) / NR;
+        if (false == VerifyTheOutput(rc, expectedPixel)) {
+            testStatus = false;
+        }
+
+        if (rc->type.type != scale->type.type) {
+            printf("TEST ERROR: output readout->image has incorrect type.\n");
+            testStatus = false;
+        }
+        psFree(rc);
+    } else {
+        printf("TEST ERROR: pmReadoutCombine() returned NULL\n");
+        testStatus = false;
+
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect length zero vector (too small).  Should generate error.\n");
+    rc = pmReadoutCombine(NULL, list, params, zeroHalf, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect type zero vector.  Should generate error.\n");
+    rc = pmReadoutCombine(NULL, list, params, zeroF64, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect length zero vector (too big).  Should generate warning.\n");
+    rc = pmReadoutCombine(output, list, params, zeroBig, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        psF32 NR = (psF32) NUM_READOUTS;
+        psF32 expectedPixel = ((NR/2.0) * (VEC_ZERO + (VEC_ZERO + VEC_SCALE * (NR - 1)))) / NR;
+
+        if (false == VerifyTheOutput(rc, expectedPixel)) {
+            testStatus = false;
+        }
+        psFree(rc);
+        rc = NULL;
+    } else {
+        printf("TEST ERROR: pmReadoutCombine() returned NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with NULL scale vector.\n");
+    rc = pmReadoutCombine(output, list, params, zero, NULL, true, 0.0, 0.0);
+
+    if (rc != NULL) {
+        psF32 NR = (psF32) NUM_READOUTS;
+        psF32 expectedPixel = ((NR/2.0) * (VEC_ZERO + (VEC_ZERO + 1.0 * (NR - 1)))) / NR;
+        if (false == VerifyTheOutput(rc, expectedPixel)) {
+            testStatus = false;
+        }
+
+        if (rc->type.type != scale->type.type) {
+            printf("TEST ERROR: output readout->image has incorrect type.\n");
+            testStatus = false;
+        }
+        psFree(rc);
+    } else {
+        printf("TEST ERROR: pmReadoutCombine() returned NULL\n");
+        testStatus = false;
+
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect length scale vector (too small).  Should generate error.\n");
+    rc = pmReadoutCombine(output, list, params, zero, scaleHalf, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect type scale vector.  Should generate error.\n");
+    rc = pmReadoutCombine(output, list, params, zero, scaleF64, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect length scale vector (too big).  Should generate warning.\n");
+    rc = pmReadoutCombine(output, list, params, zero, scaleBig, true, 0.0, 0.0);
+    if (rc != NULL) {
+        psF32 NR = (psF32) NUM_READOUTS;
+        psF32 expectedPixel = ((NR/2.0) * (VEC_ZERO + (VEC_ZERO + VEC_SCALE * (NR - 1)))) / NR;
+
+        if (false == VerifyTheOutput(rc, expectedPixel)) {
+            testStatus = false;
+        }
+        psFree(rc);
+        rc = NULL;
+    } else {
+        printf("TEST ERROR: pmReadoutCombine() returned NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() insufficient size output image.  Should generate error, return NULL.\n");
+    output = psImageAlloc(1, 1, PS_TYPE_F32);
+    rc = pmReadoutCombine(output, list, params, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+    psFree(output);
+    output = NULL;
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() row0/col0 too large.  Should generate error, return NULL.\n");
+    output = psImageAlloc(1, 1, PS_TYPE_F32);
+    *(psS32*)&output->row0 = 10000;
+    *(psS32*)&output->col0 = 10000;
+    rc = pmReadoutCombine(output, list, params, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+    psFree(output);
+    output = NULL;
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with NULL input list.  Should generate error, return NULL.\n");
+    rc = pmReadoutCombine(output, NULL, params, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with NULL params.  Should generate error, return NULL.\n");
+    rc = pmReadoutCombine(output, list, NULL, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    psStatsOptions oldStatsOpts = params->stats->options |= PS_STAT_MIN;
+    printf("Calling pmReadoutCombine() with multiple stats->options.  Should generate error, return NULL.\n");
+    rc = pmReadoutCombine(output, list, params, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+    params->stats->options = oldStatsOpts;
+
+    printf("----------------------------------------------------------------------------\n");
+    psStats *oldStats = params->stats;
+    printf("Calling pmReadoutCombine() with NULL param->stats.  Should generate error, return NULL.\n");
+    params->stats = NULL;
+    rc = pmReadoutCombine(output, list, params, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+    params->stats = oldStats;
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("============================================================================\n");
+
+    psFree(params->stats);
+    psFree(params);
+    //    psFree(output);
+    //    psFree(rc);
+    psFree(zero);
+    psFree(zeroHalf);
+    psFree(zeroBig);
+    psFree(zeroF64);
+    psFree(scale);
+    psFree(scaleHalf);
+    psFree(scaleBig);
+    psFree(scaleF64);
+    psListElem *tmpInput = (psListElem *) list->head;
+    while (NULL != tmpInput) {
+        pmReadout *tmpReadout = (pmReadout *) tmpInput->data;
+        psFree(tmpReadout);
+        tmpInput = tmpInput->next;
+    }
+    psFree(list);
+
+    printFooter(stdout, "pmReadoutCombine", "Testing bad input parameter conditions", true);
+    return(testStatus);
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tap_pmGrowthCurve
+tap_pmSourceFitModel
+tap_pmSourceFitModel_Delta
+tap_pmSourcePhotometry
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/Makefile.am	(revision 22322)
@@ -0,0 +1,51 @@
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSMODULES_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpsmodules.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
+	$(PSMODULES_LIBS)
+
+TEST_PROGS = \
+	tap_pmPeaks \
+	tap_pmGrowthCurve \
+	tap_pmMoments \
+	tap_pmSource \
+	tap_pmModel \
+	tap_pmModelUtils \
+	tap_pmModelClass \
+	tap_pmPSF \
+	tap_pmTrend2D \
+	tap_pmResiduals \
+	tap_pmSourceExtendedPars \
+	tap_pmSourceSky \
+	tap_pmSourceIO_PS1_DEV_0 \
+	tap_pmSourceIO_PS1_DEV_1 \
+	tap_pmSourceIO_SMPDATA \
+	tap_pmSourceContour \
+	tap_pmSourceUtils \
+	tap_pmSourceFitSet \
+	tap_pmPSF_IO \
+	tap_pmSourceIO_SMPDATA
+
+if BUILD_TESTS
+bin_PROGRAMS = $(TEST_PROGS)
+TESTS = $(TEST_PROGS)
+else
+check_PROGRAMS = $(TEST_PROGS)
+endif
+
+CLEANFILES = $(check_DATA) temp/* core core.* *~ *.bb *.bbg *.da gmon.out
+
+test: check
+	$(top_srcdir)/test/test.pl
+
+# Removed
+#	tap_pmSourcePhotometry
+#	tap_pmGrowthCurve
+#	tap_pmSourceFitModel
+#	tap_pmSourceFitModel_Delta
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmFringe.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmFringe.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmFringe.c	(revision 22322)
@@ -0,0 +1,130 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_ROWS 8
+#define NUM_COLS 16
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(35);
+
+
+    // Test pmFringeRegionsAlloc()
+    {
+        psMemId id = psMemGetId();
+        pmFringeRegions *fringe = pmFringeRegionsAlloc(1, 2, 3, 4, 5);
+        ok(fringe != NULL, "pmFringeRegionsAlloc() returned non-NULL");
+        ok(fringe->x == NULL, "pmFringeRegionsAlloc() set fringe->x correctly");
+        ok(fringe->y == NULL, "pmFringeRegionsAlloc() set fringe->y correctly");
+        ok(fringe->mask == NULL, "pmFringeRegionsAlloc() set fringe->mask correctly");
+        ok(fringe->nRequested == 1, "pmFringeRegionsAlloc() set fringe->nRequested correctly");
+        ok(fringe->nAccepted == 0, "pmFringeRegionsAlloc() set fringe->nAccepted correctly");
+        ok(fringe->dX == 2, "pmFringeRegionsAlloc() set fringe->dX correctly");
+        ok(fringe->dY == 3, "pmFringeRegionsAlloc() set fringe->dY correctly");
+        ok(fringe->nX == 4, "pmFringeRegionsAlloc() set fringe->nX correctly");
+        ok(fringe->nY == 5, "pmFringeRegionsAlloc() set fringe->nY correctly");
+        psFree(fringe);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    #define NUM_FRINGE_PNTS 10
+    #define DX 2
+    #define DY 2
+    #define NX 1
+    #define NY 1
+    // test pmFringeRegionsCreatePoints(): NULL random number generator,
+    // NULL fringe X, y, and mask vectors.
+    {
+        psMemId id = psMemGetId();
+        pmFringeRegions *fringe = pmFringeRegionsAlloc(NUM_FRINGE_PNTS, DX, DY, NX, NY);
+        ok(fringe != NULL, "pmFringeRegionsAlloc() returned non-NULL");
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        bool rc = pmFringeRegionsCreatePoints(fringe, img, NULL);
+        ok(rc, "pmFringeRegionsCreatePoints() returned TRUE");
+        ok(fringe->x != NULL &&
+           fringe->x->type.type == PS_TYPE_F32 &&
+           fringe->x->n ==NUM_FRINGE_PNTS, "pmFringeRegionsCreatePoints() returned correct fringe->x psVector");
+        ok(fringe->y != NULL &&
+           fringe->y->type.type == PS_TYPE_F32 &&
+           fringe->y->n ==NUM_FRINGE_PNTS, "pmFringeRegionsCreatePoints() returned correct fringe->y psVector");
+        ok(fringe->mask != NULL &&
+           fringe->mask->type.type == PS_TYPE_MASK &&
+           fringe->mask->n ==NUM_FRINGE_PNTS, "pmFringeRegionsCreatePoints() returned correct fringe->mask psVector");
+        bool errorFlag = false;
+        for (int i = 0 ; i < NUM_FRINGE_PNTS ; i++) {
+            if (fringe->mask->data.U8[i] != 0) {
+                diag("ERROR: pmFringeRegionsCreatePoints() did not set mask[%d] to 0", i);
+                errorFlag = true;
+            }
+            if (!((fringe->x->data.F32[i] >= DX) &&
+                 (fringe->x->data.F32[i] <= NUM_COLS-DX))) {
+                diag("ERROR: pmFringeRegionsCreatePoints() did not set x[%d] correctly.  It was %.2f, should be within (%d %d)", i,
+                      fringe->x->data.F32[i], DX, NUM_COLS-DX);
+                errorFlag = true;
+            }
+            if (!((fringe->y->data.F32[i] >= DY) &&
+                 (fringe->y->data.F32[i] <= NUM_ROWS-DY))) {
+                diag("ERROR: pmFringeRegionsCreatePoints() did not set x[%d] correctly.  It was %.2f, should be within (%d %d)", i,
+                      fringe->y->data.F32[i], DY, NUM_ROWS-DY);
+                errorFlag = true;
+            }
+        }
+
+        psFree(img);
+        psFree(fringe);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test pmFringeRegionsCreatePoints(): non-NULL random number generator,
+    // non-NULL fringe X, y, and mask vectors.
+    {
+        psMemId id = psMemGetId();
+        pmFringeRegions *fringe = pmFringeRegionsAlloc(NUM_FRINGE_PNTS, DX, DY, NX, NY);
+        ok(fringe != NULL, "pmFringeRegionsAlloc() returned non-NULL");
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 10);
+        fringe->x = psVectorAlloc(NUM_FRINGE_PNTS/2, PS_TYPE_F32);
+        fringe->y = psVectorAlloc(NUM_FRINGE_PNTS/2, PS_TYPE_F32);
+        fringe->mask = psVectorAlloc(NUM_FRINGE_PNTS/2, PS_TYPE_MASK);
+        bool rc = pmFringeRegionsCreatePoints(fringe, img, NULL);
+        ok(rc, "pmFringeRegionsCreatePoints() returned TRUE");
+        ok(fringe->x != NULL &&
+           fringe->x->type.type == PS_TYPE_F32 &&
+           fringe->x->n ==NUM_FRINGE_PNTS, "pmFringeRegionsCreatePoints() returned correct fringe->x psVector");
+        ok(fringe->y != NULL &&
+           fringe->y->type.type == PS_TYPE_F32 &&
+           fringe->y->n ==NUM_FRINGE_PNTS, "pmFringeRegionsCreatePoints() returned correct fringe->y psVector");
+        ok(fringe->mask != NULL &&
+           fringe->mask->type.type == PS_TYPE_MASK &&
+           fringe->mask->n ==NUM_FRINGE_PNTS, "pmFringeRegionsCreatePoints() returned correct fringe->mask psVector");
+        bool errorFlag = false;
+        for (int i = 0 ; i < NUM_FRINGE_PNTS ; i++) {
+            if (fringe->mask->data.U8[i] != 0) {
+                diag("ERROR: pmFringeRegionsCreatePoints() did not set mask[%d] to 0", i);
+                errorFlag = true;
+            }
+            if (!((fringe->x->data.F32[i] >= DX) &&
+                 (fringe->x->data.F32[i] <= NUM_COLS-DX))) {
+                diag("ERROR: pmFringeRegionsCreatePoints() did not set x[%d] correctly.  It was %.2f, should be within (%d %d)", i,
+                      fringe->x->data.F32[i], DX, NUM_COLS-DX);
+                errorFlag = true;
+            }
+            if (!((fringe->y->data.F32[i] >= DY) &&
+                 (fringe->y->data.F32[i] <= NUM_ROWS-DY))) {
+                diag("ERROR: pmFringeRegionsCreatePoints() did not set x[%d] correctly.  It was %.2f, should be within (%d %d)", i,
+                      fringe->y->data.F32[i], DY, NUM_ROWS-DY);
+                errorFlag = true;
+            }
+        }
+        psFree(rng);
+        psFree(img);
+        psFree(fringe);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmGrowthCurve.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmGrowthCurve.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmGrowthCurve.c	(revision 22322)
@@ -0,0 +1,378 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/*
+    STATUS:
+	All functions are tested.  However, some of the pmGrowthCurveCorrect()
+	tests that were supplied by IfA ae failing.
+*/
+
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(70);
+
+    // ----------------------------------------------------------------------
+    // pmGrowthCurveAlloc() tests
+    // call pmGrowthCurveAlloc() with acceptable input parameters.
+    {
+        psMemId id = psMemGetId();
+        pmGrowthCurve *growthCurve = pmGrowthCurveAlloc(1.0, 2.0, 3.0);
+        ok(growthCurve && psMemCheckGrowthCurve(growthCurve), "pmGrowthCurveAlloc() allocated a pmGrowthCurve correctly");
+        ok(growthCurve->radius && psMemCheckVector(growthCurve->radius), "pmGrowthCurveAlloc() allocated the radius psVector correctly");
+        ok(growthCurve->apMag  && psMemCheckVector(growthCurve->apMag) &&
+           growthCurve->apMag->n == growthCurve->radius->n, "pmGrowthCurveAlloc() allocated the apMag psVector correctly");
+        ok(growthCurve->refRadius == 3.0, "pmGrowthCurveAlloc() set growthCurve->refRadius correctly");
+        ok(growthCurve->maxRadius == 2.0, "pmGrowthCurveAlloc() set growthCurve->maxRadius correctly");
+        ok(growthCurve->apLoss == 0.0, "pmGrowthCurveAlloc() set growthCurve->apLoss correctly");
+        ok(growthCurve->fitMag == 0.0, "pmGrowthCurveAlloc() set growthCurve->fitMag correctly");
+        psFree(growthCurve);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmGrowthCurveCorrect() tests
+    // Call pmGrowthCurveCorrect() with NULL input pmGrowthCurve
+    {
+        psMemId id = psMemGetId();
+        pmGrowthCurve *growthCurve = pmGrowthCurveAlloc(1.0, 2.0, 3.0);
+        ok(isnan(pmGrowthCurveCorrect(NULL, 0.0)), "pmGrowthCurveCorrect() returned NAN with NULL input pmGrowthCurve");
+        psFree(growthCurve);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmGrowthCurveCorrect() with acceptable input parameters.
+    {
+        #define RADIUS 0.5
+        psMemId id = psMemGetId();
+        pmGrowthCurve *growthCurve = pmGrowthCurveAlloc(1.0, 2.0, 3.0);
+        float testCor = pmGrowthCurveCorrect(growthCurve, RADIUS);
+        float actRad = psVectorInterpolate (growthCurve->radius, growthCurve->apMag, RADIUS);
+        float actCor = growthCurve->apRef - actRad;
+
+        ok(!isnan(testCor), "pmGrowthCurveCorrect() call was successful");
+        ok(actCor == testCor, "pmGrowthCurveCorrect() calculated the correction correctly");
+
+        psFree(growthCurve);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // EM test 01: test allocation
+    // offset of 0.0,0.0 wrt growth ref source
+    {
+        psMemId id = psMemGetId();
+
+        pmGrowthCurve *growth = pmGrowthCurveAlloc (2.0, 100.0, 15.0);
+
+        ok(growth != NULL, "growth curve allocated");
+        skip_start(growth == NULL, 0, "Skipping tests because pmShutterCorrParsAlloc() failed");
+
+        ok(growth->radius->n == (100.0 - 2.0), "correct number of growth radii");
+        ok(growth->apMag->n == (100.0 - 2.0), "correct number of growth apMags");
+
+        ok_float(growth->refRadius, 15.0, "correct refRadius");
+        ok_float(growth->maxRadius, 100.0, "correct maxRadius");
+
+        // does the growth curve correctly fix aperture mags?
+
+        // generate a simple readout
+        pmReadout *readout = pmReadoutAlloc (NULL);
+        readout->image = psImageAlloc (64, 64, PS_TYPE_F32);
+        readout->mask  = psImageAlloc (64, 64, PS_TYPE_U8);
+
+        // create an empty reference image
+        psImageInit (readout->image, 0.0);
+        psImageInit (readout->mask, 0);
+
+        // generate a simple psf
+        pmModelClassInit();
+        pmPSF *psf = pmPSFBuildSimple("PS_MODEL_GAUSS", 1.5, 1.5, 0.0);
+        psf->growth = growth;
+
+        pmGrowthCurveGenerate(readout, psf, false, 0, 0);
+
+        // check ap mags for a few radii set by hand
+        ok_float_tol(growth->apMag->data.F32[0],   -9.7805, 0.0001, "apMag at radius 0: %f", growth->apMag->data.F32[0]);
+        ok_float_tol(growth->apMag->data.F32[3],  -10.3722, 0.0001, "apMag at radius 3: %f", growth->apMag->data.F32[3]);
+        ok_float_tol(growth->apMag->data.F32[10], -10.3759, 0.0001, "apMag at radius 10: %f", growth->apMag->data.F32[10]);
+        ok_float_tol(growth->apMag->data.F32[30], -10.3759, 0.0001, "apMag at radius 30: %f", growth->apMag->data.F32[30]);
+
+        ok_float_tol(growth->apRef,  -10.3759, 0.0001, "apMag at ref radius : %f", growth->apRef);
+        ok_float_tol(growth->fitMag, -10.3759, 0.0001, "fitMag : %f", growth->fitMag);
+        ok_float(growth->apLoss, 0.0, "apLoss : %f", growth->apLoss);
+
+        // create template model and measure apMag at fractional offsets
+        // XXX note model is at 0.5,0.5 subpix center
+        pmModel *modelRef = pmModelAlloc(psf->type);
+        modelRef->params->data.F32[PM_PAR_SKY] = 0;
+        modelRef->params->data.F32[PM_PAR_I0] = 1000;
+        modelRef->params->data.F32[PM_PAR_XPOS] = 32.5;
+        modelRef->params->data.F32[PM_PAR_YPOS] = 32.5;
+
+        // measure growth-corrected photometry:
+        pmSource *source = pmSourceAlloc ();
+        source->modelPSF = pmModelFromPSF (modelRef, psf);
+        source->type = PM_SOURCE_TYPE_STAR;
+        source->pixels = psMemIncrRefCounter (readout->image);
+        source->maskObj = psMemIncrRefCounter (readout->mask);
+
+        source->modelPSF->dparams->data.F32[PM_PAR_I0] = 1;
+        source->mode = PM_SOURCE_MODE_SUBTRACTED;
+
+        source->modelPSF->radiusFit = 15.0;
+
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        double refMag = source->apMag;
+
+        source->modelPSF->radiusFit = 10.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 8.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 6.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0003, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 4.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0020, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 3.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, -0.0001, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 2.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, -0.0075, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        // XXX include some apertures outside of growth correction range
+
+        psFree(modelRef);
+        psFree(source);
+        psFree(readout);
+        psFree(psf);
+
+        skip_end();
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // EM test 02: test allocation
+    // offset of 0.2,0.2 wrt growth ref source
+    {
+        psMemId id = psMemGetId();
+
+        pmGrowthCurve *growth = pmGrowthCurveAlloc (2.0, 100.0, 15.0);
+
+        ok(growth != NULL, "growth curve allocated");
+        skip_start(growth == NULL, 0, "Skipping tests because pmShutterCorrParsAlloc() failed");
+
+        ok(growth->radius->n == (100.0 - 2.0), "correct number of growth radii");
+        ok(growth->apMag->n == (100.0 - 2.0), "correct number of growth apMags");
+
+        ok_float(growth->refRadius, 15.0, "correct refRadius");
+        ok_float(growth->maxRadius, 100.0, "correct maxRadius");
+
+        // does the growth curve correctly fix aperture mags?
+
+        // generate a simple readout
+        pmReadout *readout = pmReadoutAlloc (NULL);
+        readout->image = psImageAlloc (64, 64, PS_TYPE_F32);
+        readout->mask  = psImageAlloc (64, 64, PS_TYPE_U8);
+
+        // create an empty reference image
+        psImageInit (readout->image, 0.0);
+        psImageInit (readout->mask, 0);
+
+        // generate a simple psf
+        pmPSF *psf = pmPSFBuildSimple ("PS_MODEL_GAUSS", 1.5, 1.5, 0.0);
+        psf->growth = growth;
+
+        pmGrowthCurveGenerate (readout, psf, false, 0, 0);
+
+        // check ap mags for a few radii set by hand
+        ok_float_tol(growth->apMag->data.F32[0],   -9.7805, 0.0001, "apMag at radius 0: %f", growth->apMag->data.F32[0]);
+        ok_float_tol(growth->apMag->data.F32[3],  -10.3722, 0.0001, "apMag at radius 3: %f", growth->apMag->data.F32[3]);
+        ok_float_tol(growth->apMag->data.F32[10], -10.3759, 0.0001, "apMag at radius 10: %f", growth->apMag->data.F32[10]);
+        ok_float_tol(growth->apMag->data.F32[30], -10.3759, 0.0001, "apMag at radius 30: %f", growth->apMag->data.F32[30]);
+
+        ok_float_tol(growth->apRef,  -10.3759, 0.0001, "apMag at ref radius : %f", growth->apRef);
+        ok_float_tol(growth->fitMag, -10.3759, 0.0001, "fitMag : %f", growth->fitMag);
+        ok_float(growth->apLoss, 0.0, "apLoss : %f", growth->apLoss);
+
+        // create template model and measure apMag at fractional offsets
+        // XXX note model is at 0.5,0.5 subpix center
+        pmModel *modelRef = pmModelAlloc(psf->type);
+        modelRef->params->data.F32[PM_PAR_SKY] = 0;
+        modelRef->params->data.F32[PM_PAR_I0] = 1000;
+        modelRef->params->data.F32[PM_PAR_XPOS] = 32.3;
+        modelRef->params->data.F32[PM_PAR_YPOS] = 32.3;
+
+        // measure growth-corrected photometry:
+        pmSource *source = pmSourceAlloc ();
+        source->modelPSF = pmModelFromPSF (modelRef, psf);
+        source->type = PM_SOURCE_TYPE_STAR;
+        source->pixels = psMemIncrRefCounter (readout->image);
+        source->maskObj = psMemIncrRefCounter (readout->mask);
+
+        source->modelPSF->dparams->data.F32[PM_PAR_I0] = 1;
+        source->mode = PM_SOURCE_MODE_SUBTRACTED;
+
+        source->modelPSF->radiusFit = 15.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        double refMag = source->apMag;
+
+        source->modelPSF->radiusFit = 10.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 8.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 6.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0004, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 4.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0026, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 3.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, -0.0001, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 2.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, -0.0103, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        // XXX include some apertures outside of growth correction range
+
+        psFree(modelRef);
+        psFree(source);
+        psFree(readout);
+        psFree(psf);
+
+        skip_end();
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // EM test 03: test allocation
+    // offset of 0.4,0.4 wrt growth ref source
+    {
+        psMemId id = psMemGetId();
+
+        pmGrowthCurve *growth = pmGrowthCurveAlloc (2.0, 100.0, 15.0);
+
+        ok(growth != NULL, "growth curve allocated");
+        skip_start(growth == NULL, 0, "Skipping tests because pmShutterCorrParsAlloc() failed");
+
+        ok(growth->radius->n == (100.0 - 2.0), "correct number of growth radii");
+        ok(growth->apMag->n == (100.0 - 2.0), "correct number of growth apMags");
+
+        ok_float(growth->refRadius, 15.0, "correct refRadius");
+        ok_float(growth->maxRadius, 100.0, "correct maxRadius");
+
+        // does the growth curve correctly fix aperture mags?
+
+        // generate a simple readout
+        pmReadout *readout = pmReadoutAlloc (NULL);
+        readout->image = psImageAlloc (64, 64, PS_TYPE_F32);
+        readout->mask  = psImageAlloc (64, 64, PS_TYPE_U8);
+
+        // create an empty reference image
+        psImageInit (readout->image, 0.0);
+        psImageInit (readout->mask, 0);
+
+        // generate a simple psf
+        pmPSF *psf = pmPSFBuildSimple ("PS_MODEL_GAUSS", 1.5, 1.5, 0.0);
+        psf->growth = growth;
+
+        pmGrowthCurveGenerate(readout, psf, false, 0, 0);
+
+        // check ap mags for a few radii set by hand
+        ok_float_tol(growth->apMag->data.F32[0],   -9.7805, 0.0001, "apMag at radius 0: %f", growth->apMag->data.F32[0]);
+        ok_float_tol(growth->apMag->data.F32[3],  -10.3722, 0.0001, "apMag at radius 3: %f", growth->apMag->data.F32[3]);
+        ok_float_tol(growth->apMag->data.F32[10], -10.3759, 0.0001, "apMag at radius 10: %f", growth->apMag->data.F32[10]);
+        ok_float_tol(growth->apMag->data.F32[30], -10.3759, 0.0001, "apMag at radius 30: %f", growth->apMag->data.F32[30]);
+
+        ok_float_tol(growth->apRef,  -10.3759, 0.0001, "apMag at ref radius : %f", growth->apRef);
+        ok_float_tol(growth->fitMag, -10.3759, 0.0001, "fitMag : %f", growth->fitMag);
+        ok_float(growth->apLoss, 0.0, "apLoss : %f", growth->apLoss);
+
+        // create template model and measure apMag at fractional offsets
+        // XXX note model is at 0.5,0.5 subpix center
+        pmModel *modelRef = pmModelAlloc(psf->type);
+        modelRef->params->data.F32[PM_PAR_SKY] = 0;
+        modelRef->params->data.F32[PM_PAR_I0] = 1000;
+        modelRef->params->data.F32[PM_PAR_XPOS] = 32.1;
+        modelRef->params->data.F32[PM_PAR_YPOS] = 32.1;
+
+        // measure growth-corrected photometry:
+        pmSource *source = pmSourceAlloc ();
+        source->modelPSF = pmModelFromPSF (modelRef, psf);
+        source->type = PM_SOURCE_TYPE_STAR;
+        source->pixels = psMemIncrRefCounter (readout->image);
+        source->maskObj = psMemIncrRefCounter (readout->mask);
+
+        source->modelPSF->dparams->data.F32[PM_PAR_I0] = 1;
+        source->mode = PM_SOURCE_MODE_SUBTRACTED;
+
+        source->modelPSF->radiusFit = 15.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        double refMag = source->apMag;
+
+        source->modelPSF->radiusFit = 10.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 8.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 6.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0006, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 4.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0038, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 3.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        source->modelPSF->radiusFit = 2.0;
+        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0, 0);
+        ok_float_tol(refMag - source->apMag, -0.0164, 0.0001, "growth offset is is %f", refMag - source->apMag);
+
+        // XXX include some apertures outside of growth correction range
+
+        psFree(modelRef);
+        psFree(source);
+        psFree(readout);
+        psFree(psf);
+
+        skip_end();
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmModel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmModel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmModel.c	(revision 22322)
@@ -0,0 +1,460 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.  However...
+
+    The source code for pmModelAddWithOffset() and pmModelSubWithOffset() is
+    almost exactly the same as the source code for pmModelAdd() and
+    pmModelSub().  We do not test them here.
+*/
+
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           (8+1)
+#define TEST_NUM_COLS           (8+1)
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define NUM_ROWS		8
+#define NUM_COLS		16
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.01)
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(53);
+
+    // ----------------------------------------------------------------------
+    // pmModelAlloc() tests
+    // call pmModelAlloc() with unallowable model class type
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModel *model = pmModelAlloc(-1);
+        ok(model == NULL, "pmModelAlloc() returned a NULL with unallowable model class type");
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmModelAlloc() with acceptable input params
+    {
+        #define TEST_MODEL_CLASS_TYPE 1
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        ok(model != NULL && psMemCheckModel(model), "pmModelAlloc() returned a non-NULL pmModel");
+        skip_start(!model, 1, "Skipping tests because pmModelAlloc() returned NULL");
+        ok(model->type == TEST_MODEL_CLASS_TYPE, "pmModelAlloc() set pmModel->type correctly");
+        ok(model->chisq == 0.0, "pmModelAlloc() set pmModel->chisq correctly");
+        ok(model->nDOF == 0, "pmModelAlloc() set pmModel->nDOF correctly");
+        ok(model->nIter == 0, "pmModelAlloc() set pmModel->nIter correctly");
+        ok(model->radiusFit == 0, "pmModelAlloc() set pmModel->radiusFit correctly");
+        ok(model->flags == PM_MODEL_STATUS_NONE, "pmModelAlloc() set pmModel->flags correctly");
+        ok(model->residuals == NULL, "pmModelAlloc() set pmModel->residuals correctly");
+        int nParams = pmModelClassParameterCount(TEST_MODEL_CLASS_TYPE);
+        ok(model->params != NULL && model->params->n == nParams, "pmModelAlloc() set the pmModel->params psVector correctly");
+        ok(model->dparams != NULL && model->dparams->n == nParams, "pmModelAlloc() set the pmModel->dparams psVector correctly");
+        bool errorFlag = false;
+        for (psS32 i = 0; i < nParams; i++) {
+            if (model->params->data.F32[i] != 0.0 || model->dparams->data.F32[i] != 0.0) {
+                if (VERBOSE) {
+                    diag("ERROR: params/dparams[%d] is (%.2f %.2f) should be (%.2f %.2f)\n", i, model->params->data.F32[i], model->dparams->data.F32[i], 0.0, 0.0);
+		}
+                errorFlag = true;
+	    }
+        }
+        ok(!errorFlag, "pmModelAlloc() set the members of the model->params and model->dparams psVectors correctly");
+        pmModelClass *class = pmModelClassSelect(TEST_MODEL_CLASS_TYPE);
+        ok(model->modelFunc == class->modelFunc, "pmModelAlloc() set pmModel->modelFunc correctly");
+        ok(model->modelFlux == class->modelFlux, "pmModelAlloc() set pmModel->modelFlux correctly");
+        ok(model->modelRadius == class->modelRadius, "pmModelAlloc() set pmModel->modelRadius correctly");
+        ok(model->modelLimits == class->modelLimits, "pmModelAlloc() set pmModel->modelLimits correctly");
+        ok(model->modelGuess == class->modelGuess, "pmModelAlloc() set pmModel->modelGuess correctly");
+        ok(model->modelFromPSF == class->modelFromPSF, "pmModelAlloc() set pmModel->modelFromPSF correctly");
+        ok(model->modelParamsFromPSF == class->modelParamsFromPSF, "pmModelAlloc() set pmModel->modelParamsFromPSF correctly");
+        ok(model->modelFitStatus == class->modelFitStatus, "pmModelAlloc() set pmModel->modelFitStatus correctly");
+
+        psFree(model);
+        skip_end();
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmModelCopy() tests
+    // call pmModelCopy() with NULL input pmModel parameter
+    {
+        psMemId id = psMemGetId();
+        pmModel *model = pmModelCopy(NULL);
+        ok(model == NULL, "pmModelCopy() returned NULL with NULL input pmModel parameter");
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // call pmModelCopy() with acceptable input params
+    {
+        #define TEST_MODEL_CLASS_TYPE 1
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModel *modelSrc = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        ok(modelSrc != NULL && psMemCheckModel(modelSrc), "pmModelAlloc() returned a non-NULL pmModel");
+        modelSrc->chisq = 1.0;
+        modelSrc->nDOF = 2;
+        modelSrc->nIter = 3;
+        modelSrc->flags = PM_MODEL_STATUS_NONE;
+        modelSrc->radiusFit = 4;
+        pmModel *modelDst = pmModelCopy(modelSrc);
+        ok(modelDst != NULL && psMemCheckModel(modelDst), "pmModelCopy() returned a non-NULL pmModel");
+        ok(modelDst->chisq == 1.0, "pmModelCopy() set the pmModel->chisq member correctly");
+        ok(modelDst->nDOF == 2, "pmModelCopy() set the pmModel->nDOF member correctly");
+        ok(modelDst->nIter == 3, "pmModelCopy() set the pmModel->nIter member correctly");
+        ok(modelDst->flags == PM_MODEL_STATUS_NONE, "pmModelCopy() set the pmModel->flags member correctly");
+        ok(modelDst->radiusFit == 4, "pmModelCopy() set the pmModel->radiusFit member correctly");
+
+        psFree(modelSrc);
+        psFree(modelDst);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmModelEval() tests
+    // call pmModelEval() with NULL input pmModel parameter
+    {
+        psMemId id = psMemGetId();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psF32 tmpF;
+        tmpF = pmModelEval(NULL, img, 0, 0);
+        ok(isnan(tmpF), "pmModelEval() returned NAN with NULL input pmModel parameter");
+        psFree(model);
+        psFree(img);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row)
+    // call pmModelEval() with NULL input psImage parameter
+    {
+        psMemId id = psMemGetId();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psF32 tmpF;
+        tmpF = pmModelEval(model, NULL, 0, 0);
+        ok(isnan(tmpF), "pmModelEval() returned NAN with NULL input psImage parameter");
+        psFree(model);
+        psFree(img);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row)
+    // call pmModelEval() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        for (int i = 0 ; i < NUM_ROWS ; i++) {
+            for (int j = 0 ; j < NUM_COLS ; j++) {
+                img->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+        x->data.F32[0] = (float) (NUM_COLS / 2);
+        x->data.F32[1] = (float) (NUM_ROWS / 2);
+        psF32 testF = model->modelFunc (NULL, model->params, x);
+        psF32 tmpF;
+        tmpF = pmModelEval(model, img, (int) x->data.F32[0], (int) x->data.F32[1]);
+        ok(!isnan(tmpF), "pmModelEval() returned successfully");
+        ok(testF == tmpF, "pmModelEval() evaluated the model correctly");
+        psFree(model);
+        psFree(img);
+        psFree(x);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+/* XXX: This seems to have disappeared from pmModel.h
+    // ----------------------------------------------------------------------
+    // pmModelEvalWithOffset() tests
+    // psF32 pmModelEvalWithOffset(pmModel *model, psImage *image, 
+    //                             psS32 col, psS32 row, int dx, int dy)
+    // call pmModelEvalWithOffset() with NULL input pmModel parameter
+    {
+        psMemId id = psMemGetId();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psF32 tmpF;
+        tmpF = pmModelEvalWithOffset(NULL, img, 0, 0, 0, 0);
+        ok(isnan(tmpF), "pmModelEvalWithOffset() returned NAN with NULL input pmModel parameter");
+        psFree(model);
+        psFree(img);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psF32 pmModelEvalWithOffset(pmModel *model, psImage *image, 
+    //                             psS32 col, psS32 row, int dx, int dy)
+    // call pmModelEvalWithOffset() with NULL input psImage parameter
+    {
+        psMemId id = psMemGetId();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psF32 tmpF;
+        tmpF = pmModelEvalWithOffset(model, NULL, 0, 0, 0, 0);
+        ok(isnan(tmpF), "pmModelEvalWithOffset() returned NAN with NULL input psImage parameter");
+        psFree(model);
+        psFree(img);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // psF32 pmModelEvalWithOffset(pmModel *model, psImage *image, psS32 col, psS32 row)
+    // call pmModelEvalWithOffset() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        for (int i = 0 ; i < NUM_ROWS ; i++) {
+            for (int j = 0 ; j < NUM_COLS ; j++) {
+                img->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+        x->data.F32[0] = (float) ((NUM_COLS / 2) + (NUM_COLS / 4));
+        x->data.F32[1] = (float) ((NUM_ROWS / 2) + (NUM_ROWS / 4));
+        psF32 testF = model->modelFunc (NULL, model->params, x);
+        psF32 tmpF;
+        tmpF = pmModelEvalWithOffset(model, img, (int) x->data.F32[0], (int) x->data.F32[1], NUM_COLS/4, NUM_ROWS/4);
+        ok(!isnan(tmpF), "pmModelEvalWithOffset() returned successfully");
+        ok(testF == tmpF, "pmModelEvalWithOffset() evaluated the model correctly");
+        psFree(model);
+        psFree(img);
+        psFree(x);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+*/
+
+
+    // ----------------------------------------------------------------------
+    // pmModelAdd() tests
+    // call pmModelAdd() with bad input parameters
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psImage *imgS32 = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_S32);
+        psImage *mask = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_U8);
+        for (int i = 0 ; i < NUM_ROWS ; i++) {
+            for (int j = 0 ; j < NUM_COLS ; j++) {
+                img->data.F32[i][j] = 0.0;
+                mask->data.U8[i][j] = 0;
+            }
+        }
+        pmModel *model = pmModelAlloc(pmModelClassGetType("PS_MODEL_GAUSS"));
+        psF32 *PAR = model->params->data.F32;
+        PAR[PM_PAR_XPOS] = (float) (NUM_COLS/2);
+        PAR[PM_PAR_YPOS] = (float) (NUM_ROWS/2);
+        PAR[PM_PAR_SXX] = 1.0;
+        PAR[PM_PAR_SYY] = 1.0;
+
+        // NULL psImage input parameter
+        bool rc = pmModelAdd(NULL, mask, model, PM_MODEL_OP_FUNC, 1);
+        ok(rc == false, "pmModelAdd() returned FALSE with NULL psImage input parameter");
+
+        // NULL pmModel input parameter
+        rc = pmModelAdd(img, mask, NULL, PM_MODEL_OP_FUNC, 1);
+        ok(rc == false, "pmModelAdd() returned FALSE with NULL pmModel input parameter");
+
+        // Incorrect type psImage input parameter
+        rc = pmModelAdd(imgS32, mask, model, PM_MODEL_OP_FUNC, 1);
+        ok(rc == false, "pmModelAdd() returned FALSE with Incorrect type psImage input parameter");
+
+        psFree(img);
+        psFree(imgS32);
+        psFree(mask);
+        psFree(model);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmModelAdd() with acceptable parameters
+    // We only test with a single Gaussian model, with no residuals or masks.
+    // For completeness, additional tests should be added.
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psImage *mask = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_U8);
+        for (int i = 0 ; i < NUM_ROWS ; i++) {
+            for (int j = 0 ; j < NUM_COLS ; j++) {
+                img->data.F32[i][j] = 0.0;
+                mask->data.U8[i][j] = 0;
+            }
+        }
+        pmModel *model = pmModelAlloc(pmModelClassGetType("PS_MODEL_GAUSS"));
+        psF32 *PAR = model->params->data.F32;
+        PAR[PM_PAR_I0] = 5.0;
+        PAR[PM_PAR_XPOS] = 0.0;
+        PAR[PM_PAR_YPOS] = 0.0;
+        PAR[PM_PAR_XPOS] = (float) (NUM_COLS/2);
+        PAR[PM_PAR_YPOS] = (float) (NUM_ROWS/2);
+        PAR[PM_PAR_SXX] = 10.0;
+        PAR[PM_PAR_SYY] = 10.0;
+
+        bool rc = pmModelAdd(img, mask, model, PM_MODEL_OP_FUNC, 1);
+        ok(rc == true, "pmModelAdd() returned TRUE with acceptable input parameters");
+        psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+        bool errorFlag = false;
+        for (int i = 0 ; i < NUM_ROWS ; i++) {
+            for (int j = 0 ; j < NUM_COLS ; j++) {
+                x->data.F32[0] = (float) j;
+                x->data.F32[1] = (float) i;
+                psF32 modF = model->modelFunc (NULL, model->params, x);
+                psF32 imgF = img->data.F32[i][j];
+                if (!TEST_FLOATS_EQUAL(modF, imgF)) {
+                    diag("ERROR: img[%d][%d] is %.2f, should be %.2f\n", i, j, img->data.F32[i][j], modF);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmModelAdd() set the image pixels correctly");
+        psFree(x);
+        psFree(img);
+        psFree(mask);
+        psFree(model);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmModelSub() tests
+    // call pmModelSub() with bad input parameters
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psImage *imgS32 = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_S32);
+        psImage *mask = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_U8);
+        for (int i = 0 ; i < NUM_ROWS ; i++) {
+            for (int j = 0 ; j < NUM_COLS ; j++) {
+                img->data.F32[i][j] = 0.0;
+                mask->data.U8[i][j] = 0;
+            }
+        }
+        pmModel *model = pmModelAlloc(pmModelClassGetType("PS_MODEL_GAUSS"));
+        psF32 *PAR = model->params->data.F32;
+        PAR[PM_PAR_XPOS] = (float) (NUM_COLS/2);
+        PAR[PM_PAR_YPOS] = (float) (NUM_ROWS/2);
+        PAR[PM_PAR_SXX] = 1.0;
+        PAR[PM_PAR_SYY] = 1.0;
+
+        // NULL psImage input parameter
+        bool rc = pmModelSub(NULL, mask, model, PM_MODEL_OP_FUNC, 1);
+        ok(rc == false, "pmModelSub() returned FALSE with NULL psImage input parameter");
+
+        // NULL pmModel input parameter
+        rc = pmModelSub(img, mask, NULL, PM_MODEL_OP_FUNC, 1);
+        ok(rc == false, "pmModelSub() returned FALSE with NULL pmModel input parameter");
+
+        // Incorrect type psImage input parameter
+        rc = pmModelSub(imgS32, mask, model, PM_MODEL_OP_FUNC, 1);
+        ok(rc == false, "pmModelSub() returned FALSE with Incorrect type psImage input parameter");
+
+        psFree(img);
+        psFree(imgS32);
+        psFree(mask);
+        psFree(model);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmModelSub() with acceptable parameters
+    // We only test with a single Gaussian model, with no residuals or masks.
+    // For completeness, additional tests should be added.
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psImage *mask = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_U8);
+        for (int i = 0 ; i < NUM_ROWS ; i++) {
+            for (int j = 0 ; j < NUM_COLS ; j++) {
+                img->data.F32[i][j] = 0.0;
+                mask->data.U8[i][j] = 0;
+            }
+        }
+        pmModel *model = pmModelAlloc(pmModelClassGetType("PS_MODEL_GAUSS"));
+        psF32 *PAR = model->params->data.F32;
+        PAR[PM_PAR_I0] = 5.0;
+        PAR[PM_PAR_XPOS] = 0.0;
+        PAR[PM_PAR_YPOS] = 0.0;
+        PAR[PM_PAR_XPOS] = (float) (NUM_COLS/2);
+        PAR[PM_PAR_YPOS] = (float) (NUM_ROWS/2);
+        PAR[PM_PAR_SXX] = 10.0;
+        PAR[PM_PAR_SYY] = 10.0;
+
+        bool rc = pmModelSub(img, mask, model, PM_MODEL_OP_FUNC, 1);
+        ok(rc == true, "pmModelSub() returned TRUE with acceptable input parameters");
+        psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+        bool errorFlag = false;
+        for (int i = 0 ; i < NUM_ROWS ; i++) {
+            for (int j = 0 ; j < NUM_COLS ; j++) {
+                x->data.F32[0] = (float) j;
+                x->data.F32[1] = (float) i;
+                psF32 modF = model->modelFunc (NULL, model->params, x);
+                psF32 imgF = img->data.F32[i][j];
+                if (!TEST_FLOATS_EQUAL(modF, -imgF)) {
+                    diag("ERROR: img[%d][%d] is %.2f, should be %.2f\n", i, j, img->data.F32[i][j], modF);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmModelSub() set the image pixels correctly");
+        psFree(x);
+        psFree(img);
+        psFree(mask);
+        psFree(model);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // XXX: The source code for pmModelAddWithOffset() and pmModelSubWithOffset() is
+    // almost exactly the same as the source code for pmModelAdd() and pmModelSub().
+    // We do not test them here.
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmModelClass.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmModelClass.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmModelClass.c	(revision 22322)
@@ -0,0 +1,181 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested except pmModelClassCleanup() which is deferred
+    because there's no way to test that it frees a static variable, except
+    throug hthe memory leak tests
+*/
+
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           (8+1)
+#define TEST_NUM_COLS           (8+1)
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#include "models/pmModel_GAUSS.c"
+#include "models/pmModel_PGAUSS.c"
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(37);
+
+    // ----------------------------------------------------------------------
+    // Test pmModelClassAlloc()
+    // pmModelClass *pmModelClassAlloc (int nModels)
+    {
+        psMemId id = psMemGetId();
+        pmModelClass *modelClass = pmModelClassAlloc(4);
+        ok(modelClass != NULL && psMemCheckModelClass(modelClass), "pmModelClassAlloc() returned a non-NULL pmModelClass");
+        psFree(modelClass);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Test pmModelClassCleanup(), pmModelClassSelect()
+    // Basically, call pmModelClassInit(), then pmModelClassCleanup(), and ensure that
+    // various default models are not there.
+    // XXX: We don't run this test because the spec changed and pmModelClassSelect() now calls
+    // pmModelClassInit().
+    if (0) {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModelClassCleanup();
+        ok(NULL == pmModelClassSelect(0), "pmModelClassCleanup(): removed PS_MODEL_GAUSS");
+        ok(NULL == pmModelClassSelect(1), "pmModelClassCleanup(): removed PS_MODEL_PGAUSS");
+        ok(NULL == pmModelClassSelect(2), "pmModelClassCleanup(): removed PS_MODEL_QGAUSS");
+        ok(NULL == pmModelClassSelect(3), "pmModelClassCleanup(): removed PS_MODEL_RGAUSS");
+        ok(NULL == pmModelClassSelect(4), "pmModelClassCleanup(): removed PS_MODEL_SERSIC");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Test pmModelClassInit(), pmModelClassGetName()
+    // Basically, call pmModelClassCleanup(), then pmModelClassInit() and ensure that
+    // various default models are there.
+    {
+        psMemId id = psMemGetId();
+
+        pmModelClassCleanup();
+        ok(pmModelClassInit(), "pmModelClassInit() returned TRUE");
+        ok(!strcmp(pmModelClassGetName(0), "PS_MODEL_GAUSS"), "pmModelClassInit() added pmModel PS_MODEL_GAUSS");
+        ok(!strcmp(pmModelClassGetName(1), "PS_MODEL_PGAUSS"), "pmModelClassInit() added pmModel PS_MODEL_PGAUSS");
+        ok(!strcmp(pmModelClassGetName(2), "PS_MODEL_QGAUSS"), "pmModelClassInit() added pmModel PS_MODEL_QGAUSS");
+        ok(!strcmp(pmModelClassGetName(3), "PS_MODEL_RGAUSS"), "pmModelClassInit() added pmModel PS_MODEL_RGAUSS");
+        ok(!strcmp(pmModelClassGetName(4), "PS_MODEL_SERSIC"), "pmModelClassInit() added pmModel PS_MODEL_SERSIC");
+        ok(!pmModelClassInit(), "pmModelClassInit() returned FALSE (2nd time)");
+        ok(NULL == pmModelClassGetName(-1), "pmModelClassGetName(-1) returned NULL");
+        ok(NULL == pmModelClassGetName(1000), "pmModelClassGetName(1000) returned NULL");
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Test pmModelClassGetType()
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        ok(-1 == pmModelClassGetType("BOGUS"), "pmModelClassGetType(BOGUS) returned -1");
+        ok(0 == pmModelClassGetType("PS_MODEL_GAUSS"), "pmModelClassGetType(PS_MODEL_GAUSS) successful");
+        ok(1 == pmModelClassGetType("PS_MODEL_PGAUSS"), "pmModelClassGetType(PS_MODEL_PGAUSS) successful");
+        ok(2 == pmModelClassGetType("PS_MODEL_QGAUSS"), "pmModelClassGetType(PS_MODEL_QGAUSS) successful");
+        ok(3 == pmModelClassGetType("PS_MODEL_RGAUSS"), "pmModelClassGetType(PS_MODEL_RGAUSS) successful");
+        ok(4 == pmModelClassGetType("PS_MODEL_SERSIC"), "pmModelClassGetType(PS_MODEL_SERSIC) successful");
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Test pmModelClassParameterCount()
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        ok(0 == pmModelClassParameterCount(-1), "pmModelClassParameterCount(-1) returned 0");
+        ok(0 == pmModelClassParameterCount(1000), "pmModelClassParameterCount(1000) returned 0");
+        ok(7 == pmModelClassParameterCount(0), "pmModelClassParameterCount(0) returned 7");
+        ok(7 == pmModelClassParameterCount(1), "pmModelClassParameterCount(1) returned 7");
+        ok(8 == pmModelClassParameterCount(2), "pmModelClassParameterCount(2) returned 8");
+        ok(8 == pmModelClassParameterCount(3), "pmModelClassParameterCount(3) returned 8");
+        ok(8 == pmModelClassParameterCount(4), "pmModelClassParameterCount(4) returned 8");
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Test pmModelClassSelect()
+    // pmModelClass *pmModelClassSelect (pmModelType type)
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModelClass *model = NULL;
+        model = pmModelClassSelect(-1);
+        ok(model == NULL, "pmModelClassSelect(-1) successful");
+        model = pmModelClassSelect(1000);
+        ok(model == NULL, "pmModelClassSelect(1000) successful");
+        model = pmModelClassSelect(0);
+        ok(model != NULL && !strcmp(model->name, "PS_MODEL_GAUSS"), "pmModelClassSelect(0) successful");
+        model = pmModelClassSelect(1);
+        ok(model != NULL && !strcmp(model->name, "PS_MODEL_PGAUSS"), "pmModelClassSelect(1) successful");
+        model = pmModelClassSelect(2);
+        ok(model != NULL && !strcmp(model->name, "PS_MODEL_QGAUSS"), "pmModelClassSelect(2) successful");
+        model = pmModelClassSelect(3);
+        ok(model != NULL && !strcmp(model->name, "PS_MODEL_RGAUSS"), "pmModelClassSelect(3) successful");
+        model = pmModelClassSelect(4);
+        ok(model != NULL && !strcmp(model->name, "PS_MODEL_SERSIC"), "pmModelClassSelect(4) successful");
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Test pmModelClassAdd()
+    // We create a new modelClass, then add it, then ensure that it exists.
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModelClass *newModel = pmModelClassAlloc(1);
+        newModel->name = psStringCopy("PS_MODEL_NEW00");
+        newModel->nParams = 22;
+        pmModelClassAdd(newModel);
+        int ID = pmModelClassGetType("PS_MODEL_NEW00");
+        ok(ID != -1 && !strcmp("PS_MODEL_NEW00", pmModelClassGetName(ID)), "pmModelClassAdd() added the new model successfully");
+        psFree(newModel->name);
+        psFree(newModel);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmModelUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmModelUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmModelUtils.c	(revision 22322)
@@ -0,0 +1,203 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+	All functions are tested.
+*/
+
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define NUM_ROWS		8
+#define NUM_COLS		16
+#define TEST_MODEL_CLASS_TYPE 1
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.0001)
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(46);
+
+    // ----------------------------------------------------------------------
+    // pmModelFromPSF() tests
+    // pmModel *pmModelFromPSF (pmModel *modelEXT, pmPSF *psf)
+    // call pmModelFromPSF() with NULL pmModel input parameter
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        pmPSFOptions *psfOptions = pmPSFOptionsAlloc();
+        psfOptions->type = pmModelClassGetType("PS_MODEL_GAUSS");
+        pmPSF *psf = pmPSFAlloc(psfOptions);
+        pmModel *tmpModel = pmModelFromPSF(NULL, psf);
+        ok(tmpModel == NULL, "pmModelFromPSF() returned NULL with NULL pmModel input parameter");
+        psFree(model);
+        psFree(psfOptions);
+        psFree(psf);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmModelFromPSF() with NULL pmPSF input parameter
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        pmPSFOptions *psfOptions = pmPSFOptionsAlloc();
+        psfOptions->type = pmModelClassGetType("PS_MODEL_GAUSS");
+        pmPSF *psf = pmPSFAlloc(psfOptions);
+        pmModel *tmpModel = pmModelFromPSF(model, NULL);
+        ok(tmpModel == NULL, "pmModelFromPSF() returned NULL with NULL pmPSF input parameter");
+        psFree(model);
+        psFree(psfOptions);
+        psFree(psf);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmModelFromPSF() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        pmPSFOptions *psfOptions = pmPSFOptionsAlloc();
+        psfOptions->type = pmModelClassGetType("PS_MODEL_GAUSS");
+        pmPSF *psf = pmPSFAlloc(psfOptions);
+        pmModel *tmpModel = pmModelFromPSF(model, psf);
+        ok(tmpModel != NULL, "pmModelFromPSF() returned non-NULL with acceptable input parameters");
+
+        pmModel *testModelPSF = pmModelAlloc(psf->type);
+        model->modelFromPSF(testModelPSF, model, psf);
+        ok(tmpModel->type == testModelPSF->type, "pmModelFromPSF() set the model->type correctly");
+        ok(tmpModel->chisq == testModelPSF->chisq, "pmModelFromPSF() set the model->chisq correctly");
+        ok(tmpModel->chisqNorm == testModelPSF->chisqNorm, "pmModelFromPSF() set the model->chisqNorm correctly");
+        ok(tmpModel->nDOF == testModelPSF->nDOF, "pmModelFromPSF() set the model->nDOF correctly");
+        ok(tmpModel->nIter == testModelPSF->nIter, "pmModelFromPSF() set the model->nIter correctly");
+        ok(tmpModel->flags == testModelPSF->flags, "pmModelFromPSF() set the model->flags correctly");
+        ok(tmpModel->radiusFit == testModelPSF->radiusFit, "pmModelFromPSF() set the model->radiusFit correctly");
+        ok(tmpModel->modelFunc == testModelPSF->modelFunc, "pmModelFromPSF() set the model->modelFunc correctly");
+        ok(tmpModel->modelFlux == testModelPSF->modelFlux, "pmModelFromPSF() set the model->modelFlux correctly");
+        ok(tmpModel->modelRadius == testModelPSF->modelRadius, "pmModelFromPSF() set the model->modelRadius correctly");
+        ok(tmpModel->modelLimits == testModelPSF->modelLimits, "pmModelFromPSF() set the model->modelLimits correctly");
+        ok(tmpModel->modelGuess == testModelPSF->modelGuess, "pmModelFromPSF() set the model->modelGuess correctly");
+        ok(tmpModel->modelFromPSF == testModelPSF->modelFromPSF, "pmModelFromPSF() set the model->modelFromPSF correctly");
+        ok(tmpModel->modelParamsFromPSF == testModelPSF->modelParamsFromPSF, "pmModelFromPSF() set the model->modelParamsFromPSF correctly");
+        ok(tmpModel->modelFitStatus == testModelPSF->modelFitStatus, "pmModelFromPSF() set the model->modelFitStatus correctly");
+
+        psFree(testModelPSF);
+        psFree(tmpModel);
+        psFree(model);
+        psFree(psfOptions);
+        psFree(psf);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmModelFromPSFforXY() tests
+    // pmModel *pmModelFromPSFforXY (pmPSF *psf, float Xo, float Yo, float Io)
+    // call pmModelFromPSFforXY() with NULL pmPSF input parameter
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmPSFOptions *psfOptions = pmPSFOptionsAlloc();
+        psfOptions->type = pmModelClassGetType("PS_MODEL_GAUSS");
+        pmPSF *psf = pmPSFAlloc(psfOptions);
+        pmModel *tmpModel = pmModelFromPSFforXY(NULL, 1.0, 2.0, 3.0);
+        ok(tmpModel == NULL, "pmModelFromPSFforXY() returned NULL with NULL pmPSF input parameter");
+        psFree(psfOptions);
+        psFree(psf);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmModelFromPSFforXY() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmPSFOptions *psfOptions = pmPSFOptionsAlloc();
+        psfOptions->type = pmModelClassGetType("PS_MODEL_GAUSS");
+        pmPSF *psf = pmPSFAlloc(psfOptions);
+
+        pmModel *testModelPSF = pmModelAlloc (psf->type);
+        testModelPSF->modelParamsFromPSF(testModelPSF, psf, 1.0, 2.0, 3.0);
+
+        pmModel *tmpModel = pmModelFromPSFforXY(psf, 1.0, 2.0, 3.0);
+        ok(tmpModel != NULL, "pmModelFromPSFforXY() returned non-NULL with acceptable input parameters");
+
+        ok(tmpModel->type == testModelPSF->type, "pmModelFromPSF() set the model->type correctly");
+        ok(tmpModel->chisq == testModelPSF->chisq, "pmModelFromPSF() set the model->chisq correctly");
+        ok(TEST_FLOATS_EQUAL(tmpModel->chisqNorm, testModelPSF->chisqNorm), "pmModelFromPSF() set the model->chisqNorm correctly");
+        ok(tmpModel->nDOF == testModelPSF->nDOF, "pmModelFromPSF() set the model->nDOF correctly");
+        ok(tmpModel->nIter == testModelPSF->nIter, "pmModelFromPSF() set the model->nIter correctly");
+        ok(tmpModel->flags == testModelPSF->flags, "pmModelFromPSF() set the model->flags correctly");
+        ok(tmpModel->radiusFit == testModelPSF->radiusFit, "pmModelFromPSF() set the model->radiusFit correctly");
+        ok(tmpModel->modelFunc == testModelPSF->modelFunc, "pmModelFromPSF() set the model->modelFunc correctly");
+        ok(tmpModel->modelFlux == testModelPSF->modelFlux, "pmModelFromPSF() set the model->modelFlux correctly");
+        ok(tmpModel->modelRadius == testModelPSF->modelRadius, "pmModelFromPSF() set the model->modelRadius correctly");
+        ok(tmpModel->modelLimits == testModelPSF->modelLimits, "pmModelFromPSF() set the model->modelLimits correctly");
+        ok(tmpModel->modelGuess == testModelPSF->modelGuess, "pmModelFromPSF() set the model->modelGuess correctly");
+        ok(tmpModel->modelFromPSF == testModelPSF->modelFromPSF, "pmModelFromPSF() set the model->modelFromPSF correctly");
+        ok(tmpModel->modelParamsFromPSF == testModelPSF->modelParamsFromPSF, "pmModelFromPSF() set the model->modelParamsFromPSF correctly");
+        ok(tmpModel->modelFitStatus == testModelPSF->modelFitStatus, "pmModelFromPSF() set the model->modelFitStatus correctly");
+
+        psFree(tmpModel);
+        psFree(testModelPSF);
+        psFree(psfOptions);
+        psFree(psf);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmModelSetFlux() tests
+    // bool pmModelSetFlux(pmModel *model, float flux) {
+    // call pmModelSetFlux() with NULL pmPSF input parameter
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        bool rc = pmModelSetFlux(NULL, 1.0);
+        ok(!rc, "pmModelSetFlux(() returned FALSE with NULL pmModel input parameter");
+        psFree(model);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmModelSetFlux() with acceptable input parameters
+    // XXX: We should probably test with more input values
+    {
+        psMemId id = psMemGetId();
+        pmModelClassInit();
+        pmModel *model = pmModelAlloc(TEST_MODEL_CLASS_TYPE);
+        model->params->data.F32[PM_PAR_SXX] = 1.0;
+        model->params->data.F32[PM_PAR_SYY] = 1.0;
+        model->params->data.F32[PM_PAR_SXY] = 1.0;
+
+        // Compute the test flux value
+        float tmpF = model->params->data.F32[PM_PAR_I0];
+        model->params->data.F32[PM_PAR_I0] = 1.0;
+        float testFlux = model->modelFlux (model->params);
+        testFlux = 1.0 / testFlux;    
+        model->params->data.F32[PM_PAR_I0] = tmpF;
+
+        bool rc = pmModelSetFlux(model, 1.0);
+        ok(rc, "pmModelSetFlux(() returned TRUE with acceptable input parameters");
+        ok(TEST_FLOATS_EQUAL(testFlux, model->params->data.F32[PM_PAR_I0]), "pmModelSetFlux() set the flux correctly");
+        psFree(model);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmMoments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmMoments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmMoments.c	(revision 22322)
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(11);
+
+    // Test pmMomentsAlloc()
+    {
+        psMemId id = psMemGetId();
+        pmMoments *tmpMoments = pmMomentsAlloc();
+        ok(tmpMoments != NULL, "pmMomentsAlloc() returned a non-NULL pmMoments");
+        skip_start(tmpMoments == NULL, 9, "Skipping tests because pmMomentsAlloc() returned NULL");
+        ok(tmpMoments->x == 0.0, "pmMomentsAlloc set->x correctly");
+        ok(tmpMoments->y == 0.0, "pmMomentsAlloc set->y correctly");
+        ok(tmpMoments->Sx == 0.0, "pmMomentsAlloc set->Sx correctly");
+        ok(tmpMoments->Sy == 0.0, "pmMomentsAlloc set->Sy correctly");
+        ok(tmpMoments->Sxy == 0.0, "pmMomentsAlloc set->Sxy correctly");
+        ok(tmpMoments->Sum == 0.0, "pmMomentsAlloc set->Sum correctly");
+        ok(tmpMoments->Peak == 0.0, "pmMomentsAlloc set->Peak correctly");
+        ok(tmpMoments->Sky == 0.0, "pmMomentsAlloc set->Sky correctly");
+        ok(tmpMoments->nPixels == 0, "pmMomentsAlloc set->nPixels correctly");
+        psFree(tmpMoments);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPSF.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPSF.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPSF.c	(revision 22322)
@@ -0,0 +1,326 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/*
+Tested:
+    pmPSFOptions *pmPSFOptionsAlloc()
+    pmPSF *pmPSFAlloc (pmPSFOptions *options)
+    bool psMemCheckPSF(psPtr ptr)
+Must test:
+    double pmPSF_SXYfromModel (psF32 *modelPar)
+    double pmPSF_SXYtoModel (psF32 *fittedPar)
+    bool pmGrowthCurveGenerate (pmReadout *readout, pmPSF *psf, bool ignore, psMas!
+    pmPSF *pmPSFBuildSimple (char *typeName, float sxx, float syy, float sxy, ...)
+    bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes)
+    bool pmPSF_FitToModel (psF32 *fittedPar, float minMinorAxis)
+    psEllipsePol pmPSF_ModelToFit (psF32 *modelPar)
+    psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR)
+*/
+
+
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         10
+#define TEST_FLOATS_EQUAL(X, Y) (abs((X) - (Y)) < 0.0001)
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(83);
+
+    // ----------------------------------------------------------------------
+    // pmPSFOptionsAlloc() tests
+    // pmPSFOptions *pmPSFOptionsAlloc()
+    {
+        psMemId id = psMemGetId();
+        pmPSFOptions *psfOptions = pmPSFOptionsAlloc();
+        ok(psfOptions != NULL && psMemCheckPSFOptions(psfOptions), "pmPSFOptionsAlloc() returned non-NULL");
+        ok(psfOptions->type == 0, "pmPSFOptionsAlloc set pmPSFOptions->type ");
+        ok(psfOptions->stats == NULL, "pmPSFOptionsAlloc set pmPSFOptions->stats ");
+        ok(psfOptions->psfTrendMode == PM_TREND_NONE, "pmPSFOptionsAlloc set pmPSFOptions->psfTrendMode ");
+        ok(psfOptions->psfTrendNx == 0, "pmPSFOptionsAlloc set pmPSFOptions->psfTrendNx ");
+        ok(psfOptions->psfTrendNy == 0, "pmPSFOptionsAlloc set pmPSFOptions->psfTrendNy ");
+        ok(psfOptions->psfFieldNx == 0, "pmPSFOptionsAlloc set pmPSFOptions->psfFieldNx ");
+        ok(psfOptions->psfFieldNy == 0, "pmPSFOptionsAlloc set pmPSFOptions->psfFieldNy ");
+        ok(psfOptions->psfFieldXo == 0, "pmPSFOptionsAlloc set pmPSFOptions->psfFieldXo ");
+        ok(psfOptions->psfFieldYo == 0, "pmPSFOptionsAlloc set pmPSFOptions->psfFieldYo ");
+        ok(psfOptions->poissonErrorsPhotLMM == true, "pmPSFOptionsAlloc set pmPSFOptions->poissonErrorsPhotLMM ");
+        ok(psfOptions->poissonErrorsPhotLin == false, "pmPSFOptionsAlloc set pmPSFOptions->poissonErrorsPhotLin ");
+        ok(psfOptions->poissonErrorsParams  == true, "pmPSFOptionsAlloc set pmPSFOptions->poissonErrorsParams ");
+        psFree(psfOptions);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmPSFAlloc() tests
+    // call pmPSFAlloc() with NULL input parameters
+    #define TEST_POISSON_ERRORS true
+    {
+        psMemId id = psMemGetId();
+        pmPSF *psf = pmPSFAlloc(NULL);
+        ok(psf == NULL, "pmPSFAlloc() returned NULL with NULL input parameters");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmPSFAlloc() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmPSFOptions *psfOptions = pmPSFOptionsAlloc();
+        psfOptions->psfTrendNx = 1;
+        psfOptions->psfTrendNy = 2;
+        psfOptions->psfFieldNx = 3;
+        psfOptions->psfFieldNy = 4;
+        psfOptions->psfFieldXo = 5;
+        psfOptions->psfFieldYo = 6;
+        pmModelClassInit();
+        psfOptions->type = pmModelClassGetType("PS_MODEL_GAUSS");
+        pmPSF *psf = pmPSFAlloc(psfOptions);
+        ok(psf != NULL && psMemCheckPSF(psf), "pmPSFAlloc() returned non-NULL");
+        ok(psf->type == psfOptions->type, "pmPSFAlloc() set the pmPSF->type correctly");
+        ok(psf->chisq == 0.0, "pmPSFAlloc() set the pmPSF->chisq correctly");
+        ok(psf->ApResid == 0.0, "pmPSFAlloc() set the pmPSF->ApResid correctly");
+        ok(psf->dApResid == 0.0, "pmPSFAlloc() set the pmPSF->dApResid correctly");
+        ok(psf->skyBias == 0.0, "pmPSFAlloc() set the pmPSF->skyBias correctly");
+        ok(psf->skySat == 0.0, "pmPSFAlloc() set the pmPSF->skySat correctly");
+        ok(psf->nPSFstars == 0, "pmPSFAlloc() set the pmPSF->nPSFstars correctly");
+        ok(psf->nApResid == 0, "pmPSFAlloc() set the pmPSF->nApResid correctly");
+        int Nparams = pmModelClassParameterCount(psfOptions->type);
+        ok(psf->params != NULL &&
+           psMemCheckArray(psf->params) &&
+           psf->params->n == Nparams, "pmPSFAlloc() set the pmPSF->params correctly");
+        ok(psf->poissonErrorsPhotLMM == psfOptions->poissonErrorsPhotLMM, "pmPSFAlloc() set the pmPSF->poissonErrorsPhotLMM");
+        ok(psf->poissonErrorsPhotLin == psfOptions->poissonErrorsPhotLin, "pmPSFAlloc() set the pmPSF->poissonErrorsPhotLin");
+        ok(psf->poissonErrorsParams == psfOptions->poissonErrorsParams, "pmPSFAlloc() set the pmPSF->poissonErrorsParams");
+        ok(psf->ApTrend == NULL, "pmPSFAlloc() set the pmPSF->ApTrend");
+        ok(psf->FluxScale == NULL, "pmPSFAlloc() set the pmPSF->FluxScale");
+        ok(psf->growth == NULL, "pmPSFAlloc() set the pmPSF->growth");
+        ok(psf->residuals == NULL, "pmPSFAlloc() set the pmPSF->residuals");
+        ok(psf->psfTrendMode == psfOptions->psfTrendMode, "pmPSFAlloc() set the pmPSF->psfTrendMode");
+        ok(psf->trendNx == psfOptions->psfTrendNx, "pmPSFAlloc() set the pmPSF->trendNx (%d %d)", psf->trendNx, psfOptions->psfTrendNx);
+        ok(psf->trendNy == psfOptions->psfTrendNy, "pmPSFAlloc() set the pmPSF->trendNy (%d %d)", psf->trendNy, psfOptions->psfTrendNy);
+        ok(psf->fieldNx == psfOptions->psfFieldNx, "pmPSFAlloc() set the pmPSF->fieldNx");
+        ok(psf->fieldNy == psfOptions->psfFieldNy, "pmPSFAlloc() set the pmPSF->fieldNy");
+        ok(psf->fieldXo == psfOptions->psfFieldXo, "pmPSFAlloc() set the pmPSF->fieldXo");
+        ok(psf->fieldYo == psfOptions->psfFieldYo, "pmPSFAlloc() set the pmPSF->fieldYo");
+        ok(psf->ApTrend == NULL, "pmPSFAlloc() set the pmPSF->ApTrend correctly");
+        ok(psf->FluxScale == NULL, "pmPSFAlloc() set the pmPSF->FluxScale correctly");
+
+        if (psf->poissonErrorsPhotLMM) {
+            ok(psf->ChiTrend->nX == 1, "pmPSFAlloc() set the pmPSF->ChiTrend correctly");
+	} else {
+            ok(psf->ChiTrend->nX == 2, "pmPSFAlloc() set the pmPSF->ChiTrend correctly");
+	}
+        ok(psf->growth == NULL, "pmPSFAlloc() set the pmPSF->growth correctly");
+        ok(psf->residuals == NULL, "pmPSFAlloc() set the pmPSF->residuals correctly");
+        ok(psf->params != NULL && psMemCheckArray(psf->params) && psf->params->n == Nparams, 
+           "pmPSFAlloc() set the pmPSF->params psVector correctly");
+
+        pmModelClassCleanup();
+        psFree(psf);
+        psFree(psfOptions);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmPSF_SXYfromModel() tests
+    // Call pmPSF_SXYfromModel() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        double tmpD = pmPSF_SXYfromModel(NULL);
+        ok(isnan(tmpD), "pmPSF_SXYfromModel() returned NULL with NULL input parameters");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSF_SXYfromModel() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        psF32 modelPar[20];
+        modelPar[PM_PAR_SXX] = 2.0;
+        modelPar[PM_PAR_SYY] = 3.0;
+        modelPar[PM_PAR_SXY] = 5.0;
+        double SXX = modelPar[PM_PAR_SXX];
+        double SYY = modelPar[PM_PAR_SYY];
+        double SXY = modelPar[PM_PAR_SXY];
+        psF32 verF = SXY / PS_SQR(1.0 / PS_SQR(SXX) + 1.0 / PS_SQR(SYY));
+        psF32 testF = pmPSF_SXYfromModel(modelPar);
+        ok(verF == testF, "pmPSF_SXYfromModel() calculated correctly");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmPSF_SXYtoModel() tests
+    // Call pmPSF_SXYtoModel() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        double tmpD = pmPSF_SXYtoModel(NULL);
+        ok(isnan(tmpD), "pmPSF_SXYtoModel() returned NULL with NULL input parameters");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSF_SXYtoModel() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        psF32 fittedPar[20];
+        fittedPar[PM_PAR_SXX] = 2.0;
+        fittedPar[PM_PAR_SYY] = 3.0;
+        fittedPar[PM_PAR_SXY] = 5.0;
+        double SXX = fittedPar[PM_PAR_SXX];
+        double SYY = fittedPar[PM_PAR_SYY];
+        double fit = fittedPar[PM_PAR_SXY];
+        double verF = fit * PS_SQR(1.0 / PS_SQR(SXX) + 1.0 / PS_SQR(SYY));
+        psF32 testF = pmPSF_SXYtoModel(fittedPar);
+        ok(TEST_FLOATS_EQUAL(verF, testF), "pmPSF_SXYtoModel() calculated correctly (%f %f)", verF, testF);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmPSF_FitToModel() tests
+    // Call pmPSF_FitToModel() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmPSF_FitToModel(NULL, 0.0);
+        ok(rc == false, "pmPSF_FitToModel() returned NULL with NULL input parameters");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSF_FitToModel() with NULL input parameters
+    {
+        #define MIN_MINOR_AXIS 1.0
+        psMemId id = psMemGetId();
+        psF32 origFittedPar[3], testFittedPar[3];
+        psEllipsePol pol;
+        pol.e0 = origFittedPar[PM_PAR_E0] = testFittedPar[PM_PAR_E0] = 2.0;
+        pol.e1 = origFittedPar[PM_PAR_E1] = testFittedPar[PM_PAR_E1] = 3.0;
+        pol.e2 = origFittedPar[PM_PAR_E2] = testFittedPar[PM_PAR_E2] = 5.0;
+        ok(pmPSF_FitToModel(testFittedPar, MIN_MINOR_AXIS), "pmPSF_FitToModel() returned TRUE with acceptable input parameters");
+
+        psEllipseAxes axes;
+        psEllipsePolToAxes(pol, MIN_MINOR_AXIS, &axes);
+        psEllipseShape shape = psEllipseAxesToShape(axes);
+
+        ok(TEST_FLOATS_EQUAL(testFittedPar[PM_PAR_SXX], shape.sx * M_SQRT2),
+          "pmPSF_FitToModel() set fittedPar[PM_PAR_SXX] correctly");
+        ok(TEST_FLOATS_EQUAL(testFittedPar[PM_PAR_SYY], shape.sy * M_SQRT2),
+          "pmPSF_FitToModel() set fittedPar[PM_PAR_SYY] correctly");
+        ok(TEST_FLOATS_EQUAL(testFittedPar[PM_PAR_SXY], shape.sxy),
+          "pmPSF_FitToModel() set fittedPar[PM_PAR_SXY] correctly");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmPSF_ModelToFit() tests
+    // psEllipsePol pmPSF_ModelToFit (psF32 *modelPar)
+    // Call pmPSF_ModelToFit() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        psEllipsePol pol = pmPSF_ModelToFit(NULL);
+        ok(isnan(pol.e0), "pmPSF_ModelToFit() returned NULL (psEllipsePol) with NULL input parameters");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSF_ModelToFit() with NULL input parameters
+    {
+        #define MIN_MINOR_AXIS 1.0
+        psMemId id = psMemGetId();
+        psF32 modelPar[3];
+        modelPar[PM_PAR_SXX] = 2.0;
+        modelPar[PM_PAR_SYY] = 3.0;
+        modelPar[PM_PAR_SXY] = 5.0;
+
+        psEllipsePol pol = pmPSF_ModelToFit(modelPar);
+        ok(!isnan(pol.e0), "pmPSF_ModelToFit() returned TRUE with acceptable input parameters");
+
+        psEllipseShape shape;
+        shape.sx  = modelPar[PM_PAR_SXX] / M_SQRT2;
+        shape.sy  = modelPar[PM_PAR_SYY] / M_SQRT2;
+        shape.sxy = modelPar[PM_PAR_SXY];
+        psEllipsePol actPol = psEllipseShapeToPol(shape);
+        ok(TEST_FLOATS_EQUAL(pol.e0, actPol.e0), "pmPSF_ModelToFit() set psEllipsePol.e0 correctly");
+        ok(TEST_FLOATS_EQUAL(pol.e1, actPol.e1), "pmPSF_ModelToFit() set psEllipsePol.e1 correctly");
+        ok(TEST_FLOATS_EQUAL(pol.e2, actPol.e2), "pmPSF_ModelToFit() set psEllipsePol.e2 correctly");
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmPSF_ModelToAxes() tests
+    // psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR)
+    // Call pmPSF_ModelToAxes() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        psEllipseAxes axes = pmPSF_ModelToAxes(NULL, 1.0);
+        ok(isnan(axes.major), "pmPSF_ModelToAxes() returned NULL (psEllipseAxes) with NULL input parameters");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSF_ModelToAxes() with NULL input parameters
+    {
+        #define MAX_AX 1.0
+        psMemId id = psMemGetId();
+        psF32 modelPar[3];
+        modelPar[PM_PAR_SXX] = 2.0;
+        modelPar[PM_PAR_SYY] = 3.0;
+        modelPar[PM_PAR_SXY] = 5.0;
+
+        psEllipseShape shape;
+        shape.sx  = modelPar[PM_PAR_SXX] / M_SQRT2;
+        shape.sy  = modelPar[PM_PAR_SYY] / M_SQRT2;
+        shape.sxy = modelPar[PM_PAR_SXY];
+        psEllipseAxes axes = psEllipseShapeToAxes (shape, MAX_AX);
+
+        psEllipseAxes actAxes = pmPSF_ModelToAxes(modelPar, MAX_AX);
+        ok(!isnan(actAxes.major), "pmPSF_ModelToAxes() returned TRUE with acceptable input parameters");
+        ok(TEST_FLOATS_EQUAL(actAxes.major, axes.major), "pmPSF_ModelToAxes() set psEllipseAxes.major correctly");
+        ok(TEST_FLOATS_EQUAL(actAxes.minor, axes.minor), "pmPSF_ModelToAxes() set psEllipseAxes.minor correctly");
+        ok(TEST_FLOATS_EQUAL(actAxes.theta, axes.theta), "pmPSF_ModelToAxes() set psEllipseAxes.theta correctly");
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmPSF_AxesToModel() tests
+    // bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes)
+    // Call pmPSF_AxesToModel() with NULL input parameters
+    {
+        psMemId id = psMemGetId();
+        psEllipseAxes axes;
+        bool rc = pmPSF_AxesToModel(NULL, axes);
+        ok(rc == false, "pmPSF_AxesToModel() returned NULL with NULL input parameters");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSF_AxesToModel() with NULL input parameters
+    {
+        #define MIN_MINOR_AXIS 1.0
+        psMemId id = psMemGetId();
+        psF32 modelPar[3];
+        psEllipseAxes axes;
+        axes.major = 2.0;
+        axes.minor = 3.0;
+        axes.theta = 5.0;
+        ok(pmPSF_AxesToModel(modelPar, axes), "pmPSF_AxesToModel() returned TRUE with acceptable input parameters");
+        psEllipseShape shape = psEllipseAxesToShape(axes);
+        ok(TEST_FLOATS_EQUAL(modelPar[PM_PAR_SXX], shape.sx * M_SQRT2), "pmPSF_AxesToModel() set modelPar[PM_PAR_SXX] correctly");
+        ok(TEST_FLOATS_EQUAL(modelPar[PM_PAR_SYY], shape.sy * M_SQRT2), "pmPSF_AxesToModel() set modelPar[PM_PAR_SYY] correctly");
+        ok(TEST_FLOATS_EQUAL(modelPar[PM_PAR_SXY], shape.sxy), "pmPSF_AxesToModel() set modelPar[PM_PAR_SXY] correctly");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPSF_IO.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPSF_IO.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPSF_IO.c	(revision 22322)
@@ -0,0 +1,467 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS
+    Uder construction.  Only lightly tested so far.
+*/
+
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.0001)
+#define NUM_MODELS		5
+
+#define CHIP_ALLOC_NAME        "ChipName"
+#define CELL_ALLOC_NAME        "CellName"
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define MISC_NAME2             "META01"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           4
+#define TEST_NUM_COLS           4
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_CHIPS               8
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+psPlaneTransform *PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM()
+{
+    psPlaneTransform *pt = psPlaneTransformAlloc(1, 1);
+    pt->x->coeff[1][0] = 1.0;
+    pt->y->coeff[0][1] = 1.0;
+    return(pt);
+}
+
+psPlaneDistort *PS_CREATE_4D_IDENTITY_PLANE_DISTORT()
+{
+    psPlaneDistort *pd = psPlaneDistortAlloc(1, 1, 1, 1);
+    pd->x->coeff[1][0][0][0] = 1.0;
+    pd->y->coeff[0][1][0][0] = 1.0;
+    return(pd);
+}
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = psMemDecrRefCounter((psPtr) generateSimpleReadout(cell));
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    //XXX: Should the region be set some other way?  Like through the various config files?
+//    psRegion *region = psRegionAlloc(0.0, TEST_NUM_COLS-1, 0.0, TEST_NUM_ROWS-1);
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+/******************************************************************************
+generateSimpleChip(): This function generates a pmChip data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmChip *generateSimpleChip(pmFPA *fpa)
+{
+    pmChip *chip = pmChipAlloc(fpa, CHIP_ALLOC_NAME);
+    chip->toFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    chip->fromFPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    psMetadataAddS32(chip->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(chip->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(chip->cells, NUM_CELLS);
+    for (int i = 0 ; i < NUM_CELLS ; i++) {
+        chip->cells->data[i] = psMemDecrRefCounter((psPtr) generateSimpleCell(chip));
+    }
+
+    // XXX: Add code to initialize chip pmConcepts
+
+
+    return(chip);
+}
+
+/******************************************************************************
+generateSimpleFPA(): This function generates a pmFPA data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmFPA* generateSimpleFPA(psMetadata *camera)
+{
+    pmFPA* fpa = pmFPAAlloc(camera);
+    fpa->hdu = pmHDUAlloc("cellExtName");
+    fpa->fromTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toTPA = PS_CREATE_2D_IDENTITY_PLANE_TRANSFORM();
+    fpa->toSky = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
+    psMetadataAddS32(fpa->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    if (camera != NULL) {
+        psMetadataAddS32((psMetadata *) fpa->camera, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    }
+    psMetadataAddS32(fpa->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(fpa->chips, NUM_CHIPS);
+    for (int i = 0 ; i < NUM_CHIPS ; i++) {
+        fpa->chips->data[i] = psMemDecrRefCounter((psPtr) generateSimpleChip(fpa));
+    }
+    pmConceptsBlankFPA(fpa);
+    return(fpa);
+}
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(28);
+
+
+    // ----------------------------------------------------------------------
+    // pmPSFmodelCheckDataStatusForView() tests
+    // bool pmPSFmodelCheckDataStatusForView (const pmFPAview *view, const pmFPAfile *file)
+    // Call pmPSFmodelCheckDataStatusForView() with NULL pmFPAview input parameter
+    if (1) {
+        psMemId id = psMemGetId();
+
+        pmFPAview *view = pmFPAviewAlloc(32);
+        pmFPAfile *file = pmFPAfileAlloc();
+        bool rc = pmPSFmodelCheckDataStatusForView(NULL, file);
+        ok(rc == false, "pmPSFmodelCheckDataStatusForView() returned FALSE with NULL pmFPAview input parameter");
+        psFree(view);
+        psFree(file);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSFmodelCheckDataStatusForView() with NULL pmFPAfile input parameter
+    if (1) {
+        psMemId id = psMemGetId();
+
+        pmFPAview *view = pmFPAviewAlloc(32);
+        pmFPAfile *file = pmFPAfileAlloc();
+        bool rc = pmPSFmodelCheckDataStatusForView(view, NULL);
+        ok(rc == false, "pmPSFmodelCheckDataStatusForView() returned FALSE with NULL pmFPAfile input parameter");
+        psFree(view);
+        psFree(file);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSFmodelCheckDataStatusForView() with acceptable input parameters
+    if (1) {
+        psMemId id = psMemGetId();
+
+        pmFPAview *view = pmFPAviewAlloc(32);
+        view->chip = -1;
+        view->cell = -1;
+        pmFPAfile *file = pmFPAfileAlloc();
+        psMetadata *camera = psMetadataAlloc();
+        file->fpa = generateSimpleFPA(camera);
+        bool rc = pmPSFmodelCheckDataStatusForView(view, file);
+        ok(rc == false, "pmPSFmodelCheckDataStatusForView() returned FALSE acceptable input parameters, but no PSPHOT.PSF");
+
+        // Add PSPHOT.PSF to chip->analysis and call pmPSFmodelCheckDataStatusForChip()
+        pmChip *chip = file->fpa->chips->data[0];
+        psVector *junk = psVectorAlloc(10, PS_TYPE_F32);
+        bool rc2 = psMetadataAddPtr(chip->analysis, PS_LIST_HEAD, "PSPHOT.PSF", PS_DATA_VECTOR, NULL, junk);
+        ok(rc2 == true, "added PSPHOT.PSF metadata to chip->analysis");
+        rc = pmPSFmodelCheckDataStatusForView(view, file);
+        ok(rc == true, "pmPSFmodelCheckDataStatusForView() returned TRUE acceptable input parameters");
+
+        psFree(view);
+        psFree(file->fpa);
+        file->fpa = NULL;
+        psFree(file);
+        psFree(camera);
+        psFree(junk);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmPSFmodelCheckDataStatusForFPA() tests
+    // bool pmPSFmodelCheckDataStatusForFPA (const pmFPA *fpa)
+    // Call pmPSFmodelCheckDataStatusForFPA() with NULL pmFPA input parameter
+    if (1) {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        bool rc = pmPSFmodelCheckDataStatusForFPA(NULL);
+        ok(rc == false, "pmPSFmodelCheckDataStatusForFPA() returned FALSE with NULL pmFPA input parameter");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSFmodelCheckDataStatusForFPA() with NULL pmFPA->chips input parameter
+    if (1) {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        for (int i = 0 ; i < fpa->chips->n ; i++) {
+            psFree(fpa->chips->data[i]);
+	}
+        bool rc = pmPSFmodelCheckDataStatusForFPA(NULL);
+        ok(rc == false, "pmPSFmodelCheckDataStatusForFPA() returned FALSE with NULL pmFPA->chips input parameter");
+        psFree(fpa);
+        psFree(camera);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSFmodelCheckDataStatusForFPA() with acceptable input parameters
+    if (1) {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        bool rc = pmPSFmodelCheckDataStatusForFPA(fpa);
+        ok(rc == false, "pmPSFmodelCheckDataStatusForFPA() returned FALSE with acceptable input parameters, but no PSPHOT.PSF");
+
+        // Add PSPHOT.PSF to chip->analysis and call pmPSFmodelCheckDataStatusForChip()
+        psVector *junk = psVectorAlloc(10, PS_TYPE_F32);
+        pmChip *chip = fpa->chips->data[0];
+        bool rc2 = psMetadataAddPtr(chip->analysis, PS_LIST_HEAD, "PSPHOT.PSF", PS_DATA_VECTOR, NULL, junk);
+        ok(rc2 == true, "added PSPHOT.PSF metadata to chip->analysis");
+        rc = pmPSFmodelCheckDataStatusForFPA(fpa);
+        ok(rc == true, "pmPSFmodelCheckDataStatusForFPA() returned TRUE with acceptable input parameters");
+
+        psFree(fpa);
+        psFree(camera);
+        psFree(junk);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmPSFmodelCheckDataStatusForChip() tests
+    // bool pmPSFmodelCheckDataStatusForChip (const pmChip *chip)
+    // Call pmPSFmodelCheckDataStatusForChip() with NULL pmChip input parameter
+    if (1) {
+        psMemId id = psMemGetId();
+        bool rc = pmPSFmodelCheckDataStatusForChip(NULL);
+        ok(rc == false, "pmPSFmodelCheckDataStatusForChip() returned FALSE with NULL pmChip input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSFmodelCheckDataStatusForChip() with acceptable input parameters
+    if (1) {
+        psMemId id = psMemGetId();
+        psMetadata *camera = psMetadataAlloc();
+        pmFPA *fpa = generateSimpleFPA(camera);
+        pmChip *chip = fpa->chips->data[0];
+        bool rc = pmPSFmodelCheckDataStatusForChip(chip);
+        ok(rc == false, "pmPSFmodelCheckDataStatusForChip() returned false with acceptable pmChip, but no PSPHOT.PSF");
+
+        // Add PSPHOT.PSF to chip->analysis and call pmPSFmodelCheckDataStatusForChip()
+        psVector *junk = psVectorAlloc(10, PS_TYPE_F32);
+        bool rc2 = psMetadataAddPtr(chip->analysis, PS_LIST_HEAD, "PSPHOT.PSF", PS_DATA_VECTOR, NULL, junk);
+        ok(rc2 == true, "added PSPHOT.PSF metadata to chip->analysis");
+        rc = pmPSFmodelCheckDataStatusForChip(chip);
+        ok(rc == true, "pmPSFmodelCheckDataStatusForChip() returned TRUE with acceptable input parameters");
+    
+        psFree(fpa);
+        psFree(camera);
+        psFree(junk);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmPSFmodelWrite() tests
+    // bool pmPSFmodelWrite (psMetadata *analysis, const pmFPAview *view,
+    //                       pmFPAfile *file, const pmConfig *config)
+    // Call pmPSFmodelWrite() with NULL pmFPAview input parameter
+    if (1) {
+        psMemId id = psMemGetId();
+        pmFPAview *view = pmFPAviewAlloc(32);
+        pmFPAfile *file = pmFPAfileAlloc();
+        psMetadata *camera = psMetadataAlloc();
+        file->fpa = generateSimpleFPA(camera);
+        psMetadata *analysis = psMetadataAlloc();
+        pmConfig *config = pmConfigAlloc();
+
+        bool rc = pmPSFmodelWrite(analysis, NULL, file, config);
+        ok(rc == false, "pmPSFmodelWrite() returned FALSE with NULL pmFPAview input parameter");
+
+        psFree(view);
+        psFree(file->fpa);
+        file->fpa = NULL;
+        psFree(file);
+        psFree(camera);
+        psFree(analysis);
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSFmodelWrite() with NULL pmFPAfile input parameter
+    if (1) {
+        psMemId id = psMemGetId();
+        pmFPAview *view = pmFPAviewAlloc(32);
+        pmFPAfile *file = pmFPAfileAlloc();
+        psMetadata *camera = psMetadataAlloc();
+        file->fpa = generateSimpleFPA(camera);
+        psMetadata *analysis = psMetadataAlloc();
+        pmConfig *config = pmConfigAlloc();
+
+        bool rc = pmPSFmodelWrite(analysis, view, NULL, config);
+        ok(rc == false, "pmPSFmodelWrite() returned FALSE with NULL pmFPAfile input parameter");
+
+        psFree(view);
+        psFree(file->fpa);
+        file->fpa = NULL;
+        psFree(file);
+        psFree(camera);
+        psFree(analysis);
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSFmodelWrite() with NULL pmFPAfile->file input parameter
+    if (1) {
+        psMemId id = psMemGetId();
+        pmFPAview *view = pmFPAviewAlloc(32);
+        pmFPAfile *file = pmFPAfileAlloc();
+        psMetadata *analysis = psMetadataAlloc();
+        pmConfig *config = pmConfigAlloc();
+
+        bool rc = pmPSFmodelWrite(analysis, view, NULL, config);
+        ok(rc == false, "pmPSFmodelWrite() returned FALSE with NULL pmFPAfile->fpa input parameter");
+
+        psFree(view);
+        psFree(file);
+        psFree(analysis);
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPSFmodelWrite() with acceptable input parameters
+    // XXX: This is currently being coded.  It does not work.
+    if (0) {
+        psMemId id = psMemGetId();
+        pmFPAview *view = pmFPAviewAlloc(32);
+        pmFPAfile *file = pmFPAfileAlloc();
+        psMetadata *camera = psMetadataAlloc();
+        file->fpa = generateSimpleFPA(camera);
+        pmConfigFileRead(&file->camera, "../dataFiles/camera0/camera.config", "CAMERA");
+        psMetadataPrint(stdout, file->camera, 0);
+        psMetadata *menu = psMetadataLookupMetadata(NULL, file->camera, "EXTNAME.RULES");
+        if (!menu) {
+            printf("NOTE: missing EXTNAME.RULES in camera.config\n");
+            exit(1);
+        }
+
+
+        psMetadata *analysis = psMetadataAlloc();
+        pmConfig *config = pmConfigAlloc();
+
+        // XXX: I failed here and then moved on.
+        /*
+        The function reads the PSPHOT recipes and requires that it contains a
+	PSPHOT key in the metadata.  I'm not sure how to do that, but I could
+	have hacked around it.  Then, however, it required several keys in
+	that metadata like PSF.CLUMP.X, PSF.CLUMP.Y, PSF.CLUMP.DX,
+	PSF.CLUMP.DY.  I could also put them in there, but I just gave up.
+	What should I do here?  I could easily create a metadata file with all
+	those metadata keys, but I'd rather use an existing one.
+        */
+
+        if (1) {
+            psMetadata *junk = psMetadataAlloc();
+            bool rc0 = pmConfigFileRead(&junk, "../dataFiles/recipes/psphot.config", "SAVE.PSF");
+            if (!rc0) {
+                rc0 = pmConfigFileRead(&junk, "dataFiles/recipes/psphot.config", "SAVE.PSF");
+	    }
+            ok(rc0, "Successfully read the PSPHOT recipe file");
+//          psMetadata *recipe = psMetadataLookupPtr(&rc0, junk, "SAVE.OUTPUT");
+            bool rc2 = psMetadataLookupBool(&rc0, junk, "SAVE.OUTPUT");
+            printf("rc0 is %d\n", (int) rc0);
+            printf("rc2 is %d\n", (int) rc2);
+//          if (recipe == NULL) printf("ERROR: recipe is NULL\n");
+            psMetadataPrint(stdout, junk, 0);
+            psFree(junk);
+	}
+        if (config->recipes == NULL) printf("COOL: config->recipes is NULL");
+        bool rc0 = pmConfigFileRead(&config->recipes, "../dataFiles/camera0/recipes.config", "PSPHOT");
+        if (!rc0) {
+            rc0 = pmConfigFileRead(&config->recipes, "dataFiles/camera0/recipes.config", "PSPHOT");
+	}
+        ok(rc0, "Successfully read the PSPHOT recipe file");
+psMetadataPrint(stdout, config->recipes, 0);
+
+        if (config->recipes == NULL) printf("FUCK: config->recipes is NULL");
+        psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, "PSPHOT");
+        if (!recipe) {
+            printf("FUCK: missing recipe %s\n", "PSPHOT");
+            exit(1);
+        }
+
+
+        bool rc = pmPSFmodelWrite(analysis, view, file, config);
+        ok(rc == true, "pmPSFmodelWrite() returned TRUE with acceptable input parameters");
+
+        psFree(view);
+        psFree(file->fpa);
+        file->fpa = NULL;
+        psFree(file);
+        psFree(camera);
+        psFree(analysis);
+        psFree(config);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPSFtoMetadata.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPSFtoMetadata.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPSFtoMetadata.c	(revision 22322)
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    Under construction: 10% complete.
+*/
+
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           (8)
+#define TEST_NUM_COLS           (16)
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.0001)
+#define NUM_SOURCES		100
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(1);
+
+
+    // ----------------------------------------------------------------------
+    // pmPSFtoMetadata() tests
+    // psMetadata *pmPSFtoMetadata (psMetadata *metadata, pmPSF *psf)
+    // Call pmPSFtoMetadata() with NULL psPSF input parameter
+    if (1) {
+        psMemId id = psMemGetId();
+        psMetadata *metadata = psMetadataAlloc();
+        pmPSFOptions *psfOptions = pmPSFOptionsAlloc();
+        psfOptions->psfTrendNx = 1;
+        psfOptions->psfTrendNy = 2;
+        psfOptions->psfFieldNx = 3;
+        psfOptions->psfFieldNy = 4;
+        psfOptions->psfFieldXo = 5;
+        psfOptions->psfFieldYo = 6;
+        pmModelClassInit();
+        psfOptions->type = pmModelClassGetType("PS_MODEL_GAUSS");
+        pmPSF *psf = pmPSFAlloc(psfOptions);
+        psMetadata *meta2 = pmPSFtoMetadata(metadata, NULL);
+        ok(meta2 == NULL, "pmPSFtoMetadata() returned NULL with NULL psPSF input parameter");
+        psFree(metadata);
+        psFree(psfOptions);
+        psFree(psf);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPeaks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPeaks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmPeaks.c	(revision 22322)
@@ -0,0 +1,744 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+        pmPeaksInImage(): Must debug tests for small images (1-by-1, N-by-1, 1-by-N)
+*/
+
+#define TST01_VECTOR_LENGTH 10
+#define NUM_ROWS 10
+#define NUM_COLS 10
+#define TST02_NUM_ROWS 5
+#define TST02_NUM_COLS 5
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+/******************************************************************************
+test01(): we first test pmPeaksInVector() with a variety of bad input
+parameters.  Then we test it with a simple vector both 1- and multi-elements.
+ *****************************************************************************/
+bool test_pmPeaksInVector(int n)
+{
+    psMemId id = psMemGetId();
+    bool testStatus = true;
+    psVector *inData = psVectorAlloc(n, PS_TYPE_F32);
+    inData->n = inData->nalloc;
+    psVector *outData = NULL;
+
+    // Test first pixel peak.
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (n-i);
+    }
+    inData->data.F32[0] = (float) n;
+    outData= pmPeaksInVector(inData, 0.0);
+    if (outData == NULL) {
+        diag("TEST ERROR: pmPeaksInVector returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+        if (outData->n != 1) {
+            diag("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        if (outData->data.U32[0] != 0) {
+            diag("TEST ERROR: Did not find peak at element 0.\n");
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+
+    //
+    // Test first pixel peak, large threshold
+    //
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (n-i);
+    }
+    inData->data.F32[0] = (float) n;
+    outData= pmPeaksInVector(inData, (float) (n*n));
+    if (outData == NULL) {
+        diag("TEST ERROR: pmPeaksInVector returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+        if (outData->n != 0) {
+            diag("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        psFree(outData);
+
+        // Skip remaining tests if the input vector has length 1.
+        if (n == 1) {
+            psFree(inData);
+            ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+            return(testStatus);
+        }
+    }
+
+    // Test last pixel peak.
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (i);
+    }
+    inData->data.F32[n-1] = (float) n;
+
+    outData= pmPeaksInVector(inData, 0.0);
+    if (outData == NULL) {
+        diag("TEST ERROR: pmPeaksInVector returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+        if (outData->n != 1) {
+            diag("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        if (outData->data.U32[0] != n-1) {
+            diag("TEST ERROR: Did not find peak at element %d.\n", n-1);
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+
+    // Test last pixel peak, large threshold.
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (i);
+    }
+    inData->data.F32[n-1] = (float) n;
+    outData= pmPeaksInVector(inData, (float) (n*n));
+    if (outData == NULL) {
+        diag("TEST ERROR: pmPeaksInVector returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+        if (outData->n != 0) {
+            diag("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+
+    // Test interior peaks.
+    // Set all even number elements to be peaks.
+    for (psS32 i = 0 ; i < n ; i++) {
+        if (0 == i%2) {
+            inData->data.F32[i] = (float) (2 * i);
+        } else {
+            inData->data.F32[i] = (float) (i);
+        }
+    }
+    inData->data.F32[0] = (float) n;
+
+
+    outData= pmPeaksInVector(inData, 0.0);
+    if (outData == NULL) {
+        diag("TEST ERROR: pmPeaksInVector returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+        if (outData->n != n/2) {
+            diag("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        for (psS32 i = 0 ; i < outData->n ; i++) {
+            if (outData->data.U32[i] != (2 * i)) {
+                diag("TEST ERROR: the %d-th peak is element number %d\n", i, outData->data.U32[i]);
+                testStatus = false;
+            }
+        }
+        psFree(outData);
+    }
+
+
+    // Test interior peaks, with threshold = n*n.
+    // Should generate an empty output psVector.
+    outData= pmPeaksInVector(inData, (float) (n*n));
+    if (outData == NULL) {
+        diag("TEST ERROR: pmPeaksInVector returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+        if (outData->n != 0) {
+            diag("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+    psFree(inData);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    return(testStatus);
+}
+
+
+bool test_pmPeaksInImage(int numRows, int numCols)
+{
+    psMemId id = psMemGetId();
+    bool testStatus = true;
+    psImage *inData = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psArray *outData = NULL;
+    // Initialize test image.
+    for (psS32 i = 0 ; i < numRows ; i++) {
+        for (psS32 j = 0 ; j < numCols ; j++) {
+            inData->data.F32[i][j] = PS_SQR(i - numRows/2) + PS_SQR(j-numCols/2);
+        }
+    }
+
+    // Set corner and center pixels as peaks.
+    inData->data.F32[0][0] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[0][numCols-1] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[numRows-1][0] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[numRows-1][numCols-1] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[numRows/2][numCols/2] = PS_SQR(numRows) + PS_SQR(numCols);
+
+    // Call pmPeaksInImage() with a threshold of 0.0.
+    outData = pmPeaksInImage(inData, 0.0);
+
+    if (outData == NULL) {
+        diag("TEST ERROR: pmPeaksInImage returned a NULL psList.\n");
+        testStatus = false;
+    } else {
+        psS32 expectedNumPeaks;
+        if ((numRows == 1) && (numCols == 1)) {
+            expectedNumPeaks = 1;
+        } else if ((numRows == 1) || (numCols == 1)) {
+            expectedNumPeaks = 3;
+        } else {
+            expectedNumPeaks = 5;
+        }
+        if (outData->n != expectedNumPeaks) {
+            diag("TEST ERROR: pmPeaksInImage found %ld peaks (should be %d)\n", outData->n, expectedNumPeaks);
+            testStatus = false;
+        }
+
+        // HEY: verify
+        for (psS32 i = 0 ; i < outData->n ; i++) {
+            pmPeak *tmpPeak = (pmPeak *) outData->data[i];
+            if (((tmpPeak->x == 0) && (tmpPeak->y == 0)) ||
+                    ((tmpPeak->x == 0) && (tmpPeak->y == numRows-1)) ||
+                    ((tmpPeak->x == numCols-1) && (tmpPeak->y == 0)) ||
+                    ((tmpPeak->x == numCols-1) && (tmpPeak->y == numRows-1))) {
+                if (!((tmpPeak->type & PM_PEAK_LONE) || (tmpPeak->type & PM_PEAK_EDGE))) {
+                    diag("TEST ERROR: (0) peak at (%d, %d) (%f) ->type set improperly (0x%x).",
+                          tmpPeak->y, tmpPeak->x, tmpPeak->value, tmpPeak->type);
+                    diag(" should be (0x%x or 0x%x).\n", PM_PEAK_LONE, PM_PEAK_EDGE);
+                    testStatus = false;
+                }
+            } else if ((tmpPeak->x == numCols/2) && (tmpPeak->y == numRows/2)) {
+                if (tmpPeak->type != PM_PEAK_LONE) {
+                    diag("TEST ERROR: (1) peak at (%d, %d) (%f) ->type set improperly (0x%x).\n",
+                           tmpPeak->y, tmpPeak->x, tmpPeak->value, tmpPeak->type);
+                    diag(" should be (0x%x).\n", PM_PEAK_LONE);
+                    testStatus = false;
+                }
+            } else {
+                diag("TEST ERROR: Peak at (%d, %d) (%f)\n", tmpPeak->y, tmpPeak->x, tmpPeak->value);
+                testStatus = false;
+            }
+        }
+    }
+    psFree(inData);
+    psFree(outData);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    return(testStatus);
+}
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(69);
+
+
+    // ------------------------------------------------------------------------
+    // Test pmPeakAlloc()
+    {
+        psMemId id = psMemGetId();
+        pmPeak *tmpPeak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        ok(tmpPeak != NULL, "pmPeakAlloc() returned a non-NULL pmPeak");
+        skip_start(tmpPeak == NULL, 9, "Skipping tests because pmPeakAlloc() returned NULL");
+        ok(tmpPeak->id == 1, "pmPeakAlloc() set pmPeak->id");
+        ok(tmpPeak->x == 1, "pmPeakAlloc() set pmPeak->x");
+        ok(tmpPeak->y == 2, "pmPeakAlloc() set pmPeak->y");
+        ok(tmpPeak->value == 3.0, "pmPeakAlloc() set pmPeak->value");
+        ok(tmpPeak->flux == 0, "pmPeakAlloc() pmPeak->flux");
+        ok(tmpPeak->SN == 0, "pmPeakAlloc() pmPeak->SN");
+        ok(tmpPeak->xf == 1, "pmPeakAlloc() pmPeak->xf");
+        ok(tmpPeak->yf == 2, "pmPeakAlloc() pmPeak->yF");
+        ok(tmpPeak->type == PM_PEAK_LONE, "pmPeakAlloc() pmPeak->type");
+        psFree(tmpPeak);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmPeakAlloc(): ensure pmPeak->id is properly incremented.
+    {
+        psMemId id = psMemGetId();
+        pmPeak *tmpPeak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        skip_start(tmpPeak == NULL, 1, "Skipping tests because pmPeakAlloc() returned NULL");
+        ok(tmpPeak->id == 2, "pmPeakAlloc() incremented and set pmPeak->id");
+        psFree(tmpPeak);
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Calling pmPeaksCompareAscend with NULL peak1
+    // XXX: This currently seg-faults because NULL args are not pretested in pmPeaksCompareAscend()
+    if (0) {
+        psMemId id = psMemGetId();
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 3.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareAscend(NULL, (const void **) peak2);
+        ok(rc == -1, "pmPeaksCompareAscend() returned correct result (peak1 < peak2)");
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksCompareAscend with NULL peak2
+    // XXX: This currently seg-faults because NULL args are not pretested in pmPeaksCompareAscend()
+    if (0) {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareAscend((const void **)peak1, NULL);
+        ok(rc == -1, "pmPeaksCompareAscend() returned correct result (peak1 < peak2)");
+        psFree(*peak1);
+        psFree(peak1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksCompareAscend with NULL *peak1
+    // XXX: This currently seg-faults because NULL args are not pretested in pmPeaksCompareAscend()
+    if (0) {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 3.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareAscend((const void **)peak1, (const void **) peak2);
+        ok(rc == -1, "pmPeaksCompareAscend() returned correct result (peak1 < peak2)");
+        psFree(peak1);
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksCompareAscend with NULL *peak2
+    // XXX: This currently seg-faults because NULL args are not pretested in pmPeaksCompareAscend()
+    if (0) {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        int rc = pmPeaksCompareAscend((const void **)peak1, (const void **) peak2);
+        ok(rc == -1, "pmPeaksCompareAscend() returned correct result (peak1 < peak2)");
+        psFree(*peak1);
+        psFree(peak1);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Calling pmPeaksCompareAscend with peak1 < peak2
+    {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 3.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareAscend((const void **)peak1, (const void **) peak2);
+        ok(rc == -1, "pmPeaksCompareAscend() returned correct result (peak1 < peak2)");
+        psFree(*peak1);
+        psFree(peak1);
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksCompareAscend with peak1 > peak2
+    {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 3.0, PM_PEAK_LONE);
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareAscend((const void **)peak1, (const void **) peak2);
+        ok(rc == 1, "pmPeaksCompareAscend() returned correct result (peak1 > peak2)");
+        psFree(*peak1);
+        psFree(peak1);
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksCompareAscend with peak1 == peak2
+    {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareAscend((const void **)peak1, (const void **) peak2);
+        ok(rc == 0, "pmPeaksCompareAscend() returned correct result (peak1 == peak2)", rc);
+        psFree(*peak1);
+        psFree(peak1);
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Calling pmPeaksCompareDescend with NULL peak1
+    // XXX: This currently seg-faults because NULL args are not pretested in pmPeaksCompareDescend()
+    if (0) {
+        psMemId id = psMemGetId();
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 3.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareDescend(NULL, (const void **) peak2);
+        ok(rc == -1, "pmPeaksCompareDescend() returned correct result (peak1 < peak2)");
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksCompareDescend with NULL peak2
+    // XXX: This currently seg-faults because NULL args are not pretested in pmPeaksCompareDescend()
+    if (0) {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareDescend((const void **)peak1, NULL);
+        ok(rc == -1, "pmPeaksCompareDescend() returned correct result (peak1 < peak2)");
+        psFree(*peak1);
+        psFree(peak1);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksCompareDescend with NULL *peak1
+    // XXX: This currently seg-faults because NULL args are not pretested in pmPeaksCompareDescend()
+    if (0) {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 3.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareDescend((const void **)peak1, (const void **) peak2);
+        ok(rc == -1, "pmPeaksCompareDescend() returned correct result (peak1 < peak2)");
+        psFree(peak1);
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksCompareDescend with NULL *peak2
+    // XXX: This currently seg-faults because NULL args are not pretested in pmPeaksCompareDescend()
+    if (0) {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        int rc = pmPeaksCompareDescend((const void **)peak1, (const void **) peak2);
+        ok(rc == -1, "pmPeaksCompareDescend() returned correct result (peak1 < peak2)");
+        psFree(*peak1);
+        psFree(peak1);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Calling pmPeaksCompareDescend with peak1 < peak2
+    {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 3.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareDescend((const void **)peak1, (const void **) peak2);
+        ok(rc == 1, "pmPeaksCompareDescend() returned correct result (peak1 < peak2)");
+        psFree(*peak1);
+        psFree(peak1);
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksCompareDescend with peak1 > peak2
+    {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 3.0, PM_PEAK_LONE);
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareDescend((const void **)peak1, (const void **) peak2);
+        ok(rc == -1, "pmPeaksCompareDescend() returned correct result (peak1 > peak2)");
+        psFree(*peak1);
+        psFree(peak1);
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksCompareDescend with peak1 == peak2
+    {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        int rc = pmPeaksCompareDescend((const void **)peak1, (const void **) peak2);
+        ok(rc == 0, "pmPeaksCompareDescend() returned correct result (peak1 == peak2)", rc);
+        psFree(*peak1);
+        psFree(peak1);
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmPeakSortBySN() tests
+    // int pmPeakSortBySN (const void **a, const void **b)
+    // Call pmPeakSortBySN() with acceptable input parameters.
+    // XXX: We don't test with NULL input parameters since this functions has no PS_ASSERTS to protect
+    // against that.
+    {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        (*peak1)->SN = 10.0;
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 3.0, PM_PEAK_LONE);
+        (*peak2)->SN = 20.0;
+        int rc = pmPeakSortBySN((const void **)peak1, (const void **) peak2);
+        ok(rc == 1, "pmPeakSortBySN() returned correct result (peak1 < peak2) (%d)", rc);
+        rc = pmPeakSortBySN((const void **)peak2, (const void **) peak1);
+        ok(rc == -1, "pmPeakSortBySN() returned correct result (peak2 < peak1) (%d)", rc);
+        rc = pmPeakSortBySN((const void **)peak1, (const void **) peak1);
+        ok(rc == 0, "pmPeakSortBySN() returned correct result (peak1 == peak2) (%d)", rc);
+        psFree(*peak1);
+        psFree(peak1);
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmPeakSortByY() tests
+    // int pmPeakSortByY (const void **a, const void **b)
+    // Call pmPeakSortByY() with acceptable input parameters.
+    // XXX: We don't test with NULL input parameters since this functions has no PS_ASSERTS to protect
+    // against that.
+    {
+        psMemId id = psMemGetId();
+        pmPeak **peak1 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak1 = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        (*peak1)->y = 10.0;
+        pmPeak **peak2 = (pmPeak **) psAlloc(sizeof(pmPeak *));
+        *peak2 = pmPeakAlloc(3, 4, 3.0, PM_PEAK_LONE);
+        (*peak2)->y = 20.0;
+        int rc = pmPeakSortByY((const void **)peak1, (const void **) peak2);
+        ok(rc == -1, "pmPeakSortByY() returned correct result (peak1 < peak2) (%d)", rc);
+        rc = pmPeakSortByY((const void **)peak2, (const void **) peak1);
+        ok(rc == 1, "pmPeakSortByY() returned correct result (peak2 < peak1) (%d)", rc);
+        rc = pmPeakSortByY((const void **)peak1, (const void **) peak1);
+        ok(rc == 0, "pmPeakSortByY() returned correct result (peak1 == peak2) (%d)", rc);
+        psFree(*peak1);
+        psFree(peak1);
+        psFree(*peak2);
+        psFree(peak2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // pmPeaksInVector() tests
+    // Test pmPeaksInVector() with bad input parameters.
+    // Calling pmPeaksInVector with NULL psVector.  Should generate error.
+    {
+        psMemId id = psMemGetId();
+        psVector *tmpVec = pmPeaksInVector(NULL, 0.0);
+        ok(tmpVec == NULL, "pmPeaksInVector() returned a NULL with NULL psVector input");
+        psFree(tmpVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksInVector with empty psVector.  Should generate error.
+    {
+        psMemId id = psMemGetId();
+        psVector *tmpVecEmpty = psVectorAlloc(0, PS_TYPE_F32);
+        psVector *tmpVec = pmPeaksInVector(tmpVecEmpty, 0.0);
+        ok(tmpVec == NULL, "pmPeaksInVector() returned a NULL with NULL psVector input");
+        psFree(tmpVec);
+        psFree(tmpVecEmpty);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksInVector with PS_TYPE_F64 psVector.  Should generate error.
+    {
+        psMemId id = psMemGetId();
+        psVector *tmpVecF64 = psVectorAlloc(TST01_VECTOR_LENGTH, PS_TYPE_F64);
+        psVector *tmpVec = pmPeaksInVector(tmpVecF64, 0.0);
+        ok(tmpVec == NULL, "pmPeaksInVector() returned a NULL with F64 psVector input");
+        psFree(tmpVecF64);
+        psFree(tmpVec);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    ok(test_pmPeaksInVector(1), "Tested pmPeaksInVector() on length 1 input vector");
+    ok(test_pmPeaksInVector(10), "Tested pmPeaksInVector() on length 10 input vector");
+
+
+    // ------------------------------------------------------------------------
+    // pmPeaksInImage() tests
+    // Calling pmPeaksInImage with NULL psImage.  Should generate error.
+    {
+        psMemId id = psMemGetId();
+        psArray *tmpArray = pmPeaksInImage(NULL, 0.0);
+        ok(tmpArray == NULL, "pmPeaksInImage() returned NULL with NULL input image");
+        psFree(tmpArray);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Calling pmPeaksInImage with empty psImage.  Should generate error.
+    {
+        psMemId id = psMemGetId();
+        psImage *tmpImageEmpty = psImageAlloc(0, 0, PS_TYPE_F32);
+        psArray *tmpArray = pmPeaksInImage(tmpImageEmpty, 0.0);
+        ok(tmpArray == NULL, "pmPeaksInImage() returned NULL with empty input image");
+        psFree(tmpArray);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    
+
+    // Calling pmPeaksInImage with PS_TYPE_F64 psImage.  Should generate error
+    {
+        psMemId id = psMemGetId();
+        psImage *tmpImageF64 = psImageAlloc(TST02_NUM_ROWS, TST02_NUM_COLS, PS_TYPE_F64);
+        psArray *tmpArray = pmPeaksInImage(tmpImageF64, 0.0);
+        ok(tmpArray == NULL, "pmPeaksInImage() returned NULL with F64 input image");
+        psFree(tmpImageF64);
+        psFree(tmpArray);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // XXX: Uncomment these and debug
+    //    testStatus&= test_pmPeaksInImage(1, 1);
+    //    testStatus&= test_pmPeaksInImage(2, 5);
+    //    testStatus&= test_pmPeaksInImage(5, 2);
+    // HEY: add code for small images
+    //    testStatus&= test_pmPeaksInImage(1, 1);
+    //    testStatus&= test_pmPeaksInImage(1, 8);
+    //    testStatus&= test_pmPeaksInImage(8, 1);
+    ok(test_pmPeaksInImage(TST02_NUM_ROWS, TST02_NUM_COLS),
+      "Tested pmPeaksInImage() on (%d, %d) image", TST02_NUM_ROWS, TST02_NUM_COLS);
+    ok(test_pmPeaksInImage(2*TST02_NUM_ROWS, TST02_NUM_COLS),
+      "Tested pmPeaksInImage() on (%d, %d) image", 2*TST02_NUM_ROWS, TST02_NUM_COLS);
+    ok(test_pmPeaksInImage(TST02_NUM_ROWS, 2*TST02_NUM_COLS),
+      "Tested pmPeaksInImage() on (%d, %d) image", TST02_NUM_ROWS, 2*TST02_NUM_COLS);
+
+
+    // ------------------------------------------------------------------------
+    // Test pmPeaksSubset()
+    // Calling pmPeaksSubset with NULL psList.  Should generate error.
+    {
+        psMemId id = psMemGetId();
+        psArray *outData = pmPeaksSubset(NULL, 0.0, psRegionSet(0, 0, 0, 0));
+        ok(outData == NULL, "pmPeaksSubset() returned a NULL with a NULL psArray input");
+        psFree(outData);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Set peaks in input image.  All even-column and even-row pixels are
+    // set non-zero, all other pixels are set to zero.
+    psImage *imgData = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+    psS32 numPeaksOrig = 0;
+    for (psS32 i = 0 ; i < NUM_ROWS ; i++) {
+        for (psS32 j = 0 ; j < NUM_COLS ; j++) {
+            if ((0 == i%2) && (0 == j%2)) {
+                imgData->data.F32[i][j] = (float) (i + 10);
+                numPeaksOrig++;
+            } else {
+                imgData->data.F32[i][j] = 0.0;
+            }
+        }
+    }
+    // Call pmPeaksSubset() with large maxValue and disjoint psRegion.
+    // Should not remove any peaks.
+    {
+        psMemId id = psMemGetId();
+        psArray *outData = pmPeaksInImage(imgData, 0.0);
+        psArray *outData2 = pmPeaksSubset(outData, PS_MAX_F32, psRegionSet(20, 20, 20, 20));
+        ok(outData2 != NULL && psMemCheckArray(outData),
+           "pmPeaksSubset() returned a non-NULL psArray (large maxValue)");
+        ok(outData2->n == numPeaksOrig,
+          "pmPeaksSubset() returned correct number of peaks (was %d, should be %d)", outData2->n+1, numPeaksOrig);
+        psFree(outData);
+        psFree(outData2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPeaksSubset() with small maxValue and disjoint psRegion.
+    // Should not remove any peaks.
+    {
+        psMemId id = psMemGetId();
+        psArray *outData = pmPeaksInImage(imgData, 0.0);
+        psArray *outData2 = pmPeaksSubset(outData, 0.0, psRegionSet(20, 20, 20, 20));
+        ok(outData2 != NULL && psMemCheckArray(outData),
+           "pmPeaksSubset() returned a non-NULL psArray (small maxValue)");
+        ok(outData2->n == 0,
+          "pmPeaksSubset() returned correct number of peaks (was %d, should be %d)", outData2->n, 0);
+        psFree(outData);
+        psFree(outData2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPeaksSubset() with large maxValue and enclosing psRegion.
+    // Should remove aall peaks.
+    {
+        psMemId id = psMemGetId();
+        psArray *outData = pmPeaksInImage(imgData, 0.0);
+        psRegion tmpRegion = psRegionSet(-PS_MAX_F32, PS_MAX_F32, -PS_MAX_F32, PS_MAX_F32);
+        psArray *outData2 = pmPeaksSubset(outData, PS_MAX_F32, tmpRegion);
+        ok(outData2 != NULL && psMemCheckArray(outData),
+           "pmPeaksSubset() returned a non-NULL psArray (small maxValue, enclosing psRegion)");
+        ok(outData2->n == 0,
+          "pmPeaksSubset() returned correct number of peaks (was %d, should be %d)", outData2->n, 0);
+        psFree(outData);
+        psFree(outData2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmPeaksSubset() with large maxValue and enclosing psRegion.
+    // Should remove aall peaks.
+    {
+        psMemId id = psMemGetId();
+        psArray *outData = pmPeaksInImage(imgData, 0.0);
+        psRegion tmpRegion = psRegionSet(-PS_MAX_F32, PS_MAX_F32, -PS_MAX_F32, PS_MAX_F32);
+        psArray *outData2 = pmPeaksSubset(outData, 0.0, tmpRegion);
+        ok(outData2 != NULL && psMemCheckArray(outData),
+           "pmPeaksSubset() returned a non-NULL psArray (small maxValue, inclusive psRegion)");
+        ok(outData2->n == 0,
+          "pmPeaksSubset() returned correct number of peaks (was %d, should be %d)", outData2->n, 0);
+        psFree(outData);
+        psFree(outData2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmResiduals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmResiduals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmResiduals.c	(revision 22322)
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+	All functions are tested.
+*/
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(11);
+
+
+    // Test pmFringeRegionsAlloc()
+    // pmResiduals *pmResidualsAlloc (int xSize, int ySize, int xBin, int yBin);
+    {
+        psMemId id = psMemGetId();
+        int xSize = 1;
+        int ySize = 2;
+        int xBin = 3;
+        int yBin = 4;
+        pmResiduals *resid = pmResidualsAlloc(xSize, ySize, xBin, yBin);
+        ok(resid != NULL && psMemCheckResiduals(resid), "pmResidualsAlloc() allocated a pmResiduals struct correctly");
+        ok(resid->Ro && psMemCheckImage(resid->Ro), "pmResidualsAlloc() allocated the resid->Ro image");
+        ok(resid->Rx && psMemCheckImage(resid->Rx), "pmResidualsAlloc() allocated the resid->Rx image");
+        ok(resid->Ry && psMemCheckImage(resid->Ry), "pmResidualsAlloc() allocated the resid->Ry image");
+        ok(resid->weight && psMemCheckImage(resid->weight), "pmResidualsAlloc() allocated the resid->weight image");
+        ok(resid->mask && psMemCheckImage(resid->mask), "pmResidualsAlloc() allocated the resid->mask image");
+
+        int nX = xSize * xBin;
+        int nY = ySize * yBin;
+        nX = (nX % 2) ? nX : nX + 1;
+        nY = (nY % 2) ? nY : nY + 1;
+        ok(resid->xBin == xBin, "pmResidualsAlloc() set resid->xBin correctly");
+        ok(resid->yBin == yBin, "pmResidualsAlloc() set resid->yBin correctly");
+        ok(resid->xCenter == 0.5*(nX - 1), "pmResidualsAlloc() set resid->xCenter correctly");
+        ok(resid->yCenter == 0.5*(nY - 1), "pmResidualsAlloc() set resid->xCenter correctly");
+
+        psFree(resid);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSource.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSource.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSource.c	(revision 22322)
@@ -0,0 +1,838 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/*
+Tested:
+    Tested
+        pmSourceAlloc()
+        pmSourceCopy()
+        pmSourceDefinePixels()
+        pmSourceRedefinePixels()
+        pmSourcePSFClump()
+        pmSourceGetModel()
+        pmSourceAdd()
+        pmSourceSub()
+        pmSourceAddWithOffset()
+        pmSourceSubWithOffset()
+        pmSourceOp()
+        pmSourceCacheModel()
+        pmSourceCachePSF()
+        pmSourceSortBySN()	(COMPILER ERRORS)
+        pmSourceSortByY()	(COMPILER ERRORS)
+    Must test
+        pmSourceMoments()
+        pmSourceRoughClass()
+
+*/
+
+
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           (4+1)
+#define TEST_NUM_COLS           (4+1)
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.01)
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(128);
+
+    // ----------------------------------------------------------------------
+    // Test pmSourceAlloc()
+    // pmSource *pmSourceAlloc();
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        ok(src != NULL && psMemCheckSource(src), "pmSourceAlloc() returned a non-NULL pmSource");
+        skip_start(src == NULL, 24, "Skipping tests because pmSourceAlloc() returned NULL");
+        ok(src->peak == NULL, "pmSourceAlloc() pmSource->peak correctly");
+        ok(src->pixels == NULL, "pmSourceAlloc() pmSource->pixels correctly");
+        ok(src->weight == NULL, "pmSourceAlloc() pmSource->weight correctly");
+        ok(src->maskObj == NULL, "pmSourceAlloc() pmSource->maskObj correctly");
+        ok(src->maskView == NULL, "pmSourceAlloc() pmSource->maskView correctly");
+        ok(src->modelFlux == NULL, "pmSourceAlloc() pmSource->modelFlux correctly");
+        ok(src->psfFlux == NULL, "pmSourceAlloc() pmSource->psfFlux correctly");
+        ok(src->moments == NULL, "pmSourceAlloc() pmSource->moments correctly");
+        ok(src->blends == NULL, "pmSourceAlloc() pmSource->blends correctly");
+        ok(src->modelPSF == NULL, "pmSourceAlloc() pmSource->modelPSF correctly");
+        ok(src->modelEXT == NULL, "pmSourceAlloc() pmSource->modelEXT correctly");
+        ok(src->modelConv == NULL, "pmSourceAlloc() pmSource->modelConv correctly");
+        ok(src->type == PM_SOURCE_TYPE_UNKNOWN, "pmSourceAlloc() pmSource->type correctly");
+        ok(src->mode == PM_SOURCE_MODE_DEFAULT, "pmSourceAlloc() pmSource->mode correctly");
+
+        ok(isnan(src->psfMag), "pmSourceAlloc() pmSource->psfMag correctly");
+        ok(isnan(src->extMag), "pmSourceAlloc() pmSource->extMag correctly");
+        ok(isnan(src->errMag), "pmSourceAlloc() pmSource->errMag correctly");
+        ok(isnan(src->apMag), "pmSourceAlloc() pmSource->apMag correctly");
+        ok(isnan(src->sky), "pmSourceAlloc() pmSource->sky correctly");
+        ok(isnan(src->skyErr), "pmSourceAlloc() pmSource->skyErr correctly");
+        ok(isnan(src->pixWeight), "pmSourceAlloc() pmSource->pixWeight correctly");
+        int srcID = src->id;
+        psFree(src);
+
+        // Allocate another pmSource to ensure that pmSource->id is incremented
+        pmSource *src = pmSourceAlloc();
+        ok(src != NULL, "pmSourceAlloc() returned a non-NULL pmSource");
+        ok(src->id == srcID+1, "pmSourceAlloc() incremented the pmSource->id correctly");
+        psFree(src);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceCopy() tests
+    // Call pmSourceCopy() with NULL input
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceCopy(NULL);
+        ok(src == NULL, "pmSourceCopy(NULL) returned NULL");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceCopy() with non-NULL input, but NULL members
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        ok(src != NULL, "pmSourceAlloc() returned a non-NULL pmSource");
+        pmSource *dst = pmSourceCopy(src);
+        ok(dst != NULL, "pmSourceCopy() returned a non-NULL pmSource");
+        ok(dst != src, "pmSourceCopy() allocated a new pmSource");
+        ok(dst->type == src->type, "pmSourceCopy() set the pmSource->type");
+        ok(dst->mode == src->mode, "pmSourceCopy() set the pmSource->mode");
+        psFree(src);
+        psFree(dst);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceCopy() with non-NULL input, non-NULL members
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        ok(src != NULL, "pmSourceAlloc() returned a non-NULL pmSource");
+        // Set pmPeak values
+        src->peak = pmPeakAlloc (1, 2, 3.0, PM_PEAK_LONE);
+        src->peak->xf = 4.0;
+        src->peak->yf = 5.0;
+        src->peak->flux = 6.0;
+        src->peak->SN = 10.0;
+        src->moments = pmMomentsAlloc();
+        src->moments->x = 11.0;
+        src->moments->y = 12.0;
+        src->moments->Sx = 15.0;
+        src->moments->Sy = 16.0;
+        src->moments->Sxy = 19.0;
+
+        src->pixels = psImageAlloc(2, 4, PS_TYPE_F32);
+        src->weight = psImageAlloc(6, 8, PS_TYPE_F32);
+        src->maskView  = psImageAlloc(10, 12, PS_TYPE_U8);
+        pmSource *dst = pmSourceCopy(src);
+        ok(dst != NULL, "pmSourceCopy() returned a non-NULL pmSource");
+        ok(dst != src, "pmSourceCopy() allocated a new pmSource");
+        ok(dst->type == src->type, "pmSourceCopy() set the pmSource->type");
+        ok(dst->mode == src->mode, "pmSourceCopy() set the pmSource->mode");
+        ok(dst->peak != NULL, "pmSourceCopy() allocated a new pmSource->peak");
+        ok(dst->peak->xf == src->peak->xf, "pmSourceCopy() pmSource->peak->xf");
+        ok(dst->peak->yf == src->peak->yf, "pmSourceCopy() pmSource->peak->yf");
+        ok(dst->peak->flux == src->peak->flux, "pmSourceCopy() pmSource->peak->flux");
+        ok(dst->peak->SN == src->peak->SN, "pmSourceCopy() pmSource->peak->SN");
+        ok(dst->moments != NULL, "pmSourceCopy() allocated a new pmSource->moments");
+        ok(dst->moments->x == src->moments->x, "pmSourceCopy() pmSource->moments->x");
+        ok(dst->moments->y == src->moments->y, "pmSourceCopy() pmSource->moments->y");
+        ok(dst->moments->Sx == src->moments->Sx, "pmSourceCopy() pmSource->moments->Sx");
+        ok(dst->moments->Sy == src->moments->Sy, "pmSourceCopy() pmSource->moments->Sy");
+        ok(dst->moments->Sxy == src->moments->Sxy, "pmSourceCopy() pmSource->moments->Sxy");
+
+        // XXX: We should possibly do a better job testing that these images are copied correctly.
+        ok(dst->pixels != NULL, "pmSourceCopy() allocated a new pmSource->pixels");
+        ok(dst->pixels->numCols == src->pixels->numCols && dst->pixels->numRows == src->pixels->numRows, 
+           "pmSourceCopy() generated correct size pmSource->pixels");
+        ok(dst->weight != NULL, "pmSourceCopy() allocated a new pmSource->weight");
+        ok(dst->weight->numCols == src->weight->numCols && dst->weight->numRows == src->weight->numRows, 
+           "pmSourceCopy() generated correct size pmSource->weight");
+        ok(dst->maskView != NULL, "pmSourceCopy() allocated a new pmSource->maskView");
+        ok(dst->maskView->numCols == src->maskView->numCols && dst->maskView->numRows == src->maskView->numRows, 
+           "pmSourceCopy() generated correct size pmSource->maskView");
+
+        psFree(src);
+        psFree(dst);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceDefinePixels() tests
+    // Call pmSourceDefinePixels() with NULL pmSource input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        ok(!pmSourceDefinePixels(NULL, readout, 1.0, 2.0, 3.0),
+           "pmSourceDefinePixels() returned false with NULL pmSource input parameter");
+        psFree(src);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceDefinePixels() with NULL pmReadout input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        ok(!pmSourceDefinePixels(src, NULL, 1.0, 2.0, 3.0),
+           "pmSourceDefinePixels() returned false with NULL pmReadout input parameter");
+        psFree(src);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceDefinePixels() with NULL pmReadout->image input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        psFree(readout->image);
+        readout->image = NULL;
+        ok(!pmSourceDefinePixels(src, NULL, 1.0, 2.0, 3.0),
+           "pmSourceDefinePixels() returned false with NULL pmReadout input parameter");
+        psFree(src);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceDefinePixels() with negative radius input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        ok(!pmSourceDefinePixels(src, readout, 1.0, 2.0, -3.0),
+           "pmSourceDefinePixels() returned false with negative radius input parameter");
+        psFree(src);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceDefinePixels() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        for (int i = 0 ; i < readout->image->numRows ; i++) {
+            for (int j = 0 ; j < readout->image->numCols ; j++) {
+                readout->image->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        bool rc = pmSourceDefinePixels(src, readout, (float) TEST_NUM_COLS/2, (float) TEST_NUM_ROWS/2, 2.0);
+        ok(rc, "pmSourceDefinePixels() returned TRUE with acceptable input parameters");
+        int expectedNumCols = 1 + (TEST_NUM_COLS/2);
+        int expectedNumRows = 1 + (TEST_NUM_COLS/2);
+        // XX: We only verify the size of the pixels, weight, maskView, and maskObj
+        // images.  A better test would verify that the actual data is set correctly.
+        // We don't do that here since it basically duplicated psLib function tests.
+        ok(src->pixels->numCols == expectedNumCols && src->pixels->numRows == expectedNumRows, 
+               "pmSourceDefinePixels() set the size of pmSource->pixels correctly");
+        ok(src->weight->numCols == expectedNumCols && src->weight->numRows == expectedNumRows, 
+               "pmSourceDefinePixels() set the size of pmSource->weight correctly");
+        ok(src->maskView->numCols == expectedNumCols && src->maskView->numRows == expectedNumRows, 
+               "pmSourceDefinePixels() set the size of pmSource->maskView correctly");
+        ok(src->maskObj->numCols == expectedNumCols && src->maskObj->numRows == expectedNumRows, 
+               "pmSourceDefinePixels() set the size of pmSource->maskObj correctly");
+
+        psRegion region = psRegionForSquare((float) TEST_NUM_COLS/2, (float) TEST_NUM_ROWS/2, 2.0);
+        region = psRegionForImage(readout->image, region);
+        ok(src->region.x0 == region.x0 && src->region.x1 == region.x1 &&
+           src->region.y0 == region.y0 && src->region.y1 == region.y1,
+          "pmSourceDefinePixels() set pmSource->region correctly");
+
+        psFree(src);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceRedefinePixels() tests
+    // Call pmSourceRedefinePixels() with NULL pmSource input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        ok(!pmSourceRedefinePixels(NULL, readout, 1.0, 2.0, 3.0),
+           "pmSourceRedefinePixels() returned false with NULL pmSource input parameter");
+        psFree(src);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceRedefinePixels() with NULL pmReadout input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        ok(!pmSourceRedefinePixels(src, NULL, 1.0, 2.0, 3.0),
+           "pmSourceRedefinePixels() returned false with NULL pmReadout input parameter");
+        psFree(src);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceRedefinePixels() with NULL pmReadout->image input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        psFree(readout->image);
+        readout->image = NULL;
+        ok(!pmSourceRedefinePixels(src, NULL, 1.0, 2.0, 3.0),
+           "pmSourceRedefinePixels() returned false with NULL pmReadout input parameter");
+        psFree(src);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceRedefinePixels() with negative radius input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        ok(!pmSourceRedefinePixels(src, readout, 1.0, 2.0, -3.0),
+           "pmSourceRedefinePixels() returned false with negative radius input parameter");
+        psFree(src);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceRedefinePixels() with acceptable input parameters
+    if (1) {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        pmReadout *readout = generateSimpleReadout(NULL);
+        for (int i = 0 ; i < readout->image->numRows ; i++) {
+            for (int j = 0 ; j < readout->image->numCols ; j++) {
+                readout->image->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        // First set the pixels region to a square of radius 1.
+        bool rc = pmSourceDefinePixels(src, readout, (float) TEST_NUM_COLS/2, (float) TEST_NUM_ROWS/2, 1.0);
+        ok(rc, "pmSourceDefinePixels() returned TRUE with radius 1");
+
+        // Set these flux images so we can verify they are later psFree'ed and set to NULL
+        src->modelFlux = psImageAlloc(2, 2, PS_TYPE_F32);
+        src->psfFlux = psImageAlloc(2, 2, PS_TYPE_F32);
+
+        // Now set the radius to 2, and call pmSourceRedefinePixels()
+        rc = pmSourceRedefinePixels(src, readout, (float) TEST_NUM_COLS/2, (float) TEST_NUM_ROWS/2, 2.0);
+        ok(rc, "pmSourceRedefinePixels() returned TRUE with acceptable input parameters");
+        int expectedNumCols = 1 + (TEST_NUM_COLS/2);
+        int expectedNumRows = 1 + (TEST_NUM_COLS/2);
+        // XX: We only verify the size of the pixels, weight, maskView, and maskObj
+        // images.  A better test would verify that the actual data is set correctly.
+        // We don't do that here since it basically duplicated psLib function tests.
+        ok(src->pixels->numCols == expectedNumCols && src->pixels->numRows == expectedNumRows, 
+               "pmSourceRedefinePixels() set the size of pmSource->pixels correctly");
+        ok(src->weight->numCols == expectedNumCols && src->weight->numRows == expectedNumRows, 
+               "pmSourceRedefinePixels() set the size of pmSource->weight correctly");
+        ok(src->maskView->numCols == expectedNumCols && src->maskView->numRows == expectedNumRows, 
+               "pmSourceRedefinePixels() set the size of pmSource->maskView correctly");
+        ok(src->maskObj->numCols == expectedNumCols && src->maskObj->numRows == expectedNumRows, 
+               "pmSourceRedefinePixels() set the size of pmSource->maskObj correctly");
+
+        psRegion region = psRegionForSquare((float) TEST_NUM_COLS/2, (float) TEST_NUM_ROWS/2, 2.0);
+        region = psRegionForImage(readout->image, region);
+        ok(src->region.x0 == region.x0 && src->region.x1 == region.x1 &&
+           src->region.y0 == region.y0 && src->region.y1 == region.y1,
+          "pmSourceRedefinePixels() set pmSource->region correctly");
+
+        ok(src->modelFlux == NULL, "pmSourceRedefinePixels() set the pmSource->modelFlux to NULL");
+        ok(src->psfFlux == NULL, "pmSourceRedefinePixels() set the pmSource->psfFlux to NULL");
+
+        psFree(src);
+        psFree(readout);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourcePSFClump() tests
+    // pmPSFClump pmSourcePSFClump(psArray *sources, psMetadata *recipe)
+    // Call pmSourcePSFClump() with NULL pmSource input parameter
+    #define NUM_SOURCES 10
+    {
+        psMemId id = psMemGetId();
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        psMetadata *recipe = psMetadataAlloc();
+        bool rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "PSF_CLUMP_SN_LIM", 0, NULL, 0.0);
+        rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "MOMENTS_SX_MAX", 0, NULL, 10.0);
+        rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "MOMENTS_SY_MAX", 0, NULL, 10.0);
+        rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "MOMENTS_AR_MAX", 0, NULL, 3.0);
+
+        pmPSFClump clump = pmSourcePSFClump(NULL, recipe);
+        ok(clump.X == -1.0 && clump.dX == -1.0 && 
+           clump.Y == 0.0 && clump.dY == 0.0, "pmSourcePSFClump(NULL, recipe) returned the error clump");
+
+        psFree(sources);
+        psFree(recipe);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcePSFClump() with NULL recipe input parameter
+    {
+        psMemId id = psMemGetId();
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        psMetadata *recipe = psMetadataAlloc();
+        bool rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "PSF_CLUMP_SN_LIM", 0, NULL, 0.0);
+        rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "MOMENTS_SX_MAX", 0, NULL, 10.0);
+        rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "MOMENTS_SY_MAX", 0, NULL, 10.0);
+        rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "MOMENTS_AR_MAX", 0, NULL, 3.0);
+
+        pmPSFClump clump = pmSourcePSFClump(sources, NULL);
+        ok(clump.X == -1.0 && clump.dX == -1.0 && 
+           clump.Y == 0.0 && clump.dY == 0.0, "pmSourcePSFClump(sources, NULL) returned the error clump");
+
+        psFree(sources);
+        psFree(recipe);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcePSFClump() with acceptable input parameters
+    // XX: This is a fairly simplistic test.  We set the moments of all test pmSources
+    // to (5.0, 5.0), so the clump should be fairly easy to detect.  For further testing
+    // add:
+    //	  Add outliers to the Sx and Sy data, make sure they aren't used in the calculation
+    //    Force it to get the various metadata from the recipes arg.
+    //    Add a few non pmSource types to the sources input pmArray
+    //    Add a few NULL pmSources, or NULL pmSource->moments to the sources input pmArray
+    //    Ensure the data is saved with the KEEP_PSF_CLUMP metadata item
+    //    Add cases where you fail to find a peak.
+    //    Modify data so that the inputs have different moments
+    //
+    {
+        psMemId id = psMemGetId();
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->moments = pmMomentsAlloc();
+            src->moments->Sx = 5.0;
+            src->moments->Sy = 5.0;
+            sources->data[i] = src;
+	}
+        psMetadata *recipe = psMetadataAlloc();
+        bool rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "PSF_CLUMP_SN_LIM", 0, NULL, 0.0);
+        rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "MOMENTS_SX_MAX", 0, NULL, 10.0);
+        rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "MOMENTS_SY_MAX", 0, NULL, 10.0);
+        rc = psMetadataAddF32(recipe, PS_LIST_HEAD, "MOMENTS_AR_MAX", 0, NULL, 3.0);
+
+        pmPSFClump clump = pmSourcePSFClump(sources, recipe);
+        ok(clump.X == 5.0 && clump.dX == 0.0 && 
+           clump.Y == 5.0 && clump.dY == 0.0, "pmSourcePSFClump(sources, NULL) returned the correct clump");
+
+        if (VERBOSE) {
+            printf("clump: (%.2f %.2f %.2f %.2f)\n", clump.X, clump.dX, clump.Y, clump.dY);
+	}
+        psFree(sources);
+        psFree(recipe);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceGetModel() tests
+    // pmModel *pmSourceGetModel (bool *isPSF, const pmSource *source)
+    // Call pmSourceGetModel() with NULL pmSource input parameter
+    #define NUM_SOURCES 10
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        bool isPDF;
+        pmModel *model = pmSourceGetModel(&isPDF, NULL);
+        ok(model == NULL, "pmSourceGetModel() returned NULL with NULL pmSource input parameter");
+
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call pmSourceGetModel() with acceptable input parameters
+    #define NUM_SOURCES 10
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        // For testing only:
+        src->modelPSF = (pmModel *) 1;
+        src->modelConv = (pmModel *) 2;
+        src->modelEXT = (pmModel *) 3;
+        bool isPDF;
+
+        src->modelConv = (pmModel *) 0;
+        src->type = PM_SOURCE_TYPE_UNKNOWN;
+        pmModel *model = pmSourceGetModel(&isPDF, src);
+        ok(model == NULL, "pmSourceGetModel() returned a NULL pmModel with acceptable input parameters and src->type = PM_SOURCE_TYPE_UNKNOWN");
+        ok(false == isPDF, "pmSourceGetModel() set isPDF to FALSE");
+
+        src->type = PM_SOURCE_TYPE_STAR;
+        model = pmSourceGetModel(&isPDF, src);
+        ok(model != NULL, "pmSourceGetModel() returned a non-NULL pmModel with acceptable input parameters");
+        ok(1 == (int) model, "pmSourceGetModel() returned the correct model with pmSource->type == PM_SOURCE_TYPE_STAR");
+        ok(true == isPDF, "pmSourceGetModel() set isPDF to TRUE");
+
+        src->type = PM_SOURCE_TYPE_EXTENDED;
+        src->modelConv = (pmModel *) 2;
+        model = pmSourceGetModel(&isPDF, src);
+        ok(model != NULL, "pmSourceGetModel() returned a non-NULL pmModel with acceptable input parameters");
+        ok(2 == (int) model, "pmSourceGetModel() returned the correct model with pmSource->type == PM_SOURCE_TYPE_EXTENDED (%d)", (int) model);
+        ok(false == isPDF, "pmSourceGetModel() set isPDF to FALSE");
+
+        src->modelConv = (pmModel *) 0;
+        src->type = PM_SOURCE_TYPE_EXTENDED;
+        model = pmSourceGetModel(&isPDF, src);
+        ok(model != NULL, "pmSourceGetModel() returned a non-NULL pmModel with acceptable input parameters");
+        ok(3 == (int) model, "pmSourceGetModel() returned the correct model with pmSource->type == PM_SOURCE_TYPE_EXTENDED");
+        ok(false == isPDF, "pmSourceGetModel() set isPDF to FALSE");
+
+        src->modelPSF = NULL;
+        src->modelConv = NULL;
+        src->modelEXT = NULL;
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceOp() tests
+    // bool pmSourceOp (pmSource *source, pmModelOpMode mode, bool add,
+    //                  psMaskType maskVal, int dx, int dy)
+    // Call pmSourceOp() with NULL pmSource input parameter
+    #define NUM_SOURCES 10
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        bool rc = pmSourceOp(NULL, PM_MODEL_OP_NONE, true, 1, 0, 0);
+        ok(!rc, "pmSourceOpl() returned FALSE with NULL pmSource input parameter");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmSourceOp() with acceptable parameters
+    // We only test with a single Gaussian model, with no residuals or masks.
+    // For completeness, additional tests should be added.
+        // We should also set mode &= PM_MODEL_OP_NOISE to test that the src->weights are added.
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i = 0 ; i < TEST_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < TEST_NUM_COLS ; j++) {
+                src->pixels->data.F32[i][j] = 0.0;
+            }
+        }
+        src->peak = pmPeakAlloc(TEST_NUM_COLS/2, TEST_NUM_ROWS/2, 5.0, PM_PEAK_LONE);
+        src->type = PM_SOURCE_TYPE_STAR;
+        src->modelPSF = pmModelAlloc(pmModelClassGetType("PS_MODEL_GAUSS"));
+        psF32 *PAR = src->modelPSF->params->data.F32;
+        PAR[PM_PAR_I0] = 5.0;
+        PAR[PM_PAR_XPOS] = 0.0;
+        PAR[PM_PAR_YPOS] = 0.0;
+        PAR[PM_PAR_XPOS] = (float) (TEST_NUM_COLS/2);
+        PAR[PM_PAR_YPOS] = (float) (TEST_NUM_ROWS/2);
+        PAR[PM_PAR_SXX] = 10.0;
+        PAR[PM_PAR_SYY] = 10.0;
+
+        bool rc = pmSourceOp(src, PM_SOURCE_MODE_PSFMODEL, true, 0, 0, 0);
+        ok(rc == true, "pmSourceOp() returned TRUE with acceptable input parameters");
+        psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+        bool errorFlag = false;
+        for (int i = 0 ; i < TEST_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < TEST_NUM_COLS ; j++) {
+                x->data.F32[0] = (float) j;
+                x->data.F32[1] = (float) i;
+                psF32 modF = src->modelPSF->modelFunc (NULL, src->modelPSF->params, x);
+                psF32 imgF = src->pixels->data.F32[i][j];
+                if (!TEST_FLOATS_EQUAL(modF, imgF)) {
+                    diag("ERROR: src->pixels[%d][%d] is %.2f, should be %.2f", i, j, src->pixels->data.F32[i][j], modF);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmSourceOp() set the image pixels correctly (PSF function)");
+        psFree(x);
+        psFree(src);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmSourceOp() with acceptable parameters
+    // Test source->modelFlux
+        // We should also set mode &= PM_MODEL_OP_NOISE to test that the src->weights are added.
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        src->modelFlux = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i = 0 ; i < TEST_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < TEST_NUM_COLS ; j++) {
+                src->pixels->data.F32[i][j] = 0.0;
+                src->modelFlux->data.F32[i][j] = (float) (i + j);
+            }
+        }
+        src->peak = pmPeakAlloc(TEST_NUM_COLS/2, TEST_NUM_ROWS/2, 5.0, PM_PEAK_LONE);
+        src->type = PM_SOURCE_TYPE_STAR;
+        src->modelPSF = pmModelAlloc(pmModelClassGetType("PS_MODEL_GAUSS"));
+        psF32 *PAR = src->modelPSF->params->data.F32;
+        PAR[PM_PAR_I0] = 1.0;
+        PAR[PM_PAR_XPOS] = 0.0;
+        PAR[PM_PAR_YPOS] = 0.0;
+        PAR[PM_PAR_XPOS] = (float) (TEST_NUM_COLS/2);
+        PAR[PM_PAR_YPOS] = (float) (TEST_NUM_ROWS/2);
+        PAR[PM_PAR_SXX] = 10.0;
+        PAR[PM_PAR_SYY] = 10.0;
+
+        bool rc = pmSourceOp(src, PM_SOURCE_MODE_PSFMODEL, true, 0, 0, 0);
+        ok(rc == true, "pmSourceOp() returned TRUE with acceptable input parameters");
+        psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+        bool errorFlag = false;
+        for (int i = 0 ; i < TEST_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < TEST_NUM_COLS ; j++) {
+                x->data.F32[0] = (float) j;
+                x->data.F32[1] = (float) i;
+                psF32 modF = src->modelFlux->data.F32[i][j];
+                psF32 imgF = src->pixels->data.F32[i][j];
+                if (!TEST_FLOATS_EQUAL(modF, imgF)) {
+                    diag("ERROR: src->pixels[%d][%d] is %.2f, should be %.2f", i, j, src->pixels->data.F32[i][j], modF);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmSourceOp() set the image pixels correctly (src->modelFlux cache image)");
+        psFree(x);
+        psFree(src);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceCacheModel() tests
+    // call pmSourceCacheModel() with NULL pmSource input parameter
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmSourceCacheModel(NULL, 0);
+        ok(rc == false, "pmSourceCacheModel() returned FALSE with NULL pmSource input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceCacheModel() tests
+    // bool pmSourceCacheModel (pmSource *source, psMaskType maskVal) {
+    // call pmSourceCacheModel() with acceptable parameters
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i = 0 ; i < TEST_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < TEST_NUM_COLS ; j++) {
+                src->pixels->data.F32[i][j] = 0.0;
+            }
+        }
+        src->peak = pmPeakAlloc(TEST_NUM_COLS/2, TEST_NUM_ROWS/2, 5.0, PM_PEAK_LONE);
+        src->type = PM_SOURCE_TYPE_STAR;
+        src->modelPSF = pmModelAlloc(pmModelClassGetType("PS_MODEL_GAUSS"));
+        psF32 *PAR = src->modelPSF->params->data.F32;
+        PAR[PM_PAR_I0] = 1.0;
+        PAR[PM_PAR_XPOS] = 0.0;
+        PAR[PM_PAR_YPOS] = 0.0;
+        PAR[PM_PAR_XPOS] = (float) (TEST_NUM_COLS/2);
+        PAR[PM_PAR_YPOS] = (float) (TEST_NUM_ROWS/2);
+        PAR[PM_PAR_SXX] = 10.0;
+        PAR[PM_PAR_SYY] = 10.0;
+
+        bool rc = pmSourceCacheModel(src, 0);
+        ok(rc == true, "pmSourceCacheModel() returned TRUE with acceptable input parameters");
+
+        psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+        bool errorFlag = false;
+        for (int i = 0 ; i < TEST_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < TEST_NUM_COLS ; j++) {
+                x->data.F32[0] = (float) j;
+                x->data.F32[1] = (float) i;
+                psF32 modF = src->modelPSF->modelFunc (NULL, src->modelPSF->params, x);
+                psF32 imgF = src->modelFlux->data.F32[i][j];
+                if (!TEST_FLOATS_EQUAL(modF, imgF)) {
+                    diag("ERROR: src->modelFlux[%d][%d] is %.2f, should be %.2f", i, j, src->modelFlux->data.F32[i][j], modF);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmSourceCacheModel() set the src->modelFlux correctly (PSF function)");
+        psFree(x);
+        psFree(src);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceCachePSF() tests
+    // bool pmSourceCachePSF (pmSource *source, psMaskType maskVal) {
+    // call pmSourceCachePSF() with NULL pmSource input parameter
+    {
+        psMemId id = psMemGetId();
+        bool rc = pmSourceCachePSF(NULL, 0);
+        ok(rc == false, "pmSourceCachePSF() returned FALSE with NULL pmSource input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // call pmSourceCachePSF() with acceptable parameters
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i = 0 ; i < TEST_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < TEST_NUM_COLS ; j++) {
+                src->pixels->data.F32[i][j] = 0.0;
+            }
+        }
+        src->peak = pmPeakAlloc(TEST_NUM_COLS/2, TEST_NUM_ROWS/2, 5.0, PM_PEAK_LONE);
+        src->type = PM_SOURCE_TYPE_STAR;
+        src->modelPSF = pmModelAlloc(pmModelClassGetType("PS_MODEL_GAUSS"));
+        psF32 *PAR = src->modelPSF->params->data.F32;
+        PAR[PM_PAR_I0] = 1.0;
+        PAR[PM_PAR_XPOS] = 0.0;
+        PAR[PM_PAR_YPOS] = 0.0;
+        PAR[PM_PAR_XPOS] = (float) (TEST_NUM_COLS/2);
+        PAR[PM_PAR_YPOS] = (float) (TEST_NUM_ROWS/2);
+        PAR[PM_PAR_SXX] = 10.0;
+        PAR[PM_PAR_SYY] = 10.0;
+
+        bool rc = pmSourceCachePSF(src, 0);
+        ok(rc == true, "pmSourceCachePSF() returned TRUE with acceptable input parameters");
+
+        psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+        bool errorFlag = false;
+        for (int i = 0 ; i < TEST_NUM_ROWS ; i++) {
+            for (int j = 0 ; j < TEST_NUM_COLS ; j++) {
+                x->data.F32[0] = (float) j;
+                x->data.F32[1] = (float) i;
+                psF32 modF = src->modelPSF->modelFunc (NULL, src->modelPSF->params, x);
+                psF32 imgF = src->psfFlux->data.F32[i][j];
+                if (!TEST_FLOATS_EQUAL(modF, imgF)) {
+                    diag("ERROR: src->psfFlux[%d][%d] is %.2f, should be %.2f", i, j, src->psfFlux->data.F32[i][j], modF);
+                    errorFlag = true;
+                }
+            }
+        }
+        ok(!errorFlag, "pmSourceCachePSF() set the src->psfFlux correctly (PSF function)");
+        psFree(x);
+        psFree(src);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------
+    // pmSourceSortBySN() tests
+    // int pmSourceSortBySN (const void **a, const void **b)
+    // Call pmSourceSortBySN() with acceptable input parameters.
+    // XXX: We don't test with NULL input parameters since this function has no PS_ASSERTS to protect
+    // against that.
+/* XXXX: Compiler errors: fix this
+    {
+        psMemId id = psMemGetId();
+        pmSource *src1 = pmSourceAlloc();
+        src1->peak = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        src1->peak->SN = 10.0;
+        pmSource *src2 = pmSourceAlloc();
+        src2->peak = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        src2->peak->SN = 20.0;
+
+        int rc = pmSourceSortBySN((const void **) &src1, (const void **) &src2);
+        ok(rc == 1, "pmSourceSortBySN() returned correct result (source1 < source2) (%d)", rc);
+        rc = pmSourceSortBySN((const void **) &src2, (const void **) &src1);
+        ok(rc == -1, "pmSourceSortBySN() returned correct result (source2 < source1) (%d)", rc);
+        rc = pmSourceSortBySN((const void **) &src1, (const void **) &src1);
+        ok(rc == 0, "pmSourceSortBySN() returned correct result (source1 == source2) (%d)", rc);
+
+        psFree(src1);
+        psFree(src2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------
+    // pmSourceSortByY() tests
+    // int pmSourceSortByY (const void **a, const void **b)
+    // Call pmSourceSortByY() with acceptable input parameters.
+    // XXX: We don't test with NULL input parameters since this function has no PS_ASSERTS to protect
+    // against that.
+    {
+        psMemId id = psMemGetId();
+        pmSource *src1 = pmSourceAlloc();
+        src1->peak = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        src1->peak->y = 10.0;
+        pmSource *src2 = pmSourceAlloc();
+        src2->peak = pmPeakAlloc(3, 4, 2.0, PM_PEAK_LONE);
+        src2->peak->y = 20.0;
+
+        int rc = pmSourceSortByY((const void **) &src1, (const void **) &src2);
+        ok(rc == -1, "pmSourceSortByY() returned correct result (source1 < source2) (%d)", rc);
+        rc = pmSourceSortByY((const void **) &src2, (const void **) &src1);
+        ok(rc == 1, "pmSourceSortByY() returned correct result (source2 < source1) (%d)", rc);
+        rc = pmSourceSortByY((const void **) &src1, (const void **) &src1);
+        ok(rc == 0, "pmSourceSortByY() returned correct result (source1 == source2) (%d)", rc);
+
+        psFree(src1);
+        psFree(src2);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+*/
+
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceContour.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceContour.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceContour.c	(revision 22322)
@@ -0,0 +1,179 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    XXX: All functions only tested with unallowable input parameters.
+    I tried to use acceptable data, but could not get the source code to work the
+    way I thought it should have.
+*/
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           (10)
+#define TEST_NUM_COLS           (16)
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         10
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.0001)
+#define NUM_SOURCES		100
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(11);
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceContour() tests
+    // psArray *pmSourceContour (psImage *image, int xc, int yc, float threshold)
+    // Call pmSourceContour() with NULL psImage input parameter
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psArray *array = pmSourceContour(NULL, 1, 2, 3.0);
+        ok(array == NULL, "pmSourceContour() returned NULL with NULL psImage input parameter");
+        psFree(img);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceContour() with unallowed row/column numbers
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psArray *array = pmSourceContour(img, -1, 2, 3.0);
+        ok(array == NULL, "pmSourceContour() returned NULL with column = -1");
+        array = pmSourceContour(img, TEST_NUM_COLS, 2, 3.0);
+        ok(array == NULL, "pmSourceContour() returned NULL with column >= numCols");
+        array = pmSourceContour(img, 1, -1, 3.0);
+        ok(array == NULL, "pmSourceContour() returned NULL with row = -1");
+        array = pmSourceContour(img, 1, TEST_NUM_ROWS, 3.0);
+        ok(array == NULL, "pmSourceContour() returned NULL with row >= numRows");
+
+        psFree(img);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceContour() with acceptable input parameters
+    // XXX: These tests currently fail
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i = 0 ; i < img->numRows ; i++) {
+            for (int j = 0 ; j < img->numCols ; j++) {
+                img->data.F32[i][j] = (float) ((TEST_NUM_ROWS + TEST_NUM_COLS) - (abs(i - (TEST_NUM_ROWS/2)) + abs(j - (TEST_NUM_COLS/2))));
+	    }
+	}
+        if (1) {
+            for (int i = 0 ; i < img->numRows ; i++) {
+                for (int j = 0 ; j < img->numCols ; j++) {
+                    printf("(%.0f)", img->data.F32[i][j]);
+    	    }
+                printf("\n");
+	    }
+	}
+
+        psArray *array = pmSourceContour(img, TEST_NUM_COLS/2, TEST_NUM_ROWS/2, 22.0);
+        ok(array != NULL, "pmSourceContour() returned non-NULL with acceptable input parameters");
+        for (int i = 0 ; i < array->n ; i++) {
+            psVector *vec = (psVector *) array->data[i];
+            printf("Point %d: (%.2f %.2f)\n", i, vec->data.F32[0], vec->data.F32[1]);
+	}
+
+        psFree(array);
+        psFree(img);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceContour_Crude() tests
+    // psArray *pmSourceContour_Crude_Crude(pmSource *source, psImage *image, psF32 level)
+    // Call pmSourceContour_Crude() with NULL psSource input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        src->moments = pmMomentsAlloc();
+        src->modelEXT = pmModelAlloc(1);
+        psImage *img = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i = 0 ; i < img->numRows ; i++) {
+            for (int j = 0 ; j < img->numCols ; j++) {
+                if ((i >= TEST_NUM_ROWS/4) && (i < 3*TEST_NUM_ROWS/4) &&
+                    (j >= TEST_NUM_COLS/4) && (j < 3*TEST_NUM_COLS/4)) {
+                    img->data.F32[i][j] = 5.0;
+		} else {
+                    img->data.F32[i][j] = 0.0;
+		}
+	    }
+	}
+        psArray *array = pmSourceContour_Crude(NULL, img, 3.0);
+        ok(array == NULL, "pmSourceContour_Crude() returned NULL with NULL pmSource input parameter");
+        psFree(img);
+        psFree(src);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceContour_Crude() with NULL psImage input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        src->moments = pmMomentsAlloc();
+        src->modelEXT = pmModelAlloc(1);
+        psImage *img = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i = 0 ; i < img->numRows ; i++) {
+            for (int j = 0 ; j < img->numCols ; j++) {
+                if ((i >= TEST_NUM_ROWS/4) && (i < 3*TEST_NUM_ROWS/4) &&
+                    (j >= TEST_NUM_COLS/4) && (j < 3*TEST_NUM_COLS/4)) {
+                    img->data.F32[i][j] = 5.0;
+		} else {
+                    img->data.F32[i][j] = 0.0;
+		}
+	    }
+	}
+        psArray *array = pmSourceContour_Crude(src, NULL, 3.0);
+        ok(array == NULL, "pmSourceContour_Crude() returned NULL with NULL pmImage input parameter");
+        psFree(img);
+        psFree(src);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceContour_Crude() with acceptable input parameters
+    // XXX: Must correct this
+    if (0) {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        src->moments = pmMomentsAlloc();
+        src->modelEXT = pmModelAlloc(1);
+        psImage *img = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+
+        if (0) {
+            for (int i = 0 ; i < img->numRows ; i++) {
+                for (int j = 0 ; j < img->numCols ; j++) {
+                    img->data.F32[i][j] = (float) ((TEST_NUM_ROWS + TEST_NUM_COLS) - (abs(i - (TEST_NUM_ROWS/2)) + abs(j - (TEST_NUM_COLS/2))));
+		}
+	    }
+	}
+        printf("Calling pmSourceContour_Crude()\n");
+        psArray *array = pmSourceContour_Crude(src, img, 22.0);
+        printf("Called pmSourceContour_Crude()\n");
+        ok(array != NULL, "pmSourceContour_Crude() returned non-NULL with NULL pmImage input parameter");
+        psFree(img);
+        psFree(src);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceExtendedPars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceExtendedPars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceExtendedPars.c	(revision 22322)
@@ -0,0 +1,121 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+        All functions are tested.
+*/
+
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(35);
+
+    // ----------------------------------------------------------------------
+    // pmSourceExtendedParsAlloc() tests
+    {
+        psMemId id = psMemGetId();
+        pmSourceExtendedPars *tmp = pmSourceExtendedParsAlloc();
+        ok(tmp && psMemCheckSourceExtendedPars(tmp), "pmSourceExtendedParsAlloc() allocated a pmSourceExtendedPars struct");
+
+        ok(tmp->profile == NULL, "pmSourceExtendedParsAlloc() set the ->profile member to NULL");
+        ok(tmp->annuli == NULL, "pmSourceExtendedParsAlloc() set the ->annuli member to NULL");
+        ok(tmp->isophot == NULL, "pmSourceExtendedParsAlloc() set the ->isophot member to NULL");
+        ok(tmp->petrosian == NULL, "pmSourceExtendedParsAlloc() set the ->petrosian member to NULL");
+        ok(tmp->kron == NULL, "pmSourceExtendedParsAlloc() set the ->kron member to NULL");
+
+        psFree(tmp);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceRadialProfileAlloc() tests
+    {
+        psMemId id = psMemGetId();
+        pmSourceRadialProfile *tmp = pmSourceRadialProfileAlloc();
+        ok(tmp && psMemCheckSourceRadialProfile(tmp), "pmSourceRadialProfile() allocated a pmSourceRadialProfilestruct");
+
+        ok(tmp->radius == NULL, "pmSourceRadialProfileAlloc() set the ->radius member to NULL");
+        ok(tmp->flux == NULL, "pmSourceRadialProfileAlloc() set the ->flux member to NULL");
+        ok(tmp->weight == NULL, "pmSourceRadialProfileAlloc() set the ->weight member to NULL");
+
+        psFree(tmp);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceIsophotalValuesAlloc() tests
+    {
+        psMemId id = psMemGetId();
+        pmSourceIsophotalValues *tmp = pmSourceIsophotalValuesAlloc();
+        ok(tmp && psMemCheckSourceIsophotalValues(tmp), "pmSourceIsophotalValues() allocated a pmSourceIsophotalValuesstruct");
+
+        ok(tmp->mag == 0.0, "pmSourceIsophotalValuesAlloc() set the ->mag member to 0.0");
+        ok(tmp->magErr == 0.0, "pmSourceIsophotalValuesAlloc() set the ->magErr member to 0.0");
+        ok(tmp->rad == 0.0, "pmSourceIsophotalValuesAlloc() set the ->rad member to 0.0");
+        ok(tmp->radErr == 0.0, "pmSourceIsophotalValuesAlloc() set the ->radErr member to 0.0");
+
+        psFree(tmp);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourcePetrosianValuesAlloc() tests
+    {
+        psMemId id = psMemGetId();
+        pmSourcePetrosianValues *tmp = pmSourcePetrosianValuesAlloc();
+        ok(tmp && psMemCheckSourcePetrosianValues(tmp), "pmSourcePetrosianValues() allocated a pmSourcePetrosianValuesstruct");
+
+        ok(tmp->mag == 0.0, "pmSourcePetrosianValuesAlloc() set the ->mag member to 0.0");
+        ok(tmp->magErr == 0.0, "pmSourcePetrosianValuesAlloc() set the ->magErr member to 0.0");
+        ok(tmp->rad == 0.0, "pmSourcePetrosianValuesAlloc() set the ->rad member to 0.0");
+        ok(tmp->radErr == 0.0, "pmSourcePetrosianValuesAlloc() set the ->radErr member to 0.0");
+
+        psFree(tmp);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceKronValuesAlloc() tests
+    {
+        psMemId id = psMemGetId();
+        pmSourceKronValues *tmp = pmSourceKronValuesAlloc();
+        ok(tmp && psMemCheckSourceKronValues(tmp), "pmSourceKronValues() allocated a pmSourceKronValuesstruct");
+
+        ok(tmp->mag == 0.0, "pmSourceKronValuesAlloc() set the ->mag member to 0.0");
+        ok(tmp->magErr == 0.0, "pmSourceKronValuesAlloc() set the ->magErr member to 0.0");
+        ok(tmp->rad == 0.0, "pmSourceKronValuesAlloc() set the ->rad member to 0.0");
+        ok(tmp->radErr == 0.0, "pmSourceKronValuesAlloc() set the ->radErr member to 0.0");
+
+        psFree(tmp);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceAnnuliAlloc() tests
+    {
+        psMemId id = psMemGetId();
+        pmSourceAnnuli *tmp = pmSourceAnnuliAlloc();
+        ok(tmp && psMemCheckSourceAnnuli(tmp), "pmSourceAnnuli() allocated a pmSourceAnnulistruct");
+
+        ok(tmp->flux == NULL, "pmSourceAnnuliAlloc() set the ->flux member to NULL");
+        ok(tmp->fluxErr == NULL, "pmSourceAnnuliAlloc() set the ->fluxErr member to NULL");
+        ok(tmp->fluxVar == NULL, "pmSourceAnnuliAlloc() set the ->fluxVar member to NULL");
+
+        psFree(tmp);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceFitModel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceFitModel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceFitModel.c	(revision 22322)
@@ -0,0 +1,191 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+bool fitModels (psRandom *seed, float flux, float radius, float sigma);
+bool fitModelFlux (psRandom *seed, float flux, float radius, float sigma);
+
+// tests to check accuracy of fitted models for a range of fit radii, sigma, and flux.
+// we generate a fake source, then fit the model to it.  the difference or fractional
+// difference between the true and fitted parameters is saved.
+// we run these tests 200 times each an examine the resulting distribution of deviations.
+// the tests fail if the stdevs are more than 2x the expected stdev based on poisson noise
+// ** is 2x too generous?
+int main (void)
+{
+    pmModelGroupInit ();
+    pmSourceFitModelInit (15, 0.01, 1.0, true);
+
+    // psTraceSetLevel ("psModules.objects.pmSourceFitModel", 10);
+    // psTraceSetLevel ("psLib.math.psMinimizeLMChi2", 10);
+
+    plan_tests(240);
+
+    // build a gauss-deviate vector (mean = 0.0, sigma = 1.0)
+    psRandom *seed = psRandomAlloc (PS_RANDOM_TAUS, 0);
+
+    static float radius[] = {3.0, 5.0, 7.0, 10.0, 15.0, 25.0};
+    static float sigma[] = {1.0, 1.5, 2.0};
+    static float flux[] = {10000.0, 3000.0, 1000.0, 300.0, 100.0, 30.0, 10.0};
+
+    // drop-dead simple: should always work
+    bool status = fitModels (seed, 10000.0, 10.0, 2.0);
+    skip_start (!status, 240, "*** BASIC MODEL FITTING FAILS! *** : skipping related tests");
+
+    for (int i = 0; i < sizeof(sigma)/sizeof(float); i++) {
+        for (int j = 0; j < sizeof(radius)/sizeof(float); j++) {
+            for (int k = 0; k < sizeof(flux)/sizeof(float); k++) {
+                fitModels (seed, flux[k], radius[j], sigma[i]);
+            }
+        }
+    }
+
+    skip_end();
+    return exit_status();
+}
+
+static psVector *par1 = NULL;
+static psVector *par2 = NULL;
+static psVector *par3 = NULL;
+static psVector *par4 = NULL;
+static psVector *par5 = NULL;
+
+# define NMODELS 200
+bool fitModels (psRandom *seed, float flux, float radius, float sigma)
+{
+
+    psMemId id = psMemGetId();
+
+    diag("test model fit - flux: %f, radius: %f, sigma: %f", flux, radius, sigma);
+
+    par1 = psVectorAllocEmpty (NMODELS, PS_TYPE_F32);
+    par2 = psVectorAllocEmpty (NMODELS, PS_TYPE_F32);
+    par3 = psVectorAllocEmpty (NMODELS, PS_TYPE_F32);
+    par4 = psVectorAllocEmpty (NMODELS, PS_TYPE_F32);
+    par5 = psVectorAllocEmpty (NMODELS, PS_TYPE_F32);
+
+    for (int i = 0; i < NMODELS; i++) {
+        fitModelFlux (seed, flux, radius, sigma);
+    }
+
+    float signal = 2*M_PI*sigma*sigma*flux;
+    float noise = sqrt(signal + 4*M_PI*sigma*sigma*(100 + PS_SQR(5)));
+    float dMag = noise / signal;
+    float dPos = sigma * dMag;
+    diag ("signal: %f, noise: %f, dMag: %f, dPos: %f", signal, noise, dMag, dPos);
+
+    bool status = (par1->n == NMODELS);
+    ok (status, "all %d tests passed", NMODELS);
+
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+    psVectorStats (stats, par1, NULL, NULL, 0);
+    ok ((stats->sampleStdev/dMag < 2.0), "Io ref/fit stdev: %e : %e sigma", stats->sampleStdev, stats->sampleStdev/dMag);
+    psVectorStats (stats, par2, NULL, NULL, 0);
+    ok ((stats->sampleStdev/dPos < 2.0), "Xo ref/fit stdev: %e : %e sigma", stats->sampleStdev, stats->sampleStdev/dPos);
+    psVectorStats (stats, par3, NULL, NULL, 0);
+    ok ((stats->sampleStdev/dPos < 2.0), "Yo ref/fit stdev: %e : %e sigma", stats->sampleStdev, stats->sampleStdev/dPos);
+    psVectorStats (stats, par4, NULL, NULL, 0);
+    ok ((stats->sampleStdev/dMag < 2.0), "Sx ref/fit stdev: %e : %e sigma", stats->sampleStdev, stats->sampleStdev/dMag);
+    psVectorStats (stats, par5, NULL, NULL, 0);
+    ok ((stats->sampleStdev/dMag < 2.0), "Sy ref/fit stdev: %e : %e sigma", stats->sampleStdev, stats->sampleStdev/dMag);
+
+    psFree (par1);
+    psFree (par2);
+    psFree (par3);
+    psFree (par4);
+    psFree (par5);
+    psFree (stats);
+
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+    return status;
+}
+
+bool fitModelFlux (psRandom *seed, float flux, float radius, float sigma)
+{
+
+    psVector *rnd = psVectorAlloc (1000, PS_TYPE_F32);
+    for (int i = 0; i < rnd->n; i++) {
+        rnd->data.F32[i] = psRandomGaussian (seed);
+    }
+
+    // construct a model
+    pmSource *source = pmSourceAlloc ();
+    source->moments = pmMomentsAlloc ();
+
+    pmModelType type = pmModelClassGetType ("PS_MODEL_GAUSS");
+    source->modelEXT = pmModelAlloc (type);
+
+    source->modelEXT->params->data.F32[0] = 0;
+    source->modelEXT->params->data.F32[1] = flux;
+    source->modelEXT->params->data.F32[2] = 50;
+    source->modelEXT->params->data.F32[3] = 50;
+    source->modelEXT->params->data.F32[4] = 2.0*sqrt(sigma);
+    source->modelEXT->params->data.F32[5] = 2.0*sqrt(sigma);
+    source->modelEXT->params->data.F32[6] = 0;
+
+    source->pixels = psImageAlloc (100, 100, PS_TYPE_F32);
+    source->weight = psImageAlloc (100, 100, PS_TYPE_F32);
+    source->mask   = psImageAlloc (100, 100, PS_TYPE_U8);
+    psImageInit (source->pixels, 0.0);
+    psImageInit (source->weight, 0.0);
+    psImageInit (source->mask, 0);
+
+    // create an image with the model, and add noise: gain is 1, subtracted sky is 100, readnoise is 5
+    pmModelAdd (source->pixels, source->mask, source->modelEXT, PM_MODEL_OP_FULL);
+    int npix = 0;
+    for (int j = 0; j < source->pixels->numRows; j++) {
+        for (int i = 0; i < source->pixels->numCols; i++) {
+            float flux = source->pixels->data.F32[j][i];
+            float var = flux + 100 + PS_SQR(5);
+            source->pixels->data.F32[j][i] += rnd->data.F32[npix]*sqrt(var);
+            source->weight->data.F32[j][i] = var;
+            npix ++;
+            if (npix == rnd->n)
+                npix = 0;
+        }
+    }
+
+    // psFits *fits = psFitsOpen ("test.fits", "w");
+    // psFitsWriteImage (fits, NULL, source->pixels, 0, NULL);
+    // psFitsClose (fits);
+
+    // save the original model, modify params
+    pmModel *guess = pmModelCopy (source->modelEXT);
+    guess->params->data.F32[1] *= 0.9;
+    guess->params->data.F32[2] += 1.0;
+    guess->params->data.F32[3] -= 1.0;
+    guess->params->data.F32[4] *= 0.9;
+    guess->params->data.F32[5] *= 0.9;
+
+    psImageKeepCircle (source->mask, 50, 50, radius, "OR", PM_MASK_MARK);
+    bool status = pmSourceFitModel (source, guess, PM_SOURCE_FIT_EXT);
+    if (!status) {
+        psFree (rnd);
+        psFree (source);
+        psFree (guess);
+        return false;
+    }
+    psImageKeepCircle (source->mask, 50, 50, radius, "AND", PS_NOT_U8(PM_MASK_MARK));
+
+    par1->data.F32[par1->n] = (source->modelEXT->params->data.F32[1] / guess->params->data.F32[1]);
+    par2->data.F32[par2->n] = (source->modelEXT->params->data.F32[2] - guess->params->data.F32[2]);
+    par3->data.F32[par3->n] = (source->modelEXT->params->data.F32[3] - guess->params->data.F32[3]);
+    par4->data.F32[par4->n] = (source->modelEXT->params->data.F32[4] / guess->params->data.F32[4]);
+    par5->data.F32[par5->n] = (source->modelEXT->params->data.F32[5] / guess->params->data.F32[5]);
+
+    psVectorExtend (par1, 100, 1);
+    psVectorExtend (par2, 100, 1);
+    psVectorExtend (par3, 100, 1);
+    psVectorExtend (par4, 100, 1);
+    psVectorExtend (par5, 100, 1);
+
+    psFree (rnd);
+    psFree (source);
+    psFree (guess);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceFitModel_Delta.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceFitModel_Delta.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceFitModel_Delta.c	(revision 22322)
@@ -0,0 +1,238 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+bool fitModels (psRandom *seed, float flux, float radius, float sigma);
+bool fitModelFlux (psRandom *seed, float flux, float radius, float sigma);
+bool printDev (float *src, float *par, int Npar, bool absolute);
+
+int main (void)
+{
+    pmModelGroupInit ();
+    pmSourceFitModelInit (15, 0.01, 1.0, true);
+
+    float flux = 10000;
+    float sigma = 2.0;
+    float radius = 10.0;
+
+    plan_tests(240);
+
+    // build a gauss-deviate vector (mean = 0.0, sigma = 1.0)
+    psRandom *seed = psRandomAlloc (PS_RANDOM_TAUS, 0);
+
+    // noise vector to noise up the image
+    psVector *rnd = psVectorAlloc (1000, PS_TYPE_F32);
+    for (int i = 0; i < rnd->n; i++) {
+        rnd->data.F32[i] = psRandomGaussian (seed);
+    }
+
+    // construct a GAUSS model
+    pmSource *source = pmSourceAlloc ();
+    source->moments = pmMomentsAlloc ();
+
+    pmModelType type = pmModelClassGetType ("PS_MODEL_GAUSS");
+    source->modelEXT = pmModelAlloc (type);
+
+    source->modelEXT->params->data.F32[0] = 0;
+    source->modelEXT->params->data.F32[1] = flux;
+    source->modelEXT->params->data.F32[2] = 50;
+    source->modelEXT->params->data.F32[3] = 50;
+    source->modelEXT->params->data.F32[4] = 2.0*sqrt(sigma);
+    source->modelEXT->params->data.F32[5] = 2.0*sqrt(sigma);
+    source->modelEXT->params->data.F32[6] = 0;
+
+    source->pixels = psImageAlloc (100, 100, PS_TYPE_F32);
+    source->weight = psImageAlloc (100, 100, PS_TYPE_F32);
+    source->mask   = psImageAlloc (100, 100, PS_TYPE_U8);
+    psImageInit (source->pixels, 0.0);
+    psImageInit (source->weight, 0.0);
+    psImageInit (source->mask, 0);
+
+    // create an image with the model, and add noise: gain is 1, subtracted sky is 100, readnoise is 5
+    pmModelAdd (source->pixels, source->mask, source->modelEXT, PM_MODEL_OP_FULL);
+    int npix = 0;
+    for (int j = 0; j < source->pixels->numRows; j++) {
+        for (int i = 0; i < source->pixels->numCols; i++) {
+            float flux = source->pixels->data.F32[j][i];
+            float var = flux + 100 + PS_SQR(5);
+            source->pixels->data.F32[j][i] += rnd->data.F32[npix]*sqrt(var);
+            source->weight->data.F32[j][i] = var;
+            npix ++;
+            if (npix == rnd->n)
+                npix = 0;
+        }
+    }
+
+    // fit with a PGAUSS model
+    pmModel *guess;
+
+    type = pmModelClassGetType ("PS_MODEL_PGAUSS");
+    guess = pmModelAlloc (type);
+
+    guess->params->data.F32[0] = 0;
+    guess->params->data.F32[1] = flux;
+    guess->params->data.F32[2] = 50;
+    guess->params->data.F32[3] = 50;
+    guess->params->data.F32[4] = 2.0*sqrt(sigma);
+    guess->params->data.F32[5] = 2.0*sqrt(sigma);
+    guess->params->data.F32[6] = 0;
+    // modify guess
+    guess->params->data.F32[1] *= 0.9;
+    guess->params->data.F32[2] += 1.0;
+    guess->params->data.F32[3] -= 1.0;
+    guess->params->data.F32[4] *= 0.9;
+    guess->params->data.F32[5] *= 0.9;
+
+    psImageKeepCircle (source->mask, 50, 50, radius, "OR", PM_MASK_MARK);
+    pmSourceFitModel (source, guess, PM_SOURCE_FIT_EXT);
+    psImageKeepCircle (source->mask, 50, 50, radius, "AND", PS_NOT_U8(PM_MASK_MARK));
+
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 1, false);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 2, true);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 3, true);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 4, false);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 5, false);
+
+    psImageKeepCircle (source->mask, 50, 50, radius, "OR", PM_MASK_MARK);
+    pmSourceFitModel (source, guess, PM_SOURCE_FIT_PSF);
+    psImageKeepCircle (source->mask, 50, 50, radius, "AND", PS_NOT_U8(PM_MASK_MARK));
+
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 1, false);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 2, true);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 3, true);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 4, false);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 5, false);
+
+    // muck up the Sx, Sy terms a little : how does this affect the dparams?
+    float Sx = guess->params->data.F32[4];
+    float Sy = guess->params->data.F32[5];
+    guess->params->data.F32[4] = 0.95*Sx;
+    guess->params->data.F32[5] = 0.95*Sy;
+
+    psImageKeepCircle (source->mask, 50, 50, radius, "OR", PM_MASK_MARK);
+    pmSourceFitModel (source, guess, PM_SOURCE_FIT_PSF);
+    psImageKeepCircle (source->mask, 50, 50, radius, "AND", PS_NOT_U8(PM_MASK_MARK));
+
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 1, false);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 2, true);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 3, true);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 4, false);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 5, false);
+
+    // muck up the Sx, Sy terms a little : how does this affect the dparams?
+    guess->params->data.F32[4] = 0.99*Sx;
+    guess->params->data.F32[5] = 0.99*Sy;
+
+    psImageKeepCircle (source->mask, 50, 50, radius, "OR", PM_MASK_MARK);
+    pmSourceFitModel (source, guess, PM_SOURCE_FIT_PSF);
+    psImageKeepCircle (source->mask, 50, 50, radius, "AND", PS_NOT_U8(PM_MASK_MARK));
+
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 1, false);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 2, true);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 3, true);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 4, false);
+    printDev (source->modelEXT->params->data.F32, guess->params->data.F32, 5, false);
+
+    psFree (rnd);
+    psFree (source);
+    psFree (guess);
+
+    return true;
+}
+
+bool printDev (float *src, float *fit, int Npar, bool absolute)
+{
+    float dev;
+    if (absolute) {
+        dev = (src[Npar]-fit[Npar]);
+        fprintf (stderr, "par %d : %f vs %f : abso dev %f\n", Npar, src[Npar], fit[Npar], dev);
+    } else {
+        dev = (src[Npar]/fit[Npar]);
+        fprintf (stderr, "par %d : %f vs %f : frac dev %f\n", Npar, src[Npar], fit[Npar], dev);
+    }
+    return true;
+}
+
+# if (0)
+    int main (void)
+{
+    pmModelGroupInit ();
+    pmSourceFitModelInit (15, 0.01, 1.0, true);
+
+    plan_tests(240);
+
+    // build a gauss-deviate vector (mean = 0.0, sigma = 1.0)
+    psRandom *seed = psRandomAlloc (PS_RANDOM_TAUS, 0);
+
+    static float radius[] = {3.0, 5.0, 7.0, 10.0, 15.0, 25.0};
+    static float sigma[] = {1.0, 1.5, 2.0};
+    static float flux[] = {10000.0, 3000.0, 1000.0, 300.0, 100.0, 30.0, 10.0};
+
+    for (int i = 0; i < sizeof(sigma)/sizeof(float); i++) {
+        for (int j = 0; j < sizeof(radius)/sizeof(float); j++) {
+            for (int k = 0; k < sizeof(flux)/sizeof(float); k++) {
+                fitModels (seed, flux[k], radius[j], sigma[i]);
+            }
+        }
+    }
+
+    return exit_status();
+}
+
+static psVector *par1 = NULL;
+static psVector *par2 = NULL;
+static psVector *par3 = NULL;
+static psVector *par4 = NULL;
+static psVector *par5 = NULL;
+
+bool fitModels (psRandom *seed, float flux, float radius, float sigma)
+{
+
+    psMemId id = psMemGetId();
+
+    diag("test model fit - flux: %f, radius: %f, sigma: %f", flux, radius, sigma);
+
+    par1 = psVectorAllocEmpty (200, PS_TYPE_F32);
+    par2 = psVectorAllocEmpty (200, PS_TYPE_F32);
+    par3 = psVectorAllocEmpty (200, PS_TYPE_F32);
+    par4 = psVectorAllocEmpty (200, PS_TYPE_F32);
+    par5 = psVectorAllocEmpty (200, PS_TYPE_F32);
+
+    for (int i = 0; i < 200; i++) {
+        fitModelFlux (seed, flux, radius, sigma);
+    }
+
+    float signal = 2*M_PI*sigma*sigma*flux;
+    float noise = sqrt(signal + 4*M_PI*sigma*sigma*(100 + PS_SQR(5)));
+    float dMag = noise / signal;
+    float dPos = sigma * dMag;
+    diag ("signal: %f, noise: %f, dMag: %f, dPos: %f", signal, noise, dMag, dPos);
+
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+    psVectorStats (stats, par1, NULL, NULL, 0);
+    ok ((stats->sampleStdev/dMag < 2.0), "Io ref/fit stdev: %f : %f sigma", stats->sampleStdev, stats->sampleStdev/dMag);
+    psVectorStats (stats, par2, NULL, NULL, 0);
+    ok ((stats->sampleStdev/dPos < 2.0), "Xo ref/fit stdev: %f : %f sigma", stats->sampleStdev, stats->sampleStdev/dPos);
+    psVectorStats (stats, par3, NULL, NULL, 0);
+    ok ((stats->sampleStdev/dPos < 2.0), "Yo ref/fit stdev: %f : %f sigma", stats->sampleStdev, stats->sampleStdev/dPos);
+    psVectorStats (stats, par4, NULL, NULL, 0);
+    ok ((stats->sampleStdev/dMag < 2.0), "Sx ref/fit stdev: %f : %f sigma", stats->sampleStdev, stats->sampleStdev/dMag);
+    psVectorStats (stats, par5, NULL, NULL, 0);
+    ok ((stats->sampleStdev/dMag < 2.0), "Sy ref/fit stdev: %f : %f sigma", stats->sampleStdev, stats->sampleStdev/dMag);
+
+    psFree (par1);
+    psFree (par2);
+    psFree (par3);
+    psFree (par4);
+    psFree (par5);
+    psFree (stats);
+
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    return true;
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceFitSet.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceFitSet.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceFitSet.c	(revision 22322)
@@ -0,0 +1,726 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested except:
+        pmSourceFitSetCheckLimits()
+        pmSourceFitSetFunction()
+    Those functions set static variables which aer invisible to test code.
+
+    These functions are very lightly tested and must be augmented:
+	pmSourceFitSet()
+	pmSourceFitSetMasks()
+*/
+
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           (16)
+#define TEST_NUM_COLS           (30)
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.0001)
+#define NUM_MODELS		5
+
+pmSource *create_pmSource() {
+    pmSource *src = pmSourceAlloc();
+    if (1) {
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        src->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+        if (1) {
+            for (int i = 0 ; i < TEST_NUM_ROWS ; i++) {
+                for (int j = 0 ; j < TEST_NUM_COLS ; j++) {
+                    src->pixels->data.F32[i][j] = 0.0;
+                    src->weight->data.F32[i][j] = 1.0;
+                    src->maskObj->data.U8[i][j] = 0;
+                }
+            }
+        }
+        if (1) {
+            int halfRows = TEST_NUM_ROWS/2;
+            int halfCols = TEST_NUM_COLS/2;
+            for (int i = halfRows-1 ; i < halfRows+1 ; i++) {
+                for (int j = halfCols-1 ; j < halfCols+1 ; j++) {
+                    src->pixels->data.F32[i][j] = 1.0;
+                }
+            }
+            src->pixels->data.F32[halfRows][halfCols] = 5.0;
+        }
+    }
+    return(src);
+}
+
+bool call_pmSourceFitSet() {
+    psArray *modelSet = psArrayAlloc(NUM_MODELS);
+    for (int i = 0 ; i < NUM_MODELS ; i++) {
+        modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+    }
+    pmSource *src = create_pmSource();
+    bool rc = pmSourceFitSet(src, modelSet, PM_SOURCE_FIT_PSF, 1);
+    if (!rc) {
+        diag("ERROR: pmSourceFitSet() returned FALSE");
+        return(false);
+    }
+    psFree(src);
+    for (int i = 0 ; i < NUM_MODELS ; i++) {
+        psFree(modelSet->data[i]);
+        modelSet->data[i] = NULL;
+    }
+    psFree(modelSet);
+    pmModelClassCleanup();
+  
+    return(true);
+}
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(70);
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceFitSetDataAlloc() tests
+    // Call pmSourceFitSetDataAlloc() with NULL psArray input parameter
+    {
+        psMemId id = psMemGetId();
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(1);
+	}
+        pmSourceFitSetData *fitSetData = pmSourceFitSetDataAlloc(NULL);
+        ok(fitSetData == NULL, "pmSourceFitSetDataAlloc() returned NULL with NULL psArray input parameter");
+        psFree(fitSetData);
+        pmModelClassCleanup();
+        psFree(modelSet); 
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFitSetDataAlloc() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        ok(set != NULL && psMemCheckSourceFitSetData(set),
+          "pmSourceFitSetDataAlloc() returned non-NULL with acceptable input parameters");
+        ok(set->paramSet != NULL && set->paramSet->n == modelSet->n, 
+          "pmSourceFitSetDataAlloc() set the set->paramSet psArray correctly");
+        ok(set->derivSet != NULL && set->derivSet->n == modelSet->n, 
+          "pmSourceFitSetDataAlloc() set the set->derivSet psArray correctly");
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            int nParams = pmModelClassParameterCount(i);
+            psVector *tmpV = (psVector *) (set->paramSet->data[i]);
+            ok(tmpV != NULL && psMemCheckVector(tmpV) && tmpV->n == nParams,
+               "pmSourceFitSetDataAlloc() set the set->paramSet->data psVector correctly");
+
+            tmpV = (psVector *) (set->derivSet->data[i]);
+            ok(tmpV != NULL && psMemCheckVector(tmpV) && tmpV->n == nParams,
+               "pmSourceFitSetDataAlloc() set the set->derivSet->data psVector correctly");
+
+            bool errorFlag = false;
+            for (int i = 0 ; i < tmpV->n ; i++) {
+                if (tmpV->data.F32[i] != 0.0) {
+                    errorFlag = true;
+		}
+	    }
+            ok(!errorFlag, "pmSourceFitSetDataAlloc() set the set->derivSet->data correctly");
+	}
+        
+        psFree(set);
+        pmModelClassCleanup();
+        psFree(modelSet); 
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceFitSetCheckLimits() tests
+    // Call pmSourceFitSetCheckLimits() with thisSet == NULL
+    // XXX: Can not test full functionality of pmSourceFitSetCheckLimits() because it
+    // requires that the static variable thisSet be allocated.
+    {
+        psMemId id = psMemGetId();
+        #define NUM_PARAMS 10
+        psF32 *params = (psF32 *) psAlloc(NUM_PARAMS * sizeof(psF32));
+        psF32 *betas = (psF32 *) psAlloc(NUM_PARAMS * sizeof(psF32));
+        bool rc = pmSourceFitSetCheckLimits(PS_MINIMIZE_PARAM_MIN, 10, params, betas);
+        ok(rc == false, "pmSourceFitSetCheckLimits() returned NULL with thisSet == FALSE");
+        psFree(params);
+        psFree(betas);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceFitSetFunction() tests
+    // Call pmSourceFitSetFunction() with thisSet == NULL
+    // XXX: Can not test full functionality of pmSourceFitSetCheckLimits() because it
+    // requires that the static variable thisSet be allocated.
+    {
+        psMemId id = psMemGetId();
+        #define NUM_PARAMS 10
+        psVector *deriv = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psVector *param = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psVector *x = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psF32 tmpF = pmSourceFitSetFunction(deriv, param, x);
+        ok(isnan(tmpF), "pmSourceFitSetFunction() returned NULL with thisSet == FALSE");
+        psFree(deriv);
+        psFree(param);
+        psFree(x);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceFitSetJoin() tests
+    // Call pmSourceFitSetJoin() with NULL pmSourceFitSetData input parameter
+    {
+        psMemId id = psMemGetId();
+        #define NUM_PARAMS 10
+        psVector *deriv = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psVector *param = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        bool rc = pmSourceFitSetJoin(deriv, param, NULL);
+        ok(rc == false, "pmSourceFitSetJoin() returned FALSE with NULL pmSourceFitSetData input parameter");
+        psFree(deriv);
+        psFree(param);
+        psFree(modelSet); 
+        psFree(set);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFitSetJoin() with unequal size set->paramSet and set->derivSet input parameters
+    {
+        psMemId id = psMemGetId();
+        psVector *deriv = psVectorAlloc(1000, PS_TYPE_F32);
+        psVector *param = psVectorAlloc(1000, PS_TYPE_F32);
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        psFree(set->paramSet);
+        set->paramSet = psArrayAlloc(set->derivSet->n + 1);
+        bool rc = pmSourceFitSetJoin(deriv, param, set);
+        ok(rc == false, "pmSourceFitSetJoin() returned FALSE with unequal size set->paramSet and set->derivSet input parameters");
+        psFree(deriv);
+        psFree(param);
+        psFree(modelSet); 
+        psFree(set);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFitSetJoin() with deriv and param input psVector too small
+    // XXX: Must add a PS_ASSERT to the source code to detect this
+    if (0) {
+        psMemId id = psMemGetId();
+        psVector *small = psVectorAlloc(1, PS_TYPE_F32);
+        psVector *big = psVectorAlloc(1000, PS_TYPE_F32);
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        bool rc = pmSourceFitSetJoin(small, big, set);
+        ok(rc == false, "pmSourceFitSetJoin() returned FALSE with deriv input psVector too small");
+        rc = pmSourceFitSetJoin(big, small, set);
+        ok(rc == false, "pmSourceFitSetJoin() returned FALSE with param input psVector too small");
+        psFree(small);
+        psFree(big);
+        psFree(modelSet); 
+        psFree(set);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFitSetJoin() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        psVector *deriv = psVectorAlloc(1000, PS_TYPE_F32);
+        psVector *param = psVectorAlloc(1000, PS_TYPE_F32);
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+
+        psF32 cnt = 0.0;
+        for (int i = 0; i < set->paramSet->n; i++) {
+            psVector *paramOne = set->paramSet->data[i];
+            psVector *derivOne = set->derivSet->data[i];
+            for (int j = 0; j < paramOne->n; j++) {
+                paramOne->data.F32[j] = cnt;
+                derivOne->data.F32[j] = cnt;
+                cnt = cnt + 1.0;
+            }
+        }
+
+        bool rc = pmSourceFitSetJoin(deriv, param, set);
+        ok(rc == true, "pmSourceFitSetJoin() returned TRUE acceptable input parameters");
+
+        bool errorFlag = false;
+        for (int i = 0; i < (int) cnt ; i++) {
+            if (deriv->data.F32[i] != (float) i) {
+                diag("ERROR: deriv->data.F32[%d] is %.2ff, should be %.2f\n", i, deriv->data.F32[i], (float) i);
+                errorFlag = true;
+            }
+            if (param->data.F32[i] != (float) i) {
+                diag("ERROR: param->data.F32[%d] is %.2ff, should be %.2f\n", i, param->data.F32[i], (float) i);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "pmSourceFitSetJoin() set the deriv and param psVectors correctly");
+
+        psFree(deriv);
+        psFree(param);
+        psFree(modelSet); 
+        psFree(set);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceFitSetSplit() tests
+    // Call pmSourceFitSetSplit() with NULL pmSourceFitSetData input parameter
+    {
+        psMemId id = psMemGetId();
+        #define NUM_PARAMS 10
+        psVector *deriv = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psVector *param = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        bool rc = pmSourceFitSetSplit(NULL, deriv, param);
+        ok(rc == false, "pmSourceFitSetSplit() returned FALSE with NULL pmSourceFitSetData input parameter");
+        psFree(deriv);
+        psFree(param);
+        psFree(modelSet); 
+        psFree(set);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFitSetSplit() with NULL src->paramSet and src->derivSet input parameters and
+    // src->paramSet and src->derivSet of unequal size
+    {
+        psMemId id = psMemGetId();
+        #define NUM_PARAMS 10
+        psVector *deriv = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psVector *param = psVectorAlloc(NUM_PARAMS, PS_TYPE_F32);
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        psArray *tmpArray = set->paramSet;
+        set->paramSet = NULL;
+        bool rc = pmSourceFitSetSplit(set, deriv, param);
+        ok(rc == false, "pmSourceFitSetSplit() returned FALSE with NULL src->paramSet input parameter");
+        set->paramSet = tmpArray;
+
+        tmpArray = set->derivSet;
+        set->derivSet = NULL;
+        rc = pmSourceFitSetSplit(set, deriv, param);
+        ok(rc == false, "pmSourceFitSetSplit() returned FALSE with NULL src->derivSet input parameter");
+        set->derivSet = tmpArray;
+
+        psFree(set->paramSet);
+        set->paramSet = psArrayAlloc(set->derivSet->n + 1);
+        ok(rc == false, "pmSourceFitSetSplit() returned FALSE with src->paramSet and src->derivSet of unequal size");
+
+        psFree(deriv);
+        psFree(param);
+        psFree(modelSet); 
+        psFree(set);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFitSetSplit() with NULL param input parameter
+    {
+        psMemId id = psMemGetId();
+        psVector *deriv = psVectorAlloc(1000, PS_TYPE_F32);
+        psVector *param = psVectorAlloc(1000, PS_TYPE_F32);
+        for (int i = 0 ; i < 1000 ; i++) {
+            param->data.F32[i] = (float) i;
+            deriv->data.F32[i] = (float) i;
+        }
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        bool rc = pmSourceFitSetSplit(set, deriv, NULL);
+        ok(rc == false, "pmSourceFitSetSplit() returned FALSE with NULL param input parameter");
+        psFree(deriv);
+        psFree(param);
+        psFree(modelSet); 
+        psFree(set);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFitSetSplit() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        psVector *deriv = psVectorAlloc(1000, PS_TYPE_F32);
+        psVector *param = psVectorAlloc(1000, PS_TYPE_F32);
+        for (int i = 0 ; i < 1000 ; i++) {
+            deriv->data.F32[i] = (float) i;
+            param->data.F32[i] = (float) i;
+        }
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        bool rc = pmSourceFitSetSplit(set, deriv, param);
+        ok(rc == true, "pmSourceFitSetSplit() returned FALSE with acceptable input parameters");
+
+        bool errorFlag = false;
+        psF32 cnt = 0.0;
+        for (int i = 0; i < set->paramSet->n; i++) {
+            psVector *paramOne = set->paramSet->data[i];
+            psVector *derivOne = set->derivSet->data[i];
+            for (int j = 0; j < paramOne->n; j++) {
+                if (paramOne->data.F32[j] != cnt) {
+                    diag("ERROR: paramOne->data.F32[%d] is %.2ff, should be %.2f\n", i, paramOne->data.F32[i], (float) i);
+                    errorFlag = true;
+                }
+                if (derivOne->data.F32[j] != cnt) {
+                    diag("ERROR: derivOne->data.F32[%d] is %.2ff, should be %.2f\n", i, derivOne->data.F32[i], (float) i);
+                    errorFlag = true;
+                }
+                cnt = cnt + 1.0;
+            }
+        }
+        ok(!errorFlag, "pmSourceFitSetSplit() set the deriv and param psVectors correctly");
+
+        psFree(deriv);
+        psFree(param);
+        psFree(modelSet); 
+        psFree(set);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceFitSetValues() tests
+    // Call pmSourceFitSetValues() with bad input parameters
+    {
+        psMemId id = psMemGetId();
+        #define VEC_SIZE 1000
+        #define NUM_ITER 32
+        #define TOL 0.1
+        psVector *param = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        psVector *dparam = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        pmSource *src = create_pmSource();
+        psMinimization *myMin = psMinimizationAlloc(NUM_ITER, TOL);
+        int nPix = 10;
+        bool fitStatus = true;
+        // NULL set input parameter
+        bool rc = pmSourceFitSetValues(NULL, dparam, param, src, myMin, nPix, fitStatus);
+        ok(rc == false, "pmSourceFitSetValues() returned FALSE with NULL set input parameter");
+
+        // NULL set->paramSet
+        psArray *tmpArray = set->paramSet;
+        set->paramSet = NULL;
+        rc = pmSourceFitSetValues(set, dparam, param, src, myMin, nPix, fitStatus);
+        ok(rc == false, "pmSourceFitSetValues() returned FALSE with NULL set->paramSet");
+        set->paramSet = tmpArray;
+
+        // NULL dparam input parameter
+        rc = pmSourceFitSetValues(set, NULL, param, src, myMin, nPix, fitStatus);
+        ok(rc == false, "pmSourceFitSetValues() returned FALSE with NULL dparam input parameter");
+
+        // NULL param input parameter
+        rc = pmSourceFitSetValues(set, dparam, NULL, src, myMin, nPix, fitStatus);
+        ok(rc == false, "pmSourceFitSetValues() returned FALSE with NULL param input parameter");
+
+        // NULL pmSource input parameter
+        rc = pmSourceFitSetValues(set, dparam, param, NULL, myMin, nPix, fitStatus);
+        ok(rc == false, "pmSourceFitSetValues() returned FALSE with NULL pmSource input parameter");
+
+        // NULL pmSource->pixels input parameter
+        psImage *tmpImg = src->pixels;
+        src->pixels = NULL;
+        rc = pmSourceFitSetValues(set, dparam, param, src, myMin, nPix, fitStatus);
+        ok(rc == false, "pmSourceFitSetValues() returned FALSE with NULL pmSource->pixels input parameter");
+        src->pixels = tmpImg;
+
+        // NULL psMinimization input parameter
+        rc = pmSourceFitSetValues(set, dparam, param, src, NULL, nPix, fitStatus);
+        ok(rc == false, "pmSourceFitSetValues() returned FALSE with NULL psMinimization input parameter");
+
+        psFree(param);
+        psFree(dparam);
+        psFree(modelSet); 
+        psFree(set);
+        psFree(src);
+        psFree(myMin);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmSourceFitSetValues() tests
+    // Call pmSourceFitSetValues() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        #define VEC_SIZE 1000
+        #define NUM_ITER 32
+        #define TOL 0.1
+        #define MIN_VALUE	22.0
+        #define NUM_PIX 100
+        psVector *param = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        psVector *dparam = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        for (int i = 0 ; i < VEC_SIZE ; i++) {
+            param->data.F32[i] = (float) i;
+            dparam->data.F32[i] = (float) i;
+        }
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        pmSource *src = create_pmSource();
+        psMinimization *myMin = psMinimizationAlloc(NUM_ITER, TOL);
+        int nPix = NUM_PIX;
+        bool fitStatus = true;
+
+        bool rc = pmSourceFitSetValues(set, dparam, param, src, myMin, nPix, fitStatus);
+        ok(rc == true, "pmSourceFitSetValues() returned TRUE with acceptable input paramaters");
+
+        bool errorFlag = false;
+        psF32 cnt = 0.0;
+        for (int i = 0; i < set->paramSet->n; i++) {
+            pmModel *model = set->modelSet->data[i];
+            for (int j = 0; j < model->params->n; j++) {
+                if (model->params->data.F32[j] != cnt) {
+                    diag("ERROR: model->params->data.F32[%d] is %.2ff, should be %.2f\n", i, model->params->data.F32[i], (float) i);
+                    errorFlag = true;
+                }
+                if (model->dparams->data.F32[j] != cnt) {
+                    diag("ERROR: model->dparams->data.F32[%d] is %.2ff, should be %.2f\n", i, model->dparams->data.F32[i], (float) i);
+                    errorFlag = true;
+                }
+                if (model->chisq != myMin->value) {
+                    diag("ERROR: model->chisq is %.2f, should be %.2f", model->chisq, MIN_VALUE);
+                    errorFlag = true;
+                }
+                if (model->nIter != myMin->iter) {
+                    diag("ERROR: model->nIter is %.2f, should be %.2f", model->nIter, NUM_ITER);
+                    errorFlag = true;
+                }
+                if (model->nDOF != NUM_PIX - model->params->n) {
+                    diag("ERROR: model->nDOF is %d, should be %d", model->nDOF, NUM_PIX-model->params->n);
+                    errorFlag = true;
+                }
+
+                cnt = cnt + 1.0;
+            }
+        }
+        ok(!errorFlag, "pmSourceFitSetValues() set the deriv and param psVectors correctly");
+
+        psFree(param);
+        psFree(dparam);
+        psFree(modelSet); 
+        psFree(set);
+        psFree(src);
+        psFree(myMin);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceFitSetMasks() tests
+    // Call pmSourceFitSetMasks() with bad input parameters
+    {
+        psMemId id = psMemGetId();
+        #define VEC_SIZE 1000
+        #define NUM_ITER 32
+        #define TOL 0.1
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        psMinConstraint *constraint = psMinConstraintAlloc();
+
+        // NULL psMinConstraint input parameter
+        bool rc = pmSourceFitSetMasks(NULL, set, PM_SOURCE_FIT_NORM);
+        ok(rc == false, "pmSourceFitSetMasks() returned TRUE with NULL psMinConstraint input parameter");
+
+        // NULL pmSourceFitSetData input parameter
+        rc = pmSourceFitSetMasks(constraint, NULL, PM_SOURCE_FIT_NORM);
+        ok(rc == false, "pmSourceFitSetMasks() returned TRUE with NULL pmSourceFitSetData input parameter");
+
+        psFree(modelSet); 
+        psFree(set);
+        psFree(constraint);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFitSetMasks() with acceptable input parameters
+    // For thoroughness, we should test the PM_SOURCE_FIT_PSF and PM_SOURCE_FIT_EXT mode
+    {
+        psMemId id = psMemGetId();
+        #define VEC_SIZE 1000
+        #define NUM_ITER 32
+        #define TOL 0.1
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < modelSet->n ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSourceFitSetData *set = pmSourceFitSetDataAlloc(modelSet);
+        psMinConstraint *constraint = psMinConstraintAlloc();
+        constraint->paramMask = psVectorAlloc(1000, PS_TYPE_F32);
+
+        // Acceptable input parameters
+        bool rc = pmSourceFitSetMasks(constraint, set, PM_SOURCE_FIT_NORM);
+        ok(rc == true, "pmSourceFitSetMasks() returned TRUE with acceptable input parameters");
+
+        bool errorFlag = false;
+        int n = 0;
+        for (int i = 0; i < set->paramSet->n; i++) {
+            psVector *paramOne = set->paramSet->data[i];
+            for (int j = 0; j < paramOne->n; j++) {
+                if (j == PM_PAR_I0) continue;
+                if (constraint->paramMask->data.U8[n + j] != 1) {
+                    diag("ERROR: constraint->paramMask->data.U8[%d] is %d, should be a",
+                          n + j, constraint->paramMask->data.U8[n + j]);
+                    errorFlag = true;
+                }
+            }
+            n += paramOne->n;
+        }
+        ok(!errorFlag, "pmSourceFitSetMasks() constraint->paramMask psVector correctly");
+
+        psFree(modelSet); 
+        psFree(set);
+        psFree(constraint);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceFitSet() tests
+    // Call pmSourceFitSet() with NULL psSource input parameter
+    {
+        psMemId id = psMemGetId();
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < NUM_MODELS ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSource *src = create_pmSource();
+        bool rc = pmSourceFitSet(NULL, modelSet, PM_SOURCE_FIT_PSF, 1);
+        ok(rc == false, "pmSourceFitSet() returned FALSE with NULL psSource input parameter");
+        psFree(src);
+        for (int i = 0 ; i < NUM_MODELS ; i++) {
+            psFree(modelSet->data[i]);
+            modelSet->data[i] = NULL;
+        }
+        psFree(modelSet);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFitSet() with NULL psSource pixels, weight, maskObj input parameters
+    {
+        psMemId id = psMemGetId();
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < NUM_MODELS ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSource *src = create_pmSource();
+        psImage *tmpImg = src->pixels;
+        src->pixels = NULL;
+        bool rc = pmSourceFitSet(src, modelSet, PM_SOURCE_FIT_PSF, 1);
+        ok(rc == false, "pmSourceFitSet() returned FALSE with NULL src->pixels input parameter");
+        src->pixels = tmpImg;
+
+        tmpImg = src->weight;
+        src->weight = NULL;
+        rc = pmSourceFitSet(src, modelSet, PM_SOURCE_FIT_PSF, 1);
+        ok(rc == false, "pmSourceFitSet() returned FALSE with NULL src->weight input parameter");
+        src->weight = tmpImg;
+
+        tmpImg = src->maskObj;
+        src->maskObj = NULL;
+        rc = pmSourceFitSet(src, modelSet, PM_SOURCE_FIT_PSF, 1);
+        ok(rc == false, "pmSourceFitSet() returned FALSE with NULL src->maskObj input parameter");
+        src->maskObj = tmpImg;
+        psFree(src);
+        for (int i = 0 ; i < NUM_MODELS ; i++) {
+            psFree(modelSet->data[i]);
+            modelSet->data[i] = NULL;
+        }
+        psFree(modelSet);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFitSet() with acceptable input parameters
+    // This is a verly limited test.  It only uses a simple object in the pmSource
+    // parameter and simply tests that pmSourceFitSet() returns teh correct type and mode.
+    // Must add more extensive input types.
+    if (1) {
+        psMemId id = psMemGetId();
+        psArray *modelSet = psArrayAlloc(NUM_MODELS);
+        for (int i = 0 ; i < NUM_MODELS ; i++) {
+            modelSet->data[i] = (psPtr *) pmModelAlloc(i);
+	}
+        pmSource *src = create_pmSource();
+        bool rc = pmSourceFitSet(src, modelSet, PM_SOURCE_FIT_PSF, 1);
+        ok(rc == true, "pmSourceFitSet() returned TRUE with acceptable parameters");
+        ok(src->mode & PM_SOURCE_MODE_FITTED, "pmSourceFitSet() set source->mode |= PM_SOURCE_MODE_FITTED (%d)", src->mode);
+        ok(src->type == PM_SOURCE_TYPE_UNKNOWN, "pmSourceFitSet() set source->type correctly (%d)", src->type);
+
+        psFree(src);
+        for (int i = 0 ; i < NUM_MODELS ; i++) {
+            psFree(modelSet->data[i]);
+            modelSet->data[i] = NULL;
+        }
+        psFree(modelSet);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceIO_PS1_DEV_0.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceIO_PS1_DEV_0.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceIO_PS1_DEV_0.c	(revision 22322)
@@ -0,0 +1,243 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+    XX: These tests read/write a file.  Must choose a more unique name.
+*/
+
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.0001)
+#define NUM_SOURCES		5
+#define FITS_FILENAME  ".tmp00"
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(79);
+
+
+    // ----------------------------------------------------------------------
+    // pmSourcesWrite_PS1_DEV_0() tests
+    // Call pmSourcesWrite_PS1_DEV_0() with NULL psFits input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(FITS_FILENAME, "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            src->type = PM_SOURCE_TYPE_STAR;
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_PS1_DEV_0(NULL, sources, imageHeader, tableHeader, extname);
+        ok(rc == false, "pmSourcesWrite_PS1_DEV_0() returned FALSE with NULL psFits input parameter");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesWrite_PS1_DEV_0() with NULL pmSource input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(FITS_FILENAME, "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_PS1_DEV_0(fitsFile, NULL, imageHeader, tableHeader, extname);
+        ok(rc == false, "pmSourcesWrite_PS1_DEV_0() returned FALSE with NULL pmSource input parameter");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesWrite_PS1_DEV_0() with NULL extname input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(FITS_FILENAME, "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_PS1_DEV_0(fitsFile, sources, imageHeader, tableHeader, NULL);
+        ok(rc == false, "pmSourcesWrite_PS1_DEV_0() returned FALSE with NULL extname input parameter");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourcesRead_PS1_DEV_0() tests
+    // Call pmSourcesRead_PS1_DEV_0() with NULL psFits input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(FITS_FILENAME, "r");
+        psMetadata *header = psMetadataAlloc();
+        psArray *array = pmSourcesRead_PS1_DEV_0(NULL, header);
+        ok(array == NULL, "pmSourcesRead_PS1_DEV_0() returned NULL with NULL psFits input parameter");
+        psFree(fitsFile);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesRead_PS1_DEV_0() with NULL header input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(FITS_FILENAME, "r");
+        psMetadata *header = psMetadataAlloc();
+        psArray *array = pmSourcesRead_PS1_DEV_0(fitsFile, NULL);
+        ok(array == NULL, "pmSourcesRead_PS1_DEV_0() returned NULL with NULL header input parameter");
+        psFree(fitsFile);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Call pmSourcesWrite_PS1_DEV_0() with acceptable input parameters
+    #define TEST_BASE_X_POS		10.0
+    #define TEST_BASE_Y_POS		20.0
+    #define TEST_BASE_X_ERR		30.0
+    #define TEST_BASE_Y_ERR		40.0
+    #define TEST_BASE_PSF_MAG	50.0
+    #define TEST_BASE_ERR_MAG	60.0
+    #define TEST_BASE_SKY		70.0
+    #define TEST_BASE_SKY_ERR	80.0
+    #define TEST_BASE_PIX_WEIGHT	90.0
+    #define TEST_BASE_PEAK_FLUX	120.0
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(FITS_FILENAME, "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            // Set even numbered sources to PM_SOURCE_TYPE_STAR
+            pmModel *model = NULL;
+            if (i%2) {
+                src->type = PM_SOURCE_TYPE_STAR;
+                src->modelPSF = pmModelAlloc(1);
+                model = src->modelPSF;
+	    } else {
+                src->type = PM_SOURCE_TYPE_EXTENDED;
+                src->modelConv = pmModelAlloc(1);
+                model = src->modelConv;
+ 	    }
+            for (int p = 0 ; p < model->params->n ; p++) {
+                model->params->data.F32[p] = (float) (i + p);
+                model->dparams->data.F32[p] = (float) (i + p);
+	    }
+            model->params->data.F32[PM_PAR_XPOS] = TEST_BASE_X_POS + (float) i;
+            model->params->data.F32[PM_PAR_YPOS] = TEST_BASE_Y_POS + (float) i;
+            model->dparams->data.F32[PM_PAR_XPOS] = TEST_BASE_X_ERR + (float) i;
+            model->dparams->data.F32[PM_PAR_YPOS] = TEST_BASE_Y_ERR + (float) i;
+            src->psfMag = TEST_BASE_PSF_MAG + (float) i;
+            src->errMag = TEST_BASE_ERR_MAG + (float) i;
+            src->peak->flux = TEST_BASE_PEAK_FLUX + (float) i;
+            src->sky = TEST_BASE_SKY + (float) i;
+            src->skyErr = TEST_BASE_SKY_ERR + (float) i;
+            src->pixWeight = TEST_BASE_PIX_WEIGHT + (float) i;
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_PS1_DEV_0(fitsFile, sources, imageHeader, tableHeader, extname);
+        ok(rc == true, "pmSourcesWrite_PS1_DEV_0() returned TRUE with acceptable input parameters");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesRead_PS1_DEV_0() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(FITS_FILENAME, "r");
+        psMetadata *header = psMetadataAlloc();
+        psArray *array = pmSourcesRead_PS1_DEV_0(fitsFile, header);
+        ok(array != NULL, "pmSourcesRead_PS1_DEV_0() returned non-NULL with acceptable input parameters");
+        skip_start(array == NULL, 1, "Skipping tests because pmSourcesRead_PS1_DEV_0() returned NULL");
+        for (int i = 0 ; i < array->n ; i++) {
+             pmSource *src = (pmSource *) array->data[i];
+             ok(src != NULL && psMemCheckSource(src), "pmSourcesRead_PS1_DEV_0() read source %d correctly", i);
+
+             // XXX: Source code always sets the type to PM_SOURCE_TYPE_STAR.  Is that right?
+             ok(src->type == PM_SOURCE_TYPE_STAR, "pmSourcesRead_PS1_DEV_0() set the source type correctly (is %d, should be %d)",
+                src->type, PM_SOURCE_TYPE_STAR);
+
+             ok(src->sky == (TEST_BASE_SKY + (float) i), "pmSourcesRead_PS1_DEV_0() set src->sky correctly (is %.2f, should be %.2f)",
+                src->sky, (TEST_BASE_SKY + (float) i));
+             ok(src->skyErr == (TEST_BASE_SKY_ERR + (float) i), "pmSourcesRead_PS1_DEV_0() set src->skyErr correctly (is %.2f, should be %.2f)",
+                src->skyErr, (TEST_BASE_SKY_ERR + (float) i));
+             ok(src->pixWeight == (TEST_BASE_PIX_WEIGHT + (float) i), "pmSourcesRead_PS1_DEV_0() set src->pixWeight correctly (is %.2f, should be %.2f)",
+                src->pixWeight, (TEST_BASE_PIX_WEIGHT + (float) i));
+             ok(TEST_FLOATS_EQUAL(src->peak->flux, (TEST_BASE_PEAK_FLUX + (float) i)), "pmSourcesRead_PS1_DEV_0() set src->peak->flux correctly (is %.2f, should be %.2f)",
+                src->peak->flux, (TEST_BASE_PEAK_FLUX + (float) i));
+             ok(src->psfMag == (TEST_BASE_PSF_MAG + (float) i), "pmSourcesRead_PS1_DEV_0() set src->psfMag correctly (is %.2f, should be %.2f)",
+                src->psfMag, (TEST_BASE_PSF_MAG + (float) i));
+             ok(src->errMag == (TEST_BASE_ERR_MAG + (float) i), "pmSourcesRead_PS1_DEV_0() set src->errMag correctly (is %.2f, should be %.2f)",
+                src->errMag, (TEST_BASE_ERR_MAG + (float) i));
+
+             // XXX: Source code always sets src->modelPSF.  Is that right?
+             pmModel *model = src->modelPSF;
+             ok(model != NULL  && psMemCheckModel(model), "pmSourcesRead_PS1_DEV_0() set src->modelPSF correctly");
+             skip_start(model == NULL, 2, "Skipping tests because pmSourcesRead_PS1_DEV_0() did not set src->modelPSF");
+             ok(model->params->data.F32[PM_PAR_XPOS] == (TEST_BASE_X_POS + (float) i),
+               "pmSourcesRead_PS1_DEV_0() set src->model->params->data.F32[PM_PAR_XPOS] correctly (is %.2f, should be %.2f)",
+                model->params->data.F32[PM_PAR_XPOS], (TEST_BASE_X_POS + (float) i));
+             ok(model->params->data.F32[PM_PAR_YPOS] == (TEST_BASE_Y_POS + (float) i),
+               "pmSourcesRead_PS1_DEV_0() set src->model->params->data.F32[PM_PAR_YPOS] correctly (is %.2f, should be %.2f)",
+                model->params->data.F32[PM_PAR_YPOS], (TEST_BASE_Y_POS + (float) i));
+             ok(model->dparams->data.F32[PM_PAR_XPOS] == (TEST_BASE_X_ERR + (float) i),
+               "pmSourcesRead_PS1_DEV_0() set src->model->dparams->data.F32[PM_PAR_XPOS] correctly (is %.2f, should be %.2f)",
+                model->dparams->data.F32[PM_PAR_XPOS], (TEST_BASE_X_ERR + (float) i));
+             ok(model->dparams->data.F32[PM_PAR_YPOS] == (TEST_BASE_Y_ERR + (float) i),
+               "pmSourcesRead_PS1_DEV_0() set src->model->dparams->data.F32[PM_PAR_YPOS] correctly (is %.2f, should be %.2f)",
+                model->dparams->data.F32[PM_PAR_YPOS], (TEST_BASE_Y_ERR + (float) i));
+             skip_end();
+	}
+        skip_end();
+        psFree(fitsFile);
+        psFree(header);
+        psFree(array);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceIO_PS1_DEV_1.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceIO_PS1_DEV_1.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceIO_PS1_DEV_1.c	(revision 22322)
@@ -0,0 +1,309 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    No test for pmSourcesWrite_PS1_DEV_1_XSRC() since there is no associated
+        read function.
+    All other functions are tested.
+    XX: These tests read/write a file.  Must choose a more unique name.
+*/
+
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           (8)
+#define TEST_NUM_COLS           (16)
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.0001)
+#define NUM_SOURCES		5
+#define TABLE_FILENAME	"table.fits"
+const char* tableFilename = "table.fits";
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(79);
+
+
+    // ----------------------------------------------------------------------
+    // pmSourcesWrite_PS1_DEV_1() tests
+    // Call pmSourcesWrite_PS1_DEV_1() with NULL psFits input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(TABLE_FILENAME, "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            src->type = PM_SOURCE_TYPE_STAR;
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_PS1_DEV_1(NULL, sources, imageHeader, tableHeader, extname, NULL);
+        ok(rc == false, "pmSourcesWrite_PS1_DEV_1() returned FALSE with NULL psFits input parameter");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesWrite_PS1_DEV_1() with NULL pmSource input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(TABLE_FILENAME, "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_PS1_DEV_1(fitsFile, NULL, imageHeader, tableHeader, extname, NULL);
+        ok(rc == false, "pmSourcesWrite_PS1_DEV_1() returned FALSE with NULL pmSource input parameter");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesWrite_PS1_DEV_1() with NULL extname input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(TABLE_FILENAME, "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_PS1_DEV_1(fitsFile, sources, imageHeader, tableHeader, NULL, NULL);
+        ok(rc == false, "pmSourcesWrite_PS1_DEV_1() returned FALSE with NULL extname input parameter");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourcesRead_PS1_DEV_1() tests
+    // Call pmSourcesRead_PS1_DEV_1() with NULL psFits input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(TABLE_FILENAME, "r");
+        psMetadata *header = psMetadataAlloc();
+        psArray *array = pmSourcesRead_PS1_DEV_1(NULL, header);
+        ok(array == NULL, "pmSourcesRead_PS1_DEV_1() returned NULL with NULL psFits input parameter");
+        psFree(fitsFile);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesRead_PS1_DEV_1() with NULL header input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(TABLE_FILENAME, "r");
+        psMetadata *header = psMetadataAlloc();
+        psArray *array = pmSourcesRead_PS1_DEV_1(fitsFile, NULL);
+        ok(array == NULL, "pmSourcesRead_PS1_DEV_1() returned NULL with NULL header input parameter");
+        psFree(fitsFile);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Call pmSourcesWrite_PS1_DEV_1() with acceptable input parameters
+    #define TEST_BASE_X_POS             10.0
+    #define TEST_BASE_Y_POS             20.0
+    #define TEST_BASE_X_ERR             30.0
+    #define TEST_BASE_Y_ERR             40.0
+    #define TEST_BASE_PSF_MAG		50.0
+    #define TEST_BASE_ERR_MAG		60.0
+    #define TEST_BASE_SKY               500.0
+    #define TEST_BASE_SKY_ERR		80.0
+    #define TEST_BASE_PIX_WEIGHT        90.0
+    #define TEST_BASE_PEAK_FLUX		120.0
+    #define TEST_BASE_PSF_PROB		150.0
+    #define TEST_BASE_CR_N_SIGMA	160.0
+    #define TEST_BASE_EXT_N_SIGMA       170.0   
+    #define TEST_BASE_MODE		1
+
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(TABLE_FILENAME, "w");
+        if (fitsFile == NULL) {
+            diag("ERROR: Could not create 'table' FITS file");
+            return false;
+        }
+
+        if (1) {
+            // make the PHU an image (per FITS standard, it must be)
+            psImage* image = psImageAlloc(16, 16, PS_TYPE_F32);
+            if (!psFitsWriteImage(fitsFile, NULL, image, 1, NULL)) {
+                diag("ERROR: Could not write PHU image");
+                return false;
+            }
+            psFree(image);
+        }
+
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            // Set even numbered sources to PM_SOURCE_TYPE_STAR
+            pmModel *model = NULL;
+            if (i%2) {
+                src->type = PM_SOURCE_TYPE_STAR;
+                src->modelPSF = pmModelAlloc(1);
+                model = src->modelPSF;
+	    } else {
+                src->type = PM_SOURCE_TYPE_EXTENDED;
+                src->modelConv = pmModelAlloc(1);
+                model = src->modelConv;
+	    }
+            for (int p = 0 ; p < model->params->n ; p++) {
+                model->params->data.F32[p] = (float) (i + p);
+                model->dparams->data.F32[p] = (float) (i + p);
+	    }
+
+            model->params->data.F32[PM_PAR_XPOS] = TEST_BASE_X_POS + (float) i;
+            model->params->data.F32[PM_PAR_YPOS] = TEST_BASE_Y_POS + (float) i;
+            model->dparams->data.F32[PM_PAR_XPOS] = TEST_BASE_X_ERR + (float) i;
+            model->dparams->data.F32[PM_PAR_YPOS] = TEST_BASE_Y_ERR + (float) i;
+            src->psfMag = TEST_BASE_PSF_MAG + (float) i;
+            src->errMag = TEST_BASE_ERR_MAG + (float) i;
+            src->peak->flux = TEST_BASE_PEAK_FLUX + (float) i;
+            src->sky = TEST_BASE_SKY + (float) i;
+            src->skyErr = TEST_BASE_SKY_ERR + (float) i;
+            src->pixWeight = TEST_BASE_PIX_WEIGHT + (float) i;
+            sources->data[i] = (psPtr *) src;
+            src->psfMag = TEST_BASE_PSF_MAG + (float) i;
+            src->errMag = TEST_BASE_ERR_MAG + (float) i;
+            src->sky = TEST_BASE_SKY + (float) i;
+            src->skyErr = TEST_BASE_SKY_ERR + (float) i;
+//            src->psfProb = TEST_BASE_PSF_PROB + (float) i;
+            src->crNsigma = TEST_BASE_CR_N_SIGMA + (float) i;
+            src->extNsigma = TEST_BASE_EXT_N_SIGMA + (float) i;
+            src->mode = TEST_BASE_MODE + i;
+            src->peak->SN = (float) (10 - i);
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_PS1_DEV_1(fitsFile, sources, imageHeader, tableHeader, extname, NULL);
+        ok(rc == true, "pmSourcesWrite_PS1_DEV_1() returned TRUE with acceptable input parameters");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesRead_PS1_DEV_1() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(tableFilename, "rw");
+        if (fitsFile == NULL) {
+            diag("ERROR: Could not create 'table' FITS file");
+            return false;
+        }
+
+        // XXX: I'm not exactly sure why, but without this, the psFitsReadTableSize() call
+        // in pmSourcesRead_PS1_DEV_1() fails.  However, Robert also did this in psFits.
+        if (1) {
+            psFitsMoveExtNum(fitsFile, 1, false);
+	}
+
+        // XX: Debugging purposes only.  Trying to duplicate the call to
+        // psFitsTableRead() from DEV_0
+        if (0) {
+            psArray *table = psFitsReadTable(fitsFile);
+            if (table == NULL) {
+                printf("ERROR: table is NULL\n");
+                exit(0);
+	    }
+            for (int i = 0; i < table->n; i++) {
+                psMetadata *row = table->data[i];
+                float sky = psMetadataLookupF32(NULL, row, "SKY");
+                printf("For row %d, the psFitsReadTable() produces a sky of %.2f\n", i, sky);
+	    }
+	}
+
+        psMetadata *header = psMetadataAlloc();
+        psArray *array = pmSourcesRead_PS1_DEV_1(fitsFile, header);
+        ok(array != NULL, "pmSourcesRead_PS1_DEV_1() returned non-NULL with acceptable input parameters");
+        skip_start(array == NULL, 1, "Skipping tests because pmSourcesRead_PS1_DEV_1() returned NULL");
+        for (int i = 0 ; i < array->n ; i++) {
+             pmSource *src = (pmSource *) array->data[i];
+             ok(src != NULL && psMemCheckSource(src), "pmSourcesRead_PS1_DEV_1() read source %d correctly", i);
+
+             // XXX: Source code always sets the type to PM_SOURCE_TYPE_STAR.  Is that right?
+             ok(src->type == PM_SOURCE_TYPE_STAR, "pmSourcesRead_PS1_DEV_1() set the source type correctly (is %d, should be %d)",
+                src->type, PM_SOURCE_TYPE_STAR);
+
+             ok(src->sky == (TEST_BASE_SKY + (float) i), "pmSourcesRead_PS1_DEV_1() set src->sky correctly (is %.2f, should be %.2f)",
+                src->sky, (TEST_BASE_SKY + (float) i));
+             ok(src->skyErr == (TEST_BASE_SKY_ERR + (float) i), "pmSourcesRead_PS1_DEV_1() set src->skyErr correctly (is %.2f, should be %.2f)",
+                src->skyErr, (TEST_BASE_SKY_ERR + (float) i));
+             ok(src->pixWeight == (TEST_BASE_PIX_WEIGHT + (float) i), "pmSourcesRead_PS1_DEV_1() set src->pixWeight correctly (is %.2f, should be %.2f)",
+                src->pixWeight, (TEST_BASE_PIX_WEIGHT + (float) i));
+             ok(TEST_FLOATS_EQUAL(src->peak->flux, (TEST_BASE_PEAK_FLUX + (float) i)), "pmSourcesRead_PS1_DEV_1() set src->peak->flux correctly (is %.2f, should be %.2f)",
+                src->peak->flux, (TEST_BASE_PEAK_FLUX + (float) i));
+             ok(src->psfMag == (TEST_BASE_PSF_MAG + (float) i), "pmSourcesRead_PS1_DEV_1() set src->psfMag correctly (is %.2f, should be %.2f)",
+                src->psfMag, (TEST_BASE_PSF_MAG + (float) i));
+             ok(src->errMag == (TEST_BASE_ERR_MAG + (float) i), "pmSourcesRead_PS1_DEV_1() set src->errMag correctly (is %.2f, should be %.2f)",
+                src->errMag, (TEST_BASE_ERR_MAG + (float) i));
+
+             // XXX: Source code always sets src->modelPSF.  Is that right?
+             pmModel *model = src->modelPSF;
+             ok(model != NULL  && psMemCheckModel(model), "pmSourcesRead_PS1_DEV_1() set src->modelPSF correctly");
+             skip_start(model == NULL, 2, "Skipping tests because pmSourcesRead_PS1_DEV_1() did not set src->modelPSF");
+             ok(model->params->data.F32[PM_PAR_XPOS] == (TEST_BASE_X_POS + (float) i),
+               "pmSourcesRead_PS1_DEV_1() set src->model->params->data.F32[PM_PAR_XPOS] correctly (is %.2f, should be %.2f)",
+                model->params->data.F32[PM_PAR_XPOS], (TEST_BASE_X_POS + (float) i));
+             ok(model->params->data.F32[PM_PAR_YPOS] == (TEST_BASE_Y_POS + (float) i),
+               "pmSourcesRead_PS1_DEV_1() set src->model->params->data.F32[PM_PAR_YPOS] correctly (is %.2f, should be %.2f)",
+                model->params->data.F32[PM_PAR_YPOS], (TEST_BASE_Y_POS + (float) i));
+             ok(model->dparams->data.F32[PM_PAR_XPOS] == (TEST_BASE_X_ERR + (float) i),
+               "pmSourcesRead_PS1_DEV_1() set src->model->dparams->data.F32[PM_PAR_XPOS] correctly (is %.2f, should be %.2f)",
+                model->dparams->data.F32[PM_PAR_XPOS], (TEST_BASE_X_ERR + (float) i));
+             ok(model->dparams->data.F32[PM_PAR_YPOS] == (TEST_BASE_Y_ERR + (float) i),
+               "pmSourcesRead_PS1_DEV_1() set src->model->dparams->data.F32[PM_PAR_YPOS] correctly (is %.2f, should be %.2f)",
+                model->dparams->data.F32[PM_PAR_YPOS], (TEST_BASE_Y_ERR + (float) i));
+             skip_end();
+	}
+        skip_end();
+        psFree(fitsFile);
+        psFree(header);
+        psFree(array);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceIO_SMPDATA.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceIO_SMPDATA.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceIO_SMPDATA.c	(revision 22322)
@@ -0,0 +1,276 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+*/
+
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.0001)
+#define NUM_SOURCES		5
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(69);
+
+
+    // ----------------------------------------------------------------------
+    // pmSourcesWrite_SMPDATA() tests
+    // Call pmSourcesWrite_SMPDATA() with NULL psFits input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(".tmp00", "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            src->type = PM_SOURCE_TYPE_STAR;
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_SMPDATA(NULL, sources, imageHeader, tableHeader, extname);
+        ok(rc == false, "pmSourcesWrite_SMPDATA() returned FALSE with NULL psFits input parameter");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesWrite_SMPDATA() with NULL pmSource input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(".tmp00", "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_SMPDATA(fitsFile, NULL, imageHeader, tableHeader, extname);
+        ok(rc == false, "pmSourcesWrite_SMPDATA() returned FALSE with NULL pmSource input parameter");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesWrite_SMPDATA() with NULL extname input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(".tmp00", "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_SMPDATA(fitsFile, sources, imageHeader, tableHeader, NULL);
+        ok(rc == false, "pmSourcesWrite_SMPDATA() returned FALSE with NULL extname input parameter");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourcesRead_SMPDATA() tests
+    // Call pmSourcesRead_SMPDATA() with NULL psFits input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(".tmp00", "r");
+        psMetadata *header = psMetadataAlloc();
+        psArray *array = pmSourcesRead_SMPDATA(NULL, header);
+        ok(array == NULL, "pmSourcesRead_SMPDATA() returned NULL with NULL psFits input parameter");
+        psFree(fitsFile);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourcesRead_SMPDATA() with NULL header input parameter
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(".tmp00", "r");
+        psMetadata *header = psMetadataAlloc();
+        psArray *array = pmSourcesRead_SMPDATA(fitsFile, NULL);
+        ok(array == NULL, "pmSourcesRead_SMPDATA() returned NULL with NULL header input parameter");
+        psFree(fitsFile);
+        psFree(header);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Call pmSourcesWrite_SMPDATA() with acceptable input parameters
+    #define TEST_BASE_X_POS		10.0
+    #define TEST_BASE_Y_POS		20.0
+    #define TEST_BASE_X_ERR		30.0
+    #define TEST_BASE_Y_ERR		40.0
+    #define TEST_BASE_PSF_MAG		50.0
+    #define TEST_BASE_ERR_MAG		60.0
+    #define TEST_BASE_SKY		70.0
+    #define TEST_BASE_SKY_ERR		80.0
+    #define TEST_BASE_PIX_WEIGHT	90.0
+    #define TEST_BASE_PEAK_FLUX		120.0
+    #define TEST_BASE_EXT_MAG		150.0
+    #define TEST_BASE_AP_MAG		160.0
+    // XXX: The following metadata items are not tested: FWHM_X, FWHM_Y, THETA
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(".tmp00", "w");
+        psArray *sources = psArrayAlloc(NUM_SOURCES);
+        for (int i = 0 ; i < sources->n ; i++) {
+            pmSource *src = pmSourceAlloc();
+            src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+            // Set even numbered sources to PM_SOURCE_TYPE_STAR
+            pmModel *model = NULL;
+            if (i%2) {
+                src->type = PM_SOURCE_TYPE_STAR;
+                src->modelPSF = pmModelAlloc(1);
+                model = src->modelPSF;
+	    } else {
+                src->type = PM_SOURCE_TYPE_EXTENDED;
+                src->modelConv = pmModelAlloc(1);
+                model = src->modelConv;
+ 	    }
+            for (int p = 0 ; p < model->params->n ; p++) {
+                model->params->data.F32[p] = (float) (i + p);
+                model->dparams->data.F32[p] = (float) (i + p);
+	    }
+            model->params->data.F32[PM_PAR_XPOS] = TEST_BASE_X_POS + (float) i;
+            model->params->data.F32[PM_PAR_YPOS] = TEST_BASE_Y_POS + (float) i;
+            model->dparams->data.F32[PM_PAR_XPOS] = TEST_BASE_X_ERR + (float) i;
+            model->dparams->data.F32[PM_PAR_YPOS] = TEST_BASE_Y_ERR + (float) i;
+            src->psfMag = TEST_BASE_PSF_MAG + (float) i;
+            src->errMag = TEST_BASE_ERR_MAG + (float) i;
+            src->peak->flux = TEST_BASE_PEAK_FLUX + (float) i;
+            src->sky = TEST_BASE_SKY + (float) i;
+            src->skyErr = TEST_BASE_SKY_ERR + (float) i;
+            src->pixWeight = TEST_BASE_PIX_WEIGHT + (float) i;
+            src->extMag = TEST_BASE_EXT_MAG + (float) i;
+            src->apMag = TEST_BASE_AP_MAG + (float) i;
+            sources->data[i] = (psPtr *) src;
+	}
+        psMetadata *imageHeader = psMetadataAlloc();
+        psMetadata *tableHeader = psMetadataAlloc();
+        psString extname = psStringCopy("ext");
+        bool rc = pmSourcesWrite_SMPDATA(fitsFile, sources, imageHeader, tableHeader, extname);
+        ok(rc == true, "pmSourcesWrite_SMPDATA() returned TRUE with acceptable input parameters");
+        psFree(fitsFile);
+        psFree(sources);
+        psFree(imageHeader);
+        psFree(tableHeader);
+        psFree(extname);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // Call pmSourcesRead_SMPDATA() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        psFits* fitsFile = psFitsOpen(".tmp00", "r");
+        float ZERO_POINT = 25.0;
+        psMetadata *header = psMetadataAlloc();
+        psArray *array = pmSourcesRead_SMPDATA(fitsFile, header);
+        ok(array != NULL, "pmSourcesRead_SMPDATA() returned non-NULL with acceptable input parameters");
+        skip_start(array == NULL, 1, "Skipping tests because pmSourcesRead_SMPDATA() returned NULL");
+        for (int i = 0 ; i < array->n ; i++) {
+             pmSource *src = (pmSource *) array->data[i];
+             ok(src != NULL && psMemCheckSource(src), "pmSourcesRead_SMPDATA() read source %d correctly", i);
+
+             // src->model data
+             pmModel *model = src->modelPSF;
+             ok(model != NULL  && psMemCheckModel(model), "pmSourcesRead_SMPDATA() set src->modelPSF correctly");
+             skip_start(model == NULL, 2, "Skipping tests because pmSourcesRead_SMPDATA() did not set src->modelPSF");
+             {
+                 ok(model->params->data.F32[PM_PAR_XPOS] == (TEST_BASE_X_POS + (float) i),
+                   "pmSourcesRead_SMPDATA() set src->model->params->data.F32[PM_PAR_XPOS] correctly (is %.2f, should be %.2f)",
+                    model->params->data.F32[PM_PAR_XPOS], (TEST_BASE_X_POS + (float) i));
+
+                 ok(model->params->data.F32[PM_PAR_YPOS] == (TEST_BASE_Y_POS + (float) i),
+                   "pmSourcesRead_SMPDATA() set src->model->params->data.F32[PM_PAR_YPOS] correctly (is %.2f, should be %.2f)",
+                    model->params->data.F32[PM_PAR_YPOS], (TEST_BASE_Y_POS + (float) i));
+
+                 float tmpSrcSky = TEST_BASE_SKY + (float) i;
+                 float lsky = (tmpSrcSky < 1.0) ? 0.0 : log10(tmpSrcSky);
+                 float tmpF = pow(10.0, lsky);
+                 ok(model->params->data.F32[PM_PAR_SKY] == tmpF,
+                   "pmSourcesRead_SMPDATA() set src->model->params->data.F32[PM_PAR_SKY] correctly (is %.2f, should be %.2f)",
+                    model->params->data.F32[PM_PAR_SKY], tmpF);
+	     }
+             ok(src->psfMag == (TEST_BASE_PSF_MAG + (float) i), "pmSourcesRead_SMPDATA() set src->psfMag correctly (is %.2f, should be %.2f)",
+                src->psfMag, (TEST_BASE_PSF_MAG + (float) i));
+             float tmpF =  0.001 * PS_MIN(999, (1000 * (TEST_BASE_ERR_MAG + (float) i)));
+             ok(src->errMag == tmpF, "pmSourcesRead_SMPDATA() set src->errMag correctly (is %.2f, should be %.2f)",
+                src->errMag, tmpF);
+             tmpF = PS_MIN(99.0, (TEST_BASE_EXT_MAG + ZERO_POINT)) - ZERO_POINT;
+             ok(src->extMag == tmpF, "pmSourcesRead_SMPDATA() set src->extMag correctly (is %.2f, should be %.2f)",
+                src->extMag, tmpF);
+             tmpF = PS_MIN(99.0, (TEST_BASE_AP_MAG + ZERO_POINT)) - ZERO_POINT;
+             ok(src->apMag == tmpF, "pmSourcesRead_SMPDATA() set src->apMag correctly (is %.2f, should be %.2f)",
+                src->apMag, tmpF);
+             if (i%2) {
+                 ok(src->type == PM_SOURCE_TYPE_STAR, "pmSourcesRead_SMPDATA() set the source type correctly (is %d, should be %d)",
+                    src->type, PM_SOURCE_TYPE_STAR);
+	     } else {
+                 ok(src->type == PM_SOURCE_TYPE_EXTENDED, "pmSourcesRead_SMPDATA() set the source type correctly (is %d, should be %d)",
+                    src->type, PM_SOURCE_TYPE_EXTENDED);
+	     }
+             psU8 tmpU8 = (psU8) PS_MIN(255, PS_MAX(0, (255*(TEST_BASE_PIX_WEIGHT + (float) i))));
+             tmpF = (psF32) (tmpU8 / 255.0);
+             ok(src->pixWeight == tmpF, "pmSourcesRead_SMPDATA() set src->pixWeight correctly (is %.2f, should be %.2f)",
+                src->pixWeight, tmpF);
+
+             skip_end();
+
+             if (0) { // OLD
+                 // XXX: Source code always sets the type to PM_SOURCE_TYPE_STAR.  Is that right?
+                 ok(src->sky == (TEST_BASE_SKY + (float) i), "pmSourcesRead_SMPDATA() set src->sky correctly (is %.2f, should be %.2f)",
+                    src->sky, (TEST_BASE_SKY + (float) i));
+                 if (0) {
+                     ok(src->pixWeight == (TEST_BASE_PIX_WEIGHT + (float) i), "pmSourcesRead_SMPDATA() set src->pixWeight correctly (is %.2f, should be %.2f)",
+                        src->pixWeight, (TEST_BASE_PIX_WEIGHT + (float) i));
+		 }
+                 ok(TEST_FLOATS_EQUAL(src->peak->flux, 0.0), "pmSourcesRead_SMPDATA() set src->peak->flux correctly (is %.2f, should be %.2f)",
+                    src->peak->flux, 0.0);
+                 // XXX: Source code always sets src->modelPSF.  Is that right?
+                 ok(model->dparams->data.F32[PM_PAR_XPOS] == (TEST_BASE_X_ERR + (float) i),
+                   "pmSourcesRead_SMPDATA() set src->model->dparams->data.F32[PM_PAR_XPOS] correctly (is %.2f, should be %.2f)",
+                    model->dparams->data.F32[PM_PAR_XPOS], (TEST_BASE_X_ERR + (float) i));
+                 ok(model->dparams->data.F32[PM_PAR_YPOS] == (TEST_BASE_Y_ERR + (float) i),
+                   "pmSourcesRead_SMPDATA() set src->model->dparams->data.F32[PM_PAR_YPOS] correctly (is %.2f, should be %.2f)",
+                    model->dparams->data.F32[PM_PAR_YPOS], (TEST_BASE_Y_ERR + (float) i));
+	     }
+	}
+        skip_end();
+        psFree(fitsFile);
+        psFree(header);
+        psFree(array);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourcePhotometry.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourcePhotometry.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourcePhotometry.c	(revision 22322)
@@ -0,0 +1,167 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+bool pmSourcePhotometry_TestOffsets (double radius, double sigma, double fitMag, double apMag, double err1, double err2);
+
+int main (void)
+{
+
+    pmModelGroupInit ();
+
+    plan_tests(240);
+
+    diag("pmSourcePhotometry tests");
+
+    // test consistency of interpolated photometry for a range of apertures (for a fixed PSF sigma)
+    pmSourcePhotometry_TestOffsets (15.0, 2.0, -10.3759, -10.3759, +0.0000, +0.0000);
+    pmSourcePhotometry_TestOffsets (10.0, 2.0, -10.3759, -10.3759, +0.0000, +0.0000);
+    pmSourcePhotometry_TestOffsets ( 8.0, 2.0, -10.3759, -10.3759, +0.0000, +0.0000);
+    pmSourcePhotometry_TestOffsets ( 7.0, 2.0, -10.3759, -10.3759, +0.0000, +0.0001);
+    pmSourcePhotometry_TestOffsets ( 6.0, 2.0, -10.3759, -10.3758, +0.0001, +0.0004);
+    pmSourcePhotometry_TestOffsets ( 5.0, 2.0, -10.3759, -10.3733, +0.0003, +0.0011);
+    pmSourcePhotometry_TestOffsets ( 4.0, 2.0, -10.3759, -10.3520, +0.0006, +0.0018);
+    pmSourcePhotometry_TestOffsets ( 3.0, 2.0, -10.3759, -10.2626, +0.0001, +0.0002);
+    pmSourcePhotometry_TestOffsets ( 2.0, 2.0, -10.3759,  -9.7729, -0.0027, -0.0089);
+    pmSourcePhotometry_TestOffsets ( 1.0, 2.0, -10.3759,  -8.8689, -0.0051, -0.0161);
+
+    // test consistency of interpolated photometry for a range of apertures (for a fixed PSF sigma)
+    pmSourcePhotometry_TestOffsets (15.0, 1.5, -10.3759, -10.3759, +0.0000, +0.0000);
+    pmSourcePhotometry_TestOffsets (10.0, 1.5, -10.3759, -10.3759, +0.0000, +0.0000);
+    pmSourcePhotometry_TestOffsets ( 8.0, 1.5, -10.3759, -10.3759, +0.0000, +0.0000);
+    pmSourcePhotometry_TestOffsets ( 7.0, 1.5, -10.3759, -10.3759, +0.0000, +0.0001);
+    pmSourcePhotometry_TestOffsets ( 6.0, 1.5, -10.3759, -10.3758, +0.0001, +0.0004);
+    pmSourcePhotometry_TestOffsets ( 5.0, 1.5, -10.3759, -10.3733, +0.0003, +0.0011);
+    pmSourcePhotometry_TestOffsets ( 4.0, 1.5, -10.3759, -10.3520, +0.0006, +0.0018);
+    pmSourcePhotometry_TestOffsets ( 3.0, 1.5, -10.3759, -10.2626, +0.0001, +0.0002);
+    pmSourcePhotometry_TestOffsets ( 2.0, 1.5, -10.3759,  -9.7729, -0.0027, -0.0089);
+    pmSourcePhotometry_TestOffsets ( 1.0, 1.5, -10.3759,  -8.8689, -0.0051, -0.0161);
+
+    // test consistency of interpolated photometry for a range of apertures (for a fixed PSF sigma)
+    pmSourcePhotometry_TestOffsets (15.0, 1.0,  -9.4955,  -9.4955, +0.0000, +0.0000);
+    pmSourcePhotometry_TestOffsets (10.0, 1.0,  -9.4955,  -9.4955, +0.0000, +0.0000);
+    pmSourcePhotometry_TestOffsets ( 8.0, 1.0,  -9.4955,  -9.4955, +0.0000, +0.0000);
+    pmSourcePhotometry_TestOffsets ( 7.0, 1.0,  -9.4955,  -9.4955, +0.0000, +0.0000);
+    pmSourcePhotometry_TestOffsets ( 6.0, 1.0,  -9.4955,  -9.4955, +0.0000, +0.0001);
+    pmSourcePhotometry_TestOffsets ( 5.0, 1.0,  -9.4955,  -9.4955, +0.0001, +0.0002);
+    pmSourcePhotometry_TestOffsets ( 4.0, 1.0,  -9.4955,  -9.4968, +0.0006, +0.0022);
+    pmSourcePhotometry_TestOffsets ( 3.0, 1.0,  -9.4955,  -9.4945, +0.0021, +0.0068);
+    pmSourcePhotometry_TestOffsets ( 2.0, 1.0,  -9.4955,  -9.3323, -0.0034, -0.0118);
+    pmSourcePhotometry_TestOffsets ( 1.0, 1.0,  -9.4955,  -8.6844, -0.0141, -0.0440);
+
+    return exit_status();
+}
+
+bool pmSourcePhotometry_TestOffsets (double radius, double sigma, double fitMag, double apMag, double err1, double err2)
+{
+    psMemId id = psMemGetId();
+
+    diag("pmSourcePhotometry test offsets for radius %f", radius);
+
+    // generate a simple readout
+    pmReadout *readout = pmReadoutAlloc (NULL);
+    skip_start(readout == NULL, 0, "Skipping tests because pmReadoutAlloc failed");
+
+    readout->image = psImageAlloc (64, 64, PS_TYPE_F32);
+    readout->mask  = psImageAlloc (64, 64, PS_TYPE_U8);
+
+    // create an empty reference image
+    psImageInit (readout->image, 0.0);
+    psImageInit (readout->mask, 0);
+
+    // generate a simple psf
+    pmPSF *psf = pmPSFBuildSimple ("PS_MODEL_GAUSS", sigma, sigma, 0.0);
+    // psf->growth = pmGrowthCurveAlloc (2.0, 100.0, 15.0);
+    // pmGrowthCurveGenerate (readout, psf, false);
+
+    // create a source
+    pmSource *source = pmSourceAlloc ();
+    source->pixels = psMemIncrRefCounter (readout->image);
+    source->mask   = psMemIncrRefCounter (readout->mask);
+    source->type   = PM_SOURCE_TYPE_STAR;
+    source->mode   = PM_SOURCE_MODE_SUBTRACTED;
+
+    // create template model and measure apMag at fractional offsets
+    pmModel *modelRef = pmModelAlloc(psf->type);
+    modelRef->params->data.F32[PM_PAR_SKY] = 0;
+    modelRef->params->data.F32[PM_PAR_I0] = 1000;
+    modelRef->params->data.F32[PM_PAR_XPOS] = 32.5;
+    modelRef->params->data.F32[PM_PAR_YPOS] = 32.5;
+
+    // create modelPSF from this model
+    source->modelPSF = pmModelFromPSF (modelRef, psf);
+    source->modelPSF->dparams->data.F32[PM_PAR_I0] = 1;
+    source->modelPSF->radiusFit = radius;
+
+    // measure photometry for centered source (fractional pix : 0.5,0.5)
+    // pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP);
+    pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_INTERP);
+    ok_float_tol(source->psfMag, fitMag, 0.0002, "source fitMag is %f", source->psfMag);
+    ok_float_tol(source->apMag,  apMag, 0.0002, "source apMag is %f", source->apMag);
+    ok_float(source->errMag,   0.001, "source errMag is %f", source->errMag);
+    float refMag = source->apMag;
+
+    // these use an offset of 0.2,0.2
+    // measure photometry for a sub-pixel offset position
+    source->modelPSF->params->data.F32[PM_PAR_XPOS] = 32.3;
+    source->modelPSF->params->data.F32[PM_PAR_YPOS] = 32.3;
+    pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_INTERP);
+    ok_float_tol(refMag - source->apMag,  err1, 0.0002, "offset error is %f", refMag - source->apMag);
+
+    // measure photometry for a sub-pixel offset position
+    source->modelPSF->params->data.F32[PM_PAR_XPOS] = 32.7;
+    source->modelPSF->params->data.F32[PM_PAR_YPOS] = 32.3;
+    pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_INTERP);
+    ok_float_tol(refMag - source->apMag,  err1, 0.0002, "offset error is %f", refMag - source->apMag);
+
+    // measure photometry for a sub-pixel offset position
+    source->modelPSF->params->data.F32[PM_PAR_XPOS] = 32.3;
+    source->modelPSF->params->data.F32[PM_PAR_YPOS] = 32.7;
+    pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_INTERP);
+    ok_float_tol(refMag - source->apMag,  err1, 0.0002, "offset error is %f", refMag - source->apMag);
+
+    // measure photometry for a sub-pixel offset position
+    source->modelPSF->params->data.F32[PM_PAR_XPOS] = 32.7;
+    source->modelPSF->params->data.F32[PM_PAR_YPOS] = 32.7;
+    pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_INTERP);
+    ok_float_tol(refMag - source->apMag,  err1, 0.0002, "offset error is %f", refMag - source->apMag);
+
+    // these use an offset of 0.4,0.4
+    // measure photometry for a sub-pixel offset position
+    source->modelPSF->params->data.F32[PM_PAR_XPOS] = 32.1;
+    source->modelPSF->params->data.F32[PM_PAR_YPOS] = 32.1;
+    pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_INTERP);
+    ok_float_tol(refMag - source->apMag,  err2, 0.0002, "offset error is %f", refMag - source->apMag);
+
+    // measure photometry for a sub-pixel offset position
+    source->modelPSF->params->data.F32[PM_PAR_XPOS] = 32.9;
+    source->modelPSF->params->data.F32[PM_PAR_YPOS] = 32.1;
+    pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_INTERP);
+    ok_float_tol(refMag - source->apMag,  err2, 0.0002, "offset error is %f", refMag - source->apMag);
+
+    // measure photometry for a sub-pixel offset position
+    source->modelPSF->params->data.F32[PM_PAR_XPOS] = 32.1;
+    source->modelPSF->params->data.F32[PM_PAR_YPOS] = 32.9;
+    pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_INTERP);
+    ok_float_tol(refMag - source->apMag,  err2, 0.0002, "offset error is %f", refMag - source->apMag);
+
+    // measure photometry for a sub-pixel offset position
+    source->modelPSF->params->data.F32[PM_PAR_XPOS] = 32.9;
+    source->modelPSF->params->data.F32[PM_PAR_YPOS] = 32.9;
+    pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_INTERP);
+    ok_float_tol(refMag - source->apMag,  err2, 0.0002, "offset error is %f", refMag - source->apMag);
+
+    psFree (source);
+    psFree (modelRef);
+    psFree (psf);
+    psFree (readout);
+
+    skip_end();
+
+    ok(!psMemCheckLeaks (id, NULL, stdout, false), "no memory leaks");
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceSky.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceSky.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceSky.c	(revision 22322)
@@ -0,0 +1,267 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+        pmSourceLocalSky(): needs more thorough testing with acceptable input params.
+        pmSourceLocalSkyVariance(): needs more thorough testing with acceptable input params.
+*/
+
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           (8)
+#define TEST_NUM_COLS           (16)
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.0001)
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(26);
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceLocalSky() tests
+    // Call pmSourceLocalSky() with NULL pmSource input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i =0 ; i < src->pixels->numRows ; i++) {
+            for (int j = 0 ; j < src->pixels->numCols ; j++) {
+                src->pixels->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        bool rc = pmSourceLocalSky(NULL, PS_STAT_SAMPLE_MEAN, 10.0, 1, 2);
+        ok(rc == false, "pmSourceLocalSky() returned FALSE with NULL pmSource input parameter");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceLocalSky() with NULL pmSource->pixels input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        bool rc = pmSourceLocalSky(src, PS_STAT_SAMPLE_MEAN, 10.0, 1, 2);
+        ok(rc == false, "pmSourceLocalSky() returned FALSE with NULL pmSource->pixels input parameter");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceLocalSky() with NULL pmSource->peak input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i =0 ; i < src->pixels->numRows ; i++) {
+            for (int j = 0 ; j < src->pixels->numCols ; j++) {
+                src->pixels->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        bool rc = pmSourceLocalSky(src, PS_STAT_SAMPLE_MEAN, 10.0, 1, 2);
+        ok(rc == false, "pmSourceLocalSky() returned FALSE with NULL pmSource->peak input parameter");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceLocalSky() with NULL pmSource->maskObj input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i =0 ; i < src->pixels->numRows ; i++) {
+            for (int j = 0 ; j < src->pixels->numCols ; j++) {
+                src->pixels->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        bool rc = pmSourceLocalSky(src, PS_STAT_SAMPLE_MEAN, -10.0, 1, 2);
+        ok(rc == false, "pmSourceLocalSky() returned FALSE with NULL pmSource->maskObj input parameter");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceLocalSky() with negative input radius
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i =0 ; i < src->pixels->numRows ; i++) {
+            for (int j = 0 ; j < src->pixels->numCols ; j++) {
+                src->pixels->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        bool rc = pmSourceLocalSky(src, PS_STAT_SAMPLE_MEAN, -10.0, 1, 2);
+        ok(rc == false, "pmSourceLocalSky() returned FALSE with negative input radius");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    
+    // Call pmSourceLocalSky() with acceptable input parameters
+    // XX: Future Improvements:
+    //     Test more PS_STATS types
+    //     Test more psRegion values (region bigger than image, 0 region, etc.)
+    //     Test mask values
+    //
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->pixels = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        psF32 mean = 0.0;
+        for (int i = 0 ; i < src->pixels->numRows ; i++) {
+            for (int j = 0 ; j < src->pixels->numCols ; j++) {
+                src->pixels->data.F32[i][j] = (float) (i + j);
+                mean+= (float) (i + j);
+                src->maskObj->data.U8[i][j] = 0;
+	    }
+	}
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        bool rc = pmSourceLocalSky(src, PS_STAT_SAMPLE_MEAN, 10.0, 0, 0);
+        ok(rc == true, "pmSourceLocalSky() returned TRUE with acceptable input parameters");
+        psF32 actualMean =  mean / (int) (TEST_NUM_ROWS * TEST_NUM_COLS);
+        psF32 testMean = src->moments->Sky;
+        ok(TEST_FLOATS_EQUAL(actualMean, testMean), "pmSourceLocalSky() calculated the mean correctly");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceLocalSkyVariance() tests
+    // Call pmSourceLocalSkyVariance() with NULL pmSource input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i =0 ; i < src->weight->numRows ; i++) {
+            for (int j = 0 ; j < src->weight->numCols ; j++) {
+                src->weight->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        bool rc = pmSourceLocalSkyVariance(NULL, PS_STAT_SAMPLE_MEAN, 10.0, 1, 2);
+        ok(rc == false, "pmSourceLocalSkyVariance() returned FALSE with NULL pmSource input parameter");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceLocalSkyVariance() with NULL pmSource->weight input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        bool rc = pmSourceLocalSkyVariance(src, PS_STAT_SAMPLE_MEAN, 10.0, 1, 2);
+        ok(rc == false, "pmSourceLocalSkyVariance() returned FALSE with NULL pmSource->weight input parameter");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceLocalSkyVariance() with NULL pmSource->maskObj input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i =0 ; i < src->weight->numRows ; i++) {
+            for (int j = 0 ; j < src->weight->numCols ; j++) {
+                src->weight->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        bool rc = pmSourceLocalSkyVariance(src, PS_STAT_SAMPLE_MEAN, -10.0, 1, 2);
+        ok(rc == false, "pmSourceLocalSkyVariance() returned FALSE with NULL pmSource->maskObj input parameter");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceLocalSkyVariance() with NULL pmSource->peak input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i =0 ; i < src->weight->numRows ; i++) {
+            for (int j = 0 ; j < src->weight->numCols ; j++) {
+                src->weight->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        bool rc = pmSourceLocalSkyVariance(src, PS_STAT_SAMPLE_MEAN, 10.0, 1, 2);
+        ok(rc == false, "pmSourceLocalSkyVariance() returned FALSE with NULL pmSource->peak input parameter");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceLocalSkyVariance() with negative input radius
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        for (int i =0 ; i < src->weight->numRows ; i++) {
+            for (int j = 0 ; j < src->weight->numCols ; j++) {
+                src->weight->data.F32[i][j] = (float) (i + j);
+	    }
+	}
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        bool rc = pmSourceLocalSkyVariance(src, PS_STAT_SAMPLE_MEAN, -10.0, 1, 2);
+        ok(rc == false, "pmSourceLocalSkyVariance() returned FALSE with negative input radius");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceLocalSkyVariance() with acceptable input parameters
+    // XX: Future Improvements:
+    //     Test more PS_STATS types
+    //     Test more psRegion values (region bigger than image, 0 region, etc.)
+    //     Test mask values
+    //
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        src->maskObj = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        psF32 mean = 0.0;
+        for (int i = 0 ; i < src->weight->numRows ; i++) {
+            for (int j = 0 ; j < src->weight->numCols ; j++) {
+                src->weight->data.F32[i][j] = (float) (i + j);
+                mean+= (float) (i + j);
+                src->maskObj->data.U8[i][j] = 0;
+	    }
+	}
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        bool rc = pmSourceLocalSkyVariance(src, PS_STAT_SAMPLE_MEAN, 10.0, 0, 0);
+        ok(rc == true, "pmSourceLocalSkyVariance() returned TRUE with acceptable input parameters");
+        psF32 actualMean =  mean / (int) (TEST_NUM_ROWS * TEST_NUM_COLS);
+        psF32 testMean = src->moments->dSky;
+        ok(TEST_FLOATS_EQUAL(actualMean, testMean), "pmSourceLocalSky() calculated the mean correctly");
+        psFree(src);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmSourceUtils.c	(revision 22322)
@@ -0,0 +1,272 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS
+    All functions are tested.
+       pmSourceFromModel(): Must verify the pmSourceDefinePixels() set the
+           values correctly.
+*/
+
+#define MISC_NUM                32
+#define MISC_NAME              "META00"
+#define NUM_BIAS_DATA           10
+#define TEST_NUM_ROWS           (8)
+#define TEST_NUM_COLS           (16)
+#define VERBOSE                 0
+#define ERR_TRACE_LEVEL         0
+#define TEST_FLOATS_EQUAL(X, Y) (abs((X) - (Y)) < 0.0001)
+#define NUM_SOURCES		100
+
+#define CELL_ALLOC_NAME        "CellName"
+#define NUM_READOUTS            3
+#define NUM_CELLS               10
+#define NUM_HDUS                5
+#define BASE_IMAGE              10
+#define BASE_MASK               40
+#define BASE_WEIGHT             70
+
+
+/******************************************************************************
+generateSimpleReadout(): This function generates a pmReadout data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmReadout *generateSimpleReadout(pmCell *cell)
+{
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    readout->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_U8);
+    readout->weight = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < NUM_BIAS_DATA ; i++) {
+        psImage *tmpImage = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(tmpImage, (double) i);
+        psListAdd(readout->bias, PS_LIST_HEAD, tmpImage);
+        psFree(tmpImage);
+    }
+    psMetadataAddS32(readout->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    return(readout);
+}
+
+/******************************************************************************
+generateSimpleCell(): This function generates a pmCell data structure and then
+populates its members with real data.
+ *****************************************************************************/
+pmCell *generateSimpleCell(pmChip *chip)
+{
+    pmCell *cell = pmCellAlloc(chip, CELL_ALLOC_NAME);
+
+    psMetadataAddS32(cell->analysis, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psMetadataAddS32(cell->concepts, PS_LIST_HEAD, MISC_NAME, 0, NULL, MISC_NUM);
+    psArrayRealloc(cell->readouts, NUM_READOUTS);
+    cell->hdu = pmHDUAlloc("cellExtName");
+    for (int i = 0 ; i < NUM_READOUTS ; i++) {
+        cell->readouts->data[i] = generateSimpleReadout(cell);
+    }
+
+    // First try to read data from ../dataFiles, then try dataFiles.
+    bool rc = pmConfigFileRead(&cell->hdu->format, "../dataFiles/camera0/format0.config", "Camera format 0");
+    if (!rc) {
+        rc = pmConfigFileRead(&cell->hdu->format, "dataFiles/camera0/format0.config", "Camera format 0");
+        if (!rc) {
+            diag("pmConfigFileRead() was unsuccessful (from generateSimpleCell())");
+	}
+    }
+
+    cell->hdu->images = psArrayAlloc(NUM_HDUS);
+    cell->hdu->masks = psArrayAlloc(NUM_HDUS);
+    cell->hdu->weights = psArrayAlloc(NUM_HDUS);
+    for (int k = 0 ; k < NUM_HDUS ; k++) {
+        cell->hdu->images->data[k]  = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        cell->hdu->masks->data[k]   = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_MASK);
+        cell->hdu->weights->data[k] = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_F32);
+        psImageInit(cell->hdu->images->data[k], (float) (BASE_IMAGE+k));
+        psImageInit(cell->hdu->masks->data[k], (psU8) (BASE_MASK+k));
+        psImageInit(cell->hdu->weights->data[k], (float) (BASE_WEIGHT+k));
+    }
+
+    psRegion *region = psRegionAlloc(0.0, 0.0, 0.0, 0.0);
+    // You shouldn't have to remove the key from the metadata.  Find out how to simply change the key value.
+    psMetadataRemoveKey(cell->concepts, "CELL.TRIMSEC");
+    psMetadataAddPtr(cell->concepts, PS_LIST_TAIL|PS_META_REPLACE, "CELL.TRIMSEC", PS_DATA_REGION, "I am a region", region);
+    psFree(region);
+    return(cell);
+}
+
+void myFreeCell(pmCell *cell)
+{
+    for (int k = 0 ; k < cell->readouts->n ; k++) {
+        psFree(cell->readouts->data[k]);
+    }
+    psFree(cell);
+}
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    psTraceSetLevel("psModules.objects", 0);
+    plan_tests(23);
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceModelGuess() tests
+    // Call pmSourceModelGuess() with NULL pmSource input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        src->moments = pmMomentsAlloc();
+        pmModelType type = pmModelClassGetType ("PS_MODEL_GAUSS");
+        pmModel *model = pmSourceModelGuess(NULL, type);
+        ok(model == NULL, "pmSourceModelGuess() returned NULL with NULL pmSource input parameter");
+        psFree(src);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceModelGuess() with NULL pmSource->peak input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->moments = pmMomentsAlloc();
+        pmModelType type = pmModelClassGetType ("PS_MODEL_GAUSS");
+        pmModel *model = pmSourceModelGuess(NULL, type);
+        ok(model == NULL, "pmSourceModelGuess() returned NULL with NULL pmSource->peak input parameter");
+        psFree(src);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceModelGuess() with NULL pmSource->moments input parameter
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        pmModelType type = pmModelClassGetType ("PS_MODEL_GAUSS");
+        pmModel *model = pmSourceModelGuess(NULL, type);
+        ok(model == NULL, "pmSourceModelGuess() returned NULL with NULL pmSource->moments input parameter");
+        psFree(src);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // pmModel *pmSourceModelGuess(pmSource *source, pmModelType modelType)
+    // Call pmSourceModelGuess() with acceptable input parameters
+    // We only test a single model (PS_MODEL_GAUSS), but since this function is mostly
+    // a wrapper to the model functions, that will suffice.
+    {
+        psMemId id = psMemGetId();
+        pmSource *src = pmSourceAlloc();
+        src->peak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+        src->moments = pmMomentsAlloc();
+
+        src->moments->Sx = 1.0;
+        src->moments->y = 2.0;
+        src->moments->Sxy = 3.0;
+        src->moments->Sky = 4.0;
+        src->moments->Peak = 5.0;
+        src->moments->x = 6.0;
+        src->moments->y = 7.0;
+
+        pmModelType type = pmModelClassGetType("PS_MODEL_GAUSS");
+        pmModel *testModel = pmModelAlloc(type);
+        testModel->modelGuess(testModel, src);
+        pmModel *model = pmSourceModelGuess(src, type);
+        ok(model != NULL, "pmSourceModelGuess() returned non-NULL with acceptable input parameters");
+        psF32 *PAR  = model->params->data.F32;
+        psEllipseMoments emoments;
+        emoments.x2 = src->moments->Sx;
+        emoments.y2 = src->moments->Sy;
+        emoments.xy = src->moments->Sxy;
+        // force the axis ratio to be < 20.0
+        psEllipseAxes axes = psEllipseMomentsToAxes (emoments, 20.0);
+        psEllipseShape shape = psEllipseAxesToShape (axes);
+        ok(TEST_FLOATS_EQUAL(PAR[PM_PAR_SKY], src->moments->Sky), "pmSourceModelGuess() returned set model->params[PM_PAR_SKY] correctly");
+        ok(TEST_FLOATS_EQUAL(PAR[PM_PAR_I0], src->moments->Peak - src->moments->Sky), "pmSourceModelGuess() returned set model->params[PM_PAR_IO] correctly");
+        ok(TEST_FLOATS_EQUAL(PAR[PM_PAR_XPOS], src->moments->x), "pmSourceModelGuess() returned set model->params[PM_PAR_XPOS] correctly");
+        ok(TEST_FLOATS_EQUAL(PAR[PM_PAR_YPOS], src->moments->y), "pmSourceModelGuess() returned set model->params[PM_PAR_YPOS] correctly");
+        ok(TEST_FLOATS_EQUAL(PAR[PM_PAR_SXX], PS_MAX(0.5, M_SQRT2*shape.sx)), "pmSourceModelGuess() returned set model->params[PM_PAR_SXX] correctly");
+        ok(TEST_FLOATS_EQUAL(PAR[PM_PAR_SYY], PS_MAX(0.5, M_SQRT2*shape.sy)), "pmSourceModelGuess() returned set model->params[PM_PAR_SYY] correctly");
+        ok(TEST_FLOATS_EQUAL(PAR[PM_PAR_SXY], shape.sxy), "pmSourceModelGuess() returned set model->params[PM_PAR_SXY] correctly");
+        psFree(src);
+        psFree(testModel);
+        psFree(model);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ----------------------------------------------------------------------
+    // pmSourceModelGuess() tests
+    // Call pmSourceFromModel() with NULL pmModel input parameter
+    {
+        psMemId id = psMemGetId();
+        pmModelType type = pmModelClassGetType ("PS_MODEL_GAUSS");
+        pmModel *model = pmModelAlloc(type);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *readout = cell->readouts->data[0];
+        pmSource *src = pmSourceFromModel(NULL, readout, 10.0, PM_SOURCE_TYPE_STAR);
+        ok(src == NULL, "pmSourceFromModel() returned NULL with NULL pmModel input parameter");
+        psFree(model);
+        psFree(src);
+        myFreeCell(cell);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFromModel() with NULL pmReadout input parameter
+    {
+        psMemId id = psMemGetId();
+
+        pmModelType type = pmModelClassGetType ("PS_MODEL_GAUSS");
+        pmModel *model = pmModelAlloc(type);
+        pmCell *cell = generateSimpleCell(NULL);
+        pmSource *src = pmSourceFromModel(model, NULL, 10.0, PM_SOURCE_TYPE_STAR);
+        ok(src == NULL, "pmSourceFromModel() returned NULL with NULL pmReadout input parameter");
+        psFree(model);
+        psFree(src);
+        myFreeCell(cell);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmSourceFromModel() with acceptable input parameters
+    // XXX: Must verify the pmSourceDefinePixels() set the values correctly.
+    {
+        psMemId id = psMemGetId();
+
+        pmModel *model = pmModelAlloc(pmModelClassGetType("PS_MODEL_GAUSS"));
+        float Io    = model->params->data.F32[PM_PAR_I0] = 2.0;
+        float xChip = model->params->data.F32[PM_PAR_XPOS] = 3.0;
+        float yChip = model->params->data.F32[PM_PAR_YPOS] = 5.0;
+        pmCell *cell = generateSimpleCell(NULL);
+        pmReadout *readout = cell->readouts->data[0];
+        pmSource *src = pmSourceFromModel(model, readout, 10.0, PM_SOURCE_TYPE_STAR);
+        ok(src != NULL, "pmSourceFromModel() returned non-NULL with acceptable input parameters");
+        ok(src->modelPSF == model, "pmSourceFromModel() set pmSource->modelPSF correctly");
+
+        pmPeak *tmpPeak = pmPeakAlloc (xChip, yChip, Io, PM_PEAK_LONE);
+        ok(src->peak->x == xChip, "pmSourceFromModel() set pmSource->peak->x correctly (%.2f %.2f)", src->peak->x, xChip);
+
+        psFree(model);
+        // XXX: We get psMemory aborts if the following is not done.
+        // There is probably an issue with psMemIncrRefCounter() in pmSourceUtils.c
+
+        src->modelPSF = NULL;
+        src->modelEXT = NULL;
+        psFree(src);
+        psFree(tmpPeak);
+        myFreeCell(cell);
+        pmModelClassCleanup();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmTrend2D.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmTrend2D.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tap_pmTrend2D.c	(revision 22322)
@@ -0,0 +1,504 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "tap.h"
+#include "pstap.h"
+/* STATUS:
+    All functions are tested.
+        pmTrend2DFit(): Must test the PM_TREND_MAP case.
+*/
+
+#define NUM_ROWS 8
+#define NUM_COLS 16
+#define ERR_TRACE_LEVEL 0
+#define TEST_FLOATS_EQUAL(X, Y) (abs(X - Y) < 0.01)
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    psTraceSetLevel("err", ERR_TRACE_LEVEL);
+    plan_tests(91);
+
+    // ------------------------------------------------------------------------
+    // Test pmTrend2DAlloc()
+    // Call pmTrend2DAlloc() with NULL psImage input parameter (PM_TREND_MAP)
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        pmTrend2D *trend = pmTrend2DAlloc(PM_TREND_MAP, NULL, 2, 2, stats);
+        ok(trend == NULL, "pmTrend2DAlloc() returned NULL with NULL psImage input parameter");
+        psFree(img);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DAlloc() with NULL psImage input parameter (PM_TREND_POLY_ORD)
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        pmTrend2D *trend = pmTrend2DAlloc(PM_TREND_POLY_ORD, NULL, 2, 2, stats);
+        ok(trend != NULL, "pmTrend2DAlloc() returned NULL with NULL psImage input parameter");
+        psFree(img);
+        psFree(stats);
+        psFree(trend);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DAlloc() with NULL psStats input parameter
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        pmTrend2D *trend = pmTrend2DAlloc(PM_TREND_POLY_ORD, img, 2, 2, NULL);
+        ok(trend == NULL, "pmTrend2DAlloc() returned NULL with NULL psStats input parameter");
+        psFree(img);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DAlloc() with unallowed pmTrend2DMode
+    // XXX: We skip this test because pmTrend2DAlloc() aborts.
+    if (0) {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        pmTrend2D *trend = pmTrend2DAlloc(999, img, 2, 2, stats);
+        ok(trend == NULL, "pmTrend2DAlloc() returned NULL with unallowed pmTrend2DMode");
+        psFree(img);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DAlloc() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+        // Call pmTrend2DAlloc() with PM_TREND_POLY_ORD
+        pmTrend2D *trend = pmTrend2DAlloc(PM_TREND_POLY_ORD, img, 2, 4, stats);
+        ok(trend != NULL && psMemCheckTrend2D(trend), 
+          "pmTrend2DAlloc() returned non-NULL with acceptable input parameters");
+        ok(trend->map == NULL, "pmTrend2DAlloc() set trend->map to NULL");
+        ok(trend->mode == PM_TREND_POLY_ORD, "pmTrend2DAlloc() set trend->mode correctly");
+        ok(trend->stats == stats, "pmTrend2DAlloc() set trend->stats correctly");
+        ok(trend->poly != NULL && trend->poly->type == PS_POLYNOMIAL_ORD, "pmTrend2DAlloc() set trend->poly->type correctly");
+        ok(trend->poly->type == PS_POLYNOMIAL_ORD, "pmTrend2DAlloc() set trend->poly->type correctly");
+        ok(trend->poly->nX == 2, "pmTrend2DAlloc() set trend->poly->nX correctly");
+        ok(trend->poly->nY == 4, "pmTrend2DAlloc() set trend->poly->nY correctly");
+        psFree(trend);
+
+        // Call pmTrend2DAlloc() with PM_TREND_POLY_CHEB
+        trend = pmTrend2DAlloc(PM_TREND_POLY_CHEB, img, 2, 4, stats);
+        ok(trend != NULL && psMemCheckTrend2D(trend), 
+          "pmTrend2DAlloc() returned non-NULL with acceptable input parameters");
+        ok(trend->map == NULL, "pmTrend2DAlloc() set trend->map to NULL");
+        ok(trend->mode == PM_TREND_POLY_CHEB, "pmTrend2DAlloc() set trend->mode correctly");
+        ok(trend->stats == stats, "pmTrend2DAlloc() set trend->stats correctly");
+        ok(trend->poly != NULL && trend->poly->type == PS_POLYNOMIAL_CHEB, "pmTrend2DAlloc() set trend->poly->type correctly");
+        ok(trend->poly->type == PS_POLYNOMIAL_CHEB, "pmTrend2DAlloc() set trend->poly->type correctly");
+        ok(trend->poly->nX == 2, "pmTrend2DAlloc() set trend->poly->nX correctly");
+        ok(trend->poly->nY == 4, "pmTrend2DAlloc() set trend->poly->nY correctly");
+        psFree(trend);
+
+        // Create a new pmTrend with PM_TREND_MAP
+        trend = pmTrend2DAlloc(PM_TREND_MAP, img, 2, 4, stats);
+        ok(trend->map != NULL && psMemCheckImageMap(trend->map), 
+           "pmTrend2DAlloc() set trend->map correctly");
+        psFree(trend);
+
+        psFree(img);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Test pmTrend2DNoImageAlloc()
+    // Call pmTrend2DNoImageAlloc() with NULL psImageBinning input parameter
+    {
+        psMemId id = psMemGetId();
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psImageBinning *binning = psImageBinningAlloc();
+        pmTrend2D *trend = pmTrend2DNoImageAlloc(PM_TREND_MAP, NULL, stats);
+        ok(trend == NULL, "pmTrend2DNoImageAlloc() returned NULL with NULL psImageBinning input parameter");
+        psFree(stats);
+        psFree(binning);
+        psFree(trend);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DNoImageAlloc() with NULL psStats input parameter
+    {
+        psMemId id = psMemGetId();
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psImageBinning *binning = psImageBinningAlloc();
+        pmTrend2D *trend = pmTrend2DNoImageAlloc(PM_TREND_MAP, binning, NULL);
+        ok(trend == NULL, "pmTrend2DNoImageAlloc() returned NULL with NULL psStats input parameter");
+        psFree(stats);
+        psFree(binning);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DNoImageAlloc() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psImageBinning *binning = psImageBinningAlloc();
+        binning->nXruff = 2;
+        binning->nYruff = 4;
+
+        // Call pmTrend2DNoImageAlloc() with PM_TREND_POLY_ORD
+        pmTrend2D *trend = pmTrend2DNoImageAlloc(PM_TREND_POLY_ORD, binning, stats);
+        ok(trend != NULL && psMemCheckTrend2D(trend), "pmTrend2DNoImageAlloc() returned non-NULL with acceptable input parameters");
+        ok(trend->map == NULL, "pmTrend2DAlloc() set trend->map to NULL");
+        ok(trend->mode == PM_TREND_POLY_ORD, "pmTrend2DAlloc() set trend->mode correctly");
+        ok(trend->stats == stats, "pmTrend2DAlloc() set trend->stats correctly");
+        ok(trend->poly != NULL && trend->poly->type == PS_POLYNOMIAL_ORD, "pmTrend2DAlloc() set trend->poly->type correctly");
+        ok(trend->poly->type == PS_POLYNOMIAL_ORD, "pmTrend2DAlloc() set trend->poly->type correctly");
+        ok(trend->poly->nX == 2, "pmTrend2DAlloc() set trend->poly->nX correctly");
+        ok(trend->poly->nY == 4, "pmTrend2DAlloc() set trend->poly->nY correctly");
+        psFree(trend);
+
+        // Call pmTrend2DNoImageAlloc() with PM_TREND_POLY_CHEB
+        trend = pmTrend2DNoImageAlloc(PM_TREND_POLY_CHEB, binning, stats);
+        ok(trend != NULL && psMemCheckTrend2D(trend), "pmTrend2DNoImageAlloc() returned non-NULL with acceptable input parameters");
+        ok(trend->map == NULL, "pmTrend2DAlloc() set trend->map to NULL");
+        ok(trend->mode == PM_TREND_POLY_CHEB, "pmTrend2DAlloc() set trend->mode correctly");
+        ok(trend->stats == stats, "pmTrend2DAlloc() set trend->stats correctly");
+        ok(trend->poly != NULL && trend->poly->type == PS_POLYNOMIAL_CHEB, "pmTrend2DAlloc() set trend->poly->type correctly");
+        ok(trend->poly->type == PS_POLYNOMIAL_CHEB, "pmTrend2DAlloc() set trend->poly->type correctly");
+        ok(trend->poly->nX == 2, "pmTrend2DAlloc() set trend->poly->nX correctly");
+        ok(trend->poly->nY == 4, "pmTrend2DAlloc() set trend->poly->nY correctly");
+        psFree(trend);
+
+        // Call pmTrend2DNoImageAlloc() with PM_TREND_MAP
+        trend = pmTrend2DNoImageAlloc(PM_TREND_MAP, binning, stats);
+        ok(trend != NULL && psMemCheckTrend2D(trend), "pmTrend2DNoImageAlloc() returned non-NULL with acceptable input parameters");
+        ok(trend->map && psMemCheckImageMap(trend->map), "pmTrend2DNoImageAlloc() set the trend->map correctly");
+        psFree(trend);
+
+        psFree(stats);
+        psFree(binning);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Test pmTrend2DFieldAlloc()
+    // Call pmTrend2DFieldAlloc() with NULL psStats input parameter
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        pmTrend2D *trend = pmTrend2DFieldAlloc(PM_TREND_MAP, 1, 2, 3, 4, NULL);
+        ok(trend == NULL, "pmTrend2DFieldAlloc() returned NULL with NULL psStats input parameter");
+        psFree(img);
+        psFree(stats);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DFieldAlloc() with acceptable input parameters
+    {
+        psMemId id = psMemGetId();
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        pmTrend2D *trend = pmTrend2DFieldAlloc(PM_TREND_POLY_ORD, 1, 2, 3, 4, stats);
+        ok(trend != NULL && psMemCheckTrend2D(trend), "pmTrend2DFieldAlloc() returned non-NULL with acceptable input parameters");
+        ok(trend->poly != NULL && trend->poly->type == PS_POLYNOMIAL_ORD, "pmTrend2DFieldAlloc() set trend->poly->type correctly");
+        ok(trend->poly->type == PS_POLYNOMIAL_ORD, "pmTrend2DFieldAlloc() set trend->poly->type correctly");
+        ok(trend->poly->nX == 3, "pmTrend2DFieldAlloc() set trend->poly->nX correctly");
+        ok(trend->poly->nY == 4, "pmTrend2DFieldAlloc() set trend->poly->nY correctly");
+        psFree(trend);
+        trend = NULL;
+
+        // Create a new pmTrend with PM_TREND_MAP
+        // XXX: This currently fails due to a big in pmTrend2DFieldAlloc():
+        if (0) {
+            trend = pmTrend2DFieldAlloc(PM_TREND_MAP, 1, 2, 3, 4, stats);
+            ok(trend->map != NULL && psMemCheckImageMap(trend->map), 
+               "pmTrend2DAlloc() set trend->map correctly");
+	}
+
+        psFree(stats);
+        psFree(trend);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Test pmTrend2DModeToString()
+    // psString pmTrend2DModeToString (pmTrend2DMode mode)
+    // Call pmTrend2DModeToString() with unallowed pmTrend2DMode.
+    // XX: We comment this out because pmTrend2DModeToString() aborts.
+    if (0) {
+        psMemId id = psMemGetId();
+        psString str = pmTrend2DModeToString(99);
+        ok(str == NULL, "pmTrend2DModeToString() returned NULL with unallowed pmTrend2DMode");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DModeToString() with unallowed pmTrend2DMode.
+    {
+        psMemId id = psMemGetId();
+        psString str = pmTrend2DModeToString(PM_TREND_NONE);
+        ok(!strcmp(str, "NONE"), "pmTrend2DModeToString(PM_TREND_NONE)");
+        psFree(str);
+
+        str = pmTrend2DModeToString(PM_TREND_POLY_ORD);
+        ok(!strcmp(str, "POLY_ORD"), "pmTrend2DModeToString(PM_TREND_POLY_ORD)");
+        psFree(str);
+
+        str = pmTrend2DModeToString(PM_TREND_POLY_CHEB);
+        ok(!strcmp(str, "POLY_CHEB"), "pmTrend2DModeToString(PM_TREND_POLY_CHEB)");
+        psFree(str);
+
+        str = pmTrend2DModeToString(PM_TREND_MAP);
+        ok(!strcmp(str, "MAP"), "pmTrend2DModeToString(PM_TREND_MAP)");
+        psFree(str);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Test pmTrend2DModeFromString()
+    // Call pmTrend2DModeFromString() with NULL input parameter
+    {
+        psMemId id = psMemGetId();
+        pmTrend2DMode mode = pmTrend2DModeFromString(NULL);
+        ok(PM_TREND_NONE == mode, "pmTrend2DModeFromString(NULL) returned PM_TREND_NONE");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DModeFromString() with unallowed input string
+    {
+        psMemId id = psMemGetId();
+        pmTrend2DMode mode = pmTrend2DModeFromString("BOGUS");
+        ok(PM_TREND_NONE == mode, "pmTrend2DModeFromString(BOGUS) returned PM_TREND_NONE");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DModeFromString() with acceptable input string
+    {
+        psMemId id = psMemGetId();
+        pmTrend2DMode mode = pmTrend2DModeFromString("NONE");
+        ok(PM_TREND_NONE == mode, "pmTrend2DModeFromString(NONE) returned PM_TREND_NONE");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DModeFromString() with acceptable input string
+    {
+        psMemId id = psMemGetId();
+        pmTrend2DMode mode = pmTrend2DModeFromString("POLY_ORD");
+        ok(PM_TREND_POLY_ORD == mode, "pmTrend2DModeFromString(POLY_ORD) returned PM_TREND_NONE");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DModeFromString() with acceptable input string
+    {
+        psMemId id = psMemGetId();
+        pmTrend2DMode mode = pmTrend2DModeFromString("POLY_CHEB");
+        ok(PM_TREND_POLY_CHEB == mode, "pmTrend2DModeFromString(POLY_CHEB) returned PM_TREND_NONE");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Call pmTrend2DModeFromString() with acceptable input string
+    {
+        psMemId id = psMemGetId();
+        pmTrend2DMode mode = pmTrend2DModeFromString("MAP");
+        ok(PM_TREND_MAP == mode, "pmTrend2DModeFromString(MAP) returned PM_TREND_NONE");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Test pmTrend2DFit()
+    // Call pmTrend2DFit() with bad input parameters
+    {
+        #define VEC_SIZE 9
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        pmTrend2D *trend = pmTrend2DAlloc(PM_TREND_POLY_ORD, img, 4, 4, stats);
+        ok(trend != NULL && psMemCheckTrend2D(trend), 
+          "pmTrend2DAlloc() returned non-NULL with acceptable input parameters");
+        psVector *x = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        psVector *y = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        psVector *f = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        psVector *mask = psVectorAlloc(VEC_SIZE, PS_TYPE_U8);
+        psVector *df = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        for (int i = 0 ; i < VEC_SIZE ; i++) {
+            x->data.F32[i] = (float) (i);
+            y->data.F32[i] = (float) (2 * i);
+            f->data.F32[i] = x->data.F32[i] * y->data.F32[i];
+            mask->data.U8[i] = 0;
+            df->data.F32[i] = 0.0;
+        }
+
+        // NULL pmTrend2D input parameter
+        bool rc = pmTrend2DFit(NULL, mask, 0, x, y, f, df);
+        ok(rc == false, "pmTrend2DFit() returned FALSE with NULL pmTrend2D input parameter");
+
+        // NULL mask input parameter
+        rc = pmTrend2DFit(trend, NULL, 0, x, y, f, df);
+        ok(rc == false, "pmTrend2DFit() returned FALSE with NULL mask input parameter");
+
+        // NULL x psVector input parameter
+        rc = pmTrend2DFit(trend, mask, 0, NULL, y, f, df);
+        ok(rc == false, "pmTrend2DFit() returned FALSE with NULL x psVector input parameter");
+
+        // NULL y psVector input parameter
+        rc = pmTrend2DFit(trend, mask, 0, x, NULL, f, df);
+        ok(rc == false, "pmTrend2DFit() returned FALSE with NULL y psVector input parameter");
+
+        // NULL f psVector input parameter
+        rc = pmTrend2DFit(trend, mask, 0, x, y, NULL, df);
+        ok(rc == false, "pmTrend2DFit() returned FALSE with NULL f psVector input parameter");
+
+        // NULL df psVector input parameter
+        rc = pmTrend2DFit(trend, mask, 0, x, y, f, NULL);
+        ok(rc == true, "pmTrend2DFit() returned TRUE with NULL df psVector input parameter");
+
+        psFree(img);
+        psFree(stats);
+        psFree(trend);
+        psFree(x);
+        psFree(y);
+        psFree(f);
+        psFree(mask);
+        psFree(df);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Test pmTrend2DEval()
+    // Call pmTrend2DEval() with bad input parameters
+    {
+        psMemId id = psMemGetId();
+        psF64 tmpD = pmTrend2DEval(NULL, 0.0, 0.0);
+        ok(TEST_FLOATS_EQUAL(tmpD, 0.0), "pmTrend2DEval() returned 0.0 with NULL pmTrend2D input parameter");
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Test pmTrend2DEvalVector()
+    // psVector *pmTrend2DEvalVector (pmTrend2D *trend, psVector *x, psVector *y)
+    // Call pmTrend2DEvalVector() with bad input parameters
+    {
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        pmTrend2D *trend = pmTrend2DAlloc(PM_TREND_POLY_ORD, img, 4, 4, stats);
+        psVector *x = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        psVector *y = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+
+        // NULL pmTrend2D input parameter
+        psVector *f = pmTrend2DEvalVector(NULL, x, y);
+        ok(f == NULL, "pmTrend2DEvalVector() returned NULL with NULL pmTrend2D input parameter");
+
+        // NULL x psVector input parameter
+        f = pmTrend2DEvalVector(trend, NULL, y);
+        ok(f == NULL, "pmTrend2DEvalVector() returned NULL with NULL x psVector input parameter");
+
+        // NULL y psVector input parameter
+        f = pmTrend2DEvalVector(trend, x, NULL);
+        ok(f == NULL, "pmTrend2DEvalVector() returned NULL with NULL y psVector input parameter");
+
+        psFree(img);
+        psFree(stats);
+        psFree(trend);
+        psFree(x);
+        psFree(y);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+
+    // Test pmTrend2DFit(), pmTrend2DEval(), pmTrend2DEvalVector() with acceptable input parameters
+    // NOTE: We only test with a very simple 2D polynomial fit.  This is appropriate since the
+    // polynomial testing routines are tested extensively elsewhere.
+    // XXX: Must test the PM_TREND_MAP case.
+    {
+        #define VEC_SIZE 9
+        psMemId id = psMemGetId();
+        psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+        pmTrend2D *trend = pmTrend2DAlloc(PM_TREND_POLY_ORD, img, 4, 4, stats);
+        ok(trend != NULL && psMemCheckTrend2D(trend), 
+          "pmTrend2DAlloc() returned non-NULL with acceptable input parameters");
+        psVector *x = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        psVector *y = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        psVector *f = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+        psVector *mask = psVectorAlloc(VEC_SIZE, PS_TYPE_U8);
+        psVector *df = psVectorAlloc(VEC_SIZE, PS_TYPE_F32);
+
+        int cnt = 0;
+        for (int i = 0 ; i < 3 ; i++) {
+            for (int j = 0 ; j < 3 ; j++) {
+                x->data.F32[cnt] = (float) i;
+                y->data.F32[cnt] = (float) j;
+                f->data.F32[cnt] = x->data.F32[cnt] * y->data.F32[cnt];
+                mask->data.U8[cnt] = 0;
+                df->data.F32[cnt] = 0.0;
+                cnt++;
+            }
+        }
+
+        bool rc = pmTrend2DFit(trend, mask, 0, x, y, f, NULL);
+        ok(rc == true, "pmTrend2DFit() returned TRUE with acceptable input parameters");
+
+        // Test pmTrend2DFit, pmTrend2DEval()
+        bool errorFlag = false;
+        for (int i = 0 ; i < VEC_SIZE ; i++) {
+            if (!TEST_FLOATS_EQUAL(pmTrend2DEval(trend, x->data.F32[i], y->data.F32[i]), f->data.F32[i])) {
+                diag("ERROR: at (%.2f %.2f), eval is %.2f, should be %.2f\n", x->data.F32[i], y->data.F32[i],
+                      pmTrend2DEval(trend, x->data.F32[i], y->data.F32[i]), f->data.F32[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "pmTrend2DFit() and pmTrend2DEval() set and evaluated the 2DTrend polynomial correctly");
+
+        // Test pmTrend2DEvalVector()
+        psVector *fTest = pmTrend2DEvalVector(trend, x, y);
+        errorFlag = false;
+        for (int i = 0 ; i < VEC_SIZE ; i++) {
+            if (!TEST_FLOATS_EQUAL(fTest->data.F32[i], f->data.F32[i])) {
+                diag("ERROR: at (%.2f %.2f), eval is %.2f, should be %.2f\n", 
+                      x->data.F32[i], y->data.F32[i], fTest->data.F32[i], f->data.F32[i]);
+                errorFlag = true;
+            }
+        }
+        ok(!errorFlag, "pmTrend2DFit() and pmTrend2DEval() set and evaluated the 2DTrend polynomial correctly");
+
+
+        psFree(img);
+        psFree(stats);
+        psFree(trend);
+        psFree(x);
+        psFree(y);
+        psFree(f);
+        psFree(fTest);
+        psFree(mask);
+        psFree(df);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/objects/tst_pmObjects01.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/objects/tst_pmObjects01.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/objects/tst_pmObjects01.c	(revision 22322)
@@ -0,0 +1,1300 @@
+/** @file tst_pmObjects.c
+ *
+ *  @brief Contains the tests for pmObjects.c:
+ *
+ * test00: This code will test the pmObjects routines.
+ *
+ *  @author GLG, MHPCC
+ *
+ * XXX: Must test
+ *       pmSourceRoughClass
+ *  many others...
+ *
+ *
+ * XXX: Must test output results for many other functions.
+ *
+ * XXX: There are many cases where row/col can be switched in the code.
+ * We must test that here by using non-square images.  All tests
+ * in this file should be run with non-square images.
+ *
+ * XXX: Memory leaks are not being caught.  If I allocated a psVector in these functions
+ * and never deallocate, no error is generated.
+ *
+ * XXX: Much of this file is commented out due to the API changes in rel 7.
+ *
+ *
+Fully Tested:
+    pmPeakAlloc()
+    pmMomentsAlloc()
+Weakly Tested:
+    pmSourceMoments()
+    most of psObjects.c is not tested
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-08-24 00:11:59 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include "psTest.h"
+#include "pslib.h"
+#include "pmObjects.h"
+#include "pmModelGroup.h"
+
+#define NUM_ROWS 10
+#define NUM_COLS 10
+#define ERROR_TOLERANCE 1.0
+#define ERROR_TOL 0.001
+static int test00(void);
+static int test01(void);
+static int test02(void);
+static int test03(void);
+static int test04(void);
+static int test05(void);
+//static int test06(void);
+//static int test07(void);
+//static int test08(void);
+//static int test09(void);
+//static int test15(void);
+//static int test16(void);
+//static int test20(void);
+testDescription tests[] = {
+                              {test00, 000, "pmObjects: structure allocators and deallocators", true, false},
+                              {test01, 001, "pmObjects: psFindVectorPeaks()", true, false},
+                              {test02, 001, "pmObjects: psFindImagePeaks()", true, false},
+                              {test03, 001, "pmObjects: pmCullPeaks()", true, false},
+                              {test04, 001, "pmObjects: pmSourceLocalSky()", true, false},
+                              {test05, 001, "pmObjects: pmSourceMoments()", true, false},
+                              //                              {test06, 001, "pmObjects: pmSourceSetPixelsCircle()", true, false},
+                              //                              {test07, 001, "pmObjects: pmMin()", true, false},
+                              //                              {test08, 001, "pmObjects: pmSourceModelGuess()", true, false},
+                              //{test09, 001, "pmObjects: pmSourceContour()", true, false},
+                              //{test15, 001, "pmObjects: pmSourceAddModel()", true, false},
+                              //{test16, 001, "pmObjects: pmSourceSubModel()", true, false},
+                              //{test20, 001, "pmObjects: pmSourceSubModel()", true, false},
+                              {NULL}
+                          };
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    //
+    // We include the function names here in psTraceSetLevel() commands for
+    // debugging convenience.  There is no guarantee that this list of functions
+    // is complete.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("pmPeakAlloc", 0);
+    psTraceSetLevel("pmMomentsAlloc", 0);
+    psTraceSetLevel("modelFree", 0);
+    psTraceSetLevel("pmModelAlloc", 0);
+    psTraceSetLevel("sourceFree", 0);
+    psTraceSetLevel("pmSourceAlloc", 0);
+    psTraceSetLevel("pmFindVectorPeaks", 0);
+    psTraceSetLevel("getRowVectorFromImage", 0);
+    psTraceSetLevel("myListAddPeak", 0);
+    psTraceSetLevel("pmFindImagePeaks", 0);
+    psTraceSetLevel("isItInThisRegion", 0);
+    psTraceSetLevel("pmCullPeaks", 0);
+    psTraceSetLevel("pmPeaksSubset", 0);
+    psTraceSetLevel("pmSourceLocalSky", 0);
+    psTraceSetLevel("checkRadius2", 0);
+    psTraceSetLevel("pmSourceMoments", 0);
+    psTraceSetLevel("pmComparePeakAscend", 0);
+    psTraceSetLevel("pmComparePeakDescend", 0);
+    psTraceSetLevel("pmSourcePSFClump", 0);
+    psTraceSetLevel("pmSourceRoughClass", 0);
+    psTraceSetLevel("pmSourceDefinePixels", 0);
+    psTraceSetLevel("pmSourceModelGuess", 0);
+    psTraceSetLevel("pmModelEval", 0);
+    psTraceSetLevel("findValue", 0);
+    psTraceSetLevel("pmSourceContour", 0);
+    psTraceSetLevel("pmSourceFitModel_v5", 0);
+    psTraceSetLevel("pmSourceFitModel", 0);
+    psTraceSetLevel("p_pmSourceAddOrSubModel", 0);
+    psTraceSetLevel("pmSourceAddModel", 0);
+    psTraceSetLevel("pmSourceSubModel", 0);
+
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+/******************************************************************************
+test00(): Test the various allocators and deallocators.
+ *****************************************************************************/
+int test00( void )
+{
+    bool testStatus = true;
+    psTraceSetLevel(".", 0);
+
+    printf("Testing pmPeakAlloc()...\n");
+    pmPeak *tmpPeak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+    if (tmpPeak == NULL) {
+        printf("TEST ERROR: pmPeakAlloc() returned a NULL pmPeak\n");
+        testStatus = false;
+    } else {
+        if (tmpPeak->x != 1) {
+            printf("TEST ERROR: pmPeakAlloc() improperly set pmPeak->x\n");
+            testStatus = false;
+        }
+        if (tmpPeak->y != 2) {
+            printf("TEST ERROR: pmPeakAlloc() improperly set pmPeak->y\n");
+            testStatus = false;
+        }
+        if (tmpPeak->counts != 3.0) {
+            printf("TEST ERROR: pmPeakAlloc() improperly set pmPeak->counts\n");
+            testStatus = false;
+        }
+        if (tmpPeak->class != PM_PEAK_LONE) {
+            printf("TEST ERROR: pmPeakAlloc() improperly set pmPeak->class\n");
+            testStatus = false;
+        }
+    }
+    psFree(tmpPeak);
+
+    printf("Testing pmMomentsAlloc()...\n");
+    pmMoments *tmpMoments = pmMomentsAlloc();
+    if (tmpMoments == NULL) {
+        printf("TEST ERROR: pmMomentsAlloc() returned a NULL pmMoments\n");
+        testStatus = false;
+    } else {
+        if ((fabs(tmpMoments->x-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->y-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Sx-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Sy-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Sxy-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Sum-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Peak-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Sky-0.0) > ERROR_TOL) ||
+                (tmpMoments->nPixels != 0)) {
+            printf("TEST ERROR: pmMomentsAlloc() did not properly initialize the pmMoments structure.\n");
+            printf("    tmpMoments->x is %f\n", tmpMoments->x);
+            printf("    tmpMoments->y is %f\n", tmpMoments->y);
+            printf("    tmpMoments->Sx is %f\n", tmpMoments->Sx);
+            printf("    tmpMoments->Sy is %f\n", tmpMoments->Sy);
+            printf("    tmpMoments->Sxy is %f\n", tmpMoments->Sxy);
+            printf("    tmpMoments->Sum is %f\n", tmpMoments->Sum);
+            printf("    tmpMoments->Peak is %f\n", tmpMoments->Peak);
+            printf("    tmpMoments->Sky is %f\n", tmpMoments->Sky);
+            printf("    tmp    Moments->nPixels is %d\n", tmpMoments->nPixels);
+            testStatus = false;
+        }
+    }
+    psFree(tmpMoments);
+
+
+    //
+    // Loop through each type of model
+    //
+    psS32 i = 0;
+    while (0 != pmModelClassParameterCount(i)) {
+        printf("Testing pmModelAlloc(%s)...\n", pmModelClassGetName(0));
+        pmModel *tmpModel = pmModelAlloc(i);
+        if (tmpModel == NULL) {
+            printf("TEST ERROR: pmModelAlloc(%s) returned a NULL pmModel\n", pmModelClassGetName(0));
+            testStatus = false;
+        } else {
+
+            /* XXX: Should we test that the members were set correctly?
+                        if ((tmpModel->params->n != 7) || (tmpModel->dparams->n != 7)) {
+                            printf("TEST ERROR: pmModelAlloc(PS_MODEL_GAUSS) allocated an incorrect number of params (%ld, %ld)\n",
+                                   tmpModel->params->n, tmpModel->dparams->n);
+                            testStatus = false;
+                        } else {
+                            for (psS32 i = 0 ; i < 7 ; i++) {
+                                if ((tmpModel->params->data.F32[i] != 0.0) ||
+                                        (tmpModel->dparams->data.F32[i] != 0.0)) {
+                                    printf("TEST ERROR: pmModelAlloc(PS_MODEL_GAUSS) did not ininitialize the params/dparams array to 0.0.\n");
+                                    testStatus = false;
+                                }
+                            }
+                        }
+            */
+        }
+        psFree(tmpModel);
+        i++;
+    }
+
+
+    pmSource *tmpSource = pmSourceAlloc();
+    if (tmpSource == NULL) {
+        printf("TEST ERROR: pmSourceAlloc() returned a NULL pmSource\n");
+        testStatus = false;
+    }
+    psFree(tmpSource);
+
+    return(testStatus);
+}
+
+/******************************************************************************
+test01(): we first test pmFindVectorPeaks() with a variety of bad input
+parameters.  Then we test it with a simple vector both 1- and multi-elements.
+ *****************************************************************************/
+#define TST01_VECTOR_LENGTH 10
+bool test_pmFindVectorPeaks(int n)
+{
+    bool testStatus = true;
+    psVector *inData = psVectorAlloc(n, PS_TYPE_F32);
+    inData->n = inData->nalloc;
+    psVector *outData = NULL;
+
+    printf("-------------- Calling test_pmFindVectorPeaks on an %d size vector. --------------\n", n);
+    //
+    // Test first pixel peak.
+    //
+    printf("Test pmFindVectorPeaks() with a first-element peak.\n");
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (n-i);
+    }
+    inData->data.F32[0] = (float) n;
+
+    outData= pmFindVectorPeaks(inData, 0.0);
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+        if (outData->n != 1) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        if (outData->data.U32[0] != 0) {
+            printf("TEST ERROR: Did not find peak at element 0.\n");
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+    //
+    // Test first pixel peak, large threshold
+    //
+    printf("Test pmFindVectorPeaks() with a first-element peak, large threshold.\n");
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (n-i);
+    }
+    inData->data.F32[0] = (float) n;
+
+    outData= pmFindVectorPeaks(inData, (float) (n*n));
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+
+        if (outData->n != 0) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        psFree(outData);
+
+        // Skip remaining tests if the input vector has length 1.
+        if (n == 1) {
+            psFree(inData);
+            return(testStatus);
+        }
+    }
+
+    //
+    // Test last pixel peak.
+    //
+    printf("Test pmFindVectorPeaks() with a last-element peak.\n");
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (i);
+    }
+    inData->data.F32[n-1] = (float) n;
+
+    outData= pmFindVectorPeaks(inData, 0.0);
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+
+        if (outData->n != 1) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        if (outData->data.U32[0] != n-1) {
+            printf("TEST ERROR: Did not find peak at element %d.\n", n-1);
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+    //
+    // Test last pixel peak, large threshold.
+    //
+    printf("Test pmFindVectorPeaks() with a last-element peak, large threshold.\n");
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (i);
+    }
+    inData->data.F32[n-1] = (float) n;
+
+    outData= pmFindVectorPeaks(inData, (float) (n*n));
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+
+        if (outData->n != 0) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+    //
+    // Test interior peaks.
+    // Set all even number elements to be peaks.
+    //
+    printf("Test pmFindVectorPeaks() with all even-numbered elements peak.\n");
+    for (psS32 i = 0 ; i < n ; i++) {
+        if (0 == i%2) {
+            inData->data.F32[i] = (float) (2 * i);
+        } else {
+            inData->data.F32[i] = (float) (i);
+        }
+    }
+    inData->data.F32[0] = (float) n;
+
+
+    outData= pmFindVectorPeaks(inData, 0.0);
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+
+        if (outData->n != n/2) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+
+        for (psS32 i = 0 ; i < outData->n ; i++) {
+            if (outData->data.U32[i] != (2 * i)) {
+                printf("TEST ERROR: the %d-th peak is element number %d\n", i, outData->data.U32[i]);
+                testStatus = false;
+            }
+        }
+        psFree(outData);
+    }
+
+    //
+    // Test interior peaks, with threshold = n*n.
+    // Should generate an empty output psVector.
+    //
+    printf("Test pmFindVectorPeaks() with all even-numbered elements peak, large threshold.\n");
+    outData= pmFindVectorPeaks(inData, (float) (n*n));
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+
+        if (outData->n != 0) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+    psFree(inData);
+    return(testStatus);
+}
+
+int test01( void )
+{
+    bool testStatus = true;
+    psVector *tmpVec = NULL;
+    psVector *tmpVecF64 = psVectorAlloc(TST01_VECTOR_LENGTH, PS_TYPE_F64);
+    psVector *tmpVecEmpty = psVectorAlloc(0, PS_TYPE_F32);
+    tmpVecF64->n = tmpVecF64->nalloc;
+    tmpVecEmpty->n = tmpVecEmpty->nalloc;
+
+    psTraceSetLevel(".", 0);
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindVectorPeaks with NULL psVector.  Should generate error and return NULL.\n");
+    tmpVec = pmFindVectorPeaks(NULL, 0.0);
+    if (tmpVec != NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks() returned a non-NULL psVector.\n");
+        testStatus = false;
+        psFree(tmpVec);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindVectorPeaks with empty psVector.  Should generate error and return NULL.\n");
+    tmpVec = pmFindVectorPeaks(tmpVecEmpty, 0.0);
+    if (tmpVec != NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks() returned a non-NULL psVector.\n");
+        testStatus = false;
+        psFree(tmpVec);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindVectorPeaks with PS_TYPE_F64 psVector.  Should generate error and return NULL.\n");
+    tmpVec = pmFindVectorPeaks(tmpVecF64, 0.0);
+    if (tmpVec != NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks() returned a non-NULL psVector.\n");
+        testStatus = false;
+        psFree(tmpVec);
+    }
+    testStatus&= test_pmFindVectorPeaks(1);
+    testStatus&= test_pmFindVectorPeaks(TST01_VECTOR_LENGTH);
+
+    psFree(tmpVecF64);
+    psFree(tmpVecEmpty);
+    return(testStatus);
+}
+
+/******************************************************************************
+test02():
+// XXX: Must test flat peaks.
+// XXX: test 1-by-n and n-by-1 images.
+ *****************************************************************************/
+#define TST02_NUM_ROWS 5
+#define TST02_NUM_COLS 5
+bool test_pmFindImagePeaks(int numRows, int numCols)
+{
+    printf("-------------- Calling test_pmFindImagePeaks on an %d-by-%d image. --------------\n", numRows, numCols);
+    //    if ((numRows < 4) || (numCols < 4)) {
+    //        printf("WARNING: Don't call this test with a smaller than 4-by-4 image.\n");
+    //        return(true);
+    //    }
+    bool testStatus = true;
+    psImage *inData = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psArray *outData = NULL;
+
+    //
+    // Initialize test image.
+    //
+    for (psS32 i = 0 ; i < numRows ; i++) {
+        for (psS32 j = 0 ; j < numCols ; j++) {
+            inData->data.F32[i][j] = PS_SQR(i - numRows/2) + PS_SQR(j-numCols/2);
+        }
+    }
+    //
+    // Set corner and center pixels as peaks.
+    //
+    inData->data.F32[0][0] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[0][numCols-1] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[numRows-1][0] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[numRows-1][numCols-1] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[numRows/2][numCols/2] = PS_SQR(numRows) + PS_SQR(numCols);
+
+    //
+    // Print image.
+    //
+    for (psS32 i = 0 ; i < numRows ; i++) {
+        for (psS32 j = 0 ; j < numCols ; j++) {
+            printf("(%.1f) ", inData->data.F32[i][j]);
+        }
+        printf("\n");
+    }
+
+    //
+    // Call pmFindImagePeaks() with a threshold of 0.0.
+    //
+    outData = pmFindImagePeaks(inData, 0.0);
+
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindImagePeaks returned a NULL psList.\n");
+        testStatus = false;
+    } else {
+        psS32 expectedNumPeaks;
+        if ((numRows == 1) && (numCols == 1)) {
+            expectedNumPeaks = 1;
+        } else if ((numRows == 1) || (numCols == 1)) {
+            expectedNumPeaks = 3;
+        } else {
+            expectedNumPeaks = 5;
+        }
+        if (outData->n != expectedNumPeaks) {
+            printf("TEST ERROR: pmFindImagePeaks found %ld peaks (should be %d)\n", outData->n, expectedNumPeaks);
+            testStatus = false;
+        }
+
+        // HEY: verify
+        for (psS32 i = 0 ; i < outData->n ; i++) {
+            pmPeak *tmpPeak = (pmPeak *) outData->data[i];
+            if (((tmpPeak->x == 0) && (tmpPeak->y == 0)) ||
+                    ((tmpPeak->x == 0) && (tmpPeak->y == numRows-1)) ||
+                    ((tmpPeak->x == numCols-1) && (tmpPeak->y == 0)) ||
+                    ((tmpPeak->x == numCols-1) && (tmpPeak->y == numRows-1))) {
+                if (!((tmpPeak->class & PM_PEAK_LONE) || (tmpPeak->class & PM_PEAK_EDGE))) {
+                    printf("TEST ERROR: (0) peak at (%d, %d) (%f) ->class set improperly (0x%x).",
+                           tmpPeak->y, tmpPeak->x, tmpPeak->counts, tmpPeak->class);
+                    printf(" should be (0x%x or 0x%x).\n", PM_PEAK_LONE, PM_PEAK_EDGE);
+                    testStatus = false;
+                }
+            } else if ((tmpPeak->x == numCols/2) && (tmpPeak->y == numRows/2)) {
+                if (tmpPeak->class != PM_PEAK_LONE) {
+                    printf("TEST ERROR: (1) peak at (%d, %d) (%f) ->class set improperly (0x%x).\n",
+                           tmpPeak->y, tmpPeak->x, tmpPeak->counts, tmpPeak->class);
+                    printf(" should be (0x%x).\n", PM_PEAK_LONE);
+                    testStatus = false;
+                }
+            } else {
+                printf("TEST ERROR: Peak at (%d, %d) (%f)\n", tmpPeak->y, tmpPeak->x, tmpPeak->counts);
+                testStatus = false;
+            }
+        }
+    }
+
+    psFree(inData);
+    psFree(outData);
+    return(testStatus);
+}
+
+int test02( void )
+{
+    bool testStatus = true;
+    psArray *tmpArray = NULL;
+    psImage *tmpImageF64 = psImageAlloc(TST02_NUM_ROWS, TST02_NUM_COLS, PS_TYPE_F64);
+    psImage *tmpImageEmpty = psImageAlloc(0, 0, PS_TYPE_F32);
+
+    psTraceSetLevel(".", 0);
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindImagePeaks with NULL psImage.  Should generate error and return NULL.\n");
+    tmpArray = pmFindImagePeaks(NULL, 0.0);
+    if (tmpArray != NULL) {
+        printf("TEST ERROR: pmFindImagePeaks() returned a non-NULL psImage.\n");
+        testStatus = false;
+        psFree(tmpArray);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindImagePeaks with empty psImage.  Should generate error and return NULL.\n");
+    tmpArray = pmFindImagePeaks(tmpImageEmpty, 0.0);
+    if (tmpArray != NULL) {
+        printf("TEST ERROR: pmFindImagePeaks() returned a non-NULL psImage.\n");
+        testStatus = false;
+        psFree(tmpArray);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindImagePeaks with PS_TYPE_F64 psImage.  Should generate error and return NULL.\n");
+    tmpArray = pmFindImagePeaks(tmpImageF64, 0.0);
+    if (tmpArray != NULL) {
+        printf("TEST ERROR: pmFindImagePeaks() returned a non-NULL psImage.\n");
+        testStatus = false;
+        psFree(tmpArray);
+    }
+    printf("----------------------------------------------------------------------------------\n");
+    //    testStatus&= test_pmFindImagePeaks(1, 1);
+    //    testStatus&= test_pmFindImagePeaks(2, 5);
+    //    testStatus&= test_pmFindImagePeaks(5, 2);
+    // HEY: add code for small images
+    //    testStatus&= test_pmFindImagePeaks(1, 1);
+    //    testStatus&= test_pmFindImagePeaks(1, 8);
+    //    testStatus&= test_pmFindImagePeaks(8, 1);
+    testStatus&= test_pmFindImagePeaks(TST02_NUM_ROWS,   TST02_NUM_COLS);
+    testStatus&= test_pmFindImagePeaks(2*TST02_NUM_ROWS, TST02_NUM_COLS);
+    testStatus&= test_pmFindImagePeaks(TST02_NUM_ROWS,   2*TST02_NUM_COLS);
+
+
+    psFree(tmpImageF64);
+    psFree(tmpImageEmpty);
+    return(testStatus);
+}
+
+/******************************************************************************
+test03(): We first test pmCullPeaks() with various NULL and unallowable input
+parameters.  Then we generate a list of peaks and test that pmCullPeaks()
+removes them correctly.
+ *****************************************************************************/
+int test03( void )
+{
+    bool testStatus = true;
+    psImage *imgData = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+    psArray *outData = NULL;
+
+    /* XXX: Modify for new pmCullPeaks()
+            printf("----------------------------------------------------------------------------------\n");
+            printf("Calling pmCullPeaks with NULL psList.  Should generate error and return NULL.\n");
+            outData = pmCullPeaks(NULL, 0.0, NULL);
+            if (outData != NULL) {
+                printf("TEST ERROR: pmCulPeaks() returned a non-NULL psList.\n");
+                testStatus = false;
+        }
+    */
+
+    //
+    // Set peaks in input image.  All even-column and even-row pixels are
+    // set non-zero, all other pixels are set to zero.
+    //
+    psS32 numPeaksOrig = 0;
+    for (psS32 i = 0 ; i < NUM_ROWS ; i++) {
+        for (psS32 j = 0 ; j < NUM_COLS ; j++) {
+            if ((0 == i%2) && (0 == j%2)) {
+                imgData->data.F32[i][j] = (float) (i + 10);
+                numPeaksOrig++;
+            } else {
+                imgData->data.F32[i][j] = 0.0;
+            }
+        }
+    }
+    for (psS32 i = 0 ; i < NUM_ROWS ; i++) {
+        for (psS32 j = 0 ; j < NUM_COLS ; j++) {
+            printf("(%.1f) ", imgData->data.F32[i][j]);
+        }
+        printf("\n");
+    }
+    printf("Set %d peaks\n", numPeaksOrig);
+
+    //
+    // Call pmCullPeaks() with HUGE maxValue and NULL psRegion.  Should not
+    // remove any peaks.
+    //
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmCullPeaks with large maxValue and NULL psRegion.\n");
+    outData = pmFindImagePeaks(imgData, 0.0);
+    /* XXX: Modify for new pmCullPeaks
+        outData = pmCullPeaks(outData, PS_MAX_F32, NULL);
+
+        if (outData == NULL) {
+            printf("TEST ERROR: pmCullPeaks() returned a non-NULL psList.\n");
+            testStatus = false;
+            return(testStatus);
+        }
+        if (outData->n != numPeaksOrig) {
+            printf("TEST ERROR (0): pmCullPeaks incorrectly removed peaks\n");
+            printf("The pmCullPeaks() output had %d peaks, should have had %d peaks.\n", outData->n, numPeaksOrig);
+            testStatus = false;
+        }
+    */
+    psFree(outData);
+
+    //
+    // Call pmCullPeaks() with TINY maxValue and NULL psRegion.  Should
+    // remove all peaks.
+    //
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmCullPeaks with tiny maxValue and NULL psRegion.\n");
+    outData = pmFindImagePeaks(imgData, 0.0);
+    printf("pmFindImagePeaks found %ld peaks\n", outData->n);
+    /* XXX: Modify for new pmCullPeaks
+        outData = pmCullPeaks(outData, 0.0, NULL);
+
+        if (outData == NULL) {
+            printf("TEST ERROR: pmCullPeaks() returned a non-NULL psList.\n");
+            testStatus = false;
+            return(testStatus);
+        }
+        if (outData->n != 0) {
+            printf("TEST ERROR (1): pmCullPeaks incorrectly removed peaks\n");
+            printf("The pmCullPeaks() output had %d peaks, should have had %d peaks.\n", outData->n, 0);
+            testStatus = false;
+        }
+        psFree(outData);
+    */
+
+    //
+    // Call pmCullPeaks() with HUGE maxValue and disjoint psRegion.  Should
+    // not remove any peaks.
+    //
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmCullPeaks with large maxValue and disjoint psRegion.\n");
+    outData = pmFindImagePeaks(imgData, 0.0);
+    printf("pmFindImagePeaks found %ld peaks\n", outData->n);
+    psRegion tmpRegion = psRegionSet(10000.0, 20000.0, 10000.0, 20000.0);
+
+    /* XXX: Modify for new pmCullPeaks
+        outData = pmCullPeaks(outData, PS_MAX_F32, tmpRegion);
+
+        if (outData == NULL) {
+            printf("TEST ERROR: pmCullPeaks() returned a non-NULL psList.\n");
+            testStatus = false;
+            return(testStatus);
+        }
+        if (outData->n != numPeaksOrig) {
+            printf("TEST ERROR (2): pmCullPeaks incorrectly removed peaks\n");
+            printf("The pmCullPeaks() output had %d peaks, should have had %d peaks.\n", outData->n, numPeaksOrig);
+            testStatus = false;
+        }
+    */
+    psFree(outData);
+
+    //
+    // Call pmCullPeaks() with HUGE maxValue and non-disjoint psRegion.  Should
+    // remove all peaks.
+    //
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmCullPeaks with large maxValue and non-disjoint psRegion.\n");
+    outData = pmFindImagePeaks(imgData, 0.0);
+    printf("pmFindImagePeaks found %ld peaks\n", outData->n);
+    tmpRegion = psRegionSet(-PS_MAX_F32, PS_MAX_F32, -PS_MAX_F32, PS_MAX_F32);
+    /* XXX: Modify for new pmCullPeaks
+        outData = pmCullPeaks(outData, PS_MAX_F32, tmpRegion);
+
+        if (outData == NULL) {
+            printf("TEST ERROR: pmCullPeaks() returned a non-NULL psList.\n");
+            testStatus = false;
+            return(testStatus);
+        }
+        if (outData->n != 0) {
+            printf("TEST ERROR (3): pmCullPeaks incorrectly removed peaks\n");
+            printf("The pmCullPeaks() output had %d peaks, should have had %d peaks.\n", outData->n, 0);
+            testStatus = false;
+        }
+    */
+    psFree(outData);
+
+    printf("----------------------------------------------------------------------------------\n");
+    psFree(imgData);
+    return(testStatus);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#define TST04_NUM_ROWS 100
+#define TST04_NUM_COLS 100
+#define TST04_SKY 20.0
+#define TST04_INNER_RADIUS 3
+#define TST04_OUTER_RADIUS 5
+/******************************************************************************
+test04(): We first test pmSourceLocalSky() with various NULL and unallowable
+input parameters.
+ 
+XXX: Should we produce tests with boundary numbers for the inner/outer radius?
+ 
+XXX: Call this with varying sizes for the image.
+ *****************************************************************************/
+int test04( void )
+{
+    bool testStatus = true;
+    psImage *imgData = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_F32);
+    psImageInit(imgData, TST04_SKY);
+    psImage *imgMask = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_U8);
+    psImageInit(imgMask, 0);
+    //    psImage *imgMaskS8 = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_S8);
+    //    psImageInit(imgMaskS8, 0);
+    psImage *imgDataF64 = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_F64);
+    psImageInit(imgDataF64, 0.0);
+    pmPeak *tmpPeak = pmPeakAlloc((psF32) (TST04_NUM_ROWS / 2),
+                                  (psF32) (TST04_NUM_COLS / 2),
+                                  200.0,
+                                  PM_PEAK_LONE);
+    pmSource *tmpSource = pmSourceAlloc();
+    tmpSource->pixels = imgData;
+    tmpSource->mask = imgMask;
+    tmpSource->peak = tmpPeak;
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceLocalSky with NULL tmpSource.  Should generate error and return FALSE.\n");
+    bool rc = pmSourceLocalSky(NULL, PS_STAT_SAMPLE_MEAN, 10.0);
+    if (rc != false) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a non-FALSE pmSource.\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceLocalSky with Radius<0.0.  Should generate error and return FALSE.\n");
+    rc = pmSourceLocalSky(tmpSource, PS_STAT_SAMPLE_MEAN, -10.0);
+    if (rc != false) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a non-FALSE pmSource.\n");
+        testStatus = false;
+    }
+
+    //
+    // XXX: The following code should be a separate function, and we should call it
+    // with a variety of image sizes, peaks.
+    //
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceLocalSky with valid data.\n");
+    tmpPeak->x = (psF32) (TST04_NUM_ROWS / 2);
+    tmpPeak->y = (psF32) (TST04_NUM_COLS / 2);
+    rc = pmSourceLocalSky(tmpSource, PS_STAT_SAMPLE_MEAN, 10.0);
+
+    if (rc == false) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a FALSE pmSource.\n");
+        testStatus = false;
+    } else {
+        if (tmpSource->peak == NULL) {
+            printf("TEST ERROR: pmSourceLocalSky() returned a NULL pmSource->peak.\n");
+            testStatus = false;
+        } else {
+            if (tmpSource->peak->x != tmpPeak->x) {
+                printf("TEST ERROR: pmSourceLocalSky() pmSource->peak->x was %d, should have been %d.\n",
+                       tmpSource->peak->x, tmpPeak->x);
+                testStatus = false;
+            }
+
+            if (tmpSource->peak->y != tmpPeak->y) {
+                printf("TEST ERROR: pmSourceLocalSky() pmSource->peak->y was %d, should have been %d.\n",
+                       tmpSource->peak->y, tmpPeak->y);
+                testStatus = false;
+            }
+
+            if (tmpSource->peak->counts != tmpPeak->counts) {
+                printf("TEST ERROR: pmSourceLocalSky() pmSource->peak->counts was %f, should have been %f.\n",
+                       tmpSource->peak->counts, tmpPeak->counts);
+                testStatus = false;
+            }
+
+            if (tmpSource->peak->class != tmpPeak->class) {
+                printf("TEST ERROR: pmSourceLocalSky() pmSource->peak->class was %d, should have been %d.\n",
+                       tmpSource->peak->class, tmpPeak->class);
+                testStatus = false;
+            }
+        }
+
+        if (tmpSource->moments == NULL) {
+            printf("TEST ERROR: pmSourceLocalSky() returned a NULL pmSource->moments.\n");
+            testStatus = false;
+        } else {
+            if (tmpSource->moments->Sky != TST04_SKY) {
+                printf("TEST ERROR: pmSourceLocalSky() pmSource->moments->Sky was %f, should have been %f.\n", tmpSource->moments->Sky, TST04_SKY);
+                testStatus = false;
+            }
+        }
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    psFree(tmpSource);
+    //    psFree(imgData);
+    //    psFree(imgDataF64);
+    //    psFree(imgMask);
+    //    psFree(imgMaskS8);
+    return(testStatus);
+}
+
+#define TST05_NUM_ROWS 100
+#define TST05_NUM_COLS 100
+#define TST05_SKY 20.0
+#define TST05_INNER_RADIUS 3
+#define TST05_OUTER_RADIUS 5
+/******************************************************************************
+test05(): We first test pmSourceMoments() with various NULL and unallowable
+input parameters.
+ 
+XXX: Should we produce tests with boundary numbers for the inner/outer radius?
+ 
+XXX: Call this with varying sizes for the image.
+ 
+XXX: The actual values of the moments are not tested.
+ *****************************************************************************/
+int test05( void )
+{
+    bool testStatus = true;
+    psImage *imgData = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_F32);
+    psImageInit(imgData, TST04_SKY);
+    psImage *imgMask = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_U8);
+    psImageInit(imgMask, 0);
+    pmPeak *tmpPeak = pmPeakAlloc((psF32) (TST04_NUM_ROWS / 2),
+                                  (psF32) (TST04_NUM_COLS / 2),
+                                  200.0,
+                                  PM_PEAK_LONE);
+    pmSource *tmpSource = pmSourceAlloc();
+    tmpSource->pixels = imgData;
+    tmpSource->mask = imgMask;
+    tmpSource->peak = tmpPeak;
+    psBool rc = pmSourceLocalSky(tmpSource, PS_STAT_SAMPLE_MEAN, 10.0);
+
+    if (rc == false) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a FALSE pmSource.\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceMoments with NULL pmSource.  Should generate error and return FALSE.\n");
+    rc = pmSourceMoments(NULL, 10.0);
+    if (rc != false) {
+        printf("TEST ERROR: pmSourceMoments() returned TRUE.\n");
+        testStatus = false;
+    }
+    // XXX: test with pmSource->peaks NULL
+    // XXX: test with pmSource->pixels NULL
+    // XXX: test with pmSource->mask NULL
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceMoments with radius < 0.0.  Should generate error and return FALSE.\n");
+    rc = pmSourceMoments(tmpSource, -10.0);
+    if (rc != false) {
+        printf("TEST ERROR: pmSourceMoments() returned TRUE.\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    psFree(tmpSource);
+    return(testStatus);
+
+}
+
+#define TST09_NUM_ROWS 70
+#define TST09_NUM_COLS 70
+#define TST09_SKY 5.0
+#define TST09_INNER_RADIUS 3
+#define TST09_OUTER_RADIUS 10
+#define LEVEL (TST09_SKY + 10.0)
+/******************************************************************************
+test09(): We first test pmSourceContour() with various NULL and unallowable
+input parameters.
+ 
+XXX: We don't verify the numbers.
+ *****************************************************************************/
+int test09( void )
+{
+    bool testStatus = true;
+    psImage *imgData = psImageAlloc(TST09_NUM_COLS, TST09_NUM_ROWS, PS_TYPE_F32);
+    psImageInit(imgData, TST09_SKY);
+    psImage *imgMask = psImageAlloc(TST09_NUM_COLS, TST09_NUM_ROWS, PS_TYPE_U8);
+    psImageInit(imgMask, 0);
+    pmPeak *tmpPeak = pmPeakAlloc((psF32) (TST09_NUM_ROWS / 2),
+                                  (psF32) (TST09_NUM_COLS / 2),
+                                  200.0,
+                                  PM_PEAK_LONE);
+    pmSource *tmpSource = pmSourceAlloc();
+    tmpSource->pixels = imgData;
+    tmpSource->mask = imgMask;
+    tmpSource->peak = tmpPeak;
+    psBool rc = pmSourceLocalSky(tmpSource, PS_STAT_SAMPLE_MEAN, 10.0);
+    if (rc == false) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a FALSE pmSource.\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceContour with NULL pmSource .  Should generate error, return FALSE.\n");
+    rc = pmSourceContour(NULL, imgData, LEVEL, PS_CONTOUR_CRUDE);
+    if (rc != false) {
+        printf("TEST ERROR: pmSourceContour() returned TRUE.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceContour with NULL psImage .  Should generate error, return FALSE.\n");
+    rc = pmSourceContour(tmpSource, NULL, LEVEL, PS_CONTOUR_CRUDE);
+    if (rc != FALSE) {
+        printf("TEST ERROR: pmSourceContour() returned TRUE.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+
+    //
+    // XXX: pmSourceContour() has a problem with contour tops/bottoms.
+    // Must correct this.
+    //
+    if (1) {
+        printf("----------------------------------------------------------------------------------\n");
+        printf("Calling pmSourceContour with acceptable data.\n");
+        printf("NOTE: must figure out the parameters for this test to be meaningful.\n");
+        tmpSource->modelPSF->params->data.F32[0] = TST09_SKY;
+        tmpSource->modelPSF->params->data.F32[1] = 15.0;
+        tmpSource->modelPSF->params->data.F32[2] = (psF32) (TST09_NUM_ROWS / 2);
+        tmpSource->modelPSF->params->data.F32[3] = (psF32) (TST09_NUM_COLS / 2);
+        tmpSource->modelPSF->params->data.F32[4] = 2.0;
+        tmpSource->modelPSF->params->data.F32[5] = 2.0;
+        tmpSource->modelPSF->params->data.F32[6] = 2.0;
+        rc = pmSourceContour(tmpSource, imgData, LEVEL, PS_CONTOUR_CRUDE);
+        if (rc == false) {
+            printf("TEST ERROR: pmSourceContour() returned FALSE.\n");
+            testStatus = false;
+        } else {
+            psFree(rc);
+        }
+    }
+
+    psFree(tmpSource);
+    return(testStatus);
+}
+
+#define TST15_NUM_ROWS 100
+#define TST15_NUM_COLS 100
+#define TST15_SKY 10.0
+#define TST15_INNER_RADIUS 3
+#define TST15_OUTER_RADIUS 5
+/******************************************************************************
+test15(): We first test pmSourceAddModel() with various NULL and unallowable
+input parameters.
+ 
+XXX: We don't verify the numbers.
+ *****************************************************************************/
+/*
+int test15( void )
+{
+    bool testStatus = true;
+    psImage *imgData = psImageAlloc(TST15_NUM_COLS, TST15_NUM_ROWS, PS_TYPE_F32);
+    psImageInit(imgData, TST15_SKY);
+    psImage *imgMask = psImageAlloc(TST15_NUM_COLS, TST15_NUM_ROWS, PS_TYPE_U8);
+    psImageInit(imgMask, 0);
+    pmPeak *tmpPeak = pmPeakAlloc((psF32) (TST15_NUM_ROWS / 2),
+                                  (psF32) (TST15_NUM_COLS / 2),
+                                  200.0,
+                                  PM_PEAK_LONE);
+    pmSource *tmpSource = pmSourceAlloc();
+    tmpSource->pixels = imgData;
+    tmpSource->mask = imgMask;
+    tmpSource->peak = tmpPeak;
+    psBool rc = pmSourceLocalSky(tmpSource, PS_STAT_SAMPLE_MEAN, 10.0);
+    if (rc == false) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a FALSE pmSource.\n");
+        testStatus = false;
+    }
+ 
+ 
+    tmpSource->modelPSF = pmModelAlloc(PS_MODEL_GAUSS);
+    tmpSource->modelPSF->params->data.F32[0] = 5.0;
+    tmpSource->modelPSF->params->data.F32[1] = 70.0;
+    tmpSource->modelPSF->params->data.F32[2] = (psF32) (TST15_NUM_ROWS / 2);
+    tmpSource->modelPSF->params->data.F32[3] = (psF32) (TST15_NUM_COLS / 2);
+    tmpSource->modelPSF->params->data.F32[4] = 1.0;
+    tmpSource->modelPSF->params->data.F32[5] = 1.0;
+    tmpSource->modelPSF->params->data.F32[6] = 2.0;
+ 
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceAddModel with NULL psImage.  Should generate error, return FALSE.\n");
+    rc = pmSourceAddModel(NULL, tmpSource, true);
+    if (rc == true) {
+        printf("TEST ERROR: pmSourceAddModel() returned TRUE.\n");
+        testStatus = false;
+    }
+ 
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceAddModel with NULL psSrc.  Should generate error, return FALSE.\n");
+    rc = pmSourceAddModel(imgData, NULL, true);
+    if (rc == true) {
+        printf("TEST ERROR: pmSourceAddModel() returned TRUE.\n");
+        testStatus = false;
+    }
+ 
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceAddModel with acceptable data.\n");
+    rc = pmSourceAddModel(imgData, tmpSource, true);
+    if (rc != true) {
+        printf("TEST ERROR: pmSourceAddModel() returned FALSE.\n");
+        testStatus = false;
+    }
+ 
+    psFree(tmpSource);
+    psFree(imgData);
+    return(testStatus);
+}
+*/
+
+#define TST16_NUM_ROWS 100
+#define TST16_NUM_COLS 100
+#define TST16_SKY 10.0
+#define TST16_INNER_RADIUS 3
+#define TST16_OUTER_RADIUS 5
+/******************************************************************************
+test16(): We first test pmSourceSubModel() with various NULL and unallowable
+input parameters.
+ 
+XXX: We don't verify the numbers.
+ *****************************************************************************/
+/*
+int test16( void )
+{
+    bool testStatus = true;
+    psImage *imgData = psImageAlloc(TST16_NUM_COLS, TST16_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < imgData->numRows; i++) {
+        for (psS32 j = 0 ; j < imgData->numCols; j++) {
+            imgData->data.F32[i][j] = TST16_SKY;
+        }
+    }
+    pmSource *tmpSource = NULL;
+    psBool rc = false;
+ 
+    pmPeak *tmpPeak = pmPeakAlloc((psF32) (TST16_NUM_ROWS / 2),
+                                  (psF32) (TST16_NUM_COLS / 2),
+                                  200.0,
+                                  PM_PEAK_LONE);
+ 
+    printf("Calling pmSourceLocalSky with valid data.\n");
+    tmpPeak->x = (psF32) (TST16_NUM_ROWS / 2);
+    tmpPeak->y = (psF32) (TST16_NUM_COLS / 2);
+    tmpSource = pmSourceLocalSky(imgData,
+                                 tmpPeak,
+                                 PS_STAT_SAMPLE_MEAN,
+                                 (psF32) TST16_INNER_RADIUS,
+                                 (psF32) TST16_OUTER_RADIUS);
+ 
+    if (tmpSource == NULL) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a NULL pmSource.\n");
+        testStatus = false;
+    }
+ 
+    tmpSource->modelPSF = pmModelAlloc(PS_MODEL_GAUSS);
+    tmpSource->modelPSF->params->data.F32[0] = 5.0;
+    tmpSource->modelPSF->params->data.F32[1] = 70.0;
+    tmpSource->modelPSF->params->data.F32[2] = (psF32) (TST16_NUM_ROWS / 2);
+    tmpSource->modelPSF->params->data.F32[3] = (psF32) (TST16_NUM_COLS / 2);
+    tmpSource->modelPSF->params->data.F32[4] = 1.0;
+    tmpSource->modelPSF->params->data.F32[5] = 1.0;
+    tmpSource->modelPSF->params->data.F32[6] = 2.0;
+ 
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceSubModel with NULL psImage.  Should generate error, return FALSE.\n");
+    rc = pmSourceSubModel(NULL, tmpSource, true);
+    if (rc == true) {
+        printf("TEST ERROR: pmSourceSubModel() returned TRUE.\n");
+        testStatus = false;
+    }
+ 
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceSubModel with NULL psSrc.  Should generate error, return FALSE.\n");
+    rc = pmSourceSubModel(imgData, NULL, true);
+    if (rc == true) {
+        printf("TEST ERROR: pmSourceSubModel() returned TRUE.\n");
+        testStatus = false;
+    }
+ 
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceSubModel with acceptable data.\n");
+    rc = pmSourceSubModel(imgData, tmpSource, true);
+    if (rc != true) {
+        printf("TEST ERROR: pmSourceSubModel() returned FALSE.\n");
+        testStatus = false;
+    }
+ 
+    psFree(tmpSource);
+    psFree(imgData);
+    return(testStatus);
+}
+*/
+
+#define TST20_NUM_ROWS 100
+#define TST20_NUM_COLS 100
+#define TST20_SKY 10.0
+#define TST20_INNER_RADIUS 3
+#define TST20_OUTER_RADIUS 5
+/******************************************************************************
+test20(): We first test pmSourceSubModel() with various NULL and unallowable
+input parameters.
+ 
+XXX: We don't verify the numbers.
+ *****************************************************************************/
+/*
+int test20( void )
+{
+    bool testStatus = true;
+    psImage *imgData = psImageAlloc(TST20_NUM_COLS, TST20_NUM_ROWS, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < imgData->numRows; i++) {
+        for (psS32 j = 0 ; j < imgData->numCols; j++) {
+            imgData->data.F32[i][j] = TST20_SKY;
+        }
+    }
+    pmSource *tmpSource = NULL;
+    psBool rc = false;
+ 
+    pmPeak *tmpPeak = pmPeakAlloc((psF32) (TST20_NUM_ROWS / 2),
+                                  (psF32) (TST20_NUM_COLS / 2),
+                                  200.0,
+                                  PM_PEAK_LONE);
+ 
+    printf("Calling pmSourceLocalSky with valid data.\n");
+    tmpPeak->x = (psF32) (TST20_NUM_ROWS / 2);
+    tmpPeak->y = (psF32) (TST20_NUM_COLS / 2);
+    tmpSource = pmSourceLocalSky(imgData,
+                                 tmpPeak,
+                                 PS_STAT_SAMPLE_MEAN,
+                                 (psF32) TST20_INNER_RADIUS,
+                                 (psF32) TST20_OUTER_RADIUS);
+ 
+    if (tmpSource == NULL) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a NULL pmSource.\n");
+        testStatus = false;
+    }
+ 
+    tmpSource->modelPSF = pmModelAlloc(PS_MODEL_GAUSS);
+ 
+ 
+    tmpSource->modelPSF->params->data.F32[0] = 5.0;
+    tmpSource->modelPSF->params->data.F32[1] = 70.0;
+    tmpSource->modelPSF->params->data.F32[2] = (psF32) (TST20_NUM_ROWS / 2);
+    tmpSource->modelPSF->params->data.F32[3] = (psF32) (TST20_NUM_COLS / 2);
+    tmpSource->modelPSF->params->data.F32[4] = 1.0;
+    tmpSource->modelPSF->params->data.F32[5] = 1.0;
+    tmpSource->modelPSF->params->data.F32[6] = 2.0;
+ 
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceFitModel with NULL psImage.  Should generate error, return FALSE.\n");
+    rc = pmSourceFitModel(tmpSource, NULL);
+    if (rc == true) {
+        printf("TEST ERROR: pmSourceFitModel() returned TRUE.\n");
+        testStatus = false;
+    }
+ 
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceFitModel with NULL pmSource.  Should generate error, return FALSE.\n");
+    rc = pmSourceFitModel(NULL, imgData);
+    if (rc == true) {
+        printf("TEST ERROR: pmSourceFitModel() returned TRUE.\n");
+        testStatus = false;
+    }
+ 
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceFitModel with acceptable data.\n");
+    rc = pmSourceFitModel(tmpSource, imgData);
+    printf("pmSourceFitModel returned %d\n", rc);
+ 
+    // XXX: Memory leaks are not being tested
+    psVector *junk = psVectorAlloc(10, PS_TYPE_F32);
+    junk->data.F32[0] = 0.0;
+ 
+    psFree(tmpSource);
+    psFree(imgData);
+    return(testStatus);
+}
+*/
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/pstap/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/pstap/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/pstap/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: /tags/sj_tags/sj_root_20080929/psModules/test/pstap/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/pstap/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/pstap/Makefile.am	(revision 22322)
@@ -0,0 +1,1 @@
+SUBDIRS = src
Index: /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+libpstap.la
+pstap.lo
+.deps
+.libs
Index: /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/Makefile.am	(revision 22322)
@@ -0,0 +1,18 @@
+
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSMODULES_CFLAGS)
+
+AM_LDFLAGS = \
+	$(top_builddir)/src/libpsmodules.la  \
+	$(top_builddir)/test/tap/src/libtap.la \
+	$(PSMODULES_LIBS)
+
+TEST_LTLIBS = libpstap.la
+libpstap_la_SOURCES = pstap.c
+noinst_HEADERS = pstap.h
+
+noinst_LTLIBRARIES = $(TEST_LTLIBS)
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/pstap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/pstap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/pstap.c	(revision 22322)
@@ -0,0 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <tap.h>
+#include <pslib.h>
Index: /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/pstap.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/pstap.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/pstap/src/pstap.h	(revision 22322)
@@ -0,0 +1,63 @@
+#include <pslib.h>
+
+#include "tap.h"
+
+#define done() ok(psMemCheckLeaks(0, NULL, stdout, false) == 0, "Memory Leaks"); return exit_status()
+
+        # define mem() ok(psMemCheckLeaks(psMemGetLastId(), NULL, stdout, false) == 0, "Memory Leaks")
+
+        # define checkLeaks false
+
+        # define checkMem() if(checkLeaks) mem()
+
+            # ifdef __GNUC__
+
+            // write a comment which is counted as a test (and swallowed by prove)
+            # define note(A, ...) _gen_result(1, __func__, __FILE__, __LINE__, A, ## __VA_ARGS__);
+
+// use to test the value of a float
+# define ok_float(VALUE,EXPECT,COMMENT, ...)\
+ok((fabsf((VALUE)-(EXPECT)) < FLT_EPSILON), COMMENT, ## __VA_ARGS__);
+
+// use to test the value of a double
+# define ok_double(VALUE,EXPECT,COMMENT, ...)\
+ok((fabs((VALUE)-(EXPECT)) < DBL_EPSILON), COMMENT, ## __VA_ARGS__);
+
+// use to test the value of a float within a defined tolerance
+# define ok_float_tol(VALUE,EXPECT,TOL,COMMENT, ...)\
+ok((fabsf((VALUE)-(EXPECT)) < (TOL)), COMMENT, ## __VA_ARGS__);
+
+// use to test the value of a double within a defined tolerance
+# define ok_double_tol(VALUE,EXPECT,TOL,COMMENT, ...)\
+ok((fabs((VALUE)-(EXPECT)) < (TOL)), COMMENT, ## __VA_ARGS__);
+
+# define ok_str(VALUE,EXPECT,COMMENT, ...)\
+ok(strcmp(VALUE, EXPECT) == 0, COMMENT, ## __VA_ARGS__);
+
+#elif __STDC_VERSION__ >= 199901L /* __GNUC__ */
+
+// write a comment which is counted as a test (and swallowed by prove)
+# define note(A, ...) _gen_result(1, __func__, __FILE__, __LINE__, A, ...);
+
+// use to test the value of a float
+# define ok_float(VALUE,EXPECT, ...)\
+ok((fabsf((VALUE)-(EXPECT)) < FLT_EPSILON), __VA_ARGS__);
+
+// use to test the value of a double
+# define ok_double(VALUE,EXPECT, ...)\
+ok((fabs((VALUE)-(EXPECT)) < DBL_EPSILON), __VA_ARGS__);
+
+// use to test the value of a float
+# define ok_float_tol(VALUE,EXPECT,TOL, ...)\
+ok((fabsf((VALUE)-(EXPECT)) < (TOL)), __VA_ARGS__);
+
+// use to test the value of a double
+# define ok_double_tol(VALUE,EXPECT,TOL, ...)\
+ok((fabs((VALUE)-(EXPECT)) < )(TOL)), __VA_ARGS__);
+
+# define ok_str(VALUE,EXPECT, ...)\
+ok(strcmp(VALUE, EXPECT) == 0, __VA_ARGS__);
+
+#else /* __STDC_VERSION__ */
+# error "Needs gcc or C99 compiler for variadic macros."
+#endif /* __STDC_VERSION__ */
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/.cvsignore	(revision 22322)
@@ -0,0 +1,13 @@
+.in
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.log
+config.status
+configure
+libtool
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/INSTALL	(revision 22322)
@@ -0,0 +1,8 @@
+Quick Installation
+
+    ./configure
+    make
+    make check
+    make install
+
+Run "configure --help" for additional options.
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/LICENSE
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/LICENSE	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/LICENSE	(revision 22322)
@@ -0,0 +1,23 @@
+Copyright (c) 2004 Nik Clayton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/Makefile.am	(revision 22322)
@@ -0,0 +1,5 @@
+SUBDIRS  = src
+#SUBDIRS += tests
+
+prove:
+	prove -v -r
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/README	(revision 22322)
@@ -0,0 +1,11 @@
+NAME
+     tap -- write tests that implement the Test Anything Protocol
+
+SYNOPSIS
+     #include <tap.h>
+
+DESCRIPTION
+     The tap library provides functions for writing test scripts that produce
+     output consistent with the Test Anything Protocol.  A test harness that
+     parses this protocol can run these tests and produce useful reports indi-
+     cating their success or failure.
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/bootstrap.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/bootstrap.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/bootstrap.sh	(revision 22322)
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+set -x
+aclocal19 -I /usr/local/share/aclocal || aclocal || exit 1
+autoheader259 || autoheader || exit 1
+libtoolize15 -c -f || libtoolize -c -f || glibtoolize -c -f || exit 1
+automake19 -a -c || automake -a -c || exit 1
+autoconf259 || autoconf || exit 1
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/compile
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/compile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/compile	(revision 22322)
@@ -0,0 +1,142 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand `-c -o'.
+
+scriptversion=2004-10-12.08
+
+# Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+case $1 in
+  '')
+     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand `-c -o'.
+Remove `-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file `INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit 0
+    ;;
+  -v | --v*)
+    echo "compile $scriptversion"
+    exit 0
+    ;;
+esac
+
+ofile=
+cfile=
+eat=
+
+for arg
+do
+  if test -n "$eat"; then
+    eat=
+  else
+    case $1 in
+      -o)
+	# configure might choose to run compile as `compile cc -o foo foo.c'.
+	# So we strip `-o arg' only if arg is an object.
+	eat=1
+	case $2 in
+	  *.o | *.obj)
+	    ofile=$2
+	    ;;
+	  *)
+	    set x "$@" -o "$2"
+	    shift
+	    ;;
+	esac
+	;;
+      *.c)
+	cfile=$1
+	set x "$@" "$1"
+	shift
+	;;
+      *)
+	set x "$@" "$1"
+	shift
+	;;
+    esac
+  fi
+  shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+  # If no `-o' option was seen then we might have been invoked from a
+  # pattern rule where we don't need one.  That is ok -- this is a
+  # normal compilation that the losing compiler can handle.  If no
+  # `.c' file was seen then we are probably linking.  That is also
+  # ok.
+  exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use `[/.-]' here to ensure that we don't use the same name
+# that we are using for the .o file.  Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d
+while true; do
+  if mkdir "$lockdir" >/dev/null 2>&1; then
+    break
+  fi
+  sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+  mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+  mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/configure.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/configure.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/configure.in	(revision 22322)
@@ -0,0 +1,44 @@
+AC_INIT(tap, 1.01)
+AC_CONFIG_SRCDIR(src/tap.c)
+AM_INIT_AUTOMAKE([foreign])
+AC_CONFIG_HEADERS([src/config.h])
+AC_GNU_SOURCE
+AC_PROG_CC
+AC_PROG_LIBTOOL
+AC_PROG_INSTALL
+
+# Checks for libraries
+case "$host" in
+	*-*-*freebsd4*)
+		LDFLAGS="$LDFLAGS -pthread"
+		HAVE_LIBPTHREAD=1
+		;;
+	*)
+		AC_CHECK_LIB(pthread, main)
+		;;
+esac
+
+dnl build tests at the same time as the source code
+AC_ARG_ENABLE(tests,
+  [AS_HELP_STRING(--enable-tests,build tests at same time as source)],
+  [AC_MSG_RESULT(test building enabled)
+   tests=true],
+   [tests=false])
+AM_CONDITIONAL(BUILD_TESTS, test x$tests = xtrue)
+
+# Checks for header files
+AC_HEADER_STDC
+AC_CHECK_HEADERS([stdlib.h])
+AC_CHECK_HEADERS([pthread.h])
+
+# Checks for  typedefs, structures, and compiler characteristics.
+AC_C_CONST
+
+# Checks for library functions.
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS([atexit])
+
+AC_CONFIG_FILES([Makefile
+		 src/Makefile
+		])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/.cvsignore	(revision 22322)
@@ -0,0 +1,13 @@
+.deps
+.libs
+Makefile
+Makefile.in
+libtap.la
+tap.lo
+config.h
+config.h.in
+stamp-h1
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/Makefile.am	(revision 22322)
@@ -0,0 +1,8 @@
+TEST_LTLIBS = libtap.la
+libtap_la_SOURCES = tap.c tap.h
+noinst_HEADERS = tap.h
+noinst_LTLIBRARIES = $(TEST_LTLIBS)
+
+#man_MANS = tap.3
+EXTRA_DIST = $(man_MANS)
+
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/tap.3
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/tap.3	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/tap.3	(revision 22322)
@@ -0,0 +1,380 @@
+.Dd December 20, 2004
+.Os
+.Dt TAP 3
+.Sh NAME
+.Nm tap
+.Nd write tests that implement the Test Anything Protocol
+.Sh SYNOPSIS
+.In tap.h
+.Sh DESCRIPTION
+The
+.Nm
+library provides functions for writing test scripts that produce output
+consistent with the Test Anything Protocol.  A test harness that parses
+this protocol can run these tests and produce useful reports indicating
+their success or failure.
+.Ss PRINTF STRINGS
+In the descriptions that follow, for any function that takes as the
+last two parameters
+.Dq Fa char * , Fa ...
+it can be assumed that the
+.Fa char *
+is a
+.Fn printf
+-like format string, and the optional arguments are values to be placed
+in that string.
+.Ss TEST PLANS
+.Bl -tag -width indent
+.It Xo
+.Ft int
+.Fn plan_tests "unsigned int"
+.Xc
+.It Xo
+.Ft int
+.Fn plan_no_plan "void"
+.Xc
+.It Xo
+.Ft int
+.Fn plan_skip_all "char *" "..."
+.Xc
+.El
+.Pp
+You must first specify a test plan.  This indicates how many tests you
+intend to run, and allows the test harness to notice if any tests were
+missed, or if the test program exited prematurely.
+.Pp
+To do this, use
+.Fn plan_tests ,
+which returns the number of planned tests.  The function will cause
+your program to exit prematurely if you specify 0 tests.
+.Pp
+In some situations you may not know how many tests you will be running, or
+you are developing your test program, and do not want to update the
+.Fn plan_tests
+parameter every time you make a change.  For those situations use
+.Fn plan_no_plan .
+It returns 1, and indicates to the test harness that an indeterminate number
+of tests will be run.
+.Pp
+Both
+.Fn plan_tests
+and
+.Fn plan_no_plan
+will cause your test program to exit prematurely with a diagnostic
+message if they are called more than once.
+.Pp
+If your test program detects at run time that some required functionality
+is missing (for example, it relies on a database connection which is not
+present, or a particular configuration option that has not been included
+in the running kernel) use
+.Fn plan_skip_all ,
+passing as parameters a string to display indicating the reason for skipping
+the tests.
+.Ss SIMPLE TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft unsigned int
+.Fn ok "expression" "char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn ok1 "expression"
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn pass "char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn fail "char *" "..."
+.Xc
+.El
+.Pp
+Tests are implemented as expressions checked by calls to the
+.Fn ok
+and
+.Fn ok1
+macros.  In both cases
+.Fa expression
+should evaluate to true if the test succeeded.
+.Pp
+.Fn ok
+allows you to specify a name, or comment, describing the test which will
+be included in the output.
+.Fn ok1
+is for those times when the expression to be tested is self
+explanatory and does not need an associated comment.  In those cases
+the test expression becomes the comment.
+.Pp
+These four calls are equivalent:
+.Bd -literal -offset indent
+int i = 5;
+
+ok(i == 5, "i equals 5");      /* Overly verbose */
+ok(i == 5, "i equals %d", i);  /* Just to demonstrate printf-like
+                                  behaviour of the test name */
+ok(i == 5, "i == 5");          /* Needless repetition */
+ok1(i == 5);                   /* Just right */
+.Ed
+.Pp
+It is good practice to ensure that the test name describes the meaning
+behind the test rather than what you are testing.  Viz
+.Bd -literal -offset indent
+ok(db != NULL, "db is not NULL");            /* Not bad, but */
+ok(db != NULL, "Database conn. succeeded");  /* this is better */
+.Ed
+.Pp
+.Fn ok
+and
+.Fn ok1
+return 1 if the expression evaluated to true, and 0 if it evaluated to
+false.  This lets you chain calls from
+.Fn ok
+to
+.Fn diag
+to only produce diagnostic output if the test failed.  For example, this
+code will include diagnostic information about why the database connection
+failed, but only if the test failed.
+.Bd -literal -offset indent
+ok(db != NULL, "Database conn. succeeded") ||
+    diag("Database error code: %d", dberrno);
+.Ed
+.Pp
+You also have
+.Fn pass
+and
+.Fn fail .
+From the Test::More documentation:
+.Bd -literal -offset indent
+Sometimes you just want to say that the tests have passed.
+Usually the case is you've got some complicated condition
+that is difficult to wedge into an ok().  In this case,
+you can simply use pass() (to declare the test ok) or fail
+(for not ok).
+
+Use these very, very, very sparingly.
+.Ed
+.Pp
+These are synonyms for ok(1, ...) and ok(0, ...).
+.Ss SKIPPING TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft int
+.Fn skip "unsigned int" "char *" "..."
+.Xc
+.It Xo
+.Fn skip_start "expression" "unsigned int" "char *" "..."
+.Xc
+.It Xo
+.Fn skip_end
+.Xc
+.El
+.Pp
+Sets of tests can be skipped.  Ordinarily you would do this because
+the test can't be run in this particular testing environment.
+.Pp
+For example, suppose some tests should be run as root.  If the test is
+not being run as root then the tests should be skipped.  In this 
+implementation, skipped tests are flagged as being ok, with a special
+message indicating that they were skipped.  It is your responsibility
+to ensure that the number of tests skipped (the first parameter to
+.Fn skip )
+is correct for the number of tests to skip.
+.Pp
+One way of implementing this is with a
+.Dq do { } while(0);
+loop, or an
+.Dq if( ) { } else { }
+construct, to ensure that there are no additional side effects from the
+skipped tests.
+.Bd -literal -offset indent
+if(getuid() != 0) {
+        skip(1, "because test only works as root");
+} else {
+        ok(do_something_as_root() == 0, "Did something as root");
+}
+.Ed
+.Pp
+Two macros are provided to assist with this.  The previous example could
+be re-written as follows.
+.Bd -literal -offset indent
+skip_start(getuid() != 0, 1, "because test only works as root");
+
+ok(do_something_as_root() == 0, "Did something as root");
+
+skip_end();
+.Ed
+.Ss MARKING TESTS AS Dq TODO
+.Bl -tag -width indent
+.It Xo
+.Ft void
+.Fn todo_start "char *" "..."
+.Xc
+.It Xo
+.Ft void
+.Fn todo_end "void"
+.Xc
+.El
+.Pp
+Sets of tests can be flagged as being
+.Dq TODO .
+These are tests that you expect to fail, probably because you haven't
+fixed a bug, or finished a new feature yet.  These tests will still be
+run, but with additional output that indicates that they are expected
+to fail.  Should a test start to succeed unexpectedly, tools like
+.Xr prove 1
+will indicate this, and you can move the test out of the todo
+block.  This is much more useful than simply commenting out (or
+.Dq #ifdef 0 ... #endif )
+the tests.
+.Bd -literal -offset indent
+todo_start("dwim() not returning true yet");
+
+ok(dwim(), "Did what the user wanted");
+
+todo_end();
+.Ed
+.Pp
+Should
+.Fn dwim
+ever start succeeding you will know about it as soon as you run the
+tests.  Note that
+.Em unlike
+the
+.Fn skip_*
+family, additional code between
+.Fn todo_start
+and
+.Fn todo_end
+.Em is
+executed.
+.Ss SKIP vs. TODO
+From the Test::More documentation;
+.Bd -literal -offset indent
+If it's something the user might not be able to do, use SKIP.
+This includes optional modules that aren't installed, running
+under an OS that doesn't have some feature (like fork() or
+symlinks), or maybe you need an Internet connection and one
+isn't available.
+
+If it's something the programmer hasn't done yet, use TODO.
+This is for any code you haven't written yet, or bugs you have
+yet to fix, but want to put tests in your testing script 
+(always a good idea).
+.Ed
+.Ss DIAGNOSTIC OUTPUT
+.Bl -tag -width indent
+.It Xo
+.Fr unsigned int
+.Fn diag "char *" "..."
+.Xc
+.El
+.Pp
+If your tests need to produce diagnostic output, use
+.Fn diag .
+It ensures that the output will not be considered by the TAP test harness.
+.Fn diag
+adds the necessary trailing
+.Dq \en
+for you.
+.Bd -literal -offset indent
+diag("Expected return code 0, got return code %d", rcode);
+.Ed
+.Pp
+.Fn diag
+always returns 0.
+.Ss EXIT STATUS
+.Bl -tag -width indent
+.It Xo
+.Fr int
+.Fn exit_status void
+.Xc
+.El
+.Pp
+For maximum compatability your test program should return a particular
+exit code.  This is calculated by
+.Fn exit_status
+so it is sufficient to always return from
+.Fn main
+with either
+.Dq return exit_status();
+or
+.Dq exit(exit_status());
+as appropriate.
+.Sh ENVIRONMENT
+The following environment variables affect
+.Nm .
+.Bl -tag -width indent
+.It Ev HARNESS_ACTIVE
+Causes an extra
+.Dq \en
+to be printed before any diagnostic failure output generated by
+.Nm .
+This variable is normally set if tests are being run under Perl's
+Test::Harness.
+.El
+.Sh EXAMPLES
+The
+.Pa tests
+directory in the source distribution contains numerous tests of
+.Nm
+functionality, written using
+.Nm .
+Examine them for examples of how to construct test suites.
+.Sh COMPATABILITY
+.Nm
+strives to be compatible with the Perl Test::More and Test::Harness 
+modules.  The test suite verifies that
+.Nm
+is bug-for-bug compatible with their behaviour.  This is why some
+functions which would more naturally return nothing return constant
+values.
+.Pp
+If the
+.Lb libpthread
+is found at compile time,
+.Nm
+.Em should
+be thread safe.  Indications to the contrary (and test cases that expose
+incorrect behaviour) are very welcome.
+.Sh SEE ALSO
+.Xr Test::More 1 ,
+.Xr Test::Harness 1 ,
+.Xr prove 1
+.Sh STANDARDS
+.Nm
+requires a
+.St -isoC-99
+compiler.  Some of the
+.Nm
+functionality is implemented as variadic macros, and that functionality
+was not formally codified until C99.  Patches to use
+.Nm
+with earlier compilers that have their own implementation of variadic
+macros will be gratefully received.
+.Sh HISTORY
+.Nm
+was written to help improve the quality and coverage of the FreeBSD
+regression test suite, and released in the hope that others find it
+a useful tool to help improve the quality of their code.
+.Sh AUTHORS
+.An "Nik Clayton" Aq nik@ngo.org.uk ,
+.Aq nik@FreeBSD.org
+.Pp
+.Nm
+would not exist without the efforts of
+.An "Michael G Schwern" Aq schqern@pobox.com ,
+.An "Andy Lester" Aq andy@petdance.com ,
+and the countless others who have worked on the Perl QA programme.
+.Sh BUGS
+Ideally, running the tests would have no side effects on the behaviour
+of the application you are testing.  However, it is not always possible
+to avoid them.  The following side effects of using
+.Nm
+are known.
+.Bl -bullet -offset indent
+.It
+stdout is set to unbuffered mode after calling any of the
+.Fn plan_*
+functions.
+.El
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/tap.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/tap.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/tap.c	(revision 22322)
@@ -0,0 +1,430 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap.h"
+
+static int no_plan = 0;
+static int skip_all = 0;
+static int have_plan = 0;
+static unsigned int test_count = 0; /* Number of tests that have been run */
+static unsigned int e_tests = 0; /* Expected number of tests to run */
+static unsigned int failures = 0; /* Number of tests that failed */
+static char *todo_msg = NULL;
+static char *todo_msg_fixed = "libtap malloc issue";
+static int todo = 0;
+static int test_died = 0;
+
+/* Encapsulate the pthread code in a conditional.  In the absence of
+   libpthread the code does nothing */
+#ifdef HAVE_LIBPTHREAD
+#include <pthread.h>
+static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK pthread_mutex_lock(&M);
+# define UNLOCK pthread_mutex_unlock(&M);
+#else
+# define LOCK
+# define UNLOCK
+#endif
+
+static void _expected_tests(unsigned int);
+static void _tap_init(void);
+static void _cleanup(void);
+
+/*
+ * Generate a test result.
+ *
+ * ok -- boolean, indicates whether or not the test passed.
+ * test_name -- the name of the test, may be NULL
+ * test_comment -- a comment to print afterwards, may be NULL
+ */
+unsigned int
+_gen_result(int ok, const char *func, char *file, unsigned int line,
+            char *test_name, ...)
+{
+    va_list ap;
+    char *local_test_name = NULL;
+    char *c;
+    int name_is_digits;
+
+    LOCK;
+
+    test_count++;
+
+    /* Start by taking the test name and performing any printf()
+       expansions on it */
+    if(test_name != NULL) {
+        va_start(ap, test_name);
+        vasprintf(&local_test_name, test_name, ap);
+        va_end(ap);
+
+        /* Make sure the test name contains more than digits
+           and spaces.  Emit an error message and exit if it
+           does */
+        if(local_test_name) {
+            name_is_digits = 1;
+            for(c = local_test_name; *c != '\0'; c++) {
+                if(!isdigit(*c) && !isspace(*c)) {
+                    name_is_digits = 0;
+                    break;
+                }
+            }
+
+            if(name_is_digits) {
+                diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
+                diag("    Very confusing.");
+            }
+        }
+    }
+
+    if(!ok) {
+        printf("not ");
+        failures++;
+    }
+
+    printf("ok %d", test_count);
+
+    if(test_name != NULL) {
+        printf(" - ");
+
+        /* Print the test name, escaping any '#' characters it
+           might contain */
+        if(local_test_name != NULL) {
+            flockfile(stdout);
+            for(c = local_test_name; *c != '\0'; c++) {
+                if(*c == '#')
+                    fputc('\\', stdout);
+                fputc((int)*c, stdout);
+            }
+            funlockfile(stdout);
+        } else { /* vasprintf() failed, use a fixed message */
+            printf("%s", todo_msg_fixed);
+        }
+    }
+
+    /* If we're in a todo_start() block then flag the test as being
+       TODO.  todo_msg should contain the message to print at this
+       point.  If it's NULL then asprintf() failed, and we should
+       use the fixed message.
+
+       This is not counted as a failure, so decrement the counter if
+       the test failed. */
+    if(todo) {
+        printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
+        if(!ok)
+            failures--;
+    }
+
+    printf("\n");
+
+    if(!ok) {
+        if(getenv("HARNESS_ACTIVE") != NULL)
+            fputs("\n", stderr);
+
+        diag("    Failed %stest (%s:%s() at line %d)",
+             todo ? "(TODO) " : "", file, func, line);
+    }
+    free(local_test_name);
+
+    UNLOCK;
+
+    /* We only care (when testing) that ok is positive, but here we
+       specifically only want to return 1 or 0 */
+    return ok ? 1 : 0;
+}
+
+/*
+ * Initialise the TAP library.  Will only do so once, however many times it's
+ * called.
+ */
+void
+_tap_init(void)
+{
+    static int run_once = 0;
+
+    if(!run_once) {
+        atexit(_cleanup);
+
+        /* stdout needs to be unbuffered so that the output appears
+           in the same place relative to stderr output as it does 
+           with Test::Harness */
+        setbuf(stdout, 0);
+        run_once = 1;
+    }
+}
+
+/*
+ * Note that there's no plan.
+ */
+int
+plan_no_plan(void)
+{
+
+    LOCK;
+
+    _tap_init();
+
+    if(have_plan != 0) {
+        fprintf(stderr, "You tried to plan twice!\n");
+        test_died = 1;
+        UNLOCK;
+        exit(255);
+    }
+
+    have_plan = 1;
+    no_plan = 1;
+
+    UNLOCK;
+
+    return 1;
+}
+
+/*
+ * Note that the plan is to skip all tests
+ */
+int
+plan_skip_all(char *reason)
+{
+
+    LOCK;
+
+    _tap_init();
+
+    skip_all = 1;
+
+    printf("1..0");
+
+    if(reason != NULL)
+        printf(" # Skip %s", reason);
+
+    printf("\n");
+
+    UNLOCK;
+
+    exit(0);
+}
+
+/*
+ * Note the number of tests that will be run.
+ */
+int
+plan_tests(unsigned int tests)
+{
+
+    LOCK;
+
+    _tap_init();
+
+    if(have_plan != 0) {
+        fprintf(stderr, "You tried to plan twice!\n");
+        test_died = 1;
+        UNLOCK;
+        exit(255);
+    }
+
+    if(tests == 0) {
+        fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
+        test_died = 1;
+        UNLOCK;
+        exit(255);
+    }
+
+    have_plan = 1;
+
+    _expected_tests(tests);
+
+    UNLOCK;
+
+    return e_tests;
+}
+
+unsigned int
+diag(char *fmt, ...)
+{
+    va_list ap;
+
+    fputs("# ", stderr);
+
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+
+    fputs("\n", stderr);
+
+    return 0;
+}
+
+void
+_expected_tests(unsigned int tests)
+{
+
+    printf("1..%d\n", tests);
+    e_tests = tests;
+}
+
+int
+skip(unsigned int n, char *fmt, ...)
+{
+    va_list ap;
+    char *skip_msg;
+
+    LOCK;
+
+    va_start(ap, fmt);
+    asprintf(&skip_msg, fmt, ap);
+    va_end(ap);
+
+    while(n-- > 0) {
+        test_count++;
+        printf("ok %d # skip %s\n", test_count,
+               skip_msg != NULL ?
+               skip_msg : "libtap():malloc() failed");
+    }
+
+    free(skip_msg);
+
+    UNLOCK;
+
+    return 1;
+}
+
+void
+todo_start(char *fmt, ...)
+{
+    va_list ap;
+
+    LOCK;
+
+    va_start(ap, fmt);
+    vasprintf(&todo_msg, fmt, ap);
+    va_end(ap);
+
+    todo = 1;
+
+    UNLOCK;
+}
+
+void
+todo_end(void)
+{
+
+    LOCK;
+
+    todo = 0;
+    free(todo_msg);
+
+    UNLOCK;
+}
+
+int
+exit_status(void)
+{
+    int r;
+
+    LOCK;
+
+    /* If there's no plan, just return the number of failures */
+    if(no_plan || !have_plan) {
+        UNLOCK;
+        return failures;
+    }
+
+    /* Ran too many tests?  Return the number of tests that were run
+       that shouldn't have been */
+    if(e_tests < test_count) {
+        r = test_count - e_tests;
+        UNLOCK;
+        return r;
+    }
+
+    /* Return the number of tests that failed + the number of tests
+       that weren't run */
+    r = failures + e_tests - test_count;
+    UNLOCK;
+
+    return r;
+}
+
+/*
+ * Cleanup at the end of the run, produce any final output that might be
+ * required.
+ */
+void
+_cleanup(void)
+{
+
+    LOCK;
+
+    /* If plan_no_plan() wasn't called, and we don't have a plan,
+       and we're not skipping everything, then something happened
+       before we could produce any output */
+    if(!no_plan && !have_plan && !skip_all) {
+        diag("Looks like your test died before it could output anything.");
+        UNLOCK;
+        return;
+    }
+
+    if(test_died) {
+        diag("Looks like your test died just after %d.", test_count);
+        UNLOCK;
+        return;
+    }
+
+
+    /* No plan provided, but now we know how many tests were run, and can
+       print the header at the end */
+    if(!skip_all && (no_plan || !have_plan)) {
+        printf("1..%d\n", test_count);
+    }
+
+    if((have_plan && !no_plan) && e_tests < test_count) {
+        diag("Looks like you planned %d %s but ran %d extra.",
+             e_tests, e_tests == 1 ? "test" : "tests", test_count - e_tests);
+        UNLOCK;
+        return;
+    }
+
+    if((have_plan || !no_plan) && e_tests > test_count) {
+        diag("Looks like you planned %d %s but only ran %d.",
+             e_tests, e_tests == 1 ? "test" : "tests", test_count);
+        UNLOCK;
+        return;
+    }
+
+    if(failures)
+        diag("Looks like you failed %d %s of %d.",
+             failures, failures == 1 ? "test" : "tests", test_count);
+
+    UNLOCK;
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/tap.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/tap.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/src/tap.h	(revision 22322)
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* '## __VA_ARGS__' is a gcc'ism. C99 doesn't allow the token pasting
+   and requires the caller to add the final comma if they've ommitted
+   the optional arguments */
+#ifdef __GNUC__
+# define ok(e, test, ...) ((e) ?     \
+                           _gen_result(1, __func__, __FILE__, __LINE__, \
+                                       test, ## __VA_ARGS__) :  \
+                           _gen_result(0, __func__, __FILE__, __LINE__, \
+                                       test, ## __VA_ARGS__))
+
+# define ok1(e) ((e) ?       \
+                 _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+                 _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(test, ...) ok(1, test, ## __VA_ARGS__);
+# define fail(test, ...) ok(0, test, ## __VA_ARGS__);
+
+# define skip_start(test, n, fmt, ...)   \
+do {      \
+    if((test)) {    \
+        skip(n, fmt, ## __VA_ARGS__); \
+        continue;   \
+    }
+    #elif __STDC_VERSION__ >= 199901L /* __GNUC__ */
+    # define ok(e, ...) ((e) ?      \
+                         _gen_result(1, __func__, __FILE__, __LINE__, \
+                                     __VA_ARGS__) :    \
+                         _gen_result(0, __func__, __FILE__, __LINE__, \
+                                     __VA_ARGS__))
+
+    # define ok1(e) ((e) ?       \
+                     _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+                     _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+    # define pass(...) ok(1, __VA_ARGS__);
+    # define fail(...) ok(0, __VA_ARGS__);
+
+    # define skip_start(test, n, ...)   \
+    do {      \
+        if((test)) {    \
+            skip(n,  __VA_ARGS__);  \
+            continue;   \
+        }
+        #else /* __STDC_VERSION__ */
+        # error "Needs gcc or C99 compiler for variadic macros."
+        #endif /* __STDC_VERSION__ */
+
+        #define skip_end() } while(0);
+
+    unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...);
+
+    int plan_no_plan(void);
+    int plan_skip_all(char *);
+    int plan_tests(unsigned int);
+
+    unsigned int diag(char *, ...);
+
+    int skip(unsigned int, char *, ...);
+
+    void todo_start(char *, ...);
+    void todo_end(void);
+
+    int exit_status(void);
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/Makefile.am	(revision 22322)
@@ -0,0 +1,7 @@
+SUBDIRS=	diag
+SUBDIRS+=	fail
+SUBDIRS+=	ok
+SUBDIRS+=	pass
+SUBDIRS+=	plan
+SUBDIRS+=	skip
+SUBDIRS+=	todo
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/README	(revision 22322)
@@ -0,0 +1,12 @@
+Most of the tests follow the same pattern.
+
+ * test.pl that uses Test::More, and demonstrates whatever functionality 
+   that we're trying to test.  This is the reference code.
+
+ * test.c, which tests the libtap reimplementation of the same functionality.
+
+ * test.t, which compiles the .c program, runs both test scripts, and then 
+   diffs their output to make sure it's identical.
+
+   Right now, test.t is identical in every directory.  This sucks somewhat.
+   It should either be a symlink to a common script
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    plan_tests(2);
+
+    rc = diag("A diagnostic message");
+    diag("Returned: %d", rc);
+
+    /* Make sure the failure is passed through */
+    ok(1, "test 1") || diag("ok() failed, and shouldn't");
+    ok(0, "test 2") || diag("ok() passed, and shouldn't");
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/test.pl	(revision 22322)
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+plan tests => 2;
+
+$rc = diag("A diagnostic message");
+diag("Returned: $rc");
+
+ok(1, 'test 1') or diag "ok() failed, and shouldn't";
+ok(0, 'test 2') or diag "ok() passed, and shouldn't";
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/diag/test.t	(revision 22322)
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+echo '1..7'
+
+perl $srcdir/test.pl 2>/dev/null >test.pl.out
+perlstatus=$?
+
+./test > test.c.out 2>&1
+cstatus=$?
+
+if grep "^# A diagnostic message$" test.c.out > /dev/null ; then
+    echo "ok 1 - found a diagnostic message"
+else
+    echo "not ok 1 - found a diagnostic message"
+    retval=1
+fi
+
+if grep "^# Returned: 0$" test.c.out > /dev/null ; then
+    echo "ok 2 - diag() expected return value" 
+else
+    echo "not ok 2 - diag() expected return value" 
+    retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 43)$" test.c.out > /dev/null ; then
+    echo "ok 3 - 'failed test at line' diag" 
+else
+    echo "not ok 3 - 'failed test at line' diag" 
+    retval=1
+fi
+
+if grep "^# ok() passed, and shouldn't$" test.c.out > /dev/null ; then
+    echo "ok 4 - expected diag"
+else
+    echo "ok 4 - expected diag"
+    retval=1
+fi
+
+if grep "^# Looks like you failed 1 test of 2.$" test.c.out > /dev/null ; then
+    echo "ok 5 - failed 1 test"
+ else
+    echo "ok 5 - failed 1 test"
+    retval=1
+fi
+
+sed -e '/^#/D' test.c.out > tmp
+mv tmp test.c.out
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 6 - TAP output is identical'
+else
+	retval=1
+	echo 'not ok 6 - TAP output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 7 - status code'
+else
+	retval=1
+	echo 'not ok 7 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/.cvsignore	(revision 22322)
@@ -0,0 +1,15 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_tests(2);
+    diag("Returned: %d", rc);
+
+    rc = fail("test to fail");
+    diag("Returned: %d", rc);
+
+    rc = fail("test to fail %s", "with extra string");
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 2;
+diag("Returned: " . sprintf('%d', $rc));
+
+$rc = fail('test to fail');
+diag("Returned: $rc");
+
+$rc = fail('test to fail with extra string');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/fail/test.t	(revision 22322)
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+echo '1..8'
+
+perl $srcdir/test.pl 2>/dev/null > test.pl.out
+perlstatus=$?
+
+./test > test.c.out 2>&1 
+cstatus=$?
+
+if grep "^# Returned: 2$" test.c.out >/dev/null ; then
+  echo "ok 1 - expected return value";
+else
+  echo "not ok 1 - expected return value";
+  retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 39)$" test.c.out >/dev/null ; then
+  echo "ok 2 - failed expected test";
+else
+  echo "not ok 2 - failed expected test";
+  retval=1
+fi
+
+if grep "^# Returned: 0$" test.c.out >/dev/null ; then
+  echo "ok 3 - expected return value";
+else
+  echo "not ok 3 - expected return value";
+  retval=1
+fi
+
+if grep "^#     Failed test (.*test.c:main() at line 42)$" test.c.out >/dev/null ; then
+  echo "ok 4 - failed expected test";
+else
+  echo "not ok 4 - failed expected test";
+  retval=1
+fi
+  
+if grep "^# Returned: 0$" test.c.out >/dev/null ; then
+  echo "ok 5 - expected return value";
+else
+  echo "not ok 5 - expected return value";
+  retval=1
+fi
+
+if grep "^# Looks like you failed 2 tests of 2.$" test.c.out >/dev/null ; then
+  echo "ok 6 - expected return value";
+else
+  echo "not ok 6 - expected return value";
+  retval=1
+fi
+
+sed -e '/^#/D' test.c.out > tmp
+mv tmp test.c.out
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 7 - output is identical'
+else
+	retval=1
+	echo 'not ok 7 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 8 - status code'
+else
+	retval=1
+	echo 'not ok 8 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS  =	ok
+SUBDIRS +=	ok-hash
+SUBDIRS +=	ok-numeric
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/test.c	(revision 22322)
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_tests(4);
+    diag("Returned: %d", rc);
+
+    rc = ok(1, "Test with no hash");
+    diag("Returned: %d", rc);
+
+    rc = ok(1, "Test with one # hash");
+    diag("Returned: %d", rc);
+
+    rc = ok(1, "Test with # two # hashes");
+    diag("Returned: %d", rc);
+
+    rc = ok(1, "Test with ## back to back hashes");
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/test.pl	(revision 22322)
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 4;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1, 'Test with no hash');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with one # hash');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with # two # hashes');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Test with ## back to back hashes');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-hash/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_tests(3);
+    diag("Returned: %d", rc);
+
+    rc = ok(1, "First test");
+    diag("Returned: %d", rc);
+
+    rc = ok(1, "1");
+    diag("Returned: %d", rc);
+
+    rc = ok(1, "Third test");
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/test.pl	(revision 22322)
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 3;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1, 'First test');
+diag("Returned: $rc");
+
+$rc = ok(1, '1');
+diag("Returned: $rc");
+
+$rc = ok(1, 'Third test');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok-numeric/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/test.c	(revision 22322)
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_tests(5);
+    diag("Returned: %d", rc);
+
+    rc = ok(1 == 1, "1 equals 1");
+    diag("Returned: %d", rc);
+
+    rc = ok(1 == 1, "1 equals %d", 1);
+    diag("Returned: %d", rc);
+
+    rc = ok1(1 == 1);
+    diag("Returned: %d", rc);
+
+    rc = ok(1 == 2, "1 equals 2");
+    diag("Returned: %d", rc);
+
+    rc = ok1(1 == 2);
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/test.pl	(revision 22322)
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 1, '1 equals 1'); # Used for %d testing in test.c
+diag("Returned: $rc");
+
+$rc = ok(1 == 1, '1 == 1');	# Test ok1() passes when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 2, '1 equals 2');	# Test ok() fails when it should
+diag("Returned: $rc");
+
+$rc = ok(1 == 2, '1 == 2');	# Test ok1() fails when it should
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/ok/ok/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_tests(2);
+    diag("Returned: %d", rc);
+
+    rc = pass("test to pass");
+    diag("Returned: %d", rc);
+
+    rc = pass("test to pass %s", "with extra string");
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 2;
+diag("Returned: " . sprintf('%d', $rc));
+
+$rc = pass('test to pass');
+diag("Returned: $rc");
+
+$rc = pass('test to pass with extra string');
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/pass/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/.cvsignore	(revision 22322)
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/Makefile.am	(revision 22322)
@@ -0,0 +1,7 @@
+SUBDIRS  =	no-tests
+SUBDIRS +=	no_plan
+SUBDIRS +=	not-enough-tests
+SUBDIRS +=	too-many-plans
+SUBDIRS +=	too-many-tests
+SUBDIRS +=	sane
+SUBDIRS +=	skip_all
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_tests(0);
+    diag("Returned: %d", rc);
+
+    rc = ok(1, NULL);
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 0;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no-tests/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_no_plan();
+    diag("Returned: %d", rc);
+
+    rc = ok(1, NULL);
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+my $rc = 0;
+
+use Test::More;
+
+$rc = plan qw(no_plan);
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/no_plan/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_tests(1);
+    diag("Returned: %d", rc);
+
+    rc = ok(1, NULL);
+    diag("Returned: %d", rc);
+
+    rc = ok(1, NULL);
+    diag("Returned: %d", rc);
+
+    rc = ok(1, NULL);
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/test.pl	(revision 22322)
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/not-enough-tests/test.t	(revision 22322)
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	# comment this out until we're exit-code compatible with Test::More
+	#retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/test.c	(revision 22322)
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_tests(1);
+    diag("Returned: %d", rc);
+
+    rc = ok(1, NULL);
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/test.pl	(revision 22322)
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/sane/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/plan.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/plan.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/plan.c	(revision 22322)
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap.h"
+
+/* Run pre-defined tests on the test library to make sure that the basic
+   functionality works, and it can be used to test itself afterwards */
+
+int
+main(int argc, char *argv[])
+{
+    plan_skip_all("No good reason");
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/test.c	(revision 22322)
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_skip_all("No good reason");
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/test.pl	(revision 22322)
@@ -0,0 +1,11 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan skip_all => "No good reason";
+diag("Returned: " . sprintf("%d", $rc));
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/skip_all/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/test.c	(revision 22322)
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_tests(1);
+    diag("Returned: %d", rc);
+
+    rc = ok(1, NULL);
+    diag("Returned: %d", rc);
+
+    rc = plan_tests(1);
+    diag("Returned: %d", rc);
+
+    rc = ok(0, NULL);
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/test.pl	(revision 22322)
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 1;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = plan tests => 1;
+diag("Returned: $rc");
+
+$rc = ok(0);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-plans/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/test.c	(revision 22322)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+
+    rc = plan_tests(5);
+    diag("Returned: %d", rc);
+
+    rc = ok(1, NULL);
+    diag("Returned: %d", rc);
+
+    rc = ok(0, NULL);
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/test.pl	(revision 22322)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+$rc = ok(1);
+diag("Returned: $rc");
+
+$rc = ok(0);
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/plan/too-many-tests/test.t	(revision 22322)
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+    # we're not exit-status compatible with Test::More yet
+	#retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/test.c	(revision 22322)
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+    unsigned int side_effect = 0;
+
+    rc = plan_tests(4);
+    diag("Returned: %d", rc);
+
+    rc = ok(1 == 1, "1 equals 1"); /* Should always work */
+    diag("Returned: %d", rc);
+
+    do {
+        if(1) {
+            rc = skip(1, "Testing skipping");
+            continue;
+        }
+
+        side_effect++;
+
+        ok(side_effect == 1, "side_effect checked out");
+
+    } while(0);
+
+    diag("Returned: %d", rc);
+
+    skip_start(1 == 1, 1, "Testing skipping #2");
+
+    side_effect++;
+    rc = ok(side_effect == 1, "side_effect checked out");
+    diag("Returned: %d", rc);
+
+    skip_end();
+
+    rc = ok(side_effect == 0, "side_effect is %d", side_effect);
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/test.pl	(revision 22322)
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 4;
+diag("Returned: " . sprintf("%d", $rc));
+
+my $side_effect = 0;		# Check whether skipping has side effects
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+# Start skipping
+SKIP: {
+	$rc = skip "Testing skipping", 1;
+
+	$side_effect++;
+
+	$rc = ok($side_effect == 1, '$side_effect checked out');
+}
+
+diag("Returned: $rc");
+
+SKIP: {
+	$rc = skip "Testing skipping #2", 1;
+	diag("Returned: $rc");
+
+	$side_effect++;
+
+	$rc = ok($side_effect == 1, '$side_effect checked out');
+	diag("Returned: $rc");
+}
+
+$rc = ok($side_effect == 0, "side_effect is $side_effect");
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/skip/test.t	(revision 22322)
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+.deps
+Makefile
+Makefile.in
+.libs
+test
+test.c.out
+test.pl.out
+*.bb
+*.bbg
+*.da
+gmon.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/Makefile.am	(revision 22322)
@@ -0,0 +1,13 @@
+
+TESTS = 		test.t
+TESTS_ENVIRONMENT =	$(SHELL)
+
+EXTRA_DIST = 		$(TESTS) test.pl
+
+check_PROGRAMS = 	test
+
+test_CFLAGS = 		-g -I$(top_srcdir)/src
+test_LDFLAGS = 		-L$(top_builddir)/src
+test_LDADD = 		-ltap
+
+CLEANFILES =	test.o test.c.out test.pl.out
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/test.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/test.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/test.c	(revision 22322)
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "tap.h"
+
+int
+main(int argc, char *argv[])
+{
+    unsigned int rc = 0;
+    unsigned int side_effect = 0;
+
+    rc = plan_tests(5);
+    diag("Returned: %d", rc);
+
+    rc = ok(1 == 1, "1 equals 1"); /* Should always work */
+    diag("Returned: %d", rc);
+
+    todo_start("For testing purposes");
+
+    side_effect++;
+
+    /* This test should fail */
+    rc = ok(side_effect == 0, "side_effect checked out");
+    diag("Returned: %d", rc);
+
+    /* This test should unexpectedly succeed */
+    rc = ok(side_effect == 1, "side_effect checked out");
+    diag("Returned: %d", rc);
+
+    todo_end();
+
+    todo_start("Testing printf() %s in todo_start()", "expansion");
+
+    rc = ok(0, "dummy test");
+    diag("Returned: %d", rc);
+
+    todo_end();
+
+    rc = ok(side_effect == 1, "side_effect is %d", side_effect);
+    diag("Returned: %d", rc);
+
+    return exit_status();
+}
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/test.pl	(revision 22322)
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More;
+
+my $rc = 0;
+
+$rc = plan tests => 5;
+diag("Returned: " . sprintf("%d", $rc));
+
+my $side_effect = 0;		# Check whether TODO has side effects
+
+$rc = ok(1 == 1, '1 equals 1');	# Test ok() passes when it should
+diag("Returned: $rc");
+
+# Start TODO tests
+TODO: {
+	local $TODO = 'For testing purposes';
+
+	$side_effect++;
+
+	# This test should fail
+	$rc = ok($side_effect == 0, 'side_effect checked out');
+	diag("Returned: $rc");
+
+	# This test should unexpectedly succeed
+	$rc = ok($side_effect == 1, 'side_effect checked out');
+	diag("Returned: $rc");
+}
+
+TODO: {
+	local $TODO = 'Testing printf() expansion in todo_start()';
+
+	$rc = ok(0, 'dummy test');
+	diag("Returned: $rc");
+}
+
+$rc = ok($side_effect == 1, "side_effect is $side_effect");
+diag("Returned: $rc");
Index: /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/test.t
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/test.t	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/tap/tests/todo/test.t	(revision 22322)
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+echo '1..2'
+
+perl $srcdir/test.pl 2> /dev/null > test.pl.out
+perlstatus=$?
+
+# Test:;More prints diagnostic from TODO tests on stdout
+# http://rt.cpan.org/Ticket/Display.html?id=14982
+sed '/^#/D' test.pl.out > tmp
+mv tmp test.pl.out
+
+./test 2> /dev/null > test.c.out
+cstatus=$?
+
+diff -u test.pl.out test.c.out
+
+if [ $? -eq 0 ]; then
+	echo 'ok 1 - output is identical'
+else
+	retval=1
+	echo 'not ok 1 - output is identical'
+fi
+
+if [ $perlstatus -eq $cstatus ]; then
+	echo 'ok 2 - status code'
+else
+	retval=1
+	echo 'not ok 2 - status code'
+	echo "# perlstatus = $perlstatus"
+	echo "#    cstatus = $cstatus"
+fi
+
+exit $retval
Index: /tags/sj_tags/sj_root_20080929/psModules/test/test.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psModules/test/test.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psModules/test/test.pl	(revision 22322)
@@ -0,0 +1,31 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2006  Joshua Hoblitt
+#
+# $Id: test.pl,v 1.1 2006-09-23 02:47:58 magnier Exp $
+
+use strict;
+use warnings FATAL => qw( all);
+
+use vars qw($VERSION);
+$VERSION = '0.01';
+
+use File::Find::Rule;
+use Cwd;
+
+my $rule = File::Find::Rule->new;
+# ignore .lib directories
+$rule->or($rule->new
+        ->directory
+        ->name('.libs')
+        ->prune
+        ->discard,
+        $rule->new
+    );
+$rule->name(qr/^tap_[^.]*$/)
+        ->maxdepth(2)
+        ->relative;
+
+my @test_files = $rule->in(getcwd());
+
+system("prove @test_files");
Index: /tags/sj_tags/sj_root_20080929/psastro/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/.cvsignore	(revision 22322)
@@ -0,0 +1,18 @@
+bin
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+missing
+config.guess
+libtool
+ltmain.sh
+stamp-h1
+config.sub
+psastro.pc
Index: /tags/sj_tags/sj_root_20080929/psastro/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/Makefile.am	(revision 22322)
@@ -0,0 +1,10 @@
+SUBDIRS = src
+
+CLEANFILES = *.pyc *~ core core.*
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= psastro.pc
+
+EXTRA_DIST = \
+	psastro.pc.in \
+	autogen.sh
Index: /tags/sj_tags/sj_root_20080929/psastro/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=psastro
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE  failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/psastro/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/configure.ac	(revision 22322)
@@ -0,0 +1,204 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.61)
+
+AC_INIT([psastro], [0.9.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+dnl ------------------------------------------------------------
+
+AC_PATH_PROG([ERRORCODES], [psParseErrorCodes], [missing])
+if test "$ERRORCODES" = "missing" ; then
+  AC_MSG_ERROR([psParseErrorCodes is required])
+fi
+
+dnl ------------------ kapa,libkapa options -------------------------
+dnl -- libkapa implies the requirement for libpng, libjpeg as well --
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+dnl test for command-line options: use ohana-config if not supplied
+KAPA_CFLAGS_CONFIG="true"
+KAPA_LIBS_CONFIG="true"
+AC_ARG_WITH(kapa,
+[AS_HELP_STRING(--with-kapa=DIR,Specify location of libkapa)],
+[KAPA_CFLAGS="-I$withval/include" KAPA_LIBS="-L$withval/lib" 
+ KAPA_CFLAGS_CONFIG="false"       KAPA_LIBS_CONFIG="false"])
+AC_ARG_WITH(kapa-include,
+[AS_HELP_STRING(--with-kapa-include=DIR,Specify libkapa include directory.)],
+[KAPA_CFLAGS="-I$withval" KAPA_CFLAGS_CONFIG="false"])
+AC_ARG_WITH(kapa-lib,
+[AS_HELP_STRING(--with-kapa-lib=DIR,Specify libkapa library directory.)],
+[KAPA_LIBS="-L$withval" KAPA_LIBS_CONFIG="false"])
+
+echo "KAPA_CFLAGS_CONFIG: $KAPA_CFLAGS_CONFIG"
+echo "KAPA_LIBS_CONFIG: $KAPA_LIBS_CONFIG"
+echo "KAPA_CFLAGS: $KAPA_CFLAGS"
+echo "KAPA_LIBS: $KAPA_LIBS"
+
+dnl HAVE_KAPA is set to false if any of the tests fail
+HAVE_KAPA="true"
+AC_MSG_NOTICE([checking for libkapa])
+if test "$KAPA_CFLAGS_CONFIG" = "true" -o "$KAPA_LIBS_CONFIG" = "true"; then
+  AC_MSG_NOTICE([kapa info supplied by ohana-config])
+  KAPA_CONFIG=`which ohana-config`
+  AC_CHECK_FILE($KAPA_CONFIG,[],
+    [HAVE_KAPA="false"; AC_MSG_WARN([libkapa is not found: output plots disabled.  Obtain libkapa at http://kiawe.ifa.hawaii.edu/Elixir/Ohana or use --with-kapa to specify location])])
+  
+  echo "HAVE_KAPA: $HAVE_KAPA"
+  echo "KAPA_CFLAGS_CONFIG: $KAPA_CFLAGS_CONFIG"
+
+  if test "$HAVE_KAPA" = "true" -a "$KAPA_CFLAGS_CONFIG" = "true" ; then
+   AC_MSG_NOTICE([libkapa cflags info supplied by ohana-config])
+   AC_MSG_CHECKING([libkapa cflags])
+   KAPA_CFLAGS="`${KAPA_CONFIG} --cflags`"
+   AC_MSG_RESULT([${KAPA_CFLAGS}])
+  fi
+
+  if test "$HAVE_KAPA" = "true" -a "$KAPA_LIBS_CONFIG" = "true" ; then
+   AC_MSG_NOTICE([libkapa ldflags info supplied by ohana-config])
+   AC_MSG_CHECKING([libkapa ldflags])
+   KAPA_LIBS="`${KAPA_CONFIG} --libs` -lX11"
+   AC_MSG_RESULT([${KAPA_LIBS}])
+  fi
+fi
+
+if test "$HAVE_KAPA" = "true" ; then
+ AC_MSG_NOTICE([libkapa supplied])
+ PSASTRO_CFLAGS="${PSASTRO_CFLAGS} ${KAPA_CFLAGS}"
+ PSASTRO_LIBS="${PSASTRO_LIBS} ${KAPA_LIBS}"
+else
+ AC_MSG_NOTICE([libkapa ignored])
+fi
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ libjpeg options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(jpeg,
+[AS_HELP_STRING(--with-jpeg=DIR,Specify location of libjpeg.)],
+[JPEG_CFLAGS="-I$withval/include"
+ JPEG_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(jpeg-include,
+[AS_HELP_STRING(--with-jpeg-include=DIR,Specify libjpeg include directory.)],
+[JPEG_CFLAGS="-I$withval"])
+AC_ARG_WITH(jpeg-lib,
+[AS_HELP_STRING(--with-jpeg-lib=DIR,Specify libjpeg library directory.)],
+[JPEG_LDFLAGS="-L$withval"])
+
+CFLAGS="${CFLAGS} ${JPEG_CFLAGS}"
+CPPFLAGS=${CFLAGS}
+LDFLAGS="${LDFLAGS} ${JPEG_LDFLAGS}"
+
+AC_CHECK_HEADERS([jpeglib.h],
+  [PSASTRO_CFLAGS="$PSASTRO_CFLAGS $JPEG_CFLAGS" AC_SUBST(JPEG_CFLAGS)],
+  [HAVE_KAPA=false; AC_MSG_WARN([libjpeg headers not found: output plots disabled.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
+)
+
+AC_CHECK_LIB(jpeg,jpeg_CreateCompress,
+  [PSASTRO_LIBS="$PSASTRO_LIBS $JPEG_LDFLAGS -ljpeg"],
+  [HAVE_KAPA=false; AC_MSG_WARN([libjpeg library not found: output plots disabled.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
+)
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ libpng options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(png,
+[AS_HELP_STRING(--with-png=DIR,Specify location of libpng.)],
+[PNG_CFLAGS="-I$withval/include"
+ PNG_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(png-include,
+[AS_HELP_STRING(--with-png-include=DIR,Specify libpng include directory.)],
+[PNG_CFLAGS="-I$withval"])
+AC_ARG_WITH(png-lib,
+[AS_HELP_STRING(--with-png-lib=DIR,Specify libpng library directory.)],
+[PNG_LDFLAGS="-L$withval"])
+
+CFLAGS="${CFLAGS} ${PNG_CFLAGS}"
+CPPFLAGS=${CFLAGS}
+LDFLAGS="${LDFLAGS} ${PNG_LDFLAGS}"
+
+AC_CHECK_HEADERS([png.h],
+  [PSASTRO_CFLAGS="$PSASTRO_CFLAGS $PNG_CFLAGS" AC_SUBST(PNG_CFLAGS)],
+  [HAVE_KAPA=false; AC_MSG_WARN([libpng headers not found: output plots disabled.  Obtain libpng from http://www.ijg.org/ or use --with-png to specify location.])]
+)
+
+AC_CHECK_LIB(png,png_init_io,
+  [PSASTRO_LIBS="$PSASTRO_LIBS $PNG_LDFLAGS -lpng"],
+  [HAVE_KAPA=false; AC_MSG_WARN([libpng library not found: output plots disabled.  Obtain libpng from http://www.ijg.org/ or use --with-png to specify location.])]
+)
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ use kapa or not? ---------------------
+
+if test "$HAVE_KAPA" == "true" ; then
+  AC_MSG_RESULT([including plotting functions])
+  AC_DEFINE([HAVE_KAPA],[1],[enable use of libkapa])
+else
+  AC_MSG_RESULT([skipping plotting functions])
+  AC_DEFINE([HAVE_KAPA],[0],[disable use of libkapa])
+fi
+
+dnl ------------- psLib, psModules ---------------
+PKG_CHECK_MODULES([PSLIB],    [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+PKG_CHECK_MODULES([PPSTATS],  [ppStats >= 1.0.0]) 
+
+dnl Set CFLAGS for build
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+echo "PSASTRO_CFLAGS: $PSASTRO_CFLAGS"
+echo "PSASTRO_LIBS: $PSASTRO_LIBS"
+
+AC_SUBST([PSASTRO_CFLAGS])
+AC_SUBST([PSASTRO_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  psastro.pc
+])
+
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/psastro/doc/mktest.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/doc/mktest.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/doc/mktest.txt	(revision 22322)
@@ -0,0 +1,31 @@
+
+the program, psastro-mktest, will generate a set of fake test data for
+psastro.  the config file 'psastro-mktest.conf' gives an example setup
+for this program.  It generates 4 output files:
+
+- cmpfile : this is a basic elixir-style cmp file (stars plus header)
+  which can be taken as input to psastro
+
+- catfile : this is a basic text reference catalog, which the current
+  psastro loads as a reference
+
+- rawfile : this is a text listing of all astrometry stages for the
+  generated stars: readout, cell, chip, fpa, tpa, sky, with the last
+  two columns being the magnitudes.  this file is generated by
+  applying the transformations starting at the readout and going
+  step-by-step to the sky.
+
+- reffile : this is the inverse of the preceeding file, with the
+  resulting sources transformed from the sky coordinates down
+  step-by-step to the readout pixels.  If the transformations are
+  functioning, these two files (rawfile & reffile) should be identical
+  within floating point errors.
+
+examples:
+
+build a fake dataset
+# psastro-mktest doc/psastro-mktest.conf test.cmp test.cat test.raw test.ref
+
+run psastrom on the fake data:
+# psastro doc/psastro.conf test.cmp test.dat
+
Index: /tags/sj_tags/sj_root_20080929/psastro/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/doc/notes.txt	(revision 22322)
@@ -0,0 +1,39 @@
+
+2006.12.26
+
+ work still to be done for psastro:
+
+ - test suite
+   - create a DVO database from a fake population
+   - create fake images with range of errors drawn from DVO
+
+ - 
+
+
+we have a few different astrometry circumstances for our complete
+image/chip hierarchy structure:
+
+- ChipAstrom with one readout, one cell per chip:
+
+  whether or not there is more than one chip, we need to have two
+  stages
+  
+    - in the first stage, the per-chip results are applied to the
+      chip.toFPA parameter set.  
+
+    - in the second stage, the chip results are collectively used to
+      modify the fpa.toSky terms.
+
+      - the average offsets of the chip positions relative to starting
+	coordinates are used to calculate a single average offset to
+	the boresite. 
+
+      - the average rotations of the chips relative to their starting
+        rotations are used to calculate a single average rotation of
+        the boresite
+
+
+- ChipAstrom with more than one readout and/or cell:
+
+  - match all stars up relative to first readout?
+   
Index: /tags/sj_tags/sj_root_20080929/psastro/doc/psastro-mktest.conf
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/doc/psastro-mktest.conf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/doc/psastro-mktest.conf	(revision 22322)
@@ -0,0 +1,28 @@
+
+NX            S32  2000    	 # chip dimensions
+NY            S32  2000    	 # chip dimensions
+
+NSTARS        S32  20    	 # number of fake stars to generate
+
+# the current test output file only has the basic WCS info.  
+READOUT.COL.0    F32  0		 # cell coord for readout (0,0)
+READOUT.ROW.0    F32  0		 # cell coord for readout (0,0)
+READOUT.COL.BIN  F32  1		 # readout col binning factor
+READOUT.ROW.BIN  F32  1		 # readout row binning factor
+
+CELL.COL.0    	 F32  0		 # chip coord for cell (0,0)
+CELL.ROW.0    	 F32  0		 # chip coord for cell (0,0)
+
+CHIP.COL.0    	 F32  0		 # FPA coord for chip (0,0)
+CHIP.ROW.0    	 F32  0		 # FPA coord for chip (0,0)
+CHIP.THETA    	 F32  0	         # cw rotation from chip to FPA
+CHIP.PARITY.X 	 F32  1		 # x flip 
+CHIP.PARITY.Y 	 F32  1		 # y flip 
+
+FPA.THETA     	 F32  0		 # fpa row offset
+FPA.COL.0  	 F32  0		 # TPA coord for FPA (0,0)
+FPA.ROW.0  	 F32  0		 # TPA coord for FPA (0,0)
+
+PLATE.SCALE	 F32  1	         # plate scale in arcsec/pixel
+RA		 F32  10	 # celestial coord for TPA (0,0)
+DEC     	 F32  20	 # celestial coord for TPA (0,0)
Index: /tags/sj_tags/sj_root_20080929/psastro/doc/psastro.conf
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/doc/psastro.conf	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/doc/psastro.conf	(revision 22322)
@@ -0,0 +1,17 @@
+
+PSASTRO.MATCH.RADIUS 	F32 2.0    # fitting radius in pixels
+
+PSASTRO.CHIP.NX      	S32 1      # chip-to-fpa tranformation order
+PSASTRO.CHIP.NY	     	S32 1      # chip-to-fpa tranformation order
+
+PSASTRO.CHIP.NSIGMA  	F32 3.0    # Nsigma clip for matched-fit 
+PSASTRO.CHIP.NITER   	S32 3      # number of clipping interations for matched-fit 
+
+PSASTRO.GRID.OFFSET  	F32 100.0  # maximum allowed offset for grid match
+PSASTRO.GRID.SCALE   	F32  50.0  # binning scale for grid match
+
+PSASTRO.GRID.MIN.ANGLE  F32   0.0  # starting angle
+PSASTRO.GRID.MAX.ANGLE  F32   0.0  # ending angle
+PSASTRO.GRID.DEL.ANGLE  F32   0.5  # steps between angle attempts
+
+PSASTRO.STARS.MAX       S32 300    # use no more than this number of sources
Index: /tags/sj_tags/sj_root_20080929/psastro/psastro.pc.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/psastro.pc.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/psastro.pc.in	(revision 22322)
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libpsastro
+Description: Pan-STARRS Photometry Library
+Version: @VERSION@
+Requires: pslib psmodules
+Libs: -L${libdir} -lpsastro
+Libs.private: @PSASTRO_LIBS@
+Cflags: -I${includedir} @PSASTRO_CFLAGS@
Index: /tags/sj_tags/sj_root_20080929/psastro/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/.cvsignore	(revision 22322)
@@ -0,0 +1,17 @@
+*.o
+*.lo
+.libs
+.deps
+Makefile
+Makefile.in
+psastro
+psastro.loT
+psastroErrorCodes.h
+psastroErrorCodes.c
+libpsastro.la
+config.h
+config.h.in
+stamp-h1
+psastroModel
+gpcModel
+psastroModelFit
Index: /tags/sj_tags/sj_root_20080929/psastro/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/Makefile.am	(revision 22322)
@@ -0,0 +1,109 @@
+
+lib_LTLIBRARIES = libpsastro.la
+libpsastro_la_CFLAGS = $(PSASTRO_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+libpsastro_la_LDFLAGS = $(PSASTRO_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+
+bin_PROGRAMS = psastro 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
+
+psastroModel_CFLAGS = $(PSASTRO_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+psastroModel_LDFLAGS = $(PSASTRO_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+psastroModel_LDADD = libpsastro.la
+
+psastroModelFit_CFLAGS = $(PSASTRO_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+psastroModelFit_LDFLAGS = $(PSASTRO_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+psastroModelFit_LDADD = libpsastro.la
+
+gpcModel_CFLAGS = $(PSASTRO_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+gpcModel_LDFLAGS = $(PSASTRO_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+gpcModel_LDADD = libpsastro.la
+
+psastro_SOURCES = \
+	psastro.c		    \
+        psastroArguments.c	    \
+	psastroParseCamera.c   	    \
+	psastroDataLoad.c           \
+	psastroDataSave.c           \
+	psastroMetadataStats.c      \
+	psastroCleanup.c
+
+psastroModel_SOURCES = \
+	psastroModel.c		    \
+        psastroModelArguments.c	    \
+	psastroModelParseCamera.c   \
+	psastroModelDataLoad.c      \
+	psastroModelAnalysis.c      \
+	psastroModelBoresite.c      \
+	psastroModelFitBoresite.c   \
+	psastroModelAdjust.c        \
+	psastroModelDataSave.c      \
+	psastroCleanup.c
+
+psastroModelFit_SOURCES = \
+	psastroModelFit.c \
+	psastroModelBoresite.c      \
+	psastroModelFitBoresite.c
+
+gpcModel_SOURCES = gpcModel.c
+
+libpsastro_la_SOURCES = \
+	psastroErrorCodes.c         \
+	psastroVersion.c            \
+	psastroDefineFiles.c        \
+	psastroAnalysis.c           \
+	psastroAstromGuess.c        \
+	psastroLoadRefstars.c       \
+	psastroChooseRefstars.c     \
+	psastroConvert.c	    \
+	psastroChipAstrom.c         \
+	psastroOneChipGrid.c	    \
+	psastroOneChipFit.c	    \
+	psastroRemoveClumps.c	    \
+	psastroUtils.c	       	    \
+	psastroTestFuncs.c          \
+	psastroLuminosityFunction.c \
+	psastroRefstarSubset.c      \
+	psastroFixChips.c           \
+	psastroFixChipsTest.c       \
+	psastroUseModel.c           \
+	psastroMosaicAstrom.c       \
+	psastroMosaicDistortion.c   \
+	psastroMosaicFPtoTP.c       \
+	psastroMosaicGradients.c    \
+	psastroMosaicCorrectDistortion.c   \
+	psastroMosaicChipAstrom.c   \
+	psastroMosaicOneChip.c      \
+	psastroMosaicSetAstrom.c    \
+	psastroMosaicSetMatch.c     \
+	psastroZeroPoint.c    	    \
+	psastroDemoDump.c           \
+	psastroDemoPlot.c
+
+include_HEADERS = \
+	psastro.h \
+	psastroErrorCodes.h
+
+noinst_HEADERS = \
+	psastroInternal.h \
+	psastroStandAlone.h
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
+### Error codes.
+BUILT_SOURCES = psastroErrorCodes.h psastroErrorCodes.c
+CLEANFILES = psastroErrorCodes.h psastroErrorCodes.c
+EXTRA_DIST = psastroErrorCodes.dat psastroErrorCodes.h.in psastroErrorCodes.c.in
+
+psastroErrorCodes.h : psastroErrorCodes.dat psastroErrorCodes.h.in
+	$(ERRORCODES) --data=psastroErrorCodes.dat --outdir=. psastroErrorCodes.h
+
+psastroErrorCodes.c : psastroErrorCodes.dat psastroErrorCodes.c.in psastroErrorCodes.h
+	$(ERRORCODES) --data=psastroErrorCodes.dat --outdir=. psastroErrorCodes.c
Index: /tags/sj_tags/sj_root_20080929/psastro/src/gpcModel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/gpcModel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/gpcModel.c	(revision 22322)
@@ -0,0 +1,165 @@
+# include "psastroStandAlone.h"
+
+int main (int argc, char **argv) {
+
+    // USAGE: gpcModel (input) (output)
+    // generate model for gpc based on input model for all but chips
+
+    if (argc != 3) {
+	fprintf (stderr, "USAGE: gpcModel (input) (output)\n");
+	exit (2);
+    }
+
+    psFits *input = psFitsOpen (argv[1], "r");
+    psFits *output = psFitsOpen (argv[2], "w");
+
+    { // PHU
+	psMetadata *header = psFitsReadHeader (NULL, input);
+	psFitsWriteBlank (output, header, "");
+    }
+
+    { // CHIPS
+
+	// read initial chips
+	if (!psFitsMoveExtName (input, "CHIPS")) {
+	    psError(PS_ERR_IO, false, "missing CHIPS extension in astrometry table\n");
+	    return false;
+	}
+	psArray *chips = psFitsReadTable (input);
+	if (!chips) psAbort("cannot read chips");
+	fprintf (stderr, "read %ld rows from CHIPS\n", chips->n);
+
+	psMetadata *header = psMetadataAlloc();
+	psMetadataAddStr(header, PS_LIST_TAIL, "COORD",    PS_META_REPLACE, "name of this layer",	"CHIPS");
+	psMetadataAddStr(header, PS_LIST_TAIL, "PARENT",   PS_META_REPLACE, "next layer up",     	"FOCAL_PLANE");
+	psMetadataAddStr(header, PS_LIST_TAIL, "BOUNDARY", PS_META_REPLACE, "validity region",   	"RECTANGLE");
+	psMetadataAddStr(header, PS_LIST_TAIL, "TRANSFRM", PS_META_REPLACE, "mapping to parent", 	"POLYNOMIAL");
+
+	float coeff[2][2];
+	char coeffMask[2][2];
+	coeff[0][0] = 0.0;
+	coeff[1][0] = 1.0;
+	coeff[0][1] = 0.0;
+	coeff[1][1] = 0.0;
+
+	coeffMask[0][0] = 0;
+	coeffMask[1][0] = 0;
+	coeffMask[0][1] = 0;
+	coeffMask[1][1] = 1;
+
+	psArray *table = psArrayAllocEmpty (1);
+	for (int i = 0; i < 8; i++) {
+	    for (int j = 0; j < 8; j++) {
+		if ((i == 0) && (j == 0)) continue;
+		if ((i == 0) && (j == 7)) continue;
+		if ((i == 7) && (j == 0)) continue;
+		if ((i == 7) && (j == 7)) continue;
+
+		float Xo;
+		float Yo;
+		if (i < 4) {
+		    Xo = (3 - i)*4970.0 +  60.0;
+		    Yo = (3 - j)*5133.0 + 125.0;
+		    coeff[1][0] = +1.0;
+		} else {
+		    Xo = (4 - i)*4970.0 -  60.0;
+		    Yo = (4 - j)*5133.0 - 150.0;
+		    coeff[1][0] = -1.0;
+		}	    
+
+		for (int ix = 0; ix < 2; ix++) {
+		    for (int iy = 0; iy < 2; iy++) {
+
+			psMetadata *row = psMetadataAlloc ();
+		
+			char chipname[80];
+			sprintf (chipname, "XY%d%d", i, j);
+			psMetadataAddStr(row,    PS_LIST_TAIL, "SEGMENT",  PS_META_REPLACE, "name of this segment", chipname);
+			psMetadataAddStr(row,    PS_LIST_TAIL, "PARENT",   PS_META_REPLACE, "next layer up",        "FOCAL_PLANE");
+			psMetadataAddF32(row,    PS_LIST_TAIL, "MINX",     PS_META_REPLACE, "range", 0.0);
+			psMetadataAddF32(row,    PS_LIST_TAIL, "MAXX",     PS_META_REPLACE, "range", 4846.0);
+			psMetadataAddF32(row,    PS_LIST_TAIL, "MINY",     PS_META_REPLACE, "range", 0.0);
+			psMetadataAddF32(row,    PS_LIST_TAIL, "MAXY",     PS_META_REPLACE, "range", 4868.0);
+
+			psMetadataAddS32(row,    PS_LIST_TAIL, "XORDER",   PS_META_REPLACE, "", ix);
+			psMetadataAddS32(row,    PS_LIST_TAIL, "YORDER",   PS_META_REPLACE, "", iy);
+			psMetadataAddS32(row,    PS_LIST_TAIL, "NXORDER",  PS_META_REPLACE, "", 1);
+			psMetadataAddS32(row,    PS_LIST_TAIL, "NYORDER",  PS_META_REPLACE, "", 1);
+			if ((ix == 0) && (iy == 0)) {
+			    psMetadataAddF32(row,    PS_LIST_TAIL, "POLY_X",   PS_META_REPLACE, "", Xo);
+			    psMetadataAddF32(row,    PS_LIST_TAIL, "POLY_Y",   PS_META_REPLACE, "", Yo);
+			} else {
+			    psMetadataAddF32(row,    PS_LIST_TAIL, "POLY_X",   PS_META_REPLACE, "", coeff[ix][iy]);
+			    psMetadataAddF32(row,    PS_LIST_TAIL, "POLY_Y",   PS_META_REPLACE, "", coeff[iy][ix]);
+			}
+			psMetadataAddF32(row,    PS_LIST_TAIL, "ERROR_X",  PS_META_REPLACE, "", 0.0);
+			psMetadataAddF32(row,    PS_LIST_TAIL, "ERROR_Y",  PS_META_REPLACE, "", 0.0);
+			psMetadataAddU8 (row,    PS_LIST_TAIL, "MASK_X",   PS_META_REPLACE, "", coeffMask[ix][iy]);
+			psMetadataAddU8 (row,    PS_LIST_TAIL, "MASK_Y",   PS_META_REPLACE, "", coeffMask[ix][iy]);
+			psArrayAdd (table, 100, row);
+			psFree (row);
+		    }
+		}
+	    }
+	}
+    
+	if (!psFitsWriteTable (output, header, table, "CHIPS")) {
+	    psError(PS_ERR_IO, false, "writing sky data\n");
+	    psFree(table);
+	    return false;
+	}
+    }
+
+    { // FP
+	if (!psFitsMoveExtName (input, "FP")) {
+	    psError(PS_ERR_IO, false, "missing FP extension in astrometry table\n");
+	    return false;
+	}
+	psMetadata *header = psFitsReadHeader (NULL, input);
+	psArray *table = psFitsReadTable (input);
+	if (!table) psAbort("cannot read fp");
+	fprintf (stderr, "read %ld rows from FP\n", table->n);
+
+	if (!psFitsWriteTable (output, header, table, "FP")) {
+	    psError(PS_ERR_IO, false, "writing sky data\n");
+	    psFree(table);
+	    return false;
+	}
+    }
+
+    { // TP
+	if (!psFitsMoveExtName (input, "TP")) {
+	    psError(PS_ERR_IO, false, "missing TP extension in astrometry table\n");
+	    return false;
+	}
+	psMetadata *header = psFitsReadHeader (NULL, input);
+	psArray *table = psFitsReadTable (input);
+	if (!table) psAbort("cannot read tp");
+	fprintf (stderr, "read %ld rows from TP\n", table->n);
+	if (!psFitsWriteTable (output, header, table, "TP")) {
+	    psError(PS_ERR_IO, false, "writing sky data\n");
+	    psFree(table);
+	    return false;
+	}
+    }
+
+    { // SKY
+	if (!psFitsMoveExtName (input, "SKY")) {
+	    psError(PS_ERR_IO, false, "missing SKY extension in astrometry table\n");
+	    return false;
+	}
+	psMetadata *header = psFitsReadHeader (NULL, input);
+	psArray *sky = psFitsReadTable (input);
+	if (!sky) psAbort("cannot read sky");
+	fprintf (stderr, "read %ld rows from SKY\n", sky->n);
+	if (!psFitsWriteTable (output, header, sky, "SKY")) {
+	    psError(PS_ERR_IO, false, "writing sky data\n");
+	    psFree(sky);
+	    return false;
+	}
+    }
+
+    psFitsClose (input);
+    psFitsClose (output);
+    exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastro.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastro.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastro.c	(revision 22322)
@@ -0,0 +1,53 @@
+# include "psastroStandAlone.h"
+
+static void usage (void) {
+    fprintf (stderr, "USAGE: psastro [-file image(s)] [-list imagelist] (output)\n");
+    exit (2);
+}
+
+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 = psastroArguments (argc, argv);
+    if (!config) usage ();
+
+    // load identify the data sources
+    if (!psastroParseCamera (config)) {
+	psErrorStackPrint(stderr, "error setting up the camera\n");
+	exit (1);
+    }
+
+    // load the raw detection data (using PSPHOT.SOURCES filerule)
+    // select subset of stars for astrometry
+    if (!psastroDataLoad (config)) {
+	psErrorStackPrint(stderr, "error loading input data\n");
+	exit (1);
+    }
+
+    // run the full astrometry analysis (chip and/or mosaic)
+    if (!psastroAnalysis (config)) {
+	psErrorStackPrint(stderr, "failure in psastro analysis\n");
+	exit (1);
+    }
+    
+    // write out the results
+    if (!psastroDataSave (config)) {
+	psErrorStackPrint(stderr, "failed to write out data\n");
+	exit (1);
+    }
+
+    psLogMsg ("psastro", 3, "complete psastro run: %f sec\n", psTimerMark ("complete"));
+
+    psastroCleanup (config);
+    exit (EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastro.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastro.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastro.h	(revision 22322)
@@ -0,0 +1,107 @@
+/* This file defines the library functions available to external programs.  It must be included
+ * by programs which are compiled against psphot functions.
+ */
+
+# ifndef PSASTRO_H
+# define PSASTRO_H
+
+# include "psastroErrorCodes.h"
+# define PSASTRO_RECIPE "PSASTRO" // Name of the recipe to use
+
+# define psMemCopy(A)(psMemIncrRefCounter((A)))
+# define DEG_RAD 57.295779513082322
+# define RAD_DEG  0.017453292519943
+# define SIGN(X)  (((X) == 0) ? 0 : ((fabs((double)(X))) / (X)))
+
+// this structure represents a fit to the logN / logS curve for a set of stars
+// logN = offset + slope * logS
+typedef struct {
+    double mMin;			// minimum magnitude bin with data
+    double mMax;			// maximum magnitude bin with data
+    double offset;			// fitted line offset
+    double slope;			// fitted line slope
+    double mPeak;			// mag of peak bin 
+    int nPeak;				// # of stars in peak bin
+    int sPeak;				// sum of stars to peak bin
+} pmLumFunc;
+
+bool              psastroDataSave (pmConfig *config);
+bool              psastroDefineFiles (pmConfig *config, pmFPAfile *input);
+bool              psastroDefineFile (pmConfig *config, pmFPA *input, char *filerule, char *argname, pmFPAfileType fileType, pmDetrendType detrendType);
+bool              psastroAnalysis (pmConfig *config);
+
+bool              psastroConvertFPA (pmFPA *fpa, psMetadata *recipe);
+bool              psastroConvertReadout (pmReadout *readout, psMetadata *recipe);
+psArray          *pmSourceToAstromObj (psArray *sources);
+bool              psastroAstromGuess (int *nStars, pmConfig *config);
+bool              psastroAstromGuessCheck (pmConfig *config);
+
+psPlaneDistort   *psPlaneDistortIdentity ();
+bool              psPlaneDistortIsIdentity (psPlaneDistort *distort);
+
+psArray          *psastroLoadRefstars (pmConfig *config);
+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              psastroRefstarSubset (pmReadout *readout);
+pmLumFunc        *psastroLuminosityFunction (psArray *stars);
+psArray          *psastroRemoveClumps (psArray *input, int scale);
+
+// utility functions:
+bool              psastroUpdateChipToFPA (pmFPA *fpa, pmChip *chip, psArray *rawstars, psArray *refstars);
+bool              psastroWriteStars (char *filename, psArray *sources);
+bool              psastroWriteTransform (psPlaneTransform *map);
+
+// mosaic fitting functions
+bool 		  psastroMosaicDistortion (pmFPA *fpa, psMetadata *recipe, int pass);
+psPlaneTransform *psastroMosaicFitRotAndScale (pmFPA *fpa);
+bool              psastroMosaicApplyRotAndScale (pmFPA *fpa, psPlaneTransform *TPtoFP);
+bool 		  psastroMosaicDistortionFromGradients (pmFPA *fpa, psMetadata *recipe);
+bool              psastroMosaicCorrectDistortion (pmFPA *fpa, psPlaneTransform *TPtoFP);
+bool 		  psastroMosaicCommonScale (pmFPA *fpa, psMetadata *recipe);
+bool 		  psastroMosaicAstrom (pmConfig *config);
+bool 		  psastroMosaicChipAstrom (pmFPA *fpa, psMetadata *recipe, int iteration);
+bool 		  psastroMosaicSetMatch (pmFPA *fpa, psMetadata *recipe, int iteration);
+bool 		  psastroMosaicSetAstrom (pmFPA *fpa);
+bool 		  psastroMosaicHeaders (pmConfig *config);
+bool 		  psastroMosaicRescaleChips (pmFPA *fpa);
+bool 		  psastroMosaicOneChip (pmChip *chip, pmReadout *readout, psMetadata *recipe, psMetadata *updates, int iteration);
+
+// Return version strings.
+psString 	  psastroVersion(void);
+psString 	  psastroVersionLong(void);
+
+// demo plots
+bool 		  psastroPlotRawstars (psArray *rawstars, pmFPA *fpa, pmChip *chip, psMetadata *recipe);
+bool 		  psastroPlotRefstars (psArray *refstars, psMetadata *recipe);
+bool 		  psastroPlotOneChipFit (psArray *rawstars, psArray *refstars, psArray *match, psMetadata *recipe);
+
+bool 		  psastroDumpRawstars (psArray *rawstars, pmFPA *fpa, pmChip *chip);
+bool 		  psastroDumpRefstars (psArray *refstars, char *filename);
+bool 		  psastroDumpMatches (pmFPA *fpa, char *filename);
+bool 		  psastroDumpStars (psArray *stars, char *filename);
+bool              psastroDumpMatchedStars (char *filename, psArray *rawstars, psArray *refstars, psArray *match);
+bool              psastroDumpGradients (psArray *gradients, char *filename);
+
+bool		  psastroMosaicSetAstrom_tmp (pmFPA *fpa);
+int 		  psastroSortByMag (const void *a, const void *b);
+
+bool              psastroFixChips (pmConfig *config, psMetadata *recipe);
+bool              psastroFixChipsTest (pmConfig *config, psMetadata *recipe);
+bool              psastroUseModel (pmConfig *config, psMetadata *recipe);
+bool              psastroDumpCorners (char *filenameU, char *filenameD, pmFPA *fpa);
+
+
+psArray          *psastroReadGetstarCatalog (psFits *fits);
+psArray          *psastroReadGetstar_PS1_DEV_0 (psFits *fits);
+
+bool              psastroAstromGuessSetChip (pmFPA *fpa, pmChip *chip, const pmFPAview *view, double pixelScale, bool bilevelAstrometry);
+bool              psastroAstromGuessSetFPA (pmFPA *fpa, bool *bilevelAstrometry);
+bool              psastroMetadataStats (pmConfig *config);
+
+bool psastroZeroPointReadout(psArray *rawstars, psArray *refstars, psArray *matches);
+bool psastroZeroPoint (pmConfig *config);
+
+# endif /* PSASTRO_H */
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroAnalysis.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroAnalysis.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroAnalysis.c	(revision 22322)
@@ -0,0 +1,90 @@
+# include "psastroInternal.h"
+
+bool psastroAnalysis (pmConfig *config) {
+
+    bool status;
+    int nStars;
+
+    // measure the total elapsed time in psastroAnalysis.
+    psTimerStart ("psastroAnalysis");
+
+    // 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;
+    }
+
+    if (!psastroUseModel (config, recipe)) {
+        psError (PSASTRO_ERR_UNKNOWN, false, "failed to set model astrometry\n");
+        return false;
+    }
+
+    // interpret the available initial astrometric information
+    // apply the initial guess
+    if (!psastroAstromGuess (&nStars, config)) {
+        psError (PSASTRO_ERR_UNKNOWN, false, "failed to determine initial astrometry guess\n");
+        return false;
+    }
+    if (nStars == 0) {
+        psLogMsg ("psastro", 2, "skipping astrometry analysis : no stars\n");
+        return false;
+    }
+
+    // load the reference stars overlapping the data stars
+    psArray *refs = psastroLoadRefstars(config);
+    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)) {
+        psError (PSASTRO_ERR_UNKNOWN, false, "failed to select reference data for chips\n");
+        psFree(refs);
+        return false;
+    }
+
+    // check the command-line arguments first
+    bool chipastro = psMetadataLookupBool (&status, config->arguments, "PSASTRO.CHIP.MODE");
+    if (!status) {
+        chipastro = psMetadataLookupBool (&status, recipe, "PSASTRO.CHIP.MODE");
+    }
+    bool mosastro  = psMetadataLookupBool (&status, config->arguments, "PSASTRO.MOSAIC.MODE");
+    if (!status) {
+        mosastro  = psMetadataLookupBool (&status, recipe, "PSASTRO.MOSAIC.MODE");
+    }
+    if (!chipastro && !mosastro) {
+        psLogMsg ("psastro", 3, "no astrometry mode selected, assuming chip astrometry\n");
+        chipastro = true;
+    }
+
+    if (chipastro) {
+        if (!psastroChipAstrom (config)) {
+            psError (PSASTRO_ERR_UNKNOWN, false, "failed to perform single chip astrometry\n");
+            psFree(refs);
+            return false;
+        }
+    }
+    if (mosastro) {
+        if (!psastroMosaicAstrom (config)) {
+            psError (PSASTRO_ERR_UNKNOWN, false, "failed to perform mosaic camera astrometry\n");
+            psFree(refs);
+            return false;
+        }
+    }
+
+    // psastroZeroPoint (config);
+
+    psastroAstromGuessCheck (config);
+
+    // XXX how do we specify stack astrometry?
+    // psastroStackAstrom (config, refs);
+
+    psFree (refs);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroArguments.c	(revision 22322)
@@ -0,0 +1,108 @@
+# include "psastroStandAlone.h"
+
+pmConfig *psastroArguments (int argc, char **argv) {
+
+    bool status;
+    int N;
+
+    if (argc == 1) {
+        psError(PSASTRO_ERR_ARGUMENTS, true, "No arguments supplied");
+        psErrorStackPrint(stderr, "exit");
+        return NULL;
+    }
+
+    // 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, "exit");
+        return NULL;
+    }
+
+    // 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);
+
+    // photcode : used in output to supplement header data (argument or recipe?)
+    if ((N = psArgumentGet (argc, argv, "-photcode"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (options, PS_LIST_TAIL, "PHOTCODE", PS_META_REPLACE, "", 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);
+    }
+    
+    // apply the chip correction based on the reference astrometry?
+    if ((N = psArgumentGet (argc, argv, "-fixchips"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddBool (config->arguments, PS_LIST_TAIL, "PSASTRO.FIX.CHIPS", PS_META_REPLACE, "", true);
+    }
+    // no valid header WCS: supply from astrom model
+    if ((N = psArgumentGet (argc, argv, "-use-astrommodel"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddBool (config->arguments, PS_LIST_TAIL, "PSASTRO.USE.MODEL", PS_META_REPLACE, "", true);
+    }
+    // define the reference astrometry file
+    status = pmConfigFileSetsMD (config->arguments, &argc, argv, "ASTROM.MODEL", "-astrommodel", "-astrommodellist");
+    if (status) {
+	// if supplied, assume -fixchips is desired
+        psMetadataAddBool (config->arguments, PS_LIST_TAIL, "PSASTRO.FIX.CHIPS", PS_META_REPLACE, "", true);
+    }
+
+    if ((N = psArgumentGet(argc, argv, "-stats"))) {
+        psArgumentRemove(N, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "STATS", PS_META_REPLACE, "Filename for summary statistics", argv[N]);
+        psArgumentRemove(N, &argc, argv);
+    }
+
+    // apply mosastro mode?
+    if ((N = psArgumentGet (argc, argv, "-mosastro"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddBool (config->arguments, PS_LIST_TAIL, "PSASTRO.MOSAIC.MODE", PS_META_REPLACE, "", false);
+    }
+    if ((N = psArgumentGet (argc, argv, "+mosastro"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddBool (config->arguments, PS_LIST_TAIL, "PSASTRO.MOSAIC.MODE", PS_META_REPLACE, "", true);
+    }
+
+    // apply chipastro mode?
+    if ((N = psArgumentGet (argc, argv, "-chipastro"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddBool (config->arguments, PS_LIST_TAIL, "PSASTRO.CHIP.MODE", PS_META_REPLACE, "", false);
+    }
+    if ((N = psArgumentGet (argc, argv, "+chipastro"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddBool (config->arguments, PS_LIST_TAIL, "PSASTRO.CHIP.MODE", PS_META_REPLACE, "", true);
+    }
+
+    // dump the configuration to a file?
+    if ((N = psArgumentGet (argc, argv, "-dumpconfig"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "DUMP_CONFIG", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
+    if (!status) {
+        psError(PSASTRO_ERR_ARGUMENTS, true, "Missing -file (input) or -list (input)");
+        psErrorStackPrint(stderr, "exit");
+        return NULL;
+    }
+
+    if (argc != 2) {
+        psError(PSASTRO_ERR_ARGUMENTS, true, "Incorrect arguments supplied");
+        psErrorStackPrint(stderr, "exit");
+        return NULL;
+    }
+
+    // output positions is fixed
+    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[1]);
+
+    psTrace("psastro", 1, "Done with psastroArguments...\n");
+    return (config);
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroAstromGuess.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroAstromGuess.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroAstromGuess.c	(revision 22322)
@@ -0,0 +1,384 @@
+# include "psastroInternal.h"
+# define DEBUG 0
+
+// this function loads the header WCS astrometry terms into the fpa terms and applies the
+// astrometry to the detected objects.
+
+// this function assumes the initial astrometry arrives in the form of WCS keywords in the
+// headers corresponding to the chips.
+
+bool psastroAstromGuess (int *nStars, pmConfig *config) {
+
+    bool newFPA = true;
+    bool status = false;
+    double RAmin  = +FLT_MAX;
+    double RAmax  = -FLT_MAX;
+    double DECmin = +FLT_MAX;
+    double DECmax = -FLT_MAX;
+
+    double RAminSky = NAN;
+    double RAmaxSky = NAN;
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+
+    *nStars = 0;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+	psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe!");
+	return false;
+    }
+
+    // have we already supplied the astrometry from the model?
+    bool useModel = psMetadataLookupBool (&status, config->arguments, "PSASTRO.USE.MODEL");
+    if (!status) {
+	useModel = psMetadataLookupBool (&status, recipe, "PSASTRO.USE.MODEL");
+    }
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    if (!input) {
+	psError(PSASTRO_ERR_CONFIG, true, "Can't find input data");
+	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; 
+    } 
+
+    psVector *cornerL = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerM = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerP = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerQ = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerR = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerD = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+    pmFPA *fpa = input->fpa;
+
+    if (DEBUG) psastroDumpCorners ("corners.up.guess1.dat", "corners.dn.guess1.dat", fpa);
+
+    // load mosaic-level astrometry?
+    bool bilevelAstrometry = false;
+    if (!useModel) {
+	psastroAstromGuessSetFPA (fpa, &bilevelAstrometry);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+    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 || !chip->data_exists) { continue; }
+
+	if (!useModel) {
+	    if (!psastroAstromGuessSetChip (fpa, chip, view, pixelScale, bilevelAstrometry)) 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;
+        }
+
+	// report and save the current best guess for the chip 0,0 pixel coordinates
+	{ 
+	    psPlane ptCH, ptFP, ptTP;
+	    psSphere ptSky;
+
+	    ptCH.x = 0;
+	    ptCH.y = 0;
+	    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
+	    psPlaneTransformApply (&ptTP, fpa->toTPA, &ptFP);
+	    psDeproject (&ptSky, &ptTP, fpa->toSky);
+	    psLogMsg ("psastro", 3, "0,0 pix for chip %3d = %f,%f\n", view->chip, DEG_RAD*ptSky.r, DEG_RAD*ptSky.d);
+
+	    psVectorAppend (cornerL, ptFP.x);
+	    psVectorAppend (cornerM, ptFP.y);
+	    psVectorAppend (cornerP, ptTP.x);
+	    psVectorAppend (cornerQ, ptTP.y);
+	    psVectorAppend (cornerR, ptSky.r);
+	    psVectorAppend (cornerD, ptSky.d);
+	}
+
+        // apply the new WCS guess data to all of the data in the readouts
+        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; }
+
+                psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+                if (rawstars == NULL) { continue; }
+
+		*nStars += rawstars->n;
+                for (int i = 0; i < rawstars->n; i++) {
+                    pmAstromObj *raw = rawstars->data[i];
+
+                    psPlaneTransformApply (raw->FP, chip->toFPA, raw->chip);
+                    psPlaneTransformApply (raw->TP, fpa->toTPA, raw->FP);
+                    psDeproject (raw->sky, raw->TP, fpa->toSky);
+
+                    // rationalize ra to sky range centered on boresite
+                    while (raw->sky->r < RAminSky) raw->sky->r += 2.0*M_PI;
+                    while (raw->sky->r > RAmaxSky) raw->sky->r -= 2.0*M_PI;
+
+                    RAmin = PS_MIN (raw->sky->r, RAmin);
+                    RAmax = PS_MAX (raw->sky->r, RAmax);
+
+                    DECmin = PS_MIN (raw->sky->d, DECmin);
+                    DECmax = PS_MAX (raw->sky->d, DECmax);
+                }
+
+		// dump or plot the resulting projected positions
+		if (psTraceGetLevel("psastro.dump") > 0) {
+		    psastroDumpRawstars (rawstars, fpa, chip);
+		}
+
+		if (psTraceGetLevel("psastro.plot") > 0) {
+		    psastroPlotRawstars (rawstars, fpa, chip, recipe);
+		}
+            }
+        }
+    }
+
+    if (DEBUG) psastroDumpCorners ("corners.up.guess2.dat", "corners.dn.guess2.dat", fpa);
+
+    // how many total sources are available to us?
+    psMetadataAddS32 (recipe, PS_LIST_TAIL, "NTOTSTAR",  PS_META_REPLACE, "", *nStars);
+    if (*nStars == 0) {
+	psLogMsg ("psastro", 2, "no sources available for astrometry\n");
+	psFree (view);
+	return true;
+    }
+
+    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);
+
+    psMetadataAddVector (input->fpa->analysis, PS_LIST_TAIL, "CORNER.L", PS_META_REPLACE, "corner pixel", cornerL);
+    psMetadataAddVector (input->fpa->analysis, PS_LIST_TAIL, "CORNER.M", PS_META_REPLACE, "corner pixel", cornerM);
+    psMetadataAddVector (input->fpa->analysis, PS_LIST_TAIL, "CORNER.P", PS_META_REPLACE, "corner pixel", cornerP);
+    psMetadataAddVector (input->fpa->analysis, PS_LIST_TAIL, "CORNER.Q", PS_META_REPLACE, "corner pixel", cornerQ);
+    psMetadataAddVector (input->fpa->analysis, PS_LIST_TAIL, "CORNER.R", PS_META_REPLACE, "corner pixel", cornerR);
+    psMetadataAddVector (input->fpa->analysis, PS_LIST_TAIL, "CORNER.D", PS_META_REPLACE, "corner pixel", cornerD);
+
+    psFree (cornerL);
+    psFree (cornerM);
+    psFree (cornerP);
+    psFree (cornerQ);
+    psFree (cornerR);
+    psFree (cornerD);
+
+    psFree (view);
+    return true;
+}
+
+/* coordinate frame hierachy
+   pixels (on a given readout)
+   cell
+   chip
+   FP (focal plane)
+   TP (tangent plane)
+   sky (ra, dec)
+*/
+
+bool psastroAstromGuessSetChip (pmFPA *fpa, pmChip *chip, const pmFPAview *view, double pixelScale, bool bilevelAstrometry) {
+
+    // read WCS data from the corresponding header
+    pmHDU *hdu = pmFPAviewThisHDU (view, fpa);
+    if (bilevelAstrometry) {
+	if (!pmAstromReadBilevelChip (chip, hdu->header)) {
+	    psWarning("Could not get WCS information from header for chip %d, skipping", view->chip); 
+	    return false;
+	} 
+    } else {
+	if (!pmAstromReadWCS (fpa, chip, hdu->header, pixelScale)) {
+	    psWarning("Could not get WCS information from header for chip %d, skipping", view->chip); 
+	    return false;
+	} 
+    }
+    return true;
+}
+
+bool psastroAstromGuessSetFPA (pmFPA *fpa, bool *bilevelAstrometry) {
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+    pmHDU *phu = pmFPAviewThisPHU (view, fpa);
+
+    *bilevelAstrometry = false;
+
+    // load mosaic-level astrometry?
+    if (phu) {
+	char *ctype = psMetadataLookupStr (NULL, phu->header, "CTYPE1");
+	if (ctype) {
+	    *bilevelAstrometry = !strcmp (&ctype[4], "-DIS");
+	}
+    }
+    if (*bilevelAstrometry) {
+	pmAstromReadBilevelMosaic (fpa, phu->header);
+    } 
+    psFree (view);
+    return true;
+}
+
+// we made a guess at the beginning; how does the guess compare with the result?
+bool psastroAstromGuessCheck (pmConfig *config) {
+
+    bool status;
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    if (!input) {
+	psError(PSASTRO_ERR_CONFIG, true, "Can't find input data");
+	return false;
+    }
+
+    psVector *cornerLn = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerMn = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerPn = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerQn = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerRn = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerDn = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+    pmFPA *fpa = input->fpa;
+
+    if (DEBUG) psastroDumpCorners ("corners.up.guess3.dat", "corners.dn.guess3.dat", fpa);
+
+    pmChip *chip = NULL;
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
+        if (!chip->process || !chip->file_exists || !chip->data_exists) { continue; }
+
+	psPlane ptCH, ptFP, ptTP;
+	psSphere ptSky;
+
+	ptCH.x = 0;
+	ptCH.y = 0;
+	psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
+	psPlaneTransformApply (&ptTP, fpa->toTPA, &ptFP);
+	psDeproject (&ptSky, &ptTP, fpa->toSky);
+	psLogMsg ("psastro", 3, "0,0 pix for chip %3d = %f,%f\n", view->chip, DEG_RAD*ptSky.r, DEG_RAD*ptSky.d);
+
+	// new corner locations based on the calibrated astrometry
+	psVectorAppend (cornerLn, ptFP.x);
+	psVectorAppend (cornerMn, ptFP.y);
+	psVectorAppend (cornerPn, ptTP.x);
+	psVectorAppend (cornerQn, ptTP.y);
+	psVectorAppend (cornerRn, ptSky.r);
+	psVectorAppend (cornerDn, ptSky.d);
+    }
+
+    psVector *cornerLo = psMetadataLookupPtr (&status, input->fpa->analysis, "CORNER.L");
+    psVector *cornerMo = psMetadataLookupPtr (&status, input->fpa->analysis, "CORNER.M");
+    psVector *cornerPo = psMetadataLookupPtr (&status, input->fpa->analysis, "CORNER.P");
+    psVector *cornerQo = psMetadataLookupPtr (&status, input->fpa->analysis, "CORNER.Q");
+    psVector *cornerRo = psMetadataLookupPtr (&status, input->fpa->analysis, "CORNER.R");
+    psVector *cornerDo = psMetadataLookupPtr (&status, input->fpa->analysis, "CORNER.D");
+
+    // compare the old R,D values projected to the same tangent plane as the new R,D values:
+
+    psVector *cornerPs = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *cornerQs = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+    for (int i = 0; i < cornerRo->n; i++) {
+	
+	psPlane ptTP;
+	psSphere ptSky;
+
+	ptSky.r = cornerRo->data.F32[i];
+	ptSky.d = cornerDo->data.F32[i];
+
+	psProject (&ptTP, &ptSky, fpa->toSky);
+	psVectorAppend (cornerPs, ptTP.x);
+	psVectorAppend (cornerQs, ptTP.y);
+    }
+
+    psPlaneTransform *map = psPlaneTransformAlloc (1, 1);
+    map->x->coeffMask[1][1] = PS_POLY_MASK_SET;
+    map->y->coeffMask[1][1] = PS_POLY_MASK_SET;
+    
+    psVectorFitPolynomial2D (map->x, NULL, 0, cornerPn, NULL, cornerPs, cornerQs);
+    psVectorFitPolynomial2D (map->y, NULL, 0, cornerQn, NULL, cornerPs, cornerQs);
+    
+    // apply the linear fit...
+    psVector *cornerPf = psPolynomial2DEvalVector (map->x, cornerPs, cornerQs);
+    psVector *cornerQf = psPolynomial2DEvalVector (map->y, cornerPs, cornerQs);
+
+    // ...and calculate the residual between Pn,Qn and Pf,Qf
+    psVector *cornerPd = (psVector *) psBinaryOp (NULL, cornerPn, "-", cornerPf);
+    psVector *cornerQd = (psVector *) psBinaryOp (NULL, cornerQn, "-", cornerQf);
+
+    psStats *statsP = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+    psStats *statsQ = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+
+    psVectorStats (statsP, cornerPd, NULL, NULL, 0);
+    psVectorStats (statsQ, cornerQd, NULL, NULL, 0);
+
+    float angle = atan2 (map->y->coeff[1][0], map->x->coeff[1][0]);
+    float scale = hypot (map->y->coeff[1][0], map->x->coeff[1][0]);
+    
+    psLogMsg ("psastro", 3, "boresite offset  : %f,%f\n", map->x->coeff[0][0], map->y->coeff[0][0]);
+    psLogMsg ("psastro", 3, "boresite angle   : %f, scale: %f", angle*PS_DEG_RAD, scale);
+    psLogMsg ("psastro", 3, "boresite scatter : %f,%f\n", statsP->sampleStdev, statsQ->sampleStdev);
+
+    // write the elapsed time here; this will be updated in psastroMosaicAstrometry, if called
+    psMetadata *header = psMetadataLookupMetadata (&status, input->fpa->analysis, "PSASTRO.HEADER");
+    if (!header) {
+	header = psMetadataAlloc();
+	psMetadataAddMetadata (input->fpa->analysis, PS_LIST_TAIL, "PSASTRO.HEADER",  PS_META_REPLACE, "psastro header stats", header);
+	psFree (header);  // drop this reference
+    }
+
+    psMetadataAddF32 (header, PS_LIST_TAIL, "AST_R0", PS_META_REPLACE, "boresite offset in RA (TP units)", map->x->coeff[0][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "AST_D0", PS_META_REPLACE, "boresite offset in DEC (TP units)", map->y->coeff[0][0]);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "AST_T0", PS_META_REPLACE, "boresite angle (degrees)", angle*PS_DEG_RAD);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "AST_S0", PS_META_REPLACE, "boresite scale correction", scale);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "AST_RS", PS_META_REPLACE, "boresite scatter in RA (TP units)", statsP->sampleStdev);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "AST_DS", PS_META_REPLACE, "boresite scatter in DEC (TP units)", statsQ->sampleStdev);
+
+    if (0) {
+	FILE *f = fopen ("corners.dat", "w");
+	for (int i = 0; i < cornerRo->n; i++) {
+	    fprintf (f, "%10.6f %10.6f  %9.2f %9.2f  %9.2f %9.2f  |  %10.6f %10.6f  %9.2f %9.2f  %9.2f %9.2f\n",
+		     cornerRn->data.F32[i], cornerDn->data.F32[i], cornerPn->data.F32[i], cornerQn->data.F32[i], cornerLn->data.F32[i], cornerMn->data.F32[i], 
+		     cornerRo->data.F32[i], cornerDo->data.F32[i], cornerPo->data.F32[i], cornerQo->data.F32[i], cornerLo->data.F32[i], cornerMo->data.F32[i]);
+	}
+	fclose (f);
+    }
+
+    psFree (cornerPf);
+    psFree (cornerQf);
+    psFree (cornerPd);
+    psFree (cornerQd);
+
+    psFree (statsP);
+    psFree (statsQ);
+
+    psFree (cornerLn);
+    psFree (cornerMn);
+    psFree (cornerPn);
+    psFree (cornerQn);
+    psFree (cornerRn);
+    psFree (cornerDn);
+    psFree (cornerPs);
+    psFree (cornerQs);
+    psFree (map);
+    psFree (view);
+    
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroChipAstrom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroChipAstrom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroChipAstrom.c	(revision 22322)
@@ -0,0 +1,133 @@
+# include "psastroInternal.h"
+# define NONLIN_TOL 0.001 /* tolerance in pixels */
+
+bool psastroChipAstrom (pmConfig *config) {
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe");
+        return false;
+    }
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    if (!input) {
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find input data");
+        return false;
+    }
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+    pmFPA *fpa = input->fpa;
+
+    int numGoodChips = 0;               // Number of chips for which astrometry succeeds
+
+    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; }
+
+        int numGoodCells = 0;           // Number of cells for which astrometry succeeds
+        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; }
+            if (!chip->fromFPA) { continue; }
+
+            // process each of the readouts
+            int numGoodRO = 0;          // Number of readouts for which astrometry succeeds
+            while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+                if (! readout->data_exists) { continue; }
+
+                // select the raw objects for this readout
+                psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+                if (rawstars == NULL) { continue; }
+
+                // select the raw objects for this readout
+                psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+                if (refstars == NULL) { continue; }
+
+                // the absolute minimum number of stars is 4 (for order = 1)
+                if ((rawstars->n < 4) || (refstars->n < 4)) {
+                    readout->data_exists = false;
+                    psLogMsg ("psastro", 3, "insufficient rawstars (%ld) or refstars (%ld)",
+                              rawstars->n, refstars->n);
+                    continue;
+                }
+
+                // save WCS and analysis metadata in update header
+                psMetadata *updates = psMetadataAlloc();
+
+                // XXX update the header with info to reflect the failure
+                if (!psastroOneChipGrid (fpa, chip, refstars, rawstars, recipe, updates)) {
+                    readout->data_exists = false;
+                    psLogMsg ("psastro", 3, "failed to find a solution\n");
+                    psFree (updates);
+                    continue;
+                }
+                // XXX update the header with info to reflect the failure
+                if (!psastroOneChipFit (fpa, chip, refstars, rawstars, recipe, updates)) {
+                    readout->data_exists = false;
+                    psLogMsg ("psastro", 3, "failed to find a solution\n");
+                    psFree (updates);
+                    continue;
+                }
+
+                numGoodRO++;
+
+                // write the elapsed time here; this will be updated in psastroMosaicAstrometry, if called
+                psMetadataAddF32 (updates, PS_LIST_TAIL, "DT_ASTR", PS_META_REPLACE, "elapsed psastro time", psTimerMark ("psastroAnalysis"));
+
+                pmAstromWriteWCS (updates, fpa, chip, NONLIN_TOL);
+                psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSASTRO.HEADER",  PS_DATA_METADATA, "psastro header stats", updates);
+                psFree (updates);
+
+                if (psTraceGetLevel("psastro.dump") > 0) {
+
+                    char *filename = NULL;
+                    char *chipname = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
+
+                    psStringAppend (&filename, "rawstars.ch.%s.dat", chipname);
+                    psastroDumpStars (rawstars, filename);
+                    psFree (filename);
+                    filename = NULL;
+
+                    psStringAppend (&filename, "refstars.ch.%s.dat", chipname);
+                    psastroDumpStars (refstars, filename);
+                    psFree (filename);
+                    filename = NULL;
+                }
+            }
+            if (numGoodRO > 0) {
+                numGoodCells++;
+            }
+        }
+        if (numGoodCells > 0) {
+            numGoodChips++;
+        }
+    }
+
+    if (fpa->chips->n == 1 && numGoodChips == 0) {
+        psError(PSASTRO_ERR_UNKNOWN, false, "Failed to fit single chip.");
+        return false;
+    }
+
+    if (!psastroFixChips (config, recipe)) {
+        psError(PSASTRO_ERR_UNKNOWN, false, "failed to align problematic chips");
+        return false;
+    }
+
+    psFree (view);
+    return true;
+}
+
+/* coordinate frame hierachy
+   pixels (on a given readout)
+   cell
+   chip
+   FP (focal plane)
+   TP (tangent plane)
+   sky (ra, dec)
+*/
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroChooseRefstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroChooseRefstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroChooseRefstars.c	(revision 22322)
@@ -0,0 +1,133 @@
+# include "psastroInternal.h"
+
+# define ESCAPE { \
+  psError(PS_ERR_UNKNOWN, false, "Failure in psastroChooseRefstars"); \
+  psFree (index); \
+  psFree (view); \
+  return false; \
+}
+
+bool psastroChooseRefstars (pmConfig *config, psArray *refs) {
+
+    bool status;
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+
+    // 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;
+    }
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    if (!input) {
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find input data!\n");
+        return false;
+    }
+
+    // extra field fraction to add
+    double fieldPadding = psMetadataLookupF32 (&status, recipe, "PSASTRO.FIELD.PADDING");
+    if (!status) fieldPadding = 0.0;
+
+    bool matchLumFunc = psMetadataLookupBool (&status, recipe, "PSASTRO.MATCH.LUMFUNC");
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+    pmFPA *fpa = input->fpa;
+
+    // sort by mag
+    psVector *index = psArraySortIndex (NULL, refs, psastroSortByMag);
+
+    int nMax = psMetadataLookupS32 (&status, recipe, "PSASTRO.MAX.NREF");
+
+    // de-activate all files except PSASTRO.REFSTARS
+    pmFPAfileActivate (config->files, false, NULL);
+    pmFPAfileActivate (config->files, true, "PSASTRO.OUT.REFSTARS");
+
+    // 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
+            // XXX there can only be one readout per chip in astrometry, right?
+            while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+                if (! readout->data_exists) { continue; }
+
+		psRegion *extent = pmReadoutExtent (readout);
+		if (!extent) {
+		    psError(PSASTRO_ERR_CONFIG, true, "Can't find readout size!\n");
+		    return NULL;
+		}
+
+		int Nx = abs(extent->x1 - extent->x0);
+		int Ny = abs(extent->y1 - extent->y0);
+
+                float minX = -fieldPadding*Nx;
+                float maxX = (1+fieldPadding)*Nx;
+                float minY = -fieldPadding*Ny;
+                float maxY = (1+fieldPadding)*Ny;
+
+                // the refstars is a subset within range of this chip
+                psArray *refstars = psArrayAllocEmpty (100);
+
+                // select the reference objects within range of this readout
+                // project the reference objects to this chip
+                for (int i = 0; i < refs->n; i++) {
+                    pmAstromObj *ref = pmAstromObjCopy(refs->data[index->data.S32[i]]);
+
+                    psProject (ref->TP, ref->sky, fpa->toSky);
+                    psPlaneTransformApply (ref->FP, fpa->fromTPA, ref->TP);
+                    psPlaneTransformApply (ref->chip, chip->fromFPA, ref->FP);
+
+                    // limit the X,Y range of the refs to the selected chip
+                    if (ref->chip->x < minX) goto skip;
+                    if (ref->chip->x > maxX) goto skip;
+                    if (ref->chip->y < minY) goto skip;
+                    if (ref->chip->y > maxY) goto skip;
+
+                    psArrayAdd (refstars, 100, ref);
+                skip:
+                    psFree (ref);
+
+		    if (nMax && (refstars->n >= nMax)) break;
+                }
+                psTrace ("psastro", 4, "Added %ld refstars\n", refstars->n);
+
+		psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSASTRO.REFSTARS", PS_DATA_ARRAY, "astrometry matches", refstars);
+		psFree (refstars);
+		psFree (extent);
+
+		if (matchLumFunc) {
+		    // in this case, no PSASTRO.REFSTARS is added to readout->analysis
+		    if (!psastroRefstarSubset (readout)) {
+			psError(PSASTRO_ERR_DATA, false, "Can't determine an appropriate refstar subset\n");
+			psFree (index);
+			psFree (view);
+			return false;
+		    }
+		}
+            }
+        }
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+
+    // activate all files except PSASTRO.OUTPUT
+    pmFPAfileActivate (config->files, true, NULL);
+    pmFPAfileActivate (config->files, false, "PSASTRO.OUT.REFSTARS");
+
+    bool onlyRefstars = psMetadataLookupBool (&status, recipe, "PSASTRO.ONLY.REFSTARS");
+    if (onlyRefstars) exit (0);
+
+    psFree (index);
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroCleanup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroCleanup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroCleanup.c	(revision 22322)
@@ -0,0 +1,17 @@
+# include "psastroStandAlone.h"
+
+void psastroCleanup (pmConfig *config) {
+
+    psFree (config);
+
+    psTimerStop ();
+    psMemCheckCorruption (stderr, true);
+    pmModelClassCleanup ();
+    psTimeFinalize ();
+    pmConceptsDone ();
+    pmConfigDone ();
+    fprintf (stderr, "found %d leaks at %s\n", psMemCheckLeaks (0, NULL, stdout, false), "psastro");
+    // fprintf (stderr, "found %d leaks at %s\n", psMemCheckLeaks (0, NULL, NULL, false), "psastro");
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroConvert.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroConvert.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroConvert.c	(revision 22322)
@@ -0,0 +1,173 @@
+# include "psastroInternal.h"
+// leak free 2006.04.27
+
+bool psastroConvertFPA (pmFPA *fpa, psMetadata *recipe) {
+
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    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; }
+
+        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; }
+
+                psastroConvertReadout (readout, recipe);
+            }
+        }
+    }
+    psFree (view);
+    return true;
+}
+
+// pass/apply the WCS information?
+bool psastroConvertReadout (pmReadout *readout, psMetadata *recipe) {
+
+    bool status;
+
+    // PSPHOT.SOURCES carries the pmSource objects (from psphot analysis or loaded externally)
+    psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
+    if (sources == NULL)
+        return false;
+
+    // convert the pmSource objects into pmAstromObj objects (drop !STAR and SATSTAR?)
+    psArray *inStars = pmSourceToAstromObj (sources);
+
+    // sort in ascending magnitude order
+    // psArraySort (inStars, psastroSortByMag);
+    // psVector *index = psArraySortIndex (sources, pmSourceSortBySN);
+    psVector *index = psArraySortIndex (NULL, inStars, psastroSortByMag);
+
+    // XXX need to exit gracefully is inStars->n is 0 (or 1?)
+
+    // we are going to select the brighter Nmax subset for astrometry 
+    int nMax = psMetadataLookupS32 (&status, recipe, "PSASTRO.MAX.NRAW");
+    if (!status || !nMax) nMax = inStars->n;
+
+    // we are going to select the brighter Nmax subset for astrometry 
+    float iMagMax = psMetadataLookupF32 (&status, recipe, "PSASTRO.MAX.INST.MAG.RAW");
+    float iMagMin = psMetadataLookupF32 (&status, recipe, "PSASTRO.MIN.INST.MAG.RAW");
+
+    // we are going to select the brighter Nmax subset for astrometry 
+    pmSourceMode skip = PM_SOURCE_MODE_DEFAULT;
+    char *ignoreList = psMetadataLookupStr (&status, recipe, "PSASTRO.IGNORE");
+    if (ignoreList != NULL) {
+      psArray *list = psStringSplitArray (ignoreList, ",", false);
+      for (int i = 0; i < list->n; i++) {
+	pmSourceMode mode = pmSourceModeFromString (list->data[i]);
+	if (mode == PM_SOURCE_MODE_DEFAULT) {
+	  psWarning ("unknown source mode in PSASTRO.IGNORE, skipping");
+	  continue;
+	}
+	skip |= mode;
+      }
+      psFree (list);
+    }
+
+    // choose the first nMax sources
+    int j = 0;
+    psArray *rawStars = psArrayAlloc (PS_MIN (nMax, inStars->n));
+
+    float mMin = +100.0;
+    float mMax = -100.0;
+    int nModeSkip = 0;
+    int nFaintSkip = 0;
+    int nBrightSkip = 0;
+    int nInfSkip = 0;
+
+    for (int i = 0; (i < inStars->n) && (j < rawStars->n); i++) {
+	int n = index->data.S32[i];
+	pmSource *source = sources->data[n];
+	// XXX this needs to be flexible
+	if (source->mode & skip) { 
+	  nModeSkip ++;
+	  continue;
+	}
+	if ((iMagMax != 0.0) && (source->psfMag > iMagMax)) {
+	  nFaintSkip ++;
+	  continue;
+	}
+	if ((iMagMin != 0.0) && (source->psfMag < iMagMin)) {
+	  nBrightSkip ++;
+	  continue;
+	}
+	if (!isfinite(source->psfMag)) {
+	  nInfSkip ++;
+	  continue;
+	}
+	mMin = PS_MIN (mMin, source->psfMag);
+	mMax = PS_MAX (mMax, source->psfMag);
+	rawStars->data[j] = psMemIncrRefCounter (inStars->data[n]);
+	j++;
+    }
+    rawStars->n = j;
+
+    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSASTRO.RAWSTARS", PS_DATA_ARRAY, "astrometry objects", rawStars);
+    psLogMsg ("psastro", 4, "loaded %ld sources, using %ld of %ld good sources (inst mag: %f to %f)\n", sources->n, rawStars->n, inStars->n, mMin, mMax);
+    psLogMsg ("psastro", 4, "skip reasons: mode: %d, faint: %d, bright: %d, inf: %d\n", nModeSkip, nFaintSkip, nBrightSkip, nInfSkip);
+
+    psFree (index);
+    psFree (inStars);
+    psFree (rawStars);
+
+    return true;
+}
+
+// select a magnitude range?
+psArray *pmSourceToAstromObj (psArray *sources) {
+
+    psArray *objects = psArrayAllocEmpty (sources->n);
+
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+
+        // only accept the PSF sources?
+	// XXX drop SATSTAR?
+        // if (source->type != PM_SOURCE_TYPE_STAR) continue;
+
+        pmModel *model = source->modelPSF;
+        if (model == NULL) continue;
+
+        psF32 *PAR = model->params->data.F32;
+
+        pmAstromObj *obj = pmAstromObjAlloc ();
+
+        // is the source magnitude calibrated in any sense?
+        obj->pix->x = PAR[PM_PAR_XPOS];
+        obj->pix->y = PAR[PM_PAR_YPOS];
+        obj->Mag = source->psfMag;
+
+        // XXX do we have the information giving the readout and cell offset?
+        // for the moment, assume chip == cell == readout
+        *obj->cell = *obj->pix;
+        *obj->chip = *obj->cell;
+
+        psArrayAdd (objects, 100, obj);
+        psFree (obj);
+    }
+    return objects;
+}
+
+// sort by Mag (ascending)
+int psastroSortByMag (const void *a, const void *b)
+{
+    const pmAstromObj *A = a;
+    const pmAstromObj *B = b;
+
+    psF32 mA = (isfinite(A->Mag)) ? A->Mag : FLT_MAX;
+    psF32 mB = (isfinite(B->Mag)) ? B->Mag : FLT_MAX;
+
+    psF32 diff = mA - mB;
+    if (diff > +FLT_EPSILON) return (+1);
+    if (diff < -FLT_EPSILON) return (-1);
+    return (0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroDataLoad.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroDataLoad.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroDataLoad.c	(revision 22322)
@@ -0,0 +1,75 @@
+# include "psastroStandAlone.h"
+// this loop loads the data from the input files and selects the
+// brighter stars for astrometry
+// at the end of this function, the complete stellar data is loaded
+// into the correct fpa structure locations (readout.analysis:PSPHOT.SOURCES)
+
+# define ESCAPE { \
+  psError(PS_ERR_UNKNOWN, false, "Failure in psastroDataLoad"); \
+  psFree (view); \
+  return false; \
+}
+  
+// all of the different astrometry analysis modes use the same data load loop
+bool psastroDataLoad (pmConfig *config) {
+
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    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;
+    }
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    if (!input) {
+	psError(PSASTRO_ERR_CONFIG, true, "Can't find input data!\n");
+	return false;
+    }
+
+    // de-activate all files except PSASTRO.INPUT
+    pmFPAfileActivate (config->files, false, NULL);
+    pmFPAfileActivate (config->files, true, "PSASTRO.INPUT");
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // files associated with the science image
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+
+    while ((chip = pmFPAviewNextChip (view, input->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 (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+
+	while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
+            psTrace ("psastro", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) { continue; }
+	    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+
+	    // process each of the readouts
+	    while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
+		if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+		if (!readout->data_exists) { continue; }
+
+		if (!psastroConvertReadout (readout, recipe)) ESCAPE;
+
+		if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+	    }
+	    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+	}
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+
+    psLogMsg ("psastro", 3, "load data : %f sec\n", psTimerMark ("psastro"));
+
+    psFree (view);
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroDataSave.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroDataSave.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroDataSave.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "psastroInternal.h"
+
+# define ESCAPE { \
+  psError(PS_ERR_UNKNOWN, false, "Failure in psastroDataSave"); \
+  psFree (view); \
+  return false; \
+}
+  
+// this loop saves the photometry/astrometry data files
+bool psastroDataSave (pmConfig *config) {
+
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    // 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;
+    }
+
+    // select the output data sources
+    pmFPAfile *output = psMetadataLookupPtr (NULL, config->files, "PSASTRO.OUTPUT");
+    if (!output) {
+	psError(PSASTRO_ERR_CONFIG, true, "Can't find or interpret output file rule PSASTRO.OUTPUT!\n");
+	return false;
+    }
+
+    // de-activate all files except PSASTRO.OUTPUT
+    pmFPAfileActivate (config->files, false, NULL);
+    pmFPAfileActivate (config->files, true, "PSASTRO.OUTPUT");
+    pmFPAfileActivate (config->files, true, "PSASTRO.OUT.ASTROM");
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // open/load files as needed
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+
+    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 || !chip->file_exists) { continue; }
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+
+	while ((cell = pmFPAviewNextCell (view, output->fpa, 1)) != NULL) {
+            psTrace ("psastro", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) { continue; }
+	    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+
+	    // process each of the readouts
+	    while ((readout = pmFPAviewNextReadout (view, output->fpa, 1)) != NULL) {
+		if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+		if (!readout->data_exists) { continue; }
+
+		if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+	    }
+	    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+	}
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+
+    // Write out summary statistics
+    if (!psastroMetadataStats (config)) ESCAPE;
+
+    // activate all files except PSASTRO.OUTPUT
+    pmFPAfileActivate (config->files, true, NULL);
+    pmFPAfileActivate (config->files, false, "PSASTRO.OUTPUT");
+
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroDefineFiles.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroDefineFiles.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroDefineFiles.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include "psastroInternal.h"
+
+bool psastroDefineFiles (pmConfig *config, pmFPAfile *input) {
+
+    // these calls bind the I/O handle to the specified fpa
+    bool status;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+	psError(PSASTRO_ERR_CONFIG, false, "Can't find PSASTRO recipe!\n");
+	return false;
+    }
+
+    pmFPAfile *output = pmFPAfileDefineOutput (config, input->fpa, "PSASTRO.OUTPUT");
+    if (!output) {
+	psError(PSASTRO_ERR_CONFIG, false, "Failed to build FPA from PSASTRO.INPUT");
+	return false;
+    }
+    output->save = true;
+
+    bool fixChips = psMetadataLookupBool (&status, config->arguments, "PSASTRO.FIX.CHIPS");
+    if (!status) {
+	fixChips = psMetadataLookupBool (&status, recipe, "PSASTRO.FIX.CHIPS");
+    }
+    bool useModel = psMetadataLookupBool (&status, config->arguments, "PSASTRO.USE.MODEL");
+    if (!status) {
+	fixChips = psMetadataLookupBool (&status, recipe, "PSASTRO.USE.MODEL");
+    }
+    if (fixChips || useModel) {
+        if (!psastroDefineFile (config, input->fpa, "PSASTRO.MODEL", "ASTROM.MODEL", PM_FPA_FILE_ASTROM_MODEL, PM_DETREND_TYPE_ASTROM)) {
+            psError (PS_ERR_IO, false, "Can't find an astrometry model file");
+            return NULL;
+        }
+    }
+
+    bool saveRefstars = psMetadataLookupBool (&status, config->arguments, "PSASTRO.SAVE.REFSTARS");
+    if (!status) {
+      saveRefstars = psMetadataLookupBool (&status, recipe, "PSASTRO.SAVE.REFSTARS");
+    }
+    if (saveRefstars) {
+	// look for the file in the camera config table
+	pmFPAfile *file = pmFPAfileDefineOutputFromFile  (config, input, "PSASTRO.OUT.REFSTARS");
+	if (!file) {
+	    psError (PS_ERR_IO, false, "Can't find the astrometry refstars file definition");
+	    return NULL;
+	}
+	if (file->type != PM_FPA_FILE_ASTROM_REFSTARS) {
+	    psError(PS_ERR_IO, true, "%s is not of type %s", "PSASTRO.OUT.REFSTARS", pmFPAfileStringFromType (PM_FPA_FILE_ASTROM_REFSTARS));
+	    return NULL;
+	}
+	file->save = true;
+    }
+
+
+# if (0)
+    // optionally save output plots
+    if (!pmFPAfileDefineOutput (config, input->fpa, "SOURCE.PLOT.MOMENTS")) {
+	psTrace ("psphot", 3, "Cannot find a rule for SOURCE.PLOT.MOMENTS");
+    }
+    if (!pmFPAfileDefineOutput (config, input->fpa, "SOURCE.PLOT.PSFMODEL")) {
+	psTrace ("psphot", 3, "Cannot find a rule for SOURCE.PLOT.PSFMODEL");
+    }
+# endif
+
+    return true;
+}
+
+bool psastroDefineFile (pmConfig *config, pmFPA *input, char *filerule, char *argname, pmFPAfileType fileType, pmDetrendType detrendType) {
+
+    bool status;
+    pmFPAfile *file;
+
+    // look for the file on the argument list
+    file = pmFPAfileDefineFromArgs  (&status, config, filerule, argname);
+    if (!status) {
+	psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+	return false;
+    }
+    if (file) {
+	if (file->type != fileType) {
+	    psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType (fileType));
+	    return false;
+	}
+	return true;
+    }
+
+    // look for the file in the camera config table
+    file = pmFPAfileDefineFromConf  (&status, config, filerule);
+    if (!status) {
+	psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+	return false;
+    }
+    if (file) {
+	if (file->type != fileType) {
+	    psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType (fileType));
+	    return false;
+	}
+	return true;
+    }
+
+    // look for the file to be loaded from the detrend database
+    file = pmFPAfileDefineFromDetDB (&status, config, filerule, input, detrendType);
+    if (!status) {
+	psError (PS_ERR_UNKNOWN, false, "failed to load file definition");
+	return false;
+    }
+    if (file) {
+	if (file->type != fileType) {
+	    psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType (fileType));
+	    return false;
+	}
+	return true;
+    }
+    return false;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroDemoDump.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroDemoDump.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroDemoDump.c	(revision 22322)
@@ -0,0 +1,297 @@
+# include "psastroInternal.h"
+
+// this function is used for test purposes (-trace psastro.dump.psastroAstromGuess 1)
+bool psastroDumpStars (psArray *stars, char *filename) {
+
+    FILE *f = fopen (filename, "w");
+
+    for (int i = 0; i < stars->n; i++) {
+	pmAstromObj *obj = stars->data[i];
+
+	// write out the upward projections
+	fprintf (f, "%d  %f %f  %f  %f %f  %f %f  %f %f\n", i,
+		 obj->sky->r, obj->sky->d, obj->Mag, 
+		 obj->TP->x, obj->TP->y, 
+		 obj->FP->x, obj->FP->y, 
+		 obj->chip->x, obj->chip->y);
+    }
+    fclose (f);
+    return true;
+}
+
+// this function is used for test purposes (-trace psastro.dump.psastroAstromGuess 1)
+bool psastroDumpRawstars (psArray *rawstars, pmFPA *fpa, pmChip *chip) {
+
+    char *filename = NULL;
+    char *chipname = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
+
+    psStringAppend (&filename, "rawstars.up.%s.dat", chipname);
+    FILE *f1 = fopen (filename, "w");
+    psFree (filename);
+    filename = NULL;
+
+    psStringAppend (&filename, "rawstars.dn.%s.dat", chipname);
+    FILE *f2 = fopen (filename, "w");
+    psFree (filename);
+    filename = NULL;
+
+    for (int i = 0; i < rawstars->n; i++) {
+	pmAstromObj *raw = rawstars->data[i];
+
+	psPlane *fp = psPlaneAlloc();
+	psPlane *tp = psPlaneAlloc();
+	psPlane *ch = psPlaneAlloc();
+			
+	psProject (tp, raw->sky, fpa->toSky);
+	psPlaneTransformApply (fp, fpa->fromTPA, tp);
+	psPlaneTransformApply (ch, chip->fromFPA, fp);
+			
+	// write out the upward projections
+	fprintf (f1, "%d  %f %f  %f  %f %f  %f %f  %f %f\n", i,
+		 raw->sky->r, raw->sky->d, raw->Mag, 
+		 raw->TP->x, raw->TP->y, 
+		 raw->FP->x, raw->FP->y, 
+		 raw->chip->x, raw->chip->y);
+		
+	// write out the downward projections
+	fprintf (f2, "%d  %f %f  %f  %f %f  %f %f  %f %f\n", i,
+		 raw->sky->r, raw->sky->d, raw->Mag, 
+		 tp->x, tp->y, 
+		 fp->x, fp->y, 
+		 ch->x, ch->y);
+		
+	psFree (fp);
+	psFree (tp);
+	psFree (ch);
+    }
+
+    fclose (f1);
+    fclose (f2);
+    return true;
+}
+
+bool psastroDumpMatchedStars (char *filename, psArray *rawstars, psArray *refstars, psArray *match) {
+    
+    FILE *f = fopen (filename, "w");
+
+    for (int i = 0; i < match->n; i++) {
+        pmAstromMatch *pair = match->data[i];
+        pmAstromObj *raw = rawstars->data[pair->raw];
+        pmAstromObj *ref = refstars->data[pair->ref];
+
+	fprintf (f, "%f %f  %f %f  %f %f  %f %f  %f   |   ",  
+		 DEG_RAD*raw->sky->r, DEG_RAD*raw->sky->d, 
+		 raw->TP->x, raw->TP->y, 
+		 raw->FP->x, raw->FP->y, 
+		 raw->chip->x, raw->chip->y, raw->Mag);
+
+	fprintf (f, "%f %f  %f %f  %f %f  %f %f  %f\n", 
+		 DEG_RAD*ref->sky->r, DEG_RAD*ref->sky->d, 
+		 ref->TP->x, ref->TP->y, 
+		 ref->FP->x, ref->FP->y, 
+		 ref->chip->x, ref->chip->y, ref->Mag);
+    }
+    fclose (f);
+
+    return true;
+}
+
+// this function is used for test purposes (-trace psastro.dump.psastroLoadRefstars 1)
+bool psastroDumpRefstars (psArray *refstars, char *filename) {
+
+    FILE *f = fopen (filename, "w");
+
+    for (int i = 0; i < refstars->n; i++) {
+	pmAstromObj *ref = refstars->data[i];
+
+	// write out the refstar data
+	fprintf (f, "%d  %f %f  %f\n", i,
+		 ref->sky->r, ref->sky->d, ref->Mag);
+    }
+
+    fclose (f);
+    return true;
+}
+
+bool psastroDumpMatches (pmFPA *fpa, char *filename) {
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    FILE *f = fopen (filename, "w");
+
+    // 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;
+	
+	char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
+
+	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
+	    // XXX there can only be one readout per chip, right?
+	    while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+		if (! readout->data_exists) continue;
+
+		// select the raw objects for this readout
+		psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+		if (rawstars == NULL) continue;
+
+		// select the raw objects for this readout
+		psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+		if (refstars == NULL) continue;
+		psTrace ("psastro", 4, "Trying %ld refstars\n", refstars->n);
+
+		psArray *matches = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
+		if (matches == NULL) continue;
+
+		for (int i = 0; i < matches->n; i++) {
+		    pmAstromMatch *match = matches->data[i];
+
+		    pmAstromObj *raw = rawstars->data[match->raw];
+		    fprintf (f, "%s  %f %f  %f %f  %f %f  %f %f  %f   |   ",  
+			     chipName, DEG_RAD*raw->sky->r, DEG_RAD*raw->sky->d, 
+			     raw->TP->x, raw->TP->y, 
+			     raw->FP->x, raw->FP->y, 
+			     raw->chip->x, raw->chip->y, raw->Mag);
+
+		    pmAstromObj *ref = refstars->data[match->ref];
+		    fprintf (f, "%f %f  %f %f  %f %f  %f %f  %f\n", 
+			     DEG_RAD*ref->sky->r, DEG_RAD*ref->sky->d, 
+			     ref->TP->x, ref->TP->y, 
+			     ref->FP->x, ref->FP->y, 
+			     ref->chip->x, ref->chip->y, ref->Mag);
+		}
+	    }
+	}
+    }
+    fclose (f);
+    psFree (view);
+    return true;
+}
+
+// this function is used for test purposes (-trace psastro.dump 1)
+bool psastroDumpGradients (psArray *gradients, char *filename) {
+
+    FILE *f = fopen (filename, "w");
+
+    for (int i = 0; i < gradients->n; i++) {
+	pmAstromGradient *gradient = gradients->data[i];
+
+	// write out the refstar data
+	fprintf (f, "%d  %f %f   %f %f  %f %f\n", i,
+		 gradient->FP.x, gradient->FP.y, 
+		 gradient->dTPdL.x, gradient->dTPdL.y, 
+		 gradient->dTPdM.x, gradient->dTPdM.y);
+    }
+
+    fclose (f);
+    return true;
+}
+
+bool psastroDumpCorners (char *filenameU, char *filenameD, pmFPA *fpa) {
+
+  // XXX test output of chip corners based on model
+  FILE *fu = fopen (filenameU, "w");
+  FILE *fd = fopen (filenameD, "w");
+
+  pmFPAview *view = pmFPAviewAlloc (0);
+
+  float fpaAngle = PM_DEG_RAD * atan2 (fpa->toTPA->y->coeff[1][0], fpa->toTPA->x->coeff[1][0]);
+
+  fprintf (fu, "# boresite: %f, %f @ %f\n", fpa->toSky->R*PS_DEG_RAD, fpa->toSky->D*PS_DEG_RAD, fpaAngle);
+  fprintf (fd, "# boresite: %f, %f @ %f\n", fpa->toSky->R*PS_DEG_RAD, fpa->toSky->D*PS_DEG_RAD, fpaAngle);
+
+  pmChip *chip = NULL;
+  while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
+        if (!chip->process || !chip->file_exists || !chip->data_exists) { continue; }
+
+	// XXX write out the four corners for a test
+	psRegion *region = pmChipPixels (chip);
+	psPlane ptCP, ptFP, ptTP;
+	psSphere ptSky;
+
+	// UP 0,0
+	ptCP.x = region->x0; ptCP.y = region->y0;
+	psPlaneTransformApply (&ptFP, chip->toFPA, &ptCP);
+	psPlaneTransformApply (&ptTP, fpa->toTPA, &ptFP);
+	psDeproject (&ptSky, &ptTP, fpa->toSky);
+	fprintf (fu, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+
+	// DOWN 0,0
+	psProject (&ptTP, &ptSky, fpa->toSky);
+	psPlaneTransformApply (&ptFP, fpa->fromTPA, &ptTP);
+	psPlaneTransformApply (&ptCP, chip->fromFPA, &ptFP);
+	fprintf (fd, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+
+	// UP 1,0
+	ptCP.x = region->x1; ptCP.y = region->y0;
+	psPlaneTransformApply (&ptFP, chip->toFPA, &ptCP);
+	psPlaneTransformApply (&ptTP, fpa->toTPA, &ptFP);
+	psDeproject (&ptSky, &ptTP, fpa->toSky);
+	fprintf (fu, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+	fprintf (fu, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+
+	// DOWN 1,0
+	psProject (&ptTP, &ptSky, fpa->toSky);
+	psPlaneTransformApply (&ptFP, fpa->fromTPA, &ptTP);
+	psPlaneTransformApply (&ptCP, chip->fromFPA, &ptFP);
+	fprintf (fd, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+	fprintf (fd, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+
+	// UP 1,1
+	ptCP.x = region->x1; ptCP.y = region->y1;
+	psPlaneTransformApply (&ptFP, chip->toFPA, &ptCP);
+	psPlaneTransformApply (&ptTP, fpa->toTPA, &ptFP);
+	psDeproject (&ptSky, &ptTP, fpa->toSky);
+	fprintf (fu, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+	fprintf (fu, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+
+	// DOWN 1,1
+	psProject (&ptTP, &ptSky, fpa->toSky);
+	psPlaneTransformApply (&ptFP, fpa->fromTPA, &ptTP);
+	psPlaneTransformApply (&ptCP, chip->fromFPA, &ptFP);
+	fprintf (fd, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+	fprintf (fd, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+
+	// UP 0,1
+	ptCP.x = region->x0; ptCP.y = region->y1;
+	psPlaneTransformApply (&ptFP, chip->toFPA, &ptCP);
+	psPlaneTransformApply (&ptTP, fpa->toTPA, &ptFP);
+	psDeproject (&ptSky, &ptTP, fpa->toSky);
+	fprintf (fu, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+	fprintf (fu, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+
+	// DOWN 0,1
+	psProject (&ptTP, &ptSky, fpa->toSky);
+	psPlaneTransformApply (&ptFP, fpa->fromTPA, &ptTP);
+	psPlaneTransformApply (&ptCP, chip->fromFPA, &ptFP);
+	fprintf (fd, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+	fprintf (fd, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+
+	// UP 0,0
+	ptCP.x = region->x0; ptCP.y = region->y0;
+	psPlaneTransformApply (&ptFP, chip->toFPA, &ptCP);
+	psPlaneTransformApply (&ptTP, fpa->toTPA, &ptFP);
+	psDeproject (&ptSky, &ptTP, fpa->toSky);
+	fprintf (fu, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+
+	// DOWN 0,0
+	psProject (&ptTP, &ptSky, fpa->toSky);
+	psPlaneTransformApply (&ptFP, fpa->fromTPA, &ptTP);
+	psPlaneTransformApply (&ptCP, chip->fromFPA, &ptFP);
+	fprintf (fd, "%10.6f %10.6f  %8.1f %8.1f  %8.1f %8.1f  %8.1f %8.1f\n", ptSky.r, ptSky.d, ptTP.x, ptTP.y, ptFP.x, ptFP.y, ptCP.x, ptCP.y);
+
+	psFree (region);
+  }
+
+  fclose (fu);
+  fclose (fd);
+  psFree (view);
+  return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroDemoPlot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroDemoPlot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroDemoPlot.c	(revision 22322)
@@ -0,0 +1,514 @@
+# include "psastroInternal.h"
+
+# if (HAVE_KAPA)
+# include <kapa.h>
+
+bool pmKapaPlotVectorTriple_AutoLimitsZscale_OpenGraph (int kapa, Graphdata *graphdata, psVector *xVec, psVector *yVec, psVector *zVec, bool increasing);
+
+bool psastroPlotRawstars (psArray *rawstars, pmFPA *fpa, pmChip *chip, psMetadata *recipe)
+{
+    Graphdata graphdata;
+    KapaSection section;
+
+    int kapa = pmKapaOpen (true);
+    if (kapa == -1) {
+        psError(PS_ERR_UNKNOWN, true, "failure to open kapa");
+        return false;
+    }
+
+    bool status = false;
+    float iMagMin = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.INST.MAG.MIN");
+    float iMagMax = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.INST.MAG.MAX");
+
+    KapaResize (kapa, 1000, 1000);
+    KapaInitGraph (&graphdata);
+    KapaClearPlots (kapa);
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 7;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+
+    section.dx = 0.5;
+    section.dy = 0.5;
+
+    psVector *xVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
+    psVector *yVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
+    psVector *zVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
+
+    section.x = 0.0;
+    section.y = 0.5;
+    section.name = NULL;
+    psStringAppend (&section.name, "a0");
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    int n = 0;
+    for (int i = 0; i < rawstars->n; i++) {
+        pmAstromObj *raw = rawstars->data[i];
+        if (!isfinite(raw->Mag)) continue;
+        if (raw->Mag < iMagMin) continue;
+        if (raw->Mag > iMagMax) continue;
+
+        xVec->data.F32[n] = raw->chip->x;
+        yVec->data.F32[n] = raw->chip->y;
+        zVec->data.F32[n] = raw->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa, &graphdata, xVec, yVec, zVec, false);
+
+    section.x = 0.5;
+    section.y = 0.5;
+    section.name = NULL;
+    psStringAppend (&section.name, "a1");
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    n = 0;
+    for (int i = 0; i < rawstars->n; i++) {
+        pmAstromObj *raw = rawstars->data[i];
+        if (!isfinite(raw->Mag)) continue;
+        if (raw->Mag < iMagMin) continue;
+        if (raw->Mag > iMagMax) continue;
+
+        xVec->data.F32[n] = raw->FP->x;
+        yVec->data.F32[n] = raw->FP->y;
+        zVec->data.F32[n] = raw->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa, &graphdata, xVec, yVec, zVec, false);
+
+    section.x = 0.0;
+    section.y = 0.0;
+    section.name = NULL;
+    psStringAppend (&section.name, "a2");
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    n = 0;
+    for (int i = 0; i < rawstars->n; i++) {
+        pmAstromObj *raw = rawstars->data[i];
+        if (!isfinite(raw->Mag)) continue;
+        if (raw->Mag < iMagMin) continue;
+        if (raw->Mag > iMagMax) continue;
+
+        xVec->data.F32[n] = raw->TP->x;
+        yVec->data.F32[n] = raw->TP->y;
+        zVec->data.F32[n] = raw->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+
+    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa, &graphdata, xVec, yVec, zVec, false);
+
+    section.x = 0.5;
+    section.y = 0.0;
+    section.name = NULL;
+    psStringAppend (&section.name, "a3");
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    n = 0;
+    for (int i = 0; i < rawstars->n; i++) {
+        pmAstromObj *raw = rawstars->data[i];
+        if (!isfinite(raw->Mag)) continue;
+        if (raw->Mag < iMagMin) continue;
+        if (raw->Mag > iMagMax) continue;
+
+        xVec->data.F32[n] = DEG_RAD*raw->sky->r;
+        yVec->data.F32[n] = DEG_RAD*raw->sky->d;
+        zVec->data.F32[n] = raw->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa, &graphdata, xVec, yVec, zVec, false);
+
+    // flip x (East increase to left)
+    SWAP (graphdata.xmin, graphdata.xmax);
+    KapaSetLimits (kapa, &graphdata);
+
+    // pause and wait for user input:
+    // continue, save (provide name), ??
+    char key[10], name[80];
+    fprintf (stdout, "(s)ave plot or [c]ontinue? ");
+    if (!fgets(key, 8, stdin)) {
+        psWarning("Unable to read option");
+    } else if (key[0] == 's') {
+        fprintf (stdout, "enter plot name [rawstars.png]: ");
+        if (fscanf(stdin, "%s", name) != 1) {
+            psWarning("Unable to read plot name");
+        } else if (!strcmp (name, "")) {
+            strcpy (name, "rawstars.png");
+        }
+        KapaPNG (kapa, name);
+    }
+
+    psFree (xVec);
+    psFree (yVec);
+    psFree (zVec);
+    return true;
+}
+
+bool psastroPlotRefstars (psArray *refstars, psMetadata *recipe)
+{
+    Graphdata graphdata;
+
+    int kapa = pmKapaOpen (true);
+    if (kapa == -1) {
+        psError(PS_ERR_UNKNOWN, true, "failure to open kapa");
+        return false;
+    }
+
+    bool status = false;
+    float rMagMin = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.REF.MAG.MIN");
+    float rMagMax = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.REF.MAG.MAX");
+
+    KapaResize (kapa, 1000, 1000);
+    KapaInitGraph (&graphdata);
+    KapaClearSections (kapa);
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 7;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+
+    psVector *xVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
+    psVector *yVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
+    psVector *zVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
+
+    int n = 0;
+    for (int i = 0; i < refstars->n; i++) {
+        pmAstromObj *ref = refstars->data[i];
+        if (!isfinite(ref->Mag)) continue;
+        if (ref->Mag > rMagMax) continue;
+        if (ref->Mag < rMagMin) continue;
+
+        xVec->data.F32[n] = DEG_RAD*ref->sky->r;
+        yVec->data.F32[n] = DEG_RAD*ref->sky->d;
+        zVec->data.F32[n] = ref->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa, &graphdata, xVec, yVec, zVec, false);
+
+    // flip x (East increase to left)
+    SWAP (graphdata.xmin, graphdata.xmax);
+    KapaSetLimits (kapa, &graphdata);
+
+    // pause and wait for user input:
+    // continue, save (provide name), ??
+    char key[10], name[80];
+    fprintf (stdout, "(s)ave plot or [c]ontinue? ");
+    if (!fgets(key, 8, stdin)) {
+        psWarning("Couldn't read anything.");
+    } else if (key[0] == 's') {
+        fprintf (stdout, "enter plot name [refstars.png]: ");
+        if (fscanf (stdin, "%s", name) != 1) {
+            psWarning("Unable to read name");
+        } else if (!strcmp (name, "")) {
+            strcpy (name, "refstars.png");
+        }
+        KapaPNG (kapa, name);
+    }
+
+    psFree (xVec);
+    psFree (yVec);
+    psFree (zVec);
+    return true;
+}
+
+bool psastroPlotOneChipFit (psArray *rawstars, psArray *refstars, psArray *match, psMetadata *recipe) {
+
+    Graphdata graphdata;
+    KapaSection section;
+
+    int kapa = pmKapaOpen (true);
+    if (kapa == -1) {
+        psError(PS_ERR_UNKNOWN, true, "failure to open kapa");
+        return false;
+    }
+
+    bool status = false;
+    float iMagMin = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.INST.MAG.MIN");
+    float iMagMax = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.INST.MAG.MAX");
+    float rMagMin = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.REF.MAG.MIN");
+    float rMagMax = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.REF.MAG.MAX");
+
+    KapaResize (kapa, 1000, 1000);
+    KapaInitGraph (&graphdata);
+    KapaClearPlots (kapa);
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 7;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+
+    section.dx = 0.5;
+    section.dy = 0.5;
+
+    psVector *xVec = psVectorAlloc (match->n, PS_TYPE_F32);
+    psVector *yVec = psVectorAlloc (match->n, PS_TYPE_F32);
+    psVector *zVec = psVectorAlloc (match->n, PS_TYPE_F32);
+
+    // X vs dX
+    section.x = 0.0;
+    section.y = 0.5;
+    section.name = NULL;
+    psStringAppend (&section.name, "a0");
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    int n = 0;
+    for (int i = 0; i < match->n; i++) {
+        pmAstromMatch *pair = match->data[i];
+        pmAstromObj *raw = rawstars->data[pair->raw];
+        pmAstromObj *ref = refstars->data[pair->ref];
+
+        if (raw->Mag < iMagMin) continue;
+        if (raw->Mag > iMagMax) continue;
+        if (ref->Mag < rMagMin) continue;
+        if (ref->Mag > rMagMax) continue;
+
+        xVec->data.F32[n] = raw->chip->x;
+        yVec->data.F32[n] = raw->chip->x - ref->chip->x;
+        zVec->data.F32[n] = raw->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa, &graphdata, xVec, yVec, zVec, false);
+
+    // X vs dY
+    section.x = 0.5;
+    section.y = 0.5;
+    section.name = NULL;
+    psStringAppend (&section.name, "a1");
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    n = 0;
+    for (int i = 0; i < match->n; i++) {
+        pmAstromMatch *pair = match->data[i];
+        pmAstromObj *raw = rawstars->data[pair->raw];
+        pmAstromObj *ref = refstars->data[pair->ref];
+
+        if (!isfinite(raw->Mag)) continue;
+        if (raw->Mag < iMagMin) continue;
+        if (raw->Mag > iMagMax) continue;
+        if (ref->Mag < rMagMin) continue;
+        if (ref->Mag > rMagMax) continue;
+
+        xVec->data.F32[n] = raw->chip->x;
+        yVec->data.F32[n] = raw->chip->y - ref->chip->y;
+        zVec->data.F32[n] = raw->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa, &graphdata, xVec, yVec, zVec, false);
+
+    // Y vs dX
+    section.x = 0.0;
+    section.y = 0.0;
+    section.name = NULL;
+    psStringAppend (&section.name, "a2");
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    n = 0;
+    for (int i = 0; i < match->n; i++) {
+        pmAstromMatch *pair = match->data[i];
+        pmAstromObj *raw = rawstars->data[pair->raw];
+        pmAstromObj *ref = refstars->data[pair->ref];
+
+        if (!isfinite(raw->Mag)) continue;
+        if (raw->Mag < iMagMin) continue;
+        if (raw->Mag > iMagMax) continue;
+        if (ref->Mag < rMagMin) continue;
+        if (ref->Mag > rMagMax) continue;
+
+        xVec->data.F32[n] = raw->chip->y;
+        yVec->data.F32[n] = raw->chip->x - ref->chip->x;
+        zVec->data.F32[n] = raw->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa, &graphdata, xVec, yVec, zVec, false);
+
+    // Y vs dY
+    section.x = 0.5;
+    section.y = 0.0;
+    section.name = NULL;
+    psStringAppend (&section.name, "a3");
+    KapaSetSection (kapa, &section);
+    psFree (section.name);
+
+    n = 0;
+    for (int i = 0; i < match->n; i++) {
+        pmAstromMatch *pair = match->data[i];
+        pmAstromObj *raw = rawstars->data[pair->raw];
+        pmAstromObj *ref = refstars->data[pair->ref];
+
+        if (!isfinite(raw->Mag)) continue;
+        if (raw->Mag < iMagMin) continue;
+        if (raw->Mag > iMagMax) continue;
+        if (ref->Mag < rMagMin) continue;
+        if (ref->Mag > rMagMax) continue;
+
+        xVec->data.F32[n] = raw->chip->y;
+        yVec->data.F32[n] = raw->chip->y - ref->chip->y;
+        zVec->data.F32[n] = raw->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa, &graphdata, xVec, yVec, zVec, false);
+
+    // *** X vs Y plot (different window)
+    int kapa2 = KapaOpenNamedSocket ("kapa", "XvsY");
+    if (kapa2 == -1) {
+        psError(PS_ERR_UNKNOWN, true, "failure to open kapa");
+        return false;
+    }
+
+    KapaResize (kapa2, 1000, 1000);
+    KapaInitGraph (&graphdata);
+    KapaClearPlots (kapa2);
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 2;
+    graphdata.style = 2;
+
+    psFree (xVec);
+    psFree (yVec);
+    psFree (zVec);
+
+    xVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
+    yVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
+    zVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
+
+    // X vs Y by mag (raw)
+    n = 0;
+    for (int i = 0; i < rawstars->n; i++) {
+        pmAstromObj *raw = rawstars->data[i];
+        if (!isfinite(raw->Mag)) continue;
+        if (raw->Mag < iMagMin) continue;
+        if (raw->Mag > iMagMax) continue;
+
+        xVec->data.F32[n] = raw->chip->x;
+        yVec->data.F32[n] = raw->chip->y;
+        zVec->data.F32[n] = raw->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa2, &graphdata, xVec, yVec, zVec, false);
+
+    graphdata.color = KapaColorByName ("red");
+    graphdata.ptype = 7;
+    graphdata.style = 2;
+
+    psFree (xVec);
+    psFree (yVec);
+    psFree (zVec);
+
+    xVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
+    yVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
+    zVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
+
+    // X vs Y by mag (raw)
+    n = 0;
+    for (int i = 0; i < refstars->n; i++) {
+        pmAstromObj *ref = refstars->data[i];
+        if (!isfinite(ref->Mag)) continue;
+        if (ref->Mag < rMagMin) continue;
+        if (ref->Mag > rMagMax) continue;
+
+        xVec->data.F32[n] = ref->chip->x;
+        yVec->data.F32[n] = ref->chip->y;
+        zVec->data.F32[n] = ref->Mag;
+        n++;
+    }
+    xVec->n = yVec->n = zVec->n = n;
+    pmKapaPlotVectorTriple_AutoLimitsZscale_OpenGraph (kapa2, &graphdata, xVec, yVec, zVec, false);
+
+    // pause and wait for user input:
+    // continue, save (provide name), ??
+    char key[10], name[80];
+    fprintf (stdout, "(s)ave plot or [c]ontinue? ");
+    if (!fgets (key, 8, stdin)) {
+        psWarning("Couldn't read anything");
+    } else if (key[0] == 's') {
+        fprintf (stdout, "enter plot name [chipfit.png]: ");
+        if (fscanf (stdin, "%s", name) != 1) {
+            psWarning("Couldn't read name");
+        } else if (!strcmp (name, "")) {
+            strcpy (name, "chipfit.png");
+        }
+        KapaPNG (kapa, name);
+    }
+
+    close (kapa2);
+    psFree (xVec);
+    psFree (yVec);
+    psFree (zVec);
+    return true;
+}
+
+bool pmKapaPlotVectorTriple_AutoLimitsZscale_OpenGraph (int kapa, Graphdata *graphdata, psVector *xVec, psVector *yVec, psVector *zVec, bool increasing)
+{
+
+    // set limits based on data values
+    float zmin = +FLT_MAX;
+    float zmax = -FLT_MAX;
+    for (int i = 0; i < xVec->n; i++) {
+        zmin = PS_MIN (zmin, zVec->data.F32[i]);
+        zmax = PS_MAX (zmax, zVec->data.F32[i]);
+    }
+
+    // set the scale vector
+    psVector *zScale = psVectorAlloc (zVec->n, PS_DATA_F32);
+
+    float range = zmax - zmin;
+    if (range == 0.0) {
+        psVectorInit (zScale, 1.0);
+    } else {
+        for (int i = 0; i < zVec->n; i++) {
+            if (increasing) {
+                zScale->data.F32[i] = PS_MIN (1.5, PS_MAX(0.05, 1.5*(zVec->data.F32[i] - zmin)/range));
+            } else {
+                zScale->data.F32[i] = PS_MIN (1.5, PS_MAX(0.05, 1.5*(zmax - zVec->data.F32[i])/range));
+            }
+        }
+    }
+
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, graphdata);
+
+    // the point size will be scaled from the z vector
+    graphdata->size = -1;
+    KapaPrepPlot (kapa, xVec->n, graphdata);
+    KapaPlotVector (kapa, xVec->n, xVec->data.F32, "x");
+    KapaPlotVector (kapa, yVec->n, yVec->data.F32, "y");
+    KapaPlotVector (kapa, zVec->n, zScale->data.F32, "z");
+    psFree (zScale);
+    return true;
+}
+
+# else
+
+bool psastroPlotRawstars (psArray *rawstars, pmFPA *fpa, pmChip *chip, psMetadata *recipe)
+{
+    return false;
+}
+
+bool psastroPlotRefstars (psArray *refstars, psMetadata *recipe)
+{
+    return false;
+}
+
+bool psastroPlotOneChipFit (psArray *rawstars, psArray *refstars, psArray *match,
+    psMetadata *recipe)
+{
+    return false;
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,25 @@
+/*
+ * The line
+    { PSASTRO_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+#include "pslib.h"
+#include "psastroErrorCodes.h"
+
+void psastroErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { PSASTRO_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PSASTRO_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = PSASTRO_ERR_NERROR - PSASTRO_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroErrorCodes.dat	(revision 22322)
@@ -0,0 +1,12 @@
+#
+# This file is used to generate pmErrorClasses.h
+#
+BASE = 300		First value we use; lower values belong to psLib
+UNKNOWN			Unknown PM error code
+NOT_IMPLEMENTED		Desired feature is not yet implemented
+ARGUMENTS		Incorrect arguments
+CONFIG			Problem in configure files
+IO			Problem in FITS I/O
+WCS      		Error interpreting FITS WCS information
+DATA                    Problem in data values
+REFSTARS                Problem in data values
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,18 @@
+#if !defined(PSASTRO_ERROR_CODES_H)
+#define PSASTRO_ERROR_CODES_H
+/*
+ * The line
+ *  PSASTRO_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    PSASTRO_ERR_BASE = 600,
+    PSASTRO_ERR_${ErrorCode},
+    PSASTRO_ERR_NERROR
+} psastroErrorCode;
+
+void psastroErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroFixChips.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroFixChips.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroFixChips.c	(revision 22322)
@@ -0,0 +1,258 @@
+# include "psastroInternal.h"
+# define NONLIN_TOL 0.001 /* tolerance in pixels */
+
+// XXX I think the 'badAstrom' tests may need to be adjusted: see eg the nominal rotation of
+// the chips
+bool psastroFixChips (pmConfig *config, psMetadata *recipe) {
+
+  bool status;
+
+  bool fixChips = psMetadataLookupBool (&status, config->arguments, "PSASTRO.FIX.CHIPS");
+  if (!status) {
+      fixChips = psMetadataLookupBool (&status, recipe, "PSASTRO.FIX.CHIPS");
+  }
+  if (!fixChips) return true;
+
+  // identify reference astrometry table
+  // if not defined, correction was not requested; skip step
+  pmFPAfile *astrom = psMetadataLookupPtr (NULL, config->files, "PSASTRO.MODEL");
+  if (!astrom) return true;
+
+  // select the input data sources
+  pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+  if (!input) {
+      psError(PSASTRO_ERR_CONFIG, true, "Can't find input data");
+      return false;
+  }
+
+  // acceptable limits
+  double pixelTol = psMetadataLookupF32 (&status, recipe, "PSASTRO.PIXEL.TOLERANCE");
+  if (!status) psAbort ("missing recipe value");
+
+  double angleTol = psMetadataLookupF32 (&status, recipe, "PSASTRO.ANGLE.TOLERANCE");
+  if (!status) psAbort ("missing recipe value");
+
+  // make sure the astrometry model is loaded.  de-activate all files except PSASTRO.MODEL.
+  // supply the input concepts so the model is defined using the RA, DEC, POSANGLE of the input
+  // image.
+  psFree (astrom->fpa->concepts);
+  astrom->fpa->concepts = psMemIncrRefCounter (input->fpa->concepts);
+  pmFPAfileActivate (config->files, false, NULL);
+  pmFPAfileActivate (config->files, true, "PSASTRO.MODEL");
+
+  pmFPAview *view = pmFPAviewAlloc (0);
+
+  // files associated with the science image
+  if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) {
+      psError (PS_ERR_IO, false, "Can't load the astrometry model file");
+      return false;
+  }
+
+  // we now have a set of chip solutions and a set of prediction measure the overall
+  // offset and rotation of the two systems by comparing the chip corners, projected onto
+  // the focal plane (all 4 to prevent solutions tied to a single corner)
+
+  psVector *xObs = psVectorAllocEmpty (4*input->fpa->chips->n, PS_TYPE_F32);
+  psVector *yObs = psVectorAllocEmpty (4*input->fpa->chips->n, PS_TYPE_F32);
+  psVector *xRef = psVectorAllocEmpty (4*input->fpa->chips->n, PS_TYPE_F32);
+  psVector *yRef = psVectorAllocEmpty (4*input->fpa->chips->n, PS_TYPE_F32);
+
+  int nPts = 0;
+
+  pmChip *obsChip = NULL;
+  while ((obsChip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+      if (!obsChip->process || !obsChip->file_exists || !obsChip->data_exists) { continue; }
+
+      // set the chip astrometry using the astrom file
+      pmChip *refChip = pmFPAviewThisChip (view, astrom->fpa);
+
+      psRegion *region = pmChipPixels (obsChip);
+      psPlane ptCP, ptFP;
+
+      ptCP.x = region->x0; ptCP.y = region->y0;
+      psPlaneTransformApply (&ptFP, obsChip->toFPA, &ptCP);
+      xObs->data.F32[nPts] = ptFP.x;
+      yObs->data.F32[nPts] = ptFP.y;
+      psPlaneTransformApply (&ptFP, refChip->toFPA, &ptCP);
+      xRef->data.F32[nPts] = ptFP.x;
+      yRef->data.F32[nPts] = ptFP.y;
+      nPts ++;
+
+      ptCP.x = region->x0; ptCP.y = region->y1;
+      psPlaneTransformApply (&ptFP, obsChip->toFPA, &ptCP);
+      xObs->data.F32[nPts] = ptFP.x;
+      yObs->data.F32[nPts] = ptFP.y;
+      psPlaneTransformApply (&ptFP, refChip->toFPA, &ptCP);
+      xRef->data.F32[nPts] = ptFP.x;
+      yRef->data.F32[nPts] = ptFP.y;
+      nPts ++;
+
+      ptCP.x = region->x1; ptCP.y = region->y1;
+      psPlaneTransformApply (&ptFP, obsChip->toFPA, &ptCP);
+      xObs->data.F32[nPts] = ptFP.x;
+      yObs->data.F32[nPts] = ptFP.y;
+      psPlaneTransformApply (&ptFP, refChip->toFPA, &ptCP);
+      xRef->data.F32[nPts] = ptFP.x;
+      yRef->data.F32[nPts] = ptFP.y;
+      nPts ++;
+
+      ptCP.x = region->x1; ptCP.y = region->y0;
+      psPlaneTransformApply (&ptFP, obsChip->toFPA, &ptCP);
+      xObs->data.F32[nPts] = ptFP.x;
+      yObs->data.F32[nPts] = ptFP.y;
+      psPlaneTransformApply (&ptFP, refChip->toFPA, &ptCP);
+      xRef->data.F32[nPts] = ptFP.x;
+      yRef->data.F32[nPts] = ptFP.y;
+      nPts ++;
+
+      psFree (region);
+  }
+  xObs->n = yObs->n = xRef->n = yRef->n = nPts;
+	
+  psPlaneTransform *map = psPlaneTransformAlloc (1, 1);
+  
+  psVector *mask = psVectorAlloc (nPts, PS_TYPE_U8);
+  psVectorInit (mask, 0);
+
+  psStats *stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+  stats->clipIter = 1;
+
+  for (int i = 0; i < 3; i++) {
+      psVectorClipFitPolynomial2D (map->x, stats, mask, 0xff, xObs, NULL, xRef, yRef);
+      psTrace ("psModules.astrom", 3, "x resid: %f +/- %f (%ld of %ld)\n", stats->clippedMean, stats->clippedStdev, stats->clippedNvalues, xObs->n);
+
+      psVectorClipFitPolynomial2D (map->y, stats, mask, 0xff, yObs, NULL, xRef, yRef);
+      psTrace ("psModules.astrom", 3, "x resid: %f +/- %f (%ld of %ld)\n", stats->clippedMean, stats->clippedStdev, stats->clippedNvalues, yObs->n);
+  }
+
+  // loop over all chips, select the outliers, and replace the measured astrometry with the model
+  // the measured transformation above must be applied to make the comparison, and also then applied to the 
+  // model transformation
+
+  psFree (xObs);
+  psFree (yObs);
+  psFree (xRef);
+  psFree (yRef);
+  psFree (mask);
+  psFree (stats);
+
+  psFree (view);
+  view = pmFPAviewAlloc (0);
+
+  while ((obsChip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+    psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, obsChip->file_exists, obsChip->process);
+    if (!obsChip->process || !obsChip->file_exists || !obsChip->data_exists) { continue; }
+
+    // set the chip astrometry using the astrom file
+    pmChip *refChip = pmFPAviewThisChip (view, astrom->fpa);
+
+    // bad Astrometry test:  ref pixel or angle outside nominal
+
+    psPlane refPixel = {0.0, 0.0, 0.0, 0.0};
+    psPlane obsCoord, refCoord, tmpCoord;
+
+    // find location of 0,0 pixel in focal plane coords for this chip
+    psPlaneTransformApply (&obsCoord, obsChip->toFPA, &refPixel);
+
+    // find location of 0,0 pixel in focal plane coords for ref chip
+    // apply the global field rotation and offset before comparing
+    psPlaneTransformApply (&tmpCoord, refChip->toFPA, &refPixel);
+    psPlaneTransformApply (&refCoord, map, &tmpCoord);
+    
+    psPlane offPixel = {100.0, 0.0, 100.0, 0.0};
+    psPlane obsOffPt, refOffPt;
+
+    // find location of 0,0 pixel in focal plane coords for this chip
+    psPlaneTransformApply (&obsOffPt, obsChip->toFPA, &offPixel);
+
+    // find location of 0,0 pixel in focal plane coords for ref chip
+    psPlaneTransformApply (&tmpCoord, refChip->toFPA, &offPixel);
+    psPlaneTransformApply (&refOffPt, map, &tmpCoord);
+    
+    double obsAngle = PM_DEG_RAD*atan2 (obsOffPt.y - obsCoord.y, obsOffPt.x - obsCoord.x);
+    double refAngle = PM_DEG_RAD*atan2 (refOffPt.y - refCoord.y, refOffPt.x - refCoord.x);
+
+    bool badAstrom = false;
+    badAstrom |= fabs(obsCoord.x - refCoord.x) > pixelTol;
+    badAstrom |= fabs(obsCoord.y - refCoord.y) > pixelTol;
+    badAstrom |= fabs(obsAngle   - refAngle)   > angleTol;
+
+    fprintf (stderr, "chip %d, angle: %f, pixel: %f,%f\n", view->chip, obsAngle - refAngle, obsCoord.x - refCoord.x, obsCoord.y - refCoord.y);
+
+    if (!badAstrom) continue;
+
+    psLogMsg ("psastro", PS_LOG_INFO, "fixing chip %d, angle: %f, pixel: %f,%f\n",
+	      view->chip, obsAngle - refAngle, obsCoord.x - refCoord.x, obsCoord.y - refCoord.y);
+
+    psFree (obsChip->toFPA);
+    psFree (obsChip->fromFPA);
+
+    // apply the exiting fromTPA transformation to make the new toFPA consistent with the toTPA layter
+    // XXX this only works if toTPA is at most a linear transformation
+    psPlaneTransform *toFPA = psPlaneTransformAlloc(refChip->toFPA->x->nX, refChip->toFPA->x->nY);
+    for (int i = 0; i <= refChip->toFPA->x->nX; i++) {
+	for (int j = 0; j <= refChip->toFPA->x->nY; j++) {
+	    double f1 = refChip->toFPA->x->coeffMask[i][j] ? 0.0 : map->x->coeff[1][0]*refChip->toFPA->x->coeff[i][j];
+	    double f2 = refChip->toFPA->y->coeffMask[i][j] ? 0.0 : map->x->coeff[0][1]*refChip->toFPA->y->coeff[i][j];
+	    toFPA->x->coeff[i][j] = f1 + f2;
+
+	    double g1 = refChip->toFPA->x->coeffMask[i][j] ? 0.0 : map->y->coeff[1][0]*refChip->toFPA->x->coeff[i][j];
+	    double g2 = refChip->toFPA->y->coeffMask[i][j] ? 0.0 : map->y->coeff[0][1]*refChip->toFPA->y->coeff[i][j];
+	    toFPA->y->coeff[i][j] = g1 + g2;
+	}
+    }
+    toFPA->x->coeff[0][0] += map->x->coeff[0][0];
+    toFPA->y->coeff[0][0] += map->y->coeff[0][0];
+
+    psRegion *region = pmChipPixels (obsChip);
+    obsChip->toFPA   = toFPA;
+    obsChip->fromFPA = psPlaneTransformInvert(NULL, obsChip->toFPA, *region, 50);
+    psFree (region);
+    
+    // XXX this stuff below should be applied to each readout...
+    // XXX for now, just use first readout
+    pmCell *cell = obsChip->cells->data[0];
+    pmReadout *readout = cell->readouts->data[0];
+
+    // use the new position to re-try the match fit
+    // select the raw objects for this readout
+    psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+    if (rawstars == NULL) { continue; }
+
+    // select the raw objects for this readout
+    psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+    if (refstars == NULL) { continue; }
+
+    // the absolute minimum number of stars is 4 (for order = 1)
+    if ((rawstars->n < 4) || (refstars->n < 4)) {
+	readout->data_exists = false;
+	psLogMsg ("psastro", 3, "insufficient rawstars (%ld) or refstars (%ld)", 
+		  rawstars->n, refstars->n);
+	continue;
+    } 
+
+    psastroUpdateChipToFPA (input->fpa, obsChip, rawstars, refstars);
+
+    // update the header
+    psMetadata *updates = psMemIncrRefCounter (psMetadataLookupMetadata (&status, readout->analysis, "PSASTRO.HEADER"));
+    if (!updates) {
+	updates = psMetadataAlloc ();
+    }
+
+    // XXX update the header with info to reflect the failure
+    if (!psastroOneChipFit (input->fpa, obsChip, refstars, rawstars, recipe, updates)) {
+	readout->data_exists = false;
+	psLogMsg ("psastro", 3, "failed to find a solution\n");
+	psFree (updates);
+	continue;
+    }
+
+    pmAstromWriteWCS (updates, input->fpa, obsChip, NONLIN_TOL);
+    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSASTRO.HEADER",  PS_META_REPLACE | PS_DATA_METADATA, "psastro header stats", updates);
+    psFree (updates);
+  }
+
+  psFree (map);
+  psFree (view);
+  return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroFixChipsTest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroFixChipsTest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroFixChipsTest.c	(revision 22322)
@@ -0,0 +1,247 @@
+# include "psastroInternal.h"
+# define NONLIN_TOL 0.001 /* tolerance in pixels */
+
+bool psPlaneTransformPrint (FILE *f, psPlaneTransform *in) {
+
+  fprintf (f, "x (%d,%d):\n", in->x->nX, in->x->nY);
+  for (int i = 0; i <= in->x->nX; i++) {
+    fprintf (f, " %d : ", i);
+    for (int j = 0; j <= in->x->nY; j++) {
+      fprintf (f, "%f ", in->x->coeff[i][j]);
+    }
+    fprintf (f, "\n");
+  }
+  fprintf (f, "y (%d,%d):\n", in->y->nX, in->y->nY);
+  for (int i = 0; i <= in->y->nX; i++) {
+    fprintf (f, " %d : ", i);
+    for (int j = 0; j <= in->y->nY; j++) {
+      fprintf (f, "%f ", in->y->coeff[i][j]);
+    }
+    fprintf (f, "\n");
+  }
+  return TRUE;
+}
+
+// XXX the designed version is not working correctly. Here we load a model which just has
+// the CRPIX1, CRPIX2 values from another image. Compare those positions to the current
+// postions, make an average (DC) correction, then fix the outliers.
+bool psastroFixChipsTest (pmConfig *config, psMetadata *recipe) {
+
+    bool status;
+
+    bool fixChips = psMetadataLookupBool (&status, config->arguments, "PSASTRO.FIX.CHIPS");
+    if (!status) {
+	fixChips = psMetadataLookupBool (&status, recipe, "PSASTRO.FIX.CHIPS");
+    }
+    if (!fixChips) return true;
+
+    // identify reference astrometry table
+    // if not defined, correction was not requested; skip step
+    char *modelFile = psMetadataLookupStr (&status, recipe, "PSASTRO.ROUGH.MODEL");
+    if (!modelFile) return true;
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    if (!input) {
+	psError(PSASTRO_ERR_CONFIG, true, "Can't find input data");
+	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; 
+    } 
+
+    // acceptable limits
+    double pixelTol = psMetadataLookupF32 (&status, recipe, "PSASTRO.PIXEL.TOLERANCE");
+    if (!status) psAbort ("missing recipe value");
+
+    FILE *f = fopen (modelFile, "r");
+    if (f == NULL) {
+	psError(PSASTRO_ERR_CONFIG, true, "Can't find model data %s", modelFile);
+	return false;
+    }
+
+    float Xo, Yo;
+
+    psArray *refPos = psArrayAllocEmpty (100);
+    while (fscanf (f, "%*s %*d %*d %*f %f %f", &Xo, &Yo) != EOF) {
+	psPlane *pos = psPlaneAlloc();
+	pos->x = Xo;
+	pos->y = Yo;
+	psArrayAdd (refPos, 100, pos);
+	psFree (pos);
+    }
+    assert (refPos->n == 60);
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // loop over all chips, get the current CRPIX1,CRPIX2 values
+    pmChip *obsChip = NULL;
+    psArray *obsPos = psArrayAllocEmpty (100);
+    while ((obsChip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+	psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, obsChip->file_exists, obsChip->process);
+	if (!obsChip->process || !obsChip->file_exists || !obsChip->data_exists) { continue; }
+
+	// extract CRPIX1, CRPIX2:
+	// XXX for now, pull from the header:
+	// pmCell *cell = obsChip->cells->data[0];
+	// pmReadout *readout = cell->readouts->data[0];
+	// psMetadata *header = psMetadataLookupMetadata (&status, readout->analysis, "PSASTRO.HEADER");
+	// Xo = psMetadataLookupF32 (&status, header, "CRPIX1");
+	// Yo = psMetadataLookupF32 (&status, header, "CRPIX2");
+
+	pmAstromWCS *wcs = pmAstromWCSfromFPA(input->fpa, obsChip, NONLIN_TOL);
+	Xo = wcs->crpix1;
+	Yo = wcs->crpix2;
+	psFree (wcs);
+
+	psPlane *pos = psPlaneAlloc();
+	pos->x = Xo;
+	pos->y = Yo;
+	psArrayAdd (obsPos, 100, pos);
+	psFree (pos);
+    }
+    psFree (view);
+    assert (obsPos->n == 60);
+
+    // get the offsets
+    psVector *dX = psVectorAlloc (obsPos->n, PS_TYPE_F32);
+    psVector *dY = psVectorAlloc (obsPos->n, PS_TYPE_F32);
+    for (int i = 0; i < obsPos->n; i++) {
+	psPlane *obs = obsPos->data[i];
+	psPlane *ref = refPos->data[i];
+    
+	if (i < 30) {
+	    dX->data.F32[i] = obs->x - ref->x;
+	    dY->data.F32[i] = obs->y - ref->y;
+	} else {
+	    dX->data.F32[i] = ref->x - obs->x;
+	    dY->data.F32[i] = ref->y - obs->y;
+	}
+	fprintf (stderr, "chip %d : %f - %f : %f, %f - %f: %f\n", i, obs->x, ref->x, dX->data.F32[i], obs->y, ref->y, dY->data.F32[i]);
+    }
+    
+    // get the average offset
+    psStats *stats;
+    stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
+    psVectorStats (stats, dX, NULL, NULL, 0);
+    Xo = stats->robustMedian;
+    fprintf (stderr, "offset x: %f +/- %f\n", stats->robustMedian, stats->robustStdev);
+    psFree (stats);
+
+    stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
+    psVectorStats (stats, dY, NULL, NULL, 0);
+    Yo = stats->robustMedian;
+    fprintf (stderr, "offset y: %f +/- %f\n", stats->robustMedian, stats->robustStdev);
+    psFree (stats);
+
+    // measure the rotation of the good chips
+    psVector *dT = psVectorAllocEmpty (obsPos->n, PS_TYPE_F32);
+    for (int i = 0; i < dX->n; i++) {
+	bool badAstrom = false;
+	badAstrom |= (fabs(dX->data.F32[i] - Xo) > pixelTol);
+	badAstrom |= (fabs(dY->data.F32[i] - Yo) > pixelTol);
+	if (badAstrom) continue;
+
+	pmChip *chip = input->fpa->chips->data[i];
+	float theta = atan2 (chip->toFPA->x->coeff[0][1], chip->toFPA->x->coeff[1][0]);
+	if (i >= 30) {
+	  theta -= M_PI;
+	}
+	dT->data.F32[dT->n] = theta;
+	dT->n ++;
+    }
+    stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
+    psVectorStats (stats, dT, NULL, NULL, 0);
+    float To = stats->robustMedian;
+    fprintf (stderr, "offset t: %f +/- %f\n", stats->robustMedian, stats->robustStdev);
+    psFree (stats);
+
+    // find the discrepant chips and reset
+    for (int i = 0; i < dX->n; i++) {
+	bool badAstrom = false;
+	badAstrom |= (fabs(dX->data.F32[i] - Xo) > pixelTol);
+	badAstrom |= (fabs(dY->data.F32[i] - Yo) > pixelTol);
+	if (!badAstrom) continue;
+
+	fprintf (stderr, "fixing chip %d, offset: %f %f\n",
+		 i, dX->data.F32[i] - Xo, dY->data.F32[i] - Yo);
+
+	// XXX this stuff below should be applied to each readout...
+	// XXX for now, just use first readout
+	pmFPA  *fpa  = input->fpa;
+	pmChip *chip = fpa->chips->data[i];
+	pmCell *cell = chip->cells->data[0];
+	pmReadout *readout = cell->readouts->data[0];
+
+	pmAstromWCS *wcs = pmAstromWCSfromFPA(fpa, chip, NONLIN_TOL);
+
+	psPlane *ref = refPos->data[i];
+	wcs->crpix1 = ref->x + Xo;
+	wcs->crpix2 = ref->y + Yo;
+
+	pmAstromWCStoFPA (fpa, chip, wcs, pixelScale);
+	psFree (wcs);
+
+	// apply average rotation
+	psPlaneTransform *toFPA = psPlaneTransformRotate (NULL, chip->toFPA, To);
+	psFree (chip->toFPA);
+	chip->toFPA = toFPA;
+
+	psPlaneTransform *fromFPA = psPlaneTransformRotate (NULL, chip->fromFPA, -To);
+	psFree (chip->fromFPA);
+	chip->fromFPA = fromFPA;
+
+	// XXX did we get the offset right?
+	wcs = pmAstromWCSfromFPA(fpa, chip, NONLIN_TOL);
+	psFree (wcs);
+
+	// save WCS and analysis metadata in update header
+	psMetadata *updates = psMetadataAlloc();
+
+	// select the raw objects for this readout
+	psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+	if (rawstars == NULL) { continue; }
+
+	// select the raw objects for this readout
+	psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+	if (refstars == NULL) { continue; }
+
+	// the absolute minimum number of stars is 4 (for order = 1)
+	if ((rawstars->n < 4) || (refstars->n < 4)) {
+	    readout->data_exists = false;
+	    psLogMsg ("psastro", 3, "insufficient rawstars (%ld) or refstars (%ld)", 
+		      rawstars->n, refstars->n);
+	    continue;
+	} 
+
+	psastroUpdateChipToFPA (fpa, chip, rawstars, refstars);
+
+	// XXX skip the re-fit step for a test
+	continue;
+
+	// XXX update the header with info to reflect the failure
+	if (!psastroOneChipFit (fpa, chip, refstars, rawstars, recipe, updates)) {
+	    readout->data_exists = false;
+	    psLogMsg ("psastro", 3, "failed to find a solution\n");
+	    psFree (updates);
+	    continue;
+	}
+
+	pmAstromWriteWCS (updates, fpa, chip, NONLIN_TOL);
+	psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSASTRO.HEADER",  PS_DATA_METADATA, "psastro header stats", updates);
+	psFree (updates);
+
+    }
+
+    psFree (dT);
+    psFree (dX);
+    psFree (dY);
+    psFree (refPos);
+    psFree (obsPos);
+  
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroInternal.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroInternal.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroInternal.h	(revision 22322)
@@ -0,0 +1,17 @@
+# ifdef HAVE_CONFIG_H
+# include <config.h>
+# endif
+
+# ifndef PSASTRO_INTERNAL_H
+# define PSASTRO_INTERNAL_H
+
+# include <stdio.h>
+# include <string.h>
+# include <strings.h>  // for strcasecmp
+# include <unistd.h>   // for unlink
+# include <pslib.h>
+# include <psmodules.h>
+# include <ppStats.h>
+# include "psastro.h"
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroLoadRefstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroLoadRefstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroLoadRefstars.c	(revision 22322)
@@ -0,0 +1,223 @@
+# include "psastroInternal.h"
+# define ELIXIR_MODE 1
+
+// XXX other comment
+
+psArray *psastroLoadRefstars (pmConfig *config) {
+
+    int fd;
+    bool status;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, PSASTRO_RECIPE);
+
+    // DVO APIs expect decimal degrees
+    float RAmin  = DEG_RAD*psMetadataLookupF32(NULL, recipe, "RA_MIN");
+    float RAmax  = DEG_RAD*psMetadataLookupF32(NULL, recipe, "RA_MAX");
+    float DECmin = DEG_RAD*psMetadataLookupF32(NULL, recipe, "DEC_MIN");
+    float DECmax = DEG_RAD*psMetadataLookupF32(NULL, recipe, "DEC_MAX");
+
+    // extra field fraction to add
+    double fieldPadding = psMetadataLookupF32 (&status, recipe, "PSASTRO.FIELD.PADDING");
+    PS_ASSERT (status, NULL);
+
+    float dRA = RAmax - RAmin;
+    RAmin -= dRA * fieldPadding;
+    RAmax += dRA * fieldPadding;
+
+    float dDEC = DECmax - DECmin;
+    DECmin -= dDEC * fieldPadding;
+    DECmax += dDEC * fieldPadding;
+
+    // grab the PSASTRO.CATDIR name from the PSASTRO recipe
+    char *catdir_recipe = psMetadataLookupStr(&status, recipe, "PSASTRO.CATDIR");
+    psAssert (catdir_recipe, "Need a recipe for the catdir!");
+
+    // substitute abstract name with concrete name, if present in PSASTRO.CATDIRS
+    psMetadata *catdirs = psMetadataLookupMetadata(&status, config->site, "PSASTRO.CATDIRS"); // List of cameras
+    if (!catdirs) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find PSASTRO.CATDIRS in the system configuration.\n");
+        return false;
+    }
+    char *catdir_virtual = psMetadataLookupStr(&status, catdirs, catdir_recipe);
+    char *catdir = (catdir_virtual == NULL) ? catdir_recipe : catdir_virtual;
+
+    // convert the uri to a real filename (ie, path://foobar)
+    psString CATDIR = pmConfigConvertFilename(catdir, config, false, false); // Resolved filename
+    PS_ASSERT (CATDIR, NULL);
+
+    char *getstarCommand = psStringCopy(psMetadataLookupStr(NULL, recipe, "DVO.GETSTAR"));
+    PS_ASSERT (getstarCommand, NULL);
+
+    char *outformat = psMetadataLookupStr(NULL, recipe, "DVO.GETSTAR.OUTFORMAT");
+    PS_ASSERT (outformat, NULL);
+
+    char *photcode = psMetadataLookupStr(NULL, recipe, "DVO.GETSTAR.PHOTCODE");
+    PS_ASSERT (photcode, NULL);
+
+    float MAGmax = psMetadataLookupF32(NULL, recipe, "DVO.GETSTAR.MAG.MAX");
+
+    // issue the following command:
+    // getstar -region RAmin RAmax DECmin DECmax
+    char tempFile[64];
+    sprintf (tempFile, "/tmp/psastro.XXXXXX");
+    if ((fd = mkstemp (tempFile)) == -1) {
+        psError(PSASTRO_ERR_REFSTARS, true, "error creating temp output file\n");
+        psFree(CATDIR);
+        return NULL;
+    }
+    close (fd);
+
+    psTimerStart ("psastro");
+
+    // supply a known output format (for CATALOG output) so the code below knows what to read
+    if (ELIXIR_MODE) {
+        psStringAppend (&getstarCommand, " -D CATMODE mef -D CATFORMAT elixir");
+    } else {
+        psStringAppend (&getstarCommand, " -D CATMODE mef -D CATFORMAT panstarrs");
+    }
+
+    // check for default name (use .ptolemyrc), or use specified CATDIR
+    if (strcasecmp(CATDIR, "NONE")) {
+        psStringAppend (&getstarCommand, " -D CATDIR %s", CATDIR);
+    }
+    psFree(CATDIR);
+
+    // supply the max magnitude, the output format, and the photcode
+    if (strcasecmp (photcode, "NONE")) {
+        psStringAppend (&getstarCommand, " -maglim %f", MAGmax);
+    }
+    if (strcasecmp (photcode, "NONE")) {
+        psStringAppend (&getstarCommand, " -photcode %s", photcode);
+    }
+    psStringAppend (&getstarCommand, " -format %s", outformat);
+
+    // add region and output filename
+    psStringAppend (&getstarCommand, " -region %f %f %f %f -o %s", RAmin, DECmin, RAmax, DECmax, tempFile);
+    psTrace ("psastro", 3, "%s\n", getstarCommand);
+
+    // XXX use psPipe: catch stderr, stdout, allow for Nsec timeout...
+    // use fork to add timeout capability
+    status = system (getstarCommand);
+    if (status) {
+        psError(PSASTRO_ERR_REFSTARS, true, "error loading reference data\n");
+        return NULL;
+    }
+    psFree (getstarCommand);
+
+    psLogMsg ("psastro", 3, "ran getstar : %f sec\n", psTimerMark ("psastro"));
+
+    // the output from getstar is a file with the Average table
+    psFits *fits = psFitsOpen (tempFile, "r");
+
+    psTimerStart ("psastro");
+
+    psArray *refstars = NULL;
+    if (!strcmp (outformat, "CATALOG")) {
+      refstars = psastroReadGetstarCatalog (fits);
+    }
+    if (!strcmp (outformat, "PS1_DEV_0")) {
+      refstars = psastroReadGetstar_PS1_DEV_0 (fits);
+    }
+    if (refstars == NULL) {
+        psError(PSASTRO_ERR_REFSTARS, true, "error reading reference data\n");
+        psFitsClose (fits);
+        return NULL;
+    }
+
+    psLogMsg ("psastro", 3, "loaded %ld reference stars : %f sec\n", refstars->n, psTimerMark ("psastro"));
+
+    psTrace ("psastro", 3, "loaded %ld reference stars from (%10.6f,%10.6f) - (%10.6f,%10.6f)\n",
+             refstars->n, RAmin, DECmin, RAmax, DECmax);
+
+    psFitsClose (fits);
+    unlink (tempFile);
+
+    // dump or plot the available refstars
+    if (psTraceGetLevel("psastro.dump") > 0) {
+        psastroDumpRefstars (refstars, "refstars.dat");
+    }
+
+    if (psTraceGetLevel("psastro.plot") > 0) {
+        psastroPlotRefstars (refstars, recipe);
+    }
+
+    return refstars;
+}
+
+psArray *psastroReadGetstarCatalog (psFits *fits) {
+
+    bool status;
+
+    if (ELIXIR_MODE) {
+        psFitsMoveExtName (fits, "DVO_AVERAGE_ELIXIR");
+    } else {
+        psFitsMoveExtName (fits, "DVO_AVERAGE_PANSTARRS");
+    }
+
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+
+    // convert the Average table to the pmAstromObj entries
+    psArray *refstars = psArrayAllocEmpty (numSources);
+    for (int i = 0; i < numSources; i++) {
+        pmAstromObj *ref = pmAstromObjAlloc ();
+
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+
+        // DVO tables are stored in degrees
+        if (ELIXIR_MODE) {
+            ref->sky->r   = RAD_DEG*psMetadataLookupF32 (&status, row, "RA");
+            ref->sky->d   = RAD_DEG*psMetadataLookupF32 (&status, row, "DEC");
+            ref->Mag      = 0.001*psMetadataLookupS32 (&status, row, "MAG");  // ELIXIR uses millimags
+        } else {
+            ref->sky->r   = RAD_DEG*psMetadataLookupF64 (&status, row, "RA");
+            ref->sky->d   = RAD_DEG*psMetadataLookupF64 (&status, row, "DEC");
+            ref->Mag      = psMetadataLookupF32 (&status, row, "MAG"); // PANSTARRS uses mags
+        }
+
+        // XXX VERY temporary hack to avoid M31 bulge
+        if ((fabs(ref->sky->r - 0.186438) < 0.002) && (fabs(ref->sky->d - 0.720270) < 0.002)) {
+          psFree (ref);
+          psFree (row);
+          continue;
+        }
+
+        psArrayAdd (refstars, 100, ref);
+        psFree (ref);
+        psFree (row);
+    }
+    return refstars;
+}
+
+psArray *psastroReadGetstar_PS1_DEV_0 (psFits *fits) {
+
+    bool status;
+
+    psFitsMoveExtName (fits, "GETSTAR_PS1_DEV_0");
+
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+
+    // convert the Average table to the pmAstromObj entries
+    psArray *refstars = psArrayAllocEmpty (numSources);
+    for (int i = 0; i < numSources; i++) {
+        pmAstromObj *ref = pmAstromObjAlloc ();
+
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+
+        ref->sky->r   = RAD_DEG*psMetadataLookupF32 (&status, row, "RA");
+        ref->sky->d   = RAD_DEG*psMetadataLookupF32 (&status, row, "DEC");
+        ref->Mag      = psMetadataLookupF32 (&status, row, "MAG");
+
+        // XXX VERY temporary hack to avoid M31 bulge
+        if ((fabs(ref->sky->r - 0.186438) < 0.002) && (fabs(ref->sky->d - 0.720270) < 0.002)) {
+          psFree (ref);
+          psFree (row);
+          continue;
+        }
+
+        psArrayAdd (refstars, 100, ref);
+        psFree (ref);
+        psFree (row);
+    }
+    return refstars;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroLuminosityFunction.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroLuminosityFunction.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroLuminosityFunction.c	(revision 22322)
@@ -0,0 +1,141 @@
+# include "psastroInternal.h"
+# define dMag 0.1
+// XXX put this in config?
+
+static void pmLumFuncFree (pmLumFunc *func) {
+
+    if (func == NULL) return;
+
+    return;
+}
+
+pmLumFunc *pmLumFuncAlloc () {
+
+    pmLumFunc *func = (pmLumFunc *) psAlloc(sizeof(pmLumFunc));
+    psMemSetDeallocator(func, (psFreeFunc) pmLumFuncFree);
+
+    func->mMin = 0.0;
+    func->mMax = 0.0;
+    func->slope = 0.0;
+    func->offset = 0.0;
+
+    func->mPeak = 0.0;
+    func->nPeak = 0;
+    func->sPeak = 0;
+
+    return func;
+}
+
+pmLumFunc *psastroLuminosityFunction (psArray *stars) {
+
+    if (stars->n == 0) return NULL;
+
+    // determine the max and min magnitude for the array of stars
+    pmAstromObj *star = stars->data[0];
+    double mMin = star->Mag;
+    double mMax = star->Mag;
+    for (int i = 0; i < stars->n; i++) {
+	star = stars->data[i];
+	mMin = PS_MIN (star->Mag, mMin);
+	mMax = PS_MAX (star->Mag, mMax);
+    }
+
+    int nBin = 1 + (mMax - mMin) / dMag;
+    if (nBin <= 1) {
+	psLogMsg ("psastro", 4, "insufficient valid stars for this readout\n");
+	return NULL;
+    }
+
+    // create a histogram of the magnitudes
+    // bin[0] = mMin : mMin + dMag
+    // bin[i] = mMin + i*dMag : mMin + (i+1)*dMag
+    psVector *nMags = psVectorAlloc (nBin, PS_TYPE_F32);
+    psVectorInit (nMags, 0);
+
+    for (int i = 0; i < stars->n; i++) {
+	star = stars->data[i];
+	if (!isfinite(star->Mag)) continue;
+	int bin = (star->Mag - mMin) / dMag;
+	if (bin < 0) psAbort("bin cannot be negative!");
+	if (bin >= nBin) psAbort("bin cannot be > %d!", nBin);
+	nMags->data.F32[bin] += 1.0;
+    }
+
+    // find the peak and position
+    int iPeak = 0;
+    int nPeak = 0;
+    int sPeak = 0;
+    for (int i = 0; i < nMags->n; i++) {
+	if (nMags->data.F32[i] < nPeak) continue;
+	iPeak = i;
+	nPeak = nMags->data.F32[i];
+	sPeak += nPeak;
+    }
+
+    psVector *lnMag = psVectorAllocEmpty (nBin, PS_TYPE_F32);
+    psVector *Mag = psVectorAllocEmpty (nBin, PS_TYPE_F32);
+
+    // create 2 vectors represnting the dlogN/dlogS line
+    // exclude bins with no stars
+    // exclude points after the peak with N < 0.8*peak
+    int n = 0;
+    for (int i = 0; i < nMags->n; i++) {
+	if (nMags->data.F32[i] < 1) continue;
+	if ((i > iPeak) && (nMags->data.F32[i] < 0.8*nPeak)) continue;
+	lnMag->data.F32[n] = log10 (nMags->data.F32[i]);
+	Mag->data.F32[n] = mMin + (i + 0.5)*dMag;
+	n++;
+    }
+    lnMag->n = n;
+    Mag->n = n;
+    psLogMsg ("psastro", 4, "fitting %d points to luminosity function\n", n);
+
+    psVector *mask = psVectorAlloc (Mag->n, PS_TYPE_MASK);
+    psVectorInit (mask, 0);
+
+    psPolynomial1D *line = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 1);
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+    stats->clipSigma = 3.0;
+    stats->clipIter = 3;
+
+    if (!psVectorClipFitPolynomial1D(line, stats, mask, 0xff, lnMag, NULL, Mag)) {
+	psLogMsg ("psastro", 4, "Failed to fit the luminosity function\n");
+	return(NULL);
+    }
+
+    // find min and max unmasked magnitudes
+    double mMinValid = NAN;
+    double mMaxValid = NAN;
+    for (int i = 0; i < Mag->n; i++) {
+	if (mask->data.U8[i]) continue;
+	if (isnan(mMinValid) || (Mag->data.F32[i] < mMinValid)) {
+	    mMinValid = Mag->data.F32[i];
+	}
+	if (isnan(mMaxValid) || (Mag->data.F32[i] > mMaxValid)) {
+	    mMaxValid = Mag->data.F32[i];
+	}
+    }
+
+    psLogMsg ("psastro", 4, "logN = %f + %f mag\n", line->coeff[0], line->coeff[1]);
+    psLogMsg ("psastro", 4, "logN vs mag residuals: %f +/- %f\n", stats->sampleMedian, stats->sampleStdev);
+
+    pmLumFunc *lumFunc = pmLumFuncAlloc ();
+    lumFunc->mMin = mMinValid;
+    lumFunc->mMax = mMaxValid;
+    lumFunc->offset = line->coeff[0];
+    lumFunc->slope = line->coeff[1];
+    lumFunc->mPeak = mMin + (iPeak + 0.5)*dMag;
+    lumFunc->nPeak = nPeak;
+    lumFunc->sPeak = sPeak;
+
+    psFree (lnMag);
+    psFree (nMags);
+    psFree (Mag);
+    psFree (mask);
+    psFree (line);
+    psFree (stats);
+
+    return (lumFunc);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMetadataStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMetadataStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMetadataStats.c	(revision 22322)
@@ -0,0 +1,68 @@
+# include "psastroInternal.h"
+
+bool psastroMetadataStats (pmConfig *config) {
+
+    bool status;
+
+    char *filename = psMetadataLookupStr (&status, config->arguments, "STATS");
+    if (!filename) return true; // stats not requested
+
+    // Extract statistics from the last output fpa
+    pmFPAfile *output = psMetadataLookupPtr(&status, config->files, "PSASTRO.OUTPUT");
+
+    if (!output) {
+	psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find output file (PSASTRO.OUTPUT).");
+	return false;
+    }
+
+    // create output stats metadata
+    psMetadata *stats = psMetadataAlloc ();
+
+    // extract stats for the complete fpa 
+    pmFPAview *view = pmFPAviewAlloc(0);
+
+    if (!ppStatsMetadata(stats, output->fpa, view, 0, config)) {
+	psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
+	psFree(view);
+	psFree(stats);
+	return false;
+    }
+
+    // if we did not request any specific stats, the structure is empty
+    if (stats && stats->list->n == 0) {
+	psWarning ("stats output specified, but no requested stats entries in headers");
+	psFree(view);
+	psFree(stats);
+	return true;
+    }
+
+    // convert the stats MDC to a string
+    char *statsMDC = psMetadataConfigFormat(stats);
+    if (!statsMDC || strlen(statsMDC) == 0) {
+	psError(PS_ERR_IO, false, "Unable to serialize stats metadata.\n");
+	return false;
+    } 
+
+    // convert to a real UNIX filename
+    psString resolved = pmConfigConvertFilename(filename, config, true, false); // Resolved filename
+    FILE *statsFile = fopen (resolved, "w");
+    if (!statsFile) {
+	psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
+	psFree(stats);
+	psFree(view);
+	psFree(statsMDC);
+	psFree(resolved);
+	return false;
+    }
+
+    // write the stats MDC to a file
+    // XXX why does this not call psMetadataConfigPrint?
+    fprintf(statsFile, "%s", statsMDC);
+    fclose(statsFile);
+
+    psFree(resolved);
+    psFree(statsMDC);
+    psFree(view);
+    psFree(stats);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroModel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroModel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroModel.c	(revision 22322)
@@ -0,0 +1,53 @@
+# 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 = psastroModelArguments (argc, argv);
+
+    // load identify the data sources
+    if (!psastroModelParseCamera (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 (!psastroModelDataLoad (config)) {
+	psErrorStackPrint(stderr, "error loading input data\n");
+	exit (1);
+    }
+
+    // run the full astrometry analysis (chip and/or mosaic)
+    if (!psastroModelAnalysis (config)) {
+	psErrorStackPrint(stderr, "failure in psastro model analysis\n");
+	exit (1);
+    }
+    
+    // run the full astrometry analysis (chip and/or mosaic)
+    if (!psastroModelAdjust (config)) {
+	psErrorStackPrint(stderr, "failure in psastro model adjust\n");
+	exit (1);
+    }
+    
+    // save the model
+    if (!psastroModelDataSave (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: /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelAdjust.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelAdjust.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelAdjust.c	(revision 22322)
@@ -0,0 +1,245 @@
+# include "psastroStandAlone.h"
+# define NONLIN_TOL 0.001 /* tolerance in pixels */
+# define DEBUG 0
+
+bool psastroModelAdjustBoresite (pmFPAfile *output, pmChip *refChip);
+
+bool psastroModelAdjust (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;
+    }
+
+    // if we have not measured the boresite position, no adjustment is needed
+    bool fitBoresite = psMetadataLookupBool (&status, recipe, "PSASTRO.MODEL.FIT.BORESITE");
+    if (!status) psAbort ("Can't find recipe option PSASTRO.MODEL.FIT.BORESITE");
+
+    // as an alternative to fit the boresite from a rotation sequence, we can set the boresite
+    // relative to the reference chip coordinates
+    bool setBoresite = psMetadataLookupBool (&status, recipe, "PSASTRO.MODEL.SET.BORESITE");
+    if (!status) psAbort ("Can't find recipe option PSASTRO.MODEL.SET.BORESITE");
+
+    if (fitBoresite && setBoresite) {
+	psError(PS_ERR_IO, true, "invalid to choose both FIT.BORESITE and SET.BORESITE"); 
+	return false; 
+    } 
+	
+    pmFPAfile *output = psMetadataLookupPtr (&status, config->files, "PSASTRO.OUT.MODEL");
+    if (!status) psAbort ("Can't find output pmFPAfile PSASTRO.OUT.MODEL");
+    if (!output->fpa) psAbort ("no existing input fpa contains the reference chip");
+
+    // physical pixel scale in microns per pixel
+    char *refChipName = psMetadataLookupStr (&status, recipe, "PSASTRO.MODEL.REF.CHIP");
+    if (!refChipName) {
+	psError(PS_ERR_IO, true, "reference chip is missing from recipe"); 
+	return false; 
+    } 
+
+    // get reference chip from name
+    pmChip *refChip = pmConceptsChipFromName (output->fpa, refChipName);
+    if (!refChip) psAbort ("invalid chip name for reference");
+    if (!refChip->toFPA) psAbort ("invalid astrometry for reference chip");
+
+    // save the TPA region for tranformation inversions below
+    // psRegion *tpaRegion = pmAstromFPInTP (output->fpa);
+    psRegion *fpaRegion = pmAstromFPAExtent (output->fpa);
+
+    if (DEBUG) psastroDumpCorners ("corners.up.raw.dat", "corners.dn.raw.dat", output->fpa);
+
+    if (setBoresite) {
+	float boreXchip = psMetadataLookupF32 (&status, recipe, "PSASTRO.MODEL.BORESITE.X");
+	float boreYchip = psMetadataLookupF32 (&status, recipe, "PSASTRO.MODEL.BORESITE.Y");
+	psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.X0", PS_META_REPLACE, "boresite parameter", boreXchip); 
+	psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.Y0", PS_META_REPLACE, "boresite parameter", boreYchip); 
+    }
+
+    if (fitBoresite || setBoresite) {
+	psastroModelAdjustBoresite (output, refChip);
+    } else {
+	// FPA.BORE.X0,Y0 should be 0,0 in the focal plane, not the chip.  Ask for the
+	// coordinates which make refChip->toFPA(x,y) = (0,0)
+	psPlane *PT = psPlaneTransformGetCenter (refChip->toFPA, NONLIN_TOL);
+	psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.X0", PS_META_REPLACE, "boresite parameter", PT->x); 
+	psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.Y0", PS_META_REPLACE, "boresite parameter", PT->y); 
+	psFree (PT);
+    }
+
+    // rotate the chip-to-FPA transforms to have 0.0 posangle for refChip; 
+    // compensate by rotating fpa to TPA transform
+
+    // get the current posangle of the ref chip
+    float chipAngle = atan2 (refChip->toFPA->y->coeff[1][0], refChip->toFPA->x->coeff[1][0]);
+    fprintf (stderr, "chipAngle: %f\n", chipAngle*PS_DEG_RAD);
+    // psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.POSANGLE", PS_META_REPLACE, "boresite parameter", posangle);
+
+    // rotate the chip transforms
+    for (int i = 0; i < output->fpa->chips->n; i++) {
+	pmChip *chip = output->fpa->chips->data[i];
+	if (!chip->toFPA) continue;
+	// skip chips without astrometry
+
+	// save the region of this chip for the inversion below
+	psRegion *region = pmChipPixels (chip);
+
+	psPlaneTransform *toFPA = psPlaneTransformRotate (NULL, chip->toFPA, chipAngle);
+	psFree (chip->toFPA);
+	chip->toFPA = toFPA;
+
+	// invert the new fromFPA transform to get the new toFPA transform
+	psPlaneTransform *fromFPA = psPlaneTransformInvert(NULL, chip->toFPA, *region, 50);
+	psFree (chip->fromFPA);
+	chip->fromFPA = fromFPA;
+
+	psFree (region);
+
+	// save the transformation in the header
+	pmAstromWriteBilevelChip (chip->hdu->header, chip, NONLIN_TOL);
+    }
+
+    // get the current posangle of the fpa
+    float fpaAngle = atan2 (output->fpa->toTPA->y->coeff[1][0], output->fpa->toTPA->x->coeff[1][0]);
+    fprintf (stderr, "fpaAngle: %f\n", fpaAngle*PS_DEG_RAD);
+    // psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.POSANGLE", PS_META_REPLACE, "boresite parameter", posangle);
+
+    // remove the fpa rotation to generate a rotation-free model
+    psPlaneTransform *toTPA = psPlaneTransformRotate (NULL, output->fpa->toTPA, fpaAngle);
+    psFree (output->fpa->toTPA);
+    output->fpa->toTPA = toTPA;
+
+    psFree (output->fpa->fromTPA);
+    output->fpa->fromTPA = psPlaneTransformInvert(NULL, output->fpa->toTPA, *fpaRegion, 50);
+
+    // the model now describes the unrotated focal-plane
+    if (DEBUG) psastroDumpCorners ("corners.up.rot.dat", "corners.dn.rot.dat", output->fpa);
+
+    psMetadata *header = output->fpa->hdu->header;
+    pmAstromWriteBilevelMosaic (header, output->fpa, NONLIN_TOL);
+
+    psFree (fpaRegion);
+
+    return true;
+}
+
+bool psastroModelAdjustBoresite (pmFPAfile *output, pmChip *refChip) {
+
+    bool status;
+
+    psPlane  *boreCH  = psPlaneAlloc();
+    psPlane  *boreFP  = psPlaneAlloc();    
+    psPlane  *boreTP  = psPlaneAlloc();    
+    psSphere *boreSky = psSphereAlloc();    
+
+    // correct Xo,Yo to Lo,Mo using the ref chip toFPA
+    // ref chip position of the true boresite center
+    boreCH->x = psMetadataLookupF32 (&status, output->fpa->concepts, "FPA.BORE.X0"); 
+    boreCH->y = psMetadataLookupF32 (&status, output->fpa->concepts, "FPA.BORE.Y0"); 
+    psPlaneTransformApply (boreFP, refChip->toFPA, boreCH);
+
+    // adjust the reference pixel for all chips
+    for (int i = 0; i < output->fpa->chips->n; i++) {
+	pmChip *chip = output->fpa->chips->data[i];
+	if (!chip->fromFPA) continue;
+	// skip the chips without astrometry
+
+	// save the FPA region of this chip for the inversion below
+	psRegion *region = pmChipPixels (chip);
+
+	// the current toFPA returns boreFP->x,y for the boresite; subtract this from the transformations
+	chip->toFPA->x->coeff[0][0] -= boreFP->x;
+	chip->toFPA->y->coeff[0][0] -= boreFP->y;
+
+	// psPlaneTransform *toFPA = psPlaneTransformSetCenter (NULL, chip->toFPA, -boreFP->x, -boreFP->y);
+	// psFree (chip->toFPA);
+	// chip->toFPA = toFPA;
+
+	// invert the new fromFPA transform to get the new toFPA transform
+	// the region used here is the region covered by the chip in the FPA
+	psPlaneTransform *fromFPA = psPlaneTransformInvert(NULL, chip->toFPA, *region, 50); 
+	psFree (chip->fromFPA);
+	chip->fromFPA = fromFPA;
+
+	psFree (region);
+    }
+
+    if (DEBUG) psastroDumpCorners ("corners.up.shf.dat", "corners.dn.shf.dat", output->fpa);
+
+    // we have now adjusted the chips to use the correct boresite position as the center of the focal-plane system.
+    // we now need to reconstruct the TP to FP transformation, starting from stars projected about this new boresite position.
+
+    // find the R,D of the new boresite (boreFP -> 0,0; 0,0 -> -boreFP)
+    boreFP->x = -boreFP->x;
+    boreFP->y = -boreFP->y;
+    psPlaneTransformApply (boreTP, output->fpa->toTPA, boreFP);
+    psDeproject (boreSky, boreTP, output->fpa->toSky); // find the RA,DEC coord of the focal-plane coordinate
+
+    psProjection *newSky = psProjectionAlloc (boreSky->r, boreSky->d, output->fpa->toSky->Xs, output->fpa->toSky->Ys, output->fpa->toSky->type);
+
+    // generate a collection of points on the sky using the old toTPA transformation and toSky projection, projected with the newSky projection
+    // this is the FPA coordinate range covered by the FP: 
+    psRegion *fpaRegion = pmAstromFPAExtent (output->fpa);
+    float dx = (fpaRegion->x1 - fpaRegion->x0) / 50.0;
+    float dy = (fpaRegion->y1 - fpaRegion->y0) / 50.0;
+
+    psPlane fp, tp;
+    psSphere sky;
+
+    psVector *FPx = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *FPy = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *TPx = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *TPy = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+    // XXX a test: boreFP->x,y, should transform to tp.x,y = 0,0
+    fp.x = boreFP->x;
+    fp.y = boreFP->y;
+    psPlaneTransformApply (&tp, output->fpa->toTPA, &fp);
+    psDeproject (&sky, &tp, output->fpa->toSky); // find the RA,DEC coord of the focal-plane coordinate
+    psProject (&tp, &sky, newSky); // find the RA,DEC coord of the focal-plane coordinate
+
+    int Npts = 0;
+    for (fp.x = fpaRegion->x0; fp.x <= fpaRegion->x1; fp.x += dx) {
+	for (fp.y = fpaRegion->y0; fp.y <= fpaRegion->y1; fp.y += dy) {
+	    psPlaneTransformApply (&tp, output->fpa->toTPA, &fp);
+	    psDeproject (&sky, &tp, output->fpa->toSky); // find the RA,DEC coord of the focal-plane coordinate
+	    psProject (&tp, &sky, newSky); // find the RA,DEC coord of the focal-plane coordinate
+
+	    // we are fitting points in the NEW FP system to points in the NEW TP system
+	    FPx->data.F32[Npts] = fp.x - boreFP->x;
+	    FPy->data.F32[Npts] = fp.y - boreFP->y;
+	    TPx->data.F32[Npts] = tp.x;
+	    TPy->data.F32[Npts] = tp.y;
+	    psVectorExtend (FPx, 100, 1);
+	    psVectorExtend (FPy, 100, 1);
+	    psVectorExtend (TPx, 100, 1);
+	    psVectorExtend (TPy, 100, 1);
+	    Npts ++;
+	}
+    }
+    psFree (fpaRegion);
+
+    // fit both up and down transformations to the same points
+    psVectorFitPolynomial2D (output->fpa->toTPA->x, NULL, 0, TPx, NULL, FPx, FPy);
+    psVectorFitPolynomial2D (output->fpa->toTPA->y, NULL, 0, TPy, NULL, FPx, FPy);
+    psVectorFitPolynomial2D (output->fpa->fromTPA->x, NULL, 0, FPx, NULL, TPx, TPy);
+    psVectorFitPolynomial2D (output->fpa->fromTPA->y, NULL, 0, FPy, NULL, TPx, TPy);
+
+    psFree (output->fpa->toSky);
+    output->fpa->toSky = newSky;
+
+    if (DEBUG) psastroDumpCorners ("corners.up.bore.dat", "corners.dn.bore.dat", output->fpa);
+
+    psFree (FPx);
+    psFree (FPy);
+    psFree (TPx);
+    psFree (TPy);
+
+    psFree (boreCH);
+    psFree (boreFP);
+    psFree (boreTP);
+    psFree (boreSky);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelAnalysis.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelAnalysis.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelAnalysis.c	(revision 22322)
@@ -0,0 +1,162 @@
+# include "psastroStandAlone.h"
+# define NONLIN_TOL 0.001
+
+bool psastroModelAnalysis (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;
+    }
+
+    // reference chip for boresite parameters
+    char *refChip = psMetadataLookupStr (&status, recipe, "PSASTRO.MODEL.REF.CHIP");
+    if (!refChip) {
+	psError(PS_ERR_IO, true, "reference chip is missing from recipe"); 
+	return false; 
+    } 
+
+    pmFPAfile *output = psMetadataLookupPtr (&status, config->files, "PSASTRO.OUT.MODEL");
+    if (!status) psAbort ("Can't find output pmFPAfile PSASTRO.OUT.MODEL");
+
+    // measure the boresite position from a rotation sequence?
+    bool fitBoresite = psMetadataLookupBool (&status, recipe, "PSASTRO.MODEL.FIT.BORESITE");
+    if (!fitBoresite) {
+	psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.X0", PS_META_REPLACE, "boresite parameter", 0.0); 
+	psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.Y0", PS_META_REPLACE, "boresite parameter", 0.0); 
+	psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.RX", PS_META_REPLACE, "boresite parameter", 0.0); 
+	psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.RY", PS_META_REPLACE, "boresite parameter", 0.0); 
+	psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.T0", PS_META_REPLACE, "boresite parameter", 0.0); 
+	psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.P0", PS_META_REPLACE, "boresite parameter", 0.0); 
+	psMetadataAddStr (output->fpa->concepts, PS_LIST_TAIL, "FPA.REF.CHIP", PS_META_REPLACE, "boresite parameter", refChip);
+	return true; 
+    } 
+
+    char *outroot = psMetadataLookupStr (&status, config->arguments, "OUTPUT");
+    if (!status || !outroot) psAbort ("Can't find outroot on config->arguments");
+
+    /* model analysis:
+     *
+     * determine POS_ZERO via comparison of measured and reported posangles
+     * POS_ZERO = FPA.POSANGLE - posangle
+     *
+     * determine boresite model:
+     * X = Xo + R_X cos(FPA.POSANGLE - T_0) cos(P_0) + R_Y sin(FPA.POSANGLE - T_0) sin(P_0) 
+     * Y = Yo + R_Y sin(FPA.POSANGLE - T_0) cos(P_0) - R_X cos(FPA.POSANGLE - T_0) sin(P_0) 
+     * position of reported boresite in reference chip pixels
+     * Xo, Yo : true coordinate of boresite (rotator center) in reference chip pixels
+     * R_X, R_Y : amplitude of boresite offset
+     * T_0 : reference angle for rotator
+     * P_0 : orientation of boresite ellipse
+     *
+     */
+
+    // select the input pmFPAfile pointers
+    psMetadataItem *item = psMetadataLookup (config->files, "PSASTRO.WCS");
+    if (item == NULL) psAbort("missing PSASTRO.WCS entries in config->files");
+    if (item->type != PS_DATA_METADATA_MULTI) psAbort("unexpected type for PSASTRO.WCS");
+    psArray *files = psListToArray (item->data.list);
+
+    // data storage vectors for measurements
+    psVector *posZero  = psVectorAlloc (files->n, PS_TYPE_F32);
+    psVector *Po       = psVectorAlloc (files->n, PS_TYPE_F32);
+    psVector *Xo       = psVectorAlloc (files->n, PS_TYPE_F32);
+    psVector *Yo       = psVectorAlloc (files->n, PS_TYPE_F32);
+
+    // counter for accepted measured values
+    int n = 0;
+
+    // Re-select the output fpa.  The output fpa needs to have valid astrometry for the
+    // refChip.  output->fpa is a copy of the pointer to one of the input->fpa, but the choice
+    // is arbitrary.  select a new one that has an existing ref chip
+    psFree (output->fpa);
+    output->fpa = NULL;
+
+    char filename[256];
+    snprintf (filename, 256, "%s.bore", outroot);
+    FILE *outfile = fopen (filename, "w");
+    if (!outfile) {
+        psError(PSASTRO_ERR_ARGUMENTS, true, "cannot open %s for output", filename);
+	return false;
+    }
+
+    float posBoundary = 0.0;
+
+    // extract the relevant measured and reported values from the reference chip
+    for (int i = 0; i < files->n; i++) {
+	psMetadataItem *file = files->data[i];
+	pmFPAfile *input = file->data.V;
+
+	// reported rotator position angle
+	double POSANGLE = psMetadataLookupF64 (&status, input->fpa->concepts, "FPA.POSANGLE"); 
+	if (!status) psAbort ("missing FPA.POSANGLE");
+
+    	// get reference chip from name
+	pmChip *chip = pmConceptsChipFromName (input->fpa, refChip);
+	if (!chip) psAbort ("invalid chip name for reference");
+
+	if (!chip->toFPA) continue;
+
+	if (!output->fpa) {
+	    // this one matches
+	    output->fpa = psMemIncrRefCounter(input->fpa);
+	}
+
+	// we have two measurements of the posangle (may be parity flipped to different quadrants)
+	// atan2 returns values in the range 0-2pi
+	// all posZero values should be clustered in some region, but we need to flip over the 0,360 boundary correctly.
+	// push all to one side or the other
+	float chipAngle = PM_DEG_RAD * atan2 (chip->toFPA->y->coeff[1][0], chip->toFPA->x->coeff[1][0]);
+	float fpaAngle = PM_DEG_RAD * atan2 (input->fpa->toTPA->y->coeff[1][0], input->fpa->toTPA->x->coeff[1][0]);
+
+	posZero->data.F32[n] = POSANGLE - chipAngle - fpaAngle;
+	if (n == 0) {
+	    posBoundary = posZero->data.F32[n] + 180.0;
+	} else {
+	    while (posZero->data.F32[n] > posBoundary) posZero->data.F32[n] -= 360.0;
+	    while (posZero->data.F32[n] < posBoundary - 360.0) posZero->data.F32[n] += 360.0;
+	}
+
+	Po->data.F32[n] = POSANGLE * PM_RAD_DEG; // reported position angle
+	float xc = chip->fromFPA->x->coeff[0][0]; // reported boresite x position in ref chip coordinates
+	float yc = chip->fromFPA->y->coeff[0][0]; // reported boresite y position in ref chip coordinates
+	// XXX this can also be derived from toFPA via GetCenter....
+	
+	psPlane *PT = psPlaneTransformGetCenter (chip->toFPA, NONLIN_TOL);
+	Xo->data.F32[n] = PT->x; // reported boresite x position in ref chip coordinates
+	Yo->data.F32[n] = PT->y; // reported boresite y position in ref chip coordinates
+	psFree (PT);
+
+	fprintf (outfile, "%d : %f %f : %f = %f - %f - %f | %f %f\n", i, Xo->data.F32[n], Yo->data.F32[n], posZero->data.F32[n], POSANGLE, chipAngle, fpaAngle, xc, yc);
+	n ++;
+    }
+      
+    Xo->n = n;
+    Yo->n = n;
+    Po->n = n;
+    posZero->n = n;
+
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+    psVectorStats (stats, posZero, NULL, NULL, 0);
+
+    fprintf (outfile, "# pos zero %f +/- %f\n", stats->sampleMedian, stats->sampleStdev);
+    psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.POS_ZERO", PS_META_REPLACE, "offset between obs and meas posangle", stats->sampleMedian); 
+    fclose (outfile);
+
+    psVector *params = psastroModelFitBoresite (Xo, Yo, Po, outroot);
+    if (params->n != 6) psAbort ("error");
+
+
+    psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.X0", PS_META_REPLACE, "boresite parameter", params->data.F32[PAR_X0]); 
+    psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.Y0", PS_META_REPLACE, "boresite parameter", params->data.F32[PAR_Y0]); 
+    psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.RX", PS_META_REPLACE, "boresite parameter", params->data.F32[PAR_RX]); 
+    psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.RY", PS_META_REPLACE, "boresite parameter", params->data.F32[PAR_RY]); 
+    psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.T0", PS_META_REPLACE, "boresite parameter", params->data.F32[PAR_T0]); 
+    psMetadataAddF32 (output->fpa->concepts, PS_LIST_TAIL, "FPA.BORE.P0", PS_META_REPLACE, "boresite parameter", params->data.F32[PAR_P0]); 
+    psMetadataAddStr (output->fpa->concepts, PS_LIST_TAIL, "FPA.REF.CHIP", PS_META_REPLACE, "boresite parameter", refChip);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelArguments.c	(revision 22322)
@@ -0,0 +1,63 @@
+# include "psastroStandAlone.h"
+
+static char *usage = "USAGE: psastroModel [-output root] (cmffiles)";
+
+pmConfig *psastroModelArguments (int argc, char **argv) {
+
+    int N;
+
+    if (argc == 1) {
+        psError(PSASTRO_ERR_ARGUMENTS, true, "No arguments supplied");
+        psErrorStackPrint(stderr, 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, 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
+
+    // XXX no options yet defined
+    // psMetadata *options = pmConfigRecipeOptions (config, PSASTRO_RECIPE);
+
+    // define the output filename
+    if ((N = psArgumentGet (argc, argv, "-output"))) {
+        psArgumentRemove (N, &argc, argv);
+	psMetadataAddStr (config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    } else {
+        psError(PSASTRO_ERR_ARGUMENTS, true, "Missing -output (root)");
+        psErrorStackPrint(stderr, usage);
+	exit (1);
+    }
+
+    // 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);
+    }
+
+    // each additional word is a file; create names INPUT.%d for them
+    for (int i = 0; i < argc - 1; i++) {
+	char name[16];
+	snprintf (name, 16, "INPUT.%d", i);
+	psArray *array = psArrayAlloc(1);
+	array->data[0] = psStringCopy (argv[i+1]);
+	psMetadataAddPtr(config->arguments, PS_LIST_TAIL, name,  PS_DATA_ARRAY, "", array);
+    }	
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "INPUT.N", 0, "", argc - 1);
+    return (config);
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelBoresite.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelBoresite.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelBoresite.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "psastroStandAlone.h"
+
+// the full chisq is built of two associated sums over coordinates:
+// chisq = sum ((X_obs - X_fit(t))^2 + (Y_obs - Y_fit(t))^2)
+// we use split this into a 2x long vector and use coord[1] to distinguish the X and Y terms:
+// coord[0] = measured X or measured Y
+// coord[1] =          0 or          1
+psF32 psastroModelBoresite (psVector *deriv, const psVector *params, const psVector *coord) {
+
+    psF32 *PAR = params->data.F32;
+
+    float csphi = cos(PAR[PAR_P0]);
+    float snphi = sin(PAR[PAR_P0]);
+
+    float dtheta = coord->data.F32[0] - PAR[PAR_T0];
+    float cstht = cos(dtheta);
+    float sntht = sin(dtheta);
+
+    // value is X
+    if (coord->data.F32[1] == 0) {
+
+	float value = PAR[PAR_X0] + PAR[PAR_RX]*cstht*csphi + PAR[PAR_RY]*sntht*snphi;
+
+	if (deriv) {
+	    psF32 *dPAR = deriv->data.F32;
+	    dPAR[PAR_X0] = 1.0;
+	    dPAR[PAR_Y0] = 0.0;
+	    dPAR[PAR_RX] = +cstht*csphi;
+	    dPAR[PAR_RY] = +sntht*snphi;
+	    dPAR[PAR_P0] = -PAR[PAR_RX]*cstht*snphi + PAR[PAR_RY]*sntht*csphi;
+	    dPAR[PAR_T0] =  PAR[PAR_RX]*sntht*csphi - PAR[PAR_RY]*cstht*snphi;
+	}
+	return (value);
+    }  
+
+    // value is Y
+    if (coord->data.F32[1] == 1) {
+	float value = PAR[PAR_Y0] + PAR[PAR_RY]*sntht*csphi - PAR[PAR_RX]*cstht*snphi;
+
+	if (deriv) {
+	    psF32 *dPAR = deriv->data.F32;
+	    dPAR[PAR_X0]  = 0.0;
+	    dPAR[PAR_Y0]  = 1.0;
+	    dPAR[PAR_RX]  = -cstht*snphi;
+	    dPAR[PAR_RY]  = +sntht*csphi;
+	    dPAR[PAR_P0]  = -PAR[PAR_RY]*sntht*snphi - PAR[PAR_RX]*cstht*csphi;
+	    dPAR[PAR_T0]  = -PAR[PAR_RY]*cstht*csphi - PAR[PAR_RX]*sntht*snphi;
+	}
+	return (value);
+    }  
+    psAbort ("programming error: invalid coordinate");
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelDataLoad.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelDataLoad.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelDataLoad.c	(revision 22322)
@@ -0,0 +1,100 @@
+# include "psastroStandAlone.h"
+
+// this loop loads the header data from the input files, using the output pmFPAfile to guide
+// the chip selection and related issues
+
+# define ESCAPE { \
+  psError(PS_ERR_UNKNOWN, false, "Failure in psastroModelDataLoad"); \
+  psFree (view); \
+  return false; \
+}
+  
+// all of the different astrometry analysis modes use the same data load loop
+bool psastroModelDataLoad (pmConfig *config) {
+
+    bool status;
+    pmChip *chip;
+
+    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 *output = psMetadataLookupPtr (NULL, config->files, "PSASTRO.OUT.MODEL");
+    if (!output) psAbort ("PSASTRO.OUT.MODEL not listed in config->files");
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // files associated with the science image
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+
+    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_BEFORE)) ESCAPE;
+    }
+    psLogMsg ("psastro", 3, "load headers : %f sec\n", psTimerMark ("psastro"));
+
+    // we should have a number of different files stored in config->files as PSASTRO.WCS
+    psMetadataItem *item = psMetadataLookup (config->files, "PSASTRO.WCS");
+    if (item == NULL) psAbort("missing PSASTRO.WCS entries in config->files");
+    if (item->type != PS_DATA_METADATA_MULTI) psAbort("unexpected type for PSASTRO.WCS");
+    psArray *files = psListToArray (item->data.list);
+
+    // convert the headers for the input file into fpa astrometry terms
+    for (int i = 0; i < files->n; i++) {
+	psMetadataItem *file = files->data[i];
+	pmFPAfile *input = file->data.V;
+
+	pmFPAviewReset (view);
+
+	// check PHU header to see if we are using mosaic-level or per-chip astrometry
+	bool bilevelAstrometry = false;
+	pmHDU *phu = pmFPAviewThisPHU (view, input->fpa);
+	if (phu) {
+	    char *ctype = psMetadataLookupStr (NULL, phu->header, "CTYPE1");
+	    if (ctype) bilevelAstrometry = !strcmp (&ctype[4], "-DIS");
+	}
+	if (bilevelAstrometry) {
+	    pmAstromReadBilevelMosaic (input->fpa, phu->header);
+	} 
+
+	while ((chip = pmFPAviewNextChip (view, input->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; }
+
+	    // read WCS data from the corresponding header
+	    pmHDU *hdu = pmFPAviewThisHDU (view, input->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 (input->fpa, chip, hdu->header, pixelScale)) {
+		    psWarning("Could not get WCS information from header for chip %d, skipping", view->chip); 
+		    continue;
+		} 
+	    }
+	}
+    }
+    psLogMsg ("psastro", 3, "convert wcs terms to internal format : %f sec\n", psTimerMark ("psastro"));
+
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelDataSave.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelDataSave.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelDataSave.c	(revision 22322)
@@ -0,0 +1,40 @@
+# 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: /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelFit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelFit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelFit.c	(revision 22322)
@@ -0,0 +1,37 @@
+# include "psastroStandAlone.h"
+
+int main (int argc, char **argv) {
+
+    psTimerStart ("complete");
+
+    if (argc != 3) {
+	fprintf (stderr, "USAGE: psastroModelFit (input) (output)\n");
+	exit (1);
+    }
+
+    FILE *f = fopen (argv[1], "r");
+    if (f == NULL) {
+	fprintf (stderr, "problem opening data file %s\n", argv[1]);
+	exit (2);
+    }
+
+    char name[1024];
+    psVector *Xo = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *Yo = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *Po = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+    float x, y, p;
+
+    while (fscanf (f, "%s %f %f %f", name, &x, &y, &p) != EOF) {
+	psVectorAppend (Xo, x);
+	psVectorAppend (Yo, y);
+	psVectorAppend (Po, p*PS_RAD_DEG);
+    }	
+
+    // psTraceSetLevel("psLib.math", 5);
+    psastroModelFitBoresite (Xo, Yo, Po, argv[2]);
+
+    psLogMsg ("psastro", 3, "complete psastroModelFit run: %f sec\n", psTimerMark ("complete"));
+
+    exit (EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelFitBoresite.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelFitBoresite.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelFitBoresite.c	(revision 22322)
@@ -0,0 +1,93 @@
+# include "psastroStandAlone.h"
+
+// we now have a set of observed L,M values.  fit these to the boresite model
+psVector *psastroModelFitBoresite (psVector *Xo, psVector *Yo, psVector *Po, char *outroot) {
+
+    assert (Xo->n > 2);
+    assert (Xo->n == Yo->n);
+    assert (Xo->n == Po->n);
+
+    // arrays to hold the data to be fitted
+    psArray *x = psArrayAlloc(2*Xo->n);
+    psVector *y = psVectorAlloc(2*Xo->n, PS_TYPE_F32);
+
+    int n = 0;
+    for (int i = 0; i < Xo->n; i++) {
+
+	psVector *coord = NULL;
+
+	// X coordinate value
+	coord = psVectorAlloc (2, PS_TYPE_F32);
+	coord->data.F32[1] = 0.0;
+	coord->data.F32[0] = Po->data.F32[i];
+	x->data[n] = coord;
+	y->data.F32[n] = Xo->data.F32[i];
+	n++;
+	
+	// Y coordinate value
+	coord = psVectorAlloc (2, PS_TYPE_F32);
+	coord->data.F32[1] = 1.0;
+	coord->data.F32[0] = Po->data.F32[i];
+	x->data[n] = coord;
+	y->data.F32[n] = Yo->data.F32[i];
+	n++;
+    }	
+    assert (x->n == n);
+    assert (y->n == n);
+
+    psVector *params = psVectorAlloc (6, PS_TYPE_F32);
+    
+    // create the minimization constraints
+    psMinConstraint *constraint = psMinConstraintAlloc();
+
+    // XXX for now, no parameter masks, skip checkLimits
+    // constraint->checkLimits = psastroModelBoresiteLimits;
+
+    // make an initial guess:
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_MAX | PS_STAT_MIN);
+
+    // center (Xo) = mean(Xo), RX = range / 2
+    psVectorStats (stats, Xo, NULL, NULL, 0);
+    params->data.F32[PAR_X0] = stats->sampleMean;
+    params->data.F32[PAR_RX] = (stats->max - stats->min) / 2.0;
+
+    // center (Yo) = mean(Yo), RY = range / 2
+    psVectorStats (stats, Yo, NULL, NULL, 0);
+    params->data.F32[PAR_Y0] = stats->sampleMean;
+    params->data.F32[PAR_RY] = (stats->max - stats->min) / 2.0;
+
+    params->data.F32[PAR_P0] = 0.0;
+    params->data.F32[PAR_T0] = 0.0;
+
+    psMinimization *myMin = psMinimizationAlloc (25, 0.001);
+    psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
+    
+    // fprintf (stderr, "guess values:\n");
+    // fprintf (stderr, "Xo:  %f\n", params->data.F32[PAR_X0]);
+    // fprintf (stderr, "Yo:  %f\n", params->data.F32[PAR_Y0]);
+    // fprintf (stderr, "RX:  %f\n", params->data.F32[PAR_RX]);
+    // fprintf (stderr, "RY:  %f\n", params->data.F32[PAR_RY]);
+    // fprintf (stderr, "P0:  %f\n", params->data.F32[PAR_P0]);
+    // fprintf (stderr, "T0:  %f\n", params->data.F32[PAR_T0]);
+
+    // XXX skip the weights for now
+    psMinimizeLMChi2(myMin, covar, params, constraint, x, y, NULL, psastroModelBoresite);
+
+    char filename[256];
+    snprintf (filename, 256, "%s.pars", outroot);
+    FILE *outfile = fopen (filename, "w");
+    if (!outfile) {
+        psAbort("cannot open %s for output", filename);
+    }
+
+    fprintf (outfile, "# fitted values:\n");
+    fprintf (outfile, "Xo:  %f\n", params->data.F32[PAR_X0]);
+    fprintf (outfile, "Yo:  %f\n", params->data.F32[PAR_Y0]);
+    fprintf (outfile, "RX:  %f\n", params->data.F32[PAR_RX]);
+    fprintf (outfile, "RY:  %f\n", params->data.F32[PAR_RY]);
+    fprintf (outfile, "P0:  %f\n", params->data.F32[PAR_P0]);
+    fprintf (outfile, "T0:  %f\n", params->data.F32[PAR_T0]);
+    fclose (outfile);
+
+    return params;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelParseCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelParseCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroModelParseCamera.c	(revision 22322)
@@ -0,0 +1,44 @@
+# include "psastroInternal.h"
+
+bool psastroModelParseCamera (pmConfig *config) {
+
+    bool status = false;
+    pmFPAfile *input = NULL;
+
+    int nInput = psMetadataLookupS32 (&status, config->arguments, "INPUT.N");
+    if (!status) psAbort ("missing INPUT.N in config->arguments");
+
+    // find and load the input files (this determines the camera and builds the fpa structures,
+    // but does not load the data)
+    for (int i = 0; i < nInput; i++) {
+	char name[16];
+	snprintf (name, 16, "INPUT.%d", i);
+	input = pmFPAfileDefineFromArgs (&status, config, "PSASTRO.WCS", name);
+    }
+	
+    // 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;
+
+    // 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 (output->fpa, -1, true); // deselect all chips
+	for (int i = 0; i < chips->n; i++) {
+	    int chipNum = atoi(chips->data[i]);
+	    if (! pmFPASelectChip(output->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 psastroModelParseCamera...\n");
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicAstrom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicAstrom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicAstrom.c	(revision 22322)
@@ -0,0 +1,151 @@
+# include "psastroInternal.h"
+# define NONLIN_TOL 0.001 /* tolerance in pixels */
+
+bool psastroMosaicFit (pmFPA *fpa, psMetadata *recipe, const char *rootname, int pass);
+
+// XXX require this fpa to have multiple chip extensions and a PHU?
+bool psastroMosaicAstrom (pmConfig *config) {
+
+    bool status;
+    char filename[256];
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+	psError(PSASTRO_ERR_CONFIG, false, "Can't find PSASTRO recipe!\n");
+	return false;
+    }
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    if (!input) {
+	psError(PSASTRO_ERR_CONFIG, false, "Can't find input data!\n");
+	return false;
+    }
+
+    pmFPA *fpa = input->fpa;
+
+    // before we do object matches, we need to (optionally) fix failed chips.  We compare chips with
+    // the supplied mosaic model.  Adjust significant outliers to match model.  
+    # if (0)
+    if (!psastroFixChips (config, recipe)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to align problematic chips");
+	return false;
+    }
+    if (!psastroFixChipsTest (config, recipe)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to align problematic chips");
+	return false;
+    }
+    # endif
+
+    char *outroot = psMetadataLookupStr (&status, config->arguments, "OUTPUT");
+    if (!status || !outroot) psAbort ("Can't find outroot on config->arguments");
+
+    if (!psastroMosaicFit (fpa, recipe, outroot, 0)) return false;
+    if (!psastroMosaicFit (fpa, recipe, outroot, 1)) return false;
+    if (!psastroMosaicFit (fpa, recipe, outroot, 2)) return false;
+    if (!psastroMosaicFit (fpa, recipe, outroot, 3)) return false;
+
+    // now fit the chips under the common distortion with higher-order terms
+    // first, re-perform the match with a slightly tighter circle
+    if (!psastroMosaicSetMatch (fpa, recipe, 4)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to match raw and ref stars for mosaic (4th pass)");
+	return false;
+    }
+    if (!psastroMosaicChipAstrom (fpa, recipe, 4)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to measure chip astrometry in mosaic mode (4th pass)");
+	return false;
+    }
+    if (psTraceGetLevel("psastro.dump") > 0) { 
+	snprintf (filename, 256, "%s.10.dat", outroot);
+	psastroDumpMatches (fpa, filename); 
+    }
+
+    // save WCS and analysis metadata in update header.
+    psMetadata *updates = psMetadataAlloc();
+    if (!pmAstromWriteBilevelMosaic (updates, fpa, NONLIN_TOL)) {
+	psAbort ("failed to save header terms");
+    }
+
+    // write the elapsed time here; this will be updated in psastroMosaicAstrometry, if called
+    psMetadataAddF32 (updates, PS_LIST_TAIL, "DT_ASTR", PS_META_REPLACE, "elapsed psastro time", psTimerMark ("psastroAnalysis"));
+
+    psMetadataAddMetadata (fpa->analysis, PS_LIST_TAIL, "PSASTRO.HEADER",  PS_META_REPLACE, "psastro header stats", updates);
+    psFree (updates);
+
+    // update the headers based on the results
+    // XXX need to add global summary statistics
+    // psastroMosaicHeaders (config);
+
+    return true;
+}
+
+/* coordinate frame hierachy
+ * pixels (on a given readout)
+ * cell
+ * chip
+ * FP (focal plane)
+ * TP (tangent plane)
+ * sky (ra, dec)
+ */
+
+// 1: match 4,5
+// 2: match 6,7
+// 3: match 8,9
+bool psastroMosaicFit (pmFPA *fpa, psMetadata *recipe, const char *rootname, int pass) {
+
+    char filename[256];
+
+    // given the existing per-chip astrometry, determine matches between raw and ref stars
+    // is this needed? yes, if we didn't do SingleChip astrometry first
+    if (!psastroMosaicSetMatch (fpa, recipe, pass)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to match raw and ref stars for mosaic (pass %d)", pass);
+	return false;
+    }
+
+    if ((pass == 0) && (psTraceGetLevel("psastro.dump.psastroMosaicAstrom") > 1)) { 
+	snprintf (filename, 256, "%s.0.dat", rootname);
+	psastroDumpMatches (fpa, filename); 
+    }
+
+    // fitted chips will follow the local plate-scale, hiding the distortion
+    // modify the chip->toFPA scaling to match knowledge about pixel scale,
+    // then recalculate raw and ref positions
+    if (!psastroMosaicCommonScale (fpa, recipe)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to set a common scale for the chips (pass %d)", pass);
+	return false;
+    }
+
+    if ((pass == 0) && (psTraceGetLevel("psastro.dump.psastroMosaicAstrom") > 1)) { 
+	snprintf (filename, 256, "%s.1.dat", rootname);
+	psastroDumpMatches (fpa, filename); 
+    }
+
+    // fit the distortion by fitting its gradient
+    // apply the new distortion terms up and down
+    // refit the per-chip terms with linear fits only
+    if (!psastroMosaicDistortion (fpa, recipe, pass)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to measure mosaic gradients (pass %d)", pass);
+	return false;
+    }
+
+    snprintf (filename, 256, "%s.%d.dat", rootname, 2*pass + 2);
+    if (psTraceGetLevel("psastro.dump.psastroMosaicAstrom") > 1) { psastroDumpMatches (fpa, filename); }
+    
+    // measure the astrometry for the chips under the distortion term
+    if (!psastroMosaicChipAstrom (fpa, recipe, pass)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to measure chip astrometry in mosaic mode (pass %d)", pass);
+	return false;
+    }
+
+    int traceLevel = psTraceGetLevel("psastro.dump.psastroMosaicAstrom");
+    if (traceLevel > 1) {
+	snprintf (filename, 256, "%s.%d.dat", rootname, 2*pass + 3);
+	psastroDumpMatches (fpa, filename); 
+    } else {
+	snprintf (filename, 256, "%s.dat", rootname);
+	psastroDumpMatches (fpa, filename); 
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicChipAstrom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicChipAstrom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicChipAstrom.c	(revision 22322)
@@ -0,0 +1,47 @@
+# include "psastroInternal.h"
+# define NONLIN_TOL 0.001 /* tolerance in pixels */
+
+bool psastroMosaicChipAstrom (pmFPA *fpa, psMetadata *recipe, int iteration) {
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+    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->toFPA) { 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
+            // XXX there can only be one readout per chip, right?
+            while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+                if (! readout->data_exists) { continue; }
+
+                // save WCS and analysis metadata in update header
+                psMetadata *updates = psMetadataAlloc();
+
+                if (!psastroMosaicOneChip (chip, readout, recipe, updates, iteration)) {
+                    readout->data_exists = false;
+                    psError(PS_ERR_UNKNOWN, false, "failed to find a solution for %d,%d,%d\n",
+                            view->chip, view->cell, view->readout);
+                    psFree(updates);
+                    psFree(view);
+                    return false;
+                }
+
+                // create the header keywords to descripe the results
+                pmAstromWriteBilevelChip (updates, chip, NONLIN_TOL);
+                psMetadataAddMetadata (readout->analysis, PS_LIST_TAIL, "PSASTRO.HEADER",  PS_META_REPLACE, "psastro header stats", updates);
+                psFree (updates);
+            }
+        }
+    }
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicCorrectDistortion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicCorrectDistortion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicCorrectDistortion.c	(revision 22322)
@@ -0,0 +1,81 @@
+# include "psastroInternal.h"
+
+bool psastroMosaicCorrectDistortion (pmFPA *fpa, psPlaneTransform *TPtoFP) {
+
+    // invert the linear TPtoFP transform
+    psPlaneTransform *FPtoTP = p_psPlaneTransformLinearInvert (TPtoFP);
+    if (FPtoTP == NULL) {
+        psError (PS_ERR_UNKNOWN, false, "failed to invert TPtoFP\n");
+        return false;
+    }
+
+    // store the new coeffs in a new structure
+    psPlaneTransform *toTPAnew = psPlaneTransformAlloc(fpa->toTPA->x->nX, fpa->toTPA->x->nY);
+
+    // set the new coeffs, or set the mask
+    for (int i = 0; i <= toTPAnew->y->nX; i++) {
+        for (int j = 0; j <= toTPAnew->y->nY; j++) {
+
+	    // init the coeffs
+	    toTPAnew->x->coeff[i][j] = 0.0;
+	    toTPAnew->y->coeff[i][j] = 0.0;
+
+	    // if both are masked, mask the outpu
+            if ((fpa->toTPA->x->coeffMask[i][j] & PS_POLY_MASK_SET) && (fpa->toTPA->x->coeffMask[i][j] & PS_POLY_MASK_SET)) {
+		toTPAnew->x->coeffMask[i][j] = PS_POLY_MASK_SET;
+		toTPAnew->y->coeffMask[i][j] = PS_POLY_MASK_SET;
+		continue;
+	    } 
+
+	    // set the X terms
+	    toTPAnew->x->coeff[i][j] += (fpa->toTPA->x->coeffMask[i][j] & PS_POLY_MASK_SET) ? 0.0 : FPtoTP->x->coeff[1][0] * fpa->toTPA->x->coeff[i][j];
+	    toTPAnew->x->coeff[i][j] += (fpa->toTPA->y->coeffMask[i][j] & PS_POLY_MASK_SET) ? 0.0 : FPtoTP->x->coeff[0][1] * fpa->toTPA->y->coeff[i][j];
+
+	    // set the Y terms
+	    toTPAnew->y->coeff[i][j] += (fpa->toTPA->x->coeffMask[i][j] & PS_POLY_MASK_SET) ? 0.0 : FPtoTP->y->coeff[1][0] * fpa->toTPA->x->coeff[i][j];
+	    toTPAnew->y->coeff[i][j] += (fpa->toTPA->y->coeffMask[i][j] & PS_POLY_MASK_SET) ? 0.0 : FPtoTP->y->coeff[0][1] * fpa->toTPA->y->coeff[i][j];
+        }
+    }
+
+    // adjust the 0,0 terms:
+    toTPAnew->x->coeff[0][0] += FPtoTP->x->coeff[0][0];
+    toTPAnew->y->coeff[0][0] += FPtoTP->y->coeff[0][0];
+
+    psFree (fpa->toTPA);
+    fpa->toTPA = toTPAnew;
+
+    // invert toTPA to determine fromTPA. choose an appropriate region based on the dimensions
+    // of the complete FPA
+    psRegion *region = pmAstromFPAExtent (fpa);
+
+    psFree (fpa->fromTPA);
+    fpa->fromTPA = psPlaneTransformInvert(NULL, fpa->toTPA, *region, 50);
+    psFree (region);
+
+    if (fpa->fromTPA == NULL) {
+        psError (PS_ERR_UNKNOWN, false, "failed to invert fpa->toTPA\n");
+	psFree (FPtoTP);
+        return false;
+    }
+
+    psFree (FPtoTP);
+    return true;
+}
+
+// we have three coordinate systems and two polynomial transformations:
+// TP is the raw tangent plane coordinate system (aligned with RA,DEC)
+// FP is the raw focal plane coordinate system (aligned with camera X,Y)
+// dFP is the distorted focal plane coordinate system
+
+// fpa->toTPA is a polynomial transformation from FP to dFP
+//   L(x,y) = \sum_i \sum_j A_{i,j} x^i y^j and 
+//   M(x,y) = \sum_i \sum_j B_{i,j} x^i y^j 
+//   where (x,y) are the FP coords and L,M  are the dFP coords
+
+// FPtoTP is a linear transformation from dFP to TP (we recover this from TPtoFP by inversion)
+// P(L,M) = r_xo + r_xx L + r_xy M
+// Q(L,M) = r_yo + r_yx L + r_yy M
+
+// we need to merge them into a single transformation:
+// P(x,y) = r_xo + r_xx * \sum_i \sum_j A_{i,j} x^i y^j + r_xy * \sum_i \sum_j B_{i,j} x^i y^j 
+// Q(x,y) = r_yo + r_yx * \sum_i \sum_j A_{i,j} x^i y^j + r_yy * \sum_i \sum_j B_{i,j} x^i y^j 
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicDistortion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicDistortion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicDistortion.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "psastroInternal.h"
+# define DEBUG 0
+
+bool psastroMosaicDistortion (pmFPA *fpa, psMetadata *recipe, int pass) {
+
+    // fit a linear transformation from reference TP to observed FP coords
+    psPlaneTransform *TPtoFP = psastroMosaicFitRotAndScale (fpa);
+    if (!TPtoFP) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to fit a linear TP correction\n");
+        return false;
+    }
+
+    if (DEBUG && (pass == 0)) psastroDumpCorners ("corners.up.v1.dat", "corners.dn.v1.dat", fpa);
+
+    // Correct the current reference star TP coordinates to the nearly-FP
+    // system.  We note two points here: 1) the corrected TP coordinates are
+    // NOT consistent with the sky coordinates, 2) the remaining differnce
+    // between the new TP reference coords and the observerd FP coordinates is
+    // only distortion
+    if (!psastroMosaicApplyRotAndScale (fpa, TPtoFP)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to apply the linear TP correction\n");
+	psFree (TPtoFP);
+        return false;
+    }
+
+    if (DEBUG && (pass == 0)) psastroDumpCorners ("corners.up.v2.dat", "corners.dn.v2.dat", fpa);
+
+    if (!psastroMosaicDistortionFromGradients (fpa, recipe)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to fit the distortion field\n");
+	psFree (TPtoFP);
+        return false;
+    }
+
+    if (DEBUG && (pass == 0)) psastroDumpCorners ("corners.up.v3.dat", "corners.dn.v3.dat", fpa);
+
+    if (!psastroMosaicCorrectDistortion (fpa, TPtoFP)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to correct the distortion for the linear fit\n");
+	psFree (TPtoFP);
+        return false;
+    }
+	
+    if (DEBUG && (pass == 0)) psastroDumpCorners ("corners.up.v4.dat", "corners.dn.v4.dat", fpa);
+
+    if (!psastroMosaicSetAstrom (fpa)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to apply mosaic distortion terms\n");
+	psFree (TPtoFP);
+        return false;
+    }
+
+    if (DEBUG && (pass == 0)) psastroDumpCorners ("corners.up.v5.dat", "corners.dn.v5.dat", fpa);
+
+    psFree (TPtoFP);
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicFPtoTP.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicFPtoTP.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicFPtoTP.c	(revision 22322)
@@ -0,0 +1,164 @@
+# include "psastroInternal.h"
+
+/************************************************/
+psPlaneTransform *psastroMosaicFitRotAndScale (pmFPA *fpa) {
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+
+    // XXX first pass: fit and remove any linear fp->tp transformation
+    // accumulate FP(x,y) & TP(x,y) in a single set of vectors
+
+    psVector *X  = psVectorAllocEmpty(100, PS_TYPE_F32);
+    psVector *Y  = psVectorAllocEmpty(100, PS_TYPE_F32);
+    psVector *x  = psVectorAllocEmpty(100, PS_TYPE_F32);
+    psVector *y  = psVectorAllocEmpty(100, PS_TYPE_F32);
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // int nPts = 0;
+    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->toFPA) { 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
+	    // XXX there can only be one readout per chip, right?
+	    while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+		if (! readout->data_exists) { continue; }
+
+		// select the raw objects for this readout
+		psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+		if (rawstars == NULL) { continue; }
+
+		// select the raw objects for this readout
+		psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+		if (refstars == NULL) { continue; }
+
+		psArray *match = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
+		if (match == NULL) { continue; }
+
+		// take the matched stars, first fit
+		for (int i = 0; i < match->n; i++) {
+		    pmAstromMatch *pair = match->data[i];
+		    pmAstromObj *rawStar = rawstars->data[pair->raw];
+		    pmAstromObj *refStar = refstars->data[pair->ref];
+
+		    // independent variables
+		    X->data.F32[X->n] = refStar->TP->x;
+		    Y->data.F32[Y->n] = refStar->TP->y;
+
+		    // fitted values
+		    x->data.F32[x->n] = rawStar->FP->x;
+		    y->data.F32[y->n] = rawStar->FP->y;
+
+		    psVectorExtend (X, 100, 1);
+		    psVectorExtend (Y, 100, 1);
+		    psVectorExtend (x, 100, 1);
+		    psVectorExtend (y, 100, 1);
+		}
+	    }
+	}
+    }
+    // x->n = y->n = X->n = Y->n = nPts;
+
+    // linear fit without xy cross term
+    psPlaneTransform *map = psPlaneTransformAlloc (1, 1);
+    map->x->coeffMask[1][1] = PS_POLY_MASK_SET;
+    map->y->coeffMask[1][1] = PS_POLY_MASK_SET;
+
+    // constant errors
+    psVector *mask = psVectorAlloc (X->n, PS_TYPE_U8);
+    psVectorInit (mask, 0);
+
+    // the stats options supplied are used to perform the clip fitting
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+    stats->clipIter = 1;
+
+    // fit TP-to-FP transformation
+
+    // we run 3 cycles clipping in each of x and y, with only one iteration each.
+    // XXX use the stats lookups functions to get the width and center
+    for (int i = 0; i < 3; i++) {
+	if (!psVectorClipFitPolynomial2D (map->x, stats, mask, 0xff, x, NULL, X, Y)) {
+            psError(PS_ERR_UNKNOWN, false, "failure in clip-fitting for x\n");
+	    psFree (map);
+	    map = NULL;
+	    goto escape;
+	}
+        psTrace ("psastro", 3, "x resid: %f +/- %f (%ld of %ld)\n", stats->clippedMean, stats->clippedStdev, stats->clippedNvalues, x->n);
+
+        if (!psVectorClipFitPolynomial2D (map->y, stats, mask, 0xff, y, NULL, X, Y)) {
+            psError(PS_ERR_UNKNOWN, false, "failure in clip-fitting for y\n");
+	    psFree (map);
+	    map = NULL;
+	    goto escape;
+	}
+        psTrace ("psastro", 3, "y resid: %f +/- %f (%ld of %ld)\n", stats->clippedMean, stats->clippedStdev, stats->clippedNvalues, y->n);
+    }
+
+escape:
+    psFree (x);
+    psFree (y);
+    psFree (X);
+    psFree (Y);
+    psFree (mask);
+    psFree (view);
+    psFree (stats);
+
+    return (map);
+}
+
+/*****************************/
+bool psastroMosaicApplyRotAndScale (pmFPA *fpa, psPlaneTransform *TPtoFP) {
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+    psPlane newTP;
+
+    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->toFPA) { 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
+	    // XXX there can only be one readout per chip, right?
+	    while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+		if (! readout->data_exists) { continue; }
+
+		// select the raw objects for this readout
+		psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+		if (refstars == NULL) { continue; }
+
+		// take the matched stars, first fit
+		for (int i = 0; i < refstars->n; i++) {
+		    pmAstromObj *refStar = refstars->data[i];
+
+		    // Correct the current reference star TP coordinates to the nearly-FP
+		    // system.  We note two points here: 1) the corrected TP coordinates are
+		    // NOT consistent with the sky coordinates, 2) the remaining differnce
+		    // between the new TP reference coords and the observerd FP coordinates is
+		    // only distortion
+
+		    psPlaneTransformApply (&newTP, TPtoFP, refStar->TP);
+		    refStar->TP->x = newTP.x;
+		    refStar->TP->y = newTP.y;
+		}
+	    }
+	}
+    }
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicGetGrads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicGetGrads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicGetGrads.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "psastroInternal.h"
+
+psArray *psastroMosaicGetGrads (pmFPA *fpa, psMetadata *recipe) {
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+    psArray *grads = NULL;
+
+    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; }
+	
+	psRegion *region = pmChipExtent (chip);
+
+	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
+	    // XXX there can only be one readout per chip, right?
+	    while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+		if (! readout->data_exists) { continue; }
+
+		// select the raw objects for this readout
+		psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+		if (rawstars == NULL) { continue; }
+
+		// select the raw objects for this readout
+		psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+		if (refstars == NULL) { continue; }
+
+		psArray *match = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
+		if (match == NULL) { continue; }
+
+		// measure the local gradients for this set of stars
+		// the new elements are added to the incoming gradient structure 
+		grads = pmAstromMeasureGradients (grads, rawstars, refstars, match, region, 2, 2);
+	    }
+	}
+	psFree (region);
+    }
+    psFree (view);
+    return (grads);
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicGradients.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicGradients.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicGradients.c	(revision 22322)
@@ -0,0 +1,106 @@
+# include "psastroInternal.h"
+static int nPass = 0;
+
+bool psastroMosaicDistortionFromGradients (pmFPA *fpa, psMetadata *recipe) {
+
+    bool status;
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+    psArray *gradients = NULL;
+
+    // Measure the gradient as a function of position.  This must be performed between the
+    // corrected ref->TP and the observed raw->FP, for which the distortion is a perturbation.
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    int nXcell = psMetadataLookupS32 (&status, recipe, "PSASTRO.MOSAIC.GRADIENT.NX");
+    int nYcell = psMetadataLookupS32 (&status, recipe, "PSASTRO.MOSAIC.GRADIENT.NY");
+
+    // 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->toFPA) { continue; }
+	
+	psRegion *region = pmChipExtent (chip);
+
+	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
+	    // XXX there can only be one readout per chip, right?
+	    while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+		if (! readout->data_exists) { continue; }
+
+		// select the raw objects for this readout
+		psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+		if (rawstars == NULL) { continue; }
+
+		// select the raw objects for this readout
+		psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+		if (refstars == NULL) { continue; }
+
+		psArray *match = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
+		if (match == NULL) { continue; }
+
+		// measure the local gradients for this set of stars
+		// XXX 2,2 are the number of test boxes on the chip.  put this in the recipe
+		gradients = pmAstromMeasureGradients (gradients, rawstars, refstars, match, region, nXcell, nYcell);
+	    }
+	}
+	psFree (region);
+    }
+
+    // if desired, dump the gradients to a file
+    if (psTraceGetLevel("psastro.dump") > 0) { 
+	char name[80];
+	sprintf (name, "gradients.%d.dat", nPass); 
+	psastroDumpGradients (gradients, name); 
+	nPass ++;
+    }
+
+    // Fit the gradient field and convert to the distortion terms.
+
+    // allocate mosaic-level polynomial transformation and set masks needed by DVO
+    int order = psMetadataLookupF32 (&status, recipe, "PSASTRO.MOSAIC.ORDER");
+    if (!status) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to find single-chip fit order\n");
+	psFree (gradients);
+	psFree (view);
+        return false;
+    }
+    psFree (fpa->toTPA);
+    fpa->toTPA = psPlaneTransformAlloc (order, order);
+    for (int i = 0; i <= fpa->toTPA->x->nX; i++) {
+        for (int j = 0; j <= fpa->toTPA->x->nY; j++) {
+            if (i + j > order) {
+		fpa->toTPA->x->coeffMask[i][j] = PS_POLY_MASK_SET;
+		fpa->toTPA->y->coeffMask[i][j] = PS_POLY_MASK_SET;
+            }
+        }
+    }
+
+    // physical pixel scale in microns per pixel (FP is in physical units, chip is in pixels)
+    double pixelScale = psMetadataLookupF32 (&status, recipe, "PSASTRO.PIXEL.SCALE");
+    if (!status) {
+	psError(PS_ERR_IO, false, "Failed to lookup pixel scale"); 
+	psFree (gradients);
+	psFree (view);
+	return false; 
+    } 
+
+    // fit the measured gradients with the telescope distortion model (polynomial order based on toTPA)
+    if (!pmAstromFitDistortion (fpa, gradients, pixelScale)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to fit the distortion terms\n");
+	psFree (gradients);
+	psFree (view);
+        return false;
+    }
+	
+    psFree (gradients);
+    psFree (view);
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicHeaders.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicHeaders.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicHeaders.c	(revision 22322)
@@ -0,0 +1,59 @@
+# include "psastroInternal.h"
+
+bool psastroMosaicHeaders (pmConfig *config) {
+
+    bool status = false;
+    pmChip *chip = NULL;
+
+    // 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;
+    }
+
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    if (!input) {
+	psError(PSASTRO_ERR_CONFIG, true, "Can't find input data!\n");
+	return false;
+    }
+
+    char *mosastro = psMetadataLookupStr (NULL, config->arguments, "MOSASTRO");
+
+    double plateScale = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLATE.SCALE");
+    if (!status) plateScale = 1.0;
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+    pmFPA *fpa = input->fpa;
+
+    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; }
+
+        // read WCS data from the corresponding header
+        pmHDU *hdu = pmFPAviewThisHDU (view, fpa);
+
+        pmAstromWriteBilevelChip (chip->toFPA, hdu->header, plateScale);
+    }
+
+    psMetadata *mosaic = pmAstromWriteBilevelMosaic (fpa->toSky, fpa->toTPA, plateScale);
+
+    // XXX what is the EXTNAME??
+    psFits *fits = psFitsOpen (mosastro, "w");
+    psFitsWriteBlank(fits, mosaic, "");
+    psFitsClose (fits);
+
+    psFree (view);
+    return true;
+}
+
+/* coordinate frame hierachy
+   pixels (on a given readout)
+   cell
+   chip
+   FP (focal plane)
+   TP (tangent plane)
+   sky (ra, dec)
+*/
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicOneChip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicOneChip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicOneChip.c	(revision 22322)
@@ -0,0 +1,152 @@
+# include "psastroInternal.h"
+
+# define REQUIRED_RECIPE_VALUE(VALUE, NAME, TYPE, MESSAGE)\
+  VALUE = psMetadataLookup##TYPE (&status, recipe, NAME); \
+  if (!status) { \
+   psError(PSASTRO_ERR_CONFIG, false, MESSAGE); \
+   return false; } 
+
+bool psastroMosaicOneChip (pmChip *chip, pmReadout *readout, psMetadata *recipe, psMetadata *updates, int iteration) {
+
+    bool status;
+    char errorWord[64];
+    char orderWord[64];
+
+    PS_ASSERT_PTR_NON_NULL(chip,    false);
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(recipe,  false);
+    PS_ASSERT_PTR_NON_NULL(updates, false);
+
+    // select the raw objects for this readout
+    psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+    if (rawstars == NULL) return false;
+
+    psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+    if (refstars == NULL) return false;
+
+    psArray *match = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
+    if (match == NULL) return false;
+
+    // correct radius to FP units (physical pixel scale in microns per pixel)
+    REQUIRED_RECIPE_VALUE (double pixelScale, "PSASTRO.PIXEL.SCALE", F32, "Failed to lookup pixel scale"); 
+
+    // allowed limits for valid solutions
+    snprintf (errorWord, 64, "PSASTRO.MOSAIC.MAX.ERROR.N%d", iteration);
+    REQUIRED_RECIPE_VALUE (float maxError, errorWord, F32, "failed to find single-chip max allowed error\n");
+    REQUIRED_RECIPE_VALUE (int minNstar, "PSASTRO.MOSAIC.MIN.NSTAR", S32, "failed to find single-chip min allowed stars\n");
+
+    // set the order of the per-chip fit (higher order only if iteration > 0)
+    REQUIRED_RECIPE_VALUE (int defaultOrder, "PSASTRO.MOSAIC.CHIP.ORDER", S32, "failed to find mosaic chip-level fit default order\n");
+
+    snprintf (orderWord, 64, "PSASTRO.MOSAIC.CHIP.ORDER.N%d", iteration);
+    int order = psMetadataLookupS32 (&status, recipe, orderWord);
+    if (!status || (order == -1)) {
+	order = defaultOrder;
+    }
+
+    // modify the order to correspond to the actual number of matched stars:
+    if ((match->n < 15) && (order >= 3)) order = 2;
+    if ((match->n < 11) && (order >= 2)) order = 1;
+    if ((match->n <  8) && (order >= 1)) order = 0;
+    if ((match->n <  3) || (order < 0) || (order > 3)) {
+	psLogMsg ("psastro", 3, "insufficient stars (%ld) or invalid order (%d)", match->n, order); 
+	return false; 
+    } 
+
+    psLogMsg ("psastro", PS_LOG_DETAIL, "mosaic fit chip order %d", order);
+
+    // create output toFPA; set masks appropriate to the Elixir DVO astrometry format if we are
+    // fitting 0th order, use the current polynomial of whatever order, with higher order
+    // coefficients frozen to the current values
+    if (order == 0) {
+	// set FIT mask for all higher order terms of the existing solution
+	// any existing SET masks will be retained.
+	for (int i = 0; i <= chip->toFPA->x->nX; i++) {
+	    for (int j = 0; j <= chip->toFPA->x->nY; j++) {
+		if (i + j > 0) {
+		    chip->toFPA->x->coeffMask[i][j] |= PS_POLY_MASK_FIT;
+		    chip->toFPA->y->coeffMask[i][j] |= PS_POLY_MASK_FIT;
+		}
+	    }
+	}
+    } else {
+	psFree (chip->toFPA);
+	chip->toFPA = psPlaneTransformAlloc (order, order);
+	for (int i = 0; i <= chip->toFPA->x->nX; i++) {
+	    for (int j = 0; j <= chip->toFPA->x->nY; j++) {
+		if (i + j > order) {
+		    chip->toFPA->x->coeffMask[i][j] = PS_POLY_MASK_SET;
+		    chip->toFPA->y->coeffMask[i][j] = PS_POLY_MASK_SET;
+		}
+	    }
+	}
+    }
+
+    // XXX allow statitic to be set by the user
+    // only clip if we are fitting the chip parameters.
+    psStats *fitStats = NULL;
+//    if (order == 0) {
+//	fitStats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+//	fitStats->clipSigma = psMetadataLookupF32 (&status, recipe, "PSASTRO.MOSAIC.CHIP.NSIGMA");
+//	fitStats->clipIter = psMetadataLookupS32 (&status, recipe, "PSASTRO.MOSAIC.CHIP.NITER");
+//    } else {
+	fitStats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+	fitStats->clipSigma = psMetadataLookupF32 (&status, recipe, "PSASTRO.MOSAIC.CHIP.NSIGMA");
+	fitStats->clipIter = psMetadataLookupS32 (&status, recipe, "PSASTRO.MOSAIC.CHIP.NITER");
+//    }
+
+    // need to pass in an update header, sent in from above
+    pmAstromFitResults *results = pmAstromMatchFit (chip->toFPA, rawstars, refstars, match, fitStats);
+    if (!results) {
+	psError(PSASTRO_ERR_DATA, false, "failed to perform the matched fit\n");
+	return false;
+    }
+
+    // toSky converts from FPA & TPA units (microns) to sky units (radians)
+    pmFPA *fpa = chip->parent;
+    float plateScale = 0.5*(fpa->toSky->Xs + fpa->toSky->Ys)*3600.0*PM_DEG_RAD;
+
+    // pixError is the average 1D scatter in pixels ('results' are in FPA units = microns)
+    float pixError = 0.5*(results->xStats->clippedStdev + results->yStats->clippedStdev) / pixelScale;
+
+    // astError is the average 1D scatter in arcsec ('results' are in FPA units = microns)
+    float astError = 0.5*(results->xStats->clippedStdev + results->yStats->clippedStdev) * plateScale;
+    int astNstar = results->yStats->clippedNvalues;
+
+    // if we clip away too many stars, the order may be invalid
+    if (order == 3) { minNstar = PS_MAX (15, minNstar); }
+    if (order == 2) { minNstar = PS_MAX (11, minNstar); }
+    if (order == 1) { minNstar = PS_MAX ( 8, minNstar); }
+
+    bool validSolution = true;
+
+    // XXX should these result in errors or be handled another way?
+    psLogMsg ("psastro", PS_LOG_INFO, "astrometry solution: error: %f arcsec, Nstars: %d", astError, astNstar);
+    if ((maxError > 0) && (astError > maxError)) {
+	psLogMsg("psastro", PS_LOG_INFO, "residual error is too large, failed to find a solution: %f > %f", astError, maxError);
+	validSolution = false;
+    }
+    if (astNstar < minNstar) {
+	psLogMsg("psastro", PS_LOG_INFO, "solution uses too few stars: %d < %d", astNstar, minNstar);
+	validSolution = false;
+    }
+
+    // DVO expects NASTRO = 0 if we fail to find a solution
+    psMetadataAddF32 (updates, PS_LIST_TAIL, "PERROR",   PS_META_REPLACE, "astrometry error (pixels)", pixError);
+    psMetadataAddF32 (updates, PS_LIST_TAIL, "CERROR",   PS_META_REPLACE, "astrometry error (arcsec)", astError);
+    if (validSolution) {
+	psMetadataAddF32 (updates, PS_LIST_TAIL, "CPRECISE", PS_META_REPLACE, "astrometry precision (arcsec)", astError/sqrt(astNstar));
+	psMetadataAddS32 (updates, PS_LIST_TAIL, "NASTRO",   PS_META_REPLACE, "number of astrometry stars", astNstar);
+    } else {
+	psMetadataAddF32 (updates, PS_LIST_TAIL, "CPRECISE", PS_META_REPLACE, "astrometry precision (arcsec)", 0.0);
+	psMetadataAddS32 (updates, PS_LIST_TAIL, "NASTRO",   PS_META_REPLACE, "number of astrometry stars", 0);
+    }
+    psMetadataAddF32 (updates, PS_LIST_TAIL, "EQUINOX",  PS_META_REPLACE, "", 2000.0); // XXX this is bogus: should be defined based on equinox of refstars
+
+    // determine fromFPA transformation and apply new transformation to raw & ref stars
+    psastroUpdateChipToFPA (fpa, chip, rawstars, refstars);
+
+    psFree (fitStats);
+    psFree (results);
+    return validSolution;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicSetAstrom.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicSetAstrom.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicSetAstrom.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "psastroInternal.h"
+
+bool psastroMosaicSetAstrom (pmFPA *fpa) {
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+    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->toFPA) { 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
+	    // XXX there can only be one readout per chip, right?
+	    while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+		if (! readout->data_exists) { continue; }
+
+		// select the raw objects for this readout
+		psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+		if (rawstars == NULL) { continue; }
+
+		for (int i = 0; i < rawstars->n; i++) {
+		    pmAstromObj *raw = rawstars->data[i];
+	
+		    psPlaneTransformApply (raw->FP, chip->toFPA, raw->chip);
+		    psPlaneTransformApply (raw->TP, fpa->toTPA, raw->FP);
+		    psDeproject (raw->sky, raw->TP, fpa->toSky);
+		}
+
+		psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+		if (refstars == NULL) { continue; }
+
+		for (int i = 0; i < refstars->n; i++) {
+		    pmAstromObj *ref = refstars->data[i];
+	
+		    psProject (ref->TP, ref->sky, fpa->toSky);
+		    psPlaneTransformApply (ref->FP, fpa->fromTPA, ref->TP);
+		    psPlaneTransformApply (ref->chip, chip->fromFPA, ref->FP);
+		}
+	    }
+	}
+    }
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicSetMatch.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicSetMatch.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroMosaicSetMatch.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include "psastroInternal.h"
+
+bool psastroMosaicSetMatch (pmFPA *fpa, psMetadata *recipe, int iteration) {
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+    pmFPAview *view = pmFPAviewAlloc (0);
+    char radiusWord[64];
+
+    // use small radius to match stars (assume starting astrometry is good)
+    bool status = false; 
+    sprintf (radiusWord, "PSASTRO.MOSAIC.RADIUS.N%d", iteration);
+    double RADIUS = psMetadataLookupF32 (&status, recipe, radiusWord); 
+    if (!status) { 
+	psError(PS_ERR_IO, false, "Failed to lookup matching radius: %s", radiusWord); 
+	psFree (view);
+	return false; 
+    } 
+
+    if (RADIUS <= 0.0) {
+	if (iteration == 0) {
+	    psError(PS_ERR_IO, false, "Invalid match radius for first iteration: %s", radiusWord); 
+	    psFree (view);
+	    return false; 
+	} 
+	psWarning ("skipping match for iteration %d\n", iteration);
+	psFree (view);
+	return true;
+    }
+
+    // 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
+	    // XXX there can only be one readout per chip, right?
+	    while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+		if (! readout->data_exists) { continue; }
+
+		// select the raw objects for this readout
+		psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+		if (rawstars == NULL) { continue; }
+
+		// select the raw objects for this readout
+		psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+		if (refstars == NULL) { continue; }
+		psTrace ("psastro", 4, "Trying %ld refstars\n", refstars->n);
+
+		psArray *matches = pmAstromRadiusMatchChip (rawstars, refstars, RADIUS);
+		psTrace ("psastro", 4, "Matched %ld refstars\n", matches->n);
+
+		// XXX drop the old one
+		psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSASTRO.MATCH", PS_DATA_ARRAY | PS_META_REPLACE, "astrometry matches", matches);
+		psFree (matches);
+	    }
+	}
+    }
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroOneChipFit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroOneChipFit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroOneChipFit.c	(revision 22322)
@@ -0,0 +1,166 @@
+# include "psastroInternal.h"
+
+# define REQUIRED_RECIPE_VALUE(VALUE, NAME, TYPE)\
+  VALUE = psMetadataLookup##TYPE (&status, recipe, NAME); \
+  if (!status) { \
+   psAbort ("Failed to find %s in recipe", NAME); }
+
+bool psastroOneChipFit (pmFPA *fpa, pmChip *chip, psArray *refstars, psArray *rawstars, psMetadata *recipe, psMetadata *updates) {
+
+    bool status;
+
+    // default value for match/fit : radius is in pixels
+    REQUIRED_RECIPE_VALUE (double RADIUS, "PSASTRO.MATCH.RADIUS", F32); 
+
+    // run the match/fit sequence NITER times
+    REQUIRED_RECIPE_VALUE (int nIter, "PSASTRO.MATCH.FIT.NITER", S32); 
+
+    // correct radius to FP units (physical pixel scale in microns per pixel)
+    REQUIRED_RECIPE_VALUE (double pixelScale, "PSASTRO.PIXEL.SCALE", F32); 
+    RADIUS *= pixelScale;
+
+    // select the desired chip order
+    REQUIRED_RECIPE_VALUE (int order, "PSASTRO.CHIP.ORDER", S32);
+
+    // allowed limits for valid solutions
+    REQUIRED_RECIPE_VALUE (float maxError, "PSASTRO.MAX.ERROR", F32);
+    REQUIRED_RECIPE_VALUE (int minNstar, "PSASTRO.MIN.NSTAR", S32);
+
+    psArray *match = NULL;
+    psStats *fitStats = NULL;
+    pmAstromFitResults *results = NULL;
+
+    for (int iter = 0; iter < nIter; iter++) {
+	
+	char name[128];
+
+	sprintf (name, "PSASTRO.MATCH.RADIUS.N%d", iter);
+	float radius = psMetadataLookupF32 (&status, recipe, name);
+	radius *= pixelScale;
+	if (!status || (radius == 0.0)) {
+	    radius = RADIUS;
+	}
+
+
+	// use small radius to match stars
+	match = pmAstromRadiusMatchFP (rawstars, refstars, radius);
+	if (match == NULL) {
+	    psLogMsg ("psastro", 3, "failed to find radius-matched sources\n");
+	    return false;
+	}
+
+	// modify the order to correspond to the actual number of matched stars:
+	if ((match->n < 11) && (order >= 3)) order = 2;
+	if ((match->n <  7) && (order >= 2)) order = 1;
+	if ((match->n <  4) && (order >= 1)) order = 0;
+	if (order < 1) {
+	    psLogMsg ("psastro", 3, "insufficient stars or invalid order: %ld stars", match->n); 
+	    psFree (match);
+	    return false; 
+	} 
+
+	// create output toFPA; set masks appropriate to the Elixir DVO astrometry format
+	psFree (chip->toFPA);
+	chip->toFPA = psPlaneTransformAlloc (order, order);
+	for (int i = 0; i <= chip->toFPA->x->nX; i++) {
+	    for (int j = 0; j <= chip->toFPA->x->nY; j++) {
+		if (i + j > order) {
+		    chip->toFPA->x->coeffMask[i][j] = PS_POLY_MASK_SET;
+		    chip->toFPA->y->coeffMask[i][j] = PS_POLY_MASK_SET;
+		}
+	    }
+	}
+
+	// XXX allow statitic to be set by the user
+	// fitStats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+	fitStats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
+	fitStats->clipSigma = psMetadataLookupF32 (&status, recipe, "PSASTRO.CHIP.NSIGMA");
+	fitStats->clipIter = psMetadataLookupS32 (&status, recipe, "PSASTRO.CHIP.NITER");
+
+	// improved fit for astrometric terms
+	results = pmAstromMatchFit (chip->toFPA, rawstars, refstars, match, fitStats);
+	if (!results) {
+	    psLogMsg ("psastro", 3, "failed to perform the matched fit\n");
+	    psFree (match);
+	    psFree (fitStats);
+	    return false;
+	}
+    
+	// determine fromFPA transformation and apply new transformation to raw & ref stars
+	psastroUpdateChipToFPA (fpa, chip, rawstars, refstars);
+    
+	// toSky converts from FPA & TPA units (microns) to sky units (radians)
+	float plateScale = 0.5*(fpa->toSky->Xs + fpa->toSky->Ys)*3600.0*PM_DEG_RAD;
+	// float astError = 0.5*(results->xStats->clippedStdev + results->yStats->clippedStdev) * plateScale;
+	float astError = 0.5*(results->xStats->robustStdev + results->yStats->robustStdev) * plateScale;
+	int astNstar = results->yStats->clippedNvalues;
+	psLogMsg ("psastro", PS_LOG_INFO, "pass %d, error: %f arcsec, Nstars: %d", iter, astError, astNstar);
+
+	if (iter < nIter - 1) {
+	    psFree (fitStats);
+	    psFree (results);
+	    psFree (match);
+	}
+    }
+
+    // toSky converts from FPA & TPA units (microns) to sky units (radians)
+    float plateScale = 0.5*(fpa->toSky->Xs + fpa->toSky->Ys)*3600.0*PM_DEG_RAD;
+
+    // pixError is the average 1D scatter in pixels ('results' are in FPA units = microns)
+    // float pixError = 0.5*(results->xStats->clippedStdev + results->yStats->clippedStdev) / pixelScale;
+    float pixError = 0.5*(results->xStats->robustStdev + results->yStats->robustStdev) / pixelScale;
+
+    // astError is the average 1D scatter in arcsec ('results' are in FPA units = microns)
+    // float astError = 0.5*(results->xStats->clippedStdev + results->yStats->clippedStdev) * plateScale;
+    float astError = 0.5*(results->xStats->robustStdev + results->yStats->robustStdev) * plateScale;
+    int astNstar = results->yStats->clippedNvalues;
+
+    bool validSolution = true;
+
+    // XXX should these result in errors or be handled another way?
+    psLogMsg ("psastro", PS_LOG_INFO, "astrometry solution: error: %f arcsec, Nstars: %d", astError, astNstar);
+    if (astError > maxError) {
+        psLogMsg("psastro", PS_LOG_INFO, "residual error is too large, failed to find a solution: %f > %f", astError, maxError);
+	validSolution = false;
+    }
+    if (astNstar < minNstar) {
+        psLogMsg("psastro", PS_LOG_INFO, "solution uses too few stars: %d < %d", astNstar, minNstar);
+	validSolution = false;
+    }
+
+    // DVO expects NASTRO = 0 if we fail to find a solution
+    psMetadataAddF32 (updates, PS_LIST_TAIL, "PERROR",   PS_META_REPLACE, "astrometry error (pixels)", pixError);
+    psMetadataAddF32 (updates, PS_LIST_TAIL, "CERROR",   PS_META_REPLACE, "astrometry error (arcsec)", astError);
+    if (validSolution) {
+	psMetadataAddF32 (updates, PS_LIST_TAIL, "CPRECISE", PS_META_REPLACE, "astrometry precision (arcsec)", astError/sqrt(astNstar));
+	psMetadataAddS32 (updates, PS_LIST_TAIL, "NASTRO",   PS_META_REPLACE, "number of astrometry stars", astNstar);
+    } else {
+	psMetadataAddF32 (updates, PS_LIST_TAIL, "CPRECISE", PS_META_REPLACE, "astrometry precision (arcsec)", 0.0);
+	psMetadataAddS32 (updates, PS_LIST_TAIL, "NASTRO",   PS_META_REPLACE, "number of astrometry stars", 0);
+    }
+    psMetadataAddF32 (updates, PS_LIST_TAIL, "EQUINOX",  PS_META_REPLACE, "equinox of ref catalog", 2000.0); // XXX this is bogus: should be defined based on equinox of refstars
+
+    // XXX drop from here : determine fromFPA transformation and apply new transformation to raw & ref stars
+    // psastroUpdateChipToFPA (fpa, chip, rawstars, refstars);
+    
+    // XXX check if we correctly applied the new transformation:
+    if (psTraceGetLevel("psastro.dump") > 0) {
+	psastroDumpRawstars (rawstars, fpa, chip);
+	psastroDumpMatchedStars ("match.dat", rawstars, refstars, match);
+	psastroDumpStars (refstars, "refstars.cal.dat");
+    }
+
+    if (psTraceGetLevel("psastro.plot") > 0) {
+	psastroPlotOneChipFit (rawstars, refstars, match, recipe);
+    }
+
+    psFree (match);
+    psFree (results);
+    psFree (fitStats);
+    return validSolution;
+}
+
+// psastroWriteStars ("raw.1.dat", rawstars);
+// psastroWriteStars ("ref.1.dat", refstars);
+// psastroWriteTransform (chip->toFPA);
+
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroOneChipGrid.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroOneChipGrid.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroOneChipGrid.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "psastroInternal.h"
+
+# define REQUIRED_RECIPE_VALUE(VALUE, NAME, TYPE)\
+  VALUE = psMetadataLookup##TYPE (&status, recipe, NAME); \
+  if (!status) { \
+   psAbort ("Failed to find %s in recipe", NAME); }
+
+bool psastroOneChipGrid (pmFPA *fpa, pmChip *chip, psArray *refstars, psArray *rawstars, psMetadata *recipe, psMetadata *updates) {
+
+    bool status;
+    pmAstromStats *stats = NULL;
+
+    // do we need to get a rough initial match?
+    REQUIRED_RECIPE_VALUE (bool gridSearch, "PSASTRO.GRID.SEARCH", Bool);
+    if (!gridSearch) return true;
+
+    // do we need to get a rough initial match?
+    REQUIRED_RECIPE_VALUE (int maxNstar, "PSASTRO.GRID.NSTAR.MAX", S32);
+
+    // generate the bright subset of maxNstar entries (note: rawstars is sorted by S/N)
+    psArray *subset = psArrayAlloc (PS_MIN (maxNstar, rawstars->n));
+    for (int i = 0; (i < maxNstar) && (i < rawstars->n); i++) {
+	subset->data[i] = psMemIncrRefCounter (rawstars->data[i]);
+    }
+
+    // XXX set clump scale from recipe
+    psArray *gridStars = psastroRemoveClumps (subset, 150);
+    psFree (subset);
+
+    psArray *refSubset = psastroRemoveClumps (refstars, 150);
+
+    psLogMsg ("psastro", 3, "grid search using %ld raw vs %ld ref stars\n", gridStars->n, refSubset->n);
+
+    // find initial offset / rotation / scale
+    pmAstromStats *gridStats = pmAstromGridMatch (gridStars, refSubset, recipe);
+    if (gridStats == NULL) {
+	psLogMsg ("psastro", 3, "failed to find a grid match solution\n");
+	psFree (gridStars);
+	psFree (refSubset);
+	return false;
+    }
+    psLogMsg ("psastro", 3, "basic grid search result - offset: %f,%f pixels, rotation: %f deg\n", gridStats->offset.x, gridStats->offset.y, DEG_RAD*gridStats->angle);
+
+    // tweak the position by finding peak of matches stars
+    stats = pmAstromGridTweak (gridStars, refSubset, recipe, gridStats);
+    if (stats == NULL) {
+	psLogMsg ("psastro", 3, "failed to measure tweaked grid solution\n");
+	psFree (gridStats);
+	psFree (gridStars);
+	psFree (refSubset);
+	return false;
+    }
+    psLogMsg ("psastro", 3, "tweak grid search result - offset: %f,%f pixels, rotation: %f deg\n", stats->offset.x, stats->offset.y, DEG_RAD*stats->angle);
+
+    // adjust the chip.toFPA terms only
+    pmAstromGridApply (chip->toFPA, stats);
+    psastroUpdateChipToFPA (fpa, chip, rawstars, refstars);
+    psFree (gridStats);
+    psFree (gridStars);
+    psFree (refSubset);
+    psFree (stats);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroParseCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroParseCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroParseCamera.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "psastroInternal.h"
+
+bool psastroParseCamera (pmConfig *config) {
+
+    bool status = false;
+
+    // the input image(s) are required arguments; they define the camera
+    pmFPAfile *input = pmFPAfileDefineFromArgs (&status, config, "PSASTRO.INPUT", "INPUT");
+    if (!status) {
+	psError(PSASTRO_ERR_CONFIG, false, "Failed to build FPA from PSASTRO.INPUT");
+	return false;
+    }
+
+    // define the additional input/output files associated with psphot 
+    if (!psastroDefineFiles (config, input)) {
+	psError(PSASTRO_ERR_CONFIG, false, "Trouble defining the additional input/output files");
+	return false;
+    }
+
+    // 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
+	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;
+	    }
+        }
+    }
+    psFree (chips);
+
+    psString dump_file = psMetadataLookupStr(&status, config->arguments, "DUMP_CONFIG");
+    if (dump_file) {
+        pmConfigCamerasCull(config, NULL);
+        pmConfigRecipesCull(config, "PPIMAGE,PPSTATS,PSPHOT,MASKS,PSASTRO");
+
+        pmConfigDump(config, input->fpa, dump_file);
+    }
+
+
+    psTrace("psastro", 1, "Done with psastroParseCamera...\n");
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroRefstarSubset.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroRefstarSubset.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroRefstarSubset.c	(revision 22322)
@@ -0,0 +1,90 @@
+# include "psastroInternal.h"
+
+bool psastroRefstarSubset (pmReadout *readout) {
+
+  // select the raw objects for this readout
+  psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+  if (rawstars == NULL)  {
+    psError(PSASTRO_ERR_DATA, false, "missing rawstars in psastroRefstarSubset\n");
+    return false;
+  }
+
+  // select the raw objects for this readout
+  psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+  if (refstars == NULL)  {
+    psError(PSASTRO_ERR_DATA, false, "missing refstars in psastroRefstarSubset\n");
+    return false;
+  }
+
+  // calculate luminosity functions for rawstars and refstars
+  // the samples cover the same area (the chip), so no area correction
+  // is needed...
+  psLogMsg ("psastro", 4, "measuring luminosity function for rawstars\n");
+  pmLumFunc *rawfunc = psastroLuminosityFunction (rawstars);
+  if (rawfunc == NULL) {
+    psLogMsg ("psastro", 4, "giving up on rawstars for this readout\n");
+    return true;
+  }
+  psLogMsg ("psastro", 4, "measuring luminosity function for refstars\n");
+  pmLumFunc *reffunc = psastroLuminosityFunction (refstars);
+  if (reffunc == NULL) {
+    psLogMsg ("psastro", 4, "giving up on refstars for this readout\n");
+    return true;
+  }
+
+// XXX code to better deal with mismatches in the fitted lum function
+# if (0)
+  // if the fitted slopes differ by too much, give up and just try to match the peak bin
+  fSlope = (reffunc->slope / rawfunc->slope);
+  if ((fSlope > 1.3) || (fSlope < 0.77)) {
+      // XXX do something here (choose the peak of the smaller set, generate a histogram for
+      // the other set, then choose the bin from the larger set which has nBin = nPeak
+  }
+# endif
+
+  // what is the offset between the two lines at the average magnitude?
+  double mRef = 0.5*(reffunc->mMin + reffunc->mMax);
+  double logRho = mRef * reffunc->slope + reffunc->offset;
+  double mRaw = (logRho - rawfunc->offset) / rawfunc->slope;
+
+  psLogMsg ("psastro", 4, "mRef: %f, logRho: %f, mRaw: %f\n", mRef, logRho, mRaw);
+
+  double mRefMax = rawfunc->mMax - mRaw + mRef;
+  psLogMsg ("psastro", 4, "clipping stars fainter than %f\n", mRefMax);
+
+  psArray *subset = psArrayAllocEmpty (100);
+  for (int i = 0; i < refstars->n; i++) {
+    pmAstromObj *ref = refstars->data[i];
+    if (ref->Mag > mRefMax) continue;
+    psArrayAdd (subset, 100, ref);
+  }
+
+  psLogMsg ("psastro", 4, "keeping %ld of %ld reference stars\n", subset->n, refstars->n);
+
+  psMetadataRemoveKey (readout->analysis, "PSASTRO.REFSTARS");
+  psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSASTRO.REFSTARS", PS_DATA_ARRAY, "astrometry matches", subset);
+
+  if (psTraceGetLevel("psastro.dump") > 0) {
+      pmChip *chip = readout->parent->parent;
+
+      char *filename = NULL;
+      char *chipname = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
+      psStringAppend (&filename, "refstars.%s.dat", chipname);
+      psastroDumpRefstars (subset, filename);
+      psFree (filename);
+  }
+
+  psFree (rawfunc);
+  psFree (reffunc);
+  psFree (subset);
+
+  return true;
+}
+
+/* this test is a bit sensitive to the total number of refstars or rawstars available
+   watch out if:
+   - the fitted slopes are extremely different 
+   - the average number of stars per bin is ~1
+   
+   skip the cut in these cases?
+*/
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroRemoveClumps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroRemoveClumps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroRemoveClumps.c	(revision 22322)
@@ -0,0 +1,97 @@
+# include "psastroInternal.h"
+
+// look for and exclude objects in clumps in the input list
+psArray *psastroRemoveClumps (psArray *input, int scale) {
+
+    // determine data range
+    pmAstromObj *obj = input->data[0];
+    float Xmin = obj->FP->x;
+    float Xmax = obj->FP->x;
+    float Ymin = obj->FP->y;
+    float Ymax = obj->FP->y;
+    for (int i = 0; i < input->n; i++) {
+	obj = (pmAstromObj *)input->data[i];
+	if (!isfinite(obj->FP->x)) continue;
+	if (!isfinite(obj->FP->y)) continue;
+	Xmin = PS_MIN (Xmin, obj->FP->x);
+	Xmax = PS_MAX (Xmax, obj->FP->x);
+	Ymin = PS_MIN (Ymin, obj->FP->y);
+	Ymax = PS_MAX (Ymax, obj->FP->y);
+    }
+
+    int nX = (Xmax - Xmin) / scale + 10;
+    int nY = (Ymax - Ymin) / scale + 10;
+    psImage *count = psImageAlloc (nX, nY, PS_TYPE_U32);
+    psImageInit (count, 0);
+
+    // accumulate 2D histogram in image
+    for (int i = 0; i < input->n; i++) {
+	obj = (pmAstromObj *)input->data[i];
+	if (!isfinite(obj->FP->x)) continue;
+	if (!isfinite(obj->FP->y)) continue;
+	int Xi = PS_MIN (PS_MAX((obj->FP->x - Xmin) / scale + 5, 0), count->numCols);
+	int Yi = PS_MIN (PS_MAX((obj->FP->y - Ymin) / scale + 5, 0), count->numRows);
+	count->data.U32[Yi][Xi] ++;
+    }
+
+    // determine image statistics
+    psStats *stats = psStatsAlloc (PS_STAT_MAX | PS_STAT_MAX | PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+    if (!psImageStats(stats, count, NULL, 0)) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to get image statistics.\n");
+	psFree(stats);
+	psFree(count);
+	return NULL;
+    }
+
+    if (stats->max < 1) {
+	psError(PS_ERR_UNKNOWN, false, "no valid sources in image\n");
+	psFree(stats);
+	psFree(count);
+	return NULL;
+    }
+
+    // XXX make this a user option
+    float limit = PS_MAX (5.0*stats->sampleStdev, 5.0);
+    psTrace ("psastro", 4, "skipping stars in cells with more than %f stars\n", limit);
+
+    // find and exclude objects in bad pixels
+    psArray *output = psArrayAllocEmpty (input->n);
+    for (int i = 0; i < input->n; i++) {
+	obj = (pmAstromObj *)input->data[i];
+	if (!isfinite(obj->FP->x)) continue;
+	if (!isfinite(obj->FP->y)) continue;
+	int Xi = PS_MIN (PS_MAX((obj->FP->x - Xmin) / scale + 5, 0), count->numCols);
+	int Yi = PS_MIN (PS_MAX((obj->FP->y - Ymin) / scale + 5, 0), count->numRows);
+	if (count->data.U32[Yi][Xi] > limit) continue;
+	psArrayAdd (output, 16, obj);
+    }
+
+    psFree(stats);
+    psFree(count);
+    return output;
+}
+
+# if (0)
+// make a list of the outlier pixels
+psArray *badpix = psArrayAllocEmpty (16);
+for (int iy = 0; iy < count->numRows; iy++) {
+    for (int ix = 0; ix < count->numCols; ix++) {
+	if (count->data.U32[iy][ix] > limit) {
+	    psPlane *pixel = psPlaneAlloc();
+	    pixel->x = ix;
+	    pixel->y = iy;
+	    psArrayAdd (badpix, 16, pixel);
+	    psFree (pixel);
+	}
+    }
+}
+
+if (badpix->n == 0) {
+    psArray *output = psMemIncrRefCounter (input);
+    psFree (stats);
+    psFree (count);
+    psFree (badpix);
+    return output;
+}
+# endif
+
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroStandAlone.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroStandAlone.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroStandAlone.h	(revision 22322)
@@ -0,0 +1,38 @@
+# ifdef HAVE_CONFIG_H
+# include <config.h>
+# endif
+
+#ifndef PSASTRO_STAND_ALONE_H
+#define PSASTRO_STAND_ALONE_H
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "psastro.h"
+
+// Top level functions
+pmConfig         *psastroArguments (int argc, char **argv);
+void              psastroCleanup (pmConfig *config);
+bool              psastroParseCamera (pmConfig *config);
+bool              psastroDataLoad (pmConfig *config);
+
+pmConfig         *psastroModelArguments (int argc, char **argv);
+bool 		  psastroModelParseCamera (pmConfig *config);
+bool 		  psastroModelDataLoad (pmConfig *config);
+bool 		  psastroModelAnalysis (pmConfig *config);
+bool 		  psastroModelAdjust (pmConfig *config);
+bool 		  psastroModelDataSave (pmConfig *config);
+
+psVector         *psastroModelFitBoresite (psVector *Xo, psVector *Yo, psVector *Po, char *outroot);
+psF32 		  psastroModelBoresite (psVector *deriv, const psVector *params, const psVector *coord);
+
+// these are used to define the boresite model parameters
+# define PAR_X0  0  // Xo = params[0] 
+# define PAR_Y0  1  // Yo = params[1] 
+# define PAR_RX  2  // RX = params[2] 
+# define PAR_RY  3  // RY = params[3] 
+# define PAR_P0  4  // P0 = params[4]
+# define PAR_T0  5  // phi = params[4]
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroTestFuncs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroTestFuncs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroTestFuncs.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "psastroInternal.h"
+
+// write out objects
+bool psastroWriteStars (char *filename, psArray *sources) {
+
+    // re-open, add data to end of file
+    FILE *f = fopen (filename, "w");
+    if (f == NULL) {
+	psLogMsg ("psastroWriteStars", 3, "can't open output file for output %s\n", filename);
+	return false;
+    }
+
+    for (int i = 0; i < sources->n; i++) {
+	
+	pmAstromObj *star = sources->data[i];
+
+	fprintf (f, "%8.2f %8.2f   %8.2f %8.2f   %8.2f %8.2f   %10.6f %10.6f   %8.2f %8.2f\n", 
+		 star->chip->x, star->chip->y, 
+		 star->FP->x, star->FP->y, 
+		 star->TP->x, star->TP->y, 
+		 star->sky->r*DEG_RAD, star->sky->d*DEG_RAD, 
+		 star->Mag, star->dMag);
+    }
+    fclose (f);
+    return true;
+}
+
+bool psastroWriteTransform (psPlaneTransform *map) {
+
+    // dump initial values:
+    for (int i = 0; i < map->x->nX + 1; i++) {
+	for (int j = 0; j < map->x->nY + 1; j++) {
+	    if (map->x->coeffMask[i][j] & PS_POLY_MASK_SET) continue;
+	    psLogMsg ("psastro", 4, "x term %d,%d: %f +/- %f\n", i, j, map->x->coeff[i][j], map->x->coeffErr[i][j]);
+	}
+    }
+
+    for (int i = 0; i < map->y->nX + 1; i++) {
+	for (int j = 0; j < map->y->nY + 1; j++) {
+	    if (map->y->coeffMask[i][j] & PS_POLY_MASK_SET) continue;
+	    psLogMsg ("psastro", 4, "y term %d,%d: %f +/- %f\n", i, j, map->y->coeff[i][j], map->y->coeffErr[i][j]);
+	}
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroUseModel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroUseModel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroUseModel.c	(revision 22322)
@@ -0,0 +1,138 @@
+# include "psastroInternal.h"
+# define NONLIN_TOL 0.001 /* tolerance in pixels */
+# define DEBUG 0
+
+// apply the generic astrometry model to this image.  this assumes the WCS terms either do not
+// exist or are invalid 
+bool psastroUseModel (pmConfig *config, psMetadata *recipe) {
+
+  bool status;
+
+  bool useModel = psMetadataLookupBool (&status, config->arguments, "PSASTRO.USE.MODEL");
+  if (!status) {
+      useModel = psMetadataLookupBool (&status, recipe, "PSASTRO.USE.MODEL");
+  }
+  if (!useModel) return true;
+
+  // identify reference astrometry table.  
+  // if not defined, correction was not requested; skip step
+  pmFPAfile *astrom = psMetadataLookupPtr (NULL, config->files, "PSASTRO.MODEL");
+  if (!astrom) psAbort ("programming error: model was not supplied, though requested");
+
+  // select the input data sources
+  pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+  if (!input) psAbort ("programming error: no input data");
+
+  // make sure the astrometry model is loaded.  de-activate all files except PSASTRO.MODEL.
+  pmFPAfileActivate (config->files, false, NULL);
+  pmFPAfileActivate (config->files, true, "PSASTRO.MODEL");
+
+  pmFPAview *view = pmFPAviewAlloc (0);
+
+  // files associated with the science image
+  if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) {
+      psError (PS_ERR_IO, false, "Can't load the astrometry model file");
+      return false;
+  }
+
+  // XXX TEST: apply the input image RA & DEC to the astrometry model, save corners
+  if (0) {
+      // these externally supplied values are used to set the final transformation terms
+      double RA  = psMetadataLookupF64 (&status, input->fpa->concepts, "FPA.RA");
+      double DEC = psMetadataLookupF64 (&status, input->fpa->concepts, "FPA.DEC");
+      // double POS = PM_RAD_DEG * psMetadataLookupF64 (&status, input->fpa->concepts, "FPA.POSANGLE");
+
+      // get projection scale; center is supplied
+      // XXX should this be astrom or input??
+      float Xs = psMetadataLookupF32(&status, astrom->fpa->concepts, "XSCALE") * PM_RAD_DEG;
+      float Ys = psMetadataLookupF32(&status, astrom->fpa->concepts, "YSCALE") * PM_RAD_DEG;
+
+      // allocate a new toSky projection using the reported position
+      psFree (astrom->fpa->toSky);
+      astrom->fpa->toSky = psProjectionAlloc (RA, DEC, Xs, Ys, PS_PROJ_DIS);
+
+      // local view
+      pmFPAview *myView = pmFPAviewAlloc (0);
+
+      // loop over all chips, replace input astrometry elements with those from astrom 
+      pmChip *obsChip = NULL;
+      while ((obsChip = pmFPAviewNextChip (myView, input->fpa, 1)) != NULL) {
+	  psTrace ("psastro", 4, "Chip %d: %x %x\n", myView->chip, obsChip->file_exists, obsChip->process);
+	  if (!obsChip->process || !obsChip->file_exists || !obsChip->data_exists) { continue; }
+
+	  // set the chip astrometry using the astrom file
+	  pmChip *refChip = pmFPAviewThisChip (myView, astrom->fpa);
+
+	  psFree (obsChip->toFPA);
+	  psFree (obsChip->fromFPA);
+
+	  // supply astrometry from model 
+	  obsChip->toFPA   = psMemIncrRefCounter (refChip->toFPA);
+	  obsChip->fromFPA = psMemIncrRefCounter (refChip->fromFPA);
+
+	  // XXX if we want to write out the result, update the header here.  this needs to be
+	  // updated with the correct HDU selection.  obsChip->hdu may not exist.
+	  // pmAstromWriteBilevelChip (obsChip->hdu->header, obsChip, NONLIN_TOL);
+      }
+
+      psFree (input->fpa->toSky);
+      psFree (input->fpa->toTPA);
+      psFree (input->fpa->fromTPA);
+      input->fpa->toSky   = psMemIncrRefCounter (astrom->fpa->toSky);
+      input->fpa->toTPA   = psMemIncrRefCounter (astrom->fpa->toTPA);
+      input->fpa->fromTPA = psMemIncrRefCounter (astrom->fpa->fromTPA);
+
+      // XXX this is temporarily hardwired because of model error
+      input->fpa->toSky->type = PS_PROJ_TAN;
+
+      if (DEBUG) psastroDumpCorners ("corners.up.ast1.dat", "corners.dn.ast1.dat", input->fpa);
+  }
+
+  // set the model using the RA, DEC, POSANGLE of the input image.
+  pmAstromModelSetTP (astrom, input->fpa->concepts);
+
+  if (DEBUG) psastroDumpCorners ("corners.up.ast2.dat", "corners.dn.ast2.dat", astrom->fpa);
+
+  // loop over all chips, replace input astrometry elements with those from astrom 
+  pmChip *obsChip = NULL;
+  while ((obsChip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+    psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, obsChip->file_exists, obsChip->process);
+    if (!obsChip->process || !obsChip->file_exists || !obsChip->data_exists) { continue; }
+
+    // set the chip astrometry using the astrom file
+    pmChip *refChip = pmFPAviewThisChip (view, astrom->fpa);
+
+    psFree (obsChip->toFPA);
+    psFree (obsChip->fromFPA);
+
+    // supply astrometry from model 
+    obsChip->toFPA   = psMemIncrRefCounter (refChip->toFPA);
+    obsChip->fromFPA = psMemIncrRefCounter (refChip->fromFPA);
+
+    // XXX if we want to write out the result, update the header here.  this needs to be
+    // updated with the correct HDU selection.  obsChip->hdu may not exist.
+    // pmAstromWriteBilevelChip (obsChip->hdu->header, obsChip, NONLIN_TOL);
+  }
+
+  psFree (input->fpa->toSky);
+  psFree (input->fpa->toTPA);
+  psFree (input->fpa->fromTPA);
+  input->fpa->toSky   = psMemIncrRefCounter (astrom->fpa->toSky);
+  input->fpa->toTPA   = psMemIncrRefCounter (astrom->fpa->toTPA);
+  input->fpa->fromTPA = psMemIncrRefCounter (astrom->fpa->fromTPA);
+
+  // XXX this is temporarily hardwired because of model error
+  input->fpa->toSky->type = PS_PROJ_TAN;
+
+  if (DEBUG) psastroDumpCorners ("corners.up.inp.dat", "corners.dn.inp.dat", input->fpa);
+
+  // updated with the correct HDU selection.  obsChip->hdu may not exist.
+  // psMetadata *updates = psMetadataAlloc();
+  // pmAstromWriteBilevelMosaic (updates, input->fpa, NONLIN_TOL);
+  // psMetadataAddMetadata (input->fpa->analysis, PS_LIST_TAIL, "PSASTRO.HEADER",  PS_META_REPLACE, "psastro header stats", updates);
+  // psFree (updates);
+
+  psFree (view);
+  return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroUtils.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroUtils.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroUtils.c	(revision 22322)
@@ -0,0 +1,217 @@
+# include "psastroInternal.h"
+# define RENORM 0
+
+// fix this to look up the value in the chip concepts
+static double getChipPixelScale (pmChip *chip) {
+    return 10.0;
+}
+
+// I have an FPA structure with multiple chips.  we have loaded or measured astrometry for each
+// chip with independent pixel/degree scaling values.  These will naturally compensate locally
+// somewhat for the telescope distortion.  to measure the telescope distortion, we need to
+// force the chips to have the same pixel scale and measure the difference from that solution.
+// Convert an FPA with disparate pixel scales to a common pixel scale (perhaps depending on the
+// chip -- eg, TC3)
+
+bool psastroMosaicCommonScale (pmFPA *fpa, psMetadata *recipe) {
+
+    // options : use the MIN or MAX chip as global reference or supplied pixel scales
+    // (microns/pixel), which may depend on the chip
+
+    float pixelScaleUse = 1.0, pixelScale1 = 1.0,  pixelScale2 = 1.0,  pixelScale = 1.0;
+
+    char *option = psMetadataLookupStr (NULL, recipe, "PSASTRO.COMMON.SCALE.OPTION");
+    if (option == NULL) {
+	psError(PSASTRO_ERR_DATA, false, "no choice set for common scale option\n");
+	return false;
+    }
+
+    bool useExternal = true;
+
+    // find the min or max scale chip
+    if (!strcasecmp (option, "MIN") || !strcasecmp (option, "MAX")) {
+
+	bool useMax = !strcasecmp (option, "MAX");
+	pixelScaleUse = (useMax) ? FLT_MIN : FLT_MAX;
+
+	for (int i = 0; i < fpa->chips->n; i++) {
+	    pmChip *chip = fpa->chips->data[i];
+	    if (!chip->process || !chip->file_exists) { continue; }
+	    if (!chip->toFPA) { continue; }
+
+	    pixelScale1 = hypot (chip->toFPA->x->coeff[1][0], chip->toFPA->x->coeff[0][1]);
+	    pixelScale2 = hypot (chip->toFPA->y->coeff[1][0], chip->toFPA->y->coeff[0][1]);
+	    pixelScale = 0.5*(pixelScale1 + pixelScale2);
+	    
+	    pixelScaleUse = (useMax) ? PS_MAX (pixelScale, pixelScaleUse) : PS_MIN (pixelScale, pixelScaleUse);
+	}
+	useExternal = false;
+    }
+
+    // rescale each chip by the reference scale
+    for (int i = 0; i < fpa->chips->n; i++) {
+	pmChip *chip = fpa->chips->data[i];
+	if (!chip->process || !chip->file_exists) { continue; }
+	if (!chip->toFPA) { continue; }
+
+	psPlaneTransform *toFPA = chip->toFPA;
+	psPlaneTransform *fromFPA = chip->fromFPA;
+	    
+	pixelScale1 = hypot (toFPA->x->coeff[1][0], toFPA->x->coeff[0][1]);
+	pixelScale2 = hypot (toFPA->y->coeff[1][0], toFPA->y->coeff[0][1]);
+
+	if (useExternal) {
+	    pixelScaleUse = getChipPixelScale (chip);
+	}
+
+	for (int i = 0; i <= toFPA->x->nX; i++) {
+	    for (int j = 0; j <= toFPA->x->nX; j++) {
+		toFPA->x->coeff[i][j] *= pixelScaleUse/pixelScale1;
+		toFPA->y->coeff[i][j] *= pixelScaleUse/pixelScale2;
+		fromFPA->x->coeff[i][j] *= pixelScale1/pixelScaleUse;
+		fromFPA->y->coeff[i][j] *= pixelScale2/pixelScaleUse;
+	    }
+	}
+
+    }
+    psastroMosaicSetAstrom (fpa);
+    return true;
+}
+
+bool psastroUpdateChipToFPA (pmFPA *fpa, pmChip *chip, psArray *rawstars, psArray *refstars) {
+
+    psRegion *region = pmChipPixels (chip);
+
+    psFree (chip->fromFPA);
+    chip->fromFPA = psPlaneTransformInvert (NULL, chip->toFPA, *region, 50);
+    psFree (region);
+
+    for (int i = 0; i < rawstars->n; i++) {
+        pmAstromObj *raw = rawstars->data[i];
+
+        psPlaneTransformApply (raw->FP, chip->toFPA, raw->chip);
+        psPlaneTransformApply (raw->TP, fpa->toTPA, raw->FP);
+        psDeproject (raw->sky, raw->TP, fpa->toSky);
+    }
+
+    for (int i = 0; i < refstars->n; i++) {
+        pmAstromObj *ref = refstars->data[i];
+        psPlaneTransformApply (ref->chip, chip->fromFPA, ref->FP);
+    }
+
+    return true;
+}
+
+# if 0
+
+bool psastroSelectBrightStars (pmFPA *fpa, psMetadata *config) {
+
+    bool status;
+
+    // add exclusions for objects on some basis?
+    int MAX_NSTARS = psMetadataLookupS32 (&status, config, "PSASTRO.STARS.MAX");
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        for (int j = 0; j < chip->cells->n; j++) {
+            pmCell *cell = chip->cells->data[j];
+            for (int k = 0; k < cell->readouts->n; k++) {
+                pmReadout *readout = cell->readouts->data[k];
+
+                psArray *stars = psMetadataLookupPtr (&status, readout->analysis, "STARS.FULLSET");
+                stars = psArraySort (stars, pmAstromObjSortByMag);
+
+                int nSubset = PS_MIN (MAX_NSTARS, stars->n);
+                psArray *subset = psArrayAlloc (nSubset);
+
+                for (int i = 0; i < nSubset; i++) {
+                    subset->data[i] = stars->data[i];
+                }
+                psMetadataAdd (readout->analysis, PS_LIST_TAIL, "STARS.SUBSET", PS_DATA_ARRAY, "stars from analysis", subset);
+            }
+        }
+    }
+    return true;
+}
+
+psPlaneDistort *psPlaneDistortCopy (psPlaneDistort *input) {
+
+    psPlaneDistort *output = psPlaneDistortAlloc (input->x->nX, input->x->nY, input->x->nZ, input->x->nT);
+
+    for (int i = 0; i < input->x->nX; i++) {
+        for (int j = 0; j < input->x->nY; j++) {
+            for (int k = 0; k < input->x->nZ; k++) {
+                for (int m = 0; m < input->x->nT; m++) {
+                    // x-terms
+                    output->x->mask[i][j][k][m]     = input->x->mask[i][j][k][m];
+                    output->x->coeff[i][j][k][m]    = input->x->coeff[i][j][k][m];
+                    output->x->coeffErr[i][j][k][m] = input->x->coeffErr[i][j][k][m];
+                    // y-terms
+                    output->y->mask[i][j][k][m]     = input->y->mask[i][j][k][m];
+                    output->y->coeff[i][j][k][m]    = input->y->coeff[i][j][k][m];
+                    output->y->coeffErr[i][j][k][m] = input->y->coeffErr[i][j][k][m];
+                }
+            }
+        }
+    }
+    return (output);
+}
+
+psPlaneTransform *psPlaneTransformCopy (psPlaneTransform *input) {
+
+    psPlaneTransform *output = psPlaneTransformAlloc (input->x->nX, input->x->nY);
+
+    for (int i = 0; i < input->x->nX; i++) {
+        for (int j = 0; j < input->x->nY; j++) {
+            // x-terms
+            output->x->mask[i][j]     = input->x->mask[i][j];
+            output->x->coeff[i][j]    = input->x->coeff[i][j];
+            output->x->coeffErr[i][j] = input->x->coeffErr[i][j];
+            // y-terms
+            output->y->mask[i][j]     = input->y->mask[i][j];
+            output->y->coeff[i][j]    = input->y->coeff[i][j];
+            output->y->coeffErr[i][j] = input->y->coeffErr[i][j];
+        }
+    }
+    return (output);
+}
+
+psProjection *psProjectionCopy (psProjection *input) {
+
+    psProjection *output = psProjectionAlloc (input->R, input->D, input->Xs, input->Ys, input->type);
+    return (output);
+}
+
+// returns the rotation term, forcing positive parity
+double psPlaneTransformGetRotation (psPlaneTransform *map) {
+
+    if (map->x->nX < 1) return 0;
+    if (map->x->nY < 1) return 0;
+
+    if (map->y->nX < 1) return 0;
+    if (map->y->nY < 1) return 0;
+
+    double pc1_1 = map->x->coeff[1][0];
+    double pc1_2 = map->x->coeff[0][1];
+    double pc2_1 = map->y->coeff[1][0];
+    double pc2_2 = map->y->coeff[0][1];
+
+    double px = SIGN (pc1_1);
+    double py = SIGN (pc2_2);
+
+    // both x and y terms imply an angle. take the average
+    double t1 = -atan2 (px*pc1_2, px*pc1_1);
+    double t2 = +atan2 (py*pc2_1, py*pc2_2);
+
+    // careful near -pi,+pi boundary...
+    if (t1 - t2 > M_PI/2) t2 += 2*M_PI;
+    if (t2 - t1 > M_PI/2) t1 += 2*M_PI;
+
+    double theta = 0.5*(t1 + t2);
+    while (theta < M_PI) theta += 2*M_PI;
+    while (theta > M_PI) theta -= 2*M_PI;
+
+    return (theta);
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroVersion.c	(revision 22322)
@@ -0,0 +1,20 @@
+#include "psastroInternal.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString psastroVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString psastroVersionLong(void)
+{
+    psString version = psastroVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psastro/src/psastroZeroPoint.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psastro/src/psastroZeroPoint.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psastro/src/psastroZeroPoint.c	(revision 22322)
@@ -0,0 +1,108 @@
+# include "psastroInternal.h"
+
+bool psastroZeroPointReadout(psArray *rawstars, psArray *refstars, psArray *matches);
+
+bool psastroZeroPoint (pmConfig *config) {
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+	psError(PSASTRO_ERR_CONFIG, false, "Can't find PSASTRO recipe!\n");
+	return false;
+    }
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    if (!input) {
+	psError(PSASTRO_ERR_CONFIG, false, "Can't find input data!\n");
+	return false;
+    }
+    pmFPA *fpa = input->fpa;
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // given the existing per-chip astrometry, determine matches between raw and ref stars
+    // is this needed? yes, if we didn't do SingleChip astrometry first
+    if (!psastroMosaicSetMatch (fpa, recipe, 0)) {
+	psError(PSASTRO_ERR_UNKNOWN, false, "failed to match raw and ref stars for mosaic");
+	return false;
+    }
+
+    // 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->toFPA) { 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
+	    // XXX there can only be one readout per chip, right?
+	    while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+		if (! readout->data_exists) { continue; }
+
+		// select the raw objects for this readout
+		psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+		if (rawstars == NULL) { continue; }
+
+		// select the raw objects for this readout
+		psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+		if (refstars == NULL) { continue; }
+
+		psArray *matches = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
+		if (matches == NULL) { continue; }
+
+		// calculate dMag for the matched stars
+		psastroZeroPointReadout (rawstars, refstars, matches);
+	    }
+	}
+    }
+
+    psFree (view);
+    return true;
+}
+
+// XXX for now, let's just measure <dMag> and sigma_dMag
+// XXX a test comment
+bool psastroZeroPointReadout(psArray *rawstars, psArray *refstars, psArray *matches) {
+
+    psVector *dMag  = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+    int Npts = 0;
+    for (int i = 0; i < matches->n; i++) {
+
+      pmAstromMatch *match = matches->data[i];
+
+      pmAstromObj *raw = rawstars->data[match->raw];
+
+      pmAstromObj *ref = refstars->data[match->ref];
+
+      dMag->data.F32[Npts] = ref->Mag - raw->Mag;
+      psVectorExtend (dMag, 100, 1);
+      Npts++;
+    }
+
+    psTrace ("psModules.astrom", 4, "Npts: %d\n", Npts);
+
+    if (Npts < 3) {
+      fprintf (stderr, "zero point NaN +/- NaN\n");
+      psFree (dMag);
+      return false;
+    }
+
+    // stats structure and mask for use in measuring the clipping statistic
+    // this analysis has too few data points to use the robust median method
+    psStats *stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+    psVectorStats (stats, dMag, NULL, NULL, 0);
+    fprintf (stderr, "zero point %f +/- %f\n", stats->clippedMean, stats->clippedStdev);
+
+    psFree (dMag);
+    psFree (stats);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psconfig/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/.cvsignore	(revision 22322)
@@ -0,0 +1,3 @@
+tarballs
+psconfig.csh
+psconfig.bash
Index: /tags/sj_tags/sj_root_20080929/psconfig/INSTALL
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/INSTALL	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/INSTALL	(revision 22322)
@@ -0,0 +1,183 @@
+
+The psconfig system allows the user to build and install the IPP
+software suite into a location which is flexibly defined by the user.
+The tools here also set up the user's environment variables (PATH,
+PERL5LIB, LIBRARY_PATH, etc) to make use of the installed software.
+With the psconfig tools, it is easy to switch between different
+installed versions or to recompile subsets of the IPP tree.
+
+0. Choose a target installation directory.
+
+Choose a location to store the installed software and configuration
+files.  The psconfig system places the installed binary files by
+default in directories below ~/psconfig.  There will be one directory
+for each version of the installation for a given hardware
+architecture.  To use a different location, place the following line
+in ~/.psconfigrc (otherwise, this is not needed):
+
+set PSCONFDIR = INSTALL_PATH
+
+where INSTALL_PATH is the top-level directory for all binary
+installations. 
+
+1. Set up the psconfig system.
+
+If you have not previously installed the IPP suite, and do not the
+psconfig scripts installed, you need to generate the scripts for your
+install directory.  Run the following command in this directory
+(ipp/psconfig):
+
+psbuild -bootstrap INSTALL_PATH
+
+where INSTALL_PATH is the top-level directory for all binary
+installations.  Then, follow the instructions supplied by that
+command:
+
+** if you use csh, tcsh or equivalent as your shell, add the following to your .cshrc
+    if (-e /home/kiawe/eugene/psconfig/psconfig.csh) then
+      alias  psconfig        "source /home/kiawe/eugene/psconfig/psconfig.csh"
+    else
+      alias  psconfig        "echo psconfig not available"
+    endif
+    psconfig default
+
+** if you use sh, bash or equivalent as your shell, add the following to your .bashrc
+    if [ -f /home/kiawe/eugene/psconfig/psconfig.csh ]; then
+      alias psconfig='source /home/kiawe/eugene/psconfig/psconfig.bash'
+      # In bash, can't use an alias in the same file that defines it, so need to
+      # expand it here explicitly to make 'controller host add' work in pantasks
+      source /home/kiawe/eugene/psconfig/psconfig.bash default
+    else
+      alias psconfig='echo psconfig not available'
+    fi
+    
+
+Though, in your case, "/home/kiawe/eugene/psconfig/" will be replaced
+by the value of INSTALL_PATH.  After you have set up this alias, you
+will need to source the .cshrc / .bashrc, or open a new shell, to have
+these aliases available.
+
+2. Using psconfig to set / examine your install system:
+
+Before running or compiling the IPP, it is necessary to use psconfig
+to set the installation version:
+
+psconfig (version)
+
+This command sets aliases and environment variables for the current
+shell to point at the IPP installation labeled with the given version
+name and hardware architecture.  For example:
+
+psconfig default
+
+will set the PATH to include ~/psconfig/default.linux/bin on a 32-bit
+linux system, and the other paths to point at the corresponding
+installation directories.
+
+3. Dependencies
+
+NOTE: It is possible to use the tools discussed below to manually check on the
+external dependencies.  However, the psbuild system now allows you to
+build the full suite including dependencies in a single pass.  To use
+this method, skip to section 5.
+
+3.1. External C libraries
+
+The program 'pschecklibs' in this directory will check for required
+system libraries and headers.  It examines the system libraries,
+libraries defined by LD_LIBRARY_PATH, and the installation library
+defined by psconfig.  Any missing dependencies will be listed.
+Tarballs for these libraries may be found on the Pan-STARRS web site
+at:
+
+http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extlibs-2.6.1.tgz
+http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extperl-2.6.1.tgz
+
+These should be installed so they will be available in the user's
+path.  The psconfig commands can be used to install these libraries in
+the psconfig location:
+
+'psautogen' replaces autogen.sh
+'psconfigure' replaces configure
+
+3.2 External Perl Modules
+
+The program 'pscheckperl' in this directory will check for required
+Perl modules, and can be used to install them in the appropriate user
+location in the psconfig system.  The command defaults to the latest
+perl installation table (eg, tagsets/ipp-2.6.1.perl).
+
+pscheckperl
+
+will test for the perl modules specified for the latest ipp release.
+if any modules are missing, they can be download from the Pan-STARRS
+web site:
+
+http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extperl
+
+The tarballs should be placed in a directory extperl parallel to the
+ipp directory (two levels up from this directory).  If the tarballs
+are in the correct location, they can be built by supplying the -build
+flag to pscheckperl:
+
+pscheckperl -build
+
+4. Building
+
+To build the full IPP tree using the psconfig system, run 'psbuild' in
+this directory:
+
+psbuild
+
+For additional information on using psbuild, see the listing of
+options below (or type psbuild -help)
+
+5. Single-Pass Build with External Dependencies
+
+If you have not already done so, download the tarball with the
+external libraries and perl modules from:
+
+http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extlibs-2.6.1.tgz
+http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extperl-2.6.1.tgz
+
+It should be possible to build the full IPP installation by issuing
+the single command in this directory (ipp/psconfig):
+
+psbuild -extbuild
+
+------
+
+More options for psbuild:
+
+USAGE: psbuild [options] (distribution)
+     : -version (version) : specify alternate psconfig installation version
+     : -verbose           : give additional information
+     : -extlibs (tarball) : specify the location of the extlibs tarball
+     : -extperl (tarball) : specify the location of the extlibs tarball
+     : -extcheck          : check (but do not build) the external dependencies
+     : -extbuild          : check and build (if needed) the external dependencies
+     : -clean             : clean the source directories before building
+     : -rebuild           : run 'configure (and autogen for developer)' (C code)
+     : -optimize          : set flags for optimized code
+     : -only (module)     : only build the specified module
+     : -start (module)    : begin build at specified module
+     : -stop (module)     : stop build after specified module
+
+     : -dev               : build modules not distributed in tarball
+
+     : psbuild -bootstrap : generate the psconfig scripts
+     : psbuild -list      : list the available distributions
+     : psbuild -h         : this help listing
+     : psbuild -help      : this help listing
+     : psbuild --help     : this help listing
+
+Summary of psconfig operations:
+
+psdist -tag        : tag CVS tree
+psdist -dist       : build tarball from tagged tree
+psdist -dist -head : build tarball from head
+psbuild            : build and install software in tree
+pschecklibs        : check for needed external software
+pscheckperl        : check for needed perl modules
+pscheckperl -build : build and install external modules
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/README	(revision 22322)
@@ -0,0 +1,21 @@
+
+Welcome to the IPP Software Tree.  To build the full IPP suite, you
+must be in the directory ipp/psconfig.  Follow the instructions in
+'ipp/psconfig/INSTALL'.
+
+The directory 'ipp/psconfig' contains tools for building the IPP, for
+testing the build environment, for building external C libraries and
+Perl modules, for setting up the user's environment, and for building
+a tarball to be distributed to the end users.
+
+Developer Build vs User Build
+
+Within the IPP code base, there are a few modules which are not
+distributed as part of the tarball.  These include 'glueforge', which
+is used to generate code for interacting with the database;
+'dbconfig', which defines a database layout; and 'ippdb.src', which is
+the raw auto-generated code.  The tarball is instead packaged with a
+version of ippdb for which autogen has been run, and for which there
+is a valid 'configure' script.  As a developer, it is necessary to
+build these packages.  There is a '-dev' option for psbuild which
+builds these packages.
Index: /tags/sj_tags/sj_root_20080929/psconfig/TODO
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/TODO	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/TODO	(revision 22322)
@@ -0,0 +1,40 @@
+
+2007.02.02 : IPP release 1.0 : Known Outstanding Issues
+
+This release of the IPP software is missing a number of major features
+defined in the IPP System Concept Definition.  There are also a number
+of missing minor features, bells and whistles which have been
+identified.
+
+Major Missing Features:
+
+* Phase 4 Tools.  This release does not include the Phase 4 programs
+pswarp, ppStac, and poisub, and the related infrastructure support in
+ippTools and ippScripts.
+
+* Static Sky tools.  This release does not include a number of elements
+related to the static sky definition: the definition of the static sky
+cells and tools to manipulate them; the static sky surface brightness
+modelling tool; and photometry analysis operations for large, extended
+objects.
+
+* relastro.  The global astrometry analysis software is unimplemented.
+
+* flat-field corrections.  The DVO flat-field correction analysis is
+not integrated with the rest of the IPP detrend creation system.
+
+Minor Missing Features:
+
+* full set of columns in output object tables from Phase 2.
+* ippMonitor user interface missing a variety of options
+* output plots from psphot and pspastro are not exported to ippMonitor
+* this list is incomplete.
+* ETC...
+
+In addition, there are 
+
+* incomplete documentation
+
+* incomplete testing on:
+  - Nebulous integration
+  - DVO integration
Index: /tags/sj_tags/sj_root_20080929/psconfig/psbuild
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/psbuild	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/psbuild	(revision 22322)
@@ -0,0 +1,438 @@
+#!/usr/bin/env perl
+
+$tagsets = "tagsets";
+if (!-d $tagsets || !-r $tagsets || !-x $tagsets) { die "missing the directory of distribution tables: $tagsets\n"; }
+
+$version = "";
+$clean = 0;
+$rebuild = 0;
+$optimize = 0;
+$profile = 0;
+$developer = 0;
+$start = "";
+$stop = "";
+$verbose = 0;
+
+$extlibs = "none";
+$extperl = "none";
+
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-version") {
+        $version = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-verbose") {
+        $verbose = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-extlibs") {
+        $extlibs = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-extperl") {
+        $extperl = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-clean") {
+        $clean = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-extcheck") {
+        $extlibs = "check";
+        $extperl = "check";
+        shift; next;
+    }
+    if ($ARGV[0] eq "-extbuild") {
+        $extlibs = "build";
+        $extperl = "build";
+        shift; next;
+    }
+    if ($ARGV[0] eq "-rebuild") {
+        $rebuild = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-optimize") {
+        $optimize = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-profile") {
+        $profile = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-dev") {
+        $developer = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-only") {
+        $start = $ARGV[1];
+        $stop = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-start") {
+        $start = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-stop") {
+        $stop = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-list") {
+        &list_distributions ();
+    }
+    if ($ARGV[0] eq "-bootstrap") {
+        &bootstrap ();
+    }
+    if ($ARGV[0] eq "-env") {
+        &show_environment ();
+    }
+    if ($ARGV[0] eq "-h")     { &usage (); }
+    if ($ARGV[0] eq "-help")  { &usage (); }
+    if ($ARGV[0] eq "--help") { &usage (); }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if ( @ARGV == 0) {
+    @list = <$tagsets/*.dist>;
+    $distribution = $list[-1];
+} else {
+    $distribution = "$tagsets/$ARGV[0].dist";
+}
+
+# generate new psconfig.csh if needed
+if (! -e "psconfig.csh" || ! -e "psconfig.bash") {
+    $psconfdir = $ENV{'PSCONFDIR'};
+    if ($psconfdir eq "") { die "PSCONFDIR not found, run psbuild -bootstrap and follow instructions\n"; }
+    vsystem ("cat psconfig.csh.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.csh");
+    vsystem ("cat psconfig.bash.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.bash");
+}
+
+&load_distfile ();
+
+&build_distribution ();
+exit 0;
+
+sub show_environment {
+
+    # use psconfig.csh to set needed build aliases
+
+    # set the psconfig version:
+    if ("$version" eq "") {
+        $version = $ENV{'PSVERSION'};
+    }
+    if ("$version" eq "") {
+        $version = "default";
+    }
+
+    if (! -e psconfig.csh) {
+        $psconfdir = $ENV{'PSCONFDIR'};
+        if ($psconfdir eq "") { die "PSCONFDIR not found, run psbuild -bootstrap and follow instructions\n"; }
+        vsystem ("cat psconfig.csh.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.csh");
+        vsystem ("cat psconfig.bash.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.bash");
+    }
+
+    $psconfigure = `csh psconfig.csh --psconfigure $version`; chomp $psconfigure;
+    $psautogen   = `csh psconfig.csh --psautogen $version`;   chomp $psautogen;
+    $psperlbuild = `csh psconfig.csh --psperlbuild $version`; chomp $psperlbuild;
+
+    # print "psconfigure: $psconfigure\n";
+    # print "psautogen:   $psautogen\n";
+    # print "psperlbuild: $psperlbuild\n";
+
+    # set build environment variables
+    ps_setenv (1, "PATH",                 "--path");
+    ps_setenv (1, "CPATH",                "--cpath");
+    ps_setenv (1, "ARCH",                 "--arch");
+    ps_setenv (1, "LIBRARY_PATH",    "--library_path");
+    ps_setenv (1, "LD_LIBRARY_PATH", "--ld_library_path");
+    ps_setenv (1, "PKG_CONFIG_PATH", "--pkg_config_path");
+    ps_setenv (1, "ACLOCAL_FLAGS",   "--aclocal_flags");
+    ps_setenv (1, "PERL5LIB",        "--perl5lib");
+
+    exit 0;
+}
+
+sub build_distribution {
+
+    # use psconfig.csh to set needed build aliases
+
+    if ($extlibs eq "check") {
+        $status = vsystem ("pschecklibs");
+        if ($status) { die "failed to find external libraries\n"; }
+    }
+    if ($extlibs eq "build") {
+        $status = vsystem ("pschecklibs -build");
+        if ($status) { die "failed to build external libraries\n"; }
+    }
+
+    if ($extperl eq "check") {
+        $status = vsystem ("pschecklperl");
+        if ($status) { die "failed to find external perl modules\n"; }
+    }
+    if ($extperl eq "build") {
+        $status = vsystem ("pscheckperl -build");
+        if ($status) { die "failed to build external perl modules\n"; }
+    }
+
+    # set the psconfig version:
+    if ("$version" eq "") {
+        $version = $ENV{'PSVERSION'};
+    }
+    if ("$version" eq "") {
+        $version = "default";
+    }
+
+    $psconfigure = `csh psconfig.csh --psconfigure $version`; chomp $psconfigure;
+    $psautogen   = `csh psconfig.csh --psautogen $version`;   chomp $psautogen;
+    $psperlbuild = `csh psconfig.csh --psperlbuild $version`; chomp $psperlbuild;
+
+    # print "psconfigure: $psconfigure\n";
+    # print "psautogen:   $psautogen\n";
+    # print "psperlbuild: $psperlbuild\n";
+
+    # set build environment variables
+    ps_setenv (0, "PATH",                 "--path");
+    ps_setenv (0, "CPATH",                "--cpath");
+    ps_setenv (0, "ARCH",                 "--arch");
+    ps_setenv (0, "LIBRARY_PATH",    "--library_path");
+    ps_setenv (0, "LD_LIBRARY_PATH", "--ld_library_path");
+    ps_setenv (0, "PKG_CONFIG_PATH", "--pkg_config_path");
+    ps_setenv (0, "ACLOCAL_FLAGS",   "--aclocal_flags");
+    ps_setenv (0, "PERL5LIB",        "--perl5lib");
+
+    # some versions of libtool use this value:
+    $ENV{'D'} = "";
+
+    # make sure the aclocal path exists
+    @word = split (" ", $ENV{'ACLOCAL_FLAGS'});
+    if (@word != 2) { &failure("setup", "ACLOCAL_FLAGS is not set"); }
+    if (! -e $word[1]) { vsystem ("mkdir -p $word[1]"); }
+
+    $psopts = "";
+    if ($optimize) { $psopts = "$psopts --enable-optimize"; }
+    if ($profile)  { $psopts = "$psopts --enable-profile --disable-shared --enable-static"; }
+
+    $homedir = `pwd`; chomp $homedir;
+
+    $stop_now = 0;
+    for ($i = 0; !$stop_now && ($i < @cvsname); $i++) {
+        if (($stop ne "") && ($stop eq $cvsname[$i])) { $stop_now = 1; }
+        if (($start ne "") && ($start ne $cvsname[$i])) { next; }
+        $start = "";
+
+        ($do_tag, $do_build, $do_package, $do_update, $dev_build) = $mode[$i] =~ m|(\S)(\S)(\S)(\S)(\S)|;
+        if ($developer) { $do_build = $dev_build; }
+        if ($do_build eq "N") { next; }
+
+        $workdir = "../$cvsname[$i]";
+
+        # XXX need to grab current value for cleanup
+        print "\n ** psbuild: $cvsname[$i] ** \n";
+        print "\033]0; ** psbuild: $cvsname[$i] ** \007";
+
+        if (!-d $workdir || !-r $workdir || !-x $workdir) {
+            print STDERR "WARNING: no directory for component $cvsname[$i], skipping\n";
+            next;
+        }
+
+        chdir $workdir;
+
+        # how do we build this component?
+        # - autogen.sh : configure : make : make install
+        # - configure : make : make install
+        # - make : make install
+        # - perl Build.PL : ./Build : ./Build install
+
+        if (-e "Build.PL") {
+            vsystem ("$psperlbuild");
+            if ($?) { &failure($cvsname[$i], "failure in perl Build.PL"); }
+
+            vsystem ("./Build");
+            if ($?) { &failure($cvsname[$i], "failure in Build"); }
+
+            vsystem ("./Build install");
+            if ($?) { &failure($cvsname[$i], "failure in Build install"); }
+
+            next;
+        }
+
+        if ($rebuild && $clean) {
+            if (-e "configure") { unlink "Makefile"; }
+            if ($developer && -e "configure.ac" && -e "autogen.sh") { unlink "configure"; }
+        }
+        $rebuild_this = $rebuild;
+
+        # set a local variable for this loop on rebuild;
+        if (! -e "Makefile") { $rebuild_this = 1; }
+
+        #  run autogen
+        $skip_configure = 0;
+        if ($developer && $rebuild_this && ! -e "configure" && -e "autogen.sh") {
+            $skip_configure = 1;
+            vsystem ("$psautogen $psopts");
+            if ($?) { &failure($cvsname[$i], "failure in psautogen"); }
+        }
+
+        if ($rebuild_this && -e "configure" && !$skip_configure) {
+            vsystem ("$psconfigure $psopts");
+            if ($?) { &failure($cvsname[$i], "failure in psconfigure"); }
+        }
+
+        if (! -e "Makefile") { &failure($cvsname[$i], "missing makefile: do you need to run the -dev developer build?"); }
+
+        if ($clean) {
+            vsystem ("make clean");
+            if ($?) { &failure($cvsname[$i], "failure in make clean"); }
+        }
+
+        vsystem ("make");
+        if ($?) { &failure($cvsname[$i], "failure in make"); }
+
+        vsystem ("make install");
+        if ($?) { &failure($cvsname[$i], "failure in make install"); }
+
+        print "*** done with $cvsname[$i] ***\n";
+
+      success:
+        chdir $homedir;
+    }
+    print "\033]0; ** psbuild: finished ** \007";
+    exit 0;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub list_distributions {
+    @list = <$tagsets/*.dist>;
+    foreach $line (@list) {
+        chomp $line;
+        ($dist) = $line =~ m|$tagsets/(\S*).dist|;
+        print STDERR "$dist\n";
+    }
+    exit 2;
+}
+
+sub bootstrap {
+
+    if (@ARGV != 2) { die "USAGE: psbuild -bootstrap (install_dir)\n"; }
+    $psconfdir = $ARGV[1];
+
+    # copy psconfig.csh and psconfig.bash to psconfdir
+    vsystem ("cat psconfig.csh.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.csh");
+    vsystem ("cat psconfig.bash.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.bash");
+
+    vsystem ("mkdir -p $psconfdir");
+
+    vsystem ("cp psconfig.csh $psconfdir/psconfig.csh");
+    vsystem ("cp psconfig.bash $psconfdir/psconfig.bash");
+    vsystem ("chmod u+x $psconfdir/psconfig.bash");
+
+    print STDOUT "\n";
+
+    print STDOUT "** if you use csh, tcsh or equivalent as your shell, add the following to your .cshrc\n";
+    print STDOUT "    if (-e $psconfdir/psconfig.csh) then\n";
+    print STDOUT "      alias  psconfig        \"source $psconfdir/psconfig.csh\"\n";
+    print STDOUT "    else\n";
+    print STDOUT "      alias  psconfig        \"echo psconfig not available\"\n";
+    print STDOUT "    endif\n";
+    print STDOUT "    psconfig default\n";
+    print STDOUT "\n";
+
+    print STDOUT "** if you use sh, bash or equivalent as your shell, add the following to your .bashrc\n";
+    print STDOUT "    if [ -f $psconfdir/psconfig.csh ]; then\n";
+    print STDOUT "      alias psconfig='source $psconfdir/psconfig.bash'\n";
+    print STDOUT "    else\n";
+    print STDOUT "      alias psconfig='echo psconfig not available'\n";
+    print STDOUT "    fi\n";
+    print STDOUT "    source $psconfdir/psconfig.bash default\n";
+    print STDOUT "\n";
+
+    exit 0;
+}
+
+sub load_distfile {
+    # open and read the distribution file
+    # results go into @cvsname, @branchtag, @branchver, @mode
+    open (FILE, $distribution) || die "ERROR: can't open distribution file $distribution\n";
+    @list = <FILE>;
+    close (FILE);
+
+    @mode = ();
+    @cvsname = ();
+    @branchtag = ();
+    @branchver = ();
+
+    foreach $line (@list) {
+        chop $line;
+        if ($line =~ m|^\s*$|) { next; }
+        if ($line =~ m|^\s*\#|) { next; }
+
+        ($mode, $cvsname, $branchtag, $branchver) = split (" ", $line);
+
+        if ($cvsname eq "") { die "missing module name\n"; }
+
+        ($do_tag, $do_build, $do_package, $do_update) = $mode =~ m|(\S)(\S)(\S)(\S)|;
+        if (($do_tag ne "Y") && ($do_tag ne "N")) { die "invalid tag option $do_tag\n"; }
+        if (($do_build ne "Y") && ($do_build ne "N")) { die "invalid tag option $do_build\n"; }
+        if (($do_package ne "Y") && ($do_package ne "N")) { die "invalid tag option $do_package\n"; }
+        if (($do_update ne "Y") && ($do_update ne "N")) { die "invalid tag option $do_update\n"; }
+
+        if ($verbose) { print "tag: $do_tag, build: $do_build, package: $do_package, update: $do_update "; }
+        if ($verbose) { print "module: $cvsname, branchtag: $branchtag, branchver: $branchver \n"; }
+
+        push @mode, $mode;
+        push @cvsname, $cvsname;
+        push @branchtag, $branchtag;
+        push @branchver, $branchver;
+    }
+}
+
+sub failure {
+    die "problem building $_[0] : $_[1]\n";
+    print "\033]0; ** psbuild: failure  ** \007";
+}
+
+sub usage {
+    print STDERR "USAGE: psbuild [options] (distribution)\n";
+    print STDERR "     : -version (version) : specify alternate psconfig installation version\n";
+    print STDERR "     : -verbose           : give additional information\n";
+    print STDERR "     : -extlibs (tarball) : specify the location of the extlibs tarball\n";
+    print STDERR "     : -extperl (tarball) : specify the location of the extlibs tarball\n";
+    print STDERR "     : -extcheck          : check (but do not build) the external dependencies\n";
+    print STDERR "     : -extbuild          : check and build (if needed) the external dependencies\n";
+    print STDERR "     : -clean             : clean the source directories before building\n";
+    print STDERR "     : -rebuild           : run 'configure (and autogen for developer)' (C code)\n";
+    print STDERR "     : -optimize          : set flags for optimized code\n";
+    print STDERR "     : -only (module)     : only build the specified module\n";
+    print STDERR "     : -start (module)    : begin build at specified module\n";
+    print STDERR "     : -stop (module)     : stop build after specified module\n\n";
+    print STDERR "     : -dev               : build modules not distributed in tarball\n\n";
+    print STDERR "     : psbuild -bootstrap : generate the psconfig scripts\n";
+    print STDERR "     : psbuild -list      : list the available distributions\n";
+    print STDERR "     : psbuild -h         : this help listing\n";
+    print STDERR "     : psbuild -help      : this help listing\n";
+    print STDERR "     : psbuild --help     : this help listing\n";
+    exit 2;
+}
+
+sub ps_setenv {
+
+    my $verbose = $_[0];
+    my $var = $_[1];
+    my $flag = $_[2];
+
+    my $answer = `csh psconfig.csh $flag $version`;
+    chomp $answer;
+
+    $ENV{$var} = $answer;
+    if ($verbose) {
+        print STDERR "$var = $answer\n";
+    }
+}
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/pschecklibs
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/pschecklibs	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/pschecklibs	(revision 22322)
@@ -0,0 +1,492 @@
+#!/usr/bin/env perl
+
+$tagsets = "tagsets";
+$needdev = 0;
+
+# default system library locations
+@binpath = ( );
+@libpath = ( "/usr/local/lib", "/usr/lib", "/usr/X11R6/lib", "/lib" );
+@incpath = ( "/usr/local/include", "/usr/include", "/usr/X11R6/include" );
+
+$version = "";
+$build = 0;
+%force;
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-version") {
+        $version = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-build") {
+        $build = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-force") {
+        if (@ARGV < 2) { die "-force must be coupled to a library name\n"; }
+        $force{lc($ARGV[1])} = 1;
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-h")     { &usage (); }
+    if ($ARGV[0] eq "help")   { &usage (); }
+    if ($ARGV[0] eq "-help")  { &usage (); }
+    if ($ARGV[0] eq "--help") { &usage (); }
+    if ($ARGV[0] eq "-list")  { &list_distributions(); }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+if ( @ARGV > 1) { &usage(); }
+
+if ( @ARGV == 0) {
+    @list = <$tagsets/*.libs>;
+    $file = $list[-1];
+} else {
+    $file = "$tagsets/$ARGV[0].libs";
+}
+print "examining C libraries based on $file\n\n";
+
+# load the C libraries list
+open (FILE, $file) || die "ERROR: can't open C libraries list: $file\n";
+@list = <FILE>;
+close (FILE);
+
+# set the psconfig version:
+if ("$version" eq "") {
+    $version = $ENV{'PSVERSION'};
+}
+if ("$version" eq "") {
+    $version = "default";
+}
+
+# set the lib and include paths set by psconfig
+$bindir      = `csh psconfig.csh --bin $version`;         chomp $bindir;
+$libdir      = `csh psconfig.csh --libs $version`;        chomp $libdir;
+$mandir      = `csh psconfig.csh --man $version`;         chomp $mandir;
+$incdir      = `csh psconfig.csh --include $version`;     chomp $incdir;
+$prefix      = `csh psconfig.csh --prefix $version`;      chomp $prefix;
+
+$homedir     = `pwd`; chomp $homedir;
+$psconfdir   = `csh psconfig.csh --psconfdir $version`;   chomp $psconfdir;
+$psconfigure = `csh psconfig.csh --psconfigure $version`; chomp $psconfigure;
+
+print "psconfig version: $version\n";
+print "bindir: $bindir\n";
+print "libdir: $libdir\n";
+print "incdir: $incdir\n";
+print "mandir: $mandir\n";
+print "\n";
+
+# create the directories above if not found:
+if (! -e $libdir) { vsystem ("mkdir -p $libdir"); }
+if (! -e $incdir) { vsystem ("mkdir -p $incdir"); }
+if (! -e $bindir) { vsystem ("mkdir -p $bindir"); }
+if (! -e $mandir) { vsystem ("mkdir -p $mandir"); }
+if (! -e "$mandir/man1") { vsystem ("mkdir -p $mandir/man1"); }
+if (! -e "$mandir/man3") { vsystem ("mkdir -p $mandir/man3"); }
+
+# add the path defined by LIBRARY_PATH
+@tmppath = split (":", $ENV{'LIBRARY_PATH'});
+if (@tmppath) {
+    unshift @libpath, @tmppath;
+}
+# search for, and drop, existing libdir entry in libpath?
+unshift @libpath, $libdir;
+
+# add the path defined by PATH
+@tmppath = split (":", $ENV{'PATH'});
+if (@tmppath) {
+    unshift @binpath, @tmppath;
+}
+# search for, and drop, existing libdir entry in libpath?
+unshift @binpath, $bindir;
+
+# add the path defined by PATH
+@tmppath = split (":", $ENV{'CPATH'});
+if (@tmppath) {
+    unshift @incpath, @tmppath;
+}
+# search for, and drop, existing libdir entry in libpath?
+unshift @incpath, $incdir;
+
+# add the system paths specified for each architecture
+&checkarch ();
+print "setting architecture to: $arch\n";
+print "searching for libraries in: @libpath\n";
+print "searching for programs in: @binpath\n";
+print "\n";
+
+if ($build && ! -d $psconfdir) {
+    mkdir $psconfdir || die "unable to create psconfig dir $psconfdir";
+}
+
+# read the lib distribution file, search / build each entry
+@faillibs = ();
+@failincs = ();
+foreach $line (@list) {
+    chop $line;
+    if ($line =~ m|^\s*$|) { next; }
+    if ($line =~ m|^\s*\#|) { next; }
+
+    ($type, $name, $altnames, $altpaths, $tarball, $tardir, $auto_force, $configure_opts, $make_opts, $install_opts) = split (" ", $line);
+    if (($auto_force ne "Y") && ($auto_force ne "N")) { die "invalid value for auto_force field\n"; }
+
+    if ((defined $force{lc($name)} or defined $force{'all'}) and lc($tarball) ne "none") {
+        &buildlib ($name, $tarball, $tardir, $configure_opts, $make_opts, $install_opts);
+        next;
+    }
+
+    if ($build and ($auto_force eq "Y")) {
+        &buildlib ($name, $tarball, $tardir, $configure_opts, $make_opts, $install_opts);
+        next;
+    }
+
+    ## check for the C library
+    if ($type eq "lib") {
+        $found = &checklib ($name, $altnames, $altpaths);
+    }
+    if ($type eq "bin") {
+        $found = &checkbin ($name, $altnames, $altpaths);
+    }
+    if ($type eq "inc") {
+        $found = &checkinc ($name, $altnames, $altpaths);
+    }
+
+    if ($auto_force eq "Y") {
+	print "$name will be built\n";
+    }
+
+    if ($found) {
+	if ($found eq "runtime-only") {
+	    print "runtime $name ($found)\n";
+	    push @faillibs, "$name";
+	} else {
+	    print "pass $name ($found)\n";
+	}
+        next;
+    } else {
+        print "fail $name\n";
+        push @faillibs, "$name";
+    }
+
+    if ($auto_force eq "Y") {
+	print "$name will be built\n";
+    }
+
+    if (! $build) { next; }
+    if ($type eq "inc") {
+        print "ERROR: missing header file from library which is supposedly installed\n";
+        # Will attempt to install library.
+    }
+    &buildlib ($name, $tarball, $tardir, $configure_opts, $make_opts, $install_opts);
+}
+if ($build) { exit 0; }
+print "\n";
+
+if (@faillibs > 0) {
+    print "The following C libraries are missing from your system\n";
+    foreach $name (@faillibs) {
+        print "  $name\n";
+    }
+    print "\n";
+    print "you may install them in your local path by re-running pschecklibs with -build\n";
+
+    print "*** WARNING *** Some libraries are installed in your system only for runtime use, not for linking.\n";
+    print " For many systems, it is possible to install the developer version of a library, and this may be safer\n";
+    print " If you choose to install our version of any of these libraries, please use -force (library) in your psbuild / pschecklibs options\n";
+    exit 1;
+}
+
+print "no C libraries are missing from your system\n";
+exit 0;
+
+sub buildlib {
+    my ($name, $tarball, $tardir, $configure_opts, $make_opts, $install_opts) = @_;
+
+    if ($tarball eq "NONE") {
+        print "No tarball available for $name.  You'll have to build it yourself.\n";
+        exit 1;
+    }
+
+    if ($name eq "libz") {
+        # zlib doesn't like the full list of arguments to configure
+        $psconfigure = "./configure --prefix=$prefix";
+    } else {
+        $psconfigure = `csh psconfig.csh --psconfigure $version`; chomp $psconfigure;
+    }
+
+    print "psconfigure: $psconfigure";
+
+    ## try to build the module from ../../extlibs/$tarball
+
+    # go to extlibs and unpack the tarball
+    chdir "../../extlibs";
+
+    print "extract $name from $tarball\n";
+    vsystem ("tar xvzf $tarball");
+
+    print "tardir: $tardir\n";
+
+    # enter the directory and build
+    chdir $tardir;
+
+    # build the library using psconfigure, make, make install
+    if ($configure_opts eq "NONE") {
+        vsystem ("$psconfigure");
+    } else {
+        $configure_opts = join (' ', split (',', $configure_opts));
+        vsystem ("$psconfigure $configure_opts");
+    }
+    if ($?) { &failure($name, "failure in configure"); }
+
+    if ($make_opts eq "NONE") {
+        vsystem ("make");
+    } else {
+        $make_opts = join (' ', split (',', $make_opts));
+        vsystem ("make $make_opts");
+    }
+    if ($?) { &failure($name, "failure in make"); }
+
+    if ($install_opts eq "NONE") {
+        vsystem ("make install");
+    } else {
+        $install_opts = join (' ', split (',', $install_opts));
+        vsystem ("make install $install_opts");
+    }
+    if ($?) { &failure($name, "failure in make install"); }
+
+    chdir $homedir;
+    return 1;
+}
+
+sub checklib {
+    my $name = $_[0];
+    my $altnames = $_[1];
+    my $altpaths = $_[2];
+
+    @subdirs = ".";
+    if ($altpaths ne "NONE") {
+        @altpaths = split (',', $altpaths);
+        push @subdirs, @altpaths;
+    }
+
+    @trynames = ($name);
+    if ($altnames ne "NONE") {
+        @altnames = split (',', $altnames);
+        push @trynames, @altnames;
+    }
+
+    # try each of the possible library names
+    foreach $tryname (@trynames) {
+        # try each of the library paths, with the default as well as each altpath
+        foreach $topdir ( @libpath ) {
+            foreach $subdir ( @subdirs ) {
+                if ($subdir eq ".") {
+                    $path = $topdir;
+                } else {
+                    $path = "$topdir/$subdir";
+                }
+                # print "trying $path\n";
+                if (! -e $path) { next; }
+                $libname = "$path/$tryname.a";
+                if (-e $libname) { return $libname; }
+                # print "no $libname\n";
+                $libname = "$path/$tryname.$dlltype";
+                if (-e $libname) { return $libname; }
+                # print "no $libname\n";
+            }
+        }
+
+        # if we failed to find the basic named library files, try the
+        # versions libraries if we find only a .so.N without a matching
+        # .so, we link this in the installed libdir
+	# XXX this was probably a mistake to allow some systems to build without supplied libs
+	if (1) {
+	    foreach $topdir ( @libpath ) {
+		foreach $subdir ( @subdirs ) {
+		    if ($subdir eq ".") {
+			$path = $topdir;
+		    } else {
+			$path = "$topdir/$subdir";
+		    }
+		    if (! -e $path) { next; }
+		    @libnames = <$path/$tryname.$dlltype*>;
+		    if (@libnames > 0) {
+			$libname = @libnames[-1];
+			$needdev = 1;
+			# print " *** need developer version of $name\n";
+			return "runtime-only";
+
+			# XXX old option: link in existing library
+			# symlink $libname, "$libdir/$f.$dlltype";
+			# if ($?) { exit 1; }
+			# return $libname;
+		    }
+		}
+	    }
+	}
+    }
+    return 0;
+}
+
+sub checkbin {
+    my $name = $_[0];
+    my $altnames = $_[1];
+    my $altpaths = $_[2];
+
+    # XXX drop this for bin?
+    @subdirs = ".";
+    if ($altpaths ne "NONE") {
+        @altpaths = split (',', $altpaths);
+        push @subdirs, @altpaths;
+    }
+
+    # try each of the library paths, with the default as well as each altpath
+    foreach $topdir ( @binpath ) {
+        foreach $subdir ( @subdirs ) {
+            if ($subdir eq ".") {
+                $path = $topdir;
+            } else {
+                $path = "$topdir/$subdir";
+            }
+            # print "trying $path\n";
+            if (! -e $path) { next; }
+            $binname = "$path/$name";
+            if (-e $binname) { return $binname; }
+        }
+    }
+    return 0;
+}
+
+sub checkinc {
+    my $name = $_[0];
+    my $altnames = $_[1];
+    my $altpaths = $_[2];
+
+    @subdirs = ".";
+    if ($altpaths ne "NONE") {
+        @altpaths = split (',', $altpaths);
+        push @subdirs, @altpaths;
+    }
+
+    # try each of the library paths, with the default as well as each altpath
+    foreach $topdir ( @incpath ) {
+        foreach $subdir ( @subdirs ) {
+            if ($subdir eq ".") {
+                $path = $topdir;
+            } else {
+                $path = "$topdir/$subdir";
+            }
+            if (! -e $path) { next; }
+            $incname = "$path/$name";
+            if (-e $incname) { return $incname; }
+        }
+    }
+
+    return 0;
+}
+
+sub checkarch {
+    # we are going to supplement the global libpath supplied
+
+    # check the hardware architecture:
+    $sys=`uname -s`; chomp $sys;
+
+    # default values
+    $ranlib = "ranlib";
+    $dlltype = "so";
+
+    if ($sys eq "IRIX64") {
+        $arch = "irix";
+        return;
+    }
+
+    if ($sys eq "SunOS") {
+        $ver=`uname -r | awk '{print substr($1,1,1)}'`;
+        if ($ver == 5) {
+            $arch = "sol";
+        } else {
+            $arch="sun4";
+        }
+
+        # sun (at least) seems to need the socket library (linux does not)
+        push @libpath, "/usr/openwin/lib";
+        push @incpath, "/usr/openwin/include";
+
+        # XXX this is a problem
+        print STDERR "need to add system dependent libraries (eg, libsocket, libnsl)\n";
+        exit 1;
+        $needlibs = "$needlibs libsocket libnsl";
+        $ranlib = "touch";
+        return;
+    }
+
+    if ($sys eq "Linux") {
+        $arch = "linux";
+
+        if (-e "/etc/sidious.config") {
+            $arch = "sid";
+            return;
+        }
+
+        $mach = `uname -m`; chomp $mach;
+
+        if ($mach eq "x86_64") {
+            $arch = "lin64";
+            unshift @libpath, "/lib64";
+            unshift @libpath, "/usr/lib64";
+            unshift @libpath, "/usr/X11R6/lib64";
+            return;
+        }
+        return;
+    }
+
+    if ($sys eq "Darwin") {
+        $mach = `uname -m`; chomp $mach;
+        if ($mach eq "i386") {
+            $arch="darwin_x86";
+        } else {
+            $arch = "darwin";
+        }
+        $dlltype = "dylib";
+        unshift @libpath, "/sw/lib";
+        unshift @incpath, "/sw/include";
+        unshift @incpath, "/usr/include/sys";
+        return;
+    }
+
+    if ($sys eq "HP-UX") {
+        $arch = "hpux";
+        return;
+    }
+
+    print "unknown architecture";
+    exit 1;
+}
+
+sub usage {
+    print STDERR "USAGE: pscheckperl [-version] [-build]\n";
+    exit 2;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub list_distributions {
+    @list = <$tagsets/*.perl>;
+    foreach $line (@list) {
+        chomp $line;
+        ($dist) = $line =~ m|$tagsets/(\S*).perl|;
+        print STDERR "$dist\n";
+    }
+    exit 2;
+}
+
+sub failure {
+    system ("ls");
+    die "problem building $_[0] : $_[1]\n";
+    print "\033]0; ** pschecklibs: failure  ** \007";
+}
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/pscheckmods
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/pscheckmods	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/pscheckmods	(revision 22322)
@@ -0,0 +1,18 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 2) { die "USAGE: pscheckmods (module) (version)\n"; }
+
+$x = eval "require $ARGV[0]; 1";
+if (! $x) { 
+    exit 1;
+}
+
+$version = eval "\$$ARGV[0]::VERSION";
+print "$ARGV[0]: $version\n";
+
+if ($ARGV[1] > $version) {
+    print "$ARGV[0] is too old: have $version : need $ARGV[1]\n";
+    exit 1;
+}
+
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/psconfig/pscheckperl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/pscheckperl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/pscheckperl	(revision 22322)
@@ -0,0 +1,152 @@
+#!/usr/bin/env perl
+
+$tagsets = "tagsets";
+
+$version = "";
+$build = 0;
+my %force;   # Names of module to force build
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-version") {
+	$version = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-build") {
+	$build = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-force") {
+	$force{$ARGV[1]} = 1;
+	shift; shift; next;
+    }
+    if ($ARGV[0] eq "-h")     { &usage (); }
+    if ($ARGV[0] eq "help")   { &usage (); }
+    if ($ARGV[0] eq "-help")  { &usage (); }
+    if ($ARGV[0] eq "--help") { &usage (); }
+    if ($ARGV[0] eq "-list")  { &list_distributions(); }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+if ( @ARGV > 1) { &usage(); }
+
+if ( @ARGV == 0) { 
+    @list = <$tagsets/*.perl>;
+    $file = $list[-1];
+} else {
+    $file = "$tagsets/$ARGV[0].perl";
+}
+print "examining perl modules based on $file\n";
+
+# load the perl module list
+open (FILE, $file) || die "ERROR: can't open perl module list: $file\n";
+@list = <FILE>;
+close (FILE);
+
+# set the psconfig version:
+if ("$version" eq "") {
+    $version = $ENV{'PSVERSION'};
+}
+if ("$version" eq "") {
+    $version = "default";
+}
+
+
+$prefix  = `csh -f psconfig.csh --prefix $version`; chomp $prefix;
+$perldir = `csh -f psconfig.csh --perldir $version`; chomp $perldir;
+$homedir = `pwd`; chomp $homedir;
+
+print "psconfig version: $version\n";
+print "prefix: $prefix\n";
+print "perldir: $perldir\n";
+print "PERL5LIB: $ENV{'PERL5LIB'}\n";
+$psconfdir = $ENV{'PSCONFDIR'};
+
+if ($build && ! -d $psconfdir) {
+    mkdir $psconfdir || die "unable to create psconfig dir $psconfdir";
+}
+
+$Nmissing = 0;
+@missing = ();
+foreach $line (@list) {
+    chop $line;
+    if ($line =~ m|^\s*$|) { next; }
+    if ($line =~ m|^\s*\#|) { next; }
+
+    ($N, $module, $tarball, $modver, $prompts) = split (" ", $line);
+
+    if ($modver eq "") { $modver = 0; } 
+    system ("pscheckmods $module $modver");
+    if ($? == 0) { 
+	# print "$module: found\n";
+	next; 
+    }
+
+    print "$module: missing\n";
+    unless ($build or defined $force{$module} or defined $force{'all'}) { 
+	$Nmissing ++;
+	push @missing, $module;
+	next; 
+    }
+
+    # try to build the module from ../../extperl/Module.*.tar.gz
+    chdir "../../extperl" or die "Unable to find ../../extperl directory.";
+
+    print "extract $module from $tarball\n";
+    vsystem ("tar xvzf $tarball");
+    
+    ($tardir) = $tarball =~ m|(\S*).tar.gz|;
+    print "tardir: $tardir\n";
+
+    chdir $tardir;
+
+    # build the MakeMaker makefile, setting the output directories
+    if ($prompts) {
+	@answers = split (",", $prompts);
+	open (PIPE, "|perl Makefile.PL PREFIX=$prefix LIB=$perldir");
+	foreach $answer (@answers) {
+	    print PIPE "$answer\n";
+	}
+	close (PIPE);
+    } else {
+	vsystem ("perl Makefile.PL PREFIX=$prefix LIB=$perldir");
+    }
+    
+    vsystem ("make < /dev/null");
+    vsystem ("make install");
+
+    chdir $homedir;
+}
+if (!$build) {
+    if ($Nmissing > 0) {
+	print "The following $Nmissing perl modules are missing from your system\n";
+	foreach $name (@missing) {
+	    print "  $name\n";
+	}
+	print "you may install them in your local path by re-running pscheckperl with -build\n";
+    } else {
+	print "no perl modules are missing from your system\n";
+    }
+}
+exit 0;
+
+sub usage {
+    print STDERR "USAGE: pscheckperl [-version] [-build]\n";
+    exit 2;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub list_distributions {
+    @list = <$tagsets/*.perl>;
+    foreach $line (@list) {
+	chomp $line;
+	($dist) = $line =~ m|$tagsets/(\S*).perl|;
+	print STDERR "$dist\n";
+    }
+    exit 2;
+}
Index: /tags/sj_tags/sj_root_20080929/psconfig/psconfig.bash.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/psconfig.bash.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/psconfig.bash.in	(revision 22322)
@@ -0,0 +1,41 @@
+# this script sets the PS IPP build environment for BASH shell users
+
+if [ -z $PSCONFIG_DIR ]; then
+  # PSCONFIG_DIR=/home/eugene/src/panstarrs/ipp/psconfig
+  PSCONFDIR=@PSCONFDIR@
+fi
+
+if (( $# == 0 )); then
+  /bin/csh -f $PSCONFDIR/psconfig.csh
+  return 0
+fi
+
+if [[ "$1" == "--help" ]]; then
+  echo "USAGE: psconfig (version) : set configuration to specified version"
+  return 0
+fi
+
+if [[ "$1" == "--list" ]]; then
+  /bin/csh -f $PSCONFDIR/psconfig.csh --list
+  return 0
+fi
+
+version=$1
+psconfigure=`/bin/csh -f $PSCONFDIR/psconfig.csh --psconfigure $version`
+psautogen=`/bin/csh -f $PSCONFDIR/psconfig.csh --psautogen $version`
+psperlbuild=`/bin/csh -f $PSCONFDIR/psconfig.csh --psperlbuild $version`
+alias psconfigure=$psconfigure
+alias psautogen=$psautogen
+alias psperlbuild=$psperlbuild
+
+# psconfig env variables
+export PSCONFDIR=`/bin/csh -f $PSCONFDIR/psconfig.csh --psconfdir $version`
+export PSVERSION=`/bin/csh -f $PSCONFDIR/psconfig.csh --psversion $version`
+
+# environment variables
+export PATH=`/bin/csh -f $PSCONFDIR/psconfig.csh --path $version`
+export ARCH=`/bin/csh -f $PSCONFDIR/psconfig.csh --arch $version`
+export LD_LIBRARY_PATH=`/bin/csh -f $PSCONFDIR/psconfig.csh --ld_library_path $version`
+export PKG_CONFIG_PATH=`/bin/csh -f $PSCONFDIR/psconfig.csh --pkg_config_path $version`
+export ACLOCAL_FLAGS=`/bin/csh -f $PSCONFDIR/psconfig.csh --aclocal_flags $version`
+export PERL5LIB=`/bin/csh -f $PSCONFDIR/psconfig.csh --perl5lib $version`
Index: /tags/sj_tags/sj_root_20080929/psconfig/psconfig.csh.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/psconfig.csh.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/psconfig.csh.in	(revision 22322)
@@ -0,0 +1,555 @@
+#/bin/csh -f
+# this file is sourced using the command 'psconfig'
+
+# list the defined psconfig versions
+set show_prefix = 0
+set show_libs = 0
+set show_bin = 0
+set show_man = 0
+set show_arch = 0
+set show_path = 0
+set show_cpath = 0
+set show_include = 0
+set show_configure = 0
+set show_autogen = 0
+set show_perlbuild = 0
+set show_perldir = 0
+set show_perl5lib = 0
+set show_psversion = 0
+set show_psconfdir = 0
+set show_library_path = 0
+set show_ld_library_path = 0
+set show_pkg_config_path = 0
+set show_aclocal_flags = 0
+set args = ""
+while ($#argv) 
+  switch ($argv[1])
+    case --help:
+      goto help;
+    case --list:
+      echo "PSCONFDIR : $PSCONFDIR"
+      /bin/ls $PSCONFDIR | grep -v "\<man\>" | grep -v "\<share\>" | awk -F. '{printf "  %-10s : ", $NF}{for (i = 1; i < NF-1; i++){printf "%s.", $i}}{printf "%s\n", $(NF-1)}'
+      exit 0
+    case --prefix:
+      set show_prefix = 1
+      breaksw
+    case --libs:
+      set show_libs = 1
+      breaksw
+    case --bin
+      set show_bin = 1
+      breaksw
+    case --man
+      set show_man = 1
+      breaksw
+    case --include
+      set show_include = 1
+      breaksw
+    case --psconfigure:
+      set show_configure = 1
+      breaksw;   
+    case --psautogen:
+      set show_autogen = 1
+      breaksw;
+    case --psperlbuild:
+      set show_perlbuild = 1
+      breaksw;
+    case --perldir:
+      set show_perldir = 1
+      breaksw;
+    case --perl5lib:
+      set show_perl5lib = 1
+      breaksw;
+    case --psversion
+      set show_psversion = 1
+      breaksw;
+    case --psconfdir
+      set show_psconfdir = 1
+      breaksw;
+    case --ld_library_path
+      set show_ld_library_path = 1
+      breaksw;
+    case --library_path
+      set show_library_path = 1
+      breaksw;
+    case --pkg_config_path
+      set show_pkg_config_path = 1
+      breaksw;
+    case --arch
+      set show_arch = 1
+      breaksw;
+    case --path
+      set show_path = 1
+      breaksw;
+    case --cpath
+      set show_cpath = 1
+      breaksw;
+    case --aclocal_flags
+      set show_aclocal_flags = 1
+      breaksw;
+    case -*:
+      echo "unknown option $1"
+      goto help;
+    default:
+      set args=($args $1);
+      breaksw;
+  endsw
+  shift
+end
+if ($#args != 1) goto usage
+if ("$args" == "") goto usage
+
+# make this configurable by the user
+if (! $?PSCONFDIR) then
+  setenv PSCONFDIR @PSCONFDIR@
+endif
+
+if (! $?PSVERSION) then
+  setenv PSVERSION default
+endif
+
+if (-e $HOME/.psconfigrc) then
+  source $HOME/.psconfigrc
+endif
+
+setenv PSVERSION $args[1]
+
+if ($?CPATH == 0) setenv CPATH
+if ($?LIBRARY_PATH == 0) setenv LIBRARY_PATH
+if ($?LD_LIBRARY_PATH == 0) setenv LD_LIBRARY_PATH
+if ($?PKG_CONFIG_PATH == 0) setenv PKG_CONFIG_PATH
+if ($?PERL5LIB == 0) setenv PERL5LIB
+if ($?MANPATH == 0) setenv MANPATH
+
+# identify system architecture
+set sys=`uname -s` 
+if ("$sys" == "SunOS") then
+ set ver = `uname -r | awk '{print substr($1,1,1)}'`;
+ if ($ver > 4) then 
+   set sys = "Solaris"
+ endif
+endif
+if ("$sys" == "Linux") then
+ grep "Sidious" /etc/issue > /dev/null
+ if ($status == 0) then 
+  set sys = Sidious
+ endif
+ grep "Red Hat Enterprise" /etc/issue > /dev/null
+ if ($status == 0) then 
+  set sys = RedHat
+ endif
+endif
+
+# determine architecture-dependent paths & variables for all shells
+switch ($sys)
+ case IRIX64:
+   setenv ARCH irix;
+   breaksw;
+
+ case Solaris:
+   setenv ARCH sol
+   breaksw;
+
+ case SunOs:
+   setenv ARCH sun4
+   breaksw;
+
+ case Linux:
+   setenv ARCH linux;
+   set mach=`uname -m`
+   if ("$mach" == "x86_64") setenv ARCH lin64
+   breaksw;
+
+ case Darwin:
+   setenv ARCH darwin;
+   set mach=`uname -m`
+   if ("$mach" == "i386") setenv ARCH darwin_x86
+   breaksw;
+
+ case Sidious:
+   setenv ARCH sid;
+   breaksw;
+
+ case RedHat:
+   setenv ARCH linrh;
+   set mach=`uname -m`
+   if ("$mach" == "x86_64") setenv ARCH linrh64
+   breaksw;
+
+ case HP-UX:
+    setenv ARCH hp;
+    breaksw;
+
+ default:
+   echo "unknown architecture";
+   setenv ARCH unknown;
+   breaksw;
+endsw
+ 
+setenv PSCONFIG $PSVERSION.$ARCH
+
+set newpath = ""
+set pathlist = `echo $PATH | tr ':' '\n'`
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+set bindir = {$PSCONFDIR}/{$PSCONFIG}/bin
+if ("$PSCONFIG" == "none") then
+  setenv PATH {$newpath}
+else
+  setenv PATH {$bindir}:{$newpath}
+endif
+
+set mandir  = {$PSCONFDIR}/{$PSCONFIG}/man
+set newpath = ""
+set pathlist = `echo $MANPATH | tr ':' '\n'`
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+if ("$PSCONFIG" == "none") then
+  if ("$newpath" == "") then
+    unsetenv MANPATH
+  else
+    setenv MANPATH {$newpath}
+  endif 
+else
+  if ("$newpath" == "") then
+    setenv MANPATH {$mandir}:
+  else
+    setenv MANPATH {$mandir}:{$newpath}
+  endif 
+endif 
+
+# set CPATH, used to find include files
+set incdir  = {$PSCONFDIR}/{$PSCONFIG}/include
+set newpath = ""
+set pathlist = `echo $CPATH | tr ':' '\n'`
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+
+## XXX mysql (and others?) are not installed in the correct location: they go into
+## prefix/lib/mysql regardless of configure options
+if ("$PSCONFIG" == "none") then
+  if ("$newpath" == "") then
+    unsetenv CPATH
+  else
+    setenv CPATH {$newpath}:
+  endif 
+else
+  if ("$newpath" == "") then
+    setenv CPATH {$incdir}:{$PSCONFDIR}/{$PSCONFIG}/include/mysql:
+  else
+    setenv CPATH {$incdir}:{$PSCONFDIR}/{$PSCONFIG}/include/mysql:{$newpath}:
+  endif 
+endif 
+
+# set LIBRARY_PATH and LD_LIBRARY_PATH, used to find libraries
+set libdir  = {$PSCONFDIR}/{$PSCONFIG}/lib
+set newpath = ""
+set pathlist = `echo $LD_LIBRARY_PATH | tr ':' '\n'`
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+
+## XXX mysql (and others?) are not installed in the correct location: they go into
+## prefix/lib/mysql regardless of configure options
+if ("$PSCONFIG" == "none") then
+  if ("$newpath" == "") then
+    unsetenv LD_LIBRARY_PATH
+    unsetenv LIBRARY_PATH
+  else
+    setenv LD_LIBRARY_PATH {$newpath}:
+    setenv LIBRARY_PATH {$newpath}:
+  endif 
+else
+  if ("$newpath" == "") then
+    setenv LD_LIBRARY_PATH {$libdir}:{$PSCONFDIR}/{$PSCONFIG}/lib/mysql:
+    setenv LIBRARY_PATH {$libdir}:{$PSCONFDIR}/{$PSCONFIG}/lib/mysql:
+  else
+    setenv LD_LIBRARY_PATH {$libdir}:{$PSCONFDIR}/{$PSCONFIG}/lib/mysql:{$newpath}:
+    setenv LIBRARY_PATH {$libdir}:{$PSCONFDIR}/{$PSCONFIG}/lib/mysql:{$newpath}:
+  endif 
+endif 
+
+# supplement CPATH, LIBRARY_PATH and LD_LIBRARY_PATH with a few extra common locations
+# we probably can drop the system libraries here
+set xtralibs = ( "/usr/local/lib" "/usr/lib" "/usr/X11R6/lib" "/lib" )
+set xtrapath = ( "/usr/local/include" "/usr/include" "/usr/X11R6/include" )
+
+# add architecture-dependent paths
+switch ($ARCH)
+   breaksw;
+
+ case sol:
+ case sun4:
+   set xtralibs = ( $xtralibs "/usr/openwin/lib" )
+   set xtrapath = ( $xtrapath "/usr/openwin/include")
+   breaksw;
+
+ case linux:
+ case linrh:
+   breaksw;
+
+ case lin64:
+ case linrh64:
+   set xtralibs = ( $xtralibs "/usr/lib64" "/usr/X11R6/lib64" "/lib64" )
+   breaksw;
+
+ case darwin:
+ case darwin_x86:
+   set xtralibs = ( $xtralibs "/sw/lib" )
+   set xtrapath = ( $xtrapath "/sw/include" )
+   breaksw;
+
+ case sid:
+ case hp:
+ case irix:
+
+ default:
+   echo "unknown architecture";
+   breaksw;
+endsw
+
+# add to LD_LIBRARY_PATH if not found
+foreach name ($xtralibs)
+  echo $LD_LIBRARY_PATH | grep $name> /dev/null
+  if ($status == 0) continue
+  setenv LD_LIBRARY_PATH {$LD_LIBRARY_PATH}{$name}:
+end
+# add to LIBRARY_PATH if not found
+foreach name ($xtralibs)
+  echo $LIBRARY_PATH | grep $name > /dev/null
+  if ($status == 0) continue
+  setenv LIBRARY_PATH {$LIBRARY_PATH}{$name}:
+end
+# add to CPATH if not found
+foreach name ($xtrapath)
+  echo $CPATH | grep $name > /dev/null
+  if ($status == 0) continue
+  setenv CPATH {$CPATH}{$name}:
+end
+
+# Build wants to put things in prefix/*, MakeMaker wants to put them in prefix/perl5/*
+set plibdir  = {$PSCONFDIR}/{$PSCONFIG}/lib
+# set plib5dir = {$PSCONFDIR}/{$PSCONFIG}/lib/perl5
+set newpath = ""
+set pathlist = `echo $PERL5LIB | tr ':' '\n'`
+# build newpath with contents of PERL5LIB excluding PSCONFDIR entries
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+if ("$PSCONFIG" == "none") then
+  if ("$newpath" == "") then
+    unsetenv PERL5LIB
+  else
+    setenv PERL5LIB {$newpath}
+  endif 
+else
+  if ("$newpath" == "") then
+    setenv PERL5LIB {$plibdir}:
+  else
+    setenv PERL5LIB {$plibdir}:{$newpath}
+  endif 
+endif 
+
+set newpath = ""
+set pathlist = `echo $PKG_CONFIG_PATH | tr ':' '\n'`
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+if ("$PSCONFIG" == "none") then
+  if ("$newpath" == "") then
+    unsetenv PKG_CONFIG_PATH
+  else
+    setenv PKG_CONFIG_PATH {$newpath}
+  endif
+else
+  if ("$newpath" == "") then
+    setenv PKG_CONFIG_PATH {$PSCONFDIR}/{$PSCONFIG}/lib/pkgconfig:
+  else
+    setenv PKG_CONFIG_PATH {$PSCONFDIR}/{$PSCONFIG}/lib/pkgconfig:{$newpath}
+  endif
+endif
+
+if ("$PSCONFIG" == "none") then
+  alias  psconfigure configure
+  alias  psautogen autogen.sh
+  alias  psperlbuild perl Build.PL
+else
+  alias  psconfigure ./configure --prefix={$PSCONFDIR}/{$PSCONFIG} --bindir=$bindir --libdir=$libdir --mandir=$mandir --includedir=$incdir --sysconfdir={$PSCONFDIR}/{$PSCONFIG}/etc --datadir={$PSCONFDIR}/{$PSCONFIG}/share
+  alias  psautogen ./autogen.sh  --prefix={$PSCONFDIR}/{$PSCONFIG} --bindir=$bindir --libdir=$libdir --mandir=$mandir --includedir=$incdir --sysconfdir={$PSCONFDIR}/{$PSCONFIG}/etc --datadir={$PSCONFDIR}/{$PSCONFIG}/share
+  alias  psperlbuild perl ./Build.PL --prefix {$PSCONFDIR}/{$PSCONFIG} --install_path script=$bindir --install_path arch=$bindir --install_path bin=$bindir --install_path lib=$libdir --install_path bindoc={$PSCONFDIR}/{$PSCONFIG}/man/man1 --install_path libdoc={$PSCONFDIR}/{$PSCONFIG}/man/man3
+  setenv ACLOCAL_FLAGS "-I $PSCONFDIR/$PSCONFIG/share/aclocal"
+endif
+
+# list selected environment variables
+if ($show_prefix) then
+  echo $PSCONFDIR/$PSCONFIG
+  exit 0
+endif
+if ($show_libs) then
+  echo $libdir
+  exit 0
+endif
+if ($show_bin) then
+  echo $bindir
+  exit 0
+endif
+if ($show_man) then
+  echo $mandir
+  exit 0
+endif
+if ($show_include) then
+  echo $incdir
+  exit 0
+endif
+
+# list the defined psconfig versions
+if ($show_configure) then
+  alias psconfigure 
+  exit 0
+endif
+
+# list the defined psconfig versions
+if ($show_autogen) then
+  alias psautogen
+  exit 0
+endif
+
+# list the defined psconfig versions
+if ($show_perlbuild) then
+  alias psperlbuild
+  exit 0
+endif
+
+if ($show_perldir) then
+  echo $plibdir
+  exit 0
+endif
+
+if ($show_perl5lib) then
+  echo $PERL5LIB
+  exit 0
+endif
+
+if ($show_psversion) then
+  echo $PSVERSION
+  exit 0
+endif
+
+if ($show_psconfdir) then
+  echo $PSCONFDIR
+  exit 0
+endif
+
+if ($show_ld_library_path) then
+  echo $LD_LIBRARY_PATH
+  exit 0
+endif
+
+if ($show_library_path) then
+  echo $LIBRARY_PATH
+  exit 0
+endif
+
+if ($show_pkg_config_path) then
+  echo $PKG_CONFIG_PATH
+  exit 0
+endif
+
+if ($show_arch) then
+  echo $ARCH
+  exit 0
+endif
+
+if ($show_path) then
+  echo $PATH
+  exit 0
+endif
+
+if ($show_cpath) then
+  echo $CPATH
+  exit 0
+endif
+
+if ($show_aclocal_flags) then
+  echo $ACLOCAL_FLAGS
+  exit 0
+endif
+
+
+
+exit 0
+
+usage:
+  if (! $?PSCONFDIR) echo "PSCONFDIR is not set : run psconfig with an argument to setup"
+  if (! $?PSVERSION) echo "PSVERSION is not set : run psconfig with an argument to setup"
+  if (! $?ARCH)      echo "ARCH is not set : run psconfig with an argument to setup"
+  echo $PSCONFDIR : $PSVERSION : $ARCH 
+  echo "psconfig --help for additional info"
+  exit 2
+
+help:
+  echo "psconfig: set or show the current pslib configuration information"
+  echo "USAGE: psconfig (version) : set configuration to specified version"
+  echo "       psconfig --bin     : return the current path"
+  echo "       psconfig --libs    : return the current library path"
+  echo "       psconfig --include"
+  echo 
+  echo "       psconfig --prefix  : show prefix directory"
+  echo "       psconfig --psconfigure"
+  echo "       psconfig --psautogen"
+  echo "       psconfig --psperlbuild"
+  echo "       psconfig --perldir"
+  echo "       psconfig --perl5lib"
+  echo "       psconfig --psversion"
+  echo "       psconfig --psconfdir"
+  echo "       psconfig --library_path"
+  echo "       psconfig --ld_library_path"
+  echo "       psconfig --pkg_config_path"
+  echo "       psconfig --arch"
+  echo "       psconfig --path"
+  echo "       psconfig --cpath"
+  echo "       psconfig --aclocal_flags"
+  echo
+  echo "       psconfig --list    : list currently availabe configuration versions"
+  echo "       psconfig --help    : this listing"
+  echo " use ~/.psconfigrc to set PSCONFDIR as desired"
+  exit 1
Index: /tags/sj_tags/sj_root_20080929/psconfig/psdist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/psdist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/psdist	(revision 22322)
@@ -0,0 +1,313 @@
+#!/usr/bin/env perl
+
+$tagsets = "tagsets";
+
+$cvs  = 0;
+$diff = 0;
+$settag = 0;
+$update = 0;
+$mkdist = 0;
+$module = "";
+$settag_mode = "";
+$update_mode = "";
+$mkdist_mode = "";
+$mkdist_head = 0;
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-diff") {
+        $diff = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-update") {
+        $update = 1; shift;
+        if ($ARGV[0] eq "tags") {
+            $update_mode = "tags";
+            shift; next
+        }
+        if ($ARGV[0] eq "head") {
+            $update_mode = "head";
+            shift; next
+        }
+        &usage();
+    }
+    if ($ARGV[0] eq "-tag") {
+        $settag = 1; shift;
+        if ($ARGV[0] eq "dev") {
+            $settag_mode = "dev";
+            shift; next
+        }
+        if ($ARGV[0] eq "rev") {
+            $settag_mode = "rev";
+            shift; next
+        }
+        if ($ARGV[0] eq "branch") {
+            $settag_mode = "branch";
+            shift; next
+        }
+        &usage();
+    }
+    if ($ARGV[0] eq "-dist") {
+        $mkdist = 1; shift;
+        if ($ARGV[0] eq "cvs") {
+            $mkdist_mode = "cvs";
+            shift; next
+        }
+        if ($ARGV[0] eq "tree") {
+            $mkdist_mode = "tree";
+            shift; next
+        }
+        if ($ARGV[0] eq "autogen") {
+            $mkdist_mode = "autogen";
+            shift; next
+        }
+        if ($ARGV[0] eq "tarball") {
+            $mkdist_mode = "tarball";
+            shift; next
+        }
+        &usage();
+    }
+    if ($ARGV[0] eq "-module") {
+        $module = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-head")  { $mkdist_head = 1; shift; next; }
+    if ($ARGV[0] eq "-list")  { &list_distributions(); }
+    if ($ARGV[0] eq "-h")     { &usage(); }
+    if ($ARGV[0] eq "-help")  { &usage(); }
+    if ($ARGV[0] eq "--help") { &usage(); }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if ( @ARGV != 1) { &usage (); }
+
+$distribution = $ARGV[0];
+&load_distfile ();
+
+if ($diff)   { &difflist (); }
+if ($settag) { &settags (); }
+if ($update) { &update_tree (); }
+if ($mkdist) { &make_distribution (); }
+&usage();
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub difflist {
+
+    for ($i = 0; $i < @cvsname; $i++) {
+        # the base component cannot be rdiffed
+        print STDERR "--- $cvsname[$i] ---\n";
+        if ($cvsname[$i] eq "base") { next; }
+        if ($tag[$i] eq "") { next; }
+        &vsystem ("cvs -q rdiff -s -r $tag[$i] $cvsname[$i]");
+        print STDERR "\n\n";
+    }
+    exit 0;
+}
+
+sub settags {
+    print STDERR "setting tags\n";
+    if ($settag_mode eq "") { die "-tag mode is not set\n"; }
+    for ($i = 0; $i < @cvsname; $i++) {
+        if (($module ne "") && ($module ne $cvsname[$i])) { next; }
+        ## XXX make this backwards compatible with pre-ipp-2.4 releases (with four, not five, entries)?
+        ($do_tag, $do_build, $do_dist, $do_update, $dev_build) = $mode[$i] =~ m|(\S)(\S)(\S)(\S)(\S)|;
+        if ($do_tag eq "N") {
+            push @remind, $cvsname[$i];
+            next;
+        }
+        if ($branchtag[$i] eq "") { die "branch tag missing for $cvsname[$i]\n"; }
+        if ($branchver[$i] eq "") { die "branch tag version missing\n"; }
+        $tag = "$branchtag[$i]$branchver[$i]";
+        if ($settag_mode eq "branch") {
+            &vsystem ("cvs -q rtag -b $branchtag[$i] $cvsname[$i]");
+            next;
+        }
+        if ($settag_mode eq "dev") {
+            &vsystem ("cvs -q rtag $tag $cvsname[$i]");
+            next;
+        }
+        if ($settag_mode eq "rev") {
+            &vsystem ("cvs -q rtag -r $branchtag[$i] $tag $cvsname[$i]");
+            next;
+        }
+        die "programming error";
+    }
+    for ($i = 0; $i < @remind; $i++) {
+        print STDERR "remember to set tag for $remind[$i] if needed\n";
+    }
+    exit 0;
+}
+
+sub update_tree {
+    print STDERR "updating tree\n";
+    if ($update_mode eq "") { die "-update mode is not set\n"; }
+
+    $homedir = `pwd`; chomp $homedir;
+    chdir "..";
+
+    for ($i = 0; $i < @cvsname; $i++) {
+        if (($module ne "") && ($module ne $cvsname[$i])) { next; }
+        ($do_tag, $do_build, $do_dist, $do_update) = $mode[$i] =~ m|(\S)(\S)(\S)(\S)|;
+        if ($do_update eq "N") {
+            push @remind, $cvsname[$i];
+            next;
+        }
+        # only update modules with tags
+        if ($branchtag[$i] eq "") { die "branch tag missing for $cvsname[$i]\n"; }
+        if ($branchver[$i] eq "") { die "branch tag version missing\n"; }
+        $tag = "$branchtag[$i]$branchver[$i]";
+        if ($update_mode eq "tags") {
+            &vsystem ("cvs -q co -r $tag $cvsname[$i]");
+            next;
+        }
+        if ($update_mode eq "head") {
+            &vsystem ("cvs -q co -A $cvsname[$i]");
+            next;
+        }
+        die "programming error";
+    }
+    for ($i = 0; $i < @remind; $i++) {
+        print STDERR "remember to update $remind[$i] if needed\n";
+    }
+    chdir $homedir;
+    exit 0;
+}
+
+# XXX probably need to force CVSROOT here
+sub make_distribution {
+    # extract source tree and optionally package the distribution into a tarball
+    print STDERR "making distribution\n";
+    if ($mkdist_mode eq "") { die "-dist mode is not set\n"; }
+    mkdir $distribution;
+    chdir $distribution;
+    for ($i = 0; $i < @cvsname; $i++) {
+        if (($module ne "") && ($module ne $cvsname[$i])) { next; }
+        ($do_tag, $do_build, $do_dist, $do_update) = $mode[$i] =~ m|(\S)(\S)(\S)(\S)|;
+        if ($do_dist eq "N") { next; }
+
+        if ($branchtag[$i] eq "") { die "branch tag missing for $cvsname[$i]\n"; }
+        if ($branchver[$i] eq "") { die "branch tag version missing\n"; }
+        $tag = "$branchtag[$i]$branchver[$i]";
+
+        if ($mkdist_head) {
+            &vsystem ("cvs co $cvsname[$i]");
+            if ($status) { die "error running cvs"; }
+        } else {
+            &vsystem ("cvs co -r $tag $cvsname[$i]");
+            if ($status) { die "error running cvs"; }
+        }
+    }
+    chdir "..";
+    if ($mkdist_mode eq "cvs") { exit 0; }
+
+    # remove the CVS directories
+    &vsystem ("rm -r `find $distribution -name CVS`");
+    if ($mkdist_mode eq "tree") { exit 0; }
+
+    # make sure the aclocal path exists
+    @word = split (" ", $ENV{'ACLOCAL_FLAGS'});
+    if (@word != 2) { &failure("setup", "ACLOCAL_FLAGS is not set"); }
+    if (! -e $word[1]) { vsystem ("mkdir -p $word[1]"); }
+
+    # run autogen.sh, if present, to build a configure script
+    chdir $distribution;
+    for ($i = 0; $i < @cvsname; $i++) {
+        if (($module ne "") && ($module ne $cvsname[$i])) { next; }
+        ($do_tag, $do_build, $do_dist, $do_update) = $mode[$i] =~ m|(\S)(\S)(\S)(\S)|;
+        if ($do_dist eq "N") { next; }
+
+        if (! -e "$cvsname[$i]/autogen.sh" || -e "$cvsname[$i]/Build.PL" ) { next; }
+        chdir $cvsname[$i];
+
+        vsystem ("./autogen.sh --no-configure");
+        if ($status) { die "error running autogen.sh"; }
+
+#       vsystem ("make distclean");
+#       if ($status) { die "error running make distclean"; }
+        chdir "..";
+    }
+    chdir "..";
+    if ($mkdist_mode eq "autogen") { exit 0; }
+
+    # build a tarball from the full tree
+    if (! -e tarballs) { mkdir "tarballs"; }
+    &vsystem ("tar cvzf tarballs/$distribution.tgz  $distribution");
+    if ($status) { die "error creating tarball\n"; }
+    exit 0;
+}
+
+sub list_distributions {
+    @list = <$tagsets/*.dist>;
+    foreach $line (@list) {
+        chomp $line;
+        ($dist) = $line =~ m|$tagsets/(\S*).dist|;
+        print STDERR "$dist\n";
+    }
+    exit 2;
+}
+
+sub load_distfile {
+    # open and read the distribution file
+    # results go into @cvsname, @branchtag, @branchver, @mode
+    $file = "$tagsets/$ARGV[0].dist";
+    open (FILE, $file) || die "ERROR: can't open distribution file $file\n";
+    @list = <FILE>;
+    close (FILE);
+
+    @mode = ();
+    @cvsname = ();
+    @branchtag = ();
+    @branchver = ();
+
+    foreach $line (@list) {
+        chop $line;
+        if ($line =~ m|^\s*$|) { next; }
+        if ($line =~ m|^\s*\#|) { next; }
+
+        ($mode, $cvsname, $branchtag, $branchver) = split (" ", $line);
+
+        if ($cvsname eq "") { die "missing module name\n"; }
+
+        ($do_tag, $do_build, $do_dist, $do_update) = $mode =~ m|(\S)(\S)(\S)(\S)|;
+        if (($do_tag ne "Y") && ($do_tag ne "N")) { die "invalid tag option $do_tag\n"; }
+        if (($do_build ne "Y") && ($do_build ne "N")) { die "invalid tag option $do_build\n"; }
+        if (($do_dist ne "Y") && ($do_dist ne "N")) { die "invalid tag option $do_dist\n"; }
+        if (($do_update ne "Y") && ($do_update ne "N")) { die "invalid tag option $do_update\n"; }
+
+        # print "module: $cvsname, branchtag: $branchtag, branchver: $branchver ";
+        # print "tag: $do_tag, build: $do_build, dist: $do_dist, update: $do_update\n";
+
+        push @mode, $mode;
+        push @cvsname, $cvsname;
+        push @branchtag, $branchtag;
+        push @branchver, $branchver;
+    }
+}
+
+sub usage {
+    print STDERR "USAGE: psdist [options] (distribution]\n";
+    print STDERR "     : -tag (dev)      : set tags on cvs HEAD based on distribution table (requires -tag)\n";
+    print STDERR "     : -tag (rev)      : set tags on branch based on distribution table\n";
+    print STDERR "     : -tag (branch)   : create branch tags based on distribution table (requires -tag)\n\n";
+
+    print STDERR "     : -update (tags)  : update this tree from cvs to match distribution tags\n";
+    print STDERR "     : -update (head)  : update this tree from cvs to match distribution tags\n\n";
+
+    print STDERR "     : -dist (cvs)     : check out a distribution and leave CVS directories in place\n";
+    print STDERR "     : -dist (tree)    : check out a distribution and leave a clean tree\n";
+    print STDERR "     : -dist (autogen) : check out a distribution and run autogen\n";
+    print STDERR "     : -dist (tarball) : check out a distribution and make a tarball\n";
+    print STDERR "     : -head           : use the cvs HEAD for distribution checkout (requires -dist)\n\n";
+
+    print STDERR "     : -module         : perform action only on the specified module\n";
+    print STDERR "     : -list           : list valid distributions \n";
+    print STDERR "     : -diff           : show the difference between the distribution and current cvs HEAD\n\n";
+    exit 2;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.0.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.0.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.0.dist	(revision 22322)
@@ -0,0 +1,53 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	base-1-5         -0
+  NNYY  libohana       	libohana-1-9     -0
+  NNYY  libfits        	libfits-1-7      -0
+  NNYY  libautocode    	libautocode-1-6  -0
+  NNYY  libdvo         	libdvo-1-4       -0
+  NNYY  libkapa        	libkapa-1-3      -0
+  NNYY  addstar        	addstar-1-8      -0
+  NNYY  delstar        	delstar-1-7      -0
+  NNYY  getstar        	getstar-1-3      -0
+  NNYY  kapa           	kapa-1-7         -0
+  NNYY  kii            	kii-1-7          -0
+  NNYY  relphot        	relphot-1-5      -0
+  NNYY  uniphot        	uniphot-1-5      -0
+  NNYY  opihi.base     	opihi-2-9        -0
+  NNYY  mana           	mana-1-7         -0
+  NNYY  dvo            	dvo-1-0          -0
+  NNYY  pantasks       	pantasks-1-0     -0
+  NNYY  pcontrol       	pcontrol-1-0     -0
+  NNYY  pclient        	pclient-1-0      -0
+  NYYY  psLib          	rel-1-0          -0
+  NYYY  psModules      	rel-1-0          -0
+  YYYY  psphot         	rel-0-8          -0
+  YYYY  psastro        	rel-0-8          -0
+  YYYY  ppStats        	rel-1-0          -0
+  YYYY  ppImage        	rel-1-0          -0
+  YYYY  ppNorm         	rel-1-0          -0
+  YYYY  ppMerge        	rel-1-0          -0
+  YNYY  pois           	rel-0-1          -0
+  YNYY  pswarp         	rel-0-1          -0
+  YNYY  ppStac         	rel-0-1          -0
+  YYYY  PS-IPP-Metadata-Config rel-1-0   -0
+  YYYY  PS-IPP-Config  	rel-1-0          -0
+  YYYY  ippScripts     	rel-1-0          -0
+  YYYY  glueforge      	rel-1-0          -0
+  YYYY  dbconfig       	rel-1-0          -0
+  NYNN  ippdb.src      			      
+  YYYY  ippconfig      	rel-1-0          -0
+  YYYY  ippTools       	rel-1-0          -0
+  YYYY  ippTasks       	rel-1-0          -0
+  YYYY  simtest        	rel-0-8          -0
+  YNYY  psconfig       	rel-1-0          -0
+  YNYY  ippMonitor     	rel-1-0          -0
+
+# there are externally required perl modules (see README?)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.0.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.0.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.0.perl	(revision 22322)
@@ -0,0 +1,59 @@
+# NN    Name                           Tarball                                Optional Responses
+  00    Module::Build                  Module-Build-0.2806.tar.gz                
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz            
+  02    Params::Validate               Params-Validate-0.87.tar.gz               
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz             
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz               
+  05    Time::Local                    Time-Local-1.17.tar.gz                    
+  06    DateTime                       DateTime-0.36.tar.gz                      
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                   
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz             
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz            
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz             
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz    
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz                
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                   
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                   
+  15    Digest                         Digest-1.15.tar.gz                        
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz             
+  17    version                        version-0.70.tar.gz
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz               
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz     
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz             
+  21    URI                            URI-1.35.tar.gz                           
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz          
+  23    Test::Exception                Test-Exception-0.24.tar.gz                
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz                 
+  25    Array::Compare                 Array-Compare-1.13.tar.gz                 
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz                   
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                    
+  28    Net::FTP                       libnet-1.19.tar.gz                        
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz                
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz        
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz              
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz                
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz     
+  34    CGI                            CGI.pm-3.25.tar.gz                        
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                      
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz               
+  37    LWP                            libwww-perl-5.805.tar.gz             
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz               
+  39    File::Temp                     File-Temp-0.18.tar.gz                     
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz             
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                     
+  42    YAML                           YAML-0.62.tar.gz                          y
+  43    Module::Load                   Module-Load-0.10.tar.gz                   
+  44    Params::Check                  Params-Check-0.25.tar.gz                  
+  45    Template                       Template-Toolkit-2.16.tar.gz              n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz         
+  47    Storable                       Storable-2.15.tar.gz                      
+  48    IO::String                     IO-String-1.08.tar.gz                     
+  49    Date::Parse                    TimeDate-1.16.tar.gz                      
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz                   
+  51    DB_File                        DB_File-1.814.tar.gz                      
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz                  
+  53    Heap                           Heap-0.71.tar.gz                          
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz       
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                       
+  56    Cache                          Cache-2.04.tar.gz                         
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                       
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.1.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.1.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.1.dist	(revision 22322)
@@ -0,0 +1,55 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	base-1-6         -1
+  NNYY  libohana       	libohana-1-10    -1
+  NNYY  libfits        	libfits-1-8      -1
+  NNYY  libautocode    	libautocode-1-7  -1
+  NNYY  libdvo         	libdvo-1-5       -1
+  NNYY  libkapa        	libkapa-1-4      -1
+  NNYY  addstar        	addstar-1-9      -1
+  NNYY  delstar        	delstar-1-8      -1
+  NNYY  getstar        	getstar-1-4      -1
+  NNYY  kapa           	kapa-1-8         -1
+  NNYY  kii            	kii-1-8          -1
+  NNYY  relphot        	relphot-1-6      -1
+  NNYY  uniphot        	uniphot-1-6      -1
+  NNYY  opihi.base     	opihi-2-10       -1
+  NNYY  mana           	mana-1-8         -1
+  NNYY  dvo            	dvo-1-1          -1
+  NNYY  pantasks       	pantasks-1-1     -1
+  NNYY  pcontrol       	pcontrol-1-1     -1
+  NNYY  pclient        	pclient-1-1      -1
+  NYYY  psLib          	rel-1-1          -0
+  NYYY  psModules      	rel-1-1          -0
+  YYYY  psphot         	rel-0-9          -0
+  YYYY  psastro        	rel-0-9          -0
+  YYYY  ppStats        	rel-1-1          -0
+  YYYY  ppImage        	rel-1-1          -0
+  YYYY  ppNorm         	rel-1-1          -0
+  YYYY  ppMerge        	rel-1-1          -0
+  YNYY  pedestal       	rel-1-1          -0
+  YNYY  dvoTools       	rel-1-0          -0
+  YNYY  pois           	rel-0-1          -1
+  YNYY  pswarp         	rel-0-1          -1
+  YNYY  ppStac         	rel-0-1          -1
+  YYYY  PS-IPP-Metadata-Config rel-1-1   -0
+  YYYY  PS-IPP-Config  	rel-1-1          -0
+  YYYY  ippScripts     	rel-1-1          -0
+  YYYY  glueforge      	rel-1            _01
+  NYYY  dbconfig       	rel-1_1          _13
+  NYNN  ippdb.src      			      
+  YYYY  ippconfig      	rel-1-1          -0
+  NYYY  ippTools       	rel-1_1          _13
+  YYYY  ippTasks       	rel-1-1          -0
+  YYYY  simtest        	rel-0-9          -0
+  YNYY  psconfig       	rel-1-1          -0
+  YNYY  ippMonitor     	rel-1-1          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.1.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.1.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.1.perl	(revision 22322)
@@ -0,0 +1,60 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0	    
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.2.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.2.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.2.dist	(revision 22322)
@@ -0,0 +1,56 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	base-1-6         -1
+  NNYY  libohana       	libohana-1-10    -1
+  NNYY  libfits        	libfits-1-8      -1
+  NNYY  libautocode    	libautocode-1-7  -1
+  NNYY  libdvo         	libdvo-1-5       -1
+  NNYY  libkapa        	libkapa-1-4      -1
+  NNYY  addstar        	addstar-1-9      -1
+  NNYY  delstar        	delstar-1-8      -1
+  NNYY  getstar        	getstar-1-4      -1
+  NNYY  kapa           	kapa-1-8         -1
+  NNYY  kii            	kii-1-8          -1
+  NNYY  relphot        	relphot-1-6      -1
+  NNYY  uniphot        	uniphot-1-6      -1
+  NNYY  opihi.base     	opihi-2-10       -1
+  NNYY  mana           	mana-1-8         -1
+  NNYY  dvo            	dvo-1-1          -1
+  NNYY  pantasks       	pantasks-1-1     -1
+  NNYY  pcontrol       	pcontrol-1-1     -1
+  NNYY  pclient        	pclient-1-1      -1
+  NYYY  psLib          	rel-1-1          -0
+  NYYY  psModules      	rel-1-1          -0
+  YYYY  psphot         	rel-0-9          -0
+  YYYY  psastro        	rel-0-9          -0
+  YYYY  ppStats        	rel-1-1          -0
+  YYYY  ppConfigDump   	rel-1-1          -0
+  YYYY  ppImage        	rel-1-1          -0
+  YYYY  ppNorm         	rel-1-1          -0
+  YYYY  ppMerge        	rel-1-1          -0
+  YYYY  pedestal       	rel-1-1          -0
+  YYYY  dvoTools       	rel-1-0          -0
+  YYYY  pois           	rel-0-1          -1
+  YYYY  pswarp         	rel-0-1          -1
+  YYYY  ppStac         	rel-0-1          -1
+  YYYY  PS-IPP-Metadata-Config rel-1-1   -0
+  YYYY  PS-IPP-Config  	rel-1-1          -0
+  YYYY  ippScripts     	rel-1-1          -0
+  YYYY  glueforge      	rel-1            _01
+  NYYY  dbconfig       	rel-1_1          _13
+  NYNN  ippdb.src      			      
+  YYYY  ippconfig      	rel-1-1          -0
+  NYYY  ippTools       	rel-1_1          _13
+  YYYY  ippTasks       	rel-1-1          -0
+  YYYY  simtest        	rel-0-9          -0
+  YNYY  psconfig       	rel-1-1          -0
+  YNYY  ippMonitor     	rel-1-1          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.2.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.2.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-1.2.perl	(revision 22322)
@@ -0,0 +1,60 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0	    
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.0.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.0.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.0.dist	(revision 22322)
@@ -0,0 +1,64 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	ipp-2-0  	 -0
+  NNYY  libohana       	ipp-2-0  	 -0
+  NNYY  libfits        	ipp-2-0  	 -0
+  NNYY  libautocode    	ipp-2-0  	 -0
+  NNYY  libdvo         	ipp-2-0  	 -0
+  NNYY  libkapa        	ipp-2-0  	 -0
+  NNYY  libtap.ohana   	ipp-2-0  	 -0
+  NNYY  addstar        	ipp-2-0  	 -0
+  NNYY  delstar        	ipp-2-0  	 -0
+  NNYY  getstar        	ipp-2-0  	 -0
+  NNYY  ohana.tools    	ipp-2-0  	 -0
+  NNYY  kapa           	ipp-2-0  	 -0
+  NNYY  kii            	ipp-2-0  	 -0
+  NNYY  relphot        	ipp-2-0  	 -0
+  NNYY  relastro       	ipp-2-0  	 -0
+  NNYY  uniphot        	ipp-2-0  	 -0
+  NNYY  opihi.base     	ipp-2-0  	 -0
+  NNYY  mana           	ipp-2-0  	 -0
+  NNYY  dvo            	ipp-2-0  	 -0
+  NNYY  pantasks       	ipp-2-0  	 -0
+  NNYY  pcontrol       	ipp-2-0  	 -0
+  NNYY  pclient        	ipp-2-0  	 -0      
+
+  YYYN  nebclient       ipp-2-0          -0
+  YYYN  Nebulous        ipp-2-0          -0
+  YYYY  PS-IPP-Metadata-Config ipp-2-0   -0
+  YYYY  PS-IPP-Config  	ipp-2-0          -0
+
+  NYYY  psLib          	ipp-2-0          -0
+  NYYY  psModules      	ipp-2-0          -0
+  YYYY  psphot         	ipp-2-0          -0
+  YYYY  psastro        	ipp-2-0          -0
+  YYYY  ppStats        	ipp-2-0          -0
+  YYYY  ppConfigDump   	ipp-2-0          -0
+  YYYY  ppImage        	ipp-2-0          -0
+  YYYY  ppNorm         	ipp-2-0          -0
+  YYYY  ppMerge        	ipp-2-0          -0
+  YYYY  pedestal       	ipp-2-0          -0
+  YYYY  dvoTools       	ipp-2-0          -0
+  YYYY  pois           	ipp-2-0          -0
+  YYYY  pswarp         	ipp-2-0          -0
+  YYYY  ppStac         	ipp-2-0          -0
+  YYYY  ppSim        	ipp-2-0          -0
+  YYYY  ippScripts     	ipp-2-0          -0
+  YYYY  glueforge      	ipp-2-0          -0
+  YYYY  dbconfig       	ipp-2-0          -0
+  NYNN  ippdb.src      			      
+  YYYY  ippconfig      	ipp-2-0          -0
+  YYYY  ippTools       	ipp-2-0          -0
+  YYYY  ippTasks       	ipp-2-0          -0
+  YYYY  simtest        	ipp-2-0          -0
+  YNYY  psconfig       	ipp-2-0          -0
+  YNYY  ippMonitor     	ipp-2-0          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.0.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.0.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.0.perl	(revision 22322)
@@ -0,0 +1,64 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+  60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.1.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.1.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.1.dist	(revision 22322)
@@ -0,0 +1,65 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	ipp-2-1  	 -0
+  NNYY  libohana       	ipp-2-1  	 -0
+  NNYY  libfits        	ipp-2-1  	 -0
+  NNYY  libautocode    	ipp-2-1  	 -0
+  NNYY  libdvo         	ipp-2-1  	 -0
+  NNYY  libkapa        	ipp-2-1  	 -0
+  NNYY  libtap.ohana   	ipp-2-1  	 -0
+  NNYY  addstar        	ipp-2-1  	 -0
+  NNYY  delstar        	ipp-2-1  	 -0
+  NNYY  getstar        	ipp-2-1  	 -0
+  NNYY  ohana.tools    	ipp-2-1  	 -0
+  NNYY  kapa2          	ipp-2-1  	 -0
+  NNYY  relphot        	ipp-2-1  	 -0
+  NNYY  relastro       	ipp-2-1  	 -0
+  NNYY  uniphot        	ipp-2-1  	 -0
+  NNYY  opihi.base     	ipp-2-1  	 -0
+  NNYY  mana           	ipp-2-1  	 -0
+  NNYY  dvo            	ipp-2-1  	 -0
+  NNYY  pantasks       	ipp-2-1  	 -0
+  NNYY  pcontrol       	ipp-2-1  	 -0
+  NNYY  pclient        	ipp-2-1  	 -0      
+
+  NYNN  Nebulous/nebclient ipp-2-1       -0
+  YYYN  Nebulous        ipp-2-1          -0
+  YYYY  PS-IPP-Metadata-Config ipp-2-1   -0
+  YYYY  PS-IPP-Config  	ipp-2-1          -0
+
+  NYYY  psLib          	ipp-2-1          -0
+  NYYY  psModules      	ipp-2-1          -0
+  YYYY  psphot         	ipp-2-1          -0
+  YYYY  psastro        	ipp-2-1          -0
+  YYYY  ppStats        	ipp-2-1          -0
+  YYYY  ppConfigDump   	ipp-2-1          -0
+  YYYY  ppImage        	ipp-2-1          -0
+  YYYY  ppNorm         	ipp-2-1          -0
+  YYYY  ppMerge        	ipp-2-1          -0
+  YYYY  pedestal       	ipp-2-1          -0
+  YYYY  dvoTools       	ipp-2-1          -0
+  YYYY  pswarp         	ipp-2-1          -0
+  YYYY  ppStack        	ipp-2-1          -0
+  YYYY  ppSub          	ipp-2-1          -0
+  YYYY  ppSim        	ipp-2-1          -0
+
+  YYYY  glueforge      	ipp-2-1          -0
+  YYYY  dbconfig       	ipp-2-1          -0
+  NYNN  ippdb.src      			      
+  YYYY  ippTools       	ipp-2-1          -0
+  YYYY  ippScripts     	ipp-2-1          -0
+  YYYY  ippTasks       	ipp-2-1          -0
+
+  YYYY  ippconfig      	ipp-2-1          -0
+  YYYY  simtest        	ipp-2-1          -0
+  YNYY  psconfig       	ipp-2-1          -0
+  YNYY  ippMonitor     	ipp-2-1          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.1.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.1.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.1.perl	(revision 22322)
@@ -0,0 +1,67 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.2.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.2.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.2.dist	(revision 22322)
@@ -0,0 +1,65 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	ipp-2-2  	 -0
+  NNYY  libohana       	ipp-2-2  	 -0
+  NNYY  libfits        	ipp-2-2  	 -0
+  NNYY  libautocode    	ipp-2-2  	 -0
+  NNYY  libdvo         	ipp-2-2  	 -0
+  NNYY  libkapa        	ipp-2-2  	 -0
+  NNYY  libtap.ohana   	ipp-2-2  	 -0
+  NNYY  addstar        	ipp-2-2  	 -0
+  NNYY  delstar        	ipp-2-2  	 -0
+  NNYY  getstar        	ipp-2-2  	 -0
+  NNYY  ohana.tools    	ipp-2-2  	 -0
+  NNYY  kapa2          	ipp-2-2  	 -0
+  NNYY  relphot        	ipp-2-2  	 -0
+  NNYY  relastro       	ipp-2-2  	 -0
+  NNYY  uniphot        	ipp-2-2  	 -0
+  NNYY  opihi.base     	ipp-2-2  	 -0
+  NNYY  mana           	ipp-2-2  	 -0
+  NNYY  dvo            	ipp-2-2  	 -0
+  NNYY  pantasks       	ipp-2-2  	 -0
+  NNYY  pcontrol       	ipp-2-2  	 -0
+  NNYY  pclient        	ipp-2-2  	 -0      
+
+  NYNN  Nebulous/nebclient ipp-2-2       -0
+  YYYN  Nebulous        ipp-2-2          -0
+  YYYY  PS-IPP-Metadata-Config ipp-2-2   -0
+  YYYY  PS-IPP-Config  	ipp-2-2          -0
+
+  NYYY  psLib          	ipp-2-2          -0
+  NYYY  psModules      	ipp-2-2          -0
+  YYYY  psphot         	ipp-2-2          -0
+  YYYY  psastro        	ipp-2-2          -0
+  YYYY  ppStats        	ipp-2-2          -0
+  YYYY  ppConfigDump   	ipp-2-2          -0
+  YYYY  ppImage        	ipp-2-2          -0
+  YYYY  ppNorm         	ipp-2-2          -0
+  YYYY  ppMerge        	ipp-2-2          -0
+  YYYY  pedestal       	ipp-2-2          -0
+  YYYY  dvoTools       	ipp-2-2          -0
+  YYYY  pswarp         	ipp-2-2          -0
+  YYYY  ppStack        	ipp-2-2          -0
+  YYYY  ppSub          	ipp-2-2          -0
+  YYYY  ppSim        	ipp-2-2          -0
+
+  YYYY  glueforge      	ipp-2-2          -0
+  YYYY  dbconfig       	ipp-2-2          -0
+  NYNN  ippdb.src      			      
+  YYYY  ippTools       	ipp-2-2          -0
+  YYYY  ippScripts     	ipp-2-2          -0
+  YYYY  ippTasks       	ipp-2-2          -0
+
+  YYYY  ippconfig      	ipp-2-2          -0
+  YYYY  simtest        	ipp-2-2          -0
+  YNYY  psconfig       	ipp-2-2          -0
+  YNYY  ippMonitor     	ipp-2-2          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.2.libs
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.2.libs	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.2.libs	(revision 22322)
@@ -0,0 +1,88 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      Y NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2.tar.gz      readline-5.2     Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y NONE NONE install-lib
+lib libcfitsio           NONE           NONE   cfitsio2510.tar.gz       cfitsio          Y NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     Y NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.6.tar.gz           gsl-1.6          Y NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--disable-fortran NONE NONE
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  Y NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fitsio.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/readline.h  NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.2.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.2.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.2.perl	(revision 22322)
@@ -0,0 +1,67 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.3.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.3.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.3.dist	(revision 22322)
@@ -0,0 +1,66 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	ipp-2-3  	 -0
+  NNYY  libohana       	ipp-2-3  	 -0
+  NNYY  libfits        	ipp-2-3  	 -0
+  NNYY  libautocode    	ipp-2-3  	 -0
+  NNYY  libdvo         	ipp-2-3  	 -0
+  NNYY  libkapa        	ipp-2-3  	 -0
+  NNYY  libtap.ohana   	ipp-2-3  	 -0
+  NNYY  addstar        	ipp-2-3  	 -0
+  NNYY  delstar        	ipp-2-3  	 -0
+  NNYY  getstar        	ipp-2-3  	 -0
+  NNYY  ohana.tools    	ipp-2-3  	 -0
+  NNYY  kapa2          	ipp-2-3  	 -0
+  NNYY  relphot        	ipp-2-3  	 -0
+  NNYY  relastro       	ipp-2-3  	 -0
+  NNYY  uniphot        	ipp-2-3  	 -0
+  NNYY  opihi.base     	ipp-2-3  	 -0
+  NNYY  mana           	ipp-2-3  	 -0
+  NNYY  dvo            	ipp-2-3  	 -0
+  NNYY  pantasks       	ipp-2-3  	 -0
+  NNYY  pcontrol       	ipp-2-3  	 -0
+  NNYY  pclient        	ipp-2-3  	 -0      
+
+  NYNN  Nebulous/nebclient ipp-2-3       -0
+  YYYN  Nebulous        ipp-2-3          -0
+  YYYY  PS-IPP-Metadata-Config ipp-2-3   -0
+  YYYY  PS-IPP-Config  	ipp-2-3          -0
+
+  NYYY  psLib          	ipp-2-3          -0
+  NYYY  psModules      	ipp-2-3          -0
+  YYYY  psphot         	ipp-2-3          -0
+  YYYY  psastro        	ipp-2-3          -0
+  YYYY  ppStats        	ipp-2-3          -0
+  YYYY  ppConfigDump   	ipp-2-3          -0
+  YYYY  ppImage        	ipp-2-3          -0
+  YYYY  ppNorm         	ipp-2-3          -0
+  YYYY  ppMerge        	ipp-2-3          -0
+  YYYY  pedestal       	ipp-2-3          -0
+  YYYY  dvoTools       	ipp-2-3          -0
+  YYYY  pswarp         	ipp-2-3          -0
+  YYYY  ppStack        	ipp-2-3          -0
+  YYYY  ppSub          	ipp-2-3          -0
+  YYYY  ppSim        	ipp-2-3          -0
+  YYYY  ppstamp        	ipp-2-3          -0
+
+  YYYY  glueforge      	ipp-2-3          -0
+  YYYY  dbconfig       	ipp-2-3          -0
+  NYNN  ippdb.src      			      
+  YYYY  ippTools       	ipp-2-3          -0
+  YYYY  ippScripts     	ipp-2-3          -0
+  YYYY  ippTasks       	ipp-2-3          -0
+
+  YYYY  ippconfig      	ipp-2-3          -0
+  YYYY  simtest        	ipp-2-3          -0
+  YNYY  psconfig       	ipp-2-3          -0
+  YNYY  ippMonitor     	ipp-2-3          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.3.libs
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.3.libs	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.3.libs	(revision 22322)
@@ -0,0 +1,88 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      Y NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2.tar.gz      readline-5.2     Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y NONE NONE install-lib
+lib libcfitsio           NONE           NONE   cfitsio2510.tar.gz       cfitsio          Y NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     Y NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.6.tar.gz           gsl-1.6          Y NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--disable-fortran NONE NONE
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  Y NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fitsio.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/readline.h  NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.3.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.3.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.3.perl	(revision 22322)
@@ -0,0 +1,67 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.4.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.4.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.4.dist	(revision 22322)
@@ -0,0 +1,69 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update directory from CVS?
+# |||||-- build for developer?
+# |||||
+# |||||  CVS module             CVS branch tag   CVS branch version   
+# ||||| 
+  NYNNY  Ohana
+  NNYYN  ohana.base             ipp-2-4          -0
+  NNYYN  libohana               ipp-2-4          -0
+  NNYYN  libfits                ipp-2-4          -0
+  NNYYN  libautocode            ipp-2-4          -0
+  NNYYN  libdvo                 ipp-2-4          -0
+  NNYYN  libkapa                ipp-2-4          -0
+  NNYYN  libtap.ohana           ipp-2-4          -0
+  NNYYN  addstar                ipp-2-4          -0
+  NNYYN  delstar                ipp-2-4          -0
+  NNYYN  getstar                ipp-2-4          -0
+  NNYYN  ohana.tools            ipp-2-4          -0
+  NNYYN  kapa2                  ipp-2-4          -0
+  NNYYN  relphot                ipp-2-4          -0
+  NNYYN  relastro               ipp-2-4          -0
+  NNYYN  uniphot                ipp-2-4          -0
+  NNYYN  opihi.base             ipp-2-4          -0
+  NNYYN  mana                   ipp-2-4          -0
+  NNYYN  dvo                    ipp-2-4          -0
+  NNYYN  pantasks               ipp-2-4          -0
+  NNYYN  pcontrol               ipp-2-4          -0
+  NNYYN  pclient                ipp-2-4          -0      
+          
+  NYNNY  Nebulous/nebclient     ipp-2-4          -0
+  YYYNY  Nebulous               ipp-2-4          -0
+  YYYYY  PS-IPP-Metadata-Config ipp-2-4          -0
+  YYYYY  PS-IPP-Config          ipp-2-4          -0     
+          
+  YYYYY  psLib                  ipp-2-4          -0
+  YYYYY  psModules              ipp-2-4          -0
+  YYYYY  psphot                 ipp-2-4          -0
+  YYYYY  psastro                ipp-2-4          -0
+  YYYYY  ppStats                ipp-2-4          -0
+  YYYYY  ppConfigDump           ipp-2-4          -0
+  YYYYY  ppImage                ipp-2-4          -0
+  YYYYY  ppNorm                 ipp-2-4          -0
+  YYYYY  ppMerge                ipp-2-4          -0
+  YYYYY  pedestal               ipp-2-4          -0
+  YYYYY  dvoTools               ipp-2-4          -0
+  YYYYY  pswarp                 ipp-2-4          -0
+  YYYYY  ppArith                ipp-2-4          -0
+  YYYYY  ppStack                ipp-2-4          -0
+  YYYYY  ppSub                  ipp-2-4          -0
+  YYYYY  ppSim                  ipp-2-4          -0
+  YNYYN  pstamp                 ipp-2-4          -0
+          
+  YNNYY  glueforge              ipp-2-4          -0
+  YNNYY  dbconfig               ipp-2-4          -0
+  NNNNY  ippdb.src             
+  NYYNN  ippdb                  ipp-2-4          -0
+  YYYYY  ippTools               ipp-2-4          -0
+  YYYYY  ippScripts             ipp-2-4          -0
+  YYYYY  ippTasks               ipp-2-4          -0
+          
+  YYYYY  ippconfig              ipp-2-4          -0
+  YYYYY  simtest                ipp-2-4          -0
+  YNYYN  psconfig               ipp-2-4          -0
+  YNYYN  ippMonitor             ipp-2-4          -0
+
+# there are externally required C libraries and perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.4.libs
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.4.libs	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.4.libs	(revision 22322)
@@ -0,0 +1,91 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      Y NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2.tar.gz      readline-5.2     Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y --enable-shared NONE install-lib
+# lib libcfitsio         NONE           NONE   cfitsio2510.tar.gz       cfitsio          Y NONE NONE NONE
+lib libcfitsio           NONE           NONE   cfitsio3060.tar.gz       cfitsio          Y NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     Y NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.6.tar.gz           gsl-1.6          Y NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--disable-fortran NONE NONE
+# lib libfftw3             NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-shared,--disable-fortran NONE NONE
+# paul claims we are not currently using double-point FFTs anywhere
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  Y NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fitsio.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/readline.h  NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.4.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.4.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.4.perl	(revision 22322)
@@ -0,0 +1,79 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
+  64    Astro::FITS::CFITSIO           Astro-FITS-CFITSIO-1.05.tar.gz           0
+  65    Test::More    		       Test-Simple-0.74.tar.gz                  0.49
+  66    Apache::DBI   		       Apache-DBI-1.06.tar.gz                   0
+  67    Apache2::SOAP 		       Apache2-SOAP-0.72.tar.gz                 0
+  68    Test::URI     		       Test-URI-1.08.tar.gz                     0
+  69    Sys::Statistics::Linux::DiskUsage Sys-Statistics-Linux-0.26.tar.gz      0
+  70    Config::YAML  	       	       Config-YAML-1.42.tar.gz                  0
+  72    File::ExtAttr 	       	       File-ExtAttr-1.07.tar.gz                 0
+  73    DBI           	       	       DBI-1.601.tar.gz                         0
+  71    DBD::mysql    	       	       DBD-mysql-4.006.tar.gz                   0
+  74    Net::Server::Daemonize 	       Net-Server-0.97.tar.gz                   0.05
+  75    File::Path                     File-Path-2.04.tar.gz
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.5.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.5.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.5.dist	(revision 22322)
@@ -0,0 +1,71 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update directory from CVS?
+# |||||-- build for developer?
+# |||||
+# |||||  CVS module             CVS branch tag   CVS branch version   
+# ||||| 
+  NYNNY  Ohana
+  NNYYN  ohana.base             ipp-2-5          -0
+  NNYYN  libohana               ipp-2-5          -0
+  NNYYN  libfits                ipp-2-5          -0
+  NNYYN  libautocode            ipp-2-5          -0
+  NNYYN  libdvo                 ipp-2-5          -1
+  NNYYN  libkapa                ipp-2-5          -0
+  NNYYN  libtap.ohana           ipp-2-5          -0
+  NNYYN  addstar                ipp-2-5          -1
+  NNYYN  delstar                ipp-2-5          -0
+  NNYYN  getstar                ipp-2-5          -0
+  NNYYN  ohana.tools            ipp-2-5          -0
+  NNYYN  kapa2                  ipp-2-5          -1
+  NNYYN  relphot                ipp-2-5          -1
+  NNYYN  relastro               ipp-2-5          -1
+  NNYYN  uniphot                ipp-2-5          -0
+  NNYYN  opihi.base             ipp-2-5          -1
+  NNYYN  mana                   ipp-2-5          -1
+  NNYYN  dvo                    ipp-2-5          -1
+  NNYYN  pantasks               ipp-2-5          -1
+  NNYYN  pcontrol               ipp-2-5          -1
+  NNYYN  pclient                ipp-2-5          -1      
+          
+  YYYYY  nebclient              ipp-2-5          -0
+  YYYNY  Nebulous               ipp-2-5          -0
+  YYYNY  Nebulous-Server        ipp-2-5          -0
+  YYYNY  DataStore        	ipp-2-5          -0
+  YYYYY  PS-IPP-Metadata-Config ipp-2-5          -0
+  YYYYY  PS-IPP-Config          ipp-2-5          -0     
+          
+  YYYYY  psLib                  ipp-2-5          -0
+  YYYYY  psModules              ipp-2-5          -0
+  YYYYY  psphot                 ipp-2-5          -0
+  YYYYY  psastro                ipp-2-5          -0
+  YYYYY  ppStats                ipp-2-5          -0
+  YYYYY  ppConfigDump           ipp-2-5          -0
+  YYYYY  ppImage                ipp-2-5          -0
+  YYYYY  ppNorm                 ipp-2-5          -0
+  YYYYY  ppMerge                ipp-2-5          -0
+  YYYYY  pedestal               ipp-2-5          -0
+  YYYYY  dvoTools               ipp-2-5          -0
+  YYYYY  pswarp                 ipp-2-5          -0
+  YYYYY  ppArith                ipp-2-5          -0
+  YYYYY  ppStack                ipp-2-5          -0
+  YYYYY  ppSub                  ipp-2-5          -0
+  YYYYY  ppSim                  ipp-2-5          -0
+          
+  YNNYY  glueforge              ipp-2-5          -0
+  YNNYY  dbconfig               ipp-2-5          -0
+  NNNNY  ippdb.src             
+  YYYNN  ippdb                  ipp-2-5          -1
+  YYYYY  pstamp                 ipp-2-5          -0
+  YYYYY  ippTools               ipp-2-5          -0
+  YYYYY  ippScripts             ipp-2-5          -0
+  YYYYY  ippTasks               ipp-2-5          -0
+          
+  YYYYY  ippconfig              ipp-2-5          -0
+  YNYYN  psconfig               ipp-2-5          -0
+  YNYYN  ippMonitor             ipp-2-5          -0
+  YYYYY  DataStore              ipp-2-5          -0
+
+# there are externally required C libraries and perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.5.libs
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.5.libs	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.5.libs	(revision 22322)
@@ -0,0 +1,92 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      Y NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2.tar.gz      readline-5.2     Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y --enable-shared NONE install-lib
+# lib libcfitsio         NONE           NONE   cfitsio2510.tar.gz       cfitsio          Y NONE NONE NONE
+lib libcfitsio           NONE           NONE   cfitsio3060.tar.gz       cfitsio          Y NONE NONE NONE
+#lib libmysqlclient      NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     Y NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.51a.tar.gz     mysql-5.0.51a    Y NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         Y NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--disable-fortran NONE NONE
+#lib libfftw3            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-shared,--disable-fortran NONE NONE
+# paul claims we are not currently using double-point FFTs anywhere
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  Y NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fitsio.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/readline.h  NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.5.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.5.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.5.perl	(revision 22322)
@@ -0,0 +1,79 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+#  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
+  64    Astro::FITS::CFITSIO           Astro-FITS-CFITSIO-1.05.tar.gz           0
+  65    Test::More    		       Test-Simple-0.74.tar.gz                  0.49
+#  66    Apache::DBI   		       Apache-DBI-1.06.tar.gz                   0
+#  67    Apache2::SOAP 		       Apache2-SOAP-0.72.tar.gz                 0
+  68    Test::URI     		       Test-URI-1.08.tar.gz                     0
+#  69    Sys::Statistics::Linux::DiskUsage Sys-Statistics-Linux-0.26.tar.gz      0
+#  70    Config::YAML  	       	       Config-YAML-1.42.tar.gz                  0
+#  72    File::ExtAttr 	       	       File-ExtAttr-1.07.tar.gz                 0
+  73    DBI           	       	       DBI-1.601.tar.gz                         0
+  71    DBD::mysql    	       	       DBD-mysql-4.006.tar.gz                   0
+#  74    Net::Server::Daemonize 	       Net-Server-0.97.tar.gz                   0.05
+  75    File::Path                     File-Path-2.04.tar.gz
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.1.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.1.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.1.dist	(revision 22322)
@@ -0,0 +1,69 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update directory from CVS?
+# |||||-- build for developer?
+# |||||
+# |||||  CVS module             CVS branch tag   CVS branch version   
+# ||||| 
+  YYNNY  Ohana                  ipp-2-6-1          -0
+  NNYYN  ohana.base             ipp-2-6-1          -0
+  NNYYN  libohana               ipp-2-6-1          -0
+  NNYYN  libfits                ipp-2-6-1          -0
+  NNYYN  libautocode            ipp-2-6-1          -0
+  NNYYN  libdvo                 ipp-2-6-1          -0
+  NNYYN  libkapa                ipp-2-6-1          -0
+  NNYYN  libtap.ohana           ipp-2-6-1          -0
+  NNYYN  addstar                ipp-2-6-1          -0
+  NNYYN  delstar                ipp-2-6-1          -0
+  NNYYN  getstar                ipp-2-6-1          -0
+  NNYYN  ohana.tools            ipp-2-6-1          -0
+  NNYYN  kapa2                  ipp-2-6-1          -0
+  NNYYN  relphot                ipp-2-6-1          -0
+  NNYYN  relastro               ipp-2-6-1          -0
+  NNYYN  uniphot                ipp-2-6-1          -0
+  NNYYN  opihi.base             ipp-2-6-1          -0
+  NNYYN  mana                   ipp-2-6-1          -0
+  NNYYN  dvo                    ipp-2-6-1          -0
+  NNYYN  pantasks               ipp-2-6-1          -0
+  NNYYN  pcontrol               ipp-2-6-1          -0
+  NNYYN  pclient                ipp-2-6-1          -0      
+          
+  YNNYY  nebclient              ipp-2-6-1          -0
+  YNNNY  Nebulous               ipp-2-6-1          -0
+  YYYYY  PS-IPP-Metadata-Config ipp-2-6-1          -0
+  YYYYY  PS-IPP-Config          ipp-2-6-1          -0     
+          
+  YYYYY  psLib                  ipp-2-6-1          -0
+  YYYYY  psModules              ipp-2-6-1          -0
+  YYYYY  ppStats                ipp-2-6-1          -0
+  YYYYY  psphot                 ipp-2-6-1          -0
+  YYYYY  psastro                ipp-2-6-1          -0
+  YYYYY  ppConfigDump           ipp-2-6-1          -0
+  YYYYY  ppImage                ipp-2-6-1          -0
+  YYYYY  ppNorm                 ipp-2-6-1          -0
+  YYYYY  ppMerge                ipp-2-6-1          -0
+  YNNYN  pedestal               ipp-2-6-1          -0
+  YYYYY  dvoTools               ipp-2-6-1          -0
+  YYYYY  pswarp                 ipp-2-6-1          -0
+  YYYYY  ppArith                ipp-2-6-1          -0
+  YYYYY  ppStack                ipp-2-6-1          -0
+  YYYYY  ppSub                  ipp-2-6-1          -0
+  YYYYY  ppSim                  ipp-2-6-1          -0
+          
+  YNNYY  glueforge              ipp-2-6-1          -0
+  YNNYY  dbconfig               ipp-2-6-1          -0
+  NNNNY  ippdb.src             
+  YYYNN  ippdb                  ipp-2-6-1          -0
+  YYYYY  pstamp                 ipp-2-6-1          -0
+  YYYYY  ippTools               ipp-2-6-1          -0
+  YYYYY  ippScripts             ipp-2-6-1          -0
+  YYYYY  ippTasks               ipp-2-6-1          -0
+          
+  YYYYY  ippconfig              ipp-2-6-1          -0
+  YNYYN  psconfig               ipp-2-6-1          -0
+  YNYYN  ippMonitor             ipp-2-6-1          -0
+  YYYYY  DataStore              ipp-2-6-1          -0
+
+# there are externally required C libraries and perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.1.libs
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.1.libs	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.1.libs	(revision 22322)
@@ -0,0 +1,93 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      Y NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2-p12.tar.gz  readline-5.2-p12 Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y --enable-shared NONE install-lib
+lib libcfitsio           NONE           NONE   cfitsio3090.tar.gz       cfitsio          Y NONE NONE NONE
+#lib libmysqlclient      NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     Y NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.51a.tar.gz     mysql-5.0.51a    Y NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         Y NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--enable-threads,--disable-fortran NONE NONE
+#lib libfftw3            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-shared,--disable-fortran NONE NONE
+# paul claims we are not currently using double-point FFTs anywhere
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  Y NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fftw3.h              NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--disable-fortran NONE NONE
+inc fitsio.h             NONE           NONE   cfitsio3090.tar.gz       cfitsio          Y NONE NONE NONE
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         Y NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         Y NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y --enable-shared NONE install-lib
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc mysql.h              NONE           mysql  mysql-5.0.51a.tar.gz     mysql-5.0.51a    Y NONE NONE NONE
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   readline-5.2-p12.tar.gz  readline-5.2-p12 Y NONE NONE NONE
+inc readline/readline.h  NONE           NONE   readline-5.2-p12.tar.gz  readline-5.2-p12 Y NONE NONE NONE
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.1.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.1.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.1.perl	(revision 22322)
@@ -0,0 +1,79 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+#  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
+  64    Astro::FITS::CFITSIO           Astro-FITS-CFITSIO-1.05.tar.gz           0
+  65    Test::More    		       Test-Simple-0.74.tar.gz                  0.49
+#  66    Apache::DBI   		       Apache-DBI-1.06.tar.gz                   0
+#  67    Apache2::SOAP 		       Apache2-SOAP-0.72.tar.gz                 0
+  68    Test::URI     		       Test-URI-1.08.tar.gz                     0
+#  69    Sys::Statistics::Linux::DiskUsage Sys-Statistics-Linux-0.26.tar.gz      0
+#  70    Config::YAML  	       	       Config-YAML-1.42.tar.gz                  0
+#  72    File::ExtAttr 	       	       File-ExtAttr-1.07.tar.gz                 0
+  73    DBI           	       	       DBI-1.601.tar.gz                         0
+  71    DBD::mysql    	       	       DBD-mysql-4.006.tar.gz                   0
+#  74    Net::Server::Daemonize 	       Net-Server-0.97.tar.gz                   0.05
+  75    File::Path                     File-Path-2.04.tar.gz
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.dist	(revision 22322)
@@ -0,0 +1,69 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update directory from CVS?
+# |||||-- build for developer?
+# |||||
+# |||||  CVS module             CVS branch tag   CVS branch version   
+# ||||| 
+  YYNNY  Ohana                  ipp-2-6          -0
+  NNYYN  ohana.base             ipp-2-6          -0
+  NNYYN  libohana               ipp-2-6          -0
+  NNYYN  libfits                ipp-2-6          -0
+  NNYYN  libautocode            ipp-2-6          -0
+  NNYYN  libdvo                 ipp-2-6          -0
+  NNYYN  libkapa                ipp-2-6          -0
+  NNYYN  libtap.ohana           ipp-2-6          -0
+  NNYYN  addstar                ipp-2-6          -0
+  NNYYN  delstar                ipp-2-6          -0
+  NNYYN  getstar                ipp-2-6          -0
+  NNYYN  ohana.tools            ipp-2-6          -0
+  NNYYN  kapa2                  ipp-2-6          -0
+  NNYYN  relphot                ipp-2-6          -0
+  NNYYN  relastro               ipp-2-6          -0
+  NNYYN  uniphot                ipp-2-6          -0
+  NNYYN  opihi.base             ipp-2-6          -0
+  NNYYN  mana                   ipp-2-6          -0
+  NNYYN  dvo                    ipp-2-6          -0
+  NNYYN  pantasks               ipp-2-6          -0
+  NNYYN  pcontrol               ipp-2-6          -0
+  NNYYN  pclient                ipp-2-6          -0      
+          
+  YYYYY  nebclient              ipp-2-6          -0
+  YYYNY  Nebulous               ipp-2-6          -0
+  YYYYY  PS-IPP-Metadata-Config ipp-2-6          -0
+  YYYYY  PS-IPP-Config          ipp-2-6          -0     
+          
+  YYYYY  psLib                  ipp-2-6          -0
+  YYYYY  psModules              ipp-2-6          -0
+  YYYYY  ppStats                ipp-2-6          -0
+  YYYYY  psphot                 ipp-2-6          -0
+  YYYYY  psastro                ipp-2-6          -0
+  YYYYY  ppConfigDump           ipp-2-6          -0
+  YYYYY  ppImage                ipp-2-6          -0
+  YYYYY  ppNorm                 ipp-2-6          -0
+  YYYYY  ppMerge                ipp-2-6          -0
+  YYYYY  pedestal               ipp-2-6          -0
+  YYYYY  dvoTools               ipp-2-6          -0
+  YYYYY  pswarp                 ipp-2-6          -0
+  YYYYY  ppArith                ipp-2-6          -0
+  YYYYY  ppStack                ipp-2-6          -0
+  YYYYY  ppSub                  ipp-2-6          -0
+  YYYYY  ppSim                  ipp-2-6          -0
+          
+  YNNYY  glueforge              ipp-2-6          -0
+  YNNYY  dbconfig               ipp-2-6          -0
+  NNNNY  ippdb.src             
+  YYYNN  ippdb                  ipp-2-6          -0
+  YYYYY  pstamp                 ipp-2-6          -0
+  YYYYY  ippTools               ipp-2-6          -0
+  YYYYY  ippScripts             ipp-2-6          -0
+  YYYYY  ippTasks               ipp-2-6          -0
+          
+  YYYYY  ippconfig              ipp-2-6          -0
+  YNYYN  psconfig               ipp-2-6          -0
+  YNYYN  ippMonitor             ipp-2-6          -0
+  YYYYY  DataStore              ipp-2-6          -0
+
+# there are externally required C libraries and perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.libs
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.libs	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.libs	(revision 22322)
@@ -0,0 +1,91 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      Y NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2-p12.tar.gz  readline-5.2-p12 Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y --enable-shared NONE install-lib
+lib libcfitsio           NONE           NONE   cfitsio3090.tar.gz       cfitsio          Y NONE NONE NONE
+#lib libmysqlclient      NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     Y NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.51a.tar.gz     mysql-5.0.51a    Y NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         Y NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--enable-threads,--disable-fortran NONE NONE
+#lib libfftw3            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-shared,--disable-fortran NONE NONE
+# paul claims we are not currently using double-point FFTs anywhere
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  Y NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fitsio.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/readline.h  NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.6.perl	(revision 22322)
@@ -0,0 +1,79 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+#  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
+  64    Astro::FITS::CFITSIO           Astro-FITS-CFITSIO-1.05.tar.gz           0
+  65    Test::More    		       Test-Simple-0.74.tar.gz                  0.49
+#  66    Apache::DBI   		       Apache-DBI-1.06.tar.gz                   0
+#  67    Apache2::SOAP 		       Apache2-SOAP-0.72.tar.gz                 0
+  68    Test::URI     		       Test-URI-1.08.tar.gz                     0
+#  69    Sys::Statistics::Linux::DiskUsage Sys-Statistics-Linux-0.26.tar.gz      0
+#  70    Config::YAML  	       	       Config-YAML-1.42.tar.gz                  0
+#  72    File::ExtAttr 	       	       File-ExtAttr-1.07.tar.gz                 0
+  73    DBI           	       	       DBI-1.601.tar.gz                         0
+  71    DBD::mysql    	       	       DBD-mysql-4.006.tar.gz                   0
+#  74    Net::Server::Daemonize 	       Net-Server-0.97.tar.gz                   0.05
+  75    File::Path                     File-Path-2.04.tar.gz
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.7.dist
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.7.dist	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.7.dist	(revision 22322)
@@ -0,0 +1,70 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update directory from CVS?
+# |||||-- build for developer?
+# |||||
+# |||||  CVS module             CVS branch tag   CVS branch version   
+# ||||| 
+  YYNNY  Ohana                  ipp-2-7          -0
+  NNYYN  ohana.base             ipp-2-7          -0
+  NNYYN  libohana               ipp-2-7          -0
+  NNYYN  libfits                ipp-2-7          -0
+  NNYYN  libautocode            ipp-2-7          -0
+  NNYYN  libdvo                 ipp-2-7          -0
+  NNYYN  libkapa                ipp-2-7          -0
+  NNYYN  libtap.ohana           ipp-2-7          -0
+  NNYYN  addstar                ipp-2-7          -0
+  NNYYN  delstar                ipp-2-7          -0
+  NNYYN  getstar                ipp-2-7          -0
+  NNYYN  ohana.tools            ipp-2-7          -0
+  NNYYN  kapa2                  ipp-2-7          -0
+  NNYYN  relphot                ipp-2-7          -0
+  NNYYN  relastro               ipp-2-7          -0
+  NNYYN  uniphot                ipp-2-7          -0
+  NNYYN  opihi.base             ipp-2-7          -0
+  NNYYN  mana                   ipp-2-7          -0
+  NNYYN  dvo                    ipp-2-7          -0
+  NNYYN  pantasks               ipp-2-7          -0
+  NNYYN  pcontrol               ipp-2-7          -0
+  NNYYN  pclient                ipp-2-7          -0      
+          
+  YNNYY  nebclient              ipp-2-7          -0
+  YNNNN  Nebulous               ipp-2-7          -0
+  YYYYY  PS-IPP-Metadata-Config ipp-2-7          -0
+  YYYYY  PS-IPP-Config          ipp-2-7          -0     
+          
+  YYYYY  psLib                  ipp-2-7          -0
+  YYYYY  psModules              ipp-2-7          -0
+  YYYYY  ppStats                ipp-2-7          -0
+  YYYYY  psphot                 ipp-2-7          -0
+  YYYYY  psastro                ipp-2-7          -0
+  YYYYY  ppConfigDump           ipp-2-7          -0
+  YYYYY  ppImage                ipp-2-7          -0
+  YYYYY  ppNorm                 ipp-2-7          -0
+  YYYYY  ppMerge                ipp-2-7          -0
+  YNNYN  pedestal               ipp-2-7          -0
+  YYYYY  dvoTools               ipp-2-7          -0
+  YYYYY  pswarp                 ipp-2-7          -0
+  YYYYY  ppArith                ipp-2-7          -0
+  YYYYY  ppStack                ipp-2-7          -0
+  YYYYY  ppSub                  ipp-2-7          -0
+  YYYYY  ppSim                  ipp-2-7          -0
+          
+  YNNYY  glueforge              ipp-2-7          -0
+  YNNYY  dbconfig               ipp-2-7          -0
+  NNNNY  ippdb.src             
+  YYYNN  ippdb                  ipp-2-7          -0
+  YYYYY  PStamp                 ipp-2-7          -0
+  YYYYY  pstamp                 ipp-2-7          -0
+  YYYYY  ippTools               ipp-2-7          -0
+  YYYYY  ippScripts             ipp-2-7          -0
+  YYYYY  ippTasks               ipp-2-7          -0
+          
+  YYYYY  ippconfig              ipp-2-7          -0
+  YNYYN  psconfig               ipp-2-7          -0
+  YNYYN  ippMonitor             ipp-2-7          -0
+  YYYYY  DataStore              ipp-2-7          -0
+
+# there are externally required C libraries and perl modules (see INSTALL)
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.7.libs
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.7.libs	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.7.libs	(revision 22322)
@@ -0,0 +1,94 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   force install by default?
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             N NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             N NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             N NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      N NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2-p12.tar.gz  readline-5.2-p12 Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       N --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    N NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b-p1.tgz       jpeg-6b          N --enable-shared NONE install-lib
+lib libcfitsio           NONE           NONE   cfitsio3100.tar.gz       cfitsio          N NONE NONE NONE
+#lib libmysqlclient      NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     N NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.51a.tar.gz     mysql-5.0.51a    N NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         N NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       N --enable-float,--enable-shared,--disable-fortran NONE NONE
+#lib libfftw3            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       N --enable-shared,--disable-fortran NONE NONE
+# paul claims we are not currently using double-point FFTs anywhere
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  N NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fftw3.h              NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       N --enable-float,--enable-shared,--disable-fortran NONE NONE
+inc fitsio.h             NONE           NONE   cfitsio3090.tar.gz       cfitsio          N NONE NONE NONE
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          N --enable-shared NONE install-lib
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc mysql.h              NONE           mysql  mysql-5.0.51a.tar.gz     mysql-5.0.51a    N NONE NONE NONE
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    N NONE NONE NONE
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   readline-5.2-p12.tar.gz  readline-5.2-p12 N NONE NONE NONE
+inc readline/readline.h  NONE           NONE   readline-5.2-p12.tar.gz  readline-5.2-p12 N NONE NONE NONE
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       N --shared NONE NONE
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.7.perl
===================================================================
--- /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.7.perl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psconfig/tagsets/ipp-2.7.perl	(revision 22322)
@@ -0,0 +1,79 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+#  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
+  64    Astro::FITS::CFITSIO           Astro-FITS-CFITSIO-1.05.tar.gz           0
+  65    Test::More    		       Test-Simple-0.74.tar.gz                  0.49
+#  66    Apache::DBI   		       Apache-DBI-1.06.tar.gz                   0
+#  67    Apache2::SOAP 		       Apache2-SOAP-0.72.tar.gz                 0
+  68    Test::URI     		       Test-URI-1.08.tar.gz                     0
+#  69    Sys::Statistics::Linux::DiskUsage Sys-Statistics-Linux-0.26.tar.gz      0
+#  70    Config::YAML  	       	       Config-YAML-1.42.tar.gz                  0
+#  72    File::ExtAttr 	       	       File-ExtAttr-1.07.tar.gz                 0
+  73    DBI           	       	       DBI-1.601.tar.gz                         0
+  71    DBD::mysql    	       	       DBD-mysql-4.006.tar.gz                   0
+#  74    Net::Server::Daemonize 	       Net-Server-0.97.tar.gz                   0.05
+  75    File::Path                     File-Path-2.04.tar.gz
Index: /tags/sj_tags/sj_root_20080929/psdemo/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psdemo/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psdemo/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+bin
Index: /tags/sj_tags/sj_root_20080929/psdemo/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/psdemo/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psdemo/Makefile	(revision 22322)
@@ -0,0 +1,64 @@
+default: psdemo
+help:
+	@echo "USAGE: make psdemo"
+
+CC      =       gcc
+SRC     =       src
+BIN     =       bin
+
+DESTBIN =       $(HOME)/src/bin/$(ARCH)
+
+LPSLIB  :=      $(shell pslib-config --libs)
+IPSLIB  :=      $(shell pslib-config --cflags)
+
+INCS	= 	$(IPSLIB)
+LIBS	= 	$(LPSLIB)
+CFLAGS	=	$(INCS) -std=c99 -Wall -Werror -g
+LFLAGS	=	$(LIBS) 
+
+PSDEMO = \
+$(SRC)/psdemo.$(ARCH).o            \
+$(SRC)/psdemoArgs.$(ARCH).o
+
+psdemo: $(BIN)/psdemo.$(ARCH)
+$(BIN)/psdemo.$(ARCH) : $(PSDEMO)
+$(PSDEMO): $(SRC)/psdemo.h
+
+INSTALL = psdemo
+
+# dependancy rules for binary code #########################
+.PRECIOUS: %.$(ARCH).o
+.PRECIOUS: $(BIN)/%.$(ARCH)
+
+%.$(ARCH).o : %.c
+	$(CC) $(CFLAGS) -c $< -o $@
+
+$(BIN)/%.$(ARCH) : $(SRC)/%.$(ARCH).o
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	$(CC) $^ -o $@ $(LFLAGS)
+	@echo "done with $@"
+
+$(DESTBIN)/%: $(BIN)/%.$(ARCH)
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	rm -f $(DESTBIN)/$*
+	cp $(BIN)/$*.$(ARCH) $(DESTBIN)/$*
+
+$(INSTALL): % : $(BIN)/%.$(ARCH)
+
+%.clean :
+	rm -f $(BIN)/$*.$(ARCH)
+	rm -f $(SRC)/*.$(ARCH).o
+
+%.install:
+	make $(DESTBIN)/$*
+
+# utilities #################################################
+
+install:
+	for i in $(INSTALL); do make $$i.install; done
+
+clean:	
+	rm -f $(BIN)/*.$(ARCH)
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
Index: /tags/sj_tags/sj_root_20080929/psdemo/simdata/images.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/psdemo/simdata/images.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psdemo/simdata/images.pro	(revision 22322)
@@ -0,0 +1,210 @@
+
+# set global parameters for the simulations
+macro mkimages.init
+ if ($0 != 7)
+  echo "USAGE: mkimage.empty (Nx) (Ny) (RDNOISE) (SKY) (Nstars) (psfSigma)"
+  break
+ end
+
+ $mkiNx = $1
+ $mkiNy = $2
+ $mkiRD = $3
+ $mkiSKY = $4
+ $mkiPSFsigma = $6
+
+ gaussdev mki_noisev {$mkiNx*$mkiNy} 0.0 1.0
+ dimenup mki_noisev mki_noisei $mkiNx $mkiNy
+ mcreate blank $mkiNx $mkiNy
+end
+
+# generate a fake image with bias and sky, no stars
+macro mkimage.empty
+ if ($0 != 2)
+  echo "USAGE: mkimage.empty (buffer)"
+  break
+ end
+
+ set $1 = blank + $mkiRD*mki_noisei + sqrt($mkiSKY)*mki_noisei + $mkiSKY
+end
+
+# set global parameters for the simulations
+macro mkimages.stars
+ if ($0 != 7)
+  echo "USAGE: mkimage.empty (Nx) (Ny) (RDNOISE) (SKY) (Nstars) (psfSigma)"
+  break
+ end
+
+ $mkiNx = $1
+ $mkiNy = $2
+ $mkiRD = $3
+ $mkiSKY = $4
+ $mkiPSFsigma = $6
+
+ gaussdev mki_noisev {$mkiNx*$mkiNy} 0.0 1.0
+ dimenup mki_noisev mki_noisei $mkiNx $mkiNy
+ mcreate blank $mkiNx $mkiNy
+
+ # mkstars $5 0.6
+ mkstars $5 0.4
+ # mkstars $5 0.3
+end
+
+# generate a fake image with bias, sky, stars, gaussian PFS
+macro mkimage.stars.gauss
+ if ($0 != 2)
+  echo "USAGE: mkimage.stars (buffer)"
+  break
+ end
+
+ set _tmp.stars = mki_stars_image
+
+ mkimage.smooth _tmp.stars _tmp.smooth $mkiPSFsigma mkgauss
+
+ # noise sigma = sqrt (_tmp.smooth + $mkiRD^2 + $mkiSKY)
+ set $1 = _tmp.smooth + $mkiSKY + sqrt(_tmp.smooth + $mkiSKY + $mkiRD^2)*mki_noisei
+end
+
+# generate a fake image with bias, sky, stars, gaussian PFS
+macro mkimage.stars.power
+ if ($0 != 2)
+  echo "USAGE: mkimage.stars (buffer)"
+  break
+ end
+
+ set _tmp.stars = mki_stars_image
+
+ mkimage.smooth _tmp.stars _tmp.smooth $mkiPSFsigma mkpower
+
+ # noise sigma = sqrt (_tmp.smooth + $mkiRD^2 + $mkiSKY)
+ set $1 = _tmp.smooth + $mkiSKY + sqrt(_tmp.smooth + $mkiSKY + $mkiRD^2)*mki_noisei
+end
+
+# generate a fake image with bias, sky, stars, gaussian PFS
+macro mkimage.stars.wing
+ if ($0 != 2)
+  echo "USAGE: mkimage.stars (buffer)"
+  break
+ end
+
+ set _tmp.stars = mki_stars_image
+
+ mkimage.smooth _tmp.stars _tmp.smooth $mkiPSFsigma mkwing
+
+ # noise sigma = sqrt (_tmp.smooth + $mkiRD^2 + $mkiSKY)
+ set $1 = _tmp.smooth + $mkiSKY + sqrt(_tmp.smooth + $mkiSKY + $mkiRD^2)*mki_noisei
+end
+
+# generate the vectors of star coordinates, fluxes and magnitudes
+macro mkstars
+ if ($0 != 3)
+   echo "USAGE: mkstars (Nstars) (alpha)"
+   break
+ end
+
+ local sigma alpha Nstars
+ $Nstars = $1
+ $alpha = $2
+ # alpha is dlogN/dmag = 0.6 for N ~ S^-3/2
+
+ # $sigma is flux of star with S/N = 1 (sigma_sky * 4pi sigma_psf^2)
+ $sigma = sqrt($mkiRD^2 + $mkiSKY) * 4*3.1416*$mkiPSFsigma^2
+ 
+ # faintest magnitudes:
+ # include stars down to 1 sigma
+ $M1 = -2.5*log($sigma)
+
+ # brightest magnitude in sim
+ $M0 = $M1 - log($alpha*$Nstars/0.43)/$alpha
+
+ create _tmp 0 $Nstars
+ set mki_stars_x = $mkiNx * rnd(_tmp)
+ set mki_stars_y = $mkiNy * rnd(_tmp)
+
+ # random variable between 0 and Nstars
+ set _tmpvar = $Nstars * rnd(_tmp)
+
+ # random mags drawn from random variable
+ set mki_stars_mag = $M0 + log(_tmpvar*$alpha/0.43)/$alpha
+
+ set mki_stars_flux = ten(-0.4*mki_stars_mag)
+ sort mki_stars_mag mki_stars_flux mki_stars_x mki_stars_y
+
+ # insert the stars
+ mcreate mki_stars_image $mkiNx $mkiNy
+ for i 0 $Nstars
+   zap mki_stars_image mki_stars_x[$i] mki_stars_y[$i] 1 1 -v mki_stars_flux[$i]
+ end
+end
+
+####
+macro mkimage.smooth
+ if ($0 != 5)
+   echo "USAGE: smooth (in) (out) (sigma) (func)"
+   break
+ end
+ fft2d $1 0 to sm_I_r sm_I_i
+ set sm_g = zero($1)
+ keyword sm_g NAXIS1 SMnx
+ keyword sm_g NAXIS2 SMny
+ $4 sm_g $3 -c 0 0
+ $4 sm_g $3 -c $SMnx 0
+ $4 sm_g $3 -c 0 $SMny
+ $4 sm_g $3 -c $SMnx $SMny
+ stats -q sm_g - - - -
+ set sm_g = sm_g/$TOTAL
+ fft2d sm_g 0 to sm_G_r sm_G_i
+ set sm_r = sm_G_r*sm_I_r - sm_G_i*sm_I_i
+ set sm_i = sm_G_r*sm_I_i + sm_G_i*sm_I_r
+ fft2d -inverse sm_r sm_i to sm_I_r sm_I_i
+
+ set $2 = sm_I_r*$SMnx*$SMny*$SMnx*$SMny
+
+ if (1)
+  delete sm_I_r 
+  delete sm_I_i
+  delete sm_G_r 
+  delete sm_G_i
+  delete sm_r
+  delete sm_i
+  delete sm_g
+ end
+end
+
+macro mkpower
+ if ($0 != 6)
+  echo "USAGE: mkpower (buffer) (sigma) -c X Y"
+  break
+ end
+
+ delete -q tmp
+
+ keyword $1 NAXIS1 tmpnx
+ keyword $1 NAXIS2 tmpny
+ mcreate tmp $tmpnx $tmpny
+ set tmpx = xramp(tmp) - $4
+ set tmpy = yramp(tmp) - $5
+
+# $sigma_y = $2 * $AxisRatio
+# $sigma_xy = dsin(2*$Theta)*(
+
+ set tmpz = tmpx^2 / (2 * $2^2) + tmpy^2 / (2 * $2^2)
+ set $1 = $1 + 1 / (1 + tmpz + tmpz^2.5)
+end
+
+macro mkwing
+ if ($0 != 6)
+  echo "USAGE: mkpower (buffer) (sigma) -c X Y"
+  break
+ end
+
+ delete -q tmp
+
+ keyword $1 NAXIS1 tmpnx
+ keyword $1 NAXIS2 tmpny
+ mcreate tmp $tmpnx $tmpny
+ set tmpx = xramp(tmp) - $4
+ set tmpy = yramp(tmp) - $5
+
+ set tmpz = tmpx^2 / (2 * $2^2) + tmpy^2 / (2 * $2^2)
+ set $1 = $1 + 1 / (1 + tmpz + tmpz^2)
+end
Index: /tags/sj_tags/sj_root_20080929/psdemo/simdata/sim.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/psdemo/simdata/sim.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psdemo/simdata/sim.pro	(revision 22322)
@@ -0,0 +1,214 @@
+
+#list MODULES -add /home/kiawe/eugene/src/panstarrs/ipp/ippTasks
+
+module images.pro
+
+$flatlevel = 10000
+$biaslevel = 500
+$skyrate  = 200
+$darkrate = 100
+
+macro cleanup
+  exec rm -f *.fit
+  exec rm -f *.jpg
+  exec rm -f *.fits
+  exec rm -rf logs
+end
+
+$simdir = `ipp_datapath.pl path://SIMTEST`
+
+macro init
+  exec pxadmin -delete -dbname simtest
+  exec pxadmin -create -dbname simtest
+  exec mkdir -p $simdir/raw
+  exec mkdir -p $simdir/proc
+  mkbiases
+  mkdarks
+  mkflats
+  mkobjects
+  exec ipp_serial_inject.pl $simdir/raw/*.fits --workdir path://SIMTEST/proc --dbname simtest
+end
+
+### create a bias detrun:
+# exec dettool -pretend -definebyquery -inst SIMPLE -filter NONE -det_type bias -select_exp_type bias -workdir path://SIMTEST/mkbias.0 -dbname simtest
+
+### If something goes wrong:
+# exec dettool -updatedetrun -det_id 1 -state stop -dbname simtest
+
+### create a dark detrun:
+# exec dettool -pretend -definebyquery -inst SIMPLE -filter NONE -det_type dark -select_exp_type dark -workdir path://SIMTEST/mkdark.0 -dbname simtest
+
+### create a flat detrun:
+# exec dettool -pretend -definebyquery -inst SIMPLE -filter B -det_type flat -select_exp_type flat -select_filter B -workdir path://SIMTEST/mkflat.0 -dbname simtest 
+# exec dettool -pretend -definebyquery -inst SIMPLE -filter V -det_type flat -select_exp_type flat -select_filter V -workdir path://SIMTEST/mkflat.1 -dbname simtest 
+
+# generate a set of fake biases
+macro mkbiases
+  echo "generating 10 bias images"
+  for i 0 10
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set bias = blank + $mkiRD*mki_noisei + $biaslevel
+    keyword bias AIRMASS  -wf {1 + 0.1*$i}
+    keyword bias FILTER   -w NONE
+    keyword bias POSANGLE -wf 0.0
+    keyword bias RA       -wf 0.0
+    keyword bias DEC      -wf 0.0
+    keyword bias EXPTIME  -wf 0.0
+    keyword bias DARKTIME -wf 0.0
+    keyword bias DATE-OBS -w "2006/10/01"
+    sprintf time "00:00:%02d" $i
+    keyword bias UTC-OBS  -w $time
+    keyword bias CCDSUM   -w "1 1"
+    keyword bias TELESCOP -w simscope
+    keyword bias INSTRUME -w SIMPLE
+    keyword bias OBSTYPE  -w BIAS
+    keyword bias OBJECT   -w BIAS
+    sprintf name "$simdir/raw/bias.%02d.fits" $i
+    wd bias $name
+  end
+end
+
+macro mkdarks
+  echo "generating 20 dark images"
+  for i 0 20
+    $exptime = 3 * ($i + 1)
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set dark = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime)*mki_noisei + $darkrate*$exptime + $biaslevel
+    keyword dark AIRMASS  -wf {1 + 0.1*$i}
+    keyword dark FILTER   -w  NONE
+    keyword dark POSANGLE -wf 0.0
+    keyword dark RA       -wf 0.0
+    keyword dark DEC      -wf 0.0
+    keyword dark EXPTIME  -wf $exptime
+    keyword dark DARKTIME -wf $exptime
+    keyword dark DATE-OBS -w "2006/10/01"
+    sprintf time "00:01:%02d" $i
+    keyword dark UTC-OBS  -w $time
+    keyword dark CCDSUM   -w "1 1"
+    keyword dark TELESCOP -w simscope
+    keyword dark INSTRUME -w SIMPLE
+    keyword dark OBSTYPE  -w DARK
+    keyword dark OBJECT   -w DARK
+    sprintf name "$simdir/raw/dark.%02d.fits" $i
+    wd dark $name
+  end  
+end
+
+macro mkflats
+  echo "generating 20 flat images in 2 filters"
+  $exptime = 10.0
+  for i 0 10
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set flat = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime + $flatlevel)*mki_noisei + $darkrate*$exptime + $flatlevel + $biaslevel
+    keyword flat AIRMASS  -wf {1 + 0.1*$i}
+    keyword flat FILTER   -w  V
+    keyword flat POSANGLE -wf 0.0
+    keyword flat RA       -wf 0.0
+    keyword flat DEC      -wf 0.0
+    keyword flat EXPTIME  -wf $exptime
+    keyword flat DARKTIME -wf $exptime
+    keyword flat DATE-OBS -w "2006/10/01"
+    sprintf time "00:01:%02d" $i
+    keyword flat UTC-OBS  -w $time
+    keyword flat CCDSUM   -w "1 1"
+    keyword flat TELESCOP -w simscope
+    keyword flat INSTRUME -w SIMPLE
+    keyword flat OBSTYPE  -w FLAT
+    keyword flat OBJECT   -w FLAT
+    sprintf name "$simdir/raw/flat.%02d.fits" $i
+    wd flat $name
+  end  
+
+  for i 10 20
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set flat = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime + $flatlevel)*mki_noisei + $darkrate*$exptime + $flatlevel + $biaslevel
+    keyword flat AIRMASS  -wf {1 + 0.1*$i}
+    keyword flat FILTER   -w  B
+    keyword flat POSANGLE -wf 0.0
+    keyword flat RA       -wf 0.0
+    keyword flat DEC      -wf 0.0
+    keyword flat EXPTIME  -wf $exptime
+    keyword flat DARKTIME -wf $exptime
+    keyword flat DATE-OBS -w "2006/10/01"
+    sprintf time "00:02:%02d" $i
+    keyword flat UTC-OBS  -w $time
+    keyword flat CCDSUM   -w "1 1"
+    keyword flat TELESCOP -w simscope
+    keyword flat INSTRUME -w SIMPLE
+    keyword flat OBSTYPE  -w FLAT
+    keyword flat OBJECT   -w FLAT
+    sprintf name "$simdir/raw/flat.%02d.fits" $i
+    wd flat $name
+  end  
+end
+
+macro mkobjects
+  echo "generating 10 object images in 2 filters"
+  for i 0 5
+    $exptime = 3 * ($i + 1)
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set obj = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime + $skyrate*$exptime)*mki_noisei + $darkrate*$exptime + $skyrate*$exptime + $biaslevel
+    keyword obj AIRMASS  -wf {1 + 0.1*$i}
+    keyword obj FILTER   -w  V
+    keyword obj POSANGLE -wf 0.0
+    keyword obj RA       -wf 0.0
+    keyword obj DEC      -wf 0.0
+    keyword obj EXPTIME  -wf $exptime
+    keyword obj DARKTIME -wf $exptime
+    keyword obj DATE-OBS -w "2006/10/01"
+    sprintf time "00:01:%02d" $i
+    keyword obj UTC-OBS  -w $time
+    keyword obj CCDSUM   -w "1 1"
+    keyword obj TELESCOP -w simscope
+    keyword obj INSTRUME -w SIMPLE
+    keyword obj OBSTYPE  -w OBJECT
+    keyword obj OBJECT   -w OBJECT
+    sprintf name "$simdir/raw/obj.%02d.fits" $i
+    wd obj $name
+  end  
+
+  for i 5 10
+    $exptime = 3 * ($i - 4)
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set flat = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime + $skyrate*$exptime)*mki_noisei + $darkrate*$exptime + $skyrate*$exptime + $biaslevel
+    keyword flat AIRMASS  -wf {1 + 0.1*$i}
+    keyword flat FILTER   -w  B
+    keyword flat POSANGLE -wf 0.0
+    keyword flat RA       -wf 0.0
+    keyword flat DEC      -wf 0.0
+    keyword flat EXPTIME  -wf $exptime
+    keyword flat DARKTIME -wf $exptime
+    keyword flat DATE-OBS -w "2006/10/01"
+    sprintf time "00:02:%02d" $i
+    keyword flat UTC-OBS  -w $time
+    keyword flat CCDSUM   -w "1 1"
+    keyword flat TELESCOP -w simscope
+    keyword flat INSTRUME -w SIMPLE
+    keyword flat OBSTYPE  -w OBJECT
+    keyword flat OBJECT   -w OBJECT
+    sprintf name "$simdir/raw/obj.%02d.fits" $i
+    wd flat $name
+  end  
+end
+
+macro junk
+-definebyquery Optional arguments, with default values:
+    -det_type          ()                         define the type of detrend run (required)
+    -mode              (master)                   define the mode of this detrend run
+    -exp_type          ()                         search for exp_type
+    -inst              ()                         search for camera
+    -telescope         ()                         search for telescope
+    -filter            ()                         search for filter
+    -uri               ()                         search for uri
+    -set_exp_type      ()                         define exposure type
+    -set_filter        ()                         define filter
+    -set_airmass       (nan)            define airmass
+    -set_exp_time      (nan)            define exposure time
+    -set_ccd_temp      (nan)            define ccd tempature
+    -set_posang        (nan)            define rotator position angle
+    -set_object        ()                         define exposure object
+    -set_registered    (2006-12-13T22:40:18.5)    time detrend run was registered
+    -set_use_begin     ()                         start of detrend run applicable peroid
+    -set_use_end       ()                         end of detrend run applicable peroid
+    -pretend           (FALSE)                    print the exposures that would be included in the detrend run and exit
+end
Index: /tags/sj_tags/sj_root_20080929/psdemo/src/psdemo.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psdemo/src/psdemo.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psdemo/src/psdemo.c	(revision 22322)
@@ -0,0 +1,10 @@
+# include "psdemo.h"
+
+int main (int argc, char **argv) {
+
+    psMetadata *config = NULL;
+
+    config = psdemoArgs (&argc, argv);
+
+    exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/psdemo/src/psdemo.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psdemo/src/psdemo.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psdemo/src/psdemo.h	(revision 22322)
@@ -0,0 +1,6 @@
+# include <stdio.h>
+# include <strings.h>  // for strcasecmp
+# include <unistd.h>   // for unlink
+# include <pslib.h>
+
+psMetadata  *psdemoArgs (int *argc, char **argv);
Index: /tags/sj_tags/sj_root_20080929/psdemo/src/psdemoArgs.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psdemo/src/psdemoArgs.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psdemo/src/psdemoArgs.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "psdemo.h"
+
+static void usage (void) ;
+
+psMetadata *psdemoArgs (int *argc, char **argv) {
+
+  int N;
+  int mode = PS_DATA_STRING | PS_META_REPLACE;
+
+  // basic pslib options
+  fprintf (stderr, "starting... %s\n", psLibVersion());
+  psLogSetFormat ("M");
+  psArgumentVerbosity (argc, argv);
+
+  // optional mask image - add to config
+  char *mask = NULL;
+  if ((N = psArgumentGet (*argc, argv, "-mask"))) {
+    psArgumentRemove (N, argc, argv);
+    mask = psStringCopy (argv[N]);
+    psArgumentRemove (N, argc, argv);
+  }
+
+  // optional weight image - add to config
+  char *weight = NULL;
+  if ((N = psArgumentGet (*argc, argv, "-weight"))) {
+    psArgumentRemove (N, argc, argv);
+    weight = psStringCopy (argv[N]);
+    psArgumentRemove (N, argc, argv);
+  }
+
+  // optional output residual image - add to config
+  char *resid = NULL;
+  if ((N = psArgumentGet (*argc, argv, "-resid"))) {
+    psArgumentRemove (N, argc, argv);
+    resid = psStringCopy (argv[N]);
+    psArgumentRemove (N, argc, argv);
+  }
+
+  if (*argc != 4) usage ();
+
+  // setup config information
+  psMetadata *config = psMetadataAlloc ();
+
+  // load config file 
+  // psMetadataAdd (config, PS_LIST_HEAD, "PSF_MODEL", PS_META_MULTI, "folder for psf model entries", NULL);
+  // config = psMetadataConfigRead(config, &Nfail, argv[3], FALSE);
+  // fprintf (stderr, "loaded config...\n");
+
+  // identify input image & optional weight & mask images
+  // command-line entries override config-file entries
+  psMetadataAdd (config, PS_LIST_HEAD, "IMAGE",       mode, "", argv[1]);
+  psMetadataAdd (config, PS_LIST_HEAD, "OUTPUT_FILE", mode, "", argv[2]);
+
+  if (mask != NULL) {
+    psMetadataAdd (config, PS_LIST_HEAD, "MASK_IMAGE", mode, "", mask);
+  }
+  if (weight != NULL) {
+    psMetadataAdd (config, PS_LIST_HEAD, "WEIGHT_IMAGE", mode, "", weight);
+  }
+  if (resid != NULL) {
+    psMetadataAdd (config, PS_LIST_HEAD, "RESID_IMAGE", mode, "", resid);
+  }
+  return (config);
+}
+
+static void usage (void) {
+
+    fprintf (stderr, "USAGE: psdemo (image) (output) (config)\n");
+    fprintf (stderr, "options: \n");
+    fprintf (stderr, "  -mask  (filename)\n");
+    fprintf (stderr, "  -weight (filename)\n");
+    fprintf (stderr, "  -resid (filename)\n");
+    exit (2);
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/.cvsignore	(revision 22322)
@@ -0,0 +1,19 @@
+bin
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+missing
+config.guess
+libtool
+ltmain.sh
+stamp-h1
+config.sub
+psphot.pc
+psphot-config
Index: /tags/sj_tags/sj_root_20080929/psphot/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/Makefile.am	(revision 22322)
@@ -0,0 +1,12 @@
+SUBDIRS = src
+
+CLEANFILES = *.pyc *~ core core.*
+
+bin_SCRIPTS = psphot-config 
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= psphot.pc
+
+EXTRA_DIST = \
+	psphot-config.in \
+	psphot.pc.in \
+	autogen.sh
Index: /tags/sj_tags/sj_root_20080929/psphot/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=psphot
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE  failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/psphot/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/configure.ac	(revision 22322)
@@ -0,0 +1,216 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.61)
+
+AC_INIT([psphot], [0.9.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+dnl ------------------------------------------------------------
+
+AC_PATH_PROG([ERRORCODES], [psParseErrorCodes], [missing])
+if test "$ERRORCODES" = "missing" ; then
+  AC_MSG_ERROR([psParseErrorCodes is required])
+fi
+
+dnl ------------------ kapa,libkapa options -------------------------
+dnl -- libkapa implies the requirement for libpng, libjpeg as well --
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+dnl test for command-line options: use ohana-config if not supplied
+KAPA_CFLAGS_CONFIG="true"
+KAPA_LIBS_CONFIG="true"
+AC_ARG_WITH(kapa,
+[AS_HELP_STRING(--with-kapa=DIR,Specify location of libkapa)],
+[KAPA_CFLAGS="-I$withval/include" KAPA_LIBS="-L$withval/lib" 
+ KAPA_CFLAGS_CONFIG="false"       KAPA_LIBS_CONFIG="false"])
+AC_ARG_WITH(kapa-include,
+[AS_HELP_STRING(--with-kapa-include=DIR,Specify libkapa include directory.)],
+[KAPA_CFLAGS="-I$withval" KAPA_CFLAGS_CONFIG="false"])
+AC_ARG_WITH(kapa-lib,
+[AS_HELP_STRING(--with-kapa-lib=DIR,Specify libkapa library directory.)],
+[KAPA_LIBS="-L$withval" KAPA_LIBS_CONFIG="false"])
+
+echo "KAPA_CFLAGS_CONFIG: $KAPA_CFLAGS_CONFIG"
+echo "KAPA_LIBS_CONFIG: $KAPA_LIBS_CONFIG"
+echo "KAPA_CFLAGS: $KAPA_CFLAGS"
+echo "KAPA_LIBS: $KAPA_LIBS"
+
+dnl HAVE_KAPA is set to false if any of the tests fail
+HAVE_KAPA="true"
+AC_MSG_NOTICE([checking for libkapa])
+if test "$KAPA_CFLAGS_CONFIG" = "true" -o "$KAPA_LIBS_CONFIG" = "true"; then
+  AC_MSG_NOTICE([kapa info supplied by ohana-config])
+  KAPA_CONFIG=`which ohana-config`
+  AC_CHECK_FILE($KAPA_CONFIG,[],
+    [HAVE_KAPA="false"; AC_MSG_WARN([libkapa is not found: output plots disabled.  Obtain libkapa at http://kiawe.ifa.hawaii.edu/Elixir/Ohana or use --with-kapa to specify location])])
+  
+  echo "HAVE_KAPA: $HAVE_KAPA"
+  echo "KAPA_CFLAGS_CONFIG: $KAPA_CFLAGS_CONFIG"
+
+  if test "$HAVE_KAPA" = "true" -a "$KAPA_CFLAGS_CONFIG" = "true" ; then
+   AC_MSG_NOTICE([libkapa cflags info supplied by ohana-config])
+   AC_MSG_CHECKING([libkapa cflags])
+   KAPA_CFLAGS="`${KAPA_CONFIG} --cflags`"
+   AC_MSG_RESULT([${KAPA_CFLAGS}])
+  fi
+
+  if test "$HAVE_KAPA" = "true" -a "$KAPA_LIBS_CONFIG" = "true" ; then
+   AC_MSG_NOTICE([libkapa ldflags info supplied by ohana-config])
+   AC_MSG_CHECKING([libkapa ldflags])
+   KAPA_LIBS="`${KAPA_CONFIG} --libs` -lX11"
+   AC_MSG_RESULT([${KAPA_LIBS}])
+  fi
+fi
+
+if test "$HAVE_KAPA" = "true" ; then
+ AC_MSG_NOTICE([libkapa supplied])
+ PSPHOT_CFLAGS="${PSPHOT_CFLAGS} ${KAPA_CFLAGS}"
+ PSPHOT_LIBS="${PSPHOT_LIBS} ${KAPA_LIBS}"
+else
+ AC_MSG_NOTICE([libkapa ignored])
+fi
+
+dnl HAVE_KAPA is set to false if any of the tests fail
+dnl HAVE_KAPA=true
+dnl AC_CHECK_HEADERS([kapa.h],
+dnl  [PSPHOT_CFLAGS="$PSPHOT_CFLAGS $KAPA_CFLAGS" AC_SUBST(KAPA_CFLAGS)],
+dnl  [HAVE_KAPA=false; AC_MSG_WARN([libkapa headers not found: output plots disabled.  Obtain libkapa at http://kiawe.ifa.hawaii.edu/Elixir/Ohana or use --with-kapa to specify location.])]
+dnl )
+dnl AC_CHECK_LIB(kapa,KapaInitGraph,
+dnl  [PSPHOT_LIBS="$PSPHOT_LIBS $JPEG_LDFLAGS -ljpeg"],  
+dnl  [HAVE_KAPA=false; AC_MSG_WARN([libkapa headers not found: output plots disabled.  Obtain libkapa at http://kiawe.ifa.hawaii.edu/Elixir/Ohana or use --with-kapa to specify location.])],[-lm]
+dnl )
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ libjpeg options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(jpeg,
+[AS_HELP_STRING(--with-jpeg=DIR,Specify location of libjpeg.)],
+[JPEG_CFLAGS="-I$withval/include"
+ JPEG_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(jpeg-include,
+[AS_HELP_STRING(--with-jpeg-include=DIR,Specify libjpeg include directory.)],
+[JPEG_CFLAGS="-I$withval"])
+AC_ARG_WITH(jpeg-lib,
+[AS_HELP_STRING(--with-jpeg-lib=DIR,Specify libjpeg library directory.)],
+[JPEG_LDFLAGS="-L$withval"])
+
+CFLAGS="${CFLAGS} ${JPEG_CFLAGS}"
+CPPFLAGS=${CFLAGS}
+LDFLAGS="${LDFLAGS} ${JPEG_LDFLAGS}"
+
+AC_CHECK_HEADERS([jpeglib.h],
+  [PSPHOT_CFLAGS="$PSPHOT_CFLAGS $JPEG_CFLAGS" AC_SUBST(JPEG_CFLAGS)],
+  [HAVE_KAPA=false; AC_MSG_WARN([libjpeg headers not found: output plots disabled.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
+)
+
+AC_CHECK_LIB(jpeg,jpeg_CreateCompress,
+  [PSPHOT_LIBS="$PSPHOT_LIBS $JPEG_LDFLAGS -ljpeg"],
+  [HAVE_KAPA=false; AC_MSG_WARN([libjpeg library not found: output plots disabled.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
+)
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ libpng options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(png,
+[AS_HELP_STRING(--with-png=DIR,Specify location of libpng.)],
+[PNG_CFLAGS="-I$withval/include"
+ PNG_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(png-include,
+[AS_HELP_STRING(--with-png-include=DIR,Specify libpng include directory.)],
+[PNG_CFLAGS="-I$withval"])
+AC_ARG_WITH(png-lib,
+[AS_HELP_STRING(--with-png-lib=DIR,Specify libpng library directory.)],
+[PNG_LDFLAGS="-L$withval"])
+
+CFLAGS="${CFLAGS} ${PNG_CFLAGS}"
+CPPFLAGS=${CFLAGS}
+LDFLAGS="${LDFLAGS} ${PNG_LDFLAGS}"
+
+AC_CHECK_HEADERS([png.h],
+  [PSPHOT_CFLAGS="$PSPHOT_CFLAGS $PNG_CFLAGS" AC_SUBST(PNG_CFLAGS)],
+  [HAVE_KAPA=false; AC_MSG_WARN([libpng headers not found: output plots disabled.  Obtain libpng from http://www.ijg.org/ or use --with-png to specify location.])]
+)
+
+AC_CHECK_LIB(png,png_init_io,
+  [PSPHOT_LIBS="$PSPHOT_LIBS $PNG_LDFLAGS -lpng"],
+  [HAVE_KAPA=false; AC_MSG_WARN([libpng library not found: output plots disabled.  Obtain libpng from http://www.ijg.org/ or use --with-png to specify location.])]
+)
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ use kapa or not? ---------------------
+
+if test "$HAVE_KAPA" == "true" ; then
+  AC_MSG_RESULT([including plotting functions])
+  AC_DEFINE([HAVE_KAPA],[1],[enable use of libkapa])
+else
+  AC_MSG_RESULT([skipping plotting functions])
+  AC_DEFINE([HAVE_KAPA],[0],[disable use of libkapa])
+fi
+
+dnl ------------- psLib, psModules ---------------
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+
+dnl Set CFLAGS for build
+IPP_STDOPTS
+
+CFLAGS="${CFLAGS=} -Wall -Werror"
+echo "PSPHOT_CFLAGS: $PSPHOT_CFLAGS"
+echo "PSPHOT_LIBS: $PSPHOT_LIBS"
+
+AC_SUBST([PSPHOT_CFLAGS])
+AC_SUBST([PSPHOT_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  psphot-config
+  psphot.pc
+])
+
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/2Dmodels.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/2Dmodels.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/2Dmodels.txt	(revision 22322)
@@ -0,0 +1,29 @@
+
+I am testing a new concept for the 2D representation of various values
+measured by psphot.  I have been using 2D polynomials to fit the
+variations of, eg, the psf parameters as a function of position in the
+images.  this has been somewhat successful, but it has also been
+fraught with a number of problems.  the worst issue has been the fact
+that the polynomial form is not physically motivated.  in some cases,
+eg the variation of the psf shape parameters, the true variations for
+real distributions are not well represented by a polynomial.  I have
+done some ad hoc tricks to get the functional form to look roughly
+like a polynomial, but this is really quite a hack.  the other problem
+is that it is difficult to decide if the polynomial does an acceptable
+job representing the variations.
+
+My new concept for modeling 2D variations in some parameter is to
+define a low-resolution 2D image to represent the variation.  The
+advantage is that the image may be define with whatever resolution may
+be sampled by the input data, and the functional form need not be
+constrained by the polynomial basis function.  
+
+** is this a poor substitute for using a chebychev polynomial as a
+   better basis function?
+
+psphot parameters which vary by position:
+
+pmPSF.params 
+pmPSF.ApTrend
+pmPSF.FluxScale (flux given normalization of 1.0)
+
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/config.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/config.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/config.txt	(revision 22322)
@@ -0,0 +1,56 @@
+
+# input data options
+# IMAGE             STR    file.fits
+# MASK_IMAGE	    STR    mask.fits
+# WEIGHT_IMAGE	    STR	   weight.fits
+
+# output data options
+OUTPUT_FILE	    STR	   output.smp    # output object file
+OUTPUT_MODE	    STR	   TEXT          # output mode: TEXT, OBJ, SX, CMP, CMF 
+# RESID_IMAGE	    STR	   resid.fits    # output residual image
+
+# image noise parameters
+RDNOISE             STR  HD:RDNOISE      # read-noise in electrons
+GAIN                STR  HD:GAIN         # electrons / DN
+
+# masking parameters
+XMIN                F32  32        	 # minimum valid x-coord
+XMAX                F32   0        	 # maximum valid x-coord
+YMIN                F32   1        	 # minimum valid y-coord
+YMAX                F32   0        	 # maximum valid y-coord
+SATURATION          F32  50000     	 # saturation level on this chip
+
+# image statistics parameters
+IMSTATS_NPIX        S32  100000    	 # number of pixels to use for sky estimate:
+
+# peak finding 
+PEAKS_SMOOTH_SIGMA  F32  1.0       	 # smoothing kernel sigma in pixels
+PEAKS_SMOOTH_NSIGMA F32  3.0   	   	 # smoothing kernel width in sigmas
+PEAKS_NSIGMA_LIMIT  F32  10.0  	   	 # peak significance threshold
+
+# basic object statistics
+SKY_INNER_RADIUS    F32  15		 # square annulus for local sky measurement
+SKY_OUTER_RADIUS    F32  25		 # square annulus for local sky measurement
+PSF_MOMENTS_RADIUS  F32  5
+PSF_SN_LIM          F32  100
+
+# PSF model parameters : choose the PSF model
+# list as many PSF_MODEL options as desired
+PSF_MODEL           STR  PS_MODEL_QGAUSS
+#PSF_MODEL           STR  PS_MODEL_PGAUSS
+#PSF_MODEL           STR  PS_MODEL_GAUSS
+PSF_FIT_RADIUS      F32  25		 # fitting radius for test PSF model
+
+# PSF model parameters : apply the PSF model
+PSF_FIT_NSIGMA       F32  1		 # significance for pixel included in fit
+PSF_FIT_PADDING      F32  5              # extra annulus to use for fit 
+PSF_SHAPE_NSIGMA     F32  3.0		 # max significance for shape variation
+PSF_MIN_SN           F32  2.0		 # reject objects below this significance
+PSF_MAX_CHI          F32  10.0		 # reject objects worse that this
+
+# Galaxy model parameters
+GAL_MODEL            STR  PS_MODEL_SGAUSS
+GAL_MIN_SN           F32  3
+GAL_FIT_NSIGMA       F32  1		 # significance for pixel included in fit
+GAL_FIT_PADDING      F32  5              # extra annulus to use for fit 
+GAL_MOMENTS_RADIUS   F32  9
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/footprints.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/footprints.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/footprints.txt	(revision 22322)
@@ -0,0 +1,43 @@
+
+pmFootprintCullPeaks is very expensive (15 - 30 msec per object) and
+must be performed on every peak.  We need to improve the speed (even
+at the cost of memory) and we need to thread it.
+
+*** pmFootprints makes use of the mask image, but it does not use the
+    master mask values.  this must be fixed!
+
+-- here is the algorithm
+
+** note: this is run once for each footprint...
+
+* the incoming footprint peak list is already sorted by brightness
+
+* loop over all peaks
+
+  * choose a threshold which is the peak flux - nsigma_delta*stdev
+    ? actually, the threshold is using the edge of the containing
+    subimage?  I'm a little confused by this.
+
+  * if the threshold is below a minimum threshold, we drop it
+    - the code is calling psArrayRemoveIndex for each of these, then decrementing i.
+      this is inefficient since psArray is not a psList.  two possible options here:
+      
+      * do not remove the dropped peaks from the array until the full
+        processing is done, then re-build the array in a single pass.
+        flag peaks to be dropped (either in the structure or with a
+        parallel vector)
+
+      * have the code build a separate 'bright peaks' array on the fly
+        then swap the new peaks out under the pmFootprint.
+
+  * psArrayRemoveIndex is used 3x in pmFootprints.c : review each case
+
+  * the subImage is divided into footprints above threshold about the
+    current peak.
+
+    - EAM : Need to disect pmFindFootprintsAtPoint.  Is it expensive
+        in this case?  Should we be calling the normal pmFootprints
+        function?
+
+
+  
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/notes.txt	(revision 22322)
@@ -0,0 +1,671 @@
+
+2008.06.25
+
+ timing : footprints
+
+ on GPC1, with FFT: 
+    pass 1 :     convolved with grow disc: 17.558344 sec
+    pass 2 :     convolved with grow disc: 8.915036 sec
+
+2008.01.31
+
+ Most of the fixes listed below have been finished except for:
+
+ * careful handling of the r ~ 0 fluxes / effective positions (for eg sersic)
+ * the EXT output parameters have not been tested
+
+ In addition, some further work to be done:
+
+ * multiple image detection
+ * multiple image fitting (including projection)
+ * optimization
+ * multi-threading
+
+ * possibly try the 'active deblending' concept that Ken from WISE is using.
+   * instead of fitting a double star with the positions floating at a
+     slightly random location, search for significant additions within
+     a grid about the central peak (say ~ 1sigma / 0.2 pix?).  
+     (note that this is a linear fit)
+
+ * for the blend groups, freeze the positions for the fainter sources
+   (consistent with the non-linear S/N threshold)
+
+ 
+
+2007.08.17
+
+ I am working on a number of cleanup / fixes.  I have made an overhaul
+ of the pmModel APIs, adding function pointers in the pmModel
+ structure to the class-specific utility functions (eg,
+ pmModel->modelFunc is the actual function which is evaluated). 
+
+ TO DO:
+
+ * update pmSourceFitSet to be able to include more than one model
+   type (currently it assumes the sources are all, eg, PSFs).  This is
+   now needed because the old implementation used the function lookups
+   which have been dropped.
+
+ * define a generic API set to handle 2D modelling of a scalar using
+   either polynomials (as the pmPSF code currently does) or an
+   image-based representation (as the psphot sky model currently
+   does).  
+
+   The image-based representation can automatically step down from NxM
+   super pixels to a smaller number based on the density of
+   measurements.  
+
+   Include ways to smooth and regularize the output result.
+
+   This mechanism can be applied to: psf parameters, aperture
+   residual, psf peak-to-flux variations, the psphot background
+   representation.
+
+ * generate and store the output radial profile for objects
+
+ * finish testing and incorportate the CR / EXT measurements
+
+ * adjustments to pixel center based on second derivatives : needed
+   for the sersic models.
+
+ * adjustments to pixel flux for extreme values (r ~ 0) : needed for
+   the sersic models.
+
+ * on psf stars : fall back on a Gaussian.
+
+ * OPTIMIZATIONS !!
+
+ * drop the model sky element : should only be in source->sky,dsky
+
+2006.11.16
+
+ensemble:
+
+  * first block: 
+    * select sources of interest / skip
+    * generate a guess model
+    * re-measure moments for saturated stars (MOVE)
+    * update peak position (MOVE to peaks)
+    * set the object radius 
+    * build temporary source copies (including pixels, mask, weight)
+
+  * second block:
+    * measure the cross-products and weights
+    * add to the sparse matrix
+
+  * build full-image mask
+  * measure coord weights
+  * build order elements
+
+  * solve for source amplitudes
+  * update models
+
+  * create psSparseBorder to solve matrix equations which have a large
+    sparse region, with a completely filled border of a smaller number
+    of rows.
+
+  * trying to modify psphotEnsemble to (1) be more general (not just
+    PSFs) and (2) fit for the sky offset.
+
+    - how is the radius for each source set?
+    
+
+2006.11.11
+
+  I am trying to define a complete set of tests to validate the
+  measurement of the photometry compared with the simulated images.
+  Part of my confusion is controlling exactly which corrections are
+  applied to the output magnitudes.  Here is the complete list:
+
+  psfMag_out = psfMag_meas + ApTrend(x,y,0.0,0.0)
+  apMag_out = apMag_meas + growth(radius,refRadius) + f(skyBias,skySat)
+
+  these corrections are modified by the following config settings:
+
+  IGNORE_GROWTH (sets all growth magnitudes to the fitMag value,
+  effectively turning off the growth correction)
+
+  APTREND : set the level of correction
+    NONE     : no correction is fitted
+    CONSTANT : just a single constant (applied to get psfMag_out)
+    SKYBIAS  : the skybias term is fitted (constant applied to psfMag,
+               slope applied apMag)
+  
+2006.11.08
+
+  I have been testing the pmSourceMagnitudes interpolation of the
+  source position when measuing the aperture magnitudes.  I built a
+  test suite for pmSourceMagnitudes and pmGrowthCurve.  even with
+  bicubic interpolation, there are noticable errors for small
+  apertures (ie, small compared to the PSF sigma).  I used a gaussian
+  PSF to make the tests.  it seems that, if the radius is less than 2
+  sigma or so, the interpolated aperture mag is in error by as much as
+  12mmag.  The size of this effect must depend on the smoothing
+  algorithm, the shape of the PSF, the size of the PSF relative to the
+  pixels, and the size of the subpixel interpolation.  I believe the
+  effect is a result of the smoothing introduced by the interpolation:
+  the smoothing pushes the flux in the inner portions of the PSF down
+  and enhances the flux in the outer portions.  thus, there is
+  actually a turn over in the error: at very large values, the error
+  is very small because the aperture contains all of the flux, and the
+  interpolation has little effect.  as the radius shrinks, the error
+  grows (in the sense of M_raw - M_interpolated; ie, the flux in the
+  interpolated source is too large).  around 2-3sigma, the error turns
+  over and becomes negative (too little flux in the interpolated
+  source). 
+
+  The sigmas I used for these tests (1.0, 1.5, 2.0 pix) are
+  appropriate for the range we expect to see from PS1, or any
+  well-sampled detector.  Thus, even though the effect is probably
+  less important for poorer seeing, we have to be careful to avoid it
+  in our Pan-STARRS analysis.
+
+  Currently, the minimum aperture is defined only by the config
+  variable PSF_FIT_PAD, which really implies a padding.  My proposal
+  is to require the minimum aperture to be a number of sigma (an
+  additional parameter).  Then, the formula for the radius would be
+  something like:
+
+  MAX (modelRadiusPSF (PSF_FIT_NSIGMA), PSF_FIT_RADIUS_NSIG) +
+  PSF_FIT_PAD
+
+  with PSF_FIT_RADIUS_NSIG (choose a better name!) set to something
+  like 2 or 3.
+
+2006.11.06
+
+  I have added the aperture interpolation as a component of the
+  pmSourceMagnitudes function.  
+
+  I am trying to make things a bit more organized, with more
+  consistent APIs. 
+
+  * changed pmPSFtry to use the pmSourceMagnitudes API
+  * changed pmPSFtry to use new pmSource entries for each try (saving
+  * the pmModels generated)
+
+  other notes:
+  * moved psphotGrowthCurve -> pmGrowthCurveGenerate 
+
+  things I know need to be added / fixed:
+  * measure bicube fit to peak from smoothed image
+  * use psphotEnsemble fit to re-measure normalization?
+  * extended object statistical measures 
+
+2006.11.02
+
+  Looking into the growth curve problem.  RHL sent his recipe for
+  correcting to true circular apertures.  I've done a test fix of
+  interpolating the stellar image to the desired center.  it looks
+  like bilinear interpolation is insufficient (about 8% error for 2
+  pixel radius aperture).  Unless I've got the formula wrong, the
+  effective smoothing of the gaussian is moving flux from the center
+  out and making small apertures in error that depends on the offset.
+  Bicubic could be enough to clean this up?  
+
+  I think the current code may need to be careful modified to include
+  the interpolation for every aperture measurement.  there are paired
+  'pmModelAdd' and pmModelSub steps to add and remove the model flux.
+  I need to be careful about how the mask affects these steps.
+
+  I also need to be careful in interpreting the test results: since
+  the simulated images create objects by filling in a pixel and then
+  smoothing, all objects have centroids which are exactly 0.5 pixel
+  offsets.  These all then have a fixed growth correction error, which
+  fortunately happens to be substantial (because of a 0.5 pixel offset
+  in the growth curve reference object!)
+
+  I need to double check the 0.5 pixel error issues.  There are
+  problems with the pslib version of psImageShift and
+  psImageInterpolate (or at least inconsistencies).  the pmModelAdd
+  and Sub functions and the centroid functions are probably also
+  inconsistent.
+
+2006.11.01
+
+  Trying to understand low-level errors in psphot using simulated
+  data.  the main issue I am concerned with is the bias RHL is seeing
+  in SDSS-psphot comparisons.  But there are other issues that are
+  creeping in and probably point at logical errors in the code.
+
+  * in pmPSFtry, I measure the first pass on the aperture residual (to
+    choose between PSF model options, if that is being tested).  This
+    analysis currently uses a polynomial fit (1st order) for the offset
+    and the sky bias (using r^2/flux as an independent variable).
+
+    Notes:
+
+    - is the psf-fit error distribution reasonable?  perhaps a little
+      on the high side: at 10000 DN, noise is 0.0138, should be
+      0.0107.
+
+    - scatter of ap-fit is surprisingly high.  M_input - M_fit has a
+      median chisq of 2 (mean of 5), while M_input - M_ap has a median
+      chisq of 125 (mean of 380).  This is using a radius of 15.  It
+      cleans up hugely when I go to a radius of 7 and even more so
+      with radius = 4. (In this case, the PSF sigma is 1.5, so 4 is
+      well matched and 7 is only slightly large).
+
+      * make the initial PSF fitting radius and aperture radius
+	  different?  
+      * make the initial PSF fitting radius a function of the measured
+	  PSF sigma?
+
+    - I have been fitting ap-fit mag without weights; the resulting
+      ap-fit errors are highly over-estimated since the ap-fit scatter
+      is too high.  I have added code to carry the weights (error from
+      the fit only at the moment, ie, not the ap error as well).  This
+      is good, but not as important if the radius is chosen well above.
+
+  * a bias in the psphotEnsemble results depending on the sky level
+    but not in the PSF model fits?  turning on/off CONST_PHOT_WT has
+    an equivalent effect, but is quite small.  why is one sensitive to
+    the sky and the other is not?  
+
+  * the error measured by psphotEnsemble was wrong (and inconsistent
+    for a given weighting situation): fixed the def of the error.
+
+  * still trying to understand the corrections being made.  running a
+    few tests:
+
+    keeping PSF_FIT_RADIUS at 4.0
+
+    t1 : no growth correction,    constant weights
+    t2 :    growth correction,    constant weights
+    t3 : no growth correction, no constant weights
+    t4 :    growth correction, no constant weights
+    t5 :    growth correction,    constant weights, constant ap (4)
+    t6 :    growth correction,    constant weights, integer radius
+    t7 :    growth correction,    constant weights, integer radius
+    (for t7, I've fixed the bug that the growth correction include the
+    min radius).
+
+  * psphotGrowthCurve is messed up : the measurement does not correct
+    for the actual object center, and at small radii this is an
+    important source of noise.
+
+2006.06.28
+
+  I am adding a few minor features.  First up is the ability to break
+  the processing at interesting stages.  These will be:
+
+  - PEAKS: after sources are generated, but before moments are
+    measured
+  - MOMENTS: after moments are measured, before the PSF is measured
+  - PSFMODEL: after the psf model is measured, before sources are
+    fitted in bulk
+  - ENSEMBLE: after the linear ensemble fitting
+  - DEFAULT: complete processing
+
+2006.04.22
+
+  I have updated psphot to work with the cycle 11 release of psLib and
+  psModules.  At this point, the IfA group is now working off of the
+  CVS head rather than from a separate IfA branch of psLib and
+  psModules.  The cycle 11 release of psLib needed a few updates to
+  fix minor problems.  The psModules code needed significant work to
+  fix discrepancies.  The new version of psphot has also been leak
+  checked, at least for basic configurations.  This new version of
+  psphot should be used with the new camera configuration system,
+  which can be found in the ipp/config directory.  Here are the
+  necessary tags:
+
+  psphot: psphot_dev_11_0
+  psModules: rel11_ifa_pre0
+  psLib: rel11_ifa_0
+
+2006.04.16
+
+  Examining some of the throughput issues.  the flux measurement speed
+  is strongly dependent on the interation of the function step size:
+
+  dz = 0.01 : 3.24s for 4400 objects
+  dz = 0.1  : 0.44s for 4400 objects
+
+  there is an increased error in the result with the larger step size,
+  but it is smaller than the phot error.  Still, we could do better
+  with a smarter integration function.
+
+  Including the aperture photometry on all sources, but not the
+  pixWeight, the source magnitudes takes 4.8 sec for 4400 objects.
+
+  Adding the pixWeight increases the time to 7.5 sec for 4400 objects
+
+2006.02.12
+
+  Last week, I made a new tag (psphot_dev_08) after finishing the work
+  to clean memory leaks and to modify psphot to work with Paul's
+  ppImage infrastructure.  
+
+  I have added a new background model function which generates a
+  median map based on Robert's suggestion of a) half-step windows and
+  b) linear interpolation.
+
+  Now, I am working to remove the constant sky level concepts and make
+  every relevant test use the signal/noise ratio as the trigger.  This
+  has impacts on the peak detections, the threshold for blends, and
+  the threshold for choosing pixels in the fitting routines.
+
+2005.12.23
+
+  Improvements in psphot as of psphot_dev_07
+
+  - updated to work with psModule release 9.0
+  - moved model test functions into psphot as optional mode
+  - added pmSourceFitSet : simultaneous model group fitting
+  - added bicubic interpolation to tweak up the peak coordinates
+  - added aperture residual analysis after complete object model & subtraction
+  - added 3D (x,y,rflux) aperture residual fitting
+  - added blend vs blob fitting (BlendFit)
+  - added ensemble PSF fitting, with sparse matrix inversion
+  - improved the flagging / marking of model results
+  - better abstraction for setting fit parameters
+  - various low-level fixes
+  - added PSF output option
+  - added break points
+  - added more optional output formats
+  - fixed FITS table output
+
+  Things still to be added to psphot:
+
+  - better initial background model (NxM median grid?)
+  - include local background model in EnsemblePSF Fit (use sparse matrix)
+  - 2nd pass (after object subtraction) to smooth and detect faint objects
+  - re-add background to object-subtracted image
+  - cleanup function names
+  - push more functions into psLib / psModule
+  - fix extended / galaxy models to improve stability
+  - add mode with input PSF
+  - add mode with input fixed sources (PS_SOURCE_FIXED)
+  - cleanup memory leaks
+
+2005.11.25
+
+  I've updated psphot to work with the current psLib v8, though a
+  number of psLib fixes were needed.  These are pushed under
+  psLib:eam_rel8_b2.  The psphot code which works with that version of
+  psLib is tagged psphot_dev_04.
+
+  I'm working on converting psphot to work with the current release of
+  psModules, which basically includes all of the object functions. 
+
+  - change comments are relative to psModules (vs psphot versions)
+
+  psEllipse.c : OK
+  psEllipse.h : OK
+  pmModelGroup.c : OK (missing TGAUSS entry)
+  pmModelGroup.h : OK (adds many comments)
+
+  pmPSFtry.c : a bunch of formatting weirdness (try-> got line breaks
+	       added pmPSFtryMetric_Alt
+	       fixed up usage of psVectorClipFitPolynomial1D  
+	       fixed up usage of psPolynomial..Alloc  
+
+  pmPSFtry.h : OK (Added extensive comments)
+
+  pmPSF.c : fixed up usage of psPolynomial..Alloc
+
+  pmPSF.h : OK (Added extensive comments)
+
+  pmObjects.c : cleaned up some formatting,
+		fixed usage of psImageCountPixelMask
+
+  pmObjects.h : added apMag and fitMag to pmSource
+		dropped PS_MODEL_name defines
+
+  psLibUtils.[ch] should be dropped from psModules
+  psModulesUtils.[ch] should be dropped from psModules
+
+  Makefile.am : dropped psModulesUtils.[ch], psLibUtils.[ch]
+
+  - I have successfully tested psphot with the psModules.v8 code
+    (eam_rel8_b1 tag).  I only needed to make a few modes in the
+    psModules code.  
+
+2005.11.16
+
+  I have made some fixes to make psphot work with pslib rel8_0.  I
+  need to merge psphot with the rel8_0 version of the psMinimize and
+  pmObjects code.  
+
+2005.09.06
+
+  I have built a working version of PSPhot using my own copy of the
+  pmObjects.[ch] files.  I need to minimize the difference between the
+  v0.7.0, v0.5.0, and my own version of pmObjects.c
+
+  additions to psLib based on the new version:
+  - extended the minimization to include parameter limits
+
+  changes v0.5.0 / v0.7.0 / EAM
+  - changed all ps* to pm*
+  - fixed naming for local static functions (eg modelFree not p_psModelFree)
+  - dropped all hard-coded model names (vs v0.7.0)
+  - using current psMemSetDellocator name
+  - added noise image to pmSource
+  - converted old asserts to new asserts (PS_.._CHECK..)
+  - add return from error in pmFindImagePeaks
+  - allow isItInThisRegion to have NULL region (true)
+  - fixed subImages to correspond to region definition (end is
+    exclusive) in LocalSky and SetPixelCircle
+  - dropped old, redundant code in pmSourceLocalSky
+  - fixed pmSourceMoments use of mask (0 is valid, not 1)
+  - added pmSourceMoments validity tests (numPixels, Sum, centroid shift)
+  - added correction for Sxy (XY/Sum - x*y)
+  - added pmSourcePSFClump function (and pmPSFClump structure)
+  - allow missing sources in pmSourcePSFClump (not all peaks yield
+    sources)
+  - moved pmSourceRoughClass metadata lookups out of inner loop
+  - added saturated pixel test using pmImageCountPixelMask
+  - various redefinitions of initial classes
+  - dropped old, redundant code in pmSourceSetPixelsCircle
+  - mask image set to type psU8 (was psF32)
+  - abstract object model functions for pmSourceModelGuess
+  - converted evalModel to a public function psModelEval, modified params
+  - pmSourceFitModel operates on a specified model
+  - pmSourceFitModel had option to apply/ignore PSF information
+  - pmSourceFitModel calculates yErr
+  - pmSourceFitModel calculates the covariance matrix
+  - added paramMask to pmSourceFitModel 
+  - pmSourceFitModel_EAM uses covar to carry in beta and param
+    limits...
+  - pmSourceFitModel requires solution to stay within image
+  - pmSourceFitModel calculates and saves chisq, nIter, nDOF
+  - pmSourceFitModel uses GaussNewtonDelta for frozen params
+  - added mask to pmSourceAdd, pmSourceSub
+  - model abstractions in pmSourceAdd, pmSourceSub
+  - fixed 'center' option in pmSourceAdd/Sub
+
+  - pmPSF params should be F64
+
+  lingering concerns:
+  - Polynomials and related functions are STILL messed up.  this is a
+    major priority!!!
+  - pmSourceFitModel calculates sqrt(var), psMinimize calculates sq(sqrt(var))
+  - does psImageAlloc zero the image?  if so, drop init routine
+    from pmSourcePSFClump
+  - pmSourceRoughClass uses RDNOISE, GAIN to calculate SN: should use
+    the source->noise image? part of pmSourceMoments?
+  - pmSourceMoments_EAM uses macro for checkRegion
+  - pmObjects_EAM.c uses some hardcoded mask values (LocalSky)
+  - why does LocalSky_EAM not need subImageMask->col0 = subImage->col0??
+  - psGetRowVectorFromImage should be in psLib (in psLibUtils)
+  - pmSourceSetPixelsCircle and pmSourceLocalSky probably could use
+    the psImageMaskRegion, etc functions.
+  - pmSourceSetPixelsCircle uses a hard-coded mask value
+  - define a mask registration process??
+  - pmSourceContour is using the model flux, not the image flux
+
+2005.09.05
+
+  I have a working version of PSPhot which handles PSF and galaxy
+  models.  I am tagging the module with an alpha version number. this
+  version compiles with psLib-0.5.0, at least my version of it.  My
+  current goal is to make it work with the current psLib head (nearly
+  0.7.0) with a mimimum of _EAM versions of functions.
+
+  the tag for the working version is dev_01
+
+2005.06.04
+
+  progress on psphot has moved along well.  At this point, the process
+  loads the image, finds the peaks, determines a best PSF model,
+  identifies objects which are consistent with that model, and
+  attempts to fit a galaxy model to the objects which are not
+  consistent with the model. 
+
+  a variety of minor issues remain, as well as some major issues. 
+
+  - use a proper noise image to keep the fits honest after other
+    objects have been subtracted.  
+
+  - define the masks in terms of a single mask image.  this image
+    could be provided initially by the user.   
+
+  - add noise enhancement (couple with mask marking subtraction)
+
+  - 
+
+2005.04.12
+
+- psPeak, psSource, etc: should be pmPeak, pmSource, etc
+- pmCullPeaks: should be pmCullImagePeaks
+
+- does pmSourceMoments need an image argument?
+
+- psLibInit / p_psTimeInit
+
+  we should not have to know about astronomy time concepts (ie, have a
+  timeConfig file) in order to use the time functions for basic work.
+  This means having a PS_TIME_UNIX which just works with the UNIX
+  clock and avoids all initialization issues.
+
+- psImageStats is much too slow for basic median
+  - this function does a complete sort on all pixel values.  we need
+  to use the histogram method (spec a different type of median)
+
+- psImageStats, ROBUST_MEDIAN fails
+- there are errors in psVectorStats for ROBUST MEAN, STDEV (& median?)
+  * this is ill-defined.  revise the ADD: too sensitive to the
+  binning, smoothing scales 
+
+- pmFindImagePeaks is not finding any peaks:
+  typo in interior row section: see bug 359
+  after this fix: PASS
+
+* p_psGetRowVectorFromImage : PASS
+* psGetRowVectorFromImage : PASS 
+- both have about the same time (~15 us +/- 5us) for 2048 pix on alala
+
+* pmFindVectorPeaks : PASS (returned row data in data.U32)
+
+- # include <string.h> : needed in psMetadata.c
+
+* MyListAddPeak / pmPeakAlloc are inconsistent wrt col,row vs x,y
+
+- why does psMetadataItemAllocV allocate a string of length
+  MAX_STRING_LENGTH for the comment, then use strncpy?
+
+  should be:
+
+    // set metadata item comment
+    if (comment == NULL) {
+        // Per SDRS, null isn't allowed, must use "" instead
+        metadataItem->comment = psStringCopy ("");
+    } else {
+        metadataItem->comment = psStringCopy (comment);
+    }
+
+psMetadataAdd -> psMetadataAddItem is all confused:
+ - we end up with two psMetadataItemAlloc calls, when only one should
+   be used
+ - psMetadataItemAlloc was doing the wrong thing if the incoming data
+   was NULL
+ - I think the logic in this block is wrong as well.  
+
+ * I fixed this in psMetadata.c
+
+2005.04.14 : 
+
+ - psImageStats is still much too slow
+
+ - pmObjects : changed to using psArray to carry peaks / sources,
+   rather than psList
+
+ - added function pmPeaksSubset to replace pmCullPeaks
+
+ - pmSourceLocalSky: added min/max fncs to force subimage to stay in
+   image
+
+ - Sx, Sy can go negative: have forced limit to 0,0
+
+ - pmSourceRoughClass: implemented sigmaX, sigmaY search for stellar
+   clump (uses pmComparePeakDescend)
+
+   - the search ignores the 0,0 pixel
+
+   - pmSourceRoughClass uses all detected peaks.  allow an exclusion
+     for peak > max, peak < min?
+
+   - need to push clump stats on the metadata
+
+   - the source classes are assigned so they are mutually exclusive 
+
+   - change PS_SOURCE_OTHER to STAR? 
+
+ - pmSourceSetPixelCircle:
+
+   - name should be pmSourceSetPixelsCircle (geometry could be an
+     option...)
+
+   - I cleaned the defined region to match the convensions of
+     pmSourceLocalSky
+
+   - truncate and force subimage to lie on image
+
+   - CheckRadius2 seems like an inefficient coding
+
+   - valid pixels have mask value of 1 (inverse of other funcs)
+
+ - pmSourceFitModel:
+
+   - num iterations is much too large
+
+   - 
+
+mrq:
+
+ - allocate alpha, beta arrays
+ - mrq2dcof (pars, alpha, beta)
+ - make guess pars', alpha', beta'
+ - mrq2dcof (pars', alpha', beta')
+ - if (chisq < old chisq), keep new guess
+
+
+2005.04.19:
+
+- psMinimizeLMChi2 has some serious problems.  I re-wrote it starting
+  from the mrqmin example in ohana.   after a variety little bugs, it
+  seems to work quite well.  the program 'modeltest.c' runs two
+  different gaussians through psMinimizeLMChi2, and gets the right
+  answer quite quickly.  The first pass at the implementation had a
+  variety of problems.  to avoid any worry about errors in the
+  psMatrix code, I implemented psGaussJordan, basing the code on the
+  elixir gaussj.c code.  this worked fine, and let me find the errors
+  elsewhere in the code. the LUD version now works just the same as
+  the GaussJ version.
+
+
+2005.04.21
+
+- psMinimizeLMChi2 does an excellent job now in most cases.  I am able
+  to run pmSourceFitModel on the PSF stars quite well.  I added in the
+  (nearly) correct errors (actually, just using sqrt(N), limiting to
+  N>=1).  I see one problem object, which is quite faint, and does not
+  converge well: I get very large values for x,y, Sx, Sy, etc.  I need
+  to think about constraining these fits from running way off scale.
+
+  fit:   0.123879 sec for 277 stars (gauss)
+  fit:   0.155940 sec for 277 stars (pgauss) (!)
+  fit:   0.153292 sec for 277 stars
+  (0.5 msec per star)
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/outline.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/outline.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/outline.txt	(revision 22322)
@@ -0,0 +1,21 @@
+
+Here is a summary outline of the steps taken by psphotReadout:
+
+* setup (choose recipe, choose readout, define break-point)
+* create mask and weight images if needed, set mask for analysis region
+* psphotImageMedian : construct a background model image and subtract it
+* psphotFindPeaks   : smooth and find the peak pixels from the smoothed image
+  - no pmSource defined yet, only pmPeak
+* psphotSourceStats : create sources for each peak and measure their moments
+  - pmSource defined here, with pixels covering SKY_OUTER_RADIUS
+  - no pmModel is defined yet.
+* psphotBasicDeblend : identify blended sources by proximity and valley depth
+* psphotRoughClass : source classification guess based on moments
+  - set the source.type,mode.
+  - re-calculate moments for saturated stars
+* psphotChoosePSF : define the psf model
+  - this generates model fits to the identified psf stars
+    (one for each tested model)
+  - these models are not kept (seems like a waste, but later fits must be consistent)
+* psphotGuessModels : set the initial PSF model for each object (even EXTENDED)
+  - creates modelPSF entries
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/output.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/output.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/output.txt	(revision 22322)
@@ -0,0 +1,15 @@
+
+we have several output formats:
+  - TEXT : separate ASCII tables for PSF, EXT, MNT, and NULL
+  - SX : sextractor-style ASCII file
+  - OBJ : dophot-style ASCII file
+  - CMP : old-style elixir format (header + text)
+  - CMF : fits-table
+
+we have several options for organization:
+  - single chip/readout : outroot.ext
+  - split chips : outroot.NN.ext
+  - mef chips : outroot.ext (extensions with names)
+
+  * only CMF will work with MEF model
+
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/psfmodel.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/psfmodel.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/psfmodel.txt	(revision 22322)
@@ -0,0 +1,62 @@
+
+2007.09.25
+
+  The PSF model has parameters which vary across the image.  This 2D
+  is can be represented as either a polynomial (ordinary or chebychev)
+  or as an image map.  The user requests the maximum X & Y scale
+  (PSF.TREND.NX & PSF.TREND.NY) for the variations, and
+  psphotChoosePSF / pmPSFtry attempt to find the best choice for the
+  scale.  For the polynomial representations, NX and NY are the order
+  of the variation.  For the image map, NX and NY are the size of the
+  image map.
+
+2007.09.21
+
+  there are three places where we can choose to use errors in the fits or not:
+
+  * non-linear fitting of the models to the pixel flux distribution (poissonErrorsPhotLMM)
+  * linear fitting of the models to the pixel flux distribution (poissonErrorsPhotLin)
+  * fitting of the 2D variations in the psf parameters (poissonErrorsParams)
+  * fitting of the 2D variations in the aperture residuals
+
+2007.09.20
+
+  I am upgrading the PSF model to allow the parameter variation to be
+  modeled with pmTrend2D instead of just polynomials.  I am making a
+  list of places to modify the code:
+
+pmPSFAlloc : need a method beyong psfTrendMask to carry in the psf
+options
+
+pmPSF_ModelToFit : no need to change these
+
+update pmPSFBuildSimple to set the parameters of the pmTrend, which
+ever is used.
+
+pmPSFtry.c: some significant re-work!
+
+
+
+pmPSF_IO : need new functions to save / load the trend (psImages)
+
+2006.10.27
+
+  I have been working to fix the PSF modeling in psphot.  The PSF
+  model consists of a flux model for an individual object using an
+  analytical model with a number of parameters.  For a collection of
+  PSF objects, the variation of the parameters as a function of
+  position are then themselves fitted with a model.  The PSF model for
+  a single object consists of a radial profile with a functional form
+  f(z) where a given value of z defines an elliptical coutour of the
+  form z = \frac{x^2}{2\sigma_x^2} + \frac{y^2}{2\sigma_y^2} +
+  \sigma_{xy}xy.
+
+
+
+  The term \sigma_{xy} is difficult to model as a simple function of x
+  and y (eg, a low-order polynomial).  
+
+  A better model can be constructed for
+  \frac{\sigma_{xy}}{(\sigma_x^{-2} + \sigma_y^{-2})^2}, which varies
+  like a^2 \sin 2\theta 
+
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/psphot.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/psphot.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/psphot.txt	(revision 22322)
@@ -0,0 +1,160 @@
+
+TBD priorities:
+
+- test psImageSmooth / remove psImageSmooth_EAM
+- unify pmSourceLocalSky / pmSourceLocalSky_EAM
+- unify pmSourceMoments_EAM / pmSourceMoments_EAM
+- merge with image data hierarchy stuff
+- better name for psPSF_Test
+
+Defined APIs:
+
+top-level psphot functions : these do not need to be in psModules
+  psMetadata  *psphotArguments ();
+  psImageData *psphotSetup ();
+  psStats     *psphotImageStats ();
+  psArray     *pmPeaksSigmaLimit ();
+  psArray     *psphotSourceStats ();
+  pmPSF       *psphotChoosePSF ();
+  bool         psphotApplyPSF ();
+  bool         psphotFitGalaxies ();
+  void         psphotOutput ();
+
+psphot-specific functions
+  bool         psphotMarkPSF ();
+  bool         psphotSubtractPSF ();
+  int          psphotSortBySN ();
+  int          psphotSaveImage ();
+
+** add the following entries to the psLibSDRS
+
+psLib extra utilities
+  bool         psTimerStart ();		- already in SDRS
+  void         psTimerFree (); 		- already in SDRS
+  bool         psTimerClear ();		- add to psLibSDRS
+  psF64        psTimerMark (); 		- already in SDRS
+  psS32        psLogArguments ();	- now: psArgumentVerbosity in SDRS
+  psS32        psTraceArguments ();	- now: psArgumentVerbosity in SDRS
+  int          psArgumentGet ();	- already in SDRS
+  int          psArgumentRemove ();	- already in SDRS
+  psVector    *psVectorCreate ();	- add to psLibSDRS
+  int          psImageCountPixelMask ();- add to psLibSDRS
+  psVector    *psGetRowVectorFromImage(); - add to psLibSDRS?
+  bool         p_psVectorPrintRow ();     - add to psLibSDRS?
+
+// basic image functions
+  bool         psImageInit ();		- already in SDRS
+  void         psImageSmooth_EAM ();    - test psLib version and drop
+
+// psLine functions
+  psLine      *psLineAlloc ();		- add to psLibSDRS
+  bool         psLineInit (); 		- add to psLibSDRS
+  bool         psLineAdd ();  		- add to psLibSDRS
+
+** compare with current implementation / update psLib
+   we need to define a mechanism to carry the beta values and the
+   parameter limits
+
+// minimize 
+  psBool       p_psMinLM_GuessABP_EAM ();
+  psBool       psMinimizeLMChi2_EAM();
+  psF64        p_psMinLM_dLinear ();  -- not included in pslib.h files
+
+** already in psLib : some need to be cleaned up by MHPCC
+
+// polynomial functions
+  psF64           Polynomial2DEval();
+  psImage        *psBuildSums2D();
+  psPolynomial2D *VectorFitPolynomial2DOrd_EAM();
+  psPolynomial2D *Polynomial2DAlloc();
+  void            psPolynomial2DDump ();
+  psPolynomial2D *RobustFit2D_nomask();
+  psPolynomial2D *RobustFit2D();
+  psVector       *Polynomial2DEvalVector();
+
+  psVector       *psBuildSums1D();
+  void            psPolynomial1DDump ();
+  psF64           Polynomial1DEval_EAM();
+  psVector       *Polynomial1DEvalVector_EAM();
+  psPolynomial1D *Polynomial1DAlloc();
+  psPolynomial1D *VectorFitPolynomial1DOrd_EAM();
+
+** make sure these are all in the psModules SDRS:
+
+pmObjects.h:
+  pmMoments           *pmMomentsAlloc();
+  pmModel             *pmModelAlloc();
+  pmSource            *pmSourceAlloc();
+  psVector            *pmFindVectorPeaks()
+  psArray             *pmFindImagePeaks()
+  psList              *pmCullPeaks()
+  pmSource            *pmSourceLocalSky()
+  bool                 pmSourceMoments()
+  pmPSFClump           pmSourcePSFClump()
+  bool                 pmSourceRoughClass()
+  bool                 pmSourceSetPixelsCircle()
+  pmModel             *pmSourceModelGuess()
+  psArray             *pmSourceContour()
+  bool                 pmSourceFitModel()
+  bool                 pmSourceAddModel()
+  bool                 pmSourceSubModel()
+  int                  pmModelParameterCount ();
+  char                *pmModelGetType ();
+  pmModelType          pmModelSetType ();
+  pmModelFunc          pmModelFunc_GetFunction ();
+  pmModelFlux          pmModelFlux_GetFunction ();
+  pmModelRadius        pmModelRadius_GetFunction ();
+  pmModelLimits        pmModelLimits_GetFunction ();
+  pmModelGuessFunc     pmModelGuessFunc_GetFunction ();
+  pmModelFromPSFFunc   pmModelFromPSFFunc_GetFunction ();
+  pmModelFitStatusFunc pmModelFitStatusFunc_GetFunction ();
+
+model function abstractions:
+  typedef psMinimizeLMChi2Func pmModelFunc;
+  typedef psF64 (*pmModelFlux)(const psVector *params);
+  typedef psF64 (*pmModelRadius)(const psVector *params, double flux);
+  typedef bool (*pmModelLimits)(psVector **beta_lim, psVector **params_min, psVector **params_max);
+  typedef bool (*pmModelGuessFunc)(pmModel *model, pmSource *source);
+  typedef bool (*pmModelFromPSFFunc)(pmModel *modelPSF, pmModel *modelFLT, pmPSF *psf);
+  typedef bool (*pmModelFitStatusFunc)(pmModel *model);
+
+conversions between elliptical shape representations
+  EllipseAxes  EllipseMomentsToAxes ();
+  EllipseShape EllipseAxesToShape ();
+  EllipseAxes  EllipseShapeToAxes ();
+
+output functions
+  bool         pmSourcesWriteText ();  	-- be careful about psImageData...
+  bool         pmSourcesWriteOBJ ();   	-- be careful about psImageData...
+  bool         pmSourcesWriteCMP ();   	-- be careful about psImageData...
+  bool         pmSourcesWriteCMF ();   	-- be careful about psImageData...
+  bool         pmSourcesWriteSX ();    	-- be careful about psImageData...
+  int          pmSourcesDophotType ();
+  bool         pmPeaksWriteText ();
+  bool         pmMomentsWriteText ();
+  bool         pmModelWritePSFs ();
+  bool         pmModelWriteFLTs ();
+  bool         pmModelWriteNULLs ();
+
+// psf utilities
+  pmPSF       *pmPSFAlloc ();
+  pmPSF_Test  *pmPSF_TestAlloc ();
+  pmPSF_Test  *pmPSF_TestModel ();
+  bool         pmPSFFromModels ();
+  pmModel     *pmModelFromPSF ();
+  bool         pmSourcePhotometry ();
+  bool         pmPSFMetricModel ();
+
+// psModule extra utilities
+  bool         pmSourceFitModel_EAM();  -- requires updated version of psMinimize...
+  bool         pmSourceLocalSky_EAM (); -- compare with pmSourceLocalSky & choose
+  bool         pmSourceMoments_EAM();   -- compare with pmSourceMoments & choose
+  bool         pmSourceDefinePixels();  -- be careful about psImageData...
+  bool         pmModelFitStatus ();
+  int          pmSourceDophotType ();
+  psF32        pmConfigLookupF32 ();	-- merge with Paul's image container hierarchy
+
+** this needs to be confronted with the phase2 / image container hierarchy
+
+// psImageData functions
+  psImageData *psImageDataAlloc ();
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/regions.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/regions.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/regions.txt	(revision 22322)
@@ -0,0 +1,159 @@
+
+I am having some trouble with functions that refer to both subimages
+and regions.  there are inconsistencies between the coordinate
+conventions.  I am going to go through the list of image functions are
+decide if we can choose either 1) external coordinates always refer to
+parent coordinates or 2) something else!
+
+bool psImageSet(const psImage *image, int x, int y, double complex value);
+double complex psImageGet(const psImage *image, int x, int y);
+ - sdrs does not specify
+ - code uses subimage
+
+psRegion psRegionForImage(psImage *image, psRegion in);
+ - sdrs says subimage
+ - code uses subimage
+
+psImage *psFitsReadImage(psImage *out, const psFits *fits, psRegion region, int z);
+ - region here specifies a subimage of the disk image
+ 
+bool psFitsUpdateImage(psFits *fits, const psImage *input, int x0, int y0, int z);
+ - x0,y0 here refer to the disk image coordinates
+
+bool psFitsWriteImage(psFits *fits, psMetadata *header, const psImage
+*input, int depth, const char *extname);
+ - note that the output disk image is truncated to the subimage
+
+bool psFitsInsertImage(psFits *fits, psMetadata *header, const psImage *input, int depth,
+                       const char *extname, bool after);
+ - same as above
+
+psImage *psImageSubset(psImage *image, psRegion region);
+ - code uses region as coordinates in parent image
+ - sdrs does not specify
+ - XXX I think the range tests for region fail if the input is a
+   subimage
+
+psImage *psImageCopy(psImage *output, const psImage *input, psElemType type);
+ - maintains the col0,row0 values
+
+psVector *psImageRow(psVector *out, const psImage *input, int row);
+psVector *psImageCol(psVector *out, const psImage *input, int column);
+ - code uses subimage coordinates for row and column
+
+psVector *psImageSlice()
+psVector* psImageCut()
+ - code uses subimage coordinates (no correction)
+ - sdrs does not specify
+
+psImage *psImageRebin()
+ - mask and input image must currently agree
+
+psImage *psImageTransform()
+ - sdrs is unclear: I think the region defines the output image such
+ that the pixel in the input image *parent* coordinates which
+ corresponds to region.x0,y0 in turn corresponds to the output image
+ 0,0 pixel; the output image always has col0,row0 = 0,0
+
+long psImageCountPixelMask (psImage *mask, psRegion region, psMaskType value);
+ - sdrs says the region corresponds to subimage coordinates
+
+psPolynomial2D *psImageFitPolynomial(psPolynomial2D *coeffs, const psImage *input);
+psImage *psImageEvalPolynomial(psImage *input, const psPolynomial2D *coeffs);
+ - sdrs does not specify
+ - polynomial coordinates should refer to the *parent* image
+
+double complex psImagePixelInterpolate(input, x, y, ...)
+ - sdrs does not specify
+
+int psImageOverlaySection(psImage *image, const psImage *overlay, )
+ - sdrs does not specify
+
+psImage *psPixelsToMask()
+psPixels *psPixelsFromMask()
+ - sdrs does not specify
+
+void psImageMaskRegion()
+void psImageKeepRegion()
+ - sdrs does not specify
+
+void psImageMaskCircle()
+void psImageKeepCircle()
+ - sdrs does not specify
+
+pmReadout *pmSubtractSky(pmReadout *in, psPolynomial2D *poly, psImage *mask, psU8 maskVal, 
+                         int binFactor, psStats *stats, float clipSD);
+ - choice of polynomial coordinates
+
+stats *pmFringeStats (psArray *fringePoints, psImage *image, psMetadata *config);
+ - unspecified
+
+psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row);
+ - sdrs says col,row are in *subimage* coords
+
+psArray *pmFindImagePeaks(const psImage *image, float threshold);
+ - does not specify coordinate system
+ - code uses subimage?
+
+bool pmSourceDefinePixels()
+bool pmSourceRedefinePixels()
+ - sdrs does not specify very clearly
+
+
+
+---------
+
+functions which are not affected
+psFitsReadImage()
+psFitsUpdateImage()
+psFitsWriteImage()
+psFitsInsertImage()
+psImageCopy()  -- maintains col0,row0
+psImageRebin() -- mask and image must match
+
+functions which currently use parent coordinates (CORRECT)
+psImageSubset()  -- check on range tests
+psImageMaskRegion()
+psImageKeepRegion()
+psImageMaskCircle()
+psImageKeepCircle()
+pmSourceMoments -- (OK if input peaks are in parent coords)
+
+functions which currently use subimage coordinates (FIX)
+psImageSet()
+psImageGet()
+psRegionForImage() *fixed*
+psImageRow()
+psImageCol()
+psImageSlice()
+psImageCut()
+psImageCountPixelMask ();
+pmModelEval()
+pmFindImagePeaks() *fixed*
+pmSourceDefinePixels() *fixed by psRegionForImage fix*
+pmSourceRedefinePixels() *fixed by psRegionForImage fix*
+
+functions which are unclear (CHECK)
+psImageTransform()
+psImageFitPolynomial()
+psImageEvalPolynomial()
+psImagePixelInterpolate()
+psPixelsToMask()
+pmSubtractSky()
+pmFringeStats()
+
+
+***** one ambiguity: if we have a subimage, but we do not have the
+      parent image data (subimage does not carry dimensions of parent
+      array), then a 0 or negative upper-edge of a region cannot be
+      defined relative to the parent. we can specify it relative to
+      the subimage.  so, for example, given a subimage [32,100:32,100]
+      of a much larger image, then psRegionForImage with 0,0,0,0 as
+      input would return a region with values 32,100:32,100...
+
+
+
+
+
+*** things I've modified
+    psRegionForImage expects parent (not subimage) coordinates
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/speed.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/speed.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/speed.txt	(revision 22322)
@@ -0,0 +1,129 @@
+
+psphot -g
+psModule -g
+psLib -O2
+on alala:
+
+alala: psphot -vv starfield.fits starfield starfield.cnf -resid starfield.resid.3.fits
+    load data: 0.983568 sec
+    System random seed value used  seed = 25904F7C7454AE25 hex
+    background: 1229.280029 +/- 27.700180
+    image stats: 0.063085 sec
+    System random seed value used  seed = 411E5A0600A49AF3 hex
+    fit background model: 0.580152 sec
+    smooth: 1.842415 sec
+    threshold: 83.100540 DN
+    4356 peaks: 0.571587 sec
+    4230 moments: 3.965099 sec
+    SN range: 3.355829 - 1043.401001
+    identified 107 blended objects (0.121737 sec)
+    selected candidate 300 PSF objects
+    fit flt:   3.682850 sec for 300 sources
+    fit psf:   1.704141 sec for 300 sources
+    fit stats: 0.000884 +/- 0.004499
+    try model PS_MODEL_QGAUSS, ap-fit: -0.007532 +/- 0.004499 : sky bias: -0.717999
+    select psf model: 5.402742 sec
+    selected psf model PS_MODEL_QGAUSS, ApResid: -0.007532 +/- 0.004499
+    fitting pixels with at least 27.700180 object counts
+    built models: 0.991345 (4230 objects)
+    built matrix: 1.472869 (27998 elements)
+    measure ensemble of PSFs: 4.334556
+    fit PSF models: 12.205738 sec for 4230 objects
+    replace unfitted models: 0.021473 sec (4230 objects)
+    measure aperture residuals : 0.451886 sec
+    measure full-frame aperture residual: 0.454032 sec
+    aperture residual: -0.002842 +/- 0.002729 : 0.166072 bias
+    wrote output: 4.648821 sec
+    complete psphot run: 35.334080 sec
+
+
+psphot -g
+psModule -O2
+psLib -O2
+on alala:
+
+alala: psphot -vv starfield.fits starfield starfield.cnf -resid starfield.resid.3.fits
+    load data: 1.001502 sec
+    System random seed value used  seed = 5F43A63F532D4B3F hex
+    background: 1229.280029 +/- 27.700180
+    image stats: 0.063169 sec
+    System random seed value used  seed = F5F5FF99A6578F61 hex
+    fit background model: 0.530384 sec
+    smooth: 1.856414 sec
+    threshold: 83.100540 DN
+    4365 peaks: 0.237292 sec
+    4239 moments: 4.087611 sec
+    SN range: 3.356469 - 1043.400879
+    identified 107 blended objects (0.123372 sec)
+    selected candidate 300 PSF objects
+    fit flt:   3.741444 sec for 300 sources
+    fit psf:   1.676478 sec for 300 sources
+    fit stats: 0.000883 +/- 0.004499
+    try model PS_MODEL_QGAUSS, ap-fit: -0.007532 +/- 0.004499 : sky bias: -0.717985
+    select psf model: 5.458156 sec
+    selected psf model PS_MODEL_QGAUSS, ApResid: -0.007532 +/- 0.004499
+    fitting pixels with at least 27.700180 object counts
+    built models: 0.791613 (4239 objects)
+    built matrix: 1.267909 (28101 elements)
+    measure ensemble of PSFs: 3.551364
+    fit PSF models: 11.240197 sec for 4239 objects
+    replace unfitted models: 0.018051 sec (4239 objects)
+    measure aperture residuals : 0.411856 sec
+    measure full-frame aperture residual: 0.413843 sec
+    aperture residual: -0.002831 +/- 0.002717 : 0.164772 bias
+    wrote output: 4.355429 sec
+    complete psphot run: 33.126283 sec
+
+after reducing indirection in pmSourceMoments:
+    4268 moments: 3.762452 sec
+
+psphot -O2
+psModule -O2
+psLib -O2
+
+alala: psphot -vv starfield.fits starfield starfield.cnf -resid starfield.resid.3.fits
+    load data: 0.484324 sec
+    System random seed value used  seed = 980050732A392EC7 hex
+    background: 1229.280029 +/- 27.700180
+    image stats: 0.058759 sec
+    System random seed value used  seed = 44E1A6EE77B0E9CE hex
+    fit background model: 0.395125 sec
+    smooth: 1.861776 sec
+    threshold: 83.100540 DN
+    4351 peaks: 0.234874 sec
+    4264 moments: 3.719435 sec
+    SN range: 2.023345 - 1043.401123
+    identified 124 blended objects (0.115230 sec)
+    selected candidate 300 PSF objects
+    fit flt:   3.525501 sec for 300 sources
+    fit psf:   1.595031 sec for 300 sources
+    fit stats: 0.000884 +/- 0.004499
+    try model PS_MODEL_QGAUSS, ap-fit: -0.007532 +/- 0.004499 : sky bias: -0.717976
+    select psf model: 5.160442 sec
+    selected psf model PS_MODEL_QGAUSS, ApResid: -0.007532 +/- 0.004499
+    fitting pixels with at least 27.700180 object counts
+    built models: 0.760805 (4264 objects)
+    built matrix: 0.908674 (23703 elements)
+    measure ensemble of PSFs: 3.115425
+    fit PSF models: 10.162397 sec for 4264 objects
+    replace unfitted models: 0.017491 sec (4264 objects)
+    measure aperture residuals : 0.408242 sec
+    measure full-frame aperture residual: 0.410207 sec
+    aperture residual: -0.002908 +/- 0.002734 : 0.170692 bias
+    wrote output: 4.225560 sec
+    complete psphot run: 30.149281 sec
+
+
+    fit PSF models: 10.162397 sec for 4264 objects
+    select psf model: 5.160442 sec
+    wrote output: 4.225560 sec
+    4264 moments: 3.719435 sec
+    measure ensemble of PSFs: 3.115425 sec
+
+    other : 3.5 sec
+
+    fixed per objects : 4 msec / object (17 of 30 sec)
+
+
+** still can re-compile with -DPS_NO_TRACE
+** ASSERTS cannot be compiled out at the moment!
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/timing.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/timing.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/timing.txt	(revision 22322)
@@ -0,0 +1,84 @@
+
+                                                         -- threadable?
+                                                         |
+  0.000673 sec NPEAK      0.00 msec / item (505)       - ? Find SatStars
+  0.231828 sec NPIX       0.00 msec / item (PIX)       - ? Replace Background
+  0.349591 sec NPIX       0.00 msec / item (2048*4096) - ? Background Model
+  0.609246 sec NPIX       0.00 msec / item (2048*4096) - ? Find Peaks
+  0.733840 sec NPIX       0.00 msec / item (2048*4096) - ? Find Peaks
+  6.076478 sec NPIX       0.00 msec / item (2048*4096) - ? Smooth Images
+  7.683749 sec NPIX       0.00 msec / item (2048*4096) - ? Smooth Image
+  0.003003 sec NPEAK      0.01 msec / item (505)       - ? Source Size
+  0.003934 sec NPEAK      0.01 msec / item (??)        - ? Source Sizes
+  0.019364 sec NPEAK      0.04 msec / item (505)       - ? Find Blends
+  0.045397 sec NPEAK      0.09 msec / item (??)        - ? Replaced Flux
+  0.046370 sec NPEAK      0.09 msec / item (505)       - ? Add Noise
+  0.046680 sec NPEAK      0.09 msec / item (505)       - ? Sub Noise
+  0.050980 sec NPEAK      0.10 msec / item (505)       - ? Replaced Flux
+  2.785612 sec NFOOT      0.27 msec / item (10455)     - ? Find Footprints
+  0.322163 sec NPEAK      0.64 msec / item (505)       - ? Rough Class
+  0.163793 sec NPEAK      0.66 msec / item (??)        - ? Rough Class
+  0.820221 sec NFOOT      1.40 msec / item (587)       - ? Find Footprints
+  2.311492 sec NPEAK      4.58 msec / item (??)        - ? Built Models
+  2.413531 sec NPEAK      4.78 msec / item (505)       - ? Build Models
+  1.635674 sec NPEAK      6.54 msec / item (??)        - ? Measure Moments
+  3.547435 sec NPEAK      7.02 msec / item (505)       - ? Measure Moments
+  8.118224 sec NSRCS      7.81 msec / item (1040)      - ? ApTrend
+  4.165990 sec NPEAK      8.25 msec / item (505)       - ? Fit PSF (linear)
+  4.294968 sec NPEAK      8.50 msec / item (505)       - ? Fit PSF (linear)
+  4.638916 sec NPEAK      9.19 msec / item (??)        - ? Fit PSF (linear)
+154.599482 sec NFOOT     14.79 msec / item (10455)     - ? Cull Footprints
+  1.259951 sec NPSF      17.75 msec / item (71)        - ? Fit PSF (non-linear)
+ 22.009468 sec NFOOT     37.49 msec / item (587)       - ? Cull Footprints: 
+ 15.069461 sec NPEAK     38.34 msec / item (393)       - ? Fit PSF (non-linear) + EXT (27) 
+  2.938324 sec NPSF      41.38 msec / item (71)        - ? Fit EXT (non-linear)
+  2.723019 sec NPSF      53.39 msec / item (51)        - ? PSF Residuals
+
+244.837692 sec    complete psphot run: 
+
+Note:
+
+* PSF Residuals is the most expensive per item, but will always be
+  performed on a limited number of items (typically < 300 PSF stars).
+  The maximum impact is thus limited.
+
+* The non-linear fitting is (not surprisingly) very expensive per
+  item, but again will only be performed on a limited subset (S/N >
+  20?).
+
+* The Cull Footprints is quite expensive per item, and has the serious
+  problem that it must be performed on all peaks.  This will dominate
+  all processing time unless we can address it.
+
+* The linear fitting in this example seems to surprisingly expensive
+  per item, but I think it is not quite accurate.  
+
+* ApTrend is only measured on a subset of the sources (S/N > XX, max
+  total number)
+
+* Measure Moments and Build Models are potentially a substantial drain
+  since these (probably) should be applied to all objects.  Moments is
+  currently being limited to brighter objects.  
+
+Thread strategies:
+
+
+* tests with wbb4.031117_0154.052.fits test -chip 7 : 
+
+* baseline: 
+    grow   : 18.313876 sec
+    merged : 18.772070 sec
+    total  : 26.958080 sec (dt = 8.18)
+
+* upgrade to remove psArrayRemove:
+    grow   : 18.327212 sec
+    merged : 18.778043 sec
+    total  : 25.553486 sec (dt = 6.78; ddt = 1.4)
+
+* recycle idImage:
+    grow   : 18.383393 sec
+    merged : 18.848875 sec 
+    total  : 24.721451 sec (dt = 5.88; ddt = 2.3)
+
+(no real difference??)
+
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/timing.v2.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/timing.v2.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/timing.v2.txt	(revision 22322)
@@ -0,0 +1,94 @@
+#             msec                   - multithread?
+#             item                   |
+15.558560 sec 37.31    NBRT   417  : y  non-linear fits
+ 0.000736 sec  0.00    NPEAK  550  : ?  satstar blends
+ 0.005947 sec  0.00    NPEAK 1315  : ?  source sizes
+ 0.076172 sec  0.01    NPEAK 7636  : ?  set footprint ids (NPIX?)
+ 0.003383 sec  0.01    NPEAK  550  : ?  source sizes
+ 0.019354 sec  0.04    NPEAK  550  : ?  other blends
+ 0.050572 sec  0.08    NPEAK  627  : ?  set footprint ids
+ 0.049778 sec  0.09    NPEAK  550  : ?  replaced models
+ 0.051736 sec  0.09    NPEAK  550  : ?  add noise
+ 0.051929 sec  0.09    NPEAK  550  : ?  sub noise
+ 0.055094 sec  0.10    NPEAK  550  : ?  replaced models
+ 0.152204 sec  0.20    NPEAK  765  : ?  rough class
+ 0.8      sec  0.26    NPEAK 3100  : ?  merge footprints
+ 0.268983 sec  0.49    NPEAK  550  : ?  rough class
+ 0.7      sec  1.25    NPEAK  560  : ?  cull peaks
+ 2.092846 sec  2.74    NPEAK  765  : ?  moments
+ 8.9      sec  3.05    NPEAK 2914  : ?  cull peaks
+ 3.339909 sec  4.37    NPEAK  765  : ?  built models
+ 2.572153 sec  4.68    NPEAK  550  : ?  build models
+ 3.816792 sec  6.94    NPEAK  550  : ?  moments
+ 9.337548 sec  7.10    NPEAK 1315  : ?  measure magnitudes :  for 1315 objects (643 with apertures)
+ 4.301656 sec  7.82    NPEAK  550  : ?  linear fit
+ 4.429642 sec  8.05    NPEAK  550  : ?  linear fit
+ 4.947403 sec  9.00    NPEAK  550  : ?  linear fit 
+ 0.2      sec  0.02    NPIX 8300 k : ?  found grown footprints: 
+ 0.205680 sec  0.02    NPIX 8300 k : ?  find footprints
+ 0.211664 sec  0.03    NPIX 8300 k : ?  replace background
+ 0.348357 sec  0.04    NPIX 8300 k : ?  subtracted background
+ 0.598805 sec  0.07    NPIX 8300 k : ?  find peaks
+ 0.599952 sec  0.07    NPIX 8300 k : ?  find peaks 
+ 0.723508 sec  0.09    NPIX 8300 k : ?  found footprints: 
+ 1.4      sec  0.17    NPIX 8300 k : ?  found grown footprints: 
+ 0.809106 sec 12.64    NPSF    64  : ?  psf fit
+ 2.155031 sec 34.21    NPSF    63  : ?  full fit
+ 2.592620 sec 74.07    NPSF    35  : ?  psf residuals
+ 5.931090 sec  0.71   sNPIX 8300 k : ?  smoothed signficance
+ 7.780419 sec  0.94   sNPIX 8300 k : ?  smoothed signficance
+12.947970 sec  1.56   sNPIX 8300 k : ?  convolved with grow disc: 
+14.525486 sec  1.75   sNPIX 8300 k : ?  convolved with grow disc: 
+
+Notes:
+
+Note:
+
+* PSF Residuals is the most expensive per item, but will always be
+  performed on a limited number of items (typically < 300 PSF stars).
+  The maximum impact is thus limited.
+
+* The non-linear fitting is (not surprisingly) very expensive per
+  item, but again will only be performed on a limited subset (S/N >
+  20?).
+
+* the linear fits are the next most costly, and will dominate the
+  timing since all sources must be so fit, and multiple times.
+
+* measure magnitudes is expensive, but only runs once
+
+* measure moments is expensive, but only runs once (and/or on a bright subset?)
+
+Adding it up:
+
+GPC1 : 10^11 det for 60*5000* 60 chips --> 5500 det / chip
+     : 23 megapix
+
+* cost per object:
+  
+  56.5 msec -> 310 sec for detection analysis (on kawelu, not optimized)
+
+* cost for bright detections
+
+  ~ 35 sec (for 1000 bright sources)
+
+* cost per megapixel:
+
+  5.5 sec
+
+  -> 126 sec for GPC1
+
+* other overhead (psf construction, background, smoothing)
+
+  -> 20 sec
+
+** total : 490 sec (kawelu, not optimized, 1.0GHz!)
+
+** ~ 220 sec optimized 
+** ~ 100 sec on 2.3GHz machines
+(probably there are other gains on the real hardware: memory bandwidth, ?)
+
+assuming 100sec is all we get, we need ~1.5 cores per chip to keep up at 60sec / exposure (average)
+
+120 cores for ppImage (chip)
+
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/timing.v3.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/timing.v3.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/timing.v3.txt	(revision 22322)
@@ -0,0 +1,39 @@
+0.000408 sec    found 0 satstar blend peaks, leaving 551 sources: 
+0.002058 sec    measure source sizes for 551 sources: 
+0.003553 sec    measure source sizes for 1302 sources: 
+0.009676 sec    identified 36 blended objects: 
+0.026266 sec    replaced models for 551 objects: 
+0.026892 sec    sub noise for 551 objects: 
+0.026924 sec    add noise for 551 objects: 
+0.029050 sec    replaced models for 551 objects: 
+0.032975 sec    set footprint array IDs: 
+0.047061 sec    set footprint array IDs: 
+0.080533 sec    rough classification: 
+0.107024 sec    replace background flux : 
+0.111200 sec    found 631 footprints: 
+0.132417 sec    rough classification: 
+0.182467 sec    subtracted background model: 
+0.2      sec    cull peaks
+0.297289 sec    2676 peaks: 
+0.3      sec    rest of foot
+0.300492 sec    849 peaks: 
+0.364908 sec    found 7613 footprints: 
+0.393265 sec    measure ensemble of PSFs: 
+0.405241 sec    build median image: 
+0.454711 sec    measure ensemble of PSFs: 
+0.680027 sec    measure ensemble of PSFs: 
+0.900696 sec    751 sources, 290 moments, 9 failed: 
+1.1 sec    rest of footpr
+1.147541 sec    built models for 551 objects: 
+1.289976 sec    generate residuals for 35 objects: 
+1.456877 sec    built models for 751 objects: 
+1.5      sec    select psf model (ex resid)
+1.661166 sec    551 sources, 551 moments, 8 failed: 
+2.988601 sec    built smoothed signficance image: 
+3.677652 sec    built smoothed signficance image: 
+4.354904 sec    measure magnitudes :  for 1302 objects (634 with apertures)
+5.8 sec    cull peaks
+6.594287 sec    convolved with grow disc: 
+7.413455 sec    convolved with grow disc: 
+7.616990 sec    fit models:  for 417 objects (354 psf, 31 ext, 32 failed, 134 skipped)
+55.393567 sec    complete psphot run: 
Index: /tags/sj_tags/sj_root_20080929/psphot/doc/versions.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/doc/versions.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/doc/versions.txt	(revision 22322)
@@ -0,0 +1,49 @@
+
+2006.02.02
+  I'm tagging psphot after I updated it to work with the readouts
+  using the structures being used for ppImage.  In this release, I
+  have also moved to autoconf for psphot, and the psphotReadout
+  portion is being defined as a library against which the other
+  elements are linked.  It should be possible to use this library with
+  ppImage, but I have not yet tested that.  
+
+  link psphot_dev_08 against psLib:eam_rel9_b2 and
+  psModule:eam_rel9_b2.
+
+2006.01.18
+  the tag below (psphot_dev_07) was moved to a later release.  It
+  should be used with psLib:eam_rel9_b1 from 2006/1/18 and
+  psModule:eam_rel9_p0.  
+
+2005.12.23
+  psphot_dev_07 should be compiled against psLib:eam_rel9_b1 and
+  psModule:eam_rel9_b1.  This version now handles the ensemble PSF
+  fitting, detection of blended sources, fitting of 'blends' (groups
+  of PSFs with distinct be 'blended' peaks) and 'blobs' (unresolved
+  extended objects, with a test for double PSF vs extended source
+  model.
+
+2005.11.25
+
+  psphot_dev_05 should be compiled against psLib tag eam_rel8_b2 and
+  psModules tag eam_rel8_b1.  this version removes all _EAM versions
+  of psLib and psModules code.  It now uses the psModules version of
+  the object code.
+
+  psphot_dev_04 should be compiled against psLib tag eam_rel8_b2 this
+  version removes all _EAM versions of psLib code, so that it is now
+  consistent with psLib.v8 (fixes are pushed into the eam_rel8_b2
+  branch).
+
+2005.11.16
+
+  psphot_dev_03 should be compiled against psLib tag eam_rel8_b1, a
+  branch starting from psLib rel8_0.  I needed to make some minor
+  fixes in psLib to work successfully with psLib rel8_0.
+
+  psphot_dev_02 was getting segfaults from a bug added into psLib in
+  rel8_0 in p_psVectorClippedStats.  I also fixed some errors arising
+  from changing types (argument to psMetadataParseConfig).
+
+
+
Index: /tags/sj_tags/sj_root_20080929/psphot/psphot-config.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/psphot-config.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/psphot-config.in	(revision 22322)
@@ -0,0 +1,87 @@
+#! /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+top_srcdir=@ABS_SRCDIR@
+
+usage()
+{
+    cat <<EOF
+Usage: psphot-config [OPTION]
+
+Known values for OPTION are:
+
+  --prefix		print psphot installation prefix
+  --libs		print library linking information
+  --cflags		print pre-processor and compiler flags
+  --build-libs		print library linking information to the build (non-installed) version
+  --build-cflags	print pre-processor and compiler flags to the build (non-installed) version
+  --help		display this help and exit
+  --version		output version information
+
+EOF
+
+    exit $1
+}
+
+if test $# -eq 0; then
+    usage 1
+fi
+
+cflags=false
+libs=false
+
+while test $# -gt 0; do
+    case "$1" in
+    -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+    *) optarg= ;;
+    esac
+
+    case "$1" in
+    --prefix=*)
+	prefix=$optarg
+	;;
+
+    --prefix)
+	echo $prefix
+	;;
+
+    --version)
+	echo @VERSION@
+	exit 0
+	;;
+
+    --help)
+	usage 0
+	;;
+
+    --cflags)
+       	echo -I${includedir} @PSPHOT_CFLAGS@
+       	;;
+
+    --libs)
+       	echo -L${libdir} -lpsphot @PSPHOT_LIBS@
+       	;;
+
+    --build-cflags)
+       	echo @SRCINC@ @PSPHOT_CFLAGS@
+       	;;
+
+    --build-libs)
+       	echo -L@ABS_SRCDIR@/src/.libs -lpsphot @PSPHOT_LIBS@
+       	;;
+
+    --deps)
+       	echo @PSPHOT_LIBS@
+       	;;
+    *)
+	usage
+	exit 1
+	;;
+    esac
+    shift
+done
+
+exit 0
Index: /tags/sj_tags/sj_root_20080929/psphot/psphot.pc.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/psphot.pc.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/psphot.pc.in	(revision 22322)
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libpsphot
+Description: Pan-STARRS Photometry Library
+Version: @VERSION@
+Requires: pslib psmodules
+Libs: -L${libdir} -lpsphot
+Libs.private: @PSPHOT_LIBS@
+Cflags: -I${includedir}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/.cvsignore	(revision 22322)
@@ -0,0 +1,18 @@
+*.o
+*.lo
+.libs
+.deps
+Makefile
+Makefile.in
+libpsphot.la
+psphot
+psphotTest
+psphot.loT
+psphotErrorCodes.h
+psphotErrorCodes.c
+config.h
+config.h.in
+stamp-h1
+
+polyfitTest
+growthTest
Index: /tags/sj_tags/sj_root_20080929/psphot/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/Makefile.am	(revision 22322)
@@ -0,0 +1,106 @@
+lib_LTLIBRARIES = libpsphot.la
+libpsphot_la_CFLAGS = $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+libpsphot_la_LDFLAGS = $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+
+bin_PROGRAMS = psphot
+psphot_CFLAGS = $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+psphot_LDFLAGS = $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+psphot_LDADD = libpsphot.la
+
+psphot_SOURCES = \
+        psphot.c                \
+	psphotArguments.c	\
+	psphotCleanup.c	   	\
+	psphotImageLoop.c	\
+	psphotMosaicChip.c	\
+	psphotSetMaskBits.c	\
+	psphotParseCamera.c
+
+libpsphot_la_SOURCES = \
+	psphotErrorCodes.c	       \
+	psphotCullPeaks.c	       \
+	psphotVersion.c		       \
+	psphotModelGroupInit.c	       \
+	psphotMaskReadout.c	       \
+	psphotDefineFiles.c	       \
+	psphotReadout.c		       \
+	psphotModelBackground.c	       \
+	psphotSubtractBackground.c     \
+	psphotFindDetections.c	       \
+	psphotFindPeaks.c	       \
+	psphotFindFootprints.c	       \
+	psphotSignificanceImage.c      \
+	psphotSourceStats.c	       \
+	psphotRoughClass.c	       \
+	psphotBasicDeblend.c	       \
+	psphotChoosePSF.c	       \
+	psphotGuessModels.c            \
+	psphotFitSourcesLinear.c       \
+	psphotBlendFit.c	       \
+	psphotReplaceUnfit.c	       \
+	psphotApResid.c		       \
+	psphotMagnitudes.c	       \
+	psphotSkyReplace.c	       \
+	psphotEvalPSF.c		       \
+	psphotEvalFLT.c		       \
+	psphotSourceFits.c	       \
+	psphotRadiusChecks.c	       \
+	psphotOutput.c		       \
+	psphotFakeSources.c	       \
+	psphotModelWithPSF.c           \
+	psphotExtendedSourceAnalysis.c \
+	psphotExtendedSourceFits.c     \
+	psphotRadialProfile.c	       \
+	psphotPetrosian.c	       \
+	psphotIsophotal.c	       \
+	psphotAnnuli.c		       \
+	psphotKron.c		       \
+	psphotKernelFromPSF.c	       \
+	psphotPSFConvModel.c	       \
+	psphotModelTest.c	       \
+	psphotFitSet.c		       \
+	psphotSourceFreePixels.c       \
+	psphotSummaryPlots.c           \
+	psphotMergeSources.c	       \
+	psphotLoadPSF.c	               \
+	psphotReadoutCleanup.c	       \
+	psphotSourcePlots.c	       \
+	psphotRadialPlot.c	       \
+	psphotDeblendSatstars.c	       \
+	psphotMosaicSubimage.c	       \
+	psphotMakeResiduals.c	       \
+	psphotSourceSize.c	       \
+	psphotDiagnosticPlots.c	       \
+	psphotMakeFluxScale.c	       \
+	psphotCheckStarDistribution.c  \
+	psphotAddNoise.c
+
+# dropped? psphotGrowthCurve.c
+
+include_HEADERS = \
+	psphot.h \
+	psphotErrorCodes.h
+
+noinst_HEADERS = \
+	psphotInternal.h \
+	psphotStandAlone.h
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
+# Error codes.
+BUILT_SOURCES = psphotErrorCodes.h psphotErrorCodes.c
+CLEANFILES = psphotErrorCodes.h psphotErrorCodes.c
+EXTRA_DIST = psphotErrorCodes.dat psphotErrorCodes.c.in psphotErrorCodes.h.in \
+	models/pmModel_STRAIL.c \
+	models/pmModel_TEST1.c
+
+psphotErrorCodes.h : psphotErrorCodes.dat psphotErrorCodes.h.in
+	$(ERRORCODES) --data=psphotErrorCodes.dat --outdir=. psphotErrorCodes.h
+
+psphotErrorCodes.c : psphotErrorCodes.dat psphotErrorCodes.c.in psphotErrorCodes.h
+	$(ERRORCODES) --data=psphotErrorCodes.dat --outdir=. psphotErrorCodes.c
Index: /tags/sj_tags/sj_root_20080929/psphot/src/models/pmModel_STRAIL.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/models/pmModel_STRAIL.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/models/pmModel_STRAIL.c	(revision 22322)
@@ -0,0 +1,618 @@
+
+/******************************************************************************
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = 1 / SigmaX;
+    params->data.F32[5] = 1 / SigmaY;
+    params->data.F32[6] = Sxy;
+    params->data.F32[7] = length;
+    params->data.F32[8] = theta;
+*****************************************************************************/
+
+# define PM_MODEL_FUNC            pmModelFunc_STRAIL
+# define PM_MODEL_FLUX            pmModelFlux_STRAIL
+# define PM_MODEL_GUESS           pmModelGuess_STRAIL
+# define PM_MODEL_LIMITS          pmModelLimits_STRAIL
+# define PM_MODEL_RADIUS          pmModelRadius_STRAIL
+# define PM_MODEL_FROM_PSF        pmModelFromPSF_STRAIL
+# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_STRAIL
+# define PM_MODEL_FIT_STATUS      pmModelFitStatus_STRAIL
+
+psF32 PM_MODEL_FUNC(psVector *deriv,
+                         const psVector *params,
+                         const psVector *x)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 trailLength = PAR[7];
+    psF32 theta = PAR[8];
+
+    psF32 x0 = PAR[2];  //streak center
+    psF32 y0 = PAR[3];  //streak center
+
+    //S values (1/sigma for x and y case, sigma for xy case)
+    psF32 sx = PAR[4];
+    psF32 sy = PAR[5];
+    psF32 sxy = PAR[6];
+
+    psF32 sinT=sin(theta);
+    psF32 cosT=cos(theta);
+    psF32 sin2T=sin(2.0*theta);
+    psF32 cos2T=cos(2.0*theta);
+
+    //    printf("Trying object at %4.1f,%4.1f with length %3.1f and angle %1.3f\r", x0, y0, length, theta);
+
+    //current location relative to trail center
+    psF32 X  = x->data.F32[0] - x0;
+    psF32 Y  = x->data.F32[1] - y0;
+
+    //x' and y' location (trail-orienter coords)
+    psF32 xs = X*cosT + Y*sinT;
+    psF32 ys = -1.0*X*sinT + Y*cosT;
+
+    //initialize variables to be changed below
+    psF32 x1 = 0;
+    psF32 y1 = 0;
+    psF32 px = 0;
+    psF32 py = 0;
+    psF32 z  = 0;
+    psF32 zx = 0;
+    psF32 t  = 0;
+    psF32 tx = 0;
+    psF32 r  = 0;
+    psF32 rx = 0;
+    psF32 f  = 0;
+
+    psF32 sxrot = 0;
+    psF32 syrot = 0;
+    psF32 sxyrot = 0;
+    psF32 dsxrot = 0;
+    psF32 dsyrot = 0;
+    psF32 dsxyrot = 0;
+
+    //    psF32 Rx = 0;
+    //    psF32 Ry = 0;
+    //    psF32 Rxy = 0;
+
+
+    //calculate new S values (1/sigma) for rotated frame
+    psF32 sxrotsq = PS_SQR(cosT*sx) + PS_SQR(sinT*sy) + cosT*sinT*sxy;
+    psF32 syrotsq = PS_SQR(cosT*sy) + PS_SQR(sinT*sx) - cosT*sinT*sxy;
+
+    //    psF32 testtwo=10.1;
+    //    psF32 testone=fabsf(testtwo);
+    //    fprintf (stderr, "Test: %f is the absolute value of %f?\n",testone,testtwo);
+    if (sxrotsq<0) {
+      sxrot = sqrt(-(sxrotsq));
+      syrot = sqrt(syrotsq);
+      fprintf (stderr, "error in sxrotsq: Neg,  sxrotsq=%f sx=%f sy=%f sxy=%f theta=%f\n",sxrotsq,sx,sy,sxy,theta);
+    } else if (syrotsq<0) {
+      sxrot = sqrt(sxrotsq);
+      syrot = sqrt(-(syrotsq));
+      fprintf (stderr, "error in syrotsq: Neg,  syrotsq=%f sx=%f sy=%f sxy=%f theta=%f\n",syrotsq,sx,sy,sxy,theta);
+    } else if (sxrotsq==0){
+      sxrot = 0.01;
+      syrot = sqrt(syrotsq);
+      fprintf (stderr, "error in sxrotsq: Zero,  sxrotsq=%f \n",sxrotsq);
+    } else if (syrotsq==0) {
+      syrot = 0.01;
+      sxrot = sqrt(sxrotsq);
+      fprintf (stderr, "error in syrotsq: Zero,  syrotsq=%f \n",syrotsq);
+      //      return(0);
+    }else {
+      sxrot = sqrt(sxrotsq);
+      syrot = sqrt(syrotsq);
+    }
+
+    sxyrot = sxy*cos2T + (PS_SQR(sy) - PS_SQR(sx))*sin2T;
+
+      if (isnan(sxrot)) {
+        fprintf (stderr, "error in sxrot  syrot=%f sx=%f sy=%f sxy=%f cosT=%f sinT=%f\n",syrot,sx,sy,sxy,cosT,sinT);
+      } else if (isnan(syrot)) {
+        fprintf (stderr, "error in syrot  sxrot=%f sx=%f sy=%f sxy=%f cosT=%f sinT=%f\n",sxrot,sx,sy,sxy,cosT,sinT);
+      }
+
+    //calculate length of pipe (not of trail motion)
+    psF32 length = trailLength - 2.0*2.0/sxrot;
+
+
+    if (xs < length/(-2.0)) {
+      x1 = (xs+length/2.0)*cosT - ys*sinT; //Endpoint1
+      y1 = (xs+length/2.0)*sinT + ys*cosT; //Endpoint1
+      //1.6 factor comes from by-eye testing of fits, sqrt from the later squaring of it
+      //1.6 ~= phi (golden mean)...coincidence?
+      px = sxrot*x1/sqrt(1.6);
+      py = syrot*y1;
+
+      //first find out what the falloff in the x direction is...
+      zx  = 0.5*PS_SQR(px);
+      tx = 1 + zx + zx*zx/2.0 + zx*zx*zx/6.0;
+      rx = 1.0 / (tx + zx*zx*zx*zx/24.0);
+
+      //...and now in the y direction
+      z  = 0.5*PS_SQR(py)+sxyrot*x1*y1;
+      t  = 1 + z + z*z/2.0;
+      r  = 1.0 / (t + z*z*z/6.0); /* exp (-Z) */
+      f  = PAR[1]*rx*r + PAR[0];
+
+    } else if (xs > length/2.0){
+      x1 = (xs-length/2.0)*cosT - ys*sinT; //Endpoint2
+      y1 = (xs-length/2.0)*sinT + ys*cosT; //Endpoint2
+      px = sxrot*x1/sqrt(1.6);
+      py = syrot*y1;
+      zx  = 0.5*PS_SQR(px);
+      tx = 1 + zx + zx*zx/2.0 + zx*zx*zx/6.0;
+      rx = 1.0 / (tx + zx*zx*zx*zx/24.0);
+
+      z  = 0.5*PS_SQR(py)+sxyrot*x1*y1;
+      t  = 1 + z + z*z/2.0;
+      r  = 1.0 / (t + z*z*z/6.0); /* exp (-Z) */
+      f  = PAR[1]*rx*r + PAR[0];
+
+      if (isnan(r)) {
+        fprintf (stderr, "error in +r  t=%f z=%f\n",t,z);
+      }
+
+    } else {
+      x1 = -ys*sinT;
+      y1 = ys*cosT;
+      px = sx*x1;
+      py = sy*y1;
+      z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + sxy*x1*y1;
+      t  = 1 + z + z*z/2.0;
+      r  = 1.0 / (t + z*z*z/6.0); /* exp (-Z) */
+      rx = 1.0;  //so that dF/dF0 can be generalized
+      f  = PAR[1]*r + PAR[0];
+
+      if (isnan(r)) {
+        fprintf (stderr, "error in midr  t=%f z=%f\n",t,z);
+      }
+    }
+
+
+    //ok...so this is df/dPAR[X]
+    if (deriv != NULL) {
+        psF32 q = 1;
+        //stable
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r * rx;
+
+          if (isnan(deriv->data.F32[0])) {
+            fprintf (stderr, "error in deriv0\n");
+          } else if (isnan(deriv->data.F32[1])) {
+            fprintf (stderr, "error in deriv1 r=%f rx=%f\n",r,rx);
+          }
+
+        dsxrot = 2.0*PS_SQR(sy)*sinT*cosT - 2.0*PS_SQR(sx)*sinT*cosT + sxy*(PS_SQR(cosT)-PS_SQR(sinT));
+        dsyrot = 2.0*PS_SQR(sx)*sinT*cosT - 2.0*PS_SQR(sy)*sinT*cosT - sxy*(PS_SQR(cosT)-PS_SQR(sinT));
+        dsxyrot = 2.0*cos2T*(PS_SQR(sy)-PS_SQR(sx)) - 2.0*sxy*sin2T;
+
+          if (isnan(dsxrot)) {
+            fprintf (stderr, "error in dsxrot\n");
+          } else if (isnan(dsyrot)) {
+            fprintf (stderr, "error in dsyrot\n");
+          } else if (isnan(dsxyrot)) {
+            fprintf (stderr, "error in dsxyrot\n");
+          }
+
+        //initialize variables definied in the if statements
+        //      psF32 XXX=0;
+        //      psF32 YYY=0;
+
+
+        // variable over piecewise func
+        // change the endcaps to be 4th order gaussians with sxrot_fit=1.6*sxrot
+        // y' direction can ge adequately modelled by a 3rd order gaussian with syrot
+        if (xs < length/(-2.0)) {
+
+          q=PAR[1]*r*rx;
+          deriv->data.F32[2] = -q*(rx*tx/1.6*x1*PS_SQR(sxrot) + r*t*y1*sxyrot);
+          deriv->data.F32[3] = -q*r*t*(y1*PS_SQR(syrot) + x1*sxyrot);
+          deriv->data.F32[4] = -q*(rx*tx*x1*sx*PS_SQR(cosT)/1.6*(x1+2.0*cosT/sxrot) + r*t*y1*sx*sinT*(y1*sinT+2.0*PS_SQR(syrot)*PS_SQR(cosT)/(sxrot*sxrot*sxrot)) + 2.0*r*t*sx*(-1.0*x1*y1*sin2T+y1*sxyrot*(cosT*cosT*cosT)/(sxrot*sxrot*sxrot)+x1*sxyrot*(sinT*cosT*cosT)/(sxrot*sxrot*sxrot)));
+          deriv->data.F32[5] = -q*(rx*tx*x1*sy*PS_SQR(sinT)/1.6*(x1+2.0*cosT/sxrot) + r*t*y1*sy*(y1*PS_SQR(cosT)+2.0*PS_SQR(syrot)*(sinT*sinT*sinT)/(sxrot*sxrot*sxrot)) + 2.0*r*t*sy*(x1*y1*sin2T+y1*sxyrot*(sinT*sinT*cosT)/(sxrot*sxrot*sxrot)+x1*sxyrot*(sinT*sinT*sinT)/(sxrot*sxrot*sxrot)));
+          deriv->data.F32[6] = -q*(rx*tx*x1*cosT*sinT/3.2*(x1+2.0*cosT/sxrot) + r*t*y1*cosT*sinT/2.0*(-y1+2.0*PS_SQR(syrot)*sinT/(sxrot*sxrot*sxrot)) + r*t*(x1*y1*cos2T+y1*sxyrot*sinT*cosT*cosT/(sxrot*sxrot*sxrot)+x1*sxyrot*sinT*sinT*cosT/(sxrot*sxrot*sxrot)));
+          deriv->data.F32[7] = -q*(rx*tx*PS_SQR(sxrot)*x1*cosT/3.2 + r*t*PS_SQR(syrot)*y1*sinT/2.0 + r*t*sxyrot/2.0*(y1*cosT+x1*sinT));
+          deriv->data.F32[8] = -q*(rx*tx*x1/3.2*(x1*(2.0*sinT*cosT*(PS_SQR(sy)-PS_SQR(sx))+sxy*(PS_SQR(cosT)-PS_SQR(sinT)))+PS_SQR(sxrot)*(2.0*cosT*(2.0*sinT*cosT*(PS_SQR(sy)-PS_SQR(sx))+sxy*(PS_SQR(cosT)-PS_SQR(sinT)))/(sxrot*sxrot*sxrot) - sinT*length)) + r*t*y1/2.0*(y1*(2.0*sinT*cosT*(PS_SQR(sx)-PS_SQR(sy))-sxy*(PS_SQR(cosT)-PS_SQR(sinT)))+PS_SQR(syrot)*(2.0*sinT*(2.0*sinT*cosT*(PS_SQR(sy)-PS_SQR(sx))+sxy*(PS_SQR(cosT)-PS_SQR(sinT)))/(sxrot*sxrot*sxrot)+cosT*length)) + r*t*(2.0*x1*y1*(cos2T*(PS_SQR(sy)-PS_SQR(sx))-sxy*sin2T)+y1*sxyrot/2.0*(2.0*cosT*(2.0*sinT*cosT*(PS_SQR(sy)-PS_SQR(sx))+sxy*(PS_SQR(cosT)-PS_SQR(sinT)))/(sxrot*sxrot*sxrot)-length*sinT)+x1*sxyrot/2.0*(2.0*sinT*(2.0*sinT*cosT*(PS_SQR(sy)-PS_SQR(sx))+sxy*(PS_SQR(cosT)-PS_SQR(sinT)))/(sxrot*sxrot*sxrot)+length*cosT)));
+
+          /*      if (isnan(deriv->data.F32[2])) {
+            fprintf (stderr, "error in deriv2\n");
+          } else if (isnan(deriv->data.F32[3])) {
+            fprintf (stderr, "error in deriv3\n");
+          } else if (isnan(deriv->data.F32[4])) {
+            fprintf (stderr, "error in deriv4\n");
+          } else if (isnan(deriv->data.F32[5])) {
+            fprintf (stderr, "error in deriv5\n");
+          } else if (isnan(deriv->data.F32[6])) {
+            fprintf (stderr, "error in deriv6\n");
+          } else if (isnan(deriv->data.F32[7])) {
+            fprintf (stderr, "error in deriv7\n");
+          } else if (isnan(deriv->data.F32[8])) {
+            fprintf (stderr, "error in deriv8\n");
+          }
+          */
+
+
+
+        } else if (xs > length/2.0){
+
+          q=PAR[1]*r*rx;
+          deriv->data.F32[2] = -q*(rx*tx/1.6*x1*PS_SQR(sxrot) + r*t*y1*sxyrot);
+          deriv->data.F32[3] = -q*r*t*(y1*PS_SQR(syrot) + x1*sxyrot);
+          deriv->data.F32[4] = -q*(rx*tx*x1*sx*PS_SQR(cosT)/1.6*(x1-2.0*cosT/sxrot) + r*t*y1*sx*sinT*(y1*sinT-2.0*PS_SQR(syrot)*PS_SQR(cosT)/(sxrot*sxrot*sxrot)) + 2.0*r*t*sx*(-1.0*x1*y1*sin2T-y1*sxyrot*(cosT*cosT*cosT)/(sxrot*sxrot*sxrot)-x1*sxyrot*(sinT*cosT*cosT)/(sxrot*sxrot*sxrot)));
+          deriv->data.F32[5] = -q*(rx*tx*x1*sy*PS_SQR(sinT)/1.6*(x1-2.0*cosT/sxrot) + r*t*y1*sy*(y1*PS_SQR(cosT)-2.0*PS_SQR(syrot)*(sinT*sinT*sinT)/(sxrot*sxrot*sxrot)) + 2.0*r*t*sy*(x1*y1*sin2T-y1*sxyrot*(sinT*sinT*cosT)/(sxrot*sxrot*sxrot)-x1*sxyrot*(sinT*sinT*sinT)/(sxrot*sxrot*sxrot)));
+          deriv->data.F32[6] = -q*(rx*tx*x1*cosT*sinT/3.2*(x1-2.0*cosT/sxrot) + r*t*y1*cosT*sinT/2.0*(-y1-2.0*PS_SQR(syrot)*sinT/(sxrot*sxrot*sxrot)) + r*t*(x1*y1*cos2T-y1*sxyrot*sinT*cosT*cosT/(sxrot*sxrot*sxrot)-x1*sxyrot*sinT*sinT*cosT/(sxrot*sxrot*sxrot)));
+          deriv->data.F32[7] = q*(rx*tx*PS_SQR(sxrot)*x1*cosT/3.2 + r*t*PS_SQR(syrot)*y1*sinT/2.0 + r*t*sxyrot/2.0*(y1*cosT+x1*sinT));
+          deriv->data.F32[8] = -q*(rx*tx*x1/3.2*(x1*(2.0*sinT*cosT*(PS_SQR(sy)-PS_SQR(sx))+sxy*(PS_SQR(cosT)-PS_SQR(sinT)))-PS_SQR(sxrot)*(2.0*cosT*(2.0*sinT*cosT*(PS_SQR(sy)-PS_SQR(sx))+sxy*(PS_SQR(cosT)-PS_SQR(sinT)))/(sxrot*sxrot*sxrot) - sinT*length)) + r*t*y1/2.0*(y1*(2.0*sinT*cosT*(PS_SQR(sx)-PS_SQR(sy))-sxy*(PS_SQR(cosT)-PS_SQR(sinT)))-PS_SQR(syrot)*(2.0*sinT*(2.0*sinT*cosT*(PS_SQR(sy)-PS_SQR(sx))+sxy*(PS_SQR(cosT)-PS_SQR(sinT)))/(sxrot*sxrot*sxrot)+cosT*length)) + r*t*(2.0*x1*y1*(cos2T*(PS_SQR(sy)-PS_SQR(sx))-sxy*sin2T)-y1*sxyrot/2.0*(2.0*cosT*(2.0*sinT*cosT*(PS_SQR(sy)-PS_SQR(sx))+sxy*(PS_SQR(cosT)-PS_SQR(sinT)))/(sxrot*sxrot*sxrot)-length*sinT)-x1*sxyrot/2.0*(2.0*sinT*(2.0*sinT*cosT*(PS_SQR(sy)-PS_SQR(sx))+sxy*(PS_SQR(cosT)-PS_SQR(sinT)))/(sxrot*sxrot*sxrot)+length*cosT)));
+
+
+          /*      if (isnan(deriv->data.F32[2])) {
+            fprintf (stderr, "error in deriv2\n");
+          } else if (isnan(deriv->data.F32[3])) {
+            fprintf (stderr, "error in deriv3\n");
+          } else if (isnan(deriv->data.F32[4])) {
+            fprintf (stderr, "error in deriv4\n");
+          } else if (isnan(deriv->data.F32[5])) {
+            fprintf (stderr, "error in deriv5\n");
+          } else if (isnan(deriv->data.F32[6])) {
+            fprintf (stderr, "error in deriv6\n");
+          } else if (isnan(deriv->data.F32[7])) {
+            fprintf (stderr, "error in deriv7\n");
+          } else if (isnan(deriv->data.F32[8])) {
+            fprintf (stderr, "error in deriv8\n");
+          }
+          */
+
+        } else {
+          // this does not change from before, as the y' falloff can be modelled by the standard 3rd order gaussian
+          // note difference from a pure gaussian: q = PAR[1]*r
+          q = PAR[1]*r*r*t;
+          deriv->data.F32[2] = q*(PS_SQR(sx*sinT)*x1 - PS_SQR(sy)*y1*cosT*sinT - sxy*x1*sinT*cosT + sxy*y1*PS_SQR(sinT));
+          deriv->data.F32[3] = q*(-1*PS_SQR(sx)*x1*sinT*cosT + PS_SQR(sy*cosT)*y1 + sxy*x1*PS_SQR(cosT) - sxy*y1*sinT*cosT);
+          deriv->data.F32[4] = -q*sx*PS_SQR(x1);
+          deriv->data.F32[5] = -q*sy*PS_SQR(y1);
+          deriv->data.F32[6] = -q*x1*y1;
+          deriv->data.F32[7] = 0;
+          deriv->data.F32[8] = -q*( PS_SQR(sx)*x1*(xs*sinT - ys*cosT) + PS_SQR(sy)*y1*(xs*cosT - ys*sinT) + sxy*x1*(xs*cosT - ys*sinT) + sxy*y1*(xs*sinT - ys*cosT) );
+
+          if (isnan(deriv->data.F32[2])) {
+            fprintf (stderr, "error in deriv2\n");
+          } else if (isnan(deriv->data.F32[3])) {
+            fprintf (stderr, "error in deriv3\n");
+          } else if (isnan(deriv->data.F32[4])) {
+            fprintf (stderr, "error in deriv4\n");
+          } else if (isnan(deriv->data.F32[5])) {
+            fprintf (stderr, "error in deriv5\n");
+          } else if (isnan(deriv->data.F32[6])) {
+            fprintf (stderr, "error in deriv6\n");
+          } else if (isnan(deriv->data.F32[7])) {
+            fprintf (stderr, "error in deriv7\n");
+          } else if (isnan(deriv->data.F32[8])) {
+            fprintf (stderr, "error in deriv8\n");
+          }
+
+
+        }
+    }
+    return(f);
+}
+
+//fixed
+// XXX this needs to apply the axis ratio limits to prevent avoid solutions
+# define AR_MAX 20.0
+# define AR_RATIO 0.99
+bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta) {
+
+    float beta_lim = 0;
+    float params_min = 0;
+    float params_max = 0;
+    float f1, f2, q1;
+    float q2 = 0;
+
+    // we need to calculate the limits for SXY specially
+    if (nParam == PM_PAR_SXY) {
+        f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
+        assert (q1 > 0);
+        q2  = 0.5*sqrt (q1);
+    }
+
+    switch (mode) {
+      case PS_MINIMIZE_BETA_LIMIT:
+        switch (nParam) {
+          case PM_PAR_SKY:  beta_lim = 1000;   break;
+          case PM_PAR_I0:   beta_lim = 10000;    break; // too small?
+          case PM_PAR_XPOS: beta_lim = 50;     break;
+          case PM_PAR_YPOS: beta_lim = 50;     break;
+          case PM_PAR_SXX:  beta_lim = 0.5;    break;
+          case PM_PAR_SYY:  beta_lim = 0.5;    break;
+          case PM_PAR_SXY:  beta_lim = 1.0;    break;  // set this to q2?
+          case 7:           beta_lim = 10.0;     break;
+          case 8:           beta_lim = M_PI/6.0; break;
+
+          default:
+            psAbort("invalid parameter %d for beta test", nParam);
+        }
+        if (fabs(beta[nParam]) > fabs(beta_lim)) {
+            beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
+            return false;
+        }
+        return true;
+      case PS_MINIMIZE_PARAM_MIN:
+        switch (nParam) {
+          case PM_PAR_SKY:  params_min = -1000; break;
+          case PM_PAR_I0:   params_min =     0; break;
+          case PM_PAR_XPOS: params_min =  -100; break;
+          case PM_PAR_YPOS: params_min =  -100; break;
+          case PM_PAR_SXX:  params_min =   0.5; break;
+          case PM_PAR_SYY:  params_min =   0.5; break;
+          case PM_PAR_SXY:  params_min =  -5.0; break; // set this to -q2?
+          case 7:           params_min =     0;  break;
+          case 8:           params_min = -1*M_PI; break;
+
+          default:
+            psAbort("invalid parameter %d for param min test", nParam);
+        }
+        if (params[nParam] < params_min) {
+            params[nParam] = params_min;
+            return false;
+        }
+        return true;
+      case PS_MINIMIZE_PARAM_MAX:
+        switch (nParam) {
+          case PM_PAR_SKY:  params_max =   1e5; break;
+          case PM_PAR_I0:   params_max =   1e8; break;
+          case PM_PAR_XPOS: params_max =   1e4; break;
+          case PM_PAR_YPOS: params_max =   1e4; break;
+          case PM_PAR_SXX:  params_max =   100; break;
+          case PM_PAR_SYY:  params_max =   100; break;
+          case PM_PAR_SXY:  params_max =   +q2; break;
+          case 7:           params_max =   150; break;
+          case 8:           params_max =  M_PI; break;
+          default:
+            psAbort("invalid parameter %d for param max test", nParam);
+        }
+        if (params[nParam] > params_max) {
+            params[nParam] = params_max;
+            return false;
+        }
+        return true;
+      default:
+        psAbort("invalid choice for limits");
+    }
+    psAbort("should not reach here");
+    return false;
+}
+
+//fixed
+psF64 PM_MODEL_FLUX(const psVector *params)
+{
+    float f, norm, z;
+
+    psF32 *PAR = params->data.F32;
+
+    psF64 A1   = PS_SQR(PAR[4]);
+    psF64 A2   = PS_SQR(PAR[5]);
+    psF64 A3   = PS_SQR(PAR[6]);
+    psF32 Rx=2./PS_SQR(PAR[4]);
+    psF32 Ry=2./PS_SQR(PAR[5]);
+    psF32 Rxy=PAR[6];
+
+
+    psF32 theta = PAR[8];
+    psF32 sinT=sin(theta);
+    psF32 cosT=cos(theta);
+
+    psF32 Syrot = ( PS_SQR(sinT)/Rx + PS_SQR(cosT)/Ry - Rxy*sinT*cosT );  //rotated sigma y
+
+    psF64 A4   = Syrot*PAR[7];
+
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - A3) + A4;
+    // Area is equivalent to 2 pi sigma^2 + rectangle
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+    for (z = 0.005; z < 50; z += 0.01) {
+        f = 1.0 / (1 + z + z*z/2 + z*z*z/6);
+        norm += f;
+    }
+    norm *= 0.01;
+
+    psF64 Flux = params->data.F32[1] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// also prevent 0 returns, and just send a v small number
+// return the radius which yields the requested flux
+
+//fixed, but need to change how it is called to accomodate 2 radii
+psF64 PM_MODEL_RADIUS  (const psVector *params, psF64 flux)
+{
+    if (flux <= 0) return (1.0);
+    if (params->data.F32[1] <= 0) return (1.0);
+    if (flux >= params->data.F32[1]) return (1.0);
+
+    psF32 *PAR = params->data.F32;
+    psF32 sigma  = sqrt(2.0) * hypot (1.0 / params->data.F32[4], 1.0 / params->data.F32[5]);
+
+    psF32 theta = PAR[8];
+    psF32 sinT=sin(theta);
+    psF32 cosT=cos(theta);
+    psF32 Rx=2./PS_SQR(PAR[4]);
+    psF32 Ry=2./PS_SQR(PAR[5]);
+    psF32 Rxy=PAR[6];
+    psF32 length=PAR[7];
+
+    psF32 Syrot = ( PS_SQR(sinT)/Rx + PS_SQR(cosT)/Ry - Rxy*sinT*cosT );  //rotated sigma y
+
+    psF64 radius = 0;
+    if (flux > 0){
+      psF64 radius0 = sigma * sqrt (2.0 * log(params->data.F32[1] / flux));
+      psF64 radius1 = Syrot * sqrt (2.0 * log(params->data.F32[1] / flux));
+
+      if (radius0 > radius1) {
+        radius=radius0+length/2.0;
+      } else {
+        radius=radius1+length/2.0;
+      }
+    } else {
+      radius = 1000;
+    }
+
+    if (radius < 0.01){
+      radius = 0.01;
+    }
+
+    if (isnan(radius)) {
+      fprintf (stderr, "error in code\n");
+    }
+    return (radius);
+}
+
+//fixed I think...no good way of guessing as far as I can tell
+bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
+{
+    pmMoments *Smoments = source->moments;
+    psF32     *params  = model->params->data.F32;
+
+    psEllipseAxes axes;
+    psEllipseShape shape;
+    psEllipseMoments moments;
+
+    moments.x2 = PS_SQR(Smoments->Sx);
+    moments.y2 = PS_SQR(Smoments->Sy);
+    moments.xy = Smoments->Sxy;
+    //sometimes these moment inputs are zero...why?
+
+    // solve the math to go from Moments To Shape
+    // limit the axis ratio to 20 (a guess)
+    axes = psEllipseMomentsToAxes(moments, 20.0);
+    shape = psEllipseAxesToShape(axes);
+
+    params[0] = Smoments->Sky;
+    params[1] = Smoments->Peak - Smoments->Sky;
+    params[2] = Smoments->x;
+    params[3] = Smoments->y;
+    params[7] = 2 * axes.major;
+    params[8] = axes.theta;
+
+    if (moments.x2 == 0 || moments.y2 == 0){
+      params[4] = 2.0;
+      params[5] = 2.0;
+      params[6] = 0.0;
+    } else {
+      params[4] = 1.0 / shape.sx;
+      params[5] = 1.0 / shape.sy;
+      params[6] = shape.sxy;
+    }
+
+    //    printf("Who is NaN? momx: %4.3f  momy: %4.3f  momxy: %4.3f\n", moments.x2,moments.y2, moments.xy);
+
+    return(true);
+}
+
+//fixed
+bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
+{
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+    out[7] = in[7];
+    out[8] = in[8];
+
+    for (int i = 4; i < 7; i++) {
+      pmTrend2D *trend = psf->params->data[i-4];
+        out[i] = pmTrend2DEval (trend, out[2], out[3]);
+    }
+    return(true);
+}
+
+// construct the PSF model from the FLT model and the psf
+// XXX is this sufficiently general do be a global function, not a pmModelClass function?
+bool PM_MODEL_PARAMS_FROM_PSF (pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io)
+{
+    psF32 *PAR = model->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = Io;
+    PAR[PM_PAR_XPOS] = Xo;
+    PAR[PM_PAR_YPOS] = Yo;
+
+    // supply the model-fitted parameters, or copy from the input
+    for (int i = 0; i < psf->params->n; i++) {
+        if (i == PM_PAR_SKY) continue;
+        pmTrend2D *trend = psf->params->data[i];
+        PAR[i] = pmTrend2DEval(trend, Xo, Yo);
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    // XXX user-defined value for limit?
+    if (!pmPSF_FitToModel (PAR, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, PAR, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, PAR, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)", Xo, Yo);
+            model->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+//done I think
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[4] / PAR[4]);
+    dP += PS_SQR(dPAR[5] / PAR[5]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[1] > 0);
+    status &= ((dPAR[1]/PAR[1]) < 0.5);
+    //    status &= ((dPAR[7]/PAR[7]) < 0.5);
+    //    status &= ((dPAR[8]/PAR[8]) < 0.5);
+
+    if (status) return true;
+    return false;
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_PARAMS_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psphot/src/models/pmModel_TEST1.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/models/pmModel_TEST1.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/models/pmModel_TEST1.c	(revision 22322)
@@ -0,0 +1,311 @@
+/******************************************************************************
+ * this file defines the TEST1 source shape model.  Note that these model functions are loaded
+ * by pmModelGroup.c using 'include', and thus need no 'include' statements of their own.  The
+ * models use a psVector to represent the set of parameters, with the sequence used to specify
+ * the meaning of the parameter.  The meaning of the parameters may thus vary depending on the
+ * specifics of the model.  All models which are used a PSF representations share a few
+ * parameters, for which # define names are listed in pmModel.h:
+
+ * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+ * PM_PAR_I0 1    - central intensity
+ * PM_PAR_XPOS 2  - X center of object
+ * PM_PAR_YPOS 3  - Y center of object
+ * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) * SigmaX)
+ * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) * SigmaY)
+ * PM_PAR_SXY 6   - X*Y term of elliptical contour
+ *****************************************************************************/
+
+# define PM_MODEL_FUNC            pmModelFunc_TEST1
+# define PM_MODEL_FLUX            pmModelFlux_TEST1
+# define PM_MODEL_GUESS           pmModelGuess_TEST1
+# define PM_MODEL_LIMITS          pmModelLimits_TEST1
+# define PM_MODEL_RADIUS          pmModelRadius_TEST1
+# define PM_MODEL_FROM_PSF        pmModelFromPSF_TEST1
+# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_TEST1
+# define PM_MODEL_FIT_STATUS      pmModelFitStatus_TEST1
+
+// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
+psF32 PM_MODEL_FUNC(psVector *deriv,
+                    const psVector *params,
+                    const psVector *pixcoord)
+{
+    psF32 *PAR = params->data.F32;
+
+    // XXX this is fitting sigma_x/sqrt(2), sigma_y/sqrt(2)
+    psF32 X  = pixcoord->data.F32[0] - PAR[PM_PAR_XPOS];
+    psF32 Y  = pixcoord->data.F32[1] - PAR[PM_PAR_YPOS];
+    psF32 px = X / PAR[PM_PAR_SXX];
+    psF32 py = Y / PAR[PM_PAR_SYY];
+    psF32 z  = PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y;
+    psF32 t  = 1 + z + z*z/2.0;
+    psF32 r  = 1.0 / (t + z*z*z/6.0); /* exp (-Z) */
+    psF32 f  = PAR[PM_PAR_I0]*r + PAR[PM_PAR_SKY];
+
+    if (deriv != NULL) {
+        psF32 *dPAR = deriv->data.F32;
+        psF32 q = PAR[PM_PAR_I0]*r*r*t;
+        dPAR[PM_PAR_SKY]  = +1.0;
+        dPAR[PM_PAR_I0]   = +r;
+        dPAR[PM_PAR_XPOS] = q*(2.0*px/PAR[PM_PAR_SXX] + Y*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_YPOS] = q*(2.0*py/PAR[PM_PAR_SYY] + X*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_SXX]  = +2.0*q*px*px/PAR[PM_PAR_SXX];
+        dPAR[PM_PAR_SYY]  = +2.0*q*py*py/PAR[PM_PAR_SYY];
+        dPAR[PM_PAR_SXY]  = -q*X*Y;
+    }
+    return(f);
+}
+
+// define the parameter limits
+bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
+{
+    float beta_lim = 0;
+    float params_min = 0;
+    float params_max = 0;
+
+    switch (mode) {
+      case PS_MINIMIZE_BETA_LIMIT:
+        switch (nParam) {
+          case PM_PAR_SKY:  beta_lim = 1000;  break;
+          case PM_PAR_I0:   beta_lim = 3e6;   break;
+          case PM_PAR_XPOS: beta_lim = 5;     break;
+          case PM_PAR_YPOS: beta_lim = 5;     break;
+          case PM_PAR_SXX:  beta_lim = 0.5;   break;
+          case PM_PAR_SYY:  beta_lim = 0.5;   break;
+          case PM_PAR_SXY:  beta_lim = 0.5;   break;
+          default:
+            psAbort("invalid parameter %d for beta test", nParam);
+        }
+        if (fabs(beta[nParam]) > fabs(beta_lim)) {
+            beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
+            return false;
+        }
+        return true;
+      case PS_MINIMIZE_PARAM_MIN:
+        switch (nParam) {
+          case PM_PAR_SKY:  params_min = -1000; break;
+          case PM_PAR_I0:   params_min =     0; break;
+          case PM_PAR_XPOS: params_min =  -100; break;
+          case PM_PAR_YPOS: params_min =  -100; break;
+          case PM_PAR_SXX:  params_min =   0.5; break;
+          case PM_PAR_SYY:  params_min =   0.5; break;
+          case PM_PAR_SXY:  params_min =  -5.0; break;
+          default:
+            psAbort("invalid parameter %d for param min test", nParam);
+        }
+        if (params[nParam] < params_min) {
+            params[nParam] = params_min;
+            return false;
+        }
+        return true;
+      case PS_MINIMIZE_PARAM_MAX:
+        switch (nParam) {
+          case PM_PAR_SKY:  params_max =   1e5; break;
+          case PM_PAR_I0:   params_max =   1e8; break;
+          case PM_PAR_XPOS: params_max =   1e4; break;
+          case PM_PAR_YPOS: params_max =   1e4; break;
+          case PM_PAR_SXX:  params_max =   100; break;
+          case PM_PAR_SYY:  params_max =   100; break;
+          case PM_PAR_SXY:  params_max =  +5.0; break;
+          default:
+            psAbort("invalid parameter %d for param max test", nParam);
+        }
+        if (params[nParam] > params_max) {
+            params[nParam] = params_max;
+            return false;
+        }
+        return true;
+      default:
+        psAbort("invalid choice for limits");
+    }
+    psAbort("should not reach here");
+    return false;
+}
+
+// make an initial guess for parameters
+bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
+{
+    pmMoments *moments = source->moments;
+    psF32     *PAR  = model->params->data.F32;
+
+    PAR[PM_PAR_SKY] = moments->Sky;
+    PAR[PM_PAR_I0] = moments->Peak - moments->Sky;
+    PAR[PM_PAR_XPOS] = moments->x;
+    PAR[PM_PAR_YPOS] = moments->y;
+    PAR[PM_PAR_SXX] = PS_MAX(0.5, moments->Sx);
+    PAR[PM_PAR_SYY] = PS_MAX(0.5, moments->Sy);
+    PAR[PM_PAR_SXY] = 0.0;  // XXX we can get this right if we do the integral
+
+    return(true);
+}
+
+psF64 PM_MODEL_FLUX(const psVector *params)
+{
+    float norm, z;
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    shape.sx  = PAR[PM_PAR_SXX] / sqrt(2.0);
+    shape.sy  = PAR[PM_PAR_SYY] / sqrt(2.0);
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    // Area is equivalent to 2 pi sigma^2
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+
+# define DZ 0.25
+
+    float f0 = 1.0;
+    float f1, f2;
+    for (z = DZ; z < 50; z += DZ) {
+        f1 = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
+        z += DZ;
+        f2 = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
+        norm += f0 + 4*f1 + f2;
+        f0 = f2;
+    }
+    norm *= DZ / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
+{
+    psEllipseShape shape;
+
+    if (flux <= 0)
+        return (1.0);
+    if (params->data.F32[PM_PAR_I0] <= 0)
+        return (1.0);
+    if (flux >= params->data.F32[PM_PAR_I0])
+        return (1.0);
+
+    psF32 *PAR = params->data.F32;
+
+    shape.sx  = PAR[PM_PAR_SXX] / sqrt(2.0);
+    shape.sy  = PAR[PM_PAR_SYY] / sqrt(2.0);
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    // this estimates the radius assuming f(z) is roughly exp(-z)
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 radius = axes.major * sqrt (2.0 * log(params->data.F32[PM_PAR_I0] / flux));
+
+    if (isnan(radius)) psAbort("error in code: never return invalid radius");
+    if (radius < 0) psAbort("error in code: never return invalid radius");
+
+    return (radius);
+}
+
+bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
+{
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    for (int i = 0; i < psf->params->n; i++) {
+        if (psf->params->data[i] == NULL) {
+            out[i] = in[i];
+        } else {
+            pmTrend2D *trend = psf->params->data[i];
+            out[i] = pmTrend2DEval(trend, in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+        }
+    }
+
+    // the 2D model for SXY actually fits SXY / (SXX^-2 + SYY^-2); correct here
+    out[PM_PAR_SXY] = pmPSF_SXYtoModel (out);
+
+    return(true);
+}
+
+// construct the PSF model from the FLT model and the psf
+// XXX is this sufficiently general do be a global function, not a pmModelClass function?
+bool PM_MODEL_PARAMS_FROM_PSF (pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io)
+{
+    psF32 *PAR = model->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = Io;
+    PAR[PM_PAR_XPOS] = Xo;
+    PAR[PM_PAR_YPOS] = Yo;
+
+    // supply the model-fitted parameters, or copy from the input
+    for (int i = 0; i < psf->params->n; i++) {
+        if (i == PM_PAR_SKY) continue;
+        pmTrend2D *trend = psf->params->data[i];
+        PAR[i] = pmTrend2DEval(trend, Xo, Yo);
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    // XXX user-defined value for limit?
+    if (!pmPSF_FitToModel (PAR, 0.1)) {
+        psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, PAR, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, PAR, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)", Xo, Yo);
+            model->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+// XXX double-check these definitions below
+// this test is invalid if the parameters are derived
+// from the PSF model
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
+    dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    if (status)
+        return true;
+    return false;
+}
+
+# undef PM_MODEL_FUNC
+# undef PM_MODEL_FLUX
+# undef PM_MODEL_GUESS
+# undef PM_MODEL_LIMITS
+# undef PM_MODEL_RADIUS
+# undef PM_MODEL_FROM_PSF
+# undef PM_MODEL_PARAMS_FROM_PSF
+# undef PM_MODEL_FIT_STATUS
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphot.c	(revision 22322)
@@ -0,0 +1,42 @@
+# include "psphotStandAlone.h"
+
+static void usage (void) {
+    fprintf (stderr, "USAGE: psphot [-file image(s)] [-list imagelist] (output)\n");
+    exit (PS_EXIT_CONFIG_ERROR);
+}
+
+int main (int argc, char **argv) {
+
+    psTimerStart ("complete");
+    pmErrorRegister();			// register psModule's error codes/messages
+    psphotErrorRegister();              // register our error codes/messages
+    psphotModelClassInit ();            // load implementation-specific models
+
+    // load command-line arguments, options, and system config data
+    pmConfig *config = psphotArguments (argc, argv);
+    if (!config) {
+	psErrorStackPrint(stderr, "Error reading arguments\n");
+	usage ();
+    }
+
+    // load input data (config and images (signal, noise, mask)
+    if (!psphotParseCamera (config)) {
+        psErrorStackPrint(stderr, "Error setting up the camera\n");
+        exit (psphotGetExitStatus());
+    }
+
+    // call psphot for each readout
+    if (!psphotImageLoop (config)) {
+        psErrorStackPrint(stderr, "Error in the psphot image loop\n");
+        exit (psphotGetExitStatus());
+    }
+
+    psLogMsg ("psphot", 3, "complete psphot run: %f sec\n", psTimerMark ("complete"));
+
+    psErrorCode exit_status = psphotGetExitStatus();
+    psphotCleanup (config);
+    exit (exit_status);
+}
+
+// all functions which return to this level must raise one of the top-level error codes if they
+// exit with an error.  these error codes are used to specify the program exit status
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphot.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphot.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphot.h	(revision 22322)
@@ -0,0 +1,183 @@
+/* This file defines the library functions available to external programs.  It must be included
+ * by programs which are compiled against psphot functions.
+ */
+
+#ifndef PSPHOT_H
+#define PSPHOT_H
+
+#include <psmodules.h>
+#include "psphotErrorCodes.h"
+
+#define PSPHOT_RECIPE "PSPHOT" // Name of the recipe to use
+
+#define PSPHOT_RECIPE_PSF_FAKE_ALLOW "PSF.FAKE.ALLOW" // Name for recipe component permitting fake PSFs
+
+
+// top-level psphot functions
+const char     *psphotCVSName(void);
+psString        psphotVersion(void);
+psString        psphotVersionLong(void);
+
+bool            psphotModelTest (pmConfig *config, const pmFPAview *view, psMetadata *recipe);
+bool            psphotReadout (pmConfig *config, const pmFPAview *view);
+bool            psphotReadoutCleanup (pmConfig *config, pmReadout *readout, psMetadata *recipe, pmDetections *detections, pmPSF *psf, psArray *sources);
+bool            psphotDefineFiles (pmConfig *config, pmFPAfile *input);
+bool            psphotSetMaskBits (pmConfig *config);
+
+// XXX test functions
+psArray        *psphotFakeSources ();
+
+// psphotReadout functions
+bool            psphotModelBackground (pmConfig *config, const pmFPAview *view, const char *filename);
+bool            psphotSubtractBackground (pmConfig *config, const pmFPAview *view, const char *filename) ;
+pmDetections   *psphotFindDetections (pmDetections *detections, pmReadout *readout, psMetadata *recipe);
+
+psArray        *psphotSourceStats (pmReadout *readout, psMetadata *recipe, pmDetections *detections);
+bool            psphotRoughClass (psArray *sources, psMetadata *recipe, const bool findPsfClump);
+bool            psphotBasicDeblend (psArray *sources, psMetadata *recipe);
+pmPSF          *psphotChoosePSF (pmReadout *readout, psArray *sources, psMetadata *recipe);
+bool            psphotPSFstats (pmReadout *readout, psMetadata *recipe, pmPSF *psf);
+bool            psphotMomentsStats (pmReadout *readout, psMetadata *recipe, psArray *sources);
+bool            psphotFitSourcesLinear (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, bool final);
+bool            psphotGuessModels (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf);
+bool            psphotBlendFit (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf);
+bool            psphotReplaceUnfitSources (psArray *sources, psMaskType maskVal);
+bool            psphotReplaceAllSources (psArray *sources, psMetadata *recipe);
+bool            psphotRemoveAllSources (psArray *sources, psMetadata *recipe);
+bool            psphotApResid (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf);
+bool            psphotMagnitudes (psArray *sources, psMetadata *recipe, pmPSF *psf, pmReadout *background);
+bool            psphotSkyReplace (pmConfig *config, const pmFPAview *view);
+bool            psphotExtendedSourceAnalysis (pmReadout *readout, psArray *sources, psMetadata *recipe);
+bool            psphotExtendedSourceFits (pmReadout *readout, psArray *sources, psMetadata *recipe);
+
+// used by psphotFindDetections
+psImage        *psphotSignificanceImage (pmReadout *readout, psMetadata *recipe, const int pass, psMaskType maskVal);
+psArray        *psphotFindPeaks (psImage *significance, pmReadout *readout, psMetadata *recipe, const float threshold, const int nMax);
+bool            psphotFindFootprints (pmDetections *detections, psImage *significance, pmReadout *readout, psMetadata *recipe, const int pass, psMaskType maskVal);
+psErrorCode     psphotCullPeaks(const psImage *img, const psImage *weight, const psMetadata *recipe, psArray *footprints);
+
+// used by ApResid
+bool            psphotMagErrorScale (float *errorScale, float *errorFloor, psVector *dMag, psVector *dap, psVector *mask, int nGroup);
+bool            psphotApResidTrend (pmReadout *readout, pmPSF *psf, int Npsf, int scale, float *errorScale, float *errorFloor, psVector *mask, psVector *xPos, psVector *yPos, psVector *apResid, psVector *dMag);
+
+// basic support functions
+void            psphotModelClassInit (void);
+bool            psphotGrowthCurve (pmReadout *readout, pmPSF *psf, bool ignore, psMaskType maskVal);
+bool            psphotSetMaskAndWeight (pmConfig *config, pmReadout *readout, psMetadata *recipe);
+void            psphotSourceFreePixels (psArray *sources);
+
+// functions to set the correct source pixels
+bool            psphotInitRadiusPSF (const psMetadata *recipe, const pmModelType type);
+bool            psphotCheckRadiusPSF (pmReadout *readout, pmSource *source, pmModel *model, psMaskType markVal);
+bool            psphotCheckRadiusPSFBlend (pmReadout *readout, pmSource *source, pmModel *model, psMaskType markVal, float dR);
+bool            psphotInitRadiusEXT (psMetadata *recipe, pmModelType type);
+bool            psphotCheckRadiusEXT (pmReadout *readout, pmSource *source, pmModel *model, psMaskType markVal);
+
+// output functions
+bool            psphotAddPhotcode (psMetadata *recipe, pmConfig *config, const pmFPAview *view, const char *filerule);
+bool            psphotDumpMoments (psMetadata *recipe, psArray *sources);
+psMetadata     *psphotDefineHeader (psMetadata *recipe);
+int             psphotSaveImage (psMetadata *header, psImage *image, char *filename);
+bool            psphotDumpConfig (pmConfig *config);
+pmReadout      *psphotSelectBackground (pmConfig *config, const pmFPAview *view, const bool stdev);
+
+// PSF / DBL / EXT evaluation functions
+bool            psphotEvalPSF (pmSource *source, pmModel *model);
+bool            psphotEvalDBL (pmSource *source, pmModel *model);
+bool            psphotEvalEXT (pmSource *source, pmModel *model);
+
+//  functions to support the source fitting process
+bool            psphotInitLimitsPSF (psMetadata *recipe, pmReadout *readout);
+bool            psphotInitLimitsEXT (psMetadata *recipe);
+bool            psphotFitBlend (pmReadout *readout, pmSource *source, pmPSF *psf, psMaskType maskVal, psMaskType markVal);
+bool            psphotFitBlob (pmReadout *readout, pmSource *source, psArray *sources, pmPSF *psf, psMaskType maskVal, psMaskType markVal);
+bool            psphotFitPSF (pmReadout *readout, pmSource *source, pmPSF *psf, psMaskType maskVal, psMaskType markVal);
+pmModel        *psphotFitEXT (pmReadout *readout, pmSource *source, pmModelType modelType, psMaskType maskVal, psMaskType markVal);
+psArray        *psphotFitDBL (pmReadout *readout, pmSource *source, psMaskType maskVal, psMaskType markVal);
+
+// functions to support simultaneous multi-source fitting
+bool            psphotFitSet (pmSource *oneSrc, pmModel *oneModel, char *fitset, pmSourceFitMode mode, psMaskType maskVal);
+
+// plotting functions (available if libkapa is installed)
+bool            psphotPlotMoments (pmConfig *config, pmFPAview *view, psArray *sources);
+bool            psphotPlotPSFModel (pmConfig *config, pmFPAview *view, psArray *sources);
+bool            psphotFitInit ();
+bool            psphotFitSummary ();
+bool            psphotMergeSources (psArray *oldSources, psArray *newSources);
+bool            psphotLoadExtSources (pmConfig *config, const pmFPAview *view, psArray *sources);
+pmPSF          *psphotLoadPSF (pmConfig *config, const pmFPAview *view, psMetadata *recipe);
+bool            psphotSetHeaderNstars (psMetadata *recipe, psArray *sources);
+bool            psphotAddNoise (pmReadout *readout, psArray *sources, psMetadata *recipe);
+bool            psphotSubNoise (pmReadout *readout, psArray *sources, psMetadata *recipe);
+bool            psphotAddOrSubNoise (pmReadout *readout, psArray *sources, psMetadata *recipe, bool add);
+bool            psphotRadialPlot (int *kapa, const char *filename, pmSource *source);
+bool            psphotSourcePlots (pmReadout *readout, psArray *sources, psMetadata *recipe);
+bool            psphotMosaicSubimage (psImage *outImage, pmSource *source, int Xo, int Yo, int DX, int DY);
+
+bool            psphotAddWithTest (pmSource *source, bool useState, psMaskType maskVal);
+bool            psphotSubWithTest (pmSource *source, bool useState, psMaskType maskVal);
+bool            psphotSetState (pmSource *source, bool curState, psMaskType maskVal);
+bool            psphotDeblendSatstars (psArray *sources, psMetadata *recipe);
+bool            psphotSourceSize (pmConfig *config, pmReadout *readout, psArray *sources, psMetadata *recipe, long first);
+
+bool            psphotMakeResiduals (psArray *sources, psMetadata *recipe, pmPSF *psf, psMaskType maskVal);
+
+pmModel        *psphotPSFConvModel (pmReadout *readout, pmSource *source, pmModelType modelType, psMaskType maskVal, psMaskType markVal, int psfSize);
+
+psKernel       *psphotKernelFromPSF (pmSource *source, int nPix);
+
+bool            psphotRadialProfile (pmSource *source, psMetadata *recipe, psMaskType maskVal);
+bool            psphotPetrosian (pmSource *source, psMetadata *recipe, psMaskType maskVal);
+bool            psphotIsophotal (pmSource *source, psMetadata *recipe, psMaskType maskVal);
+bool            psphotAnnuli (pmSource *source, psMetadata *recipe, psMaskType maskVal);
+bool            psphotKron (pmSource *source, psMetadata *recipe, psMaskType maskVal);
+
+// structures & functions to support psf-convolved model fitting
+
+// pmPCMData : PSF Convolved Model data storage structure
+typedef struct {
+    psImage *model;
+    psArray *dmodels;
+    psImage *modelConv;
+    psArray *dmodelsConv;
+} pmPCMData;
+
+
+// psf-convolved model fitting
+bool psphotModelWithPSF_LMM (
+    psMinimization *min,
+    psImage *covar,
+    psVector *params,
+    psMinConstraint *constraint,
+    pmSource *source,
+    const psKernel *psf,
+    psMinimizeLMChi2Func func);
+
+psF32 psphotModelWithPSF_SetABX(
+    psImage  *alpha,
+    psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    pmPCMData *pcm,
+    const pmSource *source,
+    const psKernel *psf,
+    psMinimizeLMChi2Func func);
+
+pmPCMData *pmPCMDataAlloc (
+    const psVector *params,
+    const psVector *paramMask,
+    pmSource *source);
+
+psImage *pmPCMDataSaveImage (pmPCMData *pcm);
+
+int psphotKapaOpen ();
+bool psphotKapaClose ();
+bool psphotImageBackgroundCellHistogram (psVector *values, float mean, float sigma, int ix, int iy);
+bool psphotDiagnosticPlots (pmConfig *config, char *name, ...);
+
+bool psphotMakeFluxScale (psImage *image, psMetadata *recipe, pmPSF *psf);
+
+bool psphotCheckStarDistribution (psArray *sources, psArray *stars, pmPSFOptions *options);
+int psphotSupplementStars (psArray *stars, psArray *sources, psImageBinning *binning, int ix, int iy);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotAddNoise.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotAddNoise.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotAddNoise.c	(revision 22322)
@@ -0,0 +1,100 @@
+# include "psphotInternal.h"
+
+bool psphotAddNoise (pmReadout *readout, psArray *sources, psMetadata *recipe) {
+  return psphotAddOrSubNoise (readout, sources, recipe, true);
+}
+
+bool psphotSubNoise (pmReadout *readout, psArray *sources, psMetadata *recipe) {
+  return psphotAddOrSubNoise (readout, sources, recipe, false);
+}
+
+bool psphotAddOrSubNoise (pmReadout *readout, psArray *sources, psMetadata *recipe, bool add) {
+
+    bool status = false;
+    psEllipseShape oldshape;
+    psEllipseShape newshape;
+    psEllipseAxes axes;
+
+    PS_ASSERT (readout, false);
+    PS_ASSERT (readout->parent, false);
+    PS_ASSERT (readout->parent->concepts, false);
+
+    psTimerStart ("psphot");
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    // increase variance by factor*(object noise):
+    // weight = flux/gain + rn^2/g^2
+    // we are adding factor*flux/gain
+
+    float FACTOR = psMetadataLookupF32 (&status, recipe, "NOISE.FACTOR");
+    PS_ASSERT (status, false);
+    float SIZE = psMetadataLookupF32 (&status, recipe, "NOISE.SIZE");
+    PS_ASSERT (status, false);
+
+    if (SIZE <= 0) {
+       return true;
+    }
+
+    float GAIN = psMetadataLookupF32(&status, readout->parent->concepts, "CELL.GAIN"); // Cell gain
+    PS_ASSERT (status, false);
+
+    FACTOR /= GAIN;
+
+    // loop over all source
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+
+        // skip sources which were not subtracted
+        if (!(source->mode & PM_SOURCE_MODE_SUBTRACTED)) continue;
+
+        // select appropriate model
+        pmModel *model = pmSourceGetModel (NULL, source);
+        if (model == NULL) continue;  // model must be defined
+
+        if (add) {
+            psTrace ("psphot", 4, "adding noise for object at %f,%f\n", model->params->data.F32[PM_PAR_XPOS], model->params->data.F32[PM_PAR_YPOS]);
+        } else {
+            psTrace ("psphot", 4, "remove noise for object at %f,%f\n", model->params->data.F32[PM_PAR_XPOS], model->params->data.F32[PM_PAR_YPOS]);
+        }
+
+        psF32 *PAR = model->params->data.F32;
+
+        // save original values
+        float oldI0  = PAR[PM_PAR_I0];
+        oldshape.sx  = PAR[PM_PAR_SXX];
+        oldshape.sy  = PAR[PM_PAR_SYY];
+        oldshape.sxy = PAR[PM_PAR_SXY];
+
+	// XXX can this be done more intelligently?
+	if (oldI0 == 0.0) continue;
+	if (!isfinite(oldI0)) continue;
+
+        // increase size and height of source
+        axes = psEllipseShapeToAxes (oldshape, 20.0);
+        axes.major *= SIZE;
+        axes.minor *= SIZE;
+        newshape = psEllipseAxesToShape (axes);
+        PAR[PM_PAR_I0]  = FACTOR*PS_SQR(oldI0);
+        PAR[PM_PAR_SXX] = newshape.sx;
+        PAR[PM_PAR_SYY] = newshape.sy;
+        PAR[PM_PAR_SXY] = newshape.sxy;
+
+        // XXX if we use pmSourceOp, the size (and possibly Io) will not be respected
+        pmSourceOp (source, PM_MODEL_OP_FULL | PM_MODEL_OP_NOISE, add, maskVal, 0, 0);
+
+        // restore original values
+        PAR[PM_PAR_I0]  = oldI0;
+        PAR[PM_PAR_SXX] = oldshape.sx;
+        PAR[PM_PAR_SYY] = oldshape.sy;
+        PAR[PM_PAR_SXY] = oldshape.sxy;
+    }
+    if (add) {
+        psLogMsg ("psphot.noise", PS_LOG_INFO, "add noise for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot"));
+    } else {
+        psLogMsg ("psphot.noise", PS_LOG_INFO, "sub noise for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot"));
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotAnnuli.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotAnnuli.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotAnnuli.c	(revision 22322)
@@ -0,0 +1,61 @@
+# include "psphotInternal.h"
+
+bool psphotAnnuli (pmSource *source, psMetadata *recipe, psMaskType maskVal) {
+
+  assert (source->extpars);
+  assert (source->extpars->profile);
+  assert (source->extpars->profile->radius);
+  assert (source->extpars->profile->flux);
+
+  bool status;
+
+  psVector *radius = source->extpars->profile->radius;
+  psVector *weight = source->extpars->profile->weight;
+  psVector *flux = source->extpars->profile->flux;
+
+  // XXX how do I define the radii?  we can put a vector in the recipe...
+  // radialBins defines the bounds or start and stop (we can skip some that way...
+  psVector *radialBinsLower = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
+  psVector *radialBinsUpper = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
+  assert (radialBinsLower->n == radialBinsUpper->n);
+
+  psVector *fluxValues = psVectorAlloc (radialBinsLower->n, PS_TYPE_F32);
+  psVector *fluxSquare = psVectorAlloc (radialBinsLower->n, PS_TYPE_F32);
+  psVector *fluxWeight = psVectorAlloc (radialBinsLower->n, PS_TYPE_F32);
+  psVector *pixelCount = psVectorAlloc (radialBinsLower->n, PS_TYPE_F32);
+  psVectorInit (fluxValues, 0.0);
+  psVectorInit (fluxSquare, 0.0);
+  psVectorInit (fluxWeight, 0.0);
+  psVectorInit (pixelCount, 0.0);
+
+  // XXX this code assumes the radii are in pixels.  convert from arcsec with plate scale
+  // XXX assume the annulii above are not overlapping?  much faster...
+  // XXX this might be must faster in the reverse order: loop over annulii and use disection to 
+  // skip to the start of the annulus.
+  for (int i = 0; i < flux->n; i++) {
+    for (int j = 0; j < radialBinsLower->n; j++) {
+      if (radius->data.F32[i] < radialBinsLower->data.F32[j]) continue;
+      if (radius->data.F32[i] > radialBinsUpper->data.F32[j]) continue;
+      fluxValues->data.F32[j] += flux->data.F32[i];
+      fluxSquare->data.F32[j] += PS_SQR(flux->data.F32[i]);
+      fluxWeight->data.F32[j] += weight->data.F32[i];
+      pixelCount->data.F32[j] += 1.0;
+    }
+  }
+
+  for (int j = 0; j < radialBinsLower->n; j++) {
+    fluxValues->data.F32[j] /= pixelCount->data.F32[j];
+    fluxSquare->data.F32[j] /= pixelCount->data.F32[j];
+    fluxSquare->data.F32[j] -= PS_SQR(fluxValues->data.F32[j]);
+  }
+  
+  source->extpars->annuli = pmSourceAnnuliAlloc ();
+  source->extpars->annuli->flux    = fluxValues;
+  source->extpars->annuli->fluxErr = fluxWeight;
+  source->extpars->annuli->fluxVar = fluxSquare;
+
+  psFree (pixelCount);
+
+  return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotApResid.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotApResid.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotApResid.c	(revision 22322)
@@ -0,0 +1,365 @@
+# include "psphotInternal.h"
+
+# define SKIPSTAR(MSG) { psTrace ("psphot", 3, "invalid : %s", MSG); continue; }
+// measure the aperture residual statistics and 2D variations
+
+bool psphotApResid (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf)
+{
+    int Nfail = 0;
+    int Nskip = 0;
+    int Npsf;
+    bool status;
+    pmModel *model;
+    pmSource *source;
+
+    PS_ASSERT_PTR_NON_NULL(psf, false);
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(recipe, false);
+
+    psTimerStart ("psphot");
+
+    bool measureAptrend = psMetadataLookupBool (&status, recipe, "MEASURE.APTREND");
+    if (!measureAptrend) {
+      return true;
+    }
+
+    // bit-masks to test for good/bad pixels
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psMaskType markVal = psMetadataLookupU8(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // S/N limit to perform full non-linear fits
+    float MAX_AP_OFFSET = psMetadataLookupF32 (&status, recipe, "MAX_AP_OFFSET");
+
+    // this is the smallest radius allowed: need to at least extend growth curve down to this...
+    float PSF_FIT_PAD   = psMetadataLookupF32 (&status, recipe, "PSF_FIT_PADDING");
+    bool IGNORE_GROWTH = psMetadataLookupBool (&status, recipe, "IGNORE_GROWTH");
+    bool INTERPOLATE_AP = psMetadataLookupBool (&status, recipe, "INTERPOLATE_AP");
+
+    // XXX is this still needed?  the pmTrend2D stuff should be auto-adjusting...
+    int APTREND_NSTAR_MIN = psMetadataLookupS32(&status, recipe, "APTREND.NSTAR.MIN");
+    assert (status);
+
+    // maximum order for aperture correction
+    int APTREND_ORDER_MAX = psMetadataLookupS32(&status, recipe, "APTREND.ORDER.MAX");
+    assert (status);
+
+    if (APTREND_ORDER_MAX < 1) {
+        psError(PSPHOT_ERR_CONFIG, true, "APTREND.ORDER.MAX must be 1 or more");
+        return false;
+    }
+
+    pmSourcePhotometryMode photMode = 0;
+    if (!IGNORE_GROWTH) photMode |= PM_SOURCE_PHOT_GROWTH;
+    if (INTERPOLATE_AP) photMode |= PM_SOURCE_PHOT_INTERP;
+
+    // set limits on the aperture magnitudes
+    pmSourceMagnitudesInit (recipe);
+
+    // measure the aperture loss as a function of radius for PSF
+    float REF_RADIUS = psMetadataLookupF32 (&status, recipe, "PSF_REF_RADIUS");
+    psf->growth = pmGrowthCurveAlloc (PSF_FIT_PAD, 100.0, REF_RADIUS);
+
+    if (!pmGrowthCurveGenerate (readout, psf, IGNORE_GROWTH, maskVal, markVal)) {
+        psError(PSPHOT_ERR_APERTURE, false, "Fitting aperture corrections");
+        psFree(psf->growth); psf->growth = NULL;
+        return false;
+    }
+
+    psVector *mask    = psVectorAllocEmpty (300, PS_TYPE_U8);
+    psVector *mag     = psVectorAllocEmpty (300, PS_TYPE_F32);
+    psVector *xPos    = psVectorAllocEmpty (300, PS_TYPE_F32);
+    psVector *yPos    = psVectorAllocEmpty (300, PS_TYPE_F32);
+    psVector *apResid = psVectorAllocEmpty (300, PS_TYPE_F32);
+    psVector *dMag    = psVectorAllocEmpty (300, PS_TYPE_F32);
+    Npsf = 0;
+
+    // select all good PM_SOURCE_TYPE_STAR entries
+    for (int i = 0; i < sources->n; i++) {
+        source = sources->data[i];
+        model = source->modelPSF;
+
+        if (source->type != PM_SOURCE_TYPE_STAR) SKIPSTAR ("NOT STAR");
+        if (source->mode &  PM_SOURCE_MODE_SATSTAR) SKIPSTAR ("SATSTAR");
+        if (source->mode &  PM_SOURCE_MODE_BLEND) SKIPSTAR ("BLEND");
+        if (source->mode &  PM_SOURCE_MODE_FAIL) SKIPSTAR ("FAIL STAR");
+        if (source->mode &  PM_SOURCE_MODE_POOR) SKIPSTAR ("POOR STAR");
+
+        // get growth-corrected, apTrend-uncorrected magnitudes in scaled apertures
+        // will fail if below S/N threshold or model is missing
+        if (!pmSourceMagnitudes (source, psf, photMode, maskVal)) {
+            Nskip ++;
+	    psTrace ("psphot", 3, "skip : bad source mag");
+            continue;
+        }
+
+        if (!isfinite(source->apMag) || !isfinite(source->psfMag)) {
+            Nfail ++;
+	    psTrace ("psphot", 3, "fail : nan mags : %f %f", source->apMag, source->psfMag);
+            continue;
+        }
+
+	// aperture residual for this source
+	float dap = source->apMag - source->psfMag;
+
+        // sanity clipping : if the model is very discrepant, put your expectation in the recipe
+        if ((MAX_AP_OFFSET > 0) && (fabs(dap) > MAX_AP_OFFSET)) {
+            Nfail ++;
+	    psTrace ("psphot", 3, "fail : bad dap %f %f", dap, MAX_AP_OFFSET);
+            continue;
+        }
+
+	mag->data.F32[Npsf]     = source->psfMag;
+        apResid->data.F32[Npsf] = dap;
+        xPos->data.F32[Npsf]    = model->params->data.F32[PM_PAR_XPOS];
+        yPos->data.F32[Npsf]    = model->params->data.F32[PM_PAR_YPOS];
+
+        mask->data.U8[Npsf] = 0;
+
+        dMag->data.F32[Npsf] = model->dparams->data.F32[PM_PAR_I0] / model->params->data.F32[PM_PAR_I0];
+
+        psVectorExtend (mag,     100, 1);
+        psVectorExtend (mask,    100, 1);
+        psVectorExtend (xPos,    100, 1);
+        psVectorExtend (yPos,    100, 1);
+        psVectorExtend (dMag,    100, 1);
+        psVectorExtend (apResid, 100, 1);
+        Npsf ++;
+    }
+    
+    psLogMsg ("psphot.apresid", PS_LOG_DETAIL, "measure aperture residuals for %d objects (%d skipped, %d failed, %ld invalid)\n",
+              Npsf, Nskip, Nfail, sources->n - Npsf - Nskip - Nfail);
+
+    // XXX choose a better value here?
+    if (Npsf < APTREND_NSTAR_MIN) {
+        psError(PSPHOT_ERR_APERTURE, true, "Only %d valid aperture residual sources (need %d), giving up",
+                Npsf, APTREND_NSTAR_MIN);
+        return false;
+    }
+
+    // XXX deprecating the old code which allowed the ApResid to be fitted as a function of flux and r^2/flux
+    // XXX is this asymmetric clipping still needed?  this analysis should come after neighbors are subtracted...
+    // 3hi/1lo sigma clipping on the rflux vs metric fit
+    // systematic error information
+    float errorScale = 0.0;
+    float errorFloor = 0.0;
+
+    float errorFloorMin = FLT_MAX;
+    int entryMin = -1;
+
+    // Fit out the dap vs mag trend, iterate over spatial scale until error Floor increases.
+    // Stop if Npsf / (Nx * Ny) < 3
+    for (int i = 1; i <= APTREND_ORDER_MAX; i++) {
+
+	if (!psphotApResidTrend (readout, psf, Npsf, i, &errorScale, &errorFloor, mask, xPos, yPos, apResid, dMag)) {
+	    break;
+	}
+
+	// store the resulting errorFloor values and the scales, redo the best
+	if (errorFloor < errorFloorMin) {
+	    errorFloorMin = errorFloor;
+	    entryMin = i;
+	}
+    }
+    if (entryMin == -1) {
+	psAbort ("failed on ApResid Trend");
+    }
+
+    // XXX catch error condition 
+    psphotApResidTrend (readout, psf, Npsf, entryMin, &errorScale, &errorFloor, mask, xPos, yPos, apResid, dMag);
+
+    // construct the fitted values and the residuals
+    psVector *apResidFit = pmTrend2DEvalVector (psf->ApTrend, xPos, yPos);
+    psVector *apResidRes = (psVector *) psBinaryOp (NULL, (void *) apResid, "-", (void *) apResidFit);
+    psVector *dMagSys = (psVector *) psBinaryOp (NULL, (void *) dMag, "*", (void *) psScalarAlloc(errorScale, PS_TYPE_F32));
+
+    if (psTraceGetLevel("psphot") >= 5) {
+	FILE *dumpFile = fopen ("apresid.dat", "w");
+	for (int i = 0; i < xPos->n; i++) {
+	    fprintf (dumpFile, "%f %f  %f %f %f  %f %f  %x\n", 
+		     xPos->data.F32[i], yPos->data.F32[i], 
+		     mag->data.F32[i], dMag->data.F32[i], dMagSys->data.F32[i],
+		     apResid->data.F32[i], apResidRes->data.F32[i],
+		     mask->data.U8[i]);
+	}
+	fclose (dumpFile);
+    }
+
+    // apply ApTrend results
+    float xc = 0.5*readout->image->numCols + readout->image->col0 + 0.5;
+    float yc = 0.5*readout->image->numRows + readout->image->row0 + 0.5;
+
+    psf->ApResid  = pmTrend2DEval (psf->ApTrend, xc, yc); // ap-fit at chip center
+    psf->dApResid = errorFloor;
+    psf->nApResid = Npsf;
+
+    // save results for later output
+    psMetadataAdd (recipe, PS_LIST_TAIL, "SKYBIAS",  PS_DATA_F32 | PS_META_REPLACE, "aperture sky bias",   0.0);
+    psMetadataAdd (recipe, PS_LIST_TAIL, "SKYSAT",   PS_DATA_F32 | PS_META_REPLACE, "aperture sky bias",   0.0);
+    psMetadataAdd (recipe, PS_LIST_TAIL, "APMIFIT",  PS_DATA_F32 | PS_META_REPLACE, "aperture residual",   psf->ApResid);
+    psMetadataAdd (recipe, PS_LIST_TAIL, "DAPMIFIT", PS_DATA_F32 | PS_META_REPLACE, "ap residual scatter", psf->dApResid);
+    psMetadataAdd (recipe, PS_LIST_TAIL, "APLOSS",   PS_DATA_F32 | PS_META_REPLACE, "ap residual scatter", psf->growth->apLoss);
+    psMetadataAdd (recipe, PS_LIST_TAIL, "NAPMIFIT", PS_DATA_S32 | PS_META_REPLACE, "ap residual scatter", psf->nApResid);
+
+    // psLogMsg ("psphot.apresid", PS_LOG_INFO, "measure full-frame aperture residuals for %d of %d objects: %f sec\n", Nkeep, Npsf, psTimerMark ("psphot"));
+    psLogMsg ("psphot.apresid", PS_LOG_DETAIL, "aperture residual: %f +/- %f\n", psf->ApResid, psf->dApResid);
+
+    psFree (mag);
+    psFree (mask);
+    psFree (xPos);
+    psFree (yPos);
+
+    psFree (apResid);
+    psFree (apResidFit);
+    psFree (apResidRes);
+
+    psFree (dMagSys);
+    psFree (dMag);
+
+    return true;
+}
+
+/*
+  (aprMag' - fitMag) = rflux*skyBias + ApTrend(x,y)
+  (aprMag - rflux*skyBias) - fitMag = ApTrend(x,y)
+  (aprMag - rflux*skyBias) = fitMag + ApTrend(x,y)
+*/
+
+bool psphotMagErrorScale (float *errorScale, float *errorFloor, psVector *dMag, psVector *dap, psVector *mask, int nGroup) {
+
+    psStats *statsS = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
+    psStats *statsM = psStatsAlloc (PS_STAT_SAMPLE_MEAN);
+
+    // measure the trend in bins with 10 values each; if < 10 total, use them all
+    int nBin = PS_MAX (dMag->n / nGroup, 1);
+
+    // output vectors for ApResid trend 
+    psVector *dSo = psVectorAlloc (nBin, PS_TYPE_F32);
+    psVector *dMo = psVectorAlloc (nBin, PS_TYPE_F32);
+    psVector *dRo = psVectorAlloc (nBin, PS_TYPE_F32);
+
+    // use dMag to group the dMag and dap vectors
+    psVector *index = psVectorSortIndex (NULL, dMag);
+
+    // subset vectors for dMag and dap values within the given range
+    psVector *dMSubset = psVectorAllocEmpty (nGroup, PS_TYPE_F32);
+    psVector *dASubset = psVectorAllocEmpty (nGroup, PS_TYPE_F32);
+    psVector *mkSubset = psVectorAllocEmpty (nGroup, PS_TYPE_U8);
+
+    int n = 0;
+    for (int i = 0; i < dMo->n; i++) {
+	int j;
+	for (j = 0; (j < nGroup) && (n < dMag->n); j++, n++) {
+	    int N = index->data.U32[n];
+	    dMSubset->data.F32[j] = dMag->data.F32[N];
+	    dASubset->data.F32[j] = dap->data.F32[N];
+	    mkSubset->data.U8[j]  = mask->data.U8[N];
+	}
+	dMSubset->n = j;
+	dASubset->n = j;
+	mkSubset->n = j;
+
+	psStatsInit (statsS);
+	psStatsInit (statsM);
+
+	if (j > 2) {
+	    bool status = true;
+	    status &= psVectorStats (statsS, dASubset, NULL, mkSubset, 0xff);
+	    status &= psVectorStats (statsM, dMSubset, NULL, mkSubset, 0xff);
+	    if (!status) { psErrorClear (); }
+	    dSo->data.F32[i] = statsS->robustStdev;
+	    dMo->data.F32[i] = statsM->sampleMean;
+	    dRo->data.F32[i] = statsS->robustStdev / statsM->sampleMean;
+	} else {
+	    dSo->data.F32[i] = NAN;
+	    dMo->data.F32[i] = NAN;
+	    dRo->data.F32[i] = NAN;
+	}
+    }
+    psFree (dMSubset);
+    psFree (dASubset);
+    psFree (mkSubset);
+    
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+    if (!psVectorStats (stats, dRo, NULL, NULL, 0)) {
+	// XXX better testing of raised error
+	psErrorClear();
+    }
+
+    *errorScale = stats->sampleMedian;
+    for (int i = 0; i < dSo->n; i++) {
+	*errorFloor = dSo->data.F32[i];
+	if (isfinite(*errorFloor)) break;
+    }
+
+    psFree (stats);
+    psFree (index);
+
+    psFree (dRo);
+    psFree (dMo);
+    psFree (dSo);
+    
+    psFree (statsS);
+    psFree (statsM);
+
+    return true;
+}
+
+bool psphotApResidTrend (pmReadout *readout, pmPSF *psf, int Npsf, int scale, float *errorScale, float *errorFloor, psVector *mask, psVector *xPos, psVector *yPos, psVector *apResid, psVector *dMag) {
+
+    int Nx, Ny;
+	
+    if (readout->image->numCols > readout->image->numRows) {
+	Nx = scale;
+	Ny = PS_MAX (1, Nx * (readout->image->numRows / (float) readout->image->numCols));
+    } else {
+	Ny = scale;
+	Nx = PS_MAX (1, Ny * (readout->image->numCols / (float) readout->image->numRows));
+    }
+
+    if (Npsf < 3*Nx*Ny) {
+	return false;
+    }
+
+    // the mask marks the values not used to calculate the ApTrend
+    psVectorInit (mask, 0);
+
+    // XXX stats structure for use by ApTrend : make parameters user setable
+    psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
+    stats->min = 2.0;
+    stats->max = 3.0;
+
+    // measure Trend2D for the current spatial scale
+    psFree (psf->ApTrend);
+    psf->ApTrend = pmTrend2DAlloc (PM_TREND_MAP, readout->image, Nx, Ny, stats);
+
+    // XXX test for errors here
+    pmTrend2DFit (psf->ApTrend, mask, 0xff, xPos, yPos, apResid, dMag);
+    
+    // construct the fitted values and the residuals
+    psVector *apResidFit = pmTrend2DEvalVector (psf->ApTrend, xPos, yPos);
+    psVector *apResidRes = (psVector *) psBinaryOp (NULL, (void *) apResid, "-", (void *) apResidFit);
+
+    // measure systematic errorFloor & systematic / photon scale factor
+    // XXX this is a bit arbitrary, but it forces ~3 stars from the bright bin per spatial bin
+    int nGroup = PS_MAX (3*Nx*Ny, 10);
+    psphotMagErrorScale (errorScale, errorFloor, dMag, apResidRes, mask, nGroup);
+
+    psLogMsg ("psphot.apresid", PS_LOG_INFO, "result of %d x %d grid (%d stars per bin)\n", Nx, Ny, nGroup);
+    psLogMsg ("psphot.apresid", PS_LOG_INFO, "systematic error / photon error: %f\n", *errorScale);
+    psLogMsg ("psphot.apresid", PS_LOG_INFO, "systematic scatter floor: %f\n", *errorFloor);
+
+    psFree (stats);
+    psFree (apResidFit);
+    psFree (apResidRes);
+
+    return true;
+}
+    
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotArguments.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "psphotStandAlone.h"
+
+pmConfig *psphotArguments(int argc, char **argv) {
+
+    int N;
+    bool status;
+
+    if (argc == 1) {
+        psError(PSPHOT_ERR_ARGUMENTS, true, "Too few arguments: %d", argc);
+        return NULL;
+    }
+
+    if ((N = psArgumentGet (argc, argv, "-version"))) {
+        psString version;
+        version = psphotVersionLong();    fprintf (stdout, "%s\n", version); psFree (version);
+        version = psModulesVersionLong(); fprintf (stdout, "%s\n", version); psFree (version);
+        version = psLibVersionLong();     fprintf (stdout, "%s\n", version); psFree (version);
+        exit (0);
+    }
+
+    // load config data from default locations
+    pmConfig *config = pmConfigRead(&argc, argv, PSPHOT_RECIPE);
+    if (config == NULL) {
+        psError(PSPHOT_ERR_CONFIG, false, "Can't read site configuration");
+        return NULL;
+    }
+
+    // save the following additional recipe values based on command-line options
+    // these options override the PSPHOT recipe values loaded from recipe files
+    psMetadata *options = pmConfigRecipeOptions (config, PSPHOT_RECIPE);
+
+    // run the test model (requires X,Y coordinate)
+    if ((N = psArgumentGet (argc, argv, "-modeltest"))) {
+        psMetadataAddBool (options, PS_LIST_TAIL, "TEST_FIT",   0, "", true);
+        psMetadataAddF32  (options, PS_LIST_TAIL, "TEST_FIT_X", 0, "", atof(argv[N+1]));
+        psMetadataAddF32  (options, PS_LIST_TAIL, "TEST_FIT_Y", 0, "", atof(argv[N+2]));
+
+        psArgumentRemove (N, &argc, argv);
+        psArgumentRemove (N, &argc, argv);
+        psArgumentRemove (N, &argc, argv);
+
+        // specify the modeltest model
+        if ((N = psArgumentGet (argc, argv, "-model"))) {
+            psArgumentRemove (N, &argc, argv);
+            psMetadataAddStr (options, PS_LIST_TAIL, "TEST_FIT_MODEL", 0, "", argv[N]);
+            psArgumentRemove (N, &argc, argv);
+        }
+
+        // specify the test fit mode
+        if ((N = psArgumentGet (argc, argv, "-fitmode"))) {
+            psArgumentRemove (N, &argc, argv);
+            psMetadataAddStr (options, PS_LIST_TAIL, "TEST_FIT_MODE", 0, "", argv[N]);
+            psArgumentRemove (N, &argc, argv);
+        }
+        if ((N = psArgumentGet (argc, argv, "-fitset"))) {
+            psArgumentRemove (N, &argc, argv);
+            psMetadataAddStr (options, PS_LIST_TAIL, "TEST_FIT_SET", 0, "", argv[N]);
+            psArgumentRemove (N, &argc, argv);
+        }
+    }
+
+    // photcode : used in output to supplement header data (argument or recipe?)
+    if ((N = psArgumentGet (argc, argv, "-photcode"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (options, PS_LIST_TAIL, "PHOTCODE", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // break : used from recipe throughout psphotReadout
+    if ((N = psArgumentGet (argc, argv, "-break"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (options, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // fitmode : used from recipe throughout psphotReadout
+    if ((N = psArgumentGet (argc, argv, "-fitmode"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (options, PS_LIST_TAIL, "FITMODE", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // analysis region : overrides recipe value, used in psphotReadout/psphotEnsemblePSF
+    if ((N = psArgumentGet (argc, argv, "-region"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (options, PS_LIST_TAIL, "ANALYSIS_REGION", 0, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // chip selection is used to limit chips to be processed
+    if ((N = psArgumentGet (argc, argv, "-chip"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_DATA_STRING, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // if these command-line options are supplied, load the file name lists into config->arguments
+    // override any configuration-specified source for these files
+    //
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "MASK",   "-mask",   "-masklist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "WEIGHT", "-weight", "-weightlist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "PSPHOT.PSF", "-psf", "-psflist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "SRC",    "-src",    "-srclist");
+
+    // the input file is a required argument; if not found, we will exit
+    status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
+    if (!status) {
+        psError(PSPHOT_ERR_ARGUMENTS, false, "pmConfigFileSetsMD failed to parse arguments");
+        return NULL;
+    }
+
+    if (argc != 2) {
+        psError(PSPHOT_ERR_ARGUMENTS, true, "Expected to see one more argument; saw %d", argc - 1);
+        return NULL;
+    }
+
+    // output position is fixed
+    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[1]);
+
+    psTrace("psphot", 1, "Done with psphotArguments...\n");
+    return (config);
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotBasicDeblend.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotBasicDeblend.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotBasicDeblend.c	(revision 22322)
@@ -0,0 +1,129 @@
+# include "psphotInternal.h"
+
+bool psphotBasicDeblend (psArray *sources, psMetadata *recipe) {
+
+    int N;
+    bool status;
+    float threshold;
+    pmSource *source, *testSource;
+
+    int Nblend = 0;
+
+    psTimerStart ("psphot");
+
+    float FRACTION = psMetadataLookupF32 (&status, recipe, "DEBLEND_PEAK_FRACTION");
+    if (!status) FRACTION = 0.25;
+
+    float NSIGMA   = psMetadataLookupF32 (&status, recipe, "DEBLEND_SKY_NSIGMA");
+    if (!status) NSIGMA = 5.0;
+
+    // we need sources spatially-sorted to find overlaps
+    sources = psArraySort (sources, pmSourceSortByY);
+
+    // source analysis is done in peak order (brightest first)
+    // we use an index for this so the spatial sorting is kept
+    psVector *SN = psVectorAlloc (sources->n, PS_DATA_F32);
+    for (int i = 0; i < SN->n; i++) {
+        source = sources->data[i];
+        SN->data.F32[i] = source->peak->SN;
+    }
+    psVector *index = psVectorSortIndex (NULL, SN);
+    // this results in an index of increasing SN
+
+    // examine sources in decreasing SN order
+    for (int i = sources->n - 1; i >= 0; i--) {
+        N = index->data.U32[i];
+        source = sources->data[N];
+
+        if (source->mode & PM_SOURCE_MODE_BLEND) continue;
+
+        // temporary array for overlapping objects we find
+        psArray *overlap = psArrayAllocEmpty (100);
+
+        // search backwards for overlapping sources
+        for (int j = N - 1; j >= 0; j--) {
+            testSource = sources->data[j];
+            if (testSource->peak->x <  source->pixels->col0) continue;
+            if (testSource->peak->x >= source->pixels->col0 + source->pixels->numCols) continue;
+            if (testSource->peak->y <  source->pixels->row0) break;
+            if (testSource->peak->y >= source->pixels->row0 + source->pixels->numRows) {
+                fprintf (stderr, "warning: invalid condition\n");
+                continue;
+            }
+            psArrayAdd (overlap, 100, testSource);
+        }
+
+        // search forwards for overlapping sources
+        for (int j = N + 1; j < sources->n; j++) {
+            testSource = sources->data[j];
+            if (testSource->peak->x <  source->pixels->col0) continue;
+            if (testSource->peak->x >= source->pixels->col0 + source->pixels->numCols) continue;
+            if (testSource->peak->y <  source->pixels->row0) {
+                fprintf (stderr, "warning: invalid condition\n");
+                continue;
+            }
+            if (testSource->peak->y >= source->pixels->row0 + source->pixels->numRows) break;
+            psArrayAdd (overlap, 100, testSource);
+        }
+
+        if (overlap->n == 0) {
+            psFree (overlap);
+            continue;
+        }
+
+        // this source has overlapping neighbors, check for actual blends
+        // generate source contour (1/4 peak counts)
+        // set the threshold based on user inputs
+
+        // threshold is fraction of the source peak flux
+        // image is background subtracted; source->moments->Sky should always be 0.0
+        threshold = FRACTION * source->peak->SN;
+        // threshold is no less than NSIGMA
+        threshold = PS_MAX (threshold, NSIGMA);
+
+        // generate a basic contour (set of x,y coordinates at-or-below flux level)
+        psArray *contour = pmSourceContour (source->pixels, source->peak->x, source->peak->y, threshold);
+        if (contour == NULL) {
+            psFree (overlap);
+            continue;
+        }
+
+        // the source contour consists of two vectors, xv and yv.  the contour is
+        // a series of coordinate pairs, (xv[i],yv[i]) & (xv[i+1],yv[i+1]).  both
+        // coordinate pairs have the same yv[] value, with xv[i] corresponding to
+        // the left boundary and xv[i+1] corresponding to the right boundary
+
+	// a blend can only be associated with one primary source
+
+        psVector *xv = contour->data[0];
+        psVector *yv = contour->data[1];
+        for (int k = 0; k < overlap->n; k++) {
+            testSource = overlap->data[k];
+	    if (testSource->mode & PM_SOURCE_MODE_BLEND) continue;
+            if (testSource->peak->value > source->peak->value) continue;
+            for (int j = 0; j < xv->n; j+=2) {
+                if (fabs(yv->data.F32[j] - testSource->peak->y) > 0.5) continue;
+                if (xv->data.F32[j+0] > testSource->peak->x) break;
+                if (xv->data.F32[j+1] < testSource->peak->x) break;
+
+                testSource->mode |= PM_SOURCE_MODE_BLEND;
+
+                // add this to the list of source->blends
+                if (source->blends == NULL) {
+                    source->blends = psArrayAllocEmpty (16);
+                }
+                psArrayAdd (source->blends, 16, testSource);
+
+                Nblend ++;
+                j = xv->n;
+            }
+        }
+        psFree (overlap);
+        psFree (contour);
+    }
+    psLogMsg ("psphot.deblend", PS_LOG_INFO, "identified %d blended objects: %f sec\n", Nblend, psTimerMark ("psphot"));
+
+    psFree (SN);
+    psFree (index);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotBlendFit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotBlendFit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotBlendFit.c	(revision 22322)
@@ -0,0 +1,120 @@
+# include "psphotInternal.h"
+
+// XXX I don't like this name
+bool psphotBlendFit (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf) {
+
+    int Nfit = 0;
+    int Npsf = 0;
+    int Next = 0;
+    int Nfail = 0;
+    bool status;
+
+    psTimerStart ("psphot");
+
+    // bit-masks to test for good/bad pixels
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psMaskType markVal = psMetadataLookupU8(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // source analysis is done in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // S/N limit to perform full non-linear fits
+    float FIT_SN_LIM = psMetadataLookupF32 (&status, recipe, "FULL_FIT_SN_LIM");
+
+    psphotInitLimitsPSF (recipe, readout);
+    psphotInitLimitsEXT (recipe);
+    psphotInitRadiusPSF (recipe, psf->type);
+
+    psphotFitInit ();
+
+    // option to limit analysis to a specific region
+    char *region = psMetadataLookupStr (&status, recipe, "ANALYSIS_REGION");
+    psRegion AnalysisRegion = psRegionForImage (readout->image, psRegionFromString (region));
+    if (psRegionIsNaN (AnalysisRegion)) psAbort("analysis region mis-defined");
+
+    for (int i = 0; i < sources->n; i++) {
+        // if (i%100 == 0) psphotFitSummary ();
+
+        pmSource *source = sources->data[i];
+
+        // skip non-astronomical objects (very likely defects)
+        if (source->mode &  PM_SOURCE_MODE_BLEND) continue;
+        if (source->mode &  PM_SOURCE_MODE_CR_LIMIT) continue;
+        if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
+        if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
+
+        // skip DBL second sources (ie, added by psphotFitBlob)
+        if (source->mode &  PM_SOURCE_MODE_PAIR) continue;
+
+        // limit selection to some SN limit
+        // XXX this should use peak?
+        if (source->moments == NULL) continue;
+        if (source->moments->SN < FIT_SN_LIM) continue;
+
+        // XXX this should use peak?
+        if (source->moments->x < AnalysisRegion.x0) continue;
+        if (source->moments->y < AnalysisRegion.y0) continue;
+        if (source->moments->x > AnalysisRegion.x1) continue;
+        if (source->moments->y > AnalysisRegion.y1) continue;
+
+        // if model is NULL, we don't have a starting guess
+        if (source->modelPSF == NULL) continue;
+
+        // skip sources which are insignificant flux?
+        if (source->modelPSF->params->data.F32[1] < 0.1) {
+            psTrace ("psphot", 5, "skipping near-zero source: %f, %f : %f\n",
+                     source->modelPSF->params->data.F32[1],
+                     source->modelPSF->params->data.F32[2],
+                     source->modelPSF->params->data.F32[3]);
+            continue;
+        }
+
+        // replace object in image
+        if (source->mode & PM_SOURCE_MODE_SUBTRACTED) {
+            pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+        }
+        Nfit ++;
+
+        // try fitting PSFs, then try extended sources
+        // these functions subtract the resulting fitted source (XXX and update the modelFlux?)
+	// XXX re-consider conditions under which the source has EXT fit:
+	// I should try EXT if the source size measurement says it is large
+	if (source->mode & PM_SOURCE_MODE_EXT_LIMIT) {
+	    if (psphotFitBlob (readout, source, sources, psf, maskVal, markVal)) {
+		source->type = PM_SOURCE_TYPE_EXTENDED;
+		psTrace ("psphot", 5, "source at %7.1f, %7.1f is ext", source->moments->x, source->moments->y);
+		Next ++;
+		continue;
+	    }
+	} else {
+	    if (psphotFitBlend (readout, source, psf, maskVal, markVal)) {
+		source->type = PM_SOURCE_TYPE_STAR;
+		psTrace ("psphot", 5, "source at %7.1f, %7.1f is psf", source->moments->x, source->moments->y);
+		Npsf ++;
+		continue;
+	    }
+	}
+
+        psTrace ("psphot", 5, "source at %7.1f, %7.1f failed", source->moments->x, source->moments->y);
+        Nfail ++;
+
+        // re-subtract the object, leave local sky
+        pmSourceCacheModel (source, maskVal);
+        pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+        source->mode |= PM_SOURCE_MODE_SUBTRACTED;
+    }
+
+    if (psTraceGetLevel("psphot") >= 6) {
+      psphotSaveImage (NULL, readout->image,  "image.v2.fits");
+    }
+
+    psLogMsg ("psphot.psphotBlendFit", PS_LOG_INFO, "fit models: %f sec for %d objects (%d psf, %d ext, %d failed, %ld skipped)\n", psTimerMark ("psphot"), Nfit, Npsf, Next, Nfail, sources->n - Nfit);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotCheckStarDistribution.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotCheckStarDistribution.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotCheckStarDistribution.c	(revision 22322)
@@ -0,0 +1,104 @@
+# include "psphotInternal.h"
+
+// examine the x,y distribution of the psfstars and extend the selection if needed
+// desired region and coverage are specified by pmPSFOptions
+// even if stars->n is at the limit, we will add more sources to fill out the area
+
+bool psphotCheckStarDistribution (psArray *stars, psArray *sources, pmPSFOptions *options) {
+
+    // count the number of PSFSTAR sources in each cell.  for polynomial representations, we
+    // use an image (Norder + 1) x (Norder + 1)
+    int Nx = options->psfTrendNx;
+    int Ny = options->psfTrendNy;
+    if (options->psfTrendMode != PM_TREND_MAP) {
+	Nx ++;
+	Ny ++;
+    }
+
+    // set up and image and an image binning structure to cover the field
+    psImage *nCell = psImageAlloc (Nx, Ny, PS_TYPE_S32);
+    psImageInit (nCell, 0);
+
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXruff = Nx;
+    binning->nYruff = Ny;
+    binning->nXfine = options->psfFieldNx;
+    binning->nYfine = options->psfFieldNy;
+
+    psImageBinningSetScale (binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkipByOffset (binning, options->psfFieldXo, options->psfFieldYo);
+
+    // where are the PSF stars located?
+    for (int i = 0; i < stars->n; i++) {
+	
+	pmSource *source = stars->data[i];
+        if (source->peak == NULL) continue;
+        if (!(source->mode & PM_SOURCE_MODE_PSFSTAR)) continue;
+
+	int binX = psImageBinningGetRuffX (binning, source->peak->xf);
+	int binY = psImageBinningGetRuffY (binning, source->peak->yf);
+
+	assert (binX >= 0);
+	assert (binY >= 0);
+	assert (binX <  nCell->numCols);
+	assert (binY <  nCell->numRows);
+
+	nCell->data.S32[binY][binX] ++;
+    }
+	
+    // do any cells have too few PSFSTAR sources?
+    // we use 3 as a minimum (slightly arbitrary...)
+    for (int iy = 0; iy < nCell->numRows; iy++) { 
+	for (int ix = 0; ix < nCell->numCols; ix++) { 
+	    if (nCell->data.S32[iy][ix] < 3) {
+		int nNew = psphotSupplementStars (stars, sources, binning, ix, iy);
+		if (nNew) {
+		    psLogMsg ("pmObjects", 3, "added %d fainter PSF candidates to %d,%d\n", nNew, ix, iy);
+		} else {
+		    psLogMsg ("pmObjects", 3, "tried to add to %d,%d, but no more sources are available\n", ix, iy);		    
+		}
+	    }
+	}
+    }
+
+    psFree (nCell);
+    psFree (binning);
+    return true;
+}
+
+// select more possible PSF stars in the specified cell
+// sources should be sorted by SN before calling this function
+int psphotSupplementStars (psArray *stars, psArray *sources, psImageBinning *binning, int ix, int iy) {
+
+    int nNew = 0;
+
+    float Xs = psImageBinningGetFineX (binning, ix);
+    float Xe = psImageBinningGetFineX (binning, ix + 1);
+
+    float Ys = psImageBinningGetFineY (binning, iy);
+    float Ye = psImageBinningGetFineY (binning, iy + 1);
+
+    for (int i = 0; i < sources->n; i++) {
+
+	// add sources which are marked as stars, in S/N order until...
+	pmSource *source = sources->data[i];
+        if (source->peak == NULL) continue;
+        if (source->moments == NULL) continue;
+        if (source->mode & PM_SOURCE_MODE_PSFSTAR) continue;
+        if (source->type != PM_SOURCE_TYPE_STAR) continue;
+
+	float x = source->peak->xf;
+	float y = source->peak->yf;
+
+	if (x < Xs) continue;
+	if (y < Ys) continue;
+	if (x > Xe) continue;
+	if (y > Ye) continue;
+
+	source->mode |= PM_SOURCE_MODE_PSFSTAR;
+	psArrayAdd (stars, 200, source);
+
+	nNew ++;
+    }
+    return nNew;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotChoosePSF.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotChoosePSF.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotChoosePSF.c	(revision 22322)
@@ -0,0 +1,502 @@
+# include "psphotInternal.h"
+
+// try PSF models and select best option
+pmPSF *psphotChoosePSF (pmReadout *readout, psArray *sources, psMetadata *recipe) {
+
+    bool status;
+
+    psTimerStart ("psphot");
+
+    // bit-masks to test for good/bad pixels
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psMaskType markVal = psMetadataLookupU8(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // examine PSF sources in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // structure to store user options defining the psf
+    pmPSFOptions *options = pmPSFOptionsAlloc ();
+
+    // load user options from the recipe. no need to check existence -- they are
+    // array to store candidate PSF stars
+    int NSTARS = psMetadataLookupS32 (&status, recipe, "PSF_MAX_NSTARS");
+    assert (status);
+
+    float PSF_MIN_DS = psMetadataLookupF32 (&status, recipe, "PSF_MIN_DS");
+    assert (status);
+
+    // supply the measured sky variance for optional constant errors (non-poissonian)
+    float SKY_SIG = psMetadataLookupF32 (&status, recipe, "SKY_SIG");
+    assert (status);
+
+    // use poissonian errors or local-sky errors
+    options->poissonErrorsPhotLMM = psMetadataLookupBool (&status, recipe, "POISSON.ERRORS.PHOT.LMM");
+    assert (status);
+
+    // use poissonian errors or local-sky errors
+    options->poissonErrorsPhotLin = psMetadataLookupBool (&status, recipe, "POISSON.ERRORS.PHOT.LIN");
+    assert (status);
+
+    // use poissonian errors or local-sky errors
+    options->poissonErrorsParams = psMetadataLookupBool (&status, recipe, "POISSON.ERRORS.PARAMS");
+    assert (status);
+
+    // how to model the PSF variations across the field
+    options->psfTrendMode = pmTrend2DModeFromString (psMetadataLookupStr (&status, recipe, "PSF.TREND.MODE"));
+    assert (status);
+
+    options->psfTrendNx = psMetadataLookupS32 (&status, recipe, "PSF.TREND.NX");
+    assert (status);
+
+    options->psfTrendNy = psMetadataLookupS32 (&status, recipe, "PSF.TREND.NY");
+    assert (status);
+
+    // get the fixed PSF fit radius
+    // XXX check that PSF_FIT_RADIUS < SKY_OUTER_RADIUS
+    options->radius = psMetadataLookupF32 (&status, recipe, "PSF_FIT_RADIUS");
+    assert (status);
+
+    // XXX ROBUST seems to be too agressive given the small numbers.
+    // options->stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
+    options->stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+
+    // dimensions of the field for which the PSF is defined
+    options->psfFieldNx = readout->image->numCols;
+    options->psfFieldNy = readout->image->numRows;
+    options->psfFieldXo = readout->image->col0;
+    options->psfFieldYo = readout->image->row0;
+
+    int fitIter = psMetadataLookupS32(&status, recipe, "PSF_FIT_ITER"); // Maximum number of fit iterations
+    if (!status || fitIter <= 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "PSF_FIT_ITER is not positive");
+        return false;
+    }
+    float fitTol = psMetadataLookupF32 (&status, recipe, "PSF_FIT_TOL"); // Fit tolerance
+    if (!status || !isfinite(fitTol) || fitTol <= 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "PSF_FIT_TOL is not positive");
+        return false;
+    }
+    pmSourceFitModelInit(fitIter, fitTol, PS_SQR(SKY_SIG), options->poissonErrorsPhotLMM);
+
+    psArray *stars = psArrayAllocEmpty (sources->n);
+
+    // select the candidate PSF stars (pointers to original sources)
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+        if (source->mode & PM_SOURCE_MODE_PSFSTAR) {
+            // keep NSTARS PSF stars, unmark the rest
+            if (stars->n < NSTARS) {
+                psArrayAdd (stars, 200, source);
+            } else {
+                source->mode &= ~PM_SOURCE_MODE_PSFSTAR;
+            }
+        }
+    }
+    // check that the identified psf stars sufficiently cover the region; if not, extend the
+    // limits somewhat
+    psphotCheckStarDistribution (stars, sources, options);
+
+    psLogMsg ("psphot.pspsf", PS_LOG_DETAIL, "selected candidate %ld PSF objects\n", stars->n);
+
+    // get the list pointers for the PSF_MODEL entries
+    psArray *modelNames = NULL;
+    psMetadataItem *item = psMetadataLookup (recipe, "PSF_MODEL");
+    if (item == NULL) psAbort("missing PSF_MODEL selection");
+
+    if (item->type == PS_DATA_STRING) {
+        modelNames = psArrayAlloc(1);
+        modelNames->data[0] = psStringCopy (item->data.V);
+    } else {
+        if (item->type != PS_DATA_METADATA_MULTI) psAbort("missing PSF_MODEL selection");
+        modelNames = psListToArray (item->data.list);
+    }
+
+    // generate a psf model using the first selection
+    if (stars->n == 0) {
+        psError(PSPHOT_ERR_PSF, false, "Failed to fit any models when choosing PSF");
+        bool mdok;                      // Status of MD lookup
+        if (!psMetadataLookupBool(&mdok, recipe, PSPHOT_RECIPE_PSF_FAKE_ALLOW)) {
+            return false;
+        }
+        psErrorStackPrint(stderr, "Using guess PSF model");
+        psErrorClear();
+
+        psFree(options);
+
+        // unset the PSFSTAR flags (none are used):
+        for (int i = 0; i < sources->n; i++) {
+            pmSource *source = sources->data[i];
+            source->mode &= ~PM_SOURCE_MODE_PSFSTAR;
+        }
+
+        // XXX set sxx, etc from FWHM in recipe
+        pmPSF *psf = pmPSFBuildSimple (modelNames->data[0], 1.0, 1.0, 0.0, 1.0);
+        psf->fieldNx = readout->image->numCols;
+        psf->fieldNy = readout->image->numRows;
+        psFree (modelNames);
+
+        bool status = true;
+        status &= psphotMakeFluxScale (readout->image, recipe, psf);
+        status &= psphotPSFstats (readout, recipe, psf);
+        if (!status) {
+            psError(PSPHOT_ERR_PSF, false, "Failed to fit any models when choosing PSF");
+            psFree (psf);
+            return NULL;
+        }
+
+        // XXX set DSX_MEAN, etc?
+        return psf;
+    }
+
+    // set up an array to store the results
+    psArray *models = psArrayAlloc (modelNames->n);
+
+    // try each model option listed in config
+    for (int i = 0; i < modelNames->n; i++) {
+        char *modelName = modelNames->data[i];
+        pmPSFtry *try = pmPSFtryModel (stars, modelName, options, maskVal, markVal); // Attempt at fit
+        if (!try) {
+            // No big deal --- we'll try another model
+            psErrorClear();
+            continue;
+        }
+        models->data[i] = try;
+    }
+
+    // select the best of the models
+    // here we are using the clippedStdev on the metric as the indicator
+    int bestN = -1;
+    float bestM = 0.0;
+    for (int i = 0; i < models->n; i++) {
+        pmPSFtry *try = models->data[i];
+        if (try == NULL) {
+            psTrace ("psphot", 3, "PSF model %d is NULL", i);
+            continue;
+        }
+        float M = try->psf->dApResid;
+        if (bestN < 0 || M < bestM) {
+            bestM = M;
+            bestN = i;
+        }
+    }
+
+    // use the best model:
+    if (bestN < 0) {
+        psFree (models);
+        psFree (stars);
+
+        psErrorStackPrint (stderr, "Failed to fit any models when choosing PSF");
+        psErrorClear();
+
+        bool mdok;                      // Status of MD lookup
+        if (!psMetadataLookupBool(&mdok, recipe, PSPHOT_RECIPE_PSF_FAKE_ALLOW)) {
+            psFree (modelNames);
+            psFree(options);
+            return NULL;
+        }
+
+        // generate a psf model using the first selection
+        psLogMsg ("psphot.pspsf", PS_LOG_INFO, "Using guess PSF model");
+
+        // unset the PSFSTAR flags (none are used):
+        for (int i = 0; i < sources->n; i++) {
+            pmSource *source = sources->data[i];
+            source->mode &= ~PM_SOURCE_MODE_PSFSTAR;
+        }
+
+        // XXX set sxx, etc from FWHM in recipe
+        pmPSF *psf = pmPSFBuildSimple (modelNames->data[0], 1.0, 1.0, 0.0, 1.0);
+        psf->fieldNx = readout->image->numCols;
+        psf->fieldNy = readout->image->numRows;
+        psFree (modelNames);
+
+        bool status = true;
+        status &= psphotMakeFluxScale (readout->image, recipe, psf);
+        status &= psphotPSFstats (readout, recipe, psf);
+        if (!status) {
+            psError(PSPHOT_ERR_PSF, false, "Failed to fit any models when choosing PSF");
+            psFree (psf);
+            return NULL;
+        }
+
+        // XXX set DSX_MEAN, etc?
+        return psf;
+    }
+
+    psFree (modelNames);
+    psFree (stars);
+
+    pmPSFtry *try = models->data[bestN];
+
+    // measure and save the median value of dparams[PM_PAR_SXX],dparams[PM_PAR_SYY]
+    // these are used by psphotEvalPSF to identify extended sources
+    // XXX is this still needed?
+    psVector *Sx = psVectorAllocEmpty (try->sources->n, PS_TYPE_F32);
+    psVector *Sy = psVectorAllocEmpty (try->sources->n, PS_TYPE_F32);
+    psVector *dSx = psVectorAllocEmpty (try->sources->n, PS_TYPE_F32);
+    psVector *dSy = psVectorAllocEmpty (try->sources->n, PS_TYPE_F32);
+    psVector *dSN = psVectorAllocEmpty (try->sources->n, PS_TYPE_F32);
+    for (int i = 0; i < try->sources->n; i++) {
+        // masked for: bad model fit, outlier in parameters
+        if (try->mask->data.U8[i] & PSFTRY_MASK_ALL)
+            continue;
+
+        pmSource *source = try->sources->data[i];
+        Sx->data.F32[Sx->n] = source->modelPSF->params->data.F32[PM_PAR_SXX];
+        Sy->data.F32[Sy->n] = source->modelPSF->params->data.F32[PM_PAR_SYY];
+        dSN->data.F32[dSN->n] = source->modelPSF->dparams->data.F32[PM_PAR_I0] / source->modelPSF->params->data.F32[PM_PAR_I0];
+        dSx->data.F32[dSx->n] = source->modelPSF->dparams->data.F32[PM_PAR_SXX];
+        dSy->data.F32[dSy->n] = source->modelPSF->dparams->data.F32[PM_PAR_SYY];
+        Sx->n ++;
+        Sy->n ++;
+        dSN->n ++;
+        dSx->n ++;
+        dSy->n ++;
+    }
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+
+    psVectorStats (stats, dSx, NULL, NULL, 0);
+    float dSxo = stats->sampleMean;
+    psLogMsg ("psphot.choosePSF", PS_LOG_INFO, "dSxo: mean: %f, median: %f, stdev: %f, npts: %ld", stats->sampleMean, stats->sampleMedian, stats->sampleStdev, dSx->n);
+
+    psVectorStats (stats, dSy, NULL, NULL, 0);
+    float dSyo = stats->sampleMean;
+    psLogMsg ("psphot.choosePSF", PS_LOG_INFO, "dSyo: mean: %f, median: %f, stdev: %f, npts: %ld", stats->sampleMean, stats->sampleMedian, stats->sampleStdev, dSy->n);
+
+    for (int i = 0; i < dSN->n; i++) {
+        dSx->data.F32[i] = (dSx->data.F32[i] - dSxo) / PS_MAX (PSF_MIN_DS, Sx->data.F32[i]*dSN->data.F32[i]);
+        dSy->data.F32[i] = (dSy->data.F32[i] - dSyo) / PS_MAX (PSF_MIN_DS, Sy->data.F32[i]*dSN->data.F32[i]);
+    }
+
+    psVectorStats (stats, dSx, NULL, NULL, 0);
+    float nSx = stats->sampleStdev;
+    psLogMsg ("psphot.choosePSF", PS_LOG_INFO, "ndSx: mean: %f, median: %f, stdev: %f, npts: %ld", stats->sampleMean, stats->sampleMedian, stats->sampleStdev, dSx->n);
+    psVectorStats (stats, dSy, NULL, NULL, 0);
+    float nSy = stats->sampleStdev;
+    psLogMsg ("psphot.choosePSF", PS_LOG_INFO, "ndSy: mean: %f, median: %f, stdev: %f, npts: %ld", stats->sampleMean, stats->sampleMedian, stats->sampleStdev, dSy->n);
+
+    psFree (Sx);
+    psFree (Sy);
+    psFree (dSx);
+    psFree (dSy);
+    psFree (dSN);
+    psFree (stats);
+
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "DSX_MEAN", PS_META_REPLACE, "mean offset of dSXX", dSxo);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "DSY_MEAN", PS_META_REPLACE, "mean offset of dSYY", dSyo);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "DSX_STDV", PS_META_REPLACE, "stdev of mean-corrected dSXX", nSx);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "DSY_STDV", PS_META_REPLACE, "stdev of mean-corrected dSYY", nSy);
+
+    // unset the PSFSTAR flag for stars not used for PSF model
+    for (int i = 0; i < try->sources->n; i++) {
+        pmSource *source = try->sources->data[i];
+        if (try->mask->data.U8[i]) {
+            source->mode &= ~PM_SOURCE_MODE_PSFSTAR;
+        }
+    }
+
+    // build a PSF residual image
+    if (!psphotMakeResiduals (try->sources, recipe, try->psf, maskVal)) {
+        psError(PSPHOT_ERR_PSF, false, "Unable to construct residual table for PSF");
+        psFree (models);
+        psFree(options);
+        return NULL;
+    }
+
+    // build a PSF residual image
+    if (!psphotMakeFluxScale (readout->image, recipe, try->psf)) {
+        psError(PSPHOT_ERR_PSF, false, "Unable to construct flux scale for PSF");
+        psFree (models);
+        psFree(options);
+        return NULL;
+    }
+
+    // XXX test dump of psf star data and psf-subtracted image
+    if (psTraceGetLevel("psphot.psfstars") > 5) {
+        psphotSaveImage (NULL, readout->image,  "rawstars.fits");
+
+        for (int i = 0; i < try->sources->n; i++) {
+            // masked for: bad model fit, outlier in parameters
+            if (try->mask->data.U8[i] & PSFTRY_MASK_ALL)
+                continue;
+
+            pmSource *source = try->sources->data[i];
+            float x = source->modelPSF->params->data.F32[PM_PAR_XPOS];
+            float y = source->modelPSF->params->data.F32[PM_PAR_YPOS];
+
+            // set the mask and subtract the PSF model
+            // XXX should we be using maskObj? should we be unsetting the mask?
+            // use pmModelSub because modelFlux has not been generated
+            assert (source->maskObj);
+            psImageKeepCircle (source->maskObj, x, y, options->radius, "OR", markVal);
+            pmModelSub (source->pixels, source->maskObj, source->modelPSF, PM_MODEL_OP_FULL, maskVal);
+            psImageKeepCircle (source->maskObj, x, y, options->radius, "AND", PS_NOT_U8(markVal));
+        }
+
+        FILE *f = fopen ("shapes.dat", "w");
+        for (int i = 0; i < try->sources->n; i++) {
+            psF32 inPar[10];  // must be psF32 to pmPSF_FitToModel
+
+            // masked for: bad model fit, outlier in parameters
+            if (try->mask->data.U8[i] & PSFTRY_MASK_ALL) continue;
+
+            pmSource *source = try->sources->data[i];
+            psF32 *outPar = source->modelEXT->params->data.F32;
+
+            psEllipseShape shape;
+
+            shape.sx  = outPar[PM_PAR_SXX] / M_SQRT2;
+            shape.sy  = outPar[PM_PAR_SYY] / M_SQRT2;
+            shape.sxy = outPar[PM_PAR_SXY];
+
+            psEllipsePol pol = pmPSF_ModelToFit (outPar);
+            inPar[PM_PAR_E0] = pol.e0;
+            inPar[PM_PAR_E1] = pol.e1;
+            inPar[PM_PAR_E2] = pol.e2;
+            pmPSF_FitToModel (inPar, 0.1);
+
+            psEllipseAxes axes1 = psEllipseShapeToAxes (shape, 20.0);
+            psEllipseAxes axes2;
+            (void)psEllipsePolToAxes(pol, 0.1, &axes2);
+            psEllipsePol pol2 = psEllipseAxesToPol (axes1);
+
+            fprintf (f, "%3d  %7.2f %7.2f  %7.4f %7.4f %7.4f  --  %7.4f %7.4f %7.4f  :  %7.4f %7.4f %7.4f  --  %7.4f %7.4f %7.4f : %7.4f %7.4f %6.1f : %7.4f %7.4f %6.1f\n",
+                     i, outPar[PM_PAR_XPOS], outPar[PM_PAR_YPOS],
+                     outPar[PM_PAR_SXX], outPar[PM_PAR_SXY], outPar[PM_PAR_SYY],
+                     pol.e0, pol.e1, pol.e2,
+                     pol2.e0, pol2.e1, pol2.e2,
+                     inPar[PM_PAR_SXX], inPar[PM_PAR_SXY], inPar[PM_PAR_SYY],
+                     axes1.major, axes1.minor, axes1.theta*PM_DEG_RAD,
+                     axes2.major, axes2.minor, axes2.theta*PM_DEG_RAD
+                     );
+        }
+        fclose (f);
+
+        psphotSaveImage (NULL, readout->image,  "psfstars.fits");
+        pmSourcesWritePSFs (try->sources, "psfstars.dat");
+        pmSourcesWriteEXTs (try->sources, "extstars.dat", false);
+        // XXX need alternative output function
+        // psMetadata *psfData = pmPSFtoMetadata (NULL, try->psf);
+        // psMetadataConfigWrite (psfData, "psfmodel.dat");
+        psLogMsg ("psphot.choosePSF", PS_LOG_INFO, "wrote out psf-subtracted image, psf data, exiting\n");
+        exit (0);
+    }
+
+    // save only the best model;
+    // XXX we are not saving the fitted sources
+    // XXX do we want to keep them so we may optionally write them out?
+    pmPSF *psf = psMemIncrRefCounter(try->psf);
+    psFree (models);
+
+    if (!psphotPSFstats (readout, recipe, psf)) {
+        psError(PSPHOT_ERR_PSF, false, "cannot measure PSF shape terms");
+        psFree(options);
+        psFree(psf);
+        return NULL;
+    }
+
+    char *modelName = pmModelClassGetName (psf->type);
+    psLogMsg ("psphot.pspsf", PS_LOG_INFO, "select psf model: %f sec\n", psTimerMark ("psphot"));
+    psLogMsg ("psphot.pspsf", PS_LOG_INFO, "psf model %s, ApResid: %f +/- %f\n", modelName, psf->ApResid, psf->dApResid);
+
+    psFree (options);
+    return (psf);
+}
+
+// measure average parameters of the PSF model
+bool psphotPSFstats (pmReadout *readout, psMetadata *recipe, pmPSF *psf) {
+
+    psEllipseShape shape;
+    psEllipseAxes axes;
+
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(recipe, false);
+    PS_ASSERT_PTR_NON_NULL(psf, false);
+
+    psImage *image = readout->image;
+    PS_ASSERT_PTR_NON_NULL(image, false);
+
+    // construct a normalized PSF model at this coordinate (Io = 1.0)
+    pmModel *modelPSF = pmModelFromPSFforXY (psf, 0.5*image->numCols, 0.5*image->numRows, 1.0);
+    if (modelPSF == NULL) {
+        psError(PSPHOT_ERR_PSF, false, "Failed to estimate PSF model at image centre");
+        return false;
+    }
+
+    // get the model full-width at half-max
+    double FWHM_X = 2*modelPSF->modelRadius (modelPSF->params, 0.5);
+
+    // XXX make sure this is consistent with the re-definition of PM_PAR_SXX
+    shape.sx  = modelPSF->params->data.F32[PM_PAR_SXX];
+    shape.sy  = modelPSF->params->data.F32[PM_PAR_SYY];
+    shape.sxy = modelPSF->params->data.F32[PM_PAR_SXY];
+    axes = psEllipseShapeToAxes (shape, 20.0);
+
+    double FWHM_Y = FWHM_X * (axes.minor / axes.major);
+
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "FWHM_X",   PS_META_REPLACE, "PSF FWHM Major axis", FWHM_X);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "FWHM_Y",   PS_META_REPLACE, "PSF FWHM Minor axis", FWHM_Y);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "ANGLE",    PS_META_REPLACE, "PSF angle",           axes.theta);
+    psMetadataAddS32 (recipe, PS_LIST_TAIL, "NPSFSTAR", PS_META_REPLACE, "Number of stars used to make PSF", psf->nPSFstars);
+    psMetadataAddBool(recipe, PS_LIST_TAIL, "PSFMODEL", PS_META_REPLACE, "Valid PSF Model?", true);
+    psFree (modelPSF);
+
+    return true;
+}
+
+// determine approximate PSF shape parameters based on the moments
+bool psphotMomentsStats (pmReadout *readout, psMetadata *recipe, psArray *sources) {
+
+    // without the PSF model, we can only rely on the source->moments
+    // as a measure of the FWHM values
+
+    int FWHM_N = 0;
+    double FWHM_X = 0.0;
+    double FWHM_Y = 0.0;
+    double FWHM_T = 0.0;
+
+    psEllipseMoments moments;
+    psEllipseAxes axes;
+
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(recipe, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+        if (!source) continue;
+        if (!(source->mode & PM_SOURCE_MODE_PSFSTAR)) continue;
+
+        // moments->Sx,Sy are roughly sigma_x,y
+        moments.x2 = PS_SQR(source->moments->Sx);
+        moments.y2 = PS_SQR(source->moments->Sy);
+        moments.xy = source->moments->Sxy;
+
+        // limit axis ratio < 20.0
+        axes = psEllipseMomentsToAxes (moments, 20.0);
+
+        FWHM_X += axes.major * 2.35;
+        FWHM_Y += axes.minor * 2.35;
+        FWHM_T += axes.theta;
+        FWHM_N ++;
+    }
+
+    FWHM_X /= FWHM_N;
+    FWHM_Y /= FWHM_N;
+    FWHM_T /= FWHM_N;
+
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "FWHM_X",   PS_META_REPLACE, "PSF FWHM Major axis (from moments)", FWHM_X);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "FWHM_Y",   PS_META_REPLACE, "PSF FWHM Minor axis (from moments)", FWHM_Y);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "ANGLE",    PS_META_REPLACE, "PSF angle",           FWHM_T);
+    psMetadataAddS32 (recipe, PS_LIST_TAIL, "NPSFSTAR", PS_META_REPLACE, "Number of stars used to make PSF", 0);
+    psMetadataAddBool(recipe, PS_LIST_TAIL, "PSFMODEL", PS_META_REPLACE, "Valid PSF Model?", false);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotCleanup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotCleanup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotCleanup.c	(revision 22322)
@@ -0,0 +1,36 @@
+# include "psphotStandAlone.h"
+
+void psphotCleanup (pmConfig *config) {
+
+    psFree (config);
+
+    psTimerStop ();
+    psMemCheckCorruption (stderr, true);
+    pmModelClassCleanup ();
+    psTimeFinalize ();
+    pmConceptsDone ();
+    pmConfigDone ();
+    // fprintf (stderr, "found %d leaks at %s\n", psMemCheckLeaks (0, NULL, NULL, false), "psphot");
+    fprintf (stderr, "found %d leaks at %s\n", psMemCheckLeaks (0, NULL, stdout, false), "psphot");
+    return;
+}
+
+psExit psphotGetExitStatus () {
+
+    psErrorCode err = psErrorCodeLast ();
+    switch (err) {
+      case PS_ERR_NONE:
+	return PS_EXIT_SUCCESS;
+      case PSPHOT_ERR_SYS:
+        return PS_EXIT_SYS_ERROR;
+      case PSPHOT_ERR_CONFIG:
+        return PS_EXIT_CONFIG_ERROR;
+      case PSPHOT_ERR_PROG:
+        return PS_EXIT_PROG_ERROR;
+      case PSPHOT_ERR_DATA:
+        return PS_EXIT_DATA_ERROR;
+      default:
+        return PS_EXIT_UNKNOWN_ERROR;
+    }
+    return PS_EXIT_UNKNOWN_ERROR;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotCullPeaks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotCullPeaks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotCullPeaks.c	(revision 22322)
@@ -0,0 +1,264 @@
+# include "psphotInternal.h"
+
+/*
+ * Cull a set of peaks contained in a psArray of pmFootprints
+ */
+psErrorCode
+psphotCullPeaks(const psImage *image,   // the image wherein lives the footprint
+                const psImage *weight,  // corresponding variance image
+                const psMetadata *recipe,
+                psArray *footprints) {  // array of pmFootprints
+    bool status = false;
+    float nsigma_delta = psMetadataLookupF32(&status, recipe, "FOOTPRINT_CULL_NSIGMA_DELTA");
+    if (!status) {
+        nsigma_delta = 0; // min.
+    }
+    float nsigma_min = psMetadataLookupF32(&status, recipe, "FOOTPRINT_CULL_NSIGMA_MIN");
+    if (!status) {
+        nsigma_min = 0;
+    }
+    const float skyStdev = psMetadataLookupF32(NULL, recipe, "SKY_STDEV");
+
+    return pmFootprintArrayCullPeaks(image, weight, footprints,
+                                     nsigma_delta, nsigma_min*skyStdev);
+}
+
+
+/*
+ * Cull an entire psArray of pmFootprints
+ * XXX drop this intermediate level function?
+ */
+psErrorCode
+pmFootprintArrayCullPeaks(const psImage *img, // the image wherein lives the footprint
+			  const psImage *weight,	// corresponding variance image
+			  psArray *footprints, // array of pmFootprints
+			  const float nsigma_delta, // how many sigma above local background a peak
+    					// needs to be to survive
+			  const float min_threshold) { // minimum permitted coll height
+    for (int i = 0; i < footprints->n; i++) {
+	pmFootprint *fp = footprints->data[i];
+	if (pmFootprintCullPeaks(img, weight, fp, nsigma_delta, min_threshold) != PS_ERR_NONE) {
+	    return psError(PS_ERR_UNKNOWN, false, "Culling pmFootprint %d", fp->id);
+	}
+    }
+    
+    return PS_ERR_NONE;
+}
+
+ /*
+  * Examine the peaks in a pmFootprint, and throw away the ones that are not sufficiently
+  * isolated.  More precisely, for each peak find the highest coll that you'd have to traverse
+  * to reach a still higher peak --- and if that coll's more than nsigma DN below your
+  * starting point, discard the peak.
+  */
+psErrorCode pmFootprintCullPeaks_OLD(const psImage *img, // the image wherein lives the footprint
+				 const psImage *weight,	// corresponding variance image
+				 pmFootprint *fp, // Footprint containing mortal peaks
+				 const float nsigma_delta, // how many sigma above local background a peak
+				 	// needs to be to survive
+				 const float min_threshold) { // minimum permitted coll height
+    assert (img != NULL); assert (img->type.type == PS_TYPE_F32);
+    assert (weight != NULL); assert (weight->type.type == PS_TYPE_F32);
+    assert (img->row0 == weight->row0 && img->col0 == weight->col0);
+    assert (fp != NULL);
+
+    if (fp->peaks == NULL || fp->peaks->n == 0) { // nothing to do
+	return PS_ERR_NONE;
+    }
+
+    psRegion subRegion;			// desired subregion; 1 larger than bounding box (grr)
+    subRegion.x0 = fp->bbox.x0; subRegion.x1 = fp->bbox.x1 + 1;
+    subRegion.y0 = fp->bbox.y0; subRegion.y1 = fp->bbox.y1 + 1;
+    const psImage *subImg = psImageSubset((psImage *)img, subRegion);
+    const psImage *subWt = psImageSubset((psImage *)weight, subRegion);
+    assert (subImg != NULL && subWt != NULL);
+    //
+    // We need a psArray of peaks brighter than the current peak.  We'll fake this
+    // by reusing the fp->peaks but lying about n.
+    //
+    // We do this for efficiency (otherwise I'd need two peaks lists), and we are
+    // rather too chummy with psArray in consequence.  But it works.
+    //
+    psArray *brightPeaks = psArrayAlloc(0);
+    psFree(brightPeaks->data);
+    brightPeaks->data = psMemIncrRefCounter(fp->peaks->data);// use the data from fp->peaks
+    //
+    // The brightest peak is always safe; go through other peaks trying to cull them
+    //
+    for (int i = 1; i < fp->peaks->n; i++) { // n.b. fp->peaks->n can change within the loop
+	const pmPeak *peak = fp->peaks->data[i];
+	int x = peak->x - subImg->col0;
+	int y = peak->y - subImg->row0;
+	//
+	// Find the level nsigma below the peak that must separate the peak
+	// from any of its friends
+	//
+	assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);
+	const float stdev = sqrt(subWt->data.F32[y][x]);
+	float threshold = subImg->data.F32[y][x] - nsigma_delta*stdev;
+	if (isnan(threshold) || threshold < min_threshold) {
+#if 1	  // min_threshold is assumed to be below the detection threshold,
+	  // so all the peaks are pmFootprint, and this isn't the brightest
+	    // XXX mark peak to be dropped
+	    (void)psArrayRemoveIndex(fp->peaks, i);
+	    i--;			// we moved everything down one
+	    continue;
+#else
+#error n.b. We will be running LOTS of checks at this threshold, so only find the footprint once
+	    threshold = min_threshold;
+#endif
+	}
+
+	// XXX EAM : if stdev >= 0, i'm not sure how this can ever be true?
+	if (threshold > subImg->data.F32[y][x]) {
+	    threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;
+	}
+
+	// XXX this is a bit expensive: psImageAlloc for every peak contained in this footprint
+	// perhaps this should alloc a single ID image above and pass it in to be set.
+
+	const int peak_id = 1;		// the ID for the peak of interest
+	brightPeaks->n = i;		// only stop at a peak brighter than we are
+
+	// XXX optionally use the faster pmFootprintsFind if the subimage size is large (eg, M31)
+
+	pmFootprint *peakFootprint = pmFootprintsFindAtPoint(subImg, threshold, brightPeaks, peak->y, peak->x);
+	brightPeaks->n = 0;		// don't double free
+	psImage *idImg = pmSetFootprintID(NULL, peakFootprint, peak_id);
+	psFree(peakFootprint);
+
+	// Check if any of the previous (brighter) peaks are within the footprint of this peak
+	// If so, the current peak is bogus; drop it.
+	int j;
+	for (j = 0; j < i; j++) {
+	    const pmPeak *peak2 = fp->peaks->data[j];
+	    int x2 = peak2->x - subImg->col0;
+	    int y2 = peak2->y - subImg->row0;
+	    const int peak2_id = idImg->data.S32[y2][x2]; // the ID for some other peak
+
+	    if (peak2_id == peak_id) {	// There's a brighter peak within the footprint above
+		;			// threshold; so cull our initial peak
+		(void)psArrayRemoveIndex(fp->peaks, i);
+		i--;			// we moved everything down one
+		break;
+	    }
+	}
+	if (j == i) {
+	    j++;
+	}
+
+	psFree(idImg);
+    }
+
+    brightPeaks->n = 0; psFree(brightPeaks);
+    psFree((psImage *)subImg);
+    psFree((psImage *)subWt);
+
+    return PS_ERR_NONE;
+}
+
+ /*
+  * Examine the peaks in a pmFootprint, and throw away the ones that are not sufficiently
+  * isolated.  More precisely, for each peak find the highest coll that you'd have to traverse
+  * to reach a still higher peak --- and if that coll's more than nsigma DN below your
+  * starting point, discard the peak.
+  */
+
+# define IN_PEAK 1 
+psErrorCode pmFootprintCullPeaks(const psImage *img, // the image wherein lives the footprint
+				 const psImage *weight,	// corresponding variance image
+				 pmFootprint *fp, // Footprint containing mortal peaks
+				 const float nsigma_delta, // how many sigma above local background a peak
+				 // needs to be to survive
+				 const float min_threshold) { // minimum permitted coll height
+    assert (img != NULL); assert (img->type.type == PS_TYPE_F32);
+    assert (weight != NULL); assert (weight->type.type == PS_TYPE_F32);
+    assert (img->row0 == weight->row0 && img->col0 == weight->col0);
+    assert (fp != NULL);
+
+    if (fp->peaks == NULL || fp->peaks->n == 0) { // nothing to do
+	return PS_ERR_NONE;
+    }
+
+    psRegion subRegion;			// desired subregion; 1 larger than bounding box (grr)
+    subRegion.x0 = fp->bbox.x0; subRegion.x1 = fp->bbox.x1 + 1;
+    subRegion.y0 = fp->bbox.y0; subRegion.y1 = fp->bbox.y1 + 1;
+
+    psImage *subImg = psImageSubset((psImage *)img, subRegion);
+    psImage *subWt = psImageSubset((psImage *)weight, subRegion);
+    assert (subImg != NULL && subWt != NULL);
+
+    psImage *idImg = psImageAlloc(subImg->numCols, subImg->numRows, PS_TYPE_S32);
+
+    // We need a psArray of peaks brighter than the current peak.  
+    // We reject peaks which either:
+    // 1) are below the local threshold
+    // 2) have a brighter peak within their threshold
+
+    // allocate the full-sized array.  if the final array is much smaller, we can realloc
+    // at that point.
+    psArray *brightPeaks = psArrayAllocEmpty(fp->peaks->n);
+    psArrayAdd (brightPeaks, 128, fp->peaks->data[0]);
+
+    // The brightest peak is always safe; go through other peaks trying to cull them
+    for (int i = 1; i < fp->peaks->n; i++) { // n.b. fp->peaks->n can change within the loop
+	const pmPeak *peak = fp->peaks->data[i];
+	int x = peak->x - subImg->col0;
+	int y = peak->y - subImg->row0;
+	//
+	// Find the level nsigma below the peak that must separate the peak
+	// from any of its friends
+	//
+	assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);
+	const float stdev = sqrt(subWt->data.F32[y][x]);
+	float threshold = subImg->data.F32[y][x] - nsigma_delta*stdev;
+	if (isnan(threshold) || threshold < min_threshold) {
+	    // min_threshold is assumed to be below the detection threshold,
+	    // so all the peaks are pmFootprint, and this isn't the brightest
+	    continue;
+	}
+
+	// XXX EAM : if stdev >= 0, i'm not sure how this can ever be true?
+	if (threshold > subImg->data.F32[y][x]) {
+	    threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;
+	}
+
+	// XXX this is a bit expensive: psImageAlloc for every peak contained in this footprint
+	// perhaps this should alloc a single ID image above and pass it in to be set.
+
+	// XXX optionally use the faster pmFootprintsFind if the subimage size is large (eg, M31)
+
+	// at this point brightPeaks only has the peaks brighter than the current
+	pmFootprint *peakFootprint = pmFootprintsFindAtPoint(subImg, threshold, brightPeaks, peak->y, peak->x);
+
+	// XXX need to supply the image here
+	// we set the IDs to either 1 (in peak) or 0 (not in peak)
+	pmSetFootprintID (idImg, peakFootprint, IN_PEAK);
+	psFree(peakFootprint);
+
+	// Check if any of the previous (brighter) peaks are within the footprint of this peak
+	// If so, the current peak is bogus; drop it.
+	bool keep = true;
+	for (int j = 0; keep && (j < brightPeaks->n); j++) {
+	    const pmPeak *peak2 = fp->peaks->data[j];
+	    int x2 = peak2->x - subImg->col0;
+	    int y2 = peak2->y - subImg->row0;
+	    if (idImg->data.S32[y2][x2] == IN_PEAK) 
+		// There's a brighter peak within the footprint above threshold; so cull our initial peak
+		keep = false;
+	}
+	if (!keep) continue;
+
+	psArrayAdd (brightPeaks, 128, fp->peaks->data[i]);
+    }
+
+    psFree (fp->peaks);
+    fp->peaks = brightPeaks;
+
+    psFree(idImg);
+    psFree(subImg);
+    psFree(subWt);
+
+    return PS_ERR_NONE;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotDeblendSatstars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotDeblendSatstars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotDeblendSatstars.c	(revision 22322)
@@ -0,0 +1,159 @@
+# include "psphotInternal.h"
+
+bool psphotDeblendSatstars (psArray *sources, psMetadata *recipe) {
+
+    int N;
+    pmSource *source;
+
+    psTimerStart ("psphot");
+
+    int Nblend = 0;
+    float SAT_TEST_LEVEL = 50000;
+    float SAT_MIN_RADIUS = 5.0;
+
+    // we need sources spatially-sorted to find overlaps
+    sources = psArraySort (sources, pmSourceSortByY);
+
+    // source analysis is done in peak order (brightest first)
+    // we use an index for this so the spatial sorting is kept
+    psVector *SN = psVectorAlloc (sources->n, PS_DATA_F32);
+    for (int i = 0; i < SN->n; i++) {
+        source = sources->data[i];
+        SN->data.F32[i] = source->peak->SN;
+    }
+    psVector *index = psVectorSortIndex (NULL, SN);
+    // this results in an index of increasing SN
+
+    // examine sources in decreasing SN order
+    for (int i = sources->n - 1; i >= 0; i--) {
+        N = index->data.U32[i];
+        source = sources->data[N];
+
+        // XXX filter? if (source->mode & PM_SOURCE_MODE_SATSTAR) continue;
+        if (source->mode & PM_SOURCE_MODE_BLEND) continue;
+        if (source->peak->flux < SAT_TEST_LEVEL) continue;
+
+	// save these for reference below
+	int xPeak = source->peak->x;
+	int yPeak = source->peak->y;
+
+        // generate a basic contour (set of x,y coordinates at-or-below flux level)
+        psArray *contour = pmSourceContour (source->pixels, xPeak, yPeak, SAT_TEST_LEVEL);
+        if (contour == NULL) continue;
+
+	// contour consists of a set of X,Y coords giving the boundary
+	psVector *xVec = contour->data[0];
+	psVector *yVec = contour->data[1];
+
+	// XXX should we filter based on the number of pixels in the contour?
+
+	// find the center of the contour (let's just use mid[x,y])
+	int xMin = xVec->data.F32[0];
+	int xMax = xVec->data.F32[0];
+	int yMin = yVec->data.F32[0];
+	int yMax = yVec->data.F32[0];
+	for (int j = 0; j < xVec->n; j++) {
+	    xMin = PS_MIN (xMin, xVec->data.F32[j]);
+	    xMax = PS_MAX (xMax, xVec->data.F32[j]);
+	    yMin = PS_MIN (yMin, yVec->data.F32[j]);
+	    yMax = PS_MAX (yMax, yVec->data.F32[j]);
+	}	
+	int xCenter = 0.5*(xMin + xMax);
+	int yCenter = 0.5*(yMin + yMax);
+	psFree (contour);
+
+	// reset the peak for this source to the value of the center pixel
+	source->peak->x = xCenter;
+	source->peak->y = yCenter;
+	source->peak->xf = xCenter;
+	source->peak->yf = yCenter;
+	
+	// temporary array for overlapping objects we find
+        psArray *overlap = psArrayAllocEmpty (100);
+
+        // search backwards for overlapping sources
+        for (int j = N - 1; j >= 0; j--) {
+            pmSource *testSource = sources->data[j];
+            if (testSource->peak->x <  source->pixels->col0) continue;
+            if (testSource->peak->x >= source->pixels->col0 + source->pixels->numCols) continue;
+            if (testSource->peak->y <  source->pixels->row0) break;
+            if (testSource->peak->y >= source->pixels->row0 + source->pixels->numRows) {
+                fprintf (stderr, "warning: invalid condition\n");
+                continue;
+            }
+            psArrayAdd (overlap, 100, testSource);
+        }
+
+        // search forwards for overlapping sources
+        for (int j = N + 1; j < sources->n; j++) {
+            pmSource *testSource = sources->data[j];
+            if (testSource->peak->x <  source->pixels->col0) continue;
+            if (testSource->peak->x >= source->pixels->col0 + source->pixels->numCols) continue;
+            if (testSource->peak->y <  source->pixels->row0) {
+                fprintf (stderr, "warning: invalid condition\n");
+                continue;
+            }
+            if (testSource->peak->y >= source->pixels->row0 + source->pixels->numRows) break;
+            psArrayAdd (overlap, 100, testSource);
+        }
+
+        if (overlap->n == 0) {
+            psFree (overlap);
+            continue;
+        }
+
+	// now find the contour which is at 0.5*SAT_TEST_LEVEL (xPeak, yPeak is valid high pixel)
+        contour = pmSourceContour (source->pixels, xPeak, yPeak, 0.5*SAT_TEST_LEVEL);
+        if (contour == NULL) {
+	    psFree (overlap);
+	    continue; 
+	}
+	
+	// any peaks within this contour should be dropped (mark as blend, drop after loop is done)
+	// also drop any peaks which are too close to this peal
+        psVector *xv = contour->data[0];
+        psVector *yv = contour->data[1];
+        for (int k = 0; k < overlap->n; k++) {
+            pmSource *testSource = overlap->data[k];
+	    float radius = hypot((testSource->peak->x - xCenter), (testSource->peak->y - yCenter));
+	    if (radius < SAT_MIN_RADIUS) {
+                testSource->mode |= PM_SOURCE_MODE_BLEND;
+                Nblend ++;
+		continue;
+	    }
+            for (int j = 0; j < xv->n; j+=2) {
+                if (fabs(yv->data.F32[j] - testSource->peak->y) > 0.5) continue;
+                if (xv->data.F32[j+0] > testSource->peak->x) break;
+                if (xv->data.F32[j+1] < testSource->peak->x) break;
+                testSource->mode |= PM_SOURCE_MODE_BLEND;
+                Nblend ++;
+                j = xv->n; // skip rest of contour
+            }
+        }
+        psFree (overlap);
+        psFree (contour);
+    }
+
+    // drop the sources marked as BLEND
+    for (int i = 0; i < sources->n;) {
+	pmSource *source = sources->data[i];
+
+        if (!(source->mode & PM_SOURCE_MODE_BLEND)) {
+	    i++;
+	    continue;
+	}
+
+	// shuffle the remaining sources forward
+	for (int j = i; j < sources->n - 1; j++) {
+	    sources->data[j] = sources->data[j+1];
+	}
+	psFree (source);
+	sources->n --;
+    }
+
+    psFree (SN);
+    psFree (index);
+
+    psLogMsg ("psphot", PS_LOG_INFO, "found %d satstar blend peaks, leaving %ld sources: %f sec\n", Nblend, sources->n, psTimerMark ("psphot"));
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotDefineFiles.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotDefineFiles.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotDefineFiles.c	(revision 22322)
@@ -0,0 +1,134 @@
+# include "psphotInternal.h"
+
+// XXX we need to be able to distinguish several cases:
+// 1) the particular output data was not requested
+// 2) the particular output data was requested but was not generated (skipped)
+// 3) the particular output data was requested but was not generated (for valid failure)
+// 4) the particular output data was requested but was not generated (surprising failure)
+
+// define the needed / desired I/O files
+bool psphotDefineFiles (pmConfig *config, pmFPAfile *input) {
+
+    bool status = false;
+
+    // select recipe options supplied on command line
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+
+    // the output sources are carried on the input->fpa structures
+    pmFPAfile *outsources = pmFPAfileDefineOutputFromFile (config, input, "PSPHOT.OUTPUT");
+    if (!outsources) {
+        psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.OUTPUT");
+        return false;
+    }
+    outsources->save = true;
+
+    // if we are choosing not to save the output detections, mark this file inactive so it will
+    // not be written, but will keep the detections in memory for other functions (eg, astrometry)
+    if (!psMetadataLookupBool (NULL, recipe, "SAVE.OUTPUT")) {
+        pmFPAfileActivate (config->files, false, "PSPHOT.OUTPUT");
+        outsources->save = false;
+    }
+
+    // optionally save the residual image
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.RESID")) {
+        pmFPAfile *output = pmFPAfileDefineOutputFromFile (config, input, "PSPHOT.RESID");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.RESID");
+            return false;
+        }
+        output->save = true;
+    }
+    // optionally save the background model (small FITS image)
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.BACKMDL")) {
+        int DX = psMetadataLookupS32 (&status, recipe, "BACKGROUND.XBIN");
+        int DY = psMetadataLookupS32 (&status, recipe, "BACKGROUND.YBIN");
+        pmFPAfile *output = pmFPAfileDefineFromFile (config, input, DX, DY, "PSPHOT.BACKMDL");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.BACKMDL");
+            return false;
+        }
+        output->save = true;
+    }
+    // optionally save the background model's standard deviation (small FITS image)
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.BACKMDL.STDEV")) {
+        int DX = psMetadataLookupS32 (&status, recipe, "BACKGROUND.XBIN");
+        int DY = psMetadataLookupS32 (&status, recipe, "BACKGROUND.YBIN");
+        pmFPAfile *output = pmFPAfileDefineFromFile (config, input, DX, DY, "PSPHOT.BACKMDL.STDEV");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.BACKMDL.STDEV");
+            return false;
+        }
+        output->save = true;
+    }
+    // optionally save the full background image
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.BACKGND")) {
+        pmFPAfile *output = pmFPAfileDefineFromFile (config, input,  1,  1, "PSPHOT.BACKGND");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.BACKGND");
+            return false;
+        }
+        output->save = true;
+    }
+    // optionally save the background-subtracted image
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.BACKSUB")) {
+        pmFPAfile *output = pmFPAfileDefineFromFile (config, input,  1,  1, "PSPHOT.BACKSUB");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.");
+            return false;
+        }
+        output->save = true;
+    }
+    // optionally save the PSF Model
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.PSF")) {
+        pmFPAfile *output = pmFPAfileDefineOutputFromFile (config, input, "PSPHOT.PSF.SAVE");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.PSF.SAVE");
+            return false;
+        }
+        output->save = true;
+    }
+
+    // optionally save output plots
+    // allow specific plots only
+    if (psMetadataLookupBool(NULL, recipe, "SAVE.PLOTS")) {
+        pmFPAfile *output = NULL;
+        output = pmFPAfileDefineOutputFromFile (config, input, "SOURCE.PLOT.MOMENTS");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for SOURCE.PLOT.MOMENTS");
+            return false;
+        }
+        output->save = true;
+        output = pmFPAfileDefineOutputFromFile (config, input, "SOURCE.PLOT.PSFMODEL");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for SOURCE.PLOT.PSFMODEL");
+            return false;
+        }
+        output->save = true;
+        output = pmFPAfileDefineOutputFromFile (config, input, "SOURCE.PLOT.APRESID");
+        if (!output) {
+            psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for SOURCE.PLOT.APRESID");
+            return false;
+        }
+        output->save = true;
+    }
+
+    if (psMetadataLookupPtr(NULL, config->arguments, "SRC")) {
+        if (!pmFPAfileDefineFromArgs (&status, config, "PSPHOT.INPUT.CMF", "SRC")) {
+            psError(PSPHOT_ERR_CONFIG, false, "Failed to find/build PSPHOT.INPUT.CMF");
+            return status;
+        }
+    }
+
+    if (psMetadataLookupPtr(NULL, config->arguments, "PSPHOT.PSF")) {
+        pmFPAfileBindFromArgs(&status, input, config, "PSPHOT.PSF.LOAD", "PSPHOT.PSF");
+        if (!status) {
+            psError(PSPHOT_ERR_CONFIG, false, "Failed to find/build PSPHOT.PSF.LOAD");
+            return status;
+        }
+    }
+
+    // XXX add in example PSF image thumbnails
+    // pmFPAfileConstruct (config->files, format, config->camera, "PSPHOT.PSF_SAMPLE");
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotDiagnosticPlots.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotDiagnosticPlots.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotDiagnosticPlots.c	(revision 22322)
@@ -0,0 +1,192 @@
+# include "psphotInternal.h"
+
+# if (HAVE_KAPA)
+
+static int kapa_fd = -1;
+
+int psphotKapaOpen ()
+{
+    char kapa[64];
+
+    strcpy (kapa, "kapa");
+
+    if (kapa_fd == -1) {
+        kapa_fd = KapaOpenNamedSocket (kapa, "psphot");
+    }
+    return kapa_fd;
+}
+
+bool psphotKapaClose ()
+{
+
+    if (kapa_fd == -1)
+        return true;
+    KapaClose (kapa_fd);
+    kapa_fd = -1;
+    return true;
+}
+
+bool psphotImageBackgroundCellHistogram (psVector *values, float mean, float sigma, int ix, int iy)
+{
+
+    KapaSection section;
+    Graphdata graphdata;
+
+    int kapa = pmKapaOpen (true);
+    if (kapa == -1) {
+        psError(PS_ERR_UNKNOWN, true, "failure to open kapa");
+        return false;
+    }
+
+    psStats *stats = psStatsAlloc (PS_STAT_MAX | PS_STAT_MIN);
+    psVectorStats (stats, values, NULL, NULL, 0);
+
+    psHistogram *histogram = psHistogramAlloc (stats->min, stats->max, 1000);
+    psVectorHistogram (histogram, values, NULL, NULL, 0);
+
+    KapaInitGraph (&graphdata);
+    KapaClearPlots (kapa);
+
+    // plot 1 is the full range
+    section.x  = 0.0;
+    section.y  = 0.0;
+    section.dx = 1.0;
+    section.dy = 0.5;
+    section.name = strcreate ("bottom");
+    KapaSetSection (kapa, &section);
+    free (section.name);
+
+    // set limits based on data values
+    graphdata.xmin = stats->min;
+    graphdata.xmax = stats->max;
+
+    psStatsInit (stats);
+    stats->options = PS_STAT_MAX | PS_STAT_MIN;
+    psVectorStats (stats, histogram->nums, NULL, NULL, 0);
+
+    // scale the plot to hold the histogram
+    graphdata.ymin = stats->min - 0.05*(stats->max - stats->min);
+    graphdata.ymax = stats->max + 0.05*(stats->max - stats->min);
+
+    KapaSetLimits (kapa, &graphdata);
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, &graphdata);
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 0;
+    graphdata.size = 0.5;
+    graphdata.style = 1;
+    KapaPrepPlot (kapa, histogram->nums->n, &graphdata);
+    KapaPlotVector (kapa, histogram->nums->n, histogram->bounds->data.F32, "x");
+    KapaPlotVector (kapa, histogram->nums->n, histogram->nums->data.F32, "y");
+    psFree (histogram);
+
+    // plot 2 is the +/- 10 sigma
+    section.x  = 0.0;
+    section.y  = 0.5;
+    section.dx = 1.0;
+    section.dy = 0.5;
+    section.name = strcreate ("top");
+    KapaSetSection (kapa, &section);
+    free (section.name);
+
+    // +/- 10 sigma
+    graphdata.xmin = mean - 10.0*sigma;
+    graphdata.xmax = mean + 10.0*sigma;
+
+    histogram = psHistogramAlloc (graphdata.xmin, graphdata.xmax, 100);
+    psVectorHistogram (histogram, values, NULL, NULL, 0);
+
+    psStatsInit (stats);
+    stats->options = PS_STAT_MAX | PS_STAT_MIN;
+    psVectorStats (stats, histogram->nums, NULL, NULL, 0);
+
+    // scale the plot to hold the histogram
+    graphdata.ymin = stats->min - 0.05*(stats->max - stats->min);
+    graphdata.ymax = stats->max + 0.05*(stats->max - stats->min);
+
+    KapaSetLimits (kapa, &graphdata);
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, &graphdata);
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 0;
+    graphdata.size = 0.5;
+    graphdata.style = 1;
+    KapaPrepPlot (kapa, histogram->nums->n, &graphdata);
+    KapaPlotVector (kapa, histogram->nums->n, histogram->bounds->data.F32, "x");
+    KapaPlotVector (kapa, histogram->nums->n, histogram->nums->data.F32, "y");
+
+    char line[128];
+    sprintf (line, "sky: %f +/- %f, cell %d,%d", mean, sigma, ix, iy);
+    KapaSendLabel (kapa, line, KAPA_LABEL_XP);
+
+    // pause until user types 'return'
+    fprintf(stdout, "press return");
+    if (!fgets(line, 64, stdin)) {
+        // This is just to avoid a compiler warning on some systems; it's not really necessary
+        psWarning("Couldn't read anything.");
+    }
+
+    psFree (stats);
+    psFree (histogram);
+
+    return true;
+}
+
+bool psphotDiagnosticPlots (pmConfig *config, char *name, ...) {
+
+    va_list argPtr;
+    bool status;
+
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+    assert (recipe);
+
+    psMetadata *plots = psMetadataLookupPtr (&status, recipe, "DIAGNOSTIC.PLOTS");
+    assert (plots);
+
+    // do we want the requested plot?
+    if (!psMetadataLookupBool (&status, plots, name)) return false;
+
+    // Get the variable list parameters to pass to allocation function
+    va_start(argPtr, name);
+
+    if (!strcmp(name, "IMAGE.BACKGROUND.CELL.HISTOGRAM")) {
+
+        int ix = va_arg(argPtr, psS32);
+        int iy = va_arg(argPtr, psS32);
+        float mean  = va_arg(argPtr, double);
+        float sigma = va_arg(argPtr, double);
+        psVector *values = va_arg(argPtr, psPtr);
+
+        int xPlot = psMetadataLookupS32 (&status, plots, "IMAGE.BACKGROUND.CELL.HISTOGRAM.X");
+        assert (status);
+        int yPlot = psMetadataLookupS32 (&status, plots, "IMAGE.BACKGROUND.CELL.HISTOGRAM.Y");
+        assert (status);
+
+        bool gotX = (xPlot < 0) || (xPlot == ix);
+        bool gotY = (yPlot < 0) || (yPlot == iy);
+
+        if (gotX && gotY) {
+            psphotImageBackgroundCellHistogram (values, mean, sigma, ix, iy);
+            goto done;
+        }
+    }
+
+    // Clean up stack after variable arguement has been used
+done:
+    va_end(argPtr);
+    return true;
+}
+
+# else
+
+int psphotKapaOpen () { return -1; }
+bool psphotKapaClose () { return true; }
+bool psphotImageBackgroundCellHistogram (psVector *values, float mean, float sigma, int ix, int iy)
+{
+    return true;
+}
+bool psphotDiagnosticPlots (pmConfig *config, char *name, ...) { return true; }
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,25 @@
+/*
+ * The line
+    { PSPHOT_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+#include "pslib.h"
+#include "psphotErrorCodes.h"
+
+void psphotErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { PSPHOT_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PSPHOT_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = PSPHOT_ERR_NERROR - PSPHOT_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotErrorCodes.dat	(revision 22322)
@@ -0,0 +1,17 @@
+#
+# This file is used to generate psphotErrorClasses.h
+#
+UNKNOWN			Unknown PM error code
+NOT_IMPLEMENTED		Desired feature is not yet implemented
+FITS			Problem in FITS I/O
+FITS_WCS		Error interpreting FITS WCS information
+PHOTOM			Problem in photometry
+PSF			Problem in PSF
+APERTURE		Problem with aperture photometry
+SKY			Problem in sky determination
+# these errors correspond to standard exit conditions
+ARGUMENTS               Incorrect arguments
+SYS                     System error
+CONFIG                  Problem in configure files
+PROG                    Programming error
+DATA                    invalid data
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,18 @@
+#if !defined(PSPHOT_ERROR_CODES_H)
+#define PSPHOT_ERROR_CODES_H
+/*
+ * The line
+ *  PSPHOT_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    PSPHOT_ERR_BASE = 1300,
+    PSPHOT_ERR_${ErrorCode},
+    PSPHOT_ERR_NERROR
+} psphotErrorCode;
+
+void psphotErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotEvalFLT.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotEvalFLT.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotEvalFLT.c	(revision 22322)
@@ -0,0 +1,64 @@
+# include "psphotInternal.h"
+
+bool psphotEvalEXT (pmSource *source, pmModel *model)
+{ 
+    int keep;
+
+    // do we actually have a valid EXT model?
+    if (model == NULL) {
+	source->mode &= ~PM_SOURCE_MODE_FITTED;
+	psTrace ("psphot", 5, "no model fitted?\n");
+	return false;
+    }
+
+    // was the model actually fitted?
+    if (!(model->flags & PM_MODEL_STATUS_FITTED)) {
+	source->mode &= ~PM_SOURCE_MODE_FITTED; 
+	psTrace ("psphot", 5, "no model fitted?\n");
+	return false;
+    }
+
+    // did the model fit fail for one or another reason?
+    if (model->flags & (PM_MODEL_STATUS_BADARGS | 
+			PM_MODEL_STATUS_NONCONVERGE | 
+			PM_MODEL_STATUS_OFFIMAGE)) {
+	source->mode |= PM_SOURCE_MODE_FAIL;
+	psLogMsg ("psphot", 5, "EXT fail fit\n");
+	psTrace ("psphot", 5, "EXT fail fit\n");
+
+	if (model->flags & PM_MODEL_STATUS_OFFIMAGE) {
+	  psTrace ("psphot", 5, "off image\n");
+	}
+	if (model->flags & PM_MODEL_STATUS_BADARGS) {
+	  psTrace ("psphot", 5, "bad args\n");
+	}
+	if (model->flags & PM_MODEL_STATUS_NONCONVERGE) {
+	  psTrace ("psphot", 5, "non converge\n");
+	}
+	return false;
+    }
+
+    // unless we prove otherwise, this object is extended
+    source->type = PM_SOURCE_TYPE_EXTENDED;
+
+    // the following source->mode information pertains to modelEXT:
+    source->mode |= PM_SOURCE_MODE_EXTMODEL;
+
+    // if the object has a fitted peak below 0, the fit did not converge cleanly
+    // XXX this limit is fairly arbitrary, and must be > the value is the model limits
+    if (model->params->data.F32[PM_PAR_I0] <= 0.02) {
+	source->mode |= PM_SOURCE_MODE_FAIL;
+	psTrace ("psphot", 5, "model central intensity ~ zero\n");
+	return false;
+    } 
+
+    keep = model->modelFitStatus(model);
+    if (keep) return true;
+
+    // poor-quality fit; only keep if nothing else works...
+    psLogMsg ("psphot", 5, "EXT poor fit\n");
+    psTrace ("psphot", 5, "EXT poor fit\n");
+
+    source->mode |= PM_SOURCE_MODE_POOR;
+    return false;
+}	
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotEvalPSF.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotEvalPSF.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotEvalPSF.c	(revision 22322)
@@ -0,0 +1,238 @@
+# include "psphotInternal.h"
+
+// given a pmSource which has been fitted using modelPSF, evaluate the
+// resulting fit: did the fit succeed? is this object PSF-like? is this object 
+// extended?  return status is TRUE for a valid PSF, FALSE otherwise.
+
+// identify objects consistent with PSF shape/magnitude distribution
+// we expect dparams[PM_PAR_SXX],dparams[PM_PAR_SXX] to have a scatter of:
+// sigma_x / (S/N)
+
+// any objects which is consistent with the PSF should have 
+// abs(dparams[PM_PAR_SXX]) < N * dsxLine(mag) & abs(dparams[PM_PAR_SYY]) < N * dsyLine(mag)
+// this includes a minimum buffer (DS) for the brighter objects
+
+// EAM : 2006.10.20 : I have re-defined params[PM_PAR_SXX] : it is now sqrt(2)*sigma_x, not 1/sigma_x
+
+// saturated stars should fall outside (larger), but have peaks above SATURATION
+// extended sources should be larger, cosmic rays smaller
+// we also reject objects with S/N too low or ChiSquare to high
+
+// floor for DS value 
+// XXX EAM : add to configuration?
+
+static float SATURATION;
+static float PSF_MIN_SN;
+static float PSF_MIN_DS;
+static float PSF_MAX_CHI;
+static float PSF_SHAPE_NSIGMA;
+static float PSF_DSX_MEAN;
+static float PSF_DSY_MEAN;
+static float PSF_DSX_STDEV;
+static float PSF_DSY_STDEV;
+
+bool psphotInitLimitsPSF (psMetadata *recipe, pmReadout *readout) {
+
+    bool status;
+
+    // XXX do we need to set this differently from the value used to mark saturated pixels?
+    pmCell *cell     = readout->parent;
+    SATURATION       = psMetadataLookupF32 (&status, cell->concepts, "CELL.SATURATION");
+    PSF_MIN_SN       = psMetadataLookupF32 (&status, recipe, "PSF_MIN_SN");
+    PSF_MAX_CHI      = psMetadataLookupF32 (&status, recipe, "PSF_MAX_CHI");
+    PSF_SHAPE_NSIGMA = psMetadataLookupF32 (&status, recipe, "PSF_SHAPE_NSIGMA");
+    PSF_MIN_DS       = psMetadataLookupF32 (&status, recipe, "PSF_MIN_DS");
+    if (!status) PSF_MIN_DS = 0.01;
+
+    PSF_DSX_MEAN     = psMetadataLookupF32 (&status, recipe, "DSX_MEAN");
+    PSF_DSY_MEAN     = psMetadataLookupF32 (&status, recipe, "DSY_MEAN");
+    PSF_DSX_STDEV    = psMetadataLookupF32 (&status, recipe, "DSX_STDV");
+    PSF_DSY_STDEV    = psMetadataLookupF32 (&status, recipe, "DSY_STDV");
+
+    return true;
+}
+
+// examine the model->status, fit parameters, etc and decide if the model succeeded
+// set the source->type and source->mode appropriately
+bool psphotEvalPSF (pmSource *source, pmModel *model) { 
+
+    int keep;
+    float dSX, dSY, SX, SY, SN;
+    float nSx, nSy, Chi;
+
+    // do we actually have a valid PSF model?
+    if (model == NULL) {
+	source->mode &= ~PM_SOURCE_MODE_FITTED;
+	return false;
+    }
+
+    // was the model actually fitted?
+    if (!(model->flags & PM_MODEL_STATUS_FITTED)) {
+	source->mode &= ~PM_SOURCE_MODE_FITTED; 
+	if (source->mode & PM_SOURCE_MODE_SATSTAR) {
+	    psTrace ("psphot", 5, "satstar was not fitted");
+	}
+	return false;
+    }
+    // did the model fit fail for one or another reason?
+    if (model->flags & (PM_MODEL_STATUS_BADARGS | 
+			PM_MODEL_STATUS_NONCONVERGE | 
+			PM_MODEL_STATUS_OFFIMAGE)) {
+	source->mode |= PM_SOURCE_MODE_FAIL;
+	if (source->mode & PM_SOURCE_MODE_SATSTAR) {
+	    psTrace ("psphot", 5, "satstar failed fit");
+	}
+	return false;
+    }
+
+    // unless we prove otherwise, this object is a star.
+    source->type = PM_SOURCE_TYPE_STAR;
+
+    // the following source->mode information pertains to modelPSF:
+    source->mode |= PM_SOURCE_MODE_PSFMODEL;
+
+    // if the object has fitted peak above saturation, label as SATSTAR
+    // this is a valid PSF object, but ignore the other quality tests
+    // remember: fit does not use saturated pixels (masked)
+    // XXX no extended object can saturate and stay extended...
+    if (model->params->data.F32[PM_PAR_I0] >= SATURATION) {
+	if (source->mode & PM_SOURCE_MODE_PSFSTAR) {
+	    psLogMsg ("psphot", 5, "PSFSTAR marked SATSTAR\n");
+	}
+	source->mode |=  PM_SOURCE_MODE_SATSTAR;
+	return true;
+    } 
+
+    // if the object has a fitted peak below 0.02, the source is not viable
+    // perhaps the fit did not converge cleanly
+    if (model->params->data.F32[PM_PAR_I0] <= 0.02) {
+	source->mode |= PM_SOURCE_MODE_FAIL;
+	if (source->mode & PM_SOURCE_MODE_SATSTAR) {
+	    psTrace ("psphot", 5, "satstar failed fit (peak below 0)");
+	}
+	return false;
+    } 
+
+    // if the source was predicted to be a SATSTAR, but it fitted below saturation, 
+    // make a note to the user
+    if (source->mode & PM_SOURCE_MODE_SATSTAR) {
+	psLogMsg ("psphot", 5, "SATSTAR marked normal (fitted peak below saturation)\n");
+	source->mode &= ~PM_SOURCE_MODE_SATSTAR;
+    }
+
+    SN  = model->params->data.F32[PM_PAR_I0]/model->dparams->data.F32[PM_PAR_I0];
+    SX  = model->params->data.F32[PM_PAR_SXX];
+    SY  = model->params->data.F32[PM_PAR_SYY];
+    dSX = model->dparams->data.F32[PM_PAR_SXX];
+    dSY = model->dparams->data.F32[PM_PAR_SYY];
+    Chi = model->chisqNorm / model->nDOF;
+
+    // swing of sigma_x,y in sigmas
+    nSx = (dSX - PSF_DSX_MEAN) / (PSF_DSX_STDEV * PS_MAX (PSF_MIN_DS, (SX / SN)));
+    nSy = (dSY - PSF_DSY_MEAN) / (PSF_DSY_STDEV * PS_MAX (PSF_MIN_DS, (SY / SN)));
+
+    // assign PM_SOURCE_MODE_GOODSTAR to bright objects within PSF region of dparams[]
+    keep = TRUE;
+    // keep &= (fabs(nSx) < PSF_SHAPE_NSIGMA);
+    // keep &= (fabs(nSy) < PSF_SHAPE_NSIGMA);
+    keep &= (SN > PSF_MIN_SN);
+    keep &= (Chi < PSF_MAX_CHI);
+
+    if (source->mode & PM_SOURCE_MODE_SATSTAR) {
+	psTrace ("psphot", 5, "satstar fit results: %f, %f  %d :  %f %f : %f %f : %f %f\n", 
+		     model->params->data.F32[PM_PAR_XPOS], model->params->data.F32[PM_PAR_YPOS], keep,
+		 dSX, nSx, dSY, nSy, SN, Chi);
+    }
+
+    if (keep) {
+	if (source->mode & PM_SOURCE_MODE_PSFSTAR) {
+	    psTrace ("psphot", 7, "PSFSTAR kept (%f, %f  :  %f %f : %f %f : %f %f)\n", 
+		     model->params->data.F32[PM_PAR_XPOS], model->params->data.F32[PM_PAR_YPOS], dSX, nSx, dSY, nSy, SN, Chi);
+	}
+	return true;
+    }
+
+    // this source is not a star, warn if it was a PSFSTAR
+    if (source->mode & PM_SOURCE_MODE_PSFSTAR) {
+	psTrace ("psphot", 5, "PSFSTAR demoted based on fit quality   (%f, %f  :  %f %f : %f %f : %f %f)\n", 
+		  model->params->data.F32[PM_PAR_XPOS], model->params->data.F32[PM_PAR_YPOS], dSX, nSx, dSY, nSy, SN, Chi);
+    } else {
+	psTrace ("psphot", 5, "fails PSF fit (%f, %f  :  %f %f : %f %f : %f %f)\n", 
+		  model->params->data.F32[PM_PAR_XPOS], model->params->data.F32[PM_PAR_YPOS], dSX, nSx, dSY, nSy, SN, Chi);
+    }
+
+    // object appears to be small, suspected defect.  this is a weak indication for a defect: a
+    // large positive or negative swing can also come from the bright sources for which we are 
+    // more sensitive to the wings than for the faint sources.  
+    // XXX allow -NSIGMA and +NSIGMA to have different values?
+    if ((nSx <= -PSF_SHAPE_NSIGMA) || (nSy <= -PSF_SHAPE_NSIGMA)) {
+	source->type = PM_SOURCE_TYPE_DEFECT;
+	return false;
+    }
+
+    // object appears to be large, suspected extended source
+    if ((nSx >= PSF_SHAPE_NSIGMA) || (nSy >= PSF_SHAPE_NSIGMA)) {
+	source->type = PM_SOURCE_TYPE_EXTENDED;
+	return false;
+    }
+
+    // poor-quality fit; only keep if nothing else works...
+    source->mode |= PM_SOURCE_MODE_POOR;
+    return false;
+}	
+
+// examine the model->status, fit parameters, etc and decide if the model succeeded
+// set the source->type and source->mode appropriately
+bool psphotEvalDBL (pmSource *source, pmModel *model) { 
+
+    // do we actually have a valid PSF model?
+    if (model == NULL) {
+	source->mode &= ~PM_SOURCE_MODE_FITTED;
+	return false;
+    }
+
+    // was the model actually fitted?
+    if (!(model->flags & PM_MODEL_STATUS_FITTED)) {
+	source->mode &= ~PM_SOURCE_MODE_FITTED; 
+	return false;
+    }
+    // did the model fit fail for one or another reason?
+    if (model->flags & (PM_MODEL_STATUS_BADARGS | 
+			PM_MODEL_STATUS_NONCONVERGE | 
+			PM_MODEL_STATUS_OFFIMAGE)) {
+	source->mode |= PM_SOURCE_MODE_FAIL;
+	return false;
+    }
+
+    // unless we prove otherwise, this object is a star.
+    source->type = PM_SOURCE_TYPE_STAR;
+
+    // the following source->mode information pertains to modelPSF:
+    source->mode |= PM_SOURCE_MODE_PSFMODEL;
+
+    // if the object has fitted peak above saturation, label as SATSTAR
+    // this is a valid PSF object, but ignore the other quality tests
+    // remember: fit does not use saturated pixels (masked)
+    // XXX no extended object can saturate and stay extended...
+    if (model->params->data.F32[PM_PAR_I0] >= SATURATION) {
+	if (source->mode & PM_SOURCE_MODE_PSFSTAR) {
+	    psLogMsg ("psphot", 5, "PSFSTAR marked SATSTAR\n");
+	}
+	source->mode |=  PM_SOURCE_MODE_SATSTAR;
+	return true;
+    } 
+
+    // if the object has a fitted peak below 0, the fit did not converge cleanly
+    if (model->params->data.F32[PM_PAR_I0] <= 0) {
+	source->mode |= PM_SOURCE_MODE_FAIL;
+	return false;
+    } 
+
+    // if the source was predicted to be a SATSTAR, but it fitted below saturation, 
+    // make a note to the user
+    if (source->mode & PM_SOURCE_MODE_SATSTAR) {
+	psLogMsg ("psphot", 5, "SATSTAR marked normal (fitted peak below saturation)\n");
+	source->mode &= ~PM_SOURCE_MODE_SATSTAR;
+    }
+    return true;
+}	
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotExtendedSourceAnalysis.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotExtendedSourceAnalysis.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotExtendedSourceAnalysis.c	(revision 22322)
@@ -0,0 +1,131 @@
+# include "psphotInternal.h"
+
+// aperture-like measurements for extended sources
+bool psphotExtendedSourceAnalysis (pmReadout *readout, psArray *sources, psMetadata *recipe) {
+
+    bool status;
+    int Next = 0;
+    int Npetro = 0;
+    int Nisophot = 0;
+    int Nannuli = 0;
+    int Nkron = 0;
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    // perform full non-linear fits / extended source analysis?
+    if (!psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANALYSIS")) {
+	psLogMsg ("psphot", PS_LOG_INFO, "skipping extended source measurements\n");
+	return true;
+    }
+
+    // option to limit analysis to a specific region
+    char *region = psMetadataLookupStr (&status, recipe, "ANALYSIS_REGION");
+    psRegion AnalysisRegion = psRegionForImage (readout->image, psRegionFromString (region));
+    if (psRegionIsNaN (AnalysisRegion)) psAbort("analysis region mis-defined");
+
+    // S/N limit to perform full non-linear fits
+    float SN_LIM = psMetadataLookupF32 (&status, recipe, "EXTENDED_SOURCE_SN_LIM");
+
+    // which extended source analyses should we perform?
+    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
+    bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
+    bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
+    bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
+
+    // source analysis is done in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // XXX some init functions for the extended source recipe options?
+
+    // choose the sources of interest
+    for (int i = 0; i < sources->n; i++) {
+
+	pmSource *source = sources->data[i];
+
+	// skip PSF-like and non-astronomical objects
+	if (source->type == PM_SOURCE_TYPE_STAR) continue;
+	if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
+	if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
+
+	// limit selection to some SN limit
+	assert (source->peak); // how can a source not have a peak?
+	if (source->peak->SN < SN_LIM) continue;
+
+	// limit selection by analysis region
+	if (source->peak->x < AnalysisRegion.x0) continue;
+	if (source->peak->y < AnalysisRegion.y0) continue;
+	if (source->peak->x > AnalysisRegion.x1) continue;
+	if (source->peak->y > AnalysisRegion.y1) continue;
+
+	// replace object in image
+	if (source->mode & PM_SOURCE_MODE_SUBTRACTED) {
+	    pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+	}
+	Next ++;
+
+	// if we request any of these measurements, we require the radial profile
+	if (doPetrosian || doIsophotal || doAnnuli || doKron) {
+	    if (!psphotRadialProfile (source, recipe, maskVal)) {
+		// all measurements below require the radial profile; skip them all
+		// re-subtract the object, leave local sky
+		psTrace ("psphot", 5, "failed to extract radial profile for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+		pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+		source->mode |= PM_SOURCE_MODE_SUBTRACTED;
+		continue;
+	    }
+	}
+
+	// Isophotal Mags
+	if (doIsophotal) {
+	    if (!psphotIsophotal (source, recipe, maskVal)) {
+		psTrace ("psphot", 5, "failed to measure isophotal mags for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+	    } else {
+		psTrace ("psphot", 5, "measured isophotal mags for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+		Nisophot ++;
+	    }
+	}
+
+	// Petrosian Mags
+	if (doPetrosian) {
+	    if (!psphotPetrosian (source, recipe, maskVal)) {
+		psTrace ("psphot", 5, "measured petrosian flux & radius for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+	    } else {
+		psTrace ("psphot", 5, "measured petrosian flux & radius for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+		Npetro ++;
+	    }
+	}
+
+	// Kron Mags
+	if (doKron) {
+	    if (!psphotKron (source, recipe, maskVal)) {
+		psTrace ("psphot", 5, "failed to measure kron mags for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+	    } else {
+		psTrace ("psphot", 5, "measure kron mags for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+		Nkron ++;
+	    }
+	}
+
+	// Radial Annuli
+	if (doAnnuli) {
+	    if (!psphotAnnuli (source, recipe, maskVal)) {
+		psError(PSPHOT_ERR_UNKNOWN, false, "failure in Annuli analysis");
+		return false;
+	    } 
+	    psTrace ("psphot", 5, "measured annuli for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+	    Nannuli ++;
+	}
+
+	// re-subtract the object, leave local sky
+	pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+	source->mode |= PM_SOURCE_MODE_SUBTRACTED;
+    }
+
+    psLogMsg ("psphot", PS_LOG_INFO, "extended source analysis: %f sec for %d objects\n", psTimerMark ("psphot"), Next);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d petrosian\n", Npetro);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d isophotal\n", Nisophot);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d annuli\n", Nannuli);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d kron\n", Nkron);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotExtendedSourceFits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotExtendedSourceFits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotExtendedSourceFits.c	(revision 22322)
@@ -0,0 +1,278 @@
+# include "psphotInternal.h"
+
+// non-linear model fitting for extended sources
+bool psphotExtendedSourceFits (pmReadout *readout, psArray *sources, psMetadata *recipe) {
+
+    bool status;
+    int Next = 0;
+    int Nconvolve = 0;
+    int NconvolvePass = 0;
+    int Nplain = 0;
+    int NplainPass = 0;
+    bool savePics = false;
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    psMaskType markVal = psMetadataLookupU8(&status, recipe, "MARK.PSPHOT"); // Mask value for bad pixels
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // perform full extended source non-linear fits?
+    if (!psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_FITS")) {
+        psLogMsg ("psphot", PS_LOG_INFO, "skipping extended source measurements\n");
+        return true;
+    }
+
+    // select the collection of desired models
+    psMetadata *models = psMetadataLookupMetadata (&status, recipe, "EXTENDED_SOURCE_MODELS");
+    if (!status) {
+        psWarning ("extended source model fits requested but model model is missing (EXTENDED_SOURCE_MODELS)\n");
+        return true;
+    }
+    if (models->list->n == 0) {
+        psWarning ("extended source model fits requested but no models are specified\n");
+        return true;
+    }
+
+    // validate the model entries
+    psMetadataIterator *iter = psMetadataIteratorAlloc (models, PS_LIST_HEAD, NULL);
+    psMetadataItem *item = NULL;
+    while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
+
+      if (item->type != PS_DATA_METADATA) {
+        psAbort ("Invalid type for EXTENDED_SOURCE_MODEL entry %s, not a metadata folder", item->name);
+        // XXX we could cull the bad entries or build a validated model folder
+      }
+
+      psMetadata *model = (psMetadata *) item->data.md;
+
+      // check on the model type
+      char *modelName = psMetadataLookupStr (&status, model, "MODEL");
+      int modelType = pmModelClassGetType (modelName);
+      if (modelType < 0) {
+        psAbort ("Unknown model class for EXTENDED_SOURCE_MODEL entry %s: %s", item->name, modelName);
+      }
+      psMetadataAddS32 (model, PS_LIST_TAIL, "MODEL_TYPE", PS_META_REPLACE, "", modelType);
+
+      // check on the SNLIM, set a float value
+      char *SNword = psMetadataLookupStr (&status, model, "SNLIM");
+      if (!status) {
+        psAbort("SNLIM not defined for extended source model %s\n", item->name);
+      }
+      float SNlim = atof (SNword);
+      psMetadataAddF32 (model, PS_LIST_TAIL, "SNLIM_VALUE", PS_META_REPLACE, "", SNlim);
+
+      // check on the PSF-Convolution status
+      char *convolvedWord = psMetadataLookupStr (&status, model, "PSF_CONVOLVED");
+      if (!status || (strcasecmp (convolvedWord, "true") && strcasecmp (convolvedWord, "false"))) {
+        psAbort ("PSF_CONVOLVED entry invalid or missing for EXTENDED_SOURCE_MODEL entry %s", item->name);
+      }
+      bool convolved = !strcasecmp (convolvedWord, "true");
+      psMetadataAddBool (model, PS_LIST_TAIL, "PSF_CONVOLVED_VALUE", PS_META_REPLACE, "", convolved);
+    }
+    psFree (iter);
+
+    // option to limit analysis to a specific region
+    char *region = psMetadataLookupStr (&status, recipe, "ANALYSIS_REGION");
+    psRegion AnalysisRegion = psRegionForImage (readout->image, psRegionFromString (region));
+    if (psRegionIsNaN (AnalysisRegion)) psAbort("analysis region mis-defined");
+
+    // what fraction of the PSF is used? (radius in pixels : 2 -> 5x5 box)
+    int psfSize = psMetadataLookupS32 (&status, recipe, "PCM_BOX_SIZE");
+    assert (status);
+
+    // source analysis is done in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // choose the sources of interest
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *source = sources->data[i];
+
+        // skip PSF-like and non-astronomical objects
+        if (source->type == PM_SOURCE_TYPE_STAR) continue;
+        if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
+        if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
+
+        // XXX this should use peak?
+        if (source->peak->x < AnalysisRegion.x0) continue;
+        if (source->peak->y < AnalysisRegion.y0) continue;
+        if (source->peak->x > AnalysisRegion.x1) continue;
+        if (source->peak->y > AnalysisRegion.y1) continue;
+
+        // if model is NULL, we don't have a starting guess
+        // XXX use the parameters guessed from moments
+        // if (source->modelEXT == NULL) continue;
+
+        // replace object in image
+        if (source->mode & PM_SOURCE_MODE_SUBTRACTED) {
+            pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+        }
+        Next ++;
+
+        // save the modelFlux here in case we need to subtract it (for failure)
+        psImage *modelFluxStart = psMemIncrRefCounter (source->modelFlux);
+
+        if (savePics) {
+          psphotSaveImage (NULL, readout->image, "image.xp.fits");
+        }
+
+        // array to store the pointers to the model flux images while the models are being fitted
+        psArray *modelFluxes = psArrayAllocEmpty (4);
+
+        // allocate the array to store the model fits
+        if (source->modelFits == NULL) {
+            source->modelFits = psArrayAllocEmpty (4);
+        }
+
+        // loop here over the models chosen for each source (exclude by S/N)
+        psMetadataIterator *iter = psMetadataIteratorAlloc (models, PS_LIST_HEAD, NULL);
+        psMetadataItem *item = NULL;
+        while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
+
+          // XXX this should have been forced above
+          assert (item->type == PS_DATA_METADATA);
+          psMetadata *model = (psMetadata *) item->data.md;
+
+          // check the SNlim and skip model if source is too faint
+          float SNlim = psMetadataLookupF32 (&status, model, "SNLIM_VALUE");
+          assert (status);
+
+          // limit selection to some SN limit
+          assert (source->peak); // how can a source not have a peak?
+          if (source->peak->SN < SNlim) continue;
+
+          // check on the model type
+          pmModelType modelType = psMetadataLookupS32 (&status, model, "MODEL_TYPE");
+          assert (status);
+
+          // check on the PSF-Convolution status
+          bool convolved = psMetadataLookupBool (&status, model, "PSF_CONVOLVED_VALUE");
+          assert (status);
+
+          // XXX psTraceSetLevel ("psLib.math.psMinimizeLMChi2", 6);
+          // XXX psTraceSetLevel ("psphot.psphotModelWithPSF_LMM", 6);
+
+          // fit the model as convolved or not
+          pmModel *modelFit = NULL;
+          if (convolved) {
+              modelFit = psphotPSFConvModel (readout, source, modelType, maskVal, markVal, psfSize);
+              if (!modelFit) {
+                  psTrace ("psphot", 5, "failed to fit psf-conv model for object at %f, %f", source->moments->x, source->moments->y);
+                  continue;
+              }
+              psTrace ("psphot", 4, "fit psf-conv model for %f, %f : %s chisq = %f\n", source->moments->x, source->moments->y, pmModelClassGetName (modelFit->type), modelFit->chisq);
+              Nconvolve ++;
+              if (!(modelFit->flags & (PM_MODEL_STATUS_BADARGS | PM_MODEL_STATUS_NONCONVERGE | PM_MODEL_STATUS_OFFIMAGE))) {
+                  NconvolvePass ++;
+              }
+          } else {
+              psFree (source->modelFlux);
+              source->modelFlux = NULL;
+              modelFit = psphotFitEXT (readout, source, modelType, maskVal, markVal);
+              if (!modelFit) {
+                  psTrace ("psphot", 5, "failed to fit plain model for object at %f, %f", source->moments->x, source->moments->y);
+                  continue;
+              }
+              pmSourceCacheModel (source, maskVal);
+              psTrace ("psphot", 4, "fit plain model for %f, %f : %s chisq = %f\n", source->moments->x, source->moments->y, pmModelClassGetName (modelFit->type), modelFit->chisq);
+              Nplain ++;
+              if (!(modelFit->flags & (PM_MODEL_STATUS_BADARGS | PM_MODEL_STATUS_NONCONVERGE | PM_MODEL_STATUS_OFFIMAGE))) {
+                  NplainPass ++;
+              }
+          }
+
+          // save each of the model flux images and store the best
+          psArrayAdd (modelFluxes, 4, source->modelFlux);
+
+          // test for fit quality / result
+          psArrayAdd (source->modelFits, 4, modelFit);
+
+          psFree (modelFit);
+        }
+        psFree (iter);
+
+        // evaluate the relative quality of the models, choose one
+        float minChisq = NAN;
+        int minModel = -1;
+        for (int i = 0; i < source->modelFits->n; i++) {
+            pmModel *model = source->modelFits->data[i];
+
+            if (!(model->flags & PM_MODEL_STATUS_FITTED)) continue;
+
+            if (model->flags & (PM_MODEL_STATUS_BADARGS)) continue;
+            if (model->flags & (PM_MODEL_STATUS_NONCONVERGE)) continue;
+            if (model->flags & (PM_MODEL_STATUS_OFFIMAGE)) continue;
+
+            if ((minModel < 0) || (model->chisq < minChisq)) {
+                minChisq = model->chisq;
+                minModel = i;
+            }
+        }
+
+        if (minModel == -1) {
+          // re-subtract the object, leave local sky
+          psTrace ("psphot", 5, "failed to fit extended source model to object at %f, %f", source->moments->x, source->moments->y);
+
+          // replace original model, subtract it
+          psFree (source->modelFlux);
+          source->modelFlux = modelFluxStart;
+
+          pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+          source->mode |= PM_SOURCE_MODE_SUBTRACTED;
+
+          psFree (modelFluxes);
+
+          if (savePics) {
+              psphotSaveImage (NULL, readout->image, "image.xp.fits");
+              char key[10];
+              fprintf (stdout, "continue? ");
+              if (!fgets (key, 8, stdin)) {
+                  psWarning("Couldn't read anything.");
+              } else if (key[0] == 'n') {
+                  savePics = false;
+              }
+          }
+          continue;
+        }
+
+        // save the best extended model in modelEXT
+        psFree (source->modelEXT);
+        source->modelEXT = psMemIncrRefCounter (source->modelFits->data[minModel]);
+
+        // save the modelFlux for the best model
+        psFree (source->modelFlux);
+        source->modelFlux = psMemIncrRefCounter (modelFluxes->data[minModel]);
+
+        // subtract the best fit from the object, leave local sky
+        pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+        source->mode |= PM_SOURCE_MODE_SUBTRACTED;
+
+        // the initial model flux is no longer needed
+        psFree (modelFluxStart);
+        psFree (modelFluxes);
+
+        psTrace ("psphot", 4, "best ext model for %f, %f : %s chisq = %f\n", source->moments->x, source->moments->y, pmModelClassGetName (source->modelEXT->type), source->modelEXT->chisq);
+        psTrace ("psphot", 5, "extended source model for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+
+        if (savePics) {
+          psphotSaveImage (NULL, readout->image, "image.xm.fits");
+          char key[10];
+          fprintf (stdout, "continue? ");
+          if (!fgets (key, 8, stdin)) {
+              psWarning("Couldn't read anything.");
+          } else if (key[0] == 'n') {
+              savePics = false;
+          }
+        }
+    }
+
+    psLogMsg ("psphot", PS_LOG_INFO, "extended source analysis: %f sec for %d objects\n", psTimerMark ("psphot"), Next);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d convolved models (%d passed)\n", Nconvolve, NconvolvePass);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d plain models (%d passed)\n", Nplain, NplainPass);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotExtendedSources.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotExtendedSources.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotExtendedSources.c	(revision 22322)
@@ -0,0 +1,137 @@
+# include "psphot.h"
+
+bool psphotExtendedSources (pmReadout *readout, psArray *sources, psMetadata *recipe) {
+
+    bool status;
+    int Next = 0;
+    int Nconvolve = 0;
+    int Npetro = 0;
+    int Nisophot = 0;
+    int Nannuli = 0;
+    int Nkron = 0;
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    // perform full non-linear fits / extended source analysis?
+    if (!psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_FITS")) {
+	psLogMsg ("psphot", PS_LOG_INFO, "skipping extended source measurements\n");
+	return true;
+    }
+
+    // option to limit analysis to a specific region
+    char *region = psMetadataLookupStr (&status, recipe, "ANALYSIS_REGION");
+    psRegion AnalysisRegion = psRegionForImage (readout->image, psRegionFromString (region));
+    if (psRegionIsNaN (AnalysisRegion)) psAbort("analysis region mis-defined");
+
+    // S/N limit to perform full non-linear fits
+    float SN_LIM = psMetadataLookupF32 (&status, recipe, "EXTENDED_SOURCE_SN_LIM");
+
+    // which extended source analyses should we perform?
+    bool doPSFConvModel = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PSF_CONVOLVED_MODEL");
+    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
+    bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
+    bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
+    bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
+
+    // source analysis is done in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // XXX some init functions for the extended source recipe options?
+
+    // choose the sources of interest
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *source = sources->data[i];
+
+        // skip PSF-like and non-astronomical objects
+        if (source->type == PM_SOURCE_TYPE_STAR) continue;
+        if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
+        if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
+
+        // limit selection to some SN limit
+        assert (source->peak); // how can a source not have a peak?
+        if (source->peak->SN < SN_LIM) continue;
+
+        // XXX this should use peak?
+        if (source->peak->x < AnalysisRegion.x0) continue;
+        if (source->peak->y < AnalysisRegion.y0) continue;
+        if (source->peak->x > AnalysisRegion.x1) continue;
+        if (source->peak->y > AnalysisRegion.y1) continue;
+
+        // if model is NULL, we don't have a starting guess
+	// XXX this is probably not needed for objects where we only measure aperture-like terms
+        if (source->modelEXT == NULL) continue;
+
+        // replace object in image
+        if (source->mode & PM_SOURCE_MODE_SUBTRACTED) {
+            pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+        }
+        Next ++;
+
+	if (doPSFConvModel && !psphotPSFConvModel (source, recipe, maskVal)) {
+	    psError(PSPHOT_ERR_UNKNOWN, false, "failure in PSF Convolved Model fit");
+	    return false;
+	} else {
+	  // XXX why am I caching the model?
+	    pmSourceCacheModel (source, maskVal); // XXX put this in the source model function?
+	    psTrace ("psphot", 5, "psf-convolved model for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+            Nconvolve ++;
+	}
+
+	// all of these below require the radial profile
+	// XXX push this as a test and call in each of the functions below?
+	// XXX this constructs a pmSourceExtendedParameters element
+	if (doPetrosian || doIsophotal || doAnnuli || doKron) {
+	  if (!psphotRadialProfile (source, recipe, maskVal)) {
+	    psError(PSPHOT_ERR_UNKNOWN, false, "failure to generate radial profile");
+	    return false;
+	  }
+	}
+
+	if (doPetrosian && !psphotPetrosian (source, recipe, maskVal)) {
+	    psError(PSPHOT_ERR_UNKNOWN, false, "failure in Petrosian analysis");
+	    return false;
+	} else {
+	    psTrace ("psphot", 5, "measured petrosian flux & radius for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+            Npetro ++;
+	}
+
+	if (doIsophotal && !psphotIsophotal (source, recipe, maskVal)) {
+	    psError(PSPHOT_ERR_UNKNOWN, false, "failure in Isophotal analysis");
+	    return false;
+	} else {
+	    psTrace ("psphot", 5, "psf-convolved model for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+            Nisophot ++;
+	}
+
+	if (doAnnuli && !psphotAnnuli (source, recipe, maskVal)) {
+	    psError(PSPHOT_ERR_UNKNOWN, false, "failure in Annuli analysis");
+	    return false;
+	} else {
+	    psTrace ("psphot", 5, "psf-convolved model for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+            Nannuli ++;
+	}
+
+	if (doKron && !psphotKron (source, recipe, maskVal)) {
+	    psError(PSPHOT_ERR_UNKNOWN, false, "failure in Kron analysis");
+	    return false;
+	} else {
+	    psTrace ("psphot", 5, "psf-convolved model for source at %7.1f, %7.1f", source->moments->x, source->moments->y);
+            Nkron ++;
+	}
+
+        // re-subtract the object, leave local sky
+        pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+        source->mode |= PM_SOURCE_MODE_SUBTRACTED;
+    }
+
+    psLogMsg ("psphot", PS_LOG_INFO, "extended source analysis: %f sec for %d objects\n", psTimerMark ("psphot"), Next);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d convolved models\n", Nconvolve);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d petrosian\n", Npetro);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d isophotal\n", Nisophot);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d annuli\n", Nannuli);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d kron\n", Nkron);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotFakeSources.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotFakeSources.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotFakeSources.c	(revision 22322)
@@ -0,0 +1,30 @@
+# include "psphotInternal.h"
+
+psArray *psphotFakeSources () {
+
+    // psphotUpdateHeader (header, config);
+
+    psArray *sources = psArrayAlloc (50);
+
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = pmSourceAlloc ();
+        source->moments = pmMomentsAlloc ();
+        source->moments->x = 10;
+        source->moments->y = 10;
+        source->moments->Sx = 1;
+        source->moments->Sy = 1;
+        source->moments->Sxy = 0;
+        source->moments->Sum = 1000;
+        source->moments->Peak = 100;
+        source->moments->Sky = 10;
+        source->moments->nPixels = 10;
+
+        source->peak = pmPeakAlloc (10, 10, 0, 0);
+        source->type = PM_SOURCE_TYPE_STAR;
+
+        pmModelType modelType = pmModelClassGetType ("PS_MODEL_QGAUSS");
+        source->modelPSF = pmSourceModelGuess (source, modelType);
+        sources->data[i] = source;
+    }
+    return sources;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotFindDetections.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotFindDetections.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotFindDetections.c	(revision 22322)
@@ -0,0 +1,57 @@
+# include "psphotInternal.h"
+
+// smooth the image, search for peaks, optionally define footprints based on the peaks
+pmDetections *psphotFindDetections (pmDetections *detections, pmReadout *readout, psMetadata *recipe) {
+
+    bool status;
+    int pass;
+    float NSIGMA_PEAK = 25.0;
+    int NMAX = 0;
+
+    psTimerStart ("psphot");
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    // Use the new pmFootprints approach?
+    const bool useFootprints = psMetadataLookupBool(NULL, recipe, "USE_FOOTPRINTS");
+
+    // on first pass, detections have not yet been allocated
+    if (!detections) {
+	detections = pmDetectionsAlloc();
+	pass = 1;
+        NSIGMA_PEAK = psMetadataLookupF32 (&status, recipe, "PEAKS_NSIGMA_LIMIT"); PS_ASSERT (status, NULL);
+        NMAX = psMetadataLookupS32 (&status, recipe, "PEAKS_NMAX"); PS_ASSERT (status, NULL);
+    } else {
+	pass = 2;
+        NSIGMA_PEAK = psMetadataLookupF32 (&status, recipe, "PEAKS_NSIGMA_LIMIT_2"); PS_ASSERT (status, NULL);
+	NMAX = 0; // unlimited number of peaks in final pass: allow a limit (PEAKS_NMAX_2) ?
+    }
+
+    float threshold = PS_SQR(NSIGMA_PEAK);
+
+    // move the old peaks array (if it exists) to oldPeaks 
+    // XXX generically, we should be able to call this function an arbitrary number of times
+    assert (detections->oldPeaks == NULL);
+    detections->oldPeaks = detections->peaks;
+    detections->peaks = NULL;
+
+    // generate the smoothed significance image
+    psImage *significance = psphotSignificanceImage (readout, recipe, pass, maskVal);
+
+    // detect the peaks in the significance image
+    detections->peaks = psphotFindPeaks (significance, readout, recipe, threshold, NMAX);
+    psMetadataAddF32  (recipe, PS_LIST_TAIL, "PEAK_THRESHOLD", PS_META_REPLACE, "Peak Detection Threshold", threshold);
+
+    // optionally merge peaks into footprints
+    if (useFootprints) {
+	psphotFindFootprints (detections, significance, readout, recipe, pass, maskVal);
+    }
+
+    psFree (significance);
+    return detections;
+}
+
+// if we use the footprints, the output peaks list contains both old and new peaks,
+// otherwise it only contains the new peaks.
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotFindFootprints.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotFindFootprints.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotFindFootprints.c	(revision 22322)
@@ -0,0 +1,72 @@
+# include "psphotInternal.h"
+
+bool psphotFindFootprints (pmDetections *detections, psImage *significance, pmReadout *readout, psMetadata *recipe, const int pass, psMaskType maskVal) {
+
+    bool status;
+
+    psTimerStart ("footprints");
+
+    int npixMin = psMetadataLookupS32(&status, recipe, "FOOTPRINT_NPIXMIN");
+    PS_ASSERT (status, false);
+
+    float FOOTPRINT_NSIGMA_LIMIT;
+    if (pass == 1) {
+	FOOTPRINT_NSIGMA_LIMIT = psMetadataLookupS32(&status, recipe, "FOOTPRINT_NSIGMA_LIMIT");
+    } else {
+	FOOTPRINT_NSIGMA_LIMIT = psMetadataLookupS32(&status, recipe, "FOOTPRINT_NSIGMA_LIMIT_2");
+    }
+    PS_ASSERT (status, false);
+
+    // XXX do we need to use the same threshold here as for peaks?  does it make sense for
+    // these to be different?
+
+    float threshold = PS_SQR(FOOTPRINT_NSIGMA_LIMIT);
+
+    int growRadius = 0;
+    if (pass == 1) {
+	growRadius = psMetadataLookupS32(&status, recipe, "FOOTPRINT_GROW_RADIUS");
+    } else {
+	growRadius = psMetadataLookupS32(&status, recipe, "FOOTPRINT_GROW_RADIUS_2");
+    }
+    PS_ASSERT (status, false);
+
+    // find the raw footprints & assign the peaks to those footprints 
+    psArray *footprints = pmFootprintsFind (significance, threshold, npixMin);
+    pmFootprintsAssignPeaks(footprints, detections->peaks);
+    // XXX handle the error conditions here
+
+    // footprints now owns the peaks; after culling (below), we will rebuild the peaks array
+    psFree (detections->peaks); 
+
+    psLogMsg ("psphot", PS_LOG_DETAIL, "found %ld footprints: %f sec\n", footprints->n, psTimerMark ("footprints"));
+
+    // optionally grow footprints isotropically by growRadius pixels
+    if (growRadius > 0) {
+	psArray *tmp = pmFootprintArrayGrow(footprints, growRadius);
+	psLogMsg ("psphot", PS_LOG_DETAIL, "grow footprint coverage by %d pixels, %ld footprints become %ld footprints: %f sec\n", growRadius, footprints->n, tmp->n, psTimerMark ("footprints"));
+	psFree(footprints);
+	footprints = tmp;
+    }
+
+    if (pass == 2) {
+	// merge in old peaks;
+	const int includePeaks = 0x0 | 0x2; // i.e. just from newFootprints
+	
+	// XXX EAM : still not sure I understand this: are we double-couning or undercounting peaks
+
+	psArray *mergedFootprints = pmFootprintArraysMerge(detections->footprints, footprints, includePeaks);
+	psLogMsg ("psphot", PS_LOG_DETAIL, "merged %ld new footprints with %ld existing ones: %f sec\n", footprints->n, detections->footprints->n, psTimerMark ("footprints"));
+
+	psFree(footprints);
+	psFree(detections->footprints);
+	detections->footprints = mergedFootprints;
+    } else {
+	detections->footprints = footprints;
+    }
+
+    psphotCullPeaks(readout->image, readout->weight, recipe, detections->footprints);
+    detections->peaks = pmFootprintArrayToPeaks(detections->footprints);
+    psLogMsg ("psphot", PS_LOG_INFO, "%ld peaks, %ld total footprints: %f sec\n", detections->peaks->n, detections->footprints->n, psTimerMark ("footprints"));
+
+    return detections;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotFindPeaks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotFindPeaks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotFindPeaks.c	(revision 22322)
@@ -0,0 +1,54 @@
+# include "psphotInternal.h"
+
+// Find peaks in the significance image above a threshold significance level.  The significance
+// image must be constructed to represent (S/N)^2.  If nMax is non-zero, only return a maximum
+// of nMax peaks
+psArray *psphotFindPeaks (psImage *significance, pmReadout *readout, psMetadata *recipe, const float threshold, const int nMax) {
+
+    bool status = false;
+
+    psTimerStart ("peaks");
+
+    // find the peaks in the smoothed image
+    psArray *peaks = pmPeaksInImage (significance, threshold);
+    if (peaks == NULL) {
+	// XXX should we be sending back an empty array instead of NULL?
+        // XXX this may also be due to a programming or config error
+        // XXX do we need to set something in the readout->analysis to indicate that
+        // we tried and failed to find peaks (something in the header data)
+        psError(PSPHOT_ERR_DATA, false, "no peaks found in this image");
+        return false;
+    }
+
+    // correct the peak values to S/N = sqrt(significance)
+    // get the peak flux from the unsmoothed image
+    // the peak pixel coords are guaranteed to be on the image
+    int row0 = readout->image->row0;
+    int col0 = readout->image->col0;
+    for (int i = 0; i < peaks->n; i++) {
+        pmPeak *peak = peaks->data[i];
+        peak->SN = sqrt(peak->value);
+        peak->flux = readout->image->data.F32[peak->y-row0][peak->x-col0];
+    }
+
+    // limit the total number of returned peaks as specified
+    psArraySort (peaks, pmPeakSortBySN);
+    if (nMax && (peaks->n > nMax)) {
+	psArray *tmpPeaks = psArrayAllocEmpty (nMax);
+	for (int i = 0; i < nMax; i++) {
+	    psArrayAdd (tmpPeaks, 100, peaks->data[i]);
+	}
+	psFree (peaks);
+	peaks = tmpPeaks;
+    }
+
+    // optional dump of all peak data 
+    char *output = psMetadataLookupStr (&status, recipe, "PEAKS_OUTPUT_FILE");
+    if (output && strcasecmp (output, "NONE")) {
+        pmPeaksWriteText (peaks, output);
+    }
+    psLogMsg ("psphot", PS_LOG_INFO, "%ld peaks: %f sec\n", peaks->n, psTimerMark ("peaks"));
+
+    return peaks;
+
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotFitSet.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotFitSet.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotFitSet.c	(revision 22322)
@@ -0,0 +1,48 @@
+# include "psphotInternal.h"
+
+// This is only used by psphotModelTest.c
+bool psphotFitSet (pmSource *source, pmModel *oneModel, char *fitset, pmSourceFitMode mode, psMaskType maskVal) {
+
+    double x, y, Io;
+
+    FILE *f = fopen (fitset, "r");
+    if (f == NULL) return false;
+
+    psArray *modelSet = psArrayAllocEmpty (16);
+
+    while (fscanf (f, "%lf %lf %lf", &x, &y, &Io) == 3) {
+        pmModel *model = pmModelAlloc (oneModel->type);
+
+        for (psS32 i = 0; i < model->params->n; i++) {
+            model->params->data.F32[i] = oneModel->params->data.F32[i];
+            model->dparams->data.F32[i] = oneModel->dparams->data.F32[i];
+        }
+        model->params->data.F32[1] = Io;
+        model->params->data.F32[2] = x;
+        model->params->data.F32[3] = y;
+        psArrayAdd (modelSet, 16, model);
+    }
+
+    // XXX pmSourceFitSet must cache the modelFlux?
+    pmSourceFitSet (source, modelSet, mode, maskVal);
+
+    // write out positive object
+    psphotSaveImage (NULL, source->pixels, "object.fits");
+
+    // subtract object, leave local sky
+    for (int i = 0; i < modelSet->n; i++) {
+        pmModel *model = modelSet->data[i];
+        pmModelSub (source->pixels, source->maskObj, model, PM_MODEL_OP_FULL, maskVal);
+
+        fprintf (stderr, "output parameters (obj %d):\n", i);
+        for (int n = 0; n < model->params->n; n++) {
+            fprintf (stderr, "%d : %f\n", n, model->params->data.F32[n]);
+        }
+    }
+
+    // write out
+    psphotSaveImage (NULL, source->pixels, "resid.fits");
+    psphotSaveImage (NULL, source->maskObj, "mask.fits");
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotFitSourcesLinear.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotFitSourcesLinear.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotFitSourcesLinear.c	(revision 22322)
@@ -0,0 +1,309 @@
+# include "psphotInternal.h"
+
+// fit flux (and optionally sky model) to all reasonable sources
+// with the linear fitting process.  sources must have an associated
+// model with selected pixels, and the fit radius must be defined
+
+// given the set of sources, each of which points to the pixels in the
+// science image, we construct a set of simulated sources with their own pixels.
+// these are used to determine the simultaneous linear fit of fluxes.
+// the analysis is performed wrt the simulated pixel values
+
+static bool SetBorderMatrixElements (psSparseBorder *border, pmReadout *readout, psArray *sources, bool constant_weights, int SKY_FIT_ORDER, psMaskType markVal);
+
+bool psphotFitSourcesLinear (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, bool final) {
+
+    bool status;
+    float x;
+    float y;
+    float f;
+    // float r;
+
+    psTimerStart ("psphot");
+
+    // bit-masks to test for good/bad pixels
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psMaskType markVal = psMetadataLookupU8(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // source analysis is done in spatial order
+    sources = psArraySort (sources, pmSourceSortByY);
+
+    // storage array for fitSources
+    psArray *fitSources = psArrayAllocEmpty (sources->n);
+
+    // option to limit analysis to a specific region
+    char *region = psMetadataLookupStr (&status, recipe, "ANALYSIS_REGION");
+    psRegion AnalysisRegion = psRegionFromString (region);
+    AnalysisRegion = psRegionForImage (readout->image, AnalysisRegion);
+    if (psRegionIsNaN (AnalysisRegion)) psAbort("analysis region mis-defined");
+
+    bool CONSTANT_PHOTOMETRIC_WEIGHTS =
+        psMetadataLookupBool(&status, recipe, "CONSTANT_PHOTOMETRIC_WEIGHTS");
+    if (!status) {
+        psAbort("You must provide a value for the BOOL recipe CONSTANT_PHOTOMETRIC_WEIGHTS");
+    }
+    int SKY_FIT_ORDER = psMetadataLookupS32(&status, recipe, "SKY_FIT_ORDER");
+    if (!status) {
+        SKY_FIT_ORDER = 0;
+    }
+    bool SKY_FIT_LINEAR = psMetadataLookupBool(&status, recipe, "SKY_FIT_LINEAR");
+    if (!status) {
+        SKY_FIT_LINEAR = false;
+    }
+
+    // select the sources which will be used for the fitting analysis
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+
+        // skip non-astronomical objects (very likely defects)
+        if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
+        if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
+        if (source->type == PM_SOURCE_TYPE_STAR &&
+            source->mode & PM_SOURCE_MODE_SATSTAR) continue;
+        if (final) {
+            if (source->mode &  PM_SOURCE_MODE_SUBTRACTED) continue;
+        } else {
+            if (source->mode &  PM_SOURCE_MODE_BLEND) continue;
+        }
+
+	// generate model for sources without, or skip if we can't
+	if (!source->modelFlux) {
+	    if (!pmSourceCacheModel (source, maskVal)) continue;
+	}
+
+        // save the original coords
+        x = source->peak->xf;
+        y = source->peak->yf;
+
+        // is the source in the region of interest?
+        if (x < AnalysisRegion.x0) continue;
+        if (y < AnalysisRegion.y0) continue;
+        if (x > AnalysisRegion.x1) continue;
+        if (y > AnalysisRegion.y1) continue;
+
+        psArrayAdd (fitSources, 100, source);
+    }
+    psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "built fitSources: %f (%ld objects)\n", psTimerMark ("psphot"), sources->n);
+
+    // vectors to store stats for each object
+    // psVector *weight = psVectorAlloc (fitSources->n, PS_TYPE_F32);
+    psVector *errors = psVectorAlloc (fitSources->n, PS_TYPE_F32);
+
+    // create the border matrix (includes the sparse matrix)
+    // for just sky: 1 row; for x,y terms: 3 rows
+    psSparse *sparse = psSparseAlloc (fitSources->n, 100);
+    int nBorder = (SKY_FIT_ORDER == 0) ? 1 : 3;
+    psSparseBorder *border = psSparseBorderAlloc (sparse, nBorder);
+
+    // fill out the sparse matrix elements and border elements (B)
+    // SRCi is the current source of interest
+    // SRCj is a possibly overlapping source
+    for (int i = 0; i < fitSources->n; i++) {
+        pmSource *SRCi = fitSources->data[i];
+
+        // diagonal elements of the sparse matrix (auto-cross-product)
+        f = pmSourceModelDotModel (SRCi, SRCi, CONSTANT_PHOTOMETRIC_WEIGHTS);
+        psSparseMatrixElement (sparse, i, i, f);
+
+        // the formal error depends on the weighting scheme
+        if (CONSTANT_PHOTOMETRIC_WEIGHTS) {
+            float var = pmSourceModelDotModel (SRCi, SRCi, false);
+            errors->data.F32[i] = 1.0 / sqrt(var);
+        } else {
+            errors->data.F32[i] = 1.0 / sqrt(f);
+        }
+
+
+        // find the image x model value
+        f = pmSourceDataDotModel (SRCi, SRCi, CONSTANT_PHOTOMETRIC_WEIGHTS);
+        psSparseVectorElement (sparse, i, f);
+
+        // add the per-source weights (border region)
+        switch (SKY_FIT_ORDER) {
+          case 1:
+            f = pmSourceModelWeight (SRCi, 1, CONSTANT_PHOTOMETRIC_WEIGHTS);
+            psSparseBorderElementB (border, i, 1, f);
+            f = pmSourceModelWeight (SRCi, 2, CONSTANT_PHOTOMETRIC_WEIGHTS);
+            psSparseBorderElementB (border, i, 2, f);
+
+          case 0:
+            f = pmSourceModelWeight (SRCi, 0, CONSTANT_PHOTOMETRIC_WEIGHTS);
+            psSparseBorderElementB (border, i, 0, f);
+            break;
+
+          default:
+            psAbort("Invalid SKY_FIT_ORDER %d\n", SKY_FIT_ORDER);
+            break;
+        }
+
+        // loop over all other stars following this one
+        for (int j = i + 1; j < fitSources->n; j++) {
+            pmSource *SRCj = fitSources->data[j];
+
+            // skip over disjoint source images, break after last possible overlap
+            if (SRCi->pixels->row0 + SRCi->pixels->numRows < SRCj->pixels->row0) break;
+            if (SRCj->pixels->row0 + SRCj->pixels->numRows < SRCi->pixels->row0) continue;
+            if (SRCi->pixels->col0 + SRCi->pixels->numCols < SRCj->pixels->col0) continue;
+            if (SRCj->pixels->col0 + SRCj->pixels->numCols < SRCi->pixels->col0) continue;
+
+            // got an overlap; calculate cross-product and add to output array
+            f = pmSourceModelDotModel (SRCi, SRCj, CONSTANT_PHOTOMETRIC_WEIGHTS);
+            psSparseMatrixElement (sparse, j, i, f);
+        }
+    }
+
+    psSparseResort (sparse);
+    psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "built matrix: %f (%d elements)\n", psTimerMark ("psphot"), sparse->Nelem);
+
+    // set the sky, sky_x, sky_y components of border matrix
+    SetBorderMatrixElements (border, readout, fitSources, CONSTANT_PHOTOMETRIC_WEIGHTS, SKY_FIT_ORDER, markVal);
+    psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "set border: %f (%d elements)\n", psTimerMark ("psphot"), sparse->Nelem);
+
+    psSparseConstraint constraint;
+    constraint.paramMin   = 0.0;
+    constraint.paramMax   = 1e8;
+    constraint.paramDelta = 1e8;
+
+    // solve for normalization terms (need include local sky?)
+    psVector *norm = NULL;
+    psVector *skyfit = NULL;
+    if (SKY_FIT_LINEAR) {
+        psSparseBorderSolve (&norm, &skyfit, constraint, border, 5);
+        fprintf (stderr, "skyfit: %f\n", skyfit->data.F32[0]);
+    } else {
+        norm = psSparseSolve (NULL, constraint, sparse, 5);
+        skyfit = NULL;
+    }
+    psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "solve matrix: %f (%d elements)\n", psTimerMark ("psphot"), sparse->Nelem);
+
+    // adjust I0 for fitSources and subtract
+    for (int i = 0; i < fitSources->n; i++) {
+        pmSource *source = fitSources->data[i];
+        pmModel *model = pmSourceGetModel (NULL, source);
+
+        // assign linearly-fitted normalization
+        if (isnan(norm->data.F32[i])) {
+            psAbort("linear fitted source is nan");
+        }
+        model->params->data.F32[PM_PAR_I0] = norm->data.F32[i];
+        model->dparams->data.F32[PM_PAR_I0] = errors->data.F32[i];
+        // XXX is the value of 'errors' modified by the sky fit?
+
+        // subtract object
+        pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+        source->mode |= PM_SOURCE_MODE_SUBTRACTED;
+    }
+    psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "sub models: %f (%d elements)\n", psTimerMark ("psphot"), sparse->Nelem);
+
+    // measure chisq for each source
+    for (int i = 0; final && (i < fitSources->n); i++) {
+        pmSource *source = fitSources->data[i];
+        pmModel *model = pmSourceGetModel (NULL, source);
+        pmSourceChisq (model, source->pixels, source->maskObj, source->weight, maskVal);
+    }
+    psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "get chisqs: %f (%d elements)\n", psTimerMark ("psphot"), sparse->Nelem);
+
+    // psFree (index);
+    psFree (sparse);
+    psFree (fitSources);
+    psFree (norm);
+    psFree (skyfit);
+    psFree (errors);
+    psFree (border);
+
+    psLogMsg ("psphot.ensemble", PS_LOG_INFO, "measure ensemble of PSFs: %f sec\n", psTimerMark ("psphot"));
+    return true;
+}
+
+// XXX do we need this?
+
+// Calculate the weight terms for the sky fit component of the matrix.  This function operates
+// on the pixels which correspond to all of the sources of interest.  These elements fill in
+// the border matrix components in the sparse matrix equation.
+static bool SetBorderMatrixElements (psSparseBorder *border, pmReadout *readout, psArray *sources, bool constant_weights, int SKY_FIT_ORDER, psMaskType markVal) {
+
+    // generate the image-wide weight terms
+    // turn on MARK for all image pixels
+    psRegion fullArray = psRegionSet (0, 0, 0, 0);
+    fullArray = psRegionForImage (readout->mask, fullArray);
+    psImageMaskRegion (readout->mask, fullArray, "OR", markVal);
+    psLogMsg ("psphot.ensemble", PS_LOG_INFO, "step 1: %f sec\n", psTimerMark ("psphot"));
+
+    // turn off MARK for all object pixels
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+        pmModel *model = pmSourceGetModel (NULL, source);
+        if (model == NULL) continue;
+        float x = model->params->data.F32[PM_PAR_XPOS];
+        float y = model->params->data.F32[PM_PAR_YPOS];
+        psImageMaskCircle (source->maskView, x, y, model->radiusFit, "AND", PS_NOT_U8(markVal));
+    }
+    psLogMsg ("psphot.ensemble", PS_LOG_INFO, "step 2: %f sec\n", psTimerMark ("psphot"));
+
+    // accumulate the image statistics from the masked regions
+    psF32 **image  = readout->image->data.F32;
+    psF32 **weight = readout->weight->data.F32;
+    psU8  **mask   = readout->mask->data.U8;
+
+    double w, x, y, x2, xy, y2, xc, yc, wt, f, fo, fx, fy;
+    w = x = y = x2 = xy = y2 = fo = fx = fy = 0;
+
+    int col0 = readout->image->col0;
+    int row0 = readout->image->row0;
+
+    for (int j = 0; j < readout->image->numRows; j++) {
+        for (int i = 0; i < readout->image->numCols; i++) {
+            if (mask[j][i]) continue;
+            if (constant_weights) {
+                wt = 1.0;
+            } else {
+                wt = weight[j][i];
+            }
+            f = image[j][i];
+            w   += 1/wt;
+            fo  += f/wt;
+            if (SKY_FIT_ORDER == 0) continue;
+
+            xc  = i + col0;
+            yc  = j + row0;
+            x  +=    xc/wt;
+            y  +=    yc/wt;
+            x2 += xc*xc/wt;
+            xy += xc*yc/wt;
+            y2 += yc*yc/wt;
+            fx +=  f*xc/wt;
+            fy +=  f*yc/wt;
+        }
+    }
+    psLogMsg ("psphot.ensemble", PS_LOG_INFO, "step 3: %f sec\n", psTimerMark ("psphot"));
+
+    // turn off MARK for all image pixels
+    psImageMaskRegion (readout->mask, fullArray, "AND", PS_NOT_U8(markVal));
+    psLogMsg ("psphot.ensemble", PS_LOG_INFO, "step 4: %f sec\n", psTimerMark ("psphot"));
+
+    // set the Border T elements
+    psSparseBorderElementG (border, 0, fo);
+    psSparseBorderElementT (border, 0, 0, w);
+    if (SKY_FIT_ORDER > 0) {
+        psSparseBorderElementG (border, 0, fx);
+        psSparseBorderElementG (border, 0, fy);
+        psSparseBorderElementT (border, 1, 0, x);
+        psSparseBorderElementT (border, 2, 0, y);
+        psSparseBorderElementT (border, 0, 1, x);
+        psSparseBorderElementT (border, 1, 1, x2);
+        psSparseBorderElementT (border, 2, 1, xy);
+        psSparseBorderElementT (border, 0, 2, y);
+        psSparseBorderElementT (border, 1, 2, xy);
+        psSparseBorderElementT (border, 2, 2, y2);
+    }
+    psLogMsg ("psphot.ensemble", PS_LOG_INFO, "step 5: %f sec\n", psTimerMark ("psphot"));
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotGuessModels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotGuessModels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotGuessModels.c	(revision 22322)
@@ -0,0 +1,106 @@
+# include "psphotInternal.h"
+
+// A guess for when the moments aren't available
+static pmModel *wildGuess(pmSource *source, // Source for which to guess
+                          pmPSF *psf    // The point-spread function
+    )
+{
+    pmModel *model = pmModelAlloc(psf->type);
+    psF32 *PAR = model->params->data.F32;
+    PAR[PM_PAR_SKY]  = 0;
+    // XXX get this from the image pixels
+    PAR[PM_PAR_I0]   = source->peak->flux;
+    PAR[PM_PAR_XPOS] = source->peak->xf;
+    PAR[PM_PAR_YPOS] = source->peak->yf;
+    return model;
+}
+
+// construct an initial PSF model for each object
+bool psphotGuessModels (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf) {
+
+    bool status;
+
+    psTimerStart ("psphot");
+
+    // bit-masks to test for good/bad pixels
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psMaskType markVal = psMetadataLookupU8(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // setup the PSF fit radius details
+    psphotInitRadiusPSF (recipe, psf->type);
+
+    for (int i = 0; i < sources->n; i++) {
+	pmSource *source = sources->data[i];
+
+	// skip non-astronomical objects (very likely defects)
+	if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
+	if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
+
+	// XXX if a source is faint, it will not have moments measured.
+	// it must be modelled as a PSF.  In this case, we need to use
+	// the peak centroid to get the coordinates and get the peak flux
+	// from the image?
+	pmModel *modelEXT;
+	if (!source->moments) {
+	    modelEXT = wildGuess(source, psf);
+	} else {
+	    // use the source moments, etc to guess basic model parameters
+	    modelEXT = pmSourceModelGuess (source, psf->type);
+	    if (!modelEXT) {
+		modelEXT = wildGuess(source, psf);
+	    }
+	    // these valuse are set in pmSourceModelGuess, should this rule be in there as well?
+	    if (source->mode &  PM_SOURCE_MODE_SATSTAR) {
+		modelEXT->params->data.F32[PM_PAR_XPOS] = source->moments->x;
+		modelEXT->params->data.F32[PM_PAR_YPOS] = source->moments->y;
+	    } else {
+		modelEXT->params->data.F32[PM_PAR_XPOS] = source->peak->xf;
+		modelEXT->params->data.F32[PM_PAR_YPOS] = source->peak->yf;
+	    }
+	}
+
+	// set PSF parameters for this model (apply 2D shape model)
+	pmModel *modelPSF = pmModelFromPSF (modelEXT, psf);
+	if (modelPSF == NULL) {
+	    psError(PSPHOT_ERR_PSF, false,
+		    "Failed to determine PSF model at r,c = (%d,%d); trying centre of image",
+		    source->peak->y, source->peak->x);
+	    //
+	    // Try the centre of the image
+	    //
+	    modelEXT->params->data.F32[PM_PAR_XPOS] = 0.5*readout->image->numCols;
+	    modelEXT->params->data.F32[PM_PAR_YPOS] = 0.5*readout->image->numRows;
+	    modelPSF = pmModelFromPSF (modelEXT, psf);
+	    if (modelPSF == NULL) {
+		psError(PSPHOT_ERR_PSF, false,
+			"Failed to determine PSF model at centre of image");
+		psFree(modelEXT);
+		return false;
+	    }
+
+	    source->mode |= PM_SOURCE_MODE_BADPSF;
+	}
+	psFree (modelEXT);
+
+	// XXX need to define the guess flux?
+	// set the fit radius based on the object flux limit and the model
+	psphotCheckRadiusPSF (readout, source, modelPSF, markVal);
+
+	// set the source PSF model
+	source->modelPSF = modelPSF;
+	source->modelPSF->residuals = psf->residuals;
+
+	pmSourceCacheModel (source, maskVal);
+    }
+    psLogMsg ("psphot.models", 4, "built models for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot"));
+    return true;
+}
+
+// XXX do we always know which model is supposed to be used?
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotImageLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotImageLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotImageLoop.c	(revision 22322)
@@ -0,0 +1,101 @@
+# include "psphotStandAlone.h"
+
+# define ESCAPE(MESSAGE) { \
+  psError(PSPHOT_ERR_DATA, false, MESSAGE); \
+  psFree (view); \
+  return false; \
+}
+
+bool psphotImageLoop (pmConfig *config) {
+
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    pmFPAfile *load = psMetadataLookupPtr (&status, config->files, "PSPHOT.LOAD");
+    if (!status) {
+	psError(PSPHOT_ERR_PROG, false, "Can't find input data!");
+	return false;
+    }
+    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PSPHOT.INPUT");
+    if (!status) {
+	psError(PSPHOT_ERR_PROG, false, "Can't find input data!");
+	return false;
+    }
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+    
+    // files associated with the science image
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for fpa in psphot.");
+
+    // for psphot, we force data to be read at the chip level 
+    while ((chip = pmFPAviewNextChip (view, load->fpa, 1)) != NULL) {
+        psLogMsg ("psphot", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (! chip->process || ! chip->file_exists) { continue; }
+
+	// load just the input image data (image, mask, weight)
+	pmFPAfileActivate (config->files, false, NULL);
+	pmFPAfileActivate (config->files, true, "PSPHOT.LOAD");
+	pmFPAfileActivate (config->files, true, "PSPHOT.MASK");
+	pmFPAfileActivate (config->files, true, "PSPHOT.WEIGHT");
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for Chip in psphot.");
+
+	// mosaic the cells of a chip into a single contiguous (trimmed) chip
+        if (!psphotMosaicChip(config, view, "PSPHOT.INPUT", "PSPHOT.LOAD")) ESCAPE ("Unable to mosaic chip.");
+
+	// try to load other supporting data (PSF, SRC, etc).
+	// do not re-load the following three files
+	pmFPAfileActivate (config->files, true, NULL);
+	pmFPAfileActivate (config->files, false, "PSPHOT.LOAD");
+	pmFPAfileActivate (config->files, false, "PSPHOT.MASK");
+	pmFPAfileActivate (config->files, false, "PSPHOT.WEIGHT");
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for Chip in psphot.");
+
+	// re-activate files so they will be closed and freed below
+	pmFPAfileActivate (config->files, true, NULL);
+
+	// there is now only a single chip (multiple readouts?). loop over it and process
+	while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
+            psLogMsg ("psphot", 5, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+
+	    // process each of the readouts
+	    while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
+		psLogMsg ("psphot", 6, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+		if (! readout->data_exists) { continue; }
+
+		// run the actual photometry analysis on this chip/cell/readout
+		if (!psphotReadout (config, view)) {
+		    psError(psErrorCodeLast(), false, "failure in psphotReadout for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
+		    psFree (view);
+		    return false;
+		}
+	    }
+	}
+
+	// save output which is saved at the chip level
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE ("failed output for Chip in psphot.");
+    }
+    // save output which is saved at the fpa level
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE ("failed ouput for FPA in psphot.");
+
+    // fail if we failed to handle an error
+    if (psErrorCodeLast() != PS_ERR_NONE) psAbort ("failed to handle an error!");
+
+    psFree (view);
+    return true;
+}
+
+// I/O files related to psphot:
+// PSPHOT.INPUT   : input image file(s)
+// PSPHOT.RESID   : residual image
+// PSPHOT.OUTPUT  : output object tables (object)
+
+// PSPHOT.BACKSUB : background subtracted image
+// PSPHOT.BACKGND : background model (full-scale image?)
+// PSPHOT.BACKMDL : background model (binned image?)
+// PSPHOT.PSF     : sample PSF images
+
+// PSPHOT.MASK
+// PSPHOT.WEIGHT
+// 
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotImageMedian.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotImageMedian.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotImageMedian.c	(revision 22322)
@@ -0,0 +1,392 @@
+# include "psphotInternal.h"
+static int npass = 0;
+
+// generate the median in NxN boxes, clipping heavily
+// linear interpolation to generate full-scale model
+bool psphotImageMedian (pmConfig *config, const pmFPAview *view, psMaskType maskVal)
+{
+    bool status = true;
+    static char *defaultStatsName = "FITTED_MEAN";
+
+    pmReadout *readout = NULL;
+    pmReadout *background = NULL;
+    pmReadout *backSub = NULL;
+
+    psTimerStart ("psphot");
+
+    // select the appropriate recipe information
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+
+    // user supplied seed, if available
+    unsigned long seed = psMetadataLookupS32 (&status, recipe, "IMSTATS_SEED");
+    if (!status) {
+        seed = 0;
+    }
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, seed);
+
+    // subtract this amount extra from the sky
+    float SKY_BIAS = psMetadataLookupF32 (&status, recipe, "SKY_BIAS");
+    if (!status) {
+        SKY_BIAS = 0;
+    }
+
+    // supply the sky background statistics options
+    char *statsName = psMetadataLookupStr (&status, recipe, "SKY_STAT");
+    if (statsName == NULL) {
+        statsName = defaultStatsName;
+    }
+    psStatsOptions statsOptionLocation = psStatsOptionFromString(statsName);
+    if (!(statsOptionLocation & (PS_STAT_SAMPLE_MEAN |
+                                 PS_STAT_SAMPLE_MEDIAN |
+                                 PS_STAT_ROBUST_MEDIAN |
+                                 PS_STAT_ROBUST_QUARTILE |
+                                 PS_STAT_CLIPPED_MEAN |
+                                 PS_STAT_FITTED_MEAN |
+                                 PS_STAT_FITTED_MEAN_V2 |
+                                 PS_STAT_FITTED_MEAN_V3 |
+                                 PS_STAT_FITTED_MEAN_V4))) {
+        statsOptionLocation = PS_STAT_FITTED_MEAN;
+    }
+
+    psStatsOptions statsOptionWidth = PS_STAT_NONE;
+    if (statsOptionLocation & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN)) {
+        statsOptionWidth = PS_STAT_SAMPLE_STDEV;
+    } else if (statsOptionLocation & (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_QUARTILE)) {
+#if 1
+        statsOptionWidth = PS_STAT_ROBUST_STDEV; // not set; => NaN
+#else
+        statsOptionWidth = PS_STAT_FITTED_STDEV;
+#endif
+    } else if (statsOptionLocation & PS_STAT_FITTED_MEAN) {
+        statsOptionWidth = PS_STAT_FITTED_STDEV;
+    } else if (statsOptionLocation & PS_STAT_CLIPPED_MEAN) {
+        statsOptionWidth = PS_STAT_CLIPPED_STDEV;
+    } else if (statsOptionLocation & PS_STAT_FITTED_MEAN_V2) {
+        statsOptionWidth = PS_STAT_FITTED_STDEV_V2;
+    } else if (statsOptionLocation & PS_STAT_FITTED_MEAN_V3) {
+        statsOptionWidth = PS_STAT_FITTED_STDEV_V3;
+    } else if (statsOptionLocation & PS_STAT_FITTED_MEAN_V4) {
+        statsOptionWidth = PS_STAT_FITTED_STDEV_V4;
+    } else {
+        psAbort("Unable to estimate variance of selected statsOptionLocations 0x%x", statsOptionLocation);
+    }
+
+    const psStatsOptions statsOption = statsOptionLocation | statsOptionWidth;
+    psStats *stats = psStatsAlloc (statsOption);
+
+    // set range for old-version of sky statistic
+    if (statsOptionLocation & PS_STAT_ROBUST_QUARTILE) {
+        stats->min = 0.25;
+        stats->max = 0.75;
+    }
+
+    // set user-option for number of pixels per region
+    stats->nSubsample = psMetadataLookupF32 (&status, recipe, "IMSTATS_NPIX");
+    if (!status) {
+        stats->nSubsample = 1000;
+    }
+
+    // optionally set the binsize
+    stats->binsize = psMetadataLookupF32 (&status, recipe, "SKY_HISTOGRAM_BINS");
+    if (status) {
+        stats->options |= PS_STAT_USE_BINSIZE;
+    }
+
+    // optionally set the sigma clipping
+    stats->clipSigma = psMetadataLookupF32 (&status, recipe, "SKY_CLIP_SIGMA");
+    if (!status) {
+        if ((stats->options & PS_STAT_FITTED_MEAN) || (stats->options & PS_STAT_FITTED_MEAN_V2)) {
+            stats->clipSigma = 1.0;
+        } else {
+            stats->clipSigma = 3.0;
+        }
+    }
+
+    // stats is not initialized by psStats???  use this to save the input options
+    // XXX re-work this to use the new psStatsInit function
+    psStats *statsDefaults = psStatsAlloc (statsOption);
+    *statsDefaults = *stats;
+
+    // find the currently selected readout
+    pmFPAfile *file = psMetadataLookupPtr (&status, config->files, "PSPHOT.INPUT");
+    pmFPA *inFPA = file->fpa;
+    readout = pmFPAviewThisReadout (view, inFPA);
+
+    psImage *image = readout->image;
+    psImage *mask  = readout->mask;
+
+    // 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, "BACKGROUND.XBIN");
+    binning->nYbin  = psMetadataLookupS32 (&status, recipe, "BACKGROUND.YBIN");
+
+    psImageBinningSetRuffSize(binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkip(binning, image);
+    status = psMetadataAddPtr(recipe, PS_LIST_TAIL, "PSPHOT.BACKGROUND.BINNING", PS_DATA_UNKNOWN | PS_META_REPLACE, "Background binning", binning);
+    PS_ASSERT (status, false);
+
+    // we save the binning structure for use in psphotMagnitudes
+    pmReadout *model      = pmFPAGenerateReadout (config, view, "PSPHOT.BACKMDL",       inFPA, binning);
+    pmReadout *modelStdev = pmFPAGenerateReadout (config, view, "PSPHOT.BACKMDL.STDEV", inFPA, binning);
+
+    psF32 **modelData = model->image->data.F32;
+    psF32 **modelStdevData = modelStdev->image->data.F32;
+
+    // measure clipped median for subimages
+    psRegion ruffRegion = {0,0,0,0};
+    psRegion fineRegion = {0,0,0,0};
+    for (int iy = 0; iy < model->image->numRows; iy++) {
+        for (int ix = 0; ix < model->image->numCols; ix++) {
+
+            // convert the ruff grid cell to the equivalent fine grid cell
+            // XXX we need to watch out for row0,col0
+            ruffRegion = psRegionSet (ix, ix + 2, iy, iy + 2);
+            fineRegion = psImageBinningSetFineRegion (binning, ruffRegion);
+            fineRegion = psRegionForImage (image, fineRegion);
+
+            psImage *subset  = psImageSubset (image, fineRegion);
+            if (!subset->numCols || !subset->numRows) {
+                psFree (subset);
+                continue;
+            }
+            psImage *submask = psImageSubset (mask, fineRegion);
+
+            // reset the default values
+            *stats = *statsDefaults;
+
+            // Use the selected background statistic for the first pass
+            // If it fails, fall back on the "ROBUST_MEDIAN" version
+            // If both fail, set the pixel to NAN and (below) interpolate
+            // XXX psImageBackground will probably be renamed psImageStats
+            // XXX don't bother trying if there are no valid pixels...
+
+	    psVector *sample = NULL;
+
+	    // turn on stats tracing in desired cells
+	    # if (0)
+	    psMetadata *plots = psMetadataLookupPtr (&status, recipe, "DIAGNOSTIC.PLOTS");
+	    assert (plots);
+	    
+	    int xPlot = psMetadataLookupS32 (&status, plots, "IMAGE.BACKGROUND.CELL.HISTOGRAM.X");
+	    assert (status);
+	    int yPlot = psMetadataLookupS32 (&status, plots, "IMAGE.BACKGROUND.CELL.HISTOGRAM.Y");
+	    assert (status);
+
+	    bool gotX = (xPlot < 0) || (xPlot == ix);
+	    bool gotY = (yPlot < 0) || (yPlot == iy);
+
+	    if (gotX && gotY) {
+		psTraceSetLevel ("psLib.math.vectorFittedStats_v4", 6);
+		psTraceSetLevel ("psLib.math.vectorRobustStats", 6);
+	    } else {
+		psTraceSetLevel ("psLib.math.vectorFittedStats_v4", 0);
+		psTraceSetLevel ("psLib.math.vectorRobustStats", 0);
+	    }	    
+	    # endif
+
+            if (psImageBackground(stats, &sample, subset, submask, maskVal, rng)) {
+                if (stats->options & PS_STAT_ROBUST_QUARTILE) {
+                    modelData[iy][ix] = stats->robustMedian;
+                } else {
+                    modelData[iy][ix] = psStatsGetValue(stats, statsOptionLocation);
+                }
+                modelStdevData[iy][ix] = psStatsGetValue(stats, statsOptionWidth);
+
+		// supply sample to plotting routing
+		psphotDiagnosticPlots (config, "IMAGE.BACKGROUND.CELL.HISTOGRAM", ix, iy, modelData[iy][ix], modelStdevData[iy][ix], sample);
+		psFree (sample);
+            } else {
+                psStatsOptions currentOptions = stats->options;
+                stats->options = PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV;
+                if (!psImageBackground(stats, &sample, subset, submask, maskVal, rng)) {
+                    psLogMsg ("psphot", PS_LOG_WARN, "Failed to estimate background using ROBUST_MEDIAN for "
+                               "(%dx%d, (row0,col0) = (%d,%d)",
+                               subset->numRows, subset->numCols, subset->row0, subset->col0);
+                    modelData[iy][ix] = modelStdevData[iy][ix] = NAN;
+                } else {
+                    modelData[iy][ix] = psStatsGetValue (stats, PS_STAT_ROBUST_MEDIAN);
+                    modelStdevData[iy][ix] = psStatsGetValue(stats, PS_STAT_ROBUST_STDEV);
+
+		    // supply sample to plotting routing
+		    psphotDiagnosticPlots (config, "IMAGE.BACKGROUND.CELL.HISTOGRAM", ix, iy, modelData[iy][ix], modelStdevData[iy][ix], sample);
+		    psFree (sample);
+                }
+                // drop errors caused by psImageBackground failures
+                // XXX we probably should trap and exit on serious failures
+                psErrorClear();
+                stats->options = currentOptions;
+            }
+            modelData[iy][ix] += SKY_BIAS;
+            psFree (subset);
+            psFree (submask);
+        }
+    }
+
+    if (psTraceGetLevel("psphot") > 5) {
+        char name[256];
+        sprintf (name, "backraw.%02d.fits", npass);
+        psphotSaveImage (NULL, model->image, name);
+    }
+
+    // patch over bad regions (use average of 8 possible neighbor pixels)
+    // XXX consider testing all pixels against the 8 neighbors and replacing outliers...
+    double Count = 0;                   // number of good pixels
+    double Value = 0;                   // sum of good pixel's value
+    double ValueStdev = 0;              // sum of good pixel's standard deviations
+    for (int iy = 0; iy < model->image->numRows; iy++) {
+        for (int ix = 0; ix < model->image->numCols; ix++) {
+            if (!isnan(modelData[iy][ix])) {
+                Value += modelData[iy][ix];
+                ValueStdev += modelStdevData[iy][ix];
+                Count++;
+                continue;
+            }
+
+            double value = 0;
+            double count = 0;
+            for (int jy = iy - 1; jy <= iy + 1; jy++) {
+                if (jy <   0) continue;
+                if (jy >= model->image->numRows) continue;
+                for (int jx = ix - 1; jx <= ix + 1; jx++) {
+                    if (!jx && !jy) continue;
+                    if (jx   <   0) continue;
+                    if (jx   >= model->image->numCols) continue;
+                    value += modelData[jy][jx];
+                    count += 1.0;
+                }
+            }
+            if (count > 0) modelData[iy][ix] = value / count;
+        }
+    }
+    if (Count == 0) {
+        psError (PSPHOT_ERR_DATA, true, "failed to build background image");
+	psFree(stats);
+	psFree(statsDefaults);
+	psFree(binning);
+	psFree(rng);
+        return false;
+    }
+
+    Value /= Count;
+    ValueStdev /= Count;
+
+    // patch over remaining bad regions (use global average)
+    for (int iy = 0; iy < model->image->numRows; iy++) {
+        for (int ix = 0; ix < model->image->numCols; ix++) {
+            if (!isnan(modelData[iy][ix])) continue;
+            modelData[iy][ix] = Value;
+            modelStdevData[iy][ix] = ValueStdev;
+        }
+    }
+
+    psLogMsg ("psphot", PS_LOG_MINUTIA, "build median image: %f sec\n", psTimerMark ("psphot"));
+
+    psMetadataAddF32(recipe, PS_LIST_TAIL, "SKY_MEAN", PS_META_REPLACE, "sky mean", Value);
+    psMetadataAddF32(recipe, PS_LIST_TAIL, "SKY_STDEV", PS_META_REPLACE, "sky stdev", ValueStdev);
+    psLogMsg ("psphot", PS_LOG_INFO, "image sky : mean %f stdev %f", Value, ValueStdev);
+
+    // measure image and background stats and save for later output
+    psStats *statsBck = psStatsAlloc (PS_STAT_SAMPLE_MEAN |
+                                      PS_STAT_SAMPLE_STDEV |
+                                      PS_STAT_MIN |
+                                      PS_STAT_MAX);
+    psImageStats (statsBck, model->image, NULL, 0);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "MSKY_MN",
+                      PS_META_REPLACE, "sky model mean",          statsBck->sampleMean);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "MSKY_SIG",
+                      PS_META_REPLACE, "sky model stdev",         statsBck->sampleStdev);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "MSKY_MAX",
+                      PS_META_REPLACE, "sky model maximum value", statsBck->max);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "MSKY_MIN",
+                      PS_META_REPLACE, "sky model minimum value", statsBck->min);
+    psMetadataAddS32 (recipe, PS_LIST_TAIL, "MSKY_NX",
+                      PS_META_REPLACE, "sky model size (x)",      model->image->numCols);
+    psMetadataAddS32 (recipe, PS_LIST_TAIL, "MSKY_NY",
+                      PS_META_REPLACE, "sky model size (y)",      model->image->numRows);
+    psLogMsg ("psphot", PS_LOG_INFO, "background sky : min %f mean %f max %f stdev %f",
+              statsBck->min, statsBck->sampleMean, statsBck->max, statsBck->sampleStdev);
+    psFree (statsBck);
+
+    // select background pixels, from output background file, or create
+    file = psMetadataLookupPtr (&status, config->files, "PSPHOT.BACKGND");
+    if (file) {
+        // we are using PSPHOT.BACKGND as an I/O file: select readout or create
+        if (file->mode == PM_FPA_MODE_INTERNAL) {
+            background = file->readout;
+        } else {
+            background = pmFPAviewThisReadout (view, file->fpa);
+        }
+        if (background == NULL) {
+            // readout does not yet exist: create from input
+            pmFPAfileCopyStructureView (file->fpa, inFPA, 1, 1, view);
+            background = pmFPAviewThisReadout (view, file->fpa);
+            if ((image->numCols != background->image->numCols) || (image->numRows != background->image->numRows)) {
+                psError (PSPHOT_ERR_PROG, true, "inconsistent sizes for background dimensions");
+                return false;
+            }
+        }
+    } else {
+        background = pmFPAfileDefineInternal (config->files, "PSPHOT.BACKGND", image->numCols, image->numRows, PS_TYPE_F32);
+    }
+    psF32 **backData = background->image->data.F32;
+
+    // linear interpolation to full-scale
+    if (!psImageUnbin (background->image, model->image, binning)) {
+        psError (PSPHOT_ERR_PROG, true, "inconsistent sizes for unbinning");
+        return false;
+    }
+
+    psLogMsg ("psphot", PS_LOG_MINUTIA, "build resampled image: %f sec\n", psTimerMark ("psphot"));
+
+    // back-sub image pixels, from output background file (don't create if not requested)
+    file = psMetadataLookupPtr (&status, config->files, "PSPHOT.BACKSUB");
+    if (file) {
+        // we are using PSPHOT.BACKSUB as an I/O file: select readout or create
+        backSub = pmFPAviewThisReadout (view, file->fpa);
+        if (backSub == NULL) {
+            // readout does not yet exist: create from input
+            pmFPAfileCopyStructureView (file->fpa, inFPA, 1, 1, view);
+            backSub = pmFPAviewThisReadout (view, file->fpa);
+        }
+    }
+
+    if (psTraceGetLevel("psphot") > 5) {
+        char name[256];
+        sprintf (name, "image.%02d.fits", npass);
+        psphotSaveImage (NULL, image, name);
+        sprintf (name, "back.%02d.fits", npass);
+        psphotSaveImage (NULL, background->image, name);
+        sprintf (name, "mask.%02d.fits", npass);
+        psphotSaveImage (NULL, mask, name);
+        sprintf (name, "backmdl.%02d.fits", npass);
+        psphotSaveImage (NULL, model->image, name);
+    }
+
+    // subtract the background model (save in backSub, if requested)
+    for (int j = 0; j < image->numRows; j++) {
+        for (int i = 0; i < image->numCols; i++) {
+            image->data.F32[j][i] -= backData[j][i];
+            if (backSub) {
+                backSub->image->data.F32[j][i] = image->data.F32[j][i];
+            }
+        }
+    }
+
+    if (psTraceGetLevel("psphot") > 5) {
+        char name[256];
+        sprintf (name, "backsub.%02d.fits", npass);
+        psphotSaveImage (NULL, image, name);
+    }
+    npass ++;
+
+    psLogMsg ("psphot", PS_LOG_INFO, "subtracted background model: %f sec\n", psTimerMark ("psphot"));
+    psFree(stats);
+    psFree(statsDefaults);
+    psFree(binning);
+    psFree(rng);
+
+    // the pmReadout selected in this function are all view on entries in config->files
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotInternal.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotInternal.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotInternal.h	(revision 22322)
@@ -0,0 +1,17 @@
+/* this file is included by the internal, library functions. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef PSPHOT_INTERNAL_H
+#define PSPHOT_INTERNAL_H
+
+#include <stdio.h>
+#include <strings.h>  // for strcasecmp
+#include <unistd.h>   // for unlink
+#include <pslib.h>
+#include <psmodules.h>
+#include "psphot.h"
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotIsophotal.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotIsophotal.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotIsophotal.c	(revision 22322)
@@ -0,0 +1,78 @@
+# include "psphotInternal.h"
+
+bool psphotIsophotal (pmSource *source, psMetadata *recipe, psMaskType maskVal) {
+
+  assert (source->extpars);
+  assert (source->extpars->profile);
+  assert (source->extpars->profile->radius);
+  assert (source->extpars->profile->flux);
+
+  bool status;
+
+  psVector *radius = source->extpars->profile->radius;
+  psVector *flux = source->extpars->profile->flux;
+
+  // flux at which to measure isophotal parameters
+  // XXX ISOPHOTAL_FLUX should be specified in mags, need the zero point to get counts/sec
+  float ISOPHOT_FLUX = psMetadataLookupF32 (&status, recipe, "ISOPHOTAL_FLUX");
+  assert (status);
+
+  // find the first bin below the flux level and the last above the level
+  // XXX can this be done faster with bisection?
+  // XXX do I need to worry about crazy outliers?
+  // XXX should i be smoothing or fitting the curve?
+  int firstBelow = -1;
+  int lastAbove = -1;
+  for (int i = 0; i < flux->n; i++) {
+    if (flux->data.F32[i] > ISOPHOT_FLUX) lastAbove = i;
+    if ((firstBelow < 0) && (flux->data.F32[i] < ISOPHOT_FLUX)) firstBelow = i;
+  }
+  // if we don't go out far enough, we have a problem...
+  if (lastAbove == flux->n - 1) {
+    psTrace ("psphot", 5, "did not go out far enough to reach isophotal magnitude");
+    // XXX raise a flag ?
+    return false;
+  }
+  if (firstBelow < 0) {
+    psTrace ("psphot", 5, "did not go out far enough to bound isophotal magnitude: error unmeasured");
+    // XXX raise a flag ?
+    lastAbove = firstBelow;
+    return false;
+  }
+
+  // need to examine pixels in this vicinity
+  float isophotalFluxFirst = 0;
+  float isophotalFluxLast = 0;
+  for (int i = 0; i <= PS_MAX(firstBelow, lastAbove); i++) {
+    if (i <= firstBelow) {
+      isophotalFluxFirst += flux->data.F32[i];
+    }
+    if (i <= lastAbove) {
+      isophotalFluxLast += flux->data.F32[i];
+    }
+  }
+  float isophotalFlux    = 0.5*(isophotalFluxLast + isophotalFluxFirst);
+  float isophotalFluxErr = 0.5*fabs(isophotalFluxLast - isophotalFluxFirst);
+
+  float isophotalRad     = 0.5*(radius->data.F32[firstBelow] + radius->data.F32[lastAbove]);
+  float isophotalRadErr  = 0.5*fabs(radius->data.F32[firstBelow] - radius->data.F32[lastAbove]);
+
+  if (!source->extpars->isophot) {
+    source->extpars->isophot = pmSourceIsophotalValuesAlloc ();
+  }
+
+  // these are uncalibrated: instrumental mags and pixel units
+  source->extpars->isophot->mag    = -2.5*log10(isophotalFlux);
+  source->extpars->isophot->magErr = isophotalFluxErr / isophotalFlux;
+
+  source->extpars->isophot->rad    = isophotalRad;
+  source->extpars->isophot->radErr = isophotalRadErr;
+
+  psTrace ("psphot", 5, "Isophot flux:%f +/- %f @ %f +/- %f for %f, %f\n",
+           source->extpars->isophot->mag, source->extpars->isophot->magErr,
+           source->extpars->isophot->rad, source->extpars->isophot->radErr,
+           source->peak->xf, source->peak->yf);
+
+  return true;
+
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotKernelFromPSF.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotKernelFromPSF.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotKernelFromPSF.c	(revision 22322)
@@ -0,0 +1,45 @@
+# include "psphotInternal.h"
+
+psKernel *psphotKernelFromPSF (pmSource *source, int nPix) {
+
+    assert (source);
+    assert (source->psfFlux); // XXX build if needed?
+
+    int x0 = source->peak->xf - source->psfFlux->col0;
+    int y0 = source->peak->yf - source->psfFlux->row0;
+
+    // need to decide on the size: dynamically? statically?
+    psKernel *psf = psKernelAlloc (-nPix, +nPix, -nPix, +nPix);
+
+    // XXX we should just re-construct a PSF at this location 
+    // psModelAdd (psf->image, NULL, source->modelPSF, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM | PM_MODEL_OP_CENTER);
+  
+    // if the realized PSF for this object does not cover the full kernel, give up for now
+    if (x0 + psf->xMin < 0) goto escape;
+    if (x0 + psf->xMax >= source->psfFlux->numCols) goto escape;
+    if (y0 + psf->yMin < 0) goto escape;
+    if (y0 + psf->yMax >= source->psfFlux->numRows) goto escape;
+
+    double sum = 0.0;
+    for (int j = psf->yMin; j <= psf->yMax; j++) {
+	for (int i = psf->xMin; i <= psf->xMax; i++) {
+	    double value = source->psfFlux->data.F32[y0 + j][x0 + i];
+	    psf->kernel[j][i] = value;
+	    sum += value;
+	}
+    }
+    assert (sum > 0.0);
+
+    // psf must be normalized (integral = 1.0)
+    for (int i = 0; i < psf->image->numRows; i++) {
+	for (int j = 0; j < psf->image->numCols; j++) {
+	    psf->image->data.F32[i][j] /= sum;
+	}
+    }
+
+    return psf;
+
+escape:
+    psFree (psf);
+    return NULL;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotKron.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotKron.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotKron.c	(revision 22322)
@@ -0,0 +1,8 @@
+# include "psphotInternal.h"
+
+bool psphotKron (pmSource *source, psMetadata *recipe, psMaskType maskVal) {
+
+  psLogMsg ("psphot", PS_LOG_INFO, "not implemented\n");
+  return true;
+
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotLoadPSF.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotLoadPSF.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotLoadPSF.c	(revision 22322)
@@ -0,0 +1,28 @@
+# include "psphotInternal.h"
+
+// load an externally supplied psf model
+pmPSF *psphotLoadPSF (pmConfig *config, const pmFPAview *view, psMetadata *recipe) {
+
+    // find the currently selected chip
+    pmChip *chip = pmFPAfileThisChip (config->files, view, "PSPHOT.PSF.LOAD");
+    if (!chip) return NULL;
+
+    // find the currently selected readout
+    pmReadout *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.PSF.LOAD");
+    if (!readout) return NULL;
+
+    // check if a PSF model is supplied by the user
+    pmPSF *psf = psMetadataLookupPtr (NULL, chip->analysis, "PSPHOT.PSF");
+    if (psf == NULL) {
+        psLogMsg ("psphot", 3, "no psf supplied for this chip");
+        return NULL;
+    }
+
+    if (!psphotPSFstats (readout, recipe, psf)) psAbort("cannot measure PSF shape terms");
+
+    psLogMsg ("psphot", 3, "using externally supplied PSF model for this readout");
+
+    // we return a psf which can be free (and which may be removed from the metadata)
+    psMemIncrRefCounter (psf);
+    return psf;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotMagnitudes.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotMagnitudes.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotMagnitudes.c	(revision 22322)
@@ -0,0 +1,56 @@
+# include "psphotInternal.h"
+
+bool psphotMagnitudes(psArray *sources, psMetadata *recipe, pmPSF *psf, pmReadout *background) {
+
+    bool status = false;
+    int Nap = 0;
+
+    psTimerStart ("psphot");
+
+    // bit-masks to test for good/bad pixels
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psMaskType markVal = psMetadataLookupU8(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    pmSourceMagnitudesInit (recipe);
+
+    // XXX require that we have a background model, or
+    // allow it to be missing, setting local sky to 0.0?
+    PS_ASSERT (background, false);
+
+    // the binning details are saved on the analysis metadata
+    psImageBinning *binning = psMetadataLookupPtr(&status, recipe, "PSPHOT.BACKGROUND.BINNING");
+    PS_ASSERT (status, false);
+
+    bool IGNORE_GROWTH = psMetadataLookupBool (&status, recipe, "IGNORE_GROWTH");
+    bool INTERPOLATE_AP = psMetadataLookupBool (&status, recipe, "INTERPOLATE_AP");
+
+    pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_APCORR | PM_SOURCE_PHOT_WEIGHT;
+    if (!IGNORE_GROWTH) photMode |= PM_SOURCE_PHOT_GROWTH;
+    if (INTERPOLATE_AP) photMode |= PM_SOURCE_PHOT_INTERP;
+
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+        status = pmSourceMagnitudes (source, psf, photMode, maskVal);
+        if (status) Nap ++;
+
+        source->sky = psImageUnbinPixel(source->peak->x, source->peak->y, background->image, binning);
+        if (isnan(source->sky) && false) {
+          psError(PSPHOT_ERR_SKY, false, "Setting pmSource.sky");
+          psErrorStackPrint(NULL, " ");
+          psErrorClear();
+        }
+    }
+
+    psLogMsg ("psphot.magnitudes", PS_LOG_DETAIL, "measure magnitudes : %f sec for %ld objects (%d with apertures)\n", psTimerMark ("psphot"), sources->n, Nap);
+    return true;
+}
+
+// XXX add in a measurement of the bright and faint completeness values
+// XXX push these into the recipe
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotMakeFluxScale.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotMakeFluxScale.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotMakeFluxScale.c	(revision 22322)
@@ -0,0 +1,74 @@
+# include "psphotInternal.h"
+
+bool psphotMakeFluxScale (psImage *image, psMetadata *recipe, pmPSF *psf) {
+
+    bool status = false;
+
+    psTimerStart ("residuals");
+
+    int Nx = psMetadataLookupS32(&status, recipe, "PSF.FLUXSCALE.NX");
+    int Ny = psMetadataLookupS32(&status, recipe, "PSF.FLUXSCALE.NY");
+
+    // stats structure for use by ApTrend : XXX make parameters user setable
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+
+    pmTrend2D *trend = pmTrend2DAlloc (PM_TREND_MAP, image, Nx, Ny, stats);
+
+    psVector *xPts = psVectorAlloc (Nx*Ny, PS_TYPE_F32);
+    psVector *yPts = psVectorAlloc (Nx*Ny, PS_TYPE_F32);
+    psVector *fPts = psVectorAlloc (Nx*Ny, PS_TYPE_F32);
+
+    int Npts = 0;
+    bool success = true;                // Function succeeded?
+
+    // generate a set of test normalized PSF fluxes filling the grid
+    for (int ix = 0; ix < Nx; ix++) {
+        for (int iy = 0; iy < Ny; iy++) {
+
+            float x = psImageBinningGetFineX (trend->map->binning, ix + 0.5);
+            float y = psImageBinningGetFineY (trend->map->binning, iy + 0.5);
+
+            // create normalized model object at xc,yc
+            pmModel *model = pmModelFromPSFforXY (psf, x, y, 1.0);
+            if (!model) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to generate model for grid point %d,%d", ix, iy);
+                success = false;
+                goto DONE;
+            }
+
+            // measure the fitMag for this model
+            float fitSum = model->modelFlux (model->params);
+            assert (fitSum > 0);
+            assert (isfinite(fitSum));
+
+            xPts->data.F32[Npts] = x;
+            yPts->data.F32[Npts] = y;
+            fPts->data.F32[Npts] = fitSum;
+            Npts ++;
+            assert (Npts <= xPts->nalloc);
+            psFree (model);
+        }
+    }
+    xPts->n = Npts;
+    yPts->n = Npts;
+    fPts->n = Npts;
+
+    if (!pmTrend2DFit (trend, NULL, 0xff, xPts, yPts, fPts, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to fit trend");
+        success = false;
+        goto DONE;
+    }
+
+    // XXX do something useful to measure residual statistics
+
+    psf->FluxScale = psMemIncrRefCounter(trend);
+
+ DONE:
+    psFree(xPts);
+    psFree(yPts);
+    psFree(fPts);
+    psFree(stats);
+    psFree(trend);
+
+    return success;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotMakeResiduals.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotMakeResiduals.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotMakeResiduals.c	(revision 22322)
@@ -0,0 +1,293 @@
+# include "psphotInternal.h"
+
+bool psphotMakeResiduals (psArray *sources, psMetadata *recipe, pmPSF *psf, psMaskType maskVal) {
+
+    bool status, isPSF;
+    double flux, dflux;
+    psU8 mflux;
+
+    psTimerStart ("residuals");
+
+    if (!psMetadataLookupBool(&status, recipe, "PSF.RESIDUALS")) return true;
+
+    int SPATIAL_ORDER = psMetadataLookupS32(&status, recipe, "PSF.RESIDUALS.SPATIAL_ORDER");
+    PS_ASSERT (status, false);
+    if (SPATIAL_ORDER != 0 && SPATIAL_ORDER != 1) {
+        psError(PSPHOT_ERR_CONFIG, true, "PSF.RESIDUALS.SPATIAL_ORDER must be 0 or 1 (not %d)",
+                SPATIAL_ORDER);
+        return false;
+    }
+
+    int xBin = psMetadataLookupS32(&status, recipe, "PSF.RESIDUALS.XBIN");
+    PS_ASSERT (status, false);
+    PS_ASSERT (xBin != 0, false);
+
+    int yBin = psMetadataLookupS32(&status, recipe, "PSF.RESIDUALS.YBIN");
+    PS_ASSERT (status, false);
+    PS_ASSERT (yBin != 0, false);
+
+    float nSigma = psMetadataLookupF32(&status, recipe, "PSF.RESIDUALS.NSIGMA");
+    PS_ASSERT (status, false);
+
+    float pixelSN = psMetadataLookupF32(&status, recipe, "PSF.RESIDUALS.PIX.SN");
+    PS_ASSERT (status, false);
+
+    char *modeString = psMetadataLookupStr(&status, recipe, "PSF.RESIDUALS.INTERPOLATION");
+    PS_ASSERT (status, false);
+
+    psImageInterpolateMode mode = psImageInterpolateModeFromString (modeString);
+    if (mode == PS_INTERPOLATE_NONE) {
+        psError(PSPHOT_ERR_CONFIG, false, "invalid interpolation in psphot.config");
+        return false;
+    }
+
+    char *statString = psMetadataLookupStr(&status, recipe, "PSF.RESIDUALS.STATISTIC");
+    PS_ASSERT (status, false);
+
+    psStatsOptions statOption = psStatsOptionFromString (statString);
+    if (!statOption) {
+        psError(PSPHOT_ERR_CONFIG, false, "invalid residual statistic in psphot.config");
+        return false;
+    }
+
+    // user parameters:
+    // size of aperture (determine from source images?)
+    // binning factor
+
+    // select the subset of sources which are the PSFSTARs
+    // for each input source:
+    // - construct a residual image, renormalized
+    // - construct a renormalized weight image
+    // - construct a new mask image
+
+    // construct the output residual table (Nx*DX,Ny*DY)
+    // for each output pixel:
+    // - construct a histogram of the values & weights (interpolate to the common pixel coordinate)
+    // - measure the robust median & sigma
+    // - reject (mask) input pixels which are outliers
+    // - re-measure the robust median & sigma
+    // - set output pixel, weight, and mask
+
+    // XXX need to set these correctly!!
+    const int badMask = 1;              // mask bits
+    const int poorMask = 2;             //       from psImageInterpolate
+    const int clippedMask = 4;          // mask bit set for clipped values
+
+    // determine the maximum image size from the input sources
+    int xSize = 0;
+    int ySize = 0;
+
+    psVector *xC = psVectorAllocEmpty (100, PS_TYPE_F32);
+    psVector *yC = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+    // build (DATA - MODEL) [an image] for each psf star
+    psArray *input = psArrayAllocEmpty (100);
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *source = sources->data[i];
+
+        if (!(source->mode & PM_SOURCE_MODE_PSFSTAR)) continue;
+
+        // which model to use?
+        pmModel *model = pmSourceGetModel (&isPSF, source);
+        if (model == NULL) continue;  // model must be defined
+
+        psImage *image  = psImageCopy (NULL, source->pixels,   PS_TYPE_F32);
+        psImage *weight = psImageCopy (NULL, source->weight,   PS_TYPE_F32);
+        psImage *mask   = psImageCopy (NULL, source->maskView, PS_TYPE_U8);
+        pmModelSub (image, mask, model, PM_MODEL_OP_FUNC, maskVal);
+
+        // re-normalize image and weight
+        float Io = model->params->data.F32[PM_PAR_I0];
+        psBinaryOp (image, image, "/", psScalarAlloc(Io, PS_TYPE_F32));
+        psBinaryOp (weight, weight, "/", psScalarAlloc(Io*Io, PS_TYPE_F32));
+
+        // we will interpolate the image and weight - include the mask or not?
+        // XXX consider better values for the mask bits
+        psImageInterpolateOptions *interp =
+            psImageInterpolateOptionsAlloc(mode, image, weight, NULL, 0xff, 0.0, 0.0, badMask, poorMask, 0.0);
+        psArrayAdd (input,  100, interp);
+
+        // save the X,Y position for future reference
+        xC->data.F32[xC->n] = model->params->data.F32[PM_PAR_XPOS];
+        yC->data.F32[yC->n] = model->params->data.F32[PM_PAR_YPOS];
+        psVectorExtend (xC, 100, 1);
+        psVectorExtend (yC, 100, 1);
+
+        xSize = PS_MAX (xSize, image->numCols);
+        ySize = PS_MAX (ySize, image->numRows);
+
+        // free up the excess references
+        psFree (mask);
+        psFree (image);
+        psFree (weight);
+        psFree (interp);
+    }
+    pmResiduals *resid = pmResidualsAlloc (xSize, ySize, xBin, yBin);
+
+    // x(resid) = (x(image) - Xo)*xBin + xCenter
+
+    psVector *fluxes  = psVectorAlloc (input->n, PS_TYPE_F32);
+    psVector *dfluxes = psVectorAlloc (input->n, PS_TYPE_F32);
+    psVector *fmasks  = psVectorAlloc (input->n, PS_TYPE_U8);
+
+    // statistic to use to determine baseline for clipping
+    psStats *fluxClip     = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
+    psStats *fluxClipDef  = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
+    // statistic to use to determine output flux
+    // XXX make API to convert statOption for MEAN/MEDIAN in to corresponding STDEV?
+    psStats *fluxStats    = psStatsAlloc (statOption | PS_STAT_SAMPLE_STDEV);
+    psStats *fluxStatsDef = psStatsAlloc (statOption | PS_STAT_SAMPLE_STDEV);
+
+    // Use psF64 to minimize overflow problems?
+    psImage *A = psImageAlloc(3, 3, PS_TYPE_F64); // Least-squares matrix
+    psVector *B = psVectorAlloc(3, PS_TYPE_F64); // Least-squares vector
+
+    // Solve MODEL = R + x R_x + y R_y in pixel-by-pixel a least-squares sense
+    // (If SPATIAL_ORDER == 0, just solve MODEL = R)
+    for (int oy = 0; resid != NULL && oy < resid->Ro->numRows; oy++) {
+        for (int ox = 0; ox < resid->Ro->numCols; ox++) {
+
+	    int nGoodPixel = 0;              // pixel is off the image
+    
+            // build the vector of data values for this output pixel
+            for (int i = 0; i < input->n; i++) {
+
+                psImageInterpolateOptions *interp = input->data[i];
+
+                // fractional image position
+                float ix = (ox + 0.5 - resid->xCenter) / (float) xBin + xC->data.F32[i] - interp->image->col0;
+                float iy = (oy + 0.5 - resid->yCenter) / (float) yBin + yC->data.F32[i] - interp->image->row0;
+
+                mflux = 0;
+                bool offImage = false;
+                if (psImageInterpolate (&flux, &dflux, &mflux, ix, iy, interp) == PS_INTERPOLATE_STATUS_OFF) {
+                    // This pixel is off the image
+                    offImage = true;
+		    fmasks->data.U8[i] = 1;
+		    // fprintf (stderr, "off image: %f %f : %f %f\n", ix, iy, flux, dflux);
+                } 
+                fluxes->data.F32[i] = flux;
+                dfluxes->data.F32[i] = dflux;
+                fmasks->data.U8[i] = mflux;
+		if (isnan(flux)) { 
+		    fmasks->data.U8[i] = 1;
+		}
+		if (fmasks->data.U8[i] == 0) {
+		    nGoodPixel ++;
+		}            
+	    }
+
+	    // skip pixels which are off the image...
+	    bool validPixel = (SPATIAL_ORDER == 0) ? (nGoodPixel > 1) : (nGoodPixel > 3);
+            if (!validPixel) {
+                resid->Ro->data.F32[oy][ox] = 0.0;
+                resid->Rx->data.F32[oy][ox] = 0.0;
+		resid->Ry->data.F32[oy][ox] = 0.0;
+		resid->mask->data.U8[oy][ox] = 1;
+		continue;
+            }
+
+            // measure the robust median to determine a baseline reference value
+            *fluxClip = *fluxClipDef;
+            psVectorStats (fluxClip, fluxes, NULL, fmasks, 0xff);
+            psErrorClear();             // clear (ignore) any outstanding errors
+
+            // mark input pixels which are more than N sigma from the median
+            for (int i = 0; i < fluxes->n; i++) {
+                float delta = fluxes->data.F32[i] - fluxClip->robustMedian;
+                float sigma = sqrt (dfluxes->data.F32[i]);
+                float swing = fabs(delta) / sigma;
+
+                // make this a user option
+                if (swing > nSigma) {
+                    fmasks->data.U8[i] = clippedMask;
+                }
+            }
+
+            if (SPATIAL_ORDER == 0) {
+                // measure the desired statistic on the unclipped pixels
+                *fluxStats = *fluxStatsDef;
+                psVectorStats (fluxStats, fluxes, NULL, fmasks, 0xff);
+                psErrorClear();         // clear (ignore) any outstanding errors
+
+                resid->Ro->data.F32[oy][ox] = psStatsGetValue(fluxStats, statOption);
+                resid->Rx->data.F32[oy][ox] = resid->Ry->data.F32[oy][ox] = 0.0;
+                //resid->weight->data.F32[oy][ox] = fluxStats->sampleStdev;
+
+		if (resid->Ro->data.F32[oy][ox] < pixelSN*fluxStats->sampleStdev) {
+		  resid->mask->data.U8[oy][ox] = 1;
+		}
+
+            } else {
+                assert (SPATIAL_ORDER == 1);
+                psImageInit(A, 0.0);
+                psVectorInit(B, 0.0);
+                for (int i = 0; i < fluxes->n; i++) {
+                    if (fmasks->data.U8[i]) continue;
+                    B->data.F64[0] += fluxes->data.F32[i]/dfluxes->data.F32[i];
+                    B->data.F64[1] += fluxes->data.F32[i]*xC->data.F32[i]/dfluxes->data.F32[i];
+                    B->data.F64[2] += fluxes->data.F32[i]*yC->data.F32[i]/dfluxes->data.F32[i];
+
+                    A->data.F64[0][0] += 1.0/dfluxes->data.F32[i];
+                    A->data.F64[1][0] += xC->data.F32[i]/dfluxes->data.F32[i];
+                    A->data.F64[2][0] += yC->data.F32[i]/dfluxes->data.F32[i];
+
+                    A->data.F64[1][1] += PS_SQR(xC->data.F32[i])/dfluxes->data.F32[i];
+                    A->data.F64[2][2] += PS_SQR(yC->data.F32[i])/dfluxes->data.F32[i];
+                    A->data.F64[1][2] += xC->data.F32[i]*yC->data.F32[i]/dfluxes->data.F32[i];
+                }
+
+                A->data.F64[0][1] = A->data.F64[1][0];
+                A->data.F64[0][2] = A->data.F64[2][0];
+                A->data.F64[2][1] = A->data.F64[1][2];
+
+                if (!psMatrixGJSolve(A, B)) {
+                    psError(PSPHOT_ERR_PSF, false, "Singular matrix solving for (y,x) = (%d,%d)'s residuals",
+                            oy, ox);
+                    psFree(resid); resid = NULL;
+                    break;
+                }
+
+                resid->Ro->data.F32[oy][ox] = B->data.F64[0];
+                resid->Rx->data.F32[oy][ox] = B->data.F64[1];
+                resid->Ry->data.F32[oy][ox] = B->data.F64[2];
+
+		float dRo = sqrt(A->data.F32[0][0]);
+		if (resid->Ro->data.F32[oy][ox] < pixelSN*dRo) {
+		  resid->mask->data.U8[oy][ox] = 1;
+		}
+                //resid->weight->data.F32[oy][ox] = XXX;
+            }
+        }
+    }
+
+    psFree (A);
+    psFree (B);
+
+    psLogMsg ("psphot.pspsf", PS_LOG_INFO, "generate residuals for %ld objects: %f sec\n", input->n, psTimerMark ("residuals"));
+
+    psFree (xC);
+    psFree (yC);
+    psFree (input);
+
+    psFree (fluxes);
+    psFree (dfluxes);
+    psFree (fmasks);
+
+    psFree (fluxStats);
+    psFree (fluxStatsDef);
+    psFree (fluxClip);
+    psFree (fluxClipDef);
+
+    if (resid != NULL && psTraceGetLevel("psphot") > 5) {
+      psphotSaveImage (NULL, resid->Ro,     "resid.ro.fits");
+      psphotSaveImage (NULL, resid->Rx,     "resid.rx.fits");
+      psphotSaveImage (NULL, resid->Ry,     "resid.ry.fits");
+      psphotSaveImage (NULL, resid->weight, "resid.wt.fits");
+      psphotSaveImage (NULL, resid->mask,   "resid.mk.fits");
+    }
+
+    psf->residuals = resid;
+    return (resid != NULL) ? true : false;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotMaskReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotMaskReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotMaskReadout.c	(revision 22322)
@@ -0,0 +1,55 @@
+# include "psphotInternal.h"
+
+// generate mask and weight if not defined, additional mask for restricted subregion 
+bool psphotSetMaskAndWeight (pmConfig *config, pmReadout *readout, psMetadata *recipe) {
+
+    bool status;
+
+    // ** Interpret the mask values:
+    // XXX drop the write to recipe and move config into psphotRoughClass?
+    // XXX alternatively, define a function to set the psphot recipe masks
+    psMaskType maskSat  = pmConfigMaskGet("SAT", config); // Mask value for saturated pixels
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, "MASK.SAT", PS_META_REPLACE, "user-defined mask", maskSat);
+
+    psMaskType maskBad  = pmConfigMaskGet("BAD", config); // Mask value for bad pixels
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, "MASK.BAD", PS_META_REPLACE, "user-defined mask", maskBad);
+
+    // generate mask & weight images if they don't already exit
+    if (!readout->mask) {
+        if (!pmReadoutGenerateMask(readout, maskSat, maskBad)) {
+            psError (PSPHOT_ERR_CONFIG, false, "trouble creating mask");
+            return false;
+        }
+    }
+    if (!readout->weight) {
+        if (!pmReadoutGenerateWeight(readout, true)) {
+            psError (PSPHOT_ERR_CONFIG, false, "trouble creating weight");
+            return false;
+        }
+    }
+
+    // mask the excluded outer pixels
+    // these coordinates refer to the parent image
+    // these bounds will saturate on the subimage
+    // negative upper bounds will subtract from the *subimage*
+    float XMIN  = psMetadataLookupF32 (&status, recipe, "XMIN");
+    float XMAX  = psMetadataLookupF32 (&status, recipe, "XMAX");
+    float YMIN  = psMetadataLookupF32 (&status, recipe, "YMIN");
+    float YMAX  = psMetadataLookupF32 (&status, recipe, "YMAX");
+    psRegion valid = psRegionSet (XMIN, XMAX, YMIN, YMAX);
+
+    // restrict the supplied region above to the valid area on the image
+    psRegion keep = psRegionForImage (readout->image, valid);
+
+    // psImageKeepRegion assumes the region refers to the parent coordinates
+    psImageKeepRegion (readout->mask, keep, "OR", maskBad);
+
+    // test output of files at this stage
+    if (psTraceGetLevel("psphot") >= 5) {
+        psphotSaveImage (NULL, readout->image,  "image.fits");
+        psphotSaveImage (NULL, readout->mask,   "mask.fits");
+        psphotSaveImage (NULL, readout->weight, "weight.fits");
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotMergeSources.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotMergeSources.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotMergeSources.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "psphotInternal.h"
+
+// add newly selected sources to the existing list of sources
+bool psphotMergeSources (psArray *oldSources, psArray *newSources) {
+
+    for (int i = 0; i < newSources->n; i++) {
+        pmSource *source = newSources->data[i];
+        psArrayAdd (oldSources, 100, source);
+    }
+    return true;
+}
+
+// merge the externally supplied sources with the existing sources.  mark them as having
+// mode PM_SOURCE_MODE_EXTERNAL
+bool psphotLoadExtSources (pmConfig *config, const pmFPAview *view, psArray *sources) {
+
+    // find the currently selected readout
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT.CMF");
+    if (!readout) {
+        psLogMsg ("psphot", 3, "no external sources supplied");
+        return true;
+    }
+
+    psArray *extSources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
+    if (!extSources) {
+        psLogMsg ("psphot", 3, "no external sources for this readout");
+        return true;
+    }
+
+    for (int i = 0; i < extSources->n; i++) {
+        pmSource *source = extSources->data[i];
+        source->mode |= PM_SOURCE_MODE_EXTERNAL;
+        pmModel *model = source->modelPSF;
+
+        float xpos = model->params->data.F32[PM_PAR_XPOS];
+        float ypos = model->params->data.F32[PM_PAR_YPOS];
+
+        source->peak = pmPeakAlloc(xpos, ypos, 1.0, PM_PEAK_LONE);
+        source->peak->xf = xpos;
+        source->peak->yf = ypos;
+        source->peak->flux = 1.0;
+
+        // drop the loaded source modelPSF
+        psFree (source->modelPSF);
+        source->modelPSF = NULL;
+    }
+
+    psphotMergeSources (sources, extSources);
+    psLogMsg ("psphot", 3, "%ld external sources merged to yield %ld total sources", extSources->n, sources->n);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelBackground.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelBackground.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelBackground.c	(revision 22322)
@@ -0,0 +1,320 @@
+# include "psphotInternal.h"
+static int npass = 0;
+
+// generate the median in NxN boxes, clipping heavily
+// linear interpolation to generate full-scale model
+bool psphotModelBackground (pmConfig *config, const pmFPAview *view, const char *filename)
+{
+    bool status = true;
+    static char *defaultStatsName = "FITTED_MEAN";
+
+    psTimerStart ("psphot");
+
+    // find the currently selected readout
+    pmFPAfile *file = psMetadataLookupPtr (&status, config->files, filename);
+    pmFPA *inFPA = file->fpa;
+    pmReadout *readout = pmFPAviewThisReadout (view, inFPA);
+    psImage *image = readout->image;
+    psImage *mask  = readout->mask;
+
+    // select the appropriate recipe information
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+    assert (recipe);
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    // user supplied seed, if available
+    unsigned long seed = psMetadataLookupS32 (&status, recipe, "IMSTATS_SEED");
+    if (!status) {
+        seed = 0;
+    }
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, seed);
+
+    // subtract this amount extra from the sky
+    float SKY_BIAS = psMetadataLookupF32 (&status, recipe, "SKY_BIAS");
+    if (!status) {
+        SKY_BIAS = 0;
+    }
+
+    // supply the sky background statistics options
+    char *statsName = psMetadataLookupStr (&status, recipe, "SKY_STAT");
+    if (statsName == NULL) {
+        statsName = defaultStatsName;
+    }
+    psStatsOptions statsOptionLocation = psStatsOptionFromString(statsName);
+    if (!(statsOptionLocation & (PS_STAT_SAMPLE_MEAN |
+                                 PS_STAT_SAMPLE_MEDIAN |
+                                 PS_STAT_ROBUST_MEDIAN |
+                                 PS_STAT_ROBUST_QUARTILE |
+                                 PS_STAT_CLIPPED_MEAN |
+                                 PS_STAT_FITTED_MEAN |
+                                 PS_STAT_FITTED_MEAN_V2 |
+                                 PS_STAT_FITTED_MEAN_V3 |
+                                 PS_STAT_FITTED_MEAN_V4))) {
+        statsOptionLocation = PS_STAT_FITTED_MEAN;
+    }
+
+    psStatsOptions statsOptionWidth = PS_STAT_NONE;
+    if (statsOptionLocation & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN)) {
+        statsOptionWidth = PS_STAT_SAMPLE_STDEV;
+    } else if (statsOptionLocation & (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_QUARTILE)) {
+#if 1
+        statsOptionWidth = PS_STAT_ROBUST_STDEV; // not set; => NaN
+#else
+        statsOptionWidth = PS_STAT_FITTED_STDEV;
+#endif
+    } else if (statsOptionLocation & PS_STAT_FITTED_MEAN) {
+        statsOptionWidth = PS_STAT_FITTED_STDEV;
+    } else if (statsOptionLocation & PS_STAT_CLIPPED_MEAN) {
+        statsOptionWidth = PS_STAT_CLIPPED_STDEV;
+    } else if (statsOptionLocation & PS_STAT_FITTED_MEAN_V2) {
+        statsOptionWidth = PS_STAT_FITTED_STDEV_V2;
+    } else if (statsOptionLocation & PS_STAT_FITTED_MEAN_V3) {
+        statsOptionWidth = PS_STAT_FITTED_STDEV_V3;
+    } else if (statsOptionLocation & PS_STAT_FITTED_MEAN_V4) {
+        statsOptionWidth = PS_STAT_FITTED_STDEV_V4;
+    } else {
+        psAbort("Unable to estimate variance of selected statsOptionLocations 0x%x", statsOptionLocation);
+    }
+
+    const psStatsOptions statsOption = statsOptionLocation | statsOptionWidth;
+    psStats *stats = psStatsAlloc (statsOption);
+
+    // set range for old-version of sky statistic
+    if (statsOptionLocation & PS_STAT_ROBUST_QUARTILE) {
+        stats->min = 0.25;
+        stats->max = 0.75;
+    }
+
+    // set user-option for number of pixels per region
+    stats->nSubsample = psMetadataLookupF32 (&status, recipe, "IMSTATS_NPIX");
+    if (!status) {
+        stats->nSubsample = 1000;
+    }
+
+    // optionally set the binsize
+    stats->binsize = psMetadataLookupF32 (&status, recipe, "SKY_HISTOGRAM_BINS");
+    if (status) {
+        stats->options |= PS_STAT_USE_BINSIZE;
+    }
+
+    // optionally set the sigma clipping
+    stats->clipSigma = psMetadataLookupF32 (&status, recipe, "SKY_CLIP_SIGMA");
+    if (!status) {
+        if ((stats->options & PS_STAT_FITTED_MEAN) || (stats->options & PS_STAT_FITTED_MEAN_V2)) {
+            stats->clipSigma = 1.0;
+        } else {
+            stats->clipSigma = 3.0;
+        }
+    }
+
+    // stats is not initialized by psStats???  use this to save the input options
+    // XXX re-work this to use the new psStatsInit function
+    psStats *statsDefaults = psStatsAlloc (statsOption);
+    *statsDefaults = *stats;
+
+    // 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, "BACKGROUND.XBIN");
+    binning->nYbin  = psMetadataLookupS32 (&status, recipe, "BACKGROUND.YBIN");
+
+    psImageBinningSetRuffSize(binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkip(binning, image);
+    status = psMetadataAddPtr(recipe, PS_LIST_TAIL, "PSPHOT.BACKGROUND.BINNING", PS_DATA_UNKNOWN | PS_META_REPLACE, "Background binning", binning);
+    PS_ASSERT (status, false);
+
+    // we save the binning structure for use in psphotMagnitudes
+    pmReadout *model      = pmFPAGenerateReadout (config, view, "PSPHOT.BACKMDL",       inFPA, binning);
+    pmReadout *modelStdev = pmFPAGenerateReadout (config, view, "PSPHOT.BACKMDL.STDEV", inFPA, binning);
+
+    psF32 **modelData = model->image->data.F32;
+    psF32 **modelStdevData = modelStdev->image->data.F32;
+
+    // XXXX we can thread this here by running blocks in parallel
+
+    // measure clipped median for subimages
+    psRegion ruffRegion = {0,0,0,0};
+    psRegion fineRegion = {0,0,0,0};
+    for (int iy = 0; iy < model->image->numRows; iy++) {
+        for (int ix = 0; ix < model->image->numCols; ix++) {
+
+            // convert the ruff grid cell to the equivalent fine grid cell
+            // XXX we need to watch out for row0,col0
+            ruffRegion = psRegionSet (ix, ix + 2, iy, iy + 2);
+            fineRegion = psImageBinningSetFineRegion (binning, ruffRegion);
+            fineRegion = psRegionForImage (image, fineRegion);
+
+            psImage *subset  = psImageSubset (image, fineRegion);
+            if (!subset->numCols || !subset->numRows) {
+                psFree (subset);
+                continue;
+            }
+            psImage *submask = psImageSubset (mask, fineRegion);
+
+            // reset the default values
+            *stats = *statsDefaults;
+
+            // Use the selected background statistic for the first pass
+            // If it fails, fall back on the "ROBUST_MEDIAN" version
+            // If both fail, set the pixel to NAN and (below) interpolate
+            // XXX psImageBackground will probably be renamed psImageStats
+            // XXX don't bother trying if there are no valid pixels...
+
+            psVector *sample = NULL;
+
+            // turn on stats tracing in desired cells
+            # if (0)
+            psMetadata *plots = psMetadataLookupPtr (&status, recipe, "DIAGNOSTIC.PLOTS");
+            assert (plots);
+
+            int xPlot = psMetadataLookupS32 (&status, plots, "IMAGE.BACKGROUND.CELL.HISTOGRAM.X");
+            assert (status);
+            int yPlot = psMetadataLookupS32 (&status, plots, "IMAGE.BACKGROUND.CELL.HISTOGRAM.Y");
+            assert (status);
+
+            bool gotX = (xPlot < 0) || (xPlot == ix);
+            bool gotY = (yPlot < 0) || (yPlot == iy);
+
+            if (gotX && gotY) {
+                psTraceSetLevel ("psLib.math.vectorFittedStats_v4", 6);
+                psTraceSetLevel ("psLib.math.vectorRobustStats", 6);
+            } else {
+                psTraceSetLevel ("psLib.math.vectorFittedStats_v4", 0);
+                psTraceSetLevel ("psLib.math.vectorRobustStats", 0);
+            }
+            # endif
+
+            if (psImageBackground(stats, &sample, subset, submask, maskVal, rng)) {
+                if (stats->options & PS_STAT_ROBUST_QUARTILE) {
+                    modelData[iy][ix] = stats->robustMedian;
+                } else {
+                    modelData[iy][ix] = psStatsGetValue(stats, statsOptionLocation);
+                }
+                modelStdevData[iy][ix] = psStatsGetValue(stats, statsOptionWidth);
+
+                // supply sample to plotting routing
+                psphotDiagnosticPlots (config, "IMAGE.BACKGROUND.CELL.HISTOGRAM", ix, iy, modelData[iy][ix], modelStdevData[iy][ix], sample);
+            } else {
+                psStatsOptions currentOptions = stats->options;
+                stats->options = PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV;
+                if (!psImageBackground(stats, &sample, subset, submask, maskVal, rng)) {
+                    psLogMsg ("psphot", PS_LOG_WARN, "Failed to estimate background using ROBUST_MEDIAN for "
+                               "(%dx%d, (row0,col0) = (%d,%d)",
+                               subset->numRows, subset->numCols, subset->row0, subset->col0);
+                    modelData[iy][ix] = modelStdevData[iy][ix] = NAN;
+                } else {
+                    modelData[iy][ix] = psStatsGetValue (stats, PS_STAT_ROBUST_MEDIAN);
+                    modelStdevData[iy][ix] = psStatsGetValue(stats, PS_STAT_ROBUST_STDEV);
+
+                    // supply sample to plotting routing
+                    psphotDiagnosticPlots (config, "IMAGE.BACKGROUND.CELL.HISTOGRAM", ix, iy, modelData[iy][ix], modelStdevData[iy][ix], sample);
+                }
+                // drop errors caused by psImageBackground failures
+                // XXX we probably should trap and exit on serious failures
+                psErrorClear();
+                stats->options = currentOptions;
+            }
+            psFree(sample);
+            modelData[iy][ix] += SKY_BIAS;
+            psFree (subset);
+            psFree (submask);
+        }
+    }
+
+    if (psTraceGetLevel("psphot") > 5) {
+        char name[256];
+        sprintf (name, "backraw.%02d.fits", npass);
+        psphotSaveImage (NULL, model->image, name);
+    }
+
+    // patch over bad regions (use average of 8 possible neighbor pixels)
+    // XXX consider testing all pixels against the 8 neighbors and replacing outliers...
+    double Count = 0;                   // number of good pixels
+    double Value = 0;                   // sum of good pixel's value
+    double ValueStdev = 0;              // sum of good pixel's standard deviations
+    for (int iy = 0; iy < model->image->numRows; iy++) {
+        for (int ix = 0; ix < model->image->numCols; ix++) {
+            if (!isnan(modelData[iy][ix])) {
+                Value += modelData[iy][ix];
+                ValueStdev += modelStdevData[iy][ix];
+                Count++;
+                continue;
+            }
+
+            double value = 0;
+            double count = 0;
+            for (int jy = iy - 1; jy <= iy + 1; jy++) {
+                if (jy <   0) continue;
+                if (jy >= model->image->numRows) continue;
+                for (int jx = ix - 1; jx <= ix + 1; jx++) {
+                    if (!jx && !jy) continue;
+                    if (jx   <   0) continue;
+                    if (jx   >= model->image->numCols) continue;
+                    value += modelData[jy][jx];
+                    count += 1.0;
+                }
+            }
+            if (count > 0) modelData[iy][ix] = value / count;
+        }
+    }
+    if (Count == 0) {
+        psError (PSPHOT_ERR_DATA, true, "failed to build background image");
+        psFree(stats);
+        psFree(statsDefaults);
+        psFree(binning);
+        psFree(rng);
+        return false;
+    }
+
+    Value /= Count;
+    ValueStdev /= Count;
+
+    // patch over remaining bad regions (use global average)
+    for (int iy = 0; iy < model->image->numRows; iy++) {
+        for (int ix = 0; ix < model->image->numCols; ix++) {
+            if (!isnan(modelData[iy][ix])) continue;
+            modelData[iy][ix] = Value;
+            modelStdevData[iy][ix] = ValueStdev;
+        }
+    }
+
+    psLogMsg ("psphot", PS_LOG_MINUTIA, "build median image: %f sec\n", psTimerMark ("psphot"));
+
+    psMetadataAddF32(recipe, PS_LIST_TAIL, "SKY_MEAN", PS_META_REPLACE, "sky mean", Value);
+    psMetadataAddF32(recipe, PS_LIST_TAIL, "SKY_STDEV", PS_META_REPLACE, "sky stdev", ValueStdev);
+    psLogMsg ("psphot", PS_LOG_INFO, "image sky : mean %f stdev %f", Value, ValueStdev);
+
+    // measure image and background stats and save for later output
+    psStats *statsBck = psStatsAlloc (PS_STAT_SAMPLE_MEAN |
+                                      PS_STAT_SAMPLE_STDEV |
+                                      PS_STAT_MIN |
+                                      PS_STAT_MAX);
+    psImageStats (statsBck, model->image, NULL, 0);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "MSKY_MN",
+                      PS_META_REPLACE, "sky model mean",          statsBck->sampleMean);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "MSKY_SIG",
+                      PS_META_REPLACE, "sky model stdev",         statsBck->sampleStdev);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "MSKY_MAX",
+                      PS_META_REPLACE, "sky model maximum value", statsBck->max);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "MSKY_MIN",
+                      PS_META_REPLACE, "sky model minimum value", statsBck->min);
+    psMetadataAddS32 (recipe, PS_LIST_TAIL, "MSKY_NX",
+                      PS_META_REPLACE, "sky model size (x)",      model->image->numCols);
+    psMetadataAddS32 (recipe, PS_LIST_TAIL, "MSKY_NY",
+                      PS_META_REPLACE, "sky model size (y)",      model->image->numRows);
+    psLogMsg ("psphot", PS_LOG_INFO, "background sky : min %f mean %f max %f stdev %f",
+              statsBck->min, statsBck->sampleMean, statsBck->max, statsBck->sampleStdev);
+
+    psFree(stats);
+    psFree(statsBck);
+    psFree(statsDefaults);
+    psFree(binning);
+    psFree(rng);
+
+    npass ++;
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelGroupInit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelGroupInit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelGroupInit.c	(revision 22322)
@@ -0,0 +1,25 @@
+# include "psphotInternal.h"
+
+// Add locally-defined models here.  As these mature, they can be moved to 
+// psModule/src/objects/models
+
+# include "models/pmModel_TEST1.c"
+# include "models/pmModel_STRAIL.c"
+
+static pmModelClass userModels[] = {
+    {"PS_MODEL_TEST1", 7, pmModelFunc_TEST1,  pmModelFlux_TEST1,  pmModelRadius_TEST1,  pmModelLimits_TEST1,  pmModelGuess_TEST1, pmModelFromPSF_TEST1, pmModelParamsFromPSF_TEST1, pmModelFitStatus_TEST1},
+    {"PS_MODEL_STRAIL", 9, pmModelFunc_STRAIL,  pmModelFlux_STRAIL,  pmModelRadius_STRAIL,  pmModelLimits_STRAIL,  pmModelGuess_STRAIL, pmModelFromPSF_STRAIL, pmModelParamsFromPSF_STRAIL, pmModelFitStatus_STRAIL},
+};
+
+void psphotModelClassInit (void) 
+{ 
+
+    // if pmModelClassInit returns false, we have already init'ed
+    if (!pmModelClassInit ()) return;
+
+    int Nmodels = sizeof (userModels) / sizeof (pmModelClass);
+    for (int i = 0; i < Nmodels; i++) {
+	pmModelClassAdd (&userModels[i]);
+    }
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelTest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelTest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelTest.c	(revision 22322)
@@ -0,0 +1,250 @@
+# include "psphotInternal.h"
+# define PM_SOURCE_FIT_PSF_X_EXT PM_SOURCE_FIT_PSF_AND_SKY
+
+// XXX add more test information?
+bool psphotModelTest (pmConfig *config, const pmFPAview *view, psMetadata *recipe) {
+
+    bool status;
+    int modelType = -1;
+    float obsMag, fitMag, value;
+    char name[64];
+    pmPSF *psf = NULL;
+    pmSourceFitMode fitMode;
+
+    // bit-masks to test for good/bad pixels
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psMaskType markVal = psMetadataLookupU8(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // run model fitting tests on a single source?
+    if (!psMetadataLookupBool (&status, recipe, "TEST_FIT")) return false;
+
+    psTimerStart ("modelTest");
+
+    // find the currently selected readout
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
+    PS_ASSERT_PTR_NON_NULL (readout, false);
+
+    // use poissonian errors or local-sky errors
+    bool POISSON_ERRORS = psMetadataLookupBool (&status, recipe, "POISSON_ERRORS");
+    if (!status) POISSON_ERRORS = true;
+    pmSourceFitModelInit (15, 0.1, 1.0, POISSON_ERRORS);
+
+    // find the various fitting parameters (try test values first)
+    float INNER = psMetadataLookupF32 (&status, recipe, "TEST_FIT_INNER_RADIUS");
+    if (!status || !isfinite(INNER)) {
+        INNER = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
+    }
+    float OUTER = psMetadataLookupF32 (&status, recipe, "TEST_FIT_OUTER_RADIUS");
+    if (!status || !isfinite(OUTER)) {
+        OUTER = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
+    }
+    float RADIUS = psMetadataLookupF32 (&status, recipe, "TEST_FIT_RADIUS");
+    if (!status || !isfinite(RADIUS)) {
+        RADIUS = psMetadataLookupF32 (&status, recipe, "PSF_FIT_RADIUS");
+    }
+    float mRADIUS = psMetadataLookupF32 (&status, recipe, "TEST_MOMENTS_RADIUS");
+    if (!status || !isfinite(mRADIUS)) {
+        mRADIUS = psMetadataLookupF32 (&status, recipe, "PSF_MOMENTS_RADIUS");
+    }
+
+    // define the source of interest
+    float xObj     = psMetadataLookupF32 (&status, recipe, "TEST_FIT_X");
+    float yObj     = psMetadataLookupF32 (&status, recipe, "TEST_FIT_Y");
+    if (!isfinite(xObj) || !isfinite(yObj)) psAbort ("object position is not defined");
+
+    // what fitting mode to use?
+    fitMode = PM_SOURCE_FIT_EXT;
+    char *fitModeWord = psMetadataLookupStr (&status, recipe, "TEST_FIT_MODE");
+    if (fitModeWord && !strcasecmp (fitModeWord, "PSF")) fitMode = PM_SOURCE_FIT_PSF;
+    if (fitModeWord && !strcasecmp (fitModeWord, "CONV")) fitMode = PM_SOURCE_FIT_PSF_X_EXT;
+    if (fitModeWord && !strcasecmp (fitModeWord, "DEFAULT")) fitMode = PM_SOURCE_FIT_EXT;
+
+    // construct the source structures
+    pmSource *source = pmSourceAlloc();
+    source->peak = pmPeakAlloc (xObj, yObj, 0, 0);
+    pmSourceDefinePixels (source, readout, xObj, yObj, OUTER);
+
+    // in fitMode, psf sets the model type
+    if (fitMode == PM_SOURCE_FIT_PSF) {
+        psf = psphotLoadPSF (config, view, recipe);
+        if (!psf) psAbort("PSF_INPUT_FILE not supplied");
+        modelType = psf->type;
+        source->type = PM_SOURCE_TYPE_STAR;
+    }
+    if (fitMode == PM_SOURCE_FIT_EXT) {
+        // find the model: supplied by user or first in the PSF_MODEL list
+        char *modelName  = psMetadataLookupStr (&status, recipe, "TEST_FIT_MODEL");
+        if (!status || !strcasecmp (modelName, "DEFAULT")) {
+            // get the list pointers for the PSF_MODEL entries
+
+            psList *list = NULL;
+            psMetadataItem *mdi = psMetadataLookup (recipe, "PSF_MODEL");
+            if (mdi == NULL) psAbort("missing PSF_MODEL selection");
+            if (mdi->type == PS_DATA_STRING) {
+                list = psListAlloc(NULL);
+                psListAdd (list, PS_LIST_HEAD, mdi);
+            } else {
+                if (mdi->type != PS_DATA_METADATA_MULTI) psAbort("missing PSF_MODEL selection");
+                list = psMemIncrRefCounter(mdi->data.list);
+            }
+
+            // take the first list element
+            psMetadataItem *item = psListGet (list, PS_LIST_HEAD);
+            modelName = item->data.V;
+        }
+        modelType = pmModelClassGetType (modelName);
+        if (modelType < 0) psAbort("unknown model %s", modelName);
+        source->type = PM_SOURCE_TYPE_EXTENDED;
+    }
+    if (fitMode == PM_SOURCE_FIT_PSF_X_EXT) {
+        // we need to load BOTH a psf and an ext model
+        psf = psphotLoadPSF (config, view, recipe);
+        if (!psf) psAbort("PSF_INPUT_FILE not supplied");
+
+        // find the model: supplied by user or first in the PSF_MODEL list
+        char *modelName  = psMetadataLookupStr (&status, recipe, "TEST_FIT_MODEL");
+        if (!status || !strcasecmp (modelName, "DEFAULT")) {
+            // get the list pointers for the PSF_MODEL entries
+
+            psList *list = NULL;
+            psMetadataItem *mdi = psMetadataLookup (recipe, "PSF_MODEL");
+            if (mdi == NULL) psAbort("missing PSF_MODEL selection");
+            if (mdi->type == PS_DATA_STRING) {
+                list = psListAlloc(NULL);
+                psListAdd (list, PS_LIST_HEAD, mdi);
+            } else {
+                if (mdi->type != PS_DATA_METADATA_MULTI) psAbort("missing PSF_MODEL selection");
+                list = psMemIncrRefCounter(mdi->data.list);
+            }
+
+            // take the first list element
+            psMetadataItem *item = psListGet (list, PS_LIST_HEAD);
+            modelName = item->data.V;
+        }
+        modelType = pmModelClassGetType (modelName);
+        if (modelType < 0) psAbort("unknown model %s", modelName);
+        source->type = PM_SOURCE_TYPE_EXTENDED;
+    }
+
+    // find the local sky
+    status = pmSourceLocalSky (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
+    if (!status) psAbort("pmSourceLocalSky error");
+
+    // get the source moments
+    status = pmSourceMoments (source, mRADIUS);
+    if (!status) psAbort("psSourceMoments error");
+    source->peak->value = source->moments->Peak;
+
+    fprintf (stderr, "sum: %f @ (%f, %f)\n", source->moments->Sum, source->moments->x, source->moments->y);
+    fprintf (stderr, "moments: %f, %f - %f\n", source->moments->Sx, source->moments->Sy, source->moments->Sxy);
+
+    psEllipseMoments moments;
+    moments.x2 = source->moments->Sx;
+    moments.y2 = source->moments->Sy;
+    moments.xy = source->moments->Sxy;
+    psEllipseAxes axes = psEllipseMomentsToAxes (moments, 20.0);
+
+    fprintf (stderr, "axes: %f @ (%f, %f)\n", axes.theta*180/M_PI, axes.major, axes.minor);
+
+    // get the initial model parameter guess
+    pmModel *model = pmSourceModelGuess (source, modelType);
+    source->modelEXT = model;
+
+    // if any parameters are defined by the user, take those values
+    int nParams = pmModelClassParameterCount (modelType);
+    psF32 *params = model->params->data.F32;
+    params[PM_PAR_XPOS] = xObj; // XXX use the user-supplied value,
+    params[PM_PAR_YPOS] = yObj; // XXX or use the centroid
+    for (int i = 0; i < nParams; i++) {
+        if (i == PM_PAR_XPOS) continue;
+        if (i == PM_PAR_YPOS) continue;
+
+        sprintf (name, "TEST_FIT_PAR%d", i);
+        value = psMetadataLookupF32 (&status, recipe, name);
+        if (status && isfinite (value)) {
+            params[i] = value;
+        }
+    }
+
+    float area = params[4]*params[5];
+    fprintf (stderr, "peak: %f @ (%f, %f)\n", source->moments->Sum*area, (double)source->peak->x, (double)source->peak->y);
+
+    // for PSF fitting, set the shape parameters based on the PSF & source position
+    if (fitMode == PM_SOURCE_FIT_PSF) {
+        source->modelPSF = pmModelFromPSF (model, psf);
+        psFree (model);
+        model = source->modelPSF;
+        params = model->params->data.F32;
+    }
+
+    // list model input shape
+    psEllipseShape shape;
+    shape.sx  = 1.4 / model->params->data.F32[4];
+    shape.sy  = 1.4 / model->params->data.F32[5];
+    shape.sxy = model->params->data.F32[6];
+    axes = psEllipseShapeToAxes (shape, 20.0);
+
+    fprintf (stderr, "guess: %f @ (%f, %f)\n", axes.theta*180/M_PI, axes.major, axes.minor);
+
+    fprintf (stderr, "input parameters: \n");
+    for (int i = 0; i < nParams; i++) {
+        fprintf (stderr, "%d : %f\n", i, params[i]);
+    }
+
+    // define the pixels used for the fit
+    psImageKeepCircle (source->maskObj, xObj, yObj, RADIUS, "OR", markVal);
+    psphotSaveImage (NULL, source->maskObj, "mask1.fits");
+
+    char *fitset = psMetadataLookupStr (&status, recipe, "TEST_FIT_SET");
+    if (status) {
+        status = psphotFitSet (source, model, fitset, fitMode, maskVal);
+        exit (0);
+    }
+
+    if (fitMode == PM_SOURCE_FIT_PSF_X_EXT) {
+        // build the psf for the object
+        source->modelPSF = pmModelFromPSF (model, psf);
+        source->modelEXT = model;
+
+	// what fraction of the PSF is used? (radius in pixels : 2 -> 5x5 box)
+	int psfSize = psMetadataLookupS32 (&status, recipe, "PCM_BOX_SIZE");
+	assert (status);
+
+        model = psphotPSFConvModel (readout, source, modelType, maskVal, markVal, psfSize);
+        params = model->params->data.F32;
+    } else {
+        status = pmSourceFitModel (source, model, fitMode, maskVal);
+    }
+
+    // measure the source mags
+    pmSourcePhotometryModel (&fitMag, model);
+    pmSourcePhotometryAper  (&obsMag, model, source->pixels, source->maskObj, maskVal);
+    fprintf (stderr, "ap: %f, fit: %f, apmifit: %f, nIter: %d\n", obsMag, fitMag, obsMag - fitMag, model->nIter);
+
+    // write out positive object
+    psphotSaveImage (NULL, source->pixels, "object.fits");
+
+    // subtract object, leave local sky
+    // pmModelSub (source->pixels, source->maskObj, model, PM_MODEL_OP_FULL, maskVal);
+    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+
+    fprintf (stderr, "output parameters: \n");
+    for (int i = 0; i < nParams; i++) {
+        fprintf (stderr, "%d : %f\n", i, params[i]);
+    }
+
+    // write out
+    psphotSaveImage (NULL, source->pixels, "resid.fits");
+    psphotSaveImage (NULL, source->maskObj, "mask.fits");
+
+    psLogMsg ("psphot", PS_LOG_INFO, "model test : %f sec\n", psTimerMark ("modelTest"));
+
+    exit (0);
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelWithPSF.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelWithPSF.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotModelWithPSF.c	(revision 22322)
@@ -0,0 +1,417 @@
+# include "psphotInternal.h"
+# define SAVE_IMAGES 0
+
+bool psphotModelWithPSF_LMM (
+    psMinimization *min,
+    psImage *covar,
+    psVector *params,
+    psMinConstraint *constraint,
+    pmSource *source,
+    const psKernel *psf,
+    psMinimizeLMChi2Func func)
+{
+    psTrace("psphot", 3, "---- begin ----\n");
+    PS_ASSERT_PTR_NON_NULL(min, false);
+    PS_ASSERT_VECTOR_NON_NULL(params, false);
+    PS_ASSERT_VECTOR_NON_EMPTY(params, false);
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
+    psVector *paramMask = NULL;
+    if (constraint != NULL) {
+        paramMask = constraint->paramMask;
+        if (paramMask != NULL) {
+            PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_U8, false);
+            PS_ASSERT_VECTORS_SIZE_EQUAL(params, paramMask, false);
+        }
+    }
+    PS_ASSERT_PTR_NON_NULL(func, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    psMinimizeLMLimitFunc checkLimits = NULL;
+    if (constraint) {
+        checkLimits = constraint->checkLimits;
+    }
+
+    // this function has test values and current values for several things
+    // the current value is in lower case
+    // the test value is in upper case
+
+    // allocate internal arrays (current vs Guess)
+    psImage *Alpha = NULL;
+    psVector *Beta = NULL;
+
+    // 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");
+    }
+    
+    // allocate internal arrays (current vs Guess)
+    psImage *alpha   = psImageAlloc(Alpha->numCols, Alpha->numRows, PS_TYPE_F32);
+    psVector *beta   = psVectorAlloc(Beta->n, PS_TYPE_F32);
+    psVector *Params = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    psF32 Chisq = 0.0;
+    psF32 lambda = 0.001;
+    psF32 dLinear = 0.0;
+
+    // generate PCM data storage structure
+    pmPCMData *pcm = pmPCMDataAlloc (params, paramMask, source);
+
+    // calculate initial alpha and beta, set chisq (min->value)
+    min->value = psphotModelWithPSF_SetABX(alpha, beta, params, paramMask, pcm, source, psf, func);
+    if (isnan(min->value)) {
+        min->iter = min->maxIter;
+        return(false);
+    }
+    // dump some useful info if trace is defined
+    if (psTraceGetLevel("psphot") >= 6) {
+        p_psImagePrint(psTraceGetDestination(), alpha, "alpha guess (0)");
+        p_psVectorPrint(psTraceGetDestination(), beta, "beta guess (0)");
+    }
+    if (psTraceGetLevel("psphot") >= 5) {
+        p_psVectorPrint(psTraceGetDestination(), params, "params guess (0)");
+    }
+
+    // iterate until the tolerance is reached, or give up
+    while ((min->iter < min->maxIter) && ((min->lastDelta > min->tol) || !isfinite(min->lastDelta))) {
+        psTrace("psphot", 5, "Iteration number %d.  (max iterations is %d).\n", min->iter, min->maxIter);
+        psTrace("psphot", 5, "Last delta is %f.  Min->tol is %f.\n", min->lastDelta, min->tol);
+
+
+        // set a new guess for Alpha, Beta, Params
+        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda, &dLinear)) {
+            min->iter ++;
+            lambda *= 10.0;
+            continue;
+        }
+
+        // dump some useful info if trace is defined
+        if (psTraceGetLevel("psphot") >= 6) {
+            p_psImagePrint(psTraceGetDestination(), Alpha, "Alpha guess (1)");
+            p_psVectorPrint(psTraceGetDestination(), Beta, "Beta guess (1)");
+            p_psVectorPrint(psTraceGetDestination(), beta, "beta current (1)");
+        }
+        if (psTraceGetLevel("psphot") >= 5) {
+            p_psVectorPrint(psTraceGetDestination(), Params, "params guess (1)");
+        }
+
+        // calculate Chisq for new guess, update Alpha & Beta
+        Chisq = psphotModelWithPSF_SetABX(Alpha, Beta, Params, paramMask, pcm, source, psf, func);
+        if (isnan(Chisq)) {
+            min->iter ++;
+            lambda *= 10.0;
+            continue;
+        }
+
+        // convergence criterion:
+        // compare the delta (min->value - Chisq) with the
+        // expected delta from the linear model (dLinear)
+        // accept new guess if it is an improvement (rho > 0), or else increase lambda
+        psF32 rho = (min->value - Chisq) / dLinear;
+
+        psTrace("psphot", 5, "last chisq: %f, new chisq %f, delta: %f, rho: %f\n", min->value,
+                Chisq, min->lastDelta, rho);
+
+        // dump some useful info if trace is defined
+        if (psTraceGetLevel("psphot") >= 6) {
+            p_psImagePrint(psTraceGetDestination(), Alpha, "alpha guess (2)");
+            p_psVectorPrint(psTraceGetDestination(), Beta, "beta guess (2)");
+        }
+
+        /* if (Chisq < min->value) {  */
+        if (rho > 0.0) {
+            min->lastDelta = (min->value - Chisq) / (source->pixels->numCols*source->pixels->numRows - params->n);
+            min->value = Chisq;
+            alpha  = psImageCopy(alpha, Alpha, PS_TYPE_F32);
+            beta   = psVectorCopy(beta, Beta, PS_TYPE_F32);
+            params = psVectorCopy(params, Params, PS_TYPE_F32);
+            lambda *= 0.25;
+
+	    // save the new convolved model image
+	    psFree (source->modelFlux);
+	    source->modelFlux = pmPCMDataSaveImage(pcm);
+        } else {
+            lambda *= 10.0;
+        }
+        min->iter++;
+    }
+    psTrace("psphot", 5, "chisq: %f, last delta: %f, Niter: %d\n", min->value, min->lastDelta, min->iter);
+
+    // construct & return the covariance matrix (if requested)
+    if (covar != NULL) {
+        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, NULL, 0.0, NULL)) {
+            psTrace ("psphot", 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.U8[j])) {
+		covar->data.F32[j][j] = 1.0;
+		continue;
+	    }
+	    for (int k = 0, K = 0; k < params->n; k++) {
+		if (paramMask && (paramMask->data.U8[k])) continue;
+		covar->data.F32[j][k] = Alpha->data.F32[J][K];
+		K++;
+	    }
+	    J++;
+	}
+    }
+
+    // free the internal temporary data
+    psFree(alpha);
+    psFree(Alpha);
+    psFree(beta);
+    psFree(Beta);
+    psFree(Params);
+    psFree(pcm);
+
+    if (min->iter == min->maxIter) {
+        psTrace("psphot", 3, "---- end (false) ----\n");
+        return(false);
+    }
+
+    psTrace("psphot", 3, "---- end (true) ----\n");
+    return(true);
+}
+
+psF32 psphotModelWithPSF_SetABX(
+    psImage  *alpha,
+    psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    pmPCMData *pcm,
+    const pmSource *source,
+    const psKernel *psf,
+    psMinimizeLMChi2Func func)
+{
+    // XXX: Check vector sizes.
+    PS_ASSERT_IMAGE_NON_NULL(alpha, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(beta, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+
+    PS_ASSERT_PTR_NON_NULL(source, NAN);
+    PS_ASSERT_IMAGE_NON_NULL(source->pixels, NAN);
+    PS_ASSERT_IMAGE_NON_NULL(source->weight, NAN);
+    PS_ASSERT_IMAGE_NON_NULL(source->maskObj, NAN);
+
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
+    if (paramMask) {
+        PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_MASK, false);
+    }
+
+    // 1 *** generate the model and derivative images for this parameter set
+
+    // storage for model derivatives
+    psVector *deriv = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    // working vector to store local coordinate
+    psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+
+    psImageInit (pcm->model, 0.0);
+    for (int n = 0; n < params->n; n++) {
+	if (!pcm->dmodels->data[n]) continue;
+	psImageInit (pcm->dmodels->data[n], 0.0);
+    }
+
+    // fill in the coordinate and value entries
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+
+	    // XXX can we skip some of the data points where the model
+	    // is not going to be fitted??
+
+            // skip masked points
+	    // XXX probably should not skipped masked points: 
+	    // XXX skip if convolution of unmasked pixels will not see this pixel
+            // if (source->maskObj->data.U8[i][j]) {
+	    // continue;
+	    // }
+
+            // skip zero-weight points
+	    // XXX why is this not masked?
+            // if (source->weight->data.F32[i][j] == 0) {
+	    // continue;
+	    // }
+            // skip nan value points
+	    // XXX why is this not masked?
+            // if (!isfinite(source->pixels->data.F32[i][j])) {
+	    // continue;
+	    // }
+
+            // Convert i/j to image space:
+            coord->data.F32[0] = (psF32) (j + source->pixels->col0);
+            coord->data.F32[1] = (psF32) (i + source->pixels->row0);
+
+	    pcm->model->data.F32[i][j] = func (deriv, params, coord);
+
+	    for (int n = 0; n < params->n; n++) {
+		if ((paramMask != NULL) && (paramMask->data.U8[n])) { continue; }
+		psImage *dmodel = pcm->dmodels->data[n];
+		dmodel->data.F32[i][j] = deriv->data.F32[n];
+	    }
+        }
+    }
+    psFree(coord);
+    psFree(deriv);
+
+    // convolve model and dmodel arrays with PSF
+    psImageConvolveDirect (pcm->modelConv, pcm->model, psf);
+    for (int n = 0; n < pcm->dmodels->n; n++) {
+	if (pcm->dmodels->data[n] == NULL) continue;
+	psImage *dmodel = pcm->dmodels->data[n];
+	psImage *dmodelConv = pcm->dmodelsConv->data[n];
+	psImageConvolveDirect (dmodelConv, dmodel, psf);
+    }
+
+    // XXX TEST : SAVE IMAGES
+# if (SAVE_IMAGES) 
+    psphotSaveImage (NULL, psf->image, "psf.fits");
+    psphotSaveImage (NULL, pcm->model, "model.fits");
+    psphotSaveImage (NULL, pcm->modelConv, "modelConv.fits");
+    psphotSaveImage (NULL, source->pixels, "obj.fits");
+    psphotSaveImage (NULL, source->maskObj, "mask.fits");
+    psphotSaveImage (NULL, source->weight, "weight.fits");
+# endif
+
+    // 2 *** accumulate alpha & beta 
+
+    // zero alpha and beta for summing below
+    psImageInit (alpha, 0.0);
+    psVectorInit (beta, 0.0);
+    float chisq = 0.0;
+
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+	    // XXX are we doing the right thing with the mask?
+            // skip masked points
+            if (source->maskObj->data.U8[i][j]) {
+                continue;
+            }
+            // skip zero-weight points
+            if (source->weight->data.F32[i][j] == 0) {
+                continue;
+            }
+            // skip nan value points
+            if (!isfinite(source->pixels->data.F32[i][j])) {
+                continue;
+            }
+
+	    float ymodel  = pcm->modelConv->data.F32[i][j];
+	    float yweight = 1.0 / source->weight->data.F32[i][j];
+	    float delta = ymodel - source->pixels->data.F32[i][j];
+
+	    chisq += PS_SQR(delta) * yweight;
+
+	    if (isnan(delta)) psAbort("nan in delta");
+	    if (isnan(chisq)) psAbort("nan in chisq");
+
+	    // alpha & beta only contain unmasked elements 
+	    for (int n1 = 0, N1 = 0; n1 < params->n; n1++) {
+		if ((paramMask != NULL) && (paramMask->data.U8[n1])) continue;
+		psImage *dmodel = pcm->dmodelsConv->data[n1];
+		float weight = dmodel->data.F32[i][j] * yweight;
+		for (int n2 = 0, N2 = 0; n2 <= n1; n2++) {
+		    if ((paramMask != NULL) && (paramMask->data.U8[n2])) continue;
+		    dmodel = pcm->dmodelsConv->data[n2];
+		    alpha->data.F32[N1][N2] += weight * dmodel->data.F32[i][j];
+		    N2++;
+		}
+		beta->data.F32[N1] += weight * delta;
+		N1++;
+	    }
+	}
+    }
+
+    // calculate lower-left half of alpha
+    for (psS32 j = 1; j < alpha->numCols; j++) {
+        for (psS32 k = 0; k < j; k++) {
+            alpha->data.F32[k][j] = alpha->data.F32[j][k];
+        }
+    }
+
+    return(chisq);
+}
+
+static void pmPCMDataFree (pmPCMData *pcm) {
+
+    if (pcm == NULL) return;
+
+    psFree (pcm->model);
+    psFree (pcm->modelConv);
+    psFree (pcm->dmodels);
+    psFree (pcm->dmodelsConv);
+    return;
+}
+
+pmPCMData *pmPCMDataAlloc (
+    const psVector *params,
+    const psVector *paramMask,
+    pmSource *source) {
+
+    pmPCMData *pcm = (pmPCMData *) psAlloc(sizeof(pmPCMData));
+    psMemSetDeallocator(pcm, (psFreeFunc) pmPCMDataFree);
+
+    // Allocate storage images for raw model and derivative images
+    pcm->model = psImageCopy (NULL, source->pixels, PS_TYPE_F32);
+    pcm->dmodels = psArrayAlloc (params->n);
+    for (psS32 n = 0; n < params->n; n++) {
+	pcm->dmodels->data[n] = NULL;
+	if ((paramMask != NULL) && (paramMask->data.U8[n])) { continue; }
+	pcm->dmodels->data[n] = psImageCopy (NULL, source->pixels, PS_TYPE_F32);
+    }
+
+    // Allocate storage images for convolved model and derivative images
+    pcm->modelConv = psImageCopy (NULL, source->pixels, PS_TYPE_F32);
+    pcm->dmodelsConv = psArrayAlloc (params->n);
+    for (psS32 n = 0; n < params->n; n++) {
+	pcm->dmodelsConv->data[n] = NULL;
+	if ((paramMask != NULL) && (paramMask->data.U8[n])) { continue; }
+	pcm->dmodelsConv->data[n] = psImageCopy (NULL, source->pixels, PS_TYPE_F32);
+    }
+
+    return pcm;
+}
+
+psImage *pmPCMDataSaveImage (pmPCMData *pcm) {
+
+    psImage *model = psImageCopy (NULL, pcm->modelConv, PS_TYPE_F32);
+   
+    return model;
+}
+
+/*
+ *
+ * we have a function func(param; value)
+ 
+ * basic LMM:
+ 
+ - fill in the data (x, y)
+ 
+ chisq = SetABX (alpha, beta, params, paramMask, x, y, dy, func)
+ 
+ while () {
+ GuessABP (Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda)
+ dLinear = dLinear(Beta, beta, lambda);
+ chisq = SetABX (alpha, beta, params, paramMask, x, y, dy, func)
+ convergence tests...
+ }
+ 
+ 
+
+ ** GuessABP:
+
+ f_c = sum_i (kern_i * func (x_i; p_o))
+
+ df_c/dp_o = d/dp_o [sum_i (kern_i * func (x_i; p_o))]
+
+ df_c/dp_o = sum_i (d/dp_o [kern_i * func (x_i; p_o)])
+
+ df_c/dp_o = sum_i (kern_i * d/dp_o [func (x_i; p_o)])
+
+ - generate image arrays for func, dfunc/dp_j (not masked)
+ - convolve each with psf
+ - measure delta = f_conv - data
+ - etc
+*/
+
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotMosaicChip.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotMosaicChip.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotMosaicChip.c	(revision 22322)
@@ -0,0 +1,39 @@
+# include "psphotInternal.h"
+
+bool psphotMosaicChip(pmConfig *config, const pmFPAview *view, char *outFile, char *inFile)
+{
+    bool status;                        // Status of MD lookup
+
+    pmFPAfile *in = psMetadataLookupPtr(&status, config->files, inFile); // Input file
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find required I/O file!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAfile *out = psMetadataLookupPtr(&status, config->files, outFile); // Output file
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find required I/O file!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // XXXX we are failing to get the output hdu right
+    pmChip *outChip = pmFPAviewThisChip(view, out->fpa);
+    pmChip *inChip = pmFPAviewThisChip(view, in->fpa);
+    if (!outChip->hdu && !outChip->parent->hdu) {
+        const char *name = psMetadataLookupStr(&status, in->fpa->concepts, "FPA.OBS"); // Name of FPA
+        pmFPAAddSourceFromView(out->fpa, name, view, out->format);
+    }
+
+    psMaskType blankMask = pmConfigMaskGet("BLANK", config);
+
+    // mosaic the chip, forcing a deep copy (resulting images are not subimages)
+    psTrace("pmChipMosaic", 5, "mosaic chip %s to %s (xbin,ybin: %d,%d to %d,%d)\n",
+            in->name, out->name, in->xBin, in->yBin, out->xBin, out->yBin);
+    status = pmChipMosaic(outChip, inChip, true, blankMask);
+    return status;
+}
+
+// XXX does this do everything needed?
+// * mask & weight
+// * loaded PSF model (in readout->analysis)
+// * loaded SRC sources (in readout->analysis)
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotMosaicSubimage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotMosaicSubimage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotMosaicSubimage.c	(revision 22322)
@@ -0,0 +1,46 @@
+# include "psphotInternal.h"
+
+// insert the source image into the outimage at Xo, Yo
+bool psphotMosaicSubimage (psImage *outImage, pmSource *source, int Xo, int Yo, int DX, int DY) {
+
+    psRegion inRegion, outRegion;
+    psImage *inImage = source->pixels;
+
+    // identify the region in the output image
+    outRegion = psRegionSet (Xo, Xo + DX, Yo, Yo + DX);
+    outRegion = psRegionForImage (outImage, outRegion);
+    int DXo = outRegion.x1 - outRegion.x0;
+    int DYo = outRegion.y1 - outRegion.y0;
+    if (DXo <= 0) return false;
+    if (DYo <= 0) return false;
+    
+    // center the input source in the output box
+    int dX = (DXo - 1) / 2;
+    int dY = (DYo - 1) / 2;
+
+    // int xo = inImage->col0 + inImage->numCols / 2;
+    // int yo = inImage->row0 + inImage->numRows / 2;
+
+    int xo = source->peak->xf;
+    int yo = source->peak->yf;
+
+    // adjust region to overlay input image pixels
+    inRegion = psRegionSet (xo - dX, xo + dX + 1, yo - dY, yo + dY + 1);
+    inRegion = psRegionForImage (inImage, inRegion);
+
+    float peak = source->peak->flux;
+
+    psImage *subImage = psImageSubset (inImage, inRegion);
+    psImage *newImage = psImageAlloc (subImage->numCols, subImage->numRows, PS_TYPE_F32);
+    for (int iy = 0; iy < newImage->numRows; iy++) {
+	for (int ix = 0; ix < newImage->numCols; ix++) {
+	    newImage->data.F32[iy][ix] = subImage->data.F32[iy][ix] / peak;
+	}
+    }
+
+    psImageOverlaySection (outImage, newImage, Xo, Yo, "=");
+
+    psFree (subImage);
+    psFree (newImage);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotOutput.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotOutput.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotOutput.c	(revision 22322)
@@ -0,0 +1,158 @@
+# include "psphotInternal.h"
+
+pmReadout *psphotSelectBackground (pmConfig *config,
+                                   const pmFPAview *view,
+                                   const bool stdev // return background's standard deviation?
+                                   ) {
+
+    bool status;
+    pmReadout *background;
+
+    pmFPAfile *file = psMetadataLookupPtr (&status, config->files, "PSPHOT.BACKMDL");
+    if (!file) return NULL;
+    if (file->mode == PM_FPA_MODE_INTERNAL) {
+        background = file->readout;
+    } else {
+        background = pmFPAviewThisReadout (view, file->fpa);
+    }
+    return background;
+}
+
+// XXX replace this with a call to a pmConfig function (pmConfigDump...)
+bool psphotDumpConfig (pmConfig *config) {
+
+  psMetadataConfigWrite (config->user, "user.md");
+  psMetadataConfigWrite (config->camera, "camera.md");
+  psMetadataConfigWrite (config->recipes, "recipes.md");
+  psMetadataConfigWrite (config->arguments, "arguments.md");
+  psMetadataConfigWrite (config->files, "files.md");
+  return true;
+}
+
+int psphotSaveImage (psMetadata *header, psImage *image, char *filename) {
+
+    psFits *fits = psFitsOpen (filename, "w");
+    psFitsWriteImage (fits, NULL, image, 0, NULL);
+    psFitsClose (fits);
+    return (TRUE);
+}
+
+bool psphotDumpMoments (psMetadata *recipe, psArray *sources) {
+
+    bool status;
+
+    // optional dump of all rough source data
+    char *output = psMetadataLookupStr (&status, recipe, "MOMENTS_OUTPUT_FILE");
+    if (!output) return false;
+    if (output[0] == 0) return false;
+    if (!strcasecmp (output, "NONE")) return false;
+
+    pmMomentsWriteText (sources, output);
+    return true;
+}
+
+bool psphotDumpSource (pmSource *source, char *name) {
+
+    FILE *f = fopen (name, "w");
+    if (f == NULL) psAbort("can't open file");
+
+    for (int i = 0; i < source->pixels->numRows; i++) {
+        for (int j = 0; j < source->pixels->numCols; j++) {
+            // skip masked points
+            if (source->maskObj->data.U8[i][j]) {
+                continue;
+            }
+            // skip zero-weight points
+            if (source->weight->data.F32[i][j] == 0) {
+                continue;
+            }
+
+            fprintf (f, "%d %d %f %f %d\n",
+                     (j + source->pixels->col0),
+                     (i + source->pixels->row0),
+                     source->pixels->data.F32[i][j],
+                     1.0 / source->weight->data.F32[i][j],
+                     source->maskObj->data.U8[i][j]);
+        }
+    }
+    fclose (f);
+    return true;
+}
+
+bool psphotAddPhotcode (psMetadata *recipe, pmConfig *config, const pmFPAview *view, const char *filerule) {
+
+    bool status;
+
+    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, filerule);
+    PS_ASSERT (status, false);
+
+    // determine PHOTCODE from fpa & view, overwrite in recipe
+    char *photcode = pmConceptsPhotcodeForView (input, view);
+    PS_ASSERT (photcode, false);
+
+    psMetadataAddStr (recipe, PS_LIST_TAIL, "PHOTCODE", PS_META_REPLACE, "photcode from FPA concepts", photcode);
+    psLogMsg ("psphot", 3, "PHOTCODE is %s", photcode);
+
+    psFree (photcode);
+    return true;
+}
+
+bool psphotSetHeaderNstars (psMetadata *recipe, psArray *sources) {
+
+    int nSrc = 0;
+
+    // count the number of sources which will be written
+    for (int i = 0; (sources != NULL) && (i < sources->n); i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+        pmModel *model = pmSourceGetModel (NULL, source);
+        if (model == NULL)
+            continue;
+        nSrc ++;
+    }
+    psMetadataAdd (recipe, PS_LIST_TAIL, "NSTARS", PS_DATA_S32 | PS_META_REPLACE, "NUMBER OF STARS", nSrc);
+    return true;
+}
+
+// these values are saved in an output header stub - they are added to either the
+// PHU header (CMP) or the MEF table header (CMF)
+psMetadata *psphotDefineHeader (psMetadata *recipe) {
+
+    psMetadata *header = psMetadataAlloc ();
+
+    // write necessary information to output header
+    psMetadataItemSupplement (header, recipe, "ZERO_PT");
+    psMetadataItemSupplement (header, recipe, "PHOTCODE");
+
+    psMetadataItemSupplement (header, recipe, "APMIFIT");
+    psMetadataItemSupplement (header, recipe, "DAPMIFIT");
+    psMetadataItemSupplement (header, recipe, "NAPMIFIT");
+    psMetadataItemSupplement (header, recipe, "SKYBIAS");
+    psMetadataItemSupplement (header, recipe, "SKYSAT");
+
+    // PSF model parameters (shape values for image center)
+    psMetadataItemSupplement (header, recipe, "NPSFSTAR");
+    psMetadataItemSupplement (header, recipe, "APLOSS");
+    psMetadataItemSupplement (header, recipe, "FWHM_X");
+    psMetadataItemSupplement (header, recipe, "FWHM_Y");
+    psMetadataItemSupplement (header, recipe, "ANGLE");
+
+    // XXX these need to be defined from elsewhere
+    psMetadataAdd (header, PS_LIST_TAIL, "FSATUR",   PS_DATA_F32 | PS_META_REPLACE, "SATURATION MAG",      0.0);
+    psMetadataAdd (header, PS_LIST_TAIL, "FLIMIT",   PS_DATA_F32 | PS_META_REPLACE, "COMPLETENESS MAG",    0.0);
+    psMetadataItemSupplement (header, recipe, "NSTARS");
+
+    // sky background model statistics
+    psMetadataItemSupplement (header, recipe, "MSKY_MN");
+    psMetadataItemSupplement (header, recipe, "MSKY_SIG");
+    psMetadataItemSupplement (header, recipe, "MSKY_MIN");
+    psMetadataItemSupplement (header, recipe, "MSKY_MAX");
+    psMetadataItemSupplement (header, recipe, "MSKY_NX");
+    psMetadataItemSupplement (header, recipe, "MSKY_NY");
+
+    psMetadataAddF32 (header, PS_LIST_TAIL, "DT_PHOT", PS_META_REPLACE, "elapsed psphot time", psTimerMark ("psphotReadout"));
+
+    // XXX : don't require any of these about values to exist
+    psErrorClear ();
+
+    return header;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotPSFConvModel.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotPSFConvModel.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotPSFConvModel.c	(revision 22322)
@@ -0,0 +1,139 @@
+# include "psphotInternal.h"
+# define USE_DELTA_PSF 0
+
+// save as static values so they may be set externally
+static psF32 PM_SOURCE_FIT_MODEL_NUM_ITERATIONS = 15;
+static psF32 PM_SOURCE_FIT_MODEL_TOLERANCE = 0.1;
+
+// input source has both modelPSF and modelEXT.  on successful exit, we set the
+// modelConv to contain the fitted parameters, and the modelFlux to contain the 
+// convolved model image.
+pmModel *psphotPSFConvModel (pmReadout *readout, pmSource *source, pmModelType modelType, psMaskType maskVal, psMaskType markVal, int psfSize) {
+    
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // make sure we save a cached copy of the psf flux
+    pmSourceCachePSF (source, maskVal);
+
+    // convert the cached cached psf model for this source to a psKernel
+    psKernel *psf = psphotKernelFromPSF (source, psfSize);
+    if (!psf) return NULL;
+
+# if (USE_DELTA_PSF)
+    psImageInit (psf->image, 0.0);
+    psf->image->data.F32[(int)(0.5*psf->image->numRows)][(int)(0.5*psf->image->numCols)] = 1.0;
+# endif
+
+    // generate copy of the model
+    // XXX we could modify the parameter values or even the model 
+    // here based on the observed seeing (some lookup table...)
+
+    // use the source moments, etc to guess basic model parameters
+    pmModel *modelConv = pmSourceModelGuess (source, modelType);
+    if (!modelConv) {
+	psFree (psf);
+	return NULL;
+    }
+
+    // XXX test : modify the Io, SXX, SYY terms based on the psf SXX, SYY terms:
+    psEllipseShape psfShape;
+    psfShape.sx  = source->modelPSF->params->data.F32[PM_PAR_SXX] / M_SQRT2;
+    psfShape.sxy = source->modelPSF->params->data.F32[PM_PAR_SXY];
+    psfShape.sy  = source->modelPSF->params->data.F32[PM_PAR_SYY] / M_SQRT2;
+    psEllipseAxes psfAxes = psEllipseShapeToAxes (psfShape, 20.0);
+
+    // XXX test : modify the Io, SXX, SYY terms based on the psf SXX, SYY terms:
+    psEllipseShape extShape;
+    extShape.sx  = modelConv->params->data.F32[PM_PAR_SXX] / M_SQRT2;
+    extShape.sxy = modelConv->params->data.F32[PM_PAR_SXY];
+    extShape.sy  = modelConv->params->data.F32[PM_PAR_SYY] / M_SQRT2;
+    psEllipseAxes extAxes = psEllipseShapeToAxes (extShape, 20.0);
+
+    // decrease the initial guess ellipse by psf_minor axis:
+    psEllipseAxes extAxesMod;
+    extAxesMod.major = sqrt (PS_MAX (1.0, PS_SQR(extAxes.major) - PS_SQR(psfAxes.minor)));
+    extAxesMod.minor = sqrt (PS_MAX (1.0, PS_SQR(extAxes.minor) - PS_SQR(psfAxes.minor)));
+    extAxesMod.theta = extAxes.theta;
+
+    psEllipseShape extShapeMod = psEllipseAxesToShape (extAxesMod);
+    modelConv->params->data.F32[PM_PAR_SXX] = extShapeMod.sx * M_SQRT2;
+    modelConv->params->data.F32[PM_PAR_SXY] = extShapeMod.sxy;
+    modelConv->params->data.F32[PM_PAR_SYY] = extShapeMod.sy * M_SQRT2;
+
+    // increase the initial guess central intensity by 2pi r^2:
+    modelConv->params->data.F32[PM_PAR_I0] *= (1.0 + PS_SQR(psfAxes.minor) / PS_SQR(extAxesMod.minor));
+
+    psVector *params  = modelConv->params;
+    psVector *dparams = modelConv->dparams;
+
+    psphotCheckRadiusEXT (readout, source, modelConv, markVal);
+
+    // create the minimization constraints
+    psMinConstraint *constraint = psMinConstraintAlloc();
+    constraint->paramMask = psVectorAlloc (params->n, PS_TYPE_U8);
+    constraint->checkLimits = modelConv->modelLimits;
+
+    // set parameter mask based on fitting mode
+    // we fit a model without a floating sky term
+    int nParams = params->n - 1;
+    psVectorInit (constraint->paramMask, 0);
+    constraint->paramMask->data.U8[PM_PAR_SKY] = 1;
+
+    // force the floating parameters to fall within the contraint ranges
+    for (int i = 0; i < params->n; i++) {
+	modelConv->modelLimits (PS_MINIMIZE_PARAM_MIN, i, params->data.F32, NULL);
+	modelConv->modelLimits (PS_MINIMIZE_PARAM_MAX, i, params->data.F32, NULL);
+    }
+
+    // set up the minimization process
+    psMinimization *myMin = psMinimizationAlloc (PM_SOURCE_FIT_MODEL_NUM_ITERATIONS, PM_SOURCE_FIT_MODEL_TOLERANCE);
+
+    psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
+
+    bool fitStatus = psphotModelWithPSF_LMM (myMin, covar, params, constraint, source, psf, modelConv->modelFunc);
+    for (int i = 0; i < dparams->n; i++) {
+        if (psTraceGetLevel("psphot") >= 4) {
+            fprintf (stderr, "%f ", params->data.F32[i]);
+        }
+        if ((constraint->paramMask != NULL) && constraint->paramMask->data.U8[i])
+            continue;
+        dparams->data.F32[i] = sqrt(covar->data.F32[i][i]);
+    }
+    psTrace ("psphot", 4, "niter: %d, chisq: %f", myMin->iter, myMin->value);
+
+    // renormalize output model image (generated by fitting process)
+    float Io = params->data.F32[PM_PAR_I0];
+    for (int iy = 0; iy < source->modelFlux->numRows; iy++) {
+	for (int ix = 0; ix < source->modelFlux->numCols; ix++) {
+	    source->modelFlux->data.F32[iy][ix] /= Io;
+	}
+    }
+
+    // save the resulting chisq, nDOF, nIter
+    modelConv->chisq = myMin->value;
+    modelConv->nIter = myMin->iter;
+
+    // XXX I actually need to count the number of unmasked pixels here
+    modelConv->nDOF  = source->pixels->numCols*source->pixels->numRows  -  nParams;
+
+    modelConv->flags |= PM_MODEL_STATUS_FITTED;
+    if (!fitStatus) modelConv->flags |= PM_MODEL_STATUS_NONCONVERGE;
+
+    // models can go insane: reject these
+    bool onPic = true;
+    onPic &= (params->data.F32[PM_PAR_XPOS] >= source->pixels->col0);
+    onPic &= (params->data.F32[PM_PAR_XPOS] <  source->pixels->col0 + source->pixels->numCols);
+    onPic &= (params->data.F32[PM_PAR_YPOS] >= source->pixels->row0);
+    onPic &= (params->data.F32[PM_PAR_YPOS] <  source->pixels->row0 + source->pixels->numRows);
+    if (!onPic) modelConv->flags |= PM_MODEL_STATUS_OFFIMAGE;
+
+    source->mode |= PM_SOURCE_MODE_FITTED; // XXX is this needed?
+
+    psFree(psf);
+    psFree(myMin);
+    psFree(covar);
+    psFree(constraint);
+
+    return modelConv;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotParseCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotParseCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotParseCamera.c	(revision 22322)
@@ -0,0 +1,67 @@
+# include "psphotStandAlone.h"
+
+// define the needed / desired I/O files
+bool psphotParseCamera (pmConfig *config) {
+
+    bool status = false;
+
+    // the file to be loaded may have subdivisions at the cell and readout level
+    // we load into pmFPAfile *load, then reformat into pmFPAfile *input
+    pmFPAfile *load = pmFPAfileDefineFromArgs (&status, config, "PSPHOT.LOAD", "INPUT");
+    if (!status) {
+        psError(PSPHOT_ERR_CONFIG, false, "Failed to build FPA from PSPHOT.LOAD");
+        return status;
+    }
+    load->dataLevel = PM_FPA_LEVEL_CHIP; // force load at the CHIP level
+
+    // if MASK or WEIGHT was supplied on command line, bind files to 'load'
+    // the mask and weight will be mosaicked with the image
+    pmFPAfileBindFromArgs (&status, load, config, "PSPHOT.MASK", "MASK");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+        return NULL;
+    }
+    if (!psphotSetMaskBits (config)) {
+        psError (PS_ERR_UNKNOWN, false, "failed to set mask bit values");
+        return NULL;
+    }
+
+    pmFPAfileBindFromArgs (&status, load, config, "PSPHOT.WEIGHT", "WEIGHT");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+        return NULL;
+    }
+
+    // the psphot analysis is performed on chips
+    pmFPAfile *input = pmFPAfileDefineChipMosaic(config, load->fpa, "PSPHOT.INPUT");
+    if (!input) {
+        psError(PSPHOT_ERR_CONFIG, false, _("Unable to generate new file from PSPHOT.INPUT"));
+        return NULL;
+    }
+
+    // define the additional input/output files associated with psphot
+    if (!psphotDefineFiles (config, input)) {
+        psError(PSPHOT_ERR_CONFIG, false, "Trouble defining the additional input/output files");
+        return false;
+    }
+
+    // Chip selection: turn on only the chips specified (pass status to suppress missing-key log msg)
+    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS");
+    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    if (chips->n > 0) {
+        // select on the basis of extname?
+        pmFPASelectChip (load->fpa, -1, true); // deselect all chips
+        for (int i = 0; i < chips->n; i++) {
+            int chipNum = atoi(chips->data[i]);
+            if (! pmFPASelectChip(load->fpa, chipNum, false)) {
+                psError(PSPHOT_ERR_CONFIG, false, "Chip number %d doesn't exist in camera.\n", chipNum);
+                return false;
+            }
+        }
+    }
+    psFree (chips);
+    psTrace("psphot", 1, "Done with psphotParseCamera...\n");
+
+    psErrorClear();                     // some metadata lookup may have failed
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotPetrosian.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotPetrosian.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotPetrosian.c	(revision 22322)
@@ -0,0 +1,109 @@
+# include "psphotInternal.h"
+
+bool psphotPetrosian (pmSource *source, psMetadata *recipe, psMaskType maskVal) {
+
+  bool status;
+
+  assert (source->extpars);
+  assert (source->extpars->profile);
+  assert (source->extpars->profile->radius);
+  assert (source->extpars->profile->flux);
+
+  psVector *radius = source->extpars->profile->radius;
+  psVector *flux = source->extpars->profile->flux;
+
+  // flux at which to measure isophotal parameters
+  float PETROSIAN_R0 = psMetadataLookupF32 (&status, recipe, "PETROSIAN_R0");
+  float PETROSIAN_RF = psMetadataLookupF32 (&status, recipe, "PETROSIAN_FLUX_RATIO");
+  assert (status);
+
+  // first find flux at R0
+  int firstAbove = -1;
+  int lastBelow = -1;
+  for (int i = 0; i < radius->n; i++) {
+    if (radius->data.F32[i] < PETROSIAN_R0) lastBelow = i;
+    if ((firstAbove < 0) && (radius->data.F32[i] > PETROSIAN_R0)) firstAbove = i;
+  }
+  // if we don't go out far enough, we have a problem...
+  if (lastBelow == radius->n - 1) {
+    psTrace ("psphot", 5, "did not go out far enough to reach petrosian reference radius...");
+    // XXX skip object? raise a flag ?
+    return false;
+  }
+  if (firstAbove < 0) {
+    psTrace ("psphot", 5, "did not go out far enough to bound petrosian reference radius");
+    // XXX raise a flag ?
+    return false;
+  }
+
+  // average flux in this range
+  float fluxR0 = 0.0;
+  int fluxRn = 0;
+  for (int i = PS_MIN(firstAbove, lastBelow); i <= PS_MAX(firstAbove, lastBelow); i++) {
+    fluxR0 += flux->data.F32[i];
+    fluxRn ++;
+  }
+  fluxR0 /= (float)(fluxRn);
+
+  // target flux for petrosian radius
+  float fluxRP = fluxR0 * PETROSIAN_RF;
+
+  // find the first bin below the flux level and the last above the level
+  // XXX can this be done faster with bisection?
+  // XXX do I need to worry about crazy outliers?
+  // XXX should i be smoothing or fitting the curve?
+  int firstBelow = -1;
+  int lastAbove = -1;
+  for (int i = 0; i < flux->n; i++) {
+    if (flux->data.F32[i] > fluxRP) lastAbove = i;
+    if ((firstBelow < 0) && (flux->data.F32[i] < fluxRP)) firstBelow = i;
+  }
+  // if we don't go out far enough, we have a problem...
+  if (lastAbove == radius->n - 1) {
+    psTrace ("psphot", 5, "did not go out far enough to reach petrosian radius...");
+    // XXX skip object? raise a flag ?
+    return false;
+  }
+  if (firstBelow < 0) {
+    psTrace ("psphot", 5, "did not go out far enough to bound petrosian radius");
+    // XXX raise a flag ?
+    return false;
+  }
+
+  // need to examine pixels in this vicinity
+  float fluxFirst = 0;
+  float fluxLast = 0;
+  for (int i = 0; i <= PS_MAX(firstBelow, lastAbove); i++) {
+    if (i <= firstBelow) {
+      fluxFirst += flux->data.F32[i];
+    }
+    if (i <= lastAbove) {
+      fluxLast += flux->data.F32[i];
+    }
+  }
+  float fluxRPSum    = 0.5*(fluxLast + fluxFirst);
+  float fluxRPSumErr = 0.5*fabs(fluxLast - fluxFirst);
+  // XXX need to use the weight appropriately here...
+
+  float rad     = 0.5*(radius->data.F32[firstBelow] + radius->data.F32[lastAbove]);
+  float radErr  = 0.5*fabs(radius->data.F32[firstBelow] - radius->data.F32[lastAbove]);
+
+  if (!source->extpars->petrosian) {
+    source->extpars->petrosian = pmSourcePetrosianValuesAlloc ();
+  }
+
+  // these are uncalibrated: instrumental mags and pixel units
+  source->extpars->petrosian->mag    = -2.5*log10(fluxRPSum);
+  source->extpars->petrosian->magErr = fluxRPSumErr / fluxRPSum;
+
+  source->extpars->petrosian->rad    = rad;
+  source->extpars->petrosian->radErr = radErr;
+
+  psTrace ("psphot", 5, "Petrosian flux:%f +/- %f @ %f +/- %f for %f, %f\n",
+           source->extpars->petrosian->mag, source->extpars->petrosian->magErr,
+           source->extpars->petrosian->rad, source->extpars->petrosian->radErr,
+           source->peak->xf, source->peak->yf);
+
+  return true;
+
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotRadialPlot.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotRadialPlot.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotRadialPlot.c	(revision 22322)
@@ -0,0 +1,118 @@
+# include "psphotInternal.h"
+
+// this variable is defined in psmodules.h if ohana-config is found
+# if (HAVE_KAPA)
+
+# include <kapa.h>
+
+static int nCount = 0;
+
+bool psphotRadialPlot (int *kapa, const char *filename, pmSource *source) {
+
+    Graphdata graphdata;
+
+    // only plot 50 stars for now...
+    if (nCount > 00) {
+	if (*kapa != 0) {
+	    KiiClose (*kapa);
+	    *kapa = 0;
+	}
+	return true;
+    }
+
+    // XXX get the 'showWindow' option from the recipes somewhere
+    // XXX 'showWindow = false' is broken
+    if (*kapa == 0) {
+	*kapa = pmKapaOpen (false);
+	KapaResize (*kapa, 500, 500);
+	unlink (filename);
+    }
+    if (*kapa == -1) {
+	psError(PSPHOT_ERR_UNKNOWN, true, "failure to open kapa");
+	return false;
+    }
+
+    KapaInitGraph (&graphdata);
+    KapaClearPlots (*kapa);
+
+    // examine sources to set data range
+    graphdata.xmin =  -0.05;
+    graphdata.xmax = +30.05;
+    graphdata.ymin = -0.05;
+    graphdata.ymax = +5.05;
+    KapaSetLimits (*kapa, &graphdata);
+  
+    KapaSetFont (*kapa, "helvetica", 14);
+    KapaBox (*kapa, &graphdata);
+    KapaSendLabel (*kapa, "radius (pixels)", KAPA_LABEL_XM);
+    KapaSendLabel (*kapa, "log flux (counts)", KAPA_LABEL_YM);
+	       
+    int nPts = source->pixels->numRows * source->pixels->numCols;
+    psVector *rg = psVectorAllocEmpty (nPts, PS_TYPE_F32);
+    psVector *fg = psVectorAllocEmpty (nPts, PS_TYPE_F32);
+    psVector *rb = psVectorAllocEmpty (nPts, PS_TYPE_F32);
+    psVector *fb = psVectorAllocEmpty (nPts, PS_TYPE_F32);
+
+    int ng = 0;
+    int nb = 0;
+    float Xo = source->modelPSF->params->data.F32[PM_PAR_XPOS] - source->pixels->col0;
+    float Yo = source->modelPSF->params->data.F32[PM_PAR_YPOS] - source->pixels->row0;
+    for (int iy = 0; iy < source->pixels->numRows; iy++) {
+	for (int ix = 0; ix < source->pixels->numCols; ix++) {
+	    if (source->maskObj->data.U8[iy][ix]) {
+		rb->data.F32[nb] = hypot (ix - Xo, iy - Yo) ;
+		fb->data.F32[nb] = log10(source->pixels->data.F32[iy][ix]);
+		nb++;
+	    } else {
+		rg->data.F32[ng] = hypot (ix - Xo, iy - Yo) ;
+		fg->data.F32[ng] = log10(source->pixels->data.F32[iy][ix]);
+		ng++;
+	    }
+	}
+    }
+  
+    // set the plot range here based on lflux, radius
+
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 2;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+    KapaPrepPlot (*kapa, ng, &graphdata);
+    KapaPlotVector (*kapa, ng, rg->data.F32, "x");
+    KapaPlotVector (*kapa, ng, fg->data.F32, "y");
+
+    graphdata.color = KapaColorByName ("red");
+    graphdata.ptype = 0;
+    graphdata.size = 0.3;
+    graphdata.style = 2;
+    KapaPrepPlot (*kapa, nb, &graphdata);
+    KapaPlotVector (*kapa, nb, rb->data.F32, "x");
+    KapaPlotVector (*kapa, nb, fb->data.F32, "y");
+  
+    psLogMsg ("psphot", 3, "saving plot to %s", filename);
+
+    char pagename[16];
+    sprintf (pagename, "%02d", nCount);
+    if (nCount == 0) {
+	KiiPS (*kapa, filename, false, KAPA_PS_NEWPLOT, pagename);
+    } else {
+	KiiPS (*kapa, filename, false, KAPA_PS_NEWPAGE, pagename);
+    }
+
+    psFree (rg);
+    psFree (fg);
+    psFree (rb);
+    psFree (fb);
+
+    nCount ++;
+    return true;
+}
+
+# else
+
+bool psphotRadialPlot (int *kapa, const char *filename, pmSource *source) {
+    psLogMsg ("psphot", 3, "skipping source radial plots");
+    return true;
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotRadialProfile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotRadialProfile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotRadialProfile.c	(revision 22322)
@@ -0,0 +1,71 @@
+# include "psphotInternal.h"
+
+# define COMPARE_RADIUS(A,B) (radius->data.F32[A] < radius->data.F32[B])
+# define SWAP_RADIUS(TYPE,A,B) { \
+  float tmp; \
+  if (A != B) { \
+    tmp = radius->data.F32[A]; \
+    radius->data.F32[A] = radius->data.F32[B]; \
+    radius->data.F32[B] = tmp; \
+    tmp = flux->data.F32[A]; \
+    flux->data.F32[A] = flux->data.F32[B]; \
+    flux->data.F32[B] = tmp; \
+    tmp = weight->data.F32[A]; \
+    weight->data.F32[A] = weight->data.F32[B]; \
+    weight->data.F32[B] = tmp; \
+  } \
+}
+
+bool psphotRadialProfile (pmSource *source, psMetadata *recipe, psMaskType maskVal) {
+
+    // allocate pmSourceExtendedParameters, if not already defined
+    if (!source->extpars) {
+        source->extpars = pmSourceExtendedParsAlloc ();
+    }
+
+    if (!source->extpars->profile) {
+        source->extpars->profile = pmSourceRadialProfileAlloc ();
+    }
+
+    int nPts = source->pixels->numRows * source->pixels->numCols;
+    source->extpars->profile->radius = psVectorAllocEmpty (nPts, PS_TYPE_F32);
+    source->extpars->profile->flux   = psVectorAllocEmpty (nPts, PS_TYPE_F32);
+    source->extpars->profile->weight = psVectorAllocEmpty (nPts, PS_TYPE_F32);
+
+    psVector *radius = source->extpars->profile->radius;
+    psVector *flux   = source->extpars->profile->flux;
+    psVector *weight = source->extpars->profile->weight;
+
+    // XXX use the extended source model here for Xo, Yo?
+    // XXX define a radius scaled to the elliptical contour?
+
+    int n = 0;
+    
+    float Xo = 0.0;
+    float Yo = 0.0;
+
+    if (source->modelEXT) {
+      Xo = source->modelEXT->params->data.F32[PM_PAR_XPOS] - source->pixels->col0;
+      Yo = source->modelEXT->params->data.F32[PM_PAR_YPOS] - source->pixels->row0;
+    } else {
+      Xo = source->peak->xf - source->pixels->col0;
+      Yo = source->peak->yf - source->pixels->row0;
+    }
+    for (int iy = 0; iy < source->pixels->numRows; iy++) {
+        for (int ix = 0; ix < source->pixels->numCols; ix++) {
+            if (source->maskObj->data.U8[iy][ix]) continue;
+            radius->data.F32[n] = hypot (ix - Xo, iy - Yo) ;
+            flux->data.F32[n]   = source->pixels->data.F32[iy][ix];
+            weight->data.F32[n] = source->weight->data.F32[iy][ix];
+            n++;
+        }
+    }
+    radius->n = n;
+    weight->n = n;
+    flux->n = n;
+
+    // sort the vector set by the radius
+    PSSORT (radius->n, COMPARE_RADIUS, SWAP_RADIUS, NONE);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotRadiusChecks.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotRadiusChecks.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotRadiusChecks.c	(revision 22322)
@@ -0,0 +1,107 @@
+# include "psphotInternal.h"
+# define RADIUS_TYPE int
+
+static float PSF_FIT_NSIGMA;
+static float PSF_FIT_PADDING;
+static float PSF_FIT_RADIUS = 0;	// radius to use in fitting (ignored if <= 0,
+					// and a per-object radius is calculated)
+
+bool psphotInitRadiusPSF(const psMetadata *recipe, const pmModelType type) {
+
+    bool status = true;
+
+    PSF_FIT_NSIGMA  = psMetadataLookupF32(&status, recipe, "PSF_FIT_NSIGMA");
+    PSF_FIT_PADDING = psMetadataLookupF32(&status, recipe, "PSF_FIT_PADDING");
+    PSF_FIT_RADIUS =  psMetadataLookupF32(&status, recipe, "PSF_FIT_RADIUS");
+
+    return true;
+}
+
+// call this function whenever you (re)-define the PSF model
+bool psphotCheckRadiusPSF (pmReadout *readout, pmSource *source, pmModel *model, psMaskType markVal)
+{
+    psF32 *PAR = model->params->data.F32;
+
+    // XXX do we have a better value for the sky noise level?  not really...
+    pmMoments *moments = source->moments;
+
+    // set the fit radius based on the object flux limit and the model
+    float radiusFit = PSF_FIT_RADIUS;
+    if (radiusFit <= 0) {		// use fixed radius
+	if (moments == NULL) {
+	    radiusFit = model->modelRadius(model->params, PSF_FIT_NSIGMA*moments->dSky);
+	} else {
+	    radiusFit = model->modelRadius(model->params, 1.0);
+	}
+    }
+    model->radiusFit = (RADIUS_TYPE)(radiusFit + PSF_FIT_PADDING);
+
+    if (isnan(model->radiusFit)) psAbort("error in radius");
+	
+    if (source->mode & PM_SOURCE_MODE_SATSTAR) {
+	model->radiusFit *= 2;
+    }
+
+    bool status = pmSourceRedefinePixels (source, readout, PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], model->radiusFit);
+
+    // set the mask to flag the excluded pixels
+    psImageKeepCircle (source->maskObj, PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], model->radiusFit, "OR", markVal);
+    return status;
+}
+
+bool psphotCheckRadiusPSFBlend (pmReadout *readout, pmSource *source, pmModel *model, psMaskType markVal, float dR) {
+
+    psF32 *PAR = model->params->data.F32;
+
+    pmMoments *moments = source->moments;
+    if (moments == NULL) return false;
+
+    // set the fit radius based on the object flux limit and the model
+    model->radiusFit = (RADIUS_TYPE) (model->modelRadius (model->params, PSF_FIT_NSIGMA*moments->dSky) + dR + PSF_FIT_PADDING);
+    if (isnan(model->radiusFit)) psAbort("error in radius");
+	
+    if (source->mode &  PM_SOURCE_MODE_SATSTAR) {
+	model->radiusFit *= 2;
+    }
+
+    bool status = pmSourceRedefinePixels (source, readout, PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], model->radiusFit);
+
+    // set the mask to flag the excluded pixels
+    psImageKeepCircle (source->maskObj, PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], model->radiusFit, "OR", markVal);
+    return status;
+}
+
+static float EXT_FIT_NSIGMA;
+static float EXT_FIT_PADDING;
+
+bool psphotInitRadiusEXT (psMetadata *recipe, pmModelType type) {
+
+    bool status;
+
+    EXT_FIT_NSIGMA   = psMetadataLookupF32 (&status, recipe, "EXT_FIT_NSIGMA");
+    EXT_FIT_PADDING  = psMetadataLookupF32 (&status, recipe, "EXT_FIT_PADDING");
+
+    return true;
+}
+
+// call this function whenever you (re)-define the EXT model
+bool psphotCheckRadiusEXT (pmReadout *readout, pmSource *source, pmModel *model, psMaskType markVal) {
+
+    psF32 *PAR = model->params->data.F32;
+
+    pmMoments *moments = source->moments;
+    if (moments == NULL) return false;
+
+    // set the fit radius based on the object flux limit and the model
+    float rawRadius = model->modelRadius (model->params, EXT_FIT_NSIGMA*moments->dSky);
+
+    model->radiusFit = rawRadius + EXT_FIT_PADDING;
+    if (isnan(model->radiusFit)) psAbort("error in radius");
+
+    // redefine the pixels if needed
+    bool status = pmSourceRedefinePixels (source, readout, PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], model->radiusFit);
+
+    // set the mask to flag the excluded pixels
+    psImageKeepCircle (source->maskObj, PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], model->radiusFit, "OR", markVal);
+    return status;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotReadout.c	(revision 22322)
@@ -0,0 +1,301 @@
+# include "psphotInternal.h"
+
+bool psphotReadout(pmConfig *config, const pmFPAview *view) {
+
+    // measure the total elapsed time in psphotReadout.  XXX the current threading plan
+    // for psphot envisions threading within psphotReadout, not multiple threads calling
+    // the same psphotReadout.  In the current plan, this dtime is the elapsed time used
+    // jointly by the multiple threads, not the total time used by all threads.
+    psTimerStart ("psphotReadout");
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // set the photcode for this image
+    if (!psphotAddPhotcode (recipe, config, view, "PSPHOT.INPUT")) {
+        psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
+        return false;
+    }
+
+    // find the currently selected readout
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
+    PS_ASSERT_PTR_NON_NULL (readout, false);
+
+    // optional break-point for processing
+    char *breakPt = psMetadataLookupStr (NULL, recipe, "BREAK_POINT");
+    PS_ASSERT_PTR_NON_NULL (breakPt, false);
+
+    // Generate the mask and weight images, including the user-defined analysis region of interest
+    psphotSetMaskAndWeight (config, readout, recipe);
+    if (!strcasecmp (breakPt, "NOTHING")) {
+        return psphotReadoutCleanup(config, readout, recipe, NULL, NULL, NULL);
+    }
+
+    // generate a background model (median, smoothed image)
+    if (!psphotModelBackground (config, view, "PSPHOT.INPUT")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+    if (!psphotSubtractBackground (config, view, "PSPHOT.INPUT")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+    if (!strcasecmp (breakPt, "BACKMDL")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+
+    // run a single-model test if desired (exits from here if test is run)
+    psphotModelTest (config, view, recipe);
+
+    // load the psf model, if suppled.  FWHM_X,FWHM_Y,etc are saved in the recipe
+    pmPSF *psf = psphotLoadPSF (config, view, recipe);
+
+    // several functions below behave differently if we have a PSF model already
+    bool havePSF = (psf != NULL);
+
+    // find the detections (by peak and/or footprint) in the image.
+    pmDetections *detections = psphotFindDetections (NULL, readout, recipe);
+    if (!detections) {
+        psLogMsg ("psphot", 3, "unable to find detections in this image");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, NULL);
+    }
+ 
+    // XXX test write out the footprint image
+    if (0) {
+	psImage *footprintImage = pmSetFootprintArrayIDs (detections->footprints, true);
+	psphotSaveImage (NULL, footprintImage, "footprints.1.fits");
+	psFree (footprintImage);
+    }
+
+    // XXX test write out the footprint image
+    if (0) {
+        psImage *footprintImage = pmSetFootprintArrayIDs (detections->footprints, true);
+        psphotSaveImage (NULL, footprintImage, "footprints.1.fits");
+        psFree (footprintImage);
+    }
+
+    // construct sources and measure basic stats
+    psArray *sources = psphotSourceStats (readout, recipe, detections);
+    if (!sources) return false;
+    if (!strcasecmp (breakPt, "PEAKS")) {
+        return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+    }
+
+    // find blended neighbors of very saturated stars
+    // XXX merge this with Basic Deblend?
+    psphotDeblendSatstars (sources, recipe);
+
+    // mark blended peaks PS_SOURCE_BLEND
+    if (!psphotBasicDeblend (sources, recipe)) {
+        psLogMsg ("psphot", 3, "failed on deblend analysis");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+
+    // classify sources based on moments, brightness
+    if (!psphotRoughClass (sources, recipe, havePSF)) {
+        psLogMsg ("psphot", 3, "failed to find a valid PSF clump for image");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+    if (!strcasecmp (breakPt, "MOMENTS")) {
+        return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+    }
+
+    // if we were not supplied a PSF, choose one here
+    if (psf == NULL) {
+        // use bright stellar objects to measure PSF
+        // XXX if we do not have enough stars to generate the PSF, build one
+        // from the SEEING guess and model class
+        psf = psphotChoosePSF (readout, sources, recipe);
+        if (psf == NULL) {
+            psLogMsg ("psphot", 3, "failure to construct a psf model");
+            return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+        }
+        havePSF = true;
+    }
+    if (!strcasecmp (breakPt, "PSFMODEL")) {
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+
+    // Define source fitting parameters for everything that follows PSF fits
+    // This allows different parameters to be used from the PSF fitting
+    {
+        bool status = true;             // Status of MD lookup
+        int fitIter = psMetadataLookupS32(&status, recipe, "EXT_FIT_ITER"); // Max number of fit iterations
+        if (!status || fitIter <= 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "EXT_FIT_ITER is not positive");
+            return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+        }
+        float fitTol = psMetadataLookupF32 (&status, recipe, "EXT_FIT_TOL"); // Fit tolerance
+        if (!status || !isfinite(fitTol) || fitTol <= 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "EXT_FIT_TOL is not positive");
+            return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+        }
+        bool poisson = psMetadataLookupBool(&status, recipe, "POISSON.ERRORS.PHOT.LMM"); // Poisson errors?
+        if (!status) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "POISSON.ERRORS.PHOT.LMM is not defined");
+            return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+        }
+        float skySig = psMetadataLookupF32(&status, recipe, "SKY_SIG");
+        if (!status || !isfinite(skySig) || skySig <= 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "SKY_SIG is not positive");
+            return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+        }
+        pmSourceFitModelInit(fitIter, fitTol, PS_SQR(skySig), poisson);
+    }
+
+    // include externally-supplied sources
+    psphotLoadExtSources (config, view, sources);
+
+    // construct an initial model for each object
+    psphotGuessModels (readout, sources, recipe, psf);
+
+    // XXX test output of models
+    // psphotTestSourceOutput (readout, sources, recipe, psf);
+
+    // linear PSF fit to source peaks
+    psphotFitSourcesLinear (readout, sources, recipe, psf, FALSE);
+
+    if (0) {
+        FILE *out = fopen ("out.pass1.dat", "w");
+
+        for (int i = 0; i < sources->n; i++) {
+            pmSource *source = sources->data[i];
+            if (!source) continue;
+            pmPeak *peak = source->peak;
+            if (!peak) continue;
+            pmModel *model = source->modelPSF;
+            if (!model) continue;
+            if (!model->params) continue;
+
+            fprintf (out, "%d %f %f  %f %f  %f %f\n",
+                     i, peak->xf, peak->yf, peak->flux, peak->SN,
+                     model->params->data.F32[PM_PAR_I0],
+                     model->dparams->data.F32[PM_PAR_I0]);
+        }
+        fclose (out);
+    }
+
+    // identify CRs and extended sources
+    psphotSourceSize (config, readout, sources, recipe, 0);
+    if (!strcasecmp (breakPt, "ENSEMBLE")) {
+        goto finish;
+    }
+
+    // non-linear PSF and EXT fit to brighter sources
+    psphotBlendFit (readout, sources, recipe, psf);
+
+    // replace all sources
+    psphotReplaceAllSources (sources, recipe);
+
+    // linear fit to include all sources
+    psphotFitSourcesLinear (readout, sources, recipe, psf, TRUE);
+
+    // if we only do one pass, skip to extended source analysis
+    if (!strcasecmp (breakPt, "PASS1")) {
+        goto pass1finish;
+    }
+
+    // XXX for the moment, drop the re-calc of the background (prove this works)
+    // replace background in residual image
+    // psphotSkyReplace (config, view);
+    // re-measure background model (median, smoothed image)
+    // psphotImageMedian (config, view);
+
+    // add noise for subtracted objects
+    psphotAddNoise (readout, sources, recipe);
+
+    // find fainter sources (pass 2)
+    detections = psphotFindDetections (detections, readout, recipe);
+
+    // XXX test write out the footprint image
+    if (0) {
+        psImage *footprintImage = pmSetFootprintArrayIDs (detections->footprints, true);
+        psphotSaveImage (NULL, footprintImage, "footprints.2.fits");
+        psFree (footprintImage);
+    }
+    // remove noise for subtracted objects (ie, return to normal noise level)
+    psphotSubNoise (readout, sources, recipe);
+
+    // define new sources based on only the new peaks
+    psArray *newSources = psphotSourceStats (readout, recipe, detections);
+
+    // set source type
+    if (!psphotRoughClass (newSources, recipe, havePSF)) {
+        psLogMsg ("psphot", 3, "failed to find a valid PSF clump for image");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+
+    // create full input models
+    psphotGuessModels (readout, newSources, recipe, psf);
+
+    // replace all sources so fit below applies to all at once
+    psphotReplaceAllSources (sources, recipe);
+
+    // merge the newly selected sources into the existing list
+    psphotMergeSources (sources, newSources);
+    psFree (newSources);
+
+    // linear fit to all sources
+    psphotFitSourcesLinear (readout, sources, recipe, psf, TRUE);
+
+    if (0) {
+        FILE *out = fopen ("out.pass2.dat", "w");
+
+        for (int i = 0; i < sources->n; i++) {
+            pmSource *source = sources->data[i];
+            if (!source) continue;
+            pmPeak *peak = source->peak;
+            if (!peak) continue;
+            pmModel *model = source->modelPSF;
+            if (!model) continue;
+            if (!model->params) continue;
+
+            fprintf (out, "%d %f %f  %f %f  %f %f\n",
+                     i, peak->xf, peak->yf, peak->flux, peak->SN,
+                     model->params->data.F32[PM_PAR_I0],
+                     model->dparams->data.F32[PM_PAR_I0]);
+        }
+        fclose (out);
+    }
+
+pass1finish:
+
+    // measure source size for the remaining sources
+    psphotSourceSize (config, readout, sources, recipe, 0);
+    if (0) {
+        psphotSaveImage (NULL, readout->mask, "mask.fits");
+    }
+	
+    psphotExtendedSourceAnalysis (readout, sources, recipe);
+
+    psphotExtendedSourceFits (readout, sources, recipe);
+
+finish:
+
+    // plot positive sources
+    // psphotSourcePlots (readout, sources, recipe);
+
+    // measure aperture photometry corrections
+    if (!psphotApResid (readout, sources, recipe, psf)) {
+        psLogMsg ("psphot", 3, "failed on psphotApResid");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+
+    // calculate source magnitudes
+    pmReadout *background = psphotSelectBackground (config, view, false);
+    psphotMagnitudes(sources, recipe, psf, background);
+
+    // replace failed sources?
+    // psphotReplaceUnfitSources (sources);
+
+    // replace background in residual image
+    psphotSkyReplace (config, view);
+
+    // drop the references to the image pixels held by each source
+    psphotSourceFreePixels (sources);
+
+    // create the exported-metadata and free local data
+    return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotReadoutCleanup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotReadoutCleanup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotReadoutCleanup.c	(revision 22322)
@@ -0,0 +1,88 @@
+# include "psphotInternal.h"
+
+// psphotReadoutCleanup is called on exit from psphotReadout.  If the last raised error is
+// not a DATA error, then there was a serious problem.  Only in this case, or if the fail
+// on the stats measurement, do we return false
+bool psphotReadoutCleanup (pmConfig *config, pmReadout *readout, psMetadata *recipe, pmDetections *detections, pmPSF *psf, psArray *sources) {
+
+    // remove internal pmFPAfiles, if created
+    bool status = true;
+    status &= pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL");
+    status &= pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL.STDEV");
+    status &= pmFPAfileDropInternal (config->files, "PSPHOT.BACKGND");
+    if (!status) {
+	psError(PSPHOT_ERR_PROG, false, "trouble dropping internal files");
+	psFree (psf);
+	psFree (sources);
+	psFree (detections);
+	return false;
+    }
+
+    if (psErrorCodeLast() == PSPHOT_ERR_DATA) {
+        psErrorStackPrint(stderr, "Error in the psphot readout analysis");
+	psErrorClear();
+    } 
+    if (psErrorCodeLast() != PS_ERR_NONE) { 
+	psFree (psf);
+	psFree (sources);
+	psFree (detections);
+	return false;
+    }
+
+    // use the psf-model to measure FWHM stats
+    if (psf) {
+        if (!psphotPSFstats (readout, recipe, psf)) {
+            psError(PSPHOT_ERR_PROG, false, "Failed to measure PSF shape parameters");
+	    psFree (psf);
+	    psFree (sources);
+	    psFree (detections);
+            return false;
+        }
+    }
+    // otherwise, use the source moments to measure FWHM stats
+    if (!psf && sources) {
+        if (!psphotMomentsStats (readout, recipe, sources)) {
+            psError(PSPHOT_ERR_PROG, false, "Failed to measure Moment shape parameters");
+	    psFree (psf);
+	    psFree (sources);
+	    psFree (detections);
+            return false;
+        }
+    }
+
+    // write NSTARS to the image header
+    psphotSetHeaderNstars (recipe, sources);
+
+    // create an output header with stats results
+    psMetadata *header = psphotDefineHeader (recipe);
+
+    // save the results of the analysis
+    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.HEADER",  PS_DATA_METADATA, "header stats", header);
+    if (sources) {
+	psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY, "psphot sources", sources);
+    }
+    if (psf) {
+	// save the psf for possible output.  if there was already an entry, it was loaded from external sources
+	// the new one may have been updated or modified, so replace the existing entry.  We
+	// are required to save it on the chip, but this will cause problems if we ever want to
+	// run psphot on an unmosaiced image
+	pmCell *cell = readout->parent;
+	pmChip *chip = cell->parent;
+	psMetadataAdd (chip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN | PS_META_REPLACE,  "psphot psf", psf);
+    }
+
+    if (psErrorCodeLast() != PS_ERR_NONE) {
+        psErrorStackPrint(stderr, "unexpected remaining errors");
+	abort();
+    }
+
+    // XXX move this to top of loop?
+    pmKapaClose ();
+
+    psFree (detections);
+    psFree (psf);
+    psFree (header);
+    psFree (sources);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotReplaceUnfit.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotReplaceUnfit.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotReplaceUnfit.c	(revision 22322)
@@ -0,0 +1,115 @@
+# include "psphotInternal.h"
+
+// replace the flux for sources which failed
+bool psphotReplaceUnfitSources (psArray *sources, psMaskType maskVal) {
+
+    pmSource *source;
+
+    psTimerStart ("psphot");
+
+    for (int i = 0; i < sources->n; i++) {
+      source = sources->data[i];
+
+      // replace other sources?
+      if (source->mode & PM_SOURCE_MODE_FAIL) goto replace;
+      continue;
+
+    replace:
+        pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+        source->mode &= ~PM_SOURCE_MODE_SUBTRACTED;
+    }
+    psLogMsg ("psphot.replace", 3, "replace unfitted models: %f sec (%ld objects)\n", psTimerMark ("psphot"), sources->n);
+    return true;
+}
+
+bool psphotReplaceAllSources (psArray *sources, psMetadata *recipe) {
+
+    bool status;
+    pmSource *source;
+
+    psTimerStart ("psphot");
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    for (int i = 0; i < sources->n; i++) {
+      source = sources->data[i];
+
+      // replace other sources?
+      if (!(source->mode & PM_SOURCE_MODE_SUBTRACTED)) continue;
+
+      pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+      source->mode &= ~PM_SOURCE_MODE_SUBTRACTED;
+    }
+    psLogMsg ("psphot.replace", PS_LOG_INFO, "replaced models for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot"));
+    return true;
+}
+
+bool psphotRemoveAllSources (psArray *sources, psMetadata *recipe) {
+
+    bool status;
+    pmSource *source;
+
+    psTimerStart ("psphot");
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    for (int i = 0; i < sources->n; i++) {
+      source = sources->data[i];
+
+      // replace other sources?
+      if (source->mode & PM_SOURCE_MODE_SUBTRACTED) continue;
+
+      pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+      source->mode |= PM_SOURCE_MODE_SUBTRACTED;
+    }
+    psLogMsg ("psphot.replace", PS_LOG_INFO, "replaced models for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot"));
+    return true;
+}
+
+// add source, if the source has been subtracted (or if we ignore the state)
+bool psphotAddWithTest (pmSource *source, bool useState, psMaskType maskVal) {
+
+    // what is current state? (true : add; false : sub)
+    bool state = !(source->mode & PM_SOURCE_MODE_SUBTRACTED);
+    if (state && useState) return true;
+
+    // replace the model if 1) state says it is missing or 2) useState is false (just do it)
+    if (!state || !useState) {
+        pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+    }
+    return true;
+}
+
+// sub source, if the source has been added (or if we ignore the state)
+bool psphotSubWithTest (pmSource *source, bool useState, psMaskType maskVal) {
+
+    // what is current state? (true : sub; false : add)
+    bool state = (source->mode & PM_SOURCE_MODE_SUBTRACTED);
+    if (state && useState) return true;
+
+    // replace the model if 1) state says it is missing or 2) useState is false (just do it)
+    if (!state || !useState) {
+        pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+    }
+    return true;
+}
+
+// add or sub source replace or if the source has
+bool psphotSetState (pmSource *source, bool curState, psMaskType maskVal) {
+
+    // what is desired state? (true : add; false : sub)
+    bool newState = !(source->mode & PM_SOURCE_MODE_SUBTRACTED);
+    if (curState == newState) return true;
+
+    if (curState && !newState) {
+        pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+    }
+    if (newState && !curState) {
+        pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+    }
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotRoughClass.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotRoughClass.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotRoughClass.c	(revision 22322)
@@ -0,0 +1,60 @@
+# include "psphotInternal.h"
+
+# define CHECK_STATUS(S,MSG) { \
+  if (!status) { \
+    psError(PSPHOT_ERR_CONFIG, false, "missing PSF Clump entry: %s\n", MSG); \
+    return false; \
+  } }
+
+// 2006.02.02 : no leaks
+bool psphotRoughClass (psArray *sources, psMetadata *recipe, const bool havePSF) {
+
+    bool status;
+    static pmPSFClump psfClump;
+
+    psTimerStart ("psphot");
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskSat = psMetadataLookupU8(&status, recipe, "MASK.SAT"); // Mask value for bad pixels
+    assert (maskSat);
+
+    if (!havePSF) {
+        // determine the PSF parameters from the source moment values
+        psfClump = pmSourcePSFClump (sources, recipe);
+	psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
+	psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.Y",  PS_META_REPLACE, "psf clump center", psfClump.Y);
+	psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
+	psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
+    } else {
+        // pull FWHM_X,Y from the recipe, use to define psfClump.X,Y
+        psfClump.X  = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.X");  CHECK_STATUS (status, "PSF.CLUMP.X");
+        psfClump.Y  = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.Y");  CHECK_STATUS (status, "PSF.CLUMP.Y");
+        psfClump.dX = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.DX");  CHECK_STATUS (status, "PSF.CLUMP.DX");
+        psfClump.dY = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.DY");  CHECK_STATUS (status, "PSF.CLUMP.DY");
+    }
+
+    if (psfClump.X < 0) {
+        psError(PSPHOT_ERR_PROG, false, "programming error calling pmSourcePSFClump");
+        return false;
+    }
+    if (!psfClump.X || !psfClump.Y) {
+        psError(PSPHOT_ERR_DATA, true, "Failed to find a valid PSF clump");
+        return false;
+    }
+    psLogMsg ("psphot", 3, "psf clump  X,  Y: %f, %f\n", psfClump.X, psfClump.Y);
+    psLogMsg ("psphot", 3, "psf clump DX, DY: %f, %f\n", psfClump.dX, psfClump.dY);
+
+    // group into STAR, COSMIC, EXTENDED, SATURATED, etc.
+    if (!pmSourceRoughClass (sources, recipe, psfClump, maskSat)) {
+        psError(PSPHOT_ERR_PROG, false, "programming error calling pmSourceRoughClass");
+        return false;
+    }
+
+    // optional printout of source moments only
+    psphotDumpMoments (recipe, sources);
+
+    psLogMsg ("psphot.roughclass", PS_LOG_INFO, "rough classification: %f sec\n", psTimerMark ("psphot"));
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSavePSFStars.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSavePSFStars.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSavePSFStars.c	(revision 22322)
@@ -0,0 +1,148 @@
+# include "psphotInternal.h"
+
+bool psphotSourcePlots (pmReadout *readout, psArray *sources, psMetadata *recipe) {
+
+    // bool status = false;
+    psTimerStart ("psphot");
+
+    // if this file is defined, create the necessary output data
+    pmFPAfile *file = psMetadataLookupPtr(&status, config->files, "PSPHOT.PSF.STARS");
+
+    // the source images are written to an image 10x the size of a PSF object
+    // float OUTER = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
+    // PS_ASSERT (status, false);
+
+    int DX = 21;
+    int DY = 21;
+
+    // examine PSF sources in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // counters to track the size of the image and area used in a row
+    int dX = 0;				// starting corner of next box
+    int dY = 0;				// height of row so far
+    int NX = 20*DX;			// full width of output image
+    int NY = 0;				// total height of output image
+
+    // first, examine the PSF and SAT stars:
+    // - determine bounding boxes for summary image
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *source = sources->data[i];
+
+	bool keep = false;
+        keep |= (source->mode & PM_SOURCE_MODE_PSFSTAR);
+        keep |= (source->mode & PM_SOURCE_MODE_SATSTAR);
+	if (!keep) continue;
+
+	// how does this subimage get placed into the output image?
+	// DX = source->pixels->numCols
+	// DY = source->pixels->numRows
+
+	if (dX + DX > NX) {
+	    // too wide for the rest of this row
+	    if (dX == 0) {
+		// alone on this row
+		NY += DY;
+		dX = 0;
+		dY = 0;
+	    } else {
+		// start the next row
+		NY += dY;
+		dX = DX;
+		dY = DY;
+	    }
+	} else {
+	    // extend this row
+	    dX += DX;
+	    dY = PS_MAX (dY, DY);
+	}
+    }
+
+    // allocate output image
+    psImage *outpos = psImageAlloc (NX, NY, PS_TYPE_F32);
+    psImage *outsub = psImageAlloc (NX, NY, PS_TYPE_F32);
+
+    int Xo = 0;				// starting corner of next box
+    int Yo = 0;				// starting corner of next box
+    dY = 0;				// height of row so far
+
+    int nPSF = 0;
+    int nSAT = 0;
+    int kapa = 0;			// file descriptor for plotting routine
+
+    // first, examine the PSF and SAT stars:
+    // - generate radial plots (PS plots)
+    // - create output image array
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *source = sources->data[i];
+
+	bool keep = false;
+        if (source->mode & PM_SOURCE_MODE_PSFSTAR) {
+	    nPSF ++;
+	    keep = true;
+	}
+        if (source->mode & PM_SOURCE_MODE_SATSTAR) {
+	    nSAT ++;
+	    keep = true;
+	}	    
+	if (!keep) continue;
+
+	// how does this subimage get placed into the output image?
+	// DX = source->pixels->numCols
+	// DY = source->pixels->numRows
+
+	if (Xo + DX > NX) {
+	    // too wide for the rest of this row
+	    if (Xo == 0) {
+		// place source alone on this row
+		psphotAddWithTest (source, true); // replace source if subtracted
+		psphotRadialPlot (&kapa, "radial.plots.ps", source);
+		psphotMosaicSubimage (outpos, source, Xo, Yo, DX, DY);
+
+		psphotSubWithTest (source, false); // remove source (force)
+		psphotMosaicSubimage (outsub, source, Xo, Yo, DX, DY);
+
+		psphotSetState (source, false); // replace source (has been subtracted)
+		Yo += DY;
+		Xo = 0;
+		dY = 0;
+	    } else {
+		// start the next row
+		Yo += dY;
+		Xo = 0;
+		psphotAddWithTest (source, true); // replace source if subtracted
+		psphotRadialPlot (&kapa, "radial.plots.ps", source);
+		psphotMosaicSubimage (outpos, source, Xo, Yo, DX, DY);
+
+		psphotSubWithTest (source, false); // remove source (force)
+		psphotMosaicSubimage (outsub, source, Xo, Yo, DX, DY);
+		psphotSetState (source, false); // replace source (has been subtracted)
+
+		Xo = DX;
+		dY = DY;
+	    }
+	} else {
+	    // extend this row
+	    psphotAddWithTest (source, true); // replace source if subtracted
+	    psphotRadialPlot (&kapa, "radial.plots.ps", source);
+	    psphotMosaicSubimage (outpos, source, Xo, Yo, DX, DY);
+
+	    psphotSubWithTest (source, false); // remove source (force)
+	    psphotMosaicSubimage (outsub, source, Xo, Yo, DX, DY);
+	    psphotSetState (source, false); // replace source (has been subtracted)
+
+	    Xo += DX;
+	    dY = PS_MAX (dY, DY);
+	}
+    }
+
+    psphotSaveImage (NULL, outpos, "outpos.fits");
+    psphotSaveImage (NULL, outsub, "outsub.fits");
+    psLogMsg ("psphot", PS_LOG_INFO, "plotted %d sources (%d psf, %d sat): %f sec\n", nPSF + nSAT, nPSF, nSAT, psTimerMark ("psphot"));
+
+    psFree (outpos);
+    psFree (outsub);
+    return (sources);
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSetMaskBits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSetMaskBits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSetMaskBits.c	(revision 22322)
@@ -0,0 +1,32 @@
+# include "psphotInternal.h"
+
+// XXX Should it be an error for any of these to not exist?
+
+// This function is called by the stand-alone psphot program to set the mask values in the
+// config file.  It sets the named mask values MASK.PSPHOT and MARK.PSPHOT in the PSPHOT
+// recipe.  Functions or programs which call psphotReadout as a library function must set these
+// named mask values in the PSPHOT recipe on their own.
+
+bool psphotSetMaskBits (pmConfig *config) {
+
+    psMaskType maskValue;
+    psMaskType markValue;
+
+    if (!pmConfigMaskSetBits (&maskValue, &markValue, config)) {
+	psError (PS_ERR_UNKNOWN, true, "Unable to define the mask bit values");
+	return false;
+    }
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // set maskValue and markValue in the psphot recipe
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, "MARK.PSPHOT", PS_META_REPLACE, "user-defined mask", markValue);
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "user-defined mask", maskValue);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSignificanceImage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSignificanceImage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSignificanceImage.c	(revision 22322)
@@ -0,0 +1,122 @@
+# include "psphotInternal.h"
+
+// In this function, we smooth the image and weight, then generate the significance image :
+// (S/N)^2.  If FWMH_X,Y have been recorded, use them, otherwise use PEAKS_SMOOTH_SIGMA for the
+// smoothing kernel.
+psImage *psphotSignificanceImage (pmReadout *readout, psMetadata *recipe, const int pass, psMaskType maskVal) {
+
+    float SIGMA_SMTH, NSIGMA_SMTH;
+    bool status = false;
+    bool guess = false;
+
+    // smooth the image and weight map
+    psTimerStart ("smooth");
+
+    // XXX we can a) choose fft to convolve if needed and b) multithread fftw
+
+    bool status_x, status_y;
+    float FWHM_X = psMetadataLookupF32 (&status_x, recipe, "FWHM_X");
+    float FWHM_Y = psMetadataLookupF32 (&status_y, recipe, "FWHM_Y");
+    if (status_x && status_y) {
+      // if we know the FHWM, use that to set the smoothing kernel (XXX allow an optional override?)
+      SIGMA_SMTH  = 0.5*(FWHM_X + FWHM_Y) / (2.0*sqrt(2.0*log(2.0)));
+      NSIGMA_SMTH = psMetadataLookupF32 (&status, recipe, "PEAKS_SMOOTH_NSIGMA");
+      guess = false;
+    } else {
+      // if we do not know the FWHM, use the guess smoothing kernel supplied.
+      // it is a configuration error if these are not supplied
+      SIGMA_SMTH  = psMetadataLookupF32 (&status, recipe, "PEAKS_SMOOTH_SIGMA"); 
+      PS_ASSERT (status, NULL);
+      NSIGMA_SMTH = psMetadataLookupF32 (&status, recipe, "PEAKS_SMOOTH_NSIGMA"); 
+      PS_ASSERT (status, NULL);
+      guess = true;
+    }
+    // record the actual smoothing sigma
+    psMetadataAddF32  (recipe, PS_LIST_TAIL, "SIGMA_SMOOTH", PS_META_REPLACE, "Smoothing sigma for detections", SIGMA_SMTH);
+
+    // smooth the image, applying the mask as we go
+    psImage *smooth_im = psImageCopy (NULL, readout->image, PS_TYPE_F32);
+    psImageSmoothMaskF32 (smooth_im, readout->mask, maskVal, SIGMA_SMTH, NSIGMA_SMTH);
+    psLogMsg ("psphot", PS_LOG_MINUTIA, "smooth image: %f sec\n", psTimerMark ("smooth"));
+
+    // Smooth the weight, applying the mask as we go.  The variance is smoothed by the PSF^2,
+    // renomalized to maintain the input level of the variance.  We achieve this by smoothing
+    // with a Gaussian with sigma = SIGMA_SMTH/sqrt(2) with unity normalization.  Note that
+    // this process yields a smoothed image with correlated errors.  The pixel-to-pixel
+    // variations in smooth_im will be decreased by a factor of 4*pi*SIGMA_SMTH^2, but for
+    // measurements based on apertures comparable to or larger than the smoothing kernel, the
+    // effective per-pixel variance is maintained.
+    psImage *smooth_wt = psImageCopy (NULL, readout->weight, PS_TYPE_F32);
+    psImageSmoothMaskF32 (smooth_wt, readout->mask, maskVal, SIGMA_SMTH/sqrt(2), NSIGMA_SMTH);
+    psLogMsg ("psphot", PS_LOG_MINUTIA, "smooth weight: %f sec\n", psTimerMark ("smooth"));
+
+    psImage *mask = readout->mask;
+
+    // optionally save example images under trace
+    // XXX change these to recipe value checks
+    if (psTraceGetLevel("psphot") > 5) {
+        char name[64];
+        sprintf (name, "imsmooth.v%d.fits", pass);
+        psphotSaveImage (NULL, smooth_im, name);
+        sprintf (name, "wtsmooth.v%d.fits", pass);
+        psphotSaveImage (NULL, smooth_wt, name);
+    }
+
+    // We have an input image with PSF size sigma_r.  We have smoothed it with a kernel of size
+    // sigma_s.  The result is an image with PSF size sigma_o: sigma_o^2 = sigma_r^2 +
+    // sigma_s^2.  Ideally, we are choosing sigma_s = sigma_r, in which case sigma_o^2 = 2
+    // sigma_s^2.  If we do not know the input image PSF size (initial detection stage), then
+    // we are assuming that sigma_r = sigma_s.  
+
+    // Build the significance image on top of smooth_im.  We need to correct the ratio im/wt by
+    // 2 factors: 1) the relationship the peak value and the integrated flux, and 2) the
+    // relationship between the per-pixel variance (var_i) and the total variance included in
+    // the flux measurement (effective area).  These latter correction comes from: flux = Io *
+    // 2\pi\sigma_o^2 and total variance = var_i * 4\pi\sigma_o^2, thus (S/N)^2 = flux^2 / var
+    // = (Io/var_i) \pi sigma_o^2
+
+    // in fact, because of the smoothing and the resulting correlated errors, the per-pixel
+    // varience is a bit smaller than the value predicted here.  The total variance should be
+    // \alpha 4\pi(\sigma_r_^2 + \sigma_s^2) where \alpha approaches 1 for \sigma_s / \sigma_r
+    // << 1, and \alpha approaches 0.5 for \sigma_s / \sigma_r >> 1, with a value of 0.7
+    // (actually 1/1.5) for \sigma_s = \sigma_r
+
+    // if the smoothing kernel size is known to be the psf size (\sigma_s = \sigma_r), then we
+    // can use the scaling factor of 1/1.5; otherwise, we want to be conservative and use \alpha
+    // = 0.5.  Thus, for the guess smoothing kernel, (S/N)^2 = 3.0 (Io/var_i) \pi sigma_s^2 for
+    // the guess case and (S/N)^2 = 2.0 (Io/var_i) \pi sigma_s^2 for the known case
+
+    // XXX hmm, somehow the simulations are coming up with 4pi, not 3pi (as if \alpha is 1/2.0,
+    // not 1/1.5 for sigma_s = sigma_r...
+
+    float factor = guess ? 4.0 * M_PI * PS_SQR(SIGMA_SMTH) : 4.0 * M_PI * PS_SQR(SIGMA_SMTH);
+    // record the effective area and significance scaling factor
+    float effArea = 8.0 * M_PI * PS_SQR(SIGMA_SMTH);
+    psMetadataAddF32  (recipe, PS_LIST_TAIL, "EFFECTIVE_AREA", PS_META_REPLACE, "Effective Area", effArea);
+    psMetadataAddF32  (recipe, PS_LIST_TAIL, "SIGNIFICANCE_SCALE_FACTOR", PS_META_REPLACE, "Signicance scale factor", factor);
+
+    // XXX multithread this if needed
+    for (int j = 0; j < smooth_im->numRows; j++) {
+        for (int i = 0; i < smooth_im->numCols; i++) {
+            float value = smooth_im->data.F32[j][i];
+            if (value < 0 || smooth_wt->data.F32[j][i] <= 0 || (mask->data.U8[j][i] & maskVal)) {
+                smooth_im->data.F32[j][i] = 0.0;
+            } else {
+                smooth_im->data.F32[j][i] = factor * PS_SQR(value) / smooth_wt->data.F32[j][i];
+            }
+        }
+    }
+    psLogMsg ("psphot", PS_LOG_INFO, "built smoothed signficance image: %f sec\n", psTimerMark ("smooth"));
+
+    // optionally save example images under trace
+    // XXX change these to recipe value checks
+    if (psTraceGetLevel("psphot") > 5) {
+        char name[64];
+        sprintf (name, "snsmooth.v%d.fits", pass);
+        psphotSaveImage (NULL, smooth_im, name);
+    }
+
+    psFree (smooth_wt);
+    return smooth_im;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSkyReplace.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSkyReplace.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSkyReplace.c	(revision 22322)
@@ -0,0 +1,33 @@
+# include "psphotInternal.h"
+
+// XXX make this an option?
+// in order to  successfully replace the sky, we must define a corresponding file...
+bool psphotSkyReplace (pmConfig *config, const pmFPAview *view) {
+
+    psTimerStart ("psphot");
+
+    // find the currently selected readout
+    pmReadout *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
+    if (readout == NULL) psAbort("input not defined");
+
+    // select background pixels, from output background file, or create
+    pmReadout *background = pmFPAfileThisReadout (config->files, view, "PSPHOT.BACKGND");
+    if (background == NULL) psAbort("background not defined");
+
+    // select the corresponding images
+    psF32 **image = readout->image->data.F32;
+    psU8  **mask  = readout->mask->data.U8;
+    psF32 **back  = background->image->data.F32;
+
+    // replace the background model
+    for (int j = 0; j < readout->image->numRows; j++) {
+        for (int i = 0; i < readout->image->numCols; i++) {
+            if (!mask[j][i]) {
+                image[j][i] += back[j][i];
+            }
+        }
+    }
+    psLogMsg ("psphot.sky", PS_LOG_DETAIL, "replace background flux : %f sec\n", psTimerMark ("psphot"));
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSortBySN.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSortBySN.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSortBySN.c	(revision 22322)
@@ -0,0 +1,1 @@
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceFits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceFits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceFits.c	(revision 22322)
@@ -0,0 +1,386 @@
+# include "psphotInternal.h"
+
+// given a source with an existing modelPSF, attempt a full PSF fit, subtract if successful
+// XXX this function does not call pmSourceFitModelInit : fix this?
+
+static int NfitPSF = 0;
+static int NfitBlend = 0;
+static int NfitDBL = 0;
+static int NfitEXT = 0;
+
+bool psphotFitInit () {
+    psTimerStart ("psphot.fits");
+    return true;
+}
+
+bool psphotFitSummary () {
+
+    fprintf (stderr, "fitted %5d psf, %5d blend, %5d ext, %5d dbl : %6.2f sec\n",
+             NfitPSF, NfitBlend, NfitEXT, NfitDBL, psTimerMark ("psphot.fits"));
+    return true;
+}
+
+bool psphotFitBlend (pmReadout *readout, pmSource *source, pmPSF *psf, psMaskType maskVal, psMaskType markVal) {
+
+    float x, y, dR;
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // if this source is not a possible blend, just fit as PSF
+    if ((source->blends == NULL) || (source->mode & PM_SOURCE_MODE_SATSTAR)) {
+        bool status = psphotFitPSF (readout, source, psf, maskVal, markVal);
+        return status;
+    }
+
+    // save the PSF model from the Ensemble fit
+    pmModel *PSF = pmModelCopy (source->modelPSF);
+
+    if (isnan(PSF->params->data.F32[PM_PAR_I0])) psAbort("nan in blend fit primary");
+
+    x = PSF->params->data.F32[PM_PAR_XPOS];
+    y = PSF->params->data.F32[PM_PAR_YPOS];
+
+    psArray *modelSet = psArrayAllocEmpty (source->blends->n + 1);
+    psArrayAdd (modelSet, 16, PSF);
+
+    psArray *sourceSet = psArrayAllocEmpty (source->blends->n + 1);
+    psArrayAdd (sourceSet, 16, source);
+
+    // we need to include all blends in the fit (unless primary is saturated?)
+    dR = 0;
+    for (int i = 0; i < source->blends->n; i++) {
+        pmSource *blend = source->blends->data[i];
+
+        // find the blend which is furthest from source
+        dR = PS_MAX (dR, hypot (blend->peak->xf - x, blend->peak->yf - y));
+
+        // create the model and guess parameters for this blend
+        pmModel *model = pmModelAlloc (PSF->type);
+        for (int j = 0; j < model->params->n; j++) {
+            model->params->data.F32[j] = PSF->params->data.F32[j];
+            model->dparams->data.F32[j] = PSF->dparams->data.F32[j];
+        }
+
+        // XXX assume local sky is 0.0?
+        model->params->data.F32[PM_PAR_I0] = blend->peak->flux;
+        model->params->data.F32[PM_PAR_XPOS] = blend->peak->xf;
+        model->params->data.F32[PM_PAR_YPOS] = blend->peak->yf;
+
+        // these should never be invalid values
+        // XXX drop these tests eventually
+        if (isnan(model->params->data.F32[PM_PAR_I0]))   psAbort("nan in blend fit");
+        if (isnan(model->params->data.F32[PM_PAR_XPOS])) psAbort("nan in blend fit");
+        if (isnan(model->params->data.F32[PM_PAR_YPOS])) psAbort("nan in blend fit");
+
+        // add this blend to the list
+        psArrayAdd (modelSet, 16, model);
+        psArrayAdd (sourceSet, 16, blend);
+
+        // free to avoid double counting model
+        psFree (model);
+    }
+
+    // extend source radius as needed
+    psphotCheckRadiusPSFBlend (readout, source, PSF, markVal, dR);
+
+    // fit PSF model (set/unset the pixel mask)
+    pmSourceFitSet (source, modelSet, PM_SOURCE_FIT_PSF, maskVal);
+
+    // correct model chisq for flux trend
+    double chiTrend = psPolynomial1DEval (psf->ChiTrend, PSF->params->data.F32[PM_PAR_I0]);
+    PSF->chisqNorm = PSF->chisq / chiTrend;
+
+    // evaluate the blend objects, subtract if good, free otherwise
+    for (int i = 1; i < modelSet->n; i++) {
+        pmSource *blend = sourceSet->data[i];
+        pmModel *model  = modelSet->data[i];
+
+        // correct model chisq for flux trend
+        chiTrend = psPolynomial1DEval (psf->ChiTrend, model->params->data.F32[PM_PAR_I0]);
+        model->chisqNorm = model->chisq / chiTrend;
+
+        // if this one failed, skip it
+        if (!psphotEvalPSF (blend, model)) {
+            psTrace ("psphot", 4, "failed on blend %d of %ld\n", i, modelSet->n);
+            continue;
+        }
+
+        // otherwise, supply the resulting model to the corresponding blend
+        psFree(blend->modelPSF);
+        blend->modelPSF = psMemIncrRefCounter (model);
+        psTrace ("psphot", 5, "fitted blend as PSF\n");
+
+        // build cached model and subtract
+        pmSourceCacheModel (blend, maskVal);
+        pmSourceSub (blend, PM_MODEL_OP_FULL, maskVal);
+        blend->mode |=  PM_SOURCE_MODE_SUBTRACTED;
+    }
+    NfitBlend += modelSet->n;
+
+    // evaluate the primary object
+    if (!psphotEvalPSF (source, PSF)) {
+        psTrace ("psphot", 4, "failed on blend 0 of %ld\n", modelSet->n);
+        psFree (PSF);
+        psFree (modelSet);
+        psFree (sourceSet);
+        return false;
+    }
+    psFree (modelSet);
+    psFree (sourceSet);
+
+    // save the new, successful model
+    psFree (source->modelPSF);
+    source->modelPSF = PSF;
+    psTrace ("psphot", 5, "fitted primary as PSF\n");
+
+    // build cached model and subtract
+    pmSourceCacheModel (source, maskVal);
+    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+    source->mode |=  PM_SOURCE_MODE_SUBTRACTED;
+    return true;
+}
+
+bool psphotFitPSF (pmReadout *readout, pmSource *source, pmPSF *psf, psMaskType maskVal, psMaskType markVal) {
+
+    double chiTrend;
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    NfitPSF ++;
+
+    // save the PSF model from the Ensemble fit
+    pmModel *PSF = pmModelCopy (source->modelPSF);
+    if (isnan(PSF->params->data.F32[1])) psAbort("nan in psf fit");
+
+    // extend source radius as needed
+    psphotCheckRadiusPSF (readout, source, PSF, markVal);
+
+    // fit PSF model (set/unset the pixel mask)
+    pmSourceFitModel (source, PSF, PM_SOURCE_FIT_PSF, maskVal);
+
+    // correct model chisq for flux trend
+    chiTrend = psPolynomial1DEval (psf->ChiTrend, PSF->params->data.F32[PM_PAR_I0]);
+    PSF->chisqNorm = PSF->chisq / chiTrend;
+
+    // does the PSF model succeed?
+    if (!psphotEvalPSF (source, PSF)) {
+        psFree (PSF);
+        return false;
+    }
+
+    // free old model, save new model
+    psFree (source->modelPSF);
+    source->modelPSF = PSF;
+    psTrace ("psphot", 5, "fitted as PSF\n");
+
+    // build cached model and subtract
+    pmSourceCacheModel (source, maskVal);
+    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+    source->mode |=  PM_SOURCE_MODE_SUBTRACTED;
+    return true;
+}
+
+static float EXT_MIN_SN;
+static float EXT_MOMENTS_RAD;
+static pmModelType modelTypeEXT;
+
+bool psphotInitLimitsEXT (psMetadata *recipe) {
+
+    bool status;
+
+    // extended source model parameters
+    EXT_MIN_SN       = psMetadataLookupF32 (&status, recipe, "EXT_MIN_SN");
+    EXT_MOMENTS_RAD  = psMetadataLookupF32 (&status, recipe, "EXT_MOMENTS_RADIUS");
+
+    // extended source model descriptions
+    char *modelNameEXT = psMetadataLookupStr (&status, recipe, "EXT_MODEL");
+    modelTypeEXT = pmModelClassGetType (modelNameEXT);
+    psphotInitRadiusEXT (recipe, modelTypeEXT);
+
+    return true;
+}
+
+bool psphotFitBlob (pmReadout *readout, pmSource *source, psArray *sources, pmPSF *psf, psMaskType maskVal, psMaskType markVal) {
+
+    bool okEXT, okDBL;
+    float chiEXT, chiDBL;
+    double chiTrend;
+    pmModel *ONE = NULL;
+
+    // skip the source if we don't think it is extended
+    if (source->type == PM_SOURCE_TYPE_UNKNOWN) return false;
+    if (source->type == PM_SOURCE_TYPE_DEFECT) return false;
+    if (source->type == PM_SOURCE_TYPE_SATURATED) return false;
+    if (source->moments->SN < EXT_MIN_SN) return false;
+
+    // recalculate the source moments using the larger extended-source moments radius
+    if (!pmSourceMoments (source, EXT_MOMENTS_RAD)) return false;
+
+    psTrace ("psphot", 5, "trying blob...\n");
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // this temporary source is used as a place-holder by the psphotEval functions below
+    pmSource *tmpSrc = pmSourceAlloc ();
+
+    pmModel *EXT = psphotFitEXT (readout, source, modelTypeEXT, maskVal, markVal);
+    okEXT = psphotEvalEXT (tmpSrc, EXT);
+    chiEXT = EXT->chisq / EXT->nDOF;
+
+    psArray *DBL = psphotFitDBL (readout, source, maskVal, markVal);
+    okDBL  = psphotEvalDBL (tmpSrc, DBL->data[0]);
+    okDBL &= psphotEvalDBL (tmpSrc, DBL->data[1]);
+    // XXX should I keep / save the flags set in the eval functions?
+
+    // correct first model chisqs for flux trend
+    ONE = DBL->data[0];
+    chiTrend = psPolynomial1DEval (psf->ChiTrend, ONE->params->data.F32[1]);
+    ONE->chisqNorm = ONE->chisq / chiTrend;
+
+    // save chisq for double-star/galaxy comparison
+    chiDBL = ONE->chisq / ONE->nDOF;
+
+    // correct second model chisqs for flux trend
+    ONE = DBL->data[1];
+    chiTrend = psPolynomial1DEval (psf->ChiTrend, ONE->params->data.F32[1]);
+    ONE->chisqNorm = ONE->chisq / chiTrend;
+
+    psFree (tmpSrc);
+
+    // psTraceSetLevel("psModules.objects.pmSourceFitSet", 0);
+
+    if (okEXT && okDBL) {
+        psTrace ("psphot", 5, "blob chisq: %f vs %f\n", chiEXT, chiDBL);
+        // XXX EAM : a bogus bias: need to examine this better
+        if (3*chiEXT > chiDBL) goto keepDBL;
+        goto keepEXT;
+    }
+
+    if (okEXT && !okDBL) goto keepEXT;
+    if (!okEXT && okDBL) goto keepDBL;
+
+    // both models failed; reject them both
+    psFree (EXT);
+    psFree (DBL);
+    return false;
+
+keepEXT:
+    // sub EXT
+    psFree (DBL);
+
+    // save new model
+    source->modelEXT = EXT;
+    source->type = PM_SOURCE_TYPE_EXTENDED;
+    source->mode |= PM_SOURCE_MODE_EXTMODEL;
+
+    // build cached model and subtract
+    pmSourceCacheModel (source, maskVal);
+    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+    source->mode |=  PM_SOURCE_MODE_SUBTRACTED;
+    psTrace ("psphot", 5, "blob as EXT: %f %f\n", EXT->params->data.F32[PM_PAR_XPOS], EXT->params->data.F32[PM_PAR_YPOS]);
+    return true;
+
+keepDBL:
+    // sub DLB
+    psFree (EXT);
+
+    // drop old model, save new second model...
+    psFree (source->modelPSF);
+    source->modelPSF = psMemIncrRefCounter (DBL->data[0]);
+    source->mode    |= PM_SOURCE_MODE_SUBTRACTED;
+    source->mode    |= PM_SOURCE_MODE_PAIR;
+
+    // copy most data from the primary source (modelEXT, blends stay NULL)
+    // XXX use pmSourceCopy?
+    pmSource *newSrc = pmSourceCopy (source);
+    newSrc->modelPSF = psMemIncrRefCounter (DBL->data[1]);
+
+    // build cached models and subtract
+    pmSourceCacheModel (source, maskVal);
+    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+    pmSourceCacheModel (newSrc, maskVal);
+    pmSourceSub (newSrc, PM_MODEL_OP_FULL, maskVal);
+    psTrace ("psphot", 5, "blob as DBL: %f %f\n", ONE->params->data.F32[PM_PAR_XPOS], ONE->params->data.F32[PM_PAR_YPOS]);
+
+    psArrayAdd (sources, 100, newSrc);
+    psFree (newSrc);
+    psFree (DBL);
+    return true;
+}
+
+// fit a double PSF source to an extended blob
+psArray *psphotFitDBL (pmReadout *readout, pmSource *source, psMaskType maskVal, psMaskType markVal) {
+
+    float dx, dy;
+    pmModel *DBL;
+    pmModel *PSF;
+    psEllipseAxes axes;
+    psEllipseMoments moments;
+    psArray *modelSet;
+
+    NfitDBL ++;
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // make a guess at the position of the two sources
+    moments.x2 = source->moments->Sx;
+    moments.y2 = source->moments->Sy;
+    moments.xy = source->moments->Sxy;
+    axes = psEllipseMomentsToAxes (moments, 20.0);
+
+    // XXX this is really arbitrary: 4 pixel separation?
+    dx = 2 * cos (axes.theta);
+    dy = 2 * sin (axes.theta);
+
+    // save the PSF model from the Ensemble fit
+    PSF = source->modelPSF;
+    psphotCheckRadiusPSFBlend (readout, source, PSF, markVal, 8.0);
+    if (isnan(PSF->params->data.F32[1])) psAbort("nan in dbl fit");
+
+    modelSet = psArrayAlloc (2);
+
+    DBL = pmModelCopy (PSF);
+    DBL->params->data.F32[PM_PAR_I0]  *= 0.5;
+    DBL->params->data.F32[PM_PAR_XPOS] = source->peak->xf + dx;
+    DBL->params->data.F32[PM_PAR_YPOS] = source->peak->yf + dy;
+    modelSet->data[0] = DBL;
+
+    DBL = pmModelCopy (PSF);
+    DBL->params->data.F32[PM_PAR_I0]  *= 0.5;
+    DBL->params->data.F32[PM_PAR_XPOS] = source->peak->xf - dx;
+    DBL->params->data.F32[PM_PAR_YPOS] = source->peak->yf - dy;
+    modelSet->data[1] = DBL;
+
+    // fit PSF model (set/unset the pixel mask)
+    pmSourceFitSet (source, modelSet, PM_SOURCE_FIT_PSF, maskVal);
+    return (modelSet);
+}
+
+pmModel *psphotFitEXT (pmReadout *readout, pmSource *source, pmModelType modelType, psMaskType maskVal, psMaskType markVal) {
+
+    NfitEXT ++;
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // use the source moments, etc to guess basic model parameters
+    pmModel *EXT = pmSourceModelGuess (source, modelType);
+    PS_ASSERT (EXT, NULL);
+
+    // if (isnan(EXT->params->data.F32[1])) psAbort("nan in ext fit");
+
+    psphotCheckRadiusEXT (readout, source, EXT, markVal);
+
+    if ((source->moments->Sx < 1e-3) || (source->moments->Sx < 1e-3)) {
+        psTrace ("psphot", 5, "problem source: moments: %f %f\n", source->moments->Sx, source->moments->Sy);
+    }
+
+    // fit EXT (not PSF) model (set/unset the pixel mask)
+    pmSourceFitModel (source, EXT, PM_SOURCE_FIT_EXT, maskVal);
+    return (EXT);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceFreePixels.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceFreePixels.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceFreePixels.c	(revision 22322)
@@ -0,0 +1,12 @@
+# include "psphotInternal.h"
+
+void psphotSourceFreePixels (psArray *sources) {
+
+    if (!sources) return;
+
+    for (int i = 0; i < sources->n; i++) {
+	pmSource *source = sources->data[i];
+	pmSourceFreePixels (source);
+    }
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourcePlots.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourcePlots.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourcePlots.c	(revision 22322)
@@ -0,0 +1,162 @@
+# include "psphotInternal.h"
+
+bool psphotSourcePlots (pmReadout *readout, psArray *sources, psMetadata *recipe) {
+
+    bool status = false;
+    psTimerStart ("psphot");
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    // the source images are written to an image 10x the size of a PSF object
+    // float OUTER = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
+    // PS_ASSERT (status, false);
+
+    int DX = 21;
+    int DY = 21;
+
+    // examine PSF sources in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // counters to track the size of the image and area used in a row
+    int dX = 0;                         // starting corner of next box
+    int dY = 0;                         // height of row so far
+    int NX = 20*DX;                     // full width of output image
+    int NY = DY;                        // total height of output image so far
+
+    // first, examine the PSF and SAT stars to set output image size:
+    // - add stamp widths until we exceed output image width,
+    // - then start a new row offset by max height
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *source = sources->data[i];
+
+        bool keep = false;
+        keep |= (source->mode & PM_SOURCE_MODE_PSFSTAR);
+        keep |= (source->mode & PM_SOURCE_MODE_SATSTAR);
+        if (!keep) {
+	    psTrace ("psphot", 4, "not plotting star %d: %x", i, source->mode);
+	    continue;
+	}
+
+        // how does this subimage get placed into the output image?
+        // DX = source->pixels->numCols
+        // DY = source->pixels->numRows
+
+        if (dX + DX > NX) {
+            // too wide for the rest of this row
+            if (dX == 0) {
+                // alone on this row
+                NY += DY;
+                dX = 0;
+                dY = 0;
+            } else {
+                // start the next row
+                NY += dY;
+                dX = DX;
+                dY = DY;
+            }
+        } else {
+            // extend this row
+            dX += DX;
+            dY = PS_MAX (dY, DY);
+        }
+    }
+
+    if (NY == 0) {
+	psWarning ("no PSF or SAT stars to plot? skipping.\n");
+	return false;
+    }
+
+    // allocate output image
+    psImage *outpos = psImageAlloc (NX, NY, PS_TYPE_F32);
+    psImage *outsub = psImageAlloc (NX, NY, PS_TYPE_F32);
+
+    psImageInit (outpos, 0.0);
+    psImageInit (outsub, 0.0);
+
+    int Xo = 0;                         // starting corner of next box
+    int Yo = 0;                         // starting corner of next box
+    dY = 0;                             // height of row so far
+
+    int nPSF = 0;
+    int nSAT = 0;
+    int kapa = 0;                       // file descriptor for plotting routine
+
+    // first, examine the PSF and SAT stars:
+    // - generate radial plots (PS plots)
+    // - create output image array
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *source = sources->data[i];
+
+        bool keep = false;
+        if (source->mode & PM_SOURCE_MODE_PSFSTAR) {
+            nPSF ++;
+            keep = true;
+        }
+        if (source->mode & PM_SOURCE_MODE_SATSTAR) {
+            nSAT ++;
+            keep = true;
+        }
+        if (!keep) continue;
+
+        // how does this subimage get placed into the output image?
+        // DX = source->pixels->numCols
+        // DY = source->pixels->numRows
+
+        if (Xo + DX > NX) {
+            // too wide for the rest of this row
+            if (Xo == 0) {
+                // place source alone on this row
+                psphotAddWithTest (source, true, maskVal); // replace source if subtracted
+                psphotRadialPlot (&kapa, "radial.plots.ps", source);
+                psphotMosaicSubimage (outpos, source, Xo, Yo, DX, DY);
+
+                psphotSubWithTest (source, false, maskVal); // remove source (force)
+                psphotMosaicSubimage (outsub, source, Xo, Yo, DX, DY);
+
+                psphotSetState (source, false, maskVal); // replace source (has been subtracted)
+                Yo += DY;
+                Xo = 0;
+                dY = 0;
+            } else {
+                // start the next row
+                Yo += dY;
+                Xo = 0;
+                psphotAddWithTest (source, true, maskVal); // replace source if subtracted
+                psphotRadialPlot (&kapa, "radial.plots.ps", source);
+                psphotMosaicSubimage (outpos, source, Xo, Yo, DX, DY);
+
+                psphotSubWithTest (source, false, maskVal); // remove source (force)
+                psphotMosaicSubimage (outsub, source, Xo, Yo, DX, DY);
+                psphotSetState (source, false, maskVal); // replace source (has been subtracted)
+
+                Xo = DX;
+                dY = DY;
+            }
+        } else {
+            // extend this row
+            psphotAddWithTest (source, true, maskVal); // replace source if subtracted
+            psphotRadialPlot (&kapa, "radial.plots.ps", source);
+            psphotMosaicSubimage (outpos, source, Xo, Yo, DX, DY);
+
+            psphotSubWithTest (source, false, maskVal); // remove source (force)
+            psphotMosaicSubimage (outsub, source, Xo, Yo, DX, DY);
+            psphotSetState (source, false, maskVal); // replace source (has been subtracted)
+
+            Xo += DX;
+            dY = PS_MAX (dY, DY);
+        }
+    }
+
+    psphotSaveImage (NULL, outpos, "outpos.fits");
+    psphotSaveImage (NULL, outsub, "outsub.fits");
+    psLogMsg ("psphot", PS_LOG_INFO, "plotted %d sources (%d psf, %d sat): %f sec\n", nPSF + nSAT, nPSF, nSAT, psTimerMark ("psphot"));
+
+    psFree (outpos);
+    psFree (outsub);
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceSize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceSize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceSize.c	(revision 22322)
@@ -0,0 +1,330 @@
+# include "psphotInternal.h"
+# include <gsl/gsl_sf_gamma.h>
+
+float psphotModelContour (psImage *image, psImage *weight, psImage *mask, pmModel *model, float Ro);
+
+// we need to call this function after sources have been fitted to the PSF model and
+// subtracted.  To determine the CR-nature, this function examines the 9 pixels in the 3x3
+// square containing the peak and compares the observed flux to the model.  To determine
+// the EXT-nature, this function measures the amount of positive or negative total
+// deviation from the psf model at the r = FWHM/2 position
+
+bool psphotSourceSize (pmConfig *config, pmReadout *readout, psArray *sources, psMetadata *recipe, long first)
+{
+
+    bool status;
+
+    psTimerStart ("psphot");
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    // bit to mask the cosmic-ray pixels
+    psMaskType crMask  = pmConfigMaskGet("CR", config); // Mask value for cosmic rays
+
+    float CR_NSIGMA_LIMIT = psMetadataLookupF32 (&status, recipe, "PSPHOT.CR.NSIGMA.LIMIT");
+    assert (status);
+
+    float EXT_NSIGMA_LIMIT = psMetadataLookupF32 (&status, recipe, "PSPHOT.EXT.NSIGMA.LIMIT");
+    assert (status);
+
+    int grow = psMetadataLookupS32(&status, recipe, "PSPHOT.CR.GROW"); // Growth size for CRs
+    if (!status || grow < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "PSPHOT.CR.GROW is not positive.");
+        return false;
+    }
+
+    // loop over all source
+    for (int i = first; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+
+        // skip source if it was already measured
+        if (isfinite(source->crNsigma)) continue;
+
+        // source must have been subtracted
+        if (!(source->mode & PM_SOURCE_MODE_SUBTRACTED)) continue;
+
+        psF32 **resid  = source->pixels->data.F32;
+        psF32 **weight = source->weight->data.F32;
+        psU8 **mask    = source->maskObj->data.U8;
+
+        // check for extendedness: measure the delta flux significance at the 1 sigma contour
+        source->extNsigma = psphotModelContour (source->pixels, source->weight, source->maskObj, source->modelPSF, 1.0);
+
+        // XXX prevent a source from being both CR and EXT?
+        if (source->extNsigma > EXT_NSIGMA_LIMIT) {
+          source->mode |= PM_SOURCE_MODE_EXT_LIMIT;
+        }
+
+        int xPeak = source->peak->xf - source->pixels->col0 + 0.5;
+        int yPeak = source->peak->yf - source->pixels->row0 + 0.5;
+
+        // XXX for now, skip sources which are too close to a boundary
+        // XXX raise a flag?
+        if (xPeak < 1) continue;
+        if (xPeak > source->pixels->numCols - 2) continue;
+        if (yPeak < 1) continue;
+        if (yPeak > source->pixels->numRows - 2) continue;
+
+        // XXX for now, just skip any sources with masked pixels
+        // XXX raise a flag?
+        bool keep = true;
+        for (int iy = -1; (iy <= +1) && keep; iy++) {
+            for (int ix = -1; (ix <= +1) && keep; ix++) {
+                if (mask[yPeak+iy][xPeak+ix]) { keep &= false; }
+            }
+        }
+        if (!keep) continue;
+
+        // XXX need to deal with edge peaks... and mask
+        float cX = 2*resid[yPeak][xPeak] - resid[yPeak+0][xPeak-1] - resid[yPeak+0][xPeak+1];
+        float cY = 2*resid[yPeak][xPeak] - resid[yPeak+1][xPeak+0] - resid[yPeak+1][xPeak+0];
+        float cL = 2*resid[yPeak][xPeak] - resid[yPeak-1][xPeak-1] - resid[yPeak+1][xPeak+1];
+        float cR = 2*resid[yPeak][xPeak] - resid[yPeak+1][xPeak-1] - resid[yPeak-1][xPeak+1];
+
+        float dcX = 4*weight[yPeak][xPeak] + weight[yPeak+0][xPeak-1] + weight[yPeak+0][xPeak+1];
+        float dcY = 4*weight[yPeak][xPeak] + weight[yPeak+1][xPeak+0] + weight[yPeak+1][xPeak+0];
+        float dcL = 4*weight[yPeak][xPeak] + weight[yPeak-1][xPeak-1] + weight[yPeak+1][xPeak+1];
+        float dcR = 4*weight[yPeak][xPeak] + weight[yPeak+1][xPeak-1] + weight[yPeak-1][xPeak+1];
+
+        float nX = cX / sqrt(dcX);
+        float nY = cY / sqrt(dcY);
+        float nL = cL / sqrt(dcL);
+        float nR = cR / sqrt(dcR);
+
+        // P(chisq > chisq_obs; Ndof) = gamma_Q (Ndof/2, chisq/2)
+        // Ndof = 4 ? (four measurements, no free parameters)
+        // XXX this value is going to be biased low because of systematic errors.
+        // we need to calibrate it somehow
+        // source->psfProb = gsl_sf_gamma_inc_Q (2, 0.5*chisq);
+
+        // not strictly accurate: overcounts the chisq contribution from the center pixel (by factor of 4)
+        source->psfChisq = PS_SQR (nX) + PS_SQR (nY) + PS_SQR (nX) + PS_SQR (nR);
+
+        float fCR = 0.0;
+        float fEXT = 0.0;
+        int nCR = 0;
+        int nEXT = 0;
+        if (nX > 0.0) {
+            fCR += nX;
+            nCR ++;
+        } else {
+            fEXT += nX;
+            nEXT ++;
+        }
+        if (nY > 0.0) {
+            fCR += nY;
+            nCR ++;
+        } else {
+            fEXT += nY;
+            nEXT ++;
+        }
+        if (nL > 0.0) {
+            fCR += nL;
+            nCR ++;
+        } else {
+            fEXT += nL;
+            nEXT ++;
+        }
+        if (nR > 0.0) {
+            fCR += nR;
+            nCR ++;
+        } else {
+            fEXT += nR;
+            nEXT ++;
+        }
+        source->crNsigma  = (nCR > 0)  ? fCR / nCR : 0.0;
+        // NOTE: abs needed to make the Nsigma value positive
+
+        if (!isfinite(source->crNsigma)) {
+          fprintf (stderr, ".");
+          source->crNsigma = -6.0;
+        }
+
+        // this source is thought to be a cosmic ray.  flag the detection and mask the pixels
+        if (source->crNsigma > CR_NSIGMA_LIMIT) {
+            source->mode |= PM_SOURCE_MODE_CR_LIMIT;
+            pmPeak *peak = source->peak;
+            psAssert (peak, "NULL peak");
+
+            // replace the source flux
+            pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+            source->mode &= ~PM_SOURCE_MODE_SUBTRACTED;
+
+            psImage *mask   = source->maskView;
+            psImage *pixels = source->pixels;
+            psImage *weight = source->weight;
+
+            # define SN_LIMIT 5.0
+
+            int xo = peak->x - pixels->col0;
+            int yo = peak->y - pixels->row0;
+
+            // mark the pixels in this row to the left, then the right
+            for (int ix = xo; ix >= 0; ix--) {
+                float SN = pixels->data.F32[yo][ix] / sqrt(weight->data.F32[yo][ix]);
+                if (SN > SN_LIMIT) {
+                    mask->data.U8[yo][ix] |= crMask;
+                }
+            }
+            for (int ix = xo + 1; ix < pixels->numCols; ix++) {
+                float SN = pixels->data.F32[yo][ix] / sqrt(weight->data.F32[yo][ix]);
+                if (SN > SN_LIMIT) {
+                    mask->data.U8[yo][ix] |= crMask;
+                }
+            }
+
+            // for each of the neighboring rows, mark the high pixels if they have a marked neighbor
+            // first go up:
+            for (int iy = PS_MIN(yo, mask->numRows-2); iy >= 0; iy--) {
+                // mark the pixels in this row to the left, then the right
+                for (int ix = 0; ix < pixels->numCols; ix++) {
+                    float SN = pixels->data.F32[iy][ix] / sqrt(weight->data.F32[iy][ix]);
+                    if (SN < SN_LIMIT) continue;
+
+                    bool valid = false;
+                    valid |= (mask->data.U8[iy+1][ix] & crMask);
+                    valid |= (ix > 0) ? (mask->data.U8[iy+1][ix-1] & crMask) : 0;
+                    valid |= (ix <= mask->numCols) ? (mask->data.U8[iy+1][ix+1] & crMask) : 0;
+
+                    if (!valid) continue;
+                    mask->data.U8[iy][ix] |= crMask;
+                }
+            }
+            // next go down:
+            for (int iy = PS_MIN(yo+1, mask->numRows-1); iy < pixels->numRows; iy++) {
+                // mark the pixels in this row to the left, then the right
+                for (int ix = 0; ix < pixels->numCols; ix++) {
+                    float SN = pixels->data.F32[iy][ix] / sqrt(weight->data.F32[iy][ix]);
+                    if (SN < SN_LIMIT) continue;
+
+                    bool valid = false;
+                    valid |= (mask->data.U8[iy-1][ix] & crMask);
+                    valid |= (ix > 0) ? (mask->data.U8[iy-1][ix-1] & crMask) : 0;
+                    valid |= (ix <= mask->numCols) ? (mask->data.U8[iy-1][ix+1] & crMask) : 0;
+
+                    if (!valid) continue;
+                    mask->data.U8[iy][ix] |= crMask;
+                }
+            }
+        }
+    }
+
+    if (grow > 0) {
+        psImage *newMask = psImageConvolveMask(NULL, readout->mask, crMask, crMask, -grow, grow, -grow, grow);
+        if (!newMask) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to grow CR mask");
+            return false;
+        }
+        psFree(readout->mask);
+        readout->mask = newMask;
+    }
+
+    psLogMsg ("psphot.size", PS_LOG_INFO, "measure source sizes for %ld sources: %f sec\n",
+              sources->n - first, psTimerMark ("psphot"));
+
+    return true;
+}
+
+
+// some possible classes:
+// all 4 curvatures are highly negative : CR
+// all 4 curvatures are highly positive : EXT
+
+// at least 2 are significantly negative, none are significantly positive : CR
+// at least 2 are significantly positive, none are significantly negative : EXT
+
+// any are significantly negative, some may be significantly positive : CR
+// any are significantly positive, none may be significantly positive : EXT
+
+/* Nn  Np  No
+   4   0   0   CR_1
+   3   1   0   CR_1
+   3   0   1   CR_1
+   2   2   0   CR_2
+   2   1   1   CR_2
+   2   0   2   CR_2
+   1   3   0   CR_3
+   1   2   1   CR_3
+   1   1   2   CR_3
+   1   0   3   CR_3
+   0   4   0   EXT
+   0   3   1   EXT
+   0   2   2   EXT
+   0   1   3   PSF
+   0   0   4   PSF
+*/
+
+/* Alternatively, write a f(CR) = Sum(nX,etc if >0) */
+
+
+/* I can write the formal probability that the 4 measurements are consistent with a PSF
+ * based on how large the error values are (Nsigma -> erf -> P(Nsigma)).
+ * I should examine this value as a function of flux and also examine the distribution of
+ * the probabilities for the PSF sources.
+ */
+
+
+// given the PSF ellipse parameters, navigate around the 1sigma contour, return the total
+// deviation in sigmas.  This is measure on the residual image - should we ignore negative
+// deviations?
+float psphotModelContour (psImage *image, psImage *weight, psImage *mask, pmModel *model, float Ro) {
+
+    psF32 *PAR = model->params->data.F32;
+
+    // Ro = (x / SXX)^2 + (y / SYY)^2 + x y SXY;
+    // y^2 (1/SYY^2) + y (x SXY) + (x / SXX)^2 - Ro = 0;
+    // y = [-(x SXY) +/- sqrt ((x SXY)^2 - 4 (1/SYY^2) ((x/SXX)^2 - Ro))] * [SYY^2 / 2];
+    // y = [-B +/- sqrt (B^2 - 4 A C)] / [2 A];
+
+    // min/max value of x is where T -> 0
+    // solve this for x2:
+    float Q = Ro * PS_SQR(PAR[PM_PAR_SXX]) / (1.0 - PS_SQR(PAR[PM_PAR_SXX]*PAR[PM_PAR_SYY]*PAR[PM_PAR_SXY]) / 4.0);
+    if (Q < 0.0) return NAN; // ellipse is imaginary
+
+    int xMax = sqrt(Q);
+    int xMin = -1.0*xMax;
+
+    int nPts = 0;
+    float nSigma = 0.0;
+
+    for (int x = xMin; x <= xMax; x++) {
+        float A = PS_SQR (1.0 / PAR[PM_PAR_SYY]);
+        float B = x * PAR[PM_PAR_SXY];
+        float C = PS_SQR (x / PAR[PM_PAR_SXX]) - Ro;
+
+        float T = PS_SQR(B) - 4*A*C;
+        if (T < 0.0) continue;
+
+        float yP = (-B + sqrt (T)) / (2.0 * A);
+        float yM = (-B - sqrt (T)) / (2.0 * A);
+
+        int xPix  = x  + PAR[PM_PAR_XPOS] - image->col0 + 0.5;
+        int yPixM = yM + PAR[PM_PAR_YPOS] - image->row0 + 0.5;
+        int yPixP = yP + PAR[PM_PAR_YPOS] - image->row0 + 0.5;
+
+        if (xPix < 0) continue;
+        if (xPix >= image->numCols) continue;
+
+        if ((yPixM >= 0) && (yPixM < image->numRows)) {
+            if (!mask || !mask->data.U8[yPixM][xPix]) {
+                float dSigma = image->data.F32[yPixM][xPix] / sqrt (weight->data.F32[yPixM][xPix]);
+                nSigma += dSigma;
+                nPts ++;
+            }
+        }
+
+        if (yPixM == yPixP) continue;
+
+        if ((yPixP >= 0) && (yPixP < image->numRows)) {
+            if (!mask || !mask->data.U8[yPixP][xPix]) {
+                float dSigma = image->data.F32[yPixP][xPix] / sqrt (weight->data.F32[yPixP][xPix]);
+                nSigma += dSigma;
+                nPts ++;
+            }
+        }
+    }
+    nSigma /= nPts;
+    return nSigma;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceStats.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceStats.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSourceStats.c	(revision 22322)
@@ -0,0 +1,126 @@
+# include "psphotInternal.h"
+
+psArray *psphotSourceStats (pmReadout *readout, psMetadata *recipe, pmDetections *detections)
+{
+    bool     status  = false;
+    psArray *sources = NULL;
+    float BIG_RADIUS;
+
+    psTimerStart ("psphot");
+
+    // bit-masks to test for good/bad pixels
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psMaskType markVal = psMetadataLookupU8(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // determine properties (sky, moments) of initial sources
+    float INNER    = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
+    if (!status) return NULL;
+    float OUTER    = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
+    if (!status) return NULL;
+    float RADIUS   = psMetadataLookupF32 (&status, recipe, "PSF_MOMENTS_RADIUS");
+    if (!status) return NULL;
+    float MIN_SN   = psMetadataLookupF32 (&status, recipe, "MOMENTS_SN_MIN");
+    if (!status) return NULL;
+    char *breakPt  = psMetadataLookupStr (&status, recipe, "BREAK_POINT");
+    if (!status) return NULL;
+
+    psArray *peaks = detections->peaks;
+    if (!peaks) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "No peaks found!");
+        return NULL;
+    }
+
+    sources = psArrayAllocEmpty (peaks->n);
+
+    int Nfail = 0;
+    int Nmoments = 0;
+    for (int i = 0; i < peaks->n; i++) {
+
+        pmPeak *peak = peaks->data[i];
+        if (peak->assigned) continue;
+
+        // create a new source, add peak
+        pmSource *source = pmSourceAlloc();
+        source->peak = psMemIncrRefCounter(peak);
+
+        // allocate image, weight, mask arrays for each peak (square of radius OUTER)
+        pmSourceDefinePixels (source, readout, source->peak->x, source->peak->y, OUTER);
+        if (!strcasecmp (breakPt, "PEAKS")) {
+            peak->assigned = true;
+            psArrayAdd (sources, 100, source);
+            psFree (source);
+            continue;
+        }
+
+        // skip faint sources for moments measurement
+        if (source->peak->SN < MIN_SN) {
+            peak->assigned = true;
+            psArrayAdd (sources, 100, source);
+            psFree (source);
+            continue;
+        }
+
+        // measure a local sky value
+        // the local sky is now ignored; kept here for reference only
+        status = pmSourceLocalSky (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
+        if (!status) {
+          psFree (source);
+          Nfail ++;
+          psErrorClear();
+          continue;
+        }
+
+        // measure the local sky variance (needed if noise is not sqrt(signal))
+        // XXX EAM : this should use ROBUST not SAMPLE median, but it is broken
+        status = pmSourceLocalSkyVariance (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
+        if (!status) {
+          psFree (source);
+          Nfail ++;
+          psErrorClear();
+          continue;
+        }
+
+        // measure basic source moments
+        status = pmSourceMoments (source, RADIUS);
+        if (status) {
+            // add to the source array
+            peak->assigned = true;
+            psArrayAdd (sources, 100, source);
+            psFree (source);
+            Nmoments ++;
+            continue;
+        }
+
+        // if no valid pixels, or massive swing, likely saturated source,
+        // try a much larger box
+        BIG_RADIUS = PS_MIN (INNER, 3*RADIUS);
+        psTrace ("psphot", 4, "retrying moments for %d, %d\n", source->peak->x, source->peak->y);
+        status = pmSourceMoments (source, BIG_RADIUS);
+        if (status) {
+            // add to the source array
+            peak->assigned = true;
+            psArrayAdd (sources, 100, source);
+            psFree (source);
+            Nmoments ++;
+            continue;
+        }
+
+        psFree (source);
+        Nfail ++;
+        psErrorClear();
+        continue;
+    }
+
+    psLogMsg ("psphot", PS_LOG_INFO, "%ld sources, %d moments, %d failed: %f sec\n", sources->n, Nmoments, Nfail, psTimerMark ("psphot"));
+
+    return (sources);
+}
+
+// XXX EAM : filter out bad peaks (eg, no valid pixels available for sky)
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotStandAlone.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotStandAlone.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotStandAlone.h	(revision 22322)
@@ -0,0 +1,21 @@
+# ifdef HAVE_CONFIG_H
+# include <config.h>
+# endif
+
+#ifndef PSPHOT_STAND_ALONE_H
+#define PSPHOT_STAND_ALONE_H
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "psphot.h"
+
+// Top level functions
+pmConfig       *psphotArguments (int argc, char **argv);
+bool            psphotParseCamera (pmConfig *config);
+bool            psphotImageLoop (pmConfig *config);
+bool            psphotMosaicChip(pmConfig *config, const pmFPAview *view, char *outFile, char *inFile);
+void            psphotCleanup (pmConfig *config);
+psExit          psphotGetExitStatus ();
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSubtractBackground.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSubtractBackground.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSubtractBackground.c	(revision 22322)
@@ -0,0 +1,113 @@
+# include "psphotInternal.h"
+static int npass = 0;
+
+// generate the median in NxN boxes, clipping heavily
+// linear interpolation to generate full-scale model
+bool psphotSubtractBackground (pmConfig *config, const pmFPAview *view, const char *filename) 
+{
+    bool status = true;
+    pmReadout *background = NULL;
+    pmReadout *backSub = NULL;
+
+    psTimerStart ("psphot");
+
+    // find the currently selected readout
+    pmFPAfile *file = psMetadataLookupPtr (&status, config->files, filename);
+    pmFPA *inFPA = file->fpa;
+    pmReadout *readout = pmFPAviewThisReadout (view, inFPA);
+    psImage *image = readout->image;
+    psImage *mask  = readout->mask;
+
+    // find the currently selected readout
+    pmReadout *model = pmFPAfileThisReadout (config->files, view, "PSPHOT.BACKMDL");
+    assert (model);
+
+    // select the appropriate recipe information
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+    assert (recipe);
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskVal = psMetadataLookupU8(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
+
+    psImageBinning *binning = psMetadataLookupPtr(&status, recipe, "PSPHOT.BACKGROUND.BINNING");
+    assert (binning);
+
+    // select background pixels, from output background file, or create
+    file = psMetadataLookupPtr (&status, config->files, "PSPHOT.BACKGND");
+    if (file) {
+        // we are using PSPHOT.BACKGND as an I/O file: select readout or create
+        if (file->mode == PM_FPA_MODE_INTERNAL) {
+            background = file->readout;
+        } else {
+            background = pmFPAviewThisReadout (view, file->fpa);
+        }
+        if (background == NULL) {
+            // readout does not yet exist: create from input
+            pmFPAfileCopyStructureView (file->fpa, inFPA, 1, 1, view);
+            background = pmFPAviewThisReadout (view, file->fpa);
+            if ((image->numCols != background->image->numCols) || (image->numRows != background->image->numRows)) {
+                psError (PSPHOT_ERR_PROG, true, "inconsistent sizes for background dimensions");
+                return false;
+            }
+        }
+    } else {
+        background = pmFPAfileDefineInternal (config->files, "PSPHOT.BACKGND", image->numCols, image->numRows, PS_TYPE_F32);
+    }
+    psF32 **backData = background->image->data.F32;
+
+    // linear interpolation to full-scale
+    if (!psImageUnbin (background->image, model->image, binning)) {
+        psError (PSPHOT_ERR_PROG, true, "inconsistent sizes for unbinning");
+        return false;
+    }
+
+    psLogMsg ("psphot", PS_LOG_MINUTIA, "build resampled image: %f sec\n", psTimerMark ("psphot"));
+
+    // back-sub image pixels, from output background file (don't create if not requested)
+    file = psMetadataLookupPtr (&status, config->files, "PSPHOT.BACKSUB");
+    if (file) {
+        // we are using PSPHOT.BACKSUB as an I/O file: select readout or create
+        backSub = pmFPAviewThisReadout (view, file->fpa);
+        if (backSub == NULL) {
+            // readout does not yet exist: create from input
+            pmFPAfileCopyStructureView (file->fpa, inFPA, 1, 1, view);
+            backSub = pmFPAviewThisReadout (view, file->fpa);
+        }
+    }
+
+    if (psTraceGetLevel("psphot") > 5) {
+        char name[256];
+        sprintf (name, "image.%02d.fits", npass);
+        psphotSaveImage (NULL, image, name);
+        sprintf (name, "back.%02d.fits", npass);
+        psphotSaveImage (NULL, background->image, name);
+        sprintf (name, "mask.%02d.fits", npass);
+        psphotSaveImage (NULL, mask, name);
+        sprintf (name, "backmdl.%02d.fits", npass);
+        psphotSaveImage (NULL, model->image, name);
+    }
+
+    // subtract the background model (save in backSub, if requested)
+    // XXX if needed, multithread this (fairly trivial)
+    for (int j = 0; j < image->numRows; j++) {
+        for (int i = 0; i < image->numCols; i++) {
+            image->data.F32[j][i] -= backData[j][i];
+            if (backSub) {
+                backSub->image->data.F32[j][i] = image->data.F32[j][i];
+            }
+        }
+    }
+
+    if (psTraceGetLevel("psphot") > 5) {
+        char name[256];
+        sprintf (name, "backsub.%02d.fits", npass);
+        psphotSaveImage (NULL, image, name);
+    }
+    psLogMsg ("psphot", PS_LOG_INFO, "subtracted background model: %f sec\n", psTimerMark ("psphot"));
+
+    // the pmReadout selected in this function are all view on entries in config->files
+
+    npass ++;
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotSummaryPlots.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotSummaryPlots.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotSummaryPlots.c	(revision 22322)
@@ -0,0 +1,280 @@
+# include "psphotInternal.h"
+
+// the top portion of this file defines plotting functions which use kapa for plotting.
+// if kapa is not available, these functions are defined in the bottom portion as stubs
+// which perform NOP and return false (XXX should this be true??)
+
+// this variable is defined in psmodules.h if ohana-config is found
+# if (HAVE_KAPA)
+
+# include <kapa.h>
+
+// plot the sx, sy moments plane (faint and bright sources)
+bool psphotPlotMoments (pmConfig *config, pmFPAview *view, psArray *sources) {
+
+    // select model pixels (from output background model file, or create internal file)
+    pmFPAfile *file = psMetadataLookupPtr (NULL, config->files, "PSPHOT.MOMENT.PLT");
+    if (file == NULL) {
+	psLogMsg ("psphot", 3, "skipping moments plot");
+	return false;
+    }
+
+    // pmFPAfileOpen defers disk I/O for KAPA files: just get the correct name
+    pmFPAfileOpen (file, view, config);
+
+    Graphdata graphdata;
+
+    psLogMsg ("psphot", 3, "creating moments plot");
+
+    // XXX get the 'showWindow' option from the recipes somewhere
+    int kapa = pmKapaOpen (false);
+    if (kapa == -1) {
+	psError(PSPHOT_ERR_UNKNOWN, true, "failure to open kapa");
+	return false;
+    }
+
+    KapaResize (kapa, 500, 500);
+    KapaInitGraph (&graphdata);
+
+    // examine sources to set data range
+    graphdata.xmin = -0.05;
+    graphdata.ymin = -0.05;
+    graphdata.xmax = +2.05;
+    graphdata.ymax = +2.05;
+    KapaSetLimits (kapa, &graphdata);
+  
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, &graphdata);
+    KapaSendLabel (kapa, "&ss&h_x| (pixels)", KAPA_LABEL_XM);
+    KapaSendLabel (kapa, "&ss&h_y| (pixels)", KAPA_LABEL_YM);
+	       
+    psVector *xBright = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+    psVector *yBright = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+    psVector *xFaint  = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+    psVector *yFaint  = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
+
+    // construct the vectors
+    int nB = 0;
+    int nF = 0;
+    for (int i = 0; i < sources->n; i++) {
+	pmSource *source = sources->data[i];
+	if (source->moments == NULL) continue;
+    
+	xFaint->data.F32[nF] = source->moments->Sx;
+	yFaint->data.F32[nF] = source->moments->Sy;
+	nF++;
+    
+	// XXX make this a user-defined cutoff
+	if (source->moments->SN < 25) continue;
+
+	xBright->data.F32[nB] = source->moments->Sx;
+	yBright->data.F32[nB] = source->moments->Sy;
+	nB++;
+    }
+    xFaint->n = nF;
+    yFaint->n = nF;
+
+    xBright->n = nB;
+    yBright->n = nB;
+  
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 0;
+    graphdata.size = 0.3;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, nF, &graphdata);
+    KapaPlotVector (kapa, nF, xFaint->data.F32, "x");
+    KapaPlotVector (kapa, nF, yFaint->data.F32, "y");
+  
+    graphdata.color = KapaColorByName ("red");
+    graphdata.ptype = 2;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, nB, &graphdata);
+    KapaPlotVector (kapa, nB, xBright->data.F32, "x");
+    KapaPlotVector (kapa, nB, yBright->data.F32, "y");
+
+    psLogMsg ("psphot", 3, "saving plot to %s", file->filename);
+    KapaPNG (kapa, file->filename);
+
+    psFree (xBright);
+    psFree (yBright);
+    psFree (xFaint);
+    psFree (yFaint);
+
+    return true;
+}
+
+// plot the sx, sy, sxy as vector field, 
+// plot the PSF measured sx, sy, sxy as vector field
+// pull the sources from the config / file?
+bool psphotPlotPSFModel (pmConfig *config, pmFPAview *view, psArray *sources) {
+
+    // select model pixels (from output background model file, or create internal file)
+    pmFPAfile *file = psMetadataLookupPtr (NULL, config->files, "PSPHOT.PSFMODEL.PLT");
+    if (file == NULL) {
+	psLogMsg ("psphot", 3, "skipping psf model plot");
+	return false;
+    }
+
+    // pmFPAfileOpen defers disk I/O for KAPA files: just get the correct name
+    pmFPAfileOpen (file, view, config);
+
+    Graphdata graphdata;
+
+    psLogMsg ("psphot", 3, "creating psf model plot");
+
+    int kapa = pmKapaOpen (false);
+    if (kapa == -1) {
+	psError(PSPHOT_ERR_UNKNOWN, true, "failure to open kapa");
+	return false;
+    }
+
+    // XXX make the aspect-ratio match the image
+    KapaResize (kapa, 800, 800);
+    KapaInitGraph (&graphdata);
+
+    psVector *xMNT = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+    psVector *yMNT = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+    psVector *xPSF = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+    psVector *yPSF = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+    psVector *xMIN = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+    psVector *yMIN = psVectorAllocEmpty (2*sources->n, PS_TYPE_F32);
+
+    // construct the plot vectors
+    int nMNT = 0;
+    int nPSF = 0;
+    int nMIN = 0;
+    float dx = 0;
+    float dy = 0;
+    float scale = 10;
+    for (int i = 0; i < sources->n; i++) {
+	pmSource *source = sources->data[i];
+	if (source->moments == NULL) continue;
+	if (source->moments->SN < 25) continue;
+        if (source->type != PM_SOURCE_TYPE_STAR) continue;
+    
+	pmModel *model = source->modelPSF;
+        if (model == NULL) continue;
+
+	psF32 *PAR = model->params->data.F32;
+
+	psEllipseMoments moments;
+	moments.x2 = source->moments->Sx;
+	moments.y2 = source->moments->Sy;
+	moments.xy = source->moments->Sxy;
+
+	psEllipseShape shape;
+	shape.sx  = PAR[PM_PAR_SXX] / sqrt(2.0);
+	shape.sy  = PAR[PM_PAR_SYY] / sqrt(2.0);
+	shape.sxy = PAR[PM_PAR_SXY];
+
+	// force the axis ratio to be < 20.0
+	psEllipseAxes axes_mnt = psEllipseMomentsToAxes (moments, 20.0);
+	psEllipseAxes axes_psf = psEllipseShapeToAxes (shape, 20.0);
+
+	// moments major axis
+	dx = scale*axes_mnt.major*cos(axes_mnt.theta);
+	dy = scale*axes_mnt.major*sin(axes_mnt.theta);
+	xMNT->data.F32[nMNT] = PAR[PM_PAR_XPOS] - dx;
+	yMNT->data.F32[nMNT] = PAR[PM_PAR_YPOS] - dy;
+	nMNT++;
+	xMNT->data.F32[nMNT] = PAR[PM_PAR_XPOS] + dx;
+	yMNT->data.F32[nMNT] = PAR[PM_PAR_YPOS] + dy;
+	nMNT++;
+    
+	// psf major axis
+	dx = scale*axes_psf.major*cos(axes_psf.theta);
+	dy = scale*axes_psf.major*sin(axes_psf.theta);
+	xPSF->data.F32[nPSF] = PAR[PM_PAR_XPOS] - dx;
+	yPSF->data.F32[nPSF] = PAR[PM_PAR_YPOS] - dy;
+	nPSF++;
+	xPSF->data.F32[nPSF] = PAR[PM_PAR_XPOS] + dx;
+	yPSF->data.F32[nPSF] = PAR[PM_PAR_YPOS] + dy;
+	nPSF++;
+
+	// minor axis (to show size)
+	dy = +scale*axes_psf.minor*cos(axes_psf.theta);
+	dx = -scale*axes_psf.minor*sin(axes_psf.theta);
+	xMIN->data.F32[nMIN] = PAR[PM_PAR_XPOS] - dx;
+	yMIN->data.F32[nMIN] = PAR[PM_PAR_YPOS] - dy;
+	nMIN++;
+	xMIN->data.F32[nMIN] = PAR[PM_PAR_XPOS] + dx;
+	yMIN->data.F32[nMIN] = PAR[PM_PAR_YPOS] + dy;
+	nMIN++;
+
+	graphdata.xmin = PS_MIN(graphdata.xmin, PAR[PM_PAR_XPOS]);
+	graphdata.xmax = PS_MAX(graphdata.xmax, PAR[PM_PAR_XPOS]);
+	graphdata.ymin = PS_MIN(graphdata.ymin, PAR[PM_PAR_YPOS]);
+	graphdata.ymax = PS_MAX(graphdata.ymax, PAR[PM_PAR_YPOS]);
+    }
+    xMNT->n = yMNT->n = nMNT;
+    xPSF->n = yPSF->n = nPSF;
+    xMIN->n = yMIN->n = nMIN;
+
+    float range;
+    range = graphdata.xmax - graphdata.xmin;
+    graphdata.xmax += 0.05*range;
+    graphdata.xmin -= 0.05*range;
+    range = graphdata.ymax - graphdata.ymin;
+    graphdata.ymax += 0.05*range;
+    graphdata.ymin -= 0.05*range;
+
+    // XXX set the plot range to match the image
+    KapaSetLimits (kapa, &graphdata);
+  
+    KapaSetFont (kapa, "helvetica", 14);
+    KapaBox (kapa, &graphdata);
+    KapaSendLabel (kapa, "x (pixels)", KAPA_LABEL_XM);
+    KapaSendLabel (kapa, "y (pixels)", KAPA_LABEL_YM);
+    KapaSendLabel (kapa, "vector is major axis (scale by 20) : black are moments, blue are psf model, red is psf minor axis", KAPA_LABEL_XP);
+	       
+    graphdata.color = KapaColorByName ("black");
+    graphdata.ptype = 100;
+    graphdata.size = 0.3;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, nMNT, &graphdata);
+    KapaPlotVector (kapa, nMNT, xMNT->data.F32, "x");
+    KapaPlotVector (kapa, nMNT, yMNT->data.F32, "y");
+  
+    graphdata.color = KapaColorByName ("blue");
+    graphdata.ptype = 100;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, nPSF, &graphdata);
+    KapaPlotVector (kapa, nPSF, xPSF->data.F32, "x");
+    KapaPlotVector (kapa, nPSF, yPSF->data.F32, "y");
+
+    graphdata.color = KapaColorByName ("red");
+    graphdata.ptype = 100;
+    graphdata.size = 0.5;
+    graphdata.style = 2;
+    KapaPrepPlot (kapa, nMIN, &graphdata);
+    KapaPlotVector (kapa, nMIN, xMIN->data.F32, "x");
+    KapaPlotVector (kapa, nMIN, yMIN->data.F32, "y");
+
+    psLogMsg ("psphot", 3, "saving plot to %s", file->filename);
+    KapaPNG (kapa, file->filename);
+
+    psFree (xMNT);
+    psFree (yMNT);
+    psFree (xPSF);
+    psFree (yPSF);
+    psFree (xMIN);
+    psFree (yMIN);
+
+    return true;
+}
+
+# else
+
+bool psphotPlotMoments (pmConfig *config, pmFPAview *view, psArray *sources) {
+    psLogMsg ("psphot", 3, "skipping moments plot");
+    return true;
+}
+
+bool psphotPlotPSFModel (pmConfig *config, pmFPAview *view, psArray *sources) {
+    psLogMsg ("psphot", 3, "skipping psf model plot");
+    return true;
+}
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotTest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotTest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotTest.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "psphotInternal.h"
+
+void psExit (int status, char *process, char *format, ...) {
+
+    va_list ap;
+
+    va_start (ap, format);
+    fprintf (stderr, "exiting %s\n", process);
+    vfprintf (stderr, format, ap);
+    va_end (ap);
+
+    exit (status);
+}
+
+int main (int argc, char **argv) {
+
+    psRegion region = {0,0,0,0};        // a region representing the entire array
+    psphotTestArguments (&argc, argv);
+
+    psFits *file = psFitsOpen (argv[1], "r");
+    psMetadata *header = psFitsReadHeader (NULL, file);
+    psImage *image = psFitsReadImage (NULL, file, region, 0);
+    psFitsClose (file);
+
+    psImageJpegColormap (argv[5]);
+
+    // psImage *fimage = psImageCopy (NULL, image, PS_TYPE_F32);
+
+    int binning = atof(argv[6]);
+
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN);
+    psImage *fimage = psImageRebin (NULL, image, NULL, 0, binning, stats);
+
+    float min = atof(argv[3]);
+    float max = atof(argv[4]);
+
+    psImageJpeg (fimage, argv[2], min, max);
+
+    psFree (header);
+    psFree (image);
+    exit (0);
+}
+
+
+# if (0)
+
+    psMetadata *row;
+    psArray *table;
+
+    psMetadataItem *mdi;
+
+    psMetadataConfigWrite (header, argv[2]);
+
+    // attempt to write image with NAXIS = 0
+    mdi = psMetadataLookup (header, "NAXIS");
+    mdi->data.S32 = 0;
+    mdi->type = PS_DATA_S32;
+
+    // create a test image
+    // psImage *tmpimage = psImageAlloc (10, 10, PS_DATA_F32);
+
+    // create a test table
+    table = psArrayAllocEmpty (10);
+
+    for (int i = 0; i < 10; i++) {
+        row = psMetadataAlloc ();
+        psMetadataAdd (row, PS_LIST_TAIL, "ROW",   PS_DATA_S32,    "", i);
+        psMetadataAdd (row, PS_LIST_TAIL, "FROW",  PS_TYPE_F32,    "", 0.1*i);
+        psMetadataAdd (row, PS_LIST_TAIL, "DUMMY", PS_DATA_STRING, "", "test line");
+
+        table->data[i] = row;
+    }
+    table->n = 10;
+
+    psMetadata *theader = psMetadataAlloc ();
+    psMetadataAdd (theader, PS_LIST_HEAD, "EXTNAME", PS_DATA_STRING, "extension name", "SMPFILE");
+
+    psFits *fits = psFitsOpen (argv[3], "w");
+    // psFitsWriteImage (fits, header, tmpimage, 0);
+    psFitsWriteHeader (header, fits);
+    psFitsWriteTable (fits, theader, table);
+
+# endif
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotTestArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotTestArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotTestArguments.c	(revision 22322)
@@ -0,0 +1,19 @@
+# include "psphotInternal.h"
+static int usage ();
+
+void psphotTestArguments (int *argc, char **argv) {
+
+  // basic pslib options
+  psLogSetFormat ("M");
+  psArgumentVerbosity (argc, argv);
+
+  if (*argc != 7) usage ();
+
+  return;
+}
+
+static int usage () {
+
+    fprintf (stderr, "USAGE: psphotTest (input.fits) (output.jpg) (zero) (scale) (colormap) (rebin)\n");
+    exit (2);
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotTestPSF.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotTestPSF.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotTestPSF.c	(revision 22322)
@@ -0,0 +1,131 @@
+# include "psphotInternal.h"
+
+bool psphotTestPSF (pmReadout *readout, psArray *sources, psMetadata *recipe) {
+
+    bool            status;
+    char           *modelName;
+    pmPSF          *psf = NULL;
+    psArray        *stars = NULL;
+
+    psTimerStart ("psphot");
+
+    psphotSaveImage (NULL, readout->image,  "image.fits");
+
+    // check if a PSF model is supplied by the user
+    psf = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.PSF");
+    if (psf != NULL) return psf;
+
+    // examine PSF sources in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // array to store candidate PSF stars
+    int NSTARS = psMetadataLookupS32 (&status, recipe, "PSF_MAX_NSTARS");
+    if (!status) {
+        NSTARS = PS_MIN (sources->n, 200);
+        psWarning("PSF_MAX_NSTARS is not set in the recipe --- defaulting to %d\n", NSTARS);
+    }
+
+    // use poissonian errors or local-sky errors
+    bool POISSON_ERRORS = psMetadataLookupBool (&status, recipe, "POISSON_ERRORS");
+    if (!status) {
+        POISSON_ERRORS = true;
+        psWarning("POISSON_ERRORS is not set in the recipe --- defaulting to true.\n");
+    }
+    pmSourceFitModelInit (15, 0.1, 1.0, POISSON_ERRORS);
+
+    // how to model the PSF variations across the field
+    // XXX make a default value?  or not?
+    psMetadata *md = psMetadataLookupMetadata (&status, recipe, "PSF.TREND.MASK");
+    psPolynomial2D *psfTrendMask;
+    if (!status || !md) {
+        psWarning("PSF.TREND.MASK is not set in the recipe --- defaulting to use zeroth order.\n");
+        psfTrendMask = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 0, 0);
+    } else {
+        psfTrendMask = psPolynomial2DfromMetadata (md);
+        if (!psfTrendMask) {
+            psError(PSPHOT_ERR_PSF, true, "Unable to construct polynomial from PSF.TREND.MASK in the recipe");
+            return false;
+        }
+    }
+
+    stars = psArrayAllocEmpty (sources->n);
+
+    // select the candidate PSF stars (pointers to original sources)
+    for (int i = 0; (i < sources->n) && (stars->n < NSTARS); i++) {
+        pmSource *source = sources->data[i];
+        // if (source->mode & PM_SOURCE_MODE_PSFSTAR) psArrayAdd (stars, 200, source);
+        psArrayAdd (stars, 200, source);
+    }
+    psLogMsg ("psphot.pspsf", 4, "selected candidate %ld PSF objects\n", stars->n);
+
+    if (stars->n == 0) {
+        psError(PSPHOT_ERR_PSF, true, "Failed to find any PSF candidates");
+        return NULL;
+    }
+
+    // get the fixed PSF fit radius
+    // XXX EAM : check that PSF_FIT_RADIUS < SKY_OUTER_RADIUS
+    float RADIUS = psMetadataLookupF32 (&status, recipe, "PSF_FIT_RADIUS");
+    if (!status) {
+        psWarning("PSF_FIT_RADIUS is not set in the recipe --- defaulting to 20.0\n");
+        RADIUS = 20.0;
+    }
+
+    // for this test, require a single model
+    psMetadataItem *mdi = psMetadataLookup (recipe, "PSF_MODEL");
+    if (mdi == NULL) psAbort("missing PSF_MODEL selection");
+    if (mdi->type != PS_DATA_STRING) psAbort("choose a single PSF_MODEL");
+    modelName = mdi->data.V;
+
+    pmPSFtestModel (stars, modelName, RADIUS, POISSON_ERRORS, psfTrendMask);
+
+    psphotSaveImage (NULL, readout->image,  "resid.fits");
+    psphotSaveImage (NULL, readout->mask,   "mask.fits");
+    psphotSaveImage (NULL, readout->weight, "weight.fits");
+    
+    return true;
+}
+
+bool pmPSFtestModel (psArray *sources, char *modelName, float RADIUS, bool poissonErrors, psPolynomial2D *psfTrendMask)
+{
+    bool status;
+    float x;
+    float y;
+
+    pmModelType type = pmModelSetType (modelName);
+    pmPSF *psf = pmPSFAlloc (type, poissonErrors, psfTrendMask);
+    if (psf == NULL) psAbort("unknown model");
+
+    FILE *f = fopen ("params.dat", "w");
+
+    // stage 1:  fit an independent model (freeModel) to all sources
+    psTimerStart ("fit");
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+        pmModel  *model  = pmSourceModelGuess (source, psf->type);
+        x = source->peak->x;
+        y = source->peak->y;
+
+        // set temporary object mask and fit object
+        // fit model as EXT, not PSF
+        psImageKeepCircle (source->mask, x, y, RADIUS, "OR", markVal);
+        status = pmSourceFitModel (source, model, PM_SOURCE_FIT_EXT);
+        psImageKeepCircle (source->mask, x, y, RADIUS, "AND", PS_NOT_U8(markVal));
+
+	// write fitted parameters to file
+	fprintf (f, "%f ", model->params->data.F32[PM_PAR_XPOS]);
+	fprintf (f, "%f ", model->params->data.F32[PM_PAR_YPOS]);
+
+	fprintf (f, "%f ", model->params->data.F32[PM_PAR_SXX]);
+	fprintf (f, "%f ", model->params->data.F32[PM_PAR_SYY]);
+	fprintf (f, "%f ", model->params->data.F32[PM_PAR_SXY]);
+
+	fprintf (f, "%f %d\n", model->chisq, model->nIter);
+
+	// subtract model flux
+	pmModelSub (source->pixels, source->mask, model, PM_MODEL_OP_FULL);
+    }
+    fclose (f);
+    psLogMsg ("psphot.psftest", 4, "fit ext: %f sec for %ld sources\n", psTimerMark ("fit"), sources->n);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotTestSourceOutput.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotTestSourceOutput.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotTestSourceOutput.c	(revision 22322)
@@ -0,0 +1,157 @@
+# include "psphotInternal.h"
+
+enum {
+    PSPHOT_ADD_NONE = 0,
+    PSPHOT_ADD_MODEL = 1,
+    PSPHOT_ADD_R0 = 2,
+    PSPHOT_ADD_R1 = 4,
+};
+
+bool psphotAddModel(psImage *image,
+		    pmModel *model,
+		    int mode
+    )
+{
+    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+
+    PS_ASSERT_PTR_NON_NULL(model, false);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
+
+    psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+    psVector *params = model->params;
+    psS32 imageCol;
+    psS32 imageRow;
+    psF32 skyValue = params->data.F32[0];
+    psF32 pixelValue;
+    
+    float xCenter = model->params->data.F32[PM_PAR_XPOS];
+    float yCenter = model->params->data.F32[PM_PAR_YPOS];
+    float Io = model->params->data.F32[PM_PAR_I0];
+
+    int xBin = 1;
+    int yBin = 1;
+    float xResidCenter = 0.0;
+    float yResidCenter = 0.0;
+
+    psImageInterpolateOptions *Ro = NULL;
+    psImageInterpolateOptions *Rx = NULL;
+    psImageInterpolateOptions *Ry = NULL;
+    if (model->residuals && (mode & (PSPHOT_ADD_R0 | PSPHOT_ADD_R1))) {
+	Ro = psImageInterpolateOptionsAlloc(
+	    PS_INTERPOLATE_BILINEAR,
+	    model->residuals->Ro, NULL, NULL, 0, 0.0, 0.0, 1, 0, 0.0);
+	Rx = psImageInterpolateOptionsAlloc(
+	    PS_INTERPOLATE_BILINEAR,
+	    model->residuals->Rx, NULL, NULL, 0, 0.0, 0.0, 1, 0, 0.0);
+	Ry = psImageInterpolateOptionsAlloc(
+	    PS_INTERPOLATE_BILINEAR,
+	    model->residuals->Ry, NULL, NULL, 0, 0.0, 0.0, 1, 0, 0.0);
+
+	xBin = model->residuals->xBin;
+	yBin = model->residuals->yBin;
+	xResidCenter = model->residuals->xCenter;
+	yResidCenter = model->residuals->yCenter;
+    }
+
+    for (psS32 iy = 0; iy < image->numRows; iy++) {
+        for (psS32 ix = 0; ix < image->numCols; ix++) {
+
+            // Convert i/j to image coord space:
+	    imageCol = ix + image->col0;
+	    imageRow = iy + image->row0;
+
+            x->data.F32[0] = (float) imageCol;
+            x->data.F32[1] = (float) imageRow;
+
+            // set the appropriate pixel value for this coordinate
+	    if (mode & PSPHOT_ADD_MODEL) {
+		pixelValue = model->modelFunc (NULL, params, x) - skyValue;
+	    } else {
+		pixelValue = 0.0;
+	    }
+
+	    // get the contribution from the residual model
+	    // XXX for a test, do this for all sources and all pixels
+	    if (Ro) {
+		// fractional image position
+		// this is wrong for the 'center' case
+		float ox = xBin*(ix + 0.5 + image->col0 - xCenter) + xResidCenter;
+		float oy = yBin*(iy + 0.5 + image->row0 - yCenter) + yResidCenter;
+
+		psU8 mflux = 0;
+		double Fo = 0.0;
+		double Fx = 0.0;
+		double Fy = 0.0;
+		psImageInterpolate (&Fo, NULL, &mflux, ox, oy, Ro);
+		psImageInterpolate (&Fx, NULL, &mflux, ox, oy, Rx);
+		psImageInterpolate (&Fy, NULL, &mflux, ox, oy, Ry);
+
+		if (!mflux && isfinite(Fo) && isfinite(Fx) && isfinite(Fy)) {
+		    if (mode & PSPHOT_ADD_R0) {
+			pixelValue += Io*Fo;
+		    }
+		    if (mode & PSPHOT_ADD_R1) {
+			pixelValue += Io*(xCenter*Fx + yCenter*Fy);
+		    }
+		}
+	    }
+	    image->data.F32[iy][ix] += pixelValue;
+        }
+    }
+    psFree(x);
+    psFree(Ro);
+    psFree(Rx);
+    psFree(Ry);
+    psTrace("psModules.objects", 3, "---- %s(true) end ----\n", __func__);
+    return(true);
+}
+
+// construct an initial PSF model for each object 
+bool psphotTestSourceOutput (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf) {
+
+    psImage *imMo = psImageAlloc (readout->image->numCols, readout->image->numRows, PS_TYPE_F32);
+    psImage *imR0 = psImageAlloc (readout->image->numCols, readout->image->numRows, PS_TYPE_F32);
+    psImage *imR1 = psImageAlloc (readout->image->numCols, readout->image->numRows, PS_TYPE_F32);
+    
+    // create template model
+    pmModel *modelRef = pmModelAlloc(psf->type);
+    modelRef->params->data.F32[PM_PAR_SKY] = 0;
+    modelRef->params->data.F32[PM_PAR_I0] = 1000;
+
+    int dx = 25;
+    int dy = 25;
+
+    // generate a grid of fake sources with amplitude 1000
+    for (int iy = 50; iy < imMo->numRows; iy += 100) {
+	for (int ix = 50; ix < imMo->numCols; ix += 100) {
+	    
+	    // assign the x and y coords to the image center
+	    modelRef->params->data.F32[PM_PAR_XPOS] = ix;
+	    modelRef->params->data.F32[PM_PAR_YPOS] = iy;
+	    
+	    // create modelPSF from this model
+	    pmModel *model = pmModelFromPSF (modelRef, psf);
+	    model->residuals = psf->residuals;
+
+	    // generate working image for this source
+	    psRegion region = {ix - dx, ix + dx, iy - dy, iy + dy};
+
+	    psImage *vM = psImageSubset (imMo, region);
+	    psImage *v0 = psImageSubset (imR0, region);
+	    psImage *v1 = psImageSubset (imR1, region);
+
+	    // we want to make one image o
+	    psphotAddModel (vM, model, PSPHOT_ADD_MODEL);
+	    psphotAddModel (v0, model, PSPHOT_ADD_R0);
+	    psphotAddModel (v1, model, PSPHOT_ADD_R1);
+	}
+    }
+
+    psphotSaveImage (NULL, imMo, "grid.Mo.fits");
+    psphotSaveImage (NULL, imR0, "grid.R0.fits");
+    psphotSaveImage (NULL, imR1, "grid.R1.fits");
+
+    exit (0);
+}
+
Index: /tags/sj_tags/sj_root_20080929/psphot/src/psphotVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/psphot/src/psphotVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/psphot/src/psphotVersion.c	(revision 22322)
@@ -0,0 +1,40 @@
+# include "psphotInternal.h"
+
+# if (HAVE_KAPA)
+# include <kapa.h>
+# endif
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString psphotVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString psphotVersionLong(void)
+{
+    psString version = psphotVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+
+# if (HAVE_KAPA)
+    psString ohanaVersion = psStringStripCVS (ohana_version(), "Name");
+    psString libdvoVersion = psStringStripCVS (libdvo_version(), "Name");
+
+    psStringAppend (&version, " with libkapa (ohana %s, libdvo: %s)", ohanaVersion, libdvoVersion);
+    psFree (ohanaVersion);
+    psFree (libdvoVersion);
+# else
+    psStringAppend (&version, " WITHOUT libkapa");
+# endif
+
+    psFree(tag);
+    return version;
+}
+
+// Defined by RHL; leaving for backwards compatibility.
+const char *psphotCVSName(void) {
+   return cvsTag;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/.cvsignore	(revision 22322)
@@ -0,0 +1,17 @@
+bin
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+Makefile.in
+missing
+Makefile
+config.cache
+config.guess
+config.sub
+ltmain.sh
+libtool
Index: /tags/sj_tags/sj_root_20080929/pstamp/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src scripts
+
+CLEANFILES = *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/pstamp/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=pstamp
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/pstamp/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/configure.ac	(revision 22322)
@@ -0,0 +1,47 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.61)
+
+AC_INIT([ppstamp], [0.1.1], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+AC_CONFIG_SRCDIR([scripts])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+dnl ------------------------------------------------------------
+
+AC_PATH_PROG([ERRORCODES], [psParseErrorCodes], [missing])
+if test "$ERRORCODES" = "missing" ; then
+  AC_MSG_ERROR([psParseErrorCodes is required])
+fi
+
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+
+dnl Set CFLAGS for build
+IPP_STDOPTS
+CFLAGS="${CFLAGS} -Wall -Werror"
+echo "PPSTAMP_CFLAGS: $PPSTAMP_CFLAGS"
+echo "PPSTAMP_LIBS: $PPSTAMP_LIBS"
+
+AC_SUBST([PPSTAMP_CFLAGS])
+AC_SUBST([PPSTAMP_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  scripts/Makefile
+])
+
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/.cvsignore	(revision 22322)
@@ -0,0 +1,2 @@
+Makefile.in
+Makefile
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/Makefile.am	(revision 22322)
@@ -0,0 +1,26 @@
+
+installdir = $(bindir)
+
+install_files = \
+	pstamp_finish.pl \
+	pstamp_job_run.pl \
+	pstamp_listjobs.pl \
+	pstamp_new_request.sh \
+	pstampparse.pl \
+	pstamp_parser_run.pl \
+	pstamp_queue_requests.pl \
+	pstamp_results_file.pl \
+	pstamp_revert_request.pl \
+	pstamp_runcommand.sh \
+	pstamp_webrequest.pl \
+        pstamp_get_image_job.pl \
+	request_finish.pl \
+	detect_query_read \
+	detect_response_create \
+        dquery_finish.pl \
+        dqueryparse.pl \
+	fakedresponse.pl
+
+install_SCRIPTS = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/detect_query_read
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/detect_query_read	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/detect_query_read	(revision 22322)
@@ -0,0 +1,195 @@
+#!/usr/bin/env perl
+
+# Read and parse a fits table containing a MOPS_DETECTABILITY_QUERY Extension
+# and optionally print out the contents
+
+# by default prints out the keywords in the exension header followed by the rows of the table
+# with labels
+# -l omits the labels
+# -h omits the header
+# -r omits the rows
+# so -lhr will print nothing
+
+use warnings;
+use strict;
+
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use Math::Trig;
+use Data::Dumper;
+
+use constant EXTNAME => 'MOPS_DETECTABILITY_QUERY'; # Extension name for table
+
+my $no_print_label  = 0;    # omit the labels
+my $no_print_header = 0;    # omit the header keywords
+my $no_print_rows   = 0;    # omit the rows
+
+
+my ( $input,			# Name of input text file
+     $output,			# Name of output table
+     $save_temps,		# Save temporary files?
+     );
+
+GetOptions(
+	   'input|i=s'    => \$input,
+	   'output|o=s'   => \$output,
+           'nolabel|l'    => \$no_print_label,
+           'noheader|h'   => \$no_print_header,
+           'norows|r'     => \$no_print_rows
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --input",
+           -exitval => 3) unless defined $input;
+
+if ($output && ($output ne "-")) {
+    die "cannot open $output for output\n" unless open OUTFILE, ">$output";
+    select OUTFILE;
+}
+
+my $status = 0;
+
+# The required keywords
+my $header = {
+        'QUERY_ID' => { name => 'QUERY_ID', 
+                        writetype => TSTRING, 
+                        comment => 'MOPS Query ID for this batch query',
+                        value => undef
+                      },
+        'FPA_ID'   => { name => 'FPA_ID', 
+                        writetype => TSTRING, 
+                        comment => 'orginal FPA_ID used at ingest',
+                        value => undef
+                      }, 
+        'MJD_OBS'  => { name => 'MJD-OBS', 
+                        writetype => TDOUBLE, 
+                        comment => 'starting time of the exposure, MJD',
+                        value => undef
+                      },
+        'FILTER'   => { name => 'FILTER', 
+                        writetype => TSTRING, 
+                        comment => 'effective filter use for the exposure',
+                        value => undef
+                      },
+        'OBSCODE'  => { name => 'OBSCODE', 
+                        writetype => TSTRING, 
+                        comment => 'site identifier (MPC observatory code)',
+                        value => undef
+              }
+};
+
+# key_array insures that the order that the keywords is printed out is
+# the same as the ICD
+my @key_array = qw( QUERY_ID FPA_ID MJD_OBS FILTER OBSCODE );
+
+# Specification of columns
+my $column_defs = [ 
+        # matching rownum from detectability original request
+        { name => 'ROWNUM',   type => '20A', writetype => TSTRING }, 
+        # coordinate at start of exposure, in degrees
+        { name => 'RA1_DEG',  type => 'D',   writetype => TDOUBLE },
+        # coordinate at start of exposure, in degrees
+        { name => 'DEC1_DEG', type => 'D',   writetype => TDOUBLE },
+        # coordinate at end of exposure, in degrees
+        { name => 'RA2_DEG',  type => 'D',   writetype => TDOUBLE },
+        # coordinate at end of exposure, in degrees
+        { name => 'DEC2_DEG', type => 'D',   writetype => TDOUBLE },
+        # apparent magnitude
+        { name => 'MAG',      type => 'D',   writetype => TDOUBLE },
+];
+
+# Parse the list of columns
+my @colNames;			# Names of columns
+my @colTypes;			# Types of columns
+my @colWriteType;               # type to use to write
+my %colData;			# Data for each column referenced by name
+foreach my $colSpec ( @$column_defs) {
+    push @colNames, $colSpec->{name};
+    push @colTypes, $colSpec->{type};
+    push @colWriteType, $colSpec->{writetype};
+}
+
+# Read the input file
+
+my $inFits = Astro::FITS::CFITSIO::open_file( $input, READONLY, $status ); # FITS file handle
+check_fitsio($status);
+
+$inFits->movnam_hdu(BINARY_TBL, EXTNAME, 0, $status) and check_fitsio($status);
+
+my $inHeader = $inFits->read_header(); # Header for input
+
+my $numRows;			# Number of rows in table
+$inFits->get_num_rows($numRows, $status) and check_fitsio($status);
+
+foreach my $col (@$column_defs) {
+    my ($col_num, $col_type, $col_data);
+
+    $inFits->get_colnum(0, $col->{name}, $col_num, $status) and check_fitsio($status);
+    $inFits->get_coltype($col_num, $col_type, undef, undef, $status) and check_fitsio($status);
+    $inFits->read_col($col_type, $col_num, 1, 1, $numRows, 0, $col_data, undef, $status)
+                                                                    and check_fitsio($status);
+    $colData{$col->{name}} = $col_data;
+}
+
+# Now produce the output
+
+if (!$no_print_header) {
+    my $label;
+    my $data;
+    # I don't do this because I want the keys to be printed in a particular order
+    #foreach my $key (keys %$header) {
+    foreach my $key (@key_array) {
+        my $name = $header->{$key}->{name};
+        my $value = $inHeader->{$name};
+        # get rid of quotes and whitespace
+        $value =~ s/\'//g;
+        if (defined $value) {
+            #print "$key\t\t\t$value\n";
+            $label .= sprintf "%-12s ", $key;
+            $data  .= sprintf "%-12s ", $value;
+        } else {
+            die "keyword $key not found in $input\n";
+        }
+    }
+    print "# " . $label . "\n" unless $no_print_label;
+    print $data  . "\n";
+}
+
+if (!$no_print_rows) {
+    if (!$no_print_label) {
+        print "# ";
+        foreach my $col (@$column_defs) {
+            printf "%-12s ", $col->{name};
+        }
+        print "\n";
+    }
+
+    for (my $i = 0; $i < $numRows; $i++) {
+        foreach my $col (@$column_defs) {
+            printf "%-12s ", $colData{$col->{name}}->[$i];
+        #foreach my $aref (@col_arrays) {
+            #printf "%-12s ", $aref->[$i];
+        }
+        print "\n";
+    }
+}
+
+exit 0;
+
+### Pau.
+
+# From Astro::FITS::CFITSIO demo
+sub check_fitsio
+{
+    my $status = shift;		# Status of FITSIO calls
+
+    if ($status != 0) {
+	my $msg;		# Message to output
+	Astro::FITS::CFITSIO::fits_get_errstatus( $status , $msg );
+	die "CFITSIO error: $msg\n";
+    }
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/detect_response_create
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/detect_response_create	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/detect_response_create	(revision 22322)
@@ -0,0 +1,176 @@
+#!/usr/bin/env perl
+
+# program to create a simulated MOPS_DETECTABILITY_RESPONSE file
+# based on text format input file
+
+use warnings;
+use strict;
+
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use Math::Trig;
+use Data::Dumper;
+
+use constant EXTNAME => 'MOPS_DETECTABILITY_RESPONSE'; # Extension name for output table
+use constant EXTVER =>  1;
+use constant OBSERVATORY_CODE => 566; # IAU Observatory Code
+
+my ( $input,			# Name of input Detectabilty Query table
+     $output,			# Name of output table
+     $save_temps,		# Save temporary files?
+     );
+
+GetOptions(
+	   'input|i=s'    => \$input,
+	   'output|o=s'   => \$output,
+	   'save-temps' => \$save_temps,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --input --output",
+           -exitval => 3)
+    unless defined $input 
+    and defined $output;
+
+my $status = 0;
+
+# Specification of columns to write
+my $columns = [ 
+        # matching rownum from detectability original request
+        { name => 'ROWNUM',   type => '20A', writetype => TSTRING }, 
+
+        # number of pixels used in hypothetical PSF for the query detection
+        { name => 'DETECT_N', type => 'V',   writetype => TULONG },
+
+        # detectibility, indicating the fraction of PSF pixels detetable by IPP
+        { name => 'DETECT_F', type => 'D',   writetype => TDOUBLE },
+            ];
+
+# Header translation table
+my $headers = {
+    'QUERY_ID' => { name => 'QUERY_ID', type => TSTRING, 
+                        comment => 'MOPS Query ID for this batch query' },
+    'FPA_ID' => { name => 'FPA_ID',   type => TSTRING, 
+                        comment => 'original FPA_ID used at ingest' },
+	    };
+
+# Parse the list of columns
+my @colNames;			# Names of columns
+my @colTypes;			# Types of columns
+my %colData;			# Data for each column
+my @colWriteType;                 # type to use to write
+foreach my $colSpec ( @$columns) {
+    push @colNames, $colSpec->{name};
+    push @colTypes, $colSpec->{type};
+    push @colWriteType, $colSpec->{writetype};
+    $colData{$colSpec->{name}} = [];
+}
+
+my $in;
+if ($input eq "-") {
+    $in = *STDIN;
+} else {
+    die "cannot open input file $input" unless open $in, "<$input";
+}
+
+my $numRows;
+my $inHeader = { };
+
+read_input($in);
+
+
+# Write the output
+unlink "$output" if -e "$output";
+# Output file handle
+my $outFits = Astro::FITS::CFITSIO::create_file( $output, $status );
+check_fitsio( $status );
+
+$outFits->create_img( 16, 0, undef, $status );
+check_fitsio( $status );
+
+# Start the table
+$outFits->create_tbl( BINARY_TBL(), $numRows, scalar @colNames, \@colNames, \@colTypes, undef, EXTNAME,
+		      $status );
+check_fitsio( $status );
+
+# TODO: get the extension version number from somewhere common
+$outFits->write_key( TINT, 'EXTVER', EXTVER, 'Version of this Extension', $status );
+check_fitsio( $status );
+
+# Write the Extension keywords
+foreach my $keyword ( keys %$headers ) {
+    my $value = $inHeader->{$keyword}->{value}; # Header keyword value
+    unless (defined $value) {
+	print "Can't find header keyword $keyword\n";
+	next;
+    }
+    $value =~ s/\'//g;
+    my $name    = $headers->{$keyword}->{name}; # New name
+    my $type    = $headers->{$keyword}->{type}; # Type
+    my $comment = $headers->{$keyword}->{comment}; # Comment
+    $outFits->write_key( $type, $name, $value, $comment, $status );
+    check_fitsio( $status );
+}
+
+
+for (my $i = 0; $i < scalar @colNames; $i++) {
+    my $colName = $colNames[$i];# Column name
+    my $writeType = $colWriteType[$i];
+    $outFits->write_col( $writeType, $i + 1, 1, 1, $numRows, $colData{$colName}, $status );
+    check_fitsio( $status );
+}
+
+$outFits->close_file( $status );
+
+### Pau.
+
+
+# From Astro::FITS::CFITSIO demo
+sub check_fitsio
+{
+    my $status = shift;		# Status of FITSIO calls
+
+    if ($status != 0) {
+	my $msg;		# Message to output
+	Astro::FITS::CFITSIO::fits_get_errstatus( $status , $msg );
+	die "CFITSIO error: $msg\n";
+    }
+}
+
+sub read_input
+{
+    my $inh = shift;
+    my $line;
+    # read data for header
+    while ($line = <$inh>) {
+        next if ($line =~ /^#/);    # skip comment lines
+        chomp $line;
+        ($inHeader->{QUERY_ID}->{value}, $inHeader->{FPA_ID}->{value}, $inHeader->{MJD_OBS}->{value}, 
+         $inHeader->{FILTER}->{value}, $inHeader->{OBSCODE}->{value}) 
+                = split " ", $line;
+        last;
+    }
+    die "failed to read valid header words from $input" 
+        unless defined($inHeader->{QUERY_ID}->{value}) &&
+           ($inHeader->{FPA_ID}->{value}) &&
+           ($inHeader->{MJD_OBS}->{value}) && ($inHeader->{FILTER}->{value}) &&
+           ($inHeader->{OBSCODE}->{value}) ;
+
+
+    $numRows = 0;
+    while ($line = <$inh> ) {
+        next if ($line =~ /^#/);    # skip comment lines
+        chomp $line;
+
+        my ($rownum, $npix, $flux) = split " ", $line;
+
+        push @{$colData{'ROWNUM'}},   $rownum;
+        push @{$colData{'DETECT_N'}}, $npix;
+        push @{$colData{'DETECT_F'}}, $flux;
+        $numRows++;
+    }
+}
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/dquery_finish.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/dquery_finish.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/dquery_finish.pl	(revision 22322)
@@ -0,0 +1,156 @@
+#!/bin/env perl
+
+# dquery_finish.pl
+
+use warnings;
+use strict;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+use Sys::Hostname;
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Temp qw( tempfile );
+use File::Copy;
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config qw( :standard );
+
+my ( $req_id, $req_name, $req_file, $product, $dbname, $verbose, $save_temps );
+
+GetOptions(
+           'req_id=s'   => \$req_id,
+           'req_file=s' => \$req_file,
+           'req_name=s' => \$req_name,
+           'product=s'  => \$product,
+	   'dbname=s'   => \$dbname,
+	   'verbose'    => \$verbose,
+	   'save-temps' => \$save_temps,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+my $err = "";
+
+$err .= "--req_id is required\n" if !$req_id;
+$err .= "--req_name is required\n" if !$req_name;
+$err .= "--product is required\n" if !$product;
+
+die "$err" if $err;
+
+my $missing_tools;
+
+my $pstamptool  = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+my $dsreg  = can_run('dsreg')  or (warn "Can't find dsreg"  and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+my $ipprc = PS::IPP::Config->new(); # IPP Configuration
+
+my $outputDataStoreRoot = metadataLookupStr($ipprc->{_siteConfig}, 'DATA_STORE_ROOT');
+exit ($PS_EXIT_CONFIG_ERROR) unless defined $outputDataStoreRoot; # lookup failure outputs a message
+
+if ($product eq "NULL") {
+    # parsing failed just with fault = 0 (this leaves previously set fault in place
+    stop_request($req_id, 0, $verbose);
+    exit 0;
+}
+my $prod_dir = "$outputDataStoreRoot/$product";
+if (! -e $prod_dir ) {
+    # something must have gone wrong at the parse stage
+    stop_request($req_id, $PS_EXIT_SYS_ERROR, $verbose);
+    die "product directory does not exist $prod_dir";
+}
+my $out_dir = "$prod_dir/$req_name";
+if (! -e $out_dir ) {
+    # something must have gone wrong at the parse stage
+    print STDERR "output fileset directory $out_dir does not exist\n" if $verbose;
+    if (! mkdir $out_dir ) {
+        stop_request($req_id, $PS_EXIT_SYS_ERROR, $verbose);
+        die "cannot create output directory $out_dir";
+    }
+} elsif (! -d $out_dir) {
+    stop_request($req_id, $PS_EXIT_SYS_ERROR, $verbose);
+    die "output fileset directory $out_dir exists but is not a directory";
+}
+
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+my $request_fault = 0;
+my @jobs;
+{
+    my $command = "$pstamptool -listjob -req_id $req_id";
+    $command .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+    my $output = join "", @$stdout_buf;
+    if (!$output) {
+        if ($verbose) {
+            print STDERR "Request $req_id produced no jobs.\n";
+        }
+        # assume that the parser set the fault
+    } else {
+        my $metadata =  $mdcParser->parse($output) or die("Unable to parse metdata config doc for jobs list");
+        my $jobs = parse_md_list($metadata);
+        @jobs = @$jobs;
+    }
+}
+
+my ($REGLIST, $reg_list) = tempfile("$out_dir/reqlist.XXXX", UNLINK => !$save_temps);
+foreach my $job (@jobs) {
+    my $job_id = $job->{job_id};
+    my $response_file = "response${job_id}.fits";
+    my $response_path = "$out_dir/$response_file";
+
+    if (-e $response_path) {
+        # the job generated a response file put it into the Data Store
+        print $REGLIST "$response_file|||table|\n";
+    } else  {
+        print STDERR "detect_query response file for job $job_id not found\n" if $verbose;
+        $request_fault = $PS_EXIT_UNKNOWN_ERROR;
+    }
+}
+close $REGLIST;
+
+if (-s $reg_list) {
+    my $command = "$dsreg --add $req_name --product $product --list $reg_list";
+    $command .= " --type MOPS_DETECTABILITY_RESPONSE";
+    $command .= " --dbname $dbname" if $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $request_fault = $error_code >> 8;
+        print STDERR "Unable to perform $command return code: $request_fault";
+        # fall through to call stop request
+    }
+}
+
+stop_request($req_id, $request_fault, $verbose);
+
+exit 0;
+
+sub stop_request {
+    my $req_id = shift;
+    my $fault = shift;
+    my $verbose = shift;
+    
+    my $command = "$pstamptool -updatereq -req_id $req_id -state stop";
+    $command   .= " -fault $fault" if $fault;
+    $command   .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/dqueryparse.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/dqueryparse.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/dqueryparse.pl	(revision 22322)
@@ -0,0 +1,163 @@
+#!/usr/bin/env perl
+#
+# parse a MOPS_DETCTABILITY_QUERY table and create a results file
+#
+# Note: this file is currently only a placeholder which creates a fake response file
+# and adds a completed job to the database
+#
+
+use strict;
+use warnings;
+
+use Getopt::Long qw( GetOptions );
+use Pod::Usage qw( pod2usage );
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+
+my ($req_file, $req_id, $out_dir, $product, $mode, $dbname, $verbose, $save_temps);
+
+#
+# parse args
+#
+
+GetOptions(
+        'file=s'          =>      \$req_file,
+        'req_id=s'        =>      \$req_id,
+        'out_dir=s'       =>      \$out_dir,
+        'product=s'       =>      \$product,
+        'mode=s'          =>      \$mode,
+        'dbname=s'        =>      \$dbname,
+        'verbose'         =>      \$verbose,
+        'save-temps'      =>      \$save_temps,
+) or pod2usage(2);
+
+my $err = "";
+
+if (!$req_file) {
+    $err .= "--file is required\n";
+}
+if (!$req_id) {
+    $err .= "--req_id is required\n";
+}
+if (!$out_dir) {
+    $err .="--out_dir is required\n";
+}
+if (!$product) {
+    $err .="--product is required\n";
+}
+
+
+die $err if ($err);
+
+my $missing_tools;
+my $pstamptool = can_run('pstamptool') or (warn "Can't find pstamptool" and $missing_tools =1);
+my $fakedresponse = can_run('fakedresponse.pl') or (warn "Can't find fakedresponse.pl" and $missing_tools =1);
+my $fields = can_run('fields') or (warn "Can't find fields" and $missing_tools =1);
+
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+# get the query id and check the extname and version from the header
+my $fields_output;
+{
+    my $command = "echo $req_file | $fields -x 0 EXTNAME EXTVER QUERY_ID";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    
+#   fields doesn't return zero when it succeeds
+#    unless ($success) {
+#        print STDERR @$stderr_buf;
+#    }
+    $fields_output = join "", @$stdout_buf;
+}
+my (undef, $extname, $extver, $req_name) = split " ", $fields_output;
+
+die "$req_file is missing one of EXTNAME EXTVER or QUERY_ID" 
+    if !(defined($extname) and defined($extver) and defined($req_name));
+
+die "$req_file has EXTNAME $extname not MOPS_DETECTABILITY_QUERY table"
+                if $extname ne         "MOPS_DETECTABILITY_QUERY";
+die "$req_file is version $extver expecting 1" if $extver ne 1;
+
+$out_dir .= "/$req_name";
+if (! -e $out_dir ) {
+    mkdir $out_dir or die "cannot create output directory $out_dir";
+} elsif (! -d $out_dir ) {
+    die "output fileset directory $out_dir exists but is not a directory";
+}
+
+#
+# XXX Eventually we will parse the file here, to look up the list of input images to be processed
+# and queue a job for each image.
+# The request file will be one of the arguments. Each job will look at all rows and create entries
+# in the response file the coordinates that overlap the image.
+#
+# In the meantime we don't parse the file here, pass it to the program fakedresponse.
+# It creates a response file with a line for each row in the request file
+
+my $response_file = "$out_dir/response.fits";
+my $fault;
+{
+    my $command = "$fakedresponse --input $req_file --output $response_file --workdir $out_dir";
+    $command .= " --save-temps" if $save_temps;
+    $command .= " --verbose" if $verbose;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        warn("Unable to perform $command error code: $error_code");
+    }
+    $fault = $error_code >> 8;
+}
+
+my $job_id;
+my $result;
+{
+    my $command = "$pstamptool -addjob -req_id $req_id -uri $req_file -outputBase $out_dir";
+    $command .= " -job_type detect_query -state stop -fault $fault";
+    $command .= " -rownum 0";
+    $command .= " -dbname $dbname" if $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+
+    if ($success) {
+        $job_id = join "", @$stdout_buf;
+        chomp $job_id;
+        if ($job_id && -e $response_file) {
+            rename $response_file, "$out_dir/response${job_id}.fits";
+        }
+        $result = 0;
+    } else {
+        warn("Unable to perform $command error code: $error_code");
+        $result = $error_code >> 8;
+    }
+}
+
+
+{
+    my $command = "$pstamptool -updatereq -req_id $req_id -name $req_name -outProduct $product";
+    $command .= " -fault $result" if $result;
+    $command .= " -dbname $dbname" if $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die "$command failed";
+    }
+}
+ 
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/fakedresponse.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/fakedresponse.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/fakedresponse.pl	(revision 22322)
@@ -0,0 +1,103 @@
+#!/usr/bin/env perl
+#
+# parse a MOPS_DETCTABILITY_QUERY table and create a results file
+#
+# Note: this file is currently only a placeholder which creates a fake response file
+#
+
+use strict;
+use warnings;
+
+use Getopt::Long qw( GetOptions );
+use Pod::Usage qw( pod2usage );
+use DBI;
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use File::Copy;
+use File::Basename;
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+my ($input, $output, $verbose, $save_temps, $workdir);
+#
+# parse args
+#
+
+
+GetOptions(
+        'input=s'          =>     \$input,
+        'output=s'        =>      \$output,
+        'workdir=s'       =>      \$workdir,
+        'verbose'         =>      \$verbose,
+        'save-temps'      =>      \$save_temps,
+) or pod2usage(2);
+
+my $err = "";
+
+if (!$input) {
+    $err .= "--input is required\n";
+}
+if (!$output) {
+    $err .="--output is required\n";
+}
+
+die $err if ($err);
+
+# spit the contents of the file in text format 
+# XXX Do we lose precision on the floats when we do this?
+
+my $query_text = `detect_query_read --nolabel --input $input`;
+
+my @lines = split "^", $query_text;
+
+# first line is the header keywords
+my $header = shift @lines;
+chomp $header;
+die "failed to parse $input" unless $header;
+
+my ($query_id, $fpa_id, $dateobs, $filter, $obscode) = split " ", $header;
+die "failed to parse $input" unless $query_id && $fpa_id && $dateobs && $filter && $obscode;
+
+if (!$workdir) {
+    $workdir="/tmp";
+}
+my $txt_file = "$workdir/response$$.txt";
+open OUT, ">$txt_file";
+
+print OUT "$query_id $fpa_id $dateobs $filter $obscode\n";
+
+while (my $line = shift @lines) {
+    chomp $line;
+    my ($rownum, $detect_n, $detect_f) = fake_dquery_response($line);
+
+    printf OUT "%-12s %-8d %-8.4f\n", $rownum, $detect_n, $detect_f;
+}
+close OUT;
+
+my $result = system "detect_response_create --input $txt_file --output $output";
+
+unlink $txt_file unless $save_temps;
+
+exit $result;
+
+
+sub fake_dquery_response {
+    my $line = shift;
+    my ($rownum, $ra1, $dec1, $ra2, $dec2, $mag) = split " ", $line;
+
+    # todo perhaps think more about these values
+    my $n = rand(16);
+    my $f = rand(1);
+
+    return ($rownum, $n, $f);
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_dorequest.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_dorequest.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_dorequest.pl	(revision 22322)
@@ -0,0 +1,116 @@
+#!/bin/env perl
+###
+### pstamp_dorequest.pl
+###
+###     Excecute the jobs for a given request.
+###
+###     Note: This program is not part of the postage stamp server
+###     It is intended for testing outside of pantasks environment
+###
+
+use warnings;
+use strict;
+
+if (@ARGV != 1) {
+    die "usage: $0 request_id\n";
+}
+
+my $request_id = $ARGV[0];
+
+my $verbosity = 0;
+
+use Sys::Hostname;
+my $host = hostname();
+
+if ($verbosity) {
+    print "\n\n";
+    print "Starting script $0 on $host\n\n";
+}
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+
+my $missing_tools;
+
+my $pstamptool  = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+my $ppstamp_run = can_run('ppstamp_run.pl') or (warn "Can't find ppstamp_run.pl" 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 @psjobs;
+#Look up the jobs for the given request_id
+{
+    my $command = "$pstamptool -pendingjob";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbosity);
+    unless ($success) {
+        die("Unable to perform pstamptool -pendingreq: $error_code");
+    }
+
+    if (@$stdout_buf == 0) {
+        print STDERR "no pending pstamp jobs found\n";
+        exit 1;
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        die("Unable to parse metdata config doc");
+
+    my $jobs = parse_md_list($metadata);
+
+    foreach my $job (@$jobs) {
+        if ($job->{req_id} == $request_id) {
+            # print STDERR "adding $job->{job_id} to the list\n";
+            $psjobs[@psjobs] = $job;
+        }
+    }
+}
+
+if (! @psjobs) {
+    # TODO: is this always an error, what if the job is no longer pending?
+    print STDERR "no pending postage stamp jobs for request $request_id found\n";
+    exit 1;
+}
+
+foreach my $job (@psjobs) {
+    my $command = "$ppstamp_run $job->{job_id}";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbosity);
+    unless ($success) {
+        die("Unable to perform $command: $error_code");
+    }
+    ### print @$stdout_buf;
+}
+
+#
+# Update the state of the request
+#
+{
+    ## TODO: what about request status
+    my $command = "$pstamptool -updatereq -req_id $request_id -state stop"; 
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbosity);
+    unless ($success) {
+        die("Unable to perform pstamptool -updatereq: $error_code");
+    }
+}
+
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_finish.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_finish.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_finish.pl	(revision 22322)
@@ -0,0 +1,350 @@
+#!/bin/env perl
+
+# pstamp_finish.pl
+
+use warnings;
+use strict;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+use Time::Local;
+use Sys::Hostname;
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Temp qw( tempfile );
+use File::Copy;
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config qw( :standard );
+use PStamp::RequestFile qw( :standard );
+use PStamp::Job qw( :standard );
+
+my ( $req_id, $req_name, $req_file, $product, $dbname, $verbose, $save_temps );
+
+# the char to the right of the bar may be used as a single - alias for the longer name
+# for example --input and -i are equivalent
+GetOptions(
+           'req_id=s'   => \$req_id,
+           'req_name=s' => \$req_name,
+           'req_file=s' => \$req_file,
+           'product=s'  => \$product,
+	   'dbname=s'   => \$dbname,
+	   'verbose'    => \$verbose,
+	   'save-temps' => \$save_temps,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+die "usage: --req_id id --req_name name --req_file file --product product [--dbname dbname --verbose]\n"
+    if !$req_id or !$req_name or !$req_file or !$product;
+
+my $missing_tools;
+
+my $pstamptool  = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+my $dsreg  = can_run('dsreg')  or (warn "Can't find dsreg"  and $missing_tools = 1);
+my $pstamp_results = can_run('pstamp_results_file.pl') 
+                            or (warn "Can't find pstamp_results_file.pl" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+my $ipprc = PS::IPP::Config->new(); # IPP Configuration
+
+my $outputDataStoreRoot = metadataLookupStr($ipprc->{_siteConfig}, 'DATA_STORE_ROOT');
+exit ($PS_EXIT_CONFIG_ERROR) unless defined $outputDataStoreRoot; # lookup failure outputs a message
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+{
+    # set the output fileset's name to the request name.
+    my $fileset = $req_name;
+
+    # Here we invoke the assumption that the output for the request is placed in the
+    # fileset directory directly
+    my $out_dir = "$outputDataStoreRoot/$product/$fileset";
+
+    print STDERR "product: $product  REQ_NAME: $req_name $out_dir\n" if $verbose;
+
+    if (!-e $out_dir) {
+        # something must have gone wrong parsing the request
+        print STDERR  "output fileset directory $out_dir does not exist\n";
+
+        # XXX TODO: if this fails fault the request so we pstamp_finish 
+
+        mkdir $out_dir or die "cannot create output directory $out_dir";
+
+    } elsif (! -d $out_dir ) {
+        # XXX TODO: fault the request so we pstamp_finish doesn't keep trying to process the
+        # request
+        die "output fileset directory $out_dir exists but is not a directory";
+    }
+
+    if (! -e $req_file ) {
+        print STDERR "request file $req_file is missing\n";
+        stop_request($req_id, $PS_EXIT_CONFIG_ERROR, $dbname);
+        exit  $PS_EXIT_CONFIG_ERROR;
+    }
+
+    # this function is PStamp::RequestFile::read_request_file
+    my ($header, $rows) = read_request_file($req_file);
+
+    if (!$header or !$rows) {
+        # Since a request got queued, the request file must have been readable at some
+        # point 
+        print STDERR "failed to read request_file $req_file" ;
+        stop_request($req_id, $PS_EXIT_CONFIG_ERROR, $dbname);
+        exit  $PS_EXIT_CONFIG_ERROR;
+    }
+
+    # at this point we need to find out what kind of request type it is and
+    # split the processing depending on the request type
+    # the only processing required for a detectabilty query is to build the output fileset.
+
+    # the following is for a postage stamp request
+
+    my ($rlf, $reglist_name) = tempfile ("$out_dir/reglist.XXXX", UNLINK => !$save_temps);
+    print $rlf "results.fits|||table|\n";
+    my $err_file = "$out_dir/parse_error.txt";
+    if (-e $err_file ) {
+        print $rlf "$err_file|||text|\n";
+    }
+
+    my ($tdf, $table_def_name) = tempfile ("$out_dir/tabledef.XXXX", UNLINK => !$save_temps);
+
+
+    # data for the header
+    print $tdf "$req_name|$req_id|\n";
+    my @jobs;
+    {
+        my $command = "$pstamptool -listjob -req_id $req_id";
+        $command   .= " -dbname $dbname" if $dbname;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            die("Unable to perform $command error code: $error_code");
+        }
+        my $output = join "", @$stdout_buf;
+        if (!$output) {
+            if ($verbose) {
+                print STDERR "Request $req_id produced no jobs.\n"
+            }
+            # No Jobs. 
+            # XXXX Ouch. We need results for each rownum (each request specification) in the request file 
+            # including those that produced no jobs.
+            # for now add an entry for rownum 1 and a phony error code.
+            # we've included parse_results.txt to the fileset if it exists
+            #
+            my $rownum = 1;
+            my $fault = 42; # get a real error code
+            print $tdf "$rownum|$fault|0|0|";
+            print $tdf "0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|\n";
+        } else {
+            my $metadata = $mdcParser->parse($output) or die("Unable to parse metdata config doc");
+
+            my $jobs = parse_md_list($metadata);
+
+            @jobs = @$jobs;
+        }
+    }
+
+    foreach my $job (@jobs) {
+        my $job_id = $job->{job_id};
+        my $job_type = $job->{jobType};
+        my $rownum = $job->{rownum};
+        my $fault = $job->{fault};
+        my $exp_id = $job->{exp_id};
+
+        # XXX: get the image_db from a table in the database, or maybe save it in the job
+
+        # unless $exp_id is null (e.g. stack images) get the metadata for this exposure
+        my ($row, $req_info, $project) = get_request_info($rows, $rownum);
+        my $proj_hash = resolve_project($project);
+        my $image_db = $proj_hash->{dbname};
+        my $exp_info = get_exposure_info($image_db, $exp_id);
+
+        if (($job_type eq "stamp") || ($job_type eq "get_image")) {
+            my $jreglist = "$out_dir/reglist$job_id";
+            if (open JRL, "<$jreglist") {;
+                # process the reglist file to get the list of files produced by this job
+                foreach my $line (<JRL>) {
+                    # add line to the requests's reglist
+                    print $rlf $line;
+
+                    chomp $line;
+                    my ($img_name, undef) = split '\|', $line;
+
+                    # add line to the table definition file
+                    print $tdf "$rownum|$fault|$img_name|$job_id|";
+
+                    # ra_deg and dec_deg are the coordinates of center of the stamp
+                    #
+                    # XXX  we need to get the center coordinate of the stamp.
+                    # if request used -skycenter then we have it in the request info
+
+                    print $tdf "0.0|0.0|";
+                    print $tdf "$exp_info|";
+                    print $tdf "$req_info|";
+                    print $tdf "\n";
+                }
+                close JRL;
+            } else {
+                print STDERR "no reglist file for job $job_id\n" if $verbose;
+                print $tdf "$rownum|$fault|0|$job_id|";
+                print $tdf "0|0|";       # center of (non-existent) stamp
+                print $tdf "$exp_info|";
+                print $tdf "$req_info|";
+            }
+        } else {
+            # XXX do list jobs
+            # we can probably arange things to use the code as above and skip the fileset registration
+            print STDERR "Unknown jobType: $job_type";
+            next;
+        }
+    }
+    close $rlf;
+    close $tdf;
+    my $request_fault = 0;
+    # make the results file
+    {
+        my $command = "$pstamp_results --input $table_def_name --output $out_dir/results.fits";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            print STDERR "Unable to perform $command error code: $error_code\n";
+            $request_fault = $error_code >> 8;
+        }
+    }
+    if (!$request_fault) {
+        # register the fileset
+        # Note that we are assuming that the fileset's files are already in the fileset directory.
+        # We could put them somewhere else and copy them in but for now we set the output directory
+        # to the fileset directory at parse time
+        my $command = "$dsreg --list $reglist_name --add $fileset --product $product --type PSRESULTS";
+        $command .= " --dbname $dbname" if $dbname;
+
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            #die("Unable to perform $command error code: $error_code");
+            print STDERR "Unable to perform $command error code: $error_code\n";
+            $request_fault = $error_code >> 8;
+        }
+    }
+    # set the request's state to stop
+    {
+        my $command = "$pstamptool -updatereq -req_id $req_id -state stop -fault $request_fault";
+        $command   .= " -dbname $dbname" if $dbname;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            die("Unable to perform $command error code: $error_code");
+        }
+    }
+}
+
+sub stop_request {
+    my $req_id = shift;
+    my $fault  = shift;
+    my $dbname = shift;
+
+    my $command = "$pstamptool -updatereq -req_id $req_id -state stop -fault $fault";
+    $command   .= " -dbname $dbname" if $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+}
+
+sub get_request_info {
+    my $rows = shift;
+    my $rownum = shift;
+
+    my $row = $rows->{$rownum};
+
+    # This is ugly, error prone and hard to change.
+    # Create a results file module and provide a list of the names (we have the data in the columns)
+    my $rowinfo = "$row->{PROJECT}|$row->{JOB_TYPE}|$row->{REQ_TYPE}|$row->{IMG_TYPE}|";
+    $rowinfo   .= "$row->{ID}|$row->{CLASS_ID}|$row->{OPTION_MASK}|$row->{MJD_MIN}|$row->{MJD_MAX}|";
+    $rowinfo   .= "$row->{REQFILT}|$row->{COORD_MASK}|$row->{CENTER_X}|$row->{CENTER_Y}|";
+    $rowinfo   .= "$row->{WIDTH}|$row->{HEIGHT}";
+
+    return ($row, $rowinfo, $row->{PROJECT});
+}
+
+sub get_exposure_info {
+    my $image_db= shift;
+    my $exp_id = shift;
+
+    if (!$exp_id) {
+        # no exposure id just return zeros
+        # XXX: we could put a value in for filter, but we don't have a good place to find it
+
+        #"$mjd_obs|$ra_obs|$dec_obs|$filter|$exp_time|$fpa_id";
+        return "0|0|0|0|0|0";
+    }
+
+    my $regtool = can_run('regtool') or (warn "Can't find regtool" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $command = "$regtool -processedexp -dbname $image_db -exp_id $exp_id";
+
+    # run the tool and parse the output
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+    unless ($success) {
+        # not sure if we should die here
+        die "cannot get exposure information for $exp_id from image database $image_db";
+    }
+    my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+    my $output = join "", @$stdout_buf;
+    if (!$output) {
+        print STDERR "no output returned from $command\n" if $verbose;
+        return undef;
+    }
+    my $metadata = $mdcParser->parse($output) or die("Unable to parse metdata config doc");
+
+    my $exposures = parse_md_list($metadata);
+    my $numExp = @$exposures;
+
+    die "unexpected number of exposures $numExp found for exp_id: $exp_id in DB: $image_db" if $numExp != 1;
+    
+    my $exp = $exposures->[0];
+
+    #my $info = "$mjd_obs|$ra_obs|$dec_obs|$filter|$exp_time|$fpa_id";
+
+    use constant RADIANS_TO_DEGREES => 90. / atan2(1, 0);
+    my $ra_deg   = $exp->{ra} * RADIANS_TO_DEGREES;
+    my $decl_deg = $exp->{decl} * RADIANS_TO_DEGREES;
+    my $mjd_obs = dateobs_to_mjd($exp->{dateobs}, $exp->{exp_time});
+
+    my $info = "$mjd_obs|$ra_deg|$decl_deg|$exp->{filter}|$exp->{exp_time}|$exp->{exp_name}";
+            
+    return $info;
+}
+
+sub dateobs_to_mjd {
+    my $dateobs = shift;
+    my $exp_time = shift;
+
+    # dateobs is in format: 1970-01-01T00:00:00
+
+    my ($date, $time) = split "T", $dateobs;
+    my ($year, $mon, $day) = split "-", $date;
+    my ($hr, $min, $sec) = split ":", $time;
+
+    my $ticks = timegm($sec, $min, $hr, $day, $mon-1, $year-1900);
+
+    $ticks += $exp_time / 2.0;
+
+    return 40587.0 + ($ticks/86400.);
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_get_image_job.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_get_image_job.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_get_image_job.pl	(revision 22322)
@@ -0,0 +1,166 @@
+#!/usr/bin/env perl
+#
+# Execute a get_image job for the postage stamp server
+#
+
+use strict;
+use warnings;
+
+use Getopt::Long qw( GetOptions );
+use Pod::Usage qw( pod2usage );
+use DBI;
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use File::Copy;
+use File::Basename;
+use Digest::MD5::File qw( file_md5_hex );
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+my $product;
+my $fileset;
+
+my $uri;
+my $out_dir;
+
+my $verbose = 1;
+my $ipprc;
+my $dbname;
+my $job_id;
+my $rownum;
+
+#
+# parse args
+#
+
+GetOptions(
+        'job_id=s'        =>      \$job_id,
+        'rownum=s'        =>      \$rownum,
+        'uri=s'           =>      \$uri,
+        'out_dir=s'       =>      \$out_dir,
+        'dbname=s'        =>      \$dbname,
+) or pod2usage(2);
+
+my $err = "";
+$err .= "--job_id is required\n" if (!$job_id);
+$err .= "--rownum is required\n" if (!$rownum);
+$err .= "--uri is required to specify the file list\n" if (!$uri);
+$err .= "--out_dir is required to specify the output fileset\n" if (!$out_dir);
+
+die $err if $err;
+
+# collapse any multiple slashes in output_dir
+$out_dir = File::Spec->canonpath($out_dir);
+
+my @path = split(/\//, $out_dir);
+
+my $nelem = @path;
+if ($nelem < 2) {
+    die("$out_dir is not a valid output fileset directory");
+}
+$fileset = $path[$nelem-1];
+$product = $path[$nelem-2];
+
+$_ = $out_dir;
+my ($ds_dir) = m%(.*)/$product/$fileset%;
+
+if ($verbose) {
+    print STDERR "DS root:  $ds_dir\n";
+    print STDERR "Product:  $product\n";
+    print STDERR "Fileset:  $fileset\n";
+    print STDERR "Filelist: $uri\n";
+}
+
+
+if (!stat("$ds_dir/index.txt")) {
+    $err .= "Data Store not found at '$ds_dir'.\n"
+}
+
+show_usage("Invalid product '$product'.")
+    unless defined stat("$ds_dir/$product/index.txt");
+
+if (! open(INPUT, "<$uri") ) {
+    die("failed to open file list: $uri");
+}
+
+my $reglist = "$out_dir/reglist";
+if (! open(REGLIST, ">$reglist$job_id") ) {
+    die("failed to open registration list: $reglist");
+}
+
+my @files;
+my $num = 0;
+while (<INPUT>) {
+    chomp;
+    my @fields = split /\|/;
+    my $pathname = shift @fields;
+    my $filetype = shift @fields;
+
+    ($pathname , my $filename) = resolvepath($pathname);
+
+    if ($verbose) {
+        print STDERR "$pathname @fields\n";
+    }
+
+    $num++;
+    $filename = "${rownum}_${num}_$filename";
+    my $dest_path = "$out_dir/$filename";
+    if (! copy $pathname, "$dest_path" ) {
+        die("failed trying to copy $pathname to $out_dir");
+    }
+
+    # XXX is pstamp always the right file type, if not where can we get the right one?
+    print REGLIST file_registration_line($filename, $dest_path, $filetype);
+
+    foreach my $f (@fields) {
+        print REGLIST "$f|";
+    }
+    print REGLIST "\n";
+}
+
+close(REGLIST);
+
+exit 0;
+
+sub resolvepath {
+    my $pathname = $_[0];
+
+    my $slash = index($pathname, "/");
+    if ($slash != 0) {
+        if (!$ipprc) {
+            $ipprc = PS::IPP::Config->new();
+        }
+        $pathname = $ipprc->convert_filename_absolute($pathname);
+    }
+
+    my $file = basename($pathname);
+
+    return ($pathname, $file);
+}
+
+# XXX move this to a module so the code can be shared 
+sub file_registration_line {
+    my $filename = shift;
+    my $path     = shift;
+    my $filetype = shift;
+    if (-e $path) {
+        my @finfo = stat($path);
+        die "failed to stat $path" unless (@finfo);    # XXX clean up
+        my $bytes = $finfo[7];
+        my $md5sum = file_md5_hex($path);
+
+        return "$filename|$bytes|$md5sum|$filetype|";
+    } else {
+        die "$filename not found at $path";
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_job_run.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_job_run.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_job_run.pl	(revision 22322)
@@ -0,0 +1,199 @@
+#!/bin/env perl
+###
+### pstamp_job_run.pl
+###
+###     Run a given postage stamp Job
+###
+
+use warnings;
+use strict;
+
+use Sys::Hostname;
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use File::Basename;
+use Digest::MD5::File qw( file_md5_hex );
+
+# values for OPTION_MASK
+# XXX put these in a module
+use constant PSTAMP_SELECT_IMAGE  => 1;
+use constant PSTAMP_SELECT_MASK   => 2;
+use constant PSTAMP_SELECT_WEIGHT => 4;
+
+
+my $verbose;
+my $dbname;
+my $job_id;
+
+my $host = hostname();
+
+
+GetOptions(
+    'job_id=s'  =>  \$job_id,
+    'dbname=s'  =>  \$dbname,
+    'verbose'   =>  \$verbose,
+);
+
+
+if ($verbose) {
+    print "\n\n";
+    print "Starting script $0 on $host\n\n";
+}
+
+die "job_id is required" if !$job_id;
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+
+my $missing_tools;
+
+my $pstamptool = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+my $ppstamp    = can_run('ppstamp') or (warn "Can't find ppstamp" and $missing_tools = 1);
+my $pstamp_get_image_job    = can_run('pstamp_get_image_job.pl') or (warn "Can't find pstamp_get_image_job.pl" and $missing_tools = 1);
+
+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 $psjob;
+#Look up the uri for the given job
+{
+    my $command = "$pstamptool -pendingjob -job_id $job_id";
+    $command .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform pstamptool -pendingjob: $error_code");
+    }
+
+    if (@$stdout_buf == 0) {
+        print STDERR "pending pstamp job id $job_id not found\n";
+        exit 0;
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        die("Unable to parse metdata config doc");
+
+    my $jobs = parse_md_list($metadata);
+    my $num = @$jobs;
+    die "unexpected number of jobs $num found for job: $job_id" if $num > 1;
+
+    $psjob = $jobs->[0];
+}
+
+if (!$psjob) {
+    print STDERR "postage stamp job $job_id not found\n";
+    exit 1;
+}
+
+my $rownum = $psjob->{rownum};
+my $uri = $psjob->{uri};
+my $outputBase = $psjob->{outputBase};
+my $argString = $psjob->{args};
+my $jobType = $psjob->{jobType};
+
+my $jobStatus;
+if ($jobType eq "stamp") {
+    my $command = "$ppstamp -file $uri $outputBase $argString";
+    $command .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+
+    if ($success) {
+        # XXX shouldn't need to do this, review schema`
+        my $dir = dirname($outputBase);
+
+        my $reglist = "$dir/reglist$job_id";
+
+        open F, ">$reglist" or die "can't open $reglist for output";
+
+        # we are assuming an interface with ppstamp here.
+        my @extensions = ( "fits", "mk.fits", "wt.fits");
+
+        # XXX TODO: add mask of expected file types to pstampJob so we know what to expect
+
+        my $output_mask = PSTAMP_SELECT_IMAGE;
+        my $m = 1;  # XXX we're getting a bit intimate with the bit field definitions here. do better use a hash
+        foreach my $extension (@extensions) {
+            my $do_this_one = $m & $output_mask;
+            $m = $m << 1;
+            next if (! $do_this_one);
+
+            my $basename = basename($outputBase);
+
+            my $filename = "${basename}.${extension}";
+            my $path  = "${outputBase}.${extension}";
+
+            # XXX is pstamp always the right file type, if not where can we get the right one?
+            print F file_registration_line($filename, $path, "pstamp") . "\n";
+        }
+
+        close F;
+        $jobStatus = $PS_EXIT_SUCCESS;
+    } else {
+        $jobStatus = $error_code >> 8;
+        print STDERR "ppstamp failed with error code: $jobStatus\n";
+    }
+} elsif ($jobType eq "get_image") {
+    my $command = "$pstamp_get_image_job --job_id $job_id --uri $uri --out_dir $outputBase --rownum $rownum";
+    $command .= " --dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+
+    if ($success) {
+        $jobStatus = $PS_EXIT_SUCCESS;
+    } else {
+        $jobStatus = $error_code >> 8;
+        print STDERR "ppstamp failed with error code: $jobStatus\n";
+    }
+} elsif ($jobType eq "detect_query") {
+    die("multiple detect_query jobs not supported yet");
+} else {
+    die("unknown jobType $jobType found");
+}
+
+# stop the job and set the result value
+{
+    my $command = "$pstamptool -updatejob -job_id $job_id -state stop -fault $jobStatus";
+    $command .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command: $error_code");
+    }
+}
+
+exit $jobStatus;
+
+# XXX move this to a module so it can be shared
+sub file_registration_line {
+    my $filename = shift;
+    my $path     = shift;
+    my $filetype = shift;
+    if (-e $path) {
+        my @finfo = stat($path);
+        die "failed to stat $path" unless (@finfo);    # XXX clean up
+        my $bytes = $finfo[7];
+        my $md5sum = file_md5_hex($path);
+
+        return "$filename|$bytes|$md5sum|$filetype|";
+    } else {
+        die "$filename not found at $path";
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_listjobs.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_listjobs.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_listjobs.pl	(revision 22322)
@@ -0,0 +1,120 @@
+#!/bin/env perl
+###
+### pstamp_listjobs.pl
+### list the job_id, state, and data store uri for the postage stamp jobs queued for a given 
+### postage stamp request id
+###
+
+use warnings;
+use strict;
+use Getopt::Long qw( GetOptions );
+
+my $verbose;
+my $dbname;
+
+GetOptions(
+    'verbose'   =>  \$verbose,
+    'dbname=s'  =>  \$dbname,
+);
+
+if (@ARGV != 1) {
+    die "usage: $0 request_id\n";
+}
+
+my $request_id = $ARGV[0];
+
+
+use Sys::Hostname;
+my $host = hostname();
+
+## This isn't a script to be launched by pantasks we probably want go get rid of this
+if ($verbose) {
+    print STDERR "\n\n";
+    print STDERR "Starting script $0 on $host\n\n";
+}
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+
+my $missing_tools;
+
+my $pstamptool  = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+my @psjobs;
+#Look up the jobs for the given request_id
+{
+    my $command = "$pstamptool -listjob -req_id $request_id";
+    $command .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform pstamptool -pendingreq: $error_code");
+    }
+
+    if (@$stdout_buf == 0) {
+        print STDERR "no pstamp jobs for request $request_id\n" if $verbose;
+        exit 0;
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        die("Unable to parse metdata config doc");
+
+    my $jobs = parse_md_list($metadata);
+
+    @psjobs = @$jobs;
+}
+
+if (! @psjobs) {
+    print STDERR "no postage stamp jobs for request $request_id\n";
+    exit 0;
+}
+
+foreach my $job (@psjobs) {
+    #
+    # convert from filename to data store relative uri
+    # XXX arguably we shouldn't do this here.
+    # This should be a function of the web interface. However, since this script
+    # is only used by the web interface ....
+    my $i = index($job->{outputBase}, "dsroot/");
+
+    my $add_fits = 0;
+    if ($job->{jobType} ne "get_image") {
+        $add_fits = 1;
+    }
+
+    my $uri;
+    if ($i > 0) {
+        $uri = "/ds" . substr($job->{outputBase}, $i + 6);
+    } else {
+        $uri = "$job->{outputBase}";
+    }
+
+    if ($add_fits) {
+        $uri .= ".fits";
+    }
+
+    print "$job->{job_id} $job->{state} $uri\n";
+}
+
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_new_request.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_new_request.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_new_request.sh	(revision 22322)
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+#
+# pstamp_new_request.sh
+#
+# create a postage stamp request file and add it to the data store
+
+# This is a simple program for testing purposes only and not
+# part of the postage stamp server
+
+# TODO: use getopt and take these configuration variables as command
+# line arguments or convert this script to perl and use the ipp config
+
+DATA_STORE=/var/www/html/ds/dsroot
+PRODUCT=pstamprequest
+
+if [[ $# == 0 ]] ; then
+    echo "usage: $0 fileset_id pstamp_request_arguments"
+    exit 22
+fi
+
+fileset_id=$1
+shift
+
+dir_path=${DATA_STORE}/${PRODUCT}/${fileset_id}
+echo $dir_path
+if [[ -e $dir_path ]] ; then
+    echo fileset $fileset_id already exists in $dir_path
+    exit 1
+fi
+
+if ! mkdir -p $dir_path ; then
+    $status = $?
+    echo failed to mkdir $dir_path
+    exit $status
+fi
+
+request_file=${fileset_id}.fits
+
+pstamprequest ${dir_path}/${request_file} -req_name $fileset_id $*
+status=$?
+if [[ $status != 0 ]] ; then
+    echo pstamprequest failed: $status
+    rm -r $dir_path
+    exit $status
+fi
+
+# Invoke the data store registration script
+echo $request_file\|\|\|psrequest\| | dsreg --add $fileset_id --type PSREQUEST --product $PRODUCT  --list -
+
+status=$?
+if [[ $status != 0 ]] ; then
+    echo dsreg failed: $status
+    rm -r $dir_path
+    exit $status
+fi
+
+exit 0
+
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_parser_run.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_parser_run.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_parser_run.pl	(revision 22322)
@@ -0,0 +1,289 @@
+#!/bin/env perl
+###
+### pstampparser_run.pl
+###     Run the request parser for a given request id
+### This script should be called request_parser.pl since it handles more than postage
+### stamp requests
+###
+
+use warnings;
+use strict;
+
+use Sys::Hostname;
+use Getopt::Long qw( GetOptions );
+
+my $verbose;
+my $dbname;
+my $req_id;
+
+GetOptions(
+    'verbose'   =>  \$verbose,
+    'dbname=s'  =>  \$dbname,
+    'req_id=s'  =>  \$req_id,
+);
+
+if ($verbose) {
+    my $host = hostname();
+    print "\n\n";
+    print "Starting script $0 on $host\n\n";
+}
+
+die "req_id is required" if !defined($req_id);
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+
+my $ipprc = PS::IPP::Config->new(); # IPP Configuration
+
+my $outputDataStoreRoot = metadataLookupStr($ipprc->{_siteConfig}, 'DATA_STORE_ROOT');
+exit ($PS_EXIT_CONFIG_ERROR) unless defined $outputDataStoreRoot; # lookup failure outputs a message
+my $defaultOutputRoot = $outputDataStoreRoot;
+
+my $pstamp_workdir = metadataLookupStr($ipprc->{_siteConfig}, 'PSTAMP_WORKDIR');
+exit ($PS_EXIT_CONFIG_ERROR) unless defined $pstamp_workdir; # lookup failure outputs a message
+
+# workdir is where we download request files to and place any error output from the parser
+# NOTE: this location needs to be kept in sync with the web interface ( request.php )
+my $workdir = "$pstamp_workdir/$req_id";
+
+if (! -e $workdir ) {
+    mkdir $workdir or die "failed to create working directory $workdir for request id $req_id";
+}
+
+my $defaultDSProduct = metadataLookupStr($ipprc->{_siteConfig}, 'PSTAMP_DATA_STORE_PRODUCT');
+exit ($PS_EXIT_CONFIG_ERROR) unless defined $defaultDSProduct;
+    
+my $missing_tools;
+
+my $pstamptool  = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+my $pstampparse = can_run('pstampparse.pl') or (warn "Can't find pstampparse.pl" and $missing_tools = 1);
+my $dqueryparse = can_run('dqueryparse.pl') or (warn "Can't find dqueryparse.pl" and $missing_tools = 1);
+my $dsget = can_run('dsget') or (warn "Can't find dsget" 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 $psrequest;
+#Look up the uri for the given request
+{
+    my $command = "$pstamptool -pendingreq -req_id $req_id";
+    $command .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+
+    if (@$stdout_buf == 0) {
+        print STDERR "pending pstamp request id $req_id not found\n";
+        exit 1;
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        die("Unable to parse metdata config doc");
+
+    my $requests = parse_md_list($metadata);
+
+    my $num = @$requests;
+    die "unexpected number of requests $num found" if $num > 1;
+
+    $psrequest = $requests->[0];
+}
+
+if (!$psrequest) {
+    print STDERR "pending postage stamp request $req_id not found\n";
+    exit 1;
+}
+
+my $uri = $psrequest->{uri};
+
+# if the uri is from a data store, download it 
+
+my $ds_id = $psrequest->{ds_id};
+
+my $new_uri;
+if ($ds_id && ($uri =~ /^http:/)) {
+    {
+        $new_uri = "$workdir/request.fits";
+        my $command = "$dsget --uri $uri --filename $new_uri";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            die("Unable to perform $command error code: $error_code");
+        }
+
+        $uri = $new_uri;
+    }
+}
+
+#  find the name of the output directory
+my $product;
+if ($ds_id) {
+    my $command = "$pstamptool -datastore -ds_id $ds_id";
+    $command .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+
+    if (@$stdout_buf == 0) {
+        print STDERR "data store with data store id $ds_id not found\n";
+        exit 1;
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        die("Unable to parse metdata config doc");
+
+    my $dataStores = parse_md_list($metadata);
+
+    my $dataStore = $dataStores->[0];
+
+    $product = $dataStore->{outProduct};
+} else {
+    $product = $defaultDSProduct;
+}
+
+#
+# Here is one of the places that we assume that the output for a request goes directly into
+# the output fileset directory in the data store.
+#
+my $outProductDir = "$outputDataStoreRoot/$product";
+
+die "output product directory $outProductDir does not exist" if (! -e $outProductDir) ;
+die "file with name of output product directory $outProductDir exists but is not a directory" 
+    if (! -d $outProductDir);
+
+my $parse_cmd;
+my $request_type;
+my $reqType;    # for the database
+
+if (-r $uri) {
+    # run the appropriate parse command to parse the queue the jobs for this request
+    # first check the extension header to find the EXTNAME
+    $request_type = find_request_type($uri);
+
+    if ($request_type) {
+        print STDERR "request_type for $req_id is $request_type\n" if $verbose;
+        if ($request_type eq "PS1_PS_REQUEST") {
+            $reqType = 'pstamp';
+            $parse_cmd = $pstampparse;
+        } elsif ($request_type eq "MOPS_DETECTABILITY_QUERY") {
+            $reqType = 'dquery';
+            $parse_cmd = $dqueryparse;
+        } else {
+            print STDERR "Unknown request type $request_type found in $uri";
+        }
+    } else {
+        print STDERR "No EXTNAME found keyword in $uri";
+    }
+} else {
+    if (-e $uri) {
+        print STDERR "Request file $uri is not readable";
+    } else {
+        print STDERR "Request file $uri does not exist";
+    }
+}
+
+if (!$parse_cmd) {
+    # can't go any farther, set fault and set request state to run 
+    # request_finish.pl will clean up, perhaps notifiying the operator of the input data store
+    # that they sent us a request file that we don't understand
+
+    my $command = "$pstamptool -updatereq -req_id $req_id -state run";
+    $command   .= " -reqType unknown";
+    $command   .= " -fault $PS_EXIT_DATA_ERROR";
+    $command   .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+    exit $PS_EXIT_DATA_ERROR;
+}
+
+$parse_cmd .= " --mode queue_job --req_id $req_id --product $product --out_dir $outProductDir --file $uri";
+$parse_cmd .= " --dbname $dbname" if $dbname;
+
+my $fault;
+{
+    my $error_file_name = "$workdir/parse_error.txt";
+    # get rid of any error file from previous attempt to parse this request
+    unlink $error_file_name if (-e $error_file_name);
+
+    my $command = "$parse_cmd";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+
+    # save the contents of stderr (if any) to a file. This is relevant if
+    # the file was parseable but one or more of the rows in the request file generated an error
+    my $errbuf = join "", @$stderr_buf;
+    if ($errbuf) {
+        if (!open OUT, ">$error_file_name") {
+            print STDERR ("unable to open parse_error file $error_file_name");
+        } else {
+            print OUT "$errbuf";
+            close(OUT);
+        }
+        print STDERR $errbuf if $verbose;
+    }
+
+    unless ($success) {
+        $fault = $error_code >> 8;
+    }
+}
+
+#
+# update the state of this request from 'new' to 'run' and set the uri to the downloaded location
+#
+{
+    my $command = "$pstamptool -updatereq -req_id $req_id -state run";
+    $command   .= " -reqType $reqType" if $reqType;
+    $command   .= " -uri $new_uri" if $new_uri;
+    $command   .= " -fault $fault" if $fault;
+    $command   .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+}
+
+# Note: We do not return $fault here. If there was a fatal error we've already exited.
+# If we got a fault it's due to bad input from the user we've set things up for this to be
+# handled by the task pstamp.request.finish
+
+exit 0;
+
+
+sub find_request_type {
+    # find the EXTNAME in the input fits table
+    my $file_name = shift;
+    my $out = `echo $file_name | fields -x 0 EXTNAME`;
+
+    if ($out) {
+        # output from fields is filename value
+        my ($dummy, $extname) = split " ", $out;
+
+        return $extname;
+    } else {
+        return undef;
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_queue_requests.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_queue_requests.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_queue_requests.pl	(revision 22322)
@@ -0,0 +1,198 @@
+#!/bin/env perl
+#
+# pstamp_queue_reqests.pl
+#
+# Query registered data stores for new postage stamp requests and add them to
+# the database of pending requests.
+#
+
+use warnings;
+use strict;
+
+use Getopt::Long qw( GetOptions );
+
+use Sys::Hostname;
+my $host = hostname();
+
+my $verbose;
+my $dbname;
+my $limit;
+
+GetOptions(
+    'verbose'       =>  \$verbose,
+    'dbname=s'      =>  \$dbname,
+    'limit=i'       =>  \$limit,
+);
+
+if ($verbose) {
+    print STDERR "\n\n";
+    print STDERR "Starting script $0 on $host\n\n";
+}
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+
+my $missing_tools;
+
+my $pstamptool  = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+my $dsproductls = can_run('dsproductls') or (warn "Can't find dsproductls" and $missing_tools = 1);
+my $dsfilesetls = can_run('dsfilesetls') or (warn "Can't find dsfilesetls" 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 @dataStores;
+#Look up the list of data stores to monitor
+{
+    my $command = "$pstamptool -datastore";
+    $command .= " -dbname $dbname" if $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        my $rc = $error_code >> 8;
+        die("Unable to perform pstamptool -datastore: $rc");
+    }
+
+    if (@$stdout_buf == 0) {
+        print STDERR "no data stores registered\n" if $verbose;
+        exit 0;
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        die("Unable to parse metdata config doc");
+
+    my $ds = parse_md_list($metadata);
+
+    @dataStores = @$ds;
+}
+
+if (! @dataStores) {
+    print STDERR "no postage stamp data stores found\n" if $verbose;
+    exit 1;
+}
+
+# loop over the data stores
+foreach my $ds (@dataStores) {
+    my $lastFileset;
+
+    # skip any data stores that aren't enabled
+
+    if ($ds->{state} ne "enabled") {
+        next;
+    }
+
+    my $outProduct = $ds->{outProduct};
+    my $ds_id = $ds->{ds_id};
+    my @lines;
+    {
+        my $command = "$dsproductls --uri $ds->{uri}/index.txt";
+        $command .= " --last_fileset $ds->{lastFileset}" if $ds->{lastFileset};
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            die("Unable to perform $command: $error_code");
+        }
+
+        if (@$stdout_buf == 0) {
+            print STDERR "no new request files in data store $ds_id\n" if $verbose;
+            next; # next data store
+        }
+        my $out_buf = join("", @$stdout_buf);
+        # split raw output into lines
+        @lines = split /^/, $out_buf;
+    }
+
+    #
+    # each line contains a fileset
+    #
+
+    # number that we've processed
+    my $numFilesets = 0;
+    foreach my $line (@lines) {
+        # parse the line into fields split by whitespace
+        my ($uri, $fs_name, $date, $type) = split " ", $line;
+
+        # skip comment lines
+        next if ( $uri =~ /^#.*/);
+
+        $numFilesets++;
+
+        my @files;
+        {
+            my $command = "$dsfilesetls --uri $uri";
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run(command => $command, verbose => $verbose);
+            unless ($success) {
+                die("Unable to perform $command: $error_code");
+            }
+
+            if (@$stdout_buf == 0) {
+                print STDERR "no file sets found\n";
+                next;
+            }
+            my $out_buf = join "", @$stdout_buf;
+            @files = split /^/, $out_buf;
+        }
+
+        #
+        # For each file in the fileset add a request
+        #
+        foreach my $file (@files) {
+            my ($req_uri, $fn, $size, $md5sum, $type, $chipname) = split " ", $file;
+
+            # skip comment lines
+            next if $req_uri =~ (/^#.*/);
+            {
+                my $command = "$pstamptool -addreq -uri $req_uri -ds_id $ds_id";
+                $command .= " -dbname $dbname" if $dbname;
+
+                my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                        run(command => $command, verbose => $verbose);
+
+                unless ($success) {
+                    die("Unable to perform $command: $error_code");
+                }
+            }
+        }
+        $lastFileset = $fs_name;
+        last if ($numFilesets >= $limit);
+    }
+
+    ## now update the last_fileset column in pstampDataStore
+    ## XXX: we should perhaps do this while processing each fileset so that if a later
+    ## one has an error we don't get repeats.
+
+    if ($lastFileset) {
+        # print "last fileset: $lastFileset\n";
+        {
+        my $command = "$pstamptool -ds_id $ds_id -moddatastore -last_fileset $lastFileset";
+        $command .= " -dbname $dbname" if $dbname;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+            unless ($success) {
+                die("Unable to perform pstamptool -moddatastore: $error_code");
+            }
+        }
+    }
+}
+
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_results_file.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_results_file.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_results_file.pl	(revision 22322)
@@ -0,0 +1,291 @@
+#!/bin/env perl
+
+# program to create a fits binary table with EXTNAME PS1_PS_RESULTS
+
+use warnings;
+use strict;
+
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use Math::Trig;
+use Data::Dumper;
+
+use constant EXTNAME => 'PS1_PS_RESULTS'; # Extension name for output table
+use constant EXTVER =>  1;
+
+my ( $input,			# Name of input Detectabilty Query table
+     $output,			# Name of output table
+     $save_temps,		# Save temporary files?
+     );
+
+GetOptions(
+	   'input|i=s'    => \$input,
+	   'output|o=s'   => \$output,
+	   'save-temps'   => \$save_temps,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --input --output",
+           -exitval => 3)
+    unless defined $input 
+    and defined $output;
+
+
+# The header kewords
+my $header = [
+        { name =>  'REQ_NAME', 
+                    writetype => TSTRING, 
+                    comment => 'Postage Stamp Request Name',
+                    value => undef
+        },
+        { name =>  'REQ_ID', 
+                    writetype => TLONGLONG, 
+                    comment => 'Postage Stamp Server Request id',
+                    value => undef
+        },
+];
+
+# Specification of columns to write
+my $columns = [ 
+        # rownum from original request
+        { name => 'ROWNUM',   type => 'V', writetype => TULONG }, 
+        # error code from processing this image
+        { name => 'ERROR_CODE',type => 'V',  writetype => TULONG }, 
+        # name of the postage stamp image
+        { name => 'IMG_NAME', type => '64A', writetype => TSTRING }, 
+        # job_id that produced this result
+        { name => 'JOB_ID',   type => 'K',   writetype => TLONGLONG }, 
+        ### metadata from stamp
+        #
+        # coordinate at center of stamp, in degrees
+        { name => 'RA_DEG',  type => 'D',   writetype => TDOUBLE },
+        # coordinate at center of stamp, in degrees
+        { name => 'DEC_DEG', type => 'D',   writetype => TDOUBLE },
+
+        ### metdata from parent exposure
+
+        # actual start time of exposure
+        { name => 'MJD_OBS',  type => 'D',   writetype => TDOUBLE },
+        # field center at midpoint of expsure, in degrees
+        { name => 'RA_OBS',  type => 'D',   writetype => TDOUBLE },
+        # field center at midpoint of expsure, in degrees
+        { name => 'DEC_OBS', type => 'D',   writetype => TDOUBLE },
+        # actual filter
+        { name => 'FILTER',  type => '16A', writetype => TSTRING }, 
+        # exposure time of parent image
+        { name => 'EXPTIME', type => 'D',   writetype => TDOUBLE },
+        # original FPA_ID used at ingest
+        { name => 'FPA_ID',  type => '20A', writetype => TSTRING }, 
+
+        # the following are copied from the original pstamp request
+
+        # image selection parameters
+        { name => 'PROJECT',    type => '16A', writetype => TSTRING }, 
+        { name => 'JOB_TYPE',   type => '16A', writetype => TSTRING },     
+        { name => 'REQ_TYPE',   type => '16A', writetype => TSTRING },        
+        { name => 'IMG_TYPE',   type => '16A', writetype => TSTRING },       
+        { name => 'ID',         type => '16A', writetype => TSTRING },            
+        { name => 'CLASS_ID',   type => '16A', writetype => TSTRING },    
+
+        # output parameters
+#        { name => 'STAMP_NAME', type => '16A', writetype => TSTRING },  
+        { name => 'OPTION_MASK',type => 'J', writetype   => TULONG },     
+
+        { name => 'MJD_MIN',   type => 'D', writetype   => TDOUBLE },
+        { name => 'MJD_MAX',   type => 'D', writetype   => TDOUBLE },       
+        { name => 'REQFILT',    type => '16A', writetype => TSTRING },
+
+        # 2 bits in COORD_MASK indicate what units of roi coords are
+        { name => 'COORD_MASK', type => 'J', writetype => TULONG },  
+        { name => 'CENTER_X',   type => 'D', writetype => TDOUBLE },    
+        { name => 'CENTER_Y',   type => 'D', writetype => TDOUBLE },   
+        { name => 'WIDTH',      type => 'D', writetype => TDOUBLE },   
+        { name => 'HEIGHT',     type => 'D', writetype => TDOUBLE },  
+];
+
+my $in;
+if ($input eq '-') {
+    $in = \*STDIN;
+} else {
+    open $in, "<$input" or die "cannot open $input for reading";
+}
+
+my @colData;
+foreach (@$columns) {
+    push @colData, [];
+}
+
+
+my $numRows = read_data_for_table($in,'\|', \@colData, $header); 
+if (!$numRows) {
+    print STDERR "no data in $input\n";
+    exit 1;
+}
+
+my $status = make_fits_table($output, EXTNAME, $numRows, \@colData, $columns, $header);
+
+exit $status;
+
+# TODO: put this in a module
+
+# two utility functions that may be used to create a FITS binary
+# table from hashes describing the header keywords and columns
+
+# read_table_description reads the data for a table from a simple text file
+# make_fits_table writes out the table to a named file
+
+
+# A function to build a fits binary table from supplied data 
+# 
+sub make_fits_table {
+        my $output = shift;     # name of output file
+        my $extname = shift;    # extension name
+        my $numRows = shift;    # number of rows in the table
+        my $colData = shift;    # ref to array of arrays containing the data for each column
+        my $columns = shift;    # ref to array of column descriptions (each a hash)
+                                # with keys: name, type, and writetype
+        my $header = shift;     # ref to array of header keyword descriptions - each a hash
+                                # with keys: name, name, writetype, comment, and value
+        my $status = 0;
+
+        die "incorrect arguments" if !defined($columns);
+        # note $header can be nil
+
+        # build arrays for cfitsio
+        my @colNames;			# Names of columns
+        my @colTypes;			# Types of columns
+        my @colWriteType;               # type to use to write
+
+        foreach my $colSpec ( @$columns) {
+            push @colNames, $colSpec->{name};
+            push @colTypes, $colSpec->{type};
+            push @colWriteType, $colSpec->{writetype};
+        }
+
+        if (-e $output) {
+            unlink "$output" or die "failed to remove existing $output";
+        }
+
+        my $outFits = Astro::FITS::CFITSIO::create_file( $output, $status ); # Output file handle
+        check_fitsio( $status );
+
+        $outFits->create_img( 16, 0, undef, $status );
+        check_fitsio( $status );
+
+        # Create the table
+
+        $outFits->create_tbl( BINARY_TBL(), $numRows, scalar @colNames,
+                                \@colNames, \@colTypes, undef, $extname, $status );
+        check_fitsio( $status );
+
+        # if header keyword descriptions were provided add them
+        if ($header) {
+            foreach my $headerword ( @$header ) {
+                my $value = $headerword->{value};
+                unless (defined $value) {
+                    print "Can't find header keyword $headerword\n";
+                    next;
+                }
+                # zap quotation marks
+                $value =~ s/\'//g;
+                my $name    = $headerword->{name};
+                my $type    = $headerword->{writetype};
+                my $comment = $headerword->{comment};
+                $outFits->write_key( $type, $name, $value, $comment, $status );
+                check_fitsio( $status );
+            }
+        }
+
+
+        for (my $i = 0; $i < scalar @colNames; $i++) {
+            my $writeType = $colWriteType[$i];
+            $outFits->write_col( $writeType, $i + 1, 1, 1, $numRows, $colData->[$i], $status );
+            check_fitsio( $status );
+        }
+
+        $outFits->close_file( $status );
+
+        return 0;
+
+} # end of sub make_fits_table
+
+
+
+# read the table contents from a file
+#
+# input text file format:
+#   lines that begin with '#' are comment lines and are skipped.
+#   other lines are data. Each data line is split into fields with the
+#   provided separator
+#
+# if $header is not null header the first non-commented line is read to
+# fill the value for each header keyword. The number of fields must match
+# the number of keywords.
+#
+# Following the optional header data, each data line contains data for each
+# row in the table. The number of fields must match the number of column
+# arrays provided.
+
+sub read_data_for_table {
+    my $in      = shift;    # input file handle
+    my $sep     = shift;    # string containing field separator
+    my $colData = shift;    # reference to an array of arrays for the data
+    my $header  = shift;    # rerence to array of header keyword descriptions
+
+    my $line_num = 0;
+
+    # read data for header if any data is expected
+    if ($header) {
+        my $nhead = @$header;
+        while (my $line = <$in>) {
+            $line_num++;
+            next if ($line =~ /^#/);    # skip comment lines
+            chomp $line;
+            my @vals = split /$sep/, $line;
+            my $nvals = @vals;
+            die "number of header columns in input $nvals does not equal expected number of header words $nhead"
+                    if (@vals != @$header);
+
+            for (my $i=0; $i < @$header; $i++) {
+                $header->[$i]->{value} = $vals[$i];
+            }
+
+            last; # only one header line
+        }
+    }
+
+    my $row_num = 0;
+    my $ncols = @$colData;
+    while (my $line = <$in>) {
+        $line_num++;
+        next if ($line =~ /^#/);    # skip comment lines
+        chomp $line;
+
+        my @vals = split /$sep/, $line;
+        my $nvals = @vals;
+        die "number of columns $nvals in input does not equal expected number of header "
+                . " words $ncols on line $line_num" if ($nvals != $ncols);
+
+        for (my $col = 0; $col < @$colData; $col++) {
+            $colData->[$col]->[$row_num] = $vals[$col];
+        }
+        $row_num++;
+    }
+
+    # we return the number of rows read
+    return $row_num;
+}
+
+# From Astro::FITS::CFITSIO demo
+sub check_fitsio
+{
+    my $status = shift;		# Status of FITSIO calls
+
+    if ($status != 0) {
+	my $msg;		# Message to output
+	Astro::FITS::CFITSIO::fits_get_errstatus( $status , $msg );
+	die "CFITSIO error: $msg\n";
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_revert_request.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_revert_request.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_revert_request.pl	(revision 22322)
@@ -0,0 +1,114 @@
+#!/bin/env perl
+
+# pstamp_finish.pl
+
+use warnings;
+use strict;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+use Sys::Hostname;
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Temp qw( tempfile );
+use File::Copy;
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+
+
+my ( $req_id, $dbname, $verbose, $save_temps );
+
+GetOptions(
+	   'req_id=s'   => \$req_id,
+	   'dbname=s'   => \$dbname,
+	   'verbose'    => \$verbose,
+	   'save-temps' => \$save_temps,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+my $missing_tools;
+
+my $pstamptool  = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+my $dsreg  = can_run('dsreg')  or (warn "Can't find dsreg"  and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+my $ipprc = PS::IPP::Config->new(); # IPP Configuration
+
+my $outputDataStoreRoot = metadataLookupStr($ipprc->{_siteConfig}, 'DATA_STORE_ROOT');
+exit ($PS_EXIT_CONFIG_ERROR) unless defined $outputDataStoreRoot; # lookup failure outputs a message
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+my $req;
+{
+    my $command = "$pstamptool -listreq -req_id $req_id";
+    $command   .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+    my $output = join "", @$stdout_buf;
+    if (!$output) {
+        if ($verbose) {
+            print STDERR "no requests found\n"
+        }
+        exit 0;
+    }
+    my $metadata = $mdcParser->parse($output) or die("Unable to parse metdata config doc");
+
+    my $requests = parse_md_list($metadata);
+
+    $req = $requests->[0];
+
+    die "request $req_id not found in metadata config doc" if !$req;
+}
+
+my $product = $req->{outProduct};
+
+my $req_name = $req->{name};
+
+# set the output fileset's name to the request name.
+my $fileset = $req_name;
+
+print STDERR "product: $product  REQ_NAME: $req_name\n" if $verbose;
+
+if ($product and $fileset) {
+    my $command = "$dsreg --del $fileset --product $product --rm --force";
+    $command .= " --dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die "Unable to perform $command error code: $error_code";
+    }
+}
+
+
+{
+    my $command = "$pstamptool -revertreq -req_id $req_id";
+    $command   .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_runcommand.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_runcommand.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_runcommand.sh	(revision 22322)
@@ -0,0 +1,66 @@
+#!/bin/sh
+###
+#
+# pstamp_runcommand.sh: 
+#
+# Set up the ipp environment to run a ipp commands on behalf of the postage stamp
+# server and execute the command given by the argument list 
+# This script is used by cgi or php scripts.
+
+# echo $# args are: $*
+
+if [[ $# -lt 4 ]] ;then
+    echo "usage $0 PSCONFDIR PSCONFIG WORKDIR command" >&2
+    #  EINVAL = 22
+    exit 22
+fi
+
+export PSCONFDIR=$1
+shift
+
+PSCONFIG=$1
+shift
+
+WORKDIR=$1
+shift
+export HOME=$WORKDIR
+
+
+cd $WORK_DIR
+status=$?
+if [[ $status != 0 ]] ; then
+    echo $0 cannot cd to WORK_DIR: $WORK_DIR status: $status >&2
+    exit $status
+fi
+
+###
+### configure the IPP
+###
+
+source $PSCONFDIR/psconfig.bash $PSCONFIG
+status=$?
+if [[ $status != 0 ]] ; then
+    echo error setting up IPP environment >&2
+    exit $status
+fi
+
+#echo path: $PATH >&2
+#echo command: $* >&2
+
+## make sure we are able to run the desired command
+## XXX: This test is sort of redundant. 
+## We'd get a more useful error by just going ahead and trying the command
+## or by not suppressing the error output of which
+if [[ ! -x `which $1 2> /dev/null` ]] ;then
+    status=$?
+    echo command $1 not found >&2
+    exit $status
+fi
+
+#### Finally, execute the command given by the command line arguments
+
+# make sure other users can modify files
+# todo: this is of course quite dangerous, avoid this by using proper group permissions
+umask 0
+
+$*
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_webrequest.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_webrequest.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstamp_webrequest.pl	(revision 22322)
@@ -0,0 +1,163 @@
+#!/bin/env perl
+###
+#
+# pstampwebrequest.pl: take a postage stamp request command line and process it
+#
+# The arguments are the command line parameters for the program pstamprequest
+#
+# Unless the argument -list is provided the output is the request id for the resulting request
+#
+# If -list is the first argument, the request file is built and parsed and the
+# the selected "input" uris are listed on stdout.
+#
+# Note: Despite the name there nothing particularly web specific about this program.
+#
+###
+
+use warnings;
+use strict;
+
+use Getopt::Long qw( GetOptions );
+use Sys::Hostname;
+
+my $host = hostname();
+my $verbose = 0;
+my $dbname;
+my $project;
+my $job_type;
+
+GetOptions(
+    'job_type=s'    =>  \$job_type,
+    'dbname=s'      =>  \$dbname,
+    'project=s'     => \$project,
+    'verbose'       => \$verbose,
+);
+
+
+
+if ($verbose) {
+    print "\n\n";
+    print "Starting script $0 on $host\n\n";
+}
+
+my $listMode;
+if ($job_type and ($job_type eq 'list_uri')) {
+    $listMode=1;
+} else  {
+    $listMode=0;
+}
+
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Config qw($PS_EXIT_SUCCESS
+		       $PS_EXIT_UNKNOWN_ERROR
+		       $PS_EXIT_SYS_ERROR
+		       $PS_EXIT_CONFIG_ERROR
+		       $PS_EXIT_PROG_ERROR
+		       $PS_EXIT_DATA_ERROR
+		       $PS_EXIT_TIMEOUT_ERROR
+		       metadataLookupStr
+		       metadataLookupBool
+		       caturi
+		       );
+
+use Cwd;
+
+my $missing_tools;
+
+my $pstamprequest = can_run('pstamprequest')  or (warn "Can't find pstamprequest"  and $missing_tools = 1);
+my $pstamptool = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+my $pstampparse = can_run('pstampparse.pl')  or (warn "Can't find pstampparse.pl"  and $missing_tools = 1);
+my $pstampparser_run = can_run('pstamp_parser_run.pl')  or (warn "Can't find pstamp_parser_run.pl"  and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+# make a request file
+my $cur_dir = getcwd();
+my $request_name = "web" . get_webreq_num();
+my $request_file = "$cur_dir/$request_name.fits";
+{
+    my $command = "$pstamprequest -req_name $request_name -project $project $request_file @ARGV";
+    $command .= " -$job_type" if $job_type;     # default job_type is pstamp
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        print STDERR @$stderr_buf;
+        die("Unable to perform pstamprequest: $error_code");
+    }
+}
+
+# ok at this point we have a request file add it to the database (unless we're in listMode)
+if ($listMode == 1 ) {
+    ###
+    ### In list mode just parse the file print the output and we're done
+    ###
+    my $command = "$pstampparse --mode list_uri --file $request_file";
+    $command .= " --dbname $dbname" if $dbname;
+    $command .= " --verbose" if $verbose;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+
+    if ($success) {
+        ### print "Matching Images:\n";
+        print @$stdout_buf;
+        exit 0;
+    } else {
+        # we send the output to STDOUT because that's where PHP finds it
+        print @$stdout_buf;
+        print @$stderr_buf;
+        exit 1;
+    }
+}
+
+# Queue the request
+my $req_id = 0;
+{
+
+    my $command = "$pstamptool -addreq -uri $request_file -ds_id 0";
+    $command .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        print STDERR @$stderr_buf;
+        die("Unable to perform pstamptool -addreq: $error_code");
+    }
+    $req_id = ${$stdout_buf}[0];
+}
+
+print "$req_id";
+
+exit 0;
+
+# Temporary hack
+# webrequest number is stored in a file in the current directory
+#
+sub get_webreq_num
+{
+    my $filename = "./webreq_num.txt";
+    if (! open IN, "+< $filename" ) {
+        my $initial_num = 1;
+        open IN, "> $filename" or die "can't open request $filename";
+        print IN "$initial_num\n" or die "failed to initialize $filename";
+        close IN;
+        return $initial_num;
+    }
+
+    my $webreq_num = <IN>;
+    chomp $webreq_num;
+
+    print STDERR "$webreq_num\n" if $verbose;
+
+    my $next = $webreq_num + 1;
+    truncate IN, 0;
+    seek IN, 0, 0;
+    print IN "$next\n";
+
+    close IN;
+    return $webreq_num;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstampparse.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstampparse.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/pstampparse.pl	(revision 22322)
@@ -0,0 +1,293 @@
+#!/bin/env perl
+###
+### pstampparse.pl
+###
+###     Run the postage stamp request parser for a given request id
+###
+
+use warnings;
+use strict;
+
+use Sys::Hostname;
+use Getopt::Long qw( GetOptions );
+use PStamp::RequestFile qw( :standard );
+use PStamp::Job qw( :standard );
+
+my $verbose;
+my $dbname;
+my $req_id;
+my $request_file_name;
+my $mode = "list_uri";
+my $out_dir;
+my $product;
+
+GetOptions(
+    'file=s'    =>  \$request_file_name,
+    'req_id=s'  =>  \$req_id,
+    'out_dir=s' =>  \$out_dir,
+    'product=s' =>  \$product,
+    'mode=s'    =>  \$mode,
+    'dbname=s'  =>  \$dbname,
+    'verbose'   =>  \$verbose,
+);
+
+die "invalid mode '$mode'" unless ($mode eq "list_uri") or ($mode eq "list_job") or ($mode eq "queue_job");
+die "file is required"     if !defined($request_file_name);
+
+if ($mode ne "list_uri") {
+    die "req_id is required"   if !$req_id;
+    die "out_dir is required"  if !$out_dir;
+    die "product is required"  if !$product;
+}
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config qw( :standard );
+my $ipprc = PS::IPP::Config->new(); # IPP Configuration
+
+my $missing_tools;
+
+my $pstamptool  = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+my $pstampdump  = can_run('pstampdump') or (warn "Can't find pstampdump" and $missing_tools = 1);
+my $fields  = can_run('fields') or (warn "Can't find fields" and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+#
+# get the data from the extension header
+#
+my $fields_output;
+{
+    my $command = "echo $request_file_name | $fields -x 0 EXTNAME EXTVER REQ_NAME";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    # fields doesn't return zero when it succeeds
+    #unless ($success) {
+    #    print STDERR @$stderr_buf;
+    #}
+    $fields_output = join "", @$stdout_buf;
+}
+my (undef, $extname, $extver, $req_name) = split " ", $fields_output;
+
+# make sure the file contains what we are expecting
+
+die "$request_file_name is not a PS1_PS_REQEST" if $extname ne "PS1_PS_REQUEST";
+die "REQ_NAME not found in $request_file_name"  if (!$req_name);
+die "wrong EXTVER $extver found in $request_file_name" if ($extver ne "1");
+
+if ($out_dir) {
+    $out_dir .= "/$req_name";
+    if (! -e $out_dir ) {
+        mkdir $out_dir or die "cannot create output directory $out_dir";
+    } elsif (! -d $out_dir ) {
+        die "output fileset directory $out_dir exists but is not a directory";
+    }
+}
+
+if ($req_id) {
+    my $command = "$pstamptool -updatereq -req_id $req_id  -name $req_name";
+    $command .= " -outProduct $product";
+    $command .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die "$command failed";
+    }
+}
+
+#
+# now convert the request table to an array of metadata config docs
+#
+
+my $rows;
+{
+    my $command = "$pstampdump $request_file_name";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        print STDERR @$stderr_buf;
+    }
+    my $table =  $mdcParser->parse(join "", @$stdout_buf) or
+        die("Unable to parse metdata config doc");
+
+    $rows = parse_md_list($table);
+}
+
+foreach my $row (@$rows) {
+    my $rownum   = $row->{ROWNUM};
+    my $job_type = $row->{JOB_TYPE};
+    my $project  = $row->{PROJECT};
+    my $req_type = $row->{REQ_TYPE};
+    my $img_type = $row->{IMG_TYPE};
+    my $id       = $row->{ID};
+    my $class_id = $row->{CLASS_ID};
+    my $stamp_name   = $row->{STAMP_NAME};
+    my $filter   = $row->{REQFILT};
+    my $mjd_min = $row->{MJD_MIN};
+    my $mjd_max = $row->{MJD_MAX};
+    my $x = $row->{CENTER_X};
+    my $y = $row->{CENTER_Y};
+    my $w = $row->{WIDTH};
+    my $h = $row->{HEIGHT};
+    my $coord_mask = $row->{COORD_MASK};
+    my $option_mask= $row->{OPTION_MASK};
+
+    $stamp_name = "" if $stamp_name eq "null";
+    $class_id = "" if ($class_id eq "null" or $class_id eq "all");
+    
+    # XXX: TODO: sanity check all parameters
+
+    # XXX: TODO: We shouldn't just die in this loop.
+    # If we encounter an error for a particular row
+    # add a job with the proper fault code. If there is a db or config error we should probably just
+    # trash the request.
+    die "job_type is list_uri but mode is $mode" if ($job_type eq "list_uri") and ($mode ne "list_uri");
+
+    my $proj_hash = resolve_project($ipprc, $project, $dbname);
+    die "project $project not found\n" unless $proj_hash;
+
+    my $image_db = $proj_hash->{dbname};
+    my $camera = $proj_hash->{camera};
+    my $need_magic = $proj_hash->{need_magic};
+
+    my $roi_string;
+    # XXX we're depending on other code to insure valid values for roi components
+    # this is checked in and ppstamp but I should check here so that we don't get that far,
+    # but not today
+    if ($x && ($x ne "null") && $y && ($y ne "null") && $w && ($w ne "null") && $h && ($h ne "null")) {
+        if ($coord_mask & $PSTAMP_CENTER_IN_PIXELS) {
+            $roi_string = "-pixcenter $x $y";
+        } else {
+            $roi_string = "-skycenter $x $y";
+        }
+        if ($coord_mask & $PSTAMP_RANGE_IN_PIXELS) {
+            $roi_string .= " -pixrange $w $h";
+        } else {
+            $roi_string .= " -arcrange $w $h";
+        }
+    }
+
+    die "region of interest is required to make postage stamps"
+        if (($job_type eq "stamp") && !defined($roi_string));
+
+    # find the uris for this request
+
+    if ($req_type eq "bycoord") {
+        die "region of interest is required for request type bycoord" if !defined($roi_string) ;
+        die "center must be specified in sky coordintes for bycoord" 
+            if ($coord_mask & $PSTAMP_CENTER_IN_PIXELS);
+    }
+
+    # Call PStamp::Job's locate_images routinte to get the parameters for this request specification
+    my $images = locate_images($ipprc, $image_db, $req_type, $img_type, $id, $class_id,
+            $x, $y, $mjd_min, $mjd_max, $filter);
+
+    if ($mode eq "list_uri") {
+        foreach my $image (@$images) {
+            print "$image->{image}\n";
+        }
+    } elsif ($job_type eq "get_image") {
+        # XXX TODO: Get rid of this block and use the same code as the stamp jobs
+        # XXX This doesn't work to get the mask and weight images
+        my $listfile = "$out_dir/filelist";
+
+        open LISTFILE, ">$listfile"
+            or die "failed to open file list: $listfile while parsing get_image request $req_id";
+
+        foreach my $image (@$images) {
+            print LISTFILE "$image->{image}|$img_type\n";
+        }
+        close LISTFILE;
+        my $command = "$pstamptool -addjob -req_id $req_id -job_type get_image"
+                    . " -uri $listfile -outputBase $out_dir -rownum $rownum";
+        $command .= " -dbname $dbname" if $dbname;
+        if ($mode eq "list_job") {
+            print "$command\n";
+        } else {
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+            unless ($success) {
+                print STDERR @$stderr_buf;
+                # XXX TODO: now what? Should we mark the error state for the request?
+                die "failed to queue job for request $req_id: rownum: $rownum";
+            }
+        }
+    } else {
+        my $args = $roi_string ? $roi_string : "";
+        $args .= " -class_id $class_id" if $class_id;
+
+        # sequence number for the the job for a request spec. Not to be confused with job_id
+        my $job_num = 0;
+
+        # XXX TODO: for raw and chip level images and class_id "null" if there are multiple images
+        # use -list instead of -file
+        foreach my $image (@$images) {
+            my $uri = $image->{image};
+            my $exp_id = $image->{exp_id};
+
+            $job_num++;
+
+            my $output_base = "$out_dir/${rownum}_${job_num}";
+
+            # By commenting out the next line, I hereby drop the use of STAMP_NAME
+            # $output_base .= "_${stamp_name}" if $stamp_name;
+
+            # add astrometry file for raw and chip images if one is available
+            if (($img_type eq "chip") || ($img_type eq "raw")) {
+                $args .= " -astrom $image->{astrom}" if $image->{astrom};
+            }
+
+            if ($option_mask & $PSTAMP_SELECT_MASK) {
+                $args .= " -mask $image->{mask}";
+            }
+            if ($option_mask & $PSTAMP_SELECT_WEIGHT) {
+                $args .= " -weight $image->{weight}";
+            }
+
+            # XXX sounds like magic will be handled outside of the postage stamp server
+            #if ($need_magic) {
+            #    die "no magic mask available" if ! $image->{magic_mask};
+            #    $args .= " -magic_mask $image->{magic_mask}" 
+            #}
+
+            # XXX: TODO: here is where we need to check whether or not the source inputs still exist
+            # and if not, queue a regeneration job and set the job state appropriately.
+
+            my $state = "run";
+
+            my $command = "$pstamptool -addjob -req_id $req_id -job_type $job_type"
+                . " -uri $uri -outputBase $output_base -args '$args' -rownum $rownum"
+                . " -state $state";
+            $command .= " -exp_id $exp_id" if $exp_id;
+            $command .= " -dbname $dbname" if $dbname;
+
+            if ($mode eq "list_job") { 
+                # this is a debugging mode, just print the pstamptool that would have run
+                # this is sort of like the mode -noupdate that some other tools support
+                print "$command\n";
+            } else {
+                # mode eq "queue_job"
+                my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run(command => $command, verbose => $verbose);
+                unless ($success) {
+                    print STDERR @$stderr_buf;
+                    # XXX TODO: now what? Should we mark the error state for the request?
+                    # should we keep going for other uris? If so how do we report that some
+                    # of the work that the request wanted isn't going to get done
+                    die "failed to queue job for request $req_id";
+                }
+            }
+        }
+    }
+}
+
+exit 0;
Index: /tags/sj_tags/sj_root_20080929/pstamp/scripts/request_finish.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/scripts/request_finish.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/scripts/request_finish.pl	(revision 22322)
@@ -0,0 +1,85 @@
+#!/bin/env perl
+
+# request_finish.pl
+
+use warnings;
+use strict;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+use Sys::Hostname;
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Temp qw( tempfile );
+use File::Copy;
+
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config qw( :standard );
+
+my ( $req_id, $req_name, $req_file, $req_type, $product, $dbname, $verbose, $save_temps );
+
+GetOptions(
+           'req_id=s'   => \$req_id,
+           'req_name=s' => \$req_name,
+           'req_file=s' => \$req_file,
+           'req_type=s' => \$req_type,
+           'product=s'  => \$product,
+	   'dbname=s'   => \$dbname,
+	   'verbose'    => \$verbose,
+	   'save-temps' => \$save_temps,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+my $err = "";
+
+$err .= "--req_id is required\n" if !$req_id;
+$err .= "--req_type is required\n" if !$req_type;
+$err .= "--req_file is required\n" if !$req_file;
+$err .= "--req_name is required\n" if !$req_name;
+$err .= "--product is required\n" if !$product;
+
+die "$err" if $err;
+
+my $missing_tools;
+
+my $pstamptool  = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
+my $pstamp_finish  = can_run('pstamp_finish.pl')  or (warn "Can't find pstamp_finish.pl"  and $missing_tools = 1);
+my $dquery_finish  = can_run('dquery_finish.pl')  or (warn "Can't find dquery_finish.pl"  and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+my $finish_cmd;
+if ($req_type eq 'pstamp') {
+    $finish_cmd = $pstamp_finish;
+} elsif ($req_type eq 'dquery') {
+    $finish_cmd = $dquery_finish;
+}
+if ($finish_cmd) {
+    my $command = $finish_cmd . " --req_id $req_id --req_name $req_name --req_file $req_file --product $product";
+    $command   .= " --dbname $dbname" if $dbname;
+    $command   .= " --verbose" if $verbose;
+    $command   .= " --save-temps" if $save_temps;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+} else {
+    # Unknown request type.
+    # Since we don't have a req_name there's not much we can do so just the request's state to stop.
+    print STDERR "request  $req_id has unknown reqType $req_type\n" if $verbose;
+    my $command = "$pstamptool -updatereq -req_id $req_id -state stop -fault $PS_EXIT_DATA_ERROR";
+    $command   .= " -dbname $dbname" if $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/.cvsignore	(revision 22322)
@@ -0,0 +1,14 @@
+ppstamp
+locateimages
+pstamprequest
+pstampfinish
+pstampdump
+Makefile
+Makefile.in
+.deps
+config.h
+config.h.in
+stamp-h1
+pstampErrorCodes.c
+pstampErrorCodes.h
+.libs
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/Makefile.am	(revision 22322)
@@ -0,0 +1,52 @@
+bin_PROGRAMS = ppstamp pstamprequest pstampdump
+
+noinst_HEADERS = \
+	ppstamp.h  \
+        pstampErrorCodes.h
+
+ppstamp_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) 
+pstamprequest_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) 
+pstampdump_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS)
+
+ppstamp_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS) 
+pstamprequest_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS) 
+pstampdump_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS)
+
+ppstamp_SOURCES = \
+	ppstamp.c \
+	ppstampArguments.c \
+	ppstampCleanup.c \
+        pstampErrorCodes.c \
+	ppstampMakeStamp.c \
+	ppstampMosaic.c \
+	ppstampOptions.c \
+	ppstampParseCamera.c \
+        ppstampRegion.c \
+	ppstampVersion.c \
+	pstampGetROI.c
+
+pstamprequest_SOURCES = \
+	pstamprequest.c \
+	pstampGetROI.c \
+	ppstampOptions.c
+
+pstampdump_SOURCES = \
+        pstampdump.c
+
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
+### Error codes.
+BUILT_SOURCES = pstampErrorCodes.h pstampErrorCodes.c
+CLEANFILES = pstampErrorCodes.h pstampErrorCodes.c
+
+pstampErrorCodes.h : pstampErrorCodes.dat pstampErrorCodes.h.in
+	$(ERRORCODES) --data=pstampErrorCodes.dat --outdir=. pstampErrorCodes.h
+
+pstampErrorCodes.c : pstampErrorCodes.dat pstampErrorCodes.c.in pstampErrorCodes.h
+	$(ERRORCODES) --data=pstampErrorCodes.dat --outdir=. pstampErrorCodes.c
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstamp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstamp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstamp.c	(revision 22322)
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppstamp.h"
+
+int main(int argc, char **argv)
+{
+    ppstampOptions *options = NULL;
+    int exitCode;
+
+    psTimerStart(TIMERNAME);
+    psLibInit(NULL);
+    pstampErrorRegister();
+
+    pmConfig *config = ppstampArguments(argc, argv, &options);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse command-line arguments.");
+        ppstampCleanup(config, options);
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // define the active I/O files
+    if (!ppstampParseCamera(config)) {
+        psErrorStackPrint(stderr, "Unable to parse camera.");
+        ppstampCleanup(config, options);
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // find the pixels that we need to copy, setup the output image
+    if (ppstampMakeStamp(config, options)) {
+        exitCode = 0;
+    } else {
+        exitCode = PS_EXIT_DATA_ERROR;
+    }
+
+    psLogMsg ("ppstamp", 3, "Complete ppstamp run: %f sec\n", psTimerMark(TIMERNAME));
+
+    // Cleaning up
+    ppstampCleanup(config, options);
+
+    return exitCode;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstamp.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstamp.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstamp.h	(revision 22322)
@@ -0,0 +1,41 @@
+#ifndef PP_STAMP_H
+#define PP_STAMP_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "pstamp.h"
+#include "ppstampOptions.h"
+
+#define TIMERNAME "ppstamp"             // Name of timer
+
+// Determine the processing options
+bool *ppstampOptionsParse(pmConfig *config);
+
+// Get the configuration
+pmConfig *ppstampArguments(int argc, char **argv, ppstampOptions **);
+
+// Determine what type of camera, and initialise
+bool ppstampParseCamera(pmConfig *config);
+
+bool ppstampMakeStamp(pmConfig *config, ppstampOptions *);
+pmFPAfile * ppstampBuildMosaic(pmConfig *config, pmFPAfile *input, pmFPAview *view);
+
+// free memory, check for leaks
+void ppstampCleanup (pmConfig *config, ppstampOptions *options);
+
+/// Return short version information
+psString ppstampVersion(void);
+
+/// Return long version information
+psString ppstampVersionLong(void);
+
+/// Update the metadata with version information for all dependencies
+void ppstampVersionMetadata(psMetadata *metadata, ppstampOptions *options);
+
+psRegion *ppstampCellRegion(const pmCell *cell);
+psRegion *ppstampChipRegion(const pmChip *chip);
+
+extern bool ppstampMegacamWorkaround;
+#endif
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampArguments.c	(revision 22322)
@@ -0,0 +1,108 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <pslib.h>
+#include "ppstamp.h"
+#include "ppstampOptions.h"
+#include <ctype.h>
+
+static void usage (void)
+{
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "USAGE: ppstamp -skycenter RA DEC -pixrange dx dy    [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
+    fprintf(stderr, "       ppstamp -skycenter RA DEC -arcrange dRA dDEC [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
+    fprintf(stderr, "       ppstamp -pixcenter x y    -pixrange dx dy    [-class_id class_id] [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
+    fprintf(stderr, "       ppstamp -pixcenter x y    -arcrange dRA dDEC [-class_id class_id] [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "-skycenter is specified as HH:MM:SS DD:MM:SS or decimal degrees\n");
+    fprintf(stderr, "-arcrange values are seconds of arc\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "Optional arguments:\n");
+    fprintf(stderr, "   [-class_id class_id]     selects class_id (only used with -pixcenter)\n");
+    fprintf(stderr, "   [-astrom astrom.cmp] : provide an alternative astrometry calibration\n");
+    fprintf(stderr, "   [-mask   mk_image] :   mask image\n");
+    fprintf(stderr, "   [-weight wt_image] :   weight image\n");
+    fprintf(stderr, "\n");
+
+    exit (2);
+}
+
+pmConfig *ppstampArguments(int argc, char **argv, ppstampOptions **pOptions)
+{
+    int argnum;                         // Argument number of interest
+    bool gotCenter = false;
+    bool gotRange = false;
+    ppstampOptions *options;
+
+    if (argc == 1) {
+        usage();
+    }
+
+    if (psArgumentGet (argc, argv, "-version")) {
+        psString version;
+        version = ppstampVersionLong();   fprintf (stdout, "%s\n", version); psFree (version);
+        version = psModulesVersionLong(); fprintf (stdout, "%s\n", version); psFree (version);
+        version = psLibVersionLong();     fprintf (stdout, "%s\n", version); psFree (version);
+        exit (0);
+    }
+
+    // load the site-wide configuration information
+    pmConfig *config = pmConfigRead(&argc, argv, NULL);
+    if (config == NULL) {
+        psError(PSTAMP_ERR_CONFIG, false, "Can't read site configuration!\n");
+        return(NULL);
+    }
+
+    options = ppstampOptionsAlloc();
+    *pOptions = options;
+
+    if (!pstampGetROI(&options->roip, &argc, argv, &gotCenter, &gotRange)) {
+        usage();
+    }
+
+    if (!gotCenter) {
+        psError(PSTAMP_ERR_ARGUMENTS, true, "must specify center of region of interest\n");
+        usage();
+    }
+
+    if (!gotRange) {
+        psError(PSTAMP_ERR_ARGUMENTS, true, "must specify extent of region of interest\n");
+        usage();
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-class_id"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        options->chipName = psStringCopy(argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    pmConfigFileSetsMD(config->arguments, &argc, argv, "ASTROM", "-astrom", "-astromlist");
+    pmConfigFileSetsMD(config->arguments, &argc, argv, "MASK",   "-mask", "-masklist");
+    pmConfigFileSetsMD(config->arguments, &argc, argv, "WEIGHT", "-weight", "-weightlist");
+
+    // the input file is a required argument; if not found, we will exit
+    bool status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
+    if (!status) {
+        psError(PSTAMP_ERR_ARGUMENTS, true, "must specify INPUT\n");
+        usage();
+    }
+    
+
+    // finally the output file
+    if (argc < 2) {
+        psError(PSTAMP_ERR_ARGUMENTS, true, "must specify OUTPUT\n");
+        usage();
+    } else if (argc > 2) {
+        psError(PSTAMP_ERR_ARGUMENTS, true, "unknown argument(s)\n");
+        usage();
+    }
+
+    // Add the output image (which remains on the command-line) to the arguments list
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
+
+    // psMetadataPrint(stdout, config->arguments,0);
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampCleanup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampCleanup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampCleanup.c	(revision 22322)
@@ -0,0 +1,25 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppstamp.h"
+
+void ppstampCleanup (pmConfig *config, ppstampOptions *options)
+{
+    // Free memory used by ppstamp
+    psFree(config);
+    psFree(options);
+
+    // Free memory used by psModules
+    pmConceptsDone();
+    pmConfigDone();
+    pmModelClassCleanup();
+
+    // Free memory used by psLib
+    psLibFinalize();
+
+    // psMemCheckLeaks (0, NULL, stderr, false);
+    // fprintf (stderr, "Found %d leaks in %s\n", psMemCheckLeaks (0, NULL, NULL, false), "ppstamp");
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampMakeStamp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampMakeStamp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampMakeStamp.c	(revision 22322)
@@ -0,0 +1,656 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <strings.h>
+
+#include "ppstamp.h"
+#include "pmAstrometryUtils.h"
+
+#define WCS_NONLIN_TOL 0.001            // Non-linear tolerance for header WCS
+
+typedef enum {
+    PPSTAMP_OFF,
+    PPSTAMP_PARTIALLY_ON,
+    PPSTAMP_ON,
+    PSTAMP_ERROR
+} ppstampOverlap;
+
+static void skyToChip(pmAstromObj *pt, pmFPA *fpa, pmChip *chip);
+
+// convert the input chip's transforms to the output
+static bool convertWCS(pmHDU *outHDU, pmFPA *outFPA, pmChip *outChip, pmChip *inChip, psRegion *roi)
+{
+    pmHDU *hdu = pmHDUFromChip(inChip);
+    PS_ASSERT_PTR_NON_NULL(hdu, 1)
+    PS_ASSERT_PTR_NON_NULL(hdu->header, 1)
+
+    // Change the reference pixel to account for the change in origin between the stamp and
+    // the original image
+    outChip->toFPA   = psPlaneTransformSetCenter(NULL, inChip->toFPA, (int) roi->x0, (int) roi->y0);
+
+    outChip->fromFPA = psPlaneTransformInvert(NULL, outChip->toFPA, *roi, 50);
+
+    // remove these keys which may have been copied from the input header
+    // XXX pmAstromWriteWCS should do this since it's the one that's inserting
+    // the PC00* style keywords
+    if (psMetadataLookup(outHDU->header, "CD1_1")) {
+        psMetadataRemoveKey(outHDU->header, "CD1_1");
+        psMetadataRemoveKey(outHDU->header, "CD1_2");
+        psMetadataRemoveKey(outHDU->header, "CD2_1");
+        psMetadataRemoveKey(outHDU->header, "CD2_2");
+    }
+
+    if (!pmAstromWriteWCS(outHDU->header, outFPA, outChip, WCS_NONLIN_TOL)) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to write WCS to output\n");
+        return false;
+    }
+
+    return true;
+}
+
+static bool copyMetadata(pmFPAfile *output, pmFPAfile *input, pmChip *inChip, ppstampOptions *options)
+{
+    pmChip    *outChip;
+    pmFPAview *view = pmFPAviewAlloc(0);
+
+    // our output file has a single chip
+    view->chip = 0;
+    outChip = pmFPAviewThisChip(view, output->fpa);
+    psFree(view);
+
+    // copy data from the input chip header to the output.
+    // since some of the keywords might be duplicated we may not want to copy both
+
+    // copy the fpa concepts
+    outChip->parent->concepts = psMetadataCopy(outChip->parent->concepts, inChip->parent->concepts);
+
+    pmHDU *inHDU  = pmHDUFromChip(inChip);
+    pmHDU *outHDU = pmHDUGetHighest(output->fpa, outChip, NULL);
+
+    if (inHDU->header) {
+        outHDU->header = psMetadataCopy(outHDU->header, inHDU->header);
+    } else if (!outHDU->header) {
+        outHDU->header = psMetadataAlloc();
+    }
+
+
+    // If input had WCS convert it for stamp
+    if (input->fpa->toSky) {
+        // steal the input fpa's transforms
+        output->fpa->toTPA   = input->fpa->toTPA;
+        output->fpa->fromTPA = input->fpa->fromTPA;
+        output->fpa->toSky   = input->fpa->toSky;
+
+        // drop the references
+        input->fpa->toTPA   = 0;
+        input->fpa->fromTPA = 0;
+        input->fpa->toSky   = 0;
+
+        if (!convertWCS(outHDU, output->fpa, outChip, inChip, &options->roi)) {
+            return false;
+        }
+    } else {
+        psWarning("No WCS present in input\n");
+    }
+
+    ppstampVersionMetadata(outHDU->header, options);
+
+    return true;
+}
+
+// Extract pixels from an image. If part of the bounds of the region are
+// partially outside of the input those pixels are set to zero.
+psImage *extractStampOld(psImage *image, psRegion region)
+{
+    int width  = region.x1 - region.x0;
+    int height = region.y1 - region.y0;
+
+    if (width < 0) {
+        return NULL;
+    }
+    if (height < 0) {
+        return NULL;
+    }
+
+    int srcX  = region.x0;
+    int srcY  = region.y0;
+    int lastX = region.x1;
+    int lastY = region.y1;
+    if (lastX > (image->col0 + image->numCols)) {
+        lastX = image->col0 + image->numCols;
+    }
+    if (lastY > (image->row0 + image->numRows)) {
+        lastY = image->row0 + image->numRows;
+    }
+
+    int leftBlank = 0;
+    int dstX  = 0;
+    if (srcX < image->col0) {
+        leftBlank = image->col0 - srcX;
+        dstX += leftBlank;
+        srcX = 0;
+    }
+
+    int copyWidth  = lastX - srcX;
+    int rightBlank = width - copyWidth - leftBlank;
+    if (copyWidth <= 0) {
+        return NULL;
+    }
+
+    psImage *output = psImageAlloc(width, height, image->type.type);
+
+    psElemType  type = image->type.type;
+    psS32       elementSize =  PSELEMTYPE_SIZEOF(type);
+    int         copySize = copyWidth * elementSize;
+
+    for (int dstY=0 ; dstY < height; srcY++, dstY++) {
+        psU8 *pdst = output->data.U8[dstY];
+
+        if ((srcY < image->row0) || (srcY >= lastY)) {
+            // zero the row
+            memset(pdst, 0, width*elementSize);
+        } else {
+            psU8 *psrc = image->data.U8[srcY]  + (srcX * elementSize);
+
+            if (leftBlank) {
+                memset(pdst, 0, leftBlank*elementSize);
+            }
+
+            // copy copyWidth pixels from srcX,srcY to dstX, dstY
+            pdst += dstX*elementSize;
+            memcpy(pdst, psrc, copySize);
+
+            if (rightBlank) {
+                pdst += copyWidth * elementSize;
+                memset(pdst, 0, rightBlank);
+            }
+        }
+    }
+
+
+    return output;
+}
+// Extract pixels from an image. If part of the bounds of the region are
+// partially outside of the input those pixels are set to zero.
+static psImage *extractStamp(psImage *image, psRegion region, double value)
+{
+    int width  = region.x1 - region.x0;
+    int height = region.y1 - region.y0;
+
+    if (width < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "negative width\n");
+        return NULL;
+    }
+    if (height < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "negative height\n");
+        return NULL;
+    }
+
+    int srcX  = region.x0;
+    int srcY  = region.y0;
+    int lastX = region.x1;
+    int lastY = region.y1;
+    if (lastX > (image->col0 + image->numCols)) {
+        lastX = image->col0 + image->numCols;
+    }
+    if (lastY > (image->row0 + image->numRows)) {
+        lastY = image->row0 + image->numRows;
+    }
+
+    int leftBlank = 0;
+    int dstX  = 0;
+    if (srcX < image->col0) {
+        leftBlank = image->col0 - srcX;
+        dstX += leftBlank;
+        srcX = 0;
+    }
+
+    int copyWidth  = lastX - srcX;
+//    int rightBlank = width - copyWidth - leftBlank;
+    if (copyWidth <= 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "copyWidth is invalid: %d\n", copyWidth);
+        return NULL;
+    }
+
+    psImage *output = psImageAlloc(width, height, image->type.type);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "psImageAlloc failed\n");
+        return NULL;
+    }
+
+    if (!psImageInit(output, value)) {
+        psError(PS_ERR_UNKNOWN, false, "psInitImage failed\n");
+        return NULL;
+    }
+
+    psElemType  type = image->type.type;
+    psS32       elementSize =  PSELEMTYPE_SIZEOF(type);
+    int         copySize = copyWidth * elementSize;
+
+    int     dstY = 0;
+    psS32   skip = image->row0 - srcY;
+    if (skip > 0) {
+        dstY = skip;
+        height -= skip;
+        srcY = image->row0;
+    }
+    for ( ; dstY < height; srcY++, dstY++) {
+        if (srcY >= lastY) {
+            break;
+        }
+        psU8 *pdst = output->data.U8[dstY];
+
+        psU8 *psrc = image->data.U8[srcY]  + (srcX * elementSize);
+
+        // copy copyWidth pixels from srcX,srcY to dstX, dstY
+        pdst += dstX*elementSize;
+        memcpy(pdst, psrc, copySize);
+    }
+
+    return output;
+}
+
+// Build the postage stamp output file
+
+static bool makeStamp(pmConfig *config, ppstampOptions *options, pmFPAfile *input,
+                pmChip *inChip, pmFPAview *view)
+{
+    int status = false;
+
+    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PPSTAMP.OUTPUT");
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find output data\n");
+        return false;
+    }
+    char *fpaName = psMetadataLookupStr(NULL, input->fpa->concepts, "FPA.OBS"); // Name of FPA
+    pmFPAview *outview = pmFPAviewAlloc(0);
+    pmFPAAddSourceFromView(output->fpa, fpaName, outview, output->format);
+
+    outview->chip = 0;
+    outview->cell = 0;
+    pmCell *target =  pmFPAviewThisCell(outview, output->fpa); // Target cell
+    psFree(outview);
+    outview = NULL;
+
+    //   psMetadataPrint(stderr, inChip->concepts, 0);
+
+    // These default to zero. would that be ok?
+    psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.XBIN", PS_META_REPLACE, "Binning in x", 1);
+    psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.YBIN", PS_META_REPLACE, "Binning in y", 1);
+
+    pmFPAfile *srcFile;
+    pmFPAview *srcView = pmFPAviewAlloc(0);
+    if (psArrayLength(inChip->cells) > 1) {
+        // we extract our postage stamp from a mosaic so that
+        // we don't have to manage extracting from multiple cells
+        pmFPAfile *mosaic = ppstampBuildMosaic(config, input, view);
+        if (mosaic == NULL) {
+            return false;
+        }
+        srcFile = mosaic;
+        srcView->chip = 0;
+    } else {
+        srcFile = input;
+        *srcView = *view;
+    }
+
+    // At this point we know we have one cell
+    srcView->cell = 0;
+
+    pmReadout *readout;
+    while ((readout = pmFPAviewNextReadout (srcView, srcFile->fpa, 1)) != NULL) {
+        if (!readout->data_exists) {
+            psError(PS_ERR_UNKNOWN, false, "no data in input readout!\n");
+            continue;
+        }
+        pmReadout *outReadout = pmReadoutAlloc(target);
+        if (!outReadout) {
+            psError(PS_ERR_UNKNOWN, false, "failed to allocate output readout\n");
+            status = false;
+            break;
+        }
+
+        psRegion extractRegion = options->roi;
+
+        // Close your eyes while I hack around bug 986
+        if (ppstampMegacamWorkaround) {
+            // the coordinates of the mosaic are shifted 32 pixels from the chip
+            // TODO does this always apply? For example I doubt that applies to
+            // skycells.
+            extractRegion.x0 -= 32;
+            extractRegion.x1 -= 32;
+        }
+
+        outReadout->image = extractStamp(readout->image, extractRegion,  0);
+        if (!outReadout->image) {
+            psError(PS_ERR_UNKNOWN, false, "failed to create postage stamp image\n");
+            status = false;
+            break;
+        }
+
+        outReadout->data_exists = true;
+        outReadout->parent->data_exists = true;
+        outReadout->parent->parent->data_exists = true;
+        status = true;
+
+        psFree(outReadout); // drop reference
+    }
+
+    psFree(srcView);
+
+    if (status) {
+        status = copyMetadata(output, input, inChip, options);
+    }
+    return status;
+}
+
+
+
+static bool regionContainsPoint(psRegion *r, psPlane *pt)
+{
+    if (pt->x < r->x0)
+        return false;
+    if (pt->x >= r->x1)
+        return false;
+    if (pt->y < r->y0)
+        return false;
+    if (pt->y >= r->y1)
+        return false;
+
+    return true;
+}
+
+// true if the inner region is equal to or completely contained in
+// the outer region
+static bool regionContainsRegion(psRegion *outer, psRegion *inner)
+{
+    if ((outer->x0 <= inner->x0) &&
+        (outer->y0 <= inner->y0) &&
+        (outer->x1 >= inner->x1) &&
+        (outer->y1 >= inner->y1)) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+
+static void skyToChip(pmAstromObj *pt, pmFPA *fpa, pmChip *chip)
+{
+    // convert from sky to TP to FP
+    psProject(pt->TP, pt->sky, fpa->toSky);
+    psPlaneTransformApply(pt->FP, fpa->fromTPA, pt->TP);
+    // convert from FP to chip
+    psPlaneTransformApply(pt->chip, chip->fromFPA, pt->FP);
+}
+
+static void chipToSky(pmAstromObj *pt, pmFPA *fpa, pmChip *chip)
+{
+    // chip to FP
+    psPlaneTransformApply(pt->FP, chip->toFPA, pt->chip);
+    // FP to TP to sky
+    psPlaneTransformApply(pt->TP, fpa->toTPA, pt->FP);
+    psDeproject(pt->sky, pt->TP, fpa->toSky);
+
+}
+
+static void compareToBox(psRegion *region, psPlane *pt)
+{
+    if (pt->x < region->x0)
+        region->x0 = pt->x;
+    if (pt->x > region->x1)
+        region->x1 = pt->x;
+    if (pt->y < region->y0)
+        region->y0 = pt->y;
+    if (pt->y > region->y1)
+        region->y1 = pt->y;
+}
+
+// For roi width and height given in sky coordinates find a bounding box in chip coordinates
+// that encloses the requested range
+static void findBoundingBox(ppstampOptions *options, pmFPA *fpa, pmChip *chip, pmAstromObj *center)
+{
+    pmAstromObj *pt = pmAstromObjAlloc();
+
+    if (!options->roip.celestialCenter) {
+        // Center was specified in chip coordinates, transform to the sky
+        chipToSky(center, fpa, chip);
+    }
+
+    // calculate the four corners of the bounding box in sky coordinates, translate them to
+    // chip coordinates and build the ROI by comparison.
+
+    options->roi.x0 = INFINITY;
+    options->roi.x1 = -INFINITY;
+    options->roi.y0 = INFINITY;
+    options->roi.y1 = -INFINITY;
+
+    pt->sky->rErr = 0;
+    pt->sky->dErr = 0;
+
+    pt->sky->r = center->sky->r - options->roip.dRA/2;
+    pt->sky->d = center->sky->d - options->roip.dDEC/2;
+    skyToChip(pt, fpa, chip);
+    compareToBox(&options->roi, pt->chip);
+
+    pt->sky->r = center->sky->r + options->roip.dRA/2;
+    pt->sky->d = center->sky->d - options->roip.dDEC/2;
+    skyToChip(pt, fpa, chip);
+    compareToBox(&options->roi, pt->chip);
+
+    pt->sky->r = center->sky->r + options->roip.dRA/2;
+    pt->sky->d = center->sky->d + options->roip.dDEC/2;
+    skyToChip(pt, fpa, chip);
+    compareToBox(&options->roi, pt->chip);
+
+    pt->sky->r = center->sky->r - options->roip.dRA/2;
+    pt->sky->d = center->sky->d + options->roip.dDEC/2;
+    skyToChip(pt, fpa, chip);
+    compareToBox(&options->roi, pt->chip);
+
+    psFree(pt);
+
+    // save the width and height in case we need them later
+    options->roip.dX = options->roi.x1 - options->roi.x0;
+    options->roip.dY = options->roi.y1 - options->roi.y0;
+}
+
+// findROI
+// calculate the region of interest in chip coordinates and determine whether that region
+// is completely contained on a single chip
+
+static ppstampOverlap findROI(ppstampOptions *options, pmFPAview *view,
+                    pmFPAfile *input, pmFPAfile *astrom, bool bilevelAstrometry,
+                    pmChip *chip, pmAstromObj *center)
+{
+    psString chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
+    psRegion    *chipBounds = ppstampChipRegion(chip);
+    bool        onChip = false;
+    ppstampOverlap   returnval = PPSTAMP_OFF;
+
+//    fprintf(stderr, "ppstampChipBounds: %s\n", psRegionToString(*chipBounds));
+
+    // set up the astrometry
+    pmHDU *hdu = pmFPAviewThisHDU(view, astrom->fpa);
+    PS_ASSERT_PTR_NON_NULL(hdu, 1)
+    PS_ASSERT_PTR_NON_NULL(hdu->header, 1)
+
+    // we can live without astrometry if the ROI is completely specified in pixel coordinates
+    bool mustHaveAstrometry = false;
+    if (options->roip.celestialCenter || options->roip.celestialRange) {
+        mustHaveAstrometry = true;
+    }
+    if (bilevelAstrometry) {
+        if (!pmAstromReadBilevelChip (chip, hdu->header) && mustHaveAstrometry) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read bilevel chip astrometry for input FPA.");
+            return false;
+        }
+    } else {
+        // we use a default FPA pixel scale of 1.0
+        if (!pmAstromReadWCS (input->fpa, chip, hdu->header, 1.0) && mustHaveAstrometry) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read WCS astrometry for input FPA.");
+            return false;
+        }
+    }
+
+    if (options->roip.celestialCenter) {
+
+        center->sky->r = options->roip.centerRA;
+        center->sky->d = options->roip.centerDEC;
+        center->sky->rErr = 0;
+        center->sky->dErr = 0;
+
+        skyToChip(center, input->fpa, chip);
+
+        if (regionContainsPoint(chipBounds, center->chip)) {
+            psLogMsg("ppstampMakeStamp", 2, "Found center (%f %f) on chip: %s\n",
+                center->chip->x, center->chip->y, chipName);
+            onChip = true;
+        }
+    } else {
+        // center specified in pixels.
+        // If the user specified a name of a chip name wait until we get to that one.
+        // If no chip name was specified, select this one (the first one that had data)
+        if ((options->chipName == NULL) || !strcasecmp(chipName, options->chipName)) {
+            psLogMsg("ppstampMakeStamp", 2, "Center on chip: %s\n", chipName);
+            center->chip->x = options->roip.centerX;
+            center->chip->y = options->roip.centerY;
+            center->chip->xErr = 0;
+            center->chip->yErr = 0;
+            onChip = true;
+        }
+    }
+
+    if (onChip) {
+        if (options->roip.celestialRange) {
+            findBoundingBox(options, input->fpa, chip, center);
+        } else {
+            int width  = options->roip.dX;
+            int height = options->roip.dY;
+
+            // calculate the ROI in chip coordinates
+            options->roi.x0 = center->chip->x - width / 2;
+            options->roi.x1 = options->roi.x0 + width;
+            options->roi.y0 = center->chip->y - height / 2;
+            options->roi.y1 = options->roi.y0 + height;
+        }
+
+
+        if (regionContainsRegion(chipBounds, &options->roi)) {
+            // returnval = findCell(view, options, input, center, ppCell);
+            psLogMsg("ppstampMakeStamp", 2, "ROI contained on: %s\n", chipName);
+            returnval = PPSTAMP_ON;
+        } else {
+            psLogMsg("ppstampMakeStamp", 2, "Partial Overlap chip %s\n", chipName);
+            psLogMsg("ppstampMakeStamp", 2, "ROI:         %s\n", psRegionToString(options->roi));
+            psLogMsg("ppstampMakeStamp", 2, "Chip Extent: %s\n", psRegionToString(*chipBounds));
+
+            returnval = PPSTAMP_PARTIALLY_ON;
+        }
+    }
+    psFree(chipBounds);
+
+    return returnval;
+}
+
+bool ppstampMakeStamp (pmConfig *config, ppstampOptions *options)
+{
+    bool        status = false;
+    bool        returnval = false;;
+    bool        foundOverlap = false;
+
+    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPSTAMP.INPUT");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "Can't find input file!\n");
+        return false;
+    }
+
+    pmFPAfile *astrom = psMetadataLookupPtr(NULL, config->files, "PSWARP.ASTROM");
+    if (!astrom) {
+        astrom = input;
+    } else if (astrom->camera != input->camera) {
+        psError(PS_ERR_UNKNOWN, true, "Input camera and astrometry camera do not match");
+        return false;
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
+
+    // files associated with the science image
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to load input.");
+        psFree (view);
+        return false;
+    }
+    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) {
+        if (!pmAstromReadBilevelMosaic(input->fpa, phu->header)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read bilevel mosaic astrometry for input FPA.");
+            psFree(view);
+            return false;
+        }
+    }
+
+    // Loop over the chips and find the one that contains the center
+    pmAstromObj *center = pmAstromObjAlloc();
+    pmChip *chip;
+    while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
+        bool allDone = false;
+
+        if (!chip->process || !chip->file_exists) {
+            continue;
+        }
+
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to load chip");
+            status = false;
+            break;
+        }
+
+        ppstampOverlap overlap = findROI(options, view, input, astrom, bilevelAstrometry, chip, center);
+
+        switch (overlap) {
+        case PPSTAMP_OFF:
+            // keep looking
+            break;
+        case PPSTAMP_ON:
+        case PPSTAMP_PARTIALLY_ON:
+            returnval = makeStamp(config, options, input, chip, view);
+            allDone = true;
+            foundOverlap = true;
+            break;
+        case PSTAMP_ERROR:
+            returnval = false;
+            allDone = true;
+            break;
+        default:
+            psError(PS_ERR_PROGRAMMING, false, "findROI returned unexpected value %d\n", overlap);
+            break;
+        }
+
+        pmFPAfileIOChecks(config, view, PM_FPA_AFTER);
+
+        if (allDone) {
+            view->chip = -1;
+            break;
+        }
+    }
+    pmFPAfileIOChecks(config, view, PM_FPA_AFTER);
+
+    psFree(center);
+    psFree(view);
+
+    if (!foundOverlap) {
+        fprintf(stderr, "ROI not found in input\n");
+    }
+
+    return returnval;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampMosaic.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampMosaic.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampMosaic.c	(revision 22322)
@@ -0,0 +1,41 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppstamp.h"
+
+// Mosaic the cells of a given chip
+
+
+pmFPAfile *ppstampBuildMosaic(pmConfig *config, pmFPAfile *input, pmFPAview *view)
+{
+    bool    status;
+
+    pmFPAfile *mosaic =  psMetadataLookupPtr(&status, config->files, "PPSTAMP.CHIP");
+    if (!status)  {
+        psErrorStackPrint(stderr, "can't find mosaic i/o file\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAview *mosaicView = pmFPAviewAlloc(0);
+
+    mosaicView->chip = 0;
+    pmChip  *mChip  = pmFPAviewThisChip(mosaicView, mosaic->fpa);
+    pmChip  *inChip = pmFPAviewThisChip(view, input->fpa);
+    if (!mChip->hdu && !mChip->parent->hdu) {
+        const char *name = psMetadataLookupStr(&status, input->fpa->concepts, "FPA.OBS"); // Name of FPA
+        pmFPAAddSourceFromView(mosaic->fpa, name, mosaicView, mosaic->format);
+    }
+    psFree(mosaicView);
+
+    psMaskType blankMask = pmConfigMaskGet("BLANK", config);
+
+    status = pmChipMosaic(mChip, inChip, true, blankMask);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "chip mosaic failed\n");
+        return NULL;
+    }
+
+    return mosaic;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampOptions.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampOptions.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampOptions.c	(revision 22322)
@@ -0,0 +1,32 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+#include "ppstampOptions.h"
+
+
+static void pstampOptionsFree(ppstampOptions *options)
+{
+    psFree(options->chipName);
+}
+
+ppstampOptions *ppstampOptionsAlloc(void)
+{
+    ppstampOptions *options = psAlloc(sizeof(ppstampOptions));
+    psMemSetDeallocator(options, (psFreeFunc)pstampOptionsFree);
+
+    options->roip.celestialCenter = false;
+    options->roip.centerX         = 0;
+    options->roip.centerY         = 0;
+    options->roip.centerRA        = 0;
+    options->roip.centerDEC       = 0;
+    options->roip.celestialRange  = false;
+    options->roip.dX   = 0;
+    options->roip.dY   = 0;
+    options->roip.dRA  = 0;
+    options->roip.dDEC = 0;
+    options->chipName  = NULL;
+
+    return options;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampOptions.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampOptions.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampOptions.h	(revision 22322)
@@ -0,0 +1,23 @@
+#ifndef PPSTAMP_OPTIONS_H
+#define PPSTAMP_OPTIONS_H
+
+#include "pstampROI.h"
+
+// Options for ppstamp processing
+typedef struct {
+    // input arguments
+    pstampROI   roip;
+    psString    chipName;
+    psString    cellName;
+    //
+    // Calculated Values
+    //
+    psRegion    roi;            // roi in chip coordinates
+
+} ppstampOptions;
+
+ppstampOptions *ppstampOptionsAlloc(void);
+
+#endif
+
+
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampParseCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampParseCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampParseCamera.c	(revision 22322)
@@ -0,0 +1,76 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "ppstamp.h"
+
+bool ppstampMegacamWorkaround = false;
+
+// Set up the ppstamp output Image file
+bool setupOutput(pmConfig *config, pmFPAfile *input)
+{
+    pmFPAfile *output = pmFPAfileDefineSkycell(config, NULL, "PPSTAMP.OUTPUT");
+    output->save = true;
+    return true;
+}
+
+// This function seems mis-named. What are we doing with regards to the Camera?
+// Well we are building the output image based on the camera. Is there
+// something else that I'm missing?
+
+bool ppstampParseCamera(pmConfig *config)
+{
+    bool status = false;
+
+    // the input image defines the camera, and all recipes and options the follow
+    pmFPAfile *input = pmFPAfileDefineFromArgs (&status, config, "PPSTAMP.INPUT", "INPUT");
+    if (!status || !input) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from PPSTAMP.INPUT");
+        return false;
+    }
+    if (input->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPSTAMP.INPUT is not of type IMAGE");
+        return false;
+    }
+
+    // XXX: create a filerule for PPSTAMP.ASTROM
+    pmFPAfile *astrom = pmFPAfileDefineFromArgs(&status, config, "PSWARP.ASTROM", "ASTROM");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to find astrom definitiion");
+        return NULL;
+    }
+    if (astrom) {
+        psLogMsg ("ppstamp", 3, "using supplied astrometry\n");
+    } else {
+        psLogMsg ("ppstamp", 3, "using header astrometry\n");
+    }
+
+#ifdef notyet
+    // add recipe options supplied on command line
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, RECIPE_NAME);
+#endif
+
+    // Set up the output target
+    if (!setupOutput(config, input)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to setup output.");
+        return false;
+    }
+
+    pmFPAfile *chipImage = pmFPAfileDefineChipMosaic(config, input->fpa, "PPSTAMP.CHIP");
+    if (!chipImage) {
+        psError(PS_ERR_IO, false, _("Unable to generate new file from PPSTAMP.CHIP"));
+        return NULL;
+    }
+    if (chipImage->type != PM_FPA_FILE_IMAGE) {
+        psError(PS_ERR_IO, true, "PPSTAMP.CHIP is not of type IMAGE");
+        return NULL;
+    }
+
+    // XXX: TODO: only do the workaround if the file level is chip, skycells should be fine
+    if (!strcmp(config->cameraName, "MEGACAM")) {
+        // workaround bug 986 and related
+        ppstampMegacamWorkaround = true;
+    }
+ 
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampRegion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampRegion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampRegion.c	(revision 22322)
@@ -0,0 +1,121 @@
+#include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "ppstamp.h"
+
+// Functions to calculate the image space boundaries of a given Chip or Cell
+// These are calculated in coordinates that match the wcs transformation
+// (adapted from pmFPAExtent.c)
+
+// return cell pixels bounding the readout
+static psRegion *ppstampReadoutRegion(const pmReadout *readout)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, NULL);
+
+    psImage *image = readout->image;    // Image from which to get dimensions
+    if (!image) {
+        return NULL;
+    }
+
+    // This is the difference between this function and pmReadoutExtent. 
+    // pmReadoutExtent ignores the col0, row0 of the readout
+   
+    int col0 = 0;   // should be  image->col0 - readout->col0
+    int row0 = 0;   //            image->row0 - readout->row0
+   
+    return psRegionAlloc(col0, col0 + image->numCols,
+                         row0, row0 + image->numRows);
+}
+
+// return chip pixels bounding the cell (all readouts)
+psRegion *ppstampCellRegion(const pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    psArray *readouts = cell->readouts; // Array of component readouts
+    psRegion *cellExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of cell
+    for (long i = 0; i < readouts->n; i++) {
+        pmReadout *readout = readouts->data[i]; // Readout of interest
+        psRegion *roExtent = ppstampReadoutRegion(readout); // Extent of readout
+        cellExtent->x0 = PS_MIN(cellExtent->x0, roExtent->x0);
+        cellExtent->x1 = PS_MAX(cellExtent->x1, roExtent->x1);
+        cellExtent->y0 = PS_MIN(cellExtent->y0, roExtent->y0);
+        cellExtent->y1 = PS_MAX(cellExtent->y1, roExtent->y1);
+        psFree(roExtent);
+    }
+
+    bool mdok;                          // Status of MD lookup
+    int cellX0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0"); // Cell x offset
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find CELL.X0.\n");
+        psFree(cellExtent);
+        return NULL;
+    }
+
+    int cellY0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0"); // Cell y offset
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find CELL.Y0.\n");
+        psFree(cellExtent);
+        return NULL;
+    }
+    int xParity = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY");
+    int yParity = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY");
+
+    if (xParity >= 0) {
+        cellExtent->x0 += cellX0;
+        cellExtent->x1 += cellX0;
+    } else {
+        float x0 = cellX0 - cellExtent->x1;
+        float x1 = cellX0 - cellExtent->x0;
+        cellExtent->x0 = x0;
+        cellExtent->x1 = x1;
+    }
+
+    if (yParity >= 0) {
+        cellExtent->y0 += cellY0;
+        cellExtent->y1 += cellY0;
+    } else {
+        float y0 = cellY0 - cellExtent->y1;
+        float y1 = cellY0 - cellExtent->y0;
+        cellExtent->y0 = y0;
+        cellExtent->y1 = y1;
+    }
+
+    return cellExtent;
+}
+
+// return chip pixels included in all cells
+psRegion *ppstampChipRegion(const pmChip *chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    if (ppstampMegacamWorkaround) {
+        // There is an inconsistency in the megacam parameters. 
+        // The offset to the cells is effectively contained in two
+        // places.
+        // The value of CELL.X0 for the 2 cells is 0 and 1024 respectivly
+        // while the two values for readout->image.col0 = 32 and 1056
+        // This fact makes it impossible to calculate the Chip bounds 
+        // in a way consistent with say gpc1
+        // I'm deferring this problem for now.
+        // Since all chips on megacam have the same bounds, I just hard code
+        // the answer. See bug 986
+        return psRegionAlloc(32, 2080, 0, 4612);
+    }
+
+    psArray *cells = chip->cells;       // Array of component cells
+    psRegion *chipExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of chip
+    for (long i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        psRegion *cellExtent = ppstampCellRegion(cell); // Extent of cell
+        chipExtent->x0 = PS_MIN(chipExtent->x0, cellExtent->x0);
+        chipExtent->x1 = PS_MAX(chipExtent->x1, cellExtent->x1);
+        chipExtent->y0 = PS_MIN(chipExtent->y0, cellExtent->y0);
+        chipExtent->y1 = PS_MAX(chipExtent->y1, cellExtent->y1);
+        psFree(cellExtent);
+    }
+
+    return chipExtent;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/ppstampVersion.c	(revision 22322)
@@ -0,0 +1,56 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppstamp.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString ppstampVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString ppstampVersionLong(void)
+{
+    psString version = ppstampVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
+
+void ppstampVersionMetadata(psMetadata *metadata, ppstampOptions *options)
+{
+    PS_ASSERT_METADATA_NON_NULL(metadata,);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppstamp = ppstampVersionLong(); // ppstamp version
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString head = NULL;               // Head string
+    psStringAppend(&head, "ppstamp processing at %s. Component information:", timeString);
+    psFree(timeString);
+
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppstamp, "");
+    psString roi = NULL;
+    psStringAppend(&roi, "ppstamp: region of interest: %s", psRegionToString(options->roi));
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY",  PS_META_DUPLICATE_OK, roi, "");
+
+    psFree(roi);
+    psFree(head);
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(ppstamp);
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/pstamp.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/pstamp.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/pstamp.h	(revision 22322)
@@ -0,0 +1,46 @@
+#ifndef PSTAMP_H
+#define PSTAMP_H
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>  // for strcasecmp
+#include <unistd.h>   // for unlink
+#include "pslib.h"
+#include "psmodules.h"
+// #include "psphot.h"
+// #include "psastro.h"
+// #include "ppStats.h"
+#include "pstampErrorCodes.h"
+
+typedef enum {
+    PSTAMP_UNKNOWN = -1,
+    PSTAMP_RAW,
+    PSTAMP_CHIP,
+    PSTAMP_WARP,
+    PSTAMP_DIFF,
+    PSTAMP_STACK
+} pstampImageType;
+
+
+// command modes for pstampparse 
+typedef enum {
+    PSP_MODE_UNKNOWN = 0,
+    PSP_MODE_QUEUE_JOB,
+    PSP_MODE_LIST_URI,
+    PSP_MODE_LIST_JOB
+} pspMode;
+
+#define PSTAMP_SELECT_IMAGE  1
+#define PSTAMP_SELECT_MASK   2
+#define PSTAMP_SELECT_WEIGHT 4
+
+#define PSTAMP_CENTER_IN_PIXELS 1
+#define PSTAMP_RANGE_IN_PIXELS  2
+
+#define STAMP_REQUEST_EXTNAME "PS1_PS_REQUEST"
+#define STAMP_REQUEST_VERSION "1"
+
+#define STAMP_RESULTS_EXTNAME "PS1_PS_RESULTS"
+#define STAMP_RESULTS_VERSION "1"
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/pstampErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/pstampErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/pstampErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,25 @@
+/*
+ * The line
+    { PSTAMP_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+#include "pslib.h"
+#include "pstampErrorCodes.h"
+
+void pstampErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { PSTAMP_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PSTAMP_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = PSTAMP_ERR_NERROR - PSTAMP_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/pstampErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/pstampErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/pstampErrorCodes.dat	(revision 22322)
@@ -0,0 +1,10 @@
+#
+# This file is used to generate pstampErrorCodes.h
+#
+BASE = 800		First value we use; lower values belong to psLib
+UNKNOWN			Unknown PM error code
+NOT_IMPLEMENTED		Desired feature is not yet implemented
+ARGUMENTS		Incorrect arguments
+CONFIG			Problem in configure files
+IO			Problem in FITS I/O
+DATA                    Problem in data values
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/pstampErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/pstampErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/pstampErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,18 @@
+#if !defined(PSTAMP_ERROR_CODES_H)
+#define PSTAMP_ERROR_CODES_H
+/*
+ * The line
+ *  PSTAMP_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    PSTAMP_ERR_BASE = 650,
+    PSTAMP_ERR_${ErrorCode},
+    PSTAMP_ERR_NERROR
+} pstampErrorCode;
+
+void pstampErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/pstampGetROI.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/pstampGetROI.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/pstampGetROI.c	(revision 22322)
@@ -0,0 +1,190 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "pstamp.h"
+#include "pstampROI.h"
+#include "ohana.h"
+
+static bool get2Angles(int argnum, int *pArgc, char **argv, bool bothDegrees, bool makePositive, double *p1, double *p2);
+static bool get2Ints(int argnum, int *pArgc, char **argv, bool makePositive, int *p1, int *p2);
+
+
+bool pstampGetROI(pstampROI *roip, int *pArgc, char **argv, bool *gotCenter, bool *gotRange)
+{
+    int argnum;                         // Argument number of interest
+
+    if ((argnum = psArgumentGet(*pArgc, argv, "-pixcenter"))) {
+        *gotCenter = true;
+        roip->celestialCenter = false;
+        psArgumentRemove(argnum, pArgc, argv);
+
+        roip->center[0] = argv[argnum];
+        roip->center[1] = argv[argnum+1];
+
+        if (!get2Ints(argnum, pArgc, argv, false, &roip->centerX, &roip->centerY)) {
+            psError(PSTAMP_ERR_ARGUMENTS, true, "invalid pixcenter specification: %s %s\n",
+                argv[argnum], argv[argnum+1]);
+            return false;
+        }
+        psArgumentRemove(argnum, pArgc, argv);
+        psArgumentRemove(argnum, pArgc, argv);
+    }
+
+    if ((argnum = psArgumentGet(*pArgc, argv, "-skycenter"))) {
+        if (*gotCenter) {
+            psError(PSTAMP_ERR_ARGUMENTS, true, "can't specify both -pixcenter and -skycenter\n");;
+            return false;
+        }
+        psArgumentRemove(argnum, pArgc, argv);
+
+        roip->center[0] = argv[argnum];
+        roip->center[1] = argv[argnum+1];
+
+        double raDeg, decDeg;
+        if (!get2Angles(argnum, pArgc, argv, false, false, &raDeg, &decDeg)) {
+            psError(PSTAMP_ERR_ARGUMENTS, true, "invalid skycenter specification: %s %s\n",
+                argv[argnum], argv[argnum+1]);
+            return false;
+        }
+        psArgumentRemove(argnum, pArgc, argv);
+        psArgumentRemove(argnum, pArgc, argv);
+        roip->centerRA  = DEG_TO_RAD(raDeg);
+        roip->centerDEC = DEG_TO_RAD(decDeg);
+        *gotCenter = true;
+        roip->celestialCenter = true;
+    }
+
+    if ((argnum = psArgumentGet(*pArgc, argv, "-pixrange"))) {
+        *gotRange = true;
+        roip->celestialRange = false;
+        psArgumentRemove(argnum, pArgc, argv);
+        roip->range[0] = argv[argnum];
+        roip->range[1] = argv[argnum+1];
+
+        if (!get2Ints(argnum, pArgc, argv, true, &roip->dX, &roip->dY)) {
+            psError(PSTAMP_ERR_ARGUMENTS, true, "invalid pixrange specification: %s %s\n",
+                argv[argnum], argv[argnum+1]);
+            return false;
+        }
+        psArgumentRemove(argnum, pArgc, argv);
+        psArgumentRemove(argnum, pArgc, argv);
+    }
+
+    if ((argnum = psArgumentGet(*pArgc, argv, "-arcrange"))) {
+        if (*gotRange) {
+            psError(PSTAMP_ERR_ARGUMENTS, true, "specify only one of -pixrange or -arcrange\n");;
+            return false;
+        }
+        psArgumentRemove(argnum, pArgc, argv);
+        *gotRange = true;
+        roip->celestialRange = true;
+
+        roip->range[0] = argv[argnum];
+        roip->range[1] = argv[argnum+1];
+
+        // arcrange values are seconds of arc
+        roip->dRA  = SEC_TO_RAD(fabs(atof(argv[argnum])));
+        roip->dDEC = SEC_TO_RAD(fabs(atof(argv[argnum+1])));
+
+        psArgumentRemove(argnum, pArgc, argv);
+        psArgumentRemove(argnum, pArgc, argv);
+    }
+    // I'm leaving in the -celrange option (HH:MM:SS DD:MM:SS), but not publicizing it
+    if ((argnum = psArgumentGet(*pArgc, argv, "-celrange"))) {
+        if (*gotRange) {
+            psError(PSTAMP_ERR_ARGUMENTS, true, "specify one of -pixrange, -arcrange, or -celrange\n");;
+            return false;
+        }
+        psArgumentRemove(argnum, pArgc, argv);
+        *gotRange = true;
+        roip->celestialRange = true;
+
+        roip->range[0] = argv[argnum];
+        roip->range[1] = argv[argnum+1];
+
+        double deg1, deg2;
+        if (!get2Angles(argnum, pArgc, argv, false, true, &deg1, &deg2)) {
+            psError(PSTAMP_ERR_ARGUMENTS, true, "invalid celrange specification: %s %s\n",
+                argv[argnum], argv[argnum+1]);
+            return false;
+        }
+        psArgumentRemove(argnum, pArgc, argv);
+        psArgumentRemove(argnum, pArgc, argv);
+        roip->dRA = DEG_TO_RAD(deg1);
+        roip->dDEC = DEG_TO_RAD(deg2);
+    }
+
+    if (!*gotCenter) {
+        psError(PSTAMP_ERR_ARGUMENTS, true, "must specify center");
+        return false;
+    }
+    if (!*gotRange) {
+        psError(PSTAMP_ERR_ARGUMENTS, true, "must specify range for stamp");
+        return false;
+    }
+
+    return true;
+}
+
+static bool validNumber(char *string)
+{
+    char *p = string;
+
+    if ((*p == '+') || (*p == '-')) {
+        p++;
+    }
+    return isdigit(*p);
+}
+
+static bool get2Ints(int argnum, int *pArgc, char **argv, bool makePositive, int *p1, int *p2)
+{
+    if (*pArgc < 2) {
+        return false;
+    }
+
+    if (!validNumber(argv[argnum])) {
+        return false;
+    }
+    *p1 = atoi(argv[argnum]);
+
+    if (!validNumber(argv[argnum+1])) {
+        return false;
+    }
+    *p2 = atoi(argv[argnum+1]);
+
+    if (makePositive) {
+        *p1 = abs(*p1);
+        *p2 = abs(*p2);
+    }
+
+    return true;
+}
+
+static bool get2Angles(int argnum, int *pArgc, char **argv, bool bothInDegrees, bool makePositive,
+    double *p1, double *p2)
+{
+    bool rval;
+
+    if (*pArgc < 2) {
+        return false;
+    }
+
+    if (bothInDegrees) {
+        // both values are angles of arc DD:MM:SS or decimal degrees
+        rval   = ohana_dms_to_ddd(p1, argv[argnum]);
+        if (rval) {
+            rval  = ohana_dms_to_ddd(p1, argv[argnum+1]);
+        }
+    } else {
+        // first value may be in HH:MM:SS
+        rval = ohana_str_to_radec(p1, p2, argv[argnum], argv[argnum+1]);
+    }
+
+    if (rval && makePositive) {
+        *p1 = abs(*p1);
+        *p2 = abs(*p2);
+    }
+
+    return rval;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/pstampROI.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/pstampROI.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/pstampROI.h	(revision 22322)
@@ -0,0 +1,23 @@
+#ifndef PSTAMP_ROI_H
+#define PSTAMP_ROI_H
+
+// Struct defining the Postage Stamp Region of Interest
+
+typedef struct {
+    double centerRA;
+    double centerDEC;
+    int    centerX;
+    int    centerY;
+    double dRA;
+    double dDEC;
+    int    dX;
+    int    dY;
+    bool   celestialCenter;       // true if center is in RA/dec
+    bool   celestialRange;        // true if range is in RA/dec
+    char   *center[2];            // copy of command line strings for center (from argv don't free)
+    char   *range[2];             // copy of command line strings for range (from argv don't free)
+} pstampROI;
+
+bool pstampGetROI(pstampROI *roip, int *pArgc, char **argv, bool *gotCenter, bool *gotRange);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/pstampdump.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/pstampdump.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/pstampdump.c	(revision 22322)
@@ -0,0 +1,67 @@
+// pstampdump  - read a fits table describing a postage stamp request or response file and
+//               print the contents to stdout
+//
+//              Actually should work for any kind of fits table, but I'm not sure what will
+//              happen for binary data.
+
+#include <pslib.h>
+#include <psmodules.h>
+#include <string.h>
+
+static psArray *readRequestTable(psString fileName)
+{
+    psFits *fitsFile = psFitsOpen(fileName, "r");
+    if (fitsFile == NULL) {
+        psError(PS_ERR_IO, false, "failed to open %s for output", fileName);
+        return NULL;
+    }
+
+    psArray *array = psFitsReadTable(fitsFile);
+
+    psFitsClose(fitsFile);
+
+    if (array == NULL) {
+        psError(PS_ERR_IO, false, "psFitsReadTable failed for %s", fileName);
+        return NULL;
+    } 
+
+    return array;
+}
+
+int main(int argc, char *argv[])
+{
+    if (argc != 2) {
+        fprintf(stderr, "usage: %s filename\n", argv[0]);
+        return 1;
+    }
+
+    psString fileName = argv[1];
+
+    psArray *array = readRequestTable(fileName);
+    if (array == NULL) {
+        psErrorStackPrint(stderr, "failed to read fits table: %s\n", fileName);
+        return 1;
+    }
+
+    if (!psArrayLength(array)) {
+        fprintf(stderr, "%s is an empty table\n", fileName);
+        exit(1);
+    }
+
+//    printf("FITS_TABLE METADATA\n");
+    for (int i=0; i<psArrayLength(array); i++) {
+        printf("ROW_%d METADATA\n", i);
+        // psMetadataPrint(stderr, array->data[i], 0);
+        // psMetadataConfigWrite(array->data[i], "-");
+        psString str = psMetadataConfigFormat(array->data[i]);
+        if (!str) {
+            psErrorStackPrint(stderr, "Can't write to STDOUT\n");
+            exit(PS_EXIT_SYS_ERROR);
+        }
+        printf(str);
+        printf("END\n");
+    }
+//   printf("END\n");
+
+    return 0;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/src/pstamprequest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/src/pstamprequest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/src/pstamprequest.c	(revision 22322)
@@ -0,0 +1,423 @@
+#include <pslib.h>
+#include <string.h>
+#include "pstamp.h"
+#include "pstampROI.h"
+
+// pstamprequest  - create a fits table containing a postage stamp server request
+
+
+typedef struct {
+    psMetadata  *md;
+    psString    fileName;
+    psString    requestName;
+    bool        verbose;
+} psrOptions;
+
+char *string_columns[] = {
+    "PROJECT",
+    "JOB_TYPE",
+    "REQ_TYPE", // byid,  byexp,         bycoord
+    "ID",       // db id, exposure name, n/a
+    "CLASS_ID",
+    "REQFILT",
+    "STAMP_NAME",
+    NULL
+};
+char *double_columns[]= {
+    "CENTER_X",
+    "CENTER_Y",
+    "WIDTH",
+    "HEIGHT",
+    "MJD_MIN",
+    "MJD_MAX",
+};
+char *u32_columns[] = {
+    "ROWNUM",
+    "COORD_MASK",
+    "OPTION_MASK", // bitmask or of PSTAMP_SELECT_IMAGE PSTAMP_SELECT_MASK PSTAMP_SELECT_WEIGHT
+    NULL
+};
+
+static void usage(int exitStatus)
+{
+    psErrorStackPrint(stderr, "Unable to parse command-line arguments.");
+    exit(exitStatus);
+}
+
+static psMetadata *initializeTable()
+{
+    psMetadata *md = psMetadataAlloc();
+
+    //  unset columns with string type default to "null"
+    for (char **col_name = string_columns; *col_name != NULL; col_name++) {
+        psMetadataAddStr(md, PS_LIST_TAIL, *col_name, PS_META_DEFAULT, "", "null");
+    }
+    //  unset columns with u32 type default to 0
+    for (char **col_name = u32_columns; *col_name != NULL; col_name++) {
+        psMetadataAddU32(md, PS_LIST_TAIL, *col_name, PS_META_DEFAULT, "", 0);
+    }
+    //  unset columns with f64 type default to 0.
+    for (char **col_name = double_columns; *col_name != NULL; col_name++) {
+        psMetadataAddF64(md, PS_LIST_TAIL, *col_name, PS_META_DEFAULT, "", 0.0);
+    }
+    return md;
+}
+static void getId(char *idString, int argnum, int *pArgc, char *argv[], psrOptions *options)
+{
+    if (*pArgc < 2) {
+        fprintf(stderr, "must specify %s\n", idString);
+        usage(PS_EXIT_DATA_ERROR);
+    }
+    // catch common error
+    if (*argv[argnum] == '-') {
+        fprintf(stderr, "%s is not a valid %s\n", argv[argnum], idString);
+        usage(PS_EXIT_DATA_ERROR);
+    }
+
+    psMetadataAddStr (options->md, PS_LIST_TAIL, idString, PS_META_REPLACE, "", argv[argnum]);
+    psArgumentRemove(argnum, pArgc, argv);
+}
+
+
+static pstampImageType getType(int argnum, int *pArgc, char *argv[], psrOptions *options, char *paramName)
+{
+    if (paramName) {
+        if (*pArgc < (argnum+2)) {
+            fprintf(stderr, "must specify image type and %s\n", paramName);
+            usage(PS_EXIT_DATA_ERROR);
+        }
+    }  else {
+        if (*pArgc < (argnum+1)) {
+            fprintf(stderr, "must specify image type");
+            usage(PS_EXIT_DATA_ERROR);
+        }
+    }
+    char *type = argv[argnum];
+
+    psArgumentRemove(argnum, pArgc, argv);
+
+    pstampImageType itype = PSTAMP_UNKNOWN;
+
+    if (strcmp(type, "raw") == 0) {
+        itype = PSTAMP_RAW;
+    } else if (strcmp(type, "chip") == 0) {
+        itype = PSTAMP_CHIP;
+    } else if (strcmp(type, "warp") == 0) {
+        itype = PSTAMP_WARP;
+    } else if (strcmp(type, "diff") == 0) {
+        itype = PSTAMP_DIFF;
+    } else if (strcmp(type, "stack") == 0) {
+        itype = PSTAMP_STACK;
+    } else {
+        fprintf(stderr, "unknown image type %s\n", type);
+        usage(PS_EXIT_DATA_ERROR);
+    }
+    psMetadataAddStr (options->md, PS_LIST_TAIL, "IMG_TYPE", PS_META_REPLACE, "", type);
+
+    if (paramName) {
+        getId(paramName, argnum, pArgc, argv, options);
+    }
+
+    return itype;
+}
+
+static void doById(int argnum, int *pArgc, char *argv[], psrOptions *options)
+{
+    switch (getType(argnum, pArgc, argv, options, "ID")) {
+    case PSTAMP_RAW:
+        getId("CLASS_ID", argnum, pArgc, argv, options);
+        break;
+    case PSTAMP_CHIP:
+        getId("CLASS_ID", argnum, pArgc, argv, options);
+        break;
+    case PSTAMP_WARP:
+    case PSTAMP_DIFF:
+    case PSTAMP_STACK:
+        break;
+    default:
+        fprintf(stderr, "programming error unexpected image type\n");
+        exit(1);
+    }
+}
+
+static void doByExp(int argnum, int *pArgc, char *argv[], psrOptions *options)
+{
+    switch (getType(argnum, pArgc, argv, options, "ID")) {
+    case PSTAMP_RAW:
+        getId("CLASS_ID", argnum, pArgc, argv, options);
+        break;
+    case PSTAMP_CHIP:
+        getId("CLASS_ID", argnum, pArgc, argv, options);
+        break;
+    case PSTAMP_WARP:
+    case PSTAMP_DIFF:
+    case PSTAMP_STACK:
+        break;
+    default:
+        fprintf(stderr, "programming error unexpected image type\n");
+        exit(1);
+    }
+}
+static void doByCoord(int argnum, int *pArgc, char *argv[], psrOptions *options)
+{
+    switch (getType(argnum, pArgc, argv, options, NULL)) {
+    case PSTAMP_RAW:
+        getId("CLASS_ID", argnum, pArgc, argv, options);
+        break;
+    case PSTAMP_CHIP:
+        getId("CLASS_ID", argnum, pArgc, argv, options);
+        break;
+    case PSTAMP_WARP:
+    case PSTAMP_DIFF:
+    case PSTAMP_STACK:
+        break;
+    default:
+        fprintf(stderr, "programming error unexpected image type\n");
+        exit(1);
+    }
+}
+
+static psrOptions *parseArguments(int argc, char *argv[], int *pExitStatus)
+{
+    psrOptions *options = psAlloc(sizeof(psrOptions));
+    psMetadata *md = initializeTable();
+    int         argnum;
+    bool        gotStyle  = false;
+    bool        needCoord = false;
+    bool        gotCenter = false;
+    bool        gotRange  = false;
+    bool        needROI   = false;
+    bool        makeStamps= false;
+    unsigned    optionMask = PSTAMP_SELECT_IMAGE;
+
+    options->md = md;
+
+    psMetadataAddU32 (md, PS_LIST_TAIL, "ROWNUM", PS_META_REPLACE, "", 1);
+
+    // Job type. 
+    // "stamp" for make postage stamps. 
+    // "get_image" requests that whole images to be retrieved (magic masked for gpc1)
+    // "list_uri" results in a list of matching images
+    if ((argnum = psArgumentGet(argc, argv, "-get_image"))) {
+        psMetadataAddStr (md, PS_LIST_TAIL, "JOB_TYPE", PS_META_REPLACE, "", "get_image");
+        psArgumentRemove(argnum, &argc, argv);
+    } else if ((argnum = psArgumentGet(argc, argv, "-list_uri"))) {
+        psMetadataAddStr (md, PS_LIST_TAIL, "JOB_TYPE", PS_META_REPLACE, "", "list_uri");
+        psArgumentRemove(argnum, &argc, argv);
+    } else {
+        // default JOB_TYPE is stamp
+        psMetadataAddStr (md, PS_LIST_TAIL, "JOB_TYPE", PS_META_REPLACE, "", "stamp");
+        needROI = true;
+        makeStamps = true;
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-req_name"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        if (argc < 2) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "value required for request name");
+            usage(PS_EXIT_DATA_ERROR);
+        }
+        options->requestName = argv[argnum];
+        psArgumentRemove(argnum, &argc, argv);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "req_name is required\n");
+        usage(PS_EXIT_DATA_ERROR);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-project"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        if (argc < 2) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "project is required");
+            usage(PS_EXIT_DATA_ERROR);
+        }
+        psMetadataAddStr(md, PS_LIST_TAIL, "PROJECT", PS_META_REPLACE, "", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "project is required\n");
+        usage(PS_EXIT_DATA_ERROR);
+    }
+
+    // if provided, stamp name tag will be appended to the base name for the postage stamp images
+    if ((argnum = psArgumentGet(argc, argv, "-stamp_name"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        if (argc < 2) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "missing value for stamp_name");
+            usage(PS_EXIT_DATA_ERROR);
+        }
+        psMetadataAddStr(md, PS_LIST_TAIL, "STAMP_NAME", PS_META_REPLACE, "", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-mask"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        optionMask |= PSTAMP_SELECT_MASK;
+    }
+    if ((argnum = psArgumentGet(argc, argv, "-weight"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        optionMask |= PSTAMP_SELECT_WEIGHT;
+    }
+    psMetadataAddU32(md, PS_LIST_TAIL, "OPTION_MASK", PS_META_REPLACE, "", optionMask);
+
+    // find style & image type
+    if ((argnum = psArgumentGet(argc, argv, "-bycoord"))) {
+        gotStyle = true;
+        psMetadataAddStr(md, PS_LIST_TAIL, "REQ_TYPE", PS_META_REPLACE, "", 1+argv[argnum]);
+
+        psArgumentRemove(argnum, &argc, argv); needCoord = true;
+        needROI = true;
+        doByCoord(argnum, &argc, argv, options);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-byid"))) {
+        if (gotStyle) {
+            fprintf(stderr, "only one of -bycoord -byid -byexp may be specified\n");
+            usage(PS_EXIT_DATA_ERROR);
+        }
+        gotStyle = true;
+        psMetadataAddStr(md, PS_LIST_TAIL, "REQ_TYPE", PS_META_REPLACE, "", 1+argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+        doById(argnum, &argc, argv, options);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-byexp"))) {
+        if (gotStyle) {
+            fprintf(stderr, "only one of -bycoord -byid -byexp may be specified\n");
+            usage(PS_EXIT_DATA_ERROR);
+        }
+        gotStyle = true;
+        psMetadataAddStr(md, PS_LIST_TAIL, "REQ_TYPE", PS_META_REPLACE, "", 1+argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+        doByExp(argnum, &argc, argv, options);
+    } 
+    if (!gotStyle) {
+        fprintf(stderr, "one of -bycoord -byid -byexp must be specified\n");
+        usage(PS_EXIT_DATA_ERROR);
+    }
+    if ((argnum = psArgumentGet(argc, argv, "-mjd_min"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        if (argc < 2) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "missing value mjd_min");
+            usage(PS_EXIT_DATA_ERROR);
+        }
+        psMetadataAddF64(md, PS_LIST_TAIL, "MJD_MIN", PS_META_REPLACE, "", atof(argv[argnum]));
+        psArgumentRemove(argnum, &argc, argv);
+    } 
+    if ((argnum = psArgumentGet(argc, argv, "-mjd_max"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        if (argc < 2) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "missing value mjd_max");
+            usage(PS_EXIT_DATA_ERROR);
+        }
+        psMetadataAddF64(md, PS_LIST_TAIL, "MJD_MAX", PS_META_REPLACE, "", atof(argv[argnum]));
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    pstampROI roiParam;
+
+    // try and parse the ROI even if we don't "need it" to absorb arguments if they were provided
+    if (!pstampGetROI(&roiParam, &argc, argv, &gotCenter, &gotRange)) {
+        if (needROI) {
+            usage(PS_EXIT_DATA_ERROR);
+        } else {
+            psErrorClear();
+        }
+    }
+
+    if (needROI) {
+        unsigned coord_mask = 0;
+
+        if (roiParam.celestialCenter) {
+            psMetadataAddF64 (md, PS_LIST_TAIL, "CENTER_X", PS_META_REPLACE, "",
+                RAD_TO_DEG(roiParam.centerRA));
+            psMetadataAddF64 (md, PS_LIST_TAIL, "CENTER_Y", PS_META_REPLACE, "",
+                RAD_TO_DEG(roiParam.centerDEC));
+        } else {
+            if (needCoord) {
+                fprintf(stderr, "need to specify ROI in sky coordinates with -bycoord\n");
+                usage(PS_EXIT_DATA_ERROR);
+            }
+            coord_mask |= PSTAMP_CENTER_IN_PIXELS;
+            psMetadataAddF64 (md, PS_LIST_TAIL, "CENTER_X", PS_META_REPLACE, "", atof(roiParam.center[0]));
+            psMetadataAddF64 (md, PS_LIST_TAIL, "CENTER_Y", PS_META_REPLACE, "", atof(roiParam.center[1]));
+        }
+
+        if (!roiParam.celestialRange) {
+            coord_mask |= PSTAMP_RANGE_IN_PIXELS;
+        }
+        psMetadataAddF64 (md, PS_LIST_TAIL, "WIDTH", PS_META_REPLACE, "",  atof(roiParam.range[0]));
+        psMetadataAddF64 (md, PS_LIST_TAIL, "HEIGHT", PS_META_REPLACE, "", atof(roiParam.range[1]));
+
+        psMetadataAddU32(md, PS_LIST_TAIL, "COORD_MASK", PS_META_REPLACE, "", coord_mask);
+    }
+
+    // only argument left should be the required file name for the request file
+
+    if (argc == 2) {
+        options->fileName = psStringCopy(argv[1]);
+    } else if (argc == 1) {
+        fprintf(stderr, "output file name is required\n");
+        usage(PS_EXIT_DATA_ERROR);
+    } else {
+        fprintf(stderr, "too many arguments supplied:");
+        fprintf(stderr, " %s", argv[1]);
+        for (int i=2; i<argc; i++) {
+            fprintf(stderr, ", %s", argv[i]);
+        }
+        fprintf(stderr, "\n");
+        usage(PS_EXIT_DATA_ERROR);
+    }
+
+    return options;
+}
+
+static bool writeTable(psrOptions *options, int *pExitStatus)
+{
+    psFits *fitsFile = psFitsOpen(options->fileName, "w");
+    if (fitsFile == NULL) {
+        psError(PS_ERR_IO, true, "failed to open %s for output\n", options->fileName);
+        *pExitStatus = PS_EXIT_SYS_ERROR;
+        return false;
+    }
+    psMetadata *header = psMetadataAlloc();
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "EXTVER",   PS_META_REPLACE, "", STAMP_REQUEST_VERSION);
+    psMetadataAddStr(header, PS_LIST_TAIL, "REQ_NAME", PS_META_REPLACE, "", options->requestName);
+
+    psArray    *table = psArrayAlloc(1);
+    table->data[0] = options->md;
+
+    if (!psFitsWriteTable(fitsFile, header, table, STAMP_REQUEST_EXTNAME)) {
+        psError(PS_ERR_IO, false, "failed to write fits table");
+        *pExitStatus = PS_EXIT_SYS_ERROR;
+        return false;
+    }
+
+    if (! psFitsClose(fitsFile)) {
+        psError(PS_ERR_IO, false, "failed to close fits table");
+        *pExitStatus = PS_EXIT_SYS_ERROR;
+        return false;
+    }
+
+    return true;
+}
+
+int main(int argc, char *argv[])
+{
+    int exitStatus = 0;
+
+    // all of the action happens in parseArguments
+    psrOptions *options = parseArguments(argc, argv, &exitStatus);
+
+    if (!options) {
+        return 1;
+    }
+
+    if (options->verbose) {
+        psMetadataPrint(stderr, options->md, 0);
+    }
+
+    if (!writeTable(options, &exitStatus)) {
+        psErrorStackPrint(stderr, "failed to create request table");
+    }
+    return exitStatus;
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/test/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/test/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/test/README	(revision 22322)
@@ -0,0 +1,49 @@
+This directory contains scripts for creating sample MOPS_DETECTABILTY_QUERY
+fits tables into a Data Store
+
+    testdquery.pl --query_id id [--input query_descriptor_file]
+
+Creates a detectability query table with a given QUERY_ID from a textual
+description. See below for format. If no file is provided a sample is 
+used. The request file is added to the data store product with a fileset
+name equal to the query_id
+
+testdquery.pl uses dsreg to register the fileset and thus the IPP
+configuration must be set up.
+
+The request file is created by the program
+
+    detect_query_create id  --input query_descriptor_file \
+                            --output query_file_name \
+                            [--query_id id]
+    
+
+This program creates a request file with given name and query id from the
+request descriptor file
+
+The request descriptor file format is simple. 
+
+Blank lines and lines that begin with the hash character '#' are ignored.
+
+The first valid line must contain 6 whitespace separated fields. These
+data are used to set the header keywords for the Query
+
+# QUERY_ID EXTVER FPA_ID       MJD-OBS FILTER OBSCODE
+QUERY42       1   o4608g0103o   54608     g     566
+
+If the command line option --query_id is provided it overrides the value
+in the descriptor file.
+
+Subsequent lines in the file are used to define the rows of the query.
+For example:
+
+# ROWNUM     RA1_DEG        DEC1_DEG    RA2_DEG     DEC2_DEG     MAG
+1            312.44049389 30.54022727  312.44051968 30.54024139  18.4228
+2            313.03337881 31.01317194  313.03344194 31.01324268  18.3961
+3            312.91159232 30.95195459  312.91153476 30.95190113  18.1110
+4            312.26742527 30.95207284  312.26744769 30.95206300  17.0908
+5            313.20263734 30.62317841  313.20266984 30.62310935  18.2890
+
+
+
+
Index: /tags/sj_tags/sj_root_20080929/pstamp/test/detect_query_create
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/test/detect_query_create	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/test/detect_query_create	(revision 22322)
@@ -0,0 +1,273 @@
+#!/usr/bin/env perl
+
+# create a MOPS_DETECTABILITY_QUERY fits table from a text description file
+
+use warnings;
+use strict;
+
+use Astro::FITS::CFITSIO qw( :constants );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+use constant EXTNAME => 'MOPS_DETECTABILITY_QUERY'; # Extension name for output table
+
+my ( $input,			# Name of input text file
+     $output,			# Name of output table
+     $query_id, 
+     );
+
+GetOptions(
+	   'input|i=s'    => \$input,
+	   'output|o=s'   => \$output,
+	   'query_id|q=s'  => \$query_id,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --input --output",
+           -exitval => 3) unless defined $input and defined $output;
+
+# The header kewords
+my $header = [
+        { name =>  'QUERY_ID', 
+                    writetype => TSTRING, 
+                    comment => 'MOPS Query ID for this batch query',
+                    value => undef
+        },
+        { name =>  'EXTVER', 
+                    writetype => TSTRING, 
+                    comment => 'Extension version',
+                    value => undef
+        },
+        { name =>  'FPA_ID', 
+                    writetype => TSTRING, 
+                    comment => 'orginal FPA_ID used at ingest',
+                    value => undef
+        }, 
+        { name =>  'MJD-OBS', 
+                    writetype => TDOUBLE, 
+                    comment => 'starting time of the exposure, MJD',
+                    value => undef
+        },
+        { name =>  'FILTER', 
+                    writetype => TSTRING, 
+                    comment => 'effective filter use for the exposure',
+                    value => undef
+        },
+        { name =>  'OBSCODE', 
+                    writetype => TSTRING, 
+                    comment => 'site identifier (MPC observatory code)',
+                    value => undef
+        }
+];
+
+# Specification of columns to write
+my $columns = [ 
+        # matching rownum from detectability original request
+        { name => 'ROWNUM',   type => '20A', writetype => TSTRING }, 
+        # coordinate at start of exposure, in degrees
+        { name => 'RA1_DEG',  type => 'D',   writetype => TDOUBLE },
+        # coordinate at start of exposure, in degrees
+        { name => 'DEC1_DEG', type => 'D',   writetype => TDOUBLE },
+        # coordinate at end of exposure, in degrees
+        { name => 'RA2_DEG',  type => 'D',   writetype => TDOUBLE },
+        # coordinate at end of exposure, in degrees
+        { name => 'DEC2_DEG', type => 'D',   writetype => TDOUBLE },
+        # apparent magnitude
+        { name => 'MAG',      type => 'D',   writetype => TDOUBLE },
+];
+
+my $in;
+if ($input eq '-') {
+    $in = \*STDIN;
+} else {
+    open $in, "<$input" or die "cannot open $input for reading";
+}
+
+my @colData;
+foreach (@$columns) {
+    push @colData, [];
+}
+
+
+my $numRows = read_data_for_table($in,'\s+', \@colData, $header); 
+if (!$numRows) {
+    print STDERR "no data in $input\n";
+    exit 1;
+}
+
+# overwrite the QUERY_ID value from the input file with the command
+# line argument
+
+if ($query_id) {
+    $header->[0]->{value} = $query_id;
+}
+
+my $status = make_fits_table($output, EXTNAME, $numRows, \@colData, $columns, $header);
+
+exit $status;
+
+# XXXXX: This should be in a module
+# two utility functions that may be used to create a FITS binary
+# table from hashes describing the header keywords and columns
+
+# read_table_description reads the data for a table from a simple text file
+# make_fits_table writes out the table to a named file
+
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+
+# A function to build a fits binary table from supplied data 
+# 
+sub make_fits_table {
+        my $output = shift;     # name of output file
+        my $extname = shift;    # extension name
+        my $numRows = shift;    # number of rows in the table
+        my $colData = shift;    # ref to array of arrays containing the data for each column
+        my $columns = shift;    # ref to array of column descriptions (each a hash)
+                                # with keys: name, type, and writetype
+        my $header = shift;     # ref to array of header keyword descriptions - each a hash
+                                # with keys: name, name, writetype, comment, and value
+        my $status = 0;
+
+        die "incorrect arguments" if !defined($columns);
+        # note $header can be nil
+
+        # build arrays for cfitsio
+        my @colNames;			# Names of columns
+        my @colTypes;			# Types of columns
+        my @colWriteType;               # type to use to write
+
+        foreach my $colSpec ( @$columns) {
+            push @colNames, $colSpec->{name};
+            push @colTypes, $colSpec->{type};
+            push @colWriteType, $colSpec->{writetype};
+        }
+
+        if (-e $output) {
+            unlink "$output" or die "failed to remove existing $output";
+        }
+
+        my $outFits = Astro::FITS::CFITSIO::create_file( $output, $status ); # Output file handle
+        check_fitsio( $status );
+
+        $outFits->create_img( 16, 0, undef, $status );
+        check_fitsio( $status );
+
+        # Create the table
+
+        $outFits->create_tbl( BINARY_TBL(), $numRows, scalar @colNames,
+                                \@colNames, \@colTypes, undef, $extname, $status );
+        check_fitsio( $status );
+
+        # if header keyword descriptions were provided add them
+        if ($header) {
+            foreach my $headerword ( @$header ) {
+                my $value = $headerword->{value};
+                unless (defined $value) {
+                    print "Can't find header keyword $headerword\n";
+                    next;
+                }
+                # zap quotation marks
+                $value =~ s/\'//g;
+                my $name    = $headerword->{name};
+                my $type    = $headerword->{writetype};
+                my $comment = $headerword->{comment};
+                $outFits->write_key( $type, $name, $value, $comment, $status );
+                check_fitsio( $status );
+            }
+        }
+
+
+        for (my $i = 0; $i < scalar @colNames; $i++) {
+            my $writeType = $colWriteType[$i];
+            $outFits->write_col( $writeType, $i + 1, 1, 1, $numRows, $colData->[$i], $status );
+            check_fitsio( $status );
+        }
+
+        $outFits->close_file( $status );
+
+        return 0;
+
+} # end of sub make_fits_table
+
+
+
+# read the table contents from a file
+#
+# input text file format:
+#   lines that begin with '#' are comment lines and are skipped.
+#   other lines are data. Each data line is split into fields with the
+#   provided separator
+#
+# if $header is not null header the first non-commented line is read to
+# fill the value for each header keyword. The number of fields must match
+# the number of keywords.
+#
+# Following the optional header data, each data line contains data for each
+# row in the table. The number of fields must match the number of column
+# arrays provided.
+
+sub read_data_for_table {
+    my $in      = shift;    # input file handle
+    my $sep     = shift;    # string containing field separator
+    my $colData = shift;    # reference to an array of arrays for the data
+    my $header  = shift;    # rerence to array of header keyword descriptions
+
+    my $line_num = 0;
+
+    # read data for header if any data is expected
+    if ($header) {
+        my $nhead = @$header;
+        while (my $line = <$in>) {
+            $line_num++;
+            chomp $line;
+            next if !$line;             # skip blank lines
+            next if ($line =~ /^#/);    # skip comment lines
+            my @vals = split /$sep/, $line;
+            my $nvals = @vals;
+            die "number of header columns in input $nvals does not equal expected number of header words $nhead"
+                    if (@vals != @$header);
+
+            for (my $i=0; $i < @$header; $i++) {
+                $header->[$i]->{value} = $vals[$i];
+            }
+
+            last; # only one header line
+        }
+    }
+
+    my $row_num = 0;
+    my $ncols = @$colData;
+    while (my $line = <$in>) {
+        chomp $line;
+        $line_num++;
+        next if !$line;             # skip blank lines
+        next if ($line =~ /^#/);    # skip comment lines
+
+        my @vals = split /$sep/, $line;
+        my $nvals = @vals;
+        die "number of columns $nvals in input does not equal expected number of header "
+                . " words $ncols on line $line_num" if ($nvals != $ncols);
+
+        for (my $col = 0; $col < @$colData; $col++) {
+            $colData->[$col]->[$row_num] = $vals[$col];
+        }
+        $row_num++;
+    }
+
+    # we return the number of rows read
+    return $row_num;
+}
+
+# From Astro::FITS::CFITSIO demo
+sub check_fitsio
+{
+    my $status = shift;		# Status of FITSIO calls
+
+    if ($status != 0) {
+	my $msg;		# Message to output
+	Astro::FITS::CFITSIO::fits_get_errstatus( $status , $msg );
+	die "CFITSIO error: $msg\n";
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/test/pstamp_req_create
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/test/pstamp_req_create	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/test/pstamp_req_create	(revision 22322)
@@ -0,0 +1,265 @@
+#!/usr/bin/env perl
+
+# create a Postage Stamp Request file from a textual description
+
+use warnings;
+use strict;
+
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+use constant EXTNAME => 'PS1_PS_REQUEST'; # Extension name for output table
+
+my ( $input,			# Name of input text file
+     $output,			# Name of output table
+     $req_name, 
+     );
+
+GetOptions(
+	   'input|i=s'    => \$input,
+	   'output|o=s'   => \$output,
+	   'req_name|q=s'  => \$req_name,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --input --output",
+           -exitval => 3) unless defined $input and defined $output;
+
+# The header kewords
+my $header = [
+        { name =>  'REQ_NAME', 
+                    writetype => TSTRING, 
+                    comment => 'Postage Stamp request name',
+                    value => undef
+        },
+        { name =>  'EXTVER', 
+                    writetype => TSTRING, 
+                    comment => 'Extension version',
+                    value => undef
+        },
+];
+
+# Specification of columns to write
+my $columns = [ 
+        { name => 'ROWNUM',     type => 'J',   writetype => TULONG }, 
+        { name => 'PROJECT',    type => '16A', writetype => TSTRING },
+        { name => 'JOB_TYPE',   type => '16A', writetype => TSTRING },
+
+        { name => 'STAMP_NAME', type => '16A', writetype => TSTRING },
+        { name => 'OPTION_MASK',type => 'J',   writetype => TULONG },
+
+        # image selection parameters
+        { name => 'REQ_TYPE',   type => '16A', writetype => TSTRING },
+        { name => 'IMG_TYPE',   type => '16A', writetype => TSTRING },
+        { name => 'ID',         type => '16A', writetype => TSTRING },           
+        { name => 'CLASS_ID',   type => '16A', writetype => TSTRING },
+
+
+        # 2 bits in COORD_MASK indicate what units of roi coords are
+        { name => 'COORD_MASK', type => 'J',  writetype => TULONG },
+
+        { name => 'CENTER_X',   type => 'D',  writetype => TDOUBLE },
+        { name => 'CENTER_Y',   type => 'D',  writetype => TDOUBLE },
+        { name => 'WIDTH',      type => 'D',  writetype => TDOUBLE },
+        { name => 'HEIGHT',     type => 'D',  writetype => TDOUBLE },
+
+        { name => 'REQFILT',    type => '16A', writetype => TSTRING },
+        { name => 'MJD_MIN',   type => 'D',   writetype => TDOUBLE },
+        { name => 'MJD_MAX',   type => 'D',   writetype => TDOUBLE },
+];
+
+my $in;
+if ($input eq '-') {
+    $in = \*STDIN;
+} else {
+    open $in, "<$input" or die "cannot open $input for reading";
+}
+
+my @colData;
+foreach (@$columns) {
+    push @colData, [];
+}
+
+
+my $numRows = read_data_for_table($in,'\s+', \@colData, $header); 
+if (!$numRows) {
+    print STDERR "no data in $input\n";
+    exit 1;
+}
+
+# overwrite the REQ_NAME value from the input file with the command
+# line argument
+
+if ($req_name) {
+    $header->[0]->{value} = $req_name;
+}
+
+my $status = make_fits_table($output, EXTNAME, $numRows, \@colData, $columns, $header);
+
+exit $status;
+
+# XXXXX: This should be in a module
+# two utility functions that may be used to create a FITS binary
+# table from hashes describing the header keywords and columns
+
+# read_table_description reads the data for a table from a simple text file
+# make_fits_table writes out the table to a named file
+
+
+# A function to build a fits binary table from supplied data 
+# 
+sub make_fits_table {
+        my $output = shift;     # name of output file
+        my $extname = shift;    # extension name
+        my $numRows = shift;    # number of rows in the table
+        my $colData = shift;    # ref to array of arrays containing the data for each column
+        my $columns = shift;    # ref to array of column descriptions (each a hash)
+                                # with keys: name, type, and writetype
+        my $header = shift;     # ref to array of header keyword descriptions - each a hash
+                                # with keys: name, name, writetype, comment, and value
+        my $status = 0;
+
+        die "incorrect arguments" if !defined($columns);
+        # note $header can be nil
+
+        # build arrays for cfitsio
+        my @colNames;			# Names of columns
+        my @colTypes;			# Types of columns
+        my @colWriteType;               # type to use to write
+
+        foreach my $colSpec ( @$columns) {
+            push @colNames, $colSpec->{name};
+            push @colTypes, $colSpec->{type};
+            push @colWriteType, $colSpec->{writetype};
+        }
+
+        if (-e $output) {
+            unlink "$output" or die "failed to remove existing $output";
+        }
+
+        my $outFits = Astro::FITS::CFITSIO::create_file( $output, $status ); # Output file handle
+        check_fitsio( $status );
+
+        $outFits->create_img( 16, 0, undef, $status );
+        check_fitsio( $status );
+
+        # Create the table
+
+        $outFits->create_tbl( BINARY_TBL(), $numRows, scalar @colNames,
+                                \@colNames, \@colTypes, undef, $extname, $status );
+        check_fitsio( $status );
+
+        # if header keyword descriptions were provided add them
+        if ($header) {
+            foreach my $headerword ( @$header ) {
+                my $value = $headerword->{value};
+                unless (defined $value) {
+                    print "Can't find header keyword $headerword\n";
+                    next;
+                }
+                # zap quotation marks
+                $value =~ s/\'//g;
+                my $name    = $headerword->{name};
+                my $type    = $headerword->{writetype};
+                my $comment = $headerword->{comment};
+                $outFits->write_key( $type, $name, $value, $comment, $status );
+                check_fitsio( $status );
+            }
+        }
+
+
+        for (my $i = 0; $i < scalar @colNames; $i++) {
+            my $writeType = $colWriteType[$i];
+            $outFits->write_col( $writeType, $i + 1, 1, 1, $numRows, $colData->[$i], $status );
+            check_fitsio( $status );
+        }
+
+        $outFits->close_file( $status );
+
+        return 0;
+
+} # end of sub make_fits_table
+
+
+
+# read the table contents from a file
+#
+# input text file format:
+#   lines that begin with '#' are comment lines and are skipped.
+#   other lines are data. Each data line is split into fields with the
+#   provided separator
+#
+# if $header is not null header the first non-commented line is read to
+# fill the value for each header keyword. The number of fields must match
+# the number of keywords.
+#
+# Following the optional header data, each data line contains data for each
+# row in the table. The number of fields must match the number of column
+# arrays provided.
+
+sub read_data_for_table {
+    my $in      = shift;    # input file handle
+    my $sep     = shift;    # string containing field separator
+    my $colData = shift;    # reference to an array of arrays for the data
+    my $header  = shift;    # rerence to array of header keyword descriptions
+
+    my $line_num = 0;
+
+    # read data for header if any data is expected
+    if ($header) {
+        my $nhead = @$header;
+        while (my $line = <$in>) {
+            $line_num++;
+            chomp $line;
+            next if !$line;             # skip blank lines
+            next if ($line =~ /^#/);    # skip comment lines
+            my @vals = split /$sep/, $line;
+            my $nvals = @vals;
+            die "number of header columns in input $nvals does not equal expected number of header words $nhead"
+                    if (@vals != @$header);
+
+            for (my $i=0; $i < @$header; $i++) {
+                $header->[$i]->{value} = $vals[$i];
+            }
+
+            last; # only one header line
+        }
+    }
+
+    my $row_num = 0;
+    my $ncols = @$colData;
+    while (my $line = <$in>) {
+        chomp $line;
+        $line_num++;
+        next if !$line;             # skip blank lines
+        next if ($line =~ /^#/);    # skip comment lines
+
+        my @vals = split /$sep/, $line;
+        my $nvals = @vals;
+        die "number of columns $nvals in input does not equal expected number of header "
+                . " words $ncols on line $line_num" if ($nvals != $ncols);
+
+        for (my $col = 0; $col < @$colData; $col++) {
+            $colData->[$col]->[$row_num] = $vals[$col];
+        }
+        $row_num++;
+    }
+
+    # we return the number of rows read
+    return $row_num;
+}
+
+# From Astro::FITS::CFITSIO demo
+sub check_fitsio
+{
+    my $status = shift;		# Status of FITSIO calls
+
+    if ($status != 0) {
+	my $msg;		# Message to output
+	Astro::FITS::CFITSIO::fits_get_errstatus( $status , $msg );
+	die "CFITSIO error: $msg\n";
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/pstamp/test/sample_pstamp.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/test/sample_pstamp.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/test/sample_pstamp.txt	(revision 22322)
@@ -0,0 +1,23 @@
+# Sample Postage stamp request description file
+# This can be parsed by the program pstamp_req_create to build a 
+# PS1_PSTAMP_REQUEST binary table
+
+# First line of data is for the extension header
+# The order of keywords follows TABLE 6 of ICD
+#
+# Note that value for REQ_NAME may be overriden by a command line
+# argument to pstamp_req_create.
+# Blank and comment lines are ignored
+
+# REQ_NAME EXTVER
+PSREQ00001     1
+
+# subsequent lines define the rows in the table
+
+# ROWNUM PROJECT       JOB_TYPE STAMP_NAME OPTION_MASK REQ_TYPE IMG_TYPE ID     CLASS_ID COORD_MASK CENTER_X CENTER_Y WIDTH HEIGHT REQFILT MJD_MIN MJD_MAX
+1        megacam-mops   stamp    null       1           byid     chip      419       null        3   1500    1500     100    100     null   0    0
+2        megacam-mops   stamp    null       1           byid     chip      419       null        3   1600    1500     100    100     null   0    0
+3        megacam-mops   stamp    null       1           byid     chip      419       null        3   1700    1500     100    100     null   0    0
+
+# the following row will create several jobs
+4        megacam-mops   stamp    null       1           byexp    stack     906043p   null        3   400      400     100    200     null   0    0
Index: /tags/sj_tags/sj_root_20080929/pstamp/test/sample_query.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/test/sample_query.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/test/sample_query.txt	(revision 22322)
@@ -0,0 +1,57 @@
+# Sample Detectability Query description file used by the program
+# detect_query_create
+
+# First line of data is for the extension header
+# The order of keywords follows TABLE 6 of ICD
+# Note that value for QUERY_ID may be overriden by a command line
+# argument to detect_query_create.
+# Blank and comment lines are ignored
+
+# QUERY_ID EXTVER FPA_ID       MJD-OBS FILTER OBSCODE
+QUERY42       1   o4608g0103o   54608     g     566
+
+# subsequent lines define the rows in the table
+
+# ROWNUM     RA1_DEG        DEC1_DEG    RA2_DEG     DEC2_DEG     MAG
+1            312.44049389 30.54022727  312.44051968 30.54024139  18.4228     
+2            313.03337881 31.01317194  313.03344194 31.01324268  18.3961     
+3            312.91159232 30.95195459  312.91153476 30.95190113  18.1110     
+4            312.26742527 30.95207284  312.26744769 30.95206300  17.0908     
+5            313.20263734 30.62317841  313.20266984 30.62310935  18.2890     
+6            312.85365023 30.45667552  312.85358645 30.45661659  16.9252     
+7            312.86257534 30.73005531  312.86264564 30.73012644  16.5870     
+8            312.62651492 30.28398046  312.62654595 30.28390213  16.6911     
+9            312.30703743 30.99927709  312.30708709 30.99927981  17.4722     
+10           312.95440987 30.21452885  312.95434880 30.21444691  18.5156     
+11           313.09947609 30.93800421  313.09940219 30.93795549  17.8855     
+12           312.97417836 31.11301952  312.97423473 31.11296140  16.9911     
+13           313.11781918 30.37359317  313.11784695 30.37360958  16.7468     
+14           312.76927925 30.53972354  312.76933224 30.53976126  16.8949     
+15           313.14717536 30.25958068  313.14710913 30.25951218  16.8300     
+16           312.30604633 30.88958513  312.30599425 30.88959415  18.2257     
+17           312.43565662 30.43038293  312.43560908 30.43037934  19.0005     
+18           312.55589195 30.81649562  312.55594584 30.81655133  18.6992     
+19           312.48925222 30.79092013  312.48924050 30.79087193  19.2099     
+20           312.94560207 31.15170816  312.94554641 31.15162988  17.2162     
+21           313.19589134 30.74674862  313.19594142 30.74682054  16.6793     
+22           312.70278940 30.52926292  312.70271911 30.52926109  19.0436     
+23           312.56015422 30.77354938  312.56021167 30.77360353  19.2750     
+24           312.87989916 30.78926857  312.87996776 30.78920032  18.2502     
+25           312.81101394 30.65212801  312.81096654 30.65215390  18.5487     
+26           312.55607541 30.23260894  312.55612602 30.23263745  17.2404     
+27           312.47366234 31.02186968  312.47372919 31.02192895  18.9959     
+28           312.90062087 31.13704835  312.90061590 31.13697785  17.5652     
+29           313.01177949 31.16629118  313.01181621 31.16626516  17.0270     
+30           312.40371494 30.98672316  312.40366292 30.98677392  19.1234     
+31           312.56662477 31.01082034  312.56665651 31.01078413  17.6238     
+32           312.61398923 30.64616956  312.61399907 30.64618259  18.5522     
+33           313.07268041 30.96857880  313.07272131 30.96859005  17.7002     
+34           312.36144521 30.43367984  312.36146205 30.43373589  16.6557     
+35           312.74549146 30.89623741  312.74554546 30.89630365  18.8037     
+36           312.49256362 30.78465056  312.49260740 30.78459825  16.7909     
+37           312.92641571 30.76107785  312.92634812 30.76111408  17.2734     
+38           312.84561663 30.38510072  312.84561100 30.38515538  18.3349     
+39           312.48297202 31.08290934  312.48297374 31.08290383  17.6822     
+40           312.27670734 30.89704117  312.27664961 30.89698871  17.4518     
+41           312.82856967 30.65880493  312.82859245 30.65878537  18.8543     
+42           313.05680974 30.97207965  313.05672748 30.97211785  16.6627     
Index: /tags/sj_tags/sj_root_20080929/pstamp/test/test_lookup.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/test/test_lookup.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/test/test_lookup.pl	(revision 22322)
@@ -0,0 +1,72 @@
+#!/bin/env perl
+###
+###     Tests for the PStamp module
+###
+
+use warnings;
+use strict;
+
+
+use Getopt::Long qw( GetOptions );
+use PStamp::Job qw( :standard );
+
+use PS::IPP::Config qw( :standard );
+my $ipprc = PS::IPP::Config->new(); # IPP Configuration
+
+my $verbose;
+my $image_db;
+my $req_type;
+my $img_type;
+my $id;
+my $class_id;
+my $x;
+my $y;
+my $mjd_min;
+my $mjd_max;
+my $filter;
+
+
+
+GetOptions(
+    'dbname=s'  =>  \$image_db,
+    'req_type=s'=>  \$req_type,
+    'img_type=s'=>  \$img_type,
+    'id=s'      =>  \$id,
+    'class_id=s'=>  \$class_id,
+    'x=s'       =>  \$x,
+    'y=s'       =>  \$y,
+    'mjd_min=s' =>  \$mjd_min,
+    'mjd_max=s' =>  \$mjd_max,
+    'filter=s'  =>  \$filter,
+    'verbose'   =>  \$verbose,
+);
+
+my $err = "";
+$err .= " --req_type is required" if !$req_type;
+$err .= " --img_type is required" if !$img_type;
+$err .= " --id is required" if ($req_type ne "bycoord") && !$id;
+$err .= " --dbname is required" if !$image_db;
+
+if ($err) {
+    print STDERR "$err\n";
+    exit $PS_EXIT_DATA_ERROR;
+}
+
+my $results = locate_images($ipprc, $image_db, $req_type, $img_type, $id, $class_id,
+            $x, $y, $mjd_min, $mjd_max, $filter, $verbose);
+
+foreach my $i (@$results) {
+    print "${img_type}_id: $id";
+    if ($i->{exp_id}) {
+        print " exp_id $i->{exp_id}\n"
+    } else {
+        print "\n";
+    }
+    print "\timage:  $i->{image}\n";
+    print "\tmask:   $i->{mask}\n"   if $i->{mask};
+    print "\tweight: $i->{weight}\n" if $i->{weight};
+    print "\tastrom: $i->{astrom}\n" if $i->{astrom};
+}
+
+exit 0;
+
Index: /tags/sj_tags/sj_root_20080929/pstamp/test/testrequest
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/test/testrequest	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/test/testrequest	(revision 22322)
@@ -0,0 +1,183 @@
+#!/bin/env  perl
+
+# program to create a sample detectability query or postage stamp request file
+# and register it in a Data Store
+
+#  default products for local configuration
+
+my $dquery_product = "detectability_query";
+my $pstamp_product = "pstamprequests";
+
+# note: dsreg also needs IPP site configuration to connect to the database
+
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Temp qw(tempfile);
+
+
+my ($query_id, $req_name, $product, $input, $fs_name, $verbose, $save_temps);
+
+GetOptions(
+    'input|i=s'     => \$input,
+    'query_id|q=s'  => \$query_id,
+    'req_name|r=s'  => \$req_name,
+    'verbose|v'     => \$verbose,
+    'save-temps'    => \$save_temps,
+    'dbname|d=s'    => \$dbname,
+    'product|p=s'   => \$product,
+) or pod2usage( 2 );
+
+die "--query_id or --req_name is required\n" if ((!$query_id) && (!$req_name));
+die "only one of --query_id or --req_name is allowed\n" if ($query_id && $req_name);
+
+my $dsreg = can_run("dsreg") or die "cant'f find dsreg";
+my $detect_query_create = can_run("detect_query_create") or die "cant'f find detect_query_create";
+my $pstamp_req_create = can_run("pstamp_req_create") or die "cant'f find pstamp_req_create";
+
+my $fileset_type;
+my $create_cmd;
+
+# if query_id is supplied we make a detectability query
+# else req_name was supplied and we make a postage stamp request
+if ($query_id) {
+    $create_prog = $detect_query_create;
+    $req_name = $query_id;
+    $product = $dquery_product if !$product;
+    $fileset_type = "MOPS_DETECTABILITY_QUERY";
+    $create_cmd = "$detect_query_create --query_id $query_id";
+} else {
+    die "must supply input request decription file\n" if !$input;
+    $product = $pstamp_product if !$product;
+    $fileset_type = "PSREQUEST";
+    $create_cmd = "$pstamp_req_create --req_name $req_name";
+}
+$fs_name = $req_name;
+
+# request fits table
+my $req_file;
+my $RFH;
+my $datapath;
+if ($save_temps) {
+    $req_file = "${req_name}.fits";
+    $datapath = ".";
+} else {
+    ($RFH, $req_file) = tempfile("/tmp/${req_name}.fits.XXXX", UNLINK => 1);
+    die "failed to create tempfile" if !$RFH;
+
+    $datapath = "/";
+}
+
+
+my $query_txt_file;
+my $FH;
+# if no query file is supplied make one from the Here document in the subroutine make_default_query
+if ($input) {
+    $query_txt_file = $input;
+} else {
+    ($FH, $query_txt_file) = tempfile("/tmp/dquery.txt.XXXX", UNLINK => !$save_temps);
+    die "failed to create tempfile" if !$FH;
+    make_default_dquery($FH);
+}
+
+die "can't find request description file $query_txt_file" if (!-e $query_txt_file);
+
+$create_cmd .= " --input $query_txt_file --output $req_file";
+
+my $reg_cmd = "echo '$req_file|||table|'"
+            . " | $dsreg --product $product --type $fileset_type"
+            . " --add $fs_name --copy --datapath $datapath --list -"
+            . " --dbname $dbname" if $dbname;
+
+{
+    my $command = "$create_cmd";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        print STDERR "command failed error code: $error_code\nstderr:\n\t";
+        print STDERR @$stderr_buf;
+        print "\n";
+
+        my $exit_status = $error_code >> 8;
+        print STDERR "exiting with status $exit_status\n" if $verbose;
+        exit $exit_status;
+    }
+}
+{
+    my $command = "$reg_cmd";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        print STDERR "command failed error code: $error_code\nstderr:\n\t"
+            if $verbose;
+        print STDERR @$stderr_buf;
+        print "\n" if $verbose;
+
+        my $exit_status = $error_code >> 8;
+        print STDERR "exiting with status $exit_status\n" if $verbose;
+        exit $exit_status;
+    }
+}
+exit 0;
+
+sub make_default_dquery()
+{
+    my $FH = shift;
+    my $data = <<'THE_END';
+# Sample data
+# First line of data is for the HEADER
+# order of keywords follows TABLE 6 of ICD
+# QUERY_ID EXTVER FPA_ID       MJD-OBS FILTER OBSCODE
+QUERY42       1   o4608g0103o   54608     g     566
+#
+# ROWNUM     RA1_DEG        DEC1_DEG    RA2_DEG     DEC2_DEG     MAG
+1            312.44049389 30.54022727  312.44051968 30.54024139  18.4228     
+2            313.03337881 31.01317194  313.03344194 31.01324268  18.3961     
+3            312.91159232 30.95195459  312.91153476 30.95190113  18.1110     
+4            312.26742527 30.95207284  312.26744769 30.95206300  17.0908     
+5            313.20263734 30.62317841  313.20266984 30.62310935  18.2890     
+6            312.85365023 30.45667552  312.85358645 30.45661659  16.9252     
+7            312.86257534 30.73005531  312.86264564 30.73012644  16.5870     
+8            312.62651492 30.28398046  312.62654595 30.28390213  16.6911     
+9            312.30703743 30.99927709  312.30708709 30.99927981  17.4722     
+10           312.95440987 30.21452885  312.95434880 30.21444691  18.5156     
+11           313.09947609 30.93800421  313.09940219 30.93795549  17.8855     
+12           312.97417836 31.11301952  312.97423473 31.11296140  16.9911     
+13           313.11781918 30.37359317  313.11784695 30.37360958  16.7468     
+14           312.76927925 30.53972354  312.76933224 30.53976126  16.8949     
+15           313.14717536 30.25958068  313.14710913 30.25951218  16.8300     
+16           312.30604633 30.88958513  312.30599425 30.88959415  18.2257     
+17           312.43565662 30.43038293  312.43560908 30.43037934  19.0005     
+18           312.55589195 30.81649562  312.55594584 30.81655133  18.6992     
+19           312.48925222 30.79092013  312.48924050 30.79087193  19.2099     
+20           312.94560207 31.15170816  312.94554641 31.15162988  17.2162     
+21           313.19589134 30.74674862  313.19594142 30.74682054  16.6793     
+22           312.70278940 30.52926292  312.70271911 30.52926109  19.0436     
+23           312.56015422 30.77354938  312.56021167 30.77360353  19.2750     
+24           312.87989916 30.78926857  312.87996776 30.78920032  18.2502     
+25           312.81101394 30.65212801  312.81096654 30.65215390  18.5487     
+26           312.55607541 30.23260894  312.55612602 30.23263745  17.2404     
+27           312.47366234 31.02186968  312.47372919 31.02192895  18.9959     
+28           312.90062087 31.13704835  312.90061590 31.13697785  17.5652     
+29           313.01177949 31.16629118  313.01181621 31.16626516  17.0270     
+30           312.40371494 30.98672316  312.40366292 30.98677392  19.1234     
+31           312.56662477 31.01082034  312.56665651 31.01078413  17.6238     
+32           312.61398923 30.64616956  312.61399907 30.64618259  18.5522     
+33           313.07268041 30.96857880  313.07272131 30.96859005  17.7002     
+34           312.36144521 30.43367984  312.36146205 30.43373589  16.6557     
+35           312.74549146 30.89623741  312.74554546 30.89630365  18.8037     
+36           312.49256362 30.78465056  312.49260740 30.78459825  16.7909     
+37           312.92641571 30.76107785  312.92634812 30.76111408  17.2734     
+38           312.84561663 30.38510072  312.84561100 30.38515538  18.3349     
+39           312.48297202 31.08290934  312.48297374 31.08290383  17.6822     
+40           312.27670734 30.89704117  312.27664961 30.89698871  17.4518     
+41           312.82856967 30.65880493  312.82859245 30.65878537  18.8543     
+42           313.05680974 30.97207965  313.05672748 30.97211785  16.6627     
+THE_END
+
+    print $FH $data;
+
+    close $FH;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pstamp/web/request.php
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstamp/web/request.php	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstamp/web/request.php	(revision 22322)
@@ -0,0 +1,844 @@
+<?php 
+
+// prototype postage stamp server web interface
+
+// A php program that generates a postage stamp request form
+
+// There are two modes on the form List Images simply lists the input images
+// that match the image selection critera
+
+// When make stamps is selected when the page is posted, a postage stamp request is queued.
+// 
+// Then we respond to http posts by listing the status of the jobs.
+// Once all of the jobs complete we set the page back to submission mode again
+// The mode for the page is given by $request_id != 0
+
+// XXX This is just a prototype for testing purposes. 
+
+
+// BEGIN Local configuration
+
+$WORKDIR = "/export/data1/bills/pstamp/work";
+
+$PSCONFDIR = "/export/data0/bills/psconfig";
+$PSCONFIG  = "debug";
+$PSBINDIR  = "$PSCONFDIR/$PSCONFIG.linrh64/bin";
+
+$dsroot = "/export/data1/datastore/dsroot";
+$dbname = "gpc1";
+
+// END Local configuration 
+
+# this script sets up the environment to run IPP commands with current directory
+# $WORKDIR
+$SCRIPT    = "$PSBINDIR/pstamp_runcommand.sh $PSCONFDIR $PSCONFIG $WORKDIR";
+
+// Initialize variables
+$output_array = array();
+
+$request_id = 0;
+$last_request_id = 0;
+
+$raw_selected = "";
+$chip_selected = "";
+$warp_selected = "";
+$stack_selected = "";
+$diff_selected = "";
+
+$exp_checked = "";
+$file_checked = "";
+$coord_checked = "";
+$list_checked = "";
+$pstamp_checked = "";
+$get_checked = "";
+
+$command_line = "";
+$error_line = "";
+$command_status = "";
+
+$outFileset = "";
+
+// Initialize the request variables that we depend upon
+$rvar_project="";
+
+$gpc1_selected="";
+$mops_selected="";
+$simtest_selected="";
+
+$require_class_id = 0;
+
+$rvar_user_tag="";
+$rvar_select_by = "";
+$rvar_img_type = "";
+$rvar_id = "";
+$rvar_class_id = "";
+$rvar_cell_id = "";
+
+$rvar_center_type = "";
+$rvar_range_type  = "";
+
+$rvar_RA = "";
+$rvar_DEC = "";
+$rvar_dRA = "";
+$rvar_dDEC = "";
+$rvar_X = "";
+$rvar_Y = "";
+$rvar_W = "";
+$rvar_H = "";
+
+$rvar_cmd_mode = "";
+$rvar_last_cmd_mode = "";
+$rvar_request_id = 0;
+$rvar_last_request_id = 0;
+
+// now get the values from this post
+
+import_request_variables("gp", "rvar_");
+
+if ($rvar_project == "gpc1") {
+    $gpc1_selected = "selected";
+    $require_class_id = 1;
+} else if ($rvar_project == "simtest") {
+    $simtest_selected = "selected";
+    $require_class_id = 0;
+} else {
+    $mops_selected = "selected";
+    $require_class_id = 0;
+}
+
+// figure out which select_by is set and save it's checked value 
+
+if ($rvar_select_by == "exposure_id") {
+    $exp_checked = "CHECKED";
+} else if ($rvar_select_by == "db_id") {
+    $file_checked = "CHECKED";
+} else if ($rvar_select_by == "coord") {
+    $coord_checked = "CHECKED";
+} else {
+    // nothing checked default to By Exposure
+    $exp_checked = "CHECKED";
+}
+
+// get the Image type
+if ($rvar_img_type == "raw") {
+    $raw_selected = "selected";
+} else if ($rvar_img_type == "chip") {
+    $chip_selected = "selected";
+} else if ($rvar_img_type == "warp") {
+    $warp_selected = "selected";
+} else if ($rvar_img_type == "stack") {
+    $stack_selected = "selected";
+} else if ($rvar_img_type == "diff") {
+    $diff_selected = "selected";
+}
+
+// is the center is specified in Pixels or sky coordinates
+if ($rvar_center_type == "Sky") {
+    $sky_checked="checked";
+    $pix_checked="";
+} else {
+    $sky_checked="";
+    $pix_checked="checked";
+}
+
+// is the range is specified in Pixels or sky coordinates
+if ($rvar_range_type == "Sky") {
+    $rsky_checked="checked";
+    $rpix_checked="";
+} else {
+    $rpix_checked="checked";
+    $rsky_checked="";
+}
+
+// When request_id is non-zero we respond to posts by check the status of that request
+// request_id gets set to zero when the status of all jobs for the request is 'stop'
+$request_id = $rvar_request_id;
+$last_request_id = $rvar_last_request_id;
+
+// during request the page doesn't have cmd_mode set. So we remember the lat real value
+// and use that
+if (!$rvar_cmd_mode) {
+    $rvar_cmd_mode = "$rvar_last_cmd_mode";
+}
+
+if ($rvar_cmd_mode == "Make Stamps") {
+    $pstamp_checked = "checked";
+    $list_checked = "";
+    $get_checked = "";
+} else if ($rvar_cmd_mode == "Get Images") {
+    $get_checked = "checked";
+    $list_checked = "";
+    $pstamp_checked = "";
+} else {
+    $list_checked = "checked";
+    $get_checked = "";
+    $pstamp_checked = "";
+}
+
+
+// echo "rvar_request_id: $rvar_request_id\n";
+
+// HERE is the logic for running the various commands
+
+// How do we know? Well if we aren't running the initial get of the web page?
+// Well, in that case rvar_img_type is not set so key off of that
+// TODO: find a better way to decide whether or not to run commands
+
+if ($rvar_img_type) {
+
+    $jobFinished = 0;
+    if ($request_id == 0) {
+        try {
+            $command_line = build_request_cmd();
+            $error_line = "";
+            run_command($command_line);
+            if (! $list_checked) {
+                // The only output from a successful run is the request_id
+                $request_id = Array_pop($output_array);
+                $last_request_id = $request_id;
+                setcookie("our_request_id", $request_id);
+                // echo "The request id is $request_id\n";
+                if (count($output_array) != 0) {
+                    throw new Exception("unexpected output returned by pstampwebrequest.");
+                }
+
+                $jobRunning = getRequestStatus();
+                if (!$jobRunning) {
+                    $jobFinished = 1;
+                    $request_id = 0;
+                }
+
+            } else {
+                $last_request_id = 0;
+            }
+        } catch (Exception $e) {
+            $error_line = $e->getMessage();
+        }
+    } else {
+        try {
+            // get the list of jobs for the request
+            // echo "calling getRequestStatus\n";
+            $jobRunning = getRequestStatus();
+
+            if (!$jobRunning) {
+                $jobFinished = 1;
+                $request_id = 0;
+            }
+
+        } catch (Exception $e) {
+            echo "Got Exception $request_id $e\n";
+            $error_line = $e->getMessage();
+        }
+    }
+    if ($last_request_id) {
+        // This doesn't work for get_image
+        listJobs($last_request_id, $jobFinished);
+    }
+}
+
+function build_request_cmd()
+{
+    global $rvar_project, $rvar_user_tag;
+    global $sky_checked, $rsky_checked;
+    global $list_checked;
+    global $get_checked;
+    global $rvar_RA, $rvar_DEC;
+    global $rvar_dRA, $rvar_dDEC;
+    global $rvar_X, $rvar_Y;
+    global $rvar_W, $rvar_H;
+    global $exp_checked, $file_checked, $coord_checked;
+    global $rvar_img_type;
+    global $rvar_id, $rvar_class_id;
+    global $command_line;
+    global $dbname;
+    global $require_class_id;
+    global $PSCONFDIR, $PSCONFIG, $WORKDIR;
+    global $SCRIPT;
+
+    $making_stamps = 1;
+    $cmd = "$SCRIPT pstamp_webrequest.pl";
+
+    if ($list_checked) {
+        $cmd .= " --job_type list_uri";
+        $making_stamps = 0;
+    } else if ($get_checked) {
+        $making_stamps = 0;
+        $cmd .= " --job_type get_image";
+    }
+
+    if ($dbname) {
+        $cmd .= " --dbname $dbname";
+    }
+
+    if (! $rvar_project ) {
+        throw new Exception('project must be specified.');
+    }
+    $cmd .= " --project $rvar_project";
+
+    if (!$rvar_user_tag) {
+        if ($making_stamps) {
+            throw new Exception('Stamp Name must be specified.');
+        }
+        $cmd .= " -stamp_name temp_tag";
+    } else {
+        $cmd .= " -stamp_name $rvar_user_tag";
+    }
+
+    if ($making_stamps || $coord_checked) {
+        // Set up the ROI parameters
+        if ($sky_checked) {
+            if (! $rvar_RA || ! $rvar_DEC) {
+                throw new Exception('RA and DEC must be specified.');
+            }
+            $cmd .= " -skycenter $rvar_RA $rvar_DEC";
+        } else {
+            if (! $rvar_X || ! $rvar_Y) {
+                throw new Exception('X and Y must be specified.');
+            }
+            $cmd .= " -pixcenter $rvar_X $rvar_Y";
+        }
+
+        if ($rsky_checked) {
+            if (! $rvar_dRA || ! $rvar_dDEC) {
+                throw new Exception('dRA and dDEC must be specified.');
+            }
+            $cmd .= " -arcrange $rvar_dRA $rvar_dDEC";
+        } else {
+            if (! $rvar_W || ! $rvar_H) {
+                throw new Exception('width and height must be specified.');
+            }
+            $cmd .= " -pixrange $rvar_W $rvar_H";
+        }
+    }
+
+
+    if (! $rvar_img_type) {
+        // this actually can't happen
+        throw new Exception('Must set image type.');
+    }
+
+    if ($exp_checked) {
+        if (! $rvar_id ) {
+            throw new Exception('Must set Exposure ID.');
+        }
+        $cmd .= " -byexp $rvar_img_type $rvar_id";
+    } else if ($file_checked) {
+        if (! $rvar_id ) {
+            throw new Exception('Must set ID.');
+        }
+        $cmd .= " -byid $rvar_img_type $rvar_id";
+    } else if ($coord_checked) {
+        // $cmd .= " -bycoord $rvar_img_type";
+        $coord_checked = "";
+        $exp_checked = "checked";
+        throw new Exception("Image selection by coordinate not implemented yet.");
+    }
+
+    if (($rvar_img_type == "raw") || ($rvar_img_type == "chip")) {
+        if ($require_class_id && ! $rvar_class_id ) {
+            throw new Exception("must specify Class ID with Image Type $rvar_img_type.");
+        }
+        if ((!$rvar_class_id) || ($rvar_class_id == "all")) {
+            $cmd .= " null";
+        } else {
+            $cmd .= " $rvar_class_id";
+        }
+    }
+
+    return escapeshellcmd($cmd);
+}
+
+function run_command($command_line)
+{
+    global $output_array;
+    global $error_line;
+    global $command_status;
+
+    //    echo "running $command_line\n";
+
+    exec ("$command_line", $output_array, $command_status);
+
+    $size = sizeof($output_array);
+    //     echo "command_status: $command_status output_array  contains $size lines\n";
+    if ($command_status == 0) {
+        // On success we just remember the results
+        $dump_results = 0;
+        if ($dump_results) {
+            echo "Output: $size lines\n";
+            for ($i = 0; $i < $size; $i++) {
+                echo "$output_array[$i]\n";
+            }
+        }
+    } else {
+        // copy the output to the error_line
+        $error_line = "";
+        for ($i = 0; $i < $size; $i++) {
+             $error_line .= "$output_array[$i]\n";
+        }
+    }
+
+}
+
+function printURL($line)
+{
+    global $request_id;
+    global $last_request_id;
+
+    echo "<tr><td>";
+    $doURL = 1;
+    if ($doURL) {
+//        echo "output_line: $line\n";
+        $elements = explode(" ", $line);
+        if (count($elements) == 3) {
+            $job_id   = $elements[0];
+            $state    = $elements[1];
+            $path     = $elements[2];
+            $fileName = basename($path);
+            if ($state == "stop") {
+                global $dsroot;
+                $fullpath = "$dsroot" . "$path";
+                if (file_exists($fullpath)) {
+                    // this job is done, list the url as a link
+                    echo "<a href=\"http:$path\" target=\"_blank\" type=\"image/fits\">";
+                    echo $fileName;
+                    echo "</a>";
+                    echo "&nbsp;&nbsp;&nbsp; request_id: $last_request_id &nbsp;&nbsp;";
+                    echo "job_id: $job_id &nbsp;&nbsp;&nbsp; state: $state";
+                } else {
+                    echo "request_id: $last_request_id  job_id: $job_id failed";
+                    echo "   $fullpath";
+                }
+            } else {
+                // TODO: refine this output
+                echo "$fileName&nbsp;&nbsp;&nbsp; request_id: $request_id &nbsp;&nbsp;&nbsp;";
+                echo "job_id: $job_id &nbsp;&nbsp;&nbsp; state: $state";
+            }
+        }
+    } else {
+        print "$line";
+    }
+
+    echo "</td></tr>";
+}
+
+function countRunningJobs()
+{
+    global $output_array;
+
+    $runningJobs = 0;
+    $size = sizeof($output_array);
+    for ($i = 0; $i < $size; $i++) {
+        $elements = explode(" ", $output_array[$i]);
+        if (count($elements) == 3) {
+            $state    = $elements[1];
+            if ($state != "stop") {
+                $runningJobs++;
+            }
+        } else {
+            throw new Exception ("incorrect data in job status: $output_array[$i]");
+        }
+    }
+    return $runningJobs;
+}
+
+function listJobs($request_id, $jobFinished)
+{
+    global $WORKDIR;
+    global $SCRIPT;
+
+    $command_line = "$SCRIPT pstamp_listjobs.pl $request_id";
+    global $dbname;
+    if ($dbname) {
+        $command_line .= " --dbname $dbname";
+    }
+
+    run_command($command_line);
+    if ($jobFinished) {
+        global $outFileset;
+        global $dsroot;
+        $parse_error = "$WORKDIR/$request_id/parse_error.txt";
+        #echo "reading $parse_error\n";
+        // readfile( $parse_error );
+        if (file_exists($parse_error)) {
+            $fhandle = fopen($parse_error, "r");
+            if ($fhandle) {
+                $contents = fread($fhandle, 1024);
+                if ($contents) {
+                    global $last_request_id;
+                    global $error_line;
+                    $error_line = "Request $last_request_id: $contents\n";
+                }
+            }
+        }
+    }
+}
+
+function getRequestStatus()
+{
+    global $request_id;
+    global $command_status;
+    global $output_array;
+    global $outFileset;
+    global $dbname;
+    global $SCRIPT;
+
+    $command_line = "$SCRIPT pstamptool -listreq -req_id $request_id -simple";
+    if ($dbname) {
+        $command_line .= " -dbname $dbname";
+    }
+    run_command($command_line);
+    if ($command_status == 0) {
+        $size = sizeof($output_array);
+        $runningReq = 0;
+        for ($i = 0; $i < $size; $i++) {
+            $elements = explode(" ", $output_array[$i]);
+            if (count($elements) >= 4) {
+                $state = $elements[2];
+                $outFileset = $elements[4];
+                if ($state != "stop") {
+                    $runningReq++;
+                }
+            } else {
+                throw new Exception ("incorrect data in job status: $output_array[$i]");
+            }
+        }
+        return $runningReq;
+    } else {
+        return 0;
+    }
+}
+
+?>
+
+<!-- Beginning of the HTML --------------------------------------------- -->
+<html>
+ <head>
+  <title>Postage Stamp Request Form</title>
+    <?php
+        if ($request_id != 0) {
+            // This doesn't do what I want. It does a get not a post
+
+            // echo '<META HTTP-EQUIV="refresh" CONTENT="5">';
+
+        }
+    ?>
+ </head>
+<form method="post">
+
+<body>
+
+<H1 align=center>
+Postage Stamp Request
+</h1>
+
+<!-- Whole page is a single column table -->
+
+
+<table width=90% align=center>
+
+
+<!-- first row in the main table is the image selector UI which consists of a 2 x 3 table -->
+<tr>
+<td>
+
+<table width=100% align=center>
+
+<!-- first row of image selector is "Project" pulldown menu "Select Image By" radio boxes -->
+<tr>
+<td width=20% >
+  <table>
+    <tr><td><b>Project:</b>&nbsp;&nbsp;&nbsp;</td>
+        <td><select name="project">
+           <option <?php echo $gpc1_selected;?> >gpc1
+           <option <?php echo $mops_selected;?> >megacam-mops
+           <option <?php echo $simtest_selected;?> >simtest
+        </td>
+    </tr>
+  </table>
+
+<td>
+&nbsp;<b>Select Images By:</b>&nbsp;&nbsp;&nbsp;
+<input type=radio name="select_by" value="exposure_id" <?php echo $exp_checked; ?> >Exposure Name
+&nbsp;
+<input type=radio name="select_by" value="db_id" <?php echo $file_checked; ?> >Database ID
+&nbsp;
+<input type=radio name="select_by" value="coord" <?php echo $coord_checked; ?> >Coordinates
+</td>
+</tr>
+
+
+<!-- the second row of the image selector table contains 
+    "Image Type" pulldown menu   "ID:" text input "Class ID" text input
+-->
+
+<tr>
+<td width=20%>
+<table>
+    <td><b>Image Type:</b>&nbsp;</td>
+    <td>
+        <select name="img_type">" >
+            <option <?php echo $raw_selected;?>   >raw
+            <option <?php echo $chip_selected;?>  >chip
+            <option <?php echo $warp_selected ;?> >warp
+            <option <?php echo $stack_selected;?> >stack   
+            <option <?php echo $diff_selected;?>  >diff
+        </select>
+    </td>
+</table>
+</td>
+
+<td>
+&nbsp;<b>ID/Name:</b>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+<input type="text" name="id" value= <?php echo $rvar_id; ?> >
+&nbsp;&nbsp;&nbsp;&nbsp;
+<b>
+<?php if (0 && $rvar_project == "gpc1") {
+        echo "Chip ID:";
+      } else {
+        echo "Class ID:";
+      }
+?>
+</b>
+&nbsp;<input type="text" name="class_id" size=10 value="<?php echo $rvar_class_id;?>" >
+
+&nbsp;&nbsp;&nbsp;&nbsp;
+<!-- add text input field for Cell ID, not used yet -->
+
+<!--
+<b>Cell ID:</b>&nbsp;<input type="text" name="cell_id" size=10 value="<?php echo $rvar_cell_id;?>" >
+
+-->
+
+</td>
+
+</tr>
+
+<!-- third row of table just contains a blank column followed by the user tag input -->
+<tr>
+<!-- blank label -->
+<td width=20% > </td>
+<!-- user tag input text -->
+<td>&nbsp;<b>Stamp Name:</b>&nbsp;<input type="text" name="user_tag" size=20 value=<?php echo $rvar_user_tag; ?> ></td>
+</tr>
+
+</table> <!-- end of Image selector table -->
+
+<!-- a blank row for space-->
+<tr height=20><td></td></tr>
+
+<!-- next row of main table contains the ROI input UI -->
+<tr align=center width=100%>
+    <td>
+    <!-- 2 row by 6 column table -->
+    <table width=100%>
+    <thead>
+    <tr>
+    <!-- <td></td><td></td><td><b>Center</b></td><td></td><td></td><td><b>Range</b></td> -->
+    <td><td><td><b>Center</b><td><td><td><b>Range</b>
+    </tr>
+    </thead>
+    <tr>
+        <td>
+            <input type=radio name="center_type" value="Sky" <?php echo $sky_checked; ?> >Sky
+        </td>
+        <td>
+            &nbsp;
+            RA:
+            &nbsp;
+            &nbsp;
+            <input type="text" name="RA" size=10  value= <?php echo $rvar_RA; ?> >
+        </td>
+        <td>
+            &nbsp;
+            DEC:
+            &nbsp;
+            <input type="text" name="DEC" size=10 value="<?php echo $rvar_DEC;?>" >
+        </td>
+        <td>
+            <input type=radio name="range_type" value="Sky" <?php echo $rsky_checked; ?> >Sky
+        </td>
+        <td>
+            &nbsp;
+            dRA:
+            &nbsp;
+            &nbsp;
+            <input type="text" name="dRA" size=10  value= <?php echo $rvar_dRA; ?> >
+        </td>
+        <td>
+            &nbsp;
+            dDEC:
+            &nbsp;
+            <input type="text" name="dDEC" size=10 value="<?php echo $rvar_dDEC;?>" >
+        </td>
+    </tr>
+    <tr>
+        <td>
+            <input type=radio name="center_type" value="Pixels" <?php echo $pix_checked; ?> >Pixels
+        </td>
+        <td>
+            &nbsp;
+            X:
+            &nbsp;
+            &nbsp;
+            &nbsp;
+            <input type="text" name="X" size=10  value= <?php echo $rvar_X; ?> >
+        </td>
+        <td>
+            &nbsp;
+            Y:
+            &nbsp;
+            &nbsp;
+            &nbsp;
+            <input type="text" name="Y" size=10 value="<?php echo $rvar_Y;?>" >
+        </td>
+        <td>
+            <input type=radio name="range_type" value="Pixels" <?php echo $rpix_checked; ?> >Pixels
+        </td>
+        <td>
+            &nbsp;
+            width:
+            &nbsp;
+            &nbsp;
+            <input type="text" name="W" size=10  value= <?php echo $rvar_W; ?> >
+        </td>
+        <td>
+            &nbsp;
+            height:
+            &nbsp;
+            <input type="text" name="H" size=10 value="<?php echo $rvar_H;?>" >
+        </td>
+    </tr>
+    </table>
+    </td>
+</tr>
+
+
+<!-- next row of the main table contains the Submit button and the Mode radio buttons -->
+
+<tr align=center width=100% height=50>
+  <td>
+  <table width=80%>
+  <tr>
+
+<?php
+  if ($request_id == 0): ?>
+    <td><input type=submit value="Submit"></td>
+    <td><b>Mode:</b>&nbsp;&nbsp;
+    <input type=radio name="cmd_mode" value="Make Stamps"<?php echo $pstamp_checked; ?> >Make Stamps
+    <input type=radio name="cmd_mode" value="Get Images" <?php echo $get_checked; ?> >Get Images
+    <input type=radio name="cmd_mode" value="List Images" <?php echo $list_checked; ?> >List Images
+    </td>
+<?php 
+  else: ?>
+    <td><input type=submit value="Check Status"></td>
+<!---    <td><input type=submit value="Cancel"></td> --->
+<?php
+  echo "<td><b>Request Id: $request_id";
+  endif; ?>
+
+  </tr>
+  </table>
+  </td>
+</tr>
+
+<!--- next row in the main table is the Results of the previous request --->
+
+<tr>
+    <td>
+    <b>Command:</b>&nbsp;&nbsp; <?php echo "$command_line\n";?>
+    </td>
+</tr>
+<tr>
+    <td>
+    <b>Status:</b>&nbsp;&nbsp;&nbsp; <?php echo "$command_status\n";?>
+    </td>
+</tr>
+<tr>
+    <td>
+    <b>Error:</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
+        <?php 
+            if ($error_line) {
+                echo "<pre>";
+                echo "$error_line\n";
+                echo "<pre>\n";
+            }
+        ?>
+    </td>
+</tr>
+
+<!-- a blank row for space -->
+<tr height=20><td> </td></tr>
+
+<tr>
+<td>
+<table align=center width=100% rules=none>
+<caption height=10 valign=center><b>Results</b></caption>
+
+
+<?php 
+    $size = sizeof($output_array);
+    // echo "<pre>size of output array is $size\n</pre>";
+    if ($command_status == 0) {
+        if ($list_checked) {
+            // in list mode the output is a list of image files, just list them
+            // later we might add links to cause a stamp to be made from a selected file
+            for ($i = 0; $i < $size; $i++)  {
+                // $uri = array_shift($output_array);
+                $uri = $output_array[$i];
+                echo "<tr><td>$uri</td></tr>";
+            }
+        } else {
+            // output the list of urls
+            for ($i = 0; $i < $size; $i++)  {
+                // $uri = array_shift($output_array);
+                $uri = $output_array[$i];
+                printURL($uri);
+            }
+        }
+    }
+?>
+</table>
+</td>
+</tr>
+<!-- a blank row for our hidden element here-->
+
+<!-- request_id being non-zero causes us to issue status requests instead of new requsts -->
+<!--
+
+need a way to cancel a request There's probably no reason to have the value hidden, but we do
+need to set it as the last thing that we do
+
+-->
+
+<tr height=20>
+<td>
+    <input type="hidden" name="request_id" value=<?php echo $request_id ?> >
+</td>
+<td>
+    <input type="hidden" name="last_request_id" value=<?php echo $last_request_id ?> >
+</td>
+<td>
+    <input type="hidden" name="last_cmd_mode" value=<?php echo "\"$rvar_cmd_mode\"" ?> >
+</td>
+</tr>
+</table>
+
+<!-- The end -->
+
+<p>
+<pre>
+
+<!--- rvar_select_by = <?php echo $rvar_select_by; ?>
+--->
+
+<?php 
+    // dump parameters 
+
+    // phpinfo(32);
+
+?>
+
+</body>
+</form>
+
+</html>
Index: /tags/sj_tags/sj_root_20080929/pstest/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstest/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstest/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+bin
Index: /tags/sj_tags/sj_root_20080929/pstest/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstest/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstest/Makefile	(revision 22322)
@@ -0,0 +1,59 @@
+default: tests
+help:
+	@echo "USAGE: make pstest"
+
+CC      =       gcc
+SRC     =       src
+BIN     =       bin
+
+DESTBIN =       $(HOME)/src/bin/$(ARCH)
+
+LPSLIB  :=      $(shell pslib-config --libs)
+IPSLIB  :=      $(shell pslib-config --cflags)
+
+INCS	= 	$(IPSLIB)
+LIBS	= 	$(LPSLIB)
+CFLAGS	=	$(INCS) -std=c99 -Wall -Werror -g
+LFLAGS	=	$(LIBS) 
+
+TESTS = \
+$(BIN)/tst_psPolynomial.$(ARCH)
+
+tests: $(TESTS)
+
+# dependancy rules for binary code #########################
+.PRECIOUS: %.$(ARCH).o
+.PRECIOUS: $(BIN)/%.$(ARCH)
+
+%.$(ARCH).o : %.c
+	$(CC) $(CFLAGS) -c $< -o $@
+
+$(BIN)/%.$(ARCH) : $(SRC)/%.$(ARCH).o
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	$(CC) $^ -o $@ $(LFLAGS)
+	@echo "done with $@"
+
+$(DESTBIN)/%: $(BIN)/%.$(ARCH)
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	rm -f $(DESTBIN)/$*
+	cp $(BIN)/$*.$(ARCH) $(DESTBIN)/$*
+
+$(INSTALL): % : $(BIN)/%.$(ARCH)
+
+%.clean :
+	rm -f $(BIN)/$*.$(ARCH)
+	rm -f $(SRC)/*.$(ARCH).o
+
+%.install:
+	make $(DESTBIN)/$*
+
+# utilities #################################################
+
+install:
+	for i in $(INSTALL); do make $$i.install; done
+
+clean:	
+	rm -f $(BIN)/*.$(ARCH)
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
Index: /tags/sj_tags/sj_root_20080929/pstest/src/tst_psPolynomial.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pstest/src/tst_psPolynomial.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pstest/src/tst_psPolynomial.c	(revision 22322)
@@ -0,0 +1,526 @@
+#include <stdio.h>
+#include <math.h>
+#include "pslib_strict.h"
+#include "psTest.h"
+
+static psS32 testPolynomial1DEval_01(void);
+static psS32 testPolynomial1DEvalVector_01(void);
+static psS32 testPolynomial1DEvalVector_02(void);
+static psS32 testVectorFitPolynomial1D_01(void);
+static psS32 testVectorFitPolynomial1D_02(void);
+static psS32 testVectorClipFitPolynomial1D_01(void);
+
+/* list of test functions in test suite */
+testDescription tests[] = {
+                              {testPolynomial1DEval_01,1,"psPolynomial1DEval_01",0,false},
+                              {testPolynomial1DEvalVector_01,2,"psPolynomial1DEvalVector_01",0,false},
+                              {testPolynomial1DEvalVector_02,2,"psPolynomial1DEvalVector_02",0,false},
+                              {testVectorFitPolynomial1D_01,2,"psVectorFitPolynomial1D_01",0,false},
+                              {testVectorFitPolynomial1D_02,2,"psVectorFitPolynomial1D_02",0,false},
+                              {testVectorClipFitPolynomial1D_01,2,"psVectorFitClipPolynomial1D_01",0,false},
+                              {NULL}
+                          };
+
+/* basic main function: just need to define the test suite name */
+int main(int argc, char* argv[])
+{
+    //psTraceSetLevel("VectorFitPolynomial1DOrd", 6);
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    return !runTestSuite(stderr,"psPolynomialExamples", tests, argc, argv);
+}
+
+# define X0 +5.0
+# define X1 +3.0
+# define X2 -1.0
+# define X3 -2.0
+
+
+static psPolynomial1D *makePolynomial1D(int Nord, psPolynomialType type, int ordMask) {
+
+    // define the polynomial
+    psPolynomial1D *poly = psPolynomial1DAlloc(Nord, type);
+    switch (Nord) {
+      case 3:
+	poly->coeff[3] = (ordMask==3) ?  0:X3;
+      case 2:
+	poly->coeff[2] = (ordMask==2) ?  0:X2;
+      case 1:
+	poly->coeff[1] = (ordMask==1) ?  0:X1;
+      case 0:
+	poly->coeff[0] = (ordMask==0) ?  0:X0;
+	break;
+      default:
+	break;
+    }
+    return (poly);
+}
+
+static void fillVectors(psVector *x, psVector *y, int Nord, int Npts, psPolynomialType type) {
+
+    psF32 X;//, P1, P2, P3;
+    double c[] = {(double)X0,(double)X1,(double)X2,(double)X3};
+    //double sv,d,dd;
+    double sum, Tn, Tn_1, Tn_2;
+
+    for (int i = 0; i < Npts; i++) {
+	X = (float)i/Npts - 0.5;
+	x->data.F32[i] = X;
+	switch (type) {
+	  case PS_POLYNOMIAL_ORD:
+	    switch (Nord) {
+	      case 0:
+		y->data.F32[i] = c[0];
+		break;
+	      case 1:
+		y->data.F32[i] = c[0] + c[1]*X;
+		break;
+	      case 2:
+		y->data.F32[i] = c[0] + c[1]*X + c[2]*X*X;
+		break;
+	      case 3:
+		y->data.F32[i] = c[0] + c[1]*X + c[2]*X*X + c[3]*X*X*X;
+		break;
+	      default:
+		y->data.F32[i] = 0;
+	    }
+	    break;
+	  case PS_POLYNOMIAL_CHEB:
+	    switch (Nord) {
+	      case 0:
+		y->data.F32[i] = c[0];
+		break;
+	      case 1:
+		y->data.F32[i] =c[0] + c[1]*X;
+		break;
+	      default:
+		//Clenshaw's formula approach (NR 5.5)
+		//d=0.0;
+		//dd=0.0;
+		//for (int j=Nord; j>=1; j--) {
+		//    sv = d;
+		//    d = (double)2.0*(double)X*d - dd + c[j];
+		//    dd = sv;
+		//}
+		//y->data.F32[i] = (double)X*d-dd+(double)0.5*c[0];
+
+		//Straightforward approach
+		Tn_1=X; //T1
+		Tn_2=1; //T0
+		sum=0.;
+		for (int j=2; j<=Nord; j++) {
+		    Tn = 2.0*X*Tn_1 - Tn_2;
+		    sum = sum + c[j]*Tn;
+		    Tn_2 = Tn_1;
+		    Tn_1 = Tn;
+		}
+		y->data.F32[i] = sum + c[1]*X + c[0] - 0.5*c[0];
+	    }
+	    break;
+	}
+    }
+    return;
+}
+
+# define NPTS 10
+
+int testPolynomial1DEval_01(void) {
+    
+    //# define NORD 3
+    int NORD;
+    int maxNORD = 3;
+    int retval = 0;
+    psPolynomialType type[] = {PS_POLYNOMIAL_ORD, PS_POLYNOMIAL_CHEB};
+
+    for (int k = 0; k<1; k++) {  //loop over the 2 polynomial types
+	for (int j = 1; j <= maxNORD; j++) {  //loop over the polynomial orders
+	    int nGood = 0, nBad = 0; 
+	    NORD = j; 
+	    //printf("Testing NORD=%d\n", NORD);
+	    psPolynomial1D *poly = makePolynomial1D(NORD, type[k], -1); 
+	    
+	    //create x vector, known y vector: 
+	    psVector *x = psVectorAlloc (NPTS, PS_TYPE_F32); 
+	    psVector *y = psVectorAlloc (NPTS, PS_TYPE_F32); 
+	    fillVectors (x, y, NORD, NPTS, type[k]); 
+	    
+	    psF32 xV, yV; 
+	    
+	    for (int i = 0; i < NPTS; i++) { 
+		xV = x->data.F32[i]; 
+		yV = psPolynomial1DEval (poly, xV); 
+		//Change some values around to make it fail!
+		//if (i>90 && NORD==2) {yV = yV + 0.1;}
+		if (yV == y->data.F32[i]) { 
+		    nGood++; 
+		} else { 
+		    nBad++; 
+		    printf("%.3f   -->  %.10f  & %.10f\tdel=%e\n",xV,yV,y->data.F32[i],y->data.F32[i]-yV); 
+		} 
+	    } 
+	    psFree (x); 
+	    psFree (y); 
+	    psFree (poly); 
+	    
+	    if (nBad > 0) { 
+		psError(PS_ERR_UNKNOWN,true,"%d incorrect polynomial evaluations for NORD=%d\n", nBad, NORD); 
+		retval = retval + pow(2,NORD); 
+	    } 
+	}
+    }
+    return retval;
+}
+
+
+int testPolynomial1DEvalVector_01(void) {
+    
+    //# define NORD 3
+    int NORD;
+    int maxNORD = 3;
+    int retval = 0;
+    psPolynomialType type[] = {PS_POLYNOMIAL_ORD, PS_POLYNOMIAL_CHEB};
+    
+    for (int k = 0; k<1; k++) {  //loop over the 2 polynomial types
+	for (int j = 1; j <= maxNORD; j++) {  //loop over the polynomial orders
+	    int nGood = 0, nBad = 0; 
+	    NORD = j; 
+	    //printf("Testing NORD=%d\n", NORD);
+	    psPolynomial1D *poly = makePolynomial1D(NORD, type[k], -1); 
+	    
+	    // create x vector, known y vector:
+	    psVector *x = psVectorAlloc (NPTS, PS_TYPE_F32); 
+	    psVector *y = psVectorAlloc (NPTS, PS_TYPE_F32); 
+	    fillVectors (x, y, NORD, NPTS, type[k]); 
+	    
+	    psVector *xV = x; 
+	    psVector *yV = psPolynomial1DEvalVector(poly, xV); 
+	    
+	    for (int i = 0; i < NPTS; i++) { 
+		//Change some values around to make it fail!
+		//if (i>90 && NORD==2) {yV->data.F32[i] = yV->data.F32[i] + 0.1;}
+		if (yV->data.F32[i] == y->data.F32[i]) { 
+		    nGood++; 
+		} else { 
+		    nBad++; 
+		    printf("%.3f   -->  %.3f  & %.3f\tdel=%e\n",xV->data.F32[i],yV->data.F32[i],y->data.F32[i],y->data.F32[i]-yV->data.F32[i]); 
+		} 
+	    } 
+	    psFree (x); 
+	    psFree (y); 
+	    psFree (yV); 
+	    psFree (poly); 
+	    
+	    if (nBad > 0) { 
+		psError(PS_ERR_UNKNOWN,true,"%d incorrect polynomial evaluations for NORD=%d\n", nBad, NORD); 
+		retval = retval + pow(2,NORD); 
+	    } 
+	}
+    }
+    return retval;
+}
+int testPolynomial1DEvalVector_02(void) {
+    //  check that psPolynomial1DEvalVector obeys the psPolynomial1D.mask 
+    int NORD;
+    int maxNORD = 3;
+    int retval = 0;
+    psPolynomialType type[] = {PS_POLYNOMIAL_ORD, PS_POLYNOMIAL_CHEB};
+    
+    for (int k = 0; k<1; k++) {  //loop over the 2 polynomial types
+	for (int imask=0; imask<=maxNORD; imask++) {  // mask one order at a time
+	    int j= maxNORD;
+	    NORD = j;
+	    int nGood = 0, nBad = 0; 
+	    psPolynomial1D *poly       = makePolynomial1D(NORD, type[k], -1);
+	    psPolynomial1D *polynomask = makePolynomial1D(NORD, type[k], imask);
+	    poly->mask[imask]=1;
+
+	    // create x vector, known y vector:
+	    psVector *x = psVectorAlloc (NPTS, PS_TYPE_F32); 
+	    psVector *y = psVectorAlloc (NPTS, PS_TYPE_F32); 
+	    fillVectors (x, y, NORD, NPTS, type[k]); 
+	    
+	    psVector *xV = x; 
+	    psVector *yV = psPolynomial1DEvalVector(poly, xV); 
+	    psVector *yVnm = psPolynomial1DEvalVector(polynomask, xV); 
+	    
+	    for (int i = 0; i < NPTS; i++) { 
+		if (yV->data.F32[i] == yVnm->data.F32[i]) { 
+		    nGood++; 
+		} else { 
+		    nBad++; 
+		    printf("x:%.3f  y:%.3f , %.3f , %.3f\tdel=%e\n",xV->data.F32[i],y->data.F32[i],yV->data.F32[i],yVnm->data.F32[i],yVnm->data.F32[i]-yV->data.F32[i]); 
+		} 
+	    } 
+	    psFree (x); 
+	    psFree (y); 
+	    psFree (yV); 
+	    psFree (yVnm); 
+	    psFree (poly); 
+	    psFree (polynomask); 
+	    
+	    if (nBad > 0) { 
+		psError(PS_ERR_UNKNOWN,true,"%d incorrect polynomial evaluations for NORD=%d\n", nBad, NORD); 
+		retval = retval + pow(2,NORD); 
+	    } 
+	}
+    }
+    return retval;
+}
+
+
+
+int testVectorFitPolynomial1D_01(void) { 
+    int retval=0;
+    int NORD, maxNORD=3;
+    float tolerance = 1.0E-5;
+    psPolynomialType type[] = {PS_POLYNOMIAL_ORD, PS_POLYNOMIAL_CHEB};
+    
+    for (int k = 0; k<1; k++) {  //loop over the 2 polynomial types   XXX: not using Chebyshevs right now!!!
+	for (int j = 1; j <= maxNORD; j++) {  //loop over the polynomial orders 
+	    int nGood = 0, nBad = 0; 
+	    NORD = j;
+	    printf("Testing NORD=%d\n", NORD);
+	    psPolynomial1D *poly = makePolynomial1D(NORD, type[k], -1);
+	    psPolynomial1D *polycopy = makePolynomial1D(NORD, type[k], -1);
+	    psVector *f = psVectorAlloc(NPTS, PS_TYPE_F32);
+	    psVector *fErr = psVectorAlloc(NPTS, PS_TYPE_F32);
+	    psVector *x = psVectorAlloc(NPTS, PS_TYPE_F32);
+	    
+	    fillVectors(x, f, NORD, NPTS, type[k]);
+	    psTime *now = psTimeGetNow(PS_TIME_UTC);
+	    const psRandom *r = psRandomAlloc(PS_RANDOM_TAUS, now->sec);
+	    for (int i = 0; i < NPTS; i++) {
+		double errgen = psRandomGaussian(r);
+		double amp = 1.0E-3;
+		fErr->data.F32[i] = amp*errgen;
+		//fErr->data.F32[i] = 0.;
+	    }
+	    psFree(now);
+	    psFree(r);
+
+	    //f->data.F32[NPTS-1] = X0 + 10.*X1*x->data.F32[NPTS-1];
+	    //fErr->data.F32[NPTS-1] = 0.0000001;
+
+
+	    psVectorFitPolynomial1D(poly, NULL, 0, f, fErr, x);
+	    for (int i = 0; i <= NORD; i++) { 
+		if (poly->coeff[i] <= polycopy->coeff[i]+tolerance 
+		      &&
+		    poly->coeff[i] >= polycopy->coeff[i]-tolerance) { 
+		    nGood++; 
+		} else { 
+		    nBad++; 
+		    printf("polycopy->coeff[%d]=%.20f    poly->coeff[%d]=%.20f\n",i,polycopy->coeff[i],i,poly->coeff[i]); 
+		}
+		if (polycopy->coeffErr[i] != poly->coeffErr[i]) { 
+		    printf("polycopy->coeffErr[%d]=%.20f    poly->coeffErr[%d]=%.20f\n",i,polycopy->coeffErr[i],i,poly->coeffErr[i]); 
+		}		
+	    } 
+
+	    if (nBad > 0) { 
+		psError(PS_ERR_UNKNOWN,true,"%d incorrect polynomial evaluations for NORD=%d\n", nBad, NORD); 
+		retval = retval + pow(2,NORD); 
+	    } 
+
+	    printf("NORD: %d   retval: %d\n",NORD,retval);
+	    psFree(poly);
+	    psFree(polycopy);
+	    psFree(f);
+	    psFree(fErr);
+	    psFree(x);	    
+	}
+    }
+    return retval;
+}
+
+
+int testVectorFitPolynomial1D_02(void) { 
+    // this time test that the masking in psPolynomial1D works correctly
+    int retval=0;
+    int NORD, maxNORD=3;
+    float tolerance = 1.0E-5;
+    psPolynomialType type[] = {PS_POLYNOMIAL_ORD, PS_POLYNOMIAL_CHEB};
+    
+    for (int k = 0; k<1; k++) {  //loop over the 2 polynomial types   XXX: not using Chebyshevs right now!!!
+	for (int imask=0; imask<=maxNORD; imask++) {  // mask one order at a time
+	    int j= maxNORD;
+	    NORD = j;
+	    int nGood = 0, nBad = 0; 
+	    psPolynomial1D *poly       = makePolynomial1D(NORD, type[k], -1);
+	    psPolynomial1D *polynomask = makePolynomial1D(NORD, type[k], imask);
+	    poly->mask[imask]=1;
+	    //poly and polynomask look the same now.
+
+	    psVector *mask = psVectorAlloc(NPTS, PS_TYPE_U8);
+	    psVector *f = psVectorAlloc(NPTS, PS_TYPE_F32);
+	    psVector *fErr = psVectorAlloc(NPTS, PS_TYPE_F32);
+	    psVector *x = psVectorAlloc(NPTS, PS_TYPE_F32);
+	    
+	    fillVectors(x, f, NORD, NPTS, type[k]);
+	    
+	    // add random errors to the vector
+	    psTime *now = psTimeGetNow(PS_TIME_UTC);
+	    const psRandom *r = psRandomAlloc(PS_RANDOM_TAUS, now->sec);
+	    for (int i = 0; i < NPTS; i++) {
+		double errgen = psRandomGaussian(r);
+		double amp = 1.0E-3;
+		fErr->data.F32[i] = amp*errgen;
+		//fErr->data.F32[i] = 0.;
+	    }
+	    psFree(now);
+	    psFree(r);
+	    
+	    
+	    //mask->data.U8[imask] = 0xff;
+	    //for (int ii=0; ii<=mask->n; ii++) {
+	    //printf("  %d",mask->data.U8[ii]);
+	    //}
+	    //printf("\n");
+
+	    poly->mask[imask] = 1;
+	    for (int ii=0; ii<=poly->nX; ii++) {
+		printf("%x  ", poly->mask[ii]);
+	    }
+	    printf("\n");
+
+	    psVectorFitPolynomial1D(poly, NULL, 0xff, f, fErr, x);
+	    psVectorFitPolynomial1D(polynomask, NULL, 0xff, f, fErr, x);
+
+	    psVector *xx = psVectorAlloc(NPTS, PS_TYPE_F32);
+	    psVector *yy = psPolynomial1DEvalVector(poly, xx);
+	    psVector *del;
+	    psBinaryOp(del, f, "-", yy);
+	    psPolynomial1D *polydel = makePolynomial1D(NORD, type[k], -1);
+	    psVectorFitPolynomial1D(polydel, NULL,0xff, f, fErr, x);
+	    
+
+
+	    //mask->data.U8[imask] = 0;
+	    poly->mask[imask] = 0;
+	    for (int i = 0; i <= NORD; i++) { 
+		printf("polynomask->coeff[%d]=%.8f    poly->coeff[%d]=%.8f    polydel->coeff[%d]=%.8f\n",i,polynomask->coeff[i],i,poly->coeff[i],i,polydel->coeff[i]); 
+		if (poly->coeff[i] <= polynomask->coeff[i]+tolerance 
+		    &&
+		    poly->coeff[i] >= polynomask->coeff[i]-tolerance) { 
+		    nGood++; 
+		} else { 
+		    nBad++; 
+		    //printf("polynomask->coeff[%d]=%.8f    poly->coeff[%d]=%.8f\n",i,polynomask->coeff[i],i,poly->coeff[i]); 
+		}
+		if (polynomask->coeffErr[i] != poly->coeffErr[i]) { 
+		    //printf("polynomask->coeffErr[%d]=%.8f    poly->coeffErr[%d]=%.8f\n",i,polynomask->coeffErr[i],i,poly->coeffErr[i]); 
+		}		
+	    }
+	    
+	    if (nBad > 0) { 
+		psError(PS_ERR_UNKNOWN,true,"%d incorrect polynomial evaluations for NORD=%d\n", nBad, NORD); 
+		retval = retval + pow(2,NORD); 
+	    } 
+	    
+	    printf("Mask Ord: %d   retval: %d\n", imask, retval);
+	    psFree(poly);
+	    psFree(polynomask);
+	    psFree(mask);
+	    psFree(f);
+	    psFree(fErr);
+	    psFree(x);	    
+	    psFree(xx);
+	    psFree(yy);
+	    psFree(del);
+	    psFree(polydel);
+
+	}
+    }
+    return retval;
+}
+
+
+
+
+
+int testVectorClipFitPolynomial1D_01(void) { 
+    int retval=0;
+    int NORD, maxNORD=3;
+    double Nsigma = 3.;
+    int Niter= 3.;
+    float tolerance = 1.0E-5;
+    psPolynomialType type[] = {PS_POLYNOMIAL_ORD, PS_POLYNOMIAL_CHEB};
+    
+    for (int k = 0; k<1; k++) {  //loop over the 2 polynomial types  XXX: psVectorClipFirPolynomial1D does not
+	                          //                                  work with Chebyshev now
+	for (int j = 1; j <= maxNORD; j++) {  //loop over the polynomial orders
+	    int nGood = 0, nBad = 0; 
+	    NORD = j;
+	    printf("Testing NORD=%d\n", NORD);
+	    psPolynomial1D *poly = makePolynomial1D(NORD, type[k], -1);
+	    psPolynomial1D *polycopy = makePolynomial1D(NORD, type[k], -1);
+	    psStats *stats = psStatsAlloc(PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+	    stats->clipSigma = Nsigma;
+	    stats->clipIter  = Niter;
+	    psVector *f = psVectorAlloc(NPTS, PS_TYPE_F32);
+	    psVector *fErr = psVectorAlloc(NPTS, PS_TYPE_F32);
+	    psVector *x = psVectorAlloc(NPTS, PS_TYPE_F32);
+	    
+	    fillVectors(x, f, NORD, NPTS, type[k]);
+	    psTime *now = psTimeGetNow(PS_TIME_UTC);
+	    const psRandom *r = psRandomAlloc(PS_RANDOM_TAUS, now->sec);
+	    for (int i = 0; i < NPTS; i++) {
+		double errgen = psRandomGaussian(r);
+		double amp = 1.0E-3;
+		fErr->data.F32[i] = amp*errgen;
+		//fErr->data.F32[i] = 0.;
+	    }
+	    psFree(now);
+	    psFree(r);
+
+	    //f->data.F32[NPTS-1] = X0 + 10.*X1*x->data.F32[NPTS-1];
+	    //fErr->data.F32[NPTS-1] = 0.0000001;
+
+
+	    psVectorClipFitPolynomial1D(poly, stats, NULL, 2, f, fErr, x);
+	    for (int i = 0; i <= NORD; i++) { 
+		if (poly->coeff[i] <= polycopy->coeff[i]+tolerance 
+		      &&
+		    poly->coeff[i] >= polycopy->coeff[i]-tolerance) { 
+		    nGood++; 
+		} else { 
+		    nBad++; 
+		    printf("polycopy->coeff[%d]=%.20f    poly->coeff[%d]=%.20f\n",i,polycopy->coeff[i],i,poly->coeff[i]); 
+		}
+		if (polycopy->coeffErr[i] != poly->coeffErr[i]) { 
+		    printf("polycopy->coeffErr[%d]=%.20f    poly->coeffErr[%d]=%.20f\n",i,polycopy->coeffErr[i],i,poly->coeffErr[i]); 
+		}		
+	    } 
+
+	    if (nBad > 0) { 
+		psError(PS_ERR_UNKNOWN,true,"%d incorrect polynomial evaluations for NORD=%d\n", nBad, NORD); 
+		retval = retval + pow(2,NORD); 
+	    } 
+
+	    printf("NORD: %d   retval: %d\n",NORD,retval);
+	    psFree(poly);
+	    psFree(polycopy);
+	    psFree(stats);
+	    psFree(f);
+	    psFree(fErr);
+	    psFree(x);	    
+	}
+    }
+    return retval;
+}
+
+
+
+
+
+
+
+//test psStats
+
+//test psGaussian
+
+//test psMinimization (and psMinimizeLMChi2 and psMinimizeChi2Powell?)
+
+//test psRandomUniform / psRandomGausian / psRandomPoisson
+
+//test psSpline?
Index: /tags/sj_tags/sj_root_20080929/pswarp/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/.cvsignore	(revision 22322)
@@ -0,0 +1,18 @@
+bin
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+missing
+config.guess
+libtool
+ltmain.sh
+stamp-h1
+config.sub
+pswarp.pc
Index: /tags/sj_tags/sj_root_20080929/pswarp/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/Makefile.am	(revision 22322)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *.pyc *~ core core.*
Index: /tags/sj_tags/sj_root_20080929/pswarp/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=pswarp
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE  failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/pswarp/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/configure.ac	(revision 22322)
@@ -0,0 +1,47 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.61)
+
+AC_INIT([pswarp], [0.1.1], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+dnl ------------------------------------------------------------
+
+AC_PATH_PROG([ERRORCODES], [psParseErrorCodes], [missing])
+if test "$ERRORCODES" = "missing" ; then
+  AC_MSG_ERROR([psParseErrorCodes is required])
+fi
+
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+PKG_CHECK_MODULES([PPSTATS], [ppStats >= 1.0.0]) 
+PKG_CHECK_MODULES([PSPHOT], [psphot >= 0.8.0]) 
+
+dnl Set CFLAGS for build
+IPP_STDOPTS
+CFLAGS="${CFLAGS} -Wall -Werror"
+echo "PSWARP_CFLAGS: $PSWARP_CFLAGS"
+echo "PSWARP_LIBS: $PSWARP_LIBS"
+
+AC_SUBST([PSWARP_CFLAGS])
+AC_SUBST([PSWARP_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/pswarp/doc/notes.txt
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/doc/notes.txt	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/doc/notes.txt	(revision 22322)
@@ -0,0 +1,29 @@
+
+- pswarp takes an image (and corresponding mask and weight images) and
+  warps the pixels to match the specified skycell projection and pixel
+  grid.  the astrometry of the input image is supplied by the header
+  of the image or by a specified SMP file. 
+
+- the input image, weight, mask are loaded into pmFPA structures with
+  the layout determined by the camera/format/layout
+
+- the skycell is loaded into a pmFPA structure.  at a minimum, a
+  single skycell may contain a single chip, cell, and readout, which
+  in turn correspond to the single set of pixels representing the
+  image region.  
+
+  * is there a need or use for a skycell to have a multilayered
+    structure equivalent in any way to the fpa->chip->cell->readout
+    layer?
+
+    - perhaps we will want to use a single fpa for a larger region
+      with small sky cells representing contiguous pixel regions.  
+
+    - perhaps we will want to warp an entire fpa exposure at once and
+      use a skyfpa to represent all of the possible output skycells
+      for a single input fpa exposure.
+
+- the output is a file or set of files corresponding to the skycell
+  files.  the details should be specified by the PSWARP.OUTPUT file
+  rule.
+
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/.cvsignore	(revision 22322)
@@ -0,0 +1,14 @@
+*.o
+*.lo
+.libs
+.deps
+Makefile
+Makefile.in
+pswarp
+pswarp.loT
+pswarpErrorCodes.h
+pswarpErrorCodes.c
+libpswarp.la
+config.h
+config.h.in
+stamp-h1
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/Makefile.am	(revision 22322)
@@ -0,0 +1,43 @@
+bin_PROGRAMS = pswarp
+pswarp_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PSWARP_CFLAGS)
+pswarp_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS) $(PPSTATS_LIBS) $(PSPHOT_LIBS) $(PSWARP_LIBS)
+
+pswarp_SOURCES = \
+	pswarp.c			\
+	pswarpArguments.c		\
+	pswarpCleanup.c			\
+	pswarpLoop.c			\
+	pswarpDefine.c			\
+	pswarpDefineSkycell.c		\
+	pswarpErrorCodes.c		\
+	pswarpMapGrid.c			\
+	pswarpMatchRange.c		\
+	pswarpParseCamera.c		\
+	pswarpPixelFraction.c		\
+	pswarpSetMaskBits.c		\
+	pswarpSetThreads.c	        \
+	pswarpTransformReadout.c	\
+	pswarpTransformSources.c \
+	pswarpTransformTile.c		\
+	pswarpVersion.c            
+
+noinst_HEADERS = \
+	pswarp.h \
+	pswarpErrorCodes.h
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
+### Error codes.
+BUILT_SOURCES = pswarpErrorCodes.h pswarpErrorCodes.c
+CLEANFILES = pswarpErrorCodes.h pswarpErrorCodes.c
+
+pswarpErrorCodes.h : pswarpErrorCodes.dat pswarpErrorCodes.h.in
+	$(ERRORCODES) --data=pswarpErrorCodes.dat --outdir=. pswarpErrorCodes.h
+
+pswarpErrorCodes.c : pswarpErrorCodes.dat pswarpErrorCodes.c.in pswarpErrorCodes.h
+	$(ERRORCODES) --data=pswarpErrorCodes.dat --outdir=. pswarpErrorCodes.c
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarp.c	(revision 22322)
@@ -0,0 +1,53 @@
+# include "pswarp.h"
+
+static void usage (void) {
+    fprintf(stderr, "USAGE: pswarp [-file image(s)] [-list imagelist] [options] (output) (skycell)\n");
+    fprintf(stderr, "  options:\n");
+    fprintf(stderr, "    [-astrom astrom.cmp] : provide an alternative astrometry calibration\n");
+    fprintf(stderr, "    [-mask mask.fits] : provide a corresponding mask image\n");
+    fprintf(stderr, "    [-weight weight.fits] : provide a corresponding weight image (pixel varience)\n");
+    psErrorStackPrint(stderr, "\n");
+    exit (2);
+}
+
+int main (int argc, char **argv)
+{
+    psTimerStart("pswarp");
+
+    psLibInit(NULL);
+
+    // model inits are needed in pmSourceIO
+    // models defined in psphot/src/models are not available in psastro
+    pmModelClassInit();
+
+    pmConfig *config = pswarpArguments(argc, argv);
+    if (!config) usage();
+
+    // load identify the data sources
+    if (!pswarpParseCamera(config)) {
+        psErrorStackPrint(stderr, "error setting up the camera\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    if (!pswarpOptions(config)) {
+        psErrorStackPrint(stderr, "error parsing options\n");
+        exit(PS_EXIT_SYS_ERROR);
+    }
+
+    // load the skycell layout information
+    if (!pswarpDefine(config)) {
+        psErrorStackPrint(stderr, "error loading output definition\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // load and warp
+    if (!pswarpLoop(config)) {
+        psErrorStackPrint(stderr, "error warping data\n");
+        exit(PS_EXIT_DATA_ERROR);
+    }
+
+    psLogMsg("pswarp", 3, "complete pswarp run: %f sec\n", psTimerMark("pswarp"));
+    pswarpCleanup(config);
+    psLibFinalize();
+    exit(PS_EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarp.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarp.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarp.h	(revision 22322)
@@ -0,0 +1,94 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>  // for strcasecmp
+#include <unistd.h>   // for unlink
+#include <pslib.h>
+#include <psmodules.h>
+#include <psphot.h>
+
+#define THREADED 1
+
+#include "pswarpErrorCodes.h"
+#define PSWARP_RECIPE  "PSWARP" // Name of the recipe to use
+#define PSASTRO_RECIPE "PSASTRO" // Name of the recipe to use
+
+#define PSWARP_ANALYSIS_VARFACTOR "PSWARP.VARFACTOR" // Name for variance factor in analysis metadata
+#define PSWARP_ANALYSIS_GOODPIX   "PSWARP.GOODPIX" // Name for number of good pixels in analysis metadata
+
+// a single pswarpMap converts coordinates from one image to a second image
+// the linear model is only valid over a limited range of pixels
+typedef struct {
+    double Xo, Xx, Xy;
+    double Yo, Yx, Yy;
+    int xo;
+    int yo;
+} pswarpMap;
+
+// the pswarpMapGrid carries a collection of pswarpMag structures representing the
+// local value of the pswarpMap at different locations in the image.
+typedef struct {
+    pswarpMap ***maps;
+    int nXpts, nYpts;                   // number of x,y samples in the grid
+    int nXpix, nYpix;                   // x,y spacing in src image pixels of grid samples
+    double xMin,  yMin;                 // coordinate of first grid sample
+} pswarpMapGrid;
+
+typedef struct {
+    // values which are common to all tiles
+    pmReadout *input;
+    pmReadout *output;
+    pswarpMapGrid *grid;
+    psImageInterpolateOptions *interp;
+    psImage *region;
+
+    // input values for this tile
+    int gridX;
+    int gridY;
+
+    // output values for this tile
+    long goodPixels;                    // Number of good pixels
+    int xMin, xMax, yMin, yMax;         // Bounds of tile
+} pswarpTransformTileArgs;
+
+pswarpTransformTileArgs *pswarpTransformTileArgsAlloc();
+bool pswarpTransformTile (pswarpTransformTileArgs *args);
+
+pmConfig *pswarpArguments (int argc, char **argv);
+bool pswarpOptions(pmConfig *config);
+bool pswarpSetMaskBits (pmConfig *config);
+bool pswarpParseCamera (pmConfig *config);
+bool pswarpDefine (pmConfig *config);
+bool pswarpLoop (pmConfig *config);
+void pswarpCleanup (pmConfig *config);
+bool pswarpTransformReadout (pmReadout *output, pmReadout *input, pmConfig *config);
+bool pswarpTransformSources(pmReadout *output, pmReadout *input, pmConfig *config);
+
+bool pswarpMatchRange (int *minX, int *minY, int *maxX, int *maxY, pmReadout *dest, pmReadout *src);
+
+pswarpMap *pswarpMapAlloc ();
+pswarpMapGrid *pswarpMapGridAlloc (int Nx, int Ny);
+
+pswarpMapGrid *pswarpMapGridFromImage (pmReadout *dest, pmReadout *src, int nXpix, int nYpix);
+bool pswarpMapGridSetGrid (pswarpMapGrid *grid, int ix, int iy, int *gridX, int *gridY);
+bool pswarpMapGridCoordRange (pswarpMapGrid *grid, int gridX, int gridY, psPlane *min, psPlane *max);
+int pswarpMapGridNextGrid_X (pswarpMapGrid *grid, int gridX);
+int pswarpMapGridNextGrid_Y (pswarpMapGrid *grid, int gridY);
+double pswarpMapGridMaxError (pswarpMapGrid *grid);
+bool pswarpMapApply (double *outX, double *outY, pswarpMap *map, double inX, double inY);
+bool pswarpMapSetLocalModel (pswarpMap *map, pmReadout *dest, pmReadout *src, int ix, int iy);
+
+bool pswarpDefineSkycell (pmFPAfile **outFile, pmConfig **outConfig, pmConfig *config,
+                          const char *filename, const char *argname);
+
+/// Check to see if the readout has a minimum fraction of "lit" pixels
+bool pswarpPixelFraction(const pmReadout *readout, ///< Readout to inspect
+                         psMetadata *stats, ///< Statistics to update with the result
+                         const pmConfig *config ///< Configuration
+    );
+
+// define threads for this program
+bool pswarpSetThreads ();
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpArguments.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpArguments.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpArguments.c	(revision 22322)
@@ -0,0 +1,187 @@
+# include "pswarp.h"
+# include <glob.h>
+
+pmConfig *pswarpArguments (int argc, char **argv) {
+
+    bool status;
+    int N;
+
+    if (argc == 1) {
+        psError(PSWARP_ERR_ARGUMENTS, true, "No arguments supplied");
+        return NULL;
+    }
+
+    // load config data from default locations
+    pmConfig *config = pmConfigRead(&argc, argv, PSWARP_RECIPE);
+    if (config == NULL) {
+        psError(PSWARP_ERR_CONFIG, false, "Can't read site configuration");
+        return NULL;
+    }
+
+    // save the following additional recipe values based on command-line options
+    // these options override the PSWARP recipe values loaded from recipe files
+    pmConfigRecipeOptions (config, PSWARP_RECIPE);
+
+    pmConfigFileSetsMD(config->arguments, &argc, argv, "ASTROM",   "-astrom", "-astromlist");
+
+    // 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_DATA_STRING,
+                         "Only process these chips", argv[N]);
+        psArgumentRemove(N, &argc, argv);
+    }
+
+    // Statistics file
+    if ((N = psArgumentGet(argc, argv, "-stats"))) {
+        psArgumentRemove(N, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "STATS", PS_DATA_STRING,
+                         "Filename for statistics of output image", argv[N]);
+        psArgumentRemove(N, &argc, argv);
+    }
+
+    // Number of threads
+    if ((N = psArgumentGet(argc, argv, "-threads"))) {
+        psArgumentRemove(N, &argc, argv);
+        int nThreads = atoi(argv[N]);
+        psMetadataAddS32(config->arguments, PS_LIST_TAIL, "NTHREADS", 0, "number of warp threads", nThreads);
+        psArgumentRemove(N, &argc, argv);
+
+        // create the thread pool with number of desired threads, supplying our thread launcher function
+        // XXX need to determine the number of threads from the config data
+        psThreadPoolInit (nThreads);
+    }
+    pswarpSetThreads ();
+
+    // PSF determination?
+    if ((N = psArgumentGet(argc, argv, "-psf"))) {
+        psArgumentRemove(N, &argc, argv);
+        psMetadataAddBool(config->arguments, PS_LIST_TAIL, "PSF", 0, "Do PSF determination?", true);
+    }
+    if ((N = psArgumentGet(argc, argv, "-dumpconfig"))) {
+        psArgumentRemove(N, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "DUMP_CONFIG", PS_META_REPLACE,
+                         "Filename for configuration dump", argv[N]);
+        psArgumentRemove(N, &argc, argv);
+    }
+
+
+    if (!pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list")) {
+        psError(PSWARP_ERR_ARGUMENTS, true, "Missing -file (input) or -list (input)");
+        return NULL;
+    }
+
+    // the mask and weight entries are optional (build from gain?)
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "MASK",   "-mask",   "-masklist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "WEIGHT", "-weight", "-weightlist");
+
+    if (argc != 3) {
+        psError(PSWARP_ERR_ARGUMENTS, true, "Incorrect arguments supplied");
+        return NULL;
+    }
+
+
+    psArray *array;
+
+    // output position is fixed
+    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[1]);
+
+    // skycell position is fixed
+    array = psArrayAlloc(1);
+    array->data[0] = psStringCopy (argv[2]);
+    status = psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "SKYCELL", PS_DATA_ARRAY, "", array);
+    psFree (array);
+
+    psTrace("pswarp", 1, "Done with pswarpArguments...\n");
+    return (config);
+}
+
+// Parse the recipe and format into the arguments
+bool pswarpOptions(pmConfig *config)
+{
+    // Select the appropriate recipe
+    psMetadata *recipe  = psMetadataLookupPtr(NULL, config->recipes, PSWARP_RECIPE);
+    if (!recipe) {
+        psError(PSWARP_ERR_CONFIG, true, "Can't find %s recipe!\n", PSWARP_RECIPE);
+        return false;
+    }
+
+    // Get grid size
+    bool status;                        // Status of MD lookup
+    int nGridX = psMetadataLookupS32(&status, recipe, "GRID.NX");
+    if (!status || nGridX <= 0) {
+        nGridX = 128;
+        psWarning("GRID.NX is not set in the recipe --- defaulting to %d", nGridX);
+    }
+    int nGridY = psMetadataLookupS32(&status, recipe, "GRID.NY");
+    if (!status) {
+        nGridY = 128;
+        psWarning("GRID.NY is not set in the recipe --- defaulting to %d", nGridY);
+    }
+
+    // Get interpolation mode
+    const char *name = psMetadataLookupStr (&status, recipe, "INTERPOLATION.MODE"); // Name of interp mode
+    if (!name) {
+        name = "BILINEAR";
+        psLogMsg("pswarp", 3, "defaulting to %s interpolation", name);
+    }
+    psImageInterpolateMode interpolationMode = psImageInterpolateModeFromString(name); // Mode for interp.
+    if (interpolationMode == PS_INTERPOLATE_NONE) {
+        interpolationMode = PS_INTERPOLATE_BILINEAR;
+        psLogMsg ("pswarp", 3,
+                  "Unknown interpolation mode %s, defaulting to bilinear interpolation\n", name);
+        name = "BILINEAR";
+    }
+
+    float poorFrac = psMetadataLookupF32(&status, recipe, "POOR.FRAC"); // Frac of bad flux for a "poor"
+    if (!status) {
+        poorFrac = 0.0;
+        psWarning("POOR.FRAC is not set in the %s recipe --- defaulting to %f.", PSWARP_RECIPE, poorFrac);
+    }
+
+    float acceptFrac = psMetadataLookupF32(&status, recipe, "ACCEPT.FRAC"); // Min fraction of good pixels
+    if (!status) {
+        acceptFrac = 0.0;
+        psWarning("ACCEPT.FRAC is not set in the %s recipe --- defaulting to %f.", PSWARP_RECIPE, poorFrac);
+    }
+
+    // Set recipe values in the recipe (since we've possibly altered some)
+    psMetadataAddS32(recipe, PS_LIST_TAIL, "GRID.NX", PS_META_REPLACE,
+                     "Iso-astrom grid spacing in x", nGridX);
+    psMetadataAddS32(recipe, PS_LIST_TAIL, "GRID.NY", PS_META_REPLACE,
+                     "Iso-astrom grid spacing in y", nGridY);
+    psMetadataAddStr(recipe, PS_LIST_TAIL, "INTERPOLATION.MODE", PS_META_REPLACE,
+                     "Interpolation mode", name);
+    psMetadataAddF32(recipe, PS_LIST_TAIL, "POOR.FRAC", PS_META_REPLACE,
+                     "Fraction of bad flux for a pixel to be marked as poor", poorFrac);
+    psMetadataAddF32(recipe, PS_LIST_TAIL, "ACCEPT.FRAC", PS_META_REPLACE,
+                     "Minimum fraction of good pixels for result to be accepted", acceptFrac);
+
+    // Set recipe values in the arguments
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "GRID.NX", 0,
+                     "Iso-astrom grid spacing in x", nGridX);
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "GRID.NY", 0,
+                     "Iso-astrom grid spacing in y", nGridY);
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "INTERPOLATION.MODE", 0,
+                     "Interpolation mode", interpolationMode);
+    psMetadataAddF32(config->arguments, PS_LIST_TAIL, "POOR.FRAC", 0,
+                     "Fraction of bad flux for a pixel to be marked as poor", poorFrac);
+    psMetadataAddF32(config->arguments, PS_LIST_TAIL, "ACCEPT.FRAC", 0,
+                     "Minimum fraction of good pixels for result to be accepted", acceptFrac);
+
+    psTrace("pswarp", 1, "Done with pswarpArguments...\n");
+
+    // Dump configuration, now that's it's settled
+    psString dump_file =  psMetadataLookupStr(&status, config->arguments, "DUMP_CONFIG");
+    if (dump_file) {
+        const char *skyCamera = psMetadataLookupStr(NULL, config->arguments,
+                                                    "SKYCELL.CAMERA");  // Name of camera for skycell
+        pmConfigCamerasCull(config, skyCamera);
+        pmConfigRecipesCull(config, "PSWARP,PPSTATS,PSPHOT,MASKS");
+
+        pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PSWARP.INPUT"); // Input file
+        pmConfigDump(config, input->fpa, dump_file);
+    }
+
+    return (config);
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpCleanup.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpCleanup.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpCleanup.c	(revision 22322)
@@ -0,0 +1,18 @@
+# include "pswarp.h"
+
+void pswarpCleanup (pmConfig *config) {
+
+    psFree (config);
+
+    psTimerStop ();
+    psMemCheckCorruption(stderr, true);
+    pmModelClassCleanup ();
+    psTimeFinalize ();
+    psThreadPoolFinalize ();
+    pmConceptsDone ();
+    pmConfigDone ();
+    // fprintf (stderr, "found %d leaks at %s\n", psMemCheckLeaks (0, NULL, stdout, false), "pswarp");
+    fprintf (stderr, "found %d leaks at %s\n", psMemCheckLeaks (0, NULL, NULL, false), "pswarp");
+
+    return;
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpDefine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpDefine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpDefine.c	(revision 22322)
@@ -0,0 +1,112 @@
+# include "pswarp.h"
+// this function loads the skycell metadata
+
+bool pswarpDefine (pmConfig *config) {
+
+    // load the PSWARP recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSWARP_RECIPE);
+    if (!recipe) {
+        psError(PSWARP_ERR_CONFIG, true, "Can't find PSWARP recipe!\n");
+        return false;
+    }
+
+    // select the input data sources
+    pmFPAfile *skycell = psMetadataLookupPtr (NULL, config->files, "PSWARP.SKYCELL");
+    if (!skycell) {
+        psError(PSWARP_ERR_CONFIG, false, "Can't find skycell data!\n");
+        return false;
+    }
+    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PSWARP.OUTPUT");
+    if (!output) {
+        psError(PSWARP_ERR_CONFIG, false, "Can't find output data!\n");
+        return false;
+    }
+    pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PSWARP.INPUT");
+    if (!input) {
+        psError(PSWARP_ERR_CONFIG, false, "Can't find input data!\n");
+        return false;
+    }
+
+    // open the full skycell file; no need to defer different depths. only load the header data
+    pmFPAview *view = pmFPAviewAlloc (0);
+    pmFPAfileOpen (skycell, view, config);
+
+    // Read header and create target
+    {
+        if (!pmFPAReadHeaderSet(skycell->fpa, skycell->fits, config)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read headers for skycell.");
+            psFree(view);
+            return false;
+        }
+        view->chip = 0;
+        view->cell = 0;
+        view->readout = 0;
+        pmCell *source = pmFPAfileThisCell(config->files, view, "PSWARP.SKYCELL"); // Source cell
+        pmHDU *hdu = pmHDUFromCell(source); // HDU for source
+        if (!hdu || !hdu->header) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find header for sky cell.");
+            psFree(view);
+            return false;
+        }
+        int numCols = psMetadataLookupS32(NULL, hdu->header, "NAXIS1"); // Number of columns
+        int numRows = psMetadataLookupS32(NULL, hdu->header, "NAXIS2"); // Number of rows
+
+        pmCell *target = pmFPAviewThisCell(view, output->fpa); // Target cell
+        pmReadout *readout = pmReadoutAlloc(target); // Target readout
+        readout->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        psImageInit(readout->image, NAN);
+        psFree(readout);                // Drop reference
+
+        psMetadataItemSupplement(target->concepts, source->concepts, "CELL.XBIN");
+        psMetadataItemSupplement(target->concepts, source->concepts, "CELL.YBIN");
+        psMetadataItemSupplement(target->concepts, source->concepts, "CELL.XSIZE");
+        psMetadataItemSupplement(target->concepts, source->concepts, "CELL.YSIZE");
+        psMetadataItemSupplement(target->concepts, source->concepts, "CELL.XPARITY");
+        psMetadataItemSupplement(target->concepts, source->concepts, "CELL.YPARITY");
+        psMetadataItemSupplement(target->concepts, source->concepts, "CELL.X0");
+        psMetadataItemSupplement(target->concepts, source->concepts, "CELL.Y0");
+    }
+
+    // XXX this is not a sufficient test
+    view->chip = 0;
+    view->cell = 0;
+    view->readout = -1;
+    pmHDU *phu = pmFPAviewThisPHU(view, skycell->fpa); // Skycell PHU
+    pmHDU *hdu = pmFPAviewThisHDU(view, skycell->fpa); // Skycell header
+    bool bilevelAstrometry = false;
+    if (phu) {
+        char *ctype = psMetadataLookupStr(NULL, phu->header, "CTYPE1");
+        if (ctype) {
+            bilevelAstrometry = !strcmp(&ctype[4], "-DIS");
+        }
+    }
+
+    // We read from the skycell into the output.  i.e., the output receives the desired astrometry.
+    pmChip *outputChip = pmFPAviewThisChip(view, output->fpa); // Chip in the output
+    if (bilevelAstrometry) {
+        if (!pmAstromReadBilevelMosaic(output->fpa, phu->header)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read bilevel mosaic astrometry for skycell.");
+            psFree(view);
+            return false;
+        }
+        if (!pmAstromReadBilevelChip(outputChip, hdu->header)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read bilevel chip astrometry for skycell.");
+            psFree(view);
+            return false;
+        }
+    } else {
+        // we use a default FPA pixel scale of 1.0
+        if (!pmAstromReadWCS(output->fpa, outputChip, hdu->header, 1.0)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read WCS astrometry for skycell.");
+            psFree(view);
+            return false;
+        }
+    }
+
+    const char *name = psMetadataLookupStr(NULL, input->fpa->concepts, "FPA.OBS"); // Name of FPA
+    view->chip = view->cell = view->readout = -1;
+    pmFPAAddSourceFromView(output->fpa, name, view, output->format);
+
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpDefineSkycell.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpDefineSkycell.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpDefineSkycell.c	(revision 22322)
@@ -0,0 +1,149 @@
+# include "pswarp.h"
+
+// XXX this function is based on pmFPAfileDefineFromArgs
+// a skycell consists of only one file
+
+// search for argname on the config->argument list
+// construct an FPA based on the files in this list (must represent a single FPA)
+// built the association between the FPA elements (CHIP/CELL) and the files
+// define the pmFPAfile filename and bind it to this FPA
+// save the pmFPAfile on config->files
+// return the pmFPAfile (a view to the one saved on config->files)
+bool pswarpDefineSkycell (pmFPAfile **outFile, pmConfig **outConfig, pmConfig *config, const char *filename, const char *argname)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(argname, NULL);
+
+    bool status;
+    pmFPA *fpa = NULL;
+    psFits *fits = NULL;
+    pmFPAfile *file = NULL;
+    psMetadata *phu = NULL;
+    psMetadata *format = NULL;
+    pmConfig *skyConfig = NULL;
+
+    *outFile = NULL;
+    *outConfig = NULL;
+
+    // we search the argument data for the named fileset (argname)
+    psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname);
+    if (!status) {
+        return false;
+    }
+    if (infiles->n != 1) {
+        psError(PS_ERR_IO, false, "Found n == %ld files in %s in arguments\n", infiles->n, argname);
+        return false;
+    }
+
+    // this function is implicitly an INPUT operation: do not create the file
+    psString realName = pmConfigConvertFilename (infiles->data[0], config, false, false);
+    if (!realName) {
+        psError(PS_ERR_IO, false, "Failed to convert file name %s\n", (char *) infiles->data[0]);
+        return false;
+    }
+
+    // load the header of the image
+    fits = psFitsOpen (realName, "r");
+    if (!fits) {
+        psError(PS_ERR_IO, false, "Failed to open file %s\n", realName);
+        psFree (realName);
+        return false;
+    }
+    phu = psFitsReadHeader (NULL, fits);
+    if (!phu) {
+        psError(PS_ERR_IO, false, "Failed to read file header %s\n", realName);
+        psFree (realName);
+        return false;
+    }
+    psFitsClose(fits);
+
+    // XXX format needs to be equivalent to SIMPLE
+    // determine the current format from the header
+    // determine camera if not specified already
+    // XXX EAM : this operation should be defined as a pmConfig function (pmConfigCopy?)
+    skyConfig = pmConfigAlloc(0, NULL);
+    skyConfig->user = psMemIncrRefCounter(config->user);
+    skyConfig->system = psMemIncrRefCounter(config->system);
+
+    psFree (skyConfig->files);
+    skyConfig->files = psMemIncrRefCounter(config->files);
+    psFree (skyConfig->arguments);
+    skyConfig->arguments = psMemIncrRefCounter(config->arguments);
+
+    format = pmConfigCameraFormatFromHeader (NULL, NULL, skyConfig, phu, false);
+    if (!format) {
+        psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", realName);
+        psFree(phu);
+        psFree (realName);
+        return false;
+    }
+
+    // Record the camera name of the skycell, so we can save its configuration
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "SKYCELL.CAMERA", 0, "Name of camera for skycell",
+                     skyConfig->cameraName);
+
+    // build the template fpa, set up the basic view
+    fpa = pmFPAConstruct (skyConfig->camera, skyConfig->cameraName);
+    if (!fpa) {
+        psError(PS_ERR_IO, false, "Failed to construct FPA from %s", realName);
+        psFree (realName);
+        return false;
+    }
+    psFree (realName);
+
+    // load the given filerule (from config->camera) and bind it to the fpa
+    // the returned file is just a view to the entry on config->files
+    file = pmFPAfileDefineInput (skyConfig, fpa, filename);
+    if (!file) {
+        psError(PS_ERR_IO, false, "file %s not defined\n", filename);
+        psFree(phu);
+        psFree(fpa);
+        psFree(format);
+        return false;
+    }
+    psFree (format);
+    file->format = psMemIncrRefCounter(format);
+
+    // adjust the rules to identify these files in the file->names data
+    psFree (file->filerule);
+    file->filerule = psStringCopy ("@FILES");
+    file->filesrc = psStringCopy ("{CHIP.NAME}.{CELL.NAME}");
+
+    file->fileLevel = pmFPAPHULevel(format);
+    if (file->fileLevel == PM_FPA_LEVEL_NONE) {
+        psError(PS_ERR_IO, true, "Unable to determine file level for %s\n", file->name);
+        psFree(phu);
+        psFree(fpa);
+        psFree(format);
+        psFree(file);
+        return false;
+    }
+
+    // set the view to the corresponding entry for this phu
+    pmFPAview *view = pmFPAAddSourceFromHeader (fpa, phu, format);
+    if (!view) {
+        psError(PS_ERR_IO, false, "Unable to determine source for %s", file->name);
+        psFree(phu);
+        psFree (fpa);
+        psFree (format);
+        return false;
+    }
+
+    // associate the filename with the FPA element
+    char *name = pmFPAfileNameFromRule (file->filesrc, file, view);
+
+    // save the name association in the pmFPAfile structure
+    psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[0]);
+
+    psFree (view);
+    psFree (name);
+    psFree (phu);
+    psFree (fpa);
+
+    *outFile = file;
+    *outConfig = skyConfig;
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpErrorCodes.c.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpErrorCodes.c.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpErrorCodes.c.in	(revision 22322)
@@ -0,0 +1,25 @@
+/*
+ * The line
+    { PSWARP_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+#include "pslib.h"
+#include "pswarpErrorCodes.h"
+
+void pswarpErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { PSWARP_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PSWARP_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = PSWARP_ERR_NERROR - PSWARP_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpErrorCodes.dat
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpErrorCodes.dat	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpErrorCodes.dat	(revision 22322)
@@ -0,0 +1,10 @@
+#
+# This file is used to generate pswarpErrorClasses.h
+#
+BASE = 800		First value we use; lower values belong to psLib
+UNKNOWN			Unknown PM error code
+NOT_IMPLEMENTED		Desired feature is not yet implemented
+ARGUMENTS		Incorrect arguments
+CONFIG			Problem in configure files
+IO			Problem in FITS I/O
+DATA                    Problem in data values
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpErrorCodes.h.in
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpErrorCodes.h.in	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpErrorCodes.h.in	(revision 22322)
@@ -0,0 +1,18 @@
+#if !defined(PSWARP_ERROR_CODES_H)
+#define PSWARP_ERROR_CODES_H
+/*
+ * The line
+ *  PSWARP_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    PSWARP_ERR_BASE = 600,
+    PSWARP_ERR_${ErrorCode},
+    PSWARP_ERR_NERROR
+} pswarpErrorCode;
+
+void pswarpErrorRegister(void);
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpHeadersLoad.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpHeadersLoad.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpHeadersLoad.c	(revision 22322)
@@ -0,0 +1,117 @@
+# include "pswarp.h"
+
+// XXX this function should load all of the PSWARP.INPUT headers
+// it should examine the overlap between each chip in PSWARP.INPUT
+// and the output (pswarpMatchRange) and select/de-select the chips
+// and/or cell which contribute pixels.
+
+// pswarpDataLoad should then load the pixel of the needed chips
+
+// all of the different astrometry analysis modes use the same data load loop
+bool pswarpHeadersLoad (pmConfig *config) {
+
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+    pmFPAview *view;
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSWARP.INPUT");
+    if (!input) {
+        psError(PSWARP_ERR_CONFIG, true, "Can't find input data!\n");
+        return false;
+    }
+
+    // use the external astrometry source if supplied
+    pmFPAfile *astrom = psMetadataLookupPtr (NULL, config->files, "PSWARP.ASTROM");
+    if (!astrom) {
+        astrom = input;
+    }
+
+    // select the output readout
+    view = pmFPAviewAlloc (0);
+    view->chip = 0;
+    view->cell = 0;
+    view->readout = 0;
+    pmReadout  *output = pmFPAfileThisReadout (config->files, view, "PSWARP.OUTPUT");
+    if (!output) {
+        psError(PSWARP_ERR_CONFIG, true, "Can't find output data!\n");
+        return false;
+    }
+    psFree (view);
+
+    // de-activate PSWARP.SKYCELL and PSWARP.OUTPUT
+    pmFPAfileActivate(config->files, false, "PSWARP.SKYCELL");
+    pmFPAfileActivate(config->files, false, "PSWARP.OUTPUT");
+
+    view = pmFPAviewAlloc (0);
+
+    // XXX need to read only the headers for the skycell
+    // XXX these pmAstromReadBilevel functions seem to be broken
+
+    // find the FPA phu
+    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) {
+        if (!pmAstromReadBilevelMosaic(input->fpa, phu->header)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read bilevel mosaic astrometry for input.");
+            psFree(view);
+            return false;
+        }
+    }
+
+    // files associated with the science image
+    pmFPAfileIOChecks (config, view, PM_FPA_BEFORE);
+
+    while ((chip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+        psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) { continue; }
+        pmFPAfileIOChecks (config, view, PM_FPA_BEFORE);
+
+        // read WCS data from the corresponding header
+        pmHDU *hdu = pmFPAviewThisHDU (view, astrom->fpa);
+        if (bilevelAstrometry) {
+            if (!pmAstromReadBilevelChip (chip, hdu->header)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to read bilevel chip astrometry for input.");
+                psFree(view);
+                return false;
+            }
+        } else {
+            // we use a default FPA pixel scale of 1.0
+            if (!pmAstromReadWCS(input->fpa, chip, hdu->header, 1.0)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to read WCS astrometry for input.");
+                psFree(view);
+                return false;
+            }
+        }
+
+        while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
+            psTrace ("pswarp", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) { continue; }
+            pmFPAfileIOChecks (config, view, PM_FPA_BEFORE);
+
+            // process each of the readouts
+            while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
+                pmFPAfileIOChecks (config, view, PM_FPA_BEFORE);
+                if (! readout->data_exists) { continue; }
+
+                // XXX Replace with a function to examine the overlap and turn on/off chips
+                // based on that result
+                // pswarpTransformReadout_Opt (output, readout, config);
+
+                pmFPAfileIOChecks (config, view, PM_FPA_AFTER);
+            }
+            pmFPAfileIOChecks (config, view, PM_FPA_AFTER);
+        }
+        pmFPAfileIOChecks (config, view, PM_FPA_AFTER);
+    }
+    pmFPAfileIOChecks (config, view, PM_FPA_AFTER);
+    psFree (view);
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpLoop.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpLoop.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpLoop.c	(revision 22322)
@@ -0,0 +1,400 @@
+#include "pswarp.h"
+#include <ppStats.h>
+
+#define WCS_NONLIN_TOL 0.001            // Non-linear tolerance for header WCS
+
+// Lists of file rules for the detectors and skycells, and an independent list of everything else
+// Lists must end with a NULL
+static char *detectorFiles[] = { "PSWARP.INPUT", "PSWARP.MASK", "PSWARP.WEIGHT", NULL };
+static char *skycellFiles[] = { "PSWARP.OUTPUT", "PSWARP.OUTPUT.MASK", "PSWARP.OUTPUT.WEIGHT",
+                                "PSPHOT.OUTPUT", "PSPHOT.RESID", "PSPHOT.BACKMDL", "PSPHOT.BACKMDL.STDEV",
+                                "PSPHOT.BACKGND", "PSPHOT.BACKSUB", "PSPHOT.PSF.SAVE", "SOURCE.PLOT.MOMENTS",
+                                "SOURCE.PLOT.PSFMODEL", "SOURCE.PLOT.APRESID", NULL };
+static char *independentFiles[] = { "PSWARP.ASTROM", // Read independently from the input pixels
+                                    "PSWARP.SKYCELL", // Don't care about the skycell once we have its WCS
+                                    "PSPHOT.INPUT", // This is just a pointer to PSWARP.OUTPUT
+                                    "PSWARP.OUTPUT.SOURCES", // Save these independently so we can do the PSF
+                                    NULL };
+
+// Activate a list of files
+static void fileActivation(pmConfig *config, // Configuration
+                           char **files, // Files to turn on/off
+                           bool state   // Activation state
+    )
+{
+    for (int i = 0; files[i] != NULL; i++) {
+        pmFPAfileActivate(config->files, state, files[i]);
+    }
+    return;
+}
+
+// Run down the FPA hierarchy, checking files
+static bool ioChecksBefore(pmConfig *config // Configuration
+                           )
+{
+    pmFPAview *view = pmFPAviewAlloc(0); // View for checking
+    bool status = true;                 // Status of checks
+    status &= pmFPAfileIOChecks(config, view, PM_FPA_BEFORE);
+    view->chip = 0;
+    status &= pmFPAfileIOChecks(config, view, PM_FPA_BEFORE);
+    view->cell = 0;
+    status &= pmFPAfileIOChecks(config, view, PM_FPA_BEFORE);
+    psFree(view);
+    return status;
+}
+
+// Run up the FPA hierarchy, checking files
+static bool ioChecksAfter(pmConfig *config // Configuration
+                          )
+{
+    pmFPAview *view = pmFPAviewAlloc(0); // View for checking
+    view->chip = view->cell = 0;
+    bool status = true;                 // Status of checks
+    status &= pmFPAfileIOChecks(config, view, PM_FPA_AFTER);
+    view->cell = -1;
+    status &= pmFPAfileIOChecks(config, view, PM_FPA_AFTER);
+    view->chip = -1;
+    status &= pmFPAfileIOChecks(config, view, PM_FPA_AFTER);
+    psFree(view);
+    return status;
+}
+
+
+// Loop over the inputs, warp them to the output skycell and then write out the output.
+bool pswarpLoop(pmConfig *config)
+{
+    bool status;
+
+    // load the recipe
+    psMetadata *recipe = psMetadataLookupPtr (&status, config->recipes, PSWARP_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSWARP_RECIPE);
+        return false;
+    }
+
+    // output mask bits
+    psMaskType maskValue = psMetadataLookupU8(&status, recipe, "MASK.OUTPUT");
+    psAssert (status, "MASK.OUTPUT was not defined");
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PSWARP.INPUT");
+    if (!input) {
+        psError(PSWARP_ERR_CONFIG, true, "Can't find input data!\n");
+        return false;
+    }
+
+    // use the external astrometry source if supplied
+    pmFPAfile *astrom = psMetadataLookupPtr(NULL, config->files, "PSWARP.ASTROM");
+    if (!astrom) {
+        astrom = input;
+    }
+
+    if (astrom->camera != input->camera) {
+        psError(PS_ERR_UNKNOWN, true, "Input camera and astrometry camera do not match.");
+        return false;
+    }
+
+    // select the output readout
+    pmFPAview *view = pmFPAviewAlloc(0);
+    view->chip = 0;
+    view->cell = 0;
+    view->readout = 0;
+    pmReadout *output = pmFPAfileThisReadout(config->files, view, "PSWARP.OUTPUT");
+    if (!output) {
+        psError(PSWARP_ERR_CONFIG, true, "Can't find output data!\n");
+        return false;
+    }
+    psFree (view);
+
+    bool mdok;                          // Status of MD lookup
+    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
+    psMetadata *stats = NULL;           // Container for statistics
+    FILE *statsFile = NULL;             // File stream for statistics
+    if (mdok && statsName && strlen(statsName) > 0) {
+        psString resolved = pmConfigConvertFilename(statsName, config, true, true);
+        statsFile = fopen(resolved, "w");
+        if (!statsFile) {
+            psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
+            psFree(resolved);
+            return false;
+        }
+        psFree(resolved);
+        stats = psMetadataAlloc();
+    }
+
+    // Turn all skycell files on to generate them, and then turn them off for the loop over the input images
+    // the input, which is in a different format.
+    {
+        fileActivation(config, detectorFiles, false);
+        fileActivation(config, independentFiles, false);
+        fileActivation(config, skycellFiles, true);
+        ioChecksBefore(config);
+        fileActivation(config, skycellFiles, false);
+    }
+
+    // Read the input astrometry
+    {
+        pmFPAfileActivate(config->files, true, "PSWARP.ASTROM");
+
+        pmChip *chip;
+        pmFPAview *view = pmFPAviewAlloc(0);
+        pmFPAfileIOChecks (config, view, PM_FPA_BEFORE);
+        while ((chip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+            psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+            if (!chip->process || !chip->file_exists) { continue; }
+            pmFPAfileIOChecks(config, view, PM_FPA_BEFORE);
+            pmCell *cell;
+            while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
+                psTrace ("pswarp", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+                if (!cell->process || !cell->file_exists) { continue; }
+                pmFPAfileIOChecks (config, view, PM_FPA_BEFORE);
+                pmFPAfileIOChecks (config, view, PM_FPA_AFTER);
+            }
+            pmFPAfileIOChecks (config, view, PM_FPA_AFTER);
+        }
+        pmFPAfileIOChecks (config, view, PM_FPA_AFTER);
+        psFree(view);
+
+        fileActivation(config, detectorFiles, true);
+        pmFPAfileActivate(config->files, false, "PSWARP.ASTROM");
+    }
+
+    // Turn on the source output --- we need to get rid of these so that we can measure the PSF
+    pmFPAfileActivate(config->files, true, "PSWARP.OUTPUT.SOURCES");
+
+    // Don't care about the skycell anymore --- we've read it, and that's all we need to do.
+    pmFPAfileActivate(config->files, false, "PSWARP.SKYCELL");
+    view = pmFPAviewAlloc(0);
+
+    // find the FPA phu
+    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) {
+        if (!pmAstromReadBilevelMosaic(input->fpa, phu->header)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read bilevel mosaic astrometry for input FPA.");
+            psFree(view);
+            psFree(stats);
+            return false;
+        }
+    }
+
+    psList *cells = psListAlloc(NULL);  // List of cells, for concepts averaging
+
+    // files associated with the science image
+    pmFPAfileIOChecks (config, view, PM_FPA_BEFORE);
+
+    pmChip *chip;
+    while ((chip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+        psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) { continue; }
+        pmFPAfileIOChecks (config, view, PM_FPA_BEFORE);
+
+        // read WCS data from the corresponding header
+        pmHDU *hdu = pmFPAviewThisHDU (view, astrom->fpa);
+        if (bilevelAstrometry) {
+            if (!pmAstromReadBilevelChip (chip, hdu->header)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to read bilevel chip astrometry for input FPA.");
+                psFree(view);
+                psFree(stats);
+                return false;
+            }
+        } else {
+            // we use a default FPA pixel scale of 1.0
+            if (!pmAstromReadWCS (input->fpa, chip, hdu->header, 1.0)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to read WCS astrometry for input FPA.");
+                psFree(view);
+                psFree(stats);
+                return false;
+            }
+        }
+
+        pmCell *cell;
+        while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
+            psTrace ("pswarp", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) { continue; }
+            pmFPAfileIOChecks (config, view, PM_FPA_BEFORE);
+
+            psListAdd(cells, PS_LIST_TAIL, cell);
+
+            // process each of the readouts
+            pmReadout *readout;
+            while ((readout = pmFPAviewNextReadout(view, input->fpa, 1)) != NULL) {
+                pmFPAfileIOChecks(config, view, PM_FPA_BEFORE);
+                if (!readout->data_exists) {
+                    continue;
+                }
+
+                // Copy the sources from the astrometry carrier to the input, so they can be accessed by
+                // pswarpTransformReadout
+                pmReadout *astromRO = pmFPAviewThisReadout(view, astrom->fpa); // Readout for astrometry
+                psArray *sources = psMetadataLookupPtr(&mdok, astromRO->analysis,
+                                                       "PSPHOT.SOURCES"); // Sources from astrometry
+                if (sources) {
+                    psMetadataAddPtr(readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY,
+                                     "Sources from input astrometry", sources);
+                }
+
+                pswarpTransformReadout(output, readout, config);
+
+                pmFPAfileIOChecks(config, view, PM_FPA_AFTER);
+            }
+            pmFPAfileIOChecks(config, view, PM_FPA_AFTER);
+        }
+        pmFPAfileIOChecks(config, view, PM_FPA_AFTER);
+    }
+
+    pmCell *outCell = output->parent;   // Output cell
+    pmChip *outChip = outCell->parent;  // Output chip
+    pmFPA *outFPA = outChip->parent;    // Output FP
+
+    if (!pswarpPixelFraction(output, stats, config)) {
+        // Don't write output images, and don't bother about anything else
+        output->data_exists = outCell->data_exists = outChip->data_exists = false;
+        psFree(cells);
+        psFree(view);
+        goto COMPLETED;
+    }
+
+    // Set variance factor
+    {
+        float varFactor = psMetadataLookupF32(NULL, output->analysis, PSWARP_ANALYSIS_VARFACTOR);
+        psAssert(isfinite(varFactor), "Should be something here.");
+        long goodPix = psMetadataLookupS64(NULL, output->analysis, PSWARP_ANALYSIS_GOODPIX);
+        psAssert(goodPix > 0, "Should be something here.");
+        varFactor /= goodPix;
+
+        psMetadataItem *vfItem = psMetadataLookup(outCell->concepts, "CELL.VARFACTOR"); // Item to update
+        psAssert(vfItem && vfItem->type == PS_TYPE_F32, "Concept should be as specified.");
+        if (!isfinite(vfItem->data.F32)) {
+            vfItem->data.F32 = varFactor;
+        } else {
+            vfItem->data.F32 *= varFactor;
+        }
+    }
+
+    if (!pmConceptsAverageCells(outCell, cells, NULL, NULL, false)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to average cell concepts.");
+        psFree(stats);
+        psFree(cells);
+        psFree(view);
+        return false;
+    }
+    psFree(cells);
+
+    if (!psMetadataCopy(outFPA->concepts, input->fpa->concepts)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to copy FPA concepts from input to output.");
+        psFree(stats);
+        psFree(view);
+        return false;
+    }
+
+    pmFPAfileIOChecks(config, view, PM_FPA_AFTER);
+
+    // Done with the detector side of things
+    fileActivation(config, detectorFiles, false);
+    fileActivation(config, independentFiles, false);
+    fileActivation(config, skycellFiles, true);
+
+    // We need a new PSF model for the warped frame
+    // It would be good to generate this analytically, but that's going to be tricky.
+    // We have a list of sources, so we could use those to redetermine the PSF model.
+    // Until Gene makes the necessary adaptations to psphot, we will simply redetermine the PSF model from
+    // scratch.
+    if (psMetadataLookupBool(&mdok, config->arguments, "PSF")) {
+        psMetadata *psphotRecipe = psMetadataLookupPtr(NULL, config->recipes,
+                                                       PSPHOT_RECIPE); // Recipe for psphot
+        if (!psphotRecipe) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s recipe.", PSPHOT_RECIPE);
+            return false;
+        }
+
+        psMetadataAddStr(psphotRecipe, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE,
+                         "Break point for psphot operations", "PSFMODEL");
+        psMetadataAddBool(psphotRecipe, PS_LIST_TAIL, PSPHOT_RECIPE_PSF_FAKE_ALLOW, PS_META_REPLACE,
+                          "Don't allow psphot to fit a fake PSF!", false);
+
+        // Get rid of the transformed sources so that we can measure the PSF
+        psMetadataRemoveKey(output->analysis, "PSPHOT.SOURCES");
+
+#if 0
+        pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT");
+        pmFPACopy(photFile->fpa, outFPA);
+#endif
+
+        pmFPAview *view = pmFPAviewAlloc(0); // View into skycell
+        view->chip = view->cell = view->readout = 0;
+        if (!psphotReadout(config, view)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine PSF for warped image.");
+            return false;
+        }
+        psFree(view);
+
+        pmFPAfileActivate(config->files, true, "PSPHOT.INPUT");
+        pmFPAfileActivate(config->files, true, "PSPHOT.PSF.SAVE");
+    }
+
+    // Perform statistics on the output image
+    if (stats) {
+        if (!ppStatsFPA(stats, output->parent->parent->parent, view, maskValue, config)) {
+            psWarning("Unable to perform statistics on warped image.");
+        }
+    }
+
+    pmHDU *hdu = outFPA->hdu;           // HDU for the output warped image
+    if (!hdu->header) {
+        hdu->header = psMetadataAlloc();
+    }
+
+    if (!pmAstromWriteWCS(hdu->header, outFPA, outChip, WCS_NONLIN_TOL)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate WCS header.");
+        psFree(stats);
+        return false;
+    }
+
+    // Add MD5 information for readout
+    const char *chipName = psMetadataLookupStr(NULL, output->parent->parent->concepts, "CHIP.NAME");
+    const char *cellName = psMetadataLookupStr(NULL, output->parent->concepts, "CELL.NAME");
+    psString headerName = NULL; // Header name for MD5
+    psStringAppend(&headerName, "MD5_%s_%s_%d", chipName, cellName, view->readout);
+    psVector *md5 = psImageMD5(output->image); // md5 hash
+    psString md5string = psMD5toString(md5); // String
+    psFree(md5);
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, headerName, PS_META_REPLACE,
+                     "Image MD5", md5string);
+    psFree(md5string);
+    psFree(headerName);
+    psFree(view);
+
+    // Ensure everything is written out, at every level
+    ioChecksAfter(config);
+
+    // Now done with the skycell side of things
+
+    COMPLETED:
+    // Write out summary statistics
+    if (stats) {
+        psMetadataAddF32(stats, PS_LIST_TAIL, "DT_WARP", 0, "Time for warp completion",
+                         psTimerMark("pswarp"));
+
+        const char *statsMDC = psMetadataConfigFormat(stats);
+        if (!statsMDC) {
+            psError(PS_ERR_IO, false, "Unable to get statistics MDC file.\n");
+            psFree(stats);
+            fclose(statsFile);
+            return false;
+        }
+        fprintf(statsFile, "%s", statsMDC);
+        psFree((void*)statsMDC);
+        fclose(statsFile);
+
+        psFree(stats);
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpMapGrid.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpMapGrid.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpMapGrid.c	(revision 22322)
@@ -0,0 +1,241 @@
+# include "pswarp.h"
+
+// pswarpMapGridFromImage builds a set (a grid) of locally-linear maps which convert the source
+// coordinates (src) to destination coordinates (dest).  we construct a grid with superpixel
+// spacing of nXpix, nYpix.  The transformation for each grid cell is valid for the superpixel.
+// The grid over-fills the source image so every source image pixel is guaranteed to have a map.
+pswarpMapGrid *pswarpMapGridFromImage (pmReadout *dest, pmReadout *src, int nXpix, int nYpix) {
+
+    int i, ni;
+    int j, nj;
+
+    // start counting from the center of the superpixels
+    double xMin = 0.5*nXpix;
+    double yMin = 0.5*nYpix;
+
+    // the map is defined for coordinates in the image parent frame.
+    int Nx = src->image->numCols + src->image->col0;
+    int Ny = src->image->numRows + src->image->row0;
+
+    // allocate an extra superpixel to carry the remainder
+    int nXpts = Nx / nXpix;
+    int nYpts = Ny / nYpix;
+    if (Nx % nXpix) nXpts ++;
+    if (Ny % nYpix) nYpts ++;
+
+    // create the grid of maps
+    pswarpMapGrid *grid = pswarpMapGridAlloc (nXpts, nYpts);
+
+    // 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);
+	}
+    }
+	    
+    grid->nXpix = nXpix;
+    grid->nYpix = nYpix;
+    grid->xMin = xMin;
+    grid->yMin = yMin;
+    return grid;
+}
+
+// set the grid coordinate (gridX,gridY) for the given source image coordinate (ix,iy)
+// XXX return true if the result is on the src image, false otherwise (???)
+bool pswarpMapGridSetGrid (pswarpMapGrid *grid, int ix, int iy, int *gridX, int *gridY) {
+
+    *gridX = 0.5 + (ix - grid->xMin) / (double) grid->nXpix;
+    *gridY = 0.5 + (iy - grid->yMin) / (double) grid->nYpix;
+
+    return true;
+}
+
+// given the specified grid coordinate (gridX, gridY), return the min and max coordinates for the tile
+bool pswarpMapGridCoordRange (pswarpMapGrid *grid, int gridX, int gridY, psPlane *min, psPlane *max) {
+
+    min->x = (gridX - 0.5)*grid->nXpix + grid->xMin;
+    min->y = (gridY - 0.5)*grid->nYpix + grid->yMin;
+
+    max->x = min->x + grid->nXpix;
+    max->y = min->y + grid->nYpix;
+
+    return true;
+}
+
+// given the specified grid coordinate (gridX), return the x-coordinate for the source image
+// corresponding to the next grid cell
+int pswarpMapGridNextGrid_X (pswarpMapGrid *grid, int gridX) {
+
+    int nextX = (gridX + 0.5)*grid->nXpix + grid->xMin;
+    return nextX;
+}
+
+// given the specified grid coordinate (gridY), return the y-coordinate for the source image
+// corresponding to the next grid cell
+int pswarpMapGridNextGrid_Y (pswarpMapGrid *grid, int gridY) {
+
+    int nextY = (gridY + 0.5)*grid->nYpix + grid->yMin;
+    return nextY;
+}
+
+// measure the max error accumulated in applying one grid point to its neighbors
+// XXX double-check this
+double pswarpMapGridMaxError (pswarpMapGrid *grid) {
+
+    double xRaw, yRaw;
+    double xRef, yRef;
+    double maxError = 0;
+
+    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);
+	}
+    }	
+    return maxError;
+}
+
+// given the source coordinate (inX,inY), return the destination coordinate (outX,outY)
+bool pswarpMapApply (double *outX, double *outY, pswarpMap *map, double inX, double inY) {
+
+    *outX = map->Xo + map->Xx*inX + map->Xy*inY;
+    *outY = map->Yo + map->Yx*inX + map->Yy*inY;
+
+    return true;
+}
+
+// determine the (linear) map for the given pixel (ix,iy) from source image (src) to the destination image (dest)
+// pixel is in src coords. input and output pixel coordinates are in the parent frame of the image (Note that the
+// astrometric transformations are supplied for the parent image coordinate frame.
+bool pswarpMapSetLocalModel (pswarpMap *map, pmReadout *dest, pmReadout *src, int ix, int iy) {
+
+    pmCell *cell = NULL;
+
+    cell = src->parent;
+    pmChip *chipSrc = cell->parent;
+    pmFPA *fpaSrc = chipSrc->parent;
+
+    cell = dest->parent;
+    pmChip *chipDest = cell->parent;
+    pmFPA *fpaDest = chipDest->parent;
+
+    // XXX save these as static for speed?
+    psPlane *offset = psPlaneAlloc();
+
+    psPlane *FP = psPlaneAlloc();
+    psPlane *TP = psPlaneAlloc();
+    psSphere *sky = psSphereAlloc();
+
+    psPlane *V00 = psPlaneAlloc();
+    psPlane *V10 = psPlaneAlloc();
+    psPlane *V01 = psPlaneAlloc();
+
+    // XXX need to include readout->cell->chip offsets
+
+    // V(0,0) position
+    offset->x = ix;
+    offset->y = iy;
+    psPlaneTransformApply(FP, chipSrc->toFPA, offset);
+    psPlaneTransformApply (TP, fpaSrc->toTPA, FP);
+    psDeproject (sky, TP, fpaSrc->toSky);
+    psProject (TP, sky, fpaDest->toSky);
+    psPlaneTransformApply (FP, fpaDest->fromTPA, TP);
+    psPlaneTransformApply (V00, chipDest->fromFPA, FP);
+    
+    // V(1,0) position
+    offset->x = ix + 1;
+    offset->y = iy;
+    psPlaneTransformApply(FP, chipSrc->toFPA, offset);
+    psPlaneTransformApply (TP, fpaSrc->toTPA, FP);
+    psDeproject (sky, TP, fpaSrc->toSky);
+    psProject (TP, sky, fpaDest->toSky);
+    psPlaneTransformApply (FP, fpaDest->fromTPA, TP);
+    psPlaneTransformApply (V10, chipDest->fromFPA, FP);
+    
+    // V(0,1) position
+    offset->x = ix;
+    offset->y = iy + 1;
+    psPlaneTransformApply(FP, chipSrc->toFPA, offset);
+    psPlaneTransformApply (TP, fpaSrc->toTPA, FP);
+    psDeproject (sky, TP, fpaSrc->toSky);
+    psProject (TP, sky, fpaDest->toSky);
+    psPlaneTransformApply (FP, fpaDest->fromTPA, TP);
+    psPlaneTransformApply (V01, chipDest->fromFPA, FP);
+
+    map->Xx = V10->x - V00->x;
+    map->Xy = V01->x - V00->x;
+    map->Xo = V00->x - map->Xx*ix - map->Xy*iy;
+
+    map->Yx = V10->y - V00->y;
+    map->Yy = V01->y - V00->y;
+    map->Yo = V00->y - map->Yx*ix - map->Yy*iy;
+ 
+    map->xo = ix;
+    map->yo = iy;
+
+    psFree (offset);
+    psFree (FP);
+    psFree (TP);
+    psFree (sky);
+
+    psFree (V00);
+    psFree (V10);
+    psFree (V01);
+
+    return true;
+}
+
+static void pswarpMapFree (pswarpMap *map) {
+  return;
+}
+
+pswarpMap *pswarpMapAlloc () {
+
+  pswarpMap *map = (pswarpMap *) psAlloc (sizeof(pswarpMap));
+  psMemSetDeallocator(map, (psFreeFunc) pswarpMapFree);
+
+  return map;
+}
+
+static void pswarpMapGridFree (pswarpMapGrid *grid) {
+
+    if (grid == NULL) return;
+    if (grid->maps == NULL) return;
+
+    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]);
+    }
+    psFree (grid->maps);
+    return;
+}
+
+pswarpMapGrid *pswarpMapGridAlloc (int nXpts, int nYpts) {
+
+  pswarpMapGrid *grid = (pswarpMapGrid *) psAlloc (sizeof(pswarpMapGrid));
+  psMemSetDeallocator(grid, (psFreeFunc) pswarpMapGridFree);
+
+  grid->maps = psAlloc (nXpts*sizeof(void **));
+  for (int i = 0; i < nXpts; i++) {
+      grid->maps[i] = psAlloc (nYpts*sizeof(void *));
+      for (int j = 0; j < nYpts; j++) {
+	  grid->maps[i][j] = pswarpMapAlloc();
+      }
+  }
+  grid->nXpts = nXpts;
+  grid->nYpts = nYpts;
+
+  grid->nXpix = 0;
+  grid->nYpix = 0;
+  
+  return grid;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpMatchRange.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpMatchRange.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpMatchRange.c	(revision 22322)
@@ -0,0 +1,119 @@
+# include "pswarp.h"
+
+# define TEST_MINMAX \
+    psPlaneTransformApply(destFP, chipDest->toFPA, destPix); \
+    psPlaneTransformApply (destTP, fpaDest->toTPA, destFP); \
+    psDeproject (sky, destTP, fpaDest->toSky); \
+    psProject (srcTP, sky, fpaSrc->toSky); \
+    psPlaneTransformApply (srcFP, fpaSrc->fromTPA, srcTP); \
+    psPlaneTransformApply (srcPix, chipSrc->fromFPA, srcFP); \
+    *minX = PS_MIN (*minX, srcPix->x); \
+    *minY = PS_MIN (*minY, srcPix->y); \
+    *maxX = PS_MAX (*maxX, srcPix->x); \
+    *maxY = PS_MAX (*maxY, srcPix->y);
+
+// we are warping from src to dest.  find the max overlapping pixels in the INPUT (src)
+// coordinate frame.  NOTE: these are in the parent pixel frame since the astrometric
+// transformation refers to the parent frame
+bool pswarpMatchRange (int *minX, int *minY, int *maxX, int *maxY, pmReadout *dest, pmReadout *src) {
+
+    // transform input corners and edge centers to output coords
+    // find max overlapping region of output image
+    
+    pmCell *cell = NULL;
+
+    cell = src->parent;
+    pmChip *chipSrc = cell->parent;
+    pmFPA *fpaSrc = chipSrc->parent;
+
+    cell = dest->parent;
+    pmChip *chipDest = cell->parent;
+    pmFPA *fpaDest = chipDest->parent;
+
+    *minX = src->image->numCols + src->image->col0 - 1;
+    *minY = src->image->numRows + src->image->row0 - 1;
+    *maxX = src->image->col0;
+    *maxY = src->image->row0;
+
+    // XXX save these as static for speed?
+    psPlane *srcPix = psPlaneAlloc();
+    psPlane *srcFP  = psPlaneAlloc();
+    psPlane *srcTP  = psPlaneAlloc();
+
+    psPlane *destPix = psPlaneAlloc();
+    psPlane *destFP  = psPlaneAlloc();
+    psPlane *destTP  = psPlaneAlloc();
+
+    psSphere *sky = psSphereAlloc();
+
+    destPix->x = dest->image->col0;
+    destPix->y = dest->image->row0;
+    TEST_MINMAX;
+    
+    destPix->x = dest->image->col0 + dest->image->numCols;
+    destPix->y = dest->image->row0;
+    TEST_MINMAX;
+    
+    destPix->x = dest->image->col0 + dest->image->numCols;
+    destPix->y = dest->image->row0 + dest->image->numRows;
+    TEST_MINMAX;
+    
+    destPix->x = dest->image->col0;
+    destPix->y = dest->image->row0 + dest->image->numRows;
+    TEST_MINMAX;
+    
+    destPix->x = dest->image->col0 + 0.5*dest->image->numCols;
+    destPix->y = dest->image->row0;
+    TEST_MINMAX;
+    
+    destPix->x = dest->image->col0;
+    destPix->y = dest->image->row0 + 0.5*dest->image->numRows;
+    TEST_MINMAX;
+    
+    destPix->x = dest->image->col0 + 0.5*dest->image->numCols;
+    destPix->y = dest->image->row0;
+    TEST_MINMAX;
+
+    destPix->x = dest->image->col0;
+    destPix->y = dest->image->row0 + 0.5*dest->image->numRows;
+    TEST_MINMAX;
+
+    *minX = PS_MAX (*minX, src->image->col0);
+    *minY = PS_MAX (*minY, src->image->row0);
+    *maxX = PS_MIN (*maxX, src->image->numCols + src->image->col0);
+    *maxY = PS_MIN (*maxY, src->image->numRows + src->image->row0);
+
+    // demo forward and backward transformation
+# if (0)
+    srcPix->x = *minX;
+    srcPix->y = *minY;
+    psPlaneTransformApply(srcFP, chipSrc->toFPA, srcPix); 
+    psPlaneTransformApply (srcTP, fpaSrc->toTPA, srcFP); 
+    psDeproject (sky, srcTP, fpaSrc->toSky); 
+    psProject (destTP, sky, fpaDest->toSky); 
+    psPlaneTransformApply (destFP, fpaDest->fromTPA, destTP); 
+    psPlaneTransformApply (destPix, chipDest->fromFPA, destFP); 
+    fprintf (stderr, "%f,%f -> %f,%f ", srcPix->x, srcPix->y, destPix->x, destPix->y);
+
+    psPlaneTransformApply(destFP, chipDest->toFPA, destPix); 
+    psPlaneTransformApply (destTP, fpaDest->toTPA, destFP); 
+    psDeproject (sky, destTP, fpaDest->toSky); 
+    psProject (srcTP, sky, fpaSrc->toSky); 
+    psPlaneTransformApply (srcFP, fpaSrc->fromTPA, srcTP); 
+    psPlaneTransformApply (srcPix, chipSrc->fromFPA, srcFP); 
+    fprintf (stderr, "-> %f,%f ", srcPix->x, srcPix->y);
+# endif
+
+    psFree (srcPix);
+    psFree (srcFP);
+    psFree (srcTP);
+
+    psFree (destPix);
+    psFree (destFP);
+    psFree (destTP);
+
+    psFree (sky);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpParseCamera.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpParseCamera.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpParseCamera.c	(revision 22322)
@@ -0,0 +1,148 @@
+# include "pswarp.h"
+
+bool pswarpParseCamera(pmConfig *config)
+{
+    bool status;
+    bool mdok;                          // Status of MD lookup
+    pmFPAfile *skycell = NULL;
+    pmConfig *skyConfig = NULL;
+
+    // the input image(s) are required arguments; they define the camera
+    status = false;
+    pmFPAfile *input = pmFPAfileDefineFromArgs(&status, config, "PSWARP.INPUT", "INPUT");
+    if (!input || !status) {
+        psError(PSWARP_ERR_CONFIG, false, "Failed to build FPA from PSWARP.INPUT");
+        return false;
+    }
+
+    // the input image(s) are required arguments; they define the camera
+    status = false;
+    pmFPAfile *astrom = pmFPAfileDefineFromArgs(&status, config, "PSWARP.ASTROM", "ASTROM");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+        return NULL;
+    }
+    if (astrom) {
+        psLogMsg ("pswarp", 3, "using supplied astrometry\n");
+    } else {
+        psLogMsg ("pswarp", 3, "using header astrometry\n");
+    }
+
+    // the mask is not required - but must conform to input camera
+    pmFPAfile *inMask = pmFPAfileBindFromArgs(&status, input, config, "PSWARP.MASK", "MASK");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+        return NULL;
+    }
+    if (!inMask) {
+        psLogMsg ("pswarp", 3, "no mask supplied\n");
+    }
+
+    // loading the mask here should have invoked pmConfigMaskReadHeader()
+    if (!pswarpSetMaskBits (config)) {
+        psError(PS_ERR_IO, false, "failed to set mask bits");
+        return NULL;
+    }
+
+    pmFPAfile *inWeight = pmFPAfileBindFromArgs(&status, input, config, "PSWARP.WEIGHT", "WEIGHT");
+    if (!status) {
+        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
+        return NULL;
+    }
+    if (!inWeight) {
+        psLogMsg ("pswarp", 3, "no weight supplied\n");
+    }
+
+    // the input skycell is a required argument: it defines the output image
+    // XXX we may need a different skycell structure here
+    status = pswarpDefineSkycell(&skycell, &skyConfig, config, "PSWARP.SKYCELL", "SKYCELL");
+    if (!status) {
+        psError(PSWARP_ERR_CONFIG, false, "Failed to build FPA from PSWARP.SKYCELL");
+        return false;
+    }
+    psFree(skyConfig);
+
+    // The output skycell
+    pmFPAfile *output = pmFPAfileDefineSkycell(config, NULL, "PSWARP.OUTPUT");
+    if (!output) {
+        psError(PSWARP_ERR_CONFIG, false, "Failed to build FPA from PSWARP.OUTPUT");
+        return false;
+    }
+    pmFPAfile *outMask = pmFPAfileDefineSkycell(config, output->fpa, "PSWARP.OUTPUT.MASK");
+    if (!outMask) {
+        psError(PSWARP_ERR_CONFIG, false, "Failed to build FPA from PSWARP.OUTPUT.MASK");
+        return false;
+    }
+
+    output->save = true;
+    outMask->save = true;
+
+    if (inWeight) {
+        pmFPAfile *outWeight = pmFPAfileDefineSkycell(config, output->fpa, "PSWARP.OUTPUT.WEIGHT");
+        if (!outWeight) {
+            psError(PSWARP_ERR_CONFIG, false, "Failed to build FPA from PSWARP.OUTPUT.WEIGHT");
+            return false;
+        }
+        outWeight->save = true;
+    }
+
+    if (astrom) {
+        pmFPAfile *outSources = pmFPAfileDefineSkycell(config, output->fpa, "PSWARP.OUTPUT.SOURCES");
+        if (!outSources) {
+            psError(PSWARP_ERR_CONFIG, false, "Failed to build FPA from PSWARP.OUTPUT.SOURCES");
+            return false;
+        }
+        outSources->save = true;
+    }
+
+    if (psMetadataLookupBool(&mdok, config->arguments, "PSF")) {
+        // This file, PSPHOT.INPUT, is just used as a carrier; output files (eg, PSPHOT.RESID) are defined by
+        // psphotDefineFiles
+        pmFPAfile *psphotInput = pmFPAfileDefineSkycell(config, output->fpa, "PSPHOT.INPUT");
+        if (!psphotInput) {
+            psError(PS_ERR_IO, false, _("Unable to generate output file from PSPHOT.INPUT"));
+            return false;
+        }
+
+        // Ensure psphot defines an output file for the PSF.
+        psMetadata *psphotRecipe = psMetadataLookupPtr(NULL, config->recipes, PSPHOT_RECIPE); // Recipe
+        if (!psphotRecipe) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find %s recipe.", PSPHOT_RECIPE);
+            return false;
+        }
+        psMetadataAddBool(psphotRecipe, PS_LIST_TAIL, "SAVE.PSF", PS_META_REPLACE, "Save PSF?", true);
+
+        // Define associated psphot input/output files
+        if (!psphotDefineFiles(config, psphotInput)) {
+            psError(PSPHOT_ERR_CONFIG, false,
+                    "Unable to define the additional input/output files for psphot");
+            return false;
+        }
+
+        // Turn off writing the sources we find as part of deriving the PSF --- they might clobber the ones we
+        // get from transforming the input coordinates.
+        pmFPAfile *psphotSources = psMetadataLookupPtr(NULL, config->files, "PSPHOT.OUTPUT");
+        assert(psphotSources);
+        psphotSources->save = false;
+    }
+
+    // Chip selection: turn on only the chips specified
+    char *chipLine = psMetadataLookupStr(&mdok, config->arguments, "CHIP_SELECTIONS");
+    if (mdok) {
+        psArray *chips = psStringSplitArray (chipLine, ",", false);
+        if (chips->n > 0) {
+            pmFPASelectChip (input->fpa, -1, true); // deselect all chips
+            for (int i = 0; i < chips->n; i++) {
+                int chipNum = atoi(chips->data[i]);
+                if (! pmFPASelectChip(input->fpa, chipNum, false)) {
+                    psError(PSWARP_ERR_CONFIG, true, "Chip number %d doesn't exist in camera.\n", chipNum);
+                    return false;
+                }
+            }
+        }
+        psFree (chips);
+    }
+
+    psTrace("pswarp", 1, "Done with pswarpParseCamera...\n");
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpPixelFraction.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpPixelFraction.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpPixelFraction.c	(revision 22322)
@@ -0,0 +1,90 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "pswarp.h"
+
+bool pswarpPixelFraction(const pmReadout *readout, psMetadata *stats, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_IMAGE_NON_NULL(readout->image, false);
+    PS_ASSERT_IMAGE_TYPE(readout->image, PS_TYPE_F32, false);
+    if (!readout->mask) {
+        // Can't do anything
+        return true;
+    }
+    PS_ASSERT_IMAGE_NON_NULL(readout->mask, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(readout->mask, readout->image, false);
+    PS_ASSERT_IMAGE_TYPE(readout->mask, PS_TYPE_MASK, false);
+
+    if (stats) {
+        PS_ASSERT_METADATA_NON_NULL(stats, false);
+    }
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_METADATA_NON_NULL(config->arguments, false);
+
+    bool status; 
+
+    float minFrac = psMetadataLookupF32(NULL, config->arguments, "ACCEPT.FRAC"); // Minimum fraction
+
+    // load the recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSWARP_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSWARP_RECIPE);
+        return false;
+    }
+
+    // output mask bits
+    psMaskType maskValue = psMetadataLookupU8(&status, recipe, "MASK.OUTPUT"); 
+    psAssert (status, "MASK.OUTPUT was not defined");
+
+    psImage *image = readout->image;    // Image of interest
+    psImage *mask = readout->mask;      // Mask image
+
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+    long numPix = numCols * numRows;
+
+    // Range of valid pixels
+    int xMin = INT_MAX, xMax = -INT_MAX, yMin = INT_MAX, yMax = -INT_MAX;
+
+    long numBad = 0;                     // Number of bad pixels
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            if (mask->data.PS_TYPE_MASK_DATA[y][x] & maskValue) {
+                numBad++;
+            } else {
+                if (y > yMax) {
+                    yMax = y;
+                }
+                if (y < yMin) {
+                    yMin = y;
+                }
+                if (x > xMax) {
+                    xMax = x;
+                }
+                if (x < xMin) {
+                    xMin = x;
+                }
+            }
+        }
+    }
+
+    float goodFrac = (numPix - numBad) / (float)numPix; // Fraction of good pixels
+    bool accept = (goodFrac >= minFrac ? true : false); // Accept this readout?
+
+    if (stats) {
+        psMetadataAddBool(stats, PS_LIST_HEAD, "ACCEPT", 0, "Accept this readout?", accept);
+
+        psMetadataAddS32(stats, PS_LIST_TAIL, "RANGE.XMIN", 0, "Minimum valid x value", xMin);
+        psMetadataAddS32(stats, PS_LIST_TAIL, "RANGE.XMAX", 0, "Maximum valid x value", xMax);
+        psMetadataAddS32(stats, PS_LIST_TAIL, "RANGE.YMIN", 0, "Minimum valid y value", yMin);
+        psMetadataAddS32(stats, PS_LIST_TAIL, "RANGE.YMAX", 0, "Maximum valid y value", yMax);
+    }
+
+    return accept;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpSetMaskBits.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpSetMaskBits.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpSetMaskBits.c	(revision 22322)
@@ -0,0 +1,83 @@
+# include "pswarp.h"
+
+// This function is called by the stand-alone pswarp program to set the mask values in the
+// config file.  It sets the named mask values MASK.PSPHOT and MARK.PSPHOT in the PSPHOT
+// recipe.  Functions or programs which call psphotReadout as a library function must set these
+// named mask values in the PSPHOT recipe on their own.  This function should only be called
+// after the first header of the input mask image has been loaded and the named mask bits
+// updated in the config metadata.
+
+bool pswarpSetMaskBits (pmConfig *config) {
+
+    psMaskType maskIn = 0x00;			// mask for the input image
+    psMaskType markIn = 0x00;			// mark for the input image
+    psMaskType maskOut = 0x00;			// mask for the output image
+    psMaskType markOut = 0x00;			// mark for the output image
+
+    // this function sets the required single-image mask bits
+    if (!pmConfigMaskSetBits (&maskIn, &markIn, config)) {
+	psError (PS_ERR_UNKNOWN, true, "Unable to define the mask bit values");
+	return false;
+    }
+
+    // mask for non-linear flat regions (default to DETECTOR if not defined)
+    psMaskType badMask = pmConfigMaskGet("BAD.WARP", config); 
+    if (!badMask) {
+	badMask = 0x01;
+	pmConfigMaskSet (config, "BAD.WARP", badMask);
+    }
+    maskOut |= badMask;
+
+    // mask for non-linear flat regions (default to DETECTOR if not defined)
+    psMaskType poorMask = pmConfigMaskGet("POOR.WARP", config); 
+    if (!poorMask) {
+	poorMask = 0x02;
+	pmConfigMaskSet (config, "POOR.WARP", poorMask);
+    }
+    maskOut |= poorMask;
+
+    // search for an unset bit to use for MARK:
+    markOut = 0x80;
+
+    int nBits = sizeof(psMaskType) * 8;
+    for (int i = 0; !markOut && (i < nBits); i++) {
+	if (maskOut & markOut) {
+	    markOut >>= 1;
+	} else {
+	    markOut = markOut;
+	}
+    }
+
+    if (!markOut) {
+	psError (PS_ERR_UNKNOWN, true, "Unable to define the MARK bit mask: all bits taken!");
+	return false;
+    }
+
+    // update the pswarp recipe
+    psMetadata *warpRecipe = psMetadataLookupPtr (NULL, config->recipes, PSWARP_RECIPE);
+    if (!warpRecipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSWARP_RECIPE);
+        return false;
+    }
+
+    // set maskOut and markOut in the psphot recipe
+    // NOTE: psphot works on the output images, not input images, so set the MARK and MASK correctly here
+    psMetadataAddU8 (warpRecipe, PS_LIST_TAIL, "MASK.INPUT",  PS_META_REPLACE, "user-defined mask", maskIn);
+    psMetadataAddU8 (warpRecipe, PS_LIST_TAIL, "MARK.INPUT",  PS_META_REPLACE, "user-defined mask", markIn);
+    psMetadataAddU8 (warpRecipe, PS_LIST_TAIL, "MASK.OUTPUT", PS_META_REPLACE, "user-defined mask", maskOut);
+    psMetadataAddU8 (warpRecipe, PS_LIST_TAIL, "MARK.OUTPUT", PS_META_REPLACE, "user-defined mask", markOut);
+
+    // update the psphot recipe
+    psMetadata *psphotRecipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!psphotRecipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // set maskOut and markOut in the psphot recipe
+    // NOTE: psphot works on the output images, not input images, so set the MARK and MASK correctly here
+    psMetadataAddU8 (psphotRecipe, PS_LIST_TAIL, "MARK.PSPHOT", PS_META_REPLACE, "user-defined mask", markOut);
+    psMetadataAddU8 (psphotRecipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "user-defined mask", maskOut);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpSetThreads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpSetThreads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpSetThreads.c	(revision 22322)
@@ -0,0 +1,22 @@
+# include "pswarp.h"
+
+// each thread runs this function, starting a new job when it finished with an old one
+// it is called with a (void *) pointer to its own thread pointer
+bool pswarpThread_pswarpTransformTile(psThreadJob *job)
+{
+    pswarpTransformTileArgs *args = job->args->data[0];
+    bool status = pswarpTransformTile (args);
+    return status;
+}
+
+bool pswarpSetThreads () {
+
+    psThreadTask *task = NULL;
+
+    task = psThreadTaskAlloc("PSWARP_TRANSFORM_TILE", 1);
+    task->function = &pswarpThread_pswarpTransformTile;
+    psThreadTaskAdd(task);
+    psFree(task);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpThreads.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpThreads.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpThreads.c	(revision 22322)
@@ -0,0 +1,52 @@
+# include "pswarp.h"
+
+typedef enum {
+  PSWARP_TRANSFORM_TILE
+} pswarpJobType;
+
+typedef struct {
+  pswarpJobType type;
+  void *opts;
+} pswarpJob;
+
+static psArray *jobs = NULL;
+static pthread_t *threads = NULL;
+
+bool pswarpThreadsAddJob (pwarpJob *job) {
+
+  if (jobs == NULL) {
+    jobs = psArrayAlloc (16);
+  }
+
+  psArrayAdd (jobs, job);
+  return true;
+}
+
+bool pswarpThreadsLaunchJobs () {
+
+  while () {
+    while ((job = psListGetAndRemove (jobs, PS_LIST_HEAD)) == NULL) {
+      usleep (50000);
+    }
+
+    switch (job->type) {
+      case PSWARP_TRANSFORM_TILE:
+	status = pswarpTransformTile ((pswarpTransformTileOpts *)job->opts);
+	// send a message somewhere if the job fails
+	break;
+    }
+  }  
+}
+
+// create a pool of Nthreads
+bool pswarpThreadsCreate (int nThreads) {
+
+  threads = (pthread_t *) psAlloc (sizeof(pthread_t));
+
+  for (int i = 0; i < nThreads; i++) {
+    pthread_create (&threads[i], NULL, &function, NULL);
+  }
+
+  return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpTransformReadout.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpTransformReadout.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpTransformReadout.c	(revision 22322)
@@ -0,0 +1,178 @@
+#include "pswarp.h"
+
+// NOTE: in this function, the coordinates are transformed from the OUTPUT to the INPUT
+bool pswarpTransformReadout(pmReadout *output, pmReadout *input, pmConfig *config)
+{
+    // XXX this implementation currently ignores the use of the region
+    psImage *region = NULL;             // Region to transform
+
+    psTimerStart("warp");
+
+    // Get warp parameters
+    bool mdok;                          // Status of MD lookup
+    int nGridX = psMetadataLookupS32(NULL, config->arguments, "GRID.NX"); // Number of grid points in x
+    int nGridY = psMetadataLookupS32(NULL, config->arguments, "GRID.NY"); // Number of grid points in y
+    psImageInterpolateMode interpolationMode = psMetadataLookupS32(NULL, config->arguments,
+                                                                   "INTERPOLATION.MODE"); // Mode for interp
+
+    // load the recipe
+    psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, PSWARP_RECIPE);
+    psAssert (recipe, "missing recipe %s", PSWARP_RECIPE);
+
+    // output mask bits
+    psMaskType maskIn   = psMetadataLookupU8(&mdok, recipe, "MASK.INPUT");
+    psMaskType maskPoor = pmConfigMaskGet("POOR.WARP", config);
+    psMaskType maskBad  = pmConfigMaskGet("BAD.WARP", config);
+    psAssert(mdok, "MASK.INPUT was not defined");
+
+    int nThreads = psMetadataLookupS32(&mdok, config->arguments, "NTHREADS"); // Number of threads
+    if (!mdok) {
+        nThreads = 0;
+    }
+    float poorFrac = psMetadataLookupF32(NULL, config->arguments, "POOR.FRAC"); // Flux fraction for "poor"
+
+    // pswarpMapGridFromImage builds a set of locally-linear maps which convert the
+    // output coordinates to input coordinates
+    pswarpMapGrid *grid = pswarpMapGridFromImage(input, output, nGridX, nGridY);
+
+    // XXX optionally modify the grid based on this result and force the maxError < XXX
+    double maxError = pswarpMapGridMaxError(grid); // Maximum (positional) error from using grid
+    psLogMsg("pswarp", 3, "maximum error using this grid sampling: %lf\n", maxError);
+
+    // Get range of interest
+    int xOutMin, xOutMax, yOutMin, yOutMax; // Output pixel range
+    pswarpMatchRange(&xOutMin, &yOutMin, &xOutMax, &yOutMax, input, output);
+
+    // Check the range of the grid coordinates
+#define CHECK_GRID_RANGE() { \
+        xGridMin = PS_MIN(xGridMin, xGrid); \
+        xGridMax = PS_MAX(xGridMax, xGrid); \
+        yGridMin = PS_MIN(yGridMin, yGrid); \
+        yGridMax = PS_MAX(yGridMax, yGrid); \
+    }
+
+    int xGridMin = nGridX, xGridMax = 0, yGridMin = nGridY, yGridMax = 0; // Grid range
+    int xGrid, yGrid;                   // Grid coordinates
+    pswarpMapGridSetGrid(grid, xOutMin, yOutMin, &xGrid, &yGrid);
+    CHECK_GRID_RANGE();
+    pswarpMapGridSetGrid(grid, xOutMin, yOutMax, &xGrid, &yGrid);
+    CHECK_GRID_RANGE();
+    pswarpMapGridSetGrid(grid, xOutMax, yOutMin, &xGrid, &yGrid);
+    CHECK_GRID_RANGE();
+    pswarpMapGridSetGrid(grid, xOutMax, yOutMax, &xGrid, &yGrid);
+    CHECK_GRID_RANGE();
+
+    // Interpolation options : move these from the arguments to explicit assignments
+    psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(interpolationMode, input->image,
+                                                                       input->weight, input->mask, maskIn,
+                                                                       NAN, NAN, maskBad, maskPoor, poorFrac);
+
+    if (input->weight && !output->weight) {
+        output->weight = psImageAlloc(output->image->numCols, output->image->numRows, PS_TYPE_F32);
+        psImageInit(output->weight, NAN);
+    }
+    if ((input->mask || maskPoor || maskBad) && !output->mask) {
+        output->mask = psImageAlloc(output->image->numCols, output->image->numRows, PS_TYPE_MASK);
+        psImageInit(output->mask, maskBad);
+    }
+
+    // create jobs and supply them to the threads
+    for (int gridY = yGridMin; gridY < yGridMax; gridY++) {
+        for (int gridX = xGridMin; gridX < xGridMax; gridX++) {
+            pswarpTransformTileArgs *args = pswarpTransformTileArgsAlloc();
+            args->input = psMemIncrRefCounter(input);
+            args->output = psMemIncrRefCounter(output);
+            args->grid = psMemIncrRefCounter(grid);
+            args->interp = psMemIncrRefCounter(interp);
+            args->region = psMemIncrRefCounter(region);
+
+            args->gridX = gridX;
+            args->gridY = gridY;
+            args->goodPixels = 0;
+
+            // allocate a job
+            psThreadJob *job = psThreadJobAlloc ("PSWARP_TRANSFORM_TILE");
+            psArrayAdd(job->args, 1, args);
+            if (!psThreadJobAddPending(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to warp image.");
+                return false;
+            }
+            psFree(job);
+            psFree(args);
+        }
+    }
+
+    // wait for the threads to finish and manage results
+    // wait here for the threaded jobs to finish
+    if (!psThreadPoolWait (false)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+        return false;
+    }
+
+    // each job records its own goodPixel values; sum them here
+    // we have only supplied one type of job, so we can assume the types here
+    psThreadJob *job = NULL;
+    int xMin = output->image->numCols, xMax = 0, yMin = output->image->numRows, yMax = 0; // Bounds
+    int goodPixels = 0;                 // total number of good pixels across all tiles
+    while ((job = psThreadJobGetDone()) != NULL) {
+        if (job->args->n < 1) {
+            fprintf (stderr, "error with job\n");
+        } else {
+            pswarpTransformTileArgs *args = job->args->data[0];
+            // fprintf (stderr, "finished job %d,%d, Nargs: %ld\n", args->gridX, args->gridY, job->args->n);
+            goodPixels += args->goodPixels;
+            xMin = PS_MIN(args->xMin, xMin);
+            xMax = PS_MAX(args->xMax, xMax);
+            yMin = PS_MIN(args->yMin, yMin);
+            yMax = PS_MAX(args->yMax, yMax);
+        }
+        psFree(job);
+    }
+    psFree(interp);
+    psFree(grid);
+
+    if (xMin < xMax && yMin < yMax) {
+        psTrace("pswarp.transform", 1, "Bounds [%d:%d,%d:%d]\n", xMin, xMax, yMin, yMax);
+    } else {
+        psTrace("pswarp.transform", 1, "No overlap\n");
+    }
+
+    // Store the variance factor and number of good pixels
+    if (goodPixels > 0) {
+        // Variance factor: large factor --> small scale
+        float varFactor = psImageInterpolateVarianceFactor(input->image->numCols / 2.0 + input->image->col0,
+                                                           input->image->numRows / 2.0 + input->image->row0,
+                                                           interp);
+        psMetadataItem *vfItem = psMetadataLookup(output->analysis, PSWARP_ANALYSIS_VARFACTOR);
+        if (vfItem) {
+            psMetadataItem *goodpixItem = psMetadataLookup(output->analysis, PSWARP_ANALYSIS_GOODPIX);
+            psAssert(goodpixItem, "It should be where we left it!");
+            psAssert(vfItem->type == PS_TYPE_F32 && goodpixItem->type == PS_TYPE_S64,
+                     "Should be the type we said.");
+
+            vfItem->data.F32 += varFactor * goodPixels;
+            goodpixItem->data.S64 += goodPixels;
+        } else {
+            psMetadataAddF32(output->analysis, PS_LIST_TAIL, PSWARP_ANALYSIS_VARFACTOR, 0,
+                             "Variance factor weighted by the good pixels", varFactor * goodPixels);
+            psMetadataAddS64(output->analysis, PS_LIST_TAIL, PSWARP_ANALYSIS_GOODPIX, 0,
+                             "Number of good pixels", goodPixels);
+        }
+    }
+
+    if (goodPixels > 0) {
+        if (!pswarpTransformSources(output, input, config)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+            return false;
+        }
+
+        // Data is only written out if there are good pixels
+        output->data_exists = true;
+        output->parent->data_exists = true;
+        output->parent->parent->data_exists = true;
+    }
+
+    psLogMsg("pswarp", 3, "warping analysis: %f sec\n", psTimerMark ("warp"));
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpTransformSources.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpTransformSources.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpTransformSources.c	(revision 22322)
@@ -0,0 +1,120 @@
+# include "pswarp.h"
+
+# define SOURCE_ARRAY_BUFFER 100         // Size to grow the array of sources at a time
+
+// NOTE: in this function, the coordinates are transformed from the OUTPUT to the INPUT
+bool pswarpTransformSources(pmReadout *output, pmReadout *input, pmConfig *config)
+{
+
+    // find the output pixel range
+    int minX, minY, maxX, maxY;
+    pswarpMatchRange (&minX, &minY, &maxX, &maxY, input, output);
+
+    // Get warp parameters
+    bool mdok;
+    int nGridX = psMetadataLookupS32(NULL, config->arguments, "GRID.NX");
+    int nGridY = psMetadataLookupS32(NULL, config->arguments, "GRID.NY");
+
+    // Transform sources
+    psArray *inSources = psMetadataLookupPtr(&mdok, input->analysis, "PSPHOT.SOURCES"); // Sources in source
+    if (!inSources) return true;
+
+    pswarpMapGrid *sourceGrid = pswarpMapGridFromImage(output, input, nGridX, nGridY); // Grid for sources
+
+    psArray *outSources = psMemIncrRefCounter(psMetadataLookupPtr(&mdok, output->analysis, "PSPHOT.SOURCES")); // Target sources
+    if (!outSources) {
+        outSources = psArrayAllocEmpty(SOURCE_ARRAY_BUFFER);
+        psMetadataAddPtr(output->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY,
+                         "Warped sources", outSources);
+    }
+
+    // XXX it is probably not necessary to use the locally linear transformations we should
+    // be using the full astrometry: since there are 100 - 1000 fewer sources than pixels,
+    // this does not cost us so much time.
+    for (int i = 0; i < inSources->n; i++) {
+        pmSource *source = inSources->data[i]; // Source of interest
+        pmModel *model = source->modelPSF; // Model for this source
+        float xIn, yIn;             // Coordinates of source
+        xIn = model->params->data.F32[PM_PAR_XPOS] - input->image->col0;
+        yIn = model->params->data.F32[PM_PAR_YPOS] - input->image->row0;
+
+        int xGrid, yGrid;           // Grid coordinates for local map
+        if (!pswarpMapGridSetGrid(sourceGrid, xIn + 0.5, yIn + 0.5, &xGrid, &yGrid)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to get grid coordinates for source at %f,%f\n",
+                    xIn, yIn);
+            psFree(outSources);
+            psFree(sourceGrid);
+            return false;
+        }
+        if (xGrid < 0 || xGrid >= sourceGrid->nXpts || yGrid < 0 || yGrid >= sourceGrid->nYpts) {
+            // It's not even on the grid
+            // XXX how can this happen?
+            continue;
+        }
+
+        pswarpMap *map = sourceGrid->maps[xGrid][yGrid]; // Locally linear transformation
+        double xOut, yOut;          // Output coordinates
+        if (!pswarpMapApply(&xOut, &yOut, map, xIn + 0.5, yIn + 0.5)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to transform coordinates for source at %f,%f\n",
+                    xIn, yIn);
+            psFree(outSources);
+            psFree(sourceGrid);
+            return false;
+        }
+        xOut += output->image->col0 - 0.5;
+        yOut += output->image->row0 - 0.5;
+        if (xOut < minX || xOut > maxX || yOut < minY || yOut > maxY) {
+            // It's not in the output image
+            continue;
+        }
+
+        // Generate the new source in the output frame
+        //
+        // Magnitudes will be off if there's any change in scale, but for our purposes (mainly x,y and
+        // relative flux) that's OK.
+        pmSource *new = pmSourceAlloc(); // New source
+        new->peak = pmPeakAlloc(xOut, yOut, source->peak->flux, PM_PEAK_LONE);
+        new->peak->flux = source->peak->flux;
+        new->type = source->type;
+        new->mode = source->mode;
+        new->psfMag = source->psfMag;
+        new->extMag = source->extMag;
+        new->errMag = source->errMag;
+        new->apMag = source->apMag;
+        new->pixWeight = source->pixWeight;
+        new->psfChisq = source->psfChisq;
+        new->crNsigma = source->crNsigma;
+        new->extNsigma = source->extNsigma;
+        new->sky = source->sky;
+        new->skyErr = source->skyErr;
+
+        new->modelPSF = pmModelAlloc(source->modelPSF->type);
+
+#if 0
+        // XXX Note that this will not set the correct axes
+        pmPSF_AxesToModel(new->modelPSF->params->data.F32,
+                          pmPSF_ModelToAxes(source->modelPSF->params->data.F32, 20.0));
+#endif
+
+        // Propagate the position erorrs
+        float dxIn, dyIn;           // Errors in input coordinates
+        dxIn = model->dparams->data.F32[PM_PAR_XPOS];
+        dyIn = model->dparams->data.F32[PM_PAR_YPOS];
+
+        float dxOut, dyOut;         // Errors in output coordinates
+        dxOut = sqrt(PS_SQR(map->Xx * dxIn) + PS_SQR(map->Xy * dyIn));
+        dyOut = sqrt(PS_SQR(map->Yx * dxIn) + PS_SQR(map->Yy * dyIn));
+
+        new->modelPSF->params->data.F32[PM_PAR_XPOS] = xOut;
+        new->modelPSF->params->data.F32[PM_PAR_YPOS] = yOut;
+        new->modelPSF->dparams->data.F32[PM_PAR_XPOS] = dxOut;
+        new->modelPSF->dparams->data.F32[PM_PAR_YPOS] = dyOut;
+
+        psArrayAdd(outSources, SOURCE_ARRAY_BUFFER, new);
+        psFree(new);                // Drop reference
+    }
+    psFree(sourceGrid);
+    psFree(outSources);             // Drop reference
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpTransformTile.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpTransformTile.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpTransformTile.c	(revision 22322)
@@ -0,0 +1,113 @@
+#include "pswarp.h"
+
+static void transformTileArgsFree(pswarpTransformTileArgs *args)
+{
+    psFree(args->input);
+    psFree(args->output);
+    psFree(args->grid);
+    psFree(args->interp);
+    psFree(args->region);
+    return;
+}
+
+pswarpTransformTileArgs *pswarpTransformTileArgsAlloc()
+{
+    pswarpTransformTileArgs *args = psAlloc(sizeof(pswarpTransformTileArgs));
+    psMemSetDeallocator(args, (psFreeFunc)transformTileArgsFree);
+
+    args->input = NULL;
+    args->output = NULL;
+    args->grid = NULL;
+    args->interp = NULL;
+    args->region = NULL;
+
+    args->gridX = 0;
+    args->gridY = 0;
+
+    args->goodPixels = 0;
+    args->xMin = PS_MAX_S32;
+    args->xMax = PS_MIN_S32;
+    args->yMin = PS_MAX_S32;
+    args->yMax = PS_MIN_S32;
+
+    return args;
+}
+
+bool pswarpTransformTile(pswarpTransformTileArgs *args)
+{
+    psImage *inImage = args->input->image; // Input image
+    psImage *outImage = args->output->image; // Output image
+
+    int inNumCols = inImage->numCols, inNumRows = inImage->numRows; // Size of input image
+    int outNumCols = outImage->numCols, outNumRows = outImage->numRows; // Size of output image
+    int outCol0 = outImage->col0, outRow0 = outImage->row0; // Offset of output image
+
+    psPlane minPt, maxPt;               // Minimum and maximum points for this tile
+    pswarpMapGridCoordRange(args->grid, args->gridX, args->gridY, &minPt, &maxPt);
+
+    // Dereference images for convenience
+    psF32 **outImageData     = args->output->image->data.F32;
+    psF32 **outVarData       = (args->output->weight) ? args->output->weight->data.F32 : NULL;
+    psMaskType **outMaskData = (args->output->mask)   ? args->output->mask->data.PS_TYPE_MASK_DATA : NULL;
+    psMaskType **inMaskData  = (args->input->mask)    ? args->input->mask->data.PS_TYPE_MASK_DATA : NULL;
+
+    pswarpMap *map = args->grid->maps[args->gridX][args->gridY]; // Map for this tile
+    psImage *region = args->region;     // Region to transform
+
+    // Bounds for iteration
+    int xMin = PS_MAX(minPt.x, 0);
+    int xMax = PS_MIN(maxPt.x, outNumCols);
+    int yMin = PS_MAX(minPt.y, 0);
+    int yMax = PS_MIN(maxPt.y, outNumRows);
+
+    // Iterate over the output image pixels (parent frame)
+    long goodPixels = 0;                // Number of input pixels landing on the output image
+    for (int y = yMin; y < yMax; y++) {
+        for (int x = xMin; x < xMax; x++) {
+
+            // Only transform those pixels requested
+            if (region && region->data.U8[y][x]) {
+                continue;
+            }
+
+            // pswarpMapApply converts the output coordinate (x,y) to the input coordinate.
+            // both are in the parent frames of the input and output images.
+            double xInRaw, yInRaw;      // Input raw pixel coordinates
+            pswarpMapApply(&xInRaw, &yInRaw, map, x + 0.5, y + 0.5);
+            double xIn = xInRaw - outCol0, yIn = yInRaw - outRow0; // Position on input image
+            if (xIn < 0 || xIn >= inNumCols || yIn < 0 || yIn >= inNumRows) {
+                continue;
+            }
+
+            // psImagePixelInterpolate determines the value at pixel coordinate (x,y) in child coordinates
+            double imageValue, varValue; // Value of image and variance map
+            psMaskType maskValue = inMaskData ? inMaskData[(int)yIn][(int)xIn] : 0; // Value of mask
+            if (!psImageInterpolate(&imageValue, &varValue, &maskValue, xIn, yIn, args->interp)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+                return false;
+            }
+
+            int xOut = x - outCol0, yOut = y - outRow0; // Position on output image
+
+            if (outImageData) {
+                outImageData[yOut][xOut] = imageValue;
+            }
+            if (outVarData) {
+                outVarData[yOut][xOut] = varValue;
+            }
+            if (outMaskData) {
+                outMaskData[yOut][xOut] = maskValue;
+            }
+
+            goodPixels++;
+        }
+    }
+
+    args->goodPixels = goodPixels;
+    args->xMin = xMin;
+    args->xMax = xMax;
+    args->yMin = yMin;
+    args->yMax = yMax;
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpVersion.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpVersion.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/src/pswarpVersion.c	(revision 22322)
@@ -0,0 +1,27 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include "pswarp.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString pswarpVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString pswarpVersionLong(void)
+{
+    psString version = pswarpVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
Index: /tags/sj_tags/sj_root_20080929/pswarp/test/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/test/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/test/.cvsignore	(revision 22322)
@@ -0,0 +1,1 @@
+*
Index: /tags/sj_tags/sj_root_20080929/pswarp/test/simple.image.op
===================================================================
--- /tags/sj_tags/sj_root_20080929/pswarp/test/simple.image.op	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/pswarp/test/simple.image.op	(revision 22322)
@@ -0,0 +1,56 @@
+
+$NX = 2048
+$NY = 2048
+
+macro go
+  mkref
+  mksrc
+  exec pswarp -file src.fits out ref.fits
+  exec rm -f src.fits out.fits ref.fits
+end
+
+macro mkref
+  mcreate ref $NX $NY
+  keyword ref CTYPE1 -w "RA---TAN"
+  keyword ref CTYPE2 -w "DEC--TAN"
+  keyword ref CRVAL1 -wf 0.0
+  keyword ref CRVAL2 -wf 0.0
+  keyword ref CRPIX1 -wf {$NX/2}
+  keyword ref CRPIX2 -wf {$NY/2}
+
+  keyword ref CDELT1 -wf {1/3600}
+  keyword ref CDELT2 -wf {1/3600}
+
+  keyword ref PC001001 -wf 1.0
+  keyword ref PC001002 -wf 0.0
+  keyword ref PC002001 -wf 0.0
+  keyword ref PC002002 -wf 1.0
+
+  wd ref ref.fits -bitpix -32 -bzero 0 -bscale 1
+end
+
+macro mksrc
+  mcreate src $NX $NY
+  for i 0 $NX 64
+    for j 0 $NY 64
+      zap src $i $j 1 1 -v 1
+    end
+  end
+
+  keyword src CTYPE1 -w "RA---TAN"
+  keyword src CTYPE2 -w "DEC--TAN"
+  keyword src CRVAL1 -wf 0.0
+  keyword src CRVAL2 -wf 0.0
+  keyword src CRPIX1 -wf {$NX/2}
+  keyword src CRPIX2 -wf {$NY/2}
+
+  keyword src CDELT1 -wf {1/3600}
+  keyword src CDELT2 -wf {1/3600}
+
+  keyword src PC001001 -wf {dcos(20)}
+  keyword src PC001002 -wf {-1*dsin(20)}
+  keyword src PC002001 -wf {dsin(20)}
+  keyword src PC002002 -wf {dcos(20)}
+
+  wd src src.fits -bitpix -32 -bzero 0 -bscale 1
+end
Index: /tags/sj_tags/sj_root_20080929/simtest/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/simtest/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/simtest/.cvsignore	(revision 22322)
@@ -0,0 +1,9 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.log
+config.status
+configure
+install-sh
+missing
Index: /tags/sj_tags/sj_root_20080929/simtest/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/simtest/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/simtest/Makefile.am	(revision 22322)
@@ -0,0 +1,15 @@
+
+install_files = \
+	sim.pro \
+	images.pro
+
+installdir = $(datadir)/pantasks/modules
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir) 
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /tags/sj_tags/sj_root_20080929/simtest/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/simtest/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/simtest/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=simtest
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+#($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+#        DIE=1
+#}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+#($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+#        DIE=1
+#}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+#$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+#$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/simtest/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/simtest/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/simtest/configure.ac	(revision 22322)
@@ -0,0 +1,17 @@
+AC_PREREQ(2.59)
+
+AC_INIT([simtest], [0.9.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([sim.pro])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_MAINTAINER_MODE
+
+AC_PROG_INSTALL
+
+MODULE_DIR="${datadir}/pantasks/modules"
+AC_SUBST(MODULE_DIR)
+
+AC_CONFIG_FILES([
+  Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/simtest/images.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/simtest/images.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/simtest/images.pro	(revision 22322)
@@ -0,0 +1,210 @@
+
+# set global parameters for the simulations
+macro mkimages.init
+ if ($0 != 7)
+  echo "USAGE: mkimage.empty (Nx) (Ny) (RDNOISE) (SKY) (Nstars) (psfSigma)"
+  break
+ end
+
+ $mkiNx = $1
+ $mkiNy = $2
+ $mkiRD = $3
+ $mkiSKY = $4
+ $mkiPSFsigma = $6
+
+ gaussdev mki_noisev {$mkiNx*$mkiNy} 0.0 1.0
+ dimenup mki_noisev mki_noisei $mkiNx $mkiNy
+ mcreate blank $mkiNx $mkiNy
+end
+
+# generate a fake image with bias and sky, no stars
+macro mkimage.empty
+ if ($0 != 2)
+  echo "USAGE: mkimage.empty (buffer)"
+  break
+ end
+
+ set $1 = blank + $mkiRD*mki_noisei + sqrt($mkiSKY)*mki_noisei + $mkiSKY
+end
+
+# set global parameters for the simulations
+macro mkimages.stars
+ if ($0 != 7)
+  echo "USAGE: mkimage.empty (Nx) (Ny) (RDNOISE) (SKY) (Nstars) (psfSigma)"
+  break
+ end
+
+ $mkiNx = $1
+ $mkiNy = $2
+ $mkiRD = $3
+ $mkiSKY = $4
+ $mkiPSFsigma = $6
+
+ gaussdev mki_noisev {$mkiNx*$mkiNy} 0.0 1.0
+ dimenup mki_noisev mki_noisei $mkiNx $mkiNy
+ mcreate blank $mkiNx $mkiNy
+
+ # mkstars $5 0.6
+ mkstars $5 0.4
+ # mkstars $5 0.3
+end
+
+# generate a fake image with bias, sky, stars, gaussian PFS
+macro mkimage.stars.gauss
+ if ($0 != 2)
+  echo "USAGE: mkimage.stars (buffer)"
+  break
+ end
+
+ set _tmp.stars = mki_stars_image
+
+ mkimage.smooth _tmp.stars _tmp.smooth $mkiPSFsigma mkgauss
+
+ # noise sigma = sqrt (_tmp.smooth + $mkiRD^2 + $mkiSKY)
+ set $1 = _tmp.smooth + $mkiSKY + sqrt(_tmp.smooth + $mkiSKY + $mkiRD^2)*mki_noisei
+end
+
+# generate a fake image with bias, sky, stars, gaussian PFS
+macro mkimage.stars.power
+ if ($0 != 2)
+  echo "USAGE: mkimage.stars (buffer)"
+  break
+ end
+
+ set _tmp.stars = mki_stars_image
+
+ mkimage.smooth _tmp.stars _tmp.smooth $mkiPSFsigma mkpower
+
+ # noise sigma = sqrt (_tmp.smooth + $mkiRD^2 + $mkiSKY)
+ set $1 = _tmp.smooth + $mkiSKY + sqrt(_tmp.smooth + $mkiSKY + $mkiRD^2)*mki_noisei
+end
+
+# generate a fake image with bias, sky, stars, gaussian PFS
+macro mkimage.stars.wing
+ if ($0 != 2)
+  echo "USAGE: mkimage.stars (buffer)"
+  break
+ end
+
+ set _tmp.stars = mki_stars_image
+
+ mkimage.smooth _tmp.stars _tmp.smooth $mkiPSFsigma mkwing
+
+ # noise sigma = sqrt (_tmp.smooth + $mkiRD^2 + $mkiSKY)
+ set $1 = _tmp.smooth + $mkiSKY + sqrt(_tmp.smooth + $mkiSKY + $mkiRD^2)*mki_noisei
+end
+
+# generate the vectors of star coordinates, fluxes and magnitudes
+macro mkstars
+ if ($0 != 3)
+   echo "USAGE: mkstars (Nstars) (alpha)"
+   break
+ end
+
+ local sigma alpha Nstars
+ $Nstars = $1
+ $alpha = $2
+ # alpha is dlogN/dmag = 0.6 for N ~ S^-3/2
+
+ # $sigma is flux of star with S/N = 1 (sigma_sky * 4pi sigma_psf^2)
+ $sigma = sqrt($mkiRD^2 + $mkiSKY) * 4*3.1416*$mkiPSFsigma^2
+ 
+ # faintest magnitudes:
+ # include stars down to 1 sigma
+ $M1 = -2.5*log($sigma)
+
+ # brightest magnitude in sim
+ $M0 = $M1 - log($alpha*$Nstars/0.43)/$alpha
+
+ create _tmp 0 $Nstars
+ set mki_stars_x = $mkiNx * rnd(_tmp)
+ set mki_stars_y = $mkiNy * rnd(_tmp)
+
+ # random variable between 0 and Nstars
+ set _tmpvar = $Nstars * rnd(_tmp)
+
+ # random mags drawn from random variable
+ set mki_stars_mag = $M0 + log(_tmpvar*$alpha/0.43)/$alpha
+
+ set mki_stars_flux = ten(-0.4*mki_stars_mag)
+ sort mki_stars_mag mki_stars_flux mki_stars_x mki_stars_y
+
+ # insert the stars
+ mcreate mki_stars_image $mkiNx $mkiNy
+ for i 0 $Nstars
+   zap mki_stars_image mki_stars_x[$i] mki_stars_y[$i] 1 1 -v mki_stars_flux[$i]
+ end
+end
+
+####
+macro mkimage.smooth
+ if ($0 != 5)
+   echo "USAGE: smooth (in) (out) (sigma) (func)"
+   break
+ end
+ fft2d $1 0 to sm_I_r sm_I_i
+ set sm_g = zero($1)
+ keyword sm_g NAXIS1 SMnx
+ keyword sm_g NAXIS2 SMny
+ $4 sm_g $3 -c 0 0
+ $4 sm_g $3 -c $SMnx 0
+ $4 sm_g $3 -c 0 $SMny
+ $4 sm_g $3 -c $SMnx $SMny
+ stats -q sm_g - - - -
+ set sm_g = sm_g/$TOTAL
+ fft2d sm_g 0 to sm_G_r sm_G_i
+ set sm_r = sm_G_r*sm_I_r - sm_G_i*sm_I_i
+ set sm_i = sm_G_r*sm_I_i + sm_G_i*sm_I_r
+ fft2d -inverse sm_r sm_i to sm_I_r sm_I_i
+
+ set $2 = sm_I_r*$SMnx*$SMny*$SMnx*$SMny
+
+ if (1)
+  delete sm_I_r 
+  delete sm_I_i
+  delete sm_G_r 
+  delete sm_G_i
+  delete sm_r
+  delete sm_i
+  delete sm_g
+ end
+end
+
+macro mkpower
+ if ($0 != 6)
+  echo "USAGE: mkpower (buffer) (sigma) -c X Y"
+  break
+ end
+
+ delete -q tmp
+
+ keyword $1 NAXIS1 tmpnx
+ keyword $1 NAXIS2 tmpny
+ mcreate tmp $tmpnx $tmpny
+ set tmpx = xramp(tmp) - $4
+ set tmpy = yramp(tmp) - $5
+
+# $sigma_y = $2 * $AxisRatio
+# $sigma_xy = dsin(2*$Theta)*(
+
+ set tmpz = tmpx^2 / (2 * $2^2) + tmpy^2 / (2 * $2^2)
+ set $1 = $1 + 1 / (1 + tmpz + tmpz^2.5)
+end
+
+macro mkwing
+ if ($0 != 6)
+  echo "USAGE: mkpower (buffer) (sigma) -c X Y"
+  break
+ end
+
+ delete -q tmp
+
+ keyword $1 NAXIS1 tmpnx
+ keyword $1 NAXIS2 tmpny
+ mcreate tmp $tmpnx $tmpny
+ set tmpx = xramp(tmp) - $4
+ set tmpy = yramp(tmp) - $5
+
+ set tmpz = tmpx^2 / (2 * $2^2) + tmpy^2 / (2 * $2^2)
+ set $1 = $1 + 1 / (1 + tmpz + tmpz^2)
+end
Index: /tags/sj_tags/sj_root_20080929/simtest/sim.pro
===================================================================
--- /tags/sj_tags/sj_root_20080929/simtest/sim.pro	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/simtest/sim.pro	(revision 22322)
@@ -0,0 +1,262 @@
+### Generate simulated test data for testing the Image Processing Pipeline
+
+module images.pro
+
+$flatlevel = 1000
+$biaslevel = 500
+$skyrate  = 200
+$darkrate = 100
+$shutter = 0.5
+
+macro cleanup
+  exec rm -f *.fit
+  exec rm -f *.jpg
+  exec rm -f *.fits
+  exec rm -rf logs
+end
+
+$simdir = `ipp_datapath.pl path://SIMTEST`
+
+macro init
+  exec pxadmin -delete -dbname simtest
+  exec pxadmin -create -dbname simtest
+  exec mkdir -p $simdir/raw
+  exec mkdir -p $simdir/proc
+  mkbiases
+  mkdarks
+  mkshutter
+  mkflats
+  mkobjects
+  exec ipp_serial_inject.pl $simdir/raw/*.fits --workdir path://SIMTEST/proc --dbname simtest
+end
+
+### create a bias detrun:
+# exec dettool -pretend -definebyquery -inst SIMTEST -filter NONE -det_type bias -select_exp_type bias -workdir path://SIMTEST/mkbias.0 -dbname simtest
+
+### If something goes wrong:
+# exec dettool -updatedetrun -det_id 1 -state stop -dbname simtest
+
+### create a dark detrun:
+# exec dettool -pretend -definebyquery -inst SIMTEST -filter NONE -det_type dark -select_exp_type dark -workdir path://SIMTEST/mkdark.0 -dbname simtest
+
+### create a shutter detrun:
+# exec dettool -pretend -definebyquery -inst SIMTEST -filter NONE -det_type shutter -select_exp_type shutter -workdir path://SIMTEST/mkshutter.0 -dbname simtest
+
+### create a flat detrun:
+# exec dettool -pretend -definebyquery -inst SIMTEST -filter B -det_type flat -select_exp_type flat -select_filter B -workdir path://SIMTEST/mkflat.0 -dbname simtest 
+# exec dettool -pretend -definebyquery -inst SIMTEST -filter V -det_type flat -select_exp_type flat -select_filter V -workdir path://SIMTEST/mkflat.1 -dbname simtest 
+
+# generate a set of fake biases
+macro mkbiases
+  echo "generating 10 bias images"
+  for i 0 10
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set bias = blank + $mkiRD*mki_noisei + $biaslevel
+    keyword bias AIRMASS  -wf {1 + 0.1*$i}
+    keyword bias FILTER   -w NONE
+    keyword bias POSANGLE -wf 0.0
+    keyword bias RA       -wf 0.0
+    keyword bias DEC      -wf 0.0
+    keyword bias EXPTIME  -wf 0.0
+    keyword bias DARKTIME -wf 0.0
+    keyword bias DATE-OBS -w "2006/10/01"
+    sprintf time "00:00:%02d" $i
+    keyword bias UTC-OBS  -w $time
+    keyword bias CCDSUM   -w "1 1"
+    keyword bias TELESCOP -w simscope
+    keyword bias INSTRUME -w SIMTEST
+    keyword bias OBSTYPE  -w BIAS
+    keyword bias OBJECT   -w BIAS
+    sprintf name "$simdir/raw/bias.%02d.fits" $i
+    keyword bias GAIN     -wf 1.0
+    keyword bias RDNOISE  -wf $mkiRD
+    wd bias $name
+  end
+end
+
+macro mkdarks
+  echo "generating 20 dark images"
+  for i 0 20
+    $exptime = 3 * ($i + 1)
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set dark = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime)*mki_noisei + $darkrate*$exptime + $biaslevel
+    keyword dark AIRMASS  -wf {1 + 0.1*$i}
+    keyword dark FILTER   -w  NONE
+    keyword dark POSANGLE -wf 0.0
+    keyword dark RA       -wf 0.0
+    keyword dark DEC      -wf 0.0
+    keyword dark EXPTIME  -wf $exptime
+    keyword dark DARKTIME -wf $exptime
+    keyword dark DATE-OBS -w "2006/10/01"
+    sprintf time "00:01:%02d" $i
+    keyword dark UTC-OBS  -w $time
+    keyword dark CCDSUM   -w "1 1"
+    keyword dark TELESCOP -w simscope
+    keyword dark INSTRUME -w SIMTEST
+    keyword dark OBSTYPE  -w DARK
+    keyword dark OBJECT   -w DARK
+    keyword dark GAIN     -wf 1.0
+    keyword dark RDNOISE  -wf $mkiRD
+    sprintf name "$simdir/raw/dark.%02d.fits" $i
+    wd dark $name
+  end  
+end
+
+macro mkshutter
+  echo "generating 10 shutter images"
+  for i 0 10
+    $exptime = 3 * ($i + 1)
+    $opentime = $exptime - $shutter
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set shutter = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime + $flatlevel*$opentime)*mki_noisei + $flatlevel*$opentime + $darkrate*$exptime + $biaslevel
+    keyword shutter AIRMASS  -wf 1.0
+    keyword shutter FILTER   -w  NONE
+    keyword shutter POSANGLE -wf 0.0
+    keyword shutter RA       -wf 0.0
+    keyword shutter DEC      -wf 0.0
+    keyword shutter EXPTIME  -wf $exptime
+    keyword shutter DARKTIME -wf $exptime
+    keyword shutter DATE-OBS -w "2006/10/01"
+    sprintf time "00:01:%02d" $i
+    keyword shutter UTC-OBS  -w $time
+    keyword shutter CCDSUM   -w "1 1"
+    keyword shutter TELESCOP -w simscope
+    keyword shutter INSTRUME -w SIMTEST
+    keyword shutter OBSTYPE  -w SHUTTER
+    keyword shutter OBJECT   -w SHUTTER
+    keyword shutter GAIN     -wf 1.0
+    keyword shutter RDNOISE  -wf $mkiRD
+    sprintf name "$simdir/raw/shutter.%02d.fits" $i
+    wd shutter $name
+  end
+end
+
+macro mkflats
+  echo "generating 20 flat images in 2 filters"
+  $exptime = 10.0
+  $opentime = $exptime - $shutter
+  for i 0 10
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set flat = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime + $flatlevel*$opentime)*mki_noisei + $darkrate*$exptime + $flatlevel*$opentime + $biaslevel
+    keyword flat AIRMASS  -wf {1 + 0.1*$i}
+    keyword flat FILTER   -w  V
+    keyword flat POSANGLE -wf 0.0
+    keyword flat RA       -wf 0.0
+    keyword flat DEC      -wf 0.0
+    keyword flat EXPTIME  -wf $exptime
+    keyword flat DARKTIME -wf $exptime
+    keyword flat DATE-OBS -w "2006/10/01"
+    sprintf time "00:01:%02d" $i
+    keyword flat UTC-OBS  -w $time
+    keyword flat CCDSUM   -w "1 1"
+    keyword flat TELESCOP -w simscope
+    keyword flat INSTRUME -w SIMTEST
+    keyword flat OBSTYPE  -w FLAT
+    keyword flat OBJECT   -w FLAT
+    keyword flat GAIN     -wf 1.0
+    keyword flat RDNOISE  -wf $mkiRD
+    sprintf name "$simdir/raw/flat.%02d.fits" $i
+    wd flat $name
+  end  
+
+  for i 10 20
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set flat = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime + $flatlevel*$opentime)*mki_noisei + $darkrate*$exptime + $flatlevel*$opentime + $biaslevel
+    keyword flat AIRMASS  -wf {1 + 0.1*$i}
+    keyword flat FILTER   -w  B
+    keyword flat POSANGLE -wf 0.0
+    keyword flat RA       -wf 0.0
+    keyword flat DEC      -wf 0.0
+    keyword flat EXPTIME  -wf $exptime
+    keyword flat DARKTIME -wf $exptime
+    keyword flat DATE-OBS -w "2006/10/01"
+    sprintf time "00:02:%02d" $i
+    keyword flat UTC-OBS  -w $time
+    keyword flat CCDSUM   -w "1 1"
+    keyword flat TELESCOP -w simscope
+    keyword flat INSTRUME -w SIMTEST
+    keyword flat OBSTYPE  -w FLAT
+    keyword flat OBJECT   -w FLAT
+    keyword flat GAIN     -wf 1.0
+    keyword flat RDNOISE  -wf $mkiRD
+    sprintf name "$simdir/raw/flat.%02d.fits" $i
+    wd flat $name
+  end  
+end
+
+macro mkobjects
+  echo "generating 10 object images in 2 filters"
+  for i 0 5
+    $exptime = 3 * ($i + 1)
+    $opentime = $exptime - $shutter
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set obj = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime + $skyrate*$opentime)*mki_noisei + $darkrate*$exptime + $skyrate*$opentime + $biaslevel
+    keyword obj AIRMASS  -wf {1 + 0.1*$i}
+    keyword obj FILTER   -w  V
+    keyword obj POSANGLE -wf 0.0
+    keyword obj RA       -wf 0.0
+    keyword obj DEC      -wf 0.0
+    keyword obj EXPTIME  -wf $exptime
+    keyword obj DARKTIME -wf $exptime
+    keyword obj DATE-OBS -w "2006/10/01"
+    sprintf time "00:01:%02d" $i
+    keyword obj UTC-OBS  -w $time
+    keyword obj CCDSUM   -w "1 1"
+    keyword obj TELESCOP -w simscope
+    keyword obj INSTRUME -w SIMTEST
+    keyword obj OBSTYPE  -w OBJECT
+    keyword obj OBJECT   -w OBJECT
+    keyword obj GAIN     -wf 1.0
+    keyword obj RDNOISE  -wf $mkiRD
+    sprintf name "$simdir/raw/obj.%02d.fits" $i
+    wd obj $name
+  end  
+
+  for i 5 10
+    $exptime = 3 * ($i - 4)
+    $opentime = $exptime - $shutter
+    mkimages.init 1024 1024 5 1000 0 2.0
+    set obj = blank + sqrt($mkiRD*$mkiRD + $darkrate*$exptime + $skyrate*$opentime)*mki_noisei + $darkrate*$exptime + $skyrate*$opentime + $biaslevel
+    keyword obj AIRMASS  -wf {1 + 0.1*$i}
+    keyword obj FILTER   -w  B
+    keyword obj POSANGLE -wf 0.0
+    keyword obj RA       -wf 0.0
+    keyword obj DEC      -wf 0.0
+    keyword obj EXPTIME  -wf $exptime
+    keyword obj DARKTIME -wf $exptime
+    keyword obj DATE-OBS -w "2006/10/01"
+    sprintf time "00:02:%02d" $i
+    keyword obj UTC-OBS  -w $time
+    keyword obj CCDSUM   -w "1 1"
+    keyword obj TELESCOP -w simscope
+    keyword obj INSTRUME -w SIMTEST
+    keyword obj OBSTYPE  -w OBJECT
+    keyword obj OBJECT   -w OBJECT
+    keyword obj GAIN     -wf 1.0
+    keyword obj RDNOISE  -wf $mkiRD
+    sprintf name "$simdir/raw/obj.%02d.fits" $i
+    wd obj $name
+  end  
+end
+
+macro junk
+-definebyquery Optional arguments, with default values:
+    -det_type          ()                         define the type of detrend run (required)
+    -mode              (master)                   define the mode of this detrend run
+    -exp_type          ()                         search for exp_type
+    -inst              ()                         search for camera
+    -telescope         ()                         search for telescope
+    -filter            ()                         search for filter
+    -uri               ()                         search for uri
+    -set_exp_type      ()                         define exposure type
+    -set_filter        ()                         define filter
+    -set_airmass       (nan)            define airmass
+    -set_exp_time      (nan)            define exposure time
+    -set_ccd_temp      (nan)            define ccd tempature
+    -set_posang        (nan)            define rotator position angle
+    -set_object        ()                         define exposure object
+    -set_registered    (2006-12-13T22:40:18.5)    time detrend run was registered
+    -set_use_begin     ()                         start of detrend run applicable peroid
+    -set_use_end       ()                         end of detrend run applicable peroid
+    -pretend           (FALSE)                    print the exposures that would be included in the detrend run and exit
+end
Index: /tags/sj_tags/sj_root_20080929/stac/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/.cvsignore	(revision 22322)
@@ -0,0 +1,11 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.log
+config.status
+configure
+depcomp
+install-sh
+missing
Index: /tags/sj_tags/sj_root_20080929/stac/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/Makefile.am	(revision 22322)
@@ -0,0 +1,6 @@
+SUBDIRS = src/
+
+CLEANFILES = *~ core core.*
+
+EXTRA_DIST = \
+	autogen.sh
Index: /tags/sj_tags/sj_root_20080929/stac/autogen.sh
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/autogen.sh	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/autogen.sh	(revision 22322)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=stac
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+#($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+#        echo
+#        echo "You must have $LIBTOOLIZE installed to compile $PROJECT."
+#        echo "Download the appropriate package for your distribution,"
+#        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+#        DIE=1
+#}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+#$LIBTOOLIZE --copy --force || echo "$LIBTOOLIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/sj_tags/sj_root_20080929/stac/configure.ac
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/configure.ac	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/configure.ac	(revision 22322)
@@ -0,0 +1,28 @@
+AC_PREREQ(2.61)
+
+AC_INIT([stac], [0.0.1], [price@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+dnl AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0]) 
+
+IPP_STDOPTS
+CFLAGS="${CFLAGS=} -Wall -Werror"
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /tags/sj_tags/sj_root_20080929/stac/scripts/Makefile
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/scripts/Makefile	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/scripts/Makefile	(revision 22322)
@@ -0,0 +1,45 @@
+# $Id: Makefile,v 1.1.1.1 2004-05-19 22:55:27 price Exp $
+#
+# make file
+#
+# Paul A. Price
+#
+
+CC = gcc
+FITSFLAGS = -lcfitsio -lm
+GSLFLAGS = -lgslcblas -lgsl
+
+### SUN:
+#FITSFLAGS = -lcfitsio -lm -L../cfitsio/ -lsocket -lnsl
+
+### RH Linux:
+CFLAGS = -Wall -O3 -g -march=i686
+
+# Destination directory
+DESTDIR = .
+
+# Inputs
+FAKEIMAGE = fakeimage.o memory.o
+CONVERTFITS = convertFITS.o memory.o
+
+.PHONY:	all clean
+
+all: fakeimage convertFITS
+
+.c.o:
+	$(CC) -c $(CFLAGS) $<
+
+fakeimage: $(FAKEIMAGE)
+	$(CC) $(FAKEIMAGE) -o $(DESTDIR)/fakeimage $(FITSFLAGS) $(GSLFLAGS)
+
+convertFITS: $(CONVERTFITS)
+	$(CC) $(CFLAGS) $(CONVERTFITS) -o $(DESTDIR)/convertFITS $(FITSFLAGS)
+
+clean:
+	-$(RM) *.o
+
+# Tags for emacs
+.PHONY : tags
+tags :
+	etags `find . \( -name \*.[ch] -o -name \*.cpp \) -print`
+
Index: /tags/sj_tags/sj_root_20080929/stac/scripts/README
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/scripts/README	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/scripts/README	(revision 22322)
@@ -0,0 +1,8 @@
+This directory is provided as a convenience (or a distraction, if you
+prefer).  It contains a few scripts used for playing with the image
+combination code, and a couple of programs for playing with FITS
+images, including one that generates a (very simple) fake image.
+
+The scripts use hard-wired paths that I have not fixed (and I have
+changed the directories in which various things reside when importing
+into CVS).  Be warned....
Index: /tags/sj_tags/sj_root_20080929/stac/scripts/convertFITS.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/scripts/convertFITS.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/scripts/convertFITS.c	(revision 22322)
@@ -0,0 +1,192 @@
+/*
+**
+** $Id: convertFITS.c,v 1.1.1.1 2004-05-19 22:55:27 price Exp $
+**
+** Program to convert BITPIX of FITS images.
+**
+**
+** Paul A. Price
+** 2004 January 28
+** INDNJC.
+**
+**
+** LEGAL STUFF:
+**
+** Copyright 2003, Paul A. Price.
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the GNU General Public License as
+** published by the Free Software Foundation; either version 2 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+** USA
+**
+*/
+
+
+/* Included standard header files */
+#include <stdio.h>
+#include <math.h>
+/* For getopt */
+#include <unistd.h>
+#include <getopt.h>
+/* For cfitsio */
+#include <fitsio.h>
+
+/* Included header files for components */
+#include "memory.h"
+
+/* Handy definitions */
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#define min(a,b) (((a) > (b)) ? (b) : (a))
+#define abs(a) (((a) > 0) ? (a) : (-a))
+
+
+/* Global variables */
+int verbose = 0;   /* Verbose output? */
+
+int main(int argc, char **argv)
+{
+    /* Input variables */
+    char *inputName = NULL;   /* Name of input image */
+    char *outputName = NULL;   /* Name of output image */
+    int bitpix = -32;   /* BITPIX for output image */
+
+    /* Calculation variables */
+    float *inputData;   /* The input pixels */
+    long int inputDims[2];   /* Dimensions of the input image */
+    int i;   /* Counter */
+
+    /* Variables for cfitsio */
+    int status = 0;   /* fitsio status */
+    fitsfile *infp, *outfp;   /* File pointers to FITS file */
+    int nkeys, keypos;   /* Keyword-handling variables */
+    char card[FLEN_CARD];   /* Keyword-handling variable */
+
+    /* Help function */
+    void help(void);
+    /* cfitsio error handler */
+    void ckstatus(int status);
+
+    if (argc < 3)
+    {
+        help();
+        exit(EXIT_FAILURE);
+    }
+    inputName = argv[1];
+    outputName = argv[2];
+    if (sscanf(argv[3], "%d", &bitpix) != 1)
+    {
+	help ();
+	exit (EXIT_FAILURE);
+    }
+
+
+    /* Read the input file */
+    if (verbose) printf ("Opening %s.... ", inputName);
+    if (fits_open_file(&infp,inputName,READONLY,&status)) ckstatus(status);
+    /* Get dimensions */
+    if (fits_get_img_size(infp,2,inputDims,&status)) ckstatus(status);
+    if (verbose) printf ("%ldx%ld\n",inputDims[0],inputDims[1]);
+    /* Allocate storage */
+    inputData = farray(inputDims[0]*inputDims[1]);
+    /* Read the pixels */
+    if (fits_read_img(infp, TFLOAT, 1, inputDims[0]*inputDims[1], 0, inputData, NULL, &status)) ckstatus(status);
+
+    /* Create output file */
+    if (verbose) printf ("Writing %ldx%ld output image...\n",inputDims[0],inputDims[1]);
+    if (fits_create_file(&outfp, outputName, &status)) ckstatus(status);
+
+#if 0
+    /* Write bare header */
+    if (fits_write_imghdr (outfp, bitpix, 2, inputDims, &status)) ckstatus(status);
+#endif
+
+    /* Copy FITS headers */
+    if (fits_get_hdrpos(infp, &nkeys, &keypos, &status))
+        ckstatus(status);
+    for (i=1; i <= nkeys; i++)
+    {
+        fits_read_record(infp, i, card, &status);
+        fits_write_record(outfp, card, &status);
+    }
+    ckstatus(status);
+
+    /* Write new BITPIX */
+    if (fits_update_key (outfp, TINT, "BITPIX", &bitpix, NULL, &status)) ckstatus(status);
+
+    /* Get correct BITPIX for cfitsio */
+    switch (bitpix)
+    {
+    case 8:
+    {
+	char *charData;   /* Short version of the pixels */
+	charData = carray(inputDims[0]*inputDims[1]);
+	for (i = 0; i < inputDims[0]*inputDims[1]; i++) charData[i] = (char)inputData[i];
+	/* Write pixels */
+	if (fits_write_img(outfp, TBYTE, 1, inputDims[0]*inputDims[1], charData, &status)) ckstatus(status);
+	free (charData);
+
+	break;
+    }
+    case 16:
+    {
+	short int *intData;   /* Integer version of the pixels */
+
+	intData = sarray(inputDims[0]*inputDims[1]);
+	for (i = 0; i < inputDims[0]*inputDims[1]; i++) intData[i] = (short int) inputData[i];
+	/* Write pixels */
+	if (fits_write_img(outfp, TSHORT, 1, inputDims[0]*inputDims[1], intData, &status)) ckstatus(status);
+	free (intData);
+	break;
+    }
+    default:
+	fprintf (stderr, "BITPIX must be 8 or 16\n");
+	exit (EXIT_FAILURE);
+    }
+
+    /* Close files and clean up */
+    if (fits_close_file(infp, &status)) ckstatus(status);
+    if (fits_close_file(outfp, &status)) ckstatus(status);
+    free (inputData);
+
+
+    exit (EXIT_SUCCESS);
+}
+
+
+void help(void)
+{
+    fprintf (stderr,
+	     "convertFITS [-hv] INPUT OUTPUT BITPIX\n"
+	     "\n"
+	     "\t-h      Help\n"
+	     "\t-v      Verbose\n"
+	     "\tINPUT   Input FITS file\n"
+	     "\tOUTPUT  Output FITS file\n"
+	     "\tBITPIX  BITPIX for output FITS file\n"
+	     "\n"
+	);
+}
+
+
+void ckstatus(int status)
+{
+    char msg[FLEN_ERRMSG];   /* Error message */
+
+    if (!status)
+        return;
+    fprintf (stderr,"CFITSIO returned an error code of %d\n",status);
+    while (fits_read_errmsg(msg))
+        fprintf(stderr, "CFITSIO: %s\n", msg);
+    fprintf (stderr, "Exiting....\n");
+    exit(EXIT_FAILURE);
+}
Index: /tags/sj_tags/sj_root_20080929/stac/scripts/fakeimage.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/scripts/fakeimage.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/scripts/fakeimage.c	(revision 22322)
@@ -0,0 +1,581 @@
+/*
+** $Id: fakeimage.c,v 1.4 2004-06-03 20:03:58 price Exp $
+** Program to make some fake images.
+**
+** Paul A. Price
+** 2003 September 3
+** INDNJC.
+**
+** Copyright (C) 2003 Paul A. Price
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**                                                                             
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**                                                                             
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+**
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <fitsio.h>
+#include <math.h>
+
+/* For seeding the RNG */
+#include <sys/timeb.h>
+
+/* Memory allocation routines */
+#include "memory.h"
+
+/* GSL */
+#include <gsl/gsl_sf_erf.h>		// For error functions
+
+
+/* Conversion factor by which to multiply sigma to get FWHM.  Roughly 2.35 */
+#define SIGMAtoFWHM (2.0*sqrt((double)2.0*(log((double)2.0))))
+
+/* Errors - range of sigma to use */
+#define MAXSIGMA 4.5
+#define NUMSIGMA 100
+
+
+/* Handy functions */
+#define abs(a) (((a) > 0) ? (a) : (-a))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#define min(a,b) (((a) > (b)) ? (b) : (a))
+
+/* Function declarations */
+void help(void);
+void ckstatus(int status);
+void addstar (float *data, int nx, int ny, int nxos, float x, float y, float flux, float seeing);
+float linint(double x[], double y[], int num, double val);   /* Linear interpolation */
+float gauss (float val);   /* Gaussian-type function */
+float fringe (int x, int y, float periodx, float periody, float phasex, float phasey);
+
+/* Global variables */
+int verbose=0;   /* Verbosity switch */
+long dims[2];   /* Dimensions of images */
+
+int main(int argc, char **argv)
+{
+    /* Input variables */
+    const char *noisefile, *outfile;   /* Input and output filenames */
+    double background;   /* Sky background, ADU */
+    double gain;   /* Detector gain, units e-/ADU */
+    double readnoise;   /* Detector read noise, units e- */
+    long overscan;   /* Number of overscan columns */
+    double zero;   /* Bias/Zero level for image */
+    double zeroslope;   /* Bias/Zero slope for image */
+    int numcrs;   /* Number of CRs */
+    int numstars;   /* Number of stars */
+    double seeing;   /* Seeing FWHM (pixels) */
+    char *starfile;   /* Name of file to use for stars */
+    int debug;   /* Output debug information?  (ie CR position and flux) */
+    int numbadpix;   /* Number of bad pixels */
+
+    /* Calculation variables */
+    fitsfile *fitsfp;   /* FITS files: input, output */
+    int status = 0;   /* fitsio status */
+    int i,x,y;   /* Counters */
+    float *outdata;   /* Output pixels */
+    float *noisedata;   /* Noise - Sigma to add to ideal image */
+    int *mask;   /* Mask of CRs */
+    float starx,stary;   /* Star centre */
+    float flux;   /* (peak) flux of star */
+    FILE *starfilefp;   /* File pointer for star file */
+    int usestarfile;   /* Boolean indicating whether we get the star data from the file or not */
+    int usenoisefile;   /* Boolean indicating whether we apply the noise or not */
+    long noisedims[2];   /* Dimensions of noise map */
+    double *gint, *gsigma;   /* Gaussian */
+    char datasec[FLEN_KEYWORD],biassec[FLEN_KEYWORD]; // Data section and bias section for FITS
+    struct timeb thetime;   /* For seeding the random number generator */
+    float fringeamp, fringeperiod, fringephase;	// Fringe amplitude, period and phase
+
+    /* Variables for getopt */
+    int opt;   /* Option, from getopt */
+    extern char *optarg;   /* Argument accompanying switch */
+    extern int optind;   /* getopt variables */
+
+    /* Seed the random number generator */
+    ftime(&thetime);
+    srand((unsigned int)thetime.millitm);
+
+    /* Set defaults for variables */
+    debug = 0;
+    gain = 1.0;
+    readnoise = 10.0;
+    background = 10000.0;
+    dims[0] = dims[1] = 1024;
+    numcrs = 0;
+    numbadpix = 0;
+    seeing = 2.5;
+    numstars = 0;
+    usestarfile=0;
+    noisefile=NULL;
+    usenoisefile = 0;
+    starfile=NULL;
+    overscan = 0;
+    zero = 0.;
+    zeroslope=0.;
+    fringeamp = 0.0;
+    fringeperiod = (float)dims[0] / 20.0;
+
+    while ((opt = getopt(argc, argv, "hvdkg:r:n:m:c:s:f:b:x:y:z:Z:o:p:F:")) != -1)
+	switch (opt)
+	{
+	  case 'h':
+	    help();
+	    exit(0);
+	  case 'v':
+	    verbose = 1;
+	    break;
+	  case 'd':
+	    debug = 1;
+	    verbose = 0;
+	    break;
+	  case 'k':
+	    usenoisefile = -1;
+	    break;
+	  case 'g':
+	    if (sscanf(optarg, "%lf", &gain) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 'r':
+	    if (sscanf(optarg, "%lf", &readnoise) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 'x':
+	    if (sscanf(optarg, "%ld", &dims[0]) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 'y':
+	    if (sscanf(optarg, "%ld", &dims[1]) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 'z':
+	    if (sscanf(optarg, "%lf", &zero) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 'Z':
+	    if (sscanf(optarg, "%lf", &zeroslope) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 'o':
+	    if (sscanf(optarg, "%ld", &overscan) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 'c':
+	    if (sscanf(optarg, "%d", &numcrs) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 's':
+	    if (sscanf(optarg, "%d", &numstars) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    usestarfile=0;
+	    break;
+	  case 'p':
+	    if (sscanf(optarg, "%d", &numbadpix) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 'n':
+	    noisefile = optarg;
+	    usenoisefile = 1;
+	    break;
+	  case 'b':
+	    if (sscanf(optarg, "%lf", &background) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 'm':
+	    starfile = optarg;
+	    usestarfile=1;
+	    break;
+	  case 'f':
+	    if (sscanf(optarg, "%lf", &seeing) != 1)
+	    {
+		help();
+		exit(1);
+	    }
+	    break;
+	  case 'F':
+	    /*
+	      Note: incrementing optind, so I can read more than
+	      one parameter.
+	    */
+	    
+	    if ((sscanf(argv[optind-1], "%f", &fringeamp) != 1) ||
+		(sscanf(argv[optind++], "%f", &fringeperiod) != 1) ||
+		(sscanf(argv[optind++], "%f", &fringephase) != 1 ))
+	    {
+		help();
+		exit(EXIT_FAILURE);
+	    }
+	    break;
+	  default:
+	    help();
+	    exit(1);
+	}
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc != 1)
+    {
+	help();
+	exit(1);
+    }
+    
+    outfile = argv[0];
+
+
+    /* Now, the program proper... */
+
+    /* Do we read an input, or create an empty image? */
+    if (usenoisefile == 1)
+    {
+	/* Open input file */
+	if (verbose) printf ("Opening %s.... ", noisefile);
+	if (fits_open_file(&fitsfp,noisefile,READONLY,&status)) ckstatus(status);
+
+	/* Get dimensions */
+	if (fits_get_img_size(fitsfp,2,noisedims,&status)) ckstatus(status);
+	if (verbose) printf ("%ldx%ld\n",noisedims[0],noisedims[1]);
+
+	if ((noisedims[0] != dims[0] + overscan) || (noisedims[1] != dims[1]))
+	{
+	    fprintf (stderr, "Noise map must have same dimensions as output image!\n");
+	    fprintf (stderr, "Altering output image dimensions to %ldx%ld\n",noisedims[0],noisedims[1]);
+	    dims[0] = noisedims[0];
+	    dims[1] = noisedims[1];
+	}
+
+	/* Allocate storage */
+	noisedata = farray((dims[0]+overscan)*dims[1]);
+
+	/* Read the pixels */
+	if (fits_read_img(fitsfp, TFLOAT, 1, (dims[0]+overscan)*dims[1], 0, noisedata, NULL, &status)) ckstatus(status);
+
+	/* Close the file */
+	if (fits_close_file(fitsfp, &status)) ckstatus(status);
+
+    }
+    else
+    {
+	/* Integrate gaussian so we can have a nice distribution */
+	if (verbose) printf ("Creating noise map.....\n");
+	gsigma = darray(NUMSIGMA);
+	gint = darray(NUMSIGMA);
+	for (i = 0; i < NUMSIGMA; i++)
+	{
+	    gsigma[i] = 2.0*(double)(MAXSIGMA*i)/(double)NUMSIGMA - MAXSIGMA;
+	    gint[i] = gsl_sf_erf((double)gsigma[i]);
+	}
+
+	/* Allocate memory for output */
+	noisedata = farray((dims[0]+overscan)*dims[1]);   /* Output Image */
+
+	/* Create noise map */
+	for (i=0;i<(dims[0]+overscan)*dims[1];i++) noisedata[i] = (float)linint(gint,gsigma,NUMSIGMA,(double)rand()/(double)RAND_MAX*(gint[NUMSIGMA-1]-gint[0])+gint[0]);
+    }
+
+
+    /* Create empty output file */
+    if (verbose) printf ("Creating %ldx%ld output image, %s\n",dims[0]+overscan,dims[1],outfile);
+    if (fits_create_file(&fitsfp, outfile, &status)) ckstatus(status);
+
+
+    /* Initialise the output buffer to the background */
+    outdata = farray((dims[0]+overscan)*dims[1]);
+    mask = iarray((dims[0]+overscan)*dims[1]);
+    if (verbose) printf("Initialising output buffer\n");
+    for (y=0;y<dims[1];y++)
+	for (x=0;x<dims[0]+overscan;x++)
+	{
+	    /* Bias */
+	    outdata[(dims[0]+overscan)*y+x] = zero + zeroslope*(float)y;
+	    /* CR pixel mask */
+	    mask[(dims[0]+overscan)*y+x] = 0;
+	    /* Add background to the true pixels */
+	    if (x < dims[0]) outdata[(dims[0]+overscan)*y+x] += background;
+	}
+    
+    /* Add stars */
+    if (verbose) printf("Adding stars\n");
+
+    if (usestarfile)
+    {
+	if ((starfilefp = fopen(starfile,"r")) == NULL)
+	{
+	    fprintf(stderr,"Can't open %s\n",starfile);
+	    exit(1);
+	}
+
+	while (fscanf(starfilefp,"%f %f %f",&starx,&stary,&flux) == 3)
+	    addstar(&outdata[0],dims[0],dims[1],overscan,starx,stary,flux,seeing);
+	fclose(starfilefp);
+    }
+    else
+    {
+	/* Random distribution of Stars */
+	for (i=1;i<=numstars;i++)
+	{
+	    starx = (rand()/(RAND_MAX+1.0)*dims[0]);
+	    stary = (rand()/(RAND_MAX+1.0)*dims[1]);
+	    flux = rand()/(RAND_MAX+1.0)*(32768.0-background);
+
+	    addstar(&outdata[0],dims[0],dims[1],overscan,starx,stary,flux,seeing);
+	    if (verbose) printf ("%f %f %f\n",starx,stary,flux);
+	}
+    }
+
+    /* Add fringes */
+    if (fringeamp != 0.0) {
+	for (y = 0; y < dims[1]; y++)
+	    for (x = 0; x < dims[0]; x++)
+		outdata[(dims[0]+overscan)*y+x] += fringeamp * fringe(x,y,fringeperiod,fringeperiod,fringephase,fringephase);
+    }
+    
+    /* Add Poisson noise */
+    if (usenoisefile != -1)
+    {
+	if (verbose) printf ("Adding noise\n");
+	for (y=0;y<dims[1];y++)
+	    for (x=0;x<(dims[0]+overscan);x++)
+		outdata[(dims[0]+overscan)*y+x] += noisedata[(dims[0]+overscan)*y+x]*sqrt((double)(gain*(outdata[(dims[0]+overscan)*y+x]-(zero + zeroslope*y)) + readnoise*readnoise))/gain;
+    }
+
+    /* Random distribution of bad pixels */
+    if (verbose) printf ("Adding bad pixels\n");
+    for (i = 1; i <= numbadpix; i++)
+    {
+	x = (int)(rand()/(RAND_MAX+1.0)*dims[0]);
+	y = (int)(rand()/(RAND_MAX+1.0)*dims[1]);
+	outdata[(dims[0]+overscan)*y+x] = 0.0;
+    }
+
+    /* Random distribution of CRs */
+    if (verbose) printf ("Adding CRs\n");
+    if (numcrs > dims[0]*dims[1]/2) fprintf (stderr,"Image too small (%ldx%ld) for %d CRs --- no CRs added.\n",dims[0],dims[1],numcrs);
+    else
+    {
+	for (i=1;i<=numcrs;i++)
+	{
+	    x = (int)(rand()/(RAND_MAX+1.0)*dims[0]);
+	    y = (int)(rand()/(RAND_MAX+1.0)*dims[1]);
+	    if (mask[(dims[0]+overscan)*y+x]) i--;   /* Get another one */
+	    else
+	    {
+		flux = rand()/(RAND_MAX+1.0)*(32768.0-background);
+		outdata[(dims[0]+overscan)*y+x] += flux;
+		mask[(dims[0]+overscan)*y+x] = 1;
+		if (debug) printf ("%d %d %f\n",x,y,flux);
+	    }
+	}
+    }
+
+    /* Write output */
+    if (verbose) printf ("Writing %ldx%ld output image...\n",dims[0]+overscan,dims[1]);
+    dims[0] += overscan;
+    if (fits_write_imghdr (fitsfp, -32, 2, dims, &status)) ckstatus(status);
+
+    /* Write DATASEC, BIASSEC */
+    (void)sprintf (datasec,"[%ld:%ld,%ld:%ld]",1L,dims[0]-overscan,1L,dims[1]);
+    (void)sprintf (biassec,"[%ld:%ld,%ld:%ld]",dims[0]-overscan+1,dims[0],1L,dims[1]);
+    if (fits_write_key(fitsfp, TSTRING, "DATASEC", datasec,"Data section",&status)) ckstatus(status);
+    if (fits_write_key(fitsfp, TSTRING, "BIASSEC", biassec,"Bias section",&status)) ckstatus(status);
+
+    /* Write the FITS file */
+    if (fits_write_img(fitsfp, TFLOAT, 1, dims[0]*dims[1], outdata, &status)) ckstatus(status);
+
+    /* Close files and clean up */
+    if(fits_close_file(fitsfp, &status)) ckstatus(status);
+    free ((float*)outdata);
+    free ((int*)mask);
+
+    if (verbose) printf ("Done!\n");
+
+    exit(0);
+}
+
+
+/* Functions follow */
+
+void help(void)
+{
+    fprintf(stderr,
+	    "fakeimage [-hvd] [-x NX] [-y NY] [-g GAIN] [-r READNOISE] [-b BACKGROUND] [-s NUM_STARS] [-f FWHM] [-c NUM_CRS] [-p NUM_BADPIX] [-z ZERO_LEVEL] [-o OVERSCAN] [-n NOISE_MAP] OUTPUT_IMAGE\n"
+	    "\n"
+	    "\tOUTPUT_IMAGE:   Output file (FITS file)\n"
+	    "\t-i INPUT_IMAGE: Use input image (FITS file)\n"
+	    "\t-x NX; -y NY:   Size of the created image\n"
+	    "\t-g GAIN:        Detector gain (e-/ADU)\n"
+	    "\t-r READNOISE:   Detector read noise (e-)\n"
+	    "\t-b BACKGROUND:  Sky background (ADU)\n"
+	    "\t-s NUM_STARS:   Number of stars on the image\n"
+	    "\t-m STARFILE:    Use file (x,y,flux) for stars\n"
+	    "\t-f FWHM:        Seeing FWHM\n"
+            "\t-c NUM_CRS:     Number of CRs on the image\n"
+	    "\t-p NUM_BADPIX:  Number of bad pixels on image\n"
+	    "\t-n NOISE_MAP:   Name of an image to use for the noise\n"
+	    "\t-z ZERO_LEVEL:  Bias/Zero level to add to image\n"
+	    "\t-Z ZERO_SLOPE:  Bias/Zero slope as a function of row\n"
+	    "\t-o OVERSCAN:    Number of overscan columns to add\n"
+	    "\t-k:             Klean --- no noise added\n"
+	    "\t-h:             Help\n"
+	    "\t-v:             Verbose\n"
+	    "\t-d:             Debug (ie output CR flux, position)\n"
+	    "\n"
+	);
+}
+
+void ckstatus(int status)
+{
+    char msg[81];
+    
+    if (!status)
+	return;
+    while (fits_read_errmsg(msg))
+	fprintf(stderr, "CFITSIO: %s\n", msg);
+    exit(1);
+}
+
+
+/* Finds a value in an array --- note: zero-indexed */
+int find(double value,			// Value to find
+	 double *array,			// Array to find in
+	 int n				// Number of values in array
+    )
+{
+    int low = 0;			// Low-end bracket index
+    int high = n - 1;			// High-end bracket index
+    int index;				// Index to check
+
+    /* Test the ends */
+    if (array[high] < value) {
+	return high - 1;
+    }
+    if (array[low] > value) {
+	return low;
+    }
+
+    /* Bisect */
+    do {
+	index = (high - low) / 2 + low;
+	if (array[index] > value) {
+	    high = index;
+	} else {
+	    low = index;
+	}
+    } while (high > low + 1);
+
+    return low;
+}
+
+
+
+
+float linint(double x[], double y[], int num, double val)
+{
+    /*
+      Returns the value of the function y = f(x) at val,
+      calculated by linear interpolation.
+      There are num values in arrays x and y.
+    */
+
+    int index = find(val, x, num);	// Index of position in x
+    double ans = y[index] + (y[index+1] - y[index]) * (val - x[index]) / (x[index+1] - x[index]);
+
+    return (ans);
+}
+
+float gauss (float val)
+{
+    /*
+      Returns a normal-shaped curve evaulated at val.
+    */
+    return (exp((double)(-0.5*val*val)));
+}
+
+
+void addstar (float *data, int nx, int ny, int nxos, float x, float y, float flux, float seeing)
+/*
+  Adds a Gaussian to the data
+
+  data: The pixels
+  nx, ny: Dimensions
+  nxos: Size of overscan (don't put star here)
+  x, y: Centre of the star
+  flux: peak flux of the star
+  seeing: FWHM of the star
+*/
+{
+    int j,k;   /* Counters */
+    int minj,maxj,mink,maxk;   /* Ranges */
+
+    /* Get range for star */
+    minj = max(0,(int) (x-seeing/SIGMAtoFWHM*MAXSIGMA));
+    maxj = min(nx-1,(int) (x+seeing/SIGMAtoFWHM*MAXSIGMA));
+    mink = max(0,(int) (y-seeing/SIGMAtoFWHM*MAXSIGMA));
+    maxk = min(ny-1,(int) (y+seeing/SIGMAtoFWHM*MAXSIGMA));
+
+    /* Put the star in. */
+    for (j=minj;j<=maxj;j++)
+	for (k=mink;k<=maxk;k++)
+	    data[(nx+nxos)*k+j] += flux/sqrt(2.0*M_PI)/(seeing/SIGMAtoFWHM)*exp(-1.0 * ((x-j)*(x-j) + (y-k)*(y-k))/2.0/(seeing/SIGMAtoFWHM)/(seeing/SIGMAtoFWHM));
+    
+}
+
+/* Return a "fringe" value for a pixel */
+float fringe (int x, int y,		// Pixel coordinates
+	      float periodx, float periody, // Period in x and y
+	      float phasex, float phasey// Phase for x and y
+    )
+{
+    float xval = 2.0*M_PI/periodx * (float)x + phasex;
+    float yval = 2.0*M_PI/periody * (float)y + phasey;
+
+    return cos(xval)*cos(yval);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/stac/scripts/memory.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/scripts/memory.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/scripts/memory.c	(revision 22322)
@@ -0,0 +1,238 @@
+/*
+** memory.c
+**
+** Handy little functions to allocate memory for arrays etc.
+**
+**
+** CVS: $Id: memory.c,v 1.1.1.1 2004-05-19 22:55:27 price Exp $
+**
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "memory.h"
+
+/*
+  Function to return a pointer to a generic object, such as a struct.
+*/
+
+void *papalloc (size_t size)
+{
+    void *newobject = malloc(size);
+    if (! newobject)
+    {
+	fprintf (stderr, "Error allocating memory for array: %s\n",strerror(errno));
+	abort();
+    }
+    
+    return (newobject);
+}
+	
+
+
+/*
+  Subroutine to allocate memory for an array of doubles of the
+  indicated size.  Array is zero-offset (pass size+1 for unit-offset
+  array).
+*/
+double *darray (size_t size)
+{
+    double *thearray = malloc(size*sizeof(double));
+    if (!thearray)
+    {
+	fprintf (stderr, "Error allocating memory for array: %s\n",strerror(errno));
+	abort();
+    }
+    
+    return (thearray);
+}
+			     
+
+/*
+  Subroutine to allocate memory for an array of floats of the
+  indicated size.  Array is zero-offset (pass size+1 for unit-offset
+  array).
+*/
+float *farray (size_t size)
+{
+    float *thearray = malloc(size*sizeof(float));
+    if (!thearray)
+    {
+	fprintf (stderr, "Error allocating memory for array: %s\n",strerror(errno));
+	abort();
+    }
+
+    return (thearray);
+}
+
+
+/*
+  Subroutine to allocate memory for an array of integers of the
+  indicated size.  Array is zero-offset (pass size+1 for unit-offset
+  array).
+*/
+int *iarray (size_t size)
+{
+    int *thearray = malloc(size*sizeof(int));
+    if (!thearray)
+    {
+	fprintf (stderr, "Error allocating memory for array: %s\n", strerror(errno));
+	abort();
+    }
+    
+    return (thearray);
+}
+
+/*
+  Subroutine to allocate memory for an array of chars of the indicated
+  size.  Array is zero-offset (pass size+1 for unit-offset array).
+*/
+char *carray (size_t size)
+{
+    char *thearray = malloc(size);   /* Since sizeof(char) == 1 by definition */
+    if (!thearray)
+    {
+	fprintf (stderr, "Error allocating memory for array: %s\n", strerror(errno));
+	abort();
+    }
+    
+    return (thearray);
+}
+
+/*
+  Subroutine to allocate memory for an array of short ints of the
+  indicated size.  Array is zero-offset (pass size+1 for unit-offset
+  array).
+*/
+short int *sarray (size_t size)
+{
+    short int *thearray = malloc(size*sizeof(short int));
+    if (!thearray)
+    {
+	fprintf (stderr, "Error allocating memory for array: %s\n", strerror(errno));
+	abort();
+    }
+    
+    return (thearray);
+}
+
+/*
+  Subroutine to allocate memory for an m x n rectangular matrix of
+  floats.  Matrix is zero-offset (pass size+1 for unit-offset matrix).
+*/
+float **fmatrix (size_t m, size_t n)
+{
+    int i;
+    float **thematrix = (float**)malloc(m*sizeof(float*));
+    if (!thematrix)
+    {
+	fprintf (stderr, "Error allocating memory for matrix: %s\n", strerror(errno));
+	abort();
+    }
+
+    for(i=0;i<m;i++)
+    {
+	thematrix[i] = malloc(n*sizeof(float));
+	if (!thematrix[i])
+	{
+	    fprintf (stderr, "Error allocating memory for matrix: %s\n", strerror(errno));
+	    abort();
+	}
+    }
+
+    return thematrix;
+}
+
+
+
+
+
+/*
+  Subroutine to allocate memory for a square matrix of doubles of the
+  indicated size.  Matrix is zero-offset (pass size+1 for unit-offset
+  matrix).
+*/
+double **squarematrix (size_t size)
+{
+    int i;
+    double **thematrix = (double**)malloc(size*sizeof(double*));
+    if (!thematrix)
+    {
+	fprintf (stderr, "Error allocating memory for matrix: %s\n", strerror(errno));
+	abort();
+    }
+    
+    for(i=0;i<size;i++)
+    {
+	thematrix[i] = malloc(size*sizeof(double));
+	if (!thematrix[i])
+	{
+	    fprintf (stderr, "Error allocating memory for matrix: %s\n", strerror(errno));
+	    abort();
+	}
+    }
+  
+    return thematrix;
+}
+
+
+/*
+  Subroutine to allocate memory for an m x n rectangular matrix of
+  doubles.  Matrix is zero-offset (pass size+1 for unit-offset matrix).
+*/
+double **rectmatrix (size_t m, size_t n)
+{
+    int i;
+    double **thematrix = (double**)malloc(m*sizeof(double*));
+    if (!thematrix)
+    {
+	fprintf (stderr, "Error allocating memory for matrix: %s\n", strerror(errno));
+	abort();
+    }
+
+    for(i=0;i<m;i++)
+    {
+	thematrix[i] = malloc(n*sizeof(double));
+	if (!thematrix[i])
+	{
+	    fprintf (stderr, "Error allocating memory for matrix: %s\n", strerror(errno));
+	    abort();
+	}
+    }
+
+    return thematrix;
+}
+
+
+/*
+  Subroutine to allocate memory for an m x n rectangular matrix.
+  Matrix is zero-offset (pass size+1 for unit-offset matrix).  This
+  function adds a buffer of 1 pixel around the edge (i.e. it runs from
+  -1 to m instead of 0 to m-1) --- provided by Nick Kaiser.
+*/
+double **padrectmatrix(size_t m, size_t n)
+{
+
+    int i;
+    double **thematrix = (double **)malloc((m+2)*sizeof(double*)) + 1;
+    if (!thematrix)
+    {
+	fprintf (stderr, "Error allocating memory for matrix: %s\n", strerror(errno));
+	abort();
+    }
+
+    for(i=-1;i<=m;i++)
+    {
+	thematrix[i] = malloc((n+2)*sizeof(double)) + 1;
+	if (!thematrix[i])
+	{
+	    fprintf (stderr, "Error allocating memory for matrix: %s\n", strerror(errno));
+	    abort();
+	}
+    }
+
+    return thematrix;
+}
+
Index: /tags/sj_tags/sj_root_20080929/stac/scripts/memory.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/scripts/memory.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/scripts/memory.h	(revision 22322)
@@ -0,0 +1,83 @@
+/*
+** memory.h
+**
+** Header file for memory.c
+**
+** CVS: $Id: memory.h,v 1.1.1.1 2004-05-19 22:55:27 price Exp $
+**
+*/
+
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+
+void *papalloc (size_t size);
+/*
+  Function to return a pointer to a generic object, such as a struct.
+*/
+
+double *darray (size_t size);
+/*
+  Subroutine to allocate memory for an array of doubles of the
+  indicated size.  Array is zero-offset (pass size+1 for unit-offset
+  array).
+*/
+
+float *farray (size_t size);
+/*
+  Subroutine to allocate memory for an array of floats of the
+  indicated size.  Array is zero-offset (pass size+1 for unit-offset
+  array).
+*/
+
+int *iarray (size_t size);
+/*
+  Subroutine to allocate memory for an array of integers of the
+  indicated size.  Array is zero-offset (pass size+1 for unit-offset
+  array).
+*/
+
+short int *sarray (size_t size);
+/*
+  Subroutine to allocate memory for an array of short ints of the
+  indicated size.  Array is zero-offset (pass size+1 for unit-offset
+  array).
+*/
+
+char *carray (size_t size);
+/*
+  Subroutine to allocate memory for an array of chars of the indicated
+  size.  Array is zero-offset (pass size+1 for unit-offset array).
+*/
+
+float **fmatrix (size_t m, size_t n);
+/*
+  Subroutine to allocate memory for an m x n rectangular matrix of
+  floats.  Matrix is zero-offset (pass size+1 for unit-offset matrix).
+*/
+
+double **squarematrix (size_t size);
+/*
+  Subroutine to allocate memory for a square matrix of doubles of the
+  indicated size.  Matrix is zero-offset (pass size+1 for unit-offset
+  matrix).
+*/
+
+double **rectmatrix (size_t m, size_t n);
+/*
+  Subroutine to allocate memory for an m x n rectangular matrix of
+  doubles.  Matrix is zero-offset (pass size+1 for unit-offset matrix).
+*/
+
+double **padrectmatrix(size_t m, size_t n);
+/*
+  Subroutine to allocate memory for an m x n rectangular matrix.
+  Matrix is zero-offset (pass size+1 for unit-offset matrix).  This
+  function adds a buffer of 1 pixel around the edge (i.e. it runs from
+  -1 to m instead of 0 to m-1) --- provided by Nick Kaiser.
+*/
+
+
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/stac/scripts/mkfakeimages.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/scripts/mkfakeimages.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/scripts/mkfakeimages.pl	(revision 22322)
@@ -0,0 +1,159 @@
+#!/usr/local/bin/perl
+#
+# testcombine.pl
+#
+# Program to create some dithered/rotated frames and combine them
+# to see how the combination works.
+#
+#
+# Copyright (C) 2003 Paul A. Price
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#                                                                             
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#                                                                             
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+#
+# CVS: $Id: mkfakeimages.pl,v 1.2 2004-05-20 00:15:57 price Exp $
+#
+# History:
+# Added orderly star placement.  Fixed so that the star positions are
+# accurate once mapped.  --- PAP, 20/10/03.
+# Allow different seeing between different images --- PAP 19/5/04.
+#
+
+
+# Parameters.
+$DEG_IN_RAD = 57.2957795130823;   # Degrees per radian.
+
+$GAIN = 1;			# Number of electrons per ADU
+$READNOISE = 10;		# Detector read noise (electrons);
+$NUMSTARS = 500;		# Number of stars on images.
+$INSIZE = 1024;		    # Size of input images (pixels on a side).
+$OUTSIZE = 1024;	    # Size of output image (pixels on a side).
+$SEEING = 0; # Seeing in input images (input pixels).  Zero means use array.
+$NUMCRS = 0;			# Number of CRs on input images.
+$STARFILE = "stars";	      # File containing list of stars (x,y,f).
+$IMAGE = "test";		# Base file name for images and stuff.
+$BACKGROUND = 10000;		# Sky background for input images
+$OUTSCALE = sqrt(1);  # Pixel scale of output image compared to input.
+$STARANGLE = 1.5; # Angle at which a regular star field is tilted (degrees)
+$STARFLUXMIN = 500;		# Minimum flux of stars (ADU)
+$STARFLUXMAX = 20000;		# Maximum flux of stars (ADU)
+$MAXOFFSET = 5;	 # Maximum offset for each image (output image scale).
+@ROTATIONS=(0,3);		# Rotations of the images
+@SEEING = (2.5, 4.5);	       # Seeing in input images (input pixels)
+
+
+$CREATE = "./fakeimage -d -x $INSIZE -y $INSIZE -g $GAIN -r $READNOISE -b $BACKGROUND";   # Command to create an input image
+$CREATELIST = "-m ";   # Switch to take stars from a list.
+$CREATESEEING = "-f ";   # Switch for seeing
+
+# Parse command-line arguments
+for ($i=0;$i<=$#ARGV;$i++)
+{
+    if ($ARGV[$i] eq "-numstars") { $NUMSTARS = $ARGV[++$i]; }
+    elsif ($ARGV[$i] eq "-insize") { $INSIZE = $ARGV[++$i]; }
+    elsif ($ARGV[$i] eq "-outsize") { $OUTSIZE = $ARGV[++$i]; }
+    elsif ($ARGV[$i] eq "-seeing") { $SEEING = $ARGV[++$i]; }
+    elsif ($ARGV[$i] eq "-numcrs") { $NUMCRS = $ARGV[++$i]; }
+    elsif ($ARGV[$i] eq "-switches") { $COMBINESWITCHES = $ARGV[++$i]; }
+    else
+    {
+	die "Unknown command-line switch: $ARGV[$i]\n";
+    }
+}
+
+
+
+# Generate list of stars.
+@x=();   # List of star x positions
+@y=();   # List of star y positions
+@f=();   # List of star fluxes
+open STARS, "> $IMAGE.stars";
+$num=0;
+for ($i=0;$i<sqrt($NUMSTARS);$i++)
+{
+    # Random distribution
+#    push @x, rand($OUTSIZE);
+#    push @y, rand($OUTSIZE);
+#    push @f, rand(32*1024-$BACKGROUND);
+
+    # Ordered distribution
+    for ($j=0;$j<sqrt($NUMSTARS);$j++)
+    {
+	push @x, ($i+1)*$OUTSIZE/(sqrt($NUMSTARS)+1)*cos($STARANGLE/$DEG_IN_RAD) + ($j+1)*$OUTSIZE/(sqrt($NUMSTARS)+1)*sin($STARANGLE/$DEG_IN_RAD);
+	push @y, - ($i+1)*$OUTSIZE/(sqrt($NUMSTARS)+1)*sin($STARANGLE/$DEG_IN_RAD) + ($j+1)*$OUTSIZE/(sqrt($NUMSTARS)+1)*cos($STARANGLE/$DEG_IN_RAD);
+	if ($STARFLUXMIN == $STARFLUXMAX) { push @f, $STARFLUXMAX; }
+	else { push @f, rand($STARFLUXMAX-$STARFLUXMIN) + $STARFLUXMIN; }
+
+	print STARS "$x[$num] $y[$num] $f[$num]\n";
+	$num++;
+    }
+}
+
+
+# Iterate over the input images
+for ($i=0;$i<=$#ROTATIONS;$i++)
+{
+    # Get transformation.
+    $B00 = cos($ROTATIONS[$i]/$DEG_IN_RAD) / $OUTSCALE;
+    $B01 = sin($ROTATIONS[$i]/$DEG_IN_RAD) / $OUTSCALE;
+    $B10 = - $B01;
+    $B11 = $B00;
+    $A0 = $INSIZE/2;
+    $A1 = $INSIZE/2;
+    if ($MAXOFFSET == 0)
+    {
+	$offsetx = 0;
+	$offsety = 0;
+    }
+    else
+    {
+	$offsetx = rand(2*$MAXOFFSET) - $MAXOFFSET;
+	$offsety = rand(2*$MAXOFFSET) - $MAXOFFSET;
+    }
+
+    # Output star positions to file
+    open STARS, "> ${IMAGE}_$i.stars";
+    for ($j=0;$j<$num;$j++)
+    {
+	$x = $B00*($x[$j] - 1.0 - $OUTSIZE/2 - $offsetx) + $B10*($y[$j] - 1.0 - $OUTSIZE/2 - $offsety) + $A0;
+	$y = $B01*($x[$j] - 1.0 - $OUTSIZE/2 - $offsetx) + $B11*($y[$j] - 1.0 - $OUTSIZE/2 - $offsety) + $A1;
+
+
+	print STARS "$x $y $f[$j]\n";
+    }
+    close STARS;
+
+
+    # Create image
+    if ($SEEING == 0) { $seeing = $SEEING[$i]; }
+    else { $seeing = $SEEING; }
+    print "$CREATE $CREATESWITCHES $CREATELIST ${IMAGE}_$i.stars $CREATESEEING $seeing ${IMAGE}_$i.fits\n";
+    system "$CREATE $CREATESWITCHES $CREATELIST ${IMAGE}_$i.stars $CREATESEEING $seeing ${IMAGE}_$i.fits";
+
+    # Create map
+    $B00 = $OUTSCALE*cos($ROTATIONS[$i]/$DEG_IN_RAD);
+    $B01 = - $OUTSCALE*sin($ROTATIONS[$i]/$DEG_IN_RAD);
+    $B10 = - $B01;
+    $B11 = $B00;
+
+    $A0=$OUTSIZE/2 - ($B00 + $B10)*$INSIZE/2 + $offsetx;
+    $A1=$OUTSIZE/2 - ($B01 + $B11)*$INSIZE/2 + $offsety;
+
+    # Output map
+    open MAP, "> ${IMAGE}_$i.fits.map";
+    print MAP "$A0 $A1\n$B00 $B01\n$B10 $B11\n";
+    close MAP;
+}
+
Index: /tags/sj_tags/sj_root_20080929/stac/scripts/testMap.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/scripts/testMap.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/scripts/testMap.pl	(revision 22322)
@@ -0,0 +1,54 @@
+#!/usr/local/bin/perl
+
+open STARS, "test.stars";
+my @xSky = ();
+my @ySky = ();
+while (<STARS>) {
+    my ($x, $y, $flux) = split;
+    push @xSky, $x;
+    push @ySky, $y;
+}
+
+for (my $i = 0; $i < 4; $i++) {
+    open MAP, "test_$i.fits.map";
+    my $line = <MAP>;
+    ($A0, $A1) = split /\s+/, $line;
+    my $line = <MAP>;
+    ($B00, $B01) = split /\s+/, $line;
+    my $line = <MAP>;
+    ($B10, $B11) = split /\s+/, $line;
+    close MAP;
+
+    open STARS, "test_$i.stars";
+    my @diff = ();
+    my $num = 0;
+    while (<STARS>) {
+	my ($x, $y, $flux) = split;
+
+	$xPrime = $A0 + $B00*$x + $B10*$y;
+	$yPrime = $A1 + $B01*$x + $B11*$y;
+
+	push @xdiff, $xPrime - $xSky[$num];
+	push @ydiff, $yPrime - $ySky[$num];
+
+	$num++;
+    }
+
+    print "File: $i, Difference: " . mean(\@xdiff) . ", " . mean(\@ydiff) . "\n";
+}
+
+### ENDS
+
+
+# Return the mean
+sub mean
+{
+    my ($arrayRef) = @_;
+
+    my @array = @$arrayRef;
+    my $mean = 0;
+    foreach my $value (@array) {
+	$mean += $value;
+    }
+    return $mean / (scalar @array);
+}
Index: /tags/sj_tags/sj_root_20080929/stac/scripts/testOffsets.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/scripts/testOffsets.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/scripts/testOffsets.pl	(revision 22322)
@@ -0,0 +1,54 @@
+#!/usr/bin/perl
+#
+# testOffsets.pl
+# Concerned that different offsets are producing different sensitivity to finding CRs.
+# For example, half pixel offsets in each direction seem to produce lots of false positives,
+# but tiny offsets produce few CRs found.  Need statistics.
+
+my @offsets = ();
+my @numcr = ();
+my $badNum = 0;
+for (my $iter = 0; $iter < 500; $iter++) {
+    system "./testParams.pl";
+    my $bad = 0;
+    for (my $i = 0; $i < 4; $i++) {
+
+	# Number of CRs found
+	my $wc = `wc -l test_$i.fits.cr`;
+	my ($number) = ($wc =~ /^\s*(\d+)/);
+	print "Found $number CRs in image $i\n";
+	if ($number > 2000) {
+	    $bad = 1;
+	}
+
+	# Offset
+	my $mapFile;
+	open $mapFile, "test_$i.fits.map";
+	my @map = <$mapFile>;
+	print @map . "\n";
+	close $mapFile;
+	my ($x) = ($map[1] =~ /^(\S+)/);
+	my ($y) = ($map[2] =~ /^(\S+)/);
+	my $offset = sqrt($x**2 + $y**2);
+	print "Offset for image $i is $offset\n";
+
+	push @offsets, $offset;
+	push @numcr, $number;
+    }
+
+    if ($bad) {
+	$badNum++;
+	system "mkdir bad_$badNum";
+	system "cp -f test_* testout.fits* bad_$badNum";
+    }
+}
+
+my $cr;
+open $cr, "> cr.dat";
+for (my $i = 0; $i <= $#offsets; $i++) {
+    print $cr "$offsets[$i] $numcr[$i]\n";
+}
+close $cr;
+system "sort -k 1 -n cr.dat > cr.dat.sort";
+
+__END__
Index: /tags/sj_tags/sj_root_20080929/stac/scripts/testParams.pl
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/scripts/testParams.pl	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/scripts/testParams.pl	(revision 22322)
@@ -0,0 +1,176 @@
+#!/usr/bin/perl
+#
+# testParams.pl
+# Program to run through the parameters for STAC.
+
+
+@REJECT = (3.0); # List of "reject" parameters
+@FRAC = (0.5); # List of "frac" parameters
+@GRAD = (0.6); # List of "grad" parameters
+@SEEING = (2.5); # List of seeing
+$ITERATIONS = 1; # Number of iterations for each parameter combination
+
+$RUN = "rm -f testout.fits* chi2_*.fits test_[0-3].fits.\{err,grad,mask,rejmap,shift*}; ./stac -v testout.fits test_0.fits test_1.fits test_2.fits test_3.fits";		# Command with which to run the STAC program
+$RUNREJECT = "-k";		# Rejection parameter flag
+$RUNFRAC = "-f";		# Frac parameter flag
+$RUNGRAD = "-G";		# Grad parameter flag
+$CREATEDIR = "/home/mithrandir/price/images/"; # Directory in which the image creation routines reside
+$CREATE = "./mkfakeimages.pl -diag"; # Command for the image creation
+$CREATESEEING = "-seeing";	# Switch for seeing in the image creation
+$CREATENOCRS = "-numcrs 0";	# Switch for no CRs in the image creation
+
+$WORKINGDIR = `pwd`;		# Working directory (current directory)
+chomp($WORKINGDIR);
+
+# Iterate over the parameter space
+open OUTPUT, "> testParams.out";
+foreach $seeing (@SEEING) {
+    foreach $reject (@REJECT) {
+	foreach $frac (@FRAC) {
+	    foreach $grad (@GRAD) {
+		@trueRejected = (); # Number of true CRs rejected in each iteration
+		@falseRejected = (); # Number of false CRs rejected in each iteration
+		@dBonB = (); # Relative difference between input and output slope
+		
+		# Do each iteration
+		for ($i=1;$i<=$ITERATIONS;$i++) {
+		    
+		    ### Run the program on the data with CRs
+		    
+		    # Make the data
+		    chdir $CREATEDIR;
+		    print "$CREATE $CREATESEEING $seeing\n";
+		    system "$CREATE $CREATESEEING $seeing";
+		    system "mv -f test_[0-3]\{.fits,.fits.map,.cr\} $WORKINGDIR";
+		    chdir $WORKINGDIR;
+		    
+		    # Run the program and count the number of CRs
+		    print "$RUN $RUNREJECT $reject $RUNFRAC $frac $RUNGRAD $grad |\n";
+		    open STAC, "$RUN $RUNREJECT $reject $RUNFRAC $frac $RUNGRAD $grad |";
+		    $numCRs = 0;	# Number of CRs masked
+		    while (<STAC>) {
+			print;
+			if (/(\d+) pixels masked in image \d+/) {
+			    $numCRs += $1;
+			}
+		    }
+		    close STAC;
+		    printf "---> %d pixels masked in total.\n", $numCRs;
+
+		    my @trueFlux = (); # Flux of pixels that are real CRs
+		    my @falseFlux = ();	# Flux of pixels that were masked, but not CRs
+		    for (my $i = 0; $i < 4; $i++) {
+			# Read list of CRs injected into image
+			open CR, "test_$i.cr";
+			my %originalCRs = ();
+			while (<CR>) {
+			    my ($x,$y,$flux) = split;
+			    $originalCRs{"$x,$y"} = $flux;
+			}
+			close CR;
+
+			# Read list of CRs masked by program
+			open CR, "test_$i.fits.cr";
+			while (<CR>) {
+			    my ($x,$y,$junk,$flux,$frac,$grad) = split;
+			    # See if it was injected
+			    if (exists($originalCRs{"$x,$y"})) {
+				push @trueFlux, $flux;
+			    } else {
+				push @falseFlux, $flux;
+			    }
+			}
+		    }
+
+		    # Number of true and false CRs
+		    push @trueRejected, scalar @trueFlux;
+		    push @falseRejected, scalar @falseFlux;
+
+		    print "True: " . scalar @trueFlux . "   False: " . scalar @falseFlux . "\n";
+
+		    # Get input slope
+		    system "cat test_[0-3].cr | sort -k 3 -n | awk \'{num++; print num,\$3;}\' > test_in.cr";
+		    open FIT, "guessline.pl test_in.cr |";
+		    while (<FIT>) {
+			if (/b = (\S+)/) {
+			    $inSlope = $1;
+			    last;
+			}
+		    }
+		    close FIT;
+
+		    # Get output slope
+		    open CR, "| sort -n | awk \'{num++; print num,\$1;}\' > test_out.cr";
+		    foreach $flux (@trueFlux) {
+			print CR $flux . "\n";
+		    }
+		    close CR;
+		    
+		    open FIT, "guessline.pl test_out.cr |";
+		    while (<FIT>) {
+			if (/b = (\S+)/) {
+			    $outSlope = $1;
+			    last;
+			}
+		    }
+		    close FIT;
+		    print "inSlope: $inSlope\noutSlope: $outSlope\n";
+
+		    push @dBonB, abs($outSlope - $inSlope) / $inSlope;
+		    
+		}
+		
+		# Calculate the mean
+		$trueMean = mean(\@trueRejected);
+		$falseMean = mean(\@falseRejected);
+		$trueMedian = median(\@trueRejected);
+		$falseMedian = median(\@falseRejected);
+		$dBonBMean = mean(\@dBonB);
+		$dBonBMedian = median(\@dBonB);
+		
+		printf "$seeing\t$reject\t$frac\t$grad\t%.1f\t%.1f\t%.1f\t%.1f\t%.3f\t%.3f\n",$trueMean,$trueMedian,$falseMean,$falseMedian,$dBonBMean,$dBonBMedian;
+		printf OUTPUT "$seeing\t$reject\t$frac\t$grad\t%.1f\t%.1f\t%.1f\t%.1f\t%.3f\t%.3f\n",$trueMean,$trueMedian,$falseMean,$falseMedian,$dBonBMean,$dBonBMedian;
+	    }
+	}
+    }
+}
+close OUTPUT;
+
+
+
+
+# Return the mean
+sub mean
+{
+    my ($arrayRef) = @_;
+
+    my @array = @$arrayRef;
+    my $mean = 0;
+    foreach my $value (@array) {
+	$mean += $value;
+    }
+    return $mean / (scalar @array);
+}
+
+# Return the median
+sub median
+{
+    my ($arrayRef) = @_;
+    my @array = @$arrayRef;
+    # Insertion sort
+    my @sort = ();
+    foreach my $value (@array) {
+	for ($i=0;$i<=$#sort;$i++) {
+	    if ($sort[$i] < $value) {
+		last;
+	    }
+	}
+	splice @sort, $i, 0, $value;
+    }
+    # Median is the middle one.
+    if (scalar @sort % 2 == 1) {
+	return $sort[(scalar @sort  - 1) / 2];
+    } else {
+	return ($sort[(scalar @sort)/2] + $sort[(scalar @sort)/2 - 1])/2;
+    }
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/.cvsignore
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/.cvsignore	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/.cvsignore	(revision 22322)
@@ -0,0 +1,12 @@
+.deps
+Makefile
+Makefile.in
+calcGradient
+combine
+shift
+shiftSize
+stac
+sum
+config.h
+config.h.in
+stamp-h1
Index: /tags/sj_tags/sj_root_20080929/stac/src/Makefile.am
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/Makefile.am	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/Makefile.am	(revision 22322)
@@ -0,0 +1,72 @@
+# Compilation flags for Automake
+AM_CPPFLAGS = $(PSLIB_CFLAGS)
+AM_LDFLAGS = $(PSLIB_LIBS)
+
+# Programs to install in the "bin" directory
+bin_PROGRAMS = \
+	calcGradient \
+	combine \
+	shift \
+	shiftSize \
+	stac \
+	sum
+
+# Header files that don't need to be installed
+noinst_HEADERS = \
+	combineConfig.h \
+	stac.h \
+	stacConfig.h
+
+# List of sources for each of the programs
+stac_SOURCES = \
+	stac.c \
+	stacAreaOfInterest.c \
+	stacConfig.c \
+	stacCombine.c \
+	stacErrorImages.c \
+	stacHelp.c \
+	stacInvertMaps.c \
+	stacRead.c \
+	stacRejection.c \
+	stacScales.c \
+	stacSize.c \
+	stacTime.c \
+	stacTransform.c
+
+calcGradient_SOURCES = \
+	calcGradient.c \
+	stacRejection.c
+
+combine_SOURCES = \
+	combine.c \
+	combineConfig.c \
+	stacCombine.c \
+	stacErrorImages.c \
+	stacRead.c \
+	stacScales.c \
+	stacTime.c
+
+shift_SOURCES = \
+	shift.c \
+	stacInvertMaps.c \
+	stacRead.c \
+	stacSize.c \
+	stacTransform.c
+
+shiftSize_SOURCES = \
+	shiftSize.c \
+	stacConfig.c \
+	stacRead.c \
+	stacSize.c \
+	stacWrite.c
+
+sum_SOURCES = \
+	sum.c
+
+# Clean target
+clean-local:
+	-$(RM) -f TAGS gmon.* profile.txt
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /tags/sj_tags/sj_root_20080929/stac/src/calcGradient.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/calcGradient.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/calcGradient.c	(revision 22322)
@@ -0,0 +1,59 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "stac.h"
+
+int main(int argc, char *argv[])
+{
+    if (argc != 3) {
+        printf("Usage: %s FITS_IN FITS_OUT\n", argv[0]);
+        exit(EXIT_FAILURE);
+    }
+    const char *inName = argv[1];       // Input filename
+    const char *outName = argv[2];      // Output filename
+
+    psFits *inFile = psFitsOpen(inName, "r"); // File to read
+    psRegion imageRegion = {0, 0, 0, 0}; // Region of image to read
+
+    // We only read PHUs --- not mucking around with extensions for now
+    psImage *image = psFitsReadImage(inFile, imageRegion, 0);
+    if (image == NULL) {
+        psErrorStackPrint(stderr,"Fatal error: Unable to read %s\n", inName);
+        exit(EXIT_FAILURE);
+    }
+    psTrace("calcGradients.read", 4, "Image %s is %dx%d\n", inName, image->numCols, image->numRows);
+    // Convert to 32-bit floating point, in necessary
+    if (image->type.type != PS_TYPE_F32) {
+        psTrace("calcGradients.read", 3, "Converting %s to floating point in memory....", inName);
+        psImage *temp = psImageCopy(NULL, image, PS_TYPE_F32);
+        psFree(image);
+        image = temp;
+    } else {
+        int numNaN = psImageClipNaN(image, FLT_MAX);
+        if (numNaN) {
+            psTrace("calcGradients.read", 5, "Clipped %d NaN pixels.\n", numNaN);
+        }
+    }
+    psFitsClose(inFile);
+
+    psImage *grad = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
+    for (int y = 0; y < image->numRows; y++) {
+        for (int x = 0; x < image->numCols; x++) {
+            grad->data.F32[y][x] = stacGradient(image, x, y);
+        }
+    }
+
+    psFree(image);
+
+    psFits *outFile = psFitsOpen(outName, "w");
+    if (!psFitsWriteImage(outFile, NULL, grad, 0, NULL)) {
+        psErrorStackPrint(stderr, "Unable to write image: %s\n", outName);
+    }
+    psFitsClose(outFile);
+
+    return EXIT_SUCCESS;
+}
+
Index: /tags/sj_tags/sj_root_20080929/stac/src/combine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/combine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/combine.c	(revision 22322)
@@ -0,0 +1,87 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "stac.h"
+#include "combineConfig.h"
+
+int main(int argc, char **argv)
+{
+    // Set trace levels
+    (void)psTraceSetLevel(".", 0);
+    (void)psTraceSetLevel("stac.checkMemory", 10);
+    (void)psTraceSetLevel("stac.read", 10);
+    (void)psTraceSetLevel("stac.scales", 7);
+    (void)psTraceSetLevel("stac.rescale", 10);
+    (void)psTraceSetLevel("stac.combine", 10);
+    (void)psTraceSetLevel("combine", 10);
+
+    // Set logging level
+    psLogSetLevel(9);
+
+    // Parse command line
+    combineConfig *config = combineParseConfig(argc, argv); // Configuration
+
+    psArray *headers = NULL;            // Array of headers
+    psArray *images = stacReadImages(&headers, config->inNames); // The images
+    // Make fake errors
+    psArray *errors = psArrayAlloc(images->n);
+    for (int i = 0; i < images->n; i++) {
+        psImage *image = images->data[i];
+        psImage *err = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
+        for (int y = 0; y < image->numRows; y++) {
+            for (int x = 0; x < image->numCols; x++) {
+                err->data.F32[y][x] = 1.0;
+            }
+        }
+        errors->data[i] = err;
+    }
+
+    // Calculate scales between images
+    psVector *scales = NULL;            // Relative scales between images
+    psVector *offsets = NULL;           // Offsets between images
+    (void)stacScales(&scales, &offsets, images, config->starFile, config->starMap, 0.0, 0.0, config->aper);
+
+    // Set the saturation and bad values
+    psVector *saturated = psVectorAlloc(images->n, PS_TYPE_F32); // Saturation limits
+    psVector *bad = psVectorAlloc(images->n, PS_TYPE_F32); // Bad limits
+    for (int i = 0; i < images->n; i++) {
+        saturated->data.F32[i] = (config->saturated - offsets->data.F32[i]) / scales->data.F32[i];
+        bad->data.F32[i] = (config->bad - offsets->data.F32[i]) / scales->data.F32[i];
+    }
+
+    // Rescale the images
+    (void)stacRescale(images, errors, NULL, scales, offsets);
+
+    // Combine the images
+    psImage *combined = NULL;           // Combined image
+    psArray *rejected = NULL;           // Array of rejection masks
+    stacCombine(&combined, &rejected, images, errors, config->nReject, NULL, saturated, bad, config->reject);
+
+    psFits *outFile = psFitsOpen(config->outName, "w");
+    if (!psFitsWriteImage(outFile, headers->data[0], combined, 0, NULL)) {
+        psErrorStackPrint(stderr, "Unable to write image: %s\n", config->outName);
+    }
+    psTrace("combine", 1, "Combined image written to %s\n", config->outName);
+    psFitsClose(outFile);
+
+    // Clean up
+    psFree(headers);
+    psFree(combined);
+    psFree(rejected);
+    psFree(images);
+    psFree(errors);
+    psFree(saturated);
+    psFree(bad);
+    psFree(scales);
+    psFree(offsets);
+    combineConfigFree(config);
+
+#ifdef TESTING
+    // Check memory for leaks, corruption
+    stacCheckMemory();
+#endif
+
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/combineConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/combineConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/combineConfig.c	(revision 22322)
@@ -0,0 +1,165 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include "pslib.h"
+#include "combineConfig.h"
+
+void help(const char *programName
+    )
+{
+    fprintf (stderr, "shift: shift an image, given the transformation\n"
+             "Usage: %s [-h] [-v] [-g GAIN] [-r READNOISE] [-s SAT] [-b BAD] [-p FILE MAP] [-a APER] [-k SIGMAREJ] [-n NREJECT] OUT IN1 IN2...\n"
+             "where\n"
+             "\t-h           Help (this info)\n"
+             "\t-v           Increase verbosity level\n"
+             "\t-g GAIN      Gain in e/ADU (1.0)\n"
+             "\t-r READNOISE Read noise in e (0.0)\n"
+             "\t-s SAT       Saturation point (65535)\n"
+             "\t-b BAD       Bad level (0)\n"
+             "\t-p FILE MAP  Specify file containing star coordinates, with map\n"
+             "\t-a APER      Aperture radius for photometry (3.0)\n"
+             "\t-k SIGMAREJ  k-sigma rejection threshold (3.0)\n"
+             "\t-n NREJECT   Number of rejection iterations (1)\n"
+             "\tOUT          Output image\n"
+             "\tIN1 IN2...   Input images (identical size)\n",
+             programName
+        );
+}
+
+
+combineConfig *combineConfigAlloc(void)
+{
+    combineConfig *config = psAlloc(sizeof(combineConfig)); // Configuration
+
+    // Parameters with default values
+    config->verbose = 0;                // Verbosity level
+    config->gain = 1.0;                 // Gain (e/ADU)
+    config->readnoise = 0.0;            // Read noise (e)
+    config->reject = 4.0;               // Rejection threshold (sigma)
+    config->nReject = 1;                // Number of rejection iterations
+    config->saturated = 65535.0;        // Saturation level
+    config->bad = 0.0;                  // Bad level
+    config->outName = NULL;             // Output name
+    config->inNames = NULL;             // Input names;
+    config->starFile = NULL;            // Filename of file containing stars
+    config->starMap = NULL;             // Map for stars
+    config->aper = 3.0;                 // Aperture for photometry
+
+    return config;
+}
+
+void combineConfigFree(combineConfig *config)
+{
+    psFree(config->inNames);
+    psFree(config);
+}
+
+
+combineConfig *combineParseConfig(int argc, char *argv[])
+{
+    combineConfig *config = combineConfigAlloc(); // Configuration
+
+    const char *programName = argv[0];  // Program name
+
+    /* Variables for getopt */
+    int opt;   /* Option, from getopt */
+    extern char *optarg;   /* Argument accompanying switch */
+    extern int optind;   /* getopt variables */
+
+    /* Parse command-line arguments using getopt */
+    while ((opt = getopt(argc, argv, "hvg:r:s:b:p:a:k:n:")) != -1) {
+        switch (opt) {
+          case 'h':
+            help(programName);
+            exit(EXIT_SUCCESS);
+          case 'v':
+            config->verbose++;
+            break;
+          case 'r':
+            if (sscanf(optarg, "%f", &config->readnoise) != 1) {
+                printf("Unable to read readnoise.\n");
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'g':
+            if (sscanf(optarg, "%f", &config->gain) != 1) {
+                printf("Unable to read gain.\n");
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 's':
+            if (sscanf(optarg, "%f", &config->saturated) != 1) {
+                printf("Unable to read saturation limit.\n");
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'b':
+            if (sscanf(optarg, "%f", &config->bad) != 1) {
+                printf("Unable to read bad limit.\n");
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'p':
+            if (argc < optind+1) {
+                printf("Unable to read photometric files.\n");
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            config->starFile = argv[optind-1];
+            config->starMap = argv[optind++];
+            // Note: incrementing optind, so I can read more than one parameter.
+            break;
+          case 'a':
+            if (sscanf(optarg, "%f", &config->aper) != 1) {
+                printf("Unable to read aperture.\n");
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'k':
+            if (sscanf(optarg, "%f", &config->reject) != 1) {
+                printf("Unable to read rejection limit.\n");
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'n':
+            if (sscanf(optarg, "%d", &config->nReject) != 1) {
+                printf("Unable to read number of rejection iterations.\n");
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          default:
+            printf("Bad option: %c\n", opt);
+            help(programName);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    /* Get the remaining, mandatory values */
+    argc -= optind;
+    argv += optind;
+    if (argc < 2) {
+        help(programName);
+        exit(EXIT_FAILURE);
+    }
+    config->outName = argv[0];          // Output filename
+    config->inNames = psArrayAlloc(argc-1); // Input filenames
+    for (int i = 1; i < argc; i++) {
+        config->inNames->data[i-1] = psAlloc(strlen(argv[i]));
+        strncpy(config->inNames->data[i-1], argv[i], strlen(argv[i]));
+    }
+
+    return config;
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/combineConfig.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/combineConfig.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/combineConfig.h	(revision 22322)
@@ -0,0 +1,28 @@
+#ifndef COMBINE_CONFIG_H
+#define COMBINE_CONFIG_H
+
+typedef struct {
+    int verbose;			// Verbosity level
+    float gain;				// Gain (e/ADU)
+    float readnoise;			// Read noise (e)
+    float reject;			// Rejection threshold (sigma)
+    int nReject;			// Number of rejection iterations
+    float saturated;			// Saturation level
+    float bad;				// Bad level
+    char *outName;			// Output name
+    psArray *inNames;			// Input names;
+    char *starFile;			// Filename of file containing stars
+    char *starMap;			// Map for stars
+    float aper;				// Aperture for photometry
+} combineConfig;
+
+
+combineConfig *combineConfigAlloc(void);// Allocator
+void combineConfigFree(combineConfig *config); // Deallocator
+
+void help(const char *programName);	// Print help
+combineConfig *combineParseConfig(int argc, char *argv[]); // Parse command line
+
+
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/stac/src/shift.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/shift.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/shift.c	(revision 22322)
@@ -0,0 +1,185 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include "pslib.h"
+#include "stac.h"
+
+void help(const char *programName
+    )
+{
+    fprintf (stderr, "shift: shift an image, given the transformation\n"
+             "Usage: %s [-h] [-v] [-s NX NY] IN OUT\n"
+             "where\n"
+             "\t-h           Help (this info)\n"
+             "\t-v           Increase verbosity level\n"
+             "\t-s NX NY     Size of output image\n"
+             "\tIN           Input image, which has associated .map file\n"
+             "\tOUT          Output image\n",
+             programName
+        );
+}
+
+
+int main(int argc, char **argv)
+{
+    // Set trace levels
+    (void)psTraceSetLevel(".", 0);
+    (void)psTraceSetLevel("stac.checkMemory", 10);
+    (void)psTraceSetLevel("stac.read", 10);
+    (void)psTraceSetLevel("stac.invertMaps", 10);
+    (void)psTraceSetLevel("stac.transform", 10);
+    (void)psTraceSetLevel("stac.size", 10);
+    (void)psTraceSetLevel("shift", 10);
+
+    // Set logging level
+    psLogSetLevel(9);
+
+    // Parameters with default values
+    int outnx = 0, outny = 0;           // Size of output image
+    int verbose = 0;                    // Verbosity level
+    float bad = 0.0;                    // Bad level
+
+    // Parse command line
+    const char *programName = argv[0];  // Program name
+    /* Variables for getopt */
+    int opt;   /* Option, from getopt */
+    extern char *optarg;   /* Argument accompanying switch */
+    extern int optind;   /* getopt variables */
+
+    /* Parse command-line arguments using getopt */
+    while ((opt = getopt(argc, argv, "hvb:s:")) != -1) {
+        switch (opt) {
+          case 'h':
+            help(programName);
+            exit(EXIT_SUCCESS);
+          case 'v':
+            verbose++;
+            break;
+          case 's':
+            if ((sscanf(argv[optind-1], "%d", &outnx) != 1) || (sscanf(argv[optind++], "%d", &outny) != 1)) {
+                // Note: incrementing optind, so I can read more than one parameter.
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'b':
+            if (sscanf(optarg, "%f", &bad) != 1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          default:
+            help(programName);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    /* Get the remaining, mandatory values */
+    argc -= optind;
+    argv += optind;
+    if (argc < 2) {
+        help(programName);
+        exit(EXIT_FAILURE);
+    }
+    const char *inName = argv[0];       // Input filename
+    const char *outName = argv[1];      // Output filename
+
+    psFits *imageFile = psFitsOpen(inName, "r");
+    psRegion imageRegion = {0, 0, 0, 0}; // Region of image to read
+    psMetadata *header = psFitsReadHeader(NULL, imageFile); // FITS header
+    psImage *image = psFitsReadImage(imageFile, imageRegion, 0);
+    if (image == NULL) {
+        psErrorStackPrint(stderr, "Fatal error: Unable to read %s\n", inName);
+        exit(EXIT_FAILURE);
+    }
+    psTrace("shift", 4, "Image %s is %dx%d\n", inName, image->numCols, image->numRows);
+    // Convert to 32-bit floating point, in necessary
+    if (image->type.type != PS_TYPE_F32) {
+        psTrace("shift", 5, "Converting %s to floating point in memory....", inName);
+        psImage *temp = psImageCopy(NULL, image, PS_TYPE_F32);
+        psFree(image);
+        image = temp;
+    }
+    psFitsClose(imageFile);
+
+    // Generate masks
+    psImage *mask = psImageAlloc(image->numCols, image->numRows, PS_TYPE_U8);
+    for (int y = 0; y < image->numRows; y++) {
+        for (int x = 0; x < image->numCols; x++) {
+            if (image->data.F32[y][x] <= bad) {
+                mask->data.U8[y][x] = 1;
+            } else {
+                mask->data.U8[y][x] = 0;
+            }
+        }
+    }
+
+    // Read map
+    char mapName[MAXCHAR];              // Name of map file
+    sprintf(mapName, "%s.map", inName);
+    psPlaneTransform *map = stacReadMap(mapName);
+
+    // Functions work on array of images, so:
+    psArray *images = psArrayAlloc(1);  // An array of one
+    psArray *maps = psArrayAlloc(1);    // An array of one
+    psArray *masks = psArrayAlloc(1);   // An array of one
+    images->data[0] = image;
+    maps->data[0] = map;
+    masks->data[0] = mask;
+
+    // Get size
+    if (outnx == 0 || outny == 0) {
+        psVector *xSize = psVectorAlloc(1, PS_TYPE_S32); // A vector of one
+        psVector *ySize = psVectorAlloc(1, PS_TYPE_S32); // A vector of one
+        xSize->data.S32[0] = image->numCols;
+        ySize->data.S32[0] = image->numRows;
+        stacSize(&outnx, &outny, NULL, NULL, xSize, ySize, maps);
+        psFree(xSize);
+        psFree(ySize);
+    }
+
+    // Invert maps
+    psArray *inverseMaps = stacInvertMaps(maps, outnx, outny);
+
+    // Transform inputs and errors
+    psArray *transformed = NULL;
+    (void)stacTransform(&transformed, NULL, images, inverseMaps, NULL, masks, NULL, NULL, NULL, outnx, outny);
+
+    // Update FITS header appropriately
+    psMetadataAddS32(header, PS_LIST_TAIL, "NAXIS1", PS_META_REPLACE, "Number of pixels in x",
+                     (int)((psImage*)transformed->data[0])->numCols);
+    psMetadataAddS32(header, PS_LIST_TAIL, "NAXIS2", PS_META_REPLACE, "Number of pixels in y",
+                     (int)((psImage*)transformed->data[0])->numRows);
+    psMetadataAddS32(header, PS_LIST_TAIL, "BITPIX", PS_META_REPLACE, "Bits per pixel", -32);
+
+    // Write out transformed image
+    psFits *outFile = psFitsOpen(outName, "w");
+    int numPix = psImageClipNaN(transformed->data[0], 0.0);
+    if (numPix > 0) {
+        psTrace("stac", 3, "Clipping %d NaN pixels to zero.\n", numPix);
+    }
+    if (!psFitsWriteImage(outFile, header, transformed->data[0], 0, NULL)) {
+        psErrorStackPrint(stderr, "Unable to write image: %s\n", outName);
+    }
+    psTrace("shift", 1, "Transformed image written to %s\n", outName);
+    psFitsClose(outFile);
+
+    // Free everything I've used
+    psFree(images);
+    psFree(maps);
+    psFree(masks);
+    psFree(inverseMaps);
+    psFree(transformed);
+
+#ifdef TESTING
+    // Check memory for leaks, corruption
+    stacCheckMemory();
+#endif
+
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/shiftSize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/shiftSize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/shiftSize.c	(revision 22322)
@@ -0,0 +1,129 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*
+ * shiftSize:
+ * A stripped-down version of STAC that outputs the necessary size to keep all the pixels in the individual
+ * images within the shifted images.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include "pslib.h"
+#include "stac.h"
+
+void help(const char *name)
+{
+    fprintf (stderr, "shiftSize: Calculate size for output combined image\n"
+             "Usage: %s [-h] [-v] IN1 IN2...\n"
+             "where\n"
+             "\t-h           Help (this info)\n"
+             "\t-v           Increase verbosity level\n"
+             "\tIN1, IN2...  Input images, which have associated .map files.\n",
+             name
+        );
+}
+
+int main(int argc, char *argv[])
+{
+    // Set trace levels
+    (void)psTraceSetLevel(".",0);
+    (void)psTraceSetLevel("err", 10);
+#ifdef TESTING
+    (void)psTraceSetLevel("stac.checkMemory",10);
+    (void)psTraceSetLevel("stac.read",10);
+    (void)psTraceSetLevel("stac.size",10);
+#endif
+
+    // Set logging level
+    psLogSetLevel(9);
+
+
+    int verbose = 0;                    // Verbosity level
+
+    // Parse command line
+    const char *programName = argv[0];  // Program name
+    /* Variables for getopt */
+    int opt;   /* Option, from getopt */
+    extern char *optarg;   /* Argument accompanying switch */
+    extern int optind;   /* getopt variables */
+
+    /* Parse command-line arguments using getopt */
+    while ((opt = getopt(argc, argv, "hv")) != -1) {
+        switch (opt) {
+          case 'h':
+            help(programName);
+            exit(EXIT_SUCCESS);
+          case 'v':
+            verbose++;
+            break;
+          default:
+            help(programName);
+        }
+    }
+
+    /* Get the remaining, mandatory values */
+    argc -= optind;
+    argv += optind;
+    if (argc < 1) {
+        help(programName);
+        exit(EXIT_FAILURE);
+    }
+
+    psArray *inputs = psArrayAlloc(argc); // Input filenames
+    for (int i = 0; i < argc; i++) {
+        inputs->data[i] = psStringCopy(argv[i]);
+        psTrace("stac.size", 8, "Input file: %s\n", (char*)inputs->data[i]);
+    }
+
+    // Read input files
+    psVector *xSize = psVectorAlloc(inputs->n, PS_TYPE_S32);
+    psVector *ySize = psVectorAlloc(inputs->n, PS_TYPE_S32);
+    for (int i = 0; i < inputs->n; i++) {
+        const char *filename = inputs->data[i]; // Name of FITS file
+        psFits *file = psFitsOpen(filename, "r"); // FITS file handle
+        psMetadata *header = psFitsReadHeader(NULL, file); // FITS header
+        psFitsClose(file);
+        bool mdok = true;               // Status of MD lookup
+        xSize->data.S32[i] = psMetadataLookupS32(&mdok, header, "NAXIS1");
+        if (!mdok) {
+            psError(PS_ERR_IO, true, "Unable to find NAXIS1 in %s\n", filename);
+            exit(EXIT_FAILURE);
+        }
+        ySize->data.S32[i] = psMetadataLookupS32(&mdok, header, "NAXIS2");
+        if (!mdok) {
+            psError(PS_ERR_IO, true, "Unable to find NAXIS2 in %s\n", filename);
+            exit(EXIT_FAILURE);
+        }
+        psFree(header);
+    }
+
+    // Read maps
+    psArray *maps = stacReadMaps(inputs);
+
+    // Get size
+    int outnx, outny;                   // Size of combined image
+    float xMapDiff, yMapDiff;           // Difference to apply to maps
+    stacSize(&outnx, &outny, &xMapDiff, &yMapDiff, xSize, ySize, maps);
+    psFree(xSize);
+    psFree(ySize);
+
+    printf("%d %d\n", outnx, outny);
+
+    // Write out altered maps
+    stacWriteMaps(inputs, maps);
+
+    psFree(inputs);
+    psFree(maps);
+
+#ifdef TESTING
+    // Check memory for leaks, corruption
+    stacCheckMemory();
+#endif
+
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/stac.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stac.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stac.c	(revision 22322)
@@ -0,0 +1,351 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "stac.h"
+#include "stacConfig.h"
+#include <sys/time.h>
+
+int main(int argc, char **argv)
+{
+    double startTime;
+    startTime = getTime();
+
+#if 0
+    psMemAllocateCallbackSet(stacMemPrint);
+    psMemAllocateCallbackSetID(185);
+    psMemFreeCallbackSet(stacMemPrint);
+    psMemFreeCallbackSetID(185);
+#endif
+
+    // Set trace levels
+    (void)psTraceSetLevel(".",0);
+    (void)psTraceSetLevel("stac",10);
+    (void)psTraceSetLevel("stac.checkMemory",10);
+    (void)psTraceSetLevel("stac.config",10);
+    (void)psTraceSetLevel("stac.read",10);
+    (void)psTraceSetLevel("stac.invertMaps",10);
+    (void)psTraceSetLevel("stac.errors",10);
+    (void)psTraceSetLevel("stac.transform",10);
+    (void)psTraceSetLevel("stac.combine",10);
+    (void)psTraceSetLevel("stac.rejection",10);
+    (void)psTraceSetLevel("stac.time",10);
+    (void)psTraceSetLevel("stac.area",10);
+    (void)psTraceSetLevel("stac.size",10);
+    (void)psTraceSetLevel("stac.scales",7);
+
+    // Set logging level
+    psLogSetLevel(9);
+
+    // Parse command line
+    stacConfig *config = stacParseConfig(argc, argv);
+
+    // Read input files
+    psArray *headers = NULL;            // Array of headers
+    psArray *inputs = stacReadImages(&headers, config->inputs);
+
+    // Generate masks
+    psArray *masks = psArrayAlloc(inputs->n);
+    for (int i = 0; i < inputs->n; i++) {
+        psImage *image = inputs->data[i]; // Image for which to get mask
+        psImage *mask = psImageAlloc(image->numCols, image->numRows, PS_TYPE_U8);
+        for (int y = 0; y < image->numRows; y++) {
+            for (int x = 0; x < image->numCols; x++) {
+                if (image->data.F32[y][x] <= config->bad) {
+                    mask->data.U8[y][x] = 1;
+                } else {
+                    mask->data.U8[y][x] = 0;
+                }
+            }
+        }
+        masks->data[i] = mask;
+    }
+
+    // Read maps
+    psArray *maps = stacReadMaps(config->inputs);
+
+    psTrace("stac.time", 1, "I/O completed at %f seconds\n", getTime() - startTime);
+
+    // Get size, if not input
+    if (config->outnx == 0 || config->outny == 0) {
+        psVector *xSize = psVectorAlloc(inputs->n, PS_TYPE_S32); // Sizes of images in x
+        psVector *ySize = psVectorAlloc(inputs->n, PS_TYPE_S32); // Sizes of images in y
+        for (int i = 0; i < inputs->n; i++) {
+            psImage *image = inputs->data[i]; // The i-th image
+            xSize->data.S32[i] = image->numCols;
+            ySize->data.S32[i] = image->numRows;
+        }
+        stacSize(&config->outnx, &config->outny, &config->xMapDiff, &config->yMapDiff, xSize, ySize, maps);
+        psFree(xSize);
+        psFree(ySize);
+    }
+
+    // Fix up the headers
+    for (int i = 0; i < headers->n; i++) {
+        psMetadata *header = headers->data[i];
+        psMetadataAddS32(header, PS_LIST_TAIL, "NAXIS1", PS_META_REPLACE, "Number of pixels in x",
+                         config->outnx);
+        psMetadataAddS32(header, PS_LIST_TAIL, "NAXIS2", PS_META_REPLACE, "Number of pixels in y",
+                         (int)config->outny);
+        psMetadataAddS32(header, PS_LIST_TAIL, "BITPIX", PS_META_REPLACE, "Bits per pixel", -32);
+#if 0
+        bool mdok = true;               // Result of MD lookup
+        float crpix1 = psMetadataLookupF32(&mdok, header, "CRPIX1");
+        if (mdok && !isnan(crpix1)) {
+            psMetadataAddF32(header, PS_LIST_TAIL, "CRPIX1", PS_META_REPLACE,
+                             "WCS Coordinate reference pixel", crpix1 -
+#endif
+    }
+
+    // Invert maps
+    psArray *inverseMaps = stacInvertMaps(maps, config->outnx, config->outny);
+
+    // Generate errors
+    psArray *errors = stacErrorImages(inputs, config->gain, config->readnoise);
+#ifdef TESTING
+    // Write error images out to check
+    for (int i = 0; i < inputs->n; i++) {
+        char errName[MAXCHAR];          // Filename of error image
+        sprintf(errName,"%s.err", (char*)config->inputs->data[i]);
+        psFits *errorFile = psFitsOpen(errName, "w");
+        if (!psFitsWriteImage(errorFile, NULL, errors->data[i], 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", errName);
+        }
+        psTrace("stac", 1, "Error image written to %s\n", errName);
+        psFitsClose(errorFile);
+    }
+#endif
+
+    // Transform inputs and errors
+    psArray *transformed = NULL;
+    psArray *transformedErrors = NULL;
+    (void)stacTransform(&transformed, &transformedErrors, inputs, inverseMaps, errors, masks, NULL, NULL, NULL,
+                        config->outnx, config->outny);
+    psTrace("stac.time",1,"Transformation completed at %f seconds\n", getTime() - startTime);
+
+#ifdef TESTING
+    // Write transformed images out to check
+    for (int i = 0; i < inputs->n; i++) {
+        char shiftName[MAXCHAR];        // Filename of shift image
+        char errName[MAXCHAR];          // Filename of error image
+        sprintf(shiftName,"%s.shift1", (char*)config->inputs->data[i]);
+        sprintf(errName,"%s.shifterr1",(char*)config->inputs->data[i]);
+        psFits *shiftFile = psFitsOpen(shiftName, "w");
+        psFits *errFile = psFitsOpen(errName, "w");
+        psImage *trans = transformed->data[i]; // Transformed image
+        psImage *transErr = transformedErrors->data[i]; // Transformed error image
+        if (!psFitsWriteImage(shiftFile, NULL, trans, 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", shiftName);
+        }
+        psTrace("stac", 1, "Shifted image written to %s\n", shiftName);
+        if (!psFitsWriteImage(errFile, NULL, transErr, 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", errName);
+        }
+        psTrace("stac", 1, "Shifted error image written to %s\n", errName);
+        psFitsClose(shiftFile);
+        psFitsClose(errFile);
+    }
+#endif
+
+    psVector *scales = NULL;            // Relative scales between images
+    psVector *offsets = NULL;           // Offsets between images
+    (void)stacScales(&scales, &offsets, transformed, config->starFile, config->starMapFile, config->xMapDiff,
+                     config->yMapDiff, config->aper);
+
+#ifdef TESTING
+    for (int i = 0; i < scales->n; i++) {
+        psTrace("stac", 3, "Image %d: scale %f, offset %f\n", i, scales->data.F32[i], offsets->data.F32[i]);
+    }
+#endif
+
+    // Rescale the images
+    (void)stacRescale(transformed, transformedErrors, NULL, scales, offsets);
+    // Set the saturation and bad values
+    psVector *saturated = psVectorAlloc(transformed->n, PS_TYPE_F32); // Saturation limits
+    psVector *bad = psVectorAlloc(transformed->n, PS_TYPE_F32); // Bad limits
+    float minSaturated = INFINITY;      // The minimum saturation level
+    float maxBad = -INFINITY;           // The maximum bad level
+    for (int i = 0; i < transformed->n; i++) {
+        saturated->data.F32[i] = (config->saturated - offsets->data.F32[i]) / scales->data.F32[i];
+        if (saturated->data.F32[i] < minSaturated) {
+            minSaturated = saturated->data.F32[i];
+        }
+        bad->data.F32[i] = (config->bad - offsets->data.F32[i]) / scales->data.F32[i];
+        if (bad->data.F32[i] > maxBad) {
+            maxBad = bad->data.F32[i];
+        }
+    }
+    psTrace("stac", 1, "Saturation level: %f\n", minSaturated);
+    psTrace("stac", 1, "Bad level: %f\n", maxBad);
+
+    // Save shifted images
+    if (config->saveShifts) {
+        psImage *image = NULL;          // Copy of image, to remove NAN for output
+        for (int i = 0; i < transformed->n; i++) {
+            char shiftName[MAXCHAR];    // Filename of shifted image
+            sprintf(shiftName, "%s.shift", (const char*)config->inputs->data[i]);
+            psFits *shiftFile = psFitsOpen(shiftName, "w");
+            image = psImageCopy(NULL, transformed->data[i], PS_TYPE_F32);
+            (void)psImageClipNaN(image, 0.0);
+            if (!psFitsWriteImage(shiftFile, headers->data[i], image, 0, NULL)) {
+                psErrorStackPrint(stderr, "Unable to write image: %s\n", shiftName);
+            }
+            psTrace("stac", 1, "Shifted image %d written to %s\n", i, shiftName);
+            psFitsClose(shiftFile);
+            psFree(image);
+        }
+    }
+
+    // Combine with rejection
+    psArray *rejected = NULL;
+    psImage *combined = NULL;
+    (void)stacCombine(&combined, &rejected, transformed, transformedErrors, config->nReject, NULL, saturated,
+                      bad, config->reject);
+
+    psTrace("stac.time",1,"First combination completed at %f seconds\n", getTime() - startTime);
+
+
+#ifdef TESTING
+    // Write rejection images out to check
+    for (int i = 0; i < rejected->n; i++) {
+        char rejName[MAXCHAR];  // Filename of rejection image
+        sprintf(rejName, "%s.shiftrej", (char*)config->inputs->data[i]);
+
+        psFits *rejFile = psFitsOpen(rejName, "w");
+        if (!psFitsWriteImage(rejFile, NULL, rejected->data[i], 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", rejName);
+        }
+        psTrace("stac", 1, "Rejection image written to %s\n", rejName);
+        psFitsClose(rejFile);
+    }
+
+    // Write out pre-combined image
+    char preName[MAXCHAR];              // Filename of precombined image
+    sprintf(preName, "%s.pre", config->output);
+    psFits *preFile = psFitsOpen(preName, "w");
+    if (!psFitsWriteImage(preFile, NULL, combined, 0, NULL)) {
+        psErrorStackPrint(stderr, "Unable to write image: %s\n", preName);
+    }
+    psTrace("stac", 1, "Pre-combined image written to %s\n", preName);
+    psFitsClose(preFile);
+#endif
+
+    // Get regions of interest in the source frame
+    psArray *regions = psArrayAlloc(inputs->n); // Array of images denoting regions of interest
+    for (int i = 0; i < inputs->n; i++) {
+        regions->data[i] = stacAreaOfInterest(rejected->data[i], inverseMaps->data[i],
+                                              ((psImage*)(inputs->data[i]))->numCols,
+                                              ((psImage*)(inputs->data[i]))->numRows);
+#ifdef TESTING
+        char regionName[MAXCHAR];       // Filename of region image
+        sprintf(regionName,"%s.region",(char*)config->inputs->data[i]);
+        psFits *regionFile = psFitsOpen(regionName, "w");
+        if (!psFitsWriteImage(regionFile, NULL, regions->data[i], 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", regionName);
+        }
+        psTrace("stac", 1, "Region image written to %s\n", regionName);
+        psFitsClose(regionFile);
+#endif
+    }
+
+    // Transform rejected pixels to source frame
+    psArray *rejectedSource = stacRejection(inputs, rejected, regions, maps, inverseMaps, config->frac,
+                                            config->grad, config->inputs);
+
+    // Get regions of interest in the output frame
+    psImage *combineRegion = psImageAlloc(config->outnx, config->outny, PS_TYPE_U8);
+    psImageInit(combineRegion, 0);
+    for (int i = 0; i < inputs->n; i++) {
+        psImage *region = stacAreaOfInterest(rejectedSource->data[i], maps->data[i], config->outnx,
+                                             config->outny);
+        psBinaryOp(combineRegion, combineRegion, "+", region);
+        psFree(region);
+
+        // Do OR of masks
+        psImage *mask = masks->data[i]; // Mask of input image (bad columns etc)
+        psImage *cr = rejectedSource->data[i]; // Mask of CRs
+        for (int y = 0; y < mask->numRows; y++) {
+            for (int x = 0; x < mask->numCols; x++) {
+                if (cr->data.U8[y][x] > 0) {
+                    mask->data.U8[y][x] = 1;
+                }
+            }
+        }
+    }
+
+    // Redo transformation with the masks and scales/offsets
+    (void)stacTransform(&transformed, &transformedErrors, inputs, inverseMaps, errors, masks,
+                        combineRegion, scales, offsets, config->outnx, config->outny);
+
+#ifdef TESTING
+    // Write transformed images out to check
+    for (int i = 0; i < inputs->n; i++) {
+        char shiftName[MAXCHAR];        // Filename of shift image
+        char errName[MAXCHAR];          // Filename of error image
+        sprintf(shiftName,"%s.shift2", (char*)config->inputs->data[i]);
+        sprintf(errName,"%s.shifterr2",(char*)config->inputs->data[i]);
+        psFits *shiftFile = psFitsOpen(shiftName, "w");
+        psFits *errFile = psFitsOpen(errName, "w");
+        psImage *trans = transformed->data[i]; // Transformed image
+        psImage *transErr = transformedErrors->data[i]; // Transformed error image
+        if (!psFitsWriteImage(shiftFile, NULL, trans, 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", shiftName);
+        }
+        psTrace("stac", 1, "Shifted image written to %s\n", shiftName);
+        if (!psFitsWriteImage(errFile, NULL, transErr, 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", errName);
+        }
+        psTrace("stac", 1, "Shifted error image written to %s\n", errName);
+        psFitsClose(shiftFile);
+        psFitsClose(errFile);
+    }
+#endif
+
+    // Combine the newly-transformed CR-free images, no rejection
+    psFree(rejected);
+    rejected = NULL;
+    (void)stacCombine(&combined, &rejected, transformed, transformedErrors, 0, combineRegion, saturated,
+                      bad, config->reject);
+
+    // Write output image
+    psFits *outFile = psFitsOpen(config->output, "w");
+    int numPix = psImageClipNaN(combined, 0.0);
+    if (numPix > 0) {
+        psTrace("stac", 3, "Clipping %d NaN pixels to zero.\n", numPix);
+    }
+    if (!psFitsWriteImage(outFile, headers->data[0], combined, 0, NULL)) {
+        psErrorStackPrint(stderr, "Unable to write image: %s\n", config->output);
+    }
+    psTrace("stac", 1, "Combined image written to %s\n", config->output);
+    psFitsClose(outFile);
+
+    // Free everything I've used
+    stacConfigFree(config);
+    psFree(scales);
+    psFree(offsets);
+    psFree(combineRegion);
+    psFree(regions);
+    psFree(inputs);
+    psFree(headers);
+    psFree(maps);
+    psFree(inverseMaps);
+    psFree(errors);
+    psFree(masks);
+    psFree(transformedErrors);
+    psFree(transformed);
+    psFree(rejectedSource);
+    psFree(combined);
+    psFree(saturated);
+    psFree(bad);
+
+    psTrace("stac.time",1,"Final combination completed at %f seconds\n", getTime() - startTime);
+
+#ifdef TESTING
+    // Check memory for leaks, corruption
+    stacCheckMemory();
+#endif
+
+    exit(EXIT_SUCCESS);
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/stac.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stac.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stac.h	(revision 22322)
@@ -0,0 +1,210 @@
+#ifndef STAC_H
+#define STAC_H
+
+#include "pslib.h"
+#define abs(x) ((x) >= 0 ? (x) : (-(x)))
+#define MAXCHAR 80
+
+/************************************************************************************************************/
+
+// time.c
+
+// Get the current time
+double getTime(void);
+
+/************************************************************************************************************/
+
+// stacRead.c
+
+// Read the input files and return an array of images
+psArray *stacReadImages(psArray **headers, // The image headers, to be returned
+                        psArray *filenames // The file names of the images
+    );
+
+// Read a file of coordinates (x,y)
+psArray *stacReadCoords(const char *filename);
+
+// Read a single map file and return the transformation
+psPlaneTransform *stacReadMap(const char *filename);
+
+// Read the map files and return an array of transformations
+psArray *stacReadMaps(psArray *filenames // The file names of the images whose maps are to be read
+    );
+
+/************************************************************************************************************/
+
+// stacErrorImages.c
+
+// Calculate the error images
+psArray *stacErrorImages(psArray *inputs, // Array of input images
+                         float gain,    // Gain, in e/ADU
+                         float rn       // Read noise, in e
+    );
+
+/************************************************************************************************************/
+
+// stacTransform.c
+
+// Transform input images, return success
+bool stacTransform(psArray **outputs,   // Transformed images for output
+                   psArray **outErrors, // Transformed error images for output
+                   const psArray *images, // Array of images to be transformed
+                   const psArray *maps, // Array of polynomials that do the transformation
+                   const psArray *errors, // Array of error images to be transformed
+                   const psArray *masks, // Masks of input images
+                   const psImage *region, // Region of interest for transformation
+                   const psVector *scales, // Relative scales
+                   const psVector *offsets, // Relative offsets
+                   int outnx, int outny // Size of output images
+    );
+
+/************************************************************************************************************/
+
+// stacCheckMemory.c
+
+// Check memory
+void stacCheckMemory(void);
+
+// Print out the problem
+void stacMemoryProblem(const psMemBlock* ptr, ///< the pointer to the problematic memory block.
+                       const char *file, ///< the file in which the problem originated
+                       psS32 lineno     ///< the line number in which the problem originated
+    );
+
+// Print out a memblock when it's allocated --- this function used as a callback
+psMemId stacMemPrint(const psMemBlock *ptr);
+
+
+/************************************************************************************************************/
+
+// stacCombine.c
+
+// Get the mean for a bunch of values
+float stacCombineMean(psVector *values, // Values for which to take the mean
+                      psVector *errors, // Errors in the values
+                      psVector *masks   // Masks for the values, 0 = don't use, 1 = use
+    );
+
+// Get the median for a bunch of values
+float stacCombineMedian(psVector *values, // Values for which to take the median
+                        psVector *errors, // Errors in the values
+                        psVector *masks // Masks for the values, 0 = don't use, 1 = use
+    );
+
+// Combine the transformed images
+bool stacCombine(psImage **combined,    // The combined image for output
+                 psArray **rejected,    // Array of rejection masks
+                 psArray *images,       // Array of transformed images
+                 psArray *errors,       // Array of transformed error images
+                 int nReject,           // Number of rejection iterations
+                 psImage *region,       // Region to combine
+                 psVector *saturated,   // Saturation limits for each image
+                 psVector *bad,         // Bad pixel limits for each image
+                 float reject           // Rejection (k-sigma)
+    );
+
+/************************************************************************************************************/
+
+// stacInvertMaps.c
+
+// Invert an array of maps
+psArray *stacInvertMaps(const psArray *maps, // Array of maps to invert
+                        int outnx, int outny // Size of output image
+    );
+
+/************************************************************************************************************/
+
+// stacRejection.c
+
+// Return the relative gradient for a given pixel
+float stacGradient(psImage *image,      // Input for which to measure the gradient
+                   int x, int y         // Coordinates at which to measure the gradient
+    );
+
+// Transform the rejection masks back to the source frame
+psArray *stacRejection(psArray *inputs, // Input images
+                       psArray *rejected, // Rejected images
+                       psArray *regions, // Regions of interest
+                       psArray *maps,   // Maps from input to transformed image
+                       psArray *inverseMaps, // Maps from transformed to input image
+                       float frac,      // Fraction of pixel rejected before looking more carefully
+                       float grad,      // Gradient limit for rejection
+                       psArray *names   // Names of original images (only for writing out when TESTING)
+    );
+
+/************************************************************************************************************/
+
+// stacAreaOfInterest.c
+
+// Returns the change in x' and y' for a change in x and y of 1
+bool stacPlaneTransformDeriv(psPlane *out, // Output derivative, assumed already allocated
+                             psPlaneTransform *transform, // The transform for which to obtain the derivative
+                             psPlane *in // The position at which to obtain the derivative
+    );
+
+// Return a mask image designating the region of interest in the source frame
+// Basically, uses derivatives of the transformation to work out what pixels in the source might be of interest
+psImage *stacAreaOfInterest(psImage *mask, // Mask that is to be used to determine the area of interest
+                            psPlaneTransform *map, // Map from the mask to the area of interest
+                            int nxOut, int nyOut // Size of the area of interest
+    );
+
+/************************************************************************************************************/
+
+// stacSize.c
+
+// Calculate the size of the output image
+bool stacSize(int *outnx, int *outny,   // Size of output image (returned)
+              float *xMapDiff, float *yMapDiff, // Difference applied to maps
+              const psVector *xSizes,   // Sizes of images in x
+              const psVector *ySizes,   // Sizes of images in y
+              psArray *maps             // Transformation maps
+    );
+
+/************************************************************************************************************/
+
+// stacScale.c
+
+// Calculate the background in an image
+float stacBackground(const psImage *image, // Image for which to get the background
+                     int sample         // Sample in increments of this value
+    );
+
+
+// Calculate the relative scales and offsets between the images
+bool stacScales(psVector **scalesPtr,   // Scales to return
+                psVector **offsetsPtr,  // Offsets to return
+                const psArray *images,  // Images on which to measure the scales and offsets
+                const char *starFile,   // File containing coordinates to photometer
+                const char *starMapFile, // Map for coodinates to the common output frame
+                float xMapDiff, float yMapDiff, // Difference from the map to apply (due to size difference)
+                float aper              // Aperture to use for photometry (radius)
+    );
+
+// Rescale images
+bool stacRescale(psArray *images,       // Images to rescale
+                 psArray *errImages,    // Variance images to rescale
+                 const psImage *mask,   // Mask indicating which pixels to scale
+                 const psVector *scales,// Scales for images
+                 const psVector *offsets // Offsets for images
+    );
+
+
+
+/************************************************************************************************************/
+
+// stacWrite.c
+
+// Write map out
+bool stacWriteMap(const char *mapName,  // Filename to write to
+                  psPlaneTransform *map // Map to write
+    );
+
+// Write multiple maps
+bool stacWriteMaps(const psArray *names, // Filenames of the input images (will add ".map")
+                   const psArray *maps  // Maps to write
+    );
+
+/************************************************************************************************************/
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacAreaOfInterest.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacAreaOfInterest.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacAreaOfInterest.c	(revision 22322)
@@ -0,0 +1,122 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "stac.h"
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+// Returns the change in x' and y' for a change in x and y of 1
+bool stacPlaneTransformDeriv(psPlane *out, // Output derivative, assumed already allocated
+			     psPlaneTransform *transform, // The transform for which to obtain the derivative
+			     psPlane *in // The position at which to obtain the derivative
+    )
+{
+    psPolynomial2D *xPoly = transform->x;
+    psPolynomial2D *yPoly = transform->y;
+
+    assert(xPoly->type == PS_POLYNOMIAL_ORD);
+    assert(yPoly->type == PS_POLYNOMIAL_ORD);
+
+    // Initialise derivatives
+    out->x = 0.0;
+    out->y = 0.0;
+
+    // Do x component
+    double xSum = 1.0 / in->x;		// 1/x in order to calculate x^(i-1)
+    for (int xOrder = 0; xOrder < xPoly->nX; xOrder++) {
+        double ySum = xSum / in->y;	// 1/y in order to calculate y^(j-1)
+        for (int yOrder = 0; yOrder < xPoly->nY; yOrder++) {
+            if (xPoly->mask[xOrder][yOrder] == 0) {
+		out->x += xPoly->coeff[xOrder][yOrder] * (double)xOrder * ySum * in->y;	// a_ij i x^(i-1) y^j
+		out->x += xPoly->coeff[xOrder][yOrder] * (double)yOrder * ySum * in->x; // a_ij j x^i y^(j-1)
+            }
+	    ySum *= in->y;
+        }
+	xSum *= in->x;
+    }
+
+    // Do y component
+    xSum = 1.0 / in->x;			// 1/x in order to calculate x^(i-1)
+    for (int xOrder = 0; xOrder < yPoly->nX; xOrder++) {
+        double ySum = xSum / in->y;	// 1/y in order to calculate y^(j-1)
+        for (int yOrder = 0; yOrder < yPoly->nY; yOrder++) {
+            if (yPoly->mask[xOrder][yOrder] == 0) {
+		out->y += yPoly->coeff[xOrder][yOrder] * (float)xOrder * ySum * in->y;	// a_ij i x^(i-1) y^j
+		out->y += yPoly->coeff[xOrder][yOrder] * (float)yOrder * ySum * in->x; // a_ij j x^i y^(j-1)
+            }
+	    ySum *= in->y;
+        }
+	xSum *= in->x;
+    }
+
+    return true;
+}
+
+
+
+// Calculate 
+psImage *stacAreaOfInterest(psImage *mask, // Mask that is to be used to determine the area of interest
+			    psPlaneTransform *map, // Map from the mask to the area of interest
+			    int nxOut, int nyOut // Size of the area of interest
+    )
+{
+    // Input checks
+    assert(mask);
+    assert(map);
+    assert(mask->type.type == PS_TYPE_U8);
+    
+    // Number of pixels in x and y
+    int nxIn = mask->numCols;
+    int nyIn = mask->numRows;
+
+    // Create and zero output image
+    psImage *output = psImageAlloc(nxOut, nyOut, PS_TYPE_U8);
+    for (int y = 0; y < nyOut; y++) {
+	for (int x = 0; x < nxOut; x++) {
+	    output->data.U8[y][x] = 0;
+	}
+    }
+
+    psTrace("stac.area", 3, "Finding area of interest....\n");
+
+
+    // Coordinates for the transformations
+    psPlane *inFrame = psAlloc(sizeof(psPlane));
+    psPlane *outFrame = psAlloc(sizeof(psPlane));
+    psPlane *deriv = psAlloc(sizeof(psPlane));
+
+    // Find pixels on the mask that are of interest
+    for (int y = 0; y < nyIn; y++) {
+	for (int x = 0; x < nxIn; x++) {
+	    if (mask->data.U8[y][x]) {
+		inFrame->x = (double)x + 0.5;
+		inFrame->y = (double)y + 0.5;
+		(void)psPlaneTransformApply(outFrame, map, inFrame);
+		(void)stacPlaneTransformDeriv(deriv, map, inFrame);
+
+		// Calculate range
+		int iMin = MAX((int)(outFrame->x - 0.5*deriv->x), 0);
+		int iMax = MIN((int)(outFrame->x + 0.5*deriv->x + 0.5), nxOut - 1);
+		int jMin = MAX((int)(outFrame->y - 0.5*deriv->y), 0);
+		int jMax = MIN((int)(outFrame->y + 0.5*deriv->y + 0.5), nyOut - 1);
+
+		for (int j = jMin; j <= jMax; j++) {
+		    for (int i = iMin; i <= iMax; i++) {
+			output->data.U8[j][i] = 1;
+		    }
+		}
+	    } // Pixel of interest
+	}
+    } // Iterating over input mask
+
+    psFree(inFrame);
+    psFree(outFrame);
+    psFree(deriv);
+
+    return output;
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacCheckMemory.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacCheckMemory.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacCheckMemory.c	(revision 22322)
@@ -0,0 +1,67 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "stac.h"
+
+
+#define LEAKS "leaks.dat"               // File to which to write leaks data
+
+
+psMemId stacMemPrint(const psMemBlock *ptr)
+{
+    psLogMsg("stac.memoryPrint", PS_LOG_INFO,
+             "Memory block %lu:\n"
+             "\tFile %s, line %d, size %zd\n"
+             "\tPosts: %p %p %p\n",
+             ptr->id, ptr->file, ptr->lineno, ptr->userMemorySize, ptr->startblock, ptr->endblock,
+             *(void**)((int8_t *)(ptr + 1) + ptr->userMemorySize));
+    return 0;
+}
+
+
+void stacMemoryProblem(const psMemBlock* ptr, ///< the pointer to the problematic memory block.
+                       const char *file, ///< the file in which the problem originated
+                       psS32 lineno     ///< the line number in which the problem originated
+    )
+{
+    psLogMsg("stac.checkMemory.corruption", PS_LOG_WARN,
+             "Memory corruption detected in memBlock %lu\n"
+             "\tFile %s, line %d, size %zd\n"
+             "\tPosts: %p %p %p\n",
+             ptr->id, file, lineno, ptr->userMemorySize, ptr->startblock, ptr->endblock,
+             (ptr + 1 + ptr->userMemorySize));
+}
+
+
+
+void stacCheckMemory(void)
+{
+    psMemBlock **leaks = NULL;          // List of leaks
+    FILE *leakFile;                     // File to write leaks to
+
+    psTrace("stac.checkMemory", 1, "Checking for memory problems....\n");
+
+    (void)psMemProblemCallbackSet((psMemProblemCallback)stacMemoryProblem); // Set callback for corruption
+
+    if ((leakFile = fopen(LEAKS, "w")) == NULL) {
+        fprintf(stderr, "Unable to open leaks file, %s\n", LEAKS);
+        return;
+    }
+
+    int nLeaks = psMemCheckLeaks(0, &leaks, leakFile, false); // Number of leaks
+    psTrace("stac.checkMemory", 1, "%d leaks found.\n", nLeaks);
+    for (int i = 0; i < nLeaks; i++) {
+        psLogMsg("stac.checkMemory.leaks", PS_LOG_WARN,
+                 "Memory leak detection: memBlock %lu\n"
+                 "\tFile %s, line %d, size %zd\n",
+                 leaks[i]->id, leaks[i]->file, leaks[i]->lineno, leaks[i]->userMemorySize);
+    }
+
+    int nCorrupted;                     // Number of corrupted memory blocks
+    nCorrupted = psMemCheckCorruption(false);
+    psTrace("stac.checkMemory", 1, "%d memory blocks corrupted.\n", nCorrupted);
+
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacCombine.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacCombine.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacCombine.c	(revision 22322)
@@ -0,0 +1,223 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+#include "pslib.h"
+#include "stac.h"
+
+#define SQUARE(x) ((x)*(x))
+#define ABS(x) ((x) >= 0 ? (x) : -(x))
+
+float stacCombineMean(psVector *values, // Values for which to take the mean
+                      psVector *errors, // Errors in the values
+                      psVector *masks   // Masks for the values, 1 = don't use, 0 = use
+    )
+{
+#if 0
+    // Would like to use psVectorStats, but it doesn't have errors built in yet
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    (void)psVectorStats(stats, values, NULL, masks, 1);
+    float mean = stats->sampleMean;
+    psFree(stats);
+    return mean;
+#else
+    // Instead, do it ourselves
+    double sum = 0.0;
+    double weights = 0.0;
+    int num = values->n;
+    for (int i = 0; i < num; i++) {
+        if (! masks->data.U8[i]) {
+            // "error" here is the variance
+            sum += values->data.F32[i] / errors->data.F32[i];
+            weights += 1.0 / errors->data.F32[i];
+        }
+    }
+    if (weights > 0.0) {
+        return (float)(sum/weights);
+    } else {
+        return NAN;
+    }
+#endif
+
+}
+
+
+float stacCombineMedian(psVector *values, // Values for which to take the median
+                        psVector *errors, // Errors in the values
+                        psVector *masks // Masks for the values, 0 = don't use, 1 = use
+    )
+{
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    (void)psVectorStats(stats, values, NULL, masks, 1);
+    float median = stats->sampleMedian;
+    psFree(stats);
+    return median;
+}
+
+
+
+bool stacCombine(psImage **combined,    // The combined image for output
+                 psArray **rejected,    // Array of rejection masks
+                 psArray *images,       // Array of transformed images
+                 psArray *errors,       // Array of transformed error images
+                 int nReject,           // Number of rejection iterations
+                 psImage *region,       // Region to combine
+                 psVector *saturated,   // Saturation limits for each image
+                 psVector *bad,         // Bad pixel limits for each image
+                 float reject           // Rejection (k-sigma)
+    )
+{
+    assert(combined);
+    assert(images);
+    assert(errors);
+    assert(images->n == errors->n);
+    assert(saturated);
+    assert(bad);
+    assert(saturated->n == images->n);
+    assert(bad->n == images->n);
+
+    int nImages = images->n;            // Number of images
+    int numRows = ((psImage*)images->data[0])->numRows; // Image size
+    int numCols = ((psImage*)images->data[0])->numCols; // Image size
+
+    // Check dimensions for consistency
+    for (int i = 0; i < nImages; i++) {
+        psImage *image = (psImage *)images->data[i]; // The image
+        psImage *error = (psImage *)errors->data[i]; // The error image
+
+        assert(image->numCols == numCols && image->numRows == numRows);
+        assert(error->numCols == numCols && error->numRows == numRows);
+    }
+
+    // Check combined image
+    assert(!*combined || ((*combined)->numRows == numRows && (*combined)->numCols == numCols));
+    if (*combined == NULL) {
+        *combined = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Combined image
+    }
+
+    // Check area of interest
+    assert(!region || (region->numRows == numRows && region->numCols == numCols));
+
+    psTrace("stac.combine", 1, "Combining images....\n");
+
+    psVector *pixels = psVectorAlloc(nImages, PS_TYPE_F32); // Will hold the pixels in the statistics step
+    psVector *deltas = psVectorAlloc(nImages, PS_TYPE_F32); // Will hold the errors in the statistics step
+    psVector *mask = psVectorAlloc(nImages, PS_TYPE_U8); // Will hold the mask in the statistics step
+
+    // Set up rejection masks
+    if (nReject > 0) {
+        if (*rejected == NULL) {
+            // Allocate the rejection masks, if required
+            *rejected = psArrayAlloc(nImages);
+        } else {
+            assert((*rejected)->n != nImages);
+        }
+
+        // Create and initialise rejection masks
+        for (int i = 0; i < nImages; i++) {
+            (*rejected)->data[i] = psImageAlloc(numCols, numRows, PS_TYPE_U8);
+            for (int r = 0; r < numRows; r++) {
+                for (int c = 0; c < numCols; c++) {
+                    ((psImage*)((*rejected)->data[i]))->data.U8[r][c] = 0;
+                }
+            }
+        }
+    }
+
+#ifdef TESTING
+    // chi^2 image
+    psImage *chi2 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    static int iteration = 0;           // Number of times function has been called
+#endif
+
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+
+            // Only combine those pixels requested
+            if (!region || (region && region->data.U8[y][x])) {
+
+                // Export pixels into the vector and get stats
+                for (int i = 0; i < nImages; i++) {
+                    float pixel = ((psImage*)images->data[i])->data.F32[y][x];
+                    float delta = ((psImage*)errors->data[i])->data.F32[y][x]; // This is the variance!
+                    pixels->data.F32[i] = pixel;
+                    deltas->data.F32[i] = delta;
+                    if ((pixel >= saturated->data.F32[i]) || (pixel <= bad->data.F32[i]) ||
+                        (! isfinite(pixel)) || (! isfinite(delta))) {
+                        mask->data.U8[i] = (psU8)1; // Don't use!
+                    } else {
+                        mask->data.U8[i] = (psU8)0; // Use.
+                    }
+                }
+
+                float average = stacCombineMean(pixels, deltas, mask); // Combined value
+
+                // We set the value BEFORE the rejection iteration because not all pixels that we reject
+                // here will be rejected in the final cut.
+                (*combined)->data.F32[y][x] = average;
+
+#ifdef TESTING
+                // Calculate chi^2
+                chi2->data.F32[y][x] = 0.0;
+                int numGoodPix = 0;
+                for (int i = 0; i < nImages; i++) {
+                    if (! mask->data.U8[i]) {
+                        chi2->data.F32[y][x] += SQUARE(pixels->data.F32[i] - average) / deltas->data.F32[i];
+                        numGoodPix++;
+                    }
+                }
+                chi2->data.F32[y][x] /= (float)numGoodPix;
+#endif
+
+                // Rejection iterations
+                bool keepGoing = true;  // Keep going with rejection?
+                for (int rejNum = 0; (rejNum < nReject) && keepGoing; rejNum++) {
+                    float max = 0.0;    // Maximum deviation
+                    int maxIndex = -1;  // Index of the maximum deviation
+                    for (int i = 0; i < nImages; i++) {
+                        if (!mask->data.U8[i] &&
+                            ((pixels->data.F32[i] - average) / sqrtf(deltas->data.F32[i]) > max)) {
+                            max = (pixels->data.F32[i] - average) / sqrtf(deltas->data.F32[i]);
+                            maxIndex = i;
+                        }
+                    }
+                    // Reject the pixel with the maximum deviation
+                    if (max > reject) {
+                        mask->data.U8[maxIndex] = 1;
+                        ((psImage*)((*rejected)->data[maxIndex]))->data.U8[y][x] += 1;
+                        // Re-do combination following rejection
+                        average = stacCombineMean(pixels, deltas, mask);
+                    } else {
+                        keepGoing = false;
+                    }
+                }
+
+            } // Pixels of interest
+
+        }
+    } // Iterating over output pixels
+
+#ifdef TESTING
+    // Write chi^2 image
+    iteration++;
+    char chiName[MAXCHAR];              // Filename of chi^2 image
+    sprintf(chiName,"chi2_%d.fits",iteration);
+    psFits *chiFile = psFitsOpen(chiName, "w");
+    if (!psFitsWriteImage(chiFile, NULL, chi2 , 0, NULL)) {
+        psErrorStackPrint(stderr, "Unable to write image: %s\n", chiName);
+    }
+    psTrace("stac.combine", 1, "Chi^2 image written to %s\n", chiName);
+    psFitsClose(chiFile);
+    psFree(chi2);
+#endif
+
+    // Clean up
+    psFree(pixels);
+    psFree(deltas);
+    psFree(mask);
+
+    return combined;
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacConfig.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacConfig.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacConfig.c	(revision 22322)
@@ -0,0 +1,181 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include "pslib.h"
+#include "stac.h"
+#include "stacConfig.h"
+
+stacConfig *stacConfigAlloc(void)
+{
+    // Allocate memory
+    stacConfig *config = (stacConfig*) psAlloc(sizeof(stacConfig));
+    // Set defaults
+    config->verbose = 0;
+    config->gain = 1.0;
+    config->readnoise = 0.0;
+    config->inputs = NULL;
+    config->output = NULL;
+    config->saveShifts = false;
+    config->starFile = NULL;
+    config->starMapFile = NULL;
+    config->aper = 3.0;
+    config->outnx = 0;
+    config->outny = 0;
+    config->saturated = 65535.0;        // Saturation level
+    config->bad = 0.0;                  // Bad level
+    config->reject = 2.5;
+    config->frac = 0.45;
+    config->grad = 0.7;
+    config->nReject = 2;
+
+    return config;
+}
+
+
+void stacConfigFree(stacConfig *config)
+{
+    // Free the vectors, if necessary
+    if (config->inputs) {
+        psFree(config->inputs);
+    }
+    // Free everything
+    psFree(config);
+}
+
+
+stacConfig *stacParseConfig(int argc,   // Number of command-line arguments
+                            char **argv // Command-line arguments
+    )
+{
+    stacConfig *config = stacConfigAlloc(); // Configuration values
+    const char *programName = argv[0];  // Program name
+
+    /* Variables for getopt */
+    int opt;   /* Option, from getopt */
+    extern char *optarg;   /* Argument accompanying switch */
+    extern int optind;   /* getopt variables */
+
+    /* Parse command-line arguments using getopt */
+    while ((opt = getopt(argc, argv, "hvSg:r:o:s:b:k:n:f:G:p:a:")) != -1) {
+        switch (opt) {
+          case 'h':
+            help(programName);
+            exit(EXIT_SUCCESS);
+          case 'v':
+            config->verbose++;
+            break;
+          case 'g':
+            if (sscanf(optarg, "%f", &config->gain) != 1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'r':
+            if (sscanf(optarg, "%f", &config->readnoise) != 1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'o':
+            if ((sscanf(argv[optind-1], "%d", &config->outnx) != 1) ||
+                (sscanf(argv[optind++], "%d", &config->outny) != 1)) {
+                // Note: incrementing optind, so I can read more than one parameter.
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 's':
+            if (sscanf(optarg, "%f", &config->saturated) != 1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'b':
+            if (sscanf(optarg, "%f", &config->bad) != 1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'p':
+            if (argc < optind+1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            config->starFile = argv[optind-1];
+            config->starMapFile = argv[optind++];
+            // Note: incrementing optind, so I can read more than one parameter.
+            break;
+          case 'a':
+            if (sscanf(optarg, "%f", &config->aper) != 1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'k':
+            if (sscanf(optarg, "%f", &config->reject) != 1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'n':
+            if (sscanf(optarg, "%d", &config->nReject) != 1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'f':
+            if (sscanf(optarg, "%f", &config->frac) != 1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'G':
+            if (sscanf(optarg, "%f", &config->grad) != 1) {
+                help(programName);
+                exit(EXIT_FAILURE);
+            }
+            break;
+          case 'S':
+            config->saveShifts = true;
+            break;
+          default:
+            help(programName);
+        }
+    }
+
+    /* Get the remaining, mandatory values */
+    argc -= optind;
+    argv += optind;
+    if (argc < 2) {
+        help(programName);
+        exit(EXIT_FAILURE);
+    }
+
+    config->output = argv[0];           // Output file
+    // Get the input files
+    config->inputs = psArrayAlloc(argc - 1);
+    config->inputs->n = argc - 1;
+    for (int i = 1; i < argc; i++) {
+        config->inputs->data[i-1] = psAlloc(strlen(argv[i]));
+        strncpy(config->inputs->data[i-1], argv[i], strlen(argv[i]));
+    }
+
+    // Debugging output
+    psTrace("stac.config", 8, "Parsed command line for configuration\n");
+    psTrace("stac.config", 9, "Verbosity level: %d\n",config->verbose);
+    psTrace("stac.config", 9, "%ld inputs:\n",config->inputs->n);
+    for (int i = 0; i < config->inputs->n; i++) {
+        psTrace("stac.config", 9, "\t%s\n", (char *)config->inputs->data[i]);
+    }
+    psTrace("stac.config", 9, "Output file is %s\n",config->output);
+    psTrace("stac.config", 9, "Output file size is %dx%d\n", config->outnx, config->outny);
+
+    return config;
+}
+
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacConfig.h
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacConfig.h	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacConfig.h	(revision 22322)
@@ -0,0 +1,43 @@
+#ifndef STAC_CONFIG_H
+#define STAC_CONFIG_H
+
+#include "pslib.h"
+
+// stacConfig.c
+
+// Configuration options
+typedef struct {
+    int verbose;			// Verbosity level
+    float gain, readnoise;		// Gain and readnoise for detectors
+    psArray *inputs;			// Input file names
+    const char *output;			// Output file name
+    bool saveShifts;			// Save shifted images?
+    const char *starFile;		// File with star coordinates
+    const char *starMapFile;		// File with map for stars
+    int outnx, outny;			// Size of output image
+    float xOffset, yOffset;		// Offset to get lower-left corner at 0,0
+    float saturated;			// Saturation level
+    float bad;				// Bad level
+    float reject;			// Rejection level
+    float frac;				// Fraction of input pixel that must be masked before the pixel is
+					// considered bad
+    float grad;				// Multiplier of the gradient
+    int nReject;			// Number of rejection iterations
+    float xMapDiff, yMapDiff;		// Difference between pure map and output image coordinates
+    float aper;				// Aperture size (pixels)
+} stacConfig;
+
+// Allocator
+stacConfig *stacConfigAlloc(void);
+// Deallocator
+void stacConfigFree(stacConfig *config);
+
+// Help message
+void help(const char *name);
+
+// Parse the command line and return config
+stacConfig *stacParseConfig(int argc,	// Number of command-line arguments
+			    char **argv // Command-line arguments
+    );
+
+#endif
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacErrorImages.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacErrorImages.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacErrorImages.c	(revision 22322)
@@ -0,0 +1,42 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "stac.h"
+
+psArray *stacErrorImages(psArray *inputs, // Array of input images
+                         float gain,    // Gain, in e/ADU
+                         float rn       // Read noise, in e
+    )
+{
+    float invGain = 1.0/gain;           // Inverse square root of gain
+    rn /= gain;                         // Read noise in ADU
+    psArray *errors = psArrayAlloc(inputs->n);
+
+    psTrace("stac.errors", 1, "Calculating error images....\n");
+
+    // Iterate over the input images
+    for (int i = 0; i < inputs->n; i++) {
+        psTrace("stac.errors",5,"Working on image #%d\n",i);
+
+        psImage *image = inputs->data[i]; // Pull out the image of interest
+        int numRows = image->numRows;   // Number of rows
+        int numCols = image->numCols;   // Number of columns
+        psImage *error = psImageAlloc(numCols, numRows, PS_TYPE_F32); // The error image
+
+        // Iterate over the pixels
+        for (int r = 0; r < numRows; r++) {
+            for (int c = 0; c < numCols; c++) {
+                // We actually calculate the variance
+                error->data.F32[r][c] = image->data.F32[r][c]*invGain + rn*rn;
+            }
+        }
+
+        // Put image onto the array
+        errors->data[i] = error;
+    }
+
+    return errors;
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacHelp.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacHelp.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacHelp.c	(revision 22322)
@@ -0,0 +1,31 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+void help(const char *name)
+{
+    fprintf (stderr, "STAC: Simultaneous Telescope Array Combination\n"
+	     "Usage: %s [-h] [-v] [-g GAIN] [-r RN] [-o NX NY] [-S] [-s SAT] [-b BAD] [-p FILE MAP] [-a APER] [-k REJ] [-n NREJECT] [-k FRAC] [-G GRAD] OUT IN1 IN2...\n"
+	     "where\n"
+	     "\t-h           Help (this info)\n"
+	     "\t-v           Increase verbosity level\n"
+	     "\t-g           Gain (e/ADU) for detectors\n"
+	     "\t-r           Read noise (e) for detectors\n"
+	     "\t-o NX NY     Size of output image (512, 512)\n"
+	     "\t-S           Save shifted images (false)\n"
+	     "\t-s SAT       Saturation level (65535)\n"
+	     "\t-b BAD       Bad level (0)\n"
+	     "\t-p FILE MAP  Specify file containing star coordinates, with map\n"
+	     "\t-a APER      Aperture size to use for photometry (3 pix)\n"
+	     "\t-k REJ       Rejection level (k-sigma; 3.5)\n"
+	     "\t-n NREJECT   Number of rejection iterations (2)\n"
+	     "\t-f FRAC      Fraction of pixel to be marked before considered bad (0.5)\n"
+	     "\t-d GRAD      Gradient threshold for pixel to be marked (0.0)\n"
+	     "\tOUT          Output image\n"
+	     "\tIN1, IN2...  Input images, which have associated .map files.\n",
+	     name
+	);
+}
+
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacInvertMaps.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacInvertMaps.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacInvertMaps.c	(revision 22322)
@@ -0,0 +1,207 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "stac.h"
+
+
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define NUM_GRID 20
+
+psArray *stacInvertMaps(const psArray *maps, // Array of maps to invert
+                        int outnx, int outny // Size of output image
+    )
+{
+    int nMaps = maps->n;                // Number of maps
+    psArray *inverted = psArrayAlloc(nMaps); // Array of inverted maps for output
+
+    psTrace("stac.invertMaps", 1, "Inverting maps....\n");
+
+    // Coordinates for the transformations
+    psPlane *inCoord = psAlloc(sizeof(psPlane));
+    psPlane *outCoord = psAlloc(sizeof(psPlane));
+
+    for (int mapNum = 0; mapNum < nMaps; mapNum++) {
+
+        psPlaneTransform *oldMap = (psPlaneTransform*)maps->data[mapNum]; // Uninverted map
+        // Check input
+        assert(oldMap->x->nX == oldMap->x->nY && oldMap->y->nX == oldMap->y->nY &&
+               oldMap->x->nX == oldMap->y->nX);
+        int order = oldMap->x->nX;      // Polynomial order
+        psTrace("stac.invertMaps", 4, "Generating order %d polynomial inverse transformation.\n", order);
+        psPlaneTransform *newMap = psPlaneTransformAlloc(order, order); // Inverted map
+
+        // Create fake polynomial to use in evaluation
+        psPolynomial2D *fakePoly = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, order, order);
+        for (int i = 0; i <= order; i++) {
+            for (int j = 0; j <= order; j++) {
+                fakePoly->coeff[i][j] = 1.0; // Set all coeffecients to 1
+                fakePoly->mask[i][j] = 1; // Mask all coefficients; unmask to evaluate
+            }
+        }
+
+        // A grid of xin,yin --> xout,yout
+        psVector *xIn = psVectorAlloc(NUM_GRID * NUM_GRID, PS_TYPE_F32);
+        psVector *yIn = psVectorAlloc(NUM_GRID * NUM_GRID, PS_TYPE_F32);
+        psVector *xOut = psVectorAlloc(NUM_GRID * NUM_GRID, PS_TYPE_F32);
+        psVector *yOut = psVectorAlloc(NUM_GRID * NUM_GRID, PS_TYPE_F32);
+
+        // Create grid of points
+        for (int yint = 0; yint < NUM_GRID; yint++) {
+            inCoord->y = (float)(yint * outny) / (float)(NUM_GRID - 1);
+            for (int xint = 0; xint < NUM_GRID; xint++) {
+                inCoord->x = (float)(xint * outnx) / (float)(NUM_GRID - 1);
+
+                (void)psPlaneTransformApply(outCoord, oldMap, inCoord);
+
+                xOut->data.F32[yint*NUM_GRID + xint] = inCoord->x;
+                yOut->data.F32[yint*NUM_GRID + xint] = inCoord->y;
+                xIn->data.F32[yint*NUM_GRID + xint] = outCoord->x;
+                yIn->data.F32[yint*NUM_GRID + xint] = outCoord->y;
+            }
+        }
+
+        // Initialise the matrix and vectors
+        int nCoeff = (order + 1) * (order + 2) / 2; // Number of polynomial coefficients
+        psImage *matrix = psImageAlloc(nCoeff, nCoeff, PS_TYPE_F64); // Matrix for solution
+        psVector *xVector = psVectorAlloc(nCoeff, PS_TYPE_F64); // Vector for solution in x
+        psVector *yVector = psVectorAlloc(nCoeff, PS_TYPE_F64); // Vector for solution in y
+        for (int i = 0; i < nCoeff; i++) {
+            for (int j = 0; j < nCoeff; j++) {
+                matrix->data.F64[i][j] = 0.0;
+            }
+            xVector->data.F64[i] = 0.0;
+            yVector->data.F64[i] = 0.0;
+        }
+
+        // Iterate over the grid points
+        for (int g = 0; g < NUM_GRID*NUM_GRID; g++) {
+
+            // Iterate over the polynomial coefficients, accumulating the matrix and vectors
+            for (int i = 0, ijIndex = 0; i <= order; i++) {
+                for (int j = 0; j <= order - i; j++, ijIndex++) {
+
+                    fakePoly->mask[i][j] = 0;
+                    double ijPoly = psPolynomial2DEval(fakePoly, xIn->data.F32[g], yIn->data.F32[g]);
+                    fakePoly->mask[i][j] = 1;
+
+                    for (int m = 0, mnIndex = 0; m <= order; m++) {
+                        for (int n = 0; n <= order - m; n++, mnIndex++) {
+
+                            fakePoly->mask[m][n] = 0;
+                            double mnPoly = psPolynomial2DEval(fakePoly, xIn->data.F32[g], yIn->data.F32[g]);
+                            fakePoly->mask[m][n] = 1;
+
+                            matrix->data.F64[ijIndex][mnIndex] += ijPoly * mnPoly;
+                        }
+                    }
+
+                    xVector->data.F64[ijIndex] += ijPoly * (double)xOut->data.F32[g];
+                    yVector->data.F64[ijIndex] += ijPoly * (double)yOut->data.F32[g];
+                }
+            } // Iterating over coefficients
+        } // Iterating over grid points
+
+        // Solution via LU Decomposition
+        psVector *permutation = psVectorAlloc(nCoeff, PS_TYPE_F64); // Permutation vector for LU Decomposition
+        psImage *luMatrix = psMatrixLUD(NULL, &permutation, matrix); // LU decomposed matrix
+        psVector *xSolution = psMatrixLUSolve(NULL, luMatrix, xVector, permutation); // Solution in x
+        psVector *ySolution = psMatrixLUSolve(NULL, luMatrix, yVector, permutation); // Solution in y
+
+        // Stuff coefficients into transformation
+        for (int i = 0, ijIndex = 0; i <= order; i++) {
+            for (int j = 0; j <= order - i; j++, ijIndex++) {
+                newMap->x->coeff[i][j] = xSolution->data.F64[ijIndex];
+                newMap->y->coeff[i][j] = ySolution->data.F64[ijIndex];
+            }
+        }
+        inverted->data[mapNum] = newMap;
+
+#ifdef TESTING
+        // Print x coefficients
+        psTrace("stac.invertMaps", 7, "x' = \n");
+        for (int i = 0; i <= order; i++) {
+            for (int j = 0; j <= order - i; j++) {
+                psTrace("stac.invertMaps", 7, "      %f x^%d y^%d\n", newMap->x->coeff[i][j], i, j);
+            }
+        }
+        // Print y coefficients
+        psTrace("stac.invertMaps", 7, "y' = \n");
+        for (int i = 0; i <= order; i++) {
+            for (int j = 0; j <= order - i; j++) {
+                psTrace("stac.invertMaps", 7, "      %f x^%d y^%d\n", newMap->y->coeff[i][j], i, j);
+            }
+        }
+#endif
+
+#ifdef TESTING
+        // Go forward then backward
+        double x = 123.4;
+        double y = 432.1;
+        psPlane *oldCoords = psAlloc(sizeof(psPlane));
+        oldCoords->x = x;
+        oldCoords->y = y;
+        psPlane *newCoords = psPlaneTransformApply(NULL, oldMap, oldCoords);
+        psTrace("stac.invertMaps.test", 5, "%f,%f --> %f,%f\n", x, y, newCoords->x, newCoords->y);
+        (void)psPlaneTransformApply(oldCoords, newMap, newCoords);
+        psTrace("stac.invertMaps.test", 5, "--------> %f,%f\n", oldCoords->x, oldCoords->y);
+        psFree(newCoords);
+        psFree(oldCoords);
+#endif
+
+        psFree(permutation);
+        psFree(luMatrix);
+        psFree(matrix);
+        psFree(xVector);
+        psFree(yVector);
+        psFree(xSolution);
+        psFree(ySolution);
+        psFree(fakePoly);
+        psFree(xIn);
+        psFree(yIn);
+        psFree(xOut);
+        psFree(yOut);
+    }
+
+    psFree(inCoord);
+    psFree(outCoord);
+
+
+#if 0
+        // Can't handle higher order than linear yet
+        assert(((psPlaneTransform*)maps->data[i])->x->nX == 2 &&
+               ((psPlaneTransform*)maps->data[i])->x->nY == 2 &&
+               ((psPlaneTransform*)maps->data[i])->y->nX == 2 &&
+               ((psPlaneTransform*)maps->data[i])->y->nY == 2);
+        psPlaneTransform *newMap = psPlaneTransformAlloc(2, 2); // Inverted map
+        psPlaneTransform *oldMap = (psPlaneTransform*)maps->data[i]; // Uninverted map
+
+        // Now, simply do a 2x2 matrix inversion
+
+        double a = oldMap->x->coeff[1][0];
+        double b = oldMap->x->coeff[0][1];
+        double c = oldMap->y->coeff[1][0];
+        double d = oldMap->y->coeff[0][1];
+        double e = oldMap->x->coeff[0][0];
+        double f = oldMap->y->coeff[0][0];
+
+        double invDet = 1.0 / (a * d - b * c); // Inverse of the determinant
+
+        // Not entirely sure why this works, but it appears to do so.......................................
+        newMap->x->coeff[1][0] = invDet * a;
+        newMap->x->coeff[0][1] = - invDet * b;
+        newMap->y->coeff[1][0] = - invDet * c;
+        newMap->y->coeff[0][1] = invDet * d;
+
+        newMap->x->coeff[0][0] = - invDet * (d * e + c * f);
+        newMap->y->coeff[0][0] = - invDet * (b * e + a * f);
+#endif
+
+
+    return inverted;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacRead.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacRead.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacRead.c	(revision 22322)
@@ -0,0 +1,207 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "pslib.h"
+#include "stac.h"
+
+#define BUFFER 100                      // Size of buffer for incrementally reading coordinates
+
+psArray *stacReadImages(psArray **headers, // The image headers, to be returned
+    psArray *filenames // The file names of the images
+    )
+{
+    int nFiles = filenames->n;          // The number of input files
+    psArray *images = psArrayAlloc(nFiles); // The input files, to be returned
+    assert(!headers || ! *headers || (*headers)->n == nFiles);
+    if (headers && ! *headers) {
+        *headers = psArrayAlloc(nFiles);
+    }
+
+    psRegion imageRegion = {0, 0, 0, 0}; // Region of image to read
+
+    psTrace("stac.read.images", 1, "Reading input images....\n");
+    for (int i = 0; i < nFiles; i++) {
+        psTrace("stac.read.images", 2, "Reading input image %s....\n", (char *)filenames->data[i]);
+        psFits *imageFile = psFitsOpen(filenames->data[i], "r");
+        if (!imageFile) {
+            psError(PS_ERR_IO, false, "Unable to open FITS file %s.\n", (char *)filenames->data[i]);
+            exit(EXIT_FAILURE);
+        }
+        // We only read PHUs --- not mucking around with extensions for now
+        if (headers) {
+            (*headers)->data[i] = psFitsReadHeader(NULL, imageFile);
+        }
+        psImage *image = psFitsReadImage(imageFile, imageRegion, 0);
+        if (image == NULL) {
+            psErrorStackPrint(stderr,"Fatal error: Unable to read %s\n", (char *)filenames->data[i]);
+            exit(EXIT_FAILURE);
+        }
+        psFitsClose(imageFile);
+        psTrace("stac.read.images", 4, "Image %s is %dx%d\n", (char *)filenames->data[i], image->numCols,
+                image->numRows);
+        // Convert to 32-bit floating point, in necessary
+        if (image->type.type != PS_TYPE_F32) {
+            psTrace("stac.read.images", 3, "Converting %s to floating point in memory....\n",
+                    (char *)filenames->data[i]);
+            psImage *copy = psImageCopy(NULL, image, PS_TYPE_F32);
+            psFree(image);
+            image = copy;
+        }
+        int numNaN = psImageClipNaN(image, 0.0);
+        if (numNaN) {
+            psTrace("stac.read.images", 5, "Clipped %d NaN pixels to zero.\n", numNaN);
+        }
+        images->data[i] = image;
+    }
+    psTrace("stac.read.images",1,"%d input images read.\n",nFiles);
+
+    return images;
+}
+
+psArray *stacReadCoords(const char *filename)
+{
+    FILE *file = fopen(filename, "r");
+    if (file == NULL) {
+        psLogMsg("stac.read.coords", PS_LOG_ERROR, "Cannot open coordinate file, %s\n", filename);
+        return NULL;
+    }
+
+    psTrace("stac.read.coords", 5, "Reading coordinate file, %s\n", filename);
+
+    psArray *coords = psArrayAllocEmpty(BUFFER); // The array of coordinates to be returned
+    float x, y;                         // Coordinates to read
+    while (fscanf(file, "%f %f\n", &x, &y) == 2) {
+        psPlane *coord = psPlaneAlloc();// A coordinate
+        coord->x = x;
+        coord->y = y;
+        coords->data[coords->n] = coord;
+        coords->n++;
+        if (coords->n % BUFFER == 0) {
+            coords = psArrayRealloc(coords, coords->n + BUFFER);
+        }
+    }
+
+    psTrace("stac.read.coords", 5, "%ld coordinates read.\n", coords->n);
+
+    fclose(file);
+    return coords;
+}
+
+
+psPlaneTransform *stacReadMap(const char *filename)
+{
+    FILE *mapfp = fopen(filename, "r");
+    if (mapfp == NULL) {
+        psLogMsg("stac.read.map", PS_LOG_ERROR, "Cannot open map file, %s\n", filename);
+        return NULL;
+    }
+    // Read the file
+    psTrace("stac.read.map", 5, "Reading map file %s....\n", filename);
+
+    // Format is now:
+    // order
+    // x coefficients
+    // y coefficients
+    //
+    // where the order is 1 for linear, 2 for quadratic, 3 for cubic.
+    // and the coefficients are read by the following pseudo-code:
+    //
+    //  for (int k = 0; k < order + 1; k++)
+    //      for (int j = 0; j < k; j++)
+    //          int i = k - j;
+    //              read coefficient of x^i y^j
+    //
+    // This produces the ordering:
+    // x^0 y^0, x^1 y^0, x^0 y^1, x^2 y^0, x^1 y^1, x^0 y^2, x^3 y^0, x^2 y^1, x^1 y^2, x^0 y^3
+    //
+    // This is, of course, for third order polynomials.
+    // For lower orders, the list is truncated at the appropriate level.
+
+    int order = 0;                      // Polynomial order
+    if (fscanf(mapfp, "%d", &order) != 1) {
+        psLogMsg("stac.read.map", PS_LOG_ERROR, "Unable to read map file %s\n", filename);
+        fclose(mapfp);
+        return NULL;
+    }
+
+    psTrace("stac.read.map", 5, "Polynomial order: %d\n", order);
+
+    psPlaneTransform *map = psPlaneTransformAlloc(order, order); // The transformation
+    // Set coefficient masks
+    for (int i = 0; i <= order; i++) {
+        for (int j = 0; j <= order; j++) {
+            if (i + j > order + 1) {
+                map->x->mask[i][j] = 1;
+                map->y->mask[i][j] = 1;
+            } else {
+                map->x->mask[i][j] = 0;
+                map->y->mask[i][j] = 0;
+            }
+        }
+    }
+
+    // Read x coefficients
+    psTrace("stac.read.map", 7, "x' = \n");
+    for (int k = 0; k <= order; k++) {
+        for (int j = 0; j <= k; j++) {
+            int i = k - j;
+            if (fscanf(mapfp, "%lf", &map->x->coeff[i][j]) != 1) {
+                psLogMsg("stac.read.map", PS_LOG_ERROR, "Unable to read map file %s\n", filename);
+                fclose(mapfp);
+                psFree(map);
+                return NULL;
+            }
+            psTrace("stac.read.map", 7, "      %f x^%d y^%d\n", map->x->coeff[i][j], i, j);
+        }
+    }
+    // Read y coefficients
+    psTrace("stac.read.maps", 7, "y' = \n");
+    for (int k = 0; k <= order; k++) {
+        for (int j = 0; j <= k; j++) {
+            int i = k - j;
+            if (fscanf(mapfp, "%lf", &map->y->coeff[i][j]) != 1) {
+                psLogMsg("stac.read.map", PS_LOG_ERROR, "Unable to read map file %s\n", filename);
+                fclose(mapfp);
+                psFree(map);
+                return NULL;
+            }
+            psTrace("stac.read.map", 7, "      %f x^%d y^%d\n", map->y->coeff[i][j], i, j);
+        }
+    }
+
+    fclose(mapfp);
+
+    return map;
+}
+
+
+
+psArray *stacReadMaps(psArray *filenames // The file names of the images whose maps are to be read
+    )
+{
+    int nFiles = filenames->n;          // The number of input files
+    psArray *maps = psArrayAlloc(nFiles); // The maps, to be returned
+    char mapfile[MAXCHAR];              // Filename of map
+
+    psTrace("stac.read.maps",1,"Reading maps....\n");
+    for (int i = 0; i < nFiles; i++) {
+        if (strlen(filenames->data[i]) > MAXCHAR - 4) {
+            psLogMsg("stac.read.maps",PS_LOG_ERROR,"Filename %s is too long.\n",(char *)filenames->data[i]);
+            exit(EXIT_FAILURE);
+        }
+        // Read the file
+        sprintf(mapfile, "%s.map", (const char*)filenames->data[i]);
+        maps->data[i] = stacReadMap(mapfile);
+        if (maps->data[i] == NULL) {
+            psLogMsg("stac.read.maps", PS_LOG_ERROR, "Unable to read map: %s\n", mapfile);
+        }
+
+    }
+    psTrace("stac.read.maps",1,"%d maps read.\n",nFiles);
+
+    return maps;
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacRejection.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacRejection.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacRejection.c	(revision 22322)
@@ -0,0 +1,261 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "stac.h"
+
+
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
+float stacGradient(psImage *image,      // Input for which to measure the gradient
+                   int x, int y         // Coordinates at which to measure the gradient
+    )
+{
+    int num = 0;
+    psVector *pixels = psVectorAllocEmpty(8, PS_TYPE_F32); // Array of pixels
+    psVector *mask = psVectorAllocEmpty(8, PS_TYPE_U8); // Corresponding mask
+
+    // Get limits
+    int xMin = MAX(x - 1, 0);
+    int xMax = MIN(x + 1, image->numCols - 1);
+    int yMin = MAX(y - 1, 0);
+    int yMax = MIN(y + 1, image->numRows - 1);
+    for (int j = yMin; j <= yMax; j++) {
+        for (int i = xMin; i <= xMax; i++) {
+            if ((i != x) && (j != y)) {
+                pixels->data.F32[num] = image->data.F32[j][i];
+                mask->data.U8[num] = 0;
+                num++;
+            }
+        }
+    }
+    pixels->n = num;
+    mask->n = num;
+
+    // Get the median
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    (void)psVectorStats(stats, pixels, NULL, mask, 1);
+    float median = stats->sampleMedian;
+    psFree(stats);
+    psFree(pixels);
+    psFree(mask);
+    return median / image->data.F32[y][x];
+}
+
+psArray *stacRejection(psArray *inputs, // Input images
+                       psArray *rejected, // Rejected images
+                       psArray *regions, // Regions of interest
+                       psArray *maps,   // Maps from input to transformed image
+                       psArray *inverseMaps, // Maps from transformed to input image
+                       float frac,      // Fraction of pixel rejected before looking more carefully
+                       float grad,      // Gradient limit for rejection
+                       psArray *names   // Names of original images (only for writing out when TESTING)
+    )
+{
+    int nImages = inputs->n;            // Number of input images
+
+    // Check inputs
+    assert(inputs->n == rejected->n);
+    assert(inputs->n == regions->n);
+    assert(inputs->n == maps->n);
+    assert(inputs->n == inverseMaps->n);
+    assert(!names || names->n == inputs->n);
+
+    for (int i = 0; i < nImages; i++) {
+        psImage *input = inputs->data[i];
+        psImage *region = regions->data[i];
+        assert(input->numRows == region->numRows && input->numCols == region->numCols);
+    }
+
+    // Stuff for the transformations
+    psPlane *inCoords = psAlloc(sizeof(psPlane)); // Coordinates on the input
+    psPlane *outCoords = psAlloc(sizeof(psPlane)); // Coordinates on the output
+
+    psTrace("stac.rejection", 1, "Mapping rejection masks back to source....\n");
+
+    // Vectors for calculating the mean gradient
+    psVector *grads = psVectorAlloc(nImages, PS_TYPE_F32); // Gradient for each image
+    psVector *gradsMask = psVectorAlloc(nImages, PS_TYPE_U8); // Mask for gradient vector
+
+    // Transform rejection masks back to source
+    psArray *inputRej = psArrayAlloc(nImages);
+    for (int i = 0; i < nImages; i++) {
+        // Size of input image
+        int nxInput = ((psImage*)(inputs->data[i]))->numCols;
+        int nyInput = ((psImage*)(inputs->data[i]))->numRows;
+        psImage *mask = psImageAlloc(nxInput, nyInput, PS_TYPE_U8); // The pixel mask for the input
+#ifdef TESTING
+        psImage *rejmap = psImageAlloc(nxInput, nyInput, PS_TYPE_F32); // The rejections in the source frame
+        psImage *gradient = psImageAlloc(nxInput, nyInput, PS_TYPE_F32);        // The gradient image
+#endif
+        psImage *reject = rejected->data[i]; // Pull out the mask in the output frame
+        psPlaneTransform *map = maps->data[i]; // The map from input to output
+        psImage *region = regions->data[i]; // The region of interest for this image
+
+        psTrace("stac.rejection", 3, "Transforming rejection mask %d....\n", i);
+        int nBad = 0;                   // Number of bad pixels
+
+#ifdef CRFLUX
+        // Set up CR output
+        FILE *crs = NULL;               // File for outputting details of rejected pixels
+        char crfile[MAXCHAR];   // Filename
+        sprintf(crfile,"%s.cr",names->data[i]);
+        if ((crs = fopen(crfile, "w")) == NULL) {
+            fprintf(stderr, "Unable to open file for detailed output, %s\n");
+            return NULL;
+        }
+#endif
+
+        psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(PS_INTERPOLATE_BILINEAR,
+                                                                           reject, NULL, NULL, 0, 0.0, 0.0,
+                                                                           0, 0, 0.0);
+
+        // Transform the mask
+        // Optimisation option is to only transform the pixels that have been rejected in the output,
+        // calculate derivatives of the map, and use that as a buffer around the transformed position
+        // in the input image.
+        psStats *median = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+        for (int y = 0; y < nyInput; y++) {
+            for (int x = 0; x < nxInput; x++) {
+
+                // Only transform pixels of interest
+                if (region->data.U8[y][x]) {
+
+                    inCoords->x = (double)x + 0.5;
+                    inCoords->y = (double)y + 0.5;
+                    (void)psPlaneTransformApply(outCoords, map, inCoords);
+                    double maskVal;
+                    if (!psImageInterpolate(&maskVal, NULL, NULL, outCoords->x, outCoords->y, interp)) {
+                        psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+                        psFree(grads);
+                        psFree(gradsMask);
+                        psFree(inCoords);
+                        psFree(outCoords);
+                        return NULL;
+                    }
+
+#ifdef TESTING
+                    rejmap->data.F32[y][x] = maskVal;
+#endif
+
+                    if (maskVal > frac) {
+                        // Calculate mean gradient on other images
+                        float meanGrads = 0.0;
+                        int numGrads = 0;
+                        for (int j = 0; j < nImages; j++) {
+                            if (i != j) {
+                                // Get coordinates for that image
+                                (void)psPlaneTransformApply(inCoords, inverseMaps->data[j], outCoords);
+                                int xPix = (int)(inCoords->x + 0.5);
+                                int yPix = (int)(inCoords->y + 0.5);
+                                if ((xPix >= 0) && (xPix <= ((psImage*)(inputs->data[j]))->numCols - 1) &&
+                                    (yPix >= 0) && (yPix <= ((psImage*)(inputs->data[j]))->numRows - 1)) {
+                                    // Calculate the gradient
+                                    grads->data.F32[j] = stacGradient(inputs->data[j], xPix, yPix);
+                                    if (isfinite(grads->data.F32[j])) {
+                                        gradsMask->data.U8[j] = 0;
+                                        numGrads++;
+                                    } else {
+                                        gradsMask->data.U8[j] = 1;
+                                    }
+                                } else {
+                                    gradsMask->data.U8[j] = 1; // Mask this one
+                                }
+                            } else {
+                                gradsMask->data.U8[j] = 1; // Mask this one
+                            }
+                        }
+                        if (numGrads > 0) {
+                            (void)psVectorStats(median, grads, NULL, gradsMask, (psU8)1);
+                            meanGrads = median->sampleMedian;
+                        } else {
+                            meanGrads = 0.0;
+                        }
+
+#ifdef TESTING
+                        //gradient->data.F32[y][x] = stacGradient(inputs->data[i], x, y) / meanGrads;
+                        gradient->data.F32[y][x] = meanGrads;
+#endif
+
+                        //if (stacGradient(inputs->data[i], x, y) < grad * meanGrads) {
+                        if (meanGrads > grad) {
+                            mask->data.U8[y][x] = 1;
+                            nBad++;
+
+#ifdef CRFLUX
+                            fprintf(crs, "%d %d --> %f %f %f\n", x, y,
+                                    ((psImage*)(inputs->data[i]))->data.F32[y][x], maskVal,
+                                    stacGradient(inputs->data[i], x, y));
+#endif
+
+                        } else {
+                            mask->data.U8[y][x] = 0;
+                        } // Gradient threshold
+                    } else {
+                        mask->data.U8[y][x] = 0;
+                    } // Rejection threshold
+
+                } else {
+                    mask->data.U8[y][x] = 0;
+                } // Only touching pixels of interest
+
+            }
+        } // Iterating over pixels
+        psFree(median);
+
+#ifdef CRFLUX
+        // Close file used for detailed output
+        fclose(crs);
+#endif
+
+        psTrace("stac.rejection", 1, "%d pixels masked in image %d\n", nBad, i);
+        // Clip the image, and convert to suitable mask format
+
+#ifdef TESTING
+        // Write error image out to check
+        char maskName[MAXCHAR];         // Filename of mask image
+        char rejmapName[MAXCHAR];       // Filename of rejection image
+        char gradName[MAXCHAR];         // Filename of gradient image
+        sprintf(maskName, "%s.mask", (char*)names->data[i]);
+        sprintf(rejmapName, "%s.rejmap", (char*)names->data[i]);
+        sprintf(gradName, "%s.grad", (char*)names->data[i]);
+
+        psFits *maskFile = psFitsOpen(maskName, "w");
+        psFits *rejmapFile = psFitsOpen(rejmapName, "w");
+        psFits *gradFile = psFitsOpen(gradName, "w");
+        if (!psFitsWriteImage(maskFile, NULL, mask, 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", maskName);
+        }
+        psTrace("stac", 1, "Mask image written to %s\n", maskName);
+        if (!psFitsWriteImage(rejmapFile, NULL, rejmap, 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", rejmapName);
+        }
+        psTrace("stac", 1, "Rejection map written to %s\n", rejmapName);
+        if (!psFitsWriteImage(gradFile, NULL, gradient, 0, NULL)) {
+            psErrorStackPrint(stderr, "Unable to write image: %s\n", gradName);
+        }
+        psTrace("stac", 1, "Gradient image written to %s\n", gradName);
+        psFitsClose(maskFile);
+        psFitsClose(rejmapFile);
+        psFitsClose(gradFile);
+        psFree(rejmap);
+        psFree(gradient);
+#endif
+
+        // Stuff into the array
+        inputRej->data[i] = mask;
+
+    }
+
+    psFree(grads);
+    psFree(gradsMask);
+
+    psFree(inCoords);
+    psFree(outCoords);
+
+    return inputRej;
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacScales.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacScales.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacScales.c	(revision 22322)
@@ -0,0 +1,297 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "stac.h"
+
+#define SQUARE(x) ((x)*(x))
+
+#define SAMPLE 10                       // Subsample rate for images
+
+float stacBackground(const psImage *image, // Image for which to get the background
+                     int sample         // Sample in increments of this value
+    )
+{
+    assert(image->type.type == PS_TYPE_F32);
+
+#if 1
+    int size = image->numCols * image->numRows; // Number of pixels in image
+    int numSamples = size / sample;     // Number of samples in image
+    psVector *values = psVectorAlloc(numSamples + 1, PS_TYPE_F32); // Vector containing sub-sample
+    psVector *mask = psVectorAlloc(numSamples + 1, PS_TYPE_U8); // Mask for sample
+
+    int offset = 0;                     // Offset from start of the row
+    int index = 0;                      // Sample number
+    for (int row = 0; row < image->numRows; row++) {
+        // I'd cast this as a "for", but this makes it a bit easier to understand.
+        int col = offset;
+        while (col < image->numCols) {
+            values->data.F32[index] = image->data.F32[row][col];
+            if (isnan(values->data.F32[index])) {
+                mask->data.U8[index] = 1;
+            } else {
+                mask->data.U8[index] = 0;
+            }
+            col += sample;
+            index++;
+        }
+        offset = col - image->numCols;
+    }
+
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    psVectorStats(stats, values, NULL, mask, 1);
+    float median = stats->sampleMedian;
+    psFree(stats);
+    psFree(values);
+    psFree(mask);
+#else
+    // Will use robust median instead of sampling --- it's supposed to be fast.
+    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Using a clipped mean because median is SLOW
+    stats->clipSigma = 3.0;
+    stats->clipIter = 5;
+    stats = psImageStats(stats, (psImage*)image, NULL, 0);
+    float median = stats->robustMedian;
+    psFree(stats);
+#endif
+
+    return median;
+}
+
+
+bool stacScales(psVector **scalesPtr,   // Scales to return
+                psVector **offsetsPtr,  // Offsets to return
+                const psArray *images,  // Images on which to measure the scales and offsets
+                const char *starFile,   // File containing coordinates to photometer
+                const char *starMapFile, // Map for coodinates to the common output frame
+                float xMapDiff, float yMapDiff, // Difference from the map to apply (due to size difference)
+                float aper              // Aperture to use for photometry (radius)
+    )
+{
+    assert(scalesPtr);
+    assert(offsetsPtr);
+    assert(images);
+    for (int i = 0; i < images->n; i++) {
+        psImage *image = images->data[i];
+        assert(image->type.type == PS_TYPE_F32);
+    }
+
+    psVector *scales = NULL; // Relative scales between images
+    psVector *offsets = NULL; // Offsets between images (ADU)
+    if (*scalesPtr) {
+        scales = *scalesPtr;
+        assert(scales);
+        assert(scales->n == images->n);
+        assert(scales->type.type == PS_TYPE_F32);
+    } else {
+        scales = psVectorAlloc(images->n, PS_TYPE_F32);
+        *scalesPtr = scales;
+    }
+    if (*offsetsPtr) {
+        offsets = *offsetsPtr;
+        assert(offsets);
+        assert(offsets->n == images->n);
+        assert(offsets->type.type == PS_TYPE_F32);
+    } else {
+        offsets = psVectorAlloc(images->n, PS_TYPE_F32);
+        *offsetsPtr = offsets;
+    }
+
+    // Offsets are easy
+    psTrace("stac.scales", 3, "Getting background levels....\n");
+    double startTime = getTime();
+    for (int i = 0; i < images->n; i++) {
+        offsets->data.F32[i] = stacBackground(images->data[i], SAMPLE);
+        psTrace("stac.scales", 5, "Background in image %d is %f\n", i, offsets->data.F32[i]);
+        double time = getTime();
+        psTrace("stac.scales", 10, "Took %f sec\n", time - startTime);
+        startTime = time;
+    }
+
+    // Now the scales
+    if (starFile == NULL || starMapFile == NULL) {
+        psLogMsg("stac.scales", PS_LOG_INFO,
+                 "No coordinates available to set scales --- assuming all are identical.\n");
+        for (int i = 0; i < images->n; i++) {
+            scales->data.F32[i] = 1.0;
+            psTrace("stac.scales", 5, "Scale for image %d is %f\n", i, scales->data.F32[i]);
+        }
+    } else {
+        // Read star coordinates and map
+        psArray *starCoords = stacReadCoords(starFile); // Array of star coordinates
+        psPlaneTransform *starMap = stacReadMap(starMapFile); // Transformation for star coordinates
+
+        // Transform the stellar positions to match the transformed reference frame
+        psArray *starCoordsTransformed = psArrayAlloc(starCoords->n); // Transformed positions
+        // Fix up difference between map and output frame
+        starMap->x->coeff[0][0] -= xMapDiff;
+        starMap->y->coeff[0][0] -= yMapDiff;
+        for (int i = 0; i < starCoords->n; i++) {
+            starCoordsTransformed->data[i] = psPlaneTransformApply(NULL, starMap, starCoords->data[i]);
+        }
+        psFree(starMap);
+
+        psArray *stars = psArrayAlloc(images->n); // Array of stellar photometry vectors
+        psArray *masks = psArrayAlloc(images->n); // Array of masks for stars
+
+        // Set scales relative to the first image
+        scales->data.F32[0] = 1.0;
+
+        // Iterate over images
+        for (int i = 0; i < scales->n; i++) {
+            psImage *image = images->data[i]; // The image we're working with
+
+            // Do photometry on transformed image i
+            psTrace("stac.scales", 3, "Doing photometry on image %d....\n", i);
+            psVector *photometry = psVectorAlloc(starCoords->n, PS_TYPE_F32); // Photometry of the stars
+            psVector *mask = psVectorAlloc(starCoords->n, PS_TYPE_U8); // Mask for the photometry
+            for (int j = 0; j < starCoordsTransformed->n; j++) {
+                psPlane *coords = starCoordsTransformed->data[j]; // The coordinates of the star
+
+                if (coords->x < aper || coords->y < aper ||
+                    coords->x + aper > image->numCols - 1 ||
+                    coords->y + aper > image->numRows) {
+                    mask->data.U8[j] = 1;
+                } else {
+                    // Sum flux within the aperture
+                    float sum = 0.0;
+                    int numPix = 0;
+                    float aper2 = SQUARE(aper);
+                    for (int y = (int)floorf(coords->y - aper);
+                         y <= (int)ceilf(coords->y + aper); y++) {
+                        for (int x = (int)floorf(coords->x - aper);
+                             x <= (int)ceilf(coords->x + aper); x++) {
+                            if (SQUARE((float)x + 0.5 - coords->x) + SQUARE((float)y + 0.5 - coords->y) <=
+                                aper2) {
+                                sum += image->data.F32[y][x];
+                                numPix++;
+                            }
+                        }
+                    }
+                    // Subtract background, renormalise to account for circular aperture
+                    if (numPix > 0 && sum > 0) {
+                        sum -= offsets->data.F32[i] * (float)numPix;
+                        photometry->data.F32[j] = sum * M_PI * aper2 / (float)numPix;
+                        if (photometry->data.F32[j] > 0 && isfinite(photometry->data.F32[j])) {
+                            mask->data.U8[j] = 1;
+                            psTrace("stac.scales", 8, "Star at %f,%f --> %f\n", coords->x, coords->y, sum);
+                        } else {
+                            mask->data.U8[j] = 0;
+                        }
+                    } else {
+                        mask->data.U8[j] = 0;
+                    }
+                }
+            }
+            stars->data[i] = photometry;
+            masks->data[i] = mask;
+        }
+        psFree(starCoords);
+        psFree(starCoordsTransformed);
+
+        // Get the scales
+        psVector *ref = stars->data[0]; // The reference photometry
+        psVector *refMask = masks->data[0]; // The reference mask
+#if 0
+        psStats *stats = psStatsAlloc(PS_STAT_CLIPPED_MEAN); // Statistics
+        stats->clipSigma = 2.5;
+        stats->clipIter = 3;
+#else
+        psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN); // Statistics
+#endif
+        for (int i = 1; i < scales->n; i++) {
+            psVector *input = stars->data[i];   // The comparison photometry
+            psVector *inputMask = masks->data[i]; // The comparison mask
+
+            psVector *compare = (psVector*)psBinaryOp(NULL, input, "/", ref);
+            psVector *compareMask = (psVector*)psBinaryOp(NULL, inputMask, "*", refMask);
+            (void)psBinaryOp(compareMask, psScalarAlloc(1, PS_TYPE_U8), "-", compareMask);
+
+#if 0
+            psTrace("stac.scales", 9, "Getting scale for image %d...\n", i);
+            for (int j = 0; j < compare->n; j++) {
+                if (compareMask->data.U8[j] & 1) {
+                    psTrace("stac.scales", 9, "Bad star %d: %f %f --> %f %d\n", j, input->data.F32[j],
+                            ref->data.F32[j], compare->data.F32[j], compareMask->data.U8[j]);
+                } else {
+                    psTrace("stac.scales", 9, "Good star %d: %f %f --> %f %d\n", j, input->data.F32[j],
+                            ref->data.F32[j], compare->data.F32[j], compareMask->data.U8[j]);
+                }
+            }
+#endif
+
+            psVectorStats(stats, compare, NULL, compareMask, 1);
+
+#if 0
+            scales->data.F32[i] = stats->clippedMean;
+#else
+            scales->data.F32[i] = stats->sampleMedian;
+#endif
+            psTrace("stac.scales", 5, "Scale for image %d is %f\n", i, scales->data.F32[i]);
+            psFree(compare);
+            psFree(compareMask);
+        }
+        psFree(stats);
+
+        psFree(stars);
+        psFree(masks);
+    }
+
+    return true;
+}
+
+
+bool stacRescale(psArray *images,       // Images to rescale
+                 psArray *errImages,    // Variance images to rescale
+                 const psImage *mask,   // Mask for pixels to scale
+                 const psVector *scales,// Scales for images
+                 const psVector *offsets // Offsets for images
+    )
+{
+    assert(images);
+    assert(scales);
+    assert(offsets);
+    assert(images->n == scales->n);
+    assert(images->n == offsets->n);
+    assert(!errImages || errImages->n == images->n);
+    assert(scales->type.type == PS_TYPE_F32);
+    assert(offsets->type.type == PS_TYPE_F32);
+    for (int i = 0; i < images->n; i++) {
+        psImage *image = images->data[i]; // Image of interest
+        assert(image->type.type == PS_TYPE_F32);
+        assert(scales->data.F32[i] != 0);
+        if (mask) {
+            assert(mask->type.type == PS_TYPE_U8);
+            assert(image->numCols == mask->numCols && image->numRows == mask->numRows);
+        }
+        if (errImages) {
+            psImage *errImage = errImages->data[i];
+            assert(errImage->type.type == PS_TYPE_F32);
+            assert(errImage->numCols == image->numCols && errImage->numRows == image->numRows);
+        }
+    }
+
+    for (int i = 0; i < images->n; i++) {
+        psImage *image = images->data[i]; // Image to rescale
+        psImage *errImage = NULL;       // Variance image to rescale
+        if (errImages) {
+            errImage = errImages->data[i];
+        }
+        float scale = scales->data.F32[i]; // Scale to use
+        float offset = offsets->data.F32[i]; // Offset to use
+        for (int y = 0; y < image->numRows; y++) {
+            for (int x = 0; x < image->numCols; x++) {
+                if (!mask || mask->data.F32[y][x]) {
+                    image->data.F32[y][x] = (image->data.F32[y][x] - offset) / scale;
+                    if (errImage) {
+                        errImage->data.F32[y][x] /= SQUARE(scale);
+                    }
+                }
+            }
+        }
+    }
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacSize.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacSize.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacSize.c	(revision 22322)
@@ -0,0 +1,125 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "stac.h"
+
+bool stacSize(int *outnx, int *outny,   // Size of output image (returned)
+              float *xMapDiff, float *yMapDiff, // Difference applied to maps
+              const psVector *xSizes,   // Sizes of images in x
+              const psVector *ySizes,   // Sizes of images in y
+              psArray *maps             // Transformation maps
+    )
+{
+    int nImages = xSizes->n;            // Number of input images
+    assert(ySizes->n == nImages);
+    assert(xSizes->type.type == PS_TYPE_S32);
+    assert(ySizes->type.type == PS_TYPE_S32);
+    assert(maps->n == nImages);
+    assert(outnx && outny);             // Must be able to write to these
+
+    psPlane *inCoord = psAlloc(sizeof(psPlane)); // Input coordinates
+    psPlane *outCoord = psAlloc(sizeof(psPlane)); // Output coordinates
+
+    // Initial "guess" at limits
+    float xMin = INFINITY;
+    float xMax = 0.0;
+    float yMin = INFINITY;
+    float yMax = 0.0;
+
+    for (int i = 0; i < nImages; i++) {
+        int xSize = xSizes->data.S32[i];// Size of image in x
+        int ySize = ySizes->data.S32[i];// Size of image in y
+        psPlaneTransform *map = maps->data[i]; // The map
+
+        psTrace("stac.size", 4, "Image %d:\n", i);
+
+        // Lower left corner
+        inCoord->x = 0.0;
+        inCoord->y = 0.0;
+        (void)psPlaneTransformApply(outCoord, map, inCoord);
+        if (outCoord->x < xMin) {
+            xMin = outCoord->x;
+        } else if (outCoord->x > xMax) {
+            xMax = outCoord->x;
+        }
+        if (outCoord->y < yMin) {
+            yMin = outCoord->y;
+        } else if (outCoord->y > yMax) {
+            yMax = outCoord->y;
+        }
+        psTrace("stac.size", 4, "Lower left: %f %f\n", outCoord->x, outCoord->y);
+
+        // Lower right corner
+        inCoord->x = xSize;
+        (void)psPlaneTransformApply(outCoord, map, inCoord);
+        if (outCoord->x < xMin) {
+            xMin = outCoord->x;
+        } else if (outCoord->x > xMax) {
+            xMax = outCoord->x;
+        }
+        if (outCoord->y < yMin) {
+            yMin = outCoord->y;
+        } else if (outCoord->y > yMax) {
+            yMax = outCoord->y;
+        }
+        psTrace("stac.size", 4, "Lower right: %f %f\n", outCoord->x, outCoord->y);
+
+        // Upper right corner
+        inCoord->x = 0.0;
+        inCoord->y = ySize;
+        (void)psPlaneTransformApply(outCoord, map, inCoord);
+        if (outCoord->x < xMin) {
+            xMin = outCoord->x;
+        } else if (outCoord->x > xMax) {
+            xMax = outCoord->x;
+        }
+        if (outCoord->y < yMin) {
+            yMin = outCoord->y;
+        } else if (outCoord->y > yMax) {
+            yMax = outCoord->y;
+        }
+        psTrace("stac.size", 4, "Upper right: %f %f\n", outCoord->x, outCoord->y);
+
+        // Upper left corner
+        inCoord->x = xSize;
+        (void)psPlaneTransformApply(outCoord, map, inCoord);
+        if (outCoord->x < xMin) {
+            xMin = outCoord->x;
+        } else if (outCoord->x > xMax) {
+            xMax = outCoord->x;
+        }
+        if (outCoord->y < yMin) {
+            yMin = outCoord->y;
+        } else if (outCoord->y > yMax) {
+            yMax = outCoord->y;
+        }
+        psTrace("stac.size", 4, "Upper left: %f %f\n", outCoord->x, outCoord->y);
+    }
+
+    // Tweak the maps to account for the offset
+    if (xMapDiff != NULL) {
+        *xMapDiff = floorf(xMin);
+    }
+    if (yMapDiff != NULL) {
+        *yMapDiff = floorf(yMin);
+    }
+    for (int i = 0; i < nImages; i++) {
+        psPlaneTransform *map = maps->data[i]; // The map of interest
+        map->x->coeff[0][0] -= floorf(xMin);
+        map->y->coeff[0][0] -= floorf(yMin);
+    }
+
+    *outnx = (int)ceilf(xMax - xMin);
+    *outny = (int)ceilf(yMax - yMin);
+
+    psTrace("stac.size", 1, "Output size is to be %dx%d\n", *outnx, *outny);
+
+    psFree(inCoord);
+    psFree(outCoord);
+
+    return true;
+}
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacTime.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacTime.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacTime.c	(revision 22322)
@@ -0,0 +1,16 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/time.h>
+
+double getTime(void)
+/* Gets the current time.  Got this from Nick Kaiser's fetchpix.c */
+{
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return(tv.tv_sec + 1.e-6 * tv.tv_usec);
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacTransform.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacTransform.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacTransform.c	(revision 22322)
@@ -0,0 +1,245 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "stac.h"
+
+#define SQUARE(x) ((x)*(x))
+#define MIN(x,y) (((x) > (y)) ? (y) : (x))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+// Hacked the original ps_ImagePixelInterpolateBILINEAR_F32 to add variances
+// i.e., to square the fractions when combining.
+inline psF64 p_psImageErrorInterpolateBILINEAR_F32(const psImage* input,
+                                                   float x,
+                                                   float y,
+                                                   const psImage* mask,
+                                                   unsigned int maskVal,
+                                                   psF64 unexposedValue)
+{
+    double floorX = floor((psF64)(x) - 0.5);
+    double floorY = floor((psF64)(y) - 0.5);
+    psF64 fracX = x - 0.5 - floorX;
+    psF64 fracY = y - 0.5 - floorY;
+    int intFloorX = (int) floorX;
+    int intFloorY = (int) floorY;
+    int lastX = input->numCols - 1;
+    int lastY = input->numRows - 1;
+    psF32 V00 = 0.0;
+    psF32 V01 = 0.0;
+    psF32 V10 = 0.0;
+    psF32 V11 = 0.0;
+    bool valid00 = false;
+    bool valid01 = false;
+    bool valid10 = false;
+    bool valid11 = false;
+
+    if (intFloorY >= 0 && intFloorY <= lastY) {
+        if (intFloorX >= 0 && intFloorX <= lastX) {
+            V00 = input->data.F32[intFloorY][intFloorX];
+            valid00 = true;
+        }
+        if (intFloorX >= -1 && intFloorX < lastX) {
+            V10 = input->data.F32[intFloorY][intFloorX+1];
+            valid10 = true;
+        }
+    }
+    if (intFloorY >= -1 && intFloorY < lastY) {
+        if (intFloorX >= 0 && intFloorX <= lastX) {
+            V01 = input->data.F32[intFloorY+1][intFloorX];
+            valid01 = true;
+        }
+        if (intFloorX >= -1 && intFloorX < lastX) {
+            V11 = input->data.F32[intFloorY+1][intFloorX+1];
+            valid11 = true;
+        }
+    }
+
+    /* cover likely case of all pixels being valid more efficiently */
+    if (valid00 && valid10 && valid01 && valid11) {
+        /* formula from the ADD */
+        return V00*SQUARE((1.0-fracX)*(1.0-fracY)) + V10*SQUARE(fracX*(1.0-fracY)) +
+            V01*SQUARE(fracY*(1.0-fracX)) + V11*SQUARE(fracX*fracY);
+    }
+
+    /* OK, at least one pixel is not valid - need to do it piecemeal */
+
+    psF64 V0 = 0.0;
+    bool valid0 = true;
+    if (valid00 && valid10) {
+        V0 = V00*SQUARE(1-fracX)+V10*SQUARE(fracX);
+    } else if (valid00) {
+        V0 = V00;
+    } else if (valid10) {
+        V0 = V10;
+    } else {
+        valid0 = false;
+    }
+
+    psF64 V1 = 0.0;
+    bool valid1 = true;
+    if (valid01 && valid11) {
+        V1 = V01*SQUARE(1-fracX)+V11*SQUARE(fracX);
+    } else if (valid01) {
+        V1 = V01;
+    } else if (valid11) {
+        V1 = V11;
+    } else {
+        valid1 = false;
+    }
+
+    if (valid0 && valid1) {
+        return ( V0*SQUARE(1-fracY) + V1*SQUARE(fracY) );
+    } else if (valid0) {
+        return V0;
+    } else if (valid1) {
+        return V1;
+    }
+
+    return unexposedValue;
+}
+
+
+
+bool stacTransform(psArray **outputs,   // Transformed images for output
+                   psArray **outErrors, // Transformed error images for output
+                   const psArray *images, // Array of images to be transformed
+                   const psArray *maps, // Array of polynomials that do the transformation
+                   const psArray *errors, // Array of error images to be transformed
+                   const psArray *masks, // Masks of input images
+                   const psImage *region, // Region of interest for transformation
+                   const psVector *scales, // Relative scales
+                   const psVector *offsets, // Relative offsets
+                   int outnx, int outny // Size of output images
+    )
+{
+    int nImages = images->n;            // Number of images
+
+    // Check input sizes
+    assert(images->n == maps->n);
+    assert(!errors || (images->n == errors->n));
+    assert(!scales || scales->n == images->n);
+    assert(!offsets || offsets->n == images->n);
+    assert(!scales || scales->type.type == PS_TYPE_F32);
+    assert(!offsets || offsets->type.type == PS_TYPE_F32);
+
+    // Allocate the output images if required, otherwise check the number
+    assert(!*outputs || (*outputs)->n == nImages);
+    if (*outputs == NULL) {
+        *outputs = psArrayAlloc(nImages);
+        psTrace("stac.transform", 5, "Allocating space for transformed images, %dx%d\n", outnx, outny);
+        for (int i = 0; i < nImages; i++) {
+            (*outputs)->data[i] = psImageAlloc(outnx, outny, PS_TYPE_F32);
+            psImageInit((*outputs)->data[i], 0.0);
+        }
+    }
+
+    // Allocate the output error images, if required, otherwise check the number
+    assert(!errors || ! *outErrors || errors->n == (*outErrors)->n);
+    if (errors && (*outErrors == NULL)) {
+        *outErrors = psArrayAlloc(errors->n);
+        psTrace("stac.transform", 5, "Allocating space for transformed error images, %dx%d\n", outnx, outny);
+        for (int i = 0; i < nImages; i++) {
+            (*outErrors)->data[i] = psImageAlloc(outnx, outny, PS_TYPE_F32);
+        }
+    }
+
+    // Check the masks, if specified
+    assert(!masks || masks->n == nImages);
+    if (masks != NULL) {
+        for (int i = 0; i < nImages; i++) {
+            psImage *image = images->data[i];
+            psImage *mask = masks->data[i];
+            assert(mask->numRows == image->numRows && mask->numCols == image->numCols);
+        }
+    }
+
+
+    // Stuff for the transformations
+    psPlane *detector = psAlloc(sizeof(psPlane)); // Coordinates on the detector
+    psPlane *sky = psAlloc(sizeof(psPlane)); // Coordinates on the sky
+
+
+    // Iterate over the images
+    for (int n = 0; n < nImages; n++) {
+        psTrace("stac.transform", 1, "Transforming image %d....\n",n);
+
+        // Pull out the various stuff we're working on
+        psImage *image = images->data[n]; // The input image
+        psPlaneTransform *map = maps->data[n]; // The map
+        psImage *outImage = (*outputs)->data[n]; // The output image
+        psImage *error = NULL; // The error image
+        psImage *outError = NULL; // The output error image
+        if (errors) {
+            error = errors->data[n];
+            outError = (*outErrors)->data[n];
+        }
+        float offset = 0.0;             // Relative offset
+        float scale = 1.0;              // Relative scale
+        if (offsets) {
+            offset = offsets->data.F32[n];
+        }
+        if (scales) {
+            scale = scales->data.F32[n];
+        }
+
+        // Mask
+        psImage *mask = NULL;
+        if (masks != NULL) {
+            mask = masks->data[n];
+        }
+
+        psImageInterpolateOptions *interp = psImageInterpolateOptionsAlloc(PS_INTERPOLATE_BILINEAR,
+                                                                           image, error, mask, 1, NAN, NAN,
+                                                                           1, 0, 0.0);
+
+        // Iterate over the output image pixels
+        for (int y = 0; y < outny; y++) {
+            for (int x = 0; x < outnx; x++) {
+                // Only transform those pixels requested
+                if (!region || (region && region->data.U8[y][x])) {
+                    // Transform!
+                    sky->x = (double)x + 0.5;
+                    sky->y = (double)y + 0.5;
+                    (void)psPlaneTransformApply(detector, map, sky);
+
+                    // Change PS_INTERPOLATE_BILINEAR to best available technique.
+                    double imageValue, varianceValue;
+                    if (!psImageInterpolate(&imageValue, &varianceValue, NULL,
+                                            detector->x, detector->y, interp)) {
+                        psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+                        psFree(interp);
+                        psFree(detector);
+                        psFree(sky);
+                        return false;
+                    }
+                    outImage->data.F32[y][x] = imageValue;
+
+                    if (error) {
+                        // Error is actually the variance
+                        outError->data.F32[y][x] = varianceValue;
+                    }
+
+                    outImage->data.F32[y][x] = (outImage->data.F32[y][x] - offset) / scale;
+                    if (error) {
+                        outError->data.F32[y][x] = outError->data.F32[y][x] / SQUARE(scale);
+                    }
+
+                } // Pixels of interest
+
+            }
+        } // Iterating over output pixels
+        psFree(interp);
+
+    } // Iterating over images
+
+    // Done with transformations
+    psFree(detector);
+    psFree(sky);
+
+    return true;
+}
+
Index: /tags/sj_tags/sj_root_20080929/stac/src/stacWrite.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/stacWrite.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/stacWrite.c	(revision 22322)
@@ -0,0 +1,81 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include "pslib.h"
+#include "stac.h"
+
+bool stacWriteMap(const char *mapName,  // Filename to write to
+                  psPlaneTransform *map // Map to write
+    )
+{
+    assert(mapName);
+
+    FILE *mapFile = fopen(mapName, "w");
+    if (!mapFile) {
+        fprintf(stderr, "Unable to open map file: %s\n", mapName);
+        return false;
+    }
+
+    psPolynomial2D *xMap = map->x;      // x transform
+    psPolynomial2D *yMap = map->y;      // y transform
+
+    // A crucial limitation of the current system --- the order of each polynomial must be the same
+    assert(xMap->nX == xMap->nY && yMap->nX == yMap->nY && xMap->nX == yMap->nX);
+    int order = xMap->nX;       // The polynomial order
+    fprintf(mapFile, "%d\n", order);
+
+    // x coefficients
+    for (int k = 0; k <= order; k++) {
+        for (int j = 0; j <= k; j++) {
+            int i = k - j;
+            if (xMap->mask[i][j]) {
+                fprintf(mapFile, "0.0 ");
+            } else {
+                fprintf(mapFile, "%g ", xMap->coeff[i][j]);
+            }
+        }
+    }
+    fprintf(mapFile, "\n");
+
+    // y coefficients
+    for (int k = 0; k <= order; k++) {
+        for (int j = 0; j <= k; j++) {
+            int i = k - j;
+            if (yMap->mask[i][j]) {
+                fprintf(mapFile, "0.0 ");
+            } else {
+                fprintf(mapFile, "%g ", yMap->coeff[i][j]);
+            }
+        }
+    }
+    fprintf(mapFile, "\n");
+
+    fclose(mapFile);
+
+    return true;
+}
+
+
+bool stacWriteMaps(const psArray *names, // Filenames of the input images (will add ".map")
+                   const psArray *maps  // Maps to write
+    )
+{
+    assert(names);
+    assert(maps);
+    assert(names->n == maps->n);
+
+    for (int i = 0; i < names->n; i++) {
+        char mapName[MAXCHAR];          // Filename of error image
+        sprintf(mapName, "%s.map", (const char*)names->data[i]);
+        if (!stacWriteMap(mapName, maps->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
Index: /tags/sj_tags/sj_root_20080929/stac/src/sum.c
===================================================================
--- /tags/sj_tags/sj_root_20080929/stac/src/sum.c	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/stac/src/sum.c	(revision 22322)
@@ -0,0 +1,86 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// Brain-dead simple stack of multiple frames
+// Makes no effort to zap bad pixels, or rescale images.
+// Assumes that images are already registered
+
+#include <stdio.h>
+#include "pslib.h"
+
+int main(int argc, char *argv[])
+{
+    const char *outputName = argv[1];   // Output file name
+    psArray *inputNames = psArrayAlloc(argc-2);
+    for (int i = 2; i < argc; i++) {
+        inputNames->data[i-2] = psStringCopy(argv[i]);
+    }
+
+    int numCols = 0;                    // Number of columns
+    int numRows = 0;                    // Number of rows
+    psImage *output = NULL;             // Output image
+    psImage *scale = NULL;              // Scale image
+    psMetadata *header = NULL;          // FITS header for output image
+    for (int i = 0; i < inputNames->n; i++) {
+        psFits *fits = psFitsOpen(inputNames->data[i], "r"); // FITS file
+        psRegion readRegion = {0, 0, 0, 0}; // Region to read
+        psImage *image = psFitsReadImage(fits, readRegion, 0);
+        if (numCols == 0 && numRows == 0) {
+            // First run through --- set the size, initialise the output, read the header
+            numCols = image->numCols;
+            numRows = image->numRows;
+            output = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+            scale = psImageAlloc(numCols, numRows, PS_TYPE_U8);
+            for (int y = 0; y < numCols; y++) {
+                for (int x = 0; x < numRows; x++) {
+                    output->data.F32[y][x] = 0.0;
+                    scale->data.U8[y][x] = 0;
+                }
+            }
+            header = psFitsReadHeader(NULL, fits);
+        } else if (image->numCols != numCols || image->numRows != numRows) {
+            psError(PS_ERR_IO, true, "Input images have different dimensions.  %s is %dx%d, but should be "
+                    "%dx%d\n", (char *)inputNames->data[i], image->numCols, image->numRows, numCols, numRows);
+            return EXIT_FAILURE;
+        }
+        psFitsClose(fits);
+
+        // Convert the type, if necessary
+        if (image->type.type != PS_TYPE_F32) {
+            psImage *temp = psImageCopy(NULL, image, PS_TYPE_F32);
+            psFree(image);
+            image = temp;
+        }
+
+        // Add in
+        for (int y = 0; y < numCols; y++) {
+            for (int x = 0; x < numRows; x++) {
+                if (image->data.F32[y][x] > 0) {
+                    output->data.F32[y][x] += image->data.F32[y][x];
+                    scale->data.U8[y][x]++;
+                }
+            }
+        }
+
+        psFree(image);
+    }
+    psFree(inputNames);
+
+    for (int y = 0; y < numCols; y++) {
+        for (int x = 0; x < numRows; x++) {
+            if (scale->data.U8[y][x] > 0) {
+                output->data.F32[y][x] /= (float)scale->data.U8[y][x];
+            } else {
+                output->data.F32[y][x] = 0.0;
+            }
+        }
+    }
+
+    psFits *fits = psFitsOpen(outputName, "w");
+    (void)psFitsWriteImage(fits, header, output, 0, NULL);
+    psFitsClose(fits);
+    psFree(output);
+    // Pau.
+    return EXIT_SUCCESS;
+}
Index: /tags/sj_tags/sj_root_20080929/tools/start_pantasks_client
===================================================================
--- /tags/sj_tags/sj_root_20080929/tools/start_pantasks_client	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/tools/start_pantasks_client	(revision 22322)
@@ -0,0 +1,13 @@
+#!/bin/bash
+# start up pantasks_client
+# 1st argument is the name of the host to connect to
+
+server_arg=""
+server_host=$1
+
+if [ $server_host ]; then
+   server_arg="-D PANTASKS_SERVER $server_host"
+fi
+
+echo pantasks_client $server_arg
+pantasks_client $server_arg
Index: /tags/sj_tags/sj_root_20080929/tools/start_pantasks_server
===================================================================
--- /tags/sj_tags/sj_root_20080929/tools/start_pantasks_server	(revision 22322)
+++ /tags/sj_tags/sj_root_20080929/tools/start_pantasks_server	(revision 22322)
@@ -0,0 +1,8 @@
+#!/bin/bash
+# Start up a pantasks server
+# Output is redirected to files
+
+server_redirect="-D PANTASKS_SERVER_STDOUT pantasks.stdout.log -D PANTASKS_SERVER_STDERR pantasks.stderr.log"
+
+echo pantasks_server $server_redirect
+pantasks_server $server_redirect
